From 3cdac4133909dc32df9223fa4f038b340d266b9d Mon Sep 17 00:00:00 2001 From: Karl Burtram Date: Thu, 9 Nov 2017 14:30:27 -0800 Subject: [PATCH] SQL Operations Studio Public Preview 1 (0.23) release source code --- .editorconfig | 16 + .eslintrc | 14 + .gitattributes | 0 .gitignore | 310 +- .mention-bot | 7 + .travis.yml | 50 + .vscode/extensions.json | 9 + .vscode/launch.json | 128 + .vscode/settings.json | 35 + .vscode/tasks.json | 72 + CODE_OF_CONDUCT.md | 1 + CONTRIBUTING.md | 54 + LICENSE.txt | 22 + OSSREADME.json | 775 ++ README.md | 21 +- ThirdPartyNotices.txt | 2069 +++ appveyor.yml | 19 + build/gulpfile.editor.js | 207 + build/gulpfile.extensions.js | 150 + build/gulpfile.hygiene.js | 310 + build/gulpfile.mixin.js | 69 + build/gulpfile.sql.js | 97 + build/gulpfile.test.js | 15 + build/gulpfile.vscode.js | 508 + build/gulpfile.vscode.linux.js | 289 + build/gulpfile.vscode.win32.js | 93 + build/lib/bundle.js | 456 + build/lib/bundle.ts | 643 + build/lib/compilation.js | 170 + build/lib/compilation.ts | 207 + build/lib/extensions.js | 118 + build/lib/extensions.ts | 132 + build/lib/git.js | 53 + build/lib/git.ts | 61 + build/lib/i18n.js | 1001 ++ build/lib/i18n.resources.json | 198 + build/lib/i18n.ts | 1116 ++ build/lib/nls.js | 359 + build/lib/nls.ts | 469 + build/lib/optimize.js | 241 + build/lib/optimize.ts | 330 + build/lib/reporter.js | 83 + build/lib/reporter.ts | 100 + build/lib/snapshotLoader.js | 39 + build/lib/snapshotLoader.ts | 63 + build/lib/test/i18n.test.js | 40 + build/lib/test/i18n.test.ts | 54 + build/lib/tslint/allowAsyncRule.js | 59 + build/lib/tslint/allowAsyncRule.ts | 47 + build/lib/tslint/duplicateImportsRule.js | 50 + build/lib/tslint/duplicateImportsRule.ts | 40 + build/lib/tslint/importPatternsRule.js | 90 + build/lib/tslint/importPatternsRule.ts | 87 + build/lib/tslint/layeringRule.js | 101 + build/lib/tslint/layeringRule.ts | 105 + .../lib/tslint/noUnexternalizedStringsRule.js | 182 + .../lib/tslint/noUnexternalizedStringsRule.ts | 201 + build/lib/tslint/translationRemindRule.js | 79 + build/lib/tslint/translationRemindRule.ts | 73 + build/lib/typings/OSSREADME.json | 10 + build/lib/typings/Q.d.ts | 361 + build/lib/typings/chalk.d.ts | 121 + build/lib/typings/debounce.d.ts | 18 + build/lib/typings/event-stream.d.ts | 21 + build/lib/typings/gulp-bom.d.ts | 12 + build/lib/typings/gulp-concat.d.ts | 43 + build/lib/typings/gulp-cssnano.d.ts | 12 + build/lib/typings/gulp-filter.d.ts | 29 + build/lib/typings/gulp-flatmap.d.ts | 12 + build/lib/typings/gulp-remote-src.d.ts | 23 + build/lib/typings/gulp-rename.d.ts | 29 + build/lib/typings/gulp-sourcemaps.d.ts | 27 + build/lib/typings/gulp-tsb.d.ts | 16 + build/lib/typings/gulp-util.d.ts | 133 + build/lib/typings/gulp.d.ts | 293 + build/lib/typings/is.d.ts | 11 + build/lib/typings/lazy.js.d.ts | 273 + build/lib/typings/minimatch.d.ts | 64 + build/lib/typings/object-assign.d.ts | 4 + build/lib/typings/orchestrator.d.ts | 125 + build/lib/typings/pump.d.ts | 23 + build/lib/typings/rimraf.d.ts | 17 + build/lib/typings/source-map.d.ts | 91 + build/lib/typings/through.d.ts | 22 + build/lib/typings/through2.d.ts | 38 + build/lib/typings/underscore.d.ts | 6086 +++++++++ build/lib/typings/vinyl.d.ts | 112 + build/lib/util.js | 213 + build/lib/util.ts | 271 + build/lib/watch/index.js | 38 + build/lib/watch/package.json | 11 + build/lib/watch/watch-nsfw.js | 94 + build/lib/watch/watch-win32.js | 108 + build/lib/watch/watcher.exe | Bin 0 -> 8192 bytes build/monaco/README-npm.md | 14 + build/monaco/README.md | 20 + build/monaco/ThirdPartyNotices.txt | 85 + build/monaco/api.js | 327 + build/monaco/api.ts | 382 + build/monaco/monaco.d.ts.recipe | 87 + build/monaco/package.json | 57 + build/npm/postinstall.js | 62 + build/npm/preinstall.js | 15 + build/npm/update-all-grammars.js | 73 + build/npm/update-grammar.js | 125 + build/npm/update-theme.js | 82 + build/package.json | 28 + build/tfs/common/.gitignore | 2 + build/tfs/common/common.sh | 40 + build/tfs/common/enqueue.ts | 85 + build/tfs/common/installDistro.ts | 26 + build/tfs/common/node.sh | 15 + build/tfs/common/publish.ts | 266 + build/tfs/darwin/build.sh | 41 + build/tfs/darwin/release.sh | 26 + build/tfs/darwin/smoketest.sh | 28 + build/tfs/linux/.gitignore | 1 + build/tfs/linux/build-ia32.sh | 3 + build/tfs/linux/build-x64.sh | 3 + build/tfs/linux/build.sh | 43 + build/tfs/linux/ia32/Dockerfile | 50 + build/tfs/linux/ia32/run-agent.sh | 15 + build/tfs/linux/ia32/xvfb.init | 53 + build/tfs/linux/release.sh | 86 + build/tfs/linux/repoapi_client.sh | 262 + build/tfs/linux/smoketest.sh | 46 + build/tfs/linux/x64/Dockerfile | 43 + build/tfs/linux/x64/run-agent.sh | 15 + build/tfs/linux/x64/xvfb.init | 53 + build/tfs/win32/1_build.ps1 | 55 + build/tfs/win32/2_package.ps1 | 12 + build/tfs/win32/3_upload.ps1 | 35 + build/tfs/win32/lib.ps1 | 47 + build/tfs/win32/node.ps1 | 6 + build/tfs/win32/smoketest.ps1 | 47 + build/tsconfig.json | 15 + build/tslint.json | 13 + build/win32/code.iss | 174 + build/win32/i18n/Default.isl | 337 + build/win32/i18n/Default.ko.isl | 298 + build/win32/i18n/Default.zh-cn.isl | 298 + build/win32/i18n/Default.zh-tw.isl | 298 + build/win32/i18n/messages.de.isl | 8 + build/win32/i18n/messages.en.isl | 8 + build/win32/i18n/messages.es.isl | 8 + build/win32/i18n/messages.fr.isl | 8 + build/win32/i18n/messages.hu.isl | 8 + build/win32/i18n/messages.it.isl | 8 + build/win32/i18n/messages.ja.isl | 8 + build/win32/i18n/messages.ko.isl | 8 + build/win32/i18n/messages.pt-br.isl | 8 + build/win32/i18n/messages.ru.isl | 8 + build/win32/i18n/messages.tr.isl | 8 + build/win32/i18n/messages.zh-cn.isl | 8 + build/win32/i18n/messages.zh-tw.isl | 8 + dataprotocol-node/.gitignore | 29 + dataprotocol-node/.travis.yml | 13 + dataprotocol-node/README.md | 4 + dataprotocol-node/client/.eslintrc | 24 + dataprotocol-node/client/.npmignore | 9 + dataprotocol-node/client/.vscode/launch.json | 32 + .../client/.vscode/settings.json | 11 + dataprotocol-node/client/.vscode/tasks.json | 9 + dataprotocol-node/client/README.md | 4 + dataprotocol-node/client/package.json | 32 + dataprotocol-node/client/src/codeConverter.ts | 526 + dataprotocol-node/client/src/main.ts | 2599 ++++ dataprotocol-node/client/src/protocol.ts | 1585 +++ .../client/src/protocolCodeLens.ts | 16 + .../client/src/protocolCompletionItem.ts | 16 + .../client/src/protocolConverter.ts | 769 ++ dataprotocol-node/client/src/tsconfig.json | 12 + .../client/src/typings/es6-promise/index.d.ts | 84 + .../src/typings/es6-promise/tsconfig.json | 23 + dataprotocol-node/client/src/typings/ref.d.ts | 7 + dataprotocol-node/client/src/utils/async.ts | 82 + .../client/src/utils/electron.ts | 123 + .../client/src/utils/electronForkStart.ts | 183 + dataprotocol-node/client/src/utils/is.ts | 55 + .../client/src/utils/processes.ts | 44 + .../client/src/utils/terminateProcess.sh | 16 + .../client/thirdpartynotices.txt | 31 + dataprotocol-node/jsonrpc/.eslintrc | 24 + dataprotocol-node/jsonrpc/.npmignore | 9 + dataprotocol-node/jsonrpc/.vscode/launch.json | 32 + .../jsonrpc/.vscode/settings.json | 9 + dataprotocol-node/jsonrpc/.vscode/tasks.json | 9 + dataprotocol-node/jsonrpc/README.md | 4 + dataprotocol-node/jsonrpc/package.json | 29 + dataprotocol-node/jsonrpc/src/cancellation.ts | 97 + dataprotocol-node/jsonrpc/src/events.ts | 213 + dataprotocol-node/jsonrpc/src/is.ts | 47 + dataprotocol-node/jsonrpc/src/main.ts | 576 + .../jsonrpc/src/messageReader.ts | 265 + .../jsonrpc/src/messageWriter.ts | 118 + dataprotocol-node/jsonrpc/src/messages.ts | 175 + dataprotocol-node/jsonrpc/src/tsconfig.json | 11 + .../jsonrpc/src/typings/promise.d.ts | 112 + .../jsonrpc/thirdpartynotices.txt | 31 + dataprotocol-node/types/.eslintrc | 24 + dataprotocol-node/types/.gitignore | 1 + dataprotocol-node/types/.npmignore | 9 + dataprotocol-node/types/.vscode/settings.json | 9 + dataprotocol-node/types/.vscode/tasks.json | 9 + dataprotocol-node/types/README.md | 4 + dataprotocol-node/types/package.json | 26 + dataprotocol-node/types/src/main.ts | 2468 ++++ dataprotocol-node/types/src/tsconfig.json | 10 + .../types/src/typings/promise.d.ts | 112 + extensions-modules/.gitignore | 3 + extensions-modules/.npmignore | 0 extensions-modules/package.json | 27 + .../src/configurations/config.ts | 89 + .../src/configurations/extConfig.ts | 75 + .../src/controllers/vscodeWrapper.ts | 280 + .../src/languageservice/decompressProvider.ts | 24 + .../src/languageservice/httpClient.ts | 146 + .../src/languageservice/interfaces.ts | 46 + .../src/languageservice/proxy.ts | 48 + .../src/languageservice/server.ts | 112 + .../src/languageservice/serverStatus.ts | 125 + .../src/languageservice/serviceClient.ts | 492 + .../serviceDownloadProvider.ts | 187 + .../languageservice/serviceInstallerUtil.ts | 130 + .../src/languageservice/serviceStatus.ts | 80 + extensions-modules/src/main.ts | 13 + extensions-modules/src/models/constants.ts | 26 + .../src/models/contracts/contracts.ts | 50 + .../src/models/contracts/languageService.ts | 55 + extensions-modules/src/models/interfaces.ts | 13 + extensions-modules/src/models/logger.ts | 68 + extensions-modules/src/models/platform.ts | 369 + extensions-modules/src/models/telemetry.ts | 161 + extensions-modules/src/models/utils.ts | 270 + extensions-modules/src/tsconfig.json | 19 + extensions-modules/src/typings/ref.d.ts | 7 + extensions-modules/src/typings/tmp.d.ts | 47 + .../typings/vscode-extension-telemetry.d.ts | 6 + .../src/utils/escapeException.ts | 3 + .../src/utils/validationException.ts | 3 + extensions-modules/src/views/statusView.ts | 142 + extensions/account-provider-azure/.gitignore | 0 .../account-provider-azure/package.json | 34 + .../account-provider-azure/package.nls.json | 3 + .../account-provider/azureAccountProvider.ts | 371 + .../azureAccountProviderService.ts | 87 + .../src/account-provider/interfaces.ts | 167 + .../media/microsoft_account.svg | 1 + .../media/work_school_account.svg | 1 + .../media/work_school_account_inverse.svg | 1 + .../src/account-provider/providerSettings.ts | 102 + .../src/account-provider/tokenCache.ts | 244 + .../account-provider-azure/src/constants.ts | 12 + extensions/account-provider-azure/src/main.ts | 35 + .../src/typings/ref.d.ts | 7 + .../account-provider-azure/tsconfig.json | 20 + extensions/bat/.vscodeignore | 1 + extensions/bat/OSSREADME.json | 22 + extensions/bat/language-configuration.json | 22 + extensions/bat/package.json | 19 + extensions/bat/syntaxes/Batch File.tmLanguage | 169 + .../bat/test/colorize-fixtures/test.bat | 24 + .../bat/test/colorize-results/test_bat.json | 343 + .../configuration-editing/.vscodeignore | 4 + .../configuration-editing/npm-shrinkwrap.json | 15 + extensions/configuration-editing/package.json | 87 + .../configuration-editing/src/extension.ts | 183 + .../src/settingsDocumentHelper.ts | 209 + .../src/typings/ref.d.ts | 7 + .../configuration-editing/tsconfig.json | 14 + extensions/declares.d.ts | 107 + extensions/diff/.vscodeignore | 1 + extensions/diff/OSSREADME.json | 22 + extensions/diff/language-configuration.json | 11 + extensions/diff/package.json | 26 + extensions/diff/syntaxes/diff.tmLanguage | 268 + extensions/diff/syntaxes/diff.tmLanguage.json | 168 + .../diff/test/colorize-fixtures/test.diff | 19 + .../diff/test/colorize-results/test_diff.json | 398 + extensions/docker/.vscodeignore | 1 + extensions/docker/OSSREADME.json | 8 + extensions/docker/language-configuration.json | 24 + extensions/docker/package.json | 23 + .../docker/syntaxes/docker.tmLanguage.json | 106 + .../docker/test/colorize-fixtures/Dockerfile | 14 + .../test/colorize-results/Dockerfile.json | 310 + extensions/extension-editing/.vscodeignore | 4 + .../extension-editing/npm-shrinkwrap.json | 61 + extensions/extension-editing/package.json | 52 + extensions/extension-editing/src/extension.ts | 120 + .../extension-editing/src/extensionLinter.ts | 361 + .../src/packageDocumentHelper.ts | 84 + .../extension-editing/src/typings/ref.d.ts | 6 + extensions/extension-editing/tsconfig.json | 13 + extensions/git/.vscodeignore | 5 + extensions/git/OSSREADME.json | 29 + extensions/git/npm-shrinkwrap.json | 31 + extensions/git/package.json | 801 ++ extensions/git/package.nls.json | 58 + extensions/git/resources/icons/dark/check.svg | 1 + extensions/git/resources/icons/dark/clean.svg | 1 + extensions/git/resources/icons/dark/git.svg | 1 + .../git/resources/icons/dark/open-change.svg | 1 + .../git/resources/icons/dark/open-file.svg | 1 + .../git/resources/icons/dark/refresh.svg | 1 + extensions/git/resources/icons/dark/stage.svg | 1 + .../git/resources/icons/dark/status-added.svg | 6 + .../resources/icons/dark/status-conflict.svg | 6 + .../resources/icons/dark/status-copied.svg | 6 + .../resources/icons/dark/status-deleted.svg | 6 + .../resources/icons/dark/status-ignored.svg | 6 + .../resources/icons/dark/status-modified.svg | 6 + .../resources/icons/dark/status-renamed.svg | 6 + .../resources/icons/dark/status-untracked.svg | 6 + .../git/resources/icons/dark/unstage.svg | 1 + .../git/resources/icons/light/check.svg | 1 + .../git/resources/icons/light/clean.svg | 1 + extensions/git/resources/icons/light/git.svg | 1 + .../git/resources/icons/light/open-change.svg | 1 + .../git/resources/icons/light/open-file.svg | 3 + .../git/resources/icons/light/refresh.svg | 1 + .../git/resources/icons/light/stage.svg | 1 + .../resources/icons/light/status-added.svg | 6 + .../resources/icons/light/status-conflict.svg | 6 + .../resources/icons/light/status-copied.svg | 6 + .../resources/icons/light/status-deleted.svg | 6 + .../resources/icons/light/status-ignored.svg | 6 + .../resources/icons/light/status-modified.svg | 6 + .../resources/icons/light/status-renamed.svg | 6 + .../icons/light/status-untracked.svg | 6 + .../git/resources/icons/light/unstage.svg | 1 + extensions/git/src/askpass-empty.sh | 2 + extensions/git/src/askpass-main.ts | 74 + extensions/git/src/askpass.sh | 5 + extensions/git/src/askpass.ts | 114 + extensions/git/src/autofetch.ts | 62 + extensions/git/src/commands.ts | 1412 ++ extensions/git/src/contentProvider.ts | 117 + extensions/git/src/decorators.ts | 103 + extensions/git/src/git.ts | 1067 ++ extensions/git/src/iterators.ts | 57 + extensions/git/src/main.ts | 104 + extensions/git/src/model.ts | 291 + extensions/git/src/repository.ts | 841 ++ extensions/git/src/staging.ts | 108 + extensions/git/src/statusbar.ts | 189 + extensions/git/src/test/git.test.ts | 137 + extensions/git/src/typings/refs.d.ts | 9 + extensions/git/src/uri.ts | 26 + extensions/git/src/util.ts | 191 + extensions/git/test/mocha.opts | 1 + extensions/git/tsconfig.json | 15 + extensions/gitsyntax/.vscodeignore | 1 + extensions/gitsyntax/OSSREADME.json | 29 + .../git-commit.language-configuration.json | 11 + .../git-rebase.language-configuration.json | 11 + extensions/gitsyntax/package.json | 55 + .../syntaxes/git-commit.tmLanguage.json | 148 + .../syntaxes/git-rebase.tmLanguage.json | 40 + .../test/colorize-fixtures/COMMIT_EDITMSG | 13 + .../test/colorize-fixtures/git-rebase-todo | 15 + .../test/colorize-results/COMMIT_EDITMSG.json | 255 + .../colorize-results/git-rebase-todo.json | 541 + extensions/insights-default/package.json | 137 + .../insights-default/sql/backup_detail.sql | 34 + .../insights-default/sql/backup_insight.sql | 16 + extensions/insights-default/sql/db_size.sql | 13 + extensions/insights-default/sql/qds.sql | 39 + .../insights-default/sql/qds_detail.sql | 41 + .../insights-default/sql/tablespace.sql | 25 + extensions/json/.vscode/launch.json | 38 + extensions/json/.vscode/tasks.json | 30 + extensions/json/.vscodeignore | 7 + extensions/json/OSSREADME.json | 7 + extensions/json/client/src/jsonMain.ts | 243 + extensions/json/client/src/typings/ref.d.ts | 7 + extensions/json/client/tsconfig.json | 10 + extensions/json/language-configuration.json | 18 + extensions/json/npm-shrinkwrap.json | 46 + extensions/json/package.json | 143 + extensions/json/package.nls.json | 11 + extensions/json/server/.vscode/launch.json | 32 + extensions/json/server/.vscode/tasks.json | 9 + extensions/json/server/npm-shrinkwrap.json | 81 + extensions/json/server/package.json | 29 + extensions/json/server/src/jsonServerMain.ts | 325 + .../json/server/src/languageModelCache.ts | 83 + extensions/json/server/src/utils/strings.ts | 37 + extensions/json/server/src/utils/uri.ts | 351 + extensions/json/server/tsconfig.json | 15 + extensions/json/syntaxes/JSON.tmLanguage.json | 228 + .../json/test/colorize-fixtures/test.json | 14 + .../json/test/colorize-results/test_json.json | 1168 ++ extensions/lib.core.d.ts | 3839 ++++++ extensions/markdown/.vscodeignore | 4 + extensions/markdown/OSSREADME.json | 89 + .../markdown/language-configuration.json | 41 + extensions/markdown/media/Preview.svg | 1 + .../markdown/media/PreviewOnRightPane_16x.svg | 19 + .../media/PreviewOnRightPane_16x_dark.svg | 19 + extensions/markdown/media/Preview_inverse.svg | 1 + extensions/markdown/media/ViewSource.svg | 3 + .../markdown/media/ViewSource_inverse.svg | 1 + extensions/markdown/media/csp.js | 42 + extensions/markdown/media/loading.js | 36 + extensions/markdown/media/main.js | 223 + extensions/markdown/media/markdown.css | 266 + extensions/markdown/media/tomorrow.css | 73 + extensions/markdown/npm-shrinkwrap.json | 76 + extensions/markdown/package.json | 257 + extensions/markdown/package.nls.json | 19 + extensions/markdown/snippets/markdown.json | 71 + .../markdown/src/documentLinkProvider.ts | 63 + .../markdown/src/documentSymbolProvider.ts | 25 + extensions/markdown/src/extension.ts | 308 + extensions/markdown/src/logger.ts | 78 + extensions/markdown/src/markdownEngine.ts | 162 + .../markdown/src/previewContentProvider.ts | 290 + extensions/markdown/src/security.ts | 140 + .../markdown/src/tableOfContentsProvider.ts | 94 + .../typings/markdown-it-named-headers.d.ts | 5 + extensions/markdown/src/typings/ref.d.ts | 7 + extensions/markdown/syntaxes/gulpfile.js | 149 + .../markdown/syntaxes/markdown.tmLanguage | 3624 +++++ .../syntaxes/markdown.tmLanguage.base | 1202 ++ .../markdown/test/colorize-fixtures/test.md | 105 + .../test/colorize-results/test_md.json | 2609 ++++ extensions/markdown/tsconfig.json | 21 + extensions/merge-conflict/.vscodeignore | 2 + extensions/merge-conflict/npm-shrinkwrap.json | 26 + extensions/merge-conflict/package.json | 93 + extensions/merge-conflict/package.nls.json | 15 + .../merge-conflict/src/codelensProvider.ts | 105 + .../merge-conflict/src/commandHandler.ts | 288 + .../merge-conflict/src/contentProvider.ts | 39 + extensions/merge-conflict/src/delayer.ts | 80 + .../src/documentMergeConflict.ts | 78 + .../merge-conflict/src/documentTracker.ts | 138 + extensions/merge-conflict/src/extension.ts | 20 + extensions/merge-conflict/src/interfaces.ts | 48 + .../merge-conflict/src/mergeConflictParser.ts | 168 + .../merge-conflict/src/mergeDecorator.ts | 252 + extensions/merge-conflict/src/services.ts | 67 + .../merge-conflict/src/typings/refs.d.ts | 7 + extensions/merge-conflict/tsconfig.json | 15 + .../ms-vscode.node-debug/OSSREADME.json | 129 + extensions/ms-vscode.node-debug/package.json | 8 + .../ms-vscode.node-debug2/OSSREADME.json | 150 + extensions/ms-vscode.node-debug2/package.json | 8 + extensions/mssql/.gitignore | 1 + extensions/mssql/client/src/config.json | 1127 ++ .../client/src/controllers/mainController.ts | 168 + .../src/credentialstore/credentialstore.ts | 73 + .../src/credentialstore/icredentialstore.ts | 23 + .../mssql/client/src/models/constants.ts | 317 + .../mssql/client/src/models/contracts.ts | 104 + extensions/mssql/client/src/mssqlMain.ts | 30 + .../src/resourceprovider/resourceprovider.ts | 63 + .../client/src/serialize/iserialization.ts | 18 + .../client/src/serialize/serialization.ts | 41 + extensions/mssql/client/src/typings/ref.d.ts | 8 + extensions/mssql/client/tsconfig.json | 19 + extensions/mssql/npm-shrinkwrap.json | 335 + extensions/mssql/package.json | 113 + extensions/mssql/package.nls.json | 8 + extensions/mssql/snippets/mssql.json | 298 + extensions/mssql/syntaxes/SQL.plist | 771 ++ .../mssql/syntaxes/sql.configuration.json | 3 + extensions/node.d.ts | 2313 ++++ extensions/npm-shrinkwrap.json | 11 + extensions/package.json | 11 + extensions/postinstall.js | 30 + extensions/powershell/.vscodeignore | 1 + extensions/powershell/OSSREADME.json | 7 + .../powershell/language-configuration.json | 26 + extensions/powershell/package.json | 22 + .../syntaxes/PowershellSyntax.tmLanguage | 1186 ++ .../test/colorize-fixtures/test.ps1 | 43 + .../test/colorize-results/test_ps1.json | 2422 ++++ extensions/python/.vscode/launch.json | 18 + extensions/python/.vscode/tasks.json | 30 + extensions/python/.vscodeignore | 3 + extensions/python/OSSREADME.json | 9 + extensions/python/language-configuration.json | 26 + extensions/python/package.json | 30 + extensions/python/src/pythonMain.ts | 17 + extensions/python/src/typings/ref.d.ts | 6 + .../syntaxes/MagicPython.tmLanguage.json | 5253 ++++++++ .../syntaxes/MagicRegExp.tmLanguage.json | 501 + .../python/test/colorize-fixtures/test.py | 97 + .../python/test/colorize-results/test_py.json | 6778 ++++++++++ extensions/python/tsconfig.json | 13 + extensions/r/.vscodeignore | 1 + extensions/r/OSSREADME.json | 17 + extensions/r/language-configuration.json | 22 + extensions/r/package.json | 22 + extensions/r/syntaxes/R.plist | 316 + extensions/r/syntaxes/r.tmLanguage.json | 209 + extensions/r/test/colorize-fixtures/test.r | 24 + .../r/test/colorize-results/test_r.json | 827 ++ extensions/ruby/.vscodeignore | 1 + extensions/ruby/OSSREADME.json | 22 + extensions/ruby/language-configuration.json | 29 + extensions/ruby/package.json | 24 + extensions/ruby/syntaxes/ruby.tmLanguage.json | 2814 ++++ .../ruby/test/colorize-fixtures/test.rb | 46 + .../ruby/test/colorize-results/test_rb.json | 2840 ++++ extensions/shellscript/.vscodeignore | 1 + extensions/shellscript/OSSREADME.json | 23 + .../shellscript/language-configuration.json | 26 + extensions/shellscript/package.json | 25 + .../syntaxes/Shell-Unix-Bash.tmLanguage.json | 1247 ++ .../test/colorize-fixtures/test.sh | 30 + .../test/colorize-results/test_sh.json | 1971 +++ extensions/sql/.vscodeignore | 1 + extensions/sql/OSSREADME.json | 23 + extensions/sql/language-configuration.json | 32 + extensions/sql/package.json | 19 + extensions/sql/syntaxes/SQL.plist | 771 ++ .../sql/test/colorize-fixtures/test.sql | 6 + .../sql/test/colorize-results/test_sql.json | 332 + extensions/theme-abyss/OSSREADME.json | 8 + extensions/theme-abyss/package.json | 15 + extensions/theme-abyss/themes/Abyss.tmTheme | 339 + .../theme-abyss/themes/abyss-color-theme.json | 434 + extensions/theme-carbon/package.json | 29 + .../theme-carbon/themes/dark_carbon.json | 526 + .../theme-carbon/themes/light_carbon.json | 560 + extensions/theme-defaults/package.json | 18 + .../theme-defaults/themes/hc_black.json | 274 + extensions/theme-kimbie-dark/OSSREADME.json | 8 + extensions/theme-kimbie-dark/package.json | 15 + .../themes/Kimbie_dark.tmTheme | 509 + .../themes/kimbie-dark-color-theme.json | 384 + .../theme-monokai-dimmed/OSSREADME.json | 8 + extensions/theme-monokai-dimmed/package.json | 15 + .../themes/dimmed-monokai-color-theme.json | 573 + .../themes/dimmed-monokai.tmTheme | 856 ++ extensions/theme-monokai/OSSREADME.json | 8 + extensions/theme-monokai/package.json | 15 + .../theme-monokai/themes/Monokai.tmTheme | 472 + .../themes/monokai-color-theme.json | 403 + extensions/theme-quietlight/OSSREADME.json | 8 + extensions/theme-quietlight/package.json | 15 + .../themes/QuietLight.tmTheme | 625 + .../themes/quietlight-color-theme.json | 504 + extensions/theme-red/OSSREADME.json | 8 + extensions/theme-red/package.json | 15 + .../theme-red/themes/Red-color-theme.json | 61 + extensions/theme-red/themes/red.tmTheme | 514 + extensions/theme-seti/.vscodeignore | 1 + extensions/theme-seti/OSSREADME.json | 8 + .../theme-seti/build/update-icon-theme.js | 331 + extensions/theme-seti/icons/dashboard.svg | 1 + .../theme-seti/icons/dashboard_inverse.svg | 1 + extensions/theme-seti/icons/seti.woff | Bin 0 -> 32180 bytes .../theme-seti/icons/vs-seti-icon-theme.json | 1431 ++ extensions/theme-seti/package.json | 20 + extensions/theme-seti/thirdpartynotices.txt | 32 + .../theme-solarized-dark/OSSREADME.json | 8 + extensions/theme-solarized-dark/package.json | 15 + .../themes/Solarized-dark.tmTheme | 439 + .../themes/solarized-dark-color-theme.json | 479 + .../theme-solarized-light/OSSREADME.json | 8 + extensions/theme-solarized-light/package.json | 15 + .../themes/Solarized-light.tmTheme | 434 + .../themes/solarized-light-color-theme.json | 482 + .../theme-tomorrow-night-blue/OSSREADME.json | 8 + .../theme-tomorrow-night-blue/package.json | 15 + .../themes/Tomorrow-Night-Blue.tmTheme | 293 + .../themes/tomorrow-night-blue-theme.json | 249 + extensions/vscode-colorize-tests/.gitignore | 2 + .../vscode-colorize-tests/.vscode/launch.json | 17 + .../vscode-colorize-tests/.vscode/tasks.json | 31 + extensions/vscode-colorize-tests/package.json | 19 + .../src/colorizer.test.ts | 77 + extensions/vscode-colorize-tests/src/index.ts | 28 + .../src/typings/ref.d.ts | 7 + .../vscode-colorize-tests/tsconfig.json | 14 + extensions/xml/.vscodeignore | 1 + extensions/xml/OSSREADME.json | 10 + extensions/xml/package.json | 93 + extensions/xml/syntaxes/xml.json | 443 + extensions/xml/syntaxes/xsl.json | 98 + .../xml/test/colorize-fixtures/test-7115.xml | 7 + .../xml/test/colorize-fixtures/test.xml | 20 + .../test/colorize-results/test-7115_xml.json | 585 + .../xml/test/colorize-results/test_xml.json | 1806 +++ .../xml/xml.language-configuration.json | 31 + .../xml/xsl.language-configuration.json | 20 + extensions/yaml/.gitignore | 1 + extensions/yaml/.vscodeignore | 1 + extensions/yaml/OSSREADME.json | 28 + extensions/yaml/language-configuration.json | 24 + extensions/yaml/package.json | 29 + extensions/yaml/syntaxes/yaml.json | 634 + .../test/colorize-fixtures/issue-1550.yaml | 4 + .../test/colorize-fixtures/issue-4008.yaml | 4 + .../test/colorize-fixtures/issue-6303.yaml | 9 + .../yaml/test/colorize-fixtures/test.yaml | 18 + .../colorize-results/issue-1550_yaml.json | 222 + .../colorize-results/issue-4008_yaml.json | 222 + .../colorize-results/issue-6303_yaml.json | 310 + .../yaml/test/colorize-results/test_yaml.json | 1047 ++ gulpfile.js | 77 + i18n/.gitignore | 1 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/chs/extensions/css/package.i18n.json | 74 + i18n/chs/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../chs/extensions/git/out/commands.i18n.json | 77 + i18n/chs/extensions/git/out/main.i18n.json | 11 + i18n/chs/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/chs/extensions/git/package.i18n.json | 63 + i18n/chs/extensions/grunt/out/main.i18n.json | 8 + i18n/chs/extensions/grunt/package.i18n.json | 8 + i18n/chs/extensions/gulp/out/main.i18n.json | 8 + i18n/chs/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/chs/extensions/html/package.i18n.json | 29 + i18n/chs/extensions/jake/out/main.i18n.json | 8 + i18n/chs/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/chs/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../chs/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/chs/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/chs/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 12 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + i18n/chs/src/vs/base/common/json.i18n.json | 16 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../chs/src/vs/base/common/severity.i18n.json | 10 + i18n/chs/src/vs/base/node/processes.i18n.json | 8 + i18n/chs/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/defaultConfig.i18n.json | 8 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../browser/accessibility.i18n.json | 15 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclaration.i18n.json | 24 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../inspectTMScopes.i18n.json | 9 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../common/keybindingLabels.i18n.json | 16 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../api/node/extHostExplorerView.i18n.json | 9 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeExplorers.i18n.json | 10 + .../api/node/extHostTreeView.i18n.json | 9 + .../api/node/extHostTreeViews.i18n.json | 10 + .../node/mainThreadExtensionService.i18n.json | 9 + .../node/mainThreadMessageService.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 10 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../quickopen.contribution.i18n.json | 12 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/crashReporter.i18n.json | 9 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../emmet/node/actions/balance.i18n.json | 9 + .../emmet/node/actions/editPoints.i18n.json | 9 + .../emmet/node/actions/evaluateMath.i18n.json | 8 + .../node/actions/expandAbbreviation.i18n.json | 8 + .../node/actions/incrementDecrement.i18n.json | 13 + .../emmet/node/actions/matchingPair.i18n.json | 8 + .../emmet/node/actions/mergeLines.i18n.json | 8 + .../node/actions/reflectCssValue.i18n.json | 8 + .../emmet/node/actions/removeTag.i18n.json | 8 + .../emmet/node/actions/selectItem.i18n.json | 9 + .../emmet/node/actions/splitJoinTag.i18n.json | 8 + .../node/actions/toggleComment.i18n.json | 8 + .../node/actions/updateImageSize.i18n.json | 8 + .../emmet/node/actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet/node/emmet.contribution.i18n.json | 13 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../keymapExtensions.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../browser/gitActions.contribution.i18n.json | 20 + .../parts/git/browser/gitActions.i18n.json | 46 + .../parts/git/browser/gitQuickOpen.i18n.json | 16 + .../parts/git/browser/gitServices.i18n.json | 30 + .../parts/git/browser/gitWidgets.i18n.json | 10 + .../gitWorkbenchContributions.i18n.json | 23 + .../views/changes/changesView.i18n.json | 14 + .../views/changes/changesViewer.i18n.json | 38 + .../views/disabled/disabledView.i18n.json | 8 + .../browser/views/empty/emptyView.i18n.json | 9 + .../views/gitless/gitlessView.i18n.json | 13 + .../git/browser/views/huge/hugeView.i18n.json | 10 + .../views/notroot/notrootView.i18n.json | 9 + .../noworkspace/noworkspaceView.i18n.json | 10 + .../git.contribution.i18n.json | 9 + .../git/electron-browser/gitActions.i18n.json | 12 + .../electron-main/askpassService.i18n.json | 8 + .../parts/git/node/git.lib.i18n.json | 9 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 10 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 10 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/cht/extensions/css/package.i18n.json | 74 + i18n/cht/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../cht/extensions/git/out/commands.i18n.json | 77 + i18n/cht/extensions/git/out/main.i18n.json | 11 + i18n/cht/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/cht/extensions/git/package.i18n.json | 63 + i18n/cht/extensions/grunt/out/main.i18n.json | 8 + i18n/cht/extensions/grunt/package.i18n.json | 8 + i18n/cht/extensions/gulp/out/main.i18n.json | 8 + i18n/cht/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/cht/extensions/html/package.i18n.json | 29 + i18n/cht/extensions/jake/out/main.i18n.json | 8 + i18n/cht/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/cht/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../cht/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/cht/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/cht/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 11 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + i18n/cht/src/vs/base/common/json.i18n.json | 16 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../cht/src/vs/base/common/severity.i18n.json | 10 + i18n/cht/src/vs/base/node/processes.i18n.json | 8 + i18n/cht/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/defaultConfig.i18n.json | 8 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../browser/accessibility.i18n.json | 15 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclaration.i18n.json | 24 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../inspectTMScopes.i18n.json | 9 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../common/keybindingLabels.i18n.json | 16 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../api/node/extHostExplorerView.i18n.json | 9 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeExplorers.i18n.json | 10 + .../api/node/extHostTreeView.i18n.json | 9 + .../api/node/extHostTreeViews.i18n.json | 10 + .../node/mainThreadExtensionService.i18n.json | 9 + .../node/mainThreadMessageService.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 9 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../quickopen.contribution.i18n.json | 12 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/crashReporter.i18n.json | 9 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../emmet/node/actions/balance.i18n.json | 9 + .../emmet/node/actions/editPoints.i18n.json | 9 + .../emmet/node/actions/evaluateMath.i18n.json | 8 + .../node/actions/expandAbbreviation.i18n.json | 8 + .../node/actions/incrementDecrement.i18n.json | 13 + .../emmet/node/actions/matchingPair.i18n.json | 8 + .../emmet/node/actions/mergeLines.i18n.json | 8 + .../node/actions/reflectCssValue.i18n.json | 8 + .../emmet/node/actions/removeTag.i18n.json | 8 + .../emmet/node/actions/selectItem.i18n.json | 9 + .../emmet/node/actions/splitJoinTag.i18n.json | 8 + .../node/actions/toggleComment.i18n.json | 8 + .../node/actions/updateImageSize.i18n.json | 8 + .../emmet/node/actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet/node/emmet.contribution.i18n.json | 13 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../keymapExtensions.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../browser/gitActions.contribution.i18n.json | 20 + .../parts/git/browser/gitActions.i18n.json | 46 + .../parts/git/browser/gitQuickOpen.i18n.json | 16 + .../parts/git/browser/gitServices.i18n.json | 30 + .../parts/git/browser/gitWidgets.i18n.json | 10 + .../gitWorkbenchContributions.i18n.json | 23 + .../views/changes/changesView.i18n.json | 14 + .../views/changes/changesViewer.i18n.json | 38 + .../views/disabled/disabledView.i18n.json | 8 + .../browser/views/empty/emptyView.i18n.json | 9 + .../views/gitless/gitlessView.i18n.json | 13 + .../git/browser/views/huge/hugeView.i18n.json | 10 + .../views/notroot/notrootView.i18n.json | 9 + .../noworkspace/noworkspaceView.i18n.json | 10 + .../git.contribution.i18n.json | 9 + .../git/electron-browser/gitActions.i18n.json | 12 + .../electron-main/askpassService.i18n.json | 8 + .../parts/git/node/git.lib.i18n.json | 9 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 8 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 8 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/deu/extensions/css/package.i18n.json | 74 + i18n/deu/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../deu/extensions/git/out/commands.i18n.json | 77 + i18n/deu/extensions/git/out/main.i18n.json | 11 + i18n/deu/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/deu/extensions/git/package.i18n.json | 63 + i18n/deu/extensions/grunt/out/main.i18n.json | 8 + i18n/deu/extensions/grunt/package.i18n.json | 8 + i18n/deu/extensions/gulp/out/main.i18n.json | 8 + i18n/deu/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/deu/extensions/html/package.i18n.json | 29 + i18n/deu/extensions/jake/out/main.i18n.json | 8 + i18n/deu/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/deu/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../deu/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/deu/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/deu/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 12 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + i18n/deu/src/vs/base/common/json.i18n.json | 16 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../deu/src/vs/base/common/severity.i18n.json | 10 + i18n/deu/src/vs/base/node/processes.i18n.json | 8 + i18n/deu/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/defaultConfig.i18n.json | 8 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../browser/accessibility.i18n.json | 15 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclaration.i18n.json | 24 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../inspectTMScopes.i18n.json | 9 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../common/keybindingLabels.i18n.json | 16 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../api/node/extHostExplorerView.i18n.json | 9 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeExplorers.i18n.json | 10 + .../api/node/extHostTreeView.i18n.json | 9 + .../api/node/extHostTreeViews.i18n.json | 10 + .../node/mainThreadExtensionService.i18n.json | 9 + .../node/mainThreadMessageService.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 9 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../quickopen.contribution.i18n.json | 12 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/crashReporter.i18n.json | 9 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../emmet/node/actions/balance.i18n.json | 9 + .../emmet/node/actions/editPoints.i18n.json | 9 + .../emmet/node/actions/evaluateMath.i18n.json | 8 + .../node/actions/expandAbbreviation.i18n.json | 8 + .../node/actions/incrementDecrement.i18n.json | 13 + .../emmet/node/actions/matchingPair.i18n.json | 8 + .../emmet/node/actions/mergeLines.i18n.json | 8 + .../node/actions/reflectCssValue.i18n.json | 8 + .../emmet/node/actions/removeTag.i18n.json | 8 + .../emmet/node/actions/selectItem.i18n.json | 9 + .../emmet/node/actions/splitJoinTag.i18n.json | 8 + .../node/actions/toggleComment.i18n.json | 8 + .../node/actions/updateImageSize.i18n.json | 8 + .../emmet/node/actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet/node/emmet.contribution.i18n.json | 13 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../keymapExtensions.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../browser/gitActions.contribution.i18n.json | 20 + .../parts/git/browser/gitActions.i18n.json | 46 + .../parts/git/browser/gitQuickOpen.i18n.json | 16 + .../parts/git/browser/gitServices.i18n.json | 30 + .../parts/git/browser/gitWidgets.i18n.json | 10 + .../gitWorkbenchContributions.i18n.json | 23 + .../views/changes/changesView.i18n.json | 14 + .../views/changes/changesViewer.i18n.json | 38 + .../views/disabled/disabledView.i18n.json | 8 + .../browser/views/empty/emptyView.i18n.json | 9 + .../views/gitless/gitlessView.i18n.json | 13 + .../git/browser/views/huge/hugeView.i18n.json | 10 + .../views/notroot/notrootView.i18n.json | 9 + .../noworkspace/noworkspaceView.i18n.json | 10 + .../git.contribution.i18n.json | 9 + .../git/electron-browser/gitActions.i18n.json | 12 + .../electron-main/askpassService.i18n.json | 8 + .../parts/git/node/git.lib.i18n.json | 9 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 8 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 8 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/esn/extensions/css/package.i18n.json | 74 + i18n/esn/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../esn/extensions/git/out/commands.i18n.json | 77 + i18n/esn/extensions/git/out/main.i18n.json | 11 + i18n/esn/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/esn/extensions/git/package.i18n.json | 63 + i18n/esn/extensions/grunt/out/main.i18n.json | 8 + i18n/esn/extensions/grunt/package.i18n.json | 8 + i18n/esn/extensions/gulp/out/main.i18n.json | 8 + i18n/esn/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/esn/extensions/html/package.i18n.json | 29 + i18n/esn/extensions/jake/out/main.i18n.json | 8 + i18n/esn/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/esn/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../esn/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/esn/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/esn/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 12 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + i18n/esn/src/vs/base/common/json.i18n.json | 16 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../esn/src/vs/base/common/severity.i18n.json | 10 + i18n/esn/src/vs/base/node/processes.i18n.json | 8 + i18n/esn/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/defaultConfig.i18n.json | 8 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../browser/accessibility.i18n.json | 15 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclaration.i18n.json | 24 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../inspectTMScopes.i18n.json | 9 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../common/keybindingLabels.i18n.json | 16 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../api/node/extHostExplorerView.i18n.json | 9 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeExplorers.i18n.json | 10 + .../api/node/extHostTreeView.i18n.json | 9 + .../api/node/extHostTreeViews.i18n.json | 10 + .../node/mainThreadExtensionService.i18n.json | 9 + .../node/mainThreadMessageService.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 9 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../quickopen.contribution.i18n.json | 12 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/crashReporter.i18n.json | 9 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../emmet/node/actions/balance.i18n.json | 9 + .../emmet/node/actions/editPoints.i18n.json | 9 + .../emmet/node/actions/evaluateMath.i18n.json | 8 + .../node/actions/expandAbbreviation.i18n.json | 8 + .../node/actions/incrementDecrement.i18n.json | 13 + .../emmet/node/actions/matchingPair.i18n.json | 8 + .../emmet/node/actions/mergeLines.i18n.json | 8 + .../node/actions/reflectCssValue.i18n.json | 8 + .../emmet/node/actions/removeTag.i18n.json | 8 + .../emmet/node/actions/selectItem.i18n.json | 9 + .../emmet/node/actions/splitJoinTag.i18n.json | 8 + .../node/actions/toggleComment.i18n.json | 8 + .../node/actions/updateImageSize.i18n.json | 8 + .../emmet/node/actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet/node/emmet.contribution.i18n.json | 13 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../keymapExtensions.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../browser/gitActions.contribution.i18n.json | 20 + .../parts/git/browser/gitActions.i18n.json | 46 + .../parts/git/browser/gitQuickOpen.i18n.json | 16 + .../parts/git/browser/gitServices.i18n.json | 30 + .../parts/git/browser/gitWidgets.i18n.json | 10 + .../gitWorkbenchContributions.i18n.json | 23 + .../views/changes/changesView.i18n.json | 14 + .../views/changes/changesViewer.i18n.json | 38 + .../views/disabled/disabledView.i18n.json | 8 + .../browser/views/empty/emptyView.i18n.json | 9 + .../views/gitless/gitlessView.i18n.json | 13 + .../git/browser/views/huge/hugeView.i18n.json | 10 + .../views/notroot/notrootView.i18n.json | 9 + .../noworkspace/noworkspaceView.i18n.json | 10 + .../git.contribution.i18n.json | 9 + .../git/electron-browser/gitActions.i18n.json | 12 + .../electron-main/askpassService.i18n.json | 8 + .../parts/git/node/git.lib.i18n.json | 9 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 10 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 10 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/fra/extensions/css/package.i18n.json | 74 + i18n/fra/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../fra/extensions/git/out/commands.i18n.json | 77 + i18n/fra/extensions/git/out/main.i18n.json | 11 + i18n/fra/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/fra/extensions/git/package.i18n.json | 63 + i18n/fra/extensions/grunt/out/main.i18n.json | 8 + i18n/fra/extensions/grunt/package.i18n.json | 8 + i18n/fra/extensions/gulp/out/main.i18n.json | 8 + i18n/fra/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/fra/extensions/html/package.i18n.json | 29 + i18n/fra/extensions/jake/out/main.i18n.json | 8 + i18n/fra/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/fra/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../fra/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/fra/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/fra/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 11 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + i18n/fra/src/vs/base/common/json.i18n.json | 16 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../fra/src/vs/base/common/severity.i18n.json | 10 + i18n/fra/src/vs/base/node/processes.i18n.json | 8 + i18n/fra/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/defaultConfig.i18n.json | 8 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../browser/accessibility.i18n.json | 15 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclaration.i18n.json | 24 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../inspectTMScopes.i18n.json | 9 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../common/keybindingLabels.i18n.json | 16 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../api/node/extHostExplorerView.i18n.json | 9 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeExplorers.i18n.json | 10 + .../api/node/extHostTreeView.i18n.json | 9 + .../api/node/extHostTreeViews.i18n.json | 10 + .../node/mainThreadExtensionService.i18n.json | 9 + .../node/mainThreadMessageService.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 9 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../quickopen.contribution.i18n.json | 12 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/crashReporter.i18n.json | 9 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../emmet/node/actions/balance.i18n.json | 9 + .../emmet/node/actions/editPoints.i18n.json | 9 + .../emmet/node/actions/evaluateMath.i18n.json | 8 + .../node/actions/expandAbbreviation.i18n.json | 8 + .../node/actions/incrementDecrement.i18n.json | 13 + .../emmet/node/actions/matchingPair.i18n.json | 8 + .../emmet/node/actions/mergeLines.i18n.json | 8 + .../node/actions/reflectCssValue.i18n.json | 8 + .../emmet/node/actions/removeTag.i18n.json | 8 + .../emmet/node/actions/selectItem.i18n.json | 9 + .../emmet/node/actions/splitJoinTag.i18n.json | 8 + .../node/actions/toggleComment.i18n.json | 8 + .../node/actions/updateImageSize.i18n.json | 8 + .../emmet/node/actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet/node/emmet.contribution.i18n.json | 13 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../keymapExtensions.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../browser/gitActions.contribution.i18n.json | 20 + .../parts/git/browser/gitActions.i18n.json | 46 + .../parts/git/browser/gitQuickOpen.i18n.json | 16 + .../parts/git/browser/gitServices.i18n.json | 30 + .../parts/git/browser/gitWidgets.i18n.json | 10 + .../gitWorkbenchContributions.i18n.json | 23 + .../views/changes/changesView.i18n.json | 14 + .../views/changes/changesViewer.i18n.json | 38 + .../views/disabled/disabledView.i18n.json | 8 + .../browser/views/empty/emptyView.i18n.json | 9 + .../views/gitless/gitlessView.i18n.json | 13 + .../git/browser/views/huge/hugeView.i18n.json | 10 + .../views/notroot/notrootView.i18n.json | 9 + .../noworkspace/noworkspaceView.i18n.json | 10 + .../git.contribution.i18n.json | 9 + .../git/electron-browser/gitActions.i18n.json | 12 + .../electron-main/askpassService.i18n.json | 8 + .../parts/git/node/git.lib.i18n.json | 9 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 8 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 8 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/hun/extensions/css/package.i18n.json | 74 + i18n/hun/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../hun/extensions/git/out/commands.i18n.json | 77 + i18n/hun/extensions/git/out/main.i18n.json | 11 + i18n/hun/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/hun/extensions/git/package.i18n.json | 63 + i18n/hun/extensions/grunt/out/main.i18n.json | 8 + i18n/hun/extensions/grunt/package.i18n.json | 8 + i18n/hun/extensions/gulp/out/main.i18n.json | 8 + i18n/hun/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/hun/extensions/html/package.i18n.json | 29 + i18n/hun/extensions/jake/out/main.i18n.json | 8 + i18n/hun/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/hun/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../hun/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/hun/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/hun/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 12 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../hun/src/vs/base/common/severity.i18n.json | 10 + i18n/hun/src/vs/base/node/processes.i18n.json | 8 + i18n/hun/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeViews.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 13 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 10 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 10 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/ita/extensions/css/package.i18n.json | 74 + i18n/ita/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../ita/extensions/git/out/commands.i18n.json | 77 + i18n/ita/extensions/git/out/main.i18n.json | 11 + i18n/ita/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/ita/extensions/git/package.i18n.json | 63 + i18n/ita/extensions/grunt/out/main.i18n.json | 8 + i18n/ita/extensions/grunt/package.i18n.json | 8 + i18n/ita/extensions/gulp/out/main.i18n.json | 8 + i18n/ita/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/ita/extensions/html/package.i18n.json | 29 + i18n/ita/extensions/jake/out/main.i18n.json | 8 + i18n/ita/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/ita/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../ita/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/ita/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/ita/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 11 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + i18n/ita/src/vs/base/common/json.i18n.json | 16 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../ita/src/vs/base/common/severity.i18n.json | 10 + i18n/ita/src/vs/base/node/processes.i18n.json | 8 + i18n/ita/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/defaultConfig.i18n.json | 8 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../browser/accessibility.i18n.json | 15 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclaration.i18n.json | 24 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../inspectTMScopes.i18n.json | 9 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../common/keybindingLabels.i18n.json | 16 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../api/node/extHostExplorerView.i18n.json | 9 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeExplorers.i18n.json | 10 + .../api/node/extHostTreeView.i18n.json | 9 + .../api/node/extHostTreeViews.i18n.json | 10 + .../node/mainThreadExtensionService.i18n.json | 9 + .../node/mainThreadMessageService.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 9 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../quickopen.contribution.i18n.json | 12 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/crashReporter.i18n.json | 9 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../emmet/node/actions/balance.i18n.json | 9 + .../emmet/node/actions/editPoints.i18n.json | 9 + .../emmet/node/actions/evaluateMath.i18n.json | 8 + .../node/actions/expandAbbreviation.i18n.json | 8 + .../node/actions/incrementDecrement.i18n.json | 13 + .../emmet/node/actions/matchingPair.i18n.json | 8 + .../emmet/node/actions/mergeLines.i18n.json | 8 + .../node/actions/reflectCssValue.i18n.json | 8 + .../emmet/node/actions/removeTag.i18n.json | 8 + .../emmet/node/actions/selectItem.i18n.json | 9 + .../emmet/node/actions/splitJoinTag.i18n.json | 8 + .../node/actions/toggleComment.i18n.json | 8 + .../node/actions/updateImageSize.i18n.json | 8 + .../emmet/node/actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet/node/emmet.contribution.i18n.json | 13 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../keymapExtensions.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../browser/gitActions.contribution.i18n.json | 20 + .../parts/git/browser/gitActions.i18n.json | 46 + .../parts/git/browser/gitQuickOpen.i18n.json | 16 + .../parts/git/browser/gitServices.i18n.json | 30 + .../parts/git/browser/gitWidgets.i18n.json | 10 + .../gitWorkbenchContributions.i18n.json | 23 + .../views/changes/changesView.i18n.json | 14 + .../views/changes/changesViewer.i18n.json | 38 + .../views/disabled/disabledView.i18n.json | 8 + .../browser/views/empty/emptyView.i18n.json | 9 + .../views/gitless/gitlessView.i18n.json | 13 + .../git/browser/views/huge/hugeView.i18n.json | 10 + .../views/notroot/notrootView.i18n.json | 9 + .../noworkspace/noworkspaceView.i18n.json | 10 + .../git.contribution.i18n.json | 9 + .../git/electron-browser/gitActions.i18n.json | 12 + .../electron-main/askpassService.i18n.json | 8 + .../parts/git/node/git.lib.i18n.json | 9 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 8 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 8 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/jpn/extensions/css/package.i18n.json | 74 + i18n/jpn/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../jpn/extensions/git/out/commands.i18n.json | 77 + i18n/jpn/extensions/git/out/main.i18n.json | 11 + i18n/jpn/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/jpn/extensions/git/package.i18n.json | 63 + i18n/jpn/extensions/grunt/out/main.i18n.json | 8 + i18n/jpn/extensions/grunt/package.i18n.json | 8 + i18n/jpn/extensions/gulp/out/main.i18n.json | 8 + i18n/jpn/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/jpn/extensions/html/package.i18n.json | 29 + i18n/jpn/extensions/jake/out/main.i18n.json | 8 + i18n/jpn/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/jpn/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../jpn/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/jpn/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/jpn/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 12 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + i18n/jpn/src/vs/base/common/json.i18n.json | 16 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../jpn/src/vs/base/common/severity.i18n.json | 10 + i18n/jpn/src/vs/base/node/processes.i18n.json | 8 + i18n/jpn/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/defaultConfig.i18n.json | 8 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../browser/accessibility.i18n.json | 15 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclaration.i18n.json | 24 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../inspectTMScopes.i18n.json | 9 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../common/keybindingLabels.i18n.json | 16 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../api/node/extHostExplorerView.i18n.json | 9 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeExplorers.i18n.json | 10 + .../api/node/extHostTreeView.i18n.json | 9 + .../api/node/extHostTreeViews.i18n.json | 10 + .../node/mainThreadExtensionService.i18n.json | 9 + .../node/mainThreadMessageService.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 10 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../quickopen.contribution.i18n.json | 12 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/crashReporter.i18n.json | 9 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../emmet/node/actions/balance.i18n.json | 9 + .../emmet/node/actions/editPoints.i18n.json | 9 + .../emmet/node/actions/evaluateMath.i18n.json | 8 + .../node/actions/expandAbbreviation.i18n.json | 8 + .../node/actions/incrementDecrement.i18n.json | 13 + .../emmet/node/actions/matchingPair.i18n.json | 8 + .../emmet/node/actions/mergeLines.i18n.json | 8 + .../node/actions/reflectCssValue.i18n.json | 8 + .../emmet/node/actions/removeTag.i18n.json | 8 + .../emmet/node/actions/selectItem.i18n.json | 9 + .../emmet/node/actions/splitJoinTag.i18n.json | 8 + .../node/actions/toggleComment.i18n.json | 8 + .../node/actions/updateImageSize.i18n.json | 8 + .../emmet/node/actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet/node/emmet.contribution.i18n.json | 13 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../keymapExtensions.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../browser/gitActions.contribution.i18n.json | 20 + .../parts/git/browser/gitActions.i18n.json | 46 + .../parts/git/browser/gitQuickOpen.i18n.json | 16 + .../parts/git/browser/gitServices.i18n.json | 30 + .../parts/git/browser/gitWidgets.i18n.json | 10 + .../gitWorkbenchContributions.i18n.json | 23 + .../views/changes/changesView.i18n.json | 14 + .../views/changes/changesViewer.i18n.json | 38 + .../views/disabled/disabledView.i18n.json | 8 + .../browser/views/empty/emptyView.i18n.json | 9 + .../views/gitless/gitlessView.i18n.json | 13 + .../git/browser/views/huge/hugeView.i18n.json | 10 + .../views/notroot/notrootView.i18n.json | 9 + .../noworkspace/noworkspaceView.i18n.json | 10 + .../git.contribution.i18n.json | 9 + .../git/electron-browser/gitActions.i18n.json | 12 + .../electron-main/askpassService.i18n.json | 8 + .../parts/git/node/git.lib.i18n.json | 9 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 10 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 10 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/kor/extensions/css/package.i18n.json | 74 + i18n/kor/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../kor/extensions/git/out/commands.i18n.json | 77 + i18n/kor/extensions/git/out/main.i18n.json | 11 + i18n/kor/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/kor/extensions/git/package.i18n.json | 63 + i18n/kor/extensions/grunt/out/main.i18n.json | 8 + i18n/kor/extensions/grunt/package.i18n.json | 8 + i18n/kor/extensions/gulp/out/main.i18n.json | 8 + i18n/kor/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/kor/extensions/html/package.i18n.json | 29 + i18n/kor/extensions/jake/out/main.i18n.json | 8 + i18n/kor/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/kor/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../kor/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/kor/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/kor/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 11 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + i18n/kor/src/vs/base/common/json.i18n.json | 16 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../kor/src/vs/base/common/severity.i18n.json | 10 + i18n/kor/src/vs/base/node/processes.i18n.json | 8 + i18n/kor/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/defaultConfig.i18n.json | 8 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../browser/accessibility.i18n.json | 15 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclaration.i18n.json | 24 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../inspectTMScopes.i18n.json | 9 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../common/keybindingLabels.i18n.json | 16 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../api/node/extHostExplorerView.i18n.json | 9 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeExplorers.i18n.json | 10 + .../api/node/extHostTreeView.i18n.json | 9 + .../api/node/extHostTreeViews.i18n.json | 10 + .../node/mainThreadExtensionService.i18n.json | 9 + .../node/mainThreadMessageService.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 9 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../quickopen.contribution.i18n.json | 12 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/crashReporter.i18n.json | 9 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../emmet/node/actions/balance.i18n.json | 9 + .../emmet/node/actions/editPoints.i18n.json | 9 + .../emmet/node/actions/evaluateMath.i18n.json | 8 + .../node/actions/expandAbbreviation.i18n.json | 8 + .../node/actions/incrementDecrement.i18n.json | 13 + .../emmet/node/actions/matchingPair.i18n.json | 8 + .../emmet/node/actions/mergeLines.i18n.json | 8 + .../node/actions/reflectCssValue.i18n.json | 8 + .../emmet/node/actions/removeTag.i18n.json | 8 + .../emmet/node/actions/selectItem.i18n.json | 9 + .../emmet/node/actions/splitJoinTag.i18n.json | 8 + .../node/actions/toggleComment.i18n.json | 8 + .../node/actions/updateImageSize.i18n.json | 8 + .../emmet/node/actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet/node/emmet.contribution.i18n.json | 13 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../keymapExtensions.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../browser/gitActions.contribution.i18n.json | 20 + .../parts/git/browser/gitActions.i18n.json | 46 + .../parts/git/browser/gitQuickOpen.i18n.json | 16 + .../parts/git/browser/gitServices.i18n.json | 30 + .../parts/git/browser/gitWidgets.i18n.json | 10 + .../gitWorkbenchContributions.i18n.json | 23 + .../views/changes/changesView.i18n.json | 14 + .../views/changes/changesViewer.i18n.json | 38 + .../views/disabled/disabledView.i18n.json | 8 + .../browser/views/empty/emptyView.i18n.json | 9 + .../views/gitless/gitlessView.i18n.json | 13 + .../git/browser/views/huge/hugeView.i18n.json | 10 + .../views/notroot/notrootView.i18n.json | 9 + .../noworkspace/noworkspaceView.i18n.json | 10 + .../git.contribution.i18n.json | 9 + .../git/electron-browser/gitActions.i18n.json | 12 + .../electron-main/askpassService.i18n.json | 8 + .../parts/git/node/git.lib.i18n.json | 9 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 8 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 8 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/ptb/extensions/css/package.i18n.json | 74 + i18n/ptb/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../ptb/extensions/git/out/commands.i18n.json | 77 + i18n/ptb/extensions/git/out/main.i18n.json | 11 + i18n/ptb/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/ptb/extensions/git/package.i18n.json | 63 + i18n/ptb/extensions/grunt/out/main.i18n.json | 8 + i18n/ptb/extensions/grunt/package.i18n.json | 8 + i18n/ptb/extensions/gulp/out/main.i18n.json | 8 + i18n/ptb/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/ptb/extensions/html/package.i18n.json | 29 + i18n/ptb/extensions/jake/out/main.i18n.json | 8 + i18n/ptb/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/ptb/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../ptb/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/ptb/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/ptb/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 12 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../ptb/src/vs/base/common/severity.i18n.json | 10 + i18n/ptb/src/vs/base/node/processes.i18n.json | 8 + i18n/ptb/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../browser/accessibility.i18n.json | 15 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../inspectTMScopes.i18n.json | 9 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../common/keybindingLabels.i18n.json | 16 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeViews.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 13 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../quickopen.contribution.i18n.json | 12 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/crashReporter.i18n.json | 9 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 10 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 10 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/rus/extensions/css/package.i18n.json | 74 + i18n/rus/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../rus/extensions/git/out/commands.i18n.json | 77 + i18n/rus/extensions/git/out/main.i18n.json | 11 + i18n/rus/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/rus/extensions/git/package.i18n.json | 63 + i18n/rus/extensions/grunt/out/main.i18n.json | 8 + i18n/rus/extensions/grunt/package.i18n.json | 8 + i18n/rus/extensions/gulp/out/main.i18n.json | 8 + i18n/rus/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/rus/extensions/html/package.i18n.json | 29 + i18n/rus/extensions/jake/out/main.i18n.json | 8 + i18n/rus/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/rus/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../rus/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/rus/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/rus/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 11 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + i18n/rus/src/vs/base/common/json.i18n.json | 16 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../rus/src/vs/base/common/severity.i18n.json | 10 + i18n/rus/src/vs/base/node/processes.i18n.json | 8 + i18n/rus/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/defaultConfig.i18n.json | 8 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../browser/accessibility.i18n.json | 15 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclaration.i18n.json | 24 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../inspectTMScopes.i18n.json | 9 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 32 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../common/keybindingLabels.i18n.json | 16 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../api/node/extHostExplorerView.i18n.json | 9 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeExplorers.i18n.json | 10 + .../api/node/extHostTreeView.i18n.json | 9 + .../api/node/extHostTreeViews.i18n.json | 10 + .../node/mainThreadExtensionService.i18n.json | 9 + .../node/mainThreadMessageService.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 9 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../quickopen.contribution.i18n.json | 12 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/crashReporter.i18n.json | 9 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../emmet/node/actions/balance.i18n.json | 9 + .../emmet/node/actions/editPoints.i18n.json | 9 + .../emmet/node/actions/evaluateMath.i18n.json | 8 + .../node/actions/expandAbbreviation.i18n.json | 8 + .../node/actions/incrementDecrement.i18n.json | 13 + .../emmet/node/actions/matchingPair.i18n.json | 8 + .../emmet/node/actions/mergeLines.i18n.json | 8 + .../node/actions/reflectCssValue.i18n.json | 8 + .../emmet/node/actions/removeTag.i18n.json | 8 + .../emmet/node/actions/selectItem.i18n.json | 9 + .../emmet/node/actions/splitJoinTag.i18n.json | 8 + .../node/actions/toggleComment.i18n.json | 8 + .../node/actions/updateImageSize.i18n.json | 8 + .../emmet/node/actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet/node/emmet.contribution.i18n.json | 13 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../keymapExtensions.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../browser/gitActions.contribution.i18n.json | 20 + .../parts/git/browser/gitActions.i18n.json | 46 + .../parts/git/browser/gitQuickOpen.i18n.json | 16 + .../parts/git/browser/gitServices.i18n.json | 30 + .../parts/git/browser/gitWidgets.i18n.json | 10 + .../gitWorkbenchContributions.i18n.json | 23 + .../views/changes/changesView.i18n.json | 14 + .../views/changes/changesViewer.i18n.json | 38 + .../views/disabled/disabledView.i18n.json | 8 + .../browser/views/empty/emptyView.i18n.json | 9 + .../views/gitless/gitlessView.i18n.json | 13 + .../git/browser/views/huge/hugeView.i18n.json | 10 + .../views/notroot/notrootView.i18n.json | 9 + .../noworkspace/noworkspaceView.i18n.json | 10 + .../git.contribution.i18n.json | 9 + .../git/electron-browser/gitActions.i18n.json | 12 + .../electron-main/askpassService.i18n.json | 8 + .../parts/git/node/git.lib.i18n.json | 9 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 8 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 8 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + .../out/extension.i18n.json | 8 + .../out/settingsDocumentHelper.i18n.json | 38 + .../css/client/out/cssMain.i18n.json | 8 + i18n/trk/extensions/css/package.i18n.json | 74 + i18n/trk/extensions/emmet/package.i18n.json | 48 + .../out/extensionLinter.i18n.json | 14 + .../out/packageDocumentHelper.i18n.json | 9 + .../extensions/git/out/askpass-main.i18n.json | 8 + .../trk/extensions/git/out/commands.i18n.json | 77 + i18n/trk/extensions/git/out/main.i18n.json | 11 + i18n/trk/extensions/git/out/model.i18n.json | 9 + .../extensions/git/out/repository.i18n.json | 31 + .../extensions/git/out/scmProvider.i18n.json | 8 + .../extensions/git/out/statusbar.i18n.json | 11 + i18n/trk/extensions/git/package.i18n.json | 63 + i18n/trk/extensions/grunt/out/main.i18n.json | 8 + i18n/trk/extensions/grunt/package.i18n.json | 8 + i18n/trk/extensions/gulp/out/main.i18n.json | 8 + i18n/trk/extensions/gulp/package.i18n.json | 8 + .../html/client/out/htmlMain.i18n.json | 8 + i18n/trk/extensions/html/package.i18n.json | 29 + i18n/trk/extensions/jake/out/main.i18n.json | 8 + i18n/trk/extensions/jake/package.i18n.json | 8 + .../features/bowerJSONContribution.i18n.json | 10 + .../packageJSONContribution.i18n.json | 13 + .../json/client/out/jsonMain.i18n.json | 8 + i18n/trk/extensions/json/package.i18n.json | 16 + .../markdown/out/extension.i18n.json | 8 + .../out/previewContentProvider.i18n.json | 10 + .../markdown/out/security.i18n.json | 15 + .../trk/extensions/markdown/package.i18n.json | 24 + .../out/codelensProvider.i18n.json | 11 + .../out/commandHandler.i18n.json | 13 + .../out/mergeDecorator.i18n.json | 9 + .../merge-conflict/package.i18n.json | 20 + i18n/trk/extensions/npm/package.i18n.json | 9 + .../out/features/validationProvider.i18n.json | 13 + i18n/trk/extensions/php/package.i18n.json | 14 + .../out/features/bufferSyncSupport.i18n.json | 12 + .../features/completionItemProvider.i18n.json | 9 + ...rectiveCommentCompletionProvider.i18n.json | 10 + .../implementationsCodeLensProvider.i18n.json | 10 + .../jsDocCompletionProvider.i18n.json | 8 + .../referencesCodeLensProvider.i18n.json | 10 + .../out/features/taskProvider.i18n.json | 9 + .../typescript/out/typescriptMain.i18n.json | 15 + .../out/typescriptServiceClient.i18n.json | 17 + .../typescript/out/utils/api.i18n.json | 8 + .../typescript/out/utils/logger.i18n.json | 8 + .../out/utils/projectStatus.i18n.json | 11 + .../out/utils/typingsStatus.i18n.json | 12 + .../out/utils/versionPicker.i18n.json | 11 + .../out/utils/versionProvider.i18n.json | 9 + .../extensions/typescript/package.i18n.json | 50 + .../browser/ui/actionbar/actionbar.i18n.json | 8 + .../vs/base/browser/ui/aria/aria.i18n.json | 8 + .../browser/ui/findinput/findInput.i18n.json | 8 + .../findinput/findInputCheckboxes.i18n.json | 10 + .../browser/ui/inputbox/inputBox.i18n.json | 10 + .../resourceviewer/resourceViewer.i18n.json | 16 + .../base/browser/ui/toolbar/toolbar.i18n.json | 8 + .../src/vs/base/common/errorMessage.i18n.json | 18 + .../base/common/jsonErrorMessages.i18n.json | 16 + .../vs/base/common/keybindingLabels.i18n.json | 16 + .../src/vs/base/common/processes.i18n.json | 11 + .../trk/src/vs/base/common/severity.i18n.json | 10 + i18n/trk/src/vs/base/node/processes.i18n.json | 8 + i18n/trk/src/vs/base/node/zip.i18n.json | 8 + .../browser/quickOpenModel.i18n.json | 9 + .../browser/quickOpenWidget.i18n.json | 9 + .../parts/tree/browser/treeDefaults.i18n.json | 8 + .../src/vs/code/electron-main/auth.i18n.json | 9 + .../src/vs/code/electron-main/menus.i18n.json | 185 + .../vs/code/electron-main/window.i18n.json | 8 + .../vs/code/electron-main/windows.i18n.json | 28 + .../src/vs/code/node/cliProcessMain.i18n.json | 17 + .../browser/widget/diffEditorWidget.i18n.json | 8 + .../browser/widget/diffReview.i18n.json | 15 + .../config/commonEditorConfig.i18n.json | 95 + .../common/config/editorOptions.i18n.json | 9 + .../editor/common/controller/cursor.i18n.json | 8 + .../model/textModelWithTokens.i18n.json | 8 + .../common/modes/modesRegistry.i18n.json | 8 + .../editor/common/services/bulkEdit.i18n.json | 11 + .../common/services/modeServiceImpl.i18n.json | 16 + .../services/modelServiceImpl.i18n.json | 9 + .../common/view/editorColorRegistry.i18n.json | 29 + .../common/bracketMatching.i18n.json | 8 + .../common/caretOperations.i18n.json | 9 + .../common/transpose.i18n.json | 8 + .../clipboard/browser/clipboard.i18n.json | 11 + .../contrib/comment/common/comment.i18n.json | 11 + .../contextmenu/browser/contextmenu.i18n.json | 8 + .../contrib/find/browser/findWidget.i18n.json | 21 + .../find/browser/simpleFindWidget.i18n.json | 12 + .../find/common/findController.i18n.json | 21 + .../contrib/folding/browser/folding.i18n.json | 14 + .../format/browser/formatActions.i18n.json | 13 + .../browser/goToDeclarationCommands.i18n.json | 23 + .../browser/goToDeclarationMouse.i18n.json | 8 + .../gotoError/browser/gotoError.i18n.json | 13 + .../contrib/hover/browser/hover.i18n.json | 8 + .../hover/browser/modesContentHover.i18n.json | 8 + .../common/inPlaceReplace.i18n.json | 9 + .../indentation/common/indentation.i18n.json | 15 + .../common/linesOperations.i18n.json | 25 + .../contrib/links/browser/links.i18n.json | 16 + .../multicursor/common/multicursor.i18n.json | 10 + .../browser/parameterHints.i18n.json | 8 + .../browser/parameterHintsWidget.i18n.json | 8 + .../browser/quickFixCommands.i18n.json | 10 + .../browser/referenceSearch.i18n.json | 9 + .../browser/referencesController.i18n.json | 8 + .../browser/referencesModel.i18n.json | 14 + .../browser/referencesWidget.i18n.json | 27 + .../contrib/rename/browser/rename.i18n.json | 11 + .../rename/browser/renameInputField.i18n.json | 8 + .../smartSelect/common/smartSelect.i18n.json | 9 + .../browser/suggestController.i18n.json | 9 + .../suggest/browser/suggestWidget.i18n.json | 21 + .../common/toggleTabFocusMode.i18n.json | 8 + .../common/wordHighlighter.i18n.json | 11 + .../browser/peekViewWidget.i18n.json | 8 + .../textMate/TMSyntax.i18n.json | 14 + ...guageConfigurationExtensionPoint.i18n.json | 23 + .../editor/node/textMate/TMGrammars.i18n.json | 13 + .../browser/menuItemActionItem.i18n.json | 8 + .../menusExtensionPoint.i18n.json | 43 + .../common/configurationRegistry.i18n.json | 12 + .../platform/environment/node/argv.i18n.json | 33 + .../extensionEnablementService.i18n.json | 8 + .../common/extensionManagement.i18n.json | 9 + .../node/extensionGalleryService.i18n.json | 9 + .../node/extensionManagementService.i18n.json | 22 + .../common/abstractExtensionService.i18n.json | 11 + .../common/extensionsRegistry.i18n.json | 30 + .../node/extensionValidator.i18n.json | 24 + .../historyMainService.i18n.json | 12 + .../node/integrityServiceImpl.i18n.json | 11 + .../jsonValidationExtensionPoint.i18n.json | 15 + .../abstractKeybindingService.i18n.json | 9 + .../markers/common/problemMatcher.i18n.json | 70 + .../platform/message/common/message.i18n.json | 10 + .../platform/request/node/request.i18n.json | 11 + .../common/telemetryService.i18n.json | 9 + .../common/colorExtensionPoint.i18n.json | 20 + .../theme/common/colorRegistry.i18n.json | 91 + .../workspaces/common/workspaces.i18n.json | 11 + .../mainThreadExtensionService.i18n.json | 9 + .../mainThreadMessageService.i18n.json | 10 + .../api/node/extHostDiagnostics.i18n.json | 8 + .../node/extHostExtensionActivator.i18n.json | 11 + .../workbench/api/node/extHostTask.i18n.json | 8 + .../api/node/extHostTreeViews.i18n.json | 10 + .../browser/actions/configureLocale.i18n.json | 13 + .../browser/actions/fileActions.i18n.json | 13 + .../toggleActivityBarVisibility.i18n.json | 9 + .../actions/toggleEditorLayout.i18n.json | 11 + .../actions/toggleSidebarPosition.i18n.json | 9 + .../actions/toggleSidebarVisibility.i18n.json | 9 + .../toggleStatusbarVisibility.i18n.json | 9 + .../browser/actions/toggleZenMode.i18n.json | 9 + .../actions/workspaceActions.i18n.json | 22 + .../activitybar/activitybarActions.i18n.json | 14 + .../activitybar/activitybarPart.i18n.json | 10 + .../browser/parts/compositePart.i18n.json | 9 + .../parts/editor/binaryDiffEditor.i18n.json | 8 + .../parts/editor/binaryEditor.i18n.json | 8 + .../editor/editor.contribution.i18n.json | 16 + .../parts/editor/editorActions.i18n.json | 56 + .../parts/editor/editorCommands.i18n.json | 12 + .../browser/parts/editor/editorPart.i18n.json | 14 + .../parts/editor/editorPicker.i18n.json | 13 + .../parts/editor/editorStatus.i18n.json | 51 + .../parts/editor/tabsTitleControl.i18n.json | 8 + .../parts/editor/textDiffEditor.i18n.json | 16 + .../browser/parts/editor/textEditor.i18n.json | 8 + .../parts/editor/textResourceEditor.i18n.json | 12 + .../parts/editor/titleControl.i18n.json | 15 + .../parts/panel/panelActions.i18n.json | 15 + .../browser/parts/panel/panelPart.i18n.json | 8 + .../quickopen/quickOpenController.i18n.json | 17 + .../parts/quickopen/quickopen.i18n.json | 12 + .../parts/sidebar/sidebarPart.i18n.json | 10 + .../parts/statusbar/statusbarPart.i18n.json | 9 + .../parts/titlebar/titlebarPart.i18n.json | 9 + .../vs/workbench/browser/quickopen.i18n.json | 10 + .../vs/workbench/browser/viewlet.i18n.json | 8 + .../src/vs/workbench/common/theme.i18n.json | 66 + .../electron-browser/actions.i18n.json | 46 + .../electron-browser/commands.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 12 + .../main.contribution.i18n.json | 71 + .../workbench/electron-browser/main.i18n.json | 9 + .../electron-browser/shell.i18n.json | 8 + .../electron-browser/window.i18n.json | 13 + .../electron-browser/workbench.i18n.json | 9 + .../node/extensionHostMain.i18n.json | 8 + .../workbench/node/extensionPoints.i18n.json | 11 + .../cli.contribution.i18n.json | 18 + .../electron-browser/accessibility.i18n.json | 26 + .../inspectKeybindings.i18n.json | 8 + .../inspectTMScopes.i18n.json | 9 + ...guageConfigurationExtensionPoint.i18n.json | 40 + .../textMate/inspectTMScopes.i18n.json | 9 + .../electron-browser/toggleMinimap.i18n.json | 8 + .../toggleMultiCursorModifier.i18n.json | 8 + .../toggleRenderControlCharacter.i18n.json | 8 + .../toggleRenderWhitespace.i18n.json | 8 + .../electron-browser/toggleWordWrap.i18n.json | 11 + .../wordWrapMigration.i18n.json | 11 + .../debug/browser/breakpointWidget.i18n.json | 13 + .../debug/browser/debugActionItems.i18n.json | 10 + .../debug/browser/debugActions.i18n.json | 49 + .../browser/debugActionsWidget.i18n.json | 8 + .../browser/debugContentProvider.i18n.json | 8 + .../browser/debugEditorActions.i18n.json | 15 + .../browser/debugEditorModelManager.i18n.json | 11 + .../debug/browser/debugQuickOpen.i18n.json | 11 + .../debug/browser/exceptionWidget.i18n.json | 11 + .../debug/browser/linkDetector.i18n.json | 9 + .../parts/debug/common/debug.i18n.json | 8 + .../parts/debug/common/debugModel.i18n.json | 9 + .../parts/debug/common/debugSource.i18n.json | 8 + .../debug.contribution.i18n.json | 24 + .../electron-browser/debugCommands.i18n.json | 8 + .../debugConfigurationManager.i18n.json | 38 + .../debugEditorContribution.i18n.json | 20 + .../electron-browser/debugHover.i18n.json | 8 + .../electron-browser/debugService.i18n.json | 24 + .../electron-browser/debugViewer.i18n.json | 28 + .../electron-browser/debugViews.i18n.json | 16 + .../electronDebugActions.i18n.json | 11 + .../rawDebugSession.i18n.json | 12 + .../debug/electron-browser/repl.i18n.json | 12 + .../electron-browser/replViewer.i18n.json | 12 + .../statusbarColorProvider.i18n.json | 10 + .../terminalSupport.i18n.json | 9 + .../parts/debug/node/debugAdapter.i18n.json | 23 + .../actions/showEmmetCommands.i18n.json | 8 + .../actions/balance.i18n.json | 9 + .../actions/editPoints.i18n.json | 9 + .../actions/evaluateMath.i18n.json | 8 + .../actions/expandAbbreviation.i18n.json | 8 + .../actions/incrementDecrement.i18n.json | 13 + .../actions/matchingPair.i18n.json | 8 + .../actions/mergeLines.i18n.json | 8 + .../actions/reflectCssValue.i18n.json | 8 + .../actions/removeTag.i18n.json | 8 + .../actions/selectItem.i18n.json | 9 + .../actions/splitJoinTag.i18n.json | 8 + .../actions/toggleComment.i18n.json | 8 + .../actions/updateImageSize.i18n.json | 8 + .../actions/updateTag.i18n.json | 10 + .../actions/wrapWithAbbreviation.i18n.json | 10 + .../emmet.contribution.i18n.json | 14 + .../execution.contribution.i18n.json | 17 + .../terminal.contribution.i18n.json | 15 + .../terminalService.i18n.json | 12 + .../treeExplorer.contribution.i18n.json | 14 + .../browser/treeExplorerActions.i18n.json | 8 + .../browser/treeExplorerService.i18n.json | 8 + .../browser/views/treeExplorerView.i18n.json | 8 + .../browser/dependenciesViewer.i18n.json | 9 + .../browser/extensionEditor.i18n.json | 44 + .../browser/extensionsActions.i18n.json | 65 + .../browser/extensionsQuickOpen.i18n.json | 10 + .../common/extensionsFileTemplate.i18n.json | 10 + .../common/extensionsInput.i18n.json | 8 + .../extensionTipsService.i18n.json | 16 + .../extensions.contribution.i18n.json | 15 + .../extensionsActions.i18n.json | 11 + .../extensionsUtils.i18n.json | 13 + .../extensionsViewlet.i18n.json | 18 + .../extensionsViews.i18n.json | 10 + .../node/extensionsWorkbenchService.i18n.json | 19 + .../electron-browser/feedback.i18n.json | 25 + .../editors/binaryFileEditor.i18n.json | 8 + .../browser/editors/textFileEditor.i18n.json | 11 + .../files/browser/explorerViewlet.i18n.json | 8 + .../fileActions.contribution.i18n.json | 11 + .../parts/files/browser/fileActions.i18n.json | 75 + .../files/browser/fileCommands.i18n.json | 9 + .../browser/files.contribution.i18n.json | 48 + .../files/browser/saveErrorHandler.i18n.json | 16 + .../files/browser/views/emptyView.i18n.json | 11 + .../browser/views/explorerView.i18n.json | 9 + .../browser/views/explorerViewer.i18n.json | 16 + .../browser/views/openEditorsView.i18n.json | 11 + .../browser/views/openEditorsViewer.i18n.json | 15 + .../files/common/dirtyFilesTracker.i18n.json | 8 + .../common/editors/fileEditorInput.i18n.json | 8 + .../html/browser/html.contribution.i18n.json | 9 + .../html/browser/htmlPreviewPart.i18n.json | 8 + .../parts/html/browser/webview.i18n.json | 8 + .../parts/markers/common/messages.i18n.json | 39 + .../markersElectronContributions.i18n.json | 8 + .../nps.contribution.i18n.json | 11 + .../browser/output.contribution.i18n.json | 10 + .../output/browser/outputActions.i18n.json | 11 + .../output/browser/outputPanel.i18n.json | 9 + .../parts/output/common/output.i18n.json | 9 + .../performance.contribution.i18n.json | 15 + .../browser/keybindingWidgets.i18n.json | 9 + .../browser/keybindingsEditor.i18n.json | 35 + .../keybindingsEditorContribution.i18n.json | 11 + .../preferences.contribution.i18n.json | 10 + .../browser/preferencesActions.i18n.json | 16 + .../browser/preferencesEditor.i18n.json | 17 + .../browser/preferencesRenderers.i18n.json | 20 + .../browser/preferencesService.i18n.json | 12 + .../browser/preferencesWidgets.i18n.json | 8 + .../common/keybindingsEditorModel.i18n.json | 11 + .../preferences/common/preferences.i18n.json | 9 + .../common/preferencesModels.i18n.json | 10 + .../browser/commandsHandler.i18n.json | 19 + .../browser/gotoLineHandler.i18n.json | 14 + .../browser/gotoSymbolHandler.i18n.json | 34 + .../quickopen/browser/helpHandler.i18n.json | 10 + .../browser/quickopen.contribution.i18n.json | 14 + .../browser/viewPickerHandler.i18n.json | 15 + .../relauncher.contribution.i18n.json | 12 + .../dirtydiffDecorator.i18n.json | 13 + .../scm.contribution.i18n.json | 11 + .../electron-browser/scmActivity.i18n.json | 8 + .../scm/electron-browser/scmMenus.i18n.json | 9 + .../scm/electron-browser/scmViewlet.i18n.json | 12 + .../browser/openAnythingHandler.i18n.json | 9 + .../search/browser/openFileHandler.i18n.json | 9 + .../browser/openSymbolHandler.i18n.json | 11 + .../browser/patternInputWidget.i18n.json | 10 + .../search/browser/replaceService.i18n.json | 8 + .../browser/search.contribution.i18n.json | 22 + .../search/browser/searchActions.i18n.json | 25 + .../browser/searchResultsView.i18n.json | 16 + .../search/browser/searchViewlet.i18n.json | 49 + .../search/browser/searchWidget.i18n.json | 15 + .../search/common/queryBuilder.i18n.json | 8 + .../electron-browser/TMSnippets.i18n.json | 14 + .../electron-browser/insertSnippet.i18n.json | 8 + .../snippets.contribution.i18n.json | 16 + .../snippetsService.i18n.json | 10 + .../electron-browser/tabCompletion.i18n.json | 8 + .../languageSurveys.contribution.i18n.json | 11 + .../nps.contribution.i18n.json | 11 + .../tasks/browser/buildQuickOpen.i18n.json | 10 + .../parts/tasks/browser/quickOpen.i18n.json | 12 + .../tasks/browser/restartQuickOpen.i18n.json | 10 + .../tasks/browser/taskQuickOpen.i18n.json | 10 + .../browser/terminateQuickOpen.i18n.json | 10 + .../tasks/browser/testQuickOpen.i18n.json | 10 + .../tasks/common/taskConfiguration.i18n.json | 17 + .../common/taskDefinitionRegistry.i18n.json | 11 + .../tasks/common/taskTemplates.i18n.json | 11 + .../tasks/common/taskTypeRegistry.i18n.json | 6 + .../jsonSchemaCommon.i18n.json | 39 + .../electron-browser/jsonSchema_v1.i18n.json | 14 + .../electron-browser/jsonSchema_v2.i18n.json | 48 + .../task.contribution.i18n.json | 77 + .../electron-browser/taskPanel.i18n.json | 8 + .../terminalTaskSystem.i18n.json | 12 + .../node/processRunnerDetector.i18n.json | 15 + .../tasks/node/processTaskSystem.i18n.json | 12 + .../tasks/node/taskConfiguration.i18n.json | 21 + .../browser/terminalQuickOpen.i18n.json | 12 + .../terminal.contribution.i18n.json | 34 + .../terminalActions.i18n.json | 43 + .../terminalColorRegistry.i18n.json | 12 + .../terminalConfigHelper.i18n.json | 10 + .../terminalFindWidget.i18n.json | 12 + .../terminalInstance.i18n.json | 11 + .../terminalLinkHandler.i18n.json | 10 + .../electron-browser/terminalPanel.i18n.json | 12 + .../terminalService.i18n.json | 15 + .../themes.contribution.i18n.json | 19 + ...edWorkspaceSettings.contribution.i18n.json | 11 + .../releaseNotesInput.i18n.json | 8 + .../update.contribution.i18n.json | 10 + .../update/electron-browser/update.i18n.json | 35 + .../parts/views/browser/views.i18n.json | 9 + .../browser/viewsExtensionPoint.i18n.json | 17 + .../electron-browser/watermark.i18n.json | 20 + .../overlay/browser/welcomeOverlay.i18n.json | 17 + .../vs_code_welcome_page.i18n.json | 42 + .../welcomePage.contribution.i18n.json | 13 + .../electron-browser/welcomePage.i18n.json | 38 + .../editor/editorWalkThrough.i18n.json | 9 + .../walkThrough.contribution.i18n.json | 10 + .../walkThroughActions.i18n.json | 11 + .../walkThroughPart.i18n.json | 10 + .../node/configuration.i18n.json | 20 + .../configurationEditingService.i18n.json | 22 + .../node/jsonEditingService.i18n.json | 9 + .../common/crashReporterService.i18n.json | 9 + .../editor/browser/editorService.i18n.json | 8 + .../electron-browser/extensionHost.i18n.json | 10 + .../extensionPoints.i18n.json | 11 + .../extensionService.i18n.json | 13 + .../electron-browser/fileService.i18n.json | 11 + .../services/files/node/fileService.i18n.json | 18 + .../common/keybindingEditing.i18n.json | 11 + .../keybindingService.i18n.json | 26 + .../message/browser/messageList.i18n.json | 14 + .../electron-browser/messageService.i18n.json | 9 + .../common/workbenchModeService.i18n.json | 25 + .../browser/progressService2.i18n.json | 9 + .../electron-browser/TMGrammars.i18n.json | 13 + .../electron-browser/TMSyntax.i18n.json | 14 + .../common/textFileEditorModel.i18n.json | 9 + .../textfile/common/textFileService.i18n.json | 8 + .../textFileService.i18n.json | 18 + .../themes/common/colorThemeSchema.i18n.json | 15 + .../common/fileIconThemeSchema.i18n.json | 37 + .../electron-browser/colorThemeData.i18n.json | 13 + .../workbenchThemeService.i18n.json | 41 + issue_template.md | 4 + jsconfig.json | 27 + npm-shrinkwrap.json | 880 ++ package.json | 172 + product.json | 32 + resources/darwin/bin/code.sh | 11 + resources/darwin/code.icns | Bin 0 -> 43649 bytes resources/darwin/code_file.icns | Bin 0 -> 68606 bytes resources/letterpress-dark.svg | 33 + resources/letterpress-hc.svg | 39 + resources/letterpress.svg | 39 + resources/linux/bin/code.sh | 36 + resources/linux/code.appdata.xml | 18 + resources/linux/code.desktop | 27 + resources/linux/code.png | Bin 0 -> 44657 bytes resources/linux/debian/control.template | 14 + resources/linux/debian/postinst.template | 78 + resources/linux/debian/postrm.template | 6 + resources/linux/debian/prerm.template | 7 + resources/linux/rpm/code.spec.template | 54 + resources/linux/rpm/code.xpm | 1040 ++ resources/linux/rpm/dependencies.json | 147 + resources/win32/bin/code.cmd | 6 + resources/win32/bin/code.sh | 23 + resources/win32/code.ico | Bin 0 -> 83328 bytes resources/win32/code_file.ico | Bin 0 -> 249613 bytes resources/win32/inno-big.bmp | Bin 0 -> 206040 bytes resources/win32/inno-small.bmp | Bin 0 -> 12816 bytes scripts/build.bat | 9 + scripts/build.sh | 5 + scripts/code-cli.bat | 40 + scripts/code-cli.sh | 41 + scripts/code.bat | 44 + scripts/code.sh | 47 + scripts/env.ps1 | 3 + scripts/env.sh | 6 + scripts/monaco-editor-setup.js | 38 + scripts/npm.bat | 8 + scripts/npm.sh | 29 + scripts/sql-cli.sh | 41 + scripts/sql.bat | 40 + scripts/sql.sh | 44 + scripts/test-electron.bat | 19 + scripts/test-electron.sh | 42 + scripts/test-int-mocha.bat | 1 + scripts/test-int-mocha.sh | 1 + scripts/test-integration.bat | 20 + scripts/test-integration.sh | 20 + scripts/test-mocha.bat | 25 + scripts/test-mocha.sh | 42 + scripts/test.bat | 25 + scripts/test.sh | 42 + scripts/watch.bat | 4 + scripts/watch.sh | 2 + src/.eslintrc | 19 + src/bootstrap-amd.js | 43 + src/bootstrap.js | 143 + src/buildfile.js | 18 + src/cli.js | 6 + src/main.js | 228 + src/paths.js | 24 + .../ui/breadcrumb/breadcrumb.component.ts | 58 + .../base/browser/ui/breadcrumb/interfaces.ts | 29 + .../ui/breadcrumb/media/breadcrumb.css | 32 + .../ui/breadcrumb/media/chevron_right.svg | 1 + .../media/chevron_right_inverse.svg | 1 + src/sql/base/browser/ui/checkbox/checkbox.ts | 54 + .../browser/ui/checkbox/defaultCheckbox.ts | 71 + .../base/browser/ui/checkbox/media/check.svg | 1 + .../ui/checkbox/media/check_inverse.svg | 1 + .../browser/ui/checkbox/media/checkbox.css | 24 + .../browser/ui/dropdownList/dropdownList.ts | 146 + .../ui/dropdownList/media/dropdownList.css | 34 + .../ui/dropdownList/media/dropdownarrow.svg | 1 + .../media/dropdownarrow_inverse.svg | 1 + .../browser/ui/editableDropdown/actions.ts | 23 + .../browser/ui/editableDropdown/dropdown.ts | 307 + .../editableDropdown/media/dropdownList.css | 23 + .../editableDropdown/media/dropdownarrow.svg | 1 + .../media/dropdownarrow_inverse.svg | 1 + src/sql/base/browser/ui/inputBox/inputBox.ts | 81 + src/sql/base/browser/ui/listBox/listBox.ts | 261 + src/sql/base/browser/ui/modal/dialogHelper.ts | 106 + src/sql/base/browser/ui/modal/media/back.svg | 1 + .../browser/ui/modal/media/back_inverse.svg | 1 + src/sql/base/browser/ui/modal/media/modal.css | 178 + .../browser/ui/modal/media/optionsDialog.css | 60 + src/sql/base/browser/ui/modal/modal.ts | 432 + .../base/browser/ui/modal/optionsDialog.ts | 268 + .../browser/ui/modal/optionsDialogHelper.ts | 189 + src/sql/base/browser/ui/panel/media/panel.css | 58 + .../base/browser/ui/panel/panel.component.ts | 98 + src/sql/base/browser/ui/panel/panel.module.ts | 16 + src/sql/base/browser/ui/panel/panel.ts | 166 + src/sql/base/browser/ui/panel/panelStyles.ts | 70 + .../base/browser/ui/panel/tab.component.ts | 35 + .../base/browser/ui/selectBox/selectBox.ts | 225 + .../base/browser/ui/table/media/sort-asc.gif | Bin 0 -> 830 bytes .../base/browser/ui/table/media/sort-desc.gif | Bin 0 -> 833 bytes src/sql/base/browser/ui/table/media/table.css | 34 + .../table/plugins/autoSizeColumns.plugin.ts | 137 + .../plugins/checkboxSelectColumn.plugin.ts | 189 + .../plugins/dragCellSelectionModel.plugin.ts | 364 + .../table/plugins/rowSelectionModel.plugin.ts | 164 + src/sql/base/browser/ui/table/table.ts | 273 + .../base/browser/ui/table/tableDataView.ts | 157 + src/sql/base/browser/ui/table/tableView.ts | 121 + src/sql/base/browser/ui/taskbar/actionbar.ts | 381 + .../ui/taskbar/media/change_connection.svg | 1 + .../media/change_connection_inverse.svg | 1 + .../base/browser/ui/taskbar/media/connect.svg | 1 + .../ui/taskbar/media/connect_inverse.svg | 1 + .../browser/ui/taskbar/media/copy_image.svg | 1 + .../ui/taskbar/media/copy_image_inverse.svg | 1 + .../ui/taskbar/media/create_insight.svg | 1 + .../taskbar/media/create_insight_inverse.svg | 1 + .../browser/ui/taskbar/media/disconnect.svg | 1 + .../ui/taskbar/media/disconnect_inverse.svg | 1 + .../ui/taskbar/media/ellipsis-inverse.svg | 1 + .../browser/ui/taskbar/media/ellipsis.svg | 1 + .../base/browser/ui/taskbar/media/icons.css | 91 + .../ui/taskbar/media/query-plan-inverse.svg | 1 + .../browser/ui/taskbar/media/query-plan.svg | 1 + .../ui/taskbar/media/save_as_image.svg | 1 + .../taskbar/media/save_as_image_inverse.svg | 1 + .../base/browser/ui/taskbar/media/start.svg | 1 + .../base/browser/ui/taskbar/media/stop.svg | 1 + .../base/browser/ui/taskbar/media/taskbar.css | 85 + src/sql/base/browser/ui/taskbar/taskbar.ts | 141 + src/sql/base/common/async.ts | 83 + src/sql/base/common/decorators.ts | 48 + src/sql/base/common/log.ts | 18 + src/sql/base/common/objects.ts | 34 + src/sql/base/common/promise.ts | 20 + src/sql/base/node/rangy.ts | 9 + src/sql/code/electron-main/oauth.ts | 103 + src/sql/common/browser/sqlOAuthServiceImpl.ts | 45 + src/sql/common/constants.ts | 15 + src/sql/common/pathUtilities.ts | 45 + src/sql/common/sqlOAuthService.ts | 34 + src/sql/common/telemetryKeys.ts | 40 + src/sql/common/telemetryUtilities.ts | 78 + src/sql/common/theme/colors.ts | 13 + src/sql/common/theme/styler.ts | 227 + src/sql/common/urlSerializer.ts | 27 + src/sql/data.d.ts | 1313 ++ src/sql/media/actionBarLabel.css | 34 + src/sql/media/icons/backup.svg | 1 + src/sql/media/icons/backup_inverse.svg | 1 + src/sql/media/icons/close-dark.svg | 1 + src/sql/media/icons/close.svg | 1 + src/sql/media/icons/common-icons.css | 174 + src/sql/media/icons/configdashboard.svg | 1 + .../media/icons/configdashboard_inverse.svg | 1 + src/sql/media/icons/database.svg | 1 + src/sql/media/icons/database_inverse.svg | 1 + src/sql/media/icons/ellipsis-inverse.svg | 1 + src/sql/media/icons/ellipsis.svg | 1 + src/sql/media/icons/file.svg | 1 + src/sql/media/icons/file_inverse.svg | 1 + src/sql/media/icons/filter.svg | 1 + src/sql/media/icons/filter_inverse.svg | 1 + src/sql/media/icons/loading.svg | 31 + src/sql/media/icons/loading_inverse.svg | 31 + src/sql/media/icons/new_database.svg | 1 + src/sql/media/icons/new_database_inverse.svg | 1 + src/sql/media/icons/newquery.svg | 1 + src/sql/media/icons/newquery_inverse.svg | 1 + src/sql/media/icons/refresh.svg | 1 + src/sql/media/icons/refresh_inverse.svg | 1 + src/sql/media/icons/remove.svg | 1 + src/sql/media/icons/remove_inverse.svg | 1 + src/sql/media/icons/restore.svg | 1 + src/sql/media/icons/restore_inverse.svg | 1 + src/sql/media/icons/run_history_inverse.svg | 1 + src/sql/media/icons/script_to_clipboard.svg | 1 + src/sql/media/icons/search_inverse.svg | 1 + src/sql/media/icons/server_page.svg | 1 + src/sql/media/icons/server_page_inverse.svg | 1 + src/sql/media/icons/sourcecontrol_inverse.svg | 1 + src/sql/media/icons/status_cancelled.svg | 1 + src/sql/media/icons/status_error.svg | 1 + src/sql/media/icons/status_info.svg | 1 + src/sql/media/icons/status_success.svg | 1 + src/sql/media/icons/status_warning.svg | 1 + .../media/objectTypes/AggregateFunction.svg | 1 + .../AggregateFunctionParameter_Input.svg | 1 + .../AggregateFunctionParameter_Output.svg | 1 + .../AggregateFunctionParameter_Return.svg | 1 + src/sql/media/objectTypes/ApplicationRole.svg | 1 + src/sql/media/objectTypes/Assembly.svg | 1 + src/sql/media/objectTypes/AsymmetricKey.svg | 1 + src/sql/media/objectTypes/BrokerPriority.svg | 1 + src/sql/media/objectTypes/Certificate.svg | 1 + src/sql/media/objectTypes/Column.svg | 1 + .../media/objectTypes/ColumnEncryptionKey.svg | 1 + src/sql/media/objectTypes/ColumnMasterKey.svg | 1 + src/sql/media/objectTypes/Constraint.svg | 1 + src/sql/media/objectTypes/Contract.svg | 1 + src/sql/media/objectTypes/Database.svg | 1 + .../DatabaseAndQueueEventNotification.svg | 1 + .../DatabaseAuditSpecification.svg | 1 + .../objectTypes/DatabaseEncryptionKey.svg | 1 + src/sql/media/objectTypes/DatabaseRole.svg | 1 + .../objectTypes/DatabaseScopedCredential.svg | 1 + src/sql/media/objectTypes/DatabaseTrigger.svg | 18 + .../objectTypes/Database_Unavailable.svg | 1 + src/sql/media/objectTypes/DefaultIcon.svg | 1 + .../media/objectTypes/ExternalDataSource.svg | 1 + .../media/objectTypes/ExternalFileFormat.svg | 1 + src/sql/media/objectTypes/FileGroupFile.svg | 1 + src/sql/media/objectTypes/Folder.svg | 1 + src/sql/media/objectTypes/FullTextCatalog.svg | 1 + .../media/objectTypes/FullTextStopList.svg | 1 + src/sql/media/objectTypes/Index.svg | 1 + src/sql/media/objectTypes/Key_ForeignKey.svg | 1 + src/sql/media/objectTypes/Key_PrimaryKey.svg | 1 + src/sql/media/objectTypes/Key_UniqueKey.svg | 1 + src/sql/media/objectTypes/MasterKey.svg | 1 + src/sql/media/objectTypes/MessageType.svg | 1 + .../media/objectTypes/PartitionFunction.svg | 1 + src/sql/media/objectTypes/PartitionScheme.svg | 1 + src/sql/media/objectTypes/Queue.svg | 1 + .../objectTypes/RemoteServiceBinding.svg | 1 + src/sql/media/objectTypes/Route.svg | 1 + .../objectTypes/ScalarValuedFunction.svg | 1 + .../ScalarValuedFunctionParameter_Input.svg | 1 + .../ScalarValuedFunctionParameter_Output.svg | 1 + .../ScalarValuedFunctionParameter_Return.svg | 1 + src/sql/media/objectTypes/Schema.svg | 25 + .../media/objectTypes/SearchPropertyList.svg | 1 + src/sql/media/objectTypes/SecurityPolicy.svg | 27 + src/sql/media/objectTypes/Sequence.svg | 1 + src/sql/media/objectTypes/Server.svg | 1 + .../objectTypes/ServerLevelCredential.svg | 1 + .../ServerLevelCryptographicProvider.svg | 1 + .../media/objectTypes/ServerLevelEndpoint.svg | 1 + .../objectTypes/ServerLevelLinkedServer.svg | 1 + .../ServerLevelLinkedServerLogin.svg | 1 + .../ServerLevelLinkedServerLogin_Disabled.svg | 1 + .../media/objectTypes/ServerLevelLogin.svg | 1 + .../objectTypes/ServerLevelLogin_Disabled.svg | 1 + .../objectTypes/ServerLevelServerAudit.svg | 1 + .../ServerLevelServerAuditSpecification.svg | 1 + .../objectTypes/ServerLevelServerRole.svg | 1 + .../objectTypes/ServerLevelServerTrigger.svg | 1 + src/sql/media/objectTypes/Service.svg | 1 + src/sql/media/objectTypes/SqlLogFile.svg | 1 + src/sql/media/objectTypes/Statistic.svg | 1 + src/sql/media/objectTypes/StoredProcedure.svg | 1 + .../StoredProcedureParameter_Input.svg | 1 + .../StoredProcedureParameter_Output.svg | 1 + .../StoredProcedureParameter_Return.svg | 1 + src/sql/media/objectTypes/SymmetricKey.svg | 1 + src/sql/media/objectTypes/Synonym.svg | 1 + .../objectTypes/SystemApproximateNumeric.svg | 1 + .../media/objectTypes/SystemBinaryString.svg | 1 + .../objectTypes/SystemCharacterString.svg | 1 + .../media/objectTypes/SystemClrDataType.svg | 1 + src/sql/media/objectTypes/SystemContract.svg | 1 + .../media/objectTypes/SystemDateAndTime.svg | 1 + .../media/objectTypes/SystemExactNumeric.svg | 1 + .../media/objectTypes/SystemMessageType.svg | 1 + .../media/objectTypes/SystemOtherDataType.svg | 1 + src/sql/media/objectTypes/SystemQueue.svg | 1 + src/sql/media/objectTypes/SystemService.svg | 1 + .../objectTypes/SystemSpatialDataType.svg | 1 + .../SystemUnicodeCharacterString.svg | 1 + src/sql/media/objectTypes/Table.svg | 1 + .../media/objectTypes/TableValuedFunction.svg | 1 + .../TableValuedFunctionParameter_Input.svg | 1 + .../TableValuedFunctionParameter_Output.svg | 1 + .../TableValuedFunctionParameter_Return.svg | 1 + src/sql/media/objectTypes/Table_Temporal.svg | 15 + src/sql/media/objectTypes/Trigger.svg | 1 + .../media/objectTypes/Trigger_Disabled.svg | 1 + src/sql/media/objectTypes/User.svg | 1 + .../media/objectTypes/UserDefinedDataType.svg | 1 + .../objectTypes/UserDefinedTableType.svg | 1 + .../UserDefinedTableTypeColumn.svg | 1 + .../UserDefinedTableTypeConstraint.svg | 1 + src/sql/media/objectTypes/UserDefinedType.svg | 1 + src/sql/media/objectTypes/User_Disabled.svg | 1 + src/sql/media/objectTypes/View.svg | 1 + .../media/objectTypes/XmlSchemaCollection.svg | 1 + src/sql/media/objectTypes/objecttypes.css | 640 + .../accountDialog/accountDialog.ts | 230 + .../accountDialog/accountDialogController.ts | 51 + .../accountDialog/accountViewModel.ts | 74 + .../accountDialog/media/accountDialog.css | 77 + .../accountListStatusbarItem.ts | 64 + .../media/accountListStatusbarItem.css | 11 + .../media/accounts_statusbar_inverse.svg | 1 + .../accountPicker/accountPicker.ts | 232 + .../accountPicker/accountPickerService.ts | 65 + .../accountPicker/accountPickerViewModel.ts | 50 + .../accountPicker/media/accountPicker.css | 99 + .../common/accountActions.ts | 158 + .../common/accountListRenderer.ts | 125 + .../accountManagement/common/interfaces.ts | 61 + .../common/media/accountActions.css | 14 + .../common/media/accountListRenderer.css | 26 + .../common/media/new_account.svg | 1 + .../common/media/new_account_inverse.svg | 1 + .../common/resourceProviderService.ts | 114 + .../firewallRuleDialog/firewallRuleDialog.ts | 309 + .../firewallRuleDialogController.ts | 99 + .../firewallRuleViewModel.ts | 67 + .../media/firewallRuleDialog.css | 86 + .../firewallRuleDialog/media/secure.svg | 1 + src/sql/parts/admin/common/adminService.ts | 138 + .../create/createDatabase.component.html | 132 + .../create/createDatabase.component.ts | 98 + .../admin/security/createLogin.component.html | 34 + .../admin/security/createLogin.component.ts | 30 + .../admin/security/createLogin.module.ts | 39 + .../parts/admin/security/createLoginEditor.ts | 114 + .../parts/admin/security/createLoginInput.ts | 57 + src/sql/parts/common/customInputConverter.ts | 167 + src/sql/parts/common/rxjsUtils.ts | 16 + .../common/connection.contribution.ts | 89 + src/sql/parts/connection/common/connection.ts | 27 + .../connection/common/connectionActions.ts | 59 + .../connection/common/connectionConfig.ts | 543 + .../common/connectionGlobalStatus.ts | 40 + .../parts/connection/common/connectionInfo.ts | 36 + .../connection/common/connectionManagement.ts | 341 + .../common/connectionManagementInfo.ts | 73 + .../common/connectionManagementService.ts | 1311 ++ .../connection/common/connectionProfile.ts | 227 + .../common/connectionProfileGroup.ts | 183 + .../connection/common/connectionStatus.ts | 160 + .../common/connectionStatusManager.ts | 212 + .../connection/common/connectionStore.ts | 616 + src/sql/parts/connection/common/constants.ts | 27 + .../connection/common/iconnectionConfig.ts | 32 + src/sql/parts/connection/common/interfaces.ts | 34 + .../connection/common/localizedConstants.ts | 10 + .../common/providerConnectionInfo.ts | 241 + src/sql/parts/connection/common/utils.ts | 152 + .../advancedPropertiesController.ts | 68 + .../connectionDialog/connectionController.ts | 138 + .../connectionDialogService.ts | 338 + .../connectionDialogWidget.ts | 301 + .../connectionDialog/connectionWidget.ts | 482 + .../media/connectionDialog.css | 49 + .../connectionDialog/media/sqlConnection.css | 10 + src/sql/parts/dashboard/common/actions.ts | 29 + .../common/componentHost.directive.ts | 13 + .../common/dashboardPage.component.html | 16 + .../common/dashboardPage.component.ts | 300 + .../parts/dashboard/common/dashboardWidget.ts | 38 + .../dashboardWidgetWrapper.component.html | 18 + .../dashboardWidgetWrapper.component.ts | 215 + src/sql/parts/dashboard/common/interfaces.ts | 14 + .../parts/dashboard/dashboard.component.html | 12 + .../parts/dashboard/dashboard.component.ts | 58 + src/sql/parts/dashboard/dashboard.module.ts | 106 + .../dashboard/dashboardConfig.contribution.ts | 22 + src/sql/parts/dashboard/dashboardEditor.ts | 112 + src/sql/parts/dashboard/dashboardInput.ts | 170 + .../pages/databaseDashboardPage.component.ts | 59 + .../databaseDashboardPage.contribution.ts | 158 + .../pages/serverDashboardPage.component.ts | 58 + .../pages/serverDashboardPage.contribution.ts | 163 + .../dashboard/services/breadcrumb.service.ts | 74 + .../dashboardServiceInterface.service.ts | 274 + .../widgets/explorer/explorerActions.ts | 89 + .../explorer/explorerWidget.component.html | 12 + .../explorer/explorerWidget.component.ts | 394 + .../explorer/explorerWidget.contribution.ts | 13 + .../widgets/explorer/media/explorerWidget.css | 14 + .../dashboard/widgets/insights/actions.ts | 37 + .../insights/insightsWidget.component.ts | 259 + .../insights/insightsWidget.contribution.ts | 50 + .../widgets/insights/insightsWidgetSchemas.ts | 132 + .../dashboard/widgets/insights/interfaces.ts | 60 + .../views/charts/chartInsight.component.ts | 286 + .../views/charts/chartInsight.contribution.ts | 40 + .../views/charts/types/barChart.component.ts | 47 + .../charts/types/barChart.contribution.ts | 18 + .../charts/types/doughnutChart.component.ts | 11 + .../types/doughnutChart.contribution.ts | 18 + .../types/horizontalBarChart.component.ts | 11 + .../types/horizontalBarChart.contribution.ts | 18 + .../views/charts/types/lineChart.component.ts | 98 + .../charts/types/lineChart.contribution.ts | 28 + .../views/charts/types/pieChart.component.ts | 10 + .../charts/types/pieChart.contribution.ts | 18 + .../charts/types/scatterChart.component.ts | 16 + .../charts/types/scatterChart.contribution.ts | 17 + .../charts/types/timeSeriesChart.component.ts | 67 + .../types/timeSeriesChart.contribution.ts | 17 + .../insights/views/countInsight.component.ts | 30 + .../views/countInsight.contribution.ts | 17 + .../insights/views/imageInsight.component.ts | 80 + .../views/imageInsight.contribution.ts | 31 + .../widgets/properties/propertiesJson.ts | 108 + .../propertiesWidget.component.html | 21 + .../properties/propertiesWidget.component.ts | 265 + .../widgets/tasks/tasksWidget.component.html | 21 + .../widgets/tasks/tasksWidget.component.ts | 117 + .../widgets/tasks/tasksWidget.contribution.ts | 22 + .../backup/backup.component.html | 182 + .../backup/backup.component.ts | 921 ++ .../disasterRecovery/backup/backup.module.ts | 45 + .../disasterRecovery/backup/backupDialog.ts | 105 + .../disasterRecovery/backup/constants.ts | 48 + .../backup/media/backupDialog.css | 58 + .../common/disasterRecoveryService.ts | 149 + .../common/disasterRecoveryUiService.ts | 118 + .../disasterRecovery/common/interfaces.ts | 89 + .../restore/media/restoreDialog.css | 73 + .../restore/mssqlRestoreInfo.ts | 98 + .../disasterRecovery/restore/restoreDialog.ts | 815 ++ .../restore/restoreDialogController.ts | 228 + .../restore/restoreViewModel.ts | 309 + .../parts/editData/common/editDataInput.ts | 183 + .../parts/editData/editor/editDataEditor.ts | 359 + .../editData/execution/editDataActions.ts | 201 + .../fileBrowser/common/fileBrowserService.ts | 235 + .../fileBrowser/common/fileBrowserTree.ts | 16 + src/sql/parts/fileBrowser/common/fileNode.ts | 72 + .../common/fileValidationServiceConstants.ts | 11 + .../parts/fileBrowser/common/interfaces.ts | 74 + .../fileBrowser/fileBrowserController.ts | 56 + .../fileBrowser/fileBrowserDataSource.ts | 73 + .../parts/fileBrowser/fileBrowserDialog.ts | 243 + .../fileBrowserDialogController.ts | 40 + .../parts/fileBrowser/fileBrowserRenderer.ts | 78 + .../parts/fileBrowser/fileBrowserTreeView.ts | 171 + .../parts/fileBrowser/fileBrowserViewModel.ts | 66 + .../fileBrowser/media/fileBrowserDialog.css | 58 + .../parts/grid/common/gridContentEvents.ts | 19 + src/sql/parts/grid/common/interfaces.ts | 124 + .../grid/directives/mousedown.directive.ts | 26 + .../parts/grid/directives/scroll.directive.ts | 24 + src/sql/parts/grid/load/css/qp.css | 220 + src/sql/parts/grid/load/css/qp_icons.png | Bin 0 -> 63962 bytes src/sql/parts/grid/load/loadJquery.js | 2 + src/sql/parts/grid/media/collapsedArrow.svg | 1 + .../grid/media/collapsedArrow_inverse.svg | 1 + src/sql/parts/grid/media/exitFullScreen.svg | 1 + .../grid/media/exitFullScreen_inverse.svg | 1 + src/sql/parts/grid/media/extendFullScreen.svg | 1 + .../grid/media/extendFullScreen_inverse.svg | 1 + src/sql/parts/grid/media/flexbox.css | 79 + src/sql/parts/grid/media/saveCsv.svg | 1 + src/sql/parts/grid/media/saveCsv_inverse.svg | 1 + src/sql/parts/grid/media/saveExcel.svg | 1 + .../parts/grid/media/saveExcel_inverse.svg | 1 + src/sql/parts/grid/media/saveJson.svg | 1 + src/sql/parts/grid/media/saveJson_inverse.svg | 1 + src/sql/parts/grid/media/slick.grid.css | 190 + src/sql/parts/grid/media/slickColorTheme.css | 390 + src/sql/parts/grid/media/slickGrid.css | 116 + src/sql/parts/grid/media/styles.css | 104 + src/sql/parts/grid/media/uncollapsedArrow.svg | 1 + .../grid/media/uncollapsedArrow_inverse.svg | 1 + src/sql/parts/grid/media/viewChart.svg | 1 + .../parts/grid/media/viewChart_inverse.svg | 1 + src/sql/parts/grid/services/dataService.ts | 211 + src/sql/parts/grid/services/sharedServices.ts | 57 + .../views/editData/editData.component.html | 34 + .../grid/views/editData/editData.component.ts | 496 + .../grid/views/editData/editData.module.ts | 45 + .../views/editData/editDataGridActions.ts | 68 + .../grid/views/editData/media/editData.css | 8 + src/sql/parts/grid/views/gridActions.ts | 163 + src/sql/parts/grid/views/gridCommands.ts | 76 + .../parts/grid/views/gridParentComponent.ts | 554 + .../views/query/chartViewer.component.html | 57 + .../grid/views/query/chartViewer.component.ts | 347 + .../parts/grid/views/query/chartViewer.css | 56 + .../grid/views/query/chartViewerActions.ts | 105 + .../grid/views/query/query.component.html | 79 + .../parts/grid/views/query/query.component.ts | 603 + .../insights/browser/insightsDialogView.ts | 381 + .../insights/browser/media/insightsDialog.css | 33 + .../insights/common/insightDialogActions.ts | 28 + .../insights/common/insightsDialogModel.ts | 128 + src/sql/parts/insights/common/interfaces.ts | 40 + .../parts/insights/insightsDialogService.ts | 42 + .../insights/node/insightsDialogController.ts | 160 + .../profiler/contrib/profiler.contribution.ts | 137 + .../contrib/profilerActions.contribution.ts | 32 + .../parts/profiler/contrib/profilerActions.ts | 244 + .../contrib/profilerWorkbenchActions.ts | 32 + .../profiler/dialog/media/profilerDialog.css | 13 + .../dialog/profilerColumnEditorDialog.ts | 409 + .../profiler/editor/controller/interfaces.ts | 9 + .../editor/controller/profilerFindWidget.ts | 647 + .../editor/controller/profilerTableEditor.ts | 217 + src/sql/parts/profiler/editor/interfaces.ts | 10 + .../parts/profiler/editor/profilerEditor.ts | 452 + .../parts/profiler/editor/profilerInput.ts | 136 + .../profiler/editor/profilerResourceEditor.ts | 96 + .../parts/profiler/editor/profilerState.ts | 116 + src/sql/parts/profiler/service/interfaces.ts | 103 + .../parts/profiler/service/profilerService.ts | 125 + .../profiler/service/profilerTestBackend.ts | 100 + src/sql/parts/profiler/service/testData.tsv | 3320 +++++ src/sql/parts/query/common/constants.ts | 10 + src/sql/parts/query/common/flavorStatus.ts | 218 + .../parts/query/common/localizedConstants.ts | 49 + .../parts/query/common/query.contribution.ts | 262 + src/sql/parts/query/common/queryContext.ts | 21 + .../parts/query/common/queryEditorService.ts | 35 + src/sql/parts/query/common/queryInput.ts | 249 + src/sql/parts/query/common/queryManagement.ts | 306 + .../parts/query/common/queryResultsInput.ts | 109 + .../parts/query/common/resultSerializer.ts | 300 + .../query/editor/editorDescriptorService.ts | 26 + .../query/editor/media/binarydiffeditor.css | 20 + .../editor/media/close-dirty-inverse.svg | 1 + .../parts/query/editor/media/close-dirty.svg | 1 + .../query/editor/media/close-inverse.svg | 1 + src/sql/parts/query/editor/media/close.svg | 1 + .../editor/media/editorGroupsControl.css | 152 + .../parts/query/editor/media/editorpart.css | 41 + .../parts/query/editor/media/editorpicker.css | 8 + .../parts/query/editor/media/editorstatus.css | 26 + .../query/editor/media/letterpress-dark.svg | 1 + .../query/editor/media/letterpress-hc.svg | 1 + .../parts/query/editor/media/letterpress.svg | 1 + .../query/editor/media/next-diff-inverse.svg | 1 + .../parts/query/editor/media/next-diff.svg | 1 + .../parts/query/editor/media/notabstitle.css | 47 + .../parts/query/editor/media/parseQuery.svg | 1 + .../editor/media/previous-diff-inverse.svg | 1 + .../query/editor/media/previous-diff.svg | 1 + .../parts/query/editor/media/queryEditor.css | 24 + .../query/editor/media/runWithoutDebug.svg | 1 + .../query/editor/media/sidebysideEditor.css | 12 + .../media/split-editor-horizontal-inverse.svg | 1 + .../editor/media/split-editor-horizontal.svg | 1 + .../media/split-editor-vertical-inverse.svg | 1 + .../editor/media/split-editor-vertical.svg | 1 + .../query/editor/media/stackview-inverse.svg | 20 + .../parts/query/editor/media/stackview.svg | 20 + .../parts/query/editor/media/tabstitle.css | 220 + .../query/editor/media/textdiffeditor.css | 22 + .../parts/query/editor/media/titlecontrol.css | 127 + src/sql/parts/query/editor/queryEditor.ts | 855 ++ .../parts/query/editor/queryResultsEditor.ts | 87 + .../query/execution/keyboardQueryActions.ts | 117 + src/sql/parts/query/execution/queryActions.ts | 543 + src/sql/parts/query/execution/queryModel.ts | 71 + .../query/execution/queryModelService.ts | 529 + src/sql/parts/query/execution/queryRunner.ts | 525 + src/sql/parts/query/execution/queryStatus.ts | 118 + .../query/services/queryEditorService.ts | 337 + src/sql/parts/query/views/flexibleSash.ts | 271 + .../query/views/queryOutput.component.html | 33 + .../query/views/queryOutput.component.ts | 110 + .../parts/query/views/queryOutput.module.ts | 75 + src/sql/parts/queryPlan/planXmlParser.ts | 295 + .../parts/queryPlan/queryPlan.component.ts | 69 + src/sql/parts/queryPlan/queryPlan.module.ts | 38 + src/sql/parts/queryPlan/queryPlanEditor.ts | 127 + src/sql/parts/queryPlan/queryPlanInput.ts | 67 + .../queryPlan/topOperations.component.ts | 121 + .../parts/registeredServer/common/nodeType.ts | 97 + .../common/objectExplorerService.ts | 352 + .../common/registeredServer.contribution.ts | 86 + .../parts/registeredServer/common/treeNode.ts | 123 + .../media/serverGroupDialog.css | 19 + .../serverGroup.contribution.ts | 35 + .../serverGroupController.ts | 114 + .../serverGroupDialog/serverGroupDialog.ts | 388 + .../serverGroupDialog/serverGroupViewModel.ts | 70 + .../viewlet/connectionTreeAction.ts | 453 + .../viewlet/connectionViewlet.ts | 154 + .../viewlet/dragAndDropController.ts | 206 + .../viewlet/media/add_server.svg | 1 + .../viewlet/media/add_server_inverse.svg | 1 + .../viewlet/media/collapsed-dark.svg | 1 + .../viewlet/media/connected_active_server.svg | 1 + .../media/connected_active_server_inverse.svg | 1 + .../viewlet/media/connectionViewlet.css | 136 + .../viewlet/media/disconnected_server.svg | 1 + .../media/disconnected_server_inverse.svg | 1 + .../viewlet/media/expanded-dark.svg | 1 + .../viewlet/media/new_servergroup.svg | 1 + .../viewlet/media/new_servergroup_inverse.svg | 1 + .../viewlet/media/serverTreeActions.css | 32 + .../viewlet/objectExplorerActions.ts | 370 + .../viewlet/recentConnectionDataSource.ts | 68 + .../viewlet/serverTreeActionProvider.ts | 131 + .../viewlet/serverTreeController.ts | 110 + .../viewlet/serverTreeDataSource.ts | 110 + .../viewlet/serverTreeRenderer.ts | 196 + .../viewlet/serverTreeView.ts | 387 + .../registeredServer/viewlet/templateData.ts | 27 + .../viewlet/treeCreationUtils.ts | 61 + .../viewlet/treeSelectionHandler.ts | 115 + .../viewlet/treeUpdateUtils.ts | 274 + .../common/taskHistory.contribution.ts | 125 + src/sql/parts/taskHistory/common/taskNode.ts | 115 + .../parts/taskHistory/common/taskService.ts | 226 + .../viewlet/media/status_queuedtask.svg | 1 + .../media/status_queuedtask_inverse.svg | 1 + .../viewlet/media/taskHistoryViewlet.css | 54 + .../parts/taskHistory/viewlet/taskAction.ts | 69 + .../viewlet/taskHistoryActionProvider.ts | 68 + .../viewlet/taskHistoryController.ts | 84 + .../viewlet/taskHistoryDataSource.ts | 55 + .../viewlet/taskHistoryRenderer.ts | 146 + .../taskHistory/viewlet/taskHistoryView.ts | 177 + .../taskHistory/viewlet/taskHistoryViewlet.ts | 82 + .../parts/taskHistory/viewlet/templateData.ts | 11 + .../parts/tasks/common/tasks.contribution.ts | 36 + src/sql/parts/tasks/common/tasks.ts | 18 + .../tasks/dialog/taskDialog.component.html | 10 + .../tasks/dialog/taskDialog.component.ts | 50 + .../parts/tasks/dialog/taskDialog.module.ts | 57 + .../parts/tasks/dialog/taskDialogEditor.ts | 105 + src/sql/parts/tasks/dialog/taskDialogInput.ts | 57 + .../clipboard/common/clipboardService.ts | 19 + .../electron-browser/clipboardService.ts | 30 + .../dashboard/common/insightRegistry.ts | 75 + .../dashboard/common/widgetRegistry.ts | 83 + src/sql/platform/tasks/taskRegistry.ts | 85 + .../platform/views/fixedCollapsibleView.ts | 38 + src/sql/platform/views/fixedListView.ts | 102 + .../accountManagementService.ts | 369 + .../accountManagement/accountStore.ts | 204 + .../services/accountManagement/eventTypes.ts | 58 + .../services/accountManagement/interfaces.ts | 81 + .../angularEventing/angularEventingService.ts | 60 + src/sql/services/bootstrap/bootstrapParams.ts | 31 + .../services/bootstrap/bootstrapService.ts | 114 + .../bootstrap/bootstrapServiceImpl.ts | 174 + .../capabilities/capabilitiesService.ts | 166 + .../credentials/credentialsService.ts | 80 + src/sql/services/metadata/metadataService.ts | 103 + .../services/scripting/scriptingService.ts | 114 + .../serialization/serializationService.ts | 89 + .../api/node/extHostAccountManagement.ts | 110 + .../api/node/extHostCredentialManagement.ts | 146 + .../workbench/api/node/extHostDataProtocol.ts | 541 + .../api/node/extHostResourceProvider.ts | 93 + .../api/node/extHostSerializationProvider.ts | 73 + .../api/node/mainThreadAccountManagement.ts | 78 + .../node/mainThreadCredentialManagement.ts | 66 + .../api/node/mainThreadDataProtocol.ts | 353 + .../api/node/mainThreadResourceProvider.ts | 65 + .../node/mainThreadSerializationProvider.ts | 62 + .../workbench/api/node/sqlExtHost.api.impl.ts | 288 + .../api/node/sqlExtHost.contribution.ts | 34 + .../workbench/api/node/sqlExtHost.protocol.ts | 430 + src/sql/workbench/api/node/sqlExtHostTypes.ts | 64 + .../workbench/common/actions.contribution.ts | 51 + src/sql/workbench/common/actions.ts | 369 + src/sql/workbench/common/sqlWorkbenchUtils.ts | 67 + src/sql/workbench/common/taskUtilities.ts | 374 + .../electron-browser/splashscreen/splash.png | Bin 0 -> 325356 bytes .../splashscreen/splashscreen.css | 15 + .../splashscreen/splashscreen.html | 10 + .../errorMessageDialog/errorMessageDialog.ts | 113 + .../errorMessageDialog/errorMessageService.ts | 57 + .../media/errorMessageDialog.css | 16 + src/sql/workbench/update/releaseNotes.ts | 82 + src/sqltest/common/telemetryUtilities.test.ts | 103 + .../accountManagement/accountActions.test.ts | 191 + .../accountDialogController.test.ts | 104 + .../accountViewModel.test.ts | 224 + src/sqltest/parts/admin/adminService.test.ts | 30 + .../parts/common/optionsDialogHelper.test.ts | 415 + .../advancedPropertiesDialog.test.ts | 106 + .../parts/connection/connectionConfig.test.ts | 931 ++ .../connectionDialogService.test.ts | 87 + .../connectionManagementService.test.ts | 563 + .../connection/connectionProfile.test.ts | 206 + .../connection/connectionProfileGroup.test.ts | 148 + .../connectionStatusManager.test.ts | 239 + .../parts/connection/connectionStore.test.ts | 459 + .../connection/connectionTreeActions.test.ts | 516 + .../connection/objectExplorerService.test.ts | 450 + .../connection/providerConnectionInfo.test.ts | 248 + .../widgets/explorerWidget.component.test.ts | 59 + .../propertiesWidget.component.test.ts | 108 + .../disasterRecovery/restoreViewModel.test.ts | 266 + .../insights/insightsDialogController.test.ts | 132 + .../insights/insightsDialogModel.test.ts | 253 + .../parts/query/editor/queryActions.test.ts | 567 + .../parts/query/editor/queryEditor.test.ts | 391 + .../viewlet/serverTreeView.test.ts | 93 + .../accountManagementService.test.ts | 508 + .../accountManagement/accountStore.test.ts | 442 + src/sqltest/stubs/accountManagementStubs.ts | 82 + src/sqltest/stubs/capabilitiesTestService.ts | 128 + .../stubs/connectionDialogTestService.ts | 22 + .../stubs/connectionManagementService.test.ts | 236 + src/sqltest/stubs/connectionProviderStub.ts | 46 + src/sqltest/stubs/contextKeyServiceStub.ts | 38 + src/sqltest/stubs/credentialsTestStubs.ts | 56 + src/sqltest/stubs/editorGroupService.ts | 134 + src/sqltest/stubs/errorMessageServiceStub.ts | 14 + src/sqltest/stubs/messageServiceStub.ts | 30 + .../objectExplorerProviderTestService.ts | 34 + src/sqltest/stubs/sqlOauthServiceStub.ts | 18 + src/sqltest/stubs/storageTestService.ts | 72 + src/sqltest/stubs/telemetryServiceStub.ts | 27 + src/sqltest/stubs/themeTestService.ts | 43 + .../stubs/workbenchEditorTestService.ts | 107 + .../workspaceConfigurationTestService.ts | 80 + src/sqltest/utils/eventVerifier.ts | 87 + .../api/extHostAccountManagement.test.ts | 273 + .../api/extHostCredentialManagement.test.ts | 133 + .../workbench/api/extHostDataProtocol.test.ts | 129 + src/tsconfig.json | 18 + src/typings/OSSREADME.json | 9 + src/typings/ansi-regex.d.ts | 5 + src/typings/applicationInsights.d.ts | 66 + src/typings/bootstrap.d.ts | 124 + src/typings/chokidar.d.ts | 88 + src/typings/comment-json.d.ts | 10 + src/typings/electron.d.ts | 6038 +++++++++ src/typings/error-ex.d.ts | 8 + src/typings/fast-plist.d.ts | 7 + src/typings/gc-signals.d.ts | 19 + src/typings/getmac.d.ts | 12 + src/typings/globals/core-js/index.d.ts | 3052 +++++ src/typings/globals/core-js/typings.json | 8 + src/typings/globals/jqueryui/index.d.ts | 1909 +++ src/typings/globals/jqueryui/typings.json | 8 + src/typings/globals/slickgrid/index.d.ts | 1744 +++ src/typings/globals/slickgrid/typings.json | 8 + src/typings/globals/systemjs/index.d.ts | 335 + src/typings/globals/systemjs/typings.json | 8 + src/typings/globals/typemoq/index.d.ts | 635 + src/typings/globals/underscore/index.d.ts | 6087 +++++++++ src/typings/globals/underscore/typings.json | 8 + src/typings/globals/zone.js/index.d.ts | 343 + src/typings/globals/zone.js/typings.json | 6 + src/typings/graceful-fs.d.ts | 8 + src/typings/http-proxy-agent.d.ts | 20 + src/typings/https-proxy-agent.d.ts | 24 + src/typings/iconv-lite.d.ts | 18 + src/typings/index.d.ts | 18 + src/typings/jQuery.d.ts | 3756 ++++++ src/typings/jschardet.d.ts | 11 + src/typings/keytar.d.ts | 48 + src/typings/lib.array-ext.d.ts | 9 + src/typings/minimist.d.ts | 35 + src/typings/mocha.d.ts | 234 + .../modules/@angular/common/index.d.ts | 1779 +++ .../modules/@angular/common/typings.json | 6 + .../modules/@angular/compiler/index.d.ts | 3831 ++++++ .../modules/@angular/compiler/typings.json | 6 + src/typings/modules/@angular/core/index.d.ts | 7912 +++++++++++ .../modules/@angular/core/typings.json | 6 + src/typings/modules/@angular/forms/index.d.ts | 2721 ++++ .../modules/@angular/forms/typings.json | 6 + .../platform-browser-dynamic/index.d.ts | 113 + .../platform-browser-dynamic/typings.json | 6 + .../@angular/platform-browser/index.d.ts | 1129 ++ .../@angular/platform-browser/typings.json | 6 + .../modules/@angular/router/index.d.ts | 2643 ++++ .../modules/@angular/router/typings.json | 6 + src/typings/modules/angular2-grid/index.d.ts | 369 + .../modules/angular2-grid/typings.json | 8 + .../modules/angular2-slickgrid/index.d.ts | 305 + .../modules/angular2-slickgrid/typings.json | 6 + .../modules/html-query-plan/index.d.ts | 54 + .../modules/html-query-plan/typings.json | 6 + src/typings/modules/ng2-charts/index.d.ts | 82 + src/typings/modules/ng2-charts/typings.json | 6 + src/typings/modules/rxjs/index.d.ts | 7745 +++++++++++ src/typings/modules/rxjs/typings.json | 6 + src/typings/native-keymap.d.ts | 71 + src/typings/native-watchdog.d.ts | 12 + src/typings/node-pty.d.ts | 27 + src/typings/node.d.ts | 4134 ++++++ src/typings/node.processEnv-ext.d.ts | 19 + src/typings/nsfw.d.ts | 40 + src/typings/original-fs.d.ts | 10 + src/typings/prettyData.d.ts | 11 + src/typings/pty.js.d.ts | 23 + src/typings/rangy.d.ts | 9 + src/typings/require.d.ts | 23 + src/typings/semver.d.ts | 125 + src/typings/sinon.d.ts | 448 + src/typings/splash.d.ts | 8 + src/typings/thenable.d.ts | 21 + src/typings/vscode-ripgrep.d.ts | 3 + src/typings/vscode-textmate.d.ts | 167 + src/typings/windows-foreground-love.d.ts | 10 + src/typings/windows-mutex.ts | 12 + src/typings/windows-process-tree.d.ts | 14 + src/typings/winreg.d.ts | 338 + src/typings/xterm.d.ts | 457 + src/typings/yauzl.d.ts | 47 + src/vs/base/browser/browser.ts | 163 + src/vs/base/browser/builder.css | 14 + src/vs/base/browser/builder.ts | 2206 +++ src/vs/base/browser/dnd.ts | 81 + src/vs/base/browser/dom.ts | 1056 ++ src/vs/base/browser/event.ts | 135 + src/vs/base/browser/fastDomNode.ts | 243 + src/vs/base/browser/globalMouseMoveMonitor.ts | 127 + src/vs/base/browser/htmlContentRenderer.ts | 358 + src/vs/base/browser/iframe.ts | 135 + src/vs/base/browser/keyboardEvent.ts | 286 + src/vs/base/browser/mouseEvent.ts | 196 + src/vs/base/browser/touch.ts | 275 + .../base/browser/ui/actionbar/actionbar.css | 104 + src/vs/base/browser/ui/actionbar/actionbar.ts | 768 ++ src/vs/base/browser/ui/aria/aria.css | 9 + src/vs/base/browser/ui/aria/aria.ts | 69 + src/vs/base/browser/ui/button/button.css | 30 + src/vs/base/browser/ui/button/button.ts | 172 + src/vs/base/browser/ui/checkbox/checkbox.css | 42 + src/vs/base/browser/ui/checkbox/checkbox.ts | 121 + .../browser/ui/contextview/contextview.css | 9 + .../browser/ui/contextview/contextview.ts | 273 + .../base/browser/ui/countBadge/countBadge.css | 13 + .../base/browser/ui/countBadge/countBadge.ts | 94 + src/vs/base/browser/ui/dropdown/dropdown.css | 36 + src/vs/base/browser/ui/dropdown/dropdown.ts | 265 + .../base/browser/ui/dropdown/linksDropdown.ts | 55 + .../ui/findinput/case-sensitive-dark.svg | 1 + .../browser/ui/findinput/case-sensitive.svg | 1 + .../base/browser/ui/findinput/findInput.css | 74 + src/vs/base/browser/ui/findinput/findInput.ts | 386 + .../ui/findinput/findInputCheckboxes.css | 31 + .../ui/findinput/findInputCheckboxes.ts | 63 + .../base/browser/ui/findinput/regex-dark.svg | 1 + src/vs/base/browser/ui/findinput/regex.svg | 1 + .../browser/ui/findinput/whole-word-dark.svg | 1 + .../base/browser/ui/findinput/whole-word.svg | 1 + .../ui/highlightedlabel/highlightedLabel.ts | 91 + src/vs/base/browser/ui/iconLabel/iconLabel.ts | 112 + .../base/browser/ui/iconLabel/iconlabel.css | 49 + src/vs/base/browser/ui/inputbox/inputBox.css | 119 + src/vs/base/browser/ui/inputbox/inputBox.ts | 512 + .../ui/keybindingLabel/keybindingLabel.css | 40 + .../ui/keybindingLabel/keybindingLabel.ts | 112 + src/vs/base/browser/ui/list/list.css | 46 + src/vs/base/browser/ui/list/list.ts | 38 + src/vs/base/browser/ui/list/listPaging.ts | 152 + src/vs/base/browser/ui/list/listView.ts | 350 + src/vs/base/browser/ui/list/listWidget.ts | 965 ++ src/vs/base/browser/ui/list/rangeMap.ts | 241 + src/vs/base/browser/ui/list/rowCache.ts | 93 + src/vs/base/browser/ui/menu/menu.css | 141 + src/vs/base/browser/ui/menu/menu.ts | 65 + .../ui/octiconLabel/octiconLabel.mock.ts | 33 + .../browser/ui/octiconLabel/octiconLabel.ts | 35 + .../ui/octiconLabel/octicons/OSSREADME.json | 124 + .../ui/octiconLabel/octicons/README.md | 1 + .../octicons/octicons-animations.css | 14 + .../octiconLabel/octicons/octicons-local.ttf | Bin 0 -> 50856 bytes .../ui/octiconLabel/octicons/octicons.css | 233 + .../ui/octiconLabel/octicons/octicons.eot | Bin 0 -> 29160 bytes .../ui/octiconLabel/octicons/octicons.less | 220 + .../ui/octiconLabel/octicons/octicons.scss | 220 + .../ui/octiconLabel/octicons/octicons.svg | 183 + .../ui/octiconLabel/octicons/octicons.ttf | Bin 0 -> 28992 bytes .../ui/octiconLabel/octicons/octicons.woff | Bin 0 -> 16060 bytes .../octicons/sprockets-octicons.scss | 217 + .../browser/ui/progressbar/progressbar.css | 59 + .../browser/ui/progressbar/progressbar.ts | 230 + .../ui/resourceviewer/resourceViewer.ts | 211 + .../ui/resourceviewer/resourceviewer.css | 56 + src/vs/base/browser/ui/sash/sash.css | 46 + src/vs/base/browser/ui/sash/sash.ts | 368 + .../browser/ui/scrollbar/abstractScrollbar.ts | 262 + .../ui/scrollbar/horizontalScrollbar.ts | 94 + .../ui/scrollbar/media/arrow-down-dark.svg | 1 + .../browser/ui/scrollbar/media/arrow-down.svg | 1 + .../ui/scrollbar/media/arrow-left-dark.svg | 1 + .../browser/ui/scrollbar/media/arrow-left.svg | 1 + .../ui/scrollbar/media/arrow-right-dark.svg | 1 + .../ui/scrollbar/media/arrow-right.svg | 1 + .../ui/scrollbar/media/arrow-up-dark.svg | 1 + .../browser/ui/scrollbar/media/arrow-up.svg | 1 + .../browser/ui/scrollbar/media/scrollbars.css | 147 + .../browser/ui/scrollbar/scrollableElement.ts | 580 + .../ui/scrollbar/scrollableElementOptions.ts | 133 + .../browser/ui/scrollbar/scrollbarArrow.ts | 109 + .../browser/ui/scrollbar/scrollbarState.ts | 222 + .../scrollbarVisibilityController.ts | 105 + .../browser/ui/scrollbar/verticalScrollbar.ts | 95 + .../base/browser/ui/selectBox/selectBox.css | 9 + src/vs/base/browser/ui/selectBox/selectBox.ts | 162 + .../ui/splitview/arrow-collapse-dark.svg | 1 + .../browser/ui/splitview/arrow-collapse.svg | 1 + .../ui/splitview/arrow-expand-dark.svg | 1 + .../browser/ui/splitview/arrow-expand.svg | 1 + .../base/browser/ui/splitview/splitview.css | 98 + src/vs/base/browser/ui/splitview/splitview.ts | 1043 ++ .../browser/ui/toolbar/ellipsis-inverse.svg | 1 + src/vs/base/browser/ui/toolbar/ellipsis.svg | 1 + src/vs/base/browser/ui/toolbar/toolbar.css | 22 + src/vs/base/browser/ui/toolbar/toolbar.ts | 266 + src/vs/base/browser/ui/widget.ts | 54 + src/vs/base/common/OSSREADME.json | 30 + src/vs/base/common/actions.ts | 244 + src/vs/base/common/arrays.ts | 344 + src/vs/base/common/assert.ts | 14 + src/vs/base/common/async.ts | 650 + src/vs/base/common/buildunit.json | 15 + src/vs/base/common/cache.ts | 29 + src/vs/base/common/callbackList.ts | 86 + src/vs/base/common/cancellation.ts | 94 + src/vs/base/common/charCode.ts | 422 + src/vs/base/common/collections.ts | 94 + src/vs/base/common/color.ts | 595 + src/vs/base/common/comparers.ts | 241 + src/vs/base/common/decorators.ts | 59 + src/vs/base/common/diagnostics.ts | 88 + src/vs/base/common/diff/diff.ts | 1034 ++ src/vs/base/common/diff/diff2.ts | 334 + src/vs/base/common/diff/diffChange.ts | 80 + src/vs/base/common/errorMessage.ts | 249 + src/vs/base/common/errors.ts | 284 + src/vs/base/common/event.ts | 530 + src/vs/base/common/eventEmitter.ts | 299 + src/vs/base/common/events.ts | 78 + src/vs/base/common/filters.ts | 716 + src/vs/base/common/functional.ts | 28 + src/vs/base/common/glob.ts | 648 + src/vs/base/common/graph.ts | 105 + src/vs/base/common/hash.ts | 59 + src/vs/base/common/history.ts | 93 + src/vs/base/common/htmlContent.ts | 108 + src/vs/base/common/idGenerator.ts | 22 + src/vs/base/common/iterator.ts | 106 + src/vs/base/common/json.ts | 1222 ++ src/vs/base/common/jsonEdit.ts | 142 + src/vs/base/common/jsonErrorMessages.ts | 27 + src/vs/base/common/jsonFormatter.ts | 214 + src/vs/base/common/jsonSchema.ts | 59 + src/vs/base/common/keyCodes.ts | 575 + src/vs/base/common/keybindingLabels.ts | 172 + src/vs/base/common/labels.ts | 334 + src/vs/base/common/lifecycle.ts | 124 + src/vs/base/common/map.ts | 698 + src/vs/base/common/marked/OSSREADME.json | 8 + src/vs/base/common/marked/marked.d.ts | 162 + src/vs/base/common/marked/marked.js | 10 + src/vs/base/common/marked/marked.license.txt | 19 + src/vs/base/common/marked/raw.marked.js | 1293 ++ src/vs/base/common/marshalling.ts | 44 + src/vs/base/common/mime.ts | 259 + src/vs/base/common/network.ts | 42 + src/vs/base/common/numbers.ts | 47 + src/vs/base/common/objects.ts | 290 + src/vs/base/common/paging.ts | 146 + src/vs/base/common/parsers.ts | 217 + src/vs/base/common/paths.ts | 428 + src/vs/base/common/platform.ts | 155 + src/vs/base/common/processes.ts | 252 + src/vs/base/common/scorer.ts | 134 + src/vs/base/common/scrollable.ts | 461 + src/vs/base/common/severity.ts | 62 + src/vs/base/common/stopwatch.ts | 41 + src/vs/base/common/strings.ts | 737 + src/vs/base/common/types.ts | 220 + src/vs/base/common/uri.ts | 404 + src/vs/base/common/uuid.ts | 115 + src/vs/base/common/winjs.base.d.ts | 74 + src/vs/base/common/winjs.base.js | 13 + src/vs/base/common/winjs.base.raw.js | 2071 +++ src/vs/base/common/worker/simpleWorker.ts | 392 + src/vs/base/node/config.ts | 221 + src/vs/base/node/crypto.ts | 45 + src/vs/base/node/decoder.ts | 64 + src/vs/base/node/encoding.ts | 171 + src/vs/base/node/event.ts | 18 + src/vs/base/node/extfs.ts | 452 + src/vs/base/node/flow.ts | 189 + src/vs/base/node/id.ts | 93 + src/vs/base/node/mime.ts | 173 + src/vs/base/node/paths.ts | 16 + src/vs/base/node/pfs.ts | 191 + src/vs/base/node/ports.ts | 74 + src/vs/base/node/processes.ts | 488 + src/vs/base/node/profiler.ts | 100 + src/vs/base/node/proxy.ts | 55 + src/vs/base/node/request.ts | 172 + src/vs/base/node/startupTimers.d.ts | 39 + src/vs/base/node/startupTimers.js | 128 + src/vs/base/node/stdFork.ts | 141 + src/vs/base/node/stdForkStart.js | 197 + src/vs/base/node/stream.ts | 178 + src/vs/base/node/terminateProcess.sh | 12 + src/vs/base/node/zip.ts | 115 + src/vs/base/parts/ipc/common/ipc.electron.ts | 38 + src/vs/base/parts/ipc/common/ipc.ts | 502 + .../electron-browser/ipc.electron-browser.ts | 22 + .../ipc/electron-main/ipc.electron-main.ts | 44 + src/vs/base/parts/ipc/node/ipc.cp.ts | 220 + src/vs/base/parts/ipc/node/ipc.net.ts | 220 + .../base/parts/ipc/test/node/ipc.net.test.ts | 90 + src/vs/base/parts/ipc/test/node/ipc.perf.ts | 114 + src/vs/base/parts/ipc/test/node/ipc.test.ts | 120 + src/vs/base/parts/ipc/test/node/testApp.ts | 12 + .../base/parts/ipc/test/node/testService.ts | 118 + .../parts/quickopen/browser/quickOpenModel.ts | 688 + .../quickopen/browser/quickOpenViewer.ts | 120 + .../quickopen/browser/quickOpenWidget.ts | 993 ++ .../parts/quickopen/browser/quickopen.css | 163 + .../base/parts/quickopen/common/quickOpen.ts | 91 + .../quickopen/test/browser/quickopen.test.ts | 51 + .../base/parts/tree/browser/CollapseAll.svg | 1 + .../tree/browser/CollapseAll_inverse.svg | 1 + .../parts/tree/browser/collapsed-dark.svg | 1 + .../base/parts/tree/browser/collapsed-hc.svg | 1 + src/vs/base/parts/tree/browser/collapsed.svg | 1 + .../base/parts/tree/browser/expanded-dark.svg | 1 + .../base/parts/tree/browser/expanded-hc.svg | 1 + src/vs/base/parts/tree/browser/expanded.svg | 1 + .../base/parts/tree/browser/loading-dark.svg | 31 + src/vs/base/parts/tree/browser/loading-hc.svg | 31 + src/vs/base/parts/tree/browser/loading.svg | 31 + src/vs/base/parts/tree/browser/tree.css | 121 + src/vs/base/parts/tree/browser/tree.ts | 730 + .../base/parts/tree/browser/treeDefaults.ts | 441 + src/vs/base/parts/tree/browser/treeDnd.ts | 122 + src/vs/base/parts/tree/browser/treeImpl.ts | 387 + src/vs/base/parts/tree/browser/treeModel.ts | 1383 ++ src/vs/base/parts/tree/browser/treeView.ts | 1713 +++ .../base/parts/tree/browser/treeViewModel.ts | 243 + .../parts/tree/test/browser/treeModel.test.ts | 1736 +++ .../tree/test/browser/treeViewModel.test.ts | 253 + src/vs/base/test/browser/browser.test.ts | 14 + src/vs/base/test/browser/builder.test.ts | 39 + src/vs/base/test/browser/comparers.test.ts | 58 + src/vs/base/test/browser/dom.test.ts | 15 + .../test/browser/highlightedLabel.test.ts | 55 + src/vs/base/test/browser/htmlContent.test.ts | 121 + src/vs/base/test/browser/progressBar.test.ts | 36 + .../test/browser/ui/list/rangeMap.test.ts | 348 + .../ui/scrollbar/scrollableElement.test.ts | 525 + .../ui/scrollbar/scrollbarState.test.ts | 66 + src/vs/base/test/common/actions.test.ts | 78 + src/vs/base/test/common/arrays.test.ts | 175 + src/vs/base/test/common/assert.test.ts | 35 + src/vs/base/test/common/async.test.ts | 572 + src/vs/base/test/common/cache.test.ts | 66 + src/vs/base/test/common/cancellation.test.ts | 85 + src/vs/base/test/common/charCode.test.ts | 122 + src/vs/base/test/common/collections.test.ts | 64 + src/vs/base/test/common/color.test.ts | 242 + src/vs/base/test/common/decorators.test.ts | 130 + src/vs/base/test/common/diff/diff.test.ts | 208 + src/vs/base/test/common/errors.test.ts | 47 + src/vs/base/test/common/event.test.ts | 663 + src/vs/base/test/common/eventEmitter.test.ts | 319 + .../base/test/common/filters.perf.data.d.ts | 5 + src/vs/base/test/common/filters.perf.data.js | 7 + src/vs/base/test/common/filters.perf.test.ts | 46 + src/vs/base/test/common/filters.test.ts | 436 + src/vs/base/test/common/graph.test.ts | 96 + src/vs/base/test/common/hash.test.ts | 48 + src/vs/base/test/common/history.test.ts | 122 + src/vs/base/test/common/json.test.ts | 402 + src/vs/base/test/common/jsonEdit.test.ts | 165 + src/vs/base/test/common/jsonFormatter.test.ts | 443 + src/vs/base/test/common/keyCodes.test.ts | 100 + src/vs/base/test/common/labels.test.ts | 146 + src/vs/base/test/common/lifecycle.test.ts | 90 + src/vs/base/test/common/map.test.ts | 499 + src/vs/base/test/common/marshalling.test.ts | 40 + src/vs/base/test/common/mime.test.ts | 117 + src/vs/base/test/common/network.test.ts | 92 + src/vs/base/test/common/objects.test.ts | 253 + src/vs/base/test/common/paging.test.ts | 85 + src/vs/base/test/common/paths.test.ts | 257 + src/vs/base/test/common/scorer.test.ts | 54 + src/vs/base/test/common/scrollable.test.ts | 119 + src/vs/base/test/common/strings.test.ts | 327 + src/vs/base/test/common/types.test.ts | 219 + src/vs/base/test/common/uri.test.ts | 443 + src/vs/base/test/common/utils.ts | 90 + src/vs/base/test/common/uuid.test.ts | 25 + src/vs/base/test/node/config.test.ts | 208 + src/vs/base/test/node/decoder.test.ts | 24 + .../base/test/node/encoding/encoding.test.ts | 57 + .../test/node/encoding/fixtures/empty.txt | 0 .../test/node/encoding/fixtures/some_ansi.css | 40 + .../node/encoding/fixtures/some_utf16be.css | Bin 0 -> 1408 bytes .../node/encoding/fixtures/some_utf16le.css | Bin 0 -> 1408 bytes .../test/node/encoding/fixtures/some_utf8.css | 40 + src/vs/base/test/node/extfs/extfs.test.ts | 268 + .../node/extfs/fixtures/examples/company.jxs | 23 + .../node/extfs/fixtures/examples/conway.jxs | 117 + .../node/extfs/fixtures/examples/employee.jxs | 38 + .../node/extfs/fixtures/examples/small.jxs | 24 + .../base/test/node/extfs/fixtures/index.html | 121 + src/vs/base/test/node/extfs/fixtures/site.css | 40 + src/vs/base/test/node/flow.test.ts | 490 + src/vs/base/test/node/glob.test.ts | 887 ++ .../test/node/mime/fixtures/some.cp1252.txt | 23 + .../test/node/mime/fixtures/some.css.qwoff | 35 + .../test/node/mime/fixtures/some.json.png | 11 + src/vs/base/test/node/mime/fixtures/some.pdf | Bin 0 -> 35327 bytes .../base/test/node/mime/fixtures/some.png.txt | Bin 0 -> 151 bytes .../test/node/mime/fixtures/some.qwoff.txt | Bin 0 -> 18824 bytes .../test/node/mime/fixtures/some.shiftjis.txt | 1 + .../base/test/node/mime/fixtures/some.xml.png | 3 + src/vs/base/test/node/mime/mime.test.ts | 79 + src/vs/base/test/node/pfs.test.ts | 147 + src/vs/base/test/node/port.test.ts | 38 + .../base/test/node/processes/fixtures/fork.ts | 16 + .../node/processes/fixtures/fork_large.ts | 19 + .../test/node/processes/processes.test.ts | 89 + .../base/test/node/stream/fixtures/empty.txt | 0 .../base/test/node/stream/fixtures/file.css | 40 + src/vs/base/test/node/stream/stream.test.ts | 71 + .../base/test/node/zip/fixtures/extract.zip | Bin 0 -> 146 bytes src/vs/base/test/node/zip/zip.test.ts | 29 + src/vs/base/worker/defaultWorkerFactory.ts | 88 + src/vs/base/worker/workerMain.ts | 47 + src/vs/buildunit.json | 13 + src/vs/code/buildfile.js | 25 + .../code/electron-browser/sharedProcess.html | 17 + src/vs/code/electron-browser/sharedProcess.js | 96 + .../electron-browser/sharedProcessMain.ts | 186 + src/vs/code/electron-main/app.ts | 421 + src/vs/code/electron-main/auth.html | 125 + src/vs/code/electron-main/auth.ts | 93 + src/vs/code/electron-main/keyboard.ts | 177 + src/vs/code/electron-main/launch.ts | 129 + src/vs/code/electron-main/main.ts | 217 + src/vs/code/electron-main/menus.ts | 1225 ++ src/vs/code/electron-main/sharedProcess.ts | 115 + src/vs/code/electron-main/window.ts | 868 ++ src/vs/code/electron-main/windows.ts | 1720 +++ src/vs/code/node/cli.ts | 132 + src/vs/code/node/cliProcessMain.ts | 201 + src/vs/code/node/paths.ts | 141 + src/vs/code/node/shellEnv.ts | 92 + src/vs/code/node/windowsFinder.ts | 168 + src/vs/code/test/node/argv.test.ts | 41 + .../node/fixtures/no_vscode_folder/file.txt | 0 .../vscode_folder/_vscode/keepfolder.txt | 0 .../test/node/fixtures/vscode_folder/file.txt | 0 .../_vscode/keepfolder.txt | 0 .../vscode_home_folder/_vscode/settings.json | 1 + .../node/fixtures/vscode_home_folder/file.txt | 0 src/vs/code/test/node/windowsFinder.test.ts | 170 + src/vs/css.build.js | 362 + src/vs/css.d.ts | 5 + src/vs/css.js | 121 + src/vs/editor/browser/codeEditor.ts | 39 + .../editor/browser/config/charWidthReader.ts | 159 + src/vs/editor/browser/config/configuration.ts | 364 + .../browser/config/elementSizeObserver.ts | 79 + .../editor/browser/controller/mouseHandler.ts | 573 + .../editor/browser/controller/mouseTarget.ts | 923 ++ .../browser/controller/pointerHandler.ts | 248 + .../browser/controller/textAreaHandler.css | 34 + .../browser/controller/textAreaHandler.ts | 508 + .../browser/controller/textAreaInput.ts | 585 + .../browser/controller/textAreaState.ts | 289 + src/vs/editor/browser/editorBrowser.ts | 483 + .../editor/browser/editorBrowserExtensions.ts | 43 + src/vs/editor/browser/editorDom.ts | 180 + .../browser/services/codeEditorServiceImpl.ts | 476 + .../editor/browser/view/dynamicViewOverlay.ts | 17 + src/vs/editor/browser/view/viewController.ts | 313 + src/vs/editor/browser/view/viewImpl.ts | 597 + src/vs/editor/browser/view/viewLayer.ts | 610 + .../editor/browser/view/viewOutgoingEvents.ts | 166 + src/vs/editor/browser/view/viewOverlays.ts | 286 + src/vs/editor/browser/view/viewPart.ts | 81 + .../contentWidgets/contentWidgets.ts | 431 + .../currentLineHighlight.css | 12 + .../currentLineHighlight.ts | 148 + .../currentLineMarginHighlight.css | 12 + .../currentLineMarginHighlight.ts | 125 + .../viewParts/decorations/decorations.css | 12 + .../viewParts/decorations/decorations.ts | 209 + .../editorScrollbar/editorScrollbar.ts | 155 + .../viewParts/glyphMargin/glyphMargin.css | 17 + .../viewParts/glyphMargin/glyphMargin.ts | 200 + .../viewParts/indentGuides/indentGuides.css | 12 + .../viewParts/indentGuides/indentGuides.ts | 131 + .../lineNumbers/flipped-cursor-2x.svg | 1 + .../lineNumbers/flipped-cursor-mac-2x.svg | 1 + .../lineNumbers/flipped-cursor-mac.svg | 1 + .../viewParts/lineNumbers/flipped-cursor.svg | 1 + .../viewParts/lineNumbers/lineNumbers.css | 38 + .../viewParts/lineNumbers/lineNumbers.ts | 169 + .../browser/viewParts/lines/rangeUtil.ts | 146 + .../browser/viewParts/lines/viewLine.ts | 613 + .../browser/viewParts/lines/viewLines.css | 55 + .../browser/viewParts/lines/viewLines.ts | 689 + .../linesDecorations/linesDecorations.css | 18 + .../linesDecorations/linesDecorations.ts | 113 + .../editor/browser/viewParts/margin/margin.ts | 95 + .../marginDecorations/marginDecorations.css | 15 + .../marginDecorations/marginDecorations.ts | 99 + .../browser/viewParts/minimap/minimap.css | 27 + .../browser/viewParts/minimap/minimap.ts | 911 ++ .../overlayWidgets/overlayWidgets.css | 9 + .../overlayWidgets/overlayWidgets.ts | 152 + .../overviewRuler/decorationsOverviewRuler.ts | 267 + .../viewParts/overviewRuler/overviewRuler.ts | 83 + .../overviewRuler/overviewRulerImpl.ts | 250 + .../browser/viewParts/rulers/rulers.css | 9 + .../editor/browser/viewParts/rulers/rulers.ts | 112 + .../scrollDecoration/scrollDecoration.css | 11 + .../scrollDecoration/scrollDecoration.ts | 99 + .../viewParts/selections/selections.css | 22 + .../viewParts/selections/selections.ts | 409 + .../viewParts/viewCursors/viewCursor.ts | 203 + .../viewParts/viewCursors/viewCursors.css | 85 + .../viewParts/viewCursors/viewCursors.ts | 368 + .../browser/viewParts/viewZones/viewZones.ts | 353 + .../editor/browser/widget/codeEditorWidget.ts | 535 + .../editor/browser/widget/diffEditorWidget.ts | 2059 +++ src/vs/editor/browser/widget/diffNavigator.ts | 226 + src/vs/editor/browser/widget/diffReview.ts | 822 ++ .../widget/embeddedCodeEditorWidget.ts | 56 + .../browser/widget/media/addition-inverse.svg | 1 + .../editor/browser/widget/media/addition.svg | 1 + .../browser/widget/media/close-inverse.svg | 1 + src/vs/editor/browser/widget/media/close.svg | 1 + .../browser/widget/media/deletion-inverse.svg | 1 + .../editor/browser/widget/media/deletion.svg | 1 + .../browser/widget/media/diagonal-fill.png | Bin 0 -> 185 bytes .../browser/widget/media/diffEditor.css | 94 + .../browser/widget/media/diffReview.css | 70 + src/vs/editor/browser/widget/media/editor.css | 42 + src/vs/editor/browser/widget/media/tokens.css | 9 + .../editor/common/commands/replaceCommand.ts | 120 + src/vs/editor/common/commands/shiftCommand.ts | 242 + .../commands/surroundSelectionCommand.ts | 50 + .../commands/trimTrailingWhitespaceCommand.ts | 104 + src/vs/editor/common/commonCodeEditor.ts | 1063 ++ .../common/config/commonEditorConfig.ts | 627 + src/vs/editor/common/config/editorOptions.ts | 2211 +++ src/vs/editor/common/config/editorZoom.ts | 35 + src/vs/editor/common/config/fontInfo.ts | 191 + .../editor/common/controller/coreCommands.ts | 1792 +++ src/vs/editor/common/controller/cursor.ts | 861 ++ .../common/controller/cursorCollection.ts | 256 + .../controller/cursorColumnSelection.ts | 125 + .../editor/common/controller/cursorCommon.ts | 553 + .../controller/cursorDeleteOperations.ts | 218 + .../editor/common/controller/cursorEvents.ts | 85 + .../common/controller/cursorMoveCommands.ts | 762 ++ .../common/controller/cursorMoveOperations.ts | 246 + .../common/controller/cursorTypeOperations.ts | 775 ++ .../common/controller/cursorWordOperations.ts | 444 + src/vs/editor/common/controller/oneCursor.ts | 116 + .../controller/wordCharacterClassifier.ts | 43 + .../editor/common/core/characterClassifier.ts | 80 + src/vs/editor/common/core/editOperation.ts | 48 + src/vs/editor/common/core/editorState.ts | 73 + src/vs/editor/common/core/lineTokens.ts | 174 + src/vs/editor/common/core/position.ts | 154 + src/vs/editor/common/core/range.ts | 390 + src/vs/editor/common/core/rgba.ts | 57 + src/vs/editor/common/core/selection.ts | 210 + src/vs/editor/common/core/stringBuilder.ts | 148 + src/vs/editor/common/core/token.ts | 55 + src/vs/editor/common/core/uint.ts | 95 + src/vs/editor/common/core/viewLineToken.ts | 118 + src/vs/editor/common/diff/diffComputer.ts | 554 + src/vs/editor/common/diff/nullDiffComputer.ts | 17 + src/vs/editor/common/editorAction.ts | 49 + src/vs/editor/common/editorCommon.ts | 2105 +++ .../editor/common/editorCommonExtensions.ts | 332 + src/vs/editor/common/editorContextKeys.ts | 45 + src/vs/editor/common/model/editStack.ts | 148 + .../editor/common/model/editableTextModel.ts | 837 ++ src/vs/editor/common/model/indentRanges.ts | 69 + .../editor/common/model/indentationGuesser.ts | 174 + src/vs/editor/common/model/mirrorModel.ts | 164 + src/vs/editor/common/model/model.ts | 95 + src/vs/editor/common/model/modelLine.ts | 893 ++ src/vs/editor/common/model/textModel.ts | 827 ++ src/vs/editor/common/model/textModelEvents.ts | 256 + src/vs/editor/common/model/textModelSearch.ts | 512 + .../common/model/textModelWithDecorations.ts | 969 ++ .../common/model/textModelWithMarkers.ts | 174 + .../common/model/textModelWithTokens.ts | 817 ++ src/vs/editor/common/model/textSource.ts | 149 + .../common/model/tokensBinaryEncoding.ts | 65 + src/vs/editor/common/model/wordHelper.ts | 133 + src/vs/editor/common/modes.ts | 888 ++ src/vs/editor/common/modes/abstractMode.ts | 24 + .../editor/common/modes/editorModeContext.ts | 130 + .../common/modes/languageConfiguration.ts | 229 + .../modes/languageConfigurationRegistry.ts | 776 ++ .../common/modes/languageFeatureRegistry.ts | 163 + .../editor/common/modes/languageSelector.ts | 90 + src/vs/editor/common/modes/linkComputer.ts | 286 + src/vs/editor/common/modes/modesRegistry.ts | 63 + src/vs/editor/common/modes/nullMode.ts | 43 + src/vs/editor/common/modes/supports.ts | 89 + .../common/modes/supports/characterPair.ts | 54 + .../modes/supports/electricCharacter.ts | 146 + .../common/modes/supports/indentRules.ts | 102 + .../modes/supports/inplaceReplaceSupport.ts | 100 + .../editor/common/modes/supports/onEnter.ts | 116 + .../common/modes/supports/richEditBrackets.ts | 193 + .../common/modes/supports/tokenization.ts | 404 + .../common/modes/textToHtmlTokenizer.ts | 127 + .../common/modes/tokenizationRegistry.ts | 70 + .../services/abstractCodeEditorService.ts | 158 + src/vs/editor/common/services/bulkEdit.ts | 391 + .../common/services/codeEditorService.ts | 80 + .../common/services/editorSimpleWorker.ts | 575 + .../common/services/editorWorkerService.ts | 30 + .../services/editorWorkerServiceImpl.ts | 435 + .../common/services/languagesRegistry.ts | 318 + src/vs/editor/common/services/modeService.ts | 66 + .../editor/common/services/modeServiceImpl.ts | 177 + src/vs/editor/common/services/modelService.ts | 40 + .../common/services/modelServiceImpl.ts | 595 + .../editor/common/services/resolverService.ts | 45 + .../common/services/resourceConfiguration.ts | 34 + .../services/resourceConfigurationImpl.ts | 47 + src/vs/editor/common/services/webWorker.ts | 106 + .../common/standalone/standaloneBase.ts | 245 + .../editor/common/view/editorColorRegistry.ts | 75 + .../editor/common/view/minimapCharRenderer.ts | 350 + .../editor/common/view/overviewZoneManager.ts | 394 + src/vs/editor/common/view/renderingContext.ts | 125 + .../common/view/runtimeMinimapCharRenderer.ts | 986 ++ src/vs/editor/common/view/viewContext.ts | 42 + .../editor/common/view/viewEventDispatcher.ts | 93 + src/vs/editor/common/view/viewEvents.ts | 349 + .../common/viewLayout/lineDecorations.ts | 204 + .../editor/common/viewLayout/linesLayout.ts | 475 + src/vs/editor/common/viewLayout/viewLayout.ts | 280 + .../common/viewLayout/viewLineRenderer.ts | 759 ++ .../viewLayout/viewLinesViewportData.ts | 111 + .../common/viewLayout/whitespaceComputer.ts | 465 + .../characterHardWrappingLineMapper.ts | 277 + .../common/viewModel/prefixSumComputer.ts | 264 + .../common/viewModel/splitLinesCollection.ts | 1096 ++ .../common/viewModel/viewEventHandler.ts | 195 + src/vs/editor/common/viewModel/viewModel.ts | 299 + .../common/viewModel/viewModelDecorations.ts | 195 + .../editor/common/viewModel/viewModelImpl.ts | 559 + .../browser/bracketMatching.css | 8 + .../bracketMatching/common/bracketMatching.ts | 233 + .../test/common/bracketMatching.test.ts | 101 + .../caretOperations/common/caretOperations.ts | 60 + .../common/moveCaretCommand.ts | 74 + .../caretOperations/common/transpose.ts | 71 + .../test/common/moveCarretCommand.test.ts | 70 + .../contrib/clipboard/browser/clipboard.css | 8 + .../contrib/clipboard/browser/clipboard.ts | 199 + .../contrib/codelens/browser/codelens.ts | 72 + .../codelens/browser/codelensController.ts | 306 + .../codelens/browser/codelensWidget.css | 45 + .../codelens/browser/codelensWidget.ts | 339 + .../colorPicker/browser/colorDetector.ts | 242 + .../colorPicker/browser/colorPicker.css | 118 + .../colorPicker/browser/colorPickerModel.ts | 88 + .../colorPicker/browser/colorPickerWidget.ts | 361 + .../browser/images/opacity-background.png | Bin 0 -> 173 bytes .../contrib/colorPicker/common/color.ts | 18 + .../colorPicker/common/colorFormatter.ts | 144 + .../test/common/colorFormatter.test.ts | 83 + .../comment/common/blockCommentCommand.ts | 174 + .../editor/contrib/comment/common/comment.ts | 122 + .../comment/common/lineCommentCommand.ts | 447 + .../test/common/blockCommentCommand.test.ts | 460 + .../test/common/lineCommentCommand.test.ts | 940 ++ .../contextmenu/browser/contextmenu.ts | 240 + .../contrib/cursorUndo/browser/cursorUndo.ts | 128 + src/vs/editor/contrib/dnd/browser/dnd.css | 28 + src/vs/editor/contrib/dnd/browser/dnd.ts | 210 + .../contrib/dnd/common/dragAndDropCommand.ts | 107 + src/vs/editor/contrib/find/browser/find.ts | 55 + .../contrib/find/browser/findOptionsWidget.ts | 207 + .../contrib/find/browser/findWidget.css | 356 + .../editor/contrib/find/browser/findWidget.ts | 1079 ++ .../images/cancelSelectionFind-inverse.svg | 8 + .../browser/images/cancelSelectionFind.svg | 8 + .../find/browser/images/close-dark.svg | 1 + .../contrib/find/browser/images/close.svg | 1 + .../browser/images/expando-collapsed-dark.svg | 1 + .../find/browser/images/expando-collapsed.svg | 1 + .../browser/images/expando-expanded-dark.svg | 1 + .../find/browser/images/expando-expanded.svg | 1 + .../find/browser/images/next-inverse.svg | 5 + .../contrib/find/browser/images/next.svg | 5 + .../find/browser/images/previous-inverse.svg | 5 + .../contrib/find/browser/images/previous.svg | 5 + .../browser/images/replace-all-inverse.svg | 11 + .../find/browser/images/replace-all.svg | 11 + .../find/browser/images/replace-inverse.svg | 13 + .../contrib/find/browser/images/replace.svg | 13 + .../contrib/find/browser/simpleFindWidget.css | 82 + .../contrib/find/browser/simpleFindWidget.ts | 223 + src/vs/editor/contrib/find/common/find.ts | 25 + .../contrib/find/common/findController.ts | 1277 ++ .../contrib/find/common/findDecorations.ts | 192 + .../editor/contrib/find/common/findModel.ts | 478 + .../editor/contrib/find/common/findState.ts | 219 + .../contrib/find/common/replaceAllCommand.ts | 71 + .../contrib/find/common/replacePattern.ts | 266 + .../contrib/find/test/common/find.test.ts | 89 + .../find/test/common/findController.test.ts | 871 ++ .../find/test/common/findModel.test.ts | 2037 +++ .../find/test/common/replacePattern.test.ts | 157 + .../folding/browser/arrow-collapse-dark.svg | 6 + .../folding/browser/arrow-collapse.svg | 6 + .../folding/browser/arrow-expand-dark.svg | 4 + .../contrib/folding/browser/arrow-expand.svg | 4 + .../contrib/folding/browser/folding.css | 49 + .../editor/contrib/folding/browser/folding.ts | 767 ++ .../editor/contrib/folding/common/folding.ts | 15 + .../contrib/folding/common/foldingModel.ts | 295 + .../folding/common/indentFoldStrategy.ts | 42 + .../contrib/folding/test/indentFold.test.ts | 29 + .../contrib/format/browser/formatActions.ts | 368 + src/vs/editor/contrib/format/common/format.ts | 107 + .../contrib/format/common/formatCommand.ts | 134 + .../format/test/common/formatCommand.test.ts | 313 + .../browser/clickLinkGesture.ts | 205 + .../browser/goToDeclaration.ts | 73 + .../browser/goToDeclarationCommands.ts | 371 + .../browser/goToDeclarationMouse.css | 9 + .../browser/goToDeclarationMouse.ts | 226 + .../browser/messageController.css | 38 + .../browser/messageController.ts | 184 + .../contrib/gotoError/browser/gotoError.css | 34 + .../contrib/gotoError/browser/gotoError.ts | 496 + src/vs/editor/contrib/hover/browser/hover.css | 77 + src/vs/editor/contrib/hover/browser/hover.ts | 222 + .../contrib/hover/browser/hoverOperation.ts | 189 + .../contrib/hover/browser/hoverWidgets.ts | 251 + .../hover/browser/modesContentHover.ts | 397 + .../contrib/hover/browser/modesGlyphHover.ts | 186 + src/vs/editor/contrib/hover/common/hover.ts | 41 + .../inPlaceReplace/common/inPlaceReplace.ts | 198 + .../common/inPlaceReplaceCommand.ts | 48 + .../contrib/indentation/common/indentUtils.ts | 37 + .../contrib/indentation/common/indentation.ts | 620 + .../indentation/test/indentation.test.ts | 172 + .../common/copyLinesCommand.ts | 84 + .../common/deleteLinesCommand.ts | 64 + .../linesOperations/common/linesOperations.ts | 797 ++ .../common/moveLinesCommand.ts | 356 + .../common/sortLinesCommand.ts | 105 + .../test/common/copyLinesCommand.test.ts | 200 + .../test/common/deleteLinesCommand.test.ts | 194 + .../test/common/linesOperations.test.ts | 607 + .../test/common/moveLinesCommand.test.ts | 353 + .../test/common/sortLinesCommand.test.ts | 167 + src/vs/editor/contrib/links/browser/links.css | 12 + src/vs/editor/contrib/links/browser/links.ts | 421 + src/vs/editor/contrib/links/common/links.ts | 137 + .../contrib/multicursor/common/multicursor.ts | 141 + .../test/common/multicursor.test.ts | 44 + .../browser/arrow-down-dark.svg | 1 + .../parameterHints/browser/arrow-down.svg | 1 + .../parameterHints/browser/arrow-up-dark.svg | 1 + .../parameterHints/browser/arrow-up.svg | 1 + .../parameterHints/browser/parameterHints.css | 121 + .../parameterHints/browser/parameterHints.ts | 124 + .../browser/parameterHintsWidget.ts | 494 + .../parameterHints/common/parameterHints.ts | 41 + .../quickFix/browser/lightBulbWidget.css | 30 + .../quickFix/browser/lightBulbWidget.ts | 110 + .../quickFix/browser/lightbulb-dark.svg | 1 + .../contrib/quickFix/browser/lightbulb.svg | 1 + .../contrib/quickFix/browser/quickFix.ts | 51 + .../quickFix/browser/quickFixCommands.ts | 132 + .../contrib/quickFix/browser/quickFixModel.ts | 176 + .../quickFix/browser/quickFixWidget.ts | 78 + .../test/browser/quickFixModel.test.ts | 253 + .../contrib/quickOpen/common/quickOpen.ts | 72 + .../browser/referenceSearch.ts | 221 + .../browser/referencesController.ts | 262 + .../browser/referencesModel.ts | 313 + .../browser/referencesWidget.css | 57 + .../browser/referencesWidget.ts | 923 ++ .../test/browser/referencesModel.test.ts | 40 + .../editor/contrib/rename/browser/rename.ts | 273 + .../rename/browser/renameInputField.css | 13 + .../rename/browser/renameInputField.ts | 201 + .../contrib/smartSelect/common/smartSelect.ts | 198 + .../common/tokenSelectionSupport.ts | 70 + .../contrib/smartSelect/common/tokenTree.ts | 425 + .../test/common/tokenSelectionSupport.test.ts | 118 + .../editor/contrib/snippet/browser/snippet.md | 46 + .../snippet/browser/snippetController2.ts | 227 + .../contrib/snippet/browser/snippetParser.ts | 714 + .../snippet/browser/snippetSession.css | 12 + .../contrib/snippet/browser/snippetSession.ts | 442 + .../snippet/browser/snippetVariables.ts | 109 + .../browser/snippetController2.old.test.ts | 588 + .../test/browser/snippetController2.test.ts | 277 + .../test/browser/snippetParser.test.ts | 547 + .../test/browser/snippetSession.test.ts | 514 + .../test/browser/snippetVariables.test.ts | 138 + .../suggest/browser/completionModel.ts | 211 + .../suggest/browser/media/Class_16x.svg | 1 + .../browser/media/Class_inverse_16x.svg | 1 + .../browser/media/ColorPalette_16x.svg | 1 + .../media/ColorPalette_inverse_16x.svg | 1 + .../suggest/browser/media/Constant_16x.svg | 1 + .../browser/media/Constant_16x_inverse.svg | 1 + .../suggest/browser/media/Document_16x.svg | 1 + .../browser/media/Document_inverse_16x.svg | 1 + .../suggest/browser/media/EnumItem_16x.svg | 1 + .../browser/media/EnumItem_inverse_16x.svg | 1 + .../suggest/browser/media/Enumerator_16x.svg | 1 + .../browser/media/Enumerator_inverse_16x.svg | 1 + .../browser/media/Event_16x_vscode.svg | 1 + .../media/Event_16x_vscode_inverse.svg | 1 + .../suggest/browser/media/Field_16x.svg | 1 + .../browser/media/Field_inverse_16x.svg | 1 + .../suggest/browser/media/Folder_16x.svg | 1 + .../browser/media/Folder_inverse_16x.svg | 1 + .../browser/media/ImportFile_16x_vscode.svg | 1 + .../media/ImportFile_16x_vscode_inverse.svg | 1 + .../browser/media/IntelliSenseKeyword_16x.svg | 1 + .../media/IntelliSenseKeyword_inverse_16x.svg | 1 + .../suggest/browser/media/Interface_16x.svg | 1 + .../browser/media/Interface_inverse_16x.svg | 1 + .../media/LocalVariable_16x_vscode.svg | 1 + .../LocalVariable_16x_vscode_inverse.svg | 1 + .../suggest/browser/media/Method_16x.svg | 1 + .../browser/media/Method_inverse_16x.svg | 1 + .../suggest/browser/media/Misc_16x.svg | 1 + .../browser/media/Misc_inverse_16x.svg | 1 + .../suggest/browser/media/Namespace_16x.svg | 1 + .../browser/media/Namespace_inverse_16x.svg | 1 + .../browser/media/Operator_16x_vscode.svg | 1 + .../media/Operator_16x_vscode_inverse.svg | 1 + .../suggest/browser/media/Property_16x.svg | 1 + .../browser/media/Property_inverse_16x.svg | 1 + .../suggest/browser/media/Ruler_16x.svg | 1 + .../browser/media/Ruler_inverse_16x.svg | 1 + .../suggest/browser/media/Snippet_16x.svg | 47 + .../browser/media/Snippet_inverse_16x.svg | 43 + .../suggest/browser/media/String_16x.svg | 1 + .../browser/media/String_inverse_16x.svg | 1 + .../browser/media/Structure_16x_vscode.svg | 1 + .../media/Structure_16x_vscode_inverse.svg | 1 + .../browser/media/Template_16x_vscode.svg | 1 + .../media/Template_16x_vscode_inverse.svg | 1 + .../suggest/browser/media/close-dark.svg | 1 + .../contrib/suggest/browser/media/close.svg | 1 + .../contrib/suggest/browser/media/info.svg | 1 + .../contrib/suggest/browser/media/suggest.css | 322 + .../editor/contrib/suggest/browser/suggest.ts | 245 + .../suggest/browser/suggestController.ts | 434 + .../contrib/suggest/browser/suggestModel.ts | 455 + .../contrib/suggest/browser/suggestWidget.ts | 1071 ++ .../test/browser/completionModel.test.ts | 231 + .../suggest/test/browser/suggest.test.ts | 110 + .../suggest/test/browser/suggestModel.test.ts | 460 + .../common/toggleTabFocusMode.ts | 36 + .../wordHighlighter/common/wordHighlighter.ts | 373 + .../wordOperations/common/wordOperations.ts | 391 + .../test/common/wordOperations.test.ts | 704 + .../browser/media/close-inverse.svg | 1 + .../zoneWidget/browser/media/close.svg | 1 + .../zoneWidget/browser/peekViewWidget.css | 55 + .../zoneWidget/browser/peekViewWidget.ts | 216 + .../contrib/zoneWidget/browser/zoneWidget.css | 17 + .../contrib/zoneWidget/browser/zoneWidget.ts | 480 + src/vs/editor/editor.all.ts | 44 + src/vs/editor/editor.main.ts | 53 + .../accessibilityHelp/accessibilityHelp.css | 10 + .../accessibilityHelp/accessibilityHelp.ts | 391 + src/vs/editor/standalone/browser/colorizer.ts | 192 + .../iPadShowKeyboard/iPadShowKeyboard.css | 24 + .../iPadShowKeyboard/iPadShowKeyboard.ts | 108 + .../iPadShowKeyboard/keyboard-inverse.svg | 1 + .../browser/iPadShowKeyboard/keyboard.svg | 1 + .../browser/inspectTokens/inspectTokens.css | 43 + .../browser/inspectTokens/inspectTokens.ts | 348 + .../browser/quickOpen/editorQuickOpen.ts | 173 + .../standalone/browser/quickOpen/gotoLine.css | 8 + .../standalone/browser/quickOpen/gotoLine.ts | 174 + .../browser/quickOpen/quickCommand.ts | 135 + .../quickOpen/quickOpenEditorWidget.ts | 109 + .../browser/quickOpen/quickOutline.css | 95 + .../browser/quickOpen/quickOutline.ts | 316 + .../browser/quickOpen/symbol-sprite.svg | 1 + .../standalone/browser/simpleServices.ts | 569 + .../standalone/browser/standalone-tokens.css | 251 + .../browser/standaloneCodeEditor.ts | 373 + .../standalone/browser/standaloneEditor.ts | 398 + .../standalone/browser/standaloneLanguages.ts | 743 + .../standalone/browser/standaloneServices.ts | 203 + .../browser/standaloneThemeServiceImpl.ts | 201 + .../toggleHighContrast/toggleHighContrast.ts | 38 + .../common/monarch/monarchCommon.ts | 218 + .../common/monarch/monarchCompile.ts | 543 + .../standalone/common/monarch/monarchLexer.ts | 842 ++ .../standalone/common/monarch/monarchTypes.ts | 126 + .../common/standaloneThemeService.ts | 36 + src/vs/editor/standalone/common/themes.ts | 210 + .../test/browser/simpleServices.test.ts | 56 + .../test/browser/standaloneLanguages.test.ts | 180 + .../test/browser/controller/imeTester.html | 57 + .../test/browser/controller/imeTester.ts | 183 + .../browser/controller/inputRecorder.html | 115 + .../browser/controller/textAreaState.test.ts | 643 + .../services/decorationRenderOptions.test.ts | 20 + .../test/browser/view/minimapFontCreator.html | 25 + .../test/browser/view/minimapFontCreator.ts | 145 + .../test/browser/view/viewLayer.test.ts | 753 ++ .../test/common/commands/commandTestUtils.ts | 94 + .../test/common/commands/shiftCommand.test.ts | 987 ++ .../test/common/commands/sideEditing.test.ts | 208 + .../trimTrailingWhitespaceCommand.test.ts | 82 + src/vs/editor/test/common/commentMode.ts | 21 + .../common/config/commonEditorConfig.test.ts | 182 + .../test/common/controller/cursor.test.ts | 3702 +++++ .../controller/cursorMoveCommand.test.ts | 512 + .../controller/cursorMoveHelper.test.ts | 178 + .../common/core/characterClassifier.test.ts | 39 + .../test/common/core/editorState.test.ts | 104 + .../test/common/core/lineTokens.test.ts | 292 + src/vs/editor/test/common/core/range.test.ts | 120 + .../test/common/diff/diffComputer.test.ts | 677 + src/vs/editor/test/common/editorTestUtils.ts | 13 + .../test/common/mocks/mockCodeEditor.ts | 111 + .../common/mocks/mockCodeEditorService.ts | 14 + src/vs/editor/test/common/mocks/mockMode.ts | 25 + .../test/common/mocks/testConfiguration.ts | 46 + .../common/model/editableTextModel.test.ts | 1835 +++ .../model/editableTextModelAuto.test.ts | 300 + .../model/editableTextModelTestUtils.ts | 117 + .../test/common/model/indentRanges.test.ts | 122 + .../test/common/model/model.line.test.ts | 2331 ++++ .../test/common/model/model.modes.test.ts | 290 + src/vs/editor/test/common/model/model.test.ts | 395 + .../common/model/modelDecorations.test.ts | 638 + .../common/model/modelEditOperation.test.ts | 178 + .../test/common/model/textModel.test.ts | 997 ++ .../test/common/model/textModelSearch.test.ts | 636 + .../common/model/textModelWithTokens.test.ts | 361 + .../modes/languageConfiguration.test.ts | 93 + .../common/modes/languageSelector.test.ts | 84 + .../test/common/modes/linkComputer.test.ts | 193 + .../modes/supports/characterPair.test.ts | 122 + .../modes/supports/electricCharacter.test.ts | 128 + .../common/modes/supports/onEnter.test.ts | 128 + .../modes/supports/richEditBrackets.test.ts | 75 + .../modes/supports/tokenization.test.ts | 333 + .../common/modes/textToHtmlTokenizer.test.ts | 229 + src/vs/editor/test/common/modesTestUtils.ts | 34 + .../services/editorSimpleWorker.test.ts | 171 + .../common/services/languagesRegistry.test.ts | 269 + .../test/common/services/modelService.test.ts | 333 + .../common/standalone/standaloneBase.test.ts | 142 + .../common/view/minimapCharRenderer.test.ts | 187 + .../common/view/minimapCharRendererFactory.ts | 173 + .../common/view/overviewZoneManager.test.ts | 101 + .../viewLayout/editorLayoutProvider.test.ts | 744 + .../common/viewLayout/lineDecorations.test.ts | 124 + .../common/viewLayout/linesLayout.test.ts | 597 + .../viewLayout/viewLineRenderer.test.ts | 1234 ++ .../viewLayout/whitespaceComputer.test.ts | 560 + .../characterHardWrappingLineMapper.test.ts | 109 + .../viewModel/prefixSumComputer.test.ts | 171 + .../viewModel/splitLinesCollection.test.ts | 786 ++ .../test/common/viewModel/testViewModel.ts | 26 + .../viewModel/viewModelDecorations.test.ts | 311 + .../common/viewModel/viewModelImpl.test.ts | 44 + src/vs/loader.js | 1681 +++ src/vs/monaco.d.ts | 5021 +++++++ src/vs/nls.build.js | 182 + src/vs/nls.d.ts | 12 + src/vs/nls.js | 126 + .../actions/browser/menuItemActionItem.ts | 178 + src/vs/platform/actions/common/actions.ts | 247 + src/vs/platform/actions/common/menu.ts | 144 + src/vs/platform/actions/common/menuService.ts | 28 + .../electron-browser/menusExtensionPoint.ts | 368 + .../actions/test/common/menuService.test.ts | 229 + src/vs/platform/backup/common/backup.ts | 29 + .../backup/electron-main/backupMainService.ts | 329 + .../electron-main/backupMainService.test.ts | 595 + .../electron-browser/broadcastService.ts | 55 + .../clipboard/common/clipboardService.ts | 20 + .../electron-browser/clipboardService.ts | 18 + .../commands/common/commandService.ts | 61 + src/vs/platform/commands/common/commands.ts | 149 + .../commands/test/commandService.test.ts | 104 + .../platform/commands/test/commands.test.ts | 79 + .../configuration/common/configuration.ts | 384 + .../common/configurationRegistry.ts | 313 + src/vs/platform/configuration/common/model.ts | 202 + .../node/configurationService.ts | 100 + .../test/common/configuration.test.ts | 79 + .../configuration/test/common/model.test.ts | 148 + .../test/common/testConfigurationService.ts | 79 + .../test/node/configurationService.test.ts | 264 + .../contextkey/browser/contextKeyService.ts | 300 + .../platform/contextkey/common/contextkey.ts | 477 + .../contextkey/test/common/contextkey.test.ts | 89 + .../browser/contextMenuHandler.css | 8 + .../contextview/browser/contextMenuHandler.ts | 148 + .../contextview/browser/contextMenuService.ts | 34 + .../contextview/browser/contextView.ts | 60 + .../contextview/browser/contextViewService.ts | 46 + .../credentials/common/credentials.ts | 22 + .../credentials/node/credentialsIpc.ts | 56 + .../credentials/node/credentialsService.ts | 27 + src/vs/platform/editor/common/editor.ts | 299 + .../environment/common/environment.ts | 91 + src/vs/platform/environment/node/argv.ts | 190 + .../environment/node/environmentService.ts | 188 + .../test/node/environmentService.test.ts | 44 + .../common/extensionEnablementService.ts | 140 + .../common/extensionManagement.ts | 303 + .../common/extensionManagementIpc.ts | 87 + .../common/extensionManagementUtil.ts | 97 + .../common/extensionNls.ts | 33 + .../node/extensionGalleryService.ts | 642 + .../node/extensionManagementService.ts | 601 + .../node/media/defaultIcon.png | Bin 0 -> 1686 bytes .../common/extensionEnablementService.test.ts | 277 + .../test/common/extensionManagement.test.ts | 29 + .../extensions/common/extensionHost.ts | 20 + .../platform/extensions/common/extensions.ts | 105 + .../extensions/common/extensionsRegistry.ts | 297 + .../extensions/node/extensionValidator.ts | 343 + .../test/node/extensionValidator.test.ts | 396 + src/vs/platform/files/common/files.ts | 790 ++ src/vs/platform/files/test/files.test.ts | 202 + src/vs/platform/history/common/history.ts | 31 + .../electron-main/historyMainService.ts | 252 + .../instantiation/common/descriptors.ts | 272 + .../instantiation/common/extensions.ts | 25 + .../instantiation/common/instantiation.ts | 218 + .../common/instantiationService.ts | 238 + .../instantiation/common/serviceCollection.ts | 37 + .../test/common/instantiationService.test.ts | 397 + .../test/common/instantiationServiceMock.ts | 138 + src/vs/platform/integrity/common/integrity.ts | 29 + .../integrity/node/integrityServiceImpl.ts | 186 + .../common/jsonContributionRegistry.ts | 79 + .../common/jsonValidationExtensionPoint.ts | 76 + .../common/abstractKeybindingService.ts | 168 + .../platform/keybinding/common/keybinding.ts | 81 + .../keybinding/common/keybindingResolver.ts | 323 + .../keybinding/common/keybindingsRegistry.ts | 245 + .../common/resolvedKeybindingItem.ts | 39 + .../common/usLayoutResolvedKeybinding.ts | 198 + .../common/abstractKeybindingService.test.ts | 421 + .../test/common/keybindingLabels.test.ts | 172 + .../test/common/keybindingResolver.test.ts | 398 + .../test/common/mockKeybindingService.ts | 125 + src/vs/platform/lifecycle/common/lifecycle.ts | 130 + .../lifecycle/electron-main/lifecycleMain.ts | 315 + src/vs/platform/list/browser/listService.ts | 154 + src/vs/platform/log/common/log.ts | 41 + .../platform/markers/common/markerService.ts | 334 + src/vs/platform/markers/common/markers.ts | 116 + .../platform/markers/common/problemMatcher.ts | 1657 +++ .../markers/test/common/markerService.test.ts | 199 + src/vs/platform/message/common/message.ts | 81 + src/vs/platform/message/common/messageIpc.ts | 39 + src/vs/platform/message/node/messageCli.ts | 57 + src/vs/platform/node/package.ts | 16 + src/vs/platform/node/product.ts | 89 + .../opener/browser/opener.contribution.ts | 12 + .../platform/opener/browser/openerService.ts | 83 + src/vs/platform/opener/common/opener.ts | 30 + .../opener/test/browser/openerService.test.ts | 117 + src/vs/platform/progress/common/progress.ts | 82 + src/vs/platform/quickOpen/common/quickOpen.ts | 188 + src/vs/platform/registry/common/platform.ts | 99 + .../registry/test/common/platform.test.ts | 52 + .../electron-browser/requestService.ts | 99 + .../request/electron-main/requestService.ts | 22 + src/vs/platform/request/node/request.ts | 53 + .../platform/request/node/requestService.ts | 53 + src/vs/platform/search/common/replace.ts | 188 + src/vs/platform/search/common/search.ts | 208 + .../search/test/common/replace.test.ts | 220 + src/vs/platform/statusbar/common/statusbar.ts | 70 + src/vs/platform/storage/common/migration.ts | 198 + src/vs/platform/storage/common/storage.ts | 78 + .../platform/storage/common/storageService.ts | 235 + src/vs/platform/storage/node/storage.ts | 94 + .../storage/test/browser/migration.test.ts | 22 + .../test/common/storageService.test.ts | 101 + .../telemetry/browser/errorTelemetry.ts | 150 + .../platform/telemetry/browser/idleMonitor.ts | 78 + .../platform/telemetry/common/experiments.ts | 89 + src/vs/platform/telemetry/common/telemetry.ts | 37 + .../platform/telemetry/common/telemetryIpc.ts | 43 + .../telemetry/common/telemetryService.ts | 150 + .../telemetry/common/telemetryUtils.ts | 247 + .../telemetry/node/appInsightsAppender.ts | 152 + .../telemetry/node/commonProperties.ts | 50 + .../node/workbenchCommonProperties.ts | 144 + .../appInsightsAppender.test.ts | 144 + .../electron-browser/commonProperties.test.ts | 88 + .../electron-browser/telemetryService.test.ts | 716 + .../theme/common/colorExtensionPoint.ts | 124 + src/vs/platform/theme/common/colorRegistry.ts | 391 + src/vs/platform/theme/common/styler.ts | 247 + src/vs/platform/theme/common/themeService.ts | 145 + .../theme/test/common/testThemeService.ts | 56 + src/vs/platform/update/common/update.ts | 58 + src/vs/platform/update/common/updateIpc.ts | 88 + .../electron-main/auto-updater.linux.ts | 77 + .../electron-main/auto-updater.win32.ts | 140 + .../update/electron-main/updateService.ts | 268 + src/vs/platform/url/common/url.ts | 19 + src/vs/platform/url/common/urlIpc.ts | 66 + .../platform/url/electron-main/urlService.ts | 57 + src/vs/platform/windows/common/windows.ts | 233 + src/vs/platform/windows/common/windowsIpc.ts | 279 + .../windows/electron-browser/windowService.ts | 146 + .../platform/windows/electron-main/windows.ts | 100 + .../windows/electron-main/windowsService.ts | 383 + src/vs/platform/workspace/common/workspace.ts | 200 + .../workspace/test/common/testWorkspace.ts | 18 + .../workspace/test/common/workspace.test.ts | 22 + .../platform/workspaces/common/workspaces.ts | 107 + .../workspaces/common/workspacesIpc.ts | 39 + .../electron-main/workspacesMainService.ts | 277 + .../workspacesMainService.test.ts | 301 + src/vs/vscode.d.ts | 5691 ++++++++ src/vs/vscode.proposed.d.ts | 246 + .../api/electron-browser/extHostCustomers.ts | 64 + .../extensionHost.contribution.ts | 72 + .../electron-browser/mainThreadCommands.ts | 102 + .../mainThreadConfiguration.ts | 62 + .../electron-browser/mainThreadCredentials.ts | 36 + .../mainThreadDebugService.ts | 112 + .../electron-browser/mainThreadDiagnostics.ts | 44 + .../api/electron-browser/mainThreadDialogs.ts | 63 + .../mainThreadDocumentContentProviders.ts | 85 + .../electron-browser/mainThreadDocuments.ts | 237 + .../mainThreadDocumentsAndEditors.ts | 378 + .../api/electron-browser/mainThreadEditor.ts | 379 + .../api/electron-browser/mainThreadEditors.ts | 230 + .../api/electron-browser/mainThreadErrors.ts | 28 + .../mainThreadExtensionService.ts | 38 + .../mainThreadFileSystemEventService.ts | 54 + .../electron-browser/mainThreadHeapService.ts | 139 + .../mainThreadLanguageFeatures.ts | 358 + .../electron-browser/mainThreadLanguages.ts | 30 + .../mainThreadMessageService.ts | 104 + .../mainThreadOutputService.ts | 73 + .../electron-browser/mainThreadProgress.ts | 51 + .../electron-browser/mainThreadQuickOpen.ts | 102 + .../api/electron-browser/mainThreadSCM.ts | 346 + .../mainThreadSaveParticipant.ts | 272 + .../electron-browser/mainThreadStatusBar.ts | 49 + .../api/electron-browser/mainThreadStorage.ts | 51 + .../api/electron-browser/mainThreadTask.ts | 50 + .../electron-browser/mainThreadTelemetry.ts | 31 + .../mainThreadTerminalService.ts | 82 + .../electron-browser/mainThreadTreeViews.ts | 145 + .../api/electron-browser/mainThreadWindow.ts | 35 + .../electron-browser/mainThreadWorkspace.ts | 201 + src/vs/workbench/api/node/extHost.api.impl.ts | 667 + src/vs/workbench/api/node/extHost.protocol.ts | 614 + .../workbench/api/node/extHostApiCommands.ts | 474 + src/vs/workbench/api/node/extHostCommands.ts | 224 + .../api/node/extHostConfiguration.ts | 120 + .../workbench/api/node/extHostCredentials.ts | 29 + .../workbench/api/node/extHostDebugService.ts | 213 + .../workbench/api/node/extHostDiagnostics.ts | 258 + src/vs/workbench/api/node/extHostDialogs.ts | 24 + .../node/extHostDocumentContentProviders.ts | 88 + .../workbench/api/node/extHostDocumentData.ts | 268 + .../node/extHostDocumentSaveParticipant.ts | 165 + src/vs/workbench/api/node/extHostDocuments.ts | 142 + .../api/node/extHostDocumentsAndEditors.ts | 149 + .../api/node/extHostExtensionActivator.ts | 327 + .../api/node/extHostExtensionService.ts | 422 + .../api/node/extHostFileSystemEventService.ts | 104 + .../workbench/api/node/extHostHeapService.ts | 34 + .../api/node/extHostLanguageFeatures.ts | 1072 ++ src/vs/workbench/api/node/extHostLanguages.ts | 24 + .../api/node/extHostMessageService.ts | 60 + .../api/node/extHostOutputService.ts | 78 + src/vs/workbench/api/node/extHostProgress.ts | 50 + src/vs/workbench/api/node/extHostQuickOpen.ts | 120 + src/vs/workbench/api/node/extHostSCM.ts | 367 + src/vs/workbench/api/node/extHostStatusBar.ts | 190 + src/vs/workbench/api/node/extHostStorage.ts | 25 + src/vs/workbench/api/node/extHostTask.ts | 439 + .../api/node/extHostTerminalService.ts | 172 + .../workbench/api/node/extHostTextEditor.ts | 623 + .../workbench/api/node/extHostTextEditors.ts | 131 + src/vs/workbench/api/node/extHostTreeViews.ts | 227 + .../api/node/extHostTypeConverters.ts | 454 + src/vs/workbench/api/node/extHostTypes.ts | 1385 ++ src/vs/workbench/api/node/extHostWindow.ts | 39 + src/vs/workbench/api/node/extHostWorkspace.ts | 259 + src/vs/workbench/browser/actions.ts | 375 + .../browser/actions/configureLocale.ts | 93 + .../browser/actions/media/actions.css | 13 + .../actions/media/editor-layout-inverse.svg | 1 + .../browser/actions/media/editor-layout.svg | 1 + .../actions/toggleActivityBarVisibility.ts | 45 + .../browser/actions/toggleEditorLayout.ts | 84 + .../browser/actions/toggleSidebarPosition.ts | 45 + .../actions/toggleSidebarVisibility.ts | 38 + .../actions/toggleStatusbarVisibility.ts | 45 + .../browser/actions/toggleZenMode.ts | 36 + .../browser/actions/workspaceActions.ts | 290 + src/vs/workbench/browser/activity.ts | 42 + src/vs/workbench/browser/composite.ts | 273 + src/vs/workbench/browser/labels.ts | 262 + src/vs/workbench/browser/layout.ts | 595 + src/vs/workbench/browser/media/part.css | 81 + src/vs/workbench/browser/panel.ts | 116 + src/vs/workbench/browser/part.ts | 148 + .../parts/activitybar/activitybarActions.ts | 757 ++ .../parts/activitybar/activitybarPart.ts | 562 + .../activitybar/media/activityaction.css | 81 + .../activitybar/media/activitybarpart.css | 24 + .../activitybar/media/ellipsis-global.svg | 1 + .../workbench/browser/parts/compositePart.ts | 581 + .../browser/parts/editor/baseEditor.ts | 305 + .../browser/parts/editor/binaryDiffEditor.ts | 41 + .../browser/parts/editor/binaryEditor.ts | 151 + .../parts/editor/editor.contribution.ts | 408 + .../browser/parts/editor/editorActions.ts | 1520 +++ .../browser/parts/editor/editorCommands.ts | 275 + .../parts/editor/editorGroupsControl.ts | 2126 +++ .../browser/parts/editor/editorPart.ts | 1571 +++ .../browser/parts/editor/editorPicker.ts | 274 + .../browser/parts/editor/editorStatus.ts | 1180 ++ .../editor/media/close-dirty-inverse.svg | 1 + .../parts/editor/media/close-dirty.svg | 1 + .../parts/editor/media/close-inverse.svg | 1 + .../browser/parts/editor/media/close.svg | 1 + .../editor/media/editorGroupsControl.css | 87 + .../browser/parts/editor/media/editorpart.css | 11 + .../parts/editor/media/editorpicker.css | 8 + .../parts/editor/media/editorstatus.css | 24 + .../parts/editor/media/next-diff-inverse.svg | 1 + .../browser/parts/editor/media/next-diff.svg | 1 + .../parts/editor/media/notabstitle.css | 47 + .../editor/media/previous-diff-inverse.svg | 1 + .../parts/editor/media/previous-diff.svg | 1 + .../media/split-editor-horizontal-inverse.svg | 1 + .../editor/media/split-editor-horizontal.svg | 1 + .../media/split-editor-vertical-inverse.svg | 1 + .../editor/media/split-editor-vertical.svg | 1 + .../parts/editor/media/stackview-inverse.svg | 20 + .../browser/parts/editor/media/stackview.svg | 20 + .../browser/parts/editor/media/tabstitle.css | 156 + .../parts/editor/media/textdiffeditor.css | 22 + .../parts/editor/media/titlecontrol.css | 107 + .../parts/editor/noTabsTitleControl.ts | 138 + .../browser/parts/editor/sideBySideEditor.ts | 222 + .../browser/parts/editor/tabsTitleControl.ts | 788 ++ .../browser/parts/editor/textDiffEditor.ts | 377 + .../browser/parts/editor/textEditor.ts | 334 + .../parts/editor/textResourceEditor.ts | 202 + .../browser/parts/editor/titleControl.ts | 556 + .../browser/parts/editor/webviewEditor.ts | 71 + .../browser/parts/media/compositepart.css | 17 + .../parts/panel/media/close-inverse.svg | 1 + .../browser/parts/panel/media/close.svg | 1 + .../parts/panel/media/down-inverse.svg | 1 + .../browser/parts/panel/media/down.svg | 1 + .../browser/parts/panel/media/panelpart.css | 83 + .../browser/parts/panel/media/up-inverse.svg | 1 + .../browser/parts/panel/media/up.svg | 1 + .../browser/parts/panel/panelActions.ts | 156 + .../browser/parts/panel/panelPart.ts | 269 + .../parts/quickopen/media/dirty-inverse.svg | 1 + .../browser/parts/quickopen/media/dirty.svg | 1 + .../parts/quickopen/media/quickopen.css | 24 + .../parts/quickopen/quickOpenController.ts | 1323 ++ .../parts/quickopen/quickopen.contribution.ts | 95 + .../browser/parts/quickopen/quickopen.ts | 125 + .../parts/sidebar/media/sidebarpart.css | 57 + .../browser/parts/sidebar/sidebarPart.ts | 184 + .../parts/statusbar/media/statusbarpart.css | 58 + .../browser/parts/statusbar/statusbar.ts | 58 + .../browser/parts/statusbar/statusbarPart.ts | 352 + .../parts/titlebar/media/titlebarpart.css | 31 + .../browser/parts/titlebar/titlebarPart.ts | 376 + src/vs/workbench/browser/quickopen.ts | 380 + src/vs/workbench/browser/viewlet.ts | 280 + src/vs/workbench/buildfile.js | 35 + src/vs/workbench/common/actionRegistry.ts | 191 + src/vs/workbench/common/component.ts | 81 + src/vs/workbench/common/composite.ts | 50 + src/vs/workbench/common/contributions.ts | 60 + src/vs/workbench/common/editor.ts | 898 ++ .../common/editor/binaryEditorModel.ts | 68 + .../common/editor/diffEditorInput.ts | 102 + .../common/editor/diffEditorModel.ts | 55 + .../common/editor/editorStacksModel.ts | 1285 ++ .../common/editor/rangeDecorations.ts | 117 + .../common/editor/resourceEditorInput.ts | 119 + .../common/editor/resourceEditorModel.ts | 27 + .../common/editor/textDiffEditorModel.ts | 79 + .../common/editor/textEditorModel.ts | 169 + .../common/editor/untitledEditorInput.ts | 223 + .../common/editor/untitledEditorModel.ts | 238 + src/vs/workbench/common/memento.ts | 124 + src/vs/workbench/common/panel.ts | 9 + src/vs/workbench/common/resources.ts | 176 + src/vs/workbench/common/theme.ts | 441 + src/vs/workbench/common/viewlet.ts | 15 + src/vs/workbench/electron-browser/actions.ts | 1558 +++ .../electron-browser/bootstrap/index.html | 19 + .../electron-browser/bootstrap/index.js | 268 + .../electron-browser/bootstrap/preload.js | 39 + src/vs/workbench/electron-browser/commands.ts | 423 + .../electron-browser/main.contribution.ts | 398 + src/vs/workbench/electron-browser/main.ts | 238 + .../electron-browser/media/actions.css | 13 + .../electron-browser/media/clear.svg | 1 + .../electron-browser/media/remove-dark.svg | 1 + .../electron-browser/media/remove.svg | 1 + .../electron-browser/media/shell.css | 173 + .../electron-browser/media/workbench.css | 29 + .../electron-browser/nodeCachedDataManager.ts | 110 + src/vs/workbench/electron-browser/shell.ts | 567 + src/vs/workbench/electron-browser/window.ts | 394 + .../workbench/electron-browser/workbench.ts | 1440 ++ src/vs/workbench/node/extensionHostMain.ts | 255 + src/vs/workbench/node/extensionHostProcess.ts | 131 + .../backup/common/backup.contribution.ts | 21 + .../parts/backup/common/backupModelTracker.ts | 99 + .../parts/backup/common/backupRestorer.ts | 112 + .../cli/electron-browser/cli.contribution.ts | 159 + .../codeEditor/codeEditor.contribution.ts | 16 + .../electron-browser/accessibility.css | 9 + .../electron-browser/accessibility.ts | 337 + .../electron-browser/inspectKeybindings.ts | 35 + .../languageConfigurationExtensionPoint.ts | 384 + .../electron-browser/media/WordWrap_16x.svg | 1 + .../electron-browser/media/codeEditor.css | 8 + .../electron-browser/menuPreventer.ts | 64 + .../electron-browser/selectionClipboard.ts | 103 + .../textMate/inspectTMScopes.css | 43 + .../textMate/inspectTMScopes.ts | 390 + .../electron-browser/toggleMinimap.ts | 31 + .../toggleMultiCursorModifier.ts | 45 + .../toggleRenderControlCharacter.ts | 31 + .../toggleRenderWhitespace.ts | 37 + .../electron-browser/toggleWordWrap.ts | 280 + .../electron-browser/wordWrapMigration.ts | 142 + .../parts/debug/browser/breakpointWidget.ts | 158 + .../parts/debug/browser/debugActionItems.ts | 216 + .../parts/debug/browser/debugActions.ts | 840 ++ .../parts/debug/browser/debugActionsWidget.ts | 278 + .../debug/browser/debugContentProvider.ts | 77 + .../parts/debug/browser/debugEditorActions.ts | 277 + .../debug/browser/debugEditorModelManager.ts | 387 + .../parts/debug/browser/debugQuickOpen.ts | 80 + .../parts/debug/browser/debugViewlet.ts | 105 + .../parts/debug/browser/exceptionWidget.ts | 102 + .../parts/debug/browser/linkDetector.ts | 114 + .../parts/debug/browser/media/add-focus.svg | 1 + .../parts/debug/browser/media/add-inverse.svg | 1 + .../parts/debug/browser/media/add.svg | 1 + .../media/breakpoint-conditional-dark.svg | 10 + .../browser/media/breakpoint-conditional.svg | 10 + .../debug/browser/media/breakpoint-dark.svg | 1 + .../media/breakpoint-disabled-dark.svg | 1 + .../browser/media/breakpoint-disabled.svg | 1 + .../debug/browser/media/breakpoint-hint.svg | 1 + .../media/breakpoint-unsupported-dark.svg | 17 + .../browser/media/breakpoint-unsupported.svg | 17 + .../media/breakpoint-unverified-dark.svg | 1 + .../browser/media/breakpoint-unverified.svg | 1 + .../parts/debug/browser/media/breakpoint.svg | 1 + .../debug/browser/media/breakpointWidget.css | 41 + .../media/breakpoints-activate-inverse.svg | 1 + .../browser/media/breakpoints-activate.svg | 1 + .../browser/media/clear-repl-inverse.svg | 1 + .../parts/debug/browser/media/clear-repl.svg | 1 + .../debug/browser/media/configure-inverse.svg | 1 + .../parts/debug/browser/media/configure.svg | 1 + .../debug/browser/media/continue-inverse.svg | 3 + .../parts/debug/browser/media/continue.svg | 3 + .../browser/media/current-and-breakpoint.svg | 1 + .../debug/browser/media/current-arrow.svg | 1 + .../parts/debug/browser/media/debug-dark.svg | 1 + .../browser/media/debug.contribution.css | 263 + .../browser/media/debugActionsWidget.css | 129 + .../parts/debug/browser/media/debugHover.css | 117 + .../debug/browser/media/debugViewlet.css | 396 + .../browser/media/disconnect-inverse.svg | 1 + .../parts/debug/browser/media/disconnect.svg | 1 + .../parts/debug/browser/media/drag.svg | 8 + .../debug/browser/media/exceptionWidget.css | 48 + .../debug/browser/media/pause-inverse.svg | 1 + .../parts/debug/browser/media/pause.svg | 1 + .../browser/media/remove-all-inverse.svg | 1 + .../parts/debug/browser/media/remove-all.svg | 1 + .../debug/browser/media/remove-focus.svg | 1 + .../debug/browser/media/remove-inverse.svg | 1 + .../parts/debug/browser/media/remove.svg | 1 + .../debug/browser/media/repl-inverse.svg | 1 + .../parts/debug/browser/media/repl.css | 182 + .../parts/debug/browser/media/repl.svg | 1 + .../debug/browser/media/restart-inverse.svg | 1 + .../parts/debug/browser/media/restart.svg | 1 + .../media/stackframe-and-breakpoint-dark.svg | 1 + .../media/stackframe-and-breakpoint.svg | 1 + .../browser/media/stackframe-arrow-dark.svg | 1 + .../debug/browser/media/stackframe-arrow.svg | 1 + .../debug/browser/media/step-into-inverse.svg | 1 + .../parts/debug/browser/media/step-into.svg | 1 + .../debug/browser/media/step-out-inverse.svg | 1 + .../parts/debug/browser/media/step-out.svg | 1 + .../debug/browser/media/step-over-inverse.svg | 1 + .../parts/debug/browser/media/step-over.svg | 1 + .../debug/browser/media/stop-inverse.svg | 1 + .../parts/debug/browser/media/stop.svg | 1 + src/vs/workbench/parts/debug/common/debug.ts | 642 + .../parts/debug/common/debugModel.ts | 1063 ++ .../parts/debug/common/debugProtocol.d.ts | 1461 ++ .../parts/debug/common/debugSource.ts | 45 + .../parts/debug/common/debugViewModel.ts | 97 + .../parts/debug/common/replHistory.ts | 117 + .../electron-browser/debug.contribution.ts | 185 + .../debug/electron-browser/debugCommands.ts | 225 + .../debugConfigurationManager.ts | 593 + .../debugEditorContribution.ts | 620 + .../debug/electron-browser/debugHover.ts | 357 + .../debug/electron-browser/debugService.ts | 1192 ++ .../debug/electron-browser/debugViewer.ts | 1307 ++ .../debug/electron-browser/debugViews.ts | 533 + .../electron-browser/electronDebugActions.ts | 79 + .../debug/electron-browser/rawDebugSession.ts | 534 + .../parts/debug/electron-browser/repl.ts | 414 + .../debug/electron-browser/replEditor.ts | 51 + .../debug/electron-browser/replViewer.ts | 434 + .../statusbarColorProvider.ts | 117 + .../debug/electron-browser/terminalSupport.ts | 166 + .../parts/debug/node/debugAdapter.ts | 284 + .../parts/debug/node/telemetryApp.ts | 15 + .../workbench/parts/debug/node/v8Protocol.ts | 155 + .../debug/test/common/debugSource.test.ts | 39 + .../debug/test/common/debugUtils.test.ts | 20 + .../debug/test/common/debugViewModel.test.ts | 50 + .../parts/debug/test/common/mockDebug.ts | 253 + .../debug/test/common/replHistory.test.ts | 47 + .../debug/test/node/debugAdapter.test.ts | 133 + .../parts/debug/test/node/debugModel.test.ts | 385 + .../browser/actions/showEmmetCommands.ts | 35 + .../browser/emmet.browser.contribution.ts | 8 + .../actions/expandAbbreviation.ts | 35 + .../electron-browser/emmet.contribution.ts | 9 + .../emmet/electron-browser/emmetActions.ts | 131 + .../test/electron-browser/emmetAction.test.ts | 77 + .../parts/execution/common/execution.ts | 17 + .../electron-browser/TerminalHelper.scpt | Bin 0 -> 14736 bytes .../execution.contribution.ts | 227 + .../electron-browser/iTermHelper.scpt | Bin 0 -> 6790 bytes .../execution/electron-browser/terminal.ts | 47 + .../electron-browser/terminalService.ts | 298 + .../electron-browser/terminalService.test.ts | 223 + .../extensions/browser/dependenciesViewer.ts | 212 + .../extensions/browser/extensionEditor.ts | 910 ++ .../extensions/browser/extensionsActions.ts | 1512 +++ .../extensions/browser/extensionsList.ts | 160 + .../extensions/browser/extensionsQuickOpen.ts | 101 + .../extensions/browser/extensionsWidgets.ts | 161 + .../extensions/browser/media/EmptyStar.svg | 92 + .../browser/media/FullStarLight.svg | 92 + .../browser/media/HalfStarLight.svg | 91 + .../browser/media/clear-inverse.svg | 1 + .../parts/extensions/browser/media/clear.svg | 1 + .../extensions/browser/media/defaultIcon.png | Bin 0 -> 1686 bytes .../browser/media/extensionActions.css | 63 + .../browser/media/extensionEditor.css | 296 + .../browser/media/extensionsWidgets.css | 49 + .../extensions/browser/media/loading.svg | 36 + .../browser/media/manage-inverse.svg | 1 + .../parts/extensions/browser/media/manage.svg | 1 + .../extensions/browser/media/markdown.css | 161 + .../parts/extensions/common/extensionQuery.ts | 41 + .../parts/extensions/common/extensions.ts | 90 + .../common/extensionsFileTemplate.ts | 37 + .../extensions/common/extensionsInput.ts | 56 + .../electron-browser/extensionTipsService.ts | 375 + .../extensions.contribution.ts | 203 + .../electron-browser/extensionsActions.ts | 93 + .../electron-browser/extensionsUtils.ts | 184 + .../electron-browser/extensionsViewlet.ts | 437 + .../electron-browser/extensionsViews.ts | 478 + .../media/extensions-dark.svg | 6 + .../electron-browser/media/extensions.css | 9 + .../media/extensionsViewlet.css | 180 + .../electron-browser/media/loading.svg | 36 + .../node/extensionsWorkbenchService.ts | 856 ++ .../test/common/extensionQuery.test.ts | 143 + .../extensionsActions.test.ts | 1185 ++ .../extensionsWorkbenchService.test.ts | 1025 ++ .../electron-browser/feedback.contribution.ts | 16 + .../feedback/electron-browser/feedback.ts | 364 + .../electron-browser/feedbackStatusbarItem.ts | 91 + .../electron-browser/media/close-dark.svg | 1 + .../feedback/electron-browser/media/close.svg | 1 + .../electron-browser/media/feedback.css | 283 + .../feedback/electron-browser/media/happy.svg | 1 + .../feedback/electron-browser/media/info.svg | 1 + .../feedback/electron-browser/media/sad.svg | 1 + .../electron-browser/media/smiley.svg | 1 + .../electron-browser/media/twitter.svg | 549 + .../files/browser/editors/binaryFileEditor.ts | 32 + .../files/browser/editors/textFileEditor.ts | 258 + .../parts/files/browser/explorerViewlet.ts | 258 + .../files/browser/fileActions.contribution.ts | 342 + .../parts/files/browser/fileActions.ts | 2135 +++ .../parts/files/browser/fileCommands.ts | 268 + .../files/browser/fileResultsNavigation.ts | 73 + .../parts/files/browser/files.contribution.ts | 341 + .../parts/files/browser/media/AddFile.svg | 1 + .../files/browser/media/AddFile_inverse.svg | 3 + .../parts/files/browser/media/AddFolder.svg | 1 + .../files/browser/media/AddFolder_inverse.svg | 3 + .../parts/files/browser/media/CollapseAll.svg | 1 + .../browser/media/CollapseAll_inverse.svg | 1 + .../parts/files/browser/media/Preview.svg | 1 + .../files/browser/media/Preview_inverse.svg | 1 + .../parts/files/browser/media/Refresh.svg | 1 + .../files/browser/media/Refresh_inverse.svg | 1 + .../files/browser/media/action-close-dark.svg | 1 + .../browser/media/action-close-dirty-dark.svg | 1 + .../media/action-close-dirty-focus.svg | 1 + .../browser/media/action-close-dirty.svg | 1 + .../browser/media/action-close-focus.svg | 1 + .../files/browser/media/action-close.svg | 1 + .../files/browser/media/check-inverse.svg | 1 + .../parts/files/browser/media/check.svg | 1 + .../parts/files/browser/media/closeall.svg | 1 + .../files/browser/media/closeall_inverse.svg | 1 + .../files/browser/media/collapsed-dark.svg | 1 + .../files/browser/media/collapsed-hc.svg | 1 + .../parts/files/browser/media/collapsed.svg | 1 + .../files/browser/media/expanded-dark.svg | 1 + .../parts/files/browser/media/expanded-hc.svg | 1 + .../parts/files/browser/media/expanded.svg | 1 + .../files/browser/media/explorerviewlet.css | 189 + .../parts/files/browser/media/fileactions.css | 127 + .../parts/files/browser/media/files-dark.svg | 1 + .../parts/files/browser/media/saveall.svg | 1 + .../files/browser/media/saveall_inverse.svg | 1 + .../media/split-editor-horizontal-inverse.svg | 1 + .../browser/media/split-editor-horizontal.svg | 1 + .../media/split-editor-vertical-inverse.svg | 1 + .../browser/media/split-editor-vertical.svg | 1 + .../files/browser/media/undo-inverse.svg | 1 + .../parts/files/browser/media/undo.svg | 1 + .../parts/files/browser/saveErrorHandler.ts | 312 + .../parts/files/browser/views/emptyView.ts | 108 + .../parts/files/browser/views/explorerView.ts | 946 ++ .../files/browser/views/explorerViewer.ts | 1010 ++ .../files/browser/views/openEditorsView.ts | 333 + .../files/browser/views/openEditorsViewer.ts | 512 + .../parts/files/common/dirtyFilesTracker.ts | 164 + .../files/common/editors/fileEditorInput.ts | 277 + .../files/common/editors/fileEditorTracker.ts | 319 + .../parts/files/common/explorerModel.ts | 426 + src/vs/workbench/parts/files/common/files.ts | 107 + .../files/test/browser/explorerModel.test.ts | 276 + .../test/browser/fileEditorInput.test.ts | 211 + .../test/browser/fileEditorTracker.test.ts | 209 + .../parts/html/browser/html.contribution.ts | 132 + .../parts/html/browser/htmlPreviewPart.ts | 236 + .../html/browser/media/htmlPreviewPart.css | 8 + .../parts/html/browser/webview-pre.js | 271 + .../workbench/parts/html/browser/webview.html | 8 + .../workbench/parts/html/browser/webview.ts | 466 + .../parts/html/browser/webviewEditor.ts | 188 + .../parts/html/browser/webviewFindWidget.ts | 64 + .../workbench/parts/html/common/htmlInput.ts | 32 + .../parts/markers/browser/markersPanel.ts | 418 + .../markers/browser/markersPanelActions.ts | 164 + .../markers/browser/markersTreeController.ts | 92 + .../markers/browser/markersTreeViewer.ts | 232 + .../browser/markersWorkbenchContributions.ts | 70 + .../parts/markers/browser/media/markers.css | 121 + .../browser/media/status-error-inverse.svg | 1 + .../markers/browser/media/status-error.svg | 1 + .../browser/media/status-info-inverse.svg | 1 + .../markers/browser/media/status-info.svg | 1 + .../browser/media/status-warning-inverse.svg | 1 + .../markers/browser/media/status-warning.svg | 1 + .../parts/markers/common/constants.ts | 14 + .../parts/markers/common/markersModel.ts | 366 + .../parts/markers/common/messages.ts | 62 + .../markersElectronContributions.ts | 106 + .../parts/markers/markers.contribution.ts | 9 + .../markers/test/common/markersModel.test.ts | 197 + .../output/browser/media/clear_output.svg | 1 + .../browser/media/clear_output_inverse.svg | 1 + .../parts/output/browser/media/output.css | 31 + .../output/browser/media/output_lock.svg | 1 + .../browser/media/output_lock_inverse.svg | 1 + .../output/browser/media/output_unlock.svg | 1 + .../browser/media/output_unlock_inverse.svg | 1 + .../output/browser/output.contribution.ts | 131 + .../parts/output/browser/outputActions.ts | 142 + .../parts/output/browser/outputPanel.ts | 122 + .../parts/output/browser/outputServices.ts | 383 + .../workbench/parts/output/common/output.ts | 215 + .../parts/output/common/outputLinkComputer.ts | 182 + .../parts/output/common/outputLinkProvider.ts | 108 + .../parts/output/test/bufferedContent.test.ts | 61 + .../output/test/outputLinkProvider.test.ts | 422 + .../performance.contribution.ts | 218 + .../preferences/browser/keybindingWidgets.ts | 298 + .../preferences/browser/keybindingsEditor.ts | 808 ++ .../browser/keybindingsEditorContribution.ts | 394 + .../parts/preferences/browser/media/add.svg | 1 + .../preferences/browser/media/add_inverse.svg | 1 + .../preferences/browser/media/collapseAll.svg | 1 + .../browser/media/collapseAll_inverse.svg | 1 + .../browser/media/collapsed-dark.svg | 1 + .../preferences/browser/media/collapsed.svg | 1 + .../parts/preferences/browser/media/edit.svg | 1 + .../browser/media/edit_inverse.svg | 1 + .../browser/media/expanded-dark.svg | 1 + .../preferences/browser/media/expanded.svg | 1 + .../parts/preferences/browser/media/info.svg | 1 + .../preferences/browser/media/keybindings.css | 66 + .../browser/media/keybindingsEditor.css | 190 + .../preferences/browser/media/preferences.css | 241 + .../browser/media/sort_precedence.svg | 1 + .../browser/media/sort_precedence_inverse.svg | 1 + .../browser/media/status-error.svg | 1 + .../browser/preferences.contribution.ts | 249 + .../preferences/browser/preferencesActions.ts | 173 + .../preferences/browser/preferencesEditor.ts | 948 ++ .../browser/preferencesRenderers.ts | 1109 ++ .../preferences/browser/preferencesService.ts | 422 + .../preferences/browser/preferencesWidgets.ts | 652 + .../common/keybindingsEditorModel.ts | 563 + .../parts/preferences/common/preferences.ts | 129 + .../common/preferencesContentProvider.ts | 62 + .../preferences/common/preferencesModels.ts | 920 ++ .../common/smartSnippetInserter.ts | 159 + .../keybindingsEditorContribution.test.ts | 42 + .../common/keybindingsEditorModel.test.ts | 633 + .../test/common/smartSnippetInserter.test.ts | 164 + .../quickopen/browser/commandsHandler.ts | 616 + .../quickopen/browser/gotoLineHandler.ts | 284 + .../quickopen/browser/gotoSymbolHandler.ts | 593 + .../parts/quickopen/browser/helpHandler.ts | 125 + .../quickopen/browser/media/Constant_16x.svg | 1 + .../browser/media/Constant_16x_inverse.svg | 1 + .../quickopen/browser/media/EnumItem_16x.svg | 1 + .../browser/media/EnumItem_inverse_16x.svg | 1 + .../browser/media/Event_16x_vscode.svg | 1 + .../media/Event_16x_vscode_inverse.svg | 1 + .../browser/media/Operator_16x_vscode.svg | 1 + .../media/Operator_16x_vscode_inverse.svg | 1 + .../browser/media/Structure_16x_vscode.svg | 1 + .../media/Structure_16x_vscode_inverse.svg | 1 + .../browser/media/Template_16x_vscode.svg | 1 + .../media/Template_16x_vscode_inverse.svg | 1 + .../browser/media/gotoSymbolHandler.css | 154 + .../quickopen/browser/media/symbol-sprite.svg | 1 + .../browser/quickopen.contribution.ts | 146 + .../quickopen/browser/viewPickerHandler.ts | 210 + .../relauncher.contribution.ts | 167 + src/vs/workbench/parts/scm/common/scm.ts | 8 + .../electron-browser/dirtydiffDecorator.ts | 332 + .../electron-browser/media/check-inverse.svg | 1 + .../scm/electron-browser/media/check.svg | 1 + .../media/dirtydiffDecorator.css | 20 + .../scm/electron-browser/media/icon-dark.svg | 12 + .../scm/electron-browser/media/icon-light.svg | 12 + .../scm/electron-browser/media/scmViewlet.css | 128 + .../scm/electron-browser/scm.contribution.ts | 62 + .../parts/scm/electron-browser/scmActivity.ts | 155 + .../parts/scm/electron-browser/scmMenus.ts | 102 + .../parts/scm/electron-browser/scmUtil.ts | 16 + .../parts/scm/electron-browser/scmViewlet.ts | 630 + .../search/browser/media/CollapseAll.svg | 1 + .../browser/media/CollapseAll_inverse.svg | 1 + .../parts/search/browser/media/Refresh.svg | 1 + .../search/browser/media/Refresh_inverse.svg | 1 + .../browser/media/action-query-clear-dark.svg | 1 + .../browser/media/action-query-clear.svg | 1 + .../browser/media/action-remove-dark.svg | 1 + .../browser/media/action-remove-focus.svg | 1 + .../search/browser/media/action-remove.svg | 1 + .../media/clear-search-results-dark.svg | 1 + .../browser/media/clear-search-results.svg | 1 + .../browser/media/configure-inverse.svg | 1 + .../parts/search/browser/media/configure.svg | 1 + .../search/browser/media/ellipsis-inverse.svg | 1 + .../parts/search/browser/media/ellipsis.svg | 1 + .../browser/media/excludeSettings-dark.svg | 1 + .../search/browser/media/excludeSettings.svg | 1 + .../browser/media/expando-collapsed-dark.svg | 1 + .../browser/media/expando-collapsed.svg | 1 + .../browser/media/expando-expanded-dark.svg | 1 + .../search/browser/media/expando-expanded.svg | 1 + .../search/browser/media/gitignore-dark.svg | 1 + .../parts/search/browser/media/gitignore.svg | 1 + .../search/browser/media/pattern-dark.svg | 1 + .../parts/search/browser/media/pattern.svg | 1 + .../browser/media/replace-all-inverse.svg | 11 + .../search/browser/media/replace-all.svg | 11 + .../search/browser/media/replace-inverse.svg | 13 + .../parts/search/browser/media/replace.svg | 13 + .../search/browser/media/search-dark.svg | 1 + .../browser/media/search.contribution.css | 9 + .../search/browser/media/searchviewlet.css | 413 + .../search/browser/openAnythingHandler.ts | 367 + .../parts/search/browser/openFileHandler.ts | 300 + .../parts/search/browser/openSymbolHandler.ts | 215 + .../search/browser/patternInputWidget.ts | 271 + .../search/browser/replaceContributions.ts | 14 + .../parts/search/browser/replaceService.ts | 194 + .../search/browser/search.contribution.ts | 374 + .../parts/search/browser/searchActions.ts | 642 + .../parts/search/browser/searchResultsView.ts | 329 + .../parts/search/browser/searchViewlet.ts | 1477 ++ .../parts/search/browser/searchWidget.ts | 388 + .../parts/search/common/constants.ts | 35 + .../parts/search/common/queryBuilder.ts | 310 + .../workbench/parts/search/common/replace.ts | 38 + .../workbench/parts/search/common/search.ts | 104 + .../parts/search/common/searchModel.ts | 821 ++ .../test/browser/openFileHandler.test.ts | 203 + .../search/test/browser/searchActions.test.ts | 151 + .../search/test/browser/searchViewlet.test.ts | 80 + .../search/test/common/queryBuilder.test.ts | 682 + .../search/test/common/searchModel.test.ts | 320 + .../search/test/common/searchResult.test.ts | 387 + .../snippets/electron-browser/TMSnippets.ts | 199 + .../electron-browser/insertSnippet.ts | 139 + .../electron-browser/snippets.contribution.ts | 146 + .../electron-browser/snippetsService.ts | 225 + .../electron-browser/snippetsTracker.ts | 68 + .../electron-browser/tabCompletion.ts | 127 + .../electron-browser/snippetsRegistry.test.ts | 80 + .../electron-browser/snippetsRewrite.test.ts | 47 + .../electron-browser/snippetsService.test.ts | 112 + .../languageSurveys.contribution.ts | 143 + .../electron-browser/nps.contribution.ts | 100 + .../parts/tasks/browser/quickOpen.ts | 204 + .../parts/tasks/browser/taskQuickOpen.ts | 66 + .../parts/tasks/common/problemCollectors.ts | 459 + .../tasks/common/taskDefinitionRegistry.ts | 129 + .../parts/tasks/common/taskService.ts | 68 + .../parts/tasks/common/taskSystem.ts | 120 + .../parts/tasks/common/taskTemplates.ts | 350 + src/vs/workbench/parts/tasks/common/tasks.ts | 455 + .../electron-browser/jsonSchemaCommon.ts | 244 + .../tasks/electron-browser/jsonSchema_v1.ts | 104 + .../tasks/electron-browser/jsonSchema_v2.ts | 338 + .../media/configure-inverse.svg | 1 + .../electron-browser/media/configure.svg | 1 + .../electron-browser/media/status-error.svg | 1 + .../electron-browser/media/status-info.svg | 1 + .../electron-browser/media/status-warning.svg | 1 + .../media/task.contribution.css | 88 + .../tasks/electron-browser/media/task.svg | 1 + .../electron-browser/task.contribution.ts | 2125 +++ .../electron-browser/terminalTaskSystem.ts | 751 ++ .../parts/tasks/node/processRunnerDetector.ts | 388 + .../parts/tasks/node/processTaskSystem.ts | 399 + .../parts/tasks/node/taskConfiguration.ts | 1909 +++ .../electron-browser/configuration.test.ts | 1673 +++ .../terminal/browser/terminalQuickOpen.ts | 133 + .../terminal/browser/terminalWidgetManager.ts | 93 + .../parts/terminal/common/terminal.ts | 379 + .../parts/terminal/common/terminalService.ts | 234 + .../media/configure-inverse.svg | 1 + .../electron-browser/media/configure.svg | 1 + .../electron-browser/media/kill-inverse.svg | 10 + .../terminal/electron-browser/media/kill.svg | 10 + .../electron-browser/media/new-inverse.svg | 10 + .../terminal/electron-browser/media/new.svg | 10 + .../electron-browser/media/scrollbar.css | 37 + .../electron-browser/media/terminal.css | 91 + .../electron-browser/media/widgets.css | 20 + .../terminal/electron-browser/media/xterm.css | 2103 +++ .../electron-browser/terminal.contribution.ts | 360 + .../terminal/electron-browser/terminal.ts | 23 + .../electron-browser/terminalActions.ts | 796 ++ .../electron-browser/terminalColorRegistry.ts | 170 + .../electron-browser/terminalConfigHelper.ts | 178 + .../electron-browser/terminalFindWidget.ts | 59 + .../electron-browser/terminalInstance.ts | 958 ++ .../electron-browser/terminalLinkHandler.ts | 331 + .../electron-browser/terminalPanel.ts | 381 + .../electron-browser/terminalService.ts | 246 + .../electron-browser/windowsShellHelper.ts | 111 + .../parts/terminal/node/terminalProcess.ts | 169 + .../terminalColorRegistry.test.ts | 104 + .../terminalConfigHelper.test.ts | 158 + .../electron-browser/terminalInstance.test.ts | 144 + .../terminalLinkHandler.test.ts | 204 + .../electron-browser/terminalPanel.test.ts | 22 + .../electron-browser/themes.contribution.ts | 217 + .../test/electron-browser/fixtures/foo.js | 14 + .../themes.test.contribution.ts | 267 + ...supportedWorkspaceSettings.contribution.ts | 98 + .../electron-browser/media/markdown.css | 165 + .../media/update.contribution.css | 9 + .../update/electron-browser/media/update.svg | 1 + .../electron-browser/releaseNotesEditor.ts | 155 + .../electron-browser/releaseNotesInput.ts | 48 + .../electron-browser/update.contribution.ts | 63 + .../parts/update/electron-browser/update.ts | 474 + .../parts/views/browser/media/views.css | 26 + .../workbench/parts/views/browser/treeView.ts | 459 + src/vs/workbench/parts/views/browser/views.ts | 786 ++ .../views/browser/viewsExtensionPoint.ts | 111 + .../parts/views/browser/viewsRegistry.ts | 144 + src/vs/workbench/parts/views/common/views.ts | 50 + .../watermark/electron-browser/watermark.css | 92 + .../watermark/electron-browser/watermark.ts | 228 + src/vs/workbench/parts/welcome/code-icon.svg | 1 + .../gettingStarted.contribution.ts | 13 + .../electron-browser/gettingStarted.ts | 112 + .../test/common/gettingStarted.test.ts | 47 + .../browser/media/commandpalette-dark.svg | 1 + .../overlay/browser/media/commandpalette.svg | 1 + .../overlay/browser/welcomeOverlay.css | 157 + .../welcome/overlay/browser/welcomeOverlay.ts | 252 + .../electron-browser/vs_code_welcome_page.ts | 80 + .../welcomePage.contribution.ts | 42 + .../page/electron-browser/welcomePage.css | 226 + .../page/electron-browser/welcomePage.ts | 564 + .../editor/editorWalkThrough.ts | 59 + .../editor/vs_code_editor_walkthrough.md | 165 + .../walkThrough.contribution.ts | 54 + .../electron-browser/walkThroughActions.ts | 99 + .../electron-browser/walkThroughPart.css | 140 + .../electron-browser/walkThroughPart.ts | 587 + .../node/walkThroughContentProvider.ts | 105 + .../walkThrough/node/walkThroughInput.ts | 186 + .../walkThrough/node/walkThroughUtils.ts | 25 + .../activity/common/activityBarService.ts | 95 + .../services/backup/common/backup.ts | 80 + .../services/backup/node/backupFileService.ts | 268 + .../test/node/backupFileService.test.ts | 382 + .../configuration/common/configuration.ts | 29 + .../common/configurationEditing.ts | 104 + .../common/configurationModels.ts | 181 + .../configuration/common/jsonEditing.ts | 41 + .../configuration/node/configuration.ts | 916 ++ .../node/configurationEditingService.ts | 353 + .../configuration/node/jsonEditingService.ts | 134 + .../test/common/configurationModels.test.ts | 97 + .../test/node/configuration.test.ts | 464 + .../node/configurationEditingService.test.ts | 315 + .../common/configurationResolver.ts | 22 + .../node/configurationResolverService.ts | 263 + .../node/configurationResolverService.test.ts | 365 + .../electron-browser/contextmenuService.ts | 121 + .../common/crashReporterService.ts | 43 + .../electron-browser/crashReporterService.ts | 95 + .../services/editor/browser/editorService.ts | 363 + .../services/editor/common/editorService.ts | 94 + .../editor/test/browser/editorService.test.ts | 282 + .../electron-browser/extensionHost.ts | 500 + .../electron-browser/extensionPoints.ts | 363 + .../electron-browser/extensionService.ts | 449 + .../services/extensions/node/barrier.ts | 39 + .../node/extensionDescriptionRegistry.ts | 64 + .../services/extensions/node/lazyPromise.ts | 110 + .../services/extensions/node/rpcProtocol.ts | 198 + .../files/electron-browser/fileService.ts | 308 + .../electron-browser/remoteFileService.ts | 110 + .../services/files/node/fileService.ts | 981 ++ .../services/files/node/watcher/common.ts | 119 + .../node/watcher/nsfw/nsfwWatcherService.ts | 176 + .../nsfw/test/nsfwWatcherService.test.ts | 54 + .../files/node/watcher/nsfw/watcher.ts | 23 + .../files/node/watcher/nsfw/watcherApp.ts | 14 + .../files/node/watcher/nsfw/watcherIpc.ts | 42 + .../files/node/watcher/nsfw/watcherService.ts | 128 + .../watcher/unix/chokidarWatcherService.ts | 143 + .../files/node/watcher/unix/watcher.ts | 18 + .../files/node/watcher/unix/watcherApp.ts | 14 + .../files/node/watcher/unix/watcherIpc.ts | 36 + .../files/node/watcher/unix/watcherService.ts | 97 + .../files/node/watcher/win32/CodeHelper.exe | Bin 0 -> 83968 bytes .../files/node/watcher/win32/CodeHelper.md | 8 + .../watcher/win32/csharpWatcherService.ts | 119 + .../node/watcher/win32/watcherService.ts | 69 + .../files/test/node/fileService.test.ts | 814 ++ .../fixtures/resolver/examples/company.js | 23 + .../node/fixtures/resolver/examples/conway.js | 117 + .../fixtures/resolver/examples/employee.js | 38 + .../node/fixtures/resolver/examples/small.js | 24 + .../test/node/fixtures/resolver/index.html | 121 + .../fixtures/resolver/other/deep/company.js | 23 + .../fixtures/resolver/other/deep/conway.js | 117 + .../fixtures/resolver/other/deep/employee.js | 38 + .../fixtures/resolver/other/deep/small.js | 24 + .../test/node/fixtures/resolver/site.css | 40 + .../test/node/fixtures/service/binary.txt | Bin 0 -> 274 bytes .../node/fixtures/service/deep/company.js | 23 + .../test/node/fixtures/service/deep/conway.js | 117 + .../node/fixtures/service/deep/employee.js | 38 + .../test/node/fixtures/service/deep/small.js | 24 + .../test/node/fixtures/service/index.html | 121 + .../test/node/fixtures/service/small.txt | 1 + .../node/fixtures/service/some_utf16le.css | Bin 0 -> 1408 bytes .../node/fixtures/service/some_utf8_bom.txt | 1 + .../services/files/test/node/resolver.test.ts | 188 + .../services/files/test/node/utils.ts | 18 + .../services/files/test/node/watcher.test.ts | 226 + .../services/group/common/groupService.ts | 134 + .../services/history/browser/history.ts | 708 + .../services/history/common/history.ts | 63 + .../keybinding/common/keybindingEditing.ts | 265 + .../keybinding/common/keybindingIO.ts | 194 + .../keybinding/common/keyboardMapper.ts | 17 + .../common/macLinuxFallbackKeyboardMapper.ts | 149 + .../common/macLinuxKeyboardMapper.ts | 1236 ++ .../services/keybinding/common/scanCode.ts | 691 + .../common/windowsKeyboardMapper.ts | 754 ++ .../electron-browser/keybindingService.ts | 588 + .../electron-browser/lifecycleService.ts | 110 + .../message/browser/media/messageList.css | 117 + .../services/message/browser/messageList.ts | 490 + .../message/browser/messageService.ts | 150 + .../electron-browser/messageService.ts | 105 + .../mode/common/workbenchModeService.ts | 207 + .../services/panel/common/panelService.ts | 40 + .../services/part/common/partService.ts | 124 + .../progress/browser/media/progress.svg | 31 + .../browser/media/progressService2.css | 18 + .../progress/browser/progressService.ts | 257 + .../progress/browser/progressService2.ts | 188 + .../progress/test/progressService.test.ts | 291 + src/vs/workbench/services/scm/common/scm.ts | 80 + .../services/scm/common/scmService.ts | 95 + .../services/search/node/fileSearch.ts | 741 + .../services/search/node/rawSearchService.ts | 496 + .../services/search/node/ripgrepTextSearch.ts | 553 + .../workbench/services/search/node/search.ts | 65 + .../services/search/node/searchApp.ts | 15 + .../services/search/node/searchIpc.ts | 48 + .../services/search/node/searchService.ts | 321 + .../services/search/node/textSearch.ts | 166 + .../search/node/textSearchWorkerProvider.ts | 52 + .../search/node/worker/searchWorker.ts | 369 + .../search/node/worker/searchWorkerApp.ts | 15 + .../search/node/worker/searchWorkerIpc.ts | 68 + .../search/test/node/fixtures/binary.wuff | Bin 0 -> 5486 bytes .../fixtures/examples/NullPoinderException.js | 118 + .../test/node/fixtures/examples/company.js | 23 + .../test/node/fixtures/examples/employee.js | 11190 ++++++++++++++++ .../test/node/fixtures/examples/small.js | 24 + .../subfolder/anotherfolder/anotherfile.txt | 0 .../fixtures/examples/subfolder/subfile.txt | 1 + .../search/test/node/fixtures/index.html | 121 + .../search/test/node/fixtures/more/file.txt | 1 + .../search/test/node/fixtures/site.css | 35 + .../search/test/node/fixtures/site.less | 2 + .../test/node/fixtures/some_utf16be.css | Bin 0 -> 1408 bytes .../test/node/fixtures/some_utf16le.css | Bin 0 -> 1408 bytes .../node/fixtures/üm laut汉语/汉语.txt | 1 + .../test/node/ripgrepTextSearch.test.ts | 215 + .../services/search/test/node/search.test.ts | 723 + .../search/test/node/searchService.test.ts | 271 + .../test/node/textSearch.integrationTest.ts | 310 + .../telemetry/common/workspaceStats.ts | 334 + .../telemetry/test/workspaceStats.test.ts | 110 + .../textMate/electron-browser/OSSREADME.json | 37 + .../textMate/electron-browser/TMGrammars.ts | 57 + .../textMate/electron-browser/TMHelper.ts | 137 + .../textMate/electron-browser/TMSyntax.ts | 361 + .../electron-browser/textMateService.ts | 21 + .../textfile/common/textFileEditorModel.ts | 1076 ++ .../common/textFileEditorModelManager.ts | 348 + .../textfile/common/textFileService.ts | 717 + .../services/textfile/common/textfiles.ts | 317 + .../textfile/electron-browser/modelBuilder.ts | 221 + .../electron-browser/textFileService.ts | 194 + .../textfile/test/modelBuilder.test.ts | 152 + .../textfile/test/modelBuilderAuto.test.ts | 138 + .../textfile/test/textFileEditorModel.test.ts | 450 + .../test/textFileEditorModelManager.test.ts | 348 + .../textfile/test/textFileService.test.ts | 415 + .../common/textModelResolverService.ts | 149 + .../test/textModelResolverService.test.ts | 169 + .../themes/common/colorThemeSchema.ts | 200 + .../themes/common/fileIconThemeSchema.ts | 218 + .../themes/common/workbenchThemeService.ts | 101 + .../themes/electron-browser/colorThemeData.ts | 347 + .../electron-browser/themeCompatibility.ts | 78 + .../electron-browser/workbenchThemeService.ts | 1062 ++ .../services/thread/common/threadService.ts | 42 + .../thread/electron-browser/threadService.ts | 44 + .../thread/node/abstractThreadService.ts | 85 + .../thread/node/extHostThreadService.ts | 15 + .../services/timer/common/timerService.ts | 78 + .../services/timer/node/timerService.ts | 134 + .../services/title/common/titleService.ts | 23 + .../untitled/common/untitledEditorService.ts | 306 + .../services/viewlet/browser/viewlet.ts | 51 + .../viewlet/browser/viewletService.ts | 107 + .../workspace/common/workspaceEditing.ts | 38 + .../workspace/node/workspaceEditingService.ts | 100 + .../node/workspaceMigrationService.ts | 79 + .../test/browser/actionRegistry.test.ts | 60 + .../test/browser/editorStacksModel.test.ts | 1989 +++ src/vs/workbench/test/browser/part.test.ts | 157 + .../browser/parts/editor/baseEditor.test.ts | 208 + .../workbench/test/browser/quickopen.test.ts | 89 + src/vs/workbench/test/browser/viewlet.test.ts | 45 + .../test/common/editor/editor.test.ts | 88 + .../common/editor/editorDiffModel.test.ts | 84 + .../test/common/editor/editorInput.test.ts | 94 + .../test/common/editor/editorModel.test.ts | 69 + .../test/common/editor/editorOptions.test.ts | 41 + .../common/editor/rangeDecorations.test.ts | 175 + .../common/editor/resourceEditorInput.test.ts | 45 + .../test/common/editor/untitledEditor.test.ts | 316 + src/vs/workbench/test/common/memento.test.ts | 183 + .../api/extHostApiCommands.test.ts | 439 + .../api/extHostCommands.test.ts | 60 + .../api/extHostConfiguration.test.ts | 405 + .../api/extHostDiagnostics.test.ts | 263 + .../api/extHostDocumentData.test.ts | 469 + .../extHostDocumentSaveParticipant.test.ts | 359 + .../api/extHostDocumentsAndEditors.test.ts | 61 + .../api/extHostFileSystemEventService.test.ts | 26 + .../api/extHostLanguageFeatures.test.ts | 1118 ++ .../api/extHostMessagerService.test.ts | 140 + .../api/extHostTextEditor.test.ts | 349 + .../api/extHostTreeViews.test.ts | 144 + .../electron-browser/api/extHostTypes.test.ts | 469 + .../api/extHostWorkspace.test.ts | 175 + .../api/mainThreadCommands.test.ts | 45 + .../api/mainThreadConfiguration.test.ts | 228 + .../api/mainThreadDiagnostics.test.ts | 41 + .../api/mainThreadDocuments.test.ts | 64 + .../api/mainThreadDocumentsAndEditors.test.ts | 167 + .../api/mainThreadSaveParticipant.test.ts | 75 + .../test/electron-browser/api/mock.ts | 14 + .../electron-browser/api/testThreadService.ts | 161 + .../quickopen.perf.integrationTest.ts | 179 + .../textsearch.perf.integrationTest.ts | 168 + .../workbench/test/workbenchTestServices.ts | 1141 ++ src/vs/workbench/workbench.main.css | 9 + src/vs/workbench/workbench.main.nls.js | 8 + src/vs/workbench/workbench.main.ts | 152 + test/OSSREADME.json | 35 + test/README.md | 32 + test/all.js | 294 + test/assert.js | 474 + test/browser.js | 48 + test/electron/index.js | 125 + test/electron/renderer.html | 19 + test/electron/renderer.js | 265 + test/index.html | 31 + test/mocha.opts | 5 + test/smoke/.gitignore | 7 + test/smoke/Audit.md | 13 + test/smoke/README.md | 64 + test/smoke/package.json | 26 + test/smoke/src/areas/common.ts | 188 + test/smoke/src/areas/configuration-views.ts | 64 + test/smoke/src/areas/css.ts | 62 + test/smoke/src/areas/data-loss.ts | 26 + test/smoke/src/areas/extensions.ts | 112 + test/smoke/src/areas/first-experience.ts | 21 + test/smoke/src/areas/git.ts | 167 + test/smoke/src/areas/integrated-terminal.ts | 54 + test/smoke/src/areas/javascript-debug.ts | 53 + test/smoke/src/areas/javascript.ts | 186 + test/smoke/src/areas/localization.ts | 70 + test/smoke/src/areas/search.ts | 74 + test/smoke/src/areas/statusbar.ts | 103 + test/smoke/src/areas/tasks.ts | 89 + test/smoke/src/helpers/screenshot.ts | 40 + test/smoke/src/helpers/utilities.ts | 37 + test/smoke/src/main.ts | 222 + test/smoke/src/mocha-runner.ts | 16 + test/smoke/src/spectron/application.ts | 171 + test/smoke/src/spectron/client.ts | 127 + test/smoke/src/test.ts | 40 + test/smoke/src/tests/configuration-views.ts | 57 + test/smoke/src/tests/css.ts | 61 + test/smoke/src/tests/data-loss.ts | 74 + test/smoke/src/tests/data-migration.ts | 105 + test/smoke/src/tests/explorer.ts | 43 + test/smoke/src/tests/extensions.ts | 74 + test/smoke/src/tests/git.ts | 69 + test/smoke/src/tests/integrated-terminal.ts | 40 + test/smoke/src/tests/javascript-debug.ts | 44 + test/smoke/src/tests/javascript.ts | 87 + test/smoke/src/tests/localization.ts | 50 + test/smoke/src/tests/multiroot.ts | 43 + test/smoke/src/tests/search.ts | 73 + test/smoke/src/tests/statusbar.ts | 94 + test/smoke/src/tests/tasks.ts | 56 + test/smoke/tsconfig.json | 21 + tsfmt.json | 17 + tslint.json | 77 + typings.json | 25 + 8829 files changed, 759707 insertions(+), 286 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintrc create mode 100644 .gitattributes create mode 100644 .mention-bot create mode 100644 .travis.yml create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.txt create mode 100644 OSSREADME.json create mode 100644 ThirdPartyNotices.txt create mode 100644 appveyor.yml create mode 100644 build/gulpfile.editor.js create mode 100644 build/gulpfile.extensions.js create mode 100644 build/gulpfile.hygiene.js create mode 100644 build/gulpfile.mixin.js create mode 100644 build/gulpfile.sql.js create mode 100644 build/gulpfile.test.js create mode 100644 build/gulpfile.vscode.js create mode 100644 build/gulpfile.vscode.linux.js create mode 100644 build/gulpfile.vscode.win32.js create mode 100644 build/lib/bundle.js create mode 100644 build/lib/bundle.ts create mode 100644 build/lib/compilation.js create mode 100644 build/lib/compilation.ts create mode 100644 build/lib/extensions.js create mode 100644 build/lib/extensions.ts create mode 100644 build/lib/git.js create mode 100644 build/lib/git.ts create mode 100644 build/lib/i18n.js create mode 100644 build/lib/i18n.resources.json create mode 100644 build/lib/i18n.ts create mode 100644 build/lib/nls.js create mode 100644 build/lib/nls.ts create mode 100644 build/lib/optimize.js create mode 100644 build/lib/optimize.ts create mode 100644 build/lib/reporter.js create mode 100644 build/lib/reporter.ts create mode 100644 build/lib/snapshotLoader.js create mode 100644 build/lib/snapshotLoader.ts create mode 100644 build/lib/test/i18n.test.js create mode 100644 build/lib/test/i18n.test.ts create mode 100644 build/lib/tslint/allowAsyncRule.js create mode 100644 build/lib/tslint/allowAsyncRule.ts create mode 100644 build/lib/tslint/duplicateImportsRule.js create mode 100644 build/lib/tslint/duplicateImportsRule.ts create mode 100644 build/lib/tslint/importPatternsRule.js create mode 100644 build/lib/tslint/importPatternsRule.ts create mode 100644 build/lib/tslint/layeringRule.js create mode 100644 build/lib/tslint/layeringRule.ts create mode 100644 build/lib/tslint/noUnexternalizedStringsRule.js create mode 100644 build/lib/tslint/noUnexternalizedStringsRule.ts create mode 100644 build/lib/tslint/translationRemindRule.js create mode 100644 build/lib/tslint/translationRemindRule.ts create mode 100644 build/lib/typings/OSSREADME.json create mode 100644 build/lib/typings/Q.d.ts create mode 100644 build/lib/typings/chalk.d.ts create mode 100644 build/lib/typings/debounce.d.ts create mode 100644 build/lib/typings/event-stream.d.ts create mode 100644 build/lib/typings/gulp-bom.d.ts create mode 100644 build/lib/typings/gulp-concat.d.ts create mode 100644 build/lib/typings/gulp-cssnano.d.ts create mode 100644 build/lib/typings/gulp-filter.d.ts create mode 100644 build/lib/typings/gulp-flatmap.d.ts create mode 100644 build/lib/typings/gulp-remote-src.d.ts create mode 100644 build/lib/typings/gulp-rename.d.ts create mode 100644 build/lib/typings/gulp-sourcemaps.d.ts create mode 100644 build/lib/typings/gulp-tsb.d.ts create mode 100644 build/lib/typings/gulp-util.d.ts create mode 100644 build/lib/typings/gulp.d.ts create mode 100644 build/lib/typings/is.d.ts create mode 100644 build/lib/typings/lazy.js.d.ts create mode 100644 build/lib/typings/minimatch.d.ts create mode 100644 build/lib/typings/object-assign.d.ts create mode 100644 build/lib/typings/orchestrator.d.ts create mode 100644 build/lib/typings/pump.d.ts create mode 100644 build/lib/typings/rimraf.d.ts create mode 100644 build/lib/typings/source-map.d.ts create mode 100644 build/lib/typings/through.d.ts create mode 100644 build/lib/typings/through2.d.ts create mode 100644 build/lib/typings/underscore.d.ts create mode 100644 build/lib/typings/vinyl.d.ts create mode 100644 build/lib/util.js create mode 100644 build/lib/util.ts create mode 100644 build/lib/watch/index.js create mode 100644 build/lib/watch/package.json create mode 100644 build/lib/watch/watch-nsfw.js create mode 100644 build/lib/watch/watch-win32.js create mode 100644 build/lib/watch/watcher.exe create mode 100644 build/monaco/README-npm.md create mode 100644 build/monaco/README.md create mode 100644 build/monaco/ThirdPartyNotices.txt create mode 100644 build/monaco/api.js create mode 100644 build/monaco/api.ts create mode 100644 build/monaco/monaco.d.ts.recipe create mode 100644 build/monaco/package.json create mode 100644 build/npm/postinstall.js create mode 100644 build/npm/preinstall.js create mode 100644 build/npm/update-all-grammars.js create mode 100644 build/npm/update-grammar.js create mode 100644 build/npm/update-theme.js create mode 100644 build/package.json create mode 100644 build/tfs/common/.gitignore create mode 100644 build/tfs/common/common.sh create mode 100644 build/tfs/common/enqueue.ts create mode 100644 build/tfs/common/installDistro.ts create mode 100644 build/tfs/common/node.sh create mode 100644 build/tfs/common/publish.ts create mode 100644 build/tfs/darwin/build.sh create mode 100644 build/tfs/darwin/release.sh create mode 100644 build/tfs/darwin/smoketest.sh create mode 100644 build/tfs/linux/.gitignore create mode 100644 build/tfs/linux/build-ia32.sh create mode 100644 build/tfs/linux/build-x64.sh create mode 100644 build/tfs/linux/build.sh create mode 100644 build/tfs/linux/ia32/Dockerfile create mode 100644 build/tfs/linux/ia32/run-agent.sh create mode 100644 build/tfs/linux/ia32/xvfb.init create mode 100644 build/tfs/linux/release.sh create mode 100644 build/tfs/linux/repoapi_client.sh create mode 100644 build/tfs/linux/smoketest.sh create mode 100644 build/tfs/linux/x64/Dockerfile create mode 100644 build/tfs/linux/x64/run-agent.sh create mode 100644 build/tfs/linux/x64/xvfb.init create mode 100644 build/tfs/win32/1_build.ps1 create mode 100644 build/tfs/win32/2_package.ps1 create mode 100644 build/tfs/win32/3_upload.ps1 create mode 100644 build/tfs/win32/lib.ps1 create mode 100644 build/tfs/win32/node.ps1 create mode 100644 build/tfs/win32/smoketest.ps1 create mode 100644 build/tsconfig.json create mode 100644 build/tslint.json create mode 100644 build/win32/code.iss create mode 100644 build/win32/i18n/Default.isl create mode 100644 build/win32/i18n/Default.ko.isl create mode 100644 build/win32/i18n/Default.zh-cn.isl create mode 100644 build/win32/i18n/Default.zh-tw.isl create mode 100644 build/win32/i18n/messages.de.isl create mode 100644 build/win32/i18n/messages.en.isl create mode 100644 build/win32/i18n/messages.es.isl create mode 100644 build/win32/i18n/messages.fr.isl create mode 100644 build/win32/i18n/messages.hu.isl create mode 100644 build/win32/i18n/messages.it.isl create mode 100644 build/win32/i18n/messages.ja.isl create mode 100644 build/win32/i18n/messages.ko.isl create mode 100644 build/win32/i18n/messages.pt-br.isl create mode 100644 build/win32/i18n/messages.ru.isl create mode 100644 build/win32/i18n/messages.tr.isl create mode 100644 build/win32/i18n/messages.zh-cn.isl create mode 100644 build/win32/i18n/messages.zh-tw.isl create mode 100644 dataprotocol-node/.gitignore create mode 100644 dataprotocol-node/.travis.yml create mode 100644 dataprotocol-node/README.md create mode 100644 dataprotocol-node/client/.eslintrc create mode 100644 dataprotocol-node/client/.npmignore create mode 100644 dataprotocol-node/client/.vscode/launch.json create mode 100644 dataprotocol-node/client/.vscode/settings.json create mode 100644 dataprotocol-node/client/.vscode/tasks.json create mode 100644 dataprotocol-node/client/README.md create mode 100644 dataprotocol-node/client/package.json create mode 100644 dataprotocol-node/client/src/codeConverter.ts create mode 100644 dataprotocol-node/client/src/main.ts create mode 100644 dataprotocol-node/client/src/protocol.ts create mode 100644 dataprotocol-node/client/src/protocolCodeLens.ts create mode 100644 dataprotocol-node/client/src/protocolCompletionItem.ts create mode 100644 dataprotocol-node/client/src/protocolConverter.ts create mode 100644 dataprotocol-node/client/src/tsconfig.json create mode 100644 dataprotocol-node/client/src/typings/es6-promise/index.d.ts create mode 100644 dataprotocol-node/client/src/typings/es6-promise/tsconfig.json create mode 100644 dataprotocol-node/client/src/typings/ref.d.ts create mode 100644 dataprotocol-node/client/src/utils/async.ts create mode 100644 dataprotocol-node/client/src/utils/electron.ts create mode 100644 dataprotocol-node/client/src/utils/electronForkStart.ts create mode 100644 dataprotocol-node/client/src/utils/is.ts create mode 100644 dataprotocol-node/client/src/utils/processes.ts create mode 100644 dataprotocol-node/client/src/utils/terminateProcess.sh create mode 100644 dataprotocol-node/client/thirdpartynotices.txt create mode 100644 dataprotocol-node/jsonrpc/.eslintrc create mode 100644 dataprotocol-node/jsonrpc/.npmignore create mode 100644 dataprotocol-node/jsonrpc/.vscode/launch.json create mode 100644 dataprotocol-node/jsonrpc/.vscode/settings.json create mode 100644 dataprotocol-node/jsonrpc/.vscode/tasks.json create mode 100644 dataprotocol-node/jsonrpc/README.md create mode 100644 dataprotocol-node/jsonrpc/package.json create mode 100644 dataprotocol-node/jsonrpc/src/cancellation.ts create mode 100644 dataprotocol-node/jsonrpc/src/events.ts create mode 100644 dataprotocol-node/jsonrpc/src/is.ts create mode 100644 dataprotocol-node/jsonrpc/src/main.ts create mode 100644 dataprotocol-node/jsonrpc/src/messageReader.ts create mode 100644 dataprotocol-node/jsonrpc/src/messageWriter.ts create mode 100644 dataprotocol-node/jsonrpc/src/messages.ts create mode 100644 dataprotocol-node/jsonrpc/src/tsconfig.json create mode 100644 dataprotocol-node/jsonrpc/src/typings/promise.d.ts create mode 100644 dataprotocol-node/jsonrpc/thirdpartynotices.txt create mode 100644 dataprotocol-node/types/.eslintrc create mode 100644 dataprotocol-node/types/.gitignore create mode 100644 dataprotocol-node/types/.npmignore create mode 100644 dataprotocol-node/types/.vscode/settings.json create mode 100644 dataprotocol-node/types/.vscode/tasks.json create mode 100644 dataprotocol-node/types/README.md create mode 100644 dataprotocol-node/types/package.json create mode 100644 dataprotocol-node/types/src/main.ts create mode 100644 dataprotocol-node/types/src/tsconfig.json create mode 100644 dataprotocol-node/types/src/typings/promise.d.ts create mode 100644 extensions-modules/.gitignore create mode 100644 extensions-modules/.npmignore create mode 100644 extensions-modules/package.json create mode 100644 extensions-modules/src/configurations/config.ts create mode 100644 extensions-modules/src/configurations/extConfig.ts create mode 100644 extensions-modules/src/controllers/vscodeWrapper.ts create mode 100644 extensions-modules/src/languageservice/decompressProvider.ts create mode 100644 extensions-modules/src/languageservice/httpClient.ts create mode 100644 extensions-modules/src/languageservice/interfaces.ts create mode 100644 extensions-modules/src/languageservice/proxy.ts create mode 100644 extensions-modules/src/languageservice/server.ts create mode 100644 extensions-modules/src/languageservice/serverStatus.ts create mode 100644 extensions-modules/src/languageservice/serviceClient.ts create mode 100644 extensions-modules/src/languageservice/serviceDownloadProvider.ts create mode 100644 extensions-modules/src/languageservice/serviceInstallerUtil.ts create mode 100644 extensions-modules/src/languageservice/serviceStatus.ts create mode 100644 extensions-modules/src/main.ts create mode 100644 extensions-modules/src/models/constants.ts create mode 100644 extensions-modules/src/models/contracts/contracts.ts create mode 100644 extensions-modules/src/models/contracts/languageService.ts create mode 100644 extensions-modules/src/models/interfaces.ts create mode 100644 extensions-modules/src/models/logger.ts create mode 100644 extensions-modules/src/models/platform.ts create mode 100644 extensions-modules/src/models/telemetry.ts create mode 100644 extensions-modules/src/models/utils.ts create mode 100644 extensions-modules/src/tsconfig.json create mode 100644 extensions-modules/src/typings/ref.d.ts create mode 100644 extensions-modules/src/typings/tmp.d.ts create mode 100644 extensions-modules/src/typings/vscode-extension-telemetry.d.ts create mode 100644 extensions-modules/src/utils/escapeException.ts create mode 100644 extensions-modules/src/utils/validationException.ts create mode 100644 extensions-modules/src/views/statusView.ts create mode 100644 extensions/account-provider-azure/.gitignore create mode 100644 extensions/account-provider-azure/package.json create mode 100644 extensions/account-provider-azure/package.nls.json create mode 100644 extensions/account-provider-azure/src/account-provider/azureAccountProvider.ts create mode 100644 extensions/account-provider-azure/src/account-provider/azureAccountProviderService.ts create mode 100644 extensions/account-provider-azure/src/account-provider/interfaces.ts create mode 100644 extensions/account-provider-azure/src/account-provider/media/microsoft_account.svg create mode 100644 extensions/account-provider-azure/src/account-provider/media/work_school_account.svg create mode 100644 extensions/account-provider-azure/src/account-provider/media/work_school_account_inverse.svg create mode 100644 extensions/account-provider-azure/src/account-provider/providerSettings.ts create mode 100644 extensions/account-provider-azure/src/account-provider/tokenCache.ts create mode 100644 extensions/account-provider-azure/src/constants.ts create mode 100644 extensions/account-provider-azure/src/main.ts create mode 100644 extensions/account-provider-azure/src/typings/ref.d.ts create mode 100644 extensions/account-provider-azure/tsconfig.json create mode 100644 extensions/bat/.vscodeignore create mode 100644 extensions/bat/OSSREADME.json create mode 100644 extensions/bat/language-configuration.json create mode 100644 extensions/bat/package.json create mode 100644 extensions/bat/syntaxes/Batch File.tmLanguage create mode 100644 extensions/bat/test/colorize-fixtures/test.bat create mode 100644 extensions/bat/test/colorize-results/test_bat.json create mode 100644 extensions/configuration-editing/.vscodeignore create mode 100644 extensions/configuration-editing/npm-shrinkwrap.json create mode 100644 extensions/configuration-editing/package.json create mode 100644 extensions/configuration-editing/src/extension.ts create mode 100644 extensions/configuration-editing/src/settingsDocumentHelper.ts create mode 100644 extensions/configuration-editing/src/typings/ref.d.ts create mode 100644 extensions/configuration-editing/tsconfig.json create mode 100644 extensions/declares.d.ts create mode 100644 extensions/diff/.vscodeignore create mode 100644 extensions/diff/OSSREADME.json create mode 100644 extensions/diff/language-configuration.json create mode 100644 extensions/diff/package.json create mode 100644 extensions/diff/syntaxes/diff.tmLanguage create mode 100644 extensions/diff/syntaxes/diff.tmLanguage.json create mode 100644 extensions/diff/test/colorize-fixtures/test.diff create mode 100644 extensions/diff/test/colorize-results/test_diff.json create mode 100644 extensions/docker/.vscodeignore create mode 100644 extensions/docker/OSSREADME.json create mode 100644 extensions/docker/language-configuration.json create mode 100644 extensions/docker/package.json create mode 100644 extensions/docker/syntaxes/docker.tmLanguage.json create mode 100644 extensions/docker/test/colorize-fixtures/Dockerfile create mode 100644 extensions/docker/test/colorize-results/Dockerfile.json create mode 100644 extensions/extension-editing/.vscodeignore create mode 100644 extensions/extension-editing/npm-shrinkwrap.json create mode 100644 extensions/extension-editing/package.json create mode 100644 extensions/extension-editing/src/extension.ts create mode 100644 extensions/extension-editing/src/extensionLinter.ts create mode 100644 extensions/extension-editing/src/packageDocumentHelper.ts create mode 100644 extensions/extension-editing/src/typings/ref.d.ts create mode 100644 extensions/extension-editing/tsconfig.json create mode 100644 extensions/git/.vscodeignore create mode 100644 extensions/git/OSSREADME.json create mode 100644 extensions/git/npm-shrinkwrap.json create mode 100644 extensions/git/package.json create mode 100644 extensions/git/package.nls.json create mode 100644 extensions/git/resources/icons/dark/check.svg create mode 100644 extensions/git/resources/icons/dark/clean.svg create mode 100644 extensions/git/resources/icons/dark/git.svg create mode 100644 extensions/git/resources/icons/dark/open-change.svg create mode 100644 extensions/git/resources/icons/dark/open-file.svg create mode 100644 extensions/git/resources/icons/dark/refresh.svg create mode 100644 extensions/git/resources/icons/dark/stage.svg create mode 100644 extensions/git/resources/icons/dark/status-added.svg create mode 100644 extensions/git/resources/icons/dark/status-conflict.svg create mode 100644 extensions/git/resources/icons/dark/status-copied.svg create mode 100644 extensions/git/resources/icons/dark/status-deleted.svg create mode 100644 extensions/git/resources/icons/dark/status-ignored.svg create mode 100644 extensions/git/resources/icons/dark/status-modified.svg create mode 100644 extensions/git/resources/icons/dark/status-renamed.svg create mode 100644 extensions/git/resources/icons/dark/status-untracked.svg create mode 100644 extensions/git/resources/icons/dark/unstage.svg create mode 100644 extensions/git/resources/icons/light/check.svg create mode 100644 extensions/git/resources/icons/light/clean.svg create mode 100644 extensions/git/resources/icons/light/git.svg create mode 100644 extensions/git/resources/icons/light/open-change.svg create mode 100644 extensions/git/resources/icons/light/open-file.svg create mode 100644 extensions/git/resources/icons/light/refresh.svg create mode 100644 extensions/git/resources/icons/light/stage.svg create mode 100644 extensions/git/resources/icons/light/status-added.svg create mode 100644 extensions/git/resources/icons/light/status-conflict.svg create mode 100644 extensions/git/resources/icons/light/status-copied.svg create mode 100644 extensions/git/resources/icons/light/status-deleted.svg create mode 100644 extensions/git/resources/icons/light/status-ignored.svg create mode 100644 extensions/git/resources/icons/light/status-modified.svg create mode 100644 extensions/git/resources/icons/light/status-renamed.svg create mode 100644 extensions/git/resources/icons/light/status-untracked.svg create mode 100644 extensions/git/resources/icons/light/unstage.svg create mode 100644 extensions/git/src/askpass-empty.sh create mode 100644 extensions/git/src/askpass-main.ts create mode 100644 extensions/git/src/askpass.sh create mode 100644 extensions/git/src/askpass.ts create mode 100644 extensions/git/src/autofetch.ts create mode 100644 extensions/git/src/commands.ts create mode 100644 extensions/git/src/contentProvider.ts create mode 100644 extensions/git/src/decorators.ts create mode 100644 extensions/git/src/git.ts create mode 100644 extensions/git/src/iterators.ts create mode 100644 extensions/git/src/main.ts create mode 100644 extensions/git/src/model.ts create mode 100644 extensions/git/src/repository.ts create mode 100644 extensions/git/src/staging.ts create mode 100644 extensions/git/src/statusbar.ts create mode 100644 extensions/git/src/test/git.test.ts create mode 100644 extensions/git/src/typings/refs.d.ts create mode 100644 extensions/git/src/uri.ts create mode 100644 extensions/git/src/util.ts create mode 100644 extensions/git/test/mocha.opts create mode 100644 extensions/git/tsconfig.json create mode 100644 extensions/gitsyntax/.vscodeignore create mode 100644 extensions/gitsyntax/OSSREADME.json create mode 100644 extensions/gitsyntax/git-commit.language-configuration.json create mode 100644 extensions/gitsyntax/git-rebase.language-configuration.json create mode 100644 extensions/gitsyntax/package.json create mode 100644 extensions/gitsyntax/syntaxes/git-commit.tmLanguage.json create mode 100644 extensions/gitsyntax/syntaxes/git-rebase.tmLanguage.json create mode 100644 extensions/gitsyntax/test/colorize-fixtures/COMMIT_EDITMSG create mode 100644 extensions/gitsyntax/test/colorize-fixtures/git-rebase-todo create mode 100644 extensions/gitsyntax/test/colorize-results/COMMIT_EDITMSG.json create mode 100644 extensions/gitsyntax/test/colorize-results/git-rebase-todo.json create mode 100644 extensions/insights-default/package.json create mode 100644 extensions/insights-default/sql/backup_detail.sql create mode 100644 extensions/insights-default/sql/backup_insight.sql create mode 100644 extensions/insights-default/sql/db_size.sql create mode 100644 extensions/insights-default/sql/qds.sql create mode 100644 extensions/insights-default/sql/qds_detail.sql create mode 100644 extensions/insights-default/sql/tablespace.sql create mode 100644 extensions/json/.vscode/launch.json create mode 100644 extensions/json/.vscode/tasks.json create mode 100644 extensions/json/.vscodeignore create mode 100644 extensions/json/OSSREADME.json create mode 100644 extensions/json/client/src/jsonMain.ts create mode 100644 extensions/json/client/src/typings/ref.d.ts create mode 100644 extensions/json/client/tsconfig.json create mode 100644 extensions/json/language-configuration.json create mode 100644 extensions/json/npm-shrinkwrap.json create mode 100644 extensions/json/package.json create mode 100644 extensions/json/package.nls.json create mode 100644 extensions/json/server/.vscode/launch.json create mode 100644 extensions/json/server/.vscode/tasks.json create mode 100644 extensions/json/server/npm-shrinkwrap.json create mode 100644 extensions/json/server/package.json create mode 100644 extensions/json/server/src/jsonServerMain.ts create mode 100644 extensions/json/server/src/languageModelCache.ts create mode 100644 extensions/json/server/src/utils/strings.ts create mode 100644 extensions/json/server/src/utils/uri.ts create mode 100644 extensions/json/server/tsconfig.json create mode 100644 extensions/json/syntaxes/JSON.tmLanguage.json create mode 100644 extensions/json/test/colorize-fixtures/test.json create mode 100644 extensions/json/test/colorize-results/test_json.json create mode 100644 extensions/lib.core.d.ts create mode 100644 extensions/markdown/.vscodeignore create mode 100644 extensions/markdown/OSSREADME.json create mode 100644 extensions/markdown/language-configuration.json create mode 100644 extensions/markdown/media/Preview.svg create mode 100644 extensions/markdown/media/PreviewOnRightPane_16x.svg create mode 100644 extensions/markdown/media/PreviewOnRightPane_16x_dark.svg create mode 100644 extensions/markdown/media/Preview_inverse.svg create mode 100644 extensions/markdown/media/ViewSource.svg create mode 100644 extensions/markdown/media/ViewSource_inverse.svg create mode 100644 extensions/markdown/media/csp.js create mode 100644 extensions/markdown/media/loading.js create mode 100644 extensions/markdown/media/main.js create mode 100644 extensions/markdown/media/markdown.css create mode 100644 extensions/markdown/media/tomorrow.css create mode 100644 extensions/markdown/npm-shrinkwrap.json create mode 100644 extensions/markdown/package.json create mode 100644 extensions/markdown/package.nls.json create mode 100644 extensions/markdown/snippets/markdown.json create mode 100644 extensions/markdown/src/documentLinkProvider.ts create mode 100644 extensions/markdown/src/documentSymbolProvider.ts create mode 100644 extensions/markdown/src/extension.ts create mode 100644 extensions/markdown/src/logger.ts create mode 100644 extensions/markdown/src/markdownEngine.ts create mode 100644 extensions/markdown/src/previewContentProvider.ts create mode 100644 extensions/markdown/src/security.ts create mode 100644 extensions/markdown/src/tableOfContentsProvider.ts create mode 100644 extensions/markdown/src/typings/markdown-it-named-headers.d.ts create mode 100644 extensions/markdown/src/typings/ref.d.ts create mode 100644 extensions/markdown/syntaxes/gulpfile.js create mode 100644 extensions/markdown/syntaxes/markdown.tmLanguage create mode 100644 extensions/markdown/syntaxes/markdown.tmLanguage.base create mode 100644 extensions/markdown/test/colorize-fixtures/test.md create mode 100644 extensions/markdown/test/colorize-results/test_md.json create mode 100644 extensions/markdown/tsconfig.json create mode 100644 extensions/merge-conflict/.vscodeignore create mode 100644 extensions/merge-conflict/npm-shrinkwrap.json create mode 100644 extensions/merge-conflict/package.json create mode 100644 extensions/merge-conflict/package.nls.json create mode 100644 extensions/merge-conflict/src/codelensProvider.ts create mode 100644 extensions/merge-conflict/src/commandHandler.ts create mode 100644 extensions/merge-conflict/src/contentProvider.ts create mode 100644 extensions/merge-conflict/src/delayer.ts create mode 100644 extensions/merge-conflict/src/documentMergeConflict.ts create mode 100644 extensions/merge-conflict/src/documentTracker.ts create mode 100644 extensions/merge-conflict/src/extension.ts create mode 100644 extensions/merge-conflict/src/interfaces.ts create mode 100644 extensions/merge-conflict/src/mergeConflictParser.ts create mode 100644 extensions/merge-conflict/src/mergeDecorator.ts create mode 100644 extensions/merge-conflict/src/services.ts create mode 100644 extensions/merge-conflict/src/typings/refs.d.ts create mode 100644 extensions/merge-conflict/tsconfig.json create mode 100644 extensions/ms-vscode.node-debug/OSSREADME.json create mode 100644 extensions/ms-vscode.node-debug/package.json create mode 100644 extensions/ms-vscode.node-debug2/OSSREADME.json create mode 100644 extensions/ms-vscode.node-debug2/package.json create mode 100644 extensions/mssql/.gitignore create mode 100644 extensions/mssql/client/src/config.json create mode 100644 extensions/mssql/client/src/controllers/mainController.ts create mode 100644 extensions/mssql/client/src/credentialstore/credentialstore.ts create mode 100644 extensions/mssql/client/src/credentialstore/icredentialstore.ts create mode 100644 extensions/mssql/client/src/models/constants.ts create mode 100644 extensions/mssql/client/src/models/contracts.ts create mode 100644 extensions/mssql/client/src/mssqlMain.ts create mode 100644 extensions/mssql/client/src/resourceprovider/resourceprovider.ts create mode 100644 extensions/mssql/client/src/serialize/iserialization.ts create mode 100644 extensions/mssql/client/src/serialize/serialization.ts create mode 100644 extensions/mssql/client/src/typings/ref.d.ts create mode 100644 extensions/mssql/client/tsconfig.json create mode 100644 extensions/mssql/npm-shrinkwrap.json create mode 100644 extensions/mssql/package.json create mode 100644 extensions/mssql/package.nls.json create mode 100644 extensions/mssql/snippets/mssql.json create mode 100644 extensions/mssql/syntaxes/SQL.plist create mode 100644 extensions/mssql/syntaxes/sql.configuration.json create mode 100644 extensions/node.d.ts create mode 100644 extensions/npm-shrinkwrap.json create mode 100644 extensions/package.json create mode 100644 extensions/postinstall.js create mode 100644 extensions/powershell/.vscodeignore create mode 100644 extensions/powershell/OSSREADME.json create mode 100644 extensions/powershell/language-configuration.json create mode 100644 extensions/powershell/package.json create mode 100644 extensions/powershell/syntaxes/PowershellSyntax.tmLanguage create mode 100644 extensions/powershell/test/colorize-fixtures/test.ps1 create mode 100644 extensions/powershell/test/colorize-results/test_ps1.json create mode 100644 extensions/python/.vscode/launch.json create mode 100644 extensions/python/.vscode/tasks.json create mode 100644 extensions/python/.vscodeignore create mode 100644 extensions/python/OSSREADME.json create mode 100644 extensions/python/language-configuration.json create mode 100644 extensions/python/package.json create mode 100644 extensions/python/src/pythonMain.ts create mode 100644 extensions/python/src/typings/ref.d.ts create mode 100644 extensions/python/syntaxes/MagicPython.tmLanguage.json create mode 100644 extensions/python/syntaxes/MagicRegExp.tmLanguage.json create mode 100644 extensions/python/test/colorize-fixtures/test.py create mode 100644 extensions/python/test/colorize-results/test_py.json create mode 100644 extensions/python/tsconfig.json create mode 100644 extensions/r/.vscodeignore create mode 100644 extensions/r/OSSREADME.json create mode 100644 extensions/r/language-configuration.json create mode 100644 extensions/r/package.json create mode 100644 extensions/r/syntaxes/R.plist create mode 100644 extensions/r/syntaxes/r.tmLanguage.json create mode 100644 extensions/r/test/colorize-fixtures/test.r create mode 100644 extensions/r/test/colorize-results/test_r.json create mode 100644 extensions/ruby/.vscodeignore create mode 100644 extensions/ruby/OSSREADME.json create mode 100644 extensions/ruby/language-configuration.json create mode 100644 extensions/ruby/package.json create mode 100644 extensions/ruby/syntaxes/ruby.tmLanguage.json create mode 100644 extensions/ruby/test/colorize-fixtures/test.rb create mode 100644 extensions/ruby/test/colorize-results/test_rb.json create mode 100644 extensions/shellscript/.vscodeignore create mode 100644 extensions/shellscript/OSSREADME.json create mode 100644 extensions/shellscript/language-configuration.json create mode 100644 extensions/shellscript/package.json create mode 100644 extensions/shellscript/syntaxes/Shell-Unix-Bash.tmLanguage.json create mode 100644 extensions/shellscript/test/colorize-fixtures/test.sh create mode 100644 extensions/shellscript/test/colorize-results/test_sh.json create mode 100644 extensions/sql/.vscodeignore create mode 100644 extensions/sql/OSSREADME.json create mode 100644 extensions/sql/language-configuration.json create mode 100644 extensions/sql/package.json create mode 100644 extensions/sql/syntaxes/SQL.plist create mode 100644 extensions/sql/test/colorize-fixtures/test.sql create mode 100644 extensions/sql/test/colorize-results/test_sql.json create mode 100644 extensions/theme-abyss/OSSREADME.json create mode 100644 extensions/theme-abyss/package.json create mode 100644 extensions/theme-abyss/themes/Abyss.tmTheme create mode 100644 extensions/theme-abyss/themes/abyss-color-theme.json create mode 100644 extensions/theme-carbon/package.json create mode 100644 extensions/theme-carbon/themes/dark_carbon.json create mode 100644 extensions/theme-carbon/themes/light_carbon.json create mode 100644 extensions/theme-defaults/package.json create mode 100644 extensions/theme-defaults/themes/hc_black.json create mode 100644 extensions/theme-kimbie-dark/OSSREADME.json create mode 100644 extensions/theme-kimbie-dark/package.json create mode 100644 extensions/theme-kimbie-dark/themes/Kimbie_dark.tmTheme create mode 100644 extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json create mode 100644 extensions/theme-monokai-dimmed/OSSREADME.json create mode 100644 extensions/theme-monokai-dimmed/package.json create mode 100644 extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json create mode 100644 extensions/theme-monokai-dimmed/themes/dimmed-monokai.tmTheme create mode 100644 extensions/theme-monokai/OSSREADME.json create mode 100644 extensions/theme-monokai/package.json create mode 100644 extensions/theme-monokai/themes/Monokai.tmTheme create mode 100644 extensions/theme-monokai/themes/monokai-color-theme.json create mode 100644 extensions/theme-quietlight/OSSREADME.json create mode 100644 extensions/theme-quietlight/package.json create mode 100644 extensions/theme-quietlight/themes/QuietLight.tmTheme create mode 100644 extensions/theme-quietlight/themes/quietlight-color-theme.json create mode 100644 extensions/theme-red/OSSREADME.json create mode 100644 extensions/theme-red/package.json create mode 100644 extensions/theme-red/themes/Red-color-theme.json create mode 100644 extensions/theme-red/themes/red.tmTheme create mode 100644 extensions/theme-seti/.vscodeignore create mode 100644 extensions/theme-seti/OSSREADME.json create mode 100644 extensions/theme-seti/build/update-icon-theme.js create mode 100644 extensions/theme-seti/icons/dashboard.svg create mode 100644 extensions/theme-seti/icons/dashboard_inverse.svg create mode 100644 extensions/theme-seti/icons/seti.woff create mode 100644 extensions/theme-seti/icons/vs-seti-icon-theme.json create mode 100644 extensions/theme-seti/package.json create mode 100644 extensions/theme-seti/thirdpartynotices.txt create mode 100644 extensions/theme-solarized-dark/OSSREADME.json create mode 100644 extensions/theme-solarized-dark/package.json create mode 100644 extensions/theme-solarized-dark/themes/Solarized-dark.tmTheme create mode 100644 extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json create mode 100644 extensions/theme-solarized-light/OSSREADME.json create mode 100644 extensions/theme-solarized-light/package.json create mode 100644 extensions/theme-solarized-light/themes/Solarized-light.tmTheme create mode 100644 extensions/theme-solarized-light/themes/solarized-light-color-theme.json create mode 100644 extensions/theme-tomorrow-night-blue/OSSREADME.json create mode 100644 extensions/theme-tomorrow-night-blue/package.json create mode 100644 extensions/theme-tomorrow-night-blue/themes/Tomorrow-Night-Blue.tmTheme create mode 100644 extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json create mode 100644 extensions/vscode-colorize-tests/.gitignore create mode 100644 extensions/vscode-colorize-tests/.vscode/launch.json create mode 100644 extensions/vscode-colorize-tests/.vscode/tasks.json create mode 100644 extensions/vscode-colorize-tests/package.json create mode 100644 extensions/vscode-colorize-tests/src/colorizer.test.ts create mode 100644 extensions/vscode-colorize-tests/src/index.ts create mode 100644 extensions/vscode-colorize-tests/src/typings/ref.d.ts create mode 100644 extensions/vscode-colorize-tests/tsconfig.json create mode 100644 extensions/xml/.vscodeignore create mode 100644 extensions/xml/OSSREADME.json create mode 100644 extensions/xml/package.json create mode 100644 extensions/xml/syntaxes/xml.json create mode 100644 extensions/xml/syntaxes/xsl.json create mode 100644 extensions/xml/test/colorize-fixtures/test-7115.xml create mode 100644 extensions/xml/test/colorize-fixtures/test.xml create mode 100644 extensions/xml/test/colorize-results/test-7115_xml.json create mode 100644 extensions/xml/test/colorize-results/test_xml.json create mode 100644 extensions/xml/xml.language-configuration.json create mode 100644 extensions/xml/xsl.language-configuration.json create mode 100644 extensions/yaml/.gitignore create mode 100644 extensions/yaml/.vscodeignore create mode 100644 extensions/yaml/OSSREADME.json create mode 100644 extensions/yaml/language-configuration.json create mode 100644 extensions/yaml/package.json create mode 100644 extensions/yaml/syntaxes/yaml.json create mode 100644 extensions/yaml/test/colorize-fixtures/issue-1550.yaml create mode 100644 extensions/yaml/test/colorize-fixtures/issue-4008.yaml create mode 100644 extensions/yaml/test/colorize-fixtures/issue-6303.yaml create mode 100644 extensions/yaml/test/colorize-fixtures/test.yaml create mode 100644 extensions/yaml/test/colorize-results/issue-1550_yaml.json create mode 100644 extensions/yaml/test/colorize-results/issue-4008_yaml.json create mode 100644 extensions/yaml/test/colorize-results/issue-6303_yaml.json create mode 100644 extensions/yaml/test/colorize-results/test_yaml.json create mode 100644 gulpfile.js create mode 100644 i18n/.gitignore create mode 100644 i18n/chs/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/chs/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/chs/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/chs/extensions/css/package.i18n.json create mode 100644 i18n/chs/extensions/emmet/package.i18n.json create mode 100644 i18n/chs/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/chs/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/chs/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/chs/extensions/git/out/commands.i18n.json create mode 100644 i18n/chs/extensions/git/out/main.i18n.json create mode 100644 i18n/chs/extensions/git/out/model.i18n.json create mode 100644 i18n/chs/extensions/git/out/repository.i18n.json create mode 100644 i18n/chs/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/chs/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/chs/extensions/git/package.i18n.json create mode 100644 i18n/chs/extensions/grunt/out/main.i18n.json create mode 100644 i18n/chs/extensions/grunt/package.i18n.json create mode 100644 i18n/chs/extensions/gulp/out/main.i18n.json create mode 100644 i18n/chs/extensions/gulp/package.i18n.json create mode 100644 i18n/chs/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/chs/extensions/html/package.i18n.json create mode 100644 i18n/chs/extensions/jake/out/main.i18n.json create mode 100644 i18n/chs/extensions/jake/package.i18n.json create mode 100644 i18n/chs/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/chs/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/chs/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/chs/extensions/json/package.i18n.json create mode 100644 i18n/chs/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/chs/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/chs/extensions/markdown/out/security.i18n.json create mode 100644 i18n/chs/extensions/markdown/package.i18n.json create mode 100644 i18n/chs/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/chs/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/chs/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/chs/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/chs/extensions/npm/package.i18n.json create mode 100644 i18n/chs/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/chs/extensions/php/package.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/chs/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/chs/extensions/typescript/package.i18n.json create mode 100644 i18n/chs/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/chs/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/chs/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/chs/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/chs/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/chs/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/chs/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/chs/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/chs/src/vs/base/common/json.i18n.json create mode 100644 i18n/chs/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/chs/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/chs/src/vs/base/common/processes.i18n.json create mode 100644 i18n/chs/src/vs/base/common/severity.i18n.json create mode 100644 i18n/chs/src/vs/base/node/processes.i18n.json create mode 100644 i18n/chs/src/vs/base/node/zip.i18n.json create mode 100644 i18n/chs/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/chs/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/chs/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/chs/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/chs/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/chs/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/chs/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/chs/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/chs/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/chs/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/chs/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/chs/src/vs/editor/common/config/defaultConfig.i18n.json create mode 100644 i18n/chs/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/chs/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/chs/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/chs/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/chs/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/chs/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/chs/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/chs/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/chs/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/chs/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/chs/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/chs/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/chs/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/chs/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/chs/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/chs/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/chs/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/chs/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/chs/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/chs/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/chs/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/chs/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/chs/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/chs/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/chs/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/chs/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/chs/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/chs/src/vs/platform/keybinding/common/keybindingLabels.i18n.json create mode 100644 i18n/chs/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/chs/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/chs/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/chs/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/chs/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/chs/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/chs/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/node/extHostExplorerView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/node/extHostTreeView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/api/node/mainThreadMessageService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/chs/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/chs/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/chs/src/vs/workbench/electron-browser/crashReporter.i18n.json create mode 100644 i18n/chs/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/chs/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/chs/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/chs/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/chs/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/chs/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/chs/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/gitActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/gitServices.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/git/node/git.lib.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/chs/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/cht/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/cht/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/cht/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/cht/extensions/css/package.i18n.json create mode 100644 i18n/cht/extensions/emmet/package.i18n.json create mode 100644 i18n/cht/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/cht/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/cht/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/cht/extensions/git/out/commands.i18n.json create mode 100644 i18n/cht/extensions/git/out/main.i18n.json create mode 100644 i18n/cht/extensions/git/out/model.i18n.json create mode 100644 i18n/cht/extensions/git/out/repository.i18n.json create mode 100644 i18n/cht/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/cht/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/cht/extensions/git/package.i18n.json create mode 100644 i18n/cht/extensions/grunt/out/main.i18n.json create mode 100644 i18n/cht/extensions/grunt/package.i18n.json create mode 100644 i18n/cht/extensions/gulp/out/main.i18n.json create mode 100644 i18n/cht/extensions/gulp/package.i18n.json create mode 100644 i18n/cht/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/cht/extensions/html/package.i18n.json create mode 100644 i18n/cht/extensions/jake/out/main.i18n.json create mode 100644 i18n/cht/extensions/jake/package.i18n.json create mode 100644 i18n/cht/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/cht/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/cht/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/cht/extensions/json/package.i18n.json create mode 100644 i18n/cht/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/cht/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/cht/extensions/markdown/out/security.i18n.json create mode 100644 i18n/cht/extensions/markdown/package.i18n.json create mode 100644 i18n/cht/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/cht/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/cht/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/cht/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/cht/extensions/npm/package.i18n.json create mode 100644 i18n/cht/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/cht/extensions/php/package.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/cht/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/cht/extensions/typescript/package.i18n.json create mode 100644 i18n/cht/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/cht/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/cht/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/cht/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/cht/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/cht/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/cht/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/cht/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/cht/src/vs/base/common/json.i18n.json create mode 100644 i18n/cht/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/cht/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/cht/src/vs/base/common/processes.i18n.json create mode 100644 i18n/cht/src/vs/base/common/severity.i18n.json create mode 100644 i18n/cht/src/vs/base/node/processes.i18n.json create mode 100644 i18n/cht/src/vs/base/node/zip.i18n.json create mode 100644 i18n/cht/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/cht/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/cht/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/cht/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/cht/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/cht/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/cht/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/cht/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/cht/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/cht/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/cht/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/cht/src/vs/editor/common/config/defaultConfig.i18n.json create mode 100644 i18n/cht/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/cht/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/cht/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/cht/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/cht/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/cht/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/cht/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/cht/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/cht/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/cht/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/cht/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/cht/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/cht/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/cht/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/cht/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/cht/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/cht/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/cht/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/cht/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/cht/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/cht/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/cht/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/cht/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/cht/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/cht/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/cht/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/cht/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/cht/src/vs/platform/keybinding/common/keybindingLabels.i18n.json create mode 100644 i18n/cht/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/cht/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/cht/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/cht/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/cht/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/cht/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/cht/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/node/extHostExplorerView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/node/extHostTreeView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/api/node/mainThreadMessageService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/cht/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/cht/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/cht/src/vs/workbench/electron-browser/crashReporter.i18n.json create mode 100644 i18n/cht/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/cht/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/cht/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/cht/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/cht/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/cht/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/cht/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/gitActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/gitServices.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/git/node/git.lib.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/cht/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/deu/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/deu/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/deu/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/deu/extensions/css/package.i18n.json create mode 100644 i18n/deu/extensions/emmet/package.i18n.json create mode 100644 i18n/deu/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/deu/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/deu/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/deu/extensions/git/out/commands.i18n.json create mode 100644 i18n/deu/extensions/git/out/main.i18n.json create mode 100644 i18n/deu/extensions/git/out/model.i18n.json create mode 100644 i18n/deu/extensions/git/out/repository.i18n.json create mode 100644 i18n/deu/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/deu/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/deu/extensions/git/package.i18n.json create mode 100644 i18n/deu/extensions/grunt/out/main.i18n.json create mode 100644 i18n/deu/extensions/grunt/package.i18n.json create mode 100644 i18n/deu/extensions/gulp/out/main.i18n.json create mode 100644 i18n/deu/extensions/gulp/package.i18n.json create mode 100644 i18n/deu/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/deu/extensions/html/package.i18n.json create mode 100644 i18n/deu/extensions/jake/out/main.i18n.json create mode 100644 i18n/deu/extensions/jake/package.i18n.json create mode 100644 i18n/deu/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/deu/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/deu/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/deu/extensions/json/package.i18n.json create mode 100644 i18n/deu/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/deu/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/deu/extensions/markdown/out/security.i18n.json create mode 100644 i18n/deu/extensions/markdown/package.i18n.json create mode 100644 i18n/deu/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/deu/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/deu/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/deu/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/deu/extensions/npm/package.i18n.json create mode 100644 i18n/deu/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/deu/extensions/php/package.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/deu/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/deu/extensions/typescript/package.i18n.json create mode 100644 i18n/deu/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/deu/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/deu/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/deu/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/deu/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/deu/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/deu/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/deu/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/deu/src/vs/base/common/json.i18n.json create mode 100644 i18n/deu/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/deu/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/deu/src/vs/base/common/processes.i18n.json create mode 100644 i18n/deu/src/vs/base/common/severity.i18n.json create mode 100644 i18n/deu/src/vs/base/node/processes.i18n.json create mode 100644 i18n/deu/src/vs/base/node/zip.i18n.json create mode 100644 i18n/deu/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/deu/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/deu/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/deu/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/deu/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/deu/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/deu/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/deu/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/deu/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/deu/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/deu/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/deu/src/vs/editor/common/config/defaultConfig.i18n.json create mode 100644 i18n/deu/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/deu/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/deu/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/deu/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/deu/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/deu/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/deu/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/deu/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/deu/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/deu/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/deu/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/deu/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/deu/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/deu/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/deu/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/deu/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/deu/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/deu/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/deu/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/deu/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/deu/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/deu/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/deu/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/deu/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/deu/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/deu/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/deu/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/deu/src/vs/platform/keybinding/common/keybindingLabels.i18n.json create mode 100644 i18n/deu/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/deu/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/deu/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/deu/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/deu/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/deu/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/deu/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/node/extHostExplorerView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/node/extHostTreeView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/api/node/mainThreadMessageService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/deu/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/deu/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/deu/src/vs/workbench/electron-browser/crashReporter.i18n.json create mode 100644 i18n/deu/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/deu/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/deu/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/deu/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/deu/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/deu/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/deu/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/gitActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/gitServices.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/git/node/git.lib.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/deu/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/esn/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/esn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/esn/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/esn/extensions/css/package.i18n.json create mode 100644 i18n/esn/extensions/emmet/package.i18n.json create mode 100644 i18n/esn/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/esn/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/esn/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/esn/extensions/git/out/commands.i18n.json create mode 100644 i18n/esn/extensions/git/out/main.i18n.json create mode 100644 i18n/esn/extensions/git/out/model.i18n.json create mode 100644 i18n/esn/extensions/git/out/repository.i18n.json create mode 100644 i18n/esn/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/esn/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/esn/extensions/git/package.i18n.json create mode 100644 i18n/esn/extensions/grunt/out/main.i18n.json create mode 100644 i18n/esn/extensions/grunt/package.i18n.json create mode 100644 i18n/esn/extensions/gulp/out/main.i18n.json create mode 100644 i18n/esn/extensions/gulp/package.i18n.json create mode 100644 i18n/esn/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/esn/extensions/html/package.i18n.json create mode 100644 i18n/esn/extensions/jake/out/main.i18n.json create mode 100644 i18n/esn/extensions/jake/package.i18n.json create mode 100644 i18n/esn/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/esn/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/esn/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/esn/extensions/json/package.i18n.json create mode 100644 i18n/esn/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/esn/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/esn/extensions/markdown/out/security.i18n.json create mode 100644 i18n/esn/extensions/markdown/package.i18n.json create mode 100644 i18n/esn/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/esn/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/esn/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/esn/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/esn/extensions/npm/package.i18n.json create mode 100644 i18n/esn/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/esn/extensions/php/package.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/esn/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/esn/extensions/typescript/package.i18n.json create mode 100644 i18n/esn/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/esn/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/esn/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/esn/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/esn/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/esn/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/esn/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/esn/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/esn/src/vs/base/common/json.i18n.json create mode 100644 i18n/esn/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/esn/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/esn/src/vs/base/common/processes.i18n.json create mode 100644 i18n/esn/src/vs/base/common/severity.i18n.json create mode 100644 i18n/esn/src/vs/base/node/processes.i18n.json create mode 100644 i18n/esn/src/vs/base/node/zip.i18n.json create mode 100644 i18n/esn/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/esn/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/esn/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/esn/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/esn/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/esn/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/esn/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/esn/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/esn/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/esn/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/esn/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/esn/src/vs/editor/common/config/defaultConfig.i18n.json create mode 100644 i18n/esn/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/esn/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/esn/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/esn/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/esn/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/esn/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/esn/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/esn/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/esn/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/esn/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/esn/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/esn/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/esn/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/esn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/esn/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/esn/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/esn/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/esn/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/esn/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/esn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/esn/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/esn/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/esn/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/esn/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/esn/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/esn/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/esn/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/esn/src/vs/platform/keybinding/common/keybindingLabels.i18n.json create mode 100644 i18n/esn/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/esn/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/esn/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/esn/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/esn/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/esn/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/esn/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/node/extHostExplorerView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/node/extHostTreeView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/api/node/mainThreadMessageService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/esn/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/esn/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/esn/src/vs/workbench/electron-browser/crashReporter.i18n.json create mode 100644 i18n/esn/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/esn/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/esn/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/esn/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/esn/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/esn/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/esn/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/gitActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/gitServices.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/git/node/git.lib.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/esn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/fra/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/fra/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/fra/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/fra/extensions/css/package.i18n.json create mode 100644 i18n/fra/extensions/emmet/package.i18n.json create mode 100644 i18n/fra/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/fra/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/fra/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/fra/extensions/git/out/commands.i18n.json create mode 100644 i18n/fra/extensions/git/out/main.i18n.json create mode 100644 i18n/fra/extensions/git/out/model.i18n.json create mode 100644 i18n/fra/extensions/git/out/repository.i18n.json create mode 100644 i18n/fra/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/fra/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/fra/extensions/git/package.i18n.json create mode 100644 i18n/fra/extensions/grunt/out/main.i18n.json create mode 100644 i18n/fra/extensions/grunt/package.i18n.json create mode 100644 i18n/fra/extensions/gulp/out/main.i18n.json create mode 100644 i18n/fra/extensions/gulp/package.i18n.json create mode 100644 i18n/fra/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/fra/extensions/html/package.i18n.json create mode 100644 i18n/fra/extensions/jake/out/main.i18n.json create mode 100644 i18n/fra/extensions/jake/package.i18n.json create mode 100644 i18n/fra/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/fra/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/fra/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/fra/extensions/json/package.i18n.json create mode 100644 i18n/fra/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/fra/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/fra/extensions/markdown/out/security.i18n.json create mode 100644 i18n/fra/extensions/markdown/package.i18n.json create mode 100644 i18n/fra/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/fra/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/fra/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/fra/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/fra/extensions/npm/package.i18n.json create mode 100644 i18n/fra/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/fra/extensions/php/package.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/fra/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/fra/extensions/typescript/package.i18n.json create mode 100644 i18n/fra/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/fra/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/fra/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/fra/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/fra/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/fra/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/fra/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/fra/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/fra/src/vs/base/common/json.i18n.json create mode 100644 i18n/fra/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/fra/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/fra/src/vs/base/common/processes.i18n.json create mode 100644 i18n/fra/src/vs/base/common/severity.i18n.json create mode 100644 i18n/fra/src/vs/base/node/processes.i18n.json create mode 100644 i18n/fra/src/vs/base/node/zip.i18n.json create mode 100644 i18n/fra/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/fra/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/fra/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/fra/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/fra/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/fra/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/fra/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/fra/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/fra/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/fra/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/fra/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/fra/src/vs/editor/common/config/defaultConfig.i18n.json create mode 100644 i18n/fra/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/fra/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/fra/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/fra/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/fra/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/fra/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/fra/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/fra/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/fra/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/fra/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/fra/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/fra/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/fra/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/fra/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/fra/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/fra/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/fra/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/fra/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/fra/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/fra/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/fra/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/fra/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/fra/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/fra/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/fra/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/fra/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/fra/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/fra/src/vs/platform/keybinding/common/keybindingLabels.i18n.json create mode 100644 i18n/fra/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/fra/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/fra/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/fra/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/fra/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/fra/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/fra/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/node/extHostExplorerView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/node/extHostTreeView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/api/node/mainThreadMessageService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/fra/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/fra/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/fra/src/vs/workbench/electron-browser/crashReporter.i18n.json create mode 100644 i18n/fra/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/fra/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/fra/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/fra/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/fra/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/fra/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/fra/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/gitActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/gitServices.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/git/node/git.lib.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/fra/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/hun/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/hun/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/hun/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/hun/extensions/css/package.i18n.json create mode 100644 i18n/hun/extensions/emmet/package.i18n.json create mode 100644 i18n/hun/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/hun/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/hun/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/hun/extensions/git/out/commands.i18n.json create mode 100644 i18n/hun/extensions/git/out/main.i18n.json create mode 100644 i18n/hun/extensions/git/out/model.i18n.json create mode 100644 i18n/hun/extensions/git/out/repository.i18n.json create mode 100644 i18n/hun/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/hun/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/hun/extensions/git/package.i18n.json create mode 100644 i18n/hun/extensions/grunt/out/main.i18n.json create mode 100644 i18n/hun/extensions/grunt/package.i18n.json create mode 100644 i18n/hun/extensions/gulp/out/main.i18n.json create mode 100644 i18n/hun/extensions/gulp/package.i18n.json create mode 100644 i18n/hun/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/hun/extensions/html/package.i18n.json create mode 100644 i18n/hun/extensions/jake/out/main.i18n.json create mode 100644 i18n/hun/extensions/jake/package.i18n.json create mode 100644 i18n/hun/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/hun/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/hun/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/hun/extensions/json/package.i18n.json create mode 100644 i18n/hun/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/hun/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/hun/extensions/markdown/out/security.i18n.json create mode 100644 i18n/hun/extensions/markdown/package.i18n.json create mode 100644 i18n/hun/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/hun/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/hun/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/hun/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/hun/extensions/npm/package.i18n.json create mode 100644 i18n/hun/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/hun/extensions/php/package.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/hun/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/hun/extensions/typescript/package.i18n.json create mode 100644 i18n/hun/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/hun/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/hun/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/hun/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/hun/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/hun/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/hun/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/hun/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/hun/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/hun/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/hun/src/vs/base/common/processes.i18n.json create mode 100644 i18n/hun/src/vs/base/common/severity.i18n.json create mode 100644 i18n/hun/src/vs/base/node/processes.i18n.json create mode 100644 i18n/hun/src/vs/base/node/zip.i18n.json create mode 100644 i18n/hun/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/hun/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/hun/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/hun/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/hun/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/hun/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/hun/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/hun/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/hun/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/hun/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/hun/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/hun/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/hun/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/hun/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/hun/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/hun/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/hun/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/hun/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/hun/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/hun/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/hun/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/hun/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/hun/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/hun/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/hun/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/hun/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/hun/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/hun/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/hun/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/hun/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/hun/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/hun/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/hun/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/hun/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/hun/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/hun/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/hun/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/hun/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/hun/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/hun/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/hun/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/hun/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/hun/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/hun/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/hun/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/hun/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/hun/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/hun/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/hun/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/hun/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/hun/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/hun/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/hun/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/hun/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/hun/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/hun/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/hun/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/hun/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/hun/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/ita/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/ita/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/ita/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/ita/extensions/css/package.i18n.json create mode 100644 i18n/ita/extensions/emmet/package.i18n.json create mode 100644 i18n/ita/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/ita/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/ita/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/ita/extensions/git/out/commands.i18n.json create mode 100644 i18n/ita/extensions/git/out/main.i18n.json create mode 100644 i18n/ita/extensions/git/out/model.i18n.json create mode 100644 i18n/ita/extensions/git/out/repository.i18n.json create mode 100644 i18n/ita/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/ita/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/ita/extensions/git/package.i18n.json create mode 100644 i18n/ita/extensions/grunt/out/main.i18n.json create mode 100644 i18n/ita/extensions/grunt/package.i18n.json create mode 100644 i18n/ita/extensions/gulp/out/main.i18n.json create mode 100644 i18n/ita/extensions/gulp/package.i18n.json create mode 100644 i18n/ita/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/ita/extensions/html/package.i18n.json create mode 100644 i18n/ita/extensions/jake/out/main.i18n.json create mode 100644 i18n/ita/extensions/jake/package.i18n.json create mode 100644 i18n/ita/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/ita/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/ita/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/ita/extensions/json/package.i18n.json create mode 100644 i18n/ita/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/ita/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/ita/extensions/markdown/out/security.i18n.json create mode 100644 i18n/ita/extensions/markdown/package.i18n.json create mode 100644 i18n/ita/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/ita/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/ita/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/ita/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/ita/extensions/npm/package.i18n.json create mode 100644 i18n/ita/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/ita/extensions/php/package.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/ita/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/ita/extensions/typescript/package.i18n.json create mode 100644 i18n/ita/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/ita/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/ita/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/ita/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/ita/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/ita/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/ita/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/ita/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/ita/src/vs/base/common/json.i18n.json create mode 100644 i18n/ita/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/ita/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/ita/src/vs/base/common/processes.i18n.json create mode 100644 i18n/ita/src/vs/base/common/severity.i18n.json create mode 100644 i18n/ita/src/vs/base/node/processes.i18n.json create mode 100644 i18n/ita/src/vs/base/node/zip.i18n.json create mode 100644 i18n/ita/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/ita/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/ita/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/ita/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/ita/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/ita/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/ita/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/ita/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/ita/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/ita/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/ita/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/ita/src/vs/editor/common/config/defaultConfig.i18n.json create mode 100644 i18n/ita/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/ita/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/ita/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/ita/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/ita/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/ita/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/ita/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/ita/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/ita/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/ita/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/ita/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/ita/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/ita/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/ita/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/ita/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/ita/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/ita/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/ita/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/ita/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/ita/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/ita/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/ita/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/ita/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/ita/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/ita/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/ita/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/ita/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/ita/src/vs/platform/keybinding/common/keybindingLabels.i18n.json create mode 100644 i18n/ita/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/ita/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/ita/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/ita/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/ita/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/ita/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/ita/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/node/extHostExplorerView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/node/extHostTreeView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/api/node/mainThreadMessageService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/ita/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/ita/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/ita/src/vs/workbench/electron-browser/crashReporter.i18n.json create mode 100644 i18n/ita/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/ita/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/ita/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/ita/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/ita/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/ita/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/ita/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/gitActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/gitServices.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/git/node/git.lib.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/ita/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/jpn/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/jpn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/jpn/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/jpn/extensions/css/package.i18n.json create mode 100644 i18n/jpn/extensions/emmet/package.i18n.json create mode 100644 i18n/jpn/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/jpn/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/jpn/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/jpn/extensions/git/out/commands.i18n.json create mode 100644 i18n/jpn/extensions/git/out/main.i18n.json create mode 100644 i18n/jpn/extensions/git/out/model.i18n.json create mode 100644 i18n/jpn/extensions/git/out/repository.i18n.json create mode 100644 i18n/jpn/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/jpn/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/jpn/extensions/git/package.i18n.json create mode 100644 i18n/jpn/extensions/grunt/out/main.i18n.json create mode 100644 i18n/jpn/extensions/grunt/package.i18n.json create mode 100644 i18n/jpn/extensions/gulp/out/main.i18n.json create mode 100644 i18n/jpn/extensions/gulp/package.i18n.json create mode 100644 i18n/jpn/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/jpn/extensions/html/package.i18n.json create mode 100644 i18n/jpn/extensions/jake/out/main.i18n.json create mode 100644 i18n/jpn/extensions/jake/package.i18n.json create mode 100644 i18n/jpn/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/jpn/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/jpn/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/jpn/extensions/json/package.i18n.json create mode 100644 i18n/jpn/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/jpn/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/jpn/extensions/markdown/out/security.i18n.json create mode 100644 i18n/jpn/extensions/markdown/package.i18n.json create mode 100644 i18n/jpn/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/jpn/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/jpn/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/jpn/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/jpn/extensions/npm/package.i18n.json create mode 100644 i18n/jpn/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/jpn/extensions/php/package.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/jpn/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/jpn/extensions/typescript/package.i18n.json create mode 100644 i18n/jpn/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/jpn/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/jpn/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/jpn/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/jpn/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/jpn/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/jpn/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/jpn/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/jpn/src/vs/base/common/json.i18n.json create mode 100644 i18n/jpn/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/jpn/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/jpn/src/vs/base/common/processes.i18n.json create mode 100644 i18n/jpn/src/vs/base/common/severity.i18n.json create mode 100644 i18n/jpn/src/vs/base/node/processes.i18n.json create mode 100644 i18n/jpn/src/vs/base/node/zip.i18n.json create mode 100644 i18n/jpn/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/jpn/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/jpn/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/jpn/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/jpn/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/jpn/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/jpn/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/jpn/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/jpn/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/jpn/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/jpn/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/jpn/src/vs/editor/common/config/defaultConfig.i18n.json create mode 100644 i18n/jpn/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/jpn/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/jpn/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/jpn/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/jpn/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/jpn/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/jpn/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/jpn/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/jpn/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/jpn/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/jpn/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/jpn/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/jpn/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/jpn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/jpn/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/jpn/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/jpn/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/jpn/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/jpn/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/jpn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/jpn/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/jpn/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/jpn/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/jpn/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/jpn/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/jpn/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/jpn/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/jpn/src/vs/platform/keybinding/common/keybindingLabels.i18n.json create mode 100644 i18n/jpn/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/jpn/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/jpn/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/jpn/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/jpn/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/jpn/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/jpn/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/node/extHostExplorerView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/node/extHostTreeView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/api/node/mainThreadMessageService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/electron-browser/crashReporter.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/gitActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/gitServices.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/git/node/git.lib.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/jpn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/kor/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/kor/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/kor/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/kor/extensions/css/package.i18n.json create mode 100644 i18n/kor/extensions/emmet/package.i18n.json create mode 100644 i18n/kor/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/kor/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/kor/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/kor/extensions/git/out/commands.i18n.json create mode 100644 i18n/kor/extensions/git/out/main.i18n.json create mode 100644 i18n/kor/extensions/git/out/model.i18n.json create mode 100644 i18n/kor/extensions/git/out/repository.i18n.json create mode 100644 i18n/kor/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/kor/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/kor/extensions/git/package.i18n.json create mode 100644 i18n/kor/extensions/grunt/out/main.i18n.json create mode 100644 i18n/kor/extensions/grunt/package.i18n.json create mode 100644 i18n/kor/extensions/gulp/out/main.i18n.json create mode 100644 i18n/kor/extensions/gulp/package.i18n.json create mode 100644 i18n/kor/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/kor/extensions/html/package.i18n.json create mode 100644 i18n/kor/extensions/jake/out/main.i18n.json create mode 100644 i18n/kor/extensions/jake/package.i18n.json create mode 100644 i18n/kor/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/kor/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/kor/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/kor/extensions/json/package.i18n.json create mode 100644 i18n/kor/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/kor/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/kor/extensions/markdown/out/security.i18n.json create mode 100644 i18n/kor/extensions/markdown/package.i18n.json create mode 100644 i18n/kor/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/kor/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/kor/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/kor/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/kor/extensions/npm/package.i18n.json create mode 100644 i18n/kor/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/kor/extensions/php/package.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/kor/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/kor/extensions/typescript/package.i18n.json create mode 100644 i18n/kor/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/kor/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/kor/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/kor/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/kor/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/kor/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/kor/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/kor/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/kor/src/vs/base/common/json.i18n.json create mode 100644 i18n/kor/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/kor/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/kor/src/vs/base/common/processes.i18n.json create mode 100644 i18n/kor/src/vs/base/common/severity.i18n.json create mode 100644 i18n/kor/src/vs/base/node/processes.i18n.json create mode 100644 i18n/kor/src/vs/base/node/zip.i18n.json create mode 100644 i18n/kor/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/kor/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/kor/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/kor/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/kor/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/kor/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/kor/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/kor/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/kor/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/kor/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/kor/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/kor/src/vs/editor/common/config/defaultConfig.i18n.json create mode 100644 i18n/kor/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/kor/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/kor/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/kor/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/kor/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/kor/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/kor/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/kor/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/kor/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/kor/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/kor/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/kor/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/kor/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/kor/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/kor/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/kor/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/kor/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/kor/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/kor/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/kor/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/kor/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/kor/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/kor/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/kor/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/kor/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/kor/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/kor/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/kor/src/vs/platform/keybinding/common/keybindingLabels.i18n.json create mode 100644 i18n/kor/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/kor/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/kor/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/kor/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/kor/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/kor/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/kor/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/node/extHostExplorerView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/node/extHostTreeView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/api/node/mainThreadMessageService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/kor/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/kor/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/kor/src/vs/workbench/electron-browser/crashReporter.i18n.json create mode 100644 i18n/kor/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/kor/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/kor/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/kor/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/kor/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/kor/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/kor/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/gitActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/gitServices.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/git/node/git.lib.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/kor/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/ptb/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/ptb/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/ptb/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/ptb/extensions/css/package.i18n.json create mode 100644 i18n/ptb/extensions/emmet/package.i18n.json create mode 100644 i18n/ptb/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/ptb/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/ptb/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/ptb/extensions/git/out/commands.i18n.json create mode 100644 i18n/ptb/extensions/git/out/main.i18n.json create mode 100644 i18n/ptb/extensions/git/out/model.i18n.json create mode 100644 i18n/ptb/extensions/git/out/repository.i18n.json create mode 100644 i18n/ptb/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/ptb/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/ptb/extensions/git/package.i18n.json create mode 100644 i18n/ptb/extensions/grunt/out/main.i18n.json create mode 100644 i18n/ptb/extensions/grunt/package.i18n.json create mode 100644 i18n/ptb/extensions/gulp/out/main.i18n.json create mode 100644 i18n/ptb/extensions/gulp/package.i18n.json create mode 100644 i18n/ptb/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/ptb/extensions/html/package.i18n.json create mode 100644 i18n/ptb/extensions/jake/out/main.i18n.json create mode 100644 i18n/ptb/extensions/jake/package.i18n.json create mode 100644 i18n/ptb/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/ptb/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/ptb/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/ptb/extensions/json/package.i18n.json create mode 100644 i18n/ptb/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/ptb/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/ptb/extensions/markdown/out/security.i18n.json create mode 100644 i18n/ptb/extensions/markdown/package.i18n.json create mode 100644 i18n/ptb/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/ptb/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/ptb/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/ptb/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/ptb/extensions/npm/package.i18n.json create mode 100644 i18n/ptb/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/ptb/extensions/php/package.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/ptb/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/ptb/extensions/typescript/package.i18n.json create mode 100644 i18n/ptb/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/ptb/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/ptb/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/ptb/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/ptb/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/ptb/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/ptb/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/ptb/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/ptb/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/ptb/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/ptb/src/vs/base/common/processes.i18n.json create mode 100644 i18n/ptb/src/vs/base/common/severity.i18n.json create mode 100644 i18n/ptb/src/vs/base/node/processes.i18n.json create mode 100644 i18n/ptb/src/vs/base/node/zip.i18n.json create mode 100644 i18n/ptb/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/ptb/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/ptb/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/ptb/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/ptb/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/ptb/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/ptb/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/ptb/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/ptb/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/ptb/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/ptb/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/ptb/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/ptb/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/ptb/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/ptb/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/ptb/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/ptb/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/ptb/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/ptb/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/ptb/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/ptb/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/ptb/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/ptb/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/ptb/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/ptb/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/ptb/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/ptb/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/ptb/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/ptb/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/ptb/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/ptb/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/ptb/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/ptb/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/ptb/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/ptb/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/ptb/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/ptb/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/ptb/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/ptb/src/vs/platform/keybinding/common/keybindingLabels.i18n.json create mode 100644 i18n/ptb/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/ptb/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/ptb/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/ptb/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/ptb/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/ptb/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/ptb/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/electron-browser/crashReporter.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/ptb/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/rus/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/rus/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/rus/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/rus/extensions/css/package.i18n.json create mode 100644 i18n/rus/extensions/emmet/package.i18n.json create mode 100644 i18n/rus/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/rus/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/rus/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/rus/extensions/git/out/commands.i18n.json create mode 100644 i18n/rus/extensions/git/out/main.i18n.json create mode 100644 i18n/rus/extensions/git/out/model.i18n.json create mode 100644 i18n/rus/extensions/git/out/repository.i18n.json create mode 100644 i18n/rus/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/rus/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/rus/extensions/git/package.i18n.json create mode 100644 i18n/rus/extensions/grunt/out/main.i18n.json create mode 100644 i18n/rus/extensions/grunt/package.i18n.json create mode 100644 i18n/rus/extensions/gulp/out/main.i18n.json create mode 100644 i18n/rus/extensions/gulp/package.i18n.json create mode 100644 i18n/rus/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/rus/extensions/html/package.i18n.json create mode 100644 i18n/rus/extensions/jake/out/main.i18n.json create mode 100644 i18n/rus/extensions/jake/package.i18n.json create mode 100644 i18n/rus/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/rus/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/rus/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/rus/extensions/json/package.i18n.json create mode 100644 i18n/rus/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/rus/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/rus/extensions/markdown/out/security.i18n.json create mode 100644 i18n/rus/extensions/markdown/package.i18n.json create mode 100644 i18n/rus/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/rus/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/rus/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/rus/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/rus/extensions/npm/package.i18n.json create mode 100644 i18n/rus/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/rus/extensions/php/package.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/rus/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/rus/extensions/typescript/package.i18n.json create mode 100644 i18n/rus/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/rus/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/rus/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/rus/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/rus/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/rus/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/rus/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/rus/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/rus/src/vs/base/common/json.i18n.json create mode 100644 i18n/rus/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/rus/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/rus/src/vs/base/common/processes.i18n.json create mode 100644 i18n/rus/src/vs/base/common/severity.i18n.json create mode 100644 i18n/rus/src/vs/base/node/processes.i18n.json create mode 100644 i18n/rus/src/vs/base/node/zip.i18n.json create mode 100644 i18n/rus/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/rus/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/rus/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/rus/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/rus/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/rus/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/rus/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/rus/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/rus/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/rus/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/rus/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/rus/src/vs/editor/common/config/defaultConfig.i18n.json create mode 100644 i18n/rus/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/rus/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/rus/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/rus/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/rus/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/rus/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/rus/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/rus/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/rus/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/rus/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/rus/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/rus/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/rus/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/rus/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/rus/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/rus/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/rus/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/rus/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/rus/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/rus/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/rus/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/rus/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/rus/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/rus/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/rus/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/rus/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/rus/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/rus/src/vs/platform/keybinding/common/keybindingLabels.i18n.json create mode 100644 i18n/rus/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/rus/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/rus/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/rus/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/rus/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/rus/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/rus/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/node/extHostExplorerView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/node/extHostTreeView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/api/node/mainThreadMessageService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/rus/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/rus/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/rus/src/vs/workbench/electron-browser/crashReporter.i18n.json create mode 100644 i18n/rus/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/rus/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/rus/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/rus/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/rus/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/rus/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/rus/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/gitActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/gitServices.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/git/node/git.lib.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/rus/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 i18n/trk/extensions/configuration-editing/out/extension.i18n.json create mode 100644 i18n/trk/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json create mode 100644 i18n/trk/extensions/css/client/out/cssMain.i18n.json create mode 100644 i18n/trk/extensions/css/package.i18n.json create mode 100644 i18n/trk/extensions/emmet/package.i18n.json create mode 100644 i18n/trk/extensions/extension-editing/out/extensionLinter.i18n.json create mode 100644 i18n/trk/extensions/extension-editing/out/packageDocumentHelper.i18n.json create mode 100644 i18n/trk/extensions/git/out/askpass-main.i18n.json create mode 100644 i18n/trk/extensions/git/out/commands.i18n.json create mode 100644 i18n/trk/extensions/git/out/main.i18n.json create mode 100644 i18n/trk/extensions/git/out/model.i18n.json create mode 100644 i18n/trk/extensions/git/out/repository.i18n.json create mode 100644 i18n/trk/extensions/git/out/scmProvider.i18n.json create mode 100644 i18n/trk/extensions/git/out/statusbar.i18n.json create mode 100644 i18n/trk/extensions/git/package.i18n.json create mode 100644 i18n/trk/extensions/grunt/out/main.i18n.json create mode 100644 i18n/trk/extensions/grunt/package.i18n.json create mode 100644 i18n/trk/extensions/gulp/out/main.i18n.json create mode 100644 i18n/trk/extensions/gulp/package.i18n.json create mode 100644 i18n/trk/extensions/html/client/out/htmlMain.i18n.json create mode 100644 i18n/trk/extensions/html/package.i18n.json create mode 100644 i18n/trk/extensions/jake/out/main.i18n.json create mode 100644 i18n/trk/extensions/jake/package.i18n.json create mode 100644 i18n/trk/extensions/javascript/out/features/bowerJSONContribution.i18n.json create mode 100644 i18n/trk/extensions/javascript/out/features/packageJSONContribution.i18n.json create mode 100644 i18n/trk/extensions/json/client/out/jsonMain.i18n.json create mode 100644 i18n/trk/extensions/json/package.i18n.json create mode 100644 i18n/trk/extensions/markdown/out/extension.i18n.json create mode 100644 i18n/trk/extensions/markdown/out/previewContentProvider.i18n.json create mode 100644 i18n/trk/extensions/markdown/out/security.i18n.json create mode 100644 i18n/trk/extensions/markdown/package.i18n.json create mode 100644 i18n/trk/extensions/merge-conflict/out/codelensProvider.i18n.json create mode 100644 i18n/trk/extensions/merge-conflict/out/commandHandler.i18n.json create mode 100644 i18n/trk/extensions/merge-conflict/out/mergeDecorator.i18n.json create mode 100644 i18n/trk/extensions/merge-conflict/package.i18n.json create mode 100644 i18n/trk/extensions/npm/package.i18n.json create mode 100644 i18n/trk/extensions/php/out/features/validationProvider.i18n.json create mode 100644 i18n/trk/extensions/php/package.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/features/bufferSyncSupport.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/features/completionItemProvider.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/features/taskProvider.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/typescriptMain.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/typescriptServiceClient.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/utils/api.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/utils/logger.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/utils/projectStatus.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/utils/typingsStatus.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/utils/versionPicker.i18n.json create mode 100644 i18n/trk/extensions/typescript/out/utils/versionProvider.i18n.json create mode 100644 i18n/trk/extensions/typescript/package.i18n.json create mode 100644 i18n/trk/src/vs/base/browser/ui/actionbar/actionbar.i18n.json create mode 100644 i18n/trk/src/vs/base/browser/ui/aria/aria.i18n.json create mode 100644 i18n/trk/src/vs/base/browser/ui/findinput/findInput.i18n.json create mode 100644 i18n/trk/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json create mode 100644 i18n/trk/src/vs/base/browser/ui/inputbox/inputBox.i18n.json create mode 100644 i18n/trk/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json create mode 100644 i18n/trk/src/vs/base/browser/ui/toolbar/toolbar.i18n.json create mode 100644 i18n/trk/src/vs/base/common/errorMessage.i18n.json create mode 100644 i18n/trk/src/vs/base/common/jsonErrorMessages.i18n.json create mode 100644 i18n/trk/src/vs/base/common/keybindingLabels.i18n.json create mode 100644 i18n/trk/src/vs/base/common/processes.i18n.json create mode 100644 i18n/trk/src/vs/base/common/severity.i18n.json create mode 100644 i18n/trk/src/vs/base/node/processes.i18n.json create mode 100644 i18n/trk/src/vs/base/node/zip.i18n.json create mode 100644 i18n/trk/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json create mode 100644 i18n/trk/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json create mode 100644 i18n/trk/src/vs/base/parts/tree/browser/treeDefaults.i18n.json create mode 100644 i18n/trk/src/vs/code/electron-main/auth.i18n.json create mode 100644 i18n/trk/src/vs/code/electron-main/menus.i18n.json create mode 100644 i18n/trk/src/vs/code/electron-main/window.i18n.json create mode 100644 i18n/trk/src/vs/code/electron-main/windows.i18n.json create mode 100644 i18n/trk/src/vs/code/node/cliProcessMain.i18n.json create mode 100644 i18n/trk/src/vs/editor/browser/widget/diffEditorWidget.i18n.json create mode 100644 i18n/trk/src/vs/editor/browser/widget/diffReview.i18n.json create mode 100644 i18n/trk/src/vs/editor/common/config/commonEditorConfig.i18n.json create mode 100644 i18n/trk/src/vs/editor/common/config/editorOptions.i18n.json create mode 100644 i18n/trk/src/vs/editor/common/controller/cursor.i18n.json create mode 100644 i18n/trk/src/vs/editor/common/model/textModelWithTokens.i18n.json create mode 100644 i18n/trk/src/vs/editor/common/modes/modesRegistry.i18n.json create mode 100644 i18n/trk/src/vs/editor/common/services/bulkEdit.i18n.json create mode 100644 i18n/trk/src/vs/editor/common/services/modeServiceImpl.i18n.json create mode 100644 i18n/trk/src/vs/editor/common/services/modelServiceImpl.i18n.json create mode 100644 i18n/trk/src/vs/editor/common/view/editorColorRegistry.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/comment/common/comment.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/find/browser/findWidget.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/find/common/findController.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/folding/browser/folding.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/format/browser/formatActions.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/hover/browser/hover.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/indentation/common/indentation.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/links/browser/links.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/rename/browser/rename.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json create mode 100644 i18n/trk/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json create mode 100644 i18n/trk/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json create mode 100644 i18n/trk/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/trk/src/vs/editor/node/textMate/TMGrammars.i18n.json create mode 100644 i18n/trk/src/vs/platform/actions/browser/menuItemActionItem.i18n.json create mode 100644 i18n/trk/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json create mode 100644 i18n/trk/src/vs/platform/configuration/common/configurationRegistry.i18n.json create mode 100644 i18n/trk/src/vs/platform/environment/node/argv.i18n.json create mode 100644 i18n/trk/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json create mode 100644 i18n/trk/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json create mode 100644 i18n/trk/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json create mode 100644 i18n/trk/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json create mode 100644 i18n/trk/src/vs/platform/extensions/common/abstractExtensionService.i18n.json create mode 100644 i18n/trk/src/vs/platform/extensions/common/extensionsRegistry.i18n.json create mode 100644 i18n/trk/src/vs/platform/extensions/node/extensionValidator.i18n.json create mode 100644 i18n/trk/src/vs/platform/history/electron-main/historyMainService.i18n.json create mode 100644 i18n/trk/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json create mode 100644 i18n/trk/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json create mode 100644 i18n/trk/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json create mode 100644 i18n/trk/src/vs/platform/markers/common/problemMatcher.i18n.json create mode 100644 i18n/trk/src/vs/platform/message/common/message.i18n.json create mode 100644 i18n/trk/src/vs/platform/request/node/request.i18n.json create mode 100644 i18n/trk/src/vs/platform/telemetry/common/telemetryService.i18n.json create mode 100644 i18n/trk/src/vs/platform/theme/common/colorExtensionPoint.i18n.json create mode 100644 i18n/trk/src/vs/platform/theme/common/colorRegistry.i18n.json create mode 100644 i18n/trk/src/vs/platform/workspaces/common/workspaces.i18n.json create mode 100644 i18n/trk/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/api/node/extHostDiagnostics.i18n.json create mode 100644 i18n/trk/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json create mode 100644 i18n/trk/src/vs/workbench/api/node/extHostTask.i18n.json create mode 100644 i18n/trk/src/vs/workbench/api/node/extHostTreeViews.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/actions/configureLocale.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/actions/fileActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/actions/toggleZenMode.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/actions/workspaceActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/compositePart.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/editorActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/editorPart.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/textEditor.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/editor/titleControl.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/panel/panelActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/panel/panelPart.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/quickopen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/browser/viewlet.i18n.json create mode 100644 i18n/trk/src/vs/workbench/common/theme.i18n.json create mode 100644 i18n/trk/src/vs/workbench/electron-browser/actions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/electron-browser/commands.i18n.json create mode 100644 i18n/trk/src/vs/workbench/electron-browser/extensionHost.i18n.json create mode 100644 i18n/trk/src/vs/workbench/electron-browser/main.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/electron-browser/main.i18n.json create mode 100644 i18n/trk/src/vs/workbench/electron-browser/shell.i18n.json create mode 100644 i18n/trk/src/vs/workbench/electron-browser/window.i18n.json create mode 100644 i18n/trk/src/vs/workbench/electron-browser/workbench.i18n.json create mode 100644 i18n/trk/src/vs/workbench/node/extensionHostMain.i18n.json create mode 100644 i18n/trk/src/vs/workbench/node/extensionPoints.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/browser/debugActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/common/debug.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/common/debugModel.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/common/debugSource.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/fileActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/fileCommands.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/files.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/html/browser/html.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/html/browser/webview.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/markers/common/messages.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/output/browser/output.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/output/browser/outputActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/output/browser/outputPanel.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/output/common/output.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/common/preferences.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/browser/replaceService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/browser/search.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/browser/searchActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/browser/searchWidget.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/search/common/queryBuilder.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/update/electron-browser/update.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/views/browser/views.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json create mode 100644 i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/configuration/node/configuration.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/editor/browser/editorService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/files/electron-browser/fileService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/files/node/fileService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/message/browser/messageList.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/message/electron-browser/messageService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/progress/browser/progressService2.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/textfile/common/textFileService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json create mode 100644 i18n/trk/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json create mode 100644 issue_template.md create mode 100644 jsconfig.json create mode 100644 npm-shrinkwrap.json create mode 100644 package.json create mode 100644 product.json create mode 100644 resources/darwin/bin/code.sh create mode 100644 resources/darwin/code.icns create mode 100644 resources/darwin/code_file.icns create mode 100644 resources/letterpress-dark.svg create mode 100644 resources/letterpress-hc.svg create mode 100644 resources/letterpress.svg create mode 100644 resources/linux/bin/code.sh create mode 100644 resources/linux/code.appdata.xml create mode 100644 resources/linux/code.desktop create mode 100644 resources/linux/code.png create mode 100644 resources/linux/debian/control.template create mode 100644 resources/linux/debian/postinst.template create mode 100644 resources/linux/debian/postrm.template create mode 100644 resources/linux/debian/prerm.template create mode 100644 resources/linux/rpm/code.spec.template create mode 100644 resources/linux/rpm/code.xpm create mode 100644 resources/linux/rpm/dependencies.json create mode 100644 resources/win32/bin/code.cmd create mode 100644 resources/win32/bin/code.sh create mode 100644 resources/win32/code.ico create mode 100644 resources/win32/code_file.ico create mode 100644 resources/win32/inno-big.bmp create mode 100644 resources/win32/inno-small.bmp create mode 100644 scripts/build.bat create mode 100644 scripts/build.sh create mode 100644 scripts/code-cli.bat create mode 100644 scripts/code-cli.sh create mode 100644 scripts/code.bat create mode 100644 scripts/code.sh create mode 100644 scripts/env.ps1 create mode 100644 scripts/env.sh create mode 100644 scripts/monaco-editor-setup.js create mode 100644 scripts/npm.bat create mode 100644 scripts/npm.sh create mode 100644 scripts/sql-cli.sh create mode 100644 scripts/sql.bat create mode 100644 scripts/sql.sh create mode 100644 scripts/test-electron.bat create mode 100644 scripts/test-electron.sh create mode 100644 scripts/test-int-mocha.bat create mode 100644 scripts/test-int-mocha.sh create mode 100644 scripts/test-integration.bat create mode 100644 scripts/test-integration.sh create mode 100644 scripts/test-mocha.bat create mode 100644 scripts/test-mocha.sh create mode 100644 scripts/test.bat create mode 100644 scripts/test.sh create mode 100644 scripts/watch.bat create mode 100644 scripts/watch.sh create mode 100644 src/.eslintrc create mode 100644 src/bootstrap-amd.js create mode 100644 src/bootstrap.js create mode 100644 src/buildfile.js create mode 100644 src/cli.js create mode 100644 src/main.js create mode 100644 src/paths.js create mode 100644 src/sql/base/browser/ui/breadcrumb/breadcrumb.component.ts create mode 100644 src/sql/base/browser/ui/breadcrumb/interfaces.ts create mode 100644 src/sql/base/browser/ui/breadcrumb/media/breadcrumb.css create mode 100644 src/sql/base/browser/ui/breadcrumb/media/chevron_right.svg create mode 100644 src/sql/base/browser/ui/breadcrumb/media/chevron_right_inverse.svg create mode 100644 src/sql/base/browser/ui/checkbox/checkbox.ts create mode 100644 src/sql/base/browser/ui/checkbox/defaultCheckbox.ts create mode 100644 src/sql/base/browser/ui/checkbox/media/check.svg create mode 100644 src/sql/base/browser/ui/checkbox/media/check_inverse.svg create mode 100644 src/sql/base/browser/ui/checkbox/media/checkbox.css create mode 100644 src/sql/base/browser/ui/dropdownList/dropdownList.ts create mode 100644 src/sql/base/browser/ui/dropdownList/media/dropdownList.css create mode 100644 src/sql/base/browser/ui/dropdownList/media/dropdownarrow.svg create mode 100644 src/sql/base/browser/ui/dropdownList/media/dropdownarrow_inverse.svg create mode 100644 src/sql/base/browser/ui/editableDropdown/actions.ts create mode 100644 src/sql/base/browser/ui/editableDropdown/dropdown.ts create mode 100644 src/sql/base/browser/ui/editableDropdown/media/dropdownList.css create mode 100644 src/sql/base/browser/ui/editableDropdown/media/dropdownarrow.svg create mode 100644 src/sql/base/browser/ui/editableDropdown/media/dropdownarrow_inverse.svg create mode 100644 src/sql/base/browser/ui/inputBox/inputBox.ts create mode 100644 src/sql/base/browser/ui/listBox/listBox.ts create mode 100644 src/sql/base/browser/ui/modal/dialogHelper.ts create mode 100644 src/sql/base/browser/ui/modal/media/back.svg create mode 100644 src/sql/base/browser/ui/modal/media/back_inverse.svg create mode 100644 src/sql/base/browser/ui/modal/media/modal.css create mode 100644 src/sql/base/browser/ui/modal/media/optionsDialog.css create mode 100644 src/sql/base/browser/ui/modal/modal.ts create mode 100644 src/sql/base/browser/ui/modal/optionsDialog.ts create mode 100644 src/sql/base/browser/ui/modal/optionsDialogHelper.ts create mode 100644 src/sql/base/browser/ui/panel/media/panel.css create mode 100644 src/sql/base/browser/ui/panel/panel.component.ts create mode 100644 src/sql/base/browser/ui/panel/panel.module.ts create mode 100644 src/sql/base/browser/ui/panel/panel.ts create mode 100644 src/sql/base/browser/ui/panel/panelStyles.ts create mode 100644 src/sql/base/browser/ui/panel/tab.component.ts create mode 100644 src/sql/base/browser/ui/selectBox/selectBox.ts create mode 100644 src/sql/base/browser/ui/table/media/sort-asc.gif create mode 100644 src/sql/base/browser/ui/table/media/sort-desc.gif create mode 100644 src/sql/base/browser/ui/table/media/table.css create mode 100644 src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts create mode 100644 src/sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin.ts create mode 100644 src/sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin.ts create mode 100644 src/sql/base/browser/ui/table/plugins/rowSelectionModel.plugin.ts create mode 100644 src/sql/base/browser/ui/table/table.ts create mode 100644 src/sql/base/browser/ui/table/tableDataView.ts create mode 100644 src/sql/base/browser/ui/table/tableView.ts create mode 100644 src/sql/base/browser/ui/taskbar/actionbar.ts create mode 100644 src/sql/base/browser/ui/taskbar/media/change_connection.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/change_connection_inverse.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/connect.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/connect_inverse.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/copy_image.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/copy_image_inverse.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/create_insight.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/create_insight_inverse.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/disconnect.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/disconnect_inverse.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/ellipsis-inverse.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/ellipsis.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/icons.css create mode 100644 src/sql/base/browser/ui/taskbar/media/query-plan-inverse.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/query-plan.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/save_as_image.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/save_as_image_inverse.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/start.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/stop.svg create mode 100644 src/sql/base/browser/ui/taskbar/media/taskbar.css create mode 100644 src/sql/base/browser/ui/taskbar/taskbar.ts create mode 100644 src/sql/base/common/async.ts create mode 100644 src/sql/base/common/decorators.ts create mode 100644 src/sql/base/common/log.ts create mode 100644 src/sql/base/common/objects.ts create mode 100644 src/sql/base/common/promise.ts create mode 100644 src/sql/base/node/rangy.ts create mode 100644 src/sql/code/electron-main/oauth.ts create mode 100644 src/sql/common/browser/sqlOAuthServiceImpl.ts create mode 100644 src/sql/common/constants.ts create mode 100644 src/sql/common/pathUtilities.ts create mode 100644 src/sql/common/sqlOAuthService.ts create mode 100644 src/sql/common/telemetryKeys.ts create mode 100644 src/sql/common/telemetryUtilities.ts create mode 100644 src/sql/common/theme/colors.ts create mode 100644 src/sql/common/theme/styler.ts create mode 100644 src/sql/common/urlSerializer.ts create mode 100644 src/sql/data.d.ts create mode 100644 src/sql/media/actionBarLabel.css create mode 100644 src/sql/media/icons/backup.svg create mode 100644 src/sql/media/icons/backup_inverse.svg create mode 100644 src/sql/media/icons/close-dark.svg create mode 100644 src/sql/media/icons/close.svg create mode 100644 src/sql/media/icons/common-icons.css create mode 100644 src/sql/media/icons/configdashboard.svg create mode 100644 src/sql/media/icons/configdashboard_inverse.svg create mode 100644 src/sql/media/icons/database.svg create mode 100644 src/sql/media/icons/database_inverse.svg create mode 100644 src/sql/media/icons/ellipsis-inverse.svg create mode 100644 src/sql/media/icons/ellipsis.svg create mode 100644 src/sql/media/icons/file.svg create mode 100644 src/sql/media/icons/file_inverse.svg create mode 100644 src/sql/media/icons/filter.svg create mode 100644 src/sql/media/icons/filter_inverse.svg create mode 100644 src/sql/media/icons/loading.svg create mode 100644 src/sql/media/icons/loading_inverse.svg create mode 100644 src/sql/media/icons/new_database.svg create mode 100644 src/sql/media/icons/new_database_inverse.svg create mode 100644 src/sql/media/icons/newquery.svg create mode 100644 src/sql/media/icons/newquery_inverse.svg create mode 100644 src/sql/media/icons/refresh.svg create mode 100644 src/sql/media/icons/refresh_inverse.svg create mode 100644 src/sql/media/icons/remove.svg create mode 100644 src/sql/media/icons/remove_inverse.svg create mode 100644 src/sql/media/icons/restore.svg create mode 100644 src/sql/media/icons/restore_inverse.svg create mode 100644 src/sql/media/icons/run_history_inverse.svg create mode 100644 src/sql/media/icons/script_to_clipboard.svg create mode 100644 src/sql/media/icons/search_inverse.svg create mode 100644 src/sql/media/icons/server_page.svg create mode 100644 src/sql/media/icons/server_page_inverse.svg create mode 100644 src/sql/media/icons/sourcecontrol_inverse.svg create mode 100644 src/sql/media/icons/status_cancelled.svg create mode 100644 src/sql/media/icons/status_error.svg create mode 100644 src/sql/media/icons/status_info.svg create mode 100644 src/sql/media/icons/status_success.svg create mode 100644 src/sql/media/icons/status_warning.svg create mode 100644 src/sql/media/objectTypes/AggregateFunction.svg create mode 100644 src/sql/media/objectTypes/AggregateFunctionParameter_Input.svg create mode 100644 src/sql/media/objectTypes/AggregateFunctionParameter_Output.svg create mode 100644 src/sql/media/objectTypes/AggregateFunctionParameter_Return.svg create mode 100644 src/sql/media/objectTypes/ApplicationRole.svg create mode 100644 src/sql/media/objectTypes/Assembly.svg create mode 100644 src/sql/media/objectTypes/AsymmetricKey.svg create mode 100644 src/sql/media/objectTypes/BrokerPriority.svg create mode 100644 src/sql/media/objectTypes/Certificate.svg create mode 100644 src/sql/media/objectTypes/Column.svg create mode 100644 src/sql/media/objectTypes/ColumnEncryptionKey.svg create mode 100644 src/sql/media/objectTypes/ColumnMasterKey.svg create mode 100644 src/sql/media/objectTypes/Constraint.svg create mode 100644 src/sql/media/objectTypes/Contract.svg create mode 100644 src/sql/media/objectTypes/Database.svg create mode 100644 src/sql/media/objectTypes/DatabaseAndQueueEventNotification.svg create mode 100644 src/sql/media/objectTypes/DatabaseAuditSpecification.svg create mode 100644 src/sql/media/objectTypes/DatabaseEncryptionKey.svg create mode 100644 src/sql/media/objectTypes/DatabaseRole.svg create mode 100644 src/sql/media/objectTypes/DatabaseScopedCredential.svg create mode 100644 src/sql/media/objectTypes/DatabaseTrigger.svg create mode 100644 src/sql/media/objectTypes/Database_Unavailable.svg create mode 100644 src/sql/media/objectTypes/DefaultIcon.svg create mode 100644 src/sql/media/objectTypes/ExternalDataSource.svg create mode 100644 src/sql/media/objectTypes/ExternalFileFormat.svg create mode 100644 src/sql/media/objectTypes/FileGroupFile.svg create mode 100644 src/sql/media/objectTypes/Folder.svg create mode 100644 src/sql/media/objectTypes/FullTextCatalog.svg create mode 100644 src/sql/media/objectTypes/FullTextStopList.svg create mode 100644 src/sql/media/objectTypes/Index.svg create mode 100644 src/sql/media/objectTypes/Key_ForeignKey.svg create mode 100644 src/sql/media/objectTypes/Key_PrimaryKey.svg create mode 100644 src/sql/media/objectTypes/Key_UniqueKey.svg create mode 100644 src/sql/media/objectTypes/MasterKey.svg create mode 100644 src/sql/media/objectTypes/MessageType.svg create mode 100644 src/sql/media/objectTypes/PartitionFunction.svg create mode 100644 src/sql/media/objectTypes/PartitionScheme.svg create mode 100644 src/sql/media/objectTypes/Queue.svg create mode 100644 src/sql/media/objectTypes/RemoteServiceBinding.svg create mode 100644 src/sql/media/objectTypes/Route.svg create mode 100644 src/sql/media/objectTypes/ScalarValuedFunction.svg create mode 100644 src/sql/media/objectTypes/ScalarValuedFunctionParameter_Input.svg create mode 100644 src/sql/media/objectTypes/ScalarValuedFunctionParameter_Output.svg create mode 100644 src/sql/media/objectTypes/ScalarValuedFunctionParameter_Return.svg create mode 100644 src/sql/media/objectTypes/Schema.svg create mode 100644 src/sql/media/objectTypes/SearchPropertyList.svg create mode 100644 src/sql/media/objectTypes/SecurityPolicy.svg create mode 100644 src/sql/media/objectTypes/Sequence.svg create mode 100644 src/sql/media/objectTypes/Server.svg create mode 100644 src/sql/media/objectTypes/ServerLevelCredential.svg create mode 100644 src/sql/media/objectTypes/ServerLevelCryptographicProvider.svg create mode 100644 src/sql/media/objectTypes/ServerLevelEndpoint.svg create mode 100644 src/sql/media/objectTypes/ServerLevelLinkedServer.svg create mode 100644 src/sql/media/objectTypes/ServerLevelLinkedServerLogin.svg create mode 100644 src/sql/media/objectTypes/ServerLevelLinkedServerLogin_Disabled.svg create mode 100644 src/sql/media/objectTypes/ServerLevelLogin.svg create mode 100644 src/sql/media/objectTypes/ServerLevelLogin_Disabled.svg create mode 100644 src/sql/media/objectTypes/ServerLevelServerAudit.svg create mode 100644 src/sql/media/objectTypes/ServerLevelServerAuditSpecification.svg create mode 100644 src/sql/media/objectTypes/ServerLevelServerRole.svg create mode 100644 src/sql/media/objectTypes/ServerLevelServerTrigger.svg create mode 100644 src/sql/media/objectTypes/Service.svg create mode 100644 src/sql/media/objectTypes/SqlLogFile.svg create mode 100644 src/sql/media/objectTypes/Statistic.svg create mode 100644 src/sql/media/objectTypes/StoredProcedure.svg create mode 100644 src/sql/media/objectTypes/StoredProcedureParameter_Input.svg create mode 100644 src/sql/media/objectTypes/StoredProcedureParameter_Output.svg create mode 100644 src/sql/media/objectTypes/StoredProcedureParameter_Return.svg create mode 100644 src/sql/media/objectTypes/SymmetricKey.svg create mode 100644 src/sql/media/objectTypes/Synonym.svg create mode 100644 src/sql/media/objectTypes/SystemApproximateNumeric.svg create mode 100644 src/sql/media/objectTypes/SystemBinaryString.svg create mode 100644 src/sql/media/objectTypes/SystemCharacterString.svg create mode 100644 src/sql/media/objectTypes/SystemClrDataType.svg create mode 100644 src/sql/media/objectTypes/SystemContract.svg create mode 100644 src/sql/media/objectTypes/SystemDateAndTime.svg create mode 100644 src/sql/media/objectTypes/SystemExactNumeric.svg create mode 100644 src/sql/media/objectTypes/SystemMessageType.svg create mode 100644 src/sql/media/objectTypes/SystemOtherDataType.svg create mode 100644 src/sql/media/objectTypes/SystemQueue.svg create mode 100644 src/sql/media/objectTypes/SystemService.svg create mode 100644 src/sql/media/objectTypes/SystemSpatialDataType.svg create mode 100644 src/sql/media/objectTypes/SystemUnicodeCharacterString.svg create mode 100644 src/sql/media/objectTypes/Table.svg create mode 100644 src/sql/media/objectTypes/TableValuedFunction.svg create mode 100644 src/sql/media/objectTypes/TableValuedFunctionParameter_Input.svg create mode 100644 src/sql/media/objectTypes/TableValuedFunctionParameter_Output.svg create mode 100644 src/sql/media/objectTypes/TableValuedFunctionParameter_Return.svg create mode 100644 src/sql/media/objectTypes/Table_Temporal.svg create mode 100644 src/sql/media/objectTypes/Trigger.svg create mode 100644 src/sql/media/objectTypes/Trigger_Disabled.svg create mode 100644 src/sql/media/objectTypes/User.svg create mode 100644 src/sql/media/objectTypes/UserDefinedDataType.svg create mode 100644 src/sql/media/objectTypes/UserDefinedTableType.svg create mode 100644 src/sql/media/objectTypes/UserDefinedTableTypeColumn.svg create mode 100644 src/sql/media/objectTypes/UserDefinedTableTypeConstraint.svg create mode 100644 src/sql/media/objectTypes/UserDefinedType.svg create mode 100644 src/sql/media/objectTypes/User_Disabled.svg create mode 100644 src/sql/media/objectTypes/View.svg create mode 100644 src/sql/media/objectTypes/XmlSchemaCollection.svg create mode 100644 src/sql/media/objectTypes/objecttypes.css create mode 100644 src/sql/parts/accountManagement/accountDialog/accountDialog.ts create mode 100644 src/sql/parts/accountManagement/accountDialog/accountDialogController.ts create mode 100644 src/sql/parts/accountManagement/accountDialog/accountViewModel.ts create mode 100644 src/sql/parts/accountManagement/accountDialog/media/accountDialog.css create mode 100644 src/sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem.ts create mode 100644 src/sql/parts/accountManagement/accountListStatusbar/media/accountListStatusbarItem.css create mode 100644 src/sql/parts/accountManagement/accountListStatusbar/media/accounts_statusbar_inverse.svg create mode 100644 src/sql/parts/accountManagement/accountPicker/accountPicker.ts create mode 100644 src/sql/parts/accountManagement/accountPicker/accountPickerService.ts create mode 100644 src/sql/parts/accountManagement/accountPicker/accountPickerViewModel.ts create mode 100644 src/sql/parts/accountManagement/accountPicker/media/accountPicker.css create mode 100644 src/sql/parts/accountManagement/common/accountActions.ts create mode 100644 src/sql/parts/accountManagement/common/accountListRenderer.ts create mode 100644 src/sql/parts/accountManagement/common/interfaces.ts create mode 100644 src/sql/parts/accountManagement/common/media/accountActions.css create mode 100644 src/sql/parts/accountManagement/common/media/accountListRenderer.css create mode 100644 src/sql/parts/accountManagement/common/media/new_account.svg create mode 100644 src/sql/parts/accountManagement/common/media/new_account_inverse.svg create mode 100644 src/sql/parts/accountManagement/common/resourceProviderService.ts create mode 100644 src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialog.ts create mode 100644 src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialogController.ts create mode 100644 src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleViewModel.ts create mode 100644 src/sql/parts/accountManagement/firewallRuleDialog/media/firewallRuleDialog.css create mode 100644 src/sql/parts/accountManagement/firewallRuleDialog/media/secure.svg create mode 100644 src/sql/parts/admin/common/adminService.ts create mode 100644 src/sql/parts/admin/database/create/createDatabase.component.html create mode 100644 src/sql/parts/admin/database/create/createDatabase.component.ts create mode 100644 src/sql/parts/admin/security/createLogin.component.html create mode 100644 src/sql/parts/admin/security/createLogin.component.ts create mode 100644 src/sql/parts/admin/security/createLogin.module.ts create mode 100644 src/sql/parts/admin/security/createLoginEditor.ts create mode 100644 src/sql/parts/admin/security/createLoginInput.ts create mode 100644 src/sql/parts/common/customInputConverter.ts create mode 100644 src/sql/parts/common/rxjsUtils.ts create mode 100644 src/sql/parts/connection/common/connection.contribution.ts create mode 100644 src/sql/parts/connection/common/connection.ts create mode 100644 src/sql/parts/connection/common/connectionActions.ts create mode 100644 src/sql/parts/connection/common/connectionConfig.ts create mode 100644 src/sql/parts/connection/common/connectionGlobalStatus.ts create mode 100644 src/sql/parts/connection/common/connectionInfo.ts create mode 100644 src/sql/parts/connection/common/connectionManagement.ts create mode 100644 src/sql/parts/connection/common/connectionManagementInfo.ts create mode 100644 src/sql/parts/connection/common/connectionManagementService.ts create mode 100644 src/sql/parts/connection/common/connectionProfile.ts create mode 100644 src/sql/parts/connection/common/connectionProfileGroup.ts create mode 100644 src/sql/parts/connection/common/connectionStatus.ts create mode 100644 src/sql/parts/connection/common/connectionStatusManager.ts create mode 100644 src/sql/parts/connection/common/connectionStore.ts create mode 100644 src/sql/parts/connection/common/constants.ts create mode 100644 src/sql/parts/connection/common/iconnectionConfig.ts create mode 100644 src/sql/parts/connection/common/interfaces.ts create mode 100644 src/sql/parts/connection/common/localizedConstants.ts create mode 100644 src/sql/parts/connection/common/providerConnectionInfo.ts create mode 100644 src/sql/parts/connection/common/utils.ts create mode 100644 src/sql/parts/connection/connectionDialog/advancedPropertiesController.ts create mode 100644 src/sql/parts/connection/connectionDialog/connectionController.ts create mode 100644 src/sql/parts/connection/connectionDialog/connectionDialogService.ts create mode 100644 src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts create mode 100644 src/sql/parts/connection/connectionDialog/connectionWidget.ts create mode 100644 src/sql/parts/connection/connectionDialog/media/connectionDialog.css create mode 100644 src/sql/parts/connection/connectionDialog/media/sqlConnection.css create mode 100644 src/sql/parts/dashboard/common/actions.ts create mode 100644 src/sql/parts/dashboard/common/componentHost.directive.ts create mode 100644 src/sql/parts/dashboard/common/dashboardPage.component.html create mode 100644 src/sql/parts/dashboard/common/dashboardPage.component.ts create mode 100644 src/sql/parts/dashboard/common/dashboardWidget.ts create mode 100644 src/sql/parts/dashboard/common/dashboardWidgetWrapper.component.html create mode 100644 src/sql/parts/dashboard/common/dashboardWidgetWrapper.component.ts create mode 100644 src/sql/parts/dashboard/common/interfaces.ts create mode 100644 src/sql/parts/dashboard/dashboard.component.html create mode 100644 src/sql/parts/dashboard/dashboard.component.ts create mode 100644 src/sql/parts/dashboard/dashboard.module.ts create mode 100644 src/sql/parts/dashboard/dashboardConfig.contribution.ts create mode 100644 src/sql/parts/dashboard/dashboardEditor.ts create mode 100644 src/sql/parts/dashboard/dashboardInput.ts create mode 100644 src/sql/parts/dashboard/pages/databaseDashboardPage.component.ts create mode 100644 src/sql/parts/dashboard/pages/databaseDashboardPage.contribution.ts create mode 100644 src/sql/parts/dashboard/pages/serverDashboardPage.component.ts create mode 100644 src/sql/parts/dashboard/pages/serverDashboardPage.contribution.ts create mode 100644 src/sql/parts/dashboard/services/breadcrumb.service.ts create mode 100644 src/sql/parts/dashboard/services/dashboardServiceInterface.service.ts create mode 100644 src/sql/parts/dashboard/widgets/explorer/explorerActions.ts create mode 100644 src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.html create mode 100644 src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.ts create mode 100644 src/sql/parts/dashboard/widgets/explorer/explorerWidget.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/explorer/media/explorerWidget.css create mode 100644 src/sql/parts/dashboard/widgets/insights/actions.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/insightsWidget.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/insightsWidget.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/insightsWidgetSchemas.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/interfaces.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/barChart.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/doughnutChart.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/doughnutChart.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/horizontalBarChart.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/horizontalBarChart.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/pieChart.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/pieChart.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/scatterChart.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/scatterChart.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/timeSeriesChart.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/charts/types/timeSeriesChart.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/countInsight.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/countInsight.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/imageInsight.component.ts create mode 100644 src/sql/parts/dashboard/widgets/insights/views/imageInsight.contribution.ts create mode 100644 src/sql/parts/dashboard/widgets/properties/propertiesJson.ts create mode 100644 src/sql/parts/dashboard/widgets/properties/propertiesWidget.component.html create mode 100644 src/sql/parts/dashboard/widgets/properties/propertiesWidget.component.ts create mode 100644 src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.html create mode 100644 src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.ts create mode 100644 src/sql/parts/dashboard/widgets/tasks/tasksWidget.contribution.ts create mode 100644 src/sql/parts/disasterRecovery/backup/backup.component.html create mode 100644 src/sql/parts/disasterRecovery/backup/backup.component.ts create mode 100644 src/sql/parts/disasterRecovery/backup/backup.module.ts create mode 100644 src/sql/parts/disasterRecovery/backup/backupDialog.ts create mode 100644 src/sql/parts/disasterRecovery/backup/constants.ts create mode 100644 src/sql/parts/disasterRecovery/backup/media/backupDialog.css create mode 100644 src/sql/parts/disasterRecovery/common/disasterRecoveryService.ts create mode 100644 src/sql/parts/disasterRecovery/common/disasterRecoveryUiService.ts create mode 100644 src/sql/parts/disasterRecovery/common/interfaces.ts create mode 100644 src/sql/parts/disasterRecovery/restore/media/restoreDialog.css create mode 100644 src/sql/parts/disasterRecovery/restore/mssqlRestoreInfo.ts create mode 100644 src/sql/parts/disasterRecovery/restore/restoreDialog.ts create mode 100644 src/sql/parts/disasterRecovery/restore/restoreDialogController.ts create mode 100644 src/sql/parts/disasterRecovery/restore/restoreViewModel.ts create mode 100644 src/sql/parts/editData/common/editDataInput.ts create mode 100644 src/sql/parts/editData/editor/editDataEditor.ts create mode 100644 src/sql/parts/editData/execution/editDataActions.ts create mode 100644 src/sql/parts/fileBrowser/common/fileBrowserService.ts create mode 100644 src/sql/parts/fileBrowser/common/fileBrowserTree.ts create mode 100644 src/sql/parts/fileBrowser/common/fileNode.ts create mode 100644 src/sql/parts/fileBrowser/common/fileValidationServiceConstants.ts create mode 100644 src/sql/parts/fileBrowser/common/interfaces.ts create mode 100644 src/sql/parts/fileBrowser/fileBrowserController.ts create mode 100644 src/sql/parts/fileBrowser/fileBrowserDataSource.ts create mode 100644 src/sql/parts/fileBrowser/fileBrowserDialog.ts create mode 100644 src/sql/parts/fileBrowser/fileBrowserDialogController.ts create mode 100644 src/sql/parts/fileBrowser/fileBrowserRenderer.ts create mode 100644 src/sql/parts/fileBrowser/fileBrowserTreeView.ts create mode 100644 src/sql/parts/fileBrowser/fileBrowserViewModel.ts create mode 100644 src/sql/parts/fileBrowser/media/fileBrowserDialog.css create mode 100644 src/sql/parts/grid/common/gridContentEvents.ts create mode 100644 src/sql/parts/grid/common/interfaces.ts create mode 100644 src/sql/parts/grid/directives/mousedown.directive.ts create mode 100644 src/sql/parts/grid/directives/scroll.directive.ts create mode 100644 src/sql/parts/grid/load/css/qp.css create mode 100644 src/sql/parts/grid/load/css/qp_icons.png create mode 100644 src/sql/parts/grid/load/loadJquery.js create mode 100644 src/sql/parts/grid/media/collapsedArrow.svg create mode 100644 src/sql/parts/grid/media/collapsedArrow_inverse.svg create mode 100644 src/sql/parts/grid/media/exitFullScreen.svg create mode 100644 src/sql/parts/grid/media/exitFullScreen_inverse.svg create mode 100644 src/sql/parts/grid/media/extendFullScreen.svg create mode 100644 src/sql/parts/grid/media/extendFullScreen_inverse.svg create mode 100644 src/sql/parts/grid/media/flexbox.css create mode 100644 src/sql/parts/grid/media/saveCsv.svg create mode 100644 src/sql/parts/grid/media/saveCsv_inverse.svg create mode 100644 src/sql/parts/grid/media/saveExcel.svg create mode 100644 src/sql/parts/grid/media/saveExcel_inverse.svg create mode 100644 src/sql/parts/grid/media/saveJson.svg create mode 100644 src/sql/parts/grid/media/saveJson_inverse.svg create mode 100644 src/sql/parts/grid/media/slick.grid.css create mode 100644 src/sql/parts/grid/media/slickColorTheme.css create mode 100644 src/sql/parts/grid/media/slickGrid.css create mode 100644 src/sql/parts/grid/media/styles.css create mode 100644 src/sql/parts/grid/media/uncollapsedArrow.svg create mode 100644 src/sql/parts/grid/media/uncollapsedArrow_inverse.svg create mode 100644 src/sql/parts/grid/media/viewChart.svg create mode 100644 src/sql/parts/grid/media/viewChart_inverse.svg create mode 100644 src/sql/parts/grid/services/dataService.ts create mode 100644 src/sql/parts/grid/services/sharedServices.ts create mode 100644 src/sql/parts/grid/views/editData/editData.component.html create mode 100644 src/sql/parts/grid/views/editData/editData.component.ts create mode 100644 src/sql/parts/grid/views/editData/editData.module.ts create mode 100644 src/sql/parts/grid/views/editData/editDataGridActions.ts create mode 100644 src/sql/parts/grid/views/editData/media/editData.css create mode 100644 src/sql/parts/grid/views/gridActions.ts create mode 100644 src/sql/parts/grid/views/gridCommands.ts create mode 100644 src/sql/parts/grid/views/gridParentComponent.ts create mode 100644 src/sql/parts/grid/views/query/chartViewer.component.html create mode 100644 src/sql/parts/grid/views/query/chartViewer.component.ts create mode 100644 src/sql/parts/grid/views/query/chartViewer.css create mode 100644 src/sql/parts/grid/views/query/chartViewerActions.ts create mode 100644 src/sql/parts/grid/views/query/query.component.html create mode 100644 src/sql/parts/grid/views/query/query.component.ts create mode 100644 src/sql/parts/insights/browser/insightsDialogView.ts create mode 100644 src/sql/parts/insights/browser/media/insightsDialog.css create mode 100644 src/sql/parts/insights/common/insightDialogActions.ts create mode 100644 src/sql/parts/insights/common/insightsDialogModel.ts create mode 100644 src/sql/parts/insights/common/interfaces.ts create mode 100644 src/sql/parts/insights/insightsDialogService.ts create mode 100644 src/sql/parts/insights/node/insightsDialogController.ts create mode 100644 src/sql/parts/profiler/contrib/profiler.contribution.ts create mode 100644 src/sql/parts/profiler/contrib/profilerActions.contribution.ts create mode 100644 src/sql/parts/profiler/contrib/profilerActions.ts create mode 100644 src/sql/parts/profiler/contrib/profilerWorkbenchActions.ts create mode 100644 src/sql/parts/profiler/dialog/media/profilerDialog.css create mode 100644 src/sql/parts/profiler/dialog/profilerColumnEditorDialog.ts create mode 100644 src/sql/parts/profiler/editor/controller/interfaces.ts create mode 100644 src/sql/parts/profiler/editor/controller/profilerFindWidget.ts create mode 100644 src/sql/parts/profiler/editor/controller/profilerTableEditor.ts create mode 100644 src/sql/parts/profiler/editor/interfaces.ts create mode 100644 src/sql/parts/profiler/editor/profilerEditor.ts create mode 100644 src/sql/parts/profiler/editor/profilerInput.ts create mode 100644 src/sql/parts/profiler/editor/profilerResourceEditor.ts create mode 100644 src/sql/parts/profiler/editor/profilerState.ts create mode 100644 src/sql/parts/profiler/service/interfaces.ts create mode 100644 src/sql/parts/profiler/service/profilerService.ts create mode 100644 src/sql/parts/profiler/service/profilerTestBackend.ts create mode 100644 src/sql/parts/profiler/service/testData.tsv create mode 100644 src/sql/parts/query/common/constants.ts create mode 100644 src/sql/parts/query/common/flavorStatus.ts create mode 100644 src/sql/parts/query/common/localizedConstants.ts create mode 100644 src/sql/parts/query/common/query.contribution.ts create mode 100644 src/sql/parts/query/common/queryContext.ts create mode 100644 src/sql/parts/query/common/queryEditorService.ts create mode 100644 src/sql/parts/query/common/queryInput.ts create mode 100644 src/sql/parts/query/common/queryManagement.ts create mode 100644 src/sql/parts/query/common/queryResultsInput.ts create mode 100644 src/sql/parts/query/common/resultSerializer.ts create mode 100644 src/sql/parts/query/editor/editorDescriptorService.ts create mode 100644 src/sql/parts/query/editor/media/binarydiffeditor.css create mode 100644 src/sql/parts/query/editor/media/close-dirty-inverse.svg create mode 100644 src/sql/parts/query/editor/media/close-dirty.svg create mode 100644 src/sql/parts/query/editor/media/close-inverse.svg create mode 100644 src/sql/parts/query/editor/media/close.svg create mode 100644 src/sql/parts/query/editor/media/editorGroupsControl.css create mode 100644 src/sql/parts/query/editor/media/editorpart.css create mode 100644 src/sql/parts/query/editor/media/editorpicker.css create mode 100644 src/sql/parts/query/editor/media/editorstatus.css create mode 100644 src/sql/parts/query/editor/media/letterpress-dark.svg create mode 100644 src/sql/parts/query/editor/media/letterpress-hc.svg create mode 100644 src/sql/parts/query/editor/media/letterpress.svg create mode 100644 src/sql/parts/query/editor/media/next-diff-inverse.svg create mode 100644 src/sql/parts/query/editor/media/next-diff.svg create mode 100644 src/sql/parts/query/editor/media/notabstitle.css create mode 100644 src/sql/parts/query/editor/media/parseQuery.svg create mode 100644 src/sql/parts/query/editor/media/previous-diff-inverse.svg create mode 100644 src/sql/parts/query/editor/media/previous-diff.svg create mode 100644 src/sql/parts/query/editor/media/queryEditor.css create mode 100644 src/sql/parts/query/editor/media/runWithoutDebug.svg create mode 100644 src/sql/parts/query/editor/media/sidebysideEditor.css create mode 100644 src/sql/parts/query/editor/media/split-editor-horizontal-inverse.svg create mode 100644 src/sql/parts/query/editor/media/split-editor-horizontal.svg create mode 100644 src/sql/parts/query/editor/media/split-editor-vertical-inverse.svg create mode 100644 src/sql/parts/query/editor/media/split-editor-vertical.svg create mode 100644 src/sql/parts/query/editor/media/stackview-inverse.svg create mode 100644 src/sql/parts/query/editor/media/stackview.svg create mode 100644 src/sql/parts/query/editor/media/tabstitle.css create mode 100644 src/sql/parts/query/editor/media/textdiffeditor.css create mode 100644 src/sql/parts/query/editor/media/titlecontrol.css create mode 100644 src/sql/parts/query/editor/queryEditor.ts create mode 100644 src/sql/parts/query/editor/queryResultsEditor.ts create mode 100644 src/sql/parts/query/execution/keyboardQueryActions.ts create mode 100644 src/sql/parts/query/execution/queryActions.ts create mode 100644 src/sql/parts/query/execution/queryModel.ts create mode 100644 src/sql/parts/query/execution/queryModelService.ts create mode 100644 src/sql/parts/query/execution/queryRunner.ts create mode 100644 src/sql/parts/query/execution/queryStatus.ts create mode 100644 src/sql/parts/query/services/queryEditorService.ts create mode 100644 src/sql/parts/query/views/flexibleSash.ts create mode 100644 src/sql/parts/query/views/queryOutput.component.html create mode 100644 src/sql/parts/query/views/queryOutput.component.ts create mode 100644 src/sql/parts/query/views/queryOutput.module.ts create mode 100644 src/sql/parts/queryPlan/planXmlParser.ts create mode 100644 src/sql/parts/queryPlan/queryPlan.component.ts create mode 100644 src/sql/parts/queryPlan/queryPlan.module.ts create mode 100644 src/sql/parts/queryPlan/queryPlanEditor.ts create mode 100644 src/sql/parts/queryPlan/queryPlanInput.ts create mode 100644 src/sql/parts/queryPlan/topOperations.component.ts create mode 100644 src/sql/parts/registeredServer/common/nodeType.ts create mode 100644 src/sql/parts/registeredServer/common/objectExplorerService.ts create mode 100644 src/sql/parts/registeredServer/common/registeredServer.contribution.ts create mode 100644 src/sql/parts/registeredServer/common/treeNode.ts create mode 100644 src/sql/parts/registeredServer/serverGroupDialog/media/serverGroupDialog.css create mode 100644 src/sql/parts/registeredServer/serverGroupDialog/serverGroup.contribution.ts create mode 100644 src/sql/parts/registeredServer/serverGroupDialog/serverGroupController.ts create mode 100644 src/sql/parts/registeredServer/serverGroupDialog/serverGroupDialog.ts create mode 100644 src/sql/parts/registeredServer/serverGroupDialog/serverGroupViewModel.ts create mode 100644 src/sql/parts/registeredServer/viewlet/connectionTreeAction.ts create mode 100644 src/sql/parts/registeredServer/viewlet/connectionViewlet.ts create mode 100644 src/sql/parts/registeredServer/viewlet/dragAndDropController.ts create mode 100644 src/sql/parts/registeredServer/viewlet/media/add_server.svg create mode 100644 src/sql/parts/registeredServer/viewlet/media/add_server_inverse.svg create mode 100644 src/sql/parts/registeredServer/viewlet/media/collapsed-dark.svg create mode 100644 src/sql/parts/registeredServer/viewlet/media/connected_active_server.svg create mode 100644 src/sql/parts/registeredServer/viewlet/media/connected_active_server_inverse.svg create mode 100644 src/sql/parts/registeredServer/viewlet/media/connectionViewlet.css create mode 100644 src/sql/parts/registeredServer/viewlet/media/disconnected_server.svg create mode 100644 src/sql/parts/registeredServer/viewlet/media/disconnected_server_inverse.svg create mode 100644 src/sql/parts/registeredServer/viewlet/media/expanded-dark.svg create mode 100644 src/sql/parts/registeredServer/viewlet/media/new_servergroup.svg create mode 100644 src/sql/parts/registeredServer/viewlet/media/new_servergroup_inverse.svg create mode 100644 src/sql/parts/registeredServer/viewlet/media/serverTreeActions.css create mode 100644 src/sql/parts/registeredServer/viewlet/objectExplorerActions.ts create mode 100644 src/sql/parts/registeredServer/viewlet/recentConnectionDataSource.ts create mode 100644 src/sql/parts/registeredServer/viewlet/serverTreeActionProvider.ts create mode 100644 src/sql/parts/registeredServer/viewlet/serverTreeController.ts create mode 100644 src/sql/parts/registeredServer/viewlet/serverTreeDataSource.ts create mode 100644 src/sql/parts/registeredServer/viewlet/serverTreeRenderer.ts create mode 100644 src/sql/parts/registeredServer/viewlet/serverTreeView.ts create mode 100644 src/sql/parts/registeredServer/viewlet/templateData.ts create mode 100644 src/sql/parts/registeredServer/viewlet/treeCreationUtils.ts create mode 100644 src/sql/parts/registeredServer/viewlet/treeSelectionHandler.ts create mode 100644 src/sql/parts/registeredServer/viewlet/treeUpdateUtils.ts create mode 100644 src/sql/parts/taskHistory/common/taskHistory.contribution.ts create mode 100644 src/sql/parts/taskHistory/common/taskNode.ts create mode 100644 src/sql/parts/taskHistory/common/taskService.ts create mode 100644 src/sql/parts/taskHistory/viewlet/media/status_queuedtask.svg create mode 100644 src/sql/parts/taskHistory/viewlet/media/status_queuedtask_inverse.svg create mode 100644 src/sql/parts/taskHistory/viewlet/media/taskHistoryViewlet.css create mode 100644 src/sql/parts/taskHistory/viewlet/taskAction.ts create mode 100644 src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts create mode 100644 src/sql/parts/taskHistory/viewlet/taskHistoryController.ts create mode 100644 src/sql/parts/taskHistory/viewlet/taskHistoryDataSource.ts create mode 100644 src/sql/parts/taskHistory/viewlet/taskHistoryRenderer.ts create mode 100644 src/sql/parts/taskHistory/viewlet/taskHistoryView.ts create mode 100644 src/sql/parts/taskHistory/viewlet/taskHistoryViewlet.ts create mode 100644 src/sql/parts/taskHistory/viewlet/templateData.ts create mode 100644 src/sql/parts/tasks/common/tasks.contribution.ts create mode 100644 src/sql/parts/tasks/common/tasks.ts create mode 100644 src/sql/parts/tasks/dialog/taskDialog.component.html create mode 100644 src/sql/parts/tasks/dialog/taskDialog.component.ts create mode 100644 src/sql/parts/tasks/dialog/taskDialog.module.ts create mode 100644 src/sql/parts/tasks/dialog/taskDialogEditor.ts create mode 100644 src/sql/parts/tasks/dialog/taskDialogInput.ts create mode 100644 src/sql/platform/clipboard/common/clipboardService.ts create mode 100644 src/sql/platform/clipboard/electron-browser/clipboardService.ts create mode 100644 src/sql/platform/dashboard/common/insightRegistry.ts create mode 100644 src/sql/platform/dashboard/common/widgetRegistry.ts create mode 100644 src/sql/platform/tasks/taskRegistry.ts create mode 100644 src/sql/platform/views/fixedCollapsibleView.ts create mode 100644 src/sql/platform/views/fixedListView.ts create mode 100644 src/sql/services/accountManagement/accountManagementService.ts create mode 100644 src/sql/services/accountManagement/accountStore.ts create mode 100644 src/sql/services/accountManagement/eventTypes.ts create mode 100644 src/sql/services/accountManagement/interfaces.ts create mode 100644 src/sql/services/angularEventing/angularEventingService.ts create mode 100644 src/sql/services/bootstrap/bootstrapParams.ts create mode 100644 src/sql/services/bootstrap/bootstrapService.ts create mode 100644 src/sql/services/bootstrap/bootstrapServiceImpl.ts create mode 100644 src/sql/services/capabilities/capabilitiesService.ts create mode 100644 src/sql/services/credentials/credentialsService.ts create mode 100644 src/sql/services/metadata/metadataService.ts create mode 100644 src/sql/services/scripting/scriptingService.ts create mode 100644 src/sql/services/serialization/serializationService.ts create mode 100644 src/sql/workbench/api/node/extHostAccountManagement.ts create mode 100644 src/sql/workbench/api/node/extHostCredentialManagement.ts create mode 100644 src/sql/workbench/api/node/extHostDataProtocol.ts create mode 100644 src/sql/workbench/api/node/extHostResourceProvider.ts create mode 100644 src/sql/workbench/api/node/extHostSerializationProvider.ts create mode 100644 src/sql/workbench/api/node/mainThreadAccountManagement.ts create mode 100644 src/sql/workbench/api/node/mainThreadCredentialManagement.ts create mode 100644 src/sql/workbench/api/node/mainThreadDataProtocol.ts create mode 100644 src/sql/workbench/api/node/mainThreadResourceProvider.ts create mode 100644 src/sql/workbench/api/node/mainThreadSerializationProvider.ts create mode 100644 src/sql/workbench/api/node/sqlExtHost.api.impl.ts create mode 100644 src/sql/workbench/api/node/sqlExtHost.contribution.ts create mode 100644 src/sql/workbench/api/node/sqlExtHost.protocol.ts create mode 100644 src/sql/workbench/api/node/sqlExtHostTypes.ts create mode 100644 src/sql/workbench/common/actions.contribution.ts create mode 100644 src/sql/workbench/common/actions.ts create mode 100644 src/sql/workbench/common/sqlWorkbenchUtils.ts create mode 100644 src/sql/workbench/common/taskUtilities.ts create mode 100644 src/sql/workbench/electron-browser/splashscreen/splash.png create mode 100644 src/sql/workbench/electron-browser/splashscreen/splashscreen.css create mode 100644 src/sql/workbench/electron-browser/splashscreen/splashscreen.html create mode 100644 src/sql/workbench/errorMessageDialog/errorMessageDialog.ts create mode 100644 src/sql/workbench/errorMessageDialog/errorMessageService.ts create mode 100644 src/sql/workbench/errorMessageDialog/media/errorMessageDialog.css create mode 100644 src/sql/workbench/update/releaseNotes.ts create mode 100644 src/sqltest/common/telemetryUtilities.test.ts create mode 100644 src/sqltest/parts/accountManagement/accountActions.test.ts create mode 100644 src/sqltest/parts/accountManagement/accountDialogController.test.ts create mode 100644 src/sqltest/parts/accountManagement/accountViewModel.test.ts create mode 100644 src/sqltest/parts/admin/adminService.test.ts create mode 100644 src/sqltest/parts/common/optionsDialogHelper.test.ts create mode 100644 src/sqltest/parts/connection/advancedPropertiesDialog.test.ts create mode 100644 src/sqltest/parts/connection/connectionConfig.test.ts create mode 100644 src/sqltest/parts/connection/connectionDialogService.test.ts create mode 100644 src/sqltest/parts/connection/connectionManagementService.test.ts create mode 100644 src/sqltest/parts/connection/connectionProfile.test.ts create mode 100644 src/sqltest/parts/connection/connectionProfileGroup.test.ts create mode 100644 src/sqltest/parts/connection/connectionStatusManager.test.ts create mode 100644 src/sqltest/parts/connection/connectionStore.test.ts create mode 100644 src/sqltest/parts/connection/connectionTreeActions.test.ts create mode 100644 src/sqltest/parts/connection/objectExplorerService.test.ts create mode 100644 src/sqltest/parts/connection/providerConnectionInfo.test.ts create mode 100644 src/sqltest/parts/dashboard/widgets/explorerWidget.component.test.ts create mode 100644 src/sqltest/parts/dashboard/widgets/propertiesWidget.component.test.ts create mode 100644 src/sqltest/parts/disasterRecovery/restoreViewModel.test.ts create mode 100644 src/sqltest/parts/insights/insightsDialogController.test.ts create mode 100644 src/sqltest/parts/insights/insightsDialogModel.test.ts create mode 100644 src/sqltest/parts/query/editor/queryActions.test.ts create mode 100644 src/sqltest/parts/query/editor/queryEditor.test.ts create mode 100644 src/sqltest/parts/registeredServer/viewlet/serverTreeView.test.ts create mode 100644 src/sqltest/services/accountManagement/accountManagementService.test.ts create mode 100644 src/sqltest/services/accountManagement/accountStore.test.ts create mode 100644 src/sqltest/stubs/accountManagementStubs.ts create mode 100644 src/sqltest/stubs/capabilitiesTestService.ts create mode 100644 src/sqltest/stubs/connectionDialogTestService.ts create mode 100644 src/sqltest/stubs/connectionManagementService.test.ts create mode 100644 src/sqltest/stubs/connectionProviderStub.ts create mode 100644 src/sqltest/stubs/contextKeyServiceStub.ts create mode 100644 src/sqltest/stubs/credentialsTestStubs.ts create mode 100644 src/sqltest/stubs/editorGroupService.ts create mode 100644 src/sqltest/stubs/errorMessageServiceStub.ts create mode 100644 src/sqltest/stubs/messageServiceStub.ts create mode 100644 src/sqltest/stubs/objectExplorerProviderTestService.ts create mode 100644 src/sqltest/stubs/sqlOauthServiceStub.ts create mode 100644 src/sqltest/stubs/storageTestService.ts create mode 100644 src/sqltest/stubs/telemetryServiceStub.ts create mode 100644 src/sqltest/stubs/themeTestService.ts create mode 100644 src/sqltest/stubs/workbenchEditorTestService.ts create mode 100644 src/sqltest/stubs/workspaceConfigurationTestService.ts create mode 100644 src/sqltest/utils/eventVerifier.ts create mode 100644 src/sqltest/workbench/api/extHostAccountManagement.test.ts create mode 100644 src/sqltest/workbench/api/extHostCredentialManagement.test.ts create mode 100644 src/sqltest/workbench/api/extHostDataProtocol.test.ts create mode 100644 src/tsconfig.json create mode 100644 src/typings/OSSREADME.json create mode 100644 src/typings/ansi-regex.d.ts create mode 100644 src/typings/applicationInsights.d.ts create mode 100644 src/typings/bootstrap.d.ts create mode 100644 src/typings/chokidar.d.ts create mode 100644 src/typings/comment-json.d.ts create mode 100644 src/typings/electron.d.ts create mode 100644 src/typings/error-ex.d.ts create mode 100644 src/typings/fast-plist.d.ts create mode 100644 src/typings/gc-signals.d.ts create mode 100644 src/typings/getmac.d.ts create mode 100644 src/typings/globals/core-js/index.d.ts create mode 100644 src/typings/globals/core-js/typings.json create mode 100644 src/typings/globals/jqueryui/index.d.ts create mode 100644 src/typings/globals/jqueryui/typings.json create mode 100644 src/typings/globals/slickgrid/index.d.ts create mode 100644 src/typings/globals/slickgrid/typings.json create mode 100644 src/typings/globals/systemjs/index.d.ts create mode 100644 src/typings/globals/systemjs/typings.json create mode 100644 src/typings/globals/typemoq/index.d.ts create mode 100644 src/typings/globals/underscore/index.d.ts create mode 100644 src/typings/globals/underscore/typings.json create mode 100644 src/typings/globals/zone.js/index.d.ts create mode 100644 src/typings/globals/zone.js/typings.json create mode 100644 src/typings/graceful-fs.d.ts create mode 100644 src/typings/http-proxy-agent.d.ts create mode 100644 src/typings/https-proxy-agent.d.ts create mode 100644 src/typings/iconv-lite.d.ts create mode 100644 src/typings/index.d.ts create mode 100644 src/typings/jQuery.d.ts create mode 100644 src/typings/jschardet.d.ts create mode 100644 src/typings/keytar.d.ts create mode 100644 src/typings/lib.array-ext.d.ts create mode 100644 src/typings/minimist.d.ts create mode 100644 src/typings/mocha.d.ts create mode 100644 src/typings/modules/@angular/common/index.d.ts create mode 100644 src/typings/modules/@angular/common/typings.json create mode 100644 src/typings/modules/@angular/compiler/index.d.ts create mode 100644 src/typings/modules/@angular/compiler/typings.json create mode 100644 src/typings/modules/@angular/core/index.d.ts create mode 100644 src/typings/modules/@angular/core/typings.json create mode 100644 src/typings/modules/@angular/forms/index.d.ts create mode 100644 src/typings/modules/@angular/forms/typings.json create mode 100644 src/typings/modules/@angular/platform-browser-dynamic/index.d.ts create mode 100644 src/typings/modules/@angular/platform-browser-dynamic/typings.json create mode 100644 src/typings/modules/@angular/platform-browser/index.d.ts create mode 100644 src/typings/modules/@angular/platform-browser/typings.json create mode 100644 src/typings/modules/@angular/router/index.d.ts create mode 100644 src/typings/modules/@angular/router/typings.json create mode 100644 src/typings/modules/angular2-grid/index.d.ts create mode 100644 src/typings/modules/angular2-grid/typings.json create mode 100644 src/typings/modules/angular2-slickgrid/index.d.ts create mode 100644 src/typings/modules/angular2-slickgrid/typings.json create mode 100644 src/typings/modules/html-query-plan/index.d.ts create mode 100644 src/typings/modules/html-query-plan/typings.json create mode 100644 src/typings/modules/ng2-charts/index.d.ts create mode 100644 src/typings/modules/ng2-charts/typings.json create mode 100644 src/typings/modules/rxjs/index.d.ts create mode 100644 src/typings/modules/rxjs/typings.json create mode 100644 src/typings/native-keymap.d.ts create mode 100644 src/typings/native-watchdog.d.ts create mode 100644 src/typings/node-pty.d.ts create mode 100644 src/typings/node.d.ts create mode 100644 src/typings/node.processEnv-ext.d.ts create mode 100644 src/typings/nsfw.d.ts create mode 100644 src/typings/original-fs.d.ts create mode 100644 src/typings/prettyData.d.ts create mode 100644 src/typings/pty.js.d.ts create mode 100644 src/typings/rangy.d.ts create mode 100644 src/typings/require.d.ts create mode 100644 src/typings/semver.d.ts create mode 100644 src/typings/sinon.d.ts create mode 100644 src/typings/splash.d.ts create mode 100644 src/typings/thenable.d.ts create mode 100644 src/typings/vscode-ripgrep.d.ts create mode 100644 src/typings/vscode-textmate.d.ts create mode 100644 src/typings/windows-foreground-love.d.ts create mode 100644 src/typings/windows-mutex.ts create mode 100644 src/typings/windows-process-tree.d.ts create mode 100644 src/typings/winreg.d.ts create mode 100644 src/typings/xterm.d.ts create mode 100644 src/typings/yauzl.d.ts create mode 100644 src/vs/base/browser/browser.ts create mode 100644 src/vs/base/browser/builder.css create mode 100644 src/vs/base/browser/builder.ts create mode 100644 src/vs/base/browser/dnd.ts create mode 100644 src/vs/base/browser/dom.ts create mode 100644 src/vs/base/browser/event.ts create mode 100644 src/vs/base/browser/fastDomNode.ts create mode 100644 src/vs/base/browser/globalMouseMoveMonitor.ts create mode 100644 src/vs/base/browser/htmlContentRenderer.ts create mode 100644 src/vs/base/browser/iframe.ts create mode 100644 src/vs/base/browser/keyboardEvent.ts create mode 100644 src/vs/base/browser/mouseEvent.ts create mode 100644 src/vs/base/browser/touch.ts create mode 100644 src/vs/base/browser/ui/actionbar/actionbar.css create mode 100644 src/vs/base/browser/ui/actionbar/actionbar.ts create mode 100644 src/vs/base/browser/ui/aria/aria.css create mode 100644 src/vs/base/browser/ui/aria/aria.ts create mode 100644 src/vs/base/browser/ui/button/button.css create mode 100644 src/vs/base/browser/ui/button/button.ts create mode 100644 src/vs/base/browser/ui/checkbox/checkbox.css create mode 100644 src/vs/base/browser/ui/checkbox/checkbox.ts create mode 100644 src/vs/base/browser/ui/contextview/contextview.css create mode 100644 src/vs/base/browser/ui/contextview/contextview.ts create mode 100644 src/vs/base/browser/ui/countBadge/countBadge.css create mode 100644 src/vs/base/browser/ui/countBadge/countBadge.ts create mode 100644 src/vs/base/browser/ui/dropdown/dropdown.css create mode 100644 src/vs/base/browser/ui/dropdown/dropdown.ts create mode 100644 src/vs/base/browser/ui/dropdown/linksDropdown.ts create mode 100644 src/vs/base/browser/ui/findinput/case-sensitive-dark.svg create mode 100644 src/vs/base/browser/ui/findinput/case-sensitive.svg create mode 100644 src/vs/base/browser/ui/findinput/findInput.css create mode 100644 src/vs/base/browser/ui/findinput/findInput.ts create mode 100644 src/vs/base/browser/ui/findinput/findInputCheckboxes.css create mode 100644 src/vs/base/browser/ui/findinput/findInputCheckboxes.ts create mode 100644 src/vs/base/browser/ui/findinput/regex-dark.svg create mode 100644 src/vs/base/browser/ui/findinput/regex.svg create mode 100644 src/vs/base/browser/ui/findinput/whole-word-dark.svg create mode 100644 src/vs/base/browser/ui/findinput/whole-word.svg create mode 100644 src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts create mode 100644 src/vs/base/browser/ui/iconLabel/iconLabel.ts create mode 100644 src/vs/base/browser/ui/iconLabel/iconlabel.css create mode 100644 src/vs/base/browser/ui/inputbox/inputBox.css create mode 100644 src/vs/base/browser/ui/inputbox/inputBox.ts create mode 100644 src/vs/base/browser/ui/keybindingLabel/keybindingLabel.css create mode 100644 src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts create mode 100644 src/vs/base/browser/ui/list/list.css create mode 100644 src/vs/base/browser/ui/list/list.ts create mode 100644 src/vs/base/browser/ui/list/listPaging.ts create mode 100644 src/vs/base/browser/ui/list/listView.ts create mode 100644 src/vs/base/browser/ui/list/listWidget.ts create mode 100644 src/vs/base/browser/ui/list/rangeMap.ts create mode 100644 src/vs/base/browser/ui/list/rowCache.ts create mode 100644 src/vs/base/browser/ui/menu/menu.css create mode 100644 src/vs/base/browser/ui/menu/menu.ts create mode 100644 src/vs/base/browser/ui/octiconLabel/octiconLabel.mock.ts create mode 100644 src/vs/base/browser/ui/octiconLabel/octiconLabel.ts create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/OSSREADME.json create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/README.md create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons-animations.css create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons-local.ttf create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons.css create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons.eot create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons.less create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons.scss create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons.woff create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/sprockets-octicons.scss create mode 100644 src/vs/base/browser/ui/progressbar/progressbar.css create mode 100644 src/vs/base/browser/ui/progressbar/progressbar.ts create mode 100644 src/vs/base/browser/ui/resourceviewer/resourceViewer.ts create mode 100644 src/vs/base/browser/ui/resourceviewer/resourceviewer.css create mode 100644 src/vs/base/browser/ui/sash/sash.css create mode 100644 src/vs/base/browser/ui/sash/sash.ts create mode 100644 src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts create mode 100644 src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts create mode 100644 src/vs/base/browser/ui/scrollbar/media/arrow-down-dark.svg create mode 100644 src/vs/base/browser/ui/scrollbar/media/arrow-down.svg create mode 100644 src/vs/base/browser/ui/scrollbar/media/arrow-left-dark.svg create mode 100644 src/vs/base/browser/ui/scrollbar/media/arrow-left.svg create mode 100644 src/vs/base/browser/ui/scrollbar/media/arrow-right-dark.svg create mode 100644 src/vs/base/browser/ui/scrollbar/media/arrow-right.svg create mode 100644 src/vs/base/browser/ui/scrollbar/media/arrow-up-dark.svg create mode 100644 src/vs/base/browser/ui/scrollbar/media/arrow-up.svg create mode 100644 src/vs/base/browser/ui/scrollbar/media/scrollbars.css create mode 100644 src/vs/base/browser/ui/scrollbar/scrollableElement.ts create mode 100644 src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts create mode 100644 src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts create mode 100644 src/vs/base/browser/ui/scrollbar/scrollbarState.ts create mode 100644 src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts create mode 100644 src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts create mode 100644 src/vs/base/browser/ui/selectBox/selectBox.css create mode 100644 src/vs/base/browser/ui/selectBox/selectBox.ts create mode 100644 src/vs/base/browser/ui/splitview/arrow-collapse-dark.svg create mode 100644 src/vs/base/browser/ui/splitview/arrow-collapse.svg create mode 100644 src/vs/base/browser/ui/splitview/arrow-expand-dark.svg create mode 100644 src/vs/base/browser/ui/splitview/arrow-expand.svg create mode 100644 src/vs/base/browser/ui/splitview/splitview.css create mode 100644 src/vs/base/browser/ui/splitview/splitview.ts create mode 100644 src/vs/base/browser/ui/toolbar/ellipsis-inverse.svg create mode 100644 src/vs/base/browser/ui/toolbar/ellipsis.svg create mode 100644 src/vs/base/browser/ui/toolbar/toolbar.css create mode 100644 src/vs/base/browser/ui/toolbar/toolbar.ts create mode 100644 src/vs/base/browser/ui/widget.ts create mode 100644 src/vs/base/common/OSSREADME.json create mode 100644 src/vs/base/common/actions.ts create mode 100644 src/vs/base/common/arrays.ts create mode 100644 src/vs/base/common/assert.ts create mode 100644 src/vs/base/common/async.ts create mode 100644 src/vs/base/common/buildunit.json create mode 100644 src/vs/base/common/cache.ts create mode 100644 src/vs/base/common/callbackList.ts create mode 100644 src/vs/base/common/cancellation.ts create mode 100644 src/vs/base/common/charCode.ts create mode 100644 src/vs/base/common/collections.ts create mode 100644 src/vs/base/common/color.ts create mode 100644 src/vs/base/common/comparers.ts create mode 100644 src/vs/base/common/decorators.ts create mode 100644 src/vs/base/common/diagnostics.ts create mode 100644 src/vs/base/common/diff/diff.ts create mode 100644 src/vs/base/common/diff/diff2.ts create mode 100644 src/vs/base/common/diff/diffChange.ts create mode 100644 src/vs/base/common/errorMessage.ts create mode 100644 src/vs/base/common/errors.ts create mode 100644 src/vs/base/common/event.ts create mode 100644 src/vs/base/common/eventEmitter.ts create mode 100644 src/vs/base/common/events.ts create mode 100644 src/vs/base/common/filters.ts create mode 100644 src/vs/base/common/functional.ts create mode 100644 src/vs/base/common/glob.ts create mode 100644 src/vs/base/common/graph.ts create mode 100644 src/vs/base/common/hash.ts create mode 100644 src/vs/base/common/history.ts create mode 100644 src/vs/base/common/htmlContent.ts create mode 100644 src/vs/base/common/idGenerator.ts create mode 100644 src/vs/base/common/iterator.ts create mode 100644 src/vs/base/common/json.ts create mode 100644 src/vs/base/common/jsonEdit.ts create mode 100644 src/vs/base/common/jsonErrorMessages.ts create mode 100644 src/vs/base/common/jsonFormatter.ts create mode 100644 src/vs/base/common/jsonSchema.ts create mode 100644 src/vs/base/common/keyCodes.ts create mode 100644 src/vs/base/common/keybindingLabels.ts create mode 100644 src/vs/base/common/labels.ts create mode 100644 src/vs/base/common/lifecycle.ts create mode 100644 src/vs/base/common/map.ts create mode 100644 src/vs/base/common/marked/OSSREADME.json create mode 100644 src/vs/base/common/marked/marked.d.ts create mode 100644 src/vs/base/common/marked/marked.js create mode 100644 src/vs/base/common/marked/marked.license.txt create mode 100644 src/vs/base/common/marked/raw.marked.js create mode 100644 src/vs/base/common/marshalling.ts create mode 100644 src/vs/base/common/mime.ts create mode 100644 src/vs/base/common/network.ts create mode 100644 src/vs/base/common/numbers.ts create mode 100644 src/vs/base/common/objects.ts create mode 100644 src/vs/base/common/paging.ts create mode 100644 src/vs/base/common/parsers.ts create mode 100644 src/vs/base/common/paths.ts create mode 100644 src/vs/base/common/platform.ts create mode 100644 src/vs/base/common/processes.ts create mode 100644 src/vs/base/common/scorer.ts create mode 100644 src/vs/base/common/scrollable.ts create mode 100644 src/vs/base/common/severity.ts create mode 100644 src/vs/base/common/stopwatch.ts create mode 100644 src/vs/base/common/strings.ts create mode 100644 src/vs/base/common/types.ts create mode 100644 src/vs/base/common/uri.ts create mode 100644 src/vs/base/common/uuid.ts create mode 100644 src/vs/base/common/winjs.base.d.ts create mode 100644 src/vs/base/common/winjs.base.js create mode 100644 src/vs/base/common/winjs.base.raw.js create mode 100644 src/vs/base/common/worker/simpleWorker.ts create mode 100644 src/vs/base/node/config.ts create mode 100644 src/vs/base/node/crypto.ts create mode 100644 src/vs/base/node/decoder.ts create mode 100644 src/vs/base/node/encoding.ts create mode 100644 src/vs/base/node/event.ts create mode 100644 src/vs/base/node/extfs.ts create mode 100644 src/vs/base/node/flow.ts create mode 100644 src/vs/base/node/id.ts create mode 100644 src/vs/base/node/mime.ts create mode 100644 src/vs/base/node/paths.ts create mode 100644 src/vs/base/node/pfs.ts create mode 100644 src/vs/base/node/ports.ts create mode 100644 src/vs/base/node/processes.ts create mode 100644 src/vs/base/node/profiler.ts create mode 100644 src/vs/base/node/proxy.ts create mode 100644 src/vs/base/node/request.ts create mode 100644 src/vs/base/node/startupTimers.d.ts create mode 100644 src/vs/base/node/startupTimers.js create mode 100644 src/vs/base/node/stdFork.ts create mode 100644 src/vs/base/node/stdForkStart.js create mode 100644 src/vs/base/node/stream.ts create mode 100644 src/vs/base/node/terminateProcess.sh create mode 100644 src/vs/base/node/zip.ts create mode 100644 src/vs/base/parts/ipc/common/ipc.electron.ts create mode 100644 src/vs/base/parts/ipc/common/ipc.ts create mode 100644 src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts create mode 100644 src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts create mode 100644 src/vs/base/parts/ipc/node/ipc.cp.ts create mode 100644 src/vs/base/parts/ipc/node/ipc.net.ts create mode 100644 src/vs/base/parts/ipc/test/node/ipc.net.test.ts create mode 100644 src/vs/base/parts/ipc/test/node/ipc.perf.ts create mode 100644 src/vs/base/parts/ipc/test/node/ipc.test.ts create mode 100644 src/vs/base/parts/ipc/test/node/testApp.ts create mode 100644 src/vs/base/parts/ipc/test/node/testService.ts create mode 100644 src/vs/base/parts/quickopen/browser/quickOpenModel.ts create mode 100644 src/vs/base/parts/quickopen/browser/quickOpenViewer.ts create mode 100644 src/vs/base/parts/quickopen/browser/quickOpenWidget.ts create mode 100644 src/vs/base/parts/quickopen/browser/quickopen.css create mode 100644 src/vs/base/parts/quickopen/common/quickOpen.ts create mode 100644 src/vs/base/parts/quickopen/test/browser/quickopen.test.ts create mode 100644 src/vs/base/parts/tree/browser/CollapseAll.svg create mode 100644 src/vs/base/parts/tree/browser/CollapseAll_inverse.svg create mode 100644 src/vs/base/parts/tree/browser/collapsed-dark.svg create mode 100644 src/vs/base/parts/tree/browser/collapsed-hc.svg create mode 100644 src/vs/base/parts/tree/browser/collapsed.svg create mode 100644 src/vs/base/parts/tree/browser/expanded-dark.svg create mode 100644 src/vs/base/parts/tree/browser/expanded-hc.svg create mode 100644 src/vs/base/parts/tree/browser/expanded.svg create mode 100644 src/vs/base/parts/tree/browser/loading-dark.svg create mode 100644 src/vs/base/parts/tree/browser/loading-hc.svg create mode 100644 src/vs/base/parts/tree/browser/loading.svg create mode 100644 src/vs/base/parts/tree/browser/tree.css create mode 100644 src/vs/base/parts/tree/browser/tree.ts create mode 100644 src/vs/base/parts/tree/browser/treeDefaults.ts create mode 100644 src/vs/base/parts/tree/browser/treeDnd.ts create mode 100644 src/vs/base/parts/tree/browser/treeImpl.ts create mode 100644 src/vs/base/parts/tree/browser/treeModel.ts create mode 100644 src/vs/base/parts/tree/browser/treeView.ts create mode 100644 src/vs/base/parts/tree/browser/treeViewModel.ts create mode 100644 src/vs/base/parts/tree/test/browser/treeModel.test.ts create mode 100644 src/vs/base/parts/tree/test/browser/treeViewModel.test.ts create mode 100644 src/vs/base/test/browser/browser.test.ts create mode 100644 src/vs/base/test/browser/builder.test.ts create mode 100644 src/vs/base/test/browser/comparers.test.ts create mode 100644 src/vs/base/test/browser/dom.test.ts create mode 100644 src/vs/base/test/browser/highlightedLabel.test.ts create mode 100644 src/vs/base/test/browser/htmlContent.test.ts create mode 100644 src/vs/base/test/browser/progressBar.test.ts create mode 100644 src/vs/base/test/browser/ui/list/rangeMap.test.ts create mode 100644 src/vs/base/test/browser/ui/scrollbar/scrollableElement.test.ts create mode 100644 src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts create mode 100644 src/vs/base/test/common/actions.test.ts create mode 100644 src/vs/base/test/common/arrays.test.ts create mode 100644 src/vs/base/test/common/assert.test.ts create mode 100644 src/vs/base/test/common/async.test.ts create mode 100644 src/vs/base/test/common/cache.test.ts create mode 100644 src/vs/base/test/common/cancellation.test.ts create mode 100644 src/vs/base/test/common/charCode.test.ts create mode 100644 src/vs/base/test/common/collections.test.ts create mode 100644 src/vs/base/test/common/color.test.ts create mode 100644 src/vs/base/test/common/decorators.test.ts create mode 100644 src/vs/base/test/common/diff/diff.test.ts create mode 100644 src/vs/base/test/common/errors.test.ts create mode 100644 src/vs/base/test/common/event.test.ts create mode 100644 src/vs/base/test/common/eventEmitter.test.ts create mode 100644 src/vs/base/test/common/filters.perf.data.d.ts create mode 100644 src/vs/base/test/common/filters.perf.data.js create mode 100644 src/vs/base/test/common/filters.perf.test.ts create mode 100644 src/vs/base/test/common/filters.test.ts create mode 100644 src/vs/base/test/common/graph.test.ts create mode 100644 src/vs/base/test/common/hash.test.ts create mode 100644 src/vs/base/test/common/history.test.ts create mode 100644 src/vs/base/test/common/json.test.ts create mode 100644 src/vs/base/test/common/jsonEdit.test.ts create mode 100644 src/vs/base/test/common/jsonFormatter.test.ts create mode 100644 src/vs/base/test/common/keyCodes.test.ts create mode 100644 src/vs/base/test/common/labels.test.ts create mode 100644 src/vs/base/test/common/lifecycle.test.ts create mode 100644 src/vs/base/test/common/map.test.ts create mode 100644 src/vs/base/test/common/marshalling.test.ts create mode 100644 src/vs/base/test/common/mime.test.ts create mode 100644 src/vs/base/test/common/network.test.ts create mode 100644 src/vs/base/test/common/objects.test.ts create mode 100644 src/vs/base/test/common/paging.test.ts create mode 100644 src/vs/base/test/common/paths.test.ts create mode 100644 src/vs/base/test/common/scorer.test.ts create mode 100644 src/vs/base/test/common/scrollable.test.ts create mode 100644 src/vs/base/test/common/strings.test.ts create mode 100644 src/vs/base/test/common/types.test.ts create mode 100644 src/vs/base/test/common/uri.test.ts create mode 100644 src/vs/base/test/common/utils.ts create mode 100644 src/vs/base/test/common/uuid.test.ts create mode 100644 src/vs/base/test/node/config.test.ts create mode 100644 src/vs/base/test/node/decoder.test.ts create mode 100644 src/vs/base/test/node/encoding/encoding.test.ts create mode 100644 src/vs/base/test/node/encoding/fixtures/empty.txt create mode 100644 src/vs/base/test/node/encoding/fixtures/some_ansi.css create mode 100644 src/vs/base/test/node/encoding/fixtures/some_utf16be.css create mode 100644 src/vs/base/test/node/encoding/fixtures/some_utf16le.css create mode 100644 src/vs/base/test/node/encoding/fixtures/some_utf8.css create mode 100644 src/vs/base/test/node/extfs/extfs.test.ts create mode 100644 src/vs/base/test/node/extfs/fixtures/examples/company.jxs create mode 100644 src/vs/base/test/node/extfs/fixtures/examples/conway.jxs create mode 100644 src/vs/base/test/node/extfs/fixtures/examples/employee.jxs create mode 100644 src/vs/base/test/node/extfs/fixtures/examples/small.jxs create mode 100644 src/vs/base/test/node/extfs/fixtures/index.html create mode 100644 src/vs/base/test/node/extfs/fixtures/site.css create mode 100644 src/vs/base/test/node/flow.test.ts create mode 100644 src/vs/base/test/node/glob.test.ts create mode 100644 src/vs/base/test/node/mime/fixtures/some.cp1252.txt create mode 100644 src/vs/base/test/node/mime/fixtures/some.css.qwoff create mode 100644 src/vs/base/test/node/mime/fixtures/some.json.png create mode 100644 src/vs/base/test/node/mime/fixtures/some.pdf create mode 100644 src/vs/base/test/node/mime/fixtures/some.png.txt create mode 100644 src/vs/base/test/node/mime/fixtures/some.qwoff.txt create mode 100644 src/vs/base/test/node/mime/fixtures/some.shiftjis.txt create mode 100644 src/vs/base/test/node/mime/fixtures/some.xml.png create mode 100644 src/vs/base/test/node/mime/mime.test.ts create mode 100644 src/vs/base/test/node/pfs.test.ts create mode 100644 src/vs/base/test/node/port.test.ts create mode 100644 src/vs/base/test/node/processes/fixtures/fork.ts create mode 100644 src/vs/base/test/node/processes/fixtures/fork_large.ts create mode 100644 src/vs/base/test/node/processes/processes.test.ts create mode 100644 src/vs/base/test/node/stream/fixtures/empty.txt create mode 100644 src/vs/base/test/node/stream/fixtures/file.css create mode 100644 src/vs/base/test/node/stream/stream.test.ts create mode 100644 src/vs/base/test/node/zip/fixtures/extract.zip create mode 100644 src/vs/base/test/node/zip/zip.test.ts create mode 100644 src/vs/base/worker/defaultWorkerFactory.ts create mode 100644 src/vs/base/worker/workerMain.ts create mode 100644 src/vs/buildunit.json create mode 100644 src/vs/code/buildfile.js create mode 100644 src/vs/code/electron-browser/sharedProcess.html create mode 100644 src/vs/code/electron-browser/sharedProcess.js create mode 100644 src/vs/code/electron-browser/sharedProcessMain.ts create mode 100644 src/vs/code/electron-main/app.ts create mode 100644 src/vs/code/electron-main/auth.html create mode 100644 src/vs/code/electron-main/auth.ts create mode 100644 src/vs/code/electron-main/keyboard.ts create mode 100644 src/vs/code/electron-main/launch.ts create mode 100644 src/vs/code/electron-main/main.ts create mode 100644 src/vs/code/electron-main/menus.ts create mode 100644 src/vs/code/electron-main/sharedProcess.ts create mode 100644 src/vs/code/electron-main/window.ts create mode 100644 src/vs/code/electron-main/windows.ts create mode 100644 src/vs/code/node/cli.ts create mode 100644 src/vs/code/node/cliProcessMain.ts create mode 100644 src/vs/code/node/paths.ts create mode 100644 src/vs/code/node/shellEnv.ts create mode 100644 src/vs/code/node/windowsFinder.ts create mode 100644 src/vs/code/test/node/argv.test.ts create mode 100644 src/vs/code/test/node/fixtures/no_vscode_folder/file.txt create mode 100644 src/vs/code/test/node/fixtures/vscode_folder/_vscode/keepfolder.txt create mode 100644 src/vs/code/test/node/fixtures/vscode_folder/file.txt create mode 100644 src/vs/code/test/node/fixtures/vscode_folder/nested_vscode_folder/_vscode/keepfolder.txt create mode 100644 src/vs/code/test/node/fixtures/vscode_home_folder/_vscode/settings.json create mode 100644 src/vs/code/test/node/fixtures/vscode_home_folder/file.txt create mode 100644 src/vs/code/test/node/windowsFinder.test.ts create mode 100644 src/vs/css.build.js create mode 100644 src/vs/css.d.ts create mode 100644 src/vs/css.js create mode 100644 src/vs/editor/browser/codeEditor.ts create mode 100644 src/vs/editor/browser/config/charWidthReader.ts create mode 100644 src/vs/editor/browser/config/configuration.ts create mode 100644 src/vs/editor/browser/config/elementSizeObserver.ts create mode 100644 src/vs/editor/browser/controller/mouseHandler.ts create mode 100644 src/vs/editor/browser/controller/mouseTarget.ts create mode 100644 src/vs/editor/browser/controller/pointerHandler.ts create mode 100644 src/vs/editor/browser/controller/textAreaHandler.css create mode 100644 src/vs/editor/browser/controller/textAreaHandler.ts create mode 100644 src/vs/editor/browser/controller/textAreaInput.ts create mode 100644 src/vs/editor/browser/controller/textAreaState.ts create mode 100644 src/vs/editor/browser/editorBrowser.ts create mode 100644 src/vs/editor/browser/editorBrowserExtensions.ts create mode 100644 src/vs/editor/browser/editorDom.ts create mode 100644 src/vs/editor/browser/services/codeEditorServiceImpl.ts create mode 100644 src/vs/editor/browser/view/dynamicViewOverlay.ts create mode 100644 src/vs/editor/browser/view/viewController.ts create mode 100644 src/vs/editor/browser/view/viewImpl.ts create mode 100644 src/vs/editor/browser/view/viewLayer.ts create mode 100644 src/vs/editor/browser/view/viewOutgoingEvents.ts create mode 100644 src/vs/editor/browser/view/viewOverlays.ts create mode 100644 src/vs/editor/browser/view/viewPart.ts create mode 100644 src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts create mode 100644 src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.css create mode 100644 src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts create mode 100644 src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.css create mode 100644 src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts create mode 100644 src/vs/editor/browser/viewParts/decorations/decorations.css create mode 100644 src/vs/editor/browser/viewParts/decorations/decorations.ts create mode 100644 src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts create mode 100644 src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css create mode 100644 src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts create mode 100644 src/vs/editor/browser/viewParts/indentGuides/indentGuides.css create mode 100644 src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts create mode 100644 src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg create mode 100644 src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac-2x.svg create mode 100644 src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac.svg create mode 100644 src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor.svg create mode 100644 src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css create mode 100644 src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts create mode 100644 src/vs/editor/browser/viewParts/lines/rangeUtil.ts create mode 100644 src/vs/editor/browser/viewParts/lines/viewLine.ts create mode 100644 src/vs/editor/browser/viewParts/lines/viewLines.css create mode 100644 src/vs/editor/browser/viewParts/lines/viewLines.ts create mode 100644 src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.css create mode 100644 src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts create mode 100644 src/vs/editor/browser/viewParts/margin/margin.ts create mode 100644 src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.css create mode 100644 src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts create mode 100644 src/vs/editor/browser/viewParts/minimap/minimap.css create mode 100644 src/vs/editor/browser/viewParts/minimap/minimap.ts create mode 100644 src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.css create mode 100644 src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts create mode 100644 src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts create mode 100644 src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts create mode 100644 src/vs/editor/browser/viewParts/overviewRuler/overviewRulerImpl.ts create mode 100644 src/vs/editor/browser/viewParts/rulers/rulers.css create mode 100644 src/vs/editor/browser/viewParts/rulers/rulers.ts create mode 100644 src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.css create mode 100644 src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts create mode 100644 src/vs/editor/browser/viewParts/selections/selections.css create mode 100644 src/vs/editor/browser/viewParts/selections/selections.ts create mode 100644 src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts create mode 100644 src/vs/editor/browser/viewParts/viewCursors/viewCursors.css create mode 100644 src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts create mode 100644 src/vs/editor/browser/viewParts/viewZones/viewZones.ts create mode 100644 src/vs/editor/browser/widget/codeEditorWidget.ts create mode 100644 src/vs/editor/browser/widget/diffEditorWidget.ts create mode 100644 src/vs/editor/browser/widget/diffNavigator.ts create mode 100644 src/vs/editor/browser/widget/diffReview.ts create mode 100644 src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts create mode 100644 src/vs/editor/browser/widget/media/addition-inverse.svg create mode 100644 src/vs/editor/browser/widget/media/addition.svg create mode 100644 src/vs/editor/browser/widget/media/close-inverse.svg create mode 100644 src/vs/editor/browser/widget/media/close.svg create mode 100644 src/vs/editor/browser/widget/media/deletion-inverse.svg create mode 100644 src/vs/editor/browser/widget/media/deletion.svg create mode 100644 src/vs/editor/browser/widget/media/diagonal-fill.png create mode 100644 src/vs/editor/browser/widget/media/diffEditor.css create mode 100644 src/vs/editor/browser/widget/media/diffReview.css create mode 100644 src/vs/editor/browser/widget/media/editor.css create mode 100644 src/vs/editor/browser/widget/media/tokens.css create mode 100644 src/vs/editor/common/commands/replaceCommand.ts create mode 100644 src/vs/editor/common/commands/shiftCommand.ts create mode 100644 src/vs/editor/common/commands/surroundSelectionCommand.ts create mode 100644 src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts create mode 100644 src/vs/editor/common/commonCodeEditor.ts create mode 100644 src/vs/editor/common/config/commonEditorConfig.ts create mode 100644 src/vs/editor/common/config/editorOptions.ts create mode 100644 src/vs/editor/common/config/editorZoom.ts create mode 100644 src/vs/editor/common/config/fontInfo.ts create mode 100644 src/vs/editor/common/controller/coreCommands.ts create mode 100644 src/vs/editor/common/controller/cursor.ts create mode 100644 src/vs/editor/common/controller/cursorCollection.ts create mode 100644 src/vs/editor/common/controller/cursorColumnSelection.ts create mode 100644 src/vs/editor/common/controller/cursorCommon.ts create mode 100644 src/vs/editor/common/controller/cursorDeleteOperations.ts create mode 100644 src/vs/editor/common/controller/cursorEvents.ts create mode 100644 src/vs/editor/common/controller/cursorMoveCommands.ts create mode 100644 src/vs/editor/common/controller/cursorMoveOperations.ts create mode 100644 src/vs/editor/common/controller/cursorTypeOperations.ts create mode 100644 src/vs/editor/common/controller/cursorWordOperations.ts create mode 100644 src/vs/editor/common/controller/oneCursor.ts create mode 100644 src/vs/editor/common/controller/wordCharacterClassifier.ts create mode 100644 src/vs/editor/common/core/characterClassifier.ts create mode 100644 src/vs/editor/common/core/editOperation.ts create mode 100644 src/vs/editor/common/core/editorState.ts create mode 100644 src/vs/editor/common/core/lineTokens.ts create mode 100644 src/vs/editor/common/core/position.ts create mode 100644 src/vs/editor/common/core/range.ts create mode 100644 src/vs/editor/common/core/rgba.ts create mode 100644 src/vs/editor/common/core/selection.ts create mode 100644 src/vs/editor/common/core/stringBuilder.ts create mode 100644 src/vs/editor/common/core/token.ts create mode 100644 src/vs/editor/common/core/uint.ts create mode 100644 src/vs/editor/common/core/viewLineToken.ts create mode 100644 src/vs/editor/common/diff/diffComputer.ts create mode 100644 src/vs/editor/common/diff/nullDiffComputer.ts create mode 100644 src/vs/editor/common/editorAction.ts create mode 100644 src/vs/editor/common/editorCommon.ts create mode 100644 src/vs/editor/common/editorCommonExtensions.ts create mode 100644 src/vs/editor/common/editorContextKeys.ts create mode 100644 src/vs/editor/common/model/editStack.ts create mode 100644 src/vs/editor/common/model/editableTextModel.ts create mode 100644 src/vs/editor/common/model/indentRanges.ts create mode 100644 src/vs/editor/common/model/indentationGuesser.ts create mode 100644 src/vs/editor/common/model/mirrorModel.ts create mode 100644 src/vs/editor/common/model/model.ts create mode 100644 src/vs/editor/common/model/modelLine.ts create mode 100644 src/vs/editor/common/model/textModel.ts create mode 100644 src/vs/editor/common/model/textModelEvents.ts create mode 100644 src/vs/editor/common/model/textModelSearch.ts create mode 100644 src/vs/editor/common/model/textModelWithDecorations.ts create mode 100644 src/vs/editor/common/model/textModelWithMarkers.ts create mode 100644 src/vs/editor/common/model/textModelWithTokens.ts create mode 100644 src/vs/editor/common/model/textSource.ts create mode 100644 src/vs/editor/common/model/tokensBinaryEncoding.ts create mode 100644 src/vs/editor/common/model/wordHelper.ts create mode 100644 src/vs/editor/common/modes.ts create mode 100644 src/vs/editor/common/modes/abstractMode.ts create mode 100644 src/vs/editor/common/modes/editorModeContext.ts create mode 100644 src/vs/editor/common/modes/languageConfiguration.ts create mode 100644 src/vs/editor/common/modes/languageConfigurationRegistry.ts create mode 100644 src/vs/editor/common/modes/languageFeatureRegistry.ts create mode 100644 src/vs/editor/common/modes/languageSelector.ts create mode 100644 src/vs/editor/common/modes/linkComputer.ts create mode 100644 src/vs/editor/common/modes/modesRegistry.ts create mode 100644 src/vs/editor/common/modes/nullMode.ts create mode 100644 src/vs/editor/common/modes/supports.ts create mode 100644 src/vs/editor/common/modes/supports/characterPair.ts create mode 100644 src/vs/editor/common/modes/supports/electricCharacter.ts create mode 100644 src/vs/editor/common/modes/supports/indentRules.ts create mode 100644 src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts create mode 100644 src/vs/editor/common/modes/supports/onEnter.ts create mode 100644 src/vs/editor/common/modes/supports/richEditBrackets.ts create mode 100644 src/vs/editor/common/modes/supports/tokenization.ts create mode 100644 src/vs/editor/common/modes/textToHtmlTokenizer.ts create mode 100644 src/vs/editor/common/modes/tokenizationRegistry.ts create mode 100644 src/vs/editor/common/services/abstractCodeEditorService.ts create mode 100644 src/vs/editor/common/services/bulkEdit.ts create mode 100644 src/vs/editor/common/services/codeEditorService.ts create mode 100644 src/vs/editor/common/services/editorSimpleWorker.ts create mode 100644 src/vs/editor/common/services/editorWorkerService.ts create mode 100644 src/vs/editor/common/services/editorWorkerServiceImpl.ts create mode 100644 src/vs/editor/common/services/languagesRegistry.ts create mode 100644 src/vs/editor/common/services/modeService.ts create mode 100644 src/vs/editor/common/services/modeServiceImpl.ts create mode 100644 src/vs/editor/common/services/modelService.ts create mode 100644 src/vs/editor/common/services/modelServiceImpl.ts create mode 100644 src/vs/editor/common/services/resolverService.ts create mode 100644 src/vs/editor/common/services/resourceConfiguration.ts create mode 100644 src/vs/editor/common/services/resourceConfigurationImpl.ts create mode 100644 src/vs/editor/common/services/webWorker.ts create mode 100644 src/vs/editor/common/standalone/standaloneBase.ts create mode 100644 src/vs/editor/common/view/editorColorRegistry.ts create mode 100644 src/vs/editor/common/view/minimapCharRenderer.ts create mode 100644 src/vs/editor/common/view/overviewZoneManager.ts create mode 100644 src/vs/editor/common/view/renderingContext.ts create mode 100644 src/vs/editor/common/view/runtimeMinimapCharRenderer.ts create mode 100644 src/vs/editor/common/view/viewContext.ts create mode 100644 src/vs/editor/common/view/viewEventDispatcher.ts create mode 100644 src/vs/editor/common/view/viewEvents.ts create mode 100644 src/vs/editor/common/viewLayout/lineDecorations.ts create mode 100644 src/vs/editor/common/viewLayout/linesLayout.ts create mode 100644 src/vs/editor/common/viewLayout/viewLayout.ts create mode 100644 src/vs/editor/common/viewLayout/viewLineRenderer.ts create mode 100644 src/vs/editor/common/viewLayout/viewLinesViewportData.ts create mode 100644 src/vs/editor/common/viewLayout/whitespaceComputer.ts create mode 100644 src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts create mode 100644 src/vs/editor/common/viewModel/prefixSumComputer.ts create mode 100644 src/vs/editor/common/viewModel/splitLinesCollection.ts create mode 100644 src/vs/editor/common/viewModel/viewEventHandler.ts create mode 100644 src/vs/editor/common/viewModel/viewModel.ts create mode 100644 src/vs/editor/common/viewModel/viewModelDecorations.ts create mode 100644 src/vs/editor/common/viewModel/viewModelImpl.ts create mode 100644 src/vs/editor/contrib/bracketMatching/browser/bracketMatching.css create mode 100644 src/vs/editor/contrib/bracketMatching/common/bracketMatching.ts create mode 100644 src/vs/editor/contrib/bracketMatching/test/common/bracketMatching.test.ts create mode 100644 src/vs/editor/contrib/caretOperations/common/caretOperations.ts create mode 100644 src/vs/editor/contrib/caretOperations/common/moveCaretCommand.ts create mode 100644 src/vs/editor/contrib/caretOperations/common/transpose.ts create mode 100644 src/vs/editor/contrib/caretOperations/test/common/moveCarretCommand.test.ts create mode 100644 src/vs/editor/contrib/clipboard/browser/clipboard.css create mode 100644 src/vs/editor/contrib/clipboard/browser/clipboard.ts create mode 100644 src/vs/editor/contrib/codelens/browser/codelens.ts create mode 100644 src/vs/editor/contrib/codelens/browser/codelensController.ts create mode 100644 src/vs/editor/contrib/codelens/browser/codelensWidget.css create mode 100644 src/vs/editor/contrib/codelens/browser/codelensWidget.ts create mode 100644 src/vs/editor/contrib/colorPicker/browser/colorDetector.ts create mode 100644 src/vs/editor/contrib/colorPicker/browser/colorPicker.css create mode 100644 src/vs/editor/contrib/colorPicker/browser/colorPickerModel.ts create mode 100644 src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts create mode 100644 src/vs/editor/contrib/colorPicker/browser/images/opacity-background.png create mode 100644 src/vs/editor/contrib/colorPicker/common/color.ts create mode 100644 src/vs/editor/contrib/colorPicker/common/colorFormatter.ts create mode 100644 src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts create mode 100644 src/vs/editor/contrib/comment/common/blockCommentCommand.ts create mode 100644 src/vs/editor/contrib/comment/common/comment.ts create mode 100644 src/vs/editor/contrib/comment/common/lineCommentCommand.ts create mode 100644 src/vs/editor/contrib/comment/test/common/blockCommentCommand.test.ts create mode 100644 src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts create mode 100644 src/vs/editor/contrib/contextmenu/browser/contextmenu.ts create mode 100644 src/vs/editor/contrib/cursorUndo/browser/cursorUndo.ts create mode 100644 src/vs/editor/contrib/dnd/browser/dnd.css create mode 100644 src/vs/editor/contrib/dnd/browser/dnd.ts create mode 100644 src/vs/editor/contrib/dnd/common/dragAndDropCommand.ts create mode 100644 src/vs/editor/contrib/find/browser/find.ts create mode 100644 src/vs/editor/contrib/find/browser/findOptionsWidget.ts create mode 100644 src/vs/editor/contrib/find/browser/findWidget.css create mode 100644 src/vs/editor/contrib/find/browser/findWidget.ts create mode 100644 src/vs/editor/contrib/find/browser/images/cancelSelectionFind-inverse.svg create mode 100644 src/vs/editor/contrib/find/browser/images/cancelSelectionFind.svg create mode 100644 src/vs/editor/contrib/find/browser/images/close-dark.svg create mode 100644 src/vs/editor/contrib/find/browser/images/close.svg create mode 100644 src/vs/editor/contrib/find/browser/images/expando-collapsed-dark.svg create mode 100644 src/vs/editor/contrib/find/browser/images/expando-collapsed.svg create mode 100644 src/vs/editor/contrib/find/browser/images/expando-expanded-dark.svg create mode 100644 src/vs/editor/contrib/find/browser/images/expando-expanded.svg create mode 100644 src/vs/editor/contrib/find/browser/images/next-inverse.svg create mode 100644 src/vs/editor/contrib/find/browser/images/next.svg create mode 100644 src/vs/editor/contrib/find/browser/images/previous-inverse.svg create mode 100644 src/vs/editor/contrib/find/browser/images/previous.svg create mode 100644 src/vs/editor/contrib/find/browser/images/replace-all-inverse.svg create mode 100644 src/vs/editor/contrib/find/browser/images/replace-all.svg create mode 100644 src/vs/editor/contrib/find/browser/images/replace-inverse.svg create mode 100644 src/vs/editor/contrib/find/browser/images/replace.svg create mode 100644 src/vs/editor/contrib/find/browser/simpleFindWidget.css create mode 100644 src/vs/editor/contrib/find/browser/simpleFindWidget.ts create mode 100644 src/vs/editor/contrib/find/common/find.ts create mode 100644 src/vs/editor/contrib/find/common/findController.ts create mode 100644 src/vs/editor/contrib/find/common/findDecorations.ts create mode 100644 src/vs/editor/contrib/find/common/findModel.ts create mode 100644 src/vs/editor/contrib/find/common/findState.ts create mode 100644 src/vs/editor/contrib/find/common/replaceAllCommand.ts create mode 100644 src/vs/editor/contrib/find/common/replacePattern.ts create mode 100644 src/vs/editor/contrib/find/test/common/find.test.ts create mode 100644 src/vs/editor/contrib/find/test/common/findController.test.ts create mode 100644 src/vs/editor/contrib/find/test/common/findModel.test.ts create mode 100644 src/vs/editor/contrib/find/test/common/replacePattern.test.ts create mode 100644 src/vs/editor/contrib/folding/browser/arrow-collapse-dark.svg create mode 100644 src/vs/editor/contrib/folding/browser/arrow-collapse.svg create mode 100644 src/vs/editor/contrib/folding/browser/arrow-expand-dark.svg create mode 100644 src/vs/editor/contrib/folding/browser/arrow-expand.svg create mode 100644 src/vs/editor/contrib/folding/browser/folding.css create mode 100644 src/vs/editor/contrib/folding/browser/folding.ts create mode 100644 src/vs/editor/contrib/folding/common/folding.ts create mode 100644 src/vs/editor/contrib/folding/common/foldingModel.ts create mode 100644 src/vs/editor/contrib/folding/common/indentFoldStrategy.ts create mode 100644 src/vs/editor/contrib/folding/test/indentFold.test.ts create mode 100644 src/vs/editor/contrib/format/browser/formatActions.ts create mode 100644 src/vs/editor/contrib/format/common/format.ts create mode 100644 src/vs/editor/contrib/format/common/formatCommand.ts create mode 100644 src/vs/editor/contrib/format/test/common/formatCommand.test.ts create mode 100644 src/vs/editor/contrib/goToDeclaration/browser/clickLinkGesture.ts create mode 100644 src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts create mode 100644 src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts create mode 100644 src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.css create mode 100644 src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.ts create mode 100644 src/vs/editor/contrib/goToDeclaration/browser/messageController.css create mode 100644 src/vs/editor/contrib/goToDeclaration/browser/messageController.ts create mode 100644 src/vs/editor/contrib/gotoError/browser/gotoError.css create mode 100644 src/vs/editor/contrib/gotoError/browser/gotoError.ts create mode 100644 src/vs/editor/contrib/hover/browser/hover.css create mode 100644 src/vs/editor/contrib/hover/browser/hover.ts create mode 100644 src/vs/editor/contrib/hover/browser/hoverOperation.ts create mode 100644 src/vs/editor/contrib/hover/browser/hoverWidgets.ts create mode 100644 src/vs/editor/contrib/hover/browser/modesContentHover.ts create mode 100644 src/vs/editor/contrib/hover/browser/modesGlyphHover.ts create mode 100644 src/vs/editor/contrib/hover/common/hover.ts create mode 100644 src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.ts create mode 100644 src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplaceCommand.ts create mode 100644 src/vs/editor/contrib/indentation/common/indentUtils.ts create mode 100644 src/vs/editor/contrib/indentation/common/indentation.ts create mode 100644 src/vs/editor/contrib/indentation/test/indentation.test.ts create mode 100644 src/vs/editor/contrib/linesOperations/common/copyLinesCommand.ts create mode 100644 src/vs/editor/contrib/linesOperations/common/deleteLinesCommand.ts create mode 100644 src/vs/editor/contrib/linesOperations/common/linesOperations.ts create mode 100644 src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts create mode 100644 src/vs/editor/contrib/linesOperations/common/sortLinesCommand.ts create mode 100644 src/vs/editor/contrib/linesOperations/test/common/copyLinesCommand.test.ts create mode 100644 src/vs/editor/contrib/linesOperations/test/common/deleteLinesCommand.test.ts create mode 100644 src/vs/editor/contrib/linesOperations/test/common/linesOperations.test.ts create mode 100644 src/vs/editor/contrib/linesOperations/test/common/moveLinesCommand.test.ts create mode 100644 src/vs/editor/contrib/linesOperations/test/common/sortLinesCommand.test.ts create mode 100644 src/vs/editor/contrib/links/browser/links.css create mode 100644 src/vs/editor/contrib/links/browser/links.ts create mode 100644 src/vs/editor/contrib/links/common/links.ts create mode 100644 src/vs/editor/contrib/multicursor/common/multicursor.ts create mode 100644 src/vs/editor/contrib/multicursor/test/common/multicursor.test.ts create mode 100644 src/vs/editor/contrib/parameterHints/browser/arrow-down-dark.svg create mode 100644 src/vs/editor/contrib/parameterHints/browser/arrow-down.svg create mode 100644 src/vs/editor/contrib/parameterHints/browser/arrow-up-dark.svg create mode 100644 src/vs/editor/contrib/parameterHints/browser/arrow-up.svg create mode 100644 src/vs/editor/contrib/parameterHints/browser/parameterHints.css create mode 100644 src/vs/editor/contrib/parameterHints/browser/parameterHints.ts create mode 100644 src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts create mode 100644 src/vs/editor/contrib/parameterHints/common/parameterHints.ts create mode 100644 src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css create mode 100644 src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts create mode 100644 src/vs/editor/contrib/quickFix/browser/lightbulb-dark.svg create mode 100644 src/vs/editor/contrib/quickFix/browser/lightbulb.svg create mode 100644 src/vs/editor/contrib/quickFix/browser/quickFix.ts create mode 100644 src/vs/editor/contrib/quickFix/browser/quickFixCommands.ts create mode 100644 src/vs/editor/contrib/quickFix/browser/quickFixModel.ts create mode 100644 src/vs/editor/contrib/quickFix/browser/quickFixWidget.ts create mode 100644 src/vs/editor/contrib/quickFix/test/browser/quickFixModel.test.ts create mode 100644 src/vs/editor/contrib/quickOpen/common/quickOpen.ts create mode 100644 src/vs/editor/contrib/referenceSearch/browser/referenceSearch.ts create mode 100644 src/vs/editor/contrib/referenceSearch/browser/referencesController.ts create mode 100644 src/vs/editor/contrib/referenceSearch/browser/referencesModel.ts create mode 100644 src/vs/editor/contrib/referenceSearch/browser/referencesWidget.css create mode 100644 src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts create mode 100644 src/vs/editor/contrib/referenceSearch/test/browser/referencesModel.test.ts create mode 100644 src/vs/editor/contrib/rename/browser/rename.ts create mode 100644 src/vs/editor/contrib/rename/browser/renameInputField.css create mode 100644 src/vs/editor/contrib/rename/browser/renameInputField.ts create mode 100644 src/vs/editor/contrib/smartSelect/common/smartSelect.ts create mode 100644 src/vs/editor/contrib/smartSelect/common/tokenSelectionSupport.ts create mode 100644 src/vs/editor/contrib/smartSelect/common/tokenTree.ts create mode 100644 src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts create mode 100644 src/vs/editor/contrib/snippet/browser/snippet.md create mode 100644 src/vs/editor/contrib/snippet/browser/snippetController2.ts create mode 100644 src/vs/editor/contrib/snippet/browser/snippetParser.ts create mode 100644 src/vs/editor/contrib/snippet/browser/snippetSession.css create mode 100644 src/vs/editor/contrib/snippet/browser/snippetSession.ts create mode 100644 src/vs/editor/contrib/snippet/browser/snippetVariables.ts create mode 100644 src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts create mode 100644 src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts create mode 100644 src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts create mode 100644 src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts create mode 100644 src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts create mode 100644 src/vs/editor/contrib/suggest/browser/completionModel.ts create mode 100644 src/vs/editor/contrib/suggest/browser/media/Class_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Class_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/ColorPalette_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/ColorPalette_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Constant_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Constant_16x_inverse.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Document_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Document_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/EnumItem_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/EnumItem_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Enumerator_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Enumerator_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Event_16x_vscode.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Event_16x_vscode_inverse.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Field_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Field_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Folder_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Folder_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode_inverse.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Interface_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Interface_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/LocalVariable_16x_vscode.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/LocalVariable_16x_vscode_inverse.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Method_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Method_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Misc_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Misc_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Namespace_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Namespace_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Operator_16x_vscode.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Operator_16x_vscode_inverse.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Property_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Property_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Ruler_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Ruler_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Snippet_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Snippet_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/String_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/String_inverse_16x.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Structure_16x_vscode.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Structure_16x_vscode_inverse.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Template_16x_vscode.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/Template_16x_vscode_inverse.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/close-dark.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/close.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/info.svg create mode 100644 src/vs/editor/contrib/suggest/browser/media/suggest.css create mode 100644 src/vs/editor/contrib/suggest/browser/suggest.ts create mode 100644 src/vs/editor/contrib/suggest/browser/suggestController.ts create mode 100644 src/vs/editor/contrib/suggest/browser/suggestModel.ts create mode 100644 src/vs/editor/contrib/suggest/browser/suggestWidget.ts create mode 100644 src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts create mode 100644 src/vs/editor/contrib/suggest/test/browser/suggest.test.ts create mode 100644 src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts create mode 100644 src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.ts create mode 100644 src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts create mode 100644 src/vs/editor/contrib/wordOperations/common/wordOperations.ts create mode 100644 src/vs/editor/contrib/wordOperations/test/common/wordOperations.test.ts create mode 100644 src/vs/editor/contrib/zoneWidget/browser/media/close-inverse.svg create mode 100644 src/vs/editor/contrib/zoneWidget/browser/media/close.svg create mode 100644 src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.css create mode 100644 src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.ts create mode 100644 src/vs/editor/contrib/zoneWidget/browser/zoneWidget.css create mode 100644 src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts create mode 100644 src/vs/editor/editor.all.ts create mode 100644 src/vs/editor/editor.main.ts create mode 100644 src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.css create mode 100644 src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts create mode 100644 src/vs/editor/standalone/browser/colorizer.ts create mode 100644 src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.css create mode 100644 src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts create mode 100644 src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-inverse.svg create mode 100644 src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard.svg create mode 100644 src/vs/editor/standalone/browser/inspectTokens/inspectTokens.css create mode 100644 src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts create mode 100644 src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts create mode 100644 src/vs/editor/standalone/browser/quickOpen/gotoLine.css create mode 100644 src/vs/editor/standalone/browser/quickOpen/gotoLine.ts create mode 100644 src/vs/editor/standalone/browser/quickOpen/quickCommand.ts create mode 100644 src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts create mode 100644 src/vs/editor/standalone/browser/quickOpen/quickOutline.css create mode 100644 src/vs/editor/standalone/browser/quickOpen/quickOutline.ts create mode 100644 src/vs/editor/standalone/browser/quickOpen/symbol-sprite.svg create mode 100644 src/vs/editor/standalone/browser/simpleServices.ts create mode 100644 src/vs/editor/standalone/browser/standalone-tokens.css create mode 100644 src/vs/editor/standalone/browser/standaloneCodeEditor.ts create mode 100644 src/vs/editor/standalone/browser/standaloneEditor.ts create mode 100644 src/vs/editor/standalone/browser/standaloneLanguages.ts create mode 100644 src/vs/editor/standalone/browser/standaloneServices.ts create mode 100644 src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts create mode 100644 src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts create mode 100644 src/vs/editor/standalone/common/monarch/monarchCommon.ts create mode 100644 src/vs/editor/standalone/common/monarch/monarchCompile.ts create mode 100644 src/vs/editor/standalone/common/monarch/monarchLexer.ts create mode 100644 src/vs/editor/standalone/common/monarch/monarchTypes.ts create mode 100644 src/vs/editor/standalone/common/standaloneThemeService.ts create mode 100644 src/vs/editor/standalone/common/themes.ts create mode 100644 src/vs/editor/standalone/test/browser/simpleServices.test.ts create mode 100644 src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts create mode 100644 src/vs/editor/test/browser/controller/imeTester.html create mode 100644 src/vs/editor/test/browser/controller/imeTester.ts create mode 100644 src/vs/editor/test/browser/controller/inputRecorder.html create mode 100644 src/vs/editor/test/browser/controller/textAreaState.test.ts create mode 100644 src/vs/editor/test/browser/services/decorationRenderOptions.test.ts create mode 100644 src/vs/editor/test/browser/view/minimapFontCreator.html create mode 100644 src/vs/editor/test/browser/view/minimapFontCreator.ts create mode 100644 src/vs/editor/test/browser/view/viewLayer.test.ts create mode 100644 src/vs/editor/test/common/commands/commandTestUtils.ts create mode 100644 src/vs/editor/test/common/commands/shiftCommand.test.ts create mode 100644 src/vs/editor/test/common/commands/sideEditing.test.ts create mode 100644 src/vs/editor/test/common/commands/trimTrailingWhitespaceCommand.test.ts create mode 100644 src/vs/editor/test/common/commentMode.ts create mode 100644 src/vs/editor/test/common/config/commonEditorConfig.test.ts create mode 100644 src/vs/editor/test/common/controller/cursor.test.ts create mode 100644 src/vs/editor/test/common/controller/cursorMoveCommand.test.ts create mode 100644 src/vs/editor/test/common/controller/cursorMoveHelper.test.ts create mode 100644 src/vs/editor/test/common/core/characterClassifier.test.ts create mode 100644 src/vs/editor/test/common/core/editorState.test.ts create mode 100644 src/vs/editor/test/common/core/lineTokens.test.ts create mode 100644 src/vs/editor/test/common/core/range.test.ts create mode 100644 src/vs/editor/test/common/diff/diffComputer.test.ts create mode 100644 src/vs/editor/test/common/editorTestUtils.ts create mode 100644 src/vs/editor/test/common/mocks/mockCodeEditor.ts create mode 100644 src/vs/editor/test/common/mocks/mockCodeEditorService.ts create mode 100644 src/vs/editor/test/common/mocks/mockMode.ts create mode 100644 src/vs/editor/test/common/mocks/testConfiguration.ts create mode 100644 src/vs/editor/test/common/model/editableTextModel.test.ts create mode 100644 src/vs/editor/test/common/model/editableTextModelAuto.test.ts create mode 100644 src/vs/editor/test/common/model/editableTextModelTestUtils.ts create mode 100644 src/vs/editor/test/common/model/indentRanges.test.ts create mode 100644 src/vs/editor/test/common/model/model.line.test.ts create mode 100644 src/vs/editor/test/common/model/model.modes.test.ts create mode 100644 src/vs/editor/test/common/model/model.test.ts create mode 100644 src/vs/editor/test/common/model/modelDecorations.test.ts create mode 100644 src/vs/editor/test/common/model/modelEditOperation.test.ts create mode 100644 src/vs/editor/test/common/model/textModel.test.ts create mode 100644 src/vs/editor/test/common/model/textModelSearch.test.ts create mode 100644 src/vs/editor/test/common/model/textModelWithTokens.test.ts create mode 100644 src/vs/editor/test/common/modes/languageConfiguration.test.ts create mode 100644 src/vs/editor/test/common/modes/languageSelector.test.ts create mode 100644 src/vs/editor/test/common/modes/linkComputer.test.ts create mode 100644 src/vs/editor/test/common/modes/supports/characterPair.test.ts create mode 100644 src/vs/editor/test/common/modes/supports/electricCharacter.test.ts create mode 100644 src/vs/editor/test/common/modes/supports/onEnter.test.ts create mode 100644 src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts create mode 100644 src/vs/editor/test/common/modes/supports/tokenization.test.ts create mode 100644 src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts create mode 100644 src/vs/editor/test/common/modesTestUtils.ts create mode 100644 src/vs/editor/test/common/services/editorSimpleWorker.test.ts create mode 100644 src/vs/editor/test/common/services/languagesRegistry.test.ts create mode 100644 src/vs/editor/test/common/services/modelService.test.ts create mode 100644 src/vs/editor/test/common/standalone/standaloneBase.test.ts create mode 100644 src/vs/editor/test/common/view/minimapCharRenderer.test.ts create mode 100644 src/vs/editor/test/common/view/minimapCharRendererFactory.ts create mode 100644 src/vs/editor/test/common/view/overviewZoneManager.test.ts create mode 100644 src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts create mode 100644 src/vs/editor/test/common/viewLayout/lineDecorations.test.ts create mode 100644 src/vs/editor/test/common/viewLayout/linesLayout.test.ts create mode 100644 src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts create mode 100644 src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts create mode 100644 src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts create mode 100644 src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts create mode 100644 src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts create mode 100644 src/vs/editor/test/common/viewModel/testViewModel.ts create mode 100644 src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts create mode 100644 src/vs/editor/test/common/viewModel/viewModelImpl.test.ts create mode 100644 src/vs/loader.js create mode 100644 src/vs/monaco.d.ts create mode 100644 src/vs/nls.build.js create mode 100644 src/vs/nls.d.ts create mode 100644 src/vs/nls.js create mode 100644 src/vs/platform/actions/browser/menuItemActionItem.ts create mode 100644 src/vs/platform/actions/common/actions.ts create mode 100644 src/vs/platform/actions/common/menu.ts create mode 100644 src/vs/platform/actions/common/menuService.ts create mode 100644 src/vs/platform/actions/electron-browser/menusExtensionPoint.ts create mode 100644 src/vs/platform/actions/test/common/menuService.test.ts create mode 100644 src/vs/platform/backup/common/backup.ts create mode 100644 src/vs/platform/backup/electron-main/backupMainService.ts create mode 100644 src/vs/platform/backup/test/electron-main/backupMainService.test.ts create mode 100644 src/vs/platform/broadcast/electron-browser/broadcastService.ts create mode 100644 src/vs/platform/clipboard/common/clipboardService.ts create mode 100644 src/vs/platform/clipboard/electron-browser/clipboardService.ts create mode 100644 src/vs/platform/commands/common/commandService.ts create mode 100644 src/vs/platform/commands/common/commands.ts create mode 100644 src/vs/platform/commands/test/commandService.test.ts create mode 100644 src/vs/platform/commands/test/commands.test.ts create mode 100644 src/vs/platform/configuration/common/configuration.ts create mode 100644 src/vs/platform/configuration/common/configurationRegistry.ts create mode 100644 src/vs/platform/configuration/common/model.ts create mode 100644 src/vs/platform/configuration/node/configurationService.ts create mode 100644 src/vs/platform/configuration/test/common/configuration.test.ts create mode 100644 src/vs/platform/configuration/test/common/model.test.ts create mode 100644 src/vs/platform/configuration/test/common/testConfigurationService.ts create mode 100644 src/vs/platform/configuration/test/node/configurationService.test.ts create mode 100644 src/vs/platform/contextkey/browser/contextKeyService.ts create mode 100644 src/vs/platform/contextkey/common/contextkey.ts create mode 100644 src/vs/platform/contextkey/test/common/contextkey.test.ts create mode 100644 src/vs/platform/contextview/browser/contextMenuHandler.css create mode 100644 src/vs/platform/contextview/browser/contextMenuHandler.ts create mode 100644 src/vs/platform/contextview/browser/contextMenuService.ts create mode 100644 src/vs/platform/contextview/browser/contextView.ts create mode 100644 src/vs/platform/contextview/browser/contextViewService.ts create mode 100644 src/vs/platform/credentials/common/credentials.ts create mode 100644 src/vs/platform/credentials/node/credentialsIpc.ts create mode 100644 src/vs/platform/credentials/node/credentialsService.ts create mode 100644 src/vs/platform/editor/common/editor.ts create mode 100644 src/vs/platform/environment/common/environment.ts create mode 100644 src/vs/platform/environment/node/argv.ts create mode 100644 src/vs/platform/environment/node/environmentService.ts create mode 100644 src/vs/platform/environment/test/node/environmentService.test.ts create mode 100644 src/vs/platform/extensionManagement/common/extensionEnablementService.ts create mode 100644 src/vs/platform/extensionManagement/common/extensionManagement.ts create mode 100644 src/vs/platform/extensionManagement/common/extensionManagementIpc.ts create mode 100644 src/vs/platform/extensionManagement/common/extensionManagementUtil.ts create mode 100644 src/vs/platform/extensionManagement/common/extensionNls.ts create mode 100644 src/vs/platform/extensionManagement/node/extensionGalleryService.ts create mode 100644 src/vs/platform/extensionManagement/node/extensionManagementService.ts create mode 100644 src/vs/platform/extensionManagement/node/media/defaultIcon.png create mode 100644 src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts create mode 100644 src/vs/platform/extensionManagement/test/common/extensionManagement.test.ts create mode 100644 src/vs/platform/extensions/common/extensionHost.ts create mode 100644 src/vs/platform/extensions/common/extensions.ts create mode 100644 src/vs/platform/extensions/common/extensionsRegistry.ts create mode 100644 src/vs/platform/extensions/node/extensionValidator.ts create mode 100644 src/vs/platform/extensions/test/node/extensionValidator.test.ts create mode 100644 src/vs/platform/files/common/files.ts create mode 100644 src/vs/platform/files/test/files.test.ts create mode 100644 src/vs/platform/history/common/history.ts create mode 100644 src/vs/platform/history/electron-main/historyMainService.ts create mode 100644 src/vs/platform/instantiation/common/descriptors.ts create mode 100644 src/vs/platform/instantiation/common/extensions.ts create mode 100644 src/vs/platform/instantiation/common/instantiation.ts create mode 100644 src/vs/platform/instantiation/common/instantiationService.ts create mode 100644 src/vs/platform/instantiation/common/serviceCollection.ts create mode 100644 src/vs/platform/instantiation/test/common/instantiationService.test.ts create mode 100644 src/vs/platform/instantiation/test/common/instantiationServiceMock.ts create mode 100644 src/vs/platform/integrity/common/integrity.ts create mode 100644 src/vs/platform/integrity/node/integrityServiceImpl.ts create mode 100644 src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts create mode 100644 src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.ts create mode 100644 src/vs/platform/keybinding/common/abstractKeybindingService.ts create mode 100644 src/vs/platform/keybinding/common/keybinding.ts create mode 100644 src/vs/platform/keybinding/common/keybindingResolver.ts create mode 100644 src/vs/platform/keybinding/common/keybindingsRegistry.ts create mode 100644 src/vs/platform/keybinding/common/resolvedKeybindingItem.ts create mode 100644 src/vs/platform/keybinding/common/usLayoutResolvedKeybinding.ts create mode 100644 src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts create mode 100644 src/vs/platform/keybinding/test/common/keybindingLabels.test.ts create mode 100644 src/vs/platform/keybinding/test/common/keybindingResolver.test.ts create mode 100644 src/vs/platform/keybinding/test/common/mockKeybindingService.ts create mode 100644 src/vs/platform/lifecycle/common/lifecycle.ts create mode 100644 src/vs/platform/lifecycle/electron-main/lifecycleMain.ts create mode 100644 src/vs/platform/list/browser/listService.ts create mode 100644 src/vs/platform/log/common/log.ts create mode 100644 src/vs/platform/markers/common/markerService.ts create mode 100644 src/vs/platform/markers/common/markers.ts create mode 100644 src/vs/platform/markers/common/problemMatcher.ts create mode 100644 src/vs/platform/markers/test/common/markerService.test.ts create mode 100644 src/vs/platform/message/common/message.ts create mode 100644 src/vs/platform/message/common/messageIpc.ts create mode 100644 src/vs/platform/message/node/messageCli.ts create mode 100644 src/vs/platform/node/package.ts create mode 100644 src/vs/platform/node/product.ts create mode 100644 src/vs/platform/opener/browser/opener.contribution.ts create mode 100644 src/vs/platform/opener/browser/openerService.ts create mode 100644 src/vs/platform/opener/common/opener.ts create mode 100644 src/vs/platform/opener/test/browser/openerService.test.ts create mode 100644 src/vs/platform/progress/common/progress.ts create mode 100644 src/vs/platform/quickOpen/common/quickOpen.ts create mode 100644 src/vs/platform/registry/common/platform.ts create mode 100644 src/vs/platform/registry/test/common/platform.test.ts create mode 100644 src/vs/platform/request/electron-browser/requestService.ts create mode 100644 src/vs/platform/request/electron-main/requestService.ts create mode 100644 src/vs/platform/request/node/request.ts create mode 100644 src/vs/platform/request/node/requestService.ts create mode 100644 src/vs/platform/search/common/replace.ts create mode 100644 src/vs/platform/search/common/search.ts create mode 100644 src/vs/platform/search/test/common/replace.test.ts create mode 100644 src/vs/platform/statusbar/common/statusbar.ts create mode 100644 src/vs/platform/storage/common/migration.ts create mode 100644 src/vs/platform/storage/common/storage.ts create mode 100644 src/vs/platform/storage/common/storageService.ts create mode 100644 src/vs/platform/storage/node/storage.ts create mode 100644 src/vs/platform/storage/test/browser/migration.test.ts create mode 100644 src/vs/platform/storage/test/common/storageService.test.ts create mode 100644 src/vs/platform/telemetry/browser/errorTelemetry.ts create mode 100644 src/vs/platform/telemetry/browser/idleMonitor.ts create mode 100644 src/vs/platform/telemetry/common/experiments.ts create mode 100644 src/vs/platform/telemetry/common/telemetry.ts create mode 100644 src/vs/platform/telemetry/common/telemetryIpc.ts create mode 100644 src/vs/platform/telemetry/common/telemetryService.ts create mode 100644 src/vs/platform/telemetry/common/telemetryUtils.ts create mode 100644 src/vs/platform/telemetry/node/appInsightsAppender.ts create mode 100644 src/vs/platform/telemetry/node/commonProperties.ts create mode 100644 src/vs/platform/telemetry/node/workbenchCommonProperties.ts create mode 100644 src/vs/platform/telemetry/test/electron-browser/appInsightsAppender.test.ts create mode 100644 src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts create mode 100644 src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts create mode 100644 src/vs/platform/theme/common/colorExtensionPoint.ts create mode 100644 src/vs/platform/theme/common/colorRegistry.ts create mode 100644 src/vs/platform/theme/common/styler.ts create mode 100644 src/vs/platform/theme/common/themeService.ts create mode 100644 src/vs/platform/theme/test/common/testThemeService.ts create mode 100644 src/vs/platform/update/common/update.ts create mode 100644 src/vs/platform/update/common/updateIpc.ts create mode 100644 src/vs/platform/update/electron-main/auto-updater.linux.ts create mode 100644 src/vs/platform/update/electron-main/auto-updater.win32.ts create mode 100644 src/vs/platform/update/electron-main/updateService.ts create mode 100644 src/vs/platform/url/common/url.ts create mode 100644 src/vs/platform/url/common/urlIpc.ts create mode 100644 src/vs/platform/url/electron-main/urlService.ts create mode 100644 src/vs/platform/windows/common/windows.ts create mode 100644 src/vs/platform/windows/common/windowsIpc.ts create mode 100644 src/vs/platform/windows/electron-browser/windowService.ts create mode 100644 src/vs/platform/windows/electron-main/windows.ts create mode 100644 src/vs/platform/windows/electron-main/windowsService.ts create mode 100644 src/vs/platform/workspace/common/workspace.ts create mode 100644 src/vs/platform/workspace/test/common/testWorkspace.ts create mode 100644 src/vs/platform/workspace/test/common/workspace.test.ts create mode 100644 src/vs/platform/workspaces/common/workspaces.ts create mode 100644 src/vs/platform/workspaces/common/workspacesIpc.ts create mode 100644 src/vs/platform/workspaces/electron-main/workspacesMainService.ts create mode 100644 src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts create mode 100644 src/vs/vscode.d.ts create mode 100644 src/vs/vscode.proposed.d.ts create mode 100644 src/vs/workbench/api/electron-browser/extHostCustomers.ts create mode 100644 src/vs/workbench/api/electron-browser/extensionHost.contribution.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadCommands.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadCredentials.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadDebugService.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadDiagnostics.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadDialogs.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadDocumentContentProviders.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadDocuments.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadEditor.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadEditors.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadErrors.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadHeapService.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadLanguages.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadMessageService.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadOutputService.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadProgress.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadSCM.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadStorage.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadTask.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadTelemetry.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadWindow.ts create mode 100644 src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts create mode 100644 src/vs/workbench/api/node/extHost.api.impl.ts create mode 100644 src/vs/workbench/api/node/extHost.protocol.ts create mode 100644 src/vs/workbench/api/node/extHostApiCommands.ts create mode 100644 src/vs/workbench/api/node/extHostCommands.ts create mode 100644 src/vs/workbench/api/node/extHostConfiguration.ts create mode 100644 src/vs/workbench/api/node/extHostCredentials.ts create mode 100644 src/vs/workbench/api/node/extHostDebugService.ts create mode 100644 src/vs/workbench/api/node/extHostDiagnostics.ts create mode 100644 src/vs/workbench/api/node/extHostDialogs.ts create mode 100644 src/vs/workbench/api/node/extHostDocumentContentProviders.ts create mode 100644 src/vs/workbench/api/node/extHostDocumentData.ts create mode 100644 src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts create mode 100644 src/vs/workbench/api/node/extHostDocuments.ts create mode 100644 src/vs/workbench/api/node/extHostDocumentsAndEditors.ts create mode 100644 src/vs/workbench/api/node/extHostExtensionActivator.ts create mode 100644 src/vs/workbench/api/node/extHostExtensionService.ts create mode 100644 src/vs/workbench/api/node/extHostFileSystemEventService.ts create mode 100644 src/vs/workbench/api/node/extHostHeapService.ts create mode 100644 src/vs/workbench/api/node/extHostLanguageFeatures.ts create mode 100644 src/vs/workbench/api/node/extHostLanguages.ts create mode 100644 src/vs/workbench/api/node/extHostMessageService.ts create mode 100644 src/vs/workbench/api/node/extHostOutputService.ts create mode 100644 src/vs/workbench/api/node/extHostProgress.ts create mode 100644 src/vs/workbench/api/node/extHostQuickOpen.ts create mode 100644 src/vs/workbench/api/node/extHostSCM.ts create mode 100644 src/vs/workbench/api/node/extHostStatusBar.ts create mode 100644 src/vs/workbench/api/node/extHostStorage.ts create mode 100644 src/vs/workbench/api/node/extHostTask.ts create mode 100644 src/vs/workbench/api/node/extHostTerminalService.ts create mode 100644 src/vs/workbench/api/node/extHostTextEditor.ts create mode 100644 src/vs/workbench/api/node/extHostTextEditors.ts create mode 100644 src/vs/workbench/api/node/extHostTreeViews.ts create mode 100644 src/vs/workbench/api/node/extHostTypeConverters.ts create mode 100644 src/vs/workbench/api/node/extHostTypes.ts create mode 100644 src/vs/workbench/api/node/extHostWindow.ts create mode 100644 src/vs/workbench/api/node/extHostWorkspace.ts create mode 100644 src/vs/workbench/browser/actions.ts create mode 100644 src/vs/workbench/browser/actions/configureLocale.ts create mode 100644 src/vs/workbench/browser/actions/media/actions.css create mode 100644 src/vs/workbench/browser/actions/media/editor-layout-inverse.svg create mode 100644 src/vs/workbench/browser/actions/media/editor-layout.svg create mode 100644 src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts create mode 100644 src/vs/workbench/browser/actions/toggleEditorLayout.ts create mode 100644 src/vs/workbench/browser/actions/toggleSidebarPosition.ts create mode 100644 src/vs/workbench/browser/actions/toggleSidebarVisibility.ts create mode 100644 src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts create mode 100644 src/vs/workbench/browser/actions/toggleZenMode.ts create mode 100644 src/vs/workbench/browser/actions/workspaceActions.ts create mode 100644 src/vs/workbench/browser/activity.ts create mode 100644 src/vs/workbench/browser/composite.ts create mode 100644 src/vs/workbench/browser/labels.ts create mode 100644 src/vs/workbench/browser/layout.ts create mode 100644 src/vs/workbench/browser/media/part.css create mode 100644 src/vs/workbench/browser/panel.ts create mode 100644 src/vs/workbench/browser/part.ts create mode 100644 src/vs/workbench/browser/parts/activitybar/activitybarActions.ts create mode 100644 src/vs/workbench/browser/parts/activitybar/activitybarPart.ts create mode 100644 src/vs/workbench/browser/parts/activitybar/media/activityaction.css create mode 100644 src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css create mode 100644 src/vs/workbench/browser/parts/activitybar/media/ellipsis-global.svg create mode 100644 src/vs/workbench/browser/parts/compositePart.ts create mode 100644 src/vs/workbench/browser/parts/editor/baseEditor.ts create mode 100644 src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts create mode 100644 src/vs/workbench/browser/parts/editor/binaryEditor.ts create mode 100644 src/vs/workbench/browser/parts/editor/editor.contribution.ts create mode 100644 src/vs/workbench/browser/parts/editor/editorActions.ts create mode 100644 src/vs/workbench/browser/parts/editor/editorCommands.ts create mode 100644 src/vs/workbench/browser/parts/editor/editorGroupsControl.ts create mode 100644 src/vs/workbench/browser/parts/editor/editorPart.ts create mode 100644 src/vs/workbench/browser/parts/editor/editorPicker.ts create mode 100644 src/vs/workbench/browser/parts/editor/editorStatus.ts create mode 100644 src/vs/workbench/browser/parts/editor/media/close-dirty-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-dirty.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/editorGroupsControl.css create mode 100644 src/vs/workbench/browser/parts/editor/media/editorpart.css create mode 100644 src/vs/workbench/browser/parts/editor/media/editorpicker.css create mode 100644 src/vs/workbench/browser/parts/editor/media/editorstatus.css create mode 100644 src/vs/workbench/browser/parts/editor/media/next-diff-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/next-diff.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/notabstitle.css create mode 100644 src/vs/workbench/browser/parts/editor/media/previous-diff-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/previous-diff.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-horizontal.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-vertical-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-vertical.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/stackview-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/stackview.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/tabstitle.css create mode 100644 src/vs/workbench/browser/parts/editor/media/textdiffeditor.css create mode 100644 src/vs/workbench/browser/parts/editor/media/titlecontrol.css create mode 100644 src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts create mode 100644 src/vs/workbench/browser/parts/editor/sideBySideEditor.ts create mode 100644 src/vs/workbench/browser/parts/editor/tabsTitleControl.ts create mode 100644 src/vs/workbench/browser/parts/editor/textDiffEditor.ts create mode 100644 src/vs/workbench/browser/parts/editor/textEditor.ts create mode 100644 src/vs/workbench/browser/parts/editor/textResourceEditor.ts create mode 100644 src/vs/workbench/browser/parts/editor/titleControl.ts create mode 100644 src/vs/workbench/browser/parts/editor/webviewEditor.ts create mode 100644 src/vs/workbench/browser/parts/media/compositepart.css create mode 100644 src/vs/workbench/browser/parts/panel/media/close-inverse.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/close.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/down-inverse.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/down.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/panelpart.css create mode 100644 src/vs/workbench/browser/parts/panel/media/up-inverse.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/up.svg create mode 100644 src/vs/workbench/browser/parts/panel/panelActions.ts create mode 100644 src/vs/workbench/browser/parts/panel/panelPart.ts create mode 100644 src/vs/workbench/browser/parts/quickopen/media/dirty-inverse.svg create mode 100644 src/vs/workbench/browser/parts/quickopen/media/dirty.svg create mode 100644 src/vs/workbench/browser/parts/quickopen/media/quickopen.css create mode 100644 src/vs/workbench/browser/parts/quickopen/quickOpenController.ts create mode 100644 src/vs/workbench/browser/parts/quickopen/quickopen.contribution.ts create mode 100644 src/vs/workbench/browser/parts/quickopen/quickopen.ts create mode 100644 src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css create mode 100644 src/vs/workbench/browser/parts/sidebar/sidebarPart.ts create mode 100644 src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css create mode 100644 src/vs/workbench/browser/parts/statusbar/statusbar.ts create mode 100644 src/vs/workbench/browser/parts/statusbar/statusbarPart.ts create mode 100644 src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css create mode 100644 src/vs/workbench/browser/parts/titlebar/titlebarPart.ts create mode 100644 src/vs/workbench/browser/quickopen.ts create mode 100644 src/vs/workbench/browser/viewlet.ts create mode 100644 src/vs/workbench/buildfile.js create mode 100644 src/vs/workbench/common/actionRegistry.ts create mode 100644 src/vs/workbench/common/component.ts create mode 100644 src/vs/workbench/common/composite.ts create mode 100644 src/vs/workbench/common/contributions.ts create mode 100644 src/vs/workbench/common/editor.ts create mode 100644 src/vs/workbench/common/editor/binaryEditorModel.ts create mode 100644 src/vs/workbench/common/editor/diffEditorInput.ts create mode 100644 src/vs/workbench/common/editor/diffEditorModel.ts create mode 100644 src/vs/workbench/common/editor/editorStacksModel.ts create mode 100644 src/vs/workbench/common/editor/rangeDecorations.ts create mode 100644 src/vs/workbench/common/editor/resourceEditorInput.ts create mode 100644 src/vs/workbench/common/editor/resourceEditorModel.ts create mode 100644 src/vs/workbench/common/editor/textDiffEditorModel.ts create mode 100644 src/vs/workbench/common/editor/textEditorModel.ts create mode 100644 src/vs/workbench/common/editor/untitledEditorInput.ts create mode 100644 src/vs/workbench/common/editor/untitledEditorModel.ts create mode 100644 src/vs/workbench/common/memento.ts create mode 100644 src/vs/workbench/common/panel.ts create mode 100644 src/vs/workbench/common/resources.ts create mode 100644 src/vs/workbench/common/theme.ts create mode 100644 src/vs/workbench/common/viewlet.ts create mode 100644 src/vs/workbench/electron-browser/actions.ts create mode 100644 src/vs/workbench/electron-browser/bootstrap/index.html create mode 100644 src/vs/workbench/electron-browser/bootstrap/index.js create mode 100644 src/vs/workbench/electron-browser/bootstrap/preload.js create mode 100644 src/vs/workbench/electron-browser/commands.ts create mode 100644 src/vs/workbench/electron-browser/main.contribution.ts create mode 100644 src/vs/workbench/electron-browser/main.ts create mode 100644 src/vs/workbench/electron-browser/media/actions.css create mode 100644 src/vs/workbench/electron-browser/media/clear.svg create mode 100644 src/vs/workbench/electron-browser/media/remove-dark.svg create mode 100644 src/vs/workbench/electron-browser/media/remove.svg create mode 100644 src/vs/workbench/electron-browser/media/shell.css create mode 100644 src/vs/workbench/electron-browser/media/workbench.css create mode 100644 src/vs/workbench/electron-browser/nodeCachedDataManager.ts create mode 100644 src/vs/workbench/electron-browser/shell.ts create mode 100644 src/vs/workbench/electron-browser/window.ts create mode 100644 src/vs/workbench/electron-browser/workbench.ts create mode 100644 src/vs/workbench/node/extensionHostMain.ts create mode 100644 src/vs/workbench/node/extensionHostProcess.ts create mode 100644 src/vs/workbench/parts/backup/common/backup.contribution.ts create mode 100644 src/vs/workbench/parts/backup/common/backupModelTracker.ts create mode 100644 src/vs/workbench/parts/backup/common/backupRestorer.ts create mode 100644 src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts create mode 100644 src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/accessibility.css create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/media/WordWrap_16x.svg create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/media/codeEditor.css create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/menuPreventer.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.css create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.ts create mode 100644 src/vs/workbench/parts/debug/browser/breakpointWidget.ts create mode 100644 src/vs/workbench/parts/debug/browser/debugActionItems.ts create mode 100644 src/vs/workbench/parts/debug/browser/debugActions.ts create mode 100644 src/vs/workbench/parts/debug/browser/debugActionsWidget.ts create mode 100644 src/vs/workbench/parts/debug/browser/debugContentProvider.ts create mode 100644 src/vs/workbench/parts/debug/browser/debugEditorActions.ts create mode 100644 src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts create mode 100644 src/vs/workbench/parts/debug/browser/debugQuickOpen.ts create mode 100644 src/vs/workbench/parts/debug/browser/debugViewlet.ts create mode 100644 src/vs/workbench/parts/debug/browser/exceptionWidget.ts create mode 100644 src/vs/workbench/parts/debug/browser/linkDetector.ts create mode 100644 src/vs/workbench/parts/debug/browser/media/add-focus.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/add-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/add.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint-conditional-dark.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint-conditional.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint-dark.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint-disabled-dark.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint-disabled.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint-hint.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint-unsupported-dark.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint-unsupported.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint-unverified-dark.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint-unverified.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoint.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpointWidget.css create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoints-activate-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/breakpoints-activate.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/clear-repl-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/clear-repl.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/configure-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/configure.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/continue-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/continue.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/current-and-breakpoint.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/current-arrow.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/debug-dark.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/debug.contribution.css create mode 100644 src/vs/workbench/parts/debug/browser/media/debugActionsWidget.css create mode 100644 src/vs/workbench/parts/debug/browser/media/debugHover.css create mode 100644 src/vs/workbench/parts/debug/browser/media/debugViewlet.css create mode 100644 src/vs/workbench/parts/debug/browser/media/disconnect-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/disconnect.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/drag.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/exceptionWidget.css create mode 100644 src/vs/workbench/parts/debug/browser/media/pause-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/pause.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/remove-all-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/remove-all.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/remove-focus.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/remove-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/remove.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/repl-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/repl.css create mode 100644 src/vs/workbench/parts/debug/browser/media/repl.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/restart-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/restart.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/stackframe-and-breakpoint-dark.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/stackframe-and-breakpoint.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/stackframe-arrow-dark.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/stackframe-arrow.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/step-into-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/step-into.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/step-out-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/step-out.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/step-over-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/step-over.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/stop-inverse.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/stop.svg create mode 100644 src/vs/workbench/parts/debug/common/debug.ts create mode 100644 src/vs/workbench/parts/debug/common/debugModel.ts create mode 100644 src/vs/workbench/parts/debug/common/debugProtocol.d.ts create mode 100644 src/vs/workbench/parts/debug/common/debugSource.ts create mode 100644 src/vs/workbench/parts/debug/common/debugViewModel.ts create mode 100644 src/vs/workbench/parts/debug/common/replHistory.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/debugCommands.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/debugHover.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/debugService.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/debugViewer.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/debugViews.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/repl.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/replEditor.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/replViewer.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts create mode 100644 src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts create mode 100644 src/vs/workbench/parts/debug/node/debugAdapter.ts create mode 100644 src/vs/workbench/parts/debug/node/telemetryApp.ts create mode 100644 src/vs/workbench/parts/debug/node/v8Protocol.ts create mode 100644 src/vs/workbench/parts/debug/test/common/debugSource.test.ts create mode 100644 src/vs/workbench/parts/debug/test/common/debugUtils.test.ts create mode 100644 src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts create mode 100644 src/vs/workbench/parts/debug/test/common/mockDebug.ts create mode 100644 src/vs/workbench/parts/debug/test/common/replHistory.test.ts create mode 100644 src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts create mode 100644 src/vs/workbench/parts/debug/test/node/debugModel.test.ts create mode 100644 src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts create mode 100644 src/vs/workbench/parts/emmet/browser/emmet.browser.contribution.ts create mode 100644 src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.ts create mode 100644 src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.ts create mode 100644 src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts create mode 100644 src/vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts create mode 100644 src/vs/workbench/parts/execution/common/execution.ts create mode 100644 src/vs/workbench/parts/execution/electron-browser/TerminalHelper.scpt create mode 100644 src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts create mode 100644 src/vs/workbench/parts/execution/electron-browser/iTermHelper.scpt create mode 100644 src/vs/workbench/parts/execution/electron-browser/terminal.ts create mode 100644 src/vs/workbench/parts/execution/electron-browser/terminalService.ts create mode 100644 src/vs/workbench/parts/execution/test/electron-browser/terminalService.test.ts create mode 100644 src/vs/workbench/parts/extensions/browser/dependenciesViewer.ts create mode 100644 src/vs/workbench/parts/extensions/browser/extensionEditor.ts create mode 100644 src/vs/workbench/parts/extensions/browser/extensionsActions.ts create mode 100644 src/vs/workbench/parts/extensions/browser/extensionsList.ts create mode 100644 src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts create mode 100644 src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts create mode 100644 src/vs/workbench/parts/extensions/browser/media/EmptyStar.svg create mode 100644 src/vs/workbench/parts/extensions/browser/media/FullStarLight.svg create mode 100644 src/vs/workbench/parts/extensions/browser/media/HalfStarLight.svg create mode 100644 src/vs/workbench/parts/extensions/browser/media/clear-inverse.svg create mode 100644 src/vs/workbench/parts/extensions/browser/media/clear.svg create mode 100644 src/vs/workbench/parts/extensions/browser/media/defaultIcon.png create mode 100644 src/vs/workbench/parts/extensions/browser/media/extensionActions.css create mode 100644 src/vs/workbench/parts/extensions/browser/media/extensionEditor.css create mode 100644 src/vs/workbench/parts/extensions/browser/media/extensionsWidgets.css create mode 100644 src/vs/workbench/parts/extensions/browser/media/loading.svg create mode 100644 src/vs/workbench/parts/extensions/browser/media/manage-inverse.svg create mode 100644 src/vs/workbench/parts/extensions/browser/media/manage.svg create mode 100644 src/vs/workbench/parts/extensions/browser/media/markdown.css create mode 100644 src/vs/workbench/parts/extensions/common/extensionQuery.ts create mode 100644 src/vs/workbench/parts/extensions/common/extensions.ts create mode 100644 src/vs/workbench/parts/extensions/common/extensionsFileTemplate.ts create mode 100644 src/vs/workbench/parts/extensions/common/extensionsInput.ts create mode 100644 src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts create mode 100644 src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts create mode 100644 src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts create mode 100644 src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts create mode 100644 src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts create mode 100644 src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts create mode 100644 src/vs/workbench/parts/extensions/electron-browser/media/extensions-dark.svg create mode 100644 src/vs/workbench/parts/extensions/electron-browser/media/extensions.css create mode 100644 src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css create mode 100644 src/vs/workbench/parts/extensions/electron-browser/media/loading.svg create mode 100644 src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts create mode 100644 src/vs/workbench/parts/extensions/test/common/extensionQuery.test.ts create mode 100644 src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts create mode 100644 src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts create mode 100644 src/vs/workbench/parts/feedback/electron-browser/feedback.contribution.ts create mode 100644 src/vs/workbench/parts/feedback/electron-browser/feedback.ts create mode 100644 src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts create mode 100644 src/vs/workbench/parts/feedback/electron-browser/media/close-dark.svg create mode 100644 src/vs/workbench/parts/feedback/electron-browser/media/close.svg create mode 100644 src/vs/workbench/parts/feedback/electron-browser/media/feedback.css create mode 100644 src/vs/workbench/parts/feedback/electron-browser/media/happy.svg create mode 100644 src/vs/workbench/parts/feedback/electron-browser/media/info.svg create mode 100644 src/vs/workbench/parts/feedback/electron-browser/media/sad.svg create mode 100644 src/vs/workbench/parts/feedback/electron-browser/media/smiley.svg create mode 100644 src/vs/workbench/parts/feedback/electron-browser/media/twitter.svg create mode 100644 src/vs/workbench/parts/files/browser/editors/binaryFileEditor.ts create mode 100644 src/vs/workbench/parts/files/browser/editors/textFileEditor.ts create mode 100644 src/vs/workbench/parts/files/browser/explorerViewlet.ts create mode 100644 src/vs/workbench/parts/files/browser/fileActions.contribution.ts create mode 100644 src/vs/workbench/parts/files/browser/fileActions.ts create mode 100644 src/vs/workbench/parts/files/browser/fileCommands.ts create mode 100644 src/vs/workbench/parts/files/browser/fileResultsNavigation.ts create mode 100644 src/vs/workbench/parts/files/browser/files.contribution.ts create mode 100644 src/vs/workbench/parts/files/browser/media/AddFile.svg create mode 100644 src/vs/workbench/parts/files/browser/media/AddFile_inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/AddFolder.svg create mode 100644 src/vs/workbench/parts/files/browser/media/AddFolder_inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/CollapseAll.svg create mode 100644 src/vs/workbench/parts/files/browser/media/CollapseAll_inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/Preview.svg create mode 100644 src/vs/workbench/parts/files/browser/media/Preview_inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/Refresh.svg create mode 100644 src/vs/workbench/parts/files/browser/media/Refresh_inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/action-close-dark.svg create mode 100644 src/vs/workbench/parts/files/browser/media/action-close-dirty-dark.svg create mode 100644 src/vs/workbench/parts/files/browser/media/action-close-dirty-focus.svg create mode 100644 src/vs/workbench/parts/files/browser/media/action-close-dirty.svg create mode 100644 src/vs/workbench/parts/files/browser/media/action-close-focus.svg create mode 100644 src/vs/workbench/parts/files/browser/media/action-close.svg create mode 100644 src/vs/workbench/parts/files/browser/media/check-inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/check.svg create mode 100644 src/vs/workbench/parts/files/browser/media/closeall.svg create mode 100644 src/vs/workbench/parts/files/browser/media/closeall_inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/collapsed-dark.svg create mode 100644 src/vs/workbench/parts/files/browser/media/collapsed-hc.svg create mode 100644 src/vs/workbench/parts/files/browser/media/collapsed.svg create mode 100644 src/vs/workbench/parts/files/browser/media/expanded-dark.svg create mode 100644 src/vs/workbench/parts/files/browser/media/expanded-hc.svg create mode 100644 src/vs/workbench/parts/files/browser/media/expanded.svg create mode 100644 src/vs/workbench/parts/files/browser/media/explorerviewlet.css create mode 100644 src/vs/workbench/parts/files/browser/media/fileactions.css create mode 100644 src/vs/workbench/parts/files/browser/media/files-dark.svg create mode 100644 src/vs/workbench/parts/files/browser/media/saveall.svg create mode 100644 src/vs/workbench/parts/files/browser/media/saveall_inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/split-editor-horizontal-inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/split-editor-horizontal.svg create mode 100644 src/vs/workbench/parts/files/browser/media/split-editor-vertical-inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/split-editor-vertical.svg create mode 100644 src/vs/workbench/parts/files/browser/media/undo-inverse.svg create mode 100644 src/vs/workbench/parts/files/browser/media/undo.svg create mode 100644 src/vs/workbench/parts/files/browser/saveErrorHandler.ts create mode 100644 src/vs/workbench/parts/files/browser/views/emptyView.ts create mode 100644 src/vs/workbench/parts/files/browser/views/explorerView.ts create mode 100644 src/vs/workbench/parts/files/browser/views/explorerViewer.ts create mode 100644 src/vs/workbench/parts/files/browser/views/openEditorsView.ts create mode 100644 src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts create mode 100644 src/vs/workbench/parts/files/common/dirtyFilesTracker.ts create mode 100644 src/vs/workbench/parts/files/common/editors/fileEditorInput.ts create mode 100644 src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts create mode 100644 src/vs/workbench/parts/files/common/explorerModel.ts create mode 100644 src/vs/workbench/parts/files/common/files.ts create mode 100644 src/vs/workbench/parts/files/test/browser/explorerModel.test.ts create mode 100644 src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts create mode 100644 src/vs/workbench/parts/files/test/browser/fileEditorTracker.test.ts create mode 100644 src/vs/workbench/parts/html/browser/html.contribution.ts create mode 100644 src/vs/workbench/parts/html/browser/htmlPreviewPart.ts create mode 100644 src/vs/workbench/parts/html/browser/media/htmlPreviewPart.css create mode 100644 src/vs/workbench/parts/html/browser/webview-pre.js create mode 100644 src/vs/workbench/parts/html/browser/webview.html create mode 100644 src/vs/workbench/parts/html/browser/webview.ts create mode 100644 src/vs/workbench/parts/html/browser/webviewEditor.ts create mode 100644 src/vs/workbench/parts/html/browser/webviewFindWidget.ts create mode 100644 src/vs/workbench/parts/html/common/htmlInput.ts create mode 100644 src/vs/workbench/parts/markers/browser/markersPanel.ts create mode 100644 src/vs/workbench/parts/markers/browser/markersPanelActions.ts create mode 100644 src/vs/workbench/parts/markers/browser/markersTreeController.ts create mode 100644 src/vs/workbench/parts/markers/browser/markersTreeViewer.ts create mode 100644 src/vs/workbench/parts/markers/browser/markersWorkbenchContributions.ts create mode 100644 src/vs/workbench/parts/markers/browser/media/markers.css create mode 100644 src/vs/workbench/parts/markers/browser/media/status-error-inverse.svg create mode 100644 src/vs/workbench/parts/markers/browser/media/status-error.svg create mode 100644 src/vs/workbench/parts/markers/browser/media/status-info-inverse.svg create mode 100644 src/vs/workbench/parts/markers/browser/media/status-info.svg create mode 100644 src/vs/workbench/parts/markers/browser/media/status-warning-inverse.svg create mode 100644 src/vs/workbench/parts/markers/browser/media/status-warning.svg create mode 100644 src/vs/workbench/parts/markers/common/constants.ts create mode 100644 src/vs/workbench/parts/markers/common/markersModel.ts create mode 100644 src/vs/workbench/parts/markers/common/messages.ts create mode 100644 src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.ts create mode 100644 src/vs/workbench/parts/markers/markers.contribution.ts create mode 100644 src/vs/workbench/parts/markers/test/common/markersModel.test.ts create mode 100644 src/vs/workbench/parts/output/browser/media/clear_output.svg create mode 100644 src/vs/workbench/parts/output/browser/media/clear_output_inverse.svg create mode 100644 src/vs/workbench/parts/output/browser/media/output.css create mode 100644 src/vs/workbench/parts/output/browser/media/output_lock.svg create mode 100644 src/vs/workbench/parts/output/browser/media/output_lock_inverse.svg create mode 100644 src/vs/workbench/parts/output/browser/media/output_unlock.svg create mode 100644 src/vs/workbench/parts/output/browser/media/output_unlock_inverse.svg create mode 100644 src/vs/workbench/parts/output/browser/output.contribution.ts create mode 100644 src/vs/workbench/parts/output/browser/outputActions.ts create mode 100644 src/vs/workbench/parts/output/browser/outputPanel.ts create mode 100644 src/vs/workbench/parts/output/browser/outputServices.ts create mode 100644 src/vs/workbench/parts/output/common/output.ts create mode 100644 src/vs/workbench/parts/output/common/outputLinkComputer.ts create mode 100644 src/vs/workbench/parts/output/common/outputLinkProvider.ts create mode 100644 src/vs/workbench/parts/output/test/bufferedContent.test.ts create mode 100644 src/vs/workbench/parts/output/test/outputLinkProvider.test.ts create mode 100644 src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts create mode 100644 src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts create mode 100644 src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts create mode 100644 src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts create mode 100644 src/vs/workbench/parts/preferences/browser/media/add.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/add_inverse.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/collapseAll.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/collapseAll_inverse.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/collapsed-dark.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/collapsed.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/edit.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/edit_inverse.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/expanded-dark.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/expanded.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/info.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/keybindings.css create mode 100644 src/vs/workbench/parts/preferences/browser/media/keybindingsEditor.css create mode 100644 src/vs/workbench/parts/preferences/browser/media/preferences.css create mode 100644 src/vs/workbench/parts/preferences/browser/media/sort_precedence.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/sort_precedence_inverse.svg create mode 100644 src/vs/workbench/parts/preferences/browser/media/status-error.svg create mode 100644 src/vs/workbench/parts/preferences/browser/preferences.contribution.ts create mode 100644 src/vs/workbench/parts/preferences/browser/preferencesActions.ts create mode 100644 src/vs/workbench/parts/preferences/browser/preferencesEditor.ts create mode 100644 src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts create mode 100644 src/vs/workbench/parts/preferences/browser/preferencesService.ts create mode 100644 src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts create mode 100644 src/vs/workbench/parts/preferences/common/keybindingsEditorModel.ts create mode 100644 src/vs/workbench/parts/preferences/common/preferences.ts create mode 100644 src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts create mode 100644 src/vs/workbench/parts/preferences/common/preferencesModels.ts create mode 100644 src/vs/workbench/parts/preferences/common/smartSnippetInserter.ts create mode 100644 src/vs/workbench/parts/preferences/test/browser/keybindingsEditorContribution.test.ts create mode 100644 src/vs/workbench/parts/preferences/test/common/keybindingsEditorModel.test.ts create mode 100644 src/vs/workbench/parts/preferences/test/common/smartSnippetInserter.test.ts create mode 100644 src/vs/workbench/parts/quickopen/browser/commandsHandler.ts create mode 100644 src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts create mode 100644 src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts create mode 100644 src/vs/workbench/parts/quickopen/browser/helpHandler.ts create mode 100644 src/vs/workbench/parts/quickopen/browser/media/Constant_16x.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/Constant_16x_inverse.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/EnumItem_16x.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/EnumItem_inverse_16x.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode_inverse.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode_inverse.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode_inverse.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode_inverse.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/media/gotoSymbolHandler.css create mode 100644 src/vs/workbench/parts/quickopen/browser/media/symbol-sprite.svg create mode 100644 src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts create mode 100644 src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts create mode 100644 src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts create mode 100644 src/vs/workbench/parts/scm/common/scm.ts create mode 100644 src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts create mode 100644 src/vs/workbench/parts/scm/electron-browser/media/check-inverse.svg create mode 100644 src/vs/workbench/parts/scm/electron-browser/media/check.svg create mode 100644 src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css create mode 100644 src/vs/workbench/parts/scm/electron-browser/media/icon-dark.svg create mode 100644 src/vs/workbench/parts/scm/electron-browser/media/icon-light.svg create mode 100644 src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css create mode 100644 src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts create mode 100644 src/vs/workbench/parts/scm/electron-browser/scmActivity.ts create mode 100644 src/vs/workbench/parts/scm/electron-browser/scmMenus.ts create mode 100644 src/vs/workbench/parts/scm/electron-browser/scmUtil.ts create mode 100644 src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts create mode 100644 src/vs/workbench/parts/search/browser/media/CollapseAll.svg create mode 100644 src/vs/workbench/parts/search/browser/media/CollapseAll_inverse.svg create mode 100644 src/vs/workbench/parts/search/browser/media/Refresh.svg create mode 100644 src/vs/workbench/parts/search/browser/media/Refresh_inverse.svg create mode 100644 src/vs/workbench/parts/search/browser/media/action-query-clear-dark.svg create mode 100644 src/vs/workbench/parts/search/browser/media/action-query-clear.svg create mode 100644 src/vs/workbench/parts/search/browser/media/action-remove-dark.svg create mode 100644 src/vs/workbench/parts/search/browser/media/action-remove-focus.svg create mode 100644 src/vs/workbench/parts/search/browser/media/action-remove.svg create mode 100644 src/vs/workbench/parts/search/browser/media/clear-search-results-dark.svg create mode 100644 src/vs/workbench/parts/search/browser/media/clear-search-results.svg create mode 100644 src/vs/workbench/parts/search/browser/media/configure-inverse.svg create mode 100644 src/vs/workbench/parts/search/browser/media/configure.svg create mode 100644 src/vs/workbench/parts/search/browser/media/ellipsis-inverse.svg create mode 100644 src/vs/workbench/parts/search/browser/media/ellipsis.svg create mode 100644 src/vs/workbench/parts/search/browser/media/excludeSettings-dark.svg create mode 100644 src/vs/workbench/parts/search/browser/media/excludeSettings.svg create mode 100644 src/vs/workbench/parts/search/browser/media/expando-collapsed-dark.svg create mode 100644 src/vs/workbench/parts/search/browser/media/expando-collapsed.svg create mode 100644 src/vs/workbench/parts/search/browser/media/expando-expanded-dark.svg create mode 100644 src/vs/workbench/parts/search/browser/media/expando-expanded.svg create mode 100644 src/vs/workbench/parts/search/browser/media/gitignore-dark.svg create mode 100644 src/vs/workbench/parts/search/browser/media/gitignore.svg create mode 100644 src/vs/workbench/parts/search/browser/media/pattern-dark.svg create mode 100644 src/vs/workbench/parts/search/browser/media/pattern.svg create mode 100644 src/vs/workbench/parts/search/browser/media/replace-all-inverse.svg create mode 100644 src/vs/workbench/parts/search/browser/media/replace-all.svg create mode 100644 src/vs/workbench/parts/search/browser/media/replace-inverse.svg create mode 100644 src/vs/workbench/parts/search/browser/media/replace.svg create mode 100644 src/vs/workbench/parts/search/browser/media/search-dark.svg create mode 100644 src/vs/workbench/parts/search/browser/media/search.contribution.css create mode 100644 src/vs/workbench/parts/search/browser/media/searchviewlet.css create mode 100644 src/vs/workbench/parts/search/browser/openAnythingHandler.ts create mode 100644 src/vs/workbench/parts/search/browser/openFileHandler.ts create mode 100644 src/vs/workbench/parts/search/browser/openSymbolHandler.ts create mode 100644 src/vs/workbench/parts/search/browser/patternInputWidget.ts create mode 100644 src/vs/workbench/parts/search/browser/replaceContributions.ts create mode 100644 src/vs/workbench/parts/search/browser/replaceService.ts create mode 100644 src/vs/workbench/parts/search/browser/search.contribution.ts create mode 100644 src/vs/workbench/parts/search/browser/searchActions.ts create mode 100644 src/vs/workbench/parts/search/browser/searchResultsView.ts create mode 100644 src/vs/workbench/parts/search/browser/searchViewlet.ts create mode 100644 src/vs/workbench/parts/search/browser/searchWidget.ts create mode 100644 src/vs/workbench/parts/search/common/constants.ts create mode 100644 src/vs/workbench/parts/search/common/queryBuilder.ts create mode 100644 src/vs/workbench/parts/search/common/replace.ts create mode 100644 src/vs/workbench/parts/search/common/search.ts create mode 100644 src/vs/workbench/parts/search/common/searchModel.ts create mode 100644 src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts create mode 100644 src/vs/workbench/parts/search/test/browser/searchActions.test.ts create mode 100644 src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts create mode 100644 src/vs/workbench/parts/search/test/common/queryBuilder.test.ts create mode 100644 src/vs/workbench/parts/search/test/common/searchModel.test.ts create mode 100644 src/vs/workbench/parts/search/test/common/searchResult.test.ts create mode 100644 src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts create mode 100644 src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts create mode 100644 src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts create mode 100644 src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts create mode 100644 src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts create mode 100644 src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts create mode 100644 src/vs/workbench/parts/snippets/test/electron-browser/snippetsRegistry.test.ts create mode 100644 src/vs/workbench/parts/snippets/test/electron-browser/snippetsRewrite.test.ts create mode 100644 src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts create mode 100644 src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.ts create mode 100644 src/vs/workbench/parts/surveys/electron-browser/nps.contribution.ts create mode 100644 src/vs/workbench/parts/tasks/browser/quickOpen.ts create mode 100644 src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts create mode 100644 src/vs/workbench/parts/tasks/common/problemCollectors.ts create mode 100644 src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts create mode 100644 src/vs/workbench/parts/tasks/common/taskService.ts create mode 100644 src/vs/workbench/parts/tasks/common/taskSystem.ts create mode 100644 src/vs/workbench/parts/tasks/common/taskTemplates.ts create mode 100644 src/vs/workbench/parts/tasks/common/tasks.ts create mode 100644 src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.ts create mode 100644 src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.ts create mode 100644 src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts create mode 100644 src/vs/workbench/parts/tasks/electron-browser/media/configure-inverse.svg create mode 100644 src/vs/workbench/parts/tasks/electron-browser/media/configure.svg create mode 100644 src/vs/workbench/parts/tasks/electron-browser/media/status-error.svg create mode 100644 src/vs/workbench/parts/tasks/electron-browser/media/status-info.svg create mode 100644 src/vs/workbench/parts/tasks/electron-browser/media/status-warning.svg create mode 100644 src/vs/workbench/parts/tasks/electron-browser/media/task.contribution.css create mode 100644 src/vs/workbench/parts/tasks/electron-browser/media/task.svg create mode 100644 src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts create mode 100644 src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts create mode 100644 src/vs/workbench/parts/tasks/node/processRunnerDetector.ts create mode 100644 src/vs/workbench/parts/tasks/node/processTaskSystem.ts create mode 100644 src/vs/workbench/parts/tasks/node/taskConfiguration.ts create mode 100644 src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts create mode 100644 src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts create mode 100644 src/vs/workbench/parts/terminal/browser/terminalWidgetManager.ts create mode 100644 src/vs/workbench/parts/terminal/common/terminal.ts create mode 100644 src/vs/workbench/parts/terminal/common/terminalService.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/media/configure-inverse.svg create mode 100644 src/vs/workbench/parts/terminal/electron-browser/media/configure.svg create mode 100644 src/vs/workbench/parts/terminal/electron-browser/media/kill-inverse.svg create mode 100644 src/vs/workbench/parts/terminal/electron-browser/media/kill.svg create mode 100644 src/vs/workbench/parts/terminal/electron-browser/media/new-inverse.svg create mode 100644 src/vs/workbench/parts/terminal/electron-browser/media/new.svg create mode 100644 src/vs/workbench/parts/terminal/electron-browser/media/scrollbar.css create mode 100644 src/vs/workbench/parts/terminal/electron-browser/media/terminal.css create mode 100644 src/vs/workbench/parts/terminal/electron-browser/media/widgets.css create mode 100644 src/vs/workbench/parts/terminal/electron-browser/media/xterm.css create mode 100644 src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/terminal.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/terminalService.ts create mode 100644 src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts create mode 100644 src/vs/workbench/parts/terminal/node/terminalProcess.ts create mode 100644 src/vs/workbench/parts/terminal/test/electron-browser/terminalColorRegistry.test.ts create mode 100644 src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts create mode 100644 src/vs/workbench/parts/terminal/test/electron-browser/terminalInstance.test.ts create mode 100644 src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts create mode 100644 src/vs/workbench/parts/terminal/test/electron-browser/terminalPanel.test.ts create mode 100644 src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts create mode 100644 src/vs/workbench/parts/themes/test/electron-browser/fixtures/foo.js create mode 100644 src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts create mode 100644 src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.ts create mode 100644 src/vs/workbench/parts/update/electron-browser/media/markdown.css create mode 100644 src/vs/workbench/parts/update/electron-browser/media/update.contribution.css create mode 100644 src/vs/workbench/parts/update/electron-browser/media/update.svg create mode 100644 src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts create mode 100644 src/vs/workbench/parts/update/electron-browser/releaseNotesInput.ts create mode 100644 src/vs/workbench/parts/update/electron-browser/update.contribution.ts create mode 100644 src/vs/workbench/parts/update/electron-browser/update.ts create mode 100644 src/vs/workbench/parts/views/browser/media/views.css create mode 100644 src/vs/workbench/parts/views/browser/treeView.ts create mode 100644 src/vs/workbench/parts/views/browser/views.ts create mode 100644 src/vs/workbench/parts/views/browser/viewsExtensionPoint.ts create mode 100644 src/vs/workbench/parts/views/browser/viewsRegistry.ts create mode 100644 src/vs/workbench/parts/views/common/views.ts create mode 100644 src/vs/workbench/parts/watermark/electron-browser/watermark.css create mode 100644 src/vs/workbench/parts/watermark/electron-browser/watermark.ts create mode 100644 src/vs/workbench/parts/welcome/code-icon.svg create mode 100644 src/vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution.ts create mode 100644 src/vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.ts create mode 100644 src/vs/workbench/parts/welcome/gettingStarted/test/common/gettingStarted.test.ts create mode 100644 src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette-dark.svg create mode 100644 src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette.svg create mode 100644 src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.css create mode 100644 src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts create mode 100644 src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts create mode 100644 src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts create mode 100644 src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css create mode 100644 src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts create mode 100644 src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts create mode 100644 src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md create mode 100644 src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts create mode 100644 src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts create mode 100644 src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.css create mode 100644 src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts create mode 100644 src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts create mode 100644 src/vs/workbench/parts/welcome/walkThrough/node/walkThroughInput.ts create mode 100644 src/vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils.ts create mode 100644 src/vs/workbench/services/activity/common/activityBarService.ts create mode 100644 src/vs/workbench/services/backup/common/backup.ts create mode 100644 src/vs/workbench/services/backup/node/backupFileService.ts create mode 100644 src/vs/workbench/services/backup/test/node/backupFileService.test.ts create mode 100644 src/vs/workbench/services/configuration/common/configuration.ts create mode 100644 src/vs/workbench/services/configuration/common/configurationEditing.ts create mode 100644 src/vs/workbench/services/configuration/common/configurationModels.ts create mode 100644 src/vs/workbench/services/configuration/common/jsonEditing.ts create mode 100644 src/vs/workbench/services/configuration/node/configuration.ts create mode 100644 src/vs/workbench/services/configuration/node/configurationEditingService.ts create mode 100644 src/vs/workbench/services/configuration/node/jsonEditingService.ts create mode 100644 src/vs/workbench/services/configuration/test/common/configurationModels.test.ts create mode 100644 src/vs/workbench/services/configuration/test/node/configuration.test.ts create mode 100644 src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts create mode 100644 src/vs/workbench/services/configurationResolver/common/configurationResolver.ts create mode 100644 src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts create mode 100644 src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts create mode 100644 src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts create mode 100644 src/vs/workbench/services/crashReporter/common/crashReporterService.ts create mode 100644 src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts create mode 100644 src/vs/workbench/services/editor/browser/editorService.ts create mode 100644 src/vs/workbench/services/editor/common/editorService.ts create mode 100644 src/vs/workbench/services/editor/test/browser/editorService.test.ts create mode 100644 src/vs/workbench/services/extensions/electron-browser/extensionHost.ts create mode 100644 src/vs/workbench/services/extensions/electron-browser/extensionPoints.ts create mode 100644 src/vs/workbench/services/extensions/electron-browser/extensionService.ts create mode 100644 src/vs/workbench/services/extensions/node/barrier.ts create mode 100644 src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts create mode 100644 src/vs/workbench/services/extensions/node/lazyPromise.ts create mode 100644 src/vs/workbench/services/extensions/node/rpcProtocol.ts create mode 100644 src/vs/workbench/services/files/electron-browser/fileService.ts create mode 100644 src/vs/workbench/services/files/electron-browser/remoteFileService.ts create mode 100644 src/vs/workbench/services/files/node/fileService.ts create mode 100644 src/vs/workbench/services/files/node/watcher/common.ts create mode 100644 src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts create mode 100644 src/vs/workbench/services/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts create mode 100644 src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts create mode 100644 src/vs/workbench/services/files/node/watcher/nsfw/watcherApp.ts create mode 100644 src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts create mode 100644 src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts create mode 100644 src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts create mode 100644 src/vs/workbench/services/files/node/watcher/unix/watcher.ts create mode 100644 src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts create mode 100644 src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts create mode 100644 src/vs/workbench/services/files/node/watcher/unix/watcherService.ts create mode 100644 src/vs/workbench/services/files/node/watcher/win32/CodeHelper.exe create mode 100644 src/vs/workbench/services/files/node/watcher/win32/CodeHelper.md create mode 100644 src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts create mode 100644 src/vs/workbench/services/files/node/watcher/win32/watcherService.ts create mode 100644 src/vs/workbench/services/files/test/node/fileService.test.ts create mode 100644 src/vs/workbench/services/files/test/node/fixtures/resolver/examples/company.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/resolver/examples/conway.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/resolver/examples/employee.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/resolver/examples/small.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/resolver/index.html create mode 100644 src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/company.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/conway.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/employee.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/small.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/resolver/site.css create mode 100644 src/vs/workbench/services/files/test/node/fixtures/service/binary.txt create mode 100644 src/vs/workbench/services/files/test/node/fixtures/service/deep/company.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/service/deep/conway.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/service/deep/employee.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/service/deep/small.js create mode 100644 src/vs/workbench/services/files/test/node/fixtures/service/index.html create mode 100644 src/vs/workbench/services/files/test/node/fixtures/service/small.txt create mode 100644 src/vs/workbench/services/files/test/node/fixtures/service/some_utf16le.css create mode 100644 src/vs/workbench/services/files/test/node/fixtures/service/some_utf8_bom.txt create mode 100644 src/vs/workbench/services/files/test/node/resolver.test.ts create mode 100644 src/vs/workbench/services/files/test/node/utils.ts create mode 100644 src/vs/workbench/services/files/test/node/watcher.test.ts create mode 100644 src/vs/workbench/services/group/common/groupService.ts create mode 100644 src/vs/workbench/services/history/browser/history.ts create mode 100644 src/vs/workbench/services/history/common/history.ts create mode 100644 src/vs/workbench/services/keybinding/common/keybindingEditing.ts create mode 100644 src/vs/workbench/services/keybinding/common/keybindingIO.ts create mode 100644 src/vs/workbench/services/keybinding/common/keyboardMapper.ts create mode 100644 src/vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper.ts create mode 100644 src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts create mode 100644 src/vs/workbench/services/keybinding/common/scanCode.ts create mode 100644 src/vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts create mode 100644 src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts create mode 100644 src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts create mode 100644 src/vs/workbench/services/message/browser/media/messageList.css create mode 100644 src/vs/workbench/services/message/browser/messageList.ts create mode 100644 src/vs/workbench/services/message/browser/messageService.ts create mode 100644 src/vs/workbench/services/message/electron-browser/messageService.ts create mode 100644 src/vs/workbench/services/mode/common/workbenchModeService.ts create mode 100644 src/vs/workbench/services/panel/common/panelService.ts create mode 100644 src/vs/workbench/services/part/common/partService.ts create mode 100644 src/vs/workbench/services/progress/browser/media/progress.svg create mode 100644 src/vs/workbench/services/progress/browser/media/progressService2.css create mode 100644 src/vs/workbench/services/progress/browser/progressService.ts create mode 100644 src/vs/workbench/services/progress/browser/progressService2.ts create mode 100644 src/vs/workbench/services/progress/test/progressService.test.ts create mode 100644 src/vs/workbench/services/scm/common/scm.ts create mode 100644 src/vs/workbench/services/scm/common/scmService.ts create mode 100644 src/vs/workbench/services/search/node/fileSearch.ts create mode 100644 src/vs/workbench/services/search/node/rawSearchService.ts create mode 100644 src/vs/workbench/services/search/node/ripgrepTextSearch.ts create mode 100644 src/vs/workbench/services/search/node/search.ts create mode 100644 src/vs/workbench/services/search/node/searchApp.ts create mode 100644 src/vs/workbench/services/search/node/searchIpc.ts create mode 100644 src/vs/workbench/services/search/node/searchService.ts create mode 100644 src/vs/workbench/services/search/node/textSearch.ts create mode 100644 src/vs/workbench/services/search/node/textSearchWorkerProvider.ts create mode 100644 src/vs/workbench/services/search/node/worker/searchWorker.ts create mode 100644 src/vs/workbench/services/search/node/worker/searchWorkerApp.ts create mode 100644 src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts create mode 100644 src/vs/workbench/services/search/test/node/fixtures/binary.wuff create mode 100644 src/vs/workbench/services/search/test/node/fixtures/examples/NullPoinderException.js create mode 100644 src/vs/workbench/services/search/test/node/fixtures/examples/company.js create mode 100644 src/vs/workbench/services/search/test/node/fixtures/examples/employee.js create mode 100644 src/vs/workbench/services/search/test/node/fixtures/examples/small.js create mode 100644 src/vs/workbench/services/search/test/node/fixtures/examples/subfolder/anotherfolder/anotherfile.txt create mode 100644 src/vs/workbench/services/search/test/node/fixtures/examples/subfolder/subfile.txt create mode 100644 src/vs/workbench/services/search/test/node/fixtures/index.html create mode 100644 src/vs/workbench/services/search/test/node/fixtures/more/file.txt create mode 100644 src/vs/workbench/services/search/test/node/fixtures/site.css create mode 100644 src/vs/workbench/services/search/test/node/fixtures/site.less create mode 100644 src/vs/workbench/services/search/test/node/fixtures/some_utf16be.css create mode 100644 src/vs/workbench/services/search/test/node/fixtures/some_utf16le.css create mode 100644 src/vs/workbench/services/search/test/node/fixtures/üm laut汉语/汉语.txt create mode 100644 src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts create mode 100644 src/vs/workbench/services/search/test/node/search.test.ts create mode 100644 src/vs/workbench/services/search/test/node/searchService.test.ts create mode 100644 src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts create mode 100644 src/vs/workbench/services/telemetry/common/workspaceStats.ts create mode 100644 src/vs/workbench/services/telemetry/test/workspaceStats.test.ts create mode 100644 src/vs/workbench/services/textMate/electron-browser/OSSREADME.json create mode 100644 src/vs/workbench/services/textMate/electron-browser/TMGrammars.ts create mode 100644 src/vs/workbench/services/textMate/electron-browser/TMHelper.ts create mode 100644 src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts create mode 100644 src/vs/workbench/services/textMate/electron-browser/textMateService.ts create mode 100644 src/vs/workbench/services/textfile/common/textFileEditorModel.ts create mode 100644 src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts create mode 100644 src/vs/workbench/services/textfile/common/textFileService.ts create mode 100644 src/vs/workbench/services/textfile/common/textfiles.ts create mode 100644 src/vs/workbench/services/textfile/electron-browser/modelBuilder.ts create mode 100644 src/vs/workbench/services/textfile/electron-browser/textFileService.ts create mode 100644 src/vs/workbench/services/textfile/test/modelBuilder.test.ts create mode 100644 src/vs/workbench/services/textfile/test/modelBuilderAuto.test.ts create mode 100644 src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts create mode 100644 src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts create mode 100644 src/vs/workbench/services/textfile/test/textFileService.test.ts create mode 100644 src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts create mode 100644 src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts create mode 100644 src/vs/workbench/services/themes/common/colorThemeSchema.ts create mode 100644 src/vs/workbench/services/themes/common/fileIconThemeSchema.ts create mode 100644 src/vs/workbench/services/themes/common/workbenchThemeService.ts create mode 100644 src/vs/workbench/services/themes/electron-browser/colorThemeData.ts create mode 100644 src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts create mode 100644 src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts create mode 100644 src/vs/workbench/services/thread/common/threadService.ts create mode 100644 src/vs/workbench/services/thread/electron-browser/threadService.ts create mode 100644 src/vs/workbench/services/thread/node/abstractThreadService.ts create mode 100644 src/vs/workbench/services/thread/node/extHostThreadService.ts create mode 100644 src/vs/workbench/services/timer/common/timerService.ts create mode 100644 src/vs/workbench/services/timer/node/timerService.ts create mode 100644 src/vs/workbench/services/title/common/titleService.ts create mode 100644 src/vs/workbench/services/untitled/common/untitledEditorService.ts create mode 100644 src/vs/workbench/services/viewlet/browser/viewlet.ts create mode 100644 src/vs/workbench/services/viewlet/browser/viewletService.ts create mode 100644 src/vs/workbench/services/workspace/common/workspaceEditing.ts create mode 100644 src/vs/workbench/services/workspace/node/workspaceEditingService.ts create mode 100644 src/vs/workbench/services/workspace/node/workspaceMigrationService.ts create mode 100644 src/vs/workbench/test/browser/actionRegistry.test.ts create mode 100644 src/vs/workbench/test/browser/editorStacksModel.test.ts create mode 100644 src/vs/workbench/test/browser/part.test.ts create mode 100644 src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts create mode 100644 src/vs/workbench/test/browser/quickopen.test.ts create mode 100644 src/vs/workbench/test/browser/viewlet.test.ts create mode 100644 src/vs/workbench/test/common/editor/editor.test.ts create mode 100644 src/vs/workbench/test/common/editor/editorDiffModel.test.ts create mode 100644 src/vs/workbench/test/common/editor/editorInput.test.ts create mode 100644 src/vs/workbench/test/common/editor/editorModel.test.ts create mode 100644 src/vs/workbench/test/common/editor/editorOptions.test.ts create mode 100644 src/vs/workbench/test/common/editor/rangeDecorations.test.ts create mode 100644 src/vs/workbench/test/common/editor/resourceEditorInput.test.ts create mode 100644 src/vs/workbench/test/common/editor/untitledEditor.test.ts create mode 100644 src/vs/workbench/test/common/memento.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostDocumentData.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostDocumentsAndEditors.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostFileSystemEventService.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/mainThreadCommands.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/mainThreadDocuments.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts create mode 100644 src/vs/workbench/test/electron-browser/api/mock.ts create mode 100644 src/vs/workbench/test/electron-browser/api/testThreadService.ts create mode 100644 src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts create mode 100644 src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts create mode 100644 src/vs/workbench/test/workbenchTestServices.ts create mode 100644 src/vs/workbench/workbench.main.css create mode 100644 src/vs/workbench/workbench.main.nls.js create mode 100644 src/vs/workbench/workbench.main.ts create mode 100644 test/OSSREADME.json create mode 100644 test/README.md create mode 100644 test/all.js create mode 100644 test/assert.js create mode 100644 test/browser.js create mode 100644 test/electron/index.js create mode 100644 test/electron/renderer.html create mode 100644 test/electron/renderer.js create mode 100644 test/index.html create mode 100644 test/mocha.opts create mode 100644 test/smoke/.gitignore create mode 100644 test/smoke/Audit.md create mode 100644 test/smoke/README.md create mode 100644 test/smoke/package.json create mode 100644 test/smoke/src/areas/common.ts create mode 100644 test/smoke/src/areas/configuration-views.ts create mode 100644 test/smoke/src/areas/css.ts create mode 100644 test/smoke/src/areas/data-loss.ts create mode 100644 test/smoke/src/areas/extensions.ts create mode 100644 test/smoke/src/areas/first-experience.ts create mode 100644 test/smoke/src/areas/git.ts create mode 100644 test/smoke/src/areas/integrated-terminal.ts create mode 100644 test/smoke/src/areas/javascript-debug.ts create mode 100644 test/smoke/src/areas/javascript.ts create mode 100644 test/smoke/src/areas/localization.ts create mode 100644 test/smoke/src/areas/search.ts create mode 100644 test/smoke/src/areas/statusbar.ts create mode 100644 test/smoke/src/areas/tasks.ts create mode 100644 test/smoke/src/helpers/screenshot.ts create mode 100644 test/smoke/src/helpers/utilities.ts create mode 100644 test/smoke/src/main.ts create mode 100644 test/smoke/src/mocha-runner.ts create mode 100644 test/smoke/src/spectron/application.ts create mode 100644 test/smoke/src/spectron/client.ts create mode 100644 test/smoke/src/test.ts create mode 100644 test/smoke/src/tests/configuration-views.ts create mode 100644 test/smoke/src/tests/css.ts create mode 100644 test/smoke/src/tests/data-loss.ts create mode 100644 test/smoke/src/tests/data-migration.ts create mode 100644 test/smoke/src/tests/explorer.ts create mode 100644 test/smoke/src/tests/extensions.ts create mode 100644 test/smoke/src/tests/git.ts create mode 100644 test/smoke/src/tests/integrated-terminal.ts create mode 100644 test/smoke/src/tests/javascript-debug.ts create mode 100644 test/smoke/src/tests/javascript.ts create mode 100644 test/smoke/src/tests/localization.ts create mode 100644 test/smoke/src/tests/multiroot.ts create mode 100644 test/smoke/src/tests/search.ts create mode 100644 test/smoke/src/tests/statusbar.ts create mode 100644 test/smoke/src/tests/tasks.ts create mode 100644 test/smoke/tsconfig.json create mode 100644 tsfmt.json create mode 100644 tslint.json create mode 100644 typings.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..40381bd6a7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Tab indentation +[*] +indent_style = tab +indent_size = 4 +trim_trailing_whitespace = true + +# The indent size used in the `package.json` file cannot be changed +# https://github.com/npm/npm/pull/3180#issuecomment-16336516 +[{.travis.yml,npm-shrinkwrap.json,package.json}] +indent_style = space +indent_size = 2 diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000000..2445c0c728 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,14 @@ +{ + "env": { + "node": true, + "es6": true + }, + "rules": { + "no-console": 0, + "no-cond-assign": 0, + "no-unused-vars": 1, + "no-extra-semi": "warn", + "semi": "warn" + }, + "extends": "eslint:recommended" +} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.gitignore b/.gitignore index 940794e60f..8cd3bf1828 100644 --- a/.gitignore +++ b/.gitignore @@ -1,288 +1,28 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ -**/Properties/launchSettings.json - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj +.DS_Store +npm-debug.log +Thumbs.db +.DS_Store +*.dat +*.db +*.exe *.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages *.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat +*.orig +*.vsix +*BROWSE.VC* +sqltoolsservice +coverage +test-reports +.vscode-test node_modules/ - -# Typescript v1 declaration files -typings/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs +.build/ +out/ +out-build/ +out-editor/ +out-editor-min/ +out-monaco-editor-core/ +out-vscode/ +out-vscode-min/ +build/node_modules +coverage/ +_site diff --git a/.mention-bot b/.mention-bot new file mode 100644 index 0000000000..ea1a170775 --- /dev/null +++ b/.mention-bot @@ -0,0 +1,7 @@ +{ + "maxReviewers": 2, + "requiredOrgs": ["Microsoft"], + "skipAlreadyAssignedPR": true, + "skipAlreadyMentionedPR": true, + "skipCollaboratorPR": true +} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..1e013e67a7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,50 @@ +sudo: false +language: cpp + +os: + - linux + - osx + +notifications: + email: false + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.9 + - g++-4.9 + - gcc-4.9-multilib + - g++-4.9-multilib + - zip + - libgtk2.0-0 + - libx11-dev + - libxkbfile-dev + - libsecret-1-dev + +before_install: + - git submodule update --init --recursive + - git clone --depth 1 https://github.com/creationix/nvm.git ./.nvm + - source ./.nvm/nvm.sh + - nvm install 7.4.0 + - nvm use 7.4.0 + - npm config set python `which python` + - npm install -g gulp + - if [ $TRAVIS_OS_NAME == "linux" ]; then + export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0; + sh -e /etc/init.d/xvfb start; + sleep 3; + fi + +install: + - ./scripts/npm.sh install + +script: + - gulp electron --silent + - gulp compile --silent --max_old_space_size=4096 + - gulp optimize-vscode --silent --max_old_space_size=4096 + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./scripts/test.sh --coverage --reporter dot; else ./scripts/test.sh --reporter dot; fi + +after_success: + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then node_modules/.bin/coveralls < .build/coverage/lcov.info; fi diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..6cb9439d5c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "eg2.tslint", + "dbaeumer.vscode-eslint", + "msjsdiag.debugger-for-chrome" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..6ce3298f7e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,128 @@ +{ + "version": "0.1.0", + "configurations": [ + + { + "type": "node", + "request": "launch", + "name": "Gulp Build", + "program": "${workspaceRoot}/node_modules/gulp/bin/gulp.js", + "stopOnEntry": true, + "args": [ + "watch-extension:json-client" + ], + "cwd": "${workspaceRoot}" + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Extension Host", + "protocol": "inspector", + "port": 5870, + "restart": true, + "outFiles": [ + "${workspaceRoot}/out/**/*.js" + ] + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Shared Process", + "protocol": "inspector", + "port": 5871, + "outFiles": [ + "${workspaceRoot}/out/**/*.js" + ] + }, + { + "type": "node", + "request": "attach", + "protocol": "inspector", + "name": "Attach to Search process", + "port": 7890, + "outFiles": [ + "${workspaceRoot}/out/**/*.js" + ] + }, + { + "type": "node", + "request": "attach", + "name": "Attach to CLI Process", + "protocol": "inspector", + "port": 5874, + "outFiles": [ + "${workspaceRoot}/out/**/*.js" + ] + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Main Process", + "protocol": "inspector", + "port": 5875, + "outFiles": [ + "${workspaceRoot}/out/**/*.js" + ] + }, + { + "type": "chrome", + "request": "attach", + "name": "Attach to sqlops", + "port": 9222 + }, + { + "type": "chrome", + "request": "launch", + "name": "Launch sqlops", + "windows": { + "runtimeExecutable": "${workspaceRoot}/scripts/sql.bat" + }, + "osx": { + "runtimeExecutable": "${workspaceRoot}/scripts/sql.sh" + }, + "linux": { + "runtimeExecutable": "${workspaceRoot}/scripts/sql.sh" + }, + "urlFilter": "*index.html*", + "runtimeArgs": [ + "--inspect=5875" + ], + "webRoot": "${workspaceRoot}" + }, + { + "type": "node", + "request": "launch", + "name": "Unit Tests", + "protocol": "inspector", + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + "runtimeExecutable": "${workspaceRoot}/.build/electron/sqlops.app/Contents/MacOS/Electron", + "windows": { + "runtimeExecutable": "${workspaceRoot}/.build/electron/sqlops.exe" + }, + "linux": { + "runtimeExecutable": "${workspaceRoot}/.build/electron/sqlops" + }, + "stopOnEntry": false, + "args": [ + "--timeout", + "2000" + ], + "cwd": "${workspaceRoot}", + "env": { + "ELECTRON_RUN_AS_NODE": "true" + }, + "outFiles": [ + "${workspaceRoot}/out/**/*.js" + ] + } + ], + "compounds": [ + { + "name": "Debug sqlops Main and Renderer", + "configurations": [ + "Launch sqlops", + "Attach to Main Process" + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..d41133e414 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,35 @@ +{ + "editor.insertSpaces": false, + "files.eol": "\n", + "files.trimTrailingWhitespace": true, + "files.exclude": { + ".git": true, + ".build": true, + "**/.DS_Store": true, + "build/**/*.js": { + "when": "$(basename).ts" + } + }, + "search.exclude": { + "**/node_modules": true, + "**/bower_components": true, + ".build/**": true, + "out*/**": true, + "i18n/**": true, + "extensions/**/out/**": true + }, + "tslint.enable": true, + "lcov.path": [ + "./.build/coverage/lcov.info", + "./.build/coverage-single/lcov.info" + ], + "lcov.watch": [ + { + "pattern": "**/*.test.js", + "command": "${workspaceRoot}/scripts/test.sh --coverage --run ${file}", + "windows": { + "command": "${workspaceRoot}\\scripts\\test.bat --coverage --run ${file}" + } + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..bacb3a27e8 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,72 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "label": "Build VS Code", + "group": "build", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "problemMatcher": { + "owner": "typescript", + "applyTo": "closedDocuments", + "fileLocation": [ + "absolute" + ], + "pattern": { + "regexp": "Error: ([^(]+)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\): (.*)$", + "file": 1, + "location": 2, + "message": 3 + }, + "background": { + "beginsPattern": "Starting compilation", + "endsPattern": "Finished compilation" + } + } + }, + { + "type": "gulp", + "task": "tslint", + "label": "Run tslint", + "problemMatcher": [ + "$tslint4" + ] + }, + { + "taskName": "Run tests", + "type": "shell", + "command": "./scripts/test.sh", + "windows": { + "command": ".\\scripts\\test.bat" + }, + "group": "test", + "presentation": { + "echo": true, + "reveal": "always" + } + }, + { + "taskName": "Run Dev", + "type": "shell", + "command": "./scripts/code.sh", + "windows": { + "command": ".\\scripts\\code.bat" + }, + "problemMatcher": [] + }, + { + "type": "gulp", + "task": "electron", + "label": "Download electron" + }, + { + "type": "gulp", + "task": "hygiene", + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..65c8a42b8d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..f9252a904b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,54 @@ +## Contributing Issues + +### Before Submitting an Issue +First, please do a search in [open issues](https://github.com/Microsoft/vscode/issues) to see if the issue or feature request has already been filed. Use this [query](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) to search for the most popular feature requests. + +If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment. + +👍 - upvote + +👎 - downvote + +The VS Code project is distributed across multiple repositories, try to file the issue against the correct repository [Related Projects](https://github.com/Microsoft/vscode/wiki/Related-Projects). + +If your issue is a question then please ask the question on [Stack Overflow](https://stackoverflow.com/questions/tagged/vscode) using the tag `vscode`. + +If you cannot find an existing issue that describes your bug or feature, submit an issue using the guidelines below. + +## Writing Good Bug Reports and Feature Requests + +File a single issue per problem and feature request. + +* Do not enumerate multiple bugs or feature requests in the same issue. +* Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar, but have different causes. + +The more information you can provide, the more likely someone will be successful reproducing the issue and finding a fix. + +Please include the following with each issue. + +* Version of VS Code +* List of extensions that you have installed. + +> **Tip:** You can easily add the list of extensions by creating the issue using `Report Issues` from VS Code's Help menu. + +* Reproducible steps (1... 2... 3...) and what you expected versus what you actually saw. +* Images, animations, or a link to a video. +* A code snippet that demonstrates the issue or a link to a code repository we can easily pull down onto our machine to recreate the issue. + +> **Note:** Because we need to copy and paste the code snippet, including a code snippet as a media file (i.e. .gif) is not sufficient. + +* Errors in the Dev Tools Console (Help | Toggle Developer Tools) + +Please remember to do the following: + +* Search the issue repository to see if there exists a duplicate. +* Recreate the issue after disabling all extensions (see the [docs for how to do this](https://code.visualstudio.com/docs/editor/extension-gallery#_disable-an-extension)). If you find the issue is caused by an extension you have installed please file an issue on the extension respectively. +* Simplify your code around the issue so we can better isolate the problem. + +Don't feel bad if we can't reproduce the issue and ask for more information! + +Finally, this is our [issue tracking](https://github.com/Microsoft/vscode/wiki/Issue-Tracking) work flow that describes what happens once you submitted an issue. + +## Contributing Fixes +If you are interested in fixing issues and contributing directly to the code base, +please see the document [How to Contribute](https://github.com/Microsoft/vscode/wiki/How-to-Contribute). diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000..a0127eb852 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +MICROSOFT SOFTWARE LICENSE TERMS + +MICROSOFT SQL OPERATIONS STUDIO + +Microsoft Corporation (“Microsoft”) grants you a nonexclusive, perpetual, +royalty-free right to use, copy, and modify the software code provided by us +("Software Code"). You may not sublicense the Software Code or any use of it +(except to your affiliates and to vendors to perform work on your behalf) +through distribution, network access, service agreement, lease, rental, or +otherwise. Unless applicable law gives you more rights, Microsoft reserves all +other rights not expressly granted herein, whether by implication, estoppel or +otherwise.  +  +THE SOFTWARE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL MICROSOFT OR ITS +LICENSORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +THE USE OF THE SAMPLE CODE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/OSSREADME.json b/OSSREADME.json new file mode 100644 index 0000000000..8d13681d41 --- /dev/null +++ b/OSSREADME.json @@ -0,0 +1,775 @@ +// Listing in here platform dependencies that come in at build time + +[{ + "isLicense": true, + "name": "async-each", + "repositoryURL": "https://github.com/paulmillr/async-each", + "license": "MIT", + "licenseDetail": [ + "The MIT License (MIT)", + "", + "Copyright (c) 2016 Paul Miller [(paulmillr.com)](http://paulmillr.com)", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the “Software”), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", + "THE SOFTWARE." + ], + "isProd": true +}, +{ + "isLicense": true, + "name": "chokidar", + "repositoryURL": "https://github.com/paulmillr/chokidar", + "license": "MIT", + "licenseDetail": [ + "The MIT license.", + "", + "Copyright (c) 2012 - 2016 Paul Miller [paulmillr.com](http://paulmillr.com) & Elan Shanker", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of", + "this software and associated documentation files (the \"Software\"), to deal in", + "the Software without restriction, including without limitation the rights to", + "use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies", + "of the Software, and to permit persons to whom the Software is furnished to do", + "so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ], + "isProd": true +}, +{ + "name": "chromium", + "version": "58.0.3029.110", + "repositoryURL": "http://www.chromium.org/Home", + "licenseDetail": [ + "BSD License", + "", + "Copyright 2015 The Chromium Authors. All rights reserved.", + "", + "Redistribution and use in source and binary forms, with or without modification,", + "are permitted provided that the following conditions are met:", + "", + " * Redistributions of source code must retain the above copyright notice, this", + " list of conditions and the following disclaimer.", + "", + " * Redistributions in binary form must reproduce the above copyright notice,", + " this list of conditions and the following disclaimer in the documentation", + " and/or other materials provided with the distribution.", + "", + " * Neither the name Google Inc. nor the names of its contributors may be used to", + " endorse or promote products derived from this software without specific", + " prior written permission.", + "", + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND", + "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED", + "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE", + "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR", + "ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES", + "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;", + "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON", + "ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT", + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS", + "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ], + "isProd": true +}, +{ + "name": "libchromiumcontent", + "version": "58.0.3029.110", + "license": "MIT", + "repositoryURL": "https://github.com/electron/libchromiumcontent", + "isProd": true +}, +{ + "name": "nodejs", + "version": "7.9.0", + "repositoryURL": "https://github.com/nodejs/node", + "isProd": true +}, +{ + "name": "electron", + "version": "1.7.3", + "license": "MIT", + "repositoryURL": "https://github.com/electron/electron", + "isProd": true +}, +{ + "name": "inno setup", + "version": "5.5.6", + "repositoryURL": "https://github.com/jrsoftware/issrc", + "isProd": true +}, +{ + "isLicense": true, + "name": "ripgrep", + "repositoryURL": "https://github.com/BurntSushi/ripgrep", + "license": "MIT", + "licenseDetail": [ + "The MIT License (MIT)", + "", + "Copyright (c) 2015 Andrew Gallant", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of", + "this software and associated documentation files (the \"Software\"), to deal in", + "the Software without restriction, including without limitation the rights to", + "use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies", + "of the Software, and to permit persons to whom the Software is furnished to do", + "so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ], + "isProd": true +}, +{ + "isLicense": true, + "name": "jschardet", + "repositoryURL": "https://github.com/aadsm/jschardet", + "license": "LGPL", + "licenseDetail": [ + "Chardet was originally ported from C++ by Mark Pilgrim. It is now maintained", + " by Dan Blanchard and Ian Cordasco, and was formerly maintained by Erik Rose.", + " JSChardet was ported from python to JavaScript by António Afonso ", + " (https://github.com/aadsm/jschardet) and transformed into an npm package by ", + "Markus Ast (https://github.com/brainafk)", + "", + "GNU LESSER GENERAL PUBLIC LICENSE", + "\t\t Version 2.1, February 1999", + "", + " Copyright (C) 1991,", + "1999 Free Software Foundation, Inc.", + " 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA", + " Everyone is permitted to copy and distribute verbatim copies", + " of this license document, but changing it is not allowed.", + "", + "[This is the first released version of the Lesser GPL. It also counts", + " as the successor of the GNU Library Public License, version 2, hence", + " the version number 2.1.", + "]", + "", + "\t\t\t Preamble", + "", + " The licenses for most software are designed to take away your", + "freedom to share and change it. By contrast, the GNU General Public", + "Licenses are intended to guarantee your freedom to share and change", + "free software--to make sure the software is free for all its users.", + "", + " This license, the Lesser General Public License, applies to some", + "specially designated software packages--typically libraries--of the", + "Free Software Foundation and other authors who decide to use it. You", + "can use it too, but we suggest you first think carefully about whether", + "this license or the ordinary General Public License is the better", + "strategy to use in any particular case, based on the explanations below.", + "", + " When we speak of free software, we are referring to freedom of use,", + "not price. Our General Public Licenses are designed to make sure that", + "you have the freedom to distribute copies of free software (and charge", + "for this service if you wish); that you receive source code or can get", + "it if you want it; that you can change the software and use pieces of", + "it in new free programs; and that you are informed that you can do", + "these things.", + "", + " To protect your rights, we need to make restrictions that forbid", + "distributors to deny you these rights or to ask you to surrender these", + "rights. These restrictions translate to certain responsibilities for", + "you if you distribute copies of the library or if you modify it.", + "", + " For example, if you distribute copies of the library, whether gratis", + "or for a fee, you must give the recipients all the rights that we gave", + "you. You must make sure that they, too, receive or can get the source", + "code. If you link other code with the library, you must provide", + "complete object files to the recipients, so that they can relink them", + "with the library after making changes to the library and recompiling", + "it. And you must show them these terms so they know their rights.", + "", + " We protect your rights with a two-step method: (1) we copyright the", + "library, and (2) we offer you this license, which gives you legal", + "permission to copy, distribute and/or modify the library.", + "", + " To protect each distributor, we want to make it very clear that", + "there is no warranty for the free library. Also, if the library is", + "modified by someone else and passed on, the recipients should know", + "that what they have is not the original version, so that the original", + "author's reputation will not be affected by problems that might be", + "introduced by others.", + "", + " Finally, software patents pose a constant threat to the existence of", + "any free program. We wish to make sure that a company cannot", + "effectively restrict the users of a free program by obtaining a", + "restrictive license from a patent holder. Therefore, we insist that", + "any patent license obtained for a version of the library must be", + "consistent with the full freedom of use specified in this license.", + "", + " Most GNU software, including some libraries, is covered by the", + "ordinary GNU General Public License. This license, the GNU Lesser", + "General Public License, applies to certain designated libraries, and", + "is quite different from the ordinary General Public License. We use", + "this license for certain libraries in order to permit linking those", + "libraries into non-free programs.", + "", + " When a program is linked with a library, whether statically or using", + "a shared library, the combination of the two is legally speaking a", + "combined work, a derivative of the original library. The ordinary", + "General Public License therefore permits such linking only if the", + "entire combination fits its criteria of freedom. The Lesser General", + "Public License permits more lax criteria for linking other code with", + "the library.", + "", + " We call this license the \"Lesser\" General Public License because it", + "does Less to protect the user's freedom than the ordinary General", + "Public License. It also provides other free software developers Less", + "of an advantage over competing non-free programs. These disadvantages", + "are the reason we use the ordinary General Public License for many", + "libraries. However, the Lesser license provides advantages in certain", + "special circumstances.", + "", + " For example, on rare occasions, there may be a special need to", + "encourage the widest possible use of a certain library, so that it becomes", + "a de-facto standard. To achieve this, non-free programs must be", + "allowed to use the library. A more frequent case is that a free", + "library does the same job as widely used non-free libraries. In this", + "case, there is little to gain by limiting the free library to free", + "software only, so we use the Lesser General Public License.", + "", + " In other cases, permission to use a particular library in non-free", + "programs enables a greater number of people to use a large body of", + "free software. For example, permission to use the GNU C Library in", + "non-free programs enables many more people to use the whole GNU", + "operating system, as well as its variant, the GNU/Linux operating", + "system.", + "", + " Although the Lesser General Public License is Less protective of the", + "users' freedom, it does ensure that the user of a program that is", + "linked with the Library has the freedom and the wherewithal to run", + "that program using a modified version of the Library.", + "", + " The precise terms and conditions for copying, distribution and", + "modification follow. Pay close attention to the difference between a", + "\"work based on the library\" and a \"work that uses the library\". The", + "former contains code derived from the library, whereas the latter must", + "be combined with the library in order to run.", + "", + "\t\t GNU LESSER GENERAL PUBLIC LICENSE", + " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION", + "", + " 0. This License Agreement applies to any software library or other", + "program which contains a notice placed by the copyright holder or", + "other authorized party saying it may be distributed under the terms of", + "this Lesser General Public License (also called \"this License\").", + "Each licensee is addressed as \"you\".", + "", + " A \"library\" means a collection of software functions and/or data", + "prepared so as to be conveniently linked with application programs", + "(which use some of those functions and data) to form executables.", + "", + " The \"Library\", below, refers to any such software library or work", + "which has been distributed under these terms. A \"work based on the", + "Library\" means either the Library or any derivative work under", + "copyright law: that is to say, a work containing the Library or a", + "portion of it, either verbatim or with modifications and/or translated", + "straightforwardly into another language. (Hereinafter, translation is", + "included without limitation in the term \"modification\".)", + "", + " \"Source code\" for a work means the preferred form of the work for", + "making modifications to it. For a library, complete source code means", + "all the source code for all modules it contains, plus any associated", + "interface definition files, plus the scripts used to control compilation", + "and installation of the library.", + "", + " Activities other than copying, distribution and modification are not", + "covered by this License; they are outside its scope. The act of", + "running a program using the Library is not restricted, and output from", + "such a program is covered only if its contents constitute a work based", + "on the Library (independent of the use of the Library in a tool for", + "writing it). Whether that is true depends on what the Library does", + "and what the program that uses the Library does.", + "", + " 1. You may copy and distribute verbatim copies of the Library's", + "complete source code as you receive it, in any medium, provided that", + "you conspicuously and appropriately publish on each copy an", + "appropriate copyright notice and disclaimer of warranty; keep intact", + "all the notices that refer to this License and to the absence of any", + "warranty; and distribute a copy of this License along with the", + "Library.", + "", + " You may charge a fee for the physical act of transferring a copy,", + "and you may at your option offer warranty protection in exchange for a", + "fee.", + "", + " 2. You may modify your copy or copies of the Library or any portion", + "of it, thus forming a work based on the Library, and copy and", + "distribute such modifications or work under the terms of Section 1", + "above, provided that you also meet all of these conditions:", + "", + " a) The modified work must itself be a software library.", + "", + " b) You must cause the files modified to carry prominent notices", + " stating that you changed the files and the date of any change.", + "", + " c) You must cause the whole of the work to be licensed at no", + " charge to all third parties under the terms of this License.", + "", + " d) If a facility in the modified Library refers to a function or a", + " table of data to be supplied by an application program that uses", + " the facility, other than as an argument passed when the facility", + " is invoked, then you must make a good faith effort to ensure that,", + " in the event an application does not supply such function or", + " table, the facility still operates, and performs whatever part of", + " its purpose remains meaningful.", + "", + " (For example, a function in a library to compute square roots has", + " a purpose that is entirely well-defined independent of the", + " application. Therefore, Subsection 2d requires that any", + " application-supplied function or table used by this function must", + " be optional: if the application does not supply it, the square", + " root function must still compute square roots.)", + "", + "These requirements apply to the modified work as a whole. If", + "identifiable sections of that work are not derived from the Library,", + "and can be reasonably considered independent and separate works in", + "themselves, then this License, and its terms, do not apply to those", + "sections when you distribute them as separate works. But when you", + "distribute the same sections as part of a whole which is a work based", + "on the Library, the distribution of the whole must be on the terms of", + "this License, whose permissions for other licensees extend to the", + "entire whole, and thus to each and every part regardless of who wrote", + "it.", + "", + "Thus, it is not the intent of this section to claim rights or contest", + "your rights to work written entirely by you; rather, the intent is to", + "exercise the right to control the distribution of derivative or", + "collective works based on the Library.", + "", + "In addition, mere aggregation of another work not based on the Library", + "with the Library (or with a work based on the Library) on a volume of", + "a storage or distribution medium does not bring the other work under", + "the scope of this License.", + "", + " 3. You may opt to apply the terms of the ordinary GNU General Public", + "License instead of this License to a given copy of the Library. To do", + "this, you must alter all the notices that refer to this License, so", + "that they refer to the ordinary GNU General Public License, version 2,", + "instead of to this License. (If a newer version than version 2 of the", + "ordinary GNU General Public License has appeared, then you can specify", + "that version instead if you wish.) Do not make any other change in", + "these notices.", + "", + " Once this change is made in a given copy, it is irreversible for", + "that copy, so the ordinary GNU General Public License applies to all", + "subsequent copies and derivative works made from that copy.", + "", + " This option is useful when you wish to copy part of the code of", + "the Library into a program that is not a library.", + "", + " 4. You may copy and distribute the Library (or a portion or", + "derivative of it, under Section 2) in object code or executable form", + "under the terms of Sections 1 and 2 above provided that you accompany", + "it with the complete corresponding machine-readable source code, which", + "must be distributed under the terms of Sections 1 and 2 above on a", + "medium customarily used for software interchange.", + "", + " If distribution of object code is made by offering access to copy", + "from a designated place, then offering equivalent access to copy the", + "source code from the same place satisfies the requirement to", + "distribute the source code, even though third parties are not", + "compelled to copy the source along with the object code.", + "", + " 5. A program that contains no derivative of any portion of the", + "Library, but is designed to work with the Library by being compiled or", + "linked with it, is called a \"work that uses the Library\". Such a", + "work, in isolation, is not a derivative work of the Library, and", + "therefore falls outside the scope of this License.", + "", + " However, linking a \"work that uses the Library\" with the Library", + "creates an executable that is a derivative of the Library (because it", + "contains portions of the Library), rather than a \"work that uses the", + "library\". The executable is therefore covered by this License.", + "Section 6 states terms for distribution of such executables.", + "", + " When a \"work that uses the Library\" uses material from a header file", + "that is part of the Library, the object code for the work may be a", + "derivative work of the Library even though the source code is not.", + "Whether this is true is especially significant if the work can be", + "linked without the Library, or if the work is itself a library. The", + "threshold for this to be true is not precisely defined by law.", + "", + " If such an object file uses only numerical parameters, data", + "structure layouts and accessors, and small macros and small inline", + "functions (ten lines or less in length), then the use of the object", + "file is unrestricted, regardless of whether it is legally a derivative", + "work. (Executables containing this object code plus portions of the", + "Library will still fall under Section 6.)", + "", + " Otherwise, if the work is a derivative of the Library, you may", + "distribute the object code for the work under the terms of Section 6.", + "Any executables containing that work also fall under Section 6,", + "whether or not they are linked directly with the Library itself.", + "", + " 6. As an exception to the Sections above, you may also combine or", + "link a \"work that uses the Library\" with the Library to produce a", + "work containing portions of the Library, and distribute that work", + "under terms of your choice, provided that the terms permit", + "modification of the work for the customer's own use and reverse", + "engineering for debugging such modifications.", + "", + " You must give prominent notice with each copy of the work that the", + "Library is used in it and that the Library and its use are covered by", + "this License. You must supply a copy of this License. If the work", + "during execution displays copyright notices, you must include the", + "copyright notice for the Library among them, as well as a reference", + "directing the user to the copy of this License. Also, you must do one", + "of these things:", + "", + " a) Accompany the work with the complete corresponding", + " machine-readable source code for the Library including whatever", + " changes were used in the work (which must be distributed under", + " Sections 1 and 2 above); and, if the work is an executable linked", + " with the Library, with the complete machine-readable \"work that", + " uses the Library\", as object code and/or source code, so that the", + " user can modify the Library and then relink to produce a modified", + " executable containing the modified Library. (It is understood", + " that the user who changes the contents of definitions files in the", + " Library will not necessarily be able to recompile the application", + " to use the modified definitions.)", + "", + " b) Use a suitable shared library mechanism for linking with the", + " Library. A suitable mechanism is one that (1) uses at run time a", + " copy of the library already present on the user's computer system,", + " rather than copying library functions into the executable, and (2)", + " will operate properly with a modified version of the library, if", + " the user installs one, as long as the modified version is", + " interface-compatible with the version that the work was made with.", + "", + " c) Accompany the work with a written offer, valid for at", + " least three years, to give the same user the materials", + " specified in Subsection 6a, above, for a charge no more", + " than the cost of performing this distribution.", + "", + " d) If distribution of the work is made by offering access to copy", + " from a designated place, offer equivalent access to copy the above", + " specified materials from the same place.", + "", + " e) Verify that the user has already received a copy of these", + " materials or that you have already sent this user a copy.", + "", + " For an executable, the required form of the \"work that uses the", + "Library\" must include any data and utility programs needed for", + "reproducing the executable from it. However, as a special exception,", + "the materials to be distributed need not include anything that is", + "normally distributed (in either source or binary form) with the major", + "components (compiler, kernel, and so on) of the operating system on", + "which the executable runs, unless that component itself accompanies", + "the executable.", + "", + " It may happen that this requirement contradicts the license", + "restrictions of other proprietary libraries that do not normally", + "accompany the operating system. Such a contradiction means you cannot", + "use both them and the Library together in an executable that you", + "distribute.", + "", + " 7. You may place library facilities that are a work based on the", + "Library side-by-side in a single library together with other library", + "facilities not covered by this License, and distribute such a combined", + "library, provided that the separate distribution of the work based on", + "the Library and of the other library facilities is otherwise", + "permitted, and provided that you do these two things:", + "", + " a) Accompany the combined library with a copy of the same work", + " based on the Library, uncombined with any other library", + " facilities. This must be distributed under the terms of the", + " Sections above.", + "", + " b) Give prominent notice with the combined library of the fact", + " that part of it is a work based on the Library, and explaining", + " where to find the accompanying uncombined form of the same work.", + "", + " 8. You may not copy, modify, sublicense, link with, or distribute", + "the Library except as expressly provided under this License. Any", + "attempt otherwise to copy, modify, sublicense, link with, or", + "distribute the Library is void, and will automatically terminate your", + "rights under this License. However, parties who have received copies,", + "or rights, from you under this License will not have their licenses", + "terminated so long as such parties remain in full compliance.", + "", + " 9. You are not required to accept this License, since you have not", + "signed it. However, nothing else grants you permission to modify or", + "distribute the Library or its derivative works. These actions are", + "prohibited by law if you do not accept this License. Therefore, by", + "modifying or distributing the Library (or any work based on the", + "Library), you indicate your acceptance of this License to do so, and", + "all its terms and conditions for copying, distributing or modifying", + "the Library or works based on it.", + "", + " 10. Each time you redistribute the Library (or any work based on the", + "Library), the recipient automatically receives a license from the", + "original licensor to copy, distribute, link with or modify the Library", + "subject to these terms and conditions. You may not impose any further", + "restrictions on the recipients' exercise of the rights granted herein.", + "You are not responsible for enforcing compliance by third parties with", + "this License.", + "", + " 11. If, as a consequence of a court judgment or allegation of patent", + "infringement or for any other reason (not limited to patent issues),", + "conditions are imposed on you (whether by court order, agreement or", + "otherwise) that contradict the conditions of this License, they do not", + "excuse you from the conditions of this License. If you cannot", + "distribute so as to satisfy simultaneously your obligations under this", + "License and any other pertinent obligations, then as a consequence you", + "may not distribute the Library at all. For example, if a patent", + "license would not permit royalty-free redistribution of the Library by", + "all those who receive copies directly or indirectly through you, then", + "the only way you could satisfy both it and this License would be to", + "refrain entirely from distribution of the Library.", + "", + "If any portion of this section is held invalid or unenforceable under any", + "particular circumstance, the balance of the section is intended to apply,", + "and the section as a whole is intended to apply in other circumstances.", + "", + "It is not the purpose of this section to induce you to infringe any", + "patents or other property right claims or to contest validity of any", + "such claims; this section has the sole purpose of protecting the", + "integrity of the free software distribution system which is", + "implemented by public license practices. Many people have made", + "generous contributions to the wide range of software distributed", + "through that system in reliance on consistent application of that", + "system; it is up to the author/donor to decide if he or she is willing", + "to distribute software through any other system and a licensee cannot", + "impose that choice.", + "", + "This section is intended to make thoroughly clear what is believed to", + "be a consequence of the rest of this License.", + "", + " 12. If the distribution and/or use of the Library is restricted in", + "certain countries either by patents or by copyrighted interfaces, the", + "original copyright holder who places the Library under this License may add", + "an explicit geographical distribution limitation excluding those countries,", + "so that distribution is permitted only in or among countries not thus", + "excluded. In such case, this License incorporates the limitation as if", + "written in the body of this License.", + "", + " 13. The Free Software Foundation may publish revised and/or new", + "versions of the Lesser General Public License from time to time.", + "Such new versions will be similar in spirit to the present version,", + "but may differ in detail to address new problems or concerns.", + "", + "Each version is given a distinguishing version number. If the Library", + "specifies a version number of this License which applies to it and", + "\"any later version\", you have the option of following the terms and", + "conditions either of that version or of any later version published by", + "the Free Software Foundation. If the Library does not specify a", + "license version number, you may choose any version ever published by", + "the Free Software Foundation.", + "", + " 14. If you wish to incorporate parts of the Library into other free", + "programs whose distribution conditions are incompatible with these,", + "write to the author to ask for permission. For software which is", + "copyrighted by the Free Software Foundation, write to the Free", + "Software Foundation; we sometimes make exceptions for this. Our", + "decision will be guided by the two goals of preserving the free status", + "of all derivatives of our free software and of promoting the sharing", + "and reuse of software generally.", + "", + "\t\t\t NO WARRANTY", + "", + " 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO", + "WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.", + "EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR", + "OTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY", + "KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE", + "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR", + "PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE", + "LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME", + "THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.", + "", + " 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN", + "WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY", + "AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU", + "FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR", + "CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE", + "LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING", + "RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A", + "FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF", + "SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH", + "DAMAGES.", + "", + "\t\t END OF TERMS AND CONDITIONS", + "", + " How to Apply These Terms to Your New Libraries", + "", + " If you develop a new library, and you want it to be of the greatest", + "possible use to the public, we recommend making it free software that", + "everyone can redistribute and change. You can do so by permitting", + "redistribution under these terms (or, alternatively, under the terms of the", + "ordinary General Public License).", + "", + " To apply these terms, attach the following notices to the library. It is", + "safest to attach them to the start of each source file to most effectively", + "convey the exclusion of warranty; and each file should have at least the", + "\"copyright\" line and a pointer to where the full notice is found.", + "", + " ", + " Copyright (C) ", + "", + " This library is free software; you can redistribute it and/or", + " modify it under the terms of the GNU Lesser General Public", + " License as published by the Free Software Foundation; either", + " version 2.1 of the License, or (at your option) any later version.", + "", + " This library is distributed in the hope that it will be useful,", + " but WITHOUT ANY WARRANTY; without even the implied warranty of", + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU", + " Lesser General Public License for more details.", + "", + " You should have received a copy of the GNU Lesser General Public", + " License along with this library; if not, write to the Free Software", + " Foundation, Inc.,", + "51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA", + "", + "Also add information on how to contact you by electronic and paper mail.", + "", + "You should also get your employer (if you work as a programmer) or your", + "school, if any, to sign a \"copyright disclaimer\" for the library, if", + "necessary. Here is a sample; alter the names:", + "", + " Yoyodyne, Inc., hereby disclaims all copyright interest in the", + " library `Frob' (a library for tweaking knobs) written by James Random Hacker.", + "", + " ,", + "1 April 1990", + " Ty Coon, President of Vice", + "", + "That's all there is to it!" + ], + "isProd": true +}, +{ + "isLicense": true, + "name": "@types/node", + "repositoryURL": "https://www.github.com/DefinitelyTyped/DefinitelyTyped", + "license": "MIT", + "licenseDetail": [ + "This project is licensed under the MIT license.", + "Copyrights are respective of each contributor listed at the beginning of each definition file.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "isProd": true +}, +{ + "isLicense": true, + "name": "jsonfile", + "repositoryURL": "https+ssh://git@github.com/jprichardson/node-jsonfile", + "license": "MIT", + "licenseDetail": [ + "(The MIT License)", + "", + "Copyright (c) 2012-2015, JP Richardson ", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files", + "(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,", + " merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is", + " furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE", + "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS", + "OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,", + " ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "isProd": true +}, +{ + "isLicense": true, + "name": "string_decoder", + "repositoryURL": "https://github.com/rvagg/string_decoder", + "license": "MIT", + "licenseDetail": [ + "The MIT License (MIT)", + "", + "Node.js is licensed for use as follows:", + "", + "\"\"\"", + "Copyright Node.js contributors. All rights reserved.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to", + "deal in the Software without restriction, including without limitation the", + "rights to use, copy, modify, merge, publish, distribute, sublicense, and/or", + "sell copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS", + "IN THE SOFTWARE.", + "\"\"\"", + "", + "This license applies to parts of Node.js originating from the", + "https://github.com/joyent/node repository:", + "", + "\"\"\"", + "Copyright Joyent, Inc. and other Node contributors. All rights reserved.", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to", + "deal in the Software without restriction, including without limitation the", + "rights to use, copy, modify, merge, publish, distribute, sublicense, and/or", + "sell copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS", + "IN THE SOFTWARE.", + "\"\"\"" + ], + "isProd": true +} +] diff --git a/README.md b/README.md index ef3c2bd5d8..ff26f99627 100644 --- a/README.md +++ b/README.md @@ -1 +1,20 @@ -TODO: Create a file called LICENSE (not LICENSE.TXT, LICENSE.md, etc.)… \ No newline at end of file +# SQL Operations Studio + +SQL Operations Studio is a multi-OS, multi-database management tool. + +## Contributing +If you are interested in fixing issues and contributing directly to the code base, +please see the document [How to Contribute](https://github.com/Microsoft/vscode/wiki/How-to-Contribute), which covers the following: + +* [How to build and run from source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run-from-source) +* [The development workflow, including debugging and running tests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#development-workflow) +* [Coding Guidelines](https://github.com/Microsoft/vscode/wiki/Coding-Guidelines) +* [Submitting pull requests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#pull-requests) + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the [Source EULA](LICENSE.txt). diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt new file mode 100644 index 0000000000..db3aed83fe --- /dev/null +++ b/ThirdPartyNotices.txt @@ -0,0 +1,2069 @@ +MICROSOFT SQL OPERATIONS STUDIO + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +This project incorporates components from the projects listed below. +The original copyright notices and the licenses under which Microsoft received +such components are set forth below. Microsoft reserves all rights not +expressly granted herein, whether by implication, estoppel or otherwise. + + angular: https://github.com/angular/angular + angular2-grid: https://github.com/BTMorton/angular2-grid + angular2-slickgrid: https://github.com/Microsoft/angular2-slickgrid + applicationinsights: https://github.com/Microsoft/ApplicationInsights-node.js + bootstrap: https://github.com/twbs/bootstrap + chart.js: https://github.com/Timer/chartjs + chokidar: https://github.com/paulmillr/chokidar + comment-json: https://github.com/kaelzhang/node-comment-json + core-js: https://github.com/zloirock/core-js + emmet: https://github.com/emmetio/emmet + error-ex: https://github.com/Qix-/node-error-ex + escape-string-regexp: https://github.com/sindresorhus/escape-string-regexp + fast-plist: https://github.com/Microsoft/node-fast-plist + fs-extra: https://github.com/jprichardson/node-fs-extra + gc-signals: https://github.com/Microsoft/node-gc-signals + getmac: https://github.com/bevry/getmac + graceful-fs: https://github.com/isaacs/node-graceful-fs + html-query-plan: https://github.com/JustinPealing/html-query-plan + http-proxy-agent: https://github.com/TooTallNate/node-https-proxy-agent + https-proxy-agent: https://github.com/TooTallNate/node-https-proxy-agent + iconv-lite: https://github.com/ashtuchkin/iconv-lite + jquery: https://github.com/jquery/jquery + jquery-ui: https://github.com/jquery/jquery-ui + jquery.event.drag: https://github.com/devongovett/jquery.event.drag + jschardet: https://github.com/aadsm/jschardet + make-error: https://github.com/JsCommunity/make-error + minimist: https://github.com/substack/minimist + moment: https://github.com/moment/moment + native-keymap: https://github.com/Microsoft/node-native-keymap + native-watchdog: https://github.com/Microsoft/node-native-watchdog + ng2-charts: https://github.com/valor-software/ng2-charts + node-pty: https://github.com/Tyriar/node-pty + nsfw: https://github.com/Axosoft/nsfw + pretty-data: https://github.com/vkiryukhin/pretty-data + primeng: https://github.com/primefaces/primeng + pty.js: https://github.com/chjj/pty.js + rangy: https://github.com/timdown/rangy + reflect-metadata: https://github.com/rbuckton/reflect-metadata + rxjs: https://github.com/ReactiveX/RxJS + semver: https://github.com/npm/node-semver + slickgrid: https://github.com/6pac/SlickGrid + sqltoolsservice: https://github.com/Microsoft/sqltoolsservice + svg.js: https://github.com/svgdotjs/svg.js + systemjs: https://github.com/systemjs/systemjs + underscore: https://github.com/jashkenas/underscore + v8-profiler: https://github.com/node-inspector/v8-profiler + vscode: https://github.com/microsoft/vscode + vscode-debugprotocol: https://github.com/Microsoft/vscode-debugadapter-node + vscode-ripgrep: https://github.com/roblourens/vscode-ripgrep + vscode-textmate: https://github.com/Microsoft/vscode-textmate + winreg: https://github.com/fresc81/node-winreg + xterm: https://github.com/sourcelair/xterm.js + yauzl: https://github.com/thejoshwolfe/yauzl + zone.js: https://www.npmjs.com/package/zone + + +%% angular NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License + +Copyright (c) 2014-2017 Google, Inc. http://angular.io + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF angular NOTICES AND INFORMATION + +%% angular2-grid NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2015 Ben Morton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF angular2-grid NOTICES AND INFORMATION + +%% angular2-slickgrid NOTICES AND INFORMATION BEGIN HERE +========================================= +microsoft-vscode-mssql + +Copyright (c) Microsoft Corporation +All rights reserved. + +MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the ""Software""), to deal +in the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF angular2-slickgrid NOTICES AND INFORMATION + +%% applicationinsights NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright © Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in the +Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF applicationinsights NOTICES AND INFORMATION + +%% bootstrap NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2011-2017 Twitter, Inc. +Copyright (c) 2011-2017 The Bootstrap Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF bootstrap NOTICES AND INFORMATION + +%% chart.js NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2015 Joe Haddad + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF chart.js NOTICES AND INFORMATION + +%% chokidar NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2016 Paul Miller (http://paulmillr.com) & Elan Shanker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in the +Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF chokidar NOTICES AND INFORMATION + +%% comment-json NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) 2013 kaelzhang , contributors +http://kael.me/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF comment-json NOTICES AND INFORMATION + +%% core-js NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) 2014-2017 Denis Pushkarev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF core-js NOTICES AND INFORMATION + +%% emmet NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2012 Sergey Chikuyonok + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF emmet NOTICES AND INFORMATION + +%% error-ex NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2015 JD Ballard + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF error-ex NOTICES AND INFORMATION + +%% escape-string-regexp NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF escape-string-regexp NOTICES AND INFORMATION + +%% fast-plist NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF fast-plist NOTICES AND INFORMATION + +%% fs-extra NOTICES AND INFORMATION BEGIN HERE +========================================= +(The MIT License) + +Copyright (c) 2011-2017 JP Richardson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF fs-extra NOTICES AND INFORMATION + +%% gc-signals NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF gc-signals NOTICES AND INFORMATION + +%% getmac NOTICES AND INFORMATION BEGIN HERE +========================================= +Unless stated otherwise all works are: + +Copyright © 2013+ Bevry Pty Ltd +and licensed under: + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF getmac NOTICES AND INFORMATION + +%% graceful-fs NOTICES AND INFORMATION BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter, Ben Noordhuis, and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF graceful-fs NOTICES AND INFORMATION + +%% html-query-plan NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2011 Justin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF html-query-plan NOTICES AND INFORMATION + +%% http-proxy-agent NOTICES AND INFORMATION BEGIN HERE +========================================= +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF http-proxy-agent NOTICES AND INFORMATION + +%% iconv-lite NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) 2011 Alexander Shtuchkin + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF iconv-lite NOTICES AND INFORMATION + +%% jquery NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright JS Foundation and other contributors, https://js.foundation/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/jquery + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. +========================================= +END OF jquery NOTICES AND INFORMATION + +%% jquery-ui NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright jQuery Foundation and other contributors, https://jquery.org/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/jquery-ui + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code contained within the demos directory. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. +========================================= +END OF jquery-ui NOTICES AND INFORMATION + +%% jquery.event.drag NOTICES AND INFORMATION BEGIN HERE +========================================= +https://www.npmjs.com/package/jquery.event.drag + +MIT License +Full name +MIT License + +Short identifier +MIT + +Other web pages for this license +http://www.opensource.org/licenses/MIT +Notes +None + +Text +MIT License +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Standard License Header +There is no standard license header for the license +========================================= +END OF jquery.event.drag NOTICES AND INFORMATION + +%% jschardet NOTICES AND INFORMATION BEGIN HERE +========================================= +GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +(This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.) + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + {signature of Ty Coon}, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! +========================================= +END OF jschardet NOTICES AND INFORMATION + +%% make-error NOTICES AND INFORMATION BEGIN HERE +========================================= +ISC © Julien Fontanet +========================================= +END OF make-error NOTICES AND INFORMATION + +%% minimist NOTICES AND INFORMATION BEGIN HERE +========================================= +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF minimist NOTICES AND INFORMATION + +%% moment NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF moment NOTICES AND INFORMATION + +%% native-keymap NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF native-keymap NOTICES AND INFORMATION + +%% native-watchdog NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +========================================= +END OF native-watchdog NOTICES AND INFORMATION + +%% ng2-charts NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2015-2017 Dmitriy Shekhovtsov +Copyright (c) 2015-2017 Valor Software + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF ng2-charts NOTICES AND INFORMATION + +%% node-pty NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) 2012-2015, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF node-pty NOTICES AND INFORMATION + +%% nsfw NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2015 Axosoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF nsfw NOTICES AND INFORMATION + +%% pretty-data NOTICES AND INFORMATION BEGIN HERE +========================================= +License: Dual licensed under the MIT and GPL licenses: + +http://www.opensource.org/licenses/mit-license.php + +http://www.gnu.org/licenses/gpl.html +========================================= +END OF pretty-data NOTICES AND INFORMATION + +%% primeng NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2016-2017 PrimeTek + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF primeng NOTICES AND INFORMATION + +%% pty.js NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) 2012-2015, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF pty.js NOTICES AND INFORMATION + +%% rangy NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014 Tim Down + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF rangy NOTICES AND INFORMATION + +%% reflect-metadata NOTICES AND INFORMATION BEGIN HERE +========================================= +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS +========================================= +END OF reflect-metadata NOTICES AND INFORMATION + +%% rxjs NOTICES AND INFORMATION BEGIN HERE +========================================= +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2015-2017 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +========================================= +END OF rxjs NOTICES AND INFORMATION + +%% semver NOTICES AND INFORMATION BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF semver NOTICES AND INFORMATION + +%% slickgrid NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) 2009-2016 Michael Leibman, http://github.com/mleibman/slickgrid + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF slickgrid NOTICES AND INFORMATION + +%% sqltoolsservice NOTICES AND INFORMATION BEGIN HERE +========================================= +microsoft-vscode-mssql +Copyright (c) Microsoft Corporation +All rights reserved. +MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the ""Software""), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, subject +to the following conditions: +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF sqltoolsservice NOTICES AND INFORMATION + +%% svg.js NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) 2012-2017 Wout Fierens +https://svgdotjs.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF svg.js NOTICES AND INFORMATION + +%% systemjs NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License +----------- + +Copyright (C) 2013-2016 Guy Bedford + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, subject +to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +========================================= +END OF systemjs NOTICES AND INFORMATION + +%% underscore NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) 2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative +Reporters & Editors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF underscore NOTICES AND INFORMATION + +%% v8-profiler NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) 2011, Danny Coates +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other materials +provided with the distribution. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +========================================= +END OF v8-profiler NOTICES AND INFORMATION + +%% vscode NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2015 - present Microsoft Corporation + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF vscode NOTICES AND INFORMATION + +%% vscode-debugprotocol NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF vscode-debugprotocol NOTICES AND INFORMATION + +%% vscode-ripgrep NOTICES AND INFORMATION BEGIN HERE +========================================= +vscode-ripgrep + +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the ""Software""), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, subject +to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF vscode-ripgrep NOTICES AND INFORMATION + +%% vscode-textmate NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF vscode-textmate NOTICES AND INFORMATION + +%% winreg NOTICES AND INFORMATION BEGIN HERE +========================================= +This project is released under BSD 2-Clause License. + +Copyright (c) 2016, Paul Bottin All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other materials +provided with the distribution. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +========================================= +END OF winreg NOTICES AND INFORMATION + +%% xterm NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com) +Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF xterm NOTICES AND INFORMATION + +%% yauzl NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014 Josh Wolfe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF yauzl NOTICES AND INFORMATION + +%% zone.js NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) IBM Corp. 2014,2016. All Rights Reserved. +Node module: zone +This project is licensed under the MIT License, full text below. + +-------- + +MIT license + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF zone.js NOTICES AND INFORMATION \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..49e80fed31 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,19 @@ +environment: + ELECTRON_RUN_AS_NODE: 1 + VSCODE_BUILD_VERBOSE: true + +install: + - ps: Install-Product node 7.4.0 x64 + - npm install -g npm@4 --silent + - npm install -g gulp mocha --silent + +build_script: + - .\scripts\npm.bat install + - gulp electron + - npm run compile + +test_script: + - node --version + - npm --version + - .\scripts\test.bat + - .\scripts\test-integration.bat diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js new file mode 100644 index 0000000000..eb6bbf3c13 --- /dev/null +++ b/build/gulpfile.editor.js @@ -0,0 +1,207 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +var gulp = require('gulp'); +var path = require('path'); +var util = require('./lib/util'); +var common = require('./lib/optimize'); +var es = require('event-stream'); +var File = require('vinyl'); + +var root = path.dirname(__dirname); +var sha1 = util.getVersion(root); +var semver = require('./monaco/package.json').version; +var headerVersion = semver + '(' + sha1 + ')'; + +// Build + +var editorEntryPoints = [ + { + name: 'vs/editor/editor.main', + include: [], + exclude: [ 'vs/css', 'vs/nls' ], + prepend: [ 'out-build/vs/css.js', 'out-build/vs/nls.js' ], + }, + { + name: 'vs/base/common/worker/simpleWorker', + include: [ 'vs/editor/common/services/editorSimpleWorker' ], + prepend: [ 'vs/loader.js' ], + append: [ 'vs/base/worker/workerMain' ], + dest: 'vs/base/worker/workerMain.js' + } +]; + +var editorResources = [ + 'out-build/vs/{base,editor}/**/*.{svg,png}', + '!out-build/vs/base/browser/ui/splitview/**/*', + '!out-build/vs/base/browser/ui/toolbar/**/*', + '!out-build/vs/base/browser/ui/octiconLabel/**/*', + '!out-build/vs/workbench/**', + '!**/test/**' +]; + +var editorOtherSources = [ +]; + +var BUNDLED_FILE_HEADER = [ + '/*!-----------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' * Version: ' + headerVersion, + ' * Released under the Source EULA', + ' * https://github.com/Microsoft/vscode/blob/master/LICENSE.txt', + ' *-----------------------------------------------------------*/', + '' +].join('\n'); + +function editorLoaderConfig() { + var result = common.loaderConfig(); + + // never ship octicons in editor + result.paths['vs/base/browser/ui/octiconLabel/octiconLabel'] = 'out-build/vs/base/browser/ui/octiconLabel/octiconLabel.mock'; + + // force css inlining to use base64 -- see https://github.com/Microsoft/monaco-editor/issues/148 + result['vs/css'] = { + inlineResources: 'base64', + inlineResourcesLimit: 3000 // see https://github.com/Microsoft/monaco-editor/issues/336 + }; + + return result; +} + +gulp.task('clean-optimized-editor', util.rimraf('out-editor')); +gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-client-build'], common.optimizeTask({ + entryPoints: editorEntryPoints, + otherSources: editorOtherSources, + resources: editorResources, + loaderConfig: editorLoaderConfig(), + bundleLoader: false, + header: BUNDLED_FILE_HEADER, + bundleInfo: true, + out: 'out-editor' +})); + +gulp.task('clean-minified-editor', util.rimraf('out-editor-min')); +gulp.task('minify-editor', ['clean-minified-editor', 'optimize-editor'], common.minifyTask('out-editor')); + +gulp.task('clean-editor-distro', util.rimraf('out-monaco-editor-core')); +gulp.task('editor-distro', ['clean-editor-distro', 'minify-editor', 'optimize-editor'], function() { + return es.merge( + // other assets + es.merge( + gulp.src('build/monaco/LICENSE'), + gulp.src('build/monaco/ThirdPartyNotices.txt'), + gulp.src('src/vs/monaco.d.ts') + ).pipe(gulp.dest('out-monaco-editor-core')), + + // package.json + gulp.src('build/monaco/package.json') + .pipe(es.through(function(data) { + var json = JSON.parse(data.contents.toString()); + json.private = false; + data.contents = new Buffer(JSON.stringify(json, null, ' ')); + this.emit('data', data); + })) + .pipe(gulp.dest('out-monaco-editor-core')), + + // README.md + gulp.src('build/monaco/README-npm.md') + .pipe(es.through(function(data) { + this.emit('data', new File({ + path: data.path.replace(/README-npm\.md/, 'README.md'), + base: data.base, + contents: data.contents + })); + })) + .pipe(gulp.dest('out-monaco-editor-core')), + + // dev folder + es.merge( + gulp.src('out-editor/**/*') + ).pipe(gulp.dest('out-monaco-editor-core/dev')), + + // min folder + es.merge( + gulp.src('out-editor-min/**/*') + ).pipe(filterStream(function(path) { + // no map files + return !/(\.js\.map$)|(nls\.metadata\.json$)|(bundleInfo\.json$)/.test(path); + })).pipe(es.through(function(data) { + // tweak the sourceMappingURL + if (!/\.js$/.test(data.path)) { + this.emit('data', data); + return; + } + + var relativePathToMap = path.relative(path.join(data.relative), path.join('min-maps', data.relative + '.map')); + + var strContents = data.contents.toString(); + var newStr = '//# sourceMappingURL=' + relativePathToMap.replace(/\\/g, '/'); + strContents = strContents.replace(/\/\/\# sourceMappingURL=[^ ]+$/, newStr); + + data.contents = new Buffer(strContents); + this.emit('data', data); + })).pipe(gulp.dest('out-monaco-editor-core/min')), + + // min-maps folder + es.merge( + gulp.src('out-editor-min/**/*') + ).pipe(filterStream(function(path) { + // no map files + return /\.js\.map$/.test(path); + })).pipe(gulp.dest('out-monaco-editor-core/min-maps')) + ); +}); + +gulp.task('analyze-editor-distro', function() { + var bundleInfo = require('../out-editor/bundleInfo.json'); + var graph = bundleInfo.graph; + var bundles = bundleInfo.bundles; + + var inverseGraph = {}; + Object.keys(graph).forEach(function(module) { + var dependencies = graph[module]; + dependencies.forEach(function(dep) { + inverseGraph[dep] = inverseGraph[dep] || []; + inverseGraph[dep].push(module); + }); + }); + + var detailed = {}; + Object.keys(bundles).forEach(function(entryPoint) { + var included = bundles[entryPoint]; + var includedMap = {}; + included.forEach(function(included) { + includedMap[included] = true; + }); + + var explanation = []; + included.map(function(included) { + if (included.indexOf('!') >= 0) { + return; + } + + var reason = (inverseGraph[included]||[]).filter(function(mod) { + return !!includedMap[mod]; + }); + explanation.push({ + module: included, + reason: reason + }); + }); + + detailed[entryPoint] = explanation; + }); + + console.log(JSON.stringify(detailed, null, '\t')); +}); + +function filterStream(testFunc) { + return es.through(function(data) { + if (!testFunc(data.relative)) { + return; + } + this.emit('data', data); + }); +} diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js new file mode 100644 index 0000000000..5911bf0ec3 --- /dev/null +++ b/build/gulpfile.extensions.js @@ -0,0 +1,150 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Increase max listeners for event emitters +require('events').EventEmitter.defaultMaxListeners = 100; + +const gulp = require('gulp'); +const path = require('path'); +const tsb = require('gulp-tsb'); +const es = require('event-stream'); +const filter = require('gulp-filter'); +const rimraf = require('rimraf'); +const util = require('./lib/util'); +const watcher = require('./lib/watch'); +const createReporter = require('./lib/reporter').createReporter; +const glob = require('glob'); +const sourcemaps = require('gulp-sourcemaps'); +const nlsDev = require('vscode-nls-dev'); +const root = path.dirname(__dirname); +const commit = util.getVersion(root); + +const extensionsPath = path.join(path.dirname(__dirname), 'extensions'); + +const compilations = glob.sync('**/tsconfig.json', { + cwd: extensionsPath, + ignore: ['**/out/**', '**/node_modules/**'] +}); + +const getBaseUrl = out => `https://ticino.blob.core.windows.net/sourcemaps/${commit}/${out}`; +const languages = ['chs', 'cht', 'jpn', 'kor', 'deu', 'fra', 'esn', 'rus', 'ita']; + +const tasks = compilations.map(function (tsconfigFile) { + const absolutePath = path.join(extensionsPath, tsconfigFile); + const relativeDirname = path.dirname(tsconfigFile); + + const tsOptions = require(absolutePath).compilerOptions; + tsOptions.verbose = false; + tsOptions.sourceMap = true; + + const name = relativeDirname.replace(/\//g, '-'); + + // Tasks + const clean = 'clean-extension:' + name; + const compile = 'compile-extension:' + name; + const watch = 'watch-extension:' + name; + + // Build Tasks + const cleanBuild = 'clean-extension-build:' + name; + const compileBuild = 'compile-extension-build:' + name; + const watchBuild = 'watch-extension-build:' + name; + + const root = path.join('extensions', relativeDirname); + const srcBase = path.join(root, 'src'); + const src = path.join(srcBase, '**'); + const out = path.join(root, 'out'); + const i18n = path.join(__dirname, '..', 'i18n'); + const baseUrl = getBaseUrl(out); + + function createPipeline(build, emitError) { + const reporter = createReporter(); + + tsOptions.inlineSources = !!build; + const compilation = tsb.create(tsOptions, null, null, err => reporter(err.toString())); + + return function () { + const input = es.through(); + const tsFilter = filter(['**/*.ts', '!**/lib/lib*.d.ts', '!**/node_modules/**'], { restore: true }); + const output = input + .pipe(tsFilter) + .pipe(util.loadSourcemaps()) + .pipe(compilation()) + .pipe(build ? nlsDev.rewriteLocalizeCalls() : es.through()) + .pipe(build ? util.stripSourceMappingURL() : es.through()) + .pipe(sourcemaps.write('.', { + sourceMappingURL: !build ? null : f => `${baseUrl}/${f.relative}.map`, + addComment: !!build, + includeContent: !!build, + sourceRoot: '../src' + })) + .pipe(tsFilter.restore) + .pipe(build ? nlsDev.createAdditionalLanguageFiles(languages, i18n, out) : es.through()) + .pipe(reporter.end(emitError)); + + return es.duplex(input, output); + }; + } + + const srcOpts = { cwd: path.dirname(__dirname), base: srcBase }; + + gulp.task(clean, cb => rimraf(out, cb)); + + gulp.task(compile, [clean], () => { + const pipeline = createPipeline(false, true); + const input = gulp.src(src, srcOpts); + + return input + .pipe(pipeline()) + .pipe(gulp.dest(out)); + }); + + gulp.task(watch, [clean], () => { + const pipeline = createPipeline(false); + const input = gulp.src(src, srcOpts); + const watchInput = watcher(src, srcOpts); + + return watchInput + .pipe(util.incremental(pipeline, input)) + .pipe(gulp.dest(out)); + }); + + gulp.task(cleanBuild, cb => rimraf(out, cb)); + + gulp.task(compileBuild, [clean], () => { + const pipeline = createPipeline(true, true); + const input = gulp.src(src, srcOpts); + + return input + .pipe(pipeline()) + .pipe(gulp.dest(out)); + }); + + gulp.task(watchBuild, [clean], () => { + const pipeline = createPipeline(true); + const input = gulp.src(src, srcOpts); + const watchInput = watcher(src, srcOpts); + + return watchInput + .pipe(util.incremental(() => pipeline(true), input)) + .pipe(gulp.dest(out)); + }); + + return { + clean: clean, + compile: compile, + watch: watch, + cleanBuild: cleanBuild, + compileBuild: compileBuild, + watchBuild: watchBuild + }; +}); + +gulp.task('clean-extensions', tasks.map(t => t.clean)); +gulp.task('compile-extensions', tasks.map(t => t.compile)); +gulp.task('watch-extensions', tasks.map(t => t.watch)); + +gulp.task('clean-extensions-build', tasks.map(t => t.cleanBuild)); +gulp.task('compile-extensions-build', tasks.map(t => t.compileBuild)); +gulp.task('watch-extensions-build', tasks.map(t => t.watchBuild)); \ No newline at end of file diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js new file mode 100644 index 0000000000..d2553114a7 --- /dev/null +++ b/build/gulpfile.hygiene.js @@ -0,0 +1,310 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const gulp = require('gulp'); +const filter = require('gulp-filter'); +const es = require('event-stream'); +const gulptslint = require('gulp-tslint'); +const gulpeslint = require('gulp-eslint'); +const tsfmt = require('typescript-formatter'); +const tslint = require('tslint'); + +/** + * Hygiene works by creating cascading subsets of all our files and + * passing them through a sequence of checks. Here are the current subsets, + * named according to the checks performed on them. Each subset contains + * the following one, as described in mathematical notation: + * + * all ⊃ eol ⊇ indentation ⊃ copyright ⊃ typescript + */ + +const all = [ + '*', + 'build/**/*', + 'extensions/**/*', + 'scripts/**/*', + 'src/**/*', + 'test/**/*' +]; + +const eolFilter = [ + '**', + '!ThirdPartyNotices.txt', + '!LICENSE.txt', + '!extensions/**/out/**', + '!**/node_modules/**', + '!**/fixtures/**', + '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot}', + '!build/{lib,tslintRules}/**/*.js', + '!build/monaco/**', + '!build/win32/**', + '!build/**/*.sh', + '!build/tfs/**/*.js', + '!**/Dockerfile' +]; + +const indentationFilter = [ + '**', + '!ThirdPartyNotices.txt', + '!**/*.md', + '!**/*.ps1', + '!**/*.template', + '!**/*.yml', + '!**/lib/**', + '!extensions/**/*.d.ts', + '!src/typings/**/*.d.ts', + '!src/vs/*/**/*.d.ts', + '!**/*.d.ts.recipe', + '!test/assert.js', + '!**/package.json', + '!**/npm-shrinkwrap.json', + '!**/octicons/**', + '!**/vs/base/common/marked/raw.marked.js', + '!**/vs/base/common/winjs.base.raw.js', + '!**/vs/base/node/terminateProcess.sh', + '!**/vs/nls.js', + '!**/vs/css.js', + '!**/vs/loader.js', + '!extensions/**/snippets/**', + '!extensions/**/syntaxes/**', + '!extensions/**/themes/**', + '!extensions/**/colorize-fixtures/**', + '!extensions/vscode-api-tests/testWorkspace/**' +]; + +const copyrightFilter = [ + '**', + '!**/*.desktop', + '!**/*.json', + '!**/*.html', + '!**/*.template', + '!**/*.md', + '!**/*.bat', + '!**/*.cmd', + '!**/*.xml', + '!**/*.sh', + '!**/*.txt', + '!**/*.xpm', + '!**/*.opts', + '!**/*.disabled', + '!build/**/*.init', + '!resources/win32/bin/code.js', + '!extensions/markdown/media/tomorrow.css', + '!extensions/html/server/src/modes/typescript/*' +]; + +const eslintFilter = [ + 'src/**/*.js', + 'build/gulpfile.*.js', + '!src/vs/loader.js', + '!src/vs/css.js', + '!src/vs/nls.js', + '!src/vs/css.build.js', + '!src/vs/nls.build.js', + '!src/**/winjs.base.raw.js', + '!src/**/raw.marked.js', + '!**/test/**' +]; + +const tslintFilter = [ + 'src/**/*.ts', + 'test/**/*.ts', + 'extensions/**/*.ts', + '!**/fixtures/**', + '!**/typings/**', + '!**/node_modules/**', + '!extensions/typescript/test/colorize-fixtures/**', + '!extensions/vscode-api-tests/testWorkspace/**', + '!extensions/**/*.test.ts' +]; + +const copyrightHeader = [ + '/*---------------------------------------------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' * Licensed under the Source EULA. See License.txt in the project root for license information.', + ' *--------------------------------------------------------------------------------------------*/' +].join('\n'); + +function reportFailures(failures) { + failures.forEach(failure => { + const name = failure.name || failure.fileName; + const position = failure.startPosition; + const line = position.lineAndCharacter ? position.lineAndCharacter.line : position.line; + const character = position.lineAndCharacter ? position.lineAndCharacter.character : position.character; + + console.error(`${name}:${line + 1}:${character + 1}:${failure.failure}`); + }); +} + +gulp.task('eslint', () => { + return gulp.src(all, { base: '.' }) + .pipe(filter(eslintFilter)) + .pipe(gulpeslint('src/.eslintrc')) + .pipe(gulpeslint.formatEach('compact')) + .pipe(gulpeslint.failAfterError()); +}); + +gulp.task('tslint', () => { + const options = { summarizeFailureOutput: true }; + + return gulp.src(all, { base: '.' }) + .pipe(filter(tslintFilter)) + .pipe(gulptslint({ rulesDirectory: 'build/lib/tslint' })) + .pipe(gulptslint.report(reportFailures, options)); +}); + +const hygiene = exports.hygiene = (some, options) => { + options = options || {}; + let errorCount = 0; + + const eol = es.through(function (file) { + if (/\r\n?/g.test(file.contents.toString('utf8'))) { + console.error(file.relative + ': Bad EOL found'); + errorCount++; + } + + this.emit('data', file); + }); + + const indentation = es.through(function (file) { + file.contents + .toString('utf8') + .split(/\r\n|\r|\n/) + .forEach((line, i) => { + if (/^\s*$/.test(line)) { + // empty or whitespace lines are OK + } else if (/^[\t]*[^\s]/.test(line)) { + // good indent + } else if (/^[\t]* \*/.test(line)) { + // block comment using an extra space + } else { + console.error(file.relative + '(' + (i + 1) + ',1): Bad whitespace indentation'); + errorCount++; + } + }); + + this.emit('data', file); + }); + + const copyrights = es.through(function (file) { + if (file.contents.toString('utf8').indexOf(copyrightHeader) !== 0) { + console.error(file.relative + ': Missing or bad copyright statement'); + errorCount++; + } + + this.emit('data', file); + }); + + const formatting = es.map(function (file, cb) { + tsfmt.processString(file.path, file.contents.toString('utf8'), { + verify: true, + tsfmt: true, + // verbose: true + }).then(result => { + if (result.error) { + console.error(result.message); + errorCount++; + } + cb(null, file); + + }, err => { + cb(err); + }); + }); + + const tsl = es.through(function (file) { + const configuration = tslint.Configuration.findConfiguration(null, '.'); + const options = { formatter: 'json', rulesDirectory: 'build/lib/tslint' }; + const contents = file.contents.toString('utf8'); + const linter = new tslint.Linter(options); + linter.lint(file.relative, contents, configuration.results); + const result = linter.getResult(); + + if (result.failureCount > 0) { + reportFailures(result.failures); + errorCount += result.failureCount; + } + + this.emit('data', file); + }); + + const result = gulp.src(some || all, { base: '.' }) + .pipe(filter(f => !f.stat.isDirectory())) + .pipe(filter(eolFilter)) + // {{SQL CARBON EDIT}} + //.pipe(options.skipEOL ? es.through() : eol) + .pipe(filter(indentationFilter)) + .pipe(indentation) + .pipe(filter(copyrightFilter)) + // {{SQL CARBON EDIT}} + //.pipe(copyrights); + + const typescript = result + .pipe(filter(tslintFilter)) + .pipe(formatting) + .pipe(tsl); + + const javascript = result + .pipe(filter(eslintFilter)) + .pipe(gulpeslint('src/.eslintrc')) + .pipe(gulpeslint.formatEach('compact')); + // {{SQL CARBON EDIT}} + // .pipe(gulpeslint.failAfterError()); + + return es.merge(typescript, javascript) + .pipe(es.through(null, function () { + // {{SQL CARBON EDIT}} + // if (errorCount > 0) { + // this.emit('error', 'Hygiene failed with ' + errorCount + ' errors. Check \'build/gulpfile.hygiene.js\'.'); + // } else { + // this.emit('end'); + // } + this.emit('end'); + })); +}; + +gulp.task('hygiene', () => hygiene()); + +// this allows us to run hygiene as a git pre-commit hook +if (require.main === module) { + const cp = require('child_process'); + + process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); + process.exit(1); + }); + + cp.exec('git config core.autocrlf', (err, out) => { + const skipEOL = out.trim() === 'true'; + + if (process.argv.length > 2) { + return hygiene(process.argv.slice(2), { skipEOL: skipEOL }).on('error', err => { + console.error(); + console.error(err); + process.exit(1); + }); + } + + cp.exec('git diff --cached --name-only', { maxBuffer: 2000 * 1024 }, (err, out) => { + if (err) { + console.error(); + console.error(err); + process.exit(1); + } + + const some = out + .split(/\r?\n/) + .filter(l => !!l); + + hygiene(some, { skipEOL: skipEOL }).on('error', err => { + console.error(); + console.error(err); + process.exit(1); + }); + }); + }); +} diff --git a/build/gulpfile.mixin.js b/build/gulpfile.mixin.js new file mode 100644 index 0000000000..6aff18e9a1 --- /dev/null +++ b/build/gulpfile.mixin.js @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const gulp = require('gulp'); +const json = require('gulp-json-editor'); +const buffer = require('gulp-buffer'); +const filter = require('gulp-filter'); +const es = require('event-stream'); +const util = require('./lib/util'); +const remote = require('gulp-remote-src'); +const zip = require('gulp-vinyl-zip'); +const assign = require('object-assign'); +const pkg = require('../package.json'); + +gulp.task('mixin', function () { + const repo = process.env['VSCODE_MIXIN_REPO']; + + if (!repo) { + console.log('Missing VSCODE_MIXIN_REPO, skipping mixin'); + return; + } + + const quality = process.env['VSCODE_QUALITY']; + + if (!quality) { + console.log('Missing VSCODE_QUALITY, skipping mixin'); + return; + } + + const url = `https://github.com/${repo}/archive/${pkg.distro}.zip`; + const opts = { base: url }; + const username = process.env['VSCODE_MIXIN_USERNAME']; + const password = process.env['VSCODE_MIXIN_PASSWORD']; + + if (username || password) { + opts.auth = { user: username || '', pass: password || '' }; + } + + console.log('Mixing in sources from \'' + url + '\':'); + + let all = remote('', opts) + .pipe(zip.src()) + .pipe(filter(function (f) { return !f.isDirectory(); })) + .pipe(util.rebase(1)); + + if (quality) { + const productJsonFilter = filter('product.json', { restore: true }); + const mixin = all + .pipe(filter(['quality/' + quality + '/**'])) + .pipe(util.rebase(2)) + .pipe(productJsonFilter) + .pipe(buffer()) + .pipe(json(o => assign({}, require('../product.json'), o))) + .pipe(productJsonFilter.restore); + + all = es.merge(mixin); + } + + return all + .pipe(es.mapSync(function (f) { + console.log(f.relative); + return f; + })) + .pipe(gulp.dest('.')); +}); \ No newline at end of file diff --git a/build/gulpfile.sql.js b/build/gulpfile.sql.js new file mode 100644 index 0000000000..9a2099bf40 --- /dev/null +++ b/build/gulpfile.sql.js @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const gulp = require('gulp'); +const util = require('./lib/util'); +const tsfmt = require('typescript-formatter'); +const es = require('event-stream'); +const filter = require('gulp-filter'); + +gulp.task('clean-mssql-extension', util.rimraf('extensions/mssql/node_modules')); +gulp.task('clean-credentials-extension', util.rimraf('extensions/credentials/node_modules')); +gulp.task('clean-client', util.rimraf('dataprotocol-node/client/node_modules')); +gulp.task('clean-jsonrpc', util.rimraf('dataprotocol-node/jsonrpc/node_modules')); +gulp.task('clean-server', util.rimraf('dataprotocol-node/server/node_modules')); +gulp.task('clean-types', util.rimraf('dataprotocol-node/types/node_modules')); +// {{SQL CARBON EDIT}} +gulp.task('clean-extensions-modules', util.rimraf('extensions-modules/node_modules')); +gulp.task('clean-protocol', ['clean-extensions-modules', 'clean-mssql-extension', 'clean-credentials-extension', 'clean-client', 'clean-jsonrpc', 'clean-server', 'clean-types']); + +// Tasks to clean extensions modules +gulp.task('clean-mssql-ext-mod', util.rimraf('extensions/mssql/node_modules/extensions-modules')); +gulp.task('clean-credentials-ext-mod', util.rimraf('extensions/credentials/node_modules/extensions-modules')); +gulp.task('clean-build-ext-mod', util.rimraf('build/node_modules/extensions-modules')); +gulp.task('clean-ext-mod', ['clean-mssql-ext-mod', 'clean-credentials-ext-mod', 'clean-build-ext-mod', 'clean-extensions-modules']); + +gulp.task('fmt', () => formatStagedFiles()); +const formatFiles = (some) => { + const formatting = es.map(function (file, cb) { + + tsfmt.processString(file.path, file.contents.toString('utf8'), { + replace: true, + tsfmt: true, + tslint: true, + tsconfig: true + // verbose: true + }).then(result => { + console.info('ran formatting on file ' + file.path + ' result: ' + result.message); + if (result.error) { + console.error(result.message); + errorCount++; + } + cb(null, file); + + }, err => { + cb(err); + }); + }); + return gulp.src(some, { base: '.' }) + .pipe(filter(f => !f.stat.isDirectory())) + .pipe(formatting); + +} + +const formatStagedFiles = () => { + const cp = require('child_process'); + cp.exec('git diff --name-only', { maxBuffer: 2000 * 1024 }, (err, out) => { + if (err) { + console.error(); + console.error(err); + process.exit(1); + } + + const some = out + .split(/\r?\n/) + .filter(l => !!l) + .filter(l => l.match(/.*.ts$/i)); + + formatFiles(some).on('error', err => { + console.error(); + console.error(err); + process.exit(1); + }); + }); + + cp.exec('git diff --cached --name-only', { maxBuffer: 2000 * 1024 }, (err, out) => { + if (err) { + console.error(); + console.error(err); + process.exit(1); + } + + const some = out + .split(/\r?\n/) + .filter(l => !!l) + .filter(l => l.match(/.*.ts$/i)); + + formatFiles(some).on('error', err => { + console.error(); + console.error(err); + process.exit(1); + }); + }); +} \ No newline at end of file diff --git a/build/gulpfile.test.js b/build/gulpfile.test.js new file mode 100644 index 0000000000..b7808d111a --- /dev/null +++ b/build/gulpfile.test.js @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const gulp = require('gulp'); +const mocha = require('gulp-mocha'); + +gulp.task('test', function () { + return gulp.src('test/all.js') + .pipe(mocha({ ui: 'tdd', delay: true })) + .once('end', function () { process.exit(); }); +}); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js new file mode 100644 index 0000000000..960420382b --- /dev/null +++ b/build/gulpfile.vscode.js @@ -0,0 +1,508 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const gulp = require('gulp'); +const fs = require('fs'); +const path = require('path'); +const es = require('event-stream'); +const azure = require('gulp-azure-storage'); +const electron = require('gulp-atom-electron'); +const vfs = require('vinyl-fs'); +const rename = require('gulp-rename'); +const replace = require('gulp-replace'); +const filter = require('gulp-filter'); +const buffer = require('gulp-buffer'); +const json = require('gulp-json-editor'); +const _ = require('underscore'); +const util = require('./lib/util'); +const ext = require('./lib/extensions'); +const buildfile = require('../src/buildfile'); +const common = require('./lib/optimize'); +const nlsDev = require('vscode-nls-dev'); +const root = path.dirname(__dirname); +const commit = util.getVersion(root); +const packageJson = require('../package.json'); +const product = require('../product.json'); +const shrinkwrap = require('../npm-shrinkwrap.json'); +const crypto = require('crypto'); +const i18n = require('./lib/i18n'); +var del = require('del'); + +// {{SQL CARBON EDIT}} +const serviceInstaller = require('extensions-modules/lib/languageservice/serviceInstallerUtil'); +const glob = require('glob'); + +const productDependencies = Object.keys(product.dependencies || {}); +const dependencies = Object.keys(shrinkwrap.dependencies) + .concat(productDependencies); // additional dependencies from our product configuration +const baseModules = Object.keys(process.binding('natives')).filter(n => !/^_|\//.test(n)); +// {{SQL CARBON EDIT}} +const nodeModules = [ + 'electron', + 'original-fs', + 'rxjs/Observable', + 'rxjs/Subject', + 'rxjs/Observer', + 'ng2-charts/ng2-charts', + 'rangy/lib/rangy-textrange'] + .concat(dependencies) + .concat(baseModules); + + +// Build + +const builtInExtensions = [ + { name: 'ms-vscode.node-debug', version: '1.16.10' }, + { name: 'ms-vscode.node-debug2', version: '1.16.9' } +]; + +const excludedExtensions = [ + 'vscode-api-tests', + 'vscode-colorize-tests' +]; + +const vscodeEntryPoints = _.flatten([ + buildfile.entrypoint('vs/workbench/workbench.main'), + buildfile.base, + buildfile.workbench, + buildfile.code +]); + +const vscodeResources = [ + 'out-build/main.js', + 'out-build/cli.js', + 'out-build/bootstrap.js', + 'out-build/bootstrap-amd.js', + 'out-build/paths.js', + 'out-build/vs/**/*.{svg,png,cur,html}', + 'out-build/vs/base/node/startupTimers.js', + 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh}', + 'out-build/vs/base/browser/ui/octiconLabel/octicons/**', + 'out-build/vs/workbench/browser/media/*-theme.css', + 'out-build/vs/workbench/electron-browser/bootstrap/**', + 'out-build/vs/workbench/parts/debug/**/*.json', + 'out-build/vs/workbench/parts/execution/**/*.scpt', + 'out-build/vs/workbench/parts/html/browser/webview-pre.js', + 'out-build/vs/**/markdown.css', + 'out-build/vs/workbench/parts/tasks/**/*.json', + 'out-build/vs/workbench/parts/terminal/electron-browser/terminalProcess.js', + 'out-build/vs/workbench/parts/welcome/walkThrough/**/*.md', + 'out-build/vs/workbench/services/files/**/*.exe', + 'out-build/vs/workbench/services/files/**/*.md', + 'out-build/vs/code/electron-browser/sharedProcess.js', + // {{SQL CARBON EDIT}} + 'out-build/sql/workbench/electron-browser/splashscreen/*', + 'out-build/sql/**/*.{svg,png,cur,html}', + 'out-build/sql/base/browser/ui/table/media/*.{gif,png,svg}', + 'out-build/sql/base/browser/ui/checkbox/media/*.{gif,png,svg}', + 'out-build/sql/parts/admin/**/*.html', + 'out-build/sql/parts/connection/connectionDialog/media/*.{gif,png,svg}', + 'out-build/sql/parts/common/dblist/**/*.html', + 'out-build/sql/parts/dashboard/**/*.html', + 'out-build/sql/parts/disasterRecovery/**/*.html', + 'out-build/sql/parts/common/modal/media/**', + 'out-build/sql/parts/grid/load/lib/**', + 'out-build/sql/parts/grid/load/loadJquery.js', + 'out-build/sql/parts/grid/media/**', + 'out-build/sql/parts/grid/views/**/*.html', + 'out-build/sql/parts/tasks/**/*.html', + 'out-build/sql/parts/taskHistory/viewlet/media/**', + 'out-build/sql/media/objectTypes/*.svg', + 'out-build/sql/media/icons/*.svg', + '!**/test/**' +]; + +const BUNDLED_FILE_HEADER = [ + '/*!--------------------------------------------------------', + ' * Copyright (C) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' +].join('\n'); + +var languages = ['chs', 'cht', 'jpn', 'kor', 'deu', 'fra', 'esn', 'rus', 'ita']; +if (process.env.VSCODE_QUALITY !== 'stable') { + languages = languages.concat(['ptb', 'hun', 'trk']); // Add languages requested by the community to non-stable builds +} + +gulp.task('clean-optimized-vscode', util.rimraf('out-vscode')); +gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compile-extensions-build'], common.optimizeTask({ + entryPoints: vscodeEntryPoints, + otherSources: [], + resources: vscodeResources, + loaderConfig: common.loaderConfig(nodeModules), + header: BUNDLED_FILE_HEADER, + out: 'out-vscode', + languages: languages +})); + + +gulp.task('optimize-index-js', ['optimize-vscode'], () => { + const fullpath = path.join(process.cwd(), 'out-vscode/vs/workbench/electron-browser/bootstrap/index.js'); + const contents = fs.readFileSync(fullpath).toString(); + const newContents = contents.replace('[/*BUILD->INSERT_NODE_MODULES*/]', JSON.stringify(nodeModules)); + fs.writeFileSync(fullpath, newContents); +}); + +const baseUrl = `https://ticino.blob.core.windows.net/sourcemaps/${commit}/core`; +gulp.task('clean-minified-vscode', util.rimraf('out-vscode-min')); +gulp.task('minify-vscode', ['clean-minified-vscode', 'optimize-index-js'], common.minifyTask('out-vscode', baseUrl)); + +// Package +const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8')); + +const config = { + version: packageJson.electronVersion, + productAppName: product.nameLong, + companyName: 'Microsoft Corporation', + copyright: 'Copyright (C) 2017 Microsoft. All rights reserved', + darwinIcon: 'resources/darwin/code.icns', + darwinBundleIdentifier: product.darwinBundleIdentifier, + darwinApplicationCategoryType: 'public.app-category.developer-tools', + darwinHelpBookFolder: 'VS Code HelpBook', + darwinHelpBookName: 'VS Code HelpBook', + darwinBundleDocumentTypes: [{ + name: product.nameLong + ' document', + role: 'Editor', + ostypes: ["TEXT", "utxt", "TUTX", "****"], + // {{SQL CARBON EDIT}} + extensions: ["csv", "json", "showplan", "sql", "xml"], + iconFile: 'resources/darwin/code_file.icns' + }], + darwinBundleURLTypes: [{ + role: 'Viewer', + name: product.nameLong, + urlSchemes: [product.urlProtocol] + }], + darwinCredits: darwinCreditsTemplate ? new Buffer(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : void 0, + linuxExecutableName: product.applicationName, + winIcon: 'resources/win32/code.ico', + token: process.env['VSCODE_MIXIN_PASSWORD'] || process.env['GITHUB_TOKEN'] || void 0, + repo: product.electronRepository || void 0 +}; + +function getElectron(arch) { + return () => { + const electronOpts = _.extend({}, config, { + platform: process.platform, + arch, + ffmpegChromium: true, + keepDefaultApp: true + }); + + return gulp.src('package.json') + .pipe(json({ name: product.nameShort })) + .pipe(electron(electronOpts)) + .pipe(filter(['**', '!**/app/package.json'])) + .pipe(vfs.dest('.build/electron')); + }; +} + +gulp.task('clean-electron', util.rimraf('.build/electron')); +gulp.task('electron', ['clean-electron'], getElectron(process.arch)); +gulp.task('electron-ia32', ['clean-electron'], getElectron('ia32')); +gulp.task('electron-x64', ['clean-electron'], getElectron('x64')); + + +/** + * Compute checksums for some files. + * + * @param {string} out The out folder to read the file from. + * @param {string[]} filenames The paths to compute a checksum for. + * @return {Object} A map of paths to checksums. + */ +function computeChecksums(out, filenames) { + var result = {}; + filenames.forEach(function (filename) { + var fullPath = path.join(process.cwd(), out, filename); + result[filename] = computeChecksum(fullPath); + }); + return result; +} + +/** + * Compute checksum for a file. + * + * @param {string} filename The absolute path to a filename. + * @return {string} The checksum for `filename`. + */ +function computeChecksum(filename) { + var contents = fs.readFileSync(filename); + + var hash = crypto + .createHash('md5') + .update(contents) + .digest('base64') + .replace(/=+$/, ''); + + return hash; +} + +function packageTask(platform, arch, opts) { + opts = opts || {}; + + // {{SQL CARBON EDIT}} + const destination = path.join(path.dirname(root), 'sqlops') + (platform ? '-' + platform : '') + (arch ? '-' + arch : ''); + platform = platform || process.platform; + + return () => { + const out = opts.minified ? 'out-vscode-min' : 'out-vscode'; + + const checksums = computeChecksums(out, [ + 'vs/workbench/workbench.main.js', + 'vs/workbench/workbench.main.css', + 'vs/workbench/electron-browser/bootstrap/index.html', + 'vs/workbench/electron-browser/bootstrap/index.js', + 'vs/workbench/electron-browser/bootstrap/preload.js' + ]); + + const src = gulp.src(out + '/**', { base: '.' }) + .pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + out), 'out'); })); + + const root = path.resolve(path.join(__dirname, '..')); + const localExtensionDescriptions = glob.sync('extensions/*/package.json') + .map(manifestPath => { + const extensionPath = path.dirname(path.join(root, manifestPath)); + const extensionName = path.basename(extensionPath); + return { name: extensionName, path: extensionPath }; + }) + .filter(({ name }) => excludedExtensions.indexOf(name) === -1) + .filter(({ name }) => builtInExtensions.every(b => b.name !== name)); + + const localExtensions = es.merge(...localExtensionDescriptions.map(extension => { + const nlsFilter = filter('**/*.nls.json', { restore: true }); + + return ext.fromLocal(extension.path) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)) + // // TODO@Dirk: this filter / buffer is here to make sure the nls.json files are buffered + .pipe(nlsFilter) + .pipe(buffer()) + .pipe(nlsDev.createAdditionalLanguageFiles(languages, path.join(__dirname, '..', 'i18n'))) + .pipe(nlsFilter.restore); + })); + + const localExtensionDependencies = gulp.src('extensions/node_modules/**', { base: '.' }); + + // {{SQL CARBON EDIT}} + const sources = es.merge(src, localExtensions, localExtensionDependencies) + .pipe(util.setExecutableBit(['**/*.sh'])) + .pipe(filter(['**', + '!**/*.js.map', + '!extensions/**/node_modules/**/{test, tests}/**', + '!extensions/**/node_modules/**/test.js'])); + + let version = packageJson.version; + const quality = product.quality; + + if (quality && quality !== 'stable') { + version += '-' + quality; + } + + const name = product.nameShort; + const packageJsonStream = gulp.src(['package.json'], { base: '.' }) + .pipe(json({ name, version })); + + const date = new Date().toISOString(); + const productJsonStream = gulp.src(['product.json'], { base: '.' }) + .pipe(json({ commit, date, checksums })); + + const license = gulp.src(['LICENSES.chromium.html', 'LICENSE.txt', 'ThirdPartyNotices.txt', 'licenses/**'], { base: '.' }); + + const watermark = gulp.src(['resources/letterpress.svg', 'resources/letterpress-dark.svg', 'resources/letterpress-hc.svg'], { base: '.' }); + + // TODO the API should be copied to `out` during compile, not here + const api = gulp.src('src/vs/vscode.d.ts').pipe(rename('out/vs/vscode.d.ts')); + // {{SQL CARBON EDIT}} + const dataApi = gulp.src('src/vs/data.d.ts').pipe(rename('out/sql/data.d.ts')); + + const depsSrc = _.flatten(dependencies + .map(function (d) { return ['node_modules/' + d + '/**', + '!node_modules/' + d + '/**/{test,tests}/**', + '!node_modules/' + d + '/**/test.*', + '!node_modules/' + d + '/**/*.test.*']; })); + + const deps = gulp.src(depsSrc, { base: '.', dot: true }) + .pipe(filter(['**', '!**/package-lock.json'])) + .pipe(util.cleanNodeModule('fsevents', ['binding.gyp', 'fsevents.cc', 'build/**', 'src/**', 'test/**'], ['**/*.node'])) + .pipe(util.cleanNodeModule('oniguruma', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/*.js'])) + .pipe(util.cleanNodeModule('windows-mutex', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) + .pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node'])) + .pipe(util.cleanNodeModule('native-watchdog', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) + .pipe(util.cleanNodeModule('jschardet', ['dist/**'])) + .pipe(util.cleanNodeModule('windows-foreground-love', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) + .pipe(util.cleanNodeModule('windows-process-tree', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) + .pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/index.js'])) + .pipe(util.cleanNodeModule('v8-profiler', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/index.js'])) + .pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'tools/**'], ['build/Release/**'])) + .pipe(util.cleanNodeModule('nsfw', ['binding.gyp', 'build/**', 'src/**', 'openpa/**', 'includes/**'], ['**/*.node', '**/*.a'])) + .pipe(util.cleanNodeModule('vsda', ['binding.gyp', 'README.md', 'build/**', '*.bat', '*.sh', '*.cpp', '*.h'], ['build/Release/vsda.node'])); + + let all = es.merge( + packageJsonStream, + productJsonStream, + license, + watermark, + api, + // {{SQL CARBON EDIT}} + dataApi, + sources, + deps + ); + + if (platform === 'win32') { + all = es.merge(all, gulp.src('resources/win32/code_file.ico', { base: '.' })); + } else if (platform === 'linux') { + all = es.merge(all, gulp.src('resources/linux/code.png', { base: '.' })); + } else if (platform === 'darwin') { + const shortcut = gulp.src('resources/darwin/bin/code.sh') + .pipe(rename('bin/code')); + + all = es.merge(all, shortcut); + } + + let result = all + .pipe(util.skipDirectories()) + .pipe(util.fixWin32DirectoryPermissions()) + .pipe(electron(_.extend({}, config, { platform, arch, ffmpegChromium: true }))) + .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'])); + + if (platform === 'win32') { + result = es.merge(result, gulp.src('resources/win32/bin/code.js', { base: 'resources/win32' })); + + result = es.merge(result, gulp.src('resources/win32/bin/code.cmd', { base: 'resources/win32' }) + .pipe(replace('@@NAME@@', product.nameShort)) + .pipe(rename(function (f) { f.basename = product.applicationName; }))); + + result = es.merge(result, gulp.src('resources/win32/bin/code.sh', { base: 'resources/win32' }) + .pipe(replace('@@NAME@@', product.nameShort)) + .pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; }))); + } else if (platform === 'linux') { + result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('bin/' + product.applicationName))); + } + + return result.pipe(vfs.dest(destination)); + }; +} + +const buildRoot = path.dirname(root); + +// {{SQL CARBON EDIT}} +gulp.task('clean-vscode-win32-ia32', util.rimraf(path.join(buildRoot, 'sqlops-win32-ia32'))); +gulp.task('clean-vscode-win32-x64', util.rimraf(path.join(buildRoot, 'sqlops-win32-x64'))); +gulp.task('clean-vscode-darwin', util.rimraf(path.join(buildRoot, 'sqlops-darwin'))); +gulp.task('clean-vscode-linux-ia32', util.rimraf(path.join(buildRoot, 'sqlops-linux-ia32'))); +gulp.task('clean-vscode-linux-x64', util.rimraf(path.join(buildRoot, 'sqlops-linux-x64'))); +gulp.task('clean-vscode-linux-arm', util.rimraf(path.join(buildRoot, 'sqlops-linux-arm'))); + +gulp.task('vscode-win32-ia32', ['optimize-vscode', 'clean-vscode-win32-ia32'], packageTask('win32', 'ia32')); +gulp.task('vscode-win32-x64', ['optimize-vscode', 'clean-vscode-win32-x64'], packageTask('win32', 'x64')); +gulp.task('vscode-darwin', ['optimize-vscode', 'clean-vscode-darwin'], packageTask('darwin')); +gulp.task('vscode-linux-ia32', ['optimize-vscode', 'clean-vscode-linux-ia32'], packageTask('linux', 'ia32')); +gulp.task('vscode-linux-x64', ['optimize-vscode', 'clean-vscode-linux-x64'], packageTask('linux', 'x64')); +gulp.task('vscode-linux-arm', ['optimize-vscode', 'clean-vscode-linux-arm'], packageTask('linux', 'arm')); + +gulp.task('vscode-win32-ia32-min', ['minify-vscode', 'clean-vscode-win32-ia32'], packageTask('win32', 'ia32', { minified: true })); +gulp.task('vscode-win32-x64-min', ['minify-vscode', 'clean-vscode-win32-x64'], packageTask('win32', 'x64', { minified: true })); +gulp.task('vscode-darwin-min', ['minify-vscode', 'clean-vscode-darwin'], packageTask('darwin', null, { minified: true })); +gulp.task('vscode-linux-ia32-min', ['minify-vscode', 'clean-vscode-linux-ia32'], packageTask('linux', 'ia32', { minified: true })); +gulp.task('vscode-linux-x64-min', ['minify-vscode', 'clean-vscode-linux-x64'], packageTask('linux', 'x64', { minified: true })); +gulp.task('vscode-linux-arm-min', ['minify-vscode', 'clean-vscode-linux-arm'], packageTask('linux', 'arm', { minified: true })); + +// Transifex Localizations +const vscodeLanguages = [ + 'zh-hans', + 'zh-hant', + 'ja', + 'ko', + 'de', + 'fr', + 'es', + 'ru', + 'it', + 'pt-br', + 'hu', + 'tr' +]; +const setupDefaultLanguages = [ + 'zh-hans', + 'zh-hant', + 'ko' +]; + +const apiHostname = process.env.TRANSIFEX_API_URL; +const apiName = process.env.TRANSIFEX_API_NAME; +const apiToken = process.env.TRANSIFEX_API_TOKEN; + +gulp.task('vscode-translations-push', ['optimize-vscode'], function () { + const pathToMetadata = './out-vscode/nls.metadata.json'; + const pathToExtensions = './extensions/**/*.nls.json'; + const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}'; + + return es.merge( + gulp.src(pathToMetadata).pipe(i18n.prepareXlfFiles()), + gulp.src(pathToSetup).pipe(i18n.prepareXlfFiles()), + gulp.src(pathToExtensions).pipe(i18n.prepareXlfFiles('vscode-extensions')) + ).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken)); +}); + +gulp.task('vscode-translations-pull', function () { + return es.merge( + i18n.pullXlfFiles('vscode-editor', apiHostname, apiName, apiToken, vscodeLanguages), + i18n.pullXlfFiles('vscode-workbench', apiHostname, apiName, apiToken, vscodeLanguages), + i18n.pullXlfFiles('vscode-extensions', apiHostname, apiName, apiToken, vscodeLanguages), + i18n.pullXlfFiles('vscode-setup', apiHostname, apiName, apiToken, setupDefaultLanguages) + ).pipe(vfs.dest('../vscode-localization')); +}); + +gulp.task('vscode-translations-import', function () { + return gulp.src('../vscode-localization/**/*.xlf').pipe(i18n.prepareJsonFiles()).pipe(vfs.dest('./i18n')); +}); + +// Sourcemaps + +gulp.task('upload-vscode-sourcemaps', ['minify-vscode'], () => { + const vs = gulp.src('out-vscode-min/**/*.map', { base: 'out-vscode-min' }) + .pipe(es.mapSync(f => { + f.path = `${f.base}/core/${f.relative}`; + return f; + })); + + const extensions = gulp.src('extensions/**/out/**/*.map', { base: '.' }); + + return es.merge(vs, extensions) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + key: process.env.AZURE_STORAGE_ACCESS_KEY, + container: 'sourcemaps', + prefix: commit + '/' + })); +}); + +// {{SQL CARBON EDIT}} +// Install service locally before building carbon + +function installService(extObj) { + var installer = new serviceInstaller.ServiceInstaller(extObj, true); + installer.getServiceInstallDirectoryRoot().then(serviceInstallFolder => { + console.log('Cleaning up the install folder: ' + serviceInstallFolder); + del(serviceInstallFolder + '/*').then(() => { + console.log('Installing the service. Install folder: ' + serviceInstallFolder); + installer.installService(); + }, delError => { + console.log('failed to delete the install folder error: ' + delError); + }); + }, getFolderPathError => { + console.log('failed to call getServiceInstallDirectoryRoot error: ' + getFolderPathError); + }); + +} + +gulp.task('install-sqltoolsservice', () => { + var mssqlExt = require('../extensions/mssql/client/out/models/constants'); + var extObj = new mssqlExt.Constants(); + return installService(extObj); +}); diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js new file mode 100644 index 0000000000..745e8a7c33 --- /dev/null +++ b/build/gulpfile.vscode.linux.js @@ -0,0 +1,289 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const gulp = require('gulp'); +const replace = require('gulp-replace'); +const rename = require('gulp-rename'); +const shell = require('gulp-shell'); +const es = require('event-stream'); +const vfs = require('vinyl-fs'); +const util = require('./lib/util'); +const packageJson = require('../package.json'); +const product = require('../product.json'); +const rpmDependencies = require('../resources/linux/rpm/dependencies'); + +const linuxPackageRevision = Math.floor(new Date().getTime() / 1000); + +const flatpakManifest = { + appId: product.darwinBundleIdentifier, // We need a reverse-url style identifier. + sdk: 'org.freedesktop.Sdk', + runtime: 'org.freedesktop.Sdk', + runtimeVersion: '1.4', + base: 'io.atom.electron.BaseApp', + baseFlatpakref: 'https://s3-us-west-2.amazonaws.com/electron-flatpak.endlessm.com/electron-base-app-master.flatpakref', + command: product.applicationName, + symlinks: [ + ['/share/' + product.applicationName + '/bin/' + product.applicationName, '/bin/' + product.applicationName], + ], + finishArgs: [ + '--share=ipc', '--socket=x11', // Allow showing X11 windows. + '--share=network', // Network access (e.g. for installing extension). + '--filesystem=host', // Allow access to the whole file system. + '--device=dri', // Allow OpenGL rendering. + '--filesystem=/tmp', // Needed for Chromium's single instance check. + '--socket=pulseaudio', // Some extensions may want to play sounds... + '--talk-name=org.freedesktop.Notifications', // ...or pop up notifications. + ], +}; + + +function getDebPackageArch(arch) { + return { x64: 'amd64', ia32: 'i386', arm: 'armhf' }[arch]; +} + +function prepareDebPackage(arch) { + // {{SQL CARBON EDIT}} + const binaryDir = '../sqlops-linux-' + arch; + const debArch = getDebPackageArch(arch); + const destination = '.build/linux/deb/' + debArch + '/' + product.applicationName + '-' + debArch; + + return function () { + const desktop = gulp.src('resources/linux/code.desktop', { base: '.' }) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME_SHORT@@', product.nameShort)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('usr/share/applications/' + product.applicationName + '.desktop')); + + const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' }) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@LICENSE@@', product.licenseName)) + .pipe(rename('usr/share/appdata/' + product.applicationName + '.appdata.xml')); + + const icon = gulp.src('resources/linux/code.png', { base: '.' }) + .pipe(rename('usr/share/pixmaps/' + product.applicationName + '.png')); + + const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) + .pipe(rename(function (p) { p.dirname = 'usr/share/' + product.applicationName + '/' + p.dirname; })); + + let size = 0; + const control = code.pipe(es.through( + function (f) { size += f.isDirectory() ? 4096 : f.contents.length; }, + function () { + const that = this; + gulp.src('resources/linux/debian/control.template', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@VERSION@@', packageJson.version + '-' + linuxPackageRevision)) + .pipe(replace('@@ARCHITECTURE@@', debArch)) + .pipe(replace('@@INSTALLEDSIZE@@', Math.ceil(size / 1024))) + .pipe(rename('DEBIAN/control')) + .pipe(es.through(function (f) { that.emit('data', f); }, function () { that.emit('end'); })); + })); + + const prerm = gulp.src('resources/linux/debian/prerm.template', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('DEBIAN/prerm')); + + const postrm = gulp.src('resources/linux/debian/postrm.template', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('DEBIAN/postrm')); + + const postinst = gulp.src('resources/linux/debian/postinst.template', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@ARCHITECTURE@@', debArch)) + .pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@')) + .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) + .pipe(rename('DEBIAN/postinst')); + + const all = es.merge(control, postinst, postrm, prerm, desktop, appdata, icon, code); + + return all.pipe(vfs.dest(destination)); + }; +} + +function buildDebPackage(arch) { + const debArch = getDebPackageArch(arch); + return shell.task([ + 'chmod 755 ' + product.applicationName + '-' + debArch + '/DEBIAN/postinst ' + product.applicationName + '-' + debArch + '/DEBIAN/prerm ' + product.applicationName + '-' + debArch + '/DEBIAN/postrm', + 'mkdir -p deb', + 'fakeroot dpkg-deb -b ' + product.applicationName + '-' + debArch + ' deb', + 'dpkg-scanpackages deb /dev/null > Packages' + ], { cwd: '.build/linux/deb/' + debArch }); +} + +function getRpmBuildPath(rpmArch) { + return '.build/linux/rpm/' + rpmArch + '/rpmbuild'; +} + +function getRpmPackageArch(arch) { + return { x64: 'x86_64', ia32: 'i386', arm: 'armhf' }[arch]; +} + +function prepareRpmPackage(arch) { + // {{SQL CARBON EDIT}} + const binaryDir = '../sqlops-linux-' + arch; + const rpmArch = getRpmPackageArch(arch); + + return function () { + const desktop = gulp.src('resources/linux/code.desktop', { base: '.' }) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME_SHORT@@', product.nameShort)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('BUILD/usr/share/applications/' + product.applicationName + '.desktop')); + + const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' }) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@LICENSE@@', product.licenseName)) + .pipe(rename('usr/share/appdata/' + product.applicationName + '.appdata.xml')); + + const icon = gulp.src('resources/linux/code.png', { base: '.' }) + .pipe(rename('BUILD/usr/share/pixmaps/' + product.applicationName + '.png')); + + const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) + .pipe(rename(function (p) { p.dirname = 'BUILD/usr/share/' + product.applicationName + '/' + p.dirname; })); + + const spec = gulp.src('resources/linux/rpm/code.spec.template', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@VERSION@@', packageJson.version)) + .pipe(replace('@@RELEASE@@', linuxPackageRevision)) + .pipe(replace('@@ARCHITECTURE@@', rpmArch)) + .pipe(replace('@@LICENSE@@', product.licenseName)) + .pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@')) + .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) + .pipe(replace('@@DEPENDENCIES@@', rpmDependencies[rpmArch].join(', '))) + .pipe(rename('SPECS/' + product.applicationName + '.spec')); + + const specIcon = gulp.src('resources/linux/rpm/code.xpm', { base: '.' }) + .pipe(rename('SOURCES/' + product.applicationName + '.xpm')); + + const all = es.merge(code, desktop, appdata, icon, spec, specIcon); + + return all.pipe(vfs.dest(getRpmBuildPath(rpmArch))); + }; +} + +function buildRpmPackage(arch) { + const rpmArch = getRpmPackageArch(arch); + const rpmBuildPath = getRpmBuildPath(rpmArch); + const rpmOut = rpmBuildPath + '/RPMS/' + rpmArch; + const destination = '.build/linux/rpm/' + rpmArch; + + return shell.task([ + 'mkdir -p ' + destination, + 'HOME="$(pwd)/' + destination + '" fakeroot rpmbuild -bb ' + rpmBuildPath + '/SPECS/' + product.applicationName + '.spec --target=' + rpmArch, + 'cp "' + rpmOut + '/$(ls ' + rpmOut + ')" ' + destination + '/' + ]); +} + +function getFlatpakArch(arch) { + return { x64: 'x86_64', ia32: 'i386', arm: 'arm' }[arch]; +} + +function prepareFlatpak(arch) { + // {{SQL CARBON EDIT}} + const binaryDir = '../sqlops-linux-' + arch; + const flatpakArch = getFlatpakArch(arch); + const destination = '.build/linux/flatpak/' + flatpakArch; + + return function () { + // This is not imported in the global scope to avoid requiring ImageMagick + // (or GraphicsMagick) when not building building Flatpak bundles. + const imgResize = require('gulp-image-resize'); + + const all = [16, 24, 32, 48, 64, 128, 192, 256, 512].map(function (size) { + return gulp.src('resources/linux/code.png', { base: '.' }) + .pipe(imgResize({ width: size, height: size, format: "png", noProfile: true })) + .pipe(rename('share/icons/hicolor/' + size + 'x' + size + '/apps/' + flatpakManifest.appId + '.png')); + }); + + all.push(gulp.src('resources/linux/code.desktop', { base: '.' }) + .pipe(replace('Exec=/usr/share/@@NAME@@/@@NAME@@', 'Exec=' + product.applicationName)) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME_SHORT@@', product.nameShort)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('share/applications/' + flatpakManifest.appId + '.desktop'))); + + all.push(gulp.src('resources/linux/code.appdata.xml', { base: '.' }) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME@@', flatpakManifest.appId)) + .pipe(replace('@@LICENSE@@', product.licenseName)) + .pipe(rename('share/appdata/' + flatpakManifest.appId + '.appdata.xml'))); + + all.push(gulp.src(binaryDir + '/**/*', { base: binaryDir }) + .pipe(rename(function (p) { + p.dirname = 'share/' + product.applicationName + '/' + p.dirname; + }))); + + return es.merge(all).pipe(vfs.dest(destination)); + }; +} + +function buildFlatpak(arch) { + const flatpakArch = getFlatpakArch(arch); + const manifest = {}; + for (var k in flatpakManifest) { + manifest[k] = flatpakManifest[k]; + } + manifest.files = [ + ['.build/linux/flatpak/' + flatpakArch, '/'], + ]; + const buildOptions = { + arch: flatpakArch, + subject: product.nameLong + ' ' + packageJson.version + '.' + linuxPackageRevision, + }; + // If requested, use the configured path for the OSTree repository. + if (process.env.FLATPAK_REPO) { + buildOptions.repoDir = process.env.FLATPAK_REPO; + } else { + buildOptions.bundlePath = manifest.appId + '-' + flatpakArch + '.flatpak'; + } + // Setup PGP signing if requested. + if (process.env.GPG_KEY_ID !== undefined) { + buildOptions.gpgSign = process.env.GPG_KEY_ID; + if (process.env.GPG_HOMEDIR) { + buildOptions.gpgHomedir = process.env.GPG_HOME_DIR; + } + } + return function (cb) { + require('flatpak-bundler').bundle(manifest, buildOptions, cb); + }; +} + +gulp.task('clean-vscode-linux-ia32-deb', util.rimraf('.build/linux/deb/i386')); +gulp.task('clean-vscode-linux-x64-deb', util.rimraf('.build/linux/deb/amd64')); +gulp.task('clean-vscode-linux-arm-deb', util.rimraf('.build/linux/deb/armhf')); +gulp.task('clean-vscode-linux-ia32-rpm', util.rimraf('.build/linux/rpm/i386')); +gulp.task('clean-vscode-linux-x64-rpm', util.rimraf('.build/linux/rpm/x86_64')); +gulp.task('clean-vscode-linux-arm-rpm', util.rimraf('.build/linux/rpm/armhf')); + +gulp.task('vscode-linux-ia32-prepare-deb', ['clean-vscode-linux-ia32-deb'], prepareDebPackage('ia32')); +gulp.task('vscode-linux-x64-prepare-deb', ['clean-vscode-linux-x64-deb'], prepareDebPackage('x64')); +gulp.task('vscode-linux-arm-prepare-deb', ['clean-vscode-linux-arm-deb'], prepareDebPackage('arm')); +gulp.task('vscode-linux-ia32-build-deb', ['vscode-linux-ia32-prepare-deb'], buildDebPackage('ia32')); +gulp.task('vscode-linux-x64-build-deb', ['vscode-linux-x64-prepare-deb'], buildDebPackage('x64')); +gulp.task('vscode-linux-arm-build-deb', ['vscode-linux-arm-prepare-deb'], buildDebPackage('arm')); + +gulp.task('vscode-linux-ia32-prepare-rpm', ['clean-vscode-linux-ia32-rpm'], prepareRpmPackage('ia32')); +gulp.task('vscode-linux-x64-prepare-rpm', ['clean-vscode-linux-x64-rpm'], prepareRpmPackage('x64')); +gulp.task('vscode-linux-arm-prepare-rpm', ['clean-vscode-linux-arm-rpm'], prepareRpmPackage('arm')); +gulp.task('vscode-linux-ia32-build-rpm', ['vscode-linux-ia32-prepare-rpm'], buildRpmPackage('ia32')); +gulp.task('vscode-linux-x64-build-rpm', ['vscode-linux-x64-prepare-rpm'], buildRpmPackage('x64')); +gulp.task('vscode-linux-arm-build-rpm', ['vscode-linux-arm-prepare-rpm'], buildRpmPackage('arm')); + +gulp.task('clean-vscode-linux-ia32-flatpak', util.rimraf('.build/linux/flatpak/i386')); +gulp.task('clean-vscode-linux-x64-flatpak', util.rimraf('.build/linux/flatpak/x86_64')); +gulp.task('clean-vscode-linux-arm-flatpak', util.rimraf('.build/linux/flatpak/arm')); + +gulp.task('vscode-linux-ia32-prepare-flatpak', ['clean-vscode-linux-ia32-flatpak'], prepareFlatpak('ia32')); +gulp.task('vscode-linux-x64-prepare-flatpak', ['clean-vscode-linux-x64-flatpak'], prepareFlatpak('x64')); +gulp.task('vscode-linux-arm-prepare-flatpak', ['clean-vscode-linux-arm-flatpak'], prepareFlatpak('arm')); + +gulp.task('vscode-linux-ia32-flatpak', ['vscode-linux-ia32-prepare-flatpak'], buildFlatpak('ia32')); +gulp.task('vscode-linux-x64-flatpak', ['vscode-linux-x64-prepare-flatpak'], buildFlatpak('x64')); +gulp.task('vscode-linux-arm-flatpak', ['vscode-linux-arm-prepare-flatpak'], buildFlatpak('arm')); diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js new file mode 100644 index 0000000000..74a88c368a --- /dev/null +++ b/build/gulpfile.vscode.win32.js @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const gulp = require('gulp'); +const path = require('path'); +const assert = require('assert'); +const cp = require('child_process'); +const _7z = require('7zip')['7z']; +const util = require('./lib/util'); +const pkg = require('../package.json'); +const product = require('../product.json'); + +const repoPath = path.dirname(__dirname); +// {{SQL CARBON EDIT}} +const buildPath = arch => path.join(path.dirname(repoPath), `sqlops-win32-${arch}`); +const zipDir = arch => path.join(repoPath, '.build', `win32-${arch}`, 'archive'); +const zipPath = arch => path.join(zipDir(arch), `VSCode-win32-${arch}.zip`); +const setupDir = arch => path.join(repoPath, '.build', `win32-${arch}`, 'setup'); +const issPath = path.join(__dirname, 'win32', 'code.iss'); +const innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup-compiler'))), 'bin', 'ISCC.exe'); + +function packageInnoSetup(iss, options, cb) { + options = options || {}; + + const definitions = options.definitions || {}; + const keys = Object.keys(definitions); + + keys.forEach(key => assert(typeof definitions[key] === 'string', `Missing value for '${key}' in Inno Setup package step`)); + + const defs = keys.map(key => `/d${key}=${definitions[key]}`); + const args = [iss].concat(defs); + + cp.spawn(innoSetupPath, args, { stdio: 'inherit' }) + .on('error', cb) + .on('exit', () => cb(null)); +} + +function buildWin32Setup(arch) { + return cb => { + const ia32AppId = product.win32AppId; + const x64AppId = product.win32x64AppId; + + const definitions = { + NameLong: product.nameLong, + NameShort: product.nameShort, + DirName: product.win32DirName, + Version: pkg.version, + RawVersion: pkg.version.replace(/-\w+$/, ''), + NameVersion: product.win32NameVersion, + ExeBasename: product.nameShort, + RegValueName: product.win32RegValueName, + ShellNameShort: product.win32ShellNameShort, + AppMutex: product.win32MutexName, + Arch: arch, + AppId: arch === 'ia32' ? ia32AppId : x64AppId, + IncompatibleAppId: arch === 'ia32' ? x64AppId : ia32AppId, + AppUserId: product.win32AppUserModelId, + ArchitecturesAllowed: arch === 'ia32' ? '' : 'x64', + ArchitecturesInstallIn64BitMode: arch === 'ia32' ? '' : 'x64', + SourceDir: buildPath(arch), + RepoDir: repoPath, + OutputDir: setupDir(arch) + }; + + packageInnoSetup(issPath, { definitions }, cb); + }; +} + +gulp.task('clean-vscode-win32-ia32-setup', util.rimraf(setupDir('ia32'))); +gulp.task('vscode-win32-ia32-setup', ['clean-vscode-win32-ia32-setup'], buildWin32Setup('ia32')); + +gulp.task('clean-vscode-win32-x64-setup', util.rimraf(setupDir('x64'))); +gulp.task('vscode-win32-x64-setup', ['clean-vscode-win32-x64-setup'], buildWin32Setup('x64')); + +function archiveWin32Setup(arch) { + return cb => { + const args = ['a', '-tzip', zipPath(arch), '.', '-r']; + + cp.spawn(_7z, args, { stdio: 'inherit', cwd: buildPath(arch) }) + .on('error', cb) + .on('exit', () => cb(null)); + }; +} + +gulp.task('clean-vscode-win32-ia32-archive', util.rimraf(zipDir('ia32'))); +gulp.task('vscode-win32-ia32-archive', ['clean-vscode-win32-ia32-archive'], archiveWin32Setup('ia32')); + +gulp.task('clean-vscode-win32-x64-archive', util.rimraf(zipDir('x64'))); +gulp.task('vscode-win32-x64-archive', ['clean-vscode-win32-x64-archive'], archiveWin32Setup('x64')); diff --git a/build/lib/bundle.js b/build/lib/bundle.js new file mode 100644 index 0000000000..2c95f4c3b2 --- /dev/null +++ b/build/lib/bundle.js @@ -0,0 +1,456 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +var fs = require("fs"); +var path = require("path"); +var vm = require("vm"); +/** + * Bundle `entryPoints` given config `config`. + */ +function bundle(entryPoints, config, callback) { + var entryPointsMap = {}; + entryPoints.forEach(function (module) { + entryPointsMap[module.name] = module; + }); + var allMentionedModulesMap = {}; + entryPoints.forEach(function (module) { + allMentionedModulesMap[module.name] = true; + (module.include || []).forEach(function (includedModule) { + allMentionedModulesMap[includedModule] = true; + }); + (module.exclude || []).forEach(function (excludedModule) { + allMentionedModulesMap[excludedModule] = true; + }); + }); + var code = require('fs').readFileSync(path.join(__dirname, '../../src/vs/loader.js')); + var r = vm.runInThisContext('(function(require, module, exports) { ' + code + '\n});'); + var loaderModule = { exports: {} }; + r.call({}, require, loaderModule, loaderModule.exports); + var loader = loaderModule.exports; + config.isBuild = true; + config.paths = config.paths || {}; + config.paths['vs/nls'] = 'out-build/vs/nls.build'; + config.paths['vs/css'] = 'out-build/vs/css.build'; + loader.config(config); + loader(['require'], function (localRequire) { + var resolvePath = function (path) { + var r = localRequire.toUrl(path); + if (!/\.js/.test(r)) { + return r + '.js'; + } + return r; + }; + for (var moduleId in entryPointsMap) { + var entryPoint = entryPointsMap[moduleId]; + if (entryPoint.append) { + entryPoint.append = entryPoint.append.map(resolvePath); + } + if (entryPoint.prepend) { + entryPoint.prepend = entryPoint.prepend.map(resolvePath); + } + } + }); + loader(Object.keys(allMentionedModulesMap), function () { + var modules = loader.getBuildInfo(); + var partialResult = emitEntryPoints(modules, entryPointsMap); + var cssInlinedResources = loader('vs/css').getInlinedResources(); + callback(null, { + files: partialResult.files, + cssInlinedResources: cssInlinedResources, + bundleData: partialResult.bundleData + }); + }, function (err) { return callback(err, null); }); +} +exports.bundle = bundle; +function emitEntryPoints(modules, entryPoints) { + var modulesMap = {}; + modules.forEach(function (m) { + modulesMap[m.id] = m; + }); + var modulesGraph = {}; + modules.forEach(function (m) { + modulesGraph[m.id] = m.dependencies; + }); + var sortedModules = topologicalSort(modulesGraph); + var result = []; + var usedPlugins = {}; + var bundleData = { + graph: modulesGraph, + bundles: {} + }; + Object.keys(entryPoints).forEach(function (moduleToBundle) { + var info = entryPoints[moduleToBundle]; + var rootNodes = [moduleToBundle].concat(info.include || []); + var allDependencies = visit(rootNodes, modulesGraph); + var excludes = ['require', 'exports', 'module'].concat(info.exclude || []); + excludes.forEach(function (excludeRoot) { + var allExcludes = visit([excludeRoot], modulesGraph); + Object.keys(allExcludes).forEach(function (exclude) { + delete allDependencies[exclude]; + }); + }); + var includedModules = sortedModules.filter(function (module) { + return allDependencies[module]; + }); + bundleData.bundles[moduleToBundle] = includedModules; + var res = emitEntryPoint(modulesMap, modulesGraph, moduleToBundle, includedModules, info.prepend, info.append, info.dest); + result = result.concat(res.files); + for (var pluginName in res.usedPlugins) { + usedPlugins[pluginName] = usedPlugins[pluginName] || res.usedPlugins[pluginName]; + } + }); + Object.keys(usedPlugins).forEach(function (pluginName) { + var plugin = usedPlugins[pluginName]; + if (typeof plugin.finishBuild === 'function') { + var write = function (filename, contents) { + result.push({ + dest: filename, + sources: [{ + path: null, + contents: contents + }] + }); + }; + plugin.finishBuild(write); + } + }); + return { + // TODO@TS 2.1.2 + files: extractStrings(removeDuplicateTSBoilerplate(result)), + bundleData: bundleData + }; +} +function extractStrings(destFiles) { + var parseDefineCall = function (moduleMatch, depsMatch) { + var module = moduleMatch.replace(/^"|"$/g, ''); + var deps = depsMatch.split(','); + deps = deps.map(function (dep) { + dep = dep.trim(); + dep = dep.replace(/^"|"$/g, ''); + dep = dep.replace(/^'|'$/g, ''); + var prefix = null; + var _path = null; + var pieces = dep.split('!'); + if (pieces.length > 1) { + prefix = pieces[0] + '!'; + _path = pieces[1]; + } + else { + prefix = ''; + _path = pieces[0]; + } + if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) { + var res = path.join(path.dirname(module), _path).replace(/\\/g, '/'); + return prefix + res; + } + return prefix + _path; + }); + return { + module: module, + deps: deps + }; + }; + destFiles.forEach(function (destFile, index) { + if (!/\.js$/.test(destFile.dest)) { + return; + } + if (/\.nls\.js$/.test(destFile.dest)) { + return; + } + // Do one pass to record the usage counts for each module id + var useCounts = {}; + destFile.sources.forEach(function (source) { + var matches = source.contents.match(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/); + if (!matches) { + return; + } + var defineCall = parseDefineCall(matches[1], matches[2]); + useCounts[defineCall.module] = (useCounts[defineCall.module] || 0) + 1; + defineCall.deps.forEach(function (dep) { + useCounts[dep] = (useCounts[dep] || 0) + 1; + }); + }); + var sortedByUseModules = Object.keys(useCounts); + sortedByUseModules.sort(function (a, b) { + return useCounts[b] - useCounts[a]; + }); + var replacementMap = {}; + sortedByUseModules.forEach(function (module, index) { + replacementMap[module] = index; + }); + destFile.sources.forEach(function (source) { + source.contents = source.contents.replace(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/, function (_, moduleMatch, depsMatch) { + var defineCall = parseDefineCall(moduleMatch, depsMatch); + return "define(__m[" + replacementMap[defineCall.module] + "/*" + defineCall.module + "*/], __M([" + defineCall.deps.map(function (dep) { return replacementMap[dep] + '/*' + dep + '*/'; }).join(',') + "])"; + }); + }); + destFile.sources.unshift({ + path: null, + contents: [ + '(function() {', + "var __m = " + JSON.stringify(sortedByUseModules) + ";", + "var __M = function(deps) {", + " var result = [];", + " for (var i = 0, len = deps.length; i < len; i++) {", + " result[i] = __m[deps[i]];", + " }", + " return result;", + "};" + ].join('\n') + }); + destFile.sources.push({ + path: null, + contents: '}).call(this);' + }); + }); + return destFiles; +} +function removeDuplicateTSBoilerplate(destFiles) { + // Taken from typescript compiler => emitFiles + var BOILERPLATE = [ + { start: /^var __extends/, end: /^}\)\(\);$/ }, + { start: /^var __assign/, end: /^};$/ }, + { start: /^var __decorate/, end: /^};$/ }, + { start: /^var __metadata/, end: /^};$/ }, + { start: /^var __param/, end: /^};$/ }, + { start: /^var __awaiter/, end: /^};$/ }, + ]; + destFiles.forEach(function (destFile) { + var SEEN_BOILERPLATE = []; + destFile.sources.forEach(function (source) { + var lines = source.contents.split(/\r\n|\n|\r/); + var newLines = []; + var IS_REMOVING_BOILERPLATE = false, END_BOILERPLATE; + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + if (IS_REMOVING_BOILERPLATE) { + newLines.push(''); + if (END_BOILERPLATE.test(line)) { + IS_REMOVING_BOILERPLATE = false; + } + } + else { + for (var j = 0; j < BOILERPLATE.length; j++) { + var boilerplate = BOILERPLATE[j]; + if (boilerplate.start.test(line)) { + if (SEEN_BOILERPLATE[j]) { + IS_REMOVING_BOILERPLATE = true; + END_BOILERPLATE = boilerplate.end; + } + else { + SEEN_BOILERPLATE[j] = true; + } + } + } + if (IS_REMOVING_BOILERPLATE) { + newLines.push(''); + } + else { + newLines.push(line); + } + } + } + source.contents = newLines.join('\n'); + }); + }); + return destFiles; +} +function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, append, dest) { + if (!dest) { + dest = entryPoint + '.js'; + } + var mainResult = { + sources: [], + dest: dest + }, results = [mainResult]; + var usedPlugins = {}; + var getLoaderPlugin = function (pluginName) { + if (!usedPlugins[pluginName]) { + usedPlugins[pluginName] = modulesMap[pluginName].exports; + } + return usedPlugins[pluginName]; + }; + includedModules.forEach(function (c) { + var bangIndex = c.indexOf('!'); + if (bangIndex >= 0) { + var pluginName = c.substr(0, bangIndex); + var plugin = getLoaderPlugin(pluginName); + mainResult.sources.push(emitPlugin(entryPoint, plugin, pluginName, c.substr(bangIndex + 1))); + return; + } + var module = modulesMap[c]; + if (module.path === 'empty:') { + return; + } + var contents = readFileAndRemoveBOM(module.path); + if (module.shim) { + mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); + } + else { + mainResult.sources.push(emitNamedModule(c, deps[c], module.defineLocation, module.path, contents)); + } + }); + Object.keys(usedPlugins).forEach(function (pluginName) { + var plugin = usedPlugins[pluginName]; + if (typeof plugin.writeFile === 'function') { + var req = (function () { + throw new Error('no-no!'); + }); + req.toUrl = function (something) { return something; }; + var write = function (filename, contents) { + results.push({ + dest: filename, + sources: [{ + path: null, + contents: contents + }] + }); + }; + plugin.writeFile(pluginName, entryPoint, req, write, {}); + } + }); + var toIFile = function (path) { + var contents = readFileAndRemoveBOM(path); + return { + path: path, + contents: contents + }; + }; + var toPrepend = (prepend || []).map(toIFile); + var toAppend = (append || []).map(toIFile); + mainResult.sources = toPrepend.concat(mainResult.sources).concat(toAppend); + return { + files: results, + usedPlugins: usedPlugins + }; +} +function readFileAndRemoveBOM(path) { + var BOM_CHAR_CODE = 65279; + var contents = fs.readFileSync(path, 'utf8'); + // Remove BOM + if (contents.charCodeAt(0) === BOM_CHAR_CODE) { + contents = contents.substring(1); + } + return contents; +} +function emitPlugin(entryPoint, plugin, pluginName, moduleName) { + var result = ''; + if (typeof plugin.write === 'function') { + var write = (function (what) { + result += what; + }); + write.getEntryPoint = function () { + return entryPoint; + }; + write.asModule = function (moduleId, code) { + code = code.replace(/^define\(/, 'define("' + moduleId + '",'); + result += code; + }; + plugin.write(pluginName, moduleName, write); + } + return { + path: null, + contents: result + }; +} +function emitNamedModule(moduleId, myDeps, defineCallPosition, path, contents) { + // `defineCallPosition` is the position in code: |define() + var defineCallOffset = positionToOffset(contents, defineCallPosition.line, defineCallPosition.col); + // `parensOffset` is the position in code: define|() + var parensOffset = contents.indexOf('(', defineCallOffset); + var insertStr = '"' + moduleId + '", '; + return { + path: path, + contents: contents.substr(0, parensOffset + 1) + insertStr + contents.substr(parensOffset + 1) + }; +} +function emitShimmedModule(moduleId, myDeps, factory, path, contents) { + var strDeps = (myDeps.length > 0 ? '"' + myDeps.join('", "') + '"' : ''); + var strDefine = 'define("' + moduleId + '", [' + strDeps + '], ' + factory + ');'; + return { + path: path, + contents: contents + '\n;\n' + strDefine + }; +} +/** + * Convert a position (line:col) to (offset) in string `str` + */ +function positionToOffset(str, desiredLine, desiredCol) { + if (desiredLine === 1) { + return desiredCol - 1; + } + var line = 1, lastNewLineOffset = -1; + do { + if (desiredLine === line) { + return lastNewLineOffset + 1 + desiredCol - 1; + } + lastNewLineOffset = str.indexOf('\n', lastNewLineOffset + 1); + line++; + } while (lastNewLineOffset >= 0); + return -1; +} +/** + * Return a set of reachable nodes in `graph` starting from `rootNodes` + */ +function visit(rootNodes, graph) { + var result = {}, queue = rootNodes; + rootNodes.forEach(function (node) { + result[node] = true; + }); + while (queue.length > 0) { + var el = queue.shift(); + var myEdges = graph[el] || []; + myEdges.forEach(function (toNode) { + if (!result[toNode]) { + result[toNode] = true; + queue.push(toNode); + } + }); + } + return result; +} +/** + * Perform a topological sort on `graph` + */ +function topologicalSort(graph) { + var allNodes = {}, outgoingEdgeCount = {}, inverseEdges = {}; + Object.keys(graph).forEach(function (fromNode) { + allNodes[fromNode] = true; + outgoingEdgeCount[fromNode] = graph[fromNode].length; + graph[fromNode].forEach(function (toNode) { + allNodes[toNode] = true; + outgoingEdgeCount[toNode] = outgoingEdgeCount[toNode] || 0; + inverseEdges[toNode] = inverseEdges[toNode] || []; + inverseEdges[toNode].push(fromNode); + }); + }); + // https://en.wikipedia.org/wiki/Topological_sorting + var S = [], L = []; + Object.keys(allNodes).forEach(function (node) { + if (outgoingEdgeCount[node] === 0) { + delete outgoingEdgeCount[node]; + S.push(node); + } + }); + while (S.length > 0) { + // Ensure the exact same order all the time with the same inputs + S.sort(); + var n = S.shift(); + L.push(n); + var myInverseEdges = inverseEdges[n] || []; + myInverseEdges.forEach(function (m) { + outgoingEdgeCount[m]--; + if (outgoingEdgeCount[m] === 0) { + delete outgoingEdgeCount[m]; + S.push(m); + } + }); + } + if (Object.keys(outgoingEdgeCount).length > 0) { + throw new Error('Cannot do topological sort on cyclic graph, remaining nodes: ' + Object.keys(outgoingEdgeCount)); + } + return L; +} diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts new file mode 100644 index 0000000000..96c961e229 --- /dev/null +++ b/build/lib/bundle.ts @@ -0,0 +1,643 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import fs = require('fs'); +import path = require('path'); +import vm = require('vm'); + +interface IPosition { + line: number; + col: number; +} + +interface IBuildModuleInfo { + id: string; + path: string; + defineLocation: IPosition; + dependencies: string[]; + shim: string; + exports: any; +} + +interface IBuildModuleInfoMap { + [moduleId: string]: IBuildModuleInfo; +} + +interface ILoaderPlugin { + write(pluginName: string, moduleName: string, write: ILoaderPluginWriteFunc): void; + writeFile(pluginName: string, entryPoint: string, req: ILoaderPluginReqFunc, write: (filename: string, contents: string) => void, config: any): void; + finishBuild(write: (filename: string, contents: string) => void): void; +} + +interface ILoaderPluginWriteFunc { + (something: string): void; + getEntryPoint(): string; + asModule(moduleId: string, code: string): void; +} + +interface ILoaderPluginReqFunc { + (something: string): void; + toUrl(something: string): string; +} + +export interface IEntryPoint { + name: string; + include: string[]; + exclude: string[]; + prepend: string[]; + append: string[]; + dest: string; +} + +interface IEntryPointMap { + [moduleId: string]: IEntryPoint; +} + +export interface IGraph { + [node: string]: string[]; +} + +interface INodeSet { + [node: string]: boolean; +} + +export interface IFile { + path: string; + contents: string; +} + +export interface IConcatFile { + dest: string; + sources: IFile[]; +} + +export interface IBundleData { + graph: IGraph; + bundles: { [moduleId: string]: string[]; }; +} + +export interface IBundleResult { + files: IConcatFile[]; + cssInlinedResources: string[]; + bundleData: IBundleData; +} + +interface IPartialBundleResult { + files: IConcatFile[]; + bundleData: IBundleData; +} + +export interface ILoaderConfig { + isBuild?: boolean; + paths?: { [path: string]: any; }; +} + +/** + * Bundle `entryPoints` given config `config`. + */ +export function bundle(entryPoints: IEntryPoint[], config: ILoaderConfig, callback: (err: any, result: IBundleResult) => void): void { + let entryPointsMap: IEntryPointMap = {}; + entryPoints.forEach((module: IEntryPoint) => { + entryPointsMap[module.name] = module; + }); + + let allMentionedModulesMap: { [modules: string]: boolean; } = {}; + entryPoints.forEach((module: IEntryPoint) => { + allMentionedModulesMap[module.name] = true; + (module.include || []).forEach(function (includedModule) { + allMentionedModulesMap[includedModule] = true; + }); + (module.exclude || []).forEach(function (excludedModule) { + allMentionedModulesMap[excludedModule] = true; + }); + }); + + + var code = require('fs').readFileSync(path.join(__dirname, '../../src/vs/loader.js')); + var r: Function = vm.runInThisContext('(function(require, module, exports) { ' + code + '\n});'); + var loaderModule = { exports: {} }; + r.call({}, require, loaderModule, loaderModule.exports); + + var loader: any = loaderModule.exports; + config.isBuild = true; + config.paths = config.paths || {}; + config.paths['vs/nls'] = 'out-build/vs/nls.build'; + config.paths['vs/css'] = 'out-build/vs/css.build'; + loader.config(config); + + loader(['require'], (localRequire) => { + let resolvePath = (path: string) => { + let r = localRequire.toUrl(path); + if (!/\.js/.test(r)) { + return r + '.js'; + } + return r; + }; + for (let moduleId in entryPointsMap) { + let entryPoint = entryPointsMap[moduleId]; + if (entryPoint.append) { + entryPoint.append = entryPoint.append.map(resolvePath); + } + if (entryPoint.prepend) { + entryPoint.prepend = entryPoint.prepend.map(resolvePath); + } + } + }); + + loader(Object.keys(allMentionedModulesMap), () => { + let modules = loader.getBuildInfo(); + let partialResult = emitEntryPoints(modules, entryPointsMap); + let cssInlinedResources = loader('vs/css').getInlinedResources(); + callback(null, { + files: partialResult.files, + cssInlinedResources: cssInlinedResources, + bundleData: partialResult.bundleData + }); + }, (err) => callback(err, null)); +} + +function emitEntryPoints(modules: IBuildModuleInfo[], entryPoints: IEntryPointMap): IPartialBundleResult { + let modulesMap: IBuildModuleInfoMap = {}; + modules.forEach((m: IBuildModuleInfo) => { + modulesMap[m.id] = m; + }); + + let modulesGraph: IGraph = {}; + modules.forEach((m: IBuildModuleInfo) => { + modulesGraph[m.id] = m.dependencies; + }); + + let sortedModules = topologicalSort(modulesGraph); + + let result: IConcatFile[] = []; + let usedPlugins: IPluginMap = {}; + let bundleData: IBundleData = { + graph: modulesGraph, + bundles: {} + }; + + Object.keys(entryPoints).forEach((moduleToBundle: string) => { + let info = entryPoints[moduleToBundle]; + let rootNodes = [moduleToBundle].concat(info.include || []); + let allDependencies = visit(rootNodes, modulesGraph); + let excludes: string[] = ['require', 'exports', 'module'].concat(info.exclude || []); + + excludes.forEach((excludeRoot: string) => { + let allExcludes = visit([excludeRoot], modulesGraph); + Object.keys(allExcludes).forEach((exclude: string) => { + delete allDependencies[exclude]; + }); + }); + + let includedModules = sortedModules.filter((module: string) => { + return allDependencies[module]; + }); + + bundleData.bundles[moduleToBundle] = includedModules; + + let res = emitEntryPoint( + modulesMap, + modulesGraph, + moduleToBundle, + includedModules, + info.prepend, + info.append, + info.dest + ); + + result = result.concat(res.files); + for (let pluginName in res.usedPlugins) { + usedPlugins[pluginName] = usedPlugins[pluginName] || res.usedPlugins[pluginName]; + } + }); + + Object.keys(usedPlugins).forEach((pluginName: string) => { + let plugin = usedPlugins[pluginName]; + if (typeof plugin.finishBuild === 'function') { + let write = (filename: string, contents: string) => { + result.push({ + dest: filename, + sources: [{ + path: null, + contents: contents + }] + }); + }; + plugin.finishBuild(write); + } + }); + + return { + // TODO@TS 2.1.2 + files: extractStrings(removeDuplicateTSBoilerplate(result)), + bundleData: bundleData + }; +} + +function extractStrings(destFiles: IConcatFile[]): IConcatFile[] { + let parseDefineCall = (moduleMatch: string, depsMatch: string) => { + let module = moduleMatch.replace(/^"|"$/g, ''); + let deps = depsMatch.split(','); + deps = deps.map((dep) => { + dep = dep.trim(); + dep = dep.replace(/^"|"$/g, ''); + dep = dep.replace(/^'|'$/g, ''); + let prefix: string = null; + let _path: string = null; + let pieces = dep.split('!'); + if (pieces.length > 1) { + prefix = pieces[0] + '!'; + _path = pieces[1]; + } else { + prefix = ''; + _path = pieces[0]; + } + + if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) { + let res = path.join(path.dirname(module), _path).replace(/\\/g, '/'); + return prefix + res; + } + return prefix + _path; + }); + return { + module: module, + deps: deps + }; + }; + + destFiles.forEach((destFile, index) => { + if (!/\.js$/.test(destFile.dest)) { + return; + } + if (/\.nls\.js$/.test(destFile.dest)) { + return; + } + + // Do one pass to record the usage counts for each module id + let useCounts: { [moduleId: string]: number; } = {}; + destFile.sources.forEach((source) => { + let matches = source.contents.match(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/); + if (!matches) { + return; + } + + let defineCall = parseDefineCall(matches[1], matches[2]); + useCounts[defineCall.module] = (useCounts[defineCall.module] || 0) + 1; + defineCall.deps.forEach((dep) => { + useCounts[dep] = (useCounts[dep] || 0) + 1; + }); + }); + + let sortedByUseModules = Object.keys(useCounts); + sortedByUseModules.sort((a, b) => { + return useCounts[b] - useCounts[a]; + }); + + let replacementMap: { [moduleId: string]: number; } = {}; + sortedByUseModules.forEach((module, index) => { + replacementMap[module] = index; + }); + + destFile.sources.forEach((source) => { + source.contents = source.contents.replace(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/, (_, moduleMatch, depsMatch) => { + let defineCall = parseDefineCall(moduleMatch, depsMatch); + return `define(__m[${replacementMap[defineCall.module]}/*${defineCall.module}*/], __M([${defineCall.deps.map(dep => replacementMap[dep] + '/*' + dep + '*/').join(',')}])`; + }); + }); + + destFile.sources.unshift({ + path: null, + contents: [ + '(function() {', + `var __m = ${JSON.stringify(sortedByUseModules)};`, + `var __M = function(deps) {`, + ` var result = [];`, + ` for (var i = 0, len = deps.length; i < len; i++) {`, + ` result[i] = __m[deps[i]];`, + ` }`, + ` return result;`, + `};` + ].join('\n') + }); + + destFile.sources.push({ + path: null, + contents: '}).call(this);' + }); + }); + return destFiles; +} + +function removeDuplicateTSBoilerplate(destFiles: IConcatFile[]): IConcatFile[] { + // Taken from typescript compiler => emitFiles + let BOILERPLATE = [ + { start: /^var __extends/, end: /^}\)\(\);$/ }, + { start: /^var __assign/, end: /^};$/ }, + { start: /^var __decorate/, end: /^};$/ }, + { start: /^var __metadata/, end: /^};$/ }, + { start: /^var __param/, end: /^};$/ }, + { start: /^var __awaiter/, end: /^};$/ }, + ]; + + destFiles.forEach((destFile) => { + let SEEN_BOILERPLATE = []; + destFile.sources.forEach((source) => { + let lines = source.contents.split(/\r\n|\n|\r/); + let newLines: string[] = []; + let IS_REMOVING_BOILERPLATE = false, END_BOILERPLATE: RegExp; + + for (let i = 0; i < lines.length; i++) { + let line = lines[i]; + if (IS_REMOVING_BOILERPLATE) { + newLines.push(''); + if (END_BOILERPLATE.test(line)) { + IS_REMOVING_BOILERPLATE = false; + } + } else { + for (let j = 0; j < BOILERPLATE.length; j++) { + let boilerplate = BOILERPLATE[j]; + if (boilerplate.start.test(line)) { + if (SEEN_BOILERPLATE[j]) { + IS_REMOVING_BOILERPLATE = true; + END_BOILERPLATE = boilerplate.end; + } else { + SEEN_BOILERPLATE[j] = true; + } + } + } + if (IS_REMOVING_BOILERPLATE) { + newLines.push(''); + } else { + newLines.push(line); + } + } + } + source.contents = newLines.join('\n'); + }); + }); + + return destFiles; +} + +interface IPluginMap { + [moduleId: string]: ILoaderPlugin; +} + +interface IEmitEntryPointResult { + files: IConcatFile[]; + usedPlugins: IPluginMap; +} + +function emitEntryPoint( + modulesMap: IBuildModuleInfoMap, + deps: IGraph, + entryPoint: string, + includedModules: string[], + prepend: string[], + append: string[], + dest: string +): IEmitEntryPointResult { + if (!dest) { + dest = entryPoint + '.js'; + } + let mainResult: IConcatFile = { + sources: [], + dest: dest + }, + results: IConcatFile[] = [mainResult]; + + let usedPlugins: IPluginMap = {}; + let getLoaderPlugin = (pluginName: string): ILoaderPlugin => { + if (!usedPlugins[pluginName]) { + usedPlugins[pluginName] = modulesMap[pluginName].exports; + } + return usedPlugins[pluginName]; + }; + + includedModules.forEach((c: string) => { + let bangIndex = c.indexOf('!'); + + if (bangIndex >= 0) { + let pluginName = c.substr(0, bangIndex); + let plugin = getLoaderPlugin(pluginName); + mainResult.sources.push(emitPlugin(entryPoint, plugin, pluginName, c.substr(bangIndex + 1))); + return; + } + + let module = modulesMap[c]; + + if (module.path === 'empty:') { + return; + } + + let contents = readFileAndRemoveBOM(module.path); + + if (module.shim) { + mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); + } else { + mainResult.sources.push(emitNamedModule(c, deps[c], module.defineLocation, module.path, contents)); + } + }); + + Object.keys(usedPlugins).forEach((pluginName: string) => { + let plugin = usedPlugins[pluginName]; + if (typeof plugin.writeFile === 'function') { + let req: ILoaderPluginReqFunc = (() => { + throw new Error('no-no!'); + }); + req.toUrl = something => something; + + let write = (filename: string, contents: string) => { + results.push({ + dest: filename, + sources: [{ + path: null, + contents: contents + }] + }); + }; + plugin.writeFile(pluginName, entryPoint, req, write, {}); + } + }); + + let toIFile = (path): IFile => { + let contents = readFileAndRemoveBOM(path); + return { + path: path, + contents: contents + }; + }; + + let toPrepend = (prepend || []).map(toIFile); + let toAppend = (append || []).map(toIFile); + + mainResult.sources = toPrepend.concat(mainResult.sources).concat(toAppend); + + return { + files: results, + usedPlugins: usedPlugins + }; +} + +function readFileAndRemoveBOM(path: string): string { + var BOM_CHAR_CODE = 65279; + var contents = fs.readFileSync(path, 'utf8'); + // Remove BOM + if (contents.charCodeAt(0) === BOM_CHAR_CODE) { + contents = contents.substring(1); + } + return contents; +} + +function emitPlugin(entryPoint: string, plugin: ILoaderPlugin, pluginName: string, moduleName: string): IFile { + let result = ''; + if (typeof plugin.write === 'function') { + let write: ILoaderPluginWriteFunc = ((what) => { + result += what; + }); + write.getEntryPoint = () => { + return entryPoint; + }; + write.asModule = (moduleId: string, code: string) => { + code = code.replace(/^define\(/, 'define("' + moduleId + '",'); + result += code; + }; + plugin.write(pluginName, moduleName, write); + } + return { + path: null, + contents: result + }; +} + +function emitNamedModule(moduleId: string, myDeps: string[], defineCallPosition: IPosition, path: string, contents: string): IFile { + + // `defineCallPosition` is the position in code: |define() + let defineCallOffset = positionToOffset(contents, defineCallPosition.line, defineCallPosition.col); + + // `parensOffset` is the position in code: define|() + let parensOffset = contents.indexOf('(', defineCallOffset); + + let insertStr = '"' + moduleId + '", '; + + return { + path: path, + contents: contents.substr(0, parensOffset + 1) + insertStr + contents.substr(parensOffset + 1) + }; +} + +function emitShimmedModule(moduleId: string, myDeps: string[], factory: string, path: string, contents: string): IFile { + let strDeps = (myDeps.length > 0 ? '"' + myDeps.join('", "') + '"' : ''); + let strDefine = 'define("' + moduleId + '", [' + strDeps + '], ' + factory + ');'; + return { + path: path, + contents: contents + '\n;\n' + strDefine + }; +} + +/** + * Convert a position (line:col) to (offset) in string `str` + */ +function positionToOffset(str: string, desiredLine: number, desiredCol: number): number { + if (desiredLine === 1) { + return desiredCol - 1; + } + + let line = 1, + lastNewLineOffset = -1; + + do { + if (desiredLine === line) { + return lastNewLineOffset + 1 + desiredCol - 1; + } + lastNewLineOffset = str.indexOf('\n', lastNewLineOffset + 1); + line++; + } while (lastNewLineOffset >= 0); + + return -1; +} + + +/** + * Return a set of reachable nodes in `graph` starting from `rootNodes` + */ +function visit(rootNodes: string[], graph: IGraph): INodeSet { + let result: INodeSet = {}, + queue = rootNodes; + + rootNodes.forEach((node) => { + result[node] = true; + }); + + while (queue.length > 0) { + let el = queue.shift(); + let myEdges = graph[el] || []; + myEdges.forEach((toNode) => { + if (!result[toNode]) { + result[toNode] = true; + queue.push(toNode); + } + }); + } + + return result; +} + +/** + * Perform a topological sort on `graph` + */ +function topologicalSort(graph: IGraph): string[] { + + let allNodes: INodeSet = {}, + outgoingEdgeCount: { [node: string]: number; } = {}, + inverseEdges: IGraph = {}; + + Object.keys(graph).forEach((fromNode: string) => { + allNodes[fromNode] = true; + outgoingEdgeCount[fromNode] = graph[fromNode].length; + + graph[fromNode].forEach((toNode) => { + allNodes[toNode] = true; + outgoingEdgeCount[toNode] = outgoingEdgeCount[toNode] || 0; + + inverseEdges[toNode] = inverseEdges[toNode] || []; + inverseEdges[toNode].push(fromNode); + }); + }); + + // https://en.wikipedia.org/wiki/Topological_sorting + let S: string[] = [], + L: string[] = []; + + Object.keys(allNodes).forEach((node: string) => { + if (outgoingEdgeCount[node] === 0) { + delete outgoingEdgeCount[node]; + S.push(node); + } + }); + + while (S.length > 0) { + // Ensure the exact same order all the time with the same inputs + S.sort(); + + let n: string = S.shift(); + L.push(n); + + let myInverseEdges = inverseEdges[n] || []; + myInverseEdges.forEach((m: string) => { + outgoingEdgeCount[m]--; + if (outgoingEdgeCount[m] === 0) { + delete outgoingEdgeCount[m]; + S.push(m); + } + }); + } + + if (Object.keys(outgoingEdgeCount).length > 0) { + throw new Error('Cannot do topological sort on cyclic graph, remaining nodes: ' + Object.keys(outgoingEdgeCount)); + } + + return L; +} diff --git a/build/lib/compilation.js b/build/lib/compilation.js new file mode 100644 index 0000000000..dfb4de1540 --- /dev/null +++ b/build/lib/compilation.js @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var gulp = require("gulp"); +var tsb = require("gulp-tsb"); +var es = require("event-stream"); +var watch = require('./watch'); +var nls = require("./nls"); +var util = require("./util"); +var reporter_1 = require("./reporter"); +var path = require("path"); +var bom = require("gulp-bom"); +var sourcemaps = require("gulp-sourcemaps"); +var _ = require("underscore"); +var monacodts = require("../monaco/api"); +var fs = require("fs"); +var reporter = reporter_1.createReporter(); +var rootDir = path.join(__dirname, '../../src'); +var options = require('../../src/tsconfig.json').compilerOptions; +options.verbose = false; +options.sourceMap = true; +options.rootDir = rootDir; +options.sourceRoot = util.toFileUri(rootDir); +function createCompile(build, emitError) { + var opts = _.clone(options); + opts.inlineSources = !!build; + opts.noFilesystemLookup = true; + var ts = tsb.create(opts, null, null, function (err) { return reporter(err.toString()); }); + return function (token) { + var utf8Filter = util.filter(function (data) { return /(\/|\\)test(\/|\\).*utf8/.test(data.path); }); + var tsFilter = util.filter(function (data) { return /\.ts$/.test(data.path); }); + var noDeclarationsFilter = util.filter(function (data) { return !(/\.d\.ts$/.test(data.path)); }); + var input = es.through(); + var output = input + .pipe(utf8Filter) + .pipe(bom()) + .pipe(utf8Filter.restore) + .pipe(tsFilter) + .pipe(util.loadSourcemaps()) + .pipe(ts(token)) + .pipe(noDeclarationsFilter) + .pipe(build ? nls() : es.through()) + .pipe(noDeclarationsFilter.restore) + .pipe(sourcemaps.write('.', { + addComment: false, + includeContent: !!build, + sourceRoot: options.sourceRoot + })) + .pipe(tsFilter.restore) + .pipe(reporter.end(emitError)); + return es.duplex(input, output); + }; +} +function compileTask(out, build) { + return function () { + var compile = createCompile(build, true); + var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts')); + return src + .pipe(compile()) + .pipe(gulp.dest(out)) + .pipe(monacodtsTask(out, false)); + }; +} +exports.compileTask = compileTask; +function watchTask(out, build) { + return function () { + var compile = createCompile(build); + var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts')); + var watchSrc = watch('src/**', { base: 'src' }); + return watchSrc + .pipe(util.incremental(compile, src, true)) + .pipe(gulp.dest(out)) + .pipe(monacodtsTask(out, true)); + }; +} +exports.watchTask = watchTask; +function reloadTypeScriptNodeModule() { + var util = require('gulp-util'); + function log(message) { + var rest = []; + for (var _i = 1; _i < arguments.length; _i++) { + rest[_i - 1] = arguments[_i]; + } + util.log.apply(util, [util.colors.cyan('[memory watch dog]'), message].concat(rest)); + } + function heapUsed() { + return (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2) + ' MB'; + } + return es.through(function (data) { + this.emit('data', data); + }, function () { + log('memory usage after compilation finished: ' + heapUsed()); + // It appears we are running into some variant of + // https://bugs.chromium.org/p/v8/issues/detail?id=2073 + // + // Even though all references are dropped, some + // optimized methods in the TS compiler end up holding references + // to the entire TypeScript language host (>600MB) + // + // The idea is to force v8 to drop references to these + // optimized methods, by "reloading" the typescript node module + log('Reloading typescript node module...'); + var resolvedName = require.resolve('typescript'); + var originalModule = require.cache[resolvedName]; + delete require.cache[resolvedName]; + var newExports = require('typescript'); + require.cache[resolvedName] = originalModule; + for (var prop in newExports) { + if (newExports.hasOwnProperty(prop)) { + originalModule.exports[prop] = newExports[prop]; + } + } + log('typescript node module reloaded.'); + this.emit('end'); + }); +} +function monacodtsTask(out, isWatch) { + var neededFiles = {}; + monacodts.getFilesToWatch(out).forEach(function (filePath) { + filePath = path.normalize(filePath); + neededFiles[filePath] = true; + }); + var inputFiles = {}; + for (var filePath in neededFiles) { + if (/\bsrc(\/|\\)vs\b/.test(filePath)) { + // This file is needed from source => simply read it now + inputFiles[filePath] = fs.readFileSync(filePath).toString(); + } + } + var setInputFile = function (filePath, contents) { + if (inputFiles[filePath] === contents) { + // no change + return; + } + inputFiles[filePath] = contents; + var neededInputFilesCount = Object.keys(neededFiles).length; + var availableInputFilesCount = Object.keys(inputFiles).length; + if (neededInputFilesCount === availableInputFilesCount) { + run(); + } + }; + var run = function () { + var result = monacodts.run(out, inputFiles); + if (!result.isTheSame) { + if (isWatch) { + fs.writeFileSync(result.filePath, result.content); + } + else { + resultStream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); + } + } + }; + var resultStream; + if (isWatch) { + watch('build/monaco/*').pipe(es.through(function () { + run(); + })); + } + resultStream = es.through(function (data) { + var filePath = path.normalize(data.path); + if (neededFiles[filePath]) { + setInputFile(filePath, data.contents.toString()); + } + this.emit('data', data); + }); + return resultStream; +} diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts new file mode 100644 index 0000000000..4080677623 --- /dev/null +++ b/build/lib/compilation.ts @@ -0,0 +1,207 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as gulp from 'gulp'; +import * as tsb from 'gulp-tsb'; +import * as es from 'event-stream'; +const watch = require('./watch'); +import * as nls from './nls'; +import * as util from './util'; +import { createReporter } from './reporter'; +import * as path from 'path'; +import * as bom from 'gulp-bom'; +import * as sourcemaps from 'gulp-sourcemaps'; +import * as _ from 'underscore'; +import * as monacodts from '../monaco/api'; +import * as fs from 'fs'; + +const reporter = createReporter(); + +const rootDir = path.join(__dirname, '../../src'); +const options = require('../../src/tsconfig.json').compilerOptions; +options.verbose = false; +options.sourceMap = true; +options.rootDir = rootDir; +options.sourceRoot = util.toFileUri(rootDir); + +function createCompile(build: boolean, emitError?: boolean): (token?: util.ICancellationToken) => NodeJS.ReadWriteStream { + const opts = _.clone(options); + opts.inlineSources = !!build; + opts.noFilesystemLookup = true; + + const ts = tsb.create(opts, null, null, err => reporter(err.toString())); + + return function (token?: util.ICancellationToken) { + + const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path)); + const tsFilter = util.filter(data => /\.ts$/.test(data.path)); + const noDeclarationsFilter = util.filter(data => !(/\.d\.ts$/.test(data.path))); + + const input = es.through(); + const output = input + .pipe(utf8Filter) + .pipe(bom()) + .pipe(utf8Filter.restore) + .pipe(tsFilter) + .pipe(util.loadSourcemaps()) + .pipe(ts(token)) + // .pipe(build ? reloadTypeScriptNodeModule() : es.through()) + .pipe(noDeclarationsFilter) + .pipe(build ? nls() : es.through()) + .pipe(noDeclarationsFilter.restore) + .pipe(sourcemaps.write('.', { + addComment: false, + includeContent: !!build, + sourceRoot: options.sourceRoot + })) + .pipe(tsFilter.restore) + .pipe(reporter.end(emitError)); + + return es.duplex(input, output); + }; +} + +export function compileTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { + + return function () { + const compile = createCompile(build, true); + + const src = es.merge( + gulp.src('src/**', { base: 'src' }), + gulp.src('node_modules/typescript/lib/lib.d.ts'), + ); + + return src + .pipe(compile()) + .pipe(gulp.dest(out)) + .pipe(monacodtsTask(out, false)); + }; +} + +export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { + + return function () { + const compile = createCompile(build); + + const src = es.merge( + gulp.src('src/**', { base: 'src' }), + gulp.src('node_modules/typescript/lib/lib.d.ts'), + ); + const watchSrc = watch('src/**', { base: 'src' }); + + return watchSrc + .pipe(util.incremental(compile, src, true)) + .pipe(gulp.dest(out)) + .pipe(monacodtsTask(out, true)); + }; +} + +function reloadTypeScriptNodeModule(): NodeJS.ReadWriteStream { + var util = require('gulp-util'); + function log(message: any, ...rest: any[]): void { + util.log(util.colors.cyan('[memory watch dog]'), message, ...rest); + } + + function heapUsed(): string { + return (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2) + ' MB'; + } + + return es.through(function (data) { + this.emit('data', data); + }, function () { + + log('memory usage after compilation finished: ' + heapUsed()); + + // It appears we are running into some variant of + // https://bugs.chromium.org/p/v8/issues/detail?id=2073 + // + // Even though all references are dropped, some + // optimized methods in the TS compiler end up holding references + // to the entire TypeScript language host (>600MB) + // + // The idea is to force v8 to drop references to these + // optimized methods, by "reloading" the typescript node module + + log('Reloading typescript node module...'); + + var resolvedName = require.resolve('typescript'); + + var originalModule = require.cache[resolvedName]; + delete require.cache[resolvedName]; + var newExports = require('typescript'); + require.cache[resolvedName] = originalModule; + + for (var prop in newExports) { + if (newExports.hasOwnProperty(prop)) { + originalModule.exports[prop] = newExports[prop]; + } + } + + log('typescript node module reloaded.'); + + this.emit('end'); + }); +} + +function monacodtsTask(out: string, isWatch: boolean): NodeJS.ReadWriteStream { + + const neededFiles: { [file: string]: boolean; } = {}; + monacodts.getFilesToWatch(out).forEach(function (filePath) { + filePath = path.normalize(filePath); + neededFiles[filePath] = true; + }); + + const inputFiles: { [file: string]: string; } = {}; + for (let filePath in neededFiles) { + if (/\bsrc(\/|\\)vs\b/.test(filePath)) { + // This file is needed from source => simply read it now + inputFiles[filePath] = fs.readFileSync(filePath).toString(); + } + } + + const setInputFile = (filePath: string, contents: string) => { + if (inputFiles[filePath] === contents) { + // no change + return; + } + inputFiles[filePath] = contents; + const neededInputFilesCount = Object.keys(neededFiles).length; + const availableInputFilesCount = Object.keys(inputFiles).length; + if (neededInputFilesCount === availableInputFilesCount) { + run(); + } + }; + + const run = () => { + const result = monacodts.run(out, inputFiles); + if (!result.isTheSame) { + if (isWatch) { + fs.writeFileSync(result.filePath, result.content); + } else { + resultStream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); + } + } + }; + + let resultStream: NodeJS.ReadWriteStream; + + if (isWatch) { + watch('build/monaco/*').pipe(es.through(function () { + run(); + })); + } + + resultStream = es.through(function (data) { + const filePath = path.normalize(data.path); + if (neededFiles[filePath]) { + setInputFile(filePath, data.contents.toString()); + } + this.emit('data', data); + }); + + return resultStream; +} diff --git a/build/lib/extensions.js b/build/lib/extensions.js new file mode 100644 index 0000000000..4ac26e3252 --- /dev/null +++ b/build/lib/extensions.js @@ -0,0 +1,118 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +var es = require("event-stream"); +var assign = require("object-assign"); +var remote = require("gulp-remote-src"); +var flatmap = require('gulp-flatmap'); +var vzip = require('gulp-vinyl-zip'); +var filter = require('gulp-filter'); +var rename = require('gulp-rename'); +var util = require('gulp-util'); +var buffer = require('gulp-buffer'); +var json = require('gulp-json-editor'); +var fs = require("fs"); +var path = require("path"); +var vsce = require("vsce"); +var File = require("vinyl"); +function fromLocal(extensionPath) { + var result = es.through(); + vsce.listFiles({ cwd: extensionPath }) + .then(function (fileNames) { + var files = fileNames + .map(function (fileName) { return path.join(extensionPath, fileName); }) + .map(function (filePath) { return new File({ + path: filePath, + stat: fs.statSync(filePath), + base: extensionPath, + contents: fs.createReadStream(filePath) + }); }); + es.readArray(files).pipe(result); + }) + .catch(function (err) { return result.emit('error', err); }); + return result; +} +exports.fromLocal = fromLocal; +function error(err) { + var result = es.through(); + setTimeout(function () { return result.emit('error', err); }); + return result; +} +var baseHeaders = { + 'X-Market-Client-Id': 'VSCode Build', + 'User-Agent': 'VSCode Build', +}; +function fromMarketplace(extensionName, version) { + var filterType = 7; + var value = extensionName; + var criterium = { filterType: filterType, value: value }; + var criteria = [criterium]; + var pageNumber = 1; + var pageSize = 1; + var sortBy = 0; + var sortOrder = 0; + var flags = 0x1 | 0x2 | 0x80; + var assetTypes = ['Microsoft.VisualStudio.Services.VSIXPackage']; + var filters = [{ criteria: criteria, pageNumber: pageNumber, pageSize: pageSize, sortBy: sortBy, sortOrder: sortOrder }]; + var body = JSON.stringify({ filters: filters, assetTypes: assetTypes, flags: flags }); + var headers = assign({}, baseHeaders, { + 'Content-Type': 'application/json', + 'Accept': 'application/json;api-version=3.0-preview.1', + 'Content-Length': body.length + }); + var options = { + base: 'https://marketplace.visualstudio.com/_apis/public/gallery', + requestOptions: { + method: 'POST', + gzip: true, + headers: headers, + body: body + } + }; + return remote('/extensionquery', options) + .pipe(flatmap(function (stream, f) { + var rawResult = f.contents.toString('utf8'); + var result = JSON.parse(rawResult); + var extension = result.results[0].extensions[0]; + if (!extension) { + return error("No such extension: " + extension); + } + var metadata = { + id: extension.extensionId, + publisherId: extension.publisher, + publisherDisplayName: extension.publisher.displayName + }; + var extensionVersion = extension.versions.filter(function (v) { return v.version === version; })[0]; + if (!extensionVersion) { + return error("No such extension version: " + extensionName + " @ " + version); + } + var asset = extensionVersion.files.filter(function (f) { return f.assetType === 'Microsoft.VisualStudio.Services.VSIXPackage'; })[0]; + if (!asset) { + return error("No VSIX found for extension version: " + extensionName + " @ " + version); + } + util.log('Downloading extension:', util.colors.yellow(extensionName + "@" + version), '...'); + var options = { + base: asset.source, + requestOptions: { + gzip: true, + headers: baseHeaders + } + }; + return remote('', options) + .pipe(flatmap(function (stream) { + var packageJsonFilter = filter('package.json', { restore: true }); + return stream + .pipe(vzip.src()) + .pipe(filter('extension/**')) + .pipe(rename(function (p) { return p.dirname = p.dirname.replace(/^extension\/?/, ''); })) + .pipe(packageJsonFilter) + .pipe(buffer()) + .pipe(json({ __metadata: metadata })) + .pipe(packageJsonFilter.restore); + })); + })); +} +exports.fromMarketplace = fromMarketplace; diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts new file mode 100644 index 0000000000..e96002dad1 --- /dev/null +++ b/build/lib/extensions.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as es from 'event-stream'; +import { Stream } from 'stream'; +import assign = require('object-assign'); +import remote = require('gulp-remote-src'); +const flatmap = require('gulp-flatmap'); +const vzip = require('gulp-vinyl-zip'); +const filter = require('gulp-filter'); +const rename = require('gulp-rename'); +const util = require('gulp-util'); +const buffer = require('gulp-buffer'); +const json = require('gulp-json-editor'); +import * as fs from 'fs'; +import * as path from 'path'; +import * as vsce from 'vsce'; +import * as File from 'vinyl'; + +export function fromLocal(extensionPath: string): Stream { + const result = es.through(); + + vsce.listFiles({ cwd: extensionPath }) + .then(fileNames => { + const files = fileNames + .map(fileName => path.join(extensionPath, fileName)) + .map(filePath => new File({ + path: filePath, + stat: fs.statSync(filePath), + base: extensionPath, + contents: fs.createReadStream(filePath) as any + })); + + es.readArray(files).pipe(result); + }) + .catch(err => result.emit('error', err)); + + return result; +} + +function error(err: any): Stream { + const result = es.through(); + setTimeout(() => result.emit('error', err)); + return result; +} + +const baseHeaders = { + 'X-Market-Client-Id': 'VSCode Build', + 'User-Agent': 'VSCode Build', +}; + +export function fromMarketplace(extensionName: string, version: string): Stream { + const filterType = 7; + const value = extensionName; + const criterium = { filterType, value }; + const criteria = [criterium]; + const pageNumber = 1; + const pageSize = 1; + const sortBy = 0; + const sortOrder = 0; + const flags = 0x1 | 0x2 | 0x80; + const assetTypes = ['Microsoft.VisualStudio.Services.VSIXPackage']; + const filters = [{ criteria, pageNumber, pageSize, sortBy, sortOrder }]; + const body = JSON.stringify({ filters, assetTypes, flags }); + const headers: any = assign({}, baseHeaders, { + 'Content-Type': 'application/json', + 'Accept': 'application/json;api-version=3.0-preview.1', + 'Content-Length': body.length + }); + + const options = { + base: 'https://marketplace.visualstudio.com/_apis/public/gallery', + requestOptions: { + method: 'POST', + gzip: true, + headers, + body: body + } + }; + + return remote('/extensionquery', options) + .pipe(flatmap((stream, f) => { + const rawResult = f.contents.toString('utf8'); + const result = JSON.parse(rawResult); + const extension = result.results[0].extensions[0]; + if (!extension) { + return error(`No such extension: ${extension}`); + } + + const metadata = { + id: extension.extensionId, + publisherId: extension.publisher, + publisherDisplayName: extension.publisher.displayName + }; + + const extensionVersion = extension.versions.filter(v => v.version === version)[0]; + if (!extensionVersion) { + return error(`No such extension version: ${extensionName} @ ${version}`); + } + + const asset = extensionVersion.files.filter(f => f.assetType === 'Microsoft.VisualStudio.Services.VSIXPackage')[0]; + if (!asset) { + return error(`No VSIX found for extension version: ${extensionName} @ ${version}`); + } + + util.log('Downloading extension:', util.colors.yellow(`${extensionName}@${version}`), '...'); + + const options = { + base: asset.source, + requestOptions: { + gzip: true, + headers: baseHeaders + } + }; + + return remote('', options) + .pipe(flatmap(stream => { + const packageJsonFilter = filter('package.json', { restore: true }); + + return stream + .pipe(vzip.src()) + .pipe(filter('extension/**')) + .pipe(rename(p => p.dirname = p.dirname.replace(/^extension\/?/, ''))) + .pipe(packageJsonFilter) + .pipe(buffer()) + .pipe(json({ __metadata: metadata })) + .pipe(packageJsonFilter.restore); + })); + })); +} diff --git a/build/lib/git.js b/build/lib/git.js new file mode 100644 index 0000000000..8eeb432719 --- /dev/null +++ b/build/lib/git.js @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var path = require("path"); +var fs = require("fs"); +/** + * Returns the sha1 commit version of a repository or undefined in case of failure. + */ +function getVersion(repo) { + var git = path.join(repo, '.git'); + var headPath = path.join(git, 'HEAD'); + var head; + try { + head = fs.readFileSync(headPath, 'utf8').trim(); + } + catch (e) { + return void 0; + } + if (/^[0-9a-f]{40}$/i.test(head)) { + return head; + } + var refMatch = /^ref: (.*)$/.exec(head); + if (!refMatch) { + return void 0; + } + var ref = refMatch[1]; + var refPath = path.join(git, ref); + try { + return fs.readFileSync(refPath, 'utf8').trim(); + } + catch (e) { + // noop + } + var packedRefsPath = path.join(git, 'packed-refs'); + var refsRaw; + try { + refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim(); + } + catch (e) { + return void 0; + } + var refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; + var refsMatch; + var refs = {}; + while (refsMatch = refsRegex.exec(refsRaw)) { + refs[refsMatch[2]] = refsMatch[1]; + } + return refs[ref]; +} +exports.getVersion = getVersion; diff --git a/build/lib/git.ts b/build/lib/git.ts new file mode 100644 index 0000000000..db15109889 --- /dev/null +++ b/build/lib/git.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as path from 'path'; +import * as fs from 'fs'; + +/** + * Returns the sha1 commit version of a repository or undefined in case of failure. + */ +export function getVersion(repo: string): string { + const git = path.join(repo, '.git'); + const headPath = path.join(git, 'HEAD'); + let head: string; + + try { + head = fs.readFileSync(headPath, 'utf8').trim(); + } catch (e) { + return void 0; + } + + if (/^[0-9a-f]{40}$/i.test(head)) { + return head; + } + + const refMatch = /^ref: (.*)$/.exec(head); + + if (!refMatch) { + return void 0; + } + + const ref = refMatch[1]; + const refPath = path.join(git, ref); + + try { + return fs.readFileSync(refPath, 'utf8').trim(); + } catch (e) { + // noop + } + + const packedRefsPath = path.join(git, 'packed-refs'); + let refsRaw: string; + + try { + refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim(); + } catch (e) { + return void 0; + } + + const refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; + let refsMatch: RegExpExecArray; + let refs: { [ref: string]: string } = {}; + + while (refsMatch = refsRegex.exec(refsRaw)) { + refs[refsMatch[2]] = refsMatch[1]; + } + + return refs[ref]; +} diff --git a/build/lib/i18n.js b/build/lib/i18n.js new file mode 100644 index 0000000000..6cd6af1134 --- /dev/null +++ b/build/lib/i18n.js @@ -0,0 +1,1001 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +var path = require("path"); +var fs = require("fs"); +var event_stream_1 = require("event-stream"); +var File = require("vinyl"); +var Is = require("is"); +var xml2js = require("xml2js"); +var glob = require("glob"); +var https = require("https"); +var util = require('gulp-util'); +var iconv = require('iconv-lite'); +function log(message) { + var rest = []; + for (var _i = 1; _i < arguments.length; _i++) { + rest[_i - 1] = arguments[_i]; + } + util.log.apply(util, [util.colors.green('[i18n]'), message].concat(rest)); +} +var LocalizeInfo; +(function (LocalizeInfo) { + function is(value) { + var candidate = value; + return Is.defined(candidate) && Is.string(candidate.key) && (Is.undef(candidate.comment) || (Is.array(candidate.comment) && candidate.comment.every(function (element) { return Is.string(element); }))); + } + LocalizeInfo.is = is; +})(LocalizeInfo || (LocalizeInfo = {})); +var BundledFormat; +(function (BundledFormat) { + function is(value) { + if (Is.undef(value)) { + return false; + } + var candidate = value; + var length = Object.keys(value).length; + return length === 3 && Is.defined(candidate.keys) && Is.defined(candidate.messages) && Is.defined(candidate.bundles); + } + BundledFormat.is = is; +})(BundledFormat || (BundledFormat = {})); +var PackageJsonFormat; +(function (PackageJsonFormat) { + function is(value) { + if (Is.undef(value) || !Is.object(value)) { + return false; + } + return Object.keys(value).every(function (key) { + var element = value[key]; + return Is.string(element) || (Is.object(element) && Is.defined(element.message) && Is.defined(element.comment)); + }); + } + PackageJsonFormat.is = is; +})(PackageJsonFormat || (PackageJsonFormat = {})); +var ModuleJsonFormat; +(function (ModuleJsonFormat) { + function is(value) { + var candidate = value; + return Is.defined(candidate) + && Is.array(candidate.messages) && candidate.messages.every(function (message) { return Is.string(message); }) + && Is.array(candidate.keys) && candidate.keys.every(function (key) { return Is.string(key) || LocalizeInfo.is(key); }); + } + ModuleJsonFormat.is = is; +})(ModuleJsonFormat || (ModuleJsonFormat = {})); +var Line = (function () { + function Line(indent) { + if (indent === void 0) { indent = 0; } + this.indent = indent; + this.buffer = []; + if (indent > 0) { + this.buffer.push(new Array(indent + 1).join(' ')); + } + } + Line.prototype.append = function (value) { + this.buffer.push(value); + return this; + }; + Line.prototype.toString = function () { + return this.buffer.join(''); + }; + return Line; +}()); +exports.Line = Line; +var TextModel = (function () { + function TextModel(contents) { + this._lines = contents.split(/\r\n|\r|\n/); + } + Object.defineProperty(TextModel.prototype, "lines", { + get: function () { + return this._lines; + }, + enumerable: true, + configurable: true + }); + return TextModel; +}()); +var XLF = (function () { + function XLF(project) { + this.project = project; + this.buffer = []; + this.files = Object.create(null); + } + XLF.prototype.toString = function () { + this.appendHeader(); + for (var file in this.files) { + this.appendNewLine("", 2); + for (var _i = 0, _a = this.files[file]; _i < _a.length; _i++) { + var item = _a[_i]; + this.addStringItem(item); + } + this.appendNewLine('', 2); + } + this.appendFooter(); + return this.buffer.join('\r\n'); + }; + XLF.prototype.addFile = function (original, keys, messages) { + this.files[original] = []; + var existingKeys = []; + for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) { + var key = keys_1[_i]; + // Ignore duplicate keys because Transifex does not populate those with translated values. + if (existingKeys.indexOf(key) !== -1) { + continue; + } + existingKeys.push(key); + var message = encodeEntities(messages[keys.indexOf(key)]); + var comment = undefined; + // Check if the message contains description (if so, it becomes an object type in JSON) + if (Is.string(key)) { + this.files[original].push({ id: key, message: message, comment: comment }); + } + else { + if (key['comment'] && key['comment'].length > 0) { + comment = key['comment'].map(function (comment) { return encodeEntities(comment); }).join('\r\n'); + } + this.files[original].push({ id: key['key'], message: message, comment: comment }); + } + } + }; + XLF.prototype.addStringItem = function (item) { + if (!item.id || !item.message) { + throw new Error('No item ID or value specified.'); + } + this.appendNewLine("", 4); + this.appendNewLine("" + item.message + "", 6); + if (item.comment) { + this.appendNewLine("" + item.comment + "", 6); + } + this.appendNewLine('', 4); + }; + XLF.prototype.appendHeader = function () { + this.appendNewLine('', 0); + this.appendNewLine('', 0); + }; + XLF.prototype.appendFooter = function () { + this.appendNewLine('', 0); + }; + XLF.prototype.appendNewLine = function (content, indent) { + var line = new Line(indent); + line.append(content); + this.buffer.push(line.toString()); + }; + XLF.parse = function (xlfString) { + return new Promise(function (resolve, reject) { + var parser = new xml2js.Parser(); + var files = []; + parser.parseString(xlfString, function (err, result) { + if (err) { + reject("Failed to parse XLIFF string. " + err); + } + var fileNodes = result['xliff']['file']; + if (!fileNodes) { + reject('XLIFF file does not contain "xliff" or "file" node(s) required for parsing.'); + } + fileNodes.forEach(function (file) { + var originalFilePath = file.$.original; + if (!originalFilePath) { + reject('XLIFF file node does not contain original attribute to determine the original location of the resource file.'); + } + var language = file.$['target-language'].toLowerCase(); + if (!language) { + reject('XLIFF file node does not contain target-language attribute to determine translated language.'); + } + var messages = {}; + var transUnits = file.body[0]['trans-unit']; + transUnits.forEach(function (unit) { + var key = unit.$.id; + if (!unit.target) { + return; // No translation available + } + var val = unit.target.toString(); + if (key && val) { + messages[key] = decodeEntities(val); + } + else { + reject('XLIFF file does not contain full localization data. ID or target translation for one of the trans-unit nodes is not present.'); + } + }); + files.push({ messages: messages, originalFilePath: originalFilePath, language: language }); + }); + resolve(files); + }); + }); + }; + return XLF; +}()); +exports.XLF = XLF; +var iso639_3_to_2 = { + 'chs': 'zh-cn', + 'cht': 'zh-tw', + 'csy': 'cs-cz', + 'deu': 'de', + 'enu': 'en', + 'esn': 'es', + 'fra': 'fr', + 'hun': 'hu', + 'ita': 'it', + 'jpn': 'ja', + 'kor': 'ko', + 'nld': 'nl', + 'plk': 'pl', + 'ptb': 'pt-br', + 'ptg': 'pt', + 'rus': 'ru', + 'sve': 'sv-se', + 'trk': 'tr' +}; +/** + * Used to map Transifex to VS Code language code representation. + */ +var iso639_2_to_3 = { + 'zh-hans': 'chs', + 'zh-hant': 'cht', + 'cs-cz': 'csy', + 'de': 'deu', + 'en': 'enu', + 'es': 'esn', + 'fr': 'fra', + 'hu': 'hun', + 'it': 'ita', + 'ja': 'jpn', + 'ko': 'kor', + 'nl': 'nld', + 'pl': 'plk', + 'pt-br': 'ptb', + 'pt': 'ptg', + 'ru': 'rus', + 'sv-se': 'sve', + 'tr': 'trk' +}; +function sortLanguages(directoryNames) { + return directoryNames.map(function (dirName) { + var lower = dirName.toLowerCase(); + return { + name: lower, + iso639_2: iso639_3_to_2[lower] + }; + }).sort(function (a, b) { + if (!a.iso639_2 && !b.iso639_2) { + return 0; + } + if (!a.iso639_2) { + return -1; + } + if (!b.iso639_2) { + return 1; + } + return a.iso639_2 < b.iso639_2 ? -1 : (a.iso639_2 > b.iso639_2 ? 1 : 0); + }); +} +function stripComments(content) { + /** + * First capturing group matches double quoted string + * Second matches single quotes string + * Third matches block comments + * Fourth matches line comments + */ + var regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; + var result = content.replace(regexp, function (match, m1, m2, m3, m4) { + // Only one of m1, m2, m3, m4 matches + if (m3) { + // A block comment. Replace with nothing + return ''; + } + else if (m4) { + // A line comment. If it ends in \r?\n then keep it. + var length_1 = m4.length; + if (length_1 > 2 && m4[length_1 - 1] === '\n') { + return m4[length_1 - 2] === '\r' ? '\r\n' : '\n'; + } + else { + return ''; + } + } + else { + // We match a string + return match; + } + }); + return result; +} +function escapeCharacters(value) { + var result = []; + for (var i = 0; i < value.length; i++) { + var ch = value.charAt(i); + switch (ch) { + case '\'': + result.push('\\\''); + break; + case '"': + result.push('\\"'); + break; + case '\\': + result.push('\\\\'); + break; + case '\n': + result.push('\\n'); + break; + case '\r': + result.push('\\r'); + break; + case '\t': + result.push('\\t'); + break; + case '\b': + result.push('\\b'); + break; + case '\f': + result.push('\\f'); + break; + default: + result.push(ch); + } + } + return result.join(''); +} +function processCoreBundleFormat(fileHeader, languages, json, emitter) { + var keysSection = json.keys; + var messageSection = json.messages; + var bundleSection = json.bundles; + var statistics = Object.create(null); + var total = 0; + var defaultMessages = Object.create(null); + var modules = Object.keys(keysSection); + modules.forEach(function (module) { + var keys = keysSection[module]; + var messages = messageSection[module]; + if (!messages || keys.length !== messages.length) { + emitter.emit('error', "Message for module " + module + " corrupted. Mismatch in number of keys and messages."); + return; + } + var messageMap = Object.create(null); + defaultMessages[module] = messageMap; + keys.map(function (key, i) { + total++; + if (Is.string(key)) { + messageMap[key] = messages[i]; + } + else { + messageMap[key.key] = messages[i]; + } + }); + }); + var languageDirectory = path.join(__dirname, '..', '..', 'i18n'); + var languageDirs; + if (languages) { + languageDirs = sortLanguages(languages); + } + else { + languageDirs = sortLanguages(fs.readdirSync(languageDirectory).filter(function (item) { return fs.statSync(path.join(languageDirectory, item)).isDirectory(); })); + } + languageDirs.forEach(function (language) { + if (!language.iso639_2) { + return; + } + if (process.env['VSCODE_BUILD_VERBOSE']) { + log("Generating nls bundles for: " + language.iso639_2); + } + statistics[language.iso639_2] = 0; + var localizedModules = Object.create(null); + var cwd = path.join(languageDirectory, language.name, 'src'); + modules.forEach(function (module) { + var order = keysSection[module]; + var i18nFile = path.join(cwd, module) + '.i18n.json'; + var messages = null; + if (fs.existsSync(i18nFile)) { + var content = stripComments(fs.readFileSync(i18nFile, 'utf8')); + messages = JSON.parse(content); + } + else { + if (process.env['VSCODE_BUILD_VERBOSE']) { + log("No localized messages found for module " + module + ". Using default messages."); + } + messages = defaultMessages[module]; + statistics[language.iso639_2] = statistics[language.iso639_2] + Object.keys(messages).length; + } + var localizedMessages = []; + order.forEach(function (keyInfo) { + var key = null; + if (Is.string(keyInfo)) { + key = keyInfo; + } + else { + key = keyInfo.key; + } + var message = messages[key]; + if (!message) { + if (process.env['VSCODE_BUILD_VERBOSE']) { + log("No localized message found for key " + key + " in module " + module + ". Using default message."); + } + message = defaultMessages[module][key]; + statistics[language.iso639_2] = statistics[language.iso639_2] + 1; + } + localizedMessages.push(message); + }); + localizedModules[module] = localizedMessages; + }); + Object.keys(bundleSection).forEach(function (bundle) { + var modules = bundleSection[bundle]; + var contents = [ + fileHeader, + "define(\"" + bundle + ".nls." + language.iso639_2 + "\", {" + ]; + modules.forEach(function (module, index) { + contents.push("\t\"" + module + "\": ["); + var messages = localizedModules[module]; + if (!messages) { + emitter.emit('error', "Didn't find messages for module " + module + "."); + return; + } + messages.forEach(function (message, index) { + contents.push("\t\t\"" + escapeCharacters(message) + (index < messages.length ? '",' : '"')); + }); + contents.push(index < modules.length - 1 ? '\t],' : '\t]'); + }); + contents.push('});'); + emitter.emit('data', new File({ path: bundle + '.nls.' + language.iso639_2 + '.js', contents: new Buffer(contents.join('\n'), 'utf-8') })); + }); + }); + Object.keys(statistics).forEach(function (key) { + var value = statistics[key]; + log(key + " has " + value + " untranslated strings."); + }); + languageDirs.forEach(function (dir) { + var language = dir.name; + var iso639_2 = iso639_3_to_2[language]; + if (!iso639_2) { + log("\tCouldn't find iso639 2 mapping for language " + language + ". Using default language instead."); + } + else { + var stats = statistics[iso639_2]; + if (Is.undef(stats)) { + log("\tNo translations found for language " + language + ". Using default language instead."); + } + } + }); +} +function processNlsFiles(opts) { + return event_stream_1.through(function (file) { + var fileName = path.basename(file.path); + if (fileName === 'nls.metadata.json') { + var json = null; + if (file.isBuffer()) { + json = JSON.parse(file.contents.toString('utf8')); + } + else { + this.emit('error', "Failed to read component file: " + file.relative); + } + if (BundledFormat.is(json)) { + processCoreBundleFormat(opts.fileHeader, opts.languages, json, this); + } + } + this.emit('data', file); + }); +} +exports.processNlsFiles = processNlsFiles; +function prepareXlfFiles(projectName, extensionName) { + return event_stream_1.through(function (file) { + if (!file.isBuffer()) { + throw new Error("Failed to read component file: " + file.relative); + } + var extension = path.extname(file.path); + if (extension === '.json') { + var json = JSON.parse(file.contents.toString('utf8')); + if (BundledFormat.is(json)) { + importBundleJson(file, json, this); + } + else if (PackageJsonFormat.is(json) || ModuleJsonFormat.is(json)) { + importModuleOrPackageJson(file, json, projectName, this, extensionName); + } + else { + throw new Error("JSON format cannot be deduced for " + file.relative + "."); + } + } + else if (extension === '.isl') { + importIsl(file, this); + } + }); +} +exports.prepareXlfFiles = prepareXlfFiles; +var editorProject = 'vscode-editor', workbenchProject = 'vscode-workbench', extensionsProject = 'vscode-extensions', setupProject = 'vscode-setup'; +function getResource(sourceFile) { + var resource; + if (/^vs\/platform/.test(sourceFile)) { + return { name: 'vs/platform', project: editorProject }; + } + else if (/^vs\/editor\/contrib/.test(sourceFile)) { + return { name: 'vs/editor/contrib', project: editorProject }; + } + else if (/^vs\/editor/.test(sourceFile)) { + return { name: 'vs/editor', project: editorProject }; + } + else if (/^vs\/base/.test(sourceFile)) { + return { name: 'vs/base', project: editorProject }; + } + else if (/^vs\/code/.test(sourceFile)) { + return { name: 'vs/code', project: workbenchProject }; + } + else if (/^vs\/workbench\/parts/.test(sourceFile)) { + resource = sourceFile.split('/', 4).join('/'); + return { name: resource, project: workbenchProject }; + } + else if (/^vs\/workbench\/services/.test(sourceFile)) { + resource = sourceFile.split('/', 4).join('/'); + return { name: resource, project: workbenchProject }; + } + else if (/^vs\/workbench/.test(sourceFile)) { + return { name: 'vs/workbench', project: workbenchProject }; + } + throw new Error("Could not identify the XLF bundle for " + sourceFile); +} +exports.getResource = getResource; +function importBundleJson(file, json, stream) { + var bundleXlfs = Object.create(null); + for (var source in json.keys) { + var projectResource = getResource(source); + var resource = projectResource.name; + var project = projectResource.project; + var keys = json.keys[source]; + var messages = json.messages[source]; + if (keys.length !== messages.length) { + throw new Error("There is a mismatch between keys and messages in " + file.relative); + } + var xlf = bundleXlfs[resource] ? bundleXlfs[resource] : bundleXlfs[resource] = new XLF(project); + xlf.addFile('src/' + source, keys, messages); + } + for (var resource in bundleXlfs) { + var newFilePath = bundleXlfs[resource].project + "/" + resource.replace(/\//g, '_') + ".xlf"; + var xlfFile = new File({ path: newFilePath, contents: new Buffer(bundleXlfs[resource].toString(), 'utf-8') }); + stream.emit('data', xlfFile); + } +} +// Keeps existing XLF instances and a state of how many files were already processed for faster file emission +var extensions = Object.create(null); +function importModuleOrPackageJson(file, json, projectName, stream, extensionName) { + if (ModuleJsonFormat.is(json) && json['keys'].length !== json['messages'].length) { + throw new Error("There is a mismatch between keys and messages in " + file.relative); + } + // Prepare the source path for attribute in XLF & extract messages from JSON + var formattedSourcePath = file.relative.replace(/\\/g, '/'); + var messages = Object.keys(json).map(function (key) { return json[key].toString(); }); + // Stores the amount of localization files to be transformed to XLF before the emission + var localizationFilesCount, originalFilePath; + // If preparing XLF for external extension, then use different glob pattern and source path + if (extensionName) { + localizationFilesCount = glob.sync('**/*.nls.json').length; + originalFilePath = "" + formattedSourcePath.substr(0, formattedSourcePath.length - '.nls.json'.length); + } + else { + // Used for vscode/extensions folder + extensionName = formattedSourcePath.split('/')[0]; + localizationFilesCount = glob.sync("./extensions/" + extensionName + "/**/*.nls.json").length; + originalFilePath = "extensions/" + formattedSourcePath.substr(0, formattedSourcePath.length - '.nls.json'.length); + } + var extension = extensions[extensionName] ? + extensions[extensionName] : extensions[extensionName] = { xlf: new XLF(projectName), processed: 0 }; + // .nls.json can come with empty array of keys and messages, check for it + if (ModuleJsonFormat.is(json) && json.keys.length !== 0) { + extension.xlf.addFile(originalFilePath, json.keys, json.messages); + } + else if (PackageJsonFormat.is(json) && Object.keys(json).length !== 0) { + extension.xlf.addFile(originalFilePath, Object.keys(json), messages); + } + // Check if XLF is populated with file nodes to emit it + if (++extensions[extensionName].processed === localizationFilesCount) { + var newFilePath = path.join(projectName, extensionName + '.xlf'); + var xlfFile = new File({ path: newFilePath, contents: new Buffer(extension.xlf.toString(), 'utf-8') }); + stream.emit('data', xlfFile); + } +} +function importIsl(file, stream) { + var projectName, resourceFile; + if (path.basename(file.path) === 'Default.isl') { + projectName = setupProject; + resourceFile = 'setup_default.xlf'; + } + else { + projectName = workbenchProject; + resourceFile = 'setup_messages.xlf'; + } + var xlf = new XLF(projectName), keys = [], messages = []; + var model = new TextModel(file.contents.toString()); + var inMessageSection = false; + model.lines.forEach(function (line) { + if (line.length === 0) { + return; + } + var firstChar = line.charAt(0); + switch (firstChar) { + case ';': + // Comment line; + return; + case '[': + inMessageSection = '[Messages]' === line || '[CustomMessages]' === line; + return; + } + if (!inMessageSection) { + return; + } + var sections = line.split('='); + if (sections.length !== 2) { + throw new Error("Badly formatted message found: " + line); + } + else { + var key = sections[0]; + var value = sections[1]; + if (key.length > 0 && value.length > 0) { + keys.push(key); + messages.push(value); + } + } + }); + var originalPath = file.path.substring(file.cwd.length + 1, file.path.split('.')[0].length).replace(/\\/g, '/'); + xlf.addFile(originalPath, keys, messages); + // Emit only upon all ISL files combined into single XLF instance + var newFilePath = path.join(projectName, resourceFile); + var xlfFile = new File({ path: newFilePath, contents: new Buffer(xlf.toString(), 'utf-8') }); + stream.emit('data', xlfFile); +} +function pushXlfFiles(apiHostname, username, password) { + var tryGetPromises = []; + var updateCreatePromises = []; + return event_stream_1.through(function (file) { + var project = path.dirname(file.relative); + var fileName = path.basename(file.path); + var slug = fileName.substr(0, fileName.length - '.xlf'.length); + var credentials = username + ":" + password; + // Check if resource already exists, if not, then create it. + var promise = tryGetResource(project, slug, apiHostname, credentials); + tryGetPromises.push(promise); + promise.then(function (exists) { + if (exists) { + promise = updateResource(project, slug, file, apiHostname, credentials); + } + else { + promise = createResource(project, slug, file, apiHostname, credentials); + } + updateCreatePromises.push(promise); + }); + }, function () { + var _this = this; + // End the pipe only after all the communication with Transifex API happened + Promise.all(tryGetPromises).then(function () { + Promise.all(updateCreatePromises).then(function () { + _this.emit('end'); + }).catch(function (reason) { throw new Error(reason); }); + }).catch(function (reason) { throw new Error(reason); }); + }); +} +exports.pushXlfFiles = pushXlfFiles; +function tryGetResource(project, slug, apiHostname, credentials) { + return new Promise(function (resolve, reject) { + var options = { + hostname: apiHostname, + path: "/api/2/project/" + project + "/resource/" + slug + "/?details", + auth: credentials, + method: 'GET' + }; + var request = https.request(options, function (response) { + if (response.statusCode === 404) { + resolve(false); + } + else if (response.statusCode === 200) { + resolve(true); + } + else { + reject("Failed to query resource " + project + "/" + slug + ". Response: " + response.statusCode + " " + response.statusMessage); + } + }); + request.on('error', function (err) { + reject("Failed to get " + project + "/" + slug + " on Transifex: " + err); + }); + request.end(); + }); +} +function createResource(project, slug, xlfFile, apiHostname, credentials) { + return new Promise(function (resolve, reject) { + var data = JSON.stringify({ + 'content': xlfFile.contents.toString(), + 'name': slug, + 'slug': slug, + 'i18n_type': 'XLIFF' + }); + var options = { + hostname: apiHostname, + path: "/api/2/project/" + project + "/resources", + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(data) + }, + auth: credentials, + method: 'POST' + }; + var request = https.request(options, function (res) { + if (res.statusCode === 201) { + log("Resource " + project + "/" + slug + " successfully created on Transifex."); + } + else { + reject("Something went wrong in the request creating " + slug + " in " + project + ". " + res.statusCode); + } + }); + request.on('error', function (err) { + reject("Failed to create " + project + "/" + slug + " on Transifex: " + err); + }); + request.write(data); + request.end(); + }); +} +/** + * The following link provides information about how Transifex handles updates of a resource file: + * https://dev.befoolish.co/tx-docs/public/projects/updating-content#what-happens-when-you-update-files + */ +function updateResource(project, slug, xlfFile, apiHostname, credentials) { + return new Promise(function (resolve, reject) { + var data = JSON.stringify({ content: xlfFile.contents.toString() }); + var options = { + hostname: apiHostname, + path: "/api/2/project/" + project + "/resource/" + slug + "/content", + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(data) + }, + auth: credentials, + method: 'PUT' + }; + var request = https.request(options, function (res) { + if (res.statusCode === 200) { + res.setEncoding('utf8'); + var responseBuffer_1 = ''; + res.on('data', function (chunk) { + responseBuffer_1 += chunk; + }); + res.on('end', function () { + var response = JSON.parse(responseBuffer_1); + log("Resource " + project + "/" + slug + " successfully updated on Transifex. Strings added: " + response.strings_added + ", updated: " + response.strings_added + ", deleted: " + response.strings_added); + resolve(); + }); + } + else { + reject("Something went wrong in the request updating " + slug + " in " + project + ". " + res.statusCode); + } + }); + request.on('error', function (err) { + reject("Failed to update " + project + "/" + slug + " on Transifex: " + err); + }); + request.write(data); + request.end(); + }); +} +function obtainProjectResources(projectName) { + var resources = []; + if (projectName === editorProject) { + var json = fs.readFileSync('./build/lib/i18n.resources.json', 'utf8'); + resources = JSON.parse(json).editor; + } + else if (projectName === workbenchProject) { + var json = fs.readFileSync('./build/lib/i18n.resources.json', 'utf8'); + resources = JSON.parse(json).workbench; + } + else if (projectName === extensionsProject) { + var extensionsToLocalize = glob.sync('./extensions/**/*.nls.json').map(function (extension) { return extension.split('/')[2]; }); + var resourcesToPull_1 = []; + extensionsToLocalize.forEach(function (extension) { + if (resourcesToPull_1.indexOf(extension) === -1) { + resourcesToPull_1.push(extension); + resources.push({ name: extension, project: projectName }); + } + }); + } + else if (projectName === setupProject) { + resources.push({ name: 'setup_default', project: setupProject }); + } + return resources; +} +function pullXlfFiles(projectName, apiHostname, username, password, languages, resources) { + if (!resources) { + resources = obtainProjectResources(projectName); + } + if (!resources) { + throw new Error('Transifex projects and resources must be defined to be able to pull translations from Transifex.'); + } + var credentials = username + ":" + password; + var expectedTranslationsCount = languages.length * resources.length; + var translationsRetrieved = 0, called = false; + return event_stream_1.readable(function (count, callback) { + // Mark end of stream when all resources were retrieved + if (translationsRetrieved === expectedTranslationsCount) { + return this.emit('end'); + } + if (!called) { + called = true; + var stream_1 = this; + // Retrieve XLF files from main projects + languages.map(function (language) { + resources.map(function (resource) { + retrieveResource(language, resource, apiHostname, credentials).then(function (file) { + stream_1.emit('data', file); + translationsRetrieved++; + }).catch(function (error) { throw new Error(error); }); + }); + }); + } + callback(); + }); +} +exports.pullXlfFiles = pullXlfFiles; +function retrieveResource(language, resource, apiHostname, credentials) { + return new Promise(function (resolve, reject) { + var slug = resource.name.replace(/\//g, '_'); + var project = resource.project; + var iso639 = language.toLowerCase(); + var options = { + hostname: apiHostname, + path: "/api/2/project/" + project + "/resource/" + slug + "/translation/" + iso639 + "?file&mode=onlyreviewed", + auth: credentials, + method: 'GET' + }; + var request = https.request(options, function (res) { + var xlfBuffer = []; + res.on('data', function (chunk) { return xlfBuffer.push(chunk); }); + res.on('end', function () { + if (res.statusCode === 200) { + resolve(new File({ contents: Buffer.concat(xlfBuffer), path: project + "/" + iso639_2_to_3[language] + "/" + slug + ".xlf" })); + } + reject(slug + " in " + project + " returned no data. Response code: " + res.statusCode + "."); + }); + }); + request.on('error', function (err) { + reject("Failed to query resource " + slug + " with the following error: " + err); + }); + request.end(); + }); +} +function prepareJsonFiles() { + var parsePromises = []; + return event_stream_1.through(function (xlf) { + var stream = this; + var parsePromise = XLF.parse(xlf.contents.toString()); + parsePromises.push(parsePromise); + parsePromise.then(function (resolvedFiles) { + resolvedFiles.forEach(function (file) { + var messages = file.messages, translatedFile; + // ISL file path always starts with 'build/' + if (/^build\//.test(file.originalFilePath)) { + var defaultLanguages = { 'zh-hans': true, 'zh-hant': true, 'ko': true }; + if (path.basename(file.originalFilePath) === 'Default' && !defaultLanguages[file.language]) { + return; + } + translatedFile = createIslFile('..', file.originalFilePath, messages, iso639_2_to_3[file.language]); + } + else { + translatedFile = createI18nFile(iso639_2_to_3[file.language], file.originalFilePath, messages); + } + stream.emit('data', translatedFile); + }); + }, function (rejectReason) { + throw new Error("XLF parsing error: " + rejectReason); + }); + }, function () { + var _this = this; + Promise.all(parsePromises) + .then(function () { _this.emit('end'); }) + .catch(function (reason) { throw new Error(reason); }); + }); +} +exports.prepareJsonFiles = prepareJsonFiles; +function createI18nFile(base, originalFilePath, messages) { + var content = [ + '/*---------------------------------------------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' * Licensed under the Source EULA. See License.txt in the project root for license information.', + ' *--------------------------------------------------------------------------------------------*/', + '// Do not edit this file. It is machine generated.' + ].join('\n') + '\n' + JSON.stringify(messages, null, '\t').replace(/\r\n/g, '\n'); + return new File({ + path: path.join(base, originalFilePath + '.i18n.json'), + contents: new Buffer(content, 'utf8') + }); +} +var languageNames = { + 'chs': 'Simplified Chinese', + 'cht': 'Traditional Chinese', + 'kor': 'Korean' +}; +var languageIds = { + 'chs': '$0804', + 'cht': '$0404', + 'kor': '$0412' +}; +var encodings = { + 'chs': 'CP936', + 'cht': 'CP950', + 'jpn': 'CP932', + 'kor': 'CP949', + 'deu': 'CP1252', + 'fra': 'CP1252', + 'esn': 'CP1252', + 'rus': 'CP1251', + 'ita': 'CP1252', + 'ptb': 'CP1252', + 'hun': 'CP1250', + 'trk': 'CP1254' +}; +function createIslFile(base, originalFilePath, messages, language) { + var content = []; + var originalContent; + if (path.basename(originalFilePath) === 'Default') { + originalContent = new TextModel(fs.readFileSync(originalFilePath + '.isl', 'utf8')); + } + else { + originalContent = new TextModel(fs.readFileSync(originalFilePath + '.en.isl', 'utf8')); + } + originalContent.lines.forEach(function (line) { + if (line.length > 0) { + var firstChar = line.charAt(0); + if (firstChar === '[' || firstChar === ';') { + if (line === '; *** Inno Setup version 5.5.3+ English messages ***') { + content.push("; *** Inno Setup version 5.5.3+ " + languageNames[language] + " messages ***"); + } + else { + content.push(line); + } + } + else { + var sections = line.split('='); + var key = sections[0]; + var translated = line; + if (key) { + if (key === 'LanguageName') { + translated = key + "=" + languageNames[language]; + } + else if (key === 'LanguageID') { + translated = key + "=" + languageIds[language]; + } + else if (key === 'LanguageCodePage') { + translated = key + "=" + encodings[language].substr(2); + } + else { + var translatedMessage = messages[key]; + if (translatedMessage) { + translated = key + "=" + translatedMessage; + } + } + } + content.push(translated); + } + } + }); + var tag = iso639_3_to_2[language]; + var basename = path.basename(originalFilePath); + var filePath = path.join(base, path.dirname(originalFilePath), basename) + "." + tag + ".isl"; + return new File({ + path: filePath, + contents: iconv.encode(new Buffer(content.join('\r\n'), 'utf8'), encodings[language]) + }); +} +function encodeEntities(value) { + var result = []; + for (var i = 0; i < value.length; i++) { + var ch = value[i]; + switch (ch) { + case '<': + result.push('<'); + break; + case '>': + result.push('>'); + break; + case '&': + result.push('&'); + break; + default: + result.push(ch); + } + } + return result.join(''); +} +function decodeEntities(value) { + return value.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); +} diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json new file mode 100644 index 0000000000..393438d2bf --- /dev/null +++ b/build/lib/i18n.resources.json @@ -0,0 +1,198 @@ +{ + "editor": [ + { + "name": "vs/platform", + "project": "vscode-editor" + }, + { + "name": "vs/editor/contrib", + "project": "vscode-editor" + }, + { + "name": "vs/editor", + "project": "vscode-editor" + }, + { + "name": "vs/base", + "project": "vscode-editor" + } + ], + "workbench": [ + { + "name": "vs/code", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/cli", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/codeEditor", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/debug", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/emmet", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/execution", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/explorers", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/extensions", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/feedback", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/files", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/html", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/markers", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/nps", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/output", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/performance", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/preferences", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/quickopen", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/relauncher", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/scm", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/search", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/snippets", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/surveys", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/tasks", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/terminal", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/themes", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/trust", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/update", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/views", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/watermark", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/parts/welcome", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/configuration", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/crashReporter", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/editor", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/extensions", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/files", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/keybinding", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/message", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/mode", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/progress", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/textfile", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/themes", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/textMate", + "project": "vscode-workbench" + }, + { + "name": "setup_messages", + "project": "vscode-workbench" + } + ] +} \ No newline at end of file diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts new file mode 100644 index 0000000000..34f035cede --- /dev/null +++ b/build/lib/i18n.ts @@ -0,0 +1,1116 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import * as fs from 'fs'; + +import { through, readable } from 'event-stream'; +import { ThroughStream } from 'through'; +import File = require('vinyl'); +import * as Is from 'is'; +import * as xml2js from 'xml2js'; +import * as glob from 'glob'; +import * as https from 'https'; + +var util = require('gulp-util'); +var iconv = require('iconv-lite'); + +function log(message: any, ...rest: any[]): void { + util.log(util.colors.green('[i18n]'), message, ...rest); +} + +interface Map { + [key: string]: V; +} + +interface Item { + id: string; + message: string; + comment: string; +} + +export interface Resource { + name: string; + project: string; +} + +interface ParsedXLF { + messages: Map; + originalFilePath: string; + language: string; +} + +interface LocalizeInfo { + key: string; + comment: string[]; +} + +module LocalizeInfo { + export function is(value: any): value is LocalizeInfo { + let candidate = value as LocalizeInfo; + return Is.defined(candidate) && Is.string(candidate.key) && (Is.undef(candidate.comment) || (Is.array(candidate.comment) && candidate.comment.every(element => Is.string(element)))); + } +} + +interface BundledFormat { + keys: Map<(string | LocalizeInfo)[]>; + messages: Map; + bundles: Map; +} + +module BundledFormat { + export function is(value: any): value is BundledFormat { + if (Is.undef(value)) { + return false; + } + + let candidate = value as BundledFormat; + let length = Object.keys(value).length; + + return length === 3 && Is.defined(candidate.keys) && Is.defined(candidate.messages) && Is.defined(candidate.bundles); + } +} + +interface ValueFormat { + message: string; + comment: string[]; +} + +interface PackageJsonFormat { + [key: string]: string | ValueFormat; +} + +module PackageJsonFormat { + export function is(value: any): value is PackageJsonFormat { + if (Is.undef(value) || !Is.object(value)) { + return false; + } + return Object.keys(value).every(key => { + let element = value[key]; + return Is.string(element) || (Is.object(element) && Is.defined(element.message) && Is.defined(element.comment)); + }); + } +} + +interface ModuleJsonFormat { + messages: string[]; + keys: (string | LocalizeInfo)[]; +} + +module ModuleJsonFormat { + export function is(value: any): value is ModuleJsonFormat { + let candidate = value as ModuleJsonFormat; + return Is.defined(candidate) + && Is.array(candidate.messages) && candidate.messages.every(message => Is.string(message)) + && Is.array(candidate.keys) && candidate.keys.every(key => Is.string(key) || LocalizeInfo.is(key)); + } +} + +export class Line { + private buffer: string[] = []; + + constructor(private indent: number = 0) { + if (indent > 0) { + this.buffer.push(new Array(indent + 1).join(' ')); + } + } + + public append(value: string): Line { + this.buffer.push(value); + return this; + } + + public toString(): string { + return this.buffer.join(''); + } +} + +class TextModel { + private _lines: string[]; + + constructor(contents: string) { + this._lines = contents.split(/\r\n|\r|\n/); + } + + public get lines(): string[] { + return this._lines; + } +} + +export class XLF { + private buffer: string[]; + private files: Map; + + constructor(public project: string) { + this.buffer = []; + this.files = Object.create(null); + } + + public toString(): string { + this.appendHeader(); + + for (let file in this.files) { + this.appendNewLine(``, 2); + for (let item of this.files[file]) { + this.addStringItem(item); + } + this.appendNewLine('', 2); + } + + this.appendFooter(); + return this.buffer.join('\r\n'); + } + + public addFile(original: string, keys: any[], messages: string[]) { + this.files[original] = []; + let existingKeys = []; + + for (let key of keys) { + // Ignore duplicate keys because Transifex does not populate those with translated values. + if (existingKeys.indexOf(key) !== -1) { + continue; + } + existingKeys.push(key); + + let message: string = encodeEntities(messages[keys.indexOf(key)]); + let comment: string = undefined; + + // Check if the message contains description (if so, it becomes an object type in JSON) + if (Is.string(key)) { + this.files[original].push({ id: key, message: message, comment: comment }); + } else { + if (key['comment'] && key['comment'].length > 0) { + comment = key['comment'].map(comment => encodeEntities(comment)).join('\r\n'); + } + + this.files[original].push({ id: key['key'], message: message, comment: comment }); + } + } + } + + private addStringItem(item: Item): void { + if (!item.id || !item.message) { + throw new Error('No item ID or value specified.'); + } + + this.appendNewLine(``, 4); + this.appendNewLine(`${item.message}`, 6); + + if (item.comment) { + this.appendNewLine(`${item.comment}`, 6); + } + + this.appendNewLine('', 4); + } + + private appendHeader(): void { + this.appendNewLine('', 0); + this.appendNewLine('', 0); + } + + private appendFooter(): void { + this.appendNewLine('', 0); + } + + private appendNewLine(content: string, indent?: number): void { + let line = new Line(indent); + line.append(content); + this.buffer.push(line.toString()); + } + + static parse = function (xlfString: string): Promise { + return new Promise((resolve, reject) => { + let parser = new xml2js.Parser(); + + let files: { messages: Map, originalFilePath: string, language: string }[] = []; + + parser.parseString(xlfString, function (err, result) { + if (err) { + reject(`Failed to parse XLIFF string. ${err}`); + } + + const fileNodes: any[] = result['xliff']['file']; + if (!fileNodes) { + reject('XLIFF file does not contain "xliff" or "file" node(s) required for parsing.'); + } + + fileNodes.forEach((file) => { + const originalFilePath = file.$.original; + if (!originalFilePath) { + reject('XLIFF file node does not contain original attribute to determine the original location of the resource file.'); + } + const language = file.$['target-language'].toLowerCase(); + if (!language) { + reject('XLIFF file node does not contain target-language attribute to determine translated language.'); + } + + let messages: Map = {}; + const transUnits = file.body[0]['trans-unit']; + + transUnits.forEach(unit => { + const key = unit.$.id; + if (!unit.target) { + return; // No translation available + } + + const val = unit.target.toString(); + if (key && val) { + messages[key] = decodeEntities(val); + } else { + reject('XLIFF file does not contain full localization data. ID or target translation for one of the trans-unit nodes is not present.'); + } + }); + + files.push({ messages: messages, originalFilePath: originalFilePath, language: language }); + }); + + resolve(files); + }); + }); + }; +} + +const iso639_3_to_2: Map = { + 'chs': 'zh-cn', + 'cht': 'zh-tw', + 'csy': 'cs-cz', + 'deu': 'de', + 'enu': 'en', + 'esn': 'es', + 'fra': 'fr', + 'hun': 'hu', + 'ita': 'it', + 'jpn': 'ja', + 'kor': 'ko', + 'nld': 'nl', + 'plk': 'pl', + 'ptb': 'pt-br', + 'ptg': 'pt', + 'rus': 'ru', + 'sve': 'sv-se', + 'trk': 'tr' +}; + +/** + * Used to map Transifex to VS Code language code representation. + */ +const iso639_2_to_3: Map = { + 'zh-hans': 'chs', + 'zh-hant': 'cht', + 'cs-cz': 'csy', + 'de': 'deu', + 'en': 'enu', + 'es': 'esn', + 'fr': 'fra', + 'hu': 'hun', + 'it': 'ita', + 'ja': 'jpn', + 'ko': 'kor', + 'nl': 'nld', + 'pl': 'plk', + 'pt-br': 'ptb', + 'pt': 'ptg', + 'ru': 'rus', + 'sv-se': 'sve', + 'tr': 'trk' +}; + +interface IDirectoryInfo { + name: string; + iso639_2: string; +} + +function sortLanguages(directoryNames: string[]): IDirectoryInfo[] { + return directoryNames.map((dirName) => { + var lower = dirName.toLowerCase(); + return { + name: lower, + iso639_2: iso639_3_to_2[lower] + }; + }).sort((a: IDirectoryInfo, b: IDirectoryInfo): number => { + if (!a.iso639_2 && !b.iso639_2) { + return 0; + } + if (!a.iso639_2) { + return -1; + } + if (!b.iso639_2) { + return 1; + } + return a.iso639_2 < b.iso639_2 ? -1 : (a.iso639_2 > b.iso639_2 ? 1 : 0); + }); +} + +function stripComments(content: string): string { + /** + * First capturing group matches double quoted string + * Second matches single quotes string + * Third matches block comments + * Fourth matches line comments + */ + var regexp: RegExp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; + let result = content.replace(regexp, (match, m1, m2, m3, m4) => { + // Only one of m1, m2, m3, m4 matches + if (m3) { + // A block comment. Replace with nothing + return ''; + } else if (m4) { + // A line comment. If it ends in \r?\n then keep it. + let length = m4.length; + if (length > 2 && m4[length - 1] === '\n') { + return m4[length - 2] === '\r' ? '\r\n' : '\n'; + } else { + return ''; + } + } else { + // We match a string + return match; + } + }); + return result; +} + +function escapeCharacters(value: string): string { + var result: string[] = []; + for (var i = 0; i < value.length; i++) { + var ch = value.charAt(i); + switch (ch) { + case '\'': + result.push('\\\''); + break; + case '"': + result.push('\\"'); + break; + case '\\': + result.push('\\\\'); + break; + case '\n': + result.push('\\n'); + break; + case '\r': + result.push('\\r'); + break; + case '\t': + result.push('\\t'); + break; + case '\b': + result.push('\\b'); + break; + case '\f': + result.push('\\f'); + break; + default: + result.push(ch); + } + } + return result.join(''); +} + +function processCoreBundleFormat(fileHeader: string, languages: string[], json: BundledFormat, emitter: any) { + let keysSection = json.keys; + let messageSection = json.messages; + let bundleSection = json.bundles; + + let statistics: Map = Object.create(null); + + let total: number = 0; + let defaultMessages: Map> = Object.create(null); + let modules = Object.keys(keysSection); + modules.forEach((module) => { + let keys = keysSection[module]; + let messages = messageSection[module]; + if (!messages || keys.length !== messages.length) { + emitter.emit('error', `Message for module ${module} corrupted. Mismatch in number of keys and messages.`); + return; + } + let messageMap: Map = Object.create(null); + defaultMessages[module] = messageMap; + keys.map((key, i) => { + total++; + if (Is.string(key)) { + messageMap[key] = messages[i]; + } else { + messageMap[key.key] = messages[i]; + } + }); + }); + + let languageDirectory = path.join(__dirname, '..', '..', 'i18n'); + let languageDirs; + if (languages) { + languageDirs = sortLanguages(languages); + } else { + languageDirs = sortLanguages(fs.readdirSync(languageDirectory).filter((item) => fs.statSync(path.join(languageDirectory, item)).isDirectory())); + } + languageDirs.forEach((language) => { + if (!language.iso639_2) { + return; + } + + if (process.env['VSCODE_BUILD_VERBOSE']) { + log(`Generating nls bundles for: ${language.iso639_2}`); + } + + statistics[language.iso639_2] = 0; + let localizedModules: Map = Object.create(null); + let cwd = path.join(languageDirectory, language.name, 'src'); + modules.forEach((module) => { + let order = keysSection[module]; + let i18nFile = path.join(cwd, module) + '.i18n.json'; + let messages: Map = null; + if (fs.existsSync(i18nFile)) { + let content = stripComments(fs.readFileSync(i18nFile, 'utf8')); + messages = JSON.parse(content); + } else { + if (process.env['VSCODE_BUILD_VERBOSE']) { + log(`No localized messages found for module ${module}. Using default messages.`); + } + messages = defaultMessages[module]; + statistics[language.iso639_2] = statistics[language.iso639_2] + Object.keys(messages).length; + } + let localizedMessages: string[] = []; + order.forEach((keyInfo) => { + let key: string = null; + if (Is.string(keyInfo)) { + key = keyInfo; + } else { + key = keyInfo.key; + } + let message: string = messages[key]; + if (!message) { + if (process.env['VSCODE_BUILD_VERBOSE']) { + log(`No localized message found for key ${key} in module ${module}. Using default message.`); + } + message = defaultMessages[module][key]; + statistics[language.iso639_2] = statistics[language.iso639_2] + 1; + } + localizedMessages.push(message); + }); + localizedModules[module] = localizedMessages; + }); + Object.keys(bundleSection).forEach((bundle) => { + let modules = bundleSection[bundle]; + let contents: string[] = [ + fileHeader, + `define("${bundle}.nls.${language.iso639_2}", {` + ]; + modules.forEach((module, index) => { + contents.push(`\t"${module}": [`); + let messages = localizedModules[module]; + if (!messages) { + emitter.emit('error', `Didn't find messages for module ${module}.`); + return; + } + messages.forEach((message, index) => { + contents.push(`\t\t"${escapeCharacters(message)}${index < messages.length ? '",' : '"'}`); + }); + contents.push(index < modules.length - 1 ? '\t],' : '\t]'); + }); + contents.push('});'); + emitter.emit('data', new File({ path: bundle + '.nls.' + language.iso639_2 + '.js', contents: new Buffer(contents.join('\n'), 'utf-8') })); + }); + }); + Object.keys(statistics).forEach(key => { + let value = statistics[key]; + log(`${key} has ${value} untranslated strings.`); + }); + languageDirs.forEach(dir => { + const language = dir.name; + let iso639_2 = iso639_3_to_2[language]; + if (!iso639_2) { + log(`\tCouldn't find iso639 2 mapping for language ${language}. Using default language instead.`); + } else { + let stats = statistics[iso639_2]; + if (Is.undef(stats)) { + log(`\tNo translations found for language ${language}. Using default language instead.`); + } + } + }); +} + +export function processNlsFiles(opts: { fileHeader: string; languages: string[] }): ThroughStream { + return through(function (file: File) { + let fileName = path.basename(file.path); + if (fileName === 'nls.metadata.json') { + let json = null; + if (file.isBuffer()) { + json = JSON.parse((file.contents).toString('utf8')); + } else { + this.emit('error', `Failed to read component file: ${file.relative}`); + } + if (BundledFormat.is(json)) { + processCoreBundleFormat(opts.fileHeader, opts.languages, json, this); + } + } + this.emit('data', file); + }); +} + +export function prepareXlfFiles(projectName?: string, extensionName?: string): ThroughStream { + return through( + function (file: File) { + if (!file.isBuffer()) { + throw new Error(`Failed to read component file: ${file.relative}`); + } + + const extension = path.extname(file.path); + if (extension === '.json') { + const json = JSON.parse((file.contents).toString('utf8')); + + if (BundledFormat.is(json)) { + importBundleJson(file, json, this); + } else if (PackageJsonFormat.is(json) || ModuleJsonFormat.is(json)) { + importModuleOrPackageJson(file, json, projectName, this, extensionName); + } else { + throw new Error(`JSON format cannot be deduced for ${file.relative}.`); + } + } else if (extension === '.isl') { + importIsl(file, this); + } + } + ); +} + +const editorProject: string = 'vscode-editor', + workbenchProject: string = 'vscode-workbench', + extensionsProject: string = 'vscode-extensions', + setupProject: string = 'vscode-setup'; + +export function getResource(sourceFile: string): Resource { + let resource: string; + + if (/^vs\/platform/.test(sourceFile)) { + return { name: 'vs/platform', project: editorProject }; + } else if (/^vs\/editor\/contrib/.test(sourceFile)) { + return { name: 'vs/editor/contrib', project: editorProject }; + } else if (/^vs\/editor/.test(sourceFile)) { + return { name: 'vs/editor', project: editorProject }; + } else if (/^vs\/base/.test(sourceFile)) { + return { name: 'vs/base', project: editorProject }; + } else if (/^vs\/code/.test(sourceFile)) { + return { name: 'vs/code', project: workbenchProject }; + } else if (/^vs\/workbench\/parts/.test(sourceFile)) { + resource = sourceFile.split('/', 4).join('/'); + return { name: resource, project: workbenchProject }; + } else if (/^vs\/workbench\/services/.test(sourceFile)) { + resource = sourceFile.split('/', 4).join('/'); + return { name: resource, project: workbenchProject }; + } else if (/^vs\/workbench/.test(sourceFile)) { + return { name: 'vs/workbench', project: workbenchProject }; + } + + throw new Error(`Could not identify the XLF bundle for ${sourceFile}`); +} + + +function importBundleJson(file: File, json: BundledFormat, stream: ThroughStream): void { + let bundleXlfs: Map = Object.create(null); + + for (let source in json.keys) { + const projectResource = getResource(source); + const resource = projectResource.name; + const project = projectResource.project; + + const keys = json.keys[source]; + const messages = json.messages[source]; + if (keys.length !== messages.length) { + throw new Error(`There is a mismatch between keys and messages in ${file.relative}`); + } + + let xlf = bundleXlfs[resource] ? bundleXlfs[resource] : bundleXlfs[resource] = new XLF(project); + xlf.addFile('src/' + source, keys, messages); + } + + for (let resource in bundleXlfs) { + const newFilePath = `${bundleXlfs[resource].project}/${resource.replace(/\//g, '_')}.xlf`; + const xlfFile = new File({ path: newFilePath, contents: new Buffer(bundleXlfs[resource].toString(), 'utf-8') }); + stream.emit('data', xlfFile); + } +} + +// Keeps existing XLF instances and a state of how many files were already processed for faster file emission +var extensions: Map<{ xlf: XLF, processed: number }> = Object.create(null); +function importModuleOrPackageJson(file: File, json: ModuleJsonFormat | PackageJsonFormat, projectName: string, stream: ThroughStream, extensionName?: string): void { + if (ModuleJsonFormat.is(json) && json['keys'].length !== json['messages'].length) { + throw new Error(`There is a mismatch between keys and messages in ${file.relative}`); + } + + // Prepare the source path for attribute in XLF & extract messages from JSON + const formattedSourcePath = file.relative.replace(/\\/g, '/'); + const messages = Object.keys(json).map((key) => json[key].toString()); + + // Stores the amount of localization files to be transformed to XLF before the emission + let localizationFilesCount, + originalFilePath; + // If preparing XLF for external extension, then use different glob pattern and source path + if (extensionName) { + localizationFilesCount = glob.sync('**/*.nls.json').length; + originalFilePath = `${formattedSourcePath.substr(0, formattedSourcePath.length - '.nls.json'.length)}`; + } else { + // Used for vscode/extensions folder + extensionName = formattedSourcePath.split('/')[0]; + localizationFilesCount = glob.sync(`./extensions/${extensionName}/**/*.nls.json`).length; + originalFilePath = `extensions/${formattedSourcePath.substr(0, formattedSourcePath.length - '.nls.json'.length)}`; + } + + let extension = extensions[extensionName] ? + extensions[extensionName] : extensions[extensionName] = { xlf: new XLF(projectName), processed: 0 }; + + // .nls.json can come with empty array of keys and messages, check for it + if (ModuleJsonFormat.is(json) && json.keys.length !== 0) { + extension.xlf.addFile(originalFilePath, json.keys, json.messages); + } else if (PackageJsonFormat.is(json) && Object.keys(json).length !== 0) { + extension.xlf.addFile(originalFilePath, Object.keys(json), messages); + } + + // Check if XLF is populated with file nodes to emit it + if (++extensions[extensionName].processed === localizationFilesCount) { + const newFilePath = path.join(projectName, extensionName + '.xlf'); + const xlfFile = new File({ path: newFilePath, contents: new Buffer(extension.xlf.toString(), 'utf-8') }); + stream.emit('data', xlfFile); + } +} + +function importIsl(file: File, stream: ThroughStream) { + let projectName: string, + resourceFile: string; + if (path.basename(file.path) === 'Default.isl') { + projectName = setupProject; + resourceFile = 'setup_default.xlf'; + } else { + projectName = workbenchProject; + resourceFile = 'setup_messages.xlf'; + } + + let xlf = new XLF(projectName), + keys: string[] = [], + messages: string[] = []; + + let model = new TextModel(file.contents.toString()); + let inMessageSection = false; + model.lines.forEach(line => { + if (line.length === 0) { + return; + } + let firstChar = line.charAt(0); + switch (firstChar) { + case ';': + // Comment line; + return; + case '[': + inMessageSection = '[Messages]' === line || '[CustomMessages]' === line; + return; + } + if (!inMessageSection) { + return; + } + let sections: string[] = line.split('='); + if (sections.length !== 2) { + throw new Error(`Badly formatted message found: ${line}`); + } else { + let key = sections[0]; + let value = sections[1]; + if (key.length > 0 && value.length > 0) { + keys.push(key); + messages.push(value); + } + } + }); + + const originalPath = file.path.substring(file.cwd.length + 1, file.path.split('.')[0].length).replace(/\\/g, '/'); + xlf.addFile(originalPath, keys, messages); + + // Emit only upon all ISL files combined into single XLF instance + const newFilePath = path.join(projectName, resourceFile); + const xlfFile = new File({ path: newFilePath, contents: new Buffer(xlf.toString(), 'utf-8') }); + stream.emit('data', xlfFile); +} + +export function pushXlfFiles(apiHostname: string, username: string, password: string): ThroughStream { + let tryGetPromises = []; + let updateCreatePromises = []; + + return through(function (file: File) { + const project = path.dirname(file.relative); + const fileName = path.basename(file.path); + const slug = fileName.substr(0, fileName.length - '.xlf'.length); + const credentials = `${username}:${password}`; + + // Check if resource already exists, if not, then create it. + let promise = tryGetResource(project, slug, apiHostname, credentials); + tryGetPromises.push(promise); + promise.then(exists => { + if (exists) { + promise = updateResource(project, slug, file, apiHostname, credentials); + } else { + promise = createResource(project, slug, file, apiHostname, credentials); + } + updateCreatePromises.push(promise); + }); + + }, function () { + // End the pipe only after all the communication with Transifex API happened + Promise.all(tryGetPromises).then(() => { + Promise.all(updateCreatePromises).then(() => { + this.emit('end'); + }).catch((reason) => { throw new Error(reason); }); + }).catch((reason) => { throw new Error(reason); }); + }); +} + +function tryGetResource(project: string, slug: string, apiHostname: string, credentials: string): Promise { + return new Promise((resolve, reject) => { + const options = { + hostname: apiHostname, + path: `/api/2/project/${project}/resource/${slug}/?details`, + auth: credentials, + method: 'GET' + }; + + const request = https.request(options, (response) => { + if (response.statusCode === 404) { + resolve(false); + } else if (response.statusCode === 200) { + resolve(true); + } else { + reject(`Failed to query resource ${project}/${slug}. Response: ${response.statusCode} ${response.statusMessage}`); + } + }); + request.on('error', (err) => { + reject(`Failed to get ${project}/${slug} on Transifex: ${err}`); + }); + + request.end(); + }); +} + +function createResource(project: string, slug: string, xlfFile: File, apiHostname: string, credentials: any): Promise { + return new Promise((resolve, reject) => { + const data = JSON.stringify({ + 'content': xlfFile.contents.toString(), + 'name': slug, + 'slug': slug, + 'i18n_type': 'XLIFF' + }); + const options = { + hostname: apiHostname, + path: `/api/2/project/${project}/resources`, + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(data) + }, + auth: credentials, + method: 'POST' + }; + + let request = https.request(options, (res) => { + if (res.statusCode === 201) { + log(`Resource ${project}/${slug} successfully created on Transifex.`); + } else { + reject(`Something went wrong in the request creating ${slug} in ${project}. ${res.statusCode}`); + } + }); + request.on('error', (err) => { + reject(`Failed to create ${project}/${slug} on Transifex: ${err}`); + }); + + request.write(data); + request.end(); + }); +} + +/** + * The following link provides information about how Transifex handles updates of a resource file: + * https://dev.befoolish.co/tx-docs/public/projects/updating-content#what-happens-when-you-update-files + */ +function updateResource(project: string, slug: string, xlfFile: File, apiHostname: string, credentials: string): Promise { + return new Promise((resolve, reject) => { + const data = JSON.stringify({ content: xlfFile.contents.toString() }); + const options = { + hostname: apiHostname, + path: `/api/2/project/${project}/resource/${slug}/content`, + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(data) + }, + auth: credentials, + method: 'PUT' + }; + + let request = https.request(options, (res) => { + if (res.statusCode === 200) { + res.setEncoding('utf8'); + + let responseBuffer: string = ''; + res.on('data', function (chunk) { + responseBuffer += chunk; + }); + res.on('end', () => { + const response = JSON.parse(responseBuffer); + log(`Resource ${project}/${slug} successfully updated on Transifex. Strings added: ${response.strings_added}, updated: ${response.strings_added}, deleted: ${response.strings_added}`); + resolve(); + }); + } else { + reject(`Something went wrong in the request updating ${slug} in ${project}. ${res.statusCode}`); + } + }); + request.on('error', (err) => { + reject(`Failed to update ${project}/${slug} on Transifex: ${err}`); + }); + + request.write(data); + request.end(); + }); +} + +function obtainProjectResources(projectName: string): Resource[] { + let resources: Resource[] = []; + + if (projectName === editorProject) { + const json = fs.readFileSync('./build/lib/i18n.resources.json', 'utf8'); + resources = JSON.parse(json).editor; + } else if (projectName === workbenchProject) { + const json = fs.readFileSync('./build/lib/i18n.resources.json', 'utf8'); + resources = JSON.parse(json).workbench; + } else if (projectName === extensionsProject) { + let extensionsToLocalize: string[] = glob.sync('./extensions/**/*.nls.json').map(extension => extension.split('/')[2]); + let resourcesToPull: string[] = []; + + extensionsToLocalize.forEach(extension => { + if (resourcesToPull.indexOf(extension) === -1) { // remove duplicate elements returned by glob + resourcesToPull.push(extension); + resources.push({ name: extension, project: projectName }); + } + }); + } else if (projectName === setupProject) { + resources.push({ name: 'setup_default', project: setupProject }); + } + + return resources; +} + +export function pullXlfFiles(projectName: string, apiHostname: string, username: string, password: string, languages: string[], resources?: Resource[]): NodeJS.ReadableStream { + if (!resources) { + resources = obtainProjectResources(projectName); + } + if (!resources) { + throw new Error('Transifex projects and resources must be defined to be able to pull translations from Transifex.'); + } + + const credentials = `${username}:${password}`; + let expectedTranslationsCount = languages.length * resources.length; + let translationsRetrieved = 0, called = false; + + return readable(function (count, callback) { + // Mark end of stream when all resources were retrieved + if (translationsRetrieved === expectedTranslationsCount) { + return this.emit('end'); + } + + if (!called) { + called = true; + const stream = this; + + // Retrieve XLF files from main projects + languages.map(function (language) { + resources.map(function (resource) { + retrieveResource(language, resource, apiHostname, credentials).then((file: File) => { + stream.emit('data', file); + translationsRetrieved++; + }).catch(error => { throw new Error(error); }); + }); + }); + } + + callback(); + }); +} + +function retrieveResource(language: string, resource: Resource, apiHostname, credentials): Promise { + return new Promise((resolve, reject) => { + const slug = resource.name.replace(/\//g, '_'); + const project = resource.project; + const iso639 = language.toLowerCase(); + const options = { + hostname: apiHostname, + path: `/api/2/project/${project}/resource/${slug}/translation/${iso639}?file&mode=onlyreviewed`, + auth: credentials, + method: 'GET' + }; + + let request = https.request(options, (res) => { + let xlfBuffer: Buffer[] = []; + res.on('data', (chunk: Buffer) => xlfBuffer.push(chunk)); + res.on('end', () => { + if (res.statusCode === 200) { + resolve(new File({ contents: Buffer.concat(xlfBuffer), path: `${project}/${iso639_2_to_3[language]}/${slug}.xlf` })); + } + reject(`${slug} in ${project} returned no data. Response code: ${res.statusCode}.`); + }); + }); + request.on('error', (err) => { + reject(`Failed to query resource ${slug} with the following error: ${err}`); + }); + request.end(); + }); +} + +export function prepareJsonFiles(): ThroughStream { + let parsePromises: Promise[] = []; + + return through(function (xlf: File) { + let stream = this; + let parsePromise = XLF.parse(xlf.contents.toString()); + parsePromises.push(parsePromise); + parsePromise.then( + function (resolvedFiles) { + resolvedFiles.forEach(file => { + let messages = file.messages, translatedFile; + + // ISL file path always starts with 'build/' + if (/^build\//.test(file.originalFilePath)) { + const defaultLanguages = { 'zh-hans': true, 'zh-hant': true, 'ko': true }; + if (path.basename(file.originalFilePath) === 'Default' && !defaultLanguages[file.language]) { + return; + } + + translatedFile = createIslFile('..', file.originalFilePath, messages, iso639_2_to_3[file.language]); + } else { + translatedFile = createI18nFile(iso639_2_to_3[file.language], file.originalFilePath, messages); + } + + stream.emit('data', translatedFile); + }); + }, + function (rejectReason) { + throw new Error(`XLF parsing error: ${rejectReason}`); + } + ); + }, function () { + Promise.all(parsePromises) + .then(() => { this.emit('end'); }) + .catch(reason => { throw new Error(reason); }); + }); +} + +function createI18nFile(base: string, originalFilePath: string, messages: Map): File { + let content = [ + '/*---------------------------------------------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' * Licensed under the Source EULA. See License.txt in the project root for license information.', + ' *--------------------------------------------------------------------------------------------*/', + '// Do not edit this file. It is machine generated.' + ].join('\n') + '\n' + JSON.stringify(messages, null, '\t').replace(/\r\n/g, '\n'); + + return new File({ + path: path.join(base, originalFilePath + '.i18n.json'), + contents: new Buffer(content, 'utf8') + }); +} + + +const languageNames: Map = { + 'chs': 'Simplified Chinese', + 'cht': 'Traditional Chinese', + 'kor': 'Korean' +}; + +const languageIds: Map = { + 'chs': '$0804', + 'cht': '$0404', + 'kor': '$0412' +}; + +const encodings: Map = { + 'chs': 'CP936', + 'cht': 'CP950', + 'jpn': 'CP932', + 'kor': 'CP949', + 'deu': 'CP1252', + 'fra': 'CP1252', + 'esn': 'CP1252', + 'rus': 'CP1251', + 'ita': 'CP1252', + 'ptb': 'CP1252', + 'hun': 'CP1250', + 'trk': 'CP1254' +}; + +function createIslFile(base: string, originalFilePath: string, messages: Map, language: string): File { + let content: string[] = []; + let originalContent: TextModel; + if (path.basename(originalFilePath) === 'Default') { + originalContent = new TextModel(fs.readFileSync(originalFilePath + '.isl', 'utf8')); + } else { + originalContent = new TextModel(fs.readFileSync(originalFilePath + '.en.isl', 'utf8')); + } + + originalContent.lines.forEach(line => { + if (line.length > 0) { + let firstChar = line.charAt(0); + if (firstChar === '[' || firstChar === ';') { + if (line === '; *** Inno Setup version 5.5.3+ English messages ***') { + content.push(`; *** Inno Setup version 5.5.3+ ${languageNames[language]} messages ***`); + } else { + content.push(line); + } + } else { + let sections: string[] = line.split('='); + let key = sections[0]; + let translated = line; + if (key) { + if (key === 'LanguageName') { + translated = `${key}=${languageNames[language]}`; + } else if (key === 'LanguageID') { + translated = `${key}=${languageIds[language]}`; + } else if (key === 'LanguageCodePage') { + translated = `${key}=${encodings[language].substr(2)}`; + } else { + let translatedMessage = messages[key]; + if (translatedMessage) { + translated = `${key}=${translatedMessage}`; + } + } + } + + content.push(translated); + } + } + }); + + const tag = iso639_3_to_2[language]; + const basename = path.basename(originalFilePath); + const filePath = `${path.join(base, path.dirname(originalFilePath), basename)}.${tag}.isl`; + + return new File({ + path: filePath, + contents: iconv.encode(new Buffer(content.join('\r\n'), 'utf8'), encodings[language]) + }); +} + +function encodeEntities(value: string): string { + var result: string[] = []; + for (var i = 0; i < value.length; i++) { + var ch = value[i]; + switch (ch) { + case '<': + result.push('<'); + break; + case '>': + result.push('>'); + break; + case '&': + result.push('&'); + break; + default: + result.push(ch); + } + } + return result.join(''); +} + +function decodeEntities(value: string): string { + return value.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); +} \ No newline at end of file diff --git a/build/lib/nls.js b/build/lib/nls.js new file mode 100644 index 0000000000..b98e524c0c --- /dev/null +++ b/build/lib/nls.js @@ -0,0 +1,359 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var ts = require("typescript"); +var lazy = require("lazy.js"); +var event_stream_1 = require("event-stream"); +var File = require("vinyl"); +var sm = require("source-map"); +var assign = require("object-assign"); +var path = require("path"); +var CollectStepResult; +(function (CollectStepResult) { + CollectStepResult[CollectStepResult["Yes"] = 0] = "Yes"; + CollectStepResult[CollectStepResult["YesAndRecurse"] = 1] = "YesAndRecurse"; + CollectStepResult[CollectStepResult["No"] = 2] = "No"; + CollectStepResult[CollectStepResult["NoAndRecurse"] = 3] = "NoAndRecurse"; +})(CollectStepResult || (CollectStepResult = {})); +function collect(node, fn) { + var result = []; + function loop(node) { + var stepResult = fn(node); + if (stepResult === CollectStepResult.Yes || stepResult === CollectStepResult.YesAndRecurse) { + result.push(node); + } + if (stepResult === CollectStepResult.YesAndRecurse || stepResult === CollectStepResult.NoAndRecurse) { + ts.forEachChild(node, loop); + } + } + loop(node); + return result; +} +function clone(object) { + var result = {}; + for (var id in object) { + result[id] = object[id]; + } + return result; +} +function template(lines) { + var indent = '', wrap = ''; + if (lines.length > 1) { + indent = '\t'; + wrap = '\n'; + } + return "/*---------------------------------------------------------\n * Copyright (C) Microsoft Corporation. All rights reserved.\n *--------------------------------------------------------*/\ndefine([], [" + (wrap + lines.map(function (l) { return indent + l; }).join(',\n') + wrap) + "]);"; +} +/** + * Returns a stream containing the patched JavaScript and source maps. + */ +function nls() { + var input = event_stream_1.through(); + var output = input.pipe(event_stream_1.through(function (f) { + var _this = this; + if (!f.sourceMap) { + return this.emit('error', new Error("File " + f.relative + " does not have sourcemaps.")); + } + var source = f.sourceMap.sources[0]; + if (!source) { + return this.emit('error', new Error("File " + f.relative + " does not have a source in the source map.")); + } + var root = f.sourceMap.sourceRoot; + if (root) { + source = path.join(root, source); + } + var typescript = f.sourceMap.sourcesContent[0]; + if (!typescript) { + return this.emit('error', new Error("File " + f.relative + " does not have the original content in the source map.")); + } + nls.patchFiles(f, typescript).forEach(function (f) { return _this.emit('data', f); }); + })); + return event_stream_1.duplex(input, output); +} +function isImportNode(node) { + return node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration; +} +(function (nls_1) { + function fileFrom(file, contents, path) { + if (path === void 0) { path = file.path; } + return new File({ + contents: new Buffer(contents), + base: file.base, + cwd: file.cwd, + path: path + }); + } + nls_1.fileFrom = fileFrom; + function mappedPositionFrom(source, lc) { + return { source: source, line: lc.line + 1, column: lc.character }; + } + nls_1.mappedPositionFrom = mappedPositionFrom; + function lcFrom(position) { + return { line: position.line - 1, character: position.column }; + } + nls_1.lcFrom = lcFrom; + var SingleFileServiceHost = (function () { + function SingleFileServiceHost(options, filename, contents) { + var _this = this; + this.options = options; + this.filename = filename; + this.getCompilationSettings = function () { return _this.options; }; + this.getScriptFileNames = function () { return [_this.filename]; }; + this.getScriptVersion = function () { return '1'; }; + this.getScriptSnapshot = function (name) { return name === _this.filename ? _this.file : _this.lib; }; + this.getCurrentDirectory = function () { return ''; }; + this.getDefaultLibFileName = function () { return 'lib.d.ts'; }; + this.file = ts.ScriptSnapshot.fromString(contents); + this.lib = ts.ScriptSnapshot.fromString(''); + } + return SingleFileServiceHost; + }()); + nls_1.SingleFileServiceHost = SingleFileServiceHost; + function isCallExpressionWithinTextSpanCollectStep(textSpan, node) { + if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) { + return CollectStepResult.No; + } + return node.kind === ts.SyntaxKind.CallExpression ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse; + } + function analyze(contents, options) { + if (options === void 0) { options = {}; } + var filename = 'file.ts'; + var serviceHost = new SingleFileServiceHost(assign(clone(options), { noResolve: true }), filename, contents); + var service = ts.createLanguageService(serviceHost); + var sourceFile = ts.createSourceFile(filename, contents, ts.ScriptTarget.ES5, true); + // all imports + var imports = lazy(collect(sourceFile, function (n) { return isImportNode(n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse; })); + // import nls = require('vs/nls'); + var importEqualsDeclarations = imports + .filter(function (n) { return n.kind === ts.SyntaxKind.ImportEqualsDeclaration; }) + .map(function (n) { return n; }) + .filter(function (d) { return d.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference; }) + .filter(function (d) { return d.moduleReference.expression.getText() === '\'vs/nls\''; }); + // import ... from 'vs/nls'; + var importDeclarations = imports + .filter(function (n) { return n.kind === ts.SyntaxKind.ImportDeclaration; }) + .map(function (n) { return n; }) + .filter(function (d) { return d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral; }) + .filter(function (d) { return d.moduleSpecifier.getText() === '\'vs/nls\''; }) + .filter(function (d) { return !!d.importClause && !!d.importClause.namedBindings; }); + var nlsExpressions = importEqualsDeclarations + .map(function (d) { return d.moduleReference.expression; }) + .concat(importDeclarations.map(function (d) { return d.moduleSpecifier; })) + .map(function (d) { return ({ + start: ts.getLineAndCharacterOfPosition(sourceFile, d.getStart()), + end: ts.getLineAndCharacterOfPosition(sourceFile, d.getEnd()) + }); }); + // `nls.localize(...)` calls + var nlsLocalizeCallExpressions = importDeclarations + .filter(function (d) { return d.importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport; }) + .map(function (d) { return d.importClause.namedBindings.name; }) + .concat(importEqualsDeclarations.map(function (d) { return d.name; })) + .map(function (n) { return service.getReferencesAtPosition(filename, n.pos + 1); }) + .flatten() + .filter(function (r) { return !r.isWriteAccess; }) + .map(function (r) { return collect(sourceFile, function (n) { return isCallExpressionWithinTextSpanCollectStep(r.textSpan, n); }); }) + .map(function (a) { return lazy(a).last(); }) + .filter(function (n) { return !!n; }) + .map(function (n) { return n; }) + .filter(function (n) { return n.expression.kind === ts.SyntaxKind.PropertyAccessExpression && n.expression.name.getText() === 'localize'; }); + // `localize` named imports + var allLocalizeImportDeclarations = importDeclarations + .filter(function (d) { return d.importClause.namedBindings.kind === ts.SyntaxKind.NamedImports; }) + .map(function (d) { return d.importClause.namedBindings.elements; }) + .flatten(); + // `localize` read-only references + var localizeReferences = allLocalizeImportDeclarations + .filter(function (d) { return d.name.getText() === 'localize'; }) + .map(function (n) { return service.getReferencesAtPosition(filename, n.pos + 1); }) + .flatten() + .filter(function (r) { return !r.isWriteAccess; }); + // custom named `localize` read-only references + var namedLocalizeReferences = allLocalizeImportDeclarations + .filter(function (d) { return d.propertyName && d.propertyName.getText() === 'localize'; }) + .map(function (n) { return service.getReferencesAtPosition(filename, n.name.pos + 1); }) + .flatten() + .filter(function (r) { return !r.isWriteAccess; }); + // find the deepest call expressions AST nodes that contain those references + var localizeCallExpressions = localizeReferences + .concat(namedLocalizeReferences) + .map(function (r) { return collect(sourceFile, function (n) { return isCallExpressionWithinTextSpanCollectStep(r.textSpan, n); }); }) + .map(function (a) { return lazy(a).last(); }) + .filter(function (n) { return !!n; }) + .map(function (n) { return n; }); + // collect everything + var localizeCalls = nlsLocalizeCallExpressions + .concat(localizeCallExpressions) + .map(function (e) { return e.arguments; }) + .filter(function (a) { return a.length > 1; }) + .sort(function (a, b) { return a[0].getStart() - b[0].getStart(); }) + .map(function (a) { return ({ + keySpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getEnd()) }, + key: a[0].getText(), + valueSpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getEnd()) }, + value: a[1].getText() + }); }); + return { + localizeCalls: localizeCalls.toArray(), + nlsExpressions: nlsExpressions.toArray() + }; + } + nls_1.analyze = analyze; + var TextModel = (function () { + function TextModel(contents) { + var regex = /\r\n|\r|\n/g; + var index = 0; + var match; + this.lines = []; + this.lineEndings = []; + while (match = regex.exec(contents)) { + this.lines.push(contents.substring(index, match.index)); + this.lineEndings.push(match[0]); + index = regex.lastIndex; + } + if (contents.length > 0) { + this.lines.push(contents.substring(index, contents.length)); + this.lineEndings.push(''); + } + } + TextModel.prototype.get = function (index) { + return this.lines[index]; + }; + TextModel.prototype.set = function (index, line) { + this.lines[index] = line; + }; + Object.defineProperty(TextModel.prototype, "lineCount", { + get: function () { + return this.lines.length; + }, + enumerable: true, + configurable: true + }); + /** + * Applies patch(es) to the model. + * Multiple patches must be ordered. + * Does not support patches spanning multiple lines. + */ + TextModel.prototype.apply = function (patch) { + var startLineNumber = patch.span.start.line; + var endLineNumber = patch.span.end.line; + var startLine = this.lines[startLineNumber] || ''; + var endLine = this.lines[endLineNumber] || ''; + this.lines[startLineNumber] = [ + startLine.substring(0, patch.span.start.character), + patch.content, + endLine.substring(patch.span.end.character) + ].join(''); + for (var i = startLineNumber + 1; i <= endLineNumber; i++) { + this.lines[i] = ''; + } + }; + TextModel.prototype.toString = function () { + return lazy(this.lines).zip(this.lineEndings) + .flatten().toArray().join(''); + }; + return TextModel; + }()); + nls_1.TextModel = TextModel; + function patchJavascript(patches, contents, moduleId) { + var model = new nls.TextModel(contents); + // patch the localize calls + lazy(patches).reverse().each(function (p) { return model.apply(p); }); + // patch the 'vs/nls' imports + var firstLine = model.get(0); + var patchedFirstLine = firstLine.replace(/(['"])vs\/nls\1/g, "$1vs/nls!" + moduleId + "$1"); + model.set(0, patchedFirstLine); + return model.toString(); + } + nls_1.patchJavascript = patchJavascript; + function patchSourcemap(patches, rsm, smc) { + var smg = new sm.SourceMapGenerator({ + file: rsm.file, + sourceRoot: rsm.sourceRoot + }); + patches = patches.reverse(); + var currentLine = -1; + var currentLineDiff = 0; + var source = null; + smc.eachMapping(function (m) { + var patch = patches[patches.length - 1]; + var original = { line: m.originalLine, column: m.originalColumn }; + var generated = { line: m.generatedLine, column: m.generatedColumn }; + if (currentLine !== generated.line) { + currentLineDiff = 0; + } + currentLine = generated.line; + generated.column += currentLineDiff; + if (patch && m.generatedLine - 1 === patch.span.end.line && m.generatedColumn === patch.span.end.character) { + var originalLength = patch.span.end.character - patch.span.start.character; + var modifiedLength = patch.content.length; + var lengthDiff = modifiedLength - originalLength; + currentLineDiff += lengthDiff; + generated.column += lengthDiff; + patches.pop(); + } + source = rsm.sourceRoot ? path.relative(rsm.sourceRoot, m.source) : m.source; + source = source.replace(/\\/g, '/'); + smg.addMapping({ source: source, name: m.name, original: original, generated: generated }); + }, null, sm.SourceMapConsumer.GENERATED_ORDER); + if (source) { + smg.setSourceContent(source, smc.sourceContentFor(source)); + } + return JSON.parse(smg.toString()); + } + nls_1.patchSourcemap = patchSourcemap; + function patch(moduleId, typescript, javascript, sourcemap) { + var _a = analyze(typescript), localizeCalls = _a.localizeCalls, nlsExpressions = _a.nlsExpressions; + if (localizeCalls.length === 0) { + return { javascript: javascript, sourcemap: sourcemap }; + } + var nlsKeys = template(localizeCalls.map(function (lc) { return lc.key; })); + var nls = template(localizeCalls.map(function (lc) { return lc.value; })); + var smc = new sm.SourceMapConsumer(sourcemap); + var positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]); + var i = 0; + // build patches + var patches = lazy(localizeCalls) + .map(function (lc) { return ([ + { range: lc.keySpan, content: '' + (i++) }, + { range: lc.valueSpan, content: 'null' } + ]); }) + .flatten() + .map(function (c) { + var start = lcFrom(smc.generatedPositionFor(positionFrom(c.range.start))); + var end = lcFrom(smc.generatedPositionFor(positionFrom(c.range.end))); + return { span: { start: start, end: end }, content: c.content }; + }) + .toArray(); + javascript = patchJavascript(patches, javascript, moduleId); + // since imports are not within the sourcemap information, + // we must do this MacGyver style + if (nlsExpressions.length) { + javascript = javascript.replace(/^define\(.*$/m, function (line) { + return line.replace(/(['"])vs\/nls\1/g, "$1vs/nls!" + moduleId + "$1"); + }); + } + sourcemap = patchSourcemap(patches, sourcemap, smc); + return { javascript: javascript, sourcemap: sourcemap, nlsKeys: nlsKeys, nls: nls }; + } + nls_1.patch = patch; + function patchFiles(javascriptFile, typescript) { + // hack? + var moduleId = javascriptFile.relative + .replace(/\.js$/, '') + .replace(/\\/g, '/'); + var _a = patch(moduleId, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap), javascript = _a.javascript, sourcemap = _a.sourcemap, nlsKeys = _a.nlsKeys, nls = _a.nls; + var result = [fileFrom(javascriptFile, javascript)]; + result[0].sourceMap = sourcemap; + if (nlsKeys) { + result.push(fileFrom(javascriptFile, nlsKeys, javascriptFile.path.replace(/\.js$/, '.nls.keys.js'))); + } + if (nls) { + result.push(fileFrom(javascriptFile, nls, javascriptFile.path.replace(/\.js$/, '.nls.js'))); + } + return result; + } + nls_1.patchFiles = patchFiles; +})(nls || (nls = {})); +module.exports = nls; diff --git a/build/lib/nls.ts b/build/lib/nls.ts new file mode 100644 index 0000000000..122549ab1f --- /dev/null +++ b/build/lib/nls.ts @@ -0,0 +1,469 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as lazy from 'lazy.js'; +import { duplex, through } from 'event-stream'; +import File = require('vinyl'); +import * as sm from 'source-map'; +import assign = require('object-assign'); +import path = require('path'); + +declare class FileSourceMap extends File { + public sourceMap: sm.RawSourceMap; +} + +enum CollectStepResult { + Yes, + YesAndRecurse, + No, + NoAndRecurse +} + +function collect(node: ts.Node, fn: (node: ts.Node) => CollectStepResult): ts.Node[] { + const result: ts.Node[] = []; + + function loop(node: ts.Node) { + var stepResult = fn(node); + + if (stepResult === CollectStepResult.Yes || stepResult === CollectStepResult.YesAndRecurse) { + result.push(node); + } + + if (stepResult === CollectStepResult.YesAndRecurse || stepResult === CollectStepResult.NoAndRecurse) { + ts.forEachChild(node, loop); + } + } + + loop(node); + return result; +} + +function clone(object: T): T { + var result = {}; + for (var id in object) { + result[id] = object[id]; + } + return result; +} + +function template(lines: string[]): string { + let indent = '', wrap = ''; + + if (lines.length > 1) { + indent = '\t'; + wrap = '\n'; + } + + return `/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ +define([], [${ wrap + lines.map(l => indent + l).join(',\n') + wrap}]);`; +} + +/** + * Returns a stream containing the patched JavaScript and source maps. + */ +function nls(): NodeJS.ReadWriteStream { + var input = through(); + var output = input.pipe(through(function (f: FileSourceMap) { + if (!f.sourceMap) { + return this.emit('error', new Error(`File ${f.relative} does not have sourcemaps.`)); + } + + let source = f.sourceMap.sources[0]; + if (!source) { + return this.emit('error', new Error(`File ${f.relative} does not have a source in the source map.`)); + } + + const root = f.sourceMap.sourceRoot; + if (root) { + source = path.join(root, source); + } + + const typescript = f.sourceMap.sourcesContent[0]; + if (!typescript) { + return this.emit('error', new Error(`File ${f.relative} does not have the original content in the source map.`)); + } + + nls.patchFiles(f, typescript).forEach(f => this.emit('data', f)); + })); + + return duplex(input, output); +} + +function isImportNode(node: ts.Node): boolean { + return node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration; +} + +module nls { + + export interface INlsStringResult { + javascript: string; + sourcemap: sm.RawSourceMap; + nls?: string; + nlsKeys?: string; + } + + export interface ISpan { + start: ts.LineAndCharacter; + end: ts.LineAndCharacter; + } + + export interface ILocalizeCall { + keySpan: ISpan; + key: string; + valueSpan: ISpan; + value: string; + } + + export interface ILocalizeAnalysisResult { + localizeCalls: ILocalizeCall[]; + nlsExpressions: ISpan[]; + } + + export interface IPatch { + span: ISpan; + content: string; + } + + export function fileFrom(file: File, contents: string, path: string = file.path) { + return new File({ + contents: new Buffer(contents), + base: file.base, + cwd: file.cwd, + path: path + }); + } + + export function mappedPositionFrom(source: string, lc: ts.LineAndCharacter): sm.MappedPosition { + return { source, line: lc.line + 1, column: lc.character }; + } + + export function lcFrom(position: sm.Position): ts.LineAndCharacter { + return { line: position.line - 1, character: position.column }; + } + + export class SingleFileServiceHost implements ts.LanguageServiceHost { + + private file: ts.IScriptSnapshot; + private lib: ts.IScriptSnapshot; + + constructor(private options: ts.CompilerOptions, private filename: string, contents: string) { + this.file = ts.ScriptSnapshot.fromString(contents); + this.lib = ts.ScriptSnapshot.fromString(''); + } + + getCompilationSettings = () => this.options; + getScriptFileNames = () => [this.filename]; + getScriptVersion = () => '1'; + getScriptSnapshot = (name: string) => name === this.filename ? this.file : this.lib; + getCurrentDirectory = () => ''; + getDefaultLibFileName = () => 'lib.d.ts'; + } + + function isCallExpressionWithinTextSpanCollectStep(textSpan: ts.TextSpan, node: ts.Node): CollectStepResult { + if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) { + return CollectStepResult.No; + } + + return node.kind === ts.SyntaxKind.CallExpression ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse; + } + + export function analyze(contents: string, options: ts.CompilerOptions = {}): ILocalizeAnalysisResult { + const filename = 'file.ts'; + const serviceHost = new SingleFileServiceHost(assign(clone(options), { noResolve: true }), filename, contents); + const service = ts.createLanguageService(serviceHost); + const sourceFile = ts.createSourceFile(filename, contents, ts.ScriptTarget.ES5, true); + + // all imports + const imports = lazy(collect(sourceFile, n => isImportNode(n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse)); + + // import nls = require('vs/nls'); + const importEqualsDeclarations = imports + .filter(n => n.kind === ts.SyntaxKind.ImportEqualsDeclaration) + .map(n => n) + .filter(d => d.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) + .filter(d => (d.moduleReference).expression.getText() === '\'vs/nls\''); + + // import ... from 'vs/nls'; + const importDeclarations = imports + .filter(n => n.kind === ts.SyntaxKind.ImportDeclaration) + .map(n => n) + .filter(d => d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral) + .filter(d => d.moduleSpecifier.getText() === '\'vs/nls\'') + .filter(d => !!d.importClause && !!d.importClause.namedBindings); + + const nlsExpressions = importEqualsDeclarations + .map(d => (d.moduleReference).expression) + .concat(importDeclarations.map(d => d.moduleSpecifier)) + .map(d => ({ + start: ts.getLineAndCharacterOfPosition(sourceFile, d.getStart()), + end: ts.getLineAndCharacterOfPosition(sourceFile, d.getEnd()) + })); + + // `nls.localize(...)` calls + const nlsLocalizeCallExpressions = importDeclarations + .filter(d => d.importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport) + .map(d => (d.importClause.namedBindings).name) + .concat(importEqualsDeclarations.map(d => d.name)) + + // find read-only references to `nls` + .map(n => service.getReferencesAtPosition(filename, n.pos + 1)) + .flatten() + .filter(r => !r.isWriteAccess) + + // find the deepest call expressions AST nodes that contain those references + .map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n))) + .map(a => lazy(a).last()) + .filter(n => !!n) + .map(n => n) + + // only `localize` calls + .filter(n => n.expression.kind === ts.SyntaxKind.PropertyAccessExpression && (n.expression).name.getText() === 'localize'); + + // `localize` named imports + const allLocalizeImportDeclarations = importDeclarations + .filter(d => d.importClause.namedBindings.kind === ts.SyntaxKind.NamedImports) + .map(d => (d.importClause.namedBindings).elements) + .flatten(); + + // `localize` read-only references + const localizeReferences = allLocalizeImportDeclarations + .filter(d => d.name.getText() === 'localize') + .map(n => service.getReferencesAtPosition(filename, n.pos + 1)) + .flatten() + .filter(r => !r.isWriteAccess); + + // custom named `localize` read-only references + const namedLocalizeReferences = allLocalizeImportDeclarations + .filter(d => d.propertyName && d.propertyName.getText() === 'localize') + .map(n => service.getReferencesAtPosition(filename, n.name.pos + 1)) + .flatten() + .filter(r => !r.isWriteAccess); + + // find the deepest call expressions AST nodes that contain those references + const localizeCallExpressions = localizeReferences + .concat(namedLocalizeReferences) + .map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n))) + .map(a => lazy(a).last()) + .filter(n => !!n) + .map(n => n); + + // collect everything + const localizeCalls = nlsLocalizeCallExpressions + .concat(localizeCallExpressions) + .map(e => e.arguments) + .filter(a => a.length > 1) + .sort((a, b) => a[0].getStart() - b[0].getStart()) + .map(a => ({ + keySpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getEnd()) }, + key: a[0].getText(), + valueSpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getEnd()) }, + value: a[1].getText() + })); + + return { + localizeCalls: localizeCalls.toArray(), + nlsExpressions: nlsExpressions.toArray() + }; + } + + export class TextModel { + + private lines: string[]; + private lineEndings: string[]; + + constructor(contents: string) { + const regex = /\r\n|\r|\n/g; + let index = 0; + let match: RegExpExecArray; + + this.lines = []; + this.lineEndings = []; + + while (match = regex.exec(contents)) { + this.lines.push(contents.substring(index, match.index)); + this.lineEndings.push(match[0]); + index = regex.lastIndex; + } + + if (contents.length > 0) { + this.lines.push(contents.substring(index, contents.length)); + this.lineEndings.push(''); + } + } + + public get(index: number): string { + return this.lines[index]; + } + + public set(index: number, line: string): void { + this.lines[index] = line; + } + + public get lineCount(): number { + return this.lines.length; + } + + /** + * Applies patch(es) to the model. + * Multiple patches must be ordered. + * Does not support patches spanning multiple lines. + */ + public apply(patch: IPatch): void { + const startLineNumber = patch.span.start.line; + const endLineNumber = patch.span.end.line; + + const startLine = this.lines[startLineNumber] || ''; + const endLine = this.lines[endLineNumber] || ''; + + this.lines[startLineNumber] = [ + startLine.substring(0, patch.span.start.character), + patch.content, + endLine.substring(patch.span.end.character) + ].join(''); + + for (let i = startLineNumber + 1; i <= endLineNumber; i++) { + this.lines[i] = ''; + } + } + + public toString(): string { + return lazy(this.lines).zip(this.lineEndings) + .flatten().toArray().join(''); + } + } + + export function patchJavascript(patches: IPatch[], contents: string, moduleId: string): string { + const model = new nls.TextModel(contents); + + // patch the localize calls + lazy(patches).reverse().each(p => model.apply(p)); + + // patch the 'vs/nls' imports + const firstLine = model.get(0); + const patchedFirstLine = firstLine.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`); + model.set(0, patchedFirstLine); + + return model.toString(); + } + + export function patchSourcemap(patches: IPatch[], rsm: sm.RawSourceMap, smc: sm.SourceMapConsumer): sm.RawSourceMap { + const smg = new sm.SourceMapGenerator({ + file: rsm.file, + sourceRoot: rsm.sourceRoot + }); + + patches = patches.reverse(); + let currentLine = -1; + let currentLineDiff = 0; + let source = null; + + smc.eachMapping(m => { + const patch = patches[patches.length - 1]; + const original = { line: m.originalLine, column: m.originalColumn }; + const generated = { line: m.generatedLine, column: m.generatedColumn }; + + if (currentLine !== generated.line) { + currentLineDiff = 0; + } + + currentLine = generated.line; + generated.column += currentLineDiff; + + if (patch && m.generatedLine - 1 === patch.span.end.line && m.generatedColumn === patch.span.end.character) { + const originalLength = patch.span.end.character - patch.span.start.character; + const modifiedLength = patch.content.length; + const lengthDiff = modifiedLength - originalLength; + currentLineDiff += lengthDiff; + generated.column += lengthDiff; + + patches.pop(); + } + + source = rsm.sourceRoot ? path.relative(rsm.sourceRoot, m.source) : m.source; + source = source.replace(/\\/g, '/'); + smg.addMapping({ source, name: m.name, original, generated }); + }, null, sm.SourceMapConsumer.GENERATED_ORDER); + + if (source) { + smg.setSourceContent(source, smc.sourceContentFor(source)); + } + + return JSON.parse(smg.toString()); + } + + export function patch(moduleId: string, typescript: string, javascript: string, sourcemap: sm.RawSourceMap): INlsStringResult { + const { localizeCalls, nlsExpressions } = analyze(typescript); + + if (localizeCalls.length === 0) { + return { javascript, sourcemap }; + } + + const nlsKeys = template(localizeCalls.map(lc => lc.key)); + const nls = template(localizeCalls.map(lc => lc.value)); + const smc = new sm.SourceMapConsumer(sourcemap); + const positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]); + let i = 0; + + // build patches + const patches = lazy(localizeCalls) + .map(lc => ([ + { range: lc.keySpan, content: '' + (i++) }, + { range: lc.valueSpan, content: 'null' } + ])) + .flatten() + .map(c => { + const start = lcFrom(smc.generatedPositionFor(positionFrom(c.range.start))); + const end = lcFrom(smc.generatedPositionFor(positionFrom(c.range.end))); + return { span: { start, end }, content: c.content }; + }) + .toArray(); + + javascript = patchJavascript(patches, javascript, moduleId); + + // since imports are not within the sourcemap information, + // we must do this MacGyver style + if (nlsExpressions.length) { + javascript = javascript.replace(/^define\(.*$/m, line => { + return line.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`); + }); + } + + sourcemap = patchSourcemap(patches, sourcemap, smc); + + return { javascript, sourcemap, nlsKeys, nls }; + } + + export function patchFiles(javascriptFile: File, typescript: string): File[] { + // hack? + const moduleId = javascriptFile.relative + .replace(/\.js$/, '') + .replace(/\\/g, '/'); + + const { javascript, sourcemap, nlsKeys, nls } = patch( + moduleId, + typescript, + javascriptFile.contents.toString(), + (javascriptFile).sourceMap + ); + + const result: File[] = [fileFrom(javascriptFile, javascript)]; + (result[0]).sourceMap = sourcemap; + + if (nlsKeys) { + result.push(fileFrom(javascriptFile, nlsKeys, javascriptFile.path.replace(/\.js$/, '.nls.keys.js'))); + } + + if (nls) { + result.push(fileFrom(javascriptFile, nls, javascriptFile.path.replace(/\.js$/, '.nls.js'))); + } + + return result; + } +} + +export = nls; diff --git a/build/lib/optimize.js b/build/lib/optimize.js new file mode 100644 index 0000000000..ca16939925 --- /dev/null +++ b/build/lib/optimize.js @@ -0,0 +1,241 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var path = require("path"); +var gulp = require("gulp"); +var sourcemaps = require("gulp-sourcemaps"); +var filter = require("gulp-filter"); +var minifyCSS = require("gulp-cssnano"); +var uglify = require("gulp-uglify"); +var composer = require("gulp-uglify/composer"); +var uglifyes = require("uglify-es"); +var es = require("event-stream"); +var concat = require("gulp-concat"); +var VinylFile = require("vinyl"); +var bundle = require("./bundle"); +var util = require("./util"); +var i18n = require("./i18n"); +var gulpUtil = require("gulp-util"); +var flatmap = require("gulp-flatmap"); +var pump = require("pump"); +var REPO_ROOT_PATH = path.join(__dirname, '../..'); +function log(prefix, message) { + gulpUtil.log(gulpUtil.colors.cyan('[' + prefix + ']'), message); +} +// {{SQL CARBON EDIT}} +function loaderConfig(emptyPaths) { + var result = { + paths: { + 'vs': 'out-build/vs', + 'sql': 'out-build/sql', + 'vscode': 'empty:' + }, + nodeModules: emptyPaths || [] + }; + result['vs/css'] = { inlineResources: true }; + return result; +} +exports.loaderConfig = loaderConfig; +var IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; +function loader(bundledFileHeader, bundleLoader) { + var sources = [ + 'out-build/vs/loader.js' + ]; + if (bundleLoader) { + sources = sources.concat([ + 'out-build/vs/css.js', + 'out-build/vs/nls.js' + ]); + } + var isFirst = true; + return (gulp + .src(sources, { base: 'out-build' }) + .pipe(es.through(function (data) { + if (isFirst) { + isFirst = false; + this.emit('data', new VinylFile({ + path: 'fake', + base: '', + contents: new Buffer(bundledFileHeader) + })); + this.emit('data', data); + } + else { + this.emit('data', data); + } + })) + .pipe(util.loadSourcemaps()) + .pipe(concat('vs/loader.js')) + .pipe(es.mapSync(function (f) { + f.sourceMap.sourceRoot = util.toFileUri(path.join(REPO_ROOT_PATH, 'src')); + return f; + }))); +} +function toConcatStream(bundledFileHeader, sources, dest) { + var useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); + // If a bundle ends up including in any of the sources our copyright, then + // insert a fake source at the beginning of each bundle with our copyright + var containsOurCopyright = false; + for (var i = 0, len = sources.length; i < len; i++) { + var fileContents = sources[i].contents; + if (IS_OUR_COPYRIGHT_REGEXP.test(fileContents)) { + containsOurCopyright = true; + break; + } + } + if (containsOurCopyright) { + sources.unshift({ + path: null, + contents: bundledFileHeader + }); + } + var treatedSources = sources.map(function (source) { + var root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; + var base = source.path ? root + '/out-build' : ''; + return new VinylFile({ + path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake', + base: base, + contents: new Buffer(source.contents) + }); + }); + return es.readArray(treatedSources) + .pipe(useSourcemaps ? util.loadSourcemaps() : es.through()) + .pipe(concat(dest)); +} +function toBundleStream(bundledFileHeader, bundles) { + return es.merge(bundles.map(function (bundle) { + return toConcatStream(bundledFileHeader, bundle.sources, bundle.dest); + })); +} +function optimizeTask(opts) { + var entryPoints = opts.entryPoints; + var otherSources = opts.otherSources; + var resources = opts.resources; + var loaderConfig = opts.loaderConfig; + var bundledFileHeader = opts.header; + var bundleLoader = (typeof opts.bundleLoader === 'undefined' ? true : opts.bundleLoader); + var out = opts.out; + return function () { + var bundlesStream = es.through(); // this stream will contain the bundled files + var resourcesStream = es.through(); // this stream will contain the resources + var bundleInfoStream = es.through(); // this stream will contain bundleInfo.json + bundle.bundle(entryPoints, loaderConfig, function (err, result) { + if (err) { + return bundlesStream.emit('error', JSON.stringify(err)); + } + toBundleStream(bundledFileHeader, result.files).pipe(bundlesStream); + // Remove css inlined resources + var filteredResources = resources.slice(); + result.cssInlinedResources.forEach(function (resource) { + if (process.env['VSCODE_BUILD_VERBOSE']) { + log('optimizer', 'excluding inlined: ' + resource); + } + filteredResources.push('!' + resource); + }); + gulp.src(filteredResources, { base: 'out-build' }).pipe(resourcesStream); + var bundleInfoArray = []; + if (opts.bundleInfo) { + bundleInfoArray.push(new VinylFile({ + path: 'bundleInfo.json', + base: '.', + contents: new Buffer(JSON.stringify(result.bundleData, null, '\t')) + })); + } + es.readArray(bundleInfoArray).pipe(bundleInfoStream); + }); + var otherSourcesStream = es.through(); + var otherSourcesStreamArr = []; + gulp.src(otherSources, { base: 'out-build' }) + .pipe(es.through(function (data) { + otherSourcesStreamArr.push(toConcatStream(bundledFileHeader, [data], data.relative)); + }, function () { + if (!otherSourcesStreamArr.length) { + setTimeout(function () { otherSourcesStream.emit('end'); }, 0); + } + else { + es.merge(otherSourcesStreamArr).pipe(otherSourcesStream); + } + })); + var result = es.merge(loader(bundledFileHeader, bundleLoader), bundlesStream, otherSourcesStream, resourcesStream, bundleInfoStream); + return result + .pipe(sourcemaps.write('./', { + sourceRoot: null, + addComment: true, + includeContent: true + })) + .pipe(i18n.processNlsFiles({ + fileHeader: bundledFileHeader, + languages: opts.languages + })) + .pipe(gulp.dest(out)); + }; +} +exports.optimizeTask = optimizeTask; +; +/** + * Wrap around uglify and allow the preserveComments function + * to have a file "context" to include our copyright only once per file. + */ +function uglifyWithCopyrights() { + var preserveComments = function (f) { + return function (node, comment) { + var text = comment.value; + var type = comment.type; + if (/@minifier_do_not_preserve/.test(text)) { + return false; + } + var isOurCopyright = IS_OUR_COPYRIGHT_REGEXP.test(text); + if (isOurCopyright) { + if (f.__hasOurCopyright) { + return false; + } + f.__hasOurCopyright = true; + return true; + } + if ('comment2' === type) { + // check for /*!. Note that text doesn't contain leading /* + return (text.length > 0 && text[0] === '!') || /@preserve|license|@cc_on|copyright/i.test(text); + } + else if ('comment1' === type) { + return /license|copyright/i.test(text); + } + return false; + }; + }; + var minify = composer(uglifyes); + var input = es.through(); + var output = input + .pipe(flatmap(function (stream, f) { + return stream.pipe(minify({ + output: { + comments: preserveComments(f), + // linux tfs build agent is crashing, does this help?§ + max_line_len: 3200000 + } + })); + })); + return es.duplex(input, output); +} +function minifyTask(src, sourceMapBaseUrl) { + var sourceMappingURL = sourceMapBaseUrl && (function (f) { return sourceMapBaseUrl + "/" + f.relative + ".map"; }); + return function (cb) { + var jsFilter = filter('**/*.js', { restore: true }); + var cssFilter = filter('**/*.css', { restore: true }); + pump(gulp.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, sourcemaps.init({ loadMaps: true }), uglifyWithCopyrights(), jsFilter.restore, cssFilter, minifyCSS({ reduceIdents: false }), cssFilter.restore, sourcemaps.write('./', { + sourceMappingURL: sourceMappingURL, + sourceRoot: null, + includeContent: true, + addComment: true + }), gulp.dest(src + '-min'), function (err) { + if (err instanceof uglify.GulpUglifyError) { + console.error("Uglify error in '" + (err.cause && err.cause.filename) + "'"); + } + cb(err); + }); + }; +} +exports.minifyTask = minifyTask; +; diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts new file mode 100644 index 0000000000..e708837bd2 --- /dev/null +++ b/build/lib/optimize.ts @@ -0,0 +1,330 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as path from 'path'; +import * as gulp from 'gulp'; +import * as sourcemaps from 'gulp-sourcemaps'; +import * as filter from 'gulp-filter'; +import * as minifyCSS from 'gulp-cssnano'; +import * as uglify from 'gulp-uglify'; +import * as composer from 'gulp-uglify/composer'; +import * as uglifyes from 'uglify-es'; +import * as es from 'event-stream'; +import * as concat from 'gulp-concat'; +import * as VinylFile from 'vinyl'; +import * as bundle from './bundle'; +import * as util from './util'; +import * as i18n from './i18n'; +import * as gulpUtil from 'gulp-util'; +import * as flatmap from 'gulp-flatmap'; +import * as pump from 'pump'; +import * as sm from 'source-map'; + +const REPO_ROOT_PATH = path.join(__dirname, '../..'); + +function log(prefix: string, message: string): void { + gulpUtil.log(gulpUtil.colors.cyan('[' + prefix + ']'), message); +} + +// {{SQL CARBON EDIT}} +export function loaderConfig(emptyPaths: string[]) { + const result = { + paths: { + 'vs': 'out-build/vs', + 'sql': 'out-build/sql', + 'vscode': 'empty:' + }, + nodeModules: emptyPaths || [] + }; + + result['vs/css'] = { inlineResources: true }; + + return result; +} + +const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; + +declare class FileSourceMap extends VinylFile { + public sourceMap: sm.RawSourceMap; +} + +function loader(bundledFileHeader: string, bundleLoader: boolean): NodeJS.ReadWriteStream { + let sources = [ + 'out-build/vs/loader.js' + ]; + if (bundleLoader) { + sources = sources.concat([ + 'out-build/vs/css.js', + 'out-build/vs/nls.js' + ]); + } + + let isFirst = true; + return ( + gulp + .src(sources, { base: 'out-build' }) + .pipe(es.through(function (data) { + if (isFirst) { + isFirst = false; + this.emit('data', new VinylFile({ + path: 'fake', + base: '', + contents: new Buffer(bundledFileHeader) + })); + this.emit('data', data); + } else { + this.emit('data', data); + } + })) + .pipe(util.loadSourcemaps()) + .pipe(concat('vs/loader.js')) + .pipe(es.mapSync(function (f) { + f.sourceMap.sourceRoot = util.toFileUri(path.join(REPO_ROOT_PATH, 'src')); + return f; + })) + ); +} + +function toConcatStream(bundledFileHeader: string, sources: bundle.IFile[], dest: string): NodeJS.ReadWriteStream { + const useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); + + // If a bundle ends up including in any of the sources our copyright, then + // insert a fake source at the beginning of each bundle with our copyright + let containsOurCopyright = false; + for (let i = 0, len = sources.length; i < len; i++) { + const fileContents = sources[i].contents; + if (IS_OUR_COPYRIGHT_REGEXP.test(fileContents)) { + containsOurCopyright = true; + break; + } + } + + if (containsOurCopyright) { + sources.unshift({ + path: null, + contents: bundledFileHeader + }); + } + + const treatedSources = sources.map(function (source) { + const root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; + const base = source.path ? root + '/out-build' : ''; + + return new VinylFile({ + path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake', + base: base, + contents: new Buffer(source.contents) + }); + }); + + return es.readArray(treatedSources) + .pipe(useSourcemaps ? util.loadSourcemaps() : es.through()) + .pipe(concat(dest)); +} + +function toBundleStream(bundledFileHeader: string, bundles: bundle.IConcatFile[]): NodeJS.ReadWriteStream { + return es.merge(bundles.map(function (bundle) { + return toConcatStream(bundledFileHeader, bundle.sources, bundle.dest); + })); +} + +export interface IOptimizeTaskOpts { + /** + * (for AMD files, will get bundled and get Copyright treatment) + */ + entryPoints: bundle.IEntryPoint[]; + /** + * (for non-AMD files that should get Copyright treatment) + */ + otherSources: string[]; + /** + * (svg, etc.) + */ + resources: string[]; + loaderConfig: any; + /** + * (true by default - append css and nls to loader) + */ + bundleLoader?: boolean; + /** + * (basically the Copyright treatment) + */ + header: string; + /** + * (emit bundleInfo.json file) + */ + bundleInfo: boolean; + /** + * (out folder name) + */ + out: string; + /** + * (languages to process) + */ + languages: string[]; +} +export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStream { + const entryPoints = opts.entryPoints; + const otherSources = opts.otherSources; + const resources = opts.resources; + const loaderConfig = opts.loaderConfig; + const bundledFileHeader = opts.header; + const bundleLoader = (typeof opts.bundleLoader === 'undefined' ? true : opts.bundleLoader); + const out = opts.out; + + return function () { + const bundlesStream = es.through(); // this stream will contain the bundled files + const resourcesStream = es.through(); // this stream will contain the resources + const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json + + bundle.bundle(entryPoints, loaderConfig, function (err, result) { + if (err) { return bundlesStream.emit('error', JSON.stringify(err)); } + + toBundleStream(bundledFileHeader, result.files).pipe(bundlesStream); + + // Remove css inlined resources + const filteredResources = resources.slice(); + result.cssInlinedResources.forEach(function (resource) { + if (process.env['VSCODE_BUILD_VERBOSE']) { + log('optimizer', 'excluding inlined: ' + resource); + } + filteredResources.push('!' + resource); + }); + gulp.src(filteredResources, { base: 'out-build' }).pipe(resourcesStream); + + const bundleInfoArray: VinylFile[] = []; + if (opts.bundleInfo) { + bundleInfoArray.push(new VinylFile({ + path: 'bundleInfo.json', + base: '.', + contents: new Buffer(JSON.stringify(result.bundleData, null, '\t')) + })); + } + es.readArray(bundleInfoArray).pipe(bundleInfoStream); + }); + + const otherSourcesStream = es.through(); + const otherSourcesStreamArr: NodeJS.ReadWriteStream[] = []; + + gulp.src(otherSources, { base: 'out-build' }) + .pipe(es.through(function (data) { + otherSourcesStreamArr.push(toConcatStream(bundledFileHeader, [data], data.relative)); + }, function () { + if (!otherSourcesStreamArr.length) { + setTimeout(function () { otherSourcesStream.emit('end'); }, 0); + } else { + es.merge(otherSourcesStreamArr).pipe(otherSourcesStream); + } + })); + + const result = es.merge( + loader(bundledFileHeader, bundleLoader), + bundlesStream, + otherSourcesStream, + resourcesStream, + bundleInfoStream + ); + + return result + .pipe(sourcemaps.write('./', { + sourceRoot: null, + addComment: true, + includeContent: true + })) + .pipe(i18n.processNlsFiles({ + fileHeader: bundledFileHeader, + languages: opts.languages + })) + .pipe(gulp.dest(out)); + }; +}; + +declare class FileWithCopyright extends VinylFile { + public __hasOurCopyright: boolean; +} +/** + * Wrap around uglify and allow the preserveComments function + * to have a file "context" to include our copyright only once per file. + */ +function uglifyWithCopyrights(): NodeJS.ReadWriteStream { + const preserveComments = (f: FileWithCopyright) => { + return (node, comment: { value: string; type: string; }) => { + const text = comment.value; + const type = comment.type; + + if (/@minifier_do_not_preserve/.test(text)) { + return false; + } + + const isOurCopyright = IS_OUR_COPYRIGHT_REGEXP.test(text); + + if (isOurCopyright) { + if (f.__hasOurCopyright) { + return false; + } + f.__hasOurCopyright = true; + return true; + } + + if ('comment2' === type) { + // check for /*!. Note that text doesn't contain leading /* + return (text.length > 0 && text[0] === '!') || /@preserve|license|@cc_on|copyright/i.test(text); + } else if ('comment1' === type) { + return /license|copyright/i.test(text); + } + return false; + }; + }; + + const minify = composer(uglifyes); + const input = es.through(); + const output = input + .pipe(flatmap((stream, f) => { + return stream.pipe(minify({ + output: { + comments: preserveComments(f), + // linux tfs build agent is crashing, does this help?§ + max_line_len: 3200000 + } + })); + })); + + return es.duplex(input, output); +} + +export function minifyTask(src: string, sourceMapBaseUrl: string): (cb: any) => void { + const sourceMappingURL = sourceMapBaseUrl && (f => `${sourceMapBaseUrl}/${f.relative}.map`); + + return cb => { + const jsFilter = filter('**/*.js', { restore: true }); + const cssFilter = filter('**/*.css', { restore: true }); + + pump( + gulp.src([src + '/**', '!' + src + '/**/*.map']), + jsFilter, + sourcemaps.init({ loadMaps: true }), + uglifyWithCopyrights(), + jsFilter.restore, + cssFilter, + minifyCSS({ reduceIdents: false }), + cssFilter.restore, + sourcemaps.write('./', { + sourceMappingURL, + sourceRoot: null, + includeContent: true, + addComment: true + }), + gulp.dest(src + '-min') + , (err: any) => { + if (err instanceof uglify.GulpUglifyError) { + console.error(`Uglify error in '${err.cause && err.cause.filename}'`); + } + + cb(err); + }); + }; +}; diff --git a/build/lib/reporter.js b/build/lib/reporter.js new file mode 100644 index 0000000000..a23977b3fc --- /dev/null +++ b/build/lib/reporter.js @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var es = require("event-stream"); +var _ = require("underscore"); +var util = require("gulp-util"); +var fs = require("fs"); +var path = require("path"); +var allErrors = []; +var startTime = null; +var count = 0; +function onStart() { + if (count++ > 0) { + return; + } + startTime = new Date().getTime(); + util.log("Starting " + util.colors.green('compilation') + "..."); +} +function onEnd() { + if (--count > 0) { + return; + } + log(); +} +var buildLogPath = path.join(path.dirname(path.dirname(__dirname)), '.build', 'log'); +try { + fs.mkdirSync(path.dirname(buildLogPath)); +} +catch (err) { + // ignore +} +function log() { + var errors = _.flatten(allErrors); + errors.map(function (err) { return util.log(util.colors.red('Error') + ": " + err); }); + var regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/; + var messages = errors + .map(function (err) { return regex.exec(err); }) + .filter(function (match) { return !!match; }) + .map(function (_a) { + var path = _a[1], line = _a[2], column = _a[3], message = _a[4]; + return ({ path: path, line: parseInt(line), column: parseInt(column), message: message }); + }); + try { + fs.writeFileSync(buildLogPath, JSON.stringify(messages)); + } + catch (err) { + //noop + } + util.log("Finished " + util.colors.green('compilation') + " with " + errors.length + " errors after " + util.colors.magenta((new Date().getTime() - startTime) + ' ms')); +} +function createReporter() { + var errors = []; + allErrors.push(errors); + var ReportFunc = (function () { + function ReportFunc(err) { + errors.push(err); + } + ReportFunc.hasErrors = function () { + return errors.length > 0; + }; + ReportFunc.end = function (emitError) { + errors.length = 0; + onStart(); + return es.through(null, function () { + onEnd(); + if (emitError && errors.length > 0) { + log(); + this.emit('error'); + } + else { + this.emit('end'); + } + }); + }; + return ReportFunc; + }()); + return ReportFunc; +} +exports.createReporter = createReporter; +; diff --git a/build/lib/reporter.ts b/build/lib/reporter.ts new file mode 100644 index 0000000000..6be780fcc2 --- /dev/null +++ b/build/lib/reporter.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as es from 'event-stream'; +import * as _ from 'underscore'; +import * as util from 'gulp-util'; +import * as fs from 'fs'; +import * as path from 'path'; + +const allErrors: Error[][] = []; +let startTime: number = null; +let count = 0; + +function onStart(): void { + if (count++ > 0) { + return; + } + + startTime = new Date().getTime(); + util.log(`Starting ${util.colors.green('compilation')}...`); +} + +function onEnd(): void { + if (--count > 0) { + return; + } + + log(); +} + +const buildLogPath = path.join(path.dirname(path.dirname(__dirname)), '.build', 'log'); + +try { + fs.mkdirSync(path.dirname(buildLogPath)); +} catch (err) { + // ignore +} + +function log(): void { + const errors = _.flatten(allErrors); + errors.map(err => util.log(`${util.colors.red('Error')}: ${err}`)); + + const regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/; + const messages = errors + .map(err => regex.exec(err)) + .filter(match => !!match) + .map(([, path, line, column, message]) => ({ path, line: parseInt(line), column: parseInt(column), message })); + + try { + + fs.writeFileSync(buildLogPath, JSON.stringify(messages)); + } catch (err) { + //noop + } + + util.log(`Finished ${util.colors.green('compilation')} with ${errors.length} errors after ${util.colors.magenta((new Date().getTime() - startTime) + ' ms')}`); +} + +export interface IReporter { + (err: Error): void; + hasErrors(): boolean; + end(emitError: boolean): NodeJS.ReadWriteStream; +} + +export function createReporter(): IReporter { + const errors: Error[] = []; + allErrors.push(errors); + + class ReportFunc { + constructor(err: Error) { + errors.push(err); + } + + static hasErrors(): boolean { + return errors.length > 0; + } + + static end(emitError: boolean): NodeJS.ReadWriteStream { + errors.length = 0; + onStart(); + + return es.through(null, function () { + onEnd(); + + if (emitError && errors.length > 0) { + log(); + this.emit('error'); + } else { + this.emit('end'); + } + }); + } + } + + return ReportFunc; +}; diff --git a/build/lib/snapshotLoader.js b/build/lib/snapshotLoader.js new file mode 100644 index 0000000000..aef69e7d4c --- /dev/null +++ b/build/lib/snapshotLoader.js @@ -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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +var snaps; +(function (snaps) { + var fs = require('fs'); + var path = require('path'); + var os = require('os'); + var cp = require('child_process'); + var mksnapshot = path.join(__dirname, "../../node_modules/.bin/" + (process.platform === 'win32' ? 'mksnapshot.cmd' : 'mksnapshot')); + var product = require('../../product.json'); + var arch = (process.argv.join('').match(/--arch=(.*)/) || [])[1]; + // + var loaderFilepath; + var startupBlobFilepath; + switch (process.platform) { + case 'darwin': + loaderFilepath = "VSCode-darwin/" + product.nameLong + ".app/Contents/Resources/app/out/vs/loader.js"; + startupBlobFilepath = "VSCode-darwin/" + product.nameLong + ".app/Contents/Frameworks/Electron Framework.framework/Resources/snapshot_blob.bin"; + break; + case 'win32': + case 'linux': + loaderFilepath = "VSCode-" + process.platform + "-" + arch + "/resources/app/out/vs/loader.js"; + startupBlobFilepath = "VSCode-" + process.platform + "-" + arch + "/snapshot_blob.bin"; + } + loaderFilepath = path.join(__dirname, '../../../', loaderFilepath); + startupBlobFilepath = path.join(__dirname, '../../../', startupBlobFilepath); + snapshotLoader(loaderFilepath, startupBlobFilepath); + function snapshotLoader(loaderFilepath, startupBlobFilepath) { + var inputFile = fs.readFileSync(loaderFilepath); + var wrappedInputFile = "\n\t\tvar Monaco_Loader_Init;\n\t\t(function() {\n\t\t\tvar doNotInitLoader = true;\n\t\t\t" + inputFile.toString() + ";\n\t\t\tMonaco_Loader_Init = function() {\n\t\t\t\tAMDLoader.init();\n\t\t\t\tCSSLoaderPlugin.init();\n\t\t\t\tNLSLoaderPlugin.init();\n\n\t\t\t\treturn { define, require };\n\t\t\t}\n\t\t})();\n\t\t"; + var wrappedInputFilepath = path.join(os.tmpdir(), 'wrapped-loader.js'); + console.log(wrappedInputFilepath); + fs.writeFileSync(wrappedInputFilepath, wrappedInputFile); + cp.execFileSync(mksnapshot, [wrappedInputFilepath, "--startup_blob", startupBlobFilepath]); + } +})(snaps || (snaps = {})); diff --git a/build/lib/snapshotLoader.ts b/build/lib/snapshotLoader.ts new file mode 100644 index 0000000000..79e23347ee --- /dev/null +++ b/build/lib/snapshotLoader.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +namespace snaps { + + const fs = require('fs'); + const path = require('path'); + const os = require('os'); + const cp = require('child_process'); + + const mksnapshot = path.join(__dirname, `../../node_modules/.bin/${process.platform === 'win32' ? 'mksnapshot.cmd' : 'mksnapshot'}`); + const product = require('../../product.json'); + const arch = (process.argv.join('').match(/--arch=(.*)/) || [])[1]; + + // + let loaderFilepath: string; + let startupBlobFilepath: string; + + switch (process.platform) { + case 'darwin': + loaderFilepath = `VSCode-darwin/${product.nameLong}.app/Contents/Resources/app/out/vs/loader.js`; + startupBlobFilepath = `VSCode-darwin/${product.nameLong}.app/Contents/Frameworks/Electron Framework.framework/Resources/snapshot_blob.bin`; + break; + + case 'win32': + case 'linux': + loaderFilepath = `VSCode-${process.platform}-${arch}/resources/app/out/vs/loader.js`; + startupBlobFilepath = `VSCode-${process.platform}-${arch}/snapshot_blob.bin`; + } + + loaderFilepath = path.join(__dirname, '../../../', loaderFilepath); + startupBlobFilepath = path.join(__dirname, '../../../', startupBlobFilepath); + + snapshotLoader(loaderFilepath, startupBlobFilepath); + + function snapshotLoader(loaderFilepath: string, startupBlobFilepath: string): void { + + const inputFile = fs.readFileSync(loaderFilepath); + const wrappedInputFile = ` + var Monaco_Loader_Init; + (function() { + var doNotInitLoader = true; + ${inputFile.toString()}; + Monaco_Loader_Init = function() { + AMDLoader.init(); + CSSLoaderPlugin.init(); + NLSLoaderPlugin.init(); + + return { define, require }; + } + })(); + `; + const wrappedInputFilepath = path.join(os.tmpdir(), 'wrapped-loader.js'); + console.log(wrappedInputFilepath); + fs.writeFileSync(wrappedInputFilepath, wrappedInputFile); + + cp.execFileSync(mksnapshot, [wrappedInputFilepath, `--startup_blob`, startupBlobFilepath]); + } +} diff --git a/build/lib/test/i18n.test.js b/build/lib/test/i18n.test.js new file mode 100644 index 0000000000..318372b0ba --- /dev/null +++ b/build/lib/test/i18n.test.js @@ -0,0 +1,40 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +var assert = require("assert"); +var i18n = require("../i18n"); +suite('XLF Parser Tests', function () { + var sampleXlf = 'Key #1Key #2 &'; + var sampleTranslatedXlf = 'Key #1Кнопка #1Key #2 &Кнопка #2 &'; + var originalFilePath = 'vs/base/common/keybinding'; + var keys = ['key1', 'key2']; + var messages = ['Key #1', 'Key #2 &']; + var translatedMessages = { key1: 'Кнопка #1', key2: 'Кнопка #2 &' }; + test('Keys & messages to XLF conversion', function () { + var xlf = new i18n.XLF('vscode-workbench'); + xlf.addFile(originalFilePath, keys, messages); + var xlfString = xlf.toString(); + assert.strictEqual(xlfString.replace(/\s{2,}/g, ''), sampleXlf); + }); + test('XLF to keys & messages conversion', function () { + i18n.XLF.parse(sampleTranslatedXlf).then(function (resolvedFiles) { + assert.deepEqual(resolvedFiles[0].messages, translatedMessages); + assert.strictEqual(resolvedFiles[0].originalFilePath, originalFilePath); + }); + }); + test('JSON file source path to Transifex resource match', function () { + var editorProject = 'vscode-editor', workbenchProject = 'vscode-workbench'; + var platform = { name: 'vs/platform', project: editorProject }, editorContrib = { name: 'vs/editor/contrib', project: editorProject }, editor = { name: 'vs/editor', project: editorProject }, base = { name: 'vs/base', project: editorProject }, code = { name: 'vs/code', project: workbenchProject }, workbenchParts = { name: 'vs/workbench/parts/html', project: workbenchProject }, workbenchServices = { name: 'vs/workbench/services/files', project: workbenchProject }, workbench = { name: 'vs/workbench', project: workbenchProject }; + assert.deepEqual(i18n.getResource('vs/platform/actions/browser/menusExtensionPoint'), platform); + assert.deepEqual(i18n.getResource('vs/editor/contrib/clipboard/browser/clipboard'), editorContrib); + assert.deepEqual(i18n.getResource('vs/editor/common/modes/modesRegistry'), editor); + assert.deepEqual(i18n.getResource('vs/base/common/errorMessage'), base); + assert.deepEqual(i18n.getResource('vs/code/electron-main/window'), code); + assert.deepEqual(i18n.getResource('vs/workbench/parts/html/browser/webview'), workbenchParts); + assert.deepEqual(i18n.getResource('vs/workbench/services/files/node/fileService'), workbenchServices); + assert.deepEqual(i18n.getResource('vs/workbench/browser/parts/panel/panelActions'), workbench); + }); +}); diff --git a/build/lib/test/i18n.test.ts b/build/lib/test/i18n.test.ts new file mode 100644 index 0000000000..03cdb7752e --- /dev/null +++ b/build/lib/test/i18n.test.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert = require('assert'); +import i18n = require('../i18n'); + +suite('XLF Parser Tests', () => { + const sampleXlf = 'Key #1Key #2 &'; + const sampleTranslatedXlf = 'Key #1Кнопка #1Key #2 &Кнопка #2 &'; + const originalFilePath = 'vs/base/common/keybinding'; + const keys = ['key1', 'key2']; + const messages = ['Key #1', 'Key #2 &']; + const translatedMessages = { key1: 'Кнопка #1', key2: 'Кнопка #2 &' }; + + test('Keys & messages to XLF conversion', () => { + let xlf = new i18n.XLF('vscode-workbench'); + xlf.addFile(originalFilePath, keys, messages); + const xlfString = xlf.toString(); + + assert.strictEqual(xlfString.replace(/\s{2,}/g, ''), sampleXlf); + }); + + test('XLF to keys & messages conversion', () => { + i18n.XLF.parse(sampleTranslatedXlf).then(function(resolvedFiles) { + assert.deepEqual(resolvedFiles[0].messages, translatedMessages); + assert.strictEqual(resolvedFiles[0].originalFilePath, originalFilePath); + }); + }); + + test('JSON file source path to Transifex resource match', () => { + const editorProject: string = 'vscode-editor', + workbenchProject: string = 'vscode-workbench'; + + const platform: i18n.Resource = { name: 'vs/platform', project: editorProject }, + editorContrib = { name: 'vs/editor/contrib', project: editorProject }, + editor = { name: 'vs/editor', project: editorProject }, + base = { name: 'vs/base', project: editorProject }, + code = { name: 'vs/code', project: workbenchProject }, + workbenchParts = { name: 'vs/workbench/parts/html', project: workbenchProject }, + workbenchServices = { name: 'vs/workbench/services/files', project: workbenchProject }, + workbench = { name: 'vs/workbench', project: workbenchProject}; + + assert.deepEqual(i18n.getResource('vs/platform/actions/browser/menusExtensionPoint'), platform); + assert.deepEqual(i18n.getResource('vs/editor/contrib/clipboard/browser/clipboard'), editorContrib); + assert.deepEqual(i18n.getResource('vs/editor/common/modes/modesRegistry'), editor); + assert.deepEqual(i18n.getResource('vs/base/common/errorMessage'), base); + assert.deepEqual(i18n.getResource('vs/code/electron-main/window'), code); + assert.deepEqual(i18n.getResource('vs/workbench/parts/html/browser/webview'), workbenchParts); + assert.deepEqual(i18n.getResource('vs/workbench/services/files/node/fileService'), workbenchServices); + assert.deepEqual(i18n.getResource('vs/workbench/browser/parts/panel/panelActions'), workbench); + }); +}); \ No newline at end of file diff --git a/build/lib/tslint/allowAsyncRule.js b/build/lib/tslint/allowAsyncRule.js new file mode 100644 index 0000000000..9d36af1a4b --- /dev/null +++ b/build/lib/tslint/allowAsyncRule.js @@ -0,0 +1,59 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ts = require("typescript"); +var Lint = require("tslint"); +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + var allowed = this.getOptions().ruleArguments[0]; + return this.applyWithWalker(new AsyncRuleWalker(sourceFile, this.getOptions(), allowed)); + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +var AsyncRuleWalker = (function (_super) { + __extends(AsyncRuleWalker, _super); + function AsyncRuleWalker(file, opts, allowed) { + var _this = _super.call(this, file, opts) || this; + _this.allowed = allowed; + return _this; + } + AsyncRuleWalker.prototype.visitMethodDeclaration = function (node) { + this.visitFunctionLikeDeclaration(node); + }; + AsyncRuleWalker.prototype.visitFunctionDeclaration = function (node) { + this.visitFunctionLikeDeclaration(node); + }; + AsyncRuleWalker.prototype.visitFunctionLikeDeclaration = function (node) { + var _this = this; + var flags = ts.getCombinedModifierFlags(node); + if (!(flags & ts.ModifierFlags.Async)) { + return; + } + var path = node.getSourceFile().path; + var pathParts = path.split(/\\|\//); + if (pathParts.some(function (part) { return _this.allowed.some(function (allowed) { return part === allowed; }); })) { + return; + } + var message = "You are not allowed to use async function in this layer. Allowed layers are: [" + this.allowed + "]"; + this.addFailureAtNode(node, message); + }; + return AsyncRuleWalker; +}(Lint.RuleWalker)); diff --git a/build/lib/tslint/allowAsyncRule.ts b/build/lib/tslint/allowAsyncRule.ts new file mode 100644 index 0000000000..3aa4f888ca --- /dev/null +++ b/build/lib/tslint/allowAsyncRule.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; + +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + const allowed = this.getOptions().ruleArguments[0] as string[]; + return this.applyWithWalker(new AsyncRuleWalker(sourceFile, this.getOptions(), allowed)); + } +} + +class AsyncRuleWalker extends Lint.RuleWalker { + + constructor(file: ts.SourceFile, opts: Lint.IOptions, private allowed: string[]) { + super(file, opts); + } + + protected visitMethodDeclaration(node: ts.MethodDeclaration): void { + this.visitFunctionLikeDeclaration(node); + } + + protected visitFunctionDeclaration(node: ts.FunctionDeclaration): void { + this.visitFunctionLikeDeclaration(node); + } + + private visitFunctionLikeDeclaration(node: ts.FunctionLikeDeclaration) { + const flags = ts.getCombinedModifierFlags(node); + + if (!(flags & ts.ModifierFlags.Async)) { + return; + } + + const path = (node.getSourceFile() as any).path; + const pathParts = path.split(/\\|\//); + + if (pathParts.some(part => this.allowed.some(allowed => part === allowed))) { + return; + } + + const message = `You are not allowed to use async function in this layer. Allowed layers are: [${this.allowed}]`; + this.addFailureAtNode(node, message); + } +} diff --git a/build/lib/tslint/duplicateImportsRule.js b/build/lib/tslint/duplicateImportsRule.js new file mode 100644 index 0000000000..3e01fef99b --- /dev/null +++ b/build/lib/tslint/duplicateImportsRule.js @@ -0,0 +1,50 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var path_1 = require("path"); +var Lint = require("tslint"); +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions())); + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +var ImportPatterns = (function (_super) { + __extends(ImportPatterns, _super); + function ImportPatterns(file, opts) { + var _this = _super.call(this, file, opts) || this; + _this.imports = Object.create(null); + return _this; + } + ImportPatterns.prototype.visitImportDeclaration = function (node) { + var path = node.moduleSpecifier.getText(); + // remove quotes + path = path.slice(1, -1); + if (path[0] === '.') { + path = path_1.join(path_1.dirname(node.getSourceFile().fileName), path); + } + if (this.imports[path]) { + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), "Duplicate imports for '" + path + "'.")); + } + this.imports[path] = true; + }; + return ImportPatterns; +}(Lint.RuleWalker)); diff --git a/build/lib/tslint/duplicateImportsRule.ts b/build/lib/tslint/duplicateImportsRule.ts new file mode 100644 index 0000000000..8c7b75fe3b --- /dev/null +++ b/build/lib/tslint/duplicateImportsRule.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 * as ts from 'typescript'; +import { join, dirname } from 'path'; +import * as Lint from 'tslint'; + +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions())); + } +} + +class ImportPatterns extends Lint.RuleWalker { + + private imports: { [path: string]: boolean; } = Object.create(null); + + constructor(file: ts.SourceFile, opts: Lint.IOptions) { + super(file, opts); + } + + protected visitImportDeclaration(node: ts.ImportDeclaration): void { + let path = node.moduleSpecifier.getText(); + + // remove quotes + path = path.slice(1, -1); + + if (path[0] === '.') { + path = join(dirname(node.getSourceFile().fileName), path); + } + + if (this.imports[path]) { + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Duplicate imports for '${path}'.`)); + } + + this.imports[path] = true; + } +} diff --git a/build/lib/tslint/importPatternsRule.js b/build/lib/tslint/importPatternsRule.js new file mode 100644 index 0000000000..7798870a3f --- /dev/null +++ b/build/lib/tslint/importPatternsRule.js @@ -0,0 +1,90 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ts = require("typescript"); +var Lint = require("tslint"); +var minimatch = require("minimatch"); +var path_1 = require("path"); +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + var configs = this.getOptions().ruleArguments; + for (var _i = 0, configs_1 = configs; _i < configs_1.length; _i++) { + var config = configs_1[_i]; + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions(), config)); + } + } + return []; + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +var ImportPatterns = (function (_super) { + __extends(ImportPatterns, _super); + function ImportPatterns(file, opts, _config) { + var _this = _super.call(this, file, opts) || this; + _this._config = _config; + return _this; + } + ImportPatterns.prototype.visitImportEqualsDeclaration = function (node) { + if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { + this._validateImport(node.moduleReference.expression.getText(), node); + } + }; + ImportPatterns.prototype.visitImportDeclaration = function (node) { + this._validateImport(node.moduleSpecifier.getText(), node); + }; + ImportPatterns.prototype.visitCallExpression = function (node) { + _super.prototype.visitCallExpression.call(this, node); + // import('foo') statements inside the code + if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { + var path = node.arguments[0]; + this._validateImport(path.getText(), node); + } + }; + ImportPatterns.prototype._validateImport = function (path, node) { + // remove quotes + path = path.slice(1, -1); + // resolve relative paths + if (path[0] === '.') { + path = path_1.join(this.getSourceFile().fileName, path); + } + var restrictions; + if (typeof this._config.restrictions === 'string') { + restrictions = [this._config.restrictions]; + } + else { + restrictions = this._config.restrictions; + } + var matched = false; + for (var _i = 0, restrictions_1 = restrictions; _i < restrictions_1.length; _i++) { + var pattern = restrictions_1[_i]; + if (minimatch(path, pattern)) { + matched = true; + break; + } + } + if (!matched) { + // None of the restrictions matched + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), "Imports violates '" + restrictions.join(' or ') + "' restrictions. See https://github.com/Microsoft/vscode/wiki/Code-Organization")); + } + }; + return ImportPatterns; +}(Lint.RuleWalker)); diff --git a/build/lib/tslint/importPatternsRule.ts b/build/lib/tslint/importPatternsRule.ts new file mode 100644 index 0000000000..490e119ae5 --- /dev/null +++ b/build/lib/tslint/importPatternsRule.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; +import * as minimatch from 'minimatch'; +import { join } from 'path'; + +interface ImportPatternsConfig { + target: string; + restrictions: string | string[]; +} + +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + + const configs = this.getOptions().ruleArguments; + + + for (const config of configs) { + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions(), config)); + } + } + + return []; + } +} + +class ImportPatterns extends Lint.RuleWalker { + + constructor(file: ts.SourceFile, opts: Lint.IOptions, private _config: ImportPatternsConfig) { + super(file, opts); + } + + protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void { + if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { + this._validateImport(node.moduleReference.expression.getText(), node); + } + } + + protected visitImportDeclaration(node: ts.ImportDeclaration): void { + this._validateImport(node.moduleSpecifier.getText(), node); + } + + protected visitCallExpression(node: ts.CallExpression): void { + super.visitCallExpression(node); + + // import('foo') statements inside the code + if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { + const [path] = node.arguments; + this._validateImport(path.getText(), node); + } + } + + private _validateImport(path: string, node: ts.Node): void { + // remove quotes + path = path.slice(1, -1); + + // resolve relative paths + if (path[0] === '.') { + path = join(this.getSourceFile().fileName, path); + } + + let restrictions: string[]; + if (typeof this._config.restrictions === 'string') { + restrictions = [this._config.restrictions]; + } else { + restrictions = this._config.restrictions; + } + + let matched = false; + for (const pattern of restrictions) { + if (minimatch(path, pattern)) { + matched = true; + break; + } + } + + if (!matched) { + // None of the restrictions matched + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Imports violates '${restrictions.join(' or ')}' restrictions. See https://github.com/Microsoft/vscode/wiki/Code-Organization`)); + } + } +} diff --git a/build/lib/tslint/layeringRule.js b/build/lib/tslint/layeringRule.js new file mode 100644 index 0000000000..e5390a6cc5 --- /dev/null +++ b/build/lib/tslint/layeringRule.js @@ -0,0 +1,101 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ts = require("typescript"); +var Lint = require("tslint"); +var path_1 = require("path"); +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + var parts = path_1.dirname(sourceFile.fileName).split(/\\|\//); + var ruleArgs = this.getOptions().ruleArguments[0]; + var config; + for (var i = parts.length - 1; i >= 0; i--) { + if (ruleArgs[parts[i]]) { + config = { + allowed: new Set(ruleArgs[parts[i]]).add(parts[i]), + disallowed: new Set() + }; + Object.keys(ruleArgs).forEach(function (key) { + if (!config.allowed.has(key)) { + config.disallowed.add(key); + } + }); + break; + } + } + if (!config) { + return []; + } + return this.applyWithWalker(new LayeringRule(sourceFile, config, this.getOptions())); + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +var LayeringRule = (function (_super) { + __extends(LayeringRule, _super); + function LayeringRule(file, config, opts) { + var _this = _super.call(this, file, opts) || this; + _this._config = config; + return _this; + } + LayeringRule.prototype.visitImportEqualsDeclaration = function (node) { + if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { + this._validateImport(node.moduleReference.expression.getText(), node); + } + }; + LayeringRule.prototype.visitImportDeclaration = function (node) { + this._validateImport(node.moduleSpecifier.getText(), node); + }; + LayeringRule.prototype.visitCallExpression = function (node) { + _super.prototype.visitCallExpression.call(this, node); + // import('foo') statements inside the code + if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { + var path = node.arguments[0]; + this._validateImport(path.getText(), node); + } + }; + LayeringRule.prototype._validateImport = function (path, node) { + // remove quotes + path = path.slice(1, -1); + if (path[0] === '.') { + path = path_1.join(path_1.dirname(node.getSourceFile().fileName), path); + } + var parts = path_1.dirname(path).split(/\\|\//); + for (var i = parts.length - 1; i >= 0; i--) { + var part = parts[i]; + if (this._config.allowed.has(part)) { + // GOOD - same layer + return; + } + if (this._config.disallowed.has(part)) { + // BAD - wrong layer + var message = "Bad layering. You are not allowed to access '" + part + "' from here, allowed layers are: [" + LayeringRule._print(this._config.allowed) + "]"; + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); + return; + } + } + }; + LayeringRule._print = function (set) { + var r = []; + set.forEach(function (e) { return r.push(e); }); + return r.join(', '); + }; + return LayeringRule; +}(Lint.RuleWalker)); diff --git a/build/lib/tslint/layeringRule.ts b/build/lib/tslint/layeringRule.ts new file mode 100644 index 0000000000..ddd1245f08 --- /dev/null +++ b/build/lib/tslint/layeringRule.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; +import { join, dirname } from 'path'; + +interface Config { + allowed: Set; + disallowed: Set; +} + +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + + const parts = dirname(sourceFile.fileName).split(/\\|\//); + let ruleArgs = this.getOptions().ruleArguments[0]; + + let config: Config; + for (let i = parts.length - 1; i >= 0; i--) { + if (ruleArgs[parts[i]]) { + config = { + allowed: new Set(ruleArgs[parts[i]]).add(parts[i]), + disallowed: new Set() + }; + Object.keys(ruleArgs).forEach(key => { + if (!config.allowed.has(key)) { + config.disallowed.add(key); + } + }); + break; + } + } + + if (!config) { + return []; + } + + return this.applyWithWalker(new LayeringRule(sourceFile, config, this.getOptions())); + } +} + +class LayeringRule extends Lint.RuleWalker { + + private _config: Config; + + constructor(file: ts.SourceFile, config: Config, opts: Lint.IOptions) { + super(file, opts); + this._config = config; + } + + protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void { + if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { + this._validateImport(node.moduleReference.expression.getText(), node); + } + } + + protected visitImportDeclaration(node: ts.ImportDeclaration): void { + this._validateImport(node.moduleSpecifier.getText(), node); + } + + protected visitCallExpression(node: ts.CallExpression): void { + super.visitCallExpression(node); + + // import('foo') statements inside the code + if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { + const [path] = node.arguments; + this._validateImport(path.getText(), node); + } + } + + private _validateImport(path: string, node: ts.Node): void { + // remove quotes + path = path.slice(1, -1); + + if (path[0] === '.') { + path = join(dirname(node.getSourceFile().fileName), path); + } + + const parts = dirname(path).split(/\\|\//); + for (let i = parts.length - 1; i >= 0; i--) { + const part = parts[i]; + + if (this._config.allowed.has(part)) { + // GOOD - same layer + return; + } + + if (this._config.disallowed.has(part)) { + // BAD - wrong layer + const message = `Bad layering. You are not allowed to access '${part}' from here, allowed layers are: [${LayeringRule._print(this._config.allowed)}]`; + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); + return; + } + } + } + + static _print(set: Set): string { + let r: string[] = []; + set.forEach(e => r.push(e)); + return r.join(', '); + } +} diff --git a/build/lib/tslint/noUnexternalizedStringsRule.js b/build/lib/tslint/noUnexternalizedStringsRule.js new file mode 100644 index 0000000000..351ecfb676 --- /dev/null +++ b/build/lib/tslint/noUnexternalizedStringsRule.js @@ -0,0 +1,182 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ts = require("typescript"); +var Lint = require("tslint"); +/** + * Implementation of the no-unexternalized-strings rule. + */ +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + return this.applyWithWalker(new NoUnexternalizedStringsRuleWalker(sourceFile, this.getOptions())); + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +function isStringLiteral(node) { + return node && node.kind === ts.SyntaxKind.StringLiteral; +} +function isObjectLiteral(node) { + return node && node.kind === ts.SyntaxKind.ObjectLiteralExpression; +} +function isPropertyAssignment(node) { + return node && node.kind === ts.SyntaxKind.PropertyAssignment; +} +var NoUnexternalizedStringsRuleWalker = (function (_super) { + __extends(NoUnexternalizedStringsRuleWalker, _super); + function NoUnexternalizedStringsRuleWalker(file, opts) { + var _this = _super.call(this, file, opts) || this; + _this.signatures = Object.create(null); + _this.ignores = Object.create(null); + _this.messageIndex = undefined; + _this.keyIndex = undefined; + _this.usedKeys = Object.create(null); + var options = _this.getOptions(); + var first = options && options.length > 0 ? options[0] : null; + if (first) { + if (Array.isArray(first.signatures)) { + first.signatures.forEach(function (signature) { return _this.signatures[signature] = true; }); + } + if (Array.isArray(first.ignores)) { + first.ignores.forEach(function (ignore) { return _this.ignores[ignore] = true; }); + } + if (typeof first.messageIndex !== 'undefined') { + _this.messageIndex = first.messageIndex; + } + if (typeof first.keyIndex !== 'undefined') { + _this.keyIndex = first.keyIndex; + } + } + return _this; + } + NoUnexternalizedStringsRuleWalker.prototype.visitSourceFile = function (node) { + var _this = this; + _super.prototype.visitSourceFile.call(this, node); + Object.keys(this.usedKeys).forEach(function (key) { + var occurrences = _this.usedKeys[key]; + if (occurrences.length > 1) { + occurrences.forEach(function (occurrence) { + _this.addFailure((_this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), "Duplicate key " + occurrence.key.getText() + " with different message value."))); + }); + } + }); + }; + NoUnexternalizedStringsRuleWalker.prototype.visitStringLiteral = function (node) { + this.checkStringLiteral(node); + _super.prototype.visitStringLiteral.call(this, node); + }; + NoUnexternalizedStringsRuleWalker.prototype.checkStringLiteral = function (node) { + var text = node.getText(); + var doubleQuoted = text.length >= 2 && text[0] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE && text[text.length - 1] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE; + var info = this.findDescribingParent(node); + // Ignore strings in import and export nodes. + if (info && info.isImport && doubleQuoted) { + this.addFailureAtNode(node, NoUnexternalizedStringsRuleWalker.ImportFailureMessage, new Lint.Fix(NoUnexternalizedStringsRuleWalker.ImportFailureMessage, [ + this.createReplacement(node.getStart(), 1, '\''), + this.createReplacement(node.getStart() + text.length - 1, 1, '\''), + ])); + return; + } + var callInfo = info ? info.callInfo : null; + var functionName = callInfo ? callInfo.callExpression.expression.getText() : null; + if (functionName && this.ignores[functionName]) { + return; + } + if (doubleQuoted && (!callInfo || callInfo.argIndex === -1 || !this.signatures[functionName])) { + var s = node.getText(); + var replacement = new Lint.Replacement(node.getStart(), node.getWidth(), "nls.localize('KEY-" + s.substring(1, s.length - 1) + "', " + s + ")"); + var fix = new Lint.Fix('Unexternalitzed string', [replacement]); + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), "Unexternalized string found: " + node.getText(), fix)); + return; + } + // We have a single quoted string outside a localize function name. + if (!doubleQuoted && !this.signatures[functionName]) { + return; + } + // We have a string that is a direct argument into the localize call. + var keyArg = callInfo.argIndex === this.keyIndex + ? callInfo.callExpression.arguments[this.keyIndex] + : null; + if (keyArg) { + if (isStringLiteral(keyArg)) { + this.recordKey(keyArg, this.messageIndex ? callInfo.callExpression.arguments[this.messageIndex] : undefined); + } + else if (isObjectLiteral(keyArg)) { + for (var i = 0; i < keyArg.properties.length; i++) { + var property = keyArg.properties[i]; + if (isPropertyAssignment(property)) { + var name_1 = property.name.getText(); + if (name_1 === 'key') { + var initializer = property.initializer; + if (isStringLiteral(initializer)) { + this.recordKey(initializer, this.messageIndex ? callInfo.callExpression.arguments[this.messageIndex] : undefined); + } + break; + } + } + } + } + } + var messageArg = callInfo.argIndex === this.messageIndex + ? callInfo.callExpression.arguments[this.messageIndex] + : null; + if (messageArg && messageArg !== node) { + this.addFailure(this.createFailure(messageArg.getStart(), messageArg.getWidth(), "Message argument to '" + callInfo.callExpression.expression.getText() + "' must be a string literal.")); + return; + } + }; + NoUnexternalizedStringsRuleWalker.prototype.recordKey = function (keyNode, messageNode) { + var text = keyNode.getText(); + var occurrences = this.usedKeys[text]; + if (!occurrences) { + occurrences = []; + this.usedKeys[text] = occurrences; + } + if (messageNode) { + if (occurrences.some(function (pair) { return pair.message ? pair.message.getText() === messageNode.getText() : false; })) { + return; + } + } + occurrences.push({ key: keyNode, message: messageNode }); + }; + NoUnexternalizedStringsRuleWalker.prototype.findDescribingParent = function (node) { + var parent; + while ((parent = node.parent)) { + var kind = parent.kind; + if (kind === ts.SyntaxKind.CallExpression) { + var callExpression = parent; + return { callInfo: { callExpression: callExpression, argIndex: callExpression.arguments.indexOf(node) } }; + } + else if (kind === ts.SyntaxKind.ImportEqualsDeclaration || kind === ts.SyntaxKind.ImportDeclaration || kind === ts.SyntaxKind.ExportDeclaration) { + return { isImport: true }; + } + else if (kind === ts.SyntaxKind.VariableDeclaration || kind === ts.SyntaxKind.FunctionDeclaration || kind === ts.SyntaxKind.PropertyDeclaration + || kind === ts.SyntaxKind.MethodDeclaration || kind === ts.SyntaxKind.VariableDeclarationList || kind === ts.SyntaxKind.InterfaceDeclaration + || kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.ModuleDeclaration + || kind === ts.SyntaxKind.TypeAliasDeclaration || kind === ts.SyntaxKind.SourceFile) { + return null; + } + node = parent; + } + }; + NoUnexternalizedStringsRuleWalker.ImportFailureMessage = 'Do not use double qoutes for imports.'; + NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE = '"'; + return NoUnexternalizedStringsRuleWalker; +}(Lint.RuleWalker)); diff --git a/build/lib/tslint/noUnexternalizedStringsRule.ts b/build/lib/tslint/noUnexternalizedStringsRule.ts new file mode 100644 index 0000000000..bb9b492068 --- /dev/null +++ b/build/lib/tslint/noUnexternalizedStringsRule.ts @@ -0,0 +1,201 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; + +/** + * Implementation of the no-unexternalized-strings rule. + */ +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithWalker(new NoUnexternalizedStringsRuleWalker(sourceFile, this.getOptions())); + } +} + +interface Map { + [key: string]: V; +} + +interface UnexternalizedStringsOptions { + signatures?: string[]; + messageIndex?: number; + keyIndex?: number; + ignores?: string[]; +} + +function isStringLiteral(node: ts.Node): node is ts.StringLiteral { + return node && node.kind === ts.SyntaxKind.StringLiteral; +} + +function isObjectLiteral(node: ts.Node): node is ts.ObjectLiteralExpression { + return node && node.kind === ts.SyntaxKind.ObjectLiteralExpression; +} + +function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment { + return node && node.kind === ts.SyntaxKind.PropertyAssignment; +} + +interface KeyMessagePair { + key: ts.StringLiteral; + message: ts.Node; +} + +class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker { + + private static ImportFailureMessage = 'Do not use double qoutes for imports.'; + + private static DOUBLE_QUOTE: string = '"'; + + private signatures: Map; + private messageIndex: number; + private keyIndex: number; + private ignores: Map; + + private usedKeys: Map; + + constructor(file: ts.SourceFile, opts: Lint.IOptions) { + super(file, opts); + this.signatures = Object.create(null); + this.ignores = Object.create(null); + this.messageIndex = undefined; + this.keyIndex = undefined; + this.usedKeys = Object.create(null); + let options: any[] = this.getOptions(); + let first: UnexternalizedStringsOptions = options && options.length > 0 ? options[0] : null; + if (first) { + if (Array.isArray(first.signatures)) { + first.signatures.forEach((signature: string) => this.signatures[signature] = true); + } + if (Array.isArray(first.ignores)) { + first.ignores.forEach((ignore: string) => this.ignores[ignore] = true); + } + if (typeof first.messageIndex !== 'undefined') { + this.messageIndex = first.messageIndex; + } + if (typeof first.keyIndex !== 'undefined') { + this.keyIndex = first.keyIndex; + } + } + } + + protected visitSourceFile(node: ts.SourceFile): void { + super.visitSourceFile(node); + Object.keys(this.usedKeys).forEach(key => { + let occurrences = this.usedKeys[key]; + if (occurrences.length > 1) { + occurrences.forEach(occurrence => { + this.addFailure((this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `Duplicate key ${occurrence.key.getText()} with different message value.`))); + }); + } + }); + } + + protected visitStringLiteral(node: ts.StringLiteral): void { + this.checkStringLiteral(node); + super.visitStringLiteral(node); + } + + private checkStringLiteral(node: ts.StringLiteral): void { + let text = node.getText(); + let doubleQuoted = text.length >= 2 && text[0] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE && text[text.length - 1] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE; + let info = this.findDescribingParent(node); + // Ignore strings in import and export nodes. + if (info && info.isImport && doubleQuoted) { + this.addFailureAtNode( + node, + NoUnexternalizedStringsRuleWalker.ImportFailureMessage, + new Lint.Fix(NoUnexternalizedStringsRuleWalker.ImportFailureMessage, [ + this.createReplacement(node.getStart(), 1, '\''), + this.createReplacement(node.getStart() + text.length - 1, 1, '\''), + ]) + ); + return; + } + let callInfo = info ? info.callInfo : null; + let functionName = callInfo ? callInfo.callExpression.expression.getText() : null; + if (functionName && this.ignores[functionName]) { + return; + } + + if (doubleQuoted && (!callInfo || callInfo.argIndex === -1 || !this.signatures[functionName])) { + const s = node.getText(); + const replacement = new Lint.Replacement(node.getStart(), node.getWidth(), `nls.localize('KEY-${s.substring(1, s.length - 1)}', ${s})`); + const fix = new Lint.Fix('Unexternalitzed string', [replacement]); + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Unexternalized string found: ${node.getText()}`, fix)); + return; + } + // We have a single quoted string outside a localize function name. + if (!doubleQuoted && !this.signatures[functionName]) { + return; + } + // We have a string that is a direct argument into the localize call. + let keyArg: ts.Expression = callInfo.argIndex === this.keyIndex + ? callInfo.callExpression.arguments[this.keyIndex] + : null; + if (keyArg) { + if (isStringLiteral(keyArg)) { + this.recordKey(keyArg, this.messageIndex ? callInfo.callExpression.arguments[this.messageIndex] : undefined); + } else if (isObjectLiteral(keyArg)) { + for (let i = 0; i < keyArg.properties.length; i++) { + let property = keyArg.properties[i]; + if (isPropertyAssignment(property)) { + let name = property.name.getText(); + if (name === 'key') { + let initializer = property.initializer; + if (isStringLiteral(initializer)) { + this.recordKey(initializer, this.messageIndex ? callInfo.callExpression.arguments[this.messageIndex] : undefined); + } + break; + } + } + } + } + } + let messageArg: ts.Expression = callInfo.argIndex === this.messageIndex + ? callInfo.callExpression.arguments[this.messageIndex] + : null; + if (messageArg && messageArg !== node) { + this.addFailure(this.createFailure( + messageArg.getStart(), messageArg.getWidth(), + `Message argument to '${callInfo.callExpression.expression.getText()}' must be a string literal.`)); + return; + } + } + + private recordKey(keyNode: ts.StringLiteral, messageNode: ts.Node) { + let text = keyNode.getText(); + let occurrences: KeyMessagePair[] = this.usedKeys[text]; + if (!occurrences) { + occurrences = []; + this.usedKeys[text] = occurrences; + } + if (messageNode) { + if (occurrences.some(pair => pair.message ? pair.message.getText() === messageNode.getText() : false)) { + return; + } + } + occurrences.push({ key: keyNode, message: messageNode }); + } + + private findDescribingParent(node: ts.Node): { callInfo?: { callExpression: ts.CallExpression, argIndex: number }, isImport?: boolean; } { + let parent: ts.Node; + while ((parent = node.parent)) { + let kind = parent.kind; + if (kind === ts.SyntaxKind.CallExpression) { + let callExpression = parent as ts.CallExpression; + return { callInfo: { callExpression: callExpression, argIndex: callExpression.arguments.indexOf(node) } }; + } else if (kind === ts.SyntaxKind.ImportEqualsDeclaration || kind === ts.SyntaxKind.ImportDeclaration || kind === ts.SyntaxKind.ExportDeclaration) { + return { isImport: true }; + } else if (kind === ts.SyntaxKind.VariableDeclaration || kind === ts.SyntaxKind.FunctionDeclaration || kind === ts.SyntaxKind.PropertyDeclaration + || kind === ts.SyntaxKind.MethodDeclaration || kind === ts.SyntaxKind.VariableDeclarationList || kind === ts.SyntaxKind.InterfaceDeclaration + || kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.ModuleDeclaration + || kind === ts.SyntaxKind.TypeAliasDeclaration || kind === ts.SyntaxKind.SourceFile) { + return null; + } + node = parent; + } + } +} diff --git a/build/lib/tslint/translationRemindRule.js b/build/lib/tslint/translationRemindRule.js new file mode 100644 index 0000000000..1107c5781d --- /dev/null +++ b/build/lib/tslint/translationRemindRule.js @@ -0,0 +1,79 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var Lint = require("tslint"); +var fs = require("fs"); +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + return this.applyWithWalker(new TranslationRemindRuleWalker(sourceFile, this.getOptions())); + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +var TranslationRemindRuleWalker = (function (_super) { + __extends(TranslationRemindRuleWalker, _super); + function TranslationRemindRuleWalker(file, opts) { + return _super.call(this, file, opts) || this; + } + TranslationRemindRuleWalker.prototype.visitImportDeclaration = function (node) { + var declaration = node.moduleSpecifier.getText(); + if (declaration !== "'" + TranslationRemindRuleWalker.NLS_MODULE + "'") { + return; + } + this.visitImportLikeDeclaration(node); + }; + TranslationRemindRuleWalker.prototype.visitImportEqualsDeclaration = function (node) { + var reference = node.moduleReference.getText(); + if (reference !== "require('" + TranslationRemindRuleWalker.NLS_MODULE + "')") { + return; + } + this.visitImportLikeDeclaration(node); + }; + TranslationRemindRuleWalker.prototype.visitImportLikeDeclaration = function (node) { + var currentFile = node.getSourceFile().fileName; + var matchService = currentFile.match(/vs\/workbench\/services\/\w+/); + var matchPart = currentFile.match(/vs\/workbench\/parts\/\w+/); + if (!matchService && !matchPart) { + return; + } + var resource = matchService ? matchService[0] : matchPart[0]; + var resourceDefined = false; + var json; + try { + json = fs.readFileSync('./build/lib/i18n.resources.json', 'utf8'); + } + catch (e) { + console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.'); + return; + } + var workbenchResources = JSON.parse(json).workbench; + workbenchResources.forEach(function (existingResource) { + if (existingResource.name === resource) { + resourceDefined = true; + return; + } + }); + if (!resourceDefined) { + this.addFailureAtNode(node, "Please add '" + resource + "' to ./builds/lib/i18n.resources.json file to use translations here."); + } + }; + TranslationRemindRuleWalker.NLS_MODULE = 'vs/nls'; + return TranslationRemindRuleWalker; +}(Lint.RuleWalker)); diff --git a/build/lib/tslint/translationRemindRule.ts b/build/lib/tslint/translationRemindRule.ts new file mode 100644 index 0000000000..48b01c5bef --- /dev/null +++ b/build/lib/tslint/translationRemindRule.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; +import * as fs from 'fs'; + +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithWalker(new TranslationRemindRuleWalker(sourceFile, this.getOptions())); + } +} + +class TranslationRemindRuleWalker extends Lint.RuleWalker { + + private static NLS_MODULE: string = 'vs/nls'; + + constructor(file: ts.SourceFile, opts: Lint.IOptions) { + super(file, opts); + } + + protected visitImportDeclaration(node: ts.ImportDeclaration): void { + const declaration = node.moduleSpecifier.getText(); + if (declaration !== `'${TranslationRemindRuleWalker.NLS_MODULE}'`) { + return; + } + + this.visitImportLikeDeclaration(node); + } + + protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void { + const reference = node.moduleReference.getText(); + if (reference !== `require('${TranslationRemindRuleWalker.NLS_MODULE}')`) { + return; + } + + this.visitImportLikeDeclaration(node); + } + + private visitImportLikeDeclaration(node: ts.ImportDeclaration | ts.ImportEqualsDeclaration) { + const currentFile = node.getSourceFile().fileName; + const matchService = currentFile.match(/vs\/workbench\/services\/\w+/); + const matchPart = currentFile.match(/vs\/workbench\/parts\/\w+/); + if (!matchService && !matchPart) { + return; + } + + const resource = matchService ? matchService[0] : matchPart[0]; + let resourceDefined = false; + + let json; + try { + json = fs.readFileSync('./build/lib/i18n.resources.json', 'utf8'); + } catch (e) { + console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.'); + return; + } + const workbenchResources = JSON.parse(json).workbench; + + workbenchResources.forEach(existingResource => { + if (existingResource.name === resource) { + resourceDefined = true; + return; + } + }); + + if (!resourceDefined) { + this.addFailureAtNode(node, `Please add '${resource}' to ./builds/lib/i18n.resources.json file to use translations here.`); + } + } +} diff --git a/build/lib/typings/OSSREADME.json b/build/lib/typings/OSSREADME.json new file mode 100644 index 0000000000..cdb8c7f511 --- /dev/null +++ b/build/lib/typings/OSSREADME.json @@ -0,0 +1,10 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +// All OSS in this folder is development time only +[{ + "name": "definitelytyped", + "repositoryURL": "https://github.com/DefinitelyTyped/DefinitelyTyped", + "license": "MIT", + "isDev": true +} +] \ No newline at end of file diff --git a/build/lib/typings/Q.d.ts b/build/lib/typings/Q.d.ts new file mode 100644 index 0000000000..40ccceb798 --- /dev/null +++ b/build/lib/typings/Q.d.ts @@ -0,0 +1,361 @@ +// Type definitions for Q +// Project: https://github.com/kriskowal/q +// Definitions by: Barrie Nemetchek , Andrew Gaspar , John Reilly +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/** + * If value is a Q promise, returns the promise. + * If value is a promise from another library it is coerced into a Q promise (where possible). + */ +declare function Q(promise: Q.IPromise): Q.Promise; +/** + * If value is not a promise, returns a promise that is fulfilled with value. + */ +declare function Q(value: T): Q.Promise; +/** + * Calling with nothing at all creates a void promise + */ +declare function Q(): Q.Promise; + +declare namespace Q { + type IWhenable = IPromise | T; + interface IPromise { + then(onFulfill?: (value: T) => IWhenable, onReject?: (error: any) => IWhenable): IPromise; + } + + interface Deferred { + promise: Promise; + resolve(value?: IWhenable): void; + reject(reason: any): void; + notify(value: any): void; + makeNodeResolver(): (reason: any, value: T) => void; + } + + interface Promise { + /** + * Like a finally clause, allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful for collecting resources regardless of whether a job succeeded, like closing a database connection, shutting a server down, or deleting an unneeded key from an object. + + * finally returns a promise, which will become resolved with the same fulfillment value or rejection reason as promise. However, if callback returns a promise, the resolution of the returned promise will be delayed until the promise returned from callback is finished. + */ + fin(finallyCallback: () => any): Promise; + /** + * Like a finally clause, allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful for collecting resources regardless of whether a job succeeded, like closing a database connection, shutting a server down, or deleting an unneeded key from an object. + + * finally returns a promise, which will become resolved with the same fulfillment value or rejection reason as promise. However, if callback returns a promise, the resolution of the returned promise will be delayed until the promise returned from callback is finished. + */ + finally(finallyCallback: () => any): Promise; + + /** + * The then method from the Promises/A+ specification, with an additional progress handler. + */ + then(onFulfill?: (value: T) => IWhenable, onReject?: (error: any) => IWhenable, onProgress?: Function): Promise; + + /** + * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. + * + * This is especially useful in conjunction with all + */ + spread(onFulfill: (...args: any[]) => IWhenable, onReject?: (reason: any) => IWhenable): Promise; + + fail(onRejected: (reason: any) => IWhenable): Promise; + + /** + * A sugar method, equivalent to promise.then(undefined, onRejected). + */ + catch(onRejected: (reason: any) => IWhenable): Promise; + + /** + * A sugar method, equivalent to promise.then(undefined, undefined, onProgress). + */ + progress(onProgress: (progress: any) => any): Promise; + + /** + * Much like then, but with different behavior around unhandled rejection. If there is an unhandled rejection, either because promise is rejected and no onRejected callback was provided, or because onFulfilled or onRejected threw an error or returned a rejected promise, the resulting rejection reason is thrown as an exception in a future turn of the event loop. + * + * This method should be used to terminate chains of promises that will not be passed elsewhere. Since exceptions thrown in then callbacks are consumed and transformed into rejections, exceptions at the end of the chain are easy to accidentally, silently ignore. By arranging for the exception to be thrown in a future turn of the event loop, so that it won't be caught, it causes an onerror event on the browser window, or an uncaughtException event on Node.js's process object. + * + * Exceptions thrown by done will have long stack traces, if Q.longStackSupport is set to true. If Q.onerror is set, exceptions will be delivered there instead of thrown in a future turn. + * + * The Golden Rule of done vs. then usage is: either return your promise to someone else, or if the chain ends with you, call done to terminate it. + */ + done(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any, onProgress?: (progress: any) => any): void; + + /** + * If callback is a function, assumes it's a Node.js-style callback, and calls it as either callback(rejectionReason) when/if promise becomes rejected, or as callback(null, fulfillmentValue) when/if promise becomes fulfilled. If callback is not a function, simply returns promise. + */ + nodeify(callback: (reason: any, value: any) => void): Promise; + + /** + * Returns a promise to get the named property of an object. Essentially equivalent to + * + * promise.then(function (o) { + * return o[propertyName]; + * }); + */ + get(propertyName: String): Promise; + set(propertyName: String, value: any): Promise; + delete(propertyName: String): Promise; + /** + * Returns a promise for the result of calling the named method of an object with the given array of arguments. The object itself is this in the function, just like a synchronous method call. Essentially equivalent to + * + * promise.then(function (o) { + * return o[methodName].apply(o, args); + * }); + */ + post(methodName: String, args: any[]): Promise; + /** + * Returns a promise for the result of calling the named method of an object with the given variadic arguments. The object itself is this in the function, just like a synchronous method call. + */ + invoke(methodName: String, ...args: any[]): Promise; + fapply(args: any[]): Promise; + fcall(...args: any[]): Promise; + + /** + * Returns a promise for an array of the property names of an object. Essentially equivalent to + * + * promise.then(function (o) { + * return Object.keys(o); + * }); + */ + keys(): Promise; + + /** + * A sugar method, equivalent to promise.then(function () { return value; }). + */ + thenResolve(value: U): Promise; + /** + * A sugar method, equivalent to promise.then(function () { throw reason; }). + */ + thenReject(reason: any): Promise; + + /** + * Attaches a handler that will observe the value of the promise when it becomes fulfilled, returning a promise for that same value, perhaps deferred but not replaced by the promise returned by the onFulfilled handler. + */ + tap(onFulfilled: (value: T) => any): Promise; + + timeout(ms: number, message?: string): Promise; + /** + * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. + */ + delay(ms: number): Promise; + + /** + * Returns whether a given promise is in the fulfilled state. When the static version is used on non-promises, the result is always true. + */ + isFulfilled(): boolean; + /** + * Returns whether a given promise is in the rejected state. When the static version is used on non-promises, the result is always false. + */ + isRejected(): boolean; + /** + * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. + */ + isPending(): boolean; + + valueOf(): any; + + /** + * Returns a "state snapshot" object, which will be in one of three forms: + * + * - { state: "pending" } + * - { state: "fulfilled", value: } + * - { state: "rejected", reason: } + */ + inspect(): PromiseState; + } + + interface PromiseState { + /** + * "fulfilled", "rejected", "pending" + */ + state: string; + value?: T; + reason?: any; + } + + // If no value provided, returned promise will be of void type + export function when(): Promise; + + // if no fulfill, reject, or progress provided, returned promise will be of same type + export function when(value: IWhenable): Promise; + + // If a non-promise value is provided, it will not reject or progress + export function when(value: IWhenable, onFulfilled: (val: T) => IWhenable, onRejected?: (reason: any) => IWhenable, onProgress?: (progress: any) => any): Promise; + + /** + * Currently "impossible" (and I use the term loosely) to implement due to TypeScript limitations as it is now. + * See: https://github.com/Microsoft/TypeScript/issues/1784 for discussion on it. + */ + // export function try(method: Function, ...args: any[]): Promise; + + export function fbind(method: (...args: any[]) => IWhenable, ...args: any[]): (...args: any[]) => Promise; + + export function fcall(method: (...args: any[]) => T, ...args: any[]): Promise; + + export function send(obj: any, functionName: string, ...args: any[]): Promise; + export function invoke(obj: any, functionName: string, ...args: any[]): Promise; + export function mcall(obj: any, functionName: string, ...args: any[]): Promise; + + export function denodeify(nodeFunction: Function, ...args: any[]): (...args: any[]) => Promise; + export function nbind(nodeFunction: Function, thisArg: any, ...args: any[]): (...args: any[]) => Promise; + export function nfbind(nodeFunction: Function, ...args: any[]): (...args: any[]) => Promise; + export function nfcall(nodeFunction: Function, ...args: any[]): Promise; + export function nfapply(nodeFunction: Function, args: any[]): Promise; + + export function ninvoke(nodeModule: any, functionName: string, ...args: any[]): Promise; + export function npost(nodeModule: any, functionName: string, args: any[]): Promise; + export function nsend(nodeModule: any, functionName: string, ...args: any[]): Promise; + export function nmcall(nodeModule: any, functionName: string, ...args: any[]): Promise; + + /** + * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. + */ + export function all(promises: IWhenable<[IWhenable, IWhenable, IWhenable, IWhenable, IWhenable, IWhenable]>): Promise<[A, B, C, D, E, F]>; + /** + * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. + */ + export function all(promises: IWhenable<[IWhenable, IWhenable, IWhenable, IWhenable, IWhenable]>): Promise<[A, B, C, D, E]>; + /** + * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. + */ + export function all(promises: IWhenable<[IWhenable, IWhenable, IWhenable, IWhenable]>): Promise<[A, B, C, D]>; + /** + * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. + */ + export function all(promises: IWhenable<[IWhenable, IWhenable, IWhenable]>): Promise<[A, B, C]>; + /** + * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. + */ + export function all(promises: IWhenable<[IWhenable, IWhenable]>): Promise<[A, B]>; + /** + * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. + */ + export function all(promises: IWhenable[]>): Promise; + + /** + * Returns a promise for the first of an array of promises to become settled. + */ + export function race(promises: IWhenable[]): Promise; + + /** + * Returns a promise that is fulfilled with an array of promise state snapshots, but only after all the original promises have settled, i.e. become either fulfilled or rejected. + */ + export function allSettled(promises: IWhenable[]>): Promise[]>; + + export function allResolved(promises: IWhenable[]>): Promise[]>; + + /** + * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. + * This is especially useful in conjunction with all. + */ + export function spread(promises: IWhenable[], onFulfilled: (...args: T[]) => IWhenable, onRejected?: (reason: any) => IWhenable): Promise; + + /** + * Returns a promise that will have the same result as promise, except that if promise is not fulfilled or rejected before ms milliseconds, the returned promise will be rejected with an Error with the given message. If message is not supplied, the message will be "Timed out after " + ms + " ms". + */ + export function timeout(promise: Promise, ms: number, message?: string): Promise; + + /** + * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. + */ + export function delay(promise: Promise, ms: number): Promise; + /** + * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. + */ + export function delay(value: T, ms: number): Promise; + /** + * Returns a promise that will be fulfilled with undefined after at least ms milliseconds have passed. + */ + export function delay(ms: number): Promise ; + /** + * Returns whether a given promise is in the fulfilled state. When the static version is used on non-promises, the result is always true. + */ + export function isFulfilled(promise: Promise): boolean; + /** + * Returns whether a given promise is in the rejected state. When the static version is used on non-promises, the result is always false. + */ + export function isRejected(promise: Promise): boolean; + /** + * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. + */ + export function isPending(promise: Promise): boolean; + + /** + * Returns a "deferred" object with a: + * promise property + * resolve(value) method + * reject(reason) method + * notify(value) method + * makeNodeResolver() method + */ + export function defer(): Deferred; + + /** + * Returns a promise that is rejected with reason. + */ + export function reject(reason?: any): Promise; + + export function Promise(resolver: (resolve: (val: IWhenable) => void , reject: (reason: any) => void , notify: (progress: any) => void ) => void ): Promise; + + /** + * Creates a new version of func that accepts any combination of promise and non-promise values, converting them to their fulfillment values before calling the original func. The returned version also always returns a promise: if func does a return or throw, then Q.promised(func) will return fulfilled or rejected promise, respectively. + * + * This can be useful for creating functions that accept either promises or non-promise values, and for ensuring that the function always returns a promise even in the face of unintentional thrown exceptions. + */ + export function promised(callback: (...args: any[]) => T): (...args: any[]) => Promise; + + /** + * Returns whether the given value is a Q promise. + */ + export function isPromise(object: any): boolean; + /** + * Returns whether the given value is a promise (i.e. it's an object with a then function). + */ + export function isPromiseAlike(object: any): boolean; + /** + * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. + */ + export function isPending(object: any): boolean; + /** + * If an object is not a promise, it is as "near" as possible. + * If a promise is rejected, it is as "near" as possible too. + * If it’s a fulfilled promise, the fulfillment value is nearer. + * If it’s a deferred promise and the deferred has been resolved, the + * resolution is "nearer". + */ + export function nearer(promise: Promise): T; + + /** + * This is an experimental tool for converting a generator function into a deferred function. This has the potential of reducing nested callbacks in engines that support yield. + */ + export function async(generatorFunction: any): (...args: any[]) => Promise; + export function nextTick(callback: Function): void; + + /** + * A settable property that will intercept any uncaught errors that would otherwise be thrown in the next tick of the event loop, usually as a result of done. Can be useful for getting the full stack trace of an error in browsers, which is not usually possible with window.onerror. + */ + export var onerror: (reason: any) => void; + /** + * A settable property that lets you turn on long stack trace support. If turned on, "stack jumps" will be tracked across asynchronous promise operations, so that if an uncaught error is thrown by done or a rejection reason's stack property is inspected in a rejection callback, a long stack trace is produced. + */ + export var longStackSupport: boolean; + + /** + * Calling resolve with a pending promise causes promise to wait on the passed promise, becoming fulfilled with its fulfillment value or rejected with its rejection reason (or staying pending forever, if the passed promise does). + * Calling resolve with a rejected promise causes promise to be rejected with the passed promise's rejection reason. + * Calling resolve with a fulfilled promise causes promise to be fulfilled with the passed promise's fulfillment value. + * Calling resolve with a non-promise value causes promise to be fulfilled with that value. + */ + export function resolve(object: IWhenable): Promise; + + /** + * Resets the global "Q" variable to the value it has before Q was loaded. + * This will either be undefined if there was no version or the version of Q which was already loaded before. + * @returns { The last version of Q. } + */ + export function noConflict(): typeof Q; +} + +declare module "q" { + export = Q; +} \ No newline at end of file diff --git a/build/lib/typings/chalk.d.ts b/build/lib/typings/chalk.d.ts new file mode 100644 index 0000000000..d82f86f925 --- /dev/null +++ b/build/lib/typings/chalk.d.ts @@ -0,0 +1,121 @@ +// Type definitions for chalk v0.4.0 +// Project: https://github.com/sindresorhus/chalk +// Definitions by: Diullei Gomes , Bart van der Schoor , Nico Jansen +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare namespace Chalk { + + export var enabled: boolean; + export var supportsColor: boolean; + export var styles: ChalkStyleMap; + + export function stripColor(value: string): any; + export function hasColor(str: string): boolean; + + export interface ChalkChain extends ChalkStyle { + (...text: string[]): string; + } + + export interface ChalkStyleElement { + open: string; + close: string; + } + + // General + export var reset: ChalkChain; + export var bold: ChalkChain; + export var italic: ChalkChain; + export var underline: ChalkChain; + export var inverse: ChalkChain; + export var strikethrough: ChalkChain; + + // Text colors + export var black: ChalkChain; + export var red: ChalkChain; + export var green: ChalkChain; + export var yellow: ChalkChain; + export var blue: ChalkChain; + export var magenta: ChalkChain; + export var cyan: ChalkChain; + export var white: ChalkChain; + export var gray: ChalkChain; + export var grey: ChalkChain; + + // Background colors + export var bgBlack: ChalkChain; + export var bgRed: ChalkChain; + export var bgGreen: ChalkChain; + export var bgYellow: ChalkChain; + export var bgBlue: ChalkChain; + export var bgMagenta: ChalkChain; + export var bgCyan: ChalkChain; + export var bgWhite: ChalkChain; + + + export interface ChalkStyle { + // General + reset: ChalkChain; + bold: ChalkChain; + italic: ChalkChain; + underline: ChalkChain; + inverse: ChalkChain; + strikethrough: ChalkChain; + + // Text colors + black: ChalkChain; + red: ChalkChain; + green: ChalkChain; + yellow: ChalkChain; + blue: ChalkChain; + magenta: ChalkChain; + cyan: ChalkChain; + white: ChalkChain; + gray: ChalkChain; + grey: ChalkChain; + + // Background colors + bgBlack: ChalkChain; + bgRed: ChalkChain; + bgGreen: ChalkChain; + bgYellow: ChalkChain; + bgBlue: ChalkChain; + bgMagenta: ChalkChain; + bgCyan: ChalkChain; + bgWhite: ChalkChain; + } + + export interface ChalkStyleMap { + // General + reset: ChalkStyleElement; + bold: ChalkStyleElement; + italic: ChalkStyleElement; + underline: ChalkStyleElement; + inverse: ChalkStyleElement; + strikethrough: ChalkStyleElement; + + // Text colors + black: ChalkStyleElement; + red: ChalkStyleElement; + green: ChalkStyleElement; + yellow: ChalkStyleElement; + blue: ChalkStyleElement; + magenta: ChalkStyleElement; + cyan: ChalkStyleElement; + white: ChalkStyleElement; + gray: ChalkStyleElement; + + // Background colors + bgBlack: ChalkStyleElement; + bgRed: ChalkStyleElement; + bgGreen: ChalkStyleElement; + bgYellow: ChalkStyleElement; + bgBlue: ChalkStyleElement; + bgMagenta: ChalkStyleElement; + bgCyan: ChalkStyleElement; + bgWhite: ChalkStyleElement; + } +} + +declare module "chalk" { + export = Chalk; +} diff --git a/build/lib/typings/debounce.d.ts b/build/lib/typings/debounce.d.ts new file mode 100644 index 0000000000..02171c8839 --- /dev/null +++ b/build/lib/typings/debounce.d.ts @@ -0,0 +1,18 @@ +// Type definitions for compose-function +// Project: https://github.com/component/debounce +// Definitions by: Denis Sokolov +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module "debounce" { + // Overload on boolean constants would allow us to narrow further, + // but it is not implemented for TypeScript yet + function f(f: A, interval?: number, immediate?: boolean): A + + /** + * This is required as per: + * https://github.com/Microsoft/TypeScript/issues/5073 + */ + namespace f {} + + export = f; +} \ No newline at end of file diff --git a/build/lib/typings/event-stream.d.ts b/build/lib/typings/event-stream.d.ts new file mode 100644 index 0000000000..7e5ccee5e1 --- /dev/null +++ b/build/lib/typings/event-stream.d.ts @@ -0,0 +1,21 @@ +declare module "event-stream" { + import { Stream } from 'stream'; + import { ThroughStream } from 'through'; + import { MapStream } from 'map-stream'; + + function merge(streams: Stream[]): ThroughStream; + function merge(...streams: Stream[]): ThroughStream; + function concat(...stream: Stream[]): ThroughStream; + function duplex(istream: Stream, ostream: Stream): ThroughStream; + + function through(write?: (data: any) => void, end?: () => void, + opts?: {autoDestroy: boolean; }): ThroughStream; + + function readArray(array: T[]): ThroughStream; + function writeArray(cb: (err:Error, array:T[]) => void): ThroughStream; + + function mapSync(cb: (data:I) => O): ThroughStream; + function map(cb: (data:I, cb:(err?:Error, data?: O)=>void) => O): ThroughStream; + + function readable(asyncFunction: Function): MapStream; +} \ No newline at end of file diff --git a/build/lib/typings/gulp-bom.d.ts b/build/lib/typings/gulp-bom.d.ts new file mode 100644 index 0000000000..94dc5fd6d2 --- /dev/null +++ b/build/lib/typings/gulp-bom.d.ts @@ -0,0 +1,12 @@ + +declare module "gulp-bom" { + function f(): NodeJS.ReadWriteStream; + + /** + * This is required as per: + * https://github.com/Microsoft/TypeScript/issues/5073 + */ + namespace f {} + + export = f; +} diff --git a/build/lib/typings/gulp-concat.d.ts b/build/lib/typings/gulp-concat.d.ts new file mode 100644 index 0000000000..fa5915830c --- /dev/null +++ b/build/lib/typings/gulp-concat.d.ts @@ -0,0 +1,43 @@ +// Type definitions for gulp-concat +// Project: http://github.com/wearefractal/gulp-concat +// Definitions by: Keita Kagurazaka +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module "gulp-concat" { + + interface IOptions { + newLine: string; + } + + interface IFsStats { + dev?: number; + ino?: number; + mode?: number; + nlink?: number; + uid?: number; + gid?: number; + rdev?: number; + size?: number; + blksize?: number; + blocks?: number; + atime?: Date; + mtime?: Date; + ctime?: Date; + } + + interface IVinylOptions { + cwd?: string; + base?: string; + path?: string; + stat?: IFsStats; + contents?: NodeJS.ReadableStream | Buffer; + } + + interface IConcat { + (filename: string, options?: IOptions): NodeJS.ReadWriteStream; + (options: IVinylOptions): NodeJS.ReadWriteStream; + } + + var _tmp: IConcat; + export = _tmp; +} \ No newline at end of file diff --git a/build/lib/typings/gulp-cssnano.d.ts b/build/lib/typings/gulp-cssnano.d.ts new file mode 100644 index 0000000000..48f8cbf716 --- /dev/null +++ b/build/lib/typings/gulp-cssnano.d.ts @@ -0,0 +1,12 @@ + +declare module "gulp-cssnano" { + function f(opts:{reduceIdents:boolean;}): NodeJS.ReadWriteStream; + + /** + * This is required as per: + * https://github.com/Microsoft/TypeScript/issues/5073 + */ + namespace f {} + + export = f; +} \ No newline at end of file diff --git a/build/lib/typings/gulp-filter.d.ts b/build/lib/typings/gulp-filter.d.ts new file mode 100644 index 0000000000..ab05f05972 --- /dev/null +++ b/build/lib/typings/gulp-filter.d.ts @@ -0,0 +1,29 @@ +// Type definitions for gulp-filter v3.0.1 +// Project: https://github.com/sindresorhus/gulp-filter +// Definitions by: Tanguy Krotoff +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module 'gulp-filter' { + import File = require('vinyl'); + import * as Minimatch from 'minimatch'; + + namespace filter { + interface FileFunction { + (file: File): boolean; + } + + interface Options extends Minimatch.IOptions { + restore?: boolean; + passthrough?: boolean; + } + + // A transform stream with a .restore object + interface Filter extends NodeJS.ReadWriteStream { + restore: NodeJS.ReadWriteStream + } + } + + function filter(pattern: string | string[] | filter.FileFunction, options?: filter.Options): filter.Filter; + + export = filter; +} \ No newline at end of file diff --git a/build/lib/typings/gulp-flatmap.d.ts b/build/lib/typings/gulp-flatmap.d.ts new file mode 100644 index 0000000000..82dd84e15b --- /dev/null +++ b/build/lib/typings/gulp-flatmap.d.ts @@ -0,0 +1,12 @@ +declare module 'gulp-flatmap' { + import File = require('vinyl'); + function f(fn:(stream:NodeJS.ReadWriteStream, file:File)=>NodeJS.ReadWriteStream): NodeJS.ReadWriteStream; + + /** + * This is required as per: + * https://github.com/Microsoft/TypeScript/issues/5073 + */ + namespace f {} + + export = f; +} \ No newline at end of file diff --git a/build/lib/typings/gulp-remote-src.d.ts b/build/lib/typings/gulp-remote-src.d.ts new file mode 100644 index 0000000000..6ea57f84fe --- /dev/null +++ b/build/lib/typings/gulp-remote-src.d.ts @@ -0,0 +1,23 @@ +declare module 'gulp-remote-src' { + + import stream = require("stream"); + + function remote(url: string, options: remote.IOptions): stream.Stream; + + module remote { + export interface IRequestOptions { + body?: any; + json?: boolean; + method?: string; + headers?: any; + } + + export interface IOptions { + base?: string; + buffer?: boolean; + requestOptions?: IRequestOptions; + } + } + + export = remote; +} \ No newline at end of file diff --git a/build/lib/typings/gulp-rename.d.ts b/build/lib/typings/gulp-rename.d.ts new file mode 100644 index 0000000000..0396670aca --- /dev/null +++ b/build/lib/typings/gulp-rename.d.ts @@ -0,0 +1,29 @@ +// Type definitions for gulp-rename +// Project: https://github.com/hparra/gulp-rename +// Definitions by: Asana +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module "gulp-rename" { + interface ParsedPath { + dirname?: string; + basename?: string; + extname?: string; + } + + interface Options extends ParsedPath { + prefix?: string; + suffix?: string; + } + + function rename(name: string): NodeJS.ReadWriteStream; + function rename(callback: (path: ParsedPath) => any): NodeJS.ReadWriteStream; + function rename(opts: Options): NodeJS.ReadWriteStream; + + /** + * This is required as per: + * https://github.com/Microsoft/TypeScript/issues/5073 + */ + namespace rename {} + + export = rename; +} \ No newline at end of file diff --git a/build/lib/typings/gulp-sourcemaps.d.ts b/build/lib/typings/gulp-sourcemaps.d.ts new file mode 100644 index 0000000000..74335a17d3 --- /dev/null +++ b/build/lib/typings/gulp-sourcemaps.d.ts @@ -0,0 +1,27 @@ +// Type definitions for gulp-sourcemaps +// Project: https://github.com/floridoo/gulp-sourcemaps +// Definitions by: Asana +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module "gulp-sourcemaps" { + interface InitOptions { + loadMaps?: boolean; + debug?: boolean; + } + + interface WriteMapper { + (file: string): string; + } + + interface WriteOptions { + addComment?: boolean; + includeContent?: boolean; + sourceRoot?: string | WriteMapper; + sourceMappingURLPrefix?: string | WriteMapper; + sourceMappingURL?: (f:{relative:string})=>string; + } + + export function init(opts?: InitOptions): NodeJS.ReadWriteStream; + export function write(path?: string, opts?: WriteOptions): NodeJS.ReadWriteStream; + export function write(opts?: WriteOptions): NodeJS.ReadWriteStream; +} \ No newline at end of file diff --git a/build/lib/typings/gulp-tsb.d.ts b/build/lib/typings/gulp-tsb.d.ts new file mode 100644 index 0000000000..600bfacf26 --- /dev/null +++ b/build/lib/typings/gulp-tsb.d.ts @@ -0,0 +1,16 @@ + + +declare module "gulp-tsb" { + + export interface ICancellationToken { + isCancellationRequested(): boolean; + } + + export interface IncrementalCompiler { + (token?:ICancellationToken): NodeJS.ReadWriteStream; + // program?: ts.Program; + } + + export function create(configOrName: { [option: string]: string | number | boolean; } | string, verbose?: boolean, json?: boolean, onError?: (message: any) => void): IncrementalCompiler; + +} \ No newline at end of file diff --git a/build/lib/typings/gulp-util.d.ts b/build/lib/typings/gulp-util.d.ts new file mode 100644 index 0000000000..f36b72080d --- /dev/null +++ b/build/lib/typings/gulp-util.d.ts @@ -0,0 +1,133 @@ +// Type definitions for gulp-util v3.0.x +// Project: https://github.com/gulpjs/gulp-util +// Definitions by: jedmao +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module 'gulp-util' { + + import vinyl = require('vinyl'); + import chalk = require('chalk'); + import through2 = require('through2'); + + export class File extends vinyl { } + + /** + * Replaces a file extension in a path. Returns the new path. + */ + export function replaceExtension(npath: string, ext: string): string; + + export var colors: typeof chalk; + + export var date: { + (now?: Date, mask?: string, convertLocalTimeToUTC?: boolean): any; + (date?: string, mask?: string, convertLocalTimeToUTC?: boolean): any; + masks: any; + }; + + /** + * Logs stuff. Already prefixed with [gulp] and all that. Use the right colors + * for values. If you pass in multiple arguments it will join them by a space. + */ + export function log(message?: any, ...optionalParams: any[]): void; + + /** + * This is a lodash.template function wrapper. You must pass in a valid gulp + * file object so it is available to the user or it will error. You can not + * configure any of the delimiters. Look at the lodash docs for more info. + */ + export function template(tmpl: string): (opt: { file: { path: string } }) => string; + export function template(tmpl: string, opt: { file: { path: string } }): string; + + export var env: any; + + export function beep(): void; + + /** + * Returns a stream that does nothing but pass data straight through. + */ + export var noop: typeof through2; + + export function isStream(obj: any): boolean; + + export function isBuffer(obj: any): boolean; + + export function isNull(obj: any): boolean; + + export var linefeed: string; + + export function combine(streams: NodeJS.ReadWriteStream[]): () => NodeJS.ReadWriteStream; + export function combine(...streams: NodeJS.ReadWriteStream[]): () => NodeJS.ReadWriteStream; + + /** + * This is similar to es.wait but instead of buffering text into one string + * it buffers anything into an array (so very useful for file objects). + */ + export function buffer(cb?: (err: Error, data: any[]) => void): NodeJS.ReadWriteStream; + + export class PluginError implements Error, PluginErrorOptions { + constructor(options?: PluginErrorOptions); + constructor(pluginName: string, options?: PluginErrorOptions); + constructor(pluginName: string, message: string, options?: PluginErrorOptions); + constructor(pluginName: string, message: Error, options?: PluginErrorOptions); + /** + * The module name of your plugin. + */ + name: string; + /** + * Can be a string or an existing error. + */ + message: any; + fileName: string; + lineNumber: number; + /** + * You need to include the message along with this stack. If you pass an + * error in as the message the stack will be pulled from that, otherwise one + * will be created. + */ + stack: string; + /** + * By default the stack will not be shown. Set this to true if you think the + * stack is important for your error. + */ + showStack: boolean; + /** + * Error properties will be included in err.toString(). Can be omitted by + * setting this to false. + */ + showProperties: boolean; + plugin: string; + error: Error; + } + +} + +interface PluginErrorOptions { + /** + * The module name of your plugin. + */ + name?: string; + /** + * Can be a string or an existing error. + */ + message?: any; + fileName?: string; + lineNumber?: number; + /** + * You need to include the message along with this stack. If you pass an + * error in as the message the stack will be pulled from that, otherwise one + * will be created. + */ + stack?: string; + /** + * By default the stack will not be shown. Set this to true if you think the + * stack is important for your error. + */ + showStack?: boolean; + /** + * Error properties will be included in err.toString(). Can be omitted by + * setting this to false. + */ + showProperties?: boolean; + plugin?: string; + error?: Error; +} \ No newline at end of file diff --git a/build/lib/typings/gulp.d.ts b/build/lib/typings/gulp.d.ts new file mode 100644 index 0000000000..cf9649ea59 --- /dev/null +++ b/build/lib/typings/gulp.d.ts @@ -0,0 +1,293 @@ +// Type definitions for Gulp v3.8.x +// Project: http://gulpjs.com +// Definitions by: Drew Noakes +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module "gulp" { + import Orchestrator = require("orchestrator"); + import VinylFile = require("vinyl"); + + namespace gulp { + interface Gulp extends Orchestrator { + /** + * Define a task + * @param name The name of the task. + * @param deps An array of task names to be executed and completed before your task will run. + * @param fn The function that performs the task's operations. For asynchronous tasks, you need to provide a hint when the task is complete: + *
    + *
  • Take in a callback
  • + *
  • Return a stream or a promise
  • + *
+ */ + task: Orchestrator.AddMethod; + /** + * Emits files matching provided glob or an array of globs. Returns a stream of Vinyl files that can be piped to plugins. + * @param glob Glob or array of globs to read. + * @param opt Options to pass to node-glob through glob-stream. + */ + src: SrcMethod; + /** + * Can be piped to and it will write files. Re-emits all data passed to it so you can pipe to multiple folders. + * Folders that don't exist will be created. + * + * @param outFolder The path (output folder) to write files to. Or a function that returns it, the function will be provided a vinyl File instance. + * @param opt + */ + dest: DestMethod; + /** + * Watch files and do something when a file changes. This always returns an EventEmitter that emits change events. + * + * @param glob a single glob or array of globs that indicate which files to watch for changes. + * @param opt options, that are passed to the gaze library. + * @param fn a callback or array of callbacks to be called on each change, or names of task(s) to run when a file changes, added with task(). + */ + watch: WatchMethod; + } + + interface GulpPlugin { + (...args: any[]): NodeJS.ReadWriteStream; + } + + interface WatchMethod { + /** + * Watch files and do something when a file changes. This always returns an EventEmitter that emits change events. + * + * @param glob a single glob or array of globs that indicate which files to watch for changes. + * @param fn a callback or array of callbacks to be called on each change, or names of task(s) to run when a file changes, added with task(). + */ + (glob: string|string[], fn: (WatchCallback|string)): NodeJS.EventEmitter; + /** + * Watch files and do something when a file changes. This always returns an EventEmitter that emits change events. + * + * @param glob a single glob or array of globs that indicate which files to watch for changes. + * @param fn a callback or array of callbacks to be called on each change, or names of task(s) to run when a file changes, added with task(). + */ + (glob: string|string[], fn: (WatchCallback|string)[]): NodeJS.EventEmitter; + /** + * Watch files and do something when a file changes. This always returns an EventEmitter that emits change events. + * + * @param glob a single glob or array of globs that indicate which files to watch for changes. + * @param opt options, that are passed to the gaze library. + * @param fn a callback or array of callbacks to be called on each change, or names of task(s) to run when a file changes, added with task(). + */ + (glob: string|string[], opt: WatchOptions, fn: (WatchCallback|string)): NodeJS.EventEmitter; + /** + * Watch files and do something when a file changes. This always returns an EventEmitter that emits change events. + * + * @param glob a single glob or array of globs that indicate which files to watch for changes. + * @param opt options, that are passed to the gaze library. + * @param fn a callback or array of callbacks to be called on each change, or names of task(s) to run when a file changes, added with task(). + */ + (glob: string|string[], opt: WatchOptions, fn: (WatchCallback|string)[]): NodeJS.EventEmitter; + + } + + interface DestMethod { + /** + * Can be piped to and it will write files. Re-emits all data passed to it so you can pipe to multiple folders. + * Folders that don't exist will be created. + * + * @param outFolder The path (output folder) to write files to. Or a function that returns it, the function will be provided a vinyl File instance. + * @param opt + */ + (outFolder: string|((file: VinylFile) => string), opt?: DestOptions): NodeJS.ReadWriteStream; + } + + interface SrcMethod { + /** + * Emits files matching provided glob or an array of globs. Returns a stream of Vinyl files that can be piped to plugins. + * @param glob Glob or array of globs to read. + * @param opt Options to pass to node-glob through glob-stream. + */ + (glob: string|string[], opt?: SrcOptions): NodeJS.ReadWriteStream; + } + + /** + * Options to pass to node-glob through glob-stream. + * Specifies two options in addition to those used by node-glob: + * https://github.com/isaacs/node-glob#options + */ + interface SrcOptions { + /** + * Setting this to false will return file.contents as null + * and not read the file at all. + * Default: true. + */ + read?: boolean; + + /** + * Setting this to false will return file.contents as a stream and not buffer files. + * This is useful when working with large files. + * Note: Plugins might not implement support for streams. + * Default: true. + */ + buffer?: boolean; + + /** + * The base path of a glob. + * + * Default is everything before a glob starts. + */ + base?: string; + + /** + * The current working directory in which to search. + * Defaults to process.cwd(). + */ + cwd?: string; + + /** + * The place where patterns starting with / will be mounted onto. + * Defaults to path.resolve(options.cwd, "/") (/ on Unix systems, and C:\ or some such on Windows.) + */ + root?: string; + + /** + * Include .dot files in normal matches and globstar matches. + * Note that an explicit dot in a portion of the pattern will always match dot files. + */ + dot?: boolean; + + /** + * Set to match only fles, not directories. Set this flag to prevent copying empty directories + */ + nodir?: boolean; + + /** + * By default, a pattern starting with a forward-slash will be "mounted" onto the root setting, so that a valid + * filesystem path is returned. Set this flag to disable that behavior. + */ + nomount?: boolean; + + /** + * Add a / character to directory matches. Note that this requires additional stat calls. + */ + mark?: boolean; + + /** + * Don't sort the results. + */ + nosort?: boolean; + + /** + * Set to true to stat all results. This reduces performance somewhat, and is completely unnecessary, unless + * readdir is presumed to be an untrustworthy indicator of file existence. It will cause ELOOP to be triggered one + * level sooner in the case of cyclical symbolic links. + */ + stat?: boolean; + + /** + * When an unusual error is encountered when attempting to read a directory, a warning will be printed to stderr. + * Set the silent option to true to suppress these warnings. + */ + silent?: boolean; + + /** + * When an unusual error is encountered when attempting to read a directory, the process will just continue on in + * search of other matches. Set the strict option to raise an error in these cases. + */ + strict?: boolean; + + /** + * See cache property above. Pass in a previously generated cache object to save some fs calls. + */ + cache?: boolean; + + /** + * A cache of results of filesystem information, to prevent unnecessary stat calls. + * While it should not normally be necessary to set this, you may pass the statCache from one glob() call to the + * options object of another, if you know that the filesystem will not change between calls. + */ + statCache?: boolean; + + /** + * Perform a synchronous glob search. + */ + sync?: boolean; + + /** + * In some cases, brace-expanded patterns can result in the same file showing up multiple times in the result set. + * By default, this implementation prevents duplicates in the result set. Set this flag to disable that behavior. + */ + nounique?: boolean; + + /** + * Set to never return an empty set, instead returning a set containing the pattern itself. + * This is the default in glob(3). + */ + nonull?: boolean; + + /** + * Perform a case-insensitive match. Note that case-insensitive filesystems will sometimes result in glob returning + * results that are case-insensitively matched anyway, since readdir and stat will not raise an error. + */ + nocase?: boolean; + + /** + * Set to enable debug logging in minimatch and glob. + */ + debug?: boolean; + + /** + * Set to enable debug logging in glob, but not minimatch. + */ + globDebug?: boolean; + } + + interface DestOptions { + /** + * The output folder. Only has an effect if provided output folder is relative. + * Default: process.cwd() + */ + cwd?: string; + + /** + * Octal permission string specifying mode for any folders that need to be created for output folder. + * Default: 0777. + */ + mode?: string; + } + + /** + * Options that are passed to gaze. + * https://github.com/shama/gaze + */ + interface WatchOptions { + /** Interval to pass to fs.watchFile. */ + interval?: number; + /** Delay for events called in succession for the same file/event. */ + debounceDelay?: number; + /** Force the watch mode. Either 'auto' (default), 'watch' (force native events), or 'poll' (force stat polling). */ + mode?: string; + /** The current working directory to base file patterns from. Default is process.cwd().. */ + cwd?: string; + } + + interface WatchEvent { + /** The type of change that occurred, either added, changed or deleted. */ + type: string; + /** The path to the file that triggered the event. */ + path: string; + } + + /** + * Callback to be called on each watched file change. + */ + interface WatchCallback { + (event: WatchEvent): void; + } + + interface TaskCallback { + /** + * Defines a task. + * Tasks may be made asynchronous if they are passing a callback or return a promise or a stream. + * @param cb callback used to signal asynchronous completion. Caller includes err in case of error. + */ + (cb?: (err?: any) => void): any; + } + } + + var gulp: gulp.Gulp; + + export = gulp; +} \ No newline at end of file diff --git a/build/lib/typings/is.d.ts b/build/lib/typings/is.d.ts new file mode 100644 index 0000000000..66e7501c03 --- /dev/null +++ b/build/lib/typings/is.d.ts @@ -0,0 +1,11 @@ +declare module 'is' { + function a(value: any, type: string): boolean; + function defined(value: any): boolean; + function undef(value: any): boolean; + function object(value: any): boolean; + function string(value: any): value is string; + function boolean(value: any): boolean; + function array(value: any): boolean; + function empty(value: Object | Array): boolean; + function equal | Function | Date>(value: T, other: T): boolean; +} \ No newline at end of file diff --git a/build/lib/typings/lazy.js.d.ts b/build/lib/typings/lazy.js.d.ts new file mode 100644 index 0000000000..28761b8103 --- /dev/null +++ b/build/lib/typings/lazy.js.d.ts @@ -0,0 +1,273 @@ +// Type definitions for Lazy.js 0.3.2 +// Project: https://github.com/dtao/lazy.js/ +// Definitions by: Bart van der Schoor +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +declare function Lazy(value: string): Lazy.StringLikeSequence; +declare function Lazy(value: T[]): Lazy.ArrayLikeSequence; +declare function Lazy(value: any[]): Lazy.ArrayLikeSequence; +declare function Lazy(value: Object): Lazy.ObjectLikeSequence; +declare function Lazy(value: Object): Lazy.ObjectLikeSequence; + +declare module Lazy { + function strict(): StrictLazy; + function generate(generatorFn: GeneratorCallback, length?: number): GeneratedSequence; + function range(to: number): GeneratedSequence; + function range(from: number, to: number, step?: number): GeneratedSequence; + function repeat(value: T, count?: number): GeneratedSequence; + function on(eventType: string): Sequence; + function readFile(path: string): StringLikeSequence; + function makeHttpRequest(path: string): StringLikeSequence; + + interface StrictLazy { + (value: string): StringLikeSequence; + (value: T[]): ArrayLikeSequence; + (value: any[]): ArrayLikeSequence; + (value: Object): ObjectLikeSequence; + (value: Object): ObjectLikeSequence; + strict(): StrictLazy; + generate(generatorFn: GeneratorCallback, length?: number): GeneratedSequence; + range(to: number): GeneratedSequence; + range(from: number, to: number, step?: number): GeneratedSequence; + repeat(value: T, count?: number): GeneratedSequence; + on(eventType: string): Sequence; + readFile(path: string): StringLikeSequence; + makeHttpRequest(path: string): StringLikeSequence; + } + + interface ArrayLike { + length: number; + [index: number]: T; + } + + interface Callback { + (): void; + } + + interface ErrorCallback { + (error: any): void; + } + + interface ValueCallback { + (value: T): void; + } + + interface GetKeyCallback { + (value: T): string; + } + + interface TestCallback { + (value: T): boolean; + } + + interface MapCallback { + (value: T): U; + } + + interface MapStringCallback { + (value: string): string; + } + + interface NumberCallback { + (value: T): number; + } + + interface MemoCallback { + (memo: U, value: T): U; + } + + interface GeneratorCallback { + (index: number): T; + } + + interface CompareCallback { + (x: any, y: any): number; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + interface Iterator { + new (sequence: Sequence): Iterator; + current(): T; + moveNext(): boolean; + } + + interface GeneratedSequence extends Sequence { + new(generatorFn: GeneratorCallback, length: number): GeneratedSequence; + length(): number; + } + + interface AsyncSequence extends SequenceBase { + each(callback: ValueCallback): AsyncHandle; + } + + interface AsyncHandle { + cancel(): void; + onComplete(callback: Callback): void; + onError(callback: ErrorCallback): void; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + module Sequence { + function define(methodName: string[], overrides: Object): Function; + } + + interface Sequence extends SequenceBase { + each(eachFn: ValueCallback): Sequence; + } + + interface ArraySequence extends SequenceBase { + flatten(): Sequence; + } + + interface SequenceBase extends SequenceBaser { + first(): any; + first(count: number): Sequence; + indexOf(value: any, startIndex?: number): Sequence; + + last(): any; + last(count: number): Sequence; + lastIndexOf(value: any): Sequence; + + reverse(): Sequence; + } + + interface SequenceBaser { + // TODO improve define() (needs ugly overload) + async(interval: number): AsyncSequence; + chunk(size: number): Sequence; + compact(): Sequence; + concat(var_args: T[]): Sequence; + concat(sequence: Sequence): Sequence; + consecutive(length: number): Sequence; + contains(value: T): boolean; + countBy(keyFn: GetKeyCallback): ObjectLikeSequence; + countBy(propertyName: string): ObjectLikeSequence; + dropWhile(predicateFn: TestCallback): Sequence; + every(predicateFn: TestCallback): boolean; + filter(predicateFn: TestCallback): Sequence; + find(predicateFn: TestCallback): Sequence; + findWhere(properties: Object): Sequence; + + groupBy(keyFn: GetKeyCallback): ObjectLikeSequence; + initial(count?: number): Sequence; + intersection(var_args: T[]): Sequence; + invoke(methodName: string): Sequence; + isEmpty(): boolean; + join(delimiter?: string): string; + map(mapFn: MapCallback): ArraySequence; + map(mapFn: MapCallback): Sequence; + + max(valueFn?: NumberCallback): T; + min(valueFn?: NumberCallback): T; + none(valueFn?: TestCallback): boolean; + pluck(propertyName: string): Sequence; + reduce(aggregatorFn: MemoCallback, memo?: U): U; + reduceRight(aggregatorFn: MemoCallback, memo: U): U; + reject(predicateFn: TestCallback): Sequence; + rest(count?: number): Sequence; + shuffle(): Sequence; + some(predicateFn?: TestCallback): boolean; + sort(sortFn?: CompareCallback, descending?: boolean): Sequence; + sortBy(sortFn: string, descending?: boolean): Sequence; + sortBy(sortFn: NumberCallback, descending?: boolean): Sequence; + sortedIndex(value: T): Sequence; + size(): number; + sum(valueFn?: NumberCallback): Sequence; + takeWhile(predicateFn: TestCallback): Sequence; + union(var_args: T[]): Sequence; + uniq(): Sequence; + where(properties: Object): Sequence; + without(...var_args: T[]): Sequence; + without(var_args: T[]): Sequence; + zip(var_args: T[]): ArraySequence; + + toArray(): T[]; + toObject(): Object; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + module ArrayLikeSequence { + function define(methodName: string[], overrides: Object): Function; + } + + interface ArrayLikeSequence extends Sequence { + // define()X; + concat(var_args: T[]): ArrayLikeSequence; + concat(sequence: Sequence): Sequence; + first(count?: number): ArrayLikeSequence; + get(index: number): T; + length(): number; + map(mapFn: MapCallback): ArraySequence; + map(mapFn: MapCallback): ArrayLikeSequence; + pop(): ArrayLikeSequence; + rest(count?: number): ArrayLikeSequence; + reverse(): ArrayLikeSequence; + shift(): ArrayLikeSequence; + slice(begin: number, end?: number): ArrayLikeSequence; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + module ObjectLikeSequence { + function define(methodName: string[], overrides: Object): Function; + } + + interface ObjectLikeSequence extends Sequence { + assign(other: Object): ObjectLikeSequence; + // throws error + //async(): X; + defaults(defaults: Object): ObjectLikeSequence; + functions(): Sequence; + get(property: string): ObjectLikeSequence; + invert(): ObjectLikeSequence; + keys(): Sequence; + omit(properties: string[]): ObjectLikeSequence; + pairs(): Sequence; + pick(properties: string[]): ObjectLikeSequence; + toArray(): T[]; + toObject(): Object; + values(): Sequence; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + module StringLikeSequence { + function define(methodName: string[], overrides: Object): Function; + } + + interface StringLikeSequence extends SequenceBaser { + charAt(index: number): string; + charCodeAt(index: number): number; + contains(value: string): boolean; + endsWith(suffix: string): boolean; + + first(): string; + first(count: number): StringLikeSequence; + + indexOf(substring: string, startIndex?: number): number; + + last(): string; + last(count: number): StringLikeSequence; + + lastIndexOf(substring: string, startIndex?: number): number; + mapString(mapFn: MapStringCallback): StringLikeSequence; + match(pattern: RegExp): StringLikeSequence; + reverse(): StringLikeSequence; + + split(delimiter: string): StringLikeSequence; + split(delimiter: RegExp): StringLikeSequence; + + startsWith(prefix: string): boolean; + substring(start: number, stop?: number): StringLikeSequence; + toLowerCase(): StringLikeSequence; + toUpperCase(): StringLikeSequence; + } +} + +declare module 'lazy.js' { + export = Lazy; +} + diff --git a/build/lib/typings/minimatch.d.ts b/build/lib/typings/minimatch.d.ts new file mode 100644 index 0000000000..90f2a560c1 --- /dev/null +++ b/build/lib/typings/minimatch.d.ts @@ -0,0 +1,64 @@ +// Type definitions for Minimatch 2.0.8 +// Project: https://github.com/isaacs/minimatch +// Definitions by: vvakame +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module "minimatch" { + + function M(target: string, pattern: string, options?: M.IOptions): boolean; + + namespace M { + function match(list: string[], pattern: string, options?: IOptions): string[]; + function filter(pattern: string, options?: IOptions): (element: string, indexed: number, array: string[]) => boolean; + function makeRe(pattern: string, options?: IOptions): RegExp; + + var Minimatch: IMinimatchStatic; + + interface IOptions { + debug?: boolean; + nobrace?: boolean; + noglobstar?: boolean; + dot?: boolean; + noext?: boolean; + nocase?: boolean; + nonull?: boolean; + matchBase?: boolean; + nocomment?: boolean; + nonegate?: boolean; + flipNegate?: boolean; + } + + interface IMinimatchStatic { + new (pattern: string, options?: IOptions): IMinimatch; + prototype: IMinimatch; + } + + interface IMinimatch { + pattern: string; + options: IOptions; + /** 2-dimensional array of regexp or string expressions. */ + set: any[][]; // (RegExp | string)[][] + regexp: RegExp; + negate: boolean; + comment: boolean; + empty: boolean; + + makeRe(): RegExp; // regexp or boolean + match(fname: string): boolean; + matchOne(files: string[], pattern: string[], partial: boolean): boolean; + + /** Deprecated. For internal use. */ + debug(): void; + /** Deprecated. For internal use. */ + make(): void; + /** Deprecated. For internal use. */ + parseNegate(): void; + /** Deprecated. For internal use. */ + braceExpand(pattern: string, options: IOptions): void; + /** Deprecated. For internal use. */ + parse(pattern: string, isSub?: boolean): void; + } + } + + export = M; +} \ No newline at end of file diff --git a/build/lib/typings/object-assign.d.ts b/build/lib/typings/object-assign.d.ts new file mode 100644 index 0000000000..3bf040afa8 --- /dev/null +++ b/build/lib/typings/object-assign.d.ts @@ -0,0 +1,4 @@ +declare module 'object-assign' { + function fn(target: any, ...sources: any[]): any; + export = fn; +} \ No newline at end of file diff --git a/build/lib/typings/orchestrator.d.ts b/build/lib/typings/orchestrator.d.ts new file mode 100644 index 0000000000..48dfdaae5e --- /dev/null +++ b/build/lib/typings/orchestrator.d.ts @@ -0,0 +1,125 @@ +// Type definitions for Orchestrator +// Project: https://github.com/orchestrator/orchestrator +// Definitions by: Qubo +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare type Strings = string|string[]; + +declare module "orchestrator" { + class Orchestrator { + add: Orchestrator.AddMethod; + /** + * Have you defined a task with this name? + * @param name The task name to query + */ + hasTask(name: string): boolean; + start: Orchestrator.StartMethod; + stop(): void; + + /** + * Listen to orchestrator internals + * @param event Event name to listen to: + *
    + *
  • start: from start() method, shows you the task sequence + *
  • stop: from stop() method, the queue finished successfully + *
  • err: from stop() method, the queue was aborted due to a task error + *
  • task_start: from _runTask() method, task was started + *
  • task_stop: from _runTask() method, task completed successfully + *
  • task_err: from _runTask() method, task errored + *
  • task_not_found: from start() method, you're trying to start a task that doesn't exist + *
  • task_recursion: from start() method, there are recursive dependencies in your task list + *
+ * @param cb Passes single argument: e: event details + */ + on(event: string, cb: (e: Orchestrator.OnCallbackEvent) => any): Orchestrator; + + /** + * Listen to all orchestrator events from one callback + * @param cb Passes single argument: e: event details + */ + onAll(cb: (e: Orchestrator.OnAllCallbackEvent) => any): void; + } + + namespace Orchestrator { + interface AddMethodCallback { + /** + * Accept a callback + * @param callback + */ + (callback?: Function): any; + /** + * Return a promise + */ + (): Q.Promise; + /** + * Return a stream: (task is marked complete when stream ends) + */ + (): any; //TODO: stream type should be here e.g. map-stream + } + + /** + * Define a task + */ + interface AddMethod { + /** + * Define a task + * @param name The name of the task. + * @param deps An array of task names to be executed and completed before your task will run. + * @param fn The function that performs the task's operations. For asynchronous tasks, you need to provide a hint when the task is complete: + *
    + *
  • Take in a callback
  • + *
  • Return a stream or a promise
  • + *
+ */ + (name: string, deps?: string[], fn?: AddMethodCallback|Function): Orchestrator; + /** + * Define a task + * @param name The name of the task. + * @param fn The function that performs the task's operations. For asynchronous tasks, you need to provide a hint when the task is complete: + *
    + *
  • Take in a callback
  • + *
  • Return a stream or a promise
  • + *
+ */ + (name: string, fn?: AddMethodCallback|Function): Orchestrator; + } + + /** + * Start running the tasks + */ + interface StartMethod { + /** + * Start running the tasks + * @param tasks Tasks to be executed. You may pass any number of tasks as individual arguments. + * @param cb Callback to call after run completed. + */ + (tasks: Strings, cb?: (error?: any) => any): Orchestrator; + /** + * Start running the tasks + * @param tasks Tasks to be executed. You may pass any number of tasks as individual arguments. + * @param cb Callback to call after run completed. + */ + (...tasks: Strings[]/*, cb?: (error: any) => any */): Orchestrator; + //TODO: TypeScript 1.5.3 cannot express varargs followed by callback as a last argument... + (task1: Strings, task2: Strings, cb?: (error?: any) => any): Orchestrator; + (task1: Strings, task2: Strings, task3: Strings, cb?: (error?: any) => any): Orchestrator; + (task1: Strings, task2: Strings, task3: Strings, task4: Strings, cb?: (error?: any) => any): Orchestrator; + (task1: Strings, task2: Strings, task3: Strings, task4: Strings, task5: Strings, cb?: (error?: any) => any): Orchestrator; + (task1: Strings, task2: Strings, task3: Strings, task4: Strings, task5: Strings, task6: Strings, cb?: (error?: any) => any): Orchestrator; + } + + interface OnCallbackEvent { + message: string; + task: string; + err: any; + duration?: number; + } + + interface OnAllCallbackEvent extends OnCallbackEvent { + src: string; + } + + } + + export = Orchestrator; +} \ No newline at end of file diff --git a/build/lib/typings/pump.d.ts b/build/lib/typings/pump.d.ts new file mode 100644 index 0000000000..2542552a1c --- /dev/null +++ b/build/lib/typings/pump.d.ts @@ -0,0 +1,23 @@ +declare module 'pump' { + function f( + str1:NodeJS.WritableStream, + str2:NodeJS.WritableStream, + str3:NodeJS.WritableStream, + str4:NodeJS.WritableStream, + str5:NodeJS.WritableStream, + str6:NodeJS.WritableStream, + str7:NodeJS.WritableStream, + str8:NodeJS.WritableStream, + str9:NodeJS.WritableStream, + str10:NodeJS.WritableStream, + cb:(err:any)=>void + ): NodeJS.WritableStream; + + /** + * This is required as per: + * https://github.com/Microsoft/TypeScript/issues/5073 + */ + namespace f {} + + export = f; +} \ No newline at end of file diff --git a/build/lib/typings/rimraf.d.ts b/build/lib/typings/rimraf.d.ts new file mode 100644 index 0000000000..3f3416b4dd --- /dev/null +++ b/build/lib/typings/rimraf.d.ts @@ -0,0 +1,17 @@ +// Type definitions for rimraf +// Project: https://github.com/isaacs/rimraf +// Definitions by: Carlos Ballesteros Velasco +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +// Imported from: https://github.com/soywiz/typescript-node-definitions/rimraf.d.ts + +declare module "rimraf" { + function rimraf(path: string, callback: (error: Error) => void): void; + function rimraf(path: string, opts:{}, callback: (error: Error) => void): void; + namespace rimraf { + export function sync(path: string): void; + export var EMFILE_MAX: number; + export var BUSYTRIES_MAX: number; + } + export = rimraf; +} \ No newline at end of file diff --git a/build/lib/typings/source-map.d.ts b/build/lib/typings/source-map.d.ts new file mode 100644 index 0000000000..ab5752fd34 --- /dev/null +++ b/build/lib/typings/source-map.d.ts @@ -0,0 +1,91 @@ +// Type definitions for source-map v0.1.38 +// Project: https://github.com/mozilla/source-map +// Definitions by: Morten Houston Ludvigsen +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare namespace SourceMap { + interface StartOfSourceMap { + file?: string; + sourceRoot?: string; + } + + interface RawSourceMap extends StartOfSourceMap { + version: string|number; + sources: Array; + names: Array; + sourcesContent?: string[]; + mappings: string; + } + + interface Position { + line: number; + column: number; + } + + interface MappedPosition extends Position { + source: string; + name?: string; + } + + interface MappingItem { + source: string; + generatedLine: number; + generatedColumn: number; + originalLine: number; + originalColumn: number; + name: string; + } + + interface Mapping { + generated: Position; + original: Position; + source: string; + name?: string; + } + + interface CodeWithSourceMap { + code: string; + map: SourceMapGenerator; + } + + class SourceMapConsumer { + public static GENERATED_ORDER: number; + public static ORIGINAL_ORDER: number; + + constructor(rawSourceMap: RawSourceMap); + public originalPositionFor(generatedPosition: Position): MappedPosition; + public generatedPositionFor(originalPosition: MappedPosition): Position; + public sourceContentFor(source: string): string; + public eachMapping(callback: (mapping: MappingItem) => void, context?: any, order?: number): void; + } + + class SourceMapGenerator { + constructor(startOfSourceMap?: StartOfSourceMap); + public static fromSourceMap(sourceMapConsumer: SourceMapConsumer): SourceMapGenerator; + public addMapping(mapping: Mapping): void; + public setSourceContent(sourceFile: string, sourceContent: string): void; + public applySourceMap(sourceMapConsumer: SourceMapConsumer, sourceFile?: string, sourceMapPath?: string): void; + public toString(): string; + public toJSON(): RawSourceMap; + } + + class SourceNode { + constructor(); + constructor(line: number, column: number, source: string); + constructor(line: number, column: number, source: string, chunk?: string, name?: string); + public static fromStringWithSourceMap(code: string, sourceMapConsumer: SourceMapConsumer, relativePath?: string): SourceNode; + public add(chunk: any): SourceNode; + public prepend(chunk: any): SourceNode; + public setSourceContent(sourceFile: string, sourceContent: string): void; + public walk(fn: (chunk: string, mapping: MappedPosition) => void): void; + public walkSourceContents(fn: (file: string, content: string) => void): void; + public join(sep: string): SourceNode; + public replaceRight(pattern: string, replacement: string): SourceNode; + public toString(): string; + public toStringWithSourceMap(startOfSourceMap?: StartOfSourceMap): CodeWithSourceMap; + } +} + +declare module 'source-map' { + export = SourceMap; +} \ No newline at end of file diff --git a/build/lib/typings/through.d.ts b/build/lib/typings/through.d.ts new file mode 100644 index 0000000000..254b844fb2 --- /dev/null +++ b/build/lib/typings/through.d.ts @@ -0,0 +1,22 @@ +// Type definitions for through +// Project: https://github.com/dominictarr/through +// Definitions by: Andrew Gaspar +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +declare module "through" { + import stream = require("stream"); + + function through(write?: (data:any) => void, + end?: () => void, + opts?: { + autoDestroy: boolean; + }): through.ThroughStream; + + module through { + export interface ThroughStream extends stream.Transform { + autoDestroy: boolean; + } + } + + export = through; +} \ No newline at end of file diff --git a/build/lib/typings/through2.d.ts b/build/lib/typings/through2.d.ts new file mode 100644 index 0000000000..1d92009899 --- /dev/null +++ b/build/lib/typings/through2.d.ts @@ -0,0 +1,38 @@ +// Type definitions for through2 v 2.0.0 +// Project: https://github.com/rvagg/through2 +// Definitions by: Bart van der Schoor , jedmao , Georgios Valotasios , Ben Chauvette +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module 'through2' { + + import stream = require('stream'); + + type TransformCallback = (err?: any, data?: any) => void; + type TransformFunction = (chunk: any, enc: string, callback: TransformCallback) => void; + type FlushCallback = (flushCallback: () => void) => void; + + function through2(transform?: TransformFunction, flush?: FlushCallback): stream.Transform; + + function through2(opts?: stream.DuplexOptions, transform?: TransformFunction, flush?: FlushCallback): stream.Transform; + + namespace through2 { + export interface Through2Constructor extends stream.Transform { + new(opts?: stream.DuplexOptions): stream.Transform; + (opts?: stream.DuplexOptions): stream.Transform; + } + + /** + * Convenvience method for creating object streams + */ + export function obj(transform?: TransformFunction, flush?: FlushCallback): stream.Transform; + + /** + * Creates a constructor for a custom Transform. This is useful when you + * want to use the same transform logic in multiple instances. + */ + export function ctor(opts?: stream.DuplexOptions, transfrom?: TransformFunction, flush?: FlushCallback): Through2Constructor; + } + + export = through2; + +} \ No newline at end of file diff --git a/build/lib/typings/underscore.d.ts b/build/lib/typings/underscore.d.ts new file mode 100644 index 0000000000..2e4d3d9b99 --- /dev/null +++ b/build/lib/typings/underscore.d.ts @@ -0,0 +1,6086 @@ +// Type definitions for Underscore 1.8.3 +// Project: http://underscorejs.org/ +// Definitions by: Boris Yankov , Josh Baldwin , Christopher Currens +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module _ { + /** + * underscore.js _.throttle options. + **/ + interface ThrottleSettings { + + /** + * If you'd like to disable the leading-edge call, pass this as false. + **/ + leading?: boolean; + + /** + * If you'd like to disable the execution on the trailing-edge, pass false. + **/ + trailing?: boolean; + } + + /** + * underscore.js template settings, set templateSettings or pass as an argument + * to 'template()' to override defaults. + **/ + interface TemplateSettings { + /** + * Default value is '/<%([\s\S]+?)%>/g'. + **/ + evaluate?: RegExp; + + /** + * Default value is '/<%=([\s\S]+?)%>/g'. + **/ + interpolate?: RegExp; + + /** + * Default value is '/<%-([\s\S]+?)%>/g'. + **/ + escape?: RegExp; + + /** + * By default, 'template()' places the values from your data in the local scope via the 'with' statement. + * However, you can specify a single variable name with this setting. + **/ + variable?: string; + } + + interface Collection { } + + // Common interface between Arrays and jQuery objects + interface List extends Collection { + [index: number]: T; + length: number; + } + + interface Dictionary extends Collection { + [index: string]: T; + } + + interface ListIterator { + (value: T, index: number, list: List): TResult; + } + + interface ObjectIterator { + (element: T, key: string, list: Dictionary): TResult; + } + + interface MemoIterator { + (prev: TResult, curr: T, index: number, list: List): TResult; + } + + interface MemoObjectIterator { + (prev: TResult, curr: T, key: string, list: Dictionary): TResult; + } + + interface Cancelable { + cancel() : void; + } +} + +interface UnderscoreStatic { + /** + * Underscore OOP Wrapper, all Underscore functions that take an object + * as the first parameter can be invoked through this function. + * @param key First argument to Underscore object functions. + **/ + (value: _.Dictionary): Underscore; + (value: Array): Underscore; + (value: T): Underscore; + + /* ************* + * Collections * + ************* */ + + /** + * Iterates over a list of elements, yielding each in turn to an iterator function. The iterator is + * bound to the context object, if one is passed. Each invocation of iterator is called with three + * arguments: (element, index, list). If list is a JavaScript object, iterator's arguments will be + * (value, key, object). Delegates to the native forEach function if it exists. + * @param list Iterates over this list of elements. + * @param iterator Iterator function for each element `list`. + * @param context 'this' object in `iterator`, optional. + **/ + each( + list: _.List, + iterator: _.ListIterator, + context?: any): _.List; + + /** + * @see _.each + * @param object Iterates over properties of this object. + * @param iterator Iterator function for each property on `object`. + * @param context 'this' object in `iterator`, optional. + **/ + each( + object: _.Dictionary, + iterator: _.ObjectIterator, + context?: any): _.Dictionary; + + /** + * @see _.each + **/ + forEach( + list: _.List, + iterator: _.ListIterator, + context?: any): _.List; + + /** + * @see _.each + **/ + forEach( + object: _.Dictionary, + iterator: _.ObjectIterator, + context?: any): _.Dictionary; + + /** + * Produces a new array of values by mapping each value in list through a transformation function + * (iterator). If the native map method exists, it will be used instead. If list is a JavaScript + * object, iterator's arguments will be (value, key, object). + * @param list Maps the elements of this array. + * @param iterator Map iterator function for each element in `list`. + * @param context `this` object in `iterator`, optional. + * @return The mapped array result. + **/ + map( + list: _.List, + iterator: _.ListIterator, + context?: any): TResult[]; + + /** + * @see _.map + * @param object Maps the properties of this object. + * @param iterator Map iterator function for each property on `object`. + * @param context `this` object in `iterator`, optional. + * @return The mapped object result. + **/ + map( + object: _.Dictionary, + iterator: _.ObjectIterator, + context?: any): TResult[]; + + /** + * @see _.map + **/ + collect( + list: _.List, + iterator: _.ListIterator, + context?: any): TResult[]; + + /** + * @see _.map + **/ + collect( + object: _.Dictionary, + iterator: _.ObjectIterator, + context?: any): TResult[]; + + /** + * Also known as inject and foldl, reduce boils down a list of values into a single value. + * Memo is the initial state of the reduction, and each successive step of it should be + * returned by iterator. The iterator is passed four arguments: the memo, then the value + * and index (or key) of the iteration, and finally a reference to the entire list. + * @param list Reduces the elements of this array. + * @param iterator Reduce iterator function for each element in `list`. + * @param memo Initial reduce state. + * @param context `this` object in `iterator`, optional. + * @return Reduced object result. + **/ + reduce( + list: _.Collection, + iterator: _.MemoIterator, + memo?: TResult, + context?: any): TResult; + + reduce( + list: _.Dictionary, + iterator: _.MemoObjectIterator, + memo?: TResult, + context?: any): TResult; + + /** + * @see _.reduce + **/ + inject( + list: _.Collection, + iterator: _.MemoIterator, + memo?: TResult, + context?: any): TResult; + + /** + * @see _.reduce + **/ + foldl( + list: _.Collection, + iterator: _.MemoIterator, + memo?: TResult, + context?: any): TResult; + + /** + * The right-associative version of reduce. Delegates to the JavaScript 1.8 version of + * reduceRight, if it exists. `foldr` is not as useful in JavaScript as it would be in a + * language with lazy evaluation. + * @param list Reduces the elements of this array. + * @param iterator Reduce iterator function for each element in `list`. + * @param memo Initial reduce state. + * @param context `this` object in `iterator`, optional. + * @return Reduced object result. + **/ + reduceRight( + list: _.Collection, + iterator: _.MemoIterator, + memo?: TResult, + context?: any): TResult; + + /** + * @see _.reduceRight + **/ + foldr( + list: _.Collection, + iterator: _.MemoIterator, + memo?: TResult, + context?: any): TResult; + + /** + * Looks through each value in the list, returning the first one that passes a truth + * test (iterator). The function returns as soon as it finds an acceptable element, + * and doesn't traverse the entire list. + * @param list Searches for a value in this list. + * @param iterator Search iterator function for each element in `list`. + * @param context `this` object in `iterator`, optional. + * @return The first acceptable found element in `list`, if nothing is found undefined/null is returned. + **/ + find( + list: _.List, + iterator: _.ListIterator, + context?: any): T; + + /** + * @see _.find + **/ + find( + object: _.Dictionary, + iterator: _.ObjectIterator, + context?: any): T; + + /** + * @see _.find + **/ + find( + object: _.List|_.Dictionary, + iterator: U): T; + + /** + * @see _.find + **/ + find( + object: _.List|_.Dictionary, + iterator: string): T; + + /** + * @see _.find + **/ + detect( + list: _.List, + iterator: _.ListIterator, + context?: any): T; + + /** + * @see _.find + **/ + detect( + object: _.Dictionary, + iterator: _.ObjectIterator, + context?: any): T; + + /** + * @see _.find + **/ + detect( + object: _.List|_.Dictionary, + iterator: U): T; + + /** + * @see _.find + **/ + detect( + object: _.List|_.Dictionary, + iterator: string): T; + + /** + * Looks through each value in the list, returning an array of all the values that pass a truth + * test (iterator). Delegates to the native filter method, if it exists. + * @param list Filter elements out of this list. + * @param iterator Filter iterator function for each element in `list`. + * @param context `this` object in `iterator`, optional. + * @return The filtered list of elements. + **/ + filter( + list: _.List, + iterator: _.ListIterator, + context?: any): T[]; + + /** + * @see _.filter + **/ + filter( + object: _.Dictionary, + iterator: _.ObjectIterator, + context?: any): T[]; + + /** + * @see _.filter + **/ + select( + list: _.List, + iterator: _.ListIterator, + context?: any): T[]; + + /** + * @see _.filter + **/ + select( + object: _.Dictionary, + iterator: _.ObjectIterator, + context?: any): T[]; + + /** + * Looks through each value in the list, returning an array of all the values that contain all + * of the key-value pairs listed in properties. + * @param list List to match elements again `properties`. + * @param properties The properties to check for on each element within `list`. + * @return The elements within `list` that contain the required `properties`. + **/ + where( + list: _.List, + properties: U): T[]; + + /** + * Looks through the list and returns the first value that matches all of the key-value pairs listed in properties. + * @param list Search through this list's elements for the first object with all `properties`. + * @param properties Properties to look for on the elements within `list`. + * @return The first element in `list` that has all `properties`. + **/ + findWhere( + list: _.List, + properties: U): T; + + /** + * Returns the values in list without the elements that the truth test (iterator) passes. + * The opposite of filter. + * Return all the elements for which a truth test fails. + * @param list Reject elements within this list. + * @param iterator Reject iterator function for each element in `list`. + * @param context `this` object in `iterator`, optional. + * @return The rejected list of elements. + **/ + reject( + list: _.List, + iterator: _.ListIterator, + context?: any): T[]; + + /** + * @see _.reject + **/ + reject( + object: _.Dictionary, + iterator: _.ObjectIterator, + context?: any): T[]; + + /** + * Returns true if all of the values in the list pass the iterator truth test. Delegates to the + * native method every, if present. + * @param list Truth test against all elements within this list. + * @param iterator Trust test iterator function for each element in `list`. + * @param context `this` object in `iterator`, optional. + * @return True if all elements passed the truth test, otherwise false. + **/ + every( + list: _.List, + iterator?: _.ListIterator, + context?: any): boolean; + + /** + * @see _.every + **/ + every( + list: _.Dictionary, + iterator?: _.ObjectIterator, + context?: any): boolean; + + /** + * @see _.every + **/ + all( + list: _.List, + iterator?: _.ListIterator, + context?: any): boolean; + + /** + * @see _.every + **/ + all( + list: _.Dictionary, + iterator?: _.ObjectIterator, + context?: any): boolean; + + /** + * Returns true if any of the values in the list pass the iterator truth test. Short-circuits and + * stops traversing the list if a true element is found. Delegates to the native method some, if present. + * @param list Truth test against all elements within this list. + * @param iterator Trust test iterator function for each element in `list`. + * @param context `this` object in `iterator`, optional. + * @return True if any elements passed the truth test, otherwise false. + **/ + some( + list: _.List, + iterator?: _.ListIterator, + context?: any): boolean; + + /** + * @see _.some + **/ + some( + object: _.Dictionary, + iterator?: _.ObjectIterator, + context?: any): boolean; + + /** + * @see _.some + **/ + any( + list: _.List, + iterator?: _.ListIterator, + context?: any): boolean; + + /** + * @see _.some + **/ + any( + object: _.Dictionary, + iterator?: _.ObjectIterator, + context?: any): boolean; + + any( + list: _.List, + value: T): boolean; + + /** + * Returns true if the value is present in the list. Uses indexOf internally, + * if list is an Array. + * @param list Checks each element to see if `value` is present. + * @param value The value to check for within `list`. + * @return True if `value` is present in `list`, otherwise false. + **/ + contains( + list: _.List, + value: T, + fromIndex?: number): boolean; + + /** + * @see _.contains + **/ + contains( + object: _.Dictionary, + value: T): boolean; + + /** + * @see _.contains + **/ + include( + list: _.Collection, + value: T, + fromIndex?: number): boolean; + + /** + * @see _.contains + **/ + include( + object: _.Dictionary, + value: T): boolean; + + /** + * @see _.contains + **/ + includes( + list: _.Collection, + value: T, + fromIndex?: number): boolean; + + /** + * @see _.contains + **/ + includes( + object: _.Dictionary, + value: T): boolean; + + /** + * Calls the method named by methodName on each value in the list. Any extra arguments passed to + * invoke will be forwarded on to the method invocation. + * @param list The element's in this list will each have the method `methodName` invoked. + * @param methodName The method's name to call on each element within `list`. + * @param arguments Additional arguments to pass to the method `methodName`. + **/ + invoke( + list: _.List, + methodName: string, + ...arguments: any[]): any; + + /** + * A convenient version of what is perhaps the most common use-case for map: extracting a list of + * property values. + * @param list The list to pluck elements out of that have the property `propertyName`. + * @param propertyName The property to look for on each element within `list`. + * @return The list of elements within `list` that have the property `propertyName`. + **/ + pluck( + list: _.List, + propertyName: string): any[]; + + /** + * Returns the maximum value in list. + * @param list Finds the maximum value in this list. + * @return Maximum value in `list`. + **/ + max(list: _.List): number; + + /** + * @see _.max + */ + max(object: _.Dictionary): number; + + /** + * Returns the maximum value in list. If iterator is passed, it will be used on each value to generate + * the criterion by which the value is ranked. + * @param list Finds the maximum value in this list. + * @param iterator Compares each element in `list` to find the maximum value. + * @param context `this` object in `iterator`, optional. + * @return The maximum element within `list`. + **/ + max( + list: _.List, + iterator?: _.ListIterator, + context?: any): T; + + /** + * @see _.max + */ + max( + list: _.Dictionary, + iterator?: _.ObjectIterator, + context?: any): T; + + /** + * Returns the minimum value in list. + * @param list Finds the minimum value in this list. + * @return Minimum value in `list`. + **/ + min(list: _.List): number; + + /** + * @see _.min + */ + min(o: _.Dictionary): number; + + /** + * Returns the minimum value in list. If iterator is passed, it will be used on each value to generate + * the criterion by which the value is ranked. + * @param list Finds the minimum value in this list. + * @param iterator Compares each element in `list` to find the minimum value. + * @param context `this` object in `iterator`, optional. + * @return The minimum element within `list`. + **/ + min( + list: _.List, + iterator?: _.ListIterator, + context?: any): T; + + /** + * @see _.min + */ + min( + list: _.Dictionary, + iterator?: _.ObjectIterator, + context?: any): T; + + /** + * Returns a sorted copy of list, ranked in ascending order by the results of running each value + * through iterator. Iterator may also be the string name of the property to sort by (eg. length). + * @param list Sorts this list. + * @param iterator Sort iterator for each element within `list`. + * @param context `this` object in `iterator`, optional. + * @return A sorted copy of `list`. + **/ + sortBy( + list: _.List, + iterator?: _.ListIterator, + context?: any): T[]; + + /** + * @see _.sortBy + * @param iterator Sort iterator for each element within `list`. + **/ + sortBy( + list: _.List, + iterator: string, + context?: any): T[]; + + /** + * Splits a collection into sets, grouped by the result of running each value through iterator. + * If iterator is a string instead of a function, groups by the property named by iterator on + * each of the values. + * @param list Groups this list. + * @param iterator Group iterator for each element within `list`, return the key to group the element by. + * @param context `this` object in `iterator`, optional. + * @return An object with the group names as properties where each property contains the grouped elements from `list`. + **/ + groupBy( + list: _.List, + iterator?: _.ListIterator, + context?: any): _.Dictionary; + + /** + * @see _.groupBy + * @param iterator Property on each object to group them by. + **/ + groupBy( + list: _.List, + iterator: string, + context?: any): _.Dictionary; + + /** + * Given a `list`, and an `iterator` function that returns a key for each element in the list (or a property name), + * returns an object with an index of each item. Just like _.groupBy, but for when you know your keys are unique. + **/ + indexBy( + list: _.List, + iterator: _.ListIterator, + context?: any): _.Dictionary; + + /** + * @see _.indexBy + * @param iterator Property on each object to index them by. + **/ + indexBy( + list: _.List, + iterator: string, + context?: any): _.Dictionary; + + /** + * Sorts a list into groups and returns a count for the number of objects in each group. Similar + * to groupBy, but instead of returning a list of values, returns a count for the number of values + * in that group. + * @param list Group elements in this list and then count the number of elements in each group. + * @param iterator Group iterator for each element within `list`, return the key to group the element by. + * @param context `this` object in `iterator`, optional. + * @return An object with the group names as properties where each property contains the number of elements in that group. + **/ + countBy( + list: _.List, + iterator?: _.ListIterator, + context?: any): _.Dictionary; + + /** + * @see _.countBy + * @param iterator Function name + **/ + countBy( + list: _.List, + iterator: string, + context?: any): _.Dictionary; + + /** + * Returns a shuffled copy of the list, using a version of the Fisher-Yates shuffle. + * @param list List to shuffle. + * @return Shuffled copy of `list`. + **/ + shuffle(list: _.Collection): T[]; + + /** + * Produce a random sample from the `list`. Pass a number to return `n` random elements from the list. Otherwise a single random item will be returned. + * @param list List to sample. + * @return Random sample of `n` elements in `list`. + **/ + sample(list: _.Collection, n: number): T[]; + + /** + * @see _.sample + **/ + sample(list: _.Collection): T; + + /** + * Converts the list (anything that can be iterated over), into a real Array. Useful for transmuting + * the arguments object. + * @param list object to transform into an array. + * @return `list` as an array. + **/ + toArray(list: _.Collection): T[]; + + /** + * Return the number of values in the list. + * @param list Count the number of values/elements in this list. + * @return Number of values in `list`. + **/ + size(list: _.Collection): number; + + /** + * Split array into two arrays: + * one whose elements all satisfy predicate and one whose elements all do not satisfy predicate. + * @param array Array to split in two. + * @param iterator Filter iterator function for each element in `array`. + * @param context `this` object in `iterator`, optional. + * @return Array where Array[0] are the elements in `array` that satisfies the predicate, and Array[1] the elements that did not. + **/ + partition( + array: Array, + iterator: _.ListIterator, + context?: any): T[][]; + + /********* + * Arrays * + **********/ + + /** + * Returns the first element of an array. Passing n will return the first n elements of the array. + * @param array Retrieves the first element of this array. + * @return Returns the first element of `array`. + **/ + first(array: _.List): T; + + /** + * @see _.first + * @param n Return more than one element from `array`. + **/ + first( + array: _.List, + n: number): T[]; + + /** + * @see _.first + **/ + head(array: _.List): T; + + /** + * @see _.first + **/ + head( + array: _.List, + n: number): T[]; + + /** + * @see _.first + **/ + take(array: _.List): T; + + /** + * @see _.first + **/ + take( + array: _.List, + n: number): T[]; + + /** + * Returns everything but the last entry of the array. Especially useful on the arguments object. + * Pass n to exclude the last n elements from the result. + * @param array Retrieve all elements except the last `n`. + * @param n Leaves this many elements behind, optional. + * @return Returns everything but the last `n` elements of `array`. + **/ + initial( + array: _.List, + n?: number): T[]; + + /** + * Returns the last element of an array. Passing n will return the last n elements of the array. + * @param array Retrieves the last element of this array. + * @return Returns the last element of `array`. + **/ + last(array: _.List): T; + + /** + * @see _.last + * @param n Return more than one element from `array`. + **/ + last( + array: _.List, + n: number): T[]; + + /** + * Returns the rest of the elements in an array. Pass an index to return the values of the array + * from that index onward. + * @param array The array to retrieve all but the first `index` elements. + * @param n The index to start retrieving elements forward from, optional, default = 1. + * @return Returns the elements of `array` from `index` to the end of `array`. + **/ + rest( + array: _.List, + n?: number): T[]; + + /** + * @see _.rest + **/ + tail( + array: _.List, + n?: number): T[]; + + /** + * @see _.rest + **/ + drop( + array: _.List, + n?: number): T[]; + + /** + * Returns a copy of the array with all falsy values removed. In JavaScript, false, null, 0, "", + * undefined and NaN are all falsy. + * @param array Array to compact. + * @return Copy of `array` without false values. + **/ + compact(array: _.List): T[]; + + /** + * Flattens a nested array (the nesting can be to any depth). If you pass shallow, the array will + * only be flattened a single level. + * @param array The array to flatten. + * @param shallow If true then only flatten one level, optional, default = false. + * @return `array` flattened. + **/ + flatten( + array: _.List, + shallow?: boolean): any[]; + + /** + * Returns a copy of the array with all instances of the values removed. + * @param array The array to remove `values` from. + * @param values The values to remove from `array`. + * @return Copy of `array` without `values`. + **/ + without( + array: _.List, + ...values: T[]): T[]; + + /** + * Computes the union of the passed-in arrays: the list of unique items, in order, that are + * present in one or more of the arrays. + * @param arrays Array of arrays to compute the union of. + * @return The union of elements within `arrays`. + **/ + union(...arrays: _.List[]): T[]; + + /** + * Computes the list of values that are the intersection of all the arrays. Each value in the result + * is present in each of the arrays. + * @param arrays Array of arrays to compute the intersection of. + * @return The intersection of elements within `arrays`. + **/ + intersection(...arrays: _.List[]): T[]; + + /** + * Similar to without, but returns the values from array that are not present in the other arrays. + * @param array Keeps values that are within `others`. + * @param others The values to keep within `array`. + * @return Copy of `array` with only `others` values. + **/ + difference( + array: _.List, + ...others: _.List[]): T[]; + + /** + * Produces a duplicate-free version of the array, using === to test object equality. If you know in + * advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If + * you want to compute unique items based on a transformation, pass an iterator function. + * @param array Array to remove duplicates from. + * @param isSorted True if `array` is already sorted, optional, default = false. + * @param iterator Transform the elements of `array` before comparisons for uniqueness. + * @param context 'this' object in `iterator`, optional. + * @return Copy of `array` where all elements are unique. + **/ + uniq( + array: _.List, + isSorted?: boolean, + iterator?: _.ListIterator, + context?: any): T[]; + + /** + * @see _.uniq + **/ + uniq( + array: _.List, + iterator?: _.ListIterator, + context?: any): T[]; + + /** + * @see _.uniq + **/ + unique( + array: _.List, + iterator?: _.ListIterator, + context?: any): T[]; + + /** + * @see _.uniq + **/ + unique( + array: _.List, + isSorted?: boolean, + iterator?: _.ListIterator, + context?: any): T[]; + + + /** + * Merges together the values of each of the arrays with the values at the corresponding position. + * Useful when you have separate data sources that are coordinated through matching array indexes. + * If you're working with a matrix of nested arrays, zip.apply can transpose the matrix in a similar fashion. + * @param arrays The arrays to merge/zip. + * @return Zipped version of `arrays`. + **/ + zip(...arrays: any[][]): any[][]; + + /** + * @see _.zip + **/ + zip(...arrays: any[]): any[]; + + /** + * The opposite of zip. Given a number of arrays, returns a series of new arrays, the first + * of which contains all of the first elements in the input arrays, the second of which + * contains all of the second elements, and so on. Use with apply to pass in an array + * of arrays + * @param arrays The arrays to unzip. + * @return Unzipped version of `arrays`. + **/ + unzip(...arrays: any[][]): any[][]; + + /** + * Converts arrays into objects. Pass either a single list of [key, value] pairs, or a + * list of keys, and a list of values. + * @param keys Key array. + * @param values Value array. + * @return An object containing the `keys` as properties and `values` as the property values. + **/ + object( + keys: _.List, + values: _.List): TResult; + + /** + * Converts arrays into objects. Pass either a single list of [key, value] pairs, or a + * list of keys, and a list of values. + * @param keyValuePairs Array of [key, value] pairs. + * @return An object containing the `keys` as properties and `values` as the property values. + **/ + object(...keyValuePairs: any[][]): TResult; + + /** + * @see _.object + **/ + object( + list: _.List, + values?: any): TResult; + + /** + * Returns the index at which value can be found in the array, or -1 if value is not present in the array. + * Uses the native indexOf function unless it's missing. If you're working with a large array, and you know + * that the array is already sorted, pass true for isSorted to use a faster binary search ... or, pass a number + * as the third argument in order to look for the first matching value in the array after the given index. + * @param array The array to search for the index of `value`. + * @param value The value to search for within `array`. + * @param isSorted True if the array is already sorted, optional, default = false. + * @return The index of `value` within `array`. + **/ + indexOf( + array: _.List, + value: T, + isSorted?: boolean): number; + + /** + * @see _indexof + **/ + indexOf( + array: _.List, + value: T, + startFrom: number): number; + + /** + * Returns the index of the last occurrence of value in the array, or -1 if value is not present. Uses the + * native lastIndexOf function if possible. Pass fromIndex to start your search at a given index. + * @param array The array to search for the last index of `value`. + * @param value The value to search for within `array`. + * @param from The starting index for the search, optional. + * @return The index of the last occurrence of `value` within `array`. + **/ + lastIndexOf( + array: _.List, + value: T, + from?: number): number; + + /** + * Returns the first index of an element in `array` where the predicate truth test passes + * @param array The array to search for the index of the first element where the predicate truth test passes. + * @param predicate Predicate function. + * @param context `this` object in `predicate`, optional. + * @return Returns the index of an element in `array` where the predicate truth test passes or -1.` + **/ + findIndex( + array: _.List, + predicate: _.ListIterator | {}, + context?: any): number; + + /** + * Returns the last index of an element in `array` where the predicate truth test passes + * @param array The array to search for the index of the last element where the predicate truth test passes. + * @param predicate Predicate function. + * @param context `this` object in `predicate`, optional. + * @return Returns the index of an element in `array` where the predicate truth test passes or -1.` + **/ + findLastIndex( + array: _.List, + predicate: _.ListIterator | {}, + context?: any): number; + + /** + * Uses a binary search to determine the index at which the value should be inserted into the list in order + * to maintain the list's sorted order. If an iterator is passed, it will be used to compute the sort ranking + * of each value, including the value you pass. + * @param list The sorted list. + * @param value The value to determine its index within `list`. + * @param iterator Iterator to compute the sort ranking of each value, optional. + * @return The index where `value` should be inserted into `list`. + **/ + sortedIndex( + list: _.List, + value: T, + iterator?: (x: T) => TSort, context?: any): number; + + /** + * A function to create flexibly-numbered lists of integers, handy for each and map loops. start, if omitted, + * defaults to 0; step defaults to 1. Returns a list of integers from start to stop, incremented (or decremented) + * by step, exclusive. + * @param start Start here. + * @param stop Stop here. + * @param step The number to count up by each iteration, optional, default = 1. + * @return Array of numbers from `start` to `stop` with increments of `step`. + **/ + + range( + start: number, + stop: number, + step?: number): number[]; + + /** + * @see _.range + * @param stop Stop here. + * @return Array of numbers from 0 to `stop` with increments of 1. + * @note If start is not specified the implementation will never pull the step (step = arguments[2] || 0) + **/ + range(stop: number): number[]; + + /** + * Split an **array** into several arrays containing **count** or less elements + * of initial array. + * @param array The array to split + * @param count The maximum size of the inner arrays. + */ + chunk(array: _.Collection, count: number): (_.Collection)[] + + /************* + * Functions * + *************/ + + /** + * Bind a function to an object, meaning that whenever the function is called, the value of this will + * be the object. Optionally, bind arguments to the function to pre-fill them, also known as partial application. + * @param func The function to bind `this` to `object`. + * @param context The `this` pointer whenever `fn` is called. + * @param arguments Additional arguments to pass to `fn` when called. + * @return `fn` with `this` bound to `object`. + **/ + bind( + func: Function, + context: any, + ...arguments: any[]): () => any; + + /** + * Binds a number of methods on the object, specified by methodNames, to be run in the context of that object + * whenever they are invoked. Very handy for binding functions that are going to be used as event handlers, + * which would otherwise be invoked with a fairly useless this. If no methodNames are provided, all of the + * object's function properties will be bound to it. + * @param object The object to bind the methods `methodName` to. + * @param methodNames The methods to bind to `object`, optional and if not provided all of `object`'s + * methods are bound. + **/ + bindAll( + object: any, + ...methodNames: string[]): any; + + /** + * Partially apply a function by filling in any number of its arguments, without changing its dynamic this value. + * A close cousin of bind. You may pass _ in your list of arguments to specify an argument that should not be + * pre-filled, but left open to supply at call-time. + * @param fn Function to partially fill in arguments. + * @param arguments The partial arguments. + * @return `fn` with partially filled in arguments. + **/ + + partial( + fn: { (p1: T1):T2 }, + p1: T1 + ): { (): T2 }; + + partial( + fn: { (p1: T1, p2: T2):T3 }, + p1: T1 + ): { (p2: T2): T3 }; + + partial( + fn: { (p1: T1, p2: T2):T3 }, + p1: T1, + p2: T2 + ): { (): T3 }; + + partial( + fn: { (p1: T1, p2: T2):T3 }, + stub1: UnderscoreStatic, + p2: T2 + ): { (p1: T1): T3 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3):T4 }, + p1: T1 + ): { (p2: T2, p3: T3): T4 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3):T4 }, + p1: T1, + p2: T2 + ): { (p3: T3): T4 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3):T4 }, + stub1: UnderscoreStatic, + p2: T2 + ): { (p1: T1, p3: T3): T4 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3):T4 }, + p1: T1, + p2: T2, + p3: T3 + ): { (): T4 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3):T4 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3 + ): { (p1: T1): T4 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3):T4 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3 + ): { (p2: T2): T4 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3):T4 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3 + ): { (p1: T1, p2: T2): T4 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + p1: T1 + ): { (p2: T2, p3: T3, p4: T4): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + p1: T1, + p2: T2 + ): { (p3: T3, p4: T4): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + stub1: UnderscoreStatic, + p2: T2 + ): { (p1: T1, p3: T3, p4: T4): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + p1: T1, + p2: T2, + p3: T3 + ): { (p4: T4): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3 + ): { (p1: T1, p4: T4): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3 + ): { (p2: T2, p4: T4): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3 + ): { (p1: T1, p2: T2, p4: T4): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4 + ): { (): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4 + ): { (p1: T1): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4 + ): { (p2: T2): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4 + ): { (p1: T1, p2: T2): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4 + ): { (p3: T3): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4 + ): { (p1: T1, p3: T3): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4 + ): { (p2: T2, p3: T3): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4):T5 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4 + ): { (p1: T1, p2: T2, p3: T3): T5 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1 + ): { (p2: T2, p3: T3, p4: T4, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + p2: T2 + ): { (p3: T3, p4: T4, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + p2: T2 + ): { (p1: T1, p3: T3, p4: T4, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + p2: T2, + p3: T3 + ): { (p4: T4, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3 + ): { (p1: T1, p4: T4, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3 + ): { (p2: T2, p4: T4, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3 + ): { (p1: T1, p2: T2, p4: T4, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4 + ): { (p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4 + ): { (p1: T1, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4 + ): { (p2: T2, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4 + ): { (p1: T1, p2: T2, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4 + ): { (p3: T3, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4 + ): { (p1: T1, p3: T3, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4 + ): { (p2: T2, p3: T3, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4 + ): { (p1: T1, p2: T2, p3: T3, p5: T5): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + p5: T5 + ): { (): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + p5: T5 + ): { (p1: T1): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5 + ): { (p2: T2): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5 + ): { (p1: T1, p2: T2): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p3: T3): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p1: T1, p3: T3): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p2: T2, p3: T3): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p1: T1, p2: T2, p3: T3): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p4: T4): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p4: T4): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p2: T2, p4: T4): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p2: T2, p4: T4): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p3: T3, p4: T4): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p3: T3, p4: T4): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p2: T2, p3: T3, p4: T4): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5):T6 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p2: T2, p3: T3, p4: T4): T6 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1 + ): { (p2: T2, p3: T3, p4: T4, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2 + ): { (p3: T3, p4: T4, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2 + ): { (p1: T1, p3: T3, p4: T4, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + p3: T3 + ): { (p4: T4, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3 + ): { (p1: T1, p4: T4, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3 + ): { (p2: T2, p4: T4, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3 + ): { (p1: T1, p2: T2, p4: T4, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4 + ): { (p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4 + ): { (p1: T1, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4 + ): { (p2: T2, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4 + ): { (p1: T1, p2: T2, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4 + ): { (p3: T3, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4 + ): { (p1: T1, p3: T3, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4 + ): { (p2: T2, p3: T3, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4 + ): { (p1: T1, p2: T2, p3: T3, p5: T5, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + p5: T5 + ): { (p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + p5: T5 + ): { (p1: T1, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5 + ): { (p2: T2, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5 + ): { (p1: T1, p2: T2, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p3: T3, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p1: T1, p3: T3, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p2: T2, p3: T3, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p1: T1, p2: T2, p3: T3, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p4: T4, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p4: T4, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p2: T2, p4: T4, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p2: T2, p4: T4, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p3: T3, p4: T4, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p3: T3, p4: T4, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p2: T2, p3: T3, p4: T4, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p2: T2, p3: T3, p4: T4, p6: T6): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + p5: T5, + p6: T6 + ): { (): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + p5: T5, + p6: T6 + ): { (p1: T1): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5, + p6: T6 + ): { (p2: T2): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5, + p6: T6 + ): { (p1: T1, p2: T2): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6 + ): { (p3: T3): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6 + ): { (p1: T1, p3: T3): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6 + ): { (p2: T2, p3: T3): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6 + ): { (p1: T1, p2: T2, p3: T3): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p4: T4): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p1: T1, p4: T4): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p2: T2, p4: T4): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p1: T1, p2: T2, p4: T4): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p3: T3, p4: T4): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p1: T1, p3: T3, p4: T4): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p2: T2, p3: T3, p4: T4): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p1: T1, p2: T2, p3: T3, p4: T4): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p2: T2, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p2: T2, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p3: T3, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p3: T3, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p2: T2, p3: T3, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p2: T2, p3: T3, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p4: T4, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p4: T4, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p2: T2, p4: T4, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p2: T2, p4: T4, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p3: T3, p4: T4, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p3: T3, p4: T4, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p2: T2, p3: T3, p4: T4, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6):T7 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5): T7 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1 + ): { (p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2 + ): { (p3: T3, p4: T4, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2 + ): { (p1: T1, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3 + ): { (p4: T4, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3 + ): { (p1: T1, p4: T4, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3 + ): { (p2: T2, p4: T4, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3 + ): { (p1: T1, p2: T2, p4: T4, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4 + ): { (p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4 + ): { (p1: T1, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4 + ): { (p2: T2, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4 + ): { (p1: T1, p2: T2, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4 + ): { (p3: T3, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4 + ): { (p1: T1, p3: T3, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4 + ): { (p2: T2, p3: T3, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4 + ): { (p1: T1, p2: T2, p3: T3, p5: T5, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + p5: T5 + ): { (p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + p5: T5 + ): { (p1: T1, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5 + ): { (p2: T2, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5 + ): { (p1: T1, p2: T2, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p3: T3, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p1: T1, p3: T3, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p2: T2, p3: T3, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5 + ): { (p1: T1, p2: T2, p3: T3, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p4: T4, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p4: T4, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p2: T2, p4: T4, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p2: T2, p4: T4, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p3: T3, p4: T4, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p3: T3, p4: T4, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p2: T2, p3: T3, p4: T4, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5 + ): { (p1: T1, p2: T2, p3: T3, p4: T4, p6: T6, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + p5: T5, + p6: T6 + ): { (p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + p5: T5, + p6: T6 + ): { (p1: T1, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5, + p6: T6 + ): { (p2: T2, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5, + p6: T6 + ): { (p1: T1, p2: T2, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6 + ): { (p3: T3, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6 + ): { (p1: T1, p3: T3, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6 + ): { (p2: T2, p3: T3, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6 + ): { (p1: T1, p2: T2, p3: T3, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p4: T4, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p1: T1, p4: T4, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p2: T2, p4: T4, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p1: T1, p2: T2, p4: T4, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p3: T3, p4: T4, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p1: T1, p3: T3, p4: T4, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p2: T2, p3: T3, p4: T4, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6 + ): { (p1: T1, p2: T2, p3: T3, p4: T4, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p2: T2, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p2: T2, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p3: T3, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p3: T3, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p2: T2, p3: T3, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p2: T2, p3: T3, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p4: T4, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p4: T4, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p2: T2, p4: T4, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p2: T2, p4: T4, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p3: T3, p4: T4, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p3: T3, p4: T4, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p2: T2, p3: T3, p4: T4, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6 + ): { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p7: T7): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + p5: T5, + p6: T6, + p7: T7 + ): { (): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + p5: T5, + p6: T6, + p7: T7 + ): { (p1: T1): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5, + p6: T6, + p7: T7 + ): { (p2: T2): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5, + p6: T6, + p7: T7 + ): { (p1: T1, p2: T2): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6, + p7: T7 + ): { (p3: T3): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6, + p7: T7 + ): { (p1: T1, p3: T3): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6, + p7: T7 + ): { (p2: T2, p3: T3): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + p6: T6, + p7: T7 + ): { (p1: T1, p2: T2, p3: T3): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6, + p7: T7 + ): { (p4: T4): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6, + p7: T7 + ): { (p1: T1, p4: T4): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6, + p7: T7 + ): { (p2: T2, p4: T4): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + p6: T6, + p7: T7 + ): { (p1: T1, p2: T2, p4: T4): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6, + p7: T7 + ): { (p3: T3, p4: T4): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6, + p7: T7 + ): { (p1: T1, p3: T3, p4: T4): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6, + p7: T7 + ): { (p2: T2, p3: T3, p4: T4): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + p6: T6, + p7: T7 + ): { (p1: T1, p2: T2, p3: T3, p4: T4): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p1: T1, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p2: T2, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p1: T1, p2: T2, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p3: T3, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p1: T1, p3: T3, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p2: T2, p3: T3, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p1: T1, p2: T2, p3: T3, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p4: T4, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p1: T1, p4: T4, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p2: T2, p4: T4, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p1: T1, p2: T2, p4: T4, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p3: T3, p4: T4, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p1: T1, p3: T3, p4: T4, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p2: T2, p3: T3, p4: T4, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + p6: T6, + p7: T7 + ): { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p2: T2, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p2: T2, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p3: T3, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p3: T3, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p2: T2, p3: T3, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p2: T2, p3: T3, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p4: T4, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p4: T4, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p2: T2, p4: T4, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p2: T2, p4: T4, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p3: T3, p4: T4, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p3: T3, p4: T4, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p2: T2, p3: T3, p4: T4, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + p5: T5, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p2: T2, p3: T3, p4: T4, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p2: T2, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + p4: T4, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p2: T2, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p3: T3, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p3: T3, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p2: T2, p3: T3, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + p4: T4, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p2: T2, p3: T3, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p4: T4, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p4: T4, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p2: T2, p4: T4, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + p3: T3, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p2: T2, p4: T4, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p3: T3, p4: T4, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + p2: T2, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p3: T3, p4: T4, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + p1: T1, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p2: T2, p3: T3, p4: T4, p5: T5, p6: T6): T8 }; + + partial( + fn: { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7):T8 }, + stub1: UnderscoreStatic, + stub2: UnderscoreStatic, + stub3: UnderscoreStatic, + stub4: UnderscoreStatic, + stub5: UnderscoreStatic, + stub6: UnderscoreStatic, + p7: T7 + ): { (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6): T8 }; + + /** + * Memoizes a given function by caching the computed result. Useful for speeding up slow-running computations. + * If passed an optional hashFunction, it will be used to compute the hash key for storing the result, based + * on the arguments to the original function. The default hashFunction just uses the first argument to the + * memoized function as the key. + * @param fn Computationally expensive function that will now memoized results. + * @param hashFn Hash function for storing the result of `fn`. + * @return Memoized version of `fn`. + **/ + memoize( + fn: Function, + hashFn?: (...args: any[]) => string): Function; + + /** + * Much like setTimeout, invokes function after wait milliseconds. If you pass the optional arguments, + * they will be forwarded on to the function when it is invoked. + * @param func Function to delay `waitMS` amount of ms. + * @param wait The amount of milliseconds to delay `fn`. + * @arguments Additional arguments to pass to `fn`. + **/ + delay( + func: Function, + wait: number, + ...arguments: any[]): any; + + /** + * @see _delay + **/ + delay( + func: Function, + ...arguments: any[]): any; + + /** + * Defers invoking the function until the current call stack has cleared, similar to using setTimeout + * with a delay of 0. Useful for performing expensive computations or HTML rendering in chunks without + * blocking the UI thread from updating. If you pass the optional arguments, they will be forwarded on + * to the function when it is invoked. + * @param fn The function to defer. + * @param arguments Additional arguments to pass to `fn`. + **/ + defer( + fn: Function, + ...arguments: any[]): void; + + /** + * Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, + * will only actually call the original function at most once per every wait milliseconds. Useful for + * rate-limiting events that occur faster than you can keep up with. + * By default, throttle will execute the function as soon as you call it for the first time, and, + * if you call it again any number of times during the wait period, as soon as that period is over. + * If you'd like to disable the leading-edge call, pass {leading: false}, and if you'd like to disable + * the execution on the trailing-edge, pass {trailing: false}. + * @param func Function to throttle `waitMS` ms. + * @param wait The number of milliseconds to wait before `fn` can be invoked again. + * @param options Allows for disabling execution of the throttled function on either the leading or trailing edge. + * @return `fn` with a throttle of `wait`. + **/ + throttle( + func: T, + wait: number, + options?: _.ThrottleSettings): T & _.Cancelable; + + /** + * Creates and returns a new debounced version of the passed function that will postpone its execution + * until after wait milliseconds have elapsed since the last time it was invoked. Useful for implementing + * behavior that should only happen after the input has stopped arriving. For example: rendering a preview + * of a Markdown comment, recalculating a layout after the window has stopped being resized, and so on. + * + * Pass true for the immediate parameter to cause debounce to trigger the function on the leading instead + * of the trailing edge of the wait interval. Useful in circumstances like preventing accidental double + *-clicks on a "submit" button from firing a second time. + * @param fn Function to debounce `waitMS` ms. + * @param wait The number of milliseconds to wait before `fn` can be invoked again. + * @param immediate True if `fn` should be invoked on the leading edge of `waitMS` instead of the trailing edge. + * @return Debounced version of `fn` that waits `wait` ms when invoked. + **/ + debounce( + fn: T, + wait: number, + immediate?: boolean): T & _.Cancelable; + + /** + * Creates a version of the function that can only be called one time. Repeated calls to the modified + * function will have no effect, returning the value from the original call. Useful for initialization + * functions, instead of having to set a boolean flag and then check it later. + * @param fn Function to only execute once. + * @return Copy of `fn` that can only be invoked once. + **/ + once(fn: T): T; + + /** + * Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) + * This accumulates the arguments passed into an array, after a given index. + **/ + restArgs(func: Function, starIndex?: number) : Function; + + /** + * Creates a version of the function that will only be run after first being called count times. Useful + * for grouping asynchronous responses, where you want to be sure that all the async calls have finished, + * before proceeding. + * @param number count Number of times to be called before actually executing. + * @param Function fn The function to defer execution `count` times. + * @return Copy of `fn` that will not execute until it is invoked `count` times. + **/ + after( + count: number, + fn: Function): Function; + + /** + * Creates a version of the function that can be called no more than count times. The result of + * the last function call is memoized and returned when count has been reached. + * @param number count The maxmimum number of times the function can be called. + * @param Function fn The function to limit the number of times it can be called. + * @return Copy of `fn` that can only be called `count` times. + **/ + before( + count: number, + fn: Function): Function; + + /** + * Wraps the first function inside of the wrapper function, passing it as the first argument. This allows + * the wrapper to execute code before and after the function runs, adjust the arguments, and execute it + * conditionally. + * @param fn Function to wrap. + * @param wrapper The function that will wrap `fn`. + * @return Wrapped version of `fn. + **/ + wrap( + fn: Function, + wrapper: (fn: Function, ...args: any[]) => any): Function; + + /** + * Returns a negated version of the pass-in predicate. + * @param (...args: any[]) => boolean predicate + * @return (...args: any[]) => boolean + **/ + negate(predicate: (...args: any[]) => boolean): (...args: any[]) => boolean; + + /** + * Returns the composition of a list of functions, where each function consumes the return value of the + * function that follows. In math terms, composing the functions f(), g(), and h() produces f(g(h())). + * @param functions List of functions to compose. + * @return Composition of `functions`. + **/ + compose(f1:(i:A)=>B, f2:(i:B)=>C): (i:A)=>C; + compose(...functions: Function[]): Function; + + /********** + * Objects * + ***********/ + + /** + * Retrieve all the names of the object's properties. + * @param object Retrieve the key or property names from this object. + * @return List of all the property names on `object`. + **/ + keys(object: any): string[]; + + /** + * Retrieve all the names of object's own and inherited properties. + * @param object Retrieve the key or property names from this object. + * @return List of all the property names on `object`. + **/ + allKeys(object: any): string[]; + + /** + * Return all of the values of the object's properties. + * @param object Retrieve the values of all the properties on this object. + * @return List of all the values on `object`. + **/ + values(object: _.Dictionary): T[]; + + /** + * Return all of the values of the object's properties. + * @param object Retrieve the values of all the properties on this object. + * @return List of all the values on `object`. + **/ + values(object: any): any[]; + + /** + * Like map, but for objects. Transform the value of each property in turn. + * @param object The object to transform + * @param iteratee The function that transforms property values + * @param context The optional context (value of `this`) to bind to + * @return a new _.Dictionary of property values + */ + mapObject(object: _.Dictionary, iteratee: (val: T, key: string, object: _.Dictionary) => U, context?: any): _.Dictionary; + + /** + * Like map, but for objects. Transform the value of each property in turn. + * @param object The object to transform + * @param iteratee The function that tranforms property values + * @param context The optional context (value of `this`) to bind to + */ + mapObject(object: any, iteratee: (val: any, key: string, object: any) => T, context?: any): _.Dictionary; + + /** + * Like map, but for objects. Retrieves a property from each entry in the object, as if by _.property + * @param object The object to transform + * @param iteratee The property name to retrieve + * @param context The optional context (value of `this`) to bind to + */ + mapObject(object: any, iteratee: string, context?: any): _.Dictionary; + + /** + * Convert an object into a list of [key, value] pairs. + * @param object Convert this object to a list of [key, value] pairs. + * @return List of [key, value] pairs on `object`. + **/ + pairs(object: any): any[][]; + + /** + * Returns a copy of the object where the keys have become the values and the values the keys. + * For this to work, all of your object's values should be unique and string serializable. + * @param object Object to invert key/value pairs. + * @return An inverted key/value paired version of `object`. + **/ + invert(object: any): any; + + /** + * Returns a sorted list of the names of every method in an object - that is to say, + * the name of every function property of the object. + * @param object Object to pluck all function property names from. + * @return List of all the function names on `object`. + **/ + functions(object: any): string[]; + + /** + * @see _functions + **/ + methods(object: any): string[]; + + /** + * Copy all of the properties in the source objects over to the destination object, and return + * the destination object. It's in-order, so the last source will override properties of the + * same name in previous arguments. + * @param destination Object to extend all the properties from `sources`. + * @param sources Extends `destination` with all properties from these source objects. + * @return `destination` extended with all the properties from the `sources` objects. + **/ + extend( + destination: any, + ...sources: any[]): any; + + /** + * Like extend, but only copies own properties over to the destination object. (alias: assign) + */ + extendOwn( + destination: any, + ...source: any[]): any; + + /** + * Like extend, but only copies own properties over to the destination object. (alias: extendOwn) + */ + assign( + destination: any, + ...source: any[]): any; + + /** + * Returns the first key on an object that passes a predicate test. + * @param obj the object to search in + * @param predicate Predicate function. + * @param context `this` object in `iterator`, optional. + */ + findKey(obj: _.Dictionary, predicate: _.ObjectIterator, context? : any): T + + /** + * Return a copy of the object, filtered to only have values for the whitelisted keys + * (or array of valid keys). + * @param object Object to strip unwanted key/value pairs. + * @keys The key/value pairs to keep on `object`. + * @return Copy of `object` with only the `keys` properties. + **/ + pick( + object: any, + ...keys: any[]): any; + + /** + * @see _.pick + **/ + pick( + object: any, + fn: (value: any, key: any, object: any) => any): any; + + /** + * Return a copy of the object, filtered to omit the blacklisted keys (or array of keys). + * @param object Object to strip unwanted key/value pairs. + * @param keys The key/value pairs to remove on `object`. + * @return Copy of `object` without the `keys` properties. + **/ + omit( + object: any, + ...keys: string[]): any; + + /** + * @see _.omit + **/ + omit( + object: any, + keys: string[]): any; + + /** + * @see _.omit + **/ + omit( + object: any, + iteratee: Function): any; + + /** + * Fill in null and undefined properties in object with values from the defaults objects, + * and return the object. As soon as the property is filled, further defaults will have no effect. + * @param object Fill this object with default values. + * @param defaults The default values to add to `object`. + * @return `object` with added `defaults` values. + **/ + defaults( + object: any, + ...defaults: any[]): any; + + + /** + * Creates an object that inherits from the given prototype object. + * If additional properties are provided then they will be added to the + * created object. + * @param prototype The prototype that the returned object will inherit from. + * @param props Additional props added to the returned object. + **/ + create(prototype: any, props?: Object): any; + + /** + * Create a shallow-copied clone of the object. + * Any nested objects or arrays will be copied by reference, not duplicated. + * @param object Object to clone. + * @return Copy of `object`. + **/ + clone(object: T): T; + + /** + * Invokes interceptor with the object, and then returns object. The primary purpose of this method + * is to "tap into" a method chain, in order to perform operations on intermediate results within the chain. + * @param object Argument to `interceptor`. + * @param intercepter The function to modify `object` before continuing the method chain. + * @return Modified `object`. + **/ + tap(object: T, intercepter: Function): T; + + /** + * Does the object contain the given key? Identical to object.hasOwnProperty(key), but uses a safe + * reference to the hasOwnProperty function, in case it's been overridden accidentally. + * @param object Object to check for `key`. + * @param key The key to check for on `object`. + * @return True if `key` is a property on `object`, otherwise false. + **/ + has(object: any, key: string): boolean; + + /** + * Returns a predicate function that will tell you if a passed in object contains all of the key/value properties present in attrs. + * @param attrs Object with key values pair + * @return Predicate function + **/ + matches(attrs: T): _.ListIterator; + + /** + * Returns a predicate function that will tell you if a passed in object contains all of the key/value properties present in attrs. + * @see _.matches + * @param attrs Object with key values pair + * @return Predicate function + **/ + matcher(attrs: T): _.ListIterator; + + /** + * Returns a function that will itself return the key property of any passed-in object. + * @param key Property of the object. + * @return Function which accept an object an returns the value of key in that object. + **/ + property(key: string): (object: Object) => any; + + /** + * Returns a function that will itself return the value of a object key property. + * @param key The object to get the property value from. + * @return Function which accept a key property in `object` and returns its value. + **/ + propertyOf(object: Object): (key: string) => any; + + /** + * Performs an optimized deep comparison between the two objects, + * to determine if they should be considered equal. + * @param object Compare to `other`. + * @param other Compare to `object`. + * @return True if `object` is equal to `other`. + **/ + isEqual(object: any, other: any): boolean; + + /** + * Returns true if object contains no values. + * @param object Check if this object has no properties or values. + * @return True if `object` is empty. + **/ + isEmpty(object: any): boolean; + + /** + * Returns true if the keys and values in `properties` matches with the `object` properties. + * @param object Object to be compared with `properties`. + * @param properties Properties be compared with `object` + * @return True if `object` has matching keys and values, otherwise false. + **/ + isMatch(object:any, properties:any): boolean; + + /** + * Returns true if object is a DOM element. + * @param object Check if this object is a DOM element. + * @return True if `object` is a DOM element, otherwise false. + **/ + isElement(object: any): object is Element; + + /** + * Returns true if object is an Array. + * @param object Check if this object is an Array. + * @return True if `object` is an Array, otherwise false. + **/ + isArray(object: any): object is any[]; + + /** + * Returns true if object is an Array. + * @param object Check if this object is an Array. + * @return True if `object` is an Array, otherwise false. + **/ + isArray(object: any): object is T[]; + + /** + * Returns true if object is a Symbol. + * @param object Check if this object is a Symbol. + * @return True if `object` is a Symbol, otherwise false. + **/ + isSymbol(object: any): object is symbol; + + /** + * Returns true if value is an Object. Note that JavaScript arrays and functions are objects, + * while (normal) strings and numbers are not. + * @param object Check if this object is an Object. + * @return True of `object` is an Object, otherwise false. + **/ + isObject(object: any): boolean; + + /** + * Returns true if object is an Arguments object. + * @param object Check if this object is an Arguments object. + * @return True if `object` is an Arguments object, otherwise false. + **/ + isArguments(object: any): object is IArguments; + + /** + * Returns true if object is a Function. + * @param object Check if this object is a Function. + * @return True if `object` is a Function, otherwise false. + **/ + isFunction(object: any): object is Function; + + /** + * Returns true if object inherits from an Error. + * @param object Check if this object is an Error. + * @return True if `object` is a Error, otherwise false. + **/ + isError(object:any): object is Error; + + /** + * Returns true if object is a String. + * @param object Check if this object is a String. + * @return True if `object` is a String, otherwise false. + **/ + isString(object: any): object is string; + + /** + * Returns true if object is a Number (including NaN). + * @param object Check if this object is a Number. + * @return True if `object` is a Number, otherwise false. + **/ + isNumber(object: any): object is number; + + /** + * Returns true if object is a finite Number. + * @param object Check if this object is a finite Number. + * @return True if `object` is a finite Number. + **/ + isFinite(object: any): boolean; + + /** + * Returns true if object is either true or false. + * @param object Check if this object is a bool. + * @return True if `object` is a bool, otherwise false. + **/ + isBoolean(object: any): object is boolean; + + /** + * Returns true if object is a Date. + * @param object Check if this object is a Date. + * @return True if `object` is a Date, otherwise false. + **/ + isDate(object: any): object is Date; + + /** + * Returns true if object is a RegExp. + * @param object Check if this object is a RegExp. + * @return True if `object` is a RegExp, otherwise false. + **/ + isRegExp(object: any): object is RegExp; + + /** + * Returns true if object is NaN. + * Note: this is not the same as the native isNaN function, + * which will also return true if the variable is undefined. + * @param object Check if this object is NaN. + * @return True if `object` is NaN, otherwise false. + **/ + isNaN(object: any): boolean; + + /** + * Returns true if the value of object is null. + * @param object Check if this object is null. + * @return True if `object` is null, otherwise false. + **/ + isNull(object: any): boolean; + + /** + * Returns true if value is undefined. + * @param object Check if this object is undefined. + * @return True if `object` is undefined, otherwise false. + **/ + isUndefined(value: any): boolean; + + /* ********* + * Utility * + ********** */ + + /** + * Give control of the "_" variable back to its previous owner. + * Returns a reference to the Underscore object. + * @return Underscore object reference. + **/ + noConflict(): any; + + /** + * Returns the same value that is used as the argument. In math: f(x) = x + * This function looks useless, but is used throughout Underscore as a default iterator. + * @param value Identity of this object. + * @return `value`. + **/ + identity(value: T): T; + + /** + * Creates a function that returns the same value that is used as the argument of _.constant + * @param value Identity of this object. + * @return Function that return value. + **/ + constant(value: T): () => T; + + /** + * Returns undefined irrespective of the arguments passed to it. Useful as the default + * for optional callback arguments. + * Note there is no way to indicate a 'undefined' return, so it is currently typed as void. + * @return undefined + **/ + noop(): void; + + /** + * Invokes the given iterator function n times. + * Each invocation of iterator is called with an index argument + * @param n Number of times to invoke `iterator`. + * @param iterator Function iterator to invoke `n` times. + * @param context `this` object in `iterator`, optional. + **/ + times(n: number, iterator: (n: number) => TResult, context?: any): TResult[]; + + /** + * Returns a random integer between min and max, inclusive. If you only pass one argument, + * it will return a number between 0 and that number. + * @param max The maximum random number. + * @return A random number between 0 and `max`. + **/ + random(max: number): number; + + /** + * @see _.random + * @param min The minimum random number. + * @return A random number between `min` and `max`. + **/ + random(min: number, max: number): number; + + /** + * Allows you to extend Underscore with your own utility functions. Pass a hash of + * {name: function} definitions to have your functions added to the Underscore object, + * as well as the OOP wrapper. + * @param object Mixin object containing key/function pairs to add to the Underscore object. + **/ + mixin(object: any): void; + + /** + * A mostly-internal function to generate callbacks that can be applied to each element + * in a collection, returning the desired result -- either identity, an arbitrary callback, + * a property matcher, or a propetery accessor. + * @param string|Function|Object value The value to iterate over, usually the key. + * @param any context + * @return Callback that can be applied to each element in a collection. + **/ + iteratee(value: string): Function; + iteratee(value: Function, context?: any): Function; + iteratee(value: Object): Function; + + /** + * Generate a globally-unique id for client-side models or DOM elements that need one. + * If prefix is passed, the id will be appended to it. Without prefix, returns an integer. + * @param prefix A prefix string to start the unique ID with. + * @return Unique string ID beginning with `prefix`. + **/ + uniqueId(prefix?: string): string; + + /** + * Escapes a string for insertion into HTML, replacing &, <, >, ", ', and / characters. + * @param str Raw string to escape. + * @return `str` HTML escaped. + **/ + escape(str: string): string; + + /** + * The opposite of escape, replaces &, <, >, ", and ' with their unescaped counterparts. + * @param str HTML escaped string. + * @return `str` Raw string. + **/ + unescape(str: string): string; + + /** + * If the value of the named property is a function then invoke it; otherwise, return it. + * @param object Object to maybe invoke function `property` on. + * @param property The function by name to invoke on `object`. + * @param defaultValue The value to be returned in case `property` doesn't exist or is undefined. + * @return The result of invoking the function `property` on `object. + **/ + result(object: any, property: string, defaultValue?:any): any; + + /** + * Compiles JavaScript templates into functions that can be evaluated for rendering. Useful + * for rendering complicated bits of HTML from JSON data sources. Template functions can both + * interpolate variables, using <%= ... %>, as well as execute arbitrary JavaScript code, with + * <% ... %>. If you wish to interpolate a value, and have it be HTML-escaped, use <%- ... %> When + * you evaluate a template function, pass in a data object that has properties corresponding to + * the template's free variables. If you're writing a one-off, you can pass the data object as + * the second parameter to template in order to render immediately instead of returning a template + * function. The settings argument should be a hash containing any _.templateSettings that should + * be overridden. + * @param templateString Underscore HTML template. + * @param data Data to use when compiling `templateString`. + * @param settings Settings to use while compiling. + * @return Returns the compiled Underscore HTML template. + **/ + template(templateString: string, settings?: _.TemplateSettings): (...data: any[]) => string; + + /** + * By default, Underscore uses ERB-style template delimiters, change the + * following template settings to use alternative delimiters. + **/ + templateSettings: _.TemplateSettings; + + /** + * Returns an integer timestamp for the current time, using the fastest method available in the runtime. Useful for implementing timing/animation functions. + **/ + now(): number; + + /* ********** + * Chaining * + *********** */ + + /** + * Returns a wrapped object. Calling methods on this object will continue to return wrapped objects + * until value() is used. + * @param obj Object to chain. + * @return Wrapped `obj`. + **/ + chain(obj: T[]): _Chain; + chain(obj: _.Dictionary): _Chain; + chain(obj: T): _Chain; +} + +interface Underscore { + + /* ************* + * Collections * + ************* */ + + /** + * Wrapped type `any[]`. + * @see _.each + **/ + each(iterator: _.ListIterator, context?: any): T[]; + + /** + * @see _.each + **/ + each(iterator: _.ObjectIterator, context?: any): T[]; + + /** + * @see _.each + **/ + forEach(iterator: _.ListIterator, context?: any): T[]; + + /** + * @see _.each + **/ + forEach(iterator: _.ObjectIterator, context?: any): T[]; + + /** + * Wrapped type `any[]`. + * @see _.map + **/ + map(iterator: _.ListIterator, context?: any): TResult[]; + + /** + * Wrapped type `any[]`. + * @see _.map + **/ + map(iterator: _.ObjectIterator, context?: any): TResult[]; + + /** + * @see _.map + **/ + collect(iterator: _.ListIterator, context?: any): TResult[]; + + /** + * @see _.map + **/ + collect(iterator: _.ObjectIterator, context?: any): TResult[]; + + /** + * Wrapped type `any[]`. + * @see _.reduce + **/ + reduce(iterator: _.MemoIterator, memo?: TResult, context?: any): TResult; + + /** + * @see _.reduce + **/ + inject(iterator: _.MemoIterator, memo?: TResult, context?: any): TResult; + + /** + * @see _.reduce + **/ + foldl(iterator: _.MemoIterator, memo?: TResult, context?: any): TResult; + + /** + * Wrapped type `any[]`. + * @see _.reduceRight + **/ + reduceRight(iterator: _.MemoIterator, memo?: TResult, context?: any): TResult; + + /** + * @see _.reduceRight + **/ + foldr(iterator: _.MemoIterator, memo?: TResult, context?: any): TResult; + + /** + * Wrapped type `any[]`. + * @see _.find + **/ + find(iterator: _.ListIterator|_.ObjectIterator, context?: any): T; + + /** + * @see _.find + **/ + find(interator: U): T; + + /** + * @see _.find + **/ + find(interator: string): T; + + /** + * @see _.find + **/ + detect(iterator: _.ListIterator|_.ObjectIterator, context?: any): T; + + /** + * @see _.find + **/ + detect(interator?: U): T; + + /** + * @see _.find + **/ + detect(interator?: string): T; + + /** + * Wrapped type `any[]`. + * @see _.filter + **/ + filter(iterator: _.ListIterator, context?: any): T[]; + + /** + * @see _.filter + **/ + select(iterator: _.ListIterator, context?: any): T[]; + + /** + * Wrapped type `any[]`. + * @see _.where + **/ + where(properties: U): T[]; + + /** + * Wrapped type `any[]`. + * @see _.findWhere + **/ + findWhere(properties: U): T; + + /** + * Wrapped type `any[]`. + * @see _.reject + **/ + reject(iterator: _.ListIterator, context?: any): T[]; + + /** + * Wrapped type `any[]`. + * @see _.all + **/ + all(iterator?: _.ListIterator, context?: any): boolean; + + /** + * @see _.all + **/ + every(iterator?: _.ListIterator, context?: any): boolean; + + /** + * Wrapped type `any[]`. + * @see _.any + **/ + any(iterator?: _.ListIterator, context?: any): boolean; + + /** + * @see _.any + **/ + some(iterator?: _.ListIterator, context?: any): boolean; + + /** + * Wrapped type `any[]`. + * @see _.contains + **/ + contains(value: T, fromIndex? : number): boolean; + + /** + * Alias for 'contains'. + * @see contains + **/ + include(value: T, fromIndex? : number): boolean; + + /** + * Alias for 'contains'. + * @see contains + **/ + includes(value: T, fromIndex? : number): boolean; + + /** + * Wrapped type `any[]`. + * @see _.invoke + **/ + invoke(methodName: string, ...arguments: any[]): any; + + /** + * Wrapped type `any[]`. + * @see _.pluck + **/ + pluck(propertyName: string): any[]; + + /** + * Wrapped type `number[]`. + * @see _.max + **/ + max(): number; + + /** + * Wrapped type `any[]`. + * @see _.max + **/ + max(iterator: _.ListIterator, context?: any): T; + + /** + * Wrapped type `any[]`. + * @see _.max + **/ + max(iterator?: _.ListIterator, context?: any): T; + + /** + * Wrapped type `number[]`. + * @see _.min + **/ + min(): number; + + /** + * Wrapped type `any[]`. + * @see _.min + **/ + min(iterator: _.ListIterator, context?: any): T; + + /** + * Wrapped type `any[]`. + * @see _.min + **/ + min(iterator?: _.ListIterator, context?: any): T; + + /** + * Wrapped type `any[]`. + * @see _.sortBy + **/ + sortBy(iterator?: _.ListIterator, context?: any): T[]; + + /** + * Wrapped type `any[]`. + * @see _.sortBy + **/ + sortBy(iterator: string, context?: any): T[]; + + /** + * Wrapped type `any[]`. + * @see _.groupBy + **/ + groupBy(iterator?: _.ListIterator, context?: any): _.Dictionary<_.List>; + + /** + * Wrapped type `any[]`. + * @see _.groupBy + **/ + groupBy(iterator: string, context?: any): _.Dictionary; + + /** + * Wrapped type `any[]`. + * @see _.indexBy + **/ + indexBy(iterator: _.ListIterator, context?: any): _.Dictionary; + + /** + * Wrapped type `any[]`. + * @see _.indexBy + **/ + indexBy(iterator: string, context?: any): _.Dictionary; + + /** + * Wrapped type `any[]`. + * @see _.countBy + **/ + countBy(iterator?: _.ListIterator, context?: any): _.Dictionary; + + /** + * Wrapped type `any[]`. + * @see _.countBy + **/ + countBy(iterator: string, context?: any): _.Dictionary; + + /** + * Wrapped type `any[]`. + * @see _.shuffle + **/ + shuffle(): T[]; + + /** + * Wrapped type `any[]`. + * @see _.sample + **/ + sample(n: number): T[]; + + /** + * @see _.sample + **/ + sample(): T; + + /** + * Wrapped type `any`. + * @see _.toArray + **/ + toArray(): T[]; + + /** + * Wrapped type `any`. + * @see _.size + **/ + size(): number; + + /********* + * Arrays * + **********/ + + /** + * Wrapped type `any[]`. + * @see _.first + **/ + first(): T; + + /** + * Wrapped type `any[]`. + * @see _.first + **/ + first(n: number): T[]; + + /** + * @see _.first + **/ + head(): T; + + /** + * @see _.first + **/ + head(n: number): T[]; + + /** + * @see _.first + **/ + take(): T; + + /** + * @see _.first + **/ + take(n: number): T[]; + + /** + * Wrapped type `any[]`. + * @see _.initial + **/ + initial(n?: number): T[]; + + /** + * Wrapped type `any[]`. + * @see _.last + **/ + last(): T; + + /** + * Wrapped type `any[]`. + * @see _.last + **/ + last(n: number): T[]; + + /** + * Wrapped type `any[]`. + * @see _.rest + **/ + rest(n?: number): T[]; + + /** + * @see _.rest + **/ + tail(n?: number): T[]; + + /** + * @see _.rest + **/ + drop(n?: number): T[]; + + /** + * Wrapped type `any[]`. + * @see _.compact + **/ + compact(): T[]; + + /** + * Wrapped type `any`. + * @see _.flatten + **/ + flatten(shallow?: boolean): any[]; + + /** + * Wrapped type `any[]`. + * @see _.without + **/ + without(...values: T[]): T[]; + + /** + * Wrapped type `any[]`. + * @see _.partition + **/ + partition(iterator: _.ListIterator, context?: any): T[][]; + + /** + * Wrapped type `any[][]`. + * @see _.union + **/ + union(...arrays: _.List[]): T[]; + + /** + * Wrapped type `any[][]`. + * @see _.intersection + **/ + intersection(...arrays: _.List[]): T[]; + + /** + * Wrapped type `any[]`. + * @see _.difference + **/ + difference(...others: _.List[]): T[]; + + /** + * Wrapped type `any[]`. + * @see _.uniq + **/ + uniq(isSorted?: boolean, iterator?: _.ListIterator): T[]; + + /** + * Wrapped type `any[]`. + * @see _.uniq + **/ + uniq(iterator?: _.ListIterator, context?: any): T[]; + + /** + * @see _.uniq + **/ + unique(isSorted?: boolean, iterator?: _.ListIterator): T[]; + + /** + * @see _.uniq + **/ + unique(iterator?: _.ListIterator, context?: any): T[]; + + /** + * Wrapped type `any[][]`. + * @see _.zip + **/ + zip(...arrays: any[][]): any[][]; + + /** + * Wrapped type `any[][]`. + * @see _.unzip + **/ + unzip(...arrays: any[][]): any[][]; + + /** + * Wrapped type `any[][]`. + * @see _.object + **/ + object(...keyValuePairs: any[][]): any; + + /** + * @see _.object + **/ + object(values?: any): any; + + /** + * Wrapped type `any[]`. + * @see _.indexOf + **/ + indexOf(value: T, isSorted?: boolean): number; + + /** + * @see _.indexOf + **/ + indexOf(value: T, startFrom: number): number; + + /** + * Wrapped type `any[]`. + * @see _.lastIndexOf + **/ + lastIndexOf(value: T, from?: number): number; + + /** + * @see _.findIndex + **/ + findIndex(array: _.List, predicate: _.ListIterator | {}, context?: any): number; + + /** + * @see _.findLastIndex + **/ + findLastIndex(array: _.List, predicate: _.ListIterator | {}, context?: any): number; + + /** + * Wrapped type `any[]`. + * @see _.sortedIndex + **/ + sortedIndex(value: T, iterator?: (x: T) => any, context?: any): number; + + /** + * Wrapped type `number`. + * @see _.range + **/ + range(stop: number, step?: number): number[]; + + /** + * Wrapped type `number`. + * @see _.range + **/ + range(): number[]; + + /** + * Wrapped type any[][]. + * @see _.chunk + **/ + chunk(): any[][]; + + /* *********** + * Functions * + ************ */ + + /** + * Wrapped type `Function`. + * @see _.bind + **/ + bind(object: any, ...arguments: any[]): Function; + + /** + * Wrapped type `object`. + * @see _.bindAll + **/ + bindAll(...methodNames: string[]): any; + + /** + * Wrapped type `Function`. + * @see _.partial + **/ + partial(...arguments: any[]): Function; + + /** + * Wrapped type `Function`. + * @see _.memoize + **/ + memoize(hashFn?: (n: any) => string): Function; + + /** + * Wrapped type `Function`. + * @see _.defer + **/ + defer(...arguments: any[]): void; + + /** + * Wrapped type `Function`. + * @see _.delay + **/ + delay(wait: number, ...arguments: any[]): any; + + /** + * @see _.delay + **/ + delay(...arguments: any[]): any; + + /** + * Wrapped type `Function`. + * @see _.throttle + **/ + throttle(wait: number, options?: _.ThrottleSettings): Function & _.Cancelable; + + /** + * Wrapped type `Function`. + * @see _.debounce + **/ + debounce(wait: number, immediate?: boolean): Function & _.Cancelable; + + /** + * Wrapped type `Function`. + * @see _.once + **/ + once(): Function; + + /** + * Wrapped type `Function`. + * @see _.once + **/ + restArgs(starIndex?: number) : Function; + + /** + * Wrapped type `number`. + * @see _.after + **/ + after(fn: Function): Function; + + /** + * Wrapped type `number`. + * @see _.before + **/ + before(fn: Function): Function; + + /** + * Wrapped type `Function`. + * @see _.wrap + **/ + wrap(wrapper: Function): () => Function; + + /** + * Wrapped type `Function`. + * @see _.negate + **/ + negate(): boolean; + + /** + * Wrapped type `Function[]`. + * @see _.compose + **/ + compose(...functions: Function[]): Function; + + /********* * + * Objects * + ********** */ + + /** + * Wrapped type `object`. + * @see _.keys + **/ + keys(): string[]; + + /** + * Wrapped type `object`. + * @see _.allKeys + **/ + allKeys(): string[]; + + /** + * Wrapped type `object`. + * @see _.values + **/ + values(): T[]; + + /** + * Wrapped type `object`. + * @see _.pairs + **/ + pairs(): any[][]; + + /** + * Wrapped type `object`. + * @see _.invert + **/ + invert(): any; + + /** + * Wrapped type `object`. + * @see _.functions + **/ + functions(): string[]; + + /** + * @see _.functions + **/ + methods(): string[]; + + /** + * Wrapped type `object`. + * @see _.extend + **/ + extend(...sources: any[]): any; + + /** + * Wrapped type `object`. + * @see _.extend + **/ + findKey(predicate: _.ObjectIterator, context? : any): any + + /** + * Wrapped type `object`. + * @see _.pick + **/ + pick(...keys: any[]): any; + pick(keys: any[]): any; + pick(fn: (value: any, key: any, object: any) => any): any; + + /** + * Wrapped type `object`. + * @see _.omit + **/ + omit(...keys: string[]): any; + omit(keys: string[]): any; + omit(fn: Function): any; + + /** + * Wrapped type `object`. + * @see _.defaults + **/ + defaults(...defaults: any[]): any; + + /** + * Wrapped type `any`. + * @see _.create + **/ + create(props?: Object): any; + + /** + * Wrapped type `any[]`. + * @see _.clone + **/ + clone(): T; + + /** + * Wrapped type `object`. + * @see _.tap + **/ + tap(interceptor: (...as: any[]) => any): any; + + /** + * Wrapped type `object`. + * @see _.has + **/ + has(key: string): boolean; + + /** + * Wrapped type `any[]`. + * @see _.matches + **/ + matches(): _.ListIterator; + + /** + * Wrapped type `any[]`. + * @see _.matcher + **/ + matcher(): _.ListIterator; + + /** + * Wrapped type `string`. + * @see _.property + **/ + property(): (object: Object) => any; + + /** + * Wrapped type `object`. + * @see _.propertyOf + **/ + propertyOf(): (key: string) => any; + + /** + * Wrapped type `object`. + * @see _.isEqual + **/ + isEqual(other: any): boolean; + + /** + * Wrapped type `object`. + * @see _.isEmpty + **/ + isEmpty(): boolean; + + /** + * Wrapped type `object`. + * @see _.isMatch + **/ + isMatch(): boolean; + + /** + * Wrapped type `object`. + * @see _.isElement + **/ + isElement(): boolean; + + /** + * Wrapped type `object`. + * @see _.isArray + **/ + isArray(): boolean; + + /** + * Wrapped type `object`. + * @see _.isSymbol + **/ + isSymbol(): boolean; + + /** + * Wrapped type `object`. + * @see _.isObject + **/ + isObject(): boolean; + + /** + * Wrapped type `object`. + * @see _.isArguments + **/ + isArguments(): boolean; + + /** + * Wrapped type `object`. + * @see _.isFunction + **/ + isFunction(): boolean; + + /** + * Wrapped type `object`. + * @see _.isError + **/ + isError(): boolean; + + /** + * Wrapped type `object`. + * @see _.isString + **/ + isString(): boolean; + + /** + * Wrapped type `object`. + * @see _.isNumber + **/ + isNumber(): boolean; + + /** + * Wrapped type `object`. + * @see _.isFinite + **/ + isFinite(): boolean; + + /** + * Wrapped type `object`. + * @see _.isBoolean + **/ + isBoolean(): boolean; + + /** + * Wrapped type `object`. + * @see _.isDate + **/ + isDate(): boolean; + + /** + * Wrapped type `object`. + * @see _.isRegExp + **/ + isRegExp(): boolean; + + /** + * Wrapped type `object`. + * @see _.isNaN + **/ + isNaN(): boolean; + + /** + * Wrapped type `object`. + * @see _.isNull + **/ + isNull(): boolean; + + /** + * Wrapped type `object`. + * @see _.isUndefined + **/ + isUndefined(): boolean; + + /********* * + * Utility * + ********** */ + + /** + * Wrapped type `any`. + * @see _.identity + **/ + identity(): any; + + /** + * Wrapped type `any`. + * @see _.constant + **/ + constant(): () => T; + + /** + * Wrapped type `any`. + * @see _.noop + **/ + noop(): void; + + /** + * Wrapped type `number`. + * @see _.times + **/ + times(iterator: (n: number) => TResult, context?: any): TResult[]; + + /** + * Wrapped type `number`. + * @see _.random + **/ + random(): number; + /** + * Wrapped type `number`. + * @see _.random + **/ + random(max: number): number; + + /** + * Wrapped type `object`. + * @see _.mixin + **/ + mixin(): void; + + /** + * Wrapped type `string|Function|Object`. + * @see _.iteratee + **/ + iteratee(context?: any): Function; + + /** + * Wrapped type `string`. + * @see _.uniqueId + **/ + uniqueId(): string; + + /** + * Wrapped type `string`. + * @see _.escape + **/ + escape(): string; + + /** + * Wrapped type `string`. + * @see _.unescape + **/ + unescape(): string; + + /** + * Wrapped type `object`. + * @see _.result + **/ + result(property: string, defaultValue?:any): any; + + /** + * Wrapped type `string`. + * @see _.template + **/ + template(settings?: _.TemplateSettings): (...data: any[]) => string; + + /********** * + * Chaining * + *********** */ + + /** + * Wrapped type `any`. + * @see _.chain + **/ + chain(): _Chain; + + /** + * Wrapped type `any`. + * Extracts the value of a wrapped object. + * @return Value of the wrapped object. + **/ + value(): TResult; +} + +interface _Chain { + + /* ************* + * Collections * + ************* */ + + /** + * Wrapped type `any[]`. + * @see _.each + **/ + each(iterator: _.ListIterator, context?: any): _Chain; + + /** + * @see _.each + **/ + each(iterator: _.ObjectIterator, context?: any): _Chain; + + /** + * @see _.each + **/ + forEach(iterator: _.ListIterator, context?: any): _Chain; + + /** + * @see _.each + **/ + forEach(iterator: _.ObjectIterator, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.map + **/ + map(iterator: _.ListIterator, context?: any): _ChainOfArrays; + + /** + * Wrapped type `any[]`. + * @see _.map + **/ + map(iterator: _.ListIterator, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.map + **/ + map(iterator: _.ObjectIterator, context?: any): _ChainOfArrays; + + /** + * Wrapped type `any[]`. + * @see _.map + **/ + map(iterator: _.ObjectIterator, context?: any): _Chain; + + /** + * @see _.map + **/ + collect(iterator: _.ListIterator, context?: any): _Chain; + + /** + * @see _.map + **/ + collect(iterator: _.ObjectIterator, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.reduce + **/ + reduce(iterator: _.MemoIterator, memo?: TResult, context?: any): _ChainSingle; + + /** + * @see _.reduce + **/ + inject(iterator: _.MemoIterator, memo?: TResult, context?: any): _ChainSingle; + + /** + * @see _.reduce + **/ + foldl(iterator: _.MemoIterator, memo?: TResult, context?: any): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.reduceRight + **/ + reduceRight(iterator: _.MemoIterator, memo?: TResult, context?: any): _ChainSingle; + + /** + * @see _.reduceRight + **/ + foldr(iterator: _.MemoIterator, memo?: TResult, context?: any): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.find + **/ + find(iterator: _.ListIterator|_.ObjectIterator, context?: any): _ChainSingle; + + /** + * @see _.find + **/ + find(interator: U): _ChainSingle; + + /** + * @see _.find + **/ + find(interator: string): _ChainSingle; + + /** + * @see _.find + **/ + detect(iterator: _.ListIterator|_.ObjectIterator, context?: any): _ChainSingle; + + /** + * @see _.find + **/ + detect(interator: U): _ChainSingle; + + /** + * @see _.find + **/ + detect(interator: string): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.filter + **/ + filter(iterator: _.ListIterator, context?: any): _Chain; + + /** + * @see _.filter + **/ + select(iterator: _.ListIterator, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.where + **/ + where(properties: U): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.findWhere + **/ + findWhere(properties: U): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.reject + **/ + reject(iterator: _.ListIterator, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.all + **/ + all(iterator?: _.ListIterator, context?: any): _ChainSingle; + + /** + * @see _.all + **/ + every(iterator?: _.ListIterator, context?: any): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.any + **/ + any(iterator?: _.ListIterator, context?: any): _ChainSingle; + + /** + * @see _.any + **/ + some(iterator?: _.ListIterator, context?: any): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.contains + **/ + contains(value: T, fromIndex?: number): _ChainSingle; + + /** + * Alias for 'contains'. + * @see contains + **/ + include(value: T, fromIndex?: number): _ChainSingle; + + /** + * Alias for 'contains'. + * @see contains + **/ + includes(value: T, fromIndex?: number): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.invoke + **/ + invoke(methodName: string, ...arguments: any[]): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.pluck + **/ + pluck(propertyName: string): _Chain; + + /** + * Wrapped type `number[]`. + * @see _.max + **/ + max(): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.max + **/ + max(iterator: _.ListIterator, context?: any): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.max + **/ + max(iterator?: _.ListIterator, context?: any): _ChainSingle; + + /** + * Wrapped type `number[]`. + * @see _.min + **/ + min(): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.min + **/ + min(iterator: _.ListIterator, context?: any): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.min + **/ + min(iterator?: _.ListIterator, context?: any): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.sortBy + **/ + sortBy(iterator?: _.ListIterator, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.sortBy + **/ + sortBy(iterator: string, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.groupBy + **/ + groupBy(iterator?: _.ListIterator, context?: any): _ChainOfArrays; + + /** + * Wrapped type `any[]`. + * @see _.groupBy + **/ + groupBy(iterator: string, context?: any): _ChainOfArrays; + + /** + * Wrapped type `any[]`. + * @see _.indexBy + **/ + indexBy(iterator: _.ListIterator, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.indexBy + **/ + indexBy(iterator: string, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.countBy + **/ + countBy(iterator?: _.ListIterator, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.countBy + **/ + countBy(iterator: string, context?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.shuffle + **/ + shuffle(): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.sample + **/ + sample(n: number): _Chain; + + /** + * @see _.sample + **/ + sample(): _Chain; + + /** + * Wrapped type `any`. + * @see _.toArray + **/ + toArray(): _Chain; + + /** + * Wrapped type `any`. + * @see _.size + **/ + size(): _ChainSingle; + + /********* + * Arrays * + **********/ + + /** + * Wrapped type `any[]`. + * @see _.first + **/ + first(): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.first + **/ + first(n: number): _Chain; + + /** + * @see _.first + **/ + head(): _Chain; + + /** + * @see _.first + **/ + head(n: number): _Chain; + + /** + * @see _.first + **/ + take(): _Chain; + + /** + * @see _.first + **/ + take(n: number): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.initial + **/ + initial(n?: number): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.last + **/ + last(): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.last + **/ + last(n: number): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.rest + **/ + rest(n?: number): _Chain; + + /** + * @see _.rest + **/ + tail(n?: number): _Chain; + + /** + * @see _.rest + **/ + drop(n?: number): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.compact + **/ + compact(): _Chain; + + /** + * Wrapped type `any`. + * @see _.flatten + **/ + flatten(shallow?: boolean): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.without + **/ + without(...values: T[]): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.partition + **/ + partition(iterator: _.ListIterator, context?: any): _Chain; + + /** + * Wrapped type `any[][]`. + * @see _.union + **/ + union(...arrays: _.List[]): _Chain; + + /** + * Wrapped type `any[][]`. + * @see _.intersection + **/ + intersection(...arrays: _.List[]): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.difference + **/ + difference(...others: _.List[]): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.uniq + **/ + uniq(isSorted?: boolean, iterator?: _.ListIterator): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.uniq + **/ + uniq(iterator?: _.ListIterator, context?: any): _Chain; + + /** + * @see _.uniq + **/ + unique(isSorted?: boolean, iterator?: _.ListIterator): _Chain; + + /** + * @see _.uniq + **/ + unique(iterator?: _.ListIterator, context?: any): _Chain; + + /** + * Wrapped type `any[][]`. + * @see _.zip + **/ + zip(...arrays: any[][]): _Chain; + + /** + * Wrapped type `any[][]`. + * @see _.unzip + **/ + unzip(...arrays: any[][]): _Chain; + + /** + * Wrapped type `any[][]`. + * @see _.object + **/ + object(...keyValuePairs: any[][]): _Chain; + + /** + * @see _.object + **/ + object(values?: any): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.indexOf + **/ + indexOf(value: T, isSorted?: boolean): _ChainSingle; + + /** + * @see _.indexOf + **/ + indexOf(value: T, startFrom: number): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.lastIndexOf + **/ + lastIndexOf(value: T, from?: number): _ChainSingle; + + /** + * @see _.findIndex + **/ + findIndex(predicate: _.ListIterator | {}, context?: any): _ChainSingle; + + /** + * @see _.findLastIndex + **/ + findLastIndex(predicate: _.ListIterator | {}, context?: any): _ChainSingle; + + /** + * Wrapped type `any[]`. + * @see _.sortedIndex + **/ + sortedIndex(value: T, iterator?: (x: T) => any, context?: any): _ChainSingle; + + /** + * Wrapped type `number`. + * @see _.range + **/ + range(stop: number, step?: number): _Chain; + + /** + * Wrapped type `number`. + * @see _.range + **/ + range(): _Chain; + + /** + * Wrapped type `any[][]`. + * @see _.chunk + **/ + chunk(): _Chain; + + /* *********** + * Functions * + ************ */ + + /** + * Wrapped type `Function`. + * @see _.bind + **/ + bind(object: any, ...arguments: any[]): _Chain; + + /** + * Wrapped type `object`. + * @see _.bindAll + **/ + bindAll(...methodNames: string[]): _Chain; + + /** + * Wrapped type `Function`. + * @see _.partial + **/ + partial(...arguments: any[]): _Chain; + + /** + * Wrapped type `Function`. + * @see _.memoize + **/ + memoize(hashFn?: (n: any) => string): _Chain; + + /** + * Wrapped type `Function`. + * @see _.defer + **/ + defer(...arguments: any[]): _Chain; + + /** + * Wrapped type `Function`. + * @see _.delay + **/ + delay(wait: number, ...arguments: any[]): _Chain; + + /** + * @see _.delay + **/ + delay(...arguments: any[]): _Chain; + + /** + * Wrapped type `Function`. + * @see _.throttle + **/ + throttle(wait: number, options?: _.ThrottleSettings): _Chain; + + /** + * Wrapped type `Function`. + * @see _.debounce + **/ + debounce(wait: number, immediate?: boolean): _Chain; + + /** + * Wrapped type `Function`. + * @see _.once + **/ + once(): _Chain; + + /** + * Wrapped type `Function`. + * @see _.once + **/ + restArgs(startIndex? : number): _Chain; + + /** + * Wrapped type `number`. + * @see _.after + **/ + after(func: Function): _Chain; + + /** + * Wrapped type `number`. + * @see _.before + **/ + before(fn: Function): _Chain; + + /** + * Wrapped type `Function`. + * @see _.wrap + **/ + wrap(wrapper: Function): () => _Chain; + + /** + * Wrapped type `Function`. + * @see _.negate + **/ + negate(): _Chain; + + /** + * Wrapped type `Function[]`. + * @see _.compose + **/ + compose(...functions: Function[]): _Chain; + + /********* * + * Objects * + ********** */ + + /** + * Wrapped type `object`. + * @see _.keys + **/ + keys(): _Chain; + + /** + * Wrapped type `object`. + * @see _.allKeys + **/ + allKeys(): _Chain; + + /** + * Wrapped type `object`. + * @see _.values + **/ + values(): _Chain; + + /** + * Wrapped type `object`. + * @see _.pairs + **/ + pairs(): _Chain; + + /** + * Wrapped type `object`. + * @see _.invert + **/ + invert(): _Chain; + + /** + * Wrapped type `object`. + * @see _.functions + **/ + functions(): _Chain; + + /** + * @see _.functions + **/ + methods(): _Chain; + + /** + * Wrapped type `object`. + * @see _.extend + **/ + extend(...sources: any[]): _Chain; + + /** + * Wrapped type `object`. + * @see _.extend + **/ + findKey(predicate: _.ObjectIterator, context? : any): _Chain + + /** + * Wrapped type `object`. + * @see _.pick + **/ + pick(...keys: any[]): _Chain; + pick(keys: any[]): _Chain; + pick(fn: (value: any, key: any, object: any) => any): _Chain; + + /** + * Wrapped type `object`. + * @see _.omit + **/ + omit(...keys: string[]): _Chain; + omit(keys: string[]): _Chain; + omit(iteratee: Function): _Chain; + + /** + * Wrapped type `object`. + * @see _.defaults + **/ + defaults(...defaults: any[]): _Chain; + + /** + * Wrapped type `any`. + * @see _.create + **/ + create(props?: Object): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.clone + **/ + clone(): _Chain; + + /** + * Wrapped type `object`. + * @see _.tap + **/ + tap(interceptor: (...as: any[]) => any): _Chain; + + /** + * Wrapped type `object`. + * @see _.has + **/ + has(key: string): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.matches + **/ + matches(): _Chain; + + /** + * Wrapped type `any[]`. + * @see _.matcher + **/ + matcher(): _Chain; + + /** + * Wrapped type `string`. + * @see _.property + **/ + property(): _Chain; + + /** + * Wrapped type `object`. + * @see _.propertyOf + **/ + propertyOf(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isEqual + **/ + isEqual(other: any): _Chain; + + /** + * Wrapped type `object`. + * @see _.isEmpty + **/ + isEmpty(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isMatch + **/ + isMatch(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isElement + **/ + isElement(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isArray + **/ + isArray(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isSymbol + **/ + isSymbol(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isObject + **/ + isObject(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isArguments + **/ + isArguments(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isFunction + **/ + isFunction(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isError + **/ + isError(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isString + **/ + isString(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isNumber + **/ + isNumber(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isFinite + **/ + isFinite(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isBoolean + **/ + isBoolean(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isDate + **/ + isDate(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isRegExp + **/ + isRegExp(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isNaN + **/ + isNaN(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isNull + **/ + isNull(): _Chain; + + /** + * Wrapped type `object`. + * @see _.isUndefined + **/ + isUndefined(): _Chain; + + /********* * + * Utility * + ********** */ + + /** + * Wrapped type `any`. + * @see _.identity + **/ + identity(): _Chain; + + /** + * Wrapped type `any`. + * @see _.constant + **/ + constant(): _Chain; + + /** + * Wrapped type `any`. + * @see _.noop + **/ + noop(): _Chain; + + /** + * Wrapped type `number`. + * @see _.times + **/ + times(iterator: (n: number) => TResult, context?: any): _Chain; + + /** + * Wrapped type `number`. + * @see _.random + **/ + random(): _Chain; + /** + * Wrapped type `number`. + * @see _.random + **/ + random(max: number): _Chain; + + /** + * Wrapped type `object`. + * @see _.mixin + **/ + mixin(): _Chain; + + /** + * Wrapped type `string|Function|Object`. + * @see _.iteratee + **/ + iteratee(context?: any): _Chain; + + /** + * Wrapped type `string`. + * @see _.uniqueId + **/ + uniqueId(): _Chain; + + /** + * Wrapped type `string`. + * @see _.escape + **/ + escape(): _Chain; + + /** + * Wrapped type `string`. + * @see _.unescape + **/ + unescape(): _Chain; + + /** + * Wrapped type `object`. + * @see _.result + **/ + result(property: string, defaultValue?:any): _Chain; + + /** + * Wrapped type `string`. + * @see _.template + **/ + template(settings?: _.TemplateSettings): (...data: any[]) => _Chain; + + /************* * + * Array proxy * + ************** */ + + /** + * Returns a new array comprised of the array on which it is called + * joined with the array(s) and/or value(s) provided as arguments. + * @param arr Arrays and/or values to concatenate into a new array. See the discussion below for details. + * @return A new array comprised of the array on which it is called + **/ + concat(...arr: Array): _Chain; + + /** + * Join all elements of an array into a string. + * @param separator Optional. Specifies a string to separate each element of the array. The separator is converted to a string if necessary. If omitted, the array elements are separated with a comma. + * @return The string conversions of all array elements joined into one string. + **/ + join(separator?: any): _ChainSingle; + + /** + * Removes the last element from an array and returns that element. + * @return Returns the popped element. + **/ + pop(): _ChainSingle; + + /** + * Adds one or more elements to the end of an array and returns the new length of the array. + * @param item The elements to add to the end of the array. + * @return The array with the element added to the end. + **/ + push(...item: Array): _Chain; + + /** + * Reverses an array in place. The first array element becomes the last and the last becomes the first. + * @return The reversed array. + **/ + reverse(): _Chain; + + /** + * Removes the first element from an array and returns that element. This method changes the length of the array. + * @return The shifted element. + **/ + shift(): _ChainSingle; + + /** + * Returns a shallow copy of a portion of an array into a new array object. + * @param start Zero-based index at which to begin extraction. + * @param end Optional. Zero-based index at which to end extraction. slice extracts up to but not including end. + * @return A shallow copy of a portion of an array into a new array object. + **/ + slice(start: number, end?: number): _Chain; + + /** + * Sorts the elements of an array in place and returns the array. The sort is not necessarily stable. The default sort order is according to string Unicode code points. + * @param compareFn Optional. Specifies a function that defines the sort order. If omitted, the array is sorted according to each character's Unicode code point value, according to the string conversion of each element. + * @return The sorted array. + **/ + sort(compareFn: (a: T, b: T) => boolean): _Chain; + + /** + * Changes the content of an array by removing existing elements and/or adding new elements. + * @param index Index at which to start changing the array. If greater than the length of the array, actual starting index will be set to the length of the array. If negative, will begin that many elements from the end. + * @param quantity An integer indicating the number of old array elements to remove. If deleteCount is 0, no elements are removed. In this case, you should specify at least one new element. If deleteCount is greater than the number of elements left in the array starting at index, then all of the elements through the end of the array will be deleted. + * @param items The element to add to the array. If you don't specify any elements, splice will only remove elements from the array. + * @return An array containing the deleted elements. If only one element is removed, an array of one element is returned. If no elements are removed, an empty array is returned. + **/ + splice(index: number, quantity: number, ...items: Array): _Chain; + + /** + * A string representing the specified array and its elements. + * @return A string representing the specified array and its elements. + **/ + toString(): _ChainSingle; + + /** + * Adds one or more elements to the beginning of an array and returns the new length of the array. + * @param items The elements to add to the front of the array. + * @return The array with the element added to the beginning. + **/ + unshift(...items: Array): _Chain; + + /********** * + * Chaining * + *********** */ + + /** + * Wrapped type `any`. + * @see _.chain + **/ + chain(): _Chain; + + /** + * Wrapped type `any`. + * @see _.value + **/ + value(): T[]; +} +interface _ChainSingle { + value(): T; +} +interface _ChainOfArrays extends _Chain { + flatten(shallow?: boolean): _Chain; + mapObject(fn: _.ListIterator): _ChainOfArrays; +} + +declare var _: UnderscoreStatic; + +declare module "underscore" { + export = _; +} \ No newline at end of file diff --git a/build/lib/typings/vinyl.d.ts b/build/lib/typings/vinyl.d.ts new file mode 100644 index 0000000000..a85632e172 --- /dev/null +++ b/build/lib/typings/vinyl.d.ts @@ -0,0 +1,112 @@ +// Type definitions for vinyl 0.4.3 +// Project: https://github.com/wearefractal/vinyl +// Definitions by: vvakame , jedmao +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module "vinyl" { + + import fs = require("fs"); + + /** + * A virtual file format. + */ + class File { + constructor(options?: { + /** + * Default: process.cwd() + */ + cwd?: string; + /** + * Used for relative pathing. Typically where a glob starts. + */ + base?: string; + /** + * Full path to the file. + */ + path?: string; + /** + * Path history. Has no effect if options.path is passed. + */ + history?: string[]; + /** + * The result of an fs.stat call. See fs.Stats for more information. + */ + stat?: fs.Stats; + /** + * File contents. + * Type: Buffer, Stream, or null + */ + contents?: Buffer | NodeJS.ReadWriteStream; + }); + + /** + * Default: process.cwd() + */ + public cwd: string; + /** + * Used for relative pathing. Typically where a glob starts. + */ + public base: string; + /** + * Full path to the file. + */ + public path: string; + public stat: fs.Stats; + /** + * Type: Buffer|Stream|null (Default: null) + */ + public contents: Buffer | NodeJS.ReadableStream; + /** + * Returns path.relative for the file base and file path. + * Example: + * var file = new File({ + * cwd: "/", + * base: "/test/", + * path: "/test/file.js" + * }); + * console.log(file.relative); // file.js + */ + public relative: string; + + public isBuffer(): boolean; + + public isStream(): boolean; + + public isNull(): boolean; + + public isDirectory(): boolean; + + /** + * Returns a new File object with all attributes cloned. Custom attributes are deep-cloned. + */ + public clone(opts?: { contents?: boolean }): File; + + /** + * If file.contents is a Buffer, it will write it to the stream. + * If file.contents is a Stream, it will pipe it to the stream. + * If file.contents is null, it will do nothing. + */ + public pipe( + stream: T, + opts?: { + /** + * If false, the destination stream will not be ended (same as node core). + */ + end?: boolean; + }): T; + + /** + * Returns a pretty String interpretation of the File. Useful for console.log. + */ + public inspect(): string; + } + + /** + * This is required as per: + * https://github.com/Microsoft/TypeScript/issues/5073 + */ + namespace File {} + + export = File; + +} \ No newline at end of file diff --git a/build/lib/util.js b/build/lib/util.js new file mode 100644 index 0000000000..5583dc4a56 --- /dev/null +++ b/build/lib/util.js @@ -0,0 +1,213 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var es = require("event-stream"); +var debounce = require("debounce"); +var _filter = require("gulp-filter"); +var rename = require("gulp-rename"); +var _ = require("underscore"); +var path = require("path"); +var fs = require("fs"); +var _rimraf = require("rimraf"); +var git = require("./git"); +var VinylFile = require("vinyl"); +var NoCancellationToken = { isCancellationRequested: function () { return false; } }; +function incremental(streamProvider, initial, supportsCancellation) { + var input = es.through(); + var output = es.through(); + var state = 'idle'; + var buffer = Object.create(null); + var token = !supportsCancellation ? null : { isCancellationRequested: function () { return Object.keys(buffer).length > 0; } }; + var run = function (input, isCancellable) { + state = 'running'; + var stream = !supportsCancellation ? streamProvider() : streamProvider(isCancellable ? token : NoCancellationToken); + input + .pipe(stream) + .pipe(es.through(null, function () { + state = 'idle'; + eventuallyRun(); + })) + .pipe(output); + }; + if (initial) { + run(initial, false); + } + var eventuallyRun = debounce(function () { + var paths = Object.keys(buffer); + if (paths.length === 0) { + return; + } + var data = paths.map(function (path) { return buffer[path]; }); + buffer = Object.create(null); + run(es.readArray(data), true); + }, 500); + input.on('data', function (f) { + buffer[f.path] = f; + if (state === 'idle') { + eventuallyRun(); + } + }); + return es.duplex(input, output); +} +exports.incremental = incremental; +function fixWin32DirectoryPermissions() { + if (!/win32/.test(process.platform)) { + return es.through(); + } + return es.mapSync(function (f) { + if (f.stat && f.stat.isDirectory && f.stat.isDirectory()) { + f.stat.mode = 16877; + } + return f; + }); +} +exports.fixWin32DirectoryPermissions = fixWin32DirectoryPermissions; +function setExecutableBit(pattern) { + var setBit = es.mapSync(function (f) { + f.stat.mode = 33261; + return f; + }); + if (!pattern) { + return setBit; + } + var input = es.through(); + var filter = _filter(pattern, { restore: true }); + var output = input + .pipe(filter) + .pipe(setBit) + .pipe(filter.restore); + return es.duplex(input, output); +} +exports.setExecutableBit = setExecutableBit; +function toFileUri(filePath) { + var match = filePath.match(/^([a-z])\:(.*)$/i); + if (match) { + filePath = '/' + match[1].toUpperCase() + ':' + match[2]; + } + return 'file://' + filePath.replace(/\\/g, '/'); +} +exports.toFileUri = toFileUri; +function skipDirectories() { + return es.mapSync(function (f) { + if (!f.isDirectory()) { + return f; + } + }); +} +exports.skipDirectories = skipDirectories; +function cleanNodeModule(name, excludes, includes) { + var toGlob = function (path) { return '**/node_modules/' + name + (path ? '/' + path : ''); }; + var negate = function (str) { return '!' + str; }; + var allFilter = _filter(toGlob('**'), { restore: true }); + var globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob))); + var input = es.through(); + var nodeModuleInput = input.pipe(allFilter); + var output = nodeModuleInput.pipe(_filter(globs)); + if (includes) { + var includeGlobs = includes.map(toGlob); + output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs))); + } + output = output.pipe(allFilter.restore); + return es.duplex(input, output); +} +exports.cleanNodeModule = cleanNodeModule; +function loadSourcemaps() { + var input = es.through(); + var output = input + .pipe(es.map(function (f, cb) { + if (f.sourceMap) { + cb(null, f); + return; + } + if (!f.contents) { + cb(new Error('empty file')); + return; + } + var contents = f.contents.toString('utf8'); + var reg = /\/\/# sourceMappingURL=(.*)$/g; + var lastMatch = null, match = null; + while (match = reg.exec(contents)) { + lastMatch = match; + } + if (!lastMatch) { + f.sourceMap = { + version: 3, + names: [], + mappings: '', + sources: [f.relative.replace(/\//g, '/')], + sourcesContent: [contents] + }; + cb(null, f); + return; + } + f.contents = new Buffer(contents.replace(/\/\/# sourceMappingURL=(.*)$/g, ''), 'utf8'); + fs.readFile(path.join(path.dirname(f.path), lastMatch[1]), 'utf8', function (err, contents) { + if (err) { + return cb(err); + } + f.sourceMap = JSON.parse(contents); + cb(null, f); + }); + })); + return es.duplex(input, output); +} +exports.loadSourcemaps = loadSourcemaps; +function stripSourceMappingURL() { + var input = es.through(); + var output = input + .pipe(es.mapSync(function (f) { + var contents = f.contents.toString('utf8'); + f.contents = new Buffer(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8'); + return f; + })); + return es.duplex(input, output); +} +exports.stripSourceMappingURL = stripSourceMappingURL; +function rimraf(dir) { + var retries = 0; + var retry = function (cb) { + _rimraf(dir, { maxBusyTries: 1 }, function (err) { + if (!err) { + return cb(); + } + ; + if (err.code === 'ENOTEMPTY' && ++retries < 5) { + return setTimeout(function () { return retry(cb); }, 10); + } + return cb(err); + }); + }; + return function (cb) { return retry(cb); }; +} +exports.rimraf = rimraf; +function getVersion(root) { + var version = process.env['BUILD_SOURCEVERSION']; + if (!version || !/^[0-9a-f]{40}$/i.test(version)) { + version = git.getVersion(root); + } + return version; +} +exports.getVersion = getVersion; +function rebase(count) { + return rename(function (f) { + var parts = f.dirname.split(/[\/\\]/); + f.dirname = parts.slice(count).join(path.sep); + }); +} +exports.rebase = rebase; +function filter(fn) { + var result = es.through(function (data) { + if (fn(data)) { + this.emit('data', data); + } + else { + result.restore.push(data); + } + }); + result.restore = es.through(); + return result; +} +exports.filter = filter; diff --git a/build/lib/util.ts b/build/lib/util.ts new file mode 100644 index 0000000000..ac2dd31254 --- /dev/null +++ b/build/lib/util.ts @@ -0,0 +1,271 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as es from 'event-stream'; +import * as debounce from 'debounce'; +import * as _filter from 'gulp-filter'; +import * as rename from 'gulp-rename'; +import * as _ from 'underscore'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as _rimraf from 'rimraf'; +import * as git from './git'; +import * as VinylFile from 'vinyl'; +import { ThroughStream } from 'through'; +import * as sm from 'source-map'; + +export interface ICancellationToken { + isCancellationRequested(): boolean; +} + +const NoCancellationToken: ICancellationToken = { isCancellationRequested: () => false }; + +export interface IStreamProvider { + (cancellationToken?: ICancellationToken): NodeJS.ReadWriteStream; +} + +export function incremental(streamProvider: IStreamProvider, initial: NodeJS.ReadWriteStream, supportsCancellation: boolean): NodeJS.ReadWriteStream { + const input = es.through(); + const output = es.through(); + let state = 'idle'; + let buffer = Object.create(null); + + const token: ICancellationToken = !supportsCancellation ? null : { isCancellationRequested: () => Object.keys(buffer).length > 0 }; + + const run = (input, isCancellable) => { + state = 'running'; + + const stream = !supportsCancellation ? streamProvider() : streamProvider(isCancellable ? token : NoCancellationToken); + + input + .pipe(stream) + .pipe(es.through(null, () => { + state = 'idle'; + eventuallyRun(); + })) + .pipe(output); + }; + + if (initial) { + run(initial, false); + } + + const eventuallyRun = debounce(() => { + const paths = Object.keys(buffer); + + if (paths.length === 0) { + return; + } + + const data = paths.map(path => buffer[path]); + buffer = Object.create(null); + run(es.readArray(data), true); + }, 500); + + input.on('data', (f: any) => { + buffer[f.path] = f; + + if (state === 'idle') { + eventuallyRun(); + } + }); + + return es.duplex(input, output); +} + +export function fixWin32DirectoryPermissions(): NodeJS.ReadWriteStream { + if (!/win32/.test(process.platform)) { + return es.through(); + } + + return es.mapSync(f => { + if (f.stat && f.stat.isDirectory && f.stat.isDirectory()) { + f.stat.mode = 16877; + } + + return f; + }); +} + +export function setExecutableBit(pattern: string | string[]): NodeJS.ReadWriteStream { + var setBit = es.mapSync(f => { + f.stat.mode = /* 100755 */ 33261; + return f; + }); + + if (!pattern) { + return setBit; + } + + var input = es.through(); + var filter = _filter(pattern, { restore: true }); + var output = input + .pipe(filter) + .pipe(setBit) + .pipe(filter.restore); + + return es.duplex(input, output); +} + +export function toFileUri(filePath: string): string { + const match = filePath.match(/^([a-z])\:(.*)$/i); + + if (match) { + filePath = '/' + match[1].toUpperCase() + ':' + match[2]; + } + + return 'file://' + filePath.replace(/\\/g, '/'); +} + +export function skipDirectories(): NodeJS.ReadWriteStream { + return es.mapSync(f => { + if (!f.isDirectory()) { + return f; + } + }); +} + +export function cleanNodeModule(name: string, excludes: string[], includes: string[]): NodeJS.ReadWriteStream { + const toGlob = (path: string) => '**/node_modules/' + name + (path ? '/' + path : ''); + const negate = (str: string) => '!' + str; + + const allFilter = _filter(toGlob('**'), { restore: true }); + const globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob))); + + const input = es.through(); + const nodeModuleInput = input.pipe(allFilter); + let output: NodeJS.ReadWriteStream = nodeModuleInput.pipe(_filter(globs)); + + if (includes) { + const includeGlobs = includes.map(toGlob); + output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs))); + } + + output = output.pipe(allFilter.restore); + return es.duplex(input, output); +} + +declare class FileSourceMap extends VinylFile { + public sourceMap: sm.RawSourceMap; +} + +export function loadSourcemaps(): NodeJS.ReadWriteStream { + const input = es.through(); + + const output = input + .pipe(es.map((f, cb): FileSourceMap => { + if (f.sourceMap) { + cb(null, f); + return; + } + + if (!f.contents) { + cb(new Error('empty file')); + return; + } + + const contents = (f.contents).toString('utf8'); + + const reg = /\/\/# sourceMappingURL=(.*)$/g; + let lastMatch = null, match = null; + + while (match = reg.exec(contents)) { + lastMatch = match; + } + + if (!lastMatch) { + f.sourceMap = { + version: 3, + names: [], + mappings: '', + sources: [f.relative.replace(/\//g, '/')], + sourcesContent: [contents] + }; + + cb(null, f); + return; + } + + f.contents = new Buffer(contents.replace(/\/\/# sourceMappingURL=(.*)$/g, ''), 'utf8'); + + fs.readFile(path.join(path.dirname(f.path), lastMatch[1]), 'utf8', (err, contents) => { + if (err) { return cb(err); } + + f.sourceMap = JSON.parse(contents); + cb(null, f); + }); + })); + + return es.duplex(input, output); +} + +export function stripSourceMappingURL(): NodeJS.ReadWriteStream { + const input = es.through(); + + const output = input + .pipe(es.mapSync(f => { + const contents = (f.contents).toString('utf8'); + f.contents = new Buffer(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8'); + return f; + })); + + return es.duplex(input, output); +} + +export function rimraf(dir: string): (cb: any) => void { + let retries = 0; + + const retry = cb => { + _rimraf(dir, { maxBusyTries: 1 }, (err: any) => { + if (!err) { + return cb(); + }; + + if (err.code === 'ENOTEMPTY' && ++retries < 5) { + return setTimeout(() => retry(cb), 10); + } + + return cb(err); + }); + }; + + return cb => retry(cb); +} + +export function getVersion(root: string): string { + let version = process.env['BUILD_SOURCEVERSION']; + + if (!version || !/^[0-9a-f]{40}$/i.test(version)) { + version = git.getVersion(root); + } + + return version; +} + +export function rebase(count: number): NodeJS.ReadWriteStream { + return rename(f => { + const parts = f.dirname.split(/[\/\\]/); + f.dirname = parts.slice(count).join(path.sep); + }); +} + +export interface FilterStream extends NodeJS.ReadWriteStream { + restore: ThroughStream; +} + +export function filter(fn: (data: any) => boolean): FilterStream { + const result = es.through(function (data) { + if (fn(data)) { + this.emit('data', data); + } else { + result.restore.push(data); + } + }); + + result.restore = es.through(); + return result; +} \ No newline at end of file diff --git a/build/lib/watch/index.js b/build/lib/watch/index.js new file mode 100644 index 0000000000..fb7881ca50 --- /dev/null +++ b/build/lib/watch/index.js @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const es = require('event-stream'); + +/** Ugly hack for gulp-tsb */ +function handleDeletions() { + return es.mapSync(f => { + if (/\.ts$/.test(f.relative) && !f.contents) { + f.contents = new Buffer(''); + f.stat = { mtime: new Date() }; + } + + return f; + }); +} + +let watch = void 0; + +if (!process.env['VSCODE_USE_LEGACY_WATCH']) { + try { + watch = require('./watch-nsfw'); + } catch (err) { + console.warn('Could not load our cross platform file watcher: ' + err.toString()); + console.warn('Falling back to our platform specific watcher...'); + } +} + +if (!watch) { + watch = process.platform === 'win32' ? require('./watch-win32') : require('gulp-watch'); +} + +module.exports = function () { + return watch.apply(null, arguments) + .pipe(handleDeletions()); +}; diff --git a/build/lib/watch/package.json b/build/lib/watch/package.json new file mode 100644 index 0000000000..b10e8ed272 --- /dev/null +++ b/build/lib/watch/package.json @@ -0,0 +1,11 @@ +{ + "name": "watch", + "version": "1.0.0", + "description": "", + "author": "Microsoft ", + "private": true, + "devDependencies": { + "gulp-watch": "^4.3.9", + "nsfw": "^1.0.15" + } +} diff --git a/build/lib/watch/watch-nsfw.js b/build/lib/watch/watch-nsfw.js new file mode 100644 index 0000000000..bae517a291 --- /dev/null +++ b/build/lib/watch/watch-nsfw.js @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +var nsfw = require('nsfw'); +var path = require('path'); +var fs = require('fs'); +var File = require('vinyl'); +var es = require('event-stream'); +var filter = require('gulp-filter'); + +function toChangeType(type) { + switch (type) { + case 0: return 'add'; + case 1: return 'unlink'; + case 2: return 'change'; + } +} + +function watch(root) { + var result = es.through(); + + function handleEvent(path, type) { + if (/[/\\].git[/\\]/.test(path) || /[/\\]out[/\\]/.test(path)) { + return; // filter as early as possible + } + + var file = new File({ + path: path, + base: root + }); + + file.event = type; + result.emit('data', file); + } + + nsfw(root, function(events) { + for (var i = 0; i < events.length; i++) { + var e = events[i]; + var changeType = e.action; + + if (changeType === 3 /* RENAMED */) { + handleEvent(path.join(e.directory, e.oldFile), 'unlink'); + handleEvent(path.join(e.directory, e.newFile), 'add'); + } else { + handleEvent(path.join(e.directory, e.file), toChangeType(changeType)); + } + } + }).then(function(watcher) { + watcher.start(); + }); + + return result; +} + +var cache = Object.create(null); + +module.exports = function(pattern, options) { + options = options || {}; + + var cwd = path.normalize(options.cwd || process.cwd()); + var watcher = cache[cwd]; + + if (!watcher) { + watcher = cache[cwd] = watch(cwd); + } + + var rebase = !options.base ? es.through() : es.mapSync(function(f) { + f.base = options.base; + return f; + }); + + return watcher + .pipe(filter(['**', '!.git{,/**}'])) // ignore all things git + .pipe(filter(pattern)) + .pipe(es.map(function(file, cb) { + fs.stat(file.path, function(err, stat) { + if (err && err.code === 'ENOENT') { return cb(null, file); } + if (err) { return cb(); } + if (!stat.isFile()) { return cb(); } + + fs.readFile(file.path, function(err, contents) { + if (err && err.code === 'ENOENT') { return cb(null, file); } + if (err) { return cb(); } + + file.contents = contents; + file.stat = stat; + cb(null, file); + }); + }); + })) + .pipe(rebase); +}; \ No newline at end of file diff --git a/build/lib/watch/watch-win32.js b/build/lib/watch/watch-win32.js new file mode 100644 index 0000000000..2893ff6169 --- /dev/null +++ b/build/lib/watch/watch-win32.js @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +var path = require('path'); +var cp = require('child_process'); +var fs = require('fs'); +var File = require('vinyl'); +var es = require('event-stream'); +var filter = require('gulp-filter'); + +var watcherPath = path.join(__dirname, 'watcher.exe'); + +function toChangeType(type) { + switch (type) { + case '0': return 'change'; + case '1': return 'add'; + default: return 'unlink'; + } +} + +function watch(root) { + var result = es.through(); + var child = cp.spawn(watcherPath, [root]); + + child.stdout.on('data', function(data) { + var lines = data.toString('utf8').split('\n'); + for (var i = 0; i < lines.length; i++) { + var line = lines[i].trim(); + if (line.length === 0) { + continue; + } + + var changeType = line[0]; + var changePath = line.substr(2); + + // filter as early as possible + if (/^\.git/.test(changePath) || /(^|\\)out($|\\)/.test(changePath)) { + continue; + } + + var changePathFull = path.join(root, changePath); + + var file = new File({ + path: changePathFull, + base: root + }); + + file.event = toChangeType(changeType); + result.emit('data', file); + } + }); + + child.stderr.on('data', function(data) { + result.emit('error', data); + }); + + child.on('exit', function(code) { + result.emit('error', 'Watcher died with code ' + code); + child = null; + }); + + process.once('SIGTERM', function () { process.exit(0); }); + process.once('SIGTERM', function () { process.exit(0); }); + process.once('exit', function () { child && child.kill(); }); + + return result; +} + +var cache = Object.create(null); + +module.exports = function(pattern, options) { + options = options || {}; + + var cwd = path.normalize(options.cwd || process.cwd()); + var watcher = cache[cwd]; + + if (!watcher) { + watcher = cache[cwd] = watch(cwd); + } + + var rebase = !options.base ? es.through() : es.mapSync(function (f) { + f.base = options.base; + return f; + }); + + return watcher + .pipe(filter(['**', '!.git{,/**}'])) // ignore all things git + .pipe(filter(pattern)) + .pipe(es.map(function (file, cb) { + fs.stat(file.path, function (err, stat) { + if (err && err.code === 'ENOENT') { return cb(null, file); } + if (err) { return cb(); } + if (!stat.isFile()) { return cb(); } + + fs.readFile(file.path, function (err, contents) { + if (err && err.code === 'ENOENT') { return cb(null, file); } + if (err) { return cb(); } + + file.contents = contents; + file.stat = stat; + cb(null, file); + }); + }); + })) + .pipe(rebase); +}; \ No newline at end of file diff --git a/build/lib/watch/watcher.exe b/build/lib/watch/watcher.exe new file mode 100644 index 0000000000000000000000000000000000000000..d4101748e4db5bf5d79b0310f701af59c36a79df GIT binary patch literal 8192 zcmeHMYiu0Xbw2ahA+?fLA|;BnEK8%MC{wh!B1P(DOEf8RCDEZMzC^{2Tyl2zk{oMx zW;HWQla{5>ZPV09+sJ*Su#p0B(Hb!dqb~(D`f$<)Y0*ajCujjTL4l;vkH$q~vH?3%V~j$D{Fi(WxwHg8Vui(OA-iN+L- z2Hu~%SZVD$l%|bJ577>A8YAxMBdAS0gLoL2sMIKKU}P}hvW5qIE*i~$ifF6+Td!j> zQlWqAC%VA+Akpna%&e{Ei8g@_p|`7+|C4aD8oJbM1bwc?CKHO45cIhg0LWxtE3InRB?1OQi5L>ut#-nHvWsY2k50GX6M9WSGa5@8 ziR+u83|oyW^%_@R6VzKjejQU(SM}>ilFr)6j##{n@m6m;S9`X37*L4r7#ra*zzj-y ztuY5<6LTOtlhcMP~U0;E3aJ;WT7XPAePO?Gko2N6HI)ud|P z9`HIhKs%{klR*L)9l8gx(P4N9-|)=HE>O`ZGT$VYxlUI(z&E_KH%NuzT=@b1<~_<0D8&WFK? zRc>I~=^fpz`)?o@`W*u)G=fgZJJwhd9ALE7LwiZ@5r7+*O||;SwhOG!GNO=)?w-^spL}U9V{eJJvOK z#!5$7ehi>S6AOC!iPH+NAQWEv%l(<2OkYo5?+NB4$N`*39PKxV{topQ7@rP(+g%J; z!$Bu*qi(-AO%n(oLq_dqFOH6&o(DaH+1q~F@p7QKfK)C$^qU(t0HODlKHj&ubtIZF zM+ieIwqo0RY~lH;pyX$EDDtI&T>VbAk)RZr4e`hQT8 z{yV|&`x3qbXwZtzQk%vMgI+Y=fbF+5<}?|sd0Se&E#Z#=4SGXi&u!B7*Pvt2E`_ab zpcR9@2yfiSzh;1b^KUCXV=U-HRq79PrZiY`7Jf=n2KHbT!}4W3Ph)(WNmI7cerOEQ zW~TOTrEQQ6QHr+1(i<1^j0T-<9xN z68;yULFW=bpriCP`cI@+q4xnFqi+NLEd78cOf*kF*Z4Qe9>_i(Q~#)NpRAaAP|qUM zc1-Qlw}SE`s%-*euhH`nMgOAQ0d;aOp}auP8(S5F!Yb9KG}4b_>ZkSH%7!{GNor3= zcCFN|B&j>5CP8hYV=?s#s1yyw)a%rR5ge1$OLSfDRhsL3ZljKN_W4rVY~q-*?Jn5~ zDf=1Rk{*NXY>j41on{MtBGyb|G+U?`QzLX*X`x?@sb8dX3a=A|UeMNICuWLDA*O+b zJTU)vAXVv~kmm?jh;!unSwT$S)eKm$2t_zmjT_t47{f0_PO+Y0SL ztQ-P-T*8k5em~~-DM#qvW4u4{7<4{|z2bGcqh+z@y{1gSe^!5rexEjC-hG3!x7DOfBt8ZBBC^q>d=_hbljLuYTiIi@z(cF@%eeD# z{M%(KyXaNr^Wc9;c~yFQ4bpbB{wn>p{swyb4&rGfll~NNAN@ICC;cVhLHb+39(o(H zYvAvdFhd(@R>{ydnge{0ZUDB^rvV?KUkB`y@G<%g;KP#tF?tjDwB&z6!aThV&J}FL z849RD9h9DjB%GIUO~S8A_`Qg)a13h_enG;oNVpTb$Sf5wqcE*7+s09K>Qf5S8Xgsp zp9F9naFjknpCf&ZXHad~j5#YeyO{Zhl#~kfyl6j6z#s1h4UxskffI9cy>mTu^1Nlc z1G$(O_JtJ+Ci_`7ke{0yv4fIhtqwa@5JXIA*;{J~qSmsPhVr59x%0h*^DKV_C*ZK> zIHJNFb;G{H@GczzSq}k3t)sihzB(y8fUQ?3J7R!_&kc>A&5qDic6{hO>fx#E&{yoT>D3?<#mwjh%1%vPn4)FNDT}!|Dh7GackCQ@mZpSNpqV8MLV<<~1+of-x#1MC0wf*<58Rue?&MU1pj1SD$lHz#K4ugxzm>H`x#p}kd>@(=^kz%!nx6a6&HQ4 z>=r2S%6?wZjCDoOsJrZ;8ur}4a|B)T?NE%_E;!X@Xb}$9TP5~aLqBO+H`T(okISl58@tR2{OyrCcv#hkM`V~4e>Rkm*xMA7oE ztb3WUe1v)iZ>!>2uU1g8IfDRag&$zvtXoxt7=i_Rv5eU7D;^O+-nUEj!l={IVoEsH zio}9@eNXt9LizAs!LV0a_3g!_`-+Ms%UxZ!nJT-XT@>6V+R53D9o9(mQ3hoKC(EKN zqUndc6vR2PVTBVf%q)j-&Tor<+?5yv5ejR5$3p9gI1u{#%?EbhH z+6${_8aa`hfi#wDF2X!Fsx$e}^C^>;RlL=V*w&)!1)-e}s-25wNk%5wD@_Z3+0Ki5 z#Fgb*l}F1G3C3;*b-8J{d8b?u$aBH=v9o%<4N2`d4EcEPSRZ@YWyxC~0ZC$E22|VA zsDZ4@+rX5CrSC3EvTjls{a6(uqTO!TpVbKco5VrN&7& z6{S%#Nl5@{YGN|QK-C+R<~9f#rJ$HFRb^Kj zOO1c@+~wH^`oHs{ym9CXdZnujV2KxY0M%$zjZ{M%l7SlDG^1&I%$n4U=3RJ1lc_bI zuZyh#-E6+tv|WbuB!{0exaZ9mqw2{7DUA=M_+A|`CZ(qAck`syNsV|?jS4P=Tz2zY z(~P>~l5ds9F&8U43ud7AY@k4oBPZITP_kx6Al%kbb}H0z-p>18;4Osau;-V&=rAJO zU8%WxRG5G9x@jKn>BZND0}Aasp6kyau@2|DkLP-icK7!lJ<*Lj#nJ8~3)aH%zWhS& zXaOH1HYwE5i;oHT5sfOeGc%r@sUDRFj=l5MA7!7ynLLLzEVkShU z;P8hPSw83>b1=sLw^yqC{$paN<4?>JsOx*gvz6%hefX@m#-Of5`b+nS;tTya=uyBk(bszVGyT0g(VIu)tMV4|cVvh6 zuT+X0$x!OLh{maj{(kLbpt*vF%Q<|>QZNDnqsf-6U&67*5ijD{3!!r_&%_r16TicV zrI&a$cfm`opD2@jTo>Wj$9r;Rv__ETPrwgf z49fEOuFc#1`VP0$FUPpv?(UJkdz_C;n<4ZfkbTM_V!kH)B)cCD6TPc&t5JFHPriKe z@s*-uE=O-Rr_$Kx)249qUIEvtQ|XH{XS$E4%^<|3)p9&noJy~XApQ8j=H%w&Nh^NM zGU37vPNmDf`)GhGaM237i7Fiz4`lUi&IuO_X&%I5+ZTeghoilgo4%o=RIm^b+l-&9Xg|=Zoj5 z^n&FCBJPxQa_BzMR6;*g7yHRW)j@#5$wQS44OBAzAvHE8{=dOU&C&QF+W#;5KQ;pY E1HnbtZ~y=R literal 0 HcmV?d00001 diff --git a/build/monaco/README-npm.md b/build/monaco/README-npm.md new file mode 100644 index 0000000000..76284f6243 --- /dev/null +++ b/build/monaco/README-npm.md @@ -0,0 +1,14 @@ +# monaco-editor-core + +> This npm module is a building block for the [monaco-editor](https://www.npmjs.com/package/monaco-editor) +npm module and unless you are doing something special (e.g. authoring a monaco editor language that can be shipped +and consumed independently), it is best to consume the [monaco-editor](https://www.npmjs.com/package/monaco-editor) module +that contains this module and adds languages supports. + +The Monaco Editor is the code editor that powers [VS Code](https://github.com/Microsoft/vscode), +a good page describing the code editor's features is [here](https://code.visualstudio.com/docs/editor/editingevolved). + +This npm module contains the core editor functionality, as it comes from the [vscode repository](https://github.com/Microsoft/vscode). + +## License +[Source EULA](https://github.com/Microsoft/sqlopsstudio/blob/master/LICENSE.txt) diff --git a/build/monaco/README.md b/build/monaco/README.md new file mode 100644 index 0000000000..3acbb4eebd --- /dev/null +++ b/build/monaco/README.md @@ -0,0 +1,20 @@ +# Steps to publish a new version of monaco-editor-core + +## Generate monaco.d.ts + +* The `monaco.d.ts` is now automatically generated when running `gulp watch` + +## Bump version + +* increase version in `build/monaco/package.json` + +## Generate npm contents for monaco-editor-core + +* Be sure to have all changes committed **and pushed to the remote** +* (the generated files contain the HEAD sha and that should be available on the remote) +* run gulp editor-distro + +## Publish + +* `cd out-monaco-editor-core` +* `npm publish` diff --git a/build/monaco/ThirdPartyNotices.txt b/build/monaco/ThirdPartyNotices.txt new file mode 100644 index 0000000000..197e1a8b66 --- /dev/null +++ b/build/monaco/ThirdPartyNotices.txt @@ -0,0 +1,85 @@ +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +This project incorporates components from the projects listed below. The original copyright notices and the licenses +under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted +herein, whether by implication, estoppel or otherwise. + + + + +%% winjs version 4.4.0 (https://github.com/winjs/winjs) +========================================= +WinJS + +Copyright (c) Microsoft Corporation + +All rights reserved. + +Source EULA + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +========================================= +END OF winjs NOTICES AND INFORMATION + + + + +%% string_scorer version 0.1.20 (https://github.com/joshaven/string_score) +========================================= +This software is released under the Source EULA: + +Copyright (c) Joshaven Potter + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF string_scorer NOTICES AND INFORMATION + + + + +%% chjj-marked NOTICES AND INFORMATION BEGIN HERE +========================================= +The Source EULA License + +Copyright (c) 2011-2014, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF chjj-marked NOTICES AND INFORMATION diff --git a/build/monaco/api.js b/build/monaco/api.js new file mode 100644 index 0000000000..b50f4cc027 --- /dev/null +++ b/build/monaco/api.js @@ -0,0 +1,327 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +var fs = require("fs"); +var ts = require("typescript"); +var path = require("path"); +var tsfmt = require('../../tsfmt.json'); +var util = require('gulp-util'); +function log(message) { + var rest = []; + for (var _i = 1; _i < arguments.length; _i++) { + rest[_i - 1] = arguments[_i]; + } + util.log.apply(util, [util.colors.cyan('[monaco.d.ts]'), message].concat(rest)); +} +var SRC = path.join(__dirname, '../../src'); +var OUT_ROOT = path.join(__dirname, '../../'); +var RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); +var DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); +var CURRENT_PROCESSING_RULE = ''; +function logErr(message) { + var rest = []; + for (var _i = 1; _i < arguments.length; _i++) { + rest[_i - 1] = arguments[_i]; + } + util.log(util.colors.red('[monaco.d.ts]'), 'WHILE HANDLING RULE: ', CURRENT_PROCESSING_RULE); + util.log.apply(util, [util.colors.red('[monaco.d.ts]'), message].concat(rest)); +} +function moduleIdToPath(out, moduleId) { + if (/\.d\.ts/.test(moduleId)) { + return path.join(SRC, moduleId); + } + return path.join(OUT_ROOT, out, moduleId) + '.d.ts'; +} +var SOURCE_FILE_MAP = {}; +function getSourceFile(out, inputFiles, moduleId) { + if (!SOURCE_FILE_MAP[moduleId]) { + var filePath = path.normalize(moduleIdToPath(out, moduleId)); + if (!inputFiles.hasOwnProperty(filePath)) { + logErr('CANNOT FIND FILE ' + filePath + '. YOU MIGHT NEED TO RESTART gulp'); + return null; + } + var fileContents = inputFiles[filePath]; + var sourceFile = ts.createSourceFile(filePath, fileContents, ts.ScriptTarget.ES5); + SOURCE_FILE_MAP[moduleId] = sourceFile; + } + return SOURCE_FILE_MAP[moduleId]; +} +function isDeclaration(a) { + return (a.kind === ts.SyntaxKind.InterfaceDeclaration + || a.kind === ts.SyntaxKind.EnumDeclaration + || a.kind === ts.SyntaxKind.ClassDeclaration + || a.kind === ts.SyntaxKind.TypeAliasDeclaration + || a.kind === ts.SyntaxKind.FunctionDeclaration + || a.kind === ts.SyntaxKind.ModuleDeclaration); +} +function visitTopLevelDeclarations(sourceFile, visitor) { + var stop = false; + var visit = function (node) { + if (stop) { + return; + } + switch (node.kind) { + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.EnumDeclaration: + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.VariableStatement: + case ts.SyntaxKind.TypeAliasDeclaration: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.ModuleDeclaration: + stop = visitor(node); + } + // if (node.kind !== ts.SyntaxKind.SourceFile) { + // if (getNodeText(sourceFile, node).indexOf('SymbolKind') >= 0) { + // console.log('FOUND TEXT IN NODE: ' + ts.SyntaxKind[node.kind]); + // console.log(getNodeText(sourceFile, node)); + // } + // } + if (stop) { + return; + } + ts.forEachChild(node, visit); + }; + visit(sourceFile); +} +function getAllTopLevelDeclarations(sourceFile) { + var all = []; + visitTopLevelDeclarations(sourceFile, function (node) { + if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) { + var interfaceDeclaration = node; + var triviaStart = interfaceDeclaration.pos; + var triviaEnd = interfaceDeclaration.name.pos; + var triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd }); + // // let nodeText = getNodeText(sourceFile, node); + // if (getNodeText(sourceFile, node).indexOf('SymbolKind') >= 0) { + // console.log('TRIVIA: ', triviaText); + // } + if (triviaText.indexOf('@internal') === -1) { + all.push(node); + } + } + else { + var nodeText = getNodeText(sourceFile, node); + if (nodeText.indexOf('@internal') === -1) { + all.push(node); + } + } + return false /*continue*/; + }); + return all; +} +function getTopLevelDeclaration(sourceFile, typeName) { + var result = null; + visitTopLevelDeclarations(sourceFile, function (node) { + if (isDeclaration(node)) { + if (node.name.text === typeName) { + result = node; + return true /*stop*/; + } + return false /*continue*/; + } + // node is ts.VariableStatement + if (getNodeText(sourceFile, node).indexOf(typeName) >= 0) { + result = node; + return true /*stop*/; + } + return false /*continue*/; + }); + return result; +} +function getNodeText(sourceFile, node) { + return sourceFile.getFullText().substring(node.pos, node.end); +} +function getMassagedTopLevelDeclarationText(sourceFile, declaration) { + var result = getNodeText(sourceFile, declaration); + // if (result.indexOf('MonacoWorker') >= 0) { + // console.log('here!'); + // // console.log(ts.SyntaxKind[declaration.kind]); + // } + if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { + var interfaceDeclaration = declaration; + var members = interfaceDeclaration.members; + members.forEach(function (member) { + try { + var memberText = getNodeText(sourceFile, member); + if (memberText.indexOf('@internal') >= 0 || memberText.indexOf('private') >= 0) { + // console.log('BEFORE: ', result); + result = result.replace(memberText, ''); + // console.log('AFTER: ', result); + } + } + catch (err) { + // life.. + } + }); + } + result = result.replace(/export default/g, 'export'); + result = result.replace(/export declare/g, 'export'); + return result; +} +function format(text) { + // Parse the source text + var sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); + // Get the formatting edits on the input sources + var edits = ts.formatting.formatDocument(sourceFile, getRuleProvider(tsfmt), tsfmt); + // Apply the edits on the input code + return applyEdits(text, edits); + function getRuleProvider(options) { + // Share this between multiple formatters using the same options. + // This represents the bulk of the space the formatter uses. + var ruleProvider = new ts.formatting.RulesProvider(); + ruleProvider.ensureUpToDate(options); + return ruleProvider; + } + function applyEdits(text, edits) { + // Apply edits in reverse on the existing text + var result = text; + for (var i = edits.length - 1; i >= 0; i--) { + var change = edits[i]; + var head = result.slice(0, change.span.start); + var tail = result.slice(change.span.start + change.span.length); + result = head + change.newText + tail; + } + return result; + } +} +function createReplacer(data) { + data = data || ''; + var rawDirectives = data.split(';'); + var directives = []; + rawDirectives.forEach(function (rawDirective) { + if (rawDirective.length === 0) { + return; + } + var pieces = rawDirective.split('=>'); + var findStr = pieces[0]; + var replaceStr = pieces[1]; + findStr = findStr.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&'); + findStr = '\\b' + findStr + '\\b'; + directives.push([new RegExp(findStr, 'g'), replaceStr]); + }); + return function (str) { + for (var i = 0; i < directives.length; i++) { + str = str.replace(directives[i][0], directives[i][1]); + } + return str; + }; +} +function generateDeclarationFile(out, inputFiles, recipe) { + var lines = recipe.split(/\r\n|\n|\r/); + var result = []; + lines.forEach(function (line) { + var m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + if (m1) { + CURRENT_PROCESSING_RULE = line; + var moduleId = m1[1]; + var sourceFile_1 = getSourceFile(out, inputFiles, moduleId); + if (!sourceFile_1) { + return; + } + var replacer_1 = createReplacer(m1[2]); + var typeNames = m1[3].split(/,/); + typeNames.forEach(function (typeName) { + typeName = typeName.trim(); + if (typeName.length === 0) { + return; + } + var declaration = getTopLevelDeclaration(sourceFile_1, typeName); + if (!declaration) { + logErr('Cannot find type ' + typeName); + return; + } + result.push(replacer_1(getMassagedTopLevelDeclarationText(sourceFile_1, declaration))); + }); + return; + } + var m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + if (m2) { + CURRENT_PROCESSING_RULE = line; + var moduleId = m2[1]; + var sourceFile_2 = getSourceFile(out, inputFiles, moduleId); + if (!sourceFile_2) { + return; + } + var replacer_2 = createReplacer(m2[2]); + var typeNames = m2[3].split(/,/); + var typesToExcludeMap_1 = {}; + var typesToExcludeArr_1 = []; + typeNames.forEach(function (typeName) { + typeName = typeName.trim(); + if (typeName.length === 0) { + return; + } + typesToExcludeMap_1[typeName] = true; + typesToExcludeArr_1.push(typeName); + }); + getAllTopLevelDeclarations(sourceFile_2).forEach(function (declaration) { + if (isDeclaration(declaration)) { + if (typesToExcludeMap_1[declaration.name.text]) { + return; + } + } + else { + // node is ts.VariableStatement + var nodeText = getNodeText(sourceFile_2, declaration); + for (var i = 0; i < typesToExcludeArr_1.length; i++) { + if (nodeText.indexOf(typesToExcludeArr_1[i]) >= 0) { + return; + } + } + } + result.push(replacer_2(getMassagedTopLevelDeclarationText(sourceFile_2, declaration))); + }); + return; + } + result.push(line); + }); + var resultTxt = result.join('\n'); + resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri'); + resultTxt = resultTxt.replace(/\bEventboolean): void { + let stop = false; + + let visit = (node: ts.Node): void => { + if (stop) { + return; + } + + switch (node.kind) { + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.EnumDeclaration: + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.VariableStatement: + case ts.SyntaxKind.TypeAliasDeclaration: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.ModuleDeclaration: + stop = visitor(node); + } + + // if (node.kind !== ts.SyntaxKind.SourceFile) { + // if (getNodeText(sourceFile, node).indexOf('SymbolKind') >= 0) { + // console.log('FOUND TEXT IN NODE: ' + ts.SyntaxKind[node.kind]); + // console.log(getNodeText(sourceFile, node)); + // } + // } + + if (stop) { + return; + } + ts.forEachChild(node, visit); + }; + + visit(sourceFile); +} + + +function getAllTopLevelDeclarations(sourceFile:ts.SourceFile): TSTopLevelDeclare[] { + let all:TSTopLevelDeclare[] = []; + visitTopLevelDeclarations(sourceFile, (node) => { + if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) { + let interfaceDeclaration = node; + let triviaStart = interfaceDeclaration.pos; + let triviaEnd = interfaceDeclaration.name.pos; + let triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd }); + + // // let nodeText = getNodeText(sourceFile, node); + // if (getNodeText(sourceFile, node).indexOf('SymbolKind') >= 0) { + // console.log('TRIVIA: ', triviaText); + // } + if (triviaText.indexOf('@internal') === -1) { + all.push(node); + } + } else { + let nodeText = getNodeText(sourceFile, node); + if (nodeText.indexOf('@internal') === -1) { + all.push(node); + } + } + return false /*continue*/; + }); + return all; +} + + +function getTopLevelDeclaration(sourceFile:ts.SourceFile, typeName:string): TSTopLevelDeclare { + let result:TSTopLevelDeclare = null; + visitTopLevelDeclarations(sourceFile, (node) => { + if (isDeclaration(node)) { + if (node.name.text === typeName) { + result = node; + return true /*stop*/; + } + return false /*continue*/; + } + // node is ts.VariableStatement + if (getNodeText(sourceFile, node).indexOf(typeName) >= 0) { + result = node; + return true /*stop*/; + } + return false /*continue*/; + }); + return result; +} + + +function getNodeText(sourceFile:ts.SourceFile, node:{pos:number; end:number;}): string { + return sourceFile.getFullText().substring(node.pos, node.end); +} + + +function getMassagedTopLevelDeclarationText(sourceFile:ts.SourceFile, declaration: TSTopLevelDeclare): string { + let result = getNodeText(sourceFile, declaration); + // if (result.indexOf('MonacoWorker') >= 0) { + // console.log('here!'); + // // console.log(ts.SyntaxKind[declaration.kind]); + // } + if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { + let interfaceDeclaration = declaration; + + let members:ts.NodeArray = interfaceDeclaration.members; + members.forEach((member) => { + try { + let memberText = getNodeText(sourceFile, member); + if (memberText.indexOf('@internal') >= 0 || memberText.indexOf('private') >= 0) { + // console.log('BEFORE: ', result); + result = result.replace(memberText, ''); + // console.log('AFTER: ', result); + } + } catch (err) { + // life.. + } + }); + } + result = result.replace(/export default/g, 'export'); + result = result.replace(/export declare/g, 'export'); + return result; +} + +function format(text:string): string { + + // Parse the source text + let sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); + + // Get the formatting edits on the input sources + let edits = (ts).formatting.formatDocument(sourceFile, getRuleProvider(tsfmt), tsfmt); + + // Apply the edits on the input code + return applyEdits(text, edits); + + function getRuleProvider(options: ts.FormatCodeSettings) { + // Share this between multiple formatters using the same options. + // This represents the bulk of the space the formatter uses. + let ruleProvider = new (ts).formatting.RulesProvider(); + ruleProvider.ensureUpToDate(options); + return ruleProvider; + } + + function applyEdits(text: string, edits: ts.TextChange[]): string { + // Apply edits in reverse on the existing text + let result = text; + for (let i = edits.length - 1; i >= 0; i--) { + let change = edits[i]; + let head = result.slice(0, change.span.start); + let tail = result.slice(change.span.start + change.span.length); + result = head + change.newText + tail; + } + return result; + } +} + +function createReplacer(data:string): (str:string)=>string { + data = data || ''; + let rawDirectives = data.split(';'); + let directives: [RegExp,string][] = []; + rawDirectives.forEach((rawDirective) => { + if (rawDirective.length === 0) { + return; + } + let pieces = rawDirective.split('=>'); + let findStr = pieces[0]; + let replaceStr = pieces[1]; + + findStr = findStr.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&'); + findStr = '\\b' + findStr + '\\b'; + directives.push([new RegExp(findStr, 'g'), replaceStr]); + }); + + return (str:string)=> { + for (let i = 0; i < directives.length; i++) { + str = str.replace(directives[i][0], directives[i][1]); + } + return str; + }; +} + +function generateDeclarationFile(out: string, inputFiles: { [file: string]: string; }, recipe:string): string { + let lines = recipe.split(/\r\n|\n|\r/); + let result = []; + + + lines.forEach(line => { + + let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + if (m1) { + CURRENT_PROCESSING_RULE = line; + let moduleId = m1[1]; + let sourceFile = getSourceFile(out, inputFiles, moduleId); + if (!sourceFile) { + return; + } + + let replacer = createReplacer(m1[2]); + + let typeNames = m1[3].split(/,/); + typeNames.forEach((typeName) => { + typeName = typeName.trim(); + if (typeName.length === 0) { + return; + } + let declaration = getTopLevelDeclaration(sourceFile, typeName); + if (!declaration) { + logErr('Cannot find type ' + typeName); + return; + } + result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration))); + }); + return; + } + + let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + if (m2) { + CURRENT_PROCESSING_RULE = line; + let moduleId = m2[1]; + let sourceFile = getSourceFile(out, inputFiles, moduleId); + if (!sourceFile) { + return; + } + + let replacer = createReplacer(m2[2]); + + let typeNames = m2[3].split(/,/); + let typesToExcludeMap: {[typeName:string]:boolean;} = {}; + let typesToExcludeArr: string[] = []; + typeNames.forEach((typeName) => { + typeName = typeName.trim(); + if (typeName.length === 0) { + return; + } + typesToExcludeMap[typeName] = true; + typesToExcludeArr.push(typeName); + }); + + getAllTopLevelDeclarations(sourceFile).forEach((declaration) => { + if (isDeclaration(declaration)) { + if (typesToExcludeMap[declaration.name.text]) { + return; + } + } else { + // node is ts.VariableStatement + let nodeText = getNodeText(sourceFile, declaration); + for (let i = 0; i < typesToExcludeArr.length; i++) { + if (nodeText.indexOf(typesToExcludeArr[i]) >= 0) { + return; + } + } + } + result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration))); + }); + return; + } + + result.push(line); + }); + + let resultTxt = result.join('\n'); + resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri'); + resultTxt = resultTxt.replace(/\bEvent { + + let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + if (m1) { + let moduleId = m1[1]; + result.push(moduleIdToPath(out, moduleId)); + return; + } + + let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + if (m2) { + let moduleId = m2[1]; + result.push(moduleIdToPath(out, moduleId)); + return; + } + }); + + return result; +} + +export interface IMonacoDeclarationResult { + content: string; + filePath: string; + isTheSame: boolean; +} + +export function run(out: string, inputFiles: { [file: string]: string; }): IMonacoDeclarationResult { + log('Starting monaco.d.ts generation'); + SOURCE_FILE_MAP = {}; + + let recipe = fs.readFileSync(RECIPE_PATH).toString(); + let result = generateDeclarationFile(out, inputFiles, recipe); + + let currentContent = fs.readFileSync(DECLARATION_PATH).toString(); + log('Finished monaco.d.ts generation'); + + return { + content: result, + filePath: DECLARATION_PATH, + isTheSame: currentContent === result + }; +} + +export function complainErrors() { + logErr('Not running monaco.d.ts generation due to compile errors'); +} diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe new file mode 100644 index 0000000000..4bafef0a46 --- /dev/null +++ b/build/monaco/monaco.d.ts.recipe @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module monaco { + + type Thenable = PromiseLike; + + export interface IDisposable { + dispose(): void; + } + + export interface IEvent { + (listener: (e: T) => any, thisArg?: any): IDisposable; + } + + /** + * A helper that allows to emit and listen to typed events + */ + export class Emitter { + constructor(); + readonly event: Event; + fire(event?: T): void; + dispose(): void; + } + + export enum Severity { + Ignore = 0, + Info = 1, + Warning = 2, + Error = 3, + } + +#include(vs/base/common/winjs.base.d.ts): TValueCallback, ProgressCallback, Promise +#include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken +#include(vs/base/common/uri): URI +#include(vs/editor/common/standalone/standaloneBase): KeyCode, KeyMod +#include(vs/base/common/htmlContent): IMarkdownString +#include(vs/base/browser/keyboardEvent): IKeyboardEvent +#include(vs/base/browser/mouseEvent): IMouseEvent +#include(vs/editor/common/editorCommon): IScrollEvent +#include(vs/editor/common/core/position): IPosition, Position +#include(vs/editor/common/core/range): IRange, Range +#include(vs/editor/common/core/selection): ISelection, Selection, SelectionDirection +#include(vs/editor/common/core/token): Token +} + +declare module monaco.editor { + +#includeAll(vs/editor/standalone/browser/standaloneEditor;modes.=>languages.;editorCommon.=>): +#include(vs/editor/standalone/common/standaloneThemeService): BuiltinTheme, IStandaloneThemeData, IColors +#include(vs/editor/common/modes/supports/tokenization): ITokenThemeRule +#include(vs/editor/common/services/webWorker): MonacoWebWorker, IWebWorkerOptions +#include(vs/editor/standalone/browser/standaloneCodeEditor): IEditorConstructionOptions, IDiffEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor +export interface ICommandHandler { + (...args:any[]): void; +} +#include(vs/platform/contextkey/common/contextkey): IContextKey +#include(vs/editor/standalone/browser/standaloneServices): IEditorOverrideServices +#include(vs/platform/markers/common/markers): IMarker, IMarkerData +#include(vs/editor/standalone/browser/colorizer): IColorizerOptions, IColorizerElementOptions +#include(vs/base/common/scrollable): ScrollbarVisibility +#include(vs/platform/theme/common/themeService): ThemeColor +#includeAll(vs/editor/common/editorCommon;IMode=>languages.IMode;LanguageIdentifier=>languages.LanguageIdentifier;editorOptions.=>): ISelection, IScrollEvent +#includeAll(vs/editor/common/model/textModelEvents): +#includeAll(vs/editor/common/controller/cursorEvents): +#includeAll(vs/editor/common/config/editorOptions): +#includeAll(vs/editor/browser/editorBrowser;editorCommon.=>;editorOptions.=>): +#include(vs/editor/common/config/fontInfo): FontInfo, BareFontInfo +} + +declare module monaco.languages { + +#includeAll(vs/editor/standalone/browser/standaloneLanguages;modes.=>;editorCommon.=>editor.;IMarkerData=>editor.IMarkerData): +#includeAll(vs/editor/common/modes/languageConfiguration): +#includeAll(vs/editor/common/modes;editorCommon.IRange=>IRange;editorCommon.IPosition=>IPosition;editorCommon.=>editor.): +#include(vs/editor/common/services/modeService): ILanguageExtensionPoint +#includeAll(vs/editor/standalone/common/monarch/monarchTypes): + +} + +declare module monaco.worker { + +#includeAll(vs/editor/common/services/editorSimpleWorker;): + +} diff --git a/build/monaco/package.json b/build/monaco/package.json new file mode 100644 index 0000000000..c31756516c --- /dev/null +++ b/build/monaco/package.json @@ -0,0 +1,57 @@ +{ + "name": "monaco-editor-core", + "private": true, + "version": "0.9.0", + "description": "A browser based code editor", + "author": "Microsoft Corporation", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/vscode" + }, + "bugs": { + "url": "https://github.com/Microsoft/vscode/issues" + }, + "devDependencies": { + "@types/minimist": "^1.2.0", + "@types/mocha": "^2.2.39", + "@types/semver": "^5.3.30", + "@types/sinon": "^1.16.34", + "debounce": "^1.0.0", + "eslint": "^3.4.0", + "event-stream": "^3.1.7", + "ghooks": "1.0.3", + "glob": "^5.0.13", + "gulp": "^3.8.9", + "gulp-bom": "^1.0.0", + "gulp-concat": "^2.6.0", + "gulp-cssnano": "^2.1.0", + "gulp-filter": "^3.0.0", + "gulp-flatmap": "^1.0.0", + "gulp-rename": "^1.2.0", + "gulp-sourcemaps": "^1.11.0", + "gulp-tsb": "^2.0.3", + "gulp-tslint": "^7.0.1", + "gulp-uglify": "^2.0.0", + "gulp-util": "^3.0.6", + "gulp-watch": "^4.3.9", + "is": "^3.1.0", + "istanbul": "^0.3.17", + "jsdom-no-contextify": "^3.1.0", + "lazy.js": "^0.4.2", + "minimatch": "^2.0.10", + "mocha": "^2.2.5", + "object-assign": "^4.0.1", + "pump": "^1.0.1", + "remap-istanbul": "^0.6.4", + "rimraf": "^2.2.8", + "sinon": "^1.17.2", + "source-map": "^0.4.4", + "tslint": "^4.3.1", + "typescript": "2.4.1", + "typescript-formatter": "4.0.1", + "underscore": "^1.8.2", + "vinyl": "^0.4.5", + "vscode-nls-dev": "^2.0.1" + } +} diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js new file mode 100644 index 0000000000..98e2340bd1 --- /dev/null +++ b/build/npm/postinstall.js @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const cp = require('child_process'); +const path = require('path'); +const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + +function npmInstall(location, opts) { + opts = opts || {}; + opts.cwd = location; + opts.stdio = 'inherit'; + + const result = cp.spawnSync(npm, ['install'], opts); + + if (result.error || result.status !== 0) { + process.exit(1); + } +} + +const protocol = [ + 'jsonrpc', + 'types', + 'client' +]; + +protocol.forEach(item => npmInstall(`dataprotocol-node/${item}`)); + +// {{SQL CARBON EDIT}} +npmInstall('extensions-modules'); +npmInstall('extensions'); // node modules shared by all extensions + +const extensions = [ + 'vscode-colorize-tests', + 'git', + 'json', + 'mssql', + 'configuration-editing', + 'extension-editing', + 'markdown', + 'merge-conflict', + 'account-provider-azure', + 'insights-default' +]; + +extensions.forEach(extension => npmInstall(`extensions/${extension}`)); + +function npmInstallBuildDependencies() { + // make sure we install gulp watch for the system installed + // node, since that is the driver of gulp + const env = Object.assign({}, process.env); + + delete env['npm_config_disturl']; + delete env['npm_config_target']; + delete env['npm_config_runtime']; + + npmInstall(path.join(path.dirname(__dirname), 'lib', 'watch'), { env }); +} + +npmInstall(`build`); // node modules required for build +npmInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron \ No newline at end of file diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js new file mode 100644 index 0000000000..da585a1cd6 --- /dev/null +++ b/build/npm/preinstall.js @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +if (process.env['npm_config_disturl'] !== 'https://atom.io/download/electron') { + console.error("You can't use plain npm to install Code's dependencies."); + console.error( + /^win/.test(process.platform) + ? "Please run '.\\scripts\\npm.bat install' instead." + : "Please run './scripts/npm.sh install' instead." + ); + + process.exit(1); +} \ No newline at end of file diff --git a/build/npm/update-all-grammars.js b/build/npm/update-all-grammars.js new file mode 100644 index 0000000000..bf88b22f55 --- /dev/null +++ b/build/npm/update-all-grammars.js @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const cp = require('child_process'); +const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + +function updateGrammar(location) { + const result = cp.spawnSync(npm, ['run', 'update-grammar'], { + cwd: location, + stdio: 'inherit' + }); + + if (result.error || result.status !== 0) { + process.exit(1); + } +} + +const extensions = [ + // 'bat' Grammar no longer available + 'clojure', + 'coffeescript', + 'cpp', + 'csharp', + 'css', + 'diff', + 'docker', + 'fsharp', + 'gitsyntax', + 'go', + 'groovy', + 'handlebars', + 'hlsl', + 'html', + 'ini', + 'java', + // 'javascript', updated through JavaScript + 'json', + 'less', + 'lua', + 'make', + 'markdown', + 'objective-c', + 'perl', + 'php', + // 'powershell', grammar not ready yet, @daviwil will ping when ready + 'pug', + 'python', + 'r', + 'razor', + 'ruby', + 'rust', + 'scss', + 'shaderlab', + 'shellscript', + // 'sql', customized, PRs pending + 'swift', + 'typescript', + 'vb', + 'xml', + 'yaml' +]; + +extensions.forEach(extension => updateGrammar(`extensions/${extension}`)); + +// run integration tests + +if (process.platform === 'win32') { + cp.spawn('.\scripts\test-integration.bat', [], { env: process.env, stdio: 'inherit' }); +} else { + cp.spawn('/bin/bash', ['./scripts/test-integration.sh'], { env: process.env, stdio: 'inherit' }); +} \ No newline at end of file diff --git a/build/npm/update-grammar.js b/build/npm/update-grammar.js new file mode 100644 index 0000000000..6610e610c2 --- /dev/null +++ b/build/npm/update-grammar.js @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +var path = require('path'); +var fs = require('fs'); +var plist = require('fast-plist'); +var cson = require('cson-parser'); +var https = require('https'); +var url = require('url'); + +function getOptions(urlString) { + var _url = url.parse(urlString); + return { + protocol: _url.protocol, + host: _url.host, + port: _url.port, + path: _url.path, + headers: { + 'User-Agent': 'NodeJS' + } + } +} + +function download(url, redirectCount) { + return new Promise((c, e) => { + var content = ''; + https.get(getOptions(url), function (response) { + response.on('data', function (data) { + content += data.toString(); + }).on('end', function () { + let count = redirectCount || 0; + if (count < 5 && response.statusCode >= 300 && response.statusCode <= 303 || response.statusCode === 307) { + let location = response.headers['location']; + if (location) { + console.log("Redirected " + url + " to " + location); + download(location, count+1).then(c, e); + return; + } + } + c(content); + }); + }).on('error', function (err) { + e(err.message); + }); + }); +} + +function getCommitSha(repoId, repoPath) { + var commitInfo = 'https://api.github.com/repos/' + repoId + '/commits?path=' + repoPath; + return download(commitInfo).then(function (content) { + try { + let lastCommit = JSON.parse(content)[0]; + return Promise.resolve({ + commitSha: lastCommit.sha, + commitDate: lastCommit.commit.author.date + }); + } catch (e) { + console.error("Failed extracting the SHA: " + content); + return Promise.resolve(null); + } + }, function () { + console.error('Failed loading ' + commitInfo); + return Promise.resolve(null); + }); +} + +exports.update = function (repoId, repoPath, dest, modifyGrammar) { + var contentPath = 'https://raw.githubusercontent.com/' + repoId + '/master/' + repoPath; + console.log('Reading from ' + contentPath); + return download(contentPath).then(function (content) { + var ext = path.extname(repoPath); + var grammar; + if (ext === '.tmLanguage' || ext === '.plist') { + grammar = plist.parse(content); + } else if (ext === '.cson') { + grammar = cson.parse(content); + } else if (ext === '.json') { + grammar = JSON.parse(content); + } else { + console.error('Unknown file extension: ' + ext); + return; + } + if (modifyGrammar) { + modifyGrammar(grammar); + } + return getCommitSha(repoId, repoPath).then(function (info) { + let result = { + information_for_contributors: [ + 'This file has been converted from https://github.com/' + repoId + '/blob/master/' + repoPath, + 'If you want to provide a fix or improvement, please create a pull request against the original repository.', + 'Once accepted there, we are happy to receive an update request.' + ] + }; + + if (info) { + result.version = 'https://github.com/' + repoId + '/commit/' + info.commitSha; + } + for (let key in grammar) { + result[key] = grammar[key]; + } + + try { + fs.writeFileSync(dest, JSON.stringify(result, null, '\t')); + if (info) { + console.log('Updated ' + path.basename(dest) + ' to ' + repoId + '@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); + } else { + console.log('Updated ' + path.basename(dest)); + } + } catch (e) { + console.error(e); + } + }); + + }, console.error); +} + +if (path.basename(process.argv[1]) === 'update-grammar.js') { + for (var i = 3; i < process.argv.length; i += 2) { + exports.update(process.argv[2], process.argv[i], process.argv[i + 1]); + } +} diff --git a/build/npm/update-theme.js b/build/npm/update-theme.js new file mode 100644 index 0000000000..cf267af407 --- /dev/null +++ b/build/npm/update-theme.js @@ -0,0 +1,82 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +var path = require('path'); +var fs = require('fs'); +var plist = require('fast-plist'); + +var mappings = { + "background": ["editor.background"], + "foreground": ["editor.foreground"], + "hoverHighlight": ["editor.hoverHighlightBackground"], + "linkForeground": ["editorLink.foreground"], + "selection": ["editor.selectionBackground"], + "inactiveSelection": ["editor.inactiveSelectionBackground"], + "selectionHighlightColor": ["editor.selectionHighlightBackground"], + "wordHighlight": ["editor.wordHighlightBackground"], + "wordHighlightStrong": ["editor.wordHighlightStrongBackground"], + "findMatchHighlight": ["editor.findMatchHighlightBackground", "peekViewResult.matchHighlightBackground"], + "currentFindMatchHighlight": ["editor.findMatchBackground"], + "findRangeHighlight": ["editor.findRangeHighlightBackground"], + "referenceHighlight": ["peekViewEditor.matchHighlightBackground"], + "lineHighlight": ["editor.lineHighlightBackground"], + "rangeHighlight": ["editor.rangeHighlightBackground"], + "caret": ["editorCursor.foreground"], + "invisibles": ["editorWhitespace.foreground"], + "guide": ["editorIndentGuide.background"], + "ansiBlack": ["terminal.ansiBlack"], "ansiRed": ["terminal.ansiRed"], "ansiGreen": ["terminal.ansiGreen"], "ansiYellow": ["terminal.ansiYellow"], + "ansiBlue": ["terminal.ansiBlue"], "ansiMagenta": ["terminal.ansiMagenta"], "ansiCyan": ["terminal.ansiCyan"], "ansiWhite": ["terminal.ansiWhite"], + "ansiBrightBlack": ["terminal.ansiBrightBlack"], "ansiBrightRed": ["terminal.ansiBrightRed"], "ansiBrightGreen": ["terminal.ansiBrightGreen"], + "ansiBrightYellow": ["terminal.ansiBrightYellow"], "ansiBrightBlue": ["terminal.ansiBrightBlue"], "ansiBrightMagenta": ["terminal.ansiBrightMagenta"], + "ansiBrightCyan": ["terminal.ansiBrightCyan"], "ansiBrightWhite": ["terminal.ansiBrightWhite"] +}; + +exports.update = function (srcName, destName) { + try { + console.log('reading ', srcName); + let result = {}; + let plistContent = fs.readFileSync(srcName).toString(); + let theme = plist.parse(plistContent); + let settings = theme.settings; + if (Array.isArray(settings)) { + let colorMap = {}; + for (let entry of settings) { + let scope = entry.scope; + if (scope) { + let parts = scope.split(',').map(p => p.trim()); + if (parts.length > 1) { + entry.scope = parts; + } + } else { + var entrySettings = entry.settings; + for (let entry in entrySettings) { + let mapping = mappings[entry]; + if (mapping) { + for (let newKey of mapping) { + colorMap[newKey] = entrySettings[entry]; + } + if (entry !== 'foreground' && entry !== 'background') { + delete entrySettings[entry]; + } + } + } + + } + } + result.name = theme.name; + result.tokenColors = settings; + result.colors = colorMap; + } + fs.writeFileSync(destName, JSON.stringify(result, null, '\t')); + } catch (e) { + console.log(e); + } +}; + +if (path.basename(process.argv[1]) === 'update-theme.js') { + exports.update(process.argv[2], process.argv[3]); +} diff --git a/build/package.json b/build/package.json new file mode 100644 index 0000000000..ffb1db30e6 --- /dev/null +++ b/build/package.json @@ -0,0 +1,28 @@ +{ + "name": "code-oss-dev-build", + "version": "1.0.0", + "devDependencies": { + "@types/azure": "^0.9.18", + "@types/documentdb": "^1.10.1", + "@types/es6-collections": "^0.5.30", + "@types/es6-promise": "0.0.32", + "@types/mime": "0.0.29", + "@types/node": "^7.0.13", + "@types/xml2js": "^0.0.33", + "azure-storage": "^2.1.0", + "decompress": "^4.2.0", + "documentdb": "^1.11.0", + "extensions-modules": "file:../extensions-modules", + "fs-extra-promise": "^1.0.1", + "mime": "^1.3.4", + "minimist": "^1.2.0", + "typescript": "2.4.1", + "vscode": "^1.0.1", + "xml2js": "^0.4.17" + }, + "scripts": { + "compile": "tsc", + "watch": "tsc --watch", + "postinstall": "npm run compile" + } +} diff --git a/build/tfs/common/.gitignore b/build/tfs/common/.gitignore new file mode 100644 index 0000000000..e94ecda764 --- /dev/null +++ b/build/tfs/common/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +*.js \ No newline at end of file diff --git a/build/tfs/common/common.sh b/build/tfs/common/common.sh new file mode 100644 index 0000000000..52f5353794 --- /dev/null +++ b/build/tfs/common/common.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +# set agent specific npm cache +if [ -n "$AGENT_WORKFOLDER" ] +then + export npm_config_cache="$AGENT_WORKFOLDER/npm-cache" + echo "Using npm cache: $npm_config_cache" +fi + +SUMMARY="Task;Duration"$'\n' +step() { + START=$SECONDS + TASK=$1; shift + echo "" + echo "*****************************************************************************" + echo "Start: $TASK" + echo "*****************************************************************************" + "$@" + + # Calculate total duration + TOTAL=$(echo "$SECONDS - $START" | bc) + M=$(echo "$TOTAL / 60" | bc) + S=$(echo "$TOTAL % 60" | bc) + DURATION="$(printf "%02d" $M):$(printf "%02d" $S)" + + echo "*****************************************************************************" + echo "End: $TASK, Total: $DURATION" + echo "*****************************************************************************" + SUMMARY="$SUMMARY$TASK;$DURATION"$'\n' +} + +done_steps() { + echo "" + echo "Build Summary" + echo "=============" + echo "${SUMMARY}" | column -t -s';' +} + +trap done_steps EXIT \ No newline at end of file diff --git a/build/tfs/common/enqueue.ts b/build/tfs/common/enqueue.ts new file mode 100644 index 0000000000..684fb62ec9 --- /dev/null +++ b/build/tfs/common/enqueue.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { execSync } from 'child_process'; +import { DocumentClient } from 'documentdb'; +import * as azure from 'azure-storage'; + +interface Asset { + platform: string; + type: string; + url: string; + mooncakeUrl: string; + hash: string; +} + +function queueSigningRequest(quality: string, commit: string): Promise { + const retryOperations = new azure.ExponentialRetryPolicyFilter(); + const queueSvc = azure + .createQueueService(process.env['AZURE_STORAGE_ACCOUNT_2'], process.env['AZURE_STORAGE_ACCESS_KEY_2']) + .withFilter(retryOperations); + + queueSvc.messageEncoder = new azure.QueueMessageEncoder.TextBase64QueueMessageEncoder(); + + const message = `${quality}/${commit}`; + + return new Promise((c, e) => queueSvc.createMessage('sign-darwin', message, err => err ? e(err) : c())); +} + +function isBuildSigned(quality: string, commit: string): Promise { + const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT'], { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const collection = 'dbs/builds/colls/' + quality; + const updateQuery = { + query: 'SELECT TOP 1 * FROM c WHERE c.id = @id', + parameters: [{ name: '@id', value: commit }] + }; + + return new Promise((c, e) => { + client.queryDocuments(collection, updateQuery).toArray((err, results) => { + if (err) { return e(err); } + if (results.length !== 1) { return c(false); } + + const [release] = results; + const assets: Asset[] = release.assets; + const isSigned = assets.some(a => a.platform === 'darwin' && a.type === 'archive'); + + c(isSigned); + }); + }); +} + +async function waitForSignedBuild(quality: string, commit: string): Promise { + let retries = 0; + + while (retries < 180) { + if (await isBuildSigned(quality, commit)) { + return; + } + + await new Promise(c => setTimeout(c, 10000)); + retries++; + } + + throw new Error('Timed out waiting for signed build'); +} + +async function main(quality: string): Promise { + const commit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); + + console.log(`Queueing signing request for '${quality}/${commit}'...`); + await queueSigningRequest(quality, commit); + + console.log('Waiting on signed build...'); + await waitForSignedBuild(quality, commit); + + console.log('Found signed build!'); +} + +main(process.argv[2]).catch(err => { + console.error(err); + process.exit(1); +}); \ No newline at end of file diff --git a/build/tfs/common/installDistro.ts b/build/tfs/common/installDistro.ts new file mode 100644 index 0000000000..47ad1a76f5 --- /dev/null +++ b/build/tfs/common/installDistro.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const cp = require('child_process'); +const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + +function npmInstall(package: string, args: string[]): void { + const result = cp.spawnSync(npm, ['install', package, ...args], { + stdio: 'inherit' + }); + + if (result.error || result.status !== 0) { + process.exit(1); + } +} + +const product = require('../../../product.json'); +const dependencies = product.dependencies || {} as { [name: string]: string; }; +const [, , ...args] = process.argv; + +Object.keys(dependencies).forEach(name => { + const url = dependencies[name]; + npmInstall(url, args); +}); \ No newline at end of file diff --git a/build/tfs/common/node.sh b/build/tfs/common/node.sh new file mode 100644 index 0000000000..87f95a5e1d --- /dev/null +++ b/build/tfs/common/node.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +# setup nvm +if [[ "$OSTYPE" == "darwin"* ]]; then + export NVM_DIR=~/.nvm + source $(brew --prefix nvm)/nvm.sh +else + source $NVM_DIR/nvm.sh +fi + +# install node +NODE_VERSION=7.10.0 +nvm install $NODE_VERSION +nvm use $NODE_VERSION \ No newline at end of file diff --git a/build/tfs/common/publish.ts b/build/tfs/common/publish.ts new file mode 100644 index 0000000000..80ed113816 --- /dev/null +++ b/build/tfs/common/publish.ts @@ -0,0 +1,266 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as fs from 'fs'; +import { execSync } from 'child_process'; +import { Readable } from 'stream'; +import * as crypto from 'crypto'; +import * as azure from 'azure-storage'; +import * as mime from 'mime'; +import * as minimist from 'minimist'; +import { DocumentClient, NewDocument } from 'documentdb'; + +if (process.argv.length < 6) { + console.error('Usage: node publish.js '); + process.exit(-1); +} + +function hashStream(hashName: string, stream: Readable): Promise { + return new Promise((c, e) => { + const shasum = crypto.createHash(hashName); + + stream + .on('data', shasum.update.bind(shasum)) + .on('error', e) + .on('close', () => c(shasum.digest('hex'))); + }); +} + +interface Config { + id: string; + frozen: boolean; +} + +function createDefaultConfig(quality: string): Config { + return { + id: quality, + frozen: false + }; +} + +function getConfig(quality: string): Promise { + const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT'], { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const collection = 'dbs/builds/colls/config'; + const query = { + query: `SELECT TOP 1 * FROM c WHERE c.id = @quality`, + parameters: [ + { name: '@quality', value: quality } + ] + }; + + return new Promise((c, e) => { + client.queryDocuments(collection, query).toArray((err, results) => { + if (err && err.code !== 409) { return e(err); } + + c(!results || results.length === 0 ? createDefaultConfig(quality) : results[0] as any as Config); + }); + }); +} + +interface Asset { + platform: string; + type: string; + url: string; + mooncakeUrl: string; + hash: string; + sha256hash: string; +} + +function createOrUpdate(commit: string, quality: string, platform: string, type: string, release: NewDocument, asset: Asset, isUpdate: boolean): Promise { + const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT'], { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const collection = 'dbs/builds/colls/' + quality; + const updateQuery = { + query: 'SELECT TOP 1 * FROM c WHERE c.id = @id', + parameters: [{ name: '@id', value: commit }] + }; + + let updateTries = 0; + + function update(): Promise { + updateTries++; + + return new Promise((c, e) => { + client.queryDocuments(collection, updateQuery).toArray((err, results) => { + if (err) { return e(err); } + if (results.length !== 1) { return e(new Error('No documents')); } + + const release = results[0]; + + release.assets = [ + ...release.assets.filter((a: any) => !(a.platform === platform && a.type === type)), + asset + ]; + + if (isUpdate) { + release.updates[platform] = type; + } + + client.replaceDocument(release._self, release, err => { + if (err && err.code === 409 && updateTries < 5) { return c(update()); } + if (err) { return e(err); } + + console.log('Build successfully updated.'); + c(); + }); + }); + }); + } + + return new Promise((c, e) => { + client.createDocument(collection, release, err => { + if (err && err.code === 409) { return c(update()); } + if (err) { return e(err); } + + console.log('Build successfully published.'); + c(); + }); + }); +} + +async function assertContainer(blobService: azure.BlobService, quality: string): Promise { + await new Promise((c, e) => blobService.createContainerIfNotExists(quality, { publicAccessLevel: 'blob' }, err => err ? e(err) : c())); +} + +async function doesAssetExist(blobService: azure.BlobService, quality: string, blobName: string): Promise { + const existsResult = await new Promise((c, e) => blobService.doesBlobExist(quality, blobName, (err, r) => err ? e(err) : c(r))); + return existsResult.exists; +} + +async function uploadBlob(blobService: azure.BlobService, quality: string, blobName: string, file: string): Promise { + const blobOptions: azure.BlobService.CreateBlockBlobRequestOptions = { + contentSettings: { + contentType: mime.lookup(file), + cacheControl: 'max-age=31536000, public' + } + }; + + await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(quality, blobName, file, blobOptions, err => err ? e(err) : c())); +} + +interface PublishOptions { + 'upload-only': boolean; +} + +async function publish(commit: string, quality: string, platform: string, type: string, name: string, version: string, _isUpdate: string, file: string, opts: PublishOptions): Promise { + const isUpdate = _isUpdate === 'true'; + + const queuedBy = process.env['BUILD_QUEUEDBY']; + const sourceBranch = process.env['BUILD_SOURCEBRANCH']; + const isReleased = quality === 'insider' + && /^master$|^refs\/heads\/master$/.test(sourceBranch) + && /Project Collection Service Accounts|Microsoft.VisualStudio.Services.TFS/.test(queuedBy); + + console.log('Publishing...'); + console.log('Quality:', quality); + console.log('Platforn:', platform); + console.log('Type:', type); + console.log('Name:', name); + console.log('Version:', version); + console.log('Commit:', commit); + console.log('Is Update:', isUpdate); + console.log('Is Released:', isReleased); + console.log('File:', file); + + const stream = fs.createReadStream(file); + const [sha1hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); + + console.log('SHA1:', sha1hash); + console.log('SHA256:', sha256hash); + + const blobName = commit + '/' + name; + const storageAccount = process.env['AZURE_STORAGE_ACCOUNT_2']; + + const blobService = azure.createBlobService(storageAccount, process.env['AZURE_STORAGE_ACCESS_KEY_2']) + .withFilter(new azure.ExponentialRetryPolicyFilter(20)); + + const mooncakeBlobService = azure.createBlobService(storageAccount, process.env['MOONCAKE_STORAGE_ACCESS_KEY'], `${storageAccount}.blob.core.chinacloudapi.cn`) + .withFilter(new azure.ExponentialRetryPolicyFilter(20)); + + // mooncake is fussy and far away, this is needed! + mooncakeBlobService.defaultClientRequestTimeoutInMs = 10 * 60 * 1000; + + await Promise.all([ + assertContainer(blobService, quality), + assertContainer(mooncakeBlobService, quality) + ]); + + const [blobExists, moooncakeBlobExists] = await Promise.all([ + doesAssetExist(blobService, quality, blobName), + doesAssetExist(mooncakeBlobService, quality, blobName) + ]); + + const promises = []; + + if (!blobExists) { + promises.push(uploadBlob(blobService, quality, blobName, file)); + } + + if (!moooncakeBlobExists) { + promises.push(uploadBlob(mooncakeBlobService, quality, blobName, file)); + } + + if (promises.length === 0) { + console.log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); + return; + } + + console.log('Uploading blobs to Azure storage...'); + + await Promise.all(promises); + + console.log('Blobs successfully uploaded.'); + + const config = await getConfig(quality); + + console.log('Quality config:', config); + + const asset: Asset = { + platform: platform, + type: type, + url: `${process.env['AZURE_CDN_URL']}/${quality}/${blobName}`, + mooncakeUrl: `${process.env['MOONCAKE_CDN_URL']}/${quality}/${blobName}`, + hash: sha1hash, + sha256hash + }; + + const release = { + id: commit, + timestamp: (new Date()).getTime(), + version, + isReleased: config.frozen ? false : isReleased, + sourceBranch, + queuedBy, + assets: [], + updates: {} as any + }; + + if (!opts['upload-only']) { + release.assets.push(asset); + + if (isUpdate) { + release.updates[platform] = type; + } + } + + await createOrUpdate(commit, quality, platform, type, release, asset, isUpdate); +} + +function main(): void { + const opts = minimist(process.argv.slice(2), { + boolean: ['upload-only'] + }); + + const [quality, platform, type, name, version, _isUpdate, file] = opts._; + const commit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); + + publish(commit, quality, platform, type, name, version, _isUpdate, file, opts).catch(err => { + console.error(err); + process.exit(1); + }); +} + +main(); diff --git a/build/tfs/darwin/build.sh b/build/tfs/darwin/build.sh new file mode 100644 index 0000000000..acbb849d4b --- /dev/null +++ b/build/tfs/darwin/build.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +. ./build/tfs/common/node.sh +. ./scripts/env.sh +. ./build/tfs/common/common.sh + +export VSCODE_MIXIN_PASSWORD="$1" +export AZURE_STORAGE_ACCESS_KEY="$2" +export AZURE_STORAGE_ACCESS_KEY_2="$3" +export MOONCAKE_STORAGE_ACCESS_KEY="$4" +export AZURE_DOCUMENTDB_MASTERKEY="$5" +VSO_PAT="$6" + +echo "machine monacotools.visualstudio.com password $VSO_PAT" > ~/.netrc + +step "Install dependencies" \ + npm install + +step "Hygiene" \ + npm run gulp -- hygiene + +step "Mix in repository from vscode-distro" \ + npm run gulp -- mixin + +step "Install distro dependencies" \ + node build/tfs/common/installDistro.js + +step "Build minified & upload source maps" \ + npm run gulp -- vscode-darwin-min upload-vscode-sourcemaps + +# step "Create loader snapshot" +# node build/lib/snapshotLoader.js + +step "Run unit tests" \ + ./scripts/test.sh --build --reporter dot + +step "Run integration tests" \ + ./scripts/test-integration.sh + +step "Publish release" \ + ./build/tfs/darwin/release.sh diff --git a/build/tfs/darwin/release.sh b/build/tfs/darwin/release.sh new file mode 100644 index 0000000000..24ed4c30a7 --- /dev/null +++ b/build/tfs/darwin/release.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +. ./scripts/env.sh +. ./build/tfs/common/common.sh + +(cd $BUILD_SOURCESDIRECTORY/build/tfs/common && \ + step "Install build dependencies" \ + npm i) + +REPO=`pwd` +ZIP=$REPO/../VSCode-darwin-selfsigned.zip +UNSIGNEDZIP=$REPO/../VSCode-darwin-unsigned.zip +BUILD=$REPO/../VSCode-darwin +PACKAGEJSON=`ls $BUILD/*.app/Contents/Resources/app/package.json` +VERSION=`node -p "require(\"$PACKAGEJSON\").version"` + +rm -rf $UNSIGNEDZIP +(cd $BUILD && \ + step "Create unsigned archive" \ + zip -r -X -y $UNSIGNEDZIP *) + +step "Upload unsigned archive" \ + node build/tfs/common/publish.js --upload-only $VSCODE_QUALITY darwin archive-unsigned VSCode-darwin-$VSCODE_QUALITY-unsigned.zip $VERSION false $UNSIGNEDZIP + +step "Sign build" \ + node build/tfs/common/enqueue.js $VSCODE_QUALITY \ No newline at end of file diff --git a/build/tfs/darwin/smoketest.sh b/build/tfs/darwin/smoketest.sh new file mode 100644 index 0000000000..ad1606c3aa --- /dev/null +++ b/build/tfs/darwin/smoketest.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +. ./build/tfs/common/node.sh +. ./scripts/env.sh +. ./build/tfs/common/common.sh + +export VSCODE_MIXIN_PASSWORD="$1" +VSO_PAT="$2" + +echo "machine monacotools.visualstudio.com password $VSO_PAT" > ~/.netrc + +step "Install dependencies" \ + npm install + +step "Mix in repository from vscode-distro" \ + npm run gulp -- mixin + +step "Install distro dependencies" \ + node build/tfs/common/installDistro.js + +step "Build minified & upload source maps" \ + npm run gulp -- vscode-darwin-min + +step "Run smoke test" \ + pushd test/smoke + npm install + npm test -- --latest "$AGENT_BUILDDIRECTORY/VSCode-darwin/Visual Studio Code - Insiders.app/Contents/MacOS/Electron" + popd \ No newline at end of file diff --git a/build/tfs/linux/.gitignore b/build/tfs/linux/.gitignore new file mode 100644 index 0000000000..0f46fa7086 --- /dev/null +++ b/build/tfs/linux/.gitignore @@ -0,0 +1 @@ +pat \ No newline at end of file diff --git a/build/tfs/linux/build-ia32.sh b/build/tfs/linux/build-ia32.sh new file mode 100644 index 0000000000..0b0f1c2a45 --- /dev/null +++ b/build/tfs/linux/build-ia32.sh @@ -0,0 +1,3 @@ +#!/bin/bash +set -e +./build/tfs/linux/build.sh ia32 "$@" \ No newline at end of file diff --git a/build/tfs/linux/build-x64.sh b/build/tfs/linux/build-x64.sh new file mode 100644 index 0000000000..fb5b38e02b --- /dev/null +++ b/build/tfs/linux/build-x64.sh @@ -0,0 +1,3 @@ +#!/bin/bash +set -e +./build/tfs/linux/build.sh x64 "$@" \ No newline at end of file diff --git a/build/tfs/linux/build.sh b/build/tfs/linux/build.sh new file mode 100644 index 0000000000..b3d1825c2d --- /dev/null +++ b/build/tfs/linux/build.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +. ./build/tfs/common/node.sh +. ./scripts/env.sh +. ./build/tfs/common/common.sh + +export ARCH="$1" +export VSCODE_MIXIN_PASSWORD="$2" +export AZURE_STORAGE_ACCESS_KEY="$3" +export AZURE_STORAGE_ACCESS_KEY_2="$4" +export MOONCAKE_STORAGE_ACCESS_KEY="$5" +export AZURE_DOCUMENTDB_MASTERKEY="$6" +export LINUX_REPO_PASSWORD="$7" +VSO_PAT="$8" + +echo "machine monacotools.visualstudio.com password $VSO_PAT" > ~/.netrc + +step "Install dependencies" \ + npm install --arch=$ARCH --unsafe-perm + +step "Hygiene" \ + npm run gulp -- hygiene + +step "Mix in repository from vscode-distro" \ + npm run gulp -- mixin + +step "Get Electron" \ + npm run gulp -- "electron-$ARCH" + +step "Install distro dependencies" \ + node build/tfs/common/installDistro.js --arch=$ARCH + +step "Build minified" \ + npm run gulp -- "vscode-linux-$ARCH-min" + +# step "Create loader snapshot" +# node build/lib/snapshotLoader.js --arch=$ARCH + +step "Run unit tests" \ + ./scripts/test.sh --build --reporter dot + +step "Publish release" \ + ./build/tfs/linux/release.sh diff --git a/build/tfs/linux/ia32/Dockerfile b/build/tfs/linux/ia32/Dockerfile new file mode 100644 index 0000000000..b309a1e940 --- /dev/null +++ b/build/tfs/linux/ia32/Dockerfile @@ -0,0 +1,50 @@ +FROM microsoft/vsts-agent:ubuntu-14.04-standard +MAINTAINER Joao Moreno + +ARG DEBIAN_FRONTEND=noninteractive +RUN dpkg --add-architecture i386 +RUN apt-get update + +# Dependencies +RUN apt-get install -y build-essential +RUN apt-get install -y gcc-multilib g++-multilib +RUN apt-get install -y git +RUN apt-get install -y zip +RUN apt-get install -y rpm +RUN apt-get install -y createrepo +RUN apt-get install -y python-gtk2 +RUN apt-get install -y jq +RUN apt-get install -y xvfb +RUN apt-get install -y fakeroot +RUN apt-get install -y libgtk2.0-0:i386 +RUN apt-get install -y libgconf-2-4:i386 +RUN apt-get install -y libnss3:i386 +RUN apt-get install -y libasound2:i386 +RUN apt-get install -y libxtst6:i386 +RUN apt-get install -y libfuse2 +RUN apt-get install -y libnotify-bin +RUN apt-get install -y libnotify4:i386 +RUN apt-get install -y libx11-dev:i386 +RUN apt-get install -y libxkbfile-dev:i386 +RUN apt-get install -y libxss1:i386 +RUN apt-get install -y libx11-xcb-dev:i386 +RUN apt-get install -y libgl1-mesa-glx:i386 libgl1-mesa-dri:i386 +RUN apt-get install -y libxkbfile-dev +RUN apt-get install -y bc bsdmainutils +RUN apt-get install -y libgirepository-1.0-1:i386 gir1.2-glib-2.0:i386 gir1.2-secret-1:i386 libsecret-1-dev:i386 +RUN apt-get install -y dpkg-dev:i386 + +# Xvfb +# Thanks https://medium.com/@griggheo/running-headless-selenium-webdriver-tests-in-docker-containers-342fdbabf756 +ADD xvfb.init /etc/init.d/xvfb +RUN chmod +x /etc/init.d/xvfb +RUN update-rc.d xvfb defaults + +# nvm +ENV NVM_DIR /usr/local/nvm +RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash + +# for libsecret +ENV PKG_CONFIG_PATH /usr/lib/i386-linux-gnu/pkgconfig + +CMD (service xvfb start; export DISPLAY=:10; ./start.sh) \ No newline at end of file diff --git a/build/tfs/linux/ia32/run-agent.sh b/build/tfs/linux/ia32/run-agent.sh new file mode 100644 index 0000000000..bcf9017f3c --- /dev/null +++ b/build/tfs/linux/ia32/run-agent.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ ! -f pat ]; then + echo "Error: file pat not found" + exit 1 +fi + +docker run \ + -e VSTS_ACCOUNT="monacotools" \ + -e VSTS_TOKEN="$(cat pat)" \ + -e VSTS_AGENT="tb-lnx-ia32-local" \ + -e VSTS_POOL="linux-ia32" \ + -e VSTS_WORK="/var/vsts/work" \ + --name "tb-lnx-ia32-local" \ + -it joaomoreno/vscode-vso-agent-ia32:latest \ No newline at end of file diff --git a/build/tfs/linux/ia32/xvfb.init b/build/tfs/linux/ia32/xvfb.init new file mode 100644 index 0000000000..4d77d253a2 --- /dev/null +++ b/build/tfs/linux/ia32/xvfb.init @@ -0,0 +1,53 @@ +#!/bin/bash +# +# /etc/rc.d/init.d/xvfbd +# +# chkconfig: 345 95 28 +# description: Starts/Stops X Virtual Framebuffer server +# processname: Xvfb +# +### BEGIN INIT INFO +# Provides: xvfb +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start xvfb at boot time +# Description: Enable xvfb provided by daemon. +### END INIT INFO + +[ "${NETWORKING}" = "no" ] && exit 0 + +PROG="/usr/bin/Xvfb" +PROG_OPTIONS=":10 -ac" +PROG_OUTPUT="/tmp/Xvfb.out" + +case "$1" in + start) + echo "Starting : X Virtual Frame Buffer " + $PROG $PROG_OPTIONS>>$PROG_OUTPUT 2>&1 & + disown -ar + ;; + stop) + echo "Shutting down : X Virtual Frame Buffer" + killproc $PROG + RETVAL=$? + [ $RETVAL -eq 0 ] && /bin/rm -f /var/lock/subsys/Xvfb + /var/run/Xvfb.pid + echo + ;; + restart|reload) + $0 stop + $0 start + RETVAL=$? + ;; + status) + status Xvfb + RETVAL=$? + ;; + *) + echo $"Usage: $0 (start|stop|restart|reload|status)" + exit 1 +esac + +exit $RETVAL \ No newline at end of file diff --git a/build/tfs/linux/release.sh b/build/tfs/linux/release.sh new file mode 100644 index 0000000000..41f6d35e67 --- /dev/null +++ b/build/tfs/linux/release.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +. ./scripts/env.sh +. ./build/tfs/common/common.sh + +step "Build Debian package" \ + npm run gulp -- "vscode-linux-$ARCH-build-deb" + +step "Build RPM package" \ + npm run gulp -- "vscode-linux-$ARCH-build-rpm" + +(cd $BUILD_SOURCESDIRECTORY/build/tfs/common && \ + step "Install build dependencies" \ + npm install --unsafe-perm) + +# Variables +PLATFORM_LINUX="linux-$ARCH" +PLATFORM_DEB="linux-deb-$ARCH" +PLATFORM_RPM="linux-rpm-$ARCH" +[[ "$ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64" +[[ "$ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64" +REPO="`pwd`" +ROOT="$REPO/.." +BUILDNAME="VSCode-$PLATFORM_LINUX" +BUILD="$ROOT/$BUILDNAME" +BUILD_VERSION="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/ | sed -e 's/code-[a-z]*_//g' -e 's/\.deb$//g')" +[ -z "$VSCODE_QUALITY" ] && TARBALL_FILENAME="code-$BUILD_VERSION.tar.gz" || TARBALL_FILENAME="code-$VSCODE_QUALITY-$BUILD_VERSION.tar.gz" +TARBALL_PATH="$ROOT/$TARBALL_FILENAME" +PACKAGEJSON="$BUILD/resources/app/package.json" +VERSION=$(node -p "require(\"$PACKAGEJSON\").version") + +rm -rf $ROOT/code-*.tar.* +(cd $ROOT && \ + step "Create tar.gz archive" \ + tar -czf $TARBALL_PATH $BUILDNAME) + +step "Publish tar.gz archive" \ + node build/tfs/common/publish.js $VSCODE_QUALITY $PLATFORM_LINUX archive-unsigned $TARBALL_FILENAME $VERSION true $TARBALL_PATH + +DEB_FILENAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/)" +DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME" + +step "Publish Debian package" \ + node build/tfs/common/publish.js $VSCODE_QUALITY $PLATFORM_DEB package $DEB_FILENAME $VERSION true $DEB_PATH + +RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)" +RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" + +step "Publish RPM package" \ + node build/tfs/common/publish.js $VSCODE_QUALITY $PLATFORM_RPM package $RPM_FILENAME $VERSION true $RPM_PATH + +if [ -z "$VSCODE_QUALITY" ]; then + echo "VSCODE_QUALITY is not set, skipping repo package publish" +else + if [ "$BUILD_SOURCEBRANCH" = "master" ] || [ "$BUILD_SOURCEBRANCH" = "refs/heads/master" ]; then + if [[ $BUILD_QUEUEDBY = *"Project Collection Service Accounts"* || $BUILD_QUEUEDBY = *"Microsoft.VisualStudio.Services.TFS"* ]]; then + # Get necessary information + pushd $REPO && COMMIT_HASH=$(git rev-parse HEAD) && popd + PACKAGE_NAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/ | sed -e 's/_.*//g')" + DEB_URL="https://az764295.vo.msecnd.net/$VSCODE_QUALITY/$COMMIT_HASH/$DEB_FILENAME" + RPM_URL="https://az764295.vo.msecnd.net/$VSCODE_QUALITY/$COMMIT_HASH/$RPM_FILENAME" + PACKAGE_VERSION="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/ | sed -e 's/code-[a-z]*_//g' -e 's/\_.*$//g')" + # Write config files needed by API, use eval to force environment variable expansion + DIRNAME=$(dirname $(readlink -f $0)) + pushd $DIRNAME + # Submit to apt repo + if [ "$DEB_ARCH" = "amd64" ]; then + eval echo '{ \"server\": \"azure-apt-cat.cloudapp.net\", \"protocol\": \"https\", \"port\": \"443\", \"repositoryId\": \"58a4adf642421134a1a48d1a\", \"username\": \"$LINUX_REPO_USERNAME\", \"password\": \"$LINUX_REPO_PASSWORD\" }' > apt-config.json + eval echo '{ \"name\": \"$PACKAGE_NAME\", \"version\": \"$PACKAGE_VERSION\", \"repositoryId\": \"58a4adf642421134a1a48d1a\", \"sourceUrl\": \"$DEB_URL\" }' > apt-addpkg.json + echo "Submitting apt-addpkg.json:" + cat apt-addpkg.json + + step "Publish to repositories" \ + ./repoapi_client.sh -config apt-config.json -addpkg apt-addpkg.json + fi + # Submit to yum repo (disabled as it's manual until signing is automated) + # eval echo '{ \"server\": \"azure-apt-cat.cloudapp.net\", \"protocol\": \"https\", \"port\": \"443\", \"repositoryId\": \"58a4ae3542421134a1a48d1b\", \"username\": \"$LINUX_REPO_USERNAME\", \"password\": \"$LINUX_REPO_PASSWORD\" }' > yum-config.json + # eval echo '{ \"name\": \"$PACKAGE_NAME\", \"version\": \"$PACKAGE_VERSION\", \"repositoryId\": \"58a4ae3542421134a1a48d1b\", \"sourceUrl\": \"$RPM_URL\" }' > yum-addpkg.json + # echo "Submitting yum-addpkg.json:" + # cat yum-addpkg.json + # ./repoapi_client.sh -config yum-config.json -addpkg yum-addpkg.json + popd + echo "To check repo publish status run ./repoapi_client.sh -config config.json -check " + fi + fi +fi diff --git a/build/tfs/linux/repoapi_client.sh b/build/tfs/linux/repoapi_client.sh new file mode 100644 index 0000000000..b214ef1072 --- /dev/null +++ b/build/tfs/linux/repoapi_client.sh @@ -0,0 +1,262 @@ +#!/bin/bash -e +# This is a VERY basic script for Create/Delete operations on repos and packages +# +cmd=$1 +urls=urls.txt +defaultPackageFile=new_package.json +defaultRepoFile=new_repo.json + +function Bail +{ + echo "ERROR: $@" + exit 1 +} + +function BailIfFileMissing { + file="$1" + if [ ! -f "$file" ]; then + Bail "File $file does not exist" + fi +} + +function Usage { + echo "USAGE: Manage repos and packages in an apt repository" + echo "$0 -config FILENAME -listrepos | -listpkgs | -addrepo FILENAME | -addpkg FILENAME |" + echo "-addpkgs FILENAME | -check ID | -delrepo REPOID | -delpkg PKGID" + echo -e "\t-config FILENAME : JSON file containing API server name and creds" + echo -e "\t-listrepos : List repositories" + echo -e "\t-listpkgs [REGEX] : List packages, optionally filter by REGEX" + echo -e "\t-addrepo FILENAME : Create a new repo using the specified JSON file" + echo -e "\t-addpkg FILENAME : Add package to repo using the specified JSON file" + echo -e "\t-addpkgs FILENAME : Add packages to repo using urls contained in FILENAME" + echo -e "\t-check ID : Check upload operation by ID" + echo -e "\t-delrepo REPOID : Delete the specified repo by ID" + echo -e "\t-delpkg PKGID : Delete the specified package by ID" + exit 1 +} + +function ParseFromJson { + if [ -z "$secretContents" ]; then + Bail "Unable to parse value because no JSON contents were specified" + elif [ -z "$1" ]; then + Bail "Unable to parse value from JSON because no key was specified" + fi + # Write value directly to stdout to be used by caller + echo $secretContents | jq "$1" | tr -d '"' +} + +function ParseConfigFile { + configFile="$1" + if [ -z "$configFile" ]; then + echo "Must specify -config option" + Usage + fi + BailIfFileMissing "$configFile" + secretContents=$(cat "$configFile") + + server=$(ParseFromJson .server) + protocol=$(ParseFromJson .protocol) + port=$(ParseFromJson .port) + repositoryId=$(ParseFromJson .repositoryId) + user=$(ParseFromJson .username) + pass=$(ParseFromJson .password) + baseurl="$protocol://$user:$pass@$server:$port" +} + +# List Repositories +function ListRepositories +{ + echo "Fetching repo list from $server..." + curl -k "$baseurl/v1/repositories" | sed 's/,/,\n/g' | sed 's/^"/\t"/g' + echo "" +} + +# List packages, using $1 as a regex to filter results +function ListPackages +{ + echo "Fetching package list from $server" + curl -k "$baseurl/v1/packages" | sed 's/{/\n{/g' | egrep "$1" | sed 's/,/,\n/g' | sed 's/^"/\t"/g' + echo "" +} + +# Create a new Repo using the specified JSON file +function AddRepo +{ + repoFile=$1 + if [ -z $repoFile ]; then + Bail "Error: Must specify a JSON-formatted file. Reference $defaultRepoFile.template" + fi + if [ ! -f $repoFile ]; then + Bail "Error: Cannot create repo - $repoFile does not exist" + fi + packageUrl=$(grep "url" $repoFile | head -n 1 | awk '{print $2}' | tr -d ',') + echo "Creating new repo on $server [$packageUrl]" + curl -i -k "$baseurl/v1/repositories" --data @./$repoFile -H "Content-Type: application/json" + echo "" +} + +# Upload a single package using the specified JSON file +function AddPackage +{ + packageFile=$1 + if [ -z $packageFile ]; then + Bail "Error: Must specify a JSON-formatted file. Reference $defaultPackageFile.template" + fi + if [ ! -f $packageFile ]; then + Bail "Error: Cannot add package - $packageFile does not exist" + fi + packageUrl=$(grep "sourceUrl" $packageFile | head -n 1 | awk '{print $2}') + echo "Adding package to $server [$packageUrl]" + curl -i -k "$baseurl/v1/packages" --data @./$packageFile -H "Content-Type: application/json" + echo "" +} + +# Upload a single package by dynamically creating a JSON file using a provided URL +function AddPackageByUrl +{ + url=$(echo "$1") + if [ -z "$url" ]; then + Bail "Unable to publish package because no URL was specified" + fi + tmpFile=$(mktemp) + tmpOut=$(mktemp) + if ! wget -q "$url" -O $tmpFile; then + rm -f $tmpFile $tmpFile + Bail "Unable to download URL $url" + elif dpkg -I $tmpFile > $tmpOut 2> /dev/null; then + echo "File is deb format" + pkgName=$(grep "^\s*Package:" $tmpOut | awk '{print $2}') + pkgVer=$(grep "^\s*Version:" $tmpOut | awk '{print $2}') + elif rpm -qpi $tmpFile > $tmpOut 2> /dev/null; then + echo "File is rpm format" + pkgName=$(egrep "^Name" $tmpOut | tr -d ':' | awk '{print $2}') + pkgVer=$(egrep "^Version" $tmpOut | tr -d ':' | awk '{print $2}') + else + rm -f $tmpFile $tmpOut + Bail "File is not a valid deb/rpm package $url" + fi + + rm -f $tmpFile $tmpOut + if [ -z "$pkgName" ]; then + Bail "Unable to parse package name for $url" + elif [ -z "$pkgVer" ]; then + Bail "Unable to parse package version number for $url" + fi + + # Create Package .json file + escapedUrl=$(echo "$url" | sed 's/\//\\\//g' | sed 's/\&/\\\&/g') + cp $defaultPackageFile.template $defaultPackageFile + sed -i "s/PACKAGENAME/$pkgName/g" $defaultPackageFile + sed -i "s/PACKAGEVERSION/$pkgVer/g" $defaultPackageFile + sed -i "s/PACKAGEURL/$escapedUrl/g" $defaultPackageFile + sed -i "s/REPOSITORYID/$repositoryId/g" $defaultPackageFile + # Perform Upload + AddPackage $defaultPackageFile + # Cleanup + rm -f $defaultPackageFile +} + +# Upload multiple packages by reading urls line-by-line from the specified file +function AddPackages +{ + urlFile=$1 + if [ -z $urlFile ]; then + Bail "Must specify a flat text file containing one or more URLs" + fi + if [ ! -f $urlFile ]; then + Bail "Cannot add packages. File $urlFile does not exist" + fi + for url in $(cat $urlFile); do + if [ -n "$url" ]; then + AddPackageByUrl "$url" + fi + sleep 5 + done +} + +# Check upload by ID +function CheckUpload { + id=$1 + if [ -z "$id" ]; then + Bail "Must specify an ID" + fi + curl -k $baseurl/v1/packages/queue/$id + echo "" +} + +# Delete the specified repo +function DeleteRepo +{ + repoId=$1 + if [ -z $repoId ]; then + Bail "Please specify repository ID. Run -listrepos for a list of IDs" + fi + curl -I -k -X DELETE "$baseurl/v1/repositories/$repoId" +} + +# Delete the specified package +function DeletePackage +{ + packageId=$1 + if [ -z $packageId ]; then + Bail "Please specify package ID. Run -listpkgs for a list of IDs" + fi + echo Removing pkgId $packageId from repo $repositoryId + curl -I -k -X DELETE "$baseurl/v1/packages/$packageId" +} + +# Parse params +# Not using getopts because this uses multi-char flags +operation= +while (( "$#" )); do + if [[ "$1" == "-config" ]]; then + shift + configFile="$1" + elif [[ "$1" == "-listrepos" ]]; then + operation=ListRepositories + elif [[ "$1" == "-listpkgs" ]]; then + operation=ListPackages + if [ -n "$2" ]; then + shift + operand="$1" + fi + elif [[ "$1" == "-addrepo" ]]; then + operation=AddRepo + shift + operand="$1" + elif [[ "$1" == "-addpkg" ]]; then + operation=AddPackage + shift + operand="$1" + elif [[ "$1" == "-addpkgs" ]]; then + operation=AddPackages + shift + operand="$1" + elif [[ "$1" == "-check" ]]; then + operation=CheckUpload + shift + operand="$1" + elif [[ "$1" == "-delrepo" ]]; then + operation=DeleteRepo + shift + operand="$1" + elif [[ "$1" == "-delpkg" ]]; then + operation=DeletePackage + shift + operand="$1" + else + Usage + fi + shift +done + +echo "Performing $operation $operand" +# Parse config file +ParseConfigFile "$configFile" + +# Exit if no operation was specified +if [ -z "operation" ]; then + Usage +fi + +$operation "$operand" diff --git a/build/tfs/linux/smoketest.sh b/build/tfs/linux/smoketest.sh new file mode 100644 index 0000000000..8d71fff127 --- /dev/null +++ b/build/tfs/linux/smoketest.sh @@ -0,0 +1,46 @@ +#!/bin/bash +set -e + +. ./build/tfs/common/node.sh +. ./scripts/env.sh +. ./build/tfs/common/common.sh + +export ARCH="$1" +export VSCODE_MIXIN_PASSWORD="$2" +VSO_PAT="$3" + +echo "machine monacotools.visualstudio.com password $VSO_PAT" > ~/.netrc + +step "Install dependencies" \ + npm install --arch=$ARCH --unsafe-perm + +step "Mix in repository from vscode-distro" \ + npm run gulp -- mixin + +step "Get Electron" \ + npm run gulp -- "electron-$ARCH" + +step "Install distro dependencies" \ + node build/tfs/common/installDistro.js --arch=$ARCH + +step "Build minified" \ + npm run gulp -- "vscode-linux-$ARCH-min" + +function configureEnvironment { + id -u testuser &>/dev/null || (useradd -m testuser; chpasswd <<< testuser:testpassword) + sudo -i -u testuser git config --global user.name "VS Code Agent" + sudo -i -u testuser git config --global user.email "monacotools@microsoft.com" + chown -R testuser $AGENT_BUILDDIRECTORY +} + +function runTest { + pushd test/smoke + npm install + sudo -u testuser -H xvfb-run -a -s "-screen 0 1024x768x8" npm test -- --latest "$AGENT_BUILDDIRECTORY/VSCode-linux-ia32/code-insiders" + popd +} + +step "Configure environment" configureEnvironment + +step "Run smoke test" runTest + diff --git a/build/tfs/linux/x64/Dockerfile b/build/tfs/linux/x64/Dockerfile new file mode 100644 index 0000000000..4fef9acb19 --- /dev/null +++ b/build/tfs/linux/x64/Dockerfile @@ -0,0 +1,43 @@ +FROM microsoft/vsts-agent:ubuntu-14.04-standard +MAINTAINER Joao Moreno + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update + +# Dependencies +RUN apt-get install -y build-essential +RUN apt-get install -y gcc-multilib g++-multilib +RUN apt-get install -y git +RUN apt-get install -y dpkg-dev +RUN apt-get install -y zip +RUN apt-get install -y rpm +RUN apt-get install -y createrepo +RUN apt-get install -y python-gtk2 +RUN apt-get install -y jq +RUN apt-get install -y xvfb +RUN apt-get install -y fakeroot +RUN apt-get install -y libgtk2.0-0 +RUN apt-get install -y libgconf-2-4 +RUN apt-get install -y libnss3 +RUN apt-get install -y libasound2 +RUN apt-get install -y libxtst6 +RUN apt-get install -y libfuse2 +RUN apt-get install -y libnotify-bin +RUN apt-get install -y libx11-dev +RUN apt-get install -y libxss1 +RUN apt-get install -y libx11-xcb-dev +RUN apt-get install -y libxkbfile-dev +RUN apt-get install -y bc bsdmainutils +RUN apt-get install -y libsecret-1-dev + +# Xvfb +# Thanks https://medium.com/@griggheo/running-headless-selenium-webdriver-tests-in-docker-containers-342fdbabf756 +ADD xvfb.init /etc/init.d/xvfb +RUN chmod +x /etc/init.d/xvfb +RUN update-rc.d xvfb defaults + +# nvm +ENV NVM_DIR /usr/local/nvm +RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash + +CMD (service xvfb start; export DISPLAY=:10; ./start.sh) \ No newline at end of file diff --git a/build/tfs/linux/x64/run-agent.sh b/build/tfs/linux/x64/run-agent.sh new file mode 100644 index 0000000000..76978ce2b0 --- /dev/null +++ b/build/tfs/linux/x64/run-agent.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ ! -f pat ]; then + echo "Error: file pat not found" + exit 1 +fi + +docker run \ + -e VSTS_ACCOUNT="monacotools" \ + -e VSTS_TOKEN="$(cat pat)" \ + -e VSTS_AGENT="tb-lnx-x64-local" \ + -e VSTS_POOL="linux-x64" \ + -e VSTS_WORK="/var/vsts/work" \ + --name "tb-lnx-x64-local" \ + -it joaomoreno/vscode-vso-agent-x64:latest \ No newline at end of file diff --git a/build/tfs/linux/x64/xvfb.init b/build/tfs/linux/x64/xvfb.init new file mode 100644 index 0000000000..4d77d253a2 --- /dev/null +++ b/build/tfs/linux/x64/xvfb.init @@ -0,0 +1,53 @@ +#!/bin/bash +# +# /etc/rc.d/init.d/xvfbd +# +# chkconfig: 345 95 28 +# description: Starts/Stops X Virtual Framebuffer server +# processname: Xvfb +# +### BEGIN INIT INFO +# Provides: xvfb +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start xvfb at boot time +# Description: Enable xvfb provided by daemon. +### END INIT INFO + +[ "${NETWORKING}" = "no" ] && exit 0 + +PROG="/usr/bin/Xvfb" +PROG_OPTIONS=":10 -ac" +PROG_OUTPUT="/tmp/Xvfb.out" + +case "$1" in + start) + echo "Starting : X Virtual Frame Buffer " + $PROG $PROG_OPTIONS>>$PROG_OUTPUT 2>&1 & + disown -ar + ;; + stop) + echo "Shutting down : X Virtual Frame Buffer" + killproc $PROG + RETVAL=$? + [ $RETVAL -eq 0 ] && /bin/rm -f /var/lock/subsys/Xvfb + /var/run/Xvfb.pid + echo + ;; + restart|reload) + $0 stop + $0 start + RETVAL=$? + ;; + status) + status Xvfb + RETVAL=$? + ;; + *) + echo $"Usage: $0 (start|stop|restart|reload|status)" + exit 1 +esac + +exit $RETVAL \ No newline at end of file diff --git a/build/tfs/win32/1_build.ps1 b/build/tfs/win32/1_build.ps1 new file mode 100644 index 0000000000..0090920d50 --- /dev/null +++ b/build/tfs/win32/1_build.ps1 @@ -0,0 +1,55 @@ +Param( + [string]$arch, + [string]$mixinPassword, + [string]$vsoPAT +) + +. .\build\tfs\win32\node.ps1 +. .\scripts\env.ps1 +. .\build\tfs\win32\lib.ps1 + +# Create a _netrc file to download distro dependencies +# In order to get _netrc to work, we need a HOME variable setup +"machine monacotools.visualstudio.com password ${vsoPAT}" | Out-File "$env:HOME\_netrc" -Encoding ASCII + +# Set the right architecture +$env:npm_config_arch="$arch" + +step "Install dependencies" { + exec { & npm install } +} + +step "Hygiene" { + exec { & npm run gulp -- hygiene } +} + +$env:VSCODE_MIXIN_PASSWORD = $mixinPassword +step "Mix in repository from vscode-distro" { + exec { & npm run gulp -- mixin } +} + +step "Get Electron" { + exec { & npm run gulp -- "electron-$global:arch" } +} + +step "Install distro dependencies" { + exec { & node build\tfs\common\installDistro.js } +} + +step "Build minified" { + exec { & npm run gulp -- "vscode-win32-$global:arch-min" } +} + +# step "Create loader snapshot" { +# exec { & node build\lib\snapshotLoader.js --arch=$global:arch } +# } + +step "Run unit tests" { + exec { & .\scripts\test.bat --build --reporter dot } +} + +# step "Run integration tests" { +# exec { & .\scripts\test-integration.bat } +# } + +done diff --git a/build/tfs/win32/2_package.ps1 b/build/tfs/win32/2_package.ps1 new file mode 100644 index 0000000000..dcd9061137 --- /dev/null +++ b/build/tfs/win32/2_package.ps1 @@ -0,0 +1,12 @@ +Param( + [string]$arch +) + +. .\build\tfs\win32\node.ps1 +. .\build\tfs\win32\lib.ps1 + +step "Create archive and setup package" { + exec { & npm run gulp -- "vscode-win32-$global:arch-archive" "vscode-win32-$global:arch-setup" } +} + +done \ No newline at end of file diff --git a/build/tfs/win32/3_upload.ps1 b/build/tfs/win32/3_upload.ps1 new file mode 100644 index 0000000000..8d10c90826 --- /dev/null +++ b/build/tfs/win32/3_upload.ps1 @@ -0,0 +1,35 @@ +Param( + [string]$arch, + [string]$storageKey, + [string]$mooncakeStorageKey, + [string]$documentDbKey +) + +. .\build\tfs\win32\node.ps1 +. .\build\tfs\win32\lib.ps1 + +$Repo = "$(pwd)" +$Root = "$Repo\.." +$Exe = "$Repo\.build\win32-$arch\setup\VSCodeSetup.exe" +$Zip = "$Repo\.build\win32-$arch\archive\VSCode-win32-$arch.zip" +$Build = "$Root\VSCode-win32-$arch" + +# get version +$PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json +$Version = $PackageJson.version +$Quality = "$env:VSCODE_QUALITY" +$env:AZURE_STORAGE_ACCESS_KEY_2 = $storageKey +$env:MOONCAKE_STORAGE_ACCESS_KEY = $mooncakeStorageKey +$env:AZURE_DOCUMENTDB_MASTERKEY = $documentDbKey + +$assetPlatform = if ($arch -eq "ia32") { "win32" } else { "win32-x64" } + +step "Publish archive" { + exec { & node build/tfs/common/publish.js $Quality "$global:assetPlatform-archive" archive "VSCode-win32-$global:arch-$Version.zip" $Version true $Zip } +} + +step "Publish setup package" { + exec { & node build/tfs/common/publish.js $Quality "$global:assetPlatform" setup "VSCodeSetup-$global:arch-$Version.exe" $Version true $Exe } +} + +done \ No newline at end of file diff --git a/build/tfs/win32/lib.ps1 b/build/tfs/win32/lib.ps1 new file mode 100644 index 0000000000..39a3bade9d --- /dev/null +++ b/build/tfs/win32/lib.ps1 @@ -0,0 +1,47 @@ +# stop when there's an error +$ErrorActionPreference = 'Stop' + +$env:HOME=$env:USERPROFILE + +if (Test-Path env:AGENT_WORKFOLDER) { + $env:HOME="${env:AGENT_WORKFOLDER}\home" + $env:npm_config_cache="${env:HOME}\npm-cache" + $env:npm_config_devdir="${env:HOME}\npm-devdir" + New-Item -Path "$env:HOME" -Type directory -Force | out-null + New-Item -Path "$env:npm_config_cache" -Type directory -Force | out-null +} + +# throw when a process exits with something other than 0 +function exec([scriptblock]$cmd, [string]$errorMessage = "Error executing command: " + $cmd) { + & $cmd + if ($LastExitCode -ne 0) { + throw $errorMessage + } +} + +$Summary = @() +function step($Task, $Step) { + echo "" + echo "*****************************************************************************" + echo "Start: $Task" + echo "*****************************************************************************" + echo "" + + $Stopwatch = [Diagnostics.Stopwatch]::StartNew() + Invoke-Command $Step + $Stopwatch.Stop() + $Formatted = "{0:g}" -f $Stopwatch.Elapsed + + echo "*****************************************************************************" + echo "End: $Task, Total: $Formatted" + echo "*****************************************************************************" + + $global:Summary += @{ "$Task" = $Formatted } +} + +function done() { + echo "" + echo "Build Summary" + echo "=============" + $global:Summary | Format-Table @{L="Task";E={$_.Name}}, @{L="Duration";E={$_.Value}} +} \ No newline at end of file diff --git a/build/tfs/win32/node.ps1 b/build/tfs/win32/node.ps1 new file mode 100644 index 0000000000..38afc1f39c --- /dev/null +++ b/build/tfs/win32/node.ps1 @@ -0,0 +1,6 @@ +# install node +$env:Path = $env:NVM_HOME + ";" + $env:NVM_SYMLINK + ";" + $env:Path +$NodeVersion = "7.10.0" +nvm install $NodeVersion +nvm use $NodeVersion +$env:Path = $env:NVM_HOME + "\v" + $NodeVersion + ";" + $env:Path \ No newline at end of file diff --git a/build/tfs/win32/smoketest.ps1 b/build/tfs/win32/smoketest.ps1 new file mode 100644 index 0000000000..2a6519aaaf --- /dev/null +++ b/build/tfs/win32/smoketest.ps1 @@ -0,0 +1,47 @@ +Param( + [string]$arch, + [string]$mixinPassword, + [string]$vsoPAT +) + +. .\build\tfs\win32\node.ps1 +. .\scripts\env.ps1 +. .\build\tfs\win32\lib.ps1 + +# Create a _netrc file to download distro dependencies +# In order to get _netrc to work, we need a HOME variable setup +$env:HOME = $env:USERPROFILE +"machine monacotools.visualstudio.com password ${vsoPAT}" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII + +# Set the right architecture +$env:npm_config_arch = "$arch" + +step "Install dependencies" { + exec { & npm install } +} + +$env:VSCODE_MIXIN_PASSWORD = $mixinPassword +step "Mix in repository from vscode-distro" { + exec { & npm run gulp -- mixin } +} + +step "Get Electron" { + exec { & npm run gulp -- "electron-$global:arch" } +} + +step "Install distro dependencies" { + exec { & node build\tfs\common\installDistro.js } +} + +step "Build minified" { + exec { & npm run gulp -- "vscode-win32-$global:arch-min" } +} + +step "Run smoke test" { + exec { & Push-Location test\smoke } + exec { & npm install } + exec { & npm test -- --latest "$env:AGENT_BUILDDIRECTORY\VSCode-win32-$global:arch\Code - Insiders.exe" } + exec { & Pop-Location } +} + +done \ No newline at end of file diff --git a/build/tsconfig.json b/build/tsconfig.json new file mode 100644 index 0000000000..04f3963055 --- /dev/null +++ b/build/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "noImplicitAny": false, + "removeComments": false, + "preserveConstEnums": true, + "sourceMap": false, + "experimentalDecorators": true, + "newLine": "LF" + }, + "exclude": [ + "node_modules/**" + ] +} \ No newline at end of file diff --git a/build/tslint.json b/build/tslint.json new file mode 100644 index 0000000000..e269a87c0e --- /dev/null +++ b/build/tslint.json @@ -0,0 +1,13 @@ +{ + "rules": { + "no-unused-expression": true, + "no-duplicate-variable": true, + "no-unused-variable": true, + "curly": true, + "class-name": true, + "semicolon": [ + "always" + ], + "triple-equals": true + } +} \ No newline at end of file diff --git a/build/win32/code.iss b/build/win32/code.iss new file mode 100644 index 0000000000..961b8f8516 --- /dev/null +++ b/build/win32/code.iss @@ -0,0 +1,174 @@ +#define LocalizedLanguageFile(Language = "") \ + DirExists(RepoDir + "\licenses") && Language != "" \ + ? ('; LicenseFile: "' + RepoDir + '\licenses\LICENSE-' + Language + '.txt"') \ + : '; LicenseFile: "' + RepoDir + '\LICENSE.txt"' + +[Setup] +AppId={#AppId} +AppName={#NameLong} +AppVerName={#NameVersion} +AppPublisher=Microsoft Corporation +AppPublisherURL=https://github.com/Microsoft/sqlopsstudio +AppSupportURL=https://github.com/Microsoft/sqlopsstudio +AppUpdatesURL=https://github.com/Microsoft/sqlopsstudio + +DefaultDirName={pf}\{#DirName} +DefaultGroupName={#NameLong} +AllowNoIcons=yes +OutputDir={#OutputDir} +OutputBaseFilename=SqlOpsStudioSetup +Compression=lzma +SolidCompression=yes +AppMutex={#AppMutex} +WizardImageFile={#RepoDir}\resources\win32\inno-big.bmp +WizardSmallImageFile={#RepoDir}\resources\win32\inno-small.bmp +SetupIconFile={#RepoDir}\resources\win32\code.ico +UninstallDisplayIcon={app}\{#ExeBasename}.exe +ChangesEnvironment=true +ChangesAssociations=true +MinVersion=6.1.7600 +SourceDir={#SourceDir} +AppVersion={#Version} +VersionInfoVersion={#RawVersion} +ShowLanguageDialog=auto +ArchitecturesAllowed={#ArchitecturesAllowed} +ArchitecturesInstallIn64BitMode={#ArchitecturesInstallIn64BitMode} + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl,{#RepoDir}\build\win32\i18n\messages.en.isl" {#LocalizedLanguageFile} +Name: "german"; MessagesFile: "compiler:Languages\German.isl,{#RepoDir}\build\win32\i18n\messages.de.isl" {#LocalizedLanguageFile("deu")} +Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl,{#RepoDir}\build\win32\i18n\messages.es.isl" {#LocalizedLanguageFile("esp")} +Name: "french"; MessagesFile: "compiler:Languages\French.isl,{#RepoDir}\build\win32\i18n\messages.fr.isl" {#LocalizedLanguageFile("fra")} +Name: "italian"; MessagesFile: "compiler:Languages\Italian.isl,{#RepoDir}\build\win32\i18n\messages.it.isl" {#LocalizedLanguageFile("ita")} +Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl,{#RepoDir}\build\win32\i18n\messages.ja.isl" {#LocalizedLanguageFile("jpn")} +Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl,{#RepoDir}\build\win32\i18n\messages.ru.isl" {#LocalizedLanguageFile("rus")} +Name: "korean"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.ko.isl,{#RepoDir}\build\win32\i18n\messages.ko.isl" {#LocalizedLanguageFile("kor")} +Name: "simplifiedChinese"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.zh-cn.isl,{#RepoDir}\build\win32\i18n\messages.zh-cn.isl" {#LocalizedLanguageFile("chs")} +Name: "traditionalChinese"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.zh-tw.isl,{#RepoDir}\build\win32\i18n\messages.zh-tw.isl" {#LocalizedLanguageFile("cht")} + +[InstallDelete] +Type: filesandordirs; Name: {app}\resources\app\out +Type: filesandordirs; Name: {app}\resources\app\plugins +Type: filesandordirs; Name: {app}\resources\app\extensions +Type: filesandordirs; Name: {app}\resources\app\node_modules +Type: files; Name: {app}\resources\app\Credits_45.0.2454.85.html + +[Tasks] +Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1 +Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}" +Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent + +[Files] +Source: "*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs + +[Icons] +Name: "{group}\{#NameLong}"; Filename: "{app}\{#ExeBasename}.exe"; AppUserModelID: "{#AppUserId}" +Name: "{commondesktop}\{#NameLong}"; Filename: "{app}\{#ExeBasename}.exe"; AppUserModelID: "{#AppUserId}" +Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#NameLong}"; Filename: "{app}\{#ExeBasename}.exe"; Tasks: quicklaunchicon; AppUserModelID: "{#AppUserId}" + +[Run] +Filename: "{app}\{#ExeBasename}.exe"; Description: "{cm:LaunchProgram,{#NameLong}}"; Tasks: runcode; Flags: nowait postinstall; Check: WizardSilent +Filename: "{app}\{#ExeBasename}.exe"; Description: "{cm:LaunchProgram,{#NameLong}}"; Flags: nowait postinstall; Check: WizardNotSilent + +[Registry] +Root: HKCR; Subkey: "{#RegValueName}SourceFile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,{#NameLong}}"; Flags: uninsdeletekey +Root: HKCR; Subkey: "{#RegValueName}SourceFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\code_file.ico" +Root: HKCR; Subkey: "{#RegValueName}SourceFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1""" + +Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; Tasks: addtopath; Check: NeedsAddPath(ExpandConstant('{app}\bin')) + +[Code] +// Don't allow installing conflicting architectures +function InitializeSetup(): Boolean; +var + RegKey: String; + ThisArch: String; + AltArch: String; +begin + Result := True; + + if IsWin64 then begin + RegKey := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + copy('{#IncompatibleAppId}', 2, 38) + '_is1'; + + if '{#Arch}' = 'ia32' then begin + Result := not RegKeyExists(HKLM64, RegKey); + ThisArch := '32'; + AltArch := '64'; + end else begin + Result := not RegKeyExists(HKLM32, RegKey); + ThisArch := '64'; + AltArch := '32'; + end; + + if not Result then begin + MsgBox('Please uninstall the ' + AltArch + '-bit version of {#NameShort} before installing this ' + ThisArch + '-bit version.', mbInformation, MB_OK); + end; + end; +end; + +function WizardNotSilent(): Boolean; +begin + Result := not WizardSilent(); +end; + +// http://stackoverflow.com/a/23838239/261019 +procedure Explode(var Dest: TArrayOfString; Text: String; Separator: String); +var + i, p: Integer; +begin + i := 0; + repeat + SetArrayLength(Dest, i+1); + p := Pos(Separator,Text); + if p > 0 then begin + Dest[i] := Copy(Text, 1, p-1); + Text := Copy(Text, p + Length(Separator), Length(Text)); + i := i + 1; + end else begin + Dest[i] := Text; + Text := ''; + end; + until Length(Text)=0; +end; + +function NeedsAddPath(Param: string): boolean; +var + OrigPath: string; +begin + if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', OrigPath) + then begin + Result := True; + exit; + end; + Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0; +end; + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); +var + Path: string; + CarbonPath: string; + Parts: TArrayOfString; + NewPath: string; + i: Integer; +begin + if not CurUninstallStep = usUninstall then begin + exit; + end; + if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Path) + then begin + exit; + end; + NewPath := ''; + CarbonPath := ExpandConstant('{app}\bin') + Explode(Parts, Path, ';'); + for i:=0 to GetArrayLength(Parts)-1 do begin + if CompareText(Parts[i], CarbonPath) <> 0 then begin + NewPath := NewPath + Parts[i]; + + if i < GetArrayLength(Parts) - 1 then begin + NewPath := NewPath + ';'; + end; + end; + end; + RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', NewPath); +end; diff --git a/build/win32/i18n/Default.isl b/build/win32/i18n/Default.isl new file mode 100644 index 0000000000..fdcfbb1656 --- /dev/null +++ b/build/win32/i18n/Default.isl @@ -0,0 +1,337 @@ +; *** Inno Setup version 5.5.3+ English messages *** +; +; To download user-contributed translations of this file, go to: +; http://www.jrsoftware.org/files/istrans/ +; +; Note: When translating this text, do not add periods (.) to the end of +; messages that didn't have them already, because on those messages Inno +; Setup adds the periods automatically (appending a period would result in +; two periods being displayed). + +[LangOptions] +; The following three entries are very important. Be sure to read and +; understand the '[LangOptions] section' topic in the help file. +LanguageName=English +LanguageID=$0409 +LanguageCodePage=0 +; If the language you are translating to requires special font faces or +; sizes, uncomment any of the following entries and change them accordingly. +;DialogFontName= +;DialogFontSize=8 +;WelcomeFontName=Verdana +;WelcomeFontSize=12 +;TitleFontName=Arial +;TitleFontSize=29 +;CopyrightFontName=Arial +;CopyrightFontSize=8 + +[Messages] + +; *** Application titles +SetupAppTitle=Setup +SetupWindowTitle=Setup - %1 +UninstallAppTitle=Uninstall +UninstallAppFullTitle=%1 Uninstall + +; *** Misc. common +InformationTitle=Information +ConfirmTitle=Confirm +ErrorTitle=Error + +; *** SetupLdr messages +SetupLdrStartupMessage=This will install %1. Do you wish to continue? +LdrCannotCreateTemp=Unable to create a temporary file. Setup aborted +LdrCannotExecTemp=Unable to execute file in the temporary directory. Setup aborted + +; *** Startup error messages +LastErrorMessage=%1.%n%nError %2: %3 +SetupFileMissing=The file %1 is missing from the installation directory. Please correct the problem or obtain a new copy of the program. +SetupFileCorrupt=The setup files are corrupted. Please obtain a new copy of the program. +SetupFileCorruptOrWrongVer=The setup files are corrupted, or are incompatible with this version of Setup. Please correct the problem or obtain a new copy of the program. +InvalidParameter=An invalid parameter was passed on the command line:%n%n%1 +SetupAlreadyRunning=Setup is already running. +WindowsVersionNotSupported=This program does not support the version of Windows your computer is running. +WindowsServicePackRequired=This program requires %1 Service Pack %2 or later. +NotOnThisPlatform=This program will not run on %1. +OnlyOnThisPlatform=This program must be run on %1. +OnlyOnTheseArchitectures=This program can only be installed on versions of Windows designed for the following processor architectures:%n%n%1 +MissingWOW64APIs=The version of Windows you are running does not include functionality required by Setup to perform a 64-bit installation. To correct this problem, please install Service Pack %1. +WinVersionTooLowError=This program requires %1 version %2 or later. +WinVersionTooHighError=This program cannot be installed on %1 version %2 or later. +AdminPrivilegesRequired=You must be logged in as an administrator when installing this program. +PowerUserPrivilegesRequired=You must be logged in as an administrator or as a member of the Power Users group when installing this program. +SetupAppRunningError=Setup has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. +UninstallAppRunningError=Uninstall has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. + +; *** Misc. errors +ErrorCreatingDir=Setup was unable to create the directory "%1" +ErrorTooManyFilesInDir=Unable to create a file in the directory "%1" because it contains too many files + +; *** Setup common messages +ExitSetupTitle=Exit Setup +ExitSetupMessage=Setup is not complete. If you exit now, the program will not be installed.%n%nYou may run Setup again at another time to complete the installation.%n%nExit Setup? +AboutSetupMenuItem=&About Setup... +AboutSetupTitle=About Setup +AboutSetupMessage=%1 version %2%n%3%n%n%1 home page:%n%4 +AboutSetupNote= +TranslatorNote= + +; *** Buttons +ButtonBack=< &Back +ButtonNext=&Next > +ButtonInstall=&Install +ButtonOK=OK +ButtonCancel=Cancel +ButtonYes=&Yes +ButtonYesToAll=Yes to &All +ButtonNo=&No +ButtonNoToAll=N&o to All +ButtonFinish=&Finish +ButtonBrowse=&Browse... +ButtonWizardBrowse=B&rowse... +ButtonNewFolder=&Make New Folder + +; *** "Select Language" dialog messages +SelectLanguageTitle=Select Setup Language +SelectLanguageLabel=Select the language to use during the installation: + +; *** Common wizard text +ClickNext=Click Next to continue, or Cancel to exit Setup. +BeveledLabel= +BrowseDialogTitle=Browse For Folder +BrowseDialogLabel=Select a folder in the list below, then click OK. +NewFolderName=New Folder + +; *** "Welcome" wizard page +WelcomeLabel1=Welcome to the [name] Setup Wizard +WelcomeLabel2=This will install [name/ver] on your computer.%n%nIt is recommended that you close all other applications before continuing. + +; *** "Password" wizard page +WizardPassword=Password +PasswordLabel1=This installation is password protected. +PasswordLabel3=Please provide the password, then click Next to continue. Passwords are case-sensitive. +PasswordEditLabel=&Password: +IncorrectPassword=The password you entered is not correct. Please try again. + +; *** "License Agreement" wizard page +WizardLicense=License Agreement +LicenseLabel=Please read the following important information before continuing. +LicenseLabel3=Please read the following License Agreement. You must accept the terms of this agreement before continuing with the installation. +LicenseAccepted=I &accept the agreement +LicenseNotAccepted=I &do not accept the agreement + +; *** "Information" wizard pages +WizardInfoBefore=Information +InfoBeforeLabel=Please read the following important information before continuing. +InfoBeforeClickLabel=When you are ready to continue with Setup, click Next. +WizardInfoAfter=Information +InfoAfterLabel=Please read the following important information before continuing. +InfoAfterClickLabel=When you are ready to continue with Setup, click Next. + +; *** "User Information" wizard page +WizardUserInfo=User Information +UserInfoDesc=Please enter your information. +UserInfoName=&User Name: +UserInfoOrg=&Organization: +UserInfoSerial=&Serial Number: +UserInfoNameRequired=You must enter a name. + +; *** "Select Destination Location" wizard page +WizardSelectDir=Select Destination Location +SelectDirDesc=Where should [name] be installed? +SelectDirLabel3=Setup will install [name] into the following folder. +SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse. +DiskSpaceMBLabel=At least [mb] MB of free disk space is required. +CannotInstallToNetworkDrive=Setup cannot install to a network drive. +CannotInstallToUNCPath=Setup cannot install to a UNC path. +InvalidPath=You must enter a full path with drive letter; for example:%n%nC:\APP%n%nor a UNC path in the form:%n%n\\server\share +InvalidDrive=The drive or UNC share you selected does not exist or is not accessible. Please select another. +DiskSpaceWarningTitle=Not Enough Disk Space +DiskSpaceWarning=Setup requires at least %1 KB of free space to install, but the selected drive only has %2 KB available.%n%nDo you want to continue anyway? +DirNameTooLong=The folder name or path is too long. +InvalidDirName=The folder name is not valid. +BadDirName32=Folder names cannot include any of the following characters:%n%n%1 +DirExistsTitle=Folder Exists +DirExists=The folder:%n%n%1%n%nalready exists. Would you like to install to that folder anyway? +DirDoesntExistTitle=Folder Does Not Exist +DirDoesntExist=The folder:%n%n%1%n%ndoes not exist. Would you like the folder to be created? + +; *** "Select Components" wizard page +WizardSelectComponents=Select Components +SelectComponentsDesc=Which components should be installed? +SelectComponentsLabel2=Select the components you want to install; clear the components you do not want to install. Click Next when you are ready to continue. +FullInstallation=Full installation +; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) +CompactInstallation=Compact installation +CustomInstallation=Custom installation +NoUninstallWarningTitle=Components Exist +NoUninstallWarning=Setup has detected that the following components are already installed on your computer:%n%n%1%n%nDeselecting these components will not uninstall them.%n%nWould you like to continue anyway? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceMBLabel=Current selection requires at least [mb] MB of disk space. + +; *** "Select Additional Tasks" wizard page +WizardSelectTasks=Select Additional Tasks +SelectTasksDesc=Which additional tasks should be performed? +SelectTasksLabel2=Select the additional tasks you would like Setup to perform while installing [name], then click Next. + +; *** "Select Start Menu Folder" wizard page +WizardSelectProgramGroup=Select Start Menu Folder +SelectStartMenuFolderDesc=Where should Setup place the program's shortcuts? +SelectStartMenuFolderLabel3=Setup will create the program's shortcuts in the following Start Menu folder. +SelectStartMenuFolderBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse. +MustEnterGroupName=You must enter a folder name. +GroupNameTooLong=The folder name or path is too long. +InvalidGroupName=The folder name is not valid. +BadGroupName=The folder name cannot include any of the following characters:%n%n%1 +NoProgramGroupCheck2=&Don't create a Start Menu folder + +; *** "Ready to Install" wizard page +WizardReady=Ready to Install +ReadyLabel1=Setup is now ready to begin installing [name] on your computer. +ReadyLabel2a=Click Install to continue with the installation, or click Back if you want to review or change any settings. +ReadyLabel2b=Click Install to continue with the installation. +ReadyMemoUserInfo=User information: +ReadyMemoDir=Destination location: +ReadyMemoType=Setup type: +ReadyMemoComponents=Selected components: +ReadyMemoGroup=Start Menu folder: +ReadyMemoTasks=Additional tasks: + +; *** "Preparing to Install" wizard page +WizardPreparing=Preparing to Install +PreparingDesc=Setup is preparing to install [name] on your computer. +PreviousInstallNotCompleted=The installation/removal of a previous program was not completed. You will need to restart your computer to complete that installation.%n%nAfter restarting your computer, run Setup again to complete the installation of [name]. +CannotContinue=Setup cannot continue. Please click Cancel to exit. +ApplicationsFound=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. +ApplicationsFound2=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. After the installation has completed, Setup will attempt to restart the applications. +CloseApplications=&Automatically close the applications +DontCloseApplications=&Do not close the applications +ErrorCloseApplications=Setup was unable to automatically close all applications. It is recommended that you close all applications using files that need to be updated by Setup before continuing. + +; *** "Installing" wizard page +WizardInstalling=Installing +InstallingLabel=Please wait while Setup installs [name] on your computer. + +; *** "Setup Completed" wizard page +FinishedHeadingLabel=Completing the [name] Setup Wizard +FinishedLabelNoIcons=Setup has finished installing [name] on your computer. +FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed shortcuts. +ClickFinish=Click Finish to exit Setup. +FinishedRestartLabel=To complete the installation of [name], Setup must restart your computer. Would you like to restart now? +FinishedRestartMessage=To complete the installation of [name], Setup must restart your computer.%n%nWould you like to restart now? +ShowReadmeCheck=Yes, I would like to view the README file +YesRadio=&Yes, restart the computer now +NoRadio=&No, I will restart the computer later +; used for example as 'Run MyProg.exe' +RunEntryExec=Run %1 +; used for example as 'View Readme.txt' +RunEntryShellExec=View %1 + +; *** "Setup Needs the Next Disk" stuff +ChangeDiskTitle=Setup Needs the Next Disk +SelectDiskLabel2=Please insert Disk %1 and click OK.%n%nIf the files on this disk can be found in a folder other than the one displayed below, enter the correct path or click Browse. +PathLabel=&Path: +FileNotInDir2=The file "%1" could not be located in "%2". Please insert the correct disk or select another folder. +SelectDirectoryLabel=Please specify the location of the next disk. + +; *** Installation phase messages +SetupAborted=Setup was not completed.%n%nPlease correct the problem and run Setup again. +EntryAbortRetryIgnore=Click Retry to try again, Ignore to proceed anyway, or Abort to cancel installation. + +; *** Installation status messages +StatusClosingApplications=Closing applications... +StatusCreateDirs=Creating directories... +StatusExtractFiles=Extracting files... +StatusCreateIcons=Creating shortcuts... +StatusCreateIniEntries=Creating INI entries... +StatusCreateRegistryEntries=Creating registry entries... +StatusRegisterFiles=Registering files... +StatusSavingUninstall=Saving uninstall information... +StatusRunProgram=Finishing installation... +StatusRestartingApplications=Restarting applications... +StatusRollback=Rolling back changes... + +; *** Misc. errors +ErrorInternal2=Internal error: %1 +ErrorFunctionFailedNoCode=%1 failed +ErrorFunctionFailed=%1 failed; code %2 +ErrorFunctionFailedWithMessage=%1 failed; code %2.%n%3 +ErrorExecutingProgram=Unable to execute file:%n%1 + +; *** Registry errors +ErrorRegOpenKey=Error opening registry key:%n%1\%2 +ErrorRegCreateKey=Error creating registry key:%n%1\%2 +ErrorRegWriteKey=Error writing to registry key:%n%1\%2 + +; *** INI errors +ErrorIniEntry=Error creating INI entry in file "%1". + +; *** File copying errors +FileAbortRetryIgnore=Click Retry to try again, Ignore to skip this file (not recommended), or Abort to cancel installation. +FileAbortRetryIgnore2=Click Retry to try again, Ignore to proceed anyway (not recommended), or Abort to cancel installation. +SourceIsCorrupted=The source file is corrupted +SourceDoesntExist=The source file "%1" does not exist +ExistingFileReadOnly=The existing file is marked as read-only.%n%nClick Retry to remove the read-only attribute and try again, Ignore to skip this file, or Abort to cancel installation. +ErrorReadingExistingDest=An error occurred while trying to read the existing file: +FileExists=The file already exists.%n%nWould you like Setup to overwrite it? +ExistingFileNewer=The existing file is newer than the one Setup is trying to install. It is recommended that you keep the existing file.%n%nDo you want to keep the existing file? +ErrorChangingAttr=An error occurred while trying to change the attributes of the existing file: +ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory: +ErrorReadingSource=An error occurred while trying to read the source file: +ErrorCopying=An error occurred while trying to copy a file: +ErrorReplacingExistingFile=An error occurred while trying to replace the existing file: +ErrorRestartReplace=RestartReplace failed: +ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory: +ErrorRegisterServer=Unable to register the DLL/OCX: %1 +ErrorRegSvr32Failed=RegSvr32 failed with exit code %1 +ErrorRegisterTypeLib=Unable to register the type library: %1 + +; *** Post-installation errors +ErrorOpeningReadme=An error occurred while trying to open the README file. +ErrorRestartingComputer=Setup was unable to restart the computer. Please do this manually. + +; *** Uninstaller messages +UninstallNotFound=File "%1" does not exist. Cannot uninstall. +UninstallOpenError=File "%1" could not be opened. Cannot uninstall +UninstallUnsupportedVer=The uninstall log file "%1" is in a format not recognized by this version of the uninstaller. Cannot uninstall +UninstallUnknownEntry=An unknown entry (%1) was encountered in the uninstall log +ConfirmUninstall=Are you sure you want to completely remove %1 and all of its components? +UninstallOnlyOnWin64=This installation can only be uninstalled on 64-bit Windows. +OnlyAdminCanUninstall=This installation can only be uninstalled by a user with administrative privileges. +UninstallStatusLabel=Please wait while %1 is removed from your computer. +UninstalledAll=%1 was successfully removed from your computer. +UninstalledMost=%1 uninstall complete.%n%nSome elements could not be removed. These can be removed manually. +UninstalledAndNeedsRestart=To complete the uninstallation of %1, your computer must be restarted.%n%nWould you like to restart now? +UninstallDataCorrupted="%1" file is corrupted. Cannot uninstall + +; *** Uninstallation phase messages +ConfirmDeleteSharedFileTitle=Remove Shared File? +ConfirmDeleteSharedFile2=The system indicates that the following shared file is no longer in use by any programs. Would you like for Uninstall to remove this shared file?%n%nIf any programs are still using this file and it is removed, those programs may not function properly. If you are unsure, choose No. Leaving the file on your system will not cause any harm. +SharedFileNameLabel=File name: +SharedFileLocationLabel=Location: +WizardUninstalling=Uninstall Status +StatusUninstalling=Uninstalling %1... + +; *** Shutdown block reasons +ShutdownBlockReasonInstallingApp=Installing %1. +ShutdownBlockReasonUninstallingApp=Uninstalling %1. + +; The custom messages below aren't used by Setup itself, but if you make +; use of them in your scripts, you'll want to translate them. + +[CustomMessages] + +NameAndVersion=%1 version %2 +AdditionalIcons=Additional shortcuts: +CreateDesktopIcon=Create a &desktop shortcut +CreateQuickLaunchIcon=Create a &Quick Launch shortcut +ProgramOnTheWeb=%1 on the Web +UninstallProgram=Uninstall %1 +LaunchProgram=Launch %1 +AssocFileExtension=&Associate %1 with the %2 file extension +AssocingFileExtension=Associating %1 with the %2 file extension... +AutoStartProgramGroupDescription=Startup: +AutoStartProgram=Automatically start %1 +AddonHostProgramNotFound=%1 could not be located in the folder you selected.%n%nDo you want to continue anyway? + diff --git a/build/win32/i18n/Default.ko.isl b/build/win32/i18n/Default.ko.isl new file mode 100644 index 0000000000..a7c38d12b9 --- /dev/null +++ b/build/win32/i18n/Default.ko.isl @@ -0,0 +1,298 @@ +; *** Inno Setup version 5.5.3+ Korean messages *** +; +; To download user-contributed translations of this file, go to: +; http://www.jrsoftware.org/files/istrans/ +; +; Note: When translating this text, do not add periods (.) to the end of +; messages that didn't have them already, because on those messages Inno +; Setup adds the periods automatically (appending a period would result in +; two periods being displayed). +[LangOptions] +; The following three entries are very important. Be sure to read and +; understand the '[LangOptions] section' topic in the help file. +LanguageName=Korean +LanguageID=$0412 +LanguageCodePage=949 +; If the language you are translating to requires special font faces or +; sizes, uncomment any of the following entries and change them accordingly. +;DialogFontName= +;DialogFontSize=8 +;WelcomeFontName=Verdana +;WelcomeFontSize=12 +;TitleFontName=Arial +;TitleFontSize=29 +;CopyrightFontName=Arial +;CopyrightFontSize=8 +[Messages] +; *** Application titles +SetupAppTitle=ġ +SetupWindowTitle=ġ - %1 +UninstallAppTitle= +UninstallAppFullTitle=%1 +; *** Misc. common +InformationTitle= +ConfirmTitle=Ȯ +ErrorTitle= +; *** SetupLdr messages +SetupLdrStartupMessage=׷ %1() ġ˴ϴ. Ͻðڽϱ? +LdrCannotCreateTemp=ӽ ϴ. ġ α׷ ߴܵǾϴ. +LdrCannotExecTemp=ӽ ͸ ϴ. ġ α׷ ߴܵǾϴ. +; *** Startup error messages +LastErrorMessage=%1.%n%n %2: %3 +SetupFileMissing= %1() ġ ͸ Ǿϴ. ذϰų α׷ . +SetupFileCorrupt=ġ ջǾϴ. α׷ . +SetupFileCorruptOrWrongVer=ġ ջǾų ġ α׷ ȣȯ ʽϴ. ذϰų α׷ . +InvalidParameter=ٿ ߸ Ű ޵:%n%n%1 +SetupAlreadyRunning=ġ α׷ ̹ Դϴ. +WindowsVersionNotSupported= α׷ ǻͿ Windows ʽϴ. +WindowsServicePackRequired= α׷ ġϷ %1 %2 ̻ ʿմϴ. +NotOnThisPlatform= α׷ %1 ʽϴ. +OnlyOnThisPlatform= α׷ %1 ؾ մϴ. +OnlyOnTheseArchitectures= α׷ μ Űó %n%n%1 Windows ġ ֽϴ. +MissingWOW64APIs= Windows ġ α׷ 64Ʈ ġϴ ʿ ϴ. ذϷ %1() ġϼ. +WinVersionTooLowError= α׷ ġϷ %1 %2 ̻ ʿմϴ. +WinVersionTooHighError= α׷ %1 %2 ̻󿡼 ġ ϴ. +AdminPrivilegesRequired= α׷ ġ ڷ αؾ մϴ. +PowerUserPrivilegesRequired= α׷ ġ ڳ ׷ αؾ մϴ. +SetupAppRunningError=ġ α׷ %1() ߽ϴ.%n%n ׸ νϽ ݰ Ϸ [Ȯ], Ϸ [] Ŭϼ. +UninstallAppRunningError= ۾ %1() ߽ϴ.%n%n ׸ νϽ ݰ Ϸ [Ȯ], Ϸ [] Ŭϼ. +; *** Misc. errors +ErrorCreatingDir=ġ α׷ ͸ "%1"() ϴ. +ErrorTooManyFilesInDir=͸ "%1" ʹ Ƿ ͸ ϴ. +; *** Setup common messages +ExitSetupTitle=ġ +ExitSetupMessage=ġ Ϸ ʾҽϴ. ϸ α׷ ġ ʽϴ.%n%n߿ ġ α׷ ٽ Ͽ ġ ֽϴ.%n%nġ α׷ Ͻðڽϱ? +AboutSetupMenuItem=ġ α׷ (&A)... +AboutSetupTitle=ġ α׷ +AboutSetupMessage=%1 %2%n%3%n%n%1 Ȩ:%n%4 +AboutSetupNote= +TranslatorNote= +; *** Buttons +ButtonBack=< ڷ(&B) +ButtonNext=(&N) > +ButtonInstall=ġ(&I) +ButtonOK=Ȯ +ButtonCancel= +ButtonYes=(&Y) +ButtonYesToAll= (&A) +ButtonNo=ƴϿ(&N) +ButtonNoToAll= ƴϿ(&O) +ButtonFinish=ħ(&F) +ButtonBrowse=ãƺ(&B)... +ButtonWizardBrowse=ãƺ(&R) +ButtonNewFolder= (&M) +; *** "Select Language" dialog messages +SelectLanguageTitle=ġ +SelectLanguageLabel=ġ ߿  ϼ. +; *** Common wizard text +ClickNext=Ϸ [] Ŭϰ ġ α׷ Ϸ [] Ŭϼ. +BeveledLabel= +BrowseDialogTitle= ãƺ +BrowseDialogLabel=Ʒ Ͽ [Ȯ] Ŭϼ. +NewFolderName= +; *** "Welcome" wizard page +WelcomeLabel1=[name] ġ +WelcomeLabel2= ǻͿ [name/ver]() ġմϴ.%n%nϱ ٸ α׷ ݴ ϴ. +; *** "Password" wizard page +WizardPassword=ȣ +PasswordLabel1= ġ ȣ ȣǰ ֽϴ. +PasswordLabel3=Ϸ ȣ Է [] Ŭϼ. ȣ ҹڸ մϴ. +PasswordEditLabel=ȣ(&P): +IncorrectPassword=Է ȣ ߸Ǿϴ. ٽ õϼ. +; *** "License Agreement" wizard page +WizardLicense= +LicenseLabel=ϱ ߿ о . +LicenseLabel3= о ּ. ġ Ϸ ǿ ؾ մϴ. +LicenseAccepted=࿡ (&A) +LicenseNotAccepted=࿡ (&D) +; *** "Information" wizard pages +WizardInfoBefore= +InfoBeforeLabel=ϱ ߿ о . +InfoBeforeClickLabel=ġ غ Ǹ [] Ŭմϴ. +WizardInfoAfter= +InfoAfterLabel=ϱ ߿ о . +InfoAfterClickLabel=ġ غ Ǹ [] Ŭմϴ. +; *** "User Information" wizard page +WizardUserInfo= +UserInfoDesc= Էϼ. +UserInfoName= ̸(&U): +UserInfoOrg=(&O): +UserInfoSerial=Ϸ ȣ(&S): +UserInfoNameRequired≠ Էؾ մϴ. +; *** "Select Destination Location" wizard page +WizardSelectDir= ġ +SelectDirDesc=[name]() ġϽðڽϱ? +SelectDirLabel3=ġ α׷ [name]() ġմϴ. +SelectDirBrowseLabel=Ϸ [] Ŭϼ. ٸ Ϸ [ãƺ] Ŭϼ. +DiskSpaceMBLabel= [mb]MB ũ ʿմϴ. +CannotInstallToNetworkDrive=ġ α׷ Ʈũ ̺꿡 ġ ϴ. +CannotInstallToUNCPath=ġ α׷ UNC ο ġ ϴ. +InvalidPath=̺ ڿ Բ ü θ Էؾ մϴ. :%n%nC:\APP%n%nǴ UNC :%n%n\\server\share +InvalidDrive= ̺곪 UNC ų ׸ ׼ ϴ. ٸ ̺곪 UNC ϼ. +DiskSpaceWarningTitle=ũ +DiskSpaceWarning=ġ α׷ ġϷ ġ  %1KB ʿ ̺ %2KBۿ ϴ.%n%n׷ Ͻðڽϱ? +DirNameTooLong= ̸ Ǵ ΰ ʹ ϴ. +InvalidDirName= ̸ ߸Ǿϴ. +BadDirName32= ̸ %n%n%1 ڸ ϴ. +DirExistsTitle= +DirExists= %n%n%1%n%n() ̹ ֽϴ. ׷ ش ġϽðڽϱ? +DirDoesntExistTitle= +DirDoesntExist= %n%n%1%n%n() ϴ. ðڽϱ? +; *** "Select Components" wizard page +WizardSelectComponents= +SelectComponentsDesc= Ҹ ġϽðڽϱ? +SelectComponentsLabel2=ġ Ҵ ϰ ġ Ҵ 켼. غ Ǹ [] Ŭϼ. +FullInstallation=ü ġ +; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) +CompactInstallation=Compact ġ +CustomInstallation= ġ +NoUninstallWarningTitle= Ұ +NoUninstallWarning=ġ α׷ %n%n%1%n%n() ǻͿ ̹ ġǾ ߽ϴ. ̷ Ҵ ص ŵ ʽϴ.%n%n׷ Ͻðڽϱ? +ComponentSize1=%1KB +ComponentSize2=%1MB +ComponentsDiskSpaceMBLabel= ؼ  [mb]MB ũ ʿմϴ. +; *** "Select Additional Tasks" wizard page +WizardSelectTasks=߰ ۾ +SelectTasksDesc= ۾ ߰ Ͻðڽϱ? +SelectTasksLabel2=ġ α׷ [name]() ġϴ ߰ ۾ [] Ŭϼ. +; *** "Select Start Menu Folder" wizard page +WizardSelectProgramGroup= ޴ +SelectStartMenuFolderDesc=ġ α׷ α׷ ٷ ⸦ 鵵 Ͻðڽϱ? +SelectStartMenuFolderLabel3=ġ α׷ α׷ ٷ ⸦ ޴ ϴ. +SelectStartMenuFolderBrowseLabel=Ϸ [] Ŭϼ. ٸ Ϸ [ãƺ] Ŭϼ. +MustEnterGroupName= ̸ Էؾ մϴ. +GroupNameTooLong= ̸ Ǵ ΰ ʹ ϴ. +InvalidGroupName= ̸ ߸Ǿϴ. +BadGroupName= ̸ %n%n%1 ڸ ϴ. +NoProgramGroupCheck2= ޴ (&D) +; *** "Ready to Install" wizard page +WizardReady=ġ غ +ReadyLabel1= ġ α׷ ǻͿ [name] ġ غ Ǿϴ. +ReadyLabel2a=ġ Ϸ [ġ] Ŭϰ, ϰų Ϸ [ڷ] Ŭϼ. +ReadyLabel2b=ġ Ϸ [ġ] Ŭϼ. +ReadyMemoUserInfo= : +ReadyMemoDir= ġ: +ReadyMemoType=ġ : +ReadyMemoComponents= : +ReadyMemoGroup= ޴ : +ReadyMemoTasks=߰ ۾: +; *** "Preparing to Install" wizard page +WizardPreparing=ġ غ +PreparingDesc=ġ α׷ ǻͿ [name] ġ غϰ ֽϴ. +PreviousInstallNotCompleted= α׷ ġ/ ۾ Ϸ ʾҽϴ. ش ġ ϷϷ ǻ͸ ٽ ؾ մϴ.%n%nǻ͸ ٽ [name] ġ ϷϷ ġ α׷ ٽ ϼ. +CannotContinue=ġ α׷ ϴ. Ϸ [] Ŭϼ. +ApplicationsFound=ġ α׷ Ʈؾ ϴ α׷ ǰ ֽϴ. ġ α׷ ̷ α׷ ڵ ݵ ϴ ϴ. +ApplicationsFound2=ġ α׷ Ʈؾ ϴ α׷ ǰ ֽϴ. ġ α׷ ̷ α׷ ڵ ݵ ϴ ϴ. ġ ϷǸ ġ α׷ α׷ ٽ Ϸ õմϴ. +CloseApplications= α׷ ڵ ݱ(&A) +DontCloseApplications= α׷ (&D) +ErrorCloseApplications=ġ α׷ Ϻ α׷ ڵ ϴ. ϱ ġ α׷ Ʈؾ ϴ ϴ α׷ ݴ ϴ. +; *** "Installing" wizard page +WizardInstalling=ġ +InstallingLabel=ġ α׷ ǻͿ [name]() ġϴ ٷ ּ. +; *** "Setup Completed" wizard page +FinishedHeadingLabel=[name] 縦 Ϸϴ +FinishedLabelNoIcons=ġ α׷ ǻͿ [name]() ġ߽ϴ. +FinishedLabel=ġ α׷ ǻͿ [name]() ġ߽ϴ. ġ ٷ ⸦ Ͽ ش α׷ ֽϴ. +ClickFinish=ġ α׷ Ϸ [ħ] Ŭϼ. +FinishedRestartLabel=[name] ġ ϷϷ ġ α׷ ǻ͸ ٽ ؾ մϴ. ٽ Ͻðڽϱ? +FinishedRestartMessage=[name] ġ ϷϷ ġ α׷ ǻ͸ ٽ ؾ մϴ.%n%n ٽ Ͻðڽϱ? +ShowReadmeCheck=, README ڽϴ. +YesRadio=, ǻ͸ ٽ ϰڽϴ(&Y). +NoRadio=ƴϿ, ǻ͸ ߿ ٽ ϰڽϴ(&N). +; used for example as 'Run MyProg.exe' +RunEntryExec=%1 +; used for example as 'View Readme.txt' +RunEntryShellExec=%1 +; *** "Setup Needs the Next Disk" stuff +ChangeDiskTitle=ġ α׷ ũ ʿ +SelectDiskLabel2=ũ %1() [Ȯ] Ŭϼ.%n%n ũ Ʒ ǥõ ƴ ٸ ùٸ θ Էϰų [ãƺ] Ŭϼ. +PathLabel=(&P): +FileNotInDir2="%2" "%1"() ã ϴ. ùٸ ũ ϰų ٸ ϼ. +SelectDirectoryLabel= ũ ġ ϼ. +; *** Installation phase messages +SetupAborted=ġ Ϸ ߽ϴ.%n%n ذ ġ α׷ ٽ ϼ. +EntryAbortRetryIgnore=ٽ õϷ [ٽ õ], ׷ Ϸ [], ġ Ϸ [ߴ] Ŭϼ. +; *** Installation status messages +StatusClosingApplications= α׷ ݴ ... +StatusCreateDirs=͸ ... +StatusExtractFiles= ϴ ... +StatusCreateIcons=ٷ ⸦ ... +StatusCreateIniEntries=INI ׸ ... +StatusCreateRegistryEntries=Ʈ ׸ ... +StatusRegisterFiles= ϴ ... +StatusSavingUninstall= ϴ ... +StatusRunProgram=ġ Ϸϴ ... +StatusRestartingApplications= α׷ ٽ ϴ ... +StatusRollback= ѹϴ ... +; *** Misc. errors +ErrorInternal2= : %1 +ErrorFunctionFailedNoCode=%1 +ErrorFunctionFailed=%1 , ڵ %2 +ErrorFunctionFailedWithMessage=%1 , ڵ %2.%n%3 +ErrorExecutingProgram= :%n%1 +; *** Registry errors +ErrorRegOpenKey=Ʈ Ű ߻:%n%1\%2 +ErrorRegCreateKey=Ʈ Ű ߻:%n%1\%2 +ErrorRegWriteKey=Ʈ Ű ϴ ߻:%n%1\%2 +; *** INI errors +ErrorIniEntry= "%1" INI ׸ ߿ ߻߽ϴ. +; *** File copying errors +FileAbortRetryIgnore=ٽ õϷ [ٽ õ], dzʶٷ []( ), ġ Ϸ [ߴ] Ŭϼ. +FileAbortRetryIgnore2=ٽ õϷ [ٽ õ], ׷ Ϸ []( ), ġ Ϸ [ߴ] Ŭϼ. +SourceIsCorrupted= ջǾϴ. +SourceDoesntExist= "%1"() ϴ. +ExistingFileReadOnly= б ǥõǾ ֽϴ.%n%nб Ư ϰ ٽ õϷ [ٽ õ], dzʶٷ [], ġ Ϸ [ߴ] Ŭϼ. +ErrorReadingExistingDest= д ߻: +FileExists=ش ̹ ֽϴ.%n%nġ α׷  Ͻðڽϱ? +ExistingFileNewer= ġ α׷ ġϷ Ϻ ֽԴϴ. մϴ.%n%n Ͻðڽϱ? +ErrorChangingAttr= Ư ϴ ߻: +ErrorCreatingTemp= ͸ ߻: +ErrorReadingSource= д ߻: +ErrorCopying= ϴ ߻: +ErrorReplacingExistingFile= ٲٴ ߻: +ErrorRestartReplace=RestartReplace : +ErrorRenamingTemp= ͸ ִ ̸ ٲٴ ߻: +ErrorRegisterServer=DLL/OCX : %1 +ErrorRegSvr32Failed= ڵ %1() Բ RegSvr32 +ErrorRegisterTypeLib= ̺귯 : %1 +; *** Post-installation errors +ErrorOpeningReadme=README ߿ ߻߽ϴ. +ErrorRestartingComputer=ġ α׷ ǻ͸ ٽ ϴ. ϼ. +; *** Uninstaller messages +UninstallNotFound= "%1"() ϴ. ϴ. +UninstallOpenError= "%1"() ϴ. ϴ. +UninstallUnsupportedVer= α "%1"() α׷ ν ϴ Դϴ. ϴ. +UninstallUnknownEntry= α׿ ׸(%1) ߰ߵǾϴ. +ConfirmUninstall=%1() ش Ҹ Ͻðڽϱ? +UninstallOnlyOnWin64= ġ 64Ʈ Windows ֽϴ. +OnlyAdminCanUninstall= ġ ִ ڸ ֽϴ. +UninstallStatusLabel=ǻͿ %1() ϴ ٷ ּ. +UninstalledAll=ǻͿ %1() ߽ϴ. +UninstalledMost=%1 Ű ϷǾϴ.%n%nϺ Ҵ ϴ. ̷ ׸ ֽϴ. +UninstalledAndNeedsRestart=%1 Ÿ ϷϷ ǻ͸ ٽ ؾ մϴ.%n%n ٽ Ͻðڽϱ? +UninstallDataCorrupted="%1" ջǾϴ. ϴ. +; *** Uninstallation phase messages +ConfirmDeleteSharedFileTitle= Ͻðڽϱ? +ConfirmDeleteSharedFile2=ýۿ ϴ α׷ ǥõ˴ϴ. ۾ Ͻðڽϱ?%n%n ϴ α׷ ִµ ϸ ش α׷ ùٸ ۵ ֽϴ. 𸣴 [ƴϿ] ϼ. ýۿ ״ ξ ƹ ߻ ʽϴ. +SharedFileNameLabel= ̸: +SharedFileLocationLabel=ġ: +WizardUninstalling= +StatusUninstalling=%1() ϴ ... +; *** Shutdown block reasons +ShutdownBlockReasonInstallingApp=%1() ġϴ Դϴ. +ShutdownBlockReasonUninstallingApp=%1() ϴ Դϴ. +; The custom messages below aren't used by Setup itself, but if you make +; use of them in your scripts, you'll want to translate them. +[CustomMessages] +NameAndVersion=%1 %2 +AdditionalIcons=߰ ٷ : +CreateDesktopIcon= ȭ ٷ (&D) +CreateQuickLaunchIcon= ٷ (&Q) +ProgramOnTheWeb=%1 +UninstallProgram=%1 +LaunchProgram=%1 +AssocFileExtension=%1() %2 Ȯ (&A) +AssocingFileExtension=%1() %2 Ȯ ... +AutoStartProgramGroupDescription=: +AutoStartProgram=%1 ڵ +AddonHostProgramNotFound= %1() ã ϴ.%n%n׷ Ͻðڽϱ? \ No newline at end of file diff --git a/build/win32/i18n/Default.zh-cn.isl b/build/win32/i18n/Default.zh-cn.isl new file mode 100644 index 0000000000..e384e83d30 --- /dev/null +++ b/build/win32/i18n/Default.zh-cn.isl @@ -0,0 +1,298 @@ +; *** Inno Setup version 5.5.3+ Simplified Chinese messages *** +; +; To download user-contributed translations of this file, go to: +; http://www.jrsoftware.org/files/istrans/ +; +; Note: When translating this text, do not add periods (.) to the end of +; messages that didn't have them already, because on those messages Inno +; Setup adds the periods automatically (appending a period would result in +; two periods being displayed). +[LangOptions] +; The following three entries are very important. Be sure to read and +; understand the '[LangOptions] section' topic in the help file. +LanguageName=Simplified Chinese +LanguageID=$0804 +LanguageCodePage=936 +; If the language you are translating to requires special font faces or +; sizes, uncomment any of the following entries and change them accordingly. +;DialogFontName= +;DialogFontSize=8 +;WelcomeFontName=Verdana +;WelcomeFontSize=12 +;TitleFontName=Arial +;TitleFontSize=29 +;CopyrightFontName=Arial +;CopyrightFontSize=8 +[Messages] +; *** Application titles +SetupAppTitle=װ +SetupWindowTitle=װ - %1 +UninstallAppTitle=ж +UninstallAppFullTitle=%1 ж +; *** Misc. common +InformationTitle=Ϣ +ConfirmTitle=ȷ +ErrorTitle= +; *** SetupLdr messages +SetupLdrStartupMessage=⽫װ %1ǷҪ? +LdrCannotCreateTemp=޷ʱļװֹ +LdrCannotExecTemp=޷ʱĿ¼ִļװֹ +; *** Startup error messages +LastErrorMessage=%1%n%n %2: %3 +SetupFileMissing=װĿ¼ȱʧļ %1ȡ¸ +SetupFileCorrupt=װļ𻵡ȡó¸ +SetupFileCorruptOrWrongVer=װļ𻵻˰װ汾ݡȡó¸ +InvalidParameter= %n%n%1 ϴһЧ +SetupAlreadyRunning=װС +WindowsVersionNotSupported=˳֧е Windows 汾 +WindowsServicePackRequired=˳Ҫ %1 %2 ߰汾 +NotOnThisPlatform=˳򽫲 %1 С +OnlyOnThisPlatform=˳ %1 С +OnlyOnTheseArchitectures=˳ɰװΪ´ϵṹƵ Windows 汾:%n%n%1 +MissingWOW64APIs=е Windows 汾װִ 64 λװĹܡҪ⣬밲װ %1 +WinVersionTooLowError=˳Ҫ %1 汾 %2 ߰汾 +WinVersionTooHighError=˳ܰװ %1 汾 %2 ߵİ汾ϡ +AdminPrivilegesRequired=ڰװ˳ʱΪԱ¼ +PowerUserPrivilegesRequired=װ˳ʱԹԱ Power User Աݵ¼ +SetupAppRunningError=װ⵽ %1 ǰС%n%nرʵȻ󵥻ȷԼ򵥻ȡ˳ +UninstallAppRunningError=жؼ⵽ %1 ǰС%n%nرʵȻ󵥻ȷԼ򵥻ȡ˳ +; *** Misc. errors +ErrorCreatingDir=װ޷Ŀ¼%1 +ErrorTooManyFilesInDir=޷Ŀ¼%1дļΪ̫ļ +; *** Setup common messages +ExitSetupTitle=˳װ +ExitSetupMessage=װδɡ˳ᰲװó%n%nʱٴаװɰװ%n%nǷ˳װ? +AboutSetupMenuItem=ڰװ(&A)... +AboutSetupTitle=ڰװ +AboutSetupMessage=%1 汾 %2%n%3%n%n%1 ҳ:%n%4 +AboutSetupNote= +TranslatorNote= +; *** Buttons +ButtonBack=< һ(&B) +ButtonNext=һ(&N) > +ButtonInstall=װ(&I) +ButtonOK=ȷ +ButtonCancel=ȡ +ButtonYes=(&Y) +ButtonYesToAll=ȫ(&A) +ButtonNo=(&N) +ButtonNoToAll=ȫ(&O) +ButtonFinish=(&F) +ButtonBrowse=(&B)... +ButtonWizardBrowse=(&R)... +ButtonNewFolder=½ļ(&M) +; *** "Select Language" dialog messages +SelectLanguageTitle=ѡװ +SelectLanguageLabel=ѡװʱҪʹõ: +; *** Common wizard text +ClickNext=һԼ򵥻ȡ˳װ +BeveledLabel= +BrowseDialogTitle=ļ +BrowseDialogLabel=бѡһļУȻ󵥻ȷ +NewFolderName=½ļ +; *** "Welcome" wizard page +WelcomeLabel1=ӭʹ [name] װ +WelcomeLabel2=⽫ڼϰװ [name/ver]%n%nرӦóټ +; *** "Password" wizard page +WizardPassword= +PasswordLabel1=˰װ뱣 +PasswordLabel3=ṩ룬Ȼ󵥻һԼִСд +PasswordEditLabel=(&P): +IncorrectPassword=벻ȷԡ +; *** "License Agreement" wizard page +WizardLicense=Э +LicenseLabel=ڼǰĶҪϢ +LicenseLabel3=ĶЭ顣ܴЭſɼװ +LicenseAccepted=ҽЭ(&A) +LicenseNotAccepted=ҲЭ(&D) +; *** "Information" wizard pages +WizardInfoBefore=Ϣ +InfoBeforeLabel=ڼǰĶҪϢ +InfoBeforeClickLabel=׼üװ󣬵һ +WizardInfoAfter=Ϣ +InfoAfterLabel=ڼǰĶҪϢ +InfoAfterClickLabel=׼üװ󣬵һ +; *** "User Information" wizard page +WizardUserInfo=ûϢ +UserInfoDesc=Ϣ +UserInfoName=û(&U): +UserInfoOrg=֯(&O): +UserInfoSerial=к(&S): +UserInfoNameRequired=ơ +; *** "Select Destination Location" wizard page +WizardSelectDir=ѡĿλ +SelectDirDesc=Ӧ [name] װ? +SelectDirLabel3=װὫ [name] װļС +SelectDirBrowseLabel=ҪһѡļУ +DiskSpaceMBLabel=Ҫ [mb] MB ô̿ռ䡣 +CannotInstallToNetworkDrive=װ޷װ +CannotInstallToUNCPath=װ޷װ UNC · +InvalidPath=ŵ·(:%n%nC:\APP%n%n)¸ʽ UNC ·:%n%n\\server\share +InvalidDrive=ѡ UNC ڻ򲻿ɷʡѡ +DiskSpaceWarningTitle=̿ռ䲻 +DiskSpaceWarning=װҪ %1 KB ÿռװѡ %2 KB ÿռ䡣%n%nǷҪ? +DirNameTooLong=ļƻ·̫ +InvalidDirName=ļЧ +BadDirName32=ļܰһַ:%n%n%1 +DirExistsTitle=ļд +DirExists=ļ:%n%n%1%n%nѴڡǷҪװļ? +DirDoesntExistTitle=ļв +DirDoesntExist=ļ:%n%n%1%n%nڡǷҪļ? +; *** "Select Components" wizard page +WizardSelectComponents=ѡ +SelectComponentsDesc=ӦװЩ? +SelectComponentsLabel2=ѡϣװϣװ׼󵥻һԼ +FullInstallation=ȫװ +; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) +CompactInstallation=లװ +CustomInstallation=Զ尲װ +NoUninstallWarningTitle= +NoUninstallWarning=װ⵽Ѱװ:%n%n%1%n%nȡѡЩжǡ%n%nǷҪ? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceMBLabel=ǰѡҪ [mb] MB ̿ռ䡣 +; *** "Select Additional Tasks" wizard page +WizardSelectTasks=ѡ +SelectTasksDesc=ӦִЩ? +SelectTasksLabel2=ѡװ [name] ʱϣװִеȻ󵥻һ +; *** "Select Start Menu Folder" wizard page +WizardSelectProgramGroup=ѡʼ˵ļ +SelectStartMenuFolderDesc=װӦĿݷʽõ? +SelectStartMenuFolderLabel3=װ¿ʼ˵ļдóĿݷʽ +SelectStartMenuFolderBrowseLabel=ҪһѡļУ +MustEnterGroupName=ļ +GroupNameTooLong=ļƻ·̫ +InvalidGroupName=ļЧ +BadGroupName=ļܱһַ:%n%n%1 +NoProgramGroupCheck2=ʼ˵ļ(&D) +; *** "Ready to Install" wizard page +WizardReady=װ׼ +ReadyLabel1=װ׼ڼϰװ [name] +ReadyLabel2a=װԼװ鿴κ򵥻"" +ReadyLabel2b=װԼװ +ReadyMemoUserInfo=ûϢ: +ReadyMemoDir=Ŀλ: +ReadyMemoType=װ: +ReadyMemoComponents=ѡ: +ReadyMemoGroup=ʼ˵ļ: +ReadyMemoTasks=: +; *** "Preparing to Install" wizard page +WizardPreparing=׼װ +PreparingDesc=װ׼ڼϰװ [name] +PreviousInstallNotCompleted=һİװ/ɾδɡɸðװ%n%nаװ [name] İװ +CannotContinue=װ޷뵥"ȡ"˳ +ApplicationsFound=ӦóʹҪͨװиµļװԶرЩӦó +ApplicationsFound2=ӦóʹҪͨװиµļװԶرЩӦóɰװ󣬰װ򽫳Ӧó +CloseApplications=ԶرӦó(&A) +DontCloseApplications=رӦó(&D) +ErrorCloseApplications=װ޷ԶرӦó򡣽ڼ֮ǰȹرʹͨװиµļӦó +; *** "Installing" wizard page +WizardInstalling=ڰװ +InstallingLabel=װڼϰװ [name]Եȡ +; *** "Setup Completed" wizard page +FinishedHeadingLabel= [name] װ +FinishedLabelNoIcons=װڼɰװ [name] +FinishedLabel=װڼɰװ [name]ͨѡװĿݷʽӦó +ClickFinish=ɡ˳װ +FinishedRestartLabel=Ҫ [name] İװװǷҪ? +FinishedRestartMessage=Ҫ [name] İװװ%n%nǷҪ? +ShowReadmeCheck=ǣϣ鿴 README ļ +YesRadio=ǣ(&Y) +NoRadio=ҽԺ(&N) +; used for example as 'Run MyProg.exe' +RunEntryExec= %1 +; used for example as 'View Readme.txt' +RunEntryShellExec=鿴 %1 +; *** "Setup Needs the Next Disk" stuff +ChangeDiskTitle=װҪһ +SelectDiskLabel2= %1 ȷ%n%n˴ϵļļļҵȷ·򵥻 +PathLabel=·(&P): +FileNotInDir2=ڡ%2޷λļ%1ȷĴ̻ѡļС +SelectDirectoryLabel=ָһ̵λá +; *** Installation phase messages +SetupAborted=װδɡ%n%nⲢаװ +EntryAbortRetryIgnore=ԡٴγԣԡԼ򵥻ֹȡװ +; *** Installation status messages +StatusClosingApplications=ڹرӦó... +StatusCreateDirs=ڴĿ¼... +StatusExtractFiles=ڽѹļ... +StatusCreateIcons=ڴݷʽ... +StatusCreateIniEntries=ڴ INI ... +StatusCreateRegistryEntries=ڴע... +StatusRegisterFiles=עļ... +StatusSavingUninstall=ڱжϢ... +StatusRunProgram=ɰװ... +StatusRestartingApplications=Ӧó... +StatusRollback=ڻ˸... +; *** Misc. errors +ErrorInternal2=ڲ: %1 +ErrorFunctionFailedNoCode=%1 ʧ +ErrorFunctionFailed=%1 ʧܣ %2 +ErrorFunctionFailedWithMessage=%1 ʧܣ %2%n%3 +ErrorExecutingProgram=޷ִļ:%n%1 +; *** Registry errors +ErrorRegOpenKey=עʱ:%n%1\%2 +ErrorRegCreateKey=עʱ:%n%1\%2 +ErrorRegWriteKey=дעʱ:%n%1\%2 +; *** INI errors +ErrorIniEntry=ļ%1д INI ʱ +; *** File copying errors +FileAbortRetryIgnore=ԡٴβԡļ(˲)򵥻ֹȡװ +FileAbortRetryIgnore2=ԡٴβԡԼ(˲)򵥻ֹȡװ +SourceIsCorrupted=Դļ +SourceDoesntExist=Դļ%1 +ExistingFileReadOnly=ļΪֻ״̬%n%nԡɾֻԲԣԡļ򵥻ֹȡװ +ErrorReadingExistingDest=Զȡļʱ: +FileExists=ļѴڡ%n%nǷҪװ򸲸? +ExistingFileNewer=ļȰװ԰װļ¡鱣ļ%n%nǷҪļ? +ErrorChangingAttr=ԸļԳ: +ErrorCreatingTemp=ĿĿ¼ļʱ: +ErrorReadingSource=ԶȡԴļʱ: +ErrorCopying=Ըļʱ: +ErrorReplacingExistingFile=滻ļʱ: +ErrorRestartReplace=RestartReplace ʧ: +ErrorRenamingTemp=ĿĿ¼ļʱ: +ErrorRegisterServer=޷ע DLL/OCX: %1 +ErrorRegSvr32Failed=RegSvr32 ʧܣ˳Ϊ %1 +ErrorRegisterTypeLib=޷עͿ: %1 +; *** Post-installation errors +ErrorOpeningReadme=Դ README ļʱ +ErrorRestartingComputer=װ޷ִֶд˲ +; *** Uninstaller messages +UninstallNotFound=ļ%1ڡ޷װ +UninstallOpenError=޷ļ%1޷ж +UninstallUnsupportedVer=ж־%1ĸʽ޷˰汾жسʶ޷ж +UninstallUnknownEntry=ж־зδ֪Ŀ(%1) +ConfirmUninstall=ȷҪɾ %1 ͼȫ? +UninstallOnlyOnWin64= 64 λ Windows жش˰װ +OnlyAdminCanUninstall=йȨ޵ûſжش˰װ +UninstallStatusLabel=Ӽɾ %1Եȡ +UninstalledAll=ѳɹӼɾ %1 +UninstalledMost=%1 жɡ%n%n޷ɾһЩԪءɽֶɾ +UninstalledAndNeedsRestart=Ҫ %1 жأ%n%nǷҪ? +UninstallDataCorrupted=%1ļ𻵡޷ж +; *** Uninstallation phase messages +ConfirmDeleteSharedFileTitle=ɾļ? +ConfirmDeleteSharedFile2=ϵͳʾ¹ļٱκγʹáǷҪжɾ˹ļ?%n%nгʹôļɾܲСȷѡ񡰷񡱡ļסϵͳϲκ⡣ +SharedFileNameLabel=ļ: +SharedFileLocationLabel=λ: +WizardUninstalling=ж״̬ +StatusUninstalling=ж %1... +; *** Shutdown block reasons +ShutdownBlockReasonInstallingApp=ڰװ %1 +ShutdownBlockReasonUninstallingApp=ж %1 +; The custom messages below aren't used by Setup itself, but if you make +; use of them in your scripts, you'll want to translate them. +[CustomMessages] +NameAndVersion=%1 汾 %2 +AdditionalIcons=ݷʽ: +CreateDesktopIcon=ݷʽ(&D) +CreateQuickLaunchIcon=ݷʽ(&Q) +ProgramOnTheWeb=Web ϵ %1 +UninstallProgram=ж %1 +LaunchProgram= %1 +AssocFileExtension= %1 %2 ļչ(&A) +AssocingFileExtension= %1 %2 ļչ... +AutoStartProgramGroupDescription=: +AutoStartProgram=Զ %1 +AddonHostProgramNotFound=޷ѡļжλ %1%n%nǷҪ? \ No newline at end of file diff --git a/build/win32/i18n/Default.zh-tw.isl b/build/win32/i18n/Default.zh-tw.isl new file mode 100644 index 0000000000..4746349e19 --- /dev/null +++ b/build/win32/i18n/Default.zh-tw.isl @@ -0,0 +1,298 @@ +; *** Inno Setup version 5.5.3+ Traditional Chinese messages *** +; +; To download user-contributed translations of this file, go to: +; http://www.jrsoftware.org/files/istrans/ +; +; Note: When translating this text, do not add periods (.) to the end of +; messages that didn't have them already, because on those messages Inno +; Setup adds the periods automatically (appending a period would result in +; two periods being displayed). +[LangOptions] +; The following three entries are very important. Be sure to read and +; understand the '[LangOptions] section' topic in the help file. +LanguageName=Traditional Chinese +LanguageID=$0404 +LanguageCodePage=950 +; If the language you are translating to requires special font faces or +; sizes, uncomment any of the following entries and change them accordingly. +;DialogFontName= +;DialogFontSize=8 +;WelcomeFontName=Verdana +;WelcomeFontSize=12 +;TitleFontName=Arial +;TitleFontSize=29 +;CopyrightFontName=Arial +;CopyrightFontSize=8 +[Messages] +; *** Application titles +SetupAppTitle=w˵{ +SetupWindowTitle=w˵{ - %1 +UninstallAppTitle=Ѱw +UninstallAppFullTitle=%1 Ѱw +; *** Misc. common +InformationTitle=T +ConfirmTitle=T{ +ErrorTitle=~ +; *** SetupLdr messages +SetupLdrStartupMessage=o|w %1Cn~? +LdrCannotCreateTemp=Lkإ߼ȦsɡCwˤw +LdrCannotExecTemp=LkȦsؿɮסCwˤw +; *** Startup error messages +LastErrorMessage=%1C%n%n~ %2: %3 +SetupFileMissing=w˥ؿʤɮ %1CЭץDAέso{sƥC +SetupFileCorrupt=w˵{ɮפwlCЭsoӵ{ƥC +SetupFileCorruptOrWrongVer=w˵{ɮפwlAΤۮePw˵{CЭץDAέso{sƥC +InvalidParameter=bROCWǻFLĪѼ:%n%n%1 +SetupAlreadyRunning=w˵{wb椤C +WindowsVersionNotSupported={䴩qҰ檺 Windows C +WindowsServicePackRequired={ݭn %1 Service Pack %2 ΧsC +NotOnThisPlatform={|b %1 WC +OnlyOnThisPlatform={b %1 WC +OnlyOnTheseArchitectures={uiw˦bMUCBz[c]p Windows W:%n%n%1 +MissingWOW64APIs=z檺 Windows tw˵{ 64 줸w˩һݪ\CYnץDAЦw Service Pack %1C +WinVersionTooLowError={ݭn %1 %2 ΧsC +WinVersionTooHighError={Lkw˦b %1 %2 ΧsWC +AdminPrivilegesRequired=w˦{ɡAHtκ޲znJC +PowerUserPrivilegesRequired=zw˦{ɡAHtκ޲z Power Users sժnJC +SetupAppRunningError=wˮɰ %1 ثeb椤C%n%nХߧYҦCYn~AЫ@U [Tw]; YnAЫ@U []C +UninstallAppRunningError=Ѱwˮɰ %1 ثeb椤C%n%nХߧYҦCYn~AЫ@U [Tw]; YnAЫ@U []C +; *** Misc. errors +ErrorCreatingDir=w˵{Lkإߥؿ "%1" +ErrorTooManyFilesInDir=]ؿ "%1" ]tӦhɮסAҥHLkb䤤إɮ +; *** Setup common messages +ExitSetupTitle=w +ExitSetupMessage=w˥CYߧYAN|w˵{C%n%nziHyAw˵{ӧwˡC%n%nnw˶? +AboutSetupMenuItem=w˵{(&A)... +AboutSetupTitle=w˵{ +AboutSetupMessage=%1 %2%n%3%n%n%1 :%n%4 +AboutSetupNote= +TranslatorNote= +; *** Buttons +ButtonBack=< W@B(&B) +ButtonNext=U@B(&N) > +ButtonInstall=w(&I) +ButtonOK=Tw +ButtonCancel= +ButtonYes=O(&Y) +ButtonYesToAll=ҬO(&A) +ButtonNo=_(&N) +ButtonNoToAll=ҧ_(&O) +ButtonFinish=(&F) +ButtonBrowse=s(&B)... +ButtonWizardBrowse=s(&R)... +ButtonNewFolder=إ߷sƧ(&M) +; *** "Select Language" dialog messages +SelectLanguageTitle=w˵{y +SelectLanguageLabel=w˴ҭnϥΪy: +; *** Common wizard text +ClickNext=Yn~AЫ@U [U@B]; YnwˡAЫ@U []C +BeveledLabel= +BrowseDialogTitle=sƧ +BrowseDialogLabel=бqUCM椤ƧAM@U [Tw]C +NewFolderName=sWƧ +; *** "Welcome" wizard page +WelcomeLabel1=wϥ [name] w˺F +WelcomeLabel2=o|bzqWw [name/ver]C%n%nijzҦLε{AMA~C +; *** "Password" wizard page +WizardPassword=KX +PasswordLabel1=w˨KXO@C +PasswordLabel3=дѱKXAM@U [U@B] H~CKXϤjpgC +PasswordEditLabel=KX(&P): +IncorrectPassword=JKXTCЦAդ@C +; *** "License Agreement" wizard page +WizardLicense=vX +LicenseLabel=Х\ŪUCnTA~C +LicenseLabel3=о\ŪUCvXCzXڡA~~wˡC +LicenseAccepted=ڱX(&A) +LicenseNotAccepted=ڤX(&D) +; *** "Information" wizard pages +WizardInfoBefore=T +InfoBeforeLabel=Х\ŪUCnTA~C +InfoBeforeClickLabel=zdzƦnn~wˮɡAЫ@U [U@B]C +WizardInfoAfter=T +InfoAfterLabel=Х\ŪUCnTA~C +InfoAfterClickLabel=zdzƦnn~wˮɡAЫ@U [U@B]C +; *** "User Information" wizard page +WizardUserInfo=ϥΪ̸T +UserInfoDesc=пJzTC +UserInfoName=ϥΪ̦W(&U): +UserInfoOrg=´(&O): +UserInfoSerial=Ǹ(&S): +UserInfoNameRequired=JW١C +; *** "Select Destination Location" wizard page +WizardSelectDir=تam +SelectDirDesc=N [name] w˦bB? +SelectDirLabel3=w˵{|N [name] w˦bUCƧC +SelectDirBrowseLabel=Yn~AЫ@U [U@B]CYzQPƧAЫ@U [s]C +DiskSpaceMBLabel=ܤֶ [mb] MB iκϺЪŶC +CannotInstallToNetworkDrive=w˵{Lkw˨ϺоC +CannotInstallToUNCPath=w˵{Lkw˨ UNC |C +InvalidPath=J]tϺоN|AҦp:%n%nC:\APP%n%nοJUC榡 UNC |:%n%n\\A\@ +InvalidDrive=Ϻо UNC @ΤsbεLksCпLϺо UNC @ΡC +DiskSpaceWarningTitle=ϺЪŶ +DiskSpaceWarning=w˵{ܤֻݭn %1 KB iΪŶ~wˡAҿϺоiΪŶu %2 KBC%n%nn~? +DirNameTooLong=ƧW٩θ|LC +InvalidDirName=ƧWٵLġC +BadDirName32=ƧW٤o]tUC@r:%n%n%1 +DirExistsTitle=Ƨwsb +DirExists=wƧ %n%n%1%n%nCnw˨ӸƧ? +DirDoesntExistTitle=Ƨsb +DirDoesntExist=Ƨ %n%n%1%n%n sbCnإ߸ӸƧ? +; *** "Select Components" wizard page +WizardSelectComponents= +SelectComponentsDesc=w˭Ǥ? +SelectComponentsLabel2=znw˪; Mznw˪CzdzƦnn~ɡAЫ@U [U@B]C +FullInstallation=w +; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) +CompactInstallation=²w +CustomInstallation=ۭqw +NoUninstallWarningTitle=w +NoUninstallWarning=w˵{zqwwˤFUC:%n%n%1%n%nNoǤä|ϤѰwˡC%n%nn~? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceMBLabel=ثeܦܤֻݭn [mb] MB ϺЪŶC +; *** "Select Additional Tasks" wizard page +WizardSelectTasks=Lu@ +SelectTasksDesc=ٶǨLu@? +SelectTasksLabel2=пw˵{bw [name] ɡAB~檺Lu@AM@U [U@B]C +; *** "Select Start Menu Folder" wizard page +WizardSelectProgramGroup= [}l] \Ƨ +SelectStartMenuFolderDesc=w˵{N{|mB? +SelectStartMenuFolderLabel3=w˵{NbUC [}l] \Ƨإߵ{|C +SelectStartMenuFolderBrowseLabel=Yn~AЫ@U [U@B]CYzQPƧAЫ@U [s]C +MustEnterGroupName=JƧW١C +GroupNameTooLong=ƧW٩θ|LC +InvalidGroupName=ƧWٵLġC +BadGroupName=ƧW٤o]tUC@r:%n%n%1 +NoProgramGroupCheck2=nإ [}l] \Ƨ(&D) +; *** "Ready to Install" wizard page +WizardReady=wi}lw +ReadyLabel1=w˵{{bwi}lN [name] w˨zqWC +ReadyLabel2a=Yn~wˡAЫ@U [w]; Yn˾\ܧ]wAЫ@U [W@B]C +ReadyLabel2b=Yn~wˡAЫ@U [w]C +ReadyMemoUserInfo=ϥΪ̸T: +ReadyMemoDir=تam: +ReadyMemoType=w: +ReadyMemoComponents=: +ReadyMemoGroup=[}l] \Ƨ: +ReadyMemoTasks=Lu@: +; *** "Preparing to Install" wizard page +WizardPreparing=bdzƦw +PreparingDesc=w˵{bdzƱN [name] w˨zqWC +PreviousInstallNotCompleted=W@ӵ{w/|CsҰʹqA~৹ӦwˡC%n%nЦbsҰʹqAsw˵{AH [name] wˡC +CannotContinue=w˵{Lk~CЫ@U [] HC +ApplicationsFound=w˵{sUCε{bϥΪ@ɮסCijz\w˵{۰oε{C +ApplicationsFound2=w˵{sUCε{bϥΪ@ɮסCijz\w˵{۰oε{Cw˧Aw˵{N|խsҰʳoε{C +CloseApplications=۰ε{(&A) +DontCloseApplications=nε{(&D) +ErrorCloseApplications=w˵{Lk۰Ҧε{CijzҦbϥΦw˵{sɮתε{AMA~C +; *** "Installing" wizard page +WizardInstalling=wˤ +InstallingLabel=еyԡAw˵{bN [name] w˨zqWC +; *** "Setup Completed" wizard page +FinishedHeadingLabel=b [name] w˺F +FinishedLabelNoIcons=w˵{wzqW [name] wˡC +FinishedLabel=w˵{wzqW [name] wˡCziHҦw˪|ӱҰε{C +ClickFinish=Ы@U []AHwˡC +FinishedRestartLabel=w˵{sҰʱzqA~৹ [name] wˡCnߧYsҰʶ? +FinishedRestartMessage=w˵{sҰʱzqA~৹ [name] wˡC%n%nnߧYsҰʶ? +ShowReadmeCheck=OAڭn˵Ūɮ +YesRadio=OAߧYsҰʹq(&Y) +NoRadio=_AyԦAsҰʹq(&N) +; used for example as 'Run MyProg.exe' +RunEntryExec= %1 +; used for example as 'View Readme.txt' +RunEntryShellExec=˵ %1 +; *** "Setup Needs the Next Disk" stuff +ChangeDiskTitle=w˵{ݭnU@iϤC +SelectDiskLabel2=дJϤ %1AM@U [Tw]C%n%nYϤWɮץiHbUCܤƧH~ƧAпJT|AΫ@U [s]C +PathLabel=|(&P): +FileNotInDir2=b "%2" 䤣ɮ "%1"CдJTϤAοLƧC +SelectDirectoryLabel=ЫwU@iϤmC +; *** Installation phase messages +SetupAborted=w˥wC%n%nЭץDAAsw˵{C +EntryAbortRetryIgnore=YnAդ@AЫ@U []; Yn~AЫ@U []; YnwˡAЫ@U []C +; *** Installation status messages +StatusClosingApplications=bε{... +StatusCreateDirs=bإߥؿ... +StatusExtractFiles=bYɮ... +StatusCreateIcons=bإ߱|... +StatusCreateIniEntries=bإ INI ... +StatusCreateRegistryEntries=bإߵn... +StatusRegisterFiles=bnɮ... +StatusSavingUninstall=bxsѰw˸T... +StatusRunProgram=bw... +StatusRestartingApplications=bsҰε{... +StatusRollback=b_ܧ... +; *** Misc. errors +ErrorInternal2=~: %1 +ErrorFunctionFailedNoCode=%1 +ErrorFunctionFailed=%1 ; NX %2 +ErrorFunctionFailedWithMessage=%1 ; NX %2C%n%3 +ErrorExecutingProgram=Lkɮ:%n%1 +; *** Registry errors +ErrorRegOpenKey=}ҵnXɵoͿ~:%n%1\%2 +ErrorRegCreateKey=إߵnXɵoͿ~:%n%1\%2 +ErrorRegWriteKey=gJnXɵoͿ~:%n%1\%2 +; *** INI errors +ErrorIniEntry=bɮ "%1" إ INI خɵoͿ~C +; *** File copying errors +FileAbortRetryIgnore=YnAդ@AЫ@U []; YnLɮסAЫ@U [] (ijϥ); YnwˡAЫ@U []C +FileAbortRetryIgnore2=YnAդ@AЫ@U []; Yn~AЫ@U [] (ijϥ); YnwˡAЫ@U []C +SourceIsCorrupted=l{ɤwl +SourceDoesntExist=l{ "%1" sb +ExistingFileReadOnly={ɮפwаOŪC%n%nYnŪݩʡAMAդ@AЫ@U []; YnLɮסAЫ@U []; YnwˡAЫ@U []C +ErrorReadingExistingDest=Ū{ɮ׮ɵoͿ~: +FileExists=wɮסC%n%nnѦw˵{[Hмg? +ExistingFileNewer={ɮ׸w˵{զw˪ɮ׷sCijzOd{ɮסC%n%nnOd{ɮ׶? +ErrorChangingAttr=ܧ{ɮתݩʮɵoͿ~: +ErrorCreatingTemp=զbتaؿإɮ׮ɵoͿ~: +ErrorReadingSource=Ūl{ɮɵoͿ~: +ErrorCopying=սƻsɮ׮ɵoͿ~: +ErrorReplacingExistingFile=ըN{ɮ׮ɵoͿ~: +ErrorRestartReplace=RestartReplace : +ErrorRenamingTemp=խsRWتaؿɮ׮ɵoͿ~: +ErrorRegisterServer=Lkn DLL/OCX: %1 +ErrorRegSvr32Failed=RegSvr32 ѡANX %1 +ErrorRegisterTypeLib=Lkn{w: %1 +; *** Post-installation errors +ErrorOpeningReadme=ն}Ūɮ׮ɵoͿ~C +ErrorRestartingComputer=w˵{LksҰʹqCФʰ榹@~C +; *** Uninstaller messages +UninstallNotFound=Sɮ "%1"CLkѰwˡC +UninstallOpenError=Lk}ɮ "%1"CLkѰw +UninstallUnsupportedVer=Ѱw˵{LkѸѰw˰O "%1" 榡CLkѰw +UninstallUnknownEntry=bѰw˰O줣 (%1) +ConfirmUninstall=Twn %1 ΨҦ? +UninstallOnlyOnWin64=uib 64 줸 Windows WѰw˦wˡC +OnlyAdminCanUninstall=uƨtκ޲zvϥΪ̡A~Ѱw˦wˡC +UninstallStatusLabel=bqzq %1AеyԡC +UninstalledAll=w\qzq %1C +UninstalledMost=Ѱw %1 wC%n%nصLkCziHʥ[HC +UninstalledAndNeedsRestart=Yn %1 ѰwˡAsҰʱzqC%n%nnߧYsҰʶ? +UninstallDataCorrupted="%1" ɮפwlCLkѰw +; *** Uninstallation phase messages +ConfirmDeleteSharedFileTitle=n@ɮ׶? +ConfirmDeleteSharedFile2=tΫXwL{bϥΤUC@ɮסCznѰwˡAH@ɮ׶?%n%np{bϥΦɮצӱNɮײAoǵ{iLk`B@CYTwAп [_]CNɮ׫OdbtΤWä|y󤣨}vTC +SharedFileNameLabel=ɮצW: +SharedFileLocationLabel=m: +WizardUninstalling=Ѱw˪A +StatusUninstalling=bѰw %1... +; *** Shutdown block reasons +ShutdownBlockReasonInstallingApp=bw %1C +ShutdownBlockReasonUninstallingApp=bѰw %1C +; The custom messages below aren't used by Setup itself, but if you make +; use of them in your scripts, you'll want to translate them. +[CustomMessages] +NameAndVersion=%1 %2 +AdditionalIcons=L|: +CreateDesktopIcon=إ߮ୱ|(&D) +CreateQuickLaunchIcon=إߧֳtҰʱ|(&Q) +ProgramOnTheWeb=Web W %1 +UninstallProgram=Ѱw %1 +LaunchProgram=Ұ %1 +AssocFileExtension=p %1 P %2 ɦW(&A) +AssocingFileExtension=bإ %1 P %2 ɦWpK +AutoStartProgramGroupDescription=Ұ: +AutoStartProgram=۰ʱҰ %1 +AddonHostProgramNotFound=bƧ䤣 %1C%n%nn~? \ No newline at end of file diff --git a/build/win32/i18n/messages.de.isl b/build/win32/i18n/messages.de.isl new file mode 100644 index 0000000000..755652bcdb --- /dev/null +++ b/build/win32/i18n/messages.de.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles=Aktion "Mit %1 ffnen" dem Dateikontextmen von Windows-Explorer hinzufgen +AddContextMenuFolders=Aktion "Mit %1 ffnen" dem Verzeichniskontextmen von Windows-Explorer hinzufgen +AssociateWithFiles=%1 als Editor fr untersttzte Dateitypen registrieren +AddToPath=Zu PATH hinzufgen (nach dem Neustart verfgbar) +RunAfter=%1 nach der Installation ausfhren +Other=Andere: +SourceFile=%1-Quelldatei \ No newline at end of file diff --git a/build/win32/i18n/messages.en.isl b/build/win32/i18n/messages.en.isl new file mode 100644 index 0000000000..4bd6c75ad4 --- /dev/null +++ b/build/win32/i18n/messages.en.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles=Add "Open with %1" action to Windows Explorer file context menu +AddContextMenuFolders=Add "Open with %1" action to Windows Explorer directory context menu +AssociateWithFiles=Register %1 as an editor for supported file types +AddToPath=Add to PATH (available after restart) +RunAfter=Run %1 after installation +Other=Other: +SourceFile=%1 Source File \ No newline at end of file diff --git a/build/win32/i18n/messages.es.isl b/build/win32/i18n/messages.es.isl new file mode 100644 index 0000000000..e8d5af64b6 --- /dev/null +++ b/build/win32/i18n/messages.es.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles=Agregar la accin "Abrir con %1" al men contextual de archivo del Explorador de Windows +AddContextMenuFolders=Agregar la accin "Abrir con %1" al men contextual de directorio del Explorador de Windows +AssociateWithFiles=Registrar %1 como editor para tipos de archivo admitidos +AddToPath=Agregar a PATH (disponible despus de reiniciar) +RunAfter=Ejecutar %1 despus de la instalacin +Other=Otros: +SourceFile=Archivo de origen %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.fr.isl b/build/win32/i18n/messages.fr.isl new file mode 100644 index 0000000000..3e17036f80 --- /dev/null +++ b/build/win32/i18n/messages.fr.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles=Ajouter l'action "Ouvrir avec %1" au menu contextuel de fichier de l'Explorateur Windows +AddContextMenuFolders=Ajouter l'action "Ouvrir avec %1" au menu contextuel de rpertoire de l'Explorateur Windows +AssociateWithFiles=Inscrire %1 en tant qu'diteur pour les types de fichier pris en charge +AddToPath=Ajouter PATH (disponible aprs le redmarrage) +RunAfter=Excuter %1 aprs l'installation +Other=Autre: +SourceFile=Fichier source %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.hu.isl b/build/win32/i18n/messages.hu.isl new file mode 100644 index 0000000000..f141ad1b24 --- /dev/null +++ b/build/win32/i18n/messages.hu.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles="Megnyits a kvetkezvel: %1" parancs hozzadsa a fjlok helyi menjhez a Windows Intzben +AddContextMenuFolders="Megnyits a kvetkezvel: %1" parancs hozzadsa a mappk helyi menjhez a Windows Intzben +AssociateWithFiles=%1 regisztrlsa szerkesztknt a tmogatott fjltpusokhoz +AddToPath=Hozzads a PATH-hoz (jraindts utn lesz elrhet) +RunAfter=%1 indtsa a telepts utn +Other=Egyb: +SourceFile=%1 forrsfjl \ No newline at end of file diff --git a/build/win32/i18n/messages.it.isl b/build/win32/i18n/messages.it.isl new file mode 100644 index 0000000000..7ad8a0622d --- /dev/null +++ b/build/win32/i18n/messages.it.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles=Aggiungi azione "Apri con %1" al menu di scelta rapida file di Esplora risorse +AddContextMenuFolders=Aggiungi azione "Apri con %1" al menu di scelta rapida directory di Esplora risorse +AssociateWithFiles=Registra %1 come editor per i tipi di file supportati +AddToPath=Aggiungi a PATH (disponibile dopo il riavvio) +RunAfter=Esegui %1 dopo l'installazione +Other=Altro: +SourceFile=File di origine %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.ja.isl b/build/win32/i18n/messages.ja.isl new file mode 100644 index 0000000000..8176c4ffba --- /dev/null +++ b/build/win32/i18n/messages.ja.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles=GNXv[[̃t@C ReLXg j[ [%1 ŊJ] ANVlj +AddContextMenuFolders=GNXv[[̃fBNg ReLXg j[ [%1 ŊJ] ANVlj +AssociateWithFiles=T|[gĂt@C̎ނ̃GfB^[ƂāA%1 o^ +AddToPath=PATH ւ̒lj (ċNɎgp”\ɂȂ) +RunAfter=CXg[ %1 s +Other=̑: +SourceFile=%1 \[X t@C \ No newline at end of file diff --git a/build/win32/i18n/messages.ko.isl b/build/win32/i18n/messages.ko.isl new file mode 100644 index 0000000000..28860c36b6 --- /dev/null +++ b/build/win32/i18n/messages.ko.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles="%1() " ۾ Windows Ž Ȳ ´ ޴ ߰ +AddContextMenuFolders="%1() " ۾ Windows Ž ͸ Ȳ ´ ޴ ߰ +AssociateWithFiles=%1() Ǵ Ŀ մϴ. +AddToPath=PATH ߰(ٽ ) +RunAfter=ġ %1 +Other=Ÿ: +SourceFile=%1 \ No newline at end of file diff --git a/build/win32/i18n/messages.pt-br.isl b/build/win32/i18n/messages.pt-br.isl new file mode 100644 index 0000000000..d534637f8b --- /dev/null +++ b/build/win32/i18n/messages.pt-br.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles=Adicione a ao "Abrir com %1" ao menu de contexto de arquivo do Windows Explorer +AddContextMenuFolders=Adicione a ao "Abrir com %1" ao menu de contexto de diretrio do Windows Explorer +AssociateWithFiles=Registre %1 como um editor para tipos de arquivos suportados +AddToPath=Adicione em PATH (disponvel aps reiniciar) +RunAfter=Executar %1 aps a instalao +Other=Outros: +SourceFile=Arquivo Fonte %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.ru.isl b/build/win32/i18n/messages.ru.isl new file mode 100644 index 0000000000..4d83466362 --- /dev/null +++ b/build/win32/i18n/messages.ru.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles= " %1" Windows +AddContextMenuFolders= " %1" +AssociateWithFiles= %1 +AddToPath= PATH ( ) +RunAfter= %1 +Other=: +SourceFile= %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.tr.isl b/build/win32/i18n/messages.tr.isl new file mode 100644 index 0000000000..dc241d924c --- /dev/null +++ b/build/win32/i18n/messages.tr.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles=Windows Gezgini balam mensne "%1 le A" eylemini ekle +AddContextMenuFolders=Windows Gezgini dizin balam mensne "%1 le A" eylemini ekle +AssociateWithFiles=%1 uygulamasn desteklenen dosya trleri iin bir dzenleyici olarak kayt et +AddToPath=PATH'e ekle (yeniden balattktan sonra kullanlabilir) +RunAfter=Kurulumdan sonra %1 uygulamasn altr. +Other=Dier: +SourceFile=%1 Kaynak Dosyas \ No newline at end of file diff --git a/build/win32/i18n/messages.zh-cn.isl b/build/win32/i18n/messages.zh-cn.isl new file mode 100644 index 0000000000..349fc2ccc2 --- /dev/null +++ b/build/win32/i18n/messages.zh-cn.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles=ͨ %1 򿪡ӵ Windows ԴļIJ˵ +AddContextMenuFolders=ͨ %1 򿪡ӵ Windows ԴĿ¼IJ˵ +AssociateWithFiles= %1 עΪֵ֧ļ͵ı༭ +AddToPath=ӵ PATH (Ч) +RunAfter=װ %1 +Other=: +SourceFile=%1 Դļ \ No newline at end of file diff --git a/build/win32/i18n/messages.zh-tw.isl b/build/win32/i18n/messages.zh-tw.isl new file mode 100644 index 0000000000..7c3f84aa13 --- /dev/null +++ b/build/win32/i18n/messages.zh-tw.isl @@ -0,0 +1,8 @@ +[CustomMessages] +AddContextMenuFiles=N [H %1 }] ʧ@[J Windows ɮ`ɮתާ@\ +AddContextMenuFolders=N [H %1 }] ʧ@[J Windows ɮ`ޥؿާ@\ +AssociateWithFiles=w䴩ɮN %1 Us边 +AddToPath=[J PATH (sҰʫͮ) +RunAfter=w˫ %1 +Other=L: +SourceFile=%1 ӷɮ \ No newline at end of file diff --git a/dataprotocol-node/.gitignore b/dataprotocol-node/.gitignore new file mode 100644 index 0000000000..829e00dc84 --- /dev/null +++ b/dataprotocol-node/.gitignore @@ -0,0 +1,29 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release +lib/ +out/ + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- +node_modules + +# Debug log from npm +npm-debug.log \ No newline at end of file diff --git a/dataprotocol-node/.travis.yml b/dataprotocol-node/.travis.yml new file mode 100644 index 0000000000..e1d8167704 --- /dev/null +++ b/dataprotocol-node/.travis.yml @@ -0,0 +1,13 @@ +language: node_js +sudo: false + +node_js: + - "0.12" + +install: + - cd jsonrpc && npm install && cd ../client && mkdir node_modules && cd node_modules && ln -s ../../jsonrpc vscode-jsonrpc && cd .. && npm install && cd ../server && mkdir node_modules && cd node_modules && ln -s ../../jsonrpc vscode-jsonrpc && cd .. && npm install && cd .. + - cd types && npm install && cd ../client && cd node_modules && ln -s ../../types vscode-languageserver-types && cd .. && npm install && cd ../server && cd node_modules && ln -s ../../types vscode-languageserver-types && cd .. && npm install && cd .. + +script: + - cd jsonrpc && npm run compile && cd ../types && npm run compile && cd ../client && npm run compile && cd ../server && npm run compile && cd .. + - cd jsonrpc && npm test && cd ../types && npm test && cd .. \ No newline at end of file diff --git a/dataprotocol-node/README.md b/dataprotocol-node/README.md new file mode 100644 index 0000000000..dbdefe4bf6 --- /dev/null +++ b/dataprotocol-node/README.md @@ -0,0 +1,4 @@ +# Microsoft Data Management Protocol - Node + +## License +[Source EULA](https://github.com/Microsoft/sqlopsstudio/blob/dev/license.txt) diff --git a/dataprotocol-node/client/.eslintrc b/dataprotocol-node/client/.eslintrc new file mode 100644 index 0000000000..306df0be07 --- /dev/null +++ b/dataprotocol-node/client/.eslintrc @@ -0,0 +1,24 @@ +{ + "rules": { + "indent": [ + 2, + "tab" + ], + "quotes": [ + 2, + "single" + ], + "linebreak-style": [ + 2, + "windows" + ], + "semi": [ + 2, + "always" + ] + }, + "env": { + "node": true + }, + "extends": "eslint:recommended" +} \ No newline at end of file diff --git a/dataprotocol-node/client/.npmignore b/dataprotocol-node/client/.npmignore new file mode 100644 index 0000000000..2b1e5b19e1 --- /dev/null +++ b/dataprotocol-node/client/.npmignore @@ -0,0 +1,9 @@ +.vscode/ +lib/test/ +lib/*.map +src/ +test/ +.eslintrc +.gitignore +gulpfile.js +tsd.json \ No newline at end of file diff --git a/dataprotocol-node/client/.vscode/launch.json b/dataprotocol-node/client/.vscode/launch.json new file mode 100644 index 0000000000..19f1d6dac6 --- /dev/null +++ b/dataprotocol-node/client/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + "version": "0.1.0", + // List of configurations. Add new configurations or edit existing ones. + // ONLY "node" and "mono" are supported, change "type" to switch. + "configurations": [ + { + "request": "launch", + // Name of configuration; appears in the launch configuration drop down menu. + "name": "Mocha", + // Type of configuration. Possible values: "node", "mono". + "type": "node", + // Workspace relative or absolute path to the program. + "program": "node_modules/mocha/bin/_mocha", + // Automatically stop program after launch. + "stopOnEntry": false, + // Command line arguments passed to the program. + "args": ["--timeout", "999999"], + // Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace. + "cwd": ".", + // Workspace relative or absolute path to the runtime executable to be used. Default is the runtime executable on the PATH. + "runtimeExecutable": null, + // Optional arguments passed to the runtime executable. + "runtimeArgs": [], + // Environment variables passed to the program. + "env": { }, + // Use JavaScript source maps (if they exist). + "sourceMaps": true, + // If JavaScript source maps are enabled, the generated code is expected in this directory. + "outDir": "lib" + } + ] +} diff --git a/dataprotocol-node/client/.vscode/settings.json b/dataprotocol-node/client/.vscode/settings.json new file mode 100644 index 0000000000..de8ee93ce0 --- /dev/null +++ b/dataprotocol-node/client/.vscode/settings.json @@ -0,0 +1,11 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "javascript.validate.enable": false, + "files.trimTrailingWhitespace": true, + "eslint.enable": false, + "editor.insertSpaces": false, + "editor.tabSize": 4, + "tslint.enable": false, + "typescript.tsdk": "./node_modules/typescript/lib", + "typescript.tsserver.trace": "off" +} \ No newline at end of file diff --git a/dataprotocol-node/client/.vscode/tasks.json b/dataprotocol-node/client/.vscode/tasks.json new file mode 100644 index 0000000000..0ed84877c1 --- /dev/null +++ b/dataprotocol-node/client/.vscode/tasks.json @@ -0,0 +1,9 @@ +{ + "version": "0.1.0", + "command": "npm", + "isShellCommand": true, + "args": ["run", "watch"], + "showOutput": "silent", + "isWatching": true, + "problemMatcher": "$tsc-watch" +} \ No newline at end of file diff --git a/dataprotocol-node/client/README.md b/dataprotocol-node/client/README.md new file mode 100644 index 0000000000..dbdefe4bf6 --- /dev/null +++ b/dataprotocol-node/client/README.md @@ -0,0 +1,4 @@ +# Microsoft Data Management Protocol - Node + +## License +[Source EULA](https://github.com/Microsoft/sqlopsstudio/blob/dev/license.txt) diff --git a/dataprotocol-node/client/package.json b/dataprotocol-node/client/package.json new file mode 100644 index 0000000000..2d8865a3c4 --- /dev/null +++ b/dataprotocol-node/client/package.json @@ -0,0 +1,32 @@ +{ + "name": "dataprotocol-client", + "description": "VSCode Language client implementation", + "version": "2.6.3", + "author": "Microsoft Corporation", + "license": "MIT", + "engines": { + "vscode": "^1.5.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/vscode-languageserver-node.git" + }, + "bugs": { + "url": "https://github.com/Microsoft/vscode-languageserver-node/issues" + }, + "main": "./lib/main.js", + "typings": "./lib/main", + "devDependencies": { + "typescript": "2.0.3" + }, + "dependencies": { + "dataprotocol-jsonrpc": "file:../jsonrpc", + "dataprotocol-languageserver-types": "file:../types" + }, + "scripts": { + "prepublish": "tsc -p ./src", + "compile": "tsc -p ./src", + "watch": "tsc -w -p ./src", + "update-vscode": "node ./node_modules/vscode/bin/install" + } +} diff --git a/dataprotocol-node/client/src/codeConverter.ts b/dataprotocol-node/client/src/codeConverter.ts new file mode 100644 index 0000000000..1a641104c7 --- /dev/null +++ b/dataprotocol-node/client/src/codeConverter.ts @@ -0,0 +1,526 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import * as code from 'vscode'; +import * as data from 'data'; +import * as ls from 'dataprotocol-languageserver-types'; +import * as proto from './protocol'; +import * as is from './utils/is'; +import ProtocolCompletionItem from './protocolCompletionItem'; +import ProtocolCodeLens from './protocolCodeLens'; +import os = require('os'); +import path = require('path'); + +export interface Converter { + + asUri(uri: code.Uri): string; + + asTextDocumentIdentifier(textDocument: code.TextDocument): ls.TextDocumentIdentifier; + + asOpenTextDocumentParams(textDocument: code.TextDocument): proto.DidOpenTextDocumentParams; + + asChangeTextDocumentParams(textDocument: code.TextDocument): proto.DidChangeTextDocumentParams; + asChangeTextDocumentParams(event: code.TextDocumentChangeEvent): proto.DidChangeTextDocumentParams; + + asCloseTextDocumentParams(textDocument: code.TextDocument): proto.DidCloseTextDocumentParams; + + asSaveTextDocumentParams(textDocument: code.TextDocument): proto.DidSaveTextDocumentParams; + + asTextDocumentPositionParams(textDocument: code.TextDocument, position: code.Position): proto.TextDocumentPositionParams; + + asWorkerPosition(position: code.Position): ls.Position; + + asRange(value: code.Range): ls.Range; + + asPosition(value: code.Position): ls.Position; + + asDiagnosticSeverity(value: code.DiagnosticSeverity): ls.DiagnosticSeverity; + + asDiagnostic(item: code.Diagnostic): ls.Diagnostic; + asDiagnostics(items: code.Diagnostic[]): ls.Diagnostic[]; + + asCompletionItem(item: code.CompletionItem): ls.CompletionItem; + + asTextEdit(edit: code.TextEdit): ls.TextEdit; + + asReferenceParams(textDocument: code.TextDocument, position: code.Position, options: { includeDeclaration: boolean; }): proto.ReferenceParams; + + asCodeActionContext(context: code.CodeActionContext): ls.CodeActionContext; + + asCommand(item: code.Command): ls.Command; + + asCodeLens(item: code.CodeLens): ls.CodeLens; + + asFormattingOptions(item: code.FormattingOptions): ls.FormattingOptions; + + asDocumentSymbolParams(textDocument: code.TextDocument): proto.DocumentSymbolParams; + + asCodeLensParams(textDocument: code.TextDocument): proto.CodeLensParams; + + asDocumentLink(item: code.DocumentLink): ls.DocumentLink; + + asDocumentLinkParams(textDocument: code.TextDocument): proto.DocumentLinkParams; + + asConnectionParams(connectionUri: string, connectionInfo: data.ConnectionInfo): proto.ConnectParams; + + asCapabilitiesParams(client: data.DataProtocolClientCapabilities): proto.CapabiltiesDiscoveryParams; + + asMetadataQueryParams(connectionUri: string): ls.MetadataQueryParams; + + asListDatabasesParams(connectionUri: string): proto.ListDatabasesParams; + + asTableMetadataParams(connectionUri: string, metadata: data.ObjectMetadata): proto.TableMetadataParams; + + asScriptingParams(connectionUri: string, operation: ls.ScriptOperation, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): ls.ScriptingParams; + + asConnectionDetail(connInfo: data.ConnectionInfo): ls.ConnectionDetails; + + asExpandInfo(nodeInfo: data.ExpandNodeInfo): ls.ExpandParams; + + asCloseSessionInfo(nodeInfo: data.ObjectExplorerCloseSessionInfo): ls.CloseSessionParams; + + asExecutionPlanOptions(planOptions: data.ExecutionPlanOptions): proto.ExecutionPlanOptions; + + asListTasksParams(params: data.ListTasksParams): ls.ListTasksParams; + + asCancelTaskParams(params: data.CancelTaskParams): ls.CancelTaskParams; + + asRestoreParams(ownerUri: string, params: data.RestoreInfo): ls.RestoreParams; + + asRestoreConfigInfoParams(ownerUri: string): ls.RestoreConfigInfoRequestParams; +} + +export interface URIConverter { + (value: code.Uri): string; +} + +export function createConverter(uriConverter?: URIConverter): Converter { + + const nullConverter = (value: code.Uri) => value.toString(); + + const _uriConverter: URIConverter = uriConverter || nullConverter; + + function asUri(value: code.Uri): string { + return _uriConverter(value); + } + + function asTextDocumentIdentifier(textDocument: code.TextDocument): ls.TextDocumentIdentifier { + return { + uri: _uriConverter(textDocument.uri) + }; + } + + function asOpenTextDocumentParams(textDocument: code.TextDocument): proto.DidOpenTextDocumentParams { + return { + textDocument: { + uri: _uriConverter(textDocument.uri), + languageId: textDocument.languageId, + version: textDocument.version, + text: textDocument.getText() + } + }; + } + + function isTextDocumentChangeEvent(value: any): value is code.TextDocumentChangeEvent { + let candidate = value; + return is.defined(candidate.document) && is.defined(candidate.contentChanges); + } + + function isTextDocument(value: any): value is code.TextDocument { + let candidate = value; + return is.defined(candidate.uri) && is.defined(candidate.version); + } + + function asChangeTextDocumentParams(textDocument: code.TextDocument): proto.DidChangeTextDocumentParams; + function asChangeTextDocumentParams(event: code.TextDocumentChangeEvent): proto.DidChangeTextDocumentParams; + function asChangeTextDocumentParams(arg: code.TextDocumentChangeEvent | code.TextDocument): proto.DidChangeTextDocumentParams { + if (isTextDocument(arg)) { + let result: proto.DidChangeTextDocumentParams = { + textDocument: { + uri: _uriConverter(arg.uri), + version: arg.version + }, + contentChanges: [{ text: arg.getText() }] + } + return result; + } else if (isTextDocumentChangeEvent(arg)) { + let document = arg.document; + let result: proto.DidChangeTextDocumentParams = { + textDocument: { + uri: _uriConverter(document.uri), + version: document.version + }, + contentChanges: arg.contentChanges.map((change): proto.TextDocumentContentChangeEvent => { + let range = change.range; + return { + range: { + start: { line: range.start.line, character: range.start.character }, + end: { line: range.end.line, character: range.end.character } + }, + rangeLength: change.rangeLength, + text: change.text + } + }) + } + return result; + } else { + throw Error('Unsupported text document change parameter'); + } + } + + function asCloseTextDocumentParams(textDocument: code.TextDocument): proto.DidCloseTextDocumentParams { + return { + textDocument: asTextDocumentIdentifier(textDocument) + }; + } + + function asSaveTextDocumentParams(textDocument: code.TextDocument): proto.DidSaveTextDocumentParams { + return { + textDocument: asTextDocumentIdentifier(textDocument) + } + } + + function asTextDocumentPositionParams(textDocument: code.TextDocument, position: code.Position): proto.TextDocumentPositionParams { + return { + textDocument: asTextDocumentIdentifier(textDocument), + position: asWorkerPosition(position) + }; + } + + function asWorkerPosition(position: code.Position): ls.Position { + return { line: position.line, character: position.character }; + } + + function asRange(value: code.Range): ls.Range { + if (is.undefined(value)) { + return undefined; + } else if (is.nil(value)) { + return null; + } + return { start: asPosition(value.start), end: asPosition(value.end) }; + } + + function asPosition(value: code.Position): ls.Position { + if (is.undefined(value)) { + return undefined; + } else if (is.nil(value)) { + return null; + } + return { line: value.line, character: value.character }; + } + + function set(value, func: () => void): void { + if (is.defined(value)) { + func(); + } + } + + function asDiagnosticSeverity(value: code.DiagnosticSeverity): ls.DiagnosticSeverity { + switch (value) { + case code.DiagnosticSeverity.Error: + return ls.DiagnosticSeverity.Error; + case code.DiagnosticSeverity.Warning: + return ls.DiagnosticSeverity.Warning; + case code.DiagnosticSeverity.Information: + return ls.DiagnosticSeverity.Information; + case code.DiagnosticSeverity.Hint: + return ls.DiagnosticSeverity.Hint; + } + } + + function asDiagnostic(item: code.Diagnostic): ls.Diagnostic { + let result: ls.Diagnostic = ls.Diagnostic.create(asRange(item.range), item.message); + set(item.severity, () => result.severity = asDiagnosticSeverity(item.severity)); + set(item.code, () => result.code = item.code); + set(item.source, () => result.source = item.source); + return result; + } + + function asDiagnostics(items: code.Diagnostic[]): ls.Diagnostic[] { + if (is.undefined(items) || is.nil(items)) { + return items; + } + return items.map(asDiagnostic); + } + + function asCompletionItem(item: code.CompletionItem): ls.CompletionItem { + let result: ls.CompletionItem = { label: item.label }; + set(item.detail, () => result.detail = item.detail); + set(item.documentation, () => result.documentation = item.documentation); + set(item.filterText, () => result.filterText = item.filterText); + set(item.insertText, () => result.insertText = String(item.insertText)); + // Protocol item kind is 1 based, codes item kind is zero based. + set(item.kind, () => result.kind = item.kind + 1); + set(item.sortText, () => result.sortText = item.sortText); + set(item.textEdit, () => result.textEdit = asTextEdit(item.textEdit)); + set(item.additionalTextEdits, () => result.additionalTextEdits = asTextEdits(item.additionalTextEdits)); + set(item.command, () => result.command = asCommand(item.command)); + if (item instanceof ProtocolCompletionItem) { + set(item.data, () => result.data = item.data); + } + return result; + } + + function asTextEdit(edit: code.TextEdit): ls.TextEdit { + return { range: asRange(edit.range), newText: edit.newText }; + } + + function asTextEdits(edits: code.TextEdit[]): ls.TextEdit[] { + if (is.undefined(edits) || is.nil(edits)) { + return edits; + } + return edits.map(asTextEdit); + } + + function asReferenceParams(textDocument: code.TextDocument, position: code.Position, options: { includeDeclaration: boolean; }): proto.ReferenceParams { + return { + textDocument: asTextDocumentIdentifier(textDocument), + position: asWorkerPosition(position), + context: { includeDeclaration: options.includeDeclaration } + }; + } + + function asCodeActionContext(context: code.CodeActionContext): ls.CodeActionContext { + if (is.undefined(context) || is.nil(context)) { + return context; + } + return ls.CodeActionContext.create(asDiagnostics(context.diagnostics)); + } + + function asCommand(item: code.Command): ls.Command { + let result = ls.Command.create(item.title, item.command); + if (is.defined(item.arguments)) result.arguments = item.arguments; + return result; + } + + function asCodeLens(item: code.CodeLens): ls.CodeLens { + let result = ls.CodeLens.create(asRange(item.range)); + if (is.defined(item.command)) result.command = asCommand(item.command); + if (item instanceof ProtocolCodeLens) { + if (is.defined(item.data)) result.data = item.data; + } + return result; + } + + function asFormattingOptions(item: code.FormattingOptions): ls.FormattingOptions { + return { tabSize: item.tabSize, insertSpaces: item.insertSpaces }; + } + + function asDocumentSymbolParams(textDocument: code.TextDocument): proto.DocumentSymbolParams { + return { + textDocument: asTextDocumentIdentifier(textDocument) + } + } + + function asCodeLensParams(textDocument: code.TextDocument): proto.CodeLensParams { + return { + textDocument: asTextDocumentIdentifier(textDocument) + }; + } + + function asDocumentLink(item: code.DocumentLink): ls.DocumentLink { + let result = ls.DocumentLink.create(asRange(item.range)); + if (is.defined(item.target)) result.target = asUri(item.target); + return result; + } + + function asDocumentLinkParams(textDocument: code.TextDocument): proto.DocumentLinkParams { + return { + textDocument: asTextDocumentIdentifier(textDocument) + }; + } + + function asCapabilitiesParams(client: data.DataProtocolClientCapabilities): proto.CapabiltiesDiscoveryParams { + let params: proto.CapabiltiesDiscoveryParams = { + hostName: client.hostName, + hostVersion: client.hostVersion + }; + return params; + } + + function asConnectionParams(connUri: string, connInfo: data.ConnectionInfo): proto.ConnectParams { + return { + ownerUri: connUri, + connection: { + options: connInfo.options + } + }; + } + + function asMetadataQueryParams(connectionUri: string): ls.MetadataQueryParams { + return { + ownerUri: connectionUri + }; + } + + function asListDatabasesParams(connectionUri: string): proto.ListDatabasesParams { + return { + ownerUri: connectionUri + }; + } + + function asTableMetadataParams(connectionUri: string, metadata: data.ObjectMetadata): proto.TableMetadataParams { + return { + ownerUri: connectionUri, + schema: metadata.schema, + objectName: metadata.name + }; + } + + function asScriptingParams(connectionUri: string, operation: ls.ScriptOperation, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): ls.ScriptingParams { + let scriptingObject: ls.ScriptingObject = { + type: metadata.metadataTypeName, + schema: metadata.schema, + name: metadata.name + } + let targetDatabaseEngineEdition = paramDetails.targetDatabaseEngineEdition; + let targetDatabaseEngineType = paramDetails.targetDatabaseEngineType; + let scriptCompatibilityOption = paramDetails.scriptCompatibilityOption; + let options: ls.ScriptOptions = { + scriptCreateDrop: (operation === ls.ScriptOperation.Delete) ? "ScriptDrop" : + (operation === ls.ScriptOperation.Select) ? "ScriptSelect" : "ScriptCreate", + typeOfDataToScript: "SchemaOnly", + scriptStatistics: "ScriptStatsNone", + targetDatabaseEngineEdition: targetDatabaseEngineEdition ? targetDatabaseEngineEdition : "SqlServerEnterpriseEdition", + targetDatabaseEngineType: targetDatabaseEngineType ? targetDatabaseEngineType : "SingleInstance", + scriptCompatibilityOption: scriptCompatibilityOption ? scriptCompatibilityOption : "Script140Compat" + } + return { + connectionString: null, + filePath: paramDetails.filePath, + scriptingObjects: [scriptingObject], + scriptDestination: "ToEditor", + includeObjectCriteria: null, + excludeObjectCriteria: null, + includeSchemas: null, + excludeSchemas: null, + includeTypes: null, + excludeTypes: null, + scriptOptions: options, + connectionDetails: null, + ownerURI: connectionUri, + operation: operation + }; + } + + function asConnectionDetail(connInfo: data.ConnectionInfo): ls.ConnectionDetails { + return { + options: connInfo.options + }; + } + + function asExpandInfo(nodeInfo: data.ExpandNodeInfo): ls.ExpandParams { + return { + sessionId: nodeInfo.sessionId, + nodePath: nodeInfo.nodePath + }; + } + + function asCloseSessionInfo(nodeInfo: data.ObjectExplorerCloseSessionInfo): ls.CloseSessionParams { + return { + sessionId: nodeInfo.sessionId + }; + } + + function asExecutionPlanOptions(planOptions: data.ExecutionPlanOptions): proto.ExecutionPlanOptions { + return { + includeEstimatedExecutionPlanXml: planOptions ? planOptions.displayEstimatedQueryPlan : undefined, + includeActualExecutionPlanXml: planOptions ? planOptions.displayActualQueryPlan : undefined + }; + } + + function asListTasksParams(params: data.ListTasksParams): ls.ListTasksParams { + return { + listActiveTasksOnly: params.listActiveTasksOnly + }; + } + + function asCancelTaskParams(params: data.CancelTaskParams): ls.CancelTaskParams { + return { + taskId: params.taskId + }; + } + + function asRestoreParams(ownerUri: string, params: data.RestoreInfo): ls.RestoreParams { + return { + ownerUri: ownerUri, + options: params.options, + taskExecutionMode: params.taskExecutionMode + }; + } + + function asRestoreConfigInfoParams(ownerUri: string): ls.RestoreConfigInfoRequestParams { + return { + ownerUri: ownerUri + }; + } + + return { + asUri, + asTextDocumentIdentifier, + asOpenTextDocumentParams, + asChangeTextDocumentParams, + asCloseTextDocumentParams, + asSaveTextDocumentParams, + asTextDocumentPositionParams, + asWorkerPosition, + asRange, + asPosition, + asDiagnosticSeverity, + asDiagnostic, + asDiagnostics, + asCompletionItem, + asTextEdit, + asReferenceParams, + asCodeActionContext, + asCommand, + asCodeLens, + asFormattingOptions, + asDocumentSymbolParams, + asCodeLensParams, + asDocumentLink, + asDocumentLinkParams, + asCapabilitiesParams, + asConnectionParams, + asMetadataQueryParams, + asTableMetadataParams, + asListDatabasesParams, + asScriptingParams, + asConnectionDetail, + asExpandInfo, + asCloseSessionInfo, + asExecutionPlanOptions, + asListTasksParams, + asCancelTaskParams, + asRestoreParams, + asRestoreConfigInfoParams + }; +} + +// This for backward compatibility since we exported the converter functions as API. +let defaultConverter = createConverter(); + +export const asTextDocumentIdentifier: (textDocument: code.TextDocument) => ls.TextDocumentIdentifier = defaultConverter.asTextDocumentIdentifier; +export const asOpenTextDocumentParams: (textDocument: code.TextDocument) => proto.DidOpenTextDocumentParams = defaultConverter.asOpenTextDocumentParams; +export const asChangeTextDocumentParams: (arg: code.TextDocumentChangeEvent | code.TextDocument) => proto.DidChangeTextDocumentParams = defaultConverter.asChangeTextDocumentParams; +export const asCloseTextDocumentParams: (textDocument: code.TextDocument) => proto.DidCloseTextDocumentParams = defaultConverter.asCloseTextDocumentParams; +export const asSaveTextDocumentParams: (textDocument: code.TextDocument) => proto.DidSaveTextDocumentParams = defaultConverter.asSaveTextDocumentParams; +export const asTextDocumentPositionParams: (textDocument: code.TextDocument, position: code.Position) => proto.TextDocumentPositionParams = defaultConverter.asTextDocumentPositionParams; +export const asWorkerPosition: (position: code.Position) => ls.Position = defaultConverter.asWorkerPosition; +export const asRange: (value: code.Range) => ls.Range = defaultConverter.asRange; +export const asPosition: (value: code.Position) => ls.Position = defaultConverter.asPosition; +export const asDiagnosticSeverity: (value: code.DiagnosticSeverity) => ls.DiagnosticSeverity = defaultConverter.asDiagnosticSeverity; +export const asDiagnostic: (item: code.Diagnostic) => ls.Diagnostic = defaultConverter.asDiagnostic; +export const asDiagnostics: (items: code.Diagnostic[]) => ls.Diagnostic[] = defaultConverter.asDiagnostics; +export const asCompletionItem: (item: code.CompletionItem) => ls.CompletionItem = defaultConverter.asCompletionItem; +export const asTextEdit: (edit: code.TextEdit) => ls.TextEdit = defaultConverter.asTextEdit; +export const asReferenceParams: (textDocument: code.TextDocument, position: code.Position, options: { includeDeclaration: boolean; }) => proto.ReferenceParams = defaultConverter.asReferenceParams; +export const asCodeActionContext: (context: code.CodeActionContext) => ls.CodeActionContext = defaultConverter.asCodeActionContext; +export const asCommand: (item: code.Command) => ls.Command = defaultConverter.asCommand; +export const asCodeLens: (item: code.CodeLens) => ls.CodeLens = defaultConverter.asCodeLens; +export const asFormattingOptions: (item: code.FormattingOptions) => ls.FormattingOptions = defaultConverter.asFormattingOptions; +export const asDocumentSymbolParams: (textDocument: code.TextDocument) => proto.DocumentSymbolParams = defaultConverter.asDocumentSymbolParams; +export const asCodeLensParams: (textDocument: code.TextDocument) => proto.CodeLensParams = defaultConverter.asCodeLensParams; \ No newline at end of file diff --git a/dataprotocol-node/client/src/main.ts b/dataprotocol-node/client/src/main.ts new file mode 100644 index 0000000000..4973e982cb --- /dev/null +++ b/dataprotocol-node/client/src/main.ts @@ -0,0 +1,2599 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import * as cp from 'child_process'; +import * as stream from 'stream'; +import ChildProcess = cp.ChildProcess; + +import { + workspace as Workspace, window as Window, languages as Languages, extensions as Extensions, TextDocumentChangeEvent, TextDocument, Disposable, OutputChannel, + FileSystemWatcher, Uri, DiagnosticCollection, DocumentSelector, + CancellationToken, Hover as VHover, Position as VPosition, Location as VLocation, Range as VRange, + CompletionItem as VCompletionItem, CompletionList as VCompletionList, SignatureHelp as VSignatureHelp, Definition as VDefinition, DocumentHighlight as VDocumentHighlight, + SymbolInformation as VSymbolInformation, CodeActionContext as VCodeActionContext, Command as VCommand, CodeLens as VCodeLens, + FormattingOptions as VFormattingOptions, TextEdit as VTextEdit, WorkspaceEdit as VWorkspaceEdit, MessageItem, + DocumentLink as VDocumentLink +} from 'vscode'; + +import { + ConnectionInfo, ConnectionInfoSummary, dataprotocol, DataProtocolProvider, ConnectionProvider, + DataProtocolServerCapabilities as VDataProtocolServerCapabilities, + DataProtocolClientCapabilities, CapabilitiesProvider, MetadataProvider, + ScriptingProvider, ProviderMetadata, ScriptingResult, ScriptingCompleteResult, + QueryProvider, QueryCancelResult as VQueryCancelResult, ObjectMetadata, + ListDatabasesResult as VListDatabasesResult, ChangedConnectionInfo, + SaveResultRequestResult as VSaveResultRequestResult, ScriptingParamDetails, + SaveResultsRequestParams as VSaveResultsRequestParams, ObjectExplorerProvider, + ExpandNodeInfo, ObjectExplorerCloseSessionInfo, ObjectExplorerSession, ObjectExplorerExpandInfo, + TaskServicesProvider, ListTasksParams, ListTasksResponse, CancelTaskParams, TaskProgressInfo, TaskInfo, + AdminServicesProvider, DisasterRecoveryProvider, RestoreInfo, ExecutionPlanOptions, + RestoreConfigInfo, SerializationProvider, FileBrowserProvider, FileBrowserOpenedParams, FileBrowserExpandedParams, FileBrowserValidatedParams +} from 'data'; + +import { + Message, + RequestHandler, NotificationHandler, MessageConnection, ClientMessageConnection, Logger, createClientMessageConnection, + ErrorCodes, ResponseError, RequestType, NotificationType, + MessageReader, IPCMessageReader, MessageWriter, IPCMessageWriter, Trace, Tracer, Event, Emitter +} from 'dataprotocol-jsonrpc'; + +import { + Range, Position, Location, Diagnostic, DiagnosticSeverity, Command, + TextEdit, WorkspaceEdit, WorkspaceChange, TextEditChange, + TextDocumentIdentifier, CompletionItemKind, CompletionItem, CompletionList, + Hover, MarkedString, + SignatureHelp, SignatureInformation, ParameterInformation, + Definition, CodeActionContext, + DocumentHighlight, DocumentHighlightKind, + SymbolInformation, SymbolKind, + CodeLens, + FormattingOptions, DocumentLink, + ConnectionCompleteParams, IntelliSenseReadyParams, + ConnectionProviderOptions, DataProtocolServerCapabilities, + ISelectionData, QueryExecuteBatchNotificationParams, + MetadataQueryParams, MetadataQueryResult, + ScriptOperation, ScriptingCompleteParams, + DatabaseInfo, BackupConfigInfo, CreateDatabaseResponse, CreateDatabaseParams, + LoginInfo, CreateLoginResponse, CreateLoginParams, + BackupInfo, BackupResponse, BackupParams, TaskExecutionMode, + RestoreParams, RestoreResponse, RestorePlanResponse, + DefaultDatabaseInfoResponse, DefaultDatabaseInfoParams, + GetDatabaseInfoResponse, GetDatabaseInfoParams, + BackupConfigInfoResponse, FileBrowserOpenParams, FileBrowserCloseResponse, + FileBrowserCloseParams, FileBrowserExpandParams, FileBrowserValidateParams +} from 'dataprotocol-languageserver-types'; + + +import { + InitializeRequest, InitializeParams, InitializeResult, InitializeError, ClientCapabilities, ServerCapabilities, TextDocumentSyncKind, + ShutdownRequest, + ExitNotification, + LogMessageNotification, LogMessageParams, MessageType, + ShowMessageNotification, ShowMessageParams, ShowMessageRequest, ShowMessageRequestParams, + TelemetryEventNotification, + DidChangeConfigurationNotification, DidChangeConfigurationParams, + TextDocumentPositionParams, + DidOpenTextDocumentNotification, DidOpenTextDocumentParams, DidChangeTextDocumentNotification, DidChangeTextDocumentParams, + DidCloseTextDocumentNotification, DidCloseTextDocumentParams, DidSaveTextDocumentNotification, DidSaveTextDocumentParams, + DidChangeWatchedFilesNotification, DidChangeWatchedFilesParams, FileEvent, FileChangeType, + PublishDiagnosticsNotification, PublishDiagnosticsParams, + CompletionRequest, CompletionResolveRequest, + HoverRequest, + SignatureHelpRequest, DefinitionRequest, ReferencesRequest, DocumentHighlightRequest, + DocumentSymbolRequest, WorkspaceSymbolRequest, WorkspaceSymbolParams, + CodeActionRequest, CodeActionParams, + CodeLensRequest, CodeLensResolveRequest, + DocumentFormattingRequest, DocumentFormattingParams, DocumentRangeFormattingRequest, DocumentRangeFormattingParams, + DocumentOnTypeFormattingRequest, DocumentOnTypeFormattingParams, + RenameRequest, RenameParams, + DocumentLinkRequest, DocumentLinkResolveRequest, DocumentLinkParams, + RebuildIntelliSenseNotification, RebuildIntelliSenseParams, + CapabiltiesDiscoveryRequest, + ConnectionRequest, ConnectParams, + DisconnectRequest, DisconnectParams, + CancelConnectRequest, CancelConnectParams, + ChangeDatabaseParams, ChangeDatabaseRequest, + ListDatabasesRequest, ListDatabasesParams, ListDatabasesResult, + ConnectionChangedNotification, ConnectionChangedParams, + ConnectionCompleteNotification, IntelliSenseReadyNotification, + TableMetadataRequest, ViewMetadataRequest, MetadataQueryRequest, + ScriptingRequest, ScriptingCompleteNotification, + QueryCancelRequest, QueryCancelResult, QueryCancelParams, + QueryExecuteRequest, QueryExecuteSubsetResult, QueryExecuteSubsetParams, + SimpleExecuteRequest, SimpleExecuteResult, SimpleExecuteParams, + QueryExecuteBatchStartNotification, QueryExecuteBatchCompleteNotification, QueryExecuteCompleteNotification, + QueryExecuteMessageNotification, QueryDisposeParams, QueryDisposeRequest, QueryExecuteCompleteNotificationResult, + QueryExecuteMessageParams, QueryExecuteParams, QueryExecuteResultSetCompleteNotification, QueryExecuteResultSetCompleteNotificationParams, + QueryExecuteStringParams, QueryExecuteStringRequest, + QueryExecuteStatementParams, QueryExecuteStatementRequest, + QueryExecuteSubsetRequest, SaveResultRequestResult, SaveResultsRequestParams, SaveResultsAsCsvRequest, SaveResultsAsJsonRequest, SaveResultsAsExcelRequest, + EditCommitRequest, EditCommitParams, + EditCreateRowRequest, EditCreateRowParams, EditCreateRowResult, + EditDeleteRowRequest, EditDeleteRowParams, + EditDisposeRequest, EditDisposeParams, + EditInitializeRequest, EditInitializeParams, EditInitializeFiltering, + EditRevertCellRequest, EditRevertCellParams, EditRevertCellResult, + EditRevertRowRequest, EditRevertRowParams, + EditSessionReadyNotification, EditSessionReadyParams, + EditUpdateCellRequest, EditUpdateCellParams, EditUpdateCellResult, + EditSubsetRequest, EditSubsetParams, EditSubsetResult, + ObjectExplorerCreateSessionRequest, ObjectExplorerExpandRequest, ObjectExplorerRefreshRequest, ObjectExplorerCloseSessionRequest, + ObjectExplorerCreateSessionCompleteNotification, ObjectExplorerExpandCompleteNotification, + CreateDatabaseRequest, CreateLoginRequest, BackupRequest, DefaultDatabaseInfoRequest, GetDatabaseInfoRequest, BackupConfigInfoRequest, + RestoreRequest, RestorePlanRequest, CancelRestorePlanRequest, RestoreConfigInfoRequest, + ListTasksRequest, CancelTaskRequest, TaskStatusChangedNotification, TaskCreatedNotification, + LanguageFlavorChangedNotification, DidChangeLanguageFlavorParams, FileBrowserOpenRequest, FileBrowserOpenedNotification, + FileBrowserValidateRequest, FileBrowserValidatedNotification, FileBrowserExpandRequest, FileBrowserExpandedNotification, FileBrowserCloseRequest +} from './protocol'; + +import * as c2p from './codeConverter'; +import * as p2c from './protocolConverter'; + +import * as is from './utils/is'; +import * as electron from './utils/electron'; +import { terminate } from './utils/processes'; +import { Delayer } from './utils/async'; + +export { + RequestType, NotificationType, NotificationHandler, RequestHandler, + ResponseError, InitializeError, ErrorCodes, + Position, Range, Location, TextDocumentIdentifier, TextDocumentPositionParams, + TextEdit, TextEditChange, WorkspaceChange, + c2p as Code2Protocol, p2c as Protocol2Code +} + +declare var v8debug; + + +interface IConnection { + + listen(): void; + + sendRequest(type: RequestType, params: P, token?: CancellationToken): Thenable; + sendNotification

(type: NotificationType

, params: P): void; + onNotification

(type: NotificationType

, handler: NotificationHandler

): void; + onRequest(type: RequestType, handler: RequestHandler): void; + trace(value: Trace, tracer: Tracer, sendNotification?: boolean): void; + + initialize(params: InitializeParams): Thenable; + shutdown(): Thenable; + exit(): void; + + onLogMessage(handle: NotificationHandler): void; + onShowMessage(handler: NotificationHandler): void; + onTelemetry(handler: NotificationHandler): void; + + didChangeConfiguration(params: DidChangeConfigurationParams): void; + didChangeWatchedFiles(params: DidChangeWatchedFilesParams): void; + + didOpenTextDocument(params: DidOpenTextDocumentParams): void; + didChangeTextDocument(params: DidChangeTextDocumentParams): void; + didCloseTextDocument(params: DidCloseTextDocumentParams): void; + didSaveTextDocument(params: DidSaveTextDocumentParams): void; + onDiagnostics(handler: NotificationHandler): void; + + dispose(): void; +} + +class ConsoleLogger implements Logger { + public error(message: string): void { + console.error(message); + } + public warn(message: string): void { + console.warn(message); + } + public info(message: string): void { + console.info(message); + } + public log(message: string): void { + console.log(message); + } +} + +interface ConnectionErrorHandler { + (error: Error, message: Message, count: number): void; +} + +interface ConnectionCloseHandler { + (): void; +} +function createConnection(inputStream: stream.Readable, outputStream: NodeJS.WritableStream, errorHandler: ConnectionErrorHandler, closeHandler: ConnectionCloseHandler): IConnection; +function createConnection(inputStream: NodeJS.ReadableStream, outputStream: NodeJS.WritableStream, errorHandler: ConnectionErrorHandler, closeHandler: ConnectionCloseHandler): IConnection; +function createConnection(reader: MessageReader, writer: MessageWriter, errorHandler: ConnectionErrorHandler, closeHandler: ConnectionCloseHandler): IConnection; +function createConnection(input: any, output: any, errorHandler: ConnectionErrorHandler, closeHandler: ConnectionCloseHandler): IConnection { + let logger = new ConsoleLogger(); + let connection = createClientMessageConnection(input, output, logger); + connection.onError((data) => { errorHandler(data[0], data[1], data[2]) }); + connection.onClose(closeHandler); + let result: IConnection = { + + listen: (): void => connection.listen(), + + sendRequest: (type: RequestType, params: P, token?: CancellationToken): Thenable => connection.sendRequest(type, params, token), + sendNotification:

(type: NotificationType

, params: P): void => connection.sendNotification(type, params), + onNotification:

(type: NotificationType

, handler: NotificationHandler

): void => connection.onNotification(type, handler), + onRequest: (type: RequestType, handler: RequestHandler): void => connection.onRequest(type, handler), + + trace: (value: Trace, tracer: Tracer, sendNotification: boolean = false): void => connection.trace(value, tracer, sendNotification), + + initialize: (params: InitializeParams) => connection.sendRequest(InitializeRequest.type, params), + shutdown: () => connection.sendRequest(ShutdownRequest.type, undefined), + exit: () => connection.sendNotification(ExitNotification.type), + + onLogMessage: (handler: NotificationHandler) => connection.onNotification(LogMessageNotification.type, handler), + onShowMessage: (handler: NotificationHandler) => connection.onNotification(ShowMessageNotification.type, handler), + onTelemetry: (handler: NotificationHandler) => connection.onNotification(TelemetryEventNotification.type, handler), + + didChangeConfiguration: (params: DidChangeConfigurationParams) => connection.sendNotification(DidChangeConfigurationNotification.type, params), + didChangeWatchedFiles: (params: DidChangeWatchedFilesParams) => connection.sendNotification(DidChangeWatchedFilesNotification.type, params), + + didOpenTextDocument: (params: DidOpenTextDocumentParams) => connection.sendNotification(DidOpenTextDocumentNotification.type, params), + didChangeTextDocument: (params: DidChangeTextDocumentParams | DidChangeTextDocumentParams[]) => connection.sendNotification(DidChangeTextDocumentNotification.type, params), + didCloseTextDocument: (params: DidCloseTextDocumentParams) => connection.sendNotification(DidCloseTextDocumentNotification.type, params), + didSaveTextDocument: (params: DidSaveTextDocumentParams) => connection.sendNotification(DidSaveTextDocumentNotification.type, params), + onDiagnostics: (handler: NotificationHandler) => connection.onNotification(PublishDiagnosticsNotification.type, handler), + + dispose: () => connection.dispose() + }; + + return result; +} + +export interface StreamInfo { + writer: NodeJS.WritableStream; + reader: NodeJS.ReadableStream; +} + +export interface ExecutableOptions { + cwd?: string; + stdio?: string | string[]; + env?: any; + detached?: boolean; +} + +export interface Executable { + command: string; + args?: string[]; + options?: ExecutableOptions; +} + +export interface ForkOptions { + cwd?: string; + env?: any; + encoding?: string; + execArgv?: string[]; +} + +export enum TransportKind { + stdio, + ipc +} + +export interface NodeModule { + module: string; + transport?: TransportKind; + args?: string[]; + runtime?: string; + options?: ForkOptions; +} + +export type ServerOptions = Executable | { run: Executable; debug: Executable; } | { run: NodeModule; debug: NodeModule } | NodeModule | (() => Thenable); + +/** + * An action to be performed when the connection is producing errors. + */ +export enum ErrorAction { + /** + * Continue running the server. + */ + Continue = 1, + /** + * Shutdown the server. + */ + Shutdown = 2 +} + +/** + * An action to be performed when the connection to a server got closed. + */ +export enum CloseAction { + /** + * Don't restart the server. The connection stays closed. + */ + DoNotRestart = 1, + /** + * Restart the server. + */ + Restart = 2, +} + + +/** + * A pluggable error handler that is invoked when the connection is either + * producing errors or got closed. + */ +export interface ErrorHandler { + /** + * An error has occurred while writing or reading from the connection. + * + * @param error - the error received + * @param message - the message to be delivered to the server if know. + * @param count - a count indicating how often an error is received. Will + * be reset if a message got successfully send or received. + */ + error(error: Error, message: Message, count: number): ErrorAction; + + /** + * The connection to the server got closed. + */ + closed(): CloseAction +} + +class DefaultErrorHandler implements ErrorHandler { + + private restarts: number[]; + + constructor(private name: string) { + this.restarts = []; + } + + public error(error: Error, message: Message, count): ErrorAction { + if (count && count <= 3) { + return ErrorAction.Continue; + } + return ErrorAction.Shutdown; + } + public closed(): CloseAction { + this.restarts.push(Date.now()); + if (this.restarts.length < 5) { + return CloseAction.Restart; + } else { + let diff = this.restarts[this.restarts.length - 1] - this.restarts[0]; + if (diff <= 3 * 60 * 1000) { + Window.showErrorMessage(`The ${this.name} server crashed 5 times in the last 3 minutes. The server will not be restarted.`); + return CloseAction.DoNotRestart; + } else { + this.restarts.shift(); + return CloseAction.Restart; + } + } + } +} + +export interface InitializationFailedHandler { + (error: ResponseError | Error | any): boolean; +} + +export interface SynchronizeOptions { + configurationSection?: string | string[]; + fileEvents?: FileSystemWatcher | FileSystemWatcher[]; + textDocumentFilter?: (textDocument: TextDocument) => boolean; +} + +export enum RevealOutputChannelOn { + Info = 1, + Warn = 2, + Error = 3, + Never = 4 +} + +export interface LanguageClientOptions { + documentSelector?: string | string[]; + providerId: string; + synchronize?: SynchronizeOptions; + diagnosticCollectionName?: string; + outputChannelName?: string; + revealOutputChannelOn?: RevealOutputChannelOn; + /** + * The encoding use to read stdout and stderr. Defaults + * to 'utf8' if ommitted. + */ + stdioEncoding?: string; + initializationOptions?: any | (() => any); + initializationFailedHandler?: InitializationFailedHandler; + errorHandler?: ErrorHandler; + uriConverters?: { + code2Protocol: c2p.URIConverter, + protocol2Code: p2c.URIConverter + }; + serverConnectionMetadata?: string; +} + +export enum State { + Stopped = 1, + Running = 2 +} + +export interface StateChangeEvent { + oldState: State; + newState: State; +} + +enum ClientState { + Initial, + Starting, + StartFailed, + Running, + Stopping, + Stopped +} + +interface SyncExpression { + evaluate(textDocument: TextDocument): boolean; +} + +class FalseSyncExpression implements SyncExpression { + public evaluate(textDocument: TextDocument): boolean { + return false; + } +} + +class LanguageIdExpression implements SyncExpression { + constructor(private _id: string) { + } + public evaluate(textDocument: TextDocument): boolean { + return this._id === textDocument.languageId; + } +} + +class FunctionSyncExpression implements SyncExpression { + constructor(private _func: (textDocument: TextDocument) => boolean) { + } + public evaluate(textDocument: TextDocument): boolean { + return this._func(textDocument); + } +} + +class CompositeSyncExpression implements SyncExpression { + private _expression: SyncExpression[]; + constructor(values: string[], func?: (textDocument: TextDocument) => boolean) { + this._expression = values.map(value => new LanguageIdExpression(value)); + if (func) { + this._expression.push(new FunctionSyncExpression(func)); + } + } + public evaluate(textDocument: TextDocument): boolean { + return this._expression.some(exp => exp.evaluate(textDocument)); + } +} + +export class LanguageClient { + + private _id: string; + private _name: string; + private _serverOptions: ServerOptions; + private _clientOptions: LanguageClientOptions; + private _forceDebug: boolean; + + private _state: ClientState; + private _onReady: Promise; + private _onReadyCallbacks: { resolve: () => void; reject: (error) => void; }; + private _connection: Thenable; + private _childProcess: ChildProcess; + private _outputChannel: OutputChannel; + private _capabilites: ServerCapabilities; + + private _listeners: Disposable[]; + private _providers: Disposable[]; + private _diagnostics: DiagnosticCollection; + + private _syncExpression: SyncExpression; + + private _documentSyncDelayer: Delayer; + + private _fileEvents: FileEvent[]; + private _fileEventDelayer: Delayer; + + private _telemetryEmitter: Emitter; + private _stateChangeEmitter: Emitter; + + private _trace: Trace; + private _tracer: Tracer; + + private _c2p: c2p.Converter; + private _p2c: p2c.Converter; + + public constructor(name: string, serverOptions: ServerOptions, clientOptions: LanguageClientOptions, forceDebug?: boolean); + public constructor(id: string, name: string, serverOptions: ServerOptions, clientOptions: LanguageClientOptions, forceDebug?: boolean); + public constructor(arg1: string, arg2: ServerOptions | string, arg3: LanguageClientOptions | ServerOptions, arg4: boolean | LanguageClientOptions, arg5?: boolean) { + let clientOptions: LanguageClientOptions; + let forceDebug: boolean; + if (is.string(arg2)) { + this._id = arg1; + this._name = arg2; + this._serverOptions = arg3 as ServerOptions; + clientOptions = arg4 as LanguageClientOptions; + forceDebug = arg5; + } else { + this._id = arg1.toLowerCase(); + this._name = arg1; + this._serverOptions = arg2 as ServerOptions; + clientOptions = arg3 as LanguageClientOptions; + forceDebug = arg4 as boolean; + } + if (forceDebug === void 0) { forceDebug = false; } + this._clientOptions = clientOptions || { providerId: '' }; + this._clientOptions.synchronize = this._clientOptions.synchronize || {}; + this._clientOptions.errorHandler = this._clientOptions.errorHandler || new DefaultErrorHandler(this._name); + this._clientOptions.revealOutputChannelOn == this._clientOptions.revealOutputChannelOn || RevealOutputChannelOn.Error; + this._syncExpression = this.computeSyncExpression(); + this._forceDebug = forceDebug; + + this.state = ClientState.Initial; + this._connection = null; + this._childProcess = null; + this._outputChannel = null; + + this._listeners = null; + this._providers = null; + this._diagnostics = null; + + this._fileEvents = []; + this._fileEventDelayer = new Delayer(250); + this._onReady = new Promise((resolve, reject) => { + this._onReadyCallbacks = { resolve, reject }; + }); + this._telemetryEmitter = new Emitter(); + this._stateChangeEmitter = new Emitter(); + this._tracer = { + log: (message: string, data?: string) => { + this.logTrace(message, data); + } + }; + this._c2p = c2p.createConverter(clientOptions.uriConverters ? clientOptions.uriConverters.code2Protocol : undefined); + this._p2c = p2c.createConverter(clientOptions.uriConverters ? clientOptions.uriConverters.protocol2Code : undefined); + } + + private get state(): ClientState { + return this._state; + } + + private set state(value: ClientState) { + let oldState = this.getPublicState(); + this._state = value; + let newState = this.getPublicState(); + if (newState !== oldState) { + this._stateChangeEmitter.fire({ oldState, newState }); + } + } + + private getPublicState(): State { + if (this.state === ClientState.Running) { + return State.Running; + } else { + return State.Stopped; + } + } + + private computeSyncExpression(): SyncExpression { + let documentSelector = this._clientOptions.documentSelector; + let textDocumentFilter = this._clientOptions.synchronize.textDocumentFilter; + + if (!documentSelector && !textDocumentFilter) { + return new FalseSyncExpression(); + } + if (textDocumentFilter && !documentSelector) { + return new FunctionSyncExpression(textDocumentFilter); + } + if (!textDocumentFilter && documentSelector) { + if (is.string(documentSelector)) { + return new LanguageIdExpression(documentSelector) + } else { + return new CompositeSyncExpression(documentSelector) + } + } + if (textDocumentFilter && documentSelector) { + return new CompositeSyncExpression( + is.string(documentSelector) ? [documentSelector] : documentSelector, + textDocumentFilter); + } + } + + public sendRequest(type: RequestType, params: P, token?: CancellationToken): Thenable { + return this.onReady().then(() => { + return this.resolveConnection().then((connection) => { + return this.doSendRequest(connection, type, params, token); + }); + }); + } + + private doSendRequest(connection: IConnection, type: RequestType, params: P, token?: CancellationToken): Thenable { + if (this.isConnectionActive()) { + this.forceDocumentSync(); + try { + return connection.sendRequest(type, params, token); + } catch (error) { + this.error(`Sending request ${type.method} failed.`, error); + } + } else { + return Promise.reject(new ResponseError(ErrorCodes.InternalError, 'Connection is closed.')); + } + } + + public sendNotification

(type: NotificationType

, params?: P): void { + this.onReady().then(() => { + this.resolveConnection().then((connection) => { + if (this.isConnectionActive()) { + this.forceDocumentSync(); + try { + connection.sendNotification(type, params); + } catch (error) { + this.error(`Sending notification ${type.method} failed.`, error); + } + } + }); + }, (error) => { + this.error(`Sending notification ${type.method} failed.`, error) + }); + } + + public onNotification

(type: NotificationType

, handler: NotificationHandler

): void { + this.onReady().then(() => { + this.resolveConnection().then((connection) => { + try { + connection.onNotification(type, handler); + } catch (error) { + this.error(`Registering notification handler ${type.method} failed.`, error); + } + }) + }, (error) => { + }); + } + + public onRequest(type: RequestType, handler: RequestHandler): void { + this.onReady().then(() => { + this.resolveConnection().then((connection) => { + try { + connection.onRequest(type, handler); + } catch (error) { + this.error(`Registering request handler ${type.method} failed.`, error); + } + }) + }, (error) => { + }); + } + + public get onTelemetry(): Event { + return this._telemetryEmitter.event; + } + + public get onDidChangeState(): Event { + return this._stateChangeEmitter.event; + } + + public get outputChannel(): OutputChannel { + if (!this._outputChannel) { + this._outputChannel = Window.createOutputChannel(this._clientOptions.outputChannelName ? this._clientOptions.outputChannelName : this._name); + } + return this._outputChannel; + } + + public get diagnostics(): DiagnosticCollection { + return this._diagnostics; + } + + public createDefaultErrorHandler(): ErrorHandler { + return new DefaultErrorHandler(this._name); + } + + public set trace(value: Trace) { + this._trace = value; + this.onReady().then(() => { + this.resolveConnection().then((connection) => { + connection.trace(value, this._tracer); + }) + }, (error) => { + }); + } + + private data2String(data: any): string { + if (data instanceof ResponseError) { + const responseError = data as ResponseError; + return ` Message: ${responseError.message}\n Code: ${responseError.code} ${responseError.data ? '\n' + responseError.data.toString() : ''}` + } + if (data instanceof Error) { + if (is.string(data.stack)) { + return data.stack; + } + return (data as Error).message; + } + if (is.string(data)) { + return data; + } + return data.toString(); + } + + public info(message: string, data?: any): void { + this.outputChannel.appendLine(`[Info - ${(new Date().toLocaleTimeString())}] ${message}`); + if (data) { + this.outputChannel.appendLine(this.data2String(data)); + } + if (this._clientOptions.revealOutputChannelOn <= RevealOutputChannelOn.Info) { + this.outputChannel.show(true); + } + } + + public warn(message: string, data?: any): void { + this.outputChannel.appendLine(`[Warn - ${(new Date().toLocaleTimeString())}] ${message}`); + if (data) { + this.outputChannel.appendLine(this.data2String(data)); + } + if (this._clientOptions.revealOutputChannelOn <= RevealOutputChannelOn.Warn) { + this.outputChannel.show(true); + } + } + + public error(message: string, data?: any): void { + this.outputChannel.appendLine(`[Error - ${(new Date().toLocaleTimeString())}] ${message}`); + if (data) { + this.outputChannel.appendLine(this.data2String(data)); + } + if (this._clientOptions.revealOutputChannelOn <= RevealOutputChannelOn.Error) { + this.outputChannel.show(true); + } + } + + private logTrace(message: string, data?: any): void { + this.outputChannel.appendLine(`[Trace - ${(new Date().toLocaleTimeString())}] ${message}`); + if (data) { + this.outputChannel.appendLine(this.data2String(data)); + } + this.outputChannel.show(true); + } + + public needsStart(): boolean { + return this.state === ClientState.Initial || this.state === ClientState.Stopping || this.state === ClientState.Stopped; + } + + public needsStop(): boolean { + return this.state === ClientState.Starting || this.state === ClientState.Running; + } + + public onReady(): Promise { + return this._onReady; + } + + private isConnectionActive(): boolean { + return this.state === ClientState.Running; + } + + public start(): Disposable { + this._listeners = []; + this._providers = []; + // If we restart then the diagnostics collection is reused. + if (!this._diagnostics) { + this._diagnostics = this._clientOptions.diagnosticCollectionName + ? Languages.createDiagnosticCollection(this._clientOptions.diagnosticCollectionName) + : Languages.createDiagnosticCollection(); + } + + this.state = ClientState.Starting; + if (this._clientOptions.providerId && this._clientOptions.providerId !== '') { + // hook-up SQL data protocol provider + this.hookDataProtocolProvider(this._clientOptions.providerId); + } + + this.resolveConnection().then((connection) => { + connection.onLogMessage((message) => { + switch (message.type) { + case MessageType.Error: + this.error(message.message); + break; + case MessageType.Warning: + this.warn(message.message); + break; + case MessageType.Info: + this.info(message.message); + break; + default: + this.outputChannel.appendLine(message.message); + } + }); + connection.onShowMessage((message) => { + switch (message.type) { + case MessageType.Error: + Window.showErrorMessage(message.message); + break; + case MessageType.Warning: + Window.showWarningMessage(message.message); + break; + case MessageType.Info: + Window.showInformationMessage(message.message); + break; + default: + Window.showInformationMessage(message.message); + } + }); + connection.onRequest(ShowMessageRequest.type, (params) => { + let messageFunc: (message: string, ...items: T[]) => Thenable = null; + switch (params.type) { + case MessageType.Error: + messageFunc = Window.showErrorMessage; + break; + case MessageType.Warning: + messageFunc = Window.showWarningMessage; + break; + case MessageType.Info: + messageFunc = Window.showInformationMessage; + break; + default: + messageFunc = Window.showInformationMessage; + } + return messageFunc(params.message, ...params.actions); + }); + connection.onTelemetry((data) => { + this._telemetryEmitter.fire(data); + }); + + connection.listen(); + // Error is handled in the intialize call. + this.initialize(connection).then(null, (error) => { }); + }, (error) => { + this.state = ClientState.StartFailed; + this._onReadyCallbacks.reject(error); + this.error('Starting client failed', error); + }); + return new Disposable(() => { + if (this.needsStop()) { + this.stop(); + } + }); + } + + private resolveConnection(): Thenable { + if (!this._connection) { + this._connection = this.createConnection(); + } + return this._connection; + } + + private initialize(connection: IConnection): Thenable { + this.refreshTrace(connection, false); + let initOption = this._clientOptions.initializationOptions; + let initParams: InitializeParams = { + processId: process.pid, + rootPath: Workspace.rootPath, + capabilities: {}, + initializationOptions: is.func(initOption) ? initOption() : initOption, + trace: Trace.toString(this._trace) + }; + return connection.initialize(initParams).then((result) => { + this.state = ClientState.Running; + this._capabilites = result.capabilities; + connection.onDiagnostics(params => this.handleDiagnostics(params)); + if (this._capabilites.textDocumentSync !== TextDocumentSyncKind.None) { + Workspace.onDidOpenTextDocument(t => this.onDidOpenTextDoument(connection, t), null, this._listeners); + Workspace.onDidChangeTextDocument(t => this.onDidChangeTextDocument(connection, t), null, this._listeners); + Workspace.onDidCloseTextDocument(t => this.onDidCloseTextDoument(connection, t), null, this._listeners); + Workspace.onDidSaveTextDocument(t => this.onDidSaveTextDocument(connection, t), null, this._listeners); + if (this._capabilites.textDocumentSync === TextDocumentSyncKind.Full) { + this._documentSyncDelayer = new Delayer(100); + } + } + this.hookFileEvents(connection); + this.hookConfigurationChanged(connection); + this.hookCapabilities(connection); + this._onReadyCallbacks.resolve(); + + Workspace.textDocuments.forEach(t => this.onDidOpenTextDoument(connection, t)); + return result; + }, (error: any) => { + if (this._clientOptions.initializationFailedHandler) { + if (this._clientOptions.initializationFailedHandler(error)) { + this.initialize(connection); + } else { + this.stop(); + this._onReadyCallbacks.reject(error); + } + } else if (error instanceof ResponseError && error.data && error.data.retry) { + Window.showErrorMessage(error.message, { title: 'Retry', id: "retry" }).then(item => { + if (is.defined(item) && item.id === 'retry') { + this.initialize(connection); + } else { + this.stop(); + this._onReadyCallbacks.reject(error); + } + }); + } else { + if (error && error.message) { + Window.showErrorMessage(error.message); + } + this.error('Server initialization failed.', error); + this.stop(); + this._onReadyCallbacks.reject(error); + } + }); + } + + public stop(): Thenable { + if (!this._connection) { + this.state = ClientState.Stopped; + return; + } + this.state = ClientState.Stopping; + this.cleanUp(); + // unkook listeners + return this.resolveConnection().then(connection => { + connection.shutdown().then(() => { + connection.exit(); + connection.dispose(); + this.state = ClientState.Stopped; + this._connection = null; + let toCheck = this._childProcess; + this._childProcess = null; + // Remove all markers + this.checkProcessDied(toCheck); + }); + }); + } + + private cleanUp(diagnostics: boolean = true): void { + if (this._listeners) { + this._listeners.forEach(listener => listener.dispose()); + this._listeners = null; + } + if (this._providers) { + this._providers.forEach(provider => provider.dispose()); + this._providers = null; + } + if (diagnostics) { + this._diagnostics.dispose(); + this._diagnostics = null; + } + } + + private notifyConfigurationChanged(settings: any): void { + this.onReady().then(() => { + this.resolveConnection().then(connection => { + if (this.isConnectionActive()) { + connection.didChangeConfiguration({ settings }); + } + }, (error) => { + this.error(`Syncing settings failed.`, JSON.stringify(error, null, 4)); + }); + }, (error) => { + this.error(`Syncing settings failed.`, JSON.stringify(error, null, 4)); + }); + } + + private notifyFileEvent(event: FileEvent): void { + this._fileEvents.push(event); + this._fileEventDelayer.trigger(() => { + this.onReady().then(() => { + this.resolveConnection().then(connection => { + if (this.isConnectionActive()) { + connection.didChangeWatchedFiles({ changes: this._fileEvents }); + } + this._fileEvents = []; + }); + }, (error) => { + this.error(`Notify file events failed.`, error); + }); + }); + } + + private onDidOpenTextDoument(connection: IConnection, textDocument: TextDocument): void { + if (!this._syncExpression.evaluate(textDocument)) { + return; + } + connection.didOpenTextDocument(this._c2p.asOpenTextDocumentParams(textDocument)); + } + + private onDidChangeTextDocument(connection: IConnection, event: TextDocumentChangeEvent): void { + if (!this._syncExpression.evaluate(event.document)) { + return; + } + let uri: string = event.document.uri.toString(); + if (this._capabilites.textDocumentSync === TextDocumentSyncKind.Incremental) { + connection.didChangeTextDocument(this._c2p.asChangeTextDocumentParams(event)); + } else { + if (this._documentSyncDelayer) { + this._documentSyncDelayer.trigger(() => { + connection.didChangeTextDocument(this._c2p.asChangeTextDocumentParams(event.document)); + }, -1); + } + } + } + + private onDidCloseTextDoument(connection: IConnection, textDocument: TextDocument): void { + if (!this._syncExpression.evaluate(textDocument)) { + return; + } + connection.didCloseTextDocument(this._c2p.asCloseTextDocumentParams(textDocument)); + } + + private onDidSaveTextDocument(conneciton: IConnection, textDocument: TextDocument): void { + if (!this._syncExpression.evaluate(textDocument)) { + return; + } + conneciton.didSaveTextDocument(this._c2p.asSaveTextDocumentParams(textDocument)); + } + + private forceDocumentSync(): void { + if (this._documentSyncDelayer) { + this._documentSyncDelayer.forceDelivery(); + } + } + + private handleDiagnostics(params: PublishDiagnosticsParams) { + let uri = Uri.parse(params.uri); + let diagnostics = this._p2c.asDiagnostics(params.diagnostics); + this._diagnostics.set(uri, diagnostics); + } + + private createConnection(): Thenable { + function getEnvironment(env: any): any { + if (!env) { + return process.env; + } + let result: any = Object.create(null); + Object.keys(process.env).forEach(key => result[key] = process.env[key]); + Object.keys(env).forEach(key => result[key] = env[key]); + } + + function startedInDebugMode(): boolean { + let args = (process as any).execArgv; + if (args) { + return args.some((arg) => /^--debug=?/.test(arg) || /^--debug-brk=?/.test(arg)); + }; + return false; + } + + let encoding = this._clientOptions.stdioEncoding || 'utf8'; + + let errorHandler = (error: Error, message: Message, count: number) => { + this.handleConnectionError(error, message, count); + } + + let closeHandler = () => { + this.handleConnectionClosed(); + } + + let server = this._serverOptions; + // We got a function. + if (is.func(server)) { + return server().then((result) => { + let info = result as StreamInfo; + if (info.writer && info.reader) { + return createConnection(info.reader, info.writer, errorHandler, closeHandler); + } else { + let cp = result as ChildProcess; + return createConnection(cp.stdout, cp.stdin, errorHandler, closeHandler); + } + }); + } + let json: { command?: string; module?: string } = null; + let runDebug = <{ run: any; debug: any; }>server; + if (is.defined(runDebug.run) || is.defined(runDebug.debug)) { + // We are under debugging. So use debug as well. + if (typeof v8debug === 'object' || this._forceDebug || startedInDebugMode()) { + json = runDebug.debug; + } else { + json = runDebug.run; + } + } else { + json = server; + } + if (is.defined(json.module)) { + let node: NodeModule = json; + if (node.runtime) { + let args: string[] = []; + let options: ForkOptions = node.options || Object.create(null); + if (options.execArgv) { + options.execArgv.forEach(element => args.push(element)); + } + args.push(node.module); + if (node.args) { + node.args.forEach(element => args.push(element)); + } + let execOptions: ExecutableOptions = Object.create(null); + execOptions.cwd = options.cwd || Workspace.rootPath; + execOptions.env = getEnvironment(options.env); + if (node.transport === TransportKind.ipc) { + execOptions.stdio = [null, null, null, 'ipc']; + args.push('--node-ipc'); + } else if (node.transport === TransportKind.stdio) { + args.push('--stdio'); + } + let process = cp.spawn(node.runtime, args, execOptions); + if (!process || !process.pid) { + return Promise.reject(`Launching server using runtime ${node.runtime} failed.`); + } + this._childProcess = process; + process.stderr.on('data', data => this.outputChannel.append(data.toString())); + if (node.transport === TransportKind.ipc) { + process.stdout.on('data', data => this.outputChannel.append(data.toString())); + return Promise.resolve(createConnection(new IPCMessageReader(process), new IPCMessageWriter(process), errorHandler, closeHandler)); + } else { + return Promise.resolve(createConnection(process.stdout, process.stdin, errorHandler, closeHandler)); + } + } else { + return new Promise((resolve, reject) => { + let args = node.args && node.args.slice() || []; + if (node.transport === TransportKind.ipc) { + args.push('--node-ipc'); + } else if (node.transport === TransportKind.stdio) { + args.push('--stdio'); + } + let options: ForkOptions = node.options || Object.create(null); + options.execArgv = options.execArgv || []; + options.cwd = options.cwd || Workspace.rootPath; + electron.fork(node.module, args || [], options, (error, cp) => { + if (error) { + reject(error); + } else { + this._childProcess = cp; + cp.stderr.on('data', data => this.outputChannel.append(data.toString())); + if (node.transport === TransportKind.ipc) { + cp.stdout.on('data', data => this.outputChannel.append(data.toString())); + resolve(createConnection(new IPCMessageReader(this._childProcess), new IPCMessageWriter(this._childProcess), errorHandler, closeHandler)); + } else { + resolve(createConnection(cp.stdout, cp.stdin, errorHandler, closeHandler)); + } + } + }); + }); + } + } else if (is.defined(json.command)) { + let command: Executable = json; + let options = command.options || {}; + options.cwd = options.cwd || Workspace.rootPath; + let process = cp.spawn(command.command, command.args, command.options); + if (!process || !process.pid) { + return Promise.reject(`Launching server using command ${command.command} failed.`); + } + process.stderr.on('data', data => this.outputChannel.append(data.toString())); + this._childProcess = process; + return Promise.resolve(createConnection(process.stdout, process.stdin, errorHandler, closeHandler)); + } + return Promise.reject(new Error(`Unsupported server configuartion ` + JSON.stringify(server, null, 4))); + } + + private handleConnectionClosed() { + + let self = this; + + // Check whether this is a normal shutdown in progress or the client stopped normally. + if (this.state === ClientState.Stopping || this.state === ClientState.Stopped) { + return; + } + + self._connection = null; + self._childProcess = null; + let action = this._clientOptions.errorHandler.closed(); + if (action === CloseAction.DoNotRestart) { + self.error('Connection to server got closed. Server will not be restarted.'); + self.state = ClientState.Stopped; + self.cleanUp(); + } else if (action === CloseAction.Restart && self.state !== ClientState.Stopping) { + self.info('Connection to server got closed. Server will restart.'); + self.cleanUp(false); + self.state = ClientState.Initial; + self.start(); + } + } + + private handleConnectionError(error: Error, message: Message, count: number) { + let action = this._clientOptions.errorHandler.error(error, message, count); + if (action === ErrorAction.Shutdown) { + this.error('Connection to server is erroring. Shutting down server.') + this.stop(); + } + } + + private checkProcessDied(childProcess: ChildProcess): void { + if (!childProcess) { + return; + } + setTimeout(() => { + // Test if the process is still alive. Throws an exception if not + try { + process.kill(childProcess.pid, 0); + terminate(childProcess); + } catch (error) { + // All is fine. + } + }, 2000); + } + + private hookConfigurationChanged(connection: IConnection): void { + if (!this._clientOptions.synchronize.configurationSection) { + return; + } + Workspace.onDidChangeConfiguration(e => this.onDidChangeConfiguration(connection), this, this._listeners); + this.onDidChangeConfiguration(connection); + } + + private refreshTrace(connection: IConnection, sendNotification: boolean = false): void { + let config = Workspace.getConfiguration(this._id); + let trace: Trace = Trace.Off; + if (config) { + trace = Trace.fromString(config.get('trace.server', 'off')); + } + this._trace = trace; + connection.trace(this._trace, this._tracer, sendNotification); + } + + private onDidChangeConfiguration(connection: IConnection): void { + this.refreshTrace(connection, true); + let keys: string[] = null; + let configurationSection = this._clientOptions.synchronize.configurationSection; + if (is.string(configurationSection)) { + keys = [configurationSection]; + } else if (is.stringArray(configurationSection)) { + keys = configurationSection; + } + if (keys) { + if (this.isConnectionActive()) { + connection.didChangeConfiguration({ settings: this.extractSettingsInformation(keys) }); + } + } + } + + private extractSettingsInformation(keys: string[]): any { + function ensurePath(config: any, path: string[]): any { + let current = config; + for (let i = 0; i < path.length - 1; i++) { + let obj = current[path[i]]; + if (!obj) { + obj = Object.create(null); + current[path[i]] = obj; + } + current = obj; + } + return current; + } + let result = Object.create(null); + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + let index: number = key.indexOf('.'); + let config: any = null; + if (index >= 0) { + config = Workspace.getConfiguration(key.substr(0, index)).get(key.substr(index + 1)); + } else { + config = Workspace.getConfiguration(key); + } + if (config) { + let path = keys[i].split('.'); + ensurePath(result, path)[path[path.length - 1]] = config; + } + } + return result; + } + + private hookFileEvents(connection: IConnection): void { + let fileEvents = this._clientOptions.synchronize.fileEvents; + if (!fileEvents) { + return; + } + let watchers: FileSystemWatcher[] = null; + if (is.array(fileEvents)) { + watchers = fileEvents; + } else { + watchers = [fileEvents]; + } + if (!watchers) { + return; + } + watchers.forEach(watcher => { + watcher.onDidCreate((resource) => this.notifyFileEvent( + { + uri: resource.toString(), + type: FileChangeType.Created + } + ), null, this._listeners); + watcher.onDidChange((resource) => this.notifyFileEvent( + { + uri: resource.toString(), + type: FileChangeType.Changed + } + + ), null, this._listeners); + watcher.onDidDelete((resource) => this.notifyFileEvent( + { + uri: resource.toString(), + type: FileChangeType.Deleted + } + ), null, this._listeners); + }); + } + + private hookCapabilities(connection: IConnection): void { + let documentSelector = this._clientOptions.documentSelector; + if (!documentSelector) { + return; + } + this.hookCompletionProvider(documentSelector, connection); + this.hookHoverProvider(documentSelector, connection); + this.hookSignatureHelpProvider(documentSelector, connection); + this.hookDefinitionProvider(documentSelector, connection); + this.hookReferencesProvider(documentSelector, connection); + this.hookDocumentHighlightProvider(documentSelector, connection); + this.hookDocumentSymbolProvider(documentSelector, connection); + this.hookWorkspaceSymbolProvider(connection); + this.hookCodeActionsProvider(documentSelector, connection); + this.hookCodeLensProvider(documentSelector, connection); + this.hookDocumentFormattingProvider(documentSelector, connection); + this.hookDocumentRangeFormattingProvider(documentSelector, connection); + this.hookDocumentOnTypeFormattingProvider(documentSelector, connection); + this.hookRenameProvider(documentSelector, connection); + this.hookDocumentLinkProvider(documentSelector, connection); + } + + private logFailedRequest(type: RequestType, error: any): void { + this.error(`Request ${type.method} failed.`, error); + } + + /** + * SQL-Carbon Edit + * The helper method to add connection notifications after waiting for connections to be ready. + * This is needed for early DMP registration, which can be done without waiting for connection setup to finish. + * + * @param type + * @param handler + */ + private onConnectionReadyNotification

(type: NotificationType

, handler: NotificationHandler

): void { + this.onReady().then(() => { + this.resolveConnection().then((connection) => { + connection.onNotification(type, handler); + }); + } + ); + } + + private hookDataProtocolProvider(providerId: string): void { + let self = this; + + let capabilitiesProvider: CapabilitiesProvider = { + getServerCapabilities(client: DataProtocolClientCapabilities): Thenable { + let capabilitiesPromise = self._clientOptions.serverConnectionMetadata === undefined ? + self.sendRequest(CapabiltiesDiscoveryRequest.type, self._c2p.asCapabilitiesParams(client), undefined) : + new Promise((resolve, reject) => resolve(self._clientOptions.serverConnectionMetadata)); + + return capabilitiesPromise.then(self._p2c.asServerCapabilities, (error) => { + self.logFailedRequest(ConnectionRequest.type, error); + return Promise.resolve([]); + } + ); + } + }; + + let connectionProvider: ConnectionProvider = { + handle: -1, + + connect(connUri: string, connInfo: ConnectionInfo): Thenable { + return self.sendRequest(ConnectionRequest.type, self._c2p.asConnectionParams(connUri, connInfo), undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(ConnectionRequest.type, error); + return Promise.resolve(false); + } + ); + }, + + disconnect(connUri: string): Thenable { + let params: DisconnectParams = { + ownerUri: connUri + }; + + return self.sendRequest(DisconnectRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(DisconnectRequest.type, error); + return Promise.resolve(false); + } + ); + }, + + cancelConnect(connUri: string): Thenable { + let params: CancelConnectParams = { + ownerUri: connUri + }; + + return self.sendRequest(CancelConnectRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(CancelConnectRequest.type, error); + return Promise.resolve(false); + } + ); + }, + + changeDatabase(connUri: string, newDatabase: string): Thenable { + let params: ChangeDatabaseParams = { + ownerUri: connUri, + newDatabase: newDatabase + }; + + return self.sendRequest(ChangeDatabaseRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(ChangeDatabaseRequest.type, error); + return Promise.resolve(false); + } + ); + }, + + listDatabases(connectionUri: string): Thenable { + let params: ListDatabasesParams = { + ownerUri: connectionUri + }; + + return self.sendRequest(ListDatabasesRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(ListDatabasesRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + rebuildIntelliSenseCache(connectionUri: string): Thenable { + let params: RebuildIntelliSenseParams = { + ownerUri: connectionUri + }; + + self.sendNotification(RebuildIntelliSenseNotification.type, params); + return Promise.resolve(undefined); + }, + + registerOnConnectionComplete(handler: (connSummary: ConnectionInfoSummary) => any) { + self.onConnectionReadyNotification(ConnectionCompleteNotification.type, (params: ConnectionCompleteParams) => { + handler({ + ownerUri: params.ownerUri, + connectionId: params.connectionId, + messages: params.messages, + errorMessage: params.errorMessage, + errorNumber: params.errorNumber, + serverInfo: params.serverInfo, + connectionSummary: params.connectionSummary + }); + }); + }, + + registerOnIntelliSenseCacheComplete(handler: (connectionUri: string) => any) { + self.onConnectionReadyNotification(IntelliSenseReadyNotification.type, (params: IntelliSenseReadyParams) => { + handler(params.ownerUri); + }); + }, + + registerOnConnectionChanged(handler: (changedConnInfo: ChangedConnectionInfo) => any) { + self.onConnectionReadyNotification(ConnectionChangedNotification.type, (params: ConnectionChangedParams) => { + handler({ + connectionUri: params.ownerUri, + connection: params.connection + }); + }); + } + }; + + let queryProvider: QueryProvider = { + handle: -1, + queryType: 'MSSQL', + cancelQuery(ownerUri: string): Thenable { + let params: QueryCancelParams = { ownerUri: ownerUri }; + return self.sendRequest(QueryCancelRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(QueryCancelRequest.type, error); + return Promise.reject(error); + } + ); + }, + + runQuery(ownerUri: string, selection: ISelectionData, executionPlanOptions?: ExecutionPlanOptions): Thenable { + let params: QueryExecuteParams = { + ownerUri: ownerUri, + querySelection: selection, + executionPlanOptions: self._c2p.asExecutionPlanOptions(executionPlanOptions) + }; + return self.sendRequest(QueryExecuteRequest.type, params, undefined).then( + (result) => { + return undefined; + }, + (error) => { + self.logFailedRequest(QueryExecuteRequest.type, error); + return Promise.reject(error); + } + ); + }, + + runQueryStatement(ownerUri: string, line: number, column: number): Thenable { + let params: QueryExecuteStatementParams = { + ownerUri: ownerUri, + line: line, + column: column + }; + return self.sendRequest(QueryExecuteStatementRequest.type, params, undefined).then( + (result) => { + return undefined; + }, + (error) => { + self.logFailedRequest(QueryExecuteStatementRequest.type, error); + return Promise.reject(error); + } + ); + }, + + runQueryString(ownerUri: string, queryString: string): Thenable { + let params: QueryExecuteStringParams = { ownerUri: ownerUri, query: queryString }; + return self.sendRequest(QueryExecuteStringRequest.type, params, undefined).then( + (result) => { + return undefined; + }, + (error) => { + self.logFailedRequest(QueryExecuteStringRequest.type, error); + return Promise.reject(error); + } + ); + }, + + runQueryAndReturn(ownerUri: string, queryString: string): Thenable { + let params: SimpleExecuteParams = { ownerUri: ownerUri, queryString: queryString }; + return self.sendRequest(SimpleExecuteRequest.type, params, undefined).then( + result => { + return result; + }, + error => { + self.logFailedRequest(SimpleExecuteRequest.type, error); + return Promise.reject(error); + } + ); + }, + + getQueryRows(rowData: QueryExecuteSubsetParams): Thenable { + return self.sendRequest(QueryExecuteSubsetRequest.type, rowData, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(QueryExecuteSubsetRequest.type, error); + return Promise.reject(error); + } + ); + }, + + disposeQuery(ownerUri: string): Thenable { + let params: QueryDisposeParams = { ownerUri: ownerUri }; + return self.sendRequest(QueryDisposeRequest.type, params, undefined).then( + (result) => { + return undefined; + }, + (error) => { + self.logFailedRequest(QueryDisposeRequest.type, error); + return Promise.reject(error); + } + ); + }, + + registerOnQueryComplete(handler: (result: QueryExecuteCompleteNotificationResult) => any) { + self.onConnectionReadyNotification(QueryExecuteCompleteNotification.type, (params: QueryExecuteCompleteNotificationResult) => { + handler({ + ownerUri: params.ownerUri, + batchSummaries: params.batchSummaries + }); + }); + }, + + registerOnBatchStart(handler: (batchInfo: QueryExecuteBatchNotificationParams) => any) { + self.onConnectionReadyNotification(QueryExecuteBatchStartNotification.type, (params: QueryExecuteBatchNotificationParams) => { + handler({ + batchSummary: params.batchSummary, + ownerUri: params.ownerUri + }); + }); + }, + + registerOnBatchComplete(handler: (batchInfo: QueryExecuteBatchNotificationParams) => any) { + self.onConnectionReadyNotification(QueryExecuteBatchCompleteNotification.type, (params: QueryExecuteBatchNotificationParams) => { + handler({ + batchSummary: params.batchSummary, + ownerUri: params.ownerUri + }); + }); + }, + registerOnResultSetComplete(handler: (resultSetInfo: QueryExecuteResultSetCompleteNotificationParams) => any) { + self.onConnectionReadyNotification(QueryExecuteResultSetCompleteNotification.type, (params: QueryExecuteResultSetCompleteNotificationParams) => { + handler({ + ownerUri: params.ownerUri, + resultSetSummary: params.resultSetSummary + }); + }); + }, + registerOnMessage(handler: (message: QueryExecuteMessageParams) => any) { + self.onConnectionReadyNotification(QueryExecuteMessageNotification.type, (params: QueryExecuteMessageParams) => { + handler({ + message: params.message, + ownerUri: params.ownerUri + }); + }); + }, + saveResults(requestParams: VSaveResultsRequestParams): Thenable { + switch (requestParams.resultFormat) { + case 'csv': + return self.sendRequest(SaveResultsAsCsvRequest.type, requestParams, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(EditCommitRequest.type, error); + return Promise.reject(error); + } + ); + case 'json': + return self.sendRequest(SaveResultsAsJsonRequest.type, requestParams, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(EditCommitRequest.type, error); + return Promise.reject(error); + } + ); + case 'excel': + return self.sendRequest(SaveResultsAsExcelRequest.type, requestParams, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(EditCommitRequest.type, error); + return Promise.reject(error); + } + ); + default: + return Promise.reject('unsupported format'); + } + }, + + // Edit Data Requests + commitEdit(ownerUri: string): Thenable { + let params: EditCommitParams = { ownerUri: ownerUri }; + return self.sendRequest(EditCommitRequest.type, params, undefined).then( + (result) => { + return undefined; + }, + (error) => { + self.logFailedRequest(EditCommitRequest.type, error); + return Promise.reject(error); + } + ); + }, + + createRow(ownerUri: string): Thenable { + let params: EditCreateRowParams = { ownerUri: ownerUri }; + return self.sendRequest(EditCreateRowRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(EditCreateRowRequest.type, error); + return Promise.reject(error); + } + ); + }, + + deleteRow(ownerUri: string, rowId: number): Thenable { + let params: EditDeleteRowParams = { ownerUri: ownerUri, rowId: rowId }; + return self.sendRequest(EditDeleteRowRequest.type, params, undefined).then( + (result) => { + return undefined; + }, + (error) => { + self.logFailedRequest(EditDeleteRowRequest.type, error); + return Promise.reject(error); + } + ); + }, + + disposeEdit(ownerUri: string): Thenable { + let params: EditDisposeParams = { ownerUri: ownerUri }; + return self.sendRequest(EditDisposeRequest.type, params, undefined).then( + (result) => { + return undefined; + }, + (error) => { + self.logFailedRequest(EditDisposeRequest.type, error); + return Promise.reject(error); + } + ); + }, + + initializeEdit(ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): Thenable { + let filters: EditInitializeFiltering = { LimitResults: rowLimit }; + let params: EditInitializeParams = { ownerUri: ownerUri, schemaName: schemaName, objectName: objectName, objectType: objectType, filters: filters }; + return self.sendRequest(EditInitializeRequest.type, params, undefined).then( + (result) => { + return undefined; + }, + (error) => { + self.logFailedRequest(EditInitializeRequest.type, error); + return Promise.reject(error); + } + ); + }, + + revertCell(ownerUri: string, rowId: number, columnId: number): Thenable { + let params: EditRevertCellParams = { ownerUri: ownerUri, rowId: rowId, columnId: columnId }; + return self.sendRequest(EditRevertCellRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(EditRevertCellRequest.type, error); + return Promise.reject(error); + } + ); + }, + + revertRow(ownerUri: string, rowId: number): Thenable { + let params: EditRevertRowParams = { ownerUri: ownerUri, rowId: rowId }; + return self.sendRequest(EditRevertRowRequest.type, params, undefined).then( + (result) => { + return undefined; + }, + (error) => { + self.logFailedRequest(EditRevertRowRequest.type, error); + return Promise.reject(error); + } + ); + }, + + updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable { + let params: EditUpdateCellParams = { ownerUri: ownerUri, rowId: rowId, columnId: columnId, newValue: newValue }; + return self.sendRequest(EditUpdateCellRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(EditUpdateCellRequest.type, error); + return Promise.reject(error); + } + ); + }, + + getEditRows(rowData: EditSubsetParams): Thenable { + return self.sendRequest(EditSubsetRequest.type, rowData, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(EditSubsetRequest.type, error); + return Promise.reject(error); + } + ); + }, + + // Edit Data Event Handlers + registerOnEditSessionReady(handler: (ownerUri: string, success: boolean, message: string) => any): void { + self.onConnectionReadyNotification(EditSessionReadyNotification.type, (params: EditSessionReadyParams) => { + handler(params.ownerUri, params.success, params.message); + }); + }, + }; + + let metadataProvider: MetadataProvider = { + getMetadata(connectionUri: string): Thenable { + return self.sendRequest(MetadataQueryRequest.type, + self._c2p.asMetadataQueryParams(connectionUri), undefined).then( + self._p2c.asProviderMetadata, + (error) => { + self.logFailedRequest(MetadataQueryRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + getDatabases(connectionUri: string): Thenable { + return self.sendRequest(ListDatabasesRequest.type, + self._c2p.asListDatabasesParams(connectionUri), undefined).then( + (result) => { + return result.databaseNames; + }, + (error) => { + self.logFailedRequest(ListDatabasesRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + getTableInfo(connectionUri: string, metadata: ObjectMetadata) { + return self.sendRequest(TableMetadataRequest.type, + self._c2p.asTableMetadataParams(connectionUri, metadata), undefined).then( + (result) => { + return result.columns; + }, + (error) => { + self.logFailedRequest(TableMetadataRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + getViewInfo(connectionUri: string, metadata: ObjectMetadata) { + return self.sendRequest(ViewMetadataRequest.type, + self._c2p.asTableMetadataParams(connectionUri, metadata), undefined).then( + (result) => { + return result.columns; + }, + (error) => { + self.logFailedRequest(ViewMetadataRequest.type, error); + return Promise.resolve(undefined); + } + ); + } + }; + + let adminServicesProvider: AdminServicesProvider = { + createDatabase(connectionUri: string, database: DatabaseInfo): Thenable { + let params: CreateDatabaseParams = { ownerUri: connectionUri, databaseInfo: database }; + return self.sendRequest(CreateDatabaseRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(CreateDatabaseRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + getDefaultDatabaseInfo(connectionUri: string): Thenable { + let params: DefaultDatabaseInfoParams = { ownerUri: connectionUri }; + return self.sendRequest(DefaultDatabaseInfoRequest.type, params, undefined).then( + (result) => { + return result.defaultDatabaseInfo; + }, + (error) => { + self.logFailedRequest(DefaultDatabaseInfoRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + getDatabaseInfo(connectionUri: string): Thenable { + let params: GetDatabaseInfoParams = { ownerUri: connectionUri }; + return self.sendRequest(GetDatabaseInfoRequest.type, params, undefined).then( + (result) => { + return result.databaseInfo; + }, + (error) => { + self.logFailedRequest(GetDatabaseInfoRequest.type, error); + return Promise.reject(error); + } + ); + }, + createLogin(connectionUri: string, login: LoginInfo): Thenable { + let params: CreateLoginParams = { ownerUri: connectionUri, loginInfo: login }; + return self.sendRequest(CreateLoginRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(CreateLoginRequest.type, error); + return Promise.resolve(undefined); + } + ); + } + }; + + let disasterRecoveryProvider: DisasterRecoveryProvider = { + backup(connectionUri: string, backupInfo: BackupInfo, taskExecutionMode: TaskExecutionMode): Thenable { + let params: BackupParams = { ownerUri: connectionUri, backupInfo: backupInfo, taskExecutionMode: taskExecutionMode }; + return self.sendRequest(BackupRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(BackupRequest.type, error); + return Promise.resolve([]); + } + ); + }, + getBackupConfigInfo(connectionUri: string): Thenable { + let params: DefaultDatabaseInfoParams = { ownerUri: connectionUri }; + return self.sendRequest(BackupConfigInfoRequest.type, params, undefined).then( + (result) => { + return result.backupConfigInfo; + }, + (error) => { + self.logFailedRequest(BackupConfigInfoRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + getRestorePlan(ownerUri: string, restoreInfo: RestoreInfo): Thenable { + return self.sendRequest(RestorePlanRequest.type, self._c2p.asRestoreParams(ownerUri, restoreInfo), undefined).then( + self._p2c.asRestorePlanResponse, + error => { + self.logFailedRequest(RestorePlanRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + restore(ownerUri: string, restoreInfo: RestoreInfo): Thenable { + return self.sendRequest(RestoreRequest.type, self._c2p.asRestoreParams(ownerUri, restoreInfo), undefined).then( + self._p2c.asRestoreResponse, + error => { + self.logFailedRequest(RestoreRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + getRestoreConfigInfo(ownerUri: string): Thenable { + return self.sendRequest(RestoreConfigInfoRequest.type, self._c2p.asRestoreConfigInfoParams(ownerUri), undefined).then( + self._p2c.asRestoreConfigInfo, + error => { + self.logFailedRequest(RestorePlanRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + cancelRestorePlan(ownerUri: string, restoreInfo: RestoreInfo): Thenable { + return self.sendRequest(CancelRestorePlanRequest.type, self._c2p.asRestoreParams(ownerUri, restoreInfo), undefined).then( + (result) => { + return result; + }, + error => { + self.logFailedRequest(CancelRestorePlanRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + }; + + let objectExplorer: ObjectExplorerProvider = { + createNewSession(connInfo: ConnectionInfo) { + return self.sendRequest(ObjectExplorerCreateSessionRequest.type, + self._c2p.asConnectionDetail(connInfo), undefined).then( + self._p2c.asObjectExplorerCreateSessionResponse, + (error) => { + self.logFailedRequest(ObjectExplorerCreateSessionRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + expandNode(nodeInfo: ExpandNodeInfo) { + return self.sendRequest(ObjectExplorerExpandRequest.type, + self._c2p.asExpandInfo(nodeInfo), undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(ObjectExplorerExpandRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + refreshNode(nodeInfo: ExpandNodeInfo) { + return self.sendRequest(ObjectExplorerRefreshRequest.type, + self._c2p.asExpandInfo(nodeInfo), undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(ObjectExplorerRefreshRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + closeSession(closeSessionInfo: ObjectExplorerCloseSessionInfo) { + return self.sendRequest(ObjectExplorerCloseSessionRequest.type, + self._c2p.asCloseSessionInfo(closeSessionInfo), undefined).then( + self._p2c.asObjectExplorerCloseSessionResponse, + (error) => { + self.logFailedRequest(ObjectExplorerCloseSessionRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + registerOnSessionCreated(handler: (response: ObjectExplorerSession) => any) { + self.onConnectionReadyNotification(ObjectExplorerCreateSessionCompleteNotification.type, (params: ObjectExplorerSession) => { + handler({ + sessionId: params.sessionId, + success: params.success, + rootNode: params.rootNode, + errorMessage: params.errorMessage + }); + }); + }, + + registerOnExpandCompleted(handler: (response: ObjectExplorerExpandInfo) => any) { + self.onConnectionReadyNotification(ObjectExplorerExpandCompleteNotification.type, (params: ObjectExplorerExpandInfo) => { + handler({ + sessionId: params.sessionId, + nodes: params.nodes, + errorMessage: params.errorMessage, + nodePath: params.nodePath + }); + }); + }, + }; + + + + let scriptingProvider: ScriptingProvider = { + scriptAsSelect(connectionUri: string, metadata: ObjectMetadata, paramDetails: ScriptingParamDetails): Thenable { + return self.sendRequest(ScriptingRequest.type, + self._c2p.asScriptingParams(connectionUri, ScriptOperation.Select, metadata, paramDetails), undefined).then( + self._p2c.asScriptingResult, + (error) => { + self.logFailedRequest(ScriptingRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + scriptAsCreate(connectionUri: string, metadata: ObjectMetadata, paramDetails: ScriptingParamDetails): Thenable { + return self.sendRequest(ScriptingRequest.type, + self._c2p.asScriptingParams(connectionUri, ScriptOperation.Create, metadata, paramDetails), undefined).then( + self._p2c.asScriptingResult, + (error) => { + self.logFailedRequest(ScriptingRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + scriptAsInsert(connectionUri: string, metadata: ObjectMetadata, paramDetails: ScriptingParamDetails): Thenable { + return self.sendRequest(ScriptingRequest.type, + self._c2p.asScriptingParams(connectionUri, ScriptOperation.Insert, metadata, paramDetails), undefined).then( + self._p2c.asScriptingResult, + (error) => { + self.logFailedRequest(ScriptingRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + scriptAsUpdate(connectionUri: string, metadata: ObjectMetadata, paramDetails: ScriptingParamDetails): Thenable { + return self.sendRequest(ScriptingRequest.type, + self._c2p.asScriptingParams(connectionUri, ScriptOperation.Update, metadata, paramDetails), undefined).then( + self._p2c.asScriptingResult, + (error) => { + self.logFailedRequest(ScriptingRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + scriptAsDelete(connectionUri: string, metadata: ObjectMetadata, paramDetails: ScriptingParamDetails): Thenable { + return self.sendRequest(ScriptingRequest.type, + self._c2p.asScriptingParams(connectionUri, ScriptOperation.Delete, metadata, paramDetails), undefined).then( + self._p2c.asScriptingResult, + (error) => { + self.logFailedRequest(ScriptingRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + registerOnScriptingComplete(handler: (scriptingCompleteResult: ScriptingCompleteResult) => any) { + self.onConnectionReadyNotification(ScriptingCompleteNotification.type, (params: ScriptingCompleteResult) => { + handler({ + canceled: params.canceled, + errorDetails: params.errorDetails, + errorMessage: params.errorMessage, + hasError: params.hasError, + success: params.success, + operationId: params.operationId + }); + }); + }, + }; + + let taskServicesProvider: TaskServicesProvider = { + getAllTasks(listTasksParams: ListTasksParams): Thenable { + return self.sendRequest(ListTasksRequest.type, + self._c2p.asListTasksParams(listTasksParams), undefined).then( + self._p2c.asListTasksResponse, + (error) => { + self.logFailedRequest(ListTasksRequest.type, error); + return Promise.resolve(undefined); + } + ); + + }, + cancelTask(cancelTaskParams: CancelTaskParams): Thenable { + return self.sendRequest(CancelTaskRequest.type, + self._c2p.asCancelTaskParams(cancelTaskParams), undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(CancelTaskRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + registerOnTaskCreated(handler: (response: TaskInfo) => any) { + self.onConnectionReadyNotification(TaskCreatedNotification.type, (params: TaskInfo) => { + handler(self._p2c.asTaskInfo(params)); + }); + }, + + registerOnTaskStatusChanged(handler: (response: TaskProgressInfo) => any) { + self.onConnectionReadyNotification(TaskStatusChangedNotification.type, (params: TaskProgressInfo) => { + handler({ + taskId: params.taskId, + status: params.status, + message: params.message, + script: params.script, + duration: params.duration + }); + }); + } + }; + + let fileBrowserProvider: FileBrowserProvider = { + openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable { + let params: FileBrowserOpenParams = { ownerUri: ownerUri, expandPath: expandPath, fileFilters: fileFilters, changeFilter: changeFilter }; + return self.sendRequest(FileBrowserOpenRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(FileBrowserOpenRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + registerOnFileBrowserOpened(handler: (response: FileBrowserOpenedParams) => any) { + self.onConnectionReadyNotification(FileBrowserOpenedNotification.type, (params: FileBrowserOpenedParams) => { + handler(params); + }); + }, + + expandFolderNode(ownerUri: string, expandPath: string): Thenable { + let params: FileBrowserExpandParams = { ownerUri: ownerUri, expandPath: expandPath }; + return self.sendRequest(FileBrowserExpandRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(FileBrowserExpandRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + registerOnFolderNodeExpanded(handler: (response: FileBrowserExpandedParams) => any) { + self.onConnectionReadyNotification(FileBrowserExpandedNotification.type, (params: FileBrowserExpandedParams) => { + handler(params); + }); + }, + + validateFilePaths(ownerUri: string, serviceType: string, selectedFiles: string[]): Thenable { + let params: FileBrowserValidateParams = { ownerUri: ownerUri, serviceType: serviceType, selectedFiles: selectedFiles }; + return self.sendRequest(FileBrowserValidateRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(FileBrowserValidateRequest.type, error); + return Promise.resolve(undefined); + } + ); + }, + + registerOnFilePathsValidated(handler: (response: FileBrowserValidatedParams) => any) { + self.onConnectionReadyNotification(FileBrowserValidatedNotification.type, (params: FileBrowserValidatedParams) => { + handler(params); + }); + }, + + closeFileBrowser(ownerUri: string): Thenable { + let params: FileBrowserCloseParams = { ownerUri: ownerUri }; + return self.sendRequest(FileBrowserCloseRequest.type, params, undefined).then( + (result) => { + return result; + }, + (error) => { + self.logFailedRequest(FileBrowserCloseRequest.type, error); + return Promise.resolve(undefined); + } + ); + } + }; + + let serializationProvider: SerializationProvider = { + handle: 0, + saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable { + throw new Error('NotImplemented'); + } + } + + this._providers.push(dataprotocol.registerProvider({ + handle: -1, + + providerId: providerId, + + capabilitiesProvider: capabilitiesProvider, + + connectionProvider: connectionProvider, + + queryProvider: queryProvider, + + metadataProvider: metadataProvider, + + scriptingProvider: scriptingProvider, + + objectExplorerProvider: objectExplorer, + + adminServicesProvider: adminServicesProvider, + + disasterRecoveryProvider: disasterRecoveryProvider, + + taskServicesProvider: taskServicesProvider, + + fileBrowserProvider: fileBrowserProvider + })); + + // Hook to the workspace-wide notifications that aren't routed to a specific provider + dataprotocol.onDidChangeLanguageFlavor(e => { + self.sendNotification(LanguageFlavorChangedNotification.type, e); + }, this, this._listeners); + } + + private hookCompletionProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.completionProvider) { + return; + } + + this._providers.push(Languages.registerCompletionItemProvider(documentSelector, { + provideCompletionItems: (document: TextDocument, position: VPosition, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, CompletionRequest.type, this._c2p.asTextDocumentPositionParams(document, position), token).then( + this._p2c.asCompletionResult, + (error) => { + this.logFailedRequest(CompletionRequest.type, error); + return Promise.resolve([]); + } + ); + }, + resolveCompletionItem: this._capabilites.completionProvider.resolveProvider + ? (item: VCompletionItem, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, CompletionResolveRequest.type, this._c2p.asCompletionItem(item), token).then( + this._p2c.asCompletionItem, + (error) => { + this.logFailedRequest(CompletionResolveRequest.type, error); + return Promise.resolve(item); + } + ); + } + : undefined + }, ...this._capabilites.completionProvider.triggerCharacters)); + } + + private hookHoverProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.hoverProvider) { + return; + } + + this._providers.push(Languages.registerHoverProvider(documentSelector, { + provideHover: (document: TextDocument, position: VPosition, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, HoverRequest.type, this._c2p.asTextDocumentPositionParams(document, position), token).then( + this._p2c.asHover, + (error) => { + this.logFailedRequest(HoverRequest.type, error); + return Promise.resolve(null); + } + ); + } + })); + } + + private hookSignatureHelpProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.signatureHelpProvider) { + return; + } + this._providers.push(Languages.registerSignatureHelpProvider(documentSelector, { + provideSignatureHelp: (document: TextDocument, position: VPosition, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, SignatureHelpRequest.type, this._c2p.asTextDocumentPositionParams(document, position), token).then( + this._p2c.asSignatureHelp, + (error) => { + this.logFailedRequest(SignatureHelpRequest.type, error); + return Promise.resolve(null); + } + ); + } + }, ...this._capabilites.signatureHelpProvider.triggerCharacters)); + } + + private hookDefinitionProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.definitionProvider) { + return; + } + this._providers.push(Languages.registerDefinitionProvider(documentSelector, { + provideDefinition: (document: TextDocument, position: VPosition, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, DefinitionRequest.type, this._c2p.asTextDocumentPositionParams(document, position), token).then( + this._p2c.asDefinitionResult, + (error) => { + this.logFailedRequest(DefinitionRequest.type, error); + return Promise.resolve(null); + } + ); + } + })); + } + + private hookReferencesProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.referencesProvider) { + return; + } + this._providers.push(Languages.registerReferenceProvider(documentSelector, { + provideReferences: (document: TextDocument, position: VPosition, options: { includeDeclaration: boolean; }, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, ReferencesRequest.type, this._c2p.asReferenceParams(document, position, options), token).then( + this._p2c.asReferences, + (error) => { + this.logFailedRequest(ReferencesRequest.type, error); + return Promise.resolve([]); + } + ); + } + })); + } + + private hookDocumentHighlightProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.documentHighlightProvider) { + return; + } + this._providers.push(Languages.registerDocumentHighlightProvider(documentSelector, { + provideDocumentHighlights: (document: TextDocument, position: VPosition, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, DocumentHighlightRequest.type, this._c2p.asTextDocumentPositionParams(document, position), token).then( + this._p2c.asDocumentHighlights, + (error) => { + this.logFailedRequest(DocumentHighlightRequest.type, error); + return Promise.resolve([]); + } + ); + } + })); + } + + private hookDocumentSymbolProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.documentSymbolProvider) { + return; + } + this._providers.push(Languages.registerDocumentSymbolProvider(documentSelector, { + provideDocumentSymbols: (document: TextDocument, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, DocumentSymbolRequest.type, this._c2p.asDocumentSymbolParams(document), token).then( + this._p2c.asSymbolInformations, + (error) => { + this.logFailedRequest(DocumentSymbolRequest.type, error); + return Promise.resolve([]); + } + ); + } + })); + } + + private hookWorkspaceSymbolProvider(connection: IConnection): void { + if (!this._capabilites.workspaceSymbolProvider) { + return; + } + this._providers.push(Languages.registerWorkspaceSymbolProvider({ + provideWorkspaceSymbols: (query: string, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, WorkspaceSymbolRequest.type, { query }, token).then( + this._p2c.asSymbolInformations, + (error) => { + this.logFailedRequest(WorkspaceSymbolRequest.type, error); + return Promise.resolve([]); + } + ); + } + })); + } + + private hookCodeActionsProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.codeActionProvider) { + return; + } + this._providers.push(Languages.registerCodeActionsProvider(documentSelector, { + provideCodeActions: (document: TextDocument, range: VRange, context: VCodeActionContext, token: CancellationToken): Thenable => { + let params: CodeActionParams = { + textDocument: this._c2p.asTextDocumentIdentifier(document), + range: this._c2p.asRange(range), + context: this._c2p.asCodeActionContext(context) + }; + return this.doSendRequest(connection, CodeActionRequest.type, params, token).then( + this._p2c.asCommands, + (error) => { + this.logFailedRequest(CodeActionRequest.type, error); + return Promise.resolve([]); + } + ); + } + })); + } + + private hookCodeLensProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.codeLensProvider) { + return; + } + this._providers.push(Languages.registerCodeLensProvider(documentSelector, { + provideCodeLenses: (document: TextDocument, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, CodeLensRequest.type, this._c2p.asCodeLensParams(document), token).then( + this._p2c.asCodeLenses, + (error) => { + this.logFailedRequest(CodeLensRequest.type, error); + return Promise.resolve([]); + } + ); + }, + resolveCodeLens: (this._capabilites.codeLensProvider.resolveProvider) + ? (codeLens: VCodeLens, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, CodeLensResolveRequest.type, this._c2p.asCodeLens(codeLens), token).then( + this._p2c.asCodeLens, + (error) => { + this.logFailedRequest(CodeLensResolveRequest.type, error); + return codeLens; + } + ); + } + : undefined + })); + } + + private hookDocumentFormattingProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.documentFormattingProvider) { + return; + } + this._providers.push(Languages.registerDocumentFormattingEditProvider(documentSelector, { + provideDocumentFormattingEdits: (document: TextDocument, options: VFormattingOptions, token: CancellationToken): Thenable => { + let params: DocumentFormattingParams = { + textDocument: this._c2p.asTextDocumentIdentifier(document), + options: this._c2p.asFormattingOptions(options) + }; + return this.doSendRequest(connection, DocumentFormattingRequest.type, params, token).then( + this._p2c.asTextEdits, + (error) => { + this.logFailedRequest(DocumentFormattingRequest.type, error); + return Promise.resolve([]); + } + ); + } + })); + } + + private hookDocumentRangeFormattingProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.documentRangeFormattingProvider) { + return; + } + this._providers.push(Languages.registerDocumentRangeFormattingEditProvider(documentSelector, { + provideDocumentRangeFormattingEdits: (document: TextDocument, range: VRange, options: VFormattingOptions, token: CancellationToken): Thenable => { + let params: DocumentRangeFormattingParams = { + textDocument: this._c2p.asTextDocumentIdentifier(document), + range: this._c2p.asRange(range), + options: this._c2p.asFormattingOptions(options) + }; + return this.doSendRequest(connection, DocumentRangeFormattingRequest.type, params, token).then( + this._p2c.asTextEdits, + (error) => { + this.logFailedRequest(DocumentRangeFormattingRequest.type, error); + return Promise.resolve([]); + } + ); + } + })); + } + + private hookDocumentOnTypeFormattingProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.documentOnTypeFormattingProvider) { + return; + } + let formatCapabilities = this._capabilites.documentOnTypeFormattingProvider; + this._providers.push(Languages.registerOnTypeFormattingEditProvider(documentSelector, { + provideOnTypeFormattingEdits: (document: TextDocument, position: VPosition, ch: string, options: VFormattingOptions, token: CancellationToken): Thenable => { + let params: DocumentOnTypeFormattingParams = { + textDocument: this._c2p.asTextDocumentIdentifier(document), + position: this._c2p.asPosition(position), + ch: ch, + options: this._c2p.asFormattingOptions(options) + }; + return this.doSendRequest(connection, DocumentOnTypeFormattingRequest.type, params, token).then( + this._p2c.asTextEdits, + (error) => { + this.logFailedRequest(DocumentOnTypeFormattingRequest.type, error); + return Promise.resolve([]); + } + ); + } + }, formatCapabilities.firstTriggerCharacter, ...formatCapabilities.moreTriggerCharacter)); + } + + private hookRenameProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.renameProvider) { + return; + } + this._providers.push(Languages.registerRenameProvider(documentSelector, { + provideRenameEdits: (document: TextDocument, position: VPosition, newName: string, token: CancellationToken): Thenable => { + let params: RenameParams = { + textDocument: this._c2p.asTextDocumentIdentifier(document), + position: this._c2p.asPosition(position), + newName: newName + }; + return this.doSendRequest(connection, RenameRequest.type, params, token).then( + this._p2c.asWorkspaceEdit, + (error: ResponseError) => { + this.logFailedRequest(RenameRequest.type, error); + Promise.resolve(new Error(error.message)); + } + ); + } + })); + } + + private hookDocumentLinkProvider(documentSelector: DocumentSelector, connection: IConnection): void { + if (!this._capabilites.documentLinkProvider) { + return; + } + this._providers.push(Languages.registerDocumentLinkProvider(documentSelector, { + provideDocumentLinks: (document: TextDocument, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, DocumentLinkRequest.type, this._c2p.asDocumentLinkParams(document), token).then( + this._p2c.asDocumentLinks, + (error: ResponseError) => { + this.logFailedRequest(DocumentLinkRequest.type, error); + Promise.resolve(new Error(error.message)); + } + ); + }, + resolveDocumentLink: this._capabilites.documentLinkProvider.resolveProvider + ? (link: VDocumentLink, token: CancellationToken): Thenable => { + return this.doSendRequest(connection, DocumentLinkResolveRequest.type, this._c2p.asDocumentLink(link), token).then( + this._p2c.asDocumentLink, + (error: ResponseError) => { + this.logFailedRequest(DocumentLinkResolveRequest.type, error); + Promise.resolve(new Error(error.message)); + } + ); + } + : undefined + })); + } +} + +export class SettingMonitor { + + private _listeners: Disposable[]; + + constructor(private _client: LanguageClient, private _setting: string) { + this._listeners = []; + } + + public start(): Disposable { + Workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this._listeners); + this.onDidChangeConfiguration(); + return new Disposable(() => { + if (this._client.needsStop()) { + this._client.stop(); + } + }); + } + + private onDidChangeConfiguration(): void { + let index = this._setting.indexOf('.'); + let primary = index >= 0 ? this._setting.substr(0, index) : this._setting; + let rest = index >= 0 ? this._setting.substr(index + 1) : undefined; + let enabled = rest ? Workspace.getConfiguration(primary).get(rest, false) : Workspace.getConfiguration(primary); + if (enabled && this._client.needsStart()) { + this._client.start(); + } else if (!enabled && this._client.needsStop()) { + this._client.stop(); + } + } +} \ No newline at end of file diff --git a/dataprotocol-node/client/src/protocol.ts b/dataprotocol-node/client/src/protocol.ts new file mode 100644 index 0000000000..ab23431e23 --- /dev/null +++ b/dataprotocol-node/client/src/protocol.ts @@ -0,0 +1,1585 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import { RequestType, NotificationType, ResponseError } from 'dataprotocol-jsonrpc'; + +import { + TextDocument, TextDocumentChangeEvent, TextDocumentContentChangeEvent, + Range, Position, Location, Diagnostic, DiagnosticSeverity, Command, + TextEdit, WorkspaceEdit, WorkspaceChange, TextEditChange, + TextDocumentIdentifier, VersionedTextDocumentIdentifier, TextDocumentItem, + CompletionItemKind, CompletionItem, CompletionList, + Hover, MarkedString, + SignatureHelp, SignatureInformation, ParameterInformation, + Definition, ReferenceContext, + DocumentHighlight, DocumentHighlightKind, + SymbolInformation, SymbolKind, + CodeLens, CodeActionContext, + FormattingOptions, DocumentLink, + ConnectionDetails, ServerInfo, + ConnectionSummary, ConnectionCompleteParams, IntelliSenseReadyParams, + ColumnMetadata, IDbColumn, + ConnectionProviderOptions, DataProtocolServerCapabilities, + CapabiltiesDiscoveryResult, MetadataQueryParams, MetadataQueryResult, + ScriptingParams, ScriptingResult, ScriptingCompleteParams, + BatchSummary, QueryExecuteBatchNotificationParams, ResultSetSummary, IResultMessage, ISelectionData, + DbCellValue, EditCell, EditRow, CreateSessionResponse, SessionCreatedParameters, ExpandParams, ExpandResponse, CloseSessionParams, CloseSessionResponse, + BackupInfo, BackupParams, BackupResponse, + RestoreParams, RestoreResponse, RestorePlanResponse, RestoreConfigInfoRequestParams, RestoreConfigInfoResponse, + LoginInfo, CreateLoginParams, CreateLoginResponse, GetDatabaseInfoParams, GetDatabaseInfoResponse, + DatabaseInfo, BackupConfigInfo, CreateDatabaseParams, CreateDatabaseResponse, + TaskInfo, ListTasksParams, ListTasksResponse, CancelTaskParams, TaskProgressInfo, + DefaultDatabaseInfoParams, DefaultDatabaseInfoResponse, BackupConfigInfoResponse, FileBrowserOpenParams, FileBrowserOpenedParams, + FileBrowserCloseParams, FileBrowserExpandParams, FileBrowserValidateParams, + FileBrowserCloseResponse, FileBrowserExpandedParams, FileBrowserValidatedParams +} from 'dataprotocol-languageserver-types'; + + +/** + * A parameter literal used in requests to pass a text document and a position inside that + * document. + */ +export interface TextDocumentPositionParams { + /** + * The text document. + */ + textDocument: TextDocumentIdentifier; + + /** + * The position inside the text document. + */ + position: Position; +} + + +//---- Initialize Method ---- + +/** + * Defines the capabilities provided by the client. + */ +export interface ClientCapabilities { +} + +/** + * Defines how the host (editor) should sync + * document changes to the language server. + */ +export enum TextDocumentSyncKind { + /** + * Documents should not be synced at all. + */ + None = 0, + + /** + * Documents are synced by always sending the full content + * of the document. + */ + Full = 1, + + /** + * Documents are synced by sending the full content on open. + * After that only incremental updates to the document are + * send. + */ + Incremental = 2 +} + +/** + * Completion options. + */ +export interface CompletionOptions { + /** + * The server provides support to resolve additional + * information for a completion item. + */ + resolveProvider?: boolean; + + /** + * The characters that trigger completion automatically. + */ + triggerCharacters?: string[]; +} + +/** + * Signature help options. + */ +export interface SignatureHelpOptions { + /** + * The characters that trigger signature help + * automatically. + */ + triggerCharacters?: string[]; +} + +/** + * Code Lens options. + */ +export interface CodeLensOptions { + /** + * Code lens has a resolve provider as well. + */ + resolveProvider?: boolean; +} + +/** + * Format document on type options + */ +export interface DocumentOnTypeFormattingOptions { + /** + * A character on which formatting should be triggered, like `}`. + */ + firstTriggerCharacter: string; + /** + * More trigger characters. + */ + moreTriggerCharacter?: string[] +} + +/** + * Document link options + */ +export interface DocumentLinkOptions { + /** + * Document links have a resolve provider as well. + */ + resolveProvider?: boolean; +} + +/** + * Defines the capabilities provided by a language + * server. + */ +export interface ServerCapabilities { + /** + * Defines how text documents are synced. + */ + textDocumentSync?: number; + /** + * The server provides hover support. + */ + hoverProvider?: boolean; + /** + * The server provides completion support. + */ + completionProvider?: CompletionOptions; + /** + * The server provides signature help support. + */ + signatureHelpProvider?: SignatureHelpOptions; + /** + * The server provides goto definition support. + */ + definitionProvider?: boolean; + /** + * The server provides find references support. + */ + referencesProvider?: boolean; + /** + * The server provides document highlight support. + */ + documentHighlightProvider?: boolean; + /** + * The server provides document symbol support. + */ + documentSymbolProvider?: boolean; + /** + * The server provides workspace symbol support. + */ + workspaceSymbolProvider?: boolean; + /** + * The server provides code actions. + */ + codeActionProvider?: boolean; + /** + * The server provides code lens. + */ + codeLensProvider?: CodeLensOptions; + /** + * The server provides document formatting. + */ + documentFormattingProvider?: boolean; + /** + * The server provides document range formatting. + */ + documentRangeFormattingProvider?: boolean; + /** + * The server provides document formatting on typing. + */ + documentOnTypeFormattingProvider?: DocumentOnTypeFormattingOptions; + /** + * The server provides rename support. + */ + renameProvider?: boolean; + /** + * The server provides document link support. + */ + documentLinkProvider?: DocumentLinkOptions; + + connectionProvider?: boolean; +} + +/** + * The initialize method is sent from the client to the server. + * It is send once as the first method after starting up the + * worker. The requests parameter is of type [InitializeParams](#InitializeParams) + * the response if of type [InitializeResult](#InitializeResult) of a Thenable that + * resolves to such. + */ +export namespace InitializeRequest { + export const type: RequestType = { get method() { return 'initialize'; } }; +} + +/** + * The initialize parameters + */ +export interface InitializeParams { + /** + * The process Id of the parent process that started + * the server. + */ + processId: number; + + /** + * The rootPath of the workspace. Is null + * if no folder is open. + */ + rootPath: string; + + /** + * The capabilities provided by the client (editor) + */ + capabilities: ClientCapabilities; + + /** + * User provided initialization options. + */ + initializationOptions?: any; + + /** + * The initial trace setting. If omitted trace is disabled ('off'). + */ + trace?: 'off' | 'messages' | 'verbose'; +} + +/** + * The result returned from an initilize request. + */ +export interface InitializeResult { + /** + * The capabilities the language server provides. + */ + capabilities: ServerCapabilities; +} + +/** + * The data type of the ResponseError if the + * initialize request fails. + */ +export interface InitializeError { + /** + * Indicates whether the client should retry to send the + * initilize request after showing the message provided + * in the {@link ResponseError} + */ + retry: boolean; +} + +//---- Shutdown Method ---- + +/** + * A shutdown request is sent from the client to the server. + * It is send once when the client descides to shutdown the + * server. The only notification that is sent after a shudown request + * is the exit event. + */ +export namespace ShutdownRequest { + export const type: RequestType = { get method() { return 'shutdown'; } }; +} + +//---- Exit Notification ---- + +/** + * The exit event is sent from the client to the server to + * ask the server to exit its process. + */ +export namespace ExitNotification { + export const type: NotificationType = { get method() { return 'exit'; } }; +} + +//---- Configuration notification ---- + +/** + * The configuration change notification is sent from the client to the server + * when the client's configuration has changed. The notification contains + * the changed configuration as defined by the language client. + */ +export namespace DidChangeConfigurationNotification { + export const type: NotificationType = { get method() { return 'workspace/didChangeConfiguration'; } }; +} + +/** + * The parameters of a change configuration notification. + */ +export interface DidChangeConfigurationParams { + /** + * The actual changed settings + */ + settings: any; +} + +//---- Message show and log notifications ---- + +/** + * The message type + */ +export enum MessageType { + /** + * An error message. + */ + Error = 1, + /** + * A warning message. + */ + Warning = 2, + /** + * An information message. + */ + Info = 3, + /** + * A log message. + */ + Log = 4 +} + +/** + * The parameters of a notification message. + */ +export interface ShowMessageParams { + /** + * The message type. See {@link MessageType} + */ + type: number; + + /** + * The actual message + */ + message: string; +} + +/** + * The show message notification is sent from a server to a client to ask + * the client to display a particular message in the user interface. + */ +export namespace ShowMessageNotification { + export const type: NotificationType = { get method() { return 'window/showMessage'; } }; +} + +export interface MessageActionItem { + /** + * A short title like 'Retry', 'Open Log' etc. + */ + title: string; +} + +export interface ShowMessageRequestParams { + /** + * The message type. See {@link MessageType} + */ + type: number; + + /** + * The actual message + */ + message: string; + + /** + * The message action items to present. + */ + actions?: MessageActionItem[]; +} + +/** + * The show message request is send from the server to the clinet to show a message + * and a set of options actions to the user. + */ +export namespace ShowMessageRequest { + export const type: RequestType = { get method() { return 'window/showMessageRequest'; } }; +} + +/** + * The log message notification is send from the server to the client to ask + * the client to log a particular message. + */ +export namespace LogMessageNotification { + export let type: NotificationType = { get method() { return 'window/logMessage'; } }; +} + +/** + * The log message parameters. + */ +export interface LogMessageParams { + /** + * The message type. See {@link MessageType} + */ + type: number; + + /** + * The actual message + */ + message: string; +} + +//---- Telemetry notification + +/** + * The telemetry event notification is send from the server to the client to ask + * the client to log telemetry data. + */ +export namespace TelemetryEventNotification { + export let type: NotificationType = { get method() { return 'telemetry/event'; } }; +} + +//---- Text document notifications ---- + +/** + * The parameters send in a open text document notification + */ +export interface DidOpenTextDocumentParams { + /** + * The document that was opened. + */ + textDocument: TextDocumentItem; +} + +/** + * The document open notification is sent from the client to the server to signal + * newly opened text documents. The document's truth is now managed by the client + * and the server must not try to read the document's truth using the document's + * uri. + */ +export namespace DidOpenTextDocumentNotification { + export const type: NotificationType = { get method() { return 'textDocument/didOpen'; } }; +} + +/** + * An event describing a change to a text document. If range and rangeLength are omitted + * the new text is considered to be the full content of the document. + */ +export interface TextDocumentContentChangeEvent { + /** + * The range of the document that changed. + */ + range?: Range; + + /** + * The length of the range that got replaced. + */ + rangeLength?: number; + + /** + * The new text of the document. + */ + text: string; +} + +/** + * The change text document notification's parameters. + */ +export interface DidChangeTextDocumentParams { + /** + * The document that did change. The version number points + * to the version after all provided content changes have + * been applied. + */ + textDocument: VersionedTextDocumentIdentifier; + + /** + * The actual content changes. + */ + contentChanges: TextDocumentContentChangeEvent[]; +} + +/** + * The document change notification is sent from the client to the server to signal + * changes to a text document. + */ +export namespace DidChangeTextDocumentNotification { + export const type: NotificationType = { get method() { return 'textDocument/didChange'; } }; +} + +/** + * The parameters send in a close text document notification + */ +export interface DidCloseTextDocumentParams { + /** + * The document that was closed. + */ + textDocument: TextDocumentIdentifier; +} + +/** + * The document close notification is sent from the client to the server when + * the document got closed in the client. The document's truth now exists + * where the document's uri points to (e.g. if the document's uri is a file uri + * the truth now exists on disk). + */ +export namespace DidCloseTextDocumentNotification { + export const type: NotificationType = { get method() { return 'textDocument/didClose'; } }; +} + +/** + * The parameters send in a save text document notification + */ +export interface DidSaveTextDocumentParams { + /** + * The document that was closed. + */ + textDocument: TextDocumentIdentifier; +} + +/** + * The document save notification is sent from the client to the server when + * the document got saved in the client. + */ +export namespace DidSaveTextDocumentNotification { + export const type: NotificationType = { get method() { return 'textDocument/didSave'; } }; +} + +//---- File eventing ---- + +/** + * The watched files notification is sent from the client to the server when + * the client detects changes to file watched by the lanaguage client. + */ +export namespace DidChangeWatchedFilesNotification { + export const type: NotificationType = { get method() { return 'workspace/didChangeWatchedFiles'; } }; +} + +/** + * The watched files change notification's parameters. + */ +export interface DidChangeWatchedFilesParams { + /** + * The actual file events. + */ + changes: FileEvent[]; +} + +/** + * The file event type + */ +export enum FileChangeType { + /** + * The file got created. + */ + Created = 1, + /** + * The file got changed. + */ + Changed = 2, + /** + * The file got deleted. + */ + Deleted = 3 +} + +/** + * An event describing a file change. + */ +export interface FileEvent { + /** + * The file's uri. + */ + uri: string; + /** + * The change type. + */ + type: number; +} + +//---- Diagnostic notification ---- + +/** + * Diagnostics notification are sent from the server to the client to signal + * results of validation runs. + */ +export namespace PublishDiagnosticsNotification { + export const type: NotificationType = { get method() { return 'textDocument/publishDiagnostics'; } }; +} + +/** + * The publish diagnostic notification's parameters. + */ +export interface PublishDiagnosticsParams { + /** + * The URI for which diagnostic information is reported. + */ + uri: string; + + /** + * An array of diagnostic information items. + */ + diagnostics: Diagnostic[]; +} + +//---- Completion Support -------------------------- + +/** + * Request to request completion at a given text document position. The request's + * parameter is of type [TextDocumentPosition](#TextDocumentPosition) the response + * is of type [CompletionItem[]](#CompletionItem) or [CompletionList](#CompletionList) + * or a Thenable that resolves to such. + */ +export namespace CompletionRequest { + export const type: RequestType = { get method() { return 'textDocument/completion'; } }; +} + +/** + * Request to resolve additional information for a given completion item.The request's + * parameter is of type [CompletionItem](#CompletionItem) the response + * is of type [CompletionItem](#CompletionItem) or a Thenable that resolves to such. + */ +export namespace CompletionResolveRequest { + export const type: RequestType = { get method() { return 'completionItem/resolve'; } }; +} + +//---- Hover Support ------------------------------- + +export type MarkedString = string | { language: string; value: string }; + +/** + * Request to request hover information at a given text document position. The request's + * parameter is of type [TextDocumentPosition](#TextDocumentPosition) the response is of + * type [Hover](#Hover) or a Thenable that resolves to such. + */ +export namespace HoverRequest { + export const type: RequestType = { get method() { return 'textDocument/hover'; } }; +} + +//---- SignatureHelp ---------------------------------- + +export namespace SignatureHelpRequest { + export const type: RequestType = { get method() { return 'textDocument/signatureHelp'; } }; +} + +//---- Goto Definition ------------------------------------- + + +/** + * A request to resolve the defintion location of a symbol at a given text + * document position. The request's parameter is of type [TextDocumentPosition] + * (#TextDocumentPosition) the response is of type [Definition](#Definition) or a + * Thenable that resolves to such. + */ +export namespace DefinitionRequest { + export const type: RequestType = { get method() { return 'textDocument/definition'; } }; +} + +//---- Reference Provider ---------------------------------- + +/** + * Parameters for a [ReferencesRequest](#ReferencesRequest). + */ +export interface ReferenceParams extends TextDocumentPositionParams { + context: ReferenceContext +} + +/** + * A request to resolve project-wide references for the symbol denoted + * by the given text document position. The request's parameter is of + * type [ReferenceParams](#ReferenceParams) the response is of type + * [Location[]](#Location) or a Thenable that resolves to such. + */ +export namespace ReferencesRequest { + export const type: RequestType = { get method() { return 'textDocument/references'; } }; +} + +//---- Document Highlight ---------------------------------- + +/** + * Request to resolve a [DocumentHighlight](#DocumentHighlight) for a given + * text document position. The request's parameter is of type [TextDocumentPosition] + * (#TextDocumentPosition) the request reponse is of type [DocumentHighlight[]] + * (#DocumentHighlight) or a Thenable that resolves to such. + */ +export namespace DocumentHighlightRequest { + export const type: RequestType = { get method() { return 'textDocument/documentHighlight'; } }; +} + +//---- Document Symbol Provider --------------------------- + +/** + * Parameters for a [DocumentSymbolRequest](#DocumentSymbolRequest). + */ +export interface DocumentSymbolParams { + /** + * The text document. + */ + textDocument: TextDocumentIdentifier; +} + +/** + * A request to list all symbols found in a given text document. The request's + * parameter is of type [TextDocumentIdentifier](#TextDocumentIdentifier) the + * response is of type [SymbolInformation[]](#SymbolInformation) or a Thenable + * that resolves to such. + */ +export namespace DocumentSymbolRequest { + export const type: RequestType = { get method() { return 'textDocument/documentSymbol'; } }; +} + +//---- Workspace Symbol Provider --------------------------- + +/** + * The parameters of a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). + */ +export interface WorkspaceSymbolParams { + /** + * A non-empty query string + */ + query: string; +} + +/** + * A request to list project-wide symbols matching the query string given + * by the [WorkspaceSymbolParams](#WorkspaceSymbolParams). The response is + * of type [SymbolInformation[]](#SymbolInformation) or a Thenable that + * resolves to such. + */ +export namespace WorkspaceSymbolRequest { + export const type: RequestType = { get method() { return 'workspace/symbol'; } }; +} + +//---- Code Action Provider ---------------------------------- + + + +/** + * Params for the CodeActionRequest + */ +export interface CodeActionParams { + /** + * The document in which the command was invoked. + */ + textDocument: TextDocumentIdentifier; + + /** + * The range for which the command was invoked. + */ + range: Range; + + /** + * Context carrying additional information. + */ + context: CodeActionContext; +} + +/** + * A request to provide commands for the given text document and range. + */ +export namespace CodeActionRequest { + export const type: RequestType = { get method() { return 'textDocument/codeAction'; } }; +} + +//---- Code Lens Provider ------------------------------------------- + +/** + * Params for the Code Lens request. + */ +export interface CodeLensParams { + /** + * The document to request code lens for. + */ + textDocument: TextDocumentIdentifier; +} + +/** + * A request to provide code lens for the given text document. + */ +export namespace CodeLensRequest { + export const type: RequestType = { get method() { return 'textDocument/codeLens'; } }; +} + +/** + * A request to resolve a command for a given code lens. + */ +export namespace CodeLensResolveRequest { + export const type: RequestType = { get method() { return 'codeLens/resolve'; } }; +} + +//---- Formatting ---------------------------------------------- + +export interface DocumentFormattingParams { + /** + * The document to format. + */ + textDocument: TextDocumentIdentifier; + + /** + * The format options + */ + options: FormattingOptions; +} + +/** + * A request to to format a whole document. + */ +export namespace DocumentFormattingRequest { + export const type: RequestType = { get method() { return 'textDocument/formatting'; } }; +} + +export interface DocumentRangeFormattingParams { + /** + * The document to format. + */ + textDocument: TextDocumentIdentifier; + + /** + * The range to format + */ + range: Range; + + /** + * The format options + */ + options: FormattingOptions; +} + +/** + * A request to to format a range in a document. + */ +export namespace DocumentRangeFormattingRequest { + export const type: RequestType = { get method() { return 'textDocument/rangeFormatting'; } }; +} + +export interface DocumentOnTypeFormattingParams { + /** + * The document to format. + */ + textDocument: TextDocumentIdentifier; + + /** + * The position at which this request was send. + */ + position: Position; + + /** + * The character that has been typed. + */ + ch: string; + + /** + * The format options. + */ + options: FormattingOptions; +} + +/** + * A request to format a document on type. + */ +export namespace DocumentOnTypeFormattingRequest { + export const type: RequestType = { get method() { return 'textDocument/onTypeFormatting'; } }; +} + +//---- Rename ---------------------------------------------- + +export interface RenameParams { + /** + * The document to format. + */ + textDocument: TextDocumentIdentifier; + + /** + * The position at which this request was send. + */ + position: Position; + + /** + * The new name of the symbol. If the given name is not valid the + * request must return a [ResponseError](#ResponseError) with an + * appropriate message set. + */ + newName: string; +} + +/** + * A request to rename a symbol. + */ +export namespace RenameRequest { + export const type: RequestType = { get method() { return 'textDocument/rename'; } }; +} + +//---- Document Links ---------------------------------------------- + +export interface DocumentLinkParams { + /** + * The document to provide document links for. + */ + textDocument: TextDocumentIdentifier; +} + +/** + * A request to provide document links + */ +export namespace DocumentLinkRequest { + export const type: RequestType = { get method() { return 'textDocument/documentLink'; } }; +} + +/** + * Request to resolve additional information for a given document link. The request's + * parameter is of type [DocumentLink](#DocumentLink) the response + * is of type [DocumentLink](#DocumentLink) or a Thenable that resolves to such. + */ +export namespace DocumentLinkResolveRequest { + export const type: RequestType = { get method() { return 'documentLink/resolve'; } }; +} + +//---- Refresh IntelliSense ---------------------------------------- + +/** + * Notification sent when the an IntelliSense cache invalidation is requested + */ +export namespace RebuildIntelliSenseNotification { + export const type: NotificationType = { get method(): string { return 'textDocument/rebuildIntelliSense'; } }; +} + +/** + * Rebuild IntelliSense notification parameters + */ +export class RebuildIntelliSenseParams { + /** + * URI identifying the text document + */ + public ownerUri: string; +} + +// ------------------------------- < Connect Request > ---------------------------------------------- + +/** + * Connection request message format + */ +export interface ConnectParams { + /** + * URI identifying the owner of the connection + */ + ownerUri: string; + + /** + * Details for creating the connection + */ + connection: ConnectionDetails; +} + + +// Connection request message callback declaration +export namespace ConnectionRequest { + export const type: RequestType = { get method(): string { return 'connection/connect'; } }; +} + +// ------------------------------- < Connection Complete Event > ------------------------------------ + + +export namespace ConnectionCompleteNotification { + export const type: NotificationType = { get method(): string { return 'connection/complete'; } }; +} + +// ------------------------------- < Connection Changed Event > ------------------------------------- + +/** + * Parameters for the ConnectionChanged notification. + */ +export class ConnectionChangedParams { + /** + * Owner URI of the connection that changed. + */ + public ownerUri: string; + + /** + * Summary of details containing any connection changes. + */ + public connection: ConnectionSummary; +} + +/** + * Connection changed event callback declaration. + */ +export namespace ConnectionChangedNotification { + export const type: NotificationType = { get method(): string { return 'connection/connectionchanged'; } }; +} + +// ------------------------------- < Disconnect Request > ------------------------------------------- + +// Disconnect request message format +export class DisconnectParams { + // URI identifying the owner of the connection + public ownerUri: string; +} + +// Disconnect response format +export type DisconnectResult = boolean; + +// Disconnect request message callback declaration +export namespace DisconnectRequest { + export const type: RequestType = { get method(): string { return 'connection/disconnect'; } }; +} + +// ------------------------------- < Cancel Connect Request > --------------------------------------- + + +// Cancel connect request message format +export class CancelConnectParams { + /** + * URI identifying the owner of the connection + */ + public ownerUri: string; +} + +// Cancel connect response format. +export type CancelConnectResult = boolean; + +// Cancel connect request message callback declaration +export namespace CancelConnectRequest { + export const type: RequestType = { get method(): string { return 'connection/cancelconnect'; } }; +} + +// ------------------------------- < Change Database Request > ------------------------------------- + +export class ChangeDatabaseParams { + public ownerUri: string; + public newDatabase: string; +} + +export namespace ChangeDatabaseRequest { + export const type: RequestType = { get method(): string { return 'connection/changedatabase'; } }; +} + +// ------------------------------- < List Databases Request > --------------------------------------- + +// List databases request format +export class ListDatabasesParams { + // Connection information to use for querying master + public ownerUri: string; +} + +// List databases response format +export class ListDatabasesResult { + public databaseNames: Array; +} + +// List databases request callback declaration +export namespace ListDatabasesRequest { + export const type: RequestType = { get method(): string { return 'connection/listdatabases'; } }; +} + +// Language Flavor Changed ================================================================================ + +/** + * Parameters to provide when sending a language flavor changed notification + */ +export interface DidChangeLanguageFlavorParams { + uri: string; + language: string; + flavor: string; +} + +// ------------------------------- < Language Flavor Changed Notification > --------------------------------------- +export namespace LanguageFlavorChangedNotification { + export const type: NotificationType = { get method(): string { return 'connection/languageflavorchanged'; } }; +} + +// ------------------------------- < Table Metadata Request > --------------------------------------- + +// Table metadata request format +export class TableMetadataParams { + // Connection information to use for querying master + public ownerUri: string; + + public schema: string; + + public objectName: string; +} + +// Table metadata response format +export class TableMetadataResult { + public columns: ColumnMetadata[]; +} + +// Table metadata request callback declaration +export namespace TableMetadataRequest { + export const type: RequestType = { get method(): string { return 'metadata/table'; } }; +} + +// ------------------------------- < View Metadata Request > --------------------------------------- + +// Table metadata request callback declaration +export namespace ViewMetadataRequest { + export const type: RequestType = { get method(): string { return 'metadata/view'; } }; +} + +/** + * Event sent when the language service is finished updating after a connection + */ +export namespace IntelliSenseReadyNotification { + export const type: NotificationType = { get method(): string { return 'textDocument/intelliSenseReady'; } }; +} + +// ------------------------------- < Capabilties Discovery Event > ------------------------------------ + +export class CapabiltiesDiscoveryParams { + public hostName: string; + + public hostVersion: string; +} + +export namespace CapabiltiesDiscoveryRequest { + export const type: RequestType = { get method(): string { return 'capabilities/list'; } }; +} + +// Query Execution ================================================================================ +// ------------------------------- < Query Cancellation Request > ------------------------------------ +export namespace QueryCancelRequest { + export const type: RequestType = { get method(): string { return 'query/cancel'; } }; +} + +export interface QueryCancelParams { + ownerUri: string; +} + +export interface QueryCancelResult { + messages: string; +} + +// ------------------------------- < Query Dispose Request > ------------------------------------ + +export namespace QueryDisposeRequest { + export const type: RequestType = { get method(): string { return 'query/dispose'; } }; +} + +/** + * Parameters to provide when disposing of a query + */ +export interface QueryDisposeParams { + ownerUri: string; +} + +/** + * Result received upon successful disposal of a query + */ +export interface QueryDisposeResult { +} + +// ------------------------------- < Query Execution Complete Notification > ------------------------------------ +export namespace QueryExecuteCompleteNotification { + export const type: NotificationType = { get method(): string { return 'query/complete'; } }; +} + +/** + * Result received upon successful execution of a query + */ +export interface QueryExecuteCompleteNotificationResult { + ownerUri: string; + batchSummaries: BatchSummary[]; +} + +// ------------------------------- < Query Batch Start Notification > ------------------------------------ +export namespace QueryExecuteBatchStartNotification { + export const type: NotificationType = { get method(): string { return 'query/batchStart'; } }; +} + +// ------------------------------- < Query Batch Complete Notification > ------------------------------------ +export namespace QueryExecuteBatchCompleteNotification { + export const type: NotificationType = { get method(): string { return 'query/batchComplete'; } }; +} + +// ------------------------------- < Query ResultSet Complete Notification > ------------------------------------ +export namespace QueryExecuteResultSetCompleteNotification { + export const type: NotificationType = { get method(): string { return 'query/resultSetComplete'; } }; +} + +export interface QueryExecuteResultSetCompleteNotificationParams { + resultSetSummary: ResultSetSummary; + ownerUri: string; +} + +// ------------------------------- < Query Message Notification > ------------------------------------ +export namespace QueryExecuteMessageNotification { + export const type: NotificationType = { get method(): string { return 'query/message'; } }; +} + +export class QueryExecuteMessageParams { + message: IResultMessage; + ownerUri: string; +} + +// ------------------------------- < Query Execution Request > ------------------------------------ +export namespace QueryExecuteRequest { + export const type: RequestType = { get method(): string { return 'query/executeDocumentSelection'; } }; +} + +export interface ExecutionPlanOptions { + includeEstimatedExecutionPlanXml?: boolean; + includeActualExecutionPlanXml?: boolean; +} + +export interface QueryExecuteParams { + ownerUri: string; + querySelection: ISelectionData; + executionPlanOptions?: ExecutionPlanOptions; +} + +export interface QueryExecuteResult { } + +// ------------------------------- < Query Results Request > ------------------------------------ +export namespace QueryExecuteSubsetRequest { + export const type: RequestType = { get method(): string { return 'query/subset'; } }; +} + +export interface QueryExecuteSubsetParams { + ownerUri: string; + batchIndex: number; + resultSetIndex: number; + rowsStartIndex: number; + rowsCount: number; +} + +export interface ResultSetSubset { + rowCount: number; + rows: DbCellValue[][]; +} + +export interface QueryExecuteSubsetResult { + message: string; + resultSubset: ResultSetSubset; +} + +// ------------------------------- < Execute Statement > ------------------------------------ +export interface QueryExecuteStatementParams { + ownerUri: string; + line: number; + column: number; +} + +export namespace QueryExecuteStatementRequest { + export const type: RequestType = { get method(): string { return 'query/executedocumentstatement'; } }; +} + +// --------------------------------- < Save Results as CSV Request > ------------------------------------------ +export interface SaveResultsRequestParams { + ownerUri: string; + filePath: string; + batchIndex: number; + resultSetIndex: number; + rowStartIndex: number; + rowEndIndex: number; + columnStartIndex: number; + columnEndIndex: number; + includeHeaders?: boolean; +} + +export class SaveResultRequestResult { + messages: string; +} +// save results in csv format +export namespace SaveResultsAsCsvRequest { + export const type: RequestType = { get method(): string { return 'query/saveCsv'; } }; +} +// --------------------------------- ------------------------------------------ + +// --------------------------------- < Save Results as JSON Request > ------------------------------------------ +// save results in json format +export namespace SaveResultsAsJsonRequest { + export const type: RequestType = { get method(): string { return 'query/saveJson'; } }; +} +// --------------------------------- ------------------------------------------ + +// --------------------------------- < Save Results as Excel Request > ------------------------------------------ +// save results in Excel format +export namespace SaveResultsAsExcelRequest { + export const type: RequestType = { get method(): string { return 'query/saveExcel'; } }; +} +// --------------------------------- ------------------------------------------ + +// ------------------------------- < Execute and Return > ----------------------------------- + +export interface SimpleExecuteParams { + queryString: string; + ownerUri: string; +} + +export interface SimpleExecuteResult { + rowCount: number; + columnInfo: IDbColumn[]; + rows: DbCellValue[][]; +} + +export namespace SimpleExecuteRequest { + export const type: RequestType = { get method(): string { return 'query/simpleexecute'; } }; +} + +// ------------------------------- < Execute String > ------------------------------------ +export interface QueryExecuteStringParams { + query: string; + ownerUri: string; +} + +export namespace QueryExecuteStringRequest { + export const type: RequestType = { get method(): string { return 'query/executeString'; } }; +} + +// ------------------------------- < Metadata Events > ------------------------------------ + +export namespace MetadataQueryRequest { + export const type: RequestType = { get method(): string { return 'metadata/list'; } }; +} + +// ------------------------------- < Scripting Events > ------------------------------------ + +export namespace ScriptingRequest { + export const type: RequestType = { get method(): string { return 'scripting/script'; } }; +} + +// ------------------------------- < Scripting Complete Event > ------------------------------------ + +export namespace ScriptingCompleteNotification { + export const type: NotificationType = { get method(): string { return 'scripting/scriptComplete'; } }; +} + + +// Edit Data ====================================================================================== +// Shared Interfaces -------------------------------------------------------------------------- +export interface EditSessionOperationParams { + ownerUri: string; +} + +export interface EditRowOperationParams extends EditSessionOperationParams { + rowId: number; +} + +export interface EditCellResult { + cell: EditCell; + isRowDirty: boolean; +} + +// edit/commit -------------------------------------------------------------------------------- +export namespace EditCommitRequest { + export const type: RequestType = { get method(): string { return 'edit/commit'; } }; +} +export interface EditCommitParams extends EditSessionOperationParams { } +export interface EditCommitResult { } + +// edit/createRow ----------------------------------------------------------------------------- +export namespace EditCreateRowRequest { + export const type: RequestType = { get method(): string { return 'edit/createRow'; } }; +} +export interface EditCreateRowParams extends EditSessionOperationParams { } +export interface EditCreateRowResult { + defaultValues: string[]; + newRowId: number; +} + +// edit/deleteRow ----------------------------------------------------------------------------- +export namespace EditDeleteRowRequest { + export const type: RequestType = { get method(): string { return 'edit/deleteRow'; } }; +} +export interface EditDeleteRowParams extends EditRowOperationParams { } +export interface EditDeleteRowResult { } + +// edit/dispose ------------------------------------------------------------------------------- +export namespace EditDisposeRequest { + export const type: RequestType = { get method(): string { return 'edit/dispose'; } }; +} +export interface EditDisposeParams extends EditSessionOperationParams { } +export interface EditDisposeResult { } + +// edit/initialize ---------------------------------------------------------------------------- +export namespace EditInitializeRequest { + export const type: RequestType = { get method(): string { return 'edit/initialize'; } }; +} +export interface EditInitializeFiltering { + LimitResults?: number; +} +export interface EditInitializeParams extends EditSessionOperationParams { + filters: EditInitializeFiltering; + objectName: string; + schemaName: string; + objectType: string; +} +export interface EditInitializeResult { } + +// edit/revertCell -------------------------------------------------------------------------------- +export namespace EditRevertCellRequest { + export const type: RequestType = { get method(): string { return 'edit/revertCell'; } }; +} +export interface EditRevertCellParams extends EditRowOperationParams { + columnId: number; +} +export interface EditRevertCellResult extends EditCellResult { +} + +// edit/revertRow ----------------------------------------------------------------------------- +export namespace EditRevertRowRequest { + export const type: RequestType = { get method(): string { return 'edit/revertRow'; } }; +} +export interface EditRevertRowParams extends EditRowOperationParams { } +export interface EditRevertRowResult { } + +// edit/sessionReady Event -------------------------------------------------------------------- +export namespace EditSessionReadyNotification { + export const type: NotificationType = { get method(): string { return 'edit/sessionReady'; } }; +} +export interface EditSessionReadyParams { + ownerUri: string; + success: boolean; + message: string; +} + +// edit/updateCell ---------------------------------------------------------------------------- +export namespace EditUpdateCellRequest { + export const type: RequestType = { get method(): string { return 'edit/updateCell'; } }; +} +export interface EditUpdateCellParams extends EditRowOperationParams { + columnId: number; + newValue: string; +} +export interface EditUpdateCellResult extends EditCellResult { } + +// edit/subset ------------------------------------------------------------------------------------ +export namespace EditSubsetRequest { + export const type: RequestType = { get method(): string { return 'edit/subset'; } }; +} +export interface EditSubsetParams extends EditSessionOperationParams { + rowStartIndex: number; + rowCount: number; +} +export interface EditSubsetResult { + rowCount: number; + subset: EditRow[]; +} + +// ------------------------------- < Object Explorer Events > ------------------------------------ + +export namespace ObjectExplorerCreateSessionRequest { + export const type: RequestType = { get method(): string { return 'objectexplorer/createsession'; } }; +} + +export namespace ObjectExplorerExpandRequest { + export const type: RequestType = { get method(): string { return 'objectexplorer/expand'; } }; +} + +export namespace ObjectExplorerRefreshRequest { + export const type: RequestType = { get method(): string { return 'objectexplorer/refresh'; } }; +} + +export namespace ObjectExplorerCloseSessionRequest { + export const type: RequestType = { get method(): string { return 'objectexplorer/closesession'; } }; +} + +// ------------------------------- < Object Explorer Events > ------------------------------------ + + +export namespace ObjectExplorerCreateSessionCompleteNotification { + export const type: NotificationType = { get method(): string { return 'objectexplorer/sessioncreated'; } }; +} + + +export namespace ObjectExplorerExpandCompleteNotification { + export const type: NotificationType = { get method(): string { return 'objectexplorer/expandCompleted'; } }; +} + +// ------------------------------- < Task Service Events > ------------------------------------ + +export namespace ListTasksRequest { + export const type: RequestType = { get method(): string { return 'tasks/listtasks'; } }; +} + +export namespace CancelTaskRequest { + export const type: RequestType = { get method(): string { return 'tasks/canceltask'; } }; +} + +// ------------------------------- < Task Service Events > ------------------------------------ + + +export namespace TaskStatusChangedNotification { + export const type: NotificationType = { get method(): string { return 'tasks/statuschanged'; } }; +} + +export namespace TaskCreatedNotification { + export const type: NotificationType = { get method(): string { return 'tasks/newtaskcreated'; } }; +} + +// ------------------------------- < Admin Service Events > ------------------------------------ + +export namespace CreateDatabaseRequest { + export const type: RequestType = { get method(): string { return 'admin/createdatabase'; } }; +} + +export namespace DefaultDatabaseInfoRequest { + export const type: RequestType = { get method(): string { return 'admin/defaultdatabaseinfo'; } }; +} + +export namespace CreateLoginRequest { + export const type: RequestType = { get method(): string { return 'admin/createlogin'; } }; +} + +export namespace GetDatabaseInfoRequest { + export const type: RequestType = { get method(): string { return 'admin/getdatabaseinfo'; } }; +} + +// ------------------------------- < Disaster Recovery Events > ------------------------------------ + +export namespace BackupRequest { + export const type: RequestType = { get method(): string { return 'disasterrecovery/backup'; } }; +} + +export namespace BackupConfigInfoRequest { + export const type: RequestType = { get method(): string { return 'disasterrecovery/backupconfiginfo'; } }; +} + +export namespace RestoreRequest { + export const type: RequestType = { get method(): string { return 'disasterrecovery/restore'; } }; +} + +export namespace RestorePlanRequest { + export const type: RequestType = { get method(): string { return 'disasterrecovery/restoreplan'; } }; +} + +export namespace CancelRestorePlanRequest { + export const type: RequestType = { get method(): string { return 'disasterrecovery/cancelrestoreplan'; } }; +} + +export namespace RestoreConfigInfoRequest { + export const type: RequestType = { get method(): string { return 'disasterrecovery/restoreconfiginfo'; } }; +} + +// ------------------------------- < File Browser Events > ------------------------------------ + +export namespace FileBrowserOpenRequest { + export const type: RequestType = { get method(): string { return 'filebrowser/open'; } }; +} + +export namespace FileBrowserOpenedNotification { + export const type: NotificationType = { get method(): string { return 'filebrowser/opencomplete'; } }; +} + +export namespace FileBrowserExpandRequest { + export const type: RequestType = { get method(): string { return 'filebrowser/expand'; } }; +} + +export namespace FileBrowserExpandedNotification { + export const type: NotificationType = { get method(): string { return 'filebrowser/expandcomplete'; } }; +} + +export namespace FileBrowserValidateRequest { + export const type: RequestType = { get method(): string { return 'filebrowser/validate'; } }; +} + +export namespace FileBrowserValidatedNotification { + export const type: NotificationType = { get method(): string { return 'filebrowser/validatecomplete'; } }; +} + +export namespace FileBrowserCloseRequest { + export const type: RequestType = { get method(): string { return 'filebrowser/close'; } }; +} diff --git a/dataprotocol-node/client/src/protocolCodeLens.ts b/dataprotocol-node/client/src/protocolCodeLens.ts new file mode 100644 index 0000000000..9dbd4a4bf9 --- /dev/null +++ b/dataprotocol-node/client/src/protocolCodeLens.ts @@ -0,0 +1,16 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import * as code from 'vscode'; + +export default class ProtocolCodeLens extends code.CodeLens { + + public data: any; + + constructor(range: code.Range) { + super(range); + } +} \ No newline at end of file diff --git a/dataprotocol-node/client/src/protocolCompletionItem.ts b/dataprotocol-node/client/src/protocolCompletionItem.ts new file mode 100644 index 0000000000..120f7a605b --- /dev/null +++ b/dataprotocol-node/client/src/protocolCompletionItem.ts @@ -0,0 +1,16 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import * as code from 'vscode'; + +export default class ProtocolCompletionItem extends code.CompletionItem { + + public data: any; + + constructor(label: string) { + super(label); + } +} \ No newline at end of file diff --git a/dataprotocol-node/client/src/protocolConverter.ts b/dataprotocol-node/client/src/protocolConverter.ts new file mode 100644 index 0000000000..b3708fd84d --- /dev/null +++ b/dataprotocol-node/client/src/protocolConverter.ts @@ -0,0 +1,769 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import * as code from 'vscode'; +import * as data from 'data'; +import * as ls from 'dataprotocol-languageserver-types'; +import * as is from './utils/is'; +import ProtocolCompletionItem from './protocolCompletionItem'; +import ProtocolCodeLens from './protocolCodeLens'; + +export interface Converter { + + asUri(value: string): code.Uri; + + asDiagnostics(diagnostics: ls.Diagnostic[]): code.Diagnostic[]; + + asDiagnostic(diagnostic: ls.Diagnostic): code.Diagnostic; + + asRange(value: ls.Range): code.Range; + + asPosition(value: ls.Position): code.Position; + + asDiagnosticSeverity(value: number): code.DiagnosticSeverity; + + asHover(hover: ls.Hover): code.Hover; + + asCompletionResult(result: ls.CompletionItem[] | ls.CompletionList): code.CompletionItem[] | code.CompletionList + + asCompletionItem(item: ls.CompletionItem): ProtocolCompletionItem; + + asTextEdit(edit: ls.TextEdit): code.TextEdit; + + asTextEdits(items: ls.TextEdit[]): code.TextEdit[]; + + asSignatureHelp(item: ls.SignatureHelp): code.SignatureHelp; + + asSignatureInformations(items: ls.SignatureInformation[]): code.SignatureInformation[]; + + asSignatureInformation(item: ls.SignatureInformation): code.SignatureInformation; + + asParameterInformations(item: ls.ParameterInformation[]): code.ParameterInformation[]; + + asParameterInformation(item: ls.ParameterInformation): code.ParameterInformation; + + asDefinitionResult(item: ls.Definition): code.Definition; + + asLocation(item: ls.Location): code.Location; + + asReferences(values: ls.Location[]): code.Location[]; + + asDocumentHighlights(values: ls.DocumentHighlight[]): code.DocumentHighlight[]; + + asDocumentHighlight(item: ls.DocumentHighlight): code.DocumentHighlight; + + asDocumentHighlightKind(item: ls.DocumentHighlightKind): code.DocumentHighlightKind; + + asSymbolInformations(values: ls.SymbolInformation[], uri?: code.Uri): code.SymbolInformation[]; + + asSymbolInformation(item: ls.SymbolInformation, uri?: code.Uri): code.SymbolInformation; + + asCommand(item: ls.Command): code.Command; + + asCommands(items: ls.Command[]): code.Command[]; + + asCodeLens(item: ls.CodeLens): code.CodeLens; + + asCodeLenses(items: ls.CodeLens[]): code.CodeLens[]; + + asWorkspaceEdit(item: ls.WorkspaceEdit): code.WorkspaceEdit; + + asDocumentLink(item: ls.DocumentLink): code.DocumentLink; + + asDocumentLinks(items: ls.DocumentLink[]): code.DocumentLink[]; + + asConnectionSummary(params: ls.ConnectionCompleteParams): data.ConnectionInfoSummary; + + asServerCapabilities(params: ls.CapabiltiesDiscoveryResult): data.DataProtocolServerCapabilities; + + asProviderMetadata(params: ls.MetadataQueryResult): data.ProviderMetadata; + + asScriptingResult(params: ls.ScriptingResult): data.ScriptingResult; + + asObjectExplorerSession(params: ls.SessionCreatedParameters): data.ObjectExplorerSession; + + asObjectExplorerCreateSessionResponse(params: ls.CreateSessionResponse): data.ObjectExplorerSessionResponse; + + asObjectExplorerNodeInfo(params: ls.ExpandResponse): data.ObjectExplorerExpandInfo; + + asObjectExplorerCloseSessionResponse(params: ls.CloseSessionResponse): data.ObjectExplorerCloseSessionResponse; + + asListTasksResponse(response: ls.ListTasksResponse): data.ListTasksResponse; + + asTaskInfo(params: ls.TaskInfo): data.TaskInfo; + + asRestorePlanResponse(params: ls.RestorePlanResponse): data.RestorePlanResponse; + + asRestoreResponse(params: ls.RestoreResponse): data.RestoreResponse; + + asRestoreConfigInfo(params: ls.RestoreConfigInfoResponse): data.RestoreConfigInfo; +} + +export interface URIConverter { + (value: string): code.Uri; +} + +export function createConverter(uriConverter?: URIConverter): Converter { + + const nullConverter = (value: string) => code.Uri.parse(value); + + const _uriConverter: URIConverter = uriConverter || nullConverter; + + function asUri(value: string): code.Uri { + return _uriConverter(value); + } + + function asDiagnostics(diagnostics: ls.Diagnostic[]): code.Diagnostic[] { + return diagnostics.map(asDiagnostic); + } + + function asDiagnostic(diagnostic: ls.Diagnostic): code.Diagnostic { + let result = new code.Diagnostic(asRange(diagnostic.range), diagnostic.message, asDiagnosticSeverity(diagnostic.severity)); + if (is.defined(diagnostic.code)) { + result.code = diagnostic.code; + } + if (is.defined(diagnostic.source)) { + result.source = diagnostic.source; + } + return result; + } + + function asRange(value: ls.Range): code.Range { + if (is.undefined(value)) { + return undefined; + } else if (is.nil(value)) { + return null; + } + return new code.Range(asPosition(value.start), asPosition(value.end)); + } + + function asPosition(value: ls.Position): code.Position { + if (is.undefined(value)) { + return undefined; + } else if (is.nil(value)) { + return null; + } + return new code.Position(value.line, value.character); + } + + function asDiagnosticSeverity(value: number): code.DiagnosticSeverity { + if (is.undefined(value) || is.nil(value)) { + return code.DiagnosticSeverity.Error; + } + switch (value) { + case ls.DiagnosticSeverity.Error: + return code.DiagnosticSeverity.Error; + case ls.DiagnosticSeverity.Warning: + return code.DiagnosticSeverity.Warning; + case ls.DiagnosticSeverity.Information: + return code.DiagnosticSeverity.Information; + case ls.DiagnosticSeverity.Hint: + return code.DiagnosticSeverity.Hint; + } + return code.DiagnosticSeverity.Error; + } + + function asHover(hover: ls.Hover): code.Hover { + if (is.undefined(hover)) { + return undefined; + } + if (is.nil(hover)) { + return null; + } + if (is.nil(hover.contents) || is.undefined(hover.contents)) { + // Contents must be defined or hover will throw + return null; + } + return new code.Hover(hover.contents, is.defined(hover.range) ? asRange(hover.range) : undefined); + } + + function asCompletionResult(result: ls.CompletionItem[] | ls.CompletionList): code.CompletionItem[] | code.CompletionList { + if (is.undefined(result)) { + return undefined; + } else if (is.nil(result)) { + return null; + } + if (Array.isArray(result)) { + let items = result; + return items.map(asCompletionItem); + } + let list = result; + return new code.CompletionList(list.items.map(asCompletionItem), list.isIncomplete); + } + + function set(value: T, func: () => void): void { + if (is.defined(value)) { + func(); + } + } + + function asCompletionItem(item: ls.CompletionItem): ProtocolCompletionItem { + let result = new ProtocolCompletionItem(item.label); + set(item.detail, () => result.detail = item.detail); + set(item.documentation, () => result.documentation = item.documentation); + set(item.filterText, () => result.filterText = item.filterText); + set(item.insertText, () => result.insertText = item.insertText); + // Protocol item kind is 1 based, codes item kind is zero based. + set(item.kind, () => result.kind = item.kind - 1); + set(item.sortText, () => result.sortText = item.sortText); + set(item.textEdit, () => result.textEdit = asTextEdit(item.textEdit)); + set(item.additionalTextEdits, () => result.additionalTextEdits = asTextEdits(item.additionalTextEdits)); + set(item.command, () => result.command = asCommand(item.command)); + set(item.data, () => result.data = item.data); + return result; + } + + function asTextEdit(edit: ls.TextEdit): code.TextEdit { + return new code.TextEdit(asRange(edit.range), edit.newText); + } + + function asTextEdits(items: ls.TextEdit[]): code.TextEdit[] { + if (is.undefined(items)) { + return undefined; + } else if (is.nil(items)) { + return null; + } + return items.map(asTextEdit); + } + + function asSignatureHelp(item: ls.SignatureHelp): code.SignatureHelp { + if (is.undefined(item)) { + return undefined; + } else if (is.nil(item)) { + return null; + } + let result = new code.SignatureHelp(); + set(item.activeParameter, () => result.activeParameter = item.activeParameter); + set(item.activeSignature, () => result.activeSignature = item.activeSignature); + set(item.signatures, () => result.signatures = asSignatureInformations(item.signatures)); + return result; + } + + function asSignatureInformations(items: ls.SignatureInformation[]): code.SignatureInformation[] { + return items ? items.map(asSignatureInformation) : undefined; + } + + function asSignatureInformation(item: ls.SignatureInformation): code.SignatureInformation { + if (!item) { + return undefined; + } + let result = new code.SignatureInformation(item.label); + set(item.documentation, () => result.documentation = item.documentation); + set(item.parameters, () => result.parameters = asParameterInformations(item.parameters)); + return result; + } + + function asParameterInformations(item: ls.ParameterInformation[]): code.ParameterInformation[] { + return item.map(asParameterInformation); + } + + function asParameterInformation(item: ls.ParameterInformation): code.ParameterInformation { + let result = new code.ParameterInformation(item.label); + set(item.documentation, () => result.documentation = item.documentation); + return result; + } + + function asDefinitionResult(item: ls.Definition): code.Definition { + if (is.undefined(item)) { + return undefined; + } else if (is.nil(item)) { + return null; + } + if (is.array(item)) { + return item.map(asLocation); + } else { + return asLocation(item); + } + } + + function asLocation(item: ls.Location): code.Location { + if (is.undefined(item)) { + return undefined; + } + if (is.nil(item)) { + return null; + } + return new code.Location(_uriConverter(item.uri), asRange(item.range)); + } + + function asReferences(values: ls.Location[]): code.Location[] { + if (is.undefined(values)) { + return undefined; + } + if (is.nil(values)) { + return null; + } + return values.map(asLocation); + } + + function asDocumentHighlights(values: ls.DocumentHighlight[]): code.DocumentHighlight[] { + if (is.undefined(values)) { + return undefined; + } + if (is.nil(values)) { + return null; + } + return values.map(asDocumentHighlight); + } + + function asDocumentHighlight(item: ls.DocumentHighlight): code.DocumentHighlight { + let result = new code.DocumentHighlight(asRange(item.range)); + set(item.kind, () => result.kind = asDocumentHighlightKind(item.kind)); + return result; + } + + function asDocumentHighlightKind(item: ls.DocumentHighlightKind): code.DocumentHighlightKind { + switch (item) { + case ls.DocumentHighlightKind.Text: + return code.DocumentHighlightKind.Text; + case ls.DocumentHighlightKind.Read: + return code.DocumentHighlightKind.Read; + case ls.DocumentHighlightKind.Write: + return code.DocumentHighlightKind.Write; + } + return code.DocumentHighlightKind.Text; + } + + function asSymbolInformations(values: ls.SymbolInformation[], uri?: code.Uri): code.SymbolInformation[] { + if (is.undefined(values)) { + return undefined; + } + if (is.nil(values)) { + return null; + } + return values.map(information => asSymbolInformation(information, uri)); + } + + function asSymbolInformation(item: ls.SymbolInformation, uri?: code.Uri): code.SymbolInformation { + // Symbol kind is one based in the protocol and zero based in code. + let result = new code.SymbolInformation( + item.name, item.kind - 1, + asRange(item.location.range), + item.location.uri ? _uriConverter(item.location.uri) : uri); + set(item.containerName, () => result.containerName = item.containerName); + return result; + } + + function asCommand(item: ls.Command): code.Command { + let result: code.Command = { title: item.title, command: item.command }; + set(item.arguments, () => result.arguments = item.arguments); + return result; + } + + function asCommands(items: ls.Command[]): code.Command[] { + if (is.undefined(items)) { + return undefined; + } + if (is.nil(items)) { + return null; + } + return items.map(asCommand); + } + + function asCodeLens(item: ls.CodeLens): code.CodeLens { + let result: ProtocolCodeLens = new ProtocolCodeLens(asRange(item.range)); + if (is.defined(item.command)) { result.command = asCommand(item.command); } + if (is.defined(item.data)) { result.data = item.data; } + return result; + } + + function asCodeLenses(items: ls.CodeLens[]): code.CodeLens[] { + if (is.undefined(items)) { + return undefined; + } + if (is.nil(items)) { + return null; + } + return items.map(asCodeLens); + } + + function asWorkspaceEdit(item: ls.WorkspaceEdit): code.WorkspaceEdit { + if (is.undefined(item)) { + return undefined; + } + if (is.nil(item)) { + return null; + } + let result = new code.WorkspaceEdit(); + let keys = Object.keys(item.changes); + keys.forEach(key => result.set(_uriConverter(key), asTextEdits(item.changes[key]))); + return result; + } + + function asDocumentLink(item: ls.DocumentLink): code.DocumentLink { + let range = asRange(item.range); + let target = is.defined(item.target) && asUri(item.target); + return new code.DocumentLink(range, target); + } + + function asDocumentLinks(items: ls.DocumentLink[]): code.DocumentLink[] { + if (is.undefined(items)) { + return undefined; + } + if (is.nil(items)) { + return null; + } + return items.map(asDocumentLink); + } + + function asConnectionSummary(params: ls.ConnectionCompleteParams): data.ConnectionInfoSummary { + let connSummary: data.ConnectionInfoSummary = { + ownerUri: params.ownerUri, + connectionId: params.connectionId, + messages: params.messages, + errorMessage: params.errorMessage, + errorNumber: params.errorNumber, + serverInfo: params.serverInfo, + connectionSummary: params.connectionSummary + }; + return connSummary; + } + + function asServiceOptionType(val: string): data.ServiceOptionType { + if (val === 'string') { + return data.ServiceOptionType.string; + } else if (val === 'multistring') { + return data.ServiceOptionType.multistring; + } else if (val === 'password') { + return data.ServiceOptionType.password; + } else if (val === 'number') { + return data.ServiceOptionType.number; + } else if (val === 'boolean') { + return data.ServiceOptionType.boolean; + } else if (val === 'category') { + return data.ServiceOptionType.category; + } else if (val === 'object') { + return data.ServiceOptionType.object; + } + + // assume string for unknown value types + return data.ServiceOptionType.string; + } + + function asServerCapabilities(result: ls.CapabiltiesDiscoveryResult): data.DataProtocolServerCapabilities { + let capabilities: data.DataProtocolServerCapabilities = { + protocolVersion: result.capabilities.protocolVersion, + providerName: result.capabilities.providerName, + providerDisplayName: result.capabilities.providerDisplayName, + connectionProvider: undefined, + adminServicesProvider: undefined, + features: [] + }; + + if (result.capabilities.adminServicesProvider) { + capabilities.adminServicesProvider = { + databaseInfoOptions: new Array(), + databaseFileInfoOptions: new Array(), + fileGroupInfoOptions: new Array() + }; + + if (result.capabilities.adminServicesProvider.databaseInfoOptions + && result.capabilities.adminServicesProvider.databaseInfoOptions.length > 0) { + for (let i = 0; i < result.capabilities.adminServicesProvider.databaseInfoOptions.length; ++i) { + let srcOption: ls.ServiceOption = result.capabilities.adminServicesProvider.databaseInfoOptions[i]; + let descOption: data.ServiceOption = buildServiceOption(srcOption); + capabilities.adminServicesProvider.databaseInfoOptions.push(descOption); + } + } + + if (result.capabilities.adminServicesProvider.databaseFileInfoOptions + && result.capabilities.adminServicesProvider.databaseFileInfoOptions.length > 0) { + for (let i = 0; i < result.capabilities.adminServicesProvider.databaseFileInfoOptions.length; ++i) { + let srcOption: ls.ServiceOption = result.capabilities.adminServicesProvider.databaseFileInfoOptions[i]; + let descOption: data.ServiceOption = buildServiceOption(srcOption); + capabilities.adminServicesProvider.databaseFileInfoOptions.push(descOption); + } + } + + if (result.capabilities.adminServicesProvider.fileGroupInfoOptions + && result.capabilities.adminServicesProvider.fileGroupInfoOptions.length > 0) { + for (let i = 0; i < result.capabilities.adminServicesProvider.fileGroupInfoOptions.length; ++i) { + let srcOption: ls.ServiceOption = result.capabilities.adminServicesProvider.fileGroupInfoOptions[i]; + let descOption: data.ServiceOption = buildServiceOption(srcOption); + capabilities.adminServicesProvider.fileGroupInfoOptions.push(descOption); + } + } + } + + if (result.capabilities.connectionProvider + && result.capabilities.connectionProvider.options + && result.capabilities.connectionProvider.options.length > 0) { + capabilities.connectionProvider = { + options: new Array() + }; + for (let i = 0; i < result.capabilities.connectionProvider.options.length; ++i) { + let srcOption: ls.ConnectionOption = result.capabilities.connectionProvider.options[i]; + let descOption: data.ConnectionOption = { + name: srcOption.name, + displayName: srcOption.displayName ? srcOption.displayName : srcOption.name, + description: srcOption.description, + groupName: srcOption.groupName, + defaultValue: srcOption.defaultValue, + categoryValues: srcOption.categoryValues, + isIdentity: srcOption.isIdentity, + isRequired: srcOption.isRequired, + valueType: asServiceOptionType(srcOption.valueType), + specialValueType: undefined + }; + + if (srcOption.specialValueType === 'serverName') { + descOption.specialValueType = data.ConnectionOptionSpecialType.serverName; + } else if (srcOption.specialValueType === 'databaseName') { + descOption.specialValueType = data.ConnectionOptionSpecialType.databaseName; + } else if (srcOption.specialValueType === 'authType') { + descOption.specialValueType = data.ConnectionOptionSpecialType.authType; + } else if (srcOption.specialValueType === 'userName') { + descOption.specialValueType = data.ConnectionOptionSpecialType.userName; + } else if (srcOption.specialValueType === 'password') { + descOption.specialValueType = data.ConnectionOptionSpecialType.password; + } else if (srcOption.specialValueType === 'appName') { + descOption.specialValueType = data.ConnectionOptionSpecialType.appName; + } + + capabilities.connectionProvider.options.push(descOption); + } + } + + if (result.capabilities.features + && result.capabilities.features.length > 0) { + result.capabilities.features.forEach(feature => { + let descFeature: data.FeatureMetadataProvider = { + enabled: feature.enabled, + featureName: feature.featureName, + optionsMetadata: [] + }; + capabilities.features.push(descFeature); + if (feature.optionsMetadata) { + feature.optionsMetadata.forEach(srcOption => { + descFeature.optionsMetadata.push(buildServiceOption(srcOption)); + }); + } + }); + } + + return capabilities; + } + + function buildServiceOption(srcOption: ls.ServiceOption): data.ServiceOption { + return { + name: srcOption.name, + displayName: srcOption.displayName ? srcOption.displayName : srcOption.name, + description: srcOption.description, + groupName: srcOption.groupName, + defaultValue: srcOption.defaultValue, + categoryValues: srcOption.categoryValues, + isRequired: srcOption.isRequired, + isArray: srcOption.isArray, + objectType: srcOption.objectType, + valueType: asServiceOptionType(srcOption.valueType), + }; + } + + function asProviderMetadata(params: ls.MetadataQueryResult): data.ProviderMetadata { + let objectMetadata: data.ObjectMetadata[] = []; + + if (!params.metadata || !params.metadata.length) { + return { + objectMetadata: objectMetadata + }; + } + + for (let i = 0; i < params.metadata.length; ++i) { + let metadata: ls.ObjectMetadata = params.metadata[i]; + + let metadataTypeName: string; + if (metadata.metadataTypeName) { + // Read from the provider since it's defined + metadataTypeName = metadata.metadataTypeName; + } else if (metadata.metadataType === ls.MetadataType.View) { + metadataTypeName = 'View'; + } else if (metadata.metadataType === ls.MetadataType.SProc) { + metadataTypeName = 'StoredProcedure'; + } else if (metadata.metadataType === ls.MetadataType.Function) { + metadataTypeName = 'Function'; + } else { + metadataTypeName = 'Table'; + } + + objectMetadata.push({ + metadataTypeName: metadataTypeName, + metadataType: metadata.metadataType, + name: metadata.name, + schema: metadata.schema, + urn: metadata.urn + }); + } + + return { + objectMetadata: objectMetadata + }; + } + + function asObjectExplorerSession(params: ls.SessionCreatedParameters): data.ObjectExplorerSession { + return { + success: params.success, + sessionId: params.sessionId, + rootNode: params.rootNode, + errorMessage: params.errorMessage + }; + } + + function asObjectExplorerCreateSessionResponse(params: ls.CreateSessionResponse): data.ObjectExplorerSessionResponse { + return { + sessionId: params.sessionId + }; + } + + function asObjectExplorerNodeInfo(params: ls.ExpandResponse): data.ObjectExplorerExpandInfo { + return { + sessionId: params.sessionId, + nodes: params.nodes, + errorMessage: params.errorMessage, + nodePath: params.nodePath + }; + } + + function asObjectExplorerCloseSessionResponse(params: ls.CloseSessionResponse): data.ObjectExplorerCloseSessionResponse { + return { + sessionId: params.sessionId, + success: params.success + }; + } + + function asScriptingResult(params: ls.ScriptingResult): data.ScriptingResult { + return { + operationId: params.operationId, + script: params.script + }; + } + + function asListTasksResponse(response: ls.ListTasksResponse): data.ListTasksResponse { + return { + tasks: response.tasks + }; + } + + function asTaskInfo(params: ls.TaskInfo): data.TaskInfo { + return { + taskId: params.taskId, + status: params.status, + taskExecutionMode: params.taskExecutionMode, + serverName: params.serverName, + name: params.name, + databaseName: params.databaseName, + description: params.description, + providerName: params.providerName, + isCancelable: params.isCancelable, + }; + } + + function asRestorePlanResponse(params: ls.RestorePlanResponse): data.RestorePlanResponse { + return { + backupSetsToRestore: params.backupSetsToRestore, + canRestore: params.canRestore, + databaseNamesFromBackupSets: params.databaseNamesFromBackupSets, + dbFiles: params.dbFiles, + errorMessage: params.errorMessage, + planDetails: params.planDetails, + sessionId: params.sessionId + }; + } + + function asRestoreResponse(params: ls.RestoreResponse): data.RestoreResponse { + return { + result: params.result, + errorMessage: params.errorMessage, + taskId: params.taskId + }; + } + + function asRestoreConfigInfo(params: ls.RestoreConfigInfoResponse): data.RestoreConfigInfo { + return { + configInfo: params.configInfo + }; + } + + return { + asUri, + asDiagnostics, + asDiagnostic, + asRange, + asPosition, + asDiagnosticSeverity, + asHover, + asCompletionResult, + asCompletionItem, + asTextEdit, + asTextEdits, + asSignatureHelp, + asSignatureInformations, + asSignatureInformation, + asParameterInformations, + asParameterInformation, + asDefinitionResult, + asLocation, + asReferences, + asDocumentHighlights, + asDocumentHighlight, + asDocumentHighlightKind, + asSymbolInformations, + asSymbolInformation, + asCommand, + asCommands, + asCodeLens, + asCodeLenses, + asWorkspaceEdit, + asDocumentLink, + asDocumentLinks, + asConnectionSummary, + asServerCapabilities, + asProviderMetadata, + asScriptingResult, + asObjectExplorerSession, + asObjectExplorerCreateSessionResponse, + asObjectExplorerNodeInfo, + asObjectExplorerCloseSessionResponse, + asListTasksResponse, + asTaskInfo, + asRestorePlanResponse, + asRestoreResponse, + asRestoreConfigInfo + }; +} + +// This for backward compatibility since we exported the converter functions as API. +const defaultConverter = createConverter(); + +export const asDiagnostics: (diagnostics: ls.Diagnostic[]) => code.Diagnostic[] = defaultConverter.asDiagnostics; +export const asDiagnostic: (diagnostic: ls.Diagnostic) => code.Diagnostic = defaultConverter.asDiagnostic; +export const asRange: (value: ls.Range) => code.Range = defaultConverter.asRange; +export const asPosition: (value: ls.Position) => code.Position = defaultConverter.asPosition; +export const asDiagnosticSeverity: (value: number) => code.DiagnosticSeverity = defaultConverter.asDiagnosticSeverity; +export const asHover: (hover: ls.Hover) => code.Hover = defaultConverter.asHover; +export const asCompletionResult: (result: ls.CompletionItem[] | ls.CompletionList) => code.CompletionItem[] | code.CompletionList = defaultConverter.asCompletionResult; +export const asCompletionItem: (item: ls.CompletionItem) => ProtocolCompletionItem = defaultConverter.asCompletionItem; +export const asTextEdit: (edit: ls.TextEdit) => code.TextEdit = defaultConverter.asTextEdit; +export const asTextEdits: (items: ls.TextEdit[]) => code.TextEdit[] = defaultConverter.asTextEdits; +export const asSignatureHelp: (item: ls.SignatureHelp) => code.SignatureHelp = defaultConverter.asSignatureHelp; +export const asSignatureInformations: (items: ls.SignatureInformation[]) => code.SignatureInformation[] = defaultConverter.asSignatureInformations; +export const asSignatureInformation: (item: ls.SignatureInformation) => code.SignatureInformation = defaultConverter.asSignatureInformation; +export const asParameterInformations: (item: ls.ParameterInformation[]) => code.ParameterInformation[] = defaultConverter.asParameterInformations; +export const asParameterInformation: (item: ls.ParameterInformation) => code.ParameterInformation = defaultConverter.asParameterInformation; +export const asDefinitionResult: (item: ls.Definition) => code.Definition = defaultConverter.asDefinitionResult; +export const asLocation: (item: ls.Location) => code.Location = defaultConverter.asLocation; +export const asReferences: (values: ls.Location[]) => code.Location[] = defaultConverter.asReferences; +export const asDocumentHighlights: (values: ls.DocumentHighlight[]) => code.DocumentHighlight[] = defaultConverter.asDocumentHighlights; +export const asDocumentHighlight: (item: ls.DocumentHighlight) => code.DocumentHighlight = defaultConverter.asDocumentHighlight; +export const asDocumentHighlightKind: (item: ls.DocumentHighlightKind) => code.DocumentHighlightKind = defaultConverter.asDocumentHighlightKind; +export const asSymbolInformations: (values: ls.SymbolInformation[], uri?: code.Uri) => code.SymbolInformation[] = defaultConverter.asSymbolInformations; +export const asSymbolInformation: (item: ls.SymbolInformation, uri?: code.Uri) => code.SymbolInformation = defaultConverter.asSymbolInformation; +export const asCommand: (item: ls.Command) => code.Command = defaultConverter.asCommand; +export const asCommands: (items: ls.Command[]) => code.Command[] = defaultConverter.asCommands; +export const asCodeLens: (item: ls.CodeLens) => code.CodeLens = defaultConverter.asCodeLens; +export const asCodeLenses: (items: ls.CodeLens[]) => code.CodeLens[] = defaultConverter.asCodeLenses; +export const asWorkspaceEdit: (item: ls.WorkspaceEdit) => code.WorkspaceEdit = defaultConverter.asWorkspaceEdit; +export const asDocumentLink: (item: ls.DocumentLink) => code.DocumentLink = defaultConverter.asDocumentLink; +export const asDocumentLinks: (item: ls.DocumentLink[]) => code.DocumentLink[] = defaultConverter.asDocumentLinks; \ No newline at end of file diff --git a/dataprotocol-node/client/src/tsconfig.json b/dataprotocol-node/client/src/tsconfig.json new file mode 100644 index 0000000000..79b9f99c48 --- /dev/null +++ b/dataprotocol-node/client/src/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": false, + "inlineSources": false, + "declaration": true, + "stripInternal": true, + "outDir": "../lib" + } +} \ No newline at end of file diff --git a/dataprotocol-node/client/src/typings/es6-promise/index.d.ts b/dataprotocol-node/client/src/typings/es6-promise/index.d.ts new file mode 100644 index 0000000000..4af46edfa6 --- /dev/null +++ b/dataprotocol-node/client/src/typings/es6-promise/index.d.ts @@ -0,0 +1,84 @@ +// Type definitions for es6-promise +// Project: https://github.com/jakearchibald/ES6-Promise +// Definitions by: François de Campredon , vvakame +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +interface Thenable { + then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => U | Thenable): Thenable; + then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => void): Thenable; +} + +declare class Promise implements Thenable { + /** + * If you call resolve in the body of the callback passed to the constructor, + * your promise is fulfilled with result object passed to resolve. + * If you call reject your promise is rejected with the object passed to reject. + * For consistency and debugging (eg stack traces), obj should be an instanceof Error. + * Any errors thrown in the constructor callback will be implicitly passed to reject(). + */ + constructor(callback: (resolve : (value?: T | Thenable) => void, reject: (error?: any) => void) => void); + + /** + * onFulfilled is called when/if "promise" resolves. onRejected is called when/if "promise" rejects. + * Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called. + * Both callbacks have a single parameter , the fulfillment value or rejection reason. + * "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve. + * If an error is thrown in the callback, the returned promise rejects with that error. + * + * @param onFulfilled called when/if "promise" resolves + * @param onRejected called when/if "promise" rejects + */ + then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => U | Thenable): Promise; + then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => void): Promise; + + /** + * Sugar for promise.then(undefined, onRejected) + * + * @param onRejected called when/if "promise" rejects + */ + catch(onRejected?: (error: any) => U | Thenable): Promise; +} + +declare namespace Promise { + /** + * Make a new promise from the thenable. + * A thenable is promise-like in as far as it has a "then" method. + */ + function resolve(value?: T | Thenable): Promise; + + /** + * Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error + */ + function reject(error: any): Promise; + function reject(error: T): Promise; + + /** + * Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects. + * the array passed to all can be a mixture of promise-like objects and other objects. + * The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value. + */ + function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable, T9 | Thenable, T10 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; + function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable, T9 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; + function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; + function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; + function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6]>; + function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable]): Promise<[T1, T2, T3, T4, T5]>; + function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable ]): Promise<[T1, T2, T3, T4]>; + function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable]): Promise<[T1, T2, T3]>; + function all(values: [T1 | Thenable, T2 | Thenable]): Promise<[T1, T2]>; + function all(values: (T | Thenable)[]): Promise; + + /** + * Make a Promise that fulfills when any item fulfills, and rejects if any item rejects. + */ + function race(promises: (T | Thenable)[]): Promise; +} + +declare module 'es6-promise' { + var foo: typeof Promise; // Temp variable to reference Promise in local context + namespace rsvp { + export var Promise: typeof foo; + export function polyfill(): void; + } + export = rsvp; +} diff --git a/dataprotocol-node/client/src/typings/es6-promise/tsconfig.json b/dataprotocol-node/client/src/typings/es6-promise/tsconfig.json new file mode 100644 index 0000000000..bc50e7c4fb --- /dev/null +++ b/dataprotocol-node/client/src/typings/es6-promise/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es5", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": false, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "es6-promise-tests.ts" + ] +} \ No newline at end of file diff --git a/dataprotocol-node/client/src/typings/ref.d.ts b/dataprotocol-node/client/src/typings/ref.d.ts new file mode 100644 index 0000000000..e21045c0c8 --- /dev/null +++ b/dataprotocol-node/client/src/typings/ref.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/dataprotocol-node/client/src/utils/async.ts b/dataprotocol-node/client/src/utils/async.ts new file mode 100644 index 0000000000..691901c20d --- /dev/null +++ b/dataprotocol-node/client/src/utils/async.ts @@ -0,0 +1,82 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +export interface ITask { + (): T; +} + +export class Delayer { + + public defaultDelay: number; + private timeout: NodeJS.Timer; + private completionPromise: Promise; + private onSuccess: (value?: T | Thenable) => void; + private task: ITask; + + constructor(defaultDelay: number) { + this.defaultDelay = defaultDelay; + this.timeout = null; + this.completionPromise = null; + this.onSuccess = null; + this.task = null; + } + + public trigger(task: ITask, delay: number = this.defaultDelay): Promise { + this.task = task; + if (delay >= 0) { + this.cancelTimeout(); + } + + if (!this.completionPromise) { + this.completionPromise = new Promise((resolve) => { + this.onSuccess = resolve + }).then(() => { + this.completionPromise = null; + this.onSuccess = null; + var result = this.task(); + this.task = null; + return result; + }); + } + + if (delay >= 0 || this.timeout === null) { + this.timeout = setTimeout(() => { + this.timeout = null; + this.onSuccess(null); + }, delay >= 0 ? delay : this.defaultDelay); + } + + return this.completionPromise; + } + + public forceDelivery(): T { + if (!this.completionPromise) { + return null; + } + this.cancelTimeout(); + let result: T = this.task(); + this.completionPromise = null; + this.onSuccess = null; + this.task = null; + return result; + } + + public isTriggered(): boolean { + return this.timeout !== null; + } + + public cancel(): void { + this.cancelTimeout(); + this.completionPromise = null; + } + + private cancelTimeout(): void { + if (this.timeout !== null) { + clearTimeout(this.timeout); + this.timeout = null; + } + } +} \ No newline at end of file diff --git a/dataprotocol-node/client/src/utils/electron.ts b/dataprotocol-node/client/src/utils/electron.ts new file mode 100644 index 0000000000..f55c1c344f --- /dev/null +++ b/dataprotocol-node/client/src/utils/electron.ts @@ -0,0 +1,123 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import path = require('path'); +import os = require('os'); +import net = require('net'); +import cp = require('child_process'); + +export interface IForkOptions { + cwd?: string; + env?: any; + encoding?: string; + execArgv?: string[]; +} + +function makeRandomHexString(length: number): string { + let chars = ['0', '1', '2', '3', '4', '5', '6', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; + let result = ''; + for (let i = 0; i < length; i++) { + let idx = Math.floor(chars.length * Math.random()); + result += chars[idx]; + } + return result; +} + +function generatePipeName(): string { + var randomName = 'vscode-' + makeRandomHexString(40); + if (process.platform === 'win32') { + return '\\\\.\\pipe\\' + randomName + '-sock'; + } + + // Mac/Unix: use socket file + return path.join(os.tmpdir(), randomName + '.sock'); +} + +function generatePatchedEnv(env: any, stdInPipeName: string, stdOutPipeName: string): any { + // Set the two unique pipe names and the electron flag as process env + + var newEnv: any = {}; + for (var key in env) { + newEnv[key] = env[key]; + } + + newEnv['STDIN_PIPE_NAME'] = stdInPipeName; + newEnv['STDOUT_PIPE_NAME'] = stdOutPipeName; + newEnv['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = '1'; + + return newEnv; +} + +export function fork(modulePath: string, args: string[], options: IForkOptions, callback: (error: any, cp: cp.ChildProcess) => void): void { + + var callbackCalled = false; + var resolve = (result: cp.ChildProcess) => { + if (callbackCalled) { + return; + } + callbackCalled = true; + callback(null, result); + }; + var reject = (err: any) => { + if (callbackCalled) { + return; + } + callbackCalled = true; + callback(err, null); + }; + + // Generate two unique pipe names + var stdInPipeName = generatePipeName(); + var stdOutPipeName = generatePipeName(); + + var newEnv = generatePatchedEnv(options.env || process.env, stdInPipeName, stdOutPipeName); + + var childProcess: cp.ChildProcess; + + // Begin listening to stdout pipe + var server = net.createServer((stream) => { + // The child process will write exactly one chunk with content `ready` when it has installed a listener to the stdin pipe + + stream.once('data', (chunk: Buffer) => { + // The child process is sending me the `ready` chunk, time to connect to the stdin pipe + childProcess.stdin = net.connect(stdInPipeName); + + // From now on the childProcess.stdout is available for reading + childProcess.stdout = stream; + + resolve(childProcess); + }); + }); + server.listen(stdOutPipeName); + + var serverClosed = false; + var closeServer = () => { + if (serverClosed) { + return; + } + serverClosed = true; + server.close(); + } + + // Create the process + let bootstrapperPath = path.join(__dirname, 'electronForkStart'); + childProcess = cp.fork(bootstrapperPath, [modulePath].concat(args), { + silent: true, + cwd: options.cwd, + env: newEnv, + execArgv: options.execArgv + }); + + childProcess.once('error', (err: Error) => { + closeServer(); + reject(err); + }); + + childProcess.once('exit', (err: Error) => { + closeServer(); + reject(err); + }); +} diff --git a/dataprotocol-node/client/src/utils/electronForkStart.ts b/dataprotocol-node/client/src/utils/electronForkStart.ts new file mode 100644 index 0000000000..53c7c6e2c4 --- /dev/null +++ b/dataprotocol-node/client/src/utils/electronForkStart.ts @@ -0,0 +1,183 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +var net = require('net'), + fs = require('fs'), + stream = require('stream'), + util = require('util'); + +var ENABLE_LOGGING = false; + +var log = (function () { + if (!ENABLE_LOGGING) { + return function () { }; + } + var isFirst = true; + var LOG_LOCATION = 'C:\\stdFork.log'; + return function log(str) { + if (isFirst) { + isFirst = false; + fs.writeFileSync(LOG_LOCATION, str + '\n'); + return; + } + fs.appendFileSync(LOG_LOCATION, str + '\n'); + } +})(); + +var stdInPipeName = process.env['STDIN_PIPE_NAME']; +var stdOutPipeName = process.env['STDOUT_PIPE_NAME']; + +log('STDIN_PIPE_NAME: ' + stdInPipeName); +log('STDOUT_PIPE_NAME: ' + stdOutPipeName); +log('ATOM_SHELL_INTERNAL_RUN_AS_NODE: ' + process.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE']); + +// stdout redirection to named pipe +(function () { + log('Beginning stdout redirection...'); + + // Create a writing stream to the stdout pipe + var stdOutStream = net.connect(stdOutPipeName); + + // unref stdOutStream to behave like a normal standard out + stdOutStream.unref(); + + // handle process.stdout + (process).__defineGetter__('stdout', function () { return stdOutStream; }); + + // handle process.stderr + (process).__defineGetter__('stderr', function () { return stdOutStream; }); + + var fsWriteSyncString = function (fd, str, position, encoding) { + // fs.writeSync(fd, string[, position[, encoding]]); + var buf = new Buffer(str, encoding || 'utf8'); + return fsWriteSyncBuffer(fd, buf, 0, buf.length); + }; + + var fsWriteSyncBuffer = function (fd, buffer, off, len) { + off = Math.abs(off | 0); + len = Math.abs(len | 0); + + // fs.writeSync(fd, buffer, offset, length[, position]); + var buffer_length = buffer.length; + + if (off > buffer_length) { + throw new Error('offset out of bounds'); + } + if (len > buffer_length) { + throw new Error('length out of bounds'); + } + if (((off + len) | 0) < off) { + throw new Error('off + len overflow'); + } + if (buffer_length - off < len) { + // Asking for more than is left over in the buffer + throw new Error('off + len > buffer.length'); + } + + var slicedBuffer = buffer; + if (off !== 0 || len !== buffer_length) { + slicedBuffer = buffer.slice(off, off + len); + } + + stdOutStream.write(slicedBuffer); + return slicedBuffer.length; + }; + + // handle fs.writeSync(1, ...) + var originalWriteSync = fs.writeSync; + fs.writeSync = function (fd, data, position, encoding) { + if (fd !== 1) { + return originalWriteSync.apply(fs, arguments); + } + // usage: + // fs.writeSync(fd, buffer, offset, length[, position]); + // OR + // fs.writeSync(fd, string[, position[, encoding]]); + + if (data instanceof Buffer) { + return fsWriteSyncBuffer.apply(null, arguments); + } + + // For compatibility reasons with fs.writeSync, writing null will write "null", etc + if (typeof data !== 'string') { + data += ''; + } + + return fsWriteSyncString.apply(null, arguments); + }; + + log('Finished defining process.stdout, process.stderr and fs.writeSync'); +})(); + +// stdin redirection to named pipe +(function () { + + // Begin listening to stdin pipe + var server = net.createServer(function (stream) { + // Stop accepting new connections, keep the existing one alive + server.close(); + + log('Parent process has connected to my stdin. All should be good now.'); + + // handle process.stdin + (process).__defineGetter__('stdin', function () { + return stream; + }); + + // Remove myself from process.argv + process.argv.splice(1, 1); + + // Load the actual program + var program = process.argv[1]; + log('Loading program: ' + program); + + // Unset the custom environmental variables that should not get inherited + delete process.env['STDIN_PIPE_NAME']; + delete process.env['STDOUT_PIPE_NAME']; + delete process.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE']; + + require(program); + + log('Finished loading program.'); + + var stdinIsReferenced = true; + var timer = setInterval(function () { + var listenerCount = ( + stream.listeners('data').length + + stream.listeners('end').length + + stream.listeners('close').length + + stream.listeners('error').length + ); + // log('listenerCount: ' + listenerCount); + if (listenerCount <= 1) { + // No more "actual" listeners, only internal node + if (stdinIsReferenced) { + stdinIsReferenced = false; + // log('unreferencing stream!!!'); + stream.unref(); + } + } else { + // There are "actual" listeners + if (!stdinIsReferenced) { + stdinIsReferenced = true; + stream.ref(); + } + } + // log( + // '' + stream.listeners('data').length + + // ' ' + stream.listeners('end').length + + // ' ' + stream.listeners('close').length + + // ' ' + stream.listeners('error').length + // ); + }, 1000); + timer.unref(); + }); + + + server.listen(stdInPipeName, function () { + // signal via stdout that the parent process can now begin writing to stdin pipe + process.stdout.write('ready'); + }); + +})(); \ No newline at end of file diff --git a/dataprotocol-node/client/src/utils/is.ts b/dataprotocol-node/client/src/utils/is.ts new file mode 100644 index 0000000000..f155ae89ae --- /dev/null +++ b/dataprotocol-node/client/src/utils/is.ts @@ -0,0 +1,55 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +const toString = Object.prototype.toString; + +export function defined(value: any): boolean { + return typeof value !== 'undefined'; +} + +export function undefined(value: any): boolean { + return typeof value === 'undefined'; +} + +export function nil(value: any): boolean { + return value === null; +} + +export function boolean(value: any): value is boolean { + return value === true || value === false; +} + +export function string(value: any): value is string { + return toString.call(value) === '[object String]'; +} + +export function number(value: any): value is number { + return toString.call(value) === '[object Number]'; +} + +export function error(value: any): value is Error { + return toString.call(value) === '[object Error]'; +} + +export function func(value: any): value is Function { + return toString.call(value) === '[object Function]'; +} + +export function array(value: any): value is T[] { + return Array.isArray(value); +} + +export function stringArray(value: any): value is string[] { + return array(value) && (value).every(elem => string(elem)); +} + +export function typedArray(value: any, check: (value: any) => boolean): value is T[] { + return Array.isArray(value) && (value).every(check); +} + +export function thenable(value: any): value is Thenable { + return value && func(value.then); +} \ No newline at end of file diff --git a/dataprotocol-node/client/src/utils/processes.ts b/dataprotocol-node/client/src/utils/processes.ts new file mode 100644 index 0000000000..abfebb53fa --- /dev/null +++ b/dataprotocol-node/client/src/utils/processes.ts @@ -0,0 +1,44 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import * as cp from 'child_process'; +import ChildProcess = cp.ChildProcess; + +import { join } from 'path'; + +const isWindows = (process.platform === 'win32'); +const isMacintosh = (process.platform === 'darwin'); +const isLinux = (process.platform === 'linux'); +export function terminate(process: ChildProcess, cwd?: string): boolean { + if (isWindows) { + try { + // This we run in Atom execFileSync is available. + // Ignore stderr since this is otherwise piped to parent.stderr + // which might be already closed. + let options: any = { + stdio: ['pipe', 'pipe', 'ignore'] + }; + if (cwd) { + options.cwd = cwd + } + (cp).execFileSync('taskkill', ['/T', '/F', '/PID', process.pid.toString()], options); + return true; + } catch (err) { + return false; + } + } else if (isLinux || isMacintosh) { + try { + var cmd = join(__dirname, 'terminateProcess.sh'); + var result = (cp).spawnSync(cmd, [process.pid.toString()]); + return result.error ? false : true; + } catch (err) { + return false; + } + } else { + process.kill('SIGKILL'); + return true; + } +} \ No newline at end of file diff --git a/dataprotocol-node/client/src/utils/terminateProcess.sh b/dataprotocol-node/client/src/utils/terminateProcess.sh new file mode 100644 index 0000000000..8ef631053a --- /dev/null +++ b/dataprotocol-node/client/src/utils/terminateProcess.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the Source EULA. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +terminateTree() { + for cpid in $(pgrep -P $1); do + terminateTree $cpid + done + kill -9 $1 > /dev/null 2>&1 +} + +for pid in $*; do + terminateTree $pid +done \ No newline at end of file diff --git a/dataprotocol-node/client/thirdpartynotices.txt b/dataprotocol-node/client/thirdpartynotices.txt new file mode 100644 index 0000000000..760bbb4788 --- /dev/null +++ b/dataprotocol-node/client/thirdpartynotices.txt @@ -0,0 +1,31 @@ +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +For Microsoft vscode-languageclient + +This project incorporates material from the project(s) listed below (collectively, “Third Party Code”). +Microsoft is not the original author of the Third Party Code. The original copyright notice and license +under which Microsoft received such Third Party Code are set out below. This Third Party Code is licensed +to you under their original license terms set forth below. Microsoft reserves all other rights not expressly +granted, whether by implication, estoppel or otherwise. + +1. DefinitelyTyped version 0.0.1 (https://github.com/borisyankov/DefinitelyTyped) + +This project is licensed under the Source EULA license. +Copyrights are respective of each contributor listed at the beginning of each definition file. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/.eslintrc b/dataprotocol-node/jsonrpc/.eslintrc new file mode 100644 index 0000000000..306df0be07 --- /dev/null +++ b/dataprotocol-node/jsonrpc/.eslintrc @@ -0,0 +1,24 @@ +{ + "rules": { + "indent": [ + 2, + "tab" + ], + "quotes": [ + 2, + "single" + ], + "linebreak-style": [ + 2, + "windows" + ], + "semi": [ + 2, + "always" + ] + }, + "env": { + "node": true + }, + "extends": "eslint:recommended" +} \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/.npmignore b/dataprotocol-node/jsonrpc/.npmignore new file mode 100644 index 0000000000..2b1e5b19e1 --- /dev/null +++ b/dataprotocol-node/jsonrpc/.npmignore @@ -0,0 +1,9 @@ +.vscode/ +lib/test/ +lib/*.map +src/ +test/ +.eslintrc +.gitignore +gulpfile.js +tsd.json \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/.vscode/launch.json b/dataprotocol-node/jsonrpc/.vscode/launch.json new file mode 100644 index 0000000000..10cab6e424 --- /dev/null +++ b/dataprotocol-node/jsonrpc/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + "version": "0.1.0", + // List of configurations. Add new configurations or edit existing ones. + // ONLY "node" and "mono" are supported, change "type" to switch. + "configurations": [ + { + "request": "launch", + // Name of configuration; appears in the launch configuration drop down menu. + "name": "Mocha", + // Type of configuration. Possible values: "node", "mono". + "type": "node", + // Workspace relative or absolute path to the program. + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + // Automatically stop program after launch. + "stopOnEntry": false, + // Command line arguments passed to the program. + "args": ["--timeout", "999999"], + // Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace. + "cwd": "${workspaceRoot}", + // Workspace relative or absolute path to the runtime executable to be used. Default is the runtime executable on the PATH. + "runtimeExecutable": null, + // Optional arguments passed to the runtime executable. + "runtimeArgs": [], + // Environment variables passed to the program. + "env": { }, + // Use JavaScript source maps (if they exist). + "sourceMaps": true, + // If JavaScript source maps are enabled, the generated code is expected in this directory. + "outDir": "${workspaceRoot}/lib" + } + ] +} diff --git a/dataprotocol-node/jsonrpc/.vscode/settings.json b/dataprotocol-node/jsonrpc/.vscode/settings.json new file mode 100644 index 0000000000..0c0c4bcd8e --- /dev/null +++ b/dataprotocol-node/jsonrpc/.vscode/settings.json @@ -0,0 +1,9 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "javascript.validate.enable": false, + "files.trimTrailingWhitespace": true, + "eslint.enable": false, + "editor.insertSpaces": false, + "editor.tabSize": 4, + "typescript.tsdk": "./node_modules/typescript/lib" +} \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/.vscode/tasks.json b/dataprotocol-node/jsonrpc/.vscode/tasks.json new file mode 100644 index 0000000000..0ed84877c1 --- /dev/null +++ b/dataprotocol-node/jsonrpc/.vscode/tasks.json @@ -0,0 +1,9 @@ +{ + "version": "0.1.0", + "command": "npm", + "isShellCommand": true, + "args": ["run", "watch"], + "showOutput": "silent", + "isWatching": true, + "problemMatcher": "$tsc-watch" +} \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/README.md b/dataprotocol-node/jsonrpc/README.md new file mode 100644 index 0000000000..c61863e4eb --- /dev/null +++ b/dataprotocol-node/jsonrpc/README.md @@ -0,0 +1,4 @@ +# Microsoft Data Management Protocol - Node + +## License +[Source EULA](https://github.com/Microsoft/sqlopsstudio/blob/dev/license.txt) \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/package.json b/dataprotocol-node/jsonrpc/package.json new file mode 100644 index 0000000000..32d46ce19d --- /dev/null +++ b/dataprotocol-node/jsonrpc/package.json @@ -0,0 +1,29 @@ +{ + "name": "dataprotocol-jsonrpc", + "description": "A json rpc implementation over streams", + "version": "2.4.0", + "author": "Microsoft Corporation", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/vscode-languageserver-node.git" + }, + "bugs": { + "url": "https://github.com/Microsoft/vscode-languageserver-node/issues" + }, + "engines": { + "node": ">=4.0.0 || >=6.0.0" + }, + "main": "./lib/main.js", + "typings": "./lib/main", + "devDependencies": { + "mocha": "^3.0.2", + "typescript": "2.0.3" + }, + "scripts": { + "prepublish": "tsc -p ./src", + "compile": "tsc -p ./src", + "watch": "tsc -w -p ./src", + "test": "mocha" + } +} diff --git a/dataprotocol-node/jsonrpc/src/cancellation.ts b/dataprotocol-node/jsonrpc/src/cancellation.ts new file mode 100644 index 0000000000..75e165e351 --- /dev/null +++ b/dataprotocol-node/jsonrpc/src/cancellation.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Event, Emitter } from './events'; + +export interface CancellationToken { + /** + * Is `true` when the token has been cancelled, `false` otherwise. + */ + isCancellationRequested: boolean; + + /** + * An [event](#Event) which fires upon cancellation. + */ + onCancellationRequested: Event; +} + +export namespace CancellationToken { + + export const None: CancellationToken = Object.freeze({ + isCancellationRequested: false, + onCancellationRequested: Event.None + }); + + export const Cancelled: CancellationToken = Object.freeze({ + isCancellationRequested: true, + onCancellationRequested: Event.None + }); +} + +const shortcutEvent: Event = Object.freeze(function (callback, context?) { + let handle = setTimeout(callback.bind(context), 0); + return { dispose() { clearTimeout(handle); } }; +}); + +class MutableToken implements CancellationToken { + + private _isCancelled: boolean = false; + private _emitter: Emitter; + + public cancel() { + if (!this._isCancelled) { + this._isCancelled = true; + if (this._emitter) { + this._emitter.fire(undefined); + this._emitter = undefined; + } + } + } + + get isCancellationRequested(): boolean { + return this._isCancelled; + } + + get onCancellationRequested(): Event { + if (this._isCancelled) { + return shortcutEvent; + } + if (!this._emitter) { + this._emitter = new Emitter(); + } + return this._emitter.event; + } +} + +export class CancellationTokenSource { + + private _token: CancellationToken; + + get token(): CancellationToken { + if (!this._token) { + // be lazy and create the token only when + // actually needed + this._token = new MutableToken(); + } + return this._token; + } + + cancel(): void { + if (!this._token) { + // save an object by returning the default + // cancelled token when cancellation happens + // before someone asks for the token + this._token = CancellationToken.Cancelled; + } else { + (this._token).cancel(); + } + } + + dispose(): void { + this.cancel(); + } +} \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/src/events.ts b/dataprotocol-node/jsonrpc/src/events.ts new file mode 100644 index 0000000000..9b2c612d4d --- /dev/null +++ b/dataprotocol-node/jsonrpc/src/events.ts @@ -0,0 +1,213 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +export interface Disposable { + /** + * Dispose this object. + */ + dispose(); +} + +/** + * Represents a typed event. + */ +export interface Event { + + /** + * + * @param listener The listener function will be call when the event happens. + * @param thisArgs The 'this' which will be used when calling the event listener. + * @param disposables An array to which a {{IDisposable}} will be added. The + * @return + */ + (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]): Disposable; +} + +export namespace Event { + const _disposable = { dispose() { } }; + export const None: Event = function () { return _disposable; }; +} + +/** + * Represents a type which can release resources, such + * as event listening or a timer. + */ +class DisposableImpl implements Disposable { + + /** + * Combine many disposable-likes into one. Use this method + * when having objects with a dispose function which are not + * instances of Disposable. + * + * @return Returns a new disposable which, upon dispose, will + * dispose all provides disposable-likes. + */ + static from(...disposables: { dispose(): any }[]): DisposableImpl { + return new DisposableImpl(function () { + if (disposables) { + for (let disposable of disposables) { + disposable.dispose(); + } + disposables = undefined; + } + }); + } + + private _callOnDispose: Function; + + constructor(callOnDispose: Function) { + this._callOnDispose = callOnDispose; + } + + /** + * Dispose this object. + */ + dispose(): any { + if (typeof this._callOnDispose === 'function') { + this._callOnDispose(); + this._callOnDispose = undefined; + } + } +} + +class CallbackList { + + private _callbacks: Function[]; + private _contexts: any[]; + + public add(callback: Function, context: any = null, bucket?: Disposable[]): void { + if (!this._callbacks) { + this._callbacks = []; + this._contexts = []; + } + this._callbacks.push(callback); + this._contexts.push(context); + + if (Array.isArray(bucket)) { + bucket.push({ dispose: () => this.remove(callback, context) }); + } + } + + public remove(callback: Function, context: any = null): void { + if (!this._callbacks) { + return; + } + + var foundCallbackWithDifferentContext = false; + for (var i = 0, len = this._callbacks.length; i < len; i++) { + if (this._callbacks[i] === callback) { + if (this._contexts[i] === context) { + // callback & context match => remove it + this._callbacks.splice(i, 1); + this._contexts.splice(i, 1); + return; + } else { + foundCallbackWithDifferentContext = true; + } + } + } + + if (foundCallbackWithDifferentContext) { + throw new Error('When adding a listener with a context, you should remove it with the same context'); + } + } + + public invoke(...args: any[]): any[] { + if (!this._callbacks) { + return; + } + + var ret: any[] = [], + callbacks = this._callbacks.slice(0), + contexts = this._contexts.slice(0); + + for (var i = 0, len = callbacks.length; i < len; i++) { + try { + ret.push(callbacks[i].apply(contexts[i], args)); + } catch (e) { + console.error(e); + } + } + return ret; + } + + public isEmpty(): boolean { + return !this._callbacks || this._callbacks.length === 0; + } + + public dispose(): void { + this._callbacks = undefined; + this._contexts = undefined; + } +} + +export interface EmitterOptions { + onFirstListenerAdd?: Function; + onLastListenerRemove?: Function; +} + +export class Emitter { + + private static _noop = function () { }; + + private _event: Event; + private _callbacks: CallbackList; + + constructor(private _options?: EmitterOptions) { + } + + /** + * For the public to allow to subscribe + * to events from this Emitter + */ + get event(): Event { + if (!this._event) { + this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => { + if (!this._callbacks) { + this._callbacks = new CallbackList(); + } + if (this._options && this._options.onFirstListenerAdd && this._callbacks.isEmpty()) { + this._options.onFirstListenerAdd(this); + } + this._callbacks.add(listener, thisArgs); + + let result: Disposable; + result = { + dispose: () => { + this._callbacks.remove(listener, thisArgs); + result.dispose = Emitter._noop; + if (this._options && this._options.onLastListenerRemove && this._callbacks.isEmpty()) { + this._options.onLastListenerRemove(this); + } + } + }; + if (Array.isArray(disposables)) { + disposables.push(result); + } + + return result; + }; + } + return this._event; + } + + /** + * To be kept private to fire an event to + * subscribers + */ + fire(event: T): any { + if (this._callbacks) { + this._callbacks.invoke.call(this._callbacks, event); + } + } + + dispose() { + if (this._callbacks) { + this._callbacks.dispose(); + this._callbacks = undefined; + } + } +} diff --git a/dataprotocol-node/jsonrpc/src/is.ts b/dataprotocol-node/jsonrpc/src/is.ts new file mode 100644 index 0000000000..182ef48358 --- /dev/null +++ b/dataprotocol-node/jsonrpc/src/is.ts @@ -0,0 +1,47 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +const toString = Object.prototype.toString; + +export function defined(value: any): boolean { + return typeof value !== 'undefined'; +} + +export function undefined(value: any): boolean { + return typeof value === 'undefined'; +} + +export function nil(value: any): boolean { + return value === null; +} + +export function boolean(value: any): value is boolean { + return value === true || value === false; +} + +export function string(value: any): value is string { + return toString.call(value) === '[object String]'; +} + +export function number(value: any): value is number { + return toString.call(value) === '[object Number]'; +} + +export function error(value: any): value is Error { + return toString.call(value) === '[object Error]'; +} + +export function func(value: any): value is Function { + return toString.call(value) === '[object Function]'; +} + +export function array(value: any): value is T[] { + return Array.isArray(value); +} + +export function stringArray(value: any): value is string[] { + return array(value) && (value).every(elem => string(elem)); +} \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/src/main.ts b/dataprotocol-node/jsonrpc/src/main.ts new file mode 100644 index 0000000000..d27c0fc7fa --- /dev/null +++ b/dataprotocol-node/jsonrpc/src/main.ts @@ -0,0 +1,576 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import * as is from './is'; + +import { + Message, + RequestMessage, RequestType, isRequestMessage, + ResponseMessage, isReponseMessage, ResponseError, ErrorCodes, + NotificationMessage, NotificationType, isNotificationMessage +} from './messages'; + +import { MessageReader, DataCallback, StreamMessageReader, IPCMessageReader } from './messageReader'; +import { MessageWriter, StreamMessageWriter, IPCMessageWriter } from './messageWriter'; +import { Disposable, Event, Emitter } from './events'; +import { CancellationTokenSource, CancellationToken } from './cancellation'; + +export { + Message, ErrorCodes, ResponseError, + RequestMessage, RequestType, + NotificationMessage, NotificationType, + MessageReader, DataCallback, StreamMessageReader, IPCMessageReader, + MessageWriter, StreamMessageWriter, IPCMessageWriter, + CancellationTokenSource, CancellationToken, + Disposable, Event, Emitter +} + +interface CancelParams { + /** + * The request id to cancel. + */ + id: number | string; +} + +namespace CancelNotification { + export const type: NotificationType = { get method() { return '$/cancelRequest'; } }; +} + +export interface RequestHandler { + (params: P, token: CancellationToken): R | ResponseError | Thenable>; +} + +export interface NotificationHandler

{ + (params: P): void; +} + +export interface Logger { + error(message: string): void; + warn(message: string): void; + info(message: string): void; + log(message: string): void; +} + +export enum Trace { + Off, Messages, Verbose +} + +export type TraceValues = 'off' | 'messages' | 'verbose'; +export namespace Trace { + export function fromString(value: string): Trace { + value = value.toLowerCase(); + switch (value) { + case 'off': + return Trace.Off; + case 'messages': + return Trace.Messages; + case 'verbose': + return Trace.Verbose; + default: + return Trace.Off; + } + } + + export function toString(value: Trace): TraceValues { + switch (value) { + case Trace.Off: + return 'off'; + case Trace.Messages: + return 'messages'; + case Trace.Verbose: + return 'verbose'; + default: + return 'off'; + } + } +} + +export interface SetTraceParams { + value: TraceValues; +} + +export namespace SetTraceNotification { + export const type: NotificationType = { get method() { return '$/setTraceNotification'; } }; +} + +export interface LogTraceParams { + message: string; + verbose?: string; +} + +export namespace LogTraceNotification { + export const type: NotificationType = { get method() { return '$/logTraceNotification'; } }; +} + +export interface Tracer { + log(message: string, data?: string): void; +} + +export interface MessageConnection { + sendRequest(type: RequestType, params: P, token?: CancellationToken): Thenable; + onRequest(type: RequestType, handler: RequestHandler): void; + sendNotification

(type: NotificationType

, params?: P): void; + onNotification

(type: NotificationType

, handler: NotificationHandler

): void; + trace(value: Trace, tracer: Tracer, sendNotification?: boolean): void; + onError: Event<[Error, Message, number]>; + onClose: Event; + onUnhandledNotification: Event; + listen(); + onDispose: Event; + dispose(): void; +} + +export interface ServerMessageConnection extends MessageConnection { +} + +export interface ClientMessageConnection extends MessageConnection { +} + +interface ResponsePromise { + method: string; + timerStart: number; + resolve: (response) => void; + reject: (error: any) => void +} + +enum ConnectionState { + New = 1, + Listening = 2, + Closed = 3, + Disposed = 4 +} + +function createMessageConnection(messageReader: MessageReader, messageWriter: MessageWriter, logger: Logger, client: boolean = false): T { + let sequenceNumber = 0; + const version: string = '2.0'; + + let requestHandlers: { [name: string]: RequestHandler } = Object.create(null); + let eventHandlers: { [name: string]: NotificationHandler } = Object.create(null); + + let responsePromises: { [name: string]: ResponsePromise } = Object.create(null); + let requestTokens: { [id: string]: CancellationTokenSource } = Object.create(null); + + let trace: Trace = Trace.Off; + let tracer: Tracer; + + let state: ConnectionState = ConnectionState.New; + let errorEmitter: Emitter<[Error, Message, number]> = new Emitter<[Error, Message, number]>(); + let closeEmitter: Emitter = new Emitter(); + let unhandledNotificationEmitter: Emitter = new Emitter(); + let disposeEmitter: Emitter = new Emitter(); + + function isListening(): boolean { + return state === ConnectionState.Listening; + } + + function isClosed(): boolean { + return state === ConnectionState.Closed; + } + + function isDisposed(): boolean { + return state === ConnectionState.Disposed; + } + + function closeHandler(): void { + if (state === ConnectionState.New || state === ConnectionState.Listening) { + state = ConnectionState.Closed; + closeEmitter.fire(undefined); + } + // If the connection is disposed don't sent close events. + }; + + function readErrorHandler(error: Error): void { + errorEmitter.fire([error, undefined, undefined]); + } + + function writeErrorHandler(data: [Error, Message, number]): void { + errorEmitter.fire(data); + } + + messageReader.onClose(closeHandler); + messageReader.onError(readErrorHandler); + + messageWriter.onClose(closeHandler); + messageWriter.onError(writeErrorHandler); + + function handleRequest(requestMessage: RequestMessage) { + if (isDisposed()) { + // we return here silently since we fired an event when the + // connection got disposed. + return; + } + + function reply(resultOrError: any | ResponseError): void { + let message: ResponseMessage = { + jsonrpc: version, + id: requestMessage.id + }; + if (resultOrError instanceof ResponseError) { + message.error = (>resultOrError).toJson(); + } else { + message.result = is.undefined(resultOrError) ? null : resultOrError; + } + messageWriter.write(message); + } + function replyError(error: ResponseError) { + let message: ResponseMessage = { + jsonrpc: version, + id: requestMessage.id, + error: error.toJson() + }; + messageWriter.write(message); + } + function replySuccess(result: any) { + // The JSON RPC defines that a response must either have a result or an error + // So we can't treat undefined as a valid response result. + if (is.undefined(result)) { + result = null; + } + let message: ResponseMessage = { + jsonrpc: version, + id: requestMessage.id, + result: result + }; + messageWriter.write(message); + } + + let requestHandler = requestHandlers[requestMessage.method]; + if (requestHandler) { + let cancellationSource = new CancellationTokenSource(); + let tokenKey = String(requestMessage.id); + requestTokens[tokenKey] = cancellationSource; + try { + let handlerResult = requestHandler(requestMessage.params, cancellationSource.token); + let promise = >>handlerResult; + if (!handlerResult) { + delete requestTokens[tokenKey]; + replySuccess(handlerResult); + } else if (promise.then) { + promise.then((resultOrError): any | ResponseError => { + delete requestTokens[tokenKey]; + reply(resultOrError); + }, error => { + delete requestTokens[tokenKey]; + if (error instanceof ResponseError) { + replyError(>error); + } else if (error && is.string(error.message)) { + replyError(new ResponseError(ErrorCodes.InternalError, `Request ${requestMessage.method} failed with message: ${error.message}`)); + } else { + replyError(new ResponseError(ErrorCodes.InternalError, `Request ${requestMessage.method} failed unexpectedly without providing any details.`)); + } + }); + } else { + delete requestTokens[tokenKey]; + reply(handlerResult); + } + } catch (error) { + delete requestTokens[tokenKey]; + if (error instanceof ResponseError) { + reply(>error); + } else if (error && is.string(error.message)) { + replyError(new ResponseError(ErrorCodes.InternalError, `Request ${requestMessage.method} failed with message: ${error.message}`)); + } else { + replyError(new ResponseError(ErrorCodes.InternalError, `Request ${requestMessage.method} failed unexpectedly without providing any details.`)); + } + } + } else { + replyError(new ResponseError(ErrorCodes.MethodNotFound, `Unhandled method ${requestMessage.method}`)); + } + } + + function handleResponse(responseMessage: ResponseMessage) { + if (isDisposed()) { + // See handle request. + return; + } + + let key = String(responseMessage.id); + let responsePromise = responsePromises[key]; + if (trace != Trace.Off && tracer) { + traceResponse(responseMessage, responsePromise); + } + if (responsePromise) { + delete responsePromises[key]; + try { + if (is.defined(responseMessage.error)) { + let error = responseMessage.error; + responsePromise.reject(new ResponseError(error.code, error.message, error.data)); + } else if (is.defined(responseMessage.result)) { + responsePromise.resolve(responseMessage.result); + } else { + throw new Error('Should never happen.'); + } + } catch (error) { + if (error.message) { + logger.error(`Response handler '${responsePromise.method}' failed with message: ${error.message}`); + } else { + logger.error(`Response handler '${responsePromise.method}' failed unexpectedly.`); + } + } + } + } + + function handleNotification(message: NotificationMessage) { + if (isDisposed()) { + // See handle request. + return; + } + let eventHandler: NotificationHandler; + if (message.method === CancelNotification.type.method) { + eventHandler = (params: CancelParams) => { + let id = params.id; + let source = requestTokens[String(id)]; + if (source) { + source.cancel(); + } + } + } else { + eventHandler = eventHandlers[message.method]; + } + if (eventHandler) { + try { + if (trace != Trace.Off && tracer) { + traceReceivedNotification(message); + } + eventHandler(message.params); + } catch (error) { + if (error.message) { + logger.error(`Notification handler '${message.method}' failed with message: ${error.message}`); + } else { + logger.error(`Notification handler '${message.method}' failed unexpectedly.`); + } + } + } else { + unhandledNotificationEmitter.fire(message); + } + } + + function handleInvalidMessage(message: Message) { + if (!message) { + logger.error('Received empty message.'); + return; + } + logger.error(`Received message which is neither a response nor a notification message:\n${JSON.stringify(message, null, 4)}`); + // Test whether we find an id to reject the promise + let responseMessage: ResponseMessage = message as ResponseMessage; + if (is.string(responseMessage.id) || is.number(responseMessage.id)) { + let key = String(responseMessage.id); + let responseHandler = responsePromises[key]; + if (responseHandler) { + responseHandler.reject(new Error('The received response has neither a result nor an error property.')); + } + } + } + + function traceRequest(message: RequestMessage): void { + let data: string = undefined; + if (trace === Trace.Verbose && message.params) { + data = `Params: ${JSON.stringify(message.params, null, 4)}\n\n`; + } + tracer.log(`Sending request '${message.method} - (${message.id})'.`, data); + } + + function traceSendNotification(message: NotificationMessage): void { + let data: string = undefined; + if (trace === Trace.Verbose) { + if (message.params) { + data = `Params: ${JSON.stringify(message.params, null, 4)}\n\n`; + } else { + data = 'No parameters provided.\n\n'; + } + } + tracer.log(`Sending notification '${message.method}'.`, data); + } + + function traceReceivedNotification(message: NotificationMessage): void { + if (message.method === LogTraceNotification.type.method) { + return; + } + let data: string = undefined; + if (trace === Trace.Verbose) { + if (message.params) { + data = `Params: ${JSON.stringify(message.params, null, 4)}\n\n`; + } else { + data = 'No parameters provided.\n\n'; + } + } + tracer.log(`Received notification '${message.method}'.`, data); + } + + function traceResponse(message: ResponseMessage, responsePromise: ResponsePromise): void { + let data: string = undefined; + if (trace === Trace.Verbose) { + if (message.error && message.error.data) { + data = `Error data: ${JSON.stringify(message.error.data, null, 4)}\n\n`; + } else { + if (message.result) { + data = `Result: ${JSON.stringify(message.result, null, 4)}\n\n`; + } else if (is.undefined(message.error)) { + data = 'No result returned.\n\n'; + } + } + } + if (responsePromise) { + let error = message.error ? ` Request failed: ${message.error.message} (${message.error.code}).` : ''; + tracer.log(`Received response '${responsePromise.method} - (${message.id})' in ${Date.now() - responsePromise.timerStart}ms.${error}`, data); + } else { + tracer.log(`Received response ${message.id} without active response promise.`, data); + } + } + + let callback: DataCallback = (message) => { + if (isRequestMessage(message)) { + handleRequest(message); + } else if (isReponseMessage(message)) { + handleResponse(message) + } else if (isNotificationMessage(message)) { + handleNotification(message); + } else { + handleInvalidMessage(message); + } + }; + + function throwIfClosedOrDisposed() { + if (isClosed()) { + throw new Error('Connection is closed.'); + } + if (isDisposed()) { + throw new Error('Connection is disposed.'); + } + } + + function throwIfListening() { + if (isListening()) { + throw new Error('Connection is already listening'); + } + } + + let connection: MessageConnection = { + sendNotification:

(type: NotificationType

, params): void => { + throwIfClosedOrDisposed(); + + let notificatioMessage: NotificationMessage = { + jsonrpc: version, + method: type.method, + params: params + } + if (trace != Trace.Off && tracer) { + traceSendNotification(notificatioMessage); + } + messageWriter.write(notificatioMessage); + }, + onNotification:

(type: NotificationType

, handler: NotificationHandler

) => { + throwIfClosedOrDisposed(); + + eventHandlers[type.method] = handler; + }, + sendRequest: (type: RequestType, params: P, token?: CancellationToken) => { + throwIfClosedOrDisposed(); + + let id = sequenceNumber++; + let result = new Promise>((resolve, reject) => { + let requestMessage: RequestMessage = { + jsonrpc: version, + id: id, + method: type.method, + params: params + } + let responsePromise: ResponsePromise = { method: type.method, timerStart: Date.now(), resolve, reject }; + if (trace != Trace.Off && tracer) { + traceRequest(requestMessage); + } + try { + messageWriter.write(requestMessage); + } catch (e) { + // Writing the message failed. So we need to reject the promise. + responsePromise.reject(new ResponseError(ErrorCodes.MessageWriteError, e.message ? e.message : 'Unknown reason')); + responsePromise = null; + } + if (responsePromise) { + responsePromises[String(id)] = responsePromise; + } + }); + if (token) { + token.onCancellationRequested((event) => { + connection.sendNotification(CancelNotification.type, { id }); + }); + } + return result; + }, + onRequest: (type: RequestType, handler: RequestHandler) => { + throwIfClosedOrDisposed(); + + requestHandlers[type.method] = handler; + }, + trace: (_value: Trace, _tracer: Tracer, sendNotification: boolean = false) => { + trace = _value; + if (trace === Trace.Off) { + tracer = null; + } else { + tracer = _tracer; + } + if (sendNotification && !isClosed() && !isDisposed()) { + connection.sendNotification(SetTraceNotification.type, { value: Trace.toString(_value) }); + } + }, + onError: errorEmitter.event, + onClose: closeEmitter.event, + onUnhandledNotification: unhandledNotificationEmitter.event, + onDispose: disposeEmitter.event, + dispose: () => { + if (isDisposed()) { + return; + } + state = ConnectionState.Disposed; + disposeEmitter.fire(undefined); + let error = new Error('Connection got disposed.'); + Object.keys(responsePromises).forEach((key) => { + responsePromises[key].reject(error); + }); + responsePromises = Object.create(null); + requestTokens = Object.create(null); + }, + listen: () => { + throwIfClosedOrDisposed(); + throwIfListening(); + + state = ConnectionState.Listening; + messageReader.listen(callback); + } + }; + + connection.onNotification(LogTraceNotification.type, (params) => { + if (trace === Trace.Off) { + return; + } + tracer.log(params.message, trace === Trace.Verbose ? params.verbose : undefined); + }); + return connection as T; +} + +function isMessageReader(value: any): value is MessageReader { + return is.defined(value.listen) && is.undefined(value.read); +} + +function isMessageWriter(value: any): value is MessageWriter { + return is.defined(value.write) && is.undefined(value.end); +} + +export function createServerMessageConnection(reader: MessageReader, writer: MessageWriter, logger: Logger): ServerMessageConnection; +export function createServerMessageConnection(inputStream: NodeJS.ReadableStream, outputStream: NodeJS.WritableStream, logger: Logger): ServerMessageConnection; +export function createServerMessageConnection(input: MessageReader | NodeJS.ReadableStream, output: MessageWriter | NodeJS.WritableStream, logger: Logger): ServerMessageConnection { + let reader = isMessageReader(input) ? input : new StreamMessageReader(input); + let writer = isMessageWriter(output) ? output : new StreamMessageWriter(output); + return createMessageConnection(reader, writer, logger); +} + +export function createClientMessageConnection(reader: MessageReader, writer: MessageWriter, logger: Logger): ClientMessageConnection; +export function createClientMessageConnection(inputStream: NodeJS.ReadableStream, outputStream: NodeJS.WritableStream, logger: Logger): ClientMessageConnection; +export function createClientMessageConnection(input: MessageReader | NodeJS.ReadableStream, output: MessageWriter | NodeJS.WritableStream, logger: Logger): ClientMessageConnection { + let reader = isMessageReader(input) ? input : new StreamMessageReader(input); + let writer = isMessageWriter(output) ? output : new StreamMessageWriter(output); + return createMessageConnection(reader, writer, logger, true); +} diff --git a/dataprotocol-node/jsonrpc/src/messageReader.ts b/dataprotocol-node/jsonrpc/src/messageReader.ts new file mode 100644 index 0000000000..2bf18c3620 --- /dev/null +++ b/dataprotocol-node/jsonrpc/src/messageReader.ts @@ -0,0 +1,265 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import { ChildProcess } from 'child_process'; + +import { Message } from './messages'; +import { Event, Emitter } from './events'; +import * as is from './is'; + +let DefaultSize: number = 8192; +let CR: number = new Buffer('\r', 'ascii')[0]; +let LF: number = new Buffer('\n', 'ascii')[0]; +let CRLF: string = '\r\n'; + +class MessageBuffer { + + private encoding: string; + private index: number; + private buffer: Buffer; + + constructor(encoding: string = 'utf-8') { + this.encoding = encoding; + this.index = 0; + this.buffer = new Buffer(DefaultSize); + } + + public append(chunk: Buffer | String): void { + var toAppend: Buffer = chunk; + if (typeof (chunk) == 'string') { + var str = chunk; + toAppend = new Buffer(str.length); + toAppend.write(str, 0, str.length, this.encoding); + } + if (this.buffer.length - this.index >= toAppend.length) { + toAppend.copy(this.buffer, this.index, 0, toAppend.length); + } else { + var newSize = (Math.ceil((this.index + toAppend.length) / DefaultSize) + 1) * DefaultSize; + if (this.index === 0) { + this.buffer = new Buffer(newSize); + toAppend.copy(this.buffer, 0, 0, toAppend.length); + } else { + this.buffer = Buffer.concat([this.buffer.slice(0, this.index), toAppend], newSize); + } + } + this.index += toAppend.length; + } + + public tryReadHeaders(): { [key: string]: string; } { + let result: { [key: string]: string; } = undefined; + let current = 0; + while (current + 3 < this.index && (this.buffer[current] !== CR || this.buffer[current + 1] !== LF || this.buffer[current + 2] !== CR || this.buffer[current + 3] !== LF)) { + current++; + } + // No header / body separator found (e.g CRLFCRLF) + if (current + 3 >= this.index) { + return result; + } + result = Object.create(null); + let headers = this.buffer.toString('ascii', 0, current).split(CRLF); + headers.forEach((header) => { + let index: number = header.indexOf(':'); + if (index === -1) { + throw new Error('Message header must separate key and value using :'); + } + let key = header.substr(0, index); + let value = header.substr(index + 1).trim(); + result[key] = value; + }) + + let nextStart = current + 4; + this.buffer = this.buffer.slice(nextStart); + this.index = this.index - nextStart; + return result; + } + + public tryReadContent(length: number): string { + if (this.index < length) { + return null; + } + let result = this.buffer.toString(this.encoding, 0, length); + let nextStart = length; + this.buffer.copy(this.buffer, 0, nextStart); + this.index = this.index - nextStart; + return result; + } + + public get numberOfBytes(): number { + return this.index; + } +} + +export interface DataCallback { + (data: Message): void; +} + +export interface PartialMessageInfo { + messageToken: number; + waitingTime: number; +} + +export interface MessageReader { + onError: Event; + onClose: Event; + onPartialMessage: Event; + listen(callback: DataCallback): void; +} + +export abstract class AbstractMessageReader { + + private errorEmitter: Emitter; + private closeEmitter: Emitter; + + private partialMessageEmitter: Emitter; + + constructor() { + this.errorEmitter = new Emitter(); + this.closeEmitter = new Emitter(); + this.partialMessageEmitter = new Emitter(); + } + + public get onError(): Event { + return this.errorEmitter.event; + } + + protected fireError(error: any): void { + this.errorEmitter.fire(this.asError(error)); + } + + public get onClose(): Event { + return this.closeEmitter.event; + } + + protected fireClose(): void { + this.closeEmitter.fire(undefined); + } + + public get onPartialMessage(): Event { + return this.partialMessageEmitter.event; + } + + protected firePartialMessage(info: PartialMessageInfo): void { + this.partialMessageEmitter.fire(info); + } + + private asError(error: any): Error { + if (error instanceof Error) { + return error; + } else { + return new Error(`Reader recevied error. Reason: ${is.string(error.message) ? error.message : 'unknown'}`); + } + } +} + +export class StreamMessageReader extends AbstractMessageReader implements MessageReader { + + private readable: NodeJS.ReadableStream; + private callback: DataCallback; + private buffer: MessageBuffer; + private nextMessageLength: number; + private messageToken: number; + private partialMessageTimer: NodeJS.Timer; + private _partialMessageTimeout: number; + + public constructor(readable: NodeJS.ReadableStream, encoding: string = 'utf-8') { + super(); + this.readable = readable; + this.buffer = new MessageBuffer(encoding); + this._partialMessageTimeout = 10000; + } + + public set partialMessageTimeout(timeout: number) { + this._partialMessageTimeout = timeout; + } + + public get partialMessageTimeout(): number { + return this._partialMessageTimeout; + } + + public listen(callback: DataCallback): void { + this.nextMessageLength = -1; + this.messageToken = 0; + this.partialMessageTimer = undefined; + this.callback = callback; + this.readable.on('data', (data: Buffer) => { + this.onData(data); + }); + this.readable.on('error', (error: any) => this.fireError(error)); + this.readable.on('close', () => this.fireClose()); + } + + private onData(data: Buffer | String): void { + this.buffer.append(data); + while (true) { + if (this.nextMessageLength === -1) { + let headers = this.buffer.tryReadHeaders(); + if (!headers) { + return; + } + let contentLength = headers['Content-Length']; + if (!contentLength) { + throw new Error('Header must provide a Content-Length property.'); + } + let length = parseInt(contentLength); + if (isNaN(length)) { + throw new Error('Content-Length value must be a number.'); + } + this.nextMessageLength = length; + } + var msg = this.buffer.tryReadContent(this.nextMessageLength); + if (msg === null) { + /** We haven't recevied the full message yet. */ + this.setPartialMessageTimer(); + return; + } + this.clearPartialMessageTimer(); + this.nextMessageLength = -1; + this.messageToken++; + var json = JSON.parse(msg); + this.callback(json); + } + } + + private clearPartialMessageTimer(): void { + if (this.partialMessageTimer) { + clearTimeout(this.partialMessageTimer); + this.partialMessageTimer = undefined; + } + } + + private setPartialMessageTimer(): void { + this.clearPartialMessageTimer(); + if (this._partialMessageTimeout <= 0) { + return; + } + this.partialMessageTimer = setTimeout((token, timeout) => { + this.partialMessageTimer = undefined; + if (token === this.messageToken) { + this.firePartialMessage({ messageToken: token, waitingTime: timeout }); + this.setPartialMessageTimer(); + } + }, this._partialMessageTimeout, this.messageToken, this._partialMessageTimeout); + } +} + +export class IPCMessageReader extends AbstractMessageReader implements MessageReader { + + private process: NodeJS.Process | ChildProcess; + + public constructor(process: NodeJS.Process | ChildProcess) { + super(); + this.process = process; + + let eventEmitter: NodeJS.EventEmitter = this.process; + eventEmitter.on('error', (error: any) => this.fireError(error)); + eventEmitter.on('close', () => this.fireClose()); + } + + public listen(callback: DataCallback): void { + let eventEmitter: NodeJS.EventEmitter = this.process; + eventEmitter.on('message', callback); + } +} \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/src/messageWriter.ts b/dataprotocol-node/jsonrpc/src/messageWriter.ts new file mode 100644 index 0000000000..bcf0d6b976 --- /dev/null +++ b/dataprotocol-node/jsonrpc/src/messageWriter.ts @@ -0,0 +1,118 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import { ChildProcess } from 'child_process'; + +import { Message } from './messages'; +import { Event, Emitter } from './events'; +import * as is from './is'; + +let ContentLength: string = 'Content-Length: '; +let CRLF = '\r\n'; + +export interface MessageWriter { + onError: Event<[Error, Message, number]>; + onClose: Event; + write(msg: Message): void; +} + +export abstract class AbstractMessageWriter { + + private errorEmitter: Emitter<[Error, Message, number]>; + private closeEmitter: Emitter; + + constructor() { + this.errorEmitter = new Emitter<[Error, Message, number]>(); + this.closeEmitter = new Emitter(); + } + + public get onError(): Event<[Error, Message, number]> { + return this.errorEmitter.event; + } + + protected fireError(error: any, message?: Message, count?: number): void { + this.errorEmitter.fire([this.asError(error), message, count]); + } + + public get onClose(): Event { + return this.closeEmitter.event; + } + + protected fireClose(): void { + this.closeEmitter.fire(undefined); + } + + private asError(error: any): Error { + if (error instanceof Error) { + return error; + } else { + return new Error(`Writer recevied error. Reason: ${is.string(error.message) ? error.message : 'unknown'}`); + } + } +} + +export class StreamMessageWriter extends AbstractMessageWriter implements MessageWriter { + + private writable: NodeJS.WritableStream; + private encoding: string; + private errorCount: number; + + public constructor(writable: NodeJS.WritableStream, encoding: string = 'utf8') { + super(); + this.writable = writable; + this.encoding = encoding; + this.errorCount = 0; + this.writable.on('error', (error) => this.fireError(error)); + this.writable.on('close', () => this.fireClose()); + } + + public write(msg: Message): void { + let json = JSON.stringify(msg); + let contentLength = Buffer.byteLength(json, this.encoding); + + let headers: string[] = [ + ContentLength, contentLength.toString(), CRLF, + CRLF + ]; + try { + // Header must be written in ASCII encoding + this.writable.write(headers.join(''), 'ascii'); + + // Now write the content. This can be written in any encoding + this.writable.write(json, this.encoding); + this.errorCount = 0; + } catch (error) { + this.errorCount++; + this.fireError(error, msg, this.errorCount); + } + } +} + +export class IPCMessageWriter extends AbstractMessageWriter implements MessageWriter { + + private process: NodeJS.Process | ChildProcess; + private errorCount: number; + + public constructor(process: NodeJS.Process | ChildProcess) { + super(); + this.process = process; + this.errorCount = 0; + + let eventEmitter: NodeJS.EventEmitter = this.process; + eventEmitter.on('error', (error) => this.fireError(error)); + eventEmitter.on('close', () => this.fireClose); + } + + public write(msg: Message): void { + try { + this.process.send(msg); + this.errorCount = 0; + } catch (error) { + this.errorCount++; + this.fireError(error, msg, this.errorCount); + } + } +} \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/src/messages.ts b/dataprotocol-node/jsonrpc/src/messages.ts new file mode 100644 index 0000000000..6dbf5cb07b --- /dev/null +++ b/dataprotocol-node/jsonrpc/src/messages.ts @@ -0,0 +1,175 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import * as is from './is'; + +/** + * A language server message + */ +export interface Message { + jsonrpc: string; +} + +/** + * Request message + */ +export interface RequestMessage extends Message { + + /** + * The request id. + */ + id: number | string; + + /** + * The method to be invoked. + */ + method: string; + + /** + * The method's params. + */ + params?: any +} + +/** + * Predefined error codes. + */ +export namespace ErrorCodes { + // Defined by JSON RPC + export const ParseError: number = -32700; + export const InvalidRequest: number = -32600; + export const MethodNotFound: number = -32601; + export const InvalidParams: number = -32602; + export const InternalError: number = -32603; + export const serverErrorStart: number = -32099 + export const serverErrorEnd: number = -32000; + + // Defined by VSCode. + export const MessageWriteError: number = 1; + export const MessageReadError: number = 2; +} + +export interface ResponseErrorLiteral { + /** + * A number indicating the error type that occured. + */ + code: number; + + /** + * A string providing a short decription of the error. + */ + message: string; + + /** + * A Primitive or Structured value that contains additional + * information about the error. Can be omitted. + */ + data?: D; +} + +/** + * A error object return in a response in case a request + * has failed. + */ +export class ResponseError extends Error { + + public code: number; + + public message: string; + + public data: D; + + constructor(code: number, message: string, data?: D) { + super(message); + this.code = code; + this.message = message; + if (is.defined(data)) { + this.data = data; + } + } + + public toJson(): ResponseErrorLiteral { + let result: ResponseErrorLiteral = { + code: this.code, + message: this.message + }; + if (is.defined(this.data)) { + result.data = this.data + }; + return result; + } +} + +/** + * A response message. + */ +export interface ResponseMessage extends Message { + /** + * The request id. + */ + id: number | string; + + /** + * The result of a request. This can be omitted in + * the case of an error. + */ + result?: any; + + /** + * The error object in case a request fails. + */ + error?: ResponseErrorLiteral; +} + +/** + * A interface to type the request parameter / response pair + */ +export interface RequestType { + method: string; +} + +/** + * Notification Message + */ +export interface NotificationMessage extends Message { + /** + * The method to be invoked. + */ + method: string; + + /** + * The notification's params. + */ + params?: any +} + +export interface NotificationType

{ + method: string; +} + +/** + * Tests if the given message is a request message + */ +export function isRequestMessage(message: Message): message is RequestMessage { + let candidate = message; + return candidate && is.string(candidate.method) && (is.string(candidate.id) || is.number(candidate.id)); +} + +/** + * Tests if the given message is a notification message + */ +export function isNotificationMessage(message: Message): message is NotificationMessage { + let candidate = message; + return candidate && is.string(candidate.method) && is.undefined((message).id); +} + +/** + * Tests if the given message is a response message + */ +export function isReponseMessage(message: Message): message is ResponseMessage { + let candidate = message; + return candidate && (is.defined(candidate.result) || is.defined(candidate.error)) && (is.string(candidate.id) || is.number(candidate.id)); +} \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/src/tsconfig.json b/dataprotocol-node/jsonrpc/src/tsconfig.json new file mode 100644 index 0000000000..6744afdba5 --- /dev/null +++ b/dataprotocol-node/jsonrpc/src/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES5", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "declaration": true, + "stripInternal": true, + "outDir": "../lib" + } +} \ No newline at end of file diff --git a/dataprotocol-node/jsonrpc/src/typings/promise.d.ts b/dataprotocol-node/jsonrpc/src/typings/promise.d.ts new file mode 100644 index 0000000000..925c943df5 --- /dev/null +++ b/dataprotocol-node/jsonrpc/src/typings/promise.d.ts @@ -0,0 +1,112 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + +/** + * The Thenable (E.g. PromiseLike) and Promise declarions are taken from TypeScript's + * lib.core.es6.d.ts file. See above Copyright notice. + */ + +/** + * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, + * and others. This API makes no assumption about what promise libary is being used which + * enables reusing existing code without migrating to a specific promise implementation. Still, + * we recommand the use of native promises which are available in VS Code. + */ +interface Thenable { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: R) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; + then(onfulfilled?: (value: R) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; +} + +/** + * Represents the completion of an asynchronous operation + */ +interface Promise extends Thenable { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Promise; + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Promise; + + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: (reason: any) => T | Thenable): Promise; +} + +interface PromiseConstructor { + /** + * Creates a new Promise. + * @param executor A callback used to initialize the promise. This callback is passed two arguments: + * a resolve callback used resolve the promise with a value or the result of another promise, + * and a reject callback used to reject the promise with a provided reason or error. + */ + new (executor: (resolve: (value?: T | Thenable) => void, reject: (reason?: any) => void) => void): Promise; + + /** + * Creates a Promise that is resolved with an array of results when all of the provided Promises + * resolve, or rejected when any Promise is rejected. + * @param values An array of Promises. + * @returns A new Promise. + */ + all(values: Array>): Promise; + + /** + * Creates a Promise that is resolved or rejected when any of the provided Promises are resolved + * or rejected. + * @param values An array of Promises. + * @returns A new Promise. + */ + race(values: Array>): Promise; + + /** + * Creates a new rejected promise for the provided reason. + * @param reason The reason the promise was rejected. + * @returns A new rejected Promise. + */ + reject(reason: any): Promise; + + /** + * Creates a new rejected promise for the provided reason. + * @param reason The reason the promise was rejected. + * @returns A new rejected Promise. + */ + reject(reason: any): Promise; + + /** + * Creates a new resolved promise for the provided value. + * @param value A promise. + * @returns A promise whose internal state matches the provided promise. + */ + resolve(value: T | Thenable): Promise; + + /** + * Creates a new resolved promise . + * @returns A resolved promise. + */ + resolve(): Promise; +} + +declare var Promise: PromiseConstructor; diff --git a/dataprotocol-node/jsonrpc/thirdpartynotices.txt b/dataprotocol-node/jsonrpc/thirdpartynotices.txt new file mode 100644 index 0000000000..ae194f9203 --- /dev/null +++ b/dataprotocol-node/jsonrpc/thirdpartynotices.txt @@ -0,0 +1,31 @@ +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +For Microsoft vscode-jsonrpc + +This project incorporates material from the project(s) listed below (collectively, “Third Party Code”). +Microsoft is not the original author of the Third Party Code. The original copyright notice and license +under which Microsoft received such Third Party Code are set out below. This Third Party Code is licensed +to you under their original license terms set forth below. Microsoft reserves all other rights not expressly +granted, whether by implication, estoppel or otherwise. + +1. DefinitelyTyped version 0.0.1 (https://github.com/borisyankov/DefinitelyTyped) + +This project is licensed under the Source EULA. +Copyrights are respective of each contributor listed at the beginning of each definition file. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/dataprotocol-node/types/.eslintrc b/dataprotocol-node/types/.eslintrc new file mode 100644 index 0000000000..306df0be07 --- /dev/null +++ b/dataprotocol-node/types/.eslintrc @@ -0,0 +1,24 @@ +{ + "rules": { + "indent": [ + 2, + "tab" + ], + "quotes": [ + 2, + "single" + ], + "linebreak-style": [ + 2, + "windows" + ], + "semi": [ + 2, + "always" + ] + }, + "env": { + "node": true + }, + "extends": "eslint:recommended" +} \ No newline at end of file diff --git a/dataprotocol-node/types/.gitignore b/dataprotocol-node/types/.gitignore new file mode 100644 index 0000000000..08eb0a07a6 --- /dev/null +++ b/dataprotocol-node/types/.gitignore @@ -0,0 +1 @@ +!bin/ \ No newline at end of file diff --git a/dataprotocol-node/types/.npmignore b/dataprotocol-node/types/.npmignore new file mode 100644 index 0000000000..2b1e5b19e1 --- /dev/null +++ b/dataprotocol-node/types/.npmignore @@ -0,0 +1,9 @@ +.vscode/ +lib/test/ +lib/*.map +src/ +test/ +.eslintrc +.gitignore +gulpfile.js +tsd.json \ No newline at end of file diff --git a/dataprotocol-node/types/.vscode/settings.json b/dataprotocol-node/types/.vscode/settings.json new file mode 100644 index 0000000000..0c0c4bcd8e --- /dev/null +++ b/dataprotocol-node/types/.vscode/settings.json @@ -0,0 +1,9 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "javascript.validate.enable": false, + "files.trimTrailingWhitespace": true, + "eslint.enable": false, + "editor.insertSpaces": false, + "editor.tabSize": 4, + "typescript.tsdk": "./node_modules/typescript/lib" +} \ No newline at end of file diff --git a/dataprotocol-node/types/.vscode/tasks.json b/dataprotocol-node/types/.vscode/tasks.json new file mode 100644 index 0000000000..0ed84877c1 --- /dev/null +++ b/dataprotocol-node/types/.vscode/tasks.json @@ -0,0 +1,9 @@ +{ + "version": "0.1.0", + "command": "npm", + "isShellCommand": true, + "args": ["run", "watch"], + "showOutput": "silent", + "isWatching": true, + "problemMatcher": "$tsc-watch" +} \ No newline at end of file diff --git a/dataprotocol-node/types/README.md b/dataprotocol-node/types/README.md new file mode 100644 index 0000000000..c61863e4eb --- /dev/null +++ b/dataprotocol-node/types/README.md @@ -0,0 +1,4 @@ +# Microsoft Data Management Protocol - Node + +## License +[Source EULA](https://github.com/Microsoft/sqlopsstudio/blob/dev/license.txt) \ No newline at end of file diff --git a/dataprotocol-node/types/package.json b/dataprotocol-node/types/package.json new file mode 100644 index 0000000000..e8dcaf1e07 --- /dev/null +++ b/dataprotocol-node/types/package.json @@ -0,0 +1,26 @@ +{ + "name": "dataprotocol-languageserver-types", + "description": "Types used by the Language server for node", + "version": "1.0.4", + "author": "Microsoft Corporation", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/vscode-languageserver-node.git" + }, + "bugs": { + "url": "https://github.com/Microsoft/vscode-languageserver-node/issues" + }, + "main": "./lib/main.js", + "typings": "./lib/main", + "devDependencies": { + "mocha": "^2.4.5", + "typescript": "2.0.3" + }, + "scripts": { + "prepublish": "tsc -p ./src", + "compile": "tsc -p ./src", + "watch": "tsc -w -p ./src", + "test": "mocha" + } +} diff --git a/dataprotocol-node/types/src/main.ts b/dataprotocol-node/types/src/main.ts new file mode 100644 index 0000000000..3ff93c985a --- /dev/null +++ b/dataprotocol-node/types/src/main.ts @@ -0,0 +1,2468 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export interface CreateSessionResponse { + sessionId: string; +} + +export interface SessionCreatedParameters { + success: boolean; + sessionId: string; + rootNode: NodeInfo; + errorMessage: string; +} + +export interface ExpandResponse { + nodePath: string; + sessionId: string; + nodes: NodeInfo[]; + errorMessage: string; +} + +export interface NodeInfo { + nodePath: string; + nodeType: string; + nodeSubType: string; + nodeStatus: string; + label: string; + isLeaf: boolean; + metadata: ObjectMetadata; + errorMessage: string; +} + +export interface ExpandParams { + sessionId: string; + nodePath: string; +} + +export interface CloseSessionParams { + sessionId: string; +} + +export interface CloseSessionResponse { + success: boolean; + sessionId: string; +} + +export interface CategoryValue { + displayName: string; + + name: string; +} + +export interface ServiceOption { + name: string; + + displayName: string; + + description: string; + + groupName: string; + + valueType: string; + + defaultValue: string; + + objectType: string; + + categoryValues: CategoryValue[]; + + isRequired: boolean; + + isArray: boolean; +} + +export interface ConnectionOption { + name: string; + + displayName: string; + + description: string; + + groupName: string; + + valueType: string; + + defaultValue: string; + + objectType: string; + + categoryValues: CategoryValue[]; + + specialValueType: string; + + isIdentity: boolean; + + isRequired: boolean; + + isArray: boolean; +} + +export interface ConnectionProviderOptions { + options: ConnectionOption[]; +} + +export interface AdminServicesProviderOptions { + databaseInfoOptions: ServiceOption[]; + + databaseFileInfoOptions: ServiceOption[]; + + fileGroupInfoOptions: ServiceOption[]; +} + +export interface FeatureMetadataProvider { + enabled: boolean; + featureName: string; + optionsMetadata: ServiceOption[]; +} + +export interface DataProtocolServerCapabilities { + protocolVersion: string; + + providerName: string; + + providerDisplayName: string; + + connectionProvider: ConnectionProviderOptions; + + adminServicesProvider: AdminServicesProviderOptions; + + features: FeatureMetadataProvider[]; +} + +/** + * Parameters to initialize a connection to a database + */ +export interface ConnectionDetails { + + /** + * connection options + */ + options: {}; +} + +/** + * Summary that identifies a unique database connection. + */ +export class ConnectionSummary { + /** + * server name + */ + public serverName: string; + + /** + * database name + */ + public databaseName: string; + + /** + * user name + */ + public userName: string; +} + +/** + * Connection response format. + */ +export class ConnectionCompleteParams { + /** + * URI identifying the owner of the connection + */ + public ownerUri: string; + + /** + * connection id returned from service host. + */ + public connectionId: string; + + /** + * any diagnostic messages return from the service host. + */ + public messages: string; + + /** + * Error message returned from the engine, if any. + */ + public errorMessage: string; + + /** + * Error number returned from the engine, if any. + */ + public errorNumber: number; + + /** + * Information about the connected server. + */ + public serverInfo: ServerInfo; + + /** + * information about the actual connection established + */ + public connectionSummary: ConnectionSummary; +} + +/** + * Update event parameters + */ +export class IntelliSenseReadyParams { + /** + * URI identifying the text document + */ + public ownerUri: string; +} + +/** + * Information about a SQL Server instance. + */ +export class ServerInfo { + /** + * The major version of the SQL Server instance. + */ + public serverMajorVersion: number; + + /** + * The minor version of the SQL Server instance. + */ + public serverMinorVersion: number; + + /** + * The build of the SQL Server instance. + */ + public serverReleaseVersion: number; + + /** + * The ID of the engine edition of the SQL Server instance. + */ + public engineEditionId: number; + + /** + * String containing the full server version text. + */ + public serverVersion: string; + + /** + * String describing the product level of the server. + */ + public serverLevel: string; + + /** + * The edition of the SQL Server instance. + */ + public serverEdition: string; + + /** + * Whether the SQL Server instance is running in the cloud (Azure) or not. + */ + public isCloud: boolean; + + /** + * The version of Azure that the SQL Server instance is running on, if applicable. + */ + public azureVersion: number; + + /** + * The Operating System version string of the machine running the SQL Server instance. + */ + public osVersion: string; +} + +export class CapabiltiesDiscoveryResult { + public capabilities: DataProtocolServerCapabilities; +} + +// Task Services types + +export enum TaskStatus { + notStarted = 0, + inProgress = 1, + succeeded = 2, + succeededWithWarning = 3, + failed = 4, + canceled = 5 +} + +export enum TaskExecutionMode { + execute = 0, + script = 1, + executeAndScript = 2, +} + +export interface TaskInfo { + taskId: string; + status: TaskStatus; + taskExecutionMode: TaskExecutionMode; + serverName: string; + databaseName: string; + name: string; + description: string; + providerName: string; + isCancelable: boolean; +} + +export interface ListTasksParams { + + listActiveTasksOnly: boolean; +} + +export interface ListTasksResponse { + tasks: TaskInfo[]; +} + +export interface CancelTaskParams { + taskId: string; +} + +export interface TaskProgressInfo { + taskId: string; + status: TaskStatus; + message: string; + script: string; + duration: number; +} + +// Admin Services types + +export interface DatabaseInfo { + /** + * database options + */ + options: {}; +} + +export interface BackupConfigInfo { + recoveryModel: string; + defaultBackupFolder: string; + backupEncryptors: {}; +} + +export interface LoginInfo { + name: string; +} + +export interface CreateDatabaseParams { + ownerUri: string; + + databaseInfo: DatabaseInfo; +} + +export interface CreateDatabaseResponse { + result: boolean; + taskId: number; +} + +export interface DefaultDatabaseInfoParams { + ownerUri: string; +} + +export interface DefaultDatabaseInfoResponse { + defaultDatabaseInfo: DatabaseInfo; +} + +export interface GetDatabaseInfoResponse { + databaseInfo: DatabaseInfo; +} + +export interface GetDatabaseInfoParams { + ownerUri: string; +} + +export interface BackupConfigInfoResponse { + backupConfigInfo: BackupConfigInfo; +} + +export interface CreateLoginParams { + ownerUri: string; + + loginInfo: LoginInfo; +} + +export interface CreateLoginResponse { + result: boolean; + taskId: number; +} + +// Disaster Recovery types + +export interface BackupInfo { + ownerUri: string; + + databaseName: string; + + backupType: number; + + backupComponent: number; + + backupDeviceType: number; + + selectedFiles: string; + + backupsetName: string; + + selectedFileGroup: { [path: string]: string }; + + // List of {key: backup path, value: device type} + backupPathDevices: { [path: string]: number }; + + backupPathList: [string]; + + isCopyOnly: boolean; + + formatMedia: boolean; + + initialize: boolean; + + skipTapeHeader: boolean; + + mediaName: string; + + mediaDescription: string; + + checksum: boolean; + + continueAfterError: boolean; + + logTruncation: boolean; + + tailLogBackup: boolean; + + retainDays: number; + + compressionOption: number; + + verifyBackupRequired: boolean; + + encryptionAlgorithm: number; + + encryptorType: number; + + encryptorName: string; +} + +export interface BackupParams { + ownerUri: string; + + backupInfo: BackupInfo; + + taskExecutionMode: TaskExecutionMode; +} + +export interface BackupResponse { + result: boolean; + taskId: number; +} + +export interface RestoreParams { + ownerUri: string; + options: {}; + taskExecutionMode: TaskExecutionMode; +} + +export interface RestoreConfigInfoRequestParams { + ownerUri: string; +} + +export interface RestoreConfigInfoResponse { + configInfo: { [key: string]: any }; +} + +export interface RestoreDatabaseFileInfo { + fileType: string; + + logicalFileName: string; + + originalFileName: string; + + restoreAsFileName: string; +} + +export interface FileBrowserOpenParams { + ownerUri: string; + expandPath: string; + fileFilters: string[]; + changeFilter: boolean; +} + +export interface FileTreeNode { + children: FileTreeNode[]; + isExpanded: boolean; + isFile: boolean; + name: string; + fullPath: string; +} + +export interface FileTree { + rootNode: FileTreeNode; + selectedNode: FileTreeNode; +} + +export interface FileBrowserOpenedParams { + ownerUri: string; + fileTree: FileTree; + succeeded: boolean; + message: string; +} + +export interface FileBrowserExpandParams { + ownerUri: string; + expandPath: string; +} + +export interface FileBrowserExpandedParams { + ownerUri: string; + expandPath: string; + children: FileTreeNode[]; + succeeded: boolean; + message: string; +} + +export interface FileBrowserValidateParams { + ownerUri: string; + serviceType: string; + selectedFiles: string[]; +} + +export interface FileBrowserValidatedParams { + succeeded: boolean; + message: string; +} + +export interface FileBrowserCloseParams { + ownerUri: string; +} + +export interface FileBrowserCloseResponse { + succeeded: boolean; + message: string; +} + +export interface DatabaseFileInfo { + properties: LocalizedPropertyInfo[]; + id: string; + isSelected: boolean; +} + +export interface LocalizedPropertyInfo { + propertyName: string; + propertyValue: string; + propertyDisplayName: string; + propertyValueDisplayName: string; +} + +export interface RestorePlanDetailInfo { + name: string; + currentValue: any; + isReadOnly: boolean; + isVisible: boolean; + defaultValue: any; + +} + +export interface RestorePlanResponse { + sessionId: string; + backupSetsToRestore: DatabaseFileInfo[]; + canRestore: boolean; + errorMessage: string; + dbFiles: RestoreDatabaseFileInfo[]; + databaseNamesFromBackupSets: string[]; + planDetails: { [key: string]: RestorePlanDetailInfo }; +} + +export interface RestoreResponse { + result: boolean; + taskId: string; + errorMessage: string; +} + +// Query Execution types +export interface ResultSetSummary { + id: number; + batchId: number; + rowCount: number; + columnInfo: IDbColumn[]; +} + +export interface BatchSummary { + hasError: boolean; + id: number; + selection: ISelectionData; + resultSetSummaries: ResultSetSummary[]; + executionElapsed: string; + executionEnd: string; + executionStart: string; +} + +export interface IDbColumn { + allowDBNull?: boolean; + baseCatalogName: string; + baseColumnName: string; + baseSchemaName: string; + baseServerName: string; + baseTableName: string; + columnName: string; + columnOrdinal?: number; + columnSize?: number; + isAliased?: boolean; + isAutoIncrement?: boolean; + isExpression?: boolean; + isHidden?: boolean; + isIdentity?: boolean; + isKey?: boolean; + isBytes?: boolean; + isChars?: boolean; + isSqlVariant?: boolean; + isUdt?: boolean; + dataType: string; + isXml?: boolean; + isJson?: boolean; + isLong?: boolean; + isReadOnly?: boolean; + isUnique?: boolean; + numericPrecision?: number; + numericScale?: number; + udtAssemblyQualifiedName: string; + dataTypeName: string; +} + +export interface IGridResultSet { + columns: IDbColumn[]; + rowsUri: string; + numberOfRows: number; +} + +export interface IResultMessage { + batchId?: number; + isError: boolean; + time: string; + message: string; +} + +export interface ISelectionData { + startLine: number; + startColumn: number; + endLine: number; + endColumn: number; +} + +export interface QueryExecuteBatchNotificationParams { + batchSummary: BatchSummary; + ownerUri: string; +} + +export interface DbCellValue { + displayValue: string; + isNull: boolean; +} + +export enum EditRowState { + clean = 0, + dirtyInsert = 1, + dirtyDelete = 2, + dirtyUpdate = 3 +} + +export interface EditRow { + cells: DbCellValue[]; + id: number; + isDirty: boolean; + state: EditRowState; +} + +export interface EditCell extends DbCellValue { + isDirty: boolean; +} + +export interface EditCell extends DbCellValue { + +} + +export class MetadataQueryParams { + /** + * Owner URI of the connection that changed. + */ + public ownerUri: string; +} + +export enum MetadataType { + Table = 0, + View = 1, + SProc = 2, + Function = 3 +} + +export class ObjectMetadata { + metadataType: MetadataType; + + metadataTypeName: string; + + urn: string; + + name: string; + + schema: string; +} + +export class MetadataQueryResult { + public metadata: ObjectMetadata[]; +} + +export interface ScriptingParamDetails { + filePath: string; + scriptCompatibilityOption: string; + targetDatabaseEngineEdition: string; + targetDatabaseEngineType: string; +} + +export enum ScriptOperation { + Select = 0, + Create = 1, + Insert = 2, + Update = 3, + Delete = 4 +} + +export interface ScriptOptions { + /** + * Generate ANSI padding statements + */ + scriptANSIPadding?: boolean; + + /** + * Append the generated script to a file + */ + appendToFile?: boolean; + + /** + * Continue to script if an error occurs. Otherwise, stop. + */ + continueScriptingOnError?: boolean; + + /** + * Convert user-defined data types to base types. + */ + convertUDDTToBaseType?: boolean; + + /** + * Generate script for dependent objects for each object scripted. + */ + generateScriptForDependentObjects?: boolean; + + /** + * Include descriptive headers for each object generated. + */ + includeDescriptiveHeaders?: boolean; + + /** + * Check that an object with the given name exists before dropping or altering or that an object with the given name does not exist before creating. + */ + includeIfNotExists?: boolean; + + /** + * Script options to set vardecimal storage format. + */ + includeVarDecimal?: boolean; + + /** + * Include system generated constraint names to enforce declarative referential integrity. + */ + scriptDRIIncludeSystemNames?: boolean; + + /** + * Include statements in the script that are not supported on the specified SQL Server database engine type. + */ + includeUnsupportedStatements?: boolean; + + /** + * Prefix object names with the object schema. + */ + schemaQualify?: boolean; + + /** + * Script options to set bindings option. + */ + bindings?: boolean; + + /** + * Script the objects that use collation. + */ + collation?: boolean; + + /** + * Script the default values. + */ + default?: boolean; + + /** + * Script Object CREATE/DROP statements. + */ + scriptCreateDrop: string; + + /** + * Script the Extended Properties for each object scripted. + */ + scriptExtendedProperties?: boolean; + + /** + * Script only features compatible with the specified version of SQL Server. + */ + scriptCompatibilityOption: string; + + /** + * Script only features compatible with the specified SQL Server database engine type. + */ + targetDatabaseEngineType: string; + + /** + * Script only features compatible with the specified SQL Server database engine edition. + */ + targetDatabaseEngineEdition: string; + + /** + * Script all logins available on the server. Passwords will not be scripted. + */ + scriptLogins?: boolean; + + /** + * Generate object-level permissions. + */ + scriptObjectLevelPermissions?: boolean; + + /** + * Script owner for the objects. + */ + scriptOwner?: boolean; + + /** + * Script statistics, and optionally include histograms, for each selected table or view. + */ + scriptStatistics: string; + + /** + * Generate USE DATABASE statement. + */ + scripUseDatabase?: boolean; + + /** + * Generate script that contains schema only or schema and data. + */ + typeOfDataToScript: string; + + /** + * Scripts the change tracking information. + */ + scriptChangeTracking?: boolean; + + /** + * Script the check constraints for each table or view scripted. + */ + scriptCheckConstraints?: boolean; + + /** + * Scripts the data compression information. + */ + scriptDataCompressionOptions?: boolean; + + /** + * Script the foreign keys for each table scripted. + */ + scriptForeignKeys?: boolean; + + /** + * Script the full-text indexes for each table or indexed view scripted. + */ + scriptFullTextIndexes?: boolean; + + /** + * Script the indexes (including XML and clustered indexes) for each table or indexed view scripted. + */ + scriptIndexes?: boolean; + + /** + * Script the primary keys for each table or view scripted + */ + scriptPrimaryKeys?: boolean; + + /** + * Script the triggers for each table or view scripted + */ + scriptTriggers?: boolean; + + /** + * Script the unique keys for each table or view scripted. + */ + uniqueKeys?: boolean; +} + +export interface ScriptingObject { + /** + * The database object type + */ + type: string; + + /** + * The schema of the database object + */ + schema: string; + + /** + * The database object name + */ + name: string; +} + +export interface ScriptingParams { + /** + * File path used when writing out the script. + */ + filePath: string; + + /** + * Whether scripting to a single file or file per object. + */ + scriptDestination: string; + + /** + * Connection string of the target database the scripting operation will run against. + */ + connectionString: string; + + /** + * A list of scripting objects to script + */ + scriptingObjects: ScriptingObject[]; + + /** + * A list of scripting object which specify the include criteria of objects to script. + */ + includeObjectCriteria: ScriptingObject[]; + + /** + * A list of scripting object which specify the exclude criteria of objects to not script. + */ + excludeObjectCriteria: ScriptingObject[]; + + /** + * A list of schema name of objects to script. + */ + includeSchemas: string[]; + + /** + * A list of schema name of objects to not script. + */ + excludeSchemas: string[]; + + /** + * A list of type name of objects to script. + */ + includeTypes: string[]; + + /** + * A list of type name of objects to not script. + */ + excludeTypes: string[]; + + /** + * Scripting options for the ScriptingParams + */ + scriptOptions: ScriptOptions; + + /** + * Connection details for the ScriptingParams + */ + connectionDetails: ConnectionDetails; + + /** + * Owner URI of the connection + */ + ownerURI: string; + + /** + * Whether the scripting operation is for + * select script statements + */ + selectScript: boolean; + + /** + * Operation associated with the script request + */ + operation: ScriptOperation; +} + +export interface ScriptingResult { + + operationId: string; + script: string; +} + +export interface ScriptingCompleteParams { + /** + * The error details for an error that occurred during the scripting operation. + */ + errorDetails: string; + + /** + * The error message for an error that occurred during the scripting operation. + */ + errorMessage: string; + + /** + * A value to indicate an error occurred during the scripting operation. + */ + hasError: boolean; + + /** + * A value to indicate the scripting operation was canceled. + */ + canceled: boolean; + + /** + * A value to indicate the scripting operation successfully completed. + */ + success: boolean; +} + +export class ColumnMetadata { + + hasExtendedProperties: boolean; + + defaultValue: string; + + ///

+ /// Escaped identifier for the name of the column + /// + escapedName: string; + + /// + /// Whether or not the column is computed + /// + isComputed: boolean; + + /// + /// Whether or not the column is deterministically computed + /// + isDeterministic: boolean; + + /// + /// Whether or not the column is an identity column + /// + isIdentity: boolean; + + /// + /// The ordinal ID of the column + /// + ordinal: number; + + /// + /// Whether or not the column is calculated on the server side. This could be a computed + /// column or a identity column. + /// + isCalculated: boolean; + + /// + /// Whether or not the column is used in a key to uniquely identify a row + /// + isKey: boolean; + + /// + /// Whether or not the column can be trusted for uniqueness + /// + isTrustworthyForUniqueness: boolean; + +} + +export class TableMetadata { + + columns: ColumnMetadata[]; + +} + +/** + * Position in a text document expressed as zero-based line and character offset. + */ +export interface Position { + /** + * Line position in a document (zero-based). + */ + line: number; + + /** + * Character offset on a line in a document (zero-based). + */ + character: number; +} + +/** + * The Position namespace provides helper functions to work with + * [Position](#Position) literals. + */ +export namespace Position { + /** + * Creates a new Position literal from the given line and character. + * @param line The position's line. + * @param character The position's character. + */ + export function create(line: number, character: number): Position { + return { line, character }; + } + /** + * Checks whether the given liternal conforms to the [Position](#Position) interface. + */ + export function is(value: any): value is Position { + let candidate = value as Position; + return Is.defined(candidate) && Is.number(candidate.line) && Is.number(candidate.character); + } +} + +/** + * A range in a text document expressed as (zero-based) start and end positions. + */ +export interface Range { + /** + * The range's start position + */ + start: Position; + + /** + * The range's end position + */ + end: Position; +} + +/** + * The Range namespace provides helper functions to work with + * [Range](#Range) literals. + */ +export namespace Range { + /** + * Create a new Range liternal. + * @param start The range's start position. + * @param end The range's end position. + */ + export function create(start: Position, end: Position): Range; + /** + * Create a new Range liternal. + * @param startLine The start line number. + * @param startCharacter The start character. + * @param endLine The end line number. + * @param endCharacter The end character. + */ + export function create(startLine: number, startCharacter: number, endLine: number, endCharacter: number): Range; + export function create(one: Position | number, two: Position | number, three?: number, four?: number): Range { + if (Is.number(one) && Is.number(two) && Is.number(three) && Is.number(four)) { + return { start: Position.create(one, two), end: Position.create(three, four) }; + } else if (Position.is(one) && Position.is(two)) { + return { start: one, end: two }; + } else { + throw new Error(`Range#create called with invalid arguments[${one}, ${two}, ${three}, ${four}]`); + } + } + /** + * Checks whether the given literal conforms to the [Range](#Range) interface. + */ + export function is(value: any): value is Range { + let candidate = value as Range; + return Is.defined(candidate) && Position.is(candidate.start) && Position.is(candidate.end); + } +} + +/** + * Represents a location inside a resource, such as a line + * inside a text file. + */ +export interface Location { + uri: string; + range: Range; +} + +/** + * The Location namespace provides helper functions to work with + * [Location](#Location) literals. + */ +export namespace Location { + /** + * Creates a Location literal. + * @param uri The location's uri. + * @param range The location's range. + */ + export function create(uri: string, range: Range): Location { + return { uri, range }; + } + /** + * Checks whether the given literal conforms to the [Location](#Location) interface. + */ + export function is(value: any): value is Location { + let candidate = value as Location; + return Is.defined(candidate) && Range.is(candidate.range) && (Is.string(candidate.uri) || Is.undefined(candidate.uri)); + } +} + +/** + * The diagnostic's serverity. + */ +export const enum DiagnosticSeverity { + /** + * Reports an error. + */ + Error = 1, + /** + * Reports a warning. + */ + Warning = 2, + /** + * Reports an information. + */ + Information = 3, + /** + * Reports a hint. + */ + Hint = 4 +} + +/** + * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects + * are only valid in the scope of a resource. + */ +export interface Diagnostic { + /** + * The range at which the message applies + */ + range: Range; + + /** + * The diagnostic's severity. Can be omitted. If omitted it is up to the + * client to interpret diagnostics as error, warning, info or hint. + */ + severity?: number; + + /** + * The diagnostic's code. Can be omitted. + */ + code?: number | string; + + /** + * A human-readable string describing the source of this + * diagnostic, e.g. 'typescript' or 'super lint'. + */ + source?: string; + + /** + * The diagnostic's message. + */ + message: string; +} + +/** + * The Diagnostic namespace provides helper functions to work with + * [Diagnostic](#Diagnostic) literals. + */ +export namespace Diagnostic { + /** + * Creates a new Diagnostic literal. + */ + export function create(range: Range, message: string, severity?: number, code?: number | string, source?: string): Diagnostic { + let result: Diagnostic = { range, message }; + if (Is.defined(severity)) { + result.severity = severity; + } + if (Is.defined(code)) { + result.code = code; + } + if (Is.defined(source)) { + result.source = source; + } + return result; + } + /** + * Checks whether the given literal conforms to the [Diagnostic](#Diagnostic) interface. + */ + export function is(value: any): value is Diagnostic { + let candidate = value as Diagnostic; + return Is.defined(candidate) + && Range.is(candidate.range) + && Is.string(candidate.message) + && (Is.number(candidate.severity) || Is.undefined(candidate.severity)) + && (Is.number(candidate.code) || Is.string(candidate.code) || Is.undefined(candidate.code)) + && (Is.string(candidate.source) || Is.undefined(candidate.source)); + } +} + + +/** + * Represents a reference to a command. Provides a title which + * will be used to represent a command in the UI and, optionally, + * an array of arguments which will be passed to the command handler + * function when invoked. + */ +export interface Command { + /** + * Title of the command, like `save`. + */ + title: string; + /** + * The identifier of the actual command handler. + */ + command: string; + /** + * Arguments that the command handler should be + * invoked with. + */ + arguments?: any[]; +} + + +/** + * The Command namespace provides helper functions to work with + * [Command](#Command) literals. + */ +export namespace Command { + /** + * Creates a new Command literal. + */ + export function create(title: string, command: string, ...args: any[]): Command { + let result: Command = { title, command }; + if (Is.defined(args) && args.length > 0) { + result.arguments = args; + } + return result; + } + /** + * Checks whether the given literal conforms to the [Command](#Command) interface. + */ + export function is(value: any): value is Command { + let candidate = value as Command; + return Is.defined(candidate) && Is.string(candidate.title) && Is.string(candidate.title); + } +} + +/** + * A text edit applicable to a text document. + */ +export interface TextEdit { + /** + * The range of the text document to be manipulated. To insert + * text into a document create a range where start === end. + */ + range: Range; + + /** + * The string to be inserted. For delete operations use an + * empty string. + */ + newText: string; +} + +/** + * The TextEdit namespace provides helper function to create replace, + * insert and delete edits more easily. + */ +export namespace TextEdit { + /** + * Creates a replace text edit. + * @param range The range of text to be replaced. + * @param newText The new text. + */ + export function replace(range: Range, newText: string): TextEdit { + return { range, newText }; + } + /** + * Creates a insert text edit. + * @param psotion The position to insert the text at. + * @param newText The text to be inserted. + */ + export function insert(position: Position, newText: string): TextEdit { + return { range: { start: position, end: position }, newText }; + } + /** + * Creates a delete text edit. + * @param range The range of text to be deleted. + */ + export function del(range: Range): TextEdit { + return { range, newText: '' }; + } +} + +/** + * A workspace edit represents changes to many resources managed + * in the workspace. + */ +export interface WorkspaceEdit { + // creates: { [uri: string]: string; }; + /** + * Holds changes to existing resources. + */ + changes: { [uri: string]: TextEdit[]; }; + // deletes: string[]; +} + +/** + * A change to capture text edits for existing resources. + */ +export interface TextEditChange { + /** + * Gets all text edits for this change. + * + * @return An array of text edits. + */ + all(): TextEdit[]; + + /** + * Clears the edits for this change. + */ + clear(): void; + + /** + * Insert the given text at the given position. + * + * @param position A position. + * @param newText A string. + */ + insert(position: Position, newText: string): void; + + /** + * Replace the given range with given text for the given resource. + * + * @param range A range. + * @param newText A string. + */ + replace(range: Range, newText: string): void; + + /** + * Delete the text at the given range. + * + * @param range A range. + */ + delete(range: Range): void; +} + +/** + * A workspace change helps constructing changes to a workspace. + */ +export class WorkspaceChange { + private workspaceEdit: WorkspaceEdit; + private textEditChanges: { [uri: string]: TextEditChange }; + + constructor() { + this.workspaceEdit = { + changes: Object.create(null) + }; + this.textEditChanges = Object.create(null); + } + + /** + * Returns the underlying [WorkspaceEdit](#WorkspaceEdit) literal + * use to be returned from a workspace edit operation like rename. + */ + public get edit(): WorkspaceEdit { + return this.workspaceEdit; + } + + /** + * Returns the [TextEditChange](#TextEditChange) to manage text edits + * for resources. + */ + public getTextEditChange(uri: string): TextEditChange { + class TextEditChangeImpl implements TextEditChange { + private edits: TextEdit[]; + constructor(edits: TextEdit[]) { + this.edits = edits; + } + insert(position: Position, newText: string): void { + this.edits.push(TextEdit.insert(position, newText)); + } + replace(range: Range, newText: string): void { + this.edits.push(TextEdit.replace(range, newText)); + } + delete(range: Range): void { + this.edits.push(TextEdit.del(range)); + } + all(): TextEdit[] { + return this.edits; + } + clear(): void { + this.edits.splice(0, this.edits.length); + } + } + let result = this.textEditChanges[uri]; + if (!result) { + let edits: TextEdit[] = []; + this.workspaceEdit.changes[uri] = edits; + result = new TextEditChangeImpl(edits); + this.textEditChanges[uri] = result; + } + return result; + } +} + +/** + * A literal to identify a text document in the client. + */ +export interface TextDocumentIdentifier { + /** + * The text document's uri. + */ + uri: string; +} + +/** + * The TextDocumentIdentifier namespace provides helper functions to work with + * [TextDocumentIdentifier](#TextDocumentIdentifier) literals. + */ +export namespace TextDocumentIdentifier { + /** + * Creates a new TextDocumentIdentifier literal. + * @param uri The document's uri. + */ + export function create(uri: string): TextDocumentIdentifier { + return { uri }; + } + /** + * Checks whether the given literal conforms to the [TextDocumentIdentifier](#TextDocumentIdentifier) interface. + */ + export function is(value: any): value is TextDocumentIdentifier { + let candidate = value as TextDocumentIdentifier; + return Is.defined(candidate) && Is.string(candidate.uri); + } +} + +/** + * An identifier to denote a specific version of a text document. + */ +export interface VersionedTextDocumentIdentifier extends TextDocumentIdentifier { + /** + * The version number of this document. + */ + version: number; +} + +/** + * The VersionedTextDocumentIdentifier namespace provides helper functions to work with + * [VersionedTextDocumentIdentifier](#VersionedTextDocumentIdentifier) literals. + */ +export namespace VersionedTextDocumentIdentifier { + /** + * Creates a new VersionedTextDocumentIdentifier literal. + * @param uri The document's uri. + * @param uri The document's text. + */ + export function create(uri: string, version: number): VersionedTextDocumentIdentifier { + return { uri, version }; + } + + /** + * Checks whether the given literal conforms to the [VersionedTextDocumentIdentifier](#VersionedTextDocumentIdentifier) interface. + */ + export function is(value: any): value is VersionedTextDocumentIdentifier { + let candidate = value as VersionedTextDocumentIdentifier; + return Is.defined(candidate) && Is.string(candidate.uri) && Is.number(candidate.version); + } +} + + +/** + * An item to transfer a text document from the client to the + * server. + */ +export interface TextDocumentItem { + /** + * The text document's uri. + */ + uri: string; + + /** + * The text document's language identifier + */ + languageId: string; + + /** + * The version number of this document (it will strictly increase after each + * change, including undo/redo). + */ + version: number; + + /** + * The content of the opened text document. + */ + text: string; +} + +/** + * The TextDocumentItem namespace provides helper functions to work with + * [TextDocumentItem](#TextDocumentItem) literals. + */ +export namespace TextDocumentItem { + /** + * Creates a new TextDocumentItem literal. + * @param uri The document's uri. + * @param uri The document's language identifier. + * @param uri The document's version number. + * @param uri The document's text. + */ + export function create(uri: string, languageId: string, version: number, text: string): TextDocumentItem { + return { uri, languageId, version, text }; + } + + /** + * Checks whether the given literal conforms to the [TextDocumentItem](#TextDocumentItem) interface. + */ + export function is(value: any): value is TextDocumentItem { + let candidate = value as TextDocumentItem; + return Is.defined(candidate) && Is.string(candidate.uri) && Is.string(candidate.languageId) && Is.number(candidate.version) && Is.string(candidate.text); + } +} + +/** + * The kind of a completion entry. + */ +export const enum CompletionItemKind { + Text = 1, + Method = 2, + Function = 3, + Constructor = 4, + Field = 5, + Variable = 6, + Class = 7, + Interface = 8, + Module = 9, + Property = 10, + Unit = 11, + Value = 12, + Enum = 13, + Keyword = 14, + Snippet = 15, + Color = 16, + File = 17, + Reference = 18 +} + +/** + * A completion item represents a text snippet that is + * proposed to complete text that is being typed. + */ +export interface CompletionItem { + /** + * The label of this completion item. By default + * also the text that is inserted when selecting + * this completion. + */ + label: string; + + /** + * The kind of this completion item. Based of the kind + * an icon is chosen by the editor. + */ + kind?: number; + + /** + * A human-readable string with additional information + * about this item, like type or symbol information. + */ + detail?: string; + + /** + * A human-readable string that represents a doc-comment. + */ + documentation?: string; + + /** + * A string that shoud be used when comparing this item + * with other items. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + sortText?: string; + + /** + * A string that should be used when filtering a set of + * completion items. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + filterText?: string; + + /** + * A string that should be inserted a document when selecting + * this completion. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + insertText?: string; + + /** + * An [edit](#TextEdit) which is applied to a document when selecting + * this completion. When an edit is provided the value of + * [insertText](#CompletionItem.insertText) is ignored. + */ + textEdit?: TextEdit; + + /** + * An optional array of additional [text edits](#TextEdit) that are applied when + * selecting this completion. Edits must not overlap with the main [edit](#CompletionItem.textEdit) + * nor with themselves. + */ + additionalTextEdits?: TextEdit[]; + + /** + * An optional [command](#Command) that is executed *after* inserting this completion. *Note* that + * additional modifications to the current document should be described with the + * [additionalTextEdits](#CompletionItem.additionalTextEdits)-property. + */ + command?: Command; + + /** + * An data entry field that is preserved on a completion item between + * a [CompletionRequest](#CompletionRequest) and a [CompletionResolveRequest] + * (#CompletionResolveRequest) + */ + data?: any +} + +/** + * The CompletionItem namespace provides functions to deal with + * completion items. + */ +export namespace CompletionItem { + /** + * Create a completion item and seed it with a label. + * @param label The completion item's label + */ + export function create(label: string): CompletionItem { + return { label }; + } +} + +/** + * Represents a collection of [completion items](#CompletionItem) to be presented + * in the editor. + */ +export interface CompletionList { + /** + * This list it not complete. Further typing should result in recomputing + * this list. + */ + isIncomplete: boolean; + + /** + * The completion items. + */ + items: CompletionItem[]; +} + +/** + * The CompletionList namespace provides functions to deal with + * completion lists. + */ +export namespace CompletionList { + /** + * Creates a new completion list. + * + * @param items The completion items. + * @param isIncomplete The list is not complete. + */ + export function create(items?: CompletionItem[], isIncomplete?: boolean): CompletionList { + return { items: items ? items : [], isIncomplete: !!isIncomplete }; + } +} + +/** + * MarkedString can be used to render human readable text. It is either a markdown string + * or a code-block that provides a language and a code snippet. Note that + * markdown strings will be sanitized - that means html will be escaped. + */ +export type MarkedString = string | { language: string; value: string }; + +export namespace MarkedString { + /** + * Creates a marked string from plain text. + * + * @param plainText The plain text. + */ + export function fromPlainText(plainText: string): MarkedString { + return plainText.replace(/[\\`*_{}[\]()#+\-.!]/g, "\\$&"); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash + } +} + +/** + * The result of a hove request. + */ +export interface Hover { + /** + * The hover's content + */ + contents: MarkedString | MarkedString[]; + + /** + * An optional range + */ + range?: Range; +} + +/** + * Represents a parameter of a callable-signature. A parameter can + * have a label and a doc-comment. + */ +export interface ParameterInformation { + /** + * The label of this signature. Will be shown in + * the UI. + */ + label: string; + + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + documentation?: string; +} + +/** + * The ParameterInformation namespace provides helper functions to work with + * [ParameterInformation](#ParameterInformation) literals. + */ +export namespace ParameterInformation { + /** + * Creates a new parameter information literal. + * + * @param label A label string. + * @param documentation A doc string. + */ + export function create(label: string, documentation?: string): ParameterInformation { + return documentation ? { label, documentation } : { label }; + }; +} + +/** + * Represents the signature of something callable. A signature + * can have a label, like a function-name, a doc-comment, and + * a set of parameters. + */ +export interface SignatureInformation { + /** + * The label of this signature. Will be shown in + * the UI. + */ + label: string; + + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + documentation?: string; + + /** + * The parameters of this signature. + */ + parameters?: ParameterInformation[]; +} + +/** + * The SignatureInformation namespace provides helper functions to work with + * [SignatureInformation](#SignatureInformation) literals. + */ +export namespace SignatureInformation { + export function create(label: string, documentation?: string, ...parameters: ParameterInformation[]): SignatureInformation { + let result: SignatureInformation = { label }; + if (Is.defined(documentation)) { + result.documentation = documentation; + } + if (Is.defined(parameters)) { + result.parameters = parameters; + } else { + result.parameters = []; + } + return result; + } +} + +/** + * Signature help represents the signature of something + * callable. There can be multiple signature but only one + * active and only one active parameter. + */ +export interface SignatureHelp { + /** + * One or more signatures. + */ + signatures: SignatureInformation[]; + + /** + * The active signature. + */ + activeSignature?: number; + + /** + * The active parameter of the active signature. + */ + activeParameter?: number; +} + +/** + * The definition of a symbol represented as one or many [locations](#Location). + * For most programming languages there is only one location at which a symbol is + * defined. + */ +export type Definition = Location | Location[]; + +/** + * Value-object that contains additional information when + * requesting references. + */ +export interface ReferenceContext { + /** + * Include the declaration of the current symbol. + */ + includeDeclaration: boolean; +} + + +/** + * A document highlight kind. + */ +export const enum DocumentHighlightKind { + /** + * A textual occurrance. + */ + Text = 1, + + /** + * Read-access of a symbol, like reading a variable. + */ + Read = 2, + + /** + * Write-access of a symbol, like writing to a variable. + */ + Write = 3 +} + +/** + * A document highlight is a range inside a text document which deserves + * special attention. Usually a document highlight is visualized by changing + * the background color of its range. + */ +export interface DocumentHighlight { + /** + * The range this highlight applies to. + */ + range: Range; + + /** + * The highlight kind, default is [text](#DocumentHighlightKind.Text). + */ + kind?: number; +} + +/** + * DocumentHighlight namespace to provide helper functions to work with + * [DocumentHighlight](#DocumentHighlight) literals. + */ +export namespace DocumentHighlight { + /** + * Create a DocumentHighlight object. + * @param range The range the highlight applies to. + */ + export function create(range: Range, kind?: number): DocumentHighlight { + let result: DocumentHighlight = { range }; + if (Is.number(kind)) { + result.kind = kind; + } + return result; + } +} + +/** + * A symbol kind. + */ +export const enum SymbolKind { + File = 1, + Module = 2, + Namespace = 3, + Package = 4, + Class = 5, + Method = 6, + Property = 7, + Field = 8, + Constructor = 9, + Enum = 10, + Interface = 11, + Function = 12, + Variable = 13, + Constant = 14, + String = 15, + Number = 16, + Boolean = 17, + Array = 18, +} + +/** + * Represents information about programming constructs like variables, classes, + * interfaces etc. + */ +export interface SymbolInformation { + /** + * The name of this symbol. + */ + name: string; + + /** + * The kind of this symbol. + */ + kind: number; + + /** + * The location of this symbol. + */ + location: Location; + + /** + * The name of the symbol containing this symbol. + */ + containerName?: string; +} + +export namespace SymbolInformation { + /** + * Creates a new symbol information literal. + * + * @param name The name of the symbol. + * @param kind The kind of the symbol. + * @param range The range of the location of the symbol. + * @param uri The resource of the location of symbol, defaults to the current document. + * @param containerName The name of the symbol containg the symbol. + */ + export function create(name: string, kind: SymbolKind, range: Range, uri?: string, containerName?: string): SymbolInformation { + let result: SymbolInformation = { + name, + kind, + location: { uri, range } + } + if (containerName) { + result.containerName = containerName; + } + return result; + } +} + +/** + * Parameters for a [DocumentSymbolRequest](#DocumentSymbolRequest). + */ +export interface DocumentSymbolParams { + /** + * The text document. + */ + textDocument: TextDocumentIdentifier; +} + +/** + * The parameters of a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). + */ +export interface WorkspaceSymbolParams { + /** + * A non-empty query string + */ + query: string; +} + +/** + * Contains additional diagnostic information about the context in which + * a [code action](#CodeActionProvider.provideCodeActions) is run. + */ +export interface CodeActionContext { + /** + * An array of diagnostics. + */ + diagnostics: Diagnostic[]; +} + +/** + * The CodeActionContext namespace provides helper functions to work with + * [CodeActionContext](#CodeActionContext) literals. + */ +export namespace CodeActionContext { + /** + * Creates a new CodeActionContext literal. + */ + export function create(diagnostics: Diagnostic[]): CodeActionContext { + return { diagnostics }; + } + /** + * Checks whether the given literal conforms to the [CodeActionContext](#CodeActionContext) interface. + */ + export function is(value: any): value is CodeActionContext { + let candidate = value as CodeActionContext; + return Is.defined(candidate) && Is.typedArray(candidate.diagnostics, Diagnostic.is); + } +} + +/** + * A code lens represents a [command](#Command) that should be shown along with + * source text, like the number of references, a way to run tests, etc. + * + * A code lens is _unresolved_ when no command is associated to it. For performance + * reasons the creation of a code lens and resolving should be done to two stages. + */ +export interface CodeLens { + /** + * The range in which this code lens is valid. Should only span a single line. + */ + range: Range; + + /** + * The command this code lens represents. + */ + command?: Command; + + /** + * An data entry field that is preserved on a code lens item between + * a [CodeLensRequest](#CodeLensRequest) and a [CodeLensResolveRequest] + * (#CodeLensResolveRequest) + */ + data?: any +} + +/** + * The CodeLens namespace provides helper functions to work with + * [CodeLens](#CodeLens) literals. + */ +export namespace CodeLens { + /** + * Creates a new CodeLens literal. + */ + export function create(range: Range, data?: any): CodeLens { + let result: CodeLens = { range }; + if (Is.defined(data)) result.data = data; + return result; + } + /** + * Checks whether the given literal conforms to the [CodeLens](#CodeLens) interface. + */ + export function is(value: any): value is CodeLens { + let candidate = value as CodeLens; + return Is.defined(candidate) && Range.is(candidate.range) && (Is.undefined(candidate.command) || Command.is(candidate.command)); + } +} + +/** + * Value-object describing what options formatting should use. + */ +export interface FormattingOptions { + /** + * Size of a tab in spaces. + */ + tabSize: number; + + /** + * Prefer spaces over tabs. + */ + insertSpaces: boolean; + + /** + * Signature for further properties. + */ + [key: string]: boolean | number | string; +} + +/** + * The FormattingOptions namespace provides helper functions to work with + * [FormattingOptions](#FormattingOptions) literals. + */ +export namespace FormattingOptions { + /** + * Creates a new FormattingOptions literal. + */ + export function create(tabSize: number, insertSpaces: boolean): FormattingOptions { + return { tabSize, insertSpaces }; + } + /** + * Checks whether the given literal conforms to the [FormattingOptions](#FormattingOptions) interface. + */ + export function is(value: any): value is FormattingOptions { + let candidate = value as FormattingOptions; + return Is.defined(candidate) && Is.number(candidate.tabSize) && Is.boolean(candidate.insertSpaces); + } +} + +/** + * A document link is a range in a text document that links to an internal or external resource, like another + * text document or a web site. + */ +export class DocumentLink { + + /** + * The range this link applies to. + */ + range: Range; + + /** + * The uri this link points to. + */ + target: string; +} + +/** + * The DocumentLink namespace provides helper functions to work with + * [DocumentLink](#DocumentLink) literals. + */ +export namespace DocumentLink { + /** + * Creates a new DocumentLink literal. + */ + export function create(range: Range, target?: string): DocumentLink { + return { range, target }; + } + + /** + * Checks whether the given literal conforms to the [DocumentLink](#DocumentLink) interface. + */ + export function is(value: any): value is DocumentLink { + let candidate = value as DocumentLink; + return Is.defined(candidate) && Range.is(candidate.range) && (Is.undefined(candidate.target) || Is.string(candidate.target)); + } +} + +/** + * A simple text document. Not to be implemenented. + */ +export interface TextDocument { + + /** + * The associated URI for this document. Most documents have the __file__-scheme, indicating that they + * represent files on disk. However, some documents may have other schemes indicating that they are not + * available on disk. + * + * @readonly + */ + uri: string; + + /** + * The identifier of the language associated with this document. + * + * @readonly + */ + languageId: string; + + /** + * The version number of this document (it will strictly increase after each + * change, including undo/redo). + * + * @readonly + */ + version: number; + + /** + * Get the text of this document. + * + * @return The text of this document. + */ + getText(): string; + + /** + * Converts a zero-based offset to a position. + * + * @param offset A zero-based offset. + * @return A valid [position](#Position). + */ + positionAt(offset: number): Position; + + /** + * Converts the position to a zero-based offset. + * + * The position will be [adjusted](#TextDocument.validatePosition). + * + * @param position A position. + * @return A valid zero-based offset. + */ + offsetAt(position: Position): number; + + /** + * The number of lines in this document. + * + * @readonly + */ + lineCount: number; +} + +export namespace TextDocument { + /** + * Creates a new ITextDocument literal from the given uri and content. + * @param uri The document's uri. + * @param languageId The document's language Id. + * @param content The document's content. + */ + export function create(uri: string, languageId: string, version: number, content: string): TextDocument { + return new FullTextDocument(uri, languageId, version, content); + } + /** + * Checks whether the given literal conforms to the [ITextDocument](#ITextDocument) interface. + */ + export function is(value: any): value is TextDocument { + let candidate = value as TextDocument; + return Is.defined(candidate) && Is.string(candidate.uri) && (Is.undefined(candidate.languageId) || Is.string(candidate.languageId)) && Is.number(candidate.lineCount) + && Is.func(candidate.getText) && Is.func(candidate.positionAt) && Is.func(candidate.offsetAt) ? true : false; + } +} + +/** + * Event to signal changes to a simple text document. + */ +export interface TextDocumentChangeEvent { + /** + * The document that has changed. + */ + document: TextDocument; +} + +/** + * An event describing a change to a text document. If range and rangeLength are omitted + * the new text is considered to be the full content of the document. + */ +export interface TextDocumentContentChangeEvent { + /** + * The range of the document that changed. + */ + range?: Range; + + /** + * The length of the range that got replaced. + */ + rangeLength?: number; + + /** + * The new text of the document. + */ + text: string; +} + +class FullTextDocument implements TextDocument { + + private _uri: string; + private _languageId: string; + private _version: number; + private _content: string; + private _lineOffsets: number[]; + + public constructor(uri: string, languageId: string, version: number, content: string) { + this._uri = uri; + this._languageId = languageId; + this._version = version; + this._content = content; + this._lineOffsets = null; + } + + public get uri(): string { + return this._uri; + } + + public get languageId(): string { + return this._languageId; + } + + public get version(): number { + return this._version; + } + + public getText(): string { + return this._content; + } + + public update(event: TextDocumentContentChangeEvent, version: number): void { + this._content = event.text; + this._version = version; + this._lineOffsets = null; + } + + private getLineOffsets(): number[] { + if (this._lineOffsets === null) { + let lineOffsets: number[] = []; + let text = this._content; + let isLineStart = true; + for (let i = 0; i < text.length; i++) { + if (isLineStart) { + lineOffsets.push(i); + isLineStart = false; + } + let ch = text.charAt(i); + isLineStart = (ch === '\r' || ch === '\n'); + if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') { + i++; + } + } + if (isLineStart && text.length > 0) { + lineOffsets.push(text.length); + } + this._lineOffsets = lineOffsets; + } + return this._lineOffsets; + } + + public positionAt(offset: number) { + offset = Math.max(Math.min(offset, this._content.length), 0); + + let lineOffsets = this.getLineOffsets(); + let low = 0, high = lineOffsets.length; + if (high === 0) { + return Position.create(0, offset); + } + while (low < high) { + let mid = Math.floor((low + high) / 2); + if (lineOffsets[mid] > offset) { + high = mid; + } else { + low = mid + 1; + } + } + // low is the least x for which the line offset is larger than the current offset + // or array.length if no line offset is larger than the current offset + let line = low - 1; + return Position.create(line, offset - lineOffsets[line]); + } + + public offsetAt(position: Position) { + let lineOffsets = this.getLineOffsets(); + if (position.line >= lineOffsets.length) { + return this._content.length; + } else if (position.line < 0) { + return 0; + } + let lineOffset = lineOffsets[position.line]; + let nextLineOffset = (position.line + 1 < lineOffsets.length) ? lineOffsets[position.line + 1] : this._content.length; + return Math.max(Math.min(lineOffset + position.character, nextLineOffset), lineOffset); + } + + public get lineCount() { + return this.getLineOffsets().length; + } +} + +namespace Is { + + const toString = Object.prototype.toString; + + export function defined(value: any): boolean { + return typeof value !== 'undefined'; + } + + export function undefined(value: any): boolean { + return typeof value === 'undefined'; + } + + export function boolean(value: any): value is boolean { + return value === true || value === false; + } + + export function string(value: any): value is string { + return toString.call(value) === '[object String]'; + } + + export function number(value: any): value is number { + return toString.call(value) === '[object Number]'; + } + + export function func(value: any): value is Function { + return toString.call(value) === '[object Function]'; + } + + export function typedArray(value: any, check: (value: any) => boolean): value is T[] { + return Array.isArray(value) && (value).every(check); + } + +} diff --git a/dataprotocol-node/types/src/tsconfig.json b/dataprotocol-node/types/src/tsconfig.json new file mode 100644 index 0000000000..c8c604f238 --- /dev/null +++ b/dataprotocol-node/types/src/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "ES5", + "module": "umd", + "sourceMap": false, + "declaration": true, + "stripInternal": true, + "outDir": "../lib" + } +} \ No newline at end of file diff --git a/dataprotocol-node/types/src/typings/promise.d.ts b/dataprotocol-node/types/src/typings/promise.d.ts new file mode 100644 index 0000000000..925c943df5 --- /dev/null +++ b/dataprotocol-node/types/src/typings/promise.d.ts @@ -0,0 +1,112 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + +/** + * The Thenable (E.g. PromiseLike) and Promise declarions are taken from TypeScript's + * lib.core.es6.d.ts file. See above Copyright notice. + */ + +/** + * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, + * and others. This API makes no assumption about what promise libary is being used which + * enables reusing existing code without migrating to a specific promise implementation. Still, + * we recommand the use of native promises which are available in VS Code. + */ +interface Thenable { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: R) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; + then(onfulfilled?: (value: R) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; +} + +/** + * Represents the completion of an asynchronous operation + */ +interface Promise extends Thenable { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Promise; + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Promise; + + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: (reason: any) => T | Thenable): Promise; +} + +interface PromiseConstructor { + /** + * Creates a new Promise. + * @param executor A callback used to initialize the promise. This callback is passed two arguments: + * a resolve callback used resolve the promise with a value or the result of another promise, + * and a reject callback used to reject the promise with a provided reason or error. + */ + new (executor: (resolve: (value?: T | Thenable) => void, reject: (reason?: any) => void) => void): Promise; + + /** + * Creates a Promise that is resolved with an array of results when all of the provided Promises + * resolve, or rejected when any Promise is rejected. + * @param values An array of Promises. + * @returns A new Promise. + */ + all(values: Array>): Promise; + + /** + * Creates a Promise that is resolved or rejected when any of the provided Promises are resolved + * or rejected. + * @param values An array of Promises. + * @returns A new Promise. + */ + race(values: Array>): Promise; + + /** + * Creates a new rejected promise for the provided reason. + * @param reason The reason the promise was rejected. + * @returns A new rejected Promise. + */ + reject(reason: any): Promise; + + /** + * Creates a new rejected promise for the provided reason. + * @param reason The reason the promise was rejected. + * @returns A new rejected Promise. + */ + reject(reason: any): Promise; + + /** + * Creates a new resolved promise for the provided value. + * @param value A promise. + * @returns A promise whose internal state matches the provided promise. + */ + resolve(value: T | Thenable): Promise; + + /** + * Creates a new resolved promise . + * @returns A resolved promise. + */ + resolve(): Promise; +} + +declare var Promise: PromiseConstructor; diff --git a/extensions-modules/.gitignore b/extensions-modules/.gitignore new file mode 100644 index 0000000000..a7f9bb9024 --- /dev/null +++ b/extensions-modules/.gitignore @@ -0,0 +1,3 @@ +lib/ +node_modules +*.log \ No newline at end of file diff --git a/extensions-modules/.npmignore b/extensions-modules/.npmignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extensions-modules/package.json b/extensions-modules/package.json new file mode 100644 index 0000000000..e77b8cad8b --- /dev/null +++ b/extensions-modules/package.json @@ -0,0 +1,27 @@ +{ + "name": "extensions-modules", + "version": "0.1.0", + "description": "Shared modules for Carbon extensions", + "dependencies": { + "dataprotocol-client": "file:../dataprotocol-node/client", + "decompress": "^4.2.0", + "fs-extra-promise": "^1.0.1", + "http-proxy-agent": "^2.0.0", + "https-proxy-agent": "^2.1.0", + "opener": "^1.4.3", + "tmp": "0.0.33", + "vscode-extension-telemetry": "0.0.8" + }, + "devDependencies": { + "@types/node": "^6.0.61", + "vscode": "1.0.1" + }, + "scripts": { + "prepublish": "tsc -p ./src", + "compile": "tsc -p ./src", + "watch": "tsc -w -p ./src", + "update-vscode": "node ./node_modules/vscode/bin/install" + }, + "main": "./lib/main.js", + "typings": "./lib/main" +} diff --git a/extensions-modules/src/configurations/config.ts b/extensions-modules/src/configurations/config.ts new file mode 100644 index 0000000000..12fb81d45a --- /dev/null +++ b/extensions-modules/src/configurations/config.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +const fs = require('fs'); +import * as path from 'path'; +import {IConfig} from '../languageservice/interfaces'; +import * as SharedConstants from '../models/constants'; + +/* +* Config class handles getting values from config.json. +*/ +export default class Config implements IConfig { + private _configJsonContent: any = undefined; + + private _extensionConfigSectionName: string = undefined; + private _fromBuild: boolean = undefined; + + constructor(extensionConfigSectionName: string, fromBuild?: boolean) { + this._extensionConfigSectionName = extensionConfigSectionName; + this._fromBuild = fromBuild; + } + + public get configJsonContent(): any { + if (this._configJsonContent === undefined) { + this._configJsonContent = this.loadConfig(); + } + return this._configJsonContent; + } + + public getDownloadUrl(): string { + return this.getConfigValue(SharedConstants.downloadUrlConfigKey); + } + + public getInstallDirectory(): string { + return this.getConfigValue(SharedConstants.installDirConfigKey); + } + + public getExecutableFiles(): string[] { + return this.getConfigValue(SharedConstants.executableFilesConfigKey); + } + + public getPackageVersion(): string { + return this.getConfigValue(SharedConstants.versionConfigKey); + } + + public getConfigValue(configKey: string): any { + let json = this.configJsonContent; + let toolsConfig = json[SharedConstants.serviceConfigKey]; + let configValue: string = undefined; + if (toolsConfig !== undefined) { + configValue = toolsConfig[configKey]; + } + return configValue; + } + + public getExtensionConfig(key: string, defaultValue?: any): any { + let json = this.configJsonContent; + let extensionConfig = json[this._extensionConfigSectionName]; + let configValue = extensionConfig[key]; + if (!configValue) { + configValue = defaultValue; + } + return configValue; + } + + public getWorkspaceConfig(key: string, defaultValue?: any): any { + let json = this.configJsonContent; + let configValue = json[key]; + if (!configValue) { + configValue = defaultValue; + } + return configValue; + } + + public loadConfig(): any { + let configContent = undefined; + if (this._fromBuild) { + let remainingPath = '../../../../../extensions/' + this._extensionConfigSectionName + '/client/out/config.json'; + configContent = fs.readFileSync(path.join(__dirname, remainingPath)); + } + else { + configContent = fs.readFileSync(path.join(__dirname, '../../../../client/out/config.json')); + } + return JSON.parse(configContent); + } +} diff --git a/extensions-modules/src/configurations/extConfig.ts b/extensions-modules/src/configurations/extConfig.ts new file mode 100644 index 0000000000..91dee29b93 --- /dev/null +++ b/extensions-modules/src/configurations/extConfig.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import Config from './config'; +import { workspace, WorkspaceConfiguration } from 'vscode'; +import {IConfig} from '../languageservice/interfaces'; +import * as Constants from '../models/constants'; + +/* +* ExtConfig class handles getting values from workspace config or config.json. +*/ +export default class ExtConfig implements IConfig { + + constructor(private _extensionConfigSectionName: string, private _config?: IConfig, + private _extensionConfig?: WorkspaceConfiguration, + private _workspaceConfig?: WorkspaceConfiguration) { + if (this._config === undefined) { + this._config = new Config(_extensionConfigSectionName); + } + if (this._extensionConfig === undefined) { + this._extensionConfig = workspace.getConfiguration(_extensionConfigSectionName); + } + if (this._workspaceConfig === undefined) { + this._workspaceConfig = workspace.getConfiguration(); + } + } + + public getDownloadUrl(): string { + return this.getConfigValue(Constants.downloadUrlConfigKey); + } + + public getInstallDirectory(): string { + return this.getConfigValue(Constants.installDirConfigKey); + } + + public getExecutableFiles(): string[] { + return this.getConfigValue(Constants.executableFilesConfigKey); + } + + public getPackageVersion(): string { + return this.getConfigValue(Constants.versionConfigKey); + } + + public getConfigValue(configKey: string): any { + let configValue: string = this.getExtensionConfig(`${Constants.serviceConfigKey}.${configKey}`); + if (!configValue) { + configValue = this._config.getConfigValue(configKey); + } + return configValue; + } + + public getExtensionConfig(key: string, defaultValue?: any): any { + let configValue = this._extensionConfig.get(key); + if (configValue === undefined) { + configValue = defaultValue; + } + return configValue; + } + + public getWorkspaceConfig(key: string, defaultValue?: any): any { + let configValue = this._workspaceConfig.get(key); + if (configValue === undefined) { + configValue = defaultValue; + } + return configValue; + } + + public updateWorkspaceConfig(configKey: string, configValue: any) { + this._workspaceConfig.update(configKey, configValue, true); + } +} diff --git a/extensions-modules/src/controllers/vscodeWrapper.ts b/extensions-modules/src/controllers/vscodeWrapper.ts new file mode 100644 index 0000000000..7064564ec2 --- /dev/null +++ b/extensions-modules/src/controllers/vscodeWrapper.ts @@ -0,0 +1,280 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import vscode = require('vscode'); +import {IExtensionConstants} from '../models/contracts/contracts'; + +export import TextEditor = vscode.TextEditor; + +export default class VscodeWrapper { + private _extensionConstants: IExtensionConstants; + /** + * Output channel for logging. Shared among all instances. + */ + private static _outputChannel: vscode.OutputChannel; + + /** + * Default constructor. + */ + public constructor(constants: IExtensionConstants) { + this._extensionConstants = constants; + if (typeof VscodeWrapper._outputChannel === 'undefined') { + VscodeWrapper._outputChannel = this.createOutputChannel(this._extensionConstants.outputChannelName); + } + } + + /** + * Get the current active text editor + */ + public get activeTextEditor(): vscode.TextEditor { + return vscode.window.activeTextEditor; + } + + /** + * get the current textDocument; any that are open? + */ + public get textDocuments(): vscode.TextDocument[] { + return vscode.workspace.textDocuments; + } + + /** + * Parse uri + */ + public parseUri(uri: string): vscode.Uri { + return vscode.Uri.parse(uri); + } + + /** + * Get the URI string for the current active text editor + */ + public get activeTextEditorUri(): string { + if (typeof vscode.window.activeTextEditor !== 'undefined' && + typeof vscode.window.activeTextEditor.document !== 'undefined') { + return vscode.window.activeTextEditor.document.uri.toString(); + } + return undefined; + } + + public get constants(): IExtensionConstants { + return this._extensionConstants; + } + + /** + * Create an output channel in vscode. + */ + public createOutputChannel(channelName: string): vscode.OutputChannel { + return vscode.window.createOutputChannel(channelName); + } + + /** + * Executes the command denoted by the given command identifier. + * + * When executing an editor command not all types are allowed to + * be passed as arguments. Allowed are the primitive types `string`, `boolean`, + * `number`, `undefined`, and `null`, as well as classes defined in this API. + * There are no restrictions when executing commands that have been contributed + * by extensions. + * + * @param command Identifier of the command to execute. + * @param rest Parameters passed to the command function. + * @return A thenable that resolves to the returned value of the given command. `undefined` when + * the command handler function doesn't return anything. + * @see vscode.commands.executeCommand + */ + public executeCommand(command: string, ...rest: any[]): Thenable { + return vscode.commands.executeCommand(command, ...rest); + } + + /** + * Get the configuration for a extensionName; NOT YET IMPLEMENTED + * @param extensionName The string name of the extension to get the configuration for + */ + public getConfiguration(extensionName: string): vscode.WorkspaceConfiguration { + return vscode.workspace.getConfiguration(extensionName); + } + + /** + * @return 'true' if the active editor window has a .sql file, false otherwise + */ + public get isEditingSqlFile(): boolean { + let sqlFile = false; + let editor = this.activeTextEditor; + if (editor) { + if (editor.document.languageId === this._extensionConstants.languageId) { + sqlFile = true; + } + } + return sqlFile; + } + + /** + * An event that is emitted when a [text document](#TextDocument) is disposed. + */ + public get onDidCloseTextDocument(): vscode.Event { + return vscode.workspace.onDidCloseTextDocument; + } + + /** + * An event that is emitted when a [text document](#TextDocument) is opened. + */ + public get onDidOpenTextDocument(): vscode.Event { + return vscode.workspace.onDidOpenTextDocument; + } + + /** + * An event that is emitted when a [text document](#TextDocument) is saved to disk. + */ + public get onDidSaveTextDocument(): vscode.Event { + return vscode.workspace.onDidSaveTextDocument; + } + + /** + * Opens the denoted document from disk. Will return early if the + * document is already open, otherwise the document is loaded and the + * [open document](#workspace.onDidOpenTextDocument)-event fires. + * The document to open is denoted by the [uri](#Uri). Two schemes are supported: + * + * file: A file on disk, will be rejected if the file does not exist or cannot be loaded, e.g. `file:///Users/frodo/r.ini`. + * untitled: A new file that should be saved on disk, e.g. `untitled:c:\frodo\new.js`. The language will be derived from the file name. + * + * Uris with other schemes will make this method return a rejected promise. + * + * @param uri Identifies the resource to open. + * @return A promise that resolves to a [document](#TextDocument). + * @see vscode.workspace.openTextDocument + */ + public openTextDocument(uri: vscode.Uri): Thenable { + return vscode.workspace.openTextDocument(uri); + } + + /** + * Helper to log messages to output channel. + */ + public logToOutputChannel(msg: any): void { + let date: Date = new Date(); + if (msg instanceof Array) { + msg.forEach(element => { + VscodeWrapper._outputChannel.appendLine('[' + date.toLocaleTimeString() + '] ' + element.toString()); + }); + } else { + VscodeWrapper._outputChannel.appendLine('[' + date.toLocaleTimeString() + '] ' + msg.toString()); + } + } + + /** + * Create a vscode.Range object + * @param start The start position for the range + * @param end The end position for the range + */ + public range(start: vscode.Position, end: vscode.Position): vscode.Range { + return new vscode.Range(start, end); + } + + /** + * Create a vscode.Position object + * @param line The line for the position + * @param column The column for the position + */ + public position(line: number, column: number): vscode.Position { + return new vscode.Position(line, column); + } + + /** + * Create a vscode.Selection object + * @param start The start postion of the selection + * @param end The end position of the selection + */ + public selection(start: vscode.Position, end: vscode.Position): vscode.Selection { + return new vscode.Selection(start, end); + } + + /** + * Formats and shows a vscode error message + */ + public showErrorMessage(msg: string, ...items: string[]): Thenable { + return vscode.window.showErrorMessage(this._extensionConstants.extensionName + ': ' + msg, ...items); + } + + /** + * Formats and shows a vscode information message + */ + public showInformationMessage(msg: string, ...items: string[]): Thenable { + return vscode.window.showInformationMessage(this._extensionConstants.extensionName + ': ' + msg, ...items); + } + + /** + * Shows a selection list. + * + * @param items An array of items, or a promise that resolves to an array of items. + * @param options Configures the behavior of the selection list. + * @return A promise that resolves to the selected item or undefined. + */ + public showQuickPick(items: T[] | Thenable, options?: vscode.QuickPickOptions): Thenable { + return vscode.window.showQuickPick(items, options); + } + + /** + * Show the given document in a text editor. A [column](#ViewColumn) can be provided + * to control where the editor is being shown. Might change the [active editor](#window.activeTextEditor). + * + * @param document A text document to be shown. + * @param column A view column in which the editor should be shown. The default is the [one](#ViewColumn.One), other values + * are adjusted to be __Min(column, columnCount + 1)__. + * @param preserveFocus When `true` the editor will not take focus. + * @return A promise that resolves to an [editor](#TextEditor). + */ + public showTextDocument(document: vscode.TextDocument, column?: vscode.ViewColumn, preserveFocus?: boolean): Thenable { + return vscode.window.showTextDocument(document, column, preserveFocus); + } + + /** + * Formats and shows a vscode warning message + */ + public showWarningMessage(msg: string): Thenable { + return vscode.window.showWarningMessage(this._extensionConstants.extensionName + ': ' + msg ); + } + + /** + * Returns a array of the text editors currently visible in the window + */ + public get visibleEditors(): vscode.TextEditor[] { + return vscode.window.visibleTextEditors; + } + + /** + * Create an URI from a file system path. The [scheme](#Uri.scheme) + * will be `file`. + * + * @param path A file system or UNC path. + * @return A new Uri instance. + * @see vscode.Uri.file + */ + public uriFile(path: string): vscode.Uri { + return vscode.Uri.file(path); + } + + /** + * Create an URI from a string. Will throw if the given value is not + * valid. + * + * @param value The string value of an Uri. + * @return A new Uri instance. + * @see vscode.Uri.parse + */ + public uriParse(value: string): vscode.Uri { + return vscode.Uri.parse(value); + } + + /** + * The folder that is open in VS Code. `undefined` when no folder + * has been opened. + * + * @readonly + * @see vscode.workspace.rootPath + */ + public get workspaceRootPath(): string { + return vscode.workspace.rootPath; + } +} diff --git a/extensions-modules/src/languageservice/decompressProvider.ts b/extensions-modules/src/languageservice/decompressProvider.ts new file mode 100644 index 0000000000..152fa2b0ca --- /dev/null +++ b/extensions-modules/src/languageservice/decompressProvider.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import {IDecompressProvider, IPackage} from './interfaces'; +import {ILogger} from '../models/interfaces'; +const decompress = require('decompress'); + +export default class DecompressProvider implements IDecompressProvider { + public decompress(pkg: IPackage, logger: ILogger): Promise { + return new Promise((resolve, reject) => { + decompress(pkg.tmpFile.name, pkg.installPath).then(files => { + logger.appendLine(`Done! ${files.length} files unpacked.\n`); + resolve(); + }).catch(decompressErr => { + logger.appendLine(`[ERROR] ${decompressErr}`); + reject(decompressErr); + }); + }); + } +} diff --git a/extensions-modules/src/languageservice/httpClient.ts b/extensions-modules/src/languageservice/httpClient.ts new file mode 100644 index 0000000000..98e75f8726 --- /dev/null +++ b/extensions-modules/src/languageservice/httpClient.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import {IPackage, IStatusView, PackageError, IHttpClient} from './interfaces'; +import {ILogger} from '../models/interfaces'; +import {parse as parseUrl, Url} from 'url'; +import * as https from 'https'; +import * as http from 'http'; +import {getProxyAgent} from './proxy'; + +let fs = require('fs'); + +/* + * Http client class to handle downloading files using http or https urls + */ +export default class HttpClient implements IHttpClient { + + /* + * Downloads a file and stores the result in the temp file inside the package object + */ + public downloadFile(urlString: string, pkg: IPackage, logger: ILogger, statusView: IStatusView, proxy?: string, strictSSL?: boolean): Promise { + const url = parseUrl(urlString); + let options = this.getHttpClientOptions(url, proxy, strictSSL); + let clientRequest = url.protocol === 'http:' ? http.request : https.request; + + return new Promise((resolve, reject) => { + if (!pkg.tmpFile || pkg.tmpFile.fd === 0) { + return reject(new PackageError('Temporary package file unavailable', pkg)); + } + + let request = clientRequest(options, response => { + if (response.statusCode === 301 || response.statusCode === 302) { + // Redirect - download from new location + return resolve(this.downloadFile(response.headers.location, pkg, logger, statusView, proxy, strictSSL)); + } + + if (response.statusCode !== 200) { + // Download failed - print error message + logger.appendLine(`failed (error code '${response.statusCode}')`); + return reject(new PackageError(response.statusCode.toString(), pkg)); + } + + // If status code is 200 + this.handleSuccessfulResponse(pkg, response, logger, statusView).then(_ => { + resolve(); + }).catch(err => { + reject(err); + }); + }); + + request.on('error', error => { + // reject(new PackageError(`Request error: ${error.code || 'NONE'}`, pkg, error)); + reject(new PackageError(`Request error: ${error.name || 'NONE'}`, pkg, error)); + }); + + // Execute the request + request.end(); + }); + } + + private getHttpClientOptions(url: Url, proxy?: string, strictSSL?: boolean): any { + const agent = getProxyAgent(url, proxy, strictSSL); + + let options: http.RequestOptions = { + host: url.hostname, + path: url.path, + agent: agent, + port: +url.port + }; + + if (url.protocol === 'https:') { + let httpsOptions: https.RequestOptions = { + host: url.hostname, + path: url.path, + agent: agent, + port: +url.port + }; + options = httpsOptions; + } + + return options; + } + + /* + * Calculate the download percentage and stores in the progress object + */ + public handleDataReceivedEvent(progress: IDownloadProgress, data: any, logger: ILogger, statusView: IStatusView): void { + progress.downloadedBytes += data.length; + + // Update status bar item with percentage + if (progress.packageSize > 0) { + let newPercentage = Math.ceil(100 * (progress.downloadedBytes / progress.packageSize)); + if (newPercentage !== progress.downloadPercentage) { + statusView.updateServiceDownloadingProgress(progress.downloadPercentage); + progress.downloadPercentage = newPercentage; + } + + // Update dots after package name in output console + let newDots = Math.ceil(progress.downloadPercentage / 5); + if (newDots > progress.dots) { + logger.append('.'.repeat(newDots - progress.dots)); + progress.dots = newDots; + } + } + return; + } + + private handleSuccessfulResponse(pkg: IPackage, response: http.IncomingMessage, logger: ILogger, statusView: IStatusView): Promise { + return new Promise((resolve, reject) => { + let progress: IDownloadProgress = { + packageSize: parseInt(response.headers['content-length'], 10), + dots: 0, + downloadedBytes: 0, + downloadPercentage: 0 + }; + logger.append(`(${Math.ceil(progress.packageSize / 1024)} KB) `); + response.on('data', data => { + this.handleDataReceivedEvent(progress, data, logger, statusView); + }); + let tmpFile = fs.createWriteStream(undefined, { fd: pkg.tmpFile.fd }); + response.on('end', () => { + resolve(); + }); + + response.on('error', err => { + reject(new PackageError(`Response error: ${err.name || 'NONE'}`, pkg, err)); + }); + + // Begin piping data from the response to the package file + response.pipe(tmpFile, { end: false }); + }); + } +} + +/* + * Interface to store the values needed to calculate download percentage + */ +export interface IDownloadProgress { + packageSize: number; + downloadedBytes: number; + downloadPercentage: number; + dots: number; +} diff --git a/extensions-modules/src/languageservice/interfaces.ts b/extensions-modules/src/languageservice/interfaces.ts new file mode 100644 index 0000000000..48ccceec6e --- /dev/null +++ b/extensions-modules/src/languageservice/interfaces.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as tmp from 'tmp'; +import {ILogger} from '../models/interfaces'; + +export interface IStatusView { + installingService(): void; + serviceInstalled(): void; + serviceInstallationFailed(): void; + updateServiceDownloadingProgress(downloadPercentage: number): void; +} + +export interface IConfig { + getDownloadUrl(): string; + getInstallDirectory(): string; + getExecutableFiles(): string[]; + getPackageVersion(): string; + getExtensionConfig(key: string, defaultValue?: any): any; + getWorkspaceConfig(key: string, defaultValue?: any): any; + getConfigValue(configKey: string): any; +} + +export interface IPackage { + url: string; + installPath?: string; + tmpFile: tmp.SynchronousResult; +} + +export class PackageError extends Error { + // Do not put PII (personally identifiable information) in the 'message' field as it will be logged to telemetry + constructor(public message: string, + public pkg: IPackage = undefined, + public innerError: any = undefined) { + super(message); + } +} + +export interface IHttpClient { + downloadFile(urlString: string, pkg: IPackage, logger: ILogger, statusView: IStatusView, proxy: string, strictSSL: boolean): Promise; +} + +export interface IDecompressProvider { + decompress(pkg: IPackage, logger: ILogger): Promise; +} diff --git a/extensions-modules/src/languageservice/proxy.ts b/extensions-modules/src/languageservice/proxy.ts new file mode 100644 index 0000000000..988dc12f4e --- /dev/null +++ b/extensions-modules/src/languageservice/proxy.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Url, parse as parseUrl } from 'url'; +let HttpProxyAgent = require('http-proxy-agent'); +let HttpsProxyAgent = require('https-proxy-agent'); + +function getSystemProxyURL(requestURL: Url): string { + if (requestURL.protocol === 'http:') { + return process.env.HTTP_PROXY || process.env.http_proxy || undefined; + } else if (requestURL.protocol === 'https:') { + return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || undefined; + } + + return undefined; +} + +/* + * Returns the proxy agent using the proxy url in the parameters or the system proxy. Returns null if no proxy found + */ +export function getProxyAgent(requestURL: Url, proxy?: string, strictSSL?: boolean): any { + const proxyURL = proxy || getSystemProxyURL(requestURL); + + if (!proxyURL) { + return undefined; + } + + const proxyEndpoint = parseUrl(proxyURL); + + if (!/^https?:$/.test(proxyEndpoint.protocol)) { + return undefined; + } + + strictSSL = strictSSL || true; + + const opts = { + host: proxyEndpoint.hostname, + port: Number(proxyEndpoint.port), + auth: proxyEndpoint.auth, + rejectUnauthorized: strictSSL + }; + + return requestURL.protocol === 'http:' ? new HttpProxyAgent(opts) : new HttpsProxyAgent(opts); +} diff --git a/extensions-modules/src/languageservice/server.ts b/extensions-modules/src/languageservice/server.ts new file mode 100644 index 0000000000..0e31295a4b --- /dev/null +++ b/extensions-modules/src/languageservice/server.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as path from 'path'; +import {Runtime} from '../models/platform'; +import ServiceDownloadProvider from './serviceDownloadProvider'; +import {IConfig, IStatusView} from './interfaces'; +let fs = require('fs-extra-promise'); + + +/* +* Service Provider class finds the SQL tools service executable file or downloads it if doesn't exist. +*/ +export default class ServerProvider { + + constructor(private _downloadProvider: ServiceDownloadProvider, + private _config: IConfig, + private _statusView: IStatusView, + private _extensionConfigSectionName: string) { + } + + /** + * Public get method for downloadProvider + */ + public get downloadProvider(): ServiceDownloadProvider { + return this._downloadProvider; + } + + /** + * Given a file path, returns the path to the SQL Tools service file. + */ + public findServerPath(filePath: string, executableFiles: string[] = undefined): Promise { + return fs.lstatAsync(filePath).then(stats => { + // If a file path was passed, assume its the launch file. + if (stats.isFile()) { + return filePath; + } + + // Otherwise, search the specified folder. + let candidate: string; + + if (executableFiles === undefined && this._config !== undefined) { + executableFiles = this._config.getExecutableFiles(); + } + if (executableFiles !== undefined) { + executableFiles.forEach(element => { + let executableFile = path.join(filePath, element); + if (candidate === undefined && fs.existsSync(executableFile)) { + candidate = executableFile; + return candidate; + } + }); + } + + + return candidate; + }); + } + + /** + * Download the service if doesn't exist and returns the file path. + */ + public getOrDownloadServer(runtime: Runtime): Promise { + // Attempt to find launch file path first from options, and then from the default install location. + // If SQL tools service can't be found, download it. + + return new Promise((resolve, reject) => { + return this.getServerPath(runtime).then(result => { + if (result === undefined) { + return this.downloadServerFiles(runtime).then ( downloadResult => { + resolve(downloadResult); + }); + } else { + return resolve(result); + } + }).catch(err => { + return reject(err); + }); + }).catch(err => { + throw err; + }); + } + + /** + * Returns the path of the installed service + */ + public getServerPath(runtime: Runtime): Promise { + const installDirectory = this._downloadProvider.getInstallDirectory(runtime, this._extensionConfigSectionName); + return this.findServerPath(installDirectory); + } + + /** + * Downloads the service and returns the path of the installed service + */ + public downloadServerFiles(runtime: Runtime): Promise { + return new Promise((resolve, reject) => { + const installDirectory = this._downloadProvider.getInstallDirectory(runtime, this._extensionConfigSectionName); + return this._downloadProvider.installService(runtime).then( _ => { + return this.findServerPath(installDirectory).then ( result => { + return resolve(result); + }); + }).catch(err => { + this._statusView.serviceInstallationFailed(); + reject(err); + }); + }); + } +} diff --git a/extensions-modules/src/languageservice/serverStatus.ts b/extensions-modules/src/languageservice/serverStatus.ts new file mode 100644 index 0000000000..adc0f7d6df --- /dev/null +++ b/extensions-modules/src/languageservice/serverStatus.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import {IStatusView} from './interfaces'; +import vscode = require('vscode'); +import {IExtensionConstants} from '../models/contracts/contracts'; +import * as Constants from '../models/constants'; + +/* +* The status class which includes the service initialization result. +*/ +export class ServerInitializationResult { + + public constructor( + public installedBeforeInitializing: Boolean = false, + public isRunning: Boolean = false, + public serverPath: string = undefined + ) { + + } + + public Clone(): ServerInitializationResult { + return new ServerInitializationResult(this.installedBeforeInitializing, this.isRunning, this.serverPath); + } + + public WithRunning(isRunning: Boolean): ServerInitializationResult { + return new ServerInitializationResult(this.installedBeforeInitializing, isRunning, this.serverPath); + } +} + +/* +* The status class shows service installing progress in UI +*/ +export class ServerStatusView implements IStatusView, vscode.Disposable { + private _numberOfSecondsBeforeHidingMessage = 5000; + private _statusBarItem: vscode.StatusBarItem = undefined; + private _progressTimerId: NodeJS.Timer; + private _constants: IExtensionConstants; + + constructor(constants: IExtensionConstants) { + this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right); + vscode.window.onDidChangeActiveTextEditor((params) => this.onDidChangeActiveTextEditor(params)); + vscode.workspace.onDidCloseTextDocument((params) => this.onDidCloseTextDocument(params)); + this._constants = constants; + } + + public installingService(): void { + this._statusBarItem.command = undefined; + this._statusBarItem.show(); + + this.showProgress('$(desktop-download) ' + Constants.serviceInstalling); + } + + public updateServiceDownloadingProgress(downloadPercentage: number): void { + this._statusBarItem.text = '$(cloud-download) ' + `${Constants.serviceDownloading} ... ${downloadPercentage}%`; + this._statusBarItem.show(); + } + + public serviceInstalled(): void { + + this._statusBarItem.command = undefined; + this._statusBarItem.text = this._constants.serviceInstalled; + this._statusBarItem.show(); + // Cleat the status bar after 2 seconds + setTimeout(() => { + this._statusBarItem.hide(); + }, this._numberOfSecondsBeforeHidingMessage); + } + + public serviceInstallationFailed(): void { + this._statusBarItem.command = undefined; + this._statusBarItem.text = this._constants.serviceInstallationFailed; + this._statusBarItem.show(); + } + + private showProgress(statusText: string): void { + let index = 0; + let progressTicks = [ '|', '/', '-', '\\']; + + + this._progressTimerId = setInterval(() => { + index++; + if (index > 3) { + index = 0; + } + + let progressTick = progressTicks[index]; + if (this._statusBarItem.text !== this._constants.serviceInstalled) { + this._statusBarItem.text = statusText + ' ' + progressTick; + this._statusBarItem.show(); + } + }, 200); + } + + dispose(): void { + this.destroyStatusBar(); + } + + private hideLastShownStatusBar(): void { + if (typeof this._statusBarItem !== 'undefined') { + this._statusBarItem.hide(); + } + } + + private onDidChangeActiveTextEditor(editor: vscode.TextEditor): void { + // Hide the most recently shown status bar + this.hideLastShownStatusBar(); + } + + private onDidCloseTextDocument(doc: vscode.TextDocument): void { + // Remove the status bar associated with the document + this.destroyStatusBar(); + } + + private destroyStatusBar(): void { + if (typeof this._statusBarItem !== 'undefined') { + this._statusBarItem.dispose(); + } + } +} + diff --git a/extensions-modules/src/languageservice/serviceClient.ts b/extensions-modules/src/languageservice/serviceClient.ts new file mode 100644 index 0000000000..f065bde69f --- /dev/null +++ b/extensions-modules/src/languageservice/serviceClient.ts @@ -0,0 +1,492 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import { ExtensionContext, workspace, window, OutputChannel, languages } from 'vscode'; +import { LanguageClient, LanguageClientOptions, ServerOptions, + TransportKind, RequestType, NotificationType, NotificationHandler, + ErrorAction, CloseAction } from 'dataprotocol-client'; + +import VscodeWrapper from '../controllers/vscodeWrapper'; +import Telemetry from '../models/telemetry'; +import * as Utils from '../models/utils'; +import {VersionRequest, IExtensionConstants} from '../models/contracts/contracts'; +import {Logger} from '../models/logger'; +import Constants = require('../models/constants'); +import {ILanguageClientHelper} from '../models/contracts/languageService'; +import ServerProvider from './server'; +import ServiceDownloadProvider from './serviceDownloadProvider'; +import DecompressProvider from './decompressProvider'; +import HttpClient from './httpClient'; +import ExtConfig from '../configurations/extConfig'; +import {PlatformInformation, Runtime} from '../models/platform'; +import {ServerInitializationResult, ServerStatusView} from './serverStatus'; +import StatusView from '../views/statusView'; +import * as LanguageServiceContracts from '../models/contracts/languageService'; +import * as SharedConstants from '../models/constants'; +import * as utils from '../models/utils'; +var path = require('path'); +import ServiceStatus from './serviceStatus'; + +let opener = require('opener'); +let _channel: OutputChannel = undefined; +const fs = require('fs-extra'); + +/** + * @interface IMessage + */ +interface IMessage { + jsonrpc: string; +} + +/** + * Handle Language Service client errors + * @class LanguageClientErrorHandler + */ +class LanguageClientErrorHandler { + + private vscodeWrapper: VscodeWrapper; + + /** + * Creates an instance of LanguageClientErrorHandler. + * @memberOf LanguageClientErrorHandler + */ + constructor(constants: IExtensionConstants) { + if (!this.vscodeWrapper) { + this.vscodeWrapper = new VscodeWrapper(constants); + } + Telemetry.getRuntimeId = this.vscodeWrapper.constants.getRuntimeId; + } + + /** + * Show an error message prompt with a link to known issues wiki page + * @memberOf LanguageClientErrorHandler + */ + showOnErrorPrompt(): void { + let extensionConstants = this.vscodeWrapper.constants; + Telemetry.sendTelemetryEvent(extensionConstants.serviceName + 'Crash'); + this.vscodeWrapper.showErrorMessage( + extensionConstants.serviceCrashMessage, + SharedConstants.serviceCrashButton).then(action => { + if (action && action === SharedConstants.serviceCrashButton) { + opener(extensionConstants.serviceCrashLink); + } + }); + } + + /** + * Callback for language service client error + * + * @param {Error} error + * @param {Message} message + * @param {number} count + * @returns {ErrorAction} + * + * @memberOf LanguageClientErrorHandler + */ + error(error: Error, message: IMessage, count: number): ErrorAction { + this.showOnErrorPrompt(); + + // we don't retry running the service since crashes leave the extension + // in a bad, unrecovered state + return ErrorAction.Shutdown; + } + + /** + * Callback for language service client closed + * + * @returns {CloseAction} + * + * @memberOf LanguageClientErrorHandler + */ + closed(): CloseAction { + this.showOnErrorPrompt(); + + // we don't retry running the service since crashes leave the extension + // in a bad, unrecovered state + return CloseAction.DoNotRestart; + } +} + +// The Service Client class handles communication with the VS Code LanguageClient +export default class SqlToolsServiceClient { + // singleton instance + private static _instance: SqlToolsServiceClient = undefined; + + private static _constants: IExtensionConstants = undefined; + + public static get constants(): IExtensionConstants { + return this._constants; + } + + public static set constants(constantsObject: IExtensionConstants) { + this._constants = constantsObject; + Telemetry.getRuntimeId = this._constants.getRuntimeId; + } + + private static _helper: ILanguageClientHelper = undefined; + + public static get helper(): ILanguageClientHelper { + return this._helper; + } + + public static set helper(helperObject: ILanguageClientHelper) { + this._helper = helperObject; + } + + // VS Code Language Client + private _client: LanguageClient = undefined; + + // getter method for the Language Client + private get client(): LanguageClient { + return this._client; + } + + private set client(client: LanguageClient) { + this._client = client; + } + + public installDirectory: string; + private _downloadProvider: ServiceDownloadProvider; + private _vscodeWrapper: VscodeWrapper; + + private _serviceStatus: ServiceStatus; + + private _languageClientStartTime: number = undefined; + private _installationTime: number = undefined; + + constructor( + private _server: ServerProvider, + private _logger: Logger, + private _statusView: StatusView, + private _config: ExtConfig) { + this._downloadProvider = _server.downloadProvider; + if (!this._vscodeWrapper) { + this._vscodeWrapper = new VscodeWrapper(SqlToolsServiceClient.constants); + } + this._serviceStatus = new ServiceStatus(SqlToolsServiceClient._constants.serviceName); + } + + // gets or creates the singleton service client instance + public static get instance(): SqlToolsServiceClient { + if (this._instance === undefined) { + let constants = this._constants; + let config = new ExtConfig(constants.extensionConfigSectionName); + _channel = window.createOutputChannel(constants.serviceInitializingOutputChannelName); + let logger = new Logger(text => _channel.append(text), constants); + let serverStatusView = new ServerStatusView(constants); + let httpClient = new HttpClient(); + let decompressProvider = new DecompressProvider(); + let downloadProvider = new ServiceDownloadProvider(config, logger, serverStatusView, httpClient, + decompressProvider, constants, false); + let serviceProvider = new ServerProvider(downloadProvider, config, serverStatusView, constants.extensionConfigSectionName); + let statusView = new StatusView(); + this._instance = new SqlToolsServiceClient(serviceProvider, logger, statusView, config); + } + return this._instance; + } + + // initialize the Service Client instance by launching + // out-of-proc server through the LanguageClient + public initialize(context: ExtensionContext): Promise { + this._logger.appendLine(SqlToolsServiceClient._constants.serviceInitializing); + this._languageClientStartTime = Date.now(); + return PlatformInformation.getCurrent(SqlToolsServiceClient._constants.getRuntimeId, SqlToolsServiceClient._constants.extensionName).then(platformInfo => { + return this.initializeForPlatform(platformInfo, context); + }).catch(err => { + this._vscodeWrapper.showErrorMessage(err) + }); + } + + public initializeForPlatform(platformInfo: PlatformInformation, context: ExtensionContext): Promise { + return new Promise( (resolve, reject) => { + this._logger.appendLine(SqlToolsServiceClient._constants.commandsNotAvailableWhileInstallingTheService); + this._logger.appendLine(); + this._logger.append(`Platform: ${platformInfo.toString()}`); + + if (!platformInfo.isValidRuntime()) { + // if it's an unknown Linux distro then try generic Linux x64 and give a warning to the user + if (platformInfo.isLinux()) { + this._logger.appendLine(Constants.usingDefaultPlatformMessage); + platformInfo.runtimeId = Runtime.Linux_64; + } + + let ignoreWarning: boolean = this._config.getWorkspaceConfig(Constants.ignorePlatformWarning, false); + if (!ignoreWarning) { + this._vscodeWrapper.showErrorMessage( + Constants.unsupportedPlatformErrorMessage, + Constants.neverShowAgain) + .then(action => { + if (action === Constants.neverShowAgain) { + this._config.updateWorkspaceConfig(Constants.ignorePlatformWarning, true); + } + }); + } + + Telemetry.sendTelemetryEvent('UnsupportedPlatform', {platform: platformInfo.toString()} ); + } + + if (platformInfo.runtimeId) { + this._logger.appendLine(` (${platformInfo.getRuntimeDisplayName()})`); + } else { + this._logger.appendLine(); + } + + this._logger.appendLine(); + this._server.getServerPath(platformInfo.runtimeId).then(serverPath => { + if (serverPath === undefined) { + // Check if the service already installed and if not open the output channel to show the logs + if (_channel !== undefined) { + _channel.show(); + } + let installationStartTime = Date.now(); + this._server.downloadServerFiles(platformInfo.runtimeId).then ( installedServerPath => { + this._installationTime = Date.now() - installationStartTime; + this.initializeLanguageClient(installedServerPath, context, platformInfo.runtimeId); + resolve(new ServerInitializationResult(true, true, installedServerPath)); + }).catch(downloadErr => { + reject(downloadErr); + }); + } else { + this.initializeLanguageClient(serverPath, context, platformInfo.runtimeId); + resolve(new ServerInitializationResult(false, true, serverPath)); + } + }).catch(err => { + Utils.logDebug(SqlToolsServiceClient._constants.serviceLoadingFailed + ' ' + err, SqlToolsServiceClient._constants.extensionConfigSectionName); + Utils.showErrorMsg(SqlToolsServiceClient._constants.serviceLoadingFailed, SqlToolsServiceClient._constants.extensionName); + Telemetry.sendTelemetryEvent('ServiceInitializingFailed'); + reject(err); + }); + }); + } + + /** + * Initializes the SQL language configuration + * + * @memberOf SqlToolsServiceClient + */ + private initializeLanguageConfiguration(): void { + languages.setLanguageConfiguration('sql', { + comments: { + lineComment: '--', + blockComment: ['/*', '*/'] + }, + + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'] + ], + + __characterPairSupport: { + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"', notIn: ['string'] }, + { open: '\'', close: '\'', notIn: ['string', 'comment'] } + ] + } + }); + } + + private initializeLanguageClient(serverPath: string, context: ExtensionContext, runtimeId: Runtime): void { + if (serverPath === undefined) { + Utils.logDebug(SqlToolsServiceClient._constants.invalidServiceFilePath, SqlToolsServiceClient._constants.extensionConfigSectionName); + throw new Error(SqlToolsServiceClient._constants.invalidServiceFilePath); + } else { + let self = this; + + if (SqlToolsServiceClient._constants.languageId === 'sql') { + self.initializeLanguageConfiguration(); + } + + // Use default createServerOptions if one isn't specified + let serverOptions: ServerOptions = SqlToolsServiceClient._helper ? + SqlToolsServiceClient._helper.createServerOptions(serverPath, runtimeId) : self.createServerOptions(serverPath); + this.client = this.createLanguageClient(serverOptions); + this.installDirectory = this._downloadProvider.getInstallDirectory(runtimeId, SqlToolsServiceClient._constants.extensionConfigSectionName); + + if (context !== undefined) { + // Create the language client and start the client. + let disposable = this.client.start(); + + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation + + context.subscriptions.push(disposable); + } + } + } + + public createClient(context: ExtensionContext, runtimeId: Runtime, languageClientHelper: ILanguageClientHelper, executableFiles: string[]): Promise { + return new Promise( (resolve, reject) => { + let client: LanguageClient; + this._server.findServerPath(this.installDirectory, executableFiles).then(serverPath => { + if (serverPath === undefined) { + reject(new Error(SqlToolsServiceClient._constants.invalidServiceFilePath)); + } else { + + let serverOptions: ServerOptions = languageClientHelper ? + languageClientHelper.createServerOptions(serverPath, runtimeId) : this.createServerOptions(serverPath); + + // Options to control the language client + let clientOptions: LanguageClientOptions = { + documentSelector: [SqlToolsServiceClient._constants.languageId], + providerId: '', + synchronize: { + configurationSection: SqlToolsServiceClient._constants.extensionConfigSectionName + }, + errorHandler: new LanguageClientErrorHandler(SqlToolsServiceClient._constants), + serverConnectionMetadata: this._config.getConfigValue(Constants.serverConnectionMetadata) + }; + this._serviceStatus.showServiceLoading(); + // cache the client instance for later use + client = new LanguageClient(SqlToolsServiceClient._constants.serviceName, serverOptions, clientOptions); + + if (context !== undefined) { + // Create the language client and start the client. + let disposable = client.start(); + + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation + + context.subscriptions.push(disposable); + } + client.onReady().then(this._serviceStatus.showServiceLoaded); + + resolve(client); + } + }, error => { + reject(error); + }); + }); + + } + + private createServerOptions(servicePath): ServerOptions { + let serverArgs = []; + let serverCommand: string = servicePath; + if (servicePath.endsWith('.dll')) { + serverArgs = [servicePath]; + serverCommand = 'dotnet'; + } + + // Enable diagnostic logging in the service if it is configured + let config = workspace.getConfiguration(SqlToolsServiceClient._constants.extensionConfigSectionName); + if (config) { + let logDebugInfo = config[Constants.configLogDebugInfo]; + if (logDebugInfo) { + serverArgs.push('--enable-logging'); + } + } + serverArgs.push('--log-dir'); + let logFileLocation = path.join(utils.getDefaultLogLocation(), SqlToolsServiceClient.constants.extensionName); + serverArgs.push(logFileLocation); + + // run the service host using dotnet.exe from the path + let serverOptions: ServerOptions = { command: serverCommand, args: serverArgs, transport: TransportKind.stdio }; + return serverOptions; + } + + private createLanguageClient(serverOptions: ServerOptions): LanguageClient { + // Options to control the language client + let clientOptions: LanguageClientOptions = { + documentSelector: [SqlToolsServiceClient._constants.languageId], + providerId: SqlToolsServiceClient._constants.providerId, + synchronize: { + configurationSection: SqlToolsServiceClient._constants.extensionConfigSectionName + }, + errorHandler: new LanguageClientErrorHandler(SqlToolsServiceClient._constants), + serverConnectionMetadata: this._config.getConfigValue(Constants.serverConnectionMetadata) + }; + + this._serviceStatus.showServiceLoading(); + // cache the client instance for later use + let client = new LanguageClient(SqlToolsServiceClient._constants.serviceName, serverOptions, clientOptions); + client.onReady().then( () => { + this.checkServiceCompatibility(); + this._serviceStatus.showServiceLoaded(); + client.onNotification(LanguageServiceContracts.TelemetryNotification.type, this.handleLanguageServiceTelemetryNotification()); + client.onNotification(LanguageServiceContracts.StatusChangedNotification.type, this.handleLanguageServiceStatusNotification()); + + // Report the language client startup time + let endTime = Date.now(); + let installationTime = this._installationTime || 0; + let totalTime = endTime - this._languageClientStartTime; + let processStartupTime = totalTime - installationTime; + Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', { + installationTime: String(installationTime), + processStartupTime: String(processStartupTime), + totalTime: String(totalTime), + beginningTimestamp: String(this._languageClientStartTime) + }); + this._languageClientStartTime = undefined; + this._installationTime = undefined; + }); + + return client; + } + + private handleLanguageServiceTelemetryNotification(): NotificationHandler { + return (event: LanguageServiceContracts.TelemetryParams): void => { + Telemetry.sendTelemetryEvent(event.params.eventName, event.params.properties, event.params.measures); + }; + } + + /** + * Public for testing purposes only. + */ + public handleLanguageServiceStatusNotification(): NotificationHandler { + return (event: LanguageServiceContracts.StatusChangeParams): void => { + this._statusView.languageServiceStatusChanged(event.ownerUri, event.status); + }; + } + + /** + * Send a request to the service client + * @param type The of the request to make + * @param params The params to pass with the request + * @returns A thenable object for when the request receives a response + */ + public sendRequest(type: RequestType, params?: P, client: LanguageClient = undefined): Thenable { + if (client === undefined) { + client = this._client; + } + if (client !== undefined) { + return client.sendRequest(type, params); + } + } + + /** + * Register a handler for a notification type + * @param type The notification type to register the handler for + * @param handler The handler to register + */ + public onNotification

(type: NotificationType

, handler: NotificationHandler

, client: LanguageClient = undefined): void { + if (client === undefined) { + client = this._client; + } + if (client !== undefined) { + return client.onNotification(type, handler); + } + } + + public checkServiceCompatibility(): Promise { + return new Promise((resolve, reject) => { + this._client.sendRequest(VersionRequest.type, undefined).then((result) => { + Utils.logDebug(SqlToolsServiceClient._constants.extensionName + ' service client version: ' + result, SqlToolsServiceClient._constants.extensionConfigSectionName); + + if (result === undefined || !result.startsWith(SqlToolsServiceClient._constants.serviceCompatibleVersion)) { + Utils.showErrorMsg(Constants.serviceNotCompatibleError, SqlToolsServiceClient._constants.extensionName); + Utils.logDebug(Constants.serviceNotCompatibleError, SqlToolsServiceClient._constants.extensionConfigSectionName); + resolve(false); + } else { + resolve(true); + } + }); + }); + } +} diff --git a/extensions-modules/src/languageservice/serviceDownloadProvider.ts b/extensions-modules/src/languageservice/serviceDownloadProvider.ts new file mode 100644 index 0000000000..5bd2c36417 --- /dev/null +++ b/extensions-modules/src/languageservice/serviceDownloadProvider.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Runtime, getRuntimeDisplayName } from '../models/platform'; +import * as path from 'path'; +import { IConfig, IStatusView, IPackage, PackageError, IHttpClient, IDecompressProvider } from './interfaces'; +import { ILogger } from '../models/interfaces'; +import Constants = require('../models/constants'); +import * as tmp from 'tmp'; +import {IExtensionConstants} from '../models/contracts/contracts'; + +let fse = require('fs-extra'); + +/* +* Service Download Provider class which handles downloading the SQL Tools service. +*/ +export default class ServiceDownloadProvider { + + constructor(private _config: IConfig, + private _logger: ILogger, + private _statusView: IStatusView, + private _httpClient: IHttpClient, + private _decompressProvider: IDecompressProvider, + private _extensionConstants: IExtensionConstants, + private _fromBuild: boolean) { + // Ensure our temp files get cleaned up in case of error. + tmp.setGracefulCleanup(); + } + + /** + * Returns the download url for given platform + */ + public getDownloadFileName(platform: Runtime): string { + let fileNamesJson = this._config.getConfigValue('downloadFileNames'); + console.info('Platform: ', platform.toString()); + + let fileName = fileNamesJson[platform.toString()]; + console.info('Filename: ', fileName); + + if (fileName === undefined) { + if (process.platform === 'linux') { + throw new Error('Unsupported linux distribution'); + } else { + throw new Error(`Unsupported platform: ${process.platform}`); + } + } + + return fileName; + } + + + /** + * Returns SQL tools service installed folder. + */ + public getInstallDirectory(platform: Runtime, extensionConfigSectionName: string): string { + let basePath = this.getInstallDirectoryRoot(platform, extensionConfigSectionName); + let versionFromConfig = this._config.getPackageVersion(); + basePath = basePath.replace('{#version#}', versionFromConfig); + basePath = basePath.replace('{#platform#}', getRuntimeDisplayName(platform)); + if (!fse.existsSync(basePath)) { + fse.mkdirsSync(basePath); + } + + return basePath; + } + + private getLocalUserFolderPath(platform: Runtime): string { + if (platform) { + switch (platform) { + case Runtime.Windows_64: + case Runtime.Windows_86: + return process.env.APPDATA; + case Runtime.OSX: + return process.env.HOME + '/Library/Preferences'; + default: + return process.env.HOME; + } + } + } + + /** + * Returns SQL tools service installed folder root. + */ + public getInstallDirectoryRoot(platform: Runtime, extensionConfigSectionName: string): string { + let installDirFromConfig : string; + installDirFromConfig = this._config.getInstallDirectory(); + if (!installDirFromConfig || installDirFromConfig === '') { + let rootFolderName: string = '.sqlops'; + if (platform === Runtime.Windows_64 || platform === Runtime.Windows_86) { + rootFolderName = 'sqlops'; + } + installDirFromConfig = path.join(this.getLocalUserFolderPath(platform), `/${rootFolderName}/${this._extensionConstants.installFolderName}/{#version#}/{#platform#}`); + } + let basePath: string; + if (path.isAbsolute(installDirFromConfig)) { + basePath = installDirFromConfig; + } else if (this._fromBuild) { + basePath = path.join(__dirname, '../../../../../extensions/' + extensionConfigSectionName + '/' + installDirFromConfig); + } + else { + // The path from config is relative to the out folder + basePath = path.join(__dirname, '../../../../' + installDirFromConfig); + } + return basePath; + } + + private getGetDownloadUrl(fileName: string): string { + let baseDownloadUrl = this._config.getDownloadUrl(); + let version = this._config.getPackageVersion(); + baseDownloadUrl = baseDownloadUrl.replace('{#version#}', version); + baseDownloadUrl = baseDownloadUrl.replace('{#fileName#}', fileName); + return baseDownloadUrl; + } + + /** + * Downloads the service and decompress it in the install folder. + */ + public installService(platform: Runtime): Promise { + const proxy = this._config.getWorkspaceConfig('http.proxy'); + const strictSSL = this._config.getWorkspaceConfig('http.proxyStrictSSL', true); + + return new Promise((resolve, reject) => { + const fileName = this.getDownloadFileName(platform); + const installDirectory = this.getInstallDirectory(platform, this._extensionConstants.extensionConfigSectionName); + + this._logger.appendLine(`${this._extensionConstants.serviceInstallingTo} ${installDirectory}.`); + const urlString = this.getGetDownloadUrl(fileName); + + this._logger.appendLine(`${Constants.serviceDownloading} ${urlString}`); + let pkg: IPackage = { + installPath: installDirectory, + url: urlString, + tmpFile: undefined + }; + this.createTempFile(pkg).then(tmpResult => { + pkg.tmpFile = tmpResult; + + this._httpClient.downloadFile(pkg.url, pkg, this._logger, this._statusView, proxy, strictSSL).then(_ => { + + this._logger.logDebug(`Downloaded to ${pkg.tmpFile.name}...`); + this._logger.appendLine(' Done!'); + this.install(pkg).then(result => { + resolve(true); + }).catch(installError => { + reject(installError); + }); + }).catch(downloadError => { + this._logger.appendLine(`[ERROR] ${downloadError}`); + reject(downloadError); + }); + }); + }); + } + + private createTempFile(pkg: IPackage): Promise { + return new Promise((resolve, reject) => { + tmp.file({ prefix: 'package-' }, (err, path, fd, cleanupCallback) => { + if (err) { + return reject(new PackageError('Error from tmp.file', pkg, err)); + } + + resolve({ name: path, fd: fd, removeCallback: cleanupCallback }); + }); + }); + } + + private install(pkg: IPackage): Promise { + this._logger.appendLine('Installing ...'); + this._statusView.installingService(); + + return new Promise((resolve, reject) => { + this._decompressProvider.decompress(pkg, this._logger).then(_ => { + this._statusView.serviceInstalled(); + resolve(); + }).catch(err => { + reject(err); + }); + }); + } +} + + + diff --git a/extensions-modules/src/languageservice/serviceInstallerUtil.ts b/extensions-modules/src/languageservice/serviceInstallerUtil.ts new file mode 100644 index 0000000000..948a0f9f9b --- /dev/null +++ b/extensions-modules/src/languageservice/serviceInstallerUtil.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Runtime, PlatformInformation } from '../models/platform'; +import Config from '../configurations/config'; +import ServiceDownloadProvider from './serviceDownloadProvider'; +import DecompressProvider from './decompressProvider'; +import HttpClient from './httpClient'; +import ServerProvider from './server'; +import { IStatusView } from './interfaces'; +import { ILogger } from '../models/interfaces'; +import { IExtensionConstants } from '../models/contracts/contracts'; + +class StubStatusView implements IStatusView { + installingService(): void { + console.log('...'); + } + serviceInstalled(): void { + console.log('Service installed'); + } + serviceInstallationFailed(): void { + console.log('Service installation failed'); + } + updateServiceDownloadingProgress(downloadPercentage: number): void { + if (downloadPercentage === 100) { + process.stdout.write('100%'); + } + } +} + +class StubLogger implements ILogger { + logDebug(message: string): void { + console.log(message); + } + + increaseIndent(): void { + console.log('increaseIndent'); + } + + decreaseIndent(): void { + console.log('decreaseIndent'); + } + + append(message?: string): void { + process.stdout.write(message); + } + appendLine(message?: string): void { + console.log(message); + } +} + +export class ServiceInstaller { + private _config = undefined; + private _logger = new StubLogger(); + private _statusView = new StubStatusView(); + private _httpClient = new HttpClient(); + private _decompressProvider = new DecompressProvider(); + private _downloadProvider = undefined; + private _serverProvider = undefined; + private _extensionConstants = undefined; + + constructor(extensionConstants: IExtensionConstants) { + this._extensionConstants = extensionConstants; + this._config = new Config(extensionConstants.extensionConfigSectionName, true); + this._downloadProvider = new ServiceDownloadProvider(this._config, this._logger, this._statusView, this._httpClient, this._decompressProvider, extensionConstants, true); + this._serverProvider = new ServerProvider(this._downloadProvider, this._config, this._statusView, extensionConstants.extensionConfigSectionName); + } + /* + * Installs the service for the given platform if it's not already installed. + */ + public installService(): Promise { + return PlatformInformation.getCurrent(this._extensionConstants.getRuntimeId, this._extensionConstants.extensionName).then(platformInfo => { + if (platformInfo.isValidRuntime()) { + return this._serverProvider.getOrDownloadServer(platformInfo.runtimeId); + } else { + throw new Error('unsupported runtime'); + } + }); + } + + /* + * Returns the install folder path for given platform. + */ + public getServiceInstallDirectory(runtime: Runtime): Promise { + return new Promise((resolve, reject) => { + if (runtime === undefined) { + PlatformInformation.getCurrent(this._extensionConstants.getRuntimeId, this._extensionConstants.extensionName).then(platformInfo => { + if (platformInfo.isValidRuntime()) { + resolve(this._downloadProvider.getInstallDirectory(platformInfo.runtimeId)); + } else { + reject('unsupported runtime'); + } + }).catch(error => { + reject(error); + }); + } else { + resolve(this._downloadProvider.getInstallDirectory(runtime)); + } + }); + + } + + /* + * Returns the path to the root folder of service install location. + */ + public getServiceInstallDirectoryRoot(runtime: Runtime): Promise { + return new Promise((resolve, reject) => { + if (runtime === undefined) { + PlatformInformation.getCurrent(this._extensionConstants.getRuntimeId, this._extensionConstants.extensionName).then(platformInfo => { + if (platformInfo.isValidRuntime()) { + let directoryPath: string = this._downloadProvider.getInstallDirectoryRoot(platformInfo, this._extensionConstants.extensionName); + directoryPath = directoryPath.replace('\\{#version#}', ''); + directoryPath = directoryPath.replace('\\{#platform#}', ''); + directoryPath = directoryPath.replace('/{#platform#}', ''); + directoryPath = directoryPath.replace('/{#version#}', ''); + resolve(directoryPath); + } else { + reject('unsupported runtime'); + } + }).catch(error => { + reject(error); + }); + } else { + resolve(this._downloadProvider.getInstallDirectory(runtime)); + } + }); + } +} diff --git a/extensions-modules/src/languageservice/serviceStatus.ts b/extensions-modules/src/languageservice/serviceStatus.ts new file mode 100644 index 0000000000..cf187b82d6 --- /dev/null +++ b/extensions-modules/src/languageservice/serviceStatus.ts @@ -0,0 +1,80 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import vscode = require('vscode'); + +export default class ServiceStatus implements vscode.Disposable { + + private _progressTimerId: NodeJS.Timer; + + private _statusBarItem: vscode.StatusBarItem = undefined; + + private durationStatusInMs: number = 1500; + + // These need localization + private _serviceStartingMessage: string = `Starting ${this._serviceName}`; + private _serviceStartedMessage: string = `${this._serviceName} started`; + + constructor(private _serviceName: string) { + this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); + } + + public showServiceLoading(): Promise { + return this === undefined ? + Promise.resolve() : + Promise.resolve(this.updateStatusView(this._serviceStartingMessage, true)); + } + + public showServiceLoaded(): Promise { + return this === undefined ? + Promise.resolve() : + Promise.resolve(this.updateStatusView(this._serviceStartedMessage, false, this.durationStatusInMs)); + } + + //TODO: This can be merged with the serverStatus code + private showProgress(statusText: string): void { + let index: number = 0; + let progressTicks: string[] = ['.', '..', '...', '....']; + + this._progressTimerId = setInterval(() => { + index = (index + 1) % progressTicks.length; + let progressTick = progressTicks[index]; + if (this._statusBarItem.text !== this._serviceStartedMessage) { + this._statusBarItem.text = statusText + ' ' + progressTick; + this._statusBarItem.show(); + } + }, 400); + } + + private updateStatusView(message: string, showAsProgress: boolean = false, disposeAfter: number = -1): Promise { + return new Promise((resolve, reject) => { + if (showAsProgress) { + this.showProgress(message); + } + else { + this._statusBarItem.text = message; + this._statusBarItem.show(); + if (this._progressTimerId !== undefined) { + clearInterval(this._progressTimerId); + } + } + if (disposeAfter !== -1) { + setInterval(() => { + this._statusBarItem.hide(); + }, disposeAfter); + } + resolve(); + }); + } + + dispose(): void { + if (this._progressTimerId !== undefined) { + clearInterval(this._progressTimerId); + } + this._statusBarItem.dispose(); + } + +} diff --git a/extensions-modules/src/main.ts b/extensions-modules/src/main.ts new file mode 100644 index 0000000000..ae6dee9cb4 --- /dev/null +++ b/extensions-modules/src/main.ts @@ -0,0 +1,13 @@ +import SqlToolsServiceClient from './languageservice/serviceClient'; +import ServerProvider from './languageservice/server'; +import VscodeWrapper from './controllers/vscodeWrapper'; +import * as SharedConstants from './models/constants'; +import * as Utils from './models/utils'; + +export {SqlToolsServiceClient, VscodeWrapper, SharedConstants, Utils}; +export {IExtensionConstants} from './models/contracts/contracts'; +export {ILanguageClientHelper} from './models/contracts/languageService'; +export {Runtime, PlatformInformation} from './models/platform'; +export {Telemetry} from './models/telemetry'; +export {LinuxDistribution} from './models/platform'; +export {ServiceInstaller} from './languageservice/serviceInstallerUtil'; \ No newline at end of file diff --git a/extensions-modules/src/models/constants.ts b/extensions-modules/src/models/constants.ts new file mode 100644 index 0000000000..d599ca23f9 --- /dev/null +++ b/extensions-modules/src/models/constants.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//constants +export const configLogDebugInfo: string = 'logDebugInfo'; +export const serviceNotCompatibleError: string = "Client is not compatible with the service layer"; +export const serviceDownloading: string = "Downloading"; +export const serviceInstalling: string = "Installing"; +export const unsupportedPlatformErrorMessage: string = "This platform is unsupported and application services may not function correctly"; +export const extensionActivated: string = 'activated.'; +export const extensionDeactivated: string = 'de-activated.'; +export const configEnabled: string = 'enabled'; +export const configUseDebugSource = 'useDebugSource'; +export const serviceConfigKey = 'service'; +export const executableFilesConfigKey = 'executableFiles'; +export const versionConfigKey = 'version'; +export const downloadUrlConfigKey = 'downloadUrl'; +export const installDirConfigKey = 'installDir'; +export const serviceCrashButton = "View Known Issues"; +export const configDebugSourcePath = 'debugSourcePath'; +export const neverShowAgain = "Don't show again"; +export const ignorePlatformWarning = 'ignorePlatformWarning'; +export const usingDefaultPlatformMessage = "Unknown platform detected, defaulting to Linux_x64 platform"; +export const serverConnectionMetadata = "serverConnectionMetadata"; \ No newline at end of file diff --git a/extensions-modules/src/models/contracts/contracts.ts b/extensions-modules/src/models/contracts/contracts.ts new file mode 100644 index 0000000000..97e48f55ee --- /dev/null +++ b/extensions-modules/src/models/contracts/contracts.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict' + +import {RequestType} from 'dataprotocol-client'; +import {Runtime, LinuxDistribution} from '../platform'; + +// --------------------------------- < Version Request > ------------------------------------------------- + +// Version request message callback declaration +export namespace VersionRequest { + export const type: RequestType = { get method(): string { return 'version'; } }; +} + +// Version response format +export type VersionResult = string; + +// ------------------------------- -------------------------------------------------- + +// Constants interface for each extension +export interface IExtensionConstants { + // TODO: Fill in interface + + // Definitely dependent on the extension + extensionName: string; + invalidServiceFilePath: string; + serviceName: string; + extensionConfigSectionName: string; + serviceCompatibleVersion: string; + outputChannelName: string; + languageId: string; + serviceInstallingTo: string; + serviceInitializing: string; + serviceInstalled: string; + serviceLoadingFailed: string; + serviceInstallationFailed: string; + serviceInitializingOutputChannelName: string; + commandsNotAvailableWhileInstallingTheService : string; + providerId: string; + serviceCrashMessage: string; + serviceCrashLink: string; + installFolderName: string; + telemetryExtensionName: string; + + getRuntimeId(platform: string, architecture: string, distribution: LinuxDistribution): Runtime; + +} + diff --git a/extensions-modules/src/models/contracts/languageService.ts b/extensions-modules/src/models/contracts/languageService.ts new file mode 100644 index 0000000000..e366abb255 --- /dev/null +++ b/extensions-modules/src/models/contracts/languageService.ts @@ -0,0 +1,55 @@ +import {NotificationType, ServerOptions} from 'dataprotocol-client'; +import {ITelemetryEventProperties, ITelemetryEventMeasures} from '../telemetry'; +import {Runtime} from '../platform'; + +// ------------------------------- < Telemetry Sent Event > ------------------------------------ + +/** + * Event sent when the language service send a telemetry event + */ +export namespace TelemetryNotification { + export const type: NotificationType = { get method(): string { return 'telemetry/sqlevent'; } }; +} + +/** + * Update event parameters + */ +export class TelemetryParams { + public params: { + eventName: string; + properties: ITelemetryEventProperties; + measures: ITelemetryEventMeasures; + }; +} + +// ------------------------------- ---------------------------------- + +// ------------------------------- < Status Event > ------------------------------------ + +/** + * Event sent when the language service send a status change event + */ +export namespace StatusChangedNotification { + export const type: NotificationType = { get method(): string { return 'textDocument/statusChanged'; } }; +} + +/** + * Update event parameters + */ +export class StatusChangeParams { + /** + * URI identifying the text document + */ + public ownerUri: string; + + /** + * The new status of the document + */ + public status: string; +} + +// ------------------------------- ---------------------------------- + +export interface ILanguageClientHelper { + createServerOptions(servicePath: string, runtimeId?: Runtime): ServerOptions; +} \ No newline at end of file diff --git a/extensions-modules/src/models/interfaces.ts b/extensions-modules/src/models/interfaces.ts new file mode 100644 index 0000000000..e9ec72dab1 --- /dev/null +++ b/extensions-modules/src/models/interfaces.ts @@ -0,0 +1,13 @@ +'use strict'; + +export interface ILogger { + logDebug(message: string): void; + increaseIndent(): void; + decreaseIndent(): void; + append(message?: string): void; + appendLine(message?: string): void; +} + +export interface IRuntime { + getRuntimeDisplayName(); +} diff --git a/extensions-modules/src/models/logger.ts b/extensions-modules/src/models/logger.ts new file mode 100644 index 0000000000..511d4d1cec --- /dev/null +++ b/extensions-modules/src/models/logger.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as os from 'os'; +import {ILogger} from './interfaces'; +import * as Utils from './utils'; +import {IExtensionConstants} from './contracts/contracts'; +/* +* Logger class handles logging messages using the Util functions. +*/ +export class Logger implements ILogger { + private _writer: (message: string) => void; + private _prefix: string; + private _extensionConstants: IExtensionConstants; + + private _indentLevel: number = 0; + private _indentSize: number = 4; + private _atLineStart: boolean = false; + + constructor(writer: (message: string) => void, extensionConstants: IExtensionConstants, prefix?: string) { + this._writer = writer; + this._prefix = prefix; + this._extensionConstants = extensionConstants; + } + + public logDebug(message: string): void { + Utils.logDebug(message, this._extensionConstants.extensionConfigSectionName); + } + + private _appendCore(message: string): void { + if (this._atLineStart) { + if (this._indentLevel > 0) { + const indent = ' '.repeat(this._indentLevel * this._indentSize); + this._writer(indent); + } + + if (this._prefix) { + this._writer(`[${this._prefix}] `); + } + + this._atLineStart = false; + } + + this._writer(message); + } + + public increaseIndent(): void { + this._indentLevel += 1; + } + + public decreaseIndent(): void { + if (this._indentLevel > 0) { + this._indentLevel -= 1; + } + } + + public append(message?: string): void { + message = message || ''; + this._appendCore(message); + } + + public appendLine(message?: string): void { + message = message || ''; + this._appendCore(message + os.EOL); + this._atLineStart = true; + } +} diff --git a/extensions-modules/src/models/platform.ts b/extensions-modules/src/models/platform.ts new file mode 100644 index 0000000000..bcedf9c2b1 --- /dev/null +++ b/extensions-modules/src/models/platform.ts @@ -0,0 +1,369 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as child_process from 'child_process'; +import * as fs from 'fs'; +import * as os from 'os'; + +const unknown = 'unknown'; + +export enum Runtime { + UnknownRuntime = 'Unknown', + UnknownVersion = 'Unknown', + Windows_86 = 'Windows_86', + Windows_64 = 'Windows_64', + OSX = 'OSX', + CentOS_7 = 'CentOS_7', + Debian_8 = 'Debian_8', + Fedora_23 = 'Fedora_23', + OpenSUSE_13_2 = 'OpenSUSE_13_2', + SLES_12_2 = 'SLES_12_2', + RHEL_7 = 'RHEL_7', + Ubuntu_14 = 'Ubuntu_14', + Ubuntu_16 = 'Ubuntu_16', + Linux_64 = 'Linux_64', + Linux_86 = 'Linux-86' +} + +export function getRuntimeDisplayName(runtime: Runtime): string { + switch (runtime) { + case Runtime.Windows_64: + return 'Windows'; + case Runtime.Windows_86: + return 'Windows'; + case Runtime.OSX: + return 'OSX'; + case Runtime.CentOS_7: + return 'Linux'; + case Runtime.Debian_8: + return 'Linux'; + case Runtime.Fedora_23: + return 'Linux'; + case Runtime.OpenSUSE_13_2: + return 'Linux'; + case Runtime.SLES_12_2: + return 'Linux'; + case Runtime.RHEL_7: + return 'Linux'; + case Runtime.Ubuntu_14: + return 'Linux'; + case Runtime.Ubuntu_16: + return 'Linux'; + case Runtime.Linux_64: + return 'Linux'; + case Runtime.Linux_86: + return 'Linux'; + default: + return 'Unknown'; + } +} + +export class PlatformInformation { + public runtimeId: Runtime; + + public constructor( + public platform: string, + public architecture: string, + public distribution: LinuxDistribution = undefined, + public getRuntimeId: (platform: string, architecture: string, distribution: LinuxDistribution) => Runtime) { + try { + this.runtimeId = this.getRuntimeId(platform, architecture, distribution); + } catch (err) { + this.runtimeId = undefined; + } + } + + public isWindows(): boolean { + return this.platform === 'win32'; + } + + public isMacOS(): boolean { + return this.platform === 'darwin'; + } + + public isLinux(): boolean { + return this.platform === 'linux'; + } + + public isValidRuntime(): boolean { + return this.runtimeId !== undefined && this.runtimeId !== Runtime.UnknownRuntime && this.runtimeId !== Runtime.UnknownVersion; + } + + public getRuntimeDisplayName(): string { + return getRuntimeDisplayName(this.runtimeId); + } + + public toString(): string { + let result = this.platform; + + if (this.architecture) { + if (result) { + result += ', '; + } + + result += this.architecture; + } + + if (this.distribution) { + if (result) { + result += ', '; + } + + result += this.distribution.toString(); + } + + return result; + } + + public static getCurrent(getRuntimeId: (platform: string, architecture: string, distribution: LinuxDistribution) => Runtime, + extensionName: string): Promise { + let platform = os.platform(); + let architecturePromise: Promise; + let distributionPromise: Promise; + + switch (platform) { + case 'win32': + architecturePromise = PlatformInformation.getWindowsArchitecture(); + distributionPromise = Promise.resolve(undefined); + break; + + case 'darwin': + let osVersion = os.release(); + if (parseFloat(osVersion) < 16.0 && extensionName === 'mssql') { + return Promise.reject('The current version of macOS is not supported. Only macOS Sierra and above (>= 10.12) are supported.') + } + architecturePromise = PlatformInformation.getUnixArchitecture(); + distributionPromise = Promise.resolve(undefined); + break; + + case 'linux': + architecturePromise = PlatformInformation.getUnixArchitecture(); + distributionPromise = LinuxDistribution.getCurrent(); + break; + + default: + return Promise.reject(`Unsupported platform: ${platform}`); + } + + return architecturePromise.then( arch => { + return distributionPromise.then(distro => { + return new PlatformInformation(platform, arch, distro, getRuntimeId); + }); + }); + } + + + private static getWindowsArchitecture(): Promise { + return new Promise((resolve, reject) => { + // try to get the architecture from WMIC + PlatformInformation.getWindowsArchitectureWmic().then(architecture => { + if (architecture && architecture !== unknown) { + resolve(architecture); + } else { + // sometimes WMIC isn't available on the path so then try to parse the envvar + PlatformInformation.getWindowsArchitectureEnv().then(architecture => { + resolve(architecture); + }); + } + }); + }); + } + + private static getWindowsArchitectureWmic(): Promise { + return this.execChildProcess('wmic os get osarchitecture') + .then(architecture => { + if (architecture) { + let archArray: string[] = architecture.split(os.EOL); + if (archArray.length >= 2) { + let arch = archArray[1].trim(); + + // Note: This string can be localized. So, we'll just check to see if it contains 32 or 64. + if (arch.indexOf('64') >= 0) { + return 'x86_64'; + } else if (arch.indexOf('32') >= 0) { + return 'x86'; + } + } + } + + return unknown; + }).catch((error) => { + return unknown; + }); + } + + private static getWindowsArchitectureEnv(): Promise { + return new Promise((resolve, reject) => { + if (process.env.PROCESSOR_ARCHITECTURE === 'x86' && process.env.PROCESSOR_ARCHITEW6432 === undefined) { + resolve('x86'); + } + else { + resolve('x86_64'); + } + }); + } + + private static getUnixArchitecture(): Promise { + return this.execChildProcess('uname -m') + .then(architecture => { + if (architecture) { + return architecture.trim(); + } + + return undefined; + }); + } + + private static execChildProcess(process: string): Promise { + return new Promise((resolve, reject) => { + child_process.exec(process, { maxBuffer: 500 * 1024 }, (error: Error, stdout: string, stderr: string) => { + if (error) { + reject(error); + return; + } + + if (stderr && stderr.length > 0) { + reject(new Error(stderr)); + return; + } + + resolve(stdout); + }); + }); + } + + private static getRuntimeIdHelper(distributionName: string, distributionVersion: string): Runtime { + switch (distributionName) { + case 'ubuntu': + if (distributionVersion.startsWith('14')) { + // This also works for Linux Mint + return Runtime.Ubuntu_14; + } else if (distributionVersion.startsWith('16')) { + return Runtime.Ubuntu_16; + } + + break; + case 'elementary': + case 'elementary OS': + if (distributionVersion.startsWith('0.3')) { + // Elementary OS 0.3 Freya is binary compatible with Ubuntu 14.04 + return Runtime.Ubuntu_14; + } else if (distributionVersion.startsWith('0.4')) { + // Elementary OS 0.4 Loki is binary compatible with Ubuntu 16.04 + return Runtime.Ubuntu_16; + } + + break; + case 'linuxmint': + if (distributionVersion.startsWith('18')) { + // Linux Mint 18 is binary compatible with Ubuntu 16.04 + return Runtime.Ubuntu_16; + } + + break; + case 'centos': + case 'ol': + // Oracle Linux is binary compatible with CentOS + return Runtime.CentOS_7; + case 'fedora': + return Runtime.Fedora_23; + case 'opensuse': + return Runtime.OpenSUSE_13_2; + case 'sles': + return Runtime.SLES_12_2; + case 'rhel': + return Runtime.RHEL_7; + case 'debian': + return Runtime.Debian_8; + case 'galliumos': + if (distributionVersion.startsWith('2.0')) { + return Runtime.Ubuntu_16; + } + break; + default: + return Runtime.Linux_64; + } + + return Runtime.Linux_64; + } +} + +/** + * There is no standard way on Linux to find the distribution name and version. + * Recently, systemd has pushed to standardize the os-release file. This has + * seen adoption in "recent" versions of all major distributions. + * https://www.freedesktop.org/software/systemd/man/os-release.html + */ +export class LinuxDistribution { + public constructor( + public name: string, + public version: string, + public idLike?: string[]) { } + + public static getCurrent(): Promise { + // Try /etc/os-release and fallback to /usr/lib/os-release per the synopsis + // at https://www.freedesktop.org/software/systemd/man/os-release.html. + return LinuxDistribution.fromFilePath('/etc/os-release') + .catch(() => LinuxDistribution.fromFilePath('/usr/lib/os-release')) + .catch(() => Promise.resolve(new LinuxDistribution(unknown, unknown))); + } + + public toString(): string { + return `name=${this.name}, version=${this.version}`; + } + + private static fromFilePath(filePath: string): Promise { + return new Promise((resolve, reject) => { + fs.readFile(filePath, 'utf8', (error, data) => { + if (error) { + reject(error); + } else { + resolve(LinuxDistribution.fromReleaseInfo(data)); + } + }); + }); + } + + public static fromReleaseInfo(releaseInfo: string, eol: string = os.EOL): LinuxDistribution { + let name = unknown; + let version = unknown; + let idLike: string[] = undefined; + + const lines = releaseInfo.split(eol); + for (let line of lines) { + line = line.trim(); + + let equalsIndex = line.indexOf('='); + if (equalsIndex >= 0) { + let key = line.substring(0, equalsIndex); + let value = line.substring(equalsIndex + 1); + + // Strip quotes if necessary + if (value.length > 1 && value.startsWith('"') && value.endsWith('"')) { + value = value.substring(1, value.length - 1); + } else if (value.length > 1 && value.startsWith('\'') && value.endsWith('\'')) { + value = value.substring(1, value.length - 1); + } + + if (key === 'ID') { + name = value; + } else if (key === 'VERSION_ID') { + version = value; + } else if (key === 'ID_LIKE') { + idLike = value.split(' '); + } + + if (name !== unknown && version !== unknown && idLike !== undefined) { + break; + } + } + } + + return new LinuxDistribution(name, version, idLike); + } +} + diff --git a/extensions-modules/src/models/telemetry.ts b/extensions-modules/src/models/telemetry.ts new file mode 100644 index 0000000000..27dedaa77b --- /dev/null +++ b/extensions-modules/src/models/telemetry.ts @@ -0,0 +1,161 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import vscode = require('vscode'); +import TelemetryReporter from 'vscode-extension-telemetry'; +import Utils = require('./utils'); +import { PlatformInformation, Runtime, LinuxDistribution } from './platform'; +import { IExtensionConstants } from './contracts/contracts'; + +export interface ITelemetryEventProperties { + [key: string]: string; + } + +export interface ITelemetryEventMeasures { + [key: string]: number; +} + +/** + * Filters error paths to only include source files. Exported to support testing + */ +export function FilterErrorPath(line: string): string { + if (line) { + let values: string[] = line.split('/out/'); + if (values.length <= 1) { + // Didn't match expected format + return line; + } else { + return values[1]; + } + } +} + +export class Telemetry { + private static reporter: TelemetryReporter; + private static userId: string; + private static platformInformation: PlatformInformation; + private static disabled: boolean; + private static _getRuntimeId: (platform: string, architecture: string, distribution: LinuxDistribution) => Runtime; + + public static get getRuntimeId() { + return this._getRuntimeId; + } + + public static set getRuntimeId(runtimeIdGetter: (platform: string, architecture: string, distribution: LinuxDistribution) => Runtime) { + this._getRuntimeId = runtimeIdGetter; + } + + // Get the unique ID for the current user of the extension + public static getUserId(): Promise { + return new Promise(resolve => { + // Generate the user id if it has not been created already + if (typeof this.userId === 'undefined') { + let id = Utils.generateUserId(); + id.then(newId => { + this.userId = newId; + resolve(this.userId); + }); + } else { + resolve(this.userId); + } + }); + } + + public static getPlatformInformation(): Promise { + if (this.platformInformation) { + return Promise.resolve(this.platformInformation); + } else { + return new Promise(resolve => { + PlatformInformation.getCurrent(this.getRuntimeId, 'telemetry').then(info => { + this.platformInformation = info; + resolve(this.platformInformation); + }); + }); + } + } + + + + /** + * Disable telemetry reporting + */ + public static disable(): void { + this.disabled = true; + } + + /** + * Initialize the telemetry reporter for use. + */ + public static initialize(context: vscode.ExtensionContext, extensionConstants: IExtensionConstants): void { + if (typeof this.reporter === 'undefined') { + // Check if the user has opted out of telemetry + if (!vscode.workspace.getConfiguration('telemetry').get('enableTelemetry', true)) { + this.disable(); + return; + } + + let packageInfo = Utils.getPackageInfo(context); + this.reporter = new TelemetryReporter(extensionConstants.telemetryExtensionName, packageInfo.version, packageInfo.aiKey); + } + } + + /** + * Send a telemetry event for an exception + */ + public static sendTelemetryEventForException( + err: any, methodName: string, extensionConfigName: string): void { + try { + let stackArray: string[]; + let firstLine: string = ''; + if (err !== undefined && err.stack !== undefined) { + stackArray = err.stack.split('\n'); + if (stackArray !== undefined && stackArray.length >= 2) { + firstLine = stackArray[1]; // The fist line is the error message and we don't want to send that telemetry event + firstLine = FilterErrorPath(firstLine); + } + } + + // Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII + this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine }); + Utils.logDebug('Unhandled Exception occurred. error: ' + err + ' method: ' + methodName, extensionConfigName); + } catch (telemetryErr) { + // If sending telemetry event fails ignore it so it won't break the extension + Utils.logDebug('Failed to send telemetry event. error: ' + telemetryErr, extensionConfigName); + } + } + + /** + * Send a telemetry event using application insights + */ + public static sendTelemetryEvent( + eventName: string, + properties?: ITelemetryEventProperties, + measures?: ITelemetryEventMeasures): void { + + if (typeof this.disabled === 'undefined') { + this.disabled = false; + } + if (this.disabled || typeof (this.reporter) === 'undefined') { + // Don't do anything if telemetry is disabled + return; + } + + if (!properties || typeof properties === 'undefined') { + properties = {}; + } + + // Augment the properties structure with additional common properties before sending + Promise.all([this.getUserId, this.getPlatformInformation]).then(() => { + properties['userId'] = this.userId; + properties['distribution'] = (this.platformInformation && this.platformInformation.distribution) ? + `${this.platformInformation.distribution.name}, ${this.platformInformation.distribution.version}` : ''; + + this.reporter.sendTelemetryEvent(eventName, properties, measures); + }); + } +} + +export default Telemetry; diff --git a/extensions-modules/src/models/utils.ts b/extensions-modules/src/models/utils.ts new file mode 100644 index 0000000000..4e97361ea6 --- /dev/null +++ b/extensions-modules/src/models/utils.ts @@ -0,0 +1,270 @@ +'use strict'; +import * as crypto from 'crypto'; +import * as os from 'os'; +import vscode = require('vscode'); +import Constants = require('./constants'); +import {ExtensionContext} from 'vscode'; +import fs = require('fs'); +var path = require('path'); + +// CONSTANTS ////////////////////////////////////////////////////////////////////////////////////// +const msInH = 3.6e6; +const msInM = 60000; +const msInS = 1000; + +// INTERFACES ///////////////////////////////////////////////////////////////////////////////////// + +// Interface for package.json information +export interface IPackageInfo { + name: string; + version: string; + aiKey: string; +} + +// FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////// + +// Get information from the extension's package.json file +export function getPackageInfo(context: ExtensionContext): IPackageInfo { + let extensionPackage = require(context.asAbsolutePath('./package.json')); + if (extensionPackage) { + return { + name: extensionPackage.name, + version: extensionPackage.version, + aiKey: extensionPackage.aiKey + }; + } +} + +// Generate a new GUID +export function generateGuid(): string { + let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']; + // c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx) + let oct: string = ''; + let tmp: number; + /* tslint:disable:no-bitwise */ + for (let a: number = 0; a < 4; a++) { + tmp = (4294967296 * Math.random()) | 0; + oct += hexValues[tmp & 0xF] + + hexValues[tmp >> 4 & 0xF] + + hexValues[tmp >> 8 & 0xF] + + hexValues[tmp >> 12 & 0xF] + + hexValues[tmp >> 16 & 0xF] + + hexValues[tmp >> 20 & 0xF] + + hexValues[tmp >> 24 & 0xF] + + hexValues[tmp >> 28 & 0xF]; + } + + // 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively' + let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0]; + return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12); + /* tslint:enable:no-bitwise */ +} + +// Generate a unique, deterministic ID for the current user of the extension +export function generateUserId(): Promise { + return new Promise(resolve => { + try { + let interfaces = os.networkInterfaces(); + let mac; + for(let key of Object.keys(interfaces)) { + let item = interfaces[key][0]; + if (!item.internal) { + mac = item.mac; + break; + } + } + if (mac) { + resolve(crypto.createHash('sha256').update(mac + os.homedir(), 'utf8').digest('hex')); + } else { + resolve(generateGuid()); + } + } catch (err) { + resolve(generateGuid()); // fallback + } + }); +} + +// Return 'true' if the active editor window has a .sql file, false otherwise +export function isEditingSqlFile(languageId: string): boolean { + let sqlFile = false; + let editor = getActiveTextEditor(); + if (editor) { + if (editor.document.languageId === languageId) { + sqlFile = true; + } + } + return sqlFile; +} + +// Return the active text editor if there's one +export function getActiveTextEditor(): vscode.TextEditor { + let editor = undefined; + if (vscode.window && vscode.window.activeTextEditor) { + editor = vscode.window.activeTextEditor; + } + return editor; +} + +// Retrieve the URI for the currently open file if there is one; otherwise return the empty string +export function getActiveTextEditorUri(): string { + if (typeof vscode.window.activeTextEditor !== 'undefined' && + typeof vscode.window.activeTextEditor.document !== 'undefined') { + return vscode.window.activeTextEditor.document.uri.toString(); + } + return ''; +} + +// Helper to log messages to output channel +export function logToOutputChannel(msg: any, outputChannelName: string): void { + let outputChannel = vscode.window.createOutputChannel(outputChannelName); + outputChannel.show(); + if (msg instanceof Array) { + msg.forEach(element => { + outputChannel.appendLine(element.toString()); + }); + } else { + outputChannel.appendLine(msg.toString()); + } +} + +// Helper to log debug messages +export function logDebug(msg: any, extensionConfigSectionName: string): void { + let config = vscode.workspace.getConfiguration(extensionConfigSectionName); + let logDebugInfo = config[Constants.configLogDebugInfo]; + if (logDebugInfo === true) { + let currentTime = new Date().toLocaleTimeString(); + let outputMsg = '[' + currentTime + ']: ' + msg ? msg.toString() : ''; + console.log(outputMsg); + } +} + +// Helper to show an info message +export function showInfoMsg(msg: string, extensionName: string): void { + vscode.window.showInformationMessage(extensionName + ': ' + msg ); +} + +// Helper to show an warn message +export function showWarnMsg(msg: string, extensionName: string): void { + vscode.window.showWarningMessage(extensionName + ': ' + msg ); +} + +// Helper to show an error message +export function showErrorMsg(msg: string, extensionName: string): void { + vscode.window.showErrorMessage(extensionName + ': ' + msg ); +} + +export function isEmpty(str: any): boolean { + return (!str || '' === str); +} + +export function isNotEmpty(str: any): boolean { + return (str && '' !== str); +} + +/** + * Format a string. Behaves like C#'s string.Format() function. + */ +export function formatString(str: string, ...args: any[]): string { + // This is based on code originally from https://github.com/Microsoft/vscode/blob/master/src/vs/nls.js + // License: https://github.com/Microsoft/vscode/blob/master/LICENSE.txt + let result: string; + if (args.length === 0) { + result = str; + } else { + result = str.replace(/\{(\d+)\}/g, (match, rest) => { + let index = rest[0]; + return typeof args[index] !== 'undefined' ? args[index] : match; + }); + } + return result; +} + + +/** + * Check if a file exists on disk + */ +export function isFileExisting(filePath: string): boolean { + try { + fs.statSync(filePath); + return true; + } catch (err) { + return false; + } + } + +/** + * Takes a string in the format of HH:MM:SS.MS and returns a number representing the time in + * miliseconds + * @param value The string to convert to milliseconds + * @return False is returned if the string is an invalid format, + * the number of milliseconds in the time string is returned otherwise. + */ +export function parseTimeString(value: string): number | boolean { + if (!value) { + return false; + } + let tempVal = value.split('.'); + + if (tempVal.length !== 2) { + return false; + } + + let ms = parseInt(tempVal[1].substring(0, 3), 10); + tempVal = tempVal[0].split(':'); + + if (tempVal.length !== 3) { + return false; + } + + let h = parseInt(tempVal[0], 10); + let m = parseInt(tempVal[1], 10); + let s = parseInt(tempVal[2], 10); + + return ms + (h * msInH) + (m * msInM) + (s * msInS); +} + +/** + * Takes a number of milliseconds and converts it to a string like HH:MM:SS.fff + * @param value The number of milliseconds to convert to a timespan string + * @returns A properly formatted timespan string. + */ +export function parseNumAsTimeString(value: number): string { + let tempVal = value; + let h = Math.floor(tempVal / msInH); + tempVal %= msInH; + let m = Math.floor(tempVal / msInM); + tempVal %= msInM; + let s = Math.floor(tempVal / msInS); + tempVal %= msInS; + + let hs = h < 10 ? '0' + h : '' + h; + let ms = m < 10 ? '0' + m : '' + m; + let ss = s < 10 ? '0' + s : '' + s; + let mss = tempVal < 10 ? '00' + tempVal : tempVal < 100 ? '0' + tempVal : '' + tempVal; + + let rs = hs + ':' + ms + ':' + ss; + + return tempVal > 0 ? rs + '.' + mss : rs; +} + + +// The function is a duplicate of carbon\src\paths.js. IT would be better to import path.js but it doesn't +// work for now because the extension is running in different process. +export function getAppDataPath() { + var platform = process.platform; + switch (platform) { + case 'win32': return process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming'); + case 'darwin': return path.join(os.homedir(), 'Library', 'Application Support'); + case 'linux': return process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); + default: throw new Error('Platform not supported'); + } +} +export function getDefaultLogLocation() { + var platform = process.platform; + let rootFolderName: string = '.sqlops'; + if (platform === 'win32') { + rootFolderName = 'sqlops'; + } + + return path.join(getAppDataPath(), rootFolderName); +} diff --git a/extensions-modules/src/tsconfig.json b/extensions-modules/src/tsconfig.json new file mode 100644 index 0000000000..af7eb19533 --- /dev/null +++ b/extensions-modules/src/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "../lib", + "lib": [ + "es6", "es2015.promise" + ], + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "declaration": true + }, + "exclude": [ + "node_modules" + ] +} diff --git a/extensions-modules/src/typings/ref.d.ts b/extensions-modules/src/typings/ref.d.ts new file mode 100644 index 0000000000..e0685da577 --- /dev/null +++ b/extensions-modules/src/typings/ref.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions-modules/src/typings/tmp.d.ts b/extensions-modules/src/typings/tmp.d.ts new file mode 100644 index 0000000000..ce23febc52 --- /dev/null +++ b/extensions-modules/src/typings/tmp.d.ts @@ -0,0 +1,47 @@ +// Type definitions for tmp v0.0.28 +// Project: https://www.npmjs.com/package/tmp +// Definitions by: Jared Klopper + +declare module "tmp" { + + module tmp { + interface Options extends SimpleOptions { + mode?: number; + } + + interface SimpleOptions { + prefix?: string; + postfix?: string; + template?: string; + dir?: string; + tries?: number; + keep?: boolean; + unsafeCleanup?: boolean; + } + + interface SynchronousResult { + name: string; + fd: number; + removeCallback: () => void; + } + + function file(callback: (err: any, path: string, fd: number, cleanupCallback: () => void) => void): void; + function file(config: Options, callback?: (err: any, path: string, fd: number, cleanupCallback: () => void) => void): void; + + function fileSync(config?: Options): SynchronousResult; + + function dir(callback: (err: any, path: string, cleanupCallback: () => void) => void): void; + function dir(config: Options, callback?: (err: any, path: string, cleanupCallback: () => void) => void): void; + + function dirSync(config?: Options): SynchronousResult; + + function tmpName(callback: (err: any, path: string) => void): void; + function tmpName(config: SimpleOptions, callback?: (err: any, path: string) => void): void; + + function tmpNameSync(config?: SimpleOptions): string; + + function setGracefulCleanup(): void; + } + + export = tmp; +} diff --git a/extensions-modules/src/typings/vscode-extension-telemetry.d.ts b/extensions-modules/src/typings/vscode-extension-telemetry.d.ts new file mode 100644 index 0000000000..f6177ef27a --- /dev/null +++ b/extensions-modules/src/typings/vscode-extension-telemetry.d.ts @@ -0,0 +1,6 @@ +declare module 'vscode-extension-telemetry' { + export default class TelemetryReporter { + constructor(extensionId: string, extensionVersion: string, key: string); + sendTelemetryEvent(eventName: string, properties?: { [key: string]: string }, measures?: { [key: string]: number }): void; + } +} \ No newline at end of file diff --git a/extensions-modules/src/utils/escapeException.ts b/extensions-modules/src/utils/escapeException.ts new file mode 100644 index 0000000000..e405084cbe --- /dev/null +++ b/extensions-modules/src/utils/escapeException.ts @@ -0,0 +1,3 @@ +'use strict'; + +export default require('error-ex')('EscapeException'); diff --git a/extensions-modules/src/utils/validationException.ts b/extensions-modules/src/utils/validationException.ts new file mode 100644 index 0000000000..2e545798ce --- /dev/null +++ b/extensions-modules/src/utils/validationException.ts @@ -0,0 +1,3 @@ +'use strict'; + +export default require('error-ex')('ValidationException'); diff --git a/extensions-modules/src/views/statusView.ts b/extensions-modules/src/views/statusView.ts new file mode 100644 index 0000000000..8f9856ebb2 --- /dev/null +++ b/extensions-modules/src/views/statusView.ts @@ -0,0 +1,142 @@ +import vscode = require('vscode'); +import * as Utils from '../models/utils'; + +// Status bar element for each file in the editor +class FileStatusBar { + // Item for the connection status + public statusConnection: vscode.StatusBarItem; + + // Item for the query status + public statusQuery: vscode.StatusBarItem; + + // Item for language service status + public statusLanguageService: vscode.StatusBarItem; + + // Timer used for displaying a progress indicator on queries + public progressTimerId: NodeJS.Timer; + + public currentLanguageServiceStatus: string; +} + +export default class StatusView implements vscode.Disposable { + private _statusBars: { [fileUri: string]: FileStatusBar }; + private _lastShownStatusBar: FileStatusBar; + + constructor() { + this._statusBars = {}; + vscode.window.onDidChangeActiveTextEditor((params) => this.onDidChangeActiveTextEditor(params)); + vscode.workspace.onDidCloseTextDocument((params) => this.onDidCloseTextDocument(params)); + } + + dispose(): void { + for (let bar in this._statusBars) { + if (this._statusBars.hasOwnProperty(bar)) { + this._statusBars[bar].statusConnection.dispose(); + this._statusBars[bar].statusQuery.dispose(); + this._statusBars[bar].statusLanguageService.dispose(); + clearInterval(this._statusBars[bar].progressTimerId); + delete this._statusBars[bar]; + } + } + } + + // Create status bar item if needed + private createStatusBar(fileUri: string): void { + let bar = new FileStatusBar(); + bar.statusConnection = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right); + bar.statusQuery = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right); + bar.statusLanguageService = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right); + this._statusBars[fileUri] = bar; + } + + private destroyStatusBar(fileUri: string): void { + let bar = this._statusBars[fileUri]; + if (bar) { + if (bar.statusConnection) { + bar.statusConnection.dispose(); + } + if (bar.statusQuery) { + bar.statusQuery.dispose(); + } + if (bar.statusLanguageService) { + bar.statusLanguageService.dispose(); + } + if (bar.progressTimerId) { + clearInterval(bar.progressTimerId); + } + + delete this._statusBars[fileUri]; + } + } + + private getStatusBar(fileUri: string): FileStatusBar { + if (!(fileUri in this._statusBars)) { + // Create it if it does not exist + this.createStatusBar(fileUri); + } + + let bar = this._statusBars[fileUri]; + if (bar.progressTimerId) { + clearInterval(bar.progressTimerId); + } + return bar; + } + + public languageServiceStatusChanged(fileUri: string, status: string): void { + let bar = this.getStatusBar(fileUri); + bar.currentLanguageServiceStatus = status; + this.updateStatusMessage(status, + () => { return bar.currentLanguageServiceStatus; }, (message) => { + bar.statusLanguageService.text = message; + this.showStatusBarItem(fileUri, bar.statusLanguageService); + }); + } + + public updateStatusMessage( + newStatus: string, + getCurrentStatus: () => string, + updateMessage: (message: string) => void): void { + } + + private hideLastShownStatusBar(): void { + if (typeof this._lastShownStatusBar !== 'undefined') { + this._lastShownStatusBar.statusConnection.hide(); + this._lastShownStatusBar.statusQuery.hide(); + this._lastShownStatusBar.statusLanguageService.hide(); + } + } + + private onDidChangeActiveTextEditor(editor: vscode.TextEditor): void { + // Hide the most recently shown status bar + this.hideLastShownStatusBar(); + + // Change the status bar to match the open file + if (typeof editor !== 'undefined') { + const fileUri = editor.document.uri.toString(); + const bar = this._statusBars[fileUri]; + if (bar) { + this.showStatusBarItem(fileUri, bar.statusConnection); + this.showStatusBarItem(fileUri, bar.statusLanguageService); + } + } + } + + private onDidCloseTextDocument(doc: vscode.TextDocument): void { + // Remove the status bar associated with the document + this.destroyStatusBar(doc.uri.toString()); + } + + private showStatusBarItem(fileUri: string, statusBarItem: vscode.StatusBarItem): void { + let currentOpenFile = Utils.getActiveTextEditorUri(); + + // Only show the status bar if it matches the currently open file and is not empty + if (fileUri === currentOpenFile && !Utils.isEmpty(statusBarItem.text) ) { + statusBarItem.show(); + if (fileUri in this._statusBars) { + this._lastShownStatusBar = this._statusBars[fileUri]; + } + } else { + statusBarItem.hide(); + } + } +} diff --git a/extensions/account-provider-azure/.gitignore b/extensions/account-provider-azure/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extensions/account-provider-azure/package.json b/extensions/account-provider-azure/package.json new file mode 100644 index 0000000000..ad3d0b61d4 --- /dev/null +++ b/extensions/account-provider-azure/package.json @@ -0,0 +1,34 @@ +{ + "name": "account-provider-azure", + "version": "0.0.1", + "publisher": "Microsoft", + "engines": { "vscode": "*" }, + "main": "./out/main", + "activationEvents": [ "*" ], + "scripts": { + "compile": "gulp compile-extension:account-provider-azure" + }, + "dependencies": { + "adal-node": "^0.1.16", + "decompress": "^4.0.0", + "extensions-modules": "file:../../extensions-modules", + "fs-extra-promise": "^0.3.1", + "opener": "1.4.2", + "request": "2.63.0", + "urijs": "^1.18.12", + "vscode-extension-telemetry": "^0.0.5", + "vscode-nls": "2.0.2" + }, + "devDependencies": { + "@types/node": "^8.0.24" + }, + "contributes": { + "commands": [ + { + "command": "extension.clearTokenCache", + "title": "%extension.clearTokenCache%", + "category": "Azure Accounts" + } + ] + } +} diff --git a/extensions/account-provider-azure/package.nls.json b/extensions/account-provider-azure/package.nls.json new file mode 100644 index 0000000000..de41c17e94 --- /dev/null +++ b/extensions/account-provider-azure/package.nls.json @@ -0,0 +1,3 @@ +{ + "extension.clearTokenCache": "Clear Azure Account Token Cache" +} \ No newline at end of file diff --git a/extensions/account-provider-azure/src/account-provider/azureAccountProvider.ts b/extensions/account-provider-azure/src/account-provider/azureAccountProvider.ts new file mode 100644 index 0000000000..ecbef36390 --- /dev/null +++ b/extensions/account-provider-azure/src/account-provider/azureAccountProvider.ts @@ -0,0 +1,371 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as fs from 'fs'; +import * as path from 'path'; +import * as adal from 'adal-node'; +import * as data from 'data'; +import * as request from 'request'; +import * as nls from 'vscode-nls'; +import { + Arguments, + AzureAccount, + AzureAccountProviderMetadata, + AzureAccountSecurityTokenCollection, + Tenant +} from './interfaces'; + +const localize = nls.loadMessageBundle(); + +export class AzureAccountProvider implements data.AccountProvider { + private static WorkSchoolAccountLogo: data.AccountContextualLogo = { + light: AzureAccountProvider.loadIcon('work_school_account.svg'), + dark: AzureAccountProvider.loadIcon('work_school_account_inverse.svg') + }; + private static MicrosoftAccountLogo: data.AccountContextualLogo = { + light: AzureAccountProvider.loadIcon('microsoft_account.svg'), + dark: AzureAccountProvider.loadIcon('microsoft_account.svg') + }; + + private _args: Arguments; + private _isInitialized: boolean; + private _tokenCache: adal.TokenCache; + + public metadata: AzureAccountProviderMetadata; + + constructor(metadata: AzureAccountProviderMetadata, tokenCache: adal.TokenCache) { + this.metadata = metadata; + this._args = { + host: metadata.settings.host, + clientId: metadata.settings.clientId + }; + this._isInitialized = false; + this._tokenCache = tokenCache; + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + public clear(accountKey: data.AccountKey): Thenable { + throw new Error('Not implemented'); + } + + public clearTokenCache(): Thenable { + return this._tokenCache.clear(); + } + + public getSecurityToken(account: AzureAccount): Thenable { + let self = this; + return this.doIfInitialized(() => self.getAccessTokens(account)); + } + + public initialize(restoredAccounts: data.Account[]): Thenable { + let self = this; + + return new Promise(resolve => { + // Rehydrate the accounts + restoredAccounts.forEach((account) => { + // Set the icon for the account + account.displayInfo.contextualLogo = account.properties.isMsAccount + ? AzureAccountProvider.MicrosoftAccountLogo + : AzureAccountProvider.WorkSchoolAccountLogo; + + // TODO: Set stale status based on whether we can authenticate or not + }); + + self._isInitialized = true; + + resolve(restoredAccounts); + }); + } + + public prompt(): Thenable { + let self = this; + return this.doIfInitialized(() => self.signIn()); + } + + public refresh(account: AzureAccount): Thenable { + let self = this; + return this.doIfInitialized(() => self.signIn(account.properties.isMsAccount, account.key.accountId)); + } + + // PRIVATE METHODS ///////////////////////////////////////////////////// + private static loadIcon(iconName: string) { + let filePath = path.join(__dirname, 'media', iconName); + try { + return 'image/svg+xml,' + fs.readFileSync(filePath); + } catch(e) { + return ''; + } + } + + private authenticate(tenantId: string, msa: boolean, userId: string, silent: boolean): Thenable { + let authorityUrl = `${this.metadata.settings.host}/${tenantId}`; + // TODO: Rewrite using urijs + let authorizeUrl = `${authorityUrl}/oauth2/authorize` + + `?client_id=${this.metadata.settings.clientId}` // Specify the client ID + + `&response_type=code` // Request the authorization code grant flow + + (userId ? (msa ? '&domain_hint=live.com' : '&msafed=0') : '') // Optimize prompt given existing MSA or org ID + + ((!userId && !silent) ? '&prompt=login' : '') // Require login if not silent + + (userId ? `&login_hint=${encodeURIComponent(userId)}` : '') // Show login hint if we have an existing user ID + + (this.metadata.settings.siteId ? `&site_id=${this.metadata.settings.siteId}` : '') // Site ID to use as brand on the prompt + + '&display=popup' // Causes a popup version of the UI to be shown + + `&resource=${encodeURIComponent(this.metadata.settings.signInResourceId)}` // Specify the resource for which an access token should be retrieved + + `&redirect_uri=${encodeURIComponent(this.metadata.settings.redirectUri)}`; // TODO: add locale parameter like in VSAccountProvider.TryAppendLocalParameter + + // Get the authorization code. If this is the initial authentication (the user is unknown), + // do not silently prompt. If this is a subsequent authentication for an additional tenant, + // the browser cookie cache will be used to authenticate without prompting, so run the + // browser silently. + return data.accounts.performOAuthAuthorization(authorizeUrl, silent) + .then((code: string) => { + return new Promise((resolve, reject) => { + let context = new adal.AuthenticationContext(authorityUrl, null, this._tokenCache); + context.acquireTokenWithAuthorizationCode( + code, + this.metadata.settings.redirectUri, + this.metadata.settings.signInResourceId, + this.metadata.settings.clientId, + null, + (error, response) => { + if (error) { + reject(error); + } else { + resolve( response); + } + } + ); + }); + }); + } + + private doIfInitialized(op: () => Thenable): Thenable { + return this._isInitialized + ? op() + : Promise.reject(localize('accountProviderNotInitialized', 'Account provider not initialized, cannot perform action')); + } + + private getAccessTokens(account: AzureAccount): Thenable { + let self = this; + + // TODO: Could we add some better typing here? + let accessTokenPromises: Thenable[] = []; + let tokenCollection: AzureAccountSecurityTokenCollection = {}; + for (let tenant of account.properties.tenants) { + let promise = new Promise((resolve, reject) => { + // TODO: use urijs to generate this URI + let authorityUrl = `${self.metadata.settings.host}/${tenant.id}`; + let context = new adal.AuthenticationContext(authorityUrl, null, self._tokenCache); + + // TODO: This is where we should mark the account as stale + context.acquireToken( + self.metadata.settings.armResource.id, + tenant.userId, + self.metadata.settings.clientId, + (error: adal.ErrorResponse, response: adal.SuccessResponse) => { + // Handle errors first + if (error) { + reject(error); + return; + } + + // Generate a token object and add it to the collection + tokenCollection[tenant.id] = { + expiresOn: response.expiresOn, + resource: response.resource, + token: response.accessToken, + tokenType: response.tokenType + }; + resolve(); + } + ); + }); + accessTokenPromises.push(promise); + } + + // Wait until all the tokens have been acquired then return the collection + return Promise.all(accessTokenPromises) + .then(() => tokenCollection); + } + + private getTenantDisplayName(msa: boolean, tenantId: string, userId: string): Thenable { + let self = this; + if(msa) { + return Promise.resolve(localize('microsoftAccountDisplayName', 'Microsoft Account')); + } + + return new Promise((resolve, reject) => { + // Get an access token to the AAD graph resource + // TODO: Use urijs to generate this URI + let authorityUrl = `${self.metadata.settings.host}/${tenantId}`; + let context = new adal.AuthenticationContext(authorityUrl, null, self._tokenCache); + context.acquireToken(self.metadata.settings.graphResource.id, userId, self.metadata.settings.clientId, (error, response) => { + if (error) { + reject(error); + return; + } + + request.get( + `${self.metadata.settings.graphResource.endpoint}/${tenantId}/tenantDetails?api-version=2013-04-05`, + { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${(response).accessToken}` + }, + json: true + }, + (graphError, graphResponse, body: {error: any; value: any[];}) => { + if (graphError || body['odata.error']) { + reject(graphError || body['odata.error']); + } else if (body.value.length && body.value[0].displayName) { + resolve(body.value[0].displayName); + } else { + resolve(localize('azureWorkAccountDisplayName', 'Work or school account')); + } + } + ); + }); + }); + } + + private getTenants(msa: boolean, userId: string, tenantIds: string[], homeTenant: string): Thenable { + let self = this; + + // Lookup each tenant ID that was provided + let getTenantPromises: Thenable[] = []; + for (let tenantId of tenantIds) { + let promise = this.authenticate(tenantId, msa, userId, false) + .then((response) => self.getTenantDisplayName(msa, response.tenantId, response.userId)) + .then((displayName) => { + return { + displayName: displayName, + id: tenantId, + userId: userId + }; + }); + getTenantPromises.push(promise); + } + + return Promise.all(getTenantPromises) + .then((tenants) => { + // Resort the tenants to make sure that the 'home' tenant is the first in the list + let homeTenantIndex = tenants.findIndex((tenant) => tenant.id === homeTenant); + if (homeTenantIndex >= 0) { + let homeTenant = tenants.splice(homeTenantIndex, 1); + tenants.unshift(homeTenant[0]); + } + return tenants; + }); + } + + private getTenantIds(userId: string): Thenable { + return new Promise((resolve, reject) => { + // Get an access token to the ARM resource + // TODO: Build this URL with urijs + let authorityUrl = `${this.metadata.settings.host}/common`; + let context = new adal.AuthenticationContext(authorityUrl, null, this._tokenCache); + context.acquireToken(this.metadata.settings.armResource.id, userId, this.metadata.settings.clientId, (error, response) => { + if (error) { + reject(error); + return; + } + + if (!!this.metadata.settings.adTenants && this.metadata.settings.adTenants.length > 0) { + resolve(this.metadata.settings.adTenants); + } else { + request.get( + `${this.metadata.settings.armResource.endpoint}/tenants?api-version=2015-01-01`, + { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${(response).accessToken}` + }, + json: true + }, + (armError, armResponse, body: {error: any; value: any[];}) => { + if (armError || body.error) { + reject(armError || body.error); + } else { + resolve(body.value.map(item => item.tenantId)); + } + } + ); + } + }); + }); + } + + private signIn(msa?: boolean, userId?: string): Thenable { + let self = this; + + // Initial authentication is via the common/discovery tenant + return this.authenticate('common', msa, userId, false) + .then((response: adal.TokenResponse) => { + let identityProvider = response.identityProvider; + if (identityProvider) { + identityProvider = identityProvider.toLowerCase(); + } + + // Determine if this is a microsoft account + msa = identityProvider && ( + identityProvider.indexOf('live.com') !== -1 || + identityProvider.indexOf('live-int.com') !== -1 || + identityProvider.indexOf('f8cdef31-a31e-4b4a-93e4-5f571e91255a') !== -1 || + identityProvider.indexOf('ea8a4392-515e-481f-879e-6571ff2a8a36') !== -1); + + // Get the user information + userId = response.userId; + let displayName = (response.givenName && response.familyName) + ? `${response.givenName} ${response.familyName}` + : userId; + + // Get all the additional tenants + return this.getTenantIds(userId) + .then(tenantIds => self.getTenants(msa, userId, tenantIds, response.tenantId)) + .then(tenants => { + return { + key: { + providerId: self.metadata.id, + accountId: userId + }, + name: userId, + displayInfo: { + contextualLogo: msa + ? AzureAccountProvider.MicrosoftAccountLogo + : AzureAccountProvider.WorkSchoolAccountLogo, + contextualDisplayName: tenants[0].displayName, + displayName: displayName + }, + properties: { + isMsAccount: msa, + tenants: tenants + }, + isStale: false + }; + }); + }); + } +} + +// ADAL MONKEY PATCH /////////////////////////////////////////////////////// +// Monkey patch the ADAL TokenRequest class to fix the fact that when you request a token from an +// authorization code, it doesn't update the cache +import * as TokenRequest from 'adal-node/lib/token-request'; +let getTokenWithAuthorizationCodeOriginal = TokenRequest.prototype.getTokenWithAuthorizationCode; +TokenRequest.prototype.getTokenWithAuthorizationCode = function ( + authorizationCode: string, + clientSecret: string, + callback: adal.AcquireTokenCallback +) { + this._cacheDriver = this._createCacheDriver(); + getTokenWithAuthorizationCodeOriginal.call(this, authorizationCode, clientSecret, (error: Error, response: adal.ErrorResponse|adal.TokenResponse) => { + if (error) { + callback(error, response); + } else { + this._userId = ( response).userId; + this._cacheDriver.add(response, () => callback(null, response)); + } + }); +}; \ No newline at end of file diff --git a/extensions/account-provider-azure/src/account-provider/azureAccountProviderService.ts b/extensions/account-provider-azure/src/account-provider/azureAccountProviderService.ts new file mode 100644 index 0000000000..8526f5ee6d --- /dev/null +++ b/extensions/account-provider-azure/src/account-provider/azureAccountProviderService.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as constants from '../constants'; +import * as data from 'data'; +import * as events from 'events'; +import * as nls from 'vscode-nls'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import CredentialServiceTokenCache from './tokenCache'; +import providerSettings from './providerSettings'; +import { AzureAccountProvider } from './azureAccountProvider'; +import { AzureAccountProviderMetadata } from './interfaces'; + +let localize = nls.loadMessageBundle(); + +export class AzureAccountProviderService implements vscode.Disposable { + // CONSTANTS /////////////////////////////////////////////////////////////// + private static CommandClearTokenCache = 'extension.clearTokenCache'; + private static CredentialNamespace = 'azureAccountProviderCredentials'; + + // MEMBER VARIABLES //////////////////////////////////////////////////////// + private _accountProviders: { [accountProviderId: string]: AzureAccountProvider }; + private _event: events.EventEmitter; + + constructor(private _context: vscode.ExtensionContext, private _userStoragePath: string) { + this._accountProviders = {}; + this._event = new events.EventEmitter(); + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + public activate(): Thenable { + let self = this; + + // Register commands + this._context.subscriptions.push(vscode.commands.registerCommand( + AzureAccountProviderService.CommandClearTokenCache, + () => { self._event.emit(AzureAccountProviderService.CommandClearTokenCache); } + )); + this._event.on(AzureAccountProviderService.CommandClearTokenCache, () => { self.onClearTokenCache(); }); + + // Create the token caches + // 1) Get a credential provider + // 2) Iterate over the enabled providers + // 2a) Create a token cache for provider + // 2b) Create the provider from the provider's settings + // 2c) Register the provider with the account service + return data.credentials.getProvider(AzureAccountProviderService.CredentialNamespace) + .then(credProvider => { + providerSettings.forEach(provider => { + let tokenCacheKey = `azureTokenCache-${provider.metadata.id}`; + let tokenCachePath = path.join(self._userStoragePath, tokenCacheKey); + let tokenCache = new CredentialServiceTokenCache(credProvider, tokenCacheKey, tokenCachePath); + let accountProvider = new AzureAccountProvider(provider.metadata, tokenCache); + self._accountProviders[provider.metadata.id] = accountProvider; + data.accounts.registerAccountProvider(provider.metadata, accountProvider); + }); + }) + .then(() => { return true; }); + } + + public dispose() { } + + // PRIVATE HELPERS ///////////////////////////////////////////////////// + private onClearTokenCache(): Thenable { + let self = this; + + let promises: Thenable[] = providerSettings.map(provider => { + return self._accountProviders[provider.metadata.id].clearTokenCache(); + }); + + return Promise.all(promises) + .then( + () => { + let message = localize('clearTokenCacheSuccess', 'Token cache successfully cleared'); + vscode.window.showInformationMessage(`${constants.extensionName}: ${message}`); + }, + err => { + let message = localize('clearTokenCacheFailure', 'Failed to clear token cache'); + vscode.window.showErrorMessage(`${constants.extensionName}: ${message}: ${err}`); + }); + } +} diff --git a/extensions/account-provider-azure/src/account-provider/interfaces.ts b/extensions/account-provider-azure/src/account-provider/interfaces.ts new file mode 100644 index 0000000000..34433a4022 --- /dev/null +++ b/extensions/account-provider-azure/src/account-provider/interfaces.ts @@ -0,0 +1,167 @@ +'use strict'; + +import * as data from 'data'; + +/** + * Represents a tenant (an Azure Active Directory instance) to which a user has access + */ +export interface Tenant { + /** + * Globally unique identifier of the tenant + */ + id: string; + + /** + * Display name of the tenant + */ + displayName: string; + + /** + * Identifier of the user in the tenant + */ + userId: string; +} + +/** + * Represents a resource exposed by an Azure Active Directory + */ +export interface Resource { + /** + * Identifier of the resource + */ + id: string; + + /** + * Endpoint url used to access the resource + */ + endpoint: string; +} + +/** + * Represents the arguments that identify an instantiation of the AAD account provider + */ +export interface Arguments { + /** + * Host of the authority + */ + host: string; + + /** + * Identifier of the client application + */ + clientId: string; +} + +/** + * Represents settings for an AAD account provider + */ +export interface Settings { + /** + * Host of the authority + */ + host?: string; + + /** + * Identifier of the client application + */ + clientId?: string; + + /** + * Identifier of the resource to request when signing in + */ + signInResourceId?: string; + + /** + * Information that describes the AAD graph resource + */ + graphResource?: Resource; + + /** + * Information that describes the Azure resource management resource + */ + armResource?: Resource; + + /** + * A list of tenant IDs to authenticate against. If defined, then these IDs will be used + * instead of querying the tenants endpoint of the armResource + */ + adTenants?: string[]; + + // AuthorizationCodeGrantFlowSettings ////////////////////////////////// + + /** + * An optional site ID that brands the interactive aspect of sign in + */ + siteId?: string; + + /** + * Redirect URI that is used to signify the end of the interactive aspect of sign it + */ + redirectUri?: string; +} + +/** + * + */ +export interface AzureAccountProviderMetadata extends data.AccountProviderMetadata { + /** + * Azure specific account provider settings. + */ + settings: Settings; +} + +/** + * Properties specific to an Azure account + */ +export interface AzureAccountProperties { + /** + * Whether or not the account is a Microsoft account + */ + isMsAccount: boolean; + + /** + * A list of tenants (aka directories) that the account belongs to + */ + tenants: Tenant[]; +} + +/** + * Override of the Account type to enforce properties that are AzureAccountProperties + */ +export interface AzureAccount extends data.Account { + /** + * AzureAccountProperties specifically used for Azure accounts + */ + properties: AzureAccountProperties; +} + +/** + * Token returned from a request for an access token + */ +export interface AzureAccountSecurityToken { + /** + * Access token, itself + */ + token: string; + + /** + * Date that the token expires on + */ + expiresOn: Date; + + /** + * Name of the resource the token is good for (ie, management.core.windows.net) + */ + resource: string; + + /** + * Type of the token (pretty much always 'Bearer') + */ + tokenType: string; +} + +/** + * Azure account security token maps a tenant ID to the information returned from a request to get + * an access token. The list of tenants correspond to the tenants in the account properties. + */ +export type AzureAccountSecurityTokenCollection = {[tenantId: string]: AzureAccountSecurityToken}; diff --git a/extensions/account-provider-azure/src/account-provider/media/microsoft_account.svg b/extensions/account-provider-azure/src/account-provider/media/microsoft_account.svg new file mode 100644 index 0000000000..cf6cfdbcdc --- /dev/null +++ b/extensions/account-provider-azure/src/account-provider/media/microsoft_account.svg @@ -0,0 +1 @@ +microsoft_account_16x16 \ No newline at end of file diff --git a/extensions/account-provider-azure/src/account-provider/media/work_school_account.svg b/extensions/account-provider-azure/src/account-provider/media/work_school_account.svg new file mode 100644 index 0000000000..2e1f6e72b2 --- /dev/null +++ b/extensions/account-provider-azure/src/account-provider/media/work_school_account.svg @@ -0,0 +1 @@ +other_account_16x16 \ No newline at end of file diff --git a/extensions/account-provider-azure/src/account-provider/media/work_school_account_inverse.svg b/extensions/account-provider-azure/src/account-provider/media/work_school_account_inverse.svg new file mode 100644 index 0000000000..a31370b089 --- /dev/null +++ b/extensions/account-provider-azure/src/account-provider/media/work_school_account_inverse.svg @@ -0,0 +1 @@ +other_account_inverse_16x16 \ No newline at end of file diff --git a/extensions/account-provider-azure/src/account-provider/providerSettings.ts b/extensions/account-provider-azure/src/account-provider/providerSettings.ts new file mode 100644 index 0000000000..ef16e7594c --- /dev/null +++ b/extensions/account-provider-azure/src/account-provider/providerSettings.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import * as nls from 'vscode-nls'; +import { Settings } from './interfaces'; + +const localize = nls.loadMessageBundle(); + +const publicAzureSettings = { + configKey: 'accounts.azure.enablePublicCloud', + metadata: { + displayName: localize('publicCloudDisplayName', 'Azure'), + id: 'azurePublicCloud', + settings: { + host: 'https://login.microsoftonline.com', + clientId: 'TBD', + signInResourceId: 'https://management.core.windows.net/', + graphResource: { + id: 'https://graph.windows.net/', + endpoint: 'https://graph.windows.net' + }, + armResource: { + id: 'https://management.core.windows.net/', + endpoint: 'https://management.azure.com' + }, + redirectUri: 'http://localhost/redirect' + } + } +}; + +const usGovAzureSettings = { + configKey: 'accounts.azure.enableUsGovCloud', + metadata: { + displayName: localize('usGovCloudDisplayName', 'Azure (US Government)'), + id: 'usGovAzureCloud', + settings: { + host: 'https://login.microsoftonline.com/', + clientId: 'TBD', + signInResourceId: 'https://management.core.usgovcloudapi.net/', + graphResource: { + id: 'https://graph.usgovcloudapi.net/', + endpoint: 'https://graph.usgovcloudapi.net' + }, + armResource: { + id: 'https://management.core.usgovcloudapi.net/', + endpoint: 'https://management.usgovcloudapi.net' + }, + redirectUri: 'http://localhost/redirect' + } + } +}; + +const chinaAzureSettings = { + configKey: 'accounts.azure.enableChinaCloud', + metadata: { + displayName: localize('chinaCloudDisplayName', 'Azure (China)'), + id: 'chinaAzureCloud', + settings: { + host: 'https://login.chinacloudapi.cn/', + clientId: 'TBD', + signInResourceId: 'https://management.core.chinacloudapi.cn/', + graphResource: { + id: 'https://graph.chinacloudapi.cn/', + endpoint: 'https://graph.chinacloudapi.cn' + }, + armResource: { + id: 'https://management.core.chinacloudapi.cn/', + endpoint: 'https://managemement.chinacloudapi.net' + }, + redirectUri: 'http://localhost/redirect' + } + } +}; + +const germanyAzureSettings = { + configKey: 'accounts.azure.enableGermanyCloud', + metadata: { + displayName: localize('germanyCloud', 'Azure (Germany)'), + id: 'germanyAzureCloud', + settings: { + host: 'https://login.microsoftazure.de/', + clientId: 'TBD', + signInResourceId: 'https://management.core.cloudapi.de/', + graphResource: { + id: 'https://graph.cloudapi.de/', + endpoint: 'https://graph.cloudapi.de' + }, + armResource: { + id: 'https://management.core.cloudapi.de/', + endpoint: 'https://management.microsoftazure.de' + }, + redirectUri: 'http://localhost/redirect' + } + } +}; + +export default [publicAzureSettings/*, chinaAzureSettings, germanyAzureSettings, usGovAzureSettings*/]; diff --git a/extensions/account-provider-azure/src/account-provider/tokenCache.ts b/extensions/account-provider-azure/src/account-provider/tokenCache.ts new file mode 100644 index 0000000000..4dee000ee2 --- /dev/null +++ b/extensions/account-provider-azure/src/account-provider/tokenCache.ts @@ -0,0 +1,244 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as adal from 'adal-node'; +import * as data from 'data'; +import * as crypto from 'crypto'; +import * as fs from 'fs'; + +export default class TokenCache implements adal.TokenCache { + private static CipherAlgorithm = 'aes256'; + private static CipherAlgorithmIvLength = 16; + private static CipherKeyLength = 32; + private static FsOptions = { encoding: 'ascii' }; + + private _activeOperation: Thenable; + + constructor( + private _credentialProvider: data.CredentialProvider, + private _credentialServiceKey: string, + private _cacheSerializationPath: string + ) { + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + public add(entries: adal.TokenCacheEntry[], callback: (error?: Error) => void): void { + let self = this; + + this.doOperation(() => { + return self.readCache() + .then(cache => self.addToCache(cache, entries)) + .then(updatedCache => self.writeCache(updatedCache)) + .then( + () => callback(null), + (err) => callback(err) + ); + }); + } + + public clear(): Thenable { + let self = this; + + // 1) Delete encrypted serialization file + // If we got an 'ENOENT' response, the file doesn't exist, which is fine + // 3) Delete the encryption key + return new Promise((resolve, reject) => { + fs.unlink(self._cacheSerializationPath, err => { + if (err && err.code !== 'ENOENT') { + reject(err); + } else { + resolve(); + } + }); + }) + .then(() => { return self._credentialProvider.deleteCredential(self._credentialServiceKey); }) + .then(() => {}); + } + + public find(query: adal.TokenCacheQuery, callback: (error: Error, results: adal.TokenCacheEntry[]) => void): void { + let self = this; + + this.doOperation(() => { + return self.readCache() + .then(cache => { + return cache.filter( + entry => TokenCache.findByPartial(entry, query) + ); + }) + .then( + results => callback(null, results), + (err) => callback(err, null) + ); + }); + } + + public remove(entries: adal.TokenCacheEntry[], callback: (error?: Error) => void): void { + let self = this; + + this.doOperation(() => { + return this.readCache() + .then(cache => self.removeFromCache(cache, entries)) + .then(updatedCache => self.writeCache(updatedCache)) + .then( + () => callback(null), + (err) => callback(err) + ); + }); + } + + // PRIVATE METHODS ///////////////////////////////////////////////////// + private static findByKeyHelper(entry1: adal.TokenCacheEntry, entry2: adal.TokenCacheEntry): boolean { + return entry1._authority === entry2._authority + && entry1._clientId === entry2._clientId + && entry1.userId === entry2.userId + && entry1.resource === entry2.resource; + } + + private static findByPartial(entry: adal.TokenCacheEntry, query: object): boolean { + for (let key in query) { + if (entry[key] === undefined || entry[key] !== query[key]) { + return false; + } + } + return true; + } + + private doOperation(op: () => Thenable): void { + // Initialize the active operation to an empty promise if necessary + let activeOperation = this._activeOperation || Promise.resolve(null); + + // Chain the operation to perform to the end of the existing promise + activeOperation = activeOperation.then(op); + + // Add a catch at the end to make sure we can continue after any errors + activeOperation = activeOperation.then(null, err => { + console.error(`Failed to perform token cache operation: ${err}`); + }); + + // Point the current active operation to this one + this._activeOperation = activeOperation; + } + + private addToCache(cache: adal.TokenCacheEntry[], entries: adal.TokenCacheEntry[]): adal.TokenCacheEntry[] { + // First remove entries from the db that are being updated + cache = this.removeFromCache(cache, entries); + + // Then add the new entries to the cache + entries.forEach((entry: adal.TokenCacheEntry) => { + cache.push(entry); + }); + + return cache; + } + + private getOrCreateEncryptionParams(): Thenable { + let self = this; + + return this._credentialProvider.readCredential(this._credentialServiceKey) + .then(credential => { + if (credential.password) { + // We already have encryption params, deserialize them + let splitValues = credential.password.split('|'); + if (splitValues.length === 2 && splitValues[0] && splitValues[1]) { + try { + return { + key: new Buffer(splitValues[0], 'hex'), + initializationVector: new Buffer(splitValues[1], 'hex') + }; + } catch(e) { + // Swallow the error and fall through to generate new params + console.warn('Failed to deserialize encryption params, new ones will be generated.'); + } + } + } + + // We haven't stored encryption values, so generate them + let encryptKey = crypto.randomBytes(TokenCache.CipherKeyLength); + let initializationVector = crypto.randomBytes(TokenCache.CipherAlgorithmIvLength); + + // Serialize the values + let serializedValues = `${encryptKey.toString('hex')}|${initializationVector.toString('hex')}`; + return self._credentialProvider.saveCredential(self._credentialServiceKey, serializedValues) + .then(() => { + return { + key: encryptKey, + initializationVector: initializationVector + }; + }); + }); + } + + private readCache(): Thenable { + let self = this; + + // NOTE: File system operations are performed synchronously to avoid annoying nested callbacks + // 1) Get the encryption key + // 2) Read the encrypted token cache file + // 3) Decrypt the file contents + // 4) Deserialize and return + return this.getOrCreateEncryptionParams() + .then(encryptionParams => { + try { + let cacheCipher = fs.readFileSync(self._cacheSerializationPath, TokenCache.FsOptions); + + let decipher = crypto.createDecipheriv(TokenCache.CipherAlgorithm, encryptionParams.key, encryptionParams.initializationVector); + let cacheJson = decipher.update(cacheCipher, 'hex', 'binary'); + cacheJson += decipher.final('binary'); + + return JSON.parse(cacheJson); + } catch(e) { + throw e; + } + }) + .then(null, err => { + // If reading the token cache fails, we'll just assume the tokens are garbage + console.warn(`Failed to read token cache: ${err}`); + return []; + }); + } + + private removeFromCache(cache: adal.TokenCacheEntry[], entries: adal.TokenCacheEntry[]): adal.TokenCacheEntry[] { + entries.forEach((entry: adal.TokenCacheEntry) => { + // Check to see if the entry exists + let match = cache.findIndex(entry2 => TokenCache.findByKeyHelper(entry, entry2)); + if (match >= 0) { + // Entry exists, remove it from cache + cache.splice(match, 1); + } + }); + + return cache; + } + + private writeCache(cache: adal.TokenCacheEntry[]): Thenable { + let self = this; + // NOTE: File system operations are being done synchronously to avoid annoying callback nesting + // 1) Get (or generate) the encryption key + // 2) Stringify the token cache entries + // 4) Encrypt the JSON + // 3) Write to the file + return this.getOrCreateEncryptionParams() + .then(encryptionParams => { + try { + let cacheJson = JSON.stringify(cache); + + let cipher = crypto.createCipheriv(TokenCache.CipherAlgorithm, encryptionParams.key, encryptionParams.initializationVector); + let cacheCipher = cipher.update(cacheJson, 'binary', 'hex'); + cacheCipher += cipher.final('hex'); + + fs.writeFileSync(self._cacheSerializationPath, cacheCipher, TokenCache.FsOptions); + } catch (e) { + throw e; + } + }); + } +} + +interface EncryptionParams { + key: Buffer; + initializationVector: Buffer; +} diff --git a/extensions/account-provider-azure/src/constants.ts b/extensions/account-provider-azure/src/constants.ts new file mode 100644 index 0000000000..2cf82dd183 --- /dev/null +++ b/extensions/account-provider-azure/src/constants.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vscode-nls'; + +let localize = nls.loadMessageBundle(); + +export const extensionName = localize('extensionName', 'Azure Accounts'); diff --git a/extensions/account-provider-azure/src/main.ts b/extensions/account-provider-azure/src/main.ts new file mode 100644 index 0000000000..87197555bc --- /dev/null +++ b/extensions/account-provider-azure/src/main.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as fs from 'fs'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { Utils } from 'extensions-modules'; + +import * as constants from './constants'; +import { AzureAccountProviderService } from './account-provider/azureAccountProviderService'; + +// EXTENSION ACTIVATION //////////////////////////////////////////////////// +export function activate(context: vscode.ExtensionContext): void { + // Create the folder for storing the token caches + let storagePath = path.join(Utils.getDefaultLogLocation(), constants.extensionName); + try { + if (!fs.existsSync(storagePath)) { + fs.mkdirSync(storagePath); + console.log('Initialized Azure account extension storage.'); + } + } catch(e) { + console.error(`Initialization of Azure account extension storage failed: ${e}`); + console.error('Azure accounts will not be available'); + return; + } + + // Create the provider service and activate + const accountProviderService = new AzureAccountProviderService(context, storagePath); + context.subscriptions.push(accountProviderService); + accountProviderService.activate(); +} diff --git a/extensions/account-provider-azure/src/typings/ref.d.ts b/extensions/account-provider-azure/src/typings/ref.d.ts new file mode 100644 index 0000000000..e21045c0c8 --- /dev/null +++ b/extensions/account-provider-azure/src/typings/ref.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions/account-provider-azure/tsconfig.json b/extensions/account-provider-azure/tsconfig.json new file mode 100644 index 0000000000..be1e5ee9ce --- /dev/null +++ b/extensions/account-provider-azure/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "./out", + "lib": [ + "es6", "es2015.promise" + ], + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "declaration": true, + "types": ["node"] + }, + "exclude": [ + "./node_modules" + ] +} diff --git a/extensions/bat/.vscodeignore b/extensions/bat/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/bat/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/bat/OSSREADME.json b/extensions/bat/OSSREADME.json new file mode 100644 index 0000000000..abfe7de67b --- /dev/null +++ b/extensions/bat/OSSREADME.json @@ -0,0 +1,22 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[{ + "name": "sublimehq/Packages", + "version": "0.0.0", + "license": "TextMate Bundle License", + "repositoryURL": "https://github.com/sublimehq/Packages", + "licenseDetail": [ + "Copyright (c) Sublime Packages project authors", + "", + "If not otherwise specified (see below), files in this folder fall under the following license: ", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information, ", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ] +}] diff --git a/extensions/bat/language-configuration.json b/extensions/bat/language-configuration.json new file mode 100644 index 0000000000..8b1695d8ec --- /dev/null +++ b/extensions/bat/language-configuration.json @@ -0,0 +1,22 @@ +{ + "comments": { + "lineComment": "REM" + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""] + ] +} \ No newline at end of file diff --git a/extensions/bat/package.json b/extensions/bat/package.json new file mode 100644 index 0000000000..e015ef2039 --- /dev/null +++ b/extensions/bat/package.json @@ -0,0 +1,19 @@ +{ + "name": "bat", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "languages": [{ + "id": "bat", + "extensions": [ ".bat", ".cmd"], + "aliases": [ "Batch", "bat" ], + "configuration": "./language-configuration.json" + }], + "grammars": [{ + "language": "bat", + "scopeName": "source.dosbatch", + "path": "./syntaxes/Batch File.tmLanguage" + }] + } +} \ No newline at end of file diff --git a/extensions/bat/syntaxes/Batch File.tmLanguage b/extensions/bat/syntaxes/Batch File.tmLanguage new file mode 100644 index 0000000000..7f0a106142 --- /dev/null +++ b/extensions/bat/syntaxes/Batch File.tmLanguage @@ -0,0 +1,169 @@ + + + + + uuid + E07EC438-7B75-4437-8AA1-DA94C1E6EACC + patterns + + + name + keyword.command.dosbatch + match + \b(?i)(?:append|assoc|at|attrib|break|cacls|cd|chcp|chdir|chkdsk|chkntfs|cls|cmd|color|comp|compact|convert|copy|date|del|dir|diskcomp|diskcopy|doskey|echo|endlocal|erase|fc|find|findstr|format|ftype|graftabl|help|keyb|label|md|mkdir|mode|more|move|path|pause|popd|print|prompt|pushd|rd|recover|ren|rename|replace|restore|rmdir|set|setlocal|shift|sort|start|subst|time|title|tree|type|ver|verify|vol|xcopy)\b + + + name + keyword.control.statement.dosbatch + match + \b(?i)(?:goto|call|exit)\b + + + name + keyword.control.conditional.if.dosbatch + match + \b(?i)if\s+((not)\s+)(exist|defined|errorlevel|cmdextversion)\b + + + name + keyword.control.conditional.dosbatch + match + \b(?i)(?:if|else)\b + + + name + keyword.control.repeat.dosbatch + match + \b(?i)for\b + + + name + keyword.operator.dosbatch + match + \b(?:EQU|NEQ|LSS|LEQ|GTR|GEQ)\b + + + name + comment.line.rem.dosbatch + match + \b(?i)rem(?:$|\s.*$) + + + name + comment.line.colons.dosbatch + match + \s*:\s*:.*$ + + + captures + + 1 + + name + variable.parameter.function.begin.shell + + + name + variable.parameter.function.dosbatch + match + (?i)(%)(~(?:f|d|p|n|x|s|a|t|z|\$[^:]*:)*)?\d + + + captures + + 1 + + name + variable.other.parsetime.begin.shell + + 2 + + name + variable.other.parsetime.end.shell + + + name + variable.other.parsetime.dosbatch + match + (%)[^%]+(%)|(%%)[^%]+(%%) + + + captures + + 1 + + name + variable.parameter.loop.begin.shell + + + name + variable.parameter.loop.dosbatch + match + (?i)(%%)(~(?:f|d|p|n|x|s|a|t|z|\$[^:]*:)*)?[a-z] + + + captures + + 1 + + name + variable.other.delayed.begin.shell + + 2 + + name + variable.other.delayed.end.shell + + + name + variable.other.delayed.dosbatch + match + (!)[^!]+(!) + + + begin + " + endCaptures + + 0 + + name + punctuation.definition.string.end.shell + + + beginCaptures + + 0 + + name + punctuation.definition.string.begin.shell + + + name + string.quoted.double.dosbatch + end + "|$ + + + name + keyword.operator.pipe.dosbatch + match + [|] + + + name + keyword.operator.redirect.shell + match + &>|\d*>&\d*|\d*(>>|>|<)|\d*<&|\d*<> + + + name + Batch File + scopeName + source.dosbatch + fileTypes + + bat + + + \ No newline at end of file diff --git a/extensions/bat/test/colorize-fixtures/test.bat b/extensions/bat/test/colorize-fixtures/test.bat new file mode 100644 index 0000000000..3e215fc5ef --- /dev/null +++ b/extensions/bat/test/colorize-fixtures/test.bat @@ -0,0 +1,24 @@ +@echo off +setlocal + +title VSCode Dev + +pushd %~dp0\.. + +:: Node modules +if not exist node_modules call .\scripts\npm.bat install + +:: Get electron +node .\node_modules\gulp\bin\gulp.js electron + +:: Build +if not exist out node .\node_modules\gulp\bin\gulp.js compile + +:: Configuration +set NODE_ENV=development + +call echo %%LINE:rem +=%% + +popd + +endlocal \ No newline at end of file diff --git a/extensions/bat/test/colorize-results/test_bat.json b/extensions/bat/test/colorize-results/test_bat.json new file mode 100644 index 0000000000..d0f381fc7f --- /dev/null +++ b/extensions/bat/test/colorize-results/test_bat.json @@ -0,0 +1,343 @@ +[ + { + "c": "@", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "echo", + "t": "source.dosbatch keyword.command.dosbatch", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " off", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "setlocal", + "t": "source.dosbatch keyword.command.dosbatch", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "title", + "t": "source.dosbatch keyword.command.dosbatch", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " VSCode Dev", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pushd", + "t": "source.dosbatch keyword.command.dosbatch", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "%", + "t": "source.dosbatch variable.parameter.function.dosbatch variable.parameter.function.begin.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "~dp0", + "t": "source.dosbatch variable.parameter.function.dosbatch", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\\..", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":: Node modules", + "t": "source.dosbatch comment.line.colons.dosbatch", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "if not exist", + "t": "source.dosbatch keyword.control.conditional.if.dosbatch", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " node_modules ", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "call", + "t": "source.dosbatch keyword.control.statement.dosbatch", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " .\\scripts\\npm.bat install", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":: Get electron", + "t": "source.dosbatch comment.line.colons.dosbatch", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "node .\\node_modules\\gulp\\bin\\gulp.js electron", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":: Build", + "t": "source.dosbatch comment.line.colons.dosbatch", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "if not exist", + "t": "source.dosbatch keyword.control.conditional.if.dosbatch", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " out node .\\node_modules\\gulp\\bin\\gulp.js compile", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":: Configuration", + "t": "source.dosbatch comment.line.colons.dosbatch", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "set", + "t": "source.dosbatch keyword.command.dosbatch", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " NODE_ENV=development", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "call", + "t": "source.dosbatch keyword.control.statement.dosbatch", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "echo", + "t": "source.dosbatch keyword.command.dosbatch", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.dosbatch", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "%%LINE:rem +=%%", + "t": "source.dosbatch variable.other.parsetime.dosbatch", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "popd", + "t": "source.dosbatch keyword.command.dosbatch", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "endlocal", + "t": "source.dosbatch keyword.command.dosbatch", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + } +] \ No newline at end of file diff --git a/extensions/configuration-editing/.vscodeignore b/extensions/configuration-editing/.vscodeignore new file mode 100644 index 0000000000..24428a6f75 --- /dev/null +++ b/extensions/configuration-editing/.vscodeignore @@ -0,0 +1,4 @@ +test/** +src/** +tsconfig.json +npm-shrinkwrap.json \ No newline at end of file diff --git a/extensions/configuration-editing/npm-shrinkwrap.json b/extensions/configuration-editing/npm-shrinkwrap.json new file mode 100644 index 0000000000..fd5b19bc40 --- /dev/null +++ b/extensions/configuration-editing/npm-shrinkwrap.json @@ -0,0 +1,15 @@ +{ + "name": "configuration-editing", + "version": "0.0.1", + "dependencies": { + "jsonc-parser": { + "version": "0.3.1", + "from": "jsonc-parser@0.3.1" + }, + "vscode-nls": { + "version": "2.0.2", + "from": "vscode-nls@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + } + } +} diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json new file mode 100644 index 0000000000..1fcac289e2 --- /dev/null +++ b/extensions/configuration-editing/package.json @@ -0,0 +1,87 @@ +{ + "name": "configuration-editing", + "version": "0.0.1", + "publisher": "vscode", + "engines": { + "vscode": "^1.0.0" + }, + "categories": [ + "Languages", + "Other" + ], + "activationEvents": [ + "onLanguage:json" + ], + "main": "./out/extension", + "scripts": { + "compile": "gulp compile-extension:configuration-editing", + "watch": "gulp watch-extension:configuration-editing" + }, + "dependencies": { + "jsonc-parser": "^0.3.1", + "vscode-nls": "^2.0.1" + }, + "contributes": { + "jsonValidation": [ + { + "fileMatch": "vscode://defaultsettings/keybindings.json", + "url": "vscode://schemas/keybindings" + }, + { + "fileMatch": "%APP_SETTINGS_HOME%/keybindings.json", + "url": "vscode://schemas/keybindings" + }, + { + "fileMatch": "vscode://defaultsettings/settings.json", + "url": "vscode://schemas/settings" + }, + { + "fileMatch": "vscode://defaultsettings/resourceSettings.json", + "url": "vscode://schemas/settings/resource" + }, + { + "fileMatch": "vscode://settings/workspaceSettings.json", + "url": "vscode://schemas/settings" + }, + { + "fileMatch": "%APP_SETTINGS_HOME%/settings.json", + "url": "vscode://schemas/settings" + }, + { + "fileMatch": "%APP_WORKSPACES_HOME%/*/workspace.json", + "url": "vscode://schemas/workspaceConfig" + }, + { + "fileMatch": "**/*.code-workspace", + "url": "vscode://schemas/workspaceConfig" + }, + { + "fileMatch": "%APP_SETTINGS_HOME%/locale.json", + "url": "vscode://schemas/locale" + }, + { + "fileMatch": "/.sqlops/settings.json", + "url": "vscode://schemas/settings" + }, + { + "fileMatch": "/.sqlops/launch.json", + "url": "vscode://schemas/launch" + }, + { + "fileMatch": "/.sqlops/tasks.json", + "url": "vscode://schemas/tasks" + }, + { + "fileMatch": "%APP_SETTINGS_HOME%/snippets/*.json", + "url": "vscode://schemas/snippets" + }, + { + "fileMatch": "/.vscode/extensions.json", + "url": "vscode://schemas/extensions" + } + ] + }, + "devDependencies": { + "@types/node": "^7.0.4" + } +} \ No newline at end of file diff --git a/extensions/configuration-editing/src/extension.ts b/extensions/configuration-editing/src/extension.ts new file mode 100644 index 0000000000..b6ed3899a4 --- /dev/null +++ b/extensions/configuration-editing/src/extension.ts @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as vscode from 'vscode'; +import { getLocation, visit, parse } from 'jsonc-parser'; +import * as path from 'path'; +import { SettingsDocument } from './settingsDocumentHelper'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +const decoration = vscode.window.createTextEditorDecorationType({ + color: '#b1b1b1' +}); + +let pendingLaunchJsonDecoration: NodeJS.Timer; + +export function activate(context): void { + + //keybindings.json command-suggestions + context.subscriptions.push(registerKeybindingsCompletions()); + + //settings.json suggestions + context.subscriptions.push(registerSettingsCompletions()); + + //extensions.json suggestions + context.subscriptions.push(registerExtensionsCompletions()); + + // launch.json decorations + context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => updateLaunchJsonDecorations(editor), null, context.subscriptions)); + context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(event => { + if (vscode.window.activeTextEditor && event.document === vscode.window.activeTextEditor.document) { + if (pendingLaunchJsonDecoration) { + clearTimeout(pendingLaunchJsonDecoration); + } + pendingLaunchJsonDecoration = setTimeout(() => updateLaunchJsonDecorations(vscode.window.activeTextEditor), 1000); + } + }, null, context.subscriptions)); + updateLaunchJsonDecorations(vscode.window.activeTextEditor); +} + +function registerKeybindingsCompletions(): vscode.Disposable { + const commands = vscode.commands.getCommands(true); + + return vscode.languages.registerCompletionItemProvider({ pattern: '**/keybindings.json' }, { + + provideCompletionItems(document, position, token) { + const location = getLocation(document.getText(), document.offsetAt(position)); + if (location.path[1] === 'command') { + + const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position); + return commands.then(ids => ids.map(id => newSimpleCompletionItem(JSON.stringify(id), range))); + } + } + }); +} + +function registerSettingsCompletions(): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider({ language: 'json', pattern: '**/settings.json' }, { + provideCompletionItems(document, position, token) { + return new SettingsDocument(document).provideCompletionItems(position, token); + } + }); +} + +function registerExtensionsCompletions(): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider({ pattern: '**/extensions.json' }, { + provideCompletionItems(document, position, token) { + const location = getLocation(document.getText(), document.offsetAt(position)); + const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position); + if (location.path[0] === 'recommendations') { + const config = parse(document.getText()); + const alreadyEnteredExtensions = config && config.recommendations || []; + if (Array.isArray(alreadyEnteredExtensions)) { + const knownExtensionProposals = vscode.extensions.all.filter(e => + !(e.id.startsWith('vscode.') + || e.id === 'Microsoft.vscode-markdown' + || alreadyEnteredExtensions.indexOf(e.id) > -1)); + if (knownExtensionProposals.length) { + return knownExtensionProposals.map(e => { + const item = new vscode.CompletionItem(e.id); + const insertText = `"${e.id}"`; + item.kind = vscode.CompletionItemKind.Value; + item.insertText = insertText; + item.range = range; + item.filterText = insertText; + return item; + }); + } else { + const example = new vscode.CompletionItem(localize('exampleExtension', "Example")); + example.insertText = '"vscode.csharp"'; + example.kind = vscode.CompletionItemKind.Value; + example.range = range; + return [example]; + } + } + } + return []; + } + }); +} + +function newSimpleCompletionItem(label: string, range: vscode.Range, description?: string, insertText?: string): vscode.CompletionItem { + const item = new vscode.CompletionItem(label); + item.kind = vscode.CompletionItemKind.Value; + item.detail = description; + item.insertText = insertText || label; + item.range = range; + + return item; +} + +function updateLaunchJsonDecorations(editor: vscode.TextEditor | undefined): void { + if (!editor || path.basename(editor.document.fileName) !== 'launch.json') { + return; + } + + const ranges: vscode.Range[] = []; + let addPropertyAndValue = false; + let depthInArray = 0; + visit(editor.document.getText(), { + onObjectProperty: (property, offset, length) => { + // Decorate attributes which are unlikely to be edited by the user. + // Only decorate "configurations" if it is not inside an array (compounds have a configurations property which should not be decorated). + addPropertyAndValue = property === 'version' || property === 'type' || property === 'request' || property === 'compounds' || (property === 'configurations' && depthInArray === 0); + if (addPropertyAndValue) { + ranges.push(new vscode.Range(editor.document.positionAt(offset), editor.document.positionAt(offset + length))); + } + }, + onLiteralValue: (value, offset, length) => { + if (addPropertyAndValue) { + ranges.push(new vscode.Range(editor.document.positionAt(offset), editor.document.positionAt(offset + length))); + } + }, + onArrayBegin: (offset: number, length: number) => { + depthInArray++; + }, + onArrayEnd: (offset: number, length: number) => { + depthInArray--; + } + }); + + editor.setDecorations(decoration, ranges); +} + +vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', language: 'json' }, { + provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult { + const result: vscode.SymbolInformation[] = []; + let name: string = ''; + let lastProperty = ''; + let startOffset = 0; + let depthInObjects = 0; + + visit(document.getText(), { + onObjectProperty: (property, offset, length) => { + lastProperty = property; + }, + onLiteralValue: (value: any, offset: number, length: number) => { + if (lastProperty === 'name') { + name = value; + } + }, + onObjectBegin: (offset: number, length: number) => { + depthInObjects++; + if (depthInObjects === 2) { + startOffset = offset; + } + }, + onObjectEnd: (offset: number, length: number) => { + if (name && depthInObjects === 2) { + result.push(new vscode.SymbolInformation(name, vscode.SymbolKind.Object, new vscode.Range(document.positionAt(startOffset), document.positionAt(offset)))); + } + depthInObjects--; + }, + }); + + return result; + } +}); diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts new file mode 100644 index 0000000000..231732b25c --- /dev/null +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -0,0 +1,209 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { getLocation, Location } from 'jsonc-parser'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +export class SettingsDocument { + + constructor(private document: vscode.TextDocument) { } + + public provideCompletionItems(position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult { + const location = getLocation(this.document.getText(), this.document.offsetAt(position)); + const range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position); + + // window.title + if (location.path[0] === 'window.title') { + return this.provideWindowTitleCompletionItems(location, range); + } + + // files.association + if (location.path[0] === 'files.associations') { + return this.provideFilesAssociationsCompletionItems(location, range); + } + + // files.exclude, search.exclude + if (location.path[0] === 'files.exclude' || location.path[0] === 'search.exclude') { + return this.provideExcludeCompletionItems(location, range); + } + + // files.defaultLanguage + if (location.path[0] === 'files.defaultLanguage') { + return this.provideLanguageCompletionItems(location, range); + } + + return this.provideLanguageOverridesCompletionItems(location, position); + } + + private provideWindowTitleCompletionItems(location: Location, range: vscode.Range): vscode.ProviderResult { + const completions: vscode.CompletionItem[] = []; + + completions.push(this.newSimpleCompletionItem('${activeEditorShort}', range, localize('activeEditorShort', "e.g. myFile.txt"))); + completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "e.g. myFolder/myFile.txt"))); + completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "e.g. /Users/Development/myProject/myFolder/myFile.txt"))); + completions.push(this.newSimpleCompletionItem('${rootName}', range, localize('rootName', "e.g. myFolder1, myFolder2, myFolder3"))); + completions.push(this.newSimpleCompletionItem('${rootPath}', range, localize('rootPath', "e.g. /Users/Development/myProject"))); + completions.push(this.newSimpleCompletionItem('${folderName}', range, localize('folderName', "e.g. myFolder"))); + completions.push(this.newSimpleCompletionItem('${folderPath}', range, localize('folderPath', "e.g. /Users/Development/myFolder"))); + completions.push(this.newSimpleCompletionItem('${appName}', range, localize('appName', "e.g. VS Code"))); + completions.push(this.newSimpleCompletionItem('${dirty}', range, localize('dirty', "a dirty indicator if the active editor is dirty"))); + completions.push(this.newSimpleCompletionItem('${separator}', range, localize('separator', "a conditional separator (' - ') that only shows when surrounded by variables with values"))); + + return Promise.resolve(completions); + } + + private provideFilesAssociationsCompletionItems(location: Location, range: vscode.Range): vscode.ProviderResult { + const completions: vscode.CompletionItem[] = []; + + // Key + if (location.path.length === 1) { + completions.push(this.newSnippetCompletionItem({ + label: localize('assocLabelFile', "Files with Extension"), + documentation: localize('assocDescriptionFile', "Map all files matching the glob pattern in their filename to the language with the given identifier."), + snippet: location.isAtPropertyKey ? '"*.${1:extension}": "${2:language}"' : '{ "*.${1:extension}": "${2:language}" }', + range + })); + + completions.push(this.newSnippetCompletionItem({ + label: localize('assocLabelPath', "Files with Path"), + documentation: localize('assocDescriptionPath', "Map all files matching the absolute path glob pattern in their path to the language with the given identifier."), + snippet: location.isAtPropertyKey ? '"/${1:path to file}/*.${2:extension}": "${3:language}"' : '{ "/${1:path to file}/*.${2:extension}": "${3:language}" }', + range + })); + } + + // Value + else if (location.path.length === 2 && !location.isAtPropertyKey) { + return this.provideLanguageCompletionItems(location, range); + } + + return Promise.resolve(completions); + } + + private provideExcludeCompletionItems(location: Location, range: vscode.Range): vscode.ProviderResult { + const completions: vscode.CompletionItem[] = []; + + // Key + if (location.path.length === 1) { + completions.push(this.newSnippetCompletionItem({ + label: localize('fileLabel', "Files by Extension"), + documentation: localize('fileDescription', "Match all files of a specific file extension."), + snippet: location.isAtPropertyKey ? '"**/*.${1:extension}": true' : '{ "**/*.${1:extension}": true }', + range + })); + + completions.push(this.newSnippetCompletionItem({ + label: localize('filesLabel', "Files with Multiple Extensions"), + documentation: localize('filesDescription', "Match all files with any of the file extensions."), + snippet: location.isAtPropertyKey ? '"**/*.{ext1,ext2,ext3}": true' : '{ "**/*.{ext1,ext2,ext3}": true }', + range + })); + + completions.push(this.newSnippetCompletionItem({ + label: localize('derivedLabel', "Files with Siblings by Name"), + documentation: localize('derivedDescription', "Match files that have siblings with the same name but a different extension."), + snippet: location.isAtPropertyKey ? '"**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" }' : '{ "**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" } }', + range + })); + + completions.push(this.newSnippetCompletionItem({ + label: localize('topFolderLabel', "Folder by Name (Top Level)"), + documentation: localize('topFolderDescription', "Match a top level folder with a specific name."), + snippet: location.isAtPropertyKey ? '"${1:name}": true' : '{ "${1:name}": true }', + range + })); + + completions.push(this.newSnippetCompletionItem({ + label: localize('topFoldersLabel', "Folders with Multiple Names (Top Level)"), + documentation: localize('topFoldersDescription', "Match multiple top level folders."), + snippet: location.isAtPropertyKey ? '"{folder1,folder2,folder3}": true' : '{ "{folder1,folder2,folder3}": true }', + range + })); + + completions.push(this.newSnippetCompletionItem({ + label: localize('folderLabel', "Folder by Name (Any Location)"), + documentation: localize('folderDescription', "Match a folder with a specific name in any location."), + snippet: location.isAtPropertyKey ? '"**/${1:name}": true' : '{ "**/${1:name}": true }', + range + })); + } + + // Value + else { + completions.push(this.newSimpleCompletionItem('false', range, localize('falseDescription', "Disable the pattern."))); + completions.push(this.newSimpleCompletionItem('true', range, localize('trueDescription', "Enable the pattern."))); + + completions.push(this.newSnippetCompletionItem({ + label: localize('derivedLabel', "Files with Siblings by Name"), + documentation: localize('siblingsDescription', "Match files that have siblings with the same name but a different extension."), + snippet: '{ "when": "$(basename).${1:extension}" }', + range + })); + } + + return Promise.resolve(completions); + } + + private provideLanguageCompletionItems(location: Location, range: vscode.Range, formatFunc: (string) => string = (l) => JSON.stringify(l)): vscode.ProviderResult { + return vscode.languages.getLanguages().then(languages => { + return languages.map(l => { + return this.newSimpleCompletionItem(formatFunc(l), range); + }); + }); + } + + private provideLanguageOverridesCompletionItems(location: Location, position: vscode.Position): vscode.ProviderResult { + let range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position); + const text = this.document.getText(range); + + if (location.path.length === 0) { + + let snippet = '"[${1:language}]": {\n\t"$0"\n}'; + + // Suggestion model word matching includes quotes, + // hence exclude the starting quote from the snippet and the range + // ending quote gets replaced + if (text && text.startsWith('"')) { + range = new vscode.Range(new vscode.Position(range.start.line, range.start.character + 1), range.end); + snippet = snippet.substring(1); + } + + return Promise.resolve([this.newSnippetCompletionItem({ + label: localize('languageSpecificEditorSettings', "Language specific editor settings"), + documentation: localize('languageSpecificEditorSettingsDescription', "Override editor settings for language"), + snippet, + range + })]); + } + + if (location.path.length === 1 && location.previousNode && typeof location.previousNode.value === 'string' && location.previousNode.value.startsWith('[')) { + // Suggestion model word matching includes closed sqaure bracket and ending quote + // Hence include them in the proposal to replace + return this.provideLanguageCompletionItems(location, range, language => `"[${language}]"`); + } + return Promise.resolve([]); + } + + private newSimpleCompletionItem(text: string, range: vscode.Range, description?: string, insertText?: string): vscode.CompletionItem { + const item = new vscode.CompletionItem(text); + item.kind = vscode.CompletionItemKind.Value; + item.detail = description; + item.insertText = insertText ? insertText : text; + item.range = range; + return item; + } + + private newSnippetCompletionItem(o: { label: string; documentation?: string; snippet: string; range: vscode.Range; }): vscode.CompletionItem { + const item = new vscode.CompletionItem(o.label); + item.kind = vscode.CompletionItemKind.Value; + item.documentation = o.documentation; + item.insertText = new vscode.SnippetString(o.snippet); + item.range = o.range; + return item; + } +} diff --git a/extensions/configuration-editing/src/typings/ref.d.ts b/extensions/configuration-editing/src/typings/ref.d.ts new file mode 100644 index 0000000000..acf3ed8429 --- /dev/null +++ b/extensions/configuration-editing/src/typings/ref.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions/configuration-editing/tsconfig.json b/extensions/configuration-editing/tsconfig.json new file mode 100644 index 0000000000..6971f531b1 --- /dev/null +++ b/extensions/configuration-editing/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "outDir": "./out", + "lib": [ + "es2015" + ], + "strictNullChecks": true + }, + "include": [ + "src/**/*" + ] +} diff --git a/extensions/declares.d.ts b/extensions/declares.d.ts new file mode 100644 index 0000000000..c15a532cf3 --- /dev/null +++ b/extensions/declares.d.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// + +// Declaring the following because the code gets compiled with es5, which lack definitions for console and timers. +declare var console: { + assert(value: any, message?: string, ...optionalParams: any[]): void; + dir(obj: any, options?: { showHidden?: boolean, depth?: number, colors?: boolean }): void; + error(message?: any, ...optionalParams: any[]): void; + info(message?: any, ...optionalParams: any[]): void; + log(message?: any, ...optionalParams: any[]): void; + time(label: string): void; + timeEnd(label: string): void; + trace(message?: any, ...optionalParams: any[]): void; + warn(message?: any, ...optionalParams: any[]): void; +}; + + +// ---- ES6 promise ------------------------------------------------------ + +/** + * Represents the completion of an asynchronous operation. + */ +interface Promise extends Thenable { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Promise; + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Promise; + + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: (reason: any) => T | Thenable): Promise; + + // [Symbol.toStringTag]: string; +} + +interface PromiseConstructor { + // /** + // * A reference to the prototype. + // */ + // prototype: Promise; + + /** + * Creates a new Promise. + * @param executor A callback used to initialize the promise. This callback is passed two arguments: + * a resolve callback used to resolve the promise with a value or the result of another promise, + * and a reject callback used to reject the promise with a provided reason or error. + */ + new (executor: (resolve: (value?: T | Thenable) => void, reject: (reason?: any) => void) => void): Promise; + + /** + * Creates a Promise that is resolved with an array of results when all of the provided Promises + * resolve, or rejected when any Promise is rejected. + * @param values An array of Promises. + * @returns A new Promise. + */ + all(values: Array>): Promise; + + /** + * Creates a Promise that is resolved or rejected when any of the provided Promises are resolved + * or rejected. + * @param values An array of Promises. + * @returns A new Promise. + */ + race(values: Array>): Promise; + + /** + * Creates a new rejected promise for the provided reason. + * @param reason The reason the promise was rejected. + * @returns A new rejected Promise. + */ + reject(reason: any): Promise; + + /** + * Creates a new rejected promise for the provided reason. + * @param reason The reason the promise was rejected. + * @returns A new rejected Promise. + */ + reject(reason: any): Promise; + + /** + * Creates a new resolved promise for the provided value. + * @param value A promise. + * @returns A promise whose internal state matches the provided promise. + */ + resolve(value: T | Thenable): Promise; + + /** + * Creates a new resolved promise. + * @returns A resolved promise. + */ + resolve(): Promise; + + // [Symbol.species]: Function; +} + +declare var Promise: PromiseConstructor; diff --git a/extensions/diff/.vscodeignore b/extensions/diff/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/diff/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/diff/OSSREADME.json b/extensions/diff/OSSREADME.json new file mode 100644 index 0000000000..3be6fb8138 --- /dev/null +++ b/extensions/diff/OSSREADME.json @@ -0,0 +1,22 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[{ + "name": "textmate/diff.tmbundle", + "version": "0.0.0", + "license": "TextMate Bundle License", + "repositoryURL": "https://github.com/textmate/diff.tmbundle", + "licenseDetail": [ + "Copyright (c) textmate-diff.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ] +}] \ No newline at end of file diff --git a/extensions/diff/language-configuration.json b/extensions/diff/language-configuration.json new file mode 100644 index 0000000000..da6ed54715 --- /dev/null +++ b/extensions/diff/language-configuration.json @@ -0,0 +1,11 @@ +{ + "comments": { + "lineComment": "//", + "blockComment": [ "/*", "*/" ] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ] +} \ No newline at end of file diff --git a/extensions/diff/package.json b/extensions/diff/package.json new file mode 100644 index 0000000000..a62e57243c --- /dev/null +++ b/extensions/diff/package.json @@ -0,0 +1,26 @@ +{ + "name": "diff", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "scripts": { + "update-grammar": "node ../../build/npm/update-grammar.js textmate/diff.tmbundle Syntaxes/Diff.plist ./syntaxes/diff.tmLanguage.json" + }, + "contributes": { + "languages": [ + { + "id": "diff", + "aliases": ["Diff", "diff" ], + "extensions": [".diff", ".patch", ".rej"], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "diff", + "scopeName": "source.diff", + "path": "./syntaxes/diff.tmLanguage.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/diff/syntaxes/diff.tmLanguage b/extensions/diff/syntaxes/diff.tmLanguage new file mode 100644 index 0000000000..d0c5d15437 --- /dev/null +++ b/extensions/diff/syntaxes/diff.tmLanguage @@ -0,0 +1,268 @@ + + + + + fileTypes + + patch + diff + rej + + firstLineMatch + (?x)^ + (===\ modified\ file + |==== \s* // .+ \s - \s .+ \s+ ==== + |Index:\ + |---\ [^%\n] + |\*\*\*.*\d{4}\s*$ + |\d+(,\d+)* (a|d|c) \d+(,\d+)* $ + |diff\ --git\ + |commit\ [0-9a-f]{40}$ + ) + keyEquivalent + ^~D + name + Diff + patterns + + + captures + + 1 + + name + punctuation.definition.separator.diff + + + match + ^((\*{15})|(={67})|(-{3}))$\n? + name + meta.separator.diff + + + match + ^\d+(,\d+)*(a|d|c)\d+(,\d+)*$\n? + name + meta.diff.range.normal + + + captures + + 1 + + name + punctuation.definition.range.diff + + 2 + + name + meta.toc-list.line-number.diff + + 3 + + name + punctuation.definition.range.diff + + + match + ^(@@)\s*(.+?)\s*(@@)($\n?)? + name + meta.diff.range.unified + + + captures + + 3 + + name + punctuation.definition.range.diff + + 4 + + name + punctuation.definition.range.diff + + 6 + + name + punctuation.definition.range.diff + + 7 + + name + punctuation.definition.range.diff + + + match + ^(((\-{3}) .+ (\-{4}))|((\*{3}) .+ (\*{4})))$\n? + name + meta.diff.range.context + + + match + ^diff --git a/.*$\n? + name + meta.diff.header.git + + + match + ^diff (-|\S+\s+\S+).*$\n? + name + meta.diff.header.command + + + captures + + 4 + + name + punctuation.definition.from-file.diff + + 6 + + name + punctuation.definition.from-file.diff + + 7 + + name + punctuation.definition.from-file.diff + + + match + (^(((-{3}) .+)|((\*{3}) .+))$\n?|^(={4}) .+(?= - )) + name + meta.diff.header.from-file + + + captures + + 2 + + name + punctuation.definition.to-file.diff + + 3 + + name + punctuation.definition.to-file.diff + + 4 + + name + punctuation.definition.to-file.diff + + + match + (^(\+{3}) .+$\n?| (-) .* (={4})$\n?) + name + meta.diff.header.to-file + + + captures + + 3 + + name + punctuation.definition.inserted.diff + + 6 + + name + punctuation.definition.inserted.diff + + + match + ^(((>)( .*)?)|((\+).*))$\n? + name + markup.inserted.diff + + + captures + + 1 + + name + punctuation.definition.inserted.diff + + + match + ^(!).*$\n? + name + markup.changed.diff + + + captures + + 3 + + name + punctuation.definition.inserted.diff + + 6 + + name + punctuation.definition.inserted.diff + + + match + ^(((<)( .*)?)|((-).*))$\n? + name + markup.deleted.diff + + + begin + ^(#) + captures + + 1 + + name + punctuation.definition.comment.diff + + + comment + Git produces unified diffs with embedded comments" + end + \n + name + comment.line.number-sign.diff + + + match + ^index [0-9a-f]{7,40}\.\.[0-9a-f]{7,40}.*$\n? + name + meta.diff.index.git + + + captures + + 1 + + name + punctuation.separator.key-value.diff + + 2 + + name + meta.toc-list.file-name.diff + + + match + ^Index(:) (.+)$\n? + name + meta.diff.index + + + match + ^Only in .*: .*$\n? + name + meta.diff.only-in + + + scopeName + source.diff + uuid + 7E848FF4-708E-11D9-97B4-0011242E4184 + + diff --git a/extensions/diff/syntaxes/diff.tmLanguage.json b/extensions/diff/syntaxes/diff.tmLanguage.json new file mode 100644 index 0000000000..e0d60de108 --- /dev/null +++ b/extensions/diff/syntaxes/diff.tmLanguage.json @@ -0,0 +1,168 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/textmate/diff.tmbundle/blob/master/Syntaxes/Diff.plist", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/textmate/diff.tmbundle/commit/0593bb775eab1824af97ef2172fd38822abd97d7", + "fileTypes": [ + "patch", + "diff", + "rej" + ], + "firstLineMatch": "(?x)^\n\t\t(===\\ modified\\ file\n\t\t|==== \\s* // .+ \\s - \\s .+ \\s+ ====\n\t\t|Index:\\ \n\t\t|---\\ [^%\\n]\n\t\t|\\*\\*\\*.*\\d{4}\\s*$\n\t\t|\\d+(,\\d+)* (a|d|c) \\d+(,\\d+)* $\n\t\t|diff\\ --git\\ \n\t\t|commit\\ [0-9a-f]{40}$\n\t\t)", + "keyEquivalent": "^~D", + "name": "Diff", + "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.definition.separator.diff" + } + }, + "match": "^((\\*{15})|(={67})|(-{3}))$\\n?", + "name": "meta.separator.diff" + }, + { + "match": "^\\d+(,\\d+)*(a|d|c)\\d+(,\\d+)*$\\n?", + "name": "meta.diff.range.normal" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.range.diff" + }, + "2": { + "name": "meta.toc-list.line-number.diff" + }, + "3": { + "name": "punctuation.definition.range.diff" + } + }, + "match": "^(@@)\\s*(.+?)\\s*(@@)($\\n?)?", + "name": "meta.diff.range.unified" + }, + { + "captures": { + "3": { + "name": "punctuation.definition.range.diff" + }, + "4": { + "name": "punctuation.definition.range.diff" + }, + "6": { + "name": "punctuation.definition.range.diff" + }, + "7": { + "name": "punctuation.definition.range.diff" + } + }, + "match": "^(((\\-{3}) .+ (\\-{4}))|((\\*{3}) .+ (\\*{4})))$\\n?", + "name": "meta.diff.range.context" + }, + { + "match": "^diff --git a/.*$\\n?", + "name": "meta.diff.header.git" + }, + { + "match": "^diff (-|\\S+\\s+\\S+).*$\\n?", + "name": "meta.diff.header.command" + }, + { + "captures": { + "4": { + "name": "punctuation.definition.from-file.diff" + }, + "6": { + "name": "punctuation.definition.from-file.diff" + }, + "7": { + "name": "punctuation.definition.from-file.diff" + } + }, + "match": "(^(((-{3}) .+)|((\\*{3}) .+))$\\n?|^(={4}) .+(?= - ))", + "name": "meta.diff.header.from-file" + }, + { + "captures": { + "2": { + "name": "punctuation.definition.to-file.diff" + }, + "3": { + "name": "punctuation.definition.to-file.diff" + }, + "4": { + "name": "punctuation.definition.to-file.diff" + } + }, + "match": "(^(\\+{3}) .+$\\n?| (-) .* (={4})$\\n?)", + "name": "meta.diff.header.to-file" + }, + { + "captures": { + "3": { + "name": "punctuation.definition.inserted.diff" + }, + "6": { + "name": "punctuation.definition.inserted.diff" + } + }, + "match": "^(((>)( .*)?)|((\\+).*))$\\n?", + "name": "markup.inserted.diff" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.changed.diff" + } + }, + "match": "^(!).*$\\n?", + "name": "markup.changed.diff" + }, + { + "captures": { + "3": { + "name": "punctuation.definition.deleted.diff" + }, + "6": { + "name": "punctuation.definition.deleted.diff" + } + }, + "match": "^(((<)( .*)?)|((-).*))$\\n?", + "name": "markup.deleted.diff" + }, + { + "begin": "^(#)", + "captures": { + "1": { + "name": "punctuation.definition.comment.diff" + } + }, + "comment": "Git produces unified diffs with embedded comments\"", + "end": "\\n", + "name": "comment.line.number-sign.diff" + }, + { + "match": "^index [0-9a-f]{7,40}\\.\\.[0-9a-f]{7,40}.*$\\n?", + "name": "meta.diff.index.git" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.key-value.diff" + }, + "2": { + "name": "meta.toc-list.file-name.diff" + } + }, + "match": "^Index(:) (.+)$\\n?", + "name": "meta.diff.index" + }, + { + "match": "^Only in .*: .*$\\n?", + "name": "meta.diff.only-in" + } + ], + "scopeName": "source.diff", + "uuid": "7E848FF4-708E-11D9-97B4-0011242E4184" +} \ No newline at end of file diff --git a/extensions/diff/test/colorize-fixtures/test.diff b/extensions/diff/test/colorize-fixtures/test.diff new file mode 100644 index 0000000000..f8805a8987 --- /dev/null +++ b/extensions/diff/test/colorize-fixtures/test.diff @@ -0,0 +1,19 @@ +--- lao Sat Jan 26 23:30:39 1991 ++++ tzu Sat Jan 26 23:30:50 1991 +@@ -1,7 +1,6 @@ +-The Way that can be told of is not the eternal Way; +-The name that can be named is not the eternal name. + The Nameless is the origin of Heaven and Earth; +-The Named is the mother of all things. ++The named is the mother of all things. ++ + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +@@ -9,3 +8,6 @@ + The two are the same, + But after they are produced, + they have different names. ++They both may be called deep and profound. ++Deeper and more profound, ++The door of all subtleties! \ No newline at end of file diff --git a/extensions/diff/test/colorize-results/test_diff.json b/extensions/diff/test/colorize-results/test_diff.json new file mode 100644 index 0000000000..26ab2f4fb0 --- /dev/null +++ b/extensions/diff/test/colorize-results/test_diff.json @@ -0,0 +1,398 @@ +[ + { + "c": "---", + "t": "source.diff meta.diff.header.from-file punctuation.definition.from-file.diff", + "r": { + "dark_plus": "meta.diff.header: #569CD6", + "light_plus": "meta.diff.header: #000080", + "dark_vs": "meta.diff.header: #569CD6", + "light_vs": "meta.diff.header: #000080", + "hc_black": "meta.diff.header: #000080" + } + }, + { + "c": " lao\tSat Jan 26 23:30:39 1991", + "t": "source.diff meta.diff.header.from-file", + "r": { + "dark_plus": "meta.diff.header: #569CD6", + "light_plus": "meta.diff.header: #000080", + "dark_vs": "meta.diff.header: #569CD6", + "light_vs": "meta.diff.header: #000080", + "hc_black": "meta.diff.header: #000080" + } + }, + { + "c": "+++", + "t": "source.diff meta.diff.header.to-file punctuation.definition.to-file.diff", + "r": { + "dark_plus": "meta.diff.header: #569CD6", + "light_plus": "meta.diff.header: #000080", + "dark_vs": "meta.diff.header: #569CD6", + "light_vs": "meta.diff.header: #000080", + "hc_black": "meta.diff.header: #000080" + } + }, + { + "c": " tzu\tSat Jan 26 23:30:50 1991", + "t": "source.diff meta.diff.header.to-file", + "r": { + "dark_plus": "meta.diff.header: #569CD6", + "light_plus": "meta.diff.header: #000080", + "dark_vs": "meta.diff.header: #569CD6", + "light_vs": "meta.diff.header: #000080", + "hc_black": "meta.diff.header: #000080" + } + }, + { + "c": "@@", + "t": "source.diff meta.diff.range.unified punctuation.definition.range.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.diff meta.diff.range.unified", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-1,7 +1,6", + "t": "source.diff meta.diff.range.unified meta.toc-list.line-number.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.diff meta.diff.range.unified", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@@", + "t": "source.diff meta.diff.range.unified punctuation.definition.range.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.diff markup.deleted.diff punctuation.definition.deleted.diff", + "r": { + "dark_plus": "markup.deleted: #CE9178", + "light_plus": "markup.deleted: #A31515", + "dark_vs": "markup.deleted: #CE9178", + "light_vs": "markup.deleted: #A31515", + "hc_black": "markup.deleted: #CE9178" + } + }, + { + "c": "The Way that can be told of is not the eternal Way;", + "t": "source.diff markup.deleted.diff", + "r": { + "dark_plus": "markup.deleted: #CE9178", + "light_plus": "markup.deleted: #A31515", + "dark_vs": "markup.deleted: #CE9178", + "light_vs": "markup.deleted: #A31515", + "hc_black": "markup.deleted: #CE9178" + } + }, + { + "c": "-", + "t": "source.diff markup.deleted.diff punctuation.definition.deleted.diff", + "r": { + "dark_plus": "markup.deleted: #CE9178", + "light_plus": "markup.deleted: #A31515", + "dark_vs": "markup.deleted: #CE9178", + "light_vs": "markup.deleted: #A31515", + "hc_black": "markup.deleted: #CE9178" + } + }, + { + "c": "The name that can be named is not the eternal name.", + "t": "source.diff markup.deleted.diff", + "r": { + "dark_plus": "markup.deleted: #CE9178", + "light_plus": "markup.deleted: #A31515", + "dark_vs": "markup.deleted: #CE9178", + "light_vs": "markup.deleted: #A31515", + "hc_black": "markup.deleted: #CE9178" + } + }, + { + "c": " The Nameless is the origin of Heaven and Earth;", + "t": "source.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.diff markup.deleted.diff punctuation.definition.deleted.diff", + "r": { + "dark_plus": "markup.deleted: #CE9178", + "light_plus": "markup.deleted: #A31515", + "dark_vs": "markup.deleted: #CE9178", + "light_vs": "markup.deleted: #A31515", + "hc_black": "markup.deleted: #CE9178" + } + }, + { + "c": "The Named is the mother of all things.", + "t": "source.diff markup.deleted.diff", + "r": { + "dark_plus": "markup.deleted: #CE9178", + "light_plus": "markup.deleted: #A31515", + "dark_vs": "markup.deleted: #CE9178", + "light_vs": "markup.deleted: #A31515", + "hc_black": "markup.deleted: #CE9178" + } + }, + { + "c": "+", + "t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff", + "r": { + "dark_plus": "markup.inserted: #B5CEA8", + "light_plus": "markup.inserted: #09885A", + "dark_vs": "markup.inserted: #B5CEA8", + "light_vs": "markup.inserted: #09885A", + "hc_black": "markup.inserted: #B5CEA8" + } + }, + { + "c": "The named is the mother of all things.", + "t": "source.diff markup.inserted.diff", + "r": { + "dark_plus": "markup.inserted: #B5CEA8", + "light_plus": "markup.inserted: #09885A", + "dark_vs": "markup.inserted: #B5CEA8", + "light_vs": "markup.inserted: #09885A", + "hc_black": "markup.inserted: #B5CEA8" + } + }, + { + "c": "+", + "t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff", + "r": { + "dark_plus": "markup.inserted: #B5CEA8", + "light_plus": "markup.inserted: #09885A", + "dark_vs": "markup.inserted: #B5CEA8", + "light_vs": "markup.inserted: #09885A", + "hc_black": "markup.inserted: #B5CEA8" + } + }, + { + "c": " Therefore let there always be non-being,", + "t": "source.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " so we may see their subtlety,", + "t": "source.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " And let there always be being,", + "t": "source.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@@", + "t": "source.diff meta.diff.range.unified punctuation.definition.range.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.diff meta.diff.range.unified", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-9,3 +8,6", + "t": "source.diff meta.diff.range.unified meta.toc-list.line-number.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.diff meta.diff.range.unified", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@@", + "t": "source.diff meta.diff.range.unified punctuation.definition.range.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " The two are the same,", + "t": "source.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " But after they are produced,", + "t": "source.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " they have different names.", + "t": "source.diff", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "+", + "t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff", + "r": { + "dark_plus": "markup.inserted: #B5CEA8", + "light_plus": "markup.inserted: #09885A", + "dark_vs": "markup.inserted: #B5CEA8", + "light_vs": "markup.inserted: #09885A", + "hc_black": "markup.inserted: #B5CEA8" + } + }, + { + "c": "They both may be called deep and profound.", + "t": "source.diff markup.inserted.diff", + "r": { + "dark_plus": "markup.inserted: #B5CEA8", + "light_plus": "markup.inserted: #09885A", + "dark_vs": "markup.inserted: #B5CEA8", + "light_vs": "markup.inserted: #09885A", + "hc_black": "markup.inserted: #B5CEA8" + } + }, + { + "c": "+", + "t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff", + "r": { + "dark_plus": "markup.inserted: #B5CEA8", + "light_plus": "markup.inserted: #09885A", + "dark_vs": "markup.inserted: #B5CEA8", + "light_vs": "markup.inserted: #09885A", + "hc_black": "markup.inserted: #B5CEA8" + } + }, + { + "c": "Deeper and more profound,", + "t": "source.diff markup.inserted.diff", + "r": { + "dark_plus": "markup.inserted: #B5CEA8", + "light_plus": "markup.inserted: #09885A", + "dark_vs": "markup.inserted: #B5CEA8", + "light_vs": "markup.inserted: #09885A", + "hc_black": "markup.inserted: #B5CEA8" + } + }, + { + "c": "+", + "t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff", + "r": { + "dark_plus": "markup.inserted: #B5CEA8", + "light_plus": "markup.inserted: #09885A", + "dark_vs": "markup.inserted: #B5CEA8", + "light_vs": "markup.inserted: #09885A", + "hc_black": "markup.inserted: #B5CEA8" + } + }, + { + "c": "The door of all subtleties!", + "t": "source.diff markup.inserted.diff", + "r": { + "dark_plus": "markup.inserted: #B5CEA8", + "light_plus": "markup.inserted: #09885A", + "dark_vs": "markup.inserted: #B5CEA8", + "light_vs": "markup.inserted: #09885A", + "hc_black": "markup.inserted: #B5CEA8" + } + } +] \ No newline at end of file diff --git a/extensions/docker/.vscodeignore b/extensions/docker/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/docker/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/docker/OSSREADME.json b/extensions/docker/OSSREADME.json new file mode 100644 index 0000000000..28c0191ec1 --- /dev/null +++ b/extensions/docker/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[{ + "name": "language-docker", + "version": "0.0.0", + "license": "Apache2", + "repositoryURL": "https://github.com/moby/moby", + "description": "The file syntaxes/Dockerfile.tmLanguage was included from https://github.com/moby/moby/blob/master/contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage." +}] diff --git a/extensions/docker/language-configuration.json b/extensions/docker/language-configuration.json new file mode 100644 index 0000000000..cab4f6602f --- /dev/null +++ b/extensions/docker/language-configuration.json @@ -0,0 +1,24 @@ +{ + "comments": { + "lineComment": "#" + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ] +} \ No newline at end of file diff --git a/extensions/docker/package.json b/extensions/docker/package.json new file mode 100644 index 0000000000..e58f082641 --- /dev/null +++ b/extensions/docker/package.json @@ -0,0 +1,23 @@ +{ + "name": "docker", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "scripts": { + "update-grammar": "node ../../build/npm/update-grammar.js moby/moby contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage ./syntaxes/docker.tmLanguage.json" + }, + "contributes": { + "languages": [{ + "id": "dockerfile", + "extensions": [ ".dockerfile" ], + "filenames": [ "Dockerfile" ], + "aliases": [ "Dockerfile" ], + "configuration": "./language-configuration.json" + }], + "grammars": [{ + "language": "dockerfile", + "scopeName": "source.dockerfile", + "path": "./syntaxes/docker.tmLanguage.json" + }] + } +} \ No newline at end of file diff --git a/extensions/docker/syntaxes/docker.tmLanguage.json b/extensions/docker/syntaxes/docker.tmLanguage.json new file mode 100644 index 0000000000..44a028e785 --- /dev/null +++ b/extensions/docker/syntaxes/docker.tmLanguage.json @@ -0,0 +1,106 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/moby/moby/blob/master/contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/moby/moby/commit/abd39744c6f3ed854500e423f5fabf952165161f", + "fileTypes": [ + "Dockerfile" + ], + "name": "Dockerfile", + "patterns": [ + { + "captures": { + "1": { + "name": "keyword.other.special-method.dockerfile" + }, + "2": { + "name": "keyword.other.special-method.dockerfile" + } + }, + "match": "^\\s*\\b(?i:(FROM))\\b.*?\\b(?i:(AS))\\b" + }, + { + "captures": { + "1": { + "name": "keyword.control.dockerfile" + }, + "2": { + "name": "keyword.other.special-method.dockerfile" + } + }, + "match": "^\\s*(?i:(ONBUILD)\\s+)?(?i:(ADD|ARG|CMD|COPY|ENTRYPOINT|ENV|EXPOSE|FROM|HEALTHCHECK|LABEL|MAINTAINER|RUN|SHELL|STOPSIGNAL|USER|VOLUME|WORKDIR))\\s" + }, + { + "captures": { + "1": { + "name": "keyword.operator.dockerfile" + }, + "2": { + "name": "keyword.other.special-method.dockerfile" + } + }, + "match": "^\\s*(?i:(ONBUILD)\\s+)?(?i:(CMD|ENTRYPOINT))\\s" + }, + { + "begin": "\"", + "beginCaptures": { + "1": { + "name": "punctuation.definition.string.begin.dockerfile" + } + }, + "end": "\"", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.dockerfile" + } + }, + "name": "string.quoted.double.dockerfile", + "patterns": [ + { + "match": "\\\\.", + "name": "constant.character.escaped.dockerfile" + } + ] + }, + { + "begin": "'", + "beginCaptures": { + "1": { + "name": "punctuation.definition.string.begin.dockerfile" + } + }, + "end": "'", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.dockerfile" + } + }, + "name": "string.quoted.single.dockerfile", + "patterns": [ + { + "match": "\\\\.", + "name": "constant.character.escaped.dockerfile" + } + ] + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.comment.leading.dockerfile" + }, + "2": { + "name": "comment.line.number-sign.dockerfile" + }, + "3": { + "name": "punctuation.definition.comment.dockerfile" + } + }, + "comment": "comment.line", + "match": "^(\\s*)((#).*$\\n?)" + } + ], + "scopeName": "source.dockerfile", + "uuid": "a39d8795-59d2-49af-aa00-fe74ee29576e" +} \ No newline at end of file diff --git a/extensions/docker/test/colorize-fixtures/Dockerfile b/extensions/docker/test/colorize-fixtures/Dockerfile new file mode 100644 index 0000000000..e12fffa3e4 --- /dev/null +++ b/extensions/docker/test/colorize-fixtures/Dockerfile @@ -0,0 +1,14 @@ +FROM ubuntu +MAINTAINER Kimbro Staken + +RUN apt-get install -y software-properties-common python +RUN add-apt-repository ppa:chris-lea/node.js +RUN echo "deb http://us.archive.ubuntu.com/ubuntu/ precise universe" >> /etc/apt/sources.list +RUN apt-get update +RUN apt-get install -y nodejs +#RUN apt-get install -y nodejs=0.6.12~dfsg1-1ubuntu1 +RUN mkdir /var/www + +ADD app.js /var/www/app.js + +CMD ["/usr/bin/node", "/var/www/app.js"] \ No newline at end of file diff --git a/extensions/docker/test/colorize-results/Dockerfile.json b/extensions/docker/test/colorize-results/Dockerfile.json new file mode 100644 index 0000000000..a18ec445c0 --- /dev/null +++ b/extensions/docker/test/colorize-results/Dockerfile.json @@ -0,0 +1,310 @@ +[ + { + "c": "FROM", + "t": "source.dockerfile keyword.other.special-method.dockerfile", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ubuntu", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "MAINTAINER", + "t": "source.dockerfile keyword.other.special-method.dockerfile", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " Kimbro Staken", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "RUN", + "t": "source.dockerfile keyword.other.special-method.dockerfile", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " apt-get install -y software-properties-common python", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "RUN", + "t": "source.dockerfile keyword.other.special-method.dockerfile", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " add-apt-repository ppa:chris-lea/node.js", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "RUN", + "t": "source.dockerfile keyword.other.special-method.dockerfile", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " echo ", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"deb http://us.archive.ubuntu.com/ubuntu/ precise universe\"", + "t": "source.dockerfile string.quoted.double.dockerfile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " >> /etc/apt/sources.list", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "RUN", + "t": "source.dockerfile keyword.other.special-method.dockerfile", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " apt-get update", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "RUN", + "t": "source.dockerfile keyword.other.special-method.dockerfile", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " apt-get install -y nodejs", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.dockerfile comment.line.number-sign.dockerfile punctuation.definition.comment.dockerfile", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "RUN apt-get install -y nodejs=0.6.12~dfsg1-1ubuntu1", + "t": "source.dockerfile comment.line.number-sign.dockerfile", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "RUN", + "t": "source.dockerfile keyword.other.special-method.dockerfile", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " mkdir /var/www", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "ADD", + "t": "source.dockerfile keyword.other.special-method.dockerfile", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " app.js /var/www/app.js", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "CMD", + "t": "source.dockerfile keyword.other.special-method.dockerfile", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " [", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"/usr/bin/node\"", + "t": "source.dockerfile string.quoted.double.dockerfile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ", ", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"/var/www/app.js\"", + "t": "source.dockerfile string.quoted.double.dockerfile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "] ", + "t": "source.dockerfile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + } +] \ No newline at end of file diff --git a/extensions/extension-editing/.vscodeignore b/extensions/extension-editing/.vscodeignore new file mode 100644 index 0000000000..24428a6f75 --- /dev/null +++ b/extensions/extension-editing/.vscodeignore @@ -0,0 +1,4 @@ +test/** +src/** +tsconfig.json +npm-shrinkwrap.json \ No newline at end of file diff --git a/extensions/extension-editing/npm-shrinkwrap.json b/extensions/extension-editing/npm-shrinkwrap.json new file mode 100644 index 0000000000..c546fa26f8 --- /dev/null +++ b/extensions/extension-editing/npm-shrinkwrap.json @@ -0,0 +1,61 @@ +{ + "name": "extension-editing", + "version": "0.0.1", + "dependencies": { + "@types/node": { + "version": "6.0.78", + "from": "@types/node@>=6.0.46 <7.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.78.tgz" + }, + "argparse": { + "version": "1.0.9", + "from": "argparse@>=1.0.7 <2.0.0", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz" + }, + "entities": { + "version": "1.1.1", + "from": "entities@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + }, + "jsonc-parser": { + "version": "0.3.1", + "from": "jsonc-parser@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-0.3.1.tgz" + }, + "linkify-it": { + "version": "2.0.3", + "from": "linkify-it@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz" + }, + "markdown-it": { + "version": "8.3.1", + "from": "markdown-it@>=8.3.1 <9.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.3.1.tgz" + }, + "mdurl": { + "version": "1.0.1", + "from": "mdurl@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz" + }, + "parse5": { + "version": "3.0.2", + "from": "parse5@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.2.tgz" + }, + "sprintf-js": { + "version": "1.0.3", + "from": "sprintf-js@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + }, + "uc.micro": { + "version": "1.0.3", + "from": "uc.micro@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.3.tgz" + }, + "vscode-nls": { + "version": "2.0.2", + "from": "vscode-nls@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + } + } +} diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json new file mode 100644 index 0000000000..36f1ae658a --- /dev/null +++ b/extensions/extension-editing/package.json @@ -0,0 +1,52 @@ +{ + "name": "extension-editing", + "version": "0.0.1", + "publisher": "vscode", + "engines": { + "vscode": "^1.4.0" + }, + "categories": [ + "Languages", + "Other" + ], + "activationEvents": [ + "onLanguage:json", + "onLanguage:markdown", + "onLanguage:typescript" + ], + "main": "./out/extension", + "scripts": { + "compile": "gulp compile-extension:extension-editing", + "watch": "gulp watch-extension:extension-editing" + }, + "dependencies": { + "jsonc-parser": "^0.3.1", + "markdown-it": "^8.3.1", + "parse5": "^3.0.2", + "vscode-nls": "^2.0.1" + }, + "contributes": { + "jsonValidation": [ + { + "fileMatch": "package.json", + "url": "vscode://schemas/vscode-extensions" + }, + { + "fileMatch": "*language-configuration.json", + "url": "vscode://schemas/language-configuration" + }, + { + "fileMatch": "*icon-theme.json", + "url": "vscode://schemas/icon-theme" + }, + { + "fileMatch": "*color-theme.json", + "url": "vscode://schemas/color-theme" + } + ] + }, + "devDependencies": { + "@types/markdown-it": "0.0.2", + "@types/node": "^7.0.4" + } +} diff --git a/extensions/extension-editing/src/extension.ts b/extensions/extension-editing/src/extension.ts new file mode 100644 index 0000000000..459e8403c2 --- /dev/null +++ b/extensions/extension-editing/src/extension.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as vscode from 'vscode'; +import * as ts from 'typescript'; +import { PackageDocument } from './packageDocumentHelper'; +import { ExtensionLinter } from './extensionLinter'; + +export function activate(context: vscode.ExtensionContext) { + const registration = vscode.languages.registerDocumentLinkProvider({ language: 'typescript', pattern: '**/vscode.d.ts' }, _linkProvider); + context.subscriptions.push(registration); + + //package.json suggestions + context.subscriptions.push(registerPackageDocumentCompletions()); + + context.subscriptions.push(new ExtensionLinter(context)); +} + +const _linkProvider = new class implements vscode.DocumentLinkProvider { + + private _cachedResult: { key: string; links: vscode.DocumentLink[] }; + private _linkPattern = /[^!]\[.*?\]\(#(.*?)\)/g; + + async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + const key = `${document.uri.toString()}@${document.version}`; + if (!this._cachedResult || this._cachedResult.key !== key) { + const links = await this._computeDocumentLinks(document); + this._cachedResult = { key, links }; + } + return this._cachedResult.links; + } + + private async _computeDocumentLinks(document: vscode.TextDocument): Promise { + + const results: vscode.DocumentLink[] = []; + const text = document.getText(); + const lookUp = await ast.createNamedNodeLookUp(text); + + this._linkPattern.lastIndex = 0; + let match: RegExpMatchArray; + while ((match = this._linkPattern.exec(text))) { + + const offset = lookUp(match[1]); + if (offset === -1) { + console.warn(match[1]); + continue; + } + + const targetPos = document.positionAt(offset); + const linkEnd = document.positionAt(this._linkPattern.lastIndex - 1); + const linkStart = linkEnd.translate({ characterDelta: -(1 + match[1].length) }); + + results.push(new vscode.DocumentLink( + new vscode.Range(linkStart, linkEnd), + document.uri.with({ fragment: `${1 + targetPos.line}` }))); + } + + return results; + } +}; + +namespace ast { + + export interface NamedNodeLookUp { + (dottedName: string): number; + } + + export async function createNamedNodeLookUp(str: string): Promise { + + const ts = await import('typescript'); + + const sourceFile = ts.createSourceFile('fake.d.ts', str, ts.ScriptTarget.Latest); + + const identifiers: string[] = []; + const spans: number[] = []; + + ts.forEachChild(sourceFile, function visit(node: ts.Node) { + const declIdent = (node).name; + if (declIdent && declIdent.kind === ts.SyntaxKind.Identifier) { + identifiers.push((declIdent).text); + spans.push(node.pos, node.end); + } + ts.forEachChild(node, visit); + }); + + return function (dottedName: string): number { + let start = -1; + let end = Number.MAX_VALUE; + + for (let name of dottedName.split('.')) { + let idx: number = -1; + while ((idx = identifiers.indexOf(name, idx + 1)) >= 0) { + let myStart = spans[2 * idx]; + let myEnd = spans[2 * idx + 1]; + if (myStart >= start && myEnd <= end) { + start = myStart; + end = myEnd; + break; + } + } + if (idx < 0) { + return -1; + } + } + return start; + }; + } +} + +function registerPackageDocumentCompletions(): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider({ language: 'json', pattern: '**/package.json' }, { + provideCompletionItems(document, position, token) { + return new PackageDocument(document).provideCompletionItems(position, token); + } + }); +} diff --git a/extensions/extension-editing/src/extensionLinter.ts b/extensions/extension-editing/src/extensionLinter.ts new file mode 100644 index 0000000000..6654fc9613 --- /dev/null +++ b/extensions/extension-editing/src/extensionLinter.ts @@ -0,0 +1,361 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import * as path from 'path'; + +import { parseTree, findNodeAtLocation, Node as JsonNode } from 'jsonc-parser'; +import * as nls from 'vscode-nls'; +import * as MarkdownIt from 'markdown-it'; +import * as parse5 from 'parse5'; + +import { languages, workspace, Disposable, ExtensionContext, TextDocument, Uri, Diagnostic, Range, DiagnosticSeverity, Position } from 'vscode'; + +const product = require('../../../product.json'); +const allowedBadgeProviders: string[] = (product.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase()); + +const localize = nls.loadMessageBundle(); + +const httpsRequired = localize('httpsRequired', "Images must use the HTTPS protocol."); +const svgsNotValid = localize('svgsNotValid', "SVGs are not a valid image source."); +const embeddedSvgsNotValid = localize('embeddedSvgsNotValid', "Embedded SVGs are not a valid image source."); +const dataUrlsNotValid = localize('dataUrlsNotValid', "Data URLs are not a valid image source."); +const relativeUrlRequiresHttpsRepository = localize('relativeUrlRequiresHttpsRepository', "Relative image URLs require a repository with HTTPS protocol to be specified in the package.json."); +const relativeIconUrlRequiresHttpsRepository = localize('relativeIconUrlRequiresHttpsRepository', "An icon requires a repository with HTTPS protocol to be specified in this package.json."); +const relativeBadgeUrlRequiresHttpsRepository = localize('relativeBadgeUrlRequiresHttpsRepository', "Relative badge URLs require a repository with HTTPS protocol to be specified in this package.json."); + +enum Context { + ICON, + BADGE, + MARKDOWN +} + +interface TokenAndPosition { + token: MarkdownIt.Token; + begin: number; + end: number; +} + +interface PackageJsonInfo { + isExtension: boolean; + hasHttpsRepository: boolean; +} + +export class ExtensionLinter { + + private diagnosticsCollection = languages.createDiagnosticCollection('extension-editing'); + private fileWatcher = workspace.createFileSystemWatcher('**/package.json'); + private disposables: Disposable[] = [this.diagnosticsCollection, this.fileWatcher]; + + private folderToPackageJsonInfo: Record = {}; + private packageJsonQ = new Set(); + private readmeQ = new Set(); + private timer: NodeJS.Timer; + private markdownIt = new MarkdownIt(); + + constructor(private context: ExtensionContext) { + this.disposables.push( + workspace.onDidOpenTextDocument(document => this.queue(document)), + workspace.onDidChangeTextDocument(event => this.queue(event.document)), + workspace.onDidCloseTextDocument(document => this.clear(document)), + this.fileWatcher.onDidChange(uri => this.packageJsonChanged(this.getUriFolder(uri))), + this.fileWatcher.onDidCreate(uri => this.packageJsonChanged(this.getUriFolder(uri))), + this.fileWatcher.onDidDelete(uri => this.packageJsonChanged(this.getUriFolder(uri))), + ); + workspace.textDocuments.forEach(document => this.queue(document)); + } + + private queue(document: TextDocument) { + const p = document.uri.path; + if (document.languageId === 'json' && endsWith(p, '/package.json')) { + this.packageJsonQ.add(document); + this.startTimer(); + } + this.queueReadme(document); + } + + private queueReadme(document: TextDocument) { + const p = document.uri.path; + if (document.languageId === 'markdown' && (endsWith(p.toLowerCase(), '/readme.md') || endsWith(p.toLowerCase(), '/changelog.md'))) { + this.readmeQ.add(document); + this.startTimer(); + } + } + + private startTimer() { + if (this.timer) { + clearTimeout(this.timer); + } + this.timer = setTimeout(() => { + this.lint() + .catch(console.error); + }, 300); + } + + private async lint() { + this.lintPackageJson(); + await this.lintReadme(); + } + + private lintPackageJson() { + this.packageJsonQ.forEach(document => { + this.packageJsonQ.delete(document); + if (document.isClosed) { + return; + } + + const diagnostics: Diagnostic[] = []; + + const tree = parseTree(document.getText()); + const info = this.readPackageJsonInfo(this.getUriFolder(document.uri), tree); + if (info.isExtension) { + + const icon = findNodeAtLocation(tree, ['icon']); + if (icon && icon.type === 'string') { + this.addDiagnostics(diagnostics, document, icon.offset + 1, icon.offset + icon.length - 1, icon.value, Context.ICON, info); + } + + const badges = findNodeAtLocation(tree, ['badges']); + if (badges && badges.type === 'array') { + badges.children.map(child => findNodeAtLocation(child, ['url'])) + .filter(url => url && url.type === 'string') + .map(url => this.addDiagnostics(diagnostics, document, url.offset + 1, url.offset + url.length - 1, url.value, Context.BADGE, info)); + } + + } + this.diagnosticsCollection.set(document.uri, diagnostics); + }); + } + + private async lintReadme() { + for (const document of Array.from(this.readmeQ)) { + this.readmeQ.delete(document); + if (document.isClosed) { + return; + } + + const folder = this.getUriFolder(document.uri); + let info = this.folderToPackageJsonInfo[folder.toString()]; + if (!info) { + const tree = await this.loadPackageJson(folder); + info = this.readPackageJsonInfo(folder, tree); + } + if (!info.isExtension) { + this.diagnosticsCollection.set(document.uri, []); + return; + } + + const text = document.getText(); + const tokens = this.markdownIt.parse(text, {}); + const tokensAndPositions = (function toTokensAndPositions(this: ExtensionLinter, tokens: MarkdownIt.Token[], begin = 0, end = text.length): TokenAndPosition[] { + const tokensAndPositions = tokens.map(token => { + if (token.map) { + const tokenBegin = document.offsetAt(new Position(token.map[0], 0)); + const tokenEnd = begin = document.offsetAt(new Position(token.map[1], 0)); + return { + token, + begin: tokenBegin, + end: tokenEnd + }; + } + const image = token.type === 'image' && this.locateToken(text, begin, end, token, token.attrGet('src')); + const other = image || this.locateToken(text, begin, end, token, token.content); + return other || { + token, + begin, + end: begin + }; + }); + return tokensAndPositions.concat( + ...tokensAndPositions.filter(tnp => tnp.token.children && tnp.token.children.length) + .map(tnp => toTokensAndPositions.call(this, tnp.token.children, tnp.begin, tnp.end)) + ); + }).call(this, tokens); + + const diagnostics: Diagnostic[] = []; + + tokensAndPositions.filter(tnp => tnp.token.type === 'image' && tnp.token.attrGet('src')) + .map(inp => { + const src = inp.token.attrGet('src'); + const begin = text.indexOf(src, inp.begin); + if (begin !== -1 && begin < inp.end) { + this.addDiagnostics(diagnostics, document, begin, begin + src.length, src, Context.MARKDOWN, info); + } else { + const content = inp.token.content; + const begin = text.indexOf(content, inp.begin); + if (begin !== -1 && begin < inp.end) { + this.addDiagnostics(diagnostics, document, begin, begin + content.length, src, Context.MARKDOWN, info); + } + } + }); + + let svgStart: Diagnostic; + tokensAndPositions.filter(tnp => tnp.token.type === 'text' && tnp.token.content) + .map(tnp => { + const parser = new parse5.SAXParser({ locationInfo: true }); + parser.on('startTag', (name, attrs, selfClosing, location) => { + if (name === 'img') { + const src = attrs.find(a => a.name === 'src'); + if (src && src.value) { + const begin = text.indexOf(src.value, tnp.begin + location.startOffset); + if (begin !== -1 && begin < tnp.end) { + this.addDiagnostics(diagnostics, document, begin, begin + src.value.length, src.value, Context.MARKDOWN, info); + } + } + } else if (name === 'svg') { + const begin = tnp.begin + location.startOffset; + const end = tnp.begin + location.endOffset; + const range = new Range(document.positionAt(begin), document.positionAt(end)); + svgStart = new Diagnostic(range, embeddedSvgsNotValid, DiagnosticSeverity.Warning); + diagnostics.push(svgStart); + } + }); + parser.on('endTag', (name, location) => { + if (name === 'svg' && svgStart) { + const end = tnp.begin + location.endOffset; + svgStart.range = new Range(svgStart.range.start, document.positionAt(end)); + } + }); + parser.write(tnp.token.content); + parser.end(); + }); + + this.diagnosticsCollection.set(document.uri, diagnostics); + }; + } + + private locateToken(text: string, begin: number, end: number, token: MarkdownIt.Token, content: string) { + if (content) { + const tokenBegin = text.indexOf(content, begin); + if (tokenBegin !== -1) { + const tokenEnd = tokenBegin + content.length; + if (tokenEnd <= end) { + begin = tokenEnd; + return { + token, + begin: tokenBegin, + end: tokenEnd + }; + } + } + } + } + + private readPackageJsonInfo(folder: Uri, tree: JsonNode) { + const engine = tree && findNodeAtLocation(tree, ['engines', 'vscode']); + const repo = tree && findNodeAtLocation(tree, ['repository', 'url']); + const info: PackageJsonInfo = { + isExtension: !!(engine && engine.type === 'string'), + hasHttpsRepository: !!(repo && repo.type === 'string' && repo.value && parseUri(repo.value).scheme.toLowerCase() === 'https') + }; + const str = folder.toString(); + const oldInfo = this.folderToPackageJsonInfo[str]; + if (oldInfo && (oldInfo.isExtension !== info.isExtension || oldInfo.hasHttpsRepository !== info.hasHttpsRepository)) { + this.packageJsonChanged(folder); // clears this.folderToPackageJsonInfo[str] + } + this.folderToPackageJsonInfo[str] = info; + return info; + } + + private async loadPackageJson(folder: Uri) { + const file = folder.with({ path: path.posix.join(folder.path, 'package.json') }); + const exists = await fileExists(file.fsPath); + if (!exists) { + return undefined; + } + const document = await workspace.openTextDocument(file); + return parseTree(document.getText()); + } + + private packageJsonChanged(folder: Uri) { + delete this.folderToPackageJsonInfo[folder.toString()]; + const str = folder.toString().toLowerCase(); + workspace.textDocuments.filter(document => this.getUriFolder(document.uri).toString().toLowerCase() === str) + .forEach(document => this.queueReadme(document)); + } + + private getUriFolder(uri: Uri) { + return uri.with({ path: path.posix.dirname(uri.path) }); + } + + private addDiagnostics(diagnostics: Diagnostic[], document: TextDocument, begin: number, end: number, src: string, context: Context, info: PackageJsonInfo) { + const uri = parseUri(src); + const scheme = uri.scheme.toLowerCase(); + + if (scheme && scheme !== 'https' && scheme !== 'data') { + const range = new Range(document.positionAt(begin), document.positionAt(end)); + diagnostics.push(new Diagnostic(range, httpsRequired, DiagnosticSeverity.Warning)); + } + + if (scheme === 'data') { + const range = new Range(document.positionAt(begin), document.positionAt(end)); + diagnostics.push(new Diagnostic(range, dataUrlsNotValid, DiagnosticSeverity.Warning)); + } + + if (!scheme && !info.hasHttpsRepository) { + const range = new Range(document.positionAt(begin), document.positionAt(end)); + let message = (() => { + switch (context) { + case Context.ICON: return relativeIconUrlRequiresHttpsRepository; + case Context.BADGE: return relativeBadgeUrlRequiresHttpsRepository; + default: return relativeUrlRequiresHttpsRepository; + } + })(); + diagnostics.push(new Diagnostic(range, message, DiagnosticSeverity.Warning)); + } + + if (endsWith(uri.path.toLowerCase(), '.svg') && allowedBadgeProviders.indexOf(uri.authority.toLowerCase()) === -1) { + const range = new Range(document.positionAt(begin), document.positionAt(end)); + diagnostics.push(new Diagnostic(range, svgsNotValid, DiagnosticSeverity.Warning)); + } + } + + private clear(document: TextDocument) { + this.diagnosticsCollection.delete(document.uri); + this.packageJsonQ.delete(document); + } + + public dispose() { + this.disposables.forEach(d => d.dispose()); + this.disposables = []; + } +} + +function endsWith(haystack: string, needle: string): boolean { + let diff = haystack.length - needle.length; + if (diff > 0) { + return haystack.indexOf(needle, diff) === diff; + } else if (diff === 0) { + return haystack === needle; + } else { + return false; + } +} + +function fileExists(path: string): Promise { + return new Promise((resolve, reject) => { + fs.lstat(path, (err, stats) => { + if (!err) { + resolve(true); + } else if (err.code === 'ENOENT') { + resolve(false); + } else { + reject(err); + } + }); + }); +} + +function parseUri(src: string) { + try { + return Uri.parse(src); + } catch (err) { + try { + return Uri.parse(encodeURI(src)); + } catch (err) { + return Uri.parse(''); + } + } +} \ No newline at end of file diff --git a/extensions/extension-editing/src/packageDocumentHelper.ts b/extensions/extension-editing/src/packageDocumentHelper.ts new file mode 100644 index 0000000000..0fb6637f3a --- /dev/null +++ b/extensions/extension-editing/src/packageDocumentHelper.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { getLocation, Location } from 'jsonc-parser'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +export class PackageDocument { + + constructor(private document: vscode.TextDocument) { } + + public provideCompletionItems(position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult { + const location = getLocation(this.document.getText(), this.document.offsetAt(position)); + + if (location.path.length >= 2 && location.path[1] === 'configurationDefaults') { + return this.provideLanguageOverridesCompletionItems(location, position); + } + + } + + private provideLanguageOverridesCompletionItems(location: Location, position: vscode.Position): vscode.ProviderResult { + let range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position); + const text = this.document.getText(range); + + if (location.path.length === 2) { + + let snippet = '"[${1:language}]": {\n\t"$0"\n}'; + + // Suggestion model word matching includes quotes, + // hence exclude the starting quote from the snippet and the range + // ending quote gets replaced + if (text && text.startsWith('"')) { + range = new vscode.Range(new vscode.Position(range.start.line, range.start.character + 1), range.end); + snippet = snippet.substring(1); + } + + return Promise.resolve([this.newSnippetCompletionItem({ + label: localize('languageSpecificEditorSettings', "Language specific editor settings"), + documentation: localize('languageSpecificEditorSettingsDescription', "Override editor settings for language"), + snippet, + range + })]); + } + + if (location.path.length === 3 && location.previousNode && typeof location.previousNode.value === 'string' && location.previousNode.value.startsWith('[')) { + + // Suggestion model word matching includes starting quote and open sqaure bracket + // Hence exclude them from the proposal range + range = new vscode.Range(new vscode.Position(range.start.line, range.start.character + 2), range.end); + + return vscode.languages.getLanguages().then(languages => { + return languages.map(l => { + + // Suggestion model word matching includes closed sqaure bracket and ending quote + // Hence include them in the proposal to replace + return this.newSimpleCompletionItem(l, range, '', l + ']"'); + }); + }); + } + return Promise.resolve([]); + } + + private newSimpleCompletionItem(text: string, range: vscode.Range, description?: string, insertText?: string): vscode.CompletionItem { + const item = new vscode.CompletionItem(text); + item.kind = vscode.CompletionItemKind.Value; + item.detail = description; + item.insertText = insertText ? insertText : text; + item.range = range; + return item; + } + + private newSnippetCompletionItem(o: { label: string; documentation?: string; snippet: string; range: vscode.Range; }): vscode.CompletionItem { + const item = new vscode.CompletionItem(o.label); + item.kind = vscode.CompletionItemKind.Value; + item.documentation = o.documentation; + item.insertText = new vscode.SnippetString(o.snippet); + item.range = o.range; + return item; + } +} diff --git a/extensions/extension-editing/src/typings/ref.d.ts b/extensions/extension-editing/src/typings/ref.d.ts new file mode 100644 index 0000000000..e0aee1c0fd --- /dev/null +++ b/extensions/extension-editing/src/typings/ref.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// diff --git a/extensions/extension-editing/tsconfig.json b/extensions/extension-editing/tsconfig.json new file mode 100644 index 0000000000..a2b5bcdfdd --- /dev/null +++ b/extensions/extension-editing/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "es2015" + ], + "module": "commonjs", + "outDir": "./out" + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/git/.vscodeignore b/extensions/git/.vscodeignore new file mode 100644 index 0000000000..7ba0880ef8 --- /dev/null +++ b/extensions/git/.vscodeignore @@ -0,0 +1,5 @@ +src/** +test/** +out/test/** +tsconfig.json +npm-shrinkwrap.json \ No newline at end of file diff --git a/extensions/git/OSSREADME.json b/extensions/git/OSSREADME.json new file mode 100644 index 0000000000..0e3ddd52fa --- /dev/null +++ b/extensions/git/OSSREADME.json @@ -0,0 +1,29 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[{ + "name": "textmate/git.tmbundle", + "version": "0.0.0", + "license": "MIT", + "repositoryURL": "https://github.com/textmate/git.tmbundle", + "licenseDetail": [ + "Copyright (c) 2008 Tim Harper", + "", + "Permission is hereby granted, free of charge, to any person obtaining", + "a copy of this software and associated documentation files (the\"", + "Software\"), to deal in the Software without restriction, including", + "without limitation the rights to use, copy, modify, merge, publish,", + "distribute, sublicense, and/or sell copies of the Software, and to", + "permit persons to whom the Software is furnished to do so, subject to", + "the following conditions:", + "", + "The above copyright notice and this permission notice shall be", + "included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", + "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", + "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", + "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE", + "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", + "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION", + "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] +}] \ No newline at end of file diff --git a/extensions/git/npm-shrinkwrap.json b/extensions/git/npm-shrinkwrap.json new file mode 100644 index 0000000000..d757c142e9 --- /dev/null +++ b/extensions/git/npm-shrinkwrap.json @@ -0,0 +1,31 @@ +{ + "name": "git", + "version": "0.0.1", + "dependencies": { + "applicationinsights": { + "version": "0.18.0", + "from": "applicationinsights@0.18.0", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz" + }, + "iconv-lite": { + "version": "0.4.15", + "from": "iconv-lite@0.4.15", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz" + }, + "vscode-extension-telemetry": { + "version": "0.0.7", + "from": "vscode-extension-telemetry@>=0.0.8 <0.0.9", + "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz" + }, + "vscode-nls": { + "version": "2.0.2", + "from": "vscode-nls@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + }, + "winreg": { + "version": "1.2.3", + "from": "winreg@1.2.3", + "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz" + } + } +} diff --git a/extensions/git/package.json b/extensions/git/package.json new file mode 100644 index 0000000000..bd646d7f8a --- /dev/null +++ b/extensions/git/package.json @@ -0,0 +1,801 @@ +{ + "name": "git", + "publisher": "vscode", + "displayName": "git", + "description": "Git", + "version": "0.0.1", + "engines": { + "vscode": "^1.5.0" + }, + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "enableProposedApi": true, + "categories": [ + "Other" + ], + "activationEvents": [ + "*" + ], + "main": "./out/main", + "scripts": { + "compile": "gulp compile-extension:git", + "watch": "gulp watch-extension:git" + }, + "contributes": { + "commands": [ + { + "command": "git.clone", + "title": "%command.clone%", + "category": "Git" + }, + { + "command": "git.init", + "title": "%command.init%", + "category": "Git", + "icon": { + "light": "resources/icons/light/git.svg", + "dark": "resources/icons/dark/git.svg" + } + }, + { + "command": "git.refresh", + "title": "%command.refresh%", + "category": "Git", + "icon": { + "light": "resources/icons/light/refresh.svg", + "dark": "resources/icons/dark/refresh.svg" + } + }, + { + "command": "git.openChange", + "title": "%command.openChange%", + "category": "Git", + "icon": { + "light": "resources/icons/light/open-change.svg", + "dark": "resources/icons/dark/open-change.svg" + } + }, + { + "command": "git.openFile", + "title": "%command.openFile%", + "category": "Git", + "icon": { + "light": "resources/icons/light/open-file.svg", + "dark": "resources/icons/dark/open-file.svg" + } + }, + { + "command": "git.openHEADFile", + "title": "%command.openHEADFile%", + "category": "Git" + }, + { + "command": "git.stage", + "title": "%command.stage%", + "category": "Git", + "icon": { + "light": "resources/icons/light/stage.svg", + "dark": "resources/icons/dark/stage.svg" + } + }, + { + "command": "git.stageAll", + "title": "%command.stageAll%", + "category": "Git", + "icon": { + "light": "resources/icons/light/stage.svg", + "dark": "resources/icons/dark/stage.svg" + } + }, + { + "command": "git.stageSelectedRanges", + "title": "%command.stageSelectedRanges%", + "category": "Git" + }, + { + "command": "git.revertSelectedRanges", + "title": "%command.revertSelectedRanges%", + "category": "Git" + }, + { + "command": "git.unstage", + "title": "%command.unstage%", + "category": "Git", + "icon": { + "light": "resources/icons/light/unstage.svg", + "dark": "resources/icons/dark/unstage.svg" + } + }, + { + "command": "git.unstageAll", + "title": "%command.unstageAll%", + "category": "Git", + "icon": { + "light": "resources/icons/light/unstage.svg", + "dark": "resources/icons/dark/unstage.svg" + } + }, + { + "command": "git.unstageSelectedRanges", + "title": "%command.unstageSelectedRanges%", + "category": "Git" + }, + { + "command": "git.clean", + "title": "%command.clean%", + "category": "Git", + "icon": { + "light": "resources/icons/light/clean.svg", + "dark": "resources/icons/dark/clean.svg" + } + }, + { + "command": "git.cleanAll", + "title": "%command.cleanAll%", + "category": "Git", + "icon": { + "light": "resources/icons/light/clean.svg", + "dark": "resources/icons/dark/clean.svg" + } + }, + { + "command": "git.commit", + "title": "%command.commit%", + "category": "Git", + "icon": { + "light": "resources/icons/light/check.svg", + "dark": "resources/icons/dark/check.svg" + } + }, + { + "command": "git.commitStaged", + "title": "%command.commitStaged%", + "category": "Git" + }, + { + "command": "git.commitStagedSigned", + "title": "%command.commitStagedSigned%", + "category": "Git" + }, + { + "command": "git.commitStagedAmend", + "title": "%command.commitStagedAmend%", + "category": "Git" + }, + { + "command": "git.commitAll", + "title": "%command.commitAll%", + "category": "Git" + }, + { + "command": "git.commitAllSigned", + "title": "%command.commitAllSigned%", + "category": "Git" + }, + { + "command": "git.commitAllAmend", + "title": "%command.commitAllAmend%", + "category": "Git" + }, + { + "command": "git.undoCommit", + "title": "%command.undoCommit%", + "category": "Git" + }, + { + "command": "git.checkout", + "title": "%command.checkout%", + "category": "Git" + }, + { + "command": "git.branch", + "title": "%command.branch%", + "category": "Git" + }, + { + "command": "git.deleteBranch", + "title": "%command.deleteBranch%", + "category": "Git" + }, + { + "command": "git.merge", + "title": "%command.merge%", + "category": "Git" + }, + { + "command": "git.createTag", + "title": "%command.createTag%", + "category": "Git" + }, + { + "command": "git.pull", + "title": "%command.pull%", + "category": "Git" + }, + { + "command": "git.pullRebase", + "title": "%command.pullRebase%", + "category": "Git" + }, + { + "command": "git.pullFrom", + "title": "%command.pullFrom%", + "category": "Git" + }, + { + "command": "git.push", + "title": "%command.push%", + "category": "Git" + }, + { + "command": "git.pushTo", + "title": "%command.pushTo%", + "category": "Git" + }, + { + "command": "git.pushWithTags", + "title": "%command.pushWithTags%", + "category": "Git" + }, + { + "command": "git.sync", + "title": "%command.sync%", + "category": "Git" + }, + { + "command": "git.publish", + "title": "%command.publish%", + "category": "Git" + }, + { + "command": "git.showOutput", + "title": "%command.showOutput%", + "category": "Git" + }, + { + "command": "git.ignore", + "title": "%command.ignore%", + "category": "Git" + }, + { + "command": "git.stash", + "title": "%command.stash%", + "category": "Git" + }, + { + "command": "git.stashPop", + "title": "%command.stashPop%", + "category": "Git" + }, + { + "command": "git.stashPopLatest", + "title": "%command.stashPopLatest%", + "category": "Git" + } + ], + "menus": { + "commandPalette": [ + { + "command": "git.clone", + "when": "config.git.enabled" + }, + { + "command": "git.init", + "when": "config.git.enabled" + }, + { + "command": "git.refresh", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.openFile", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.openHEADFile", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.openChange", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.stage", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.stageAll", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.stageSelectedRanges", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.revertSelectedRanges", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.unstage", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.unstageAll", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.unstageSelectedRanges", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.clean", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.cleanAll", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commit", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commitStaged", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commitStagedSigned", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commitStagedAmend", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commitAll", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commitAllSigned", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commitAllAmend", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.undoCommit", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.checkout", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.branch", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.deleteBranch", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.pull", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.pullFrom", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.pullRebase", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.pullFrom", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.merge", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.createTag", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.push", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.pushTo", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.pushWithTags", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.sync", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.publish", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.showOutput", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.ignore", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.stash", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.stashPop", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.stashPopLatest", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + } + ], + "scm/title": [ + { + "command": "git.init", + "group": "navigation", + "when": "config.git.enabled && !scmProvider && gitOpenRepositoryCount == 0" + }, + { + "command": "git.commit", + "group": "navigation", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.refresh", + "group": "navigation", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.sync", + "group": "1_sync", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.pull", + "group": "1_sync", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.pullRebase", + "group": "1_sync", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.pullFrom", + "group": "1_sync", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.push", + "group": "1_sync", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.pushTo", + "group": "1_sync", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.publish", + "group": "2_publish", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.commitStaged", + "group": "3_commit", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.commitStagedSigned", + "group": "3_commit", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.commitStagedAmend", + "group": "3_commit", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.commitAll", + "group": "3_commit", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.commitAllSigned", + "group": "3_commit", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.commitAllAmend", + "group": "3_commit", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.undoCommit", + "group": "3_commit", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.unstageAll", + "group": "4_stage", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.cleanAll", + "group": "4_stage", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.stash", + "group": "5_stash", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.stashPop", + "group": "5_stash", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.stashPopLatest", + "group": "5_stash", + "when": "config.git.enabled && scmProvider == git" + }, + { + "command": "git.showOutput", + "group": "7_repository", + "when": "config.git.enabled && scmProvider == git" + } + ], + "scm/resourceGroup/context": [ + { + "command": "git.stageAll", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == merge", + "group": "1_modification" + }, + { + "command": "git.stageAll", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == merge", + "group": "inline" + }, + { + "command": "git.unstageAll", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == index", + "group": "1_modification" + }, + { + "command": "git.unstageAll", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == index", + "group": "inline" + }, + { + "command": "git.cleanAll", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "1_modification" + }, + { + "command": "git.stageAll", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "1_modification" + }, + { + "command": "git.cleanAll", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "inline" + }, + { + "command": "git.stageAll", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "inline" + } + ], + "scm/resourceState/context": [ + { + "command": "git.stage", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == merge", + "group": "1_modification" + }, + { + "command": "git.stage", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == merge", + "group": "inline" + }, + { + "command": "git.openChange", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == index", + "group": "navigation" + }, + { + "command": "git.openFile", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == index", + "group": "navigation" + }, + { + "command": "git.openHEADFile", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == index", + "group": "navigation" + }, + { + "command": "git.unstage", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == index", + "group": "1_modification" + }, + { + "command": "git.unstage", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == index", + "group": "inline" + }, + { + "command": "git.openChange", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "navigation" + }, + { + "command": "git.openHEADFile", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "navigation" + }, + { + "command": "git.openFile", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "navigation" + }, + { + "command": "git.stage", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "1_modification" + }, + { + "command": "git.clean", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "1_modification" + }, + { + "command": "git.clean", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "inline" + }, + { + "command": "git.stage", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "inline" + }, + { + "command": "git.ignore", + "when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree", + "group": "1_modification@3" + } + ], + "editor/title": [ + { + "command": "git.openFile", + "group": "navigation", + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != extension && resourceScheme != merge-conflict.conflict-diff" + }, + { + "command": "git.openChange", + "group": "navigation", + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme != extension" + }, + { + "command": "git.stageSelectedRanges", + "group": "2_git@1", + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != merge-conflict.conflict-diff" + }, + { + "command": "git.unstageSelectedRanges", + "group": "2_git@2", + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != merge-conflict.conflict-diff" + }, + { + "command": "git.revertSelectedRanges", + "group": "2_git@3", + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != merge-conflict.conflict-diff" + } + ] + }, + "configuration": { + "title": "Git", + "properties": { + "git.enabled": { + "type": "boolean", + "description": "%config.enabled%", + "default": true + }, + "git.path": { + "type": [ + "string", + "null" + ], + "description": "%config.path%", + "default": null, + "isExecutable": true + }, + "git.autorefresh": { + "type": "boolean", + "description": "%config.autorefresh%", + "default": true + }, + "git.autofetch": { + "type": "boolean", + "description": "%config.autofetch%", + "default": true + }, + "git.confirmSync": { + "type": "boolean", + "description": "%config.confirmSync%", + "default": true + }, + "git.countBadge": { + "type": "string", + "enum": [ + "all", + "tracked", + "off" + ], + "description": "%config.countBadge%", + "default": "all" + }, + "git.checkoutType": { + "type": "string", + "enum": [ + "all", + "local", + "tags", + "remote" + ], + "description": "%config.checkoutType%", + "default": "all" + }, + "git.ignoreLegacyWarning": { + "type": "boolean", + "description": "%config.ignoreLegacyWarning%", + "default": false + }, + "git.ignoreLimitWarning": { + "type": "boolean", + "description": "%config.ignoreLimitWarning%", + "default": false + }, + "git.defaultCloneDirectory": { + "type": "string", + "default": null, + "description": "%config.defaultCloneDirectory%" + }, + "git.enableSmartCommit": { + "type": "boolean", + "description": "%config.enableSmartCommit%", + "default": false + }, + "git.enableCommitSigning": { + "type": "boolean", + "description": "%config.enableCommitSigning%", + "default": false + } + } + } + }, + "dependencies": { + "iconv-lite": "0.4.15", + "vscode-extension-telemetry": "0.0.8", + "vscode-nls": "2.0.2" + }, + "devDependencies": { + "@types/mocha": "^2.2.41", + "@types/node": "^7.0.4", + "mocha": "^3.2.0" + } +} \ No newline at end of file diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json new file mode 100644 index 0000000000..da2c9210fd --- /dev/null +++ b/extensions/git/package.nls.json @@ -0,0 +1,58 @@ +{ + "command.clone": "Clone", + "command.init": "Initialize Repository", + "command.close": "Close Repository", + "command.refresh": "Refresh", + "command.openChange": "Open Changes", + "command.openFile": "Open File", + "command.openHEADFile": "Open File (HEAD)", + "command.stage": "Stage Changes", + "command.stageAll": "Stage All Changes", + "command.stageSelectedRanges": "Stage Selected Ranges", + "command.revertSelectedRanges": "Revert Selected Ranges", + "command.unstage": "Unstage Changes", + "command.unstageAll": "Unstage All Changes", + "command.unstageSelectedRanges": "Unstage Selected Ranges", + "command.clean": "Discard Changes", + "command.cleanAll": "Discard All Changes", + "command.commit": "Commit", + "command.commitStaged": "Commit Staged", + "command.commitStagedSigned": "Commit Staged (Signed Off)", + "command.commitStagedAmend": "Commit Staged (Amend)", + "command.commitAll": "Commit All", + "command.commitAllSigned": "Commit All (Signed Off)", + "command.commitAllAmend": "Commit All (Amend)", + "command.undoCommit": "Undo Last Commit", + "command.checkout": "Checkout to...", + "command.branch": "Create Branch...", + "command.deleteBranch": "Delete Branch...", + "command.merge": "Merge Branch...", + "command.createTag": "Create Tag", + "command.pull": "Pull", + "command.pullRebase": "Pull (Rebase)", + "command.pullFrom": "Pull from...", + "command.push": "Push", + "command.pushTo": "Push to...", + "command.pushWithTags": "Push With Tags", + "command.sync": "Sync", + "command.publish": "Publish Branch", + "command.showOutput": "Show Git Output", + "command.ignore": "Add File to .gitignore", + "command.stash": "Stash", + "command.stashPop": "Pop Stash...", + "command.stashPopLatest": "Pop Latest Stash", + "config.enabled": "Whether git is enabled", + "config.path": "Path to the git executable", + "config.autorefresh": "Whether auto refreshing is enabled", + "config.autofetch": "Whether auto fetching is enabled", + "config.enableLongCommitWarning": "Whether long commit messages should be warned about", + "config.confirmSync": "Confirm before synchronizing git repositories", + "config.countBadge": "Controls the git badge counter. `all` counts all changes. `tracked` counts only the tracked changes. `off` turns it off.", + "config.checkoutType": "Controls what type of branches are listed when running `Checkout to...`. `all` shows all refs, `local` shows only the local branchs, `tags` shows only tags and `remote` shows only remote branches.", + "config.ignoreLegacyWarning": "Ignores the legacy Git warning", + "config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository", + "config.defaultCloneDirectory": "The default location where to clone a git repository", + "config.enableSmartCommit": "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." +} \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/check.svg b/extensions/git/resources/icons/dark/check.svg new file mode 100644 index 0000000000..c225b2f597 --- /dev/null +++ b/extensions/git/resources/icons/dark/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/clean.svg b/extensions/git/resources/icons/dark/clean.svg new file mode 100644 index 0000000000..9f17563338 --- /dev/null +++ b/extensions/git/resources/icons/dark/clean.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/git.svg b/extensions/git/resources/icons/dark/git.svg new file mode 100644 index 0000000000..c08b1c2e40 --- /dev/null +++ b/extensions/git/resources/icons/dark/git.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/open-change.svg b/extensions/git/resources/icons/dark/open-change.svg new file mode 100644 index 0000000000..c951728aba --- /dev/null +++ b/extensions/git/resources/icons/dark/open-change.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/open-file.svg b/extensions/git/resources/icons/dark/open-file.svg new file mode 100644 index 0000000000..f6302185aa --- /dev/null +++ b/extensions/git/resources/icons/dark/open-file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/refresh.svg b/extensions/git/resources/icons/dark/refresh.svg new file mode 100644 index 0000000000..d79fdaa4e8 --- /dev/null +++ b/extensions/git/resources/icons/dark/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/stage.svg b/extensions/git/resources/icons/dark/stage.svg new file mode 100644 index 0000000000..3475c1e196 --- /dev/null +++ b/extensions/git/resources/icons/dark/stage.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/status-added.svg b/extensions/git/resources/icons/dark/status-added.svg new file mode 100644 index 0000000000..cdc40f45f1 --- /dev/null +++ b/extensions/git/resources/icons/dark/status-added.svg @@ -0,0 +1,6 @@ + + + + A + + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/status-conflict.svg b/extensions/git/resources/icons/dark/status-conflict.svg new file mode 100644 index 0000000000..53b243c8b9 --- /dev/null +++ b/extensions/git/resources/icons/dark/status-conflict.svg @@ -0,0 +1,6 @@ + + + + C + + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/status-copied.svg b/extensions/git/resources/icons/dark/status-copied.svg new file mode 100644 index 0000000000..7bd78c9427 --- /dev/null +++ b/extensions/git/resources/icons/dark/status-copied.svg @@ -0,0 +1,6 @@ + + + + C + + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/status-deleted.svg b/extensions/git/resources/icons/dark/status-deleted.svg new file mode 100644 index 0000000000..e7596e2e2a --- /dev/null +++ b/extensions/git/resources/icons/dark/status-deleted.svg @@ -0,0 +1,6 @@ + + + + D + + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/status-ignored.svg b/extensions/git/resources/icons/dark/status-ignored.svg new file mode 100644 index 0000000000..85abc367a2 --- /dev/null +++ b/extensions/git/resources/icons/dark/status-ignored.svg @@ -0,0 +1,6 @@ + + + + I + + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/status-modified.svg b/extensions/git/resources/icons/dark/status-modified.svg new file mode 100644 index 0000000000..d0de37d346 --- /dev/null +++ b/extensions/git/resources/icons/dark/status-modified.svg @@ -0,0 +1,6 @@ + + + + M + + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/status-renamed.svg b/extensions/git/resources/icons/dark/status-renamed.svg new file mode 100644 index 0000000000..a77fb41179 --- /dev/null +++ b/extensions/git/resources/icons/dark/status-renamed.svg @@ -0,0 +1,6 @@ + + + + R + + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/status-untracked.svg b/extensions/git/resources/icons/dark/status-untracked.svg new file mode 100644 index 0000000000..c6a48e14f0 --- /dev/null +++ b/extensions/git/resources/icons/dark/status-untracked.svg @@ -0,0 +1,6 @@ + + + + U + + \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/unstage.svg b/extensions/git/resources/icons/dark/unstage.svg new file mode 100644 index 0000000000..2de46fcf5b --- /dev/null +++ b/extensions/git/resources/icons/dark/unstage.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/extensions/git/resources/icons/light/check.svg b/extensions/git/resources/icons/light/check.svg new file mode 100644 index 0000000000..d45df06edf --- /dev/null +++ b/extensions/git/resources/icons/light/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/clean.svg b/extensions/git/resources/icons/light/clean.svg new file mode 100644 index 0000000000..1fa6ba48a1 --- /dev/null +++ b/extensions/git/resources/icons/light/clean.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/git.svg b/extensions/git/resources/icons/light/git.svg new file mode 100644 index 0000000000..d1049a44d0 --- /dev/null +++ b/extensions/git/resources/icons/light/git.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/open-change.svg b/extensions/git/resources/icons/light/open-change.svg new file mode 100644 index 0000000000..3a205509bc --- /dev/null +++ b/extensions/git/resources/icons/light/open-change.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/open-file.svg b/extensions/git/resources/icons/light/open-file.svg new file mode 100644 index 0000000000..fccdf83d46 --- /dev/null +++ b/extensions/git/resources/icons/light/open-file.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/extensions/git/resources/icons/light/refresh.svg b/extensions/git/resources/icons/light/refresh.svg new file mode 100644 index 0000000000..e034574819 --- /dev/null +++ b/extensions/git/resources/icons/light/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/stage.svg b/extensions/git/resources/icons/light/stage.svg new file mode 100644 index 0000000000..bdecdb0e45 --- /dev/null +++ b/extensions/git/resources/icons/light/stage.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/extensions/git/resources/icons/light/status-added.svg b/extensions/git/resources/icons/light/status-added.svg new file mode 100644 index 0000000000..587fc08f5f --- /dev/null +++ b/extensions/git/resources/icons/light/status-added.svg @@ -0,0 +1,6 @@ + + + + A + + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/status-conflict.svg b/extensions/git/resources/icons/light/status-conflict.svg new file mode 100644 index 0000000000..b6088ecd08 --- /dev/null +++ b/extensions/git/resources/icons/light/status-conflict.svg @@ -0,0 +1,6 @@ + + + + C + + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/status-copied.svg b/extensions/git/resources/icons/light/status-copied.svg new file mode 100644 index 0000000000..151fdb2cdb --- /dev/null +++ b/extensions/git/resources/icons/light/status-copied.svg @@ -0,0 +1,6 @@ + + + + C + + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/status-deleted.svg b/extensions/git/resources/icons/light/status-deleted.svg new file mode 100644 index 0000000000..7ed166accf --- /dev/null +++ b/extensions/git/resources/icons/light/status-deleted.svg @@ -0,0 +1,6 @@ + + + + D + + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/status-ignored.svg b/extensions/git/resources/icons/light/status-ignored.svg new file mode 100644 index 0000000000..85abc367a2 --- /dev/null +++ b/extensions/git/resources/icons/light/status-ignored.svg @@ -0,0 +1,6 @@ + + + + I + + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/status-modified.svg b/extensions/git/resources/icons/light/status-modified.svg new file mode 100644 index 0000000000..ff338b8141 --- /dev/null +++ b/extensions/git/resources/icons/light/status-modified.svg @@ -0,0 +1,6 @@ + + + + M + + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/status-renamed.svg b/extensions/git/resources/icons/light/status-renamed.svg new file mode 100644 index 0000000000..a77fb41179 --- /dev/null +++ b/extensions/git/resources/icons/light/status-renamed.svg @@ -0,0 +1,6 @@ + + + + R + + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/status-untracked.svg b/extensions/git/resources/icons/light/status-untracked.svg new file mode 100644 index 0000000000..c6a48e14f0 --- /dev/null +++ b/extensions/git/resources/icons/light/status-untracked.svg @@ -0,0 +1,6 @@ + + + + U + + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/unstage.svg b/extensions/git/resources/icons/light/unstage.svg new file mode 100644 index 0000000000..f5d128b2df --- /dev/null +++ b/extensions/git/resources/icons/light/unstage.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/extensions/git/src/askpass-empty.sh b/extensions/git/src/askpass-empty.sh new file mode 100644 index 0000000000..8fb014e5cc --- /dev/null +++ b/extensions/git/src/askpass-empty.sh @@ -0,0 +1,2 @@ +#!/bin/sh +echo '' \ No newline at end of file diff --git a/extensions/git/src/askpass-main.ts b/extensions/git/src/askpass-main.ts new file mode 100644 index 0000000000..cb29d0ffe1 --- /dev/null +++ b/extensions/git/src/askpass-main.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as http from 'http'; +import * as fs from 'fs'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +function fatal(err: any): void { + console.error(localize('missOrInvalid', "Missing or invalid credentials.")); + console.error(err); + process.exit(1); +} + +function main(argv: string[]): void { + if (argv.length !== 5) { + return fatal('Wrong number of arguments'); + } + + if (!process.env['VSCODE_GIT_ASKPASS_HANDLE']) { + return fatal('Missing handle'); + } + + if (!process.env['VSCODE_GIT_ASKPASS_PIPE']) { + return fatal('Missing pipe'); + } + + if (process.env['VSCODE_GIT_COMMAND'] === 'fetch') { + return fatal('Skip fetch commands'); + } + + const output = process.env['VSCODE_GIT_ASKPASS_PIPE']; + const socketPath = process.env['VSCODE_GIT_ASKPASS_HANDLE']; + const request = argv[2]; + const host = argv[4].substring(1, argv[4].length - 2); + const opts: http.RequestOptions = { + socketPath, + path: '/', + method: 'POST' + }; + + const req = http.request(opts, res => { + if (res.statusCode !== 200) { + return fatal(`Bad status code: ${res.statusCode}`); + } + + const chunks: string[] = []; + res.setEncoding('utf8'); + res.on('data', (d: string) => chunks.push(d)); + res.on('end', () => { + const raw = chunks.join(''); + + try { + const result = JSON.parse(raw); + fs.writeFileSync(output, result + '\n'); + } catch (err) { + return fatal(`Error parsing response`); + } + + setTimeout(() => process.exit(0), 0); + }); + }); + + req.on('error', () => fatal('Error in request')); + req.write(JSON.stringify({ request, host })); + req.end(); +} + +main(process.argv); diff --git a/extensions/git/src/askpass.sh b/extensions/git/src/askpass.sh new file mode 100644 index 0000000000..b6d374f2c3 --- /dev/null +++ b/extensions/git/src/askpass.sh @@ -0,0 +1,5 @@ +#!/bin/sh +VSCODE_GIT_ASKPASS_PIPE=`mktemp` +VSCODE_GIT_ASKPASS_PIPE="$VSCODE_GIT_ASKPASS_PIPE" "$VSCODE_GIT_ASKPASS_NODE" "$VSCODE_GIT_ASKPASS_MAIN" $* +cat $VSCODE_GIT_ASKPASS_PIPE +rm $VSCODE_GIT_ASKPASS_PIPE \ No newline at end of file diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts new file mode 100644 index 0000000000..437f898305 --- /dev/null +++ b/extensions/git/src/askpass.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Disposable, window, InputBoxOptions } from 'vscode'; +import { denodeify } from './util'; +import * as path from 'path'; +import * as http from 'http'; +import * as os from 'os'; +import * as crypto from 'crypto'; + +const randomBytes = denodeify(crypto.randomBytes); + +export interface AskpassEnvironment { + GIT_ASKPASS: string; + ELECTRON_RUN_AS_NODE?: string; + VSCODE_GIT_ASKPASS_NODE?: string; + VSCODE_GIT_ASKPASS_MAIN?: string; + VSCODE_GIT_ASKPASS_HANDLE?: string; +} + +function getIPCHandlePath(nonce: string): string { + if (process.platform === 'win32') { + return `\\\\.\\pipe\\vscode-git-askpass-${nonce}-sock`; + } + + if (process.env['XDG_RUNTIME_DIR']) { + return path.join(process.env['XDG_RUNTIME_DIR'], `vscode-git-askpass-${nonce}.sock`); + } + + return path.join(os.tmpdir(), `vscode-git-askpass-${nonce}.sock`); +} + +export class Askpass implements Disposable { + + private server: http.Server; + private ipcHandlePathPromise: Promise; + private enabled = true; + + constructor() { + this.server = http.createServer((req, res) => this.onRequest(req, res)); + this.ipcHandlePathPromise = this.setup().catch(err => { + console.error(err); + return ''; + }); + } + + private async setup(): Promise { + const buffer = await randomBytes(20); + const nonce = buffer.toString('hex'); + const ipcHandlePath = getIPCHandlePath(nonce); + + try { + this.server.listen(ipcHandlePath); + this.server.on('error', err => console.error(err)); + } catch (err) { + console.error('Could not launch git askpass helper.'); + this.enabled = false; + } + + return ipcHandlePath; + } + + private onRequest(req: http.ServerRequest, res: http.ServerResponse): void { + const chunks: string[] = []; + req.setEncoding('utf8'); + req.on('data', (d: string) => chunks.push(d)); + req.on('end', () => { + const { request, host } = JSON.parse(chunks.join('')); + + this.prompt(host, request).then(result => { + res.writeHead(200); + res.end(JSON.stringify(result)); + }, () => { + res.writeHead(500); + res.end(); + }); + }); + } + + private async prompt(host: string, request: string): Promise { + const options: InputBoxOptions = { + password: /password/i.test(request), + placeHolder: request, + prompt: `Git: ${host}`, + ignoreFocusOut: true + }; + + return await window.showInputBox(options) || ''; + } + + async getEnv(): Promise { + if (!this.enabled) { + return { + GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh') + }; + } + + return { + ELECTRON_RUN_AS_NODE: '1', + GIT_ASKPASS: path.join(__dirname, 'askpass.sh'), + VSCODE_GIT_ASKPASS_NODE: process.execPath, + VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js'), + VSCODE_GIT_ASKPASS_HANDLE: await this.ipcHandlePathPromise + }; + } + + dispose(): void { + this.server.close(); + } +} \ No newline at end of file diff --git a/extensions/git/src/autofetch.ts b/extensions/git/src/autofetch.ts new file mode 100644 index 0000000000..8a41c8648f --- /dev/null +++ b/extensions/git/src/autofetch.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { workspace, Disposable } from 'vscode'; +import { GitErrorCodes } from './git'; +import { Repository } from './repository'; +import { throttle } from './decorators'; + +export class AutoFetcher { + + private static Period = 3 * 60 * 1000 /* three minutes */; + private disposables: Disposable[] = []; + private timer: NodeJS.Timer; + + constructor(private repository: Repository) { + workspace.onDidChangeConfiguration(this.onConfiguration, this, this.disposables); + this.onConfiguration(); + } + + private onConfiguration(): void { + const gitConfig = workspace.getConfiguration('git'); + + if (gitConfig.get('autofetch') === false) { + this.disable(); + } else { + this.enable(); + } + } + + enable(): void { + if (this.timer) { + return; + } + + this.fetch(); + this.timer = setInterval(() => this.fetch(), AutoFetcher.Period); + } + + disable(): void { + clearInterval(this.timer); + } + + @throttle + private async fetch(): Promise { + try { + await this.repository.fetch(); + } catch (err) { + if (err.gitErrorCode === GitErrorCodes.AuthenticationFailed) { + this.disable(); + } + } + } + + dispose(): void { + this.disable(); + this.disposables.forEach(d => d.dispose()); + } +} diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts new file mode 100644 index 0000000000..be10091b3e --- /dev/null +++ b/extensions/git/src/commands.ts @@ -0,0 +1,1412 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Uri, commands, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation } from 'vscode'; +import { Ref, RefType, Git, GitErrorCodes, Branch } from './git'; +import { Repository, Resource, Status, CommitOptions, ResourceGroupType } from './repository'; +import { Model } from './model'; +import { toGitUri, fromGitUri } from './uri'; +import { applyLineChanges, intersectDiffWithRange, toLineRanges, invertLineChange } from './staging'; +import * as path from 'path'; +import * as os from 'os'; +import TelemetryReporter from 'vscode-extension-telemetry'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +class CheckoutItem implements QuickPickItem { + + protected get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } + protected get treeish(): string | undefined { return this.ref.name; } + get label(): string { return this.ref.name || this.shortCommit; } + get description(): string { return this.shortCommit; } + + constructor(protected ref: Ref) { } + + async run(repository: Repository): Promise { + const ref = this.treeish; + + if (!ref) { + return; + } + + await repository.checkout(ref); + } +} + +class CheckoutTagItem extends CheckoutItem { + + get description(): string { + return localize('tag at', "Tag at {0}", this.shortCommit); + } +} + +class CheckoutRemoteHeadItem extends CheckoutItem { + + get description(): string { + return localize('remote branch at', "Remote branch at {0}", this.shortCommit); + } + + protected get treeish(): string | undefined { + if (!this.ref.name) { + return; + } + + const match = /^[^/]+\/(.*)$/.exec(this.ref.name); + return match ? match[1] : this.ref.name; + } +} + +class BranchDeleteItem implements QuickPickItem { + + private get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } + get branchName(): string | undefined { return this.ref.name; } + get label(): string { return this.branchName || ''; } + get description(): string { return this.shortCommit; } + + constructor(private ref: Ref) { } + + async run(repository: Repository, force?: boolean): Promise { + if (!this.branchName) { + return; + } + await repository.deleteBranch(this.branchName, force); + } +} + +class MergeItem implements QuickPickItem { + + get label(): string { return this.ref.name || ''; } + get description(): string { return this.ref.name || ''; } + + constructor(protected ref: Ref) { } + + async run(repository: Repository): Promise { + await repository.merge(this.ref.name! || this.ref.commit!); + } +} + +class CreateBranchItem implements QuickPickItem { + + get label(): string { return localize('create branch', '$(plus) Create new branch'); } + get description(): string { return ''; } + + async run(repository: Repository): Promise { + await commands.executeCommand('git.branch'); + } +} + +interface CommandOptions { + repository?: boolean; + diff?: boolean; +} + +interface Command { + commandId: string; + key: string; + method: Function; + options: CommandOptions; +} + +const Commands: Command[] = []; + +function command(commandId: string, options: CommandOptions = {}): Function { + return (target: any, key: string, descriptor: any) => { + if (!(typeof descriptor.value === 'function')) { + throw new Error('not supported'); + } + + Commands.push({ commandId, key, method: descriptor.value, options }); + }; +} + +export class CommandCenter { + + private disposables: Disposable[]; + + constructor( + private git: Git, + private model: Model, + private outputChannel: OutputChannel, + private telemetryReporter: TelemetryReporter + ) { + this.disposables = Commands.map(({ commandId, key, method, options }) => { + const command = this.createCommand(commandId, key, method, options); + + if (options.diff) { + return commands.registerDiffInformationCommand(commandId, command); + } else { + return commands.registerCommand(commandId, command); + } + }); + } + + @command('git.refresh', { repository: true }) + async refresh(repository: Repository): Promise { + await repository.status(); + } + + @command('git.openResource') + async openResource(resource: Resource): Promise { + await this._openResource(resource, undefined, true, false); + } + + private async _openResource(resource: Resource, preview?: boolean, preserveFocus?: boolean, preserveSelection?: boolean): Promise { + const left = this.getLeftResource(resource); + const right = this.getRightResource(resource); + const title = this.getTitle(resource); + + if (!right) { + // TODO + console.error('oh no'); + return; + } + + const opts: TextDocumentShowOptions = { + preserveFocus, + preview, + viewColumn: window.activeTextEditor && window.activeTextEditor.viewColumn || ViewColumn.One + }; + + const activeTextEditor = window.activeTextEditor; + + if (preserveSelection && activeTextEditor && activeTextEditor.document.uri.fsPath === right.fsPath) { + opts.selection = activeTextEditor.selection; + } + + if (!left) { + const document = await workspace.openTextDocument(right); + await window.showTextDocument(document, opts); + return; + } + + return await commands.executeCommand('vscode.diff', left, right, title, opts); + } + + private getLeftResource(resource: Resource): Uri | undefined { + switch (resource.type) { + case Status.INDEX_MODIFIED: + case Status.INDEX_RENAMED: + return toGitUri(resource.original, 'HEAD'); + + case Status.MODIFIED: + return toGitUri(resource.resourceUri, '~'); + + case Status.DELETED_BY_THEM: + return toGitUri(resource.resourceUri, ''); + } + } + + private getRightResource(resource: Resource): Uri | undefined { + switch (resource.type) { + case Status.INDEX_MODIFIED: + case Status.INDEX_ADDED: + case Status.INDEX_COPIED: + case Status.INDEX_RENAMED: + return toGitUri(resource.resourceUri, ''); + + case Status.INDEX_DELETED: + case Status.DELETED_BY_THEM: + case Status.DELETED: + return toGitUri(resource.resourceUri, 'HEAD'); + + case Status.MODIFIED: + case Status.UNTRACKED: + case Status.IGNORED: + const repository = this.model.getRepository(resource.resourceUri); + + if (!repository) { + return; + } + + const uriString = resource.resourceUri.toString(); + const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); + + if (indexStatus && indexStatus.renameResourceUri) { + return indexStatus.renameResourceUri; + } + + return resource.resourceUri; + + case Status.BOTH_ADDED: + case Status.BOTH_MODIFIED: + return resource.resourceUri; + } + } + + private getTitle(resource: Resource): string { + const basename = path.basename(resource.resourceUri.fsPath); + + switch (resource.type) { + case Status.INDEX_MODIFIED: + case Status.INDEX_RENAMED: + case Status.DELETED_BY_THEM: + return `${basename} (Index)`; + + case Status.MODIFIED: + case Status.BOTH_ADDED: + case Status.BOTH_MODIFIED: + return `${basename} (Working Tree)`; + } + + return ''; + } + + @command('git.clone') + async clone(): Promise { + const url = await window.showInputBox({ + prompt: localize('repourl', "Repository URL"), + ignoreFocusOut: true + }); + + if (!url) { + this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'no_URL' }); + return; + } + + const config = workspace.getConfiguration('git'); + const value = config.get('defaultCloneDirectory') || os.homedir(); + + const parentPath = await window.showInputBox({ + prompt: localize('parent', "Parent Directory"), + value, + ignoreFocusOut: true + }); + + if (!parentPath) { + this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'no_directory' }); + return; + } + + const clonePromise = this.git.clone(url, parentPath); + + + try { + window.withProgress({ location: ProgressLocation.SourceControl, title: localize('cloning', "Cloning git repository...") }, () => clonePromise); + window.withProgress({ location: ProgressLocation.Window, title: localize('cloning', "Cloning git repository...") }, () => clonePromise); + + const repositoryPath = await clonePromise; + + const open = localize('openrepo', "Open Repository"); + const result = await window.showInformationMessage(localize('proposeopen', "Would you like to open the cloned repository?"), open); + + const openFolder = result === open; + this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: openFolder ? 1 : 0 }); + if (openFolder) { + commands.executeCommand('vscode.openFolder', Uri.file(repositoryPath)); + } + } catch (err) { + if (/already exists and is not an empty directory/.test(err && err.stderr || '')) { + this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'directory_not_empty' }); + } else { + this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'error' }); + } + throw err; + } + } + + @command('git.init') + async init(): Promise { + const value = workspace.workspaceFolders && workspace.workspaceFolders.length > 0 + ? workspace.workspaceFolders[0].uri.fsPath + : os.homedir(); + + const path = await window.showInputBox({ + placeHolder: localize('path to init', "Folder path"), + prompt: localize('provide path', "Please provide a folder path to initialize a Git repository"), + value, + ignoreFocusOut: true + }); + + if (!path) { + return; + } + + await this.git.init(path); + await this.model.tryOpenRepository(path); + } + + @command('git.openFile') + async openFile(arg?: Resource | Uri, ...resourceStates: SourceControlResourceState[]): Promise { + const preserveFocus = arg instanceof Resource; + + let uris: Uri[] | undefined; + + if (arg instanceof Uri) { + if (arg.scheme === 'git') { + uris = [Uri.file(fromGitUri(arg).path)]; + } else if (arg.scheme === 'file') { + uris = [arg]; + } + } else { + let resource = arg; + + if (!(resource instanceof Resource)) { + // can happen when called from a keybinding + resource = this.getSCMResource(); + } + + if (resource) { + uris = [...resourceStates.map(r => r.resourceUri), resource.resourceUri]; + } + } + + if (!uris) { + return; + } + + const preview = uris.length === 1 ? true : false; + const activeTextEditor = window.activeTextEditor; + for (const uri of uris) { + const opts: TextDocumentShowOptions = { + preserveFocus, + preview: preview, + viewColumn: activeTextEditor && activeTextEditor.viewColumn || ViewColumn.One + }; + + if (activeTextEditor && activeTextEditor.document.uri.fsPath === uri.fsPath) { + opts.selection = activeTextEditor.selection; + } + + const document = await workspace.openTextDocument(uri); + await window.showTextDocument(document, opts); + } + } + + @command('git.openHEADFile') + async openHEADFile(arg?: Resource | Uri): Promise { + let resource: Resource | undefined = undefined; + + if (arg instanceof Resource) { + resource = arg; + } else if (arg instanceof Uri) { + resource = this.getSCMResource(arg); + } else { + resource = this.getSCMResource(); + } + + if (!resource) { + return; + } + + const HEAD = this.getLeftResource(resource); + + if (!HEAD) { + window.showWarningMessage(localize('HEAD not available', "HEAD version of '{0}' is not available.", path.basename(resource.resourceUri.fsPath))); + return; + } + + return await commands.executeCommand('vscode.open', HEAD); + } + + @command('git.openChange') + async openChange(arg?: Resource | Uri, ...resourceStates: SourceControlResourceState[]): Promise { + const preserveFocus = arg instanceof Resource; + const preserveSelection = arg instanceof Uri || !arg; + let resources: Resource[] | undefined = undefined; + + if (arg instanceof Uri) { + const resource = this.getSCMResource(arg); + if (resource !== undefined) { + resources = [resource]; + } + } else { + let resource: Resource | undefined = undefined; + + if (arg instanceof Resource) { + resource = arg; + } else { + resource = this.getSCMResource(); + } + + if (resource) { + resources = [...resourceStates as Resource[], resource]; + } + } + + if (!resources) { + return; + } + + const preview = resources.length === 1 ? undefined : false; + for (const resource of resources) { + await this._openResource(resource, preview, preserveFocus, preserveSelection); + } + } + + @command('git.stage') + async stage(...resourceStates: SourceControlResourceState[]): Promise { + if (resourceStates.length === 0 || !(resourceStates[0].resourceUri instanceof Uri)) { + const resource = this.getSCMResource(); + + if (!resource) { + return; + } + + resourceStates = [resource]; + } + + const selection = resourceStates.filter(s => s instanceof Resource) as Resource[]; + const mergeConflicts = selection.filter(s => s.resourceGroupType === ResourceGroupType.Merge); + + if (mergeConflicts.length > 0) { + const message = mergeConflicts.length > 1 + ? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", mergeConflicts.length) + : localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(mergeConflicts[0].resourceUri.fsPath)); + + const yes = localize('yes', "Yes"); + const pick = await window.showWarningMessage(message, { modal: true }, yes); + + if (pick !== yes) { + return; + } + } + + const workingTree = selection + .filter(s => s.resourceGroupType === ResourceGroupType.WorkingTree); + + const scmResources = [...workingTree, ...mergeConflicts]; + + if (!scmResources.length) { + return; + } + + const resources = scmResources.map(r => r.resourceUri); + await this.runByRepository(resources, async (repository, resources) => repository.add(resources)); + } + + @command('git.stageAll', { repository: true }) + async stageAll(repository: Repository): Promise { + const resources = repository.mergeGroup.resourceStates.filter(s => s instanceof Resource) as Resource[]; + const mergeConflicts = resources.filter(s => s.resourceGroupType === ResourceGroupType.Merge); + + if (mergeConflicts.length > 0) { + const message = mergeConflicts.length > 1 + ? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", mergeConflicts.length) + : localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(mergeConflicts[0].resourceUri.fsPath)); + + const yes = localize('yes', "Yes"); + const pick = await window.showWarningMessage(message, { modal: true }, yes); + + if (pick !== yes) { + return; + } + } + + await repository.add([]); + } + + @command('git.stageSelectedRanges', { diff: true }) + async stageSelectedRanges(diffs: LineChange[]): Promise { + const textEditor = window.activeTextEditor; + + if (!textEditor) { + return; + } + + const modifiedDocument = textEditor.document; + const modifiedUri = modifiedDocument.uri; + + if (modifiedUri.scheme !== 'file') { + return; + } + + const originalUri = toGitUri(modifiedUri, '~'); + const originalDocument = await workspace.openTextDocument(originalUri); + const selectedLines = toLineRanges(textEditor.selections, modifiedDocument); + const selectedDiffs = diffs + .map(diff => selectedLines.reduce((result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range), null)) + .filter(d => !!d) as LineChange[]; + + if (!selectedDiffs.length) { + return; + } + + const result = applyLineChanges(originalDocument, modifiedDocument, selectedDiffs); + + await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result)); + } + + @command('git.revertSelectedRanges', { diff: true }) + async revertSelectedRanges(diffs: LineChange[]): Promise { + const textEditor = window.activeTextEditor; + + if (!textEditor) { + return; + } + + const modifiedDocument = textEditor.document; + const modifiedUri = modifiedDocument.uri; + + if (modifiedUri.scheme !== 'file') { + return; + } + + const originalUri = toGitUri(modifiedUri, '~'); + const originalDocument = await workspace.openTextDocument(originalUri); + const selections = textEditor.selections; + const selectedDiffs = diffs.filter(diff => { + const modifiedRange = diff.modifiedEndLineNumber === 0 + ? new Range(modifiedDocument.lineAt(diff.modifiedStartLineNumber - 1).range.end, modifiedDocument.lineAt(diff.modifiedStartLineNumber).range.start) + : new Range(modifiedDocument.lineAt(diff.modifiedStartLineNumber - 1).range.start, modifiedDocument.lineAt(diff.modifiedEndLineNumber - 1).range.end); + + return selections.every(selection => !selection.intersection(modifiedRange)); + }); + + if (selectedDiffs.length === diffs.length) { + return; + } + + const basename = path.basename(modifiedUri.fsPath); + const message = localize('confirm revert', "Are you sure you want to revert the selected changes in {0}?", basename); + const yes = localize('revert', "Revert Changes"); + const pick = await window.showWarningMessage(message, { modal: true }, yes); + + if (pick !== yes) { + return; + } + + const result = applyLineChanges(originalDocument, modifiedDocument, selectedDiffs); + const edit = new WorkspaceEdit(); + edit.replace(modifiedUri, new Range(new Position(0, 0), modifiedDocument.lineAt(modifiedDocument.lineCount - 1).range.end), result); + workspace.applyEdit(edit); + } + + @command('git.unstage') + async unstage(...resourceStates: SourceControlResourceState[]): Promise { + if (resourceStates.length === 0 || !(resourceStates[0].resourceUri instanceof Uri)) { + const resource = this.getSCMResource(); + + if (!resource) { + return; + } + + resourceStates = [resource]; + } + + const scmResources = resourceStates + .filter(s => s instanceof Resource && s.resourceGroupType === ResourceGroupType.Index) as Resource[]; + + if (!scmResources.length) { + return; + } + + const resources = scmResources.map(r => r.resourceUri); + await this.runByRepository(resources, async (repository, resources) => repository.revert(resources)); + } + + @command('git.unstageAll', { repository: true }) + async unstageAll(repository: Repository): Promise { + await repository.revert([]); + } + + @command('git.unstageSelectedRanges', { diff: true }) + async unstageSelectedRanges(diffs: LineChange[]): Promise { + const textEditor = window.activeTextEditor; + + if (!textEditor) { + return; + } + + const modifiedDocument = textEditor.document; + const modifiedUri = modifiedDocument.uri; + + if (modifiedUri.scheme !== 'git') { + return; + } + + const { ref } = fromGitUri(modifiedUri); + + if (ref !== '') { + return; + } + + const originalUri = toGitUri(modifiedUri, 'HEAD'); + const originalDocument = await workspace.openTextDocument(originalUri); + const selectedLines = toLineRanges(textEditor.selections, modifiedDocument); + const selectedDiffs = diffs + .map(diff => selectedLines.reduce((result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range), null)) + .filter(d => !!d) as LineChange[]; + + if (!selectedDiffs.length) { + return; + } + + const invertedDiffs = selectedDiffs.map(invertLineChange); + const result = applyLineChanges(modifiedDocument, originalDocument, invertedDiffs); + + await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result)); + } + + @command('git.clean') + async clean(...resourceStates: SourceControlResourceState[]): Promise { + if (resourceStates.length === 0 || !(resourceStates[0].resourceUri instanceof Uri)) { + const resource = this.getSCMResource(); + + if (!resource) { + return; + } + + resourceStates = [resource]; + } + + const scmResources = resourceStates + .filter(s => s instanceof Resource && s.resourceGroupType === ResourceGroupType.WorkingTree) as Resource[]; + + if (!scmResources.length) { + return; + } + + const untrackedCount = scmResources.reduce((s, r) => s + (r.type === Status.UNTRACKED ? 1 : 0), 0); + let message: string; + let yes = localize('discard', "Discard Changes"); + + if (scmResources.length === 1) { + if (untrackedCount > 0) { + message = localize('confirm delete', "Are you sure you want to DELETE {0}?", path.basename(scmResources[0].resourceUri.fsPath)); + yes = localize('delete file', "Delete file"); + } else { + message = localize('confirm discard', "Are you sure you want to discard changes in {0}?", path.basename(scmResources[0].resourceUri.fsPath)); + } + } else { + message = localize('confirm discard multiple', "Are you sure you want to discard changes in {0} files?", scmResources.length); + + if (untrackedCount > 0) { + message = `${message}\n\n${localize('warn untracked', "This will DELETE {0} untracked files!", untrackedCount)}`; + } + } + + const pick = await window.showWarningMessage(message, { modal: true }, yes); + + if (pick !== yes) { + return; + } + + const resources = scmResources.map(r => r.resourceUri); + await this.runByRepository(resources, async (repository, resources) => repository.clean(resources)); + } + + @command('git.cleanAll', { repository: true }) + async cleanAll(repository: Repository): Promise { + let resources = repository.workingTreeGroup.resourceStates; + + if (resources.length === 0) { + return; + } + + const trackedResources = resources.filter(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED); + const untrackedResources = resources.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED); + + if (untrackedResources.length === 0) { + const message = resources.length === 1 + ? localize('confirm discard all single', "Are you sure you want to discard changes in {0}?", path.basename(resources[0].resourceUri.fsPath)) + : localize('confirm discard all', "Are you sure you want to discard ALL changes in {0} files?\nThis is IRREVERSIBLE!\nYour current working set will be FOREVER LOST.", resources.length); + const yes = resources.length === 1 + ? localize('discardAll multiple', "Discard 1 File") + : localize('discardAll', "Discard All {0} Files", resources.length); + const pick = await window.showWarningMessage(message, { modal: true }, yes); + + if (pick !== yes) { + return; + } + + await repository.clean(resources.map(r => r.resourceUri)); + return; + } else if (resources.length === 1) { + const message = localize('confirm delete', "Are you sure you want to DELETE {0}?", path.basename(resources[0].resourceUri.fsPath)); + const yes = localize('delete file', "Delete file"); + const pick = await window.showWarningMessage(message, { modal: true }, yes); + + if (pick !== yes) { + return; + } + + await repository.clean(resources.map(r => r.resourceUri)); + } else if (trackedResources.length === 0) { + const message = localize('confirm delete multiple', "Are you sure you want to DELETE {0} files?", resources.length); + const yes = localize('delete files', "Delete Files"); + const pick = await window.showWarningMessage(message, { modal: true }, yes); + + if (pick !== yes) { + return; + } + + await repository.clean(resources.map(r => r.resourceUri)); + + } else { // resources.length > 1 && untrackedResources.length > 0 && trackedResources.length > 0 + const untrackedMessage = untrackedResources.length === 1 + ? localize('there are untracked files single', "The following untracked file will be DELETED FROM DISK if discarded: {0}.", path.basename(untrackedResources[0].resourceUri.fsPath)) + : localize('there are untracked files', "There are {0} untracked files which will be DELETED FROM DISK if discarded.", untrackedResources.length); + + const message = localize('confirm discard all 2', "{0}\n\nThis is IRREVERSIBLE, your current working set will be FOREVER LOST.", untrackedMessage, resources.length); + + const yesTracked = trackedResources.length === 1 + ? localize('yes discard tracked', "Discard 1 Tracked File", trackedResources.length) + : localize('yes discard tracked multiple', "Discard {0} Tracked Files", trackedResources.length); + + const yesAll = localize('discardAll', "Discard All {0} Files", resources.length); + const pick = await window.showWarningMessage(message, { modal: true }, yesTracked, yesAll); + + if (pick === yesTracked) { + resources = trackedResources; + } else if (pick !== yesAll) { + return; + } + + await repository.clean(resources.map(r => r.resourceUri)); + } + } + + private async smartCommit( + repository: Repository, + getCommitMessage: () => Promise, + opts?: CommitOptions + ): Promise { + const config = workspace.getConfiguration('git'); + const enableSmartCommit = config.get('enableSmartCommit') === true; + const enableCommitSigning = config.get('enableCommitSigning') === true; + const noStagedChanges = repository.indexGroup.resourceStates.length === 0; + const noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0; + + // no changes, and the user has not configured to commit all in this case + if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit) { + + // prompt the user if we want to commit all or not + const message = localize('no staged changes', "There are no staged changes to commit.\n\nWould you like to automatically stage all your changes and commit them directly?"); + const yes = localize('yes', "Yes"); + const always = localize('always', "Always"); + const pick = await window.showWarningMessage(message, { modal: true }, yes, always); + + if (pick === always) { + config.update('enableSmartCommit', true, true); + } else if (pick !== yes) { + return false; // do not commit on cancel + } + } + + if (!opts) { + opts = { all: noStagedChanges }; + } + + // enable signing of commits if configurated + opts.signCommit = enableCommitSigning; + + if ( + // no changes + (noStagedChanges && noUnstagedChanges) + // or no staged changes and not `all` + || (!opts.all && noStagedChanges) + ) { + window.showInformationMessage(localize('no changes', "There are no changes to commit.")); + return false; + } + + const message = await getCommitMessage(); + + if (!message) { + return false; + } + + await repository.commit(message, opts); + + return true; + } + + private async commitWithAnyInput(repository: Repository, opts?: CommitOptions): Promise { + const message = repository.inputBox.value; + const getCommitMessage = async () => { + if (message) { + return message; + } + + return await window.showInputBox({ + placeHolder: localize('commit message', "Commit message"), + prompt: localize('provide commit message', "Please provide a commit message"), + ignoreFocusOut: true + }); + }; + + const didCommit = await this.smartCommit(repository, getCommitMessage, opts); + + if (message && didCommit) { + repository.inputBox.value = await repository.getCommitTemplate(); + } + } + + @command('git.commit', { repository: true }) + async commit(repository: Repository): Promise { + await this.commitWithAnyInput(repository); + } + + @command('git.commitWithInput', { repository: true }) + async commitWithInput(repository: Repository): Promise { + if (!repository.inputBox.value) { + return; + } + + const didCommit = await this.smartCommit(repository, async () => repository.inputBox.value); + + if (didCommit) { + repository.inputBox.value = await repository.getCommitTemplate(); + } + } + + @command('git.commitStaged', { repository: true }) + async commitStaged(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { all: false }); + } + + @command('git.commitStagedSigned', { repository: true }) + async commitStagedSigned(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { all: false, signoff: true }); + } + + @command('git.commitStagedAmend', { repository: true }) + async commitStagedAmend(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { all: false, amend: true }); + } + + @command('git.commitAll', { repository: true }) + async commitAll(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { all: true }); + } + + @command('git.commitAllSigned', { repository: true }) + async commitAllSigned(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { all: true, signoff: true }); + } + + @command('git.commitAllAmend', { repository: true }) + async commitAllAmend(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { all: true, amend: true }); + } + + @command('git.undoCommit', { repository: true }) + async undoCommit(repository: Repository): Promise { + const HEAD = repository.HEAD; + + if (!HEAD || !HEAD.commit) { + return; + } + + const commit = await repository.getCommit('HEAD'); + await repository.reset('HEAD~'); + repository.inputBox.value = commit.message; + } + + @command('git.checkout', { repository: true }) + async checkout(repository: Repository, treeish: string): Promise { + if (typeof treeish === 'string') { + return await repository.checkout(treeish); + } + + const config = workspace.getConfiguration('git'); + const checkoutType = config.get('checkoutType') || 'all'; + const includeTags = checkoutType === 'all' || checkoutType === 'tags'; + const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; + + const createBranch = new CreateBranchItem(); + + const heads = repository.refs.filter(ref => ref.type === RefType.Head) + .map(ref => new CheckoutItem(ref)); + + const tags = (includeTags ? repository.refs.filter(ref => ref.type === RefType.Tag) : []) + .map(ref => new CheckoutTagItem(ref)); + + const remoteHeads = (includeRemotes ? repository.refs.filter(ref => ref.type === RefType.RemoteHead) : []) + .map(ref => new CheckoutRemoteHeadItem(ref)); + + const picks = [createBranch, ...heads, ...tags, ...remoteHeads]; + const placeHolder = localize('select a ref to checkout', 'Select a ref to checkout'); + const choice = await window.showQuickPick(picks, { placeHolder }); + + if (!choice) { + return; + } + + await choice.run(repository); + } + + @command('git.branch', { repository: true }) + async branch(repository: Repository): Promise { + const result = await window.showInputBox({ + placeHolder: localize('branch name', "Branch name"), + prompt: localize('provide branch name', "Please provide a branch name"), + ignoreFocusOut: true + }); + + if (!result) { + return; + } + + const name = result.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$/g, '-'); + await repository.branch(name); + } + + @command('git.deleteBranch', { repository: true }) + async deleteBranch(repository: Repository, name: string, force?: boolean): Promise { + let run: (force?: boolean) => Promise; + if (typeof name === 'string') { + run = force => repository.deleteBranch(name, force); + } else { + const currentHead = repository.HEAD && repository.HEAD.name; + const heads = repository.refs.filter(ref => ref.type === RefType.Head && ref.name !== currentHead) + .map(ref => new BranchDeleteItem(ref)); + + const placeHolder = localize('select branch to delete', 'Select a branch to delete'); + const choice = await window.showQuickPick(heads, { placeHolder }); + + if (!choice || !choice.branchName) { + return; + } + name = choice.branchName; + run = force => choice.run(repository, force); + } + + try { + await run(force); + } catch (err) { + if (err.gitErrorCode !== GitErrorCodes.BranchNotFullyMerged) { + throw err; + } + + const message = localize('confirm force delete branch', "The branch '{0}' is not fully merged. Delete anyway?", name); + const yes = localize('delete branch', "Delete Branch"); + const pick = await window.showWarningMessage(message, yes); + + if (pick === yes) { + await run(true); + } + } + } + + @command('git.merge', { repository: true }) + async merge(repository: Repository): Promise { + const config = workspace.getConfiguration('git'); + const checkoutType = config.get('checkoutType') || 'all'; + const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; + + const heads = repository.refs.filter(ref => ref.type === RefType.Head) + .filter(ref => ref.name || ref.commit) + .map(ref => new MergeItem(ref as Branch)); + + const remoteHeads = (includeRemotes ? repository.refs.filter(ref => ref.type === RefType.RemoteHead) : []) + .filter(ref => ref.name || ref.commit) + .map(ref => new MergeItem(ref as Branch)); + + const picks = [...heads, ...remoteHeads]; + const placeHolder = localize('select a branch to merge from', 'Select a branch to merge from'); + const choice = await window.showQuickPick(picks, { placeHolder }); + + if (!choice) { + return; + } + + try { + await choice.run(repository); + } catch (err) { + if (err.gitErrorCode !== GitErrorCodes.Conflict) { + throw err; + } + + const message = localize('merge conflicts', "There are merge conflicts. Resolve them before committing."); + await window.showWarningMessage(message); + } + } + + @command('git.createTag', { repository: true }) + async createTag(repository: Repository): Promise { + const inputTagName = await window.showInputBox({ + placeHolder: localize('tag name', "Tag name"), + prompt: localize('provide tag name', "Please provide a tag name"), + ignoreFocusOut: true + }); + + if (!inputTagName) { + return; + } + + const inputMessage = await window.showInputBox({ + placeHolder: localize('tag message', "Message"), + prompt: localize('provide tag message', "Please provide a message to annotate the tag"), + ignoreFocusOut: true + }); + + const name = inputTagName.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$/g, '-'); + const message = inputMessage || name; + await repository.tag(name, message); + } + + @command('git.pullFrom', { repository: true }) + async pullFrom(repository: Repository): Promise { + const remotes = repository.remotes; + + if (remotes.length === 0) { + window.showWarningMessage(localize('no remotes to pull', "Your repository has no remotes configured to pull from.")); + return; + } + + const picks = remotes.map(r => ({ label: r.name, description: r.url })); + const placeHolder = localize('pick remote pull repo', "Pick a remote to pull the branch from"); + const pick = await window.showQuickPick(picks, { placeHolder }); + + if (!pick) { + return; + } + + const branchName = await window.showInputBox({ + placeHolder: localize('branch name', "Branch name"), + prompt: localize('provide branch name', "Please provide a branch name"), + ignoreFocusOut: true + }); + + if (!branchName) { + return; + } + + repository.pull(false, pick.label, branchName); + } + + @command('git.pull', { repository: true }) + async pull(repository: Repository): Promise { + const remotes = repository.remotes; + + if (remotes.length === 0) { + window.showWarningMessage(localize('no remotes to pull', "Your repository has no remotes configured to pull from.")); + return; + } + + await repository.pull(); + } + + @command('git.pullRebase', { repository: true }) + async pullRebase(repository: Repository): Promise { + const remotes = repository.remotes; + + if (remotes.length === 0) { + window.showWarningMessage(localize('no remotes to pull', "Your repository has no remotes configured to pull from.")); + return; + } + + await repository.pullWithRebase(); + } + + @command('git.push', { repository: true }) + async push(repository: Repository): Promise { + const remotes = repository.remotes; + + if (remotes.length === 0) { + window.showWarningMessage(localize('no remotes to push', "Your repository has no remotes configured to push to.")); + return; + } + + await repository.push(); + } + + @command('git.pushWithTags', { repository: true }) + async pushWithTags(repository: Repository): Promise { + const remotes = repository.remotes; + + if (remotes.length === 0) { + window.showWarningMessage(localize('no remotes to push', "Your repository has no remotes configured to push to.")); + return; + } + + await repository.pushTags(); + + window.showInformationMessage(localize('push with tags success', "Successfully pushed with tags.")); + } + + @command('git.pushTo', { repository: true }) + async pushTo(repository: Repository): Promise { + const remotes = repository.remotes; + + if (remotes.length === 0) { + window.showWarningMessage(localize('no remotes to push', "Your repository has no remotes configured to push to.")); + return; + } + + if (!repository.HEAD || !repository.HEAD.name) { + window.showWarningMessage(localize('nobranch', "Please check out a branch to push to a remote.")); + return; + } + + const branchName = repository.HEAD.name; + const picks = remotes.map(r => ({ label: r.name, description: r.url })); + const placeHolder = localize('pick remote', "Pick a remote to publish the branch '{0}' to:", branchName); + const pick = await window.showQuickPick(picks, { placeHolder }); + + if (!pick) { + return; + } + + repository.pushTo(pick.label, branchName); + } + + @command('git.sync', { repository: true }) + async sync(repository: Repository): Promise { + const HEAD = repository.HEAD; + + if (!HEAD || !HEAD.upstream) { + return; + } + + const config = workspace.getConfiguration('git'); + const shouldPrompt = config.get('confirmSync') === true; + + if (shouldPrompt) { + const message = localize('sync is unpredictable', "This action will push and pull commits to and from '{0}'.", HEAD.upstream); + const yes = localize('ok', "OK"); + const neverAgain = localize('never again', "OK, Never Show Again"); + const pick = await window.showWarningMessage(message, { modal: true }, yes, neverAgain); + + if (pick === neverAgain) { + await config.update('confirmSync', false, true); + } else if (pick !== yes) { + return; + } + } + + await repository.sync(); + } + + @command('git.publish', { repository: true }) + async publish(repository: Repository): Promise { + const remotes = repository.remotes; + + if (remotes.length === 0) { + window.showWarningMessage(localize('no remotes to publish', "Your repository has no remotes configured to publish to.")); + return; + } + + const branchName = repository.HEAD && repository.HEAD.name || ''; + const picks = repository.remotes.map(r => r.name); + const placeHolder = localize('pick remote', "Pick a remote to publish the branch '{0}' to:", branchName); + const choice = await window.showQuickPick(picks, { placeHolder }); + + if (!choice) { + return; + } + + await repository.pushTo(choice, branchName, true); + } + + @command('git.showOutput') + showOutput(): void { + this.outputChannel.show(); + } + + @command('git.ignore', { repository: true }) + async ignore(repository: Repository, ...resourceStates: SourceControlResourceState[]): Promise { + if (resourceStates.length === 0 || !(resourceStates[0].resourceUri instanceof Uri)) { + const uri = window.activeTextEditor && window.activeTextEditor.document.uri; + + if (!uri) { + return; + } + + return await repository.ignore([uri]); + } + + const uris = resourceStates + .filter(s => s instanceof Resource) + .map(r => r.resourceUri); + + if (!uris.length) { + return; + } + + await repository.ignore(uris); + } + + @command('git.stash', { repository: true }) + async stash(repository: Repository): Promise { + if (repository.workingTreeGroup.resourceStates.length === 0) { + window.showInformationMessage(localize('no changes stash', "There are no changes to stash.")); + return; + } + + const message = await window.showInputBox({ + prompt: localize('provide stash message', "Optionally provide a stash message"), + placeHolder: localize('stash message', "Stash message") + }); + + if (typeof message === 'undefined') { + return; + } + + await repository.createStash(message); + } + + @command('git.stashPop', { repository: true }) + async stashPop(repository: Repository): Promise { + const stashes = await repository.getStashes(); + + if (stashes.length === 0) { + window.showInformationMessage(localize('no stashes', "There are no stashes to restore.")); + return; + } + + const picks = stashes.map(r => ({ label: `#${r.index}: ${r.description}`, description: '', details: '', id: r.index })); + const placeHolder = localize('pick stash to pop', "Pick a stash to pop"); + const choice = await window.showQuickPick(picks, { placeHolder }); + + if (!choice) { + return; + } + + await repository.popStash(choice.id); + } + + @command('git.stashPopLatest', { repository: true }) + async stashPopLatest(repository: Repository): Promise { + const stashes = await repository.getStashes(); + + if (stashes.length === 0) { + window.showInformationMessage(localize('no stashes', "There are no stashes to restore.")); + return; + } + + await repository.popStash(); + } + + private createCommand(id: string, key: string, method: Function, options: CommandOptions): (...args: any[]) => any { + const result = (...args) => { + let result: Promise; + + if (!options.repository) { + result = Promise.resolve(method.apply(this, args)); + } else { + // try to guess the repository based on the first argument + const repository = this.model.getRepository(args[0]); + let repositoryPromise: Promise; + + if (repository) { + repositoryPromise = Promise.resolve(repository); + } else if (this.model.repositories.length === 1) { + repositoryPromise = Promise.resolve(this.model.repositories[0]); + } else { + repositoryPromise = this.model.pickRepository(); + } + + result = repositoryPromise.then(repository => { + if (!repository) { + return Promise.resolve(); + } + + return Promise.resolve(method.apply(this, [repository, ...args])); + }); + } + + this.telemetryReporter.sendTelemetryEvent('git.command', { command: id }); + + return result.catch(async err => { + let message: string; + + switch (err.gitErrorCode) { + case GitErrorCodes.DirtyWorkTree: + message = localize('clean repo', "Please clean your repository working tree before checkout."); + break; + case GitErrorCodes.PushRejected: + message = localize('cant push', "Can't push refs to remote. Run 'Pull' first to integrate your changes."); + break; + default: + const hint = (err.stderr || err.message || String(err)) + .replace(/^error: /mi, '') + .replace(/^> husky.*$/mi, '') + .split(/[\r\n]/) + .filter(line => !!line) + [0]; + + message = hint + ? localize('git error details', "Git: {0}", hint) + : localize('git error', "Git error"); + + break; + } + + if (!message) { + console.error(err); + return; + } + + const outputChannel = this.outputChannel as OutputChannel; + const openOutputChannelChoice = localize('open git log', "Open Git Log"); + const choice = await window.showErrorMessage(message, openOutputChannelChoice); + + if (choice === openOutputChannelChoice) { + outputChannel.show(); + } + }); + }; + + // patch this object, so people can call methods directly + this[key] = result; + + return result; + } + + private getSCMResource(uri?: Uri): Resource | undefined { + uri = uri ? uri : window.activeTextEditor && window.activeTextEditor.document.uri; + + if (!uri) { + return undefined; + } + + if (uri.scheme === 'git') { + const { path } = fromGitUri(uri); + uri = Uri.file(path); + } + + if (uri.scheme === 'file') { + const uriString = uri.toString(); + const repository = this.model.getRepository(uri); + + if (!repository) { + return undefined; + } + + return repository.workingTreeGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString)[0] + || repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString)[0]; + } + } + + private runByRepository(resource: Uri, fn: (repository: Repository, resource: Uri) => Promise): Promise; + private runByRepository(resources: Uri[], fn: (repository: Repository, resources: Uri[]) => Promise): Promise; + private async runByRepository(arg: Uri | Uri[], fn: (repository: Repository, resources: any) => Promise): Promise { + const resources = arg instanceof Uri ? [arg] : arg; + const isSingleResource = arg instanceof Uri; + + const groups = resources.reduce((result, resource) => { + const repository = this.model.getRepository(resource); + + if (!repository) { + console.warn('Could not find git repository for ', resource); + return result; + } + + const tuple = result.filter(p => p[0] === repository)[0]; + + if (tuple) { + tuple.resources.push(resource); + } else { + result.push({ repository, resources: [resource] }); + } + + return result; + }, [] as { repository: Repository, resources: Uri[] }[]); + + const promises = groups + .map(({ repository, resources }) => fn(repository as Repository, isSingleResource ? resources[0] : resources)); + + return Promise.all(promises); + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + } +} \ No newline at end of file diff --git a/extensions/git/src/contentProvider.ts b/extensions/git/src/contentProvider.ts new file mode 100644 index 0000000000..2e17fbf2c2 --- /dev/null +++ b/extensions/git/src/contentProvider.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { workspace, Uri, Disposable, Event, EventEmitter, window } from 'vscode'; +import { debounce } from './decorators'; +import { fromGitUri } from './uri'; +import { Model, ModelChangeEvent } from './model'; + +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 onDidChangeEmitter = new EventEmitter(); + get onDidChange(): Event { return this.onDidChangeEmitter.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), + workspace.registerTextDocumentContentProvider('git', this) + ); + + setInterval(() => this.cleanup(), FIVE_MINUTES); + } + + private onDidChangeRepository({ repository }: ModelChangeEvent): void { + this.changedRepositoryRoots.add(repository.root); + this.eventuallyFireChangeEvents(); + } + + @debounce(1100) + private eventuallyFireChangeEvents(): void { + this.fireChangeEvents(); + } + + private fireChangeEvents(): void { + Object.keys(this.cache).forEach(key => { + const uri = this.cache[key].uri; + const fsPath = uri.fsPath; + + for (const root of this.changedRepositoryRoots) { + if (fsPath.startsWith(root)) { + this.onDidChangeEmitter.fire(uri); + return; + } + } + }); + + this.changedRepositoryRoots.clear(); + } + + async provideTextDocumentContent(uri: Uri): Promise { + const repository = this.model.getRepository(uri); + + if (!repository) { + return ''; + } + + const cacheKey = uri.toString(); + const timestamp = new Date().getTime(); + const cacheValue = { uri, timestamp }; + + this.cache[cacheKey] = cacheValue; + + let { path, ref } = fromGitUri(uri); + + if (ref === '~') { + const fileUri = Uri.file(path); + const uriString = fileUri.toString(); + const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.original.toString() === uriString); + ref = indexStatus ? '' : 'HEAD'; + } + + 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 isOpen = window.visibleTextEditors.some(e => e.document.uri.fsPath === row.uri.fsPath); + + if (isOpen || now - row.timestamp < THREE_MINUTES) { + cache[row.uri.toString()] = row; + } + }); + + this.cache = cache; + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + } +} \ No newline at end of file diff --git a/extensions/git/src/decorators.ts b/extensions/git/src/decorators.ts new file mode 100644 index 0000000000..183bc3fb19 --- /dev/null +++ b/extensions/git/src/decorators.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { done } from './util'; + +function decorate(decorator: (fn: Function, key: string) => Function): Function { + return (target: any, key: string, descriptor: any) => { + let fnKey: string | null = null; + let fn: Function | null = null; + + if (typeof descriptor.value === 'function') { + fnKey = 'value'; + fn = descriptor.value; + } else if (typeof descriptor.get === 'function') { + fnKey = 'get'; + fn = descriptor.get; + } + + if (!fn || !fnKey) { + throw new Error('not supported'); + } + + descriptor[fnKey] = decorator(fn, key); + }; +} + +function _memoize(fn: Function, key: string): Function { + const memoizeKey = `$memoize$${key}`; + + return function (...args: any[]) { + if (!this.hasOwnProperty(memoizeKey)) { + Object.defineProperty(this, memoizeKey, { + configurable: false, + enumerable: false, + writable: false, + value: fn.apply(this, args) + }); + } + + return this[memoizeKey]; + }; +} + +export const memoize = decorate(_memoize); + +function _throttle(fn: Function, key: string): Function { + const currentKey = `$throttle$current$${key}`; + const nextKey = `$throttle$next$${key}`; + + const trigger = function (...args: any[]) { + if (this[nextKey]) { + return this[nextKey]; + } + + if (this[currentKey]) { + this[nextKey] = done(this[currentKey]).then(() => { + this[nextKey] = undefined; + return trigger.apply(this, args); + }); + + return this[nextKey]; + } + + this[currentKey] = fn.apply(this, args) as Promise; + + const clear = () => this[currentKey] = undefined; + done(this[currentKey]).then(clear, clear); + + return this[currentKey]; + }; + + return trigger; +} + +export const throttle = decorate(_throttle); + +function _sequentialize(fn: Function, key: string): Function { + const currentKey = `__$sequence$${key}`; + + return function (...args: any[]) { + const currentPromise = this[currentKey] as Promise || Promise.resolve(null); + const run = async () => await fn.apply(this, args); + this[currentKey] = currentPromise.then(run, run); + return this[currentKey]; + }; +} + +export const sequentialize = decorate(_sequentialize); + +export function debounce(delay: number): Function { + return decorate((fn, key) => { + const timerKey = `$debounce$${key}`; + + return function (...args: any[]) { + clearTimeout(this[timerKey]); + this[timerKey] = setTimeout(() => fn.apply(this, args), delay); + }; + }); +} \ No newline at end of file diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts new file mode 100644 index 0000000000..e266174f1e --- /dev/null +++ b/extensions/git/src/git.ts @@ -0,0 +1,1067 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import * as cp from 'child_process'; +import { EventEmitter } from 'events'; +import iconv = require('iconv-lite'); +import { assign, uniqBy, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp } from './util'; + +const readdir = denodeify(fs.readdir); +const readfile = denodeify(fs.readFile); + +export interface IGit { + path: string; + version: string; +} + +export interface IFileStatus { + x: string; + y: string; + path: string; + rename?: string; +} + +export interface Remote { + name: string; + url: string; +} + +export interface Stash { + index: number; + description: string; +} + +export enum RefType { + Head, + RemoteHead, + Tag +} + +export interface Ref { + type: RefType; + name?: string; + commit?: string; + remote?: string; +} + +export interface Branch extends Ref { + upstream?: string; + ahead?: number; + behind?: number; +} + +function parseVersion(raw: string): string { + return raw.replace(/^git version /, ''); +} + +function findSpecificGit(path: string): Promise { + return new Promise((c, e) => { + const buffers: Buffer[] = []; + const child = cp.spawn(path, ['--version']); + child.stdout.on('data', (b: Buffer) => buffers.push(b)); + child.on('error', e); + child.on('exit', code => code ? e(new Error('Not found')) : c({ path, version: parseVersion(Buffer.concat(buffers).toString('utf8').trim()) })); + }); +} + +function findGitDarwin(): Promise { + return new Promise((c, e) => { + cp.exec('which git', (err, gitPathBuffer) => { + if (err) { + return e('git not found'); + } + + const path = gitPathBuffer.toString().replace(/^\s+|\s+$/g, ''); + + function getVersion(path: string) { + // make sure git executes + cp.exec('git --version', (err, stdout: Buffer) => { + if (err) { + return e('git not found'); + } + + return c({ path, version: parseVersion(stdout.toString('utf8').trim()) }); + }); + } + + if (path !== '/usr/bin/git') { + return getVersion(path); + } + + // must check if XCode is installed + cp.exec('xcode-select -p', (err: any) => { + if (err && err.code === 2) { + // git is not installed, and launching /usr/bin/git + // will prompt the user to install it + + return e('git not found'); + } + + getVersion(path); + }); + }); + }); +} + +function findSystemGitWin32(base: string): Promise { + if (!base) { + return Promise.reject('Not found'); + } + + return findSpecificGit(path.join(base, 'Git', 'cmd', 'git.exe')); +} + +function findGitHubGitWin32(): Promise { + const github = path.join(process.env['LOCALAPPDATA'], 'GitHub'); + + return readdir(github).then(children => { + const git = children.filter(child => /^PortableGit/.test(child))[0]; + + if (!git) { + return Promise.reject('Not found'); + } + + return findSpecificGit(path.join(github, git, 'cmd', 'git.exe')); + }); +} + +function findGitWin32(): Promise { + return findSystemGitWin32(process.env['ProgramW6432']) + .then(void 0, () => findSystemGitWin32(process.env['ProgramFiles(x86)'])) + .then(void 0, () => findSystemGitWin32(process.env['ProgramFiles'])) + .then(void 0, () => findSpecificGit('git')) + .then(void 0, () => findGitHubGitWin32()); +} + +export function findGit(hint: string | undefined): Promise { + var first = hint ? findSpecificGit(hint) : Promise.reject(null); + + return first.then(void 0, () => { + switch (process.platform) { + case 'darwin': return findGitDarwin(); + case 'win32': return findGitWin32(); + default: return findSpecificGit('git'); + } + }); +} + + +export interface IExecutionResult { + exitCode: number; + stdout: string; + stderr: string; +} + +async function exec(child: cp.ChildProcess, options: any = {}): Promise { + if (!child.stdout || !child.stderr) { + throw new GitError({ + message: 'Failed to get stdout or stderr from git process.' + }); + } + + const disposables: IDisposable[] = []; + + const once = (ee: NodeJS.EventEmitter, name: string, fn: Function) => { + ee.once(name, fn); + disposables.push(toDisposable(() => ee.removeListener(name, fn))); + }; + + const on = (ee: NodeJS.EventEmitter, name: string, fn: Function) => { + ee.on(name, fn); + disposables.push(toDisposable(() => ee.removeListener(name, fn))); + }; + + let encoding = options.encoding || 'utf8'; + encoding = iconv.encodingExists(encoding) ? encoding : 'utf8'; + + const [exitCode, stdout, stderr] = await Promise.all([ + new Promise((c, e) => { + once(child, 'error', e); + once(child, 'exit', c); + }), + new Promise(c => { + const buffers: Buffer[] = []; + on(child.stdout, 'data', b => buffers.push(b)); + once(child.stdout, 'close', () => c(iconv.decode(Buffer.concat(buffers), encoding))); + }), + new Promise(c => { + const buffers: Buffer[] = []; + on(child.stderr, 'data', b => buffers.push(b)); + once(child.stderr, 'close', () => c(Buffer.concat(buffers).toString('utf8'))); + }) + ]); + + dispose(disposables); + + return { exitCode, stdout, stderr }; +} + +export interface IGitErrorData { + error?: Error; + message?: string; + stdout?: string; + stderr?: string; + exitCode?: number; + gitErrorCode?: string; + gitCommand?: string; +} + +export class GitError { + + error?: Error; + message: string; + stdout?: string; + stderr?: string; + exitCode?: number; + gitErrorCode?: string; + gitCommand?: string; + + constructor(data: IGitErrorData) { + if (data.error) { + this.error = data.error; + this.message = data.error.message; + } else { + this.error = void 0; + } + + this.message = this.message || data.message || 'Git error'; + this.stdout = data.stdout; + this.stderr = data.stderr; + this.exitCode = data.exitCode; + this.gitErrorCode = data.gitErrorCode; + this.gitCommand = data.gitCommand; + } + + toString(): string { + let result = this.message + ' ' + JSON.stringify({ + exitCode: this.exitCode, + gitErrorCode: this.gitErrorCode, + gitCommand: this.gitCommand, + stdout: this.stdout, + stderr: this.stderr + }, [], 2); + + if (this.error) { + result += (this.error).stack; + } + + return result; + } +} + +export interface IGitOptions { + gitPath: string; + version: string; + env?: any; +} + +export const GitErrorCodes = { + BadConfigFile: 'BadConfigFile', + AuthenticationFailed: 'AuthenticationFailed', + NoUserNameConfigured: 'NoUserNameConfigured', + NoUserEmailConfigured: 'NoUserEmailConfigured', + NoRemoteRepositorySpecified: 'NoRemoteRepositorySpecified', + NotAGitRepository: 'NotAGitRepository', + NotAtRepositoryRoot: 'NotAtRepositoryRoot', + Conflict: 'Conflict', + UnmergedChanges: 'UnmergedChanges', + PushRejected: 'PushRejected', + RemoteConnectionError: 'RemoteConnectionError', + DirtyWorkTree: 'DirtyWorkTree', + CantOpenResource: 'CantOpenResource', + GitNotFound: 'GitNotFound', + CantCreatePipe: 'CantCreatePipe', + CantAccessRemote: 'CantAccessRemote', + RepositoryNotFound: 'RepositoryNotFound', + RepositoryIsLocked: 'RepositoryIsLocked', + BranchNotFullyMerged: 'BranchNotFullyMerged', + NoRemoteReference: 'NoRemoteReference', + NoLocalChanges: 'NoLocalChanges', + NoStashFound: 'NoStashFound', + LocalChangesOverwritten: 'LocalChangesOverwritten' +}; + +function getGitErrorCode(stderr: string): string | undefined { + if (/Another git process seems to be running in this repository|If no other git process is currently running/.test(stderr)) { + return GitErrorCodes.RepositoryIsLocked; + } else if (/Authentication failed/.test(stderr)) { + return GitErrorCodes.AuthenticationFailed; + } else if (/Not a git repository/.test(stderr)) { + return GitErrorCodes.NotAGitRepository; + } else if (/bad config file/.test(stderr)) { + return GitErrorCodes.BadConfigFile; + } else if (/cannot make pipe for command substitution|cannot create standard input pipe/.test(stderr)) { + return GitErrorCodes.CantCreatePipe; + } else if (/Repository not found/.test(stderr)) { + return GitErrorCodes.RepositoryNotFound; + } else if (/unable to access/.test(stderr)) { + return GitErrorCodes.CantAccessRemote; + } else if (/branch '.+' is not fully merged/.test(stderr)) { + return GitErrorCodes.BranchNotFullyMerged; + } else if (/Couldn\'t find remote ref/.test(stderr)) { + return GitErrorCodes.NoRemoteReference; + } + + return void 0; +} + +export class Git { + + private gitPath: string; + private version: string; + private env: any; + + private _onOutput = new EventEmitter(); + get onOutput(): EventEmitter { return this._onOutput; } + + constructor(options: IGitOptions) { + this.gitPath = options.gitPath; + this.version = options.version; + this.env = options.env || {}; + } + + open(repository: string): Repository { + return new Repository(this, repository); + } + + async init(repository: string): Promise { + await this.exec(repository, ['init']); + return; + } + + async clone(url: string, parentPath: string): Promise { + const folderName = decodeURI(url).replace(/^.*\//, '').replace(/\.git$/, '') || 'repository'; + const folderPath = path.join(parentPath, folderName); + + await mkdirp(parentPath); + await this.exec(parentPath, ['clone', url, folderPath]); + return folderPath; + } + + async getRepositoryRoot(repositoryPath: string): Promise { + const result = await this.exec(repositoryPath, ['rev-parse', '--show-toplevel']); + return path.normalize(result.stdout.trim()); + } + + async exec(cwd: string, args: string[], options: any = {}): Promise { + options = assign({ cwd }, options || {}); + return await this._exec(args, options); + } + + stream(cwd: string, args: string[], options: any = {}): cp.ChildProcess { + options = assign({ cwd }, options || {}); + return this.spawn(args, options); + } + + private async _exec(args: string[], options: any = {}): Promise { + const child = this.spawn(args, options); + + if (options.input) { + child.stdin.end(options.input, 'utf8'); + } + + const result = await exec(child, options); + + if (options.log !== false && result.stderr.length > 0) { + this.log(`${result.stderr}\n`); + } + + if (result.exitCode) { + return Promise.reject(new GitError({ + message: 'Failed to execute git', + stdout: result.stdout, + stderr: result.stderr, + exitCode: result.exitCode, + gitErrorCode: getGitErrorCode(result.stderr), + gitCommand: args[0] + })); + } + + return result; + } + + spawn(args: string[], options: any = {}): cp.ChildProcess { + if (!this.gitPath) { + throw new Error('git could not be found in the system.'); + } + + if (!options) { + options = {}; + } + + if (!options.stdio && !options.input) { + options.stdio = ['ignore', null, null]; // Unless provided, ignore stdin and leave default streams for stdout and stderr + } + + 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' + }); + + if (options.log !== false) { + this.log(`git ${args.join(' ')}\n`); + } + + return cp.spawn(this.gitPath, args, options); + } + + private log(output: string): void { + this._onOutput.emit('log', output); + } +} + +export interface Commit { + hash: string; + message: string; +} + +export class GitStatusParser { + + private lastRaw = ''; + private result: IFileStatus[] = []; + + get status(): IFileStatus[] { + return this.result; + } + + update(raw: string): void { + let i = 0; + let nextI: number | undefined; + + raw = this.lastRaw + raw; + + while ((nextI = this.parseEntry(raw, i)) !== undefined) { + i = nextI; + } + + this.lastRaw = raw.substr(i); + } + + private parseEntry(raw: string, i: number): number | undefined { + if (i + 4 >= raw.length) { + return; + } + + let lastIndex: number; + const entry: IFileStatus = { + x: raw.charAt(i++), + y: raw.charAt(i++), + rename: undefined, + path: '' + }; + + // space + i++; + + if (entry.x === 'R' || entry.x === 'C') { + lastIndex = raw.indexOf('\0', i); + + if (lastIndex === -1) { + return; + } + + entry.rename = raw.substring(i, lastIndex); + i = lastIndex + 1; + } + + lastIndex = raw.indexOf('\0', i); + + if (lastIndex === -1) { + return; + } + + entry.path = raw.substring(i, lastIndex); + + // If path ends with slash, it must be a nested git repo + if (entry.path[entry.path.length - 1] !== '/') { + this.result.push(entry); + } + + return lastIndex + 1; + } +} + +export class Repository { + + constructor( + private _git: Git, + private repositoryRoot: string + ) { } + + get git(): Git { + return this._git; + } + + get root(): string { + return this.repositoryRoot; + } + + // TODO@Joao: rename to exec + async run(args: string[], options: any = {}): Promise { + return await this.git.exec(this.repositoryRoot, args, options); + } + + stream(args: string[], options: any = {}): cp.ChildProcess { + return this.git.stream(this.repositoryRoot, args, options); + } + + spawn(args: string[], options: any = {}): cp.ChildProcess { + return this.git.spawn(args, options); + } + + async config(scope: string, key: string, value: any, options: any): Promise { + const args = ['config']; + + if (scope) { + args.push('--' + scope); + } + + args.push(key); + + if (value) { + args.push(value); + } + + const result = await this.run(args, options); + return result.stdout; + } + + async buffer(object: string, encoding: string = 'utf8'): Promise { + const child = this.stream(['show', object]); + + if (!child.stdout) { + return Promise.reject('Can\'t open file from git'); + } + + const { exitCode, stdout } = await exec(child, { encoding }); + + if (exitCode) { + return Promise.reject(new GitError({ + message: 'Could not show object.', + exitCode + })); + } + + return stdout; + + // TODO@joao + // return new Promise((c, e) => { + // detectMimesFromStream(child.stdout, null, (err, result) => { + // if (err) { + // e(err); + // } else if (isBinaryMime(result.mimes)) { + // e({ + // message: localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), + // fileOperationResult: FileOperationResult.FILE_IS_BINARY + // }); + // } else { + // c(this.doBuffer(object)); + // } + // }); + // }); + } + + async add(paths: string[]): Promise { + const args = ['add', '-A', '--']; + + if (paths && paths.length) { + args.push.apply(args, paths); + } else { + args.push('.'); + } + + await this.run(args); + } + + async stage(path: string, data: string): Promise { + const child = this.stream(['hash-object', '--stdin', '-w', '--path', path], { stdio: [null, null, null] }); + child.stdin.end(data, 'utf8'); + + const { exitCode, stdout } = await exec(child); + + if (exitCode) { + throw new GitError({ + message: 'Could not hash object.', + exitCode: exitCode + }); + } + + await this.run(['update-index', '--cacheinfo', '100644', stdout, path]); + } + + async checkout(treeish: string, paths: string[]): Promise { + const args = ['checkout', '-q']; + + if (treeish) { + args.push(treeish); + } + + if (paths && paths.length) { + args.push('--'); + args.push.apply(args, paths); + } + + try { + await this.run(args); + } catch (err) { + if (/Please, commit your changes or stash them/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.DirtyWorkTree; + } + + throw err; + } + } + + async commit(message: string, opts: { all?: boolean, amend?: boolean, signoff?: boolean, signCommit?: boolean } = Object.create(null)): Promise { + const args = ['commit', '--quiet', '--allow-empty-message', '--file', '-']; + + if (opts.all) { + args.push('--all'); + } + + if (opts.amend) { + args.push('--amend'); + } + + if (opts.signoff) { + args.push('--signoff'); + } + + if (opts.signCommit) { + args.push('-S'); + } + + try { + await this.run(args, { input: message || '' }); + } catch (commitErr) { + if (/not possible because you have unmerged files/.test(commitErr.stderr || '')) { + commitErr.gitErrorCode = GitErrorCodes.UnmergedChanges; + throw commitErr; + } + + try { + await this.run(['config', '--get-all', 'user.name']); + } catch (err) { + err.gitErrorCode = GitErrorCodes.NoUserNameConfigured; + throw err; + } + + try { + await this.run(['config', '--get-all', 'user.email']); + } catch (err) { + err.gitErrorCode = GitErrorCodes.NoUserEmailConfigured; + throw err; + } + + throw commitErr; + } + } + + async branch(name: string, checkout: boolean): Promise { + const args = checkout ? ['checkout', '-q', '-b', name] : ['branch', '-q', name]; + await this.run(args); + } + + async deleteBranch(name: string, force?: boolean): Promise { + const args = ['branch', force ? '-D' : '-d', name]; + await this.run(args); + } + + async merge(ref: string): Promise { + const args = ['merge', ref]; + + try { + await this.run(args); + } catch (err) { + if (/^CONFLICT /m.test(err.stdout || '')) { + err.gitErrorCode = GitErrorCodes.Conflict; + } + + throw err; + } + } + + async tag(name: string, message?: string): Promise { + let args = ['tag']; + + if (message) { + args = [...args, '-a', name, '-m', message]; + } else { + args = [...args, name]; + } + + await this.run(args); + } + + async clean(paths: string[]): Promise { + const pathsByGroup = groupBy(paths, p => path.dirname(p)); + const groups = Object.keys(pathsByGroup).map(k => pathsByGroup[k]); + const tasks = groups.map(paths => () => this.run(['clean', '-f', '-q', '--'].concat(paths))); + + for (let task of tasks) { + await task(); + } + } + + async undo(): Promise { + await this.run(['clean', '-fd']); + + try { + await this.run(['checkout', '--', '.']); + } catch (err) { + if (/did not match any file\(s\) known to git\./.test(err.stderr || '')) { + return; + } + + throw err; + } + } + + async reset(treeish: string, hard: boolean = false): Promise { + const args = ['reset']; + + if (hard) { + args.push('--hard'); + } + + args.push(treeish); + + await this.run(args); + } + + async revert(treeish: string, paths: string[]): Promise { + const result = await this.run(['branch']); + let args: string[]; + + // In case there are no branches, we must use rm --cached + if (!result.stdout) { + args = ['rm', '--cached', '-r', '--']; + } else { + args = ['reset', '-q', treeish, '--']; + } + + if (paths && paths.length) { + args.push.apply(args, paths); + } else { + args.push('.'); + } + + try { + await this.run(args); + } catch (err) { + // In case there are merge conflicts to be resolved, git reset will output + // some "needs merge" data. We try to get around that. + if (/([^:]+: needs merge\n)+/m.test(err.stdout || '')) { + return; + } + + throw err; + } + } + + async fetch(): Promise { + try { + await this.run(['fetch']); + } catch (err) { + if (/No remote repository specified\./.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.NoRemoteRepositorySpecified; + } else if (/Could not read from remote repository/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.RemoteConnectionError; + } + + throw err; + } + } + + async pull(rebase?: boolean, remote?: string, branch?: string): Promise { + const args = ['pull']; + + if (rebase) { + args.push('-r'); + } + + if (remote && branch) { + args.push(remote); + args.push(branch); + } + + try { + await this.run(args); + } catch (err) { + if (/^CONFLICT \([^)]+\): \b/m.test(err.stdout || '')) { + err.gitErrorCode = GitErrorCodes.Conflict; + } else if (/Please tell me who you are\./.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.NoUserNameConfigured; + } else if (/Could not read from remote repository/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.RemoteConnectionError; + } else if (/Pull is not possible because you have unmerged files|Cannot pull with rebase: You have unstaged changes|Your local changes to the following files would be overwritten|Please, commit your changes before you can merge/.test(err.stderr)) { + err.gitErrorCode = GitErrorCodes.DirtyWorkTree; + } + + throw err; + } + } + + async push(remote?: string, name?: string, setUpstream: boolean = false, tags = false): Promise { + const args = ['push']; + + if (setUpstream) { + args.push('-u'); + } + + if (tags) { + args.push('--tags'); + } + + if (remote) { + args.push(remote); + } + + if (name) { + args.push(name); + } + + try { + await this.run(args); + } catch (err) { + if (/^error: failed to push some refs to\b/m.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.PushRejected; + } else if (/Could not read from remote repository/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.RemoteConnectionError; + } + + throw err; + } + } + + async createStash(message?: string): Promise { + try { + const args = ['stash', 'save']; + + if (message) { + args.push('--', message); + } + + await this.run(args); + } catch (err) { + if (/No local changes to save/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.NoLocalChanges; + } + + throw err; + } + } + + async popStash(index?: number): Promise { + try { + const args = ['stash', 'pop']; + + if (typeof index === 'string') { + args.push(`stash@{${index}}`); + } + + await this.run(args); + } catch (err) { + if (/No stash found/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.NoStashFound; + } else if (/error: Your local changes to the following files would be overwritten/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.LocalChangesOverwritten; + } + + throw err; + } + } + + getStatus(limit = 5000): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> { + return new Promise<{ status: IFileStatus[]; didHitLimit: boolean; }>((c, e) => { + const parser = new GitStatusParser(); + const child = this.stream(['status', '-z', '-u']); + + const onExit = exitCode => { + if (exitCode !== 0) { + const stderr = stderrData.join(''); + return e(new GitError({ + message: 'Failed to execute git', + stderr, + exitCode, + gitErrorCode: getGitErrorCode(stderr), + gitCommand: 'status' + })); + } + + c({ status: parser.status, didHitLimit: false }); + }; + + const onStdoutData = (raw: string) => { + parser.update(raw); + + if (parser.status.length > 5000) { + child.removeListener('exit', onExit); + child.stdout.removeListener('data', onStdoutData); + child.kill(); + + c({ status: parser.status.slice(0, 5000), didHitLimit: true }); + } + }; + + child.stdout.setEncoding('utf8'); + child.stdout.on('data', onStdoutData); + + const stderrData: string[] = []; + child.stderr.setEncoding('utf8'); + child.stderr.on('data', raw => stderrData.push(raw as string)); + + child.on('error', e); + child.on('exit', onExit); + }); + } + + async getHEAD(): Promise { + try { + const result = await this.run(['symbolic-ref', '--short', 'HEAD']); + + if (!result.stdout) { + throw new Error('Not in a branch'); + } + + return { name: result.stdout.trim(), commit: void 0, type: RefType.Head }; + } catch (err) { + const result = await this.run(['rev-parse', 'HEAD']); + + if (!result.stdout) { + throw new Error('Error parsing HEAD'); + } + + return { name: void 0, commit: result.stdout.trim(), type: RefType.Head }; + } + } + + async getRefs(): Promise { + const result = await this.run(['for-each-ref', '--format', '%(refname) %(objectname)']); + + const fn = (line): Ref | null => { + let match: RegExpExecArray | null; + + if (match = /^refs\/heads\/([^ ]+) ([0-9a-f]{40})$/.exec(line)) { + return { name: match[1], commit: match[2], type: RefType.Head }; + } else if (match = /^refs\/remotes\/([^/]+)\/([^ ]+) ([0-9a-f]{40})$/.exec(line)) { + return { name: `${match[1]}/${match[2]}`, commit: match[3], type: RefType.RemoteHead, remote: match[1] }; + } else if (match = /^refs\/tags\/([^ ]+) ([0-9a-f]{40})$/.exec(line)) { + return { name: match[1], commit: match[2], type: RefType.Tag }; + } + + return null; + }; + + return result.stdout.trim().split('\n') + .filter(line => !!line) + .map(fn) + .filter(ref => !!ref) as Ref[]; + } + + async getStashes(): Promise { + const result = await this.run(['stash', 'list']); + const regex = /^stash@{(\d+)}:(.+)$/; + const rawStashes = result.stdout.trim().split('\n') + .filter(b => !!b) + .map(line => regex.exec(line)) + .filter(g => !!g) + .map(([, index, description]: RegExpExecArray) => ({ index: parseInt(index), description })); + + return rawStashes; + } + + async getRemotes(): Promise { + const result = await this.run(['remote', '--verbose']); + const regex = /^([^\s]+)\s+([^\s]+)\s/; + const rawRemotes = result.stdout.trim().split('\n') + .filter(b => !!b) + .map(line => regex.exec(line)) + .filter(g => !!g) + .map((groups: RegExpExecArray) => ({ name: groups[1], url: groups[2] })); + + return uniqBy(rawRemotes, remote => remote.name); + } + + async getBranch(name: string): Promise { + if (name === 'HEAD') { + return this.getHEAD(); + } + + const result = await this.run(['rev-parse', name]); + + if (!result.stdout) { + return Promise.reject(new Error('No such branch')); + } + + const commit = result.stdout.trim(); + + try { + const res2 = await this.run(['rev-parse', '--symbolic-full-name', '--abbrev-ref', name + '@{u}']); + const upstream = res2.stdout.trim(); + + const res3 = await this.run(['rev-list', '--left-right', name + '...' + upstream]); + + let ahead = 0, behind = 0; + let i = 0; + + while (i < res3.stdout.length) { + switch (res3.stdout.charAt(i)) { + case '<': ahead++; break; + case '>': behind++; break; + default: i++; break; + } + + while (res3.stdout.charAt(i++) !== '\n') { /* no-op */ } + } + + return { name, type: RefType.Head, commit, upstream, ahead, behind }; + } catch (err) { + return { name, type: RefType.Head, commit }; + } + } + + async getCommitTemplate(): Promise { + try { + const result = await this.run(['config', '--get', 'commit.template']); + + if (!result.stdout) { + return ''; + } + + // https://github.com/git/git/blob/3a0f269e7c82aa3a87323cb7ae04ac5f129f036b/path.c#L612 + const homedir = os.homedir(); + let templatePath = result.stdout.trim() + .replace(/^~([^\/]*)\//, (_, user) => `${user ? path.join(path.dirname(homedir), user) : homedir}/`); + + if (!path.isAbsolute(templatePath)) { + templatePath = path.join(this.repositoryRoot, templatePath); + } + + const raw = await readfile(templatePath, 'utf8'); + return raw.replace(/^\s*#.*$\n?/gm, '').trim(); + + } catch (err) { + return ''; + } + } + + async getCommit(ref: string): Promise { + const result = await this.run(['show', '-s', '--format=%H\n%B', ref]); + const match = /^([0-9a-f]{40})\n([^]*)$/m.exec(result.stdout.trim()); + + if (!match) { + return Promise.reject('bad commit format'); + } + + return { hash: match[1], message: match[2] }; + } +} diff --git a/extensions/git/src/iterators.ts b/extensions/git/src/iterators.ts new file mode 100644 index 0000000000..999d55c162 --- /dev/null +++ b/extensions/git/src/iterators.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +function* filter(it: IterableIterator, condition: (t: T, i: number) => boolean): IterableIterator { + let i = 0; + for (let t of it) { + if (condition(t, i++)) { + yield t; + } + } +} + +function* map(it: IterableIterator, fn: (t: T, i: number) => R): IterableIterator { + let i = 0; + for (let t of it) { + yield fn(t, i++); + } +} + +export interface FunctionalIterator extends Iterable { + filter(condition: (t: T, i: number) => boolean): FunctionalIterator; + map(fn: (t: T, i: number) => R): FunctionalIterator; + toArray(): T[]; +} + +class FunctionalIteratorImpl implements FunctionalIterator { + + constructor(private iterator: IterableIterator) { } + + filter(condition: (t: T, i: number) => boolean): FunctionalIterator { + return new FunctionalIteratorImpl(filter(this.iterator, condition)); + } + + map(fn: (t: T, i: number) => R): FunctionalIterator { + return new FunctionalIteratorImpl(map(this.iterator, fn)); + } + + toArray(): T[] { + return Array.from(this.iterator); + } + + [Symbol.iterator](): IterableIterator { + return this.iterator; + } +} + +export function iterate(obj: T[] | IterableIterator): FunctionalIterator { + if (Array.isArray(obj)) { + return new FunctionalIteratorImpl(obj[Symbol.iterator]()); + } + + return new FunctionalIteratorImpl(obj); +} \ No newline at end of file diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts new file mode 100644 index 0000000000..9806f69119 --- /dev/null +++ b/extensions/git/src/main.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vscode-nls'; +const localize = nls.config(process.env.VSCODE_NLS_CONFIG)(); +import { ExtensionContext, workspace, window, Disposable, commands, Uri } from 'vscode'; +import { findGit, Git, IGit } from './git'; +import { Model } from './model'; +import { CommandCenter } from './commands'; +import { GitContentProvider } from './contentProvider'; +import { Askpass } from './askpass'; +import { toDisposable } from './util'; +import TelemetryReporter from 'vscode-extension-telemetry'; + +async function init(context: ExtensionContext, disposables: Disposable[]): Promise { + const { name, version, aiKey } = require(context.asAbsolutePath('./package.json')) as { name: string, version: string, aiKey: string }; + const telemetryReporter: TelemetryReporter = new TelemetryReporter(name, version, aiKey); + disposables.push(telemetryReporter); + + const outputChannel = window.createOutputChannel('Git'); + disposables.push(outputChannel); + + const config = workspace.getConfiguration('git'); + const enabled = config.get('enabled') === true; + const pathHint = workspace.getConfiguration('git').get('path'); + const info = await findGit(pathHint); + const askpass = new Askpass(); + const env = await askpass.getEnv(); + const git = new Git({ gitPath: info.path, version: info.version, env }); + const model = new Model(git); + disposables.push(model); + + const onRepository = () => commands.executeCommand('setContext', 'gitOpenRepositoryCount', `${model.repositories.length}`); + model.onDidOpenRepository(onRepository, null, disposables); + model.onDidCloseRepository(onRepository, null, disposables); + onRepository(); + + if (!enabled) { + const commandCenter = new CommandCenter(git, model, outputChannel, telemetryReporter); + disposables.push(commandCenter); + return; + } + + outputChannel.appendLine(localize('using git', "Using git {0} from {1}", info.version, info.path)); + + const onOutput = str => outputChannel.append(str); + git.onOutput.addListener('log', onOutput); + disposables.push(toDisposable(() => git.onOutput.removeListener('log', onOutput))); + + disposables.push( + new CommandCenter(git, model, outputChannel, telemetryReporter), + new GitContentProvider(model), + ); + + await checkGitVersion(info); +} + +export function activate(context: ExtensionContext): any { + const disposables: Disposable[] = []; + context.subscriptions.push(new Disposable(() => Disposable.from(...disposables).dispose())); + + init(context, disposables) + .catch(err => console.error(err)); +} + +async function checkGitVersion(info: IGit): Promise { + + // {{SQL CARBON EDIT}} + // remove Git version check on since for Carbon + + return; + + /* + const config = workspace.getConfiguration('git'); + const shouldIgnore = config.get('ignoreLegacyWarning') === true; + + if (shouldIgnore) { + return; + } + + if (!/^[01]/.test(info.version)) { + return; + } + + const update = localize('updateGit', "Update Git"); + const neverShowAgain = localize('neverShowAgain', "Don't show again"); + + const choice = await window.showWarningMessage( + localize('git20', "You seem to have git {0} installed. Code works best with git >= 2", info.version), + update, + neverShowAgain + ); + + if (choice === update) { + commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/')); + } else if (choice === neverShowAgain) { + await config.update('ignoreLegacyWarning', true, true); + } + */ +} \ No newline at end of file diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts new file mode 100644 index 0000000000..bfcd5d6a61 --- /dev/null +++ b/extensions/git/src/model.ts @@ -0,0 +1,291 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor } from 'vscode'; +import { Repository, RepositoryState } from './repository'; +import { memoize, sequentialize, debounce } from './decorators'; +import { dispose, anyEvent, filterEvent } from './util'; +import { Git, GitErrorCodes } from './git'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +class RepositoryPick implements QuickPickItem { + @memoize get label(): string { return path.basename(this.repository.root); } + @memoize get description(): string { return path.dirname(this.repository.root); } + constructor(public readonly repository: Repository) { } +} + +export interface ModelChangeEvent { + repository: Repository; + uri: Uri; +} + +interface OpenRepository extends Disposable { + repository: Repository; +} + +export class Model { + + private _onDidOpenRepository = new EventEmitter(); + readonly onDidOpenRepository: Event = this._onDidOpenRepository.event; + + private _onDidCloseRepository = new EventEmitter(); + readonly onDidCloseRepository: Event = this._onDidCloseRepository.event; + + private _onDidChangeRepository = new EventEmitter(); + readonly onDidChangeRepository: Event = this._onDidChangeRepository.event; + + private openRepositories: OpenRepository[] = []; + get repositories(): Repository[] { return this.openRepositories.map(r => r.repository); } + + private possibleGitRepositoryPaths = new Set(); + + private enabled = false; + private configurationChangeDisposable: Disposable; + private disposables: Disposable[] = []; + + constructor(private git: Git) { + const config = workspace.getConfiguration('git'); + this.enabled = config.get('enabled') === true; + + this.configurationChangeDisposable = workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this); + + if (this.enabled) { + this.enable(); + } + } + + private onDidChangeConfiguration(): void { + const config = workspace.getConfiguration('git'); + const enabled = config.get('enabled') === true; + + if (enabled === this.enabled) { + return; + } + + this.enabled = enabled; + + if (enabled) { + this.enable(); + } else { + this.disable(); + } + } + + private enable(): void { + workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables); + this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }); + + window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables); + this.onDidChangeVisibleTextEditors(window.visibleTextEditors); + + const fsWatcher = workspace.createFileSystemWatcher('**'); + this.disposables.push(fsWatcher); + + const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete); + const onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\/\.git\//.test(uri.path)); + const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri)); + onPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables); + + this.scanWorkspaceFolders(); + } + + private disable(): void { + const openRepositories = [...this.openRepositories]; + openRepositories.forEach(r => r.dispose()); + this.openRepositories = []; + + this.possibleGitRepositoryPaths.clear(); + this.disposables = dispose(this.disposables); + } + + /** + * Scans the first level of each workspace folder, looking + * for git repositories. + */ + private async scanWorkspaceFolders(): Promise { + for (const folder of workspace.workspaceFolders || []) { + const root = folder.uri.fsPath; + const children = await new Promise((c, e) => fs.readdir(root, (err, r) => err ? e(err) : c(r))); + + children + .filter(child => child !== '.git') + .forEach(child => this.tryOpenRepository(path.join(root, child))); + } + } + + private onPossibleGitRepositoryChange(uri: Uri): void { + const possibleGitRepositoryPath = uri.fsPath.replace(/\.git.*$/, ''); + this.possibleGitRepositoryPaths.add(possibleGitRepositoryPath); + this.eventuallyScanPossibleGitRepositories(); + } + + @debounce(500) + private eventuallyScanPossibleGitRepositories(): void { + for (const path of this.possibleGitRepositoryPaths) { + this.tryOpenRepository(path); + } + + this.possibleGitRepositoryPaths.clear(); + } + + private async onDidChangeWorkspaceFolders({ added, removed }: WorkspaceFoldersChangeEvent): Promise { + const possibleRepositoryFolders = added + .filter(folder => !this.getOpenRepository(folder.uri)); + + const activeRepositoriesList = window.visibleTextEditors + .map(editor => this.getRepository(editor.document.uri)) + .filter(repository => !!repository) as Repository[]; + + const activeRepositories = new Set(activeRepositoriesList); + const openRepositoriesToDispose = removed + .map(folder => this.getOpenRepository(folder.uri)) + .filter(r => !!r && !activeRepositories.has(r.repository)) as OpenRepository[]; + + possibleRepositoryFolders.forEach(p => this.tryOpenRepository(p.uri.fsPath)); + openRepositoriesToDispose.forEach(r => r.dispose()); + } + + private onDidChangeVisibleTextEditors(editors: TextEditor[]): void { + editors.forEach(editor => { + const uri = editor.document.uri; + + if (uri.scheme !== 'file') { + return; + } + + const repository = this.getRepository(uri); + + if (repository) { + return; + } + + this.tryOpenRepository(path.dirname(uri.fsPath)); + }); + } + + @sequentialize + async tryOpenRepository(path: string): Promise { + if (this.getRepository(path)) { + return; + } + + try { + const repositoryRoot = await this.git.getRepositoryRoot(path); + + // This can happen whenever `path` has the wrong case sensitivity in + // case insensitive file systems + // https://github.com/Microsoft/vscode/issues/33498 + if (this.getRepository(repositoryRoot)) { + return; + } + + const repository = new Repository(this.git.open(repositoryRoot)); + + this.open(repository); + } catch (err) { + if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) { + return; + } + + // console.error('Failed to find repository:', err); + } + } + + private open(repository: Repository): void { + const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed); + const disappearListener = onDidDisappearRepository(() => dispose()); + const changeListener = repository.onDidChangeRepository(uri => this._onDidChangeRepository.fire({ repository, uri })); + const dispose = () => { + disappearListener.dispose(); + changeListener.dispose(); + repository.dispose(); + this.openRepositories = this.openRepositories.filter(e => e !== openRepository); + this._onDidCloseRepository.fire(repository); + }; + + const openRepository = { repository, dispose }; + this.openRepositories.push(openRepository); + this._onDidOpenRepository.fire(repository); + } + + async pickRepository(): Promise { + if (this.openRepositories.length === 0) { + throw new Error(localize('no repositories', "There are no available repositories")); + } + + const picks = this.openRepositories.map(e => new RepositoryPick(e.repository)); + const placeHolder = localize('pick repo', "Choose a repository"); + const pick = await window.showQuickPick(picks, { placeHolder }); + + return pick && pick.repository; + } + + getRepository(sourceControl: SourceControl): Repository | undefined; + getRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined; + getRepository(path: string): Repository | undefined; + getRepository(resource: Uri): Repository | undefined; + getRepository(hint: any): Repository | undefined { + const liveRepository = this.getOpenRepository(hint); + return liveRepository && liveRepository.repository; + } + + private getOpenRepository(repository: Repository): OpenRepository | undefined; + private getOpenRepository(sourceControl: SourceControl): OpenRepository | undefined; + private getOpenRepository(resourceGroup: SourceControlResourceGroup): OpenRepository | undefined; + private getOpenRepository(path: string): OpenRepository | undefined; + private getOpenRepository(resource: Uri): OpenRepository | undefined; + private getOpenRepository(hint: any): OpenRepository | undefined { + if (!hint) { + return undefined; + } + + if (hint instanceof Repository) { + return this.openRepositories.filter(r => r.repository === hint)[0]; + } + + if (typeof hint === 'string') { + hint = Uri.file(hint); + } + + if (hint instanceof Uri) { + const resourcePath = hint.fsPath; + + for (const liveRepository of this.openRepositories) { + const relativePath = path.relative(liveRepository.repository.root, resourcePath); + + if (!/^\.\./.test(relativePath)) { + return liveRepository; + } + } + + return undefined; + } + + for (const liveRepository of this.openRepositories) { + const repository = liveRepository.repository; + + if (hint === repository.sourceControl) { + return liveRepository; + } + + if (hint === repository.mergeGroup || hint === repository.indexGroup || hint === repository.workingTreeGroup) { + return liveRepository; + } + } + + return undefined; + } + + dispose(): void { + this.disable(); + this.configurationChangeDisposable.dispose(); + } +} \ No newline at end of file diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts new file mode 100644 index 0000000000..e61df649d3 --- /dev/null +++ b/extensions/git/src/repository.ts @@ -0,0 +1,841 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, Disposable, ProgressLocation, window, workspace, WorkspaceEdit } from 'vscode'; +import { Repository as BaseRepository, Ref, Branch, Remote, Commit, GitErrorCodes, Stash } from './git'; +import { anyEvent, filterEvent, eventToPromise, dispose, find } from './util'; +import { memoize, throttle, debounce } from './decorators'; +import { toGitUri } from './uri'; +import { AutoFetcher } from './autofetch'; +import * as path from 'path'; +import * as nls from 'vscode-nls'; +import * as fs from 'fs'; +import { StatusBarCommands } from './statusbar'; + +const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); + +const localize = nls.loadMessageBundle(); +const iconsRootPath = path.join(path.dirname(__dirname), 'resources', 'icons'); + +function getIconUri(iconName: string, theme: string): Uri { + return Uri.file(path.join(iconsRootPath, theme, `${iconName}.svg`)); +} + +export enum RepositoryState { + Idle, + Disposed +} + +export enum Status { + INDEX_MODIFIED, + INDEX_ADDED, + INDEX_DELETED, + INDEX_RENAMED, + INDEX_COPIED, + + MODIFIED, + DELETED, + UNTRACKED, + IGNORED, + + ADDED_BY_US, + ADDED_BY_THEM, + DELETED_BY_US, + DELETED_BY_THEM, + BOTH_ADDED, + BOTH_DELETED, + BOTH_MODIFIED +} + +export enum ResourceGroupType { + Merge, + Index, + WorkingTree +} + +export class Resource implements SourceControlResourceState { + + @memoize + get resourceUri(): Uri { + if (this.renameResourceUri && (this._type === Status.MODIFIED || this._type === Status.DELETED || this._type === Status.INDEX_RENAMED || this._type === Status.INDEX_COPIED)) { + return this.renameResourceUri; + } + + return this._resourceUri; + } + + @memoize + get command(): Command { + return { + command: 'git.openResource', + title: localize('open', "Open"), + arguments: [this] + }; + } + + get resourceGroupType(): ResourceGroupType { return this._resourceGroupType; } + get type(): Status { return this._type; } + get original(): Uri { return this._resourceUri; } + get renameResourceUri(): Uri | undefined { return this._renameResourceUri; } + + private static Icons = { + light: { + Modified: getIconUri('status-modified', 'light'), + Added: getIconUri('status-added', 'light'), + Deleted: getIconUri('status-deleted', 'light'), + Renamed: getIconUri('status-renamed', 'light'), + Copied: getIconUri('status-copied', 'light'), + Untracked: getIconUri('status-untracked', 'light'), + Ignored: getIconUri('status-ignored', 'light'), + Conflict: getIconUri('status-conflict', 'light'), + }, + dark: { + Modified: getIconUri('status-modified', 'dark'), + Added: getIconUri('status-added', 'dark'), + Deleted: getIconUri('status-deleted', 'dark'), + Renamed: getIconUri('status-renamed', 'dark'), + Copied: getIconUri('status-copied', 'dark'), + Untracked: getIconUri('status-untracked', 'dark'), + Ignored: getIconUri('status-ignored', 'dark'), + Conflict: getIconUri('status-conflict', 'dark') + } + }; + + private getIconPath(theme: string): Uri | undefined { + switch (this.type) { + case Status.INDEX_MODIFIED: return Resource.Icons[theme].Modified; + case Status.MODIFIED: return Resource.Icons[theme].Modified; + case Status.INDEX_ADDED: return Resource.Icons[theme].Added; + case Status.INDEX_DELETED: return Resource.Icons[theme].Deleted; + case Status.DELETED: return Resource.Icons[theme].Deleted; + case Status.INDEX_RENAMED: return Resource.Icons[theme].Renamed; + case Status.INDEX_COPIED: return Resource.Icons[theme].Copied; + case Status.UNTRACKED: return Resource.Icons[theme].Untracked; + case Status.IGNORED: return Resource.Icons[theme].Ignored; + case Status.BOTH_DELETED: return Resource.Icons[theme].Conflict; + case Status.ADDED_BY_US: return Resource.Icons[theme].Conflict; + case Status.DELETED_BY_THEM: return Resource.Icons[theme].Conflict; + case Status.ADDED_BY_THEM: return Resource.Icons[theme].Conflict; + case Status.DELETED_BY_US: return Resource.Icons[theme].Conflict; + case Status.BOTH_ADDED: return Resource.Icons[theme].Conflict; + case Status.BOTH_MODIFIED: return Resource.Icons[theme].Conflict; + default: return void 0; + } + } + + private get tooltip(): string { + switch (this.type) { + case Status.INDEX_MODIFIED: return localize('index modified', "Index Modified"); + case Status.MODIFIED: return localize('modified', "Modified"); + case Status.INDEX_ADDED: return localize('index added', "Index Added"); + case Status.INDEX_DELETED: return localize('index deleted', "Index Deleted"); + case Status.DELETED: return localize('deleted', "Deleted"); + case Status.INDEX_RENAMED: return localize('index renamed', "Index Renamed"); + case Status.INDEX_COPIED: return localize('index copied', "Index Copied"); + case Status.UNTRACKED: return localize('untracked', "Untracked"); + case Status.IGNORED: return localize('ignored', "Ignored"); + case Status.BOTH_DELETED: return localize('both deleted', "Both Deleted"); + case Status.ADDED_BY_US: return localize('added by us', "Added By Us"); + case Status.DELETED_BY_THEM: return localize('deleted by them', "Deleted By Them"); + case Status.ADDED_BY_THEM: return localize('added by them', "Added By Them"); + case Status.DELETED_BY_US: return localize('deleted by us', "Deleted By Us"); + case Status.BOTH_ADDED: return localize('both added', "Both Added"); + case Status.BOTH_MODIFIED: return localize('both modified', "Both Modified"); + default: return ''; + } + } + + private get strikeThrough(): boolean { + switch (this.type) { + case Status.DELETED: + case Status.BOTH_DELETED: + case Status.DELETED_BY_THEM: + case Status.DELETED_BY_US: + case Status.INDEX_DELETED: + return true; + default: + return false; + } + } + + @memoize + private get faded(): boolean { + // TODO@joao + return false; + // const workspaceRootPath = this.workspaceRoot.fsPath; + // return this.resourceUri.fsPath.substr(0, workspaceRootPath.length) !== workspaceRootPath; + } + + get decorations(): SourceControlResourceDecorations { + const light = { iconPath: this.getIconPath('light') }; + const dark = { iconPath: this.getIconPath('dark') }; + const tooltip = this.tooltip; + const strikeThrough = this.strikeThrough; + const faded = this.faded; + + return { strikeThrough, faded, tooltip, light, dark }; + } + + constructor( + private _resourceGroupType: ResourceGroupType, + private _resourceUri: Uri, + private _type: Status, + private _renameResourceUri?: Uri + ) { } +} + +export enum Operation { + Status = 1 << 0, + Add = 1 << 1, + RevertFiles = 1 << 2, + Commit = 1 << 3, + Clean = 1 << 4, + Branch = 1 << 5, + Checkout = 1 << 6, + Reset = 1 << 7, + Fetch = 1 << 8, + Pull = 1 << 9, + Push = 1 << 10, + Sync = 1 << 11, + Show = 1 << 12, + Stage = 1 << 13, + GetCommitTemplate = 1 << 14, + DeleteBranch = 1 << 15, + Merge = 1 << 16, + Ignore = 1 << 17, + Tag = 1 << 18, + Stash = 1 << 19 +} + +// function getOperationName(operation: Operation): string { +// switch (operation) { +// case Operation.Status: return 'Status'; +// case Operation.Add: return 'Add'; +// case Operation.RevertFiles: return 'RevertFiles'; +// case Operation.Commit: return 'Commit'; +// case Operation.Clean: return 'Clean'; +// case Operation.Branch: return 'Branch'; +// case Operation.Checkout: return 'Checkout'; +// case Operation.Reset: return 'Reset'; +// case Operation.Fetch: return 'Fetch'; +// case Operation.Pull: return 'Pull'; +// case Operation.Push: return 'Push'; +// case Operation.Sync: return 'Sync'; +// case Operation.Init: return 'Init'; +// case Operation.Show: return 'Show'; +// case Operation.Stage: return 'Stage'; +// case Operation.GetCommitTemplate: return 'GetCommitTemplate'; +// default: return 'unknown'; +// } +// } + +function isReadOnly(operation: Operation): boolean { + switch (operation) { + case Operation.Show: + case Operation.GetCommitTemplate: + return true; + default: + return false; + } +} + +function shouldShowProgress(operation: Operation): boolean { + switch (operation) { + case Operation.Fetch: + return false; + default: + return true; + } +} + +export interface Operations { + isIdle(): boolean; + isRunning(operation: Operation): boolean; +} + +class OperationsImpl implements Operations { + + constructor(private readonly operations: number = 0) { + // noop + } + + start(operation: Operation): OperationsImpl { + return new OperationsImpl(this.operations | operation); + } + + end(operation: Operation): OperationsImpl { + return new OperationsImpl(this.operations & ~operation); + } + + isRunning(operation: Operation): boolean { + return (this.operations & operation) !== 0; + } + + isIdle(): boolean { + return this.operations === 0; + } +} + +export interface CommitOptions { + all?: boolean; + amend?: boolean; + signoff?: boolean; + signCommit?: boolean; +} + +export interface GitResourceGroup extends SourceControlResourceGroup { + resourceStates: Resource[]; +} + +export class Repository implements Disposable { + + private _onDidChangeRepository = new EventEmitter(); + readonly onDidChangeRepository: Event = this._onDidChangeRepository.event; + + private _onDidChangeState = new EventEmitter(); + readonly onDidChangeState: Event = this._onDidChangeState.event; + + private _onDidChangeStatus = new EventEmitter(); + readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; + + private _onRunOperation = new EventEmitter(); + readonly onRunOperation: Event = this._onRunOperation.event; + + private _onDidRunOperation = new EventEmitter(); + readonly onDidRunOperation: Event = this._onDidRunOperation.event; + + @memoize + get onDidChangeOperations(): Event { + return anyEvent(this.onRunOperation as Event, this.onDidRunOperation as Event); + } + + private _sourceControl: SourceControl; + get sourceControl(): SourceControl { return this._sourceControl; } + + get inputBox(): SourceControlInputBox { return this._sourceControl.inputBox; } + + private _mergeGroup: SourceControlResourceGroup; + get mergeGroup(): GitResourceGroup { return this._mergeGroup as GitResourceGroup; } + + private _indexGroup: SourceControlResourceGroup; + get indexGroup(): GitResourceGroup { return this._indexGroup as GitResourceGroup; } + + private _workingTreeGroup: SourceControlResourceGroup; + get workingTreeGroup(): GitResourceGroup { return this._workingTreeGroup as GitResourceGroup; } + + private _HEAD: Branch | undefined; + get HEAD(): Branch | undefined { + return this._HEAD; + } + + private _refs: Ref[] = []; + get refs(): Ref[] { + return this._refs; + } + + private _remotes: Remote[] = []; + get remotes(): Remote[] { + return this._remotes; + } + + private _operations = new OperationsImpl(); + get operations(): Operations { return this._operations; } + + private _state = RepositoryState.Idle; + get state(): RepositoryState { return this._state; } + set state(state: RepositoryState) { + this._state = state; + this._onDidChangeState.fire(state); + + this._HEAD = undefined; + this._refs = []; + this._remotes = []; + this.mergeGroup.resourceStates = []; + this.indexGroup.resourceStates = []; + this.workingTreeGroup.resourceStates = []; + this._sourceControl.count = 0; + } + + get root(): string { + return this.repository.root; + } + + private isRepositoryHuge = false; + private didWarnAboutLimit = false; + private disposables: Disposable[] = []; + + constructor( + private readonly repository: BaseRepository + ) { + const fsWatcher = workspace.createFileSystemWatcher('**'); + this.disposables.push(fsWatcher); + + const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete); + const onRepositoryChange = filterEvent(onWorkspaceChange, uri => !/^\.\./.test(path.relative(repository.root, uri.fsPath))); + const onRelevantRepositoryChange = filterEvent(onRepositoryChange, uri => !/\/\.git\/index\.lock$/.test(uri.path)); + onRelevantRepositoryChange(this.onFSChange, this, this.disposables); + + const onRelevantGitChange = filterEvent(onRelevantRepositoryChange, uri => /\/\.git\//.test(uri.path)); + onRelevantGitChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables); + + const label = `${path.basename(repository.root)} (Git)`; + + this._sourceControl = scm.createSourceControl('git', label); + this._sourceControl.acceptInputCommand = { command: 'git.commitWithInput', title: localize('commit', "Commit"), arguments: [this._sourceControl] }; + this._sourceControl.quickDiffProvider = this; + this.disposables.push(this._sourceControl); + + this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "Merge Changes")); + this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "Staged Changes")); + this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "Changes")); + + this.mergeGroup.hideWhenEmpty = true; + this.indexGroup.hideWhenEmpty = true; + + this.disposables.push(this.mergeGroup); + this.disposables.push(this.indexGroup); + this.disposables.push(this.workingTreeGroup); + + this.disposables.push(new AutoFetcher(this)); + + const statusBar = new StatusBarCommands(this); + this.disposables.push(statusBar); + statusBar.onDidChange(() => this._sourceControl.statusBarCommands = statusBar.commands, null, this.disposables); + this._sourceControl.statusBarCommands = statusBar.commands; + + this.updateCommitTemplate(); + this.status(); + } + + provideOriginalResource(uri: Uri): Uri | undefined { + if (uri.scheme !== 'file') { + return; + } + + return toGitUri(uri, '', true); + } + + private async updateCommitTemplate(): Promise { + try { + this._sourceControl.commitTemplate = await this.repository.getCommitTemplate(); + } catch (e) { + // noop + } + } + + // @throttle + // async init(): Promise { + // if (this.state !== State.NotAGitRepository) { + // return; + // } + + // await this.git.init(this.workspaceRoot.fsPath); + // await this.status(); + // } + + @throttle + async status(): Promise { + await this.run(Operation.Status); + } + + async add(resources: Uri[]): Promise { + await this.run(Operation.Add, () => this.repository.add(resources.map(r => r.fsPath))); + } + + async stage(resource: Uri, contents: string): Promise { + const relativePath = path.relative(this.repository.root, resource.fsPath).replace(/\\/g, '/'); + await this.run(Operation.Stage, () => this.repository.stage(relativePath, contents)); + } + + async revert(resources: Uri[]): Promise { + await this.run(Operation.RevertFiles, () => this.repository.revert('HEAD', resources.map(r => r.fsPath))); + } + + async commit(message: string, opts: CommitOptions = Object.create(null)): Promise { + await this.run(Operation.Commit, async () => { + if (opts.all) { + await this.repository.add([]); + } + + await this.repository.commit(message, opts); + }); + } + + async clean(resources: Uri[]): Promise { + await this.run(Operation.Clean, async () => { + const toClean: string[] = []; + const toCheckout: string[] = []; + + resources.forEach(r => { + const raw = r.toString(); + const scmResource = find(this.workingTreeGroup.resourceStates, sr => sr.resourceUri.toString() === raw); + + if (!scmResource) { + return; + } + + switch (scmResource.type) { + case Status.UNTRACKED: + case Status.IGNORED: + toClean.push(r.fsPath); + break; + + default: + toCheckout.push(r.fsPath); + break; + } + }); + + const promises: Promise[] = []; + + if (toClean.length > 0) { + promises.push(this.repository.clean(toClean)); + } + + if (toCheckout.length > 0) { + promises.push(this.repository.checkout('', toCheckout)); + } + + await Promise.all(promises); + }); + } + + async branch(name: string): Promise { + await this.run(Operation.Branch, () => this.repository.branch(name, true)); + } + + async deleteBranch(name: string, force?: boolean): Promise { + await this.run(Operation.DeleteBranch, () => this.repository.deleteBranch(name, force)); + } + + async merge(ref: string): Promise { + await this.run(Operation.Merge, () => this.repository.merge(ref)); + } + + async tag(name: string, message?: string): Promise { + await this.run(Operation.Tag, () => this.repository.tag(name, message)); + } + + async checkout(treeish: string): Promise { + await this.run(Operation.Checkout, () => this.repository.checkout(treeish, [])); + } + + async getCommit(ref: string): Promise { + return await this.repository.getCommit(ref); + } + + async reset(treeish: string, hard?: boolean): Promise { + await this.run(Operation.Reset, () => this.repository.reset(treeish, hard)); + } + + @throttle + async fetch(): Promise { + try { + await this.run(Operation.Fetch, () => this.repository.fetch()); + } catch (err) { + // noop + } + } + + @throttle + async pullWithRebase(): Promise { + await this.run(Operation.Pull, () => this.repository.pull(true)); + } + + @throttle + async pull(rebase?: boolean, remote?: string, name?: string): Promise { + await this.run(Operation.Pull, () => this.repository.pull(rebase, remote, name)); + } + + @throttle + async push(): Promise { + await this.run(Operation.Push, () => this.repository.push()); + } + + async pullFrom(rebase?: boolean, remote?: string, branch?: string): Promise { + await this.run(Operation.Pull, () => this.repository.pull(rebase, remote, branch)); + } + + async pushTo(remote?: string, name?: string, setUpstream: boolean = false): Promise { + await this.run(Operation.Push, () => this.repository.push(remote, name, setUpstream)); + } + + async pushTags(remote?: string): Promise { + await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true)); + } + + @throttle + async sync(): Promise { + await this.run(Operation.Sync, async () => { + await this.repository.pull(); + + const shouldPush = this.HEAD && typeof this.HEAD.ahead === 'number' ? this.HEAD.ahead > 0 : true; + + if (shouldPush) { + await this.repository.push(); + } + }); + } + + async show(ref: string, filePath: string): Promise { + return await this.run(Operation.Show, async () => { + const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/'); + const configFiles = workspace.getConfiguration('files'); + const encoding = configFiles.get('encoding'); + + return await this.repository.buffer(`${ref}:${relativePath}`, encoding); + }); + } + + async getStashes(): Promise { + return await this.repository.getStashes(); + } + + async createStash(message?: string): Promise { + return await this.run(Operation.Stash, () => this.repository.createStash(message)); + } + + async popStash(index?: number): Promise { + return await this.run(Operation.Stash, () => this.repository.popStash(index)); + } + + async getCommitTemplate(): Promise { + return await this.run(Operation.GetCommitTemplate, async () => this.repository.getCommitTemplate()); + } + + async ignore(files: Uri[]): Promise { + return await this.run(Operation.Ignore, async () => { + const ignoreFile = `${this.repository.root}${path.sep}.gitignore`; + const textToAppend = files + .map(uri => path.relative(this.repository.root, uri.fsPath).replace(/\\/g, '/')) + .join('\n'); + + const document = await new Promise(c => fs.exists(ignoreFile, c)) + ? await workspace.openTextDocument(ignoreFile) + : await workspace.openTextDocument(Uri.file(ignoreFile).with({ scheme: 'untitled' })); + + await window.showTextDocument(document); + + const edit = new WorkspaceEdit(); + const lastLine = document.lineAt(document.lineCount - 1); + const text = lastLine.isEmptyOrWhitespace ? `${textToAppend}\n` : `\n${textToAppend}\n`; + + edit.insert(document.uri, lastLine.range.end, text); + workspace.applyEdit(edit); + }); + } + + private async run(operation: Operation, runOperation: () => Promise = () => Promise.resolve(null)): Promise { + if (this.state !== RepositoryState.Idle) { + throw new Error('Repository not initialized'); + } + + const run = async () => { + this._operations = this._operations.start(operation); + this._onRunOperation.fire(operation); + + try { + const result = await this.retryRun(runOperation); + + if (!isReadOnly(operation)) { + await this.updateModelState(); + } + + return result; + } catch (err) { + if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) { + this.state = RepositoryState.Disposed; + } + + throw err; + } finally { + this._operations = this._operations.end(operation); + this._onDidRunOperation.fire(operation); + } + }; + + return shouldShowProgress(operation) + ? window.withProgress({ location: ProgressLocation.SourceControl }, run) + : run(); + } + + private async retryRun(runOperation: () => Promise = () => Promise.resolve(null)): Promise { + let attempt = 0; + + while (true) { + try { + attempt++; + return await runOperation(); + } catch (err) { + if (err.gitErrorCode === GitErrorCodes.RepositoryIsLocked && attempt <= 10) { + // quatratic backoff + await timeout(Math.pow(attempt, 2) * 50); + } else { + throw err; + } + } + } + } + + @throttle + private async updateModelState(): Promise { + const { status, didHitLimit } = await this.repository.getStatus(); + const config = workspace.getConfiguration('git'); + const shouldIgnore = config.get('ignoreLimitWarning') === true; + + this.isRepositoryHuge = didHitLimit; + + if (didHitLimit && !shouldIgnore && !this.didWarnAboutLimit) { + const ok = { title: localize('ok', "OK"), isCloseAffordance: true }; + const neverAgain = { title: localize('neveragain', "Never Show Again") }; + + window.showWarningMessage(localize('huge', "The git repository at '{0}' has too many active changes, only a subset of Git features will be enabled.", this.repository.root), ok, neverAgain).then(result => { + if (result === neverAgain) { + config.update('ignoreLimitWarning', true, false); + } + }); + + this.didWarnAboutLimit = true; + } + + let HEAD: Branch | undefined; + + try { + HEAD = await this.repository.getHEAD(); + + if (HEAD.name) { + try { + HEAD = await this.repository.getBranch(HEAD.name); + } catch (err) { + // noop + } + } + } catch (err) { + // noop + } + + const [refs, remotes] = await Promise.all([this.repository.getRefs(), this.repository.getRemotes()]); + + this._HEAD = HEAD; + this._refs = refs; + this._remotes = remotes; + + const index: Resource[] = []; + const workingTree: Resource[] = []; + const merge: Resource[] = []; + + status.forEach(raw => { + const uri = Uri.file(path.join(this.repository.root, raw.path)); + const renameUri = raw.rename ? Uri.file(path.join(this.repository.root, raw.rename)) : undefined; + + switch (raw.x + raw.y) { + case '??': return workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.UNTRACKED)); + case '!!': return workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.IGNORED)); + case 'DD': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_DELETED)); + case 'AU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.ADDED_BY_US)); + case 'UD': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.DELETED_BY_THEM)); + case 'UA': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.ADDED_BY_THEM)); + case 'DU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.DELETED_BY_US)); + case 'AA': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_ADDED)); + case 'UU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_MODIFIED)); + } + + let isModifiedInIndex = false; + + switch (raw.x) { + case 'M': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_MODIFIED)); isModifiedInIndex = true; break; + case 'A': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_ADDED)); break; + case 'D': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_DELETED)); break; + case 'R': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_RENAMED, renameUri)); break; + case 'C': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_COPIED, renameUri)); break; + } + + switch (raw.y) { + case 'M': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.MODIFIED, renameUri)); break; + case 'D': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.DELETED, renameUri)); break; + } + }); + + // set resource groups + this.mergeGroup.resourceStates = merge; + this.indexGroup.resourceStates = index; + this.workingTreeGroup.resourceStates = workingTree; + + // set count badge + const countBadge = workspace.getConfiguration('git').get('countBadge'); + let count = merge.length + index.length + workingTree.length; + + switch (countBadge) { + case 'off': count = 0; break; + case 'tracked': count = count - workingTree.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED).length; break; + } + + this._sourceControl.count = count; + + // set context key + let stateContextKey = ''; + + switch (this.state) { + case RepositoryState.Idle: stateContextKey = 'idle'; break; + case RepositoryState.Disposed: stateContextKey = 'norepo'; break; + } + + this._onDidChangeStatus.fire(); + } + + private onFSChange(uri: Uri): void { + const config = workspace.getConfiguration('git'); + const autorefresh = config.get('autorefresh'); + + if (!autorefresh) { + return; + } + + if (this.isRepositoryHuge) { + return; + } + + if (!this.operations.isIdle()) { + return; + } + + this.eventuallyUpdateWhenIdleAndWait(); + } + + @debounce(1000) + private eventuallyUpdateWhenIdleAndWait(): void { + this.updateWhenIdleAndWait(); + } + + @throttle + private async updateWhenIdleAndWait(): Promise { + await this.whenIdleAndFocused(); + await this.status(); + await timeout(5000); + } + + private async whenIdleAndFocused(): Promise { + while (true) { + if (!this.operations.isIdle()) { + await eventToPromise(this.onDidRunOperation); + continue; + } + + if (!window.state.focused) { + const onDidFocusWindow = filterEvent(window.onDidChangeWindowState, e => e.focused); + await eventToPromise(onDidFocusWindow); + continue; + } + + return; + } + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts new file mode 100644 index 0000000000..1dc7f451de --- /dev/null +++ b/extensions/git/src/staging.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TextDocument, Range, LineChange, Selection } from 'vscode'; + +export function applyLineChanges(original: TextDocument, modified: TextDocument, diffs: LineChange[]): string { + const result: string[] = []; + let currentLine = 0; + + for (let diff of diffs) { + const isInsertion = diff.originalEndLineNumber === 0; + const isDeletion = diff.modifiedEndLineNumber === 0; + + result.push(original.getText(new Range(currentLine, 0, isInsertion ? diff.originalStartLineNumber : diff.originalStartLineNumber - 1, 0))); + + if (!isDeletion) { + let fromLine = diff.modifiedStartLineNumber - 1; + let fromCharacter = 0; + + if (isInsertion && diff.originalStartLineNumber === original.lineCount) { + fromLine = original.lineCount - 1; + fromCharacter = original.lineAt(fromLine).range.end.character; + } + + result.push(modified.getText(new Range(fromLine, fromCharacter, diff.modifiedEndLineNumber, 0))); + } + + currentLine = isInsertion ? diff.originalStartLineNumber : diff.originalEndLineNumber; + } + + result.push(original.getText(new Range(currentLine, 0, original.lineCount, 0))); + + return result.join(''); +} + +export function toLineRanges(selections: Selection[], textDocument: TextDocument): Range[] { + const lineRanges = selections.map(s => { + const startLine = textDocument.lineAt(s.start.line); + const endLine = textDocument.lineAt(s.end.line); + return new Range(startLine.range.start, endLine.range.end); + }); + + lineRanges.sort((a, b) => a.start.line - b.start.line); + + const result = lineRanges.reduce((result, l) => { + if (result.length === 0) { + result.push(l); + return result; + } + + const [last, ...rest] = result; + const intersection = l.intersection(last); + + if (intersection) { + return [intersection, ...rest]; + } + + if (l.start.line === last.end.line + 1) { + const merge = new Range(last.start, l.end); + return [merge, ...rest]; + } + + return [l, ...result]; + }, [] as Range[]); + + result.reverse(); + + return result; +} + +function getModifiedRange(textDocument: TextDocument, diff: LineChange): Range { + return diff.modifiedEndLineNumber === 0 + ? new Range(textDocument.lineAt(diff.modifiedStartLineNumber - 1).range.end, textDocument.lineAt(diff.modifiedStartLineNumber).range.start) + : new Range(textDocument.lineAt(diff.modifiedStartLineNumber - 1).range.start, textDocument.lineAt(diff.modifiedEndLineNumber - 1).range.end); +} + +export function intersectDiffWithRange(textDocument: TextDocument, diff: LineChange, range: Range): LineChange | null { + const modifiedRange = getModifiedRange(textDocument, diff); + const intersection = range.intersection(modifiedRange); + + if (!intersection) { + return null; + } + + if (diff.modifiedEndLineNumber === 0) { + return diff; + } else { + return { + originalStartLineNumber: diff.originalStartLineNumber, + originalEndLineNumber: diff.originalEndLineNumber, + modifiedStartLineNumber: intersection.start.line + 1, + modifiedEndLineNumber: intersection.end.line + 1 + }; + } +} + +export function invertLineChange(diff: LineChange): LineChange { + return { + modifiedStartLineNumber: diff.originalStartLineNumber, + modifiedEndLineNumber: diff.originalEndLineNumber, + originalStartLineNumber: diff.modifiedStartLineNumber, + originalEndLineNumber: diff.modifiedEndLineNumber + }; +} \ No newline at end of file diff --git a/extensions/git/src/statusbar.ts b/extensions/git/src/statusbar.ts new file mode 100644 index 0000000000..710bbbf4a8 --- /dev/null +++ b/extensions/git/src/statusbar.ts @@ -0,0 +1,189 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Disposable, Command, EventEmitter, Event } from 'vscode'; +import { RefType, Branch } from './git'; +import { Repository, Operation } from './repository'; +import { anyEvent, dispose } from './util'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +class CheckoutStatusBar { + + private _onDidChange = new EventEmitter(); + get onDidChange(): Event { return this._onDidChange.event; } + private disposables: Disposable[] = []; + + constructor(private repository: Repository) { + repository.onDidChangeStatus(this._onDidChange.fire, this._onDidChange, this.disposables); + } + + get command(): Command | undefined { + const HEAD = this.repository.HEAD; + + if (!HEAD) { + return undefined; + } + + const tag = this.repository.refs.filter(iref => iref.type === RefType.Tag && iref.commit === HEAD.commit)[0]; + const tagName = tag && tag.name; + const head = HEAD.name || tagName || (HEAD.commit || '').substr(0, 8); + const title = '$(git-branch) ' + + head + + (this.repository.workingTreeGroup.resourceStates.length > 0 ? '*' : '') + + (this.repository.indexGroup.resourceStates.length > 0 ? '+' : '') + + (this.repository.mergeGroup.resourceStates.length > 0 ? '!' : ''); + + return { + command: 'git.checkout', + tooltip: localize('checkout', 'Checkout...'), + title, + arguments: [this.repository.sourceControl] + }; + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + } +} + +interface SyncStatusBarState { + isSyncRunning: boolean; + hasRemotes: boolean; + HEAD: Branch | undefined; +} + +class SyncStatusBar { + + private static StartState: SyncStatusBarState = { + isSyncRunning: false, + hasRemotes: false, + HEAD: undefined + }; + + private _onDidChange = new EventEmitter(); + get onDidChange(): Event { return this._onDidChange.event; } + private disposables: Disposable[] = []; + + private _state: SyncStatusBarState = SyncStatusBar.StartState; + private get state() { return this._state; } + private set state(state: SyncStatusBarState) { + this._state = state; + this._onDidChange.fire(); + } + + constructor(private repository: Repository) { + repository.onDidChangeStatus(this.onModelChange, this, this.disposables); + repository.onDidChangeOperations(this.onOperationsChange, this, this.disposables); + this._onDidChange.fire(); + } + + private onOperationsChange(): void { + this.state = { + ...this.state, + isSyncRunning: this.repository.operations.isRunning(Operation.Sync) + }; + } + + private onModelChange(): void { + this.state = { + ...this.state, + hasRemotes: this.repository.remotes.length > 0, + HEAD: this.repository.HEAD + }; + } + + get command(): Command | undefined { + if (!this.state.hasRemotes) { + return undefined; + } + + const HEAD = this.state.HEAD; + let icon = '$(sync)'; + let text = ''; + let command = ''; + let tooltip = ''; + + if (HEAD && HEAD.name && HEAD.commit) { + if (HEAD.upstream) { + if (HEAD.ahead || HEAD.behind) { + text += `${HEAD.behind}↓ ${HEAD.ahead}↑`; + } + command = 'git.sync'; + tooltip = localize('sync changes', "Synchronize Changes"); + } else { + icon = '$(cloud-upload)'; + command = 'git.publish'; + tooltip = localize('publish changes', "Publish Changes"); + } + } else { + command = ''; + tooltip = ''; + } + + if (this.state.isSyncRunning) { + icon = '$(sync~spin)'; + command = ''; + tooltip = localize('syncing changes', "Synchronizing Changes..."); + } + + return { + command, + title: [icon, text].join(' ').trim(), + tooltip, + arguments: [this.repository.sourceControl] + }; + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + } +} + +export class StatusBarCommands { + + private syncStatusBar: SyncStatusBar; + private checkoutStatusBar: CheckoutStatusBar; + private disposables: Disposable[] = []; + + constructor(repository: Repository) { + this.syncStatusBar = new SyncStatusBar(repository); + this.checkoutStatusBar = new CheckoutStatusBar(repository); + } + + get onDidChange(): Event { + return anyEvent( + this.syncStatusBar.onDidChange, + this.checkoutStatusBar.onDidChange + ); + } + + get commands(): Command[] { + const result: Command[] = []; + + const checkout = this.checkoutStatusBar.command; + + if (checkout) { + result.push(checkout); + } + + const sync = this.syncStatusBar.command; + + if (sync) { + result.push(sync); + } + + return result; + } + + dispose(): void { + this.syncStatusBar.dispose(); + this.checkoutStatusBar.dispose(); + this.disposables = dispose(this.disposables); + } +} \ No newline at end of file diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts new file mode 100644 index 0000000000..8bb559b623 --- /dev/null +++ b/extensions/git/src/test/git.test.ts @@ -0,0 +1,137 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { GitStatusParser } from '../git'; +import * as assert from 'assert'; + +suite('git', () => { + suite('GitStatusParser', () => { + test('empty parser', () => { + const parser = new GitStatusParser(); + assert.deepEqual(parser.status, []); + }); + + test('empty parser 2', () => { + const parser = new GitStatusParser(); + parser.update(''); + assert.deepEqual(parser.status, []); + }); + + test('simple', () => { + const parser = new GitStatusParser(); + parser.update('?? file.txt\0'); + assert.deepEqual(parser.status, [ + { path: 'file.txt', rename: undefined, x: '?', y: '?' } + ]); + }); + + test('simple 2', () => { + const parser = new GitStatusParser(); + parser.update('?? file.txt\0'); + parser.update('?? file2.txt\0'); + parser.update('?? file3.txt\0'); + assert.deepEqual(parser.status, [ + { path: 'file.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file2.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file3.txt', rename: undefined, x: '?', y: '?' } + ]); + }); + + test('empty lines', () => { + const parser = new GitStatusParser(); + parser.update(''); + parser.update('?? file.txt\0'); + parser.update(''); + parser.update(''); + parser.update('?? file2.txt\0'); + parser.update(''); + parser.update('?? file3.txt\0'); + parser.update(''); + assert.deepEqual(parser.status, [ + { path: 'file.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file2.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file3.txt', rename: undefined, x: '?', y: '?' } + ]); + }); + + test('combined', () => { + const parser = new GitStatusParser(); + parser.update('?? file.txt\0?? file2.txt\0?? file3.txt\0'); + assert.deepEqual(parser.status, [ + { path: 'file.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file2.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file3.txt', rename: undefined, x: '?', y: '?' } + ]); + }); + + test('split 1', () => { + const parser = new GitStatusParser(); + parser.update('?? file.txt\0?? file2'); + parser.update('.txt\0?? file3.txt\0'); + assert.deepEqual(parser.status, [ + { path: 'file.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file2.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file3.txt', rename: undefined, x: '?', y: '?' } + ]); + }); + + test('split 2', () => { + const parser = new GitStatusParser(); + parser.update('?? file.txt'); + parser.update('\0?? file2.txt\0?? file3.txt\0'); + assert.deepEqual(parser.status, [ + { path: 'file.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file2.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file3.txt', rename: undefined, x: '?', y: '?' } + ]); + }); + + test('split 3', () => { + const parser = new GitStatusParser(); + parser.update('?? file.txt\0?? file2.txt\0?? file3.txt'); + parser.update('\0'); + assert.deepEqual(parser.status, [ + { path: 'file.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file2.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file3.txt', rename: undefined, x: '?', y: '?' } + ]); + }); + + test('rename', () => { + const parser = new GitStatusParser(); + parser.update('R newfile.txt\0file.txt\0?? file2.txt\0?? file3.txt\0'); + assert.deepEqual(parser.status, [ + { path: 'file.txt', rename: 'newfile.txt', x: 'R', y: ' ' }, + { path: 'file2.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file3.txt', rename: undefined, x: '?', y: '?' } + ]); + }); + + test('rename split', () => { + const parser = new GitStatusParser(); + parser.update('R newfile.txt\0fil'); + parser.update('e.txt\0?? file2.txt\0?? file3.txt\0'); + assert.deepEqual(parser.status, [ + { path: 'file.txt', rename: 'newfile.txt', x: 'R', y: ' ' }, + { path: 'file2.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file3.txt', rename: undefined, x: '?', y: '?' } + ]); + }); + + test('rename split 3', () => { + const parser = new GitStatusParser(); + parser.update('?? file2.txt\0R new'); + parser.update('file.txt\0fil'); + parser.update('e.txt\0?? file3.txt\0'); + assert.deepEqual(parser.status, [ + { path: 'file2.txt', rename: undefined, x: '?', y: '?' }, + { path: 'file.txt', rename: 'newfile.txt', x: 'R', y: ' ' }, + { path: 'file3.txt', rename: undefined, x: '?', y: '?' } + ]); + }); + }); +}); \ No newline at end of file diff --git a/extensions/git/src/typings/refs.d.ts b/extensions/git/src/typings/refs.d.ts new file mode 100644 index 0000000000..31aa9d4d26 --- /dev/null +++ b/extensions/git/src/typings/refs.d.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// +/// +/// diff --git a/extensions/git/src/uri.ts b/extensions/git/src/uri.ts new file mode 100644 index 0000000000..6a8e700f1e --- /dev/null +++ b/extensions/git/src/uri.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Uri } from 'vscode'; + +export function fromGitUri(uri: Uri): { path: string; ref: string; } { + return JSON.parse(uri.query); +} + +// As a mitigation for extensions like ESLint showing warnings and errors +// for git URIs, let's change the file extension of these uris to .git, +// when `replaceFileExtension` is true. +export function toGitUri(uri: Uri, ref: string, replaceFileExtension = false): Uri { + return uri.with({ + scheme: 'git', + path: replaceFileExtension ? `${uri.path}.git` : uri.path, + query: JSON.stringify({ + path: uri.fsPath, + ref + }) + }); +} \ No newline at end of file diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts new file mode 100644 index 0000000000..17187a9a60 --- /dev/null +++ b/extensions/git/src/util.ts @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Event } from 'vscode'; +import { dirname } from 'path'; +import * as fs from 'fs'; + +export function log(...args: any[]): void { + console.log.apply(console, ['git:', ...args]); +} + +export interface IDisposable { + dispose(): void; +} + +export function dispose(disposables: T[]): T[] { + disposables.forEach(d => d.dispose()); + return []; +} + +export function toDisposable(dispose: () => void): IDisposable { + return { dispose }; +} + +export function combinedDisposable(disposables: IDisposable[]): IDisposable { + return toDisposable(() => dispose(disposables)); +} + +export const EmptyDisposable = toDisposable(() => null); + +export function mapEvent(event: Event, map: (i: I) => O): Event { + return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables); +} + +export function filterEvent(event: Event, filter: (e: T) => boolean): Event { + return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); +} + +export function anyEvent(...events: Event[]): Event { + return (listener, thisArgs = null, disposables?) => { + const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i)))); + + if (disposables) { + disposables.push(result); + } + + return result; + }; +} + +export function done(promise: Promise): Promise { + return promise.then(() => void 0); +} + +export function onceEvent(event: Event): Event { + return (listener, thisArgs = null, disposables?) => { + const result = event(e => { + result.dispose(); + return listener.call(thisArgs, e); + }, null, disposables); + + return result; + }; +} + +export function eventToPromise(event: Event): Promise { + return new Promise(c => onceEvent(event)(c)); +} + +export function once(fn: (...args: any[]) => any): (...args: any[]) => any { + let didRun = false; + + return (...args) => { + if (didRun) { + return; + } + + return fn(...args); + }; +} + +export function assign(destination: T, ...sources: any[]): T { + for (const source of sources) { + Object.keys(source).forEach(key => destination[key] = source[key]); + } + + return destination; +} + +export function uniqBy(arr: T[], fn: (el: T) => string): T[] { + const seen = Object.create(null); + + return arr.filter(el => { + const key = fn(el); + + if (seen[key]) { + return false; + } + + seen[key] = true; + return true; + }); +} + +export function groupBy(arr: T[], fn: (el: T) => string): { [key: string]: T[] } { + return arr.reduce((result, el) => { + const key = fn(el); + result[key] = [...(result[key] || []), el]; + return result; + }, Object.create(null)); +} + +export function denodeify(fn: Function): (...args) => Promise { + return (...args) => new Promise((c, e) => fn(...args, (err, r) => err ? e(err) : c(r))); +} + +export function nfcall(fn: Function, ...args): Promise { + return new Promise((c, e) => fn(...args, (err, r) => err ? e(err) : c(r))); +} + +export async function mkdirp(path: string, mode?: number): Promise { + const mkdir = async () => { + try { + await nfcall(fs.mkdir, path, mode); + } catch (err) { + if (err.code === 'EEXIST') { + const stat = await nfcall(fs.stat, path); + + if (stat.isDirectory) { + return; + } + + throw new Error(`'${path}' exists and is not a directory.`); + } + + throw err; + } + }; + + // is root? + if (path === dirname(path)) { + return true; + } + + try { + await mkdir(); + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + + await mkdirp(dirname(path), mode); + await mkdir(); + } + + return true; +} + +export function uniqueFilter(keyFn: (t: T) => string): (t: T) => boolean { + const seen: { [key: string]: boolean; } = Object.create(null); + + return element => { + const key = keyFn(element); + + if (seen[key]) { + return false; + } + + seen[key] = true; + return true; + }; +} + +export function find(array: T[], fn: (t: T) => boolean): T | undefined { + let result: T | undefined = undefined; + + array.some(e => { + if (fn(e)) { + result = e; + return true; + } + + return false; + }); + + return result; +} \ No newline at end of file diff --git a/extensions/git/test/mocha.opts b/extensions/git/test/mocha.opts new file mode 100644 index 0000000000..93c2e8fffb --- /dev/null +++ b/extensions/git/test/mocha.opts @@ -0,0 +1 @@ +--ui tdd out/test \ No newline at end of file diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json new file mode 100644 index 0000000000..254c9e6745 --- /dev/null +++ b/extensions/git/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es6", + "lib": [ + "es2016" + ], + "module": "commonjs", + "outDir": "./out", + "strictNullChecks": true, + "experimentalDecorators": true + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/gitsyntax/.vscodeignore b/extensions/gitsyntax/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/gitsyntax/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/gitsyntax/OSSREADME.json b/extensions/gitsyntax/OSSREADME.json new file mode 100644 index 0000000000..0e3ddd52fa --- /dev/null +++ b/extensions/gitsyntax/OSSREADME.json @@ -0,0 +1,29 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[{ + "name": "textmate/git.tmbundle", + "version": "0.0.0", + "license": "MIT", + "repositoryURL": "https://github.com/textmate/git.tmbundle", + "licenseDetail": [ + "Copyright (c) 2008 Tim Harper", + "", + "Permission is hereby granted, free of charge, to any person obtaining", + "a copy of this software and associated documentation files (the\"", + "Software\"), to deal in the Software without restriction, including", + "without limitation the rights to use, copy, modify, merge, publish,", + "distribute, sublicense, and/or sell copies of the Software, and to", + "permit persons to whom the Software is furnished to do so, subject to", + "the following conditions:", + "", + "The above copyright notice and this permission notice shall be", + "included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", + "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", + "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", + "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE", + "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", + "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION", + "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] +}] \ No newline at end of file diff --git a/extensions/gitsyntax/git-commit.language-configuration.json b/extensions/gitsyntax/git-commit.language-configuration.json new file mode 100644 index 0000000000..b61fbe63d3 --- /dev/null +++ b/extensions/gitsyntax/git-commit.language-configuration.json @@ -0,0 +1,11 @@ +{ + "comments": { + "lineComment": "#", + "blockComment": [ "#", " " ] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ] +} \ No newline at end of file diff --git a/extensions/gitsyntax/git-rebase.language-configuration.json b/extensions/gitsyntax/git-rebase.language-configuration.json new file mode 100644 index 0000000000..b61fbe63d3 --- /dev/null +++ b/extensions/gitsyntax/git-rebase.language-configuration.json @@ -0,0 +1,11 @@ +{ + "comments": { + "lineComment": "#", + "blockComment": [ "#", " " ] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ] +} \ No newline at end of file diff --git a/extensions/gitsyntax/package.json b/extensions/gitsyntax/package.json new file mode 100644 index 0000000000..bc1a1810f9 --- /dev/null +++ b/extensions/gitsyntax/package.json @@ -0,0 +1,55 @@ +{ + "name": "gitsyntax", + "publisher": "vscode", + "displayName": "gitsyntax", + "description": "Git Syntax", + "version": "0.0.1", + "engines": { + "vscode": "^1.5.0" + }, + "categories": [ + "Other" + ], + "scripts": { + "update-grammar": "node ../../build/npm/update-grammar.js textmate/git.tmbundle Syntaxes/Git%20Commit%20Message.tmLanguage ./syntaxes/git-commit.tmLanguage.json Syntaxes/Git%20Rebase%20Message.tmLanguage ./syntaxes/git-rebase.tmLanguage.json" + }, + "contributes": { + "languages": [ + { + "id": "git-commit", + "aliases": [ + "Git Commit Message", + "git-commit" + ], + "filenames": [ + "COMMIT_EDITMSG", + "MERGE_MSG" + ], + "configuration": "./git-commit.language-configuration.json" + }, + { + "id": "git-rebase", + "aliases": [ + "Git Rebase Message", + "git-rebase" + ], + "filenames": [ + "git-rebase-todo" + ], + "configuration": "./git-rebase.language-configuration.json" + } + ], + "grammars": [ + { + "language": "git-commit", + "scopeName": "text.git-commit", + "path": "./syntaxes/git-commit.tmLanguage.json" + }, + { + "language": "git-rebase", + "scopeName": "text.git-rebase", + "path": "./syntaxes/git-rebase.tmLanguage.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/gitsyntax/syntaxes/git-commit.tmLanguage.json b/extensions/gitsyntax/syntaxes/git-commit.tmLanguage.json new file mode 100644 index 0000000000..5b2ccacd56 --- /dev/null +++ b/extensions/gitsyntax/syntaxes/git-commit.tmLanguage.json @@ -0,0 +1,148 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/textmate/git.tmbundle/blob/master/Syntaxes/Git%20Commit%20Message.tmLanguage", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/textmate/git.tmbundle/commit/93897a78c6e52bef13dadc0d4091d203c5facb40", + "fileTypes": [ + "COMMIT_EDITMSG", + "MERGE_MSG" + ], + "foldingStartMarker": "^\\+\\+\\+", + "foldingStopMarker": "^---", + "name": "Git Commit Message", + "patterns": [ + { + "begin": "\\A(?!# Please enter the commit message)", + "end": "^(?=# Please enter the commit message)", + "name": "meta.scope.message.git-commit", + "patterns": [ + { + "begin": "\\A(?=#)", + "end": "^(?!#)", + "patterns": [ + { + "include": "#comment" + } + ] + }, + { + "begin": "^(?!# Please enter the commit message)", + "end": "^(?=# Please enter the commit message)", + "patterns": [ + { + "begin": "\\G", + "end": "^(?!\\G)", + "name": "meta.scope.subject.git-commit", + "patterns": [ + { + "captures": { + "1": { + "name": "keyword.other.$2.git-commit" + } + }, + "match": "\\G((fixup|squash)!)\\s*" + }, + { + "match": ".{73,}$", + "name": "invalid.illegal.line-too-long.git-commit" + }, + { + "match": ".{51,}$", + "name": "invalid.deprecated.line-too-long.git-commit" + } + ] + }, + { + "begin": "^(?!# Please enter the commit message)", + "end": "^(?=# Please enter the commit message)", + "patterns": [ + { + "include": "#comment" + } + ] + } + ] + } + ] + }, + { + "begin": "^(?=# Please enter the commit message)", + "end": "\\z", + "name": "meta.scope.metadata.git-commit", + "patterns": [ + { + "include": "#metadata" + } + ] + } + ], + "repository": { + "comment": { + "begin": "^(#)", + "captures": { + "1": { + "name": "punctuation.definition.comment.git-commit" + } + }, + "end": "\\n", + "name": "comment.line.number-sign.git-commit" + }, + "metadata": { + "patterns": [ + { + "begin": "(?=^# Changes to be committed:)", + "end": "(?!\\G)((?=^# \\w)|(?!^#))", + "patterns": [ + { + "begin": "(^[ \\t]+)?(?=#)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.git-commit" + } + }, + "contentName": "comment.line.number-sign.git-commit", + "end": "(?!\\G)^", + "patterns": [ + { + "match": "\\G#", + "name": "punctuation.definition.comment.git-commit" + }, + { + "match": "((modified|renamed):.*)$\\n?", + "name": "markup.changed.git-commit" + }, + { + "match": "(new file:.*)$\\n?", + "name": "markup.inserted.git-commit" + }, + { + "match": "(deleted:.*)$\\n?", + "name": "markup.deleted.git-commit" + } + ] + } + ] + }, + { + "include": "#comment" + }, + { + "begin": "(?=diff\\ \\-\\-git)", + "comment": "diff presented at the end of the commit message when using commit -v.", + "contentName": "source.diff", + "end": "\\z", + "name": "meta.embedded.diff.git-commit", + "patterns": [ + { + "include": "source.diff" + } + ] + } + ] + } + }, + "scopeName": "text.git-commit", + "uuid": "BFE83C06-8508-44BE-A975-95A57BF619A7" +} \ No newline at end of file diff --git a/extensions/gitsyntax/syntaxes/git-rebase.tmLanguage.json b/extensions/gitsyntax/syntaxes/git-rebase.tmLanguage.json new file mode 100644 index 0000000000..89ef957a1b --- /dev/null +++ b/extensions/gitsyntax/syntaxes/git-rebase.tmLanguage.json @@ -0,0 +1,40 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/textmate/git.tmbundle/blob/master/Syntaxes/Git%20Rebase%20Message.tmLanguage", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/textmate/git.tmbundle/commit/d1db42c2d71948662098183a6df519fb53a7a15b", + "fileTypes": [ + "git-rebase-todo" + ], + "name": "Git Rebase Message", + "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.definition.comment.git-rebase" + } + }, + "match": "^\\s*(#).*$\\n?", + "name": "comment.line.number-sign.git-rebase" + }, + { + "captures": { + "1": { + "name": "support.function.git-rebase" + }, + "2": { + "name": "constant.sha.git-rebase" + }, + "3": { + "name": "meta.commit-message.git-rebase" + } + }, + "match": "^\\s*(pick|p|reword|r|edit|e|squash|s|fixup|f|exec|x|drop|d)\\s+([0-9a-f]+)\\s+(.*)$", + "name": "meta.commit-command.git-rebase" + } + ], + "scopeName": "text.git-rebase", + "uuid": "7F1CC209-5F6D-486A-8180-09FA282381A1" +} \ No newline at end of file diff --git a/extensions/gitsyntax/test/colorize-fixtures/COMMIT_EDITMSG b/extensions/gitsyntax/test/colorize-fixtures/COMMIT_EDITMSG new file mode 100644 index 0000000000..e81d55d0ed --- /dev/null +++ b/extensions/gitsyntax/test/colorize-fixtures/COMMIT_EDITMSG @@ -0,0 +1,13 @@ +This is the summary line. It can't be too long. +After I can write a much more detailed description without quite the same restrictions on length. + +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# On branch master +# Your branch is up-to-date with 'origin/master'. +# +# Changes to be committed: +# deleted: README.md +# modified: index.less +# new file: spec/COMMIT_EDITMSG +# \ No newline at end of file diff --git a/extensions/gitsyntax/test/colorize-fixtures/git-rebase-todo b/extensions/gitsyntax/test/colorize-fixtures/git-rebase-todo new file mode 100644 index 0000000000..3b6df1cd4f --- /dev/null +++ b/extensions/gitsyntax/test/colorize-fixtures/git-rebase-todo @@ -0,0 +1,15 @@ +pick 1fc6c95 Patch A +squash fa39187 Something to add to patch A +pick 7b36971 Something to move before patch B +pick 6b2481b Patch B +fixup c619268 A fix for Patch B +edit dd1475d Something I want to split +reword 4ca2acc i cant' typ goods + +# Commands: +# p, pick = use commit +# r, reword = use commit, but edit the commit message +# e, edit = use commit, but stop for amending +# s, squash = use commit, but meld into previous commit +# f, fixup = like "squash", but discard this commit's log message +# x, exec = run command (the rest of the line) using shell \ No newline at end of file diff --git a/extensions/gitsyntax/test/colorize-results/COMMIT_EDITMSG.json b/extensions/gitsyntax/test/colorize-results/COMMIT_EDITMSG.json new file mode 100644 index 0000000000..b5457404c8 --- /dev/null +++ b/extensions/gitsyntax/test/colorize-results/COMMIT_EDITMSG.json @@ -0,0 +1,255 @@ +[ + { + "c": "This is the summary line. It can't be too long.", + "t": "text.git-commit meta.scope.message.git-commit meta.scope.subject.git-commit", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "After I can write a much more detailed description without quite the same restrictions on length.", + "t": "text.git-commit meta.scope.message.git-commit", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Please enter the commit message for your changes. Lines starting", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " with '#' will be ignored, and an empty message aborts the commit.", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " On branch master", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Your branch is up-to-date with 'origin/master'.", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Changes to be committed:", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\t", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "deleted: README.md", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit markup.deleted.git-commit", + "r": { + "dark_plus": "markup.deleted: #CE9178", + "light_plus": "markup.deleted: #A31515", + "dark_vs": "markup.deleted: #CE9178", + "light_vs": "markup.deleted: #A31515", + "hc_black": "markup.deleted: #CE9178" + } + }, + { + "c": "#", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\t", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "modified: index.less", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit markup.changed.git-commit", + "r": { + "dark_plus": "markup.changed: #569CD6", + "light_plus": "markup.changed: #0451A5", + "dark_vs": "markup.changed: #569CD6", + "light_vs": "markup.changed: #0451A5", + "hc_black": "markup.changed: #569CD6" + } + }, + { + "c": "#", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\t", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "new file: spec/COMMIT_EDITMSG", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit markup.inserted.git-commit", + "r": { + "dark_plus": "markup.inserted: #B5CEA8", + "light_plus": "markup.inserted: #09885A", + "dark_vs": "markup.inserted: #B5CEA8", + "light_vs": "markup.inserted: #09885A", + "hc_black": "markup.inserted: #B5CEA8" + } + }, + { + "c": "#", + "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + } +] \ No newline at end of file diff --git a/extensions/gitsyntax/test/colorize-results/git-rebase-todo.json b/extensions/gitsyntax/test/colorize-results/git-rebase-todo.json new file mode 100644 index 0000000000..87781fdbe0 --- /dev/null +++ b/extensions/gitsyntax/test/colorize-results/git-rebase-todo.json @@ -0,0 +1,541 @@ +[ + { + "c": "pick", + "t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase", + "r": { + "dark_plus": "support.function.git-rebase: #9CDCFE", + "light_plus": "support.function.git-rebase: #0451A5", + "dark_vs": "support.function.git-rebase: #9CDCFE", + "light_vs": "support.function.git-rebase: #0451A5", + "hc_black": "support.function.git-rebase: #D4D4D4" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1fc6c95", + "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", + "r": { + "dark_plus": "constant.sha.git-rebase: #B5CEA8", + "light_plus": "constant.sha.git-rebase: #09885A", + "dark_vs": "constant.sha.git-rebase: #B5CEA8", + "light_vs": "constant.sha.git-rebase: #09885A", + "hc_black": "constant.sha.git-rebase: #B5CEA8" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Patch A", + "t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "squash", + "t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase", + "r": { + "dark_plus": "support.function.git-rebase: #9CDCFE", + "light_plus": "support.function.git-rebase: #0451A5", + "dark_vs": "support.function.git-rebase: #9CDCFE", + "light_vs": "support.function.git-rebase: #0451A5", + "hc_black": "support.function.git-rebase: #D4D4D4" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fa39187", + "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", + "r": { + "dark_plus": "constant.sha.git-rebase: #B5CEA8", + "light_plus": "constant.sha.git-rebase: #09885A", + "dark_vs": "constant.sha.git-rebase: #B5CEA8", + "light_vs": "constant.sha.git-rebase: #09885A", + "hc_black": "constant.sha.git-rebase: #B5CEA8" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Something to add to patch A", + "t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pick", + "t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase", + "r": { + "dark_plus": "support.function.git-rebase: #9CDCFE", + "light_plus": "support.function.git-rebase: #0451A5", + "dark_vs": "support.function.git-rebase: #9CDCFE", + "light_vs": "support.function.git-rebase: #0451A5", + "hc_black": "support.function.git-rebase: #D4D4D4" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "7b36971", + "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", + "r": { + "dark_plus": "constant.sha.git-rebase: #B5CEA8", + "light_plus": "constant.sha.git-rebase: #09885A", + "dark_vs": "constant.sha.git-rebase: #B5CEA8", + "light_vs": "constant.sha.git-rebase: #09885A", + "hc_black": "constant.sha.git-rebase: #B5CEA8" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Something to move before patch B", + "t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pick", + "t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase", + "r": { + "dark_plus": "support.function.git-rebase: #9CDCFE", + "light_plus": "support.function.git-rebase: #0451A5", + "dark_vs": "support.function.git-rebase: #9CDCFE", + "light_vs": "support.function.git-rebase: #0451A5", + "hc_black": "support.function.git-rebase: #D4D4D4" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "6b2481b", + "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", + "r": { + "dark_plus": "constant.sha.git-rebase: #B5CEA8", + "light_plus": "constant.sha.git-rebase: #09885A", + "dark_vs": "constant.sha.git-rebase: #B5CEA8", + "light_vs": "constant.sha.git-rebase: #09885A", + "hc_black": "constant.sha.git-rebase: #B5CEA8" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Patch B", + "t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fixup", + "t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase", + "r": { + "dark_plus": "support.function.git-rebase: #9CDCFE", + "light_plus": "support.function.git-rebase: #0451A5", + "dark_vs": "support.function.git-rebase: #9CDCFE", + "light_vs": "support.function.git-rebase: #0451A5", + "hc_black": "support.function.git-rebase: #D4D4D4" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "c619268", + "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", + "r": { + "dark_plus": "constant.sha.git-rebase: #B5CEA8", + "light_plus": "constant.sha.git-rebase: #09885A", + "dark_vs": "constant.sha.git-rebase: #B5CEA8", + "light_vs": "constant.sha.git-rebase: #09885A", + "hc_black": "constant.sha.git-rebase: #B5CEA8" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A fix for Patch B", + "t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "edit", + "t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase", + "r": { + "dark_plus": "support.function.git-rebase: #9CDCFE", + "light_plus": "support.function.git-rebase: #0451A5", + "dark_vs": "support.function.git-rebase: #9CDCFE", + "light_vs": "support.function.git-rebase: #0451A5", + "hc_black": "support.function.git-rebase: #D4D4D4" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "dd1475d", + "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", + "r": { + "dark_plus": "constant.sha.git-rebase: #B5CEA8", + "light_plus": "constant.sha.git-rebase: #09885A", + "dark_vs": "constant.sha.git-rebase: #B5CEA8", + "light_vs": "constant.sha.git-rebase: #09885A", + "hc_black": "constant.sha.git-rebase: #B5CEA8" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Something I want to split", + "t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "reword", + "t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase", + "r": { + "dark_plus": "support.function.git-rebase: #9CDCFE", + "light_plus": "support.function.git-rebase: #0451A5", + "dark_vs": "support.function.git-rebase: #9CDCFE", + "light_vs": "support.function.git-rebase: #0451A5", + "hc_black": "support.function.git-rebase: #D4D4D4" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "4ca2acc", + "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", + "r": { + "dark_plus": "constant.sha.git-rebase: #B5CEA8", + "light_plus": "constant.sha.git-rebase: #09885A", + "dark_vs": "constant.sha.git-rebase: #B5CEA8", + "light_vs": "constant.sha.git-rebase: #09885A", + "hc_black": "constant.sha.git-rebase: #B5CEA8" + } + }, + { + "c": " ", + "t": "text.git-rebase meta.commit-command.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "i cant' typ goods", + "t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Commands:", + "t": "text.git-rebase comment.line.number-sign.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " p, pick = use commit", + "t": "text.git-rebase comment.line.number-sign.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " r, reword = use commit, but edit the commit message", + "t": "text.git-rebase comment.line.number-sign.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " e, edit = use commit, but stop for amending", + "t": "text.git-rebase comment.line.number-sign.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " s, squash = use commit, but meld into previous commit", + "t": "text.git-rebase comment.line.number-sign.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " f, fixup = like \"squash\", but discard this commit's log message", + "t": "text.git-rebase comment.line.number-sign.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " x, exec = run command (the rest of the line) using shell", + "t": "text.git-rebase comment.line.number-sign.git-rebase", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + } +] \ No newline at end of file diff --git a/extensions/insights-default/package.json b/extensions/insights-default/package.json new file mode 100644 index 0000000000..b6b11db8e3 --- /dev/null +++ b/extensions/insights-default/package.json @@ -0,0 +1,137 @@ +{ + "name": "insights-default", + "version": "0.1.0", + "publisher": "Microsoft", + "engines": { + "vscode": "*" + }, + "contributes": { + "insights": [ + { + "id": "query-data-store-db-insight", + "contrib": { + "name": "Top 5 Slowest Queries", + "provider": "MSSQL", + "gridItemConfig": { + "x": 2, + "y": 1 + }, + "type": { + "timeSeries": { + "dataDirection": "horizontal", + "dataType": "point", + "legendPosition": "top", + "labelFirstColumn": false, + "columnsAsLabels": false + } + }, + "queryFile": "./sql/qds.sql", + "details": { + "queryFile": "./sql/qds_detail.sql", + "label": { + "icon": "file", + "column": "query_id", + "state": [] + }, + "value": "max_duration" + } + } + }, + { + "id": "table-space-db-insight", + "contrib": { + "name": "Space used per table", + "provider": "MSSQL", + "gridItemConfig": { + "x": 2, + "y": 1 + }, + "type": { + "horizontalBar": { + "dataDirection": "vertical", + "dataType": "number", + "legendPosition": "top", + "labelFirstColumn": false, + "columnsAsLabels": true + } + }, + "queryFile": "./sql/tablespace.sql" + } + }, + { + "id": "all-database-size-server-insight", + "contrib": { + "name": "Database Size (MB)", + "provider": "MSSQL", + "edition": [0,1,2,3,4], + "gridItemConfig": { + "x": 2, + "y": 2 + }, + "type": { + "horizontalBar": { + "dataDirection": "vertical", + "dataType": "number", + "legendPosition": "none", + "columnsAsLabels": true + } + }, + "queryFile": "./sql/db_size.sql" + } + }, + { + "id": "backup-history-server-insight", + "contrib": { + "name": "Backup Status", + "provider": "MSSQL", + "edition": [0,1,2,3,4], + "gridItemConfig": { + "x": 1, + "y": 1 + }, + "type": { + "count": null + }, + "queryFile": "./sql/backup_insight.sql", + "details": { + "queryFile": "./sql/backup_detail.sql", + "label": { + "icon": "database", + "column": "Database", + "state": [ + { + "condition": { + "if": "equals", + "equals": "No backup found" + }, + "color": "red" + }, + { + "condition": { + "if": "equals", + "equals": "Older than 24hrs" + }, + "color": "orange" + }, + { + "condition": { + "if": "equals", + "equals": "Within 24hrs" + }, + "color": "green" + } + ] + }, + "value": "Backup_Health", + "actions": { + "types": [ + "backup" + ], + "database": "${Database}" + } + } + } + } + ] + } +} \ No newline at end of file diff --git a/extensions/insights-default/sql/backup_detail.sql b/extensions/insights-default/sql/backup_detail.sql new file mode 100644 index 0000000000..b17328fa43 --- /dev/null +++ b/extensions/insights-default/sql/backup_detail.sql @@ -0,0 +1,34 @@ +declare @condition tinyint; +SET @condition = 24; + +select +d.database_id as [Database ID], +d.name as [Database], +d.recovery_model_desc as [Recovery model], +d.state_desc as [Database state], +case + when b.type = N'D' then N'Database' + when b.type = N'I' then N'Differential Database' + when b.type = N'L' then N'Log' + when b.type = N'F' then N'File or Filegroup' + when b.type = N'G' then N'Differental File' + when b.type = N'P' then N'Partial' + when b.type = N'Q' then N'Differential Partial' + else NULL + end +as [Backup type], +b.backup_start_date as [Backup start date], +b.backup_finish_date as [Backup finish date], +case + when m.last_backup_time is null then N'No backup found' + when datediff(hh, m.last_backup_time, getdate()) > @condition then N'Older than 24hrs' + else N'Within 24hrs' + end as [Backup_Health] +from sys.databases as d +left join msdb..backupset as b on d.name = b.database_name +left join (select bs.database_name, max(bs.backup_start_date) as last_backup_time + from msdb..backupset as bs + group by bs.database_name ) as m on d.name = m.database_name and b.backup_start_date = m.last_backup_time +where (b.backup_start_date is null or b.backup_start_date = m.last_backup_time) + and d.database_id > 4 +order by d.database_id asc diff --git a/extensions/insights-default/sql/backup_insight.sql b/extensions/insights-default/sql/backup_insight.sql new file mode 100644 index 0000000000..9ad4f489ba --- /dev/null +++ b/extensions/insights-default/sql/backup_insight.sql @@ -0,0 +1,16 @@ +declare @condition tinyint; +SET @condition = 24; +with + backupInsight_cte (database_id, last_backup, health_check) + as + ( + select d.database_id, max(b.backup_start_date) AS last_backup, case when (datediff( hh , max(b.backup_start_date) , getdate()) < @condition) then 1 else 0 end as health_check + from sys.databases as d left join msdb..backupset as b on d.name = b.database_name + where d.database_id > 4 + group by d.database_id + ) +select + coalesce(sum(health_check),0) [Within 24hrs], + coalesce(sum(case when health_check = 0 AND last_backup IS NOT NULL then 1 else 0 end),0) [Older than 24hrs], + coalesce(sum(case when health_check = 0 AND last_backup IS NULL then 1 else 0 end),0) [No backup found] +from backupInsight_cte \ No newline at end of file diff --git a/extensions/insights-default/sql/db_size.sql b/extensions/insights-default/sql/db_size.sql new file mode 100644 index 0000000000..a21a2f276b --- /dev/null +++ b/extensions/insights-default/sql/db_size.sql @@ -0,0 +1,13 @@ +with fs +as +( + select database_id, type, size * 8.0 / 1024 size + from sys.master_files +) +select top 10 + name, + (select sum(size) from fs where type = 0 and fs.database_id = db.database_id) DataFileSizeMB, + (select sum(size) from fs where type = 1 and fs.database_id = db.database_id) LogFileSizeMB +from sys.databases db +where database_id > 4 +order by DataFileSizeMB \ No newline at end of file diff --git a/extensions/insights-default/sql/qds.sql b/extensions/insights-default/sql/qds.sql new file mode 100644 index 0000000000..049ec9a9bf --- /dev/null +++ b/extensions/insights-default/sql/qds.sql @@ -0,0 +1,39 @@ +declare @qds_status int = (SELECT actual_state +FROM sys.database_query_store_options) +if @qds_status > 0 +Begin +WITH SlowestQry AS( + SELECT TOP 5 + q.query_id, + MAX(rs.max_duration ) max_duration + FROM sys.query_store_query_text AS qt + JOIN sys.query_store_query AS q + ON qt.query_text_id = q.query_text_id + JOIN sys.query_store_plan AS p + ON q.query_id = p.query_id + JOIN sys.query_store_runtime_stats AS rs + ON p.plan_id = rs.plan_id + WHERE rs.last_execution_time > DATEADD(week, -1, GETUTCDATE()) + AND is_internal_query = 0 + GROUP BY q.query_id + ORDER BY MAX(rs.max_duration ) DESC) +SELECT + q.query_id, + format(rs.last_execution_time,'yyyy-MM-dd hh:mm:ss') as [last_execution_time], + rs.max_duration, + p.plan_id +FROM sys.query_store_query_text AS qt + JOIN sys.query_store_query AS q + ON qt.query_text_id = q.query_text_id + JOIN sys.query_store_plan AS p + ON q.query_id = p.query_id + JOIN sys.query_store_runtime_stats AS rs + ON p.plan_id = rs.plan_id + JOIN SlowestQry tq + ON tq.query_id = q.query_id +WHERE rs.last_execution_time > DATEADD(week, -1, GETUTCDATE()) +AND is_internal_query = 0 +order by format(rs.last_execution_time,'yyyy-MM-dd hh:mm:ss') +END +else +select 0 as [query_id], getdate() as [QDS is not enabled], 0 as [max_duration] \ No newline at end of file diff --git a/extensions/insights-default/sql/qds_detail.sql b/extensions/insights-default/sql/qds_detail.sql new file mode 100644 index 0000000000..0dbdf46375 --- /dev/null +++ b/extensions/insights-default/sql/qds_detail.sql @@ -0,0 +1,41 @@ +declare @qds_status int = (SELECT actual_state +FROM sys.database_query_store_options) +if @qds_status > 0 +Begin +WITH SlowestQry AS( + SELECT TOP 5 + q.query_id, + MAX(rs.max_duration ) max_duration + FROM sys.query_store_query_text AS qt + JOIN sys.query_store_query AS q + ON qt.query_text_id = q.query_text_id + JOIN sys.query_store_plan AS p + ON q.query_id = p.query_id + JOIN sys.query_store_runtime_stats AS rs + ON p.plan_id = rs.plan_id + WHERE rs.last_execution_time > DATEADD(week, -1, GETUTCDATE()) + AND is_internal_query = 0 + GROUP BY q.query_id + ORDER BY MAX(rs.max_duration ) DESC) +SELECT + q.query_id, + format(rs.last_execution_time,'yyyy-MM-dd hh:mm:ss') as [last_execution_time], + rs.max_duration, + p.plan_id , + qt.query_sql_text, + p.query_plan +FROM SlowestQry tq + join sys.query_store_query as q + on tq.query_id = q.query_id + JOIN sys.query_store_query_text AS qt + ON qt.query_text_id = q.query_text_id + JOIN sys.query_store_plan AS p + ON q.query_id = p.query_id + JOIN sys.query_store_runtime_stats AS rs + ON p.plan_id = rs.plan_id +WHERE rs.last_execution_time > DATEADD(week, -1, GETUTCDATE()) +AND is_internal_query = 0 +order by q.query_id, rs.max_duration desc +END +else +select 0 as [query_id], getdate() as [QDS is not enabled], 0 as [max_duration] \ No newline at end of file diff --git a/extensions/insights-default/sql/tablespace.sql b/extensions/insights-default/sql/tablespace.sql new file mode 100644 index 0000000000..8b8c78c30c --- /dev/null +++ b/extensions/insights-default/sql/tablespace.sql @@ -0,0 +1,25 @@ +SELECT Top 5 TABL.name AS table_name, +SUM(PART.rows) AS rows_count, +SUM(ALOC.total_pages) AS total_pages, +SUM(ALOC.used_pages) AS used_pages, +SUM(ALOC.data_pages) AS data_pages, +(SUM(ALOC.total_pages)*8/1024) AS total_space_MB, +(SUM(ALOC.used_pages)*8/1024) AS used_space_MB, +(SUM(ALOC.data_pages)*8/1024) AS data_space_MB +FROM sys.Tables AS TABL +INNER JOIN sys.Indexes AS INDX +ON TABL.object_id = INDX.object_id +INNER JOIN sys.Partitions AS PART +ON INDX.object_id = PART.object_id +AND INDX.index_id = PART.index_id +INNER JOIN sys.Allocation_Units AS ALOC +ON PART.partition_id = ALOC.container_id +WHERE +INDX.object_id > 255 +AND INDX.index_id <= 1 +GROUP BY TABL.name, +INDX.object_id, +INDX.index_id, +INDX.name +ORDER BY +(SUM(ALOC.total_pages)*8/1024) DESC \ No newline at end of file diff --git a/extensions/json/.vscode/launch.json b/extensions/json/.vscode/launch.json new file mode 100644 index 0000000000..ac84c7bdf2 --- /dev/null +++ b/extensions/json/.vscode/launch.json @@ -0,0 +1,38 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}" + ], + "stopOnEntry": false, + "sourceMaps": true, + "outFiles": ["${workspaceRoot}/client/out"], + "preLaunchTask": "npm" + }, + { + "name": "Launch Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/client/out/test" ], + "stopOnEntry": false, + "sourceMaps": true, + "outFiles": ["${workspaceRoot}/client/out/test"], + "preLaunchTask": "npm" + }, + { + "name": "Attach Language Server", + "type": "node", + "request": "attach", + "port": 6004, + "sourceMaps": true, + "outFiles": ["${workspaceRoot}/server/out"] + } + + ] +} \ No newline at end of file diff --git a/extensions/json/.vscode/tasks.json b/extensions/json/.vscode/tasks.json new file mode 100644 index 0000000000..a132a04214 --- /dev/null +++ b/extensions/json/.vscode/tasks.json @@ -0,0 +1,30 @@ +// Available variables which can be used inside of strings. +// ${workspaceRoot}: the root folder of the team +// ${file}: the current opened file +// ${fileBasename}: the current opened file's basename +// ${fileDirname}: the current opened file's dirname +// ${fileExtname}: the current opened file's extension +// ${cwd}: the current working directory of the spawned process + +// A task runner that calls a custom npm script that compiles the extension. +{ + "version": "0.1.0", + + // we want to run npm + "command": "npm", + + // the command is a shell script + "isShellCommand": true, + + // show the output window only if unrecognized errors occur. + "showOutput": "silent", + + // we run the custom script "compile" as defined in package.json + "args": ["run", "compile"], + + // The tsc compiler is started in watching mode + "isWatching": true, + + // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "problemMatcher": "$tsc-watch" +} \ No newline at end of file diff --git a/extensions/json/.vscodeignore b/extensions/json/.vscodeignore new file mode 100644 index 0000000000..7785bab61c --- /dev/null +++ b/extensions/json/.vscodeignore @@ -0,0 +1,7 @@ +test/** +client/tsconfig.json +client/src/** +server/tsconfig.json +server/src/** +server/node_modules/@types/** +npm-shrinkwrap.json \ No newline at end of file diff --git a/extensions/json/OSSREADME.json b/extensions/json/OSSREADME.json new file mode 100644 index 0000000000..57bcccdcca --- /dev/null +++ b/extensions/json/OSSREADME.json @@ -0,0 +1,7 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[{ + "name": "Benvie/JavaScriptNext.tmLanguage", + "version": "0.0.0", + "license": "MIT", + "repositoryURL": "https://github.com/Microsoft/vscode-JSON.tmLanguage" +}] \ No newline at end of file diff --git a/extensions/json/client/src/jsonMain.ts b/extensions/json/client/src/jsonMain.ts new file mode 100644 index 0000000000..69a150499f --- /dev/null +++ b/extensions/json/client/src/jsonMain.ts @@ -0,0 +1,243 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as path from 'path'; + +import { workspace, languages, ExtensionContext, extensions, Uri, TextDocument, ColorRange, Color } from 'vscode'; +import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient'; +import TelemetryReporter from 'vscode-extension-telemetry'; +import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed'; + +import { DocumentColorRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; + + +import * as nls from 'vscode-nls'; +let localize = nls.loadMessageBundle(); + +namespace VSCodeContentRequest { + export const type: RequestType = new RequestType('vscode/content'); +} + +export interface ISchemaAssociations { + [pattern: string]: string[]; +} + +namespace SchemaAssociationNotification { + export const type: NotificationType = new NotificationType('json/schemaAssociations'); +} + +interface IPackageInfo { + name: string; + version: string; + aiKey: string; +} + +interface Settings { + json?: { + schemas?: JSONSchemaSettings[]; + format?: { enable: boolean; }; + }; + http?: { + proxy: string; + proxyStrictSSL: boolean; + }; +} + +interface JSONSettings { + schemas: JSONSchemaSettings[]; +} + +interface JSONSchemaSettings { + fileMatch?: string[]; + url?: string; + schema?: any; +} + +const ColorFormat_HEX = { + opaque: '"#{red:X}{green:X}{blue:X}"', + transparent: '"#{red:X}{green:X}{blue:X}{alpha:X}"' +}; + +export function activate(context: ExtensionContext) { + + let packageInfo = getPackageInfo(context); + let telemetryReporter: TelemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); + context.subscriptions.push(telemetryReporter); + + // The server is implemented in node + let serverModule = context.asAbsolutePath(path.join('server', 'out', 'jsonServerMain.js')); + // The debug options for the server + let debugOptions = { execArgv: ['--nolazy', '--inspect=6004'] }; + + // If the extension is launch in debug mode the debug server options are use + // Otherwise the run options are used + let serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } + }; + + let documentSelector = ['json']; + + // Options to control the language client + let clientOptions: LanguageClientOptions = { + // Register the server for json documents + documentSelector, + synchronize: { + // Synchronize the setting section 'json' to the server + configurationSection: ['json', 'http'], + fileEvents: workspace.createFileSystemWatcher('**/*.json') + }, + middleware: { + workspace: { + didChangeConfiguration: () => client.sendNotification(DidChangeConfigurationNotification.type, { settings: getSettings() }) + } + } + }; + + // Create the language client and start the client. + let client = new LanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), serverOptions, clientOptions); + client.registerFeature(new ConfigurationFeature(client)); + + let disposable = client.start(); + client.onReady().then(() => { + client.onTelemetry(e => { + if (telemetryReporter) { + telemetryReporter.sendTelemetryEvent(e.key, e.data); + } + }); + + // handle content request + client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { + let uri = Uri.parse(uriPath); + return workspace.openTextDocument(uri).then(doc => { + return doc.getText(); + }, error => { + return Promise.reject(error); + }); + }); + + client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); + + // register color provider + context.subscriptions.push(languages.registerColorProvider(documentSelector, { + provideDocumentColors(document: TextDocument): Thenable { + let params = client.code2ProtocolConverter.asDocumentSymbolParams(document); + return client.sendRequest(DocumentColorRequest.type, params).then(symbols => { + return symbols.map(symbol => { + let range = client.protocol2CodeConverter.asRange(symbol.range); + let color = new Color(symbol.color.red * 255, symbol.color.green * 255, symbol.color.blue * 255, symbol.color.alpha); + return new ColorRange(range, color, [ColorFormat_HEX]); + }); + }); + } + })); + }); + + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation + context.subscriptions.push(disposable); + + languages.setLanguageConfiguration('json', { + wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/, + indentationRules: { + increaseIndentPattern: /^.*(\{[^}]*|\[[^\]]*)$/, + decreaseIndentPattern: /^\s*[}\]],?\s*$/ + } + }); +} + +function getSchemaAssociation(context: ExtensionContext): ISchemaAssociations { + let associations: ISchemaAssociations = {}; + extensions.all.forEach(extension => { + let packageJSON = extension.packageJSON; + if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation) { + let jsonValidation = packageJSON.contributes.jsonValidation; + if (Array.isArray(jsonValidation)) { + jsonValidation.forEach(jv => { + let { fileMatch, url } = jv; + if (fileMatch && url) { + if (url[0] === '.' && url[1] === '/') { + url = Uri.file(path.join(extension.extensionPath, url)).toString(); + } + if (fileMatch[0] === '%') { + fileMatch = fileMatch.replace(/%APP_SETTINGS_HOME%/, '/User'); + fileMatch = fileMatch.replace(/%APP_WORKSPACES_HOME%/, '/Workspaces'); + } else if (fileMatch.charAt(0) !== '/' && !fileMatch.match(/\w+:\/\//)) { + fileMatch = '/' + fileMatch; + } + let association = associations[fileMatch]; + if (!association) { + association = []; + associations[fileMatch] = association; + } + association.push(url); + } + }); + } + } + }); + return associations; +} + +function getSettings(): Settings { + let httpSettings = workspace.getConfiguration('http'); + let jsonSettings = workspace.getConfiguration('json'); + + let schemas = []; + + let settings: Settings = { + http: { + proxy: httpSettings.get('proxy'), + proxyStrictSSL: httpSettings.get('proxyStrictSSL') + }, + json: { + format: jsonSettings.get('format'), + schemas: schemas, + } + }; + let settingsSchemas = jsonSettings.get('schemas'); + if (Array.isArray(settingsSchemas)) { + schemas.push(...settingsSchemas); + } + + let folders = workspace.workspaceFolders; + if (folders) { + folders.forEach(folder => { + let jsonConfig = workspace.getConfiguration('json', folder.uri); + let schemaConfigInfo = jsonConfig.inspect('schemas'); + let folderSchemas = schemaConfigInfo.workspaceFolderValue; + if (Array.isArray(folderSchemas)) { + folderSchemas.forEach(schema => { + let url = schema.url; + if (!url && schema.schema) { + url = schema.schema.id; + } + if (url && url[0] === '.') { + url = Uri.file(path.normalize(path.join(folder.uri.fsPath, url))).toString(); + } + let fileMatch = schema.fileMatch; + if (fileMatch) { + fileMatch = fileMatch.map(m => folder.uri.toString() + '*' + m); + } + schemas.push({ url, fileMatch, schema: schema.schema }); + }); + }; + }); + } + return settings; +} + +function getPackageInfo(context: ExtensionContext): IPackageInfo { + let extensionPackage = require(context.asAbsolutePath('./package.json')); + if (extensionPackage) { + return { + name: extensionPackage.name, + version: extensionPackage.version, + aiKey: extensionPackage.aiKey + }; + } + return null; +} \ No newline at end of file diff --git a/extensions/json/client/src/typings/ref.d.ts b/extensions/json/client/src/typings/ref.d.ts new file mode 100644 index 0000000000..95ef63c259 --- /dev/null +++ b/extensions/json/client/src/typings/ref.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// \ No newline at end of file diff --git a/extensions/json/client/tsconfig.json b/extensions/json/client/tsconfig.json new file mode 100644 index 0000000000..31c07df105 --- /dev/null +++ b/extensions/json/client/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "outDir": "./out", + "lib": [ + "es5", "es2015.promise" + ] + } +} \ No newline at end of file diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json new file mode 100644 index 0000000000..53ccbd2354 --- /dev/null +++ b/extensions/json/language-configuration.json @@ -0,0 +1,18 @@ +{ + "comments": { + "lineComment": "//", + "blockComment": [ "/*", "*/" ] + }, + "brackets": [ + ["{", "}"], + ["[", "]"] + ], + "autoClosingPairs": [ + { "open": "{", "close": "}", "notIn": ["string"] }, + { "open": "[", "close": "]", "notIn": ["string"] }, + { "open": "(", "close": ")", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] }, + { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, + { "open": "`", "close": "`", "notIn": ["string", "comment"] } + ] +} \ No newline at end of file diff --git a/extensions/json/npm-shrinkwrap.json b/extensions/json/npm-shrinkwrap.json new file mode 100644 index 0000000000..4caf64d28b --- /dev/null +++ b/extensions/json/npm-shrinkwrap.json @@ -0,0 +1,46 @@ +{ + "name": "json", + "version": "0.1.0", + "dependencies": { + "applicationinsights": { + "version": "0.18.0", + "from": "applicationinsights@0.18.0", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz" + }, + "vscode-extension-telemetry": { + "version": "0.0.8", + "from": "vscode-extension-telemetry@>=0.0.8 <0.0.9", + "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz" + }, + "vscode-jsonrpc": { + "version": "3.3.1", + "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" + }, + "vscode-languageclient": { + "version": "3.4.0-next.17", + "from": "vscode-languageclient@next", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.17.tgz" + }, + "vscode-languageserver-protocol": { + "version": "3.1.1", + "from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz" + }, + "vscode-languageserver-types": { + "version": "3.3.0", + "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" + }, + "vscode-nls": { + "version": "2.0.2", + "from": "vscode-nls@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + }, + "winreg": { + "version": "1.2.3", + "from": "winreg@1.2.3", + "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz" + } + } +} diff --git a/extensions/json/package.json b/extensions/json/package.json new file mode 100644 index 0000000000..ba30bb1045 --- /dev/null +++ b/extensions/json/package.json @@ -0,0 +1,143 @@ +{ + "name": "json", + "version": "0.1.0", + "publisher": "vscode", + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "engines": { + "vscode": "0.10.x" + }, + "activationEvents": [ + "onLanguage:json" + ], + "enableProposedApi": true, + "main": "./client/out/jsonMain", + "scripts": { + "compile": "gulp compile-extension:json-client && gulp compile-extension:json-server", + "postinstall": "cd server && npm install", + "install-client-next": "npm install vscode-languageclient@next -f -S", + "install-client-local": "npm install ../../../vscode-languageserver-node/client -f -S", + "update-grammar": "node ../../build/npm/update-grammar.js Microsoft/vscode-JSON.tmLanguage JSON.tmLanguage ./syntaxes/JSON.tmLanguage.json" + }, + "contributes": { + "languages": [ + { + "id": "json", + "aliases": [ + "JSON", + "json" + ], + "extensions": [ + ".json", + ".bowerrc", + ".jshintrc", + ".jscsrc", + ".eslintrc", + ".babelrc", + ".webmanifest", + ".code-workspace" + ], + "mimetypes": [ + "application/json", + "application/manifest+json" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "json", + "scopeName": "source.json", + "path": "./syntaxes/JSON.tmLanguage.json" + } + ], + "jsonValidation": [ + { + "fileMatch": "*.schema.json", + "url": "http://json-schema.org/draft-04/schema#" + } + ], + "configuration": { + "id": "json", + "order": 20, + "type": "object", + "title": "JSON", + "properties": { + "json.schemas": { + "type": "array", + "scope": "resource", + "description": "%json.schemas.desc%", + "items": { + "type": "object", + "default": { + "fileMatch": [ + "/myfile" + ], + "url": "schemaURL" + }, + "properties": { + "url": { + "type": "string", + "default": "/user.schema.json", + "description": "%json.schemas.url.desc%" + }, + "fileMatch": { + "type": "array", + "items": { + "type": "string", + "default": "MyFile.json", + "description": "%json.schemas.fileMatch.item.desc%" + }, + "minItems": 1, + "description": "%json.schemas.fileMatch.desc%" + }, + "schema": { + "$ref": "http://json-schema.org/draft-04/schema#", + "description": "%json.schemas.schema.desc%" + } + } + } + }, + "json.format.enable": { + "type": "boolean", + "scope": "window", + "default": true, + "description": "%json.format.enable.desc%" + }, + "json.trace.server": { + "type": "string", + "scope": "window", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "off", + "description": "%json.tracing.desc%" + }, + "json.colorDecorators.enable": { + "type": "boolean", + "scope": "window", + "default": true, + "description": "%json.colorDecorators.enable.desc%", + "deprecationMessage": "%json.colorDecorators.enable.deprecationMessage%" + } + } + }, + "configurationDefaults": { + "[json]": { + "editor.quickSuggestions": { + "strings": true + } + } + } + }, + "dependencies": { + "vscode-extension-telemetry": "0.0.8", + "vscode-languageclient": "3.4.0-next.17", + "vscode-languageserver-protocol": "^3.1.1", + "vscode-nls": "2.0.2" + }, + "devDependencies": { + "@types/node": "^6.0.51" + } +} diff --git a/extensions/json/package.nls.json b/extensions/json/package.nls.json new file mode 100644 index 0000000000..31385fd819 --- /dev/null +++ b/extensions/json/package.nls.json @@ -0,0 +1,11 @@ +{ + "json.schemas.desc": "Associate schemas to JSON files in the current project", + "json.schemas.url.desc": "A URL to a schema or a relative path to a schema in the current directory", + "json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas.", + "json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.", + "json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.", + "json.format.enable.desc": "Enable/disable default JSON formatter (requires restart)", + "json.tracing.desc": "Traces the communication between VS Code and the JSON language server.", + "json.colorDecorators.enable.desc": "Enables or disables color decorators", + "json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`." +} \ No newline at end of file diff --git a/extensions/json/server/.vscode/launch.json b/extensions/json/server/.vscode/launch.json new file mode 100644 index 0000000000..28ac186af5 --- /dev/null +++ b/extensions/json/server/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + "version": "0.1.0", + // List of configurations. Add new configurations or edit existing ones. + "configurations": [ + { + "name": "Attach", + "type": "node", + "request": "attach", + "port": 6004, + "sourceMaps": true, + "outDir": "${workspaceRoot}/out" + }, + { + "name": "Unit Tests", + "type": "node", + "request": "launch", + "program": "${workspaceRoot}/../../../node_modules/mocha/bin/_mocha", + "stopOnEntry": false, + "args": [ + "--timeout", + "999999", + "--colors" + ], + "cwd": "${workspaceRoot}", + "runtimeExecutable": null, + "runtimeArgs": [], + "env": {}, + "sourceMaps": true, + "outDir": "${workspaceRoot}/out" + } + ] +} \ No newline at end of file diff --git a/extensions/json/server/.vscode/tasks.json b/extensions/json/server/.vscode/tasks.json new file mode 100644 index 0000000000..6a159d6a5f --- /dev/null +++ b/extensions/json/server/.vscode/tasks.json @@ -0,0 +1,9 @@ +{ + "version": "0.1.0", + "command": "npm", + "isShellCommand": true, + "showOutput": "silent", + "args": ["run", "watch"], + "isWatching": true, + "problemMatcher": "$tsc-watch" +} \ No newline at end of file diff --git a/extensions/json/server/npm-shrinkwrap.json b/extensions/json/server/npm-shrinkwrap.json new file mode 100644 index 0000000000..517efec26a --- /dev/null +++ b/extensions/json/server/npm-shrinkwrap.json @@ -0,0 +1,81 @@ +{ + "name": "vscode-json-languageserver", + "version": "1.0.0", + "dependencies": { + "agent-base": { + "version": "1.0.2", + "from": "agent-base@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-1.0.2.tgz" + }, + "debug": { + "version": "2.6.6", + "from": "debug@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz" + }, + "extend": { + "version": "3.0.1", + "from": "extend@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz" + }, + "http-proxy-agent": { + "version": "0.2.7", + "from": "http-proxy-agent@>=0.2.6 <0.3.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-0.2.7.tgz" + }, + "https-proxy-agent": { + "version": "0.3.6", + "from": "https-proxy-agent@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-0.3.6.tgz" + }, + "jsonc-parser": { + "version": "1.0.0", + "from": "jsonc-parser@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-1.0.0.tgz" + }, + "ms": { + "version": "0.7.3", + "from": "ms@0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" + }, + "request-light": { + "version": "0.2.1", + "from": "request-light@0.2.1", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.2.1.tgz" + }, + "vscode-json-languageservice": { + "version": "2.0.16", + "from": "vscode-json-languageservice@next", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.16.tgz" + }, + "vscode-jsonrpc": { + "version": "3.3.1", + "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" + }, + "vscode-languageserver": { + "version": "3.4.0-next.6", + "from": "vscode-languageserver@next", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.6.tgz" + }, + "vscode-languageserver-protocol": { + "version": "3.1.1", + "from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz" + }, + "vscode-languageserver-types": { + "version": "3.3.0", + "from": "vscode-languageserver-types@>=3.0.3 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" + }, + "vscode-nls": { + "version": "2.0.2", + "from": "vscode-nls@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + }, + "vscode-uri": { + "version": "1.0.1", + "from": "vscode-uri@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.1.tgz" + } + } +} diff --git a/extensions/json/server/package.json b/extensions/json/server/package.json new file mode 100644 index 0000000000..3b6e892cc4 --- /dev/null +++ b/extensions/json/server/package.json @@ -0,0 +1,29 @@ +{ + "name": "vscode-json-languageserver", + "description": "JSON language server", + "version": "1.0.0", + "author": "Microsoft Corporation", + "license": "MIT", + "engines": { + "node": "*" + }, + "dependencies": { + "jsonc-parser": "^1.0.0", + "request-light": "^0.2.1", + "vscode-json-languageservice": "^2.0.16", + "vscode-languageserver": "3.4.0-next.6", + "vscode-languageserver-protocol": "^3.1.1", + "vscode-nls": "^2.0.2" + }, + "devDependencies": { + "@types/node": "^6.0.51" + }, + "scripts": { + "compile": "gulp compile-extension:json-server", + "watch": "gulp watch-extension:json-server", + "install-service-next": "npm install vscode-json-languageservice@next -f -S", + "install-service-local": "npm install ../../../../vscode-json-languageservice -f -S", + "install-server-next": "npm install vscode-languageserver@next -f -S", + "install-server-local": "npm install ../../../../vscode-languageserver-node/server -f -S" + } +} diff --git a/extensions/json/server/src/jsonServerMain.ts b/extensions/json/server/src/jsonServerMain.ts new file mode 100644 index 0000000000..f67d944820 --- /dev/null +++ b/extensions/json/server/src/jsonServerMain.ts @@ -0,0 +1,325 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { + createConnection, IConnection, + TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType, + DocumentRangeFormattingRequest, Disposable, ServerCapabilities +} from 'vscode-languageserver'; + +import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; + +import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; +import path = require('path'); +import fs = require('fs'); +import URI from './utils/uri'; +import * as URL from 'url'; +import Strings = require('./utils/strings'); +import { JSONDocument, JSONSchema, LanguageSettings, getLanguageService } from 'vscode-json-languageservice'; +import { getLanguageModelCache } from './languageModelCache'; + +import * as nls from 'vscode-nls'; +nls.config(process.env['VSCODE_NLS_CONFIG']); + +interface ISchemaAssociations { + [pattern: string]: string[]; +} + +namespace SchemaAssociationNotification { + export const type: NotificationType = new NotificationType('json/schemaAssociations'); +} + +namespace VSCodeContentRequest { + export const type: RequestType = new RequestType('vscode/content'); +} + +// Create a connection for the server +let connection: IConnection = createConnection(); + +console.log = connection.console.log.bind(connection.console); +console.error = connection.console.error.bind(connection.console); + +// Create a simple text document manager. The text document manager +// supports full document sync only +let documents: TextDocuments = new TextDocuments(); +// Make the text document manager listen on the connection +// for open, change and close text document events +documents.listen(connection); + +let clientSnippetSupport = false; +let clientDynamicRegisterSupport = false; + +// After the server has started the client sends an initilize request. The server receives +// in the passed params the rootPath of the workspace plus the client capabilities. +let workspaceRoot: URI; +connection.onInitialize((params: InitializeParams): InitializeResult => { + workspaceRoot = URI.parse(params.rootPath); + + function hasClientCapability(...keys: string[]) { + let c = params.capabilities; + for (let i = 0; c && i < keys.length; i++) { + c = c[keys[i]]; + } + return !!c; + } + + clientSnippetSupport = hasClientCapability('textDocument', 'completion', 'completionItem', 'snippetSupport'); + clientDynamicRegisterSupport = hasClientCapability('workspace', 'symbol', 'dynamicRegistration'); + let capabilities: ServerCapabilities & CPServerCapabilities = { + // Tell the client that the server works in FULL text document sync mode + textDocumentSync: documents.syncKind, + completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : null, + hoverProvider: true, + documentSymbolProvider: true, + documentRangeFormattingProvider: false, + colorProvider: true + }; + + return { capabilities }; +}); + +let workspaceContext = { + resolveRelativePath: (relativePath: string, resource: string) => { + return URL.resolve(resource, relativePath); + } +}; + +let schemaRequestService = (uri: string): Thenable => { + if (Strings.startsWith(uri, 'file://')) { + let fsPath = URI.parse(uri).fsPath; + return new Promise((c, e) => { + fs.readFile(fsPath, 'UTF-8', (err, result) => { + err ? e('') : c(result.toString()); + }); + }); + } else if (Strings.startsWith(uri, 'vscode://')) { + return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => { + return responseText; + }, error => { + return error.message; + }); + } + if (uri.indexOf('//schema.management.azure.com/') !== -1) { + connection.telemetry.logEvent({ + key: 'json.schema', + value: { + schemaURL: uri + } + }); + } + let headers = { 'Accept-Encoding': 'gzip, deflate' }; + return xhr({ url: uri, followRedirects: 5, headers }).then(response => { + return response.responseText; + }, (error: XHRResponse) => { + return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); + }); +}; + +// create the JSON language service +let languageService = getLanguageService({ + schemaRequestService, + workspaceContext, + contributions: [] +}); + +// The settings interface describes the server relevant settings part +interface Settings { + json: { + schemas: JSONSchemaSettings[]; + format: { enable: boolean; }; + }; + http: { + proxy: string; + proxyStrictSSL: boolean; + }; +} + +interface JSONSchemaSettings { + fileMatch?: string[]; + url?: string; + schema?: JSONSchema; +} + +let jsonConfigurationSettings: JSONSchemaSettings[] = void 0; +let schemaAssociations: ISchemaAssociations = void 0; +let formatterRegistration: Thenable = null; + +// The settings have changed. Is send on server activation as well. +connection.onDidChangeConfiguration((change) => { + var settings = change.settings; + configureHttpRequests(settings.http && settings.http.proxy, settings.http && settings.http.proxyStrictSSL); + + jsonConfigurationSettings = settings.json && settings.json.schemas; + updateConfiguration(); + + // dynamically enable & disable the formatter + if (clientDynamicRegisterSupport) { + let enableFormatter = settings && settings.json && settings.json.format && settings.json.format.enable; + if (enableFormatter) { + if (!formatterRegistration) { + formatterRegistration = connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector: [{ language: 'json' }] }); + } + } else if (formatterRegistration) { + formatterRegistration.then(r => r.dispose()); + formatterRegistration = null; + } + } +}); + +// The jsonValidation extension configuration has changed +connection.onNotification(SchemaAssociationNotification.type, associations => { + schemaAssociations = associations; + updateConfiguration(); +}); + +function updateConfiguration() { + let languageSettings: LanguageSettings = { + validate: true, + allowComments: true, + schemas: [] + }; + if (schemaAssociations) { + for (var pattern in schemaAssociations) { + let association = schemaAssociations[pattern]; + if (Array.isArray(association)) { + association.forEach(uri => { + languageSettings.schemas.push({ uri, fileMatch: [pattern] }); + }); + } + } + } + if (jsonConfigurationSettings) { + jsonConfigurationSettings.forEach(schema => { + let uri = schema.url; + if (!uri && schema.schema) { + uri = schema.schema.id; + } + if (!uri && schema.fileMatch) { + uri = 'vscode://schemas/custom/' + encodeURIComponent(schema.fileMatch.join('&')); + } + if (uri) { + if (uri[0] === '.' && workspaceRoot) { + // workspace relative path + uri = URI.file(path.normalize(path.join(workspaceRoot.fsPath, uri))).toString(); + } + languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema }); + } + }); + } + languageService.configure(languageSettings); + + // Revalidate any open text documents + documents.all().forEach(triggerValidation); +} + +// The content of a text document has changed. This event is emitted +// when the text document first opened or when its content has changed. +documents.onDidChangeContent((change) => { + triggerValidation(change.document); +}); + +// a document has closed: clear all diagnostics +documents.onDidClose(event => { + cleanPendingValidation(event.document); + connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); +}); + +let pendingValidationRequests: { [uri: string]: NodeJS.Timer; } = {}; +const validationDelayMs = 200; + +function cleanPendingValidation(textDocument: TextDocument): void { + let request = pendingValidationRequests[textDocument.uri]; + if (request) { + clearTimeout(request); + delete pendingValidationRequests[textDocument.uri]; + } +} + +function triggerValidation(textDocument: TextDocument): void { + cleanPendingValidation(textDocument); + pendingValidationRequests[textDocument.uri] = setTimeout(() => { + delete pendingValidationRequests[textDocument.uri]; + validateTextDocument(textDocument); + }, validationDelayMs); +} + +function validateTextDocument(textDocument: TextDocument): void { + if (textDocument.getText().length === 0) { + // ignore empty documents + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: [] }); + return; + } + + let jsonDocument = getJSONDocument(textDocument); + languageService.doValidation(textDocument, jsonDocument).then(diagnostics => { + // Send the computed diagnostics to VSCode. + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + }); +} + +connection.onDidChangeWatchedFiles((change) => { + // Monitored files have changed in VSCode + let hasChanges = false; + change.changes.forEach(c => { + if (languageService.resetSchema(c.uri)) { + hasChanges = true; + } + }); + if (hasChanges) { + documents.all().forEach(validateTextDocument); + } +}); + +let jsonDocuments = getLanguageModelCache(10, 60, document => languageService.parseJSONDocument(document)); +documents.onDidClose(e => { + jsonDocuments.onDocumentRemoved(e.document); +}); +connection.onShutdown(() => { + jsonDocuments.dispose(); +}); + +function getJSONDocument(document: TextDocument): JSONDocument { + return jsonDocuments.get(document); +} + +connection.onCompletion(textDocumentPosition => { + let document = documents.get(textDocumentPosition.textDocument.uri); + let jsonDocument = getJSONDocument(document); + return languageService.doComplete(document, textDocumentPosition.position, jsonDocument); +}); + +connection.onCompletionResolve(completionItem => { + return languageService.doResolve(completionItem); +}); + +connection.onHover(textDocumentPositionParams => { + let document = documents.get(textDocumentPositionParams.textDocument.uri); + let jsonDocument = getJSONDocument(document); + return languageService.doHover(document, textDocumentPositionParams.position, jsonDocument); +}); + +connection.onDocumentSymbol(documentSymbolParams => { + let document = documents.get(documentSymbolParams.textDocument.uri); + let jsonDocument = getJSONDocument(document); + return languageService.findDocumentSymbols(document, jsonDocument); +}); + +connection.onDocumentRangeFormatting(formatParams => { + let document = documents.get(formatParams.textDocument.uri); + return languageService.format(document, formatParams.range, formatParams.options); +}); + +connection.onRequest(DocumentColorRequest.type, params => { + let document = documents.get(params.textDocument.uri); + if (document) { + let jsonDocument = getJSONDocument(document); + return languageService.findDocumentColors(document, jsonDocument); + } + return []; +}); + +// Listen on the connection +connection.listen(); \ No newline at end of file diff --git a/extensions/json/server/src/languageModelCache.ts b/extensions/json/server/src/languageModelCache.ts new file mode 100644 index 0000000000..074285d07c --- /dev/null +++ b/extensions/json/server/src/languageModelCache.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TextDocument } from 'vscode-languageserver'; + +export interface LanguageModelCache { + get(document: TextDocument): T; + onDocumentRemoved(document: TextDocument): void; + dispose(): void; +} + +export function getLanguageModelCache(maxEntries: number, cleanupIntervalTimeInSec: number, parse: (document: TextDocument) => T): LanguageModelCache { + let languageModels: { [uri: string]: { version: number, languageId: string, cTime: number, languageModel: T } } = {}; + let nModels = 0; + + let cleanupInterval = void 0; + if (cleanupIntervalTimeInSec > 0) { + cleanupInterval = setInterval(() => { + let cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; + let uris = Object.keys(languageModels); + for (let uri of uris) { + let languageModelInfo = languageModels[uri]; + if (languageModelInfo.cTime < cutoffTime) { + delete languageModels[uri]; + nModels--; + } + } + }, cleanupIntervalTimeInSec * 1000); + } + + return { + get(document: TextDocument): T { + let version = document.version; + let languageId = document.languageId; + let languageModelInfo = languageModels[document.uri]; + if (languageModelInfo && languageModelInfo.version === version && languageModelInfo.languageId === languageId) { + languageModelInfo.cTime = Date.now(); + return languageModelInfo.languageModel; + } + let languageModel = parse(document); + languageModels[document.uri] = { languageModel, version, languageId, cTime: Date.now() }; + if (!languageModelInfo) { + nModels++; + } + + if (nModels === maxEntries) { + let oldestTime = Number.MAX_VALUE; + let oldestUri = null; + for (let uri in languageModels) { + let languageModelInfo = languageModels[uri]; + if (languageModelInfo.cTime < oldestTime) { + oldestUri = uri; + oldestTime = languageModelInfo.cTime; + } + } + if (oldestUri) { + delete languageModels[oldestUri]; + nModels--; + } + } + return languageModel; + + }, + onDocumentRemoved(document: TextDocument) { + let uri = document.uri; + if (languageModels[uri]) { + delete languageModels[uri]; + nModels--; + } + }, + dispose() { + if (typeof cleanupInterval !== 'undefined') { + clearInterval(cleanupInterval); + cleanupInterval = void 0; + languageModels = {}; + nModels = 0; + } + } + }; +} \ No newline at end of file diff --git a/extensions/json/server/src/utils/strings.ts b/extensions/json/server/src/utils/strings.ts new file mode 100644 index 0000000000..ae35803d72 --- /dev/null +++ b/extensions/json/server/src/utils/strings.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +export function startsWith(haystack: string, needle: string): boolean { + if (haystack.length < needle.length) { + return false; + } + + for (let i = 0; i < needle.length; i++) { + if (haystack[i] !== needle[i]) { + return false; + } + } + + return true; +} + +/** + * Determines if haystack ends with needle. + */ +export function endsWith(haystack: string, needle: string): boolean { + let diff = haystack.length - needle.length; + if (diff > 0) { + return haystack.lastIndexOf(needle) === diff; + } else if (diff === 0) { + return haystack === needle; + } else { + return false; + } +} + +export function convertSimple2RegExpPattern(pattern: string): string { + return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*'); +} diff --git a/extensions/json/server/src/utils/uri.ts b/extensions/json/server/src/utils/uri.ts new file mode 100644 index 0000000000..332c486250 --- /dev/null +++ b/extensions/json/server/src/utils/uri.ts @@ -0,0 +1,351 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +function _encode(ch: string): string { + return '%' + ch.charCodeAt(0).toString(16).toUpperCase(); +} + +// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent +function encodeURIComponent2(str: string): string { + return encodeURIComponent(str).replace(/[!'()*]/g, _encode); +} + +function encodeNoop(str: string): string { + return str; +} + + +/** + * Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986. + * This class is a simple parser which creates the basic component paths + * (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation + * and encoding. + * + * foo://example.com:8042/over/there?name=ferret#nose + * \_/ \______________/\_________/ \_________/ \__/ + * | | | | | + * scheme authority path query fragment + * | _____________________|__ + * / \ / \ + * urn:example:animal:ferret:nose + * + * + */ +export default class URI { + + private static _empty = ''; + private static _slash = '/'; + private static _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; + private static _driveLetterPath = /^\/[a-zA-z]:/; + private static _upperCaseDrive = /^(\/)?([A-Z]:)/; + + private _scheme: string; + private _authority: string; + private _path: string; + private _query: string; + private _fragment: string; + private _formatted: string; + private _fsPath: string; + + constructor() { + this._scheme = URI._empty; + this._authority = URI._empty; + this._path = URI._empty; + this._query = URI._empty; + this._fragment = URI._empty; + + this._formatted = null; + this._fsPath = null; + } + + /** + * scheme is the 'http' part of 'http://www.msft.com/some/path?query#fragment'. + * The part before the first colon. + */ + get scheme() { + return this._scheme; + } + + /** + * authority is the 'www.msft.com' part of 'http://www.msft.com/some/path?query#fragment'. + * The part between the first double slashes and the next slash. + */ + get authority() { + return this._authority; + } + + /** + * path is the '/some/path' part of 'http://www.msft.com/some/path?query#fragment'. + */ + get path() { + return this._path; + } + + /** + * query is the 'query' part of 'http://www.msft.com/some/path?query#fragment'. + */ + get query() { + return this._query; + } + + /** + * fragment is the 'fragment' part of 'http://www.msft.com/some/path?query#fragment'. + */ + get fragment() { + return this._fragment; + } + + // ---- filesystem path ----------------------- + + /** + * Returns a string representing the corresponding file system path of this URI. + * Will handle UNC paths and normalize windows drive letters to lower-case. Also + * uses the platform specific path separator. Will *not* validate the path for + * invalid characters and semantics. Will *not* look at the scheme of this URI. + */ + get fsPath() { + if (!this._fsPath) { + var value: string; + if (this._authority && this.scheme === 'file') { + // unc path: file://shares/c$/far/boo + value = `//${this._authority}${this._path}`; + } else if (URI._driveLetterPath.test(this._path)) { + // windows drive letter: file:///c:/far/boo + value = this._path[1].toLowerCase() + this._path.substr(2); + } else { + // other path + value = this._path; + } + if (process.platform === 'win32') { + value = value.replace(/\//g, '\\'); + } + this._fsPath = value; + } + return this._fsPath; + } + + // ---- modify to new ------------------------- + + public with(scheme: string, authority: string, path: string, query: string, fragment: string): URI { + var ret = new URI(); + ret._scheme = scheme || this.scheme; + ret._authority = authority || this.authority; + ret._path = path || this.path; + ret._query = query || this.query; + ret._fragment = fragment || this.fragment; + URI._validate(ret); + return ret; + } + + public withScheme(value: string): URI { + return this.with(value, undefined, undefined, undefined, undefined); + } + + public withAuthority(value: string): URI { + return this.with(undefined, value, undefined, undefined, undefined); + } + + public withPath(value: string): URI { + return this.with(undefined, undefined, value, undefined, undefined); + } + + public withQuery(value: string): URI { + return this.with(undefined, undefined, undefined, value, undefined); + } + + public withFragment(value: string): URI { + return this.with(undefined, undefined, undefined, undefined, value); + } + + // ---- parse & validate ------------------------ + + public static parse(value: string): URI { + const ret = new URI(); + const data = URI._parseComponents(value); + ret._scheme = data.scheme; + ret._authority = decodeURIComponent(data.authority); + ret._path = decodeURIComponent(data.path); + ret._query = decodeURIComponent(data.query); + ret._fragment = decodeURIComponent(data.fragment); + URI._validate(ret); + return ret; + } + + public static file(path: string): URI { + + const ret = new URI(); + ret._scheme = 'file'; + + // normalize to fwd-slashes + path = path.replace(/\\/g, URI._slash); + + // check for authority as used in UNC shares + // or use the path as given + if (path[0] === URI._slash && path[0] === path[1]) { + let idx = path.indexOf(URI._slash, 2); + if (idx === -1) { + ret._authority = path.substring(2); + } else { + ret._authority = path.substring(2, idx); + ret._path = path.substring(idx); + } + } else { + ret._path = path; + } + + // Ensure that path starts with a slash + // or that it is at least a slash + if (ret._path[0] !== URI._slash) { + ret._path = URI._slash + ret._path; + } + + URI._validate(ret); + + return ret; + } + + private static _parseComponents(value: string): UriComponents { + + const ret: UriComponents = { + scheme: URI._empty, + authority: URI._empty, + path: URI._empty, + query: URI._empty, + fragment: URI._empty, + }; + + const match = URI._regexp.exec(value); + if (match) { + ret.scheme = match[2] || ret.scheme; + ret.authority = match[4] || ret.authority; + ret.path = match[5] || ret.path; + ret.query = match[7] || ret.query; + ret.fragment = match[9] || ret.fragment; + } + return ret; + } + + public static create(scheme?: string, authority?: string, path?: string, query?: string, fragment?: string): URI { + return new URI().with(scheme, authority, path, query, fragment); + } + + private static _validate(ret: URI): void { + + // validation + // path, http://tools.ietf.org/html/rfc3986#section-3.3 + // If a URI contains an authority component, then the path component + // must either be empty or begin with a slash ("/") character. If a URI + // does not contain an authority component, then the path cannot begin + // with two slash characters ("//"). + if (ret.authority && ret.path && ret.path[0] !== '/') { + throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character'); + } + if (!ret.authority && ret.path.indexOf('//') === 0) { + throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")'); + } + } + + // ---- printing/externalize --------------------------- + + /** + * + * @param skipEncoding Do not encode the result, default is `false` + */ + public toString(skipEncoding: boolean = false): string { + if (!skipEncoding) { + if (!this._formatted) { + this._formatted = URI._asFormatted(this, false); + } + return this._formatted; + } else { + // we don't cache that + return URI._asFormatted(this, true); + } + } + + private static _asFormatted(uri: URI, skipEncoding: boolean): string { + + const encoder = !skipEncoding + ? encodeURIComponent2 + : encodeNoop; + + const parts: string[] = []; + + let { scheme, authority, path, query, fragment } = uri; + if (scheme) { + parts.push(scheme, ':'); + } + if (authority || scheme === 'file') { + parts.push('//'); + } + if (authority) { + authority = authority.toLowerCase(); + let idx = authority.indexOf(':'); + if (idx === -1) { + parts.push(encoder(authority)); + } else { + parts.push(encoder(authority.substr(0, idx)), authority.substr(idx)); + } + } + if (path) { + // lower-case windown drive letters in /C:/fff + const m = URI._upperCaseDrive.exec(path); + if (m) { + path = m[1] + m[2].toLowerCase() + path.substr(m[1].length + m[2].length); + } + + // encode every segement but not slashes + // make sure that # and ? are always encoded + // when occurring in paths - otherwise the result + // cannot be parsed back again + let lastIdx = 0; + while (true) { + let idx = path.indexOf(URI._slash, lastIdx); + if (idx === -1) { + parts.push(encoder(path.substring(lastIdx)).replace(/[#?]/, _encode)); + break; + } + parts.push(encoder(path.substring(lastIdx, idx)).replace(/[#?]/, _encode), URI._slash); + lastIdx = idx + 1; + }; + } + if (query) { + parts.push('?', encoder(query)); + } + if (fragment) { + parts.push('#', encoder(fragment)); + } + + return parts.join(URI._empty); + } + + public toJSON(): any { + return { + scheme: this.scheme, + authority: this.authority, + path: this.path, + fsPath: this.fsPath, + query: this.query, + fragment: this.fragment, + external: this.toString(), + $mid: 1 + }; + } +} + +interface UriComponents { + scheme: string; + authority: string; + path: string; + query: string; + fragment: string; +} + +interface UriState extends UriComponents { + $mid: number; + fsPath: string; + external: string; +} diff --git a/extensions/json/server/tsconfig.json b/extensions/json/server/tsconfig.json new file mode 100644 index 0000000000..deecf69b8b --- /dev/null +++ b/extensions/json/server/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "sourceMap": true, + "sourceRoot": "../src", + "outDir": "./out", + "lib": [ + "es5", "es2015.promise" + ] + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/json/syntaxes/JSON.tmLanguage.json b/extensions/json/syntaxes/JSON.tmLanguage.json new file mode 100644 index 0000000000..f37355667b --- /dev/null +++ b/extensions/json/syntaxes/JSON.tmLanguage.json @@ -0,0 +1,228 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/Microsoft/vscode-JSON.tmLanguage/blob/master/JSON.tmLanguage", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/Microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70", + "fileTypes": [ + "json", + "sublime-settings", + "sublime-menu", + "sublime-keymap", + "sublime-mousemap", + "sublime-theme", + "sublime-build", + "sublime-project", + "sublime-completions" + ], + "foldingStartMarker": "(?x) # turn on extended mode\n ^ # a line beginning with\n \\s* # some optional space\n [{\\[] # the start of an object or array\n (?! # but not followed by\n .* # whatever\n [}\\]] # and the close of an object or array\n ,? # an optional comma\n \\s* # some optional space\n $ # at the end of the line\n )\n | # ...or...\n [{\\[] # the start of an object or array\n \\s* # some optional space\n $ # at the end of the line", + "foldingStopMarker": "(?x) # turn on extended mode\n ^ # a line beginning with\n \\s* # some optional space\n [}\\]] # and the close of an object or array", + "keyEquivalent": "^~J", + "name": "JSON (Javascript Next)", + "patterns": [ + { + "include": "#value" + } + ], + "repository": { + "array": { + "begin": "\\[", + "beginCaptures": { + "0": { + "name": "punctuation.definition.array.begin.json" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.definition.array.end.json" + } + }, + "name": "meta.structure.array.json", + "patterns": [ + { + "include": "#value" + }, + { + "match": ",", + "name": "punctuation.separator.array.json" + }, + { + "match": "[^\\s\\]]", + "name": "invalid.illegal.expected-array-separator.json" + } + ] + }, + "comments": { + "patterns": [ + { + "begin": "/\\*\\*(?!/)", + "captures": { + "0": { + "name": "punctuation.definition.comment.json" + } + }, + "end": "\\*/", + "name": "comment.block.documentation.json" + }, + { + "begin": "/\\*", + "captures": { + "0": { + "name": "punctuation.definition.comment.json" + } + }, + "end": "\\*/", + "name": "comment.block.json" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.comment.json" + } + }, + "match": "(//).*$\\n?", + "name": "comment.line.double-slash.js" + } + ] + }, + "constant": { + "match": "\\b(?:true|false|null)\\b", + "name": "constant.language.json" + }, + "number": { + "match": "(?x) # turn on extended mode\n -? # an optional minus\n (?:\n 0 # a zero\n | # ...or...\n [1-9] # a 1-9 character\n \\d* # followed by zero or more digits\n )\n (?:\n (?:\n \\. # a period\n \\d+ # followed by one or more digits\n )?\n (?:\n [eE] # an e character\n [+-]? # followed by an option +/-\n \\d+ # followed by one or more digits\n )? # make exponent optional\n )? # make decimal portion optional", + "name": "constant.numeric.json" + }, + "object": { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.dictionary.begin.json" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.definition.dictionary.end.json" + } + }, + "name": "meta.structure.dictionary.json", + "patterns": [ + { + "comment": "the JSON object key", + "include": "#objectkey" + }, + { + "include": "#comments" + }, + { + "begin": ":", + "beginCaptures": { + "0": { + "name": "punctuation.separator.dictionary.key-value.json" + } + }, + "end": "(,)|(?=\\})", + "endCaptures": { + "1": { + "name": "punctuation.separator.dictionary.pair.json" + } + }, + "name": "meta.structure.dictionary.value.json", + "patterns": [ + { + "comment": "the JSON object value", + "include": "#value" + }, + { + "match": "[^\\s,]", + "name": "invalid.illegal.expected-dictionary-separator.json" + } + ] + }, + { + "match": "[^\\s\\}]", + "name": "invalid.illegal.expected-dictionary-separator.json" + } + ] + }, + "string": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.json" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.json" + } + }, + "name": "string.quoted.double.json", + "patterns": [ + { + "include": "#stringcontent" + } + ] + }, + "objectkey": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.support.type.property-name.begin.json" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.support.type.property-name.end.json" + } + }, + "name": "string.json support.type.property-name.json", + "patterns": [ + { + "include": "#stringcontent" + } + ] + }, + "stringcontent": { + "patterns": [ + { + "match": "(?x) # turn on extended mode\n \\\\ # a literal backslash\n (?: # ...followed by...\n [\"\\\\/bfnrt] # one of these characters\n | # ...or...\n u # a u\n [0-9a-fA-F]{4}) # and four hex digits", + "name": "constant.character.escape.json" + }, + { + "match": "\\\\.", + "name": "invalid.illegal.unrecognized-string-escape.json" + } + ] + }, + "value": { + "patterns": [ + { + "include": "#constant" + }, + { + "include": "#number" + }, + { + "include": "#string" + }, + { + "include": "#array" + }, + { + "include": "#object" + }, + { + "include": "#comments" + } + ] + } + }, + "scopeName": "source.json", + "uuid": "8f97457b-516e-48ce-83c7-08ae12fb327a" +} \ No newline at end of file diff --git a/extensions/json/test/colorize-fixtures/test.json b/extensions/json/test/colorize-fixtures/test.json new file mode 100644 index 0000000000..189b7269d4 --- /dev/null +++ b/extensions/json/test/colorize-fixtures/test.json @@ -0,0 +1,14 @@ +{ + // a comment + "options": { + "myBool": true, + "myInteger": 1, + "myString": "String\u0056", + "myNumber": 1.24, + "myNull": null, + "myArray": [ 1, "Hello", true, null, [], {}], + "myObject" : { + "foo": "bar" + } + } +} \ No newline at end of file diff --git a/extensions/json/test/colorize-results/test_json.json b/extensions/json/test/colorize-results/test_json.json new file mode 100644 index 0000000000..5f9b48b1ea --- /dev/null +++ b/extensions/json/test/colorize-results/test_json.json @@ -0,0 +1,1168 @@ +[ + { + "c": "{", + "t": "source.json meta.structure.dictionary.json punctuation.definition.dictionary.begin.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "//", + "t": "source.json meta.structure.dictionary.json comment.line.double-slash.js punctuation.definition.comment.json", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " a comment", + "t": "source.json meta.structure.dictionary.json comment.line.double-slash.js", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\t", + "t": "source.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "options", + "t": "source.json meta.structure.dictionary.json string.json support.type.property-name.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": ":", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json punctuation.definition.dictionary.begin.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "myBool", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": ":", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "true", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json constant.language.json", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.pair.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "myInteger", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": ":", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json constant.numeric.json", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.pair.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "myString", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": ":", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json punctuation.definition.string.begin.json", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "String", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\\u0056", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json constant.character.escape.json", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json punctuation.definition.string.end.json", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.pair.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "myNumber", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": ":", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1.24", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json constant.numeric.json", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.pair.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "myNull", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": ":", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "null", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json constant.language.json", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.pair.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "myArray", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": ":", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json punctuation.definition.array.begin.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json constant.numeric.json", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json punctuation.separator.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json string.quoted.double.json punctuation.definition.string.begin.json", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Hello", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json string.quoted.double.json", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json string.quoted.double.json punctuation.definition.string.end.json", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json punctuation.separator.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "true", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json constant.language.json", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json punctuation.separator.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "null", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json constant.language.json", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json punctuation.separator.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json meta.structure.array.json punctuation.definition.array.begin.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json meta.structure.array.json punctuation.definition.array.end.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json punctuation.separator.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json meta.structure.dictionary.json punctuation.definition.dictionary.begin.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json meta.structure.dictionary.json punctuation.definition.dictionary.end.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json punctuation.definition.array.end.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.pair.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "myObject", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json punctuation.definition.dictionary.begin.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t\t", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "foo", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": ":", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json punctuation.definition.string.begin.json", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "bar", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json punctuation.definition.string.end.json", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\t\t", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json punctuation.definition.dictionary.end.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json punctuation.definition.dictionary.end.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.json meta.structure.dictionary.json punctuation.definition.dictionary.end.json", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + } +] \ No newline at end of file diff --git a/extensions/lib.core.d.ts b/extensions/lib.core.d.ts new file mode 100644 index 0000000000..fb3fe52c13 --- /dev/null +++ b/extensions/lib.core.d.ts @@ -0,0 +1,3839 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + +/// +///////////////////////////// +/// ECMAScript APIs +///////////////////////////// + +declare var NaN: number; +declare var Infinity: number; + +/** + * Evaluates JavaScript code and executes it. + * @param x A String value that contains valid JavaScript code. + */ +declare function eval(x: string): any; + +/** + * Converts A string to an integer. + * @param s A string to convert into a number. + * @param radix A value between 2 and 36 that specifies the base of the number in numString. + * If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal. + * All other strings are considered decimal. + */ +declare function parseInt(s: string, radix?: number): number; + +/** + * Converts a string to a floating-point number. + * @param string A string that contains a floating-point number. + */ +declare function parseFloat(string: string): number; + +/** + * Returns a Boolean value that indicates whether a value is the reserved value NaN (not a number). + * @param number A numeric value. + */ +declare function isNaN(number: number): boolean; + +/** + * Determines whether a supplied number is finite. + * @param number Any numeric value. + */ +declare function isFinite(number: number): boolean; + +/** + * Gets the unencoded version of an encoded Uniform Resource Identifier (URI). + * @param encodedURI A value representing an encoded URI. + */ +declare function decodeURI(encodedURI: string): string; + +/** + * Gets the unencoded version of an encoded component of a Uniform Resource Identifier (URI). + * @param encodedURIComponent A value representing an encoded URI component. + */ +declare function decodeURIComponent(encodedURIComponent: string): string; + +/** + * Encodes a text string as a valid Uniform Resource Identifier (URI) + * @param uri A value representing an encoded URI. + */ +declare function encodeURI(uri: string): string; + +/** + * Encodes a text string as a valid component of a Uniform Resource Identifier (URI). + * @param uriComponent A value representing an encoded URI component. + */ +declare function encodeURIComponent(uriComponent: string): string; + +interface PropertyDescriptor { + configurable?: boolean; + enumerable?: boolean; + value?: any; + writable?: boolean; + get? (): any; + set? (v: any): void; +} + +interface PropertyDescriptorMap { + [s: string]: PropertyDescriptor; +} + +interface Object { + /** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */ + constructor: Function; + + /** Returns a string representation of an object. */ + toString(): string; + + /** Returns a date converted to a string using the current locale. */ + toLocaleString(): string; + + /** Returns the primitive value of the specified object. */ + valueOf(): Object; + + /** + * Determines whether an object has a property with the specified name. + * @param v A property name. + */ + hasOwnProperty(v: string): boolean; + + /** + * Determines whether an object exists in another object's prototype chain. + * @param v Another object whose prototype chain is to be checked. + */ + isPrototypeOf(v: Object): boolean; + + /** + * Determines whether a specified property is enumerable. + * @param v A property name. + */ + propertyIsEnumerable(v: string): boolean; +} + +interface ObjectConstructor { + new (value?: any): Object; + (): any; + (value: any): any; + + /** A reference to the prototype for a class of objects. */ + prototype: Object; + + /** + * Returns the prototype of an object. + * @param o The object that references the prototype. + */ + getPrototypeOf(o: any): any; + + /** + * Gets the own property descriptor of the specified object. + * An own property descriptor is one that is defined directly on the object and is not inherited from the object's prototype. + * @param o Object that contains the property. + * @param p Name of the property. + */ + getOwnPropertyDescriptor(o: any, p: string): PropertyDescriptor; + + /** + * Returns the names of the own properties of an object. The own properties of an object are those that are defined directly + * on that object, and are not inherited from the object's prototype. The properties of an object include both fields (objects) and functions. + * @param o Object that contains the own properties. + */ + getOwnPropertyNames(o: any): string[]; + + /** + * Creates an object that has the specified prototype, and that optionally contains specified properties. + * @param o Object to use as a prototype. May be null + * @param properties JavaScript object that contains one or more property descriptors. + */ + create(o: any, properties?: PropertyDescriptorMap): any; + + /** + * Adds a property to an object, or modifies attributes of an existing property. + * @param o Object on which to add or modify the property. This can be a native JavaScript object (that is, a user-defined object or a built in object) or a DOM object. + * @param p The property name. + * @param attributes Descriptor for the property. It can be for a data property or an accessor property. + */ + defineProperty(o: any, p: string, attributes: PropertyDescriptor): any; + + /** + * Adds one or more properties to an object, and/or modifies attributes of existing properties. + * @param o Object on which to add or modify the properties. This can be a native JavaScript object or a DOM object. + * @param properties JavaScript object that contains one or more descriptor objects. Each descriptor object describes a data property or an accessor property. + */ + defineProperties(o: any, properties: PropertyDescriptorMap): any; + + /** + * Prevents the modification of attributes of existing properties, and prevents the addition of new properties. + * @param o Object on which to lock the attributes. + */ + seal(o: T): T; + + /** + * Prevents the modification of existing property attributes and values, and prevents the addition of new properties. + * @param o Object on which to lock the attributes. + */ + freeze(o: T): T; + + /** + * Prevents the addition of new properties to an object. + * @param o Object to make non-extensible. + */ + preventExtensions(o: T): T; + + /** + * Returns true if existing property attributes cannot be modified in an object and new properties cannot be added to the object. + * @param o Object to test. + */ + isSealed(o: any): boolean; + + /** + * Returns true if existing property attributes and values cannot be modified in an object, and new properties cannot be added to the object. + * @param o Object to test. + */ + isFrozen(o: any): boolean; + + /** + * Returns a value that indicates whether new properties can be added to an object. + * @param o Object to test. + */ + isExtensible(o: any): boolean; + + /** + * Returns the names of the enumerable properties and methods of an object. + * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object. + */ + keys(o: any): string[]; +} + +/** + * Provides functionality common to all JavaScript objects. + */ +declare var Object: ObjectConstructor; + +/** + * Creates a new function. + */ +interface Function { + /** + * Calls the function, substituting the specified object for the this value of the function, and the specified array for the arguments of the function. + * @param thisArg The object to be used as the this object. + * @param argArray A set of arguments to be passed to the function. + */ + apply(thisArg: any, argArray?: any): any; + + /** + * Calls a method of an object, substituting another object for the current object. + * @param thisArg The object to be used as the current object. + * @param argArray A list of arguments to be passed to the method. + */ + call(thisArg: any, ...argArray: any[]): any; + + /** + * For a given function, creates a bound function that has the same body as the original function. + * The this object of the bound function is associated with the specified object, and has the specified initial parameters. + * @param thisArg An object to which the this keyword can refer inside the new function. + * @param argArray A list of arguments to be passed to the new function. + */ + bind(thisArg: any, ...argArray: any[]): any; + + prototype: any; + length: number; + + // Non-standard extensions + arguments: any; + caller: Function; +} + +interface FunctionConstructor { + /** + * Creates a new function. + * @param args A list of arguments the function accepts. + */ + new (...args: string[]): Function; + (...args: string[]): Function; + prototype: Function; +} + +declare var Function: FunctionConstructor; + +interface IArguments { + [index: number]: any; + length: number; + callee: Function; +} + +interface String { + /** Returns a string representation of a string. */ + toString(): string; + + /** + * Returns the character at the specified index. + * @param pos The zero-based index of the desired character. + */ + charAt(pos: number): string; + + /** + * Returns the Unicode value of the character at the specified location. + * @param index The zero-based index of the desired character. If there is no character at the specified index, NaN is returned. + */ + charCodeAt(index: number): number; + + /** + * Returns a string that contains the concatenation of two or more strings. + * @param strings The strings to append to the end of the string. + */ + concat(...strings: string[]): string; + + /** + * Returns the position of the first occurrence of a substring. + * @param searchString The substring to search for in the string + * @param position The index at which to begin searching the String object. If omitted, search starts at the beginning of the string. + */ + indexOf(searchString: string, position?: number): number; + + /** + * Returns the last occurrence of a substring in the string. + * @param searchString The substring to search for. + * @param position The index at which to begin searching. If omitted, the search begins at the end of the string. + */ + lastIndexOf(searchString: string, position?: number): number; + + /** + * Determines whether two strings are equivalent in the current locale. + * @param that String to compare to target string + */ + localeCompare(that: string): number; + + /** + * Matches a string with a regular expression, and returns an array containing the results of that search. + * @param regexp A variable name or string literal containing the regular expression pattern and flags. + */ + match(regexp: string): RegExpMatchArray; + + /** + * Matches a string with a regular expression, and returns an array containing the results of that search. + * @param regexp A regular expression object that contains the regular expression pattern and applicable flags. + */ + match(regexp: RegExp): RegExpMatchArray; + + /** + * Replaces text in a string, using a regular expression or search string. + * @param searchValue A string that represents the regular expression. + * @param replaceValue A string containing the text to replace for every successful match of searchValue in this string. + */ + replace(searchValue: string, replaceValue: string): string; + + /** + * Replaces text in a string, using a regular expression or search string. + * @param searchValue A string that represents the regular expression. + * @param replacer A function that returns the replacement text. + */ + replace(searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; + + /** + * Replaces text in a string, using a regular expression or search string. + * @param searchValue A Regular Expression object containing the regular expression pattern and applicable flags. + * @param replaceValue A string containing the text to replace for every successful match of searchValue in this string. + */ + replace(searchValue: RegExp, replaceValue: string): string; + + /** + * Replaces text in a string, using a regular expression or search string. + * @param searchValue A Regular Expression object containing the regular expression pattern and applicable flags + * @param replacer A function that returns the replacement text. + */ + replace(searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; + + /** + * Finds the first substring match in a regular expression search. + * @param regexp The regular expression pattern and applicable flags. + */ + search(regexp: string): number; + + /** + * Finds the first substring match in a regular expression search. + * @param regexp The regular expression pattern and applicable flags. + */ + search(regexp: RegExp): number; + + /** + * Returns a section of a string. + * @param start The index to the beginning of the specified portion of stringObj. + * @param end The index to the end of the specified portion of stringObj. The substring includes the characters up to, but not including, the character indicated by end. + * If this value is not specified, the substring continues to the end of stringObj. + */ + slice(start?: number, end?: number): string; + + /** + * Split a string into substrings using the specified separator and return them as an array. + * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned. + * @param limit A value used to limit the number of elements returned in the array. + */ + split(separator: string, limit?: number): string[]; + + /** + * Split a string into substrings using the specified separator and return them as an array. + * @param separator A Regular Express that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned. + * @param limit A value used to limit the number of elements returned in the array. + */ + split(separator: RegExp, limit?: number): string[]; + + /** + * Returns the substring at the specified location within a String object. + * @param start The zero-based index number indicating the beginning of the substring. + * @param end Zero-based index number indicating the end of the substring. The substring includes the characters up to, but not including, the character indicated by end. + * If end is omitted, the characters from start through the end of the original string are returned. + */ + substring(start: number, end?: number): string; + + /** Converts all the alphabetic characters in a string to lowercase. */ + toLowerCase(): string; + + /** Converts all alphabetic characters to lowercase, taking into account the host environment's current locale. */ + toLocaleLowerCase(): string; + + /** Converts all the alphabetic characters in a string to uppercase. */ + toUpperCase(): string; + + /** Returns a string where all alphabetic characters have been converted to uppercase, taking into account the host environment's current locale. */ + toLocaleUpperCase(): string; + + /** Removes the leading and trailing white space and line terminator characters from a string. */ + trim(): string; + + /** Returns the length of a String object. */ + length: number; + + // IE extensions + /** + * Gets a substring beginning at the specified location and having the specified length. + * @param from The starting position of the desired substring. The index of the first character in the string is zero. + * @param length The number of characters to include in the returned substring. + */ + substr(from: number, length?: number): string; + + /** Returns the primitive value of the specified object. */ + valueOf(): string; + + [index: number]: string; +} + +interface StringConstructor { + new (value?: any): String; + (value?: any): string; + prototype: String; + fromCharCode(...codes: number[]): string; +} + +/** + * Allows manipulation and formatting of text strings and determination and location of substrings within strings. + */ +declare var String: StringConstructor; + +interface Boolean { + /** Returns the primitive value of the specified object. */ + valueOf(): boolean; +} + +interface BooleanConstructor { + new (value?: any): Boolean; + (value?: any): boolean; + prototype: Boolean; +} + +declare var Boolean: BooleanConstructor; + +interface Number { + /** + * Returns a string representation of an object. + * @param radix Specifies a radix for converting numeric values to strings. This value is only used for numbers. + */ + toString(radix?: number): string; + + /** + * Returns a string representing a number in fixed-point notation. + * @param fractionDigits Number of digits after the decimal point. Must be in the range 0 - 20, inclusive. + */ + toFixed(fractionDigits?: number): string; + + /** + * Returns a string containing a number represented in exponential notation. + * @param fractionDigits Number of digits after the decimal point. Must be in the range 0 - 20, inclusive. + */ + toExponential(fractionDigits?: number): string; + + /** + * Returns a string containing a number represented either in exponential or fixed-point notation with a specified number of digits. + * @param precision Number of significant digits. Must be in the range 1 - 21, inclusive. + */ + toPrecision(precision?: number): string; + + /** Returns the primitive value of the specified object. */ + valueOf(): number; +} + +interface NumberConstructor { + new (value?: any): Number; + (value?: any): number; + prototype: Number; + + /** The largest number that can be represented in JavaScript. Equal to approximately 1.79E+308. */ + MAX_VALUE: number; + + /** The closest number to zero that can be represented in JavaScript. Equal to approximately 5.00E-324. */ + MIN_VALUE: number; + + /** + * A value that is not a number. + * In equality comparisons, NaN does not equal any value, including itself. To test whether a value is equivalent to NaN, use the isNaN function. + */ + NaN: number; + + /** + * A value that is less than the largest negative number that can be represented in JavaScript. + * JavaScript displays NEGATIVE_INFINITY values as -infinity. + */ + NEGATIVE_INFINITY: number; + + /** + * A value greater than the largest number that can be represented in JavaScript. + * JavaScript displays POSITIVE_INFINITY values as infinity. + */ + POSITIVE_INFINITY: number; +} + +/** An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers. */ +declare var Number: NumberConstructor; + +interface TemplateStringsArray extends Array { + raw: string[]; +} + +interface Math { + /** The mathematical constant e. This is Euler's number, the base of natural logarithms. */ + E: number; + /** The natural logarithm of 10. */ + LN10: number; + /** The natural logarithm of 2. */ + LN2: number; + /** The base-2 logarithm of e. */ + LOG2E: number; + /** The base-10 logarithm of e. */ + LOG10E: number; + /** Pi. This is the ratio of the circumference of a circle to its diameter. */ + PI: number; + /** The square root of 0.5, or, equivalently, one divided by the square root of 2. */ + SQRT1_2: number; + /** The square root of 2. */ + SQRT2: number; + /** + * Returns the absolute value of a number (the value without regard to whether it is positive or negative). + * For example, the absolute value of -5 is the same as the absolute value of 5. + * @param x A numeric expression for which the absolute value is needed. + */ + abs(x: number): number; + /** + * Returns the arc cosine (or inverse cosine) of a number. + * @param x A numeric expression. + */ + acos(x: number): number; + /** + * Returns the arcsine of a number. + * @param x A numeric expression. + */ + asin(x: number): number; + /** + * Returns the arctangent of a number. + * @param x A numeric expression for which the arctangent is needed. + */ + atan(x: number): number; + /** + * Returns the angle (in radians) from the X axis to a point. + * @param y A numeric expression representing the cartesian y-coordinate. + * @param x A numeric expression representing the cartesian x-coordinate. + */ + atan2(y: number, x: number): number; + /** + * Returns the smallest number greater than or equal to its numeric argument. + * @param x A numeric expression. + */ + ceil(x: number): number; + /** + * Returns the cosine of a number. + * @param x A numeric expression that contains an angle measured in radians. + */ + cos(x: number): number; + /** + * Returns e (the base of natural logarithms) raised to a power. + * @param x A numeric expression representing the power of e. + */ + exp(x: number): number; + /** + * Returns the greatest number less than or equal to its numeric argument. + * @param x A numeric expression. + */ + floor(x: number): number; + /** + * Returns the natural logarithm (base e) of a number. + * @param x A numeric expression. + */ + log(x: number): number; + /** + * Returns the larger of a set of supplied numeric expressions. + * @param values Numeric expressions to be evaluated. + */ + max(...values: number[]): number; + /** + * Returns the smaller of a set of supplied numeric expressions. + * @param values Numeric expressions to be evaluated. + */ + min(...values: number[]): number; + /** + * Returns the value of a base expression taken to a specified power. + * @param x The base value of the expression. + * @param y The exponent value of the expression. + */ + pow(x: number, y: number): number; + /** Returns a pseudorandom number between 0 and 1. */ + random(): number; + /** + * Returns a supplied numeric expression rounded to the nearest number. + * @param x The value to be rounded to the nearest number. + */ + round(x: number): number; + /** + * Returns the sine of a number. + * @param x A numeric expression that contains an angle measured in radians. + */ + sin(x: number): number; + /** + * Returns the square root of a number. + * @param x A numeric expression. + */ + sqrt(x: number): number; + /** + * Returns the tangent of a number. + * @param x A numeric expression that contains an angle measured in radians. + */ + tan(x: number): number; +} +/** An intrinsic object that provides basic mathematics functionality and constants. */ +declare var Math: Math; + +/** Enables basic storage and retrieval of dates and times. */ +interface Date { + /** Returns a string representation of a date. The format of the string depends on the locale. */ + toString(): string; + /** Returns a date as a string value. */ + toDateString(): string; + /** Returns a time as a string value. */ + toTimeString(): string; + /** Returns a value as a string value appropriate to the host environment's current locale. */ + toLocaleString(): string; + /** Returns a date as a string value appropriate to the host environment's current locale. */ + toLocaleDateString(): string; + /** Returns a time as a string value appropriate to the host environment's current locale. */ + toLocaleTimeString(): string; + /** Returns the stored time value in milliseconds since midnight, January 1, 1970 UTC. */ + valueOf(): number; + /** Gets the time value in milliseconds. */ + getTime(): number; + /** Gets the year, using local time. */ + getFullYear(): number; + /** Gets the year using Universal Coordinated Time (UTC). */ + getUTCFullYear(): number; + /** Gets the month, using local time. */ + getMonth(): number; + /** Gets the month of a Date object using Universal Coordinated Time (UTC). */ + getUTCMonth(): number; + /** Gets the day-of-the-month, using local time. */ + getDate(): number; + /** Gets the day-of-the-month, using Universal Coordinated Time (UTC). */ + getUTCDate(): number; + /** Gets the day of the week, using local time. */ + getDay(): number; + /** Gets the day of the week using Universal Coordinated Time (UTC). */ + getUTCDay(): number; + /** Gets the hours in a date, using local time. */ + getHours(): number; + /** Gets the hours value in a Date object using Universal Coordinated Time (UTC). */ + getUTCHours(): number; + /** Gets the minutes of a Date object, using local time. */ + getMinutes(): number; + /** Gets the minutes of a Date object using Universal Coordinated Time (UTC). */ + getUTCMinutes(): number; + /** Gets the seconds of a Date object, using local time. */ + getSeconds(): number; + /** Gets the seconds of a Date object using Universal Coordinated Time (UTC). */ + getUTCSeconds(): number; + /** Gets the milliseconds of a Date, using local time. */ + getMilliseconds(): number; + /** Gets the milliseconds of a Date object using Universal Coordinated Time (UTC). */ + getUTCMilliseconds(): number; + /** Gets the difference in minutes between the time on the local computer and Universal Coordinated Time (UTC). */ + getTimezoneOffset(): number; + /** + * Sets the date and time value in the Date object. + * @param time A numeric value representing the number of elapsed milliseconds since midnight, January 1, 1970 GMT. + */ + setTime(time: number): number; + /** + * Sets the milliseconds value in the Date object using local time. + * @param ms A numeric value equal to the millisecond value. + */ + setMilliseconds(ms: number): number; + /** + * Sets the milliseconds value in the Date object using Universal Coordinated Time (UTC). + * @param ms A numeric value equal to the millisecond value. + */ + setUTCMilliseconds(ms: number): number; + + /** + * Sets the seconds value in the Date object using local time. + * @param sec A numeric value equal to the seconds value. + * @param ms A numeric value equal to the milliseconds value. + */ + setSeconds(sec: number, ms?: number): number; + /** + * Sets the seconds value in the Date object using Universal Coordinated Time (UTC). + * @param sec A numeric value equal to the seconds value. + * @param ms A numeric value equal to the milliseconds value. + */ + setUTCSeconds(sec: number, ms?: number): number; + /** + * Sets the minutes value in the Date object using local time. + * @param min A numeric value equal to the minutes value. + * @param sec A numeric value equal to the seconds value. + * @param ms A numeric value equal to the milliseconds value. + */ + setMinutes(min: number, sec?: number, ms?: number): number; + /** + * Sets the minutes value in the Date object using Universal Coordinated Time (UTC). + * @param min A numeric value equal to the minutes value. + * @param sec A numeric value equal to the seconds value. + * @param ms A numeric value equal to the milliseconds value. + */ + setUTCMinutes(min: number, sec?: number, ms?: number): number; + /** + * Sets the hour value in the Date object using local time. + * @param hours A numeric value equal to the hours value. + * @param min A numeric value equal to the minutes value. + * @param sec A numeric value equal to the seconds value. + * @param ms A numeric value equal to the milliseconds value. + */ + setHours(hours: number, min?: number, sec?: number, ms?: number): number; + /** + * Sets the hours value in the Date object using Universal Coordinated Time (UTC). + * @param hours A numeric value equal to the hours value. + * @param min A numeric value equal to the minutes value. + * @param sec A numeric value equal to the seconds value. + * @param ms A numeric value equal to the milliseconds value. + */ + setUTCHours(hours: number, min?: number, sec?: number, ms?: number): number; + /** + * Sets the numeric day-of-the-month value of the Date object using local time. + * @param date A numeric value equal to the day of the month. + */ + setDate(date: number): number; + /** + * Sets the numeric day of the month in the Date object using Universal Coordinated Time (UTC). + * @param date A numeric value equal to the day of the month. + */ + setUTCDate(date: number): number; + /** + * Sets the month value in the Date object using local time. + * @param month A numeric value equal to the month. The value for January is 0, and other month values follow consecutively. + * @param date A numeric value representing the day of the month. If this value is not supplied, the value from a call to the getDate method is used. + */ + setMonth(month: number, date?: number): number; + /** + * Sets the month value in the Date object using Universal Coordinated Time (UTC). + * @param month A numeric value equal to the month. The value for January is 0, and other month values follow consecutively. + * @param date A numeric value representing the day of the month. If it is not supplied, the value from a call to the getUTCDate method is used. + */ + setUTCMonth(month: number, date?: number): number; + /** + * Sets the year of the Date object using local time. + * @param year A numeric value for the year. + * @param month A zero-based numeric value for the month (0 for January, 11 for December). Must be specified if numDate is specified. + * @param date A numeric value equal for the day of the month. + */ + setFullYear(year: number, month?: number, date?: number): number; + /** + * Sets the year value in the Date object using Universal Coordinated Time (UTC). + * @param year A numeric value equal to the year. + * @param month A numeric value equal to the month. The value for January is 0, and other month values follow consecutively. Must be supplied if numDate is supplied. + * @param date A numeric value equal to the day of the month. + */ + setUTCFullYear(year: number, month?: number, date?: number): number; + /** Returns a date converted to a string using Universal Coordinated Time (UTC). */ + toUTCString(): string; + /** Returns a date as a string value in ISO format. */ + toISOString(): string; + /** Used by the JSON.stringify method to enable the transformation of an object's data for JavaScript Object Notation (JSON) serialization. */ + toJSON(key?: any): string; +} + +interface DateConstructor { + new (): Date; + new (value: number): Date; + new (value: string): Date; + new (year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date; + (): string; + prototype: Date; + /** + * Parses a string containing a date, and returns the number of milliseconds between that date and midnight, January 1, 1970. + * @param s A date string + */ + parse(s: string): number; + /** + * Returns the number of milliseconds between midnight, January 1, 1970 Universal Coordinated Time (UTC) (or GMT) and the specified date. + * @param year The full year designation is required for cross-century date accuracy. If year is between 0 and 99 is used, then year is assumed to be 1900 + year. + * @param month The month as an number between 0 and 11 (January to December). + * @param date The date as an number between 1 and 31. + * @param hours Must be supplied if minutes is supplied. An number from 0 to 23 (midnight to 11pm) that specifies the hour. + * @param minutes Must be supplied if seconds is supplied. An number from 0 to 59 that specifies the minutes. + * @param seconds Must be supplied if milliseconds is supplied. An number from 0 to 59 that specifies the seconds. + * @param ms An number from 0 to 999 that specifies the milliseconds. + */ + UTC(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): number; + now(): number; +} + +declare var Date: DateConstructor; + +interface RegExpMatchArray extends Array { + index?: number; + input?: string; +} + +interface RegExpExecArray extends Array { + index: number; + input: string; +} + +interface RegExp { + /** + * Executes a search on a string using a regular expression pattern, and returns an array containing the results of that search. + * @param string The String object or string literal on which to perform the search. + */ + exec(string: string): RegExpExecArray; + + /** + * Returns a Boolean value that indicates whether or not a pattern exists in a searched string. + * @param string String on which to perform the search. + */ + test(string: string): boolean; + + /** Returns a copy of the text of the regular expression pattern. Read-only. The regExp argument is a Regular expression object. It can be a variable name or a literal. */ + source: string; + + /** Returns a Boolean value indicating the state of the global flag (g) used with a regular expression. Default is false. Read-only. */ + global: boolean; + + /** Returns a Boolean value indicating the state of the ignoreCase flag (i) used with a regular expression. Default is false. Read-only. */ + ignoreCase: boolean; + + /** Returns a Boolean value indicating the state of the multiline flag (m) used with a regular expression. Default is false. Read-only. */ + multiline: boolean; + + lastIndex: number; + + // Non-standard extensions + compile(): RegExp; +} + +interface RegExpConstructor { + new (pattern: string, flags?: string): RegExp; + (pattern: string, flags?: string): RegExp; + prototype: RegExp; + + // Non-standard extensions + $1: string; + $2: string; + $3: string; + $4: string; + $5: string; + $6: string; + $7: string; + $8: string; + $9: string; + lastMatch: string; +} + +declare var RegExp: RegExpConstructor; + +interface Error { + name: string; + message: string; +} + +interface ErrorConstructor { + new (message?: string): Error; + (message?: string): Error; + prototype: Error; +} + +declare var Error: ErrorConstructor; + +interface EvalError extends Error { +} + +interface EvalErrorConstructor { + new (message?: string): EvalError; + (message?: string): EvalError; + prototype: EvalError; +} + +declare var EvalError: EvalErrorConstructor; + +interface RangeError extends Error { +} + +interface RangeErrorConstructor { + new (message?: string): RangeError; + (message?: string): RangeError; + prototype: RangeError; +} + +declare var RangeError: RangeErrorConstructor; + +interface ReferenceError extends Error { +} + +interface ReferenceErrorConstructor { + new (message?: string): ReferenceError; + (message?: string): ReferenceError; + prototype: ReferenceError; +} + +declare var ReferenceError: ReferenceErrorConstructor; + +interface SyntaxError extends Error { +} + +interface SyntaxErrorConstructor { + new (message?: string): SyntaxError; + (message?: string): SyntaxError; + prototype: SyntaxError; +} + +declare var SyntaxError: SyntaxErrorConstructor; + +interface TypeError extends Error { +} + +interface TypeErrorConstructor { + new (message?: string): TypeError; + (message?: string): TypeError; + prototype: TypeError; +} + +declare var TypeError: TypeErrorConstructor; + +interface URIError extends Error { +} + +interface URIErrorConstructor { + new (message?: string): URIError; + (message?: string): URIError; + prototype: URIError; +} + +declare var URIError: URIErrorConstructor; + +interface JSON { + /** + * Converts a JavaScript Object Notation (JSON) string into an object. + * @param text A valid JSON string. + * @param reviver A function that transforms the results. This function is called for each member of the object. + * If a member contains nested objects, the nested objects are transformed before the parent object is. + */ + parse(text: string, reviver?: (key: any, value: any) => any): any; + /** + * Converts a JavaScript value to a JavaScript Object Notation (JSON) string. + * @param value A JavaScript value, usually an object or array, to be converted. + */ + stringify(value: any): string; + /** + * Converts a JavaScript value to a JavaScript Object Notation (JSON) string. + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer A function that transforms the results. + */ + stringify(value: any, replacer: (key: string, value: any) => any): string; + /** + * Converts a JavaScript value to a JavaScript Object Notation (JSON) string. + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer Array that transforms the results. + */ + stringify(value: any, replacer: any[]): string; + /** + * Converts a JavaScript value to a JavaScript Object Notation (JSON) string. + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer A function that transforms the results. + * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + */ + stringify(value: any, replacer: (key: string, value: any) => any, space: string | number): string; + /** + * Converts a JavaScript value to a JavaScript Object Notation (JSON) string. + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer Array that transforms the results. + * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + */ + stringify(value: any, replacer: any[], space: string | number): string; +} +/** + * An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format. + */ +declare var JSON: JSON; + + +///////////////////////////// +/// ECMAScript Array API (specially handled by compiler) +///////////////////////////// + +interface Array { + /** + * Gets or sets the length of the array. This is a number one higher than the highest element defined in an array. + */ + length: number; + /** + * Returns a string representation of an array. + */ + toString(): string; + toLocaleString(): string; + /** + * Appends new elements to an array, and returns the new length of the array. + * @param items New elements of the Array. + */ + push(...items: T[]): number; + /** + * Removes the last element from an array and returns it. + */ + pop(): T; + /** + * Combines two or more arrays. + * @param items Additional items to add to the end of array1. + */ + concat(...items: U[]): T[]; + /** + * Combines two or more arrays. + * @param items Additional items to add to the end of array1. + */ + concat(...items: T[]): T[]; + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma. + */ + join(separator?: string): string; + /** + * Reverses the elements in an Array. + */ + reverse(): T[]; + /** + * Removes the first element from an array and returns it. + */ + shift(): T; + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(start?: number, end?: number): T[]; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(compareFn?: (a: T, b: T) => number): T[]; + + /** + * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. + * @param start The zero-based location in the array from which to start removing elements. + */ + splice(start: number): T[]; + + /** + * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. + * @param start The zero-based location in the array from which to start removing elements. + * @param deleteCount The number of elements to remove. + * @param items Elements to insert into the array in place of the deleted elements. + */ + splice(start: number, deleteCount: number, ...items: T[]): T[]; + + /** + * Inserts new elements at the start of an array. + * @param items Elements to insert at the start of the Array. + */ + unshift(...items: T[]): number; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0. + */ + indexOf(searchElement: T, fromIndex?: number): number; + + /** + * Returns the index of the last occurrence of a specified value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at the last index in the array. + */ + lastIndexOf(searchElement: T, fromIndex?: number): number; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls the callbackfn function for each element in array1 until the callbackfn returns false, or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the callbackfn function for each element in array1 until the callbackfn returns true, or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; + + /** + * Calls a defined callback function on each element of an array, and returns an array that contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + filter(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[]; + + /** + * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; + /** + * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + reduce(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; + /** + * Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + + [n: number]: T; +} + +interface ArrayConstructor { + new (arrayLength?: number): any[]; + new (arrayLength: number): T[]; + new (...items: T[]): T[]; + (arrayLength?: number): any[]; + (arrayLength: number): T[]; + (...items: T[]): T[]; + isArray(arg: any): arg is Array; + prototype: Array; +} + +declare var Array: ArrayConstructor; + +interface TypedPropertyDescriptor { + enumerable?: boolean; + configurable?: boolean; + writable?: boolean; + value?: T; + get?: () => T; + set?: (value: T) => void; +} + +declare type ClassDecorator = (target: TFunction) => TFunction | void; +declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void; +declare type MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => TypedPropertyDescriptor | void; +declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void; + +declare type PromiseConstructorLike = new (executor: (resolve: (value?: T | PromiseLike) => void, reject: (reason?: any) => void) => void) => PromiseLike; + +interface PromiseLike { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike; + then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => void): PromiseLike; +} + +interface ArrayLike { + length: number; + [n: number]: T; +} + + +/** + * Represents a raw buffer of binary data, which is used to store data for the + * different typed arrays. ArrayBuffers cannot be read from or written to directly, + * but can be passed to a typed array or DataView Object to interpret the raw + * buffer as needed. + */ +interface ArrayBuffer { + /** + * Read-only. The length of the ArrayBuffer (in bytes). + */ + byteLength: number; + + /** + * Returns a section of an ArrayBuffer. + */ + slice(begin:number, end?:number): ArrayBuffer; +} + +interface ArrayBufferConstructor { + prototype: ArrayBuffer; + new (byteLength: number): ArrayBuffer; + isView(arg: any): arg is ArrayBufferView; +} +declare var ArrayBuffer: ArrayBufferConstructor; + +interface ArrayBufferView { + /** + * The ArrayBuffer instance referenced by the array. + */ + buffer: ArrayBuffer; + + /** + * The length in bytes of the array. + */ + byteLength: number; + + /** + * The offset in bytes of the array. + */ + byteOffset: number; +} + +interface DataView { + buffer: ArrayBuffer; + byteLength: number; + byteOffset: number; + /** + * Gets the Float32 value at the specified byte offset from the start of the view. There is + * no alignment constraint; multi-byte values may be fetched from any offset. + * @param byteOffset The place in the buffer at which the value should be retrieved. + */ + getFloat32(byteOffset: number, littleEndian?: boolean): number; + + /** + * Gets the Float64 value at the specified byte offset from the start of the view. There is + * no alignment constraint; multi-byte values may be fetched from any offset. + * @param byteOffset The place in the buffer at which the value should be retrieved. + */ + getFloat64(byteOffset: number, littleEndian?: boolean): number; + + /** + * Gets the Int8 value at the specified byte offset from the start of the view. There is + * no alignment constraint; multi-byte values may be fetched from any offset. + * @param byteOffset The place in the buffer at which the value should be retrieved. + */ + getInt8(byteOffset: number): number; + + /** + * Gets the Int16 value at the specified byte offset from the start of the view. There is + * no alignment constraint; multi-byte values may be fetched from any offset. + * @param byteOffset The place in the buffer at which the value should be retrieved. + */ + getInt16(byteOffset: number, littleEndian?: boolean): number; + /** + * Gets the Int32 value at the specified byte offset from the start of the view. There is + * no alignment constraint; multi-byte values may be fetched from any offset. + * @param byteOffset The place in the buffer at which the value should be retrieved. + */ + getInt32(byteOffset: number, littleEndian?: boolean): number; + + /** + * Gets the Uint8 value at the specified byte offset from the start of the view. There is + * no alignment constraint; multi-byte values may be fetched from any offset. + * @param byteOffset The place in the buffer at which the value should be retrieved. + */ + getUint8(byteOffset: number): number; + + /** + * Gets the Uint16 value at the specified byte offset from the start of the view. There is + * no alignment constraint; multi-byte values may be fetched from any offset. + * @param byteOffset The place in the buffer at which the value should be retrieved. + */ + getUint16(byteOffset: number, littleEndian?: boolean): number; + + /** + * Gets the Uint32 value at the specified byte offset from the start of the view. There is + * no alignment constraint; multi-byte values may be fetched from any offset. + * @param byteOffset The place in the buffer at which the value should be retrieved. + */ + getUint32(byteOffset: number, littleEndian?: boolean): number; + + /** + * Stores an Float32 value at the specified byte offset from the start of the view. + * @param byteOffset The place in the buffer at which the value should be set. + * @param value The value to set. + * @param littleEndian If false or undefined, a big-endian value should be written, + * otherwise a little-endian value should be written. + */ + setFloat32(byteOffset: number, value: number, littleEndian?: boolean): void; + + /** + * Stores an Float64 value at the specified byte offset from the start of the view. + * @param byteOffset The place in the buffer at which the value should be set. + * @param value The value to set. + * @param littleEndian If false or undefined, a big-endian value should be written, + * otherwise a little-endian value should be written. + */ + setFloat64(byteOffset: number, value: number, littleEndian?: boolean): void; + + /** + * Stores an Int8 value at the specified byte offset from the start of the view. + * @param byteOffset The place in the buffer at which the value should be set. + * @param value The value to set. + */ + setInt8(byteOffset: number, value: number): void; + + /** + * Stores an Int16 value at the specified byte offset from the start of the view. + * @param byteOffset The place in the buffer at which the value should be set. + * @param value The value to set. + * @param littleEndian If false or undefined, a big-endian value should be written, + * otherwise a little-endian value should be written. + */ + setInt16(byteOffset: number, value: number, littleEndian?: boolean): void; + + /** + * Stores an Int32 value at the specified byte offset from the start of the view. + * @param byteOffset The place in the buffer at which the value should be set. + * @param value The value to set. + * @param littleEndian If false or undefined, a big-endian value should be written, + * otherwise a little-endian value should be written. + */ + setInt32(byteOffset: number, value: number, littleEndian?: boolean): void; + + /** + * Stores an Uint8 value at the specified byte offset from the start of the view. + * @param byteOffset The place in the buffer at which the value should be set. + * @param value The value to set. + */ + setUint8(byteOffset: number, value: number): void; + + /** + * Stores an Uint16 value at the specified byte offset from the start of the view. + * @param byteOffset The place in the buffer at which the value should be set. + * @param value The value to set. + * @param littleEndian If false or undefined, a big-endian value should be written, + * otherwise a little-endian value should be written. + */ + setUint16(byteOffset: number, value: number, littleEndian?: boolean): void; + + /** + * Stores an Uint32 value at the specified byte offset from the start of the view. + * @param byteOffset The place in the buffer at which the value should be set. + * @param value The value to set. + * @param littleEndian If false or undefined, a big-endian value should be written, + * otherwise a little-endian value should be written. + */ + setUint32(byteOffset: number, value: number, littleEndian?: boolean): void; +} + +interface DataViewConstructor { + new (buffer: ArrayBuffer, byteOffset?: number, byteLength?: number): DataView; +} +declare var DataView: DataViewConstructor; + +/** + * A typed array of 8-bit integer values. The contents are initialized to 0. If the requested + * number of bytes could not be allocated an exception is raised. + */ +interface Int8Array { + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * The ArrayBuffer instance referenced by the array. + */ + buffer: ArrayBuffer; + + /** + * The length in bytes of the array. + */ + byteLength: number; + + /** + * The offset in bytes of the array. + */ + byteOffset: number; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(target: number, start: number, end?: number): Int8Array; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls + * the callbackfn function for each element in array1 until the callbackfn returns false, + * or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + every(callbackfn: (value: number, index: number, array: Int8Array) => boolean, thisArg?: any): boolean; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: number, start?: number, end?: number): Int8Array; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls + * the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + filter(callbackfn: (value: number, index: number, array: Int8Array) => boolean, thisArg?: any): Int8Array; + + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: number, index: number, obj: Array) => boolean, thisArg?: any): number; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: number) => boolean, thisArg?: any): number; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + forEach(callbackfn: (value: number, index: number, array: Int8Array) => void, thisArg?: any): void; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + indexOf(searchElement: number, fromIndex?: number): number; + + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the + * resulting String. If omitted, the array elements are separated with a comma. + */ + join(separator?: string): string; + + /** + * Returns the index of the last occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + lastIndexOf(searchElement: number, fromIndex?: number): number; + + /** + * The length of the array. + */ + length: number; + + /** + * Calls a defined callback function on each element of an array, and returns an array that + * contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + map(callbackfn: (value: number, index: number, array: Int8Array) => number, thisArg?: any): Int8Array; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int8Array) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an + * argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int8Array) => U, initialValue: U): U; + + /** + * Reverses the elements in an Array. + */ + reverse(): Int8Array; + + /** + * Sets a value or an array of values. + * @param index The index of the location to set. + * @param value The value to set. + */ + set(index: number, value: number): void; + + /** + * Sets a value or an array of values. + * @param array A typed or untyped array of values to set. + * @param offset The index in the current array at which the values are to be written. + */ + set(array: ArrayLike, offset?: number): void; + + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(start?: number, end?: number): Int8Array; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the + * callbackfn function for each element in array1 until the callbackfn returns true, or until + * the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + some(callbackfn: (value: number, index: number, array: Int8Array) => boolean, thisArg?: any): boolean; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If + * omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(compareFn?: (a: number, b: number) => number): Int8Array; + + /** + * Gets a new Int8Array view of the ArrayBuffer store for this array, referencing the elements + * at begin, inclusive, up to end, exclusive. + * @param begin The index of the beginning of the array. + * @param end The index of the end of the array. + */ + subarray(begin: number, end?: number): Int8Array; + + /** + * Converts a number to a string by using the current locale. + */ + toLocaleString(): string; + + /** + * Returns a string representation of an array. + */ + toString(): string; + + [index: number]: number; +} +interface Int8ArrayConstructor { + prototype: Int8Array; + new (length: number): Int8Array; + new (array: ArrayLike): Int8Array; + new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Int8Array; + + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * Returns a new array from a set of elements. + * @param items A set of elements to include in the new array object. + */ + of(...items: number[]): Int8Array; + + /** + * Creates an array from an array-like or iterable object. + * @param arrayLike An array-like or iterable object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(arrayLike: ArrayLike, mapfn?: (v: number, k: number) => number, thisArg?: any): Int8Array; + +} +declare var Int8Array: Int8ArrayConstructor; + +/** + * A typed array of 8-bit unsigned integer values. The contents are initialized to 0. If the + * requested number of bytes could not be allocated an exception is raised. + */ +interface Uint8Array { + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * The ArrayBuffer instance referenced by the array. + */ + buffer: ArrayBuffer; + + /** + * The length in bytes of the array. + */ + byteLength: number; + + /** + * The offset in bytes of the array. + */ + byteOffset: number; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(target: number, start: number, end?: number): Uint8Array; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls + * the callbackfn function for each element in array1 until the callbackfn returns false, + * or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + every(callbackfn: (value: number, index: number, array: Uint8Array) => boolean, thisArg?: any): boolean; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: number, start?: number, end?: number): Uint8Array; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls + * the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + filter(callbackfn: (value: number, index: number, array: Uint8Array) => boolean, thisArg?: any): Uint8Array; + + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: number, index: number, obj: Array) => boolean, thisArg?: any): number; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: number) => boolean, thisArg?: any): number; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + forEach(callbackfn: (value: number, index: number, array: Uint8Array) => void, thisArg?: any): void; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + indexOf(searchElement: number, fromIndex?: number): number; + + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the + * resulting String. If omitted, the array elements are separated with a comma. + */ + join(separator?: string): string; + + /** + * Returns the index of the last occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + lastIndexOf(searchElement: number, fromIndex?: number): number; + + /** + * The length of the array. + */ + length: number; + + /** + * Calls a defined callback function on each element of an array, and returns an array that + * contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + map(callbackfn: (value: number, index: number, array: Uint8Array) => number, thisArg?: any): Uint8Array; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8Array) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an + * argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8Array) => U, initialValue: U): U; + + /** + * Reverses the elements in an Array. + */ + reverse(): Uint8Array; + + /** + * Sets a value or an array of values. + * @param index The index of the location to set. + * @param value The value to set. + */ + set(index: number, value: number): void; + + /** + * Sets a value or an array of values. + * @param array A typed or untyped array of values to set. + * @param offset The index in the current array at which the values are to be written. + */ + set(array: ArrayLike, offset?: number): void; + + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(start?: number, end?: number): Uint8Array; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the + * callbackfn function for each element in array1 until the callbackfn returns true, or until + * the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + some(callbackfn: (value: number, index: number, array: Uint8Array) => boolean, thisArg?: any): boolean; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If + * omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(compareFn?: (a: number, b: number) => number): Uint8Array; + + /** + * Gets a new Uint8Array view of the ArrayBuffer store for this array, referencing the elements + * at begin, inclusive, up to end, exclusive. + * @param begin The index of the beginning of the array. + * @param end The index of the end of the array. + */ + subarray(begin: number, end?: number): Uint8Array; + + /** + * Converts a number to a string by using the current locale. + */ + toLocaleString(): string; + + /** + * Returns a string representation of an array. + */ + toString(): string; + + [index: number]: number; +} + +interface Uint8ArrayConstructor { + prototype: Uint8Array; + new (length: number): Uint8Array; + new (array: ArrayLike): Uint8Array; + new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint8Array; + + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * Returns a new array from a set of elements. + * @param items A set of elements to include in the new array object. + */ + of(...items: number[]): Uint8Array; + + /** + * Creates an array from an array-like or iterable object. + * @param arrayLike An array-like or iterable object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(arrayLike: ArrayLike, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint8Array; + +} +declare var Uint8Array: Uint8ArrayConstructor; + +/** + * A typed array of 8-bit unsigned integer (clamped) values. The contents are initialized to 0. + * If the requested number of bytes could not be allocated an exception is raised. + */ +interface Uint8ClampedArray { + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * The ArrayBuffer instance referenced by the array. + */ + buffer: ArrayBuffer; + + /** + * The length in bytes of the array. + */ + byteLength: number; + + /** + * The offset in bytes of the array. + */ + byteOffset: number; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(target: number, start: number, end?: number): Uint8ClampedArray; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls + * the callbackfn function for each element in array1 until the callbackfn returns false, + * or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + every(callbackfn: (value: number, index: number, array: Uint8ClampedArray) => boolean, thisArg?: any): boolean; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: number, start?: number, end?: number): Uint8ClampedArray; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls + * the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + filter(callbackfn: (value: number, index: number, array: Uint8ClampedArray) => boolean, thisArg?: any): Uint8ClampedArray; + + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: number, index: number, obj: Array) => boolean, thisArg?: any): number; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: number) => boolean, thisArg?: any): number; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + forEach(callbackfn: (value: number, index: number, array: Uint8ClampedArray) => void, thisArg?: any): void; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + indexOf(searchElement: number, fromIndex?: number): number; + + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the + * resulting String. If omitted, the array elements are separated with a comma. + */ + join(separator?: string): string; + + /** + * Returns the index of the last occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + lastIndexOf(searchElement: number, fromIndex?: number): number; + + /** + * The length of the array. + */ + length: number; + + /** + * Calls a defined callback function on each element of an array, and returns an array that + * contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + map(callbackfn: (value: number, index: number, array: Uint8ClampedArray) => number, thisArg?: any): Uint8ClampedArray; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an + * argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => U, initialValue: U): U; + + /** + * Reverses the elements in an Array. + */ + reverse(): Uint8ClampedArray; + + /** + * Sets a value or an array of values. + * @param index The index of the location to set. + * @param value The value to set. + */ + set(index: number, value: number): void; + + /** + * Sets a value or an array of values. + * @param array A typed or untyped array of values to set. + * @param offset The index in the current array at which the values are to be written. + */ + set(array: Uint8ClampedArray, offset?: number): void; + + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(start?: number, end?: number): Uint8ClampedArray; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the + * callbackfn function for each element in array1 until the callbackfn returns true, or until + * the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + some(callbackfn: (value: number, index: number, array: Uint8ClampedArray) => boolean, thisArg?: any): boolean; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If + * omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(compareFn?: (a: number, b: number) => number): Uint8ClampedArray; + + /** + * Gets a new Uint8ClampedArray view of the ArrayBuffer store for this array, referencing the elements + * at begin, inclusive, up to end, exclusive. + * @param begin The index of the beginning of the array. + * @param end The index of the end of the array. + */ + subarray(begin: number, end?: number): Uint8ClampedArray; + + /** + * Converts a number to a string by using the current locale. + */ + toLocaleString(): string; + + /** + * Returns a string representation of an array. + */ + toString(): string; + + [index: number]: number; +} + +interface Uint8ClampedArrayConstructor { + prototype: Uint8ClampedArray; + new (length: number): Uint8ClampedArray; + new (array: ArrayLike): Uint8ClampedArray; + new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint8ClampedArray; + + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * Returns a new array from a set of elements. + * @param items A set of elements to include in the new array object. + */ + of(...items: number[]): Uint8ClampedArray; + + /** + * Creates an array from an array-like or iterable object. + * @param arrayLike An array-like or iterable object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(arrayLike: ArrayLike, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint8ClampedArray; +} +declare var Uint8ClampedArray: Uint8ClampedArrayConstructor; + +/** + * A typed array of 16-bit signed integer values. The contents are initialized to 0. If the + * requested number of bytes could not be allocated an exception is raised. + */ +interface Int16Array { + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * The ArrayBuffer instance referenced by the array. + */ + buffer: ArrayBuffer; + + /** + * The length in bytes of the array. + */ + byteLength: number; + + /** + * The offset in bytes of the array. + */ + byteOffset: number; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(target: number, start: number, end?: number): Int16Array; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls + * the callbackfn function for each element in array1 until the callbackfn returns false, + * or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + every(callbackfn: (value: number, index: number, array: Int16Array) => boolean, thisArg?: any): boolean; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: number, start?: number, end?: number): Int16Array; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls + * the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + filter(callbackfn: (value: number, index: number, array: Int16Array) => boolean, thisArg?: any): Int16Array; + + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: number, index: number, obj: Array) => boolean, thisArg?: any): number; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: number) => boolean, thisArg?: any): number; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + forEach(callbackfn: (value: number, index: number, array: Int16Array) => void, thisArg?: any): void; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + indexOf(searchElement: number, fromIndex?: number): number; + + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the + * resulting String. If omitted, the array elements are separated with a comma. + */ + join(separator?: string): string; + + /** + * Returns the index of the last occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + lastIndexOf(searchElement: number, fromIndex?: number): number; + + /** + * The length of the array. + */ + length: number; + + /** + * Calls a defined callback function on each element of an array, and returns an array that + * contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + map(callbackfn: (value: number, index: number, array: Int16Array) => number, thisArg?: any): Int16Array; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int16Array) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an + * argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int16Array) => U, initialValue: U): U; + + /** + * Reverses the elements in an Array. + */ + reverse(): Int16Array; + + /** + * Sets a value or an array of values. + * @param index The index of the location to set. + * @param value The value to set. + */ + set(index: number, value: number): void; + + /** + * Sets a value or an array of values. + * @param array A typed or untyped array of values to set. + * @param offset The index in the current array at which the values are to be written. + */ + set(array: ArrayLike, offset?: number): void; + + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(start?: number, end?: number): Int16Array; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the + * callbackfn function for each element in array1 until the callbackfn returns true, or until + * the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + some(callbackfn: (value: number, index: number, array: Int16Array) => boolean, thisArg?: any): boolean; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If + * omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(compareFn?: (a: number, b: number) => number): Int16Array; + + /** + * Gets a new Int16Array view of the ArrayBuffer store for this array, referencing the elements + * at begin, inclusive, up to end, exclusive. + * @param begin The index of the beginning of the array. + * @param end The index of the end of the array. + */ + subarray(begin: number, end?: number): Int16Array; + + /** + * Converts a number to a string by using the current locale. + */ + toLocaleString(): string; + + /** + * Returns a string representation of an array. + */ + toString(): string; + + [index: number]: number; +} + +interface Int16ArrayConstructor { + prototype: Int16Array; + new (length: number): Int16Array; + new (array: ArrayLike): Int16Array; + new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Int16Array; + + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * Returns a new array from a set of elements. + * @param items A set of elements to include in the new array object. + */ + of(...items: number[]): Int16Array; + + /** + * Creates an array from an array-like or iterable object. + * @param arrayLike An array-like or iterable object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(arrayLike: ArrayLike, mapfn?: (v: number, k: number) => number, thisArg?: any): Int16Array; + +} +declare var Int16Array: Int16ArrayConstructor; + +/** + * A typed array of 16-bit unsigned integer values. The contents are initialized to 0. If the + * requested number of bytes could not be allocated an exception is raised. + */ +interface Uint16Array { + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * The ArrayBuffer instance referenced by the array. + */ + buffer: ArrayBuffer; + + /** + * The length in bytes of the array. + */ + byteLength: number; + + /** + * The offset in bytes of the array. + */ + byteOffset: number; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(target: number, start: number, end?: number): Uint16Array; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls + * the callbackfn function for each element in array1 until the callbackfn returns false, + * or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + every(callbackfn: (value: number, index: number, array: Uint16Array) => boolean, thisArg?: any): boolean; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: number, start?: number, end?: number): Uint16Array; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls + * the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + filter(callbackfn: (value: number, index: number, array: Uint16Array) => boolean, thisArg?: any): Uint16Array; + + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: number, index: number, obj: Array) => boolean, thisArg?: any): number; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: number) => boolean, thisArg?: any): number; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + forEach(callbackfn: (value: number, index: number, array: Uint16Array) => void, thisArg?: any): void; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + indexOf(searchElement: number, fromIndex?: number): number; + + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the + * resulting String. If omitted, the array elements are separated with a comma. + */ + join(separator?: string): string; + + /** + * Returns the index of the last occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + lastIndexOf(searchElement: number, fromIndex?: number): number; + + /** + * The length of the array. + */ + length: number; + + /** + * Calls a defined callback function on each element of an array, and returns an array that + * contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + map(callbackfn: (value: number, index: number, array: Uint16Array) => number, thisArg?: any): Uint16Array; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint16Array) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an + * argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint16Array) => U, initialValue: U): U; + + /** + * Reverses the elements in an Array. + */ + reverse(): Uint16Array; + + /** + * Sets a value or an array of values. + * @param index The index of the location to set. + * @param value The value to set. + */ + set(index: number, value: number): void; + + /** + * Sets a value or an array of values. + * @param array A typed or untyped array of values to set. + * @param offset The index in the current array at which the values are to be written. + */ + set(array: ArrayLike, offset?: number): void; + + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(start?: number, end?: number): Uint16Array; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the + * callbackfn function for each element in array1 until the callbackfn returns true, or until + * the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + some(callbackfn: (value: number, index: number, array: Uint16Array) => boolean, thisArg?: any): boolean; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If + * omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(compareFn?: (a: number, b: number) => number): Uint16Array; + + /** + * Gets a new Uint16Array view of the ArrayBuffer store for this array, referencing the elements + * at begin, inclusive, up to end, exclusive. + * @param begin The index of the beginning of the array. + * @param end The index of the end of the array. + */ + subarray(begin: number, end?: number): Uint16Array; + + /** + * Converts a number to a string by using the current locale. + */ + toLocaleString(): string; + + /** + * Returns a string representation of an array. + */ + toString(): string; + + [index: number]: number; +} + +interface Uint16ArrayConstructor { + prototype: Uint16Array; + new (length: number): Uint16Array; + new (array: ArrayLike): Uint16Array; + new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint16Array; + + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * Returns a new array from a set of elements. + * @param items A set of elements to include in the new array object. + */ + of(...items: number[]): Uint16Array; + + /** + * Creates an array from an array-like or iterable object. + * @param arrayLike An array-like or iterable object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(arrayLike: ArrayLike, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint16Array; + +} +declare var Uint16Array: Uint16ArrayConstructor; +/** + * A typed array of 32-bit signed integer values. The contents are initialized to 0. If the + * requested number of bytes could not be allocated an exception is raised. + */ +interface Int32Array { + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * The ArrayBuffer instance referenced by the array. + */ + buffer: ArrayBuffer; + + /** + * The length in bytes of the array. + */ + byteLength: number; + + /** + * The offset in bytes of the array. + */ + byteOffset: number; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(target: number, start: number, end?: number): Int32Array; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls + * the callbackfn function for each element in array1 until the callbackfn returns false, + * or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + every(callbackfn: (value: number, index: number, array: Int32Array) => boolean, thisArg?: any): boolean; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: number, start?: number, end?: number): Int32Array; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls + * the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + filter(callbackfn: (value: number, index: number, array: Int32Array) => boolean, thisArg?: any): Int32Array; + + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: number, index: number, obj: Array) => boolean, thisArg?: any): number; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: number) => boolean, thisArg?: any): number; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + forEach(callbackfn: (value: number, index: number, array: Int32Array) => void, thisArg?: any): void; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + indexOf(searchElement: number, fromIndex?: number): number; + + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the + * resulting String. If omitted, the array elements are separated with a comma. + */ + join(separator?: string): string; + + /** + * Returns the index of the last occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + lastIndexOf(searchElement: number, fromIndex?: number): number; + + /** + * The length of the array. + */ + length: number; + + /** + * Calls a defined callback function on each element of an array, and returns an array that + * contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + map(callbackfn: (value: number, index: number, array: Int32Array) => number, thisArg?: any): Int32Array; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int32Array) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an + * argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int32Array) => U, initialValue: U): U; + + /** + * Reverses the elements in an Array. + */ + reverse(): Int32Array; + + /** + * Sets a value or an array of values. + * @param index The index of the location to set. + * @param value The value to set. + */ + set(index: number, value: number): void; + + /** + * Sets a value or an array of values. + * @param array A typed or untyped array of values to set. + * @param offset The index in the current array at which the values are to be written. + */ + set(array: ArrayLike, offset?: number): void; + + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(start?: number, end?: number): Int32Array; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the + * callbackfn function for each element in array1 until the callbackfn returns true, or until + * the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + some(callbackfn: (value: number, index: number, array: Int32Array) => boolean, thisArg?: any): boolean; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If + * omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(compareFn?: (a: number, b: number) => number): Int32Array; + + /** + * Gets a new Int32Array view of the ArrayBuffer store for this array, referencing the elements + * at begin, inclusive, up to end, exclusive. + * @param begin The index of the beginning of the array. + * @param end The index of the end of the array. + */ + subarray(begin: number, end?: number): Int32Array; + + /** + * Converts a number to a string by using the current locale. + */ + toLocaleString(): string; + + /** + * Returns a string representation of an array. + */ + toString(): string; + + [index: number]: number; +} + +interface Int32ArrayConstructor { + prototype: Int32Array; + new (length: number): Int32Array; + new (array: ArrayLike): Int32Array; + new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Int32Array; + + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * Returns a new array from a set of elements. + * @param items A set of elements to include in the new array object. + */ + of(...items: number[]): Int32Array; + + /** + * Creates an array from an array-like or iterable object. + * @param arrayLike An array-like or iterable object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(arrayLike: ArrayLike, mapfn?: (v: number, k: number) => number, thisArg?: any): Int32Array; +} +declare var Int32Array: Int32ArrayConstructor; + +/** + * A typed array of 32-bit unsigned integer values. The contents are initialized to 0. If the + * requested number of bytes could not be allocated an exception is raised. + */ +interface Uint32Array { + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * The ArrayBuffer instance referenced by the array. + */ + buffer: ArrayBuffer; + + /** + * The length in bytes of the array. + */ + byteLength: number; + + /** + * The offset in bytes of the array. + */ + byteOffset: number; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(target: number, start: number, end?: number): Uint32Array; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls + * the callbackfn function for each element in array1 until the callbackfn returns false, + * or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + every(callbackfn: (value: number, index: number, array: Uint32Array) => boolean, thisArg?: any): boolean; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: number, start?: number, end?: number): Uint32Array; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls + * the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + filter(callbackfn: (value: number, index: number, array: Uint32Array) => boolean, thisArg?: any): Uint32Array; + + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: number, index: number, obj: Array) => boolean, thisArg?: any): number; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: number) => boolean, thisArg?: any): number; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + forEach(callbackfn: (value: number, index: number, array: Uint32Array) => void, thisArg?: any): void; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + indexOf(searchElement: number, fromIndex?: number): number; + + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the + * resulting String. If omitted, the array elements are separated with a comma. + */ + join(separator?: string): string; + + /** + * Returns the index of the last occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + lastIndexOf(searchElement: number, fromIndex?: number): number; + + /** + * The length of the array. + */ + length: number; + + /** + * Calls a defined callback function on each element of an array, and returns an array that + * contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + map(callbackfn: (value: number, index: number, array: Uint32Array) => number, thisArg?: any): Uint32Array; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint32Array) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an + * argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint32Array) => U, initialValue: U): U; + + /** + * Reverses the elements in an Array. + */ + reverse(): Uint32Array; + + /** + * Sets a value or an array of values. + * @param index The index of the location to set. + * @param value The value to set. + */ + set(index: number, value: number): void; + + /** + * Sets a value or an array of values. + * @param array A typed or untyped array of values to set. + * @param offset The index in the current array at which the values are to be written. + */ + set(array: ArrayLike, offset?: number): void; + + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(start?: number, end?: number): Uint32Array; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the + * callbackfn function for each element in array1 until the callbackfn returns true, or until + * the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + some(callbackfn: (value: number, index: number, array: Uint32Array) => boolean, thisArg?: any): boolean; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If + * omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(compareFn?: (a: number, b: number) => number): Uint32Array; + + /** + * Gets a new Uint32Array view of the ArrayBuffer store for this array, referencing the elements + * at begin, inclusive, up to end, exclusive. + * @param begin The index of the beginning of the array. + * @param end The index of the end of the array. + */ + subarray(begin: number, end?: number): Uint32Array; + + /** + * Converts a number to a string by using the current locale. + */ + toLocaleString(): string; + + /** + * Returns a string representation of an array. + */ + toString(): string; + + [index: number]: number; +} + +interface Uint32ArrayConstructor { + prototype: Uint32Array; + new (length: number): Uint32Array; + new (array: ArrayLike): Uint32Array; + new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint32Array; + + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * Returns a new array from a set of elements. + * @param items A set of elements to include in the new array object. + */ + of(...items: number[]): Uint32Array; + + /** + * Creates an array from an array-like or iterable object. + * @param arrayLike An array-like or iterable object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(arrayLike: ArrayLike, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint32Array; +} +declare var Uint32Array: Uint32ArrayConstructor; + +/** + * A typed array of 32-bit float values. The contents are initialized to 0. If the requested number + * of bytes could not be allocated an exception is raised. + */ +interface Float32Array { + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * The ArrayBuffer instance referenced by the array. + */ + buffer: ArrayBuffer; + + /** + * The length in bytes of the array. + */ + byteLength: number; + + /** + * The offset in bytes of the array. + */ + byteOffset: number; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(target: number, start: number, end?: number): Float32Array; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls + * the callbackfn function for each element in array1 until the callbackfn returns false, + * or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + every(callbackfn: (value: number, index: number, array: Float32Array) => boolean, thisArg?: any): boolean; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: number, start?: number, end?: number): Float32Array; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls + * the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + filter(callbackfn: (value: number, index: number, array: Float32Array) => boolean, thisArg?: any): Float32Array; + + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: number, index: number, obj: Array) => boolean, thisArg?: any): number; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: number) => boolean, thisArg?: any): number; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + forEach(callbackfn: (value: number, index: number, array: Float32Array) => void, thisArg?: any): void; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + indexOf(searchElement: number, fromIndex?: number): number; + + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the + * resulting String. If omitted, the array elements are separated with a comma. + */ + join(separator?: string): string; + + /** + * Returns the index of the last occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + lastIndexOf(searchElement: number, fromIndex?: number): number; + + /** + * The length of the array. + */ + length: number; + + /** + * Calls a defined callback function on each element of an array, and returns an array that + * contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + map(callbackfn: (value: number, index: number, array: Float32Array) => number, thisArg?: any): Float32Array; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float32Array) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an + * argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float32Array) => U, initialValue: U): U; + + /** + * Reverses the elements in an Array. + */ + reverse(): Float32Array; + + /** + * Sets a value or an array of values. + * @param index The index of the location to set. + * @param value The value to set. + */ + set(index: number, value: number): void; + + /** + * Sets a value or an array of values. + * @param array A typed or untyped array of values to set. + * @param offset The index in the current array at which the values are to be written. + */ + set(array: ArrayLike, offset?: number): void; + + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(start?: number, end?: number): Float32Array; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the + * callbackfn function for each element in array1 until the callbackfn returns true, or until + * the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + some(callbackfn: (value: number, index: number, array: Float32Array) => boolean, thisArg?: any): boolean; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If + * omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(compareFn?: (a: number, b: number) => number): Float32Array; + + /** + * Gets a new Float32Array view of the ArrayBuffer store for this array, referencing the elements + * at begin, inclusive, up to end, exclusive. + * @param begin The index of the beginning of the array. + * @param end The index of the end of the array. + */ + subarray(begin: number, end?: number): Float32Array; + + /** + * Converts a number to a string by using the current locale. + */ + toLocaleString(): string; + + /** + * Returns a string representation of an array. + */ + toString(): string; + + [index: number]: number; +} + +interface Float32ArrayConstructor { + prototype: Float32Array; + new (length: number): Float32Array; + new (array: ArrayLike): Float32Array; + new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Float32Array; + + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * Returns a new array from a set of elements. + * @param items A set of elements to include in the new array object. + */ + of(...items: number[]): Float32Array; + + /** + * Creates an array from an array-like or iterable object. + * @param arrayLike An array-like or iterable object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(arrayLike: ArrayLike, mapfn?: (v: number, k: number) => number, thisArg?: any): Float32Array; + +} +declare var Float32Array: Float32ArrayConstructor; + +/** + * A typed array of 64-bit float values. The contents are initialized to 0. If the requested + * number of bytes could not be allocated an exception is raised. + */ +interface Float64Array { + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * The ArrayBuffer instance referenced by the array. + */ + buffer: ArrayBuffer; + + /** + * The length in bytes of the array. + */ + byteLength: number; + + /** + * The offset in bytes of the array. + */ + byteOffset: number; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(target: number, start: number, end?: number): Float64Array; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls + * the callbackfn function for each element in array1 until the callbackfn returns false, + * or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + every(callbackfn: (value: number, index: number, array: Float64Array) => boolean, thisArg?: any): boolean; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: number, start?: number, end?: number): Float64Array; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls + * the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + filter(callbackfn: (value: number, index: number, array: Float64Array) => boolean, thisArg?: any): Float64Array; + + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: number, index: number, obj: Array) => boolean, thisArg?: any): number; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: number) => boolean, thisArg?: any): number; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + forEach(callbackfn: (value: number, index: number, array: Float64Array) => void, thisArg?: any): void; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + indexOf(searchElement: number, fromIndex?: number): number; + + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the + * resulting String. If omitted, the array elements are separated with a comma. + */ + join(separator?: string): string; + + /** + * Returns the index of the last occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the + * search starts at index 0. + */ + lastIndexOf(searchElement: number, fromIndex?: number): number; + + /** + * The length of the array. + */ + length: number; + + /** + * Calls a defined callback function on each element of an array, and returns an array that + * contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the + * callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + map(callbackfn: (value: number, index: number, array: Float64Array) => number, thisArg?: any): Float64Array; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array. The return value of + * the callback function is the accumulated result, and is provided as an argument in the next + * call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the + * callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float64Array) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an + * argument instead of an array value. + */ + reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number, initialValue?: number): number; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. + * The return value of the callback function is the accumulated result, and is provided as an + * argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls + * the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start + * the accumulation. The first call to the callbackfn function provides this value as an argument + * instead of an array value. + */ + reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float64Array) => U, initialValue: U): U; + + /** + * Reverses the elements in an Array. + */ + reverse(): Float64Array; + + /** + * Sets a value or an array of values. + * @param index The index of the location to set. + * @param value The value to set. + */ + set(index: number, value: number): void; + + /** + * Sets a value or an array of values. + * @param array A typed or untyped array of values to set. + * @param offset The index in the current array at which the values are to be written. + */ + set(array: ArrayLike, offset?: number): void; + + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(start?: number, end?: number): Float64Array; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the + * callbackfn function for each element in array1 until the callbackfn returns true, or until + * the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. + * If thisArg is omitted, undefined is used as the this value. + */ + some(callbackfn: (value: number, index: number, array: Float64Array) => boolean, thisArg?: any): boolean; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If + * omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(compareFn?: (a: number, b: number) => number): Float64Array; + + /** + * Gets a new Float64Array view of the ArrayBuffer store for this array, referencing the elements + * at begin, inclusive, up to end, exclusive. + * @param begin The index of the beginning of the array. + * @param end The index of the end of the array. + */ + subarray(begin: number, end?: number): Float64Array; + + /** + * Converts a number to a string by using the current locale. + */ + toLocaleString(): string; + + /** + * Returns a string representation of an array. + */ + toString(): string; + + [index: number]: number; +} + +interface Float64ArrayConstructor { + prototype: Float64Array; + new (length: number): Float64Array; + new (array: ArrayLike): Float64Array; + new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Float64Array; + + /** + * The size in bytes of each element in the array. + */ + BYTES_PER_ELEMENT: number; + + /** + * Returns a new array from a set of elements. + * @param items A set of elements to include in the new array object. + */ + of(...items: number[]): Float64Array; + + /** + * Creates an array from an array-like or iterable object. + * @param arrayLike An array-like or iterable object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(arrayLike: ArrayLike, mapfn?: (v: number, k: number) => number, thisArg?: any): Float64Array; +} +declare var Float64Array: Float64ArrayConstructor; diff --git a/extensions/markdown/.vscodeignore b/extensions/markdown/.vscodeignore new file mode 100644 index 0000000000..24428a6f75 --- /dev/null +++ b/extensions/markdown/.vscodeignore @@ -0,0 +1,4 @@ +test/** +src/** +tsconfig.json +npm-shrinkwrap.json \ No newline at end of file diff --git a/extensions/markdown/OSSREADME.json b/extensions/markdown/OSSREADME.json new file mode 100644 index 0000000000..79c18ea56c --- /dev/null +++ b/extensions/markdown/OSSREADME.json @@ -0,0 +1,89 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[ +{ + "name": "chriskempson/tomorrow-theme", + "version": "0.0.0", + "license": "MIT", + "repositoryURL": "https://github.com/chriskempson/tomorrow-theme", + "licenseDetail": [ + "Copyright (C) 2013 Chris Kempson", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,", "and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] +}, +{ + "isLicense": true, + "name": "markdown-it-named-headers", + "licenseDetail": [ + "Copyright (c) markdown-it-named-headers authors", + "", + "This is free and unencumbered software released into the public domain.", + "", + "Anyone is free to copy, modify, publish, use, compile, sell, or", + "distribute this software, either in source code form or as a compiled", + "binary, for any purpose, commercial or non-commercial, and by any", + "means.", + "", + "In jurisdictions that recognize copyright laws, the author or authors", + "of this software dedicate any and all copyright interest in the", + "software to the public domain. We make this dedication for the benefit", + "of the public at large and to the detriment of our heirs and", + "successors. We intend this dedication to be an overt act of", + "relinquishment in perpetuity of all present and future rights to this", + "software under copyright law.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", + "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", + "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.", + "IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR", + "OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,", + "ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR", + "OTHER DEALINGS IN THE SOFTWARE.", + "", + "For more information, please refer to " + ] +}, +{ + "name": "textmate/markdown.tmbundle", + "version": "0.0.0", + "license": "TextMate Bundle License", + "repositoryURL": "https://github.com/textmate/markdown.tmbundle", + "licenseDetail": [ + "Copyright (c) markdown.tmbundle authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ] +}, +{ + "isLicense": true, + "name": "uc.micro", + "licenseDetail": [ + " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE", + " Version 2, December 2004", + "", + " Copyright (C) 2004 Sam Hocevar ", + "", + " Everyone is permitted to copy and distribute verbatim or modified", + " copies of this license document, and changing it is allowed as long", + " as the name is changed.", + "", + " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE", + " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION", + "", + " 0. You just DO WHAT THE FUCK YOU WANT TO." + ] +}] diff --git a/extensions/markdown/language-configuration.json b/extensions/markdown/language-configuration.json new file mode 100644 index 0000000000..6c811c66aa --- /dev/null +++ b/extensions/markdown/language-configuration.json @@ -0,0 +1,41 @@ +{ + "comments": { + // symbols used for start and end a block comment. Remove this entry if your language does not support block comments + "blockComment": [ + "" + ] + }, + // symbols used as brackets + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + { + "open": "{", + "close": "}" + }, + { + "open": "[", + "close": "]" + }, + { + "open": "(", + "close": ")" + }, + { + "open": "<", + "close": ">", + "notIn": [ + "string" + ] + } + ], + "surroundingPairs": [ + ["(", ")"], + ["[", "]"], + ["`", "`"] + ] +} \ No newline at end of file diff --git a/extensions/markdown/media/Preview.svg b/extensions/markdown/media/Preview.svg new file mode 100644 index 0000000000..c60599eda4 --- /dev/null +++ b/extensions/markdown/media/Preview.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/markdown/media/PreviewOnRightPane_16x.svg b/extensions/markdown/media/PreviewOnRightPane_16x.svg new file mode 100644 index 0000000000..eefaadfcc7 --- /dev/null +++ b/extensions/markdown/media/PreviewOnRightPane_16x.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/extensions/markdown/media/PreviewOnRightPane_16x_dark.svg b/extensions/markdown/media/PreviewOnRightPane_16x_dark.svg new file mode 100644 index 0000000000..c18634bbaf --- /dev/null +++ b/extensions/markdown/media/PreviewOnRightPane_16x_dark.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/extensions/markdown/media/Preview_inverse.svg b/extensions/markdown/media/Preview_inverse.svg new file mode 100644 index 0000000000..87700309c0 --- /dev/null +++ b/extensions/markdown/media/Preview_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/markdown/media/ViewSource.svg b/extensions/markdown/media/ViewSource.svg new file mode 100644 index 0000000000..fccdf83d46 --- /dev/null +++ b/extensions/markdown/media/ViewSource.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/extensions/markdown/media/ViewSource_inverse.svg b/extensions/markdown/media/ViewSource_inverse.svg new file mode 100644 index 0000000000..f6302185aa --- /dev/null +++ b/extensions/markdown/media/ViewSource_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/markdown/media/csp.js b/extensions/markdown/media/csp.js new file mode 100644 index 0000000000..2a5d6061f4 --- /dev/null +++ b/extensions/markdown/media/csp.js @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +(function () { + const settings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-settings')); + const strings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-strings')); + + let didShow = false; + + const showCspWarning = () => { + if (didShow) { + return; + } + didShow = true; + const args = [settings.previewUri]; + + const notification = document.createElement('a'); + notification.innerText = strings.cspAlertMessageText; + notification.setAttribute('id', 'code-csp-warning'); + notification.setAttribute('title', strings.cspAlertMessageTitle); + + notification.setAttribute('role', 'button'); + notification.setAttribute('aria-label', strings.cspAlertMessageLabel); + notification.setAttribute('href', `command:markdown.showPreviewSecuritySelector?${encodeURIComponent(JSON.stringify(args))}`); + + document.body.appendChild(notification); + }; + + document.addEventListener('securitypolicyviolation', () => { + showCspWarning(); + }); + + window.addEventListener('message', (event) => { + if (event && event.data && event.data.name === 'vscode-did-block-svg') { + showCspWarning(); + } + }); +}()); \ No newline at end of file diff --git a/extensions/markdown/media/loading.js b/extensions/markdown/media/loading.js new file mode 100644 index 0000000000..74e01d00cd --- /dev/null +++ b/extensions/markdown/media/loading.js @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// @ts-check + +'use strict'; + + +(function () { + const unloadedStyles = []; + + const onStyleLoadError = (event) => { + const source = event.target.dataset.source; + unloadedStyles.push(source); + }; + + window.addEventListener('DOMContentLoaded', () => { + for (const link of document.getElementsByClassName('code-user-style')) { + if (link.dataset.source) { + link.onerror = onStyleLoadError; + } + } + }) + + window.addEventListener('load', () => { + if (!unloadedStyles.length) { + return; + } + const args = [unloadedStyles]; + window.parent.postMessage({ + command: 'did-click-link', + data: `command:_markdown.onPreviewStyleLoadError?${encodeURIComponent(JSON.stringify(args))}` + }, 'file://'); + }); +}()); \ No newline at end of file diff --git a/extensions/markdown/media/main.js b/extensions/markdown/media/main.js new file mode 100644 index 0000000000..5a4a5f1d06 --- /dev/null +++ b/extensions/markdown/media/main.js @@ -0,0 +1,223 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +(function () { + // From https://remysharp.com/2010/07/21/throttling-function-calls + function throttle(fn, threshhold, scope) { + threshhold || (threshhold = 250); + var last, deferTimer; + return function () { + var context = scope || this; + + var now = +new Date, + args = arguments; + if (last && now < last + threshhold) { + // hold on to it + clearTimeout(deferTimer); + deferTimer = setTimeout(function () { + last = now; + fn.apply(context, args); + }, threshhold + last - now); + } else { + last = now; + fn.apply(context, args); + } + }; + } + + /** + * Find the html elements that map to a specific target line in the editor. + * + * If an exact match, returns a single element. If the line is between elements, + * returns the element prior to and the element after the given line. + */ + function getElementsForSourceLine(targetLine) { + const lines = document.getElementsByClassName('code-line'); + let previous = lines[0] && +lines[0].getAttribute('data-line') ? { line: +lines[0].getAttribute('data-line'), element: lines[0] } : null; + for (const element of lines) { + const lineNumber = +element.getAttribute('data-line'); + if (isNaN(lineNumber)) { + continue; + } + const entry = { line: lineNumber, element: element }; + if (lineNumber === targetLine) { + return { previous: entry, next: null }; + } else if (lineNumber > targetLine) { + return { previous, next: entry }; + } + previous = entry; + } + return { previous }; + } + + /** + * Find the html elements that are at a specific pixel offset on the page. + */ + function getLineElementsAtPageOffset(offset) { + const lines = document.getElementsByClassName('code-line'); + const position = offset - window.scrollY; + let previous = null; + for (const element of lines) { + const line = +element.getAttribute('data-line'); + if (isNaN(line)) { + continue; + } + const bounds = element.getBoundingClientRect(); + const entry = { element, line }; + if (position < bounds.top) { + if (previous && previous.fractional < 1) { + previous.line += previous.fractional; + return { previous }; + } + return { previous, next: entry }; + } + entry.fractional = (position - bounds.top) / (bounds.height); + previous = entry; + } + return { previous }; + } + + function getSourceRevealAddedOffset() { + return -(window.innerHeight * 1 / 5); + } + + /** + * Attempt to reveal the element for a source line in the editor. + */ + function scrollToRevealSourceLine(line) { + const { previous, next } = getElementsForSourceLine(line); + marker.update(previous && previous.element); + if (previous && settings.scrollPreviewWithEditorSelection) { + let scrollTo = 0; + if (next) { + // Between two elements. Go to percentage offset between them. + const betweenProgress = (line - previous.line) / (next.line - previous.line); + const elementOffset = next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top; + scrollTo = previous.element.getBoundingClientRect().top + betweenProgress * elementOffset; + } else { + scrollTo = previous.element.getBoundingClientRect().top; + } + window.scroll(0, window.scrollY + scrollTo + getSourceRevealAddedOffset()); + } + } + + function getEditorLineNumberForPageOffset(offset) { + const { previous, next } = getLineElementsAtPageOffset(offset); + if (previous) { + if (next) { + const betweenProgress = (offset - window.scrollY - previous.element.getBoundingClientRect().top) / (next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top); + return previous.line + betweenProgress * (next.line - previous.line); + } else { + return previous.line; + } + } + return null; + } + + + class ActiveLineMarker { + update(before) { + this._unmarkActiveElement(this._current); + this._markActiveElement(before); + this._current = before; + } + + _unmarkActiveElement(element) { + if (!element) { + return; + } + element.className = element.className.replace(/\bcode-active-line\b/g); + } + + _markActiveElement(element) { + if (!element) { + return; + } + element.className += ' code-active-line'; + } + } + + var scrollDisabled = true; + var marker = new ActiveLineMarker(); + const settings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-settings')); + + function onLoad() { + if (settings.scrollPreviewWithEditorSelection) { + const initialLine = +settings.line; + if (!isNaN(initialLine)) { + setTimeout(() => { + scrollDisabled = true; + scrollToRevealSourceLine(initialLine); + }, 0); + } + } + } + + if (document.readyState === 'loading' || document.readyState === 'uninitialized') { + document.addEventListener('DOMContentLoaded', onLoad); + } else { + onLoad(); + } + + + window.addEventListener('resize', () => { + scrollDisabled = true; + }, true); + + window.addEventListener('message', (() => { + const doScroll = throttle(line => { + scrollDisabled = true; + scrollToRevealSourceLine(line); + }, 50); + return event => { + const line = +event.data.line; + if (!isNaN(line)) { + doScroll(line); + } + }; + })(), false); + + document.addEventListener('dblclick', event => { + if (!settings.doubleClickToSwitchToEditor) { + return; + } + + // Ignore clicks on links + for (let node = event.target; node; node = node.parentNode) { + if (node.tagName === "A") { + return; + } + } + + const offset = event.pageY; + const line = getEditorLineNumberForPageOffset(offset); + if (!isNaN(line)) { + const args = [settings.source, line]; + window.parent.postMessage({ + command: "did-click-link", + data: `command:_markdown.didClick?${encodeURIComponent(JSON.stringify(args))}` + }, "file://"); + } + }); + + if (settings.scrollEditorWithPreview) { + window.addEventListener('scroll', throttle(() => { + if (scrollDisabled) { + scrollDisabled = false; + } else { + const line = getEditorLineNumberForPageOffset(window.scrollY); + if (!isNaN(line)) { + const args = [settings.source, line]; + window.parent.postMessage({ + command: 'did-click-link', + data: `command:_markdown.revealLine?${encodeURIComponent(JSON.stringify(args))}` + }, 'file://'); + } + } + }, 50)); + } +}()); \ No newline at end of file diff --git a/extensions/markdown/media/markdown.css b/extensions/markdown/media/markdown.css new file mode 100644 index 0000000000..4e1cf15fdc --- /dev/null +++ b/extensions/markdown/media/markdown.css @@ -0,0 +1,266 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +body { + font-family: "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + font-size: 14px; + padding: 0 26px; + line-height: 22px; + word-wrap: break-word; +} + +#code-csp-warning { + position: fixed; + top: 0; + right: 0; + color: white; + margin: 16px; + text-align: center; + font-size: 12px; + font-family: sans-serif; + background-color:#444444; + cursor: pointer; + padding: 6px; + box-shadow: 1px 1px 1px rgba(0,0,0,.25); +} + +#code-csp-warning:hover { + text-decoration: none; + background-color:#007acc; + box-shadow: 2px 2px 2px rgba(0,0,0,.25); +} + + +body.scrollBeyondLastLine { + margin-bottom: calc(100vh - 22px); +} + +body.showEditorSelection .code-line { + position: relative; +} + +body.showEditorSelection .code-active-line:before, +body.showEditorSelection .code-line:hover:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: -12px; + height: 100%; +} + +body.showEditorSelection li.code-active-line:before, +body.showEditorSelection li.code-line:hover:before { + left: -30px; +} + +.vscode-light.showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(0, 0, 0, 0.15); +} + +.vscode-light.showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(0, 0, 0, 0.40); +} + +.vscode-light.showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +.vscode-dark.showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(255, 255, 255, 0.4); +} + +.vscode-dark.showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(255, 255, 255, 0.60); +} + +.vscode-dark.showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +.vscode-high-contrast.showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(255, 160, 0, 0.7); +} + +.vscode-high-contrast.showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(255, 160, 0, 1); +} + +.vscode-high-contrast.showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +img { + max-width: 100%; + max-height: 100%; +} + +a { + color: #4080D0; + text-decoration: none; +} + +a:focus, +input:focus, +select:focus, +textarea:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +hr { + border: 0; + height: 2px; + border-bottom: 2px solid; +} + +h1 { + padding-bottom: 0.3em; + line-height: 1.2; + border-bottom-width: 1px; + border-bottom-style: solid; +} + +h1, h2, h3 { + font-weight: normal; +} + +h1 code, +h2 code, +h3 code, +h4 code, +h5 code, +h6 code { + font-size: inherit; + line-height: auto; +} + +a:hover { + color: #4080D0; + text-decoration: underline; +} + +table { + border-collapse: collapse; +} + +table > thead > tr > th { + text-align: left; + border-bottom: 1px solid; +} + +table > thead > tr > th, +table > thead > tr > td, +table > tbody > tr > th, +table > tbody > tr > td { + padding: 5px 10px; +} + +table > tbody > tr + tr > td { + border-top: 1px solid; +} + +blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left: 5px solid; +} + +code { + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-size: 14px; + line-height: 19px; +} + +body.wordWrap pre { + white-space: pre-wrap; +} + +.mac code { + font-size: 12px; + line-height: 18px; +} + +pre:not(.hljs), +pre.hljs code > div { + padding: 16px; + border-radius: 3px; + overflow: auto; +} + +/** Theming */ + +.vscode-light, +.vscode-light pre code { + color: rgb(30, 30, 30); +} + +.vscode-dark, +.vscode-dark pre code { + color: #DDD; +} + +.vscode-high-contrast, +.vscode-high-contrast pre code { + color: white; +} + +.vscode-light code { + color: #A31515; +} + +.vscode-dark code { + color: #D7BA7D; +} + +.vscode-light pre:not(.hljs), +.vscode-light code > div { + background-color: rgba(220, 220, 220, 0.4); +} + +.vscode-dark pre:not(.hljs), +.vscode-dark code > div { + background-color: rgba(10, 10, 10, 0.4); +} + +.vscode-high-contrast pre:not(.hljs), +.vscode-high-contrast code > div { + background-color: rgb(0, 0, 0); +} + +.vscode-high-contrast h1 { + border-color: rgb(0, 0, 0); +} + +.vscode-light table > thead > tr > th { + border-color: rgba(0, 0, 0, 0.69); +} + +.vscode-dark table > thead > tr > th { + border-color: rgba(255, 255, 255, 0.69); +} + +.vscode-light h1, +.vscode-light hr, +.vscode-light table > tbody > tr + tr > td { + border-color: rgba(0, 0, 0, 0.18); +} + +.vscode-dark h1, +.vscode-dark hr, +.vscode-dark table > tbody > tr + tr > td { + border-color: rgba(255, 255, 255, 0.18); +} + +.vscode-light blockquote, +.vscode-dark blockquote { + background: rgba(127, 127, 127, 0.1); + border-color: rgba(0, 122, 204, 0.5); +} + +.vscode-high-contrast blockquote { + background: transparent; + border-color: #fff; +} \ No newline at end of file diff --git a/extensions/markdown/media/tomorrow.css b/extensions/markdown/media/tomorrow.css new file mode 100644 index 0000000000..96edd12b02 --- /dev/null +++ b/extensions/markdown/media/tomorrow.css @@ -0,0 +1,73 @@ +/* Tomorrow Theme */ +/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ + +/* Tomorrow Comment */ +.hljs-comment, +.hljs-quote { + color: #8e908c; +} + +/* Tomorrow Red */ +.hljs-variable, +.hljs-template-variable, +.hljs-tag, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class, +.hljs-regexp, +.hljs-deletion { + color: #c82829; +} + +/* Tomorrow Orange */ +.hljs-number, +.hljs-built_in, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params, +.hljs-meta, +.hljs-link { + color: #f5871f; +} + +/* Tomorrow Yellow */ +.hljs-attribute { + color: #eab700; +} + +/* Tomorrow Green */ +.hljs-string, +.hljs-symbol, +.hljs-bullet, +.hljs-addition { + color: #718c00; +} + +/* Tomorrow Blue */ +.hljs-title, +.hljs-section { + color: #4271ae; +} + +/* Tomorrow Purple */ +.hljs-keyword, +.hljs-selector-tag { + color: #8959a8; +} + +.hljs { + display: block; + overflow-x: auto; + color: #4d4d4c; + padding: 0.5em; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} \ No newline at end of file diff --git a/extensions/markdown/npm-shrinkwrap.json b/extensions/markdown/npm-shrinkwrap.json new file mode 100644 index 0000000000..f8ae61f746 --- /dev/null +++ b/extensions/markdown/npm-shrinkwrap.json @@ -0,0 +1,76 @@ +{ + "name": "vscode-markdown", + "version": "0.2.0", + "dependencies": { + "applicationinsights": { + "version": "0.18.0", + "from": "applicationinsights@0.18.0", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz" + }, + "argparse": { + "version": "1.0.9", + "from": "argparse@>=1.0.7 <2.0.0", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz" + }, + "entities": { + "version": "1.1.1", + "from": "entities@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + }, + "highlight.js": { + "version": "9.5.0", + "from": "highlight.js@>=9.3.0 <10.0.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.5.0.tgz" + }, + "linkify-it": { + "version": "2.0.3", + "from": "linkify-it@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz" + }, + "markdown-it": { + "version": "8.2.2", + "from": "markdown-it@8.2.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.2.2.tgz" + }, + "markdown-it-named-headers": { + "version": "0.0.4", + "from": "markdown-it-named-headers@0.0.4", + "resolved": "https://registry.npmjs.org/markdown-it-named-headers/-/markdown-it-named-headers-0.0.4.tgz" + }, + "mdurl": { + "version": "1.0.1", + "from": "mdurl@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz" + }, + "sprintf-js": { + "version": "1.0.3", + "from": "sprintf-js@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + }, + "string": { + "version": "3.3.1", + "from": "string@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/string/-/string-3.3.1.tgz" + }, + "uc.micro": { + "version": "1.0.3", + "from": "uc.micro@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.3.tgz" + }, + "vscode-extension-telemetry": { + "version": "0.0.7", + "from": "vscode-extension-telemetry@>=0.0.8 <0.0.9", + "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz" + }, + "vscode-nls": { + "version": "2.0.2", + "from": "vscode-nls@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + }, + "winreg": { + "version": "1.2.3", + "from": "winreg@1.2.3", + "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz" + } + } +} \ No newline at end of file diff --git a/extensions/markdown/package.json b/extensions/markdown/package.json new file mode 100644 index 0000000000..dedbd9dbcf --- /dev/null +++ b/extensions/markdown/package.json @@ -0,0 +1,257 @@ +{ + "name": "vscode-markdown", + "displayName": "VS Code Markdown", + "description": "Markdown for VS Code", + "version": "0.2.0", + "publisher": "Microsoft", + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "engines": { + "vscode": "^1.0.0" + }, + "main": "./out/extension", + "categories": [ + "Languages" + ], + "activationEvents": [ + "onLanguage:markdown", + "onCommand:markdown.refreshPreview", + "onCommand:markdown.showPreview", + "onCommand:markdown.showPreviewToSide", + "onCommand:markdown.showSource", + "onCommand:markdown.showPreviewSecuritySelector" + ], + "contributes": { + "languages": [ + { + "id": "markdown", + "aliases": [ + "Markdown", + "markdown" + ], + "extensions": [ + ".md", + ".mdown", + ".markdown", + ".markdn" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "markdown", + "scopeName": "text.html.markdown", + "path": "./syntaxes/markdown.tmLanguage" + } + ], + "commands": [ + { + "command": "markdown.showPreview", + "title": "%markdown.preview.title%", + "category": "Markdown", + "icon": { + "light": "./media/Preview.svg", + "dark": "./media/Preview_inverse.svg" + } + }, + { + "command": "markdown.showPreviewToSide", + "title": "%markdown.previewSide.title%", + "category": "Markdown", + "icon": { + "light": "./media/PreviewOnRightPane_16x.svg", + "dark": "./media/PreviewOnRightPane_16x_dark.svg" + } + }, + { + "command": "markdown.showSource", + "title": "%markdown.showSource.title%", + "category": "Markdown", + "icon": { + "light": "./media/ViewSource.svg", + "dark": "./media/ViewSource_inverse.svg" + } + }, + { + "command": "markdown.refreshPreview", + "title": "%markdown.refreshPreview.title%", + "category": "Markdown" + }, + { + "command": "markdown.showPreviewSecuritySelector", + "title": "%markdown.showPreviewSecuritySelector.title%", + "category": "Markdown" + } + ], + "menus": { + "editor/title": [ + { + "command": "markdown.showPreviewToSide", + "when": "editorLangId == markdown", + "alt": "markdown.showPreview", + "group": "navigation" + }, + { + "command": "markdown.showSource", + "when": "resourceScheme == markdown", + "group": "navigation" + }, + { + "command": "markdown.refreshPreview", + "when": "resourceScheme == markdown" + }, + { + "command": "markdown.showPreviewSecuritySelector", + "when": "resourceScheme == markdown" + } + ], + "explorer/context": [ + { + "command": "markdown.showPreview", + "when": "resourceLangId == markdown", + "group": "navigation" + } + ], + "commandPalette": [ + { + "command": "markdown.showPreview", + "when": "editorLangId == markdown", + "group": "navigation" + }, + { + "command": "markdown.showPreviewToSide", + "when": "editorLangId == markdown", + "group": "navigation" + }, + { + "command": "markdown.showSource", + "when": "resourceScheme == markdown", + "group": "navigation" + }, + { + "command": "markdown.showPreviewSecuritySelector", + "when": "editorLangId == markdown" + } + ] + }, + "keybindings": [ + { + "command": "markdown.showPreview", + "key": "shift+ctrl+v", + "mac": "shift+cmd+v", + "when": "editorLangId == markdown" + }, + { + "command": "markdown.showPreviewToSide", + "key": "ctrl+k v", + "mac": "cmd+k v", + "when": "editorLangId == markdown" + } + ], + "snippets": [ + { + "language": "markdown", + "path": "./snippets/markdown.json" + } + ], + "configuration": { + "type": "object", + "title": "Markdown", + "order": 20, + "properties": { + "markdown.styles": { + "type": "array", + "default": [], + "description": "%markdown.styles.dec%" + }, + "markdown.previewFrontMatter": { + "type": "string", + "enum": [ + "hide", + "show" + ], + "default": "hide", + "description": "%markdown.previewFrontMatter.dec%" + }, + "markdown.preview.breaks": { + "type": "boolean", + "default": false, + "description": "%markdown.preview.breaks.desc%" + }, + "markdown.preview.linkify": { + "type": "boolean", + "default": true, + "description": "%markdown.preview.linkify%" + }, + "markdown.preview.fontFamily": { + "type": "string", + "default": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'HelveticaNeue-Light', 'Ubuntu', 'Droid Sans', sans-serif", + "description": "%markdown.preview.fontFamily.desc%" + }, + "markdown.preview.fontSize": { + "type": "number", + "default": 14, + "description": "%markdown.preview.fontSize.desc%" + }, + "markdown.preview.lineHeight": { + "type": "number", + "default": 1.6, + "description": "%markdown.preview.lineHeight.desc%" + }, + "markdown.preview.scrollPreviewWithEditorSelection": { + "type": "boolean", + "default": true, + "description": "%markdown.preview.scrollPreviewWithEditorSelection.desc%" + }, + "markdown.preview.markEditorSelection": { + "type": "boolean", + "default": true, + "description": "%markdown.preview.markEditorSelection.desc%" + }, + "markdown.preview.scrollEditorWithPreview": { + "type": "boolean", + "default": true, + "description": "%markdown.preview.scrollEditorWithPreview.desc%" + }, + "markdown.preview.doubleClickToSwitchToEditor": { + "type": "boolean", + "default": true, + "description": "%markdown.preview.doubleClickToSwitchToEditor.desc%" + }, + "markdown.trace": { + "type": "string", + "enum": [ + "off", + "verbose" + ], + "default": "off", + "description": "%markdown.trace.desc%" + } + } + }, + "configurationDefaults": { + "[markdown]": { + "editor.wordWrap": "on", + "editor.quickSuggestions": false + } + } + }, + "scripts": { + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown ./tsconfig.json", + "update-grammar": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ./syntaxes/gulpfile.js" + }, + "dependencies": { + "highlight.js": "9.5.0", + "markdown-it": "8.2.2", + "markdown-it-named-headers": "0.0.4", + "vscode-extension-telemetry": "0.0.8", + "vscode-nls": "2.0.2" + }, + "devDependencies": { + "@types/highlight.js": "^9.1.9", + "@types/markdown-it": "0.0.2", + "@types/node": "^7.0.4", + "gulp-rename": "^1.2.2", + "gulp-replace": "^0.5.4" + } +} diff --git a/extensions/markdown/package.nls.json b/extensions/markdown/package.nls.json new file mode 100644 index 0000000000..2b9dae3721 --- /dev/null +++ b/extensions/markdown/package.nls.json @@ -0,0 +1,19 @@ +{ + "markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a
for every newline.", + "markdown.preview.linkify": "Enable or disable conversion of URL-like text to links in the markdown preview.", + "markdown.preview.doubleClickToSwitchToEditor.desc": "Double click in the markdown preview to switch to the editor.", + "markdown.preview.fontFamily.desc": "Controls the font family used in the markdown preview.", + "markdown.preview.fontSize.desc": "Controls the font size in pixels used in the markdown preview.", + "markdown.preview.lineHeight.desc": "Controls the line height used in the markdown preview. This number is relative to the font size.", + "markdown.preview.markEditorSelection.desc": "Mark the current editor selection in the markdown preview.", + "markdown.preview.scrollEditorWithPreview.desc": "When the markdown preview is scrolled, update the view of the editor.", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "Scrolls the markdown preview to reveal the currently selected line from the editor.", + "markdown.preview.title" : "Open Preview", + "markdown.previewFrontMatter.dec": "Sets how YAML front matter should be rendered in the markdown preview. 'hide' removes the front matter. Otherwise, the front matter is treated as markdown content.", + "markdown.previewSide.title" : "Open Preview to the Side", + "markdown.showSource.title" : "Show Source", + "markdown.styles.dec": "A list of URLs or local paths to CSS style sheets to use from the markdown preview. Relative paths are interpreted relative to the folder open in the explorer. If there is no open folder, they are interpreted relative to the location of the markdown file. All '\\' need to be written as '\\\\'.", + "markdown.showPreviewSecuritySelector.title": "Change Preview Security Settings", + "markdown.trace.desc": "Enable debug logging for the markdown extension.", + "markdown.refreshPreview.title": "Refresh Preview" +} \ No newline at end of file diff --git a/extensions/markdown/snippets/markdown.json b/extensions/markdown/snippets/markdown.json new file mode 100644 index 0000000000..6bba2591d1 --- /dev/null +++ b/extensions/markdown/snippets/markdown.json @@ -0,0 +1,71 @@ +{ + "Insert bold text": { + "prefix": "bold", + "body": "**${1:${TM_SELECTED_TEXT}}**$0", + "description": "Insert bold text" + }, + "Insert italic text": { + "prefix": "italic", + "body": "*${1:${TM_SELECTED_TEXT}}*$0", + "description": "Insert italic text" + }, + "Insert quoted text": { + "prefix": "quote", + "body": "> ${1:${TM_SELECTED_TEXT}}", + "description": "Insert quoted text" + }, + "Insert code": { + "prefix": "code", + "body": "`${1:${TM_SELECTED_TEXT}}`$0", + "description": "Insert code" + }, + "Insert fenced code block": { + "prefix": "fenced codeblock", + "body": [ + "```${1:language}", + "$0", + "```" + ], + "description": "Insert fenced code block" + }, + "Insert heading": { + "prefix": "heading", + "body": "# ${1:text}", + "description": "Insert heading" + }, + "Insert unordered list": { + "prefix": "unordered list", + "body": [ + "- ${1:first}", + "- ${2:second}", + "- ${3:third}", + "$0" + ], + "description": "Insert unordered list" + }, + "Insert ordered list": { + "prefix": "ordered list", + "body": [ + "1. ${1:first}", + "2. ${2:second}", + "3. ${3:third}", + "$0" + ], + "description": "Insert ordered list" + }, + "Insert horizontal rule": { + "prefix": "horizontal rule", + "body": "----------\n", + "description": "Insert horizontal rule" + }, + "Insert link": { + "prefix": "link", + "body": "[${1:text}](http://${2:link})$0", + "description": "Insert link" + }, + "Insert image": { + "prefix": "image", + "body": "![${1:alt}](http://${2:link})$0", + "description": "Insert image" + } +} diff --git a/extensions/markdown/src/documentLinkProvider.ts b/extensions/markdown/src/documentLinkProvider.ts new file mode 100644 index 0000000000..39969f3884 --- /dev/null +++ b/extensions/markdown/src/documentLinkProvider.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as vscode from 'vscode'; +import * as path from 'path'; + +export default class MarkdownDocumentLinkProvider implements vscode.DocumentLinkProvider { + + private _linkPattern = /(\[[^\]]*\]\(\s*?)(((((?=.*\)\)+)|(?=.*\)\]+))[^\s\)]+?)|([^\s]+)))\)/g; + + constructor() { } + + public provideDocumentLinks(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.DocumentLink[] { + const results: vscode.DocumentLink[] = []; + const base = path.dirname(document.uri.fsPath); + const text = document.getText(); + + this._linkPattern.lastIndex = 0; + let match: RegExpMatchArray | null; + while ((match = this._linkPattern.exec(text))) { + const pre = match[1]; + const link = match[2]; + const offset = (match.index || 0) + pre.length; + const linkStart = document.positionAt(offset); + const linkEnd = document.positionAt(offset + link.length); + try { + results.push(new vscode.DocumentLink( + new vscode.Range(linkStart, linkEnd), + this.normalizeLink(document, link, base))); + } catch (e) { + // noop + } + } + + return results; + } + + private normalizeLink(document: vscode.TextDocument, link: string, base: string): vscode.Uri { + const uri = vscode.Uri.parse(link); + if (uri.scheme) { + return uri; + } + + // assume it must be a file + let resourcePath = uri.path; + if (!uri.path) { + resourcePath = document.uri.path; + } else if (uri.path[0] === '/') { + const root = vscode.workspace.getWorkspaceFolder(document.uri); + if (root) { + resourcePath = path.join(root.uri.fsPath, uri.path); + } + } else { + resourcePath = path.join(base, uri.path); + } + + return vscode.Uri.parse(`command:_markdown.openDocumentLink?${encodeURIComponent(JSON.stringify({ fragment: uri.fragment, path: resourcePath }))}`); + } +} diff --git a/extensions/markdown/src/documentSymbolProvider.ts b/extensions/markdown/src/documentSymbolProvider.ts new file mode 100644 index 0000000000..a6e1671c37 --- /dev/null +++ b/extensions/markdown/src/documentSymbolProvider.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as vscode from 'vscode'; + +import { MarkdownEngine } from './markdownEngine'; +import { TableOfContentsProvider } from './tableOfContentsProvider'; + +export default class MDDocumentSymbolProvider implements vscode.DocumentSymbolProvider { + + constructor( + private engine: MarkdownEngine + ) { } + + public async provideDocumentSymbols(document: vscode.TextDocument): Promise { + const toc = await new TableOfContentsProvider(this.engine, document).getToc(); + return toc.map(entry => { + return new vscode.SymbolInformation('#'.repeat(entry.level) + ' ' + entry.text, vscode.SymbolKind.Namespace, '', entry.location); + }); + } +} \ No newline at end of file diff --git a/extensions/markdown/src/extension.ts b/extensions/markdown/src/extension.ts new file mode 100644 index 0000000000..d6f50aa489 --- /dev/null +++ b/extensions/markdown/src/extension.ts @@ -0,0 +1,308 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vscode-nls'; +const localize = nls.config(process.env.VSCODE_NLS_CONFIG)(); +import * as vscode from 'vscode'; +import * as path from 'path'; +import TelemetryReporter from 'vscode-extension-telemetry'; +import { MarkdownEngine } from './markdownEngine'; +import DocumentLinkProvider from './documentLinkProvider'; +import MDDocumentSymbolProvider from './documentSymbolProvider'; +import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security'; +import { MDDocumentContentProvider, getMarkdownUri, isMarkdownFile } from './previewContentProvider'; +import { TableOfContentsProvider } from './tableOfContentsProvider'; +import { Logger } from './logger'; + +interface IPackageInfo { + name: string; + version: string; + aiKey: string; +} + +interface OpenDocumentLinkArgs { + path: string; + fragment: string; +} + +const resolveExtensionResources = (extension: vscode.Extension, stylePath: string): vscode.Uri => { + const resource = vscode.Uri.parse(stylePath); + if (resource.scheme) { + return resource; + } + return vscode.Uri.file(path.join(extension.extensionPath, stylePath)); +}; + +var telemetryReporter: TelemetryReporter | null; + +export function activate(context: vscode.ExtensionContext) { + const packageInfo = getPackageInfo(); + telemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); + if (telemetryReporter) { + context.subscriptions.push(telemetryReporter); + } + + const cspArbiter = new ExtensionContentSecurityPolicyArbiter(context.globalState); + const engine = new MarkdownEngine(); + + const logger = new Logger(); + + const contentProvider = new MDDocumentContentProvider(engine, context, cspArbiter, logger); + const contentProviderRegistration = vscode.workspace.registerTextDocumentContentProvider('markdown', contentProvider); + const previewSecuritySelector = new PreviewSecuritySelector(cspArbiter, contentProvider); + + for (const extension of vscode.extensions.all) { + const contributes = extension.packageJSON && extension.packageJSON.contributes; + if (!contributes) { + continue; + } + + const styles = contributes['markdown.previewStyles']; + if (styles && Array.isArray(styles)) { + for (const style of styles) { + try { + contentProvider.addStyle(resolveExtensionResources(extension, style)); + } catch (e) { + // noop + } + } + } + + const scripts = contributes['markdown.previewScripts']; + if (scripts && Array.isArray(scripts)) { + for (const script of scripts) { + try { + contentProvider.addScript(resolveExtensionResources(extension, script)); + } catch (e) { + // noop + } + } + } + + if (contributes['markdown.markdownItPlugins']) { + extension.activate().then(() => { + if (extension.exports && extension.exports.extendMarkdownIt) { + engine.addPlugin((md: any) => extension.exports.extendMarkdownIt(md)); + } + }); + } + } + + const symbolsProvider = new MDDocumentSymbolProvider(engine); + const symbolsProviderRegistration = vscode.languages.registerDocumentSymbolProvider({ language: 'markdown' }, symbolsProvider); + context.subscriptions.push(contentProviderRegistration, symbolsProviderRegistration); + + context.subscriptions.push(vscode.languages.registerDocumentLinkProvider('markdown', new DocumentLinkProvider())); + + context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreview', (uri) => showPreview(cspArbiter, uri, false))); + context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreviewToSide', uri => showPreview(cspArbiter, uri, true))); + context.subscriptions.push(vscode.commands.registerCommand('markdown.showSource', showSource)); + + context.subscriptions.push(vscode.commands.registerCommand('_markdown.revealLine', (uri, line) => { + const sourceUri = vscode.Uri.parse(decodeURIComponent(uri)); + logger.log('revealLine', { uri, sourceUri: sourceUri.toString(), line }); + + vscode.window.visibleTextEditors + .filter(editor => isMarkdownFile(editor.document) && editor.document.uri.fsPath === sourceUri.fsPath) + .forEach(editor => { + const sourceLine = Math.floor(line); + const fraction = line - sourceLine; + const text = editor.document.lineAt(sourceLine).text; + const start = Math.floor(fraction * text.length); + editor.revealRange( + new vscode.Range(sourceLine, start, sourceLine + 1, 0), + vscode.TextEditorRevealType.AtTop); + }); + })); + + context.subscriptions.push(vscode.commands.registerCommand('_markdown.didClick', (uri: string, line) => { + const sourceUri = vscode.Uri.parse(decodeURIComponent(uri)); + return vscode.workspace.openTextDocument(sourceUri) + .then(document => vscode.window.showTextDocument(document)) + .then(editor => + vscode.commands.executeCommand('revealLine', { lineNumber: Math.floor(line), at: 'center' }) + .then(() => editor)) + .then(editor => { + if (editor) { + editor.selection = new vscode.Selection( + new vscode.Position(Math.floor(line), 0), + new vscode.Position(Math.floor(line), 0)); + } + }); + })); + + context.subscriptions.push(vscode.commands.registerCommand('_markdown.openDocumentLink', (args: OpenDocumentLinkArgs) => { + const tryRevealLine = async (editor: vscode.TextEditor) => { + if (editor && args.fragment) { + const toc = new TableOfContentsProvider(engine, editor.document); + const line = await toc.lookup(args.fragment); + if (!isNaN(line)) { + return editor.revealRange( + new vscode.Range(line, 0, line, 0), + vscode.TextEditorRevealType.AtTop); + } + } + }; + if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document) && vscode.window.activeTextEditor.document.uri.fsPath === args.path) { + return tryRevealLine(vscode.window.activeTextEditor); + } else { + const resource = vscode.Uri.file(args.path); + return vscode.workspace.openTextDocument(resource) + .then(vscode.window.showTextDocument) + .then(tryRevealLine, _ => vscode.commands.executeCommand('vscode.open', resource)); + } + })); + + context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreviewSecuritySelector', (resource: string | undefined) => { + if (resource) { + const source = vscode.Uri.parse(resource).query; + previewSecuritySelector.showSecutitySelectorForResource(vscode.Uri.parse(source)); + } else { + if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.languageId === 'markdown') { + previewSecuritySelector.showSecutitySelectorForResource(vscode.window.activeTextEditor.document.uri); + } + } + })); + + context.subscriptions.push(vscode.commands.registerCommand('markdown.refreshPreview', (resource: string | undefined) => { + if (resource) { + const source = vscode.Uri.parse(resource); + contentProvider.update(source); + } else if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) { + contentProvider.update(getMarkdownUri(vscode.window.activeTextEditor.document.uri)); + } else { + // update all generated md documents + for (const document of vscode.workspace.textDocuments) { + if (document.uri.scheme === 'markdown') { + contentProvider.update(document.uri); + } + } + } + })); + + context.subscriptions.push(vscode.commands.registerCommand('_markdown.onPreviewStyleLoadError', (resources: string[]) => { + vscode.window.showWarningMessage(localize('onPreviewStyleLoadError', "Could not load 'markdown.styles': {0}", resources.join(', '))); + })); + + context.subscriptions.push(vscode.workspace.onDidSaveTextDocument(document => { + if (isMarkdownFile(document)) { + const uri = getMarkdownUri(document.uri); + contentProvider.update(uri); + } + })); + + context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(event => { + if (isMarkdownFile(event.document)) { + const uri = getMarkdownUri(event.document.uri); + contentProvider.update(uri); + } + })); + + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { + logger.updateConfiguration(); + contentProvider.updateConfiguration(); + })); + + context.subscriptions.push(vscode.window.onDidChangeTextEditorSelection(event => { + if (isMarkdownFile(event.textEditor.document)) { + const markdownFile = getMarkdownUri(event.textEditor.document.uri); + logger.log('updatePreviewForSelection', { markdownFile: markdownFile.toString() }); + + vscode.commands.executeCommand('_workbench.htmlPreview.postMessage', + markdownFile, + { + line: event.selections[0].active.line + }); + } + })); +} + + +function showPreview(cspArbiter: ExtensionContentSecurityPolicyArbiter, uri?: vscode.Uri, sideBySide: boolean = false) { + let resource = uri; + if (!(resource instanceof vscode.Uri)) { + if (vscode.window.activeTextEditor) { + // we are relaxed and don't check for markdown files + resource = vscode.window.activeTextEditor.document.uri; + } + } + + if (!(resource instanceof vscode.Uri)) { + if (!vscode.window.activeTextEditor) { + // this is most likely toggling the preview + return vscode.commands.executeCommand('markdown.showSource'); + } + // nothing found that could be shown or toggled + return; + } + + const thenable = vscode.commands.executeCommand('vscode.previewHtml', + getMarkdownUri(resource), + getViewColumn(sideBySide), + `Preview '${path.basename(resource.fsPath)}'`, + { + allowScripts: true, + allowSvgs: cspArbiter.shouldAllowSvgsForResource(resource) + }); + + if (telemetryReporter) { + telemetryReporter.sendTelemetryEvent('openPreview', { + where: sideBySide ? 'sideBySide' : 'inPlace', + how: (uri instanceof vscode.Uri) ? 'action' : 'pallete' + }); + } + + return thenable; +} + +function getViewColumn(sideBySide: boolean): vscode.ViewColumn | undefined { + const active = vscode.window.activeTextEditor; + if (!active) { + return vscode.ViewColumn.One; + } + + if (!sideBySide) { + return active.viewColumn; + } + + switch (active.viewColumn) { + case vscode.ViewColumn.One: + return vscode.ViewColumn.Two; + case vscode.ViewColumn.Two: + return vscode.ViewColumn.Three; + } + + return active.viewColumn; +} + +function showSource(mdUri: vscode.Uri) { + if (!mdUri) { + return vscode.commands.executeCommand('workbench.action.navigateBack'); + } + + const docUri = vscode.Uri.parse(mdUri.query); + for (const editor of vscode.window.visibleTextEditors) { + if (editor.document.uri.scheme === docUri.scheme && editor.document.uri.fsPath === docUri.fsPath) { + return vscode.window.showTextDocument(editor.document, editor.viewColumn); + } + } + + return vscode.workspace.openTextDocument(docUri) + .then(vscode.window.showTextDocument); +} + +function getPackageInfo(): IPackageInfo | null { + const extention = vscode.extensions.getExtension('Microsoft.vscode-markdown'); + if (extention && extention.packageJSON) { + return { + name: extention.packageJSON.name, + version: extention.packageJSON.version, + aiKey: extention.packageJSON.aiKey + }; + } + return null; +} diff --git a/extensions/markdown/src/logger.ts b/extensions/markdown/src/logger.ts new file mode 100644 index 0000000000..17aef6d1aa --- /dev/null +++ b/extensions/markdown/src/logger.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { OutputChannel, window, workspace } from 'vscode'; + +enum Trace { + Off, + Verbose +} + +namespace Trace { + export function fromString(value: string): Trace { + value = value.toLowerCase(); + switch (value) { + case 'off': + return Trace.Off; + case 'verbose': + return Trace.Verbose; + default: + return Trace.Off; + } + } +} + + +function isString(value: any): value is string { + return Object.prototype.toString.call(value) === '[object String]'; +} + +export class Logger { + private trace: Trace; + private _output: OutputChannel; + + constructor() { + this.updateConfiguration(); + } + + public log(message: string, data?: any): void { + if (this.trace === Trace.Verbose) { + this.output.appendLine(`[Log - ${(new Date().toLocaleTimeString())}] ${message}`); + if (data) { + this.output.appendLine(this.data2String(data)); + } + } + } + + public updateConfiguration() { + this.trace = this.readTrace(); + } + + private get output(): OutputChannel { + if (!this._output) { + this._output = window.createOutputChannel('Markdown'); + } + return this._output; + } + + private readTrace(): Trace { + return Trace.fromString(workspace.getConfiguration().get('markdown.trace', 'off')); + } + + private data2String(data: any): string { + if (data instanceof Error) { + if (isString(data.stack)) { + return data.stack; + } + return (data as Error).message; + } + if (isString(data)) { + return data; + } + return JSON.stringify(data, undefined, 2); + } +} \ No newline at end of file diff --git a/extensions/markdown/src/markdownEngine.ts b/extensions/markdown/src/markdownEngine.ts new file mode 100644 index 0000000000..b2ac85ea71 --- /dev/null +++ b/extensions/markdown/src/markdownEngine.ts @@ -0,0 +1,162 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as path from 'path'; +import { TableOfContentsProvider } from './tableOfContentsProvider'; +import { MarkdownIt, Token } from 'markdown-it'; + +const FrontMatterRegex = /^---\s*[^]*?(-{3}|\.{3})\s*/; + +export class MarkdownEngine { + private md: MarkdownIt; + + private firstLine: number; + + private currentDocument: vscode.Uri; + + private plugins: Array<(md: any) => any> = []; + + public addPlugin(factory: (md: any) => any): void { + if (this.md) { + this.usePlugin(factory); + } else { + this.plugins.push(factory); + } + } + + private usePlugin(factory: (md: any) => any): void { + try { + this.md = factory(this.md); + } catch (e) { + // noop + } + } + + private async getEngine(): Promise { + if (!this.md) { + const hljs = await import('highlight.js'); + const mdnh = await import('markdown-it-named-headers'); + this.md = (await import('markdown-it'))({ + html: true, + highlight: (str: string, lang: string) => { + if (lang && hljs.getLanguage(lang)) { + try { + return `

${hljs.highlight(lang, str, true).value}
`; + } catch (error) { } + } + return `
${this.md.utils.escapeHtml(str)}
`; + } + }).use(mdnh, { + slugify: (header: string) => TableOfContentsProvider.slugify(header) + }); + + for (const plugin of this.plugins) { + this.usePlugin(plugin); + } + this.plugins = []; + + for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'blockquote_open', 'list_item_open']) { + this.addLineNumberRenderer(this.md, renderName); + } + + this.addLinkNormalizer(this.md); + this.addLinkValidator(this.md); + } + + const config = vscode.workspace.getConfiguration('markdown'); + this.md.set({ + breaks: config.get('preview.breaks', false), + linkify: config.get('preview.linkify', true) + }); + return this.md; + } + + private stripFrontmatter(text: string): { text: string, offset: number } { + let offset = 0; + const frontMatterMatch = FrontMatterRegex.exec(text); + if (frontMatterMatch) { + const frontMatter = frontMatterMatch[0]; + offset = frontMatter.split(/\r\n|\n|\r/g).length - 1; + text = text.substr(frontMatter.length); + } + return { text, offset }; + } + + public async render(document: vscode.Uri, stripFrontmatter: boolean, text: string): Promise { + let offset = 0; + if (stripFrontmatter) { + const markdownContent = this.stripFrontmatter(text); + offset = markdownContent.offset; + text = markdownContent.text; + } + this.currentDocument = document; + this.firstLine = offset; + const engine = await this.getEngine(); + return engine.render(text); + } + + public async parse(document: vscode.Uri, source: string): Promise { + const { text, offset } = this.stripFrontmatter(source); + this.currentDocument = document; + const engine = await this.getEngine(); + + return engine.parse(text, {}).map(token => { + if (token.map) { + token.map[0] += offset; + } + return token; + }); + } + + private addLineNumberRenderer(md: any, ruleName: string): void { + const original = md.renderer.rules[ruleName]; + md.renderer.rules[ruleName] = (tokens: any, idx: number, options: any, env: any, self: any) => { + const token = tokens[idx]; + if (token.map && token.map.length) { + token.attrSet('data-line', this.firstLine + token.map[0]); + token.attrJoin('class', 'code-line'); + } + + if (original) { + return original(tokens, idx, options, env, self); + } else { + return self.renderToken(tokens, idx, options, env, self); + } + }; + } + + private addLinkNormalizer(md: any): void { + const normalizeLink = md.normalizeLink; + md.normalizeLink = (link: string) => { + try { + let uri = vscode.Uri.parse(link); + if (!uri.scheme && uri.path && !uri.fragment) { + // Assume it must be a file + if (uri.path[0] === '/') { + const root = vscode.workspace.getWorkspaceFolder(this.currentDocument); + if (root) { + uri = vscode.Uri.file(path.join(root.uri.fsPath, uri.path)); + } + } else { + uri = vscode.Uri.file(path.join(path.dirname(this.currentDocument.path), uri.path)); + } + return normalizeLink(uri.toString(true)); + } + } catch (e) { + // noop + } + return normalizeLink(link); + }; + } + + private addLinkValidator(md: any): void { + const validateLink = md.validateLink; + md.validateLink = (link: string) => { + // support file:// links + return validateLink(link) || link.indexOf('file:') === 0; + }; + } +} \ No newline at end of file diff --git a/extensions/markdown/src/previewContentProvider.ts b/extensions/markdown/src/previewContentProvider.ts new file mode 100644 index 0000000000..ed6903fa51 --- /dev/null +++ b/extensions/markdown/src/previewContentProvider.ts @@ -0,0 +1,290 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as vscode from 'vscode'; +import * as path from 'path'; +import { MarkdownEngine } from './markdownEngine'; + +import * as nls from 'vscode-nls'; +import { Logger } from './logger'; +import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from './security'; +const localize = nls.loadMessageBundle(); + +const previewStrings = { + cspAlertMessageText: localize('preview.securityMessage.text', 'Some content has been disabled in this document'), + cspAlertMessageTitle: localize('preview.securityMessage.title', 'Potentially unsafe or insecure content has been disabled in the markdown preview. Change the Markdown preview security setting to allow insecure content or enable scripts'), + cspAlertMessageLabel: localize('preview.securityMessage.label', 'Content Disabled Security Warning') +}; + +export function isMarkdownFile(document: vscode.TextDocument) { + return document.languageId === 'markdown' + && document.uri.scheme !== 'markdown'; // prevent processing of own documents +} + +export function getMarkdownUri(uri: vscode.Uri) { + if (uri.scheme === 'markdown') { + return uri; + } + + return uri.with({ + scheme: 'markdown', + path: uri.path + '.rendered', + query: uri.toString() + }); +} + +class MarkdownPreviewConfig { + public static getCurrentConfig() { + return new MarkdownPreviewConfig(); + } + + public readonly scrollBeyondLastLine: boolean; + public readonly wordWrap: boolean; + public readonly previewFrontMatter: string; + public readonly lineBreaks: boolean; + public readonly doubleClickToSwitchToEditor: boolean; + public readonly scrollEditorWithPreview: boolean; + public readonly scrollPreviewWithEditorSelection: boolean; + public readonly markEditorSelection: boolean; + + public readonly lineHeight: number; + public readonly fontSize: number; + public readonly fontFamily: string | undefined; + public readonly styles: string[]; + + private constructor() { + const editorConfig = vscode.workspace.getConfiguration('editor'); + const markdownConfig = vscode.workspace.getConfiguration('markdown'); + const markdownEditorConfig = vscode.workspace.getConfiguration('[markdown]'); + + this.scrollBeyondLastLine = editorConfig.get('scrollBeyondLastLine', false); + + this.wordWrap = editorConfig.get('wordWrap', 'off') !== 'off'; + if (markdownEditorConfig && markdownEditorConfig['editor.wordWrap']) { + this.wordWrap = markdownEditorConfig['editor.wordWrap'] !== 'off'; + } + + this.previewFrontMatter = markdownConfig.get('previewFrontMatter', 'hide'); + this.scrollPreviewWithEditorSelection = !!markdownConfig.get('preview.scrollPreviewWithEditorSelection', true); + this.scrollEditorWithPreview = !!markdownConfig.get('preview.scrollEditorWithPreview', true); + this.lineBreaks = !!markdownConfig.get('preview.breaks', false); + this.doubleClickToSwitchToEditor = !!markdownConfig.get('preview.doubleClickToSwitchToEditor', true); + this.markEditorSelection = !!markdownConfig.get('preview.markEditorSelection', true); + + this.fontFamily = markdownConfig.get('preview.fontFamily', undefined); + this.fontSize = Math.max(8, +markdownConfig.get('preview.fontSize', NaN)); + this.lineHeight = Math.max(0.6, +markdownConfig.get('preview.lineHeight', NaN)); + + this.styles = markdownConfig.get('styles', []); + } + + public isEqualTo(otherConfig: MarkdownPreviewConfig) { + for (let key in this) { + if (this.hasOwnProperty(key) && key !== 'styles') { + if (this[key] !== otherConfig[key]) { + return false; + } + } + } + + // Check styles + if (this.styles.length !== otherConfig.styles.length) { + return false; + } + for (let i = 0; i < this.styles.length; ++i) { + if (this.styles[i] !== otherConfig.styles[i]) { + return false; + } + } + + return true; + } + + [key: string]: any; +} + +export class MDDocumentContentProvider implements vscode.TextDocumentContentProvider { + private _onDidChange = new vscode.EventEmitter(); + private _waiting: boolean = false; + + private config: MarkdownPreviewConfig; + + private extraStyles: Array = []; + private extraScripts: Array = []; + + constructor( + private engine: MarkdownEngine, + private context: vscode.ExtensionContext, + private cspArbiter: ContentSecurityPolicyArbiter, + private logger: Logger + ) { + this.config = MarkdownPreviewConfig.getCurrentConfig(); + } + + public addScript(resource: vscode.Uri): void { + this.extraScripts.push(resource); + } + + public addStyle(resource: vscode.Uri): void { + this.extraStyles.push(resource); + } + + private getMediaPath(mediaFile: string): string { + return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))).toString(); + } + + private fixHref(resource: vscode.Uri, href: string): string { + if (!href) { + return href; + } + + // Use href if it is already an URL + const hrefUri = vscode.Uri.parse(href); + if (['file', 'http', 'https'].indexOf(hrefUri.scheme) >= 0) { + return hrefUri.toString(); + } + + // Use href as file URI if it is absolute + if (path.isAbsolute(href)) { + return vscode.Uri.file(href).toString(); + } + + // use a workspace relative path if there is a workspace + let root = vscode.workspace.getWorkspaceFolder(resource); + if (root) { + return vscode.Uri.file(path.join(root.uri.fsPath, href)).toString(); + } + + // otherwise look relative to the markdown file + return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString(); + } + + private computeCustomStyleSheetIncludes(uri: vscode.Uri): string { + if (this.config.styles && Array.isArray(this.config.styles)) { + return this.config.styles.map((style) => { + return ``; + }).join('\n'); + } + return ''; + } + + private getSettingsOverrideStyles(nonce: string): string { + return ``; + } + + private getStyles(resource: vscode.Uri, nonce: string): string { + const baseStyles = [ + this.getMediaPath('markdown.css'), + this.getMediaPath('tomorrow.css') + ].concat(this.extraStyles.map(resource => resource.toString())); + + return `${baseStyles.map(href => ``).join('\n')} + ${this.getSettingsOverrideStyles(nonce)} + ${this.computeCustomStyleSheetIncludes(resource)}`; + } + + private getScripts(nonce: string): string { + const scripts = [this.getMediaPath('main.js')].concat(this.extraScripts.map(resource => resource.toString())); + return scripts + .map(source => ``) + .join('\n'); + } + + public async provideTextDocumentContent(uri: vscode.Uri): Promise { + const sourceUri = vscode.Uri.parse(uri.query); + + let initialLine: number | undefined = undefined; + const editor = vscode.window.activeTextEditor; + if (editor && editor.document.uri.fsPath === sourceUri.fsPath) { + initialLine = editor.selection.active.line; + } + + const document = await vscode.workspace.openTextDocument(sourceUri); + this.config = MarkdownPreviewConfig.getCurrentConfig(); + + const initialData = { + previewUri: uri.toString(), + source: sourceUri.toString(), + line: initialLine, + scrollPreviewWithEditorSelection: this.config.scrollPreviewWithEditorSelection, + scrollEditorWithPreview: this.config.scrollEditorWithPreview, + doubleClickToSwitchToEditor: this.config.doubleClickToSwitchToEditor + }; + + this.logger.log('provideTextDocumentContent', initialData); + + // Content Security Policy + const nonce = new Date().getTime() + '' + new Date().getMilliseconds(); + const csp = this.getCspForResource(sourceUri, nonce); + + const body = await this.engine.render(sourceUri, this.config.previewFrontMatter === 'hide', document.getText()); + return ` + + + + ${csp} + + + + ${this.getStyles(sourceUri, nonce)} + + + + ${body} +
+ ${this.getScripts(nonce)} + + `; + } + + public updateConfiguration() { + const newConfig = MarkdownPreviewConfig.getCurrentConfig(); + if (!this.config.isEqualTo(newConfig)) { + this.config = newConfig; + // update all generated md documents + for (const document of vscode.workspace.textDocuments) { + if (document.uri.scheme === 'markdown') { + this.update(document.uri); + } + } + } + } + + get onDidChange(): vscode.Event { + return this._onDidChange.event; + } + + public update(uri: vscode.Uri) { + if (!this._waiting) { + this._waiting = true; + setTimeout(() => { + this._waiting = false; + this._onDidChange.fire(uri); + }, 300); + } + } + + private getCspForResource(resource: vscode.Uri, nonce: string): string { + switch (this.cspArbiter.getSecurityLevelForResource(resource)) { + case MarkdownPreviewSecurityLevel.AllowInsecureContent: + return ``; + + case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent: + return ''; + + case MarkdownPreviewSecurityLevel.Strict: + default: + return ``; + } + } +} diff --git a/extensions/markdown/src/security.ts b/extensions/markdown/src/security.ts new file mode 100644 index 0000000000..b9cf989123 --- /dev/null +++ b/extensions/markdown/src/security.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as vscode from 'vscode'; + +import { getMarkdownUri, MDDocumentContentProvider } from './previewContentProvider'; + +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +export enum MarkdownPreviewSecurityLevel { + Strict = 0, + AllowInsecureContent = 1, + AllowScriptsAndAllContent = 2 +} + +export interface ContentSecurityPolicyArbiter { + getSecurityLevelForResource(resource: vscode.Uri): MarkdownPreviewSecurityLevel; + + setSecurityLevelForResource(resource: vscode.Uri, level: MarkdownPreviewSecurityLevel): Thenable; + + shouldAllowSvgsForResource(resource: vscode.Uri): void; +} + +export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPolicyArbiter { + private readonly old_trusted_workspace_key = 'trusted_preview_workspace:'; + private readonly security_level_key = 'preview_security_level:'; + + constructor( + private globalState: vscode.Memento + ) { } + + public getSecurityLevelForResource(resource: vscode.Uri): MarkdownPreviewSecurityLevel { + // Use new security level setting first + const level = this.globalState.get(this.security_level_key + this.getRoot(resource), undefined); + if (typeof level !== 'undefined') { + return level; + } + + // Fallback to old trusted workspace setting + if (this.globalState.get(this.old_trusted_workspace_key + this.getRoot(resource), false)) { + return MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent; + } + return MarkdownPreviewSecurityLevel.Strict; + } + + public setSecurityLevelForResource(resource: vscode.Uri, level: MarkdownPreviewSecurityLevel): Thenable { + return this.globalState.update(this.security_level_key + this.getRoot(resource), level); + } + + public shouldAllowSvgsForResource(resource: vscode.Uri) { + const securityLevel = this.getSecurityLevelForResource(resource); + return securityLevel === MarkdownPreviewSecurityLevel.AllowInsecureContent || securityLevel === MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent; + } + + private getRoot(resource: vscode.Uri): vscode.Uri { + if (vscode.workspace.workspaceFolders) { + const folderForResource = vscode.workspace.getWorkspaceFolder(resource); + if (folderForResource) { + return folderForResource.uri; + } + + if (vscode.workspace.workspaceFolders.length) { + return vscode.workspace.workspaceFolders[0].uri; + } + } + + return resource; + } +} + +export class PreviewSecuritySelector { + + public constructor( + private cspArbiter: ContentSecurityPolicyArbiter, + private contentProvider: MDDocumentContentProvider + ) { } + + public async showSecutitySelectorForResource(resource: vscode.Uri): Promise { + interface PreviewSecurityPickItem extends vscode.QuickPickItem { + type: 'moreinfo' | MarkdownPreviewSecurityLevel; + } + + function markActiveWhen(when: boolean): string { + return when ? '• ' : ''; + } + + const currentSecurityLevel = this.cspArbiter.getSecurityLevelForResource(resource); + const selection = await vscode.window.showQuickPick( + [ + { + type: MarkdownPreviewSecurityLevel.Strict, + label: markActiveWhen(currentSecurityLevel === MarkdownPreviewSecurityLevel.Strict) + localize('strict.title', 'Strict'), + description: localize('strict.description', 'Only load secure content'), + }, { + type: MarkdownPreviewSecurityLevel.AllowInsecureContent, + label: markActiveWhen(currentSecurityLevel === MarkdownPreviewSecurityLevel.AllowInsecureContent) + localize('insecureContent.title', 'Allow insecure content'), + description: localize('insecureContent.description', 'Enable loading content over http'), + }, { + type: MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent, + label: markActiveWhen(currentSecurityLevel === MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent) + localize('disable.title', 'Disable'), + description: localize('disable.description', 'Allow all content and script execution. Not recommended'), + }, { + type: 'moreinfo', + label: localize('moreInfo.title', 'More Information'), + description: '' + } + ], { + placeHolder: localize( + 'preview.showPreviewSecuritySelector.title', + 'Select security settings for Markdown previews in this workspace'), + }); + + if (!selection) { + return; + } + + if (selection.type === 'moreinfo') { + vscode.commands.executeCommand('vscode.open', vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=854414')); + return; + } + + await this.cspArbiter.setSecurityLevelForResource(resource, selection.type); + + const sourceUri = getMarkdownUri(resource); + + await vscode.commands.executeCommand('_workbench.htmlPreview.updateOptions', + sourceUri, + { + allowScripts: true, + allowSvgs: this.cspArbiter.shouldAllowSvgsForResource(resource) + }); + + this.contentProvider.update(sourceUri); + } +} diff --git a/extensions/markdown/src/tableOfContentsProvider.ts b/extensions/markdown/src/tableOfContentsProvider.ts new file mode 100644 index 0000000000..4a896b4007 --- /dev/null +++ b/extensions/markdown/src/tableOfContentsProvider.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as vscode from 'vscode'; + +import { MarkdownEngine } from './markdownEngine'; + +export interface TocEntry { + slug: string; + text: string; + level: number; + line: number; + location: vscode.Location; +} + +export class TableOfContentsProvider { + private toc: TocEntry[]; + + public constructor( + private engine: MarkdownEngine, + private document: vscode.TextDocument + ) { } + + public async getToc(): Promise { + if (!this.toc) { + try { + this.toc = await this.buildToc(this.document); + } catch (e) { + this.toc = []; + } + } + return this.toc; + } + + public async lookup(fragment: string): Promise { + const slug = TableOfContentsProvider.slugify(fragment); + for (const entry of await this.getToc()) { + if (entry.slug === slug) { + return entry.line; + } + } + return NaN; + } + + private async buildToc(document: vscode.TextDocument): Promise { + const toc: TocEntry[] = []; + const tokens = await this.engine.parse(document.uri, document.getText()); + + for (const heading of tokens.filter(token => token.type === 'heading_open')) { + const lineNumber = heading.map[0]; + const line = document.lineAt(lineNumber); + const href = TableOfContentsProvider.slugify(line.text); + const level = TableOfContentsProvider.getHeaderLevel(heading.markup); + if (href) { + toc.push({ + slug: href, + text: TableOfContentsProvider.getHeaderText(line.text), + level: level, + line: lineNumber, + location: new vscode.Location(document.uri, line.range) + }); + } + } + return toc; + } + + private static getHeaderLevel(markup: string): number { + if (markup === '=') { + return 1; + } else if (markup === '-') { + return 2; + } else { // '#', '##', ... + return markup.length; + } + } + + private static getHeaderText(header: string): string { + return header.replace(/^\s*#+\s*(.*?)\s*\1*$/, (_, word) => `${word.trim()}`); + } + + public static slugify(header: string): string { + return encodeURI(header.trim() + .toLowerCase() + .replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`]/g, '') + .replace(/\s+/g, '-') + .replace(/^\-+/, '') + .replace(/\-+$/, '')); + } +} + diff --git a/extensions/markdown/src/typings/markdown-it-named-headers.d.ts b/extensions/markdown/src/typings/markdown-it-named-headers.d.ts new file mode 100644 index 0000000000..b525589725 --- /dev/null +++ b/extensions/markdown/src/typings/markdown-it-named-headers.d.ts @@ -0,0 +1,5 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +declare module 'markdown-it-named-headers' { } \ No newline at end of file diff --git a/extensions/markdown/src/typings/ref.d.ts b/extensions/markdown/src/typings/ref.d.ts new file mode 100644 index 0000000000..acf3ed8429 --- /dev/null +++ b/extensions/markdown/src/typings/ref.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions/markdown/syntaxes/gulpfile.js b/extensions/markdown/syntaxes/gulpfile.js new file mode 100644 index 0000000000..e04c73fe1d --- /dev/null +++ b/extensions/markdown/syntaxes/gulpfile.js @@ -0,0 +1,149 @@ +var gulp = require('gulp'); +var replace = require('gulp-replace'); +var rename = require('gulp-rename'); + +const languages = [ + { name: 'css', identifiers: ['css', 'css.erb'], source: 'source.css' }, + { name: 'basic', identifiers: ['html', 'htm', 'shtml', 'xhtml', 'inc', 'tmpl', 'tpl'], source: 'text.html.basic' }, + { name: 'ini', identifiers: ['ini', 'conf'], source: 'source.ini' }, + { name: 'java', identifiers: ['java', 'bsh'], source: 'source.java' }, + { name: 'lua', identifiers: ['lua'], source: 'source.lua' }, + { name: 'makefile', identifiers: ['Makefile', 'makefile', 'GNUmakefile', 'OCamlMakefile'], source: 'source.makefile' }, + { name: 'perl', identifiers: ['perl', 'pl', 'pm', 'pod', 't', 'PL', 'psgi', 'vcl'], source: 'source.perl' }, + { name: 'r', identifiers: ['R', 'r', 's', 'S', 'Rprofile'], source: 'source.r' }, + { name: 'ruby', identifiers: ['ruby', 'rb', 'rbx', 'rjs', 'Rakefile', 'rake', 'cgi', 'fcgi', 'gemspec', 'irbrc', 'Capfile', 'ru', 'prawn', 'Cheffile', 'Gemfile', 'Guardfile', 'Hobofile', 'Vagrantfile', 'Appraisals', 'Rantfile', 'Berksfile', 'Berksfile.lock', 'Thorfile', 'Puppetfile'], source: 'source.ruby' }, + // Left to its own devices, the PHP grammar will match HTML as a combination of operators + // and constants. Therefore, HTML must take precedence over PHP in order to get proper + // syntax highlighting. + { name: 'php', identifiers: ['php', 'php3', 'php4', 'php5', 'phpt', 'phtml', 'aw', 'ctp'], source: ['text.html.basic', 'text.html.php#language'] }, + { name: 'sql', identifiers: ['sql', 'ddl', 'dml'], source: 'source.sql' }, + { name: 'vs_net', identifiers: ['vb'], source: 'source.asp.vb.net' }, + { name: 'xml', identifiers: ['xml', 'xsd', 'tld', 'jsp', 'pt', 'cpt', 'dtml', 'rss', 'opml'], source: 'text.xml' }, + { name: 'xsl', identifiers: ['xsl', 'xslt'], source: 'text.xml.xsl' }, + { name: 'yaml', identifiers: ['yaml', 'yml'], source: 'source.yaml' }, + { name: 'dosbatch', identifiers: ['bat', 'batch'], source: 'source.dosbatch' }, + { name: 'clojure', identifiers: ['clj', 'cljs', 'clojure'], source: 'source.clojure' }, + { name: 'coffee', identifiers: ['coffee', 'Cakefile', 'coffee.erb'], source: 'source.coffee' }, + { name: 'c', identifiers: ['c', 'h'], source: 'source.c' }, + { name: 'cpp', identifiers: ['cpp', 'c\\+\\+', 'cxx'], source: 'source.cpp' }, + { name: 'diff', identifiers: ['patch', 'diff', 'rej'], source: 'source.diff' }, + { name: 'dockerfile', identifiers: ['dockerfile', 'Dockerfile'], source: 'source.dockerfile' }, + { name: 'git_commit', identifiers: ['COMMIT_EDITMSG', 'MERGE_MSG'], source: 'text.git-commit' }, + { name: 'git_rebase', identifiers: ['git-rebase-todo'], source: 'text.git-rebase' }, + { name: 'go', identifiers: ['go', 'golang'], source: 'source.go' }, + { name: 'groovy', identifiers: ['groovy', 'gvy'], source: 'source.groovy' }, + { name: 'jade', identifiers: ['jade', 'pug'], source: 'text.jade' }, + + { name: 'js', identifiers: ['js', 'jsx', 'javascript', 'es6', 'mjs'], source: 'source.js' }, + { name: 'js_regexp', identifiers: ['regexp'], source: 'source.js.regexp' }, + { name: 'json', identifiers: ['json', 'sublime-settings', 'sublime-menu', 'sublime-keymap', 'sublime-mousemap', 'sublime-theme', 'sublime-build', 'sublime-project', 'sublime-completions'], source: 'source.json' }, + { name: 'less', identifiers: ['less'], source: 'source.css.less' }, + { name: 'objc', identifiers: ['objectivec', 'objective-c', 'mm', 'objc', 'obj-c', 'm', 'h'], source: 'source.objc' }, + { name: 'scss', identifiers: ['scss'], source: 'source.css.scss' }, + + { name: 'perl6', identifiers: ['perl6', 'p6', 'pl6', 'pm6', 'nqp'], source: 'source.perl.6' }, + { name: 'powershell', identifiers: ['powershell', 'ps1', 'psm1', 'psd1'], source: 'source.powershell' }, + { name: 'python', identifiers: ['python', 'py', 'py3', 'rpy', 'pyw', 'cpy', 'SConstruct', 'Sconstruct', 'sconstruct', 'SConscript', 'gyp', 'gypi'], source: 'source.python' }, + { name: 'regexp_python', identifiers: ['re'], source: 'source.regexp.python' }, + { name: 'rust', identifiers: ['rust', 'rs'], source: 'source.rust' }, + { name: 'scala', identifiers: ['scala', 'sbt'], source: 'source.scala' }, + { name: 'shell', identifiers: ['shell', 'sh', 'bash', 'zsh', 'bashrc', 'bash_profile', 'bash_login', 'profile', 'bash_logout', '.textmate_init'], source: 'source.shell' }, + { name: 'ts', identifiers: ['typescript', 'ts'], source: 'source.ts' }, + { name: 'tsx', identifiers: ['tsx'], source: 'source.tsx' }, + { name: 'csharp', identifiers: ['cs', 'csharp', 'c#'], source: 'source.cs' }, + { name: 'fsharp', identifiers: ['fs', 'fsharp', 'f#'], source: 'source.fsharp' }, +]; + +const fencedCodeBlockDefinition = (name, identifiers, sourceScope) => { + if (!Array.isArray(sourceScope)) { + sourceScope = [sourceScope]; + } + + const scopes = sourceScope.map(scope => + ` + include + ${scope} +`).join('\n'); + + return `fenced_code_block_${name} + + begin + (^|\\G)(\\s*)(\`{3,}|~{3,})\\s*(?i:(${identifiers.join('|')})(\\s+[^\`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\\G)(\\2|\\s{0,3})(\\3)\\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\\G)(\\s*)(.*) + while + (^|\\G)(?!\\s*([\`~]{3,})\\s*$) + patterns + +${indent(4, scopes)} + + + +`; +}; + +const indent = (count, text) => { + const indent = new Array(count + 1).join('\t'); + return text.replace(/^/gm, indent); +}; + +const fencedCodeBlockInclude = (name) => + ` + include + #fenced_code_block_${name} +`; + + +const fencedCodeBlockDefinitions = () => + languages + .map(language => fencedCodeBlockDefinition(language.name, language.identifiers, language.source)) + .join('\n'); + + + +const fencedCodeBlockIncludes = () => + languages + .map(language => fencedCodeBlockInclude(language.name)) + .join('\n'); + + +gulp.task('default', function () { + gulp.src(['markdown.tmLanguage.base']) + .pipe(replace('{{languageIncludes}}', indent(4, fencedCodeBlockIncludes()))) + .pipe(replace('{{languageDefinitions}}', indent(4, fencedCodeBlockDefinitions()))) + .pipe(rename('markdown.tmLanguage')) + .pipe(gulp.dest('.')); +}); diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage b/extensions/markdown/syntaxes/markdown.tmLanguage new file mode 100644 index 0000000000..552aec2100 --- /dev/null +++ b/extensions/markdown/syntaxes/markdown.tmLanguage @@ -0,0 +1,3624 @@ + + + + + fileTypes + + md + mdown + markdown + markdn + + keyEquivalent + ^~M + name + Markdown + patterns + + + include + #frontMatter + + + include + #block + + + repository + + block + + patterns + + + include + #separator + + + include + #heading + + + include + #blockquote + + + include + #lists + + + include + #fenced_code_block_css + + + include + #fenced_code_block_basic + + + include + #fenced_code_block_ini + + + include + #fenced_code_block_java + + + include + #fenced_code_block_lua + + + include + #fenced_code_block_makefile + + + include + #fenced_code_block_perl + + + include + #fenced_code_block_r + + + include + #fenced_code_block_ruby + + + include + #fenced_code_block_php + + + include + #fenced_code_block_sql + + + include + #fenced_code_block_vs_net + + + include + #fenced_code_block_xml + + + include + #fenced_code_block_xsl + + + include + #fenced_code_block_yaml + + + include + #fenced_code_block_dosbatch + + + include + #fenced_code_block_clojure + + + include + #fenced_code_block_coffee + + + include + #fenced_code_block_c + + + include + #fenced_code_block_cpp + + + include + #fenced_code_block_diff + + + include + #fenced_code_block_dockerfile + + + include + #fenced_code_block_git_commit + + + include + #fenced_code_block_git_rebase + + + include + #fenced_code_block_go + + + include + #fenced_code_block_groovy + + + include + #fenced_code_block_jade + + + include + #fenced_code_block_js + + + include + #fenced_code_block_js_regexp + + + include + #fenced_code_block_json + + + include + #fenced_code_block_less + + + include + #fenced_code_block_objc + + + include + #fenced_code_block_scss + + + include + #fenced_code_block_perl6 + + + include + #fenced_code_block_powershell + + + include + #fenced_code_block_python + + + include + #fenced_code_block_regexp_python + + + include + #fenced_code_block_rust + + + include + #fenced_code_block_scala + + + include + #fenced_code_block_shell + + + include + #fenced_code_block_ts + + + include + #fenced_code_block_tsx + + + include + #fenced_code_block_csharp + + + include + #fenced_code_block_fsharp + + + include + #fenced_code_block_unknown + + + include + #raw_block + + + include + #link-def + + + include + #html + + + include + #paragraph + + + repository + + blockquote + + begin + (^|\G)[ ]{0,3}(>) ? + captures + + 2 + + name + beginning.punctuation.definition.quote.markdown + + + name + markup.quote.markdown + patterns + + + include + #block + + + while + (^|\G)\s*(>) ? + + heading + + begin + (?:^|\G)[ ]{0,3}(#{1,6})\s*(?=[\S[^#]]) + captures + + 1 + + name + punctuation.definition.heading.markdown + + + contentName + entity.name.section.markdown + end + \s*(#{1,6})?$\n? + name + markup.heading.markdown + patterns + + + include + #inline + + + + heading-setext + + patterns + + + match + ^(={3,})(?=[ \t]*$\n?) + name + markup.heading.setext.1.markdown + + + match + ^(-{3,})(?=[ \t]*$\n?) + name + markup.heading.setext.2.markdown + + + + html + + patterns + + + begin + (^|\G)\s*(<!--) + end + (-->) + name + comment.block.html + captures + + 1 + + name + punctuation.definition.comment.html + + 2 + + name + punctuation.definition.comment.html + + + + + begin + (^|\G)\s*(?=<(script|style|pre)(\s|$|>)(?!.*?</(script|style|pre)>)) + patterns + + + begin + (\s*|$) + patterns + + + include + text.html.basic + + + while + ^\s*(?!</(script|style|pre)>) + + + end + (?=</(script|style|pre)>) + + + begin + (^|\G)\s*(?=</?(address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h1|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(\s|$|/?>)) + patterns + + + include + text.html.basic + + + while + ^(?!\s*$) + + + begin + (^|\G)\s*(?=(<[a-zA-Z0-9\-](/?>|\s.*?>)|</[a-zA-Z0-9\-]>)\s*$) + patterns + + + include + text.html.basic + + + while + ^(?!\s*$) + + + + link-def + + captures + + 1 + + name + punctuation.definition.constant.markdown + + 10 + + name + punctuation.definition.string.end.markdown + + 11 + + name + string.other.link.description.title.markdown + + 12 + + name + punctuation.definition.string.begin.markdown + + 13 + + name + punctuation.definition.string.end.markdown + + 2 + + name + constant.other.reference.link.markdown + + 3 + + name + punctuation.definition.constant.markdown + + 4 + + name + punctuation.separator.key-value.markdown + + 5 + + name + punctuation.definition.link.markdown + + 6 + + name + markup.underline.link.markdown + + 7 + + name + punctuation.definition.link.markdown + + 8 + + name + string.other.link.description.title.markdown + + 9 + + name + punctuation.definition.string.begin.markdown + + + match + ^(?x: + \s* # Leading whitespace + (\[)(.+?)(\])(:) # Reference name + [ \t]* # Optional whitespace + (<?)(\S+?)(>?) # The url + [ \t]* # Optional whitespace + (?: + ((\().+?(\))) # Match title in quotes… + | ((").+?(")) # or in parens. + )? # Title is optional + \s* # Optional whitespace + $ + ) + name + meta.link.reference.def.markdown + + list_paragraph + + begin + (^|\G)(?=\S)(?![*+-]\s|[0-9]+\.\s) + name + meta.paragraph.markdown + patterns + + + include + #inline + + + include + text.html.basic + + + include + #heading-setext + + + while + (^|\G)(?!\s*$|#|[ ]{0,3}([-*_][ ]{2,}){3,}[ \t]*$\n?|>|[ ]{0,3}[*+-]|[ ]{0,3}[0-9]+\.) + + lists + + patterns + + + begin + (^|\G)([ ]{0,3})([*+-])([ ]{1,3}|\t) + beginCaptures + + 3 + + name + beginning.punctuation.definition.list.markdown + + + comment + Currently does not support un-indented second lines. + name + markup.list.unnumbered.markdown + patterns + + + include + #list_paragraph + + + include + #block + + + while + ((^|\G)([ ]{4}|\t))|(^[ \t]*$) + + + begin + (^|\G)([ ]{0,3})([0-9]+\.)([ ]{1,3}|\t) + beginCaptures + + 3 + + name + beginning.punctuation.definition.list.markdown + + + name + markup.list.numbered.markdown + patterns + + + include + #list_paragraph + + + include + #block + + + while + ((^|\G)([ ]{4}|\t))|(^[ \t]*$) + + + + paragraph + + begin + (^|\G)[ ]{0,3}(?=\S) + name + meta.paragraph.markdown + patterns + + + include + #inline + + + include + text.html.basic + + + include + #heading-setext + + + while + (^|\G)((?=\s*[-=]{3,}\s*$)|[ ]{4,}(?=\S)) + + fenced_code_block_css + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(css|css.erb)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.css + + + + + + fenced_code_block_basic + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(html|htm|shtml|xhtml|inc|tmpl|tpl)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.html.basic + + + + + + fenced_code_block_ini + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(ini|conf)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.ini + + + + + + fenced_code_block_java + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(java|bsh)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.java + + + + + + fenced_code_block_lua + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(lua)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.lua + + + + + + fenced_code_block_makefile + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(Makefile|makefile|GNUmakefile|OCamlMakefile)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.makefile + + + + + + fenced_code_block_perl + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(perl|pl|pm|pod|t|PL|psgi|vcl)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.perl + + + + + + fenced_code_block_r + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(R|r|s|S|Rprofile)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.r + + + + + + fenced_code_block_ruby + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(ruby|rb|rbx|rjs|Rakefile|rake|cgi|fcgi|gemspec|irbrc|Capfile|ru|prawn|Cheffile|Gemfile|Guardfile|Hobofile|Vagrantfile|Appraisals|Rantfile|Berksfile|Berksfile.lock|Thorfile|Puppetfile)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.ruby + + + + + + fenced_code_block_php + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(php|php3|php4|php5|phpt|phtml|aw|ctp)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.html.basic + + + include + text.html.php#language + + + + + + fenced_code_block_sql + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(sql|ddl|dml)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.sql + + + + + + fenced_code_block_vs_net + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(vb)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.asp.vb.net + + + + + + fenced_code_block_xml + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(xml|xsd|tld|jsp|pt|cpt|dtml|rss|opml)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.xml + + + + + + fenced_code_block_xsl + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(xsl|xslt)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.xml.xsl + + + + + + fenced_code_block_yaml + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(yaml|yml)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.yaml + + + + + + fenced_code_block_dosbatch + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(bat|batch)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.dosbatch + + + + + + fenced_code_block_clojure + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(clj|cljs|clojure)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.clojure + + + + + + fenced_code_block_coffee + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(coffee|Cakefile|coffee.erb)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.coffee + + + + + + fenced_code_block_c + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(c|h)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.c + + + + + + fenced_code_block_cpp + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(cpp|c\+\+|cxx)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.cpp + + + + + + fenced_code_block_diff + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(patch|diff|rej)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.diff + + + + + + fenced_code_block_dockerfile + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(dockerfile|Dockerfile)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.dockerfile + + + + + + fenced_code_block_git_commit + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(COMMIT_EDITMSG|MERGE_MSG)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.git-commit + + + + + + fenced_code_block_git_rebase + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(git-rebase-todo)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.git-rebase + + + + + + fenced_code_block_go + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(go|golang)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.go + + + + + + fenced_code_block_groovy + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(groovy|gvy)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.groovy + + + + + + fenced_code_block_jade + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(jade|pug)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.jade + + + + + + fenced_code_block_js + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(js|jsx|javascript|es6|mjs)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.js + + + + + + fenced_code_block_js_regexp + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(regexp)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.js.regexp + + + + + + fenced_code_block_json + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(json|sublime-settings|sublime-menu|sublime-keymap|sublime-mousemap|sublime-theme|sublime-build|sublime-project|sublime-completions)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.json + + + + + + fenced_code_block_less + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(less)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.css.less + + + + + + fenced_code_block_objc + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(objectivec|objective-c|mm|objc|obj-c|m|h)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.objc + + + + + + fenced_code_block_scss + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(scss)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.css.scss + + + + + + fenced_code_block_perl6 + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(perl6|p6|pl6|pm6|nqp)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.perl.6 + + + + + + fenced_code_block_powershell + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(powershell|ps1|psm1|psd1)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.powershell + + + + + + fenced_code_block_python + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(python|py|py3|rpy|pyw|cpy|SConstruct|Sconstruct|sconstruct|SConscript|gyp|gypi)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.python + + + + + + fenced_code_block_regexp_python + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(re)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.regexp.python + + + + + + fenced_code_block_rust + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(rust|rs)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.rust + + + + + + fenced_code_block_scala + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(scala|sbt)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.scala + + + + + + fenced_code_block_shell + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(shell|sh|bash|zsh|bashrc|bash_profile|bash_login|profile|bash_logout|.textmate_init)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.shell + + + + + + fenced_code_block_ts + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(typescript|ts)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.ts + + + + + + fenced_code_block_tsx + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(tsx)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.tsx + + + + + + fenced_code_block_csharp + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(cs|csharp|c#)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.cs + + + + + + fenced_code_block_fsharp + + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(fs|fsharp|f#)(\s+[^`~]*)?$) + name + markup.fenced_code.block.markdown + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns + + + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.fsharp + + + + + + fenced_code_block_unknown + + name + markup.fenced_code.block.markdown + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?=([^`~]*)?$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 4 + + name + fenced_code.block.language + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + + raw_block + + begin + (^|\G)([ ]{4}|\t) + name + markup.raw.block.markdown + while + (^|\G)([ ]{4}|\t) + + separator + + match + (^|\G)[ ]{0,3}([*-_])([ ]{0,2}\2){2,}[ \t]*$\n? + name + meta.separator.markdown + + + + inline + + patterns + + + include + #ampersand + + + include + #bracket + + + include + #bold + + + include + #italic + + + include + #raw + + + include + #escape + + + include + #image-inline + + + include + #image-ref + + + include + #link-email + + + include + #link-inet + + + include + #link-inline + + + include + #link-ref + + + include + #link-ref-literal + + + repository + + ampersand + + comment + + Markdown will convert this for us. We match it so that the + HTML grammar will not mark it up as invalid. + + match + &(?!([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+);) + name + meta.other.valid-ampersand.markdown + + bold + + begin + (?x) + (\*\*|__)(?=\S) # Open + (?= + ( + <[^>]*+> # HTML tags + | (?<raw>`+)([^`]|(?!(?<!`)\k<raw>(?!`))`)*+\k<raw> + # Raw + | \\[\\`*_{}\[\]()#.!+\->]?+ # Escapes + | \[ + ( + (?<square> # Named group + [^\[\]\\] # Match most chars + | \\. # Escaped chars + | \[ \g<square>*+ \] # Nested brackets + )*+ + \] + ( + ( # Reference Link + [ ]? # Optional space + \[[^\]]*+\] # Ref name + ) + | ( # Inline Link + \( # Opening paren + [ \t]*+ # Optional whitespace + <?(.*?)>? # URL + [ \t]*+ # Optional whitespace + ( # Optional Title + (?<title>['"]) + (.*?) + \k<title> + )? + \) + ) + ) + ) + | (?!(?<=\S)\1). # Everything besides + # style closer + )++ + (?<=\S)\1 # Close + ) + + captures + + 1 + + name + punctuation.definition.bold.markdown + + + end + (?<=\S)(\1) + name + markup.bold.markdown + patterns + + + applyEndPatternLast + 1 + begin + (?=<[^>]*?>) + end + (?<=>) + patterns + + + include + text.html.basic + + + + + include + #escape + + + include + #ampersand + + + include + #bracket + + + include + #raw + + + include + #italic + + + include + #image-inline + + + include + #link-inline + + + include + #link-inet + + + include + #link-email + + + include + #image-ref + + + include + #link-ref-literal + + + include + #link-ref + + + + bracket + + comment + + Markdown will convert this for us. We match it so that the + HTML grammar will not mark it up as invalid. + + match + <(?![a-z/?\$!]) + name + meta.other.valid-bracket.markdown + + escape + + match + \\[-`*_#+.!(){}\[\]\\>] + name + constant.character.escape.markdown + + image-inline + + captures + + 1 + + name + punctuation.definition.string.begin.markdown + + 10 + + name + string.other.link.description.title.markdown + + 11 + + name + punctuation.definition.string.markdown + + 12 + + name + punctuation.definition.string.markdown + + 13 + + name + string.other.link.description.title.markdown + + 14 + + name + punctuation.definition.string.markdown + + 15 + + name + punctuation.definition.string.markdown + + 16 + + name + punctuation.definition.metadata.markdown + + 2 + + name + string.other.link.description.markdown + + 4 + + name + punctuation.definition.string.end.markdown + + 5 + + name + invalid.illegal.whitespace.markdown + + 6 + + name + punctuation.definition.metadata.markdown + + 7 + + name + punctuation.definition.link.markdown + + 8 + + name + markup.underline.link.image.markdown + + 9 + + name + punctuation.definition.link.markdown + + + match + (?x: + (\!\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\]) + # Match the link text. + ([ ])? # Space not allowed + (\() # Opening paren for url + (<?)(\S+?)(>?) # The url + [ \t]* # Optional whitespace + (?: + ((\().+?(\))) # Match title in parens… + | ((").+?(")) # or in quotes. + )? # Title is optional + \s* # Optional whitespace + (\)) + ) + name + meta.image.inline.markdown + + image-ref + + captures + + 1 + + name + punctuation.definition.string.begin.markdown + + 2 + + name + string.other.link.description.markdown + + 4 + + name + punctuation.definition.string.begin.markdown + + 5 + + name + punctuation.definition.constant.markdown + + 6 + + name + constant.other.reference.link.markdown + + 7 + + name + punctuation.definition.constant.markdown + + + match + (\!\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\])[ ]?(\[)(.*?)(\]) + name + meta.image.reference.markdown + + italic + + begin + (?x) + (\*|_)(?=\S) # Open + (?= + ( + <[^>]*+> # HTML tags + | (?<raw>`+)([^`]|(?!(?<!`)\k<raw>(?!`))`)*+\k<raw> + # Raw + | \\[\\`*_{}\[\]()#.!+\->]?+ # Escapes + | \[ + ( + (?<square> # Named group + [^\[\]\\] # Match most chars + | \\. # Escaped chars + | \[ \g<square>*+ \] # Nested brackets + )*+ + \] + ( + ( # Reference Link + [ ]? # Optional space + \[[^\]]*+\] # Ref name + ) + | ( # Inline Link + \( # Opening paren + [ \t]*+ # Optional whtiespace + <?(.*?)>? # URL + [ \t]*+ # Optional whtiespace + ( # Optional Title + (?<title>['"]) + (.*?) + \k<title> + )? + \) + ) + ) + ) + | \1\1 # Must be bold closer + | (?!(?<=\S)\1). # Everything besides + # style closer + )++ + (?<=\S)\1 # Close + ) + + captures + + 1 + + name + punctuation.definition.italic.markdown + + + end + (?<=\S)(\1)((?!\1)|(?=\1\1)) + name + markup.italic.markdown + patterns + + + applyEndPatternLast + 1 + begin + (?=<[^>]*?>) + end + (?<=>) + patterns + + + include + text.html.basic + + + + + include + #escape + + + include + #ampersand + + + include + #bracket + + + include + #raw + + + include + #bold + + + include + #image-inline + + + include + #link-inline + + + include + #link-inet + + + include + #link-email + + + include + #image-ref + + + include + #link-ref-literal + + + include + #link-ref + + + + link-email + + captures + + 1 + + name + punctuation.definition.link.markdown + + 2 + + name + markup.underline.link.markdown + + 4 + + name + punctuation.definition.link.markdown + + + match + (<)((?:mailto:)?[-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(>) + name + meta.link.email.lt-gt.markdown + + link-inet + + captures + + 1 + + name + punctuation.definition.link.markdown + + 2 + + name + markup.underline.link.markdown + + 3 + + name + punctuation.definition.link.markdown + + + match + (<)((?:https?|ftp)://.*?)(>) + name + meta.link.inet.markdown + + link-inline + + captures + + 1 + + name + punctuation.definition.string.begin.markdown + + 10 + + name + string.other.link.description.title.markdown + + 11 + + name + punctuation.definition.string.begin.markdown + + 12 + + name + punctuation.definition.string.end.markdown + + 13 + + name + string.other.link.description.title.markdown + + 14 + + name + punctuation.definition.string.begin.markdown + + 15 + + name + punctuation.definition.string.end.markdown + + 16 + + name + punctuation.definition.metadata.markdown + + 2 + + name + string.other.link.title.markdown + + 4 + + name + punctuation.definition.string.end.markdown + + 5 + + name + invalid.illegal.whitespace.markdown + + 6 + + name + punctuation.definition.metadata.markdown + + 7 + + name + punctuation.definition.link.markdown + + 8 + + name + markup.underline.link.markdown + + 9 + + name + punctuation.definition.link.markdown + + + match + (?x: + (\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\]) + # Match the link text. + ([ ])? # Space not allowed + (\() # Opening paren for url + (<?)(.*?)(>?) # The url + [ \t]* # Optional whitespace + (?: + ((\().+?(\))) # Match title in parens… + | ((").+?(")) # or in quotes. + )? # Title is optional + \s* # Optional whitespace + (\)) + ) + name + meta.link.inline.markdown + + link-ref + + captures + + 1 + + name + punctuation.definition.string.begin.markdown + + 2 + + name + string.other.link.title.markdown + + 4 + + name + punctuation.definition.string.end.markdown + + 5 + + name + punctuation.definition.constant.begin.markdown + + 6 + + name + constant.other.reference.link.markdown + + 7 + + name + punctuation.definition.constant.end.markdown + + + match + (\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\])(\[)([^\]]*+)(\]) + name + meta.link.reference.markdown + + link-ref-literal + + captures + + 1 + + name + punctuation.definition.string.begin.markdown + + 2 + + name + string.other.link.title.markdown + + 4 + + name + punctuation.definition.string.end.markdown + + 5 + + name + punctuation.definition.constant.begin.markdown + + 6 + + name + punctuation.definition.constant.end.markdown + + + match + (\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\])[ ]?(\[)(\]) + name + meta.link.reference.literal.markdown + + raw + + captures + + 1 + + name + punctuation.definition.raw.markdown + + 3 + + name + punctuation.definition.raw.markdown + + + match + (`+)([^`]|(?!(?<!`)\1(?!`))`)*+(\1) + name + markup.inline.raw.markdown + + + + frontMatter + + begin + \A-{3}\s*$ + while + ^(?!(-{3}|\.{3})\s*$) + patterns + + + include + source.yaml + + + + + scopeName + text.html.markdown + uuid + 0A1D9874-B448-11D9-BD50-000D93B6E43C + + diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage.base b/extensions/markdown/syntaxes/markdown.tmLanguage.base new file mode 100644 index 0000000000..4c48b6741f --- /dev/null +++ b/extensions/markdown/syntaxes/markdown.tmLanguage.base @@ -0,0 +1,1202 @@ + + + + + fileTypes + + md + mdown + markdown + markdn + + keyEquivalent + ^~M + name + Markdown + patterns + + + include + #frontMatter + + + include + #block + + + repository + + block + + patterns + + + include + #separator + + + include + #heading + + + include + #blockquote + + + include + #lists + +{{languageIncludes}} + + include + #fenced_code_block_unknown + + + include + #raw_block + + + include + #link-def + + + include + #html + + + include + #paragraph + + + repository + + blockquote + + begin + (^|\G)[ ]{0,3}(>) ? + captures + + 2 + + name + beginning.punctuation.definition.quote.markdown + + + name + markup.quote.markdown + patterns + + + include + #block + + + while + (^|\G)\s*(>) ? + + heading + + begin + (?:^|\G)[ ]{0,3}(#{1,6})\s*(?=[\S[^#]]) + captures + + 1 + + name + punctuation.definition.heading.markdown + + + contentName + entity.name.section.markdown + end + \s*(#{1,6})?$\n? + name + markup.heading.markdown + patterns + + + include + #inline + + + + heading-setext + + patterns + + + match + ^(={3,})(?=[ \t]*$\n?) + name + markup.heading.setext.1.markdown + + + match + ^(-{3,})(?=[ \t]*$\n?) + name + markup.heading.setext.2.markdown + + + + html + + patterns + + + begin + (^|\G)\s*(<!--) + end + (-->) + name + comment.block.html + captures + + 1 + + name + punctuation.definition.comment.html + + 2 + + name + punctuation.definition.comment.html + + + + + begin + (^|\G)\s*(?=<(script|style|pre)(\s|$|>)(?!.*?</(script|style|pre)>)) + patterns + + + begin + (\s*|$) + patterns + + + include + text.html.basic + + + while + ^\s*(?!</(script|style|pre)>) + + + end + (?=</(script|style|pre)>) + + + begin + (^|\G)\s*(?=</?(address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h1|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(\s|$|/?>)) + patterns + + + include + text.html.basic + + + while + ^(?!\s*$) + + + begin + (^|\G)\s*(?=(<[a-zA-Z0-9\-](/?>|\s.*?>)|</[a-zA-Z0-9\-]>)\s*$) + patterns + + + include + text.html.basic + + + while + ^(?!\s*$) + + + + link-def + + captures + + 1 + + name + punctuation.definition.constant.markdown + + 10 + + name + punctuation.definition.string.end.markdown + + 11 + + name + string.other.link.description.title.markdown + + 12 + + name + punctuation.definition.string.begin.markdown + + 13 + + name + punctuation.definition.string.end.markdown + + 2 + + name + constant.other.reference.link.markdown + + 3 + + name + punctuation.definition.constant.markdown + + 4 + + name + punctuation.separator.key-value.markdown + + 5 + + name + punctuation.definition.link.markdown + + 6 + + name + markup.underline.link.markdown + + 7 + + name + punctuation.definition.link.markdown + + 8 + + name + string.other.link.description.title.markdown + + 9 + + name + punctuation.definition.string.begin.markdown + + + match + ^(?x: + \s* # Leading whitespace + (\[)(.+?)(\])(:) # Reference name + [ \t]* # Optional whitespace + (<?)(\S+?)(>?) # The url + [ \t]* # Optional whitespace + (?: + ((\().+?(\))) # Match title in quotes… + | ((").+?(")) # or in parens. + )? # Title is optional + \s* # Optional whitespace + $ + ) + name + meta.link.reference.def.markdown + + list_paragraph + + begin + (^|\G)(?=\S)(?![*+-]\s|[0-9]+\.\s) + name + meta.paragraph.markdown + patterns + + + include + #inline + + + include + text.html.basic + + + include + #heading-setext + + + while + (^|\G)(?!\s*$|#|[ ]{0,3}([-*_][ ]{2,}){3,}[ \t]*$\n?|>|[ ]{0,3}[*+-]|[ ]{0,3}[0-9]+\.) + + lists + + patterns + + + begin + (^|\G)([ ]{0,3})([*+-])([ ]{1,3}|\t) + beginCaptures + + 3 + + name + beginning.punctuation.definition.list.markdown + + + comment + Currently does not support un-indented second lines. + name + markup.list.unnumbered.markdown + patterns + + + include + #list_paragraph + + + include + #block + + + while + ((^|\G)([ ]{4}|\t))|(^[ \t]*$) + + + begin + (^|\G)([ ]{0,3})([0-9]+\.)([ ]{1,3}|\t) + beginCaptures + + 3 + + name + beginning.punctuation.definition.list.markdown + + + name + markup.list.numbered.markdown + patterns + + + include + #list_paragraph + + + include + #block + + + while + ((^|\G)([ ]{4}|\t))|(^[ \t]*$) + + + + paragraph + + begin + (^|\G)[ ]{0,3}(?=\S) + name + meta.paragraph.markdown + patterns + + + include + #inline + + + include + text.html.basic + + + include + #heading-setext + + + while + (^|\G)((?=\s*[-=]{3,}\s*$)|[ ]{4,}(?=\S)) + +{{languageDefinitions}} + fenced_code_block_unknown + + name + markup.fenced_code.block.markdown + begin + (^|\G)(\s*)(`{3,}|~{3,})\s*(?=([^`~]*)?$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 4 + + name + fenced_code.block.language + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + + raw_block + + begin + (^|\G)([ ]{4}|\t) + name + markup.raw.block.markdown + while + (^|\G)([ ]{4}|\t) + + separator + + match + (^|\G)[ ]{0,3}([*-_])([ ]{0,2}\2){2,}[ \t]*$\n? + name + meta.separator.markdown + + + + inline + + patterns + + + include + #ampersand + + + include + #bracket + + + include + #bold + + + include + #italic + + + include + #raw + + + include + #escape + + + include + #image-inline + + + include + #image-ref + + + include + #link-email + + + include + #link-inet + + + include + #link-inline + + + include + #link-ref + + + include + #link-ref-literal + + + repository + + ampersand + + comment + + Markdown will convert this for us. We match it so that the + HTML grammar will not mark it up as invalid. + + match + &(?!([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+);) + name + meta.other.valid-ampersand.markdown + + bold + + begin + (?x) + (\*\*|__)(?=\S) # Open + (?= + ( + <[^>]*+> # HTML tags + | (?<raw>`+)([^`]|(?!(?<!`)\k<raw>(?!`))`)*+\k<raw> + # Raw + | \\[\\`*_{}\[\]()#.!+\->]?+ # Escapes + | \[ + ( + (?<square> # Named group + [^\[\]\\] # Match most chars + | \\. # Escaped chars + | \[ \g<square>*+ \] # Nested brackets + )*+ + \] + ( + ( # Reference Link + [ ]? # Optional space + \[[^\]]*+\] # Ref name + ) + | ( # Inline Link + \( # Opening paren + [ \t]*+ # Optional whitespace + <?(.*?)>? # URL + [ \t]*+ # Optional whitespace + ( # Optional Title + (?<title>['"]) + (.*?) + \k<title> + )? + \) + ) + ) + ) + | (?!(?<=\S)\1). # Everything besides + # style closer + )++ + (?<=\S)\1 # Close + ) + + captures + + 1 + + name + punctuation.definition.bold.markdown + + + end + (?<=\S)(\1) + name + markup.bold.markdown + patterns + + + applyEndPatternLast + 1 + begin + (?=<[^>]*?>) + end + (?<=>) + patterns + + + include + text.html.basic + + + + + include + #escape + + + include + #ampersand + + + include + #bracket + + + include + #raw + + + include + #italic + + + include + #image-inline + + + include + #link-inline + + + include + #link-inet + + + include + #link-email + + + include + #image-ref + + + include + #link-ref-literal + + + include + #link-ref + + + + bracket + + comment + + Markdown will convert this for us. We match it so that the + HTML grammar will not mark it up as invalid. + + match + <(?![a-z/?\$!]) + name + meta.other.valid-bracket.markdown + + escape + + match + \\[-`*_#+.!(){}\[\]\\>] + name + constant.character.escape.markdown + + image-inline + + captures + + 1 + + name + punctuation.definition.string.begin.markdown + + 10 + + name + string.other.link.description.title.markdown + + 11 + + name + punctuation.definition.string.markdown + + 12 + + name + punctuation.definition.string.markdown + + 13 + + name + string.other.link.description.title.markdown + + 14 + + name + punctuation.definition.string.markdown + + 15 + + name + punctuation.definition.string.markdown + + 16 + + name + punctuation.definition.metadata.markdown + + 2 + + name + string.other.link.description.markdown + + 4 + + name + punctuation.definition.string.end.markdown + + 5 + + name + invalid.illegal.whitespace.markdown + + 6 + + name + punctuation.definition.metadata.markdown + + 7 + + name + punctuation.definition.link.markdown + + 8 + + name + markup.underline.link.image.markdown + + 9 + + name + punctuation.definition.link.markdown + + + match + (?x: + (\!\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\]) + # Match the link text. + ([ ])? # Space not allowed + (\() # Opening paren for url + (<?)(\S+?)(>?) # The url + [ \t]* # Optional whitespace + (?: + ((\().+?(\))) # Match title in parens… + | ((").+?(")) # or in quotes. + )? # Title is optional + \s* # Optional whitespace + (\)) + ) + name + meta.image.inline.markdown + + image-ref + + captures + + 1 + + name + punctuation.definition.string.begin.markdown + + 2 + + name + string.other.link.description.markdown + + 4 + + name + punctuation.definition.string.begin.markdown + + 5 + + name + punctuation.definition.constant.markdown + + 6 + + name + constant.other.reference.link.markdown + + 7 + + name + punctuation.definition.constant.markdown + + + match + (\!\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\])[ ]?(\[)(.*?)(\]) + name + meta.image.reference.markdown + + italic + + begin + (?x) + (\*|_)(?=\S) # Open + (?= + ( + <[^>]*+> # HTML tags + | (?<raw>`+)([^`]|(?!(?<!`)\k<raw>(?!`))`)*+\k<raw> + # Raw + | \\[\\`*_{}\[\]()#.!+\->]?+ # Escapes + | \[ + ( + (?<square> # Named group + [^\[\]\\] # Match most chars + | \\. # Escaped chars + | \[ \g<square>*+ \] # Nested brackets + )*+ + \] + ( + ( # Reference Link + [ ]? # Optional space + \[[^\]]*+\] # Ref name + ) + | ( # Inline Link + \( # Opening paren + [ \t]*+ # Optional whtiespace + <?(.*?)>? # URL + [ \t]*+ # Optional whtiespace + ( # Optional Title + (?<title>['"]) + (.*?) + \k<title> + )? + \) + ) + ) + ) + | \1\1 # Must be bold closer + | (?!(?<=\S)\1). # Everything besides + # style closer + )++ + (?<=\S)\1 # Close + ) + + captures + + 1 + + name + punctuation.definition.italic.markdown + + + end + (?<=\S)(\1)((?!\1)|(?=\1\1)) + name + markup.italic.markdown + patterns + + + applyEndPatternLast + 1 + begin + (?=<[^>]*?>) + end + (?<=>) + patterns + + + include + text.html.basic + + + + + include + #escape + + + include + #ampersand + + + include + #bracket + + + include + #raw + + + include + #bold + + + include + #image-inline + + + include + #link-inline + + + include + #link-inet + + + include + #link-email + + + include + #image-ref + + + include + #link-ref-literal + + + include + #link-ref + + + + link-email + + captures + + 1 + + name + punctuation.definition.link.markdown + + 2 + + name + markup.underline.link.markdown + + 4 + + name + punctuation.definition.link.markdown + + + match + (<)((?:mailto:)?[-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(>) + name + meta.link.email.lt-gt.markdown + + link-inet + + captures + + 1 + + name + punctuation.definition.link.markdown + + 2 + + name + markup.underline.link.markdown + + 3 + + name + punctuation.definition.link.markdown + + + match + (<)((?:https?|ftp)://.*?)(>) + name + meta.link.inet.markdown + + link-inline + + captures + + 1 + + name + punctuation.definition.string.begin.markdown + + 10 + + name + string.other.link.description.title.markdown + + 11 + + name + punctuation.definition.string.begin.markdown + + 12 + + name + punctuation.definition.string.end.markdown + + 13 + + name + string.other.link.description.title.markdown + + 14 + + name + punctuation.definition.string.begin.markdown + + 15 + + name + punctuation.definition.string.end.markdown + + 16 + + name + punctuation.definition.metadata.markdown + + 2 + + name + string.other.link.title.markdown + + 4 + + name + punctuation.definition.string.end.markdown + + 5 + + name + invalid.illegal.whitespace.markdown + + 6 + + name + punctuation.definition.metadata.markdown + + 7 + + name + punctuation.definition.link.markdown + + 8 + + name + markup.underline.link.markdown + + 9 + + name + punctuation.definition.link.markdown + + + match + (?x: + (\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\]) + # Match the link text. + ([ ])? # Space not allowed + (\() # Opening paren for url + (<?)(.*?)(>?) # The url + [ \t]* # Optional whitespace + (?: + ((\().+?(\))) # Match title in parens… + | ((").+?(")) # or in quotes. + )? # Title is optional + \s* # Optional whitespace + (\)) + ) + name + meta.link.inline.markdown + + link-ref + + captures + + 1 + + name + punctuation.definition.string.begin.markdown + + 2 + + name + string.other.link.title.markdown + + 4 + + name + punctuation.definition.string.end.markdown + + 5 + + name + punctuation.definition.constant.begin.markdown + + 6 + + name + constant.other.reference.link.markdown + + 7 + + name + punctuation.definition.constant.end.markdown + + + match + (\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\])(\[)([^\]]*+)(\]) + name + meta.link.reference.markdown + + link-ref-literal + + captures + + 1 + + name + punctuation.definition.string.begin.markdown + + 2 + + name + string.other.link.title.markdown + + 4 + + name + punctuation.definition.string.end.markdown + + 5 + + name + punctuation.definition.constant.begin.markdown + + 6 + + name + punctuation.definition.constant.end.markdown + + + match + (\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\])[ ]?(\[)(\]) + name + meta.link.reference.literal.markdown + + raw + + captures + + 1 + + name + punctuation.definition.raw.markdown + + 3 + + name + punctuation.definition.raw.markdown + + + match + (`+)([^`]|(?!(?<!`)\1(?!`))`)*+(\1) + name + markup.inline.raw.markdown + + + + frontMatter + + begin + \A-{3}\s*$ + while + ^(?!(-{3}|\.{3})\s*$) + patterns + + + include + source.yaml + + + + + scopeName + text.html.markdown + uuid + 0A1D9874-B448-11D9-BD50-000D93B6E43C + + diff --git a/extensions/markdown/test/colorize-fixtures/test.md b/extensions/markdown/test/colorize-fixtures/test.md new file mode 100644 index 0000000000..5a003c7b7f --- /dev/null +++ b/extensions/markdown/test/colorize-fixtures/test.md @@ -0,0 +1,105 @@ +# Header 1 # +## Header 2 ## +### Header 3 ### (Hashes on right are optional) +## Markdown plus h2 with a custom ID ## {#id-goes-here} +[Link back to H2](#id-goes-here) + +### Alternate heading styles: +Alternate Header 1 +================== +Alternate Header 2 +------------------ + + +
+
+ nested div +
+ + This is a div _with_ underscores + and a & bold element. + +
+ +* Bullet lists are easy too +- Another one ++ Another one + + + nested list + +This is a paragraph, which is text surrounded by +whitespace. Paragraphs can be on one +line (or many), and can drone on for hours. + +Now some inline markup like _italics_, **bold**, +and `code()`. Note that underscores +in_words_are ignored. + +````application/json + { value: ["or with a mime type"] } +```` + +> Blockquotes are like quoted text in email replies +>> And, they can be nested + +1. A numbered list +2. Which is numbered +3. With periods and a space + +And now some code: + + // Code is just text indented a bit + which(is_easy) to_remember(); + +And a block + +~~~ +// Markdown extra adds un-indented code blocks too + +if (this_is_more_code == true && !indented) { + // tild wrapped code blocks, also not indented +} +~~~ + +Text with +two trailing spaces +(on the right) +can be used +for things like poems + +### Horizontal rules + +* * * * +**** +-------------------------- + +![picture alt](/images/photo.jpeg "Title is optional") + +## Markdown plus tables ## + +| Header | Header | Right | +| ------ | ------ | -----: | +| Cell | Cell | $10 | +| Cell | Cell | $20 | + +* Outer pipes on tables are optional +* Colon used for alignment (right versus left) + +## Markdown plus definition lists ## + +Bottled water +: $ 1.25 +: $ 1.55 (Large) + +Milk +Pop +: $ 1.75 + +* Multiple definitions and terms are possible +* Definitions can include multiple paragraphs too + +*[ABBR]: Markdown plus abbreviations (produces an tag) \ No newline at end of file diff --git a/extensions/markdown/test/colorize-results/test_md.json b/extensions/markdown/test/colorize-results/test_md.json new file mode 100644 index 0000000000..c40d553625 --- /dev/null +++ b/extensions/markdown/test/colorize-results/test_md.json @@ -0,0 +1,2609 @@ +[ + { + "c": "#", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Header 1", + "t": "text.html.markdown markup.heading.markdown entity.name.section.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "#", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Header 2", + "t": "text.html.markdown markup.heading.markdown entity.name.section.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "###", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Header 3 ### (Hashes on right are optional)", + "t": "text.html.markdown markup.heading.markdown entity.name.section.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Markdown plus h2 with a custom ID ## {#id-goes-here}", + "t": "text.html.markdown markup.heading.markdown entity.name.section.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "[", + "t": "text.html.markdown meta.paragraph.markdown meta.link.inline.markdown punctuation.definition.string.begin.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Link back to H2", + "t": "text.html.markdown meta.paragraph.markdown meta.link.inline.markdown string.other.link.title.markdown", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "]", + "t": "text.html.markdown meta.paragraph.markdown meta.link.inline.markdown punctuation.definition.string.end.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "text.html.markdown meta.paragraph.markdown meta.link.inline.markdown punctuation.definition.metadata.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#id-goes-here", + "t": "text.html.markdown meta.paragraph.markdown meta.link.inline.markdown markup.underline.link.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "text.html.markdown meta.paragraph.markdown meta.link.inline.markdown punctuation.definition.metadata.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "###", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Alternate heading styles:", + "t": "text.html.markdown markup.heading.markdown entity.name.section.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Alternate Header 1", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "==================", + "t": "text.html.markdown meta.paragraph.markdown markup.heading.setext.1.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Alternate Header 2", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "------------------", + "t": "text.html.markdown meta.paragraph.markdown markup.heading.setext.2.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "", + "t": "text.html.markdown comment.block.html punctuation.definition.comment.html", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "<", + "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "div", + "t": "text.html.markdown meta.tag.block.any.html entity.name.tag.block.any.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.tag.block.any.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "class", + "t": "text.html.markdown meta.tag.block.any.html entity.other.attribute-name.html", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.html.markdown meta.tag.block.any.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "custom-class", + "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.end.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.tag.block.any.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "markdown", + "t": "text.html.markdown meta.tag.block.any.html entity.other.attribute-name.html", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.html.markdown meta.tag.block.any.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "1", + "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.end.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.html.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "div", + "t": "text.html.markdown meta.tag.block.any.html entity.name.tag.block.any.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " nested div", + "t": "text.html.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.html.markdown punctuation.whitespace.embedded.leading.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "script", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "type", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html entity.other.attribute-name.html", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html punctuation.definition.string.begin.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "text/x-koka", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html punctuation.definition.string.end.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " function( x: int ) { return x*x; }", + "t": "text.html.markdown meta.embedded.block.html source.unknown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.embedded.block.html source.unknown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " This is a div _with_ underscores", + "t": "text.html.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " and a ", + "t": "text.html.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "&", + "t": "text.html.markdown invalid.illegal.bad-ampersand.html", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747" + } + }, + { + "c": " ", + "t": "text.html.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "b", + "t": "text.html.markdown meta.tag.inline.any.html entity.name.tag.inline.any.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.tag.inline.any.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "class", + "t": "text.html.markdown meta.tag.inline.any.html entity.other.attribute-name.html", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.html.markdown meta.tag.inline.any.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.html.markdown meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "bold", + "t": "text.html.markdown meta.tag.inline.any.html string.quoted.double.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.html.markdown meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "bold", + "t": "text.html.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " element.", + "t": "text.html.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown punctuation.whitespace.embedded.leading.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "style", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.embedded.block.html source.css", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "body", + "t": "text.html.markdown meta.embedded.block.html source.css meta.selector.css entity.name.tag.css", + "r": { + "dark_plus": "entity.name.tag.css: #D7BA7D", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag.css: #D7BA7D", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag.css: #D7BA7D" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.embedded.block.html source.css", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css punctuation.section.property-list.begin.bracket.curly.css", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "font", + "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css meta.property-name.css support.type.property-name.css", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name: #FF0000", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name: #FF0000", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": ":", + "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css punctuation.separator.key-value.css", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css meta.property-value.css string.quoted.double.css punctuation.definition.string.begin.css", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Consolas", + "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css meta.property-value.css string.quoted.double.css", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css meta.property-value.css string.quoted.double.css punctuation.definition.string.end.css", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css punctuation.section.property-list.end.bracket.curly.css", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.embedded.block.html source.css", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.begin.html source.css", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "/", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "style", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "", + "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "*", + "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.unnumbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Bullet lists are easy too", + "t": "text.html.markdown markup.list.unnumbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.unnumbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Another one", + "t": "text.html.markdown markup.list.unnumbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "+", + "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.unnumbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Another one", + "t": "text.html.markdown markup.list.unnumbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.unnumbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "+", + "t": "text.html.markdown markup.list.unnumbered.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.unnumbered.markdown markup.list.unnumbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "nested list", + "t": "text.html.markdown markup.list.unnumbered.markdown markup.list.unnumbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "This is a paragraph, which is text surrounded by", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "whitespace. Paragraphs can be on one", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "line (or many), and can drone on for hours.", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Now some inline markup like ", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "_", + "t": "text.html.markdown meta.paragraph.markdown markup.italic.markdown punctuation.definition.italic.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "italics", + "t": "text.html.markdown meta.paragraph.markdown markup.italic.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "_", + "t": "text.html.markdown meta.paragraph.markdown markup.italic.markdown punctuation.definition.italic.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ", ", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "**", + "t": "text.html.markdown meta.paragraph.markdown markup.bold.markdown punctuation.definition.bold.markdown", + "r": { + "dark_plus": "markup.bold: #569CD6", + "light_plus": "markup.bold: #000080", + "dark_vs": "markup.bold: #569CD6", + "light_vs": "markup.bold: #000080", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "bold", + "t": "text.html.markdown meta.paragraph.markdown markup.bold.markdown", + "r": { + "dark_plus": "markup.bold: #569CD6", + "light_plus": "markup.bold: #000080", + "dark_vs": "markup.bold: #569CD6", + "light_vs": "markup.bold: #000080", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "**", + "t": "text.html.markdown meta.paragraph.markdown markup.bold.markdown punctuation.definition.bold.markdown", + "r": { + "dark_plus": "markup.bold: #569CD6", + "light_plus": "markup.bold: #000080", + "dark_vs": "markup.bold: #569CD6", + "light_vs": "markup.bold: #000080", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "and ", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "`", + "t": "text.html.markdown meta.paragraph.markdown markup.inline.raw.markdown punctuation.definition.raw.markdown", + "r": { + "dark_plus": "markup.inline.raw: #CE9178", + "light_plus": "markup.inline.raw: #800000", + "dark_vs": "markup.inline.raw: #CE9178", + "light_vs": "markup.inline.raw: #800000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "code()", + "t": "text.html.markdown meta.paragraph.markdown markup.inline.raw.markdown", + "r": { + "dark_plus": "markup.inline.raw: #CE9178", + "light_plus": "markup.inline.raw: #800000", + "dark_vs": "markup.inline.raw: #CE9178", + "light_vs": "markup.inline.raw: #800000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "`", + "t": "text.html.markdown meta.paragraph.markdown markup.inline.raw.markdown punctuation.definition.raw.markdown", + "r": { + "dark_plus": "markup.inline.raw: #CE9178", + "light_plus": "markup.inline.raw: #800000", + "dark_vs": "markup.inline.raw: #CE9178", + "light_vs": "markup.inline.raw: #800000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ". Note that underscores", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "in", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "_", + "t": "text.html.markdown meta.paragraph.markdown markup.italic.markdown punctuation.definition.italic.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "words", + "t": "text.html.markdown meta.paragraph.markdown markup.italic.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "_", + "t": "text.html.markdown meta.paragraph.markdown markup.italic.markdown punctuation.definition.italic.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "are ignored.", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "````", + "t": "text.html.markdown markup.fenced_code.block.markdown punctuation.definition.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "application/json", + "t": "text.html.markdown markup.fenced_code.block.markdown fenced_code.block.language", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " { value: [\"or with a mime type\"] }", + "t": "text.html.markdown markup.fenced_code.block.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "````", + "t": "text.html.markdown markup.fenced_code.block.markdown punctuation.definition.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "text.html.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", + "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", + "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.quote.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Blockquotes are like quoted text in email replies", + "t": "text.html.markdown markup.quote.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "text.html.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", + "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", + "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "text.html.markdown markup.quote.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", + "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", + "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.quote.markdown markup.quote.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "And, they can be nested", + "t": "text.html.markdown markup.quote.markdown markup.quote.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1.", + "t": "text.html.markdown markup.list.numbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.numbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A numbered list", + "t": "text.html.markdown markup.list.numbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "2.", + "t": "text.html.markdown markup.list.numbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.numbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Which is numbered", + "t": "text.html.markdown markup.list.numbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "3.", + "t": "text.html.markdown markup.list.numbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.numbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "With periods and a space", + "t": "text.html.markdown markup.list.numbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "And now some code:", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " // Code is just text indented a bit", + "t": "text.html.markdown markup.raw.block.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " which(is_easy) to_remember();", + "t": "text.html.markdown markup.raw.block.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "And a block", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "~~~", + "t": "text.html.markdown markup.fenced_code.block.markdown punctuation.definition.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "// Markdown extra adds un-indented code blocks too", + "t": "text.html.markdown markup.fenced_code.block.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "if (this_is_more_code == true && !indented) {", + "t": "text.html.markdown markup.fenced_code.block.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " // tild wrapped code blocks, also not indented", + "t": "text.html.markdown markup.fenced_code.block.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "text.html.markdown markup.fenced_code.block.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "~~~", + "t": "text.html.markdown markup.fenced_code.block.markdown punctuation.definition.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Text with", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "two trailing spaces", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(on the right)", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "can be used", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "for things like poems", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "###", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Horizontal rules", + "t": "text.html.markdown markup.heading.markdown entity.name.section.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "* * * *", + "t": "text.html.markdown meta.separator.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "****", + "t": "text.html.markdown meta.separator.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "--------------------------", + "t": "text.html.markdown meta.separator.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "![", + "t": "text.html.markdown meta.paragraph.markdown meta.image.inline.markdown punctuation.definition.string.begin.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "picture alt", + "t": "text.html.markdown meta.paragraph.markdown meta.image.inline.markdown string.other.link.description.markdown", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "]", + "t": "text.html.markdown meta.paragraph.markdown meta.image.inline.markdown punctuation.definition.string.end.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "text.html.markdown meta.paragraph.markdown meta.image.inline.markdown punctuation.definition.metadata.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/images/photo.jpeg", + "t": "text.html.markdown meta.paragraph.markdown meta.image.inline.markdown markup.underline.link.image.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown meta.paragraph.markdown meta.image.inline.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.html.markdown meta.paragraph.markdown meta.image.inline.markdown string.other.link.description.title.markdown punctuation.definition.string.markdown", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Title is optional", + "t": "text.html.markdown meta.paragraph.markdown meta.image.inline.markdown string.other.link.description.title.markdown", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.html.markdown meta.paragraph.markdown meta.image.inline.markdown string.other.link.description.title.markdown punctuation.definition.string.markdown", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "text.html.markdown meta.paragraph.markdown meta.image.inline.markdown punctuation.definition.metadata.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "##", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Markdown plus tables", + "t": "text.html.markdown markup.heading.markdown entity.name.section.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "| Header | Header | Right |", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "| ------ | ------ | -----: |", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "| Cell | Cell | $10 |", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "| Cell | Cell | $20 |", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.unnumbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Outer pipes on tables are optional", + "t": "text.html.markdown markup.list.unnumbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.unnumbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Colon used for alignment (right versus left)", + "t": "text.html.markdown markup.list.unnumbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "##", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Markdown plus definition lists", + "t": "text.html.markdown markup.heading.markdown entity.name.section.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", + "t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "Bottled water", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ": $ 1.25", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ": $ 1.55 (Large)", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Milk", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Pop", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ": $ 1.75", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.unnumbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Multiple definitions and terms are possible", + "t": "text.html.markdown markup.list.unnumbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "r": { + "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", + "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.list.unnumbered.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Definitions can include multiple paragraphs too", + "t": "text.html.markdown markup.list.unnumbered.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*[ABBR]: Markdown plus abbreviations (produces an ", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "abbr", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html entity.name.tag.inline.any.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " tag)", + "t": "text.html.markdown meta.paragraph.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + } +] \ No newline at end of file diff --git a/extensions/markdown/tsconfig.json b/extensions/markdown/tsconfig.json new file mode 100644 index 0000000000..d4e3763558 --- /dev/null +++ b/extensions/markdown/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "lib": [ + "es6", + "es2015.promise", + "dom" + ], + "outDir": "./out", + "sourceMap": true, + "strictNullChecks": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/merge-conflict/.vscodeignore b/extensions/merge-conflict/.vscodeignore new file mode 100644 index 0000000000..d43a539fdd --- /dev/null +++ b/extensions/merge-conflict/.vscodeignore @@ -0,0 +1,2 @@ +src/** +tsconfig.json \ No newline at end of file diff --git a/extensions/merge-conflict/npm-shrinkwrap.json b/extensions/merge-conflict/npm-shrinkwrap.json new file mode 100644 index 0000000000..ec715d1907 --- /dev/null +++ b/extensions/merge-conflict/npm-shrinkwrap.json @@ -0,0 +1,26 @@ +{ + "name": "merge-conflict", + "version": "0.7.0", + "dependencies": { + "applicationinsights": { + "version": "0.18.0", + "from": "applicationinsights@0.18.0", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz" + }, + "vscode-extension-telemetry": { + "version": "0.0.8", + "from": "vscode-extension-telemetry@0.0.8", + "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz" + }, + "vscode-nls": { + "version": "2.0.2", + "from": "vscode-nls@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + }, + "winreg": { + "version": "1.2.3", + "from": "winreg@1.2.3", + "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz" + } + } +} diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json new file mode 100644 index 0000000000..4ecece640e --- /dev/null +++ b/extensions/merge-conflict/package.json @@ -0,0 +1,93 @@ +{ + "name": "merge-conflict", + "publisher": "vscode", + "displayName": "merge-conflict", + "description": "Merge Conflict", + "version": "0.7.0", + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "engines": { + "vscode": "^1.5.0" + }, + "categories": [ + "Other" + ], + "activationEvents": [ + "*" + ], + "main": "./out/extension", + "scripts": { + "compile": "gulp compile-extension:merge-conflict", + "watch": "gulp watch-extension:merge-conflict" + }, + "contributes": { + "commands": [ + { + "category": "%command.category%", + "title": "%command.accept.all-incoming%", + "command": "merge-conflict.accept.all-incoming" + }, + { + "category": "%command.category%", + "title": "%command.accept.all-both%", + "command": "merge-conflict.accept.all-both" + }, + { + "category": "%command.category%", + "title": "%command.accept.current%", + "command": "merge-conflict.accept.current" + }, + { + "category": "%command.category%", + "title": "%command.accept.incoming%", + "command": "merge-conflict.accept.incoming" + }, + { + "category": "%command.category%", + "title": "%command.accept.selection%", + "command": "merge-conflict.accept.selection" + }, + { + "category": "%command.category%", + "title": "%command.accept.both%", + "command": "merge-conflict.accept.both" + }, + { + "category": "%command.category%", + "title": "%command.next%", + "command": "merge-conflict.next" + }, + { + "category": "%command.category%", + "title": "%command.previous%", + "command": "merge-conflict.previous" + }, + { + "category": "%command.category%", + "title": "%command.compare%", + "command": "merge-conflict.compare" + } + ], + "configuration": { + "title": "%config.title%", + "properties": { + "merge-conflict.codeLens.enabled": { + "type": "boolean", + "description": "%config.codeLensEnabled%", + "default": true + }, + "merge-conflict.decorators.enabled": { + "type": "boolean", + "description": "%config.decoratorsEnabled%", + "default": true + } + } + } + }, + "dependencies": { + "vscode-extension-telemetry": "0.0.8", + "vscode-nls": "^2.0.2" + }, + "devDependencies": { + "@types/node": "^7.0.4" + } +} \ No newline at end of file diff --git a/extensions/merge-conflict/package.nls.json b/extensions/merge-conflict/package.nls.json new file mode 100644 index 0000000000..b2927ca343 --- /dev/null +++ b/extensions/merge-conflict/package.nls.json @@ -0,0 +1,15 @@ +{ + "command.category": "Merge Conflict", + "command.accept.all-incoming": "Accept All Incoming", + "command.accept.all-both": "Accept All Both", + "command.accept.current": "Accept Current", + "command.accept.incoming": "Accept Incoming", + "command.accept.selection": "Accept Selection", + "command.accept.both": "Accept Both", + "command.next": "Next Conflict", + "command.previous": "Previous Conflict", + "command.compare": "Compare Current Conflict", + "config.title": "Merge Conflict", + "config.codeLensEnabled": "Enable/disable merge conflict block CodeLens within editor", + "config.decoratorsEnabled": "Enable/disable merge conflict decorators within editor" +} \ No newline at end of file diff --git a/extensions/merge-conflict/src/codelensProvider.ts b/extensions/merge-conflict/src/codelensProvider.ts new file mode 100644 index 0000000000..c2d4a3dcb9 --- /dev/null +++ b/extensions/merge-conflict/src/codelensProvider.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as interfaces from './interfaces'; +import { loadMessageBundle } from 'vscode-nls'; +const localize = loadMessageBundle(); + +export default class MergeConflictCodeLensProvider implements vscode.CodeLensProvider, vscode.Disposable { + private codeLensRegistrationHandle: vscode.Disposable | null; + private config: interfaces.IExtensionConfiguration; + private tracker: interfaces.IDocumentMergeConflictTracker; + + constructor(private context: vscode.ExtensionContext, trackerService: interfaces.IDocumentMergeConflictTrackerService) { + this.tracker = trackerService.createTracker('codelens'); + } + + begin(config: interfaces.IExtensionConfiguration) { + this.config = config; + + if (this.config.enableCodeLens) { + this.registerCodeLensProvider(); + } + } + + configurationUpdated(updatedConfig: interfaces.IExtensionConfiguration) { + + if (updatedConfig.enableCodeLens === false && this.codeLensRegistrationHandle) { + this.codeLensRegistrationHandle.dispose(); + this.codeLensRegistrationHandle = null; + } + else if (updatedConfig.enableCodeLens === true && !this.codeLensRegistrationHandle) { + this.registerCodeLensProvider(); + } + + this.config = updatedConfig; + } + + + dispose() { + if (this.codeLensRegistrationHandle) { + this.codeLensRegistrationHandle.dispose(); + this.codeLensRegistrationHandle = null; + } + } + + async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + + if (!this.config || !this.config.enableCodeLens) { + return null; + } + + let conflicts = await this.tracker.getConflicts(document); + + if (!conflicts || conflicts.length === 0) { + return null; + } + + let items: vscode.CodeLens[] = []; + + conflicts.forEach(conflict => { + let acceptCurrentCommand: vscode.Command = { + command: 'merge-conflict.accept.current', + title: localize('acceptCurrentChange', 'Accept Current Change'), + arguments: ['known-conflict', conflict] + }; + + let acceptIncomingCommand: vscode.Command = { + command: 'merge-conflict.accept.incoming', + title: localize('acceptIncomingChange', 'Accept Incoming Change'), + arguments: ['known-conflict', conflict] + }; + + let acceptBothCommand: vscode.Command = { + command: 'merge-conflict.accept.both', + title: localize('acceptBothChanges', 'Accept Both Changes'), + arguments: ['known-conflict', conflict] + }; + + let diffCommand: vscode.Command = { + command: 'merge-conflict.compare', + title: localize('compareChanges', 'Compare Changes'), + arguments: [conflict] + }; + + items.push( + new vscode.CodeLens(conflict.range, acceptCurrentCommand), + new vscode.CodeLens(conflict.range.with(conflict.range.start.with({ character: conflict.range.start.character + 1 })), acceptIncomingCommand), + new vscode.CodeLens(conflict.range.with(conflict.range.start.with({ character: conflict.range.start.character + 2 })), acceptBothCommand), + new vscode.CodeLens(conflict.range.with(conflict.range.start.with({ character: conflict.range.start.character + 3 })), diffCommand) + ); + }); + + return items; + } + + private registerCodeLensProvider() { + this.codeLensRegistrationHandle = vscode.languages.registerCodeLensProvider([ + { scheme: 'file' }, + { scheme: 'untitled' }, + ], this); + } +} diff --git a/extensions/merge-conflict/src/commandHandler.ts b/extensions/merge-conflict/src/commandHandler.ts new file mode 100644 index 0000000000..1aa276493f --- /dev/null +++ b/extensions/merge-conflict/src/commandHandler.ts @@ -0,0 +1,288 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import * as interfaces from './interfaces'; +import ContentProvider from './contentProvider'; +import * as path from 'path'; +import { loadMessageBundle } from 'vscode-nls'; +const localize = loadMessageBundle(); + +interface IDocumentMergeConflictNavigationResults { + canNavigate: boolean; + conflict?: interfaces.IDocumentMergeConflict; +} + +enum NavigationDirection { + Forwards, + Backwards +} + +export default class CommandHandler implements vscode.Disposable { + + private disposables: vscode.Disposable[] = []; + private tracker: interfaces.IDocumentMergeConflictTracker; + + constructor(private context: vscode.ExtensionContext, trackerService: interfaces.IDocumentMergeConflictTrackerService) { + this.tracker = trackerService.createTracker('commands'); + } + + begin() { + this.disposables.push( + this.registerTextEditorCommand('merge-conflict.accept.current', this.acceptCurrent), + this.registerTextEditorCommand('merge-conflict.accept.incoming', this.acceptIncoming), + this.registerTextEditorCommand('merge-conflict.accept.selection', this.acceptSelection), + this.registerTextEditorCommand('merge-conflict.accept.both', this.acceptBoth), + this.registerTextEditorCommand('merge-conflict.accept.all-current', this.acceptAllCurrent), + this.registerTextEditorCommand('merge-conflict.accept.all-incoming', this.acceptAllIncoming), + this.registerTextEditorCommand('merge-conflict.accept.all-both', this.acceptAllBoth), + this.registerTextEditorCommand('merge-conflict.next', this.navigateNext), + this.registerTextEditorCommand('merge-conflict.previous', this.navigatePrevious), + this.registerTextEditorCommand('merge-conflict.compare', this.compare) + ); + } + + private registerTextEditorCommand(command: string, cb: (editor: vscode.TextEditor, ...args) => Promise) { + return vscode.commands.registerCommand(command, (...args) => { + const editor = vscode.window.activeTextEditor; + return editor && cb.call(this, editor, ...args); + }); + } + + acceptCurrent(editor: vscode.TextEditor, ...args): Promise { + return this.accept(interfaces.CommitType.Current, editor, ...args); + } + + acceptIncoming(editor: vscode.TextEditor, ...args): Promise { + return this.accept(interfaces.CommitType.Incoming, editor, ...args); + } + + acceptBoth(editor: vscode.TextEditor, ...args): Promise { + return this.accept(interfaces.CommitType.Both, editor, ...args); + } + + acceptAllCurrent(editor: vscode.TextEditor, ...args): Promise { + return this.acceptAll(interfaces.CommitType.Current, editor); + } + + acceptAllIncoming(editor: vscode.TextEditor, ...args): Promise { + return this.acceptAll(interfaces.CommitType.Incoming, editor); + } + + acceptAllBoth(editor: vscode.TextEditor, ...args): Promise { + return this.acceptAll(interfaces.CommitType.Both, editor); + } + + async compare(editor: vscode.TextEditor, conflict: interfaces.IDocumentMergeConflict | null, ...args) { + const fileName = path.basename(editor.document.uri.fsPath); + + // No conflict, command executed from command palette + if (!conflict) { + conflict = await this.findConflictContainingSelection(editor); + + // Still failed to find conflict, warn the user and exit + if (!conflict) { + vscode.window.showWarningMessage(localize('cursorNotInConflict', 'Editor cursor is not within a merge conflict')); + return; + } + } + + const scheme = editor.document.uri.scheme; + let range = conflict.current.content; + const leftUri = editor.document.uri.with({ + scheme: ContentProvider.scheme, + query: JSON.stringify({ scheme, range }) + }); + + range = conflict.incoming.content; + const rightUri = leftUri.with({ query: JSON.stringify({ scheme, range }) }); + + const title = localize('compareChangesTitle', '{0}: Current Changes ⟷ Incoming Changes', fileName); + vscode.commands.executeCommand('vscode.diff', leftUri, rightUri, title); + } + + navigateNext(editor: vscode.TextEditor, ...args): Promise { + return this.navigate(editor, NavigationDirection.Forwards); + } + + navigatePrevious(editor: vscode.TextEditor, ...args): Promise { + return this.navigate(editor, NavigationDirection.Backwards); + } + + async acceptSelection(editor: vscode.TextEditor, ...args): Promise { + let conflict = await this.findConflictContainingSelection(editor); + + if (!conflict) { + vscode.window.showWarningMessage(localize('cursorNotInConflict', 'Editor cursor is not within a merge conflict')); + return; + } + + let typeToAccept: interfaces.CommitType; + let tokenAfterCurrentBlock: vscode.Range = conflict.splitter; + + if (conflict.commonAncestors.length > 0) { + tokenAfterCurrentBlock = conflict.commonAncestors[0].header; + } + + // Figure out if the cursor is in current or incoming, we do this by seeing if + // the active position is before or after the range of the splitter or common + // ancestors marker. We can use this trick as the previous check in + // findConflictByActiveSelection will ensure it's within the conflict range, so + // we don't falsely identify "current" or "incoming" if outside of a conflict range. + if (editor.selection.active.isBefore(tokenAfterCurrentBlock.start)) { + typeToAccept = interfaces.CommitType.Current; + } + else if (editor.selection.active.isAfter(conflict.splitter.end)) { + typeToAccept = interfaces.CommitType.Incoming; + } + else if (editor.selection.active.isBefore(conflict.splitter.start)) { + vscode.window.showWarningMessage(localize('cursorOnCommonAncestorsRange', 'Editor cursor is within the common ancestors block, please move it to either the "current" or "incoming" block')); + return; + } + else { + vscode.window.showWarningMessage(localize('cursorOnSplitterRange', 'Editor cursor is within the merge conflict splitter, please move it to either the "current" or "incoming" block')); + return; + } + + this.tracker.forget(editor.document); + conflict.commitEdit(typeToAccept, editor); + } + + dispose() { + this.disposables.forEach(disposable => disposable.dispose()); + this.disposables = []; + } + + private async navigate(editor: vscode.TextEditor, direction: NavigationDirection): Promise { + let navigationResult = await this.findConflictForNavigation(editor, direction); + + if (!navigationResult) { + vscode.window.showWarningMessage(localize('noConflicts', 'No merge conflicts found in this file')); + return; + } + else if (!navigationResult.canNavigate) { + vscode.window.showWarningMessage(localize('noOtherConflictsInThisFile', 'No other merge conflicts within this file')); + return; + } + else if (!navigationResult.conflict) { + // TODO: Show error message? + return; + } + + // Move the selection to the first line of the conflict + editor.selection = new vscode.Selection(navigationResult.conflict.range.start, navigationResult.conflict.range.start); + editor.revealRange(navigationResult.conflict.range, vscode.TextEditorRevealType.Default); + } + + private async accept(type: interfaces.CommitType, editor: vscode.TextEditor, ...args): Promise { + + let conflict: interfaces.IDocumentMergeConflict | null; + + // If launched with known context, take the conflict from that + if (args[0] === 'known-conflict') { + conflict = args[1]; + } + else { + // Attempt to find a conflict that matches the current cursor position + conflict = await this.findConflictContainingSelection(editor); + } + + if (!conflict) { + vscode.window.showWarningMessage(localize('cursorNotInConflict', 'Editor cursor is not within a merge conflict')); + return; + } + + // Tracker can forget as we know we are going to do an edit + this.tracker.forget(editor.document); + conflict.commitEdit(type, editor); + } + + private async acceptAll(type: interfaces.CommitType, editor: vscode.TextEditor): Promise { + let conflicts = await this.tracker.getConflicts(editor.document); + + if (!conflicts || conflicts.length === 0) { + vscode.window.showWarningMessage(localize('noConflicts', 'No merge conflicts found in this file')); + return; + } + + // For get the current state of the document, as we know we are doing to do a large edit + this.tracker.forget(editor.document); + + // Apply all changes as one edit + await editor.edit((edit) => conflicts.forEach(conflict => { + conflict.applyEdit(type, editor, edit); + })); + } + + private async findConflictContainingSelection(editor: vscode.TextEditor, conflicts?: interfaces.IDocumentMergeConflict[]): Promise { + + if (!conflicts) { + conflicts = await this.tracker.getConflicts(editor.document); + } + + if (!conflicts || conflicts.length === 0) { + return null; + } + + for (let i = 0; i < conflicts.length; i++) { + if (conflicts[i].range.contains(editor.selection.active)) { + return conflicts[i]; + } + } + + return null; + } + + private async findConflictForNavigation(editor: vscode.TextEditor, direction: NavigationDirection, conflicts?: interfaces.IDocumentMergeConflict[]): Promise { + if (!conflicts) { + conflicts = await this.tracker.getConflicts(editor.document); + } + + if (!conflicts || conflicts.length === 0) { + return null; + } + + let selection = editor.selection.active; + if (conflicts.length === 1) { + if (conflicts[0].range.contains(selection)) { + return { + canNavigate: false + }; + } + + return { + canNavigate: true, + conflict: conflicts[0] + }; + } + + let predicate: (conflict) => boolean; + let fallback: () => interfaces.IDocumentMergeConflict; + + if (direction === NavigationDirection.Forwards) { + predicate = (conflict) => selection.isBefore(conflict.range.start); + fallback = () => conflicts![0]; + } else if (direction === NavigationDirection.Backwards) { + predicate = (conflict) => selection.isAfter(conflict.range.start); + fallback = () => conflicts![conflicts!.length - 1]; + } else { + throw new Error(`Unsupported direction ${direction}`); + } + + for (let i = 0; i < conflicts.length; i++) { + if (predicate(conflicts[i]) && !conflicts[i].range.contains(selection)) { + return { + canNavigate: true, + conflict: conflicts[i] + }; + } + } + + // Went all the way to the end, return the head + return { + canNavigate: true, + conflict: fallback() + }; + } +} \ No newline at end of file diff --git a/extensions/merge-conflict/src/contentProvider.ts b/extensions/merge-conflict/src/contentProvider.ts new file mode 100644 index 0000000000..7befd8db1e --- /dev/null +++ b/extensions/merge-conflict/src/contentProvider.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +import * as vscode from 'vscode'; +import * as interfaces from './interfaces'; + +export default class MergeConflictContentProvider implements vscode.TextDocumentContentProvider, vscode.Disposable { + + static scheme = 'merge-conflict.conflict-diff'; + + constructor(private context: vscode.ExtensionContext) { + } + + begin(config: interfaces.IExtensionConfiguration) { + this.context.subscriptions.push( + vscode.workspace.registerTextDocumentContentProvider(MergeConflictContentProvider.scheme, this) + ); + } + + dispose() { + } + + async provideTextDocumentContent(uri: vscode.Uri): Promise { + try { + const { scheme, range } = JSON.parse(uri.query) as { scheme: string; range: { line: number, character: number }[] }; + const [start, end] = range; + + const document = await vscode.workspace.openTextDocument(uri.with({ scheme, query: '' })); + const text = document.getText(new vscode.Range(start.line, start.character, end.line, end.character)); + return text; + } + catch (ex) { + await vscode.window.showErrorMessage('Unable to show comparison'); + return null; + } + } +} \ No newline at end of file diff --git a/extensions/merge-conflict/src/delayer.ts b/extensions/merge-conflict/src/delayer.ts new file mode 100644 index 0000000000..415f99329b --- /dev/null +++ b/extensions/merge-conflict/src/delayer.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +export interface ITask { + (): T; +} + +export class Delayer { + + public defaultDelay: number; + private timeout: any; // Timer + private completionPromise: Promise | null; + private onSuccess: ((value?: T | Thenable | null) => void) | null; + private task: ITask | null; + + constructor(defaultDelay: number) { + this.defaultDelay = defaultDelay; + this.timeout = null; + this.completionPromise = null; + this.onSuccess = null; + this.task = null; + } + + public trigger(task: ITask, delay: number = this.defaultDelay): Promise { + this.task = task; + if (delay >= 0) { + this.cancelTimeout(); + } + + if (!this.completionPromise) { + this.completionPromise = new Promise((resolve) => { + this.onSuccess = resolve; + }).then(() => { + this.completionPromise = null; + this.onSuccess = null; + var result = this.task!(); + this.task = null; + return result; + }); + } + + if (delay >= 0 || this.timeout === null) { + this.timeout = setTimeout(() => { + this.timeout = null; + this.onSuccess!(null); + }, delay >= 0 ? delay : this.defaultDelay); + } + + return this.completionPromise; + } + + public forceDelivery(): Promise | null { + if (!this.completionPromise) { + return null; + } + this.cancelTimeout(); + let result = this.completionPromise; + this.onSuccess!(null); + return result; + } + + public isTriggered(): boolean { + return this.timeout !== null; + } + + public cancel(): void { + this.cancelTimeout(); + this.completionPromise = null; + } + + private cancelTimeout(): void { + if (this.timeout !== null) { + clearTimeout(this.timeout); + this.timeout = null; + } + } +} \ No newline at end of file diff --git a/extensions/merge-conflict/src/documentMergeConflict.ts b/extensions/merge-conflict/src/documentMergeConflict.ts new file mode 100644 index 0000000000..2c325e98cc --- /dev/null +++ b/extensions/merge-conflict/src/documentMergeConflict.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as interfaces from './interfaces'; +import * as vscode from 'vscode'; + +export class DocumentMergeConflict implements interfaces.IDocumentMergeConflict { + + public range: vscode.Range; + public current: interfaces.IMergeRegion; + public incoming: interfaces.IMergeRegion; + public commonAncestors: interfaces.IMergeRegion[]; + public splitter: vscode.Range; + + constructor(document: vscode.TextDocument, descriptor: interfaces.IDocumentMergeConflictDescriptor) { + this.range = descriptor.range; + this.current = descriptor.current; + this.incoming = descriptor.incoming; + this.commonAncestors = descriptor.commonAncestors; + this.splitter = descriptor.splitter; + } + + public commitEdit(type: interfaces.CommitType, editor: vscode.TextEditor, edit?: vscode.TextEditorEdit): Thenable { + + if (edit) { + + this.applyEdit(type, editor, edit); + return Promise.resolve(true); + }; + + return editor.edit((edit) => this.applyEdit(type, editor, edit)); + } + + public applyEdit(type: interfaces.CommitType, editor: vscode.TextEditor, edit: vscode.TextEditorEdit): void { + + // Each conflict is a set of ranges as follows, note placements or newlines + // which may not in in spans + // [ Conflict Range -- (Entire content below) + // [ Current Header ]\n -- >>>>> Header + // [ Current Content ] -- (content) + // [ Splitter ]\n -- ===== + // [ Incoming Content ] -- (content) + // [ Incoming Header ]\n -- <<<<< Incoming + // ] + if (type === interfaces.CommitType.Current) { + // Replace [ Conflict Range ] with [ Current Content ] + let content = editor.document.getText(this.current.content); + this.replaceRangeWithContent(content, edit); + } + else if (type === interfaces.CommitType.Incoming) { + let content = editor.document.getText(this.incoming.content); + this.replaceRangeWithContent(content, edit); + } + else if (type === interfaces.CommitType.Both) { + // Replace [ Conflict Range ] with [ Current Content ] + \n + [ Incoming Content ] + + const currentContent = editor.document.getText(this.current.content); + const incomingContent = editor.document.getText(this.incoming.content); + + edit.replace(this.range, currentContent.concat(incomingContent)); + } + } + + private replaceRangeWithContent(content: string, edit: vscode.TextEditorEdit) { + if (this.isNewlineOnly(content)) { + edit.replace(this.range, ''); + return; + } + + // Replace [ Conflict Range ] with [ Current Content ] + edit.replace(this.range, content); + } + + private isNewlineOnly(text: string) { + return text === '\n' || text === '\r\n'; + } +} \ No newline at end of file diff --git a/extensions/merge-conflict/src/documentTracker.ts b/extensions/merge-conflict/src/documentTracker.ts new file mode 100644 index 0000000000..44125f5c53 --- /dev/null +++ b/extensions/merge-conflict/src/documentTracker.ts @@ -0,0 +1,138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { MergeConflictParser } from './mergeConflictParser'; +import * as interfaces from './interfaces'; +import { Delayer } from './delayer'; + +class ScanTask { + public origins: Set = new Set(); + public delayTask: Delayer; + + constructor(delayTime: number, initialOrigin: string) { + this.origins.add(initialOrigin); + this.delayTask = new Delayer(delayTime); + } + + public addOrigin(name: string): boolean { + if (this.origins.has(name)) { + return false; + } + + return false; + } + + public hasOrigin(name: string): boolean { + return this.origins.has(name); + } +} + +class OriginDocumentMergeConflictTracker implements interfaces.IDocumentMergeConflictTracker { + constructor(private parent: DocumentMergeConflictTracker, private origin: string) { + } + + getConflicts(document: vscode.TextDocument): PromiseLike { + return this.parent.getConflicts(document, this.origin); + } + + isPending(document: vscode.TextDocument): boolean { + return this.parent.isPending(document, this.origin); + } + + forget(document: vscode.TextDocument) { + this.parent.forget(document); + } +} + +export default class DocumentMergeConflictTracker implements vscode.Disposable, interfaces.IDocumentMergeConflictTrackerService { + private cache: Map = new Map(); + private delayExpireTime: number = 250; + + getConflicts(document: vscode.TextDocument, origin: string): PromiseLike { + // Attempt from cache + + let key = this.getCacheKey(document); + + if (!key) { + // Document doesn't have a uri, can't cache it, so return + return Promise.resolve(this.getConflictsOrEmpty(document, [origin])); + } + + let cacheItem = this.cache.get(key); + if (!cacheItem) { + cacheItem = new ScanTask(this.delayExpireTime, origin); + this.cache.set(key, cacheItem); + } + else { + cacheItem.addOrigin(origin); + } + + return cacheItem.delayTask.trigger(() => { + let conflicts = this.getConflictsOrEmpty(document, Array.from(cacheItem!.origins)); + + if (this.cache) { + this.cache.delete(key!); + } + + return conflicts; + }); + } + + isPending(document: vscode.TextDocument, origin: string): boolean { + if (!document) { + return false; + } + + let key = this.getCacheKey(document); + if (!key) { + return false; + } + + var task = this.cache.get(key); + + if (!task) { + return false; + } + + return task.hasOrigin(origin); + } + + createTracker(origin: string): interfaces.IDocumentMergeConflictTracker { + return new OriginDocumentMergeConflictTracker(this, origin); + } + + forget(document: vscode.TextDocument) { + let key = this.getCacheKey(document); + + if (key) { + this.cache.delete(key); + } + } + + dispose() { + this.cache.clear(); + } + + private getConflictsOrEmpty(document: vscode.TextDocument, origins: string[]): interfaces.IDocumentMergeConflict[] { + const containsConflict = MergeConflictParser.containsConflict(document); + + if (!containsConflict) { + return []; + } + + const conflicts = MergeConflictParser.scanDocument(document); + return conflicts; + } + + private getCacheKey(document: vscode.TextDocument): string | null { + if (document.uri && document.uri) { + return document.uri.toString(); + } + + return null; + } +} + diff --git a/extensions/merge-conflict/src/extension.ts b/extensions/merge-conflict/src/extension.ts new file mode 100644 index 0000000000..a5c20025d4 --- /dev/null +++ b/extensions/merge-conflict/src/extension.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vscode-nls'; +nls.config(process.env.VSCODE_NLS_CONFIG)(); +import * as vscode from 'vscode'; +import MergeConflictServices from './services'; + +export function activate(context: vscode.ExtensionContext) { + // Register disposables + const services = new MergeConflictServices(context); + services.begin(); + context.subscriptions.push(services); +} + +export function deactivate() { +} + diff --git a/extensions/merge-conflict/src/interfaces.ts b/extensions/merge-conflict/src/interfaces.ts new file mode 100644 index 0000000000..b6e8363d3f --- /dev/null +++ b/extensions/merge-conflict/src/interfaces.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; + +export interface IMergeRegion { + name: string; + header: vscode.Range; + content: vscode.Range; + decoratorContent: vscode.Range; +} + +export enum CommitType { + Current, + Incoming, + Both +} + +export interface IExtensionConfiguration { + enableCodeLens: boolean; + enableDecorations: boolean; + enableEditorOverview: boolean; +} + +export interface IDocumentMergeConflict extends IDocumentMergeConflictDescriptor { + commitEdit(type: CommitType, editor: vscode.TextEditor, edit?: vscode.TextEditorEdit); + applyEdit(type: CommitType, editor: vscode.TextEditor, edit: vscode.TextEditorEdit); +} + +export interface IDocumentMergeConflictDescriptor { + range: vscode.Range; + current: IMergeRegion; + incoming: IMergeRegion; + commonAncestors: IMergeRegion[]; + splitter: vscode.Range; +} + +export interface IDocumentMergeConflictTracker { + getConflicts(document: vscode.TextDocument): PromiseLike; + isPending(document: vscode.TextDocument): boolean; + forget(document: vscode.TextDocument); +} + +export interface IDocumentMergeConflictTrackerService { + createTracker(origin: string): IDocumentMergeConflictTracker; + forget(document: vscode.TextDocument); +} diff --git a/extensions/merge-conflict/src/mergeConflictParser.ts b/extensions/merge-conflict/src/mergeConflictParser.ts new file mode 100644 index 0000000000..58649c3111 --- /dev/null +++ b/extensions/merge-conflict/src/mergeConflictParser.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import * as interfaces from './interfaces'; +import { DocumentMergeConflict } from './documentMergeConflict'; + +const startHeaderMarker = '<<<<<<<'; +const commonAncestorsMarker = '|||||||'; +const splitterMarker = '======='; +const endFooterMarker = '>>>>>>>'; + +interface IScanMergedConflict { + startHeader: vscode.TextLine; + commonAncestors: vscode.TextLine[]; + splitter?: vscode.TextLine; + endFooter?: vscode.TextLine; +} + +export class MergeConflictParser { + + static scanDocument(document: vscode.TextDocument): interfaces.IDocumentMergeConflict[] { + + // Scan each line in the document, we already know there is at least a <<<<<<< and + // >>>>>> marker within the document, we need to group these into conflict ranges. + // We initially build a scan match, that references the lines of the header, splitter + // and footer. This is then converted into a full descriptor containing all required + // ranges. + + let currentConflict: IScanMergedConflict | null = null; + const conflictDescriptors: interfaces.IDocumentMergeConflictDescriptor[] = []; + + for (let i = 0; i < document.lineCount; i++) { + const line = document.lineAt(i); + + // Ignore empty lines + if (!line || line.isEmptyOrWhitespace) { + continue; + } + + // Is this a start line? <<<<<<< + if (line.text.startsWith(startHeaderMarker)) { + if (currentConflict !== null) { + // Error, we should not see a startMarker before we've seen an endMarker + currentConflict = null; + + // Give up parsing, anything matched up this to this point will be decorated + // anything after will not + break; + } + + // Create a new conflict starting at this line + currentConflict = { startHeader: line, commonAncestors: [] }; + } + // Are we within a conflict block and is this a common ancestors marker? ||||||| + else if (currentConflict && !currentConflict.splitter && line.text.startsWith(commonAncestorsMarker)) { + currentConflict.commonAncestors.push(line); + } + // Are we within a conflict block and is this a splitter? ======= + else if (currentConflict && !currentConflict.splitter && line.text.startsWith(splitterMarker)) { + currentConflict.splitter = line; + } + // Are we within a conflict block and is this a footer? >>>>>>> + else if (currentConflict && line.text.startsWith(endFooterMarker)) { + currentConflict.endFooter = line; + + // Create a full descriptor from the lines that we matched. This can return + // null if the descriptor could not be completed. + let completeDescriptor = MergeConflictParser.scanItemTolMergeConflictDescriptor(document, currentConflict); + + if (completeDescriptor !== null) { + conflictDescriptors.push(completeDescriptor); + } + + // Reset the current conflict to be empty, so we can match the next + // starting header marker. + currentConflict = null; + } + } + + return conflictDescriptors + .filter(Boolean) + .map(descriptor => new DocumentMergeConflict(document, descriptor)); + } + + private static scanItemTolMergeConflictDescriptor(document: vscode.TextDocument, scanned: IScanMergedConflict): interfaces.IDocumentMergeConflictDescriptor | null { + // Validate we have all the required lines within the scan item. + if (!scanned.startHeader || !scanned.splitter || !scanned.endFooter) { + return null; + } + + let tokenAfterCurrentBlock: vscode.TextLine = scanned.commonAncestors[0] || scanned.splitter; + + // Assume that descriptor.current.header, descriptor.incoming.header and descriptor.splitter + // have valid ranges, fill in content and total ranges from these parts. + // NOTE: We need to shift the decorator range back one character so the splitter does not end up with + // two decoration colors (current and splitter), if we take the new line from the content into account + // the decorator will wrap to the next line. + return { + current: { + header: scanned.startHeader.range, + decoratorContent: new vscode.Range( + scanned.startHeader.rangeIncludingLineBreak.end, + MergeConflictParser.shiftBackOneCharacter(document, tokenAfterCurrentBlock.range.start, scanned.startHeader.rangeIncludingLineBreak.end)), + // Current content is range between header (shifted for linebreak) and splitter or common ancestors mark start + content: new vscode.Range( + scanned.startHeader.rangeIncludingLineBreak.end, + tokenAfterCurrentBlock.range.start), + name: scanned.startHeader.text.substring(startHeaderMarker.length + 1) + }, + commonAncestors: scanned.commonAncestors.map((currentTokenLine, index, commonAncestors) => { + let nextTokenLine = commonAncestors[index + 1] || scanned.splitter; + return { + header: currentTokenLine.range, + decoratorContent: new vscode.Range( + currentTokenLine.rangeIncludingLineBreak.end, + MergeConflictParser.shiftBackOneCharacter(document, nextTokenLine.range.start, currentTokenLine.rangeIncludingLineBreak.end)), + // Each common ancestors block is range between one common ancestors token + // (shifted for linebreak) and start of next common ancestors token or splitter + content: new vscode.Range( + currentTokenLine.rangeIncludingLineBreak.end, + nextTokenLine.range.start), + name: currentTokenLine.text.substring(commonAncestorsMarker.length + 1) + }; + }), + splitter: scanned.splitter.range, + incoming: { + header: scanned.endFooter.range, + decoratorContent: new vscode.Range( + scanned.splitter.rangeIncludingLineBreak.end, + MergeConflictParser.shiftBackOneCharacter(document, scanned.endFooter.range.start, scanned.splitter.rangeIncludingLineBreak.end)), + // Incoming content is range between splitter (shifted for linebreak) and footer start + content: new vscode.Range( + scanned.splitter.rangeIncludingLineBreak.end, + scanned.endFooter.range.start), + name: scanned.endFooter.text.substring(endFooterMarker.length + 1) + }, + // Entire range is between current header start and incoming header end (including line break) + range: new vscode.Range(scanned.startHeader.range.start, scanned.endFooter.rangeIncludingLineBreak.end) + }; + } + + static containsConflict(document: vscode.TextDocument): boolean { + if (!document) { + return false; + } + + let text = document.getText(); + return text.includes(startHeaderMarker) && text.includes(endFooterMarker); + } + + private static shiftBackOneCharacter(document: vscode.TextDocument, range: vscode.Position, unlessEqual: vscode.Position): vscode.Position { + if (range.isEqual(unlessEqual)) { + return range; + } + + let line = range.line; + let character = range.character - 1; + + if (character < 0) { + line--; + character = document.lineAt(line).range.end.character; + } + + return new vscode.Position(line, character); + } +} diff --git a/extensions/merge-conflict/src/mergeDecorator.ts b/extensions/merge-conflict/src/mergeDecorator.ts new file mode 100644 index 0000000000..526552cfc8 --- /dev/null +++ b/extensions/merge-conflict/src/mergeDecorator.ts @@ -0,0 +1,252 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import * as interfaces from './interfaces'; +import { loadMessageBundle } from 'vscode-nls'; +const localize = loadMessageBundle(); + +export default class MergeDectorator implements vscode.Disposable { + + private decorations: { [key: string]: vscode.TextEditorDecorationType } = {}; + + private decorationUsesWholeLine: boolean = true; // Useful for debugging, set to false to see exact match ranges + + private config: interfaces.IExtensionConfiguration; + private tracker: interfaces.IDocumentMergeConflictTracker; + private updating = new Map(); + + constructor(private context: vscode.ExtensionContext, trackerService: interfaces.IDocumentMergeConflictTrackerService) { + this.tracker = trackerService.createTracker('decorator'); + } + + begin(config: interfaces.IExtensionConfiguration) { + this.config = config; + this.registerDecorationTypes(config); + + // Check if we already have a set of active windows, attempt to track these. + vscode.window.visibleTextEditors.forEach(e => this.applyDecorations(e)); + + vscode.workspace.onDidOpenTextDocument(event => { + this.applyDecorationsFromEvent(event); + }, null, this.context.subscriptions); + + vscode.workspace.onDidChangeTextDocument(event => { + this.applyDecorationsFromEvent(event.document); + }, null, this.context.subscriptions); + + vscode.window.onDidChangeVisibleTextEditors((e) => { + // Any of which could be new (not just the active one). + e.forEach(e => this.applyDecorations(e)); + }, null, this.context.subscriptions); + } + + configurationUpdated(config: interfaces.IExtensionConfiguration) { + this.config = config; + this.registerDecorationTypes(config); + + // Re-apply the decoration + vscode.window.visibleTextEditors.forEach(e => { + this.removeDecorations(e); + this.applyDecorations(e); + }); + } + + private registerDecorationTypes(config: interfaces.IExtensionConfiguration) { + + // Dispose of existing decorations + Object.keys(this.decorations).forEach(k => this.decorations[k].dispose()); + this.decorations = {}; + + // None of our features are enabled + if (!config.enableDecorations || !config.enableEditorOverview) { + return; + } + + // Create decorators + if (config.enableDecorations || config.enableEditorOverview) { + this.decorations['current.content'] = vscode.window.createTextEditorDecorationType( + this.generateBlockRenderOptions('merge.currentContentBackground', 'editorOverviewRuler.currentContentForeground', config) + ); + + this.decorations['incoming.content'] = vscode.window.createTextEditorDecorationType( + this.generateBlockRenderOptions('merge.incomingContentBackground', 'editorOverviewRuler.incomingContentForeground', config) + ); + + this.decorations['commonAncestors.content'] = vscode.window.createTextEditorDecorationType( + this.generateBlockRenderOptions('merge.commonContentBackground', 'editorOverviewRuler.commonContentForeground', config) + ); + } + + if (config.enableDecorations) { + this.decorations['current.header'] = vscode.window.createTextEditorDecorationType({ + isWholeLine: this.decorationUsesWholeLine, + backgroundColor: new vscode.ThemeColor('merge.currentHeaderBackground'), + color: new vscode.ThemeColor('editor.foreground'), + outlineStyle: 'solid', + outlineWidth: '1pt', + outlineColor: new vscode.ThemeColor('merge.border'), + after: { + contentText: ' ' + localize('currentChange', '(Current Change)'), + color: new vscode.ThemeColor('descriptionForeground') + } + }); + + this.decorations['commonAncestors.header'] = vscode.window.createTextEditorDecorationType({ + isWholeLine: this.decorationUsesWholeLine, + backgroundColor: new vscode.ThemeColor('merge.commonHeaderBackground'), + color: new vscode.ThemeColor('editor.foreground'), + outlineStyle: 'solid', + outlineWidth: '1pt', + outlineColor: new vscode.ThemeColor('merge.border') + }); + + this.decorations['splitter'] = vscode.window.createTextEditorDecorationType({ + color: new vscode.ThemeColor('editor.foreground'), + outlineStyle: 'solid', + outlineWidth: '1pt', + outlineColor: new vscode.ThemeColor('merge.border'), + isWholeLine: this.decorationUsesWholeLine, + }); + + this.decorations['incoming.header'] = vscode.window.createTextEditorDecorationType({ + backgroundColor: new vscode.ThemeColor('merge.incomingHeaderBackground'), + color: new vscode.ThemeColor('editor.foreground'), + outlineStyle: 'solid', + outlineWidth: '1pt', + outlineColor: new vscode.ThemeColor('merge.border'), + isWholeLine: this.decorationUsesWholeLine, + after: { + contentText: ' ' + localize('incomingChange', '(Incoming Change)'), + color: new vscode.ThemeColor('descriptionForeground') + } + }); + } + } + + dispose() { + + // TODO: Replace with Map + Object.keys(this.decorations).forEach(name => { + this.decorations[name].dispose(); + }); + + this.decorations = {}; + } + + private generateBlockRenderOptions(backgroundColor: string, overviewRulerColor: string, config: interfaces.IExtensionConfiguration): vscode.DecorationRenderOptions { + + let renderOptions: vscode.DecorationRenderOptions = {}; + + if (config.enableDecorations) { + renderOptions.backgroundColor = new vscode.ThemeColor(backgroundColor); + renderOptions.isWholeLine = this.decorationUsesWholeLine; + } + + if (config.enableEditorOverview) { + renderOptions.overviewRulerColor = new vscode.ThemeColor(overviewRulerColor); + renderOptions.overviewRulerLane = vscode.OverviewRulerLane.Full; + } + + return renderOptions; + } + + private applyDecorationsFromEvent(eventDocument: vscode.TextDocument) { + for (var i = 0; i < vscode.window.visibleTextEditors.length; i++) { + if (vscode.window.visibleTextEditors[i].document === eventDocument) { + // Attempt to apply + this.applyDecorations(vscode.window.visibleTextEditors[i]); + } + } + } + + private async applyDecorations(editor: vscode.TextEditor) { + if (!editor || !editor.document) { return; } + + if (!this.config || (!this.config.enableDecorations && !this.config.enableEditorOverview)) { + return; + } + + // If we have a pending scan from the same origin, exit early. (Cannot use this.tracker.isPending() because decorations are per editor.) + if (this.updating.get(editor)) { + return; + } + + try { + this.updating.set(editor, true); + + let conflicts = await this.tracker.getConflicts(editor.document); + if (vscode.window.visibleTextEditors.indexOf(editor) === -1) { + return; + } + + if (conflicts.length === 0) { + this.removeDecorations(editor); + return; + } + + // Store decorations keyed by the type of decoration, set decoration wants a "style" + // to go with it, which will match this key (see constructor); + let matchDecorations: { [key: string]: vscode.DecorationOptions[] } = {}; + + let pushDecoration = (key: string, d: vscode.DecorationOptions) => { + matchDecorations[key] = matchDecorations[key] || []; + matchDecorations[key].push(d); + }; + + conflicts.forEach(conflict => { + // TODO, this could be more effective, just call getMatchPositions once with a map of decoration to position + if (!conflict.current.decoratorContent.isEmpty) { + pushDecoration('current.content', { range: conflict.current.decoratorContent }); + } + if (!conflict.incoming.decoratorContent.isEmpty) { + pushDecoration('incoming.content', { range: conflict.incoming.decoratorContent }); + } + + conflict.commonAncestors.forEach(commonAncestorsRegion => { + if (!commonAncestorsRegion.decoratorContent.isEmpty) { + pushDecoration('commonAncestors.content', { range: commonAncestorsRegion.decoratorContent }); + } + }); + + if (this.config.enableDecorations) { + pushDecoration('current.header', { range: conflict.current.header }); + pushDecoration('splitter', { range: conflict.splitter }); + pushDecoration('incoming.header', { range: conflict.incoming.header }); + + conflict.commonAncestors.forEach(commonAncestorsRegion => { + pushDecoration('commonAncestors.header', { range: commonAncestorsRegion.header }); + }); + } + }); + + // For each match we've generated, apply the generated decoration with the matching decoration type to the + // editor instance. Keys in both matches and decorations should match. + Object.keys(matchDecorations).forEach(decorationKey => { + let decorationType = this.decorations[decorationKey]; + + if (decorationType) { + editor.setDecorations(decorationType, matchDecorations[decorationKey]); + } + }); + + } finally { + this.updating.delete(editor); + } + } + + private removeDecorations(editor: vscode.TextEditor) { + // Remove all decorations, there might be none + Object.keys(this.decorations).forEach(decorationKey => { + + // Race condition, while editing the settings, it's possible to + // generate regions before the configuration has been refreshed + let decorationType = this.decorations[decorationKey]; + + if (decorationType) { + editor.setDecorations(decorationType, []); + } + }); + } +} \ No newline at end of file diff --git a/extensions/merge-conflict/src/services.ts b/extensions/merge-conflict/src/services.ts new file mode 100644 index 0000000000..dd1250ed49 --- /dev/null +++ b/extensions/merge-conflict/src/services.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import DocumentTracker from './documentTracker'; +import CodeLensProvider from './codelensProvider'; +import CommandHandler from './commandHandler'; +import ContentProvider from './contentProvider'; +import Decorator from './mergeDecorator'; +import * as interfaces from './interfaces'; + +const ConfigurationSectionName = 'merge-conflict'; + +export default class ServiceWrapper implements vscode.Disposable { + + private services: vscode.Disposable[] = []; + + constructor(private context: vscode.ExtensionContext) { + } + + begin() { + + let configuration = this.createExtensionConfiguration(); + const documentTracker = new DocumentTracker(); + + this.services.push( + documentTracker, + new CommandHandler(this.context, documentTracker), + new CodeLensProvider(this.context, documentTracker), + new ContentProvider(this.context), + new Decorator(this.context, documentTracker), + ); + + this.services.forEach((service: any) => { + if (service.begin && service.begin instanceof Function) { + service.begin(configuration); + } + }); + + vscode.workspace.onDidChangeConfiguration(() => { + this.services.forEach((service: any) => { + if (service.configurationUpdated && service.configurationUpdated instanceof Function) { + service.configurationUpdated(this.createExtensionConfiguration()); + } + }); + }); + } + + createExtensionConfiguration(): interfaces.IExtensionConfiguration { + const workspaceConfiguration = vscode.workspace.getConfiguration(ConfigurationSectionName); + const codeLensEnabled: boolean = workspaceConfiguration.get('codeLens.enabled', true); + const decoratorsEnabled: boolean = workspaceConfiguration.get('decorators.enabled', true); + + return { + enableCodeLens: codeLensEnabled, + enableDecorations: decoratorsEnabled, + enableEditorOverview: decoratorsEnabled + }; + } + + dispose() { + this.services.forEach(disposable => disposable.dispose()); + this.services = []; + } +} + diff --git a/extensions/merge-conflict/src/typings/refs.d.ts b/extensions/merge-conflict/src/typings/refs.d.ts new file mode 100644 index 0000000000..acf3ed8429 --- /dev/null +++ b/extensions/merge-conflict/src/typings/refs.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions/merge-conflict/tsconfig.json b/extensions/merge-conflict/tsconfig.json new file mode 100644 index 0000000000..ce3e55fff2 --- /dev/null +++ b/extensions/merge-conflict/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es6", + "lib": [ + "es2016" + ], + "module": "commonjs", + "outDir": "./out", + "strictNullChecks": true, + "experimentalDecorators": true + }, + "include": [ + "src/**/*" + ] +} diff --git a/extensions/ms-vscode.node-debug/OSSREADME.json b/extensions/ms-vscode.node-debug/OSSREADME.json new file mode 100644 index 0000000000..8a1d797344 --- /dev/null +++ b/extensions/ms-vscode.node-debug/OSSREADME.json @@ -0,0 +1,129 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[ +{ + "name": "agent-base", + "repositoryURL": "https://github.com/TooTallNate/node-agent-base", + "version": "1.0.2", + "license": "MIT", + "isProd": true +}, +{ + "name": "balanced-match", + "repositoryURL": "https://github.com/juliangruber/balanced-match", + "version": "0.4.2", + "license": "MIT", + "isProd": true +}, +{ + "name": "brace-expansion", + "repositoryURL": "https://github.com/juliangruber/brace-expansion", + "version": "1.1.6", + "license": "MIT", + "isProd": true +}, +{ + "name": "concat-map", + "repositoryURL": "https://github.com/substack/node-concat-map", + "version": "0.0.1", + "license": "MIT", + "isProd": true +}, +{ + "name": "debug", + "repositoryURL": "https://github.com/visionmedia/debug", + "version": "2.3.3", + "license": "MIT", + "isProd": true +}, +{ + "name": "extend", + "repositoryURL": "https://github.com/justmoon/node-extend", + "version": "3.0.0", + "license": "MIT", + "isProd": true +}, +{ + "name": "fs.realpath", + "repositoryURL": "https://github.com/isaacs/fs.realpath", + "version": "1.0.0", + "license": "ISC", + "isProd": true +}, +{ + "name": "glob", + "repositoryURL": "https://github.com/isaacs/node-glob", + "version": "7.1.1", + "license": "ISC", + "isProd": true +}, +{ + "name": "http-proxy-agent", + "repositoryURL": "https://github.com/TooTallNate/node-http-proxy-agent", + "version": "0.2.7", + "license": "MIT", + "isProd": true +}, +{ + "name": "https-proxy-agent", + "repositoryURL": "https://github.com/TooTallNate/node-https-proxy-agent", + "version": "0.3.6", + "license": "MIT", + "isProd": true +}, +{ + "name": "inflight", + "repositoryURL": "https://github.com/npm/inflight", + "version": "1.0.6", + "license": "ISC", + "isProd": true +}, +{ + "name": "inherits", + "repositoryURL": "https://github.com/isaacs/inherits", + "version": "2.0.3", + "license": "ISC", + "isProd": true +}, +{ + "name": "minimatch", + "repositoryURL": "https://github.com/isaacs/minimatch", + "version": "3.0.3", + "license": "ISC", + "isProd": true +}, +{ + "name": "ms", + "repositoryURL": "https://github.com/rauchg/ms.js", + "version": "0.7.2", + "license": "MIT", + "isProd": true +}, +{ + "name": "once", + "repositoryURL": "https://github.com/isaacs/once", + "version": "1.4.0", + "license": "ISC", + "isProd": true +}, +{ + "name": "path-is-absolute", + "repositoryURL": "https://github.com/sindresorhus/path-is-absolute", + "version": "1.0.1", + "license": "MIT", + "isProd": true +}, +{ + "name": "source-map", + "repositoryURL": "https://github.com/mozilla/source-map", + "version": "0.5.6", + "license": "BSD-3-Clause", + "isProd": true +}, +{ + "name": "wrappy", + "repositoryURL": "https://github.com/npm/wrappy", + "version": "1.0.2", + "license": "ISC", + "isProd": true +} +] \ No newline at end of file diff --git a/extensions/ms-vscode.node-debug/package.json b/extensions/ms-vscode.node-debug/package.json new file mode 100644 index 0000000000..87569809e4 --- /dev/null +++ b/extensions/ms-vscode.node-debug/package.json @@ -0,0 +1,8 @@ +{ + "name": "node-debug-placeholder", + "version": "1.6.0", + "publisher": "vscode", + "engines": { + "vscode": "1.6.x" + } +} \ No newline at end of file diff --git a/extensions/ms-vscode.node-debug2/OSSREADME.json b/extensions/ms-vscode.node-debug2/OSSREADME.json new file mode 100644 index 0000000000..e2b9268eb4 --- /dev/null +++ b/extensions/ms-vscode.node-debug2/OSSREADME.json @@ -0,0 +1,150 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[ +{ + "name": "agent-base", + "repositoryURL": "https://github.com/TooTallNate/node-agent-base", + "version": "1.0.2", + "license": "MIT", + "isProd": true +}, +{ + "name": "balanced-match", + "repositoryURL": "https://github.com/juliangruber/balanced-match", + "version": "0.4.2", + "license": "MIT", + "isProd": true +}, +{ + "name": "brace-expansion", + "repositoryURL": "https://github.com/juliangruber/brace-expansion", + "version": "1.1.6", + "license": "MIT", + "isProd": true +}, +{ + "name": "concat-map", + "repositoryURL": "https://github.com/substack/node-concat-map", + "version": "0.0.1", + "license": "MIT", + "isProd": true +}, +{ + "name": "debug", + "repositoryURL": "https://github.com/visionmedia/debug", + "version": "2.2.0", + "license": "MIT", + "isProd": true +}, +{ + "name": "extend", + "repositoryURL": "https://github.com/justmoon/node-extend", + "version": "3.0.0", + "license": "MIT", + "isProd": true +}, +{ + "name": "fs.realpath", + "repositoryURL": "https://github.com/isaacs/fs.realpath", + "version": "1.0.0", + "license": "ISC", + "isProd": true +}, +{ + "name": "glob", + "repositoryURL": "https://github.com/isaacs/node-glob", + "version": "7.1.0", + "license": "ISC", + "isProd": true +}, +{ + "name": "http-proxy-agent", + "repositoryURL": "https://github.com/TooTallNate/node-http-proxy-agent", + "version": "0.2.7", + "license": "MIT", + "isProd": true +}, +{ + "name": "https-proxy-agent", + "repositoryURL": "https://github.com/TooTallNate/node-https-proxy-agent", + "version": "0.3.6", + "license": "MIT", + "isProd": true +}, +{ + "name": "inflight", + "repositoryURL": "https://github.com/npm/inflight", + "version": "1.0.5", + "license": "ISC", + "isProd": true +}, +{ + "name": "inherits", + "repositoryURL": "https://github.com/isaacs/inherits", + "version": "2.0.3", + "license": "ISC", + "isProd": true +}, +{ + "name": "minimatch", + "repositoryURL": "https://github.com/isaacs/minimatch", + "version": "3.0.3", + "license": "ISC", + "isProd": true +}, +{ + "name": "ms", + "repositoryURL": "https://github.com/rauchg/ms.js", + "version": "0.7.1", + "license": "MIT", + "isProd": true +}, +{ + "name": "once", + "repositoryURL": "https://github.com/isaacs/once", + "version": "1.4.0", + "license": "ISC", + "isProd": true +}, +{ + "name": "options", + "repositoryURL": "https://github.com/einaros/options.js", + "version": "0.0.6", + "license": "MIT", + "isProd": true +}, +{ + "name": "ultron", + "repositoryURL": "https://github.com/unshiftio/ultron", + "version": "1.0.2", + "license": "MIT", + "isProd": true +}, +{ + "name": "path-is-absolute", + "repositoryURL": "https://github.com/sindresorhus/path-is-absolute", + "version": "1.0.0", + "license": "MIT", + "isProd": true +}, +{ + "name": "source-map", + "repositoryURL": "https://github.com/mozilla/source-map", + "version": "0.5.6", + "license": "BSD-3-Clause", + "isProd": true +}, +{ + "name": "wrappy", + "repositoryURL": "https://github.com/npm/wrappy", + "version": "1.0.2", + "license": "ISC", + "isProd": true +}, +{ + "name": "ws", + "repositoryURL": "https://github.com/websockets/ws", + "version": "1.1.1", + "license": "MIT", + "isProd": true +} +] \ No newline at end of file diff --git a/extensions/ms-vscode.node-debug2/package.json b/extensions/ms-vscode.node-debug2/package.json new file mode 100644 index 0000000000..26777f7dae --- /dev/null +++ b/extensions/ms-vscode.node-debug2/package.json @@ -0,0 +1,8 @@ +{ + "name": "node-debug2-placeholder", + "version": "0.0.3", + "publisher": "vscode", + "engines": { + "vscode": "1.6.x" + } +} \ No newline at end of file diff --git a/extensions/mssql/.gitignore b/extensions/mssql/.gitignore new file mode 100644 index 0000000000..e58344bcee --- /dev/null +++ b/extensions/mssql/.gitignore @@ -0,0 +1 @@ +sqltoolsservice diff --git a/extensions/mssql/client/src/config.json b/extensions/mssql/client/src/config.json new file mode 100644 index 0000000000..5c3c7575cd --- /dev/null +++ b/extensions/mssql/client/src/config.json @@ -0,0 +1,1127 @@ +{ + "service": { + "downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}", + "version": "1.2.0-alpha.37", + "downloadFileNames": { + "Windows_86": "win-x86-netcoreapp2.0.zip", + "Windows_64": "win-x64-netcoreapp2.0.zip", + "OSX": "osx-x64-netcoreapp2.0.tar.gz", + "CentOS_7": "rhel-x64-netcoreapp2.0.tar.gz", + "Debian_8": "rhel-x64-netcoreapp2.0.tar.gz", + "Fedora_23": "rhel-x64-netcoreapp2.0.tar.gz", + "OpenSUSE_13_2": "rhel-x64-netcoreapp2.0.tar.gz", + "RHEL_7": "rhel-x64-netcoreapp2.0.tar.gz", + "SLES_12_2": "rhel-x64-netcoreapp2.0.tar.gz", + "Ubuntu_14": "rhel-x64-netcoreapp2.0.tar.gz", + "Ubuntu_16": "rhel-x64-netcoreapp2.0.tar.gz" + }, + "installDir": "sqltoolsservice/{#platform#}/{#version#}", + "executableFiles": ["MicrosoftSqlToolsServiceLayer.exe", "MicrosoftSqlToolsServiceLayer"], + "serverConnectionMetadata": { + "capabilities": { + "protocolVersion": "1.0", + "providerName": "MSSQL", + "providerDisplayName": "Microsoft SQL Server", + "connectionProvider": { + "options": [ + { + "specialValueType": "serverName", + "isIdentity": true, + "name": "server", + "displayName": "Server name", + "description": "Name of the SQL Server instance", + "groupName": "Source", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "specialValueType": "databaseName", + "isIdentity": true, + "name": "database", + "displayName": "Database name", + "description": "The name of the initial catalog or database int the data source", + "groupName": "Source", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": "authType", + "isIdentity": true, + "name": "authenticationType", + "displayName": "Authentication type", + "description": "Specifies the method of authenticating with SQL Server", + "groupName": "Security", + "valueType": "category", + "defaultValue": null, + "objectType": null, + "categoryValues": [ + { + "displayName": "SQL Login", + "name": "SqlLogin" + }, + { + "displayName": "Windows Authentication", + "name": "Integrated" + } + ], + "isRequired": true, + "isArray": false + }, + { + "specialValueType": "userName", + "isIdentity": true, + "name": "user", + "displayName": "User name", + "description": "Indicates the user ID to be used when connecting to the data source", + "groupName": "Security", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "specialValueType": "password", + "isIdentity": true, + "name": "password", + "displayName": "Password", + "description": "Indicates the password to be used when connecting to the data source", + "groupName": "Security", + "valueType": "password", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "applicationIntent", + "displayName": "Application intent", + "description": "Declares the application workload type when connecting to a server", + "groupName": "Initialization", + "valueType": "category", + "defaultValue": null, + "objectType": null, + "categoryValues": [ + { + "displayName": "ReadWrite", + "name": "ReadWrite" + }, + { + "displayName": "ReadOnly", + "name": "ReadOnly" + } + ], + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "asynchronousProcessing", + "displayName": "Asynchronous processing enabled", + "description": "When true, enables usage of the Asynchronous functionality in the .Net Framework Data Provider", + "groupName": "Initialization", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "connectTimeout", + "displayName": "Connect timeout", + "description": "The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error", + "groupName": "Initialization", + "valueType": "number", + "defaultValue": "15", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "currentLanguage", + "displayName": "Current language", + "description": "The SQL Server language record name", + "groupName": "Initialization", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "columnEncryptionSetting", + "displayName": "Column encryption setting", + "description": "Default column encryption setting for all the commands on the connection", + "groupName": "Security", + "valueType": "category", + "defaultValue": null, + "objectType": null, + "categoryValues": [ + { + "displayName": null, + "name": "Disabled" + }, + { + "displayName": null, + "name": "Enabled" + } + ], + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "encrypt", + "displayName": "Encrypt", + "description": "When true, SQL Server uses SSL encryption for all data sent between the client and server if the servers has a certificate installed", + "groupName": "Security", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "persistSecurityInfo", + "displayName": "Persist security info", + "description": "When false, security-sensitive information, such as the password, is not returned as part of the connection", + "groupName": "Security", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "trustServerCertificate", + "displayName": "Trust server certificate", + "description": "When true (and encrypt=true), SQL Server uses SSL encryption for all data sent between the client and server without validating the server certificate", + "groupName": "Security", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "attachedDBFileName", + "displayName": "Attached DB file name", + "description": "The name of the primary file, including the full path name, of an attachable database", + "groupName": "Source", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "contextConnection", + "displayName": "Context connection", + "description": "When true, indicates the connection should be from the SQL server context. Available only when running in the SQL Server process", + "groupName": "Source", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "port", + "displayName": "Port", + "description": null, + "groupName": null, + "valueType": "number", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "connectRetryCount", + "displayName": "Connect retry count", + "description": "Number of attempts to restore connection", + "groupName": "Connection Resiliency", + "valueType": "number", + "defaultValue": "1", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "connectRetryInterval", + "displayName": "Connect retry interval", + "description": "Delay between attempts to restore connection", + "groupName": "Connection Resiliency", + "valueType": "number", + "defaultValue": "10", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": "appName", + "isIdentity": false, + "name": "applicationName", + "displayName": "Application name", + "description": "The name of the application", + "groupName": "Context", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "workstationId", + "displayName": "Workstation Id", + "description": "The name of the workstation connecting to SQL Server", + "groupName": "Context", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "pooling", + "displayName": "Pooling", + "description": "When true, the connection object is drawn from the appropriate pool, or if necessary, is created and added to the appropriate pool", + "groupName": "Pooling", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "maxPoolSize", + "displayName": "Max pool size", + "description": "The maximum number of connections allowed in the pool", + "groupName": "Pooling", + "valueType": "number", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "minPoolSize", + "displayName": "Min pool size", + "description": "The minimum number of connections allowed in the pool", + "groupName": "Pooling", + "valueType": "number", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "loadBalanceTimeout", + "displayName": "Load balance timeout", + "description": "The minimum amount of time (in seconds) for this connection to live in the pool before being destroyed", + "groupName": "Pooling", + "valueType": "number", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "replication", + "displayName": "Replication", + "description": "Used by SQL Server in Replication", + "groupName": "Replication", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "attachDbFilename", + "displayName": "Attach DB filename", + "description": null, + "groupName": null, + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "failoverPartner", + "displayName": "Failover partner", + "description": "The name or network address of the instance of SQL Server that acts as a failover partner", + "groupName": " Source", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "multiSubnetFailover", + "displayName": "Multi subnet failover", + "description": null, + "groupName": null, + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "multipleActiveResultSets", + "displayName": "Multiple active result sets", + "description": "When true, multiple result sets can be returned and read from one connection", + "groupName": "Advanced", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "packetSize", + "displayName": "Packet size", + "description": "Size in bytes of the network packets used to communicate with an instance of SQL Server", + "groupName": "Advanced", + "valueType": "number", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "typeSystemVersion", + "displayName": "Type system version", + "description": "Indicates which server type system then provider will expose through the DataReader", + "groupName": "Advanced", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + } + ] + }, + "adminServicesProvider": { + "databaseInfoOptions": [ + { + "name": "name", + "displayName": "Name", + "description": "Name of the database", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "owner", + "displayName": "Owner", + "description": "Database owner", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "collation", + "displayName": "Collation", + "description": "Database collation", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "recursiveTriggers", + "displayName": "Recursive Triggers", + "description": "Recursive triggers", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "trustworthy", + "displayName": "Trustworthy", + "description": "Trustworthy", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "ansiNullDefault", + "displayName": "AnsiNullDefault", + "description": "Ansi null default", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "ansiNulls", + "displayName": "AnsiNulls", + "description": "AnsiNulls", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "ansiNulls", + "displayName": "AnsiPadding", + "description": "Ansi padding", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "ansiNulls", + "displayName": "AnsiWarnings", + "description": "Ansi warnings", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "isFilestreamEnabled", + "displayName": "IsFilestreamEnabled", + "description": "Is filestream enabled", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "isReadCommittedSnapshotOn", + "displayName": "IsReadCommittedSnapshotOn", + "description": "Is read committed snapshot on", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "isReadOnly", + "displayName": "IsReadOnly", + "description": "Is read only", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "isSystemDB", + "displayName": "IsSystemDB", + "description": "Is system database", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "maxDop", + "displayName": "MaxDop", + "description": "Max degree of parallelism", + "groupName": "Other", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "databaseContainmentType", + "displayName": "DatabaseContainmentType", + "description": "Database containment type", + "groupName": "Other", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "databaseState", + "displayName": "DatabaseState", + "description": "Database state", + "groupName": "Other", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "recoveryModel", + "displayName": "RecoveryModel", + "description": "Recovery model", + "groupName": "Other", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "lastBackupDate", + "displayName": "LastBackupDate", + "description": "Last backup date", + "groupName": "Other", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "lastLogBackupDate", + "displayName": "LastLogBackupDate", + "description": "Last log backup date", + "groupName": "Other", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "compatibilityLevel", + "displayName": "CompatibilityLevel", + "description": "Compatibility level", + "groupName": "Other", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "fileGroups", + "displayName": "File Groups", + "description": "File groups", + "groupName": "General", + "valueType": "object", + "defaultValue": null, + "objectType": "FileGroupInfo", + "categoryValues": null, + "isRequired": true, + "isArray": true + }, + { + "name": "databaseFiles", + "displayName": "Database Files", + "description": "Database Files", + "groupName": "General", + "valueType": "object", + "defaultValue": null, + "objectType": "DatabaseFileInfo", + "categoryValues": null, + "isRequired": true, + "isArray": true + } + ], + "databaseFileInfoOptions": [ + { + "name": "name", + "displayName": "Name", + "description": "Name of the database file", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "physicalName", + "displayName": "Physical Name", + "description": "Physical name of the database file", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "autogrowth", + "displayName": "Autogrowth", + "description": "Autogrowth", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "databaseFileType", + "displayName": "DatabaseFileType", + "description": "Database file type", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "folder", + "displayName": "Folder", + "description": "Folder", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "size", + "displayName": "Size", + "description": "Size", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "fileGroup", + "displayName": "FileGroup", + "description": "File group", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "initialSize", + "displayName": "InitialSize", + "description": "Initial size", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "isPrimaryFile", + "displayName": "IsPrimaryFile", + "description": "Is primary file", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + } + ], + "fileGroupInfoOptions": [ + { + "name": "name", + "displayName": "Name", + "description": "Name of the file group", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "fileGroupType", + "displayName": "FileGroupType", + "description": "File group type", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "isDefault", + "displayName": "IsDefault", + "description": "Is default", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "isFileStream", + "displayName": "IsFileStream", + "description": "Is file stream", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "isMemoryOptimized", + "displayName": "IsMemoryOptimized", + "description": "Is memory optimized", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "name": "isReadOnly", + "displayName": "IsReadOnly", + "description": "Is read-only", + "groupName": "General", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + } + ] + }, + "features": [ + { + "enabled": true, + "featureName": "Restore", + "optionsMetadata": [ + { + "name": "keepReplication", + "displayName": "Keep Replication", + "description": "Preserve the replication settings (WITH KEEP_REPLICATION)", + "groupName": "Restore options", + "valueType": "boolean", + "defaultValue": "false", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "replaceDatabase", + "displayName": "ReplaceDatabase", + "description": "Overwrite the existing database (WITH REPLACE)", + "groupName": "Restore options", + "valueType": "boolean", + "defaultValue": "false", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "setRestrictedUser", + "displayName": "SetRestrictedUser", + "description": "Restrict access to the restored database (WITH RESTRICTED_USER)", + "groupName": "Restore options", + "valueType": "boolean", + "defaultValue": "false", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "recoveryState", + "displayName": "Recovery State", + "description": "Recovery State", + "groupName": "Restore options", + "valueType": "category", + "defaultValue": "WithRecovery", + "objectType": null, + "categoryValues": [ + { + "displayName": "RESTORE WITH RECOVERY", + "name": "WithRecovery" + }, + { + "displayName": "RESTORE WITH NORECOVERY", + "name": "WithNoRecovery" + }, + { + "displayName": "RESTORE WITH STANDBY", + "name": "WithStandBy" + } + ], + "isRequired": false, + "isArray": false + }, + { + "name": "standbyFile", + "displayName": "Standby file", + "description": "Standby file", + "groupName": "Restore options", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "backupTailLog", + "displayName": "Backup Tail Log", + "description": "Take tail-log backup before restore", + "groupName": "Tail-Log backup", + "valueType": "boolean", + "defaultValue": "true", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "backupTailLog", + "displayName": "Backup Tail Log", + "description": "Take tail-log backup before restore", + "groupName": "Tail-Log backup", + "valueType": "boolean", + "defaultValue": "true", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "tailLogBackupFile", + "displayName": "Tail Log Backup File", + "description": "Tail Log Backup File", + "groupName": "Tail-Log backup", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "tailLogWithNoRecovery", + "displayName": "Tail Log With NoRecovery", + "description": "Leave source database in the restoring state(WITH NORECOVERY)", + "groupName": "Tail-Log backup", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "closeExistingConnections", + "displayName": "Close Existing Connections", + "description": "Close existing connections to destination database", + "groupName": "Server connections", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "relocateDbFiles", + "displayName": "Relocate all files", + "description": "Relocate all files", + "groupName": "Restore database files as", + "valueType": "boolean", + "defaultValue": "false", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "dataFileFolder", + "displayName": "Data file folder", + "description": "Data file folder", + "groupName": "Restore database files as", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "name": "logFileFolder", + "displayName": "Log file folder", + "description": "Log file folder", + "groupName": "Restore database files as", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + } + ] + }, + { + "enabled": true, + "featureName": "serializationService", + "optionsMetadata": [] + } + ] + } + } + } +} diff --git a/extensions/mssql/client/src/controllers/mainController.ts b/extensions/mssql/client/src/controllers/mainController.ts new file mode 100644 index 0000000000..aa032568be --- /dev/null +++ b/extensions/mssql/client/src/controllers/mainController.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import vscode = require('vscode'); +import data = require('data'); +import { Constants } from '../models/constants'; +import { Serialization } from '../serialize/serialization'; +import { AzureResourceProvider } from '../resourceProvider/resourceProvider'; +import { CredentialStore } from '../credentialstore/credentialstore'; +import {IExtensionConstants, Telemetry, SharedConstants, SqlToolsServiceClient, VscodeWrapper, Utils, PlatformInformation} from 'extensions-modules'; +import { LanguageClient } from 'dataprotocol-client'; + +/** + * The main controller class that initializes the extension + */ +export default class MainController implements vscode.Disposable { + private _context: vscode.ExtensionContext; + private _vscodeWrapper: VscodeWrapper; + private _initialized: boolean = false; + private _serialization: Serialization; + private _credentialStore: CredentialStore; + private static _extensionConstants: IExtensionConstants = new Constants(); + private _client: SqlToolsServiceClient; + /** + * The main controller constructor + * @constructor + */ + constructor(context: vscode.ExtensionContext, + vscodeWrapper?: VscodeWrapper) { + this._context = context; + this._vscodeWrapper = vscodeWrapper || new VscodeWrapper(MainController._extensionConstants); + SqlToolsServiceClient.constants = MainController._extensionConstants; + this._client = SqlToolsServiceClient.instance; + this._credentialStore = new CredentialStore(this._client); + this._serialization = new Serialization(this._client); + } + + /** + * Disposes the controller + */ + dispose(): void { + this.deactivate(); + } + + /** + * Deactivates the extension + */ + public deactivate(): void { + Utils.logDebug(SharedConstants.extensionDeactivated, MainController._extensionConstants.extensionConfigSectionName); + } + + /** + * Initializes the extension + */ + public activate(): Promise { + return this.initialize(); + } + + /** + * Returns a flag indicating if the extension is initialized + */ + public isInitialized(): boolean { + return this._initialized; + } + + private createClient( executableFiles: string[]): Promise { + return PlatformInformation.getCurrent(SqlToolsServiceClient.constants.getRuntimeId, SqlToolsServiceClient.constants.extensionName).then( platformInfo => { + return SqlToolsServiceClient.instance.createClient(this._context, platformInfo.runtimeId, undefined, executableFiles); + }); + } + + private createCredentialClient(): Promise { + return this.createClient(['MicrosoftSqlToolsCredentials.exe', 'MicrosoftSqlToolsCredentials']); + } + + private createSerializationClient(): Promise { + return this.createClient(['MicrosoftSqlToolsSerialization.exe', 'MicrosoftSqlToolsSerialization']); + } + + private createResourceProviderClient(): Promise { + return this.createClient(['SqlToolsResourceProviderService.exe', 'SqlToolsResourceProviderService']); + } + + /** + * Initializes the extension + */ + public initialize(): Promise { + const self = this; + + // initialize language service client + return new Promise( (resolve, reject) => { + const self = this; + SqlToolsServiceClient.instance.initialize(self._context).then(serverResult => { + + // Initialize telemetry + Telemetry.initialize(self._context, new Constants()); + + // telemetry for activation + Telemetry.sendTelemetryEvent('ExtensionActivated', {}, + { serviceInstalled: serverResult.installedBeforeInitializing ? 1 : 0 } + ); + + /* + self.createSerializationClient().then(serializationClient => { + let serialization = new Serialization(self._client, serializationClient); + // Serialization + let serializationProvider: data.SerializationProvider = { + handle: 0, + saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable { + return self._serialization.saveAs(saveFormat, savePath, results, appendToFile); + } + }; + data.serialization.registerProvider(serializationProvider); + }, error => { + Utils.logDebug('Cannot find Serialization executables. error: ' + error , MainController._extensionConstants.extensionConfigSectionName); + }); + + self.createResourceProviderClient().then(rpClient => { + let resourceProvider = new AzureResourceProvider(self._client, rpClient); + data.resources.registerResourceProvider({ + displayName: 'Azure SQL Resource Provider', // TODO Localize + id: 'Microsoft.Azure.SQL.ResourceProvider', + settings: { + + } + }, resourceProvider); + Utils.logDebug('resourceProvider registered', MainController._extensionConstants.extensionConfigSectionName); + }, error => { + Utils.logDebug('Cannot find ResourceProvider executables. error: ' + error , MainController._extensionConstants.extensionConfigSectionName); + }); + */ + + self.createCredentialClient().then(credentialClient => { + + self._credentialStore.languageClient = credentialClient; + let credentialProvider: data.CredentialProvider = { + handle: 0, + saveCredential(credentialId: string, password: string): Thenable { + return self._credentialStore.saveCredential(credentialId, password); + }, + readCredential(credentialId: string): Thenable { + return self._credentialStore.readCredential(credentialId); + }, + deleteCredential(credentialId: string): Thenable { + return self._credentialStore.deleteCredential(credentialId); + } + }; + data.credentials.registerProvider(credentialProvider); + Utils.logDebug('credentialProvider registered', MainController._extensionConstants.extensionConfigSectionName); + }, error => { + Utils.logDebug('Cannot find credentials executables. error: ' + error , MainController._extensionConstants.extensionConfigSectionName); + }); + + + + Utils.logDebug(SharedConstants.extensionActivated, MainController._extensionConstants.extensionConfigSectionName); + self._initialized = true; + resolve(true); + }).catch(err => { + Telemetry.sendTelemetryEventForException(err, 'initialize', MainController._extensionConstants.extensionConfigSectionName); + reject(err); + }); + }); + } +} diff --git a/extensions/mssql/client/src/credentialstore/credentialstore.ts b/extensions/mssql/client/src/credentialstore/credentialstore.ts new file mode 100644 index 0000000000..2d16c0031c --- /dev/null +++ b/extensions/mssql/client/src/credentialstore/credentialstore.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as Contracts from '../models/contracts'; +import { ICredentialStore } from './icredentialstore'; +import { SqlToolsServiceClient, Utils } from 'extensions-modules'; +import { LanguageClient } from 'dataprotocol-client'; + +/** + * Implements a credential storage for Windows, Mac (darwin), or Linux. + * + * Allows a single credential to be stored per service (that is, one username per service); + */ +export class CredentialStore implements ICredentialStore { + + public languageClient: LanguageClient; + + constructor(private _client?: SqlToolsServiceClient) { + if (!this._client) { + this._client = SqlToolsServiceClient.instance; + } + } + + /** + * Gets a credential saved in the credential store + * + * @param {string} credentialId the ID uniquely identifying this credential + * @returns {Promise} Promise that resolved to the credential, or undefined if not found + */ + public readCredential(credentialId: string): Promise { + Utils.logDebug(this.languageClient, 'MainController._extensionConstants'); + let self = this; + let cred: Contracts.Credential = new Contracts.Credential(); + cred.credentialId = credentialId; + return new Promise( (resolve, reject) => { + self._client + .sendRequest(Contracts.ReadCredentialRequest.type, cred, this.languageClient) + .then(returnedCred => { + resolve(returnedCred); + }, err => reject(err)); + }); + } + + public saveCredential(credentialId: string, password: any): Promise { + let self = this; + let cred: Contracts.Credential = new Contracts.Credential(); + cred.credentialId = credentialId; + cred.password = password; + return new Promise( (resolve, reject) => { + self._client + .sendRequest(Contracts.SaveCredentialRequest.type, cred, this.languageClient) + .then(status => { + resolve(status); + }, err => reject(err)); + }); + } + + public deleteCredential(credentialId: string): Promise { + let self = this; + let cred: Contracts.Credential = new Contracts.Credential(); + cred.credentialId = credentialId; + return new Promise( (resolve, reject) => { + self._client + .sendRequest(Contracts.DeleteCredentialRequest.type, cred, this.languageClient) + .then(status => { + resolve(status); + }, err => reject(err)); + }); + } +} diff --git a/extensions/mssql/client/src/credentialstore/icredentialstore.ts b/extensions/mssql/client/src/credentialstore/icredentialstore.ts new file mode 100644 index 0000000000..a9df9cf64c --- /dev/null +++ b/extensions/mssql/client/src/credentialstore/icredentialstore.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +'use strict'; + + +// This code is originally from https://github.com/microsoft/vsts-vscode +// License: https://github.com/Microsoft/vsts-vscode/blob/master/LICENSE.txt + +import { Credential } from '../models/contracts'; + +/** + * A credential store that securely stores sensitive information in a platform-specific manner + * + * @export + * @interface ICredentialStore + */ +export interface ICredentialStore { + readCredential(credentialId: string): Promise; + saveCredential(credentialId: string, password: any): Promise; + deleteCredential(credentialId: string): Promise; +} diff --git a/extensions/mssql/client/src/models/constants.ts b/extensions/mssql/client/src/models/constants.ts new file mode 100644 index 0000000000..61d6831d5a --- /dev/null +++ b/extensions/mssql/client/src/models/constants.ts @@ -0,0 +1,317 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import {IExtensionConstants} from 'extensions-modules/lib/models/contracts/contracts'; +import {Runtime, LinuxDistribution} from 'extensions-modules/lib/models/platform'; + +// constants +export class Constants implements IExtensionConstants { + public readonly languageId = 'sql'; + public readonly extensionName = 'mssql'; + public readonly extensionConfigSectionName = 'mssql'; + public readonly connectionApplicationName = 'vscode-mssql'; + public readonly outputChannelName = 'MSSQL'; + public readonly connectionConfigFilename = 'settings.json'; + public readonly connectionsArrayName = 'mssql.connections'; + public readonly cmdRunQuery = 'extension.runQuery'; + public readonly cmdCancelQuery = 'extension.cancelQuery'; + public readonly cmdConnect = 'extension.connect'; + public readonly cmdDisconnect = 'extension.disconnect'; + public readonly cmdChooseDatabase = 'extension.chooseDatabase'; + public readonly cmdShowReleaseNotes = 'extension.showReleaseNotes'; + public readonly cmdShowGettingStarted = 'extension.showGettingStarted'; + public readonly cmdNewQuery = 'extension.newQuery'; + public readonly cmdManageConnectionProfiles = 'extension.manageProfiles'; + public readonly sqlDbPrefix = '.database.windows.net'; + public readonly defaultConnectionTimeout = 15; + public readonly azureSqlDbConnectionTimeout = 30; + public readonly azureDatabase = 'Azure'; + public readonly defaultPortNumber = 1433; + public readonly sqlAuthentication = 'SqlLogin'; + public readonly defaultDatabase = 'master'; + public readonly errorPasswordExpired = 18487; + public readonly errorPasswordNeedsReset = 18488; + public readonly maxDisplayedStatusTextLength = 50; + public readonly outputContentTypeRoot = 'root'; + public readonly outputContentTypeMessages = 'messages'; + public readonly outputContentTypeResultsetMeta = 'resultsetsMeta'; + public readonly outputContentTypeColumns = 'columns'; + public readonly outputContentTypeRows = 'rows'; + public readonly outputContentTypeConfig = 'config'; + public readonly outputContentTypeSaveResults = 'saveResults'; + public readonly outputContentTypeOpenLink = 'openLink'; + public readonly outputContentTypeCopy = 'copyResults'; + public readonly outputContentTypeEditorSelection = 'setEditorSelection'; + public readonly outputContentTypeShowError = 'showError'; + public readonly outputContentTypeShowWarning = 'showWarning'; + public readonly outputServiceLocalhost = 'http://localhost:'; + public readonly msgContentProviderSqlOutputHtml = 'dist/html/sqlOutput.ejs'; + public readonly contentProviderMinFile = 'dist/js/app.min.js'; + public readonly configLogDebugInfo = 'logDebugInfo'; + public readonly providerId = 'MSSQL'; + public readonly installFolderName = 'sqltoolsservice'; + public readonly telemetryExtensionName = 'carbon-mssql'; + + // localizable strings + public readonly configMyConnectionsNoServerName = 'Missing server name in user preferences connection: '; + public readonly msgLocalWebserviceStaticContent = 'LocalWebService: added static html content path: '; + public readonly msgLocalWebserviceStarted = 'LocalWebService listening on port '; + public readonly msgRunQueryAllBatchesExecuted = 'runQuery: all batches executed'; + public readonly msgStartedExecute = 'Started query execution for document "{0}"'; + public readonly msgFinishedExecute = 'Finished query execution for document "{0}"'; + public readonly msgRunQueryError = 'runQuery: error: '; + public readonly msgRunQueryExecutingBatch = 'runQuery: executeBatch called with SQL: '; + public readonly msgRunQueryAddBatchResultsets = 'runQuery: adding resultsets for batch: '; + public readonly msgRunQueryAddBatchError = 'runQuery: adding error message for batch: '; + public readonly msgRunQueryConnectionActive = 'runQuery: active connection is connected, using it to run query'; + public readonly msgRunQueryConnectionDisconnected = 'runQuery: active connection is disconnected, reconnecting'; + public readonly msgRunQueryNoConnection = 'runQuery: no active connection - prompting for user'; + public readonly msgRunQueryInProgress = 'A query is already running for this editor session. Please cancel this query or wait for its completion.'; + public readonly runQueryBatchStartMessage = 'Started executing query at '; + public readonly runQueryBatchStartLine = 'Line {0}'; + public readonly msgCancelQueryFailed = 'Canceling the query failed: {0}'; + public readonly msgCancelQueryNotRunning = 'Cannot cancel query as no query is running.'; + public readonly msgCancelQuerySuccess = 'Successfully canceled the query.'; + public readonly msgContentProviderOnContentUpdated = 'Content provider: onContentUpdated called'; + public readonly msgContentProviderAssociationFailure = 'Content provider: Unable to associate status view for current file'; + public readonly msgContentProviderOnRootEndpoint = 'LocalWebService: Root end-point called'; + public readonly msgContentProviderOnResultsEndpoint = 'LocalWebService: ResultsetsMeta endpoint called'; + public readonly msgContentProviderOnMessagesEndpoint = 'LocalWebService: Messages end-point called'; + public readonly msgContentProviderOnColumnsEndpoint = 'LocalWebService: Columns end-point called for index = '; + public readonly msgContentProviderOnRowsEndpoint = 'LocalWebService: Rows end-point called for index = '; + public readonly msgContentProviderOnClear = 'Content provider: clear called'; + public readonly msgContentProviderOnUpdateContent = 'Content provider: updateContent called'; + public readonly msgContentProviderProvideContent = 'Content provider: provideTextDocumentContent called: '; + public readonly msgChooseDatabaseNotConnected = 'No connection was found. Please connect to a server first.'; + public readonly msgChooseDatabasePlaceholder = 'Choose a database from the list below'; + public readonly msgConnectionError = 'Error {0}: {1}'; + public readonly msgConnectionError2 = 'Failed to connect: {0}'; + public readonly msgConnectionErrorPasswordExpired = 'Error {0}: {1} Please login as a different user and change the password using ALTER LOGIN.'; + public readonly connectionErrorChannelName = 'Connection Errors'; + public readonly msgPromptCancelConnect = 'Server connection in progress. Do you want to cancel?'; + public readonly msgPromptClearRecentConnections = 'Confirm to clear recent connections list'; + public readonly msgOpenSqlFile = 'To use this command, Open a .sql file -or- ' + + 'Change editor language to "SQL" -or- ' + + 'Select T-SQL text in the active SQL editor.'; + public readonly recentConnectionsPlaceholder = 'Choose a connection profile from the list below'; + public readonly msgNoConnectionsInSettings = 'To use this command, add connection profile to User Settings.'; + public readonly labelOpenGlobalSettings = 'Open Global Settings'; + public readonly labelOpenWorkspaceSettings = 'Open Workspace Settings'; + public readonly CreateProfileFromConnectionsListLabel = 'Create Connection Profile'; + public readonly CreateProfileLabel = 'Create'; + public readonly ClearRecentlyUsedLabel = 'Clear Recent Connections List'; + public readonly EditProfilesLabel = 'Edit'; + public readonly RemoveProfileLabel = 'Remove'; + public readonly ManageProfilesPrompt = 'Manage Connection Profiles'; + public readonly SampleServerName = '{{put-server-name-here}}'; + public readonly serverPrompt = 'Server name'; + public readonly serverPlaceholder = 'hostname\\instance or .database.windows.net'; + public readonly databasePrompt = 'Database name'; + public readonly databasePlaceholder = '[Optional] Database to connect (press Enter to connect to database)'; + public readonly databaseDefaultValue = 'master'; + public readonly authTypePrompt = 'Authentication Type'; + public readonly authTypeIntegrated = 'Integrated'; + public readonly authTypeSql = 'SQL Login'; + public readonly authTypeAdUniversal = 'Active Directory Universal'; + public readonly usernamePrompt = 'User name'; + public readonly usernamePlaceholder = 'User name (SQL Login)'; + public readonly passwordPrompt = 'Password'; + public readonly passwordPlaceholder = 'Password (SQL Login)'; + public readonly msgSavePassword = 'Save Password? If \'No\', password will be required each time you connect'; + public readonly profileNamePrompt = 'Profile Name'; + public readonly profileNamePlaceholder = '[Optional] Enter a name for this profile'; + public readonly filepathPrompt = 'File path'; + public readonly filepathPlaceholder = 'File name'; + public readonly filepathMessage = 'File name'; + public readonly overwritePrompt = 'A file with this name already exists. Do you want to replace the existing file?'; + public readonly overwritePlaceholder = 'A file with this name already exists'; + public readonly msgSaveResultInProgress = 'A save request is already executing. Please wait for its completion.'; + public readonly msgCannotOpenContent = 'Error occurred opening content in editor.'; + public readonly msgSaveStarted = 'Started saving results to '; + public readonly msgSaveFailed = 'Failed to save results. '; + public readonly msgSaveSucceeded = 'Successfully saved results to '; + public readonly msgSelectProfile = 'Select connection profile'; + public readonly msgSelectProfileToRemove = 'Select profile to remove'; + public readonly confirmRemoveProfilePrompt = 'Confirm to remove this profile.'; + public readonly msgNoProfilesSaved = 'No connection profile to remove.'; + public readonly msgProfileRemoved = 'Profile removed successfully'; + public readonly msgProfileCreated = 'Profile created successfully'; + public readonly msgProfileCreatedAndConnected = 'Profile created and connected'; + public readonly msgClearedRecentConnections = 'Recent connections list cleared'; + public readonly msgSelectionIsRequired = 'Selection is required.'; + public readonly msgIsRequired = ' is required.'; + public readonly msgRetry = 'Retry'; + public readonly msgError = 'Error: '; + public readonly msgYes = 'Yes'; + public readonly msgNo = 'No'; + public readonly defaultDatabaseLabel = ''; + public readonly notConnectedLabel = 'Disconnected'; + public readonly notConnectedTooltip = 'Click to connect to a database'; + public readonly connectingLabel = 'Connecting'; + public readonly connectingTooltip = 'Connecting to: '; + public readonly connectedLabel = 'Connected.'; + public readonly connectErrorLabel = 'Connection error'; + public readonly connectErrorTooltip = 'Error connecting to: '; + public readonly connectErrorCode = 'Errorcode: '; + public readonly connectErrorMessage = 'ErrorMessage: '; + public readonly executeQueryLabel = 'Executing query '; + public readonly cancelingQueryLabel = 'Canceling query '; + public readonly updatingIntelliSenseLabel = 'Updating IntelliSense...'; + public readonly unfoundResult = 'Data was disposed when text editor was closed; to view data please reexecute query.'; + public readonly serviceCompatibleVersion = '1.0.0'; + public readonly serviceNotCompatibleError = 'Client is not compatible with the service layer'; + public readonly serviceInstallingTo = 'Installing SQL tools service to'; + public readonly serviceInitializing = 'Initializing SQL tools service for the mssql extension.'; + public readonly commandsNotAvailableWhileInstallingTheService = 'Note: mssql commands will be available after installing the service.'; + public readonly serviceInstalled = 'Sql Tools Service installed'; + public readonly serviceInstallationFailed = 'Failed to install Sql Tools Service'; + public readonly serviceLoadingFailed = 'Failed to load Sql Tools Service'; + public readonly invalidServiceFilePath = 'Invalid file path for Sql Tools Service'; + public readonly extensionNotInitializedError = 'Unable to execute the command while the extension is initializing. Please try again later.'; + public readonly untitledScheme = 'untitled'; + public readonly untitledSaveTimeThreshold = 10.0; + public readonly renamedOpenTimeThreshold = 10.0; + public readonly msgChangeLanguageMode = 'To use this command, you must set the language to \"SQL\". Confirm to change language mode.'; + public readonly timeToWaitForLanguageModeChange = 10000.0; + public readonly msgChangedDatabaseContext = 'Changed database context to \"{0}\" for document \"{1}\"'; + public readonly msgPromptRetryCreateProfile = 'Error: Unable to connect using the connection information provided. Retry profile creation?'; + public readonly retryLabel = 'Retry'; + public readonly msgConnecting = 'Connecting to server \"{0}\" on document \"{1}\".'; + public readonly msgConnectedServerInfo = 'Connected to server \"{0}\" on document \"{1}\". Server information: {2}'; + public readonly msgConnectionFailed = 'Error connecting to server \"{0}\". Details: {1}'; + public readonly msgChangingDatabase = 'Changing database context to \"{0}\" on server \"{1}\" on document \"{2}\".'; + public readonly msgChangedDatabase = 'Changed database context to \"{0}\" on server \"{1}\" on document \"{2}\".'; + public readonly msgDisconnected = 'Disconnected on document \"{0}\"'; + public readonly msgErrorReadingConfigFile = 'Error: Unable to load connection profiles from [{0}]. Check if the file is formatted correctly.'; + public readonly msgErrorOpeningConfigFile = 'Error: Unable to open connection profile settings file.'; + public readonly extConfigResultKeys = ['shortcuts', 'messagesDefaultOpen']; + public readonly extConfigResultFontFamily = 'resultsFontFamily'; + public readonly extConfigResultFontSize = 'resultsFontSize'; + public readonly titleResultsPane = 'Results: {0}'; + public readonly macOpenSslErrorMessage = `OpenSSL version >=1.0.1 is required to connect.`; + public readonly macOpenSslHelpButton = 'Help'; + public readonly macOpenSslHelpLink = 'https://github.com/Microsoft/vscode-mssql/wiki/OpenSSL-Configuration'; + public readonly serviceName = 'SQLToolsService'; + public readonly serviceInitializingOutputChannelName = 'SqlToolsService Initialization'; + public readonly gettingStartedGuideLink = 'https://aka.ms/mssql-getting-started'; + public readonly serviceCrashMessage = 'SQL Tools Service component exited unexpectedly. Please restart SQL Operations Studio.'; + public readonly serviceCrashLink = 'https://github.com/Microsoft/vscode-mssql/wiki/SqlToolsService-Known-Issues'; + public readonly gettingDefinitionMessage = 'Getting definition ...'; + public readonly definitionRequestedStatus = 'DefinitionRequested'; + public readonly definitionRequestCompletedStatus = 'DefinitionRequestCompleted'; + public readonly updatingIntelliSenseStatus = 'updatingIntelliSense'; + public readonly intelliSenseUpdatedStatus = 'intelliSenseUpdated'; + + /** + * Returns a supported .NET Core Runtime ID (RID) for the current platform. The list of Runtime IDs + * is available at https://github.com/dotnet/corefx/tree/master/pkg/Microsoft.NETCore.Platforms. + */ + public getRuntimeId(platform: string, architecture: string, distribution: LinuxDistribution): Runtime { + switch (platform) { + case 'win32': + switch (architecture) { + case 'x86': return Runtime.Windows_86; + case 'x86_64': return Runtime.Windows_64; + default: + } + + throw new Error(`Unsupported Windows architecture: ${architecture}`); + + case 'darwin': + if (architecture === 'x86_64') { + // Note: We return the El Capitan RID for Sierra + return Runtime.OSX; + } + + throw new Error(`Unsupported macOS architecture: ${architecture}`); + + case 'linux': + if (architecture === 'x86_64') { + + // First try the distribution name + let runtimeId = Constants.getRuntimeIdHelper(distribution.name, distribution.version); + + // If the distribution isn't one that we understand, but the 'ID_LIKE' field has something that we understand, use that + // + // NOTE: 'ID_LIKE' doesn't specify the version of the 'like' OS. So we will use the 'VERSION_ID' value. This will restrict + // how useful ID_LIKE will be since it requires the version numbers to match up, but it is the best we can do. + if (runtimeId === Runtime.UnknownRuntime && distribution.idLike && distribution.idLike.length > 0) { + for (let id of distribution.idLike) { + runtimeId = Constants.getRuntimeIdHelper(id, distribution.version); + if (runtimeId !== Runtime.UnknownRuntime) { + break; + } + } + } + + if (runtimeId !== Runtime.UnknownRuntime && runtimeId !== Runtime.UnknownVersion) { + return runtimeId; + } + } + + // If we got here, this is not a Linux distro or architecture that we currently support. + throw new Error(`Unsupported Linux distro: ${distribution.name}, ${distribution.version}, ${architecture}`); + default : + // If we got here, we've ended up with a platform we don't support like 'freebsd' or 'sunos'. + // Chances are, VS Code doesn't support these platforms either. + throw Error('Unsupported platform ' + platform); + } + } + + private static getRuntimeIdHelper(distributionName: string, distributionVersion: string): Runtime { + switch (distributionName) { + case 'ubuntu': + if (distributionVersion.startsWith('14')) { + // This also works for Linux Mint + return Runtime.Ubuntu_14; + } else if (distributionVersion.startsWith('16')) { + return Runtime.Ubuntu_16; + } + + break; + case 'elementary': + case 'elementary OS': + if (distributionVersion.startsWith('0.3')) { + // Elementary OS 0.3 Freya is binary compatible with Ubuntu 14.04 + return Runtime.Ubuntu_14; + } else if (distributionVersion.startsWith('0.4')) { + // Elementary OS 0.4 Loki is binary compatible with Ubuntu 16.04 + return Runtime.Ubuntu_16; + } + + break; + case 'linuxmint': + if (distributionVersion.startsWith('18')) { + // Linux Mint 18 is binary compatible with Ubuntu 16.04 + return Runtime.Ubuntu_16; + } + + break; + case 'centos': + case 'ol': + // Oracle Linux is binary compatible with CentOS + return Runtime.CentOS_7; + case 'fedora': + return Runtime.Fedora_23; + case 'opensuse': + return Runtime.OpenSUSE_13_2; + case 'sles': + return Runtime.SLES_12_2; + case 'rhel': + return Runtime.RHEL_7; + case 'debian': + return Runtime.Debian_8; + case 'galliumos': + if (distributionVersion.startsWith('2.0')) { + return Runtime.Ubuntu_16; + } + break; + default: + return Runtime.UnknownRuntime; + } + + return Runtime.UnknownVersion; + } +} \ No newline at end of file diff --git a/extensions/mssql/client/src/models/contracts.ts b/extensions/mssql/client/src/models/contracts.ts new file mode 100644 index 0000000000..a66b19b434 --- /dev/null +++ b/extensions/mssql/client/src/models/contracts.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +'use strict'; + +import {RequestType} from 'dataprotocol-client'; +import * as data from 'data'; + +// DEV-NOTE: Still finalizing what we'll need as part of this interface +/** + * Contains necessary information for serializing and saving results + * @param {string} saveFormat the format / type that the results will be saved in + * @param {string} savePath path the results will be saved to + * @param {string} results either a subset or all of the results we wish to save to savePath + * @param {boolean} appendToFile Whether we should append or overwrite the file in savePath +*/ +export class SaveResultsInfo { + + + constructor(public saveFormat: string, public savePath: string, public results: string, + public appendToFile: boolean) { + } +} + +export namespace SaveAsRequest { + export const type: RequestType = { get method(): string { return 'query/saveAs'; } }; +} + +// --------------------------------- < Read Credential Request > ------------------------------------------------- + +// Read Credential request message callback declaration +export namespace ReadCredentialRequest { + export const type: RequestType = { get method(): string { return 'credential/read'; } }; +} + +/** + * Parameters to initialize a connection to a database + */ +export class Credential { + /** + * Unique ID identifying the credential + */ + public credentialId: string; + + /** + * password + */ + public password: string; +} + +// --------------------------------- ------------------------------------------------- + +// --------------------------------- < Save Credential Request > ------------------------------------------------- + +// Save Credential request message callback declaration +export namespace SaveCredentialRequest { + export const type: RequestType = { get method(): string { return 'credential/save'; } }; +} +// --------------------------------- ------------------------------------------------- + + +// --------------------------------- < Delete Credential Request > ------------------------------------------------- + +// Delete Credential request message callback declaration +export namespace DeleteCredentialRequest { + export const type: RequestType = { get method(): string { return 'credential/delete'; } }; +} +// --------------------------------- ------------------------------------------------- + +// ------------------------------- < Resource Events > ------------------------------------ +export namespace CreateFirewallRuleRequest { + export const type: RequestType = { get method(): string { return 'resource/createFirewallRule'; } }; +} + +export namespace HandleFirewallRuleRequest { + export const type: RequestType = { get method(): string { return 'resource/handleFirewallRule'; } }; +} + +// Firewall rule interfaces +export interface CreateFirewallRuleParams { + account: data.Account; + serverName: string; + startIpAddress: string; + endIpAddress: string; + securityTokenMappings: {}; +} + +export interface CreateFirewallRuleResponse { + result: boolean; + errorMessage: string; +} + +export interface HandleFirewallRuleParams { + errorCode: number; + errorMessage: string; + connectionTypeId: string; +} + +export interface HandleFirewallRuleResponse { + result: boolean; + ipAddress: string; +} + diff --git a/extensions/mssql/client/src/mssqlMain.ts b/extensions/mssql/client/src/mssqlMain.ts new file mode 100644 index 0000000000..99a7127068 --- /dev/null +++ b/extensions/mssql/client/src/mssqlMain.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import vscode = require('vscode'); +import MainController from './controllers/mainController'; + +let controller: MainController = undefined; + +export function activate(context: vscode.ExtensionContext) { + controller = new MainController(context); + context.subscriptions.push(controller); + controller.activate(); +} + +// this method is called when your extension is deactivated +export function deactivate(): void { + if (controller) { + controller.deactivate(); + } +} + +/** + * Exposed for testing purposes + */ +export function getController(): MainController { + return controller; +} diff --git a/extensions/mssql/client/src/resourceprovider/resourceprovider.ts b/extensions/mssql/client/src/resourceprovider/resourceprovider.ts new file mode 100644 index 0000000000..5cae4173fc --- /dev/null +++ b/extensions/mssql/client/src/resourceprovider/resourceprovider.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as Contracts from '../models/contracts'; +import { SqlToolsServiceClient } from 'extensions-modules'; +import { LanguageClient } from 'dataprotocol-client'; +import * as data from 'data'; + + +/** + * Implements a credential storage for Windows, Mac (darwin), or Linux. + * + * Allows a single credential to be stored per service (that is, one username per service); + */ +export class AzureResourceProvider implements data.ResourceProvider { + + public languageClient: LanguageClient; + + constructor(private _client?: SqlToolsServiceClient, langClient?: LanguageClient) { + if (!this._client) { + this._client = SqlToolsServiceClient.instance; + } + this.languageClient = langClient; + } + + public createFirewallRule(account: data.Account, firewallruleInfo: data.FirewallRuleInfo): Thenable { + let self = this; + return new Promise((resolve, reject) => { + self._client. + sendRequest(Contracts.CreateFirewallRuleRequest.type, self.asCreateFirewallRuleParams(account, firewallruleInfo), self.languageClient) + .then(response => { + resolve(response); + }, err => reject(err)); + }); + } + + public handleFirewallRule(errorCode: number, errorMessage: string, connectionTypeId: string): Thenable { + let self = this; + return new Promise((resolve, reject) => { + let params: Contracts.HandleFirewallRuleParams = { errorCode: errorCode, errorMessage: errorMessage, connectionTypeId: connectionTypeId }; + + self._client. + sendRequest(Contracts.HandleFirewallRuleRequest.type, params, self.languageClient) + .then(response => { + resolve(response); + }, err => reject(err)); + }); + } + + private asCreateFirewallRuleParams(account: data.Account, params: data.FirewallRuleInfo): Contracts.CreateFirewallRuleParams { + return { + account: account, + serverName: params.serverName, + startIpAddress: params.startIpAddress, + endIpAddress: params.endIpAddress, + securityTokenMappings: params.securityTokenMappings + }; + } +} + diff --git a/extensions/mssql/client/src/serialize/iserialization.ts b/extensions/mssql/client/src/serialize/iserialization.ts new file mode 100644 index 0000000000..f0be34c67c --- /dev/null +++ b/extensions/mssql/client/src/serialize/iserialization.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { SaveResultsInfo } from '../models/contracts'; +import * as data from 'data'; + +/** + * Serializer for saving results into a different format + * + * @export + * @interface ISerialization + */ +export interface ISerialization { + saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Promise; +} diff --git a/extensions/mssql/client/src/serialize/serialization.ts b/extensions/mssql/client/src/serialize/serialization.ts new file mode 100644 index 0000000000..a41b062756 --- /dev/null +++ b/extensions/mssql/client/src/serialize/serialization.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as Contracts from '../models/contracts'; +import { ISerialization } from './iserialization'; +import { SqlToolsServiceClient } from 'extensions-modules'; +import * as data from 'data'; +import { LanguageClient } from 'dataprotocol-client'; + +/** + * Implements serializer for query results + */ +export class Serialization implements ISerialization { + + constructor(private _client?: SqlToolsServiceClient, private _languageClient?: LanguageClient) { + if (!this._client) { + this._client = SqlToolsServiceClient.instance; + } + } + + /** + * Saves results as a specified path + * + * @param {string} credentialId the ID uniquely identifying this credential + * @returns {Promise} Promise that resolved to the credential, or undefined if not found + */ + public saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Promise { + let self = this; + let resultsInfo: Contracts.SaveResultsInfo = new Contracts.SaveResultsInfo(saveFormat, savePath, results, appendToFile); + return new Promise( (resolve, reject) => { + self._client + .sendRequest(Contracts.SaveAsRequest.type, resultsInfo, this._languageClient) + .then(result => { + resolve(result); + }, err => reject(err)); + }); + } +} diff --git a/extensions/mssql/client/src/typings/ref.d.ts b/extensions/mssql/client/src/typings/ref.d.ts new file mode 100644 index 0000000000..ab20470402 --- /dev/null +++ b/extensions/mssql/client/src/typings/ref.d.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// +/// \ No newline at end of file diff --git a/extensions/mssql/client/tsconfig.json b/extensions/mssql/client/tsconfig.json new file mode 100644 index 0000000000..e6baa6d40d --- /dev/null +++ b/extensions/mssql/client/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "./out", + "lib": [ + "es6", "es2015.promise" + ], + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "declaration": true + }, + "exclude": [ + "node_modules" + ] +} diff --git a/extensions/mssql/npm-shrinkwrap.json b/extensions/mssql/npm-shrinkwrap.json new file mode 100644 index 0000000000..cdfaa4379d --- /dev/null +++ b/extensions/mssql/npm-shrinkwrap.json @@ -0,0 +1,335 @@ +{ + "name": "mssql", + "version": "0.1.0", + "dependencies": { + "agent-base": { + "version": "4.1.1", + "from": "agent-base@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.1.1.tgz" + }, + "applicationinsights": { + "version": "0.18.0", + "from": "applicationinsights@0.18.0", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz" + }, + "base64-js": { + "version": "0.0.8", + "from": "base64-js@0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz" + }, + "bl": { + "version": "1.2.1", + "from": "bl@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz" + }, + "bluebird": { + "version": "3.5.0", + "from": "bluebird@>=3.5.0 <4.0.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz" + }, + "buffer": { + "version": "3.6.0", + "from": "buffer@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz" + }, + "buffer-crc32": { + "version": "0.2.13", + "from": "buffer-crc32@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" + }, + "commander": { + "version": "2.8.1", + "from": "commander@>=2.8.1 <2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "dataprotocol-client": { + "version": "2.6.3", + "from": "..\\..\\dataprotocol-node\\client", + "resolved": "file:..\\..\\dataprotocol-node\\client" + }, + "dataprotocol-jsonrpc": { + "version": "2.4.0", + "from": "..\\..\\dataprotocol-node\\jsonrpc", + "resolved": "file:..\\..\\dataprotocol-node\\jsonrpc" + }, + "dataprotocol-languageserver-types": { + "version": "1.0.4", + "from": "..\\..\\dataprotocol-node\\types", + "resolved": "file:..\\..\\dataprotocol-node\\types" + }, + "debug": { + "version": "2.6.8", + "from": "debug@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + }, + "decompress": { + "version": "4.2.0", + "from": "decompress@>=4.2.0 <5.0.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz" + }, + "decompress-tar": { + "version": "4.1.1", + "from": "decompress-tar@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz" + }, + "decompress-tarbz2": { + "version": "4.1.1", + "from": "decompress-tarbz2@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "dependencies": { + "file-type": { + "version": "6.1.0", + "from": "file-type@>=6.1.0 <7.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.1.0.tgz" + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "from": "decompress-targz@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz" + }, + "decompress-unzip": { + "version": "4.0.1", + "from": "decompress-unzip@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "dependencies": { + "file-type": { + "version": "3.9.0", + "from": "file-type@>=3.8.0 <4.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz" + } + } + }, + "end-of-stream": { + "version": "1.4.0", + "from": "end-of-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz" + }, + "es6-promise": { + "version": "4.1.1", + "from": "es6-promise@>=4.0.3 <5.0.0", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz" + }, + "es6-promisify": { + "version": "5.0.0", + "from": "es6-promisify@>=5.0.0 <6.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" + }, + "extensions-modules": { + "version": "0.1.0", + "from": "..\\..\\extensions-modules", + "resolved": "file:..\\..\\extensions-modules" + }, + "fd-slicer": { + "version": "1.0.1", + "from": "fd-slicer@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz" + }, + "file-type": { + "version": "5.2.0", + "from": "file-type@>=5.2.0 <6.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz" + }, + "fs-extra": { + "version": "2.1.2", + "from": "fs-extra@>=2.1.2 <3.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz" + }, + "fs-extra-promise": { + "version": "1.0.1", + "from": "fs-extra-promise@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/fs-extra-promise/-/fs-extra-promise-1.0.1.tgz" + }, + "get-stream": { + "version": "2.3.1", + "from": "get-stream@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz" + }, + "graceful-fs": { + "version": "4.1.11", + "from": "graceful-fs@>=4.1.10 <5.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + }, + "graceful-readlink": { + "version": "1.0.1", + "from": "graceful-readlink@>=1.0.0", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + }, + "http-proxy-agent": { + "version": "2.0.0", + "from": "http-proxy-agent@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.0.0.tgz" + }, + "https-proxy-agent": { + "version": "2.1.0", + "from": "https-proxy-agent@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.1.0.tgz" + }, + "ieee754": { + "version": "1.1.8", + "from": "ieee754@>=1.1.4 <2.0.0", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@>=2.0.3 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "is-natural-number": { + "version": "4.0.1", + "from": "is-natural-number@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz" + }, + "is-stream": { + "version": "1.1.0", + "from": "is-stream@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "jsonfile": { + "version": "2.4.0", + "from": "jsonfile@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz" + }, + "make-dir": { + "version": "1.0.0", + "from": "make-dir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz" + }, + "ms": { + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + }, + "object-assign": { + "version": "4.1.1", + "from": "object-assign@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + }, + "once": { + "version": "1.4.0", + "from": "once@>=1.4.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + }, + "opener": { + "version": "1.4.3", + "from": "opener@>=1.4.3 <2.0.0", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz" + }, + "os-tmpdir": { + "version": "1.0.2", + "from": "os-tmpdir@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + }, + "pend": { + "version": "1.2.0", + "from": "pend@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" + }, + "pify": { + "version": "2.3.0", + "from": "pify@>=2.3.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + }, + "pinkie": { + "version": "2.0.4", + "from": "pinkie@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + }, + "pinkie-promise": { + "version": "2.0.1", + "from": "pinkie-promise@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "process-nextick-args@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "readable-stream": { + "version": "2.3.3", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + }, + "safe-buffer": { + "version": "5.1.1", + "from": "safe-buffer@>=5.1.1 <5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + }, + "seek-bzip": { + "version": "1.0.5", + "from": "seek-bzip@>=1.0.5 <2.0.0", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz" + }, + "string_decoder": { + "version": "1.0.3", + "from": "string_decoder@>=1.0.3 <1.1.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" + }, + "strip-dirs": { + "version": "2.0.0", + "from": "strip-dirs@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.0.0.tgz" + }, + "tar-stream": { + "version": "1.5.4", + "from": "tar-stream@>=1.5.2 <2.0.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.6 <3.0.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + }, + "tmp": { + "version": "0.0.33", + "from": "tmp@0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" + }, + "unbzip2-stream": { + "version": "1.2.5", + "from": "unbzip2-stream@>=1.0.9 <2.0.0", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + }, + "vscode-extension-telemetry": { + "version": "0.0.8", + "from": "vscode-extension-telemetry@0.0.8", + "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz" + }, + "winreg": { + "version": "1.2.3", + "from": "winreg@1.2.3", + "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz" + }, + "wrappy": { + "version": "1.0.2", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + }, + "xtend": { + "version": "4.0.1", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + }, + "yauzl": { + "version": "2.8.0", + "from": "yauzl@>=2.4.2 <3.0.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz" + } + } +} diff --git a/extensions/mssql/package.json b/extensions/mssql/package.json new file mode 100644 index 0000000000..1992730c81 --- /dev/null +++ b/extensions/mssql/package.json @@ -0,0 +1,113 @@ +{ + "name": "mssql", + "version": "0.1.0", + "publisher": "Microsoft", + "aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412", + "engines": { + "vscode": "0.10.x" + }, + "activationEvents": [ + "*" + ], + "main": "./client/out/mssqlMain", + "extensionDependencies": [ + "vscode.sql" + ], + "scripts": { + "compile": "gulp compile-extension:mssql-client", + "postinstall": "node ./node_modules/vscode/bin/install" + }, + "contributes": { + "languages": [ + { + "id": "sql", + "extensions": [ + ".sql" + ], + "aliases": [ + "SQL" + ], + "configuration": "./syntaxes/sql.configuration.json" + } + ], + "grammars": [ + { + "language": "sql", + "scopeName": "source.sql", + "path": "./syntaxes/SQL.plist" + } + ], + "outputChannels": [ + "MSSQL" + ], + "snippets": [ + { + "language": "sql", + "path": "./snippets/mssql.json" + } + ], + "configuration": { + "type": "object", + "title": "MSSQL configuration", + "properties": { + "mssql.query.displayBitAsNumber": { + "type": "boolean", + "default": true, + "description": "Should BIT columns be displayed as numbers (1 or 0)? If false, BIT columns will be displayed as 'true' or 'false'" + }, + "mssql.format.alignColumnDefinitionsInColumns": { + "type": "boolean", + "description": "Should column definitions be aligned?", + "default": false + }, + "mssql.format.datatypeCasing": { + "type": "string", + "description": "Should data types be formatted as UPPERCASE, lowercase, or none (not formatted)", + "default": "none", + "enum": [ + "none", + "uppercase", + "lowercase" + ] + }, + "mssql.format.keywordCasing": { + "type": "string", + "description": "Should keywords be formatted as UPPERCASE, lowercase, or none (not formatted)", + "default": "none", + "enum": [ + "none", + "uppercase", + "lowercase" + ] + }, + "mssql.format.placeCommasBeforeNextStatement": { + "type": "boolean", + "description": "should commas be placed at the beginning of each statement in a list e.g. ', mycolumn2' instead of at the end e.g. 'mycolumn1,'", + "default": false + }, + "mssql.format.placeSelectStatementReferencesOnNewLine": { + "type": "boolean", + "description": "Should references to objects in a select statements be split into separate lines? E.g. for 'SELECT C1, C2 FROM T1' both C1 and C2 will be on separate lines", + "default": false + }, + "mssql.logDebugInfo": { + "type": "boolean", + "default": false, + "description": "[Optional] Log debug output to the VS Code console (Help -> Toggle Developer Tools)" + }, + "ignorePlatformWarning": { + "type": "boolean", + "description": "[Optional] Do not show unsupported platform warnings", + "default": false + } + } + } + }, + "dependencies": { + "dataprotocol-client": "file:../../dataprotocol-node/client", + "extensions-modules": "file:../../extensions-modules" + }, + "devDependencies": { + "vscode": "1.0.1" + } +} diff --git a/extensions/mssql/package.nls.json b/extensions/mssql/package.nls.json new file mode 100644 index 0000000000..46fe51b221 --- /dev/null +++ b/extensions/mssql/package.nls.json @@ -0,0 +1,8 @@ +{ + "json.schemas.desc": "Associate schemas to JSON files in the current project", + "json.schemas.url.desc": "A URL to a schema or a relative path to a schema in the current directory", + "json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas.", + "json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.", + "json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.", + "json.format.enable.desc": "Enable/disable default JSON formatter (requires restart)" +} \ No newline at end of file diff --git a/extensions/mssql/snippets/mssql.json b/extensions/mssql/snippets/mssql.json new file mode 100644 index 0000000000..f6fbc5703b --- /dev/null +++ b/extensions/mssql/snippets/mssql.json @@ -0,0 +1,298 @@ +{ + "Get extension help": { + "prefix": "sqlExtensionHelp", + "body": [ + "/*", + "mssql getting started:", + "-----------------------------", + "1. Change language mode to SQL: Open a .sql file or press Ctrl+K M (Cmd+K M on Mac) and choose 'SQL'.", + "2. Connect to a database: Press F1 to show the command palette, type 'sqlcon' or 'sql' then click 'Connect'.", + "3. Use the T-SQL editor: Type T-SQL statements in the editor using T-SQL IntelliSense or type 'sql' to see a list of code snippets you can tweak & reuse.", + "4. Run T-SQL statements: Press F1 and type 'sqlex' or press Ctrl+Shift+e (Cmd+Shift+e on Mac) to execute all the T-SQL code in the editor.", + "", + "Tip #1: Put GO on a line by itself to separate T-SQL batches.", + "Tip #2: Select some T-SQL text in the editor and press `Ctrl+Shift+e` (`Cmd+Shift+e` on Mac) to execute the selection", + "*/" + ], + "description": "Get extension help" + }, + + "Create a new Database": { + "prefix": "sqlCreateDatabase", + "body": [ + "-- Create a new database called '${1:DatabaseName}'", + "-- Connect to the 'master' database to run this snippet", + "USE master", + "GO", + "-- Create the new database if it does not exist already", + "IF NOT EXISTS (", + "\tSELECT name", + "\t\tFROM sys.databases", + "\t\tWHERE name = N'${1:DatabaseName}'", + ")", + "CREATE DATABASE ${1:DatabaseName}", + "GO" + ], + "description": "Create a new Database" + }, + + "Drop a Database": { + "prefix": "sqlDropDatabase", + "body": [ + "-- Drop the database '${1:DatabaseName}'", + "-- Connect to the 'master' database to run this snippet", + "USE master", + "GO", + "-- Uncomment the ALTER DATABASE statement below to set the database to SINGLE_USER mode if the drop database command fails because the database is in use.", + "-- ALTER DATABASE ${1:DatabaseName} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;", + "-- Drop the database if it exists", + "IF EXISTS (", + " SELECT name", + " FROM sys.databases", + " WHERE name = N'${1:DatabaseName}'", + ")", + "DROP DATABASE ${1:DatabaseName}", + "GO" + ], + "description": "Drop a Database" + }, + + "Create a new Table": { + "prefix": "sqlCreateTable", + "body": [ + "-- Create a new table called '${1:TableName}' in schema '${2:SchemaName}'", + "-- Drop the table if it already exists", + "IF OBJECT_ID('${2:SchemaName}.${1:TableName}', 'U') IS NOT NULL", + "DROP TABLE ${2:SchemaName}.${1:TableName}", + "GO", + "-- Create the table in the specified schema", + "CREATE TABLE ${2:SchemaName}.${1:TableName}", + "(", + "\t${1:TableName}Id INT NOT NULL PRIMARY KEY, -- primary key column", + "\t$3Column1 [NVARCHAR](50) NOT NULL,", + "\t$4Column2 [NVARCHAR](50) NOT NULL", + "\t-- specify more columns here", + ");", + "GO" + ], + "description": "Create a new Table" + }, + + "Drop a Table": { + "prefix": "sqlDropTable", + "body": [ + "-- Drop the table '${1:TableName}' in schema '${2:SchemaName}'", + "IF EXISTS (", + "\tSELECT *", + "\t\tFROM sys.tables", + "\t\tJOIN sys.schemas", + "\t\t\tON sys.tables.schema_id = sys.schemas.schema_id", + "\tWHERE sys.schemas.name = N'${2:SchemaName}'", + "\t\tAND sys.tables.name = N'${1:TableName}'", + ")", + "\tDROP TABLE ${2:SchemaName}.${1:TableName}", + "GO" + ], + "description": "Drop a Table" + }, + + "Add a new column to a Table": { + "prefix": "sqlAddColumn", + "body": [ + "-- Add a new column '${1:NewColumnName}' to table '${2:TableName}' in schema '${3:SchemaName}'", + "ALTER TABLE ${3:SchemaName}.${2:TableName}", + "\tADD ${1:NewColumnName} /*new_column_name*/ int /*new_column_datatype*/ NULL /*new_column_nullability*/", + "GO" + ], + "description": "Add a new column to a Table" + }, + + "Drop a column from a Table": { + "prefix": "sqlDropColumn", + "body": [ + "-- Drop '${1:ColumnName}' from table '${2:TableName}' in schema '${3:SchemaName}'", + "ALTER TABLE ${3:SchemaName}.${2:TableName}", + "\tDROP COLUMN ${1:ColumnName}", + "GO" + ], + "description": "Add a new column to a Table" + }, + + "Select rows from a Table or a View": { + "prefix": "sqlSelect", + "body": [ + "-- Select rows from a Table or View '${1:TableOrViewName}' in schema '${2:SchemaName}'", + "SELECT * FROM ${2:SchemaName}.${1:TableOrViewName}", + "WHERE $3\t/* add search conditions here */", + "GO" + ], + "description": "Select rows from a Table or a View" + }, + + "Insert rows into a Table": { + "prefix": "sqlInsertRows", + "body": [ + "-- Insert rows into table '${1:TableName}'", + "INSERT INTO ${1:TableName}", + "( -- columns to insert data into", + " $2[Column1], [Column2], [Column3]", + ")", + "VALUES", + "( -- first row: values for the columns in the list above", + " $3Column1_Value, Column2_Value, Column3_Value", + "),", + "( -- second row: values for the columns in the list above", + " $4Column1_Value, Column2_Value, Column3_Value", + ")", + "-- add more rows here", + "GO" + ], + "description": "Insert rows into a Table" + }, + + "Delete rows from a Table": { + "prefix": "sqlDeleteRows", + "body": [ + "-- Delete rows from table '${1:TableName}'", + "DELETE FROM ${1:TableName}", + "WHERE $2\t/* add search conditions here */", + "GO" + ], + "description": "Delete rows from a Table" + }, + + "Update rows in a Table": { + "prefix": "sqlUpdateRows", + "body": [ + "-- Update rows in table '${1:TableName}'", + "UPDATE ${1:TableName}", + "SET", + "\t$2[Colum1] = Colum1_Value,", + "\t$3[Colum2] = Colum2_Value", + "\t-- add more columns and values here", + "WHERE $4\t/* add search conditions here */", + "GO" + ], + "description": "Update rows in a Table" + }, + + "Create a stored procedure": { + "prefix": "sqlCreateStoredProc", + "body": [ + "-- Create a new stored procedure called '${1:StoredProcedureName}' in schema '${2:SchemaName}'", + "-- Drop the stored procedure if it already exists", + "IF EXISTS (", + "SELECT *", + "\tFROM INFORMATION_SCHEMA.ROUTINES", + "WHERE SPECIFIC_SCHEMA = N'${2:SchemaName}'", + "\tAND SPECIFIC_NAME = N'${1:StoredProcedureName}'", + ")", + "DROP PROCEDURE ${2:SchemaName}.${1:StoredProcedureName}", + "GO", + "-- Create the stored procedure in the specified schema", + "CREATE PROCEDURE ${2:SchemaName}.${1:StoredProcedureName}", + "\t$3@param1 /*parameter name*/ int /*datatype_for_param1*/ = 0, /*default_value_for_param1*/", + "\t$4@param2 /*parameter name*/ int /*datatype_for_param1*/ = 0 /*default_value_for_param2*/", + "-- add more stored procedure parameters here", + "AS", + "\t-- body of the stored procedure", + "\tSELECT @param1, @param2", + "GO", + "-- example to execute the stored procedure we just created", + "EXECUTE ${2:SchemaName}.${1:StoredProcedureName} 1 /*value_for_param1*/, 2 /*value_for_param2*/", + "GO" + ], + "description": "Create a stored procedure" + }, + + "Drop a stored procedure": { + "prefix": "sqlDropStoredProc", + "body": [ + "-- Drop the stored procedure called '${1:StoredProcedureName}' in schema '${2:SchemaName}'", + "IF EXISTS (", + "SELECT *", + "\tFROM INFORMATION_SCHEMA.ROUTINES", + "WHERE SPECIFIC_SCHEMA = N'${2:SchemaName}'", + "\tAND SPECIFIC_NAME = N'${1:StoredProcedureName}'", + ")", + "DROP PROCEDURE ${2:SchemaName}.${1:StoredProcedureName}", + "GO" + ], + "description": "Drop a stored procedure" + }, + + "List tables": { + "prefix": "sqlListTablesAndViews", + "body": [ + "-- Get a list of tables and views in the current database", + "SELECT table_catalog [database], table_schema [schema], table_name name, table_type type", + "FROM information_schema.tables", + "GO" + ], + "description": "List tables and vies in the current database" + }, + + "List databases": { + "prefix": "sqlListDatabases", + "body": [ + "-- Get a list of databases", + "SELECT name FROM sys.databases", + "GO" + ], + "description": "List databases" + }, + + "List columns": { + "prefix": "sqlListColumns", + "body": [ + "-- List columns in all tables whose name is like '${1:TableName}'", + "SELECT ", + "\tTableName = tbl.table_schema + '.' + tbl.table_name, ", + "\tColumnName = col.column_name, ", + "\tColumnDataType = col.data_type", + "FROM information_schema.tables tbl", + "INNER JOIN information_schema.columns col ", + "\tON col.table_name = tbl.table_name", + "\tAND col.table_schema = tbl.table_schema", + "", + "WHERE tbl.table_type = 'base table' and tbl.table_name like '%${1:TableName}%'", + "GO" + ], + "description": "Lists all the columns and their types for tables matching a LIKE statement" + }, + + "Show space used by tables": { + "prefix": "sqlGetSpaceUsed", + "body": [ + "-- Get the space used by table ${1:TableName}", + "SELECT TABL.name AS table_name,", + "INDX.name AS index_name,", + "SUM(PART.rows) AS rows_count,", + "SUM(ALOC.total_pages) AS total_pages,", + "SUM(ALOC.used_pages) AS used_pages,", + "SUM(ALOC.data_pages) AS data_pages,", + "(SUM(ALOC.total_pages)*8/1024) AS total_space_MB,", + "(SUM(ALOC.used_pages)*8/1024) AS used_space_MB,", + "(SUM(ALOC.data_pages)*8/1024) AS data_space_MB", + "FROM sys.Tables AS TABL", + "INNER JOIN sys.Indexes AS INDX", + "ON TABL.object_id = INDX.object_id", + "INNER JOIN sys.Partitions AS PART", + "ON INDX.object_id = PART.object_id", + "AND INDX.index_id = PART.index_id", + "INNER JOIN sys.Allocation_Units AS ALOC", + "ON PART.partition_id = ALOC.container_id", + "WHERE TABL.name LIKE '%${1:TableName}%'", + "AND INDX.object_id > 255", + "AND INDX.index_id <= 1", + "GROUP BY TABL.name, ", + "INDX.object_id,", + "INDX.index_id,", + "INDX.name", + "ORDER BY Object_Name(INDX.object_id),", + "(SUM(ALOC.total_pages)*8/1024) DESC", + "GO" + ], + "description": "Get Space Used by Tables" + } +} diff --git a/extensions/mssql/syntaxes/SQL.plist b/extensions/mssql/syntaxes/SQL.plist new file mode 100644 index 0000000000..f8df289f72 --- /dev/null +++ b/extensions/mssql/syntaxes/SQL.plist @@ -0,0 +1,771 @@ + + + + + fileTypes + + sql + ddl + dml + + keyEquivalent + ^~S + name + SQL + patterns + + + match + \b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference |automatic|autopilot|availability|availability_mode|backup_priority|base64|basic|batches|batchsize|before|bigint|binary|binding|bit|block|blocksize|bmk|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|cast|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|cleanup_policy|clear|clear_port|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create|credential|cross|cryptographic|cryptographic_provider|cube|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|days|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|dec|decimal|declare|decrypt|decrypt_a|decryption|default_database|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|elements|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|exec|executable|exists|expand|expiredate|expiry_date|explicit|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|field_terminator|fieldterminator|file|filelistonly|filegroup|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hours|http|identity_value|if|ifnull|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kilobytes_per_batch|labelonly|langid|language|last|lastrow|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minutes|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|procedure_name|profile|provider|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|restart|restore|restricted_user|resume|retaindays|retention|return|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|schemabinding|scoped|scroll|scroll_locks|sddl|secexpr|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics_incremental|statistics_norecompute|statistics_only|statman|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|tran|transaction|transfer|triple_des|triple_des_3key|trustworthy|try|tsql|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|webmethod|weekday|weight|well_formed_xml|when|widechar|widechar_ansi|widenative|windows|with|within|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|zone)\b + name + keyword.other.sql + + + include + #comments + + + captures + + 1 + + name + keyword.other.create.sql + + 2 + + name + keyword.other.sql + + 5 + + name + entity.name.function.sql + + + match + (?i:^\s*(create(?:\s+or\s+replace)?)\s+(aggregate|conversion|database|domain|function|group|(unique\s+)?index|language|operator class|operator|rule|schema|sequence|table|tablespace|trigger|type|user|view)\s+)(['"`]?)(\w+)\4 + name + meta.create.sql + + + captures + + 1 + + name + keyword.other.create.sql + + 2 + + name + keyword.other.sql + + + match + (?i:^\s*(drop)\s+(aggregate|conversion|database|domain|function|group|index|language|operator class|operator|rule|schema|sequence|table|tablespace|trigger|type|user|view)) + name + meta.drop.sql + + + captures + + 1 + + name + keyword.other.create.sql + + 2 + + name + keyword.other.table.sql + + 3 + + name + entity.name.function.sql + + 4 + + name + keyword.other.cascade.sql + + + match + (?i:\s*(drop)\s+(table)\s+(\w+)(\s+cascade)?\b) + name + meta.drop.sql + + + captures + + 1 + + name + keyword.other.create.sql + + 2 + + name + keyword.other.table.sql + + + match + (?i:^\s*(alter)\s+(aggregate|conversion|database|domain|function|group|index|language|operator class|operator|rule|schema|sequence|table|tablespace|trigger|type|user|view)\s+) + name + meta.alter.sql + + + captures + + 1 + + name + storage.type.sql + + 10 + + name + constant.numeric.sql + + 11 + + name + storage.type.sql + + 12 + + name + storage.type.sql + + 13 + + name + storage.type.sql + + 14 + + name + constant.numeric.sql + + 15 + + name + storage.type.sql + + 2 + + name + storage.type.sql + + 3 + + name + constant.numeric.sql + + 4 + + name + storage.type.sql + + 5 + + name + constant.numeric.sql + + 6 + + name + storage.type.sql + + 7 + + name + constant.numeric.sql + + 8 + + name + constant.numeric.sql + + 9 + + name + storage.type.sql + + + match + (?xi) + + # normal stuff, capture 1 + \b(bigint|bigserial|bit|boolean|box|bytea|cidr|circle|date|double\sprecision|inet|int|integer|line|lseg|macaddr|money|oid|path|point|polygon|real|serial|smallint|sysdate|text)\b + + # numeric suffix, capture 2 + 3i + |\b(bit\svarying|character\s(?:varying)?|tinyint|var\schar|float|interval)\((\d+)\) + + # optional numeric suffix, capture 4 + 5i + |\b(char|number|varchar\d?)\b(?:\((\d+)\))? + + # special case, capture 6 + 7i + 8i + |\b(numeric|decimal)\b(?:\((\d+),(\d+)\))? + + # special case, captures 9, 10i, 11 + |\b(times?)\b(?:\((\d+)\))?(\swith(?:out)?\stime\szone\b)? + + # special case, captures 12, 13, 14i, 15 + |\b(timestamp)(?:(s|tz))?\b(?:\((\d+)\))?(\s(with|without)\stime\szone\b)? + + + + + match + (?i:\b((?:primary|foreign)\s+key|references|on\sdelete(\s+cascade)?|check|constraint)\b) + name + storage.modifier.sql + + + match + \b\d+\b + name + constant.numeric.sql + + + match + (?i:\b(select(\s+distinct)?|insert\s+(ignore\s+)?into|update|delete|from|set|where|group\sby|or|like|and|union(\s+all)?|having|order\sby|limit|(inner|cross)\s+join|join|straight_join|(left|right)(\s+outer)?\s+join|natural(\s+(left|right)(\s+outer)?)?\s+join)\b) + name + keyword.other.DML.sql + + + match + (?i:\b(on|((is\s+)?not\s+)?null)\b) + name + keyword.other.DDL.create.II.sql + + + match + (?i:\bvalues\b) + name + keyword.other.DML.II.sql + + + match + (?i:\b(begin(\s+work)?|start\s+transaction|commit(\s+work)?|rollback(\s+work)?)\b) + name + keyword.other.LUW.sql + + + match + (?i:\b(grant(\swith\sgrant\soption)?|revoke)\b) + name + keyword.other.authorization.sql + + + match + (?i:\bin\b) + name + keyword.other.data-integrity.sql + + + match + (?i:^\s*(comment\s+on\s+(table|column|aggregate|constraint|database|domain|function|index|operator|rule|schema|sequence|trigger|type|view))\s+.*?\s+(is)\s+) + name + keyword.other.object-comments.sql + + + match + (?i)\bAS\b + name + keyword.other.alias.sql + + + match + (?i)\b(DESC|ASC)\b + name + keyword.other.order.sql + + + match + \* + name + keyword.operator.star.sql + + + match + [!<>]?=|<>|<|> + name + keyword.operator.comparison.sql + + + match + -|\+|/ + name + keyword.operator.math.sql + + + match + \|\| + name + keyword.operator.concatenator.sql + + + comment + List of SQL99 built-in functions from http://www.oreilly.com/catalog/sqlnut/chapter/ch04.html + match + (?i)\b(CURRENT_(DATE|TIME(STAMP)?|USER)|(SESSION|SYSTEM)_USER)\b + name + support.function.scalar.sql + + + comment + List of SQL99 built-in functions from http://www.oreilly.com/catalog/sqlnut/chapter/ch04.html + match + (?i)\b(AVG|COUNT|MIN|MAX|SUM)(?=\s*\() + name + support.function.aggregate.sql + + + match + (?i)\b(CONCATENATE|CONVERT|LOWER|SUBSTRING|TRANSLATE|TRIM|UPPER)\b + name + support.function.string.sql + + + captures + + 1 + + name + constant.other.database-name.sql + + 2 + + name + constant.other.table-name.sql + + + match + (\w+?)\.(\w+) + + + include + #strings + + + include + #regexps + + + captures + + 1 + + name + punctuation.section.scope.begin.sql + + 2 + + name + punctuation.section.scope.end.sql + + + comment + Allow for special ↩ behavior + match + (\()(\)) + name + meta.block.sql + + + repository + + comments + + patterns + + + begin + (^[ \t]+)?(?=--) + beginCaptures + + 1 + + name + punctuation.whitespace.comment.leading.sql + + + end + (?!\G) + patterns + + + begin + -- + beginCaptures + + 0 + + name + punctuation.definition.comment.sql + + + end + \n + name + comment.line.double-dash.sql + + + + + begin + (^[ \t]+)?(?=#) + beginCaptures + + 1 + + name + punctuation.whitespace.comment.leading.sql + + + end + (?!\G) + patterns + + + begin + # + beginCaptures + + 0 + + name + punctuation.definition.comment.sql + + + end + \n + name + comment.line.number-sign.sql + + + + + begin + /\* + captures + + 0 + + name + punctuation.definition.comment.sql + + + end + \*/ + name + comment.block.c + + + + regexps + + patterns + + + begin + /(?=\S.*/) + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + end + / + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.regexp.sql + patterns + + + include + #string_interpolation + + + match + \\/ + name + constant.character.escape.slash.sql + + + + + begin + %r\{ + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + comment + We should probably handle nested bracket pairs!?! -- Allan + end + \} + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.regexp.modr.sql + patterns + + + include + #string_interpolation + + + + + + string_escape + + match + \\. + name + constant.character.escape.sql + + string_interpolation + + captures + + 1 + + name + punctuation.definition.string.begin.sql + + 3 + + name + punctuation.definition.string.end.sql + + + match + (#\{)([^\}]*)(\}) + name + string.interpolated.sql + + strings + + patterns + + + captures + + 1 + + name + punctuation.definition.string.begin.sql + + 2 + + name + punctuation.definition.string.end.sql + + + comment + this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines. + match + (')[^']*(') + name + string.quoted.single.sql + + + begin + ' + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + end + ' + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.quoted.single.sql + patterns + + + include + #string_escape + + + + + captures + + 1 + + name + punctuation.definition.string.begin.sql + + 2 + + name + punctuation.definition.string.end.sql + + + comment + this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines. + match + (`)[^`\\]*(`) + name + string.quoted.other.backtick.sql + + + begin + ` + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + end + ` + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.quoted.other.backtick.sql + patterns + + + include + #string_escape + + + + + captures + + 1 + + name + punctuation.definition.string.begin.sql + + 2 + + name + punctuation.definition.string.end.sql + + + comment + this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines. + match + (")[^"#]*(") + name + string.quoted.double.sql + + + begin + " + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + end + " + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.quoted.double.sql + patterns + + + include + #string_interpolation + + + + + begin + %\{ + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + end + \} + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.other.quoted.brackets.sql + patterns + + + include + #string_interpolation + + + + + + + scopeName + source.sql + uuid + C49120AC-6ECC-11D9-ACC8-000D93589AF6 + + diff --git a/extensions/mssql/syntaxes/sql.configuration.json b/extensions/mssql/syntaxes/sql.configuration.json new file mode 100644 index 0000000000..0e0dcd235c --- /dev/null +++ b/extensions/mssql/syntaxes/sql.configuration.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/extensions/node.d.ts b/extensions/node.d.ts new file mode 100644 index 0000000000..df0802add5 --- /dev/null +++ b/extensions/node.d.ts @@ -0,0 +1,2313 @@ +// Type definitions for Node.js v4.x +// Project: http://nodejs.org/ +// Definitions by: Microsoft TypeScript , DefinitelyTyped +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/// + +/************************************************ +* * +* Node.js v4.x API * +* * +************************************************/ + +interface Error { + stack?: string; +} + + +// compat for TypeScript 1.8 +// if you use with --target es3 or --target es5 and use below definitions, +// use the lib.es6.d.ts that is bundled with TypeScript 1.8. +interface MapConstructor {} +interface WeakMapConstructor {} +interface SetConstructor {} +interface WeakSetConstructor {} + +/************************************************ +* * +* GLOBAL * +* * +************************************************/ +declare var process: NodeJS.Process; +declare var global: any; + +declare var __filename: string; +declare var __dirname: string; + +declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; +declare function clearTimeout(timeoutId: NodeJS.Timer): void; +declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; +declare function clearInterval(intervalId: NodeJS.Timer): void; +declare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any; +declare function clearImmediate(immediateId: any): void; + +interface NodeRequireFunction { + (id: string): any; +} + +interface NodeRequire extends NodeRequireFunction { + resolve(id:string): string; + cache: any; + extensions: any; + main: any; +} + +declare var require: NodeRequire; + +interface NodeModule { + exports: any; + require: NodeRequireFunction; + id: string; + filename: string; + loaded: boolean; + parent: any; + children: any[]; +} + +declare var module: NodeModule; + +// Same as module.exports +declare var exports: any; +declare var SlowBuffer: { + new (str: string, encoding?: string): Buffer; + new (size: number): Buffer; + new (size: Uint8Array): Buffer; + new (array: any[]): Buffer; + prototype: Buffer; + isBuffer(obj: any): boolean; + byteLength(string: string, encoding?: string): number; + concat(list: Buffer[], totalLength?: number): Buffer; +}; + + +// Buffer class +type BufferEncoding = "ascii" | "utf8" | "utf16le" | "ucs2" | "binary" | "hex"; +interface Buffer extends NodeBuffer {} + +/** + * Raw data is stored in instances of the Buffer class. + * A Buffer is similar to an array of integers but corresponds to a raw memory allocation outside the V8 heap. A Buffer cannot be resized. + * Valid string encodings: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex' + */ +declare var Buffer: { + /** + * Allocates a new buffer containing the given {str}. + * + * @param str String to store in buffer. + * @param encoding encoding to use, optional. Default is 'utf8' + */ + new (str: string, encoding?: string): Buffer; + /** + * Allocates a new buffer of {size} octets. + * + * @param size count of octets to allocate. + */ + new (size: number): Buffer; + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + */ + new (array: Uint8Array): Buffer; + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + */ + new (array: any[]): Buffer; + /** + * Copies the passed {buffer} data onto a new {Buffer} instance. + * + * @param buffer The buffer to copy. + */ + new (buffer: Buffer): Buffer; + prototype: Buffer; + /** + * Returns true if {obj} is a Buffer + * + * @param obj object to test. + */ + isBuffer(obj: any): obj is Buffer; + /** + * Returns true if {encoding} is a valid encoding argument. + * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex' + * + * @param encoding string to test. + */ + isEncoding(encoding: string): boolean; + /** + * Gives the actual byte length of a string. encoding defaults to 'utf8'. + * This is not the same as String.prototype.length since that returns the number of characters in a string. + * + * @param string string to test. + * @param encoding encoding used to evaluate (defaults to 'utf8') + */ + byteLength(string: string, encoding?: string): number; + /** + * Returns a buffer which is the result of concatenating all the buffers in the list together. + * + * If the list has no items, or if the totalLength is 0, then it returns a zero-length buffer. + * If the list has exactly one item, then the first item of the list is returned. + * If the list has more than one item, then a new Buffer is created. + * + * @param list An array of Buffer objects to concatenate + * @param totalLength Total length of the buffers when concatenated. + * If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly. + */ + concat(list: Buffer[], totalLength?: number): Buffer; + /** + * The same as buf1.compare(buf2). + */ + compare(buf1: Buffer, buf2: Buffer): number; +}; + +/************************************************ +* * +* GLOBAL INTERFACES * +* * +************************************************/ +declare namespace NodeJS { + export interface ErrnoException extends Error { + errno?: number; + code?: string; + path?: string; + syscall?: string; + stack?: string; + } + + export interface EventEmitter { + addListener(event: string, listener: Function): this; + on(event: string, listener: Function): this; + once(event: string, listener: Function): this; + removeListener(event: string, listener: Function): this; + removeAllListeners(event?: string): this; + setMaxListeners(n: number): this; + getMaxListeners(): number; + listeners(event: string): Function[]; + emit(event: string, ...args: any[]): boolean; + listenerCount(type: string): number; + } + + export interface ReadableStream extends EventEmitter { + readable: boolean; + read(size?: number): string|Buffer; + setEncoding(encoding: string): void; + pause(): void; + resume(): void; + pipe(destination: T, options?: { end?: boolean; }): T; + unpipe(destination?: T): void; + unshift(chunk: string): void; + unshift(chunk: Buffer): void; + wrap(oldStream: ReadableStream): ReadableStream; + } + + export interface WritableStream extends EventEmitter { + writable: boolean; + write(buffer: Buffer|string, cb?: Function): boolean; + write(str: string, encoding?: string, cb?: Function): boolean; + end(): void; + end(buffer: Buffer, cb?: Function): void; + end(str: string, cb?: Function): void; + end(str: string, encoding?: string, cb?: Function): void; + } + + export interface ReadWriteStream extends ReadableStream, WritableStream {} + + export interface Events extends EventEmitter { } + + export interface Domain extends Events { + run(fn: Function): void; + add(emitter: Events): void; + remove(emitter: Events): void; + bind(cb: (err: Error, data: any) => any): any; + intercept(cb: (data: any) => any): any; + dispose(): void; + + addListener(event: string, listener: Function): this; + on(event: string, listener: Function): this; + once(event: string, listener: Function): this; + removeListener(event: string, listener: Function): this; + removeAllListeners(event?: string): this; + } + + export interface MemoryUsage { + rss: number; + heapTotal: number; + heapUsed: number; + } + + export interface Process extends EventEmitter { + stdout: WritableStream; + stderr: WritableStream; + stdin: ReadableStream; + argv: string[]; + execArgv: string[]; + execPath: string; + abort(): void; + chdir(directory: string): void; + cwd(): string; + env: any; + exit(code?: number): void; + getgid(): number; + setgid(id: number): void; + setgid(id: string): void; + getuid(): number; + setuid(id: number): void; + setuid(id: string): void; + version: string; + versions: { + http_parser: string; + node: string; + v8: string; + ares: string; + uv: string; + zlib: string; + openssl: string; + }; + config: { + target_defaults: { + cflags: any[]; + default_configuration: string; + defines: string[]; + include_dirs: string[]; + libraries: string[]; + }; + variables: { + clang: number; + host_arch: string; + node_install_npm: boolean; + node_install_waf: boolean; + node_prefix: string; + node_shared_openssl: boolean; + node_shared_v8: boolean; + node_shared_zlib: boolean; + node_use_dtrace: boolean; + node_use_etw: boolean; + node_use_openssl: boolean; + target_arch: string; + v8_no_strict_aliasing: number; + v8_use_snapshot: boolean; + visibility: string; + }; + }; + kill(pid:number, signal?: string|number): void; + pid: number; + title: string; + arch: string; + platform: string; + memoryUsage(): MemoryUsage; + nextTick(callback: Function): void; + umask(mask?: number): number; + uptime(): number; + hrtime(time?:number[]): number[]; + domain: Domain; + + // Worker + send?(message: any, sendHandle?: any): void; + disconnect(): void; + connected: boolean; + } + + export interface Global { + Array: typeof Array; + ArrayBuffer: typeof ArrayBuffer; + Boolean: typeof Boolean; + Buffer: typeof Buffer; + DataView: typeof DataView; + Date: typeof Date; + Error: typeof Error; + EvalError: typeof EvalError; + Float32Array: typeof Float32Array; + Float64Array: typeof Float64Array; + Function: typeof Function; + GLOBAL: Global; + Infinity: typeof Infinity; + Int16Array: typeof Int16Array; + Int32Array: typeof Int32Array; + Int8Array: typeof Int8Array; + // Intl: typeof Intl; + JSON: typeof JSON; + Map: MapConstructor; + Math: typeof Math; + NaN: typeof NaN; + Number: typeof Number; + Object: typeof Object; + Promise: Function; + RangeError: typeof RangeError; + ReferenceError: typeof ReferenceError; + RegExp: typeof RegExp; + Set: SetConstructor; + String: typeof String; + Symbol: Function; + SyntaxError: typeof SyntaxError; + TypeError: typeof TypeError; + URIError: typeof URIError; + Uint16Array: typeof Uint16Array; + Uint32Array: typeof Uint32Array; + Uint8Array: typeof Uint8Array; + Uint8ClampedArray: Function; + WeakMap: WeakMapConstructor; + WeakSet: WeakSetConstructor; + clearImmediate: (immediateId: any) => void; + clearInterval: (intervalId: NodeJS.Timer) => void; + clearTimeout: (timeoutId: NodeJS.Timer) => void; + console: typeof console; + decodeURI: typeof decodeURI; + decodeURIComponent: typeof decodeURIComponent; + encodeURI: typeof encodeURI; + encodeURIComponent: typeof encodeURIComponent; + escape: (str: string) => string; + eval: typeof eval; + global: Global; + isFinite: typeof isFinite; + isNaN: typeof isNaN; + parseFloat: typeof parseFloat; + parseInt: typeof parseInt; + process: Process; + root: Global; + setImmediate: (callback: (...args: any[]) => void, ...args: any[]) => any; + setInterval: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; + setTimeout: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; + undefined: typeof undefined; + unescape: (str: string) => string; + gc: () => void; + v8debug?: any; + } + + export interface Timer { + ref() : void; + unref() : void; + } +} + +/** + * @deprecated + */ +interface NodeBuffer { + [index: number]: number; + write(string: string, offset?: number, length?: number, encoding?: string): number; + toString(encoding?: string, start?: number, end?: number): string; + toJSON(): any; + length: number; + equals(otherBuffer: Buffer): boolean; + compare(otherBuffer: Buffer): number; + copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; + slice(start?: number, end?: number): Buffer; + writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number; + readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number; + readIntLE(offset: number, byteLength: number, noAssert?: boolean): number; + readIntBE(offset: number, byteLength: number, noAssert?: boolean): number; + readUInt8(offset: number, noAssert?: boolean): number; + readUInt16LE(offset: number, noAssert?: boolean): number; + readUInt16BE(offset: number, noAssert?: boolean): number; + readUInt32LE(offset: number, noAssert?: boolean): number; + readUInt32BE(offset: number, noAssert?: boolean): number; + readInt8(offset: number, noAssert?: boolean): number; + readInt16LE(offset: number, noAssert?: boolean): number; + readInt16BE(offset: number, noAssert?: boolean): number; + readInt32LE(offset: number, noAssert?: boolean): number; + readInt32BE(offset: number, noAssert?: boolean): number; + readFloatLE(offset: number, noAssert?: boolean): number; + readFloatBE(offset: number, noAssert?: boolean): number; + readDoubleLE(offset: number, noAssert?: boolean): number; + readDoubleBE(offset: number, noAssert?: boolean): number; + writeUInt8(value: number, offset: number, noAssert?: boolean): number; + writeUInt16LE(value: number, offset: number, noAssert?: boolean): number; + writeUInt16BE(value: number, offset: number, noAssert?: boolean): number; + writeUInt32LE(value: number, offset: number, noAssert?: boolean): number; + writeUInt32BE(value: number, offset: number, noAssert?: boolean): number; + writeInt8(value: number, offset: number, noAssert?: boolean): number; + writeInt16LE(value: number, offset: number, noAssert?: boolean): number; + writeInt16BE(value: number, offset: number, noAssert?: boolean): number; + writeInt32LE(value: number, offset: number, noAssert?: boolean): number; + writeInt32BE(value: number, offset: number, noAssert?: boolean): number; + writeFloatLE(value: number, offset: number, noAssert?: boolean): number; + writeFloatBE(value: number, offset: number, noAssert?: boolean): number; + writeDoubleLE(value: number, offset: number, noAssert?: boolean): number; + writeDoubleBE(value: number, offset: number, noAssert?: boolean): number; + fill(value: any, offset?: number, end?: number): Buffer; + indexOf(value: string | number | Buffer, byteOffset?: number): number; +} + +/************************************************ +* * +* MODULES * +* * +************************************************/ +declare module "buffer" { + export var INSPECT_MAX_BYTES: number; + var BuffType: typeof Buffer; + var SlowBuffType: typeof SlowBuffer; + export { BuffType as Buffer, SlowBuffType as SlowBuffer }; +} + +declare module "querystring" { + export interface StringifyOptions { + encodeURIComponent?: Function; + } + + export interface ParseOptions { + maxKeys?: number; + decodeURIComponent?: Function; + } + + export function stringify(obj: T, sep?: string, eq?: string, options?: StringifyOptions): string; + export function parse(str: string, sep?: string, eq?: string, options?: ParseOptions): any; + export function parse(str: string, sep?: string, eq?: string, options?: ParseOptions): T; + export function escape(str: string): string; + export function unescape(str: string): string; +} + +declare module "events" { + export class EventEmitter implements NodeJS.EventEmitter { + static EventEmitter: EventEmitter; + static listenerCount(emitter: EventEmitter, event: string): number; // deprecated + static defaultMaxListeners: number; + + addListener(event: string, listener: Function): this; + on(event: string, listener: Function): this; + once(event: string, listener: Function): this; + removeListener(event: string, listener: Function): this; + removeAllListeners(event?: string): this; + setMaxListeners(n: number): this; + getMaxListeners(): number; + listeners(event: string): Function[]; + emit(event: string, ...args: any[]): boolean; + listenerCount(type: string): number; + } +} + +declare module "http" { + import * as events from "events"; + import * as net from "net"; + import * as stream from "stream"; + + export interface RequestOptions { + protocol?: string; + host?: string; + hostname?: string; + family?: number; + port?: number; + localAddress?: string; + socketPath?: string; + method?: string; + path?: string; + headers?: { [key: string]: any }; + auth?: string; + agent?: Agent|boolean; + } + + export interface Server extends events.EventEmitter, net.Server { + setTimeout(msecs: number, callback: Function): void; + maxHeadersCount: number; + timeout: number; + } + /** + * @deprecated Use IncomingMessage + */ + export interface ServerRequest extends IncomingMessage { + connection: net.Socket; + } + export interface ServerResponse extends events.EventEmitter, stream.Writable { + // Extended base methods + write(buffer: Buffer): boolean; + write(buffer: Buffer, cb?: Function): boolean; + write(str: string, cb?: Function): boolean; + write(str: string, encoding?: string, cb?: Function): boolean; + write(str: string, encoding?: string, fd?: string): boolean; + + writeContinue(): void; + writeHead(statusCode: number, reasonPhrase?: string, headers?: any): void; + writeHead(statusCode: number, headers?: any): void; + statusCode: number; + statusMessage: string; + headersSent: boolean; + setHeader(name: string, value: string | string[]): void; + sendDate: boolean; + getHeader(name: string): string; + removeHeader(name: string): void; + write(chunk: any, encoding?: string): any; + addTrailers(headers: any): void; + + // Extended base methods + end(): void; + end(buffer: Buffer, cb?: Function): void; + end(str: string, cb?: Function): void; + end(str: string, encoding?: string, cb?: Function): void; + end(data?: any, encoding?: string): void; + } + export interface ClientRequest extends events.EventEmitter, stream.Writable { + // Extended base methods + write(buffer: Buffer): boolean; + write(buffer: Buffer, cb?: Function): boolean; + write(str: string, cb?: Function): boolean; + write(str: string, encoding?: string, cb?: Function): boolean; + write(str: string, encoding?: string, fd?: string): boolean; + + write(chunk: any, encoding?: string): void; + abort(): void; + setTimeout(timeout: number, callback?: Function): void; + setNoDelay(noDelay?: boolean): void; + setSocketKeepAlive(enable?: boolean, initialDelay?: number): void; + + setHeader(name: string, value: string | string[]): void; + getHeader(name: string): string; + removeHeader(name: string): void; + addTrailers(headers: any): void; + + // Extended base methods + end(): void; + end(buffer: Buffer, cb?: Function): void; + end(str: string, cb?: Function): void; + end(str: string, encoding?: string, cb?: Function): void; + end(data?: any, encoding?: string): void; + } + export interface IncomingMessage extends events.EventEmitter, stream.Readable { + httpVersion: string; + headers: any; + rawHeaders: string[]; + trailers: any; + rawTrailers: any; + setTimeout(msecs: number, callback: Function): NodeJS.Timer; + /** + * Only valid for request obtained from http.Server. + */ + method?: string; + /** + * Only valid for request obtained from http.Server. + */ + url?: string; + /** + * Only valid for response obtained from http.ClientRequest. + */ + statusCode?: number; + /** + * Only valid for response obtained from http.ClientRequest. + */ + statusMessage?: string; + socket: net.Socket; + } + /** + * @deprecated Use IncomingMessage + */ + export interface ClientResponse extends IncomingMessage { } + + export interface AgentOptions { + /** + * Keep sockets around in a pool to be used by other requests in the future. Default = false + */ + keepAlive?: boolean; + /** + * When using HTTP KeepAlive, how often to send TCP KeepAlive packets over sockets being kept alive. Default = 1000. + * Only relevant if keepAlive is set to true. + */ + keepAliveMsecs?: number; + /** + * Maximum number of sockets to allow per host. Default for Node 0.10 is 5, default for Node 0.12 is Infinity + */ + maxSockets?: number; + /** + * Maximum number of sockets to leave open in a free state. Only relevant if keepAlive is set to true. Default = 256. + */ + maxFreeSockets?: number; + } + + export class Agent { + maxSockets: number; + sockets: any; + requests: any; + + constructor(opts?: AgentOptions); + + /** + * Destroy any sockets that are currently in use by the agent. + * It is usually not necessary to do this. However, if you are using an agent with KeepAlive enabled, + * then it is best to explicitly shut down the agent when you know that it will no longer be used. Otherwise, + * sockets may hang open for quite a long time before the server terminates them. + */ + destroy(): void; + } + + export var METHODS: string[]; + + export var STATUS_CODES: { + [errorCode: number]: string; + [errorCode: string]: string; + }; + export function createServer(requestListener?: (request: IncomingMessage, response: ServerResponse) =>void ): Server; + export function createClient(port?: number, host?: string): any; + export function request(options: RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest; + export function get(options: any, callback?: (res: IncomingMessage) => void): ClientRequest; + export var globalAgent: Agent; +} + +declare module "cluster" { + import * as child from "child_process"; + import * as events from "events"; + + export interface ClusterSettings { + exec?: string; + args?: string[]; + silent?: boolean; + } + + export interface Address { + address: string; + port: number; + addressType: string; + } + + export class Worker extends events.EventEmitter { + id: string; + process: child.ChildProcess; + suicide: boolean; + send(message: any, sendHandle?: any): void; + kill(signal?: string): void; + destroy(signal?: string): void; + disconnect(): void; + } + + export var settings: ClusterSettings; + export var isMaster: boolean; + export var isWorker: boolean; + export function setupMaster(settings?: ClusterSettings): void; + export function fork(env?: any): Worker; + export function disconnect(callback?: Function): void; + export var worker: Worker; + export var workers: Worker[]; + + // Event emitter + export function addListener(event: string, listener: Function): void; + export function on(event: "disconnect", listener: (worker: Worker) => void): void; + export function on(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): void; + export function on(event: "fork", listener: (worker: Worker) => void): void; + export function on(event: "listening", listener: (worker: Worker, address: any) => void): void; + export function on(event: "message", listener: (worker: Worker, message: any) => void): void; + export function on(event: "online", listener: (worker: Worker) => void): void; + export function on(event: "setup", listener: (settings: any) => void): void; + export function on(event: string, listener: Function): any; + export function once(event: string, listener: Function): void; + export function removeListener(event: string, listener: Function): void; + export function removeAllListeners(event?: string): void; + export function setMaxListeners(n: number): void; + export function listeners(event: string): Function[]; + export function emit(event: string, ...args: any[]): boolean; +} + +declare module "zlib" { + import * as stream from "stream"; + export interface ZlibOptions { chunkSize?: number; windowBits?: number; level?: number; memLevel?: number; strategy?: number; dictionary?: any; } + + export interface Gzip extends stream.Transform { } + export interface Gunzip extends stream.Transform { } + export interface Deflate extends stream.Transform { } + export interface Inflate extends stream.Transform { } + export interface DeflateRaw extends stream.Transform { } + export interface InflateRaw extends stream.Transform { } + export interface Unzip extends stream.Transform { } + + export function createGzip(options?: ZlibOptions): Gzip; + export function createGunzip(options?: ZlibOptions): Gunzip; + export function createDeflate(options?: ZlibOptions): Deflate; + export function createInflate(options?: ZlibOptions): Inflate; + export function createDeflateRaw(options?: ZlibOptions): DeflateRaw; + export function createInflateRaw(options?: ZlibOptions): InflateRaw; + export function createUnzip(options?: ZlibOptions): Unzip; + + export function deflate(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function deflateSync(buf: Buffer, options?: ZlibOptions): any; + export function deflateRaw(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function deflateRawSync(buf: Buffer, options?: ZlibOptions): any; + export function gzip(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function gzipSync(buf: Buffer, options?: ZlibOptions): any; + export function gunzip(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function gunzipSync(buf: Buffer, options?: ZlibOptions): any; + export function inflate(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function inflateSync(buf: Buffer, options?: ZlibOptions): any; + export function inflateRaw(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function inflateRawSync(buf: Buffer, options?: ZlibOptions): any; + export function unzip(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function unzipSync(buf: Buffer, options?: ZlibOptions): any; + + // Constants + export var Z_NO_FLUSH: number; + export var Z_PARTIAL_FLUSH: number; + export var Z_SYNC_FLUSH: number; + export var Z_FULL_FLUSH: number; + export var Z_FINISH: number; + export var Z_BLOCK: number; + export var Z_TREES: number; + export var Z_OK: number; + export var Z_STREAM_END: number; + export var Z_NEED_DICT: number; + export var Z_ERRNO: number; + export var Z_STREAM_ERROR: number; + export var Z_DATA_ERROR: number; + export var Z_MEM_ERROR: number; + export var Z_BUF_ERROR: number; + export var Z_VERSION_ERROR: number; + export var Z_NO_COMPRESSION: number; + export var Z_BEST_SPEED: number; + export var Z_BEST_COMPRESSION: number; + export var Z_DEFAULT_COMPRESSION: number; + export var Z_FILTERED: number; + export var Z_HUFFMAN_ONLY: number; + export var Z_RLE: number; + export var Z_FIXED: number; + export var Z_DEFAULT_STRATEGY: number; + export var Z_BINARY: number; + export var Z_TEXT: number; + export var Z_ASCII: number; + export var Z_UNKNOWN: number; + export var Z_DEFLATED: number; + export var Z_NULL: number; +} + +declare module "os" { + export interface CpuInfo { + model: string; + speed: number; + times: { + user: number; + nice: number; + sys: number; + idle: number; + irq: number; + }; + } + + export interface NetworkInterfaceInfo { + address: string; + netmask: string; + family: string; + mac: string; + internal: boolean; + } + + export function tmpdir(): string; + export function homedir(): string; + export function endianness(): string; + export function hostname(): string; + export function type(): string; + export function platform(): string; + export function arch(): string; + export function release(): string; + export function uptime(): number; + export function loadavg(): number[]; + export function totalmem(): number; + export function freemem(): number; + export function cpus(): CpuInfo[]; + export function networkInterfaces(): {[index: string]: NetworkInterfaceInfo[]}; + export var EOL: string; +} + +declare module "https" { + import * as tls from "tls"; + import * as events from "events"; + import * as http from "http"; + + export interface ServerOptions { + pfx?: any; + key?: any; + passphrase?: string; + cert?: any; + ca?: any; + crl?: any; + ciphers?: string; + honorCipherOrder?: boolean; + requestCert?: boolean; + rejectUnauthorized?: boolean; + NPNProtocols?: any; + SNICallback?: (servername: string) => any; + } + + export interface RequestOptions extends http.RequestOptions{ + pfx?: any; + key?: any; + passphrase?: string; + cert?: any; + ca?: any; + ciphers?: string; + rejectUnauthorized?: boolean; + secureProtocol?: string; + } + + export interface Agent { + maxSockets: number; + sockets: any; + requests: any; + } + export var Agent: { + new (options?: RequestOptions): Agent; + }; + export interface Server extends tls.Server { } + export function createServer(options: ServerOptions, requestListener?: Function): Server; + export function request(options: RequestOptions, callback?: (res: http.IncomingMessage) =>void ): http.ClientRequest; + export function get(options: RequestOptions, callback?: (res: http.IncomingMessage) =>void ): http.ClientRequest; + export var globalAgent: Agent; +} + +declare module "punycode" { + export function decode(string: string): string; + export function encode(string: string): string; + export function toUnicode(domain: string): string; + export function toASCII(domain: string): string; + export var ucs2: ucs2; + interface ucs2 { + decode(string: string): number[]; + encode(codePoints: number[]): string; + } + export var version: any; +} + +declare module "repl" { + import * as stream from "stream"; + import * as events from "events"; + + export interface ReplOptions { + prompt?: string; + input?: NodeJS.ReadableStream; + output?: NodeJS.WritableStream; + terminal?: boolean; + eval?: Function; + useColors?: boolean; + useGlobal?: boolean; + ignoreUndefined?: boolean; + writer?: Function; + } + export function start(options: ReplOptions): events.EventEmitter; +} + +declare module "readline" { + import * as events from "events"; + import * as stream from "stream"; + + export interface Key { + sequence?: string; + name?: string; + ctrl?: boolean; + meta?: boolean; + shift?: boolean; + } + + export interface ReadLine extends events.EventEmitter { + setPrompt(prompt: string): void; + prompt(preserveCursor?: boolean): void; + question(query: string, callback: (answer: string) => void): void; + pause(): ReadLine; + resume(): ReadLine; + close(): void; + write(data: string|Buffer, key?: Key): void; + } + + export interface Completer { + (line: string): CompleterResult; + (line: string, callback: (err: any, result: CompleterResult) => void): any; + } + + export interface CompleterResult { + completions: string[]; + line: string; + } + + export interface ReadLineOptions { + input: NodeJS.ReadableStream; + output?: NodeJS.WritableStream; + completer?: Completer; + terminal?: boolean; + historySize?: number; + } + + export function createInterface(input: NodeJS.ReadableStream, output?: NodeJS.WritableStream, completer?: Completer, terminal?: boolean): ReadLine; + export function createInterface(options: ReadLineOptions): ReadLine; + + export function cursorTo(stream: NodeJS.WritableStream, x: number, y: number): void; + export function moveCursor(stream: NodeJS.WritableStream, dx: number|string, dy: number|string): void; + export function clearLine(stream: NodeJS.WritableStream, dir: number): void; + export function clearScreenDown(stream: NodeJS.WritableStream): void; +} + +declare module "vm" { + export interface Context { } + export interface Script { + runInThisContext(): void; + runInNewContext(sandbox?: Context): void; + } + export function runInThisContext(code: string, filename?: string): void; + export function runInNewContext(code: string, sandbox?: Context, filename?: string): void; + export function runInContext(code: string, context: Context, filename?: string): void; + export function createContext(initSandbox?: Context): Context; + export function createScript(code: string, filename?: string): Script; +} + +declare module "child_process" { + import * as events from "events"; + import * as stream from "stream"; + + export interface ChildProcess extends events.EventEmitter { + stdin: stream.Writable; + stdout: stream.Readable; + stderr: stream.Readable; + stdio: [stream.Writable, stream.Readable, stream.Readable]; + pid: number; + connected: boolean; + kill(signal?: string): void; + send(message: any, sendHandle?: any): void; + disconnect(): void; + unref(): void; + } + + export interface SpawnOptions { + cwd?: string; + env?: any; + stdio?: any; + detached?: boolean; + uid?: number; + gid?: number; + shell?: boolean | string; + } + export function spawn(command: string, args?: string[], options?: SpawnOptions): ChildProcess; + + export interface ExecOptions { + cwd?: string; + env?: any; + shell?: string; + timeout?: number; + maxBuffer?: number; + killSignal?: string; + uid?: number; + gid?: number; + } + export interface ExecOptionsWithStringEncoding extends ExecOptions { + encoding: BufferEncoding; + } + export interface ExecOptionsWithBufferEncoding extends ExecOptions { + encoding: string; // specify `null`. + } + export function exec(command: string, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; + export function exec(command: string, options: ExecOptions, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; + + export interface ExecFileOptions { + cwd?: string; + env?: any; + timeout?: number; + maxBuffer?: number; + killSignal?: string; + uid?: number; + gid?: number; + } + export interface ExecFileOptionsWithStringEncoding extends ExecFileOptions { + encoding: BufferEncoding; + } + export interface ExecFileOptionsWithBufferEncoding extends ExecFileOptions { + encoding: string; // specify `null`. + } + export function execFile(file: string, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; + export function execFile(file: string, options?: ExecFileOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; + // usage. child_process.execFile("file.sh", {encoding: null as string}, (err, stdout, stderr) => {}); + export function execFile(file: string, options?: ExecFileOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; + export function execFile(file: string, options?: ExecFileOptions, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; + export function execFile(file: string, args?: string[], callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; + export function execFile(file: string, args?: string[], options?: ExecFileOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; + // usage. child_process.execFile("file.sh", ["foo"], {encoding: null as string}, (err, stdout, stderr) => {}); + export function execFile(file: string, args?: string[], options?: ExecFileOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; + export function execFile(file: string, args?: string[], options?: ExecFileOptions, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; + + export interface ForkOptions { + cwd?: string; + env?: any; + execPath?: string; + execArgv?: string[]; + silent?: boolean; + uid?: number; + gid?: number; + } + export function fork(modulePath: string, args?: string[], options?: ForkOptions): ChildProcess; + + export interface SpawnSyncOptions { + cwd?: string; + input?: string | Buffer; + stdio?: any; + env?: any; + uid?: number; + gid?: number; + timeout?: number; + killSignal?: string; + maxBuffer?: number; + encoding?: string; + shell?: boolean | string; + } + export interface SpawnSyncOptionsWithStringEncoding extends SpawnSyncOptions { + encoding: BufferEncoding; + } + export interface SpawnSyncOptionsWithBufferEncoding extends SpawnSyncOptions { + encoding: string; // specify `null`. + } + export interface SpawnSyncReturns { + pid: number; + output: string[]; + stdout: T; + stderr: T; + status: number; + signal: string; + error: Error; + } + export function spawnSync(command: string): SpawnSyncReturns; + export function spawnSync(command: string, options?: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns; + export function spawnSync(command: string, options?: SpawnSyncOptionsWithBufferEncoding): SpawnSyncReturns; + export function spawnSync(command: string, options?: SpawnSyncOptions): SpawnSyncReturns; + export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns; + export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptionsWithBufferEncoding): SpawnSyncReturns; + export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptions): SpawnSyncReturns; + + export interface ExecSyncOptions { + cwd?: string; + input?: string | Buffer; + stdio?: any; + env?: any; + shell?: string; + uid?: number; + gid?: number; + timeout?: number; + killSignal?: string; + maxBuffer?: number; + encoding?: string; + } + export interface ExecSyncOptionsWithStringEncoding extends ExecSyncOptions { + encoding: BufferEncoding; + } + export interface ExecSyncOptionsWithBufferEncoding extends ExecSyncOptions { + encoding: string; // specify `null`. + } + export function execSync(command: string): Buffer; + export function execSync(command: string, options?: ExecSyncOptionsWithStringEncoding): string; + export function execSync(command: string, options?: ExecSyncOptionsWithBufferEncoding): Buffer; + export function execSync(command: string, options?: ExecSyncOptions): Buffer; + + export interface ExecFileSyncOptions { + cwd?: string; + input?: string | Buffer; + stdio?: any; + env?: any; + uid?: number; + gid?: number; + timeout?: number; + killSignal?: string; + maxBuffer?: number; + encoding?: string; + } + export interface ExecFileSyncOptionsWithStringEncoding extends ExecFileSyncOptions { + encoding: BufferEncoding; + } + export interface ExecFileSyncOptionsWithBufferEncoding extends ExecFileSyncOptions { + encoding: string; // specify `null`. + } + export function execFileSync(command: string): Buffer; + export function execFileSync(command: string, options?: ExecFileSyncOptionsWithStringEncoding): string; + export function execFileSync(command: string, options?: ExecFileSyncOptionsWithBufferEncoding): Buffer; + export function execFileSync(command: string, options?: ExecFileSyncOptions): Buffer; + export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptionsWithStringEncoding): string; + export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptionsWithBufferEncoding): Buffer; + export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptions): Buffer; +} + +declare module "url" { + export interface Url { + href?: string; + protocol?: string; + auth?: string; + hostname?: string; + port?: string; + host?: string; + pathname?: string; + search?: string; + query?: any; // string | Object + slashes?: boolean; + hash?: string; + path?: string; + } + + export function parse(urlStr: string, parseQueryString?: boolean , slashesDenoteHost?: boolean ): Url; + export function format(url: Url): string; + export function resolve(from: string, to: string): string; +} + +declare module "dns" { + export function lookup(domain: string, family: number, callback: (err: Error, address: string, family: number) =>void ): string; + export function lookup(domain: string, callback: (err: Error, address: string, family: number) =>void ): string; + export function resolve(domain: string, rrtype: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolve(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolve4(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolve6(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolveMx(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolveTxt(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolveSrv(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolveNs(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolveCname(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function reverse(ip: string, callback: (err: Error, domains: string[]) =>void ): string[]; +} + +declare module "net" { + import * as stream from "stream"; + + export interface Socket extends stream.Duplex { + // Extended base methods + write(buffer: Buffer): boolean; + write(buffer: Buffer, cb?: Function): boolean; + write(str: string, cb?: Function): boolean; + write(str: string, encoding?: string, cb?: Function): boolean; + write(str: string, encoding?: string, fd?: string): boolean; + + connect(port: number, host?: string, connectionListener?: Function): void; + connect(path: string, connectionListener?: Function): void; + bufferSize: number; + setEncoding(encoding?: string): void; + write(data: any, encoding?: string, callback?: Function): void; + destroy(): void; + pause(): void; + resume(): void; + setTimeout(timeout: number, callback?: Function): void; + setNoDelay(noDelay?: boolean): void; + setKeepAlive(enable?: boolean, initialDelay?: number): void; + address(): { port: number; family: string; address: string; }; + unref(): void; + ref(): void; + + remoteAddress: string; + remoteFamily: string; + remotePort: number; + localAddress: string; + localPort: number; + bytesRead: number; + bytesWritten: number; + + // Extended base methods + end(): void; + end(buffer: Buffer, cb?: Function): void; + end(str: string, cb?: Function): void; + end(str: string, encoding?: string, cb?: Function): void; + end(data?: any, encoding?: string): void; + } + + export var Socket: { + new (options?: { fd?: string; type?: string; allowHalfOpen?: boolean; }): Socket; + }; + + export interface ListenOptions { + port?: number; + host?: string; + backlog?: number; + path?: string; + exclusive?: boolean; + } + + export interface Server extends Socket { + listen(port: number, hostname?: string, backlog?: number, listeningListener?: Function): Server; + listen(port: number, hostname?: string, listeningListener?: Function): Server; + listen(port: number, backlog?: number, listeningListener?: Function): Server; + listen(port: number, listeningListener?: Function): Server; + listen(path: string, backlog?: number, listeningListener?: Function): Server; + listen(path: string, listeningListener?: Function): Server; + listen(handle: any, backlog?: number, listeningListener?: Function): Server; + listen(handle: any, listeningListener?: Function): Server; + listen(options: ListenOptions, listeningListener?: Function): Server; + close(callback?: Function): Server; + address(): { port: number; family: string; address: string; }; + getConnections(cb: (error: Error, count: number) => void): void; + ref(): Server; + unref(): Server; + maxConnections: number; + connections: number; + } + export function createServer(connectionListener?: (socket: Socket) =>void ): Server; + export function createServer(options?: { allowHalfOpen?: boolean; }, connectionListener?: (socket: Socket) =>void ): Server; + export function connect(options: { port: number, host?: string, localAddress? : string, localPort? : string, family? : number, allowHalfOpen?: boolean; }, connectionListener?: Function): Socket; + export function connect(port: number, host?: string, connectionListener?: Function): Socket; + export function connect(path: string, connectionListener?: Function): Socket; + export function createConnection(options: { port: number, host?: string, localAddress? : string, localPort? : string, family? : number, allowHalfOpen?: boolean; }, connectionListener?: Function): Socket; + export function createConnection(port: number, host?: string, connectionListener?: Function): Socket; + export function createConnection(path: string, connectionListener?: Function): Socket; + export function isIP(input: string): number; + export function isIPv4(input: string): boolean; + export function isIPv6(input: string): boolean; +} + +declare module "dgram" { + import * as events from "events"; + + interface RemoteInfo { + address: string; + port: number; + size: number; + } + + interface AddressInfo { + address: string; + family: string; + port: number; + } + + export function createSocket(type: string, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket; + + interface Socket extends events.EventEmitter { + send(buf: Buffer, offset: number, length: number, port: number, address: string, callback?: (error: Error, bytes: number) => void): void; + bind(port: number, address?: string, callback?: () => void): void; + close(): void; + address(): AddressInfo; + setBroadcast(flag: boolean): void; + setMulticastTTL(ttl: number): void; + setMulticastLoopback(flag: boolean): void; + addMembership(multicastAddress: string, multicastInterface?: string): void; + dropMembership(multicastAddress: string, multicastInterface?: string): void; + } +} + +declare module "fs" { + import * as stream from "stream"; + import * as events from "events"; + + interface Stats { + isFile(): boolean; + isDirectory(): boolean; + isBlockDevice(): boolean; + isCharacterDevice(): boolean; + isSymbolicLink(): boolean; + isFIFO(): boolean; + isSocket(): boolean; + dev: number; + ino: number; + mode: number; + nlink: number; + uid: number; + gid: number; + rdev: number; + size: number; + blksize: number; + blocks: number; + atime: Date; + mtime: Date; + ctime: Date; + birthtime: Date; + } + + interface FSWatcher extends events.EventEmitter { + close(): void; + } + + export interface ReadStream extends stream.Readable { + close(): void; + } + export interface WriteStream extends stream.Writable { + close(): void; + bytesWritten: number; + } + + /** + * Asynchronous rename. + * @param oldPath + * @param newPath + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function rename(oldPath: string, newPath: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + /** + * Synchronous rename + * @param oldPath + * @param newPath + */ + export function renameSync(oldPath: string, newPath: string): void; + export function truncate(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function truncate(path: string, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function truncateSync(path: string, len?: number): void; + export function ftruncate(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function ftruncate(fd: number, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function ftruncateSync(fd: number, len?: number): void; + export function chown(path: string, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function chownSync(path: string, uid: number, gid: number): void; + export function fchown(fd: number, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function fchownSync(fd: number, uid: number, gid: number): void; + export function lchown(path: string, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function lchownSync(path: string, uid: number, gid: number): void; + export function chmod(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function chmod(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function chmodSync(path: string, mode: number): void; + export function chmodSync(path: string, mode: string): void; + export function fchmod(fd: number, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function fchmod(fd: number, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function fchmodSync(fd: number, mode: number): void; + export function fchmodSync(fd: number, mode: string): void; + export function lchmod(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function lchmod(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function lchmodSync(path: string, mode: number): void; + export function lchmodSync(path: string, mode: string): void; + export function stat(path: string, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; + export function lstat(path: string, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; + export function fstat(fd: number, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; + export function statSync(path: string): Stats; + export function lstatSync(path: string): Stats; + export function fstatSync(fd: number): Stats; + export function link(srcpath: string, dstpath: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function linkSync(srcpath: string, dstpath: string): void; + export function symlink(srcpath: string, dstpath: string, type?: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function symlinkSync(srcpath: string, dstpath: string, type?: string): void; + export function readlink(path: string, callback?: (err: NodeJS.ErrnoException, linkString: string) => any): void; + export function readlinkSync(path: string): string; + export function realpath(path: string, callback?: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void; + export function realpath(path: string, cache: {[path: string]: string}, callback: (err: NodeJS.ErrnoException, resolvedPath: string) =>any): void; + export function realpathSync(path: string, cache?: { [path: string]: string }): string; + /* + * Asynchronous unlink - deletes the file specified in {path} + * + * @param path + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function unlink(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + /* + * Synchronous unlink - deletes the file specified in {path} + * + * @param path + */ + export function unlinkSync(path: string): void; + /* + * Asynchronous rmdir - removes the directory specified in {path} + * + * @param path + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function rmdir(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + /* + * Synchronous rmdir - removes the directory specified in {path} + * + * @param path + */ + export function rmdirSync(path: string): void; + /* + * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. + * + * @param path + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function mkdir(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + /* + * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. + * + * @param path + * @param mode + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function mkdir(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + /* + * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. + * + * @param path + * @param mode + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function mkdir(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + /* + * Synchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. + * + * @param path + * @param mode + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function mkdirSync(path: string, mode?: number): void; + /* + * Synchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. + * + * @param path + * @param mode + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function mkdirSync(path: string, mode?: string): void; + export function readdir(path: string, callback?: (err: NodeJS.ErrnoException, files: string[]) => void): void; + export function readdirSync(path: string): string[]; + export function close(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function closeSync(fd: number): void; + export function open(path: string, flags: string, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void; + export function open(path: string, flags: string, mode: number, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void; + export function open(path: string, flags: string, mode: string, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void; + export function openSync(path: string, flags: string, mode?: number): number; + export function openSync(path: string, flags: string, mode?: string): number; + export function utimes(path: string, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function utimes(path: string, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function utimesSync(path: string, atime: number, mtime: number): void; + export function utimesSync(path: string, atime: Date, mtime: Date): void; + export function futimes(fd: number, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function futimes(fd: number, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function futimesSync(fd: number, atime: number, mtime: number): void; + export function futimesSync(fd: number, atime: Date, mtime: Date): void; + export function fsync(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function fsyncSync(fd: number): void; + export function fdatasync(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function fdatasyncSync(fd: number): void; + export function write(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void; + export function write(fd: number, buffer: Buffer, offset: number, length: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void; + export function write(fd: number, data: any, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; + export function write(fd: number, data: any, offset: number, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; + export function write(fd: number, data: any, offset: number, encoding: string, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; + export function writeSync(fd: number, buffer: Buffer, offset: number, length: number, position?: number): number; + export function writeSync(fd: number, data: any, position?: number, enconding?: string): number; + export function read(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, bytesRead: number, buffer: Buffer) => void): void; + export function readSync(fd: number, buffer: Buffer, offset: number, length: number, position: number): number; + /* + * Asynchronous readFile - Asynchronously reads the entire contents of a file. + * + * @param fileName + * @param encoding + * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + */ + export function readFile(filename: string, encoding: string, callback: (err: NodeJS.ErrnoException, data: string) => void): void; + /* + * Asynchronous readFile - Asynchronously reads the entire contents of a file. + * + * @param fileName + * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFile returns a string; otherwise it returns a Buffer. + * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + */ + export function readFile(filename: string, options: { encoding: string; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: string) => void): void; + /* + * Asynchronous readFile - Asynchronously reads the entire contents of a file. + * + * @param fileName + * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFile returns a string; otherwise it returns a Buffer. + * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + */ + export function readFile(filename: string, options: { flag?: string; }, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; + /* + * Asynchronous readFile - Asynchronously reads the entire contents of a file. + * + * @param fileName + * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + */ + export function readFile(filename: string, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; + /* + * Synchronous readFile - Synchronously reads the entire contents of a file. + * + * @param fileName + * @param encoding + */ + export function readFileSync(filename: string, encoding: string): string; + /* + * Synchronous readFile - Synchronously reads the entire contents of a file. + * + * @param fileName + * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer. + */ + export function readFileSync(filename: string, options: { encoding: string; flag?: string; }): string; + /* + * Synchronous readFile - Synchronously reads the entire contents of a file. + * + * @param fileName + * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer. + */ + export function readFileSync(filename: string, options?: { flag?: string; }): Buffer; + export function writeFile(filename: string|number, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; + export function writeFile(filename: string|number, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; + export function writeFile(filename: string|number, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; + export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void; + export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void; + export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; + export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; + export function appendFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; + export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void; + export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void; + export function watchFile(filename: string, listener: (curr: Stats, prev: Stats) => void): void; + export function watchFile(filename: string, options: { persistent?: boolean; interval?: number; }, listener: (curr: Stats, prev: Stats) => void): void; + export function unwatchFile(filename: string, listener?: (curr: Stats, prev: Stats) => void): void; + export function watch(filename: string, listener?: (event: string, filename: string) => any): FSWatcher; + export function watch(filename: string, options: { persistent?: boolean; }, listener?: (event: string, filename: string) => any): FSWatcher; + export function exists(path: string, callback?: (exists: boolean) => void): void; + export function existsSync(path: string): boolean; + /** Constant for fs.access(). File is visible to the calling process. */ + export var F_OK: number; + /** Constant for fs.access(). File can be read by the calling process. */ + export var R_OK: number; + /** Constant for fs.access(). File can be written by the calling process. */ + export var W_OK: number; + /** Constant for fs.access(). File can be executed by the calling process. */ + export var X_OK: number; + /** Tests a user's permissions for the file specified by path. */ + export function access(path: string, callback: (err: NodeJS.ErrnoException) => void): void; + export function access(path: string, mode: number, callback: (err: NodeJS.ErrnoException) => void): void; + /** Synchronous version of fs.access. This throws if any accessibility checks fail, and does nothing otherwise. */ + export function accessSync(path: string, mode ?: number): void; + export function createReadStream(path: string, options?: { + flags?: string; + encoding?: string; + fd?: number; + mode?: number; + autoClose?: boolean; + }): ReadStream; + export function createWriteStream(path: string, options?: { + flags?: string; + encoding?: string; + fd?: number; + mode?: number; + }): WriteStream; +} + +declare module "path" { + + /** + * A parsed path object generated by path.parse() or consumed by path.format(). + */ + export interface ParsedPath { + /** + * The root of the path such as '/' or 'c:\' + */ + root: string; + /** + * The full directory path such as '/home/user/dir' or 'c:\path\dir' + */ + dir: string; + /** + * The file name including extension (if any) such as 'index.html' + */ + base: string; + /** + * The file extension (if any) such as '.html' + */ + ext: string; + /** + * The file name without extension (if any) such as 'index' + */ + name: string; + } + + /** + * Normalize a string path, reducing '..' and '.' parts. + * When multiple slashes are found, they're replaced by a single one; when the path contains a trailing slash, it is preserved. On Windows backslashes are used. + * + * @param p string path to normalize. + */ + export function normalize(p: string): string; + /** + * Join all arguments together and normalize the resulting path. + * Arguments must be strings. In v0.8, non-string arguments were silently ignored. In v0.10 and up, an exception is thrown. + * + * @param paths string paths to join. + */ + export function join(...paths: any[]): string; + /** + * Join all arguments together and normalize the resulting path. + * Arguments must be strings. In v0.8, non-string arguments were silently ignored. In v0.10 and up, an exception is thrown. + * + * @param paths string paths to join. + */ + export function join(...paths: string[]): string; + /** + * The right-most parameter is considered {to}. Other parameters are considered an array of {from}. + * + * Starting from leftmost {from} paramter, resolves {to} to an absolute path. + * + * If {to} isn't already absolute, {from} arguments are prepended in right to left order, until an absolute path is found. If after using all {from} paths still no absolute path is found, the current working directory is used as well. The resulting path is normalized, and trailing slashes are removed unless the path gets resolved to the root directory. + * + * @param pathSegments string paths to join. Non-string arguments are ignored. + */ + export function resolve(...pathSegments: any[]): string; + /** + * Determines whether {path} is an absolute path. An absolute path will always resolve to the same location, regardless of the working directory. + * + * @param path path to test. + */ + export function isAbsolute(path: string): boolean; + /** + * Solve the relative path from {from} to {to}. + * At times we have two absolute paths, and we need to derive the relative path from one to the other. This is actually the reverse transform of path.resolve. + * + * @param from + * @param to + */ + export function relative(from: string, to: string): string; + /** + * Return the directory name of a path. Similar to the Unix dirname command. + * + * @param p the path to evaluate. + */ + export function dirname(p: string): string; + /** + * Return the last portion of a path. Similar to the Unix basename command. + * Often used to extract the file name from a fully qualified path. + * + * @param p the path to evaluate. + * @param ext optionally, an extension to remove from the result. + */ + export function basename(p: string, ext?: string): string; + /** + * Return the extension of the path, from the last '.' to end of string in the last portion of the path. + * If there is no '.' in the last portion of the path or the first character of it is '.', then it returns an empty string + * + * @param p the path to evaluate. + */ + export function extname(p: string): string; + /** + * The platform-specific file separator. '\\' or '/'. + */ + export var sep: string; + /** + * The platform-specific file delimiter. ';' or ':'. + */ + export var delimiter: string; + /** + * Returns an object from a path string - the opposite of format(). + * + * @param pathString path to evaluate. + */ + export function parse(pathString: string): ParsedPath; + /** + * Returns a path string from an object - the opposite of parse(). + * + * @param pathString path to evaluate. + */ + export function format(pathObject: ParsedPath): string; + + export module posix { + export function normalize(p: string): string; + export function join(...paths: any[]): string; + export function resolve(...pathSegments: any[]): string; + export function isAbsolute(p: string): boolean; + export function relative(from: string, to: string): string; + export function dirname(p: string): string; + export function basename(p: string, ext?: string): string; + export function extname(p: string): string; + export var sep: string; + export var delimiter: string; + export function parse(p: string): ParsedPath; + export function format(pP: ParsedPath): string; + } + + export module win32 { + export function normalize(p: string): string; + export function join(...paths: any[]): string; + export function resolve(...pathSegments: any[]): string; + export function isAbsolute(p: string): boolean; + export function relative(from: string, to: string): string; + export function dirname(p: string): string; + export function basename(p: string, ext?: string): string; + export function extname(p: string): string; + export var sep: string; + export var delimiter: string; + export function parse(p: string): ParsedPath; + export function format(pP: ParsedPath): string; + } +} + +declare module "string_decoder" { + export interface NodeStringDecoder { + write(buffer: Buffer): string; + detectIncompleteChar(buffer: Buffer): number; + } + export var StringDecoder: { + new (encoding: string): NodeStringDecoder; + }; +} + +declare module "tls" { + import * as crypto from "crypto"; + import * as net from "net"; + import * as stream from "stream"; + + var CLIENT_RENEG_LIMIT: number; + var CLIENT_RENEG_WINDOW: number; + + export interface TlsOptions { + host?: string; + port?: number; + pfx?: any; //string or buffer + key?: any; //string or buffer + passphrase?: string; + cert?: any; + ca?: any; //string or buffer + crl?: any; //string or string array + ciphers?: string; + honorCipherOrder?: any; + requestCert?: boolean; + rejectUnauthorized?: boolean; + NPNProtocols?: any; //array or Buffer; + SNICallback?: (servername: string) => any; + } + + export interface ConnectionOptions { + host?: string; + port?: number; + socket?: net.Socket; + pfx?: any; //string | Buffer + key?: any; //string | Buffer + passphrase?: string; + cert?: any; //string | Buffer + ca?: any; //Array of string | Buffer + rejectUnauthorized?: boolean; + NPNProtocols?: any; //Array of string | Buffer + servername?: string; + } + + export interface Server extends net.Server { + close(): Server; + address(): { port: number; family: string; address: string; }; + addContext(hostName: string, credentials: { + key: string; + cert: string; + ca: string; + }): void; + maxConnections: number; + connections: number; + } + + export interface ClearTextStream extends stream.Duplex { + authorized: boolean; + authorizationError: Error; + getPeerCertificate(): any; + getCipher: { + name: string; + version: string; + }; + address: { + port: number; + family: string; + address: string; + }; + remoteAddress: string; + remotePort: number; + } + + export interface SecurePair { + encrypted: any; + cleartext: any; + } + + export interface SecureContextOptions { + pfx?: any; //string | buffer + key?: any; //string | buffer + passphrase?: string; + cert?: any; // string | buffer + ca?: any; // string | buffer + crl?: any; // string | string[] + ciphers?: string; + honorCipherOrder?: boolean; + } + + export interface SecureContext { + context: any; + } + + export function createServer(options: TlsOptions, secureConnectionListener?: (cleartextStream: ClearTextStream) =>void ): Server; + export function connect(options: TlsOptions, secureConnectionListener?: () =>void ): ClearTextStream; + export function connect(port: number, host?: string, options?: ConnectionOptions, secureConnectListener?: () =>void ): ClearTextStream; + export function connect(port: number, options?: ConnectionOptions, secureConnectListener?: () =>void ): ClearTextStream; + export function createSecurePair(credentials?: crypto.Credentials, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean): SecurePair; + export function createSecureContext(details: SecureContextOptions): SecureContext; +} + +declare module "crypto" { + export interface CredentialDetails { + pfx: string; + key: string; + passphrase: string; + cert: string; + ca: any; //string | string array + crl: any; //string | string array + ciphers: string; + } + export interface Credentials { context?: any; } + export function createCredentials(details: CredentialDetails): Credentials; + export function createHash(algorithm: string): Hash; + export function createHmac(algorithm: string, key: string): Hmac; + export function createHmac(algorithm: string, key: Buffer): Hmac; + export interface Hash { + update(data: any, input_encoding?: string): Hash; + digest(encoding: 'buffer'): Buffer; + digest(encoding: string): any; + digest(): Buffer; + } + export interface Hmac extends NodeJS.ReadWriteStream { + update(data: any, input_encoding?: string): Hmac; + digest(encoding: 'buffer'): Buffer; + digest(encoding: string): any; + digest(): Buffer; + } + export function createCipher(algorithm: string, password: any): Cipher; + export function createCipheriv(algorithm: string, key: any, iv: any): Cipher; + export interface Cipher extends NodeJS.ReadWriteStream { + update(data: Buffer): Buffer; + update(data: string, input_encoding: "utf8"|"ascii"|"binary"): Buffer; + update(data: Buffer, input_encoding: any, output_encoding: "binary"|"base64"|"hex"): string; + update(data: string, input_encoding: "utf8"|"ascii"|"binary", output_encoding: "binary"|"base64"|"hex"): string; + final(): Buffer; + final(output_encoding: string): string; + setAutoPadding(auto_padding: boolean): void; + getAuthTag(): Buffer; + } + export function createDecipher(algorithm: string, password: any): Decipher; + export function createDecipheriv(algorithm: string, key: any, iv: any): Decipher; + export interface Decipher extends NodeJS.ReadWriteStream { + update(data: Buffer): Buffer; + update(data: string, input_encoding: "binary"|"base64"|"hex"): Buffer; + update(data: Buffer, input_encoding: any, output_encoding: "utf8"|"ascii"|"binary"): string; + update(data: string, input_encoding: "binary"|"base64"|"hex", output_encoding: "utf8"|"ascii"|"binary"): string; + final(): Buffer; + final(output_encoding: string): string; + setAutoPadding(auto_padding: boolean): void; + setAuthTag(tag: Buffer): void; + } + export function createSign(algorithm: string): Signer; + export interface Signer extends NodeJS.WritableStream { + update(data: any): void; + sign(private_key: string, output_format: string): string; + } + export function createVerify(algorith: string): Verify; + export interface Verify extends NodeJS.WritableStream { + update(data: any): void; + verify(object: string, signature: string, signature_format?: string): boolean; + } + export function createDiffieHellman(prime_length: number): DiffieHellman; + export function createDiffieHellman(prime: number, encoding?: string): DiffieHellman; + export interface DiffieHellman { + generateKeys(encoding?: string): string; + computeSecret(other_public_key: string, input_encoding?: string, output_encoding?: string): string; + getPrime(encoding?: string): string; + getGenerator(encoding: string): string; + getPublicKey(encoding?: string): string; + getPrivateKey(encoding?: string): string; + setPublicKey(public_key: string, encoding?: string): void; + setPrivateKey(public_key: string, encoding?: string): void; + } + export function getDiffieHellman(group_name: string): DiffieHellman; + export function pbkdf2(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number, callback: (err: Error, derivedKey: Buffer) => any): void; + export function pbkdf2(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number, digest: string, callback: (err: Error, derivedKey: Buffer) => any): void; + export function pbkdf2Sync(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number) : Buffer; + export function pbkdf2Sync(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number, digest: string) : Buffer; + export function randomBytes(size: number): Buffer; + export function randomBytes(size: number, callback: (err: Error, buf: Buffer) =>void ): void; + export function pseudoRandomBytes(size: number): Buffer; + export function pseudoRandomBytes(size: number, callback: (err: Error, buf: Buffer) =>void ): void; + export interface RsaPublicKey { + key: string; + padding?: any; + } + export interface RsaPrivateKey { + key: string; + passphrase?: string, + padding?: any; + } + export function publicEncrypt(public_key: string|RsaPublicKey, buffer: Buffer): Buffer + export function privateDecrypt(private_key: string|RsaPrivateKey, buffer: Buffer): Buffer +} + +declare module "stream" { + import * as events from "events"; + + export class Stream extends events.EventEmitter { + pipe(destination: T, options?: { end?: boolean; }): T; + } + + export interface ReadableOptions { + highWaterMark?: number; + encoding?: string; + objectMode?: boolean; + } + + export class Readable extends events.EventEmitter implements NodeJS.ReadableStream { + readable: boolean; + constructor(opts?: ReadableOptions); + _read(size: number): void; + read(size?: number): any; + setEncoding(encoding: string): void; + pause(): void; + resume(): void; + destroy(): void; + pipe(destination: T, options?: { end?: boolean; }): T; + unpipe(destination?: T): void; + unshift(chunk: any): void; + wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream; + push(chunk: any, encoding?: string): boolean; + } + + export interface WritableOptions { + highWaterMark?: number; + decodeStrings?: boolean; + objectMode?: boolean; + } + + export class Writable extends events.EventEmitter implements NodeJS.WritableStream { + writable: boolean; + constructor(opts?: WritableOptions); + _write(chunk: any, encoding: string, callback: Function): void; + write(chunk: any, cb?: Function): boolean; + write(chunk: any, encoding?: string, cb?: Function): boolean; + end(): void; + end(chunk: any, cb?: Function): void; + end(chunk: any, encoding?: string, cb?: Function): void; + } + + export interface DuplexOptions extends ReadableOptions, WritableOptions { + allowHalfOpen?: boolean; + } + + // Note: Duplex extends both Readable and Writable. + export class Duplex extends Readable implements NodeJS.ReadWriteStream { + writable: boolean; + constructor(opts?: DuplexOptions); + _write(chunk: any, encoding: string, callback: Function): void; + write(chunk: any, cb?: Function): boolean; + write(chunk: any, encoding?: string, cb?: Function): boolean; + end(): void; + end(chunk: any, cb?: Function): void; + end(chunk: any, encoding?: string, cb?: Function): void; + } + + export interface TransformOptions extends ReadableOptions, WritableOptions {} + + // Note: Transform lacks the _read and _write methods of Readable/Writable. + export class Transform extends events.EventEmitter implements NodeJS.ReadWriteStream { + readable: boolean; + writable: boolean; + constructor(opts?: TransformOptions); + _transform(chunk: any, encoding: string, callback: Function): void; + _flush(callback: Function): void; + read(size?: number): any; + setEncoding(encoding: string): void; + pause(): void; + resume(): void; + pipe(destination: T, options?: { end?: boolean; }): T; + unpipe(destination?: T): void; + unshift(chunk: any): void; + wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream; + push(chunk: any, encoding?: string): boolean; + write(chunk: any, cb?: Function): boolean; + write(chunk: any, encoding?: string, cb?: Function): boolean; + end(): void; + end(chunk: any, cb?: Function): void; + end(chunk: any, encoding?: string, cb?: Function): void; + } + + export class PassThrough extends Transform {} +} + +declare module "util" { + export interface InspectOptions { + showHidden?: boolean; + depth?: number; + colors?: boolean; + customInspect?: boolean; + } + + export function format(format: any, ...param: any[]): string; + export function debug(string: string): void; + export function error(...param: any[]): void; + export function puts(...param: any[]): void; + export function print(...param: any[]): void; + export function log(string: string): void; + export function inspect(object: any, showHidden?: boolean, depth?: number, color?: boolean): string; + export function inspect(object: any, options: InspectOptions): string; + export function isArray(object: any): boolean; + export function isRegExp(object: any): boolean; + export function isDate(object: any): boolean; + export function isError(object: any): boolean; + export function inherits(constructor: any, superConstructor: any): void; + export function debuglog(key:string): (msg:string,...param: any[])=>void; +} + +declare module "assert" { + function internal (value: any, message?: string): void; + namespace internal { + export class AssertionError implements Error { + name: string; + message: string; + actual: any; + expected: any; + operator: string; + generatedMessage: boolean; + + constructor(options?: {message?: string; actual?: any; expected?: any; + operator?: string; stackStartFunction?: Function}); + } + + export function fail(actual?: any, expected?: any, message?: string, operator?: string): void; + export function ok(value: any, message?: string): void; + export function equal(actual: any, expected: any, message?: string): void; + export function notEqual(actual: any, expected: any, message?: string): void; + export function deepEqual(actual: any, expected: any, message?: string): void; + export function notDeepEqual(acutal: any, expected: any, message?: string): void; + export function strictEqual(actual: any, expected: any, message?: string): void; + export function notStrictEqual(actual: any, expected: any, message?: string): void; + export function deepStrictEqual(actual: any, expected: any, message?: string): void; + export function notDeepStrictEqual(actual: any, expected: any, message?: string): void; + export var throws: { + (block: Function, message?: string): void; + (block: Function, error: Function, message?: string): void; + (block: Function, error: RegExp, message?: string): void; + (block: Function, error: (err: any) => boolean, message?: string): void; + }; + + export var doesNotThrow: { + (block: Function, message?: string): void; + (block: Function, error: Function, message?: string): void; + (block: Function, error: RegExp, message?: string): void; + (block: Function, error: (err: any) => boolean, message?: string): void; + }; + + export function ifError(value: any): void; + } + + export = internal; +} + +declare module "tty" { + import * as net from "net"; + + export function isatty(fd: number): boolean; + export interface ReadStream extends net.Socket { + isRaw: boolean; + setRawMode(mode: boolean): void; + isTTY: boolean; + } + export interface WriteStream extends net.Socket { + columns: number; + rows: number; + isTTY: boolean; + } +} + +declare module "domain" { + import * as events from "events"; + + export class Domain extends events.EventEmitter implements NodeJS.Domain { + run(fn: Function): void; + add(emitter: events.EventEmitter): void; + remove(emitter: events.EventEmitter): void; + bind(cb: (err: Error, data: any) => any): any; + intercept(cb: (data: any) => any): any; + dispose(): void; + } + + export function create(): Domain; +} + +declare module "constants" { + export var E2BIG: number; + export var EACCES: number; + export var EADDRINUSE: number; + export var EADDRNOTAVAIL: number; + export var EAFNOSUPPORT: number; + export var EAGAIN: number; + export var EALREADY: number; + export var EBADF: number; + export var EBADMSG: number; + export var EBUSY: number; + export var ECANCELED: number; + export var ECHILD: number; + export var ECONNABORTED: number; + export var ECONNREFUSED: number; + export var ECONNRESET: number; + export var EDEADLK: number; + export var EDESTADDRREQ: number; + export var EDOM: number; + export var EEXIST: number; + export var EFAULT: number; + export var EFBIG: number; + export var EHOSTUNREACH: number; + export var EIDRM: number; + export var EILSEQ: number; + export var EINPROGRESS: number; + export var EINTR: number; + export var EINVAL: number; + export var EIO: number; + export var EISCONN: number; + export var EISDIR: number; + export var ELOOP: number; + export var EMFILE: number; + export var EMLINK: number; + export var EMSGSIZE: number; + export var ENAMETOOLONG: number; + export var ENETDOWN: number; + export var ENETRESET: number; + export var ENETUNREACH: number; + export var ENFILE: number; + export var ENOBUFS: number; + export var ENODATA: number; + export var ENODEV: number; + export var ENOENT: number; + export var ENOEXEC: number; + export var ENOLCK: number; + export var ENOLINK: number; + export var ENOMEM: number; + export var ENOMSG: number; + export var ENOPROTOOPT: number; + export var ENOSPC: number; + export var ENOSR: number; + export var ENOSTR: number; + export var ENOSYS: number; + export var ENOTCONN: number; + export var ENOTDIR: number; + export var ENOTEMPTY: number; + export var ENOTSOCK: number; + export var ENOTSUP: number; + export var ENOTTY: number; + export var ENXIO: number; + export var EOPNOTSUPP: number; + export var EOVERFLOW: number; + export var EPERM: number; + export var EPIPE: number; + export var EPROTO: number; + export var EPROTONOSUPPORT: number; + export var EPROTOTYPE: number; + export var ERANGE: number; + export var EROFS: number; + export var ESPIPE: number; + export var ESRCH: number; + export var ETIME: number; + export var ETIMEDOUT: number; + export var ETXTBSY: number; + export var EWOULDBLOCK: number; + export var EXDEV: number; + export var WSAEINTR: number; + export var WSAEBADF: number; + export var WSAEACCES: number; + export var WSAEFAULT: number; + export var WSAEINVAL: number; + export var WSAEMFILE: number; + export var WSAEWOULDBLOCK: number; + export var WSAEINPROGRESS: number; + export var WSAEALREADY: number; + export var WSAENOTSOCK: number; + export var WSAEDESTADDRREQ: number; + export var WSAEMSGSIZE: number; + export var WSAEPROTOTYPE: number; + export var WSAENOPROTOOPT: number; + export var WSAEPROTONOSUPPORT: number; + export var WSAESOCKTNOSUPPORT: number; + export var WSAEOPNOTSUPP: number; + export var WSAEPFNOSUPPORT: number; + export var WSAEAFNOSUPPORT: number; + export var WSAEADDRINUSE: number; + export var WSAEADDRNOTAVAIL: number; + export var WSAENETDOWN: number; + export var WSAENETUNREACH: number; + export var WSAENETRESET: number; + export var WSAECONNABORTED: number; + export var WSAECONNRESET: number; + export var WSAENOBUFS: number; + export var WSAEISCONN: number; + export var WSAENOTCONN: number; + export var WSAESHUTDOWN: number; + export var WSAETOOMANYREFS: number; + export var WSAETIMEDOUT: number; + export var WSAECONNREFUSED: number; + export var WSAELOOP: number; + export var WSAENAMETOOLONG: number; + export var WSAEHOSTDOWN: number; + export var WSAEHOSTUNREACH: number; + export var WSAENOTEMPTY: number; + export var WSAEPROCLIM: number; + export var WSAEUSERS: number; + export var WSAEDQUOT: number; + export var WSAESTALE: number; + export var WSAEREMOTE: number; + export var WSASYSNOTREADY: number; + export var WSAVERNOTSUPPORTED: number; + export var WSANOTINITIALISED: number; + export var WSAEDISCON: number; + export var WSAENOMORE: number; + export var WSAECANCELLED: number; + export var WSAEINVALIDPROCTABLE: number; + export var WSAEINVALIDPROVIDER: number; + export var WSAEPROVIDERFAILEDINIT: number; + export var WSASYSCALLFAILURE: number; + export var WSASERVICE_NOT_FOUND: number; + export var WSATYPE_NOT_FOUND: number; + export var WSA_E_NO_MORE: number; + export var WSA_E_CANCELLED: number; + export var WSAEREFUSED: number; + export var SIGHUP: number; + export var SIGINT: number; + export var SIGILL: number; + export var SIGABRT: number; + export var SIGFPE: number; + export var SIGKILL: number; + export var SIGSEGV: number; + export var SIGTERM: number; + export var SIGBREAK: number; + export var SIGWINCH: number; + export var SSL_OP_ALL: number; + export var SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: number; + export var SSL_OP_CIPHER_SERVER_PREFERENCE: number; + export var SSL_OP_CISCO_ANYCONNECT: number; + export var SSL_OP_COOKIE_EXCHANGE: number; + export var SSL_OP_CRYPTOPRO_TLSEXT_BUG: number; + export var SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: number; + export var SSL_OP_EPHEMERAL_RSA: number; + export var SSL_OP_LEGACY_SERVER_CONNECT: number; + export var SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: number; + export var SSL_OP_MICROSOFT_SESS_ID_BUG: number; + export var SSL_OP_MSIE_SSLV2_RSA_PADDING: number; + export var SSL_OP_NETSCAPE_CA_DN_BUG: number; + export var SSL_OP_NETSCAPE_CHALLENGE_BUG: number; + export var SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG: number; + export var SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: number; + export var SSL_OP_NO_COMPRESSION: number; + export var SSL_OP_NO_QUERY_MTU: number; + export var SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: number; + export var SSL_OP_NO_SSLv2: number; + export var SSL_OP_NO_SSLv3: number; + export var SSL_OP_NO_TICKET: number; + export var SSL_OP_NO_TLSv1: number; + export var SSL_OP_NO_TLSv1_1: number; + export var SSL_OP_NO_TLSv1_2: number; + export var SSL_OP_PKCS1_CHECK_1: number; + export var SSL_OP_PKCS1_CHECK_2: number; + export var SSL_OP_SINGLE_DH_USE: number; + export var SSL_OP_SINGLE_ECDH_USE: number; + export var SSL_OP_SSLEAY_080_CLIENT_DH_BUG: number; + export var SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG: number; + export var SSL_OP_TLS_BLOCK_PADDING_BUG: number; + export var SSL_OP_TLS_D5_BUG: number; + export var SSL_OP_TLS_ROLLBACK_BUG: number; + export var ENGINE_METHOD_DSA: number; + export var ENGINE_METHOD_DH: number; + export var ENGINE_METHOD_RAND: number; + export var ENGINE_METHOD_ECDH: number; + export var ENGINE_METHOD_ECDSA: number; + export var ENGINE_METHOD_CIPHERS: number; + export var ENGINE_METHOD_DIGESTS: number; + export var ENGINE_METHOD_STORE: number; + export var ENGINE_METHOD_PKEY_METHS: number; + export var ENGINE_METHOD_PKEY_ASN1_METHS: number; + export var ENGINE_METHOD_ALL: number; + export var ENGINE_METHOD_NONE: number; + export var DH_CHECK_P_NOT_SAFE_PRIME: number; + export var DH_CHECK_P_NOT_PRIME: number; + export var DH_UNABLE_TO_CHECK_GENERATOR: number; + export var DH_NOT_SUITABLE_GENERATOR: number; + export var NPN_ENABLED: number; + export var RSA_PKCS1_PADDING: number; + export var RSA_SSLV23_PADDING: number; + export var RSA_NO_PADDING: number; + export var RSA_PKCS1_OAEP_PADDING: number; + export var RSA_X931_PADDING: number; + export var RSA_PKCS1_PSS_PADDING: number; + export var POINT_CONVERSION_COMPRESSED: number; + export var POINT_CONVERSION_UNCOMPRESSED: number; + export var POINT_CONVERSION_HYBRID: number; + export var O_RDONLY: number; + export var O_WRONLY: number; + export var O_RDWR: number; + export var S_IFMT: number; + export var S_IFREG: number; + export var S_IFDIR: number; + export var S_IFCHR: number; + export var S_IFLNK: number; + export var O_CREAT: number; + export var O_EXCL: number; + export var O_TRUNC: number; + export var O_APPEND: number; + export var F_OK: number; + export var R_OK: number; + export var W_OK: number; + export var X_OK: number; + export var UV_UDP_REUSEADDR: number; +} \ No newline at end of file diff --git a/extensions/npm-shrinkwrap.json b/extensions/npm-shrinkwrap.json new file mode 100644 index 0000000000..5933662026 --- /dev/null +++ b/extensions/npm-shrinkwrap.json @@ -0,0 +1,11 @@ +{ + "name": "vscode-extensions", + "version": "0.0.1", + "dependencies": { + "typescript": { + "version": "2.5.2", + "from": "typescript@2.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.2.tgz" + } + } +} diff --git a/extensions/package.json b/extensions/package.json new file mode 100644 index 0000000000..89341a27eb --- /dev/null +++ b/extensions/package.json @@ -0,0 +1,11 @@ +{ + "name": "vscode-extensions", + "version": "0.0.1", + "description": "Dependencies shared by all extensions", + "dependencies": { + "typescript": "2.5.2" + }, + "scripts": { + "postinstall": "node ./postinstall" + } +} diff --git a/extensions/postinstall.js b/extensions/postinstall.js new file mode 100644 index 0000000000..ea57fc45a7 --- /dev/null +++ b/extensions/postinstall.js @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const toDelete = new Set(['tsc.js', 'tsserverlibrary.js', 'typescriptServices.js']); + +const root = path.join(__dirname, 'node_modules', 'typescript', 'lib'); +for (let name of fs.readdirSync(root)) { + if (name === 'lib.d.ts' || name.match(/^lib\..*\.d\.ts$/) || name === 'protocol.d.ts') { + continue; + } + if (name === 'typescript.js' || name === 'typescript.d.ts') { + // used by html and extension editing + continue; + } + + if (toDelete.has(name) || name.match(/\.d\.ts$/)) { + try { + fs.unlinkSync(path.join(root, name)); + console.log(`removed '${path.join(root, name)}'`); + } catch (e) { + console.warn(e); + } + } +} \ No newline at end of file diff --git a/extensions/powershell/.vscodeignore b/extensions/powershell/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/powershell/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/powershell/OSSREADME.json b/extensions/powershell/OSSREADME.json new file mode 100644 index 0000000000..a456107d65 --- /dev/null +++ b/extensions/powershell/OSSREADME.json @@ -0,0 +1,7 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[{ + "name": "SublimeText/PowerShell", + "version": "0.0.0", + "license": "MIT", + "repositoryURL": "https://github.com/SublimeText/PowerShell" +}] diff --git a/extensions/powershell/language-configuration.json b/extensions/powershell/language-configuration.json new file mode 100644 index 0000000000..b409b3ce4c --- /dev/null +++ b/extensions/powershell/language-configuration.json @@ -0,0 +1,26 @@ +{ + "comments": { + "lineComment": "#", + "blockComment": [ "<#", "#>" ] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + { "open": "\"", "close": "\"", "notIn": ["string"]}, + { "open": "'", "close": "'", "notIn": ["string", "comment"]}, + ["/**", " */"] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ] +} \ No newline at end of file diff --git a/extensions/powershell/package.json b/extensions/powershell/package.json new file mode 100644 index 0000000000..7448dd048a --- /dev/null +++ b/extensions/powershell/package.json @@ -0,0 +1,22 @@ +{ + "name": "powershell", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "languages": [{ + "id": "powershell", + "extensions": [ ".ps1", ".psm1", ".psd1", ".pssc", ".psrc" ], + "aliases": [ "PowerShell", "powershell", "ps", "ps1" ], + "configuration": "./language-configuration.json" + }], + "grammars": [{ + "language": "powershell", + "scopeName": "source.powershell", + "path": "./syntaxes/PowershellSyntax.tmLanguage" + }] + }, + "scripts": { + "update-grammar": "node ../../build/npm/update-grammar.js SublimeText/PowerShell Support/PowershellSyntax.tmLanguage ./syntaxes/powershell.tmLanguage.json" + } +} \ No newline at end of file diff --git a/extensions/powershell/syntaxes/PowershellSyntax.tmLanguage b/extensions/powershell/syntaxes/PowershellSyntax.tmLanguage new file mode 100644 index 0000000000..f54cf896b5 --- /dev/null +++ b/extensions/powershell/syntaxes/PowershellSyntax.tmLanguage @@ -0,0 +1,1186 @@ + + + + + fileTypes + + ps1 + psm1 + psd1 + + name + PowerShell + patterns + + + begin + <# + beginCaptures + + 0 + + name + punctuation.start.definition.comment.block.powershell + + + end + #> + endCaptures + + 0 + + name + punctuation.end.definition.comment.block.powershell + + + name + comment.block.powershell + patterns + + + include + #commentEmbeddedDocs + + + + + begin + (?<![\\-])# + end + $ + name + comment.line.number-sign.powershell + patterns + + + include + #commentEmbeddedDocs + + + + + match + [2-6]>&1|>>|>|<<|<|>|>\||[1-6]>|[1-6]>> + name + keyword.operator.redirection.powershell + + + include + #commands + + + include + #variable + + + include + #interpolatedStringContent + + + include + #function + + + include + #attribute + + + include + #type + + + begin + (?<!(?<!`)")" + end + "(?!") + name + string.quoted.double.powershell + patterns + + + include + #variableNoProperty + + + include + #doubleQuotedStringEscapes + + + include + #interpolation + + + match + `\s*$ + name + keyword.other.powershell + + + + + comment + Needed to parse stuff correctly in 'argument mode'. (See about_parsing.) + include + #doubleQuotedStringEscapes + + + begin + (?<!')' + end + '(?!') + name + string.quoted.single.powershell + patterns + + + match + '' + name + constant.character.escape.powershell + + + + + begin + \@"(?=$) + end + ^"@ + name + string.quoted.double.heredoc.powershell + patterns + + + include + #variableNoProperty + + + include + #doubleQuotedStringEscapes + + + include + #interpolation + + + + + begin + \@'(?=$) + end + ^'@ + name + string.quoted.single.heredoc.powershell + patterns + + + match + '' + name + constant.character.escape.powershell + + + + + include + #numericConstant + + + begin + @\( + captures + + 0 + + name + keyword.other.powershell + + + end + \) + name + meta.group.array-expression.powershell + patterns + + + include + $self + + + + + begin + \$\( + captures + + 0 + + name + keyword.other.powershell + + + comment + TODO: move to repo; make recursive. + end + \) + name + meta.group.complex.subexpression.powershell + patterns + + + include + $self + + + + + match + (?<!\w)-([ci]?[lg][te]|eq|ne) + name + keyword.operator.logical.powershell + + + match + (\b(([A-Za-z0-9\-_\.]+)\.(?i:exe|com|cmd|bat))\b) + name + support.function.powershell + + + match + (?<!\w)((?i:begin|break|catch|continue|data|define|do|dynamicparam|else|elseif|end|exit|finally|for|foreach(?!-object)|from|if|in|inlinescript|parallel|param|process|return|switch|throw|trap|try|until|using|var|where(?!=-object)|while)|%|\?)(?!\w) + name + keyword.control.powershell + + + captures + + 1 + + name + storage.type.powershell + + 2 + + name + entity.name.function + + + comment + capture should be entity.name.type, but it doesn't provide a good color in the default schema. + match + (?<!\w)((?i:class)|%|\?)(?:\s)+((?:\p{L}|\d|_|-|)+)\b + + + match + (?<!\w)-(?i:is(?:not)?|as)\b + name + keyword.operator.comparison.powershell + + + match + (?<!\w)-(?i:[ic]?(?:eq|ne|[gl][te]|(?:not)?(?:like|match|contains|in)|replace))(?!\p{L}) + name + keyword.operator.comparison.powershell + + + match + (?<!\w)-(?i:join|split)(?!\p{L})|! + name + keyword.operator.unary.powershell + + + match + (?<!\w)-(?i:and|or|not|xor)(?!\p{L})|! + name + keyword.operator.logical.powershell + + + match + (?<!\w)-(?i:band|bor|bnot|bxor)(?!\p{L}) + name + keyword.operator.bitwise.powershell + + + match + (?<!\w)-(?i:f)(?!\p{L}) + name + keyword.operator.string-format.powershell + + + match + [+%*/-]?=|[+/*%-] + name + keyword.operator.assignment.powershell + + + match + \|{2}|&{2}|; + name + keyword.other.statement-separator.powershell + + + match + &|(?<!\w)\.(?= )|`|,|\| + name + keyword.operator.other.powershell + + + comment + This is very imprecise, is there a syntax for 'must come after...' + match + (?<!\s|^)\.\.(?=\d|\(|\$) + name + keyword.operator.range.powershell + + + repository + + attribute + + begin + \[(\p{L}|\.|``\d+)+(?=\() + beginCaptures + + 0 + + name + entity.name.tag + + 1 + + name + entity.name.tag + + + end + \] + endCaptures + + 0 + + name + entity.name.tag + + + patterns + + + begin + \( + end + \) + name + entity.other.attribute-name + patterns + + + captures + + 0 + + name + entity.other.attribute.parameter.powershell + + 1 + + name + constant.language.powershell + + 2 + + name + variable.other.powershell + + + comment + really we should match the known attributes first + match + (\w+)\s*=?([^"']*?|'[^']*?'|"[^"]*?")?(?=,|\)) + name + entity.other.attribute-name.powershell + + + include + #variable + + + + + + commands + + patterns + + + comment + Verb-Noun pattern: + match + (?:(\p{L}|\d|_|-|\\|\:)*\\)?\b(?i:Add|Approve|Assert|Backup|Block|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy|Debug|Deny|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|Format|Get|Grant|Group|Hide|Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Mount|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push|Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke|Save|Search|Select|Send|Set|Show|Skip|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Test|Trace|Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Write)\-.+?(?:\.(?i:exe|cmd|bat|ps1))?\b + name + support.function.powershell + + + comment + Builtin cmdlets with reserved verbs + match + (?<!\w)(?i:foreach-object)(?!\w) + name + support.function.powershell + + + + commentEmbeddedDocs + + patterns + + + captures + + 1 + + name + constant.string.documentation.powershell + + 2 + + name + keyword.operator.documentation.powershell + + + match + (?i:\s*(\.)(SYNOPSIS|DESCRIPTION|EXAMPLE|INPUTS|OUTPUTS|NOTES|LINK|COMPONENT|FUNCTIONALITY)) + name + comment.documentation.embedded.powershell + + + captures + + 1 + + name + constant.string.documentation.powershell + + 2 + + name + keyword.operator.documentation.powershell + + 3 + + name + keyword.operator.documentation.powershell + + + match + (?i:\s*(\.)(PARAMETER|FORWARDHELPTARGETNAME|FORWARDHELPCATEGORY|REMOTEHELPRUNSPACE|EXTERNALHELP)\s+([a-z0-9-_]+)) + name + comment.documentation.embedded.powershell + + + captures + + 1 + + name + constant.string.documentation.powershell + + 2 + + name + keyword.operator.documentation.powershell + + 3 + + name + string.quoted.double.heredoc.powershell + + + match + (?i:requires\s+-(Version\s+\d(.\d+)?|Assembly\s+(.*)|Module\s+(.*)|PsSnapIn\s+(.*)|ShellId\s+(.*))) + name + comment.documentation.embedded.powershell + + + + doubleQuotedStringEscapes + + patterns + + + match + `[0abnfrvt"'$`] + name + constant.character.escape.powershell + + + match + "" + name + constant.character.escape.powershell + + + + function + + begin + (?<!\S)(?i)(function|filter|configuration|workflow)\s+(?:(global|local|script|private):)?((?:\p{L}|\d|_|-|\.)+) + beginCaptures + + 0 + + name + meta.function + + 1 + + name + storage.type + + 2 + + name + storage.modifier.scope.powershell + + 3 + + name + entity.name.function.powershell + + + end + \{|\( + + interpolatedStringContent + + begin + \( + beginCaptures + + 0 + + name + keyword.other.powershell + + + contentName + interpolated.simple.source.powershell + end + \) + endCaptures + + 0 + + name + keyword.other.powershell + + + patterns + + + include + $self + + + include + #interpolation + + + include + #interpolatedStringContent + + + + interpolation + + begin + (\$)\( + beginCaptures + + 0 + + name + keyword.other.powershell + + + contentName + interpolated.complex.source.powershell + end + \) + endCaptures + + 0 + + name + keyword.other.powershell + + + patterns + + + include + $self + + + include + #interpolation + + + include + #interpolatedStringContent + + + + numericConstant + + patterns + + + captures + + 1 + + name + keyword.operator.math.powershell + + 2 + + name + support.constant.powershell + + 3 + + name + keyword.other.powershell + + + match + (?<!\w)(?i:(0x)([a-f0-9]+)((?i:L)?(?i:[kmgtp]b)?))(?!\w) + name + constant.numeric.hexadecimal.powershell + + + captures + + 1 + + name + support.constant.powershell + + 2 + + name + keyword.operator.math.powershell + + 3 + + name + support.constant.powershell + + 4 + + name + keyword.other.powershell + + 5 + + name + keyword.other.powershell + + + match + (?<!\w)(?i:(\d*\.?\d+)(?:((?i:E)[+-]?)(\d+))?((?i:[DL])?)((?i:[kmgtp]b)?))(?!\w) + name + constant.numeric.scientific.powershell + + + + scriptblock + + begin + \{ + end + \} + name + meta.scriptblock.powershell + patterns + + + include + $self + + + + type + + begin + \[ + beginCaptures + + 0 + + name + entity.other.attribute-name + + + comment + name should be entity.name.type but default schema doesn't have a good color for it + end + \] + endCaptures + + 0 + + name + entity.other.attribute-name + + + patterns + + + match + (\p{L}|\.|``\d+)+? + name + entity.other.attribute-name + + + include + $self + + + + variable + + patterns + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + constant.language.powershell + + + comment + These are special constants. + match + (\$)(?i:(False|Null|True))\b + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + support.constant.variable.powershell + + 3 + + name + entity.name.function.invocation.powershell + + + comment + These are the other built-in constants. + match + (\$)(?i:(Error|ExecutionContext|Host|Home|PID|PsHome|PsVersionTable|ShellID))((?:\.(?:\p{L}|\d|_)+)*\b)?\b + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + support.constant.automatic.powershell + + 3 + + name + entity.name.function.invocation.powershell + + + comment + Automatic variables are not constants, but they are read-only. In monokai (default) color schema support.variable doesn't have color, so we use constant. + match + (\$)(?i:(\$|\^|\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))((?:\.(?:\p{L}|\d|_)+)*\b)?\b + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + variable.language.powershell + + 3 + + name + entity.name.function.invocation.powershell + + + comment + Style preference variables as language variables so that they stand out. + match + (\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|ProgressPreference|PsCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|VerbosePreference|WarningPreference|WhatIfPreference))((?:\.(?:\p{L}|\d|_)+)*\b)?\b + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + storage.modifier.scope.powershell + + 3 + + name + variable.other.normal.powershell + + 4 + + name + entity.name.function.invocation.powershell + + + match + (?i:(\$)(global|local|private|script|using|workflow):((?:\p{L}|\d|_)+))((?:\.(?:\p{L}|\d|_)+)*\b)? + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + storage.modifier.scope.powershell + + 3 + + name + variable.other.readwrite.powershell + + 4 + + name + keyword.other.powershell + + 5 + + name + entity.name.function.invocation.powershell + + + match + (?i:(\$\{)(global|local|private|script|using|workflow):([^}]*[^}`])(\}))((?:\.(?:\p{L}|\d|_)+)*\b)? + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + support.variable.drive.powershell + + 3 + + name + variable.other.readwrite.powershell + + 4 + + name + entity.name.function.invocation.powershell + + + match + (?i:(\$)((?:\p{L}|\d|_)+:)?((?:\p{L}|\d|_)+))((?:\.(?:\p{L}|\d|_)+)*\b)? + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + support.variable.drive.powershell + + 3 + + name + variable.other.readwrite.powershell + + 4 + + name + keyword.other.powershell + + 5 + + name + entity.name.function.invocation.powershell + + + match + (?i:(\$\{)((?:\p{L}|\d|_)+:)?([^}]*[^}`])(\}))((?:\.(?:\p{L}|\d|_)+)*\b)? + + + + variableNoProperty + + patterns + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + constant.language.powershell + + + comment + These are special constants. + match + (\$)(?i:(False|Null|True))\b + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + support.constant.variable.powershell + + 3 + + name + entity.name.function.invocation.powershell + + + comment + These are the other built-in constants. + match + (\$)(?i:(Error|ExecutionContext|Host|Home|PID|PsHome|PsVersionTable|ShellID))\b + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + support.variable.automatic.powershell + + 3 + + name + entity.name.function.invocation.powershell + + + comment + Automatic variables are not constants, but they are read-only... + match + (\$)(?i:(\$|\^|\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))\b + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + variable.language.powershell + + 3 + + name + entity.name.function.invocation.powershell + + + comment + Style preference variables as language variables so that they stand out. + match + (\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|ProgressPreference|PsCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|VerbosePreference|WarningPreference|WhatIfPreference))\b + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + storage.modifier.scope.powershell + + 3 + + name + variable.other.normal.powershell + + 4 + + name + entity.name.function.invocation.powershell + + + match + (?i:(\$)(global|local|private|script|using|workflow):((?:\p{L}|\d|_)+)) + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + storage.modifier.scope.powershell + + 3 + + name + variable.other.readwrite.powershell + + 4 + + name + keyword.other.powershell + + 5 + + name + entity.name.function.invocation.powershell + + + match + (?i:(\$\{)(global|local|private|script|using|workflow):([^}]*[^}`])(\})) + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + support.variable.drive.powershell + + 3 + + name + variable.other.readwrite.powershell + + 4 + + name + entity.name.function.invocation.powershell + + + match + (?i:(\$)((?:\p{L}|\d|_)+:)?((?:\p{L}|\d|_)+)) + + + captures + + 1 + + name + keyword.other.powershell + + 2 + + name + support.variable.drive.powershell + + 3 + + name + variable.other.readwrite.powershell + + 4 + + name + keyword.other.powershell + + 5 + + name + entity.name.function.invocation.powershell + + + match + (?i:(\$\{)((?:\p{L}|\d|_)+:)?([^}]*[^}`])(\})) + + + + + scopeName + source.powershell + uuid + f8f5ffb0-503e-11df-9879-0800200c9a66 + + diff --git a/extensions/powershell/test/colorize-fixtures/test.ps1 b/extensions/powershell/test/colorize-fixtures/test.ps1 new file mode 100644 index 0000000000..8f524fe823 --- /dev/null +++ b/extensions/powershell/test/colorize-fixtures/test.ps1 @@ -0,0 +1,43 @@ +# Copyright Microsoft Corporation + +function Test-IsAdmin() { + try { + $identity = [Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object Security.Principal.WindowsPrincipal -ArgumentList $identity + return $principal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator ) + } catch { + throw "Failed to determine if the current user has elevated privileges. The error was: '{0}'." -f $_ + } +} + +function Invoke-Environment() +{ + param + ( + [Parameter(Mandatory=1)][string]$Command + ) + + foreach($_ in cmd /c "$Command 2>&1 & set") { + if ($_ -match '^([^=]+)=(.*)') { + [System.Environment]::SetEnvironmentVariable($matches[1], $matches[2]) + } + } +} +Write-Host -Object 'Initializing Azure PowerShell environment...'; + +# PowerShell commands need elevation for dependencies installation and running tests +if (!(Test-IsAdmin)){ + Write-Host -Object 'Please launch command under administrator account. It is needed for environment setting up and unit test.' -ForegroundColor Red; +} + +$env:AzurePSRoot = Split-Path -Parent -Path $env:AzurePSRoot; + +if (Test-Path -Path "$env:ADXSDKProgramFiles\Microsoft Visual Studio 12.0") { + $vsVersion="12.0" +} else { + $vsVersion="11.0" +} + +$setVSEnv = '"{0}\Microsoft Visual Studio {1}\VC\vcvarsall.bat" x64' -f $env:ADXSDKProgramFiles, $vsVersion; + +Invoke-Environment -Command $setVSEnv; \ No newline at end of file diff --git a/extensions/powershell/test/colorize-results/test_ps1.json b/extensions/powershell/test/colorize-results/test_ps1.json new file mode 100644 index 0000000000..bce680fee4 --- /dev/null +++ b/extensions/powershell/test/colorize-results/test_ps1.json @@ -0,0 +1,2422 @@ +[ + { + "c": "# Copyright Microsoft Corporation", + "t": "source.powershell comment.line.number-sign.powershell", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "function", + "t": "source.powershell meta.function storage.type", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.powershell meta.function", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Test-IsAdmin", + "t": "source.powershell meta.function entity.name.function.powershell", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "() {", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "try", + "t": "source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " {", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "identity", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[Security.Principal.WindowsIdentity]", + "t": "source.powershell entity.other.attribute-name", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "::GetCurrent", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "()", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "principal", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "New-Object", + "t": "source.powershell support.function.powershell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " Security.Principal.WindowsPrincipal ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "ArgumentList ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "identity", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "return", + "t": "source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "principal", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".IsInRole", + "t": "source.powershell entity.name.function.invocation.powershell", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[Security.Principal.WindowsBuiltInRole]", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "::Administrator ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " } ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "catch", + "t": "source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " {", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "throw", + "t": "source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"Failed to determine if the current user has elevated privileges. The error was: '{0}'.\"", + "t": "source.powershell string.quoted.double.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-f", + "t": "source.powershell keyword.operator.string-format.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "_", + "t": "source.powershell support.constant.automatic.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " }", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "function", + "t": "source.powershell meta.function storage.type", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.powershell meta.function", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Invoke-Environment", + "t": "source.powershell meta.function entity.name.function.powershell", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "param", + "t": "source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[Paramete", + "t": "source.powershell interpolated.simple.source.powershell entity.name.tag", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": "r", + "t": "source.powershell interpolated.simple.source.powershell entity.name.tag entity.name.tag", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": "(", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "Mandatory", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name entity.other.attribute-name.powershell entity.other.attribute.parameter.powershell constant.language.powershell", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": "=", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name entity.other.attribute-name.powershell entity.other.attribute.parameter.powershell", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "1", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name entity.other.attribute-name.powershell entity.other.attribute.parameter.powershell variable.other.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "]", + "t": "source.powershell interpolated.simple.source.powershell entity.name.tag", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": "[string]", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "$", + "t": "source.powershell interpolated.simple.source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "Command", + "t": "source.powershell interpolated.simple.source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "foreach", + "t": "source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "(", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "$", + "t": "source.powershell interpolated.simple.source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "_", + "t": "source.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "in", + "t": "source.powershell interpolated.simple.source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " cmd ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/", + "t": "source.powershell interpolated.simple.source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "c ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "Command", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " 2>&1 & set\"", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " {", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "if", + "t": "source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "$", + "t": "source.powershell interpolated.simple.source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "_", + "t": "source.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-match", + "t": "source.powershell interpolated.simple.source.powershell keyword.operator.comparison.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'^([^=]+)=(.*)'", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.single.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " {", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[System.Environment]", + "t": "source.powershell entity.other.attribute-name", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "::SetEnvironmentVariable", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "$", + "t": "source.powershell interpolated.simple.source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "matches", + "t": "source.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "1", + "t": "source.powershell interpolated.simple.source.powershell constant.numeric.scientific.powershell support.constant.powershell", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "]", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.powershell interpolated.simple.source.powershell keyword.operator.other.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell interpolated.simple.source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "matches", + "t": "source.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "2", + "t": "source.powershell interpolated.simple.source.powershell constant.numeric.scientific.powershell support.constant.powershell", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "]", + "t": "source.powershell interpolated.simple.source.powershell entity.other.attribute-name", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " }", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " }", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Write-Host", + "t": "source.powershell support.function.powershell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "Object ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'Initializing Azure PowerShell environment...'", + "t": "source.powershell string.quoted.single.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ";", + "t": "source.powershell keyword.other.statement-separator.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "# PowerShell commands need elevation for dependencies installation and running tests", + "t": "source.powershell comment.line.number-sign.powershell", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "if", + "t": "source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "!", + "t": "source.powershell interpolated.simple.source.powershell keyword.operator.unary.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "(", + "t": "source.powershell interpolated.simple.source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "Test-IsAdmin", + "t": "source.powershell interpolated.simple.source.powershell interpolated.simple.source.powershell support.function.powershell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": ")", + "t": "source.powershell interpolated.simple.source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": ")", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "{", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Write-Host", + "t": "source.powershell support.function.powershell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "Object ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'Please launch command under administrator account. It is needed for environment setting up and unit test.'", + "t": "source.powershell string.quoted.single.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "ForegroundColor Red", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.powershell keyword.other.statement-separator.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "}", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "env:", + "t": "source.powershell support.variable.drive.powershell", + "r": { + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.variable: #9CDCFE" + } + }, + { + "c": "AzurePSRoot", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Split-Path", + "t": "source.powershell support.function.powershell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "Parent ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "Path ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "env:", + "t": "source.powershell support.variable.drive.powershell", + "r": { + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.variable: #9CDCFE" + } + }, + { + "c": "AzurePSRoot", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ";", + "t": "source.powershell keyword.other.statement-separator.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "if", + "t": "source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "Test-Path", + "t": "source.powershell interpolated.simple.source.powershell support.function.powershell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.powershell interpolated.simple.source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "Path ", + "t": "source.powershell interpolated.simple.source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "env:", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell support.variable.drive.powershell", + "r": { + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "support.variable: #9CDCFE" + } + }, + { + "c": "ADXSDKProgramFiles", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\\Microsoft Visual Studio 12.0\"", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " {", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "vsVersion", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "=", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "\"12.0\"", + "t": "source.powershell string.quoted.double.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "} ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "else", + "t": "source.powershell keyword.control.powershell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " {", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "vsVersion", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "=", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "\"11.0\"", + "t": "source.powershell string.quoted.double.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "}", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "setVSEnv", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'\"{0}\\Microsoft Visual Studio {1}\\VC\\vcvarsall.bat\" x64'", + "t": "source.powershell string.quoted.single.powershell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-f", + "t": "source.powershell keyword.operator.string-format.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "env:", + "t": "source.powershell support.variable.drive.powershell", + "r": { + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.variable: #9CDCFE" + } + }, + { + "c": "ADXSDKProgramFiles", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.powershell keyword.operator.other.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "vsVersion", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ";", + "t": "source.powershell keyword.other.statement-separator.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "Invoke-Environment", + "t": "source.powershell support.function.powershell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.powershell keyword.operator.assignment.powershell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "Command ", + "t": "source.powershell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.powershell keyword.other.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "setVSEnv", + "t": "source.powershell variable.other.readwrite.powershell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ";", + "t": "source.powershell keyword.other.statement-separator.powershell", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + } +] \ No newline at end of file diff --git a/extensions/python/.vscode/launch.json b/extensions/python/.vscode/launch.json new file mode 100644 index 0000000000..476551beba --- /dev/null +++ b/extensions/python/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}" + ], + "stopOnEntry": false, + "sourceMaps": true, + "outDir": "${workspaceRoot}/out", + "preLaunchTask": "npm" + } + ] +} \ No newline at end of file diff --git a/extensions/python/.vscode/tasks.json b/extensions/python/.vscode/tasks.json new file mode 100644 index 0000000000..a132a04214 --- /dev/null +++ b/extensions/python/.vscode/tasks.json @@ -0,0 +1,30 @@ +// Available variables which can be used inside of strings. +// ${workspaceRoot}: the root folder of the team +// ${file}: the current opened file +// ${fileBasename}: the current opened file's basename +// ${fileDirname}: the current opened file's dirname +// ${fileExtname}: the current opened file's extension +// ${cwd}: the current working directory of the spawned process + +// A task runner that calls a custom npm script that compiles the extension. +{ + "version": "0.1.0", + + // we want to run npm + "command": "npm", + + // the command is a shell script + "isShellCommand": true, + + // show the output window only if unrecognized errors occur. + "showOutput": "silent", + + // we run the custom script "compile" as defined in package.json + "args": ["run", "compile"], + + // The tsc compiler is started in watching mode + "isWatching": true, + + // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "problemMatcher": "$tsc-watch" +} \ No newline at end of file diff --git a/extensions/python/.vscodeignore b/extensions/python/.vscodeignore new file mode 100644 index 0000000000..ebab1d50b9 --- /dev/null +++ b/extensions/python/.vscodeignore @@ -0,0 +1,3 @@ +test/** +src/** +tsconfig.json \ No newline at end of file diff --git a/extensions/python/OSSREADME.json b/extensions/python/OSSREADME.json new file mode 100644 index 0000000000..cf4449588e --- /dev/null +++ b/extensions/python/OSSREADME.json @@ -0,0 +1,9 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[ + { + "name": "MagicStack/MagicPython", + "version": "0.0.0", + "license": "MIT", + "repositoryURL": "https://github.com/MagicStack/MagicPython" + } +] \ No newline at end of file diff --git a/extensions/python/language-configuration.json b/extensions/python/language-configuration.json new file mode 100644 index 0000000000..c995ea91f3 --- /dev/null +++ b/extensions/python/language-configuration.json @@ -0,0 +1,26 @@ +{ + "comments": { + "lineComment": "#", + "blockComment": [ "'''", "'''" ] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + { "open": "{", "close": "}" }, + { "open": "[", "close": "]" }, + { "open": "(", "close": ")" }, + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string", "comment"] } + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ] + // enhancedBrackets: [ { open: /.*:\s*$/, closeComplete: 'else:' } ], +} \ No newline at end of file diff --git a/extensions/python/package.json b/extensions/python/package.json new file mode 100644 index 0000000000..49c9d6ace9 --- /dev/null +++ b/extensions/python/package.json @@ -0,0 +1,30 @@ +{ + "name": "python", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "activationEvents": ["onLanguage:python"], + "main": "./out/pythonMain", + "contributes": { + "languages": [{ + "id": "python", + "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi" ], + "aliases": [ "Python", "py" ], + "firstLine": "^#!/.*\\bpython[0-9.-]*\\b", + "configuration": "./language-configuration.json" + }], + "grammars": [{ + "language": "python", + "scopeName": "source.python", + "path": "./syntaxes/MagicPython.tmLanguage.json" + },{ + "scopeName": "source.regexp.python", + "path": "./syntaxes/MagicRegExp.tmLanguage.json" + }] + }, + "scripts": { + "compile": "gulp compile-extension:python", + "watch": "gulp watch-extension:python", + "update-grammar": "node ../../build/npm/update-grammar.js MagicStack/MagicPython grammars/MagicPython.tmLanguage ./syntaxes/MagicPython.tmLanguage.json grammars/MagicRegExp.tmLanguage ./syntaxes/MagicRegExp.tmLanguage.json" + } +} diff --git a/extensions/python/src/pythonMain.ts b/extensions/python/src/pythonMain.ts new file mode 100644 index 0000000000..e6761baa00 --- /dev/null +++ b/extensions/python/src/pythonMain.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +import { ExtensionContext, languages, IndentAction } from 'vscode'; + +export function activate(context: ExtensionContext): any { + languages.setLanguageConfiguration('python', { + onEnterRules: [ + { + beforeText: /^\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async).*?:\s*$/, + action: { indentAction: IndentAction.Indent } + } + ] + }); +} \ No newline at end of file diff --git a/extensions/python/src/typings/ref.d.ts b/extensions/python/src/typings/ref.d.ts new file mode 100644 index 0000000000..7507008914 --- /dev/null +++ b/extensions/python/src/typings/ref.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// \ No newline at end of file diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json new file mode 100644 index 0000000000..812ddf9735 --- /dev/null +++ b/extensions/python/syntaxes/MagicPython.tmLanguage.json @@ -0,0 +1,5253 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/MagicStack/MagicPython/blob/master/grammars/MagicPython.tmLanguage", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/MagicStack/MagicPython/commit/b453f26ed856c9b16a053517c41207e3a72cc7d5", + "name": "MagicPython", + "scopeName": "source.python", + "fileTypes": [ + "py", + "py3", + "rpy", + "pyw", + "cpy", + "pyi", + "SConstruct", + "Sconstruct", + "sconstruct", + "SConscript", + "gyp", + "gypi", + "wsgi", + "kv", + "Snakefile", + "tac" + ], + "first_line_match": "^#![ \\t]*/.*\\bpython[\\d\\.]*\\b", + "firstLineMatch": "^#![ \\t]*/.*\\bpython[\\d\\.]*\\b", + "uuid": "742deb57-6e38-4192-bed6-410746efd85d", + "patterns": [ + { + "include": "#statement" + }, + { + "include": "#expression" + } + ], + "repository": { + "impossible": { + "comment": "This is a special rule that should be used where no match is desired. It is not a good idea to match something like '1{0}' because in some cases that can result in infinite loops in token generation. So the rule instead matches and impossible expression to allow a match to fail and move to the next token.", + "match": "$.^" + }, + "statement": { + "patterns": [ + { + "include": "#import" + }, + { + "include": "#class-declaration" + }, + { + "include": "#function-declaration" + }, + { + "include": "#statement-keyword" + }, + { + "include": "#assignment-operator" + }, + { + "include": "#decorator" + }, + { + "include": "#docstring-statement" + }, + { + "include": "#semicolon" + } + ] + }, + "semicolon": { + "patterns": [ + { + "name": "invalid.deprecated.semicolon.python", + "match": "\\;$" + } + ] + }, + "comments": { + "patterns": [ + { + "name": "comment.line.number-sign.python", + "contentName": "meta.typehint.comment.python", + "begin": "(?x)\n (?:\n \\# \\s* (type:)\n \\s*+ (?# we want `\\s*+` which is possessive quantifier since\n we do not actually want to backtrack when matching\n whitespace here)\n (?! $ | \\#)\n )\n", + "end": "(?:$|(?=\\#))", + "beginCaptures": { + "0": { + "name": "meta.typehint.comment.python" + }, + "1": { + "name": "comment.typehint.directive.notation.python" + } + }, + "patterns": [ + { + "name": "comment.typehint.ignore.notation.python", + "match": "(?x)\n \\G ignore\n (?= \\s* (?: $ | \\#))\n" + }, + { + "name": "comment.typehint.type.notation.python", + "match": "(?x)\n (?))" + }, + { + "name": "comment.typehint.variable.notation.python", + "match": "([[:alpha:]_]\\w*)" + } + ] + }, + { + "include": "#comments-base" + } + ] + }, + "docstring-statement": { + "begin": "^(?=\\s*[rR]?(\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\"))", + "end": "(?<=\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\")", + "patterns": [ + { + "include": "#docstring" + } + ] + }, + "docstring": { + "patterns": [ + { + "name": "string.quoted.docstring.multi.python", + "begin": "(\\'\\'\\'|\\\"\\\"\\\")", + "end": "(\\1)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.string.begin.python" + } + }, + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.python" + } + }, + "patterns": [ + { + "include": "#docstring-prompt" + }, + { + "include": "#codetags" + }, + { + "include": "#docstring-guts-unicode" + } + ] + }, + { + "name": "string.quoted.docstring.raw.multi.python", + "begin": "([rR])(\\'\\'\\'|\\\"\\\"\\\")", + "end": "(\\2)", + "beginCaptures": { + "1": { + "name": "storage.type.string.python" + }, + "2": { + "name": "punctuation.definition.string.begin.python" + } + }, + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.python" + } + }, + "patterns": [ + { + "include": "#string-consume-escape" + }, + { + "include": "#docstring-prompt" + }, + { + "include": "#codetags" + } + ] + }, + { + "name": "string.quoted.docstring.single.python", + "begin": "(\\'|\\\")", + "end": "(\\1)|((?>>|\\.\\.\\.) \\s) (?=\\s*\\S)\n )\n", + "captures": { + "1": { + "name": "keyword.control.flow.python" + } + } + }, + "statement-keyword": { + "patterns": [ + { + "name": "storage.type.function.python", + "match": "\\b((async\\s+)?\\s*def)\\b" + }, + { + "name": "keyword.control.flow.python", + "match": "(?x)\n \\b(?>= | //= | \\*\\*=\n | \\+= | -= | /= | @=\n | \\*= | %= | ~= | \\^= | &= | \\|=\n | =(?!=)\n" + }, + "operator": { + "match": "(?x)\n \\b(?> | & | \\| | \\^ | ~) (?# 3)\n\n | (\\*\\* | \\* | \\+ | - | % | // | / | @) (?# 4)\n\n | (!= | == | >= | <= | < | >) (?# 5)\n", + "captures": { + "1": { + "name": "keyword.operator.logical.python" + }, + "2": { + "name": "keyword.control.flow.python" + }, + "3": { + "name": "keyword.operator.bitwise.python" + }, + "4": { + "name": "keyword.operator.arithmetic.python" + }, + "5": { + "name": "keyword.operator.comparison.python" + } + } + }, + "punctuation": { + "patterns": [ + { + "name": "punctuation.separator.colon.python", + "match": ":" + }, + { + "name": "punctuation.separator.element.python", + "match": "," + } + ] + }, + "literal": { + "patterns": [ + { + "name": "constant.language.python", + "match": "\\b(True|False|None|NotImplemented|Ellipsis)\\b" + }, + { + "include": "#number" + } + ] + }, + "number": { + "name": "constant.numeric.python", + "patterns": [ + { + "include": "#number-float" + }, + { + "include": "#number-dec" + }, + { + "include": "#number-hex" + }, + { + "include": "#number-oct" + }, + { + "include": "#number-bin" + }, + { + "include": "#number-long" + }, + { + "name": "invalid.illegal.name.python", + "match": "\\b[0-9]+\\w+" + } + ] + }, + "number-float": { + "name": "constant.numeric.float.python", + "match": "(?x)\n (?=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)? [bcdeEfFgGnosxX%]? )?\n })\n )\n", + "captures": { + "2": { + "name": "storage.type.format.python" + }, + "3": { + "name": "storage.type.format.python" + } + } + }, + { + "name": "constant.character.format.placeholder.other.python", + "begin": "(?x)\n \\{\n \\w*? (\\.[[:alpha:]_]\\w*? | \\[[^\\]'\"]+\\])*?\n (![rsa])?\n (:)\n (?=[^'\"}\\n]*\\})\n", + "end": "\\}", + "beginCaptures": { + "2": { + "name": "storage.type.format.python" + }, + "3": { + "name": "storage.type.format.python" + } + }, + "patterns": [ + { + "match": "(?x) \\{ [^'\"}\\n]*? \\} (?=.*?\\})\n" + } + ] + } + ] + }, + "fstring-formatting": { + "patterns": [ + { + "include": "#fstring-formatting-braces" + }, + { + "include": "#fstring-formatting-singe-brace" + } + ] + }, + "fstring-formatting-singe-brace": { + "name": "invalid.illegal.brace.python", + "match": "(}(?!}))" + }, + "import": { + "comment": "Import statements\n", + "patterns": [ + { + "match": "(?x)\n \\s* \\b(from)\\b \\s*(\\.+)\\s* (import)?\n", + "captures": { + "1": { + "name": "keyword.control.import.python" + }, + "2": { + "name": "punctuation.separator.period.python" + }, + "3": { + "name": "keyword.control.import.python" + } + } + }, + { + "name": "keyword.control.import.python", + "match": "\\b(?)", + "end": "(?=:)", + "beginCaptures": { + "1": { + "name": "punctuation.separator.annotation.result.python" + } + }, + "patterns": [ + { + "include": "#expression" + } + ] + }, + "item-access": { + "patterns": [ + { + "name": "meta.item-access.python", + "begin": "(?x)\n \\b(?=\n [[:alpha:]_]\\w* \\s* \\[\n )\n", + "end": "(\\])", + "endCaptures": { + "1": { + "name": "punctuation.definition.arguments.end.python" + } + }, + "patterns": [ + { + "include": "#item-name" + }, + { + "include": "#item-index" + }, + { + "include": "#expression" + } + ] + } + ] + }, + "item-name": { + "patterns": [ + { + "include": "#special-variables" + }, + { + "include": "#builtin-functions" + }, + { + "include": "#special-names" + }, + { + "match": "(?x)\n \\b ([[:alpha:]_]\\w*) \\b\n" + } + ] + }, + "item-index": { + "begin": "(\\[)", + "end": "(?=\\])", + "beginCaptures": { + "1": { + "name": "punctuation.definition.arguments.begin.python" + } + }, + "contentName": "meta.item-access.arguments.python", + "patterns": [ + { + "name": "punctuation.separator.slice.python", + "match": ":" + }, + { + "include": "#expression" + } + ] + }, + "decorator": { + "name": "meta.function.decorator.python", + "begin": "(?x)\n ^\\s*\n (@) \\s* (?=[[:alpha:]_]\\w*)\n", + "end": "(?x)\n ( \\) )\n # trailing whitespace and comments are legal\n (?: (.*?) (?=\\s*(?:\\#|$)) )\n | (?=\\n|\\#)\n", + "beginCaptures": { + "1": { + "name": "entity.name.function.decorator.python" + } + }, + "endCaptures": { + "1": { + "name": "punctuation.definition.arguments.end.python" + }, + "2": { + "name": "invalid.illegal.decorator.python" + } + }, + "patterns": [ + { + "include": "#decorator-name" + }, + { + "include": "#function-arguments" + } + ] + }, + "decorator-name": { + "patterns": [ + { + "include": "#builtin-callables" + }, + { + "include": "#illegal-object-name" + }, + { + "name": "entity.name.function.decorator.python", + "match": "(?x)\n ([[:alpha:]_]\\w*) | \\.\n" + }, + { + "include": "#line-continuation" + }, + { + "name": "invalid.illegal.decorator.python", + "match": "(?x)\n \\s* ([^([:alpha:]\\s_\\.#\\\\] .*?) (?=\\#|$)\n", + "captures": { + "1": { + "name": "invalid.illegal.decorator.python" + } + } + } + ] + }, + "call-wrapper-inheritance": { + "comment": "same as a function call, but in inheritance context", + "name": "meta.function-call.python", + "begin": "(?x)\n \\b(?=\n ([[:alpha:]_]\\w*) \\s* (\\()\n )\n", + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.definition.arguments.end.python" + } + }, + "patterns": [ + { + "include": "#inheritance-name" + }, + { + "include": "#function-arguments" + } + ] + }, + "inheritance-name": { + "patterns": [ + { + "include": "#lambda-incomplete" + }, + { + "include": "#builtin-possible-callables" + }, + { + "include": "#inheritance-identifier" + } + ] + }, + "function-call": { + "name": "meta.function-call.python", + "begin": "(?x)\n \\b(?=\n ([[:alpha:]_]\\w*) \\s* (\\()\n )\n", + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.definition.arguments.end.python" + } + }, + "patterns": [ + { + "include": "#special-variables" + }, + { + "include": "#function-name" + }, + { + "include": "#function-arguments" + } + ] + }, + "function-name": { + "patterns": [ + { + "include": "#builtin-possible-callables" + }, + { + "comment": "Some color schemas support meta.function-call.generic scope", + "name": "meta.function-call.generic.python", + "match": "(?x)\n \\b ([[:alpha:]_]\\w*) \\b\n" + } + ] + }, + "function-arguments": { + "begin": "(?x)\n (?:\n (\\()\n (?:\\s*(\\*\\*|\\*))?\n )\n", + "end": "(?=\\))(?!\\)\\s*\\()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.arguments.begin.python" + }, + "2": { + "name": "keyword.operator.unpacking.arguments.python" + } + }, + "contentName": "meta.function-call.arguments.python", + "patterns": [ + { + "match": "(?x)\n (?:\n (,)\n (?:\\s*(\\*\\*|\\*))?\n )\n", + "captures": { + "1": { + "name": "punctuation.separator.arguments.python" + }, + "2": { + "name": "keyword.operator.unpacking.arguments.python" + } + } + }, + { + "include": "#lambda-incomplete" + }, + { + "include": "#illegal-names" + }, + { + "match": "\\b([[:alpha:]_]\\w*)\\s*(=)(?!=)", + "captures": { + "1": { + "name": "variable.parameter.function-call.python" + }, + "2": { + "name": "keyword.operator.assignment.python" + } + } + }, + { + "name": "keyword.operator.assignment.python", + "match": "=(?!=)" + }, + { + "include": "#expression" + }, + { + "match": "\\s*(\\))\\s*(\\()", + "captures": { + "1": { + "name": "punctuation.definition.arguments.end.python" + }, + "2": { + "name": "punctuation.definition.arguments.begin.python" + } + } + } + ] + }, + "builtin-callables": { + "patterns": [ + { + "include": "#illegal-names" + }, + { + "include": "#illegal-object-name" + }, + { + "include": "#builtin-exceptions" + }, + { + "include": "#builtin-functions" + }, + { + "include": "#builtin-types" + } + ] + }, + "builtin-possible-callables": { + "patterns": [ + { + "include": "#builtin-callables" + }, + { + "include": "#magic-names" + } + ] + }, + "builtin-exceptions": { + "name": "support.type.exception.python", + "match": "(?x) (?" + }, + "regexp-base-expression": { + "patterns": [ + { + "include": "#regexp-quantifier" + }, + { + "include": "#regexp-base-common" + } + ] + }, + "fregexp-base-expression": { + "patterns": [ + { + "include": "#fregexp-quantifier" + }, + { + "include": "#fstring-formatting-braces" + }, + { + "match": "\\{.*?\\}" + }, + { + "include": "#regexp-base-common" + } + ] + }, + "fstring-formatting-braces": { + "patterns": [ + { + "comment": "empty braces are illegal", + "match": "({)(\\s*?)(})", + "captures": { + "1": { + "name": "constant.character.format.placeholder.other.python" + }, + "2": { + "name": "invalid.illegal.brace.python" + }, + "3": { + "name": "constant.character.format.placeholder.other.python" + } + } + }, + { + "name": "constant.character.escape.python", + "match": "({{|}})" + } + ] + }, + "regexp-base-common": { + "patterns": [ + { + "name": "support.other.match.any.regexp", + "match": "\\." + }, + { + "name": "support.other.match.begin.regexp", + "match": "\\^" + }, + { + "name": "support.other.match.end.regexp", + "match": "\\$" + }, + { + "name": "keyword.operator.quantifier.regexp", + "match": "[+*?]\\??" + }, + { + "name": "keyword.operator.disjunction.regexp", + "match": "\\|" + }, + { + "include": "#regexp-escape-sequence" + } + ] + }, + "regexp-quantifier": { + "name": "keyword.operator.quantifier.regexp", + "match": "(?x)\n \\{(\n \\d+ | \\d+,(\\d+)? | ,\\d+\n )\\}\n" + }, + "fregexp-quantifier": { + "name": "keyword.operator.quantifier.regexp", + "match": "(?x)\n \\{\\{(\n \\d+ | \\d+,(\\d+)? | ,\\d+\n )\\}\\}\n" + }, + "regexp-backreference-number": { + "name": "meta.backreference.regexp", + "match": "(\\\\[1-9]\\d?)", + "captures": { + "1": { + "name": "entity.name.tag.backreference.regexp" + } + } + }, + "regexp-backreference": { + "name": "meta.backreference.named.regexp", + "match": "(?x)\n (\\() (\\?P= \\w+(?:\\s+[[:alnum:]]+)?) (\\))\n", + "captures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.backreference.named.begin.regexp" + }, + "2": { + "name": "entity.name.tag.named.backreference.regexp" + }, + "3": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.backreference.named.end.regexp" + } + } + }, + "regexp-flags": { + "name": "storage.modifier.flag.regexp", + "match": "\\(\\?[aiLmsux]+\\)" + }, + "regexp-escape-special": { + "name": "support.other.escape.special.regexp", + "match": "\\\\([AbBdDsSwWZ])" + }, + "regexp-escape-character": { + "name": "constant.character.escape.regexp", + "match": "(?x)\n \\\\ (\n x[0-9A-Fa-f]{2}\n | 0[0-7]{1,2}\n | [0-7]{3}\n )\n" + }, + "regexp-escape-unicode": { + "name": "constant.character.unicode.regexp", + "match": "(?x)\n \\\\ (\n u[0-9A-Fa-f]{4}\n | U[0-9A-Fa-f]{8}\n )\n" + }, + "regexp-escape-catchall": { + "name": "constant.character.escape.regexp", + "match": "\\\\(.|\\n)" + }, + "regexp-escape-sequence": { + "patterns": [ + { + "include": "#regexp-escape-special" + }, + { + "include": "#regexp-escape-character" + }, + { + "include": "#regexp-escape-unicode" + }, + { + "include": "#regexp-backreference-number" + }, + { + "include": "#regexp-escape-catchall" + } + ] + }, + "regexp-charecter-set-escapes": { + "patterns": [ + { + "name": "constant.character.escape.regexp", + "match": "\\\\[abfnrtv\\\\]" + }, + { + "include": "#regexp-escape-special" + }, + { + "name": "constant.character.escape.regexp", + "match": "\\\\([0-7]{1,3})" + }, + { + "include": "#regexp-escape-character" + }, + { + "include": "#regexp-escape-unicode" + }, + { + "include": "#regexp-escape-catchall" + } + ] + }, + "codetags": { + "match": "(?:\\b(NOTE|XXX|HACK|FIXME|BUG|TODO)\\b)", + "captures": { + "1": { + "name": "keyword.codetag.notation.python" + } + } + }, + "comments-base": { + "name": "comment.line.number-sign.python", + "begin": "(\\#)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.comment.python" + } + }, + "end": "($)", + "patterns": [ + { + "include": "#codetags" + } + ] + }, + "comments-string-single-three": { + "name": "comment.line.number-sign.python", + "begin": "(\\#)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.comment.python" + } + }, + "end": "($|(?='''))", + "patterns": [ + { + "include": "#codetags" + } + ] + }, + "comments-string-double-three": { + "name": "comment.line.number-sign.python", + "begin": "(\\#)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.comment.python" + } + }, + "end": "($|(?=\"\"\"))", + "patterns": [ + { + "include": "#codetags" + } + ] + }, + "single-one-regexp-expression": { + "patterns": [ + { + "include": "#regexp-base-expression" + }, + { + "include": "#single-one-regexp-character-set" + }, + { + "include": "#single-one-regexp-comments" + }, + { + "include": "#regexp-flags" + }, + { + "include": "#single-one-regexp-named-group" + }, + { + "include": "#regexp-backreference" + }, + { + "include": "#single-one-regexp-lookahead" + }, + { + "include": "#single-one-regexp-lookahead-negative" + }, + { + "include": "#single-one-regexp-lookbehind" + }, + { + "include": "#single-one-regexp-lookbehind-negative" + }, + { + "include": "#single-one-regexp-conditional" + }, + { + "include": "#single-one-regexp-parentheses-non-capturing" + }, + { + "include": "#single-one-regexp-parentheses" + } + ] + }, + "single-one-regexp-character-set": { + "patterns": [ + { + "match": "(?x)\n \\[ \\^? \\] (?! .*?\\])\n" + }, + { + "name": "meta.character.set.regexp", + "begin": "(\\[)(\\^)?(\\])?", + "end": "(\\]|(?=\\'))|((?=(?)\n", + "end": "(\\)|(?=\\'))|((?=(?)\n", + "end": "(\\)|(?=\\'\\'\\'))", + "beginCaptures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp" + }, + "2": { + "name": "entity.name.tag.named.group.regexp" + } + }, + "endCaptures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#single-three-regexp-expression" + }, + { + "include": "#comments-string-single-three" + } + ] + }, + "single-three-regexp-comments": { + "name": "comment.regexp", + "begin": "\\(\\?#", + "end": "(\\)|(?=\\'\\'\\'))", + "beginCaptures": { + "0": { + "name": "punctuation.comment.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "punctuation.comment.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#codetags" + } + ] + }, + "single-three-regexp-lookahead": { + "begin": "(\\()\\?=", + "end": "(\\)|(?=\\'\\'\\'))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookahead.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookahead.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#single-three-regexp-expression" + }, + { + "include": "#comments-string-single-three" + } + ] + }, + "single-three-regexp-lookahead-negative": { + "begin": "(\\()\\?!", + "end": "(\\)|(?=\\'\\'\\'))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookahead.negative.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookahead.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookahead.negative.regexp punctuation.parenthesis.lookahead.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#single-three-regexp-expression" + }, + { + "include": "#comments-string-single-three" + } + ] + }, + "single-three-regexp-lookbehind": { + "begin": "(\\()\\?<=", + "end": "(\\)|(?=\\'\\'\\'))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookbehind.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookbehind.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookbehind.regexp punctuation.parenthesis.lookbehind.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#single-three-regexp-expression" + }, + { + "include": "#comments-string-single-three" + } + ] + }, + "single-three-regexp-lookbehind-negative": { + "begin": "(\\()\\?)\n", + "end": "(\\)|(?=\"))|((?=(?)\n", + "end": "(\\)|(?=\"\"\"))", + "beginCaptures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp" + }, + "2": { + "name": "entity.name.tag.named.group.regexp" + } + }, + "endCaptures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#double-three-regexp-expression" + }, + { + "include": "#comments-string-double-three" + } + ] + }, + "double-three-regexp-comments": { + "name": "comment.regexp", + "begin": "\\(\\?#", + "end": "(\\)|(?=\"\"\"))", + "beginCaptures": { + "0": { + "name": "punctuation.comment.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "punctuation.comment.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#codetags" + } + ] + }, + "double-three-regexp-lookahead": { + "begin": "(\\()\\?=", + "end": "(\\)|(?=\"\"\"))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookahead.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookahead.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#double-three-regexp-expression" + }, + { + "include": "#comments-string-double-three" + } + ] + }, + "double-three-regexp-lookahead-negative": { + "begin": "(\\()\\?!", + "end": "(\\)|(?=\"\"\"))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookahead.negative.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookahead.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookahead.negative.regexp punctuation.parenthesis.lookahead.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#double-three-regexp-expression" + }, + { + "include": "#comments-string-double-three" + } + ] + }, + "double-three-regexp-lookbehind": { + "begin": "(\\()\\?<=", + "end": "(\\)|(?=\"\"\"))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookbehind.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookbehind.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookbehind.regexp punctuation.parenthesis.lookbehind.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#double-three-regexp-expression" + }, + { + "include": "#comments-string-double-three" + } + ] + }, + "double-three-regexp-lookbehind-negative": { + "begin": "(\\()\\?)\n", + "end": "(\\)|(?=\\'))|((?=(?)\n", + "end": "(\\)|(?=\\'\\'\\'))", + "beginCaptures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp" + }, + "2": { + "name": "entity.name.tag.named.group.regexp" + } + }, + "endCaptures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#single-three-fregexp-expression" + }, + { + "include": "#comments-string-single-three" + } + ] + }, + "single-three-fregexp-lookahead": { + "begin": "(\\()\\?=", + "end": "(\\)|(?=\\'\\'\\'))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookahead.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookahead.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#single-three-fregexp-expression" + }, + { + "include": "#comments-string-single-three" + } + ] + }, + "single-three-fregexp-lookahead-negative": { + "begin": "(\\()\\?!", + "end": "(\\)|(?=\\'\\'\\'))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookahead.negative.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookahead.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookahead.negative.regexp punctuation.parenthesis.lookahead.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#single-three-fregexp-expression" + }, + { + "include": "#comments-string-single-three" + } + ] + }, + "single-three-fregexp-lookbehind": { + "begin": "(\\()\\?<=", + "end": "(\\)|(?=\\'\\'\\'))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookbehind.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookbehind.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookbehind.regexp punctuation.parenthesis.lookbehind.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#single-three-fregexp-expression" + }, + { + "include": "#comments-string-single-three" + } + ] + }, + "single-three-fregexp-lookbehind-negative": { + "begin": "(\\()\\?)\n", + "end": "(\\)|(?=\"))|((?=(?)\n", + "end": "(\\)|(?=\"\"\"))", + "beginCaptures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp" + }, + "2": { + "name": "entity.name.tag.named.group.regexp" + } + }, + "endCaptures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#double-three-fregexp-expression" + }, + { + "include": "#comments-string-double-three" + } + ] + }, + "double-three-fregexp-lookahead": { + "begin": "(\\()\\?=", + "end": "(\\)|(?=\"\"\"))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookahead.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookahead.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#double-three-fregexp-expression" + }, + { + "include": "#comments-string-double-three" + } + ] + }, + "double-three-fregexp-lookahead-negative": { + "begin": "(\\()\\?!", + "end": "(\\)|(?=\"\"\"))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookahead.negative.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookahead.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookahead.negative.regexp punctuation.parenthesis.lookahead.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#double-three-fregexp-expression" + }, + { + "include": "#comments-string-double-three" + } + ] + }, + "double-three-fregexp-lookbehind": { + "begin": "(\\()\\?<=", + "end": "(\\)|(?=\"\"\"))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookbehind.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookbehind.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookbehind.regexp punctuation.parenthesis.lookbehind.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#double-three-fregexp-expression" + }, + { + "include": "#comments-string-double-three" + } + ] + }, + "double-three-fregexp-lookbehind-negative": { + "begin": "(\\()\\?=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)? [bcdeEfFgGnosxX%]? )(?=})\n", + "captures": { + "1": { + "name": "storage.type.format.python" + }, + "2": { + "name": "storage.type.format.python" + } + } + }, + { + "include": "#fstring-terminator-single-tail" + } + ] + }, + "fstring-terminator-single-tail": { + "begin": "(![rsa])?(:)(?=.*?{)", + "end": "(?=})|(?=\\n)", + "beginCaptures": { + "1": { + "name": "storage.type.format.python" + }, + "2": { + "name": "storage.type.format.python" + } + }, + "patterns": [ + { + "include": "#fstring-illegal-single-brace" + }, + { + "include": "#fstring-single-brace" + }, + { + "name": "storage.type.format.python", + "match": "([bcdeEfFgGnosxX%])(?=})" + }, + { + "name": "storage.type.format.python", + "match": "(\\.\\d+)" + }, + { + "name": "storage.type.format.python", + "match": "(,)" + }, + { + "name": "storage.type.format.python", + "match": "(\\d+)" + }, + { + "name": "storage.type.format.python", + "match": "(\\#)" + }, + { + "name": "storage.type.format.python", + "match": "([-+ ])" + }, + { + "name": "storage.type.format.python", + "match": "([<>=^])" + }, + { + "name": "storage.type.format.python", + "match": "(\\w)" + } + ] + }, + "fstring-fnorm-quoted-multi-line": { + "name": "meta.fstring.python", + "begin": "(\\b[fF])([bBuU])?('''|\"\"\")", + "end": "(\\3)", + "beginCaptures": { + "1": { + "name": "string.interpolated.python string.quoted.multi.python storage.type.string.python" + }, + "2": { + "name": "invalid.illegal.prefix.python" + }, + "3": { + "name": "punctuation.definition.string.begin.python string.interpolated.python string.quoted.multi.python" + } + }, + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.python string.interpolated.python string.quoted.multi.python" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#fstring-guts" + }, + { + "include": "#fstring-illegal-multi-brace" + }, + { + "include": "#fstring-multi-brace" + }, + { + "include": "#fstring-multi-core" + } + ] + }, + "fstring-normf-quoted-multi-line": { + "name": "meta.fstring.python", + "begin": "(\\b[bBuU])([fF])('''|\"\"\")", + "end": "(\\3)", + "beginCaptures": { + "1": { + "name": "invalid.illegal.prefix.python" + }, + "2": { + "name": "string.interpolated.python string.quoted.multi.python storage.type.string.python" + }, + "3": { + "name": "punctuation.definition.string.begin.python string.quoted.multi.python" + } + }, + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.python string.interpolated.python string.quoted.multi.python" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#fstring-guts" + }, + { + "include": "#fstring-illegal-multi-brace" + }, + { + "include": "#fstring-multi-brace" + }, + { + "include": "#fstring-multi-core" + } + ] + }, + "fstring-raw-quoted-multi-line": { + "name": "meta.fstring.python", + "begin": "(\\b(?:[R][fF]|[fF][R]))('''|\"\"\")", + "end": "(\\2)", + "beginCaptures": { + "1": { + "name": "string.interpolated.python string.quoted.raw.multi.python storage.type.string.python" + }, + "2": { + "name": "punctuation.definition.string.begin.python string.quoted.raw.multi.python" + } + }, + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.python string.interpolated.python string.quoted.raw.multi.python" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#fstring-raw-guts" + }, + { + "include": "#fstring-illegal-multi-brace" + }, + { + "include": "#fstring-multi-brace" + }, + { + "include": "#fstring-raw-multi-core" + } + ] + }, + "fstring-multi-core": { + "name": "string.interpolated.python string.quoted.multi.python", + "match": "(?x)\n (.+?)\n (\n (?# .* and .*? in multi-line match need special handling of\n newlines otherwise SublimeText and Atom will match slightly\n differently.\n\n The guard for newlines has to be separate from the\n lookahead because of special $ matching rule.)\n ($\\n?)\n |\n (?=[\\\\\\}\\{]|'''|\"\"\")\n )\n (?# due to how multiline regexps are matched we need a special case\n for matching a newline character)\n | \\n\n" + }, + "fstring-raw-multi-core": { + "name": "string.interpolated.python string.quoted.raw.multi.python", + "match": "(?x)\n (.+?)\n (\n (?# .* and .*? in multi-line match need special handling of\n newlines otherwise SublimeText and Atom will match slightly\n differently.\n\n The guard for newlines has to be separate from the\n lookahead because of special $ matching rule.)\n ($\\n?)\n |\n (?=[\\\\\\}\\{]|'''|\"\"\")\n )\n (?# due to how multiline regexps are matched we need a special case\n for matching a newline character)\n | \\n\n" + }, + "fstring-multi-brace": { + "comment": "value interpolation using { ... }", + "begin": "(\\{)", + "end": "(?x)\n (\\})\n", + "beginCaptures": { + "1": { + "name": "constant.character.format.placeholder.other.python" + } + }, + "endCaptures": { + "1": { + "name": "constant.character.format.placeholder.other.python" + } + }, + "patterns": [ + { + "include": "#fstring-terminator-multi" + }, + { + "include": "#f-expression" + } + ] + }, + "fstring-terminator-multi": { + "patterns": [ + { + "name": "storage.type.format.python", + "match": "(![rsa])(?=})" + }, + { + "match": "(?x)\n (![rsa])?\n ( : \\w? [<>=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)? [bcdeEfFgGnosxX%]? )(?=})\n", + "captures": { + "1": { + "name": "storage.type.format.python" + }, + "2": { + "name": "storage.type.format.python" + } + } + }, + { + "include": "#fstring-terminator-multi-tail" + } + ] + }, + "fstring-terminator-multi-tail": { + "begin": "(![rsa])?(:)(?=.*?{)", + "end": "(?=})", + "beginCaptures": { + "1": { + "name": "storage.type.format.python" + }, + "2": { + "name": "storage.type.format.python" + } + }, + "patterns": [ + { + "include": "#fstring-illegal-multi-brace" + }, + { + "include": "#fstring-multi-brace" + }, + { + "name": "storage.type.format.python", + "match": "([bcdeEfFgGnosxX%])(?=})" + }, + { + "name": "storage.type.format.python", + "match": "(\\.\\d+)" + }, + { + "name": "storage.type.format.python", + "match": "(,)" + }, + { + "name": "storage.type.format.python", + "match": "(\\d+)" + }, + { + "name": "storage.type.format.python", + "match": "(\\#)" + }, + { + "name": "storage.type.format.python", + "match": "([-+ ])" + }, + { + "name": "storage.type.format.python", + "match": "([<>=^])" + }, + { + "name": "storage.type.format.python", + "match": "(\\w)" + } + ] + } + } +} \ No newline at end of file diff --git a/extensions/python/syntaxes/MagicRegExp.tmLanguage.json b/extensions/python/syntaxes/MagicRegExp.tmLanguage.json new file mode 100644 index 0000000000..0de066f367 --- /dev/null +++ b/extensions/python/syntaxes/MagicRegExp.tmLanguage.json @@ -0,0 +1,501 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/MagicStack/MagicPython/blob/master/grammars/MagicRegExp.tmLanguage", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/MagicStack/MagicPython/commit/361a4964a559481330764a447e7bab88d4f1b01b", + "name": "MagicRegExp", + "scopeName": "source.regexp.python", + "fileTypes": [ + "re" + ], + "uuid": "39e15186-71e6-11e5-b82c-7c6d62900c7c", + "patterns": [ + { + "include": "#regexp-expression" + } + ], + "repository": { + "regexp-base-expression": { + "patterns": [ + { + "include": "#regexp-quantifier" + }, + { + "include": "#regexp-base-common" + } + ] + }, + "fregexp-base-expression": { + "patterns": [ + { + "include": "#fregexp-quantifier" + }, + { + "include": "#fstring-formatting-braces" + }, + { + "match": "\\{.*?\\}" + }, + { + "include": "#regexp-base-common" + } + ] + }, + "fstring-formatting-braces": { + "patterns": [ + { + "comment": "empty braces are illegal", + "match": "({)(\\s*?)(})", + "captures": { + "1": { + "name": "constant.character.format.placeholder.other.python" + }, + "2": { + "name": "invalid.illegal.brace.python" + }, + "3": { + "name": "constant.character.format.placeholder.other.python" + } + } + }, + { + "name": "constant.character.escape.python", + "match": "({{|}})" + } + ] + }, + "regexp-base-common": { + "patterns": [ + { + "name": "support.other.match.any.regexp", + "match": "\\." + }, + { + "name": "support.other.match.begin.regexp", + "match": "\\^" + }, + { + "name": "support.other.match.end.regexp", + "match": "\\$" + }, + { + "name": "keyword.operator.quantifier.regexp", + "match": "[+*?]\\??" + }, + { + "name": "keyword.operator.disjunction.regexp", + "match": "\\|" + }, + { + "include": "#regexp-escape-sequence" + } + ] + }, + "regexp-quantifier": { + "name": "keyword.operator.quantifier.regexp", + "match": "(?x)\n \\{(\n \\d+ | \\d+,(\\d+)? | ,\\d+\n )\\}\n" + }, + "fregexp-quantifier": { + "name": "keyword.operator.quantifier.regexp", + "match": "(?x)\n \\{\\{(\n \\d+ | \\d+,(\\d+)? | ,\\d+\n )\\}\\}\n" + }, + "regexp-backreference-number": { + "name": "meta.backreference.regexp", + "match": "(\\\\[1-9]\\d?)", + "captures": { + "1": { + "name": "entity.name.tag.backreference.regexp" + } + } + }, + "regexp-backreference": { + "name": "meta.backreference.named.regexp", + "match": "(?x)\n (\\() (\\?P= \\w+(?:\\s+[[:alnum:]]+)?) (\\))\n", + "captures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.backreference.named.begin.regexp" + }, + "2": { + "name": "entity.name.tag.named.backreference.regexp" + }, + "3": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.backreference.named.end.regexp" + } + } + }, + "regexp-flags": { + "name": "storage.modifier.flag.regexp", + "match": "\\(\\?[aiLmsux]+\\)" + }, + "regexp-escape-special": { + "name": "support.other.escape.special.regexp", + "match": "\\\\([AbBdDsSwWZ])" + }, + "regexp-escape-character": { + "name": "constant.character.escape.regexp", + "match": "(?x)\n \\\\ (\n x[0-9A-Fa-f]{2}\n | 0[0-7]{1,2}\n | [0-7]{3}\n )\n" + }, + "regexp-escape-unicode": { + "name": "constant.character.unicode.regexp", + "match": "(?x)\n \\\\ (\n u[0-9A-Fa-f]{4}\n | U[0-9A-Fa-f]{8}\n )\n" + }, + "regexp-escape-catchall": { + "name": "constant.character.escape.regexp", + "match": "\\\\(.|\\n)" + }, + "regexp-escape-sequence": { + "patterns": [ + { + "include": "#regexp-escape-special" + }, + { + "include": "#regexp-escape-character" + }, + { + "include": "#regexp-escape-unicode" + }, + { + "include": "#regexp-backreference-number" + }, + { + "include": "#regexp-escape-catchall" + } + ] + }, + "regexp-charecter-set-escapes": { + "patterns": [ + { + "name": "constant.character.escape.regexp", + "match": "\\\\[abfnrtv\\\\]" + }, + { + "include": "#regexp-escape-special" + }, + { + "name": "constant.character.escape.regexp", + "match": "\\\\([0-7]{1,3})" + }, + { + "include": "#regexp-escape-character" + }, + { + "include": "#regexp-escape-unicode" + }, + { + "include": "#regexp-escape-catchall" + } + ] + }, + "codetags": { + "match": "(?:\\b(NOTE|XXX|HACK|FIXME|BUG|TODO)\\b)", + "captures": { + "1": { + "name": "keyword.codetag.notation.python" + } + } + }, + "regexp-expression": { + "patterns": [ + { + "include": "#regexp-base-expression" + }, + { + "include": "#regexp-character-set" + }, + { + "include": "#regexp-comments" + }, + { + "include": "#regexp-flags" + }, + { + "include": "#regexp-named-group" + }, + { + "include": "#regexp-backreference" + }, + { + "include": "#regexp-lookahead" + }, + { + "include": "#regexp-lookahead-negative" + }, + { + "include": "#regexp-lookbehind" + }, + { + "include": "#regexp-lookbehind-negative" + }, + { + "include": "#regexp-conditional" + }, + { + "include": "#regexp-parentheses-non-capturing" + }, + { + "include": "#regexp-parentheses" + } + ] + }, + "regexp-character-set": { + "patterns": [ + { + "match": "(?x)\n \\[ \\^? \\] (?! .*?\\])\n" + }, + { + "name": "meta.character.set.regexp", + "begin": "(\\[)(\\^)?(\\])?", + "end": "(\\])", + "beginCaptures": { + "1": { + "name": "punctuation.character.set.begin.regexp constant.other.set.regexp" + }, + "2": { + "name": "keyword.operator.negation.regexp" + }, + "3": { + "name": "constant.character.set.regexp" + } + }, + "endCaptures": { + "1": { + "name": "punctuation.character.set.end.regexp constant.other.set.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#regexp-charecter-set-escapes" + }, + { + "name": "constant.character.set.regexp", + "match": "[^\\n]" + } + ] + } + ] + }, + "regexp-named-group": { + "name": "meta.named.regexp", + "begin": "(?x)\n (\\() (\\?P <\\w+(?:\\s+[[:alnum:]]+)?>)\n", + "end": "(\\))", + "beginCaptures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp" + }, + "2": { + "name": "entity.name.tag.named.group.regexp" + } + }, + "endCaptures": { + "1": { + "name": "support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#regexp-expression" + } + ] + }, + "regexp-comments": { + "name": "comment.regexp", + "begin": "\\(\\?#", + "end": "(\\))", + "beginCaptures": { + "0": { + "name": "punctuation.comment.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "punctuation.comment.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#codetags" + } + ] + }, + "regexp-lookahead": { + "begin": "(\\()\\?=", + "end": "(\\))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookahead.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookahead.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#regexp-expression" + } + ] + }, + "regexp-lookahead-negative": { + "begin": "(\\()\\?!", + "end": "(\\))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookahead.negative.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookahead.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookahead.negative.regexp punctuation.parenthesis.lookahead.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#regexp-expression" + } + ] + }, + "regexp-lookbehind": { + "begin": "(\\()\\?<=", + "end": "(\\))", + "beginCaptures": { + "0": { + "name": "keyword.operator.lookbehind.regexp" + }, + "1": { + "name": "punctuation.parenthesis.lookbehind.begin.regexp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.operator.lookbehind.regexp punctuation.parenthesis.lookbehind.end.regexp" + }, + "2": { + "name": "invalid.illegal.newline.python" + } + }, + "patterns": [ + { + "include": "#regexp-expression" + } + ] + }, + "regexp-lookbehind-negative": { + "begin": "(\\()\\?>> for a in foo(2, b=1, + ... c=3): + ... print(a) + 0 + 1 +''' diff --git a/extensions/python/test/colorize-results/test_py.json b/extensions/python/test/colorize-results/test_py.json new file mode 100644 index 0000000000..b456114b1e --- /dev/null +++ b/extensions/python/test/colorize-results/test_py.json @@ -0,0 +1,6778 @@ +[ + { + "c": "from", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " banana ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "import", + "t": "source.python keyword.control.import.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "class", + "t": "source.python meta.class.python storage.type.class.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.class.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Monkey", + "t": "source.python meta.class.python entity.name.type.class.python", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.python meta.class.python punctuation.section.class.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Bananas the monkey can eat.", + "t": "source.python comment.line.number-sign.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\tcapacity ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "10", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "\t", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "def", + "t": "source.python meta.function.python storage.type.function.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "eat", + "t": "source.python meta.function.python entity.name.function.python", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python variable.parameter.function.language.special.self.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.separator.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "N", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python meta.function.python punctuation.section.function.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'''", + "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Make the monkey eat N bananas!", + "t": "source.python string.quoted.docstring.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'''", + "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\t\tcapacity ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " capacity ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " N", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "banana", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "size", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "def", + "t": "source.python meta.function.python storage.type.function.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "feeding_frenzy", + "t": "source.python meta.function.python entity.name.function.python", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python variable.parameter.function.language.special.self.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python meta.function.python punctuation.section.function.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "eat", + "t": "source.python meta.function-call.python meta.function-call.generic.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "9.25", + "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.float.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "return", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Yum yum", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\t", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "def", + "t": "source.python meta.function.python storage.type.function.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "some_func", + "t": "source.python meta.function.python entity.name.function.python", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "a", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ":", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.separator.annotation.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t\t\t\t", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "lambda", + "t": "source.python meta.function.python meta.function.parameters.python meta.lambda-function.python storage.type.function.lambda.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python meta.lambda-function.python meta.function.lambda.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "x", + "t": "source.python meta.function.python meta.function.parameters.python meta.lambda-function.python meta.function.lambda.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "=", + "t": "source.python meta.function.python meta.function.parameters.python meta.lambda-function.python meta.function.lambda.parameters.python keyword.operator.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "None", + "t": "source.python meta.function.python meta.function.parameters.python meta.lambda-function.python meta.function.lambda.parameters.python constant.language.python", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ":", + "t": "source.python meta.function.python meta.function.parameters.python meta.lambda-function.python punctuation.section.function.lambda.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t\t\t\t", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.dict.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "key", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.separator.dict.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " val", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t\t\t\t\t", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "for", + "t": "source.python meta.function.python meta.function.parameters.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " key", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " val ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "in", + "t": "source.python meta.function.python meta.function.parameters.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": "\t\t\t\t\t\t\t", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.parenthesis.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "x ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "if", + "t": "source.python meta.function.python meta.function.parameters.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " x ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "is", + "t": "source.python meta.function.python meta.function.parameters.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "not", + "t": "source.python meta.function.python meta.function.parameters.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "None", + "t": "source.python meta.function.python meta.function.parameters.python constant.language.python", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "else", + "t": "source.python meta.function.python meta.function.parameters.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.list.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.list.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.parenthesis.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t\t\t\t", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.dict.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python meta.function.python meta.function.parameters.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "42", + "t": "source.python meta.function.python meta.function.parameters.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ")", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python meta.function.python punctuation.section.function.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pass", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "if", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1900", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " year ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "2100", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "and", + "t": "source.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<=", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " month ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<=", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "12", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\\", + "t": "source.python punctuation.separator.continuation.line.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "and", + "t": "source.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<=", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " day ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<=", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "31", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "and", + "t": "source.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "0", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<=", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " hour ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "24", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\\", + "t": "source.python punctuation.separator.continuation.line.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "and", + "t": "source.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "0", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<=", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " minute ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "60", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "and", + "t": "source.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "0", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<=", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " second ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "60", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.colon.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Looks like a valid date", + "t": "source.python comment.line.number-sign.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "return", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "def", + "t": "source.python meta.function.python storage.type.function.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "firstn", + "t": "source.python meta.function.python entity.name.function.python", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "g", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.separator.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "n", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python meta.function.python punctuation.section.function.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "for", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " i ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "in", + "t": "source.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "range", + "t": "source.python meta.function-call.python support.function.builtin.python", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "n", + "t": "source.python meta.function-call.python meta.function-call.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.colon.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t\t", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "yield", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " g", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "next", + "t": "source.python meta.function-call.python meta.function-call.generic.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "reduce", + "t": "source.python meta.function-call.python variable.legacy.builtin.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "lambda", + "t": "source.python meta.function-call.python meta.function-call.arguments.python meta.lambda-function.python storage.type.function.lambda.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function-call.python meta.function-call.arguments.python meta.lambda-function.python meta.function.lambda.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "x", + "t": "source.python meta.function-call.python meta.function-call.arguments.python meta.lambda-function.python meta.function.lambda.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.python meta.function-call.python meta.function-call.arguments.python meta.lambda-function.python meta.function.lambda.parameters.python punctuation.separator.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "y", + "t": "source.python meta.function-call.python meta.function-call.arguments.python meta.lambda-function.python meta.function.lambda.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ":", + "t": "source.python meta.function-call.python meta.function-call.arguments.python meta.lambda-function.python punctuation.section.function.lambda.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " x", + "t": "source.python meta.function-call.python meta.function-call.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "+", + "t": "source.python meta.function-call.python meta.function-call.arguments.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "y", + "t": "source.python meta.function-call.python meta.function-call.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.python meta.function-call.python meta.function-call.arguments.python punctuation.separator.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python meta.function-call.python meta.function-call.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.python meta.function-call.python meta.function-call.arguments.python punctuation.definition.list.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "47", + "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.python meta.function-call.python meta.function-call.arguments.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "11", + "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.python meta.function-call.python meta.function-call.arguments.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "42", + "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.python meta.function-call.python meta.function-call.arguments.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "13", + "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "]", + "t": "source.python meta.function-call.python meta.function-call.arguments.python punctuation.definition.list.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "woerter ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.python punctuation.definition.dict.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "house", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.dict.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Haus", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "cat", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.dict.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Katze", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "black", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.dict.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "schwarz", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "}", + "t": "source.python punctuation.definition.dict.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "mydictionary ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.python punctuation.definition.dict.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "foo", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.dict.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "23", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "comment", + "t": "source.python comment.line.number-sign.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "bar", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.dict.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "hello", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "sqadsad", + "t": "source.python comment.line.number-sign.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "}", + "t": "source.python punctuation.definition.dict.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "def", + "t": "source.python meta.function.python storage.type.function.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "steuern", + "t": "source.python meta.function.python entity.name.function.python", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "einkommen", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python meta.function.python punctuation.section.function.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"\"\"", + "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Berechnung der zu zahlenden Steuern fuer ein zu versteuerndes Einkommen von x", + "t": "source.python string.quoted.docstring.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"\"\"", + "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "if", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " einkommen ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<=", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "8004", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.colon.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " steuer ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "0", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "elif", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " einkommen ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<=", + "t": "source.python keyword.operator.comparison.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "13469", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.colon.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " y ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.python punctuation.parenthesis.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "einkommen ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "8004.0", + "t": "source.python constant.numeric.float.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ")", + "t": "source.python punctuation.parenthesis.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/", + "t": "source.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "10000.0", + "t": "source.python constant.numeric.float.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " steuer ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.python punctuation.parenthesis.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "912.17", + "t": "source.python constant.numeric.float.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " y ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "+", + "t": "source.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1400", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ")", + "t": "source.python punctuation.parenthesis.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "y", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "else", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.colon.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " steuer ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " einkommen ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "0.44", + "t": "source.python constant.numeric.float.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.python keyword.operator.arithmetic.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "15694", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "return", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " steuer", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "def", + "t": "source.python meta.function.python storage.type.function.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "beliebig", + "t": "source.python meta.function.python entity.name.function.python", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "x", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.separator.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "y", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.separator.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.python meta.function.python meta.function.parameters.python keyword.operator.unpacking.parameter.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "mehr", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python meta.function.python punctuation.section.function.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "print", + "t": "source.python support.function.builtin.python", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "x=", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " x", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ", x=", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " y", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "print", + "t": "source.python support.function.builtin.python", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "mehr: ", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " mehr", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "class", + "t": "source.python meta.class.python storage.type.class.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.class.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Memoize", + "t": "source.python meta.class.python entity.name.type.class.python", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.python meta.class.python punctuation.section.class.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "def", + "t": "source.python meta.function.python storage.type.function.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "__init__", + "t": "source.python meta.function.python support.function.magic.python", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python variable.parameter.function.language.special.self.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.separator.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fn", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python meta.function.python punctuation.section.function.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.python variable.language.special.self.python", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fn ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " fn", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.python variable.language.special.self.python", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "memo ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.python punctuation.definition.dict.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.python punctuation.definition.dict.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "def", + "t": "source.python meta.function.python storage.type.function.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.python meta.function.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "__call__", + "t": "source.python meta.function.python support.function.magic.python", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python variable.parameter.function.language.special.self.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.separator.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python meta.function.python meta.function.parameters.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.python meta.function.python meta.function.parameters.python keyword.operator.unpacking.parameter.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "args", + "t": "source.python meta.function.python meta.function.parameters.python variable.parameter.function.language.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.python meta.function.python meta.function.parameters.python punctuation.definition.parameters.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python meta.function.python punctuation.section.function.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "if", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " args ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "not", + "t": "source.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "in", + "t": "source.python keyword.operator.logical.python", + "r": { + "dark_plus": "keyword.operator.logical.python: #569CD6", + "light_plus": "keyword.operator.logical.python: #0000FF", + "dark_vs": "keyword.operator.logical.python: #569CD6", + "light_vs": "keyword.operator.logical.python: #0000FF", + "hc_black": "keyword.operator.logical.python: #569CD6" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.python variable.language.special.self.python", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "memo", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.colon.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.python variable.language.special.self.python", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "memo", + "t": "source.python meta.item-access.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.python meta.item-access.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "args", + "t": "source.python meta.item-access.python meta.item-access.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.python meta.item-access.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.python variable.language.special.self.python", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fn", + "t": "source.python meta.function-call.python meta.function-call.generic.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.python meta.function-call.python keyword.operator.unpacking.arguments.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "args", + "t": "source.python meta.function-call.python meta.function-call.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "return", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.python variable.language.special.self.python", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "memo", + "t": "source.python meta.item-access.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.python meta.item-access.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "args", + "t": "source.python meta.item-access.python meta.item-access.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.python meta.item-access.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "res ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " re", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "search", + "t": "source.python meta.function-call.python meta.function-call.generic.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "r", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python storage.type.string.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "\"", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "[", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.begin.regexp constant.other.set.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "0-9-", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp constant.character.set.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "]", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.end.regexp constant.other.set.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "*", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.end.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "\\s", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.escape.special.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "*", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "[", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.begin.regexp constant.other.set.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "A-Za-z", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp constant.character.set.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "]", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.end.regexp constant.other.set.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "+", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.end.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": ",", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "\\s", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.escape.special.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "+", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": ".", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.match.any.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "*", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.end.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "\"", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": ",", + "t": "source.python meta.function-call.python meta.function-call.arguments.python punctuation.separator.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " i", + "t": "source.python meta.function-call.python meta.function-call.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "while", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "True", + "t": "source.python constant.language.python", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.colon.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "try", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.colon.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " n ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "raw_input", + "t": "source.python meta.function-call.python variable.legacy.builtin.python", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Number: ", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " n ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "int", + "t": "source.python meta.function-call.python support.type.python", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "n", + "t": "source.python meta.function-call.python meta.function-call.arguments.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "break", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "except", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "ValueError", + "t": "source.python support.type.exception.python", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.colon.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "print", + "t": "source.python meta.function-call.python support.function.builtin.python", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.python meta.function-call.python punctuation.definition.arguments.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Not a number", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python meta.function-call.python meta.function-call.arguments.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.python meta.function-call.python punctuation.definition.arguments.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "async", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "with", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "EXPR", + "t": "source.python constant.other.caps.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "as", + "t": "source.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "VAR", + "t": "source.python constant.other.caps.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.colon.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "BLOCK", + "t": "source.python constant.other.caps.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Comments in dictionary items should be colorized accordingly", + "t": "source.python comment.line.number-sign.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "my_dictionary ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.python punctuation.definition.dict.begin.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "foo", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.dict.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "23", + "t": "source.python constant.numeric.dec.python", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.python punctuation.separator.element.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " this should be colorized as comment", + "t": "source.python comment.line.number-sign.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "bar", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ":", + "t": "source.python punctuation.separator.dict.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "foobar", + "t": "source.python string.quoted.single.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.python string.quoted.single.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "this should be colorized as comment", + "t": "source.python comment.line.number-sign.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "}", + "t": "source.python punctuation.definition.dict.end.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " test raw strings", + "t": "source.python comment.line.number-sign.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "text ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "r", + "t": "source.python string.regexp.quoted.multi.python storage.type.string.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "\"\"\"", + "t": "source.python string.regexp.quoted.multi.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "interval ``", + "t": "source.python string.regexp.quoted.multi.python", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "[", + "t": "source.python string.regexp.quoted.multi.python meta.character.set.regexp punctuation.character.set.begin.regexp constant.other.set.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "1,2)`` leads to", + "t": "source.python string.regexp.quoted.multi.python meta.character.set.regexp constant.character.set.regexp", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "\"\"\"", + "t": "source.python string.regexp.quoted.multi.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "highlight_error ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.python keyword.operator.assignment.python", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "True", + "t": "source.python constant.language.python", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": "#", + "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " highlight doctests", + "t": "source.python comment.line.number-sign.python", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "r", + "t": "source.python string.quoted.docstring.raw.multi.python storage.type.string.python", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "'''", + "t": "source.python string.quoted.docstring.raw.multi.python punctuation.definition.string.begin.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Module docstring", + "t": "source.python string.quoted.docstring.raw.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " Some text followed by code sample:", + "t": "source.python string.quoted.docstring.raw.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.python string.quoted.docstring.raw.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">>> ", + "t": "source.python string.quoted.docstring.raw.multi.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "for a in foo(2, b=1,", + "t": "source.python string.quoted.docstring.raw.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.python string.quoted.docstring.raw.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "... ", + "t": "source.python string.quoted.docstring.raw.multi.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " c=3):", + "t": "source.python string.quoted.docstring.raw.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.python string.quoted.docstring.raw.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "... ", + "t": "source.python string.quoted.docstring.raw.multi.python keyword.control.flow.python", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " print(a)", + "t": "source.python string.quoted.docstring.raw.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " 0", + "t": "source.python string.quoted.docstring.raw.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " 1", + "t": "source.python string.quoted.docstring.raw.multi.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'''", + "t": "source.python string.quoted.docstring.raw.multi.python punctuation.definition.string.end.python", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + } +] \ No newline at end of file diff --git a/extensions/python/tsconfig.json b/extensions/python/tsconfig.json new file mode 100644 index 0000000000..a2b5bcdfdd --- /dev/null +++ b/extensions/python/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "es2015" + ], + "module": "commonjs", + "outDir": "./out" + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/r/.vscodeignore b/extensions/r/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/r/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/r/OSSREADME.json b/extensions/r/OSSREADME.json new file mode 100644 index 0000000000..9cf3b6c42f --- /dev/null +++ b/extensions/r/OSSREADME.json @@ -0,0 +1,17 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[{ + "name": "textmate/r.tmbundle", + "version": "0.0.0", + "license": "TextMate Bundle License", + "repositoryURL": "https://github.com/textmate/r.tmbundle", + "licenseDetail": [ + "Copyright (c) textmate-r.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this folder fall under the following license: ", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose." + ] +}] diff --git a/extensions/r/language-configuration.json b/extensions/r/language-configuration.json new file mode 100644 index 0000000000..6293d4d61a --- /dev/null +++ b/extensions/r/language-configuration.json @@ -0,0 +1,22 @@ +{ + "comments": { + "lineComment": "#" + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""] + ] +} \ No newline at end of file diff --git a/extensions/r/package.json b/extensions/r/package.json new file mode 100644 index 0000000000..e1104e2344 --- /dev/null +++ b/extensions/r/package.json @@ -0,0 +1,22 @@ +{ + "name": "r", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "scripts": { + "update-grammar": "node ../../build/npm/update-grammar.js textmate/r.tmbundle Syntaxes/R.plist ./syntaxes/r.tmLanguage.json" + }, + "contributes": { + "languages": [{ + "id": "r", + "extensions": [ ".r", ".rhistory", ".rprofile", ".rt" ], + "aliases": [ "R", "r" ], + "configuration": "./language-configuration.json" + }], + "grammars": [{ + "language": "r", + "scopeName": "source.r", + "path": "./syntaxes/r.tmLanguage.json" + }] + } +} diff --git a/extensions/r/syntaxes/R.plist b/extensions/r/syntaxes/R.plist new file mode 100644 index 0000000000..58022aa142 --- /dev/null +++ b/extensions/r/syntaxes/R.plist @@ -0,0 +1,316 @@ + + + + + fileTypes + + R + r + s + S + Rprofile + + keyEquivalent + ^~R + name + R + patterns + + + captures + + 1 + + name + comment.line.pragma.r + + 2 + + name + entity.name.pragma.name.r + + + match + ^(#pragma[ \t]+mark)[ \t](.*) + name + comment.line.pragma-mark.r + + + begin + (^[ \t]+)?(?=#) + beginCaptures + + 1 + + name + punctuation.whitespace.comment.leading.r + + + end + (?!\G) + patterns + + + begin + # + beginCaptures + + 0 + + name + punctuation.definition.comment.r + + + end + \n + name + comment.line.number-sign.r + + + + + match + \b(logical|numeric|character|complex|matrix|array|data\.frame|list|factor)(?=\s*\() + name + storage.type.r + + + match + \b(function|if|break|next|repeat|else|for|return|switch|while|in|invisible)\b + name + keyword.control.r + + + match + \b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)(i|L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\b + name + constant.numeric.r + + + match + \b(T|F|TRUE|FALSE|NULL|NA|Inf|NaN)\b + name + constant.language.r + + + match + \b(pi|letters|LETTERS|month\.abb|month\.name)\b + name + support.constant.misc.r + + + match + (\-|\+|\*|\/|%\/%|%%|%\*%|%in%|%o%|%x%|\^) + name + keyword.operator.arithmetic.r + + + match + (=|<-|<<-|->|->>) + name + keyword.operator.assignment.r + + + match + (==|!=|<>|<|>|<=|>=) + name + keyword.operator.comparison.r + + + match + (!|&{1,2}|[|]{1,2}) + name + keyword.operator.logical.r + + + match + (\.\.\.|\$|@|:|\~) + name + keyword.other.r + + + begin + " + beginCaptures + + 0 + + name + punctuation.definition.string.begin.r + + + end + " + endCaptures + + 0 + + name + punctuation.definition.string.end.r + + + name + string.quoted.double.r + patterns + + + match + \\. + name + constant.character.escape.r + + + + + begin + ' + beginCaptures + + 0 + + name + punctuation.definition.string.begin.r + + + end + ' + endCaptures + + 0 + + name + punctuation.definition.string.end.r + + + name + string.quoted.single.r + patterns + + + match + \\. + name + constant.character.escape.r + + + + + captures + + 1 + + name + entity.name.function.r + + 2 + + name + keyword.operator.assignment.r + + 3 + + name + keyword.control.r + + + match + ([[:alpha:].][[:alnum:]._]*)\s*(<-)\s*(function) + name + meta.function.r + + + captures + + 1 + + name + entity.name.tag.r + + 4 + + name + entity.name.type.r + + + match + (setMethod|setReplaceMethod|setGeneric|setGroupGeneric|setClass)\s*\(\s*([[:alpha:]\d]+\s*=\s*)?("|\x{27})([a-zA-Z._\[\$@][a-zA-Z0-9._\[]*?)\3.* + name + meta.method.declaration.r + + + match + ([[:alpha:].][[:alnum:]._]*)\s*\( + + + captures + + 1 + + name + variable.parameter.r + + 2 + + name + keyword.operator.assignment.r + + + match + ([[:alpha:].][[:alnum:]._]*)\s*(=)(?=[^=]) + + + match + \b([\d_][[:alnum:]._]+)\b + name + invalid.illegal.variable.other.r + + + match + \b([[:alnum:]_]+)(?=::) + name + entity.namespace.r + + + match + \b([[:alnum:]._]+)\b + name + variable.other.r + + + begin + \{ + beginCaptures + + 0 + + name + punctuation.section.block.begin.r + + + end + \} + endCaptures + + 0 + + name + punctuation.section.block.end.r + + + name + meta.block.r + patterns + + + include + source.r + + + + + scopeName + source.r + uuid + B2E6B78D-6E70-11D9-A369-000D93B3A10E + + \ No newline at end of file diff --git a/extensions/r/syntaxes/r.tmLanguage.json b/extensions/r/syntaxes/r.tmLanguage.json new file mode 100644 index 0000000000..0ba828e6eb --- /dev/null +++ b/extensions/r/syntaxes/r.tmLanguage.json @@ -0,0 +1,209 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/textmate/r.tmbundle/blob/master/Syntaxes/R.plist", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/textmate/r.tmbundle/commit/6b04ff3424f3f1cdfe64a9cfb71d8765959be250", + "fileTypes": [ + "R", + "r", + "s", + "S", + "Rprofile" + ], + "keyEquivalent": "^~R", + "name": "R", + "patterns": [ + { + "captures": { + "1": { + "name": "comment.line.pragma.r" + }, + "2": { + "name": "entity.name.pragma.name.r" + } + }, + "match": "^(#pragma[ \\t]+mark)[ \\t](.*)", + "name": "comment.line.pragma-mark.r" + }, + { + "begin": "(^[ \\t]+)?(?=#)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.r" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "#", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.r" + } + }, + "end": "\\n", + "name": "comment.line.number-sign.r" + } + ] + }, + { + "match": "\\b(logical|numeric|character|complex|matrix|array|data\\.frame|list|factor)(?=\\s*\\()", + "name": "storage.type.r" + }, + { + "match": "\\b(function|if|break|next|repeat|else|for|return|switch|while|in|invisible)\\b", + "name": "keyword.control.r" + }, + { + "match": "\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)(i|L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b", + "name": "constant.numeric.r" + }, + { + "match": "\\b(T|F|TRUE|FALSE|NULL|NA|Inf|NaN)\\b", + "name": "constant.language.r" + }, + { + "match": "\\b(pi|letters|LETTERS|month\\.abb|month\\.name)\\b", + "name": "support.constant.misc.r" + }, + { + "match": "(\\-|\\+|\\*|\\/|%\\/%|%%|%\\*%|%in%|%o%|%x%|\\^)", + "name": "keyword.operator.arithmetic.r" + }, + { + "match": "(=|<-|<<-|->|->>)", + "name": "keyword.operator.assignment.r" + }, + { + "match": "(==|!=|<>|<|>|<=|>=)", + "name": "keyword.operator.comparison.r" + }, + { + "match": "(!|&{1,2}|[|]{1,2})", + "name": "keyword.operator.logical.r" + }, + { + "match": "(\\.\\.\\.|\\$|@|:|\\~)", + "name": "keyword.other.r" + }, + { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.r" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.r" + } + }, + "name": "string.quoted.double.r", + "patterns": [ + { + "match": "\\\\.", + "name": "constant.character.escape.r" + } + ] + }, + { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.r" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.r" + } + }, + "name": "string.quoted.single.r", + "patterns": [ + { + "match": "\\\\.", + "name": "constant.character.escape.r" + } + ] + }, + { + "captures": { + "1": { + "name": "entity.name.function.r" + }, + "2": { + "name": "keyword.operator.assignment.r" + }, + "3": { + "name": "keyword.control.r" + } + }, + "match": "([[:alpha:].][[:alnum:]._]*)\\s*(<-)\\s*(function)", + "name": "meta.function.r" + }, + { + "captures": { + "1": { + "name": "entity.name.tag.r" + }, + "4": { + "name": "entity.name.type.r" + } + }, + "match": "(setMethod|setReplaceMethod|setGeneric|setGroupGeneric|setClass)\\s*\\(\\s*([[:alpha:]\\d]+\\s*=\\s*)?(\"|\\x{27})([a-zA-Z._\\[\\$@][a-zA-Z0-9._\\[]*?)\\3.*", + "name": "meta.method.declaration.r" + }, + { + "match": "([[:alpha:].][[:alnum:]._]*)\\s*\\(" + }, + { + "captures": { + "1": { + "name": "variable.parameter.r" + }, + "2": { + "name": "keyword.operator.assignment.r" + } + }, + "match": "([[:alpha:].][[:alnum:]._]*)\\s*(=)(?=[^=])" + }, + { + "match": "\\b([\\d_][[:alnum:]._]+)\\b", + "name": "invalid.illegal.variable.other.r" + }, + { + "match": "\\b([[:alnum:]_]+)(?=::)", + "name": "entity.namespace.r" + }, + { + "match": "\\b([[:alnum:]._]+)\\b", + "name": "variable.other.r" + }, + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.r" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.r" + } + }, + "name": "meta.block.r", + "patterns": [ + { + "include": "source.r" + } + ] + } + ], + "scopeName": "source.r", + "uuid": "B2E6B78D-6E70-11D9-A369-000D93B3A10E" +} \ No newline at end of file diff --git a/extensions/r/test/colorize-fixtures/test.r b/extensions/r/test/colorize-fixtures/test.r new file mode 100644 index 0000000000..8262188bc7 --- /dev/null +++ b/extensions/r/test/colorize-fixtures/test.r @@ -0,0 +1,24 @@ +# © Microsoft. All rights reserved. + +#' Add together two numbers. +#' +#' @param x A number. +#' @param y A number. +#' @return The sum of \code{x} and \code{y}. +#' @examples +#' add(1, 1) +#' add(10, 1) +add <- function(x, y) { + x + y +} + +add(1, -2, 2.0) +add(1.0e10, 2.0e10) + +paste("one", NULL) +paste(NA, 'two') + +paste("multi- + line", + 'multi- + line') diff --git a/extensions/r/test/colorize-results/test_r.json b/extensions/r/test/colorize-results/test_r.json new file mode 100644 index 0000000000..545fbd4b31 --- /dev/null +++ b/extensions/r/test/colorize-results/test_r.json @@ -0,0 +1,827 @@ +[ + { + "c": "#", + "t": "source.r comment.line.number-sign.r punctuation.definition.comment.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " © Microsoft. All rights reserved.", + "t": "source.r comment.line.number-sign.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.r comment.line.number-sign.r punctuation.definition.comment.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "' Add together two numbers.", + "t": "source.r comment.line.number-sign.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.r comment.line.number-sign.r punctuation.definition.comment.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "'", + "t": "source.r comment.line.number-sign.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.r comment.line.number-sign.r punctuation.definition.comment.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "' @param x A number.", + "t": "source.r comment.line.number-sign.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.r comment.line.number-sign.r punctuation.definition.comment.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "' @param y A number.", + "t": "source.r comment.line.number-sign.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.r comment.line.number-sign.r punctuation.definition.comment.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "' @return The sum of \\code{x} and \\code{y}.", + "t": "source.r comment.line.number-sign.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.r comment.line.number-sign.r punctuation.definition.comment.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "' @examples", + "t": "source.r comment.line.number-sign.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.r comment.line.number-sign.r punctuation.definition.comment.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "' add(1, 1)", + "t": "source.r comment.line.number-sign.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.r comment.line.number-sign.r punctuation.definition.comment.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "' add(10, 1)", + "t": "source.r comment.line.number-sign.r", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "add", + "t": "source.r meta.function.r entity.name.function.r", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.r meta.function.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<-", + "t": "source.r meta.function.r keyword.operator.assignment.r", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.r meta.function.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "function", + "t": "source.r meta.function.r keyword.control.r", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "(", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "x", + "t": "source.r variable.other.r", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ", ", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "y", + "t": "source.r variable.other.r", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ") ", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.r meta.block.r punctuation.section.block.begin.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.r meta.block.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "x", + "t": "source.r meta.block.r variable.other.r", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.r meta.block.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "+", + "t": "source.r meta.block.r keyword.operator.arithmetic.r", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.r meta.block.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "y", + "t": "source.r meta.block.r variable.other.r", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "}", + "t": "source.r meta.block.r punctuation.section.block.end.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "add(", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.r constant.numeric.r", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ", ", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.r keyword.operator.arithmetic.r", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "2", + "t": "source.r constant.numeric.r", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ", ", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "2.0", + "t": "source.r constant.numeric.r", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ")", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "add(", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1.0e10", + "t": "source.r constant.numeric.r", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ", ", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "2.0e10", + "t": "source.r constant.numeric.r", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ")", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "paste(", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.r string.quoted.double.r punctuation.definition.string.begin.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "one", + "t": "source.r string.quoted.double.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.r string.quoted.double.r punctuation.definition.string.end.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ", ", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NULL", + "t": "source.r constant.language.r", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ")", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "paste(", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NA", + "t": "source.r constant.language.r", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ", ", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.r string.quoted.single.r punctuation.definition.string.begin.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "two", + "t": "source.r string.quoted.single.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.r string.quoted.single.r punctuation.definition.string.end.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "paste(", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.r string.quoted.double.r punctuation.definition.string.begin.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "multi-", + "t": "source.r string.quoted.double.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " line", + "t": "source.r string.quoted.double.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.r string.quoted.double.r punctuation.definition.string.end.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.r string.quoted.single.r punctuation.definition.string.begin.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "multi-", + "t": "source.r string.quoted.single.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " line", + "t": "source.r string.quoted.single.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.r string.quoted.single.r punctuation.definition.string.end.r", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.r", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + } +] \ No newline at end of file diff --git a/extensions/ruby/.vscodeignore b/extensions/ruby/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/ruby/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/ruby/OSSREADME.json b/extensions/ruby/OSSREADME.json new file mode 100644 index 0000000000..b4af78a035 --- /dev/null +++ b/extensions/ruby/OSSREADME.json @@ -0,0 +1,22 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[{ + "name": "textmate/ruby.tmbundle", + "version": "0.0.0", + "license": "TextMate Bundle License", + "repositoryURL": "https://github.com/textmate/ruby.tmbundle", + "licenseDetail": [ + "Copyright (c) textmate-ruby.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this folder fall under the following license: ", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information, ", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ] +}] diff --git a/extensions/ruby/language-configuration.json b/extensions/ruby/language-configuration.json new file mode 100644 index 0000000000..fe8a5e9e2a --- /dev/null +++ b/extensions/ruby/language-configuration.json @@ -0,0 +1,29 @@ +{ + "comments": { + "lineComment": "#", + "blockComment": [ "=begin", "=end" ] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "indentationRules": { + "increaseIndentPattern": "^\\s*((begin|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while)|(.*\\sdo\\b))\\b[^\\{;]*$", + "decreaseIndentPattern": "^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(end|rescue|ensure|else|elsif|when)\\b)" + } +} \ No newline at end of file diff --git a/extensions/ruby/package.json b/extensions/ruby/package.json new file mode 100644 index 0000000000..43f0d35aae --- /dev/null +++ b/extensions/ruby/package.json @@ -0,0 +1,24 @@ +{ + "name": "ruby", + "version": "0.2.1", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "scripts": { + "update-grammar": "node ../../build/npm/update-grammar.js textmate/ruby.tmbundle Syntaxes/Ruby.plist ./syntaxes/ruby.tmLanguage.json" + }, + "contributes": { + "languages": [{ + "id": "ruby", + "extensions": [ ".rb", ".rbx", ".rjs", ".gemspec", ".rake", ".ru", ".erb" ], + "filenames": [ "rakefile", "gemfile", "guardfile", "podfile", "capfile" ], + "aliases": [ "Ruby", "rb" ], + "firstLine": "^#!/.*\\bruby\\b", + "configuration": "./language-configuration.json" + }], + "grammars": [{ + "language": "ruby", + "scopeName": "source.ruby", + "path": "./syntaxes/ruby.tmLanguage.json" + }] + } +} diff --git a/extensions/ruby/syntaxes/ruby.tmLanguage.json b/extensions/ruby/syntaxes/ruby.tmLanguage.json new file mode 100644 index 0000000000..4e11b70013 --- /dev/null +++ b/extensions/ruby/syntaxes/ruby.tmLanguage.json @@ -0,0 +1,2814 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/textmate/ruby.tmbundle/blob/master/Syntaxes/Ruby.plist", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/textmate/ruby.tmbundle/commit/74713556df10fbc7b1f9e99013ab1e34cd836f56", + "comment": "\n\tTODO: unresolved issues\n\n\ttext:\n\t\"p <]?\\d+|m)?|\\sex)\n (?=:(?=\\s*set?\\s[^\\n:]+:)|:(?!\\s*set?\\s))\n (?:(?:\\s|\\s*:\\s*)\\w*(?:\\s*=(?:[^\\n\\\\\\s]|\\\\.)*)?)*\n [\\s:]\n (?:filetype|ft|syntax)\\s*=\n ruby\n (?=\\s|:|$)\n)", + "keyEquivalent": "^~R", + "name": "Ruby", + "patterns": [ + { + "captures": { + "1": { + "name": "keyword.control.class.ruby" + }, + "2": { + "name": "entity.name.type.class.ruby" + }, + "3": { + "name": "keyword.operator.other.ruby" + }, + "4": { + "name": "entity.other.inherited-class.ruby" + }, + "5": { + "name": "keyword.operator.other.ruby" + }, + "6": { + "name": "variable.other.object.ruby" + } + }, + "match": "^\\s*(class)\\s+(?:([.a-zA-Z0-9_:]+)(?:\\s*(<)\\s*([.a-zA-Z0-9_:]+))?|(<<)\\s*([.a-zA-Z0-9_:]+))", + "name": "meta.class.ruby" + }, + { + "captures": { + "1": { + "name": "keyword.control.module.ruby" + }, + "2": { + "name": "entity.name.type.module.ruby" + }, + "3": { + "name": "entity.other.inherited-class.module.first.ruby" + }, + "4": { + "name": "punctuation.separator.inheritance.ruby" + }, + "5": { + "name": "entity.other.inherited-class.module.second.ruby" + }, + "6": { + "name": "punctuation.separator.inheritance.ruby" + }, + "7": { + "name": "entity.other.inherited-class.module.third.ruby" + }, + "8": { + "name": "punctuation.separator.inheritance.ruby" + } + }, + "match": "^\\s*(module)\\s+(([A-Z]\\w*(::))?([A-Z]\\w*(::))?([A-Z]\\w*(::))*[A-Z]\\w*)", + "name": "meta.module.ruby" + }, + { + "comment": "else if is a common mistake carried over from other languages. it works if you put in a second end, but it’s never what you want.", + "match": "(?[a-zA-Z_]\\w*(?>[?!])?)(:)(?!:)", + "name": "constant.other.symbol.hashkey.ruby" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.constant.ruby" + } + }, + "comment": "symbols as hash key (1.8 syntax)", + "match": "(?[a-zA-Z_]\\w*(?>[?!])?)(?=\\s*=>)", + "name": "constant.other.symbol.hashkey.ruby" + }, + { + "comment": "everything being a reserved word, not a value and needing a 'end' is a..", + "match": "(?|_|\\*|\\$|\\?|:|\"|-[0adFiIlpvw])", + "name": "variable.other.readwrite.global.pre-defined.ruby" + }, + { + "begin": "\\b(ENV)\\[", + "beginCaptures": { + "1": { + "name": "variable.other.constant.ruby" + } + }, + "end": "\\]", + "name": "meta.environment-variable.ruby", + "patterns": [ + { + "include": "$self" + } + ] + }, + { + "match": "\\b[A-Z]\\w*(?=((\\.|::)[A-Za-z]|\\[))", + "name": "support.class.ruby" + }, + { + "match": "\\b(abort|at_exit|autoload[?]?|binding|callcc|caller|caller_locations|chomp|chop|eval|exec|exit|exit!|fork|format|gets|global_variables|gsub|lambda|load|local_variables|open|p|print|printf|proc|putc|puts|rand|readline|readlines|select|set_trace_func|sleep|spawn|sprintf|srand|sub|syscall|system|test|trace_var|trap|untrace_var|warn)(\\b|(?<=[?!]))(?![?!])", + "name": "support.function.kernel.ruby" + }, + { + "match": "\\b[_A-Z]\\w*\\b", + "name": "variable.other.constant.ruby" + }, + { + "begin": "(?x)\n\t\t\t (?=def\\b) # an optimization to help Oniguruma fail fast\n\t\t\t (?<=^|\\s)(def)\\s+ # the def keyword\n\t\t\t ( (?>[a-zA-Z_]\\w*(?>\\.|::))? # a method name prefix\n\t\t\t (?>[a-zA-Z_]\\w*(?>[?!]|=(?!>))? # the method name\n\t\t\t |===?|!=|!~|>[>=]?|<=>|<[<=]?|[%&`/\\|^]|\\*\\*?|=?~|[-+]@?|\\[\\]=?) ) # …or an operator method\n\t\t\t \\s*(\\() # the openning parenthesis for arguments\n\t\t\t ", + "beginCaptures": { + "1": { + "name": "keyword.control.def.ruby" + }, + "2": { + "name": "entity.name.function.ruby" + }, + "3": { + "name": "punctuation.definition.parameters.ruby" + } + }, + "comment": "the method pattern comes from the symbol pattern, see there for a explaination", + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.parameters.ruby" + } + }, + "name": "meta.function.method.with-arguments.ruby", + "patterns": [ + { + "begin": "(?=[&*_a-zA-Z])", + "end": "(?=[,)])", + "patterns": [ + { + "captures": { + "1": { + "name": "storage.type.variable.ruby" + }, + "2": { + "name": "constant.other.symbol.hashkey.parameter.function.ruby" + }, + "3": { + "name": "punctuation.definition.constant.ruby" + }, + "4": { + "name": "variable.parameter.function.ruby" + } + }, + "match": "\\G([&*]?)(?:([_a-zA-Z]\\w*(:))|([_a-zA-Z]\\w*))" + }, + { + "include": "#parens" + }, + { + "include": "#braces" + }, + { + "include": "$self" + } + ] + } + ], + "repository": { + "braces": { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.section.function.begin.ruby" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.section.function.end.ruby" + } + }, + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#braces" + }, + { + "include": "$self" + } + ] + }, + "parens": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.function.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.function.end.ruby" + } + }, + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#braces" + }, + { + "include": "$self" + } + ] + } + } + }, + { + "begin": "(?x)\n\t\t\t (?=def\\b) # an optimization to help Oniguruma fail fast\n\t\t\t (?<=^|\\s)(def)\\s+ # the def keyword\n\t\t\t ( (?>[a-zA-Z_]\\w*(?>\\.|::))? # a method name prefix\n\t\t\t (?>[a-zA-Z_]\\w*(?>[?!]|=(?!>))? # the method name\n\t\t\t |===?|!=|!~|>[>=]?|<=>|<[<=]?|[%&`/\\|^]|\\*\\*?|=?~|[-+]@?|\\[\\]=?) ) # …or an operator method\n\t\t\t [ \\t] # the space separating the arguments\n\t\t\t (?=[ \\t]*[^\\s#;]) # make sure arguments and not a comment follow\n\t\t\t ", + "beginCaptures": { + "1": { + "name": "keyword.control.def.ruby" + }, + "2": { + "name": "entity.name.function.ruby" + } + }, + "comment": "same as the previous rule, but without parentheses around the arguments", + "end": "$", + "name": "meta.function.method.with-arguments.ruby", + "patterns": [ + { + "begin": "(?![\\s,])", + "end": "(?=,|$)", + "patterns": [ + { + "captures": { + "1": { + "name": "storage.type.variable.ruby" + }, + "2": { + "name": "constant.other.symbol.hashkey.parameter.function.ruby" + }, + "3": { + "name": "punctuation.definition.constant.ruby" + }, + "4": { + "name": "variable.parameter.function.ruby" + } + }, + "match": "\\G([&*]?)(?:([_a-zA-Z]\\w*(:))|([_a-zA-Z]\\w*))", + "name": "variable.parameter.function.ruby" + }, + { + "include": "$self" + } + ] + } + ] + }, + { + "captures": { + "1": { + "name": "keyword.control.def.ruby" + }, + "3": { + "name": "entity.name.function.ruby" + } + }, + "comment": " the optional name is just to catch the def also without a method-name", + "match": "(?x)\n\t\t\t (?=def\\b) # an optimization to help Oniguruma fail fast\n\t\t\t (?<=^|\\s)(def)\\b # the def keyword\n\t\t\t ( \\s+ # an optional group of whitespace followed by…\n\t\t\t ( (?>[a-zA-Z_]\\w*(?>\\.|::))? # a method name prefix\n\t\t\t (?>[a-zA-Z_]\\w*(?>[?!]|=(?!>))? # the method name\n\t\t\t |===?|!=|!~|>[>=]?|<=>|<[<=]?|[%&`/\\|^]|\\*\\*?|=?~|[-+]@?|\\[\\]=?) ) )? # …or an operator method\n\t\t\t ", + "name": "meta.function.method.without-arguments.ruby" + }, + { + "match": "\\b\\d(?>_?\\d)*(?=\\.\\d|[eE])(\\.\\d(?>_?\\d)*)?([eE][-+]?\\d(?>_?\\d)*)?r?i?\\b", + "name": "constant.numeric.float.ruby" + }, + { + "match": "\\b(0|(0[dD]\\d|[1-9])(?>_?\\d)*)r?i?\\b", + "name": "constant.numeric.integer.ruby" + }, + { + "match": "\\b0[xX]\\h(?>_?\\h)*r?i?\\b", + "name": "constant.numeric.hex.ruby" + }, + { + "match": "\\b0[bB][01](?>_?[01])*r?i?\\b", + "name": "constant.numeric.binary.ruby" + }, + { + "match": "\\b0([oO]?[0-7](?>_?[0-7])*)?r?i?\\b", + "name": "constant.numeric.octal.ruby" + }, + { + "begin": ":'", + "captures": { + "0": { + "name": "punctuation.definition.constant.ruby" + } + }, + "end": "'", + "name": "constant.other.symbol.single-quoted.ruby", + "patterns": [ + { + "match": "\\\\['\\\\]", + "name": "constant.character.escape.ruby" + } + ] + }, + { + "begin": ":\"", + "captures": { + "0": { + "name": "punctuation.definition.constant.ruby" + } + }, + "end": "\"", + "name": "constant.other.symbol.double-quoted.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "comment": "Needs higher precidence than regular expressions.", + "match": "(?~(?:\\[,|&;]\n\t\t\t | [\\s;]if\\s\t\t\t# keywords\n\t\t\t | [\\s;]elsif\\s\n\t\t\t | [\\s;]while\\s\n\t\t\t | [\\s;]unless\\s\n\t\t\t | [\\s;]when\\s\n\t\t\t | [\\s;]assert_match\\s\n\t\t\t | [\\s;]or\\s\t\t\t# boolean opperators\n\t\t\t | [\\s;]and\\s\n\t\t\t | [\\s;]not\\s\n\t\t\t | [\\s.]index\\s\t\t\t# methods\n\t\t\t | [\\s.]scan\\s\n\t\t\t | [\\s.]sub\\s\n\t\t\t | [\\s.]sub!\\s\n\t\t\t | [\\s.]gsub\\s\n\t\t\t | [\\s.]gsub!\\s\n\t\t\t | [\\s.]match\\s\n\t\t\t )\n\t\t\t | (?<= # or a look-behind with line anchor:\n\t\t\t ^when\\s # duplication necessary due to limits of regex\n\t\t\t | ^if\\s\n\t\t\t | ^elsif\\s\n\t\t\t | ^while\\s\n\t\t\t | ^unless\\s\n\t\t\t )\n\t\t\t )\n\t\t\t \\s*((/))(?![*+{}?])\n\t\t\t", + "captures": { + "1": { + "name": "string.regexp.classic.ruby" + }, + "2": { + "name": "punctuation.definition.string.ruby" + } + }, + "comment": "regular expressions (normal)\n\t\t\twe only start a regexp if the character before it (excluding whitespace)\n\t\t\tis what we think is before a regexp\n\t\t\t", + "contentName": "string.regexp.classic.ruby", + "end": "((/[eimnosux]*))", + "patterns": [ + { + "include": "#regex_sub" + } + ] + }, + { + "captures": { + "1": { + "name": "punctuation.definition.constant.ruby" + } + }, + "comment": "symbols", + "match": "(?[a-zA-Z_]\\w*(?>[?!]|=(?![>=]))?|===?|>[>=]?|<=>|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[\\]=?|(@@?|\\$)[a-zA-Z_]\\w*)", + "name": "constant.other.symbol.ruby" + }, + { + "begin": "^=begin", + "captures": { + "0": { + "name": "punctuation.definition.comment.ruby" + } + }, + "comment": "multiline comments", + "end": "^=end", + "name": "comment.block.documentation.ruby" + }, + { + "begin": "(^[ \\t]+)?(?=#)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.ruby" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "#", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.ruby" + } + }, + "end": "\\n", + "name": "comment.line.number-sign.ruby" + } + ] + }, + { + "comment": "\n\t\t\tmatches questionmark-letters.\n\n\t\t\texamples (1st alternation = hex):\n\t\t\t?\\x1 ?\\x61\n\n\t\t\texamples (2nd alternation = octal):\n\t\t\t?\\0 ?\\07 ?\\017\n\n\t\t\texamples (3rd alternation = escaped):\n\t\t\t?\\n ?\\b\n\n\t\t\texamples (4th alternation = meta-ctrl):\n\t\t\t?\\C-a ?\\M-a ?\\C-\\M-\\C-\\M-a\n\n\t\t\texamples (4th alternation = normal):\n\t\t\t?a ?A ?0 \n\t\t\t?* ?\" ?( \n\t\t\t?. ?#\n\t\t\t\n\t\t\t\n\t\t\tthe negative lookbehind prevents against matching\n\t\t\tp(42.tainted?)\n\t\t\t", + "match": "(?<<[-~](\"?)((?:[_\\w]+_|)HTML)\\b\\1))", + "comment": "Heredoc with embedded html", + "end": "(?!\\G)", + "name": "meta.embedded.block.html", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)HTML)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "text.html", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "text.html.basic" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)XML)\\b\\1))", + "comment": "Heredoc with embedded xml", + "end": "(?!\\G)", + "name": "meta.embedded.block.xml", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)XML)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "text.xml", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "text.xml" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)SQL)\\b\\1))", + "comment": "Heredoc with embedded sql", + "end": "(?!\\G)", + "name": "meta.embedded.block.sql", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)SQL)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.sql", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.sql" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)CSS)\\b\\1))", + "comment": "Heredoc with embedded css", + "end": "(?!\\G)", + "name": "meta.embedded.block.css", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)CSS)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.css", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.css" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)CPP)\\b\\1))", + "comment": "Heredoc with embedded c++", + "end": "(?!\\G)", + "name": "meta.embedded.block.c++", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)CPP)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.c++", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.c++" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)C)\\b\\1))", + "comment": "Heredoc with embedded c", + "end": "(?!\\G)", + "name": "meta.embedded.block.c", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)C)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.c", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.c" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)(?:JS|JAVASCRIPT))\\b\\1))", + "comment": "Heredoc with embedded javascript", + "end": "(?!\\G)", + "name": "meta.embedded.block.js", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)(?:JS|JAVASCRIPT))\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.js", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.js" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)JQUERY)\\b\\1))", + "comment": "Heredoc with embedded jQuery javascript", + "end": "(?!\\G)", + "name": "meta.embedded.block.js.jquery", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)JQUERY)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.js.jquery", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.js.jquery" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)(?:SH|SHELL))\\b\\1))", + "comment": "Heredoc with embedded shell", + "end": "(?!\\G)", + "name": "meta.embedded.block.shell", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)(?:SH|SHELL))\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.shell", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.shell" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)LUA)\\b\\1))", + "comment": "Heredoc with embedded lua", + "end": "(?!\\G)", + "name": "meta.embedded.block.lua", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)LUA)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.lua", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.lua" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)RUBY)\\b\\1))", + "comment": "Heredoc with embedded ruby", + "end": "(?!\\G)", + "name": "meta.embedded.block.ruby", + "patterns": [ + { + "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)RUBY)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.ruby", + "end": "\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.ruby" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?>=\\s*<<(\\w+))", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "^\\1$", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "(?><<[-~](\\w+))", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "comment": "heredoc with indented terminator", + "end": "\\s*\\1$", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "(?<=\\{|do|\\{\\s|do\\s)(\\|)", + "captures": { + "1": { + "name": "punctuation.separator.arguments.ruby" + } + }, + "end": "(?", + "name": "punctuation.separator.key-value" + }, + { + "match": "->", + "name": "support.function.kernel.lambda.ruby" + }, + { + "match": "<<=|%=|&{1,2}=|\\*=|\\*\\*=|\\+=|-=|\\^=|\\|{1,2}=|<<", + "name": "keyword.operator.assignment.augmented.ruby" + }, + { + "match": "<=>|<(?!<|=)|>(?!<|=|>)|<=|>=|===|==|=~|!=|!~|(?<=[ \\t])\\?", + "name": "keyword.operator.comparison.ruby" + }, + { + "match": "(?>", + "name": "keyword.operator.other.ruby" + }, + { + "match": ";", + "name": "punctuation.separator.statement.ruby" + }, + { + "match": ",", + "name": "punctuation.separator.object.ruby" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.namespace.ruby" + } + }, + "comment": "Mark as namespace separator if double colons followed by capital letter", + "match": "(::)\\s*(?=[A-Z])" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.method.ruby" + } + }, + "comment": "Mark as method separator if double colons not followed by capital letter", + "match": "(\\.|::)\\s*(?![A-Z])" + }, + { + "comment": "Must come after method and constant separators to prefer double colons", + "match": ":", + "name": "punctuation.separator.other.ruby" + }, + { + "match": "\\{", + "name": "punctuation.section.scope.begin.ruby" + }, + { + "match": "\\}", + "name": "punctuation.section.scope.end.ruby" + }, + { + "match": "\\[", + "name": "punctuation.section.array.begin.ruby" + }, + { + "match": "\\]", + "name": "punctuation.section.array.end.ruby" + }, + { + "match": "\\(|\\)", + "name": "punctuation.section.function.ruby" + } + ], + "repository": { + "escaped_char": { + "match": "\\\\(?:[0-7]{1,3}|x[\\da-fA-F]{1,2}|.)", + "name": "constant.character.escape.ruby" + }, + "heredoc": { + "begin": "^<<[-~]?\\w+", + "end": "$", + "patterns": [ + { + "include": "$self" + } + ] + }, + "interpolated_ruby": { + "patterns": [ + { + "begin": "#\\{", + "beginCaptures": { + "0": { + "name": "punctuation.section.embedded.begin.ruby" + } + }, + "contentName": "source.ruby", + "end": "(\\})", + "endCaptures": { + "0": { + "name": "punctuation.section.embedded.end.ruby" + }, + "1": { + "name": "source.ruby" + } + }, + "name": "meta.embedded.line.ruby", + "patterns": [ + { + "include": "#nest_curly_and_self" + }, + { + "include": "$self" + } + ], + "repository": { + "nest_curly_and_self": { + "patterns": [ + { + "begin": "\\{", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "\\}", + "patterns": [ + { + "include": "#nest_curly_and_self" + } + ] + }, + { + "include": "$self" + } + ] + } + } + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.ruby" + } + }, + "match": "(#@)[a-zA-Z_]\\w*", + "name": "variable.other.readwrite.instance.ruby" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.ruby" + } + }, + "match": "(#@@)[a-zA-Z_]\\w*", + "name": "variable.other.readwrite.class.ruby" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.ruby" + } + }, + "match": "(#\\$)[a-zA-Z_]\\w*", + "name": "variable.other.readwrite.global.ruby" + } + ] + }, + "percent_literals": { + "patterns": [ + { + "begin": "%i(?:([(\\[{<])|([^\\w\\s]|_))", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "[)\\]}>]\\2|\\1\\2", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "meta.array.symbol.ruby", + "patterns": [ + { + "begin": "\\G(?<=\\()(?!\\))", + "end": "(?=\\))", + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#symbol" + } + ] + }, + { + "begin": "\\G(?<=\\[)(?!\\])", + "end": "(?=\\])", + "patterns": [ + { + "include": "#brackets" + }, + { + "include": "#symbol" + } + ] + }, + { + "begin": "\\G(?<=\\{)(?!\\})", + "end": "(?=\\})", + "patterns": [ + { + "include": "#braces" + }, + { + "include": "#symbol" + } + ] + }, + { + "begin": "\\G(?<=<)(?!>)", + "end": "(?=>)", + "patterns": [ + { + "include": "#angles" + }, + { + "include": "#symbol" + } + ] + }, + { + "include": "#symbol" + } + ], + "repository": { + "angles": { + "patterns": [ + { + "captures": { + "0": { + "name": "constant.character.escape.ruby" + } + }, + "match": "\\\\<|\\\\>", + "name": "constant.other.symbol.ruby" + }, + { + "begin": "<", + "captures": { + "0": { + "name": "constant.other.symbol.ruby" + } + }, + "end": ">", + "patterns": [ + { + "include": "#angles" + }, + { + "include": "#symbol" + } + ] + } + ] + }, + "braces": { + "patterns": [ + { + "captures": { + "0": { + "name": "constant.character.escape.ruby" + } + }, + "match": "\\\\\\{|\\\\\\}", + "name": "constant.other.symbol.ruby" + }, + { + "begin": "\\{", + "captures": { + "0": { + "name": "constant.other.symbol.ruby" + } + }, + "end": "\\}", + "patterns": [ + { + "include": "#braces" + }, + { + "include": "#symbol" + } + ] + } + ] + }, + "brackets": { + "patterns": [ + { + "captures": { + "0": { + "name": "constant.character.escape.ruby" + } + }, + "match": "\\\\\\[|\\\\\\]", + "name": "constant.other.symbol.ruby" + }, + { + "begin": "\\[", + "captures": { + "0": { + "name": "constant.other.symbol.ruby" + } + }, + "end": "\\]", + "patterns": [ + { + "include": "#brackets" + }, + { + "include": "#symbol" + } + ] + } + ] + }, + "parens": { + "patterns": [ + { + "captures": { + "0": { + "name": "constant.character.escape.ruby" + } + }, + "match": "\\\\\\(|\\\\\\)", + "name": "constant.other.symbol.ruby" + }, + { + "begin": "\\(", + "captures": { + "0": { + "name": "constant.other.symbol.ruby" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#symbol" + } + ] + } + ] + }, + "symbol": { + "patterns": [ + { + "captures": { + "0": { + "name": "constant.character.escape.ruby" + } + }, + "match": "\\\\\\\\|\\\\[ ]", + "name": "constant.other.symbol.ruby" + }, + { + "match": "\\S\\w*", + "name": "constant.other.symbol.ruby" + } + ] + } + } + }, + { + "begin": "%I(?:([(\\[{<])|([^\\w\\s]|_))", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "[)\\]}>]\\2|\\1\\2", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "meta.array.symbol.interpolated.ruby", + "patterns": [ + { + "begin": "\\G(?<=\\()(?!\\))", + "end": "(?=\\))", + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#symbol" + } + ] + }, + { + "begin": "\\G(?<=\\[)(?!\\])", + "end": "(?=\\])", + "patterns": [ + { + "include": "#brackets" + }, + { + "include": "#symbol" + } + ] + }, + { + "begin": "\\G(?<=\\{)(?!\\})", + "end": "(?=\\})", + "patterns": [ + { + "include": "#braces" + }, + { + "include": "#symbol" + } + ] + }, + { + "begin": "\\G(?<=<)(?!>)", + "end": "(?=>)", + "patterns": [ + { + "include": "#angles" + }, + { + "include": "#symbol" + } + ] + }, + { + "include": "#symbol" + } + ], + "repository": { + "angles": { + "patterns": [ + { + "begin": "<", + "captures": { + "0": { + "name": "constant.other.symbol.ruby" + } + }, + "end": ">", + "patterns": [ + { + "include": "#angles" + }, + { + "include": "#symbol" + } + ] + } + ] + }, + "braces": { + "patterns": [ + { + "begin": "\\{", + "captures": { + "0": { + "name": "constant.other.symbol.ruby" + } + }, + "end": "\\}", + "patterns": [ + { + "include": "#braces" + }, + { + "include": "#symbol" + } + ] + } + ] + }, + "brackets": { + "patterns": [ + { + "begin": "\\[", + "captures": { + "0": { + "name": "constant.other.symbol.ruby" + } + }, + "end": "\\]", + "patterns": [ + { + "include": "#brackets" + }, + { + "include": "#symbol" + } + ] + } + ] + }, + "parens": { + "patterns": [ + { + "begin": "\\(", + "captures": { + "0": { + "name": "constant.other.symbol.ruby" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#symbol" + } + ] + } + ] + }, + "symbol": { + "patterns": [ + { + "begin": "(?=\\\\|#\\{)", + "end": "(?!\\G)", + "name": "constant.other.symbol.ruby", + "patterns": [ + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + } + ] + }, + { + "match": "\\S\\w*", + "name": "constant.other.symbol.ruby" + } + ] + } + } + }, + { + "begin": "%q(?:([(\\[{<])|([^\\w\\s]|_))", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "[)\\]}>]\\2|\\1\\2", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "begin": "\\G(?<=\\()(?!\\))", + "end": "(?=\\))", + "patterns": [ + { + "include": "#parens" + } + ] + }, + { + "begin": "\\G(?<=\\[)(?!\\])", + "end": "(?=\\])", + "patterns": [ + { + "include": "#brackets" + } + ] + }, + { + "begin": "\\G(?<=\\{)(?!\\})", + "end": "(?=\\})", + "patterns": [ + { + "include": "#braces" + } + ] + }, + { + "begin": "\\G(?<=<)(?!>)", + "end": "(?=>)", + "patterns": [ + { + "include": "#angles" + } + ] + } + ], + "repository": { + "angles": { + "patterns": [ + { + "match": "\\\\<|\\\\>|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "begin": "<", + "end": ">", + "patterns": [ + { + "include": "#angles" + } + ] + } + ] + }, + "braces": { + "patterns": [ + { + "match": "\\\\\\{|\\\\\\}|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "begin": "\\{", + "end": "\\}", + "patterns": [ + { + "include": "#braces" + } + ] + } + ] + }, + "brackets": { + "patterns": [ + { + "match": "\\\\\\[|\\\\\\]|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "begin": "\\[", + "end": "\\]", + "patterns": [ + { + "include": "#brackets" + } + ] + } + ] + }, + "parens": { + "patterns": [ + { + "match": "\\\\\\(|\\\\\\)|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "begin": "\\(", + "end": "\\)", + "patterns": [ + { + "include": "#parens" + } + ] + } + ] + } + } + }, + { + "begin": "%Q?(?:([(\\[{<])|([^\\w\\s=]|_))", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "[)\\]}>]\\2|\\1\\2", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "begin": "\\G(?<=\\()(?!\\))", + "end": "(?=\\))", + "patterns": [ + { + "include": "#parens" + } + ] + }, + { + "begin": "\\G(?<=\\[)(?!\\])", + "end": "(?=\\])", + "patterns": [ + { + "include": "#brackets" + } + ] + }, + { + "begin": "\\G(?<=\\{)(?!\\})", + "end": "(?=\\})", + "patterns": [ + { + "include": "#braces" + } + ] + }, + { + "begin": "\\G(?<=<)(?!>)", + "end": "(?=>)", + "patterns": [ + { + "include": "#angles" + } + ] + }, + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + } + ], + "repository": { + "angles": { + "patterns": [ + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + }, + { + "begin": "<", + "end": ">", + "patterns": [ + { + "include": "#angles" + } + ] + } + ] + }, + "braces": { + "patterns": [ + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + }, + { + "begin": "\\{", + "end": "\\}", + "patterns": [ + { + "include": "#braces" + } + ] + } + ] + }, + "brackets": { + "patterns": [ + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + }, + { + "begin": "\\[", + "end": "\\]", + "patterns": [ + { + "include": "#brackets" + } + ] + } + ] + }, + "parens": { + "patterns": [ + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + }, + { + "begin": "\\(", + "end": "\\)", + "patterns": [ + { + "include": "#parens" + } + ] + } + ] + } + } + }, + { + "begin": "%r(?:([(\\[{<])|([^\\w\\s]|_))", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "([)\\]}>]\\2|\\1\\2)[eimnosux]*", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.regexp.percent.ruby", + "patterns": [ + { + "begin": "\\G(?<=\\()(?!\\))", + "end": "(?=\\))", + "patterns": [ + { + "include": "#parens" + } + ] + }, + { + "begin": "\\G(?<=\\[)(?!\\])", + "end": "(?=\\])", + "patterns": [ + { + "include": "#brackets" + } + ] + }, + { + "begin": "\\G(?<=\\{)(?!\\})", + "end": "(?=\\})", + "patterns": [ + { + "include": "#braces" + } + ] + }, + { + "begin": "\\G(?<=<)(?!>)", + "end": "(?=>)", + "patterns": [ + { + "include": "#angles" + } + ] + }, + { + "include": "#regex_sub" + } + ], + "repository": { + "angles": { + "patterns": [ + { + "include": "#regex_sub" + }, + { + "begin": "<", + "end": ">", + "patterns": [ + { + "include": "#angles" + } + ] + } + ] + }, + "braces": { + "patterns": [ + { + "include": "#regex_sub" + }, + { + "begin": "\\{", + "end": "\\}", + "patterns": [ + { + "include": "#braces" + } + ] + } + ] + }, + "brackets": { + "patterns": [ + { + "include": "#regex_sub" + }, + { + "begin": "\\[", + "end": "\\]", + "patterns": [ + { + "include": "#brackets" + } + ] + } + ] + }, + "parens": { + "patterns": [ + { + "include": "#regex_sub" + }, + { + "begin": "\\(", + "end": "\\)", + "patterns": [ + { + "include": "#parens" + } + ] + } + ] + } + } + }, + { + "begin": "%s(?:([(\\[{<])|([^\\w\\s]|_))", + "beginCaptures": { + "0": { + "name": "punctuation.definition.constant.begin.ruby" + } + }, + "end": "[)\\]}>]\\2|\\1\\2", + "endCaptures": { + "0": { + "name": "punctuation.definition.constant.end.ruby" + } + }, + "name": "constant.other.symbol.percent.ruby", + "patterns": [ + { + "begin": "\\G(?<=\\()(?!\\))", + "end": "(?=\\))", + "patterns": [ + { + "include": "#parens" + } + ] + }, + { + "begin": "\\G(?<=\\[)(?!\\])", + "end": "(?=\\])", + "patterns": [ + { + "include": "#brackets" + } + ] + }, + { + "begin": "\\G(?<=\\{)(?!\\})", + "end": "(?=\\})", + "patterns": [ + { + "include": "#braces" + } + ] + }, + { + "begin": "\\G(?<=<)(?!>)", + "end": "(?=>)", + "patterns": [ + { + "include": "#angles" + } + ] + } + ], + "repository": { + "angles": { + "patterns": [ + { + "match": "\\\\<|\\\\>|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "begin": "<", + "end": ">", + "patterns": [ + { + "include": "#angles" + } + ] + } + ] + }, + "braces": { + "patterns": [ + { + "match": "\\\\\\{|\\\\\\}|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "begin": "\\{", + "end": "\\}", + "patterns": [ + { + "include": "#braces" + } + ] + } + ] + }, + "brackets": { + "patterns": [ + { + "match": "\\\\\\[|\\\\\\]|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "begin": "\\[", + "end": "\\]", + "patterns": [ + { + "include": "#brackets" + } + ] + } + ] + }, + "parens": { + "patterns": [ + { + "match": "\\\\\\(|\\\\\\)|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "begin": "\\(", + "end": "\\)", + "patterns": [ + { + "include": "#parens" + } + ] + } + ] + } + } + }, + { + "begin": "%w(?:([(\\[{<])|([^\\w\\s]|_))", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "[)\\]}>]\\2|\\1\\2", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "meta.array.string.ruby", + "patterns": [ + { + "begin": "\\G(?<=\\()(?!\\))", + "end": "(?=\\))", + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#string" + } + ] + }, + { + "begin": "\\G(?<=\\[)(?!\\])", + "end": "(?=\\])", + "patterns": [ + { + "include": "#brackets" + }, + { + "include": "#string" + } + ] + }, + { + "begin": "\\G(?<=\\{)(?!\\})", + "end": "(?=\\})", + "patterns": [ + { + "include": "#braces" + }, + { + "include": "#string" + } + ] + }, + { + "begin": "\\G(?<=<)(?!>)", + "end": "(?=>)", + "patterns": [ + { + "include": "#angles" + }, + { + "include": "#string" + } + ] + }, + { + "include": "#string" + } + ], + "repository": { + "angles": { + "patterns": [ + { + "captures": { + "0": { + "name": "constant.character.escape.ruby" + } + }, + "match": "\\\\<|\\\\>", + "name": "string.other.ruby" + }, + { + "begin": "<", + "captures": { + "0": { + "name": "string.other.ruby" + } + }, + "end": ">", + "patterns": [ + { + "include": "#angles" + }, + { + "include": "#string" + } + ] + } + ] + }, + "braces": { + "patterns": [ + { + "captures": { + "0": { + "name": "constant.character.escape.ruby" + } + }, + "match": "\\\\\\{|\\\\\\}", + "name": "string.other.ruby" + }, + { + "begin": "\\{", + "captures": { + "0": { + "name": "string.other.ruby" + } + }, + "end": "\\}", + "patterns": [ + { + "include": "#braces" + }, + { + "include": "#string" + } + ] + } + ] + }, + "brackets": { + "patterns": [ + { + "captures": { + "0": { + "name": "constant.character.escape.ruby" + } + }, + "match": "\\\\\\[|\\\\\\]", + "name": "string.other.ruby" + }, + { + "begin": "\\[", + "captures": { + "0": { + "name": "string.other.ruby" + } + }, + "end": "\\]", + "patterns": [ + { + "include": "#brackets" + }, + { + "include": "#string" + } + ] + } + ] + }, + "parens": { + "patterns": [ + { + "captures": { + "0": { + "name": "constant.character.escape.ruby" + } + }, + "match": "\\\\\\(|\\\\\\)", + "name": "string.other.ruby" + }, + { + "begin": "\\(", + "captures": { + "0": { + "name": "string.other.ruby" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#string" + } + ] + } + ] + }, + "string": { + "patterns": [ + { + "captures": { + "0": { + "name": "constant.character.escape.ruby" + } + }, + "match": "\\\\\\\\|\\\\[ ]", + "name": "string.other.ruby" + }, + { + "match": "\\S\\w*", + "name": "string.other.ruby" + } + ] + } + } + }, + { + "begin": "%W(?:([(\\[{<])|([^\\w\\s]|_))", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "[)\\]}>]\\2|\\1\\2", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "meta.array.string.interpolated.ruby", + "patterns": [ + { + "begin": "\\G(?<=\\()(?!\\))", + "end": "(?=\\))", + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#string" + } + ] + }, + { + "begin": "\\G(?<=\\[)(?!\\])", + "end": "(?=\\])", + "patterns": [ + { + "include": "#brackets" + }, + { + "include": "#string" + } + ] + }, + { + "begin": "\\G(?<=\\{)(?!\\})", + "end": "(?=\\})", + "patterns": [ + { + "include": "#braces" + }, + { + "include": "#string" + } + ] + }, + { + "begin": "\\G(?<=<)(?!>)", + "end": "(?=>)", + "patterns": [ + { + "include": "#angles" + }, + { + "include": "#string" + } + ] + }, + { + "include": "#string" + } + ], + "repository": { + "angles": { + "patterns": [ + { + "begin": "<", + "captures": { + "0": { + "name": "string.other.ruby" + } + }, + "end": ">", + "patterns": [ + { + "include": "#angles" + }, + { + "include": "#string" + } + ] + } + ] + }, + "braces": { + "patterns": [ + { + "begin": "\\{", + "captures": { + "0": { + "name": "string.other.ruby" + } + }, + "end": "\\}", + "patterns": [ + { + "include": "#braces" + }, + { + "include": "#string" + } + ] + } + ] + }, + "brackets": { + "patterns": [ + { + "begin": "\\[", + "captures": { + "0": { + "name": "string.other.ruby" + } + }, + "end": "\\]", + "patterns": [ + { + "include": "#brackets" + }, + { + "include": "#string" + } + ] + } + ] + }, + "parens": { + "patterns": [ + { + "begin": "\\(", + "captures": { + "0": { + "name": "string.other.ruby" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#string" + } + ] + } + ] + }, + "string": { + "patterns": [ + { + "begin": "(?=\\\\|#\\{)", + "end": "(?!\\G)", + "name": "string.other.ruby", + "patterns": [ + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + } + ] + }, + { + "match": "\\S\\w*", + "name": "string.other.ruby" + } + ] + } + } + }, + { + "begin": "%x(?:([(\\[{<])|([^\\w\\s]|_))", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "[)\\]}>]\\2|\\1\\2", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.interpolated.percent.ruby", + "patterns": [ + { + "begin": "\\G(?<=\\()(?!\\))", + "end": "(?=\\))", + "patterns": [ + { + "include": "#parens" + } + ] + }, + { + "begin": "\\G(?<=\\[)(?!\\])", + "end": "(?=\\])", + "patterns": [ + { + "include": "#brackets" + } + ] + }, + { + "begin": "\\G(?<=\\{)(?!\\})", + "end": "(?=\\})", + "patterns": [ + { + "include": "#braces" + } + ] + }, + { + "begin": "\\G(?<=<)(?!>)", + "end": "(?=>)", + "patterns": [ + { + "include": "#angles" + } + ] + }, + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + } + ], + "repository": { + "angles": { + "patterns": [ + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + }, + { + "begin": "<", + "end": ">", + "patterns": [ + { + "include": "#angles" + } + ] + } + ] + }, + "braces": { + "patterns": [ + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + }, + { + "begin": "\\{", + "end": "\\}", + "patterns": [ + { + "include": "#braces" + } + ] + } + ] + }, + "brackets": { + "patterns": [ + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + }, + { + "begin": "\\[", + "end": "\\]", + "patterns": [ + { + "include": "#brackets" + } + ] + } + ] + }, + "parens": { + "patterns": [ + { + "include": "#escaped_char" + }, + { + "include": "#interpolated_ruby" + }, + { + "begin": "\\(", + "end": "\\)", + "patterns": [ + { + "include": "#parens" + } + ] + } + ] + } + } + } + ] + }, + "regex_sub": { + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.quantifier.begin.ruby" + }, + "3": { + "name": "punctuation.definition.quantifier.end.ruby" + } + }, + "match": "(\\{)\\d+(,\\d+)?(\\})", + "name": "keyword.operator.quantifier.ruby" + }, + { + "begin": "\\[\\^?", + "beginCaptures": { + "0": { + "name": "punctuation.definition.character-class.begin.ruby" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.definition.character-class.end.ruby" + } + }, + "name": "constant.other.character-class.set.ruby", + "patterns": [ + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "\\(\\?#", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.end.ruby" + } + }, + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "\\(", + "captures": { + "0": { + "name": "punctuation.definition.group.ruby" + } + }, + "end": "\\)", + "name": "meta.group.regexp.ruby", + "patterns": [ + { + "include": "#regex_sub" + } + ] + }, + { + "begin": "(?<=^|\\s)(#)\\s(?=[[a-zA-Z0-9,. \\t?!-][^\\x{00}-\\x{7F}]]*$)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.comment.ruby" + } + }, + "comment": "We are restrictive in what we allow to go after the comment character to avoid false positives, since the availability of comments depend on regexp flags.", + "end": "$\\n?", + "name": "comment.line.number-sign.ruby" + } + ] + } + }, + "scopeName": "source.ruby", + "uuid": "E00B62AC-6B1C-11D9-9B1F-000D93589AF6" +} \ No newline at end of file diff --git a/extensions/ruby/test/colorize-fixtures/test.rb b/extensions/ruby/test/colorize-fixtures/test.rb new file mode 100644 index 0000000000..9b81e2fd1c --- /dev/null +++ b/extensions/ruby/test/colorize-fixtures/test.rb @@ -0,0 +1,46 @@ +# encoding: utf-8 +# Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0 +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. + +module Azure::ARM::Scheduler + # + # A service client - single point of access to the REST API. + # + class SchedulerManagementClient < MsRestAzure::AzureServiceClient + include Azure::ARM::Scheduler::Models + include MsRestAzure + + # @return job_collections + attr_reader :job_collections + + # + # Creates initializes a new instance of the SchedulerManagementClient class. + # @param credentials [MsRest::ServiceClientCredentials] credentials to authorize HTTP requests made by the service client. + # @param base_url [String] the base URI of the service. + # @param options [Array] filters to be applied to the HTTP requests. + # + def initialize(credentials, base_url = nil, options = nil) + super(credentials, options) + @base_url = base_url || 'https://management.azure.com' + + fail ArgumentError, 'credentials is nil' if credentials.nil? + fail ArgumentError, 'invalid type of credentials input parameter' unless credentials.is_a?(MsRest::ServiceClientCredentials) + @credentials = credentials + + @job_collections = JobCollections.new(self) + @jobs = Jobs.new(self) + @api_version = '2016-01-01' + @long_running_operation_retry_timeout = 30 + @generate_client_request_id = true + if MacOS.version >= :mavericks + version = `#{MAVERICKS_PKG_PATH}/usr/bin/clang --version` + else + version = `/usr/bin/clang --version` + end + version = version[/clang-(\d+\.\d+\.\d+(\.\d+)?)/, 1] || "0" + version < latest_version + end + + end +end \ No newline at end of file diff --git a/extensions/ruby/test/colorize-results/test_rb.json b/extensions/ruby/test/colorize-results/test_rb.json new file mode 100644 index 0000000000..4c6e89c33f --- /dev/null +++ b/extensions/ruby/test/colorize-results/test_rb.json @@ -0,0 +1,2840 @@ +[ + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " encoding: utf-8", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Changes may cause incorrect behavior and will be lost if the code is", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " regenerated.", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "module", + "t": "source.ruby meta.module.ruby keyword.control.module.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.ruby meta.module.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Azure", + "t": "source.ruby meta.module.ruby entity.name.type.module.ruby entity.other.inherited-class.module.first.ruby", + "r": { + "dark_plus": "entity.other.inherited-class: #4EC9B0", + "light_plus": "entity.other.inherited-class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.other.inherited-class: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.ruby meta.module.ruby entity.name.type.module.ruby entity.other.inherited-class.module.first.ruby punctuation.separator.inheritance.ruby", + "r": { + "dark_plus": "entity.other.inherited-class: #4EC9B0", + "light_plus": "entity.other.inherited-class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.other.inherited-class: #4EC9B0" + } + }, + { + "c": "ARM", + "t": "source.ruby meta.module.ruby entity.name.type.module.ruby entity.other.inherited-class.module.second.ruby", + "r": { + "dark_plus": "entity.other.inherited-class: #4EC9B0", + "light_plus": "entity.other.inherited-class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.other.inherited-class: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.ruby meta.module.ruby entity.name.type.module.ruby entity.other.inherited-class.module.second.ruby punctuation.separator.inheritance.ruby", + "r": { + "dark_plus": "entity.other.inherited-class: #4EC9B0", + "light_plus": "entity.other.inherited-class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.other.inherited-class: #4EC9B0" + } + }, + { + "c": "Scheduler", + "t": "source.ruby meta.module.ruby entity.name.type.module.ruby", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " A service client - single point of access to the REST API.", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ruby meta.class.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "class", + "t": "source.ruby meta.class.ruby keyword.control.class.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.ruby meta.class.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "SchedulerManagementClient", + "t": "source.ruby meta.class.ruby entity.name.type.class.ruby", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.ruby meta.class.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.ruby meta.class.ruby keyword.operator.other.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby meta.class.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "MsRestAzure::AzureServiceClient", + "t": "source.ruby meta.class.ruby entity.other.inherited-class.ruby", + "r": { + "dark_plus": "entity.other.inherited-class: #4EC9B0", + "light_plus": "entity.other.inherited-class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.other.inherited-class: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "include", + "t": "source.ruby keyword.other.special-method.ruby", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Azure", + "t": "source.ruby support.class.ruby", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.ruby punctuation.separator.namespace.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "ARM", + "t": "source.ruby support.class.ruby", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.ruby punctuation.separator.namespace.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Scheduler", + "t": "source.ruby support.class.ruby", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.ruby punctuation.separator.namespace.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Models", + "t": "source.ruby variable.other.constant.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "include", + "t": "source.ruby keyword.other.special-method.ruby", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "MsRestAzure", + "t": "source.ruby variable.other.constant.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " @return job_collections", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "attr_reader", + "t": "source.ruby keyword.other.special-method.ruby", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.ruby constant.other.symbol.ruby punctuation.definition.constant.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "job_collections", + "t": "source.ruby constant.other.symbol.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Creates initializes a new instance of the SchedulerManagementClient class.", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " @param credentials [MsRest::ServiceClientCredentials] credentials to authorize HTTP requests made by the service client.", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " @param base_url [String] the base URI of the service.", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " @param options [Array] filters to be applied to the HTTP requests.", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "def", + "t": "source.ruby meta.function.method.with-arguments.ruby keyword.control.def.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.ruby meta.function.method.with-arguments.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "initialize", + "t": "source.ruby meta.function.method.with-arguments.ruby entity.name.function.ruby", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.ruby meta.function.method.with-arguments.ruby punctuation.definition.parameters.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "credentials", + "t": "source.ruby meta.function.method.with-arguments.ruby variable.parameter.function.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ", ", + "t": "source.ruby meta.function.method.with-arguments.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "base_url", + "t": "source.ruby meta.function.method.with-arguments.ruby variable.parameter.function.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby meta.function.method.with-arguments.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby meta.function.method.with-arguments.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby meta.function.method.with-arguments.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "nil", + "t": "source.ruby meta.function.method.with-arguments.ruby constant.language.ruby", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ", ", + "t": "source.ruby meta.function.method.with-arguments.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "options", + "t": "source.ruby meta.function.method.with-arguments.ruby variable.parameter.function.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby meta.function.method.with-arguments.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby meta.function.method.with-arguments.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby meta.function.method.with-arguments.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "nil", + "t": "source.ruby meta.function.method.with-arguments.ruby constant.language.ruby", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ")", + "t": "source.ruby meta.function.method.with-arguments.ruby punctuation.definition.parameters.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "super", + "t": "source.ruby keyword.control.pseudo-method.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "(", + "t": "source.ruby punctuation.section.function.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "credentials", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.ruby punctuation.separator.object.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " options", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.ruby punctuation.section.function.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.ruby variable.other.readwrite.instance.ruby punctuation.definition.variable.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "base_url", + "t": "source.ruby variable.other.readwrite.instance.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " base_url ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "||", + "t": "source.ruby keyword.operator.logical.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.ruby string.quoted.single.ruby punctuation.definition.string.begin.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "https://management.azure.com", + "t": "source.ruby string.quoted.single.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.ruby string.quoted.single.ruby punctuation.definition.string.end.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fail", + "t": "source.ruby keyword.other.special-method.ruby", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "ArgumentError", + "t": "source.ruby variable.other.constant.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.ruby punctuation.separator.object.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.ruby string.quoted.single.ruby punctuation.definition.string.begin.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "credentials is nil", + "t": "source.ruby string.quoted.single.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.ruby string.quoted.single.ruby punctuation.definition.string.end.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "if", + "t": "source.ruby keyword.control.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " credentials", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.ruby punctuation.separator.method.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "nil?", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fail", + "t": "source.ruby keyword.other.special-method.ruby", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "ArgumentError", + "t": "source.ruby variable.other.constant.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.ruby punctuation.separator.object.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.ruby string.quoted.single.ruby punctuation.definition.string.begin.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "invalid type of credentials input parameter", + "t": "source.ruby string.quoted.single.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.ruby string.quoted.single.ruby punctuation.definition.string.end.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "unless", + "t": "source.ruby keyword.control.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " credentials", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.ruby punctuation.separator.method.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "is_a?", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.ruby punctuation.section.function.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "MsRest", + "t": "source.ruby support.class.ruby", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.ruby punctuation.separator.namespace.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "ServiceClientCredentials", + "t": "source.ruby variable.other.constant.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.ruby punctuation.section.function.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.ruby variable.other.readwrite.instance.ruby punctuation.definition.variable.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "credentials", + "t": "source.ruby variable.other.readwrite.instance.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " credentials", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.ruby variable.other.readwrite.instance.ruby punctuation.definition.variable.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "job_collections", + "t": "source.ruby variable.other.readwrite.instance.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "JobCollections", + "t": "source.ruby support.class.ruby", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": ".", + "t": "source.ruby punctuation.separator.method.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "new", + "t": "source.ruby keyword.other.special-method.ruby", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "(", + "t": "source.ruby punctuation.section.function.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.ruby variable.language.self.ruby", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.ruby punctuation.section.function.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.ruby variable.other.readwrite.instance.ruby punctuation.definition.variable.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "jobs", + "t": "source.ruby variable.other.readwrite.instance.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Jobs", + "t": "source.ruby support.class.ruby", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": ".", + "t": "source.ruby punctuation.separator.method.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "new", + "t": "source.ruby keyword.other.special-method.ruby", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "(", + "t": "source.ruby punctuation.section.function.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.ruby variable.language.self.ruby", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.ruby punctuation.section.function.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.ruby variable.other.readwrite.instance.ruby punctuation.definition.variable.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "api_version", + "t": "source.ruby variable.other.readwrite.instance.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.ruby string.quoted.single.ruby punctuation.definition.string.begin.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "2016-01-01", + "t": "source.ruby string.quoted.single.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.ruby string.quoted.single.ruby punctuation.definition.string.end.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.ruby variable.other.readwrite.instance.ruby punctuation.definition.variable.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "long_running_operation_retry_timeout", + "t": "source.ruby variable.other.readwrite.instance.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "30", + "t": "source.ruby constant.numeric.integer.ruby", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.ruby variable.other.readwrite.instance.ruby punctuation.definition.variable.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "generate_client_request_id", + "t": "source.ruby variable.other.readwrite.instance.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "true", + "t": "source.ruby constant.language.ruby", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "if", + "t": "source.ruby keyword.control.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "MacOS", + "t": "source.ruby support.class.ruby", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": ".", + "t": "source.ruby punctuation.separator.method.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "version ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">=", + "t": "source.ruby keyword.operator.comparison.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.ruby constant.other.symbol.ruby punctuation.definition.constant.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "mavericks", + "t": "source.ruby constant.other.symbol.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " version ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "`", + "t": "source.ruby string.interpolated.ruby punctuation.definition.string.begin.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "#{", + "t": "source.ruby string.interpolated.ruby meta.embedded.line.ruby punctuation.section.embedded.begin.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "MAVERICKS_PKG_PATH", + "t": "source.ruby string.interpolated.ruby meta.embedded.line.ruby source.ruby variable.other.constant.ruby", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "}", + "t": "source.ruby string.interpolated.ruby meta.embedded.line.ruby punctuation.section.embedded.end.ruby source.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "/usr/bin/clang --version", + "t": "source.ruby string.interpolated.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "`", + "t": "source.ruby string.interpolated.ruby punctuation.definition.string.end.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "else", + "t": "source.ruby keyword.control.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " version ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "`", + "t": "source.ruby string.interpolated.ruby punctuation.definition.string.begin.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "/usr/bin/clang --version", + "t": "source.ruby string.interpolated.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "`", + "t": "source.ruby string.interpolated.ruby punctuation.definition.string.end.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "end", + "t": "source.ruby keyword.control.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " version ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ruby keyword.operator.assignment.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " version", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.ruby punctuation.section.array.begin.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/", + "t": "source.ruby string.regexp.classic.ruby punctuation.definition.string.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "clang-", + "t": "source.ruby string.regexp.classic.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "(", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby punctuation.definition.group.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "\\d", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "+", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "\\.\\d", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "+", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "\\.\\d", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "+", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "(", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby meta.group.regexp.ruby punctuation.definition.group.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "\\.\\d", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby meta.group.regexp.ruby constant.character.escape.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "+", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby meta.group.regexp.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": ")", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby meta.group.regexp.ruby punctuation.definition.group.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "?", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": ")", + "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby punctuation.definition.group.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": "/", + "t": "source.ruby string.regexp.classic.ruby punctuation.definition.string.ruby", + "r": { + "dark_plus": "string.regexp: #D16969", + "light_plus": "string.regexp: #811F3F", + "dark_vs": "string.regexp: #D16969", + "light_vs": "string.regexp: #811F3F", + "hc_black": "string.regexp: #D16969" + } + }, + { + "c": ",", + "t": "source.ruby punctuation.separator.object.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.ruby constant.numeric.integer.ruby", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "]", + "t": "source.ruby punctuation.section.array.end.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "||", + "t": "source.ruby keyword.operator.logical.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.ruby string.quoted.double.ruby punctuation.definition.string.begin.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "0", + "t": "source.ruby string.quoted.double.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.ruby string.quoted.double.ruby punctuation.definition.string.end.ruby", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " version ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.ruby keyword.operator.comparison.ruby", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " latest_version", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "end", + "t": "source.ruby keyword.control.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "end", + "t": "source.ruby keyword.control.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "end", + "t": "source.ruby keyword.control.ruby", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + } +] \ No newline at end of file diff --git a/extensions/shellscript/.vscodeignore b/extensions/shellscript/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/shellscript/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/shellscript/OSSREADME.json b/extensions/shellscript/OSSREADME.json new file mode 100644 index 0000000000..34422dec51 --- /dev/null +++ b/extensions/shellscript/OSSREADME.json @@ -0,0 +1,23 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "textmate/shellscript.tmbundle", + "version": "0.0.0", + "license": "TextMate Bundle License", + "repositoryURL": "https://github.com/textmate/shellscript.tmbundle", + "licenseDetail": [ + "Copyright (c) textmate-shellscript.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ] +}] diff --git a/extensions/shellscript/language-configuration.json b/extensions/shellscript/language-configuration.json new file mode 100644 index 0000000000..01b6a8a282 --- /dev/null +++ b/extensions/shellscript/language-configuration.json @@ -0,0 +1,26 @@ +{ + "comments": { + "lineComment": "#" + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"], + ["`", "`"] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"], + ["`", "`"] + ] +} \ No newline at end of file diff --git a/extensions/shellscript/package.json b/extensions/shellscript/package.json new file mode 100644 index 0000000000..e0ff83c749 --- /dev/null +++ b/extensions/shellscript/package.json @@ -0,0 +1,25 @@ +{ + "name": "shellscript", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "scripts": { + "update-grammar": "node ../../build/npm/update-grammar.js textmate/shellscript.tmbundle Syntaxes/Shell-Unix-Bash.tmLanguage ./syntaxes/Shell-Unix-Bash.tmLanguage.json" + }, + "contributes": { + "languages": [{ + "id": "shellscript", + "aliases": ["Shell Script (Bash)", "shellscript", "bash", "sh", "zsh"], + "extensions": [".sh", ".bash", ".bashrc", ".bash_aliases", ".bash_profile", ".bash_login", ".ebuild", ".install", ".profile", ".bash_logout", ".zsh", ".zshrc", ".zprofile", ".zlogin", ".zlogout", ".zshenv", ".zsh-theme"], + "filenames": ["PKGBUILD"], + "firstLine": "^#!.*\\b(bash|zsh|sh|tcsh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-", + "configuration": "./language-configuration.json", + "mimetypes": ["text/x-shellscript"] + }], + "grammars": [{ + "language": "shellscript", + "scopeName": "source.shell", + "path": "./syntaxes/Shell-Unix-Bash.tmLanguage.json" + }] + } +} diff --git a/extensions/shellscript/syntaxes/Shell-Unix-Bash.tmLanguage.json b/extensions/shellscript/syntaxes/Shell-Unix-Bash.tmLanguage.json new file mode 100644 index 0000000000..88cf23c686 --- /dev/null +++ b/extensions/shellscript/syntaxes/Shell-Unix-Bash.tmLanguage.json @@ -0,0 +1,1247 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/textmate/shellscript.tmbundle/blob/master/Syntaxes/Shell-Unix-Bash.tmLanguage", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/textmate/shellscript.tmbundle/commit/ba95d7b742caef130911d878f42f66bdd80181e4", + "fileTypes": [ + "sh", + "bash", + "zsh", + "bashrc", + "bash_profile", + "bash_login", + "profile", + "bash_logout", + ".textmate_init" + ], + "firstLineMatch": "^#!.*\\b(bash|zsh|sh|tcsh)|^#.*-\\*-.*\\bshell-script\\b.*-\\*-", + "keyEquivalent": "^~S", + "name": "Shell Script (Bash)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pipeline" + }, + { + "include": "#list" + }, + { + "include": "#compound-command" + }, + { + "include": "#loop" + }, + { + "include": "#string" + }, + { + "include": "#function-definition" + }, + { + "include": "#variable" + }, + { + "include": "#interpolation" + }, + { + "include": "#heredoc" + }, + { + "include": "#herestring" + }, + { + "include": "#redirection" + }, + { + "include": "#pathname" + }, + { + "include": "#keyword" + }, + { + "include": "#support" + }, + { + "include": "#lines" + } + ], + "repository": { + "case-clause": { + "patterns": [ + { + "begin": "(?=\\S)", + "end": ";;", + "endCaptures": { + "0": { + "name": "punctuation.terminator.case-clause.shell" + } + }, + "name": "meta.scope.case-clause.shell", + "patterns": [ + { + "begin": "(\\(|(?=\\S))", + "captures": { + "0": { + "name": "punctuation.definition.case-pattern.shell" + } + }, + "end": "\\)", + "name": "meta.scope.case-pattern.shell", + "patterns": [ + { + "match": "\\|", + "name": "punctuation.separator.pipe-sign.shell" + }, + { + "include": "#string" + }, + { + "include": "#variable" + }, + { + "include": "#interpolation" + }, + { + "include": "#pathname" + } + ] + }, + { + "begin": "(?<=\\))", + "end": "(?=;;)", + "name": "meta.scope.case-clause-body.shell", + "patterns": [ + { + "include": "$self" + } + ] + } + ] + } + ] + }, + "comment": { + "begin": "(^[ \\t]+)?(?;'\"\\\\|$&()]+)(?:\\s*(\\(\\)))?", + "beginCaptures": { + "1": { + "name": "storage.type.function.shell" + }, + "2": { + "name": "entity.name.function.shell" + }, + "3": { + "name": "punctuation.definition.arguments.shell" + } + }, + "end": ";|&|\\n", + "endCaptures": { + "0": { + "name": "punctuation.definition.function.shell" + } + }, + "name": "meta.function.shell", + "patterns": [ + { + "include": "$self" + } + ] + }, + { + "begin": "(?<=^|;|&)\\s*([^\\s<>;'\"\\\\|$&()]+)\\s*(\\(\\))", + "beginCaptures": { + "1": { + "name": "entity.name.function.shell" + }, + "2": { + "name": "punctuation.definition.arguments.shell" + } + }, + "end": ";|&|\\n", + "endCaptures": { + "0": { + "name": "punctuation.definition.function.shell" + } + }, + "name": "meta.function.shell", + "patterns": [ + { + "include": "$self" + } + ] + } + ] + }, + "heredoc": { + "patterns": [ + { + "begin": "(<<)-\\s*(\"|'|)(RUBY)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "source.ruby.embedded.shell", + "end": "^\\t*(RUBY)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.no-indent.ruby.shell", + "patterns": [ + { + "include": "source.ruby" + } + ] + }, + { + "begin": "(<<)\\s*(\"|'|)(RUBY)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "source.ruby.embedded.shell", + "end": "^(RUBY)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.ruby.shell", + "patterns": [ + { + "include": "source.ruby" + } + ] + }, + { + "begin": "(<<)-\\s*(\"|'|)(PYTHON)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "source.python.embedded.shell", + "end": "^\\t*(PYTHON)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.no-indent.python.shell", + "patterns": [ + { + "include": "source.python" + } + ] + }, + { + "begin": "(<<)\\s*(\"|'|)(PYTHON)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "source.python.embedded.shell", + "end": "^(PYTHON)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.python.shell", + "patterns": [ + { + "include": "source.python" + } + ] + }, + { + "begin": "(<<)-\\s*(\"|'|)(APPLESCRIPT)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "source.applescript.embedded.shell", + "end": "^\\t*(APPLESCRIPT)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.no-indent.applescript.shell", + "patterns": [ + { + "include": "source.applescript" + } + ] + }, + { + "begin": "(<<)\\s*(\"|'|)(APPLESCRIPT)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "source.applescript.embedded.shell", + "end": "^(APPLESCRIPT)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.applescript.shell", + "patterns": [ + { + "include": "source.applescript" + } + ] + }, + { + "begin": "(<<)-\\s*(\"|'|)(HTML)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "text.html.embedded.shell", + "end": "^\\t*(HTML)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.no-indent.html.shell", + "patterns": [ + { + "include": "text.html.basic" + } + ] + }, + { + "begin": "(<<)\\s*(\"|'|)(HTML)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "text.html.embedded.shell", + "end": "^(HTML)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.html.shell", + "patterns": [ + { + "include": "text.html.basic" + } + ] + }, + { + "begin": "(<<)-\\s*(\"|'|)(MARKDOWN)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "text.html.markdown.embedded.shell", + "end": "^\\t*(MARKDOWN)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.no-indent.markdown.shell", + "patterns": [ + { + "include": "text.html.markdown" + } + ] + }, + { + "begin": "(<<)\\s*(\"|'|)(MARKDOWN)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "text.html.markdown.embedded.shell", + "end": "^(MARKDOWN)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.markdown.shell", + "patterns": [ + { + "include": "text.html.markdown" + } + ] + }, + { + "begin": "(<<)-\\s*(\"|'|)(TEXTILE)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "text.html.textile.embedded.shell", + "end": "^\\t*(TEXTILE)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.no-indent.textile.shell", + "patterns": [ + { + "include": "text.html.textile" + } + ] + }, + { + "begin": "(<<)\\s*(\"|'|)(TEXTILE)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "contentName": "text.html.textile.embedded.shell", + "end": "^(TEXTILE)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.textile.shell", + "patterns": [ + { + "include": "text.html.textile" + } + ] + }, + { + "begin": "(<<)-\\s*(\"|'|)\\\\?(\\w+)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "end": "^\\t*(\\3)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.no-indent.shell" + }, + { + "begin": "(<<)\\s*(\"|'|)\\\\?(\\w+)\\2", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "3": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "captures": { + "0": { + "name": "punctuation.definition.string.shell" + } + }, + "end": "^(\\3)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.shell" + } + ] + }, + "herestring": { + "patterns": [ + { + "captures": { + "1": { + "name": "keyword.operator.herestring.shell" + }, + "2": { + "name": "string.quoted.single.herestring.shell" + }, + "3": { + "name": "punctuation.definition.string.begin.shell" + }, + "4": { + "name": "punctuation.definition.string.end.shell" + } + }, + "match": "(<<<)\\s*((')[^']*('))", + "name": "meta.herestring.shell" + }, + { + "captures": { + "1": { + "name": "keyword.operator.herestring.shell" + }, + "2": { + "name": "string.quoted.double.herestring.shell" + }, + "3": { + "name": "punctuation.definition.string.begin.shell" + }, + "6": { + "name": "punctuation.definition.string.end.shell" + } + }, + "match": "(<<<)\\s*((\")(\\\\(\"|\\\\)|[^\"])*(\"))", + "name": "meta.herestring.shell" + }, + { + "captures": { + "1": { + "name": "keyword.operator.herestring.shell" + }, + "2": { + "name": "string.unquoted.herestring.shell" + } + }, + "match": "(<<<)\\s*(([^\\s\\\\]|\\\\.)+)", + "name": "meta.herestring.shell" + } + ] + }, + "interpolation": { + "patterns": [ + { + "begin": "\\$\\({2}", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.shell" + } + }, + "end": "\\){2}", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.shell" + } + }, + "name": "string.other.math.shell", + "patterns": [ + { + "include": "#math" + } + ] + }, + { + "begin": "`", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.shell" + } + }, + "end": "`", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.shell" + } + }, + "name": "string.interpolated.backtick.shell", + "patterns": [ + { + "match": "\\\\[`\\\\$]", + "name": "constant.character.escape.shell" + }, + { + "include": "$self" + } + ] + }, + { + "begin": "\\$\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.shell" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.shell" + } + }, + "name": "string.interpolated.dollar.shell", + "patterns": [ + { + "include": "$self" + } + ] + } + ] + }, + "keyword": { + "patterns": [ + { + "match": "(?<=^|;|&|\\s)(?:if|then|else|elif|fi|for|in|do|done|select|case|continue|esac|while|until|return|coproc)(?=\\s|;|&|$)", + "name": "keyword.control.shell" + }, + { + "match": "(?<=^|;|&|\\s)(?:export|declare|typeset|local|readonly)(?=\\s|;|&|$)", + "name": "storage.modifier.shell" + } + ] + }, + "lines": { + "patterns": [ + { + "match": "\\\\\\n", + "name": "constant.character.escape.newline.shell" + } + ] + }, + "list": { + "patterns": [ + { + "match": ";|&&|&|\\|\\|", + "name": "keyword.operator.list.shell" + } + ] + }, + "logical-expression": { + "patterns": [ + { + "comment": "do we want a special rule for ( expr )?", + "match": "=[=~]?|!=?|<|>|&&|\\|\\|", + "name": "keyword.operator.logical.shell" + }, + { + "match": "(?[>=]?|==|!=|\\^|\\|{1,2}|&{1,2}|\\?|\\:|,|=|[*/%+\\-&^|]=|<<=|>>=", + "name": "keyword.operator.arithmetic.shell" + }, + { + "match": "0[xX]\\h+", + "name": "constant.numeric.hex.shell" + }, + { + "match": "0\\d+", + "name": "constant.numeric.octal.shell" + }, + { + "match": "\\d{1,2}#[0-9a-zA-Z@_]+", + "name": "constant.numeric.other.shell" + }, + { + "match": "\\d+", + "name": "constant.numeric.integer.shell" + } + ] + }, + "pathname": { + "patterns": [ + { + "match": "(?<=\\s|:|=|^)~", + "name": "keyword.operator.tilde.shell" + }, + { + "match": "\\*|\\?", + "name": "keyword.operator.glob.shell" + }, + { + "begin": "([?*+@!])(\\()", + "beginCaptures": { + "1": { + "name": "keyword.operator.extglob.shell" + }, + "2": { + "name": "punctuation.definition.extglob.shell" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.definition.extglob.shell" + } + }, + "name": "meta.structure.extglob.shell", + "patterns": [ + { + "include": "$self" + } + ] + } + ] + }, + "pipeline": { + "patterns": [ + { + "match": "(?<=^|;|&|\\s)(time)(?=\\s|;|&|$)", + "name": "keyword.other.shell" + }, + { + "match": "[|!]", + "name": "keyword.operator.pipe.shell" + } + ] + }, + "redirection": { + "patterns": [ + { + "begin": "[><]\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.shell" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.shell" + } + }, + "name": "string.interpolated.process-substitution.shell", + "patterns": [ + { + "include": "$self" + } + ] + }, + { + "comment": "valid: &>word >&word >word [n]>&[n] [n]word [n]>>word [n]<&word (last one is duplicate)", + "match": "&>|\\d*>&\\d*|\\d*(>>|>|<)|\\d*<&|\\d*<>", + "name": "keyword.operator.redirect.shell" + } + ] + }, + "string": { + "patterns": [ + { + "match": "\\\\.", + "name": "constant.character.escape.shell" + }, + { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.shell" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.shell" + } + }, + "name": "string.quoted.single.shell" + }, + { + "begin": "\\$?\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.shell" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.shell" + } + }, + "name": "string.quoted.double.shell", + "patterns": [ + { + "match": "\\\\[\\$`\"\\\\\\n]", + "name": "constant.character.escape.shell" + }, + { + "include": "#variable" + }, + { + "include": "#interpolation" + } + ] + }, + { + "begin": "\\$'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.shell" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.shell" + } + }, + "name": "string.quoted.single.dollar.shell", + "patterns": [ + { + "match": "\\\\(a|b|e|f|n|r|t|v|\\\\|')", + "name": "constant.character.escape.ansi-c.shell" + }, + { + "match": "\\\\[0-9]{3}", + "name": "constant.character.escape.octal.shell" + }, + { + "match": "\\\\x[0-9a-fA-F]{2}", + "name": "constant.character.escape.hex.shell" + }, + { + "match": "\\\\c.", + "name": "constant.character.escape.control-char.shell" + } + ] + } + ] + }, + "support": { + "patterns": [ + { + "match": "(?<=^|;|&|\\s)(?::|\\.)(?=\\s|;|&|$)", + "name": "support.function.builtin.shell" + }, + { + "match": "(?<=^|;|&|\\s)(?:alias|bg|bind|break|builtin|caller|cd|command|compgen|complete|dirs|disown|echo|enable|eval|exec|exit|false|fc|fg|getopts|hash|help|history|jobs|kill|let|logout|mapfile|popd|printf|pushd|pwd|read(array)?|readonly|set|shift|shopt|source|suspend|test|times|trap|true|type|ulimit|umask|unalias|unset|wait)(?=\\s|;|&|$)", + "name": "support.function.builtin.shell" + } + ] + }, + "variable": { + "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.definition.variable.shell" + } + }, + "match": "(\\$)[a-zA-Z_][a-zA-Z0-9_]*", + "name": "variable.other.normal.shell" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.shell" + } + }, + "match": "(\\$)[-*@#?$!0_]", + "name": "variable.other.special.shell" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.shell" + } + }, + "match": "(\\$)[1-9]", + "name": "variable.other.positional.shell" + }, + { + "begin": "\\$\\{", + "captures": { + "0": { + "name": "punctuation.definition.variable.shell" + } + }, + "end": "\\}", + "name": "variable.other.bracket.shell", + "patterns": [ + { + "match": "!|:[-=?+]?|\\*|@|#{1,2}|%{1,2}|/", + "name": "keyword.operator.expansion.shell" + }, + { + "captures": { + "1": { + "name": "punctuation.section.array.shell" + }, + "3": { + "name": "punctuation.section.array.shell" + } + }, + "match": "(\\[)([^\\]]+)(\\])" + }, + { + "include": "#string" + }, + { + "include": "#variable" + }, + { + "include": "#interpolation" + } + ] + } + ] + } + }, + "scopeName": "source.shell", + "uuid": "DDEEA3ED-6B1C-11D9-8B10-000D93589AF6" +} \ No newline at end of file diff --git a/extensions/shellscript/test/colorize-fixtures/test.sh b/extensions/shellscript/test/colorize-fixtures/test.sh new file mode 100644 index 0000000000..4c5bf8f7ea --- /dev/null +++ b/extensions/shellscript/test/colorize-fixtures/test.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname $(dirname $(realpath "$0"))) +else + ROOT=$(dirname $(dirname $(readlink -f $0))) +fi + +DEVELOPER=$(xcode-select -print-path) +LIPO=$(xcrun -sdk iphoneos -find lipo) + +function code() { + cd $ROOT + + # Node modules + test -d node_modules || ./scripts/npm.sh install + + # Configuration + export NODE_ENV=development + + # Launch Code + if [[ "$OSTYPE" == "darwin"* ]]; then + exec ./.build/electron/Electron.app/Contents/MacOS/Electron . "$@" + else + exec ./.build/electron/electron . "$@" + fi +} + +code "$@" diff --git a/extensions/shellscript/test/colorize-results/test_sh.json b/extensions/shellscript/test/colorize-results/test_sh.json new file mode 100644 index 0000000000..3bb5e394f1 --- /dev/null +++ b/extensions/shellscript/test/colorize-results/test_sh.json @@ -0,0 +1,1971 @@ +[ + { + "c": "#!", + "t": "source.shell comment.line.shebang.shell punctuation.definition.comment.line.shebang.shell", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "/usr/bin/env bash", + "t": "source.shell comment.line.shebang.shell", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "if", + "t": "source.shell meta.scope.if-block.shell keyword.control.shell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[[", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell punctuation.definition.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell variable.other.normal.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "OSTYPE", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell variable.other.normal.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\"", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "==", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell keyword.operator.logical.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "darwin", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "*", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell keyword.operator.glob.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]]", + "t": "source.shell meta.scope.if-block.shell meta.scope.logical-expression.shell punctuation.definition.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.shell meta.scope.if-block.shell keyword.operator.list.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "then", + "t": "source.shell meta.scope.if-block.shell keyword.control.shell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "\t", + "t": "source.shell meta.scope.if-block.shell meta.function.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "realpath", + "t": "source.shell meta.scope.if-block.shell meta.function.shell entity.name.function.shell", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "source.shell meta.scope.if-block.shell meta.function.shell punctuation.definition.arguments.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell punctuation.definition.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[[", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell meta.scope.logical-expression.shell punctuation.definition.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell meta.scope.logical-expression.shell variable.other.positional.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "1", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell meta.scope.logical-expression.shell variable.other.positional.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell meta.scope.logical-expression.shell keyword.operator.logical.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " /", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell meta.scope.logical-expression.shell keyword.operator.glob.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]]", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell meta.scope.logical-expression.shell punctuation.definition.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "&&", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell keyword.operator.list.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "echo", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell support.function.builtin.shell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell variable.other.positional.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "1", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell variable.other.positional.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\"", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "||", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell keyword.operator.pipe.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "echo", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell support.function.builtin.shell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell variable.other.normal.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "PWD", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell variable.other.normal.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "/", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "${", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell variable.other.bracket.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "1", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell variable.other.bracket.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "#", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell variable.other.bracket.shell keyword.operator.expansion.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": ".", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell variable.other.bracket.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "/", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell variable.other.bracket.shell keyword.operator.expansion.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "}", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell variable.other.bracket.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\"", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell string.quoted.double.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ";", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell keyword.operator.list.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.shell meta.scope.if-block.shell meta.function.shell meta.scope.group.shell punctuation.definition.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\tROOT=", + "t": "source.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "dirname ", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$(", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "dirname ", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$(", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "realpath ", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.quoted.double.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.quoted.double.shell variable.other.special.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "0", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.quoted.double.shell variable.other.special.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\"", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.quoted.double.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "else", + "t": "source.shell meta.scope.if-block.shell keyword.control.shell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "\tROOT=", + "t": "source.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "dirname ", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$(", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "dirname ", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$(", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "readlink -f ", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell variable.other.special.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "0", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell variable.other.special.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell string.interpolated.dollar.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell string.interpolated.dollar.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.shell meta.scope.if-block.shell string.interpolated.dollar.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "fi", + "t": "source.shell meta.scope.if-block.shell keyword.control.shell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "DEVELOPER=", + "t": "source.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.shell string.interpolated.dollar.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "xcode-select -print-path", + "t": "source.shell string.interpolated.dollar.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.shell string.interpolated.dollar.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "LIPO=", + "t": "source.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.shell string.interpolated.dollar.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "xcrun -sdk iphoneos -find lipo", + "t": "source.shell string.interpolated.dollar.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.shell string.interpolated.dollar.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "function", + "t": "source.shell meta.function.shell storage.type.function.shell", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "code", + "t": "source.shell meta.function.shell entity.name.function.shell", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "source.shell meta.function.shell punctuation.definition.arguments.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.shell meta.function.shell meta.scope.group.shell punctuation.definition.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "cd", + "t": "source.shell meta.function.shell meta.scope.group.shell support.function.builtin.shell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$", + "t": "source.shell meta.function.shell meta.scope.group.shell variable.other.normal.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "ROOT", + "t": "source.shell meta.function.shell meta.scope.group.shell variable.other.normal.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\t", + "t": "source.shell meta.function.shell meta.scope.group.shell punctuation.whitespace.comment.leading.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell punctuation.definition.comment.shell", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Node modules", + "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\t", + "t": "source.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "test", + "t": "source.shell meta.function.shell meta.scope.group.shell support.function.builtin.shell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " -d node_modules ", + "t": "source.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "||", + "t": "source.shell meta.function.shell meta.scope.group.shell keyword.operator.pipe.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ./scripts/npm.sh install", + "t": "source.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.shell meta.function.shell meta.scope.group.shell punctuation.whitespace.comment.leading.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell punctuation.definition.comment.shell", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Configuration", + "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\t", + "t": "source.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "export", + "t": "source.shell meta.function.shell meta.scope.group.shell storage.modifier.shell", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6" + } + }, + { + "c": " NODE_ENV=development", + "t": "source.shell meta.function.shell meta.scope.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.shell meta.function.shell meta.scope.group.shell punctuation.whitespace.comment.leading.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell punctuation.definition.comment.shell", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Launch Code", + "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\t", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "if", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell keyword.control.shell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[[", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell punctuation.definition.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell variable.other.normal.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "OSTYPE", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell variable.other.normal.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\"", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "==", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell keyword.operator.logical.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "darwin", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell string.quoted.double.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "*", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell keyword.operator.glob.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]]", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell meta.scope.logical-expression.shell punctuation.definition.logical-expression.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell keyword.operator.list.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "then", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell keyword.control.shell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "\t\t", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "exec", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell support.function.builtin.shell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ./.build/electron/Electron.app/Contents/MacOS/Electron ", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell support.function.builtin.shell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell string.quoted.double.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell string.quoted.double.shell variable.other.special.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "@", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell string.quoted.double.shell variable.other.special.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\"", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell string.quoted.double.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\t", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "else", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell keyword.control.shell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "\t\t", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "exec", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell support.function.builtin.shell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ./.build/electron/electron ", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell support.function.builtin.shell", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell string.quoted.double.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell string.quoted.double.shell variable.other.special.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "@", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell string.quoted.double.shell variable.other.special.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\"", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell string.quoted.double.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\t", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fi", + "t": "source.shell meta.function.shell meta.scope.group.shell meta.scope.if-block.shell keyword.control.shell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "}", + "t": "source.shell meta.function.shell meta.scope.group.shell punctuation.definition.group.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "code ", + "t": "source.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.shell string.quoted.double.shell punctuation.definition.string.begin.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$", + "t": "source.shell string.quoted.double.shell variable.other.special.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "@", + "t": "source.shell string.quoted.double.shell variable.other.special.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "\"", + "t": "source.shell string.quoted.double.shell punctuation.definition.string.end.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + } +] \ No newline at end of file diff --git a/extensions/sql/.vscodeignore b/extensions/sql/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/sql/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/sql/OSSREADME.json b/extensions/sql/OSSREADME.json new file mode 100644 index 0000000000..6b4512cb48 --- /dev/null +++ b/extensions/sql/OSSREADME.json @@ -0,0 +1,23 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "textmate/sql.tmbundle", + "version": "0.0.0", + "license": "TextMate Bundle License", + "repositoryURL": "https://github.com/textmate/sql.tmbundle", + "licenseDetail": [ + "Copyright (c) textmate-sql.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this folder fall under the following license: ", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information, ", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ] +}] diff --git a/extensions/sql/language-configuration.json b/extensions/sql/language-configuration.json new file mode 100644 index 0000000000..ee314b0184 --- /dev/null +++ b/extensions/sql/language-configuration.json @@ -0,0 +1,32 @@ +{ + "comments": { + "lineComment": "--", + "blockComment": [ "/*", "*/" ] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ] + + // enhancedBrackets:[ + // { openTrigger: 'n', open: /begin$/i, closeComplete: 'end', matchCase: true }, + // { openTrigger: 'e', open: /case$/i, closeComplete: 'end', matchCase: true }, + // { openTrigger: 'n', open: /when$/i, closeComplete: 'then', matchCase: true } + // ], + // noindentBrackets: '()', +} \ No newline at end of file diff --git a/extensions/sql/package.json b/extensions/sql/package.json new file mode 100644 index 0000000000..b012cf5cdd --- /dev/null +++ b/extensions/sql/package.json @@ -0,0 +1,19 @@ +{ + "name": "sql", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "languages": [{ + "id": "sql", + "extensions": [ ".sql", ".dsql" ], + "aliases": [ "SQL" ], + "configuration": "./language-configuration.json" + }], + "grammars": [{ + "language": "sql", + "scopeName": "source.sql", + "path": "./syntaxes/SQL.plist" + }] + } +} \ No newline at end of file diff --git a/extensions/sql/syntaxes/SQL.plist b/extensions/sql/syntaxes/SQL.plist new file mode 100644 index 0000000000..76b90bd93d --- /dev/null +++ b/extensions/sql/syntaxes/SQL.plist @@ -0,0 +1,771 @@ + + + + + fileTypes + + sql + ddl + dml + + keyEquivalent + ^~S + name + SQL + patterns + + + match + \b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|alias|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|array|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference |automatic|autopilot|availability|availability_mode|backup_priority|base64|basic|batches|batchsize|before|bigint|binary|binding|bit|block|blocksize|bmk|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|cast|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|cleanup_policy|clear|clear_port|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create|credential|cross|cryptographic|cryptographic_provider|cube|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|days|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|dec|decimal|declare|decrypt|decrypt_a|decryption|default_database|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|diagnostics|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|elements|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|exec|executable|exists|expand|expiredate|expiry_date|explicit|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|field_terminator|fieldterminator|file|filelistonly|filegroup|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|foreach|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hours|http|identity_value|if|ifnull|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|immutable|implicit_transactions|include|include_null_values|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kilobytes_per_batch|labelonly|langid|language|last|lastrow|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minutes|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|perform|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|procedure_name|profile|provider|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|restart|restore|restricted_user|resume|retaindays|retention|return|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|schemabinding|scoped|scroll|scroll_locks|sddl|secexpr|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|stable|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics_incremental|statistics_norecompute|statistics_only|statman|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|tran|transaction|transfer|triple_des|triple_des_3key|trustworthy|try|tsql|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|volatile|webmethod|weekday|weight|well_formed_xml|when|widechar|widechar_ansi|widenative|windows|with|within|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|zone)\b + name + keyword.other.sql + + + include + #comments + + + captures + + 1 + + name + keyword.other.create.sql + + 2 + + name + keyword.other.sql + + 5 + + name + entity.name.function.sql + + + match + (?i:^\s*(create(?:\s+or\s+replace)?)\s+(aggregate|conversion|database|domain|function|group|(unique\s+)?index|language|operator class|operator|rule|schema|sequence|table|tablespace|trigger|type|user|view)\s+)(['"`]?)(\w+)\4 + name + meta.create.sql + + + captures + + 1 + + name + keyword.other.create.sql + + 2 + + name + keyword.other.sql + + + match + (?i:^\s*(drop)\s+(aggregate|conversion|database|domain|function|group|index|language|operator class|operator|rule|schema|sequence|table|tablespace|trigger|type|user|view)) + name + meta.drop.sql + + + captures + + 1 + + name + keyword.other.create.sql + + 2 + + name + keyword.other.table.sql + + 3 + + name + entity.name.function.sql + + 4 + + name + keyword.other.cascade.sql + + + match + (?i:\s*(drop)\s+(table)\s+(\w+)(\s+cascade)?\b) + name + meta.drop.sql + + + captures + + 1 + + name + keyword.other.create.sql + + 2 + + name + keyword.other.table.sql + + + match + (?i:^\s*(alter)\s+(aggregate|conversion|database|domain|function|group|index|language|operator class|operator|rule|schema|sequence|table|tablespace|trigger|type|user|view)\s+) + name + meta.alter.sql + + + captures + + 1 + + name + storage.type.sql + + 10 + + name + constant.numeric.sql + + 11 + + name + storage.type.sql + + 12 + + name + storage.type.sql + + 13 + + name + storage.type.sql + + 14 + + name + constant.numeric.sql + + 15 + + name + storage.type.sql + + 2 + + name + storage.type.sql + + 3 + + name + constant.numeric.sql + + 4 + + name + storage.type.sql + + 5 + + name + constant.numeric.sql + + 6 + + name + storage.type.sql + + 7 + + name + constant.numeric.sql + + 8 + + name + constant.numeric.sql + + 9 + + name + storage.type.sql + + + match + (?xi) + + # normal stuff, capture 1 + \b(bigint|bigserial|bit|boolean|box|bytea|cidr|circle|date|double\sprecision|inet|int|integer|line|lseg|macaddr|money|oid|path|point|polygon|real|serial|smallint|sysdate|text)\b + + # numeric suffix, capture 2 + 3i + |\b(bit\svarying|character\s(?:varying)?|tinyint|var\schar|float|interval)\((\d+)\) + + # optional numeric suffix, capture 4 + 5i + |\b(char|number|varchar\d?)\b(?:\((\d+)\))? + + # special case, capture 6 + 7i + 8i + |\b(numeric|decimal)\b(?:\((\d+),(\d+)\))? + + # special case, captures 9, 10i, 11 + |\b(times?)\b(?:\((\d+)\))?(\swith(?:out)?\stime\szone\b)? + + # special case, captures 12, 13, 14i, 15 + |\b(timestamp)(?:(s|tz))?\b(?:\((\d+)\))?(\s(with|without)\stime\szone\b)? + + + + + match + (?i:\b((?:primary|foreign)\s+key|references|on\sdelete(\s+cascade)?|check|constraint)\b) + name + storage.modifier.sql + + + match + \b\d+\b + name + constant.numeric.sql + + + match + (?i:\b(select(\s+distinct)?|insert\s+(ignore\s+)?into|update|delete|from|set|where|group\sby|or|like|and|union(\s+all)?|having|order\sby|limit|(inner|cross)\s+join|join|straight_join|(left|right)(\s+outer)?\s+join|natural(\s+(left|right)(\s+outer)?)?\s+join)\b) + name + keyword.other.DML.sql + + + match + (?i:\b(on|((is\s+)?not\s+)?null)\b) + name + keyword.other.DDL.create.II.sql + + + match + (?i:\bvalues\b) + name + keyword.other.DML.II.sql + + + match + (?i:\b(begin(\s+work)?|start\s+transaction|commit(\s+work)?|rollback(\s+work)?)\b) + name + keyword.other.LUW.sql + + + match + (?i:\b(grant(\swith\sgrant\soption)?|revoke)\b) + name + keyword.other.authorization.sql + + + match + (?i:\bin\b) + name + keyword.other.data-integrity.sql + + + match + (?i:^\s*(comment\s+on\s+(table|column|aggregate|constraint|database|domain|function|index|operator|rule|schema|sequence|trigger|type|view))\s+.*?\s+(is)\s+) + name + keyword.other.object-comments.sql + + + match + (?i)\bAS\b + name + keyword.other.alias.sql + + + match + (?i)\b(DESC|ASC)\b + name + keyword.other.order.sql + + + match + \* + name + keyword.operator.star.sql + + + match + [!<>]?=|<>|<|> + name + keyword.operator.comparison.sql + + + match + -|\+|/ + name + keyword.operator.math.sql + + + match + \|\| + name + keyword.operator.concatenator.sql + + + comment + List of SQL99 built-in functions from http://www.oreilly.com/catalog/sqlnut/chapter/ch04.html + match + (?i)\b(CURRENT_(DATE|TIME(STAMP)?|USER)|(SESSION|SYSTEM)_USER)\b + name + support.function.scalar.sql + + + comment + List of SQL99 built-in functions from http://www.oreilly.com/catalog/sqlnut/chapter/ch04.html + match + (?i)\b(AVG|COUNT|MIN|MAX|SUM)(?=\s*\() + name + support.function.aggregate.sql + + + match + (?i)\b(CONCATENATE|CONVERT|LOWER|SUBSTRING|TRANSLATE|TRIM|UPPER)\b + name + support.function.string.sql + + + captures + + 1 + + name + constant.other.database-name.sql + + 2 + + name + constant.other.table-name.sql + + + match + (\w+?)\.(\w+) + + + include + #strings + + + include + #regexps + + + captures + + 1 + + name + punctuation.section.scope.begin.sql + + 2 + + name + punctuation.section.scope.end.sql + + + comment + Allow for special ↩ behavior + match + (\()(\)) + name + meta.block.sql + + + repository + + comments + + patterns + + + begin + (^[ \t]+)?(?=--) + beginCaptures + + 1 + + name + punctuation.whitespace.comment.leading.sql + + + end + (?!\G) + patterns + + + begin + -- + beginCaptures + + 0 + + name + punctuation.definition.comment.sql + + + end + \n + name + comment.line.double-dash.sql + + + + + begin + (^[ \t]+)?(?=#) + beginCaptures + + 1 + + name + punctuation.whitespace.comment.leading.sql + + + end + (?!\G) + patterns + + + begin + # + beginCaptures + + 0 + + name + punctuation.definition.comment.sql + + + end + \n + name + comment.line.number-sign.sql + + + + + begin + /\* + captures + + 0 + + name + punctuation.definition.comment.sql + + + end + \*/ + name + comment.block.c + + + + regexps + + patterns + + + begin + /(?=\S.*/) + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + end + / + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.regexp.sql + patterns + + + include + #string_interpolation + + + match + \\/ + name + constant.character.escape.slash.sql + + + + + begin + %r\{ + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + comment + We should probably handle nested bracket pairs!?! -- Allan + end + \} + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.regexp.modr.sql + patterns + + + include + #string_interpolation + + + + + + string_escape + + match + \\. + name + constant.character.escape.sql + + string_interpolation + + captures + + 1 + + name + punctuation.definition.string.begin.sql + + 3 + + name + punctuation.definition.string.end.sql + + + match + (#\{)([^\}]*)(\}) + name + string.interpolated.sql + + strings + + patterns + + + captures + + 1 + + name + punctuation.definition.string.begin.sql + + 2 + + name + punctuation.definition.string.end.sql + + + comment + this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines. + match + (')[^']*(') + name + string.quoted.single.sql + + + begin + ' + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + end + ' + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.quoted.single.sql + patterns + + + include + #string_escape + + + + + captures + + 1 + + name + punctuation.definition.string.begin.sql + + 2 + + name + punctuation.definition.string.end.sql + + + comment + this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines. + match + (`)[^`\\]*(`) + name + string.quoted.other.backtick.sql + + + begin + ` + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + end + ` + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.quoted.other.backtick.sql + patterns + + + include + #string_escape + + + + + captures + + 1 + + name + punctuation.definition.string.begin.sql + + 2 + + name + punctuation.definition.string.end.sql + + + comment + this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines. + match + (")[^"#]*(") + name + string.quoted.double.sql + + + begin + " + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + end + " + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.quoted.double.sql + patterns + + + include + #string_interpolation + + + + + begin + %\{ + beginCaptures + + 0 + + name + punctuation.definition.string.begin.sql + + + end + \} + endCaptures + + 0 + + name + punctuation.definition.string.end.sql + + + name + string.other.quoted.brackets.sql + patterns + + + include + #string_interpolation + + + + + + + scopeName + source.sql + uuid + C49120AC-6ECC-11D9-ACC8-000D93589AF6 + + diff --git a/extensions/sql/test/colorize-fixtures/test.sql b/extensions/sql/test/colorize-fixtures/test.sql new file mode 100644 index 0000000000..033eeab921 --- /dev/null +++ b/extensions/sql/test/colorize-fixtures/test.sql @@ -0,0 +1,6 @@ +CREATE VIEW METRIC_STATS (ID, MONTH, TEMP_C, RAIN_C) AS +SELECT ID, +MONTH, +(TEMP_F - 32) * 5 /9, +RAIN_I * 0.3937 +FROM STATS; \ No newline at end of file diff --git a/extensions/sql/test/colorize-results/test_sql.json b/extensions/sql/test/colorize-results/test_sql.json new file mode 100644 index 0000000000..7b133d4de6 --- /dev/null +++ b/extensions/sql/test/colorize-results/test_sql.json @@ -0,0 +1,332 @@ +[ + { + "c": "CREATE", + "t": "source.sql meta.create.sql keyword.other.create.sql", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.sql meta.create.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "VIEW", + "t": "source.sql meta.create.sql keyword.other.sql", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.sql meta.create.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "METRIC_STATS", + "t": "source.sql meta.create.sql entity.name.function.sql", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": " (ID, MONTH, TEMP_C, RAIN_C) ", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "AS", + "t": "source.sql keyword.other.alias.sql", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "SELECT", + "t": "source.sql keyword.other.DML.sql", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ID,", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "MONTH,", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(TEMP_F ", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.sql keyword.operator.math.sql", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "32", + "t": "source.sql constant.numeric.sql", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ") ", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.sql keyword.operator.star.sql", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "5", + "t": "source.sql constant.numeric.sql", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/", + "t": "source.sql keyword.operator.math.sql", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "9", + "t": "source.sql constant.numeric.sql", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "RAIN_I ", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.sql keyword.operator.star.sql", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "0", + "t": "source.sql constant.numeric.sql", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ".", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "3937", + "t": "source.sql constant.numeric.sql", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "FROM", + "t": "source.sql keyword.other.DML.sql", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " STATS;", + "t": "source.sql", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + } +] \ No newline at end of file diff --git a/extensions/theme-abyss/OSSREADME.json b/extensions/theme-abyss/OSSREADME.json new file mode 100644 index 0000000000..ddb630bfd9 --- /dev/null +++ b/extensions/theme-abyss/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "Colorsublime-Themes", + "version": "0.1.0", + "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", + "description": "The themes in this folders are copied from colorsublime.com. <<>>" +}] diff --git a/extensions/theme-abyss/package.json b/extensions/theme-abyss/package.json new file mode 100644 index 0000000000..dd1aa7b31a --- /dev/null +++ b/extensions/theme-abyss/package.json @@ -0,0 +1,15 @@ +{ + "name": "theme-abyss", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "themes": [ + { + "label": "Abyss", + "uiTheme": "vs-dark", + "path": "./themes/abyss-color-theme.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-abyss/themes/Abyss.tmTheme b/extensions/theme-abyss/themes/Abyss.tmTheme new file mode 100644 index 0000000000..953fd645af --- /dev/null +++ b/extensions/theme-abyss/themes/Abyss.tmTheme @@ -0,0 +1,339 @@ + + + + + name + Ofer 1 + settings + + + settings + + background + #000c18 + caret + #ddbb88 + foreground + #6688cc + invisibles + #002040 + lineHighlight + #082050 + selection + #770811 + guide + #002952 + + + + name + Comment + scope + comment + settings + + foreground + #223355 + + + + name + String + scope + string + settings + + foreground + #22aa44 + + + + name + Number + scope + constant.numeric + settings + + foreground + #f280d0 + + + + name + Built-in constant + scope + constant.language + settings + + foreground + #f280d0 + + + + name + User-defined constant + scope + constant.character, constant.other + settings + + foreground + #f280d0 + + + + name + Variable + scope + variable + settings + + fontStyle + + + + + name + Keyword + scope + keyword + settings + + foreground + #225588 + + + + name + Storage + scope + storage + settings + + fontStyle + + foreground + #225588 + + + + name + Storage type + scope + storage.type + settings + + fontStyle + italic + foreground + #9966b8 + + + + name + Class name + scope + entity.name.class, entity.name.type + settings + + fontStyle + underline + foreground + #ffeebb + + + + name + Inherited class + scope + entity.other.inherited-class + settings + + fontStyle + italic underline + foreground + #ddbb88 + + + + name + Function name + scope + entity.name.function + settings + + fontStyle + + foreground + #ddbb88 + + + + name + Function argument + scope + variable.parameter + settings + + fontStyle + italic + foreground + #2277ff + + + + name + Tag name + scope + entity.name.tag + settings + + fontStyle + + foreground + #225588 + + + + name + Tag attribute + scope + entity.other.attribute-name + settings + + fontStyle + + foreground + #ddbb88 + + + + name + Library function + scope + support.function + settings + + fontStyle + + foreground + #9966b8 + + + + name + Library constant + scope + support.constant + settings + + fontStyle + + foreground + #9966b8 + + + + name + Library class/type + scope + support.type, support.class + settings + + fontStyle + italic + foreground + #9966b8 + + + + name + Library variable + scope + support.other.variable + settings + + fontStyle + + + + + name + Invalid + scope + invalid + settings + + background + #F92672 + fontStyle + + foreground + #F8F8F0 + + + + name + Invalid deprecated + scope + invalid.deprecated + settings + + background + #AE81FF + foreground + #F8F8F0 + + + + name + Markup Quote + scope + markup.quote + settings + + foreground + #22aa44 + + + + name + Markup Styling + scope + markup.bold, markup.italic + settings + + foreground + #22aa44 + + + + name + Markup Inline + scope + markup.inline.raw + settings + + fontStyle + + foreground + #9966b8 + + + + name + Markup Setext Header + scope + markup.heading.setext + settings + + fontStyle + + foreground + #ddbb88 + + + + uuid + 96A92F4B-E068-4DC6-9635-F55E7D920420 + + diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json new file mode 100644 index 0000000000..9901ee0d6b --- /dev/null +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -0,0 +1,434 @@ +{ + "name": "Abyss", + "tokenColors": [ + { + "settings": { + "background": "#000c18", + "foreground": "#6688cc" + } + }, + { + "name": "Comment", + "scope": "comment", + "settings": { + "foreground": "#384887" + } + }, + { + "name": "String", + "scope": "string", + "settings": { + "foreground": "#22aa44" + } + }, + { + "name": "Number", + "scope": "constant.numeric", + "settings": { + "foreground": "#f280d0" + } + }, + { + "name": "Built-in constant", + "scope": "constant.language", + "settings": { + "foreground": "#f280d0" + } + }, + { + "name": "User-defined constant", + "scope": [ + "constant.character", + "constant.other" + ], + "settings": { + "foreground": "#f280d0" + } + }, + { + "name": "Variable", + "scope": "variable", + "settings": { + "fontStyle": "" + } + }, + { + "name": "Keyword", + "scope": "keyword", + "settings": { + "foreground": "#225588" + } + }, + { + "name": "Storage", + "scope": "storage", + "settings": { + "fontStyle": "", + "foreground": "#225588" + } + }, + { + "name": "Storage type", + "scope": "storage.type", + "settings": { + "fontStyle": "italic", + "foreground": "#9966b8" + } + }, + { + "name": "Class name", + "scope": [ + "entity.name.class", + "entity.name.type" + ], + "settings": { + "fontStyle": "underline", + "foreground": "#ffeebb" + } + }, + { + "name": "Inherited class", + "scope": "entity.other.inherited-class", + "settings": { + "fontStyle": "italic underline", + "foreground": "#ddbb88" + } + }, + { + "name": "Function name", + "scope": "entity.name.function", + "settings": { + "fontStyle": "", + "foreground": "#ddbb88" + } + }, + { + "name": "Function argument", + "scope": "variable.parameter", + "settings": { + "fontStyle": "italic", + "foreground": "#2277ff" + } + }, + { + "name": "Tag name", + "scope": "entity.name.tag", + "settings": { + "fontStyle": "", + "foreground": "#225588" + } + }, + { + "name": "Tag attribute", + "scope": "entity.other.attribute-name", + "settings": { + "fontStyle": "", + "foreground": "#ddbb88" + } + }, + { + "name": "Library function", + "scope": "support.function", + "settings": { + "fontStyle": "", + "foreground": "#9966b8" + } + }, + { + "name": "Library constant", + "scope": "support.constant", + "settings": { + "fontStyle": "", + "foreground": "#9966b8" + } + }, + { + "name": "Library class/type", + "scope": [ + "support.type", + "support.class" + ], + "settings": { + "fontStyle": "italic", + "foreground": "#9966b8" + } + }, + { + "name": "Library variable", + "scope": "support.other.variable", + "settings": { + "fontStyle": "" + } + }, + { + "name": "Invalid", + "scope": "invalid", + "settings": { + "background": "#F92672", + "fontStyle": "", + "foreground": "#F8F8F0" + } + }, + { + "name": "Invalid deprecated", + "scope": "invalid.deprecated", + "settings": { + "background": "#AE81FF", + "foreground": "#F8F8F0" + } + }, + { + "name": "diff: header", + "scope": [ + "meta.diff", + "meta.diff.header" + ], + "settings": { + "background": "#b58900", + "fontStyle": "italic", + "foreground": "#E0EDDD" + } + }, + { + "name": "diff: deleted", + "scope": "markup.deleted", + "settings": { + "background": "#eee8d5", + "fontStyle": "", + "foreground": "#dc322f" + } + }, + { + "name": "diff: changed", + "scope": "markup.changed", + "settings": { + "background": "#eee8d5", + "fontStyle": "", + "foreground": "#cb4b16" + } + }, + { + "name": "diff: inserted", + "scope": "markup.inserted", + "settings": { + "background": "#eee8d5", + "foreground": "#219186" + } + }, + { + "name": "Markup Quote", + "scope": "markup.quote", + "settings": { + "foreground": "#22aa44" + } + }, + { + "name": "Markup Styling", + "scope": [ + "markup.bold", + "markup.italic" + ], + "settings": { + "foreground": "#22aa44" + } + }, + { + "name": "Markup Inline", + "scope": "markup.inline.raw", + "settings": { + "fontStyle": "", + "foreground": "#9966b8" + } + }, + { + "name": "Markup Setext Header", + "scope": "markup.heading.setext", + "settings": { + "fontStyle": "", + "foreground": "#ddbb88" + } + } + ], + "colors": { + + // Base + // "foreground": "", + "focusBorder": "#596F99", + // "contrastActiveBorder": "", + // "contrastBorder": "", + + // "widget.shadow": "", + + "input.background": "#181f2f", + // "input.border": "", + // "input.foreground": "", + "inputOption.activeBorder": "#1D4A87", + "inputValidation.infoBorder": "#384078", + "inputValidation.infoBackground": "#051336", + "inputValidation.warningBackground": "#5B7E7A", + "inputValidation.warningBorder": "#5B7E7A", + "inputValidation.errorBackground": "#A22D44", + "inputValidation.errorBorder": "#AB395B", + + "badge.background": "#0063a5", + "progressBar.background": "#0063a5", + + "dropdown.background": "#181f2f", + // "dropdown.foreground": "", + // "dropdown.border": "", + + "button.background": "#2B3C5D", + // "button.foreground": "", + + "list.activeSelectionBackground": "#08286b", + // "list.activeSelectionForeground": "", + "list.focusBackground": "#08286b", + "list.hoverBackground": "#061940", + "list.inactiveSelectionBackground": "#152037", + "list.dropBackground": "#041D52", + "list.highlightForeground": "#0063a5", + + "scrollbar.shadow": "#515E91AA", + "scrollbarSlider.activeBackground": "#3B3F5188", + "scrollbarSlider.background": "#1F2230AA", + "scrollbarSlider.hoverBackground": "#3B3F5188", + + // Editor + "editor.background": "#000c18", + // "editor.foreground": "#6688cc", + "editorWidget.background": "#262641", + "editorCursor.foreground": "#ddbb88", + "editorWhitespace.foreground": "#103050", + "editor.lineHighlightBackground": "#082050", + "editor.selectionBackground": "#770811", + "editorIndentGuide.background": "#002952", + "editorHoverWidget.background": "#000c38", + "editorHoverWidget.border": "#004c18", + "editorLineNumber.foreground": "#406385", + "editorMarkerNavigation.background": "#060621", + "editorMarkerNavigationError.background": "#AB395B", + "editorMarkerNavigationWarning.background": "#5B7E7A", + "editorLink.activeForeground": "#0063a5", + // "editor.findMatchBackground": "", + "editor.findMatchHighlightBackground": "#eeeeee44", + // "editor.findRangeHighlightBackground": "", + // "editor.hoverHighlightBackground": "", + // "editor.inactiveSelectionBackground": "", + // "editor.lineHighlightBorder": "", + // "editor.rangeHighlightBackground": "", + // "editor.selectionHighlightBackground": "", + // "editor.wordHighlightBackground": "", + // "editor.wordHighlightStrongBackground": "", + + // Editor: Suggest Widget + // "editorSuggestWidget.background": "", + // "editorSuggestWidget.border": "", + // "editorSuggestWidget.foreground": "", + // "editorSuggestWidget.highlightForeground": "", + // "editorSuggestWidget.selectedBackground": "", + + // Editor: Peek View + "peekViewResult.background": "#060621", + // "peekViewResult.lineForeground": "", + // "peekViewResult.selectionBackground": "", + // "peekViewResult.selectionForeground": "", + "peekViewEditor.background": "#10192c", + "peekViewTitle.background": "#10192c", + "peekView.border": "#2b2b4a", + "peekViewEditor.matchHighlightBackground": "#eeeeee33", + // "peekViewResult.fileForeground": "", + "peekViewResult.matchHighlightBackground": "#eeeeee44", + // "peekViewTitleLabel.foreground": "", + // "peekViewTitleDescription.foreground": "", + + // Editor: Diff + "diffEditor.insertedTextBackground": "#31958A55", + // "diffEditor.insertedTextBorder": "", + "diffEditor.removedTextBackground": "#892F4688", + // "diffEditor.removedTextBorder": "", + + // Workbench: Title + "titleBar.activeBackground": "#10192c", + // "titleBar.activeForeground": "", + // "titleBar.inactiveBackground": "", + // "titleBar.inactiveForeground": "", + + // Workbench: Editors + // "editorGroupHeader.noTabsBackground": "", + "editorGroup.border": "#2b2b4a", + "editorGroup.background": "#1c1c2a", + "editorGroup.dropBackground": "#25375daa", + "editorGroupHeader.tabsBackground": "#1c1c2a", + + // Workbench: Tabs + "tab.border": "#2b2b4a", + // "tab.activeBackground": "", + "tab.inactiveBackground": "#10192c", + // "tab.activeForeground": "", + // "tab.inactiveForeground": "", + + // Workbench: Activity Bar + "activityBar.background": "#051336", + // "activityBar.foreground": "", + // "activityBarBadge.background": "", + // "activityBarBadge.foreground": "", + // "activityBar.dropBackground": "", + + // Workbench: Panel + // "panel.background": "", + "panel.border": "#2b2b4a", + // "panelTitle.activeBorder": "", + // "panelTitle.activeForeground": "", + // "panelTitle.inactiveForeground": "", + + // Workbench: Side Bar + "sideBar.background": "#060621", + // "sideBarTitle.foreground": "", + "sideBarSectionHeader.background": "#10192c", + + // Workbench: Status Bar + "statusBar.background": "#10192c", + "statusBar.noFolderBackground": "#10192c", + "statusBar.debuggingBackground": "#10192c", + // "statusBar.foreground": "", + "statusBarItem.prominentBackground": "#0063a5", + "statusBarItem.prominentHoverBackground": "#0063a5dd", + // "statusBarItem.activeBackground": "", + // "statusBarItem.hoverBackground": "", + + // Workbench: Debug + "debugToolBar.background": "#051336", + "debugExceptionWidget.background": "#051336", + "debugExceptionWidget.border": "#AB395B", + + // Workbench: Notifications + "notification.background": "#182543", + // "notification.foreground": "", + + // Workbench: Quick Open + "pickerGroup.border": "#596F99", + "pickerGroup.foreground": "#596F99", + + // Workbench: Extensions + "extensionButton.prominentBackground": "#5f8b3b", + "extensionButton.prominentHoverBackground": "#5f8b3bbb", + + // Workbench: Terminal + "terminal.ansiBlack": "#111111", + "terminal.ansiRed": "#ff9da4", + "terminal.ansiGreen": "#d1f1a9", + "terminal.ansiYellow": "#ffeead", + "terminal.ansiBlue": "#bbdaff", + "terminal.ansiMagenta": "#ebbbff", + "terminal.ansiCyan": "#99ffff", + "terminal.ansiWhite": "#cccccc", + "terminal.ansiBrightBlack": "#333333", + "terminal.ansiBrightRed": "#ff7882", + "terminal.ansiBrightGreen": "#b8f171", + "terminal.ansiBrightYellow": "#ffe580", + "terminal.ansiBrightBlue": "#80baff", + "terminal.ansiBrightMagenta": "#d778ff", + "terminal.ansiBrightCyan": "#78ffff", + "terminal.ansiBrightWhite": "#ffffff" + } +} \ No newline at end of file diff --git a/extensions/theme-carbon/package.json b/extensions/theme-carbon/package.json new file mode 100644 index 0000000000..3792355e9e --- /dev/null +++ b/extensions/theme-carbon/package.json @@ -0,0 +1,29 @@ +{ + "name": "theme-carbon", + "displayName": "SQL Operations Studio Themes", + "description": "The light and dark themes for SQL Operations Studio", + "version": "0.1.0", + "publisher": "sql", + "engines": { + "vscode": "*" + }, + "categories": [ + "Themes" + ], + "contributes": { + "themes": [ + { + "id": "Default Light SQL Operations Studio", + "label": "Light SQL Operations Studio (default light)", + "uiTheme": "vs", + "path": "./themes/light_carbon.json" + }, + { + "id": "Default Dark SQL Operations Studio", + "label": "Dark SQL Operations Studio (default dark)", + "uiTheme": "vs-dark", + "path": "./themes/dark_carbon.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-carbon/themes/dark_carbon.json b/extensions/theme-carbon/themes/dark_carbon.json new file mode 100644 index 0000000000..2dc0cf8dc9 --- /dev/null +++ b/extensions/theme-carbon/themes/dark_carbon.json @@ -0,0 +1,526 @@ +{ + "name": "SQL Operations Studio-dark-theme", + "type": "dark", + "colors": { + + // base + "foreground": "#fffffe", + "focusBorder": "#0E639C", + "selection.background": "#3062d6", + + //text colors + "textLinkForeground": "#30B4FF", + "textLinkActiveForeground": "#30B4FF", + + //Button control + "button.background": "#00BCF2", + "button.foreground": "#212121", + "button.hoverBackground": "#0099BC", + + // TODO add support for these + // "button.secondaryBackground": "#c8c8c8", + // "button.secondaryHoverBackground": "#a6a6a6", + // "button.secondaryForeground": "#333333", + // "button.disabledBackground": "#444444" , + // "button.disabledForeground": "#888888" , + + //Dropdown Control + "dropdown.background": "#333333", + "dropdown.foreground": "#fffffe", + "dropdown.border": "#888888", + + //Input Control + "input.background": "#212121", + "input.border": "#888888", + "input.disabled.background": "#444444", + "input.disabled.foreground": "#888888", + "inputOption.activeBorder": "#007ACC", + "input.placeholderForeground": "#888888", + "inputValidation.errorBackground": "#D02E00", + "inputValidation.errorBorder": "#D02E00", + + //List and trees + "list.activeSelectionBackground": "#3062d6", + "list.hoverBackground": "#444444", + "pickerGroup.border": "#00BCF2", + "activityBar.background": "#444444", + "sideBar.background": "#333333", + "editorGroupHeader.tabsBackground": "#444444", + "editor.background": "#212121", + "editor.foreground": "#ffffff", + "editorWidget.background": "#444444", + "editorLink.activeForeground": "#30B4FF", + "editorGroup.border": "#333333", + "editorGroup.background": "#212121", + "tab.activeBackground": "#212121", + "tab.activeForeground": "#ffffff", + "tab.inactiveBackground": "#444444", + "tab.inactiveForeground": "#888888", + "tab.border": "#3c3c3c", + "panel.background": "#212121", + "panel.border": "#515151", + "panelTitle.activeForeground": "#ffffff", + "panelTitle.inactiveForeground": "#888888" + }, + "tokenColors": [ + { + "settings": { + "foreground": "#ffffffff", + "background": "#212121ff" + } + }, + { + "scope": "emphasis", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "strong", + "settings": { + "fontStyle": "bold" + } + }, + { + "scope": "header", + "settings": { + "foreground": "#000080" + } + }, + { + "scope": "comment", + "settings": { + "foreground": "#608b4e" + } + }, + { + "scope": "constant.language", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": [ + "constant.numeric" + ], + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "constant.regexp", + "settings": { + "foreground": "#646695" + } + }, + { + "scope": "entity.name.tag", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "entity.name.tag.css", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": [ + "entity.other.attribute-name.class.css", + "entity.other.attribute-name.class.mixin.css", + "entity.other.attribute-name.id.css", + "entity.other.attribute-name.parent-selector.css", + "entity.other.attribute-name.pseudo-class.css", + "entity.other.attribute-name.pseudo-element.css", + "source.css.less entity.other.attribute-name.id", + "entity.other.attribute-name.attribute.scss", + "entity.other.attribute-name.scss" + ], + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "scope": "invalid", + "settings": { + "foreground": "#f44747" + } + }, + { + "scope": "markup.underline", + "settings": { + "fontStyle": "underline" + } + }, + { + "scope": "markup.bold", + "settings": { + "fontStyle": "bold", + "foreground": "#569cd6" + } + }, + { + "scope": "markup.heading", + "settings": { + "fontStyle": "bold", + "foreground": "#569cd6" + } + }, + { + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "markup.inserted", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "markup.deleted", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "markup.changed", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "beginning.punctuation.definition.quote.markdown", + "settings": { + "foreground": "#608b4e" + } + }, + { + "scope": "beginning.punctuation.definition.list.markdown", + "settings": { + "foreground": "#6796e6" + } + }, + { + "scope": "markup.inline.raw", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "meta.selector", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "name": "brackets of XML/HTML tags", + "scope": "punctuation.definition.tag", + "settings": { + "foreground": "#808080" + } + }, + { + "scope": "meta.preprocessor", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "meta.preprocessor.string", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "meta.preprocessor.numeric", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "meta.structure.dictionary.key.python", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "meta.diff.header", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage.type", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage.modifier", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "string", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.tag", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.value", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.regexp", + "settings": { + "foreground": "#d16969" + } + }, + { + "name": "JavaScript string interpolation ${}", + "scope": [ + "punctuation.definition.template-expression.begin.js", + "punctuation.definition.template-expression.begin.ts", + "punctuation.definition.template-expression.end.ts", + "punctuation.definition.template-expression.end.js" + ], + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": [ + "support.type.vendored.property-name", + "support.type.property-name", + "variable.css", + "variable.scss", + "variable.other.less" + ], + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "keyword", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.control", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "scope": [ + "keyword.operator.new", + "keyword.operator.expression", + "keyword.operator.cast", + "keyword.operator.sizeof", + "keyword.operator.logical.python" + ], + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.other.unit", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": [ + "punctuation.section.embedded.begin.php", + "punctuation.section.embedded.end.php" + ], + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "support.function.git-rebase", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "constant.sha.git-rebase", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "name": "coloring of the Java import and package identifiers", + "scope": [ + "storage.modifier.import.java", + "variable.language.wildcard.java", + "storage.modifier.package.java" + ], + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "name": "this.self", + "scope": "variable.language", + "settings": { + "foreground": "#569cd6" + } + }, + { + "name": "Function declarations", + "scope": [ + "entity.name.function", + "support.function", + "support.constant.handlebars" + ], + "settings": { + "foreground": "#DCDCAA" + } + }, + { + "name": "Types declaration and references", + "scope": [ + "meta.return-type", + "support.class", + "support.type", + "entity.name.type", + "entity.name.class", + "storage.type.cs", + "storage.type.generic.cs", + "storage.type.modifier.cs", + "storage.type.variable.cs", + "storage.type.annotation.java", + "storage.type.generic.java", + "storage.type.java", + "storage.type.object.array.java", + "storage.type.primitive.array.java", + "storage.type.primitive.java", + "storage.type.token.java", + "storage.type.groovy", + "storage.type.annotation.groovy", + "storage.type.parameters.groovy", + "storage.type.generic.groovy", + "storage.type.object.array.groovy", + "storage.type.primitive.array.groovy", + "storage.type.primitive.groovy" + ], + "settings": { + "foreground": "#4EC9B0" + } + }, + { + "name": "Types declaration and references, TS grammar specific", + "scope": [ + "meta.type.cast.expr", + "meta.type.new.expr", + "support.constant.math", + "support.constant.dom", + "support.constant.json", + "entity.other.inherited-class" + ], + "settings": { + "foreground": "#4EC9B0" + } + }, + { + "name": "Control flow keywords", + "scope": "keyword.control", + "settings": { + "foreground": "#C586C0" + } + }, + { + "name": "Variable and parameter name", + "scope": [ + "variable", + "meta.definition.variable.name", + "support.variable" + ], + "settings": { + "foreground": "#9CDCFE" + } + }, + { + "name": "Object keys, TS grammar specific", + "scope": [ + "meta.object-literal.key", + "meta.object-literal.key entity.name.function" + ], + "settings": { + "foreground": "#9CDCFE" + } + }, + { + "name": "CSS property value", + "scope": [ + "support.constant.property-value", + "support.constant.font-name", + "support.constant.media-type", + "support.constant.media", + "constant.other.color.rgb-value", + "constant.other.rgb-value", + "support.constant.color" + ], + "settings": { + "foreground": "#CE9178" + } + }, + { + "scope": "token.info-token", + "settings": { + "foreground": "#6796e6" + } + }, + { + "scope": "token.warn-token", + "settings": { + "foreground": "#cd9731" + } + }, + { + "scope": "token.error-token", + "settings": { + "foreground": "#f44747" + } + }, + { + "scope": "token.debug-token", + "settings": { + "foreground": "#b267e6" + } + } + ] +} \ No newline at end of file diff --git a/extensions/theme-carbon/themes/light_carbon.json b/extensions/theme-carbon/themes/light_carbon.json new file mode 100644 index 0000000000..5e2b19ceb2 --- /dev/null +++ b/extensions/theme-carbon/themes/light_carbon.json @@ -0,0 +1,560 @@ +{ + "name": "SQL Operations Studio-light-theme", + "type": "light", + "colors": { + // base + "foreground": "#4a4a4a", + "focusBorder": "#00BCF2", + "selection.background": "#C9D0D9", + "widget.shadow": "#666666", + + // text colors + "textLinkForeground": "#3062D6", + "textLinkActiveForeground": "#3062D6", + + //Button control + "button.background": "#00BCF2", + "button.foreground": "#212121", + "button.hoverBackground": "#0099BC", + + // TODO add support for these + // "button.secondaryBackground": "#c8c8c8", + // "button.secondaryHoverBackground": "#a6a6a6", + // "button.secondaryForeground": "#333333", + // "button.disabledBackground": "#eaeaea", + // "button.disabledForeground": "#888888", + + //Dropdown Control + "dropdown.background": "#EAEAEA00", + "dropdown.foreground": "#4a4a4a", + "dropdown.border": "#C8C8C8", + + //badge + "badge.background": "#777777", + "badge.foreground": "#ffffff", + + //Input Control + "input.background": "#EAEAEA00", + "input.border": "#c8c8c8", + "input.disabled.background": "#dcdcdc", + "input.disabled.foreground": "#888888", + "inputOption.activeBorder": "#666666", + "input.placeholderForeground": "#888888", + "inputValidation.errorBackground": "#ffeaea", + "inputValidation.errorBorder": "#f1897f", + + //List and tree + "list.activeSelectionBackground": "#3062d6", + "list.hoverBackground": "#dcdcdc", + "pickerGroup.border": "#00BCF2", + + // Workbench: Activity Bar + "activityBar.background": "#212121", + + // Workbench: Side Bar + "sideBar.background": "#EAEAEA", + "editorGroupHeader.tabsBackground": "#f4f4f4", + "editor.background": "#fffffe", + "editor.foreground": "#212121", + "editorWidget.background": "#C8C8C8", + "editorLink.activeForeground": "#3062D6", + "editorGroup.border": "#f4f4f4", + "editorGroup.background": "#fffffe", + + // Workbench: Tabs + "tab.activeBackground": "#FFFFFE", + "tab.activeForeground": "#4A4A4A", + "tab.inactiveBackground": "#f4f4f4", + "tab.inactiveForeground": "#888888", + "tab.border": "#EAEAEA", + "tab.unfocusedInactiveForeground": "#888888", + "tab.unfocusedActiveForeground": "#212121", + "panel.background": "#FFFFFE", + "panel.border": "#C8C8C8", + "panelTitle.activeForeground": "#212121", + "panelTitle.inactiveForeground": "#888888" + }, + "tokenColors": [ + { + "settings": { + "foreground": "#212121ff", + "background": "#fffffeff" + } + }, + { + "scope": "emphasis", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "strong", + "settings": { + "fontStyle": "bold" + } + }, + { + "scope": "meta.diff.header", + "settings": { + "foreground": "#000080" + } + }, + { + "scope": "comment", + "settings": { + "foreground": "#008000" + } + }, + { + "scope": "constant.language", + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": [ + "constant.numeric" + ], + "settings": { + "foreground": "#09885a" + } + }, + { + "scope": "constant.regexp", + "settings": { + "foreground": "#811f3f" + } + }, + { + "name": "css tags in selectors, xml tags", + "scope": "entity.name.tag", + "settings": { + "foreground": "#800000" + } + }, + { + "scope": "entity.name.selector", + "settings": { + "foreground": "#800000" + } + }, + { + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#ff0000" + } + }, + { + "scope": [ + "entity.other.attribute-name.class.css", + "entity.other.attribute-name.class.mixin.css", + "entity.other.attribute-name.id.css", + "entity.other.attribute-name.parent-selector.css", + "entity.other.attribute-name.pseudo-class.css", + "entity.other.attribute-name.pseudo-element.css", + "source.css.less entity.other.attribute-name.id", + "entity.other.attribute-name.attribute.scss", + "entity.other.attribute-name.scss" + ], + "settings": { + "foreground": "#800000" + } + }, + { + "scope": "invalid", + "settings": { + "foreground": "#cd3131" + } + }, + { + "scope": "markup.underline", + "settings": { + "fontStyle": "underline" + } + }, + { + "scope": "markup.bold", + "settings": { + "fontStyle": "bold", + "foreground": "#000080" + } + }, + { + "scope": "markup.heading", + "settings": { + "fontStyle": "bold", + "foreground": "#800000" + } + }, + { + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "markup.inserted", + "settings": { + "foreground": "#09885a" + } + }, + { + "scope": "markup.deleted", + "settings": { + "foreground": "#a31515" + } + }, + { + "scope": "markup.changed", + "settings": { + "foreground": "#0451a5" + } + }, + { + "scope": [ + "beginning.punctuation.definition.quote.markdown", + "beginning.punctuation.definition.list.markdown" + ], + "settings": { + "foreground": "#0451a5" + } + }, + { + "scope": "markup.inline.raw", + "settings": { + "foreground": "#800000" + } + }, + { + "scope": "meta.selector", + "settings": { + "foreground": "#800000" + } + }, + { + "name": "brackets of XML/HTML tags", + "scope": "punctuation.definition.tag", + "settings": { + "foreground": "#800000" + } + }, + { + "scope": "meta.preprocessor", + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": "meta.preprocessor.string", + "settings": { + "foreground": "#a31515" + } + }, + { + "scope": "meta.preprocessor.numeric", + "settings": { + "foreground": "#09885a" + } + }, + { + "scope": "meta.structure.dictionary.key.python", + "settings": { + "foreground": "#0451a5" + } + }, + { + "scope": "storage", + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": "storage.type", + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": "storage.modifier", + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": "string", + "settings": { + "foreground": "#a31515" + } + }, + { + "scope": [ + "string.comment.buffered.block.jade", + "string.quoted.jade", + "string.interpolated.jade", + "string.unquoted.plain.in.yaml", + "string.unquoted.plain.out.yaml", + "string.unquoted.block.yaml", + "string.quoted.single.yaml", + "string.quoted.double.xml", + "string.quoted.single.xml", + "string.unquoted.cdata.xml", + "string.quoted.double.html", + "string.quoted.single.html", + "string.unquoted.html", + "string.quoted.single.handlebars", + "string.quoted.double.handlebars" + ], + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": "string.regexp", + "settings": { + "foreground": "#811f3f" + } + }, + { + "name": "JavaScript string interpolation ${}", + "scope": [ + "punctuation.definition.template-expression.begin.js", + "punctuation.definition.template-expression.begin.ts", + "punctuation.definition.template-expression.end.ts", + "punctuation.definition.template-expression.end.js" + ], + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": [ + "support.constant.property-value", + "support.constant.font-name", + "support.constant.media-type", + "support.constant.media", + "constant.other.color.rgb-value", + "constant.other.rgb-value", + "support.constant.color" + ], + "settings": { + "foreground": "#0451a5" + } + }, + { + "scope": [ + "support.type.vendored.property-name", + "support.type.property-name", + "variable.css", + "variable.scss", + "variable.other.less" + ], + "settings": { + "foreground": "#ff0000" + } + }, + { + "scope": "support.type.property-name.json", + "settings": { + "foreground": "#0451a5" + } + }, + { + "scope": "keyword", + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": "keyword.control", + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#000000" + } + }, + { + "scope": [ + "keyword.operator.new", + "keyword.operator.expression", + "keyword.operator.cast", + "keyword.operator.sizeof", + "keyword.operator.logical.python" + ], + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": "keyword.other.unit", + "settings": { + "foreground": "#09885a" + } + }, + { + "scope": [ + "punctuation.section.embedded.begin.php", + "punctuation.section.embedded.end.php" + ], + "settings": { + "foreground": "#800000" + } + }, + { + "scope": "support.function.git-rebase", + "settings": { + "foreground": "#0451a5" + } + }, + { + "scope": "constant.sha.git-rebase", + "settings": { + "foreground": "#09885a" + } + }, + { + "name": "coloring of the Java import and package identifiers", + "scope": [ + "storage.modifier.import.java", + "variable.language.wildcard.java", + "storage.modifier.package.java" + ], + "settings": { + "foreground": "#000000" + } + }, + { + "name": "this.self", + "scope": "variable.language", + "settings": { + "foreground": "#0000ff" + } + }, + { + "name": "Function declarations", + "scope": [ + "entity.name.function", + "support.function", + "support.constant.handlebars" + ], + "settings": { + "foreground": "#795E26" + } + }, + { + "name": "Types declaration and references", + "scope": [ + "meta.return-type", + "support.class", + "support.type", + "entity.name.type", + "entity.name.class", + "storage.type.cs", + "storage.type.generic.cs", + "storage.type.modifier.cs", + "storage.type.variable.cs", + "storage.type.annotation.java", + "storage.type.generic.java", + "storage.type.java", + "storage.type.object.array.java", + "storage.type.primitive.array.java", + "storage.type.primitive.java", + "storage.type.token.java", + "storage.type.groovy", + "storage.type.annotation.groovy", + "storage.type.parameters.groovy", + "storage.type.generic.groovy", + "storage.type.object.array.groovy", + "storage.type.primitive.array.groovy", + "storage.type.primitive.groovy" + ], + "settings": { + "foreground": "#267f99" + } + }, + { + "name": "Types declaration and references, TS grammar specific", + "scope": [ + "meta.type.cast.expr", + "meta.type.new.expr", + "support.constant.math", + "support.constant.dom", + "support.constant.json", + "entity.other.inherited-class" + ], + "settings": { + "foreground": "#267f99" + } + }, + { + "name": "Control flow keywords", + "scope": "keyword.control", + "settings": { + "foreground": "#AF00DB" + } + }, + { + "name": "Variable and parameter name", + "scope": [ + "variable", + "meta.definition.variable.name", + "support.variable" + ], + "settings": { + "foreground": "#001080" + } + }, + { + "name": "Object keys, TS grammar specific", + "scope": [ + "meta.object-literal.key", + "meta.object-literal.key entity.name.function" + ], + "settings": { + "foreground": "#001080" + } + }, + { + "name": "CSS property value", + "scope": [ + "support.constant.property-value", + "support.constant.font-name", + "support.constant.media-type", + "support.constant.media", + "constant.other.color.rgb-value", + "constant.other.rgb-value", + "support.constant.color" + ], + "settings": { + "foreground": "#0451a5" + } + }, + { + "scope": "token.info-token", + "settings": { + "foreground": "#316bcd" + } + }, + { + "scope": "token.warn-token", + "settings": { + "foreground": "#cd9731" + } + }, + { + "scope": "token.error-token", + "settings": { + "foreground": "#cd3131" + } + }, + { + "scope": "token.debug-token", + "settings": { + "foreground": "#800080" + } + } + ] +} \ No newline at end of file diff --git a/extensions/theme-defaults/package.json b/extensions/theme-defaults/package.json new file mode 100644 index 0000000000..8549eb073f --- /dev/null +++ b/extensions/theme-defaults/package.json @@ -0,0 +1,18 @@ +{ + "name": "theme-defaults", + "displayName": "High Contrast Theme", + "description": "High Contrast theme", + "categories": [ "Themes" ], + "version": "0.1.10", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "themes": [ + { + "label": "High Contrast", + "uiTheme": "hc-black", + "path": "./themes/hc_black.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json new file mode 100644 index 0000000000..24c024d01e --- /dev/null +++ b/extensions/theme-defaults/themes/hc_black.json @@ -0,0 +1,274 @@ +{ + "name": "Dark Visual Studio", + "settings": [ + { + "scope": "emphasis", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "strong", + "settings": { + "fontStyle": "bold" + } + }, + { + "scope": "header", + "settings": { + "foreground": "#000080" + } + }, + + { + "scope": "comment", + "settings": { + "foreground": "#7ca668" + } + }, + { + "scope": "constant.language", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "constant.numeric", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "constant.regexp", + "settings": { + "foreground": "#b46695" + } + }, + { + "scope": "constant.rgb-value", + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "scope": "entity.name.tag", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "entity.name.selector", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "entity.other.attribute-name.css", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "scope": "invalid", + "settings": { + "foreground": "#f44747" + } + }, + { + "scope": "markup.underline", + "settings": { + "fontStyle": "underline" + } + }, + { + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "scope": "markup.heading", + "settings": { + "foreground": "#6796e6" + } + }, + { + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "markup.inserted", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "markup.deleted", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "markup.changed", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "meta.selector", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "name": "brackets of XML/HTML tags", + "scope": "punctuation.definition.tag", + "settings": { + "foreground": "#808080" + } + }, + { + "scope": "meta.preprocessor", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "meta.preprocessor.string", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "meta.preprocessor.numeric", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "meta.structure.dictionary.key.python", + "settings": { + "foreground": "#9cdcfe" + } + }, + { + "scope": "storage", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage.type", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "storage.modifier", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "string", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.tag", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.value", + "settings": { + "foreground": "#ce9178" + } + }, + { + "scope": "string.regexp", + "settings": { + "foreground": "#d16969" + } + }, + { + "name": "JavaScript string interpolation ${}", + "scope": "string.template-expression", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "support.type.property-name", + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "scope": "keyword", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.control", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "scope": ["keyword.operator.new", "keyword.operator.expression"], + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "keyword.other.unit", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "scope": "support.function.git-rebase", + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "scope": "constant.sha.git-rebase", + "settings": { + "foreground": "#b5cea8" + } + }, + { + "name": "coloring of the Java import and package identifiers", + "scope": ["storage.modifier.import.java", "storage.modifier.package.java"], + "settings": { + "foreground": "#d4d4d4" + } + }, + { + "name": "coloring of the TS this", + "scope": "variable.this", + "settings": { + "foreground": "#569cd6" + } + } + ] +} \ No newline at end of file diff --git a/extensions/theme-kimbie-dark/OSSREADME.json b/extensions/theme-kimbie-dark/OSSREADME.json new file mode 100644 index 0000000000..ddb630bfd9 --- /dev/null +++ b/extensions/theme-kimbie-dark/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "Colorsublime-Themes", + "version": "0.1.0", + "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", + "description": "The themes in this folders are copied from colorsublime.com. <<>>" +}] diff --git a/extensions/theme-kimbie-dark/package.json b/extensions/theme-kimbie-dark/package.json new file mode 100644 index 0000000000..f22ca5e5f0 --- /dev/null +++ b/extensions/theme-kimbie-dark/package.json @@ -0,0 +1,15 @@ +{ + "name": "theme-kimbie-dark", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "themes": [ + { + "label": "Kimbie Dark", + "uiTheme": "vs-dark", + "path": "./themes/kimbie-dark-color-theme.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-kimbie-dark/themes/Kimbie_dark.tmTheme b/extensions/theme-kimbie-dark/themes/Kimbie_dark.tmTheme new file mode 100644 index 0000000000..6f978635ef --- /dev/null +++ b/extensions/theme-kimbie-dark/themes/Kimbie_dark.tmTheme @@ -0,0 +1,509 @@ + + + + + + author + Jan T. Sott + name + Kimbie (dark) + comment + https://github.com/idleberg/Kimbie.tmTheme + semanticClass + theme.dark.kimbie + colorSpaceName + sRGB + gutterSettings + + background + #5e452b + divider + #5e452b + foreground + #a57a4c + selectionBackground + #84613d + selectionForeground + #d6baad + + settings + + + settings + + background + #221a0f + caret + #d3af86 + foreground + #d3af86 + invisibles + #a57a4c + lineHighlight + #5e452b + selection + #84613d + + + + name + Text + scope + variable.parameter.function + settings + + foreground + #d3af86 + + + + name + Comments + scope + comment, punctuation.definition.comment + settings + + foreground + #a57a4c + + + + name + Punctuation + scope + punctuation.definition.string, punctuation.definition.variable, punctuation.definition.string, punctuation.definition.parameters, punctuation.definition.string, punctuation.definition.array + settings + + foreground + #d3af86 + + + + name + Delimiters + scope + none + settings + + foreground + #d3af86 + + + + name + Operators + scope + keyword.operator + settings + + foreground + #d3af86 + + + + name + Keywords + scope + keyword, keyword.control + settings + + foreground + #98676a + + + + name + Variables + scope + variable + settings + + foreground + #dc3958 + + + + name + Functions + scope + entity.name.function, meta.require, support.function.any-method + settings + + foreground + #8ab1b0 + + + + name + Classes + scope + support.class, entity.name.class, entity.name.type + settings + + foreground + #f06431 + + + + name + Methods + scope + keyword.other.special-method + settings + + foreground + #8ab1b0 + + + + name + Storage + scope + storage + settings + + foreground + #98676a + + + + name + Support + scope + support.function + settings + + foreground + #7e602c + + + + name + Strings, Inherited Class + scope + string, constant.other.symbol, entity.other.inherited-class + settings + + foreground + #889b4a + + + + name + Integers + scope + constant.numeric + settings + + foreground + #f79a32 + + + + name + Floats + scope + none + settings + + foreground + #f79a32 + + + + name + Boolean + scope + none + settings + + foreground + #f79a32 + + + + name + Constants + scope + constant + settings + + foreground + #f79a32 + + + + name + Tags + scope + entity.name.tag + settings + + foreground + #dc3958 + + + + name + Attributes + scope + entity.other.attribute-name + settings + + foreground + #f79a32 + + + + name + Attribute IDs + scope + entity.other.attribute-name.id, punctuation.definition.entity + settings + + foreground + #8ab1b0 + + + + name + Selector + scope + meta.selector + settings + + foreground + #98676a + + + + name + Values + scope + none + settings + + foreground + #f79a32 + + + + name + Headings + scope + markup.heading, markup.heading.setext, punctuation.definition.heading, entity.name.section + settings + + fontStyle + + foreground + #8ab1b0 + + + + name + Units + scope + keyword.other.unit + settings + + foreground + #f79a32 + + + + name + Bold + scope + markup.bold, punctuation.definition.bold + settings + + fontStyle + bold + foreground + #f06431 + + + + name + Italic + scope + markup.italic, punctuation.definition.italic + settings + + fontStyle + italic + foreground + #98676a + + + + name + Code + scope + markup.inline.raw + settings + + foreground + #889b4a + + + + name + Link Text + scope + string.other.link + settings + + foreground + #dc3958 + + + + name + Link Url + scope + meta.link + settings + + foreground + #f79a32 + + + + name + Lists + scope + markup.list + settings + + foreground + #dc3958 + + + + name + Quotes + scope + markup.quote + settings + + foreground + #f79a32 + + + + name + Separator + scope + meta.separator + settings + + background + #84613d + foreground + #d3af86 + + + + name + Inserted + scope + markup.inserted + settings + + foreground + #889b4a + + + + name + Deleted + scope + markup.deleted + settings + + foreground + #dc3958 + + + + name + Changed + scope + markup.changed + settings + + foreground + #98676a + + + + name + Colors + scope + constant.other.color + settings + + foreground + #7e602c + + + + name + Regular Expressions + scope + string.regexp + settings + + foreground + #7e602c + + + + name + Escape Characters + scope + constant.character.escape + settings + + foreground + #7e602c + + + + name + Embedded + scope + punctuation.section.embedded, variable.interpolation + settings + + foreground + #18401e + + + + name + Invalid + scope + invalid.illegal + settings + + foreground + #dc3958 + + + + uuid + 3afc3658-e264-4790-85c5-4c4c85f4b1ce + + diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json new file mode 100644 index 0000000000..f9a84a5c5e --- /dev/null +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -0,0 +1,384 @@ +{ + "name": "Kimbie Dark", + "type": "dark", + "colors": { + "input.background": "#51412c", + "editor.background": "#221a0f", + "editor.foreground": "#d3af86", + "focusBorder": "#a57a4c", + "list.highlightForeground": "#e3b583", + "list.activeSelectionBackground": "#7c5021", + "list.hoverBackground": "#7c502166", + "list.focusBackground": "#7c5021AA", + "list.inactiveSelectionBackground": "#645342", + "pickerGroup.foreground": "#e3b583", + "pickerGroup.border": "#e3b583", + "inputOption.activeBorder": "#a57a4c", + "selection.background": "#84613daa", + "editor.selectionBackground": "#84613daa", + "editorWidget.background": "#131510", + "editorHoverWidget.background": "#221a14", + "editorGroupHeader.tabsBackground": "#131510", + "editorGroup.background": "#0f0c08", + "tab.inactiveBackground": "#131510", + "titleBar.activeBackground": "#423523", + "statusBar.background": "#423523", + "statusBar.debuggingBackground": "#423523", + "statusBar.noFolderBackground": "#423523", + "activityBar.background": "#221a0f", + "activityBar.foreground": "#d3af86", + "sideBar.background": "#362712", + "editor.lineHighlightBackground": "#5e452b", + "editorCursor.foreground": "#d3af86", + "editorWhitespace.foreground": "#a57a4c", + "peekViewTitle.background": "#362712", + "peekView.border": "#5e452b", + "peekViewResult.background": "#362712", + "peekViewEditor.background": "#221a14", + "peekViewEditor.matchHighlightBackground": "#84613daa", + "notification.background": "#473a29", + "button.background": "#6e583b", + "inputValidation.infoBorder": "#1b60a5", + "inputValidation.infoBackground": "#2b2a42", + "inputValidation.warningBackground": "#51412c", + // "inputValidation.warningBorder": "#5B7E7A", + "inputValidation.errorBackground": "#5f0d0d", + "inputValidation.errorBorder": "#9d2f23", + "badge.background": "#7f5d38", + "progressBar.background": "#7f5d38" + }, + "tokenColors": [ + { + "settings": { + "background": "#221a0f", + "foreground": "#d3af86" + } + }, + { + "name": "Text", + "scope": "variable.parameter.function", + "settings": { + "foreground": "#d3af86" + } + }, + { + "name": "Comments", + "scope": [ + "comment", + "punctuation.definition.comment" + ], + "settings": { + "foreground": "#a57a4c" + } + }, + { + "name": "Punctuation", + "scope": [ + "punctuation.definition.string", + "punctuation.definition.variable", + "punctuation.definition.string", + "punctuation.definition.parameters", + "punctuation.definition.string", + "punctuation.definition.array" + ], + "settings": { + "foreground": "#d3af86" + } + }, + { + "name": "Delimiters", + "scope": "none", + "settings": { + "foreground": "#d3af86" + } + }, + { + "name": "Operators", + "scope": "keyword.operator", + "settings": { + "foreground": "#d3af86" + } + }, + { + "name": "Keywords", + "scope": [ + "keyword", + "keyword.control" + ], + "settings": { + "foreground": "#98676a" + } + }, + { + "name": "Variables", + "scope": "variable", + "settings": { + "foreground": "#dc3958" + } + }, + { + "name": "Functions", + "scope": [ + "entity.name.function", + "meta.require", + "support.function.any-method" + ], + "settings": { + "foreground": "#8ab1b0" + } + }, + { + "name": "Classes", + "scope": [ + "support.class", + "entity.name.class", + "entity.name.type" + ], + "settings": { + "foreground": "#f06431" + } + }, + { + "name": "Methods", + "scope": "keyword.other.special-method", + "settings": { + "foreground": "#8ab1b0" + } + }, + { + "name": "Storage", + "scope": "storage", + "settings": { + "foreground": "#98676a" + } + }, + { + "name": "Support", + "scope": "support.function", + "settings": { + "foreground": "#7e602c" + } + }, + { + "name": "Strings, Inherited Class", + "scope": [ + "string", + "constant.other.symbol", + "entity.other.inherited-class" + ], + "settings": { + "foreground": "#889b4a" + } + }, + { + "name": "Integers", + "scope": "constant.numeric", + "settings": { + "foreground": "#f79a32" + } + }, + { + "name": "Floats", + "scope": "none", + "settings": { + "foreground": "#f79a32" + } + }, + { + "name": "Boolean", + "scope": "none", + "settings": { + "foreground": "#f79a32" + } + }, + { + "name": "Constants", + "scope": "constant", + "settings": { + "foreground": "#f79a32" + } + }, + { + "name": "Tags", + "scope": "entity.name.tag", + "settings": { + "foreground": "#dc3958" + } + }, + { + "name": "Attributes", + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#f79a32" + } + }, + { + "name": "Attribute IDs", + "scope": [ + "entity.other.attribute-name.id", + "punctuation.definition.entity" + ], + "settings": { + "foreground": "#8ab1b0" + } + }, + { + "name": "Selector", + "scope": "meta.selector", + "settings": { + "foreground": "#98676a" + } + }, + { + "name": "Values", + "scope": "none", + "settings": { + "foreground": "#f79a32" + } + }, + { + "name": "Headings", + "scope": [ + "markup.heading", + "markup.heading.setext", + "punctuation.definition.heading", + "entity.name.section" + ], + "settings": { + "fontStyle": "", + "foreground": "#8ab1b0" + } + }, + { + "name": "Units", + "scope": "keyword.other.unit", + "settings": { + "foreground": "#f79a32" + } + }, + { + "name": "Bold", + "scope": [ + "markup.bold", + "punctuation.definition.bold" + ], + "settings": { + "fontStyle": "bold", + "foreground": "#f06431" + } + }, + { + "name": "Italic", + "scope": [ + "markup.italic", + "punctuation.definition.italic" + ], + "settings": { + "fontStyle": "italic", + "foreground": "#98676a" + } + }, + { + "name": "Code", + "scope": "markup.inline.raw", + "settings": { + "foreground": "#889b4a" + } + }, + { + "name": "Link Text", + "scope": "string.other.link", + "settings": { + "foreground": "#dc3958" + } + }, + { + "name": "Link Url", + "scope": "meta.link", + "settings": { + "foreground": "#f79a32" + } + }, + { + "name": "Lists", + "scope": "markup.list", + "settings": { + "foreground": "#dc3958" + } + }, + { + "name": "Quotes", + "scope": "markup.quote", + "settings": { + "foreground": "#f79a32" + } + }, + { + "name": "Separator", + "scope": "meta.separator", + "settings": { + "background": "#84613d", + "foreground": "#d3af86" + } + }, + { + "name": "Inserted", + "scope": "markup.inserted", + "settings": { + "foreground": "#889b4a" + } + }, + { + "name": "Deleted", + "scope": "markup.deleted", + "settings": { + "foreground": "#dc3958" + } + }, + { + "name": "Changed", + "scope": "markup.changed", + "settings": { + "foreground": "#98676a" + } + }, + { + "name": "Colors", + "scope": "constant.other.color", + "settings": { + "foreground": "#7e602c" + } + }, + { + "name": "Regular Expressions", + "scope": "string.regexp", + "settings": { + "foreground": "#7e602c" + } + }, + { + "name": "Escape Characters", + "scope": "constant.character.escape", + "settings": { + "foreground": "#7e602c" + } + }, + { + "name": "Embedded", + "scope": [ + "punctuation.section.embedded", + "variable.interpolation" + ], + "settings": { + "foreground": "#088649" + } + }, + { + "name": "Invalid", + "scope": "invalid.illegal", + "settings": { + "foreground": "#dc3958" + } + } + ] +} \ No newline at end of file diff --git a/extensions/theme-monokai-dimmed/OSSREADME.json b/extensions/theme-monokai-dimmed/OSSREADME.json new file mode 100644 index 0000000000..ddb630bfd9 --- /dev/null +++ b/extensions/theme-monokai-dimmed/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "Colorsublime-Themes", + "version": "0.1.0", + "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", + "description": "The themes in this folders are copied from colorsublime.com. <<>>" +}] diff --git a/extensions/theme-monokai-dimmed/package.json b/extensions/theme-monokai-dimmed/package.json new file mode 100644 index 0000000000..18ed5256ad --- /dev/null +++ b/extensions/theme-monokai-dimmed/package.json @@ -0,0 +1,15 @@ +{ + "name": "theme-monokai-dimmed", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "themes": [ + { + "label": "Monokai Dimmed", + "uiTheme": "vs-dark", + "path": "./themes/dimmed-monokai-color-theme.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json new file mode 100644 index 0000000000..60277bbbf0 --- /dev/null +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -0,0 +1,573 @@ +{ + "type": "dark", + "colors": { + "dropdown.background": "#525252", + "list.activeSelectionBackground": "#707070", + "list.focusBackground": "#707070", + "list.inactiveSelectionBackground": "#4e4e4e", + "list.hoverBackground": "#707070", + "list.highlightForeground": "#e58520", + "button.background": "#565656", + "editor.background": "#1e1e1e", + "editor.foreground": "#c5c8c6", + "editor.selectionBackground": "#373b41", + "editor.lineHighlightBackground": "#303030", + "editorCursor.foreground": "#c07020", + "editorWhitespace.foreground": "#505037", + "editorIndentGuide.background": "#505037", + "editorGroupHeader.tabsBackground": "#282828", + "editorGroup.background": "#1e1e1e", + "tab.inactiveBackground": "#404040", + "tab.border": "#303030", + "tab.inactiveForeground": "#d8d8d8", + "peekView.border": "#3655b5", + "panelTitle.activeForeground": "#ffffff", + "statusBar.background": "#505050", + "statusBar.debuggingBackground": "#505050", + "statusBar.noFolderBackground": "#505050", + "titleBar.activeBackground": "#505050", + "activityBar.background": "#353535", + "activityBar.foreground": "#ffffff", + "activityBarBadge.background": "#3655b5", + "sideBar.background": "#272727", + "sideBarSectionHeader.background": "#505050", + "notification.background": "#353535", + "pickerGroup.foreground": "#b0b0b0", + "terminal.ansiWhite": "#ffffff", + "inputOption.activeBorder": "#3655b5", + "focusBorder": "#3655b5" + }, + "tokenColors": [ + { + "settings": { + "background": "#1e1e1e", + "foreground": "#C5C8C6" + } + }, + { + "name": "By uonick", + "settings": { + "background": "#202025ff", + "foreground": "#c5c8c6ff" + } + }, + { + "name": "Comment", + "scope": "comment", + "settings": { + "fontStyle": "\n ", + "foreground": "#9A9B99" + } + }, + { + "name": "String", + "scope": "string", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9AA83A" + } + }, + { + "name": "String Embedded Source", + "scope": "string source", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#D08442" + } + }, + { + "name": "Number", + "scope": "constant.numeric", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#6089B4" + } + }, + { + "name": "Built-in constant", + "scope": "constant.language", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#408080" + } + }, + { + "name": "User-defined constant", + "scope": "constant.character, constant.other", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#8080FF", + "background": "#1e1e1e" + } + }, + { + "name": "Keyword", + "scope": "keyword", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#6089B4" + } + }, + { + "name": "Support", + "scope": "support", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#C7444A" + } + }, + { + "name": "Storage", + "scope": "storage", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9872A2" + } + }, + { + "name": "Class name", + "scope": "entity.name.class, entity.name.type", + "settings": { + "fontStyle": "\n \t\t\t \t", + "foreground": "#9B0000", + "background": "#1E1E1E" + } + }, + { + "name": "Inherited class", + "scope": "entity.other.inherited-class", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#C7444A" + } + }, + { + "name": "Function name", + "scope": "entity.name.function", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#CE6700" + } + }, + { + "name": "Function argument", + "scope": "variable.parameter", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#6089B4" + } + }, + { + "name": "Tag name", + "scope": "entity.name.tag", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9872A2" + } + }, + { + "name": "Tag attribute", + "scope": "entity.other.attribute-name", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9872A2" + } + }, + { + "name": "Library function", + "scope": "support.function", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9872A2" + } + }, + { + "name": "Keyword", + "scope": "keyword", + "settings": { + "fontStyle": "\n \t\t\t\t", + "foreground": "#676867" + } + }, + { + "name": "Class Variable", + "scope": "variable.other, variable.js, punctuation.separator.variable", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#6089B4" + } + }, + { + "name": "Language Constant", + "scope": "constant.language", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#FF0080" + } + }, + { + "name": "Meta Brace", + "scope": "punctuation.section.embedded -(source string source punctuation.section.embedded), meta.brace.erb.html", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#008200" + } + }, + { + "name": "Invalid", + "scope": "invalid", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#FF0B00" + } + }, + { + "name": "Normal Variable", + "scope": "variable.other.php, variable.other.normal", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#6089B4" + } + }, + { + "name": "Function Object", + "scope": "meta.function-call.object", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9872A2" + } + }, + { + "name": "Function Call Variable", + "scope": "variable.other.property", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9872A2" + } + }, + { + "name": "Keyword Control", + "scope": "keyword.control", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9872A2" + } + }, + { + "name": "Tag", + "scope": "meta.tag", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#D0B344" + } + }, + { + "name": "Tag Name", + "scope": "entity.name.tag", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#6089B4" + } + }, + { + "name": "Doctype", + "scope": "meta.doctype, meta.tag.sgml-declaration.doctype, meta.tag.sgml.doctype", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9AA83A" + } + }, + { + "name": "Tag Inline Source", + "scope": "meta.tag.inline source, text.html.php.source", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9AA83A" + } + }, + { + "name": "Tag Other", + "scope": "meta.tag.other, entity.name.tag.style, entity.name.tag.script, meta.tag.block.script, source.js.embedded punctuation.definition.tag.html, source.css.embedded punctuation.definition.tag.html", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9872A2" + } + }, + { + "name": "Tag Attribute", + "scope": "entity.other.attribute-name, meta.tag punctuation.definition.string", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#D0B344" + } + }, + { + "name": "Tag Value", + "scope": "meta.tag string -source -punctuation, text source text meta.tag string -punctuation", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#6089B4" + } + }, + { + "name": "Meta Brace", + "scope": "punctuation.section.embedded -(source string source punctuation.section.embedded), meta.brace.erb.html", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#D0B344" + } + }, + { + "name": "HTML ID", + "scope": "meta.toc-list.id", + "settings": { + "foreground": "#9AA83A" + } + }, + { + "name": "HTML String", + "scope": "string.quoted.double.html, punctuation.definition.string.begin.html, punctuation.definition.string.end.html", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9AA83A" + } + }, + { + "name": "HTML Tags", + "scope": "punctuation.definition.tag.html, punctuation.definition.tag.begin, punctuation.definition.tag.end", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#6089B4" + } + }, + { + "name": "CSS ID", + "scope": "meta.selector.css entity.other.attribute-name.id", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9872A2" + } + }, + { + "name": "CSS Property Name", + "scope": "support.type.property-name.css", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#676867" + } + }, + { + "name": "CSS Property Value", + "scope": "meta.property-group support.constant.property-value.css, meta.property-value support.constant.property-value.css", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#C7444A" + } + }, + { + "name": "JavaScript Variable", + "scope": "variable.language.js", + "settings": { + "foreground": "#CC555A" + } + }, + { + "name": "Template Definition", + "scope": [ + "punctuation.definition.template-expression", + "punctuation.section.embedded.coffee" + ], + "settings": { + "foreground": "#D08442" + } + }, + { + "name": "Reset JavaScript string interpolation expression", + "scope": [ + "meta.template.expression" + ], + "settings": { + "foreground": "#C5C8C6" + } + }, + { + "name": "PHP Function Call", + "scope": "meta.function-call.object.php", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#D0B344" + } + }, + { + "name": "PHP Single Quote HMTL Fix", + "scope": "punctuation.definition.string.end.php, punctuation.definition.string.begin.php", + "settings": { + "foreground": "#9AA83A" + } + }, + { + "name": "PHP Parenthesis HMTL Fix", + "scope": "source.php.embedded.line.html", + "settings": { + "foreground": "#676867" + } + }, + { + "name": "PHP Punctuation Embedded", + "scope": "punctuation.section.embedded.begin.php, punctuation.section.embedded.end.php", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#D08442" + } + }, + { + "name": "Ruby Symbol", + "scope": "constant.other.symbol.ruby", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#9AA83A" + } + }, + { + "name": "Ruby Variable", + "scope": "variable.language.ruby", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#D0B344" + } + }, + { + "name": "Ruby Special Method", + "scope": "keyword.other.special-method.ruby", + "settings": { + "fontStyle": "\n \t\t\t", + "foreground": "#D9B700" + } + }, + { + "name": "Ruby Embedded Source", + "scope": "source.ruby.embedded.source", + "settings": { + "foreground": "#D08442" + } + }, + { + "name": "SQL", + "scope": "keyword.other.DML.sql", + "settings": { + "fontStyle": "\n \t\t\t\t", + "foreground": "#D0B344" + } + }, + { + "name": "diff: header", + "scope": "meta.diff, meta.diff.header", + "settings": { + "background": "#b58900", + "fontStyle": "italic", + "foreground": "#E0EDDD" + } + }, + { + "name": "diff: deleted", + "scope": "markup.deleted", + "settings": { + "background": "#eee8d5", + "fontStyle": "", + "foreground": "#dc322f" + } + }, + { + "name": "diff: changed", + "scope": "markup.changed", + "settings": { + "background": "#eee8d5", + "fontStyle": "", + "foreground": "#cb4b16" + } + }, + { + "name": "diff: inserted", + "scope": "markup.inserted", + "settings": { + "background": "#eee8d5", + "foreground": "#219186" + } + }, + { + "name": "Markup Quote", + "scope": "markup.quote", + "settings": { + "foreground": "#9872A2" + } + }, + { + "name": "Markup Lists", + "scope": "markup.list", + "settings": { + "foreground": "#9AA83A" + } + }, + { + "name": "Markup Styling", + "scope": "markup.bold, markup.italic", + "settings": { + "foreground": "#6089B4" + } + }, + { + "name": "Markup Inline", + "scope": "markup.inline.raw", + "settings": { + "fontStyle": "", + "foreground": "#FF0080" + } + }, + { + "name": "Markup Headings", + "scope": "markup.heading", + "settings": { + "foreground": "#D0B344" + } + }, + { + "name": "Markup Setext Header", + "scope": "markup.heading.setext", + "settings": { + "fontStyle": "", + "foreground": "#D0B344" + } + }, + { + "scope": "token.info-token", + "settings": { + "foreground": "#6796e6" + } + }, + { + "scope": "token.warn-token", + "settings": { + "foreground": "#cd9731" + } + }, + { + "scope": "token.error-token", + "settings": { + "foreground": "#f44747" + } + }, + { + "scope": "token.debug-token", + "settings": { + "foreground": "#b267e6" + } + }, + { + "name": "this.self", + "scope": "variable.language", + "settings": { + "foreground": "#c7444a" + } + } + ] +} \ No newline at end of file diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai.tmTheme b/extensions/theme-monokai-dimmed/themes/dimmed-monokai.tmTheme new file mode 100644 index 0000000000..5c542a4394 --- /dev/null +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai.tmTheme @@ -0,0 +1,856 @@ + + + + author + uonick + comment + Dimmed - Monokai + name + Dimmed - Monokai + settings + + + settings + + background + #1e1e1e + caret + #fc5604 + foreground + #C5C8C6 + invisibles + #4B4E55 + lineHighlight + #282A2E + selection + #373B41 + + + + name + By uonick + settings + + + + + name + Comment + scope + comment + settings + + fontStyle + + + foreground + #9A9B99 + + + + name + String + scope + string + settings + + fontStyle + + + foreground + #9AA83A + + + + name + String Embedded Source + scope + string source + settings + + fontStyle + + + foreground + #D08442 + + + + name + Number + scope + constant.numeric + settings + + fontStyle + + + foreground + #6089B4 + + + + name + Built-in constant + scope + constant.language + settings + + fontStyle + + + foreground + #408080 + + + + name + User-defined constant + scope + constant.character, constant.other + settings + + fontStyle + + + foreground + #8080FF + background + #1e1e1e + + + + name + Keyword + scope + keyword + settings + + fontStyle + + + foreground + #6089B4 + + + + name + Support + scope + support + settings + + fontStyle + + + foreground + #C7444A + + + + name + Storage + scope + storage + settings + + fontStyle + + + foreground + #9872A2 + + + + name + Class name + scope + entity.name.class, entity.name.type + settings + + fontStyle + + + foreground + #9B0000 + background + #1E1E1E + + + + name + Inherited class + scope + entity.other.inherited-class + settings + + fontStyle + + + foreground + #C7444A + + + + name + Function name + scope + entity.name.function + settings + + fontStyle + + + foreground + #CE6700 + + + + name + Function argument + scope + variable.parameter + settings + + fontStyle + + + foreground + #6089B4 + + + + name + Tag name + scope + entity.name.tag + settings + + fontStyle + + + foreground + #9872A2 + + + + name + Tag attribute + scope + entity.other.attribute-name + settings + + fontStyle + + + foreground + #9872A2 + + + + name + Library function + scope + support.function + settings + + fontStyle + + + foreground + #9872A2 + + + + name + Keyword + scope + keyword + settings + + fontStyle + + + foreground + #676867 + + + + name + Class Variable + scope + variable.other, variable.js, punctuation.separator.variable + settings + + fontStyle + + + foreground + #6089B4 + + + + name + Language Constant + scope + constant.language + settings + + fontStyle + + + foreground + #FF0080 + + + + name + Meta Brace + scope + punctuation.section.embedded -(source string source punctuation.section.embedded), meta.brace.erb.html + settings + + fontStyle + + + foreground + #008200 + + + + name + Invalid + scope + invalid + settings + + fontStyle + + + foreground + #FF0B00 + + + + name + Normal Variable + scope + variable.other.php, variable.other.normal + settings + + fontStyle + + + foreground + #6089B4 + + + + name + Function Call + scope + meta.function-call + settings + + fontStyle + + + foreground + #0080FF + + + + name + Function Object + scope + meta.function-call.object + settings + + fontStyle + + + foreground + #9872A2 + + + + name + Function Call Variable + scope + variable.other.property + settings + + fontStyle + + + foreground + #9872A2 + + + + name + Keyword Control + scope + keyword.control + settings + + fontStyle + + + foreground + #9872A2 + + + + name + Tag + scope + meta.tag + settings + + fontStyle + + + foreground + #D0B344 + + + + name + Tag Name + scope + entity.name.tag + settings + + fontStyle + + + foreground + #6089B4 + + + + name + Doctype + scope + meta.doctype, meta.tag.sgml-declaration.doctype, meta.tag.sgml.doctype + settings + + fontStyle + + + foreground + #9AA83A + + + + name + Tag Inline Source + scope + meta.tag.inline source, text.html.php.source + settings + + fontStyle + + + foreground + #9AA83A + + + + name + Tag Other + scope + meta.tag.other, entity.name.tag.style, entity.name.tag.script, meta.tag.block.script, source.js.embedded punctuation.definition.tag.html, source.css.embedded punctuation.definition.tag.html + settings + + fontStyle + + + foreground + #9872A2 + + + + name + Tag Attribute + scope + entity.other.attribute-name, meta.tag punctuation.definition.string + settings + + fontStyle + + + foreground + #D0B344 + + + + name + Tag Value + scope + meta.tag string -source -punctuation, text source text meta.tag string -punctuation + settings + + fontStyle + + + foreground + #6089B4 + + + + name + Meta Brace + scope + punctuation.section.embedded -(source string source punctuation.section.embedded), meta.brace.erb.html + settings + + fontStyle + + + foreground + #D0B344 + + + + name + HTML ID + scope + meta.toc-list.id + settings + + foreground + #9AA83A + + + + name + HTML String + scope + string.quoted.double.html, punctuation.definition.string.begin.html, punctuation.definition.string.end.html + settings + + fontStyle + + + foreground + #9AA83A + + + + name + HTML Tags + scope + punctuation.definition.tag.html, punctuation.definition.tag.begin, punctuation.definition.tag.end + settings + + fontStyle + + + foreground + #6089B4 + + + + name + CSS ID + scope + meta.selector.css entity.other.attribute-name.id + settings + + fontStyle + + + foreground + #9872A2 + + + + name + CSS Property Name + scope + support.type.property-name.css + settings + + fontStyle + + + foreground + #676867 + + + + name + CSS Property Value + scope + meta.property-group support.constant.property-value.css, meta.property-value support.constant.property-value.css + settings + + fontStyle + + + foreground + #C7444A + + + + name + JavaScript Variable + scope + variable.language.js + settings + + foreground + #CC555A + + + + name + PHP Function Call + scope + meta.function-call.object.php + settings + + fontStyle + + + foreground + #D0B344 + + + + name + PHP Single Quote HMTL Fix + scope + punctuation.definition.string.end.php, punctuation.definition.string.begin.php + settings + + foreground + #9AA83A + + + + name + PHP Parenthesis HMTL Fix + scope + source.php.embedded.line.html + settings + + foreground + #676867 + + + + name + PHP Punctuation Embedded + scope + punctuation.section.embedded.begin.php, punctuation.section.embedded.end.php + settings + + fontStyle + + + foreground + #D08442 + + + + name + Ruby Symbol + scope + constant.other.symbol.ruby + settings + + fontStyle + + + foreground + #9AA83A + + + + name + Ruby Variable + scope + variable.language.ruby + settings + + fontStyle + + + foreground + #D0B344 + + + + name + Ruby Special Method + scope + keyword.other.special-method.ruby + settings + + fontStyle + + + foreground + #D9B700 + + + + name + Ruby Embedded Source + scope + source.ruby.embedded.source + settings + + foreground + #D08442 + + + + name + SQL + scope + keyword.other.DML.sql + settings + + fontStyle + + + foreground + #D0B344 + + + + name + diff: header + scope + meta.diff, meta.diff.header + settings + + background + #b58900 + fontStyle + italic + foreground + #E0EDDD + + + + name + diff: deleted + scope + markup.deleted + settings + + background + #eee8d5 + fontStyle + + foreground + #dc322f + + + + name + diff: changed + scope + markup.changed + settings + + background + #eee8d5 + fontStyle + + foreground + #cb4b16 + + + + name + diff: inserted + scope + markup.inserted + settings + + background + #eee8d5 + foreground + #219186 + + + + name + Markup Quote + scope + markup.quote + settings + + foreground + #9872A2 + + + + name + Markup Lists + scope + markup.list + settings + + foreground + #9AA83A + + + + name + Markup Styling + scope + markup.bold, markup.italic + settings + + foreground + #6089B4 + + + + name + Markup Inline + scope + markup.inline.raw + settings + + fontStyle + + foreground + #FF0080 + + + + name + Markup Headings + scope + markup.heading + settings + + foreground + #D0B344 + + + + name + Markup Setext Header + scope + markup.heading.setext + settings + + fontStyle + + foreground + #D0B344 + + + + + + diff --git a/extensions/theme-monokai/OSSREADME.json b/extensions/theme-monokai/OSSREADME.json new file mode 100644 index 0000000000..ddb630bfd9 --- /dev/null +++ b/extensions/theme-monokai/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "Colorsublime-Themes", + "version": "0.1.0", + "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", + "description": "The themes in this folders are copied from colorsublime.com. <<>>" +}] diff --git a/extensions/theme-monokai/package.json b/extensions/theme-monokai/package.json new file mode 100644 index 0000000000..4e4fdd358b --- /dev/null +++ b/extensions/theme-monokai/package.json @@ -0,0 +1,15 @@ +{ + "name": "theme-monokai", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "themes": [ + { + "label": "Monokai", + "uiTheme": "vs-dark", + "path": "./themes/monokai-color-theme.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-monokai/themes/Monokai.tmTheme b/extensions/theme-monokai/themes/Monokai.tmTheme new file mode 100644 index 0000000000..81c05dcb79 --- /dev/null +++ b/extensions/theme-monokai/themes/Monokai.tmTheme @@ -0,0 +1,472 @@ + + + + + name + Monokai + settings + + + settings + + background + #272822 + caret + #F8F8F0 + foreground + #F8F8F2 + invisibles + #3B3A32 + lineHighlight + #3E3D32 + selection + #49483E + findHighlight + #FFE792 + findHighlightForeground + #000000 + selectionBorder + #222218 + activeGuide + #9D550FB0 + guide + #48473E + + bracketsForeground + #F8F8F2A5 + bracketsOptions + underline + + bracketContentsForeground + #F8F8F2A5 + bracketContentsOptions + underline + + tagsOptions + stippled_underline + + + + name + Comment + scope + comment + settings + + foreground + #75715E + + + + name + String + scope + string + settings + + foreground + #E6DB74 + + + + name + Template Definition + scope + punctuation.definition.template-expression + settings + + foreground + #F92672 + + + + name + Number + scope + constant.numeric + settings + + foreground + #AE81FF + + + + + name + Built-in constant + scope + constant.language + settings + + foreground + #AE81FF + + + + name + User-defined constant + scope + constant.character, constant.other + settings + + foreground + #AE81FF + + + + name + Variable + scope + variable + settings + + fontStyle + + + + + name + Keyword + scope + keyword + settings + + foreground + #F92672 + + + + name + Storage + scope + storage + settings + + fontStyle + + foreground + #F92672 + + + + name + Storage type + scope + storage.type + settings + + fontStyle + italic + foreground + #66D9EF + + + + name + Class name + scope + entity.name.type, entity.name.class + settings + + fontStyle + underline + foreground + #A6E22E + + + + name + Inherited class + scope + entity.other.inherited-class + settings + + fontStyle + italic underline + foreground + #A6E22E + + + + name + Function name + scope + entity.name.function + settings + + fontStyle + + foreground + #A6E22E + + + + name + Function argument + scope + variable.parameter + settings + + fontStyle + italic + foreground + #FD971F + + + + name + Tag name + scope + entity.name.tag + settings + + fontStyle + + foreground + #F92672 + + + + name + Tag attribute + scope + entity.other.attribute-name + settings + + fontStyle + + foreground + #A6E22E + + + + name + Library function + scope + support.function + settings + + fontStyle + + foreground + #66D9EF + + + + name + Library constant + scope + support.constant + settings + + fontStyle + + foreground + #66D9EF + + + + name + Library class/type + scope + support.type, support.class + settings + + fontStyle + italic + foreground + #66D9EF + + + + name + Library variable + scope + support.other.variable + settings + + fontStyle + + + + + name + Invalid + scope + invalid + settings + + background + #F92672 + fontStyle + + foreground + #F8F8F0 + + + + name + Invalid deprecated + scope + invalid.deprecated + settings + + background + #AE81FF + foreground + #F8F8F0 + + + + name + JSON String + scope + meta.structure.dictionary.json string.quoted.double.json + settings + + foreground + #CFCFC2 + + + + + name + diff.header + scope + meta.diff, meta.diff.header + settings + + foreground + #75715E + + + + name + diff.deleted + scope + markup.deleted + settings + + foreground + #F92672 + + + + name + diff.inserted + scope + markup.inserted + settings + + foreground + #A6E22E + + + + name + diff.changed + scope + markup.changed + settings + + foreground + #E6DB74 + + + + + scope + constant.numeric.line-number.find-in-files - match + settings + + foreground + #AE81FFA0 + + + + scope + entity.name.filename.find-in-files + settings + + foreground + #E6DB74 + + + + + name + Markup Quote + scope + markup.quote + settings + + foreground + #F92672 + + + + name + Markup Lists + scope + markup.list + settings + + foreground + #E6DB74 + + + + name + Markup Styling + scope + markup.bold, markup.italic + settings + + foreground + #66D9EF + + + + name + Markup Inline + scope + markup.inline.raw + settings + + fontStyle + + foreground + #FD971F + + + + name + Markup Headings + scope + markup.heading + settings + + foreground + #A6E22E + + + + name + Markup Setext Header + scope + markup.heading.setext + settings + + fontStyle + + foreground + #A6E22E + + + + + + uuid + D8D5E82E-3D5B-46B5-B38E-8C841C21347D + + diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json new file mode 100644 index 0000000000..fa42db4a3c --- /dev/null +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -0,0 +1,403 @@ +// This theme's colors are based on the original Monokai: +// #1e1f1c (tab well, borders) +// #272822 (editor background) +// #414339 (selection) +// #75715e (focus) +// #f8f8f2 (editor foreground) +{ + "type": "dark", + "colors": { + "dropdown.background": "#414339", + "list.activeSelectionBackground": "#75715E", + "list.focusBackground": "#414339", + "list.inactiveSelectionBackground": "#414339", + "list.hoverBackground": "#272822", + "list.dropBackground": "#414339", + "list.highlightForeground": "#f8f8f2", + "button.background": "#75715E", + "editor.background": "#272822", + "editor.foreground": "#f8f8f2", + "selection.background": "#ccccc7", + "editor.selectionBackground": "#49483e", + "editor.lineHighlightBackground": "#3e3d32", + "editorCursor.foreground": "#f8f8f0", + "editorWhitespace.foreground": "#464741", + "editorIndentGuide.background": "#464741", + "editorGroupHeader.tabsBackground": "#1e1f1c", + "editorGroup.dropBackground": "#41433980", + "tab.inactiveBackground": "#414339", + "tab.border": "#1e1f1c", + "tab.inactiveForeground": "#ccccc7", // needs to be bright so it's readable when another editor group is focused + "widget.shadow": "#000000", + "progressBar.background": "#75715E", + "badge.background": "#75715E", + "badge.foreground": "#f8f8f2", + "editorLineNumber.foreground": "#90908a", + "panelTitle.activeForeground": "#f8f8f2", + "panelTitle.activeBorder": "#75715E", + "panelTitle.inactiveForeground": "#75715E", + "panel.border": "#414339", + "titleBar.activeBackground": "#1e1f1c", + "statusBar.background": "#414339", + "statusBar.noFolderBackground": "#414339", + "statusBar.debuggingBackground": "#75715E", + "activityBar.background": "#272822", + "activityBar.foreground": "#f8f8f2", + "activityBar.dropBackground": "#414339", + "sideBar.background": "#1e1f1c", + "sideBarSectionHeader.background": "#272822", + "pickerGroup.foreground": "#75715E", + "input.background": "#414339", + "inputOption.activeBorder": "#75715E", + "focusBorder": "#75715E", + "editorWidget.background": "#1e1f1c", + "debugToolBar.background": "#1e1f1c", + "diffEditor.insertedTextBackground": "#66852880", // middle of #272822 and #a6e22e + "diffEditor.removedTextBackground": "#90274A80", // middle of #272822 and #f92672 + "inputValidation.errorBackground": "#90274A", // middle of #272822 and #f92672 + "inputValidation.errorBorder": "#f92672", + "inputValidation.warningBackground": "#848528", // middle of #272822 and #e2e22e + "inputValidation.warningBorder": "#e2e22e", + "inputValidation.infoBackground": "#546190", // middle of #272822 and #819aff + "inputValidation.infoBorder": "#819aff", + "editorHoverWidget.background": "#414339", + "editorHoverWidget.border": "#75715E", + "editorSuggestWidget.background": "#272822", + "editorSuggestWidget.border": "#75715E", + "editorGroup.border": "#414339", + "peekView.border": "#75715E", + "peekViewEditor.background": "#272822", + "peekViewResult.background": "#1e1f1c", + "peekViewTitle.background": "#1e1f1c", + "peekViewResult.selectionBackground": "#414339", + "peekViewResult.matchHighlightBackground": "#75715E", + "peekViewEditor.matchHighlightBackground": "#75715E", + "terminal.ansiBlack": "#333333", + "terminal.ansiRed": "#C4265E", // the bright color with ~75% transparent on the background + "terminal.ansiGreen": "#86B42B", + "terminal.ansiYellow": "#B3B42B", + "terminal.ansiBlue": "#6A7EC8", + "terminal.ansiMagenta": "#8C6BC8", + "terminal.ansiCyan": "#56ADBC", + "terminal.ansiWhite": "#e3e3dd", + "terminal.ansiBrightBlack": "#666666", + "terminal.ansiBrightRed": "#f92672", + "terminal.ansiBrightGreen": "#A6E22E", + "terminal.ansiBrightYellow": "#e2e22e", // hue shifted #A6E22E + "terminal.ansiBrightBlue": "#819aff", // hue shifted #AE81FF + "terminal.ansiBrightMagenta": "#AE81FF", + "terminal.ansiBrightCyan": "#66D9EF", + "terminal.ansiBrightWhite": "#f8f8f2" + }, + "tokenColors": [ + { + "settings": { + "background": "#272822", + "foreground": "#F8F8F2" + } + }, + { + "name": "Comment", + "scope": "comment", + "settings": { + "foreground": "#75715E" + } + }, + { + "name": "String", + "scope": "string", + "settings": { + "foreground": "#E6DB74" + } + }, + { + "name": "Template Definition", + "scope": [ + "punctuation.definition.template-expression", + "punctuation.section.embedded.coffee" + ], + "settings": { + "foreground": "#F92672" + } + }, + { + "name": "Reset JavaScript string interpolation expression", + "scope": [ + "meta.template.expression" + ], + "settings": { + "foreground": "#F8F8F2" + } + }, + { + "name": "Number", + "scope": "constant.numeric", + "settings": { + "foreground": "#AE81FF" + } + }, + { + "name": "Built-in constant", + "scope": "constant.language", + "settings": { + "foreground": "#AE81FF" + } + }, + { + "name": "User-defined constant", + "scope": "constant.character, constant.other", + "settings": { + "foreground": "#AE81FF" + } + }, + { + "name": "Variable", + "scope": "variable", + "settings": { + "fontStyle": "", + "foreground": "#F8F8F2" + } + }, + { + "name": "Keyword", + "scope": "keyword", + "settings": { + "foreground": "#F92672" + } + }, + { + "name": "Storage", + "scope": "storage", + "settings": { + "fontStyle": "", + "foreground": "#F92672" + } + }, + { + "name": "Storage type", + "scope": "storage.type", + "settings": { + "fontStyle": "italic", + "foreground": "#66D9EF" + } + }, + { + "name": "Class name", + "scope": "entity.name.type, entity.name.class", + "settings": { + "fontStyle": "underline", + "foreground": "#A6E22E" + } + }, + { + "name": "Inherited class", + "scope": "entity.other.inherited-class", + "settings": { + "fontStyle": "italic underline", + "foreground": "#A6E22E" + } + }, + { + "name": "Function name", + "scope": "entity.name.function", + "settings": { + "fontStyle": "", + "foreground": "#A6E22E" + } + }, + { + "name": "Function argument", + "scope": "variable.parameter", + "settings": { + "fontStyle": "italic", + "foreground": "#FD971F" + } + }, + { + "name": "Tag name", + "scope": "entity.name.tag", + "settings": { + "fontStyle": "", + "foreground": "#F92672" + } + }, + { + "name": "Tag attribute", + "scope": "entity.other.attribute-name", + "settings": { + "fontStyle": "", + "foreground": "#A6E22E" + } + }, + { + "name": "Library function", + "scope": "support.function", + "settings": { + "fontStyle": "", + "foreground": "#66D9EF" + } + }, + { + "name": "Library constant", + "scope": "support.constant", + "settings": { + "fontStyle": "", + "foreground": "#66D9EF" + } + }, + { + "name": "Library class/type", + "scope": "support.type, support.class", + "settings": { + "fontStyle": "italic", + "foreground": "#66D9EF" + } + }, + { + "name": "Library variable", + "scope": "support.other.variable", + "settings": { + "fontStyle": "" + } + }, + { + "name": "Invalid", + "scope": "invalid", + "settings": { + "background": "#F92672", + "fontStyle": "", + "foreground": "#F8F8F0" + } + }, + { + "name": "Invalid deprecated", + "scope": "invalid.deprecated", + "settings": { + "background": "#AE81FF", + "foreground": "#F8F8F0" + } + }, + { + "name": "JSON String", + "scope": "meta.structure.dictionary.json string.quoted.double.json", + "settings": { + "foreground": "#CFCFC2" + } + }, + { + "name": "diff.header", + "scope": "meta.diff, meta.diff.header", + "settings": { + "foreground": "#75715E" + } + }, + { + "name": "diff.deleted", + "scope": "markup.deleted", + "settings": { + "foreground": "#F92672" + } + }, + { + "name": "diff.inserted", + "scope": "markup.inserted", + "settings": { + "foreground": "#A6E22E" + } + }, + { + "name": "diff.changed", + "scope": "markup.changed", + "settings": { + "foreground": "#E6DB74" + } + }, + { + "scope": "constant.numeric.line-number.find-in-files - match", + "settings": { + "foreground": "#AE81FFA0" + } + }, + { + "scope": "entity.name.filename.find-in-files", + "settings": { + "foreground": "#E6DB74" + } + }, + { + "name": "Markup Quote", + "scope": "markup.quote", + "settings": { + "foreground": "#F92672" + } + }, + { + "name": "Markup Lists", + "scope": "markup.list", + "settings": { + "foreground": "#E6DB74" + } + }, + { + "name": "Markup Styling", + "scope": "markup.bold, markup.italic", + "settings": { + "foreground": "#66D9EF" + } + }, + { + "name": "Markup Inline", + "scope": "markup.inline.raw", + "settings": { + "fontStyle": "", + "foreground": "#FD971F" + } + }, + { + "name": "Markup Headings", + "scope": "markup.heading", + "settings": { + "foreground": "#A6E22E" + } + }, + { + "name": "Markup Setext Header", + "scope": "markup.heading.setext", + "settings": { + "fontStyle": "", + "foreground": "#A6E22E" + } + }, + { + "scope": "token.info-token", + "settings": { + "foreground": "#6796e6" + } + }, + { + "scope": "token.warn-token", + "settings": { + "foreground": "#cd9731" + } + }, + { + "scope": "token.error-token", + "settings": { + "foreground": "#f44747" + } + }, + { + "scope": "token.debug-token", + "settings": { + "foreground": "#b267e6" + } + }, + { + "name": "this.self", + "scope": "variable.language", + "settings": { + "foreground": "#FD971F" + } + } + ] +} diff --git a/extensions/theme-quietlight/OSSREADME.json b/extensions/theme-quietlight/OSSREADME.json new file mode 100644 index 0000000000..ddb630bfd9 --- /dev/null +++ b/extensions/theme-quietlight/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "Colorsublime-Themes", + "version": "0.1.0", + "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", + "description": "The themes in this folders are copied from colorsublime.com. <<>>" +}] diff --git a/extensions/theme-quietlight/package.json b/extensions/theme-quietlight/package.json new file mode 100644 index 0000000000..b8c78c374c --- /dev/null +++ b/extensions/theme-quietlight/package.json @@ -0,0 +1,15 @@ +{ + "name": "theme-quietlight", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "themes": [ + { + "label": "Quiet Light", + "uiTheme": "vs", + "path": "./themes/quietlight-color-theme.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-quietlight/themes/QuietLight.tmTheme b/extensions/theme-quietlight/themes/QuietLight.tmTheme new file mode 100644 index 0000000000..34ee12f54c --- /dev/null +++ b/extensions/theme-quietlight/themes/QuietLight.tmTheme @@ -0,0 +1,625 @@ + + + + + author + Martin Kühl + comment + Based on the Quiet Light theme for Espresso by Ian Beck. + name + Quiet Light + settings + + + settings + + background + #F5F5F5 + caret + #000000 + foreground + #333333 + invisibles + #AAAAAA + lineHighlight + #E4F6D4 + selection + #C9D0D9 + + + + name + Comments + scope + comment, punctuation.definition.comment + settings + + fontStyle + italic + foreground + #AAAAAA + + + + name + Comments: Preprocessor + scope + comment.block.preprocessor + settings + + fontStyle + + foreground + #AAAAAA + + + + name + Comments: Documentation + scope + comment.documentation, comment.block.documentation + settings + + foreground + #448C27 + + + + name + Invalid - Deprecated + scope + invalid.deprecated + settings + + background + #96000014 + + + + name + Invalid - Illegal + scope + invalid.illegal + settings + + background + #96000014 + foreground + #660000 + + + + name + Operators + scope + keyword.operator + settings + + foreground + #777777 + + + + name + Keywords + scope + keyword, storage + settings + + foreground + #4B83CD + + + + name + Types + scope + storage.type, support.type + settings + + foreground + #7A3E9D + + + + name + Language Constants + scope + constant.language, support.constant, variable.language + settings + + foreground + #AB6526 + + + + name + Variables + scope + variable, support.variable + settings + + foreground + #7A3E9D + + + + name + Functions + scope + entity.name.function, support.function + settings + + fontStyle + bold + foreground + #AA3731 + + + + name + Classes + scope + entity.name.type, entity.other.inherited-class, support.class + settings + + fontStyle + bold + foreground + #7A3E9D + + + + name + Exceptions + scope + entity.name.exception + settings + + foreground + #660000 + + + + name + Sections + scope + entity.name.section + settings + + fontStyle + bold + + + + name + Numbers, Characters + scope + constant.numeric, constant.character, constant + settings + + foreground + #AB6526 + + + + name + Strings + scope + string + settings + + foreground + #448C27 + + + + name + Strings: Escape Sequences + scope + constant.character.escape + settings + + foreground + #777777 + + + + name + Strings: Regular Expressions + scope + string.regexp + settings + + foreground + #4B83CD + + + + name + Strings: Symbols + scope + constant.other.symbol + settings + + foreground + #AB6526 + + + + name + Punctuation + scope + punctuation + settings + + foreground + #777777 + + + + name + Embedded Source + scope + string source, text source + settings + + background + #EAEBE6 + + + + name + ----------------------------------- + settings + + + + name + HTML: Doctype Declaration + scope + meta.tag.sgml.doctype, meta.tag.sgml.doctype string, meta.tag.sgml.doctype entity.name.tag, meta.tag.sgml punctuation.definition.tag.html + settings + + foreground + #AAAAAA + + + + name + HTML: Tags + scope + meta.tag, punctuation.definition.tag.html, punctuation.definition.tag.begin.html, punctuation.definition.tag.end.html + settings + + foreground + #91B3E0 + + + + name + HTML: Tag Names + scope + entity.name.tag + settings + + foreground + #4B83CD + + + + name + HTML: Attribute Names + scope + meta.tag entity.other.attribute-name, entity.other.attribute-name.html + settings + + fontStyle + italic + foreground + #91B3E0 + + + + name + HTML: Entities + scope + constant.character.entity, punctuation.definition.entity + settings + + foreground + #AB6526 + + + + name + ----------------------------------- + settings + + + + name + CSS: Selectors + scope + meta.selector, meta.selector entity, meta.selector entity punctuation, entity.name.tag.css + settings + + foreground + #7A3E9D + + + + name + CSS: Property Names + scope + meta.property-name, support.type.property-name + settings + + foreground + #AB6526 + + + + name + CSS: Property Values + scope + meta.property-value, meta.property-value constant.other, support.constant.property-value + settings + + foreground + #448C27 + + + + name + CSS: Important Keyword + scope + keyword.other.important + settings + + fontStyle + bold + + + + name + ----------------------------------- + settings + + + + name + Markup: Changed + scope + markup.changed + settings + + background + #FFFFDD + foreground + #000000 + + + + name + Markup: Deletion + scope + markup.deleted + settings + + background + #FFDDDD + foreground + #000000 + + + + name + Markup: Emphasis + scope + markup.italic + settings + + fontStyle + italic + + + + name + Markup: Error + scope + markup.error + settings + + background + #96000014 + foreground + #660000 + + + + name + Markup: Insertion + scope + markup.inserted + settings + + background + #DDFFDD + foreground + #000000 + + + + name + Markup: Link + scope + meta.link + settings + + foreground + #4B83CD + + + + name + Markup: Output + scope + markup.output, markup.raw + settings + + foreground + #777777 + + + + name + Markup: Prompt + scope + markup.prompt + settings + + foreground + #777777 + + + + name + Markup: Heading + scope + markup.heading + settings + + foreground + #AA3731 + + + + name + Markup: Strong + scope + markup.bold + settings + + fontStyle + bold + + + + name + Markup: Traceback + scope + markup.traceback + settings + + foreground + #660000 + + + + name + Markup: Underline + scope + markup.underline + settings + + fontStyle + underline + + + + name + Markup Quote + scope + markup.quote + settings + + foreground + #7A3E9D + + + + name + Markup Lists + scope + markup.list + settings + + foreground + #4B83CD + + + + name + Markup Styling + scope + markup.bold, markup.italic + settings + + foreground + #448C27 + + + + name + Markup Inline + scope + markup.inline.raw + settings + + fontStyle + + foreground + #AB6526 + + + + name + ----------------------------------- + settings + + + + name + Extra: Diff Range + scope + meta.diff.range, meta.diff.index, meta.separator + settings + + background + #DDDDFF + foreground + #434343 + + + + name + Extra: Diff From + scope + meta.diff.header.from-file + settings + + background + #FFDDDD + foreground + #434343 + + + + name + Extra: Diff To + scope + meta.diff.header.to-file + settings + + background + #DDFFDD + foreground + #434343 + + + + uuid + 231D6A91-5FD1-4CBE-BD2A-0F36C08693F1 + + diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json new file mode 100644 index 0000000000..4ffeda5516 --- /dev/null +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -0,0 +1,504 @@ +{ + "name": "Quiet Light", + "tokenColors": [ + { + "settings": { + "background": "#F5F5F5", + "foreground": "#333333" + } + }, + { + "name": "Comments", + "scope": [ + "comment", + "punctuation.definition.comment" + ], + "settings": { + "fontStyle": "italic", + "foreground": "#AAAAAA" + } + }, + { + "name": "Comments: Preprocessor", + "scope": "comment.block.preprocessor", + "settings": { + "fontStyle": "", + "foreground": "#AAAAAA" + } + }, + { + "name": "Comments: Documentation", + "scope": [ + "comment.documentation", + "comment.block.documentation" + ], + "settings": { + "foreground": "#448C27" + } + }, + { + "name": "Invalid - Deprecated", + "scope": "invalid.deprecated", + "settings": { + "background": "#96000014" + } + }, + { + "name": "Invalid - Illegal", + "scope": "invalid.illegal", + "settings": { + "background": "#96000014", + "foreground": "#660000" + } + }, + { + "name": "Operators", + "scope": "keyword.operator", + "settings": { + "foreground": "#777777" + } + }, + { + "name": "Keywords", + "scope": [ + "keyword", + "storage" + ], + "settings": { + "foreground": "#4B83CD" + } + }, + { + "name": "Types", + "scope": [ + "storage.type", + "support.type" + ], + "settings": { + "foreground": "#7A3E9D" + } + }, + { + "name": "Language Constants", + "scope": [ + "constant.language", + "support.constant", + "variable.language" + ], + "settings": { + "foreground": "#AB6526" + } + }, + { + "name": "Variables", + "scope": [ + "variable", + "support.variable" + ], + "settings": { + "foreground": "#7A3E9D" + } + }, + { + "name": "Functions", + "scope": [ + "entity.name.function", + "support.function" + ], + "settings": { + "fontStyle": "bold", + "foreground": "#AA3731" + } + }, + { + "name": "Classes", + "scope": [ + "entity.name.type", + "entity.other.inherited-class", + "support.class" + ], + "settings": { + "fontStyle": "bold", + "foreground": "#7A3E9D" + } + }, + { + "name": "Exceptions", + "scope": "entity.name.exception", + "settings": { + "foreground": "#660000" + } + }, + { + "name": "Sections", + "scope": "entity.name.section", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Numbers, Characters", + "scope": [ + "constant.numeric", + "constant.character", + "constant" + ], + "settings": { + "foreground": "#AB6526" + } + }, + { + "name": "Strings", + "scope": "string", + "settings": { + "foreground": "#448C27" + } + }, + { + "name": "Strings: Escape Sequences", + "scope": "constant.character.escape", + "settings": { + "foreground": "#777777" + } + }, + { + "name": "Strings: Regular Expressions", + "scope": "string.regexp", + "settings": { + "foreground": "#4B83CD" + } + }, + { + "name": "Strings: Symbols", + "scope": "constant.other.symbol", + "settings": { + "foreground": "#AB6526" + } + }, + { + "name": "Punctuation", + "scope": "punctuation", + "settings": { + "foreground": "#777777" + } + }, + { + "name": "Embedded Source", + "scope": [ + "string source", + "text source" + ], + "settings": { + "background": "#EAEBE6" + } + }, + { + "name": "-----------------------------------", + "settings": {} + }, + { + "name": "HTML: Doctype Declaration", + "scope": [ + "meta.tag.sgml.doctype", + "meta.tag.sgml.doctype string", + "meta.tag.sgml.doctype entity.name.tag", + "meta.tag.sgml punctuation.definition.tag.html" + ], + "settings": { + "foreground": "#AAAAAA" + } + }, + { + "name": "HTML: Tags", + "scope": [ + "meta.tag", + "punctuation.definition.tag.html", + "punctuation.definition.tag.begin.html", + "punctuation.definition.tag.end.html" + ], + "settings": { + "foreground": "#91B3E0" + } + }, + { + "name": "HTML: Tag Names", + "scope": "entity.name.tag", + "settings": { + "foreground": "#4B83CD" + } + }, + { + "name": "HTML: Attribute Names", + "scope": [ + "meta.tag entity.other.attribute-name", + "entity.other.attribute-name.html" + ], + "settings": { + "fontStyle": "italic", + "foreground": "#91B3E0" + } + }, + { + "name": "HTML: Entities", + "scope": [ + "constant.character.entity", + "punctuation.definition.entity" + ], + "settings": { + "foreground": "#AB6526" + } + }, + { + "name": "-----------------------------------", + "settings": {} + }, + { + "name": "CSS: Selectors", + "scope": [ + "meta.selector", + "meta.selector entity", + "meta.selector entity punctuation", + "entity.name.tag.css" + ], + "settings": { + "foreground": "#7A3E9D" + } + }, + { + "name": "CSS: Property Names", + "scope": [ + "meta.property-name", + "support.type.property-name" + ], + "settings": { + "foreground": "#AB6526" + } + }, + { + "name": "CSS: Property Values", + "scope": [ + "meta.property-value", + "meta.property-value constant.other", + "support.constant.property-value" + ], + "settings": { + "foreground": "#448C27" + } + }, + { + "name": "CSS: Important Keyword", + "scope": "keyword.other.important", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "-----------------------------------", + "settings": {} + }, + { + "name": "Markup: Changed", + "scope": "markup.changed", + "settings": { + "background": "#FFFFDD", + "foreground": "#000000" + } + }, + { + "name": "Markup: Deletion", + "scope": "markup.deleted", + "settings": { + "background": "#FFDDDD", + "foreground": "#000000" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, + { + "name": "Markup: Error", + "scope": "markup.error", + "settings": { + "background": "#96000014", + "foreground": "#660000" + } + }, + { + "name": "Markup: Insertion", + "scope": "markup.inserted", + "settings": { + "background": "#DDFFDD", + "foreground": "#000000" + } + }, + { + "name": "Markup: Link", + "scope": "meta.link", + "settings": { + "foreground": "#4B83CD" + } + }, + { + "name": "Markup: Output", + "scope": [ + "markup.output", + "markup.raw" + ], + "settings": { + "foreground": "#777777" + } + }, + { + "name": "Markup: Prompt", + "scope": "markup.prompt", + "settings": { + "foreground": "#777777" + } + }, + { + "name": "Markup: Heading", + "scope": "markup.heading", + "settings": { + "foreground": "#AA3731" + } + }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Traceback", + "scope": "markup.traceback", + "settings": { + "foreground": "#660000" + } + }, + { + "name": "Markup: Underline", + "scope": "markup.underline", + "settings": { + "fontStyle": "underline" + } + }, + { + "name": "Markup Quote", + "scope": "markup.quote", + "settings": { + "foreground": "#7A3E9D" + } + }, + { + "name": "Markup Lists", + "scope": "markup.list", + "settings": { + "foreground": "#4B83CD" + } + }, + { + "name": "Markup Styling", + "scope": [ + "markup.bold", + "markup.italic" + ], + "settings": { + "foreground": "#448C27" + } + }, + { + "name": "Markup Inline", + "scope": "markup.inline.raw", + "settings": { + "fontStyle": "", + "foreground": "#AB6526" + } + }, + { + "name": "-----------------------------------", + "settings": {} + }, + { + "name": "Extra: Diff Range", + "scope": [ + "meta.diff.range", + "meta.diff.index", + "meta.separator" + ], + "settings": { + "background": "#DDDDFF", + "foreground": "#434343" + } + }, + { + "name": "Extra: Diff From", + "scope": "meta.diff.header.from-file", + "settings": { + "background": "#FFDDDD", + "foreground": "#434343" + } + }, + { + "name": "Extra: Diff To", + "scope": "meta.diff.header.to-file", + "settings": { + "background": "#DDFFDD", + "foreground": "#434343" + } + } + ], + "colors": { + "focusBorder": "#A6B39B", + "pickerGroup.foreground": "#A6B39B", + "pickerGroup.border": "#749351", + "list.activeSelectionForeground": "#6c6c6c", + "list.focusBackground": "#CADEB9", + "list.activeSelectionBackground": "#c4d9b1", + "list.inactiveSelectionBackground": "#d3dbcd", + "list.highlightForeground": "#9769dc", + "selection.background": "#C9D0D9", + "editor.background": "#F5F5F5", + "editorWhitespace.foreground": "#AAAAAA", + "editor.lineHighlightBackground": "#E4F6D4", + "editor.selectionBackground": "#C9D0D9", + "panel.background": "#F5F5F5", + "sideBar.background": "#F2F2F2", + "sideBarSectionHeader.background": "#ede8ef", + "editorLineNumber.foreground": "#9DA39A", + "editorCursor.foreground": "#54494B", + "inputOption.activeBorder": "#adafb7", + "dropdown.background": "#F5F5F5", + "editor.findMatchBackground": "#BF9CAC", + "editor.findMatchHighlightBackground": "#edc9d8", + "peekViewEditor.matchHighlightBackground": "#C2DFE3", + "peekViewTitle.background": "#F2F8FC", + "peekViewEditor.background": "#F2F8FC", + "peekViewResult.background": "#F2F8FC", + "peekView.border": "#705697", + "peekViewResult.matchHighlightBackground": "#93C6D6", + "statusBar.background": "#705697", + "statusBar.noFolderBackground": "#705697", + "statusBar.debuggingBackground": "#705697", + "activityBar.background": "#EDEDF5", + "activityBar.foreground": "#705697", + "activityBarBadge.background": "#705697", + "titleBar.activeBackground": "#c4b7d7", + "button.background": "#705697", + "notification.background": "#442e66", + "editorGroup.dropBackground": "#C9D0D988", + "inputValidation.infoBorder": "#4ec1e5", + "inputValidation.infoBackground": "#f2fcff", + "inputValidation.warningBackground": "#fffee2", + "inputValidation.warningBorder": "#ffe055", + "inputValidation.errorBackground": "#ffeaea", + "inputValidation.errorBorder": "#f1897f", + "errorForeground": "#f1897f", + "badge.background": "#705697AA", + "progressBar.background": "#705697" + } +} \ No newline at end of file diff --git a/extensions/theme-red/OSSREADME.json b/extensions/theme-red/OSSREADME.json new file mode 100644 index 0000000000..ddb630bfd9 --- /dev/null +++ b/extensions/theme-red/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "Colorsublime-Themes", + "version": "0.1.0", + "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", + "description": "The themes in this folders are copied from colorsublime.com. <<>>" +}] diff --git a/extensions/theme-red/package.json b/extensions/theme-red/package.json new file mode 100644 index 0000000000..f2b0403c69 --- /dev/null +++ b/extensions/theme-red/package.json @@ -0,0 +1,15 @@ +{ + "name": "theme-red", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "themes": [ + { + "label": "Red", + "uiTheme": "vs-dark", + "path": "./themes/Red-color-theme.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json new file mode 100644 index 0000000000..94d2c20ae2 --- /dev/null +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -0,0 +1,61 @@ +{ + "tokenColors": "./red.tmTheme", + "colors": { + // window + "activityBar.background": "#580000", + "tab.inactiveBackground": "#300a0a", + "tab.activeBackground": "#490000", + "sideBar.background": "#330000", + "statusBar.background": "#700000", + "statusBar.noFolderBackground": "#700000", + "editorGroupHeader.tabsBackground": "#330000", + "titleBar.activeBackground": "#770000", + "titleBar.inactiveBackground": "#772222", + "selection.background": "#ff777788", + // editor + "editor.background": "#390000", + "editorGroup.border": "#ff666633", + "editorGroup.background": "#1c0101", + "editorCursor.foreground": "#970000", + "editor.foreground": "#F8F8F8", + "editorWhitespace.foreground": "#c10000", + "editor.selectionBackground": "#750000", + "editorLineNumber.foreground": "#ff777788", + "editorWidget.background": "#300000", + "editorHoverWidget.background": "#300000", + "editorSuggestWidget.background": "#300000", + "editorSuggestWidget.border": "#220000", + "editor.lineHighlightBackground": "#ff000033", + "editor.hoverHighlightBackground": "#ff000044", + "editor.selectionHighlightBackground": "#f5500039", + "editorLink.activeForeground": "#FFD0AA", + "peekViewTitle.background": "#550000", + "peekView.border": "#ff000044", + "peekViewResult.background": "#400000", + "peekViewEditor.background": "#300000", + // UI + "debugToolBar.background": "#660000", + "focusBorder": "#ff6666aa", + "button.background": "#833", + "dropdown.background": "#580000", + "input.background": "#580000", + "inputOption.activeBorder": "#cc0000", + "inputValidation.infoBackground": "#550000", + "inputValidation.infoBorder": "#DB7E58", + "list.hoverBackground": "#800000", + "list.activeSelectionBackground": "#880000", + "list.inactiveSelectionBackground": "#770000", + "list.dropBackground": "#662222", + "list.focusBackground": "#660000", + "list.highlightForeground": "#ff4444", + "notification.background": "#662222", + "pickerGroup.foreground": "#cc9999", + "pickerGroup.border": "#ff000033", + "badge.background": "#cc3333", + "progressBar.background": "#cc3333", + "errorForeground": "#ffeaea", + "extensionButton.prominentBackground": "#cc3333", + "extensionButton.prominentHoverBackground": "#cc333388" + }, + "name": "Red" +} \ No newline at end of file diff --git a/extensions/theme-red/themes/red.tmTheme b/extensions/theme-red/themes/red.tmTheme new file mode 100644 index 0000000000..2e7039551f --- /dev/null +++ b/extensions/theme-red/themes/red.tmTheme @@ -0,0 +1,514 @@ + + + + + name + Red + settings + + + settings + + background + #390000 + caret + #970000 + foreground + #F8F8F8 + invisibles + #c10000 + lineHighlight + #0000004A + selection + #750000 + + + + name + Comment + scope + comment + settings + + fontStyle + italic + foreground + #e7c0c0ff + + + + name + Constant + scope + constant + settings + + fontStyle + + foreground + #994646ff + + + + name + Keyword + scope + keyword + settings + + fontStyle + + foreground + #f12727ff + + + + name + Entity + scope + entity + settings + + fontStyle + + foreground + #fec758ff + + + + name + Storage + scope + storage + settings + + fontStyle + bold + foreground + #ff6262ff + + + + name + String + scope + string + settings + + fontStyle + + foreground + #cd8d8dff + + + + name + Support + scope + support + settings + + fontStyle + + foreground + #9df39fff + + + + name + Variable + scope + variable + settings + + fontStyle + italic + foreground + #fb9a4bff + + + + name + Invalid + scope + invalid + settings + + background + #fd6209ff + foreground + #ffffffff + + + + name + Embedded Source + scope + text source + settings + + background + #b0b3ba14 + + + + name + Embedded Source (Bright) + scope + text.html.ruby source + settings + + background + #b1b3ba21 + + + + name + Entity inherited-class + scope + entity.other.inherited-class + settings + + fontStyle + underline + foreground + #aa5507ff + + + + name + String embedded-source + scope + string.quoted source + settings + + fontStyle + + foreground + #9df39fff + + + + name + String constant + scope + string constant + settings + + fontStyle + + foreground + #ffe862ff + + + + name + String.regexp + scope + string.regexp + settings + + foreground + #ffb454ff + + + + name + String variable + scope + string variable + settings + + foreground + #edef7dff + + + + name + Support.function + scope + support.function + settings + + fontStyle + + foreground + #ffb454ff + + + + name + Support.constant + scope + support.constant + settings + + fontStyle + + foreground + #eb939aff + + + + name + Doctype/XML Processing + scope + declaration.sgml.html declaration.doctype, declaration.sgml.html declaration.doctype entity, declaration.sgml.html declaration.doctype string, declaration.xml-processing, declaration.xml-processing entity, declaration.xml-processing string + settings + + fontStyle + + foreground + #73817dff + + + + name + Meta.tag.A + scope + declaration.tag, declaration.tag entity, meta.tag, meta.tag entity + settings + + fontStyle + + foreground + #ec0d1eff + + + + name + css tag-name + scope + meta.selector.css entity.name.tag + settings + + fontStyle + + foreground + #aa5507ff + + + + name + css#id + scope + meta.selector.css entity.other.attribute-name.id + settings + + foreground + #fec758ff + + + + name + css.class + scope + meta.selector.css entity.other.attribute-name.class + settings + + fontStyle + + foreground + #41a83eff + + + + name + css property-name: + scope + support.type.property-name.css + settings + + fontStyle + + foreground + #96dd3bff + + + + name + css property-value; + scope + meta.property-group support.constant.property-value.css, meta.property-value support.constant.property-value.css + settings + + fontStyle + italic + foreground + #ffe862ff + + + + name + css additional-constants + scope + meta.property-value support.constant.named-color.css, meta.property-value constant + settings + + fontStyle + + foreground + #ffe862ff + + + + name + css @at-rule + scope + meta.preprocessor.at-rule keyword.control.at-rule + settings + + foreground + #fd6209ff + + + + name + css constructor.argument + scope + meta.constructor.argument.css + settings + + fontStyle + + foreground + #ec9799ff + + + + name + diff.header + scope + meta.diff, meta.diff.header + settings + + background + #0b2f20ff + fontStyle + italic + foreground + #f8f8f8ff + + + + name + diff.deleted + scope + markup.deleted + settings + + background + #fedcddff + foreground + #ec9799ff + + + + name + diff.changed + scope + markup.changed + settings + + background + #c4b14aff + foreground + #f8f8f8ff + + + + name + diff.inserted + scope + markup.inserted + settings + + background + #9bf199ff + foreground + #41a83eff + + + + + name + Markup Quote + scope + markup.quote + settings + + foreground + #f12727ff + + + + name + Markup Lists + scope + markup.list + settings + + foreground + #ff6262ff + + + + name + Markup Styling + scope + markup.bold, markup.italic + settings + + foreground + #fb9a4bff + + + + name + Markup Inline + scope + markup.inline.raw + settings + + fontStyle + + foreground + #cd8d8dff + + + + name + Markup Headings + scope + markup.heading + settings + + foreground + #fec758ff + + + + name + Markup Setext Header + scope + markup.heading.setext + settings + + fontStyle + + foreground + #fec758ff + + + + + + + uuid + AC0A28C5-65E6-42A6-AD05-4D01735652EE + colorSpaceName + sRGB + semanticClass + theme.dark.django + + \ No newline at end of file diff --git a/extensions/theme-seti/.vscodeignore b/extensions/theme-seti/.vscodeignore new file mode 100644 index 0000000000..e390b124a6 --- /dev/null +++ b/extensions/theme-seti/.vscodeignore @@ -0,0 +1 @@ +build/** \ No newline at end of file diff --git a/extensions/theme-seti/OSSREADME.json b/extensions/theme-seti/OSSREADME.json new file mode 100644 index 0000000000..7601d594b3 --- /dev/null +++ b/extensions/theme-seti/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "seti-ui", + "version": "0.1.0", + "repositoryURL": "https://github.com/jesseweed/seti-ui", + "description": "The file ./icons/seti.woff has been copied from https://github.com/jesseweed/seti-ui/blob/master/styles/_fonts/seti/seti.woff" +}] diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js new file mode 100644 index 0000000000..1a6c26f4e0 --- /dev/null +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -0,0 +1,331 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +var path = require('path'); +var fs = require('fs'); +var https = require('https'); +var url = require('url'); + +function getCommitSha(repoId, repoPath) { + var commitInfo = 'https://api.github.com/repos/' + repoId + '/commits?path=' + repoPath; + return download(commitInfo).then(function (content) { + try { + let lastCommit = JSON.parse(content)[0]; + return Promise.resolve({ + commitSha: lastCommit.sha, + commitDate: lastCommit.commit.author.date + }); + } catch (e) { + return Promise.resolve(null); + } + }, function () { + console.err('Failed loading ' + commitInfo); + return Promise.resolve(null); + }); +} + +function download(source) { + if (source.startsWith('.')) { + return readFile(source); + } + return new Promise((c, e) => { + var _url = url.parse(source); + var options = { host: _url.host, port: _url.port, path: _url.path, headers: { 'User-Agent': 'NodeJS' }}; + var content = ''; + https.get(options, function (response) { + response.on('data', function (data) { + content += data.toString(); + }).on('end', function () { + c(content); + }); + }).on('error', function (err) { + e(err.message); + }); + }); +} + +function readFile(fileName) { + return new Promise((c, e) => { + fs.readFile(fileName, function(err, data) { + if (err) { + e(err); + } else { + c(data.toString()); + } + }); + }); +} + +function downloadBinary(source, dest) { + if (source.startsWith('.')) { + return copyFile(source, dest); + } + + return new Promise((c, e) => { + https.get(source, function (response) { + switch(response.statusCode) { + case 200: + var file = fs.createWriteStream(dest); + response.on('data', function(chunk){ + file.write(chunk); + }).on('end', function(){ + file.end(); + c(null); + }).on('error', function (err) { + fs.unlink(dest); + e(err.message); + }); + break; + case 301: + case 302: + case 303: + case 307: + console.log('redirect to ' + response.headers.location); + downloadBinary(response.headers.location, dest).then(c, e); + break; + default: + e(new Error('Server responded with status code ' + response.statusCode)); + } + }); + }); +} + +function copyFile(fileName, dest) { + return new Promise((c, e) => { + var cbCalled = false; + function handleError(err) { + if (!cbCalled) { + e(err); + cbCalled = true; + } + } + var rd = fs.createReadStream(fileName); + rd.on("error", handleError); + var wr = fs.createWriteStream(dest); + wr.on("error", handleError); + wr.on("close", function() { + if (!cbCalled) { + c(); + cbCalled = true; + } + }); + rd.pipe(wr); + }); +} + +function invertColor(color) { + var res = '#'; + for (var i = 1; i < 7; i+=2) { + var newVal = 255 - parseInt('0x' + color.substr(i, 2), 16); + res += newVal.toString(16); + } + return res; +} + +function getLanguageMappings() { + let langMappings = {} + var allExtensions = fs.readdirSync('..'); + for (var i= 0; i < allExtensions.length; i++) { + let dirPath = path.join('..', allExtensions[i], 'package.json'); + if (fs.existsSync(dirPath)) { + let content = fs.readFileSync(dirPath).toString(); + let jsonContent = JSON.parse(content); + let languages = jsonContent.contributes && jsonContent.contributes.languages; + if (Array.isArray(languages)) { + for (var k = 0; k < languages.length; k++) { + var languageId = languages[k].id; + if (languageId) { + var extensions = languages[k].extensions; + var mapping = {}; + if (Array.isArray(extensions)) { + mapping.extensions = extensions.map(function (e) { return e.substr(1).toLowerCase(); }); + } + var filenames = languages[k].filenames; + if (Array.isArray(filenames)) { + mapping.fileNames = filenames.map(function (f) { return f.toLowerCase(); }); + } + langMappings[languageId] = mapping; + } + } + } + } + } + return langMappings; +} + +//var font = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti/seti.woff'; +var font = '../../../seti-ui/styles/_fonts/seti/seti.woff'; + +exports.copyFont = function() { + return downloadBinary(font, './icons/seti.woff'); +}; + +//var fontMappings = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti.less'; +//var mappings = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/components/icons/mapping.less'; +//var colors = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/ui-variables.less'; + +var fontMappings = '../../../seti-ui/styles/_fonts/seti.less'; +var mappings = '../../../seti-ui/styles/components/icons/mapping.less'; +var colors = '../../../seti-ui/styles/ui-variables.less'; + +exports.update = function () { + + console.log('Reading from ' + fontMappings); + var def2Content = {}; + var ext2Def = {}; + var fileName2Def = {}; + var def2ColorId = {}; + var colorId2Value = {}; + var lang2Def = {}; + + function writeFileIconContent(info) { + var iconDefinitions = {}; + + for (var def in def2Content) { + var entry = { fontCharacter: def2Content[def] }; + var colorId = def2ColorId[def]; + if (colorId) { + var colorValue = colorId2Value[colorId]; + if (colorValue) { + entry.fontColor = colorValue; + + var entryInverse = { fontCharacter: entry.fontCharacter, fontColor: invertColor(colorValue) }; + iconDefinitions[def + '_light'] = entryInverse; + } + } + iconDefinitions[def] = entry; + } + + function getInvertSet(input) { + var result = {}; + for (var assoc in input) { + let invertDef = input[assoc] + '_light'; + if (iconDefinitions[invertDef]) { + result[assoc] = invertDef; + } + } + return result; + } + + var res = { + information_for_contributors: [ + 'This file has been generated from data in https://github.com/jesseweed/seti-ui:', + '- icon definitions: styles/_fonts/seti.less', + '- icon colors: styles/ui-variables.less', + '- file associations: styles/icons/mapping.less', + 'If you want to provide a fix or improvement, please create a pull request against the jesseweed/seti-ui repository.', + 'Once accepted there, we are happy to receive an update request.', + ], + fonts: [{ + id: "seti", + src: [{ "path": "./seti.woff", "format": "woff" }], + weight: "normal", + style: "normal", + size: "150%" + }], + iconDefinitions: iconDefinitions, + // folder: "_folder", + file: "_default", + fileExtensions: ext2Def, + fileNames: fileName2Def, + languageIds: lang2Def, + light: { + file: "_default_light", + fileExtensions: getInvertSet(ext2Def), + languageIds: getInvertSet(lang2Def), + fileNames: getInvertSet(fileName2Def) + }, + version: 'https://github.com/jesseweed/seti-ui/commit/' + info.commitSha, + }; + + var path = './icons/vs-seti-icon-theme.json'; + fs.writeFileSync(path, JSON.stringify(res, null, '\t')); + console.log('written ' + path); + } + + + var match; + + return download(fontMappings).then(function (content) { + var regex = /@([\w-]+):\s*'(\\E[0-9A-F]+)';/g; + while ((match = regex.exec(content)) !== null) { + def2Content['_' + match[1]] = match[2]; + } + + return download(mappings).then(function (content) { + var regex2 = /\.icon-(?:set|partial)\('([\w-\.]+)',\s*'([\w-]+)',\s*(@[\w-]+)\)/g; + while ((match = regex2.exec(content)) !== null) { + let pattern = match[1]; + let def = '_' + match[2]; + let colorId = match[3]; + if (pattern[0] === '.') { + ext2Def[pattern.substr(1).toLowerCase()] = def; + } else { + fileName2Def[pattern.toLowerCase()] = def; + } + def2ColorId[def] = colorId; + } + // replace extensions for languageId + var langMappings = getLanguageMappings(); + for (var lang in langMappings) { + var mappings = langMappings[lang]; + var exts = mappings.extensions || []; + var fileNames = mappings.fileNames || []; + var preferredDef = null; + // use the first file association for the preferred definition + for (let i1 = 0; i1 < exts.length && !preferredDef; i1++) { + preferredDef = ext2Def[exts[i1]]; + } + // use the first file association for the preferred definition + for (let i1 = 0; i1 < fileNames.length && !preferredDef; i1++) { + preferredDef = fileName2Def[fileNames[i1]]; + } + if (preferredDef) { + lang2Def[lang] = preferredDef; + for (let i2 = 0; i2 < exts.length; i2++) { + // remove the extension association, unless it is different from the preferred + if (ext2Def[exts[i2]] === preferredDef) { + delete ext2Def[exts[i2]]; + } + } + for (let i2 = 0; i2 < fileNames.length; i2++) { + // remove the fileName association, unless it is different from the preferred + if (fileName2Def[fileNames[i2]] === preferredDef) { + delete fileName2Def[fileNames[i2]]; + } + } + } + } + + + return download(colors).then(function (content) { + var regex3 = /(@[\w-]+):\s*(#[0-9a-z]+)/g; + while ((match = regex3.exec(content)) !== null) { + colorId2Value[match[1]] = match[2]; + } + return getCommitSha('jesseweed/seti-ui', 'styles/_fonts/seti.less').then(function (info) { + try { + writeFileIconContent(info); + if (info) { + console.log('Updated to jesseweed/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); + } + + } catch (e) { + console.error(e); + } + }); + }); + }); + }, console.error); +}; + +if (path.basename(process.argv[1]) === 'update-icon-theme.js') { + exports.copyFont().then(() => exports.update()); +} + + + diff --git a/extensions/theme-seti/icons/dashboard.svg b/extensions/theme-seti/icons/dashboard.svg new file mode 100644 index 0000000000..c5c67d4001 --- /dev/null +++ b/extensions/theme-seti/icons/dashboard.svg @@ -0,0 +1 @@ +dashboard \ No newline at end of file diff --git a/extensions/theme-seti/icons/dashboard_inverse.svg b/extensions/theme-seti/icons/dashboard_inverse.svg new file mode 100644 index 0000000000..c27b357454 --- /dev/null +++ b/extensions/theme-seti/icons/dashboard_inverse.svg @@ -0,0 +1 @@ +dashboard_inverse \ No newline at end of file diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff new file mode 100644 index 0000000000000000000000000000000000000000..b823e159a1db496854d436a8e81fc24d1c4be031 GIT binary patch literal 32180 zcmY&eQ*B1b#>6(h*tRCNZQHi(WMbRKgcIAw#LmvY`?UAmI^9*LZ{xAM+Fe0h z8~_6N7rxB^xc}O|jQ`{R_5XiJsHh1803gu+RGxoOC5|EYR8V1L`KO8gQU0O%|&hF3as8&5L;09+CPaIOLXs!joDk`flCh9&@j`@_FFhJWBd zl97qB_$LAY9%TPG-an8*-$F!K*t&T9(**x<`hW92Tqy$pHulEm!~3&s+I0+fEi_x(FX&efk#byX#N@xOdbaiOc&5j6&ev=pl#kgyf2xds6+97sU> zqUWxSG34c&Jj1W&OR%PE&x!G&^ik|(tihQhy=!dD;bzAyb?qFi`4Ds_DAAUNFfy8>PFk8mc(j@$ zR?GEiuGQvXc$VeGAKSxbt>}GY9Kf|1M7PH1txH4u2799SvwZ**_po7{L(293`Vey* z)(TqAA$mFYkmihIp59w~zZipKreCc?DhMRbKJH4#0261&i0!pQy7%S@2$JWpao%l; zIqwMsqvr^8aW}(P9%uIvfpN@z9+J-?TK?-Gc=8C8(!sZe3PVetzkuA4rqb&JTSRs2 zq{`1anpqX!thi-Nw>MMD=gd3!OyQym6rZ5g(jt zuO1bPJ{gNX9g96mCgZ=v$YeyzWW>sBi<;JmnBEXQrWHG`5lDM~BaJmCjXfZZMI(((CXH30 zx#6Z?;Q6t@L%YCJewTModZm}>mM{4pB>7$>`JN>C-YD6nU*aoP;;UA|N4!9YZiy7x zT=y@do9!c-!^PG)#n#(I*W1L_-9^{k#nzui*Pq4Kkwn*##Ma|Q*W<<36-3t+#MWm; z*Js7n8AaC_#n$UY*ME>+DJ4ExCqCgNK4~XD@gzQFNP6p(eCm~a3YB~+9euYcke>Kp za-T$xbEA=-$VTPds~zUp|I@IzPr}E!1;l1v$(?5G6P?S35B0b7o!LBcQo3PsOf)=+ zyk@zdbDW>OtFG1Co_=kpE%9wF%GB^JvTkL~$CxFHOU7W5v6;ox*E10(*x(o%ju^Eu zOPfpuN{00Z4|5R5fN_Y2VM{dVi8{orgm$tP8j>)W5ki$FOt;zcg*i)JKh!{T$`xt zIUJ_%yEoo8PCT2a>*nhzTkrjsv4+0yrCOcu;>OKe;80grxc_c#I?(MnG;UYna zXdm%XJMjj44~8G^unirAp3|nn8Nw@}$VnMy$iSA;FQ?=CgcX7*n0F0rWntaHX7I(( zEtJp3>UNmsn#Ef_VLw)Dx2(K5>U*@6#?s`DG6w2J^sNThH)%PmG>)a(3bmt z^I{>O5bOrIBpse8!j26Px2yqIyPzM_GKni_V4|yOf+{z5--q;G=%W%(!(K6pgj6Wt1C%k5Lwh`>}IBbxhX>2Ifke?LE|8+_tzV~#b?++5Z z9Zp<5{152hinH`sa*2#YCb}OsltOB0Bk9}k+l;=afAc@bi;$qs{O8ZtYu&PoLK)cH zg9oP-M!9|`uc4Q*GKjIH_k6xyUbC*)08eMjMA*NXAMB8?^a&K8&d|R$P;^x0Nykhzsjy>?|)zS<(aN2;wjFO1wFaMA6<5Xj5K@oOM2hr0C3M6jHg~ z%`6Dl3Ed34UD8Ez$7Vkdt7KYDZ3$PmWvEcFW@YT0uqd)^E_FM1VTTO5m-#!-fL+!U zZPI3?`q^r`+Ubrv7X7<>9t#4drdN#hh_~H|=^6h8&ETqFe^s5cV|~;@e*8pLDY64Eon=Nk7=d(BtU?AS&z5w zE8wg8WPp*%g*RYR4d;+-V+Lgfo6sKW;BGH<#lpQKpD*bOyv@h&n%)e(VMWu>4vq*>(?TXU!z6 zWaq_%#-R<(?}mzGo@{a?0*W!I|_HUI;Q)!?R#c($;ww zp$x&Rqn>}rW}v_>DpV9Y>jsq^R@t>_p~#-A2K2Lniny0tjpiLeTm}_41cI1g{xpC+j$o+ex+-+|1NwRPmw*5hvq9%LI9^FV#wucRx{1S=rq-5Xlde# zk=5Y3^En}AEB5_ld?$&-RjN?Qh9DlLrwTvK0oKk%S~FWYNqaV>X;!I`d?25KjhQbr zy5@SWJlbt^yF_a^(;X|Ld5LvkIZQ2ZbIO6bD}qiAhkt#zJ!hp8`enqp-Z#6?uy=GRuT37eq}t z{gUr{CK~Jc=}FSHadW+%0q5XiCA42v)`?D{*IP1}>e&QuDRiwG$&OO^tsiasB& zR_r3tCnzUKng#)S)yhj@^L^1n;R)oqA=r9SEwMo8q3$^-heUp>(!V12-o4^M?jSL5 zHN?_dzx`&vE~K8>`^44+AlF^Ql9tMpPh5$o`~v7;hG)3vimufWp@Y46a?)&RS-`r6 zkCqOfft>kBdAd`6jFdN%(>>M7buNQZJ$Kkdn^|Wwj+TELyOso?1Dvy!vI6bgSVsfZ z4y;(u&x~&Dwe%zd!G!qKH~WSP6%QJuzKh)id46rhsAk^;Pr3#rC|iPnORP!&65uXj zIO}!J-~oA3fosX_uyGbmV)N@HCKIn$$R(=>@cU&v6glQZ@YRv?Cjb zqv&bYj895ed)iOLue>^R^*WA@l9EMAHeoAL^n)liXx$Ea%ML=yB<^L1m26R1pvLb; zosie4DT`{lT6g$&oD8w4uEK4;%t`L4P1mLtx zWxNA;3P!XFk$AW|$eZOC;*`TmnZZO?LALZkmjuoMC}}?hdd&PJNNeu|8%xT^-#sdb zI2D!_UIl87ftKDV9X$(6XZiS?npZC$h_0TF)?9s1*Oltyx}S}q3OZ%(twYN#93QLX zKmJrg5T*tNi3k5xM+1fS&q<)9Di`>RS%=Akv4QopSBHo(Xe(qXl6Snu+Z2j`(l3ltGv6+IGQLHc74@PLRq07h z>#zKfiOvnbKUIEQ7_OMD&P=?rM9q7X9gpX}d05#~<^fS@;DW6zr-1X)R_bD>>23pJ za`p@d)}83gHST85yY_N-Zv$P$vmCq{{tZax(H=9%&QoXAx-2h)RJr=Q66=0~ys9pK zkkp?}g7aps!HL10L(|pZr!mw=5Kl2Npz#^?uVID37V!;oYfWc@UqhF z#4eTi)q!8I@2U0n>KF1=H})~b=Lm5r@-e3XLU$XIy6Ms}UfUww;AIXRj76|I=v6Fn zbSR7~z_7mPZ$NjMZ-^-z8Cy;x#a8KgRwv(xjG#-hC7LDJ_xvWlT;VXxtMpC<-Ui;M z$H?%^t9P>5dIgw?J_@yfmo_Jpje$Y?zAJ9iUIyW##mo*{Y?c#h^f+hpuCFtJ>%6@k3`&-Gd^7D;jgnN zMAk>bDBc;_^pnc;vq6t#^RN%(j=|?X5r2dSnW_RG5EePo!;POYQ1!T^xZDX~Z#*Hn zU>CvU!tM{nulg(g=+ zoczpPe_`Xl0n_u(1!A11w8ce&%f&%2AZbN#;B)pM-SewARkUKs{U1|(Lv~7x8MO^r zJ-R6yu(e=sGkh;j5qe~JXnT75Z>rrJYx($c~%^(m}?{A`4&&ZfDxM;KJ zuNX4PjoCuV;|TJ!r3boHB5_`7CcVR##1J~1@i=7iU_!^=s6(GpQ;9-a(!bb+LIZB& z^w2>CGCp^Zwo${k*dL|xKB4j)=Iw?l=?3K9zt!t@4JsTwP|sbn9>3GA)JPo4O`&gY zFcwmO{gu=eJJ>oe{1hKJ!PsPmy6mjU4bc4h4%I#2B^)W9ut=Jv(C`Dq0KJ%Y)Dv1v z))TWsm<5*&aEPnC8yhw^eu8oDbu7q@yV!Dtd@IPgxlu?|^g92wKz~3fVxf@b$p;mU zoh;Ms?L|xVtvk@@US9u^+;Vlr-rB{(?dQ{2pz>D!@V#Iq?e-z!;FUJTmn7rF%i}-a zx2NnT<3`&(yMwvy5DxU3>mw%~2p*dy@&WK;t)lm02Bi#k z_IE|FeYx&@3uoA}I+BXyeOR2dlek0S8p{XW99L9z0qWw%j>*N zmj|^{jwCiEXX|ls%Sgz!RZ>@6_o3Q2*9#5|uh_nj%Na1&>%@8k+_dQSBLp&st~5p= z%@;gYX!j_AgctN?&Q_Z4L!Ndzt#I!T?hLbq;y?rJA`z5cnOh2b%J|uvkJ)b=feD8% z&%4(%O7r}j-vYp{qS><>T3@**2(q)gG4oyx= z1%1Y#tGm~U+C8*vUd zDDCF@5`7lm4E|sQtb22l5|b1E@xImV^?tcNp_)l$hYE%RTMq@%+bx^tC!-f{n|dd)oxwh-Qbp-F5LRkM942J`%!9H-*O~!ID zyVW_j)#~8!chZ213ZU1gExpt3JQuvC7SGKn8^7M%4+nC=G%`Rnmc=gIR4; z-9x1C0PVx1>My%1-)$0m8!zw>=#@1bSA1V{c0BVCTC`Gu_uy?*HDFB3^gcF48?0W; zOK?TFk#lTVzuCoH>eb};TX=N(42!MM^E5r)6c?#I2Vf|_P$zJ|YCJo4n(5wifs^HP zw;WE?`$F6-}2jYgiM|m9=N_^J?nV2nfm1Oyj)VSH20w?iZvHY2Pb2}BA&}RYVbRiV#ny^ z+aS2mWn;aow`CrB^3%;V$?MUq#_#blxAb_R^eT8jpm#Hu2mYJrT-N;2AUr3dHH}n) z!cRSZsHqlR!`^)vl#^fa{_{2d#=V7!A#D})A;Wq!Xc1M8Rmwa;-m64ZnqYXG>x}!%_Yj`cyByMNpIk5R_rec>x4!j~#}D%f=KY@M z-1zQfo8B|IdqzGVF@O9L|Jn{Mm`g`n^?L_7wlmtmice!0G~v7pL~ zLg%JU%JIXMF;ItSvwY9PXos}-xhHu)zLR}EqGt!5DGBoh?mZlRGH(zYi!1g(!X{ma z&4Y1XmrDhFioVxopNPJ}+z!u{jr~1Na{XU^TI+|;**9nh>d%?0jh&7Fz<8LEl)geL zgq2W2=r+lz`5ezZG}4Yf?EUVMrcrrb^V_XAIKJ=fzmB6TXwQC+?YQLH`tQ|ETy&*5 zoYeW@6$c!yWlvn3*cUlq-QDYSgO%pJ z#~_1dG3F;|nkwsbM4a3t?2IHX?MaIS+1+0ezDN%byefi1O!Pr<$SAbmp)AJ95M@2` z!zQB#;`&IWH~~+8$XzDFV8N0F%*Z64kxCXC45AyO{_IW=+pCi99k_xcBH*x{yk4pl ztI@Sm%b2HPrfoWh(q%Ht$uK#qW#eO7gQ%dU03%Sv0v@^Q0-JHMYjs)reeAtz<+PKO zx0){tX10K|U1{@Yo;fIauX22P3NFTcH*bi{F1gUp7Wbu*^fpy09NHI9x?}aOy=mMA zmv9rVzF!H1p(!=)1zvG>AL>I)gQ6bp%1$fVNlR}pg)23<`DDO0BfE1*thB2X`kO_P zM{+3G2v*Ks-^r~cOV#Yns-JQSP=%L&JVe{HP*cGS`8wp6UTO(EfwkO(i2oazgYG zGO3doe2)-M;=6jgx(TV=fbxar*=?@AX9IITPVcDNhJqdL7lH2>y-WP={dmUt*+9Ds z%kmcMy$PSdKrQh8^dPApM-f5#5#&(G2sEz_KsX7D>#BQ;;CPJ86J-!k(dA&B1&JG{ z40%AMN4l_~w|hgncdPMN$N?}Z;%)u>BY%Yc-FA;B2bI$*zp9G@83oRdv{^Wj@8v{d znB!HEz*iC^uI@&ph;bz+Y-Sg|}QRAV(z=MK+|jyc+`i)b`S zAuE_E`#Kg24f`qH^QA9K0kes#{;9tk)`T6-%u}#c<5LFrG+O_KzH6)Zf^y$`erEu3 zzYn@y@sNx@6stX1E-*en@z2X}fCPFK4!Bc8qjk49ND-1Ng6Jk=bYQLULLN8R6@nN| z(V6UD;+z^;y!8%MS_b(J37K(S$Y}1us>{&`DL!+9z->0Kr+V#XeQuv!C(X!rA0NWi z^1S~diz@1x1u}q1Ia2cC*vPsQjNJktT`Fr;Rt%3=e$p7&DIPcT%*AS{kQK2CX7d^k z?-wbr2${UbLWkW(r2LTyrCN7mOTTBbE+ZWFFOo zn$$vv`<4W%C#nW;ogvGQc_3V$(Fob?myHKskTLqq+!Fnm%nQgFi$Fqc z9ZpGgWx=^=Br|&zB&WHoK$(;J!U=7da~E7jQ5lLh4DH(KPN)!Wv{E-o z7fw{$~*q}Uuof#_K8l=P>j> zun9i$GH+Fin<`7aq4YD^X2WHRMX;29mLMe?pRUkG_c|*M4H&lu>N$o@tF2=W9H&-t z6KasDyr`y?jSkv8TnH6Mg5V60(+x+pNQG$BFPG0WOv#$_jCGvRon6M$*1I%0S}ZIc z;=`;+>REPKn$Mihp>yMkkIAC^1=+T#hgVCOk$Zg+0|`{0fG{@WZtW7H&piZ6o1t|c z!lL)!)G@1^#3;v4XkoYTZNwLpKi>vFaus%K61H!P0hkNcKw7Xd!zlO6ZD2_RQFNg# z=6^1-wuc0|>AVDT4UVCMFnvD0FG+U58~6QD218c{Es8$hhyfZrZ7}^AGvv*ncabi2 z0>SEKe8aM-&@}k{+>CJbxUStS(UQN&UuM~3$)ZI@>v+U2YNzV~?LFS^nDSiS?X61k zn*!#YDrQ;AFiCgDATn@D=(g&x{F65go7)ZYvZFtbDyV)@c;sz6hxp zN`+qlxTt%B6{J}%I<@J9OPWD8;8(JY8HHvJb_{gU8TnR7WI6oL-!2{O->!J#4VOxH zf00LLpDGW^-6phpo_pCj@2?sss5UEqxEo218!@fGIGAa29$NQ}wT5mW1VBU}({LF9 z1HQgZfj_y|%Pb$h%v1}vM5=;s3PJNAdyMYboCxep8ZFP%h%%Nc`6YPgW;t%7WNcxj z^BwWbpX(rg!VEe#^@8oB-Lg9AT7W`4pPwa>Q#Vbfp=UJivBle|r)}`>8fD_;He)Ig zvj_N9YlSXzuB4ZxS-Qa6>%)%?m?KKh|T`yIori*b(IMLEHzy z-G}5Y(39;rx!ee|K|3ufBoMrj2MX(BT6luCL)^FQi@{PSj1u2Tx)bH(|l^a?fMjWt<3U0{?7mX z1y?2D_H^7-j#!=NwYwg>ydSrG+Nk>q+n+JD?s>%?!KC(JXjp(w*s6KL!xj}K9FVk* zv^H!c)G;YlKzN22rh@UXMba~0KfamrOQE1c5UG-BPBj&wGs$PfL|q0nU1rx}#$Lv| zG$Lu1@)OJEY?@;->78FekT{WOUZ~z=hy!CfrRY!1I|Tqjd<#%QR!o^LB)oO&^U4eT zJ@7~zFH9r{2vrIe_sd3LOBEYa}@AbR0o`wS+Jxa zKjdCIuxho!sp1zg@*qG~aEOkbUE;Kga5VI}`4;@T+^e5H0-mBvH15cH8`ed}5iHDt=)U zzPGSl35(?c5MY!GLR5~ln2%S2M&Y?xGYA3`+(qiCI*nW{;{W|(7pO&6!=tK!evn1bgJ?R?#ijPjPC|7{v{JYUefFbzJT_#gEKjpx` zcbDjmaa92H54XA_Xn}KTI1O`LIP1z7{MG;&46`q{RyzvdsxpEYmnzwWiNFwX*KcxC z@D&x-kY75Sz9_Ho8D<6k(v^o2^Xwh7xbt0vD2!QVee23PZv8Y4LdQtzDN=#!4wHt9 zLK${r&4ErNE6!Y&Zp)b{9MK%Bw(H4jp_S!c8Ag_QRA&)&P25Z(ZkvQC&R(u}VK?A) zssM4T-iav2(A*xq`As5ZBm*TR&AtN-3cThF+~~30y#Gv5+EVAKYp$-rnq8wR_^K21 zM={9ryXs-`4-;@$$xtaLVwdUpX(-fqdj9x7W8kjxD8SJ^Xh*L_+$X z|8CU200qV-*RR&&F}aH^9E{p=$WRFiptv>GF?v)&U!kXAnpz<^)KGQ^XOx**ZoRE} zH-wo93M*^=T$u>?3*;iI!s}I9y8_q%w?FAXaVxyftOHnGwjmZZ>rl69*29LhKy%>_ z3ym|3p`Ax8%J%`OVEsZjZ8LVjZCo?Mx@DQk^m{hZjLu)!v;51v3|H&_O1}^?dMSmS*sAtB9oLP*03f4EF5ajB3ieM^Lx;seMhprVrwiug1Qo9#_6E|G_-r=k`b4 zc9{SB$6tgudv=h`_gu%=0kY30l^c&Sl|$`(uM-{tu^~!(kJ|yfJQD@Ie=VE>!!cK3 zADD4dHNgTyL2q_J66z$j-f2m*c8Ek`^b^yDClKunUa&49v{Z*o9Fs;q2%_c=J(Yp3 zG3V?gL=WyMgZuMq;yDk1qGuVxmvAFwp#@}wI2Oe}mvdhkd%joR1TUV+da`l1Ylfds z>S;+cD17Xx<8YM-x!jxqYx8~xW~iXHjvpb@_3b!{7AGIiAkuQ1Sg8runkomL%zifO z$I79li25JvvuMZ`+dbT|E2XV?cN&KU;`I$~K683M;0aRW{d*AAyyAFxOa*4V1QFmq zYe=n>Qymvng<^^$IYe28N6if2t79>bR?kob4|<8a@Lw0bVX>@{h$Jvf`+nWKyrk3b zS|RW02q0>){;|YDro^5uLOjOKs3oo~Y)IKG;(-w)(Si!a)b7QD+GYd`a-+dVpofX*dHvEo40>_KNh7Cil&V?NRhIE13aWK#LJfIVLA0vn;$!rir z3it94!!*7fJ=>vg_u&-5FO&5$H*{`UPsn$%2;oQ12EUjJSsK|M*-&s~<{S_R&jJCy zC{4!;K*eCJT}H0wnOyk*9WfMQ4)F8dgY8>xsg8)>b%=1c>~S-3jlGU1_l7RSTEXKO7` z`tMJTf>S_unoqz7&mG+>{_FGM{dk>rhn6iy<#ZG25-MbV)Ynn7)lrdXXlviF84x>p zN*p2sS2uNnLRp8cc;S}>Rm&VXGmYI~C5~*^nR;Ud2s;EtU*X5l8AMnqUc0)iI9l9{ zoX%Qud={Hp6{z{-Fdf;c3+7ESC_T1Gn5lj?C4+#^^J9m42OLu~o* zUj=(95?h*yfj%9u)x-qU-U^V(*)*IOaWWgJ3cXOr@oQriKwRMk2VW|4-Lg zqLwW_4K2fJMK~<9JdDb7BD;2I-?&tW0KkJ0%+2J3*6K~CDrB~xt0_a#(sRby9M#C2 z3vp%4<#S-O@i>}zMP5m-*DB7R$UYw^LIIB6W`}B&@0e%E+y}9XN$&_Y;h4_TO z`=Su(r9r@X0@i0neDhALL7o@dk+x*i@%)g(8Jd%goEBi0wCzqa4B)jlsaTCpl|$sw zwIS!}DXmnyUx|2c<(|AYICxjqtOOnH`tV;KOLVkm`sJzGgtdRBxtt48!%kvWe1pfR z_eV_JpTJMIik35*S+Ek+2rHGC2ir0@{H9&CY6WT9WnqX2v6lT+Di3NrbOK^l^p0?h zVLaLy&Ik4T_!JR)6%`s1!AM$vB*YxAmt-o<*jpWk!J7c&_jBo0Xak#wJ{PLZ-*()r zLW6ruaTo}pF@!P!$9fW3QWWV^ng*>b1?3VlxH_W}6lZ@>9d1)*3$nLDoUel;0-Y}q z)yST}ojt#<`a0l9Q6_V!u*hOGDr2Jq;8u$mjEBuw^N#Os_V}jrGaIh^edmm_89+vQ ztSL6KKAy>PN)b)>-GJ&jZqTSGWfjS3)=%dIoXF**ca3|OX~i*ft$WyZDpj}SxOmlF z(d7f_q}y;*wg#WMb13d}YMq}UeVez}NpIs8Fnl`#6OqAn6mU(eCAbcyEbJn^UN=LQ zhZ<71b3oqgP1>Mbn;OW@HhksYWj zuLkpBFyPr>VxZ@-shz5`C}%GD%H9jFOxiTiw2Q&!F!dxYHupV5MijxEm(z$LWK1qf zz0-_<<(ks$;bPf8!5u0c`pd$?gM6c;F%4a&;_SNEz){Tvu5JnwbEtjFb+xa1a^OhL z=NF6_c4i=})7gwV_^DD2@B!C|9}vI+{~iPaI2TaGu0zxHKsVw+9mkVkGD30td#b|n z$%^)2##~TRp%CP!7UmpzEAI}S9G z01qnd(}7YMX+6*;DLBT`yJ>1p+eSs@%X5^gSVMY4YN@9oyJ3-Vvef3{n-*Dw6^x6! zblcO+*P`&!@a}NeMGWvPP%;~!nx`+scV2a*e=snLVIGmQUKWhoD@J=jU>C{LnLs;l zz_*pnGHtyQ#WS9X53(4@oAX)q;&ijUE!?GwL~0nyBSD0h)8_uBv+Dd*;h>>=;?^y$ zrZ#}g+)FzWF!n=7-txq0!CAE|88;yr%=z{KH-&y(k0Sc1oYAtO-g<9KrLzpr2sWa} z8jQLmmyC2yk>PBrAKsuH`UGULO9vic$kYv7t(?x;Bj@*mTV8wEPQO673N~PB-RC^C zokxPZ1Wb|i&&i1}7W|nH+zilA;UUF<3gI+2+yW`2J76j6U>btgnc`Yq)tj6~eOQFf z0S25BFFRIbS&MIHw<$8ByL8H)tDt3G3VA4p`tLT-FBzM`Wc$JS#=p%1uT8o@ID%rFtcdXUNJU*m-)(g6W%{Qi=}GO;M|qA^nr8iER*tGN*A$7k5NxAkr9YFhDZ@{M`hu# znZ%dOE&DWz7@bNGh2jkkIrqmvF?64|K1%l;sRp)p&<4nr!h`(s=(U$N5D-SZfU+DR z04&eT?GvLNxL`!e|EY;E<*#Os)H*`&fN$?V%$1+U6_~2h^e+YH6k+b)55bm>VO~FJ zp{~2_FPcOheEervo}iy*EC)E~1B64=ZFe<;i%*1h!m{x-eA@u&*U>qUIyzJ`0~lDN-gT|$$SLq1^W=Es&D6d$p2%9UPUFI=6>0caC&0?O>6Q5F z&p=ZP3zI5UVJ*4Bv&G))agNS8;@2d>2or4n1f)+hRCmJ!5lu6wSjVX{7uU5h0~FdM zGNYLlgu&|8L=yvx^V`>^jzS>OMz0~6h**5TX`+oN@$A3|0-)^{``e6Jk%p2sb#^9L z_Flpqrqqe#v4#FLP1PQ3@J_igxOu;Ztui^YUh3tTh^$!P^iVOGP+w}BYJg{Bv^Mot z){eFMk7x*=`a?x?F1|4V6DLXW>d@lvrp9#R;d}4LGsS!#XgzQ(ws;dCHZ#e{TX6fx3KRxi)R#8Fq5eM1;02NB0cI?3B{=rZGg3ig_iEb&mVtxwTQP4gbY!aW~A@f z+}bCJ@22W?fTsCbvvQ4Vo(Pg=^jNFqKnI{&$##p=TNLt1>1ddn!d%x!3?LhVsy8$6 zAa5L?L2?a1F3Z;A^Ir#9+x0|&4_*(DbSI~rV}z3>Slqv@cKuvK5hRL+SJ+`PfqagC zQDE8H_y~&Vzg^hTKoWG^E{YVB{}8(BeyV>8c|y5bT<2WMs>F8JLiOB(9DtZ>mo#S? z0cS*pfz|u*XVf$-*=XPjy59liI$pd7=&~G)vOxq~m__wGj_>fkwd2VzCMK z2>n9Ikjc)Go(KLJ`XV#qWEh(=Z6~u+wjc#y2H7=ue=b)MgiIqg(owG0^%jM zi}~{M%X(i}bWFifjT-x@DtoDP&U_&)&;bN>Nm~_ZVBGd0t;$u=Ng$4Ro~_9V;TA=a z@uN9+m$)qHRS+;umoA$&!Qu{g!N;lT(b+ve=qp|?jD|~6%COZb_ziWWMRpeXV%_(MSy}mhHA@V$Z3(~1Ph(&iPQk;y;c@D?Vjg-Xqfp;B$ntPo+lc zL+}}5OTh(aTJ*wUdbiU?!dy}vGjhjqL_2BJ4~I-@N2dA|q}n#aI5Q!`;?uA;;Nd@8 zyRQVOGU`1A`lkX?gN8f9x&d^%JQEia+2gYbNJ;sWM5NzQK2aaIuLyVCckhSsbIW{Bs?)(tT z5IH0`M10vA*vYL`D02C;)))33e25)*t{*3QB^VljT^5|QUQZh-1M7-NS!xMeUT8|5 z0!T(>Sz;pyJnVl$8ncOl0NXI^>;eaL>B}Gy+ew8EiSnLdg_b$k70nFLzcyE3-#{G@ zL`kVKv-E97`puxCq!;1Sj6vOFxIQzHc-V2tI|}~Ap+eb}S?~X?erOUva5*c;QlgTg zL4~dwNG$XMZ8v~$TrjrvH)Qz04~Xkhs4K#>U&uS3YAH|Qh&oYJQ!I+Ot95z6@WUzQ zOYZ|b+kiKcGMFlFe9eQZJW(}2xPm8{^+S?;J0P4tbHMhsAlsQGnv?Qn{FsVpVg$*R zHv~zo1UP6OpY*%W9T6_lG;Z&2q{6MNBVD-mt3|x1HmcRAD_s-4hXTi*a<>Er`9~0A zx7RD&^&K-T6E2-Re8^kb{4!adZ6zMVj@(3Bg_8o6i+xzqFAri8U1V(TMFeU&-6JS= z9M-)_+>EC}>e;|m)H;x`OMk@^1x6kGXsl9wa|4L!zBnh)5^NAUZhxJ69rMGJ{$|c& zw2Iy4IR4*!({tP~T3I=e4MjwW1rG$9RNeruW=1M%X_5gWH9b7a=SEoSrbG4vQSW`)mWR^ZP#ZH(gbX7x9AYQ1#S&%yx9F6LDRT2GwX7poB{^5V{l6OM%A&Mg})j1&aiWz zLHJAZL^+9ubZG=_F~IAo9T@zu^8?ZiR^9Rk z4)p2Ee){%85tVA3?!rQR_-c8CDu%xWAv+j-^V8GqcmH^6%c@&`O$C;s;KHlYpH7Um zH;n;7hae4$UprV+>~l2<^_{~af`4nps~`Kz-Yv$lbFCvE$0oCeX4UtTv{$O|Me=6X8Jt?39fbo@0i z53W7QeXJ>;;eAoDAAe8D;qH7s4K^RQwPc>H9SZYPQjuELZlRt9DboCqfM z@DTN$ta}so-XBc=K1R&szwQ6nNwharV7%<(uKKlfSDKgrPN<>{FK7Nh|Ge|{_jRXd zGIJ^1QDDp9>|~tgw zLyx7%7bw5O+GzO0rVgyJW@Qc?=L_*Fy6f%!|C=k<8rPHYdO}Sys^7>{S{{P88`OwD3 z+QtTX&HT2zClm6)!QpMQ9?pMcbL$d$V^kxb>1?#iLB3kw*xsmbyyw}U+xYAszTy@1 z(DFC?+w+e^we#1D57omfYwoV4w`$kpzH)*ZL1 z3JEO28G}W>EolV_WfwpVSJ*2F`RSl zw}0(ccYMJUWV?6OnY|^_-#fFrbiZ9P*QDvl72CI0Bp5k?E_`-*tX@IiqDkP^6r{=k z=tb?v(wXoaH)olQQuUe0PVrm);(0uJ@b^AI?tA{vmRBx(;J0qN@Bt8u{pFRl<(s}e z|Jce3eapAkR-XUu*S+ooA6WbR@SfMf-v`!i(EcBFCR$hdsJ?rh{2#9J3pZW(AS2!X z?-~nv@<;RwY7auGvIc}4urwS@i|ut~2y&_KMx7UQV{h&D*WA8h#@Tu$G&gR)hhB76 zJL75lfHQwCCw-^eURi17{``yGE55eFOltpF`wjFS^h)FXj2?jeQLa+5qGUg5LG4DR zc2q4E#gAr5`uAS@JAjN-h^>iR#;MdUOq0O%uPR{#?VluCxHpUCj>;1-M5k7|&(x#g zWSvZUGu*1OST?Dgm?Bj>xT&<=DY_N%(Ig&&fSRJG0IE*=gK@3|YF{jT2}QkNED4oZ zw!g6kdLGz8U9XvHdD%R6a@;67Fbsf%hQZbD&ylUHcwvB*??@Myh?$8mOL1VicErJq zX633Wol*%v696Z$$+_en;1kIj<00T99^mts)a&S$Z-!P}b>lLylIxje$JFOt9d2oK zT7fK~Cqk04Z*#xyp_dG|Y{Ovx6Ht#%t-9lypshm_LvOe{FQp=LYi-vtJgY20hCEL` zn7LUDSQ_A*yDXhYBtdY>sqS+46BL)s35Kh^Z4nqC=Ij!1lnJoO&+0{XJ^~pF`{PoR zmn{SrCT#%Lg36pQU?MSP#bOv=Q~(g&DY)^kH27_-R>DA&ZV{7)(A*u1Vyc$S&^IPj z5&&*!!>FTApwZG1?TRFr@X-ok)QQcE#K2pnYr#l@Ex=5_yw7(L(CS6)W!in(8*#6| za6gSmxw2HkU?J+kfGnc3YJ;D+*d+M!ray&F@CYWADYtCDNZ3({J1Q$!?GoSZ!MRxx zVFnNcbcM2&Z|0*S3|{Od6oSQuzT9!K&ZF45&&#^O^%$^*ty_*t zhY)7E4g=dg-pxWGEyi^rS-_S$%{F|&F!jss5TS6=sx(Ta%o0usr=BPJIjj6bwIv~# z&2-61ygvUlA+;*Tku5`a<3|D1nYh3UVJFNssk@GZTAWa)=lb@l%~l)Tl_+MWESrIw zHOk$b9T;KU5+t}I5}Tu|6#=AeCCpV@5co`AUTIWpAmp<1RcGjicI61r2En<-yc2$h z8;ejdPjF#mpF#*FOAY9Rby`M*>&WjjIM6MtA6EUaJ_#68eYQe zPdhD}o;hlyL0^Pc*8+r~SF4e4Q^z7Sh?b&uXK*kPej68HMO?t>O zr~yjjeUPQLv-o16|4+`Xr(LI)VONP2KFPVm~e$ac<6G5>hEn{HFR z;~vWTYm_d3jt_A2Gr`%fbjCM5i}&aM%ZaO5))c(N zi1YmCh50qk=sgxEDjiu#7r&s(WlQct#qg!^#18L`Ut8q(WfUyg5jVBVf``4-W z1kBwlD0O|in=bU=0s2Mlr?j8beyP~?PdT*H{cWrkVnKPlQP}KnUAX|3X|lgMA0M0) zJAC6JprnBJV|2PLG*2AiUF)=~6dJBN5-L9yfEjAv-C}cb#Jo|e7PsekdOE;e;TK8` zpG4 zu;m3(Eg6QH0O@~pDMziMi|Oj_%IOSKxiH~GXF(lBJm>*wgJ7aihqR1RcMYH`s0!I^ z>y&%`n*{|C#2LnUz-%`4vTIl-HHAeUYMZWLx@AgXnE``|FqTqEB*)#;m}SG1JK4b# zr%yB-AxnmRV5!y~n;iF`!ZpJ*G<^%S^7`gAhQo1J1Do^4!53LRCtT>n(se)-xQ{`SHjm0qar?`{9x_R3THf9=21#lBy? z#c$j}j|5-&3UQx#X8!5VfBu2TAOH1VCl~I#bAA_`9PaIrY=^8IA$vQ|9NFF30i?e3 znH}wNF7PkVe+{i|X@|92v=?drT>C$?4{N`u{i^n_wWp!pLqr0JsW493EmCOrH_6SX z{aKNAk1o~4?$JfU{VwVE#v_!bD)d+E<&j>oz4(?gtJE&ln0+A=m3bRaJkq+n9Tsv0 zCg;*&vCa{hXCFY~2bR$557T0SL?mXLhsc#5wCY$kONy^23{qgjd>kzZFzFEE0Khd$4=%fQoW-`gd=M9ViM~WHD8?gowQJA( zUlLL;XW3G<`uTpjT3yPrI-$S#GyF{ba&pSoArcamSeCnbFas? z5HBxBj>yjfK!hq1P}HTeaH4YV{qMfM228T6EVIrz_nveA@2$FZ>vnZlcXd@w-Rhq1 zneMLNNoJ-enJ{FMNhVG*m`E@fo&+=qOHg$EiJOEd8V$h>1VLCHMFm!H5k-l_;KQQg zgG-DnuEO)gMPZE(tLtiZ`Fqmr_kOp!C$Q`GOjUQ?xg5aoWP@>8Up-c|Ycm5-F!kKY8qyDU1T8C6!( z0m{^5hIDV5fiq0ULHRdPW|9%Lv%Egq8G?QjMq|{5qYc^1c}-8FCyl1#!AO3+HJVIL4z`!v z$X}|~JS|j6TEggX=|#oKMe=c;dlnmgZO3z#zpWhrC%x_HFDFg>l|;5>|2nx&sT)Pw zPG6fxSb6R$`ZBhZmD zno35Xz5wGrfvw`a0B{7t03GQHtR5S~DvFD$s4bOL&Q@Mo`PY>PEAJ%f|NE8CRz6>O zrt&{3|6KV>27va|^AcJxAvIa-(}OGeQ%>rc9bh0eavM; zrGa}l`i@t?J>)7?@Xgl-dcY~tUTkRh5Vpi$@8W9p&U4Zacg^7=pt=4>#~uM z5sj^EVa!IW*{tPcvvc2~WTV&$bR-0iXTU@mnpWrfOq-!;z z=0dz^lD2z(?~?+7tJ-QW)UzOWYO7iu`th)t*M}}5IYs?&im*CkJkun`56_*|O>L6b zSx(hKpl1J=wf8^i&HN1=5A}k0P}wI>u1VYX7Gl#3d~!*O-7`TLG_#$Icr#FE8vR8Q zq={db#wwD`wFm8?R}YQbbi&S|PUwWhpF)PimKDh59ZwNK7DL~Un)L)Lf(ye&aJHIP z2T9d8kG6(N4cj`5-Rj{auDN~NNYN}Zuj;sID{aQBHqE?twUs3#Jrj39HwxGLui9^g zc|EU@v*OlGv(RU%wW^7C8jA^8l*-ty4m@8>oR(u2!f#aQx1Sy{QL8#JCr*3ymz{RQ zSNZ zmM}dg&{P>V&E;bkX^K+Ea<2;XxzMeWg}M-0q?5^N34x`Cqy`^>yPAYpCtutM(vZCC zz!s!fhy&E#Qzq=yn?XJ6)CWuY#EU=g#+%7=@3U?LlO|lnsP0dYVqO?PddJNDCX?uD0CN=Dl>w?qU&_ zgkh@4!XOViFr0hk+wiHe+D9^iddHi;At2LGzC#3v97C#At-RQK`HhaJP52?En z#jY00ypV)(5X=0b#`xu}xaS1mV%sDh=$!ZmA&M8EA3)cY2DvDci3F$Bw~g z`lzOf9hgvh%^)Mu?>9)L>7-D5Z*viiA)BETu8^m1r%z`C186~_-+A?T34bOYDiP84 zKIh<<{}!+6dv5rY!Iz%=>l9o3W=;!qeZH?veIn6Fe|jWJL(TO{ET8N?QnG=v9gcLM zfg}+u9i%5908cfr7NSW3VUgJuEzhEHppWFz5iy#iv_coi>sGU&^a@x4_LmD}ta2nS zwdPyK!J=Vc9#iE1k)%pAJ&OkiVG0444&h`sVhB!gBMnIxwVuxr)Clyhfy_v>M<6ai zHK|cpj!zjD3v9u=_VmfzuDlxHy<-oF8Hm)VDhw*|Y^>JF!a*>rNkp)`=X zR+zn~m6Pk5M_P4Fs9h4r0dbgFhlL9EBT2oUSg!lGHKHfig%UDp_oaMf15u2`WU=BA z3ikd9zkP3s-h3L)w;SI}{P*Fj*q`g0FUj{*ZZB(Hku1QP{cy(erj9C-Wy#R_;{3i2 zF7t1DEL+&iVeFk)%+YxUC_{oWf1EJK^3KhxoS~bqdybnw>X`0Xu zJfg616F+LL*`Xmn+ri40W4p=nsf<)IkvmdBm}=V&Lef&iz4oYwY4vWqs%;C?n|!V%GxjG3L>QHL21WR;wbO&9sqllsG*nez2^(#b6lu>uaLLPNu%*Kfc&1!kCr!) zqgsDfi~Y$Va%Xkg^3uR;x|s>$^|QC1*)?r_XfdeIit`&laT1&dvbwX; zKD6yPhgObUe9>0Z4asj1MNfY{LV&%RCYADr*TV_7jpZ;L!|Sn_?72RjYVYs;0QX~9IR&&} z#1vuvugJWtz|jN3DyOL+@4uY%0{{t-Ej4?7p2y>EbmsQy>f*xaCUQ8ujI)^@rKdGH zcF?>@z2KI#IlX0DUp#WdZ8vNkrMIJUwYj)3e$kM8wyRz`TTizK2<>_l>4Twek{UM; zAF*|G^&LmG2#(^S@1(c$`!!JHGnJsysI2lT)|Hj>m0Kz=L+lJ0mt0Gcj3UFCITJDp z`xke z4esGX{N)Au$j4ne;ht{u6wW`@>>fWAx~UsB=yfW*6boT1O)8IlDF7T06IXcKy=&W0mH~N@WMWNm|A70LMD> z)?c;U|3?ixNW`?FVqLLZ|w~;)dAdE7qzt6%8V1?=`{_-9~KDCnatTnj3ZUX!;xVV~1<0 zU%#c^x#D1M|BgDxZN7?EH;aAHX3k<>Iasj(;1q}M0Nf>?GOA0DD{Jwg;~dLOhs3j| ztB#5~@S&}Z?)a|J*1AikSiR$r@{gOF=R8;X$-@AeO>67tT_e5tjVZl}dv$X4iPrk) z*r8dRi)QxX*=i^6pB~1ZQeSZVFoI3!dMQocyS3{k9*hE4?$bV1OC+PtS8gsVNz9|D z4irzRWrhcvsMv-o*LUWsXG_et&sl;YW^lgWVDH=N@^dSG>_#_jYz>D${7%n4dE$q@ z_obbvZhX&93$Pe6l<&9bN!bDoe&aDsLcD|l?iJ<}4WhkU}UwFlJ zFHSmx@Up%K^jUkgcH&es4-w?h+#LOEk_K_ZJIK=`x%CFpC3j%PU)p2N_Lrz8njyLO zo#P?r(2!6c7c3wcSqvKId9~8OBx9o19C_}N_#cc5GTg&j%}Zy4?Uhi(ZY^?Si+l&S z(M*b+=4Jp`2PsW`@A2fYqhRULKXOJ$`ev=e<62m&24rcf$P1|>p^40N?c^%rMP#5J zK&|w~@f$9_Vz}yuQU*@d_Q=|?Onc|#3)(mB3>ZX0rfGHW8oZXNK#3HN?{s#jD#di_7Q2TCD11typLk_mKg6&3gpJt0{nd^;I$RkFGS0n(rI+}H=9r23yZ}tWk zuRpq&U(-G#bCiCU4#y-aU98`Y7;0g3S zNWG%+c^VZbBXS5!IRxeIHCyBi<1gpy0xf}cj!QNsy#F%2Y;=%-%`5%9waAlmT5->qrY^|;K2t~wr3XB+8OiayYR4pY zgA=Fm7D z_L^2=>sClM-$6Xax2fx%SF0<}27Qyw@VhOtfq27{Pl`W!^2z5;KKbO=o_tb1`8|Jf z7hd8zdV4RuF6rc#$j6DZ+)B4{w(@<@OQZh43JU=+t4^vtN7ZpTGh9$Xgm zHFOi_6!QYoU|zs#)~oATZr@73<#JNo1=lkGWo)+#%k%cIJ%g_Se=vWd-rsyOU*?q~PA~-z>c(xi3jPdCm*G zg*(HvMgxRy%V?S2}p24_iasqkn|hkNfRF0<9;N5t)G$IRZ} zekcuHaa-Vd!Ljoss;maFR(q>~Kqlp*0O3g^~#g1%pOxKWTnNGMY3ae zDyJ(K$c}v}>9yBY?yJ1D@=KMERQ_}2OO<~Tw&;qfxJrDxc$xSE0yYEZ;7FLp~-yA^%){Rz5BNMt(&;M;c&3Evv2?sHr-kPOG!( zI(3u!QT32|v-&yp4)qJ_J?j1HH`Qb657b|(r`6x6uURWr*IKu(wyw9n%X+nSk9D8* z6V^l42drPWe#`nD>kq8YS$}1H(e~}5_IdlFeT)6G_S@|b*uQRn!v3`V8T<407wv!4 zeMqnCtM&EzJM?Y(d-VPKLH!Q>KK%jxJNl3Hf74$wid0x+md%FQGD9;p7tFVt z?=`PA_nV(E51BWa_nMEHKQx~;e`Wr+`MR^{^qs@b3Fj*38s|pmrOtOduXFBk9&p~~ z{G#)#&WD^QoKHD_;{3VuwDSe$@0@>dzT$ktwcWs7B$0H?-F2^ZFSsvu?{r`3-tGQ? zd$0Qw?t|_l?mON0yN|p7$^DG`dG{Ij@7@39e%-T544|Ff#9&)I>vhe}bj0>lVGthM z$jw~(#z2~wGDaZS4i(cls%9#y6m-X&rsyeDuo{^9s4?TBO@?bZ z&w%I|BiWmbXB2xdC7fC$i{2?YL|by~0CopB9oOWgYMx*-Wz5HNWTj8pHUwC(Hskpk zbYqm0;@9Ppe6LI2Oc~m5n|wf1H)J4UjlNrqO@_V<8@O^-+Lzo4tnrlV#nXBt8;fXfRFF!oXX|=l2_Qfumt?A=*ZhvY?{~lg~4Rvh}6|l*OEy?ePeNF&wnq z7)%?bV@;$(G%+lb9QZ-=iapbUubIrLM_rVohOQ$75usVx0GXi!Qi0`N=$QZnndRdd zbqgyou^do4CNs91_u-U~?Yu>{WsjG#rW4>bEwkwZoF)=OjC^O?B?1qVdwFv8dH=A0RqV;RtBz)&86uQ)Kd07vE-_-H_6nDG$Nr2_*FzDx?*cotPWv=dxY8N>#<8rr0WZW_Z#$) zgy>+5RO%@ZMc*8Jm~A0kI}#kfHn4!8myj@B>5WSCSRB|=feQe6o3n_er;})@ zi5!7g`3NEdL&{7;voZr@J#lqC?=r8Bwz(J`>d?WoK;5emL?=C!=kQ=e%tvx! zhe*1^E;PD>LOWmr;OA0n5lb*3)&Qyw@huoT(}Up%?IEiUk-$4chH^3!d7qGuzgmkz zg|(kBax87zmR?gjNko!Z($xUqP{K*1r49>A1EkH18S^a&mF?P&gK|e$-+Bc$8Nc1K zeap6QM_%%G3rpIrSOO?EZXlP!eRlG|iqO+|!=#}>ABdvr#S zAV(avJy4_3DRYQ%7sbaM>4o$~OENGZJy%#r@kYFTBt?~;6e2g7PO*Fm_&QZWpGefs zVsubMV$+2H5OCnbC`+OSGeFIx%y{3HQS8M}1tDUx^YT;wf?@#iLTL;Vx_9Ep+UOIiY@++f1(q0NPu!2opi6h<=^M z(>}#`xC}K>V%dF&sU*R@N|bcQb~=U$`D7XodPIna%5H;;_@2!9)U7l0$4SXMwKg|j z+3KxQ?hhVgO)7pIy-_PZBFF;e)HlINc>K$rk0*wgdL;gou6%0#G%5ZFQf=8XfuC1+rv>Jf0S=1p>#CWx-`zyal!EE59pD>g9xFiWkW zSQWy<4z{xA)Tc&!s4`-e@$=Mdgt=qF(-Z!}cIhPj2Vg>Olw>g9cMI`Bz&r|^C>XkH z$?%MIa{AlV@>fA{H2Txx87^N>cUfc3#<+n;KY|__Mj4Qgap&l$MWp;Gl<`2HEypVR zfQvA=J3>4O7NYz9Bz;_%&lih2#S{k{Gl+*}Yx$eFpK*D71xmr%Z=T;xh?WrdLE49a+L>%qNO4@&`zm^VE_a7TH~NMnMFKCq)V1Am(r>^!6VSwt@UQ8@wt0 zo#*9V7+^31J-?@7iK13%c)xo-eFP`v@gO%5sD}U+a&!g5Jve~t2+LWmAOzfxmh?b) z50ccRsWJ0^Q1ZZY7zjQ6`h9uA94v_@*VCh(0Me%_rKXJ$;D;#>;edLVW@F)J#0E#52=Pk?pc3j%p&YQo8_`*5ejL>gp{XZr z1$0(R8P`Hg74f^v@UuZmS_N>icBW*vCw$_=zNYhdbCa~%ABBsAgx38 z`dQ#6JQpojdUOwqC4rIEWwVMI9M=6v1(*q*a;jEbb7{)bDec9Dz;a43{)H%RJ0@}CN2UaE0<~GL(&s@1ERlM7sQPbqSI_lULi`^3HjiH<&{_?X17{V zUWIs5u8;n4e7$3k;A|3BXq=`DSasCIc4+oE2+?CSavKsxTG~L0i2#7PXpN}mQ46K4 zX$-uY+b0sql}#racz{H9z1Y=Bq-_NZRLen5t?PQW10Hhm04;J0Mi{F*pX>`eGPYh& zU_`Q|!XW7+PE{mMzi?s_7>7+{)-_mkz_Z-M6tx~q~)7{Br2o{HQGZ=r1G3hT9IQS63ld0Xc`Ba z(n35POX8@m=DH|WG#4e1fDnHPL~BhZtix?)g=A|I2R0E>a$OI{2SlyqqXZPbHue(5+ht&h(sf+SGshxT4mb|nIohhm5C6}?Fh31JUq;BSa^^U z7zE>3#_i$s+)0)#B_xQ;UqoMeT>n1Szr ze4)pw_0*+0cgvB7=n1xqAR8Z(%0+QR*sWH8T9q{wYd{Br48*05W(yIPsEr#*)LQ2X z*dejxf=|*zs0em4YU}Az#Fs+Nr9lc(r%Dfyev<|zM7k!832+T8YK-sHNvqLJPXmOw z0smR?9<=a6VqHN;0MKn%m9gNc6YWTl*sQ;h)^D3qBhp}^0xii51`lT~#v`M#_Z$Q) zRZ6Q`ag}!j)+z*nR`E##nM@0p2gAYicTljwM7s1NPz6*#z*?w8uqus1)Rc^n49gh? z2ZzdH3*Cnra9F#O#088b-H0TVSgkvLl?J%Z1_7p$Yf!BTW+1yBWNI4UW_z9{j;+e} zvh57!V^}pJhox(rLyHH4;k5P~lA|uXCd_h_=XDsb+=Jfs8!@blehGb(woEKuI&Tz&XX2KJ`y1>npDM^N`UkkGa~5)oDe6Be2L zxK^{(#IR`?Gn=EZ!G-J|qJ$-Yf?!EQsR}-kOj|1uy2>r{1n6l4#177GTdcxGbzqXq z(q1bq<$eEQ|6}!|e=16!2gE7c9JE5$9M)#5#WSxwf0t-K>lht4b?3^WyWaniwfN%Tgw|;tyZtI9o4;f zp;3#pww>B+XJHVuR{N7xxX9^#t;=WCsB*k=Jy)FnDeS);B>Tr=9FY4W(J?ElMw`b*cbyk>N(M)h~YJaI{!#+|Ue?+PZzRUu20nRAh$J3;Ej)Xw6X%Uxc#6y zrX)v7tLo^K*hVeFv3T~n`-_1T*PS|j{<{16g%sDHy7E7$=ahQyE%I~MUwPH}b3b;d z!0&7S&7neka_^Vlr|v!`t^Mcxyb8*Ciz4lrL9gSB9+}FwyzAn*a~H+!AOHA$;wt+0 z6;Z$c{=E;1Z+q@3xpfe;aYP-x6tgj}yEqa3YA95H@c1|AN34h&_kK;>B~zsS|SzufxjrTE7qYEap!{8;4)L{9;x6k(vfgF0IqqPk*(Nf@<@3sNC7 ztC|n(5*xfn%!DAsd7ch7JCM7Cn0Zv8;%qLCrlhL?E6zZzSoYmjr^FbGs9oK!?@VYK zk4mI5MhL_D>U56CO2__%ueeFGp*j-zU)(?~qfLJ!-E5v|2Pzmg0sr3KavLonzW{_1 zCHCIif;UIv#?>jK4=Yro#e!N>qtF$}p}vBZ1At`JMwvucBEv?=W&&}1`b&>}2pasu zkNp-T8vR{<{!sb(F(JQ5Qtg2YUVN-XL?MN%_hJTJ_JDLeVXfsV@k+J%!$A}A>MSL_ zH7^*3V+u54G%RvxZAQh96T=PU23yc=kP2yxvITqxRLtg7=`X=TMW zH5(zqhS+N!k@Kyu{+LttP=BJ=<#K9S%r$S0a&5GXHwYL=3weLTafz8Uo^joIK)%z_ z6(O?Tq&gX8DA_upzp+BDj#ys&k9NcIw-$y?bXqqwQFQ4@uuyZHUe({qw?g&LULqI1 ze35@{zWL(CFYo=fkp7mx+EMl{)mcye#!pN_SEz;G$qjIZ+4$rA^MO z=-poa$lu?)neRZ|r(B>mi@6SI;Hq}uvWqd3t31CEL(Ow@+gh;?N#KOdSqpxf;btnI=tg%6*{EkhOJ)LC3dS>K5Sq+ zFp=$?N-Vh5T=_?nrg~mYj$2fV^v!Y^>$@SQwHcu78s#=4%8zo5g@$Z6p=%u@&h2j? zK+@DgJ9cDoVn@p2^oDpylD6=!lCoxocfR28&2L#;w>r(`!dj_hJpXSaopA{z2DpVA zm95ICG9zo`ROPD5)s=IV8!9(dZsy$nN`D@L2C#2LrHN6sm;OIRz@?Y^|9n@Szw!d6 zN4G4t$cb5lUiT<*_RefbomJ=e>c(-5`0v_v_P$!L+b6%}XR`Z${X8HIz?Ub>WP%dO z8`Hs@;egFV?oMT{QU|@d100gXDV3M7IkpUId0stSf0fqp0 zoMT{MU|{89U|@bh9%k7wZVH|X*b4d!fD51uFbvKO z3JoX?a1G=R8V*7ZXby@Fq7K{;A`m3rN92X83m>1?4pcw=iS{Zg3 zk{T2mmKw+#L>r_WW*+VzLLuNIOe5MP8YDI)ZYIVmm@39A04o|RIxB`N+$>rw@GUAW zVl9v^<}b!DjxiK68ZtC8Y%`WKz%*JlZZ`NgiZ|Xm!aGho`aD8B#62KAuszm3Fg}z& zAV0W3Qb2S;RzZ3~6heehuu%?C-ck}$Fj7`iN>nCPW>pqdN>%iDoMT{QU|^`_Vr4kb z00K-v%msuD4FAD=1^_9O0&)O&oL!K;PQx%1g-_bFKq~r31c-?{`0+SV{og;tK)%3J`X?Up-uK`sF@pEF7|K zga@>D!bAE8;SrwkDC`q2ydem$XfE+8+`~0?!hQOqa7ca=9^fAD!bAEG;Spkd3Xk!G z?^e5ft+PXsG#>pE>!!_ZrDoC8k1Jj2CUJVJHYe2{v$=CdnZ}lCX)C9T!m7Hld!4#y z=Uly*OpN#_wIy0;IOJHfXE>lhg63@hdd%)^4vPv3Genr4Pgm?!mq<8kY?;{L#J%O$ zXPDDE7|w|0iPh*8=@GPFg^SFQ)@FHHq%XkH^;x)XEH}EFj!rOQU z@8UhYj}P!6KElWN1fSv}KEvnu0$<`Qe2s7LEiU0Ze2*XSBYwiq_yxb>H~fx2@F!02 z7dUDNNDvyd=%CPJfW`;|3x~__2#A<4V~q{AxPrg&53b_h1uo^>IZcd*G3S$1Hm`Y2 zI=593rSWtjxy1QO)j(*!?CcERzjd;UIwPMo)!DIN0GPbOyVq+QiS2g|~?bm-(sQCKuu|a802}!crD^8i!EFJP59N5r})yRe@qurVaVE zZ?dH7ScC*AZj0c2x+y~3CU(m()#;8{nA<$8RTB?mngpKS9$8F+L@R9;*Q#B{t-Hb2 uoPNq@CCRdRqH?WFVk%^7>m}Rhz&4}Kw%i-GO(a}VZn5|eH3ml`0000MkWMWC literal 0 HcmV?d00001 diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json new file mode 100644 index 0000000000..a11152a02c --- /dev/null +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -0,0 +1,1431 @@ +{ + "information_for_contributors": [ + "This file has been generated from data in https://github.com/jesseweed/seti-ui:", + "- icon definitions: styles/_fonts/seti.less", + "- icon colors: styles/ui-variables.less", + "- file associations: styles/components/icons/mapping.lesst", + "If you want to provide a fix or improvement, please create a pull request against the jesseweed/seti-ui repository.", + "Once accepted there, we are happy to receive an update request." + ], + "fonts": [ + { + "id": "seti", + "src": [ + { + "path": "./seti.woff", + "format": "woff" + } + ], + "weight": "normal", + "style": "normal", + "size": "150%" + } + ], + "iconDefinitions": { + "_R": { + "fontCharacter": "\\E001" + }, + "_apple": { + "fontCharacter": "\\E002" + }, + "_asm_light": { + "fontCharacter": "\\E003", + "fontColor": "#33c1bb" + }, + "_asm": { + "fontCharacter": "\\E003", + "fontColor": "#cc3e44" + }, + "_audio_light": { + "fontCharacter": "\\E004", + "fontColor": "#5f8b3b" + }, + "_audio": { + "fontCharacter": "\\E004", + "fontColor": "#a074c4" + }, + "_babel_light": { + "fontCharacter": "\\E005", + "fontColor": "#3434be" + }, + "_babel": { + "fontCharacter": "\\E005", + "fontColor": "#cbcb41" + }, + "_bower_light": { + "fontCharacter": "\\E006", + "fontColor": "#1c86cc" + }, + "_bower": { + "fontCharacter": "\\E006", + "fontColor": "#e37933" + }, + "_bsl_light": { + "fontCharacter": "\\E007", + "fontColor": "#33c1bb" + }, + "_bsl": { + "fontCharacter": "\\E007", + "fontColor": "#cc3e44" + }, + "_c-sharp_light": { + "fontCharacter": "\\E008", + "fontColor": "#ae6545" + }, + "_c-sharp": { + "fontCharacter": "\\E008", + "fontColor": "#519aba" + }, + "_c_light": { + "fontCharacter": "\\E009", + "fontColor": "#3434be" + }, + "_c": { + "fontCharacter": "\\E009", + "fontColor": "#cbcb41" + }, + "_cake_light": { + "fontCharacter": "\\E00A", + "fontColor": "#33c1bb" + }, + "_cake": { + "fontCharacter": "\\E00A", + "fontColor": "#cc3e44" + }, + "_cake_php_light": { + "fontCharacter": "\\E00B", + "fontColor": "#33c1bb" + }, + "_cake_php": { + "fontCharacter": "\\E00B", + "fontColor": "#cc3e44" + }, + "_checkbox-unchecked": { + "fontCharacter": "\\E00C" + }, + "_checkbox": { + "fontCharacter": "\\E00D" + }, + "_cjsx": { + "fontCharacter": "\\E00E" + }, + "_clock_light": { + "fontCharacter": "\\E00F", + "fontColor": "#927f79" + }, + "_clock": { + "fontCharacter": "\\E00F", + "fontColor": "#6d8086" + }, + "_code-climate_light": { + "fontCharacter": "\\E010", + "fontColor": "#723eb6" + }, + "_code-climate": { + "fontCharacter": "\\E010", + "fontColor": "#8dc149" + }, + "_coffee_light": { + "fontCharacter": "\\E011", + "fontColor": "#3434be" + }, + "_coffee": { + "fontCharacter": "\\E011", + "fontColor": "#cbcb41" + }, + "_coffee_erb": { + "fontCharacter": "\\E012" + }, + "_coldfusion_light": { + "fontCharacter": "\\E013", + "fontColor": "#ae6545" + }, + "_coldfusion": { + "fontCharacter": "\\E013", + "fontColor": "#519aba" + }, + "_config_light": { + "fontCharacter": "\\E014", + "fontColor": "#927f79" + }, + "_config": { + "fontCharacter": "\\E014", + "fontColor": "#6d8086" + }, + "_cpp_light": { + "fontCharacter": "\\E015", + "fontColor": "#5f8b3b" + }, + "_cpp": { + "fontCharacter": "\\E015", + "fontColor": "#a074c4" + }, + "_css_light": { + "fontCharacter": "\\E016", + "fontColor": "#ae6545" + }, + "_css": { + "fontCharacter": "\\E016", + "fontColor": "#519aba" + }, + "_csv_light": { + "fontCharacter": "\\E017", + "fontColor": "#723eb6" + }, + "_csv": { + "fontCharacter": "\\E017", + "fontColor": "#8dc149" + }, + "_d_light": { + "fontCharacter": "\\E018", + "fontColor": "#33c1bb" + }, + "_d": { + "fontCharacter": "\\E018", + "fontColor": "#cc3e44" + }, + "_dashboard_light": { + "iconPath": "./dashboard.svg" + }, + "_dashboard": { + "iconPath": "./dashboard_inverse.svg" + }, + "_db_light": { + "fontCharacter": "\\E019", + "fontColor": "#aac7a" + }, + "_db": { + "fontCharacter": "\\E019", + "fontColor": "#f55385" + }, + "_default_light": { + "fontCharacter": "\\E01A", + "fontColor": "#2b2829" + }, + "_default": { + "fontCharacter": "\\E01A", + "fontColor": "#d4d7d6" + }, + "_deprecation-cop": { + "fontCharacter": "\\E01B" + }, + "_docker_light": { + "fontCharacter": "\\E01C", + "fontColor": "#aac7a" + }, + "_docker": { + "fontCharacter": "\\E01C", + "fontColor": "#f55385" + }, + "_editorconfig": { + "fontCharacter": "\\E01D" + }, + "_ejs_light": { + "fontCharacter": "\\E01E", + "fontColor": "#3434be" + }, + "_ejs": { + "fontCharacter": "\\E01E", + "fontColor": "#cbcb41" + }, + "_elixir_light": { + "fontCharacter": "\\E01F", + "fontColor": "#5f8b3b" + }, + "_elixir": { + "fontCharacter": "\\E01F", + "fontColor": "#a074c4" + }, + "_elixir_script_light": { + "fontCharacter": "\\E020", + "fontColor": "#5f8b3b" + }, + "_elixir_script": { + "fontCharacter": "\\E020", + "fontColor": "#a074c4" + }, + "_elm_light": { + "fontCharacter": "\\E021", + "fontColor": "#ae6545" + }, + "_elm": { + "fontCharacter": "\\E021", + "fontColor": "#519aba" + }, + "_error": { + "fontCharacter": "\\E022" + }, + "_eslint_light": { + "fontCharacter": "\\E023", + "fontColor": "#b2a5a1" + }, + "_eslint": { + "fontCharacter": "\\E023", + "fontColor": "#4d5a5e" + }, + "_f-sharp_light": { + "fontCharacter": "\\E024", + "fontColor": "#ae6545" + }, + "_f-sharp": { + "fontCharacter": "\\E024", + "fontColor": "#519aba" + }, + "_favicon_light": { + "fontCharacter": "\\E025", + "fontColor": "#3434be" + }, + "_favicon": { + "fontCharacter": "\\E025", + "fontColor": "#cbcb41" + }, + "_firebase_light": { + "fontCharacter": "\\E026", + "fontColor": "#1c86cc" + }, + "_firebase": { + "fontCharacter": "\\E026", + "fontColor": "#e37933" + }, + "_firefox_light": { + "fontCharacter": "\\E027", + "fontColor": "#1c86cc" + }, + "_firefox": { + "fontCharacter": "\\E027", + "fontColor": "#e37933" + }, + "_folder": { + "fontCharacter": "\\E028" + }, + "_font_light": { + "fontCharacter": "\\E029", + "fontColor": "#33c1bb" + }, + "_font": { + "fontCharacter": "\\E029", + "fontColor": "#cc3e44" + }, + "_git_light": { + "fontCharacter": "\\E02A", + "fontColor": "#beaca4" + }, + "_git": { + "fontCharacter": "\\E02A", + "fontColor": "#41535b" + }, + "_git_folder": { + "fontCharacter": "\\E02B" + }, + "_git_ignore": { + "fontCharacter": "\\E02C" + }, + "_github": { + "fontCharacter": "\\E02D" + }, + "_go_light": { + "fontCharacter": "\\E02E", + "fontColor": "#ae6545" + }, + "_go": { + "fontCharacter": "\\E02E", + "fontColor": "#519aba" + }, + "_go2_light": { + "fontCharacter": "\\E02F", + "fontColor": "#ae6545" + }, + "_go2": { + "fontCharacter": "\\E02F", + "fontColor": "#519aba" + }, + "_gradle_light": { + "fontCharacter": "\\E030", + "fontColor": "#723eb6" + }, + "_gradle": { + "fontCharacter": "\\E030", + "fontColor": "#8dc149" + }, + "_grails_light": { + "fontCharacter": "\\E031", + "fontColor": "#723eb6" + }, + "_grails": { + "fontCharacter": "\\E031", + "fontColor": "#8dc149" + }, + "_grunt_light": { + "fontCharacter": "\\E032", + "fontColor": "#1c86cc" + }, + "_grunt": { + "fontCharacter": "\\E032", + "fontColor": "#e37933" + }, + "_gulp_light": { + "fontCharacter": "\\E033", + "fontColor": "#33c1bb" + }, + "_gulp": { + "fontCharacter": "\\E033", + "fontColor": "#cc3e44" + }, + "_hacklang": { + "fontCharacter": "\\E034" + }, + "_haml_light": { + "fontCharacter": "\\E035", + "fontColor": "#33c1bb" + }, + "_haml": { + "fontCharacter": "\\E035", + "fontColor": "#cc3e44" + }, + "_haskell_light": { + "fontCharacter": "\\E036", + "fontColor": "#5f8b3b" + }, + "_haskell": { + "fontCharacter": "\\E036", + "fontColor": "#a074c4" + }, + "_heroku_light": { + "fontCharacter": "\\E037", + "fontColor": "#5f8b3b" + }, + "_heroku": { + "fontCharacter": "\\E037", + "fontColor": "#a074c4" + }, + "_hex_light": { + "fontCharacter": "\\E038", + "fontColor": "#33c1bb" + }, + "_hex": { + "fontCharacter": "\\E038", + "fontColor": "#cc3e44" + }, + "_html_light": { + "fontCharacter": "\\E039", + "fontColor": "#1c86cc" + }, + "_html": { + "fontCharacter": "\\E039", + "fontColor": "#e37933" + }, + "_html_erb": { + "fontCharacter": "\\E03A" + }, + "_ignored_light": { + "fontCharacter": "\\E03B", + "fontColor": "#beaca4" + }, + "_ignored": { + "fontCharacter": "\\E03B", + "fontColor": "#41535b" + }, + "_illustrator_light": { + "fontCharacter": "\\E03C", + "fontColor": "#3434be" + }, + "_illustrator": { + "fontCharacter": "\\E03C", + "fontColor": "#cbcb41" + }, + "_image_light": { + "fontCharacter": "\\E03D", + "fontColor": "#5f8b3b" + }, + "_image": { + "fontCharacter": "\\E03D", + "fontColor": "#a074c4" + }, + "_info_light": { + "fontCharacter": "\\E03E", + "fontColor": "#ae6545" + }, + "_info": { + "fontCharacter": "\\E03E", + "fontColor": "#519aba" + }, + "_ionic_light": { + "fontCharacter": "\\E03F", + "fontColor": "#ae6545" + }, + "_ionic": { + "fontCharacter": "\\E03F", + "fontColor": "#519aba" + }, + "_jade_light": { + "fontCharacter": "\\E040", + "fontColor": "#33c1bb" + }, + "_jade": { + "fontCharacter": "\\E040", + "fontColor": "#cc3e44" + }, + "_java_light": { + "fontCharacter": "\\E041", + "fontColor": "#33c1bb" + }, + "_java": { + "fontCharacter": "\\E041", + "fontColor": "#cc3e44" + }, + "_javascript_light": { + "fontCharacter": "\\E042", + "fontColor": "#ae6545" + }, + "_javascript": { + "fontCharacter": "\\E042", + "fontColor": "#519aba" + }, + "_jenkins_light": { + "fontCharacter": "\\E043", + "fontColor": "#33c1bb" + }, + "_jenkins": { + "fontCharacter": "\\E043", + "fontColor": "#cc3e44" + }, + "_jinja_light": { + "fontCharacter": "\\E044", + "fontColor": "#33c1bb" + }, + "_jinja": { + "fontCharacter": "\\E044", + "fontColor": "#cc3e44" + }, + "_js_erb": { + "fontCharacter": "\\E045" + }, + "_json_light": { + "fontCharacter": "\\E046", + "fontColor": "#3434be" + }, + "_json": { + "fontCharacter": "\\E046", + "fontColor": "#cbcb41" + }, + "_julia_light": { + "fontCharacter": "\\E047", + "fontColor": "#5f8b3b" + }, + "_julia": { + "fontCharacter": "\\E047", + "fontColor": "#a074c4" + }, + "_karma_light": { + "fontCharacter": "\\E048", + "fontColor": "#723eb6" + }, + "_karma": { + "fontCharacter": "\\E048", + "fontColor": "#8dc149" + }, + "_less_light": { + "fontCharacter": "\\E049", + "fontColor": "#ae6545" + }, + "_less": { + "fontCharacter": "\\E049", + "fontColor": "#519aba" + }, + "_license_light": { + "fontCharacter": "\\E04A", + "fontColor": "#33c1bb" + }, + "_license": { + "fontCharacter": "\\E04A", + "fontColor": "#cc3e44" + }, + "_liquid_light": { + "fontCharacter": "\\E04B", + "fontColor": "#723eb6" + }, + "_liquid": { + "fontCharacter": "\\E04B", + "fontColor": "#8dc149" + }, + "_livescript_light": { + "fontCharacter": "\\E04C", + "fontColor": "#ae6545" + }, + "_livescript": { + "fontCharacter": "\\E04C", + "fontColor": "#519aba" + }, + "_lock_light": { + "fontCharacter": "\\E04D", + "fontColor": "#723eb6" + }, + "_lock": { + "fontCharacter": "\\E04D", + "fontColor": "#8dc149" + }, + "_lua_light": { + "fontCharacter": "\\E04E", + "fontColor": "#ae6545" + }, + "_lua": { + "fontCharacter": "\\E04E", + "fontColor": "#519aba" + }, + "_makefile_light": { + "fontCharacter": "\\E04F", + "fontColor": "#ae6545" + }, + "_makefile": { + "fontCharacter": "\\E04F", + "fontColor": "#519aba" + }, + "_markdown_light": { + "fontCharacter": "\\E050", + "fontColor": "#ae6545" + }, + "_markdown": { + "fontCharacter": "\\E050", + "fontColor": "#519aba" + }, + "_maven_light": { + "fontCharacter": "\\E051", + "fontColor": "#33c1bb" + }, + "_maven": { + "fontCharacter": "\\E051", + "fontColor": "#cc3e44" + }, + "_mdo_light": { + "fontCharacter": "\\E052", + "fontColor": "#33c1bb" + }, + "_mdo": { + "fontCharacter": "\\E052", + "fontColor": "#cc3e44" + }, + "_mustache_light": { + "fontCharacter": "\\E053", + "fontColor": "#1c86cc" + }, + "_mustache": { + "fontCharacter": "\\E053", + "fontColor": "#e37933" + }, + "_new-file": { + "fontCharacter": "\\E054" + }, + "_npm_light": { + "fontCharacter": "\\E055", + "fontColor": "#33c1bb" + }, + "_npm": { + "fontCharacter": "\\E055", + "fontColor": "#cc3e44" + }, + "_npm_ignored_light": { + "fontCharacter": "\\E056", + "fontColor": "#beaca4" + }, + "_npm_ignored": { + "fontCharacter": "\\E056", + "fontColor": "#41535b" + }, + "_nunjucks_light": { + "fontCharacter": "\\E057", + "fontColor": "#723eb6" + }, + "_nunjucks": { + "fontCharacter": "\\E057", + "fontColor": "#8dc149" + }, + "_ocaml_light": { + "fontCharacter": "\\E058", + "fontColor": "#1c86cc" + }, + "_ocaml": { + "fontCharacter": "\\E058", + "fontColor": "#e37933" + }, + "_pdf_light": { + "fontCharacter": "\\E059", + "fontColor": "#33c1bb" + }, + "_pdf": { + "fontCharacter": "\\E059", + "fontColor": "#cc3e44" + }, + "_perl_light": { + "fontCharacter": "\\E05A", + "fontColor": "#ae6545" + }, + "_perl": { + "fontCharacter": "\\E05A", + "fontColor": "#519aba" + }, + "_photoshop_light": { + "fontCharacter": "\\E05B", + "fontColor": "#ae6545" + }, + "_photoshop": { + "fontCharacter": "\\E05B", + "fontColor": "#519aba" + }, + "_php_light": { + "fontCharacter": "\\E05C", + "fontColor": "#5f8b3b" + }, + "_php": { + "fontCharacter": "\\E05C", + "fontColor": "#a074c4" + }, + "_powershell_light": { + "fontCharacter": "\\E05D", + "fontColor": "#ae6545" + }, + "_powershell": { + "fontCharacter": "\\E05D", + "fontColor": "#519aba" + }, + "_project": { + "fontCharacter": "\\E05E" + }, + "_pug_light": { + "fontCharacter": "\\E05F", + "fontColor": "#33c1bb" + }, + "_pug": { + "fontCharacter": "\\E05F", + "fontColor": "#cc3e44" + }, + "_puppet_light": { + "fontCharacter": "\\E060", + "fontColor": "#3434be" + }, + "_puppet": { + "fontCharacter": "\\E060", + "fontColor": "#cbcb41" + }, + "_python_light": { + "fontCharacter": "\\E061", + "fontColor": "#ae6545" + }, + "_python": { + "fontCharacter": "\\E061", + "fontColor": "#519aba" + }, + "_rails": { + "fontCharacter": "\\E062" + }, + "_react_light": { + "fontCharacter": "\\E063", + "fontColor": "#ae6545" + }, + "_react": { + "fontCharacter": "\\E063", + "fontColor": "#519aba" + }, + "_rollup_light": { + "fontCharacter": "\\E064", + "fontColor": "#33c1bb" + }, + "_rollup": { + "fontCharacter": "\\E064", + "fontColor": "#cc3e44" + }, + "_ruby_light": { + "fontCharacter": "\\E065", + "fontColor": "#33c1bb" + }, + "_ruby": { + "fontCharacter": "\\E065", + "fontColor": "#cc3e44" + }, + "_rust_light": { + "fontCharacter": "\\E066", + "fontColor": "#927f79" + }, + "_rust": { + "fontCharacter": "\\E066", + "fontColor": "#6d8086" + }, + "_salesforce_light": { + "fontCharacter": "\\E067", + "fontColor": "#ae6545" + }, + "_salesforce": { + "fontCharacter": "\\E067", + "fontColor": "#519aba" + }, + "_sass_light": { + "fontCharacter": "\\E068", + "fontColor": "#aac7a" + }, + "_sass": { + "fontCharacter": "\\E068", + "fontColor": "#f55385" + }, + "_sbt_light": { + "fontCharacter": "\\E069", + "fontColor": "#ae6545" + }, + "_sbt": { + "fontCharacter": "\\E069", + "fontColor": "#519aba" + }, + "_scala_light": { + "fontCharacter": "\\E06A", + "fontColor": "#33c1bb" + }, + "_scala": { + "fontCharacter": "\\E06A", + "fontColor": "#cc3e44" + }, + "_search": { + "fontCharacter": "\\E06B" + }, + "_settings": { + "fontCharacter": "\\E06C" + }, + "_shell_light": { + "fontCharacter": "\\E06D", + "fontColor": "#b2a5a1" + }, + "_shell": { + "fontCharacter": "\\E06D", + "fontColor": "#4d5a5e" + }, + "_slim_light": { + "fontCharacter": "\\E06E", + "fontColor": "#1c86cc" + }, + "_slim": { + "fontCharacter": "\\E06E", + "fontColor": "#e37933" + }, + "_smarty_light": { + "fontCharacter": "\\E06F", + "fontColor": "#3434be" + }, + "_smarty": { + "fontCharacter": "\\E06F", + "fontColor": "#cbcb41" + }, + "_spring_light": { + "fontCharacter": "\\E070", + "fontColor": "#723eb6" + }, + "_spring": { + "fontCharacter": "\\E070", + "fontColor": "#8dc149" + }, + "_stylus_light": { + "fontCharacter": "\\E071", + "fontColor": "#723eb6" + }, + "_stylus": { + "fontCharacter": "\\E071", + "fontColor": "#8dc149" + }, + "_sublime_light": { + "fontCharacter": "\\E072", + "fontColor": "#1c86cc" + }, + "_sublime": { + "fontCharacter": "\\E072", + "fontColor": "#e37933" + }, + "_svg_light": { + "fontCharacter": "\\E073", + "fontColor": "#5f8b3b" + }, + "_svg": { + "fontCharacter": "\\E073", + "fontColor": "#a074c4" + }, + "_swift_light": { + "fontCharacter": "\\E074", + "fontColor": "#1c86cc" + }, + "_swift": { + "fontCharacter": "\\E074", + "fontColor": "#e37933" + }, + "_terraform_light": { + "fontCharacter": "\\E075", + "fontColor": "#5f8b3b" + }, + "_terraform": { + "fontCharacter": "\\E075", + "fontColor": "#a074c4" + }, + "_tex_light": { + "fontCharacter": "\\E076", + "fontColor": "#2b2829" + }, + "_tex": { + "fontCharacter": "\\E076", + "fontColor": "#d4d7d6" + }, + "_time-cop": { + "fontCharacter": "\\E077" + }, + "_todo": { + "fontCharacter": "\\E078" + }, + "_twig_light": { + "fontCharacter": "\\E079", + "fontColor": "#723eb6" + }, + "_twig": { + "fontCharacter": "\\E079", + "fontColor": "#8dc149" + }, + "_typescript_light": { + "fontCharacter": "\\E07A", + "fontColor": "#ae6545" + }, + "_typescript": { + "fontCharacter": "\\E07A", + "fontColor": "#519aba" + }, + "_vala_light": { + "fontCharacter": "\\E07B", + "fontColor": "#927f79" + }, + "_vala": { + "fontCharacter": "\\E07B", + "fontColor": "#6d8086" + }, + "_video_light": { + "fontCharacter": "\\E07C", + "fontColor": "#aac7a" + }, + "_video": { + "fontCharacter": "\\E07C", + "fontColor": "#f55385" + }, + "_vue_light": { + "fontCharacter": "\\E07D", + "fontColor": "#723eb6" + }, + "_vue": { + "fontCharacter": "\\E07D", + "fontColor": "#8dc149" + }, + "_windows_light": { + "fontCharacter": "\\E07E", + "fontColor": "#ae6545" + }, + "_windows": { + "fontCharacter": "\\E07E", + "fontColor": "#519aba" + }, + "_word_light": { + "fontCharacter": "\\E07F", + "fontColor": "#ae6545" + }, + "_word": { + "fontCharacter": "\\E07F", + "fontColor": "#519aba" + }, + "_xls_light": { + "fontCharacter": "\\E080", + "fontColor": "#723eb6" + }, + "_xls": { + "fontCharacter": "\\E080", + "fontColor": "#8dc149" + }, + "_xml_light": { + "fontCharacter": "\\E081", + "fontColor": "#1c86cc" + }, + "_xml": { + "fontCharacter": "\\E081", + "fontColor": "#e37933" + }, + "_yarn_light": { + "fontCharacter": "\\E082", + "fontColor": "#ae6545" + }, + "_yarn": { + "fontCharacter": "\\E082", + "fontColor": "#519aba" + }, + "_yml_light": { + "fontCharacter": "\\E083", + "fontColor": "#5f8b3b" + }, + "_yml": { + "fontCharacter": "\\E083", + "fontColor": "#a074c4" + }, + "_zip_light": { + "fontCharacter": "\\E084", + "fontColor": "#927f79" + }, + "_zip": { + "fontCharacter": "\\E084", + "fontColor": "#6d8086" + } + }, + "file": "_default", + "fileExtensions": { + "bsl": "_bsl", + "mdo": "_mdo", + "asm": "_asm", + "s": "_asm", + "h": "_c", + "cfc": "_coldfusion", + "cfm": "_coldfusion", + "config": "_config", + "cfg": "_config", + "conf": "_config", + "cson": "_json", + "css.map": "_css", + "sss": "_css", + "csv": "_csv", + "xls": "_xls", + "xlsx": "_xls", + "cake": "_cake", + "ctp": "_cake_php", + "d": "_d", + "doc": "_word", + "docx": "_word", + "ejs": "_ejs", + "ex": "_elixir", + "exs": "_elixir_script", + "elm": "_elm", + "ico": "_favicon", + "gitignore": "_git", + "gitconfig": "_git", + "gitkeep": "_git", + "gitattributes": "_git", + "gitmodules": "_git", + "slide": "_go", + "article": "_go", + "gradle": "_gradle", + "gsp": "_grails", + "haml": "_haml", + "hjs": "_mustache", + "hs": "_haskell", + "lhs": "_haskell", + "class": "_java", + "classpath": "_java", + "js.map": "_javascript", + "spec.js": "_javascript", + "es": "_javascript", + "es5": "_javascript", + "es7": "_javascript", + "jinja": "_jinja", + "jinja2": "_jinja", + "jl": "_julia", + "liquid": "_liquid", + "ls": "_livescript", + "mustache": "_mustache", + "stache": "_mustache", + "njk": "_nunjucks", + "nunjucks": "_nunjucks", + "nunjs": "_nunjucks", + "nunj": "_nunjucks", + "njs": "_nunjucks", + "nj": "_nunjucks", + "npm-debug.log": "_npm", + "npmignore": "_npm", + "npmrc": "_npm", + "ml": "_ocaml", + "mli": "_ocaml", + "cmx": "_ocaml", + "cmxa": "_ocaml", + "php.inc": "_php", + "pug": "_pug", + "pp": "_puppet", + "epp": "_puppet", + "cjsx": "_react", + "erb.html": "_ruby", + "html.erb": "_ruby", + "sass": "_sass", + "springbeans": "_spring", + "slim": "_slim", + "smarty.tpl": "_smarty", + "sbt": "_sbt", + "scala": "_scala", + "styl": "_stylus", + "tf": "_terraform", + "tf.json": "_terraform", + "tex": "_tex", + "sty": "_tex", + "dtx": "_tex", + "ins": "_tex", + "txt": "_default", + "toml": "_config", + "twig": "_twig", + "vala": "_vala", + "vapi": "_vala", + "vue": "_vue", + "jar": "_zip", + "zip": "_zip", + "ai": "_illustrator", + "psd": "_photoshop", + "pdf": "_pdf", + "eot": "_font", + "ttf": "_font", + "woff": "_font", + "woff2": "_font", + "gif": "_image", + "jpg": "_image", + "jpeg": "_image", + "png": "_image", + "pxm": "_image", + "svg": "_svg", + "svgx": "_image", + "sublime-project": "_sublime", + "sublime-workspace": "_sublime", + "component": "_salesforce", + "cls": "_salesforce", + "fish": "_shell", + "mov": "_video", + "ogv": "_video", + "webm": "_video", + "avi": "_video", + "mpg": "_video", + "mp4": "_video", + "mp3": "_audio", + "ogg": "_audio", + "wav": "_audio", + "babelrc": "_babel", + "bowerrc": "_bower", + "dockerignore": "_docker", + "codeclimate.yml": "_code-climate", + "eslintrc": "_eslint", + "eslintrc.js": "_eslint", + "eslintrc.yaml": "_eslint", + "eslintrc.yml": "_eslint", + "eslintrc.json": "_eslint", + "eslintignore": "_eslint", + "firebaserc": "_firebase", + "jshintrc": "_javascript", + "jscsrc": "_javascript", + "direnv": "_config", + "env": "_config", + "static": "_config", + "editorconfig": "_config", + "slugignore": "_config", + "tmp": "_clock", + "htaccess": "_config", + "key": "_lock", + "cert": "_lock", + "ds_store": "_ignored", + "dashbord": "_dashboard" + }, + "fileNames": { + "mix": "_hex", + "karma.conf.js": "_karma", + "karma.conf.coffee": "_karma", + "readme.md": "_info", + "changelog.md": "_clock", + "changelog": "_clock", + "version.md": "_clock", + "version": "_clock", + "mvnw": "_maven", + "mime.types": "_config", + "jenkinsfile": "_jenkins", + "bower.json": "_bower", + "docker-healthcheck": "_docker", + "docker-compose.yml": "_docker", + "firebase.json": "_firebase", + "geckodriver": "_firefox", + "gruntfile.js": "_grunt", + "gruntfile.babel.js": "_grunt", + "gruntfile.coffee": "_grunt", + "gulpfile": "_gulp", + "ionic.config.json": "_ionic", + "ionic.project": "_ionic", + "rollup.config.js": "_rollup", + "sass-lint.yml": "_sass", + "yarn.clean": "_yarn", + "yarn.lock": "_yarn", + "license": "_license", + "licence": "_license", + "copying": "_license", + "compiling": "_license", + "contributing": "_license", + "qmakefile": "_makefile", + "omakefile": "_makefile", + "cmakelists.txt": "_makefile", + "procfile": "_heroku", + "todo": "_todo", + "npm-debug.log": "_npm_ignored" + }, + "languageIds": { + "bat": "_windows", + "coffeescript": "_coffee", + "c": "_c", + "cpp": "_cpp", + "csharp": "_c-sharp", + "css": "_css", + "dashboard": "_dashboard", + "dockerfile": "_docker", + "fsharp": "_f-sharp", + "go": "_go2", + "groovy": "_grails", + "handlebars": "_mustache", + "html": "_html", + "properties": "_java", + "java": "_java", + "javascriptreact": "_react", + "javascript": "_javascript", + "json": "_json", + "less": "_less", + "lua": "_lua", + "makefile": "_makefile", + "markdown": "_markdown", + "objective-c": "_c", + "perl": "_perl", + "php": "_php", + "powershell": "_powershell", + "jade": "_jade", + "python": "_python", + "ruby": "_ruby", + "rust": "_rust", + "scss": "_sass", + "shellscript": "_shell", + "sql": "_db", + "swift": "_swift", + "typescript": "_typescript", + "typescriptreact": "_react", + "xml": "_xml", + "yaml": "_yml" + }, + "light": { + "file": "_default_light", + "fileExtensions": { + "bsl": "_bsl_light", + "mdo": "_mdo_light", + "asm": "_asm_light", + "s": "_asm_light", + "h": "_c_light", + "cfc": "_coldfusion_light", + "cfm": "_coldfusion_light", + "config": "_config_light", + "cfg": "_config_light", + "conf": "_config_light", + "cson": "_json_light", + "css.map": "_css_light", + "sss": "_css_light", + "csv": "_csv_light", + "xls": "_xls_light", + "xlsx": "_xls_light", + "cake": "_cake_light", + "ctp": "_cake_php_light", + "d": "_d_light", + "doc": "_word_light", + "docx": "_word_light", + "ejs": "_ejs_light", + "ex": "_elixir_light", + "exs": "_elixir_script_light", + "elm": "_elm_light", + "ico": "_favicon_light", + "gitignore": "_git_light", + "gitconfig": "_git_light", + "gitkeep": "_git_light", + "gitattributes": "_git_light", + "gitmodules": "_git_light", + "slide": "_go_light", + "article": "_go_light", + "gradle": "_gradle_light", + "gsp": "_grails_light", + "haml": "_haml_light", + "hjs": "_mustache_light", + "hs": "_haskell_light", + "lhs": "_haskell_light", + "class": "_java_light", + "classpath": "_java_light", + "js.map": "_javascript_light", + "spec.js": "_javascript_light", + "es": "_javascript_light", + "es5": "_javascript_light", + "es7": "_javascript_light", + "jinja": "_jinja_light", + "jinja2": "_jinja_light", + "jl": "_julia_light", + "liquid": "_liquid_light", + "ls": "_livescript_light", + "mustache": "_mustache_light", + "stache": "_mustache_light", + "njk": "_nunjucks_light", + "nunjucks": "_nunjucks_light", + "nunjs": "_nunjucks_light", + "nunj": "_nunjucks_light", + "njs": "_nunjucks_light", + "nj": "_nunjucks_light", + "npm-debug.log": "_npm_light", + "npmignore": "_npm_light", + "npmrc": "_npm_light", + "ml": "_ocaml_light", + "mli": "_ocaml_light", + "cmx": "_ocaml_light", + "cmxa": "_ocaml_light", + "php.inc": "_php_light", + "pug": "_pug_light", + "pp": "_puppet_light", + "epp": "_puppet_light", + "cjsx": "_react_light", + "erb.html": "_ruby_light", + "html.erb": "_ruby_light", + "sass": "_sass_light", + "springbeans": "_spring_light", + "slim": "_slim_light", + "smarty.tpl": "_smarty_light", + "sbt": "_sbt_light", + "scala": "_scala_light", + "styl": "_stylus_light", + "tf": "_terraform_light", + "tf.json": "_terraform_light", + "tex": "_tex_light", + "sty": "_tex_light", + "dtx": "_tex_light", + "ins": "_tex_light", + "txt": "_default_light", + "toml": "_config_light", + "twig": "_twig_light", + "vala": "_vala_light", + "vapi": "_vala_light", + "vue": "_vue_light", + "jar": "_zip_light", + "zip": "_zip_light", + "ai": "_illustrator_light", + "psd": "_photoshop_light", + "pdf": "_pdf_light", + "eot": "_font_light", + "ttf": "_font_light", + "woff": "_font_light", + "woff2": "_font_light", + "gif": "_image_light", + "jpg": "_image_light", + "jpeg": "_image_light", + "png": "_image_light", + "pxm": "_image_light", + "svg": "_svg_light", + "svgx": "_image_light", + "sublime-project": "_sublime_light", + "sublime-workspace": "_sublime_light", + "component": "_salesforce_light", + "cls": "_salesforce_light", + "fish": "_shell_light", + "mov": "_video_light", + "ogv": "_video_light", + "webm": "_video_light", + "avi": "_video_light", + "mpg": "_video_light", + "mp4": "_video_light", + "mp3": "_audio_light", + "ogg": "_audio_light", + "wav": "_audio_light", + "babelrc": "_babel_light", + "bowerrc": "_bower_light", + "dockerignore": "_docker_light", + "codeclimate.yml": "_code-climate_light", + "eslintrc": "_eslint_light", + "eslintrc.js": "_eslint_light", + "eslintrc.yaml": "_eslint_light", + "eslintrc.yml": "_eslint_light", + "eslintrc.json": "_eslint_light", + "eslintignore": "_eslint_light", + "firebaserc": "_firebase_light", + "jshintrc": "_javascript_light", + "jscsrc": "_javascript_light", + "direnv": "_config_light", + "env": "_config_light", + "static": "_config_light", + "editorconfig": "_config_light", + "slugignore": "_config_light", + "tmp": "_clock_light", + "htaccess": "_config_light", + "key": "_lock_light", + "cert": "_lock_light", + "ds_store": "_ignored_light", + "dashboard": "_dashboard_light" + }, + "languageIds": { + "bat": "_windows_light", + "coffeescript": "_coffee_light", + "c": "_c_light", + "cpp": "_cpp_light", + "csharp": "_c-sharp_light", + "css": "_css_light", + "dashboard": "_dashboard_light", + "dockerfile": "_docker_light", + "fsharp": "_f-sharp_light", + "go": "_go2_light", + "groovy": "_grails_light", + "handlebars": "_mustache_light", + "html": "_html_light", + "properties": "_java_light", + "java": "_java_light", + "javascriptreact": "_react_light", + "javascript": "_javascript_light", + "json": "_json_light", + "less": "_less_light", + "lua": "_lua_light", + "makefile": "_makefile_light", + "markdown": "_markdown_light", + "objective-c": "_c_light", + "perl": "_perl_light", + "php": "_php_light", + "powershell": "_powershell_light", + "jade": "_jade_light", + "python": "_python_light", + "ruby": "_ruby_light", + "rust": "_rust_light", + "scss": "_sass_light", + "shellscript": "_shell_light", + "sql": "_db_light", + "swift": "_swift_light", + "typescript": "_typescript_light", + "typescriptreact": "_react_light", + "xml": "_xml_light", + "yaml": "_yml_light" + }, + "fileNames": { + "mix": "_hex_light", + "karma.conf.js": "_karma_light", + "karma.conf.coffee": "_karma_light", + "readme.md": "_info_light", + "changelog.md": "_clock_light", + "changelog": "_clock_light", + "version.md": "_clock_light", + "version": "_clock_light", + "mvnw": "_maven_light", + "mime.types": "_config_light", + "jenkinsfile": "_jenkins_light", + "bower.json": "_bower_light", + "docker-healthcheck": "_docker_light", + "docker-compose.yml": "_docker_light", + "firebase.json": "_firebase_light", + "geckodriver": "_firefox_light", + "gruntfile.js": "_grunt_light", + "gruntfile.babel.js": "_grunt_light", + "gruntfile.coffee": "_grunt_light", + "gulpfile": "_gulp_light", + "ionic.config.json": "_ionic_light", + "ionic.project": "_ionic_light", + "rollup.config.js": "_rollup_light", + "sass-lint.yml": "_sass_light", + "yarn.clean": "_yarn_light", + "yarn.lock": "_yarn_light", + "license": "_license_light", + "licence": "_license_light", + "copying": "_license_light", + "compiling": "_license_light", + "contributing": "_license_light", + "qmakefile": "_makefile_light", + "omakefile": "_makefile_light", + "cmakelists.txt": "_makefile_light", + "procfile": "_heroku_light", + "npm-debug.log": "_npm_ignored_light" + } + }, + "version": "https://github.com/jesseweed/seti-ui/commit/c44b29c7a5b2f189fccfd58ceea02b117678caa9" +} \ No newline at end of file diff --git a/extensions/theme-seti/package.json b/extensions/theme-seti/package.json new file mode 100644 index 0000000000..21c6189055 --- /dev/null +++ b/extensions/theme-seti/package.json @@ -0,0 +1,20 @@ +{ + "name": "vscode-theme-seti", + "private": true, + "version": "0.1.0", + "description": "A file icon theme made out of the Seti UI file icons", + "publisher": "vscode", + "scripts": { + "update": "node ./build/update-icon-theme.js" + }, + "engines": { "vscode": "*" }, + "contributes": { + "iconThemes": [ + { + "id": "vs-seti", + "label": "Seti (Visual Studio Code)", + "path": "./icons/vs-seti-icon-theme.json" + } + ] + } +} diff --git a/extensions/theme-seti/thirdpartynotices.txt b/extensions/theme-seti/thirdpartynotices.txt new file mode 100644 index 0000000000..5faf4bb5a8 --- /dev/null +++ b/extensions/theme-seti/thirdpartynotices.txt @@ -0,0 +1,32 @@ + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +For Microsoft vscode-theme-seti + +This file is based on or incorporates material from the projects listed below ("Third Party OSS"). The original copyright +notice and the license under which Microsoft received such Third Party OSS, are set forth below. Such licenses and notice +are provided for informational purposes only. Microsoft licenses the Third Party OSS to you under the licensing terms for +the Microsoft product or service. Microsoft reserves all other rights not expressly granted under this agreement, whether +by implication, estoppel or otherwise.† + +1. Seti UI - A subtle dark colored UI theme for Atom. (https://github.com/jesseweed/seti-ui) + +Copyright (c) 2014 Jesse Weed + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/extensions/theme-solarized-dark/OSSREADME.json b/extensions/theme-solarized-dark/OSSREADME.json new file mode 100644 index 0000000000..ddb630bfd9 --- /dev/null +++ b/extensions/theme-solarized-dark/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "Colorsublime-Themes", + "version": "0.1.0", + "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", + "description": "The themes in this folders are copied from colorsublime.com. <<>>" +}] diff --git a/extensions/theme-solarized-dark/package.json b/extensions/theme-solarized-dark/package.json new file mode 100644 index 0000000000..3d8f91a8dc --- /dev/null +++ b/extensions/theme-solarized-dark/package.json @@ -0,0 +1,15 @@ +{ + "name": "theme-solarized-dark", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "themes": [ + { + "label": "Solarized Dark", + "uiTheme": "vs-dark", + "path": "./themes/solarized-dark-color-theme.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-solarized-dark/themes/Solarized-dark.tmTheme b/extensions/theme-solarized-dark/themes/Solarized-dark.tmTheme new file mode 100644 index 0000000000..d6f531fcd4 --- /dev/null +++ b/extensions/theme-solarized-dark/themes/Solarized-dark.tmTheme @@ -0,0 +1,439 @@ + + + + + name + Solarized (dark) + settings + + + settings + + background + #002B36 + caret + #D30102 + foreground + #93A1A1 + invisibles + #93A1A180 + lineHighlight + #073642 + selection + #073642 + + + + name + Comment + scope + comment + settings + + fontStyle + italic + foreground + #657B83 + + + + name + String + scope + string + settings + + foreground + #2AA198 + + + + name + Regexp + scope + string.regexp + settings + + foreground + #D30102 + + + + name + Number + scope + constant.numeric + settings + + foreground + #D33682 + + + + name + Variable + scope + variable.language, variable.other + settings + + foreground + #268BD2 + + + + name + Keyword + scope + keyword + settings + + foreground + #859900 + + + + name + Storage + scope + storage + settings + + fontStyle + bold + foreground + #93A1A1 + + + + name + Class name + scope + entity.name.class, entity.name.type + settings + + fontStyle + + foreground + #CB4B16 + + + + name + Function name + scope + entity.name.function + settings + + foreground + #268BD2 + + + + name + Variable start + scope + punctuation.definition.variable + settings + + foreground + #859900 + + + + name + Embedded code markers + scope + punctuation.section.embedded.begin, punctuation.section.embedded.end + settings + + foreground + #D30102 + + + + name + Built-in constant + scope + constant.language, meta.preprocessor + settings + + foreground + #B58900 + + + + name + Support.construct + scope + support.function.construct, keyword.other.new + settings + + foreground + #CB4B16 + + + + name + User-defined constant + scope + constant.character, constant.other + settings + + foreground + #CB4B16 + + + + name + Inherited class + scope + entity.other.inherited-class + settings + + foreground + #6C71C4 + + + + name + Function argument + scope + variable.parameter + settings + + + + name + Tag name + scope + entity.name.tag + settings + + foreground + #268BD2 + + + + name + Tag start/end + scope + punctuation.definition.tag + settings + + foreground + #657B83 + + + + name + Tag attribute + scope + entity.other.attribute-name + settings + + foreground + #93A1A1 + + + + name + Library function + scope + support.function + settings + + foreground + #268BD2 + + + + name + Continuation + scope + punctuation.separator.continuation + settings + + foreground + #D30102 + + + + name + Library constant + scope + support.constant + settings + + + + name + Library class/type + scope + support.type, support.class + settings + + foreground + #859900 + + + + name + Library Exception + scope + support.type.exception + settings + + foreground + #CB4B16 + + + + name + Library variable + scope + support.other.variable + settings + + + + name + Invalid + scope + invalid + settings + + + + name + diff: header + scope + meta.diff, meta.diff.header + settings + + background + #b58900 + fontStyle + italic + foreground + #E0EDDD + + + + name + diff: deleted + scope + markup.deleted + settings + + background + #eee8d5 + fontStyle + + foreground + #dc322f + + + + name + diff: changed + scope + markup.changed + settings + + background + #eee8d5 + fontStyle + + foreground + #cb4b16 + + + + name + diff: inserted + scope + markup.inserted + settings + + background + #eee8d5 + foreground + #219186 + + + + name + Markup Quote + scope + markup.quote + settings + + foreground + #859900 + + + + name + Markup Lists + scope + markup.list + settings + + foreground + #B58900 + + + + name + Markup Styling + scope + markup.bold, markup.italic + settings + + foreground + #D33682 + + + + name + Markup Inline + scope + markup.inline.raw + settings + + fontStyle + + foreground + #2AA198 + + + + name + Markup Headings + scope + markup.heading + settings + + foreground + #268BD2 + + + + name + Markup Setext Header + scope + markup.heading.setext + settings + + fontStyle + + foreground + #268BD2 + + + + + uuid + F930B0BF-AA03-4232-A30F-CEF749FF8E72 + + diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json new file mode 100644 index 0000000000..5309eb6fd1 --- /dev/null +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -0,0 +1,479 @@ +{ + "name": "Solarized (dark)", + "tokenColors": [ + { + "settings": { + "background": "#002B36", + "foreground": "#93A1A1" + } + }, + { + "name": "Comment", + "scope": "comment", + "settings": { + "fontStyle": "italic", + "foreground": "#657B83" + } + }, + { + "name": "String", + "scope": "string", + "settings": { + "foreground": "#2AA198" + } + }, + { + "name": "Regexp", + "scope": "string.regexp", + "settings": { + "foreground": "#D30102" + } + }, + { + "name": "Number", + "scope": "constant.numeric", + "settings": { + "foreground": "#D33682" + } + }, + { + "name": "Variable", + "scope": [ + "variable.language", + "variable.other" + ], + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Keyword", + "scope": "keyword", + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Storage", + "scope": "storage", + "settings": { + "fontStyle": "bold", + "foreground": "#93A1A1" + } + }, + { + "name": "Class name", + "scope": [ + "entity.name.class", + "entity.name.type" + ], + "settings": { + "fontStyle": "", + "foreground": "#CB4B16" + } + }, + { + "name": "Function name", + "scope": "entity.name.function", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Variable start", + "scope": "punctuation.definition.variable", + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Embedded code markers", + "scope": [ + "punctuation.section.embedded.begin", + "punctuation.section.embedded.end" + ], + "settings": { + "foreground": "#D30102" + } + }, + { + "name": "Built-in constant", + "scope": [ + "constant.language", + "meta.preprocessor" + ], + "settings": { + "foreground": "#B58900" + } + }, + { + "name": "Support.construct", + "scope": [ + "support.function.construct", + "keyword.other.new" + ], + "settings": { + "foreground": "#CB4B16" + } + }, + { + "name": "User-defined constant", + "scope": [ + "constant.character", + "constant.other" + ], + "settings": { + "foreground": "#CB4B16" + } + }, + { + "name": "Inherited class", + "scope": "entity.other.inherited-class", + "settings": { + "foreground": "#6C71C4" + } + }, + { + "name": "Function argument", + "scope": "variable.parameter", + "settings": {} + }, + { + "name": "Tag name", + "scope": "entity.name.tag", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Tag start/end", + "scope": "punctuation.definition.tag", + "settings": { + "foreground": "#657B83" + } + }, + { + "name": "Tag attribute", + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#93A1A1" + } + }, + { + "name": "Library function", + "scope": "support.function", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Continuation", + "scope": "punctuation.separator.continuation", + "settings": { + "foreground": "#D30102" + } + }, + { + "name": "Library constant", + "scope": "support.constant", + "settings": {} + }, + { + "name": "Library class/type", + "scope": [ + "support.type", + "support.class" + ], + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Library Exception", + "scope": "support.type.exception", + "settings": { + "foreground": "#CB4B16" + } + }, + { + "name": "Library variable", + "scope": "support.other.variable", + "settings": {} + }, + { + "name": "Invalid", + "scope": "invalid", + "settings": {} + }, + { + "name": "diff: header", + "scope": [ + "meta.diff", + "meta.diff.header" + ], + "settings": { + "background": "#b58900", + "fontStyle": "italic", + "foreground": "#E0EDDD" + } + }, + { + "name": "diff: deleted", + "scope": "markup.deleted", + "settings": { + "background": "#eee8d5", + "fontStyle": "", + "foreground": "#dc322f" + } + }, + { + "name": "diff: changed", + "scope": "markup.changed", + "settings": { + "background": "#eee8d5", + "fontStyle": "", + "foreground": "#cb4b16" + } + }, + { + "name": "diff: inserted", + "scope": "markup.inserted", + "settings": { + "background": "#eee8d5", + "foreground": "#219186" + } + }, + { + "name": "Markup Quote", + "scope": "markup.quote", + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Markup Lists", + "scope": "markup.list", + "settings": { + "foreground": "#B58900" + } + }, + { + "name": "Markup Styling", + "scope": [ + "markup.bold", + "markup.italic" + ], + "settings": { + "foreground": "#D33682" + } + }, + { + "name": "Markup Inline", + "scope": "markup.inline.raw", + "settings": { + "fontStyle": "", + "foreground": "#2AA198" + } + }, + { + "name": "Markup Headings", + "scope": "markup.heading", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Markup Setext Header", + "scope": "markup.heading.setext", + "settings": { + "fontStyle": "", + "foreground": "#268BD2" + } + } + ], + "colors": { + + // Base + // "foreground": "", + "focusBorder": "#2AA19899", + // "contrastActiveBorder": "", + // "contrastBorder": "", + + // "widget.shadow": "", + + "selection.background": "#2AA19899", + + "input.background": "#003847", + "input.foreground": "#93A1A1", + "input.placeholderForeground": "#93A1A1AA", + // "input.border": "", + + "inputOption.activeBorder": "#2AA19899", + "inputValidation.infoBorder": "#363b5f", + "inputValidation.infoBackground": "#052730", + "inputValidation.warningBackground": "#5d5938", + "inputValidation.warningBorder": "#9d8a5e", + "inputValidation.errorBackground": "#571b26", + "inputValidation.errorBorder": "#a92049", + + "errorForeground": "#ffeaea", + + "badge.background": "#047aa6", + "progressBar.background": "#047aa6", + + "dropdown.background": "#00212B", + "dropdown.border": "#2AA19899", + // "dropdown.foreground": "", + + "button.background": "#2AA19899", + // "button.foreground": "", + + "list.activeSelectionBackground": "#005A6F", + // "list.activeSelectionForeground": "", + "list.focusBackground": "#005A6F", + "list.hoverBackground": "#004454AA", + "list.inactiveSelectionBackground": "#00445488", + "list.dropBackground": "#00445488", + "list.highlightForeground": "#1ebcc5", + + // "scrollbar.shadow": "", + // "scrollbarSlider.activeBackground": "", + // "scrollbarSlider.background": "", + // "scrollbarSlider.hoverBackground": "", + + // Editor + "editor.background": "#002B36", + // "editor.foreground": "#6688cc", + "editorWidget.background": "#00212B", + "editorCursor.foreground": "#D30102", + "editorWhitespace.foreground": "#93A1A180", + "editor.lineHighlightBackground": "#073642", + "editor.selectionBackground": "#073642", + // "editorIndentGuide.background": "", + "editorHoverWidget.background": "#004052", + // "editorHoverWidget.border": "", + // "editorLineNumber.foreground": "", + // "editorMarkerNavigation.background": "", + "editorMarkerNavigationError.background": "#AB395B", + "editorMarkerNavigationWarning.background": "#5B7E7A", + // "editorLink.activeForeground": "", + // "editor.findMatchBackground": "", + // "editor.findMatchHighlightBackground": "", + // "editor.findRangeHighlightBackground": "", + // "editor.hoverHighlightBackground": "", + // "editor.inactiveSelectionBackground": "", + // "editor.lineHighlightBorder": "", + // "editor.rangeHighlightBackground": "", + // "editor.selectionHighlightBackground": "", + // "editor.wordHighlightBackground": "", + // "editor.wordHighlightStrongBackground": "", + + // Editor: Suggest + // "editorSuggestWidget.background": "", + // "editorSuggestWidget.border": "", + // "editorSuggestWidget.foreground": "", + // "editorSuggestWidget.highlightForeground": "", + // "editorSuggestWidget.selectedBackground": "", + + // Editor: Peek View + "peekViewResult.background": "#00212B", + // "peekViewResult.lineForeground": "", + // "peekViewResult.selectionBackground": "", + // "peekViewResult.selectionForeground": "", + "peekViewEditor.background": "#10192c", + "peekViewTitle.background": "#00212B", + "peekView.border": "#2b2b4a", + "peekViewEditor.matchHighlightBackground": "#7744AA40", + // "peekViewResult.fileForeground": "", + // "peekViewResult.matchHighlightBackground": "", + // "peekViewTitleLabel.foreground": "", + // "peekViewTitleDescription.foreground": "", + + // Editor: Diff + // "diffEditor.insertedTextBackground": "", + // "diffEditor.insertedTextBorder": "", + // "diffEditor.removedTextBackground": "", + // "diffEditor.removedTextBorder": "", + + // Workbench: Title + "titleBar.activeBackground": "#002C39", + // "titleBar.inactiveBackground": "", + // "titleBar.activeForeground": "", + // "titleBar.inactiveForeground": "", + + // Workbench: Editors + // "editorGroupHeader.noTabsBackground": "", + "editorGroup.border": "#00212B", + "editorGroup.background": "#011b23", + "editorGroup.dropBackground": "#2AA19844", + "editorGroupHeader.tabsBackground": "#004052", + + // Workbench: Tabs + "tab.activeForeground": "#d6dbdb", + "tab.activeBackground": "#002B37", + "tab.inactiveForeground": "#93A1A1", + "tab.inactiveBackground": "#004052", + "tab.border": "#003847", + + // Workbench: Activity Bar + "activityBar.background": "#003847", + // "activityBarBadge.background": "", + // "activityBar.dropBackground": "", + // "activityBar.foreground": "", + // "activityBarBadge.foreground": "", + + // Workbench: Panel + // "panel.background": "", + "panel.border": "#2b2b4a", + // "panelTitle.activeBorder": "", + // "panelTitle.activeForeground": "", + // "panelTitle.inactiveForeground": "", + + // Workbench: Side Bar + "sideBar.background": "#00212B", + "sideBarTitle.foreground": "#93A1A1", + // "sideBarSectionHeader.background": "", + + // Workbench: Status Bar + "statusBar.foreground": "#93A1A1", + "statusBar.background": "#00212B", + "statusBar.debuggingBackground": "#00212B", + "statusBar.noFolderBackground": "#00212B", + "statusBarItem.prominentBackground": "#003847", + "statusBarItem.prominentHoverBackground": "#003847", + // "statusBarItem.activeBackground": "", + // "statusBarItem.hoverBackground": "", + + // Workbench: Debug + "debugToolBar.background": "#00212B", + "debugExceptionWidget.background": "#00212B", + "debugExceptionWidget.border": "#AB395B", + + // Workbench: Notifications + "notification.background": "#003847", + // "notification.foreground": "", + + // Workbench: Quick Open + "pickerGroup.foreground": "#2AA19899", + "pickerGroup.border": "#2AA19899", + + // Workbench: Terminal + // Colors sourced from the official palette http://ethanschoonover.com/solarized + "terminal.ansiBlack": "#073642", + "terminal.ansiRed": "#dc322f", + "terminal.ansiGreen": "#859900", + "terminal.ansiYellow": "#b58900", + "terminal.ansiBlue": "#268bd2", + "terminal.ansiMagenta": "#d33682", + "terminal.ansiCyan": "#2aa198", + "terminal.ansiWhite": "#eee8d5", + "terminal.ansiBrightBlack": "#586e75", + "terminal.ansiBrightRed": "#cb4b16", + "terminal.ansiBrightGreen": "#586e75", + "terminal.ansiBrightYellow": "#657b83", + "terminal.ansiBrightBlue": "#839496", + "terminal.ansiBrightMagenta": "#6c71c4", + "terminal.ansiBrightCyan": "#93a1a1", + "terminal.ansiBrightWhite": "#fdf6e3" + } +} \ No newline at end of file diff --git a/extensions/theme-solarized-light/OSSREADME.json b/extensions/theme-solarized-light/OSSREADME.json new file mode 100644 index 0000000000..ddb630bfd9 --- /dev/null +++ b/extensions/theme-solarized-light/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "Colorsublime-Themes", + "version": "0.1.0", + "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", + "description": "The themes in this folders are copied from colorsublime.com. <<>>" +}] diff --git a/extensions/theme-solarized-light/package.json b/extensions/theme-solarized-light/package.json new file mode 100644 index 0000000000..57570d33cb --- /dev/null +++ b/extensions/theme-solarized-light/package.json @@ -0,0 +1,15 @@ +{ + "name": "theme-solarized-light", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "themes": [ + { + "label": "Solarized Light", + "uiTheme": "vs", + "path": "./themes/solarized-light-color-theme.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-solarized-light/themes/Solarized-light.tmTheme b/extensions/theme-solarized-light/themes/Solarized-light.tmTheme new file mode 100644 index 0000000000..f7f635fe64 --- /dev/null +++ b/extensions/theme-solarized-light/themes/Solarized-light.tmTheme @@ -0,0 +1,434 @@ + + + + + name + Solarized (light) + settings + + + settings + + background + #FDF6E3 + caret + #000000 + foreground + #586E75 + invisibles + #93A1A180 + lineHighlight + #EEE8D5 + selection + #073642 + + + + name + Comment + scope + comment + settings + + fontStyle + italic + foreground + #93A1A1 + + + + name + String + scope + string + settings + + foreground + #2AA198 + + + + name + Regexp + scope + string.regexp + settings + + foreground + #D30102 + + + + name + Number + scope + constant.numeric + settings + + foreground + #D33682 + + + + name + Variable + scope + variable.language, variable.other + settings + + foreground + #268BD2 + + + + name + Keyword + scope + keyword + settings + + foreground + #859900 + + + + name + Storage + scope + storage + settings + + fontStyle + bold + foreground + #073642 + + + + name + Class name + scope + entity.name.class, entity.name.type + settings + + foreground + #268BD2 + + + + name + Function name + scope + entity.name.function + settings + + foreground + #268BD2 + + + + name + Variable start + scope + punctuation.definition.variable + settings + + foreground + #859900 + + + + name + Embedded code markers + scope + punctuation.section.embedded.begin, punctuation.section.embedded.end + settings + + foreground + #D30102 + + + + name + Built-in constant + scope + constant.language, meta.preprocessor + settings + + foreground + #B58900 + + + + name + Support.construct + scope + support.function.construct, keyword.other.new + settings + + foreground + #D30102 + + + + name + User-defined constant + scope + constant.character, constant.other + settings + + foreground + #CB4B16 + + + + name + Inherited class + scope + entity.other.inherited-class + settings + + + + name + Function argument + scope + variable.parameter + settings + + + + name + Tag name + scope + entity.name.tag + settings + + foreground + #268BD2 + + + + name + Tag start/end + scope + punctuation.definition.tag.begin, punctuation.definition.tag.end + settings + + foreground + #93A1A1 + + + + name + Tag attribute + scope + entity.other.attribute-name + settings + + foreground + #93A1A1 + + + + name + Library function + scope + support.function + settings + + foreground + #268BD2 + + + + name + Continuation + scope + punctuation.separator.continuation + settings + + foreground + #D30102 + + + + name + Library constant + scope + support.constant + settings + + + + name + Library class/type + scope + support.type, support.class + settings + + foreground + #859900 + + + + name + Library Exception + scope + support.type.exception + settings + + foreground + #CB4B16 + + + + name + Library variable + scope + support.other.variable + settings + + + + name + Invalid + scope + invalid + settings + + + + name + diff: header + scope + meta.diff, meta.diff.header + settings + + background + #b58900 + fontStyle + italic + foreground + #E0EDDD + + + + name + diff: deleted + scope + markup.deleted + settings + + background + #eee8d5 + fontStyle + + foreground + #dc322f + + + + name + diff: changed + scope + markup.changed + settings + + background + #eee8d5 + fontStyle + + foreground + #cb4b16 + + + + name + diff: inserted + scope + markup.inserted + settings + + background + #eee8d5 + foreground + #219186 + + + + name + Markup Quote + scope + markup.quote + settings + + foreground + #859900 + + + + name + Markup Lists + scope + markup.list + settings + + foreground + #B58900 + + + + name + Markup Styling + scope + markup.bold, markup.italic + settings + + foreground + #D33682 + + + + name + Markup Inline + scope + markup.inline.raw + settings + + fontStyle + + foreground + #2AA198 + + + + name + Markup Headings + scope + markup.heading + settings + + foreground + #268BD2 + + + + name + Markup Setext Header + scope + markup.heading.setext + settings + + fontStyle + + foreground + #268BD2 + + + + + uuid + 38E819D9-AE02-452F-9231-ECC3B204AFD7 + + diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json new file mode 100644 index 0000000000..3abeb7e749 --- /dev/null +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -0,0 +1,482 @@ +{ + "name": "Solarized (light)", + "tokenColors": [ + { + "settings": { + "background": "#FDF6E3", + "foreground": "#657B83" + } + }, + { + "name": "Comment", + "scope": "comment", + "settings": { + "fontStyle": "italic", + "foreground": "#93A1A1" + } + }, + { + "name": "String", + "scope": "string", + "settings": { + "foreground": "#2AA198" + } + }, + { + "name": "Regexp", + "scope": "string.regexp", + "settings": { + "foreground": "#D30102" + } + }, + { + "name": "Number", + "scope": "constant.numeric", + "settings": { + "foreground": "#D33682" + } + }, + { + "name": "Variable", + "scope": [ + "variable.language", + "variable.other" + ], + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Keyword", + "scope": "keyword", + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Storage", + "scope": "storage", + "settings": { + "fontStyle": "bold", + "foreground": "#073642" + } + }, + { + "name": "Class name", + "scope": [ + "entity.name.class", + "entity.name.type" + ], + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Function name", + "scope": "entity.name.function", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Variable start", + "scope": "punctuation.definition.variable", + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Embedded code markers", + "scope": [ + "punctuation.section.embedded.begin", + "punctuation.section.embedded.end" + ], + "settings": { + "foreground": "#D30102" + } + }, + { + "name": "Built-in constant", + "scope": [ + "constant.language", + "meta.preprocessor" + ], + "settings": { + "foreground": "#B58900" + } + }, + { + "name": "Support.construct", + "scope": [ + "support.function.construct", + "keyword.other.new" + ], + "settings": { + "foreground": "#D30102" + } + }, + { + "name": "User-defined constant", + "scope": [ + "constant.character", + "constant.other" + ], + "settings": { + "foreground": "#CB4B16" + } + }, + { + "name": "Inherited class", + "scope": "entity.other.inherited-class", + "settings": {} + }, + { + "name": "Function argument", + "scope": "variable.parameter", + "settings": {} + }, + { + "name": "Tag name", + "scope": "entity.name.tag", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Tag start/end", + "scope": [ + "punctuation.definition.tag.begin", + "punctuation.definition.tag.end" + ], + "settings": { + "foreground": "#93A1A1" + } + }, + { + "name": "Tag attribute", + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#93A1A1" + } + }, + { + "name": "Library function", + "scope": "support.function", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Continuation", + "scope": "punctuation.separator.continuation", + "settings": { + "foreground": "#D30102" + } + }, + { + "name": "Library constant", + "scope": "support.constant", + "settings": {} + }, + { + "name": "Library class/type", + "scope": [ + "support.type", + "support.class" + ], + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Library Exception", + "scope": "support.type.exception", + "settings": { + "foreground": "#CB4B16" + } + }, + { + "name": "Library variable", + "scope": "support.other.variable", + "settings": {} + }, + { + "name": "Invalid", + "scope": "invalid", + "settings": {} + }, + { + "name": "diff: header", + "scope": [ + "meta.diff", + "meta.diff.header" + ], + "settings": { + "background": "#b58900", + "fontStyle": "italic", + "foreground": "#E0EDDD" + } + }, + { + "name": "diff: deleted", + "scope": "markup.deleted", + "settings": { + "background": "#eee8d5", + "fontStyle": "", + "foreground": "#dc322f" + } + }, + { + "name": "diff: changed", + "scope": "markup.changed", + "settings": { + "background": "#eee8d5", + "fontStyle": "", + "foreground": "#cb4b16" + } + }, + { + "name": "diff: inserted", + "scope": "markup.inserted", + "settings": { + "background": "#eee8d5", + "foreground": "#219186" + } + }, + { + "name": "Markup Quote", + "scope": "markup.quote", + "settings": { + "foreground": "#859900" + } + }, + { + "name": "Markup Lists", + "scope": "markup.list", + "settings": { + "foreground": "#B58900" + } + }, + { + "name": "Markup Styling", + "scope": [ + "markup.bold", + "markup.italic" + ], + "settings": { + "foreground": "#D33682" + } + }, + { + "name": "Markup Inline", + "scope": "markup.inline.raw", + "settings": { + "fontStyle": "", + "foreground": "#2AA198" + } + }, + { + "name": "Markup Headings", + "scope": "markup.heading", + "settings": { + "foreground": "#268BD2" + } + }, + { + "name": "Markup Setext Header", + "scope": "markup.heading.setext", + "settings": { + "fontStyle": "", + "foreground": "#268BD2" + } + } + ], + "colors": { + + // Base + // "foreground": "", + "focusBorder": "#D3AF86", + // "contrastActiveBorder": "", + // "contrastBorder": "", + + // "widget.shadow": "", + + "input.background": "#DDD6C1", + // "input.border": "", + "input.foreground": "#586E75", + "input.placeholderForeground": "#586E75AA", + "inputOption.activeBorder": "#D3AF86", + // "inputValidation.infoBorder": "", + // "inputValidation.infoBackground": "", + // "inputValidation.warningBackground": "", + // "inputValidation.warningBorder": "", + // "inputValidation.errorBackground": "", + // "inputValidation.errorBorder": "", + + "badge.background": "#B58900AA", + "progressBar.background": "#B58900", + + "dropdown.background": "#EEE8D5", + // "dropdown.foreground": "", + "dropdown.border": "#D3AF86", + + "button.background": "#AC9D57", + // "button.foreground": "", + + "selection.background": "#CCC4B0", + + "list.activeSelectionBackground": "#DFCA88", + "list.activeSelectionForeground": "#6C6C6C", + "list.focusBackground": "#DFCA8866", + "list.hoverBackground": "#DFCA8844", + "list.inactiveSelectionBackground": "#D1CBB8", + "list.highlightForeground": "#B58900", + + // "scrollbar.shadow": "", + // "scrollbarSlider.activeBackground": "", + // "scrollbarSlider.background": "", + // "scrollbarSlider.hoverBackground": "", + + // Editor + "editor.background": "#FDF6E3", + // "editor.foreground": "#6688cc", + "editorWidget.background": "#EEE8D5", + "editorCursor.foreground": "#657B83", + "editorWhitespace.foreground": "#586E7580", + "editor.lineHighlightBackground": "#EEE8D5", + "editor.selectionBackground": "#EEE8D5", + // "editorIndentGuide.background": "", + "editorHoverWidget.background": "#CCC4B0", + // "editorHoverWidget.border": "", + // "editorLineNumber.foreground": "", + // "editorMarkerNavigation.background": "", + // "editorMarkerNavigationError.background": "", + // "editorMarkerNavigationWarning.background": "", + // "editorLink.activeForeground": "", + // "editor.findMatchBackground": "", + // "editor.findMatchHighlightBackground": "", + // "editor.findRangeHighlightBackground": "", + // "editor.hoverHighlightBackground": "", + // "editor.inactiveSelectionBackground": "", + // "editor.lineHighlightBorder": "", + // "editor.rangeHighlightBackground": "", + // "editor.selectionHighlightBackground": "", + // "editor.wordHighlightBackground": "", + // "editor.wordHighlightStrongBackground": "", + + // Editor: Suggest Widget + // "editorSuggestWidget.background": "", + // "editorSuggestWidget.border": "", + // "editorSuggestWidget.foreground": "", + // "editorSuggestWidget.highlightForeground": "", + // "editorSuggestWidget.selectedBackground": "", + + // Editor: Peek View + "peekViewResult.background": "#EEE8D5", + // "peekViewResult.lineForeground": "", + // "peekViewResult.selectionBackground": "", + // "peekViewResult.selectionForeground": "", + "peekViewEditor.background": "#FFFBF2", + "peekViewTitle.background": "#EEE8D5", + "peekView.border": "#B58900", + "peekViewEditor.matchHighlightBackground": "#7744AA40", + // "peekViewResult.fileForeground": "", + // "peekViewResult.matchHighlightBackground": "", + // "peekViewTitleLabel.foreground": "", + // "peekViewTitleDescription.foreground": "", + + // Editor: Diff + // "diffEditor.insertedTextBackground": "", + // "diffEditor.insertedTextBorder": "", + // "diffEditor.removedTextBackground": "", + // "diffEditor.removedTextBorder": "", + + // Workbench: Title + "titleBar.activeBackground": "#EEE8D5", + // "titleBar.activeForeground": "", + // "titleBar.inactiveBackground": "", + // "titleBar.inactiveForeground": "", + + // Workbench: Editors + // "editorGroupHeader.noTabsBackground": "", + "editorGroup.border": "#DDD6C1", + "editorGroup.background": "#FFFBF2", + "editorGroup.dropBackground": "#DDD6C1AA", + "editorGroupHeader.tabsBackground": "#D9D2C2", + + // Workbench: Tabs + "tab.border": "#DDD6C1", + "tab.activeBackground": "#FDF6E3", + "tab.inactiveForeground": "#586E75", + "tab.inactiveBackground": "#D3CBB7", + // "tab.activeBackground": "", + // "tab.activeForeground": "", + // "tab.inactiveForeground": "", + + // Workbench: Activity Bar + "activityBar.background": "#DDD6C1", + "activityBar.foreground": "#584c27", + "activityBar.dropBackground": "#EEE8D5", + "activityBarBadge.background": "#B58900", + // "activityBarBadge.foreground": "", + + // Workbench: Panel + // "panel.background": "", + "panel.border": "#DDD6C1", + // "panelTitle.activeBorder": "", + // "panelTitle.activeForeground": "", + // "panelTitle.inactiveForeground": "", + + // Workbench: Side Bar + "sideBar.background": "#EEE8D5", + "sideBarTitle.foreground": "#586E75", + // "sideBarSectionHeader.background": "", + + // Workbench: Status Bar + "statusBar.foreground": "#586E75", + "statusBar.background": "#EEE8D5", + "statusBar.debuggingBackground": "#EEE8D5", + "statusBar.noFolderBackground": "#EEE8D5", + // "statusBar.foreground": "", + "statusBarItem.prominentBackground": "#DDD6C1", + "statusBarItem.prominentHoverBackground": "#DDD6C199", + // "statusBarItem.activeBackground": "", + // "statusBarItem.hoverBackground": "", + + // Workbench: Debug + "debugToolBar.background": "#DDD6C1", + "debugExceptionWidget.background": "#DDD6C1", + "debugExceptionWidget.border": "#AB395B", + + // Workbench: Notifications + "notification.background": "#999178", + // "notification.foreground": "", + + // Workbench: Quick Open + "pickerGroup.border": "#2AA19899", + "pickerGroup.foreground": "#2AA19899", + + // Extensions + "extensionButton.prominentBackground": "#b58900", + "extensionButton.prominentHoverBackground": "#584c27aa", + + // Workbench: Terminal + // Colors sourced from the official palette http://ethanschoonover.com/solarized + "terminal.ansiBlack": "#073642", + "terminal.ansiRed": "#dc322f", + "terminal.ansiGreen": "#859900", + "terminal.ansiYellow": "#b58900", + "terminal.ansiBlue": "#268bd2", + "terminal.ansiMagenta": "#d33682", + "terminal.ansiCyan": "#2aa198", + "terminal.ansiWhite": "#eee8d5", + "terminal.ansiBrightBlack": "#586e75", + "terminal.ansiBrightRed": "#cb4b16", + "terminal.ansiBrightGreen": "#586e75", + "terminal.ansiBrightYellow": "#657b83", + "terminal.ansiBrightBlue": "#839496", + "terminal.ansiBrightMagenta": "#6c71c4", + "terminal.ansiBrightCyan": "#93a1a1", + "terminal.ansiBrightWhite": "#eee8d5" + } +} \ No newline at end of file diff --git a/extensions/theme-tomorrow-night-blue/OSSREADME.json b/extensions/theme-tomorrow-night-blue/OSSREADME.json new file mode 100644 index 0000000000..ddb630bfd9 --- /dev/null +++ b/extensions/theme-tomorrow-night-blue/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "Colorsublime-Themes", + "version": "0.1.0", + "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", + "description": "The themes in this folders are copied from colorsublime.com. <<>>" +}] diff --git a/extensions/theme-tomorrow-night-blue/package.json b/extensions/theme-tomorrow-night-blue/package.json new file mode 100644 index 0000000000..4600cd5914 --- /dev/null +++ b/extensions/theme-tomorrow-night-blue/package.json @@ -0,0 +1,15 @@ +{ + "name": "theme-tomorrow-night-blue", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "themes": [ + { + "label": "Tomorrow Night Blue", + "uiTheme": "vs-dark", + "path": "./themes/tomorrow-night-blue-theme.json" + } + ] + } +} \ No newline at end of file diff --git a/extensions/theme-tomorrow-night-blue/themes/Tomorrow-Night-Blue.tmTheme b/extensions/theme-tomorrow-night-blue/themes/Tomorrow-Night-Blue.tmTheme new file mode 100644 index 0000000000..977629f72a --- /dev/null +++ b/extensions/theme-tomorrow-night-blue/themes/Tomorrow-Night-Blue.tmTheme @@ -0,0 +1,293 @@ + + + + + comment + http://chriskempson.com + name + Tomorrow Night - Blue + settings + + + settings + + background + #002451 + caret + #FFFFFF + foreground + #FFFFFF + invisibles + #404F7D + lineHighlight + #00346E + selection + #003F8E + + + + name + Comment + scope + comment + settings + + foreground + #7285B7 + + + + name + Foreground, Operator + scope + keyword.operator.class, keyword.operator, constant.other, source.php.embedded.line + settings + + fontStyle + + foreground + #FFFFFF + + + + name + Variable, String Link, Regular Expression, Tag Name, GitGutter deleted + scope + variable, support.other.variable, string.other.link, string.regexp, entity.name.tag, entity.other.attribute-name, meta.tag, declaration.tag, markup.deleted.git_gutter + settings + + foreground + #FF9DA4 + + + + name + Number, Constant, Function Argument, Tag Attribute, Embedded + scope + constant.numeric, constant.language, support.constant, constant.character, variable.parameter, punctuation.section.embedded, keyword.other.unit + settings + + fontStyle + + foreground + #FFC58F + + + + name + Class, Support + scope + entity.name.class, entity.name.type, support.type, support.class + settings + + fontStyle + + foreground + #FFEEAD + + + + name + String, Symbols, Inherited Class, Markup Heading, GitGutter inserted + scope + string, constant.other.symbol, entity.other.inherited-class, markup.heading, markup.inserted.git_gutter + settings + + fontStyle + + foreground + #D1F1A9 + + + + name + Operator, Misc + scope + keyword.operator, constant.other.color + settings + + foreground + #99FFFF + + + + name + Function, Special Method, Block Level, GitGutter changed + scope + entity.name.function, meta.function-call, support.function, keyword.other.special-method, meta.block-level, markup.changed.git_gutter + settings + + fontStyle + + foreground + #BBDAFF + + + + name + Keyword, Storage + scope + keyword, storage, storage.type, entity.name.tag.css + settings + + fontStyle + + foreground + #EBBBFF + + + + name + Invalid + scope + invalid + settings + + background + #F99DA5 + fontStyle + + foreground + #FFFFFF + + + + name + Separator + scope + meta.separator + settings + + background + #BBDAFE + foreground + #FFFFFF + + + + name + Deprecated + scope + invalid.deprecated + settings + + background + #EBBBFF + fontStyle + + foreground + #FFFFFF + + + + name + Diff foreground + scope + markup.inserted.diff, markup.deleted.diff, meta.diff.header.to-file, meta.diff.header.from-file + settings + + foreground + #FFFFFF + + + + name + Diff insertion + scope + markup.inserted.diff, meta.diff.header.to-file + settings + + foreground + #718c00 + + + + name + Diff deletion + scope + markup.deleted.diff, meta.diff.header.from-file + settings + + foreground + #c82829 + + + + name + Diff header + scope + meta.diff.header.from-file, meta.diff.header.to-file + settings + + foreground + #FFFFFF + background + #4271ae + + + + name + Diff range + scope + meta.diff.range + settings + + fontStyle + italic + foreground + #3e999f + + + + + name + Markup Quote + scope + markup.quote + settings + + foreground + #FFC58F + + + + name + Markup Lists + scope + markup.list + settings + + foreground + #BBDAFF + + + + name + Markup Styling + scope + markup.bold, markup.italic + settings + + foreground + #FFC58F + + + + name + Markup Inline + scope + markup.inline.raw + settings + + fontStyle + + foreground + #FF9DA4 + + + + + uuid + 3F4BB232-3C3A-4396-99C0-06A9573715E9 + + \ No newline at end of file diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json new file mode 100644 index 0000000000..830caf27cb --- /dev/null +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json @@ -0,0 +1,249 @@ +{ + "type": "dark", + "colors": { + "focusBorder": "#bbdaff", + "errorForeground": "#a92049", + "input.background": "#001733", + "dropdown.background": "#001733", + "list.focusBackground": "#ffffff60", + "list.activeSelectionBackground": "#ffffff60", + "list.inactiveSelectionBackground": "#ffffff40", + "list.hoverBackground": "#ffffff30", + "list.highlightForeground": "#bbdaff", + "pickerGroup.foreground": "#bbdaff", + "editor.background": "#002451", + "editor.foreground": "#ffffff", + "editor.selectionBackground": "#003f8e", + "editor.lineHighlightBackground": "#00346e", + "editorCursor.foreground": "#ffffff", + "editorWhitespace.foreground": "#404f7d", + "editorWidget.background": "#001c40", + "editorHoverWidget.background": "#001c40", + "editorHoverWidget.border": "#ffffff44", + "editorGroup.border": "#404f7d", + "editorGroupHeader.tabsBackground": "#001733", + "editorGroup.background": "#1c1c2a", + "editorGroup.dropBackground": "#25375daa", + "peekViewResult.background": "#001c40", + "tab.inactiveBackground": "#001c40", + "debugToolBar.background": "#001c40", + "titleBar.activeBackground": "#001126", + "statusBar.background": "#001126", + "statusBar.noFolderBackground": "#001126", + "statusBar.debuggingBackground": "#001126", + "activityBar.background": "#001733", + "progressBar.background": "#bbdaffcc", + "badge.background": "#bbdaffcc", + "badge.foreground": "#001733", + "sideBar.background": "#001c40", + "terminal.ansiBlack": "#111111", + "terminal.ansiRed": "#ff9da4", + "terminal.ansiGreen": "#d1f1a9", + "terminal.ansiYellow": "#ffeead", + "terminal.ansiBlue": "#bbdaff", + "terminal.ansiMagenta": "#ebbbff", + "terminal.ansiCyan": "#99ffff", + "terminal.ansiWhite": "#cccccc", + "terminal.ansiBrightBlack": "#333333", + "terminal.ansiBrightRed": "#ff7882", + "terminal.ansiBrightGreen": "#b8f171", + "terminal.ansiBrightYellow": "#ffe580", + "terminal.ansiBrightBlue": "#80baff", + "terminal.ansiBrightMagenta": "#d778ff", + "terminal.ansiBrightCyan": "#78ffff", + "terminal.ansiBrightWhite": "#ffffff" + }, + "tokenColors": [ + { + "settings": { + "background": "#002451", + "foreground": "#FFFFFF" + } + }, + { + "name": "Comment", + "scope": "comment", + "settings": { + "foreground": "#7285B7" + } + }, + { + "name": "Foreground, Operator", + "scope": "keyword.operator.class, keyword.operator, constant.other, source.php.embedded.line", + "settings": { + "fontStyle": "", + "foreground": "#FFFFFF" + } + }, + { + "name": "Variable, String Link, Regular Expression, Tag Name, GitGutter deleted", + "scope": "variable, support.other.variable, string.other.link, string.regexp, entity.name.tag, entity.other.attribute-name, meta.tag, declaration.tag, markup.deleted.git_gutter", + "settings": { + "foreground": "#FF9DA4" + } + }, + { + "name": "Number, Constant, Function Argument, Tag Attribute, Embedded", + "scope": "constant.numeric, constant.language, support.constant, constant.character, variable.parameter, punctuation.section.embedded, keyword.other.unit", + "settings": { + "fontStyle": "", + "foreground": "#FFC58F" + } + }, + { + "name": "Class, Support", + "scope": "entity.name.class, entity.name.type, support.type, support.class", + "settings": { + "fontStyle": "", + "foreground": "#FFEEAD" + } + }, + { + "name": "String, Symbols, Inherited Class, Markup Heading, GitGutter inserted", + "scope": "string, constant.other.symbol, entity.other.inherited-class, markup.heading, markup.inserted.git_gutter", + "settings": { + "fontStyle": "", + "foreground": "#D1F1A9" + } + }, + { + "name": "Operator, Misc", + "scope": "keyword.operator, constant.other.color", + "settings": { + "foreground": "#99FFFF" + } + }, + { + "name": "Function, Special Method, Block Level, GitGutter changed", + "scope": "entity.name.function, meta.function-call, support.function, keyword.other.special-method, meta.block-level, markup.changed.git_gutter", + "settings": { + "fontStyle": "", + "foreground": "#BBDAFF" + } + }, + { + "name": "Keyword, Storage", + "scope": "keyword, storage, storage.type, entity.name.tag.css", + "settings": { + "fontStyle": "", + "foreground": "#EBBBFF" + } + }, + { + "name": "Invalid", + "scope": "invalid", + "settings": { + "background": "#F99DA5", + "fontStyle": "", + "foreground": "#FFFFFF" + } + }, + { + "name": "Separator", + "scope": "meta.separator", + "settings": { + "background": "#BBDAFE", + "foreground": "#FFFFFF" + } + }, + { + "name": "Deprecated", + "scope": "invalid.deprecated", + "settings": { + "background": "#EBBBFF", + "fontStyle": "", + "foreground": "#FFFFFF" + } + }, + { + "name": "Diff foreground", + "scope": "markup.inserted.diff, markup.deleted.diff, meta.diff.header.to-file, meta.diff.header.from-file", + "settings": { + "foreground": "#FFFFFF" + } + }, + { + "name": "Diff insertion", + "scope": "markup.inserted.diff, meta.diff.header.to-file", + "settings": { + "foreground": "#718c00" + } + }, + { + "name": "Diff deletion", + "scope": "markup.deleted.diff, meta.diff.header.from-file", + "settings": { + "foreground": "#c82829" + } + }, + { + "name": "Diff header", + "scope": "meta.diff.header.from-file, meta.diff.header.to-file", + "settings": { + "foreground": "#FFFFFF", + "background": "#4271ae" + } + }, + { + "name": "Diff range", + "scope": "meta.diff.range", + "settings": { + "fontStyle": "italic", + "foreground": "#3e999f" + } + }, + { + "name": "Markup Quote", + "scope": "markup.quote", + "settings": { + "foreground": "#FFC58F" + } + }, + { + "name": "Markup Lists", + "scope": "markup.list", + "settings": { + "foreground": "#BBDAFF" + } + }, + { + "name": "Markup Styling", + "scope": "markup.bold, markup.italic", + "settings": { + "foreground": "#FFC58F" + } + }, + { + "name": "Markup Inline", + "scope": "markup.inline.raw", + "settings": { + "fontStyle": "", + "foreground": "#FF9DA4" + } + }, + { + "scope": "token.info-token", + "settings": { + "foreground": "#6796e6" + } + }, + { + "scope": "token.warn-token", + "settings": { + "foreground": "#cd9731" + } + }, + { + "scope": "token.error-token", + "settings": { + "foreground": "#f44747" + } + }, + { + "scope": "token.debug-token", + "settings": { + "foreground": "#b267e6" + } + } + ] +} \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/.gitignore b/extensions/vscode-colorize-tests/.gitignore new file mode 100644 index 0000000000..8e5962ee72 --- /dev/null +++ b/extensions/vscode-colorize-tests/.gitignore @@ -0,0 +1,2 @@ +out +node_modules \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/.vscode/launch.json b/extensions/vscode-colorize-tests/.vscode/launch.json new file mode 100644 index 0000000000..76d6fa6986 --- /dev/null +++ b/extensions/vscode-colorize-tests/.vscode/launch.json @@ -0,0 +1,17 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +{ + "version": "0.1.0", + "configurations": [ + { + "name": "Launch Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["${workspaceRoot}/../../", "${workspaceRoot}/test", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out" ], + "stopOnEntry": false, + "sourceMaps": true, + "outDir": "${workspaceRoot}/out", + "preLaunchTask": "npm" + } + ] +} \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/.vscode/tasks.json b/extensions/vscode-colorize-tests/.vscode/tasks.json new file mode 100644 index 0000000000..97e40acf63 --- /dev/null +++ b/extensions/vscode-colorize-tests/.vscode/tasks.json @@ -0,0 +1,31 @@ +// Available variables which can be used inside of strings. +// ${workspaceRoot}: the root folder of the team +// ${file}: the current opened file +// ${relativeFile}: the current opened file relative to cwd +// ${fileBasename}: the current opened file's basename +// ${fileDirname}: the current opened file's dirname +// ${fileExtname}: the current opened file's extension +// ${cwd}: the current working directory of the spawned process + +// A task runner that calls a custom npm script that compiles the extension. +{ + "version": "0.1.0", + + // we want to run npm + "command": "npm", + + // the command is a shell script + "isShellCommand": true, + + // show the output window only if unrecognized errors occur. + "showOutput": "silent", + + // we run the custom script "compile" as defined in package.json + "args": ["run", "compile", "--loglevel", "silent"], + + // The tsc compiler is started in watching mode + "isWatching": true, + + // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "problemMatcher": "$tsc-watch" +} \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json new file mode 100644 index 0000000000..2e886b6718 --- /dev/null +++ b/extensions/vscode-colorize-tests/package.json @@ -0,0 +1,19 @@ +{ + "name": "vscode-colorize-tests", + "description": "Colorize tests for VS Code", + "version": "0.0.1", + "publisher": "vscode", + "private": true, + "engines": { + "vscode": "*" + }, + "scripts": { + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json", + "postinstall": "node ./node_modules/vscode/bin/install" + }, + "devDependencies": { + "@types/mocha": "^2.2.38", + "@types/node": "^7.0.4", + "vscode": "1.0.1" + } +} \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/src/colorizer.test.ts b/extensions/vscode-colorize-tests/src/colorizer.test.ts new file mode 100644 index 0000000000..8e1cfd4cfb --- /dev/null +++ b/extensions/vscode-colorize-tests/src/colorizer.test.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { commands, Uri } from 'vscode'; +import { join, basename, normalize, dirname } from 'path'; +import * as fs from 'fs'; + +function assertUnchangedTokens(testFixurePath: string, done) { + let fileName = basename(testFixurePath); + + return commands.executeCommand('_workbench.captureSyntaxTokens', Uri.file(testFixurePath)).then(data => { + try { + let resultsFolderPath = join(dirname(dirname(testFixurePath)), 'colorize-results'); + if (!fs.existsSync(resultsFolderPath)) { + fs.mkdirSync(resultsFolderPath); + } + let resultPath = join(resultsFolderPath, fileName.replace('.', '_') + '.json'); + if (fs.existsSync(resultPath)) { + let previousData = JSON.parse(fs.readFileSync(resultPath).toString()); + try { + assert.deepEqual(data, previousData); + } catch (e) { + fs.writeFileSync(resultPath, JSON.stringify(data, null, '\t'), { flag: 'w' }); + if (Array.isArray(data) && Array.isArray(previousData) && data.length === previousData.length) { + for (let i= 0; i < data.length; i++) { + let d = data[i]; + let p = previousData[i]; + if (d.c !== p.c || hasThemeChange(d.r, p.r)) { + throw e; + } + } + // different but no tokenization ot color change: no failure + } else { + throw e; + } + } + } else { + fs.writeFileSync(resultPath, JSON.stringify(data, null, '\t')); + } + done(); + } catch (e) { + done(e); + } + }, done); +} + +function hasThemeChange(d: any, p: any) : boolean { + let keys = Object.keys(d); + for (let key of keys) { + if (d[key] !== p[key]) { + return true; + } + } + return false; +}; + +suite('colorization', () => { + let extensionsFolder = normalize(join(__dirname, '../../')); + let extensions = fs.readdirSync(extensionsFolder); + extensions.forEach(extension => { + let extensionColorizeFixturePath = join(extensionsFolder, extension, 'test', 'colorize-fixtures'); + if (fs.existsSync(extensionColorizeFixturePath)) { + let fixturesFiles = fs.readdirSync(extensionColorizeFixturePath); + fixturesFiles.forEach(fixturesFile => { + // define a test for each fixture + test(extension + '-' + fixturesFile, function (done) { + assertUnchangedTokens(join(extensionColorizeFixturePath, fixturesFile), done); + }); + }); + } + }); +}); diff --git a/extensions/vscode-colorize-tests/src/index.ts b/extensions/vscode-colorize-tests/src/index.ts new file mode 100644 index 0000000000..c4e1ed029d --- /dev/null +++ b/extensions/vscode-colorize-tests/src/index.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// +// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING +// +// This file is providing the test runner to use when running extension tests. +// By default the test runner in use is Mocha based. +// +// You can provide your own test runner if you want to override it by exporting +// a function run(testRoot: string, clb: (error:Error) => void) that the extension +// host can call to run the tests. The test runner is expected to use console.log +// to report the results back to the caller. When the tests are finished, return +// a possible error to the callback or null if none. + +const testRunner = require('vscode/lib/testrunner'); + +// You can directly control Mocha options by uncommenting the following lines +// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info +testRunner.configure({ + ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) + useColors: process.platform !== 'win32', // colored output from test results (only windows cannot handle) + timeout: 60000 +}); + +export = testRunner; \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/src/typings/ref.d.ts b/extensions/vscode-colorize-tests/src/typings/ref.d.ts new file mode 100644 index 0000000000..d31d666b28 --- /dev/null +++ b/extensions/vscode-colorize-tests/src/typings/ref.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions/vscode-colorize-tests/tsconfig.json b/extensions/vscode-colorize-tests/tsconfig.json new file mode 100644 index 0000000000..366e25ea09 --- /dev/null +++ b/extensions/vscode-colorize-tests/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES5", + "outDir": "out", + "lib": [ + "es2015" + ], + "sourceMap": true + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/xml/.vscodeignore b/extensions/xml/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/xml/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/xml/OSSREADME.json b/extensions/xml/OSSREADME.json new file mode 100644 index 0000000000..accbe07495 --- /dev/null +++ b/extensions/xml/OSSREADME.json @@ -0,0 +1,10 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "atom/language-xml", + "version": "0.0.0", + "license": "MIT", + "repositoryURL": "https://github.com/atom/language-xml", + "description": "The files syntaxes/xml.json and syntaxes/xsl.json were derived from the Atom package https://github.com/atom/language-xml which were originally converted from the TextMate bundle https://github.com/textmate/xml.tmbundle." + +}] diff --git a/extensions/xml/package.json b/extensions/xml/package.json new file mode 100644 index 0000000000..c9210274b2 --- /dev/null +++ b/extensions/xml/package.json @@ -0,0 +1,93 @@ +{ + "name": "xml", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "contributes": { + "languages": [{ + "id": "xml", + "extensions": [ + ".xml", + ".xsd", + ".ascx", + ".atom", + ".axml", + ".bpmn", + ".config", + ".cpt", + ".csl", + ".csproj", + ".csproj.user", + ".dita", + ".ditamap", + ".dtd", + ".dtml", + ".fsproj", + ".fxml", + ".iml", + ".isml", + ".jmx", + ".launch", + ".menu", + ".mxml", + ".nuspec", + ".opml", + ".owl", + ".proj", + ".props", + ".pt", + ".publishsettings", + ".pubxml", + ".pubxml.user", + ".rdf", + ".rng", + ".rss", + ".shproj", + ".storyboard", + ".svg", + ".targets", + ".tld", + ".tmx", + ".vbproj", + ".vbproj.user", + ".vcxproj", + ".vcxproj.filters", + ".wsdl", + ".wxi", + ".wxl", + ".wxs", + ".xaml", + ".xbl", + ".xib", + ".xlf", + ".xliff", + ".xpdl", + ".xul", + ".xoml" + ], + "firstLine" : "(\\<\\?xml.*)|(\\\n)\n|\n# Modeline\n(?i:\n # Emacs\n -\\*-(?:\\s*(?=[^:;\\s]+\\s*-\\*-)|(?:.*?[;\\s]|(?<=-\\*-))mode\\s*:\\s*)\n xml\n (?=[\\s;]|(?]?\\d+|m)?|\\sex)(?=:(?=\\s*set?\\s[^\\n:]+:)|:(?!\\s*set?\\s))(?:(?:\\s|\\s*:\\s*)\\w*(?:\\s*=(?:[^\\n\\\\\\s]|\\\\.)*)?)*[\\s:](?:filetype|ft|syntax)\\s*=\n xml\n (?=\\s|:|$)\n)", + "patterns": [ + { + "begin": "(<\\?)\\s*([-_a-zA-Z0-9]+)", + "captures": { + "1": { + "name": "punctuation.definition.tag.xml" + }, + "2": { + "name": "entity.name.tag.xml" + } + }, + "end": "(\\?>)", + "name": "meta.tag.preprocessor.xml", + "patterns": [ + { + "match": " ([a-zA-Z-]+)", + "name": "entity.other.attribute-name.xml" + }, + { + "include": "#doublequotedString" + }, + { + "include": "#singlequotedString" + } + ] + }, + { + "begin": "()", + "name": "meta.tag.sgml.doctype.xml", + "patterns": [ + { + "include": "#internalSubset" + } + ] + }, + { + "include": "#comments" + }, + { + "begin": "(<)((?:([-_a-zA-Z0-9]+)(:))?([-_a-zA-Z0-9:]+))(?=(\\s[^>]*)?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.xml" + }, + "2": { + "name": "entity.name.tag.xml" + }, + "3": { + "name": "entity.name.tag.namespace.xml" + }, + "4": { + "name": "punctuation.separator.namespace.xml" + }, + "5": { + "name": "entity.name.tag.localname.xml" + } + }, + "end": "(>)()", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.xml" + }, + "2": { + "name": "punctuation.definition.tag.xml" + }, + "3": { + "name": "entity.name.tag.xml" + }, + "4": { + "name": "entity.name.tag.namespace.xml" + }, + "5": { + "name": "punctuation.separator.namespace.xml" + }, + "6": { + "name": "entity.name.tag.localname.xml" + }, + "7": { + "name": "punctuation.definition.tag.xml" + } + }, + "name": "meta.tag.no-content.xml", + "patterns": [ + { + "include": "#tagStuff" + } + ] + }, + { + "begin": "()", + "name": "meta.tag.xml", + "patterns": [ + { + "include": "#tagStuff" + } + ] + }, + { + "include": "#entity" + }, + { + "include": "#bare-ampersand" + }, + { + "begin": "<%@", + "beginCaptures": { + "0": { + "name": "punctuation.section.embedded.begin.xml" + } + }, + "end": "%>", + "endCaptures": { + "0": { + "name": "punctuation.section.embedded.end.xml" + } + }, + "name": "source.java-props.embedded.xml", + "patterns": [ + { + "match": "page|include|taglib", + "name": "keyword.other.page-props.xml" + } + ] + }, + { + "begin": "<%[!=]?(?!--)", + "beginCaptures": { + "0": { + "name": "punctuation.section.embedded.begin.xml" + } + }, + "end": "(?!--)%>", + "endCaptures": { + "0": { + "name": "punctuation.section.embedded.end.xml" + } + }, + "name": "source.java.embedded.xml", + "patterns": [ + { + "include": "source.java" + } + ] + }, + { + "begin": "", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.xml" + } + }, + "name": "string.unquoted.cdata.xml" + } + ], + "repository": { + "EntityDecl": { + "begin": "()", + "patterns": [ + { + "include": "#doublequotedString" + }, + { + "include": "#singlequotedString" + } + ] + }, + "bare-ampersand": { + "match": "&", + "name": "invalid.illegal.bad-ampersand.xml" + }, + "doublequotedString": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.xml" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.xml" + } + }, + "name": "string.quoted.double.xml", + "patterns": [ + { + "include": "#entity" + }, + { + "include": "#bare-ampersand" + } + ] + }, + "entity": { + "captures": { + "1": { + "name": "punctuation.definition.constant.xml" + }, + "3": { + "name": "punctuation.definition.constant.xml" + } + }, + "match": "(&)([:a-zA-Z_][:a-zA-Z0-9_.-]*|#[0-9]+|#x[0-9a-fA-F]+)(;)", + "name": "constant.character.entity.xml" + }, + "internalSubset": { + "begin": "(\\[)", + "captures": { + "1": { + "name": "punctuation.definition.constant.xml" + } + }, + "end": "(\\])", + "name": "meta.internalsubset.xml", + "patterns": [ + { + "include": "#EntityDecl" + }, + { + "include": "#parameterEntity" + }, + { + "include": "#comments" + } + ] + }, + "parameterEntity": { + "captures": { + "1": { + "name": "punctuation.definition.constant.xml" + }, + "3": { + "name": "punctuation.definition.constant.xml" + } + }, + "match": "(%)([:a-zA-Z_][:a-zA-Z0-9_.-]*)(;)", + "name": "constant.character.parameter-entity.xml" + }, + "singlequotedString": { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.xml" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.xml" + } + }, + "name": "string.quoted.single.xml", + "patterns": [ + { + "include": "#entity" + }, + { + "include": "#bare-ampersand" + } + ] + }, + "tagStuff": { + "patterns": [ + { + "captures": { + "1": { + "name": "entity.other.attribute-name.namespace.xml" + }, + "2": { + "name": "entity.other.attribute-name.xml" + }, + "3": { + "name": "punctuation.separator.namespace.xml" + }, + "4": { + "name": "entity.other.attribute-name.localname.xml" + } + }, + "match": "(?:^|\\s+)(?:([-\\w.]+)((:)))?([-\\w.:]+)\\s*=" + }, + { + "include": "#doublequotedString" + }, + { + "include": "#singlequotedString" + } + ] + }, + "comments": { + "begin": "<[!%]--", + "captures": { + "0": { + "name": "punctuation.definition.comment.xml" + } + }, + "end": "--%?>", + "name": "comment.block.xml" + } + } +} \ No newline at end of file diff --git a/extensions/xml/syntaxes/xsl.json b/extensions/xml/syntaxes/xsl.json new file mode 100644 index 0000000000..116fe42ea4 --- /dev/null +++ b/extensions/xml/syntaxes/xsl.json @@ -0,0 +1,98 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/atom/language-xml/blob/master/grammars/xsl.cson", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/atom/language-xml/commit/507de2ee7daca60cf02e9e21fbeb92bbae73e280", + "scopeName": "text.xml.xsl", + "name": "XSL", + "fileTypes": [ + "xsl", + "xslt" + ], + "patterns": [ + { + "begin": "(<)(xsl)((:))(template)", + "captures": { + "1": { + "name": "punctuation.definition.tag.xml" + }, + "2": { + "name": "entity.name.tag.namespace.xml" + }, + "3": { + "name": "entity.name.tag.xml" + }, + "4": { + "name": "punctuation.separator.namespace.xml" + }, + "5": { + "name": "entity.name.tag.localname.xml" + } + }, + "end": "(>)", + "name": "meta.tag.xml.template", + "patterns": [ + { + "captures": { + "1": { + "name": "entity.other.attribute-name.namespace.xml" + }, + "2": { + "name": "entity.other.attribute-name.xml" + }, + "3": { + "name": "punctuation.separator.namespace.xml" + }, + "4": { + "name": "entity.other.attribute-name.localname.xml" + } + }, + "match": " (?:([-_a-zA-Z0-9]+)((:)))?([a-zA-Z-]+)" + }, + { + "include": "#doublequotedString" + }, + { + "include": "#singlequotedString" + } + ] + }, + { + "include": "text.xml" + } + ], + "repository": { + "doublequotedString": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.xml" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.xml" + } + }, + "name": "string.quoted.double.xml" + }, + "singlequotedString": { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.xml" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.xml" + } + }, + "name": "string.quoted.single.xml" + } + } +} \ No newline at end of file diff --git a/extensions/xml/test/colorize-fixtures/test-7115.xml b/extensions/xml/test/colorize-fixtures/test-7115.xml new file mode 100644 index 0000000000..74bf37e903 --- /dev/null +++ b/extensions/xml/test/colorize-fixtures/test-7115.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/extensions/xml/test/colorize-fixtures/test.xml b/extensions/xml/test/colorize-fixtures/test.xml new file mode 100644 index 0000000000..af12b7ea57 --- /dev/null +++ b/extensions/xml/test/colorize-fixtures/test.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + Lucy + 1952-03-03 + bossy, crabby and selfish + + + + + + diff --git a/extensions/xml/test/colorize-results/test-7115_xml.json b/extensions/xml/test/colorize-results/test-7115_xml.json new file mode 100644 index 0000000000..5a14d78ce7 --- /dev/null +++ b/extensions/xml/test/colorize-results/test-7115_xml.json @@ -0,0 +1,585 @@ +[ + { + "c": "", + "t": "text.xml meta.tag.preprocessor.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "WorkFine", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "NoColorWithNonLatinCharacters_АБВ", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NextTagnotWork", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "something", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Поле", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "tagnotwork", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "WorkFine", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": "/>", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "Error_АБВГД", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": "/>", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + } +] \ No newline at end of file diff --git a/extensions/xml/test/colorize-results/test_xml.json b/extensions/xml/test/colorize-results/test_xml.json new file mode 100644 index 0000000000..80525b2917 --- /dev/null +++ b/extensions/xml/test/colorize-results/test_xml.json @@ -0,0 +1,1806 @@ +[ + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "project", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "target", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "name", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "jar", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "mkdir", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "dir", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "build/jar", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "/>", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "jar", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "destfile", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "build/jar/HelloWorld.jar", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "basedir", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "build/classes", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "manifest", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "attribute", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "name", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Main-Class", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "value", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "oata.HelloWorld", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "/>", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.xml comment.block.xml punctuation.definition.comment.xml", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "character", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "id", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Lucy", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "hr", + "t": "text.xml meta.tag.xml entity.name.tag.namespace.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "text.xml meta.tag.xml entity.name.tag.xml punctuation.separator.namespace.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": "name", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "Lucy", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "hr", + "t": "text.xml meta.tag.xml entity.name.tag.namespace.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "text.xml meta.tag.xml entity.name.tag.xml punctuation.separator.namespace.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": "born", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "1952-03-03", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "qualification", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "bossy, crabby and selfish", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "VisualState.Setters", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "Setter", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Target", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "inputPanel.Orientation", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Value", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Vertical", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "/>", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "Setter", + "t": "text.xml meta.tag.xml entity.name.tag.localname.xml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Target", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "inputButton.Margin", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Value", + "t": "text.xml meta.tag.xml entity.other.attribute-name.localname.xml", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": "=", + "t": "text.xml meta.tag.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.begin.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "0,4,0,0", + "t": "text.xml meta.tag.xml string.quoted.double.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.xml meta.tag.xml string.quoted.double.xml punctuation.definition.string.end.xml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.xml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.xml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "/>", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": " ", + "t": "text.xml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "", + "t": "text.xml meta.tag.xml punctuation.definition.tag.xml", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + } +] \ No newline at end of file diff --git a/extensions/xml/xml.language-configuration.json b/extensions/xml/xml.language-configuration.json new file mode 100644 index 0000000000..faaa59c1cd --- /dev/null +++ b/extensions/xml/xml.language-configuration.json @@ -0,0 +1,31 @@ +{ + "comments": { + "lineComment": "", + "blockComment": [""] + }, + "brackets": [ + ["<", ">"] + ], + "autoClosingPairs": [ + ["<", ">"], + ["'", "'"], + ["\"", "\""] + ], + "surroundingPairs": [ + ["<", ">"], + ["'", "'"], + ["\"", "\""] + ] + + // enhancedBrackets: [{ + // tokenType: 'tag.tag-$1.xml', + // openTrigger: '>', + // open: /<(\w[\w\d]*)([^\/>]*(?!\/)>)[^<>]*$/i, + // closeComplete: '', + // closeTrigger: '>', + // close: /<\/(\w[\w\d]*)\s*>$/i + // }], + + // autoClosingPairs: [['\'', '\''], ['"', '"'] ] + +} diff --git a/extensions/xml/xsl.language-configuration.json b/extensions/xml/xsl.language-configuration.json new file mode 100644 index 0000000000..7605fc4a7d --- /dev/null +++ b/extensions/xml/xsl.language-configuration.json @@ -0,0 +1,20 @@ +{ + "comments": { + "lineComment": "", + "blockComment": [""] + }, + "brackets": [ + ["<", ">"] + ] + + // enhancedBrackets: [{ + // tokenType: 'tag.tag-$1.xml', + // openTrigger: '>', + // open: /<(\w[\w\d]*)([^\/>]*(?!\/)>)[^<>]*$/i, + // closeComplete: '', + // closeTrigger: '>', + // close: /<\/(\w[\w\d]*)\s*>$/i + // }], + + // autoClosingPairs: [['\'', '\''], ['"', '"'] ] +} diff --git a/extensions/yaml/.gitignore b/extensions/yaml/.gitignore new file mode 100644 index 0000000000..3c3629e647 --- /dev/null +++ b/extensions/yaml/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/extensions/yaml/.vscodeignore b/extensions/yaml/.vscodeignore new file mode 100644 index 0000000000..77ab386fc7 --- /dev/null +++ b/extensions/yaml/.vscodeignore @@ -0,0 +1 @@ +test/** \ No newline at end of file diff --git a/extensions/yaml/OSSREADME.json b/extensions/yaml/OSSREADME.json new file mode 100644 index 0000000000..bcdae2a4c8 --- /dev/null +++ b/extensions/yaml/OSSREADME.json @@ -0,0 +1,28 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "textmate/yaml.tmbundle", + "version": "0.0.0", + "license": "TextMate Bundle License", + "repositoryURL": "https://github.com/textmate/yaml.tmbundle", + "licenseDetail": [ + "Copyright (c) 2015 FichteFoll ", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] +}] diff --git a/extensions/yaml/language-configuration.json b/extensions/yaml/language-configuration.json new file mode 100644 index 0000000000..cab4f6602f --- /dev/null +++ b/extensions/yaml/language-configuration.json @@ -0,0 +1,24 @@ +{ + "comments": { + "lineComment": "#" + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ] +} \ No newline at end of file diff --git a/extensions/yaml/package.json b/extensions/yaml/package.json new file mode 100644 index 0000000000..ce9cc13b3a --- /dev/null +++ b/extensions/yaml/package.json @@ -0,0 +1,29 @@ +{ + "name": "yaml", + "version": "0.1.0", + "publisher": "vscode", + "engines": { "vscode": "*" }, + "scripts": { + "update-grammar": "node ../../build/npm/update-grammar.js textmate/yaml.tmbundle Syntaxes/YAML.tmLanguage ./syntaxes/yaml.json" + }, + "contributes": { + "languages": [{ + "id": "yaml", + "aliases": ["YAML", "yaml"], + "extensions": [".eyaml", ".eyml", ".yaml", ".yml"], + "firstLine": "^#cloud-config", + "configuration": "./language-configuration.json" + }], + "grammars": [{ + "language": "yaml", + "scopeName": "source.yaml", + "path": "./syntaxes/yaml.json" + }], + "configurationDefaults": { + "[yaml]": { + "editor.insertSpaces": true, + "editor.tabSize": 2 + } + } + } +} \ No newline at end of file diff --git a/extensions/yaml/syntaxes/yaml.json b/extensions/yaml/syntaxes/yaml.json new file mode 100644 index 0000000000..9d755531ed --- /dev/null +++ b/extensions/yaml/syntaxes/yaml.json @@ -0,0 +1,634 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/textmate/yaml.tmbundle/blob/master/Syntaxes/YAML.tmLanguage", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/textmate/yaml.tmbundle/commit/efc96efafe5e48480cf55a2ed124b388cbea4440", + "fileTypes": [ + "yaml", + "yml", + "rviz", + "reek", + "clang-format", + "yaml-tmlanguage", + "syntax", + "sublime-syntax" + ], + "firstLineMatch": "^%YAML( ?1.\\d+)?", + "keyEquivalent": "^~Y", + "name": "YAML", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#property" + }, + { + "include": "#directive" + }, + { + "match": "^---", + "name": "entity.other.document.begin.yaml" + }, + { + "match": "^\\.{3}", + "name": "entity.other.document.end.yaml" + }, + { + "include": "#node" + } + ], + "repository": { + "block-collection": { + "patterns": [ + { + "include": "#block-sequence" + }, + { + "include": "#block-mapping" + } + ] + }, + "block-mapping": { + "patterns": [ + { + "include": "#block-pair" + } + ] + }, + "block-node": { + "patterns": [ + { + "include": "#prototype" + }, + { + "include": "#block-scalar" + }, + { + "include": "#block-collection" + }, + { + "include": "#flow-scalar-plain-out" + }, + { + "include": "#flow-node" + } + ] + }, + "block-pair": { + "patterns": [ + { + "begin": "\\?", + "beginCaptures": { + "1": { + "name": "punctuation.definition.key-value.begin.yaml" + } + }, + "end": "(?=\\?)|^ *(:)|(:)", + "endCaptures": { + "1": { + "name": "punctuation.separator.key-value.mapping.yaml" + }, + "2": { + "name": "invalid.illegal.expected-newline.yaml" + } + }, + "name": "meta.block-mapping.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + { + "begin": "(?x)\n (?=\n (?x:\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n )\n (\n [^\\s:]\n | : \\S\n | \\s+ (?![#\\s])\n )*\n \\s*\n :\n\t\t\t\t\t\t\t(\\s|$)\n )\n ", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", + "patterns": [ + { + "include": "#flow-scalar-plain-out-implicit-type" + }, + { + "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n ", + "beginCaptures": { + "0": { + "name": "entity.name.tag.yaml" + } + }, + "contentName": "entity.name.tag.yaml", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", + "name": "string.unquoted.plain.out.yaml" + } + ] + }, + { + "match": ":(?=\\s|$)", + "name": "punctuation.separator.key-value.mapping.yaml" + } + ] + }, + "block-scalar": { + "begin": "(?:(\\|)|(>))([1-9])?([-+])?(.*\\n?)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.block.scalar.literal.yaml" + }, + "2": { + "name": "punctuation.definition.block.scalar.folded.yaml" + }, + "3": { + "name": "constant.numeric.indentation-indicator.yaml" + }, + "4": { + "name": "support.other.chomping-indicator.yaml" + }, + "5": { + "patterns": [ + { + "include": "#comment" + }, + { + "match": ".+", + "name": "invalid.illegal.expected-comment-or-newline.yaml" + } + ] + } + }, + "end": "^(?=\\S)|(?!\\G)", + "patterns": [ + { + "begin": "^([ ]+)(?! )", + "end": "^(?!\\1|\\s*$)", + "name": "string.unquoted.block.yaml" + } + ] + }, + "block-sequence": { + "match": "(-)( |\\t|$)", + "name": "punctuation.definition.block.sequence.item.yaml" + }, + "comment": { + "begin": "(?:(^[ \\t]*)|[ \\t]+)(?=#\\p{Print}*$)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.yaml" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "#", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.yaml" + } + }, + "end": "\\n", + "name": "comment.line.number-sign.yaml" + } + ] + }, + "directive": { + "begin": "^%", + "beginCaptures": { + "0": { + "name": "punctuation.definition.directive.begin.yaml" + } + }, + "end": "(?=$|[ \\t]+($|#))", + "name": "meta.directive.yaml", + "patterns": [ + { + "captures": { + "1": { + "name": "keyword.other.directive.yaml.yaml" + }, + "2": { + "name": "constant.numeric.yaml-version.yaml" + } + }, + "match": "\\G(YAML)[ \\t]+(\\d+\\.\\d+)" + }, + { + "captures": { + "1": { + "name": "keyword.other.directive.tag.yaml" + }, + "2": { + "name": "storage.type.tag-handle.yaml" + }, + "3": { + "name": "support.type.tag-prefix.yaml" + } + }, + "match": "(?x)\n \\G\n (TAG)\n (?:[ \\t]+\n ((?:!(?:[0-9A-Za-z\\-]*!)?))\n (?:[ \\t]+ (\n ! (?x: %\\p{XDigit}{2} | [0-9A-Za-z\\-#;/?:@&=+$,_.!~*'()\\[\\]] )*\n | (?![,!\\[\\]{}]) (?x: %\\p{XDigit}{2} | [0-9A-Za-z\\-#;/?:@&=+$,_.!~*'()\\[\\]] )+\n )\n )?\n )?\n " + }, + { + "captures": { + "1": { + "name": "support.other.directive.reserved.yaml" + }, + "2": { + "name": "string.unquoted.directive-name.yaml" + }, + "3": { + "name": "string.unquoted.directive-parameter.yaml" + } + }, + "match": "(?x) \\G (\\w+) (?:[ \\t]+ (\\w+) (?:[ \\t]+ (\\w+))? )?" + }, + { + "match": "\\S+", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + }, + "flow-alias": { + "captures": { + "1": { + "name": "keyword.control.flow.alias.yaml" + }, + "2": { + "name": "punctuation.definition.alias.yaml" + }, + "3": { + "name": "variable.other.alias.yaml" + }, + "4": { + "name": "invalid.illegal.character.anchor.yaml" + } + }, + "match": "((\\*))([^\\s\\[\\]/{/},]+)([^\\s\\]},]\\S*)?" + }, + "flow-collection": { + "patterns": [ + { + "include": "#flow-sequence" + }, + { + "include": "#flow-mapping" + } + ] + }, + "flow-mapping": { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.mapping.begin.yaml" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.definition.mapping.end.yaml" + } + }, + "name": "meta.flow-mapping.yaml", + "patterns": [ + { + "include": "#prototype" + }, + { + "match": ",", + "name": "punctuation.separator.mapping.yaml" + }, + { + "include": "#flow-pair" + } + ] + }, + "flow-node": { + "patterns": [ + { + "include": "#prototype" + }, + { + "include": "#flow-alias" + }, + { + "include": "#flow-collection" + }, + { + "include": "#flow-scalar" + } + ] + }, + "flow-pair": { + "patterns": [ + { + "begin": "\\?", + "beginCaptures": { + "0": { + "name": "punctuation.definition.key-value.begin.yaml" + } + }, + "end": "(?=[},\\]])", + "name": "meta.flow-pair.explicit.yaml", + "patterns": [ + { + "include": "#prototype" + }, + { + "include": "#flow-pair" + }, + { + "include": "#flow-node" + }, + { + "begin": ":(?=\\s|$|[\\[\\]{},])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.key-value.mapping.yaml" + } + }, + "end": "(?=[},\\]])", + "patterns": [ + { + "include": "#flow-value" + } + ] + } + ] + }, + { + "begin": "(?x)\n (?=\n (?:\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n )\n (\n [^\\s:[\\[\\]{},]]\n | : [^\\s[\\[\\]{},]]\n | \\s+ (?![#\\s])\n )*\n \\s*\n :\n\t\t\t\t\t\t\t(\\s|$)\n )\n ", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", + "name": "meta.flow-pair.key.yaml", + "patterns": [ + { + "include": "#flow-scalar-plain-in-implicit-type" + }, + { + "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n ", + "beginCaptures": { + "0": { + "name": "entity.name.tag.yaml" + } + }, + "contentName": "entity.name.tag.yaml", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", + "name": "string.unquoted.plain.in.yaml" + } + ] + }, + { + "include": "#flow-node" + }, + { + "begin": ":(?=\\s|$|[\\[\\]{},])", + "captures": { + "0": { + "name": "punctuation.separator.key-value.mapping.yaml" + } + }, + "end": "(?=[},\\]])", + "name": "meta.flow-pair.yaml", + "patterns": [ + { + "include": "#flow-value" + } + ] + } + ] + }, + "flow-scalar": { + "patterns": [ + { + "include": "#flow-scalar-double-quoted" + }, + { + "include": "#flow-scalar-single-quoted" + }, + { + "include": "#flow-scalar-plain-in" + } + ] + }, + "flow-scalar-double-quoted": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "string.quoted.double.yaml", + "patterns": [ + { + "match": "\\\\([0abtnvfre \"/\\\\N_Lp]|x\\d\\d|u\\d{4}|U\\d{8})", + "name": "constant.character.escape.yaml" + }, + { + "match": "\\\\\\n", + "name": "constant.character.escape.double-quoted.newline.yaml" + } + ] + }, + "flow-scalar-plain-in": { + "patterns": [ + { + "include": "#flow-scalar-plain-in-implicit-type" + }, + { + "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n ", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", + "name": "string.unquoted.plain.in.yaml" + } + ] + }, + "flow-scalar-plain-in-implicit-type": { + "patterns": [ + { + "captures": { + "1": { + "name": "constant.language.null.yaml" + }, + "2": { + "name": "constant.language.boolean.yaml" + }, + "3": { + "name": "constant.numeric.integer.yaml" + }, + "4": { + "name": "constant.numeric.float.yaml" + }, + "5": { + "name": "constant.other.timestamp.yaml" + }, + "6": { + "name": "constant.language.value.yaml" + }, + "7": { + "name": "constant.language.merge.yaml" + } + }, + "match": "(?x)\n (?x:\n (null|Null|NULL|~)\n | (y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)\n | (\n (?:\n [-+]? 0b [0-1_]+ # (base 2)\n | [-+]? 0 [0-7_]+ # (base 8)\n | [-+]? (?: 0|[1-9][0-9_]*) # (base 10)\n | [-+]? 0x [0-9a-fA-F_]+ # (base 16)\n | [-+]? [1-9] [0-9_]* (?: :[0-5]?[0-9])+ # (base 60)\n )\n )\n | (\n (?x:\n [-+]? (?: [0-9] [0-9_]*)? \\. [0-9.]* (?: [eE] [-+] [0-9]+)? # (base 10)\n | [-+]? [0-9] [0-9_]* (?: :[0-5]?[0-9])+ \\. [0-9_]* # (base 60)\n | [-+]? \\. (?: inf|Inf|INF) # (infinity)\n | \\. (?: nan|NaN|NAN) # (not a number)\n )\n )\n | (\n (?x:\n \\d{4} - \\d{2} - \\d{2} # (y-m-d)\n | \\d{4} # (year)\n - \\d{1,2} # (month)\n - \\d{1,2} # (day)\n (?: [Tt] | [ \\t]+) \\d{1,2} # (hour)\n : \\d{2} # (minute)\n : \\d{2} # (second)\n (?: \\.\\d*)? # (fraction)\n (?:\n (?:[ \\t]*) Z\n | [-+] \\d{1,2} (?: :\\d{1,2})?\n )? # (time zone)\n )\n )\n | (=)\n | (<<)\n )\n (?:\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n )\n " + } + ] + }, + "flow-scalar-plain-out": { + "patterns": [ + { + "include": "#flow-scalar-plain-out-implicit-type" + }, + { + "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n ", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", + "name": "string.unquoted.plain.out.yaml" + } + ] + }, + "flow-scalar-plain-out-implicit-type": { + "patterns": [ + { + "captures": { + "1": { + "name": "constant.language.null.yaml" + }, + "2": { + "name": "constant.language.boolean.yaml" + }, + "3": { + "name": "constant.numeric.integer.yaml" + }, + "4": { + "name": "constant.numeric.float.yaml" + }, + "5": { + "name": "constant.other.timestamp.yaml" + }, + "6": { + "name": "constant.language.value.yaml" + }, + "7": { + "name": "constant.language.merge.yaml" + } + }, + "match": "(?x)\n (?x:\n (null|Null|NULL|~)\n | (y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)\n | (\n (?:\n [-+]? 0b [0-1_]+ # (base 2)\n | [-+]? 0 [0-7_]+ # (base 8)\n | [-+]? (?: 0|[1-9][0-9_]*) # (base 10)\n | [-+]? 0x [0-9a-fA-F_]+ # (base 16)\n | [-+]? [1-9] [0-9_]* (?: :[0-5]?[0-9])+ # (base 60)\n )\n )\n | (\n (?x:\n [-+]? (?: [0-9] [0-9_]*)? \\. [0-9.]* (?: [eE] [-+] [0-9]+)? # (base 10)\n | [-+]? [0-9] [0-9_]* (?: :[0-5]?[0-9])+ \\. [0-9_]* # (base 60)\n | [-+]? \\. (?: inf|Inf|INF) # (infinity)\n | \\. (?: nan|NaN|NAN) # (not a number)\n )\n )\n | (\n (?x:\n \\d{4} - \\d{2} - \\d{2} # (y-m-d)\n | \\d{4} # (year)\n - \\d{1,2} # (month)\n - \\d{1,2} # (day)\n (?: [Tt] | [ \\t]+) \\d{1,2} # (hour)\n : \\d{2} # (minute)\n : \\d{2} # (second)\n (?: \\.\\d*)? # (fraction)\n (?:\n (?:[ \\t]*) Z\n | [-+] \\d{1,2} (?: :\\d{1,2})?\n )? # (time zone)\n )\n )\n | (=)\n | (<<)\n )\n (?x:\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n )\n " + } + ] + }, + "flow-scalar-single-quoted": { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "end": "'(?!')", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "string.quoted.single.yaml", + "patterns": [ + { + "match": "''", + "name": "constant.character.escape.single-quoted.yaml" + } + ] + }, + "flow-sequence": { + "begin": "\\[", + "beginCaptures": { + "0": { + "name": "punctuation.definition.sequence.begin.yaml" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.definition.sequence.end.yaml" + } + }, + "name": "meta.flow-sequence.yaml", + "patterns": [ + { + "include": "#prototype" + }, + { + "match": ",", + "name": "punctuation.separator.sequence.yaml" + }, + { + "include": "#flow-pair" + }, + { + "include": "#flow-node" + } + ] + }, + "flow-value": { + "patterns": [ + { + "begin": "\\G(?![},\\]])", + "end": "(?=[},\\]])", + "name": "meta.flow-pair.value.yaml", + "patterns": [ + { + "include": "#flow-node" + } + ] + } + ] + }, + "node": { + "patterns": [ + { + "include": "#block-node" + } + ] + }, + "property": { + "begin": "(?=!|&)", + "end": "(?!\\G)", + "name": "meta.property.yaml", + "patterns": [ + { + "captures": { + "1": { + "name": "keyword.control.property.anchor.yaml" + }, + "2": { + "name": "punctuation.definition.anchor.yaml" + }, + "3": { + "name": "entity.name.type.anchor.yaml" + }, + "4": { + "name": "invalid.illegal.character.anchor.yaml" + } + }, + "match": "\\G((&))([^\\s\\[\\]/{/},]+)(\\S+)?" + }, + { + "match": "(?x)\n \\G\n (?:\n ! < (?: %\\p{XDigit}{2} | [0-9A-Za-z\\-#;/?:@&=+$,_.!~*'()\\[\\]] )+ >\n | (?:!(?:[0-9A-Za-z\\-]*!)?) (?: %\\p{XDigit}{2} | [0-9A-Za-z\\-#;/?:@&=+$_.~*'()] )+\n | !\n )\n (?=\\ |\\t|$)\n ", + "name": "storage.type.tag-handle.yaml" + }, + { + "match": "\\S+", + "name": "invalid.illegal.tag-handle.yaml" + } + ] + }, + "prototype": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#property" + } + ] + } + }, + "scopeName": "source.yaml", + "uuid": "686AD6AE-33F3-4493-9512-9E9FC1D5417F" +} \ No newline at end of file diff --git a/extensions/yaml/test/colorize-fixtures/issue-1550.yaml b/extensions/yaml/test/colorize-fixtures/issue-1550.yaml new file mode 100644 index 0000000000..b8a82c8ff6 --- /dev/null +++ b/extensions/yaml/test/colorize-fixtures/issue-1550.yaml @@ -0,0 +1,4 @@ +test1 : dsd +test2 : abc-def +test-3 : abcdef +test-4 : abc-def \ No newline at end of file diff --git a/extensions/yaml/test/colorize-fixtures/issue-4008.yaml b/extensions/yaml/test/colorize-fixtures/issue-4008.yaml new file mode 100644 index 0000000000..da02551f2a --- /dev/null +++ b/extensions/yaml/test/colorize-fixtures/issue-4008.yaml @@ -0,0 +1,4 @@ +- blue: a="brown,not_brown" +- not_blue: foo +- blue: foo="}" +- not_blue: 1 \ No newline at end of file diff --git a/extensions/yaml/test/colorize-fixtures/issue-6303.yaml b/extensions/yaml/test/colorize-fixtures/issue-6303.yaml new file mode 100644 index 0000000000..b325af0da7 --- /dev/null +++ b/extensions/yaml/test/colorize-fixtures/issue-6303.yaml @@ -0,0 +1,9 @@ +swagger: '2.0' +info: + description: 'The API Management Service API defines an updated and refined version + of the concepts currently known as Developer, APP, and API Product in Edge. Of + note is the introduction of the API concept, missing previously from Edge + + ' + title: API Management Service API + version: initial \ No newline at end of file diff --git a/extensions/yaml/test/colorize-fixtures/test.yaml b/extensions/yaml/test/colorize-fixtures/test.yaml new file mode 100644 index 0000000000..7083eed0cc --- /dev/null +++ b/extensions/yaml/test/colorize-fixtures/test.yaml @@ -0,0 +1,18 @@ +# sequencer protocols for Laser eye surgery +--- +- step: &id001 # defines anchor label &id001 + instrument: Lasik 2000 + pulseEnergy: 5.4 + spotSize: 1mm + +- step: *id001 # refers to the first step (with anchor &id001) +- step: *id001 + spotSize: 2mm +- step: *id002 +- {name: John Smith, age: 33} +- name: Mary Smith + age: 27 + men: [John Smith, Bill Jones] +women: + - Mary Smith + - Susan Williams \ No newline at end of file diff --git a/extensions/yaml/test/colorize-results/issue-1550_yaml.json b/extensions/yaml/test/colorize-results/issue-1550_yaml.json new file mode 100644 index 0000000000..79d6d39cc9 --- /dev/null +++ b/extensions/yaml/test/colorize-results/issue-1550_yaml.json @@ -0,0 +1,222 @@ +[ + { + "c": "test1", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "dsd", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "test2", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "abc-def", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "test-3", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "abcdef", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "test-4", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "abc-def", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + } +] \ No newline at end of file diff --git a/extensions/yaml/test/colorize-results/issue-4008_yaml.json b/extensions/yaml/test/colorize-results/issue-4008_yaml.json new file mode 100644 index 0000000000..4039077206 --- /dev/null +++ b/extensions/yaml/test/colorize-results/issue-4008_yaml.json @@ -0,0 +1,222 @@ +[ + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "blue", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "a=\"brown,not_brown\"", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "not_blue", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "foo", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "blue", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "foo=\"}\"", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "not_blue", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.yaml constant.numeric.integer.yaml", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + } +] \ No newline at end of file diff --git a/extensions/yaml/test/colorize-results/issue-6303_yaml.json b/extensions/yaml/test/colorize-results/issue-6303_yaml.json new file mode 100644 index 0000000000..edd710c06e --- /dev/null +++ b/extensions/yaml/test/colorize-results/issue-6303_yaml.json @@ -0,0 +1,310 @@ +[ + { + "c": "swagger", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.begin.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "2.0", + "t": "source.yaml string.quoted.single.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.end.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "info", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "description", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.begin.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "The API Management Service API defines an updated and refined version", + "t": "source.yaml string.quoted.single.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " of the concepts currently known as Developer, APP, and API Product in Edge. Of", + "t": "source.yaml string.quoted.single.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " note is the introduction of the API concept, missing previously from Edge", + "t": "source.yaml string.quoted.single.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.yaml string.quoted.single.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.end.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "title", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "API Management Service API", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "version", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "initial", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + } +] \ No newline at end of file diff --git a/extensions/yaml/test/colorize-results/test_yaml.json b/extensions/yaml/test/colorize-results/test_yaml.json new file mode 100644 index 0000000000..e4e35ba7ee --- /dev/null +++ b/extensions/yaml/test/colorize-results/test_yaml.json @@ -0,0 +1,1047 @@ +[ + { + "c": "#", + "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " sequencer protocols for Laser eye surgery", + "t": "source.yaml comment.line.number-sign.yaml", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "---", + "t": "source.yaml entity.other.document.begin.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "step", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "&", + "t": "source.yaml meta.property.yaml keyword.control.property.anchor.yaml punctuation.definition.anchor.yaml", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "id001", + "t": "source.yaml meta.property.yaml entity.name.type.anchor.yaml", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " defines anchor label &id001", + "t": "source.yaml comment.line.number-sign.yaml", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "instrument", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Lasik 2000", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pulseEnergy", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "5.4", + "t": "source.yaml constant.numeric.float.yaml", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "spotSize", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1mm", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "step", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "id001", + "t": "source.yaml variable.other.alias.yaml", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " refers to the first step (with anchor &id001)", + "t": "source.yaml comment.line.number-sign.yaml", + "r": { + "dark_plus": "comment: #608B4E", + "light_plus": "comment: #008000", + "dark_vs": "comment: #608B4E", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "step", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "id001", + "t": "source.yaml variable.other.alias.yaml", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "spotSize", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "2mm", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "step", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "id002", + "t": "source.yaml variable.other.alias.yaml", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.yaml meta.flow-mapping.yaml punctuation.definition.mapping.begin.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "name", + "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "John Smith", + "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml string.unquoted.plain.in.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.in.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.in.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.yaml meta.flow-mapping.yaml punctuation.separator.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml meta.flow-mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "age", + "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "33", + "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml constant.numeric.integer.yaml", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "}", + "t": "source.yaml meta.flow-mapping.yaml punctuation.definition.mapping.end.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "name", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Mary Smith", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "age", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "27", + "t": "source.yaml constant.numeric.integer.yaml", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "men", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.yaml meta.flow-sequence.yaml punctuation.definition.sequence.begin.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "John Smith", + "t": "source.yaml meta.flow-sequence.yaml string.unquoted.plain.in.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.in.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.in.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.yaml meta.flow-sequence.yaml punctuation.separator.sequence.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml meta.flow-sequence.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Bill Jones", + "t": "source.yaml meta.flow-sequence.yaml string.unquoted.plain.in.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.in.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.in.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "]", + "t": "source.yaml meta.flow-sequence.yaml punctuation.definition.sequence.end.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "women", + "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ":", + "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Mary Smith", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "- ", + "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Susan Williams", + "t": "source.yaml string.unquoted.plain.out.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178" + } + } +] \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000000..80c726a0df --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +// Increase max listeners for event emitters +require('events').EventEmitter.defaultMaxListeners = 100; + +const gulp = require('gulp'); +const util = require('./build/lib/util'); +const path = require('path'); +const compilation = require('./build/lib/compilation'); + +// Fast compile for development time +gulp.task('clean-client', util.rimraf('out')); +gulp.task('compile-client', ['clean-client'], compilation.compileTask('out', false)); +gulp.task('watch-client', ['clean-client'], compilation.watchTask('out', false)); + +// Full compile, including nls and inline sources in sourcemaps, for build +gulp.task('clean-client-build', util.rimraf('out-build')); +gulp.task('compile-client-build', ['clean-client-build'], compilation.compileTask('out-build', true)); +gulp.task('watch-client-build', ['clean-client-build'], compilation.watchTask('out-build', true)); + +// Default +gulp.task('default', ['compile']); + +// All +gulp.task('clean', ['clean-client', 'clean-extensions']); +gulp.task('compile', ['compile-client', 'compile-extensions']); +gulp.task('watch', ['watch-client', 'watch-extensions']); + +// All Build +gulp.task('clean-build', ['clean-client-build', 'clean-extensions-build']); +gulp.task('compile-build', ['compile-client-build', 'compile-extensions-build']); +gulp.task('watch-build', ['watch-client-build', 'watch-extensions-build']); + +var ALL_EDITOR_TASKS = [ + // Always defined tasks + 'clean-client', + 'compile-client', + 'watch-client', + 'clean-client-build', + 'compile-client-build', + 'watch-client-build', + + // Editor tasks (defined in gulpfile.editor) + 'clean-optimized-editor', + 'optimize-editor', + 'clean-minified-editor', + 'minify-editor', + 'clean-editor-distro', + 'editor-distro', + 'analyze-editor-distro', + + // hygiene tasks + 'tslint', + 'hygiene', +]; + +var runningEditorTasks = process.argv.length > 2 && process.argv.slice(2).every(function (arg) { return (ALL_EDITOR_TASKS.indexOf(arg) !== -1); }); + +process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); + process.exit(1); +}); + +if (runningEditorTasks) { + require(`./build/gulpfile.editor`); + require(`./build/gulpfile.hygiene`); +} else { + // Load all the gulpfiles only if running tasks other than the editor tasks + const build = path.join(__dirname, 'build'); + require('glob').sync('gulpfile.*.js', { cwd: build }) + .forEach(f => require(`./build/${f}`)); +} \ No newline at end of file diff --git a/i18n/.gitignore b/i18n/.gitignore new file mode 100644 index 0000000000..2664d9bf9a --- /dev/null +++ b/i18n/.gitignore @@ -0,0 +1 @@ +!out/ \ No newline at end of file diff --git a/i18n/chs/extensions/configuration-editing/out/extension.i18n.json b/i18n/chs/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..34acc1dbb9 --- /dev/null +++ b/i18n/chs/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "示例" +} \ No newline at end of file diff --git a/i18n/chs/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/chs/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..d6953463e7 --- /dev/null +++ b/i18n/chs/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "例如 myFile.txt", + "activeEditorMedium": "例如 myFolder/myFile.txt", + "activeEditorLong": "例如 /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "例如 myFolder1、myFolder2、myFolder3", + "rootPath": "例如 /Users/Development/myProject", + "folderName": "例如 myFolder", + "folderPath": "例如 /Users/Development/myFolder", + "appName": "例如 VS Code", + "dirty": "一个更新的指示器,指示活动编辑器是否更新", + "separator": "一个条件分隔符(\"-\"),仅在左右是具有值的变量时才显示", + "assocLabelFile": "带扩展名的文件", + "assocDescriptionFile": "将所有匹配其文件名内的 glob 模式的文件映射到具有给定标识符的语言。", + "assocLabelPath": "带路径的文件", + "assocDescriptionPath": "将所有匹配其路径内绝对路径 glob 模式的文件映射到具有给定标识符的语言。", + "fileLabel": "按扩展名的文件", + "fileDescription": "与具有特定文件扩展名的所有文件匹配。", + "filesLabel": "具有多个扩展名的文件", + "filesDescription": "与具有任意文件扩展名的所有文件匹配。", + "derivedLabel": "具有同级文件的文件(按名称)", + "derivedDescription": "与具有名称相同但扩展名不同的同级文件的文件匹配。", + "topFolderLabel": "按名称的文件夹(顶级)", + "topFolderDescription": "与具有特定名称的顶级文件夹匹配。", + "topFoldersLabel": "使用多个名称的文件夹(顶级)", + "topFoldersDescription": "与多个顶级文件夹匹配。", + "folderLabel": "按名称的文件夹(任意位置)", + "folderDescription": "与任意位置具有特定名称的文件夹匹配。", + "falseDescription": "禁用该模式。", + "trueDescription": "启用该模式。", + "siblingsDescription": "与具有名称相同但扩展名不同的同级文件的文件匹配。", + "languageSpecificEditorSettings": "特定语言编辑器设置", + "languageSpecificEditorSettingsDescription": "替代语言编辑器设置" +} \ No newline at end of file diff --git a/i18n/chs/extensions/css/client/out/cssMain.i18n.json b/i18n/chs/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..acd804e11a --- /dev/null +++ b/i18n/chs/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "CSS 语言服务器" +} \ No newline at end of file diff --git a/i18n/chs/extensions/css/package.i18n.json b/i18n/chs/extensions/css/package.i18n.json new file mode 100644 index 0000000000..4bf8b12df1 --- /dev/null +++ b/i18n/chs/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "参数数量无效", + "css.lint.boxModel.desc": "使用边距或边框时,不要使用宽度或高度", + "css.lint.compatibleVendorPrefixes.desc": "使用供应商特定前缀时,确保同时包括所有其他供应商特定属性", + "css.lint.duplicateProperties.desc": "不要使用重复的样式定义", + "css.lint.emptyRules.desc": "不要使用空规则集", + "css.lint.float.desc": "避免使用“float”。浮动会带来脆弱的 CSS,如果布局的某一方面更改,将很容易破坏 CSS。", + "css.lint.fontFaceProperties.desc": "@font-face 规则必须定义 \"src\" 和 \"font-family\" 属性", + "css.lint.hexColorLength.desc": "十六进制颜色必须由三个或六个十六进制数字组成", + "css.lint.idSelector.desc": "选择器不应包含 ID,因为这些规则与 HTML 的耦合过于紧密。", + "css.lint.ieHack.desc": "仅当支持 IE7 及更低版本时,才需要 IE hack", + "css.lint.important.desc": "避免使用 !important。它表明整个 CSS 的特异性已经失去控制且需要重构。", + "css.lint.importStatement.desc": "Import 语句不会并行加载", + "css.lint.propertyIgnoredDueToDisplay.desc": "因显示而忽略属性。例如,使用 \"display: inline\"时,宽度、高度、上边距、下边距和 float 属性将不起作用", + "css.lint.universalSelector.desc": "已知通配选择符 (*) 慢", + "css.lint.unknownProperties.desc": "未知的属性。", + "css.lint.unknownVendorSpecificProperties.desc": "未知的供应商特定属性。", + "css.lint.vendorPrefix.desc": "使用供应商特定前缀时,还应包括标准属性", + "css.lint.zeroUnits.desc": "零不需要单位", + "css.trace.server.desc": "跟踪 VS Code 与 CSS 语言服务器之间的通信。", + "css.validate.title": "控制 CSS 验证和问题严重性。", + "css.validate.desc": "启用或禁用所有验证", + "less.lint.argumentsInColorFunction.desc": "参数数量无效", + "less.lint.boxModel.desc": "使用边距或边框时,不要使用宽度或高度", + "less.lint.compatibleVendorPrefixes.desc": "使用供应商特定前缀时,确保同时包括所有其他供应商特定属性", + "less.lint.duplicateProperties.desc": "不要使用重复的样式定义", + "less.lint.emptyRules.desc": "不要使用空规则集", + "less.lint.float.desc": "避免使用“float”。浮动会带来脆弱的 CSS,如果布局的某一方面更改,将很容易破坏 CSS。", + "less.lint.fontFaceProperties.desc": "@font-face 规则必须定义 \"src\" 和 \"font-family\" 属性", + "less.lint.hexColorLength.desc": "十六进制颜色必须由三个或六个十六进制数字组成", + "less.lint.idSelector.desc": "选择器不应包含 ID,因为这些规则与 HTML 的耦合过于紧密。", + "less.lint.ieHack.desc": "仅当支持 IE7 及更低版本时,才需要 IE hack", + "less.lint.important.desc": "避免使用 !important。它表明整个 CSS 的特异性已经失去控制且需要重构。", + "less.lint.importStatement.desc": "Import 语句不会并行加载", + "less.lint.propertyIgnoredDueToDisplay.desc": "因显示而忽略属性。例如,使用 \"display: inline\"时,宽度、高度、上边距、下边距和 float 属性将不起作用", + "less.lint.universalSelector.desc": "已知通配选择符 (*) 慢", + "less.lint.unknownProperties.desc": "未知的属性。", + "less.lint.unknownVendorSpecificProperties.desc": "未知的供应商特定属性。", + "less.lint.vendorPrefix.desc": "使用供应商特定前缀时,还应包括标准属性", + "less.lint.zeroUnits.desc": "零不需要单位", + "less.validate.title": "控制 LESS 验证和问题严重性。", + "less.validate.desc": "启用或禁用所有验证", + "scss.lint.argumentsInColorFunction.desc": "参数数量无效", + "scss.lint.boxModel.desc": "使用边距或边框时,不要使用宽度或高度", + "scss.lint.compatibleVendorPrefixes.desc": "使用供应商特定前缀时,确保同时包括所有其他供应商特定属性", + "scss.lint.duplicateProperties.desc": "不要使用重复的样式定义", + "scss.lint.emptyRules.desc": "不要使用空规则集", + "scss.lint.float.desc": "避免使用“float”。浮动会带来脆弱的 CSS,如果布局的某一方面更改,将很容易破坏 CSS。", + "scss.lint.fontFaceProperties.desc": "@font-face 规则必须定义 \"src\" 和 \"font-family\" 属性", + "scss.lint.hexColorLength.desc": "十六进制颜色必须由三个或六个十六进制数字组成", + "scss.lint.idSelector.desc": "选择器不应包含 ID,因为这些规则与 HTML 的耦合过于紧密。", + "scss.lint.ieHack.desc": "仅当支持 IE7 及更低版本时,才需要 IE hack", + "scss.lint.important.desc": "避免使用 !important。它表明整个 CSS 的特异性已经失去控制且需要重构。", + "scss.lint.importStatement.desc": "Import 语句不会并行加载", + "scss.lint.propertyIgnoredDueToDisplay.desc": "因显示而忽略属性。例如,使用 \"display: inline\"时,宽度、高度、上边距、下边距和 float 属性将不起作用", + "scss.lint.universalSelector.desc": "已知通配选择符 (*) 慢", + "scss.lint.unknownProperties.desc": "未知的属性。", + "scss.lint.unknownVendorSpecificProperties.desc": "未知的供应商特定属性。", + "scss.lint.vendorPrefix.desc": "使用供应商特定前缀时,还应包括标准属性", + "scss.lint.zeroUnits.desc": "零不需要单位", + "scss.validate.title": "控制 SCSS 验证和问题严重性。", + "scss.validate.desc": "启用或禁用所有验证", + "less.colorDecorators.enable.desc": "启用或禁用颜色修饰器", + "scss.colorDecorators.enable.desc": "启用或禁用颜色修饰器", + "css.colorDecorators.enable.desc": "启用或禁用颜色修饰器", + "css.colorDecorators.enable.deprecationMessage": "已弃用设置 \"css.colorDecorators.enabl\",请改用 \"editor.colorDecorators\"。", + "scss.colorDecorators.enable.deprecationMessage": "已弃用设置 \"scss.colorDecorators.enable\",请改用 \"editor.colorDecorators\"。", + "less.colorDecorators.enable.deprecationMessage": "已弃用设置 \"less.colorDecorators.enable\",请改用 \"editor.colorDecorators\"。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/emmet/package.i18n.json b/i18n/chs/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..a7d9cef9c9 --- /dev/null +++ b/i18n/chs/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "使用缩写包围", + "command.wrapIndividualLinesWithAbbreviation": "输入缩写包围个别行", + "command.removeTag": "移除标签", + "command.updateTag": "更新标签", + "command.matchTag": "转至匹配对", + "command.balanceIn": "平衡(向内)", + "command.balanceOut": "平衡(向外)", + "command.prevEditPoint": "转到上一编辑点", + "command.nextEditPoint": "转到下一编辑点", + "command.mergeLines": "合并行", + "command.selectPrevItem": "选择上一项", + "command.selectNextItem": "选择下一项", + "command.splitJoinTag": "分离/联接标记", + "command.toggleComment": "切换注释", + "command.evaluateMathExpression": "求数学表达式的值", + "command.updateImageSize": "更新图像大小", + "command.reflectCSSValue": "映射 CSS 值", + "command.incrementNumberByOne": "增加 1", + "command.decrementNumberByOne": "减少 1", + "command.incrementNumberByOneTenth": "增加 0.1", + "command.decrementNumberByOneTenth": "减少 0.1", + "command.incrementNumberByTen": "增加 10", + "command.decrementNumberByTen": "减少 10", + "emmetSyntaxProfiles": "为指定的语法定义配置文件或使用带有特定规则的配置文件。", + "emmetExclude": "不应展开 Emmet 缩写的语言数组。", + "emmetExtensionsPath": "含有 Emmet 配置文件和片段的文件夹路径。", + "emmetShowExpandedAbbreviation": "显示扩展的 Emmet 缩写作为建议。\n\"inMarkupAndStylesheetFilesOnly\" 选项适用于 html、haml、jade、slim、xml、xsl、css、scss、sass、less 和 stylus。\n\"always\" 选项适用于 markup/css 文件的所有部分。", + "emmetShowAbbreviationSuggestions": "显示可能的 Emmet 缩写作为建议。在风格表中或将 emmet.showExpandedAbbreviation 设置为 \"never\" 时不适用。", + "emmetIncludeLanguages": "启用使用默认不支持的语言的 Emmet 缩写。在此添加该语言与支持 Emmet 的语言之间的映射。示例: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "要用于 Emmet 代码片段的变量", + "emmetTriggerExpansionOnTab": "启用后,按 TAB 键时,将展开 Emmet 缩写。", + "emmetPreferences": "用于修改 Emmet 某些操作和解析程序的行为的首选项。", + "emmetPreferencesIntUnit": "整数值的默认单位", + "emmetPreferencesFloatUnit": "浮点数值的默认单位", + "emmetPreferencesCssAfter": "展开 CSS 缩写时在 CSS 属性末尾放置的符号", + "emmetPreferencesSassAfter": "在 Sass 文件中展开 CSS 缩写时在 CSS 属性末尾放置的符号", + "emmetPreferencesStylusAfter": "在 Stylus 文件中展开 CSS 缩写时在 CSS 属性末尾放置的符号", + "emmetPreferencesCssBetween": "展开 CSS 缩写时在 CSS 属性之间放置的符号", + "emmetPreferencesSassBetween": "在 Sass 文件中展开 CSS 缩写时在 CSS 属性之间放置的符号", + "emmetPreferencesStylusBetween": "在 Stylus 文件中展开 CSS 缩写时在 CSS 属性之间放置的符号", + "emmetShowSuggestionsAsSnippets": "若为 \"true\",Emmet 建议将会显示为代码片段,根据editor.snippetSuggestions 设置进行排列。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/chs/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..9566ec98e7 --- /dev/null +++ b/i18n/chs/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "图像必须使用 HTTPS 协议。", + "svgsNotValid": "SVG 不是有效的图像源。", + "embeddedSvgsNotValid": "嵌入的 SVG 不是有效的图像源。", + "dataUrlsNotValid": "数据 URL 不是有效的图像源。", + "relativeUrlRequiresHttpsRepository": "相关 URL 要求在 package.json 中指定使用 HTTPS 协议的存储库。", + "relativeIconUrlRequiresHttpsRepository": "图标要求在此 package.json 中指定使用 HTTPS 协议的存储库。", + "relativeBadgeUrlRequiresHttpsRepository": "相对徽章 URL 要求在 package.json 中指定使用 HTTPS 协议的存储库。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/chs/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..c81db9ae1f --- /dev/null +++ b/i18n/chs/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "特定语言编辑器设置", + "languageSpecificEditorSettingsDescription": "替代语言编辑器设置" +} \ No newline at end of file diff --git a/i18n/chs/extensions/git/out/askpass-main.i18n.json b/i18n/chs/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..1efd74315a --- /dev/null +++ b/i18n/chs/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "凭据丢失或无效。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/git/out/commands.i18n.json b/i18n/chs/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..65b8911123 --- /dev/null +++ b/i18n/chs/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "{0} 处的 Tag", + "remote branch at": "{0} 处的远程分支", + "create branch": "$(plus) 创建新分支", + "repourl": "存储库 URL", + "parent": "父目录", + "cloning": "正在克隆 GIT 存储库...", + "openrepo": "打开存储库", + "proposeopen": "是否要打开已克隆存储库?", + "path to init": "文件夹路径", + "provide path": "请提供用于初始化 Git 存储库的文件夹路径", + "HEAD not available": "“{0}”的 HEAD 版本不可用。", + "confirm stage files with merge conflicts": "确定要暂存含有合并冲突的 {0} 个文件吗?", + "confirm stage file with merge conflicts": "确定要暂存含有合并冲突的 {0} 吗?", + "yes": "是", + "confirm revert": "是否确实要还原 {0} 中的已选更改?", + "revert": "还原更改", + "discard": "放弃更改", + "confirm delete": "是否确定要删除 {0}?", + "delete file": "删除文件", + "confirm discard": "确定要放弃 {0} 中更改吗?", + "confirm discard multiple": "是否确实要放弃 {0} 文件中的更改?", + "warn untracked": "这将删除 {0} 个未跟踪的文件!", + "confirm discard all single": "确定要放弃 {0} 中更改吗?", + "confirm discard all": "确定要放弃在 {0} 个文件中的所有更改吗?\n此操作不可撤销!\n你当前的工作集将会永远丢失。", + "discardAll multiple": "放弃 1 个文件", + "discardAll": "放弃所有 {0} 个文件", + "confirm delete multiple": "是否确定要删除 {0} 个文件?", + "delete files": "删除文件", + "there are untracked files single": "若放弃下面未跟踪的文件,其将被从硬盘上删除: {0}。", + "there are untracked files": "若放弃 {0} 个未跟踪的文件,其将被从硬盘上删除。", + "confirm discard all 2": "{0}\n\n此操作不可撤销,你当前的工作集将会永远丢失。", + "yes discard tracked": "放弃 1 个已跟踪的文件", + "yes discard tracked multiple": "放弃 {0} 个已跟踪的文件", + "no staged changes": "现在没有暂存的更改以供提交\n\n是否要直接自动暂存所有更改并提交?", + "always": "始终", + "no changes": "没有要提交的更改。", + "commit message": "提交消息", + "provide commit message": "请提供提交消息", + "select a ref to checkout": "选择要签出的 ref", + "branch name": "分支名称", + "provide branch name": "请提供分支名称", + "select branch to delete": "选择要删除的分支", + "confirm force delete branch": "“{0}”分支未被完全合并。是否仍要删除?", + "delete branch": "删除分支", + "select a branch to merge from": "选择要从其合并的分支", + "merge conflicts": "存在合并冲突。请在提交之前解决这些冲突。", + "tag name": "标签名称", + "provide tag name": "请提供标签名称", + "tag message": "消息", + "provide tag message": "请提供消息以对标签进行注释", + "no remotes to pull": "存储库未配置任何从其中进行拉取的远程存储库。", + "pick remote pull repo": "选择要从其拉取分支的远程位置", + "no remotes to push": "存储库未配置任何要推送到的远程存储库。", + "push with tags success": "已成功带标签进行推送。", + "nobranch": "请签出一个分支以推送到远程。", + "pick remote": "选取要将分支“{0}”发布到的远程:", + "sync is unpredictable": "此操作从“{0}”推送和拉取提交。", + "ok": "确定", + "never again": "好,永不再显示", + "no remotes to publish": "存储库未配置任何要发布到的远程存储库。", + "no changes stash": "没有要存储的更改。", + "provide stash message": "提供存储消息(可选)", + "stash message": "存储消息", + "no stashes": "没有可以恢复的存储。", + "pick stash to pop": "选择要弹出的存储", + "clean repo": "在签出前,请清理存储库工作树。", + "cant push": "无法推送 refs 到远端。请先运行“拉取”功能以整合你的更改。", + "git error details": "Git:{0}", + "git error": "Git 错误", + "open git log": "打开 GIT 日志" +} \ No newline at end of file diff --git a/i18n/chs/extensions/git/out/main.i18n.json b/i18n/chs/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..7d6a9b6f6d --- /dev/null +++ b/i18n/chs/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "使用 {1} 中的 GIT {0}", + "updateGit": "更新 GIT", + "neverShowAgain": "不再显示", + "git20": "你似乎已安装 Git {0}。Code 和 Git 版本 >=2 一起工作最佳" +} \ No newline at end of file diff --git a/i18n/chs/extensions/git/out/model.i18n.json b/i18n/chs/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..1e816f0732 --- /dev/null +++ b/i18n/chs/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "没有可用存储库", + "pick repo": "选择存储库" +} \ No newline at end of file diff --git a/i18n/chs/extensions/git/out/repository.i18n.json b/i18n/chs/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..244f45b47d --- /dev/null +++ b/i18n/chs/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "打开", + "index modified": "已修改索引", + "modified": "已修改", + "index added": "已添加索引", + "index deleted": "已删除索引", + "deleted": "已删除", + "index renamed": "已重命名索引", + "index copied": "已复制索引", + "untracked": "未跟踪的", + "ignored": "已忽略", + "both deleted": "两者均已删除", + "added by us": "已由我们添加", + "deleted by them": "已被他们删除", + "added by them": "已由他们添加", + "deleted by us": "已被我们删除", + "both added": "两者均已添加", + "both modified": "二者均已修改", + "commit": "提交", + "merge changes": "合并更改", + "staged changes": "暂存的更改", + "changes": "更改", + "ok": "确定", + "neveragain": "不再显示", + "huge": "Git 存储库“{0}”中存在大量活动更改,将仅启用部分 Git 功能。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/git/out/scmProvider.i18n.json b/i18n/chs/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..2c5179f84b --- /dev/null +++ b/i18n/chs/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "提交" +} \ No newline at end of file diff --git a/i18n/chs/extensions/git/out/statusbar.i18n.json b/i18n/chs/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..e1c0d7f4ca --- /dev/null +++ b/i18n/chs/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "签出...", + "sync changes": "同步更改", + "publish changes": "发布更改", + "syncing changes": "正在同步更改..." +} \ No newline at end of file diff --git a/i18n/chs/extensions/git/package.i18n.json b/i18n/chs/extensions/git/package.i18n.json new file mode 100644 index 0000000000..51f00e0090 --- /dev/null +++ b/i18n/chs/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "克隆", + "command.init": "初始化存储库", + "command.close": "关闭存储库", + "command.refresh": "刷新", + "command.openChange": "打开更改", + "command.openFile": "打开文件", + "command.openHEADFile": "打开文件 (HEAD)", + "command.stage": "暂存更改", + "command.stageAll": "暂存所有更改", + "command.stageSelectedRanges": "暂存所选范围", + "command.revertSelectedRanges": "还原所选更改", + "command.unstage": "取消暂存更改", + "command.unstageAll": "取消暂存所有更改", + "command.unstageSelectedRanges": "取消暂存所选范围", + "command.clean": "放弃更改", + "command.cleanAll": "放弃所有更改", + "command.commit": "Commit", + "command.commitStaged": "提交已暂存文件", + "command.commitStagedSigned": "提交已暂存文件(已签名)", + "command.commitStagedAmend": "已暂存提交(修改)", + "command.commitAll": "全部提交", + "command.commitAllSigned": "全部提交(已签名)", + "command.commitAllAmend": "全部提交(修改)", + "command.undoCommit": "撤消上次提交", + "command.checkout": "签出到...", + "command.branch": "创建分支...", + "command.deleteBranch": "删除分支...", + "command.merge": "合并分支...", + "command.createTag": "创建标签", + "command.pull": "拉取", + "command.pullRebase": "拉取(变基)", + "command.pullFrom": "拉取自...", + "command.push": "推送", + "command.pushTo": "推送到...", + "command.pushWithTags": "带标签推送", + "command.sync": "同步", + "command.publish": "发布分支", + "command.showOutput": "显示 GIT 输出", + "command.ignore": "将文件添加到 .gitignore", + "command.stash": "存储", + "command.stashPop": "弹出存储...", + "command.stashPopLatest": "弹出最新存储", + "config.enabled": "是否已启用 GIT", + "config.path": "Git 可执行文件路径", + "config.autorefresh": "是否已启用自动刷新", + "config.autofetch": "是否启用了自动提取", + "config.enableLongCommitWarning": "是否针对长段提交消息进行警告", + "config.confirmSync": "同步 Git 存储库前进行确认", + "config.countBadge": "控制 Git 徽章计数器。“all”计算所有更改。“tracked”只计算跟踪的更改。“off”关闭此功能。", + "config.checkoutType": "控制运行“签出到...”时列出的分支的类型。“all”显示所有 refs,“local”只显示本地分支,“tags”只显示标签,“remote”只显示远程分支。", + "config.ignoreLegacyWarning": "忽略旧版 Git 警告", + "config.ignoreLimitWarning": "忽略“存储库中存在大量更改”的警告", + "config.defaultCloneDirectory": "克隆 Git 存储库的默认位置", + "config.enableSmartCommit": "在没有暂存的更改时提交所有更改。", + "config.enableCommitSigning": "启用使用 GPG 签名的提交", + "config.discardAllScope": "控制运行\"放弃所有更改\"命令时放弃的更改。\"all\" 放弃所有更改。 \"tracked\" 只放弃跟踪的文件。 \"prompt\" 每次运行此操作时显示提示对话框。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/grunt/out/main.i18n.json b/i18n/chs/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..915c2c1a14 --- /dev/null +++ b/i18n/chs/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "自动检测 Grunt 失败,错误:{0}" +} \ No newline at end of file diff --git a/i18n/chs/extensions/grunt/package.i18n.json b/i18n/chs/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..aed162c357 --- /dev/null +++ b/i18n/chs/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "控制自动检测 Grunt 任务是否打开。默认开启。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/gulp/out/main.i18n.json b/i18n/chs/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..1ef218321e --- /dev/null +++ b/i18n/chs/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "自动检测 gulp 失败,错误:{0}" +} \ No newline at end of file diff --git a/i18n/chs/extensions/gulp/package.i18n.json b/i18n/chs/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..16ec217e8f --- /dev/null +++ b/i18n/chs/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "控制自动检测 Gulp 任务是否打开。默认开启。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/html/client/out/htmlMain.i18n.json b/i18n/chs/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..ef72aad7a3 --- /dev/null +++ b/i18n/chs/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "HTML 语言服务器" +} \ No newline at end of file diff --git a/i18n/chs/extensions/html/package.i18n.json b/i18n/chs/extensions/html/package.i18n.json new file mode 100644 index 0000000000..6086673c8c --- /dev/null +++ b/i18n/chs/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "启用/禁用默认 HTML 格式化程序", + "html.format.wrapLineLength.desc": "每行最大字符数(0 = 禁用)。", + "html.format.unformatted.desc": "以逗号分隔的标记列表不应重设格式。\"null\" 默认为所有列于 https://www.w3.org/TR/html5/dom.html#phrasing-content 的标记。", + "html.format.contentUnformatted.desc": "以逗号分隔的标记列表,不应在其中重新设置内容的格式。\"null\" 默认为 \"pre\" 标记。", + "html.format.indentInnerHtml.desc": "缩进 和 部分。", + "html.format.preserveNewLines.desc": "是否要保留元素前面的现有换行符。仅适用于元素前,不适用于标记内或文本。", + "html.format.maxPreserveNewLines.desc": "要保留在一个区块中的换行符的最大数量。对于无限制使用 \"null\"。", + "html.format.indentHandlebars.desc": "格式和缩进 {{#foo}} 和 {{/foo}}。", + "html.format.endWithNewline.desc": "以新行结束。", + "html.format.extraLiners.desc": "标记列表,以逗号分隔,其前应有额外新行。\"null\" 默认为“标头、正文、/html”。", + "html.format.wrapAttributes.desc": "对属性进行换行。", + "html.format.wrapAttributes.auto": "仅在超出行长度时才对属性进行换行。", + "html.format.wrapAttributes.force": "对除第一个属性外的其他每个属性进行换行。", + "html.format.wrapAttributes.forcealign": "对除第一个属性外的其他每个属性进行换行,并保持对齐。", + "html.format.wrapAttributes.forcemultiline": "对每个属性进行换行。", + "html.suggest.angular1.desc": "配置内置 HTML 语言支持是否建议 Angular V1 标记和属性。", + "html.suggest.ionic.desc": "配置内置 HTML 语言支持是否建议 Ionic 标记、属性和值。", + "html.suggest.html5.desc": "配置内置 HTML 语言支持是否建议 HTML5 标记、属性和值。", + "html.trace.server.desc": "跟踪 VS Code 与 HTML 语言服务器之间的通信。", + "html.validate.scripts": "配置内置的 HTML 语言支持是否对嵌入的脚本进行验证。", + "html.validate.styles": "配置内置的 HTML 语言支持是否对嵌入的样式进行验证。", + "html.autoClosingTags": "启用/禁用 HTML 标记的自动关闭。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/jake/out/main.i18n.json b/i18n/chs/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..f53c6f6cf8 --- /dev/null +++ b/i18n/chs/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "自动检测 Jake 失败,错误:{0}" +} \ No newline at end of file diff --git a/i18n/chs/extensions/jake/package.i18n.json b/i18n/chs/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..a98ff579be --- /dev/null +++ b/i18n/chs/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "控制自动检测 Jake 任务是否打开。默认开启。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/chs/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..536fcdbdd4 --- /dev/null +++ b/i18n/chs/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "默认 bower.json", + "json.bower.error.repoaccess": "对 Bower 存储库发出的请求失败: {0}", + "json.bower.latest.version": "最新" +} \ No newline at end of file diff --git a/i18n/chs/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/chs/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..de03ea2b70 --- /dev/null +++ b/i18n/chs/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "默认 package.json", + "json.npm.error.repoaccess": "对 NPM 存储库发出的请求失败: {0}", + "json.npm.latestversion": "当前最新版本的包", + "json.npm.majorversion": "与最新主要版本(1.x.x)匹配", + "json.npm.minorversion": "与最新次要版本(1.2.x)匹配", + "json.npm.version.hover": "最新版本: {0}" +} \ No newline at end of file diff --git a/i18n/chs/extensions/json/client/out/jsonMain.i18n.json b/i18n/chs/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..10cf5b04be --- /dev/null +++ b/i18n/chs/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "JSON 语言服务器" +} \ No newline at end of file diff --git a/i18n/chs/extensions/json/package.i18n.json b/i18n/chs/extensions/json/package.i18n.json new file mode 100644 index 0000000000..401ddbca06 --- /dev/null +++ b/i18n/chs/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "将当前项目中的 JSON 文件与架构关联起来", + "json.schemas.url.desc": "当前目录中指向架构的 URL 或相对路径", + "json.schemas.fileMatch.desc": "将 JSON 文件解析到架构时,用于匹配的一组文件模式。", + "json.schemas.fileMatch.item.desc": "将 JSON 文件解析到架构时,用于匹配的可以包含 \"*\" 的文件模式。", + "json.schemas.schema.desc": "给定 URL 的架构定义。只需提供该架构以避免对架构 URL 的访问。", + "json.format.enable.desc": "启用/禁用默认 JSON 格式化程序(需要重启)", + "json.tracing.desc": "跟踪 VS Code 与 JSON 语言服务器之间的通信。", + "json.colorDecorators.enable.desc": "启用或禁用颜色修饰器", + "json.colorDecorators.enable.deprecationMessage": "已弃用设置 \"json.colorDecorators.enable\",请改用 \"editor.colorDecorators\"。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/markdown/out/extension.i18n.json b/i18n/chs/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..0eaca301b8 --- /dev/null +++ b/i18n/chs/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "无法加载“markdown.styles”:{0}" +} \ No newline at end of file diff --git a/i18n/chs/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/chs/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..354f362ef4 --- /dev/null +++ b/i18n/chs/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "已禁用此文档中的部分内容", + "preview.securityMessage.title": "已禁用此 Markdown 预览中的可能不安全的内容。更改 Markdown 预览安全设置以允许不安全内容或启用脚本。", + "preview.securityMessage.label": "已禁用内容安全警告" +} \ No newline at end of file diff --git a/i18n/chs/extensions/markdown/out/security.i18n.json b/i18n/chs/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..c233a85bc6 --- /dev/null +++ b/i18n/chs/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "严格", + "strict.description": "仅载入安全内容", + "insecureContent.title": "允许不安全内容", + "insecureContent.description": "允许通过 http 载入内容", + "disable.title": "禁用", + "disable.description": "允许所有内容,执行所有脚本。不推荐", + "moreInfo.title": "详细信息", + "preview.showPreviewSecuritySelector.title": "选择此工作区中 Markdown 预览的安全设置" +} \ No newline at end of file diff --git a/i18n/chs/extensions/markdown/package.i18n.json b/i18n/chs/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..cb3d2b82c2 --- /dev/null +++ b/i18n/chs/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "设置换行符如何在 markdown 预览中呈现。将其设置为 \"true\" 会为每一个新行创建一个
。", + "markdown.preview.linkify": "在 Markdown 预览中启用或禁用将类似 URL 的文本转换为链接。", + "markdown.preview.doubleClickToSwitchToEditor.desc": "在 Markdown 预览中双击切换到编辑器。", + "markdown.preview.fontFamily.desc": "控制 Markdown 预览中使用的字体系列。", + "markdown.preview.fontSize.desc": "控制 Markdown 预览中使用的字号(以像素为单位)。", + "markdown.preview.lineHeight.desc": "控制 Markdown 预览中使用的行高。此数值与字号相关。", + "markdown.preview.markEditorSelection.desc": "在 Markdown 预览中标记当前的编辑器选定内容。", + "markdown.preview.scrollEditorWithPreview.desc": "当 Markdown 预览滚动时,更新编辑器的视图。", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "滚动 Markdown 预览以显示编辑器中当前所选的行。", + "markdown.preview.title": "打开预览", + "markdown.previewFrontMatter.dec": "设置如何在 Markdown 预览中呈现 YAML 扉页。“隐藏”会删除扉页。否则,扉页则被视为 Markdown 内容。", + "markdown.previewSide.title": "打开侧边预览", + "markdown.showSource.title": "显示源", + "markdown.styles.dec": "要在 Markdown 预览中使用的 CSS 样式表的 URL 或本地路径列表。相对路径被解释为相对于资源管理器中打开的文件夹。如果没有任何打开的文件夹,则会被解释为相对于 Markdown 文件的位置。所有的 \"\\\" 需写为 \"\\\\\"。", + "markdown.showPreviewSecuritySelector.title": "更改预览安全设置", + "markdown.trace.desc": "对 Markdown 扩展启用调试日志记录。", + "markdown.refreshPreview.title": "刷新预览" +} \ No newline at end of file diff --git a/i18n/chs/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/chs/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..76220c39dd --- /dev/null +++ b/i18n/chs/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "采用当前更改", + "acceptIncomingChange": "采用传入的更改", + "acceptBothChanges": "保留双方更改", + "compareChanges": "比较变更" +} \ No newline at end of file diff --git a/i18n/chs/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/chs/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..4c4fbff2ac --- /dev/null +++ b/i18n/chs/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "编辑器光标不在合并冲突内", + "compareChangesTitle": "{0}:当前更改 ⟷ 传入的更改", + "cursorOnCommonAncestorsRange": "编辑器光标在共同来源块上,请将其移动至“当前”或“传入”区域中", + "cursorOnSplitterRange": "编辑器光标在合并冲突分割线上,请将其移动至“当前”或“传入”区域中", + "noConflicts": "没有在此文件中找到合并冲突", + "noOtherConflictsInThisFile": "此文件中没有其他合并冲突了" +} \ No newline at end of file diff --git a/i18n/chs/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/chs/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..9928bbda9d --- /dev/null +++ b/i18n/chs/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(当前更改)", + "incomingChange": "(传入的更改)" +} \ No newline at end of file diff --git a/i18n/chs/extensions/merge-conflict/package.i18n.json b/i18n/chs/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..e0c4f39677 --- /dev/null +++ b/i18n/chs/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "合并冲突", + "command.accept.all-incoming": "全部采用传入版本", + "command.accept.all-both": "全部保留两者", + "command.accept.current": "采用当前内容", + "command.accept.incoming": "采用传入内容", + "command.accept.selection": "采用选中版本", + "command.accept.both": "保留两者", + "command.next": "下一个冲突", + "command.previous": "上一个冲突", + "command.compare": "比较当前冲突", + "config.title": "合并冲突", + "config.codeLensEnabled": "启用/禁用编辑器内合并冲突区域的 CodeLens", + "config.decoratorsEnabled": "启用/禁用编辑器内的合并冲突修饰器" +} \ No newline at end of file diff --git a/i18n/chs/extensions/npm/package.i18n.json b/i18n/chs/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..f32ee26ca3 --- /dev/null +++ b/i18n/chs/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "控制自动检测 npm 脚本是否打开。默认开启。", + "config.npm.runSilent": "使用 \"--silent\" 选项运行 npm 命令" +} \ No newline at end of file diff --git a/i18n/chs/extensions/php/out/features/validationProvider.i18n.json b/i18n/chs/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..1376892ed1 --- /dev/null +++ b/i18n/chs/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "是否允许执行 {0} (定义为工作区设置)以进行 PHP 文件的 lint 操作?", + "php.yes": "允许", + "php.no": "不允许", + "wrongExecutable": "无法验证,因为 {0} 不是有效的 PHP 可执行文件。请使用设置 \"php.validate.executablePath\" 配置 PHP 可执行文件。", + "noExecutable": "无法验证,因为未设置任何 PHP 可执行文件。请使用设置 \"php.validate.executablePath\" 配置 PHP 可执行文件。", + "unknownReason": "使用路径运行 php 失败: {0}。原因未知。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/php/package.i18n.json b/i18n/chs/extensions/php/package.i18n.json new file mode 100644 index 0000000000..72d8df0b2e --- /dev/null +++ b/i18n/chs/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "如果已启用内置 PHP 语言建议,则进行配置。此支持建议 PHP 全局变量和变量。", + "configuration.validate.enable": "启用/禁用内置的 PHP 验证。", + "configuration.validate.executablePath": "指向 PHP 可执行文件。", + "configuration.validate.run": "不管 linter 是在 save 还是在 type 上运行。", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "禁止 PHP 验证程序(定义为工作区设置)" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/chs/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..831b1b3196 --- /dev/null +++ b/i18n/chs/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "正在使用 TypeScript ({1}) 实现的编辑器功能。TypeScript ({0}) 已经全局安装在你的电脑上。VS Code 中发生的错误可能会与 TCS 中不同", + "moreInformation": "详细信息", + "doNotCheckAgain": "不再检查", + "close": "关闭", + "updateTscCheck": "已将用户设置 \"typescript.check.tscVersion\" 更新为 false" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/chs/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..95d3267d9f --- /dev/null +++ b/i18n/chs/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "正在获取 typings...", + "acquiringTypingsDetail": "获取 IntelliSense 的 typings 定义。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/chs/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..fbe5b4e3b0 --- /dev/null +++ b/i18n/chs/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "在 JavaScript 文件中启用语义检查。必须在文件顶部。", + "ts-nocheck": "在 JavaScript 文件中禁用语义检查。必须在文件顶部。", + "ts-ignore": "取消文件下一行的 @ts-check 错误提示。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/chs/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..d085e50a24 --- /dev/null +++ b/i18n/chs/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 个实现", + "manyImplementationLabel": "{0} 个实现", + "implementationsErrorLabel": "无法确定实现" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/chs/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..c7e3b19584 --- /dev/null +++ b/i18n/chs/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "JSDoc 注释" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/chs/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..26519bd052 --- /dev/null +++ b/i18n/chs/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 个引用", + "manyReferenceLabel": "{0} 个引用", + "referenceErrorLabel": "无法确定引用" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/chs/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..7454d2d6c7 --- /dev/null +++ b/i18n/chs/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "监视 - {0}", + "buildTscLabel": "构建 - {0}" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/typescriptMain.i18n.json b/i18n/chs/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..6792e1d960 --- /dev/null +++ b/i18n/chs/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "请在 VS Code 中打开一个文件夹,以使用 TypeScript 或 JavaScript 项目", + "typescript.projectConfigUnsupportedFile": "无法确定 TypeScript 或 JavaScript 项目。不受支持的文件类型", + "typescript.projectConfigCouldNotGetInfo": "无法确定 TypeScript 或 JavaScript 项目", + "typescript.noTypeScriptProjectConfig": "文件不属于 TypeScript 项目", + "typescript.noJavaScriptProjectConfig": "文件不属于 JavaScript 项目", + "typescript.configureTsconfigQuickPick": "配置 tsconfig.json", + "typescript.configureJsconfigQuickPick": "配置 jsconfig.json", + "typescript.projectConfigLearnMore": "了解详细信息" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/chs/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..cebdcba4bb --- /dev/null +++ b/i18n/chs/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "路径 {0} 未指向有效的 tsserver 安装。请回退到捆绑的 TypeScript 版本。", + "serverCouldNotBeStarted": "无法启动 TypeScript 语言服务器。错误消息为: {0}", + "typescript.openTsServerLog.notSupported": "TS 服务器日志记录需要 TS 2.2.2+", + "typescript.openTsServerLog.loggingNotEnabled": "TS 服务器日志记录已关闭。请设置 \"typescript.tsserver.log\" 并重启\n TS 服务器以启用日志记录", + "typescript.openTsServerLog.enableAndReloadOption": "启用日志记录并重启 TS 服务器", + "typescript.openTsServerLog.noLogFile": "TS 服务器尚未启动日志记录。", + "openTsServerLog.openFileFailedFailed": "无法打开 TS 服务器日志文件", + "serverDiedAfterStart": "TypeScript 语言服务在其启动后已中止 5 次。将不会重启该服务。", + "serverDiedReportIssue": "报告问题", + "serverDied": "在过去 5 分钟内,TypeScript 语言服务意外中止了 5 次。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/utils/api.i18n.json b/i18n/chs/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..37fb14a09c --- /dev/null +++ b/i18n/chs/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "无效版本" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/utils/logger.i18n.json b/i18n/chs/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/chs/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/chs/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..d3b059703a --- /dev/null +++ b/i18n/chs/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "若要启用项目范围内的 JavaScript/TypeScript 语言功能,请排除包含多个文件的文件夹,例如: {0}", + "hintExclude.generic": "若要启用项目范围内的 JavaScript/TypeScript 语言功能,请排除包含不需要处理的源文件的大型文件夹。", + "large.label": "配置排除", + "hintExclude.tooltip": "若要启用项目范围内的 JavaScript/TypeScript 语言功能,请排除包含不需要处理的源文件的大型文件夹。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/chs/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..52c678b9ce --- /dev/null +++ b/i18n/chs/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "提取数据以实现更好的 TypeScript IntelliSense", + "typesInstallerInitializationFailed.title": "无法为 JavaScript 语言功能安装 typings 文件。请确认 NPM 已经安装或者在你的用户设置中配置“typescript.npm”", + "typesInstallerInitializationFailed.moreInformation": "详细信息", + "typesInstallerInitializationFailed.doNotCheckAgain": "不要再次检查", + "typesInstallerInitializationFailed.close": "关闭" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/chs/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..518d148185 --- /dev/null +++ b/i18n/chs/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "使用 VS Code 的版本", + "useWorkspaceVersionOption": "使用工作区版本", + "learnMore": "了解详细信息", + "selectTsVersion": "选择用于 JavaScript 和 TypeScript 语言功能的 TypeScript 版本" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/chs/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..902b4a0e0d --- /dev/null +++ b/i18n/chs/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "无法获取此目录 TypeScript 的版本", + "noBundledServerFound": "VS Code 的 tsserver 已被其他应用程序(例如运行异常的病毒检测工具)删除。请重新安装 VS Code。" +} \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/package.i18n.json b/i18n/chs/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..cc1225af16 --- /dev/null +++ b/i18n/chs/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "重载项目", + "javascript.reloadProjects.title": "重载项目", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "完成函数的参数签名。", + "typescript.tsdk.desc": "指定包含要使用的 tsserver 和 lib*.d.ts 文件的文件夹路径。", + "typescript.disableAutomaticTypeAcquisition": "禁用自动获取类型。需要 TypeScript >= 2.0.6。", + "typescript.tsserver.log": "将 TS 服务器的日志保存到一个文件。此日志可用于诊断 TS 服务器问题。日志可能包含你的项目中的文件路径、源代码和其他可能敏感的信息。", + "typescript.tsserver.trace": "对发送到 TS 服务器的消息启用跟踪。此跟踪信息可用于诊断 TS 服务器问题。 跟踪信息可能包含你的项目中的文件路径、源代码和其他可能敏感的信息。", + "typescript.validate.enable": "启用/禁用 TypeScript 验证。", + "typescript.format.enable": "启用/禁用默认 TypeScript 格式化程序。", + "javascript.format.enable": "启用/禁用 JavaScript 格式化程序。", + "format.insertSpaceAfterCommaDelimiter": "定义逗号分隔符后面的空格处理。", + "format.insertSpaceAfterConstructor": "定义构造器关键字后的空格处理。要求 TypeScript >= 2.3.0。", + "format.insertSpaceAfterSemicolonInForStatements": "在 For 语句中,定义分号后面的空格处理。", + "format.insertSpaceBeforeAndAfterBinaryOperators": "定义二进制运算符后面的空格处理", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "定义控制流语句中关键字后面的空格处理。", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "定义匿名函数的函数关键字后面的空格处理。", + "format.insertSpaceBeforeFunctionParenthesis": "在函数参数括号前定义空格处理。需要 TypeScript >= 2.1.5。", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "定义非空圆括号的左括号后面和右括号前面的空格处理。", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "定义非空方括号的左括号后面和右括号前面的空格处理。", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "定义非空括号的左括号后面和右括号前面的空格处理。要求 TypeScript >= 2.3.0。", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "定义模板字符串的左括号后面和右括号前面的空格处理。要求 TypeScript >= 2.0.6。", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "定义 JSX 表达式左括号后面和右括号前面的空格处理。要求 TypeScript >= 2.0.6。", + "format.insertSpaceAfterTypeAssertion": "定义 TypeScript 中类型断言后的空格处理。要求 TypeScript >= 2.4。", + "format.placeOpenBraceOnNewLineForFunctions": "定义函数的左大括号是否放置在新的一行。", + "format.placeOpenBraceOnNewLineForControlBlocks": "定义控制块的左括号是否放置在新的一行。", + "javascript.validate.enable": "启用/禁用 JavaScript 验证。", + "typescript.goToProjectConfig.title": "转到项目配置", + "javascript.goToProjectConfig.title": "转到项目配置", + "javascript.referencesCodeLens.enabled": "启用/禁用在 JavaScript 文件中引用 CodeLens。", + "typescript.referencesCodeLens.enabled": "启用/禁用在 TypeScript 文件中引用 CodeLens。要求 TypeScript >= 2.0.6。", + "typescript.implementationsCodeLens.enabled": "启用/禁用实现 CodeLens。要求 TypeScript >= 2.2.0。", + "typescript.openTsServerLog.title": "打开 TS 服务器日志", + "typescript.restartTsServer": "重启 TS 服务器", + "typescript.selectTypeScriptVersion.title": "选择 TypeScript 版本", + "jsDocCompletion.enabled": "启用/禁用自动 JSDoc 注释", + "javascript.implicitProjectConfig.checkJs": "启用/禁用 JavaScript 文件的语义检查。现有的 jsconfig.json 或\n tsconfig.json 文件会覆盖此设置。要求 TypeScript >=2.3.1。", + "typescript.npm": "指定用于自动获取类型的 NPM 可执行文件的路径。要求 TypeScript >= 2.3.4。", + "typescript.check.npmIsInstalled": "检查是否安装了 NPM 以自动获取类型。", + "javascript.nameSuggestions": "启用/禁用在 JavaScript 建议列表中包含文件中的唯一名称。", + "typescript.tsc.autoDetect": "控制自动检测 tsc 任务是否打开。", + "typescript.problemMatchers.tsc.label": "TypeScript 问题", + "typescript.problemMatchers.tscWatch.label": "TypeScript 问题(观看模式)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/chs/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/chs/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/chs/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..6258043c58 --- /dev/null +++ b/i18n/chs/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (已再次发生)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/chs/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..9f2b16286d --- /dev/null +++ b/i18n/chs/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "输入" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/chs/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..b82803f1c8 --- /dev/null +++ b/i18n/chs/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "区分大小写", + "wordsDescription": "全字匹配", + "regexDescription": "使用正则表达式" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/chs/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..958a839614 --- /dev/null +++ b/i18n/chs/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "错误: {0}", + "alertWarningMessage": "警告: {0}", + "alertInfoMessage": "信息: {0}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/chs/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..f17619a48f --- /dev/null +++ b/i18n/chs/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "图像太大,无法在编辑器中显示。 ", + "resourceOpenExternalButton": "使用外部程序打开图片?", + "nativeBinaryError": "文件将不在编辑器中显示,因为它是二进制文件、非常大或使用不支持的文本编码。", + "sizeB": "{0} B", + "sizeKB": "{0} KB", + "sizeMB": "{0} MB", + "sizeGB": "{0} GB", + "sizeTB": "{0} TB" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/chs/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..54b9a047ce --- /dev/null +++ b/i18n/chs/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "更多" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/common/errorMessage.i18n.json b/i18n/chs/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..ba5de54273 --- /dev/null +++ b/i18n/chs/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}。错误代码: {1}", + "error.permission.verbose": "权限被拒绝(HTTP {0})", + "error.permission": "权限被拒绝", + "error.http.verbose": "{0} (HTTP {1}: {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "未知连接错误 ({0})", + "error.connection.unknown": "出现未知连接错误。您的 Internet 连接已断开,或者您连接的服务器已脱机。", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "出现未知错误。有关详细信息,请参阅日志。", + "nodeExceptionMessage": "发生了系统错误 ({0})", + "error.moreErrors": "{0} 个(共 {1} 个错误)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/common/json.i18n.json b/i18n/chs/src/vs/base/common/json.i18n.json new file mode 100644 index 0000000000..575bc9c0f6 --- /dev/null +++ b/i18n/chs/src/vs/base/common/json.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "符号无效", + "error.invalidNumberFormat": "数字格式无效", + "error.propertyNameExpected": "需要属性名", + "error.valueExpected": "需要值", + "error.colonExpected": "需要冒号", + "error.commaExpected": "需要逗号", + "error.closeBraceExpected": "需要右大括号", + "error.closeBracketExpected": "需要右括号", + "error.endOfFileExpected": "预期的文件结尾" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/chs/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..b02defaa32 --- /dev/null +++ b/i18n/chs/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "符号无效", + "error.invalidNumberFormat": "数字格式无效", + "error.propertyNameExpected": "需要属性名", + "error.valueExpected": "需要值", + "error.colonExpected": "需要冒号", + "error.commaExpected": "需要逗号", + "error.closeBraceExpected": "需要右大括号", + "error.closeBracketExpected": "需要右括号", + "error.endOfFileExpected": "需要文件结尾(EOF)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/common/keybindingLabels.i18n.json b/i18n/chs/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..b234e36af1 --- /dev/null +++ b/i18n/chs/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Shift", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Shift", + "altKey.long": "Alt", + "cmdKey.long": "Command", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/common/processes.i18n.json b/i18n/chs/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..59096c2e33 --- /dev/null +++ b/i18n/chs/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "错误: 可执行信息必须定义字符串类型的命令。", + "ExecutableParser.isShellCommand": "警告: isShellCommand 的类型必须为布尔型。正在忽略值 {0}。", + "ExecutableParser.args": "警告: args 的类型必须为 string[]。正在忽略值 {0}。", + "ExecutableParser.invalidCWD": "警告: options.cwd 的类型必须为字符串。正在忽略值 {0}。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/common/severity.i18n.json b/i18n/chs/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..12d93e2b8c --- /dev/null +++ b/i18n/chs/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "错误", + "sev.warning": "警告", + "sev.info": "信息" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/node/processes.i18n.json b/i18n/chs/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..895bd858a7 --- /dev/null +++ b/i18n/chs/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "无法对 UNC 驱动器执行 shell 命令。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/node/zip.i18n.json b/i18n/chs/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..80af4b73c7 --- /dev/null +++ b/i18n/chs/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "在 Zip 中找不到 {0}。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/chs/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..f1abce57ca --- /dev/null +++ b/i18n/chs/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0},选取器", + "quickOpenAriaLabel": "选取器" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/chs/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..9b47fbb68a --- /dev/null +++ b/i18n/chs/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "快速选取器。键入以缩小结果范围。", + "treeAriaLabel": "快速选取器" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/chs/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..35bfa944dd --- /dev/null +++ b/i18n/chs/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "折叠" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/code/electron-main/auth.i18n.json b/i18n/chs/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..ca5242ba7e --- /dev/null +++ b/i18n/chs/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "需要验证代理", + "proxyauth": "{0} 代理需要验证。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/code/electron-main/menus.i18n.json b/i18n/chs/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..6d071b2f85 --- /dev/null +++ b/i18n/chs/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "文件(&&F)", + "mEdit": "编辑(&&E)", + "mSelection": "选择(&&S)", + "mView": "查看(&&V)", + "mGoto": "转到(&&G)", + "mDebug": "调试(&&D)", + "mWindow": "窗口", + "mHelp": "帮助(&&H)", + "mTask": "任务(&&T)", + "miNewWindow": "新建窗口(&&W)", + "mAbout": "关于 {0}", + "mServices": "服务", + "mHide": "隐藏 {0}", + "mHideOthers": "隐藏其他", + "mShowAll": "全部显示", + "miQuit": "退出 {0}", + "miNewFile": "新建文件(&&N)", + "miOpen": "打开(&&O)...", + "miOpenWorkspace": "打开工作区(&&O)...", + "miOpenFolder": "打开文件夹(&&F)...", + "miOpenFile": "打开文件(&&O)...", + "miOpenRecent": "打开最近的文件(&&R)", + "miSaveWorkspaceAs": "将工作区另存为(&&S)...", + "miAddFolderToWorkspace": "将文件夹添加到工作区(&&A)...", + "miSave": "保存(&&S)", + "miSaveAs": "另存为(&&A)...", + "miSaveAll": "全部保存(&&L)", + "miAutoSave": "自动保存", + "miRevert": "还原文件(&&V)", + "miCloseWindow": "关闭窗口(&&W)", + "miCloseWorkspace": "关闭工作区(&&W)", + "miCloseFolder": "关闭文件夹(&&F)", + "miCloseEditor": "关闭编辑器(&&C)", + "miExit": "退出(&&X)", + "miOpenSettings": "设置(&&S)", + "miOpenKeymap": "键盘快捷方式(&&K)", + "miOpenKeymapExtensions": "键映射扩展(&&K)", + "miOpenSnippets": "用户代码片段(&&S)", + "miSelectColorTheme": "颜色主题(&&C)", + "miSelectIconTheme": "文件图标主题(&&I)", + "miPreferences": "首选项(&&P)", + "miReopenClosedEditor": "重新打开已关闭的编辑器(&&R)", + "miMore": "更多(&&M)...", + "miClearRecentOpen": "清除最近打开记录", + "miUndo": "撤消(&&U)", + "miRedo": "恢复(&&R)", + "miCut": "剪切(&&T)", + "miCopy": "复制(&&C)", + "miPaste": "粘贴(&&P)", + "miFind": "查找(&&F)", + "miReplace": "替换(&&R)", + "miFindInFiles": "在文件中查找(&&I)", + "miReplaceInFiles": "在文件中替换(&&I)", + "miEmmetExpandAbbreviation": "Emmet: 展开缩写(&&X)", + "miShowEmmetCommands": "Emmet(&&M)...", + "miToggleLineComment": "切换行注释(&&T)", + "miToggleBlockComment": "切换块注释(&&B)", + "miMultiCursorAlt": "切换为“Alt+单击”进行多光标功能", + "miMultiCursorCmd": "切换为“Cmd+单击”进行多光标功能", + "miMultiCursorCtrl": "切换为“Ctrl+单击”进行多光标功能", + "miInsertCursorAbove": "在上面添加光标(&&A)", + "miInsertCursorBelow": "在下面添加光标(&&D)", + "miInsertCursorAtEndOfEachLineSelected": "在行尾添加光标(&&U)", + "miAddSelectionToNextFindMatch": "添加下一个匹配项(&&N)", + "miAddSelectionToPreviousFindMatch": "添加上一个匹配项(&&R)", + "miSelectHighlights": "选择所有匹配项(&&O)", + "miCopyLinesUp": "向上复制一行(&&C)", + "miCopyLinesDown": "向下复制一行(&&P)", + "miMoveLinesUp": "向上移动一行(&&V)", + "miMoveLinesDown": "向下移动一行(&&L)", + "miSelectAll": "全选(&&S)", + "miSmartSelectGrow": "展开选定内容(&&E)", + "miSmartSelectShrink": "缩小选定范围(&&S)", + "miViewExplorer": "资源管理器(&&E)", + "miViewSearch": "搜索(&&S)", + "miViewSCM": "SCM(&&C)", + "miViewDebug": "调试(&&D)", + "miViewExtensions": "扩展(&&X)", + "miToggleOutput": "输出(&&O)", + "miToggleDebugConsole": "调试控制台(&&B)", + "miToggleIntegratedTerminal": "集成终端(&&I)", + "miMarker": "问题(&&P)", + "miAdditionalViews": "其他视图(&&V)", + "miCommandPalette": "命令面板(&&C)...", + "miToggleFullScreen": "切换全屏(&&F)", + "miToggleZenMode": "切换 Zen 模式", + "miToggleMenuBar": "切换菜单栏(&&B)", + "miSplitEditor": "拆分编辑器(&&E)", + "miToggleEditorLayout": "切换编辑器组布局(&&L)", + "miToggleSidebar": "切换侧边栏(&&T)", + "miMoveSidebarRight": "向右移动侧边栏(&&M)", + "miMoveSidebarLeft": "向左移动侧边栏(&&M)", + "miTogglePanel": "切换面板(&&P)", + "miHideStatusbar": "隐藏状态栏(&&H)", + "miShowStatusbar": "显示状态栏(&&S)", + "miHideActivityBar": "隐藏活动栏(&&A)", + "miShowActivityBar": "显示活动栏(&&A)", + "miToggleWordWrap": "切换自动换行(&&W)", + "miToggleMinimap": "切换小地图(&&M)", + "miToggleRenderWhitespace": "切换呈现空格(&&R)", + "miToggleRenderControlCharacters": "切换控制字符(&&C)", + "miZoomIn": "放大(&&Z)", + "miZoomOut": "缩小(&&U)", + "miZoomReset": "重置缩放(&&R)", + "miBack": "后退(&&B)", + "miForward": "前进(&&F)", + "miNextEditor": "下一个编辑器(&&N)", + "miPreviousEditor": "上一个编辑器(&&P)", + "miNextEditorInGroup": "组中下一个使用过的编辑器(&&N)", + "miPreviousEditorInGroup": "组中上一个使用过的编辑器(&&P)", + "miSwitchEditor": "切换编辑器(&&E)", + "miFocusFirstGroup": "第一组(&&F)", + "miFocusSecondGroup": "第二组(&&S)", + "miFocusThirdGroup": "第三组(&&T)", + "miNextGroup": "下一个组(&&N)", + "miPreviousGroup": "上一个组(&&P)", + "miSwitchGroup": "切换组(&&G)", + "miGotoFile": "转到文件(&&F)...", + "miGotoSymbolInFile": "转到文件中的符号(&&S)...", + "miGotoSymbolInWorkspace": "转到工作区中的符号(&&W)...", + "miGotoDefinition": "转到定义(&&D)...", + "miGotoTypeDefinition": "转到类型定义(&&T)", + "miGotoImplementation": "转到实现(&&I)", + "miGotoLine": "转到行(&&L)...", + "miStartDebugging": "启动调试(&&S)", + "miStartWithoutDebugging": "非调试启动(&&W)", + "miStopDebugging": "停止调试(&&S)", + "miRestart Debugging": "重启调试(&&R)", + "miOpenConfigurations": "打开配置(&&C)", + "miAddConfiguration": "添加配置...", + "miStepOver": "单步跳过(&&O)", + "miStepInto": "单步执行(&&I)", + "miStepOut": "单步跳出(&&U)", + "miContinue": "继续(&&C)", + "miToggleBreakpoint": "切换断点(&&B)", + "miConditionalBreakpoint": "条件断点(&&C)...", + "miColumnBreakpoint": "列断点(&&O)", + "miFunctionBreakpoint": "函数断点(&&F)...", + "miNewBreakpoint": "新建断点(&&N)", + "miEnableAllBreakpoints": "启用所有断点", + "miDisableAllBreakpoints": "禁用所有断点(&&L)", + "miRemoveAllBreakpoints": "删除所有断点(&&R)", + "miInstallAdditionalDebuggers": "安装其他调试器(&&I)...", + "mMinimize": "最小化", + "mZoom": "缩放", + "mBringToFront": "全部置于顶层", + "miSwitchWindow": "切换窗口(&&W)...", + "miToggleDevTools": "切换开发人员工具(&&T)", + "miAccessibilityOptions": "辅助功能选项(&&O)", + "miReportIssues": "报告问题(&&I)", + "miWelcome": "欢迎使用(&&W)", + "miInteractivePlayground": "交互式演练场(&&I)", + "miDocumentation": "文档(&&D)", + "miReleaseNotes": "发行说明(&&R)", + "miKeyboardShortcuts": "快捷键参考(&&K)", + "miIntroductoryVideos": "介绍性视频(&&V)", + "miTipsAndTricks": "提示与技巧(&&T)", + "miTwitter": "在 Twitter 上加入我们(&&J)", + "miUserVoice": "搜索功能请求(&&S)", + "miLicense": "查看许可证(&&L)", + "miPrivacyStatement": "隐私声明(&&P)", + "miAbout": "关于(&&A)", + "miRunTask": "运行任务(&&R)...", + "miBuildTask": "运行生成任务(&&B)...", + "miRunningTask": "显示正在运行的任务(&&G)...", + "miRestartTask": "重启正在运行的任务(&&E)...", + "miTerminateTask": "终止任务(&&T)...", + "miConfigureTask": "配置任务(&&C)", + "miConfigureBuildTask": "配置默认生成任务(&&F)", + "accessibilityOptionsWindowTitle": "辅助功能选项", + "miRestartToUpdate": "重启以更新...", + "miCheckingForUpdates": "正在检查更新...", + "miDownloadUpdate": "下载可用更新", + "miDownloadingUpdate": "正在下载更新...", + "miInstallingUpdate": "正在安装更新...", + "miCheckForUpdates": "检查更新...", + "aboutDetail": "\n版本 {0}\n提交 {1}\n日期 {2}\nShell {3}\n渲染器 {4}\nNode {5}\n架构 {6}", + "okButton": "确定" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/code/electron-main/window.i18n.json b/i18n/chs/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..da1af9c982 --- /dev/null +++ b/i18n/chs/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "你仍可以通过按 **Alt** 键访问菜单栏。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/code/electron-main/windows.i18n.json b/i18n/chs/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..2534422760 --- /dev/null +++ b/i18n/chs/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "确定", + "pathNotExistTitle": "路径不存在", + "pathNotExistDetail": "磁盘上似乎不再存在路径“{0}”。", + "openWorkspace": "打开(&&O)...", + "openWorkspaceTitle": "打开工作区", + "save": "保存(&&S)", + "doNotSave": "不保存(&&N)", + "cancel": "取消", + "saveWorkspaceMessage": "你是否要将你的工作区配置保存为文件?", + "saveWorkspaceDetail": "若要再次打开此工作区,请先保存。", + "saveWorkspace": "保存工作区", + "reopen": "重新打开", + "wait": "保持等待", + "close": "关闭", + "appStalled": "窗口不再响应", + "appStalledDetail": "你可以重新打开或关闭窗口,或者保持等待。", + "appCrashed": "窗口出现故障", + "appCrashedDetail": "我们对此引起的不便表示抱歉! 请重启该窗口从上次停止的位置继续。", + "open": "打开", + "openFolder": "打开文件夹", + "openFile": "打开文件" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/code/node/cliProcessMain.i18n.json b/i18n/chs/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..7b3c2880cb --- /dev/null +++ b/i18n/chs/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "找不到扩展“{0}”。", + "notInstalled": "未安装扩展“{0}”。", + "useId": "确保使用完整扩展 ID,包括发布服务器,如: {0}", + "successVsixInstall": "已成功安装扩展“{0}”!", + "alreadyInstalled": "已安装扩展“{0}”。", + "foundExtension": "在应用商店中找到了“{0}”。", + "installing": "正在安装...", + "successInstall": "已成功安装扩展“{0}” v{1}!", + "uninstalling": "正在卸载 {0}...", + "successUninstall": "已成功卸载扩展“{0}”!" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/chs/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..708320c323 --- /dev/null +++ b/i18n/chs/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "文件过大,无法比较。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/chs/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..7be875ed02 --- /dev/null +++ b/i18n/chs/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "关闭", + "header": "第 {0} 个差异(共 {1} 个): 未修改 {2}, {3} 行,已修改 {4}, {5} 行", + "blankLine": "空白", + "equalLine": "未修改 {0},已修改 {1}: {2}", + "insertLine": "+ 已修改 {0}: {1}", + "deleteLine": "- 未修改 {0}: {1} ", + "editor.action.diffReview.next": "转至下一个差异", + "editor.action.diffReview.prev": "转至上一个差异" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/chs/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..8fa4d1f978 --- /dev/null +++ b/i18n/chs/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "编辑器", + "fontFamily": "控制字体系列。", + "fontWeight": "控制字体粗细。", + "fontSize": "以像素为单位控制字号。", + "lineHeight": "控制行高。使用 0 通过字号计算行高。", + "letterSpacing": "以像素为单位控制字符间距。", + "lineNumbers": "控制行号的显示。可能的值为“开”、“关”和“相对”。“相对”将显示从当前光标位置开始计数的行数。", + "rulers": "显示垂直标尺的列", + "wordSeparators": "执行文字相关的导航或操作时将用作文字分隔符的字符", + "tabSize": "一个制表符等于的空格数。该设置在 `editor.detectIndentation` 启用时根据文件内容进行重写。", + "tabSize.errorMessage": "应为“number”。注意,值“auto”已由“editor.detectIndentation”设置替换。", + "insertSpaces": "按 \"Tab\" 时插入空格。该设置在 `editor.detectIndentation` 启用时根据文件内容进行重写。", + "insertSpaces.errorMessage": "应为 \"boolean\"。注意,值 \"auto\" 已由 \"editor.detectIndentation\" 设置替换。", + "detectIndentation": "当打开文件时,将基于文件内容检测 \"editor.tabSize\" 和 \"editor.insertSpaces\"。", + "roundedSelection": "控制选取范围是否有圆角", + "scrollBeyondLastLine": "控制编辑器是否可以滚动到最后一行之后", + "smoothScrolling": "控制编辑器是否在滚动时使用动画", + "minimap.enabled": "控制是否显示 minimap", + "minimap.showSlider": "控制是否自动隐藏小地图滑块。可选值为 \"always\" 和 \"mouseover\"", + "minimap.renderCharacters": "呈现某行上的实际字符(与颜色块相反)", + "minimap.maxColumn": "限制最小映射的宽度,尽量多地呈现特定数量的列", + "find.seedSearchStringFromSelection": "控制是否将编辑器的选中内容作为搜索词填入到查找组件", + "find.autoFindInSelection": "控制当编辑器中选中多个字符或多行文字时是否开启“在选定内容中查找”选项 ", + "wordWrap.off": "永不换行。", + "wordWrap.on": "将在视区宽度处换行。", + "wordWrap.wordWrapColumn": "将在 \"editor.wordWrapColumn\" 处换行。", + "wordWrap.bounded": "将在最小视区和 \"editor.wordWrapColumn\" 处换行。", + "wordWrap": "控制折行方式。可以选择: - “off” (禁用折行), - “on” (视区折行), - “wordWrapColumn”(在“editor.wordWrapColumn”处折行)或 - “bounded”(在视区与“editor.wordWrapColumn”两者的较小者处折行)。", + "wordWrapColumn": "在 \"editor.wordWrap\" 为 \"wordWrapColumn\" 或 \"bounded\" 时控制编辑器列的换行。", + "wrappingIndent": "控制折行的缩进。可以是“none”、“same”或“indent”。", + "mouseWheelScrollSensitivity": "要对鼠标滚轮滚动事件的 \"deltaX\" 和 \"deltaY\" 使用的乘数 ", + "multiCursorModifier.ctrlCmd": "映射到“Control”(Windows 和 Linux)或“Command”(OSX)。", + "multiCursorModifier.alt": "映射到“Alt”(Windows 和 Linux)或“Option”(OSX)。", + "multiCursorModifier": "用鼠标添加多个光标时使用的修改键。“ctrlCmd”映射为“Control”(Windows 和 Linux)或“Command”(OSX)。“转到定义”和“打开链接”功能的鼠标手势将会相应调整,不与多光标修改键冲突。", + "quickSuggestions.strings": "在字符串内启用快速建议。", + "quickSuggestions.comments": "在注释内启用快速建议。", + "quickSuggestions.other": "在字符串和注释外启用快速建议。", + "quickSuggestions": "控制键入时是否应自动显示建议", + "quickSuggestionsDelay": "控制延迟多少毫秒后将显示快速建议", + "parameterHints": "启用在输入时显示含有参数文档和类型信息的小面板", + "autoClosingBrackets": "控制编辑器是否应该在左括号后自动插入右括号", + "formatOnType": "控制编辑器是否应在键入后自动设置行的格式", + "formatOnPaste": "控制编辑器是否应自动设置粘贴内容的格式。格式化程序必须可用并且能设置文档中某一范围的格式。", + "autoIndent": "控制编辑器是否应在用户键入、粘贴或移动行时自动调整缩进。语言的缩进规则必须可用。", + "suggestOnTriggerCharacters": "控制键入触发器字符时是否应自动显示建议", + "acceptSuggestionOnEnter": "控制按“Enter”键是否像按“Tab”键一样接受建议。这能帮助避免“插入新行”和“接受建议”之间的歧义。值为“smart”时表示,仅当文字改变时,按“Enter”键才能接受建议", + "acceptSuggestionOnCommitCharacter": "控制是否应在遇到提交字符时接受建议。例如,在 JavaScript 中,分号(\";\")可以为提交字符,可接受建议并键入该字符。", + "snippetSuggestions.top": "在其他建议上方显示代码片段建议。", + "snippetSuggestions.bottom": "在其他建议下方显示代码片段建议。", + "snippetSuggestions.inline": "在其他建议中穿插显示代码片段建议。", + "snippetSuggestions.none": "不显示代码片段建议。", + "snippetSuggestions": "控制是否将代码段与其他建议一起显示以及它们的排序方式。", + "emptySelectionClipboard": "控制没有选择内容的复制是否复制当前行。", + "wordBasedSuggestions": "控制是否应根据文档中的字数计算完成。", + "suggestFontSize": "建议小组件的字号", + "suggestLineHeight": "建议小组件的行高", + "selectionHighlight": "控制编辑器是否应突出显示选项的近似匹配", + "occurrencesHighlight": "控制编辑器是否应该突出显示语义符号次数", + "overviewRulerLanes": "控制可在概述标尺同一位置显示的效果数量", + "overviewRulerBorder": "控制概述标尺周围是否要绘制边框。", + "cursorBlinking": "控制光标动画样式,可能的值为 \"blink\"、\"smooth\"、\"phase\"、\"expand\" 和 \"solid\"", + "mouseWheelZoom": "通过使用鼠标滚轮同时按住 Ctrl 可缩放编辑器的字体", + "cursorStyle": "控制光标样式,接受的值为 \"block\"、\"block-outline\"、\"line\"、\"line-thin\" 、\"underline\" 和 \"underline-thin\"", + "fontLigatures": "启用字体连字", + "hideCursorInOverviewRuler": "控制光标是否应隐藏在概述标尺中。", + "renderWhitespace": "控制编辑器中呈现空白字符的方式,可能为“无”、“边界”和“全部”。“边界”选项不会在单词之间呈现单空格。", + "renderControlCharacters": "控制编辑器是否应呈现控制字符", + "renderIndentGuides": "控制编辑器是否应呈现缩进参考线", + "renderLineHighlight": "控制编辑器应如何呈现当前行突出显示,可能为“无”、“装订线”、“线”和“全部”。", + "codeLens": "控制编辑器是否显示代码滤镜", + "folding": "控制编辑器是否启用代码折叠功能", + "showFoldingControls": "控制是否自动隐藏导航线上的折叠控件。", + "matchBrackets": "当选择其中一项时,将突出显示匹配的括号。", + "glyphMargin": "控制编辑器是否应呈现垂直字形边距。字形边距最常用于调试。", + "useTabStops": "在制表位后插入和删除空格", + "trimAutoWhitespace": "删除尾随自动插入的空格", + "stablePeek": "即使在双击编辑器内容或按 Esc 键时,也要保持速览编辑器的打开状态。", + "dragAndDrop": "控制编辑器是否应该允许通过拖放移动所选项。", + "accessibilitySupport.auto": "编辑器将使用平台 API 以检测是否附加了屏幕阅读器。", + "accessibilitySupport.on": "编辑器将对屏幕阅读器的使用进行永久优化。", + "accessibilitySupport.off": "编辑器将不再对屏幕阅读器的使用进行优化。", + "accessibilitySupport": "控制编辑器是否应运行在对屏幕阅读器进行优化的模式。", + "links": "控制编辑器是否应检测链接并使它们可被点击", + "colorDecorators": "控制编辑器是否显示内联颜色修饰器和颜色选取器。", + "sideBySide": "控制 Diff 编辑器以并排或内联形式显示差异", + "ignoreTrimWhitespace": "控制差异编辑器是否将对前导空格或尾随空格的更改显示为差异", + "renderIndicators": "控制差异编辑器是否为已添加/删除的更改显示 +/- 指示符号", + "selectionClipboard": "控制是否支持 Linux 主剪贴板。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/common/config/defaultConfig.i18n.json b/i18n/chs/src/vs/editor/common/config/defaultConfig.i18n.json new file mode 100644 index 0000000000..5316a92b85 --- /dev/null +++ b/i18n/chs/src/vs/editor/common/config/defaultConfig.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorViewAccessibleLabel": "编辑器内容" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/chs/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..cca3db479b --- /dev/null +++ b/i18n/chs/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "现在无法访问编辑器。按 Alt+F1 显示选项。", + "editorViewAccessibleLabel": "编辑器内容" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/common/controller/cursor.i18n.json b/i18n/chs/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..c9a75ce384 --- /dev/null +++ b/i18n/chs/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "执行命令时出现意外异常。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/chs/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..2490d464ed --- /dev/null +++ b/i18n/chs/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "标记输入时模式失败。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/chs/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..5068038aa6 --- /dev/null +++ b/i18n/chs/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "纯文本" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/chs/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..c8104d1f29 --- /dev/null +++ b/i18n/chs/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "这些文件也已同时更改: {0}", + "summary.0": "未做编辑", + "summary.nm": "在 {1} 个文件中进行了 {0} 次编辑", + "summary.n0": "在 1 个文件中进行了 {0} 次编辑" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/chs/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..595867808e --- /dev/null +++ b/i18n/chs/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "有助于语言声明。", + "vscode.extension.contributes.languages.id": "语言 ID。", + "vscode.extension.contributes.languages.aliases": "语言的别名。", + "vscode.extension.contributes.languages.extensions": "与语言关联的文件扩展名。", + "vscode.extension.contributes.languages.filenames": "与语言关联的文件名。", + "vscode.extension.contributes.languages.filenamePatterns": "与语言关联的文件名 glob 模式。", + "vscode.extension.contributes.languages.mimetypes": "与语言关联的 Mime 类型。", + "vscode.extension.contributes.languages.firstLine": "与语言文件的第一行匹配的正则表达式。", + "vscode.extension.contributes.languages.configuration": "包含语言配置选项的文件的相对路径。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/chs/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/chs/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/chs/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..4d6366bcc2 --- /dev/null +++ b/i18n/chs/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "光标所在行高亮内容的背景颜色。", + "lineHighlightBorderBox": "光标所在行四周边框的背景颜色。", + "rangeHighlight": "突出显示范围的背景颜色,例如 \"Quick Open\" 和“查找”功能。", + "caret": "编辑器光标颜色。", + "editorCursorBackground": "编辑器光标的背景色。可以自定义块型光标覆盖字符的颜色。", + "editorWhitespaces": "编辑器中空白字符颜色。", + "editorIndentGuides": "编辑器缩进参考线颜色。", + "editorLineNumbers": "编辑器行号颜色。", + "editorRuler": "编辑器标尺的颜色。", + "editorCodeLensForeground": "编辑器 CodeLens 的前景色", + "editorBracketMatchBackground": "匹配括号的背景色", + "editorBracketMatchBorder": "匹配括号外框颜色", + "editorOverviewRulerBorder": "概览标尺边框的颜色。", + "editorGutter": "编辑器导航线的背景色。导航线包括边缘符号和行号。", + "errorForeground": "编辑器中错误波浪线的前景色。", + "errorBorder": "编辑器中错误波浪线的边框颜色。", + "warningForeground": "编辑器中警告波浪线的前景色。", + "warningBorder": "编辑器中警告波浪线的边框颜色。", + "overviewRulerRangeHighlight": "概述范围突出显示的标尺标记颜色。", + "overviewRuleError": "概述错误的标尺标记颜色。", + "overviewRuleWarning": "概述警告的标尺标记颜色。", + "overviewRuleInfo": "概述信息的标尺标记颜色。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json b/i18n/chs/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json new file mode 100644 index 0000000000..c5a42d31c1 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "introMsg": "感谢试用 VS Code 的辅助功能选项。", + "status": "状态:", + "tabFocusModeOnMsg": "在当前编辑器中按 Tab 会将焦点移动到下一个可聚焦的元素。按 {0} 来切换此行为。", + "tabFocusModeOnMsgNoKb": "在当前编辑器中按 Tab 会将焦点移动到下一个可聚焦的元素。当前无法通过键绑定触发命令 {0}。", + "tabFocusModeOffMsg": "在当前编辑器中按 Tab 将插入制表符。按 {0} 来切换此行为。", + "tabFocusModeOffMsgNoKb": "在当前编辑器中按 Tab 会插入制表符。当前无法通过键绑定触发命令 {0}。", + "outroMsg": "你可以按 Esc 键来消除此提示并返回到编辑器。", + "ShowAccessibilityHelpAction": "显示辅助功能帮助" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/chs/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..c3ce3a0311 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "转到括号" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/chs/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..5a4abf8324 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "将插入点左移", + "caret.moveRight": "将插入点右移" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/chs/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..892f7e5353 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "转置字母" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/chs/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..5ebec2b786 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "剪切", + "actions.clipboard.copyLabel": "复制", + "actions.clipboard.pasteLabel": "粘贴", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "复制并突出显示语法" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/chs/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..cb060b9c65 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "切换行注释", + "comment.line.add": "添加行注释", + "comment.line.remove": "删除行注释", + "comment.block": "切换块注释" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/chs/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..d14d7d38db --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "显示编辑器上下文菜单" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/chs/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..5f759a73a8 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "查找", + "placeholder.find": "查找", + "label.previousMatchButton": "上一个匹配", + "label.nextMatchButton": "下一个匹配", + "label.toggleSelectionFind": "在选定内容中查找", + "label.closeButton": "关闭", + "label.replace": "替换", + "placeholder.replace": "替换", + "label.replaceButton": "替换", + "label.replaceAllButton": "全部替换", + "label.toggleReplaceButton": "切换替换模式", + "title.matchesCountLimit": "仅前 999 个结果突出显示,但所有查找操作均针对整个文本。", + "label.matchesLocation": "第 {0} 个(共 {1} 个)", + "label.noResults": "无结果" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/chs/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..5bd366b9b3 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "查找", + "placeholder.find": "查找", + "label.previousMatchButton": "上一个匹配", + "label.nextMatchButton": "下一个匹配", + "label.closeButton": "关闭" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/chs/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..978d454202 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "查找", + "findNextMatchAction": "查找下一个", + "findPreviousMatchAction": "查找上一个", + "nextSelectionMatchFindAction": "查找下一个选择", + "previousSelectionMatchFindAction": "查找上一个选择", + "startReplace": "替换", + "addSelectionToNextFindMatch": "将选择添加到下一个查找匹配项", + "addSelectionToPreviousFindMatch": "将选择内容添加到上一查找匹配项", + "moveSelectionToNextFindMatch": "将上次选择移动到下一个查找匹配项", + "moveSelectionToPreviousFindMatch": "将上个选择内容移动到上一查找匹配项", + "selectAllOccurrencesOfFindMatch": "选择所有找到的查找匹配项", + "changeAll.label": "更改所有匹配项", + "showNextFindTermAction": "显示下一个搜索结果", + "showPreviousFindTermAction": "显示上一个搜索结果" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/chs/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..cd2c6d8f0e --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "展开", + "unFoldRecursivelyAction.label": "以递归方式展开", + "foldAction.label": "折叠", + "foldRecursivelyAction.label": "以递归方式折叠", + "foldAllAction.label": "全部折叠", + "unfoldAllAction.label": "全部展开", + "foldLevelAction.label": "折叠级别 {0}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/chs/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..99c227d4c1 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "在第 {0} 行进行了 1 次格式编辑", + "hintn1": "在第 {1} 行进行了 {0} 次格式编辑", + "hint1n": "第 {0} 行到第 {1} 行间进行了 1 次格式编辑", + "hintnn": "第 {1} 行到第 {2} 行间进行了 {0} 次格式编辑", + "formatDocument.label": "格式化文件", + "formatSelection.label": "格式化选定代码" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json b/i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json new file mode 100644 index 0000000000..39a285da89 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "未找到“{0}”的任何定义", + "generic.noResults": "找不到定义", + "meta.title": " – {0} 定义", + "actions.goToDecl.label": "转到定义", + "actions.goToDeclToSide.label": "打开侧边的定义", + "actions.previewDecl.label": "查看定义", + "goToImplementation.noResultWord": "未找到“{0}”的实现", + "goToImplementation.generic.noResults": "未找到实现", + "meta.implementations.title": "– {0} 个实现", + "actions.goToImplementation.label": "转到实现", + "actions.peekImplementation.label": "速览实现", + "goToTypeDefinition.noResultWord": "未找到“{0}”的类型定义", + "goToTypeDefinition.generic.noResults": "未找到类型定义", + "meta.typeDefinitions.title": " – {0} 个类型定义", + "actions.goToTypeDefinition.label": "转到类型定义", + "actions.peekTypeDefinition.label": "快速查看类型定义", + "multipleResults": "单击显示 {0} 个定义。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..9945aba4a7 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "未找到“{0}”的任何定义", + "generic.noResults": "找不到定义", + "meta.title": " – {0} 定义", + "actions.goToDecl.label": "转到定义", + "actions.goToDeclToSide.label": "打开侧边的定义", + "actions.previewDecl.label": "查看定义", + "goToImplementation.noResultWord": "未找到“{0}”的实现", + "goToImplementation.generic.noResults": "未找到实现", + "meta.implementations.title": "– {0} 个实现", + "actions.goToImplementation.label": "转到实现", + "actions.peekImplementation.label": "速览实现", + "goToTypeDefinition.noResultWord": "未找到“{0}”的类型定义", + "goToTypeDefinition.generic.noResults": "未找到类型定义", + "meta.typeDefinitions.title": " – {0} 个类型定义", + "actions.goToTypeDefinition.label": "转到类型定义", + "actions.peekTypeDefinition.label": "快速查看类型定义" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..0fc0c18ffb --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "单击显示 {0} 个定义。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/chs/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..a75fe50394 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "转到下一个错误或警告", + "markerAction.previous.label": "转到上一个错误或警告", + "editorMarkerNavigationError": "编辑器标记导航小组件错误颜色。", + "editorMarkerNavigationWarning": "编辑器标记导航小组件警告颜色。", + "editorMarkerNavigationBackground": "编辑器标记导航小组件背景色。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/chs/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..275e544002 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "显示悬停" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/chs/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..42eca1001a --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "正在加载..." +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/chs/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..49e2d9b967 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "替换为上一个值", + "InPlaceReplaceAction.next.label": "替换为下一个值" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/chs/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..16c138ee0e --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "将缩进转换为空格", + "indentationToTabs": "将缩进转换为制表符", + "configuredTabSize": "已配置制表符大小", + "selectTabWidth": "选择当前文件的制表符大小", + "indentUsingTabs": "使用“Tab”缩进", + "indentUsingSpaces": "使用空格缩进", + "detectIndentation": "检查内容中的缩进", + "editor.reindentlines": "重新缩进行" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json b/i18n/chs/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f6024434ea --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "开发者: 检查 TM 作用域", + "inspectTMScopesWidget.loading": "正在加载..." +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/chs/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..6e1b36acb6 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "向上复制行", + "lines.copyDown": "向下复制行", + "lines.moveUp": "向上移动行", + "lines.moveDown": "向下移动行", + "lines.sortAscending": "按升序排列行", + "lines.sortDescending": "按降序排列行", + "lines.trimTrailingWhitespace": "裁剪尾随空格", + "lines.delete": "删除行", + "lines.indent": "行缩进", + "lines.outdent": "行减少缩进", + "lines.insertBefore": "在上面插入行", + "lines.insertAfter": "在下面插入行", + "lines.deleteAllLeft": "删除左侧所有内容", + "lines.deleteAllRight": "删除右侧所有内容", + "lines.joinLines": "合并行", + "editor.transpose": "转置游标处的字符", + "editor.transformToUppercase": "转换为大写", + "editor.transformToLowercase": "转换为小写" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/chs/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..18c350b1c0 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "Cmd + 单击以跟踪链接", + "links.navigate": "Ctrl + 单击以跟踪链接", + "links.command.mac": "Cmd + 单击以执行命令", + "links.command": "Ctrl + 单击以执行命令", + "links.navigate.al": "Alt + 单击以访问链接", + "links.command.al": "Alt + 单击以执行命令", + "invalid.url": "抱歉,无法打开此链接,因为其格式不正确: {0}", + "missing.url": "抱歉,无法打开此链接,因为其目标丢失。", + "label": "打开链接" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/chs/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..3163732b8f --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "在上面添加光标", + "mutlicursor.insertBelow": "在下面添加光标", + "mutlicursor.insertAtEndOfEachLineSelected": "在行尾添加光标" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/chs/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..eaa1348149 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "触发参数提示" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/chs/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..594503e6de --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0},提示" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/chs/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..5165ab5956 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "显示修补程序({0})", + "quickFix": "显示修补程序", + "quickfix.trigger.label": "快速修复" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..ae0ba89ea5 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": " – {0} 个引用", + "references.action.label": "查找所有引用" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..a904374fb1 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "正在加载..." +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..66518d98cd --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "在文件 {0} 的 {1} 行 {2} 列的符号", + "aria.fileReferences.1": "{0} 中有 1 个符号,完整路径:{1}", + "aria.fileReferences.N": "{1} 中有 {0} 个符号,完整路径:{2}", + "aria.result.0": "未找到结果", + "aria.result.1": "在 {0} 中找到 1 个符号", + "aria.result.n1": "在 {1} 中找到 {0} 个符号", + "aria.result.nm": "在 {1} 个文件中找到 {0} 个符号" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..fe98a8e7c0 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "解析文件失败。", + "referencesCount": "{0} 个引用", + "referenceCount": "{0} 个引用", + "missingPreviewMessage": "无可用预览", + "treeAriaLabel": "引用", + "noResults": "无结果", + "peekView.alternateTitle": "引用", + "peekViewTitleBackground": "速览视图标题区域背景颜色。", + "peekViewTitleForeground": "速览视图标题颜色。", + "peekViewTitleInfoForeground": "速览视图标题信息颜色。", + "peekViewBorder": "速览视图边框和箭头颜色。", + "peekViewResultsBackground": "速览视图结果列表背景颜色。", + "peekViewResultsMatchForeground": "速览视图结果列表中行节点的前景色。", + "peekViewResultsFileForeground": "速览视图结果列表中文件节点的前景色。", + "peekViewResultsSelectionBackground": "速览视图结果列表中所选条目的背景颜色。", + "peekViewResultsSelectionForeground": "速览视图结果列表中所选条目的前景色。", + "peekViewEditorBackground": "速览视图编辑器背景颜色。", + "peekViewEditorGutterBackground": "速览视图编辑器中导航线的背景颜色。", + "peekViewResultsMatchHighlight": "在速览视图结果列表中匹配突出显示颜色。", + "peekViewEditorMatchHighlight": "在速览视图编辑器中匹配突出显示颜色。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/chs/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..dc6ccb98ce --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "无结果。", + "aria": "成功将“{0}”重命名为“{1}”。摘要:{2}", + "rename.failed": "抱歉,重命名无法执行。", + "rename.label": "重命名符号" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/chs/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..273d7872d8 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "重命名输入。键入新名称并按 \"Enter\" 提交。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/chs/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..d503181786 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "扩大选择", + "smartSelect.shrink": "缩小选择" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/chs/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..789cf8e40f --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "确认“{0}”插入以下文本:{1}", + "suggest.trigger.label": "触发建议" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/chs/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..c76ef06468 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "建议小组件的背景颜色", + "editorSuggestWidgetBorder": "建议小组件的边框颜色", + "editorSuggestWidgetForeground": "建议小组件的前景颜色。", + "editorSuggestWidgetSelectedBackground": "建议小组件中被选择条目的背景颜色。", + "editorSuggestWidgetHighlightForeground": "建议小组件中匹配内容的高亮颜色。", + "readMore": "阅读详细信息...{0}", + "suggestionWithDetailsAriaLabel": "{0}(建议)具有详细信息", + "suggestionAriaLabel": "{0},建议", + "readLess": "阅读简略信息...{0}", + "suggestWidget.loading": "正在加载...", + "suggestWidget.noSuggestions": "无建议。", + "suggestionAriaAccepted": "{0},已接受", + "ariaCurrentSuggestionWithDetails": "{0}(建议)具有详细信息", + "ariaCurrentSuggestion": "{0},建议" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/chs/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..76dc6af404 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "切换 Tab 键是否移动焦点" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/chs/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..57376afca8 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "读取访问时符号的背景颜色,例如读取变量时。", + "wordHighlightStrong": "写入访问时符号的背景颜色,例如写入变量时。", + "overviewRulerWordHighlightForeground": "概述符号突出显示的标尺标记颜色。", + "overviewRulerWordHighlightStrongForeground": "概述写访问符号突出显示的标尺标记颜色。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/chs/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..6d896f0ab3 --- /dev/null +++ b/i18n/chs/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "关闭" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/chs/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..a9c9702c26 --- /dev/null +++ b/i18n/chs/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "“contributes.{0}.language”中存在未知的语言。提供的值: {1}", + "invalid.scopeName": "“contributes.{0}.scopeName”中应为字符串。提供的值: {1}", + "invalid.path.0": "“contributes.{0}.path”中应为字符串。提供的值: {1}", + "invalid.injectTo": "\"contributes.{0}.injectTo\" 中的值无效。必须为语言范围名称数组。提供的值: {1}", + "invalid.embeddedLanguages": "\"contributes.{0}.embeddedLanguages\" 中的值无效。必须为从作用域名称到语言的对象映射。提供的值: {1}", + "invalid.path.1": "“contributes.{0}.path”({1})应包含在扩展的文件夹({2})内。这可能会使扩展不可移植。", + "no-tm-grammar": "没有注册这种语言的 TM 语法。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/chs/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..52e58dda52 --- /dev/null +++ b/i18n/chs/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "错误分析 {0}: {1}", + "schema.openBracket": "左方括号字符或字符串序列。", + "schema.closeBracket": "右方括号字符或字符串序列。", + "schema.comments": "定义注释符号", + "schema.blockComments": "定义块注释的标记方式。", + "schema.blockComment.begin": "作为块注释开头的字符序列。", + "schema.blockComment.end": "作为块注释结尾的字符序列。", + "schema.lineComment": "作为行注释开头的字符序列。", + "schema.brackets": "定义增加和减少缩进的括号。", + "schema.autoClosingPairs": "定义括号对。当输入左方括号时,将自动插入右方括号。", + "schema.autoClosingPairs.notIn": "定义禁用了自动配对的作用域列表。", + "schema.surroundingPairs": "定义可用于包围所选字符串的括号对。", + "schema.wordPattern": "此语言的文本定义。", + "schema.wordPattern.pattern": "用于匹配文本的正则表达式模式。", + "schema.wordPattern.flags": "用于匹配文本的正则表达式标志。", + "schema.wordPattern.flags.errorMessage": "必须匹配模式“/^([gimuy]+)$/”。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/chs/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..2086a50c94 --- /dev/null +++ b/i18n/chs/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "贡献 textmate tokenizer。", + "vscode.extension.contributes.grammars.language": "此语法为其贡献了内容的语言标识符。", + "vscode.extension.contributes.grammars.scopeName": "tmLanguage 文件所用的 textmate 范围名称。", + "vscode.extension.contributes.grammars.path": "tmLanguage 文件的路径。该路径是相对于扩展文件夹,通常以 \"./syntaxes/\" 开头。", + "vscode.extension.contributes.grammars.embeddedLanguages": "如果此语法包含嵌入式语言,则为作用域名称到语言 ID 的映射。", + "vscode.extension.contributes.grammars.injectTo": "此语法注入到的语言范围名称列表。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/chs/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/chs/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/chs/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..60c2fa8fad --- /dev/null +++ b/i18n/chs/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "菜单项必须为一个数组", + "requirestring": "属性“{0}”是必要属性,其类型必须是“string”", + "optstring": "属性“{0}”可以被省略,否则其类型必须为“string”", + "vscode.extension.contributes.menuItem.command": "要执行的命令的标识符。该命令必须在 \"commands\" 部分中声明", + "vscode.extension.contributes.menuItem.alt": "要执行的替代命令的标识符。该命令必须在“命令”部分中声明", + "vscode.extension.contributes.menuItem.when": "为显示此项必须为 \"ture\" 的条件", + "vscode.extension.contributes.menuItem.group": "此命令所属的组", + "vscode.extension.contributes.menus": "向编辑器提供菜单项", + "menus.commandPalette": "命令面板", + "menus.editorTitle": "编辑器标题菜单", + "menus.editorContext": "编辑器上下文菜单", + "menus.explorerContext": "文件资源管理器上下文菜单", + "menus.editorTabContext": "编辑器选项卡上下文菜单", + "menus.debugCallstackContext": "调试调用堆栈上下文菜单", + "menus.scmTitle": "源代码管理标题菜单", + "menus.resourceGroupContext": "源代码管理资源组上下文菜单", + "menus.resourceStateContext": "源代码管理资源状态上下文菜单", + "view.viewTitle": "提供的视图的标题菜单", + "view.itemContext": "提供的视图中的项目的上下文菜单", + "nonempty": "应为非空值。", + "opticon": "可以省略属性“图标”或者它必须是一个字符串或类似“{dark, light}”的文本", + "requireStringOrObject": "属性“{0}”为必需且其类型必须为“字符串”或“对象”", + "requirestrings": "属性“{0}”和“{1}`”为必需且其类型必须为“字符串”", + "vscode.extension.contributes.commandType.command": "要执行的命令的标识符", + "vscode.extension.contributes.commandType.title": "在 UI 中依据其表示命令的标题", + "vscode.extension.contributes.commandType.category": "(可选)类别字符串按命令在 UI 中分组", + "vscode.extension.contributes.commandType.icon": "(可选)在 UI 中用来表示命令的图标。文件路径或者主题配置", + "vscode.extension.contributes.commandType.icon.light": "使用浅色主题时的图标路径", + "vscode.extension.contributes.commandType.icon.dark": "使用深色主题时的图标路径", + "vscode.extension.contributes.commands": "对命令面板提供命令。", + "dup": "命令“{0}”多次出现在“命令”部分。", + "menuId.invalid": "“{0}”为无效菜单标识符", + "missing.command": "菜单项引用未在“命令”部分进行定义的命令“{0}”。", + "missing.altCommand": "菜单项引用未在“命令”部分进行定义的 alt 命令“{0}”。", + "dupe.command": "菜单项引用与默认和 alt 命令相同的命令", + "nosupport.altCommand": "抱歉,目前仅有“editor/title”菜单的“navigation”组支持 alt 命令" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/chs/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..49b1c9f557 --- /dev/null +++ b/i18n/chs/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "默认配置替代", + "overrideSettings.description": "针对 {0} 语言,配置替代编辑器设置。", + "overrideSettings.defaultDescription": "针对某种语言,配置替代编辑器设置。", + "config.property.languageDefault": "无法注册“{0}”。其符合描述特定语言编辑器设置的表达式 \"\\\\[.*\\\\]$\"。请使用 \"configurationDefaults\"。", + "config.property.duplicate": "无法注册“{0}”。此属性已注册。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/environment/node/argv.i18n.json b/i18n/chs/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..0890acf0d5 --- /dev/null +++ b/i18n/chs/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "\"--goto\" 模式中的参数格式应为 \"FILE(:LINE(:CHARACTER))\"。", + "diff": "将两个文件相互比较。", + "add": "将文件夹添加到最后一个活动窗口。", + "goto": "打开路径下的文件并定位到特定行和特定字符。", + "locale": "要使用的区域设置(例如 en-US 或 zh-TW)。", + "newWindow": "强制创建一个新的 Code 实例。", + "performance": "通过启用 \"Developer: Startup Performance\" 命令开始。", + "prof-startup": "启动期间运行 CPU 探查器", + "reuseWindow": "在上一活动窗口中强制打开文件或文件夹。", + "userDataDir": "指定存放用户数据的目录,此目录在作为根运行时十分有用。", + "verbose": "打印详细输出(表示 - 等待)。", + "wait": "等窗口关闭后再返回。", + "extensionHomePath": "设置扩展的根路径。", + "listExtensions": "列出已安装的扩展。", + "showVersions": "使用 --list-extension 时,显示已安装扩展的版本。", + "installExtension": "安装扩展。", + "uninstallExtension": "卸载扩展。", + "experimentalApis": "启用扩展程序实验性 api 功能。", + "disableExtensions": "禁用所有已安装的扩展。", + "disableGPU": "禁用 GPU 硬件加速。", + "version": "打印版本。", + "help": "打印使用情况。", + "usage": "使用情况", + "options": "选项", + "paths": "路径", + "optionsUpperCase": "选项" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/chs/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..430638e42e --- /dev/null +++ b/i18n/chs/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "无工作区" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/chs/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..1eb773c7ec --- /dev/null +++ b/i18n/chs/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "扩展", + "preferences": "首选项" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/chs/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..fd3a3dfd22 --- /dev/null +++ b/i18n/chs/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "未找到扩展名", + "noCompatible": "找不到可与此代码版本兼容的 {0} 版本。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/chs/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..b3d2d69d68 --- /dev/null +++ b/i18n/chs/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "扩展无效: package.json 不是 JSON 文件。", + "restartCode": "请先重启 Code 再重新安装 {0}。", + "installDependeciesConfirmation": "安装”{0}“还会安装其依赖项。是否要继续?", + "install": "是", + "doNotInstall": "否", + "uninstallDependeciesConfirmation": "要仅卸载“{0}”或者其依赖项也一起卸载?", + "uninstallOnly": "仅", + "uninstallAll": "全部", + "cancel": "取消", + "uninstallConfirmation": "是否确定要卸载“{0}”?", + "ok": "确定", + "singleDependentError": "无法卸载扩展程序“{0}”。扩展程序“{1}”依赖于此。", + "twoDependentsError": "无法卸载扩展程序“{0}”。扩展程序“{1}”、“{2}”依赖于此。", + "multipleDependentsError": "无法卸载扩展程序“{0}”。扩展程序“{1}”、“{2}”以及其他扩展程序都依赖于此。", + "notExists": "找不到扩展" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/chs/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..9ca24fae43 --- /dev/null +++ b/i18n/chs/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "无法激活扩展”{1}“。原因: 未知依赖关系“{0}”。", + "failedDep1": "无法激活扩展”{1}“。原因: 无法激活依赖关系”{0}“。", + "failedDep2": "无法激活扩展”{0}“。原因: 依赖关系多于 10 级(最可能是依赖关系循环)。", + "activationError": "激活扩展“{0}”失败: {1}。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/chs/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..b163622532 --- /dev/null +++ b/i18n/chs/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "对于 VS Code 扩展程序,指定该扩展程序与之兼容的 VS Code 版本。不能为 *. 例如: ^0.10.5 表示最低兼容 VS Code 版本 0.10.5。", + "vscode.extension.publisher": "VS Code 扩展的发布服务器。", + "vscode.extension.displayName": "VS Code 库中使用的扩展的显示名称。", + "vscode.extension.categories": "VS Code 库用于对扩展进行分类的类别。", + "vscode.extension.galleryBanner": "VS Code 商城使用的横幅。", + "vscode.extension.galleryBanner.color": "VS Code 商城页标题上的横幅颜色。", + "vscode.extension.galleryBanner.theme": "横幅中使用的字体颜色主题。", + "vscode.extension.contributes": "由此包表示的 VS Code 扩展的所有贡献。", + "vscode.extension.preview": "在 Marketplace 中设置扩展,将其标记为“预览”。", + "vscode.extension.activationEvents": "VS Code 扩展的激活事件。", + "vscode.extension.activationEvents.onLanguage": "在打开被解析为指定语言的文件时发出的激活事件。", + "vscode.extension.activationEvents.onCommand": "在调用指定命令时发出的激活事件。", + "vscode.extension.activationEvents.onDebug": "在指定类型的调试会话开始时发出的激活事件。", + "vscode.extension.activationEvents.workspaceContains": "在打开至少包含一个匹配指定 glob 模式的文件的文件夹时发出的激活事件。", + "vscode.extension.activationEvents.onView": "在指定视图被展开时发出的激活事件。", + "vscode.extension.activationEvents.star": "在 VS Code 启动时发出的激活事件。为确保良好的最终用户体验,请仅在其他激活事件组合不适用于你的情况时,才在扩展中使用此事件。", + "vscode.extension.badges": "在 Marketplace 的扩展页边栏中显示的徽章数组。", + "vscode.extension.badges.url": "徽章图像 URL。", + "vscode.extension.badges.href": "徽章链接。", + "vscode.extension.badges.description": "徽章说明。", + "vscode.extension.extensionDependencies": "其他扩展的依赖关系。扩展的标识符始终是 ${publisher}.${name}。例如: vscode.csharp。", + "vscode.extension.scripts.prepublish": "包作为 VS Code 扩展发布前执行的脚本。", + "vscode.extension.icon": "128 x 128 像素图标的路径。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/chs/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..9407f81019 --- /dev/null +++ b/i18n/chs/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "无法分析 \"engines.vscode\" 值 {0}。例如请使用: ^0.10.0、^1.2.3、^0.11.0、^0.10.x 等。", + "versionSpecificity1": "\"engines.vscode\" ({0}) 中指定的版本不够具体。对于 1.0.0 之前的 vscode 版本,请至少定义主要和次要想要的版本。例如: ^0.10.0、0.10.x、0.11.0 等。", + "versionSpecificity2": "\"engines.vscode\" ({0}) 中指定的版本不够具体。对于 1.0.0 之后的 vscode 版本,请至少定义主要想要的版本。例如: ^1.10.0、1.10.x、1.x.x、2.x.x 等。", + "versionMismatch": "扩展与 Code {0} 不兼容。扩展需要: {1}。", + "extensionDescription.empty": "已获得空扩展说明", + "extensionDescription.publisher": "属性“{0}”是必要属性,其类型必须是 \"string\"", + "extensionDescription.name": "属性“{0}”是必要属性,其类型必须是 \"string\"", + "extensionDescription.version": "属性“{0}”是必要属性,其类型必须是 \"string\"", + "extensionDescription.engines": "属性“{0}”是必要属性,其类型必须是 \"object\"", + "extensionDescription.engines.vscode": "属性“{0}”是必要属性,其类型必须是 \"string\"", + "extensionDescription.extensionDependencies": "属性“{0}”可以省略,否则其类型必须是 \"string[]\"", + "extensionDescription.activationEvents1": "属性“{0}”可以省略,否则其类型必须是 \"string[]\"", + "extensionDescription.activationEvents2": "必须同时指定或同时省略属性”{0}“和”{1}“", + "extensionDescription.main1": "属性“{0}”可以省略,否则其类型必须是 \"string\"", + "extensionDescription.main2": "应在扩展文件夹({1})中包含 \"main\" ({0})。这可能会使扩展不可移植。", + "extensionDescription.main3": "必须同时指定或同时省略属性”{0}“和”{1}“", + "notSemver": "扩展版本与 semver 不兼容。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/chs/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..7153aea6d9 --- /dev/null +++ b/i18n/chs/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "新建窗口", + "newWindowDesc": "打开一个新窗口", + "recentFolders": "最近使用的工作区", + "folderDesc": "{0} {1}", + "codeWorkspace": "代码工作区" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/chs/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..dd6e576f92 --- /dev/null +++ b/i18n/chs/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "确定", + "integrity.dontShowAgain": "不再显示", + "integrity.moreInfo": "详细信息", + "integrity.prompt": "{0} 安装似乎损坏。请重新安装。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/chs/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..22f40e6a94 --- /dev/null +++ b/i18n/chs/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "用于 json 架构配置。", + "contributes.jsonValidation.fileMatch": "要匹配的文件模式,例如 \"package.json\" 或 \"*.launch\"。", + "contributes.jsonValidation.url": "到扩展文件夹('./')的架构 URL (\"http:\"、\"https:\")或相对路径。", + "invalid.jsonValidation": "configuration.jsonValidation 必须是数组", + "invalid.fileMatch": "必须定义 \"configuration.jsonValidation.fileMatch\" ", + "invalid.url": "configuration.jsonValidation.url 必须是 URL 或相对路径", + "invalid.url.fileschema": "configuration.jsonValidation.url 是无效的相对 URL: {0}", + "invalid.url.schema": "configuration.jsonValidation.url 必须以 \"http:\"、\"https:\" 或 \"./\" 开头以引用位于扩展中的架构。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/chs/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..393713ac8d --- /dev/null +++ b/i18n/chs/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "已按下({0})。正在等待同时按下第二个键...", + "missing.chord": "组合键({0}, {1})不是命令。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/keybinding/common/keybindingLabels.i18n.json b/i18n/chs/src/vs/platform/keybinding/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..b234e36af1 --- /dev/null +++ b/i18n/chs/src/vs/platform/keybinding/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Shift", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Shift", + "altKey.long": "Alt", + "cmdKey.long": "Command", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/chs/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..4aef9c45f5 --- /dev/null +++ b/i18n/chs/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "循环属性仅在最一个行匹配程序上受支持。", + "ProblemPatternParser.problemPattern.missingRegExp": "问题模式缺少正则表达式。", + "ProblemPatternParser.problemPattern.missingProperty": "问题模式无效。它必须至少包含一个文件、消息和行或位置匹配组。", + "ProblemPatternParser.invalidRegexp": "错误:字符串 {0} 不是有效的正则表达式。\n", + "ProblemPatternSchema.regexp": "用于在输出中查找错误、警告或信息的正则表达式。", + "ProblemPatternSchema.file": "文件名的匹配组索引。如果省略,则使用 1。", + "ProblemPatternSchema.location": "问题位置的匹配组索引。有效的位置模式为(line)、(line,column)和(startLine,startColumn,endLine,endColumn)。如果省略了,将假定(line,column)。", + "ProblemPatternSchema.line": "问题行的匹配组索引。默认值为 2", + "ProblemPatternSchema.column": "问题行字符的匹配组索引。默认值为 3", + "ProblemPatternSchema.endLine": "问题结束行的匹配组索引。默认为未定义", + "ProblemPatternSchema.endColumn": "问题结束行字符的匹配组索引。默认为未定义", + "ProblemPatternSchema.severity": "问题严重性的匹配组索引。默认为未定义", + "ProblemPatternSchema.code": "问题代码的匹配组索引。默认为未定义", + "ProblemPatternSchema.message": "消息的匹配组索引。如果省略,则在指定了位置时默认值为 4,在其他情况下默认值为 5。", + "ProblemPatternSchema.loop": "在多行中,匹配程序循环指示是否只要匹配就在循环中执行此模式。只能在多行模式的最后一个模式上指定。", + "NamedProblemPatternSchema.name": "问题模式的名称。", + "NamedMultiLineProblemPatternSchema.name": "问题多行问题模式的名称。", + "NamedMultiLineProblemPatternSchema.patterns": "实际模式。", + "ProblemPatternExtPoint": "提供问题模式", + "ProblemPatternRegistry.error": "无效问题模式。此模式将被忽略。", + "ProblemMatcherParser.noProblemMatcher": "错误: 描述无法转换为问题匹配程序:\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "错误: 描述未定义有效的问题模式:\n{0}\n", + "ProblemMatcherParser.noOwner": "错误: 描述未定义所有者:\n{0}\n", + "ProblemMatcherParser.noFileLocation": "错误: 描述未定义文件位置:\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "信息:未知严重性 {0}。有效值为“error”、“warning”和“info”。\n", + "ProblemMatcherParser.noDefinedPatter": "错误: 标识符为 {0} 的模式不存在。", + "ProblemMatcherParser.noIdentifier": "错误: 模式属性引用空标识符。", + "ProblemMatcherParser.noValidIdentifier": "错误: 模式属性 {0} 是无效的模式变量名。", + "ProblemMatcherParser.problemPattern.watchingMatcher": "问题匹配程序必须定义监视的开始模式和结束模式。", + "ProblemMatcherParser.invalidRegexp": "错误: 字符串 {0} 不是有效的正则表达式。\n", + "WatchingPatternSchema.regexp": "用于检测后台任务开始或结束的正则表达式。", + "WatchingPatternSchema.file": "文件名的匹配组索引。可以省略。", + "PatternTypeSchema.name": "所提供或预定义模式的名称", + "PatternTypeSchema.description": "问题模式或者所提供或预定义问题模式的名称。如果已指定基准,则可以省略。", + "ProblemMatcherSchema.base": "要使用的基问题匹配程序的名称。", + "ProblemMatcherSchema.owner": "代码内问题的所有者。如果指定了基准,则可省略。如果省略,并且未指定基准,则默认值为“外部”。", + "ProblemMatcherSchema.severity": "捕获问题的默认严重性。如果模式未定义严重性的匹配组,则使用。", + "ProblemMatcherSchema.applyTo": "控制文本文档上报告的问题是否仅应用于打开、关闭或所有文档。", + "ProblemMatcherSchema.fileLocation": "定义应如何解释问题模式中报告的文件名。", + "ProblemMatcherSchema.background": "用于跟踪在后台任务上激活的匹配程序的开始和结束的模式。", + "ProblemMatcherSchema.background.activeOnStart": "如果设置为 true,则会在任务开始时激活后台监控。这相当于发出与 beginPattern 匹配的行。", + "ProblemMatcherSchema.background.beginsPattern": "如果在输出内匹配,则会发出后台任务开始的信号。", + "ProblemMatcherSchema.background.endsPattern": "如果在输出内匹配,则会发出后台任务结束的信号。", + "ProblemMatcherSchema.watching.deprecated": "“watching”属性已被弃用。请改用“background”。", + "ProblemMatcherSchema.watching": "用于跟踪监视匹配程序开始和结束的模式。", + "ProblemMatcherSchema.watching.activeOnStart": "如果设置为 true,则当任务开始时观察程序处于活动模式。这相当于发出与 beginPattern 匹配的行。", + "ProblemMatcherSchema.watching.beginsPattern": "如果在输出内匹配,则在监视任务开始时会发出信号。", + "ProblemMatcherSchema.watching.endsPattern": "如果在输出内匹配,则在监视任务结束时会发出信号。", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "此属性已弃用。请改用观看属性。", + "LegacyProblemMatcherSchema.watchedBegin": "一个正则表达式,发出受监视任务开始执行(通过文件监视触发)的信号。", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "此属性已弃用。请改用观看属性。", + "LegacyProblemMatcherSchema.watchedEnd": "一个正则表达式,发出受监视任务结束执行的信号。", + "NamedProblemMatcherSchema.name": "要引用的问题匹配程序的名称。", + "NamedProblemMatcherSchema.label": "问题匹配程序的人类可读标签。", + "ProblemMatcherExtPoint": "提供问题匹配程序", + "msCompile": "微软编译器问题", + "lessCompile": "Less 问题", + "gulp-tsc": "Gulp TSC 问题", + "jshint": "JSHint 问题", + "jshint-stylish": "JSHint stylish 问题", + "eslint-compact": "ESLint compact 问题", + "eslint-stylish": "ESLint stylish 问题", + "go": "Go 问题" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/message/common/message.i18n.json b/i18n/chs/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..3c036be39c --- /dev/null +++ b/i18n/chs/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "关闭", + "later": "稍后", + "cancel": "取消" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/request/node/request.i18n.json b/i18n/chs/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..9fd9c35110 --- /dev/null +++ b/i18n/chs/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "要使用的代理设置。如果尚未设置,则将从 http_proxy 和 https_proxy 环境变量获取", + "strictSSL": "是否应根据提供的 CA 列表验证代理服务器证书。", + "proxyAuthorization": "要作为每个网络请求的 \"Proxy-Authorization\" 标头发送的值。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/chs/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..3d003e1ca2 --- /dev/null +++ b/i18n/chs/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "遥测", + "telemetry.enableTelemetry": "启用要发送给 Microsoft 的使用情况数据和错误。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/chs/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..a26272a54f --- /dev/null +++ b/i18n/chs/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "提供由扩展定义的主题颜色", + "contributes.color.id": "主题颜色标识符", + "contributes.color.id.format": "标识符应满足 aa[.bb]*", + "contributes.color.description": "主题颜色描述", + "contributes.defaults.light": "浅色主题的默认颜色。应为十六进制颜色值 (#RRGGBB[AA]) 或是主题颜色标识符,其提供默认值。", + "contributes.defaults.dark": "深色主题的默认颜色。应为十六进制颜色值 (#RRGGBB[AA]) 或是主题颜色标识符,其提供默认值。", + "contributes.defaults.highContrast": "高对比度主题的默认颜色。应为十六进制颜色值 (#RRGGBB[AA]) 或是主题颜色标识符,其提供默认值。", + "invalid.colorConfiguration": "\"configuration.colors\" 必须是数组", + "invalid.default.colorType": "{0} 必须为十六进制颜色值 (#RRGGBB[AA] 或 #RGB[A]) 或是主题颜色标识符,其提供默认值。", + "invalid.id": "必须定义 \"configuration.colors.id\",且不能为空", + "invalid.id.format": "\"configuration.colors.id\" 必须满足 word[.word]*", + "invalid.description": "必须定义 \"configuration.colors.description\",且不能为空", + "invalid.defaults": "必须定义 “configuration.colors.defaults”,且须包含 \"light\"(浅色)、\"dark\"(深色) 和 \"highContrast\"(高对比度)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/chs/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..c5aac9691d --- /dev/null +++ b/i18n/chs/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "颜色格式无效。请使用 #RGB、#RGBA、#RRGGBB 或 #RRGGBBAA", + "schema.colors": "工作台中使用的颜色。", + "foreground": "整体前景色。此颜色仅在不被组件覆盖时适用。", + "errorForeground": "错误信息的整体前景色。此颜色仅在不被组件覆盖时适用。", + "descriptionForeground": "提供其他信息的说明文本的前景色,例如标签文本。", + "focusBorder": "焦点元素的整体边框颜色。此颜色仅在不被其他组件覆盖时适用。", + "contrastBorder": "在元素周围额外的一层边框,用来提高对比度从而区别其他元素。", + "activeContrastBorder": "在活动元素周围额外的一层边框,用来提高对比度从而区别其他元素。", + "selectionBackground": "工作台所选文本的背景颜色(例如输入字段或文本区域)。注意,本设置不适用于编辑器。", + "textSeparatorForeground": "文字分隔符的颜色。", + "textLinkForeground": "文本中链接的前景色。", + "textLinkActiveForeground": "文本中活动链接的前景色。", + "textPreformatForeground": "预格式化文本段的前景色。", + "textBlockQuoteBackground": "文本中块引用的背景颜色。", + "textBlockQuoteBorder": "文本中块引用的边框颜色。", + "textCodeBlockBackground": "文本中代码块的背景颜色。", + "widgetShadow": "编辑器内小组件(如查找/替换)的阴影颜色。", + "inputBoxBackground": "输入框背景色。", + "inputBoxForeground": "输入框前景色。", + "inputBoxBorder": "输入框边框。", + "inputBoxActiveOptionBorder": "输入字段中已激活选项的边框颜色。", + "inputPlaceholderForeground": "输入框中占位符的前景色。", + "inputValidationInfoBackground": "严重性为信息时输入验证的背景颜色。", + "inputValidationInfoBorder": "严重性为信息时输入验证的边框颜色。", + "inputValidationWarningBackground": "严重性为警告时输入验证的背景颜色。", + "inputValidationWarningBorder": "严重性为警告时输入验证的边框颜色。", + "inputValidationErrorBackground": "严重性为错误时输入验证的背景颜色。", + "inputValidationErrorBorder": "严重性为错误时输入验证的边框颜色。", + "dropdownBackground": "下拉列表背景色。", + "dropdownForeground": "下拉列表前景色。", + "dropdownBorder": "下拉列表边框。", + "listFocusBackground": "焦点项在列表或树活动时的背景颜色。活动的列表或树具有键盘焦点,非活动的没有。", + "listFocusForeground": "焦点项在列表或树活动时的背景颜色。活动的列表或树具有键盘焦点,非活动的没有。", + "listActiveSelectionBackground": "已选项在列表或树活动时的背景颜色。活动的列表或树具有键盘焦点,非活动的没有。", + "listActiveSelectionForeground": "已选项在列表或树活动时的前景颜色。活动的列表或树具有键盘焦点,非活动的没有。", + "listInactiveSelectionBackground": "已选项在列表或树非活动时的背景颜色。活动的列表或树具有键盘焦点,非活动的没有。", + "listInactiveSelectionForeground": "已选项在列表或树非活动时的前景颜色。活动的列表或树具有键盘焦点,非活动的没有。", + "listHoverBackground": "使用鼠标移动项目时,列表或树的背景颜色。", + "listHoverForeground": "鼠标在项目上悬停时,列表或树的前景颜色。", + "listDropBackground": "使用鼠标移动项目时,列表或树进行拖放的背景颜色。", + "highlight": "在列表或树中搜索时,其中匹配内容的高亮颜色。", + "pickerGroupForeground": "快速选取器分组标签的颜色。", + "pickerGroupBorder": "快速选取器分组边框的颜色。", + "buttonForeground": "按钮前景色。", + "buttonBackground": "按钮背景色。", + "buttonHoverBackground": "按钮在悬停时的背景颜色。", + "badgeBackground": "Badge 背景色。Badge 是小型的信息标签,如表示搜索结果数量的标签。", + "badgeForeground": "Badge 前景色。Badge 是小型的信息标签,如表示搜索结果数量的标签。", + "scrollbarShadow": "表示视图被滚动的滚动条阴影。", + "scrollbarSliderBackground": "滚动条滑块背景色", + "scrollbarSliderHoverBackground": "滚动条滑块在悬停时的背景色", + "scrollbarSliderActiveBackground": "滚动条滑块被激活时的背景色", + "progressBarBackground": "表示长时间操作的进度条的背景色。", + "editorBackground": "编辑器背景颜色。", + "editorForeground": "编辑器默认前景色。", + "editorWidgetBackground": "编辑器组件(如查找/替换)背景颜色。", + "editorWidgetBorder": "编辑器小部件的边框颜色。此颜色仅在小部件有边框且不被小部件重写时适用。", + "editorSelectionBackground": "编辑器所选内容的颜色。", + "editorSelectionForeground": "用以彰显高对比度的所选文本的颜色。", + "editorInactiveSelection": "非活动编辑器中所选内容的颜色。", + "editorSelectionHighlight": "与所选内容具有相同内容的区域颜色。", + "editorFindMatch": "当前搜索匹配项的颜色。", + "findMatchHighlight": "其他搜索匹配项的颜色。", + "findRangeHighlight": "限制搜索的范围的颜色。", + "hoverHighlight": "悬停提示显示时文本底下的高亮颜色。", + "hoverBackground": "编辑器悬停提示的背景颜色。", + "hoverBorder": "光标悬停时编辑器的边框颜色。", + "activeLinkForeground": "活动链接颜色。", + "diffEditorInserted": "已插入文本的背景颜色。", + "diffEditorRemoved": "被删除文本的背景颜色。", + "diffEditorInsertedOutline": "插入的文本的轮廓颜色。", + "diffEditorRemovedOutline": "被删除文本的轮廓颜色。", + "mergeCurrentHeaderBackground": "内联合并冲突中当前版本区域的标头背景色。", + "mergeCurrentContentBackground": "内联合并冲突中当前版本区域的内容背景色。", + "mergeIncomingHeaderBackground": "内联合并冲突中传入的版本区域的标头背景色。", + "mergeIncomingContentBackground": "内联合并冲突中传入的版本区域的内容背景色。", + "mergeCommonHeaderBackground": "内联合并冲突中共同祖先区域的标头背景色。", + "mergeCommonContentBackground": "内联合并冲突中共同祖先区域的内容背景色。", + "mergeBorder": "内联合并冲突中标头和分割线的边框颜色。", + "overviewRulerCurrentContentForeground": "内联合并冲突中当前版本区域的概览标尺前景色。", + "overviewRulerIncomingContentForeground": "内联合并冲突中传入的版本区域的概览标尺前景色。", + "overviewRulerCommonContentForeground": "内联合并冲突中共同祖先区域的概览标尺前景色。", + "overviewRulerFindMatchForeground": "概述查找匹配项的标尺标记颜色。", + "overviewRulerSelectionHighlightForeground": "概述选择突出显示的标尺标记颜色。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/chs/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..6bd7799117 --- /dev/null +++ b/i18n/chs/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "代码工作区", + "untitledWorkspace": "无标题 (工作区)", + "workspaceNameVerbose": "{0} (工作区)", + "workspaceName": "{0} (工作区)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/chs/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..356e7ae83d --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "使用扩展程序 {1} 覆盖扩展程序 {0}。", + "extensionUnderDevelopment": "正在 {0} 处加载开发扩展程序" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/chs/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..a4530a353f --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "关闭", + "cancel": "取消", + "ok": "确定" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/chs/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..d8ceec9988 --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "未显示 {0} 个进一步的错误和警告。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/node/extHostExplorerView.i18n.json b/i18n/chs/src/vs/workbench/api/node/extHostExplorerView.i18n.json new file mode 100644 index 0000000000..ebea9e9bea --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/node/extHostExplorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "没有注册 ID 为“{0}”的 TreeExplorerNodeProvider。", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider“{0}”无法提供根节点。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/chs/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..9ca24fae43 --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "无法激活扩展”{1}“。原因: 未知依赖关系“{0}”。", + "failedDep1": "无法激活扩展”{1}“。原因: 无法激活依赖关系”{0}“。", + "failedDep2": "无法激活扩展”{0}“。原因: 依赖关系多于 10 级(最可能是依赖关系循环)。", + "activationError": "激活扩展“{0}”失败: {1}。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/chs/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json b/i18n/chs/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json new file mode 100644 index 0000000000..452117149d --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "没有注册 ID 为“{0}”的 TreeExplorerNodeProvider。", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider“{0}”无法提供根节点。", + "treeExplorer.failedToResolveChildren": "TreeExplorerNodeProvider“{0}”无法解析 Children。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/node/extHostTreeView.i18n.json b/i18n/chs/src/vs/workbench/api/node/extHostTreeView.i18n.json new file mode 100644 index 0000000000..ebea9e9bea --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/node/extHostTreeView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "没有注册 ID 为“{0}”的 TreeExplorerNodeProvider。", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider“{0}”无法提供根节点。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/chs/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..a9e56cc855 --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "没有注册 ID 为“{0}”的树形图。", + "treeItem.notFound": "没有在树中找到 ID 为“{0}”的项目。", + "treeView.duplicateElement": "已注册元素 {0}。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json b/i18n/chs/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..356e7ae83d --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "使用扩展程序 {1} 覆盖扩展程序 {0}。", + "extensionUnderDevelopment": "正在 {0} 处加载开发扩展程序" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/api/node/mainThreadMessageService.i18n.json b/i18n/chs/src/vs/workbench/api/node/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..a4530a353f --- /dev/null +++ b/i18n/chs/src/vs/workbench/api/node/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "关闭", + "cancel": "取消", + "ok": "确定" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..d72fa62d1f --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "配置语言", + "displayLanguage": "定义 VSCode 的显示语言。", + "doc": "请参阅 {0},了解支持的语言列表。", + "restart": "更改此值需要重启 VSCode。", + "fail.createSettings": "无法创建“{0}”({1})。", + "JsonSchema.locale": "要使用的 UI 语言。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..464ade05ac --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "打开文件夹...", + "openFileFolder": "打开...", + "add": "添加" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..cf9e5627fd --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "切换活动栏可见性", + "view": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..b80d18ba7b --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "切换编辑器组布局(垂直/水平)", + "horizontalLayout": "水平编辑器组布局", + "verticalLayout": "垂直编辑器组布局", + "view": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..4a757c4192 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "切换边栏位置", + "view": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..be9db50e60 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "切换侧边栏可见性", + "view": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..d7da7b4813 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "切换状态栏可见性", + "view": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..6b64a9104f --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "切换 Zen 模式", + "view": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..fee3b22ef5 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "打开文件夹...", + "openFileFolder": "打开...", + "addFolderToWorkspace": "将文件夹添加到工作区...", + "add": "添加(&&A)", + "addFolderToWorkspaceTitle": "将文件夹添加到工作区", + "newWorkspace": "新建工作区...", + "select": "选择(&&S)", + "selectWorkspace": "选择文件夹作为工作区", + "removeFolderFromWorkspace": "将文件夹从工作区删除", + "saveWorkspaceAsAction": "将工作区另存为...", + "saveEmptyWorkspaceNotSupported": "请先打开一个工作区再保存。", + "save": "保存(&&S)", + "saveWorkspace": "保存工作区", + "openWorkspaceAction": "打开工作区...", + "openWorkspaceConfigFile": "打开工作区配置文件" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..63f72a2b0c --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "从活动栏删除", + "keepInActivityBar": "保留活动栏", + "titleKeybinding": "{0} ({1})", + "additionalViews": "其他视图", + "numberBadge": "{0} ({1})", + "manageExtension": "管理扩展", + "toggle": "切换已固定的视图" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..f40ae094db --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "隐藏活动栏", + "activityBarAriaLabel": "活动视图切换器", + "globalActions": "全局动作" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..4171ada77c --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "{0} 操作", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..a05a338322 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "二进制查看器" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..e26644acf6 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "文本编辑器", + "textDiffEditor": "文本差异编辑器", + "binaryDiffEditor": "二进制差异编辑器", + "sideBySideEditor": "并排编辑器", + "groupOnePicker": "在第一组中显示编辑器", + "groupTwoPicker": "在第二组中显示编辑器", + "groupThreePicker": "在第三组中显示编辑器", + "allEditorsPicker": "显示所有已打开的编辑器", + "view": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..76ad308589 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "拆分编辑器", + "joinTwoGroups": "合并两个组的编辑器", + "navigateEditorGroups": "在编辑器组间进行导航", + "focusActiveEditorGroup": "聚焦活动编辑器组", + "focusFirstEditorGroup": "聚焦于第一个编辑器组", + "focusSecondEditorGroup": "聚焦于第二个编辑器组", + "focusThirdEditorGroup": "聚焦于第三个编辑器组", + "focusPreviousGroup": "聚焦于上一个组", + "focusNextGroup": "聚焦于下一个组", + "openToSide": "打开到侧边", + "closeEditor": "关闭编辑器", + "revertAndCloseActiveEditor": "还原并关闭编辑器", + "closeEditorsToTheLeft": "关闭左侧编辑器", + "closeEditorsToTheRight": "关闭右侧编辑器", + "closeAllEditors": "关闭所有编辑器", + "closeUnmodifiedEditors": "关闭组中未作更改的编辑器", + "closeEditorsInOtherGroups": "关闭其他组中的编辑器", + "closeOtherEditorsInGroup": "关闭其他编辑器", + "closeEditorsInGroup": "关闭组中的所有编辑器", + "moveActiveGroupLeft": "向左移动编辑器组", + "moveActiveGroupRight": "向右移动编辑器组", + "minimizeOtherEditorGroups": "最小化其他编辑器组", + "evenEditorGroups": "编辑器组平均宽度", + "maximizeEditor": "最大化编辑器组并隐藏边栏", + "keepEditor": "保留编辑器", + "openNextEditor": "打开下一个编辑器", + "openPreviousEditor": "打开上一个编辑器", + "nextEditorInGroup": "打开组中的下一个编辑器", + "openPreviousEditorInGroup": "打开组中上一个编辑器", + "navigateNext": "前进", + "navigatePrevious": "后退", + "reopenClosedEditor": "重新打开已关闭的编辑器", + "clearRecentFiles": "清除最近打开", + "showEditorsInFirstGroup": "在第一组中显示编辑器", + "showEditorsInSecondGroup": "在第二组中显示编辑器", + "showEditorsInThirdGroup": "在第三组中显示编辑器", + "showEditorsInGroup": "显示组中的编辑器", + "showAllEditors": "显示所有编辑器", + "openPreviousRecentlyUsedEditorInGroup": "打开组中上一个最近使用的编辑器", + "openNextRecentlyUsedEditorInGroup": "打开组中下一个最近使用的编辑器", + "navigateEditorHistoryByInput": "从历史记录里打开上一个编辑器", + "openNextRecentlyUsedEditor": "打开下一个最近使用的编辑器", + "openPreviousRecentlyUsedEditor": "打开上一个最近使用的编辑器", + "clearEditorHistory": "清除编辑器历史记录", + "focusLastEditorInStack": "打开组中上一个编辑器", + "moveEditorLeft": "向左移动编辑器", + "moveEditorRight": "向右移动编辑器", + "moveEditorToPreviousGroup": "将编辑器移动到上一组", + "moveEditorToNextGroup": "将编辑器移动到下一组" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..19315f6f04 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "按标签或按组移动活动编辑器", + "editorCommand.activeEditorMove.arg.name": "活动编辑器移动参数", + "editorCommand.activeEditorMove.arg.description": "参数属性:\n\t\t\t\t\t\t* 'to': 提供向何处移动的字符串值。\n\t\t\t\t\t\t* 'by': 提供要移动的单元的字符串值。按标签或按组。\n\t\t\t\t\t\t* 'value': 提供要移动的位置或绝对位置数量的数字值。\n\t\t\t\t\t", + "commandDeprecated": "已删除命令 **{0}**。你可以改用 **{1}**", + "openKeybindings": "配置键盘快捷方式" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..a243de02fa --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "左侧", + "groupTwoVertical": "居中", + "groupThreeVertical": "右侧", + "groupOneHorizontal": "顶部", + "groupTwoHorizontal": "居中", + "groupThreeHorizontal": "底部", + "editorOpenError": "无法打开“{0}”: {1}。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..eae263aea0 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, 编辑器组选取器", + "groupLabel": "组: {0}", + "noResultsFoundInGroup": "未在组中找到匹配的已打开编辑器", + "noOpenedEditors": "已打开的编辑器组列表当前为空", + "noResultsFound": "未找到匹配的已打开编辑器", + "noOpenedEditorsAllGroups": "已打开的编辑器列表当前为空" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..d8e7df6a31 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "行 {0},列 {1} (已选择{2})", + "singleSelection": "行 {0},列 {1}", + "multiSelectionRange": "{0} 选择(已选择 {1} 个字符)", + "multiSelection": "{0} 选择", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "按 Tab 移动焦点", + "screenReaderDetected": "检测到屏幕阅读器", + "screenReaderDetectedExtra": "如果你没有使用屏幕阅读器,请将设置中的“editor.accessibilitySupport”改为“off”。", + "disableTabMode": "禁用辅助功能模式", + "gotoLine": "转到行", + "indentation": "缩进", + "selectEncoding": "选择编码", + "selectEOL": "选择行尾序列", + "selectLanguageMode": "选择语言模式", + "fileInfo": "文件信息", + "spacesSize": "空格: {0}", + "tabSize": "制表符长度: {0}", + "showLanguageExtensions": "搜索“{0}”的应用市场扩展程序...", + "changeMode": "更改语言模式", + "noEditor": "此时没有处于活动状态的文本编辑器", + "languageDescription": "({0}) - 已配置的语言", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "语言(标识符)", + "configureModeSettings": "配置“{0}”语言基础设置...", + "configureAssociationsExt": "“{0}”的配置文件关联...", + "autoDetect": "自动检测", + "pickLanguage": "选择语言模式", + "currentAssociation": "当前关联", + "pickLanguageToConfigure": "选择要与“{0}”关联的语言模式", + "changeIndentation": "更改缩进", + "noWritableCodeEditor": "活动代码编辑器为只读模式。", + "indentView": "更改视图", + "indentConvert": "转换文件", + "pickAction": "选择操作", + "changeEndOfLine": "更改行尾序列", + "pickEndOfLine": "选择行尾序列", + "changeEncoding": "更改文件编码", + "noFileEditor": "此时无活动文件", + "saveWithEncoding": "通过编码保存", + "reopenWithEncoding": "通过编码重新打开", + "guessedEncoding": "通过内容猜测", + "pickEncodingForReopen": "选择文件编码以重新打开文件", + "pickEncodingForSave": "选择用于保存的文件编码" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..c930e1bca6 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "选项卡操作" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..a85def588c --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "文本差异编辑器", + "readonlyEditorWithInputAriaLabel": "{0}。只读文本比较编辑器。", + "readonlyEditorAriaLabel": "只读文本比较编辑器。", + "editableEditorWithInputAriaLabel": "{0}。文本文件比较编辑器。", + "editableEditorAriaLabel": "文本文件比较编辑器。", + "navigate.next.label": "下一个更改", + "navigate.prev.label": "上一个更改", + "inlineDiffLabel": "切换到内联视图", + "sideBySideDiffLabel": "切换到并行视图" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..4495f5b6d4 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0},所在组为: {1}。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..4b8ab5b218 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "文本编辑器", + "readonlyEditorWithInputAriaLabel": "{0}。只读文本编辑器。", + "readonlyEditorAriaLabel": "只读文本编辑器。", + "untitledFileEditorWithInputAriaLabel": "{0}。无标题文件文本编辑器。", + "untitledFileEditorAriaLabel": "无标题文件文本编辑器。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..900075b854 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "关闭", + "closeOthers": "关闭其他", + "closeRight": "关闭到右侧", + "closeAll": "全部关闭", + "closeAllUnmodified": "关闭未更改", + "keepOpen": "保持打开状态", + "showOpenedEditors": "显示打开的编辑器", + "araLabelEditorActions": "编辑器操作" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..220c747b93 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "关闭面板", + "togglePanel": "切换面板", + "focusPanel": "聚焦到面板中", + "toggleMaximizedPanel": "切换最大化面板", + "maximizePanel": "最大化面板大小", + "minimizePanel": "恢复面板大小", + "view": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..5c82ccd695 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "活动面板切换器" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..3dec1e68d2 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} (按 \"Enter\" 以确认或按 \"Esc\" 以取消)", + "inputModeEntry": "按 \"Enter\" 以确认或按 \"Esc\" 以取消", + "emptyPicks": "无条目可供选取", + "quickOpenInput": "键入 \"?\" 从此处获取有关可进行的操作的帮助", + "historyMatches": "最近打开", + "noResultsFound1": "未找到结果", + "canNotRunPlaceholder": "在当前上下文中无法使用此 Quick Open 处理程序", + "entryAriaLabel": "{0},最近打开", + "removeFromEditorHistory": "从历史记录中删除", + "pickHistory": "选择要从历史记录中删除的编辑器项" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..ca2cb4d6c5 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "转到文件...", + "quickNavigateNext": "在 Quick Open 中导航到下一个", + "quickNavigatePrevious": "在 Quick Open 中导航到上一个", + "quickSelectNext": "在 Quick Open 中选择“下一步”", + "quickSelectPrevious": "在 Quick Open 中选择“上一步”" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..ca2cb4d6c5 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "转到文件...", + "quickNavigateNext": "在 Quick Open 中导航到下一个", + "quickNavigatePrevious": "在 Quick Open 中导航到上一个", + "quickSelectNext": "在 Quick Open 中选择“下一步”", + "quickSelectPrevious": "在 Quick Open 中选择“上一步”" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..777dc3cada --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "隐藏侧边栏", + "focusSideBar": "聚焦信息侧边栏", + "viewCategory": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..5d79731993 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "命令“{0}”当前未启用,无法运行。", + "manageExtension": "管理扩展" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..18251dc520 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[不受支持]", + "devExtensionWindowTitlePrefix": "[扩展开发主机]" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/quickopen.i18n.json b/i18n/chs/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..363e161ea2 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "没有匹配的结果", + "noResultsFound2": "未找到结果", + "entryAriaLabel": "{0},命令" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/viewlet.i18n.json b/i18n/chs/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..c606a8f433 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "全部折叠" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/common/theme.i18n.json b/i18n/chs/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..9d7ad7496a --- /dev/null +++ b/i18n/chs/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "活动选项卡的背景色。在编辑器区域,选项卡是编辑器的容器。可在一个编辑器组中打开多个选项卡。可以有多个编辑器组。", + "tabInactiveBackground": "非活动选项卡的背景色。在编辑器区域,选项卡是编辑器的容器。可在一个编辑器组中打开多个选项卡。可以有多个编辑器组。", + "tabBorder": "用于将选项卡彼此分隔开的边框。选项卡是编辑器区域中编辑器的容器。可在一个编辑器组中打开多个选项卡。可以存在多个编辑器组。", + "tabActiveBorder": "用于高亮活动的选项卡的边框。选项卡是编辑器区域中编辑器的容器。可在一个编辑器组中打开多个选项卡。可以存在多个编辑器组。", + "tabActiveUnfocusedBorder": "用于高亮一个失去焦点的编辑器组中的活动选项卡的边框。选项卡是编辑器区域中编辑器的容器。可在一个编辑器组中打开多个选项卡。可以存在多个编辑器组。", + "tabActiveForeground": "活动组中活动选项卡的前景色。在编辑器区域,选项卡是编辑器的容器。可在一个编辑器组中打开多个选项卡。可以有多个编辑器组。", + "tabInactiveForeground": "活动组中非活动选项卡的前景色。在编辑器区域,选项卡是编辑器的容器。可在一个编辑器组中打开多个选项卡。可以有多个编辑器组。", + "tabUnfocusedActiveForeground": "一个失去焦点的编辑器组中的活动选项卡的前景色。在编辑器区域,选项卡是编辑器的容器。可在一个编辑器组中打开多个选项卡。可以有多个编辑器组。", + "tabUnfocusedInactiveForeground": "在一个失去焦点的组中非活动选项卡的前景色。在编辑器区域,选项卡是编辑器的容器。可在一个编辑器组中打开多个选项卡。可以有多个编辑器组。", + "editorGroupBackground": "编辑器组的背景颜色。编辑器组是编辑器的容器。此颜色在拖动编辑器组时显示。", + "tabsContainerBackground": "启用选项卡时编辑器组标题的背景颜色。编辑器组是编辑器的容器。", + "tabsContainerBorder": "选项卡启用时编辑器组标题的边框颜色。编辑器组是编辑器的容器。", + "editorGroupHeaderBackground": "禁用选项卡时编辑器组标题的背景颜色。编辑器组是编辑器的容器。", + "editorGroupBorder": "将多个编辑器组彼此分隔开的颜色。编辑器组是编辑器的容器。", + "editorDragAndDropBackground": "拖动编辑器时的背景颜色。此颜色应有透明度,以便编辑器内容能透过背景。", + "panelBackground": "面板的背景色。面板显示在编辑器区域下方,可包含输出和集成终端等视图。", + "panelBorder": "分隔到编辑器的顶部面板边框色。面板显示在编辑器区域下方,可包含输出和集成终端等视图。", + "panelActiveTitleForeground": "活动面板的标题颜色。面板显示在编辑器区域下方,并包含输出和集成终端等视图。", + "panelInactiveTitleForeground": "非活动面板的标题颜色。面板显示在编辑器区域下方,并包含输出和集成终端等视图。", + "panelActiveTitleBorder": "活动面板的边框颜色。面板显示在编辑器区域下方,包含输出和集成终端等视图。", + "statusBarForeground": "工作区打开时状态栏的前景色。状态栏显示在窗口底部。", + "statusBarNoFolderForeground": "没有打开文件夹时状态栏的前景色。状态栏显示在窗口底部。", + "statusBarBackground": "工作区打开时状态栏的背景色。状态栏显示在窗口底部。", + "statusBarNoFolderBackground": "没有打开文件夹时状态栏的背景色。状态栏显示在窗口底部。", + "statusBarBorder": "状态栏分隔侧边栏和编辑器的边框颜色。状态栏显示在窗口底部。", + "statusBarNoFolderBorder": "当没有打开文件夹时,用来使状态栏与侧边栏、编辑器分隔的状态栏边框颜色。状态栏显示在窗口底部。", + "statusBarItemActiveBackground": "单击时的状态栏项背景色。状态栏显示在窗口底部。", + "statusBarItemHoverBackground": "悬停时的状态栏项背景色。状态栏显示在窗口底部。", + "statusBarProminentItemBackground": "状态栏突出显示项的背景颜色。突出显示项比状态栏中的其他条目更显眼,表明其重要性更高。状态栏显示在窗口底部。", + "statusBarProminentItemHoverBackground": "状态栏突出显示项在悬停时的背景颜色。突出显示项比状态栏中的其他条目更显眼,表明其重要性更高。状态栏显示在窗口底部。", + "activityBarBackground": "活动栏背景色。活动栏显示在最左侧或最右侧,并允许在侧边栏的视图间切换。", + "activityBarForeground": "活动栏前景色(例如用于图标)。活动栏显示在最左侧或最右侧,并允许在侧边栏的视图间切换。", + "activityBarBorder": "活动栏分隔侧边栏的边框颜色。活动栏显示在最左侧或最右侧,并可以切换侧边栏的视图。", + "activityBarDragAndDropBackground": "活动栏项在被拖放时的反馈颜色。此颜色应有透明度,以便活动栏条目能透过此颜色。活动栏显示在最左侧或最右侧,并允许在侧边栏视图之间切换。", + "activityBarBadgeBackground": "活动通知徽章背景色。活动栏显示在最左侧或最右侧,并允许在侧边栏的视图间切换。", + "activityBarBadgeForeground": "活动通知徽章前景色。活动栏显示在最左侧或最右侧,并允许在侧边栏的视图间切换。", + "sideBarBackground": "侧边栏背景色。侧边栏是资源管理器和搜索等视图的容器。", + "sideBarForeground": "侧边栏前景色。侧边栏是资源管理器和搜索等视图的容器。", + "sideBarBorder": "侧边栏分隔编辑器的边框颜色。侧边栏包含资源管理器、搜索等视图。", + "sideBarTitleForeground": "侧边栏标题前景色。侧边栏是资源管理器和搜索等视图的容器。", + "sideBarDragAndDropBackground": "侧边栏中的部分在拖放时的反馈颜色。此颜色应有透明度,以便侧边栏中的部分仍能透过。侧边栏是资源管理器和搜索等视图的容器。", + "sideBarSectionHeaderBackground": "侧边栏节标题的背景颜色。侧边栏是资源管理器和搜索等视图的容器。", + "sideBarSectionHeaderForeground": "侧边栏节标题的前景色。侧边栏包括资源管理器、搜索等视图。", + "titleBarActiveForeground": "窗口处于活动状态时的标题栏前景色。请注意,该颜色当前仅在 macOS 上受支持。", + "titleBarInactiveForeground": "窗口处于非活动状态时的标题栏前景色。请注意,该颜色当前仅在 macOS 上受支持。", + "titleBarActiveBackground": "窗口处于活动状态时的标题栏背景色。请注意,该颜色当前仅在 macOS 上受支持。", + "titleBarInactiveBackground": "窗口处于非活动状态时的标题栏背景色。请注意,该颜色当前仅在 macOS 上受支持。", + "titleBarBorder": "标题栏边框颜色。请注意,该颜色当前仅在 macOS 上受支持。", + "notificationsForeground": "通知前景色。通知从窗口顶部滑入。", + "notificationsBackground": "通知背颜色。通知从窗口顶部滑入。", + "notificationsButtonBackground": "通知按钮背景色。通知从窗口顶部滑入。", + "notificationsButtonHoverBackground": "悬停时的通知按钮背景色。通知从窗口顶部滑入。", + "notificationsButtonForeground": "通知按钮前景色。通知从窗口顶部滑入。", + "notificationsInfoBackground": "消息通知背景色。通知从窗口顶部滑入。", + "notificationsInfoForeground": "消息通知前景色。通知从窗口顶部滑入。", + "notificationsWarningBackground": "警告通知背颜色。通知从窗口顶部滑入。", + "notificationsWarningForeground": "警告通知前景色。通知从窗口顶部滑入。", + "notificationsErrorBackground": "错误通知背景色。通知从窗口顶部滑入。", + "notificationsErrorForeground": "错误通知前景色。通知从窗口顶部滑入。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/chs/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..22d3bc383f --- /dev/null +++ b/i18n/chs/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "关闭编辑器", + "closeWindow": "关闭窗口", + "closeWorkspace": "关闭工作区", + "noWorkspaceOpened": "此实例当前没有打开工作区,无法关闭。", + "newWindow": "新建窗口", + "toggleFullScreen": "切换全屏", + "toggleMenuBar": "切换菜单栏", + "toggleDevTools": "切换开发人员工具", + "zoomIn": "放大", + "zoomOut": "缩小", + "zoomReset": "重置缩放", + "appPerf": "启动性能", + "reloadWindow": "重新加载窗口", + "switchWindowPlaceHolder": "选择切换的窗口", + "current": "当前窗口", + "close": "关闭窗口", + "switchWindow": "切换窗口...", + "quickSwitchWindow": "快速切换窗口...", + "workspaces": "工作区", + "files": "文件", + "openRecentPlaceHolderMac": "选择并打开(按住 Cmd 键在新窗口中打开) ", + "openRecentPlaceHolder": "选择并打开(按住 Ctrl 键在新窗口中打开)", + "remove": "从最近打开中删除", + "openRecent": "打开最近的文件…", + "quickOpenRecent": "快速打开最近的文件…", + "closeMessages": "关闭通知消息", + "reportIssues": "报告问题", + "reportPerformanceIssue": "报告性能问题", + "keybindingsReference": "键盘快捷方式参考", + "openDocumentationUrl": "文档", + "openIntroductoryVideosUrl": "入门视频", + "openTipsAndTricksUrl": "提示与技巧", + "toggleSharedProcess": "切换共享进程", + "navigateLeft": "导航到左侧视图", + "navigateRight": "导航到右侧视图", + "navigateUp": "导航到上方视图", + "navigateDown": "导航到下方视图", + "increaseViewSize": "增加当前视图大小", + "decreaseViewSize": "减小当前视图大小" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/chs/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/chs/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/electron-browser/crashReporter.i18n.json b/i18n/chs/src/vs/workbench/electron-browser/crashReporter.i18n.json new file mode 100644 index 0000000000..d2bbf90ef6 --- /dev/null +++ b/i18n/chs/src/vs/workbench/electron-browser/crashReporter.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "遥测", + "telemetry.enableCrashReporting": "启用要发送给 Microsoft 的故障报表。\n此选项需重启才可生效。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/chs/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..7c717b6bb4 --- /dev/null +++ b/i18n/chs/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "扩展未在 10 秒内启动,可能在第一行已停止,需要调试器才能继续。", + "extensionHostProcess.startupFail": "扩展主机未在 10 秒内启动,这可能是一个问题。", + "extensionHostProcess.error": "扩展主机中的错误: {0}", + "devTools": "开发人员工具", + "extensionHostProcess.crash": "扩展主机意外终止。请重新加载窗口以恢复。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/chs/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..032dce9bd2 --- /dev/null +++ b/i18n/chs/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "查看", + "help": "帮助", + "file": "文件", + "workspaces": "工作区", + "developer": "开发者", + "showEditorTabs": "控制打开的编辑器是否显示在选项卡中。", + "editorTabCloseButton": "控制编辑器的选项卡关闭按钮的位置,或当设置为 \"off\" 时禁用关闭它们。", + "showIcons": "控制打开的编辑器是否随图标一起显示。这还需启用图标主题。", + "enablePreview": "控制打开的编辑器是否显示为预览。预览编辑器可以重新使用,直到将其保留(例如,双击或编辑)。", + "enablePreviewFromQuickOpen": "控制 Quick Open 中打开的编辑器是否显示为预览。预览编辑器可以重新使用,直到将其保留(例如,通过双击或编辑)。", + "editorOpenPositioning": "控制打开编辑器的位置。选择“左侧”或“右侧”以在当前活动位置的左侧或右侧打开编辑器。选择“第一个”或“最后一个”以从当前活动位置独立打开编辑器。", + "revealIfOpen": "控制打开时编辑器是否显示在任何可见组中。如果禁用,编辑器会优先在当前活动编辑器组中打开。如果启用,会显示已打开的编辑器而不是在当前活动编辑器组中再次打开。请注意,有些情况下会忽略此设置,例如强制编辑器在特定组中或在当前活动组的边侧打开时。", + "commandHistory": "控制命令面板中保留最近使用命令的数量。设置为 0 则禁用命令历史记录。", + "preserveInput": "控制是否在再次打开命令面板时恢复上一次的输入内容。", + "closeOnFocusLost": "控制 Quick Open 是否应在失去焦点时自动关闭。", + "openDefaultSettings": "控制打开设置时是否打开显示所有默认设置的编辑器。", + "sideBarLocation": "控制边栏的位置。它可显示在工作台的左侧或右侧。", + "statusBarVisibility": "控制工作台底部状态栏的可见性。", + "activityBarVisibility": "控制工作台中活动栏的可见性。", + "closeOnFileDelete": "控制文件被其他某些进程删除或重命名时是否应该自动关闭显示文件的编辑器。禁用此项会保持编辑器作为此类事件的脏文件打开。请注意,从应用程序内部进行删除操作会始终关闭编辑器,并且脏文件始终不会关闭以保存数据。", + "fontAliasing": "控制工作台中字体的渲染方式\n- default: 次像素平滑字体。将在大多数非 retina 显示器上显示最清晰的文字\n- antialiased: 进行像素而不是次像素级别的字体平滑。可能会导致字体整体显示得更细\n- none: 禁用字体平滑。将显示边缘粗糙、有锯齿的文字", + "workbench.fontAliasing.default": "次像素平滑字体。将在大多数非 retina 显示器上显示最清晰的文字。", + "workbench.fontAliasing.antialiased": "进行像素而不是次像素级别的字体平滑。可能会导致字体整体显示得更细。", + "workbench.fontAliasing.none": "禁用字体平滑。将显示边缘粗糙、有锯齿的文字。", + "swipeToNavigate": "使用三指横扫在打开的文件之间导航", + "workbenchConfigurationTitle": "工作台", + "window.openFilesInNewWindow.on": "文件将在新窗口中打开", + "window.openFilesInNewWindow.off": "文件将在该文件的文件夹打开的窗口中打开,或在上一个活动窗口中打开", + "window.openFilesInNewWindow.default": "文件将在该文件的文件夹打开的窗口中打开,或在上一个活动窗口中打开,除非通过平台或从查找程序(仅限 macOS)打开", + "openFilesInNewWindow": "控制是否应在新窗口中打开文件。\n- default: 文件将在该文件的文件夹打开的窗口中打开,或在上一个活动窗口中打开,除非该文件通过平台或从查找程序(仅限 macOS)打开\n- on: 文件将在新窗口中打开\n- off: 文件将在该文件的文件夹打开的窗口中打开,或在上一个活动窗口中打开\n注意,可能仍会存在忽略此设置的情况(例如当使用 -new-window 或 -reuse-window 命令行选项时)。", + "window.openFoldersInNewWindow.on": "文件夹将在新窗口中打开", + "window.openFoldersInNewWindow.off": "文件夹将替换上一个活动窗口", + "window.openFoldersInNewWindow.default": "文件夹在新窗口中打开,除非从应用程序内选取一个文件夹(例如,通过“文件”菜单)", + "openFoldersInNewWindow": "控制文件夹应在新窗口中打开还是替换上一活动窗口。\n- default: 文件夹将在新窗口中打开,除非文件是从应用程序内选取的(例如通过“文件”菜单)\n- on: 文件夹将在新窗口中打开\n- off: 文件夹将替换上一活动窗口\n注意,可能仍会存在忽略此设置的情况(例如当使用 -new-window 或 -reuse-window 命令行选项时)。", + "window.reopenFolders.all": "重新打开所有窗口。", + "window.reopenFolders.folders": "重新打开所有文件夹。空工作区将不会被恢复。", + "window.reopenFolders.one": "重新打开上一个活动窗口。", + "window.reopenFolders.none": "永远不重新打开窗口。总是以一个空窗口启动。", + "restoreWindows": "控制重启后重新打开窗口的方式。选择 \"none\" 则永远在启动时打开一个空工作区,\"one\" 则重新打开最后使用的窗口,\"folders\" 则重新打开所有含有文件夹的窗口,\"all\" 则重新打开上次会话的所有窗口。", + "restoreFullscreen": "如果窗口已退出全屏模式,控制其是否应还原为全屏模式。", + "zoomLevel": "调整窗口的缩放级别。原始大小是 0,每次递增(例如 1)或递减(例如 -1)表示放大或缩小 20%。也可以输入小数以便以更精细的粒度调整缩放级别。", + "title": "基于活动编辑器控制窗口标题。变量基于上下文进行替换:\n${activeEditorShort}: 例如 myFile.txt\n${activeEditorMedium}: 例如 myFolder/myFile.txt\n${activeEditorLong}: 例如 /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: 例如 myFolder\n${folderPath}: 例如 /Users/Development/myFolder\n${rootName}: 例如 myFolder1, myFolder2, myFolder3\n${rootPath}: 例如 /Users/Development/myWorkspace\n${appName}: 例如 VS Code\n${dirty}: 更新的指示器(如果活动编辑器已更新)\n${separator}: 仅在被带值的变量包围时显示的条件分隔符(\" - \")", + "window.newWindowDimensions.default": "在屏幕中心打开新窗口。", + "window.newWindowDimensions.inherit": "以与上一个活动窗口相同的尺寸打开新窗口。", + "window.newWindowDimensions.maximized": "打开最大化的新窗口。", + "window.newWindowDimensions.fullscreen": "在全屏模式下打开新窗口。", + "newWindowDimensions": "控制在已有窗口时新打开窗口的尺寸。默认情况下,新窗口将以小尺寸在屏幕的中央打开。当设置为“inherit”时,新窗口将继承上一活动窗口的尺寸,设置为“maximized”时窗口将被最大化,设置为“fullscreen”时则变为全屏。请注意,此设置对第一个打开的窗口无效。第一个窗口总是会恢复关闭前的大小和位置。", + "closeWhenEmpty": "控制关闭最后一个编辑器是否关闭整个窗口。此设置仅适用于不显示文件夹的窗口。", + "window.menuBarVisibility.default": "菜单仅在全屏模式下隐藏。", + "window.menuBarVisibility.visible": "菜单始终可见,即使处于全屏模式下。", + "window.menuBarVisibility.toggle": "菜单隐藏,但可以通过 Alt 键显示。", + "window.menuBarVisibility.hidden": "菜单始终隐藏。", + "menuBarVisibility": "控制菜单栏的可见性。“切换”设置表示隐藏菜单栏,按一次 Alt 键则将显示此菜单栏。默认情况下,除非窗口为全屏,否则菜单栏可见。", + "enableMenuBarMnemonics": "如果启用,则可使用 Alt 快捷键打开主菜单。禁用助记键允许将这些 Alt 快捷键绑定到编辑器命令。", + "autoDetectHighContrast": "如果已启用,将自动更改为高对比度主题;如果 Windows 正在使用高对比度主题,则当离开 Windows 高对比度主题时会更改为深色主题。", + "titleBarStyle": "调整窗口标题栏的外观。更改需要在完全重启后才能应用。", + "window.nativeTabs": "\n启用macOS Sierra窗口选项卡。请注意,更改需要完全重新启动程序才能生效。如果配置此选项,本机选项卡将禁用自定义标题栏样式。", + "windowConfigurationTitle": "窗口", + "zenModeConfigurationTitle": "Zen 模式", + "zenMode.fullScreen": "控制打开 Zen Mode 是否也会将工作台置于全屏模式。", + "zenMode.hideTabs": "控制打开 Zen 模式是否也会隐藏工作台选项卡。", + "zenMode.hideStatusBar": "控制打开 Zen 模式是否也会隐藏工作台底部的状态栏。", + "zenMode.hideActivityBar": "控制打开 Zen 模式是否也会隐藏工作台左侧的活动栏。", + "zenMode.restore": "控制如果某窗口已退出 zen 模式,是否应还原到 zen 模式。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/electron-browser/main.i18n.json b/i18n/chs/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..a318e0acb8 --- /dev/null +++ b/i18n/chs/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "无法加载需要的文件。您的 Internet 连接已断开,或者您连接的服务器已脱机。请刷新浏览器并重试。", + "loaderErrorNative": "未能加载所需文件。请重启应用程序重试。详细信息: {0}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/chs/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..b90d3c3c56 --- /dev/null +++ b/i18n/chs/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "不建议将 Code 作为“根”运行。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/electron-browser/window.i18n.json b/i18n/chs/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..61172aec61 --- /dev/null +++ b/i18n/chs/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "撤消", + "redo": "恢复", + "cut": "剪切", + "copy": "复制", + "paste": "粘贴", + "selectAll": "全选" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/chs/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..7328fb202c --- /dev/null +++ b/i18n/chs/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "开发者", + "file": "文件" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/chs/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..8ca7401876 --- /dev/null +++ b/i18n/chs/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "路径 {0} 未指向有效的扩展测试运行程序。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/chs/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..e6321fb69e --- /dev/null +++ b/i18n/chs/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "未能分析 {0}: {1}。", + "fileReadFail": "无法读取文件 {0}: {1}。", + "jsonsParseFail": "未能分析 {0} 或 {1}: {2}。", + "missingNLSKey": "无法找到键 {0} 的消息。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..568c3e360a --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "在 PATH 中安装“{0}”命令", + "not available": "此命令不可用", + "successIn": "已成功在 PATH 中安装了 Shell 命令“{0}”。", + "warnEscalation": "代码将通过 \"osascript\" 提示需要管理员权限才可安装 shell 命令。", + "ok": "确定", + "cantCreateBinFolder": "无法创建 \"/usr/local/bin\"。", + "cancel2": "取消", + "aborted": "已中止", + "uninstall": "从 PATH 中卸载“{0}”命令", + "successFrom": "已成功从 PATH 卸载 Shell 命令“{0}”。", + "shellCommand": "Shell 命令" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..e04e6c349f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "正在更改“editor.accessibilitySupport”设置为“on”。", + "openingDocs": "正在打开 VS Code 辅助功能文档页面。", + "introMsg": "感谢试用 VS Code 的辅助功能选项。", + "status": "状态:", + "changeConfigToOnMac": "要配置编辑器对屏幕阅读器进行永久优化,请按 Command+E。", + "changeConfigToOnWinLinux": "要配置编辑器对屏幕阅读器进行永久优化,请按 Ctrl+E。", + "auto_unknown": "编辑器被配置为使用平台 API 以检测是否附加了屏幕阅读器,但当前运行时不支持此功能。", + "auto_on": "编辑器自动检测到已附加屏幕阅读器。", + "auto_off": "编辑器被配置为自动检测是否附加了屏幕阅读器,当前未检测到。", + "configuredOn": "编辑器被配置为对屏幕阅读器的使用进行永久优化 — 你可以编辑设置中的“editor.accessibilitySupport”以改变此行为。", + "configuredOff": "编辑器被配置为不对屏幕阅读器的使用进行优化。", + "tabFocusModeOnMsg": "在当前编辑器中按 Tab 会将焦点移动到下一个可聚焦的元素。按 {0} 来切换此行为。", + "tabFocusModeOnMsgNoKb": "在当前编辑器中按 Tab 会将焦点移动到下一个可聚焦的元素。当前无法通过键绑定触发命令 {0}。", + "tabFocusModeOffMsg": "在当前编辑器中按 Tab 将插入制表符。按 {0} 来切换此行为。", + "tabFocusModeOffMsgNoKb": "在当前编辑器中按 Tab 会插入制表符。当前无法通过键绑定触发命令 {0}。", + "openDocMac": "按 Command+H 以打开浏览器窗口,其中包含更多有关 VS Code 辅助功能的信息。", + "openDocWinLinux": "按 Ctrl+H 以打开浏览器窗口,其中包含更多有关 VS Code 辅助功能的信息。", + "outroMsg": "你可以按 Esc 或 Shift+Esc 消除此工具提示并返回到编辑器。", + "ShowAccessibilityHelpAction": "显示辅助功能帮助" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..efe98b6533 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "开发者: 检查键映射" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f6024434ea --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "开发者: 检查 TM 作用域", + "inspectTMScopesWidget.loading": "正在加载..." +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..dcb5e67cae --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "错误分析 {0}: {1}", + "schema.openBracket": "左方括号字符或字符串序列。", + "schema.closeBracket": "右方括号字符或字符串序列。", + "schema.comments": "定义注释符号", + "schema.blockComments": "定义块注释的标记方式。", + "schema.blockComment.begin": "作为块注释开头的字符序列。", + "schema.blockComment.end": "作为块注释结尾的字符序列。", + "schema.lineComment": "作为行注释开头的字符序列。", + "schema.brackets": "定义增加和减少缩进的括号。", + "schema.autoClosingPairs": "定义括号对。当输入左方括号时,将自动插入右方括号。", + "schema.autoClosingPairs.notIn": "定义禁用了自动配对的作用域列表。", + "schema.surroundingPairs": "定义可用于包围所选字符串的括号对。", + "schema.wordPattern": "此语言的文本定义。", + "schema.wordPattern.pattern": "用于匹配文本的正则表达式模式。", + "schema.wordPattern.flags": "用于匹配文本的正则表达式标志。", + "schema.wordPattern.flags.errorMessage": "必须匹配模式“/^([gimuy]+)$/”。", + "schema.indentationRules": "语言的缩进设置。", + "schema.indentationRules.increaseIndentPattern": "如果一行文本匹配此模式,则之后所有内容都应被缩进一次(直到匹配其他规则)。", + "schema.indentationRules.increaseIndentPattern.pattern": "increaseIndentPattern 的正则表达式模式。", + "schema.indentationRules.increaseIndentPattern.flags": "increaseIndentPattern 的正则表达式标志。", + "schema.indentationRules.increaseIndentPattern.errorMessage": "必须匹配模式“/^([gimuy]+)$/”。", + "schema.indentationRules.decreaseIndentPattern": "如果一行文本匹配此模式,则之后所有内容都应被取消缩进一次(直到匹配其他规则)。", + "schema.indentationRules.decreaseIndentPattern.pattern": "decreaseIndentPattern 的正则表达式模式。", + "schema.indentationRules.decreaseIndentPattern.flags": "decreaseIndentPattern 的正则表达式标志。", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "必须匹配模式“/^([gimuy]+)$/”。", + "schema.indentationRules.indentNextLinePattern": "如果某一行匹配此模式,那么仅此行之后的**下一行**应缩进一次。", + "schema.indentationRules.indentNextLinePattern.pattern": "indentNextLinePattern 的正则表达式模式。", + "schema.indentationRules.indentNextLinePattern.flags": "indentNextLinePattern 的正则表达式标志。", + "schema.indentationRules.indentNextLinePattern.errorMessage": "必须匹配模式“/^([gimuy]+)$/”。", + "schema.indentationRules.unIndentedLinePattern": "如果某一行匹配此模式,那么不应更改此行的缩进,且不应针对其他规则对其进行计算。", + "schema.indentationRules.unIndentedLinePattern.pattern": "unIndentedLinePattern 的正则表达式模式。", + "schema.indentationRules.unIndentedLinePattern.flags": "unIndentedLinePattern 的正则表达式标志。", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "必须匹配模式“/^([gimuy]+)$/”。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f6024434ea --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "开发者: 检查 TM 作用域", + "inspectTMScopesWidget.loading": "正在加载..." +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..2e5d0dfdaf --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "查看: 切换小地图" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..e636106ec9 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "切换多行修改键" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..17b497d4d5 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "查看: 切换控制字符" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..a98da5c602 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "查看: 切换呈现空格" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..7244089744 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "查看: 切换自动换行", + "wordWrap.notInDiffEditor": "不能在差异编辑器中切换自动换行。", + "unwrapMinified": "为此文件禁用折行", + "wrapMinified": "为此文件启用换行" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..551360c676 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "确定", + "wordWrapMigration.dontShowAgain": "不再显示", + "wordWrapMigration.openSettings": "打开设置", + "wordWrapMigration.prompt": "已弃用设置 \"editor.wrappingColumn\",改用 \"editor.wordWrap\"。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..0bb17f6231 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "在表达式计算结果为 true 时中断。按 \"Enter\" 表示接受,\"Esc\" 表示取消。", + "breakpointWidgetAriaLabel": "如果此条件为 true,程序仅会在此处停止。按 Enter 接受或按 Esc 取消。", + "breakpointWidgetHitCountPlaceholder": "在满足命中次数条件时中断。按 \"Enter\" 表示接受,\"Esc\" 表示取消。", + "breakpointWidgetHitCountAriaLabel": "如果达到命中次数,程序仅会在此处停止。按 Enter 接受或按 Esc 取消。", + "expression": "表达式", + "hitCount": "命中次数" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..30075f3d16 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "没有配置", + "addConfigTo": "添加配置 ({0})...", + "addConfiguration": "添加配置..." +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..2f585a5d15 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "打开 {0}", + "launchJsonNeedsConfigurtion": "配置或修复 \"launch.json\"", + "noFolderDebugConfig": "请先打开一个文件夹以进行高级调试配置。", + "startDebug": "开始调试", + "startWithoutDebugging": "开始执行(不调试)", + "selectAndStartDebugging": "选择并开始调试", + "restartDebug": "重启", + "reconnectDebug": "重新连接", + "stepOverDebug": "单步跳过", + "stepIntoDebug": "单步调试", + "stepOutDebug": "单步跳出", + "stopDebug": "停止", + "disconnectDebug": "断开连接", + "continueDebug": "继续", + "pauseDebug": "暂停", + "restartFrame": "重新启动框架", + "removeBreakpoint": "删除断点", + "removeAllBreakpoints": "删除所有断点", + "enableBreakpoint": "启用断点", + "disableBreakpoint": "禁用断点", + "enableAllBreakpoints": "启用所有断点", + "disableAllBreakpoints": "禁用所有断点", + "activateBreakpoints": "激活断点", + "deactivateBreakpoints": "停用断点", + "reapplyAllBreakpoints": "重新应用所有断点", + "addFunctionBreakpoint": "添加函数断点", + "renameFunctionBreakpoint": "重命名函数断点", + "addConditionalBreakpoint": "添加条件断点...", + "editConditionalBreakpoint": "编辑断点...", + "setValue": "设置值", + "addWatchExpression": "添加表达式", + "editWatchExpression": "编辑表达式", + "addToWatchExpressions": "添加到监视", + "removeWatchExpression": "删除表达式", + "removeAllWatchExpressions": "删除所有表达式", + "clearRepl": "清除控制台", + "debugConsoleAction": "调试控制台", + "unreadOutput": "调试控制台中的新输出", + "debugFocusConsole": "焦点调试控制台", + "focusProcess": "焦点过程", + "stepBackDebug": "后退", + "reverseContinue": "反向" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..b2215292b5 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "调试工具栏背景颜色。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..62b7875ff4 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "无法解析无调试会话的资源" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..f4a8381a2e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "调试: 切换断点", + "columnBreakpointAction": "调试: 列断点", + "columnBreakpoint": "添加列断点", + "conditionalBreakpointEditorAction": "调试: 添加条件断点...", + "runToCursor": "运行到光标处", + "debugEvaluate": "调试: 求值", + "debugAddToWatch": "调试: 添加到监视", + "showDebugHover": "调试: 显示悬停" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..543fb1b67c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "已禁用断点", + "breakpointUnverifieddHover": "未验证的断点", + "breakpointDirtydHover": "未验证的断点。对文件进行了修改,请重启调试会话。", + "breakpointUnsupported": "不受此调试类型支持的条件断点" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..98692408e2 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},调试", + "debugAriaLabel": "键入启动配置的名称以运行。", + "noConfigurationsMatching": "无任何调试配置匹配", + "noConfigurationsFound": "找不到任何调试配置。请创建一个 \"launch.json\" 文件。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..bb37e67176 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "异常小组件边框颜色。", + "debugExceptionWidgetBackground": "异常小组件背景颜色。", + "exceptionThrownWithId": "发生异常: {0}", + "exceptionThrown": "出现异常。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..04be52fda7 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "点击以跟进(Cmd + 点击打开到侧边)", + "fileLink": "点击以跟进(Ctrl + 点击打开到侧边))" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..c780a6d855 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "内部调试控制台的控制行为。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..ffc867197e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "不可用", + "startDebugFirst": "请启动调试会话以评估" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..3940a9fd02 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "未知源" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..2ff64dde47 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "显示调试", + "toggleDebugPanel": "调试控制台", + "debug": "调试", + "debugPanel": "调试控制台", + "variables": "变量", + "watch": "监视", + "callStack": "调用堆栈", + "breakpoints": "断点", + "view": "查看", + "debugCategory": "调试", + "debugCommands": "调试配置", + "debugConfigurationTitle": "调试", + "allowBreakpointsEverywhere": "允许在任何文件中设置断点", + "openExplorerOnEnd": "调试会话结束时自动打开资源管理器视图", + "inlineValues": "调试时,在编辑器中显示变量值内联", + "hideActionBar": "控制是否应该隐藏浮点调试操作栏", + "launch": "全局的调试启动配置。应用作跨工作区共享的 \"launch.json\" 的替代。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..5a112eaea8 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "请先打开一个文件夹以进行高级调试配置。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..efeebed7b7 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "用于调试适配器。", + "vscode.extension.contributes.debuggers.type": "此调试适配器的唯一标识符。", + "vscode.extension.contributes.debuggers.label": "显示此调试适配器的名称。", + "vscode.extension.contributes.debuggers.program": "调试适配器程序的路径。该路径是绝对路径或相对于扩展文件夹的相对路径。", + "vscode.extension.contributes.debuggers.args": "要传递给适配器的可选参数。", + "vscode.extension.contributes.debuggers.runtime": "可选运行时,以防程序属性不可执行,但需要运行时。", + "vscode.extension.contributes.debuggers.runtimeArgs": "可选运行时参数。", + "vscode.extension.contributes.debuggers.variables": "将 \"launch.json\" 中的交互式变量(例如 ${action.pickProcess})映射到命令中。", + "vscode.extension.contributes.debuggers.initialConfigurations": "用于生成初始 \"launch.json\" 的配置。", + "vscode.extension.contributes.debuggers.languages": "可能被视为“默认调试程序”的调试扩展的语言列表。", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "如果指定的 VS Code 将调用此命令以确定调试适配器的可执行路径和要传递的参数。", + "vscode.extension.contributes.debuggers.startSessionCommand": "如果指定的 VS Code 将为针对此扩展的“调试”或“运行”操作调用此命令。", + "vscode.extension.contributes.debuggers.configurationSnippets": "用于在 \"launch.json\" 中添加新配置的代码段。", + "vscode.extension.contributes.debuggers.configurationAttributes": "用于验证 \"launch.json\" 的 JSON 架构配置。", + "vscode.extension.contributes.debuggers.windows": "Windows 特定的设置。", + "vscode.extension.contributes.debuggers.windows.runtime": "用于 Windows 的运行时。", + "vscode.extension.contributes.debuggers.osx": "OS X 特定的设置。", + "vscode.extension.contributes.debuggers.osx.runtime": "用于 OSX 的运行时。", + "vscode.extension.contributes.debuggers.linux": "Linux 特定的设置。", + "vscode.extension.contributes.debuggers.linux.runtime": "用于 Linux 的运行时。", + "vscode.extension.contributes.breakpoints": "添加断点。", + "vscode.extension.contributes.breakpoints.language": "对此语言允许断点。", + "app.launch.json.title": "启动", + "app.launch.json.version": "此文件格式的版本。", + "app.launch.json.configurations": "配置列表。使用 IntelliSense 添加新配置或编辑现有配置。", + "app.launch.json.compounds": "复合列表。每个复合可引用多个配置,这些配置将一起启动。", + "app.launch.json.compound.name": "复合的名称。在启动配置下拉菜单中显示。", + "app.launch.json.compounds.configurations": "将作为此复合的一部分启动的配置名称。", + "debugNoType": "不可省略调试适配器“类型”,其类型必须是“字符串”。", + "selectDebug": "选择环境", + "DebugConfig.failed": "无法在 \".vscode\" 文件夹({0})内创建 \"launch.json\" 文件。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..76dd51cf2e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "删除断点", + "removeBreakpointOnColumn": "在列 {0} 上删除断点", + "removeLineBreakpoint": "删除行断点", + "editBreakpoints": "编辑断点", + "editBreakpointOnColumn": "在列 {0} 上编辑断点", + "editLineBrekapoint": "编辑行断点", + "enableDisableBreakpoints": "启用/禁用断点", + "disableColumnBreakpoint": "在列 {0} 上禁用断点", + "disableBreakpointOnLine": "禁用行断点", + "enableBreakpoints": "在列 {0} 上启用断点", + "enableBreakpointOnLine": "启用行断点", + "addBreakpoint": "添加断点", + "addConfiguration": "添加配置..." +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..0a11cedb6f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "调试悬停" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..3cfc72d417 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "仅显示了此对象的基元值。", + "debuggingPaused": "已暂停调试,原因 {0},{1} {2}", + "debuggingStarted": "已开始调试。", + "debuggingStopped": "已停止调试。", + "breakpointAdded": "已添加断点,行 {0}, 文件 {1}", + "breakpointRemoved": "已删除断点,行 {0},文件 {1}", + "compoundMustHaveConfigurations": "复合项必须拥有 \"configurations\" 属性集,才能启动多个配置。", + "configMissing": "\"launch.json\" 中缺少配置“{0}”。", + "debugTypeNotSupported": "配置的类型“{0}”不受支持。", + "debugTypeMissing": "所选的启动配置缺少属性 \"type\"。", + "preLaunchTaskErrors": "preLaunchTask“{0}”期间检测到多个生成错误。", + "preLaunchTaskError": "preLaunchTask“{0}”期间检测到一个生成错误。", + "preLaunchTaskExitCode": "preLaunchTask“{0}”已终止,退出代码为 {1}。", + "debugAnyway": "仍进行调试", + "noFolderWorkspaceDebugError": "无法调试活动文件。请确保它保存在磁盘上,并确保已为该文件类型安装了调试扩展。", + "NewLaunchConfig": "请设置应用程序的启动配置文件。{0}", + "DebugTaskNotFound": "找不到 preLaunchTask“{0}”。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..e56deda055 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "进程", + "paused": "已暂停", + "running": "正在运行", + "thread": "线程", + "pausedOn": "因 {0} 已暂停", + "loadMoreStackFrames": "加载多个堆栈帧", + "threadAriaLabel": "线程 {0},调用堆栈,调试", + "stackFrameAriaLabel": "堆栈帧 {0} 行 {1} {2},调用堆栈,调试", + "variableValueAriaLabel": "键入新的变量值", + "variableScopeAriaLabel": "范围 {0},变量,调试", + "variableAriaLabel": "{0} 值 {1},变量,调试", + "watchExpressionPlaceholder": "要监视的表达式", + "watchExpressionInputAriaLabel": "键入监视表达式", + "watchExpressionAriaLabel": "{0} 值 {1},监视,调试", + "watchVariableAriaLabel": "{0} 值 {1},监视,调试", + "functionBreakpointPlaceholder": "要断开的函数", + "functionBreakPointInputAriaLabel": "键入函数断点", + "functionBreakpointsNotSupported": "此调试类型不支持函数断点", + "breakpointAriaLabel": "断点行 {0} {1},断点,调试", + "functionBreakpointAriaLabel": "函数断点 {0},断点,调试", + "exceptionBreakpointAriaLabel": "异常断点 {0},断点,调试" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..ff582bbae5 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "变量部分", + "variablesAriaTreeLabel": "调试变量", + "expressionsSection": "表达式部分", + "watchAriaTreeLabel": "调试监视表达式", + "callstackSection": "调用堆栈部分", + "debugStopped": "因 {0} 已暂停", + "callStackAriaLabel": "调试调用堆栈", + "breakpointsSection": "断点部分", + "breakpointsAriaTreeLabel": "调试断点" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..dfdf359424 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "复制值", + "copy": "复制", + "copyAll": "全部复制", + "copyStackTrace": "复制调用堆栈" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..32e78be47e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "详细信息", + "unableToLaunchDebugAdapter": "无法从“{0}”启动调试适配器。", + "unableToLaunchDebugAdapterNoArgs": "无法启动调试适配器。", + "stoppingDebugAdapter": "{0}。正在停止调试适配器。", + "debugAdapterCrash": "调试适配器进程已意外终止" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..37a8d511f8 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "读取 Eval 打印循环面板", + "actions.repl.historyPrevious": "上一个历史记录", + "actions.repl.historyNext": "下一个历史记录", + "actions.repl.acceptInput": "接受 REPL 的输入", + "actions.repl.copyAll": "调试: 复制控制台所有内容" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..2874cceae4 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "对象状态捕获自第一个评估", + "replVariableAriaLabel": "变量 {0} 具有值 {1}、读取 Eval 打印循环,调试", + "replExpressionAriaLabel": "表达式 {0} 具有值 {1},读取 Eval 打印循环,调试", + "replValueOutputAriaLabel": "{0},读取 Eval 打印循环,调试", + "replKeyValueOutputAriaLabel": "输出变量 {0} 具有值 {1},读取 Eval 打印循环,调试" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..64a6fed671 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "调试程序时状态栏的背景色。状态栏显示在窗口底部", + "statusBarDebuggingForeground": "调试程序时状态栏的前景色。状态栏显示在窗口底部", + "statusBarDebuggingBorder": "调试程序时区别于侧边栏和编辑器的状态栏边框颜色。状态栏显示在窗口底部。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..c5cba27f7d --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "调试对象", + "debug.terminal.not.available.error": "集成终端不可用" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..569c9b3fcd --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "调试适配器可执行的“{0}”不存在。", + "debugAdapterCannotDetermineExecutable": "无法确定调试适配器“{0}”的可执行文件。", + "launch.config.comment1": "使用 IntelliSense 了解相关属性。 ", + "launch.config.comment2": "悬停以查看现有属性的描述。", + "launch.config.comment3": "欲了解更多信息,请访问: {0}", + "debugType": "配置类型。", + "debugTypeNotRecognised": "无法识别此调试类型。确保已经安装并启用相应的调试扩展。", + "node2NotSupported": "不再支持 \"node2\",改用 \"node\",并将 \"protocol\" 属性设为 \"inspector\"。", + "debugName": "配置名称;在启动配置下拉菜单中显示。", + "debugRequest": "请求配置类型。可以是“启动”或“附加”。", + "debugServer": "仅用于调试扩展开发: 如果已指定端口,VS 代码会尝试连接到在服务器模式中运行的调试适配器", + "debugPrelaunchTask": "调试会话开始前要运行的任务。", + "debugWindowsConfiguration": "特定于 Windows 的启动配置属性。", + "debugOSXConfiguration": "特定于 OS X 的启动配置属性。", + "debugLinuxConfiguration": "特定于 Linux 的启动配置属性。", + "deprecatedVariables": "已弃用 \"env.\"、\"config.\" 和 \"command.\",改用 \"env:\"、\"config:\" 和 \"command:\"。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..d3bfd8b553 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "显示 Emmet 命令" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..267678cd26 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: 平衡(向内)", + "balanceOutward": "Emmet: 平衡(向外)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..572fb99d33 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: 转到上一编辑点", + "nextEditPoint": "Emmet: 转到下一编辑点" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..bdca586f2c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: 求数学表达式的值" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..02a2005aa7 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: 展开缩写" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..b93225c12c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: 增加 0.1", + "incrementNumberByOne": "Emmet: 增加 1", + "incrementNumberByTen": "Emmet: 增加 10", + "decrementNumberByOneTenth": "Emmet: 减少 0.1", + "decrementNumberByOne": "Emmet: 减少 1", + "decrementNumberByTen": "Emmet: 减少 10" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..db1d3db03b --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: 转到匹配对" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..ed729d43fb --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: 合并行" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..2c70530e1b --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: 反射 CSS 值" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..d990884bd0 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: 删除标签" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..adb539931e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: 选择上一项", + "selectNextItem": "Emmet: 选择下一项" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..65e95a35ff --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: 分离/联接标签" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..2fdb0ea5f4 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: 切换注释" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..b38d05c81f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: 更新图像大小" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..ae9c920632 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: 更新标签", + "enterTag": "输入标签", + "tag": "标签" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..28fd88425e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: 使用缩写进行包围", + "enterAbbreviation": "输入缩写", + "abbreviation": "缩写" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..83168d136a --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "启用后,按 TAB 键时,将展开 Emmet 缩写。若 emmet.useNewemmet 设置为 true,则不适用。", + "emmetPreferences": "用于修改 Emmet 的某些操作和解决程序的行为的首选项。若 emmet.useNewemmet 设置为 true,则不适用。", + "emmetSyntaxProfiles": "为指定的语法定义配置文件或使用带有特定规则的配置文件。", + "emmetExclude": "不应展开 Emmet 缩写的语言数组。", + "emmetExtensionsPath": "包含 Emmet 配置文件、片段和首选项的文件夹路径。当 emmet.useNewEmmet 设置为 true 时,只有配置文件才服从于扩展路径。", + "useNewEmmet": "试用新版 Emmet 模块(最终会替换旧版单一 Emmet 库)体验所有 Emmet 功能。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json new file mode 100644 index 0000000000..267678cd26 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: 平衡(向内)", + "balanceOutward": "Emmet: 平衡(向外)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json new file mode 100644 index 0000000000..eaca2d22b7 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: 上一编辑点", + "nextEditPoint": "Emmet: 下一编辑点" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..bd1f799b50 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: 评估数学表达式" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..02a2005aa7 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: 展开缩写" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..4c1c5b2652 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: 以 0.1 为增量", + "incrementNumberByOne": "Emmet: 以 1 为增量", + "incrementNumberByTen": "Emmet: 以 10 为增量", + "decrementNumberByOneTenth": "Emmet: 以 0.1 为减量", + "decrementNumberByOne": "Emmet: 以 1 为减量", + "decrementNumberByTen": "Emmet: 以 10 为减量" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..db1d3db03b --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: 转到匹配对" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..ed729d43fb --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: 合并行" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..2c70530e1b --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: 反射 CSS 值" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json new file mode 100644 index 0000000000..1462a80b16 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: 删除标记" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json new file mode 100644 index 0000000000..adb539931e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: 选择上一项", + "selectNextItem": "Emmet: 选择下一项" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..e74400f216 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: 分离/联接标记" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..2fdb0ea5f4 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: 切换注释" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..5960c7d08c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: 更新映像大小" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json new file mode 100644 index 0000000000..e3549af352 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: 更新标记", + "enterTag": "输入标记", + "tag": "标记" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..572a1bf989 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: 使用缩写进行包装", + "enterAbbreviation": "输入缩写", + "abbreviation": "缩写" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json new file mode 100644 index 0000000000..cc0adfc871 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "启用后,按 TAB 键时,将展开 Emmet 缩写。", + "emmetPreferences": "用于修改 Emmet 的某些操作和解决程序的首选项。", + "emmetSyntaxProfiles": "为指定的语法定义配置文件或使用带有特定规则的配置文件。", + "emmetExclude": "emmet 缩写不应在其中展开的语言数组。", + "emmetExtensionsPath": "转至包含 Emmet 配置文件、片段和首选项的文件的路径" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..70757a0a26 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "外部终端", + "explorer.openInTerminalKind": "自定义要启动的终端类型。", + "terminal.external.windowsExec": "自定义要在 Windows 上运行的终端。", + "terminal.external.osxExec": "自定义要在 OS X 上运行的终端应用程序。", + "terminal.external.linuxExec": "自定义要在 Linux 上运行的终端。", + "globalConsoleActionWin": "打开新命令提示符", + "globalConsoleActionMacLinux": "打开新终端", + "scopedConsoleActionWin": "在命令提示符中打开", + "scopedConsoleActionMacLinux": "在终端中打开", + "openFolderInIntegratedTerminal": "在终端中打开" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..533e5dfeed --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "外部终端", + "terminal.external.windowsExec": "自定义要在 Windows 上运行的终端。", + "terminal.external.osxExec": "自定义要在 OS X 上运行的终端应用程序。", + "terminal.external.linuxExec": "自定义要在 Linux 上运行的终端。", + "globalConsoleActionWin": "打开新命令提示符", + "globalConsoleActionMacLinux": "打开新终端", + "scopedConsoleActionWin": "在命令提示符中打开", + "scopedConsoleActionMacLinux": "在终端中打开" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/chs/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..03112f28b4 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "VS Code 控制台", + "mac.terminal.script.failed": "脚本“{0}”失败,退出代码为 {1}", + "mac.terminal.type.not.supported": "不支持“{0}”", + "press.any.key": "按任意键继续...", + "linux.term.failed": "“{0}”失败,退出代码为 {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..afbdc29705 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "添加自定义视图", + "vscode.extension.contributes.view.id": "用于标识通过 vscode.workspace.createTreeView 创建的视图的唯一 ID", + "vscode.extension.contributes.view.label": "用于呈现视图的人类可读的字符串", + "vscode.extension.contributes.view.icon": "视图图标的路径", + "vscode.extension.contributes.views": "添加自定义视图", + "showViewlet": "显示 {0}", + "view": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..5f1685713d --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "刷新" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..a031ad0742 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "没有注册 ID 为 {providerId}的 TreeExplorerNodeProvider。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/chs/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..3a0cd4fb56 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "树资源管理器部分" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..5655b9291b --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "错误", + "Unknown Dependency": "未知依赖项:" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..41fa1de8ee --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "扩展名", + "extension id": "扩展标识符", + "publisher": "发布服务器名称", + "install count": "安装计数", + "rating": "评级", + "license": "许可证", + "details": "详细信息", + "contributions": "发布内容", + "changelog": "更改日志", + "dependencies": "依赖项", + "noReadme": "无可用自述文件。", + "noChangelog": "无可用的更改日志。", + "noContributions": "没有发布内容", + "noDependencies": "没有依赖项", + "settings": "设置({0})", + "setting name": "名称", + "description": "描述", + "default": "默认", + "debuggers": "调试程序({0})", + "debugger name": "名称", + "debugger type": "类型", + "views": "视图 ({0})", + "view id": "ID", + "view name": "名称", + "view location": "位置", + "themes": "主题({0})", + "JSON Validation": "JSON 验证({0})", + "commands": "命令({0})", + "command name": "名称", + "keyboard shortcuts": "键盘快捷方式", + "menuContexts": "菜单上下文", + "languages": "语言({0})", + "language id": "ID", + "language name": "名称", + "file extensions": "文件扩展名", + "grammar": "语法", + "snippets": "代码片段" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..2897e5c599 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "安装", + "installing": "正在安装", + "uninstallAction": "卸载", + "Uninstalling": "正在卸载", + "updateAction": "更新", + "updateTo": "更新到 {0}", + "enableForWorkspaceAction.label": "启用(工作区)", + "enableAlwaysAction.label": "启用(始终)", + "disableForWorkspaceAction.label": "禁用(工作区)", + "disableAlwaysAction.label": "禁用(始终)", + "ManageExtensionAction.uninstallingTooltip": "正在卸载", + "enableForWorkspaceAction": "工作区", + "enableGloballyAction": "始终", + "enableAction": "启用", + "disableForWorkspaceAction": "工作区", + "disableGloballyAction": "始终", + "disableAction": "禁用", + "checkForUpdates": "检查更新", + "enableAutoUpdate": "启用自动更新扩展", + "disableAutoUpdate": "禁用自动更新扩展", + "updateAll": "更新所有扩展", + "reloadAction": "重新加载", + "postUpdateTooltip": "重载以更新", + "postUpdateMessage": "重载此窗口以激活更新的扩展“{0}”?", + "postEnableTooltip": "重载以激活", + "postEnableMessage": "重载此窗口以激活扩展“{0}”?", + "postDisableTooltip": "重载以停用", + "postDisableMessage": "重载此窗口以停用扩展“{0}”?", + "postUninstallTooltip": "重载以停用", + "postUninstallMessage": "重载此窗口以停用未安装的扩展“{0}”?", + "reload": "重载窗口(&&R)", + "toggleExtensionsViewlet": "显示扩展", + "installExtensions": "安装扩展", + "showEnabledExtensions": "显示启用的扩展", + "showInstalledExtensions": "显示已安装扩展", + "showDisabledExtensions": "显示已禁用的扩展", + "clearExtensionsInput": "清除扩展输入", + "showOutdatedExtensions": "显示过时扩展", + "showPopularExtensions": "显示常用的扩展", + "showRecommendedExtensions": "显示推荐的扩展", + "showWorkspaceRecommendedExtensions": "显示工作区建议的扩展名", + "showRecommendedKeymapExtensions": "显示推荐键映射", + "showRecommendedKeymapExtensionsShort": "键映射", + "showLanguageExtensions": "显示语言扩展", + "showLanguageExtensionsShort": "语言扩展", + "showAzureExtensions": "显示 Azure 扩展", + "showAzureExtensionsShort": "Azure 扩展", + "configureWorkspaceRecommendedExtensions": "配置建议的扩展(工作区)", + "ConfigureWorkspaceRecommendations.noWorkspace": "建议仅在工作区文件夹上可用。", + "OpenExtensionsFile.failed": "无法在 \".vscode\" 文件夹({0})内创建 \"extensions.json\" 文件。", + "builtin": "内置", + "disableAll": "禁用所有已安装的扩展", + "disableAllWorkspace": "禁用此工作区的所有已安装的扩展", + "enableAll": "启用所有已安装的扩展", + "enableAllWorkspace": "启用此工作区的所有已安装的扩展", + "extensionButtonProminentBackground": "扩展中突出操作的按钮背景色(比如 安装按钮)。", + "extensionButtonProminentForeground": "扩展中突出操作的按钮前景色(比如 安装按钮)。", + "extensionButtonProminentHoverBackground": "扩展中突出操作的按钮被悬停时的颜色(比如 安装按钮)。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..500da66aaf --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "按 Enter 管理你的扩展。", + "searchFor": "按 Enter 以在应用商店中搜索“{0}”。", + "noExtensionsToInstall": "键入扩展名称" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..d3cb94eff1 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "扩展", + "app.extensions.json.recommendations": "扩展推荐项的列表。扩展的标识符始终为 \"${publisher}.${name}\"。例如 \"vscode.csharp\"。", + "app.extension.identifier.errorMessage": "预期的格式 \"${publisher}.${name}\"。例如: \"vscode.csharp\"。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..8b3aa1b13b --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "扩展: {0}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..640d084587 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "建议对这种类型的文件使用“{0}”扩展。", + "showRecommendations": "显示建议", + "neverShowAgain": "不再显示", + "close": "关闭", + "workspaceRecommended": "此工作区具有扩展建议。", + "ignoreExtensionRecommendations": "你是否要忽略所有推荐的扩展?", + "ignoreAll": "是,忽略全部", + "no": "否", + "cancel": "取消" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..6a779bc159 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "管理扩展", + "galleryExtensionsCommands": "安装库扩展", + "extension": "扩展", + "extensions": "扩展", + "view": "查看", + "extensionsConfigurationTitle": "扩展", + "extensionsAutoUpdate": "自动更新扩展", + "extensionsIgnoreRecommendations": "忽略推荐的扩展" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..87b768f188 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "打开扩展文件夹", + "installVSIX": "从 VSIX 安装...", + "InstallVSIXAction.success": "已成功安装扩展。重启以启用它。", + "InstallVSIXAction.reloadNow": "立即重新加载" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..15410b377c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "禁用其他键映射 ({0}) 以避免键绑定之间的冲突?", + "yes": "是", + "no": "否", + "betterMergeDisabled": "现已内置 Better Merge 扩展。此扩展已被安装并禁用,且能被卸载。", + "uninstall": "卸载", + "later": "稍后" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..991e01404e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "商店", + "installedExtensions": "已安装", + "searchInstalledExtensions": "已安装", + "recommendedExtensions": "推荐", + "searchExtensions": "在应用商店中搜索扩展", + "sort by installs": "排序依据: 安装计数", + "sort by rating": "排序依据: 分级", + "sort by name": "排序依据: 名称", + "suggestProxyError": "市场返回 \"ECONNREFUSED\"。请检查 \"http.proxy\" 设置。", + "extensions": "扩展", + "outdatedExtensions": "{0} 个过时的扩展" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..2e4aef330f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "扩展", + "no extensions found": "找不到扩展。", + "suggestProxyError": "市场返回 \"ECONNREFUSED\"。请检查 \"http.proxy\" 设置。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json new file mode 100644 index 0000000000..856df88d6e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "禁用其他键映射以避免键绑定之间的冲突?", + "yes": "是", + "no": "否" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..d72582c2aa --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "启用“{0}”也会启用其依赖项。是否要继续?", + "enable": "是", + "doNotEnable": "否", + "disableDependeciesConfirmation": "是想仅禁用“{0}”还是想同时禁用其依赖项?", + "disableOnly": "仅", + "disableAll": "全部", + "cancel": "取消", + "singleDependentError": "无法禁用扩展“{0}”。扩展“{1}”取决于此。", + "twoDependentsError": "无法禁用扩展“{0}”。扩展“{1}”和扩展“{2}”取决于此。", + "multipleDependentsError": "无法禁用扩展“{0}”。扩展“{1}”、“{2}”和其他扩展取决于此。", + "installConfirmation": "是否要安装“{0}”扩展?", + "install": "安装" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/chs/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..2c5c17d74d --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "Tweet 反馈", + "label.sendASmile": "通过 Tweet 向我们发送反馈。", + "patchedVersion1": "安装已损坏。", + "patchedVersion2": "如果提交了 bug,请指定此项。", + "sentiment": "您的体验如何?", + "smileCaption": "愉快", + "frownCaption": "忧伤", + "other ways to contact us": "联系我们的其他方式", + "submit a bug": "提交 bug", + "request a missing feature": "请求缺失功能", + "tell us why?": "告诉我们原因?", + "commentsHeader": "注释", + "tweet": "Tweet", + "character left": "剩余字符", + "characters left": "剩余字符", + "feedbackSending": "正在发送", + "feedbackSent": "谢谢", + "feedbackSendingError": "重试" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..ad1eb8fa8a --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "二进制文件查看器" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..1f73cf2eb8 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "文本文件编辑器", + "createFile": "创建文件", + "fileEditorWithInputAriaLabel": "{0}。文本文件编辑器。", + "fileEditorAriaLabel": "文本文件编辑器。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..5c2d1c929d --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "文件夹" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..e623188663 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "文件", + "revealInSideBar": "在侧边栏中显示", + "acceptLocalChanges": "使用你的更改并覆盖磁盘上的内容。", + "revertLocalChanges": "放弃你的更改并还原为磁盘上的内容" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..a2bb1df09c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "重试", + "rename": "重命名", + "newFile": "新建文件", + "newFolder": "新建文件夹", + "openFolderFirst": "先打开一个文件夹,以在其中创建文件或文件夹。", + "newUntitledFile": "新的无标题文件", + "createNewFile": "新建文件", + "createNewFolder": "新建文件夹", + "deleteButtonLabelRecycleBin": "移动到回收站(&&M)", + "deleteButtonLabelTrash": "移动到回收站(&&M)", + "deleteButtonLabel": "删除(&&D)", + "dirtyMessageFolderOneDelete": "你正在删除的文件夹有 1 个文件具有未保存的更改。是否继续?", + "dirtyMessageFolderDelete": "你正在删除的文件夹有 {0} 个文件具有未保存的更改。是否继续?", + "dirtyMessageFileDelete": "你正在删除的文件具有未保存的更改。是否继续?", + "dirtyWarning": "如果不保存,更改将丢失。", + "confirmMoveTrashMessageFolder": "是否确实要删除“{0}”及其内容?", + "confirmMoveTrashMessageFile": "是否确实要删除“{0}”?", + "undoBin": "可以从回收站还原。", + "undoTrash": "可以从回收站还原。", + "confirmDeleteMessageFolder": "是否确定要永久删除“{0}”及其内容?", + "confirmDeleteMessageFile": "是否确定要永久删除“{0}”?", + "irreversible": "此操作不可逆!", + "permDelete": "永久删除", + "delete": "删除", + "importFiles": "导入文件", + "confirmOverwrite": "目标文件夹中已存在具有相同名称的文件或文件夹。是否要替换它?", + "replaceButtonLabel": "替换(&&R)", + "copyFile": "复制", + "pasteFile": "粘贴", + "duplicateFile": "重复", + "openToSide": "打开到侧边", + "compareSource": "选择以进行比较", + "globalCompareFile": "比较活动文件与...", + "pickHistory": "选择要进行比较的之前已打开的文件", + "unableToFileToCompare": "无法将所选文件与“{0}”进行比较。", + "openFileToCompare": "首先打开文件以将其与另外一个文件比较。", + "compareWith": "将“{0}”与“{1}”比较", + "compareFiles": "比较文件", + "refresh": "刷新", + "save": "保存", + "saveAs": "另存为...", + "saveAll": "全部保存", + "saveAllInGroup": "保存组中的全部内容", + "saveFiles": "保存已更新文件", + "revert": "还原文件", + "focusOpenEditors": "专注于“打开的编辑器”视图", + "focusFilesExplorer": "关注文件资源浏览器", + "showInExplorer": "在侧边栏中显示活动文件", + "openFileToShow": "请先打开要在浏览器中显示的文件", + "collapseExplorerFolders": "在资源管理器中折叠文件夹", + "refreshExplorer": "刷新资源管理器", + "openFile": "打开文件...", + "openFileInNewWindow": "在新窗口中打开活动文件", + "openFileToShowInNewWindow": "请先打开要在新窗口中打开的文件", + "revealInWindows": "在资源管理器中显示", + "revealInMac": "在 Finder 中显示", + "openContainer": "打开所在的文件夹", + "revealActiveFileInWindows": "Windows 资源管理器中显示活动文件", + "revealActiveFileInMac": "在 Finder 中显示活动文件", + "openActiveFileContainer": "打开活动文件所在的文件夹", + "copyPath": "复制路径", + "copyPathOfActive": "复制活动文件的路径", + "emptyFileNameError": "必须提供文件或文件夹名。", + "fileNameExistsError": "此位置已存在文件或文件夹 **{0}**。请选择其他名称。", + "invalidFileNameError": "名称 **{0}** 作为文件或文件夹名无效。请选择其他名称。", + "filePathTooLongError": "名称 **{0}** 导致路径太长。请选择更短的名称。", + "compareWithSaved": "比较活动与已保存的文件", + "modifiedLabel": "{0} (磁盘上) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..d6828da6da --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "首先打开文件以复制其路径", + "openFileToReveal": "首先打开文件以展现" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..343d21db43 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "显示资源管理器", + "explore": "资源管理器", + "view": "查看", + "textFileEditor": "文本文件编辑器", + "binaryFileEditor": "二进制文件编辑器", + "filesConfigurationTitle": "文件", + "exclude": "配置 glob 模式以排除文件和文件夹。", + "files.exclude.boolean": "匹配文件路径所依据的 glob 模式。设置为 true 或 false 可启用或禁用该模式。", + "files.exclude.when": "对匹配文件的同级文件的其他检查。使用 $(basename) 作为匹配文件名的变量。", + "associations": "配置语言的文件关联(如: \"*.extension\": \"html\")。这些关联的优先级高于已安装语言的默认关联。", + "encoding": "读取和编写文件时将使用的默认字符集编码。", + "autoGuessEncoding": "启用时,会在打开文件时尝试猜测字符集编码", + "eol": "默认行尾字符。使用 \\n 表示 LF,\\r\\n 表示 CRLF。", + "trimTrailingWhitespace": "启用后,将在保存文件时剪裁尾随空格。", + "insertFinalNewline": "启用后,保存文件时在文件末尾插入一个最终新行。", + "files.autoSave.off": "永不自动保存更新后的文件。", + "files.autoSave.afterDelay": "配置 \"files.autoSaveDelay\" 后自动保存更新后的文件。", + "files.autoSave.onFocusChange": "编辑器失去焦点时自动保存更新后的文件。", + "files.autoSave.onWindowChange": "窗口失去焦点时自动保存更新后的文件。", + "autoSave": "控制已更新文件的自动保存。接受的值:“{0}”、\"{1}”、“{2}”(编辑器失去焦点)、“{3}”(窗口失去焦点)。如果设置为“{4}”,则可在 \"files.autoSaveDelay\" 中配置延迟。", + "autoSaveDelay": "控制在多少毫秒后自动保存更改过的文件。仅在“files.autoSave”设置为“{0}”时适用。", + "watcherExclude": "配置文件路径的 glob 模式以从文件监视排除。模式必须在绝对路径上匹配(例如 ** 前缀或完整路径需正确匹配)。更改此设置需要重启。如果在启动时遇到 Code 消耗大量 CPU 时间,则可以排除大型文件夹以减少初始加载。", + "hotExit.off": "禁用热退出。", + "hotExit.onExit": "应用程序关闭时将触发热退出。在 Windows/Linux 上关闭最后一个窗口或触发 workbench.action.quit 命令(命令托盘、键绑定、菜单)会引起应用程序关闭。下次启动时将还原所有已备份的窗口。", + "hotExit.onExitAndWindowClose": "应用程序关闭时将触发热退出。在 Windows/Linux 上关闭最后一个窗口、触发 workbench.action.quit 命令(命令托盘、键绑定、菜单)会引起应用程序关闭。对于任何有文件夹打开的窗口,则不论该窗口是否是最后一个窗口。下次启动时将还原所有未打开文件夹的窗口。若要还原打开有文件夹的窗口,请将“window.restoreWindows”设置为“all”。", + "hotExit": "控制是否在会话间记住未保存的文件,以允许在退出编辑器时跳过保存提示。", + "useExperimentalFileWatcher": "使用新的试验文件观察程序。", + "defaultLanguage": "分配给新文件的默认语言模式。", + "editorConfigurationTitle": "编辑器", + "formatOnSave": "保存时设置文件的格式。格式化程序必须可用,不能自动保存文件,并且不能关闭编辑器。", + "explorerConfigurationTitle": "文件资源管理器", + "openEditorsVisible": "在“打开的编辑器”窗格中显示的编辑器数量。将其设置为 0 可隐藏窗格。", + "dynamicHeight": "控制打开的编辑器部分的高度是否应动态适应元素数量。", + "autoReveal": "控制资源管理器是否应在打开文件时自动显示并选择它们。", + "enableDragAndDrop": "控制资源管理器是否应该允许通过拖放移动文件和文件夹。", + "sortOrder.default": "按名称的字母顺序排列文件和文件夹。文件夹显示在文件前。", + "sortOrder.mixed": "按名称的字母顺序排列文件和文件夹。两者穿插显示。", + "sortOrder.filesFirst": "按名称的字母顺序排列文件和文件夹。文件显示在文件夹前。", + "sortOrder.type": "按扩展名的字母顺序排列文件和文件夹。文件夹显示在文件前。", + "sortOrder.modified": "按最后修改日期降序排列文件和文件夹。文件夹显示在文件前。", + "sortOrder": "控制资源管理器文件和文件夹的排列顺序。除了默认排列顺序,你也可以设置为 \"mixed\" (文件和文件夹一起排序)、\"type\" (按文件类型排)、\"modified\" (按最后修改日期排)或是 \"filesFirst\" (将文件排在文件夹前)。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..3b43bbd0f9 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "放弃", + "overwrite": "覆盖", + "retry": "重试", + "readonlySaveError": "无法保存“{0}”: 文件写保护。选择“覆盖”以删除保护。 ", + "genericSaveError": "未能保存“{0}”: {1}", + "staleSaveError": "无法保存“{0}”: 磁盘上的内容较新。单击 **比较** 以比较你的版本和磁盘上的版本。", + "compareChanges": "比较", + "saveConflictDiffLabel": "{0} (on disk) ↔ {1} (in {2}) - 解决保存的冲突", + "userGuide": "使用右侧编辑器工具栏的操作来**撤消**你的更改或用你的更改来**覆盖**磁盘上的内容" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..70388cf630 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "无打开的文件夹", + "explorerSection": "文件资源管理器部分", + "noWorkspaceHelp": "尚未打开文件夹。", + "openFolder": "打开文件夹" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..8ab37ca734 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "文件资源管理器部分", + "treeAriaLabel": "文件资源管理器" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..a13c32d825 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "键入文件名。按 Enter 以确认或按 Esc 以取消。", + "filesExplorerViewerAriaLabel": "{0},文件资源管理器", + "dropFolders": "你是否要将文件夹添加到工作区?", + "dropFolder": "你是否要将文件夹添加到工作区?", + "addFolders": "添加文件夹(&&A)", + "addFolder": "添加文件夹(&&A)", + "confirmOverwriteMessage": "目标文件夹中已存在“{0}”。是否要将其替换?", + "irreversible": "此操作不可逆!", + "replaceButtonLabel": "替换(&&R)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..2eeb43ba3d --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "打开的编辑器", + "openEditosrSection": "打开的编辑器部分", + "treeAriaLabel": "打开的编辑器: 活动文件列表", + "dirtyCounter": "{0} 个未保存" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..c329c15533 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}, 编辑器组", + "openEditorAriaLabel": "{0}, 打开编辑器", + "saveAll": "全部保存", + "closeAllUnmodified": "关闭未更改", + "closeAll": "全部关闭", + "compareWithSaved": "与已保存文件比较", + "close": "关闭", + "closeOthers": "关闭其他" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/chs/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..aecd7a0de7 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0} 个未保存的文件" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/chs/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..19c4a6cf27 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (磁盘上已删除)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json new file mode 100644 index 0000000000..46656ecccd --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "switchToChangesView": "切换到更改视图", + "openInEditor": "切换到编辑器视图", + "workbenchStage": "暂存", + "workbenchUnstage": "取消暂存", + "stageSelectedLines": "暂存选定行", + "unstageSelectedLines": "取消暂存选定的行", + "revertSelectedLines": "还原所选行", + "confirmRevertMessage": "是否确实要还原所选更改?", + "irreversible": "此操作不可逆!", + "revertChangesLabel": "还原更改(&&R)", + "openChange": "打开更改", + "openFile": "打开文件", + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/gitActions.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/gitActions.i18n.json new file mode 100644 index 0000000000..c5d15175fd --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/gitActions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openChange": "打开更改", + "openFile": "打开文件", + "init": "初始化", + "refresh": "刷新", + "stageChanges": "暂存", + "stageAllChanges": "全部暂存", + "confirmUndoMessage": "是否确定要清理所有更改?", + "confirmUndoAllOne": "{0} 个文件中存在未暂存的更改。\n\n此操作是不可逆的!", + "confirmUndoAllMultiple": "{0} 个文件中存在未暂存的更改。\n\n此操作是不可逆的!", + "cleanChangesLabel": "清理更改(&&C)", + "confirmUndo": "是否确实要清理“{0}”中的更改?", + "irreversible": "此操作不可逆!", + "undoChanges": "清理", + "undoAllChanges": "全部清理", + "unstage": "取消暂存", + "unstageAllChanges": "取消全部暂存", + "dirtyTreeCheckout": "无法签出。请首先提交或存储工作。", + "commitStaged": "提交已暂存文件", + "commitStagedAmend": "已暂存提交(修改)", + "commitStagedSignedOff": "提交暂存数据(已签收)", + "commit": "Commit", + "commitMessage": "提交消息", + "commitAll": "全部提交", + "commitAllSignedOff": "提交所有数据(已签收)", + "commitAll2": "全部提交", + "commitStaged2": "提交已暂存文件", + "dirtyTreePull": "无法拉取。请首先提交或存储工作。", + "authFailed": "在 GIT 远程上进行身份验证失败。", + "pushToRemote": "推送到...", + "pushToRemotePickMessage": "选择将分支“{0}”推送到的远程位置:", + "publish": "发布", + "confirmPublishMessage": "是否确定要将“{0}”发布到“{1}”?", + "confirmPublishMessageButton": "发布(&&P)", + "publishPickMessage": "选取要将分支“{0}”发布到的远程:", + "sync is unpredictable": "此操作从“{0}”推送和拉取提交。", + "ok": "确定", + "cancel": "取消", + "never again": "好,永不再显示", + "undoLastCommit": "撤消上次提交" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json new file mode 100644 index 0000000000..1b520c0cf3 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refAriaLabel": "{0},GIT", + "checkoutBranch": "{0} 处的分支", + "checkoutRemoteBranch": "{0} 处的远程分支", + "checkoutTag": "{0} 处的 Tag", + "alreadyCheckedOut": "分支 {0} 已是当前分支", + "branchAriaLabel": "{0},GIT 分支", + "createBranch": "创建分支 {0}", + "noBranches": "无其他分支", + "notValidBranchName": "请提供有效的分支名称" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/gitServices.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/gitServices.i18n.json new file mode 100644 index 0000000000..b0b3e2380c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/gitServices.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cantOpen": "无法打开此 git 资源。", + "gitIndexChanges": "{0} (index) ↔ {1}", + "gitIndexChangesDesc": "{0} - 对索引的更改", + "gitIndexChangesRenamed": "{0} ← {1}", + "gitIndexChangesRenamedDesc": "{0} - 已重命名 - 对索引的更改", + "workingTreeChanges": "{0} (HEAD) ↔ {1}", + "workingTreeChangesDesc": "{0} - 对工作树的更改", + "gitMergeChanges": "{0} (merge) ↔ {1}", + "gitMergeChangesDesc": "{0} - 合并更改", + "updateGit": "你似乎已安装 git {0}。在 git >=2.0.0 情况下代码工作最佳。", + "download": "下载", + "neverShowAgain": "不再显示", + "configureUsernameEmail": "请配置 GIT 用户名和电子邮件。", + "badConfigFile": "Git {0}", + "unmergedChanges": "提交更改前,你应首先解决未合并的更改。", + "showOutput": "显示输出", + "cancel": "取消", + "checkNativeConsole": "运行 GIT 操作存在问题。请审阅输出或使用控制台检查你的存储库的状态。", + "changesFromIndex": "{0} (index)", + "changesFromIndexDesc": "{0} - 对索引的更改", + "changesFromTree": "{0} ({1})", + "changesFromTreeDesc": "{0} - 对 {1} 的更改", + "cantOpenResource": "无法打开此 git 资源。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json new file mode 100644 index 0000000000..ad7d0ca01f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "publishBranch": "发布分支", + "syncBranch": "同步更改", + "gitNotEnabled": "此工作区中未启用 GIT。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json new file mode 100644 index 0000000000..8fe7c404ca --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gitProgressBadge": "正在运行 GIT 状态", + "gitPendingChangesBadge": "{0} 个挂起的更改", + "toggleGitViewlet": "显示 GIT", + "git": "GIT", + "view": "查看", + "gitCommands": "GIT 命令", + "gitConfigurationTitle": "GIT", + "gitEnabled": "是否启用了 GIT", + "gitPath": "可执行 GIT 的路径", + "gitAutoRefresh": "是否已启用自动刷新", + "gitAutoFetch": "是否启用了自动提取。", + "gitLongCommit": "是否针对长段提交消息进行警告", + "gitLargeRepos": "始终允许大型存储库由 Code 托管。", + "confirmSync": "同步 Git 存储库前请先进行确认。", + "countBadge": "控制 git 徽章计数器。", + "checkoutType": "控制列出哪些分支类型。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json new file mode 100644 index 0000000000..d0f5b2cd19 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "needMessage": "请提供提交消息。您可以始终按下“{0}”以提交更改。如果存在任何暂存的更改,将仅提交这些更改;否则,提交所有更改。", + "nothingToCommit": "在有一些更改要提交时,键入提交信息,并按下“{0}”以提交更改。如果存在任何暂存的更改,将仅提交这些更改;否则,提交所有更改。", + "longCommit": "建议保持提交的第一行在 50 个字符以内。可以随时使用更多行显示额外信息。", + "commitMessage": "Message (press {0} to commit)", + "commitMessageAriaLabel": "GIT: 键入提交信息并按 {0} 以提交", + "treeAriaLabel": "GIT 更改视图", + "showOutput": "显示 GIT 输出" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json new file mode 100644 index 0000000000..e82f1345be --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stagedChanges": "暂存的更改", + "allChanges": "更改", + "mergeChanges": "合并更改", + "outsideOfWorkspace": "此文件位于当前工作区之外。", + "modified-char": "M", + "added-char": "A", + "deleted-char": "D", + "renamed-char": "R", + "copied-char": "C", + "untracked-char": "U", + "ignored-char": "!", + "title-index-modified": "已在索引中修改", + "title-modified": "已修改", + "title-index-added": "已添加到索引", + "title-index-deleted": "已在索引中删除", + "title-deleted": "已删除", + "title-index-renamed": "已在索引中重新命名", + "title-index-copied": "已在索引中复制", + "title-untracked": "未跟踪的", + "title-ignored": "已忽略", + "title-conflict-both-deleted": "冲突: 二者均已删除", + "title-conflict-added-by-us": "冲突: 已由我们添加", + "title-conflict-deleted-by-them": "冲突: 已由他们删除", + "title-conflict-added-by-them": "冲突: 已由他们添加", + "title-conflict-deleted-by-us": "冲突: 已由我们删除", + "title-conflict-both-added": "冲突: 二者均已添加", + "title-conflict-both-modified": "冲突: 二者均已修改", + "fileStatusAriaLabel": "文件夹 {1} 中的文件 {0} 具有状态: {2},GIT", + "ariaLabelStagedChanges": "暂存的更改,GIT", + "ariaLabelChanges": "更改,GIT", + "ariaLabelMerge": "合并,GIT" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json new file mode 100644 index 0000000000..3c77ddc9ce --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disabled": "在设置中禁用 GIT。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json new file mode 100644 index 0000000000..9291485e76 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noGit": "此工作区尚未使用 Git 源代码管理。", + "gitinit": "初始化 GIT 存储库" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json new file mode 100644 index 0000000000..46e6fd3988 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "macInstallWith": "可以使用 {0} 进行安装,也可以从 {1} 下载,或者只需在终端提示上键入 {3} 来安装 {2} 命令行开发人员工具。 ", + "winInstallWith": "可以使用 {0} 进行安装,也可以从 {1} 下载。", + "linuxDownloadFrom": "可从 {0} 中下载它。", + "downloadFrom": "可从 {0} 中下载它。", + "looksLike": "你的系统上似乎未安装 GIT。", + "pleaseRestart": "安装 GIT 后,请重启 VSCode。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json new file mode 100644 index 0000000000..75237729cf --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "huge": "你的存储库中似乎存在大量活动更改。\n这会导致 Code 变得非常缓慢。", + "setting": "可通过以下设置永久禁用此警告:", + "allo": "允许使用大型存储库" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json new file mode 100644 index 0000000000..51c0284f44 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrongRoot": "此目录似乎包含在 GIT 存储库中。", + "pleaseRestart": "打开存储库的根目录以访问 GIT 功能。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json b/i18n/chs/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json new file mode 100644 index 0000000000..49024fdd8b --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspaceHelp": "尚未打开文件夹。", + "pleaseRestart": "通过 GIT 存储库打开文件夹以访问 GIT 功能。", + "openFolder": "打开文件夹" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json new file mode 100644 index 0000000000..cf06d065b8 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSCMViewlet": "显示 SCM", + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json b/i18n/chs/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json new file mode 100644 index 0000000000..489424592e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "valid": "提供一个有效的 GIT 存储库 URL", + "url": "存储库 URL", + "directory": "目标克隆目录", + "cloning": "正在克隆存储库“{0}”...", + "already exists": "目标存储库已存在,请选择要克隆到的另一个目录。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json b/i18n/chs/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json new file mode 100644 index 0000000000..6f2dc5416f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/git/node/git.lib.i18n.json b/i18n/chs/src/vs/workbench/parts/git/node/git.lib.i18n.json new file mode 100644 index 0000000000..365ea5d5ae --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/git/node/git.lib.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorBuffer": "无法从 git 中打开文件", + "fileBinaryError": "文件似乎是二进制文件,无法作为文档打开" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..39ffff008a --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "HTML 预览", + "devtools.webview": "开发人员: Webview 工具" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/chs/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..0d0620bccf --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "无效的编辑器输入。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/chs/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..6d0cbdacfb --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "开发人员: Webview 工具" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/chs/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..9393200b50 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "查看", + "problems.view.show.label": "显示问题", + "problems.panel.configuration.title": "问题预览", + "problems.panel.configuration.autoreveal": "控制问题预览是否应在打开文件时自动显示它们。", + "markers.panel.title.problems": "问题", + "markers.panel.aria.label.problems.tree": "按文件分组的问题", + "markers.panel.no.problems.build": "目前尚未在工作区检测到问题。", + "markers.panel.no.problems.filters": "使用提供的筛选条件未找到结果", + "markers.panel.action.filter": "筛选器问题", + "markers.panel.filter.placeholder": "按类型或文本进行筛选", + "markers.panel.filter.errors": "错误", + "markers.panel.filter.warnings": "警告", + "markers.panel.filter.infos": "信息", + "markers.panel.single.error.label": "1 个错误", + "markers.panel.multiple.errors.label": "{0} 个错误", + "markers.panel.single.warning.label": "1 条警告", + "markers.panel.multiple.warnings.label": "{0} 条警告", + "markers.panel.single.info.label": "1 条信息", + "markers.panel.multiple.infos.label": "{0} 条信息", + "markers.panel.single.unknown.label": "1 个未知", + "markers.panel.multiple.unknowns.label": "{0} 个未知", + "markers.panel.at.ln.col.number": "({0},{1})", + "problems.tree.aria.label.resource": "含有 {1} 问题的 {0}", + "problems.tree.aria.label.error.marker": "由 {0} 生成的错误: 第 {2} 行处的 {1} 和字符 {3}", + "problems.tree.aria.label.error.marker.nosource": "错误: 第 {1} 行处的 {0} 和字符 {2}", + "problems.tree.aria.label.warning.marker": "由 {0} 生成的警告: 第 {2} 行处的 {1} 和字符 {3}", + "problems.tree.aria.label.warning.marker.nosource": "警告: 第 {1} 行处的 {0} 和字符 {2}", + "problems.tree.aria.label.info.marker": "由 {0} 生成的信息: 第 {2} 行处的 {1} 和字符 {3}", + "problems.tree.aria.label.info.marker.nosource": "信息: 第 {1} 行处的 {0} 和字符 {2}", + "problems.tree.aria.label.marker": "由 {0} 生成的问题: 第 {2} 行处的 {1} 和字符 {3}", + "problems.tree.aria.label.marker.nosource": "问题: 第 {1} 行处的 {0} 和字符 {2}", + "errors.warnings.show.label": "显示错误和警告" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/chs/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..166cb48a5a --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "复制" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..155c3d6f00 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "您愿意参与一次简短的反馈调查吗?", + "takeSurvey": "参加调查", + "remindLater": "稍后提醒", + "neverAgain": "不再显示" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..8615e07965 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "输出", + "viewCategory": "查看", + "clearOutput.label": "清除输出" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/chs/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..2669e26b32 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "切换输出", + "clearOutput": "清除输出", + "toggleOutputScrollLock": "切换输出 Scroll Lock", + "switchToOutput.label": "切换到输出" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/chs/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..8f49259e11 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0},输出面板", + "outputPanelAriaLabel": "输出面板" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/chs/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..f4e79b4369 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "输出", + "channel": "用于“{0}”" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..935fa6bd4a --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "检测到启动缓慢", + "slow.detail": "抱歉,出现了启动缓慢的情况。请重启“{0}”并启用分析,将分析文件与我们共享,我们会努力提高启动速度。", + "prof.message": "已成功创建描述文件。", + "prof.detail": "请创建问题并手动附加以下文件:\n{0}", + "prof.restartAndFileIssue": "创建问题并重启", + "prof.restart": "重启", + "prof.thanks": "感谢您的帮助。", + "prof.detail.restart": "需要重新启动才能继续使用“{0}”。再次感谢您的贡献。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..eae5536e95 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "按所需的键组合,然后按 Enter。按 Esc 可取消。", + "defineKeybinding.chordsTo": "加上" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..b717894011 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "键盘快捷方式", + "SearchKeybindings.AriaLabel": "搜索键绑定", + "SearchKeybindings.Placeholder": "搜索键绑定", + "sortByPrecedene": "按优先级排序", + "header-message": "高级自定义请打开和编辑", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "键绑定", + "changeLabel": "更改键绑定", + "addLabel": "添加键绑定", + "removeLabel": "删除键绑定", + "resetLabel": "重置键绑定", + "showConflictsLabel": "显示冲突", + "copyLabel": "复制", + "error": "编辑键绑定时发生错误“{0}”。请打开 \"keybindings.json\" 文件并检查。", + "command": "命令", + "keybinding": "键绑定", + "source": "源", + "when": "何时", + "editKeybindingLabelWithKey": "更改键绑定{0}", + "editKeybindingLabel": "更改键绑定", + "addKeybindingLabelWithKey": "添加键绑定", + "addKeybindingLabel": "添加键绑定", + "commandAriaLabel": "命令为 {0}。", + "keybindingAriaLabel": "键绑定为 {0}。", + "noKeybinding": "未分配键绑定。", + "sourceAriaLabel": "源为 {0}。", + "whenAriaLabel": "时间为 {0}。", + "noWhen": "没有时间上下文。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..e608587e44 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "定义键绑定", + "defineKeybinding.kbLayoutErrorMessage": "在当前键盘布局下无法生成此组合键。", + "defineKeybinding.kbLayoutLocalAndUSMessage": "在你的键盘布局上为 **{0}**(美国标准布局上为 **{1}**)。", + "defineKeybinding.kbLayoutLocalMessage": "在你的键盘布局上为 **{0}**。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..4b46981015 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "默认首选项编辑器", + "keybindingsEditor": "键绑定编辑器", + "preferences": "首选项" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..58344b70d5 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "打开用户设置", + "openGlobalKeybindings": "打开键盘快捷方式", + "openGlobalKeybindingsFile": "打开键盘快捷方式文件", + "openWorkspaceSettings": "打开工作区设置", + "openFolderSettings": "打开文件夹设置", + "pickFolder": "选择文件夹", + "configureLanguageBasedSettings": "配置语言特定的设置...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "选择语言" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..eeeb3e3c3d --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "默认设置", + "SearchSettingsWidget.AriaLabel": "搜索设置", + "SearchSettingsWidget.Placeholder": "搜索设置", + "totalSettingsMessage": "总计 {0} 个设置", + "noSettingsFound": "无结果", + "oneSettingFound": "1 个设置匹配", + "settingsFound": "{0} 个设置匹配", + "fileEditorWithInputAriaLabel": "{0}。文本文件编辑器。", + "fileEditorAriaLabel": "文本文件编辑器。", + "preferencesAriaLabel": "默认首选项。只读文本编辑器。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..6a92aba4cf --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "将设置放入此处以覆盖\"默认设置\"。", + "errorInvalidConfiguration": "无法写入设置。请更正文件中的错误/警告,然后重试。", + "emptyWorkspaceSettingsHeader": "将设置放入此处以覆盖\"用户设置\"。", + "emptyFolderSettingsHeader": "将文件夹设置放入此处以覆盖\"工作区设置\"。", + "defaultFolderSettingsTitle": "默认文件夹设置", + "defaultSettingsTitle": "默认设置", + "noSettingsFound": "未找到设置。", + "editTtile": "编辑", + "replaceDefaultValue": "在设置中替换", + "copyDefaultValue": "复制到设置", + "unsupportedPHPExecutablePathSetting": "此设置必须是“用户设置”。若要为工作区配置 PHP,请打开 PHP 文件并单击状态栏中的“PHP 路径”。", + "unsupportedWorkspaceSetting": "此设置必须是“用户设置”。", + "unsupportedWorkbenchSetting": "当前无法应用此设置。将在直接打开此文件夹时应用。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..cce0ee474a --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "首先打开文件夹以创建工作区设置", + "emptyKeybindingsHeader": "将键绑定放入此文件中以覆盖默认值", + "defaultKeybindings": "默认的键绑定", + "folderSettingsName": "{0} (文件夹设置)", + "fail.createSettings": "无法创建“{0}”({1})。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..be1a87b422 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "文件夹设置" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..2d06cf4e1f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "默认", + "user": "用户", + "meta": "元数据", + "option": "选项" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..31bee75331 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "用户设置", + "workspaceSettingsTarget": "工作区设置" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..0d46bc1290 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "常用设置", + "noSettings": "没有设置", + "defaultKeybindingsHeader": "通过将键绑定放入键绑定文件中来覆盖键绑定。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..6605bccb5d --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "显示所有命令", + "clearCommandHistory": "清除命令历史记录", + "showCommands.label": "命令面板...", + "entryAriaLabelWithKey": "{0}、{1} ,命令", + "entryAriaLabel": "{0},命令", + "canNotRun": "无法从此处运行命令“{0}”。", + "actionNotEnabled": "在当前上下文中没有启用命令“{0}”。", + "recentlyUsed": "最近使用", + "morecCommands": "其他命令", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "没有匹配的命令" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..9bc4afe7c7 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "转到行...", + "gotoLineLabelEmptyWithLimit": "键入要导航到的介于 1 和 {0} 之间的行号", + "gotoLineLabelEmpty": "键入要导航到的行号", + "gotoLineColumnLabel": "转到行 {0} 和字符 {1}", + "gotoLineLabel": "转至行 {0}", + "gotoLineHandlerAriaLabel": "键入要导航到的行号。", + "cannotRunGotoLine": "首先打开文本文件以转到行" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..b23fbf022f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "转到文件中的符号...", + "symbols": "符号({0})", + "method": "方法({0})", + "function": "函数({0})", + "_constructor": "构造函数({0})", + "variable": "变量({0})", + "class": "类({0})", + "interface": "接口({0})", + "namespace": "命名空间({0})", + "package": "包({0})", + "modules": "模块({0})", + "property": "属性({0})", + "enum": "枚举({0})", + "string": "字符串({0})", + "rule": "规则({0})", + "file": "文件({0})", + "array": "数组({0})", + "number": "编号({0})", + "boolean": "布尔值({0})", + "object": "对象({0})", + "key": "键({0})", + "entryAriaLabel": "{0},符号", + "noSymbolsMatching": "没有匹配的符号", + "noSymbolsFound": "找不到符号", + "gotoSymbolHandlerAriaLabel": "键入以减少当前活动编辑器的符号", + "cannotRunGotoSymbolInFile": "没有该文件的任何符号信息", + "cannotRunGotoSymbol": "首先打开文本文件以转到符号" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..a8a3d0b76c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},选取器帮助", + "globalCommands": "全局命令", + "editorCommands": "编辑器命令" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..43bb25c105 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "显示并运行命令", + "gotoLineDescriptionMac": "转到行", + "gotoLineDescriptionWin": "转到行", + "gotoSymbolDescription": "转到“文件”中的“符号”", + "gotoSymbolDescriptionScoped": "按类别转到“文件”中的“符号”", + "helpDescription": "显示帮助", + "viewPickerDescription": "打开视图" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..0a57995cb6 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},视图选取器", + "views": "视图", + "panels": "面板", + "terminals": "终端", + "terminalTitle": "{0}: {1}", + "channels": "输出", + "openView": "打开视图", + "quickOpenView": "Quick Open 视图" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..dbfb8c96e8 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "设置已更改,需要重启才能生效。", + "relaunchSettingDetail": "按下“重启”按钮以重新启动 {0} 并启用该设置。", + "restart": "重启", + "relaunchWorkspaceMessage": "此工作区更改需要重载扩展系统。", + "reload": "重新加载" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..069b2778bb --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "编辑器导航线中被修改行的背景颜色。", + "editorGutterAddedBackground": "编辑器导航线中已插入行的背景颜色。", + "editorGutterDeletedBackground": "编辑器导航线中被删除行的背景颜色。", + "overviewRulerModifiedForeground": "概述已修改内容的标尺标记颜色。", + "overviewRulerAddedForeground": "概述已添加内容的标尺标记颜色。", + "overviewRulerDeletedForeground": "概述已删除内容的标尺标记颜色。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..ee8c3664c7 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "显示 Git", + "source control": "源代码管理", + "toggleSCMViewlet": "显示 SCM", + "view": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..acb091b1a3 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0} 个挂起的更改" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..f72dd9f715 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "安装其他 SCM 提供程序...", + "switch provider": "切换源代码管理系统..." +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..2f13ef2025 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "消息(按 {0} 提交)", + "installAdditionalSCMProviders": "安装其他 SCM 提供程序...", + "no open repo": "没有可用的源控件。", + "source control": "源代码管理", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..714f8b8bf2 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "文件和符号结果", + "fileResults": "文件结果" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..48b4b29267 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},文件选取器", + "searchResults": "搜索结果" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..79ccca2992 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},符号选取器", + "symbols": "符号结果", + "noSymbolsMatching": "没有匹配的符号", + "noSymbolsWithoutInput": "键入以搜索符号" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..2f4f7c9fbe --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "输入", + "useIgnoreFilesDescription": "使用忽略文件", + "useExcludeSettingsDescription": "使用排除设置" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..6a3a04581b --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (Replace Preview)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..76967bab4a --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "转到工作区中的符号...", + "name": "搜索", + "showSearchViewlet": "显示搜索", + "view": "查看", + "findInFiles": "在文件中查找", + "openAnythingHandlerDescription": "转到文件", + "openSymbolDescriptionNormal": "转到工作区中的符号", + "searchOutputChannelTitle": "搜索", + "searchConfigurationTitle": "搜索", + "exclude": "配置 glob 模式以在搜索中排除文件和文件夹。从 files.exclude 设置中继承所有 glob 模式。", + "exclude.boolean": "匹配文件路径所依据的 glob 模式。设置为 true 或 false 可启用或禁用该模式。", + "exclude.when": "对匹配文件的同级文件的其他检查。使用 $(basename) 作为匹配文件名的变量。", + "useRipgrep": "控制是否在文本搜索中使用 ripgrep", + "useIgnoreFilesByDefault": "控制在新工作区中搜索时是否默认使用 .gitignore 和 .ignore 文件。", + "search.quickOpen.includeSymbols": "配置为在 Quick Open 文件结果中包括全局符号搜索的结果。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..d61d26ea92 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "显示下一个搜索包含模式", + "previousSearchIncludePattern": "显示上一个搜索包含模式", + "nextSearchExcludePattern": "显示下一个搜索排除模式", + "previousSearchExcludePattern": "显示上一个搜索排除模式", + "nextSearchTerm": "显示下一个搜索词", + "previousSearchTerm": "显示上一个搜索词", + "focusNextInputBox": "聚焦下一个输入框", + "focusPreviousInputBox": "聚焦上一个输入框", + "replaceInFiles": "在文件中替换", + "findInWorkspace": "在工作区中查找...", + "findInFolder": "在文件夹中查找...", + "RefreshAction.label": "刷新", + "ClearSearchResultsAction.label": "清除搜索结果", + "FocusNextSearchResult.label": "聚焦下一搜索结果", + "FocusPreviousSearchResult.label": "聚焦上一搜索结果", + "RemoveAction.label": "删除", + "file.replaceAll.label": "全部替换", + "match.replace.label": "替换" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..d01c9c5463 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "其他文件", + "searchFileMatches": "已找到 {0} 个文件", + "searchFileMatch": "已找到 {0} 个文件", + "searchMatches": "已找到 {0} 个匹配项", + "searchMatch": "已找到 {0} 个匹配项", + "folderMatchAriaLabel": "根目录 {1} 中找到 {0} 个匹配,搜索结果", + "fileMatchAriaLabel": "文件夹 {2} 的文件 {1} 中有 {0} 个匹配项,搜索结果", + "replacePreviewResultAria": "在第 {2} 列替换词组 {0} 为 {1},同行文本为 {3}", + "searchResultAria": "在第 {1} 列找到词组 {0},同行文本为 {2}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..1d440e78ee --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "切换搜索详细信息", + "searchScope.includes": "要包含的文件", + "label.includes": "搜索包含模式", + "searchScope.excludes": "要排除的文件", + "label.excludes": "搜索排除模式", + "replaceAll.confirmation.title": "全部替换", + "replaceAll.confirm.button": "替换", + "replaceAll.occurrence.file.message": "已将 {1} 文件中出现的 {0} 替换为“{2}”。", + "removeAll.occurrence.file.message": "已替换 {1} 文件中出现的 {0}。", + "replaceAll.occurrence.files.message": "已将 {1} 文件中出现的 {0} 替换为“{2}”。", + "removeAll.occurrence.files.message": "已替换 {1} 文件中出现的 {0}。", + "replaceAll.occurrences.file.message": "已将 {1} 文件中出现的 {0} 替换为“{2}”。", + "removeAll.occurrences.file.message": "已替换 {1} 文件中出现的 {0}。", + "replaceAll.occurrences.files.message": "已将 {1} 个文件中出现的 {0} 处替换为“{2}”。 ", + "removeAll.occurrences.files.message": "已替换 {1} 文件中出现的 {0}。", + "removeAll.occurrence.file.confirmation.message": "是否将 {1} 文件中出现的 {0} 替换为“{2}”?", + "replaceAll.occurrence.file.confirmation.message": "替换 {1} 个文件中出现的全部 {0} 处? ", + "removeAll.occurrence.files.confirmation.message": "是否将 {1} 文件中出现的 {0} 替换为“{2}”?", + "replaceAll.occurrence.files.confirmation.message": "是否替换 {1} 文件中出现的 {0}?", + "removeAll.occurrences.file.confirmation.message": "是否将 {1} 文件中出现的 {0} 替换为“{2}”?", + "replaceAll.occurrences.file.confirmation.message": "是否替换 {1} 文件中出现的 {0}?", + "removeAll.occurrences.files.confirmation.message": "是否将 {1} 文件中出现的 {0} 替换为“{2}”?", + "replaceAll.occurrences.files.confirmation.message": "是否替换 {1} 文件中出现的 {0}?", + "treeAriaLabel": "搜索结果", + "searchPathNotFoundError": "找不到搜索路径: {0}", + "searchMaxResultsWarning": "结果集仅包含所有匹配项的子集。请使你的搜索更加具体,减少结果。", + "searchCanceled": "在找到结果前取消了搜索 - ", + "noResultsIncludesExcludes": "在“{0}”中找不到结果(“{1}”除外) - ", + "noResultsIncludes": "“{0}”中未找到任何结果 - ", + "noResultsExcludes": "除“{0}”外,未找到任何结果 - ", + "noResultsFound": "找不到结果。查看设置中配置的排除项并忽略文件 - ", + "rerunSearch.message": "再次搜索", + "rerunSearchInAll.message": "在所有文件中再次搜索", + "openSettings.message": "打开设置", + "openSettings.learnMore": "了解详细信息", + "ariaSearchResultsStatus": "搜索 {1} 文件中返回的 {0} 个结果", + "search.file.result": "{1} 文件中有 {0} 个结果", + "search.files.result": "{1} 文件中有 {0} 个结果", + "search.file.results": "{1} 文件中有 {0} 个结果", + "search.files.results": "{1} 文件中有 {0} 个结果", + "searchWithoutFolder": "尚未打开文件夹。当前仅可搜索打开的文件夹 - ", + "openFolder": "打开文件夹" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..62eae22a14 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "全部替换(提交搜索以启用)", + "search.action.replaceAll.enabled.label": "全部替换", + "search.replace.toggle.button.title": "切换替换", + "label.Search": "搜索: 键入搜索术语,然后按 Enter 进行搜索或按 Escape 取消", + "search.placeHolder": "搜索", + "label.Replace": "替换: 键入替换术语,然后按 Enter 预览或按 Escape 取消", + "search.replace.placeHolder": "替换", + "regexp.validationFailure": "表达式与所有内容相匹配" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/chs/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..76967cf144 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "工作区中没有名为“{0}”的文件夹 " +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..e3848b865c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "添加代码段。", + "vscode.extension.contributes.snippets-language": "此代码片段参与的语言标识符。", + "vscode.extension.contributes.snippets-path": "代码片段文件的路径。该路径相对于扩展文件夹,通常以 \"./snippets/\" 开头。", + "invalid.language": "“contributes.{0}.language”中存在未知的语言。提供的值: {1}", + "invalid.path.0": "“contributes.{0}.path”中应为字符串。提供的值: {1}", + "invalid.path.1": "“contributes.{0}.path”({1})应包含在扩展的文件夹({2})内。这可能会使扩展不可移植。", + "badVariableUse": "“{0}”代码片段很可能混淆了片段变量和片段占位符。有关详细信息,请访问 https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..43c5c7fc5b --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "插入代码片段" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..6d9b51a329 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "选择代码片段的语言", + "openSnippet.errorOnCreate": "无法创建 {0}", + "openSnippet.label": "打开用户代码段", + "preferences": "首选项", + "snippetSchema.json.default": "空代码片段", + "snippetSchema.json": "用户代码片段配置", + "snippetSchema.json.prefix": "在 Intellisense 中选择代码片段时将使用的前缀", + "snippetSchema.json.body": "代码片段的内容。使用“$1”和“${1:defaultText}”定义光标位置,使用“$0”定义最终光标位置。使用“${varName}”和“${varName:defaultText}”插入变量值,例如“这是文件:$TM_FILENAME”。", + "snippetSchema.json.description": "代码片段描述。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..9be112fad1 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "用户代码片段", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0},{1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..ed636e185e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "当其前缀匹配时插入代码段。当 \"quickSuggestions\" 未启用时,效果最佳。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..74b973573c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "帮助我们改善对 {0} 的支持", + "takeShortSurvey": "参与小调查", + "remindLater": "稍后提醒", + "neverAgain": "不再显示" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..4c38e8b3c9 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "您愿意参与一次简短的反馈调查吗?", + "takeSurvey": "参与调查", + "remindLater": "稍后提醒", + "neverAgain": "不再显示" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..c02b126fd9 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "键入生成任务的名称", + "noTasksMatching": "没有匹配的任务", + "noTasksFound": "找不到生成任务" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..c03f420dbf --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},任务", + "recentlyUsed": "最近使用的任务", + "configured": "已配置的任务", + "detected": "检测到的任务", + "customizeTask": "配置任务" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..b7e9f72377 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "键入要重启的任务的名称", + "noTasksMatching": "没有匹配的任务", + "noTasksFound": "没有发现要重启的任务" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..e4dd0df2e9 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "键入要运行的任务的名称", + "noTasksMatching": "没有匹配的任务", + "noTasksFound": "找不到任务" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..afebd74c4c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Type the name of a task to terminate", + "noTasksMatching": "没有匹配的任务", + "noTasksFound": "No tasks to terminate found" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..6e537ddcdc --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "键入测试任务的名称", + "noTasksMatching": "没有匹配的任务", + "noTasksFound": "没有找到测试任务" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..100ab7076c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "警告: options.cwd 必须属于字符串类型。正在忽略值 {0}\n", + "ConfigurationParser.noargs": "错误: 命令参数必须是字符串数组。提供的值为:\n{0}", + "ConfigurationParser.noShell": "警告: 仅当在终端中执行任务时支持 shell 配置。", + "ConfigurationParser.noName": "错误: 声明范围内的问题匹配程序必须具有名称:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "警告: 已定义的问题匹配程序未知。受支持的类型为 string | ProblemMatcher | (string | ProblemMatcher)[]。\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "错误: 无效的 problemMatcher 引用: {0}\n", + "ConfigurationParser.noTaskName": "错误: 任务必须提供 taskName 属性。将忽略该任务。\n{0}\n", + "taskConfiguration.shellArgs": "警告: 任务“{0}”是 shell 命令,该命令的名称或其中一个参数具有非转义空格。若要确保命令行引用正确,请将参数合并到该命令。", + "taskConfiguration.noCommandOrDependsOn": "错误:任务“{0}”既不指定命令,也不指定 dependsOn 属性。将忽略该任务。其定义为:\n{1}", + "taskConfiguration.noCommand": "错误: 任务“{0}”未定义命令。将忽略该任务。其定义为:\n{1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..bbda7fac79 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "实际任务类型", + "TaskDefinition.properties": "任务类型的其他属性", + "TaskTypeConfiguration.noType": "任务类型配置缺少必需的 \"taskType\" 属性", + "TaskDefinitionExtPoint": "配置任务种类" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..4f71c0df40 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": "执行 .NET Core 生成命令", + "msbuild": "执行生成目标", + "externalCommand": "运行任意外部命令的示例", + "Maven": "执行常见的 maven 命令" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..a9438f8c4c --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "其他命令选项", + "JsonSchema.options.cwd": "已执行程序或脚本的当前工作目录。如果省略,则使用代码的当前工作区根。", + "JsonSchema.options.env": "已执行程序或 shell 的环境。如果省略,则使用父进程的环境。", + "JsonSchema.shellConfiguration": "配置使用的 shell。", + "JsonSchema.shell.executable": "待使用的 shell。", + "JsonSchema.shell.args": "shell 参数。", + "JsonSchema.command": "要执行的命令。可以是外部程序或 shell 命令。", + "JsonSchema.tasks.args": "在调用此任务时传递给命令的参数。", + "JsonSchema.tasks.taskName": "任务名称", + "JsonSchema.tasks.windows": "Windows 特定的命令配置", + "JsonSchema.tasks.mac": "Mac 特定的命令配置", + "JsonSchema.tasks.linux": "Linux 特定的命令配置", + "JsonSchema.tasks.suppressTaskName": "控制是否将任务名作为参数添加到命令。如果省略,则使用全局定义的值。", + "JsonSchema.tasks.showOutput": "控制是否显示正在运行的任务的输出。如果省略,则使用全局定义的值。", + "JsonSchema.echoCommand": "控制是否将已执行的命令回显到输出。默认值为 false。", + "JsonSchema.tasks.watching.deprecation": "已弃用。改用 isBackground。", + "JsonSchema.tasks.watching": "已执行的任务是否保持活动状态,并且是否在监视文件系统。", + "JsonSchema.tasks.background": "已执行的任务是否保持活动状态并在后台运行。", + "JsonSchema.tasks.promptOnClose": "当 VS 代码与运行的任务一起关闭时是否提示用户。", + "JsonSchema.tasks.build": "将此任务映射到代码的默认生成命令。", + "JsonSchema.tasks.test": "将此任务映射到代码的默认测试命令。", + "JsonSchema.tasks.matchers": "要使用的问题匹配程序。可以是字符串或问题匹配程序定义,或字符串和问题匹配程序数组。", + "JsonSchema.args": "传递到命令的其他参数。", + "JsonSchema.showOutput": "控制是否显示运行任务的输出。如果省略,则使用“始终”。", + "JsonSchema.watching.deprecation": "已弃用。改用 isBackground。", + "JsonSchema.watching": "已执行的任务是否保持活动状态,并且是否在监视文件系统。", + "JsonSchema.background": "已执行的任务是否保持活动状态并在后台运行。", + "JsonSchema.promptOnClose": "在具有正在运行的后台任务的情况下关闭 VS 代码时是否提示用户。", + "JsonSchema.suppressTaskName": "控制是否将任务名作为参数添加到命令。默认值是 false。", + "JsonSchema.taskSelector": "指示参数是任务的前缀。", + "JsonSchema.matchers": "要使用的问题匹配程序。可以是字符串或问题匹配程序定义,或字符串和问题匹配程序数组。", + "JsonSchema.tasks": "任务配置。通常是外部任务运行程序中已定义任务的扩充。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..123e4f0496 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "配置的版本号", + "JsonSchema._runner": "此 runner 已完成使命。请使用官方 runner 属性", + "JsonSchema.runner": "定义任务是否作为进程执行,输出显示在输出窗口还是在终端内。", + "JsonSchema.windows": "Windows 特定的命令配置", + "JsonSchema.mac": "Mac 特定的命令配置", + "JsonSchema.linux": "Linux 特定的命令配置", + "JsonSchema.shell": "指定命令是 shell 命令还是外部程序。如果省略,默认值是 false。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..a127e4b40e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "指定命令是 shell 命令还是外部程序。如果省略,默认值是 false。", + "JsonSchema.tasks.isShellCommand.deprecated": "isShellCommand 属性已被弃用。请改为使用任务的 type 属性和选项中的 shell 属性。另请参阅 1.14 发行说明。", + "JsonSchema.tasks.dependsOn.string": "此任务依赖的另一任务。", + "JsonSchema.tasks.dependsOn.array": "此任务依赖的其他任务。", + "JsonSchema.tasks.presentation": "配置用于显示任务输出和读取输入的面板。", + "JsonSchema.tasks.presentation.echo": "控制是否将执行的命令显示到面板中。默认值为“true”。", + "JsonSchema.tasks.presentation.focus": "控制面板是否获取焦点。默认值为“false”。如果设置为“true”,面板也会显示。", + "JsonSchema.tasks.presentation.reveal.always": "总是在此任务执行时显示终端。", + "JsonSchema.tasks.presentation.reveal.silent": "仅在任务没有关联问题匹配程序且在执行时发生错误时显示终端。", + "JsonSchema.tasks.presentation.reveal.never": "不要在此任务执行时显示终端。", + "JsonSchema.tasks.presentation.reveals": "控制是否显示运行此任务的面板。默认值为“always”。", + "JsonSchema.tasks.presentation.instance": "控制是否在任务间共享面板。同一个任务使用相同面板还是每次运行时新创建一个面板。", + "JsonSchema.tasks.terminal": "terminal 属性已被弃用。请改为使用 presentation", + "JsonSchema.tasks.group.kind": "任务的执行组。", + "JsonSchema.tasks.group.isDefault": "定义此任务是否为组中的默认任务。", + "JsonSchema.tasks.group.defaultBuild": "将任务标记为默认生成任务。", + "JsonSchema.tasks.group.defaultTest": "将任务标记为默认测试任务。", + "JsonSchema.tasks.group.build": "将任务标记为可通过“运行生成任务”命令访问的生成任务。", + "JsonSchema.tasks.group.test": "将任务标记为可通过“运行测试任务”命令访问的测试任务。", + "JsonSchema.tasks.group.none": "将任务分配为没有组", + "JsonSchema.tasks.group": "定义此任务属于的执行组。它支持 \"build\" 以将其添加到生成组,也支持 \"test\" 以将其添加到测试组。", + "JsonSchema.tasks.type": "定义任务是被作为进程运行还是在 shell 中作为命令运行。默认作为进程运行。", + "JsonSchema.version": "配置的版本号。", + "JsonSchema.tasks.identifier": "用于在 launch.json 或 dependsOn 子句中引用任务的用户定义标识符。", + "JsonSchema.tasks.taskLabel": "任务标签", + "JsonSchema.tasks.taskName": "任务名称", + "JsonSchema.tasks.taskName.deprecated": "任务的 name 属性已被弃用。请改为使用 label 属性。", + "JsonSchema.tasks.background": "已执行的任务是否保持活动状态并在后台运行。", + "JsonSchema.tasks.promptOnClose": "当 VS 代码与运行的任务一起关闭时是否提示用户。", + "JsonSchema.tasks.matchers": "要使用的问题匹配程序。可以是字符串或问题匹配程序定义,或字符串和问题匹配程序数组。", + "JsonSchema.customizations.customizes.type": "要自定义的任务类型", + "JsonSchema.tasks.customize.deprecated": "customize 属性已被弃用。请参阅 1.14 发行说明了解如何迁移到新的任务自定义方法", + "JsonSchema.tasks.showOputput.deprecated": "showOutput 属性已被弃用。请改为使用 presentation 属性内的 reveal 属性。另请参阅 1.14 发行说明。", + "JsonSchema.tasks.echoCommand.deprecated": "isBuildCommand 属性已被弃用。请改为使用 presentation 属性内的 echo 属性。另请参阅 1.14 发行说明。", + "JsonSchema.tasks.suppressTaskName.deprecated": "suppressTaskName 属性已被弃用。请改为在任务中内嵌命令及其参数。另请参阅 1.14 发行说明。", + "JsonSchema.tasks.isBuildCommand.deprecated": "isBuildCommand 属性已被弃用。请改为使用 group 属性。另请参阅 1.14 发行说明。", + "JsonSchema.tasks.isTestCommand.deprecated": "isTestCommand 属性已被弃用。请改为使用 group 属性。另请参阅 1.14 发行说明。", + "JsonSchema.tasks.taskSelector.deprecated": "taskSelector 属性已被弃用。请改为在任务中内嵌命令及其参数。另请参阅 1.14 发行说明。", + "JsonSchema.windows": "Windows 特定的命令配置", + "JsonSchema.mac": "Mac 特定的命令配置", + "JsonSchema.linux": "Linux 特定的命令配置" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..43181faf4b --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "任务", + "ConfigureTaskRunnerAction.noWorkspace": "任务仅在工作区文件夹上可用。", + "ConfigureTaskRunnerAction.quickPick.template": "选择任务运行程序", + "ConfigureTaskRunnerAction.autoDetecting": "适用于 {0} 的自动检测任务", + "ConfigureTaskRunnerAction.autoDetect": "自动检测系统任务失败。请使用默认模板。有关详细信息,请参阅任务输出。", + "ConfigureTaskRunnerAction.autoDetectError": "自动检测任务系统时出现错误。有关详细信息,请参阅任务输出。", + "ConfigureTaskRunnerAction.failed": "无法在 \".vscode\" 文件夹中创建 \"tasks.json\" 文件。查看任务输出了解详细信息。", + "ConfigureTaskRunnerAction.label": "配置任务运行程序", + "ConfigureBuildTaskAction.label": "配置生成任务", + "CloseMessageAction.label": "关闭", + "ShowTerminalAction.label": "查看终端", + "problems": "问题", + "manyMarkers": "99+", + "runningTasks": "显示运行中的任务", + "tasks": "任务", + "TaskSystem.noHotSwap": "更改需要重新加载窗口的任务执行引擎。", + "TaskService.noBuildTask1": "未定义任何生成任务。使用 \"isBuildCommand\" 在 tasks.json 文件中标记任务。", + "TaskService.noBuildTask2": "未定义任何生成任务。在 tasks.json 文件中将任务标记为 \"build\" 组。", + "TaskService.noTestTask1": "未定义任何测试任务。使用 \"isTestCommand\" 在 tasks.json 文件中标记任务。", + "TaskService.noTestTask2": "未定义任何测试任务。在 tasks.json 文件中将任务标记为 \"test\" 组。", + "TaskServer.noTask": "未找到要执行的请求任务 {0}。", + "TaskService.attachProblemMatcher.continueWithout": "继续而不扫描任务输出", + "TaskService.attachProblemMatcher.never": "永不扫描任务输出", + "TaskService.attachProblemMatcher.learnMoreAbout": "了解有关扫描任务输出的详细信息", + "selectProblemMatcher": "选择针对何种错误和警告扫描任务输出", + "customizeParseErrors": "当前任务配置存在错误。请先更正错误,再自定义任务。", + "moreThanOneBuildTask": "当前 tasks.json 中定义了多个生成任务。正在执行第一个。\n", + "TaskSystem.activeSame.background": "任务 \"{0}\" 已激活并处于后台模式。若要终止任务,请选择“任务”菜单中的“终止任务...”。", + "TaskSystem.activeSame.noBackground": "任务 \"{0}\" 已处于活动状态。若要终止任务,请选择“任务”菜单中的“终止任务...”。", + "TaskSystem.active": "当前已有任务正在运行。请先终止它,然后再执行另一项任务。", + "TaskSystem.restartFailed": "未能终止并重启任务 {0}", + "TaskSystem.configurationErrors": "错误: 提供的任务配置具有验证错误,无法使用。请首先改正错误。", + "TaskSystem.invalidTaskJson": "错误: tasks.json 文件的内容具有语法错误。请先更正错误然后再执行任务。\n", + "TaskSystem.runningTask": "存在运行中的任务。要终止它吗?", + "TaskSystem.terminateTask": "终止任务(&&T)", + "TaskSystem.noProcess": "启动的任务不再存在。如果任务已衍生出后台进程,则退出 VS Code 可能会导致出现孤立的进程。若要避免此情况,请使用等待标记启动最后一个后台进程。", + "TaskSystem.exitAnyways": "仍然退出(&&E)", + "TerminateAction.label": "终止任务", + "TaskSystem.unknownError": "运行任务时发生了错误。请参见任务日志了解详细信息。", + "TaskService.noWorkspace": "任务仅在工作区文件夹上可用。", + "recentlyUsed": "最近使用的任务", + "configured": "已配置的任务", + "detected": "检测到的任务", + "TaskService.fetchingBuildTasks": "正在获取生成任务...", + "TaskService.noBuildTaskTerminal": "未能找到生成任务。按“配置生成任务”来定义一个。", + "TaskService.pickBuildTask": "选择要运行的生成任务", + "TaskService.fetchingTestTasks": "正在获取测试任务...", + "TaskService.noTestTaskTerminal": "未能找到测试任务。按“配置任务运行程序”来定义一个。", + "TaskService.pickTestTask": "选择要运行的测试任务", + "TaskService.noTaskRunning": "当前没有任务在运行。", + "TaskService.tastToTerminate": "选择要终止的任务", + "TerminateAction.noProcess": "启动的进程不再存在。如果任务衍生的后台任务退出 VS Code,则可能会导致出现孤立的进程。", + "TerminateAction.failed": "未能终止运行中的任务", + "TaskService.noTaskToRestart": "没有要重启的任务。", + "TaskService.tastToRestart": "选择要重启的任务", + "TaskService.defaultBuildTaskExists": "{0} 已被标记为默认生成任务。", + "TaskService.pickDefaultBuildTask": "选择要用作默认生成任务的任务", + "TaskService.defaultTestTaskExists": "{0} 已被标记为默认测试任务。", + "TaskService.pickDefaultTestTask": "选择要用作默认测试任务的任务", + "TaskService.noTaskIsRunning": "没有任务在运行。", + "TaskService.pickShowTask": "选择要显示输出的任务", + "ShowLogAction.label": "显示任务日志", + "RunTaskAction.label": "运行任务", + "RestartTaskAction.label": "重启正在运行的任务", + "ShowTasksAction.label": "显示运行中的任务", + "BuildAction.label": "运行生成任务", + "TestAction.label": "运行测试任务", + "ConfigureDefaultBuildTask.label": "配置默认生成任务", + "ConfigureDefaultTestTask.label": "配置默认测试任务", + "quickOpen.task": "运行任务" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..bb08a20b43 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "任务" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..7839f281d2 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "在执行任务时发生未知错误。请参见任务输出日志了解详细信息。", + "TerminalTaskSystem.terminalName": "任务 - {0}", + "reuseTerminal": "终端将被任务重用,按任意键关闭。", + "TerminalTaskSystem": "无法对 UNC 驱动器执行 shell 命令。", + "unkownProblemMatcher": "无法解析问题匹配程序 {0}。此匹配程序将被忽略" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..3202602643 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "正在运行的 gulp --tasks-simple 没有列出任何任务。你运行 npm 安装了吗?", + "TaskSystemDetector.noJakeTasks": "正在运行的 jake --tasks 没有列出任何任务。你运行 npm 安装了吗?", + "TaskSystemDetector.noGulpProgram": "你的系统上没有安装 Gulp。运行 npm install -g gulp 以安装它。", + "TaskSystemDetector.noJakeProgram": "你的系统上没有安装 Jake。运行 npm install -g jake 以安装它。", + "TaskSystemDetector.noGruntProgram": "你的系统上没有安装 Grunt。运行 npm install -g grunt 以安装它。", + "TaskSystemDetector.noProgram": "找不到程序 {0}。消息是 {1}", + "TaskSystemDetector.buildTaskDetected": "检测到名为“{0}”的生成任务。", + "TaskSystemDetector.testTaskDetected": "测试检测到的名为“{0}”的测试任务。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..f640a6a6cb --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "在执行任务时发生未知错误。请参见任务输出日志了解详细信息。", + "TaskRunnerSystem.watchingBuildTaskFinished": "\n监视生成任务已完成", + "TaskRunnerSystem.childProcessError": "Failed to launch external program {0} {1}.", + "TaskRunnerSystem.cancelRequested": "\n已根据用户请求终止了任务'{0}' ", + "unkownProblemMatcher": "无法解析问题匹配程序 {0}。此匹配程序将被忽略" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..36e9cba306 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "警告: options.cwd 必须属于字符串类型。正在忽略值 {0}\n", + "ConfigurationParser.noargs": "错误: 命令参数必须是字符串数组。提供的值为:\n{0}", + "ConfigurationParser.noShell": "警告: 仅当在终端中执行任务时支持 shell 配置。", + "ConfigurationParser.noName": "错误: 声明范围内的问题匹配程序必须具有名称:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "警告: 已定义的问题匹配程序未知。受支持的类型为 string | ProblemMatcher | (string | ProblemMatcher)[]。\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "错误: 无效的 problemMatcher 引用: {0}\n", + "ConfigurationParser.noTaskType": "错误: 任务必须提供 type 属性。将忽略该任务。\n{0}\n", + "ConfigurationParser.noTypeDefinition": "错误: 没有注册任务类型“{0}”。你是不是忘记安装含有相应任务提供器的扩展?", + "ConfigurationParser.notCustom": "错误: 任务未声明为自定义任务。将忽略配置。\n{0}\n", + "ConfigurationParser.noTaskName": "错误: 任务必须提供 taskName 属性。将忽略该任务。\n{0}\n", + "taskConfiguration.shellArgs": "警告: 任务“{0}”是 shell 命令,该命令的名称或其中一个参数具有非转义空格。若要确保命令行引用正确,请将参数合并到该命令。", + "taskConfiguration.noCommandOrDependsOn": "错误:任务“{0}”既不指定命令,也不指定 dependsOn 属性。将忽略该任务。其定义为:\n{1}", + "taskConfiguration.noCommand": "错误: 任务“{0}”未定义命令。将忽略该任务。其定义为:\n{1}", + "TaskParse.noOsSpecificGlobalTasks": "任务版本 2.0.0 不支持全局操作系统特定任务。请将他们转换为含有操作系统特定命令的任务。受影响的任务有:\n{0}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..3c80c52b76 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0},终端选取器", + "termCreateEntryAriaLabel": "{0},新建终端", + "'workbench.action.terminal.newplus": "$(plus) 新建集成终端", + "noTerminalsMatching": "没有匹配的终端", + "noTerminalsFound": "没有打开终端" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..28c3685152 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "显示所有已打开的终端", + "terminalIntegratedConfigurationTitle": "集成终端", + "terminal.integrated.shell.linux": "终端在 Linux 上使用的 shell 的路径。", + "terminal.integrated.shellArgs.linux": "在 Linux 终端上时要使用的命令行参数。", + "terminal.integrated.shell.osx": "终端在 OS X 上使用的 shell 的路径。", + "terminal.integrated.shellArgs.osx": "在 OS X 终端上时要使用的命令行参数。", + "terminal.integrated.shell.windows": "终端在 Windows 使用的 shell 路径。使用随 Windows 一起提供的 shell (cmd、PowerShell 或 Bash on Ubuntu) 时。", + "terminal.integrated.shellArgs.windows": "在 Windows 终端上时使用的命令行参数。", + "terminal.integrated.rightClickCopyPaste": "设置后,在终端内右键单击时,这将阻止显示上下文菜单,相反,它将在有选项时进行复制,并且在没有选项时进行粘贴。", + "terminal.integrated.fontFamily": "控制终端的字体系列,这在编辑器中是默认的。fontFamily 的值。", + "terminal.integrated.fontLigatures": "控制是否在终端中启用字体连字。", + "terminal.integrated.fontSize": "控制终端的字号(以像素为单位)。", + "terminal.integrated.lineHeight": "控制终端的行高,此数字乘以终端字号得到实际行高(以像素表示)。", + "terminal.integrated.enableBold": "是否在终端内启用粗体文本,这需要终端 shell 的支持。", + "terminal.integrated.cursorBlinking": "控制终端光标是否闪烁。", + "terminal.integrated.cursorStyle": "控制终端游标的样式。", + "terminal.integrated.scrollback": "控制终端保持在缓冲区的最大行数。", + "terminal.integrated.setLocaleVariables": "控制是否在终端启动时设置区域设置变量,在 OS X 上默认设置为 true,在其他平台上为 false。", + "terminal.integrated.cwd": "将在其中启动终端的一个显式起始路径,它用作 shell 进程的当前工作目录(cwd)。当根目录为不方便的 cwd 时,此路径在工作区设置中可能十分有用。", + "terminal.integrated.confirmOnExit": "在存在活动终端会话的情况下,退出时是否要确认。", + "terminal.integrated.commandsToSkipShell": "一组命令 ID,其键绑定不发送到 shell 而始终由 Code 处理。这使得通常由 shell 使用的键绑定的使用效果与未将终端设为焦点时相同,例如按 Ctrl+P 启动 Quick Open。", + "terminal.integrated.env.osx": "要添加到 VS Code 进程中的带有环境变量的对象,其会被 OS X 终端使用。", + "terminal.integrated.env.linux": "要添加到 VS Code 进程中的带有环境变量的对象,其会被 Linux 终端使用。", + "terminal.integrated.env.windows": "要添加到 VS Code 进程中的带有环境变量的对象,其会被 Windows 终端使用。", + "terminal": "终端", + "terminalCategory": "终端", + "viewCategory": "查看" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..0f01dc8b49 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "切换集成终端", + "workbench.action.terminal.kill": "终止活动终端实例", + "workbench.action.terminal.kill.short": "终止终端", + "workbench.action.terminal.quickKill": "终止终端实例", + "workbench.action.terminal.copySelection": "复制所选内容", + "workbench.action.terminal.selectAll": "全选", + "workbench.action.terminal.deleteWordLeft": "删除左侧的字符", + "workbench.action.terminal.deleteWordRight": "删除右侧的字符", + "workbench.action.terminal.new": "新建集成终端", + "workbench.action.terminal.new.short": "新的终端", + "workbench.action.terminal.focus": "聚焦于终端", + "workbench.action.terminal.focusNext": "聚焦于下一终端", + "workbench.action.terminal.focusAtIndex": "焦点终端 {0}", + "workbench.action.terminal.focusPrevious": "聚焦于上一终端", + "workbench.action.terminal.paste": "粘贴到活动终端中", + "workbench.action.terminal.DefaultShell": "选择默认 Shell", + "workbench.action.terminal.runSelectedText": "在活动终端运行所选文本", + "workbench.action.terminal.runActiveFile": "在活动终端中运行活动文件", + "workbench.action.terminal.runActiveFile.noFile": "只有磁盘上的文件可在终端上运行", + "workbench.action.terminal.switchTerminalInstance": "切换终端实例", + "workbench.action.terminal.scrollDown": "向下滚动(行)", + "workbench.action.terminal.scrollDownPage": "向下滚动(页)", + "workbench.action.terminal.scrollToBottom": "滚动到底部", + "workbench.action.terminal.scrollUp": "向上滚动(行)", + "workbench.action.terminal.scrollUpPage": "向上滚动(页)", + "workbench.action.terminal.scrollToTop": "滚动到顶部", + "workbench.action.terminal.clear": "清除", + "workbench.action.terminal.allowWorkspaceShell": "允许配置工作区 Shell", + "workbench.action.terminal.disallowWorkspaceShell": "禁止配置工作区 Shell", + "workbench.action.terminal.rename": "重命名", + "workbench.action.terminal.rename.prompt": "输入终端名称", + "workbench.action.terminal.focusFindWidget": "聚焦于查找小组件", + "workbench.action.terminal.hideFindWidget": "隐藏查找小组件", + "nextTerminalFindTerm": "显示下一个搜索结果", + "previousTerminalFindTerm": "显示上一个搜索结果", + "quickOpenTerm": "终端: 切换活动终端" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..25ab85eb43 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "终端的背景颜色,允许终端的颜色与面板不同。", + "terminal.foreground": "终端的前景颜色。", + "terminalCursor.foreground": "终端光标的前景色。", + "terminalCursor.background": "终端光标的背景色。允许自定义被 block 光标遮住的字符的颜色。", + "terminal.ansiColor": "终端中的 ANSI 颜色“{0}”。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..c51f05ecee --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "是否允许在终端中启动 {0} (定义为工作区设置)?", + "allow": "Allow", + "disallow": "Disallow" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..5bd366b9b3 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "查找", + "placeholder.find": "查找", + "label.previousMatchButton": "上一个匹配", + "label.nextMatchButton": "下一个匹配", + "label.closeButton": "关闭" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..747cb5afb9 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "没有在终端中选择要复制的内容", + "terminal.integrated.exitedWithCode": "通过退出代码 {0} 终止的终端进程", + "terminal.integrated.waitOnExit": "按任意键以关闭终端", + "terminal.integrated.launchFailed": "终端进程命令“{0} {1}”无法启动(退出代码: {2})" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..6dc7fc70f8 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "Alt + 单击以访问链接", + "terminalLinkHandler.followLinkCmd": "Cmd + 单击以跟踪链接", + "terminalLinkHandler.followLinkCtrl": "Ctrl + 单击以跟踪链接" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..41a7bf75ad --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "复制", + "createNewTerminal": "新的终端", + "paste": "粘贴", + "selectAll": "全选", + "clear": "清除" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..393fa1fb4e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "可通过选择“自定义”按钮来更改默认的终端 shell。", + "customize": "自定义", + "cancel": "取消", + "never again": "好,永不再显示", + "terminal.integrated.chooseWindowsShell": "选择首选的终端 shell,你可稍后在设置中进行更改", + "terminalService.terminalCloseConfirmationSingular": "存在一个活动的终端会话,是否要终止此会话?", + "terminalService.terminalCloseConfirmationPlural": "存在 {0} 个活动的终端会话,是否要终止这些会话?", + "yes": "是" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..7a4782c776 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "颜色主题", + "installColorThemes": "安装其他颜色主题...", + "themes.selectTheme": "选择颜色主题(按上下箭头键预览)", + "selectIconTheme.label": "文件图标主题", + "installIconThemes": "安装其他文件图标主题...", + "noIconThemeLabel": "无", + "noIconThemeDesc": "禁用文件图标", + "problemChangingIconTheme": "设置图标主题时出现问题: {0}", + "themes.selectIconTheme": "选择文件图标主题", + "generateColorTheme.label": "使用当前设置生成颜色主题", + "preferences": "首选项", + "developer": "开发者" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..467efacbcd --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "此工作区包含仅可在“用户设置”中设置的设置。({0})", + "openWorkspaceSettings": "打开工作区设置", + "openDocumentation": "了解详细信息", + "ignore": "忽略" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/chs/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..f54121b3d7 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "发行说明: {0}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..23ad30d469 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "发行说明", + "updateConfigurationTitle": "更新", + "updateChannel": "配置是否从更新通道接收自动更新。更改后需要重启。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..46d8ca269f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "立即更新", + "later": "稍后", + "unassigned": "未分配", + "releaseNotes": "发行说明", + "showReleaseNotes": "显示发行说明", + "downloadNow": "立即下载", + "read the release notes": "欢迎使用 {0} v{1}! 是否要阅读发布说明?", + "licenseChanged": "我们的许可条款已更改,请仔细浏览。", + "license": "读取许可证", + "neveragain": "不再显示", + "64bitisavailable": "{0} 的 Windows 64 位版现已可用!", + "learn more": "了解详细信息", + "updateIsReady": "有新的 {0} 的更新可用。", + "thereIsUpdateAvailable": "存在可用更新。", + "updateAvailable": "{0} 将在重启后更新。", + "noUpdatesAvailable": "当前没有可用的更新。", + "commandPalette": "命令面板...", + "settings": "设置", + "keyboardShortcuts": "键盘快捷方式", + "selectTheme.label": "颜色主题", + "themes.selectIconTheme.label": "文件图标主题", + "not available": "更新不可用", + "checkingForUpdates": "正在检查更新...", + "DownloadUpdate": "下载可用更新", + "DownloadingUpdate": "正在下载更新...", + "InstallingUpdate": "正在安装更新...", + "restartToUpdate": "重启以更新...", + "checkForUpdates": "检查更新..." +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/chs/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..288768296f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} 操作", + "hideView": "从侧边栏中隐藏" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/chs/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..a10101a416 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "视图必须为数组", + "requirestring": "属性“{0}”是必需的,其类型必须是“string”", + "optstring": "属性“{0}”可以省略,否则其类型必须是 \"string\"", + "vscode.extension.contributes.view.id": "视图的标识符。使用标识符通过“vscode.window.registerTreeDataProviderForView” API 注册数据提供程序。同时将“onView:${id}”事件注册到“activationEvents”以激活你的扩展。", + "vscode.extension.contributes.view.name": "人类可读的视图名称。将会被显示", + "vscode.extension.contributes.view.when": "显示此视图必须为真的条件", + "vscode.extension.contributes.views": "向编辑器提供视图", + "views.explorer": "资源管理器视图", + "views.debug": "调试视图", + "locationId.invalid": "“{0}”为无效视图位置" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/chs/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..7c57fc55ce --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "显示所有命令", + "watermark.quickOpen": "转到文件", + "watermark.openFile": "打开文件", + "watermark.openFolder": "打开文件夹", + "watermark.openFileFolder": "打开文件或文件夹", + "watermark.openRecent": "打开最近的文件", + "watermark.newUntitledFile": "新的无标题文件", + "watermark.toggleTerminal": "切换终端", + "watermark.findInFiles": "在文件中查找", + "watermark.startDebugging": "开始调试", + "watermark.unboundCommand": "未绑定", + "workbenchConfigurationTitle": "工作台", + "tips.enabled": "启用后,当没有打开编辑器时将显示水印提示。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/chs/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..93dd37700d --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "文件资源管理器", + "welcomeOverlay.search": "跨文件搜索", + "welcomeOverlay.git": "源代码管理", + "welcomeOverlay.debug": "启动和调试", + "welcomeOverlay.extensions": "管理扩展", + "welcomeOverlay.problems": "查看错误和警告", + "welcomeOverlay.commandPalette": "查找并运行所有命令", + "welcomeOverlay": "用户界面概述", + "hideWelcomeOverlay": "隐藏界面概述", + "help": "帮助" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..0f522d32f8 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "编辑进化", + "welcomePage.start": "开始", + "welcomePage.newFile": "新建文件", + "welcomePage.openFolder": "打开文件夹...", + "welcomePage.cloneGitRepository": "克隆 Git 存储库...", + "welcomePage.recent": "最近", + "welcomePage.moreRecent": "更多...", + "welcomePage.noRecentFolders": "无最近使用文件夹", + "welcomePage.help": "帮助", + "welcomePage.keybindingsCheatsheet": "可打印的键盘速查表", + "welcomePage.introductoryVideos": "入门视频", + "welcomePage.tipsAndTricks": "提示与技巧", + "welcomePage.productDocumentation": "产品文档", + "welcomePage.gitHubRepository": "GitHub 存储库", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "启动时显示欢迎页", + "welcomePage.customize": "自定义", + "welcomePage.installExtensionPacks": "工具和语言", + "welcomePage.installExtensionPacksDescription": "安装对 {0} 和 {1} 的支持", + "welcomePage.moreExtensions": "更多", + "welcomePage.installKeymapDescription": "安装键盘快捷方式", + "welcomePage.installKeymapExtension": "安装 {0} 和 {1} 的键盘快捷方式", + "welcomePage.others": "其他", + "welcomePage.colorTheme": "颜色主题", + "welcomePage.colorThemeDescription": "使编辑器和代码呈现你喜欢的外观", + "welcomePage.learn": "学习", + "welcomePage.showCommands": "查找并运行所有命令", + "welcomePage.showCommandsDescription": "使用命令面板快速访问和搜索命令 ({0})", + "welcomePage.interfaceOverview": "界面概述", + "welcomePage.interfaceOverviewDescription": "查看突出显示主要 UI 组件的叠加图", + "welcomePage.deployToAzure": "将应用部署到云端", + "welcomePage.deployToAzureDescription": "了解如何将你的 Node 应用部署到 Azure 应用服务", + "welcomePage.interactivePlayground": "交互式演练场", + "welcomePage.interactivePlaygroundDescription": "在简短演练中试用编辑器的基本功能" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..eb354650ab --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "工作台", + "workbench.startupEditor.none": "启动(不带编辑器)。", + "workbench.startupEditor.welcomePage": "打开欢迎页面(默认)。", + "workbench.startupEditor.newUntitledFile": "打开新的无标题文档", + "workbench.startupEditor": "在未能恢复上一会话信息的情况下,控制启动时显示的编辑器。选择 \"none\" 表示启动时不打开编辑器,\"welcomePage\" 表示打开欢迎页面(默认),\"newUntitledFile\" 表示打开新的无标题文档(仅打开一个空工作区)。", + "help": "帮助" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..76893fcf54 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "欢迎使用", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "显示 Azure 扩展", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "已安装对 {0} 的支持。", + "welcomePage.willReloadAfterInstallingExtensionPack": "安装对 {0} 的额外支持后,将重载窗口。", + "welcomePage.installingExtensionPack": "正在安装对 {0} 的额外支持...", + "welcomePage.extensionPackNotFound": "找不到对 {0} (ID: {1}) 的支持。", + "welcomePage.keymapAlreadyInstalled": "已安装 {0} 键盘快捷方式。", + "welcomePage.willReloadAfterInstallingKeymap": "安装 {0} 键盘快捷方式后,将重载窗口。", + "welcomePage.installingKeymap": "正在安装 {0} 键盘快捷方式...", + "welcomePage.keymapNotFound": "找不到 ID 为 {1} 的 {0} 键盘快捷方式。", + "welcome.title": "欢迎使用", + "welcomePage.openFolderWithPath": "打开路径为 {1} 的文件夹 {0}", + "welcomePage.extensionListSeparator": "、", + "welcomePage.installKeymap": "安装 {0} 键映射", + "welcomePage.installExtensionPack": "安装对 {0} 的额外支持", + "welcomePage.installedKeymap": "已安装 {0} 键映射", + "welcomePage.installedExtensionPack": "已安装 {0} 支持", + "ok": "确定", + "details": "详细信息", + "cancel": "取消", + "welcomePage.buttonBackground": "欢迎页按钮的背景色。", + "welcomePage.buttonHoverBackground": "欢迎页按钮被悬停时的背景色。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..711f44e16f --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "交互式演练场", + "editorWalkThrough": "交互式演练场" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..7e3d9699d0 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "交互式演练场", + "help": "帮助", + "interactivePlayground": "交互式演练场" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..933c806664 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "向上滚动(行)", + "editorWalkThrough.arrowDown": "向下滚动(行)", + "editorWalkThrough.pageUp": "向上滚动(页)", + "editorWalkThrough.pageDown": "向下滚动(页)" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..440041b34d --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "未绑定", + "walkThrough.gitNotFound": "你的系统上似乎未安装 Git。", + "walkThrough.embeddedEditorBackground": "嵌入于交互式演练场中的编辑器的背景颜色。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/chs/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..a3a10796ac --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "用于配置字符串。", + "vscode.extension.contributes.configuration.title": "设置摘要。此标签将在设置文件中用作分隔注释。", + "vscode.extension.contributes.configuration.properties": "配置属性的描述。", + "scope.window.description": "特定于窗口的配置,可在“用户”或“工作区”设置中配置。", + "scope.resource.description": "特定于资源的配置,可在“用户”、“工作区”或“文件夹”设置中配置。", + "scope.description": "配置适用的范围。可用范围有“窗口”和“资源”。", + "invalid.type": "如果进行设置,\"configuration.type\" 必须设置为对象", + "invalid.title": "configuration.title 必须是字符串", + "vscode.extension.contributes.defaultConfiguration": "按语言提供默认编辑器配置设置。", + "invalid.properties": "configuration.properties 必须是对象", + "workspaceConfig.folders.description": "要在工作区中加载的文件夹列表。必须为文件夹完整路径。例如 \"/root/folderA\" 或 \"./folderA\"。后者表示根据工作区文件位置进行解析的相对路径。", + "workspaceConfig.folder.description": "文件路径。例如 \"/root/folderA\" 或 \"./folderA\"。后者表示根据工作区文件位置进行解析的相对路径。", + "workspaceConfig.settings.description": "工作区设置" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/chs/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..1d2ac55850 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "打开设置", + "close": "关闭", + "saveAndRetry": "保存设置并重试", + "errorUnknownKey": "没有注册配置 {1},因此无法写入 {0}。", + "errorInvalidFolderConfiguration": "{0} 不支持文件夹资源域,因此无法写入\"文件夹设置\"。", + "errorInvalidUserTarget": "{0} 不支持全局域,因此无法写入\"用户设置\"。", + "errorInvalidFolderTarget": "未提供资源,因此无法写入\"文件夹设置\"。", + "errorNoWorkspaceOpened": "没有打开任何工作区,因此无法写入 {0}。请先打开一个工作区,然后重试。", + "errorInvalidConfiguration": "无法写入设置。请打开 **用户设置** 更正文件中的错误/警告,然后重试。", + "errorInvalidConfigurationWorkspace": "无法写入设置。请打开 **工作区设置** 更正文件中的错误/警告,然后重试。", + "errorConfigurationFileDirty": "文件已变更,因此无法写入设置。请先保存 **用户设置** 文件,然后重试。", + "errorConfigurationFileDirtyWorkspace": "文件已变更,因此无法写入设置。请先保存 **工作区设置** 文件,然后重试。", + "userTarget": "用户设置", + "workspaceTarget": "工作区设置", + "folderTarget": "文件夹设置" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/chs/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..0fa7ba03a3 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "无法写入文件。请打开文件以更正错误或警告,然后重试。", + "errorFileDirty": "无法写入文件因为其已变更。请先保存此文件,然后重试。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/chs/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..d2bbf90ef6 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "遥测", + "telemetry.enableCrashReporting": "启用要发送给 Microsoft 的故障报表。\n此选项需重启才可生效。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/chs/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..9fa13e6abb --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "扩展未在 10 秒内启动,可能在第一行已停止,需要调试器才能继续。", + "extensionHostProcess.startupFail": "扩展主机未在 10 秒内启动,可能发生了一个问题。", + "extensionHostProcess.error": "扩展主机中的错误: {0}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..e6321fb69e --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "未能分析 {0}: {1}。", + "fileReadFail": "无法读取文件 {0}: {1}。", + "jsonsParseFail": "未能分析 {0} 或 {1}: {2}。", + "missingNLSKey": "无法找到键 {0} 的消息。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..7b6400aa04 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "开发人员工具", + "restart": "重启扩展宿主", + "extensionHostProcess.crash": "扩展宿主意外终止。", + "extensionHostProcess.unresponsiveCrash": "扩展宿主因没有响应而被终止。", + "overwritingExtension": "使用扩展程序 {1} 覆盖扩展程序 {0}。", + "extensionUnderDevelopment": "正在 {0} 处加载开发扩展程序" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/chs/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..ec4edd6c46 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "需要 Microsoft .NET Framework 4.5。请访问链接安装它。", + "installNet": "下载 .NET Framework 4.5", + "neverShowAgain": "不再显示", + "trashFailed": "未能将“{0}”移动到垃圾桶" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/chs/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..b8967c78c2 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "无效的文件资源({0})", + "fileIsDirectoryError": "文件是目录({0})", + "fileNotModifiedError": "自以下时间未修改的文件:", + "fileTooLargeError": "文件太大,无法打开", + "fileBinaryError": "文件似乎是二进制文件,无法作为文档打开", + "fileNotFoundError": "找不到文件({0})", + "fileMoveConflict": "无法移动/复制。文件已存在于目标位置。", + "unableToMoveCopyError": "无法移动/复制。文件将替换其所在的文件夹。", + "foldersCopyError": "无法将文件夹复制到工作区中。请选择单独的文件来进行复制。", + "fileModifiedError": "自以下时间已修改的文件:", + "fileReadOnlyError": "文件为只读文件" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/chs/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..c3b4b7ff82 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "无法写入,因为文件已更新。请保存**键绑定**文件,然后重试。", + "parseErrors": "无法写入键绑定。请打开*键绑定文件***以更正文件中的错误/警告,然后重试。", + "errorInvalidConfiguration": "无法写入键绑定。**键绑定文件**具有不是数组类型的对象。请打开文件进行清理,然后重试。", + "emptyKeybindingsHeader": "将键绑定放入此文件中以覆盖默认值" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/chs/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..b0d0135d61 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "应为非空值。", + "requirestring": "属性“{0}”是必需的,其类型必须是“字符串”", + "optstring": "属性“{0}”可以省略,或者其类型必须是“字符串”", + "vscode.extension.contributes.keybindings.command": "要在触发键绑定时运行的命令的标识符。", + "vscode.extension.contributes.keybindings.key": "按键或按键序列。用加号分隔按键,用空格分隔序列。例如,Ctrl+O 和 Ctrl+L L(连续按键)。", + "vscode.extension.contributes.keybindings.mac": "Mac 特定的键或键序列。", + "vscode.extension.contributes.keybindings.linux": "Linux 特定的键或键序列。", + "vscode.extension.contributes.keybindings.win": "Windows 特定的键或键序列。", + "vscode.extension.contributes.keybindings.when": "键处于活动状态时的条件。", + "vscode.extension.contributes.keybindings": "用于键绑定。", + "invalid.keybindings": "无效的“contributes.{0}”: {1}", + "unboundCommands": "以下是其他可用命令:", + "keybindings.json.title": "键绑定配置", + "keybindings.json.key": "键或键序列(用空格分隔)", + "keybindings.json.command": "要执行的命令的名称", + "keybindings.json.when": "键处于活动状态时的条件。", + "keybindings.json.args": "要传递给命令以执行的参数。", + "keyboardConfigurationTitle": "键盘", + "dispatch": "控制按键的调度逻辑以使用“keydown.code”(推荐) 或“keydown.keyCode”。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/chs/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..86379a6074 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "错误: {0}", + "alertWarningMessage": "警告: {0}", + "alertInfoMessage": "信息: {0}", + "error": "错误", + "warning": "警告", + "info": "信息", + "close": "关闭" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/chs/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..600844ef96 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "是(&&Y)", + "cancelButton": "取消" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/chs/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..df88340980 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "有助于语言声明。", + "vscode.extension.contributes.languages.id": "语言 ID。", + "vscode.extension.contributes.languages.aliases": "语言的别名。", + "vscode.extension.contributes.languages.extensions": "与语言关联的文件扩展名。", + "vscode.extension.contributes.languages.filenames": "与语言关联的文件名。", + "vscode.extension.contributes.languages.filenamePatterns": "与语言关联的文件名 glob 模式。", + "vscode.extension.contributes.languages.mimetypes": "与语言关联的 Mime 类型。", + "vscode.extension.contributes.languages.firstLine": "与语言文件的第一行匹配的正则表达式。", + "vscode.extension.contributes.languages.configuration": "包含语言配置选项的文件的相对路径。", + "invalid": "“contributes.{0}”无效。应为数组。", + "invalid.empty": "“contributes.{0}”的值为空", + "require.id": "属性“{0}”是必需的,其类型必须是“字符串”", + "opt.extensions": "属性“{0}”可以省略,其类型必须是 \"string[]\"", + "opt.filenames": "属性“{0}”可以省略,其类型必须是 \"string[]\"", + "opt.firstLine": "属性“{0}”可以省略,其类型必须是“字符串”", + "opt.configuration": "属性“{0}”可以省略,其类型必须是“字符串”", + "opt.aliases": "属性“{0}”可以省略,其类型必须是 \"string[]\"", + "opt.mimetypes": "属性“{0}”可以省略,其类型必须是 \"string[]\"" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/chs/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..441530a01b --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1}", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/chs/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..2086a50c94 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "贡献 textmate tokenizer。", + "vscode.extension.contributes.grammars.language": "此语法为其贡献了内容的语言标识符。", + "vscode.extension.contributes.grammars.scopeName": "tmLanguage 文件所用的 textmate 范围名称。", + "vscode.extension.contributes.grammars.path": "tmLanguage 文件的路径。该路径是相对于扩展文件夹,通常以 \"./syntaxes/\" 开头。", + "vscode.extension.contributes.grammars.embeddedLanguages": "如果此语法包含嵌入式语言,则为作用域名称到语言 ID 的映射。", + "vscode.extension.contributes.grammars.injectTo": "此语法注入到的语言范围名称列表。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/chs/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..a9c9702c26 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "“contributes.{0}.language”中存在未知的语言。提供的值: {1}", + "invalid.scopeName": "“contributes.{0}.scopeName”中应为字符串。提供的值: {1}", + "invalid.path.0": "“contributes.{0}.path”中应为字符串。提供的值: {1}", + "invalid.injectTo": "\"contributes.{0}.injectTo\" 中的值无效。必须为语言范围名称数组。提供的值: {1}", + "invalid.embeddedLanguages": "\"contributes.{0}.embeddedLanguages\" 中的值无效。必须为从作用域名称到语言的对象映射。提供的值: {1}", + "invalid.path.1": "“contributes.{0}.path”({1})应包含在扩展的文件夹({2})内。这可能会使扩展不可移植。", + "no-tm-grammar": "没有注册这种语言的 TM 语法。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/chs/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..22f60d8138 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "文件已更新。请首先保存它,然后再通过另一个编码重新打开它。", + "genericSaveError": "未能保存“{0}”: {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/chs/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..fb1bec1b2c --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "无法备份文件(错误: {0}),尝试保存文件以退出。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/chs/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..8580fd5f5d --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "是否要保存对 {0} 的更改?", + "saveChangesMessages": "是否要保存对下列 {0} 个文件的更改?", + "moreFile": "...1 个其他文件未显示", + "moreFiles": "...{0} 个其他文件未显示", + "saveAll": "全部保存(&&S)", + "save": "保存(&&S)", + "dontSave": "不保存(&&N)", + "cancel": "取消", + "saveChangesDetail": "如果不保存,更改将丢失。", + "allFiles": "所有文件", + "noExt": "无扩展" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/chs/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..c9db35a399 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "标记的颜色和样式。", + "schema.token.foreground": "标记的前景色。", + "schema.token.fontStyle": "规则字体样式:“斜体”、“粗体”和“下划线”中的一种或者组合", + "schema.fontStyle.error": "字体样式必须为 \"italic\"(斜体)、 \"bold\"(粗体)和 \"underline\"(下划线)的组合。", + "schema.properties.name": "规则的描述。", + "schema.properties.scope": "此规则适用的范围选择器。", + "schema.tokenColors.path": "tmTheme 文件路径(相对于当前文件)。", + "schema.colors": "语法突出显示颜色" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/chs/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..2e91f2b3f2 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "展开文件夹的文件夹图标。展开文件夹图标是可选的。如果未设置,将显示为文件夹定义的图标。", + "schema.folder": "折叠文件夹的文件夹图标,如果未设置 folderExpanded,也指展开文件夹的文件夹图标。", + "schema.file": "默认文件图标,针对不与任何扩展名、文件名或语言 ID 匹配的所有文件显示。", + "schema.folderNames": "将文件夹名关联到图标。对象键是文件夹名,但不包括任何路径段。不允许任何模式或通配符。文件夹名匹配不区分大小写。", + "schema.folderName": "关联的图标定义的 ID。", + "schema.folderNamesExpanded": "将文件夹名关联到展开文件夹的图标。对象键是文件夹名,但不包括任何路径段。不允许任何模式或通配符。文件夹名匹配不区分大小写。", + "schema.folderNameExpanded": "关联的图标定义的 ID。", + "schema.fileExtensions": "将文件扩展名关联到图标。对象键是文件扩展名。扩展名是文件名的最后一个部分,位于最后一个点之后(不包括该点)。比较扩展名时不区分大小写。", + "schema.fileExtension": "关联的图标定义的 ID。", + "schema.fileNames": "将文件名关联到图标。对象键是完整的文件名,但不包括任何路径段。文件名可以包括句点和可能的文件扩展名。不允许任何模式或通配符。文件名匹配不区分大小写。", + "schema.fileName": "关联的图标定义的 ID。", + "schema.languageIds": "将语言与图标相关联。对象键是语言贡献点中定义的语言 ID。", + "schema.languageId": "关联的图标定义的 ID。", + "schema.fonts": "图标定义中使用的字体。", + "schema.id": "字体的 ID。", + "schema.src": "字体的位置。", + "schema.font-path": "相对于当前图标主题文件的字体路径。", + "schema.font-format": "字体的格式。", + "schema.font-weight": "字体的粗细。", + "schema.font-sstyle": "字体的样式。", + "schema.font-size": "字体的默认大小。", + "schema.iconDefinitions": "将文件关联到图标时可以使用的所有图标的说明。", + "schema.iconDefinition": "图标定义。对象键是定义的 ID。", + "schema.iconPath": "使用 SVG 或 PNG 时: 到图像的路径。该路径相对于图标设置文件。", + "schema.fontCharacter": "使用字形字体时: 要使用的字体中的字符。", + "schema.fontColor": "使用字形字体时: 要使用的颜色。", + "schema.fontSize": "使用某种字体时: 文本字体的字体大小(以百分比表示)。如果未设置,则默认为字体定义中的大小。", + "schema.fontId": "使用某种字体时: 字体的 ID。如果未设置,则默认为第一个字体定义。", + "schema.light": "浅色主题中文件图标的可选关联。", + "schema.highContrast": "高对比度颜色主题中文件图标的可选关联。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/chs/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..0e379dcd24 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "分析 JSON 主题文件 {0} 时出现问题", + "error.invalidformat.colors": "分析颜色主题文件时出现问题:{0}。属性“colors”不是“object”类型。", + "error.invalidformat.tokenColors": "分析颜色主题文件时出现问题:{0}。属性 \"tokenColors\" 应为指定颜色的数组或是指向 TextMate 主题文件的路径", + "error.plist.invalidformat": "分析 tmTheme 文件时出现问题:{0}。“settings”不是数组。", + "error.cannotparse": "分析 tmTheme 文件时出现问题:{0}", + "error.cannotload": "分析 tmTheme 文件 {0} 时出现问题:{1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/chs/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..ed4b9f1f18 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "用户设置中使用的图标主题的 ID。", + "vscode.extension.contributes.themes.label": "显示在 UI 中的颜色主题标签。", + "vscode.extension.contributes.themes.uiTheme": "用于定义编辑器周围颜色的基本主题: \"vs\" 是浅色主题,\"vs-dark\" 是深色主题。\"hc-black\" 是深色高对比度主题。", + "vscode.extension.contributes.themes.path": "tmTheme 文件的路径。该路径相对于扩展文件夹,通常为 \"./themes/themeFile.tmTheme\"。", + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "用户设置中使用的图标主题的 ID。", + "vscode.extension.contributes.iconThemes.label": "UI 中显示的图标主题的标签。", + "vscode.extension.contributes.iconThemes.path": "图标主题定义文件的路径。该路径相对于扩展文件夹,通常是 \"./icons/awesome-icon-theme.json\"。", + "migration.completed": "已向用户设置添加了新的主题设置。{0} 中可备份。", + "error.cannotloadtheme": "无法加载 {0}: {1}", + "reqarray": "扩展点“{0}”必须是一个数组。", + "reqpath": "“contributes.{0}.path”中应为字符串。提供的值: {1}", + "invalid.path.1": "“contributes.{0}.path”({1})应包含在扩展的文件夹({2})内。这可能会使扩展不可移植。", + "reqid": "“contributes.{0}.id”中应为字符串。提供的值: {1}", + "error.cannotloadicontheme": "Unable to load {0}", + "error.cannotparseicontheme": "Problems parsing file icons file: {0}", + "colorTheme": "指定工作台中使用的颜色主题。", + "colorThemeError": "主题未知或未安装。", + "iconTheme": "指定在工作台中使用的图标主题,或指定 \"null\" 以不显示任何文件图标。", + "noIconThemeDesc": "No file icons", + "iconThemeError": "文件图标主题未知或未安装。", + "workbenchColors": "覆盖当前所选颜色主题的颜色。", + "workbenchColors.deprecated": "该设置不再是实验性设置,并已重命名为“workbench.colorCustomizations”", + "workbenchColors.deprecatedDescription": "改用“workbench.colorCustomizations”", + "editorColors": "覆盖当前所选颜色主题中的编辑器颜色和字体样式。", + "editorColors.comments": "设置注释的颜色和样式", + "editorColors.strings": "设置字符串文本的颜色和样式", + "editorColors.keywords": "设置关键字的颜色和样式。", + "editorColors.numbers": "设置数字的颜色和样式。", + "editorColors.types": "设置类型定义与引用的颜色和样式。", + "editorColors.functions": "设置函数定义与引用的颜色和样式。", + "editorColors.variables": "设置变量定义和引用的颜色和样式。", + "editorColors.textMateRules": "使用 TextMate 主题规则设置颜色和样式(高级)。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/configuration-editing/out/extension.i18n.json b/i18n/cht/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..c5007111c2 --- /dev/null +++ b/i18n/cht/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "範例" +} \ No newline at end of file diff --git a/i18n/cht/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/cht/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..d5e4432954 --- /dev/null +++ b/i18n/cht/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "例如 myFile.txt", + "activeEditorMedium": "例如 myFolder/myFile.txt", + "activeEditorLong": "例如 /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "例如 myFolder1, , myFolder2, myFolder3", + "rootPath": "例如 /Users/Development/myProject", + "folderName": "例如 myFolder", + "folderPath": "例如 /Users/Development/myFolder", + "appName": "例如 VS Code", + "dirty": "若使用中的編輯器已變更,即為已變更的指示區", + "separator": "條件式分隔符號 (' - '),只會在前後為具有值的變數時顯示", + "assocLabelFile": "檔案副檔名", + "assocDescriptionFile": "將檔案名稱符合 Glob 模式的所有檔案,對應到具有指定識別碼的語言。", + "assocLabelPath": "檔案路徑", + "assocDescriptionPath": "將檔案路徑符合絕對路徑 Glob 模式的所有檔案,對應到具有指定識別碼的語言。", + "fileLabel": "依副檔名排列的檔案", + "fileDescription": "比對所有具特定副檔名的檔案。", + "filesLabel": "具多個副檔名的檔案", + "filesDescription": "比對所有具任何副檔名的檔案。", + "derivedLabel": "依名稱排列且同層級的檔案", + "derivedDescription": "比對名稱相同但副檔名不同的同層級檔案。", + "topFolderLabel": "依名稱排列的資料夾 (最上層)", + "topFolderDescription": "比對具特定名稱的最上層資料夾。", + "topFoldersLabel": "具多個名稱的資料夾 (最上層)", + "topFoldersDescription": "比對多個最上層資料夾。", + "folderLabel": "依名稱排列的資料夾 (任何位置)", + "folderDescription": "在所有位置比對具特定名稱的資料夾。", + "falseDescription": "停用模式。", + "trueDescription": "啟用模式。", + "siblingsDescription": "比對名稱相同但副檔名不同的同層級檔案。", + "languageSpecificEditorSettings": "語言專用編輯器設定", + "languageSpecificEditorSettingsDescription": "針對語言覆寫編輯器設定" +} \ No newline at end of file diff --git a/i18n/cht/extensions/css/client/out/cssMain.i18n.json b/i18n/cht/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..4c07df23fa --- /dev/null +++ b/i18n/cht/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "CSS 語言伺服器" +} \ No newline at end of file diff --git a/i18n/cht/extensions/css/package.i18n.json b/i18n/cht/extensions/css/package.i18n.json new file mode 100644 index 0000000000..86b1638d3a --- /dev/null +++ b/i18n/cht/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "參數數目無效", + "css.lint.boxModel.desc": "使用填補或框線時不要使用寬度或高度。", + "css.lint.compatibleVendorPrefixes.desc": "在使用廠商專屬的前置詞時,請確定也包括其他所有的廠商特定屬性。", + "css.lint.duplicateProperties.desc": "請勿使用重複的樣式定義", + "css.lint.emptyRules.desc": "請勿使用空白規則集", + "css.lint.float.desc": "避免使用 'float'。浮動會使 CSS 脆弱,在版面配置的任一層面改變時容易中斷。", + "css.lint.fontFaceProperties.desc": "@font-face 規則必須定義 'src' 和 'font-family' 屬性", + "css.lint.hexColorLength.desc": "十六進位色彩必須由三個或六個十六進位數字組成", + "css.lint.idSelector.desc": "選取器不應包含 ID,因為這些規則與 HTML 結合過於緊密。", + "css.lint.ieHack.desc": "只有在支援 IE7 及更舊的版本時才需要 IE Hack", + "css.lint.important.desc": "避免使用 !important。這表示整個 CSS 的明確性皆失控,需要重構。", + "css.lint.importStatement.desc": "匯入陳述式不會平行載入", + "css.lint.propertyIgnoredDueToDisplay.desc": "屬性因顯示而忽略。例如,若為 'display: inline',則 width、height、margin-top、margin-bottom 以及 float 屬性就不會有任何作用。", + "css.lint.universalSelector.desc": "已知通用選取器 (*) 速度緩慢", + "css.lint.unknownProperties.desc": "未知的屬性。", + "css.lint.unknownVendorSpecificProperties.desc": "未知的廠商特定屬性。", + "css.lint.vendorPrefix.desc": "在使用廠商專屬的前置詞時,也包括標準屬性。", + "css.lint.zeroUnits.desc": "零不需要任何單位", + "css.trace.server.desc": "追蹤 VS Code 與 CSS 語言伺服器之間的通訊。", + "css.validate.title": "控制 CSS 驗證與問題嚴重性。", + "css.validate.desc": "啟用或停用所有驗證", + "less.lint.argumentsInColorFunction.desc": "參數數目無效", + "less.lint.boxModel.desc": "使用填補或框線時不要使用寬度或高度。", + "less.lint.compatibleVendorPrefixes.desc": "在使用廠商專屬的前置詞時,請確定也包括其他所有的廠商特定屬性。", + "less.lint.duplicateProperties.desc": "請勿使用重複的樣式定義", + "less.lint.emptyRules.desc": "請勿使用空白規則集", + "less.lint.float.desc": "避免使用 'float'。浮動會使 CSS 脆弱,在版面配置的任一層面改變時容易中斷。", + "less.lint.fontFaceProperties.desc": "@font-face 規則必須定義 'src' 和 'font-family' 屬性", + "less.lint.hexColorLength.desc": "十六進位色彩必須由三個或六個十六進位數字組成", + "less.lint.idSelector.desc": "選取器不應包含 ID,因為這些規則與 HTML 結合過於緊密。", + "less.lint.ieHack.desc": "只有在支援 IE7 及更舊的版本時才需要 IE Hack", + "less.lint.important.desc": "避免使用 !important。這表示整個 CSS 的明確性皆失控,需要重構。", + "less.lint.importStatement.desc": "匯入陳述式不會平行載入", + "less.lint.propertyIgnoredDueToDisplay.desc": "屬性因顯示而忽略。例如,若為 'display: inline',則 width、height、margin-top、margin-bottom 以及 float 屬性就不會有任何作用。", + "less.lint.universalSelector.desc": "已知通用選取器 (*) 速度緩慢", + "less.lint.unknownProperties.desc": "未知的屬性。", + "less.lint.unknownVendorSpecificProperties.desc": "未知的廠商特定屬性。", + "less.lint.vendorPrefix.desc": "在使用廠商專屬的前置詞時,也包括標準屬性。", + "less.lint.zeroUnits.desc": "零不需要任何單位", + "less.validate.title": "控制 LESS 驗證與問題嚴重性。", + "less.validate.desc": "啟用或停用所有驗證", + "scss.lint.argumentsInColorFunction.desc": "參數數目無效", + "scss.lint.boxModel.desc": "使用填補或框線時不要使用寬度或高度。", + "scss.lint.compatibleVendorPrefixes.desc": "在使用廠商專屬的前置詞時,請確定也包括其他所有的廠商特定屬性。", + "scss.lint.duplicateProperties.desc": "請勿使用重複的樣式定義", + "scss.lint.emptyRules.desc": "請勿使用空白規則集", + "scss.lint.float.desc": "避免使用 'float'。浮動會使 CSS 脆弱,在版面配置的任一層面改變時容易中斷。", + "scss.lint.fontFaceProperties.desc": "@font-face 規則必須定義 'src' 和 'font-family' 屬性", + "scss.lint.hexColorLength.desc": "十六進位色彩必須由三個或六個十六進位數字組成", + "scss.lint.idSelector.desc": "選取器不應包含 ID,因為這些規則與 HTML 結合過於緊密。", + "scss.lint.ieHack.desc": "只有在支援 IE7 及更舊的版本時才需要 IE Hack", + "scss.lint.important.desc": "避免使用 !important。這表示整個 CSS 的明確性皆失控,需要重構。", + "scss.lint.importStatement.desc": "匯入陳述式不會平行載入", + "scss.lint.propertyIgnoredDueToDisplay.desc": "屬性因顯示而忽略。例如,若為 'display: inline',則 width、height、margin-top、margin-bottom 以及 float 屬性就不會有任何作用。", + "scss.lint.universalSelector.desc": "已知通用選取器 (*) 速度緩慢", + "scss.lint.unknownProperties.desc": "未知的屬性。", + "scss.lint.unknownVendorSpecificProperties.desc": "未知的廠商特定屬性。", + "scss.lint.vendorPrefix.desc": "在使用廠商專屬的前置詞時,也包括標準屬性。", + "scss.lint.zeroUnits.desc": "零不需要任何單位", + "scss.validate.title": "控制 SCSS 驗證與問題嚴重性。", + "scss.validate.desc": "啟用或停用所有驗證", + "less.colorDecorators.enable.desc": "啟用或停用彩色裝飾項目", + "scss.colorDecorators.enable.desc": "啟用或停用彩色裝飾項目", + "css.colorDecorators.enable.desc": "啟用或停用彩色裝飾項目", + "css.colorDecorators.enable.deprecationMessage": "設定 `css.colorDecorators.enable` 已淘汰,改為 `editor.colorDecorators`。", + "scss.colorDecorators.enable.deprecationMessage": "設定 `scss.colorDecorators.enable` 已淘汰,改為 `editor.colorDecorators`。", + "less.colorDecorators.enable.deprecationMessage": "設定 `less.colorDecorators.enable` 已淘汰,改為 `editor.colorDecorators`。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/emmet/package.i18n.json b/i18n/cht/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..33409799e3 --- /dev/null +++ b/i18n/cht/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "使用縮寫換行", + "command.wrapIndividualLinesWithAbbreviation": "使用縮寫換每一行", + "command.removeTag": "移除標籤", + "command.updateTag": "更新標記", + "command.matchTag": "前往相符的配對", + "command.balanceIn": "平衡(向內)", + "command.balanceOut": "平衡(向外)", + "command.prevEditPoint": "移至上一個編輯端點", + "command.nextEditPoint": "移至下一個編輯端點", + "command.mergeLines": "合併行", + "command.selectPrevItem": "選取上一個項目", + "command.selectNextItem": "選取下一個項目", + "command.splitJoinTag": "分割/聯結標記", + "command.toggleComment": "切換註解", + "command.evaluateMathExpression": "評估數學運算式", + "command.updateImageSize": "更新影像大小", + "command.reflectCSSValue": "反射 CSS 值", + "command.incrementNumberByOne": "依 1 遞增", + "command.decrementNumberByOne": "依 1 遞減", + "command.incrementNumberByOneTenth": "依 0.1 遞增", + "command.decrementNumberByOneTenth": "依 0.1 遞減", + "command.incrementNumberByTen": "依 10 遞增", + "command.decrementNumberByTen": "依 10 遞減", + "emmetSyntaxProfiles": "為指定的語法定義設定檔,或透過特定規則使用自己的設定檔。", + "emmetExclude": "不應展開 Emmet 縮寫的語言陣列。", + "emmetExtensionsPath": "包含 Emmet 設定檔與程式碼片段的資料夾路徑。」", + "emmetShowExpandedAbbreviation": "顯示建議之展開的 Emmet 縮寫\n\"inMarkupAndStylesheetFilesOnly\" 選項適用於 html、haml、jade、slim、xml、xsl、css、scss、sass、less 和 stylus。\n不論標記/css 為何,該選項「一律」適用於檔案的所有部分。", + "emmetShowAbbreviationSuggestions": "顯示建議之可能的 Emmet 縮寫。但在樣式表內,或若 emmet.showExpandedAbbreviation 設為「永不」,則不適用。", + "emmetIncludeLanguages": "以預設未支援的語言啟用 Emmet 縮寫。在此新增該語言和 Emmet 支援的語言間之對應。例如: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"} ", + "emmetVariables": "要在 Emmet 程式碼片段中使用的變數", + "emmetTriggerExpansionOnTab": "如有啟用,只要按 Tab 鍵就能展開 Emmet 縮寫。", + "emmetPreferences": "喜好設定,用以修改某些動作的行為及 Emmet 的解析程式。", + "emmetPreferencesIntUnit": "整數值的預設單位", + "emmetPreferencesFloatUnit": "浮點值的預設單位", + "emmetPreferencesCssAfter": "展開 CSS 縮寫時,要放在 CSS 屬性結尾的符號 ", + "emmetPreferencesSassAfter": "在 SASS 檔案中展開 CSS 縮寫時,要放在 CSS 屬性結尾的符號", + "emmetPreferencesStylusAfter": "在手寫筆檔案中展開 CSS 縮寫時,要放在 CSS 屬性結尾的符號", + "emmetPreferencesCssBetween": "展開 CSS 縮寫時,要放在 CSS 屬性與值之間的符號", + "emmetPreferencesSassBetween": "在 SASS 檔案中展開 CSS 縮寫時,要放在 CSS 屬性與值之間的符號", + "emmetPreferencesStylusBetween": "在手寫筆檔案中展開 CSS 縮寫時,要放在 CSS 屬性與值之間的符號", + "emmetShowSuggestionsAsSnippets": "若為 true,則 Emmet 建議會顯示為程式碼片段,可讓您在每個 editor.snippetSuggestions 設定為其排序。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/cht/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..41cccf5060 --- /dev/null +++ b/i18n/cht/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "影像必須使用 HTTPS 通訊協定。", + "svgsNotValid": "SVGs 不是有效的影像來源。", + "embeddedSvgsNotValid": "內嵌 SVGs 不是有效的影像來源。", + "dataUrlsNotValid": "資料 URL 不是有效的影像來源。", + "relativeUrlRequiresHttpsRepository": "相對影像 URL 必須在 package.json 中指定使用 HTTPS 通訊協定的存放庫。", + "relativeIconUrlRequiresHttpsRepository": "圖示必須在 package.json 中指定使用 HTTPS 通訊協定的存放庫。", + "relativeBadgeUrlRequiresHttpsRepository": "相對徽章 URL 必須在 package.json 中指定使用 HTTPS 通訊協定的存放庫。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/cht/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..d963485da5 --- /dev/null +++ b/i18n/cht/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "語言專用編輯器設定", + "languageSpecificEditorSettingsDescription": "針對語言覆寫編輯器設定" +} \ No newline at end of file diff --git a/i18n/cht/extensions/git/out/askpass-main.i18n.json b/i18n/cht/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..faaa819c2f --- /dev/null +++ b/i18n/cht/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "缺少認證或其無效。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/git/out/commands.i18n.json b/i18n/cht/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..565cb78fb5 --- /dev/null +++ b/i18n/cht/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "位於 {0} 的標記", + "remote branch at": "位於 {0} 的遠端分支", + "create branch": "$(plus) 建立新的分支", + "repourl": "儲存庫 URL", + "parent": "父目錄", + "cloning": "正在複製 Git 儲存庫...", + "openrepo": "開啟儲存庫", + "proposeopen": "要開啟複製的儲存庫嗎?", + "path to init": "資料夾路徑", + "provide path": "請提供資料夾路徑以初始化 Git 存放庫", + "HEAD not available": "'{0}' 的 HEAD 版本無法使用。", + "confirm stage files with merge conflicts": "確定要暫存 {0} 個有合併衝突的檔案嗎?", + "confirm stage file with merge conflicts": "確定要暫存有合併衝突的 {0} 嗎?", + "yes": "是", + "confirm revert": "確定要還原您在 {0} 中選取的變更嗎?", + "revert": "還原變更", + "discard": "捨棄變更", + "confirm delete": "您確定要刪除'{0}'嗎?", + "delete file": "刪除檔案", + "confirm discard": "確定要捨棄 {0} 中的變更嗎?", + "confirm discard multiple": "確定要捨棄 {0} 檔案中的變更嗎?", + "warn untracked": "這會刪除 {0} 個未追蹤的檔案!", + "confirm discard all single": "確定要捨棄 {0} 中的變更嗎?", + "confirm discard all": "確定要捨棄在 {0} 個檔案中的所有變更嗎? 此動作無法復原! 您會永遠失去目前的工作集。", + "discardAll multiple": "捨棄1個檔案", + "discardAll": "捨棄所有 {0} 檔案", + "confirm delete multiple": "確定要刪除 {0} 個檔案嗎?", + "delete files": "刪除檔案", + "there are untracked files single": "下列未追蹤檔案若被捨棄,將會從磁碟中刪除: {0}。", + "there are untracked files": "有 {0} 個未追蹤檔案若被捨棄,將會從磁碟中刪除。", + "confirm discard all 2": "{0}\n\n這項動作無法復原,您會永久失去目前的工作集。", + "yes discard tracked": "捨棄 1 個追蹤的檔案", + "yes discard tracked multiple": "捨棄 {0} 個追蹤的檔案", + "no staged changes": "沒有暫存變更進行提交\n\n您希望自動暫存您所有變更並直接提交?", + "always": "永遠", + "no changes": "沒有任何變更要認可。", + "commit message": "認可訊息", + "provide commit message": "請提供認可訊息", + "select a ref to checkout": "選擇參考進行簽出", + "branch name": "分支名稱", + "provide branch name": "請提供分支名稱", + "select branch to delete": "選擇分支進行刪除", + "confirm force delete branch": "分支 '{0}' 尚未完整合併. 確定刪除嗎?", + "delete branch": "刪除分支", + "select a branch to merge from": "選擇要合併的分支。", + "merge conflicts": "合併衝突。提交前請解決衝突。", + "tag name": "標籤名稱", + "provide tag name": "請提供標籤名稱", + "tag message": "訊息", + "provide tag message": "請提供訊息以標註標籤", + "no remotes to pull": "您的儲存庫未設定要提取的遠端來源。", + "pick remote pull repo": "挑選要將分支提取出的遠端", + "no remotes to push": "您的儲存庫未設定要推送的遠端目標。", + "push with tags success": "已成功使用標籤推送。", + "nobranch": "請簽出分支以推送到遠端。", + "pick remote": "挑選要發行分支 '{0}' 的目標遠端:", + "sync is unpredictable": "此動作會將認可發送至 '{0}' 及從中提取認可。", + "ok": "確定", + "never again": "確定不要再顯示", + "no remotes to publish": "您的儲存庫未設定要發行的遠端目標。", + "no changes stash": "沒有變更可供隱藏。", + "provide stash message": "可選擇提供隱藏訊息", + "stash message": "隱藏訊息", + "no stashes": "沒有要隱藏可供還原。", + "pick stash to pop": "請挑選要快顯的隱藏", + "clean repo": "請先清除您的本地儲存庫工作區再簽出。", + "cant push": "無法將參考推送到遠端。請先執行 [提取] 以整合您的變更。", + "git error details": "Git: {0}", + "git error": "Git 錯誤", + "open git log": "開啟 Git 記錄" +} \ No newline at end of file diff --git a/i18n/cht/extensions/git/out/main.i18n.json b/i18n/cht/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..0536255b1f --- /dev/null +++ b/i18n/cht/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "正在使用來自 {1} 的 git {0}", + "updateGit": "更新 Git", + "neverShowAgain": "不要再顯示", + "git20": "您似乎已安裝 Git {0}。Code 搭配 Git >= 2 的執行效果最佳" +} \ No newline at end of file diff --git a/i18n/cht/extensions/git/out/model.i18n.json b/i18n/cht/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..6c395152f0 --- /dev/null +++ b/i18n/cht/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "沒有存放庫可供使用", + "pick repo": "請選擇存放庫" +} \ No newline at end of file diff --git a/i18n/cht/extensions/git/out/repository.i18n.json b/i18n/cht/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..0823c34181 --- /dev/null +++ b/i18n/cht/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "開啟", + "index modified": "已修改索引", + "modified": "已修改", + "index added": "已新增索引", + "index deleted": "已刪除索引", + "deleted": "已刪除", + "index renamed": "已重新命名索引", + "index copied": "已複製索引", + "untracked": "已取消追蹤", + "ignored": "已忽略", + "both deleted": "皆已刪除", + "added by us": "已由我們新增", + "deleted by them": "已受到他們刪除", + "added by them": "已由他們新增", + "deleted by us": "已受到我們刪除", + "both added": "皆已新增", + "both modified": "皆已修改", + "commit": "認可", + "merge changes": "合併變更", + "staged changes": "已分段的變更", + "changes": "變更", + "ok": "確定", + "neveragain": "不要再顯示", + "huge": "位於 '{0}' 的 Git 儲存庫有過多使用中的變更,只有部份 Git 功能會被啟用。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/git/out/scmProvider.i18n.json b/i18n/cht/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..9ceef7aacb --- /dev/null +++ b/i18n/cht/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "認可" +} \ No newline at end of file diff --git a/i18n/cht/extensions/git/out/statusbar.i18n.json b/i18n/cht/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..bf952c86f4 --- /dev/null +++ b/i18n/cht/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "簽出...", + "sync changes": "同步處理變更", + "publish changes": "發行變更", + "syncing changes": "正在同步處理變更..." +} \ No newline at end of file diff --git a/i18n/cht/extensions/git/package.i18n.json b/i18n/cht/extensions/git/package.i18n.json new file mode 100644 index 0000000000..e143933fb1 --- /dev/null +++ b/i18n/cht/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "複製", + "command.init": "初始化儲存庫", + "command.close": "關閉存放庫", + "command.refresh": "重新整理", + "command.openChange": "開啟變更", + "command.openFile": "開啟檔案", + "command.openHEADFile": "開啟檔案 (HEAD)", + "command.stage": "暫存變更", + "command.stageAll": "暫存所有變更", + "command.stageSelectedRanges": "暫存選取的範圍", + "command.revertSelectedRanges": "還原選取的範圍", + "command.unstage": "取消暫存變更", + "command.unstageAll": "取消暫存所有變更", + "command.unstageSelectedRanges": "取消暫存選取的範圍", + "command.clean": "捨棄變更", + "command.cleanAll": "捨棄所有變更", + "command.commit": "Commit", + "command.commitStaged": "認可暫存", + "command.commitStagedSigned": "認可暫存 (已簽章)", + "command.commitStagedAmend": "認可暫存 (修改)", + "command.commitAll": "全部認可", + "command.commitAllSigned": "全部認可 (已簽章)", + "command.commitAllAmend": "全部認可(修改)", + "command.undoCommit": "復原上次認可", + "command.checkout": "簽出至...", + "command.branch": "建立分支...", + "command.deleteBranch": "刪除分支...", + "command.merge": "合併分支...", + "command.createTag": "建立標籤", + "command.pull": "提取", + "command.pullRebase": "提取 (重訂基底)", + "command.pullFrom": "從...提取", + "command.push": "推送", + "command.pushTo": "推送至...", + "command.pushWithTags": "使用標籤推送", + "command.sync": "同步處理", + "command.publish": "發行分支", + "command.showOutput": "顯示 Git 輸出", + "command.ignore": "將檔案新增到 .gitignore", + "command.stash": "隱藏", + "command.stashPop": "快顯隱藏...", + "command.stashPopLatest": "快顯上一次的隱藏", + "config.enabled": "是否啟用 GIT", + "config.path": "Git 可執行檔的路徑", + "config.autorefresh": "是否啟用自動重新整理", + "config.autofetch": "是否啟用自動擷取", + "config.enableLongCommitWarning": "是否發出長認可訊息的警告", + "config.confirmSync": "請先確認再同步處理 GIT 存放庫", + "config.countBadge": "控制 git 徽章計數器。[全部] 會計算所有變更。[已追蹤] 只會計算追蹤的變更。[關閉] 會將其關閉。", + "config.checkoutType": "控制在執行 [簽出至...] 時,會列出那些類型的分支。[全部] 會顯示所有參考,[本機] 只會顯示本機分支,[標記] 只會顯示標記,[遠端] 只會顯示遠端分支。", + "config.ignoreLegacyWarning": "略過舊的 Git 警告", + "config.ignoreLimitWarning": "當儲存庫中有過多變更時,略過警告。", + "config.defaultCloneDirectory": "複製 Git 儲存庫的預設位置", + "config.enableSmartCommit": "無暫存變更時提交所有變更。", + "config.enableCommitSigning": "啟用 GPG 認可簽署。", + "config.discardAllScope": "控制 `Discard all changes` 命令會捨棄的變更。`all` 會捨棄所有變更。`tracked` 只會捨棄追蹤的檔案。`prompt` 會在每次動作執行時顯示提示對話方塊。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/grunt/out/main.i18n.json b/i18n/cht/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..c0da69ad69 --- /dev/null +++ b/i18n/cht/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Grunt 的自動偵測失敗。錯誤: {0}" +} \ No newline at end of file diff --git a/i18n/cht/extensions/grunt/package.i18n.json b/i18n/cht/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..93fa3fe298 --- /dev/null +++ b/i18n/cht/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "控制 Grunt 工作的自動偵測為開啟或關閉。預設為開。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/gulp/out/main.i18n.json b/i18n/cht/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..1b6f88df24 --- /dev/null +++ b/i18n/cht/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Gulp 的自動偵測失敗。錯誤: {0}" +} \ No newline at end of file diff --git a/i18n/cht/extensions/gulp/package.i18n.json b/i18n/cht/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..eb16e02cd9 --- /dev/null +++ b/i18n/cht/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "控制 Gulp 工作的自動偵測為開啟或關閉。預設為開。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/html/client/out/htmlMain.i18n.json b/i18n/cht/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..14f8c4345c --- /dev/null +++ b/i18n/cht/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "HTML 語言伺服器" +} \ No newline at end of file diff --git a/i18n/cht/extensions/html/package.i18n.json b/i18n/cht/extensions/html/package.i18n.json new file mode 100644 index 0000000000..c12bb1d7f0 --- /dev/null +++ b/i18n/cht/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "啟用/停用預設 HTML 格式器", + "html.format.wrapLineLength.desc": "每行的字元數上限 (0 = 停用)。", + "html.format.unformatted.desc": "不應重新格式化的逗號分隔標記清單。'null' 預設為 https://www.w3.org/TR/html5/dom.html#phrasing-content 中列出的所有標記。", + "html.format.contentUnformatted.desc": "逗點分隔的標記清單,其中內容的格式不應重新設定。'null' 預設為 'pre' 標記。", + "html.format.indentInnerHtml.desc": "縮排 及 區段。", + "html.format.preserveNewLines.desc": "是否應保留項目前方現有的分行符號。僅適用於項目前方,而不適用於標記內或文字。", + "html.format.maxPreserveNewLines.desc": "一個區塊要保留的最大分行符號數。使用 'null' 表示無限制。", + "html.format.indentHandlebars.desc": "格式化並縮排 {{#foo}} 及 {{/foo}}。", + "html.format.endWithNewline.desc": "以新行字元結尾。", + "html.format.extraLiners.desc": "前方應有額外新行字元的標記清單,須以逗號分隔。'null' 的預設值為 \"head, body, /html\"。", + "html.format.wrapAttributes.desc": "將屬性換行。", + "html.format.wrapAttributes.auto": "只在超過行的長度時將屬性換行。", + "html.format.wrapAttributes.force": "將第一個以外的每個屬性換行。", + "html.format.wrapAttributes.forcealign": "將第一個以外的每個屬性換行,並保持對齊。", + "html.format.wrapAttributes.forcemultiline": "將每個屬性換行。", + "html.suggest.angular1.desc": "設定內建 HTML 語言支援是否建議 Angular V1 標記和屬性。", + "html.suggest.ionic.desc": "設定內建 HTML 語言支援是否建議 Ionic 標記、屬性和值。", + "html.suggest.html5.desc": "設定內建 HTML 語言支援是否建議 HTML5 標記、屬性和值。", + "html.trace.server.desc": "追蹤 VS Code 與 HTML 語言伺服器之間的通訊。", + "html.validate.scripts": "設定內建 HTML 語言支援是否會驗證內嵌指定碼。", + "html.validate.styles": "設定內建 HTML 語言支援是否會驗證內嵌樣式。", + "html.autoClosingTags": "啟用/停用 HTML 標籤的自動關閉功能。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/jake/out/main.i18n.json b/i18n/cht/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..ec9b967e40 --- /dev/null +++ b/i18n/cht/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Jake 的自動偵測失敗。錯誤: {0}" +} \ No newline at end of file diff --git a/i18n/cht/extensions/jake/package.i18n.json b/i18n/cht/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..3623cf44cd --- /dev/null +++ b/i18n/cht/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "控制 Jake 工作的自動偵測為開啟或關閉。預設為開。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/cht/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..8d83c00b1e --- /dev/null +++ b/i18n/cht/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "預設 bower.json", + "json.bower.error.repoaccess": "對 Bower 儲存機制的要求失敗: {0}", + "json.bower.latest.version": "最新" +} \ No newline at end of file diff --git a/i18n/cht/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/cht/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..71f9b080ee --- /dev/null +++ b/i18n/cht/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "預設 package.json", + "json.npm.error.repoaccess": "對 NPM 儲存機制的要求失敗: {0}", + "json.npm.latestversion": "封裝目前的最新版本", + "json.npm.majorversion": "比對最新的主要版本 (1.x.x)", + "json.npm.minorversion": "比對最新的次要版本 (1.2.x)", + "json.npm.version.hover": "最新版本: {0}" +} \ No newline at end of file diff --git a/i18n/cht/extensions/json/client/out/jsonMain.i18n.json b/i18n/cht/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..06b098d371 --- /dev/null +++ b/i18n/cht/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "JSON 語言伺服器" +} \ No newline at end of file diff --git a/i18n/cht/extensions/json/package.i18n.json b/i18n/cht/extensions/json/package.i18n.json new file mode 100644 index 0000000000..d8ff226610 --- /dev/null +++ b/i18n/cht/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "在結構描述與目前專案的 JSON 檔案之間建立關聯", + "json.schemas.url.desc": "目前目錄中的結構描述 URL 或結構描述相對路徑", + "json.schemas.fileMatch.desc": "檔案模式陣列,在將 JSON 檔案解析成結構描述時的比對對象。", + "json.schemas.fileMatch.item.desc": "可包含 '*' 的檔案模式,在將 JSON 檔案解析成結構描述時的比對對象。", + "json.schemas.schema.desc": "指定 URL 的結構描述定義。只須提供結構描述以避免存取結構描述 URL。", + "json.format.enable.desc": "啟用/停用預設 JSON 格式器 (需要重新啟動)", + "json.tracing.desc": "追蹤 VS Code 與 JSON 語言伺服器之間的通訊。", + "json.colorDecorators.enable.desc": "啟用或停用彩色裝飾項目", + "json.colorDecorators.enable.deprecationMessage": "設定 `json.colorDecorators.enable` 已淘汰,將改為 `editor.colorDecorators`。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/markdown/out/extension.i18n.json b/i18n/cht/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..ce38de874f --- /dev/null +++ b/i18n/cht/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "無法載入 ‘markdown.style' 樣式:{0}" +} \ No newline at end of file diff --git a/i18n/cht/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/cht/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..195130cf82 --- /dev/null +++ b/i18n/cht/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "此文件中的部分內容已停用", + "preview.securityMessage.title": "Markdown 預覽中已停用可能不安全或不安全的內容。請將 Markdown 預覽的安全性設定變更為允許不安全內容,或啟用指令碼", + "preview.securityMessage.label": "內容已停用安全性警告" +} \ No newline at end of file diff --git a/i18n/cht/extensions/markdown/out/security.i18n.json b/i18n/cht/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..8340c244ea --- /dev/null +++ b/i18n/cht/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "嚴謹", + "strict.description": "僅載入安全內容", + "insecureContent.title": "允許不安全的內容", + "insecureContent.description": "啟用 http 載入內容", + "disable.title": "停用", + "disable.description": "允許所有內容與指令碼執行。不建議", + "moreInfo.title": "詳細資訊", + "preview.showPreviewSecuritySelector.title": "選擇此工作區 Markdown 預覽的安全性設定" +} \ No newline at end of file diff --git a/i18n/cht/extensions/markdown/package.i18n.json b/i18n/cht/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..16769f7361 --- /dev/null +++ b/i18n/cht/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "設定在 Markdown 預覽中如何顯示分行符號。設為 'trure' 為每個新行建立
", + "markdown.preview.linkify": "啟用或停用在 Markdown 預覽中將類似 URL 的文字轉換為連結。", + "markdown.preview.doubleClickToSwitchToEditor.desc": "在 Markdown 預覽中按兩下會切換到編輯器。", + "markdown.preview.fontFamily.desc": "控制 Markdown 預覽中使用的字型家族。", + "markdown.preview.fontSize.desc": "控制 Markdown 預覽中使用的字型大小 (以像素為單位)。", + "markdown.preview.lineHeight.desc": "控制 Markdown 預覽中使用的行高。此數字相對於字型大小。", + "markdown.preview.markEditorSelection.desc": "在 Markdown 預覽中標記目前的編輯器選取範圍。", + "markdown.preview.scrollEditorWithPreview.desc": "捲動 Markdown 預覽時會更新編輯器的檢視。", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "捲動 Markdown 預覽以顯示目前從編輯器選取的行。", + "markdown.preview.title": "開啟預覽", + "markdown.previewFrontMatter.dec": "設定 YAML 前端在 Markdown 預覽中呈現的方式。[隱藏] 會移除前端。否則,前端會視為 Markdown 內容。", + "markdown.previewSide.title": "在一側開啟預覽", + "markdown.showSource.title": "顯示來源", + "markdown.styles.dec": "可從 Markdown 預覽使用之 CSS 樣式表的 URL 或本機路徑清單。相對路徑是相對於在總管中開啟的資料夾來進行解釋。若沒有開啟資料夾,路徑則是相對於 Markdown 檔案的位置來進行解釋。所有 '\\' 都必須寫成 '\\\\'。", + "markdown.showPreviewSecuritySelector.title": "變更預覽的安全性設定", + "markdown.trace.desc": "允許 Markdown 延伸模組進行偵錯記錄。", + "markdown.refreshPreview.title": "重新整理預覽" +} \ No newline at end of file diff --git a/i18n/cht/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/cht/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..854a91dd0f --- /dev/null +++ b/i18n/cht/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "接受當前變更", + "acceptIncomingChange": "接受來源變更", + "acceptBothChanges": "接受兩者變更", + "compareChanges": "比較變更" +} \ No newline at end of file diff --git a/i18n/cht/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/cht/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..ec7a245fe4 --- /dev/null +++ b/i18n/cht/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "編輯器游標不在衝突合併範圍之內", + "compareChangesTitle": "{0}: 當前變更⟷來源變更", + "cursorOnCommonAncestorsRange": "編輯器游標在共同上階區塊內,請移動至「當前項目」或「來源項目」區塊", + "cursorOnSplitterRange": "編輯器游標在衝突合併工具範圍內,請移動至\"當前項目\"或來源項目\"區塊", + "noConflicts": "檔案內找不到需要合併衝突項目", + "noOtherConflictsInThisFile": "此檔案內沒有其他的衝突合併項目" +} \ No newline at end of file diff --git a/i18n/cht/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/cht/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..25cde69863 --- /dev/null +++ b/i18n/cht/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(目前變更)", + "incomingChange": "(來源變更)" +} \ No newline at end of file diff --git a/i18n/cht/extensions/merge-conflict/package.i18n.json b/i18n/cht/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..5c6428a480 --- /dev/null +++ b/i18n/cht/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "合併衝突", + "command.accept.all-incoming": "接受所有來源", + "command.accept.all-both": "接受兩者所有項目", + "command.accept.current": "接受當前項目", + "command.accept.incoming": "接受來源項目", + "command.accept.selection": "接受選取項目", + "command.accept.both": "接受兩者", + "command.next": "下一個衝突", + "command.previous": "前一個衝突", + "command.compare": "比較目前衝突", + "config.title": "合併衝突", + "config.codeLensEnabled": "啟用/停用 編輯器CodeLens衝突合併 ", + "config.decoratorsEnabled": "啟用/停用 編輯器衝突合併色彩裝飾" +} \ No newline at end of file diff --git a/i18n/cht/extensions/npm/package.i18n.json b/i18n/cht/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..ea1bb1cb09 --- /dev/null +++ b/i18n/cht/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "控制是否自動檢測npm腳本.預設為開啟.", + "config.npm.runSilent": "以 '--silent' 選項執行 npm 命令 " +} \ No newline at end of file diff --git a/i18n/cht/extensions/php/out/features/validationProvider.i18n.json b/i18n/cht/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..8155e2d317 --- /dev/null +++ b/i18n/cht/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "要允許 {0} (定義為工作區設定) 執行,以使用 lint 標記 PHP 檔案嗎?", + "php.yes": "允許", + "php.no": "不允許", + "wrongExecutable": "因為 {0} 不是有效的 PHP 可執行檔,所以無法驗證。您可以使用設定 'php.validate.executablePath' 設定 PHP 可執行檔。", + "noExecutable": "因為未設定任何 PHP 可執行檔,所以無法驗證。您可以使用 'php.validate.executablePath' 設定可執行檔。", + "unknownReason": "無法使用路徑 {0} 執行 PHP。原因不明。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/php/package.i18n.json b/i18n/cht/extensions/php/package.i18n.json new file mode 100644 index 0000000000..8b4d884fad --- /dev/null +++ b/i18n/cht/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "設定是否啟用內建 PHP 語言建議。此支援會建議 PHP 全域和變數。", + "configuration.validate.enable": "啟用/停用內建 PHP 驗證。", + "configuration.validate.executablePath": "指向 PHP 可執行檔。", + "configuration.validate.run": "是否在儲存或輸入時執行 linter。", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "禁止 PHP 驗證可執行檔 (定義為工作區設定)" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/cht/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..78287f5afa --- /dev/null +++ b/i18n/cht/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInformation": "詳細資訊", + "doNotCheckAgain": "不要再檢查", + "close": "關閉", + "updateTscCheck": "已將使用者設定 'typescript.check.tscVersion' 更新為 false" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/cht/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..403b95c055 --- /dev/null +++ b/i18n/cht/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "正在擷取 typings...", + "acquiringTypingsDetail": "正在為 IntelliSense 擷取 typings 定義。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/cht/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..a951d8cd68 --- /dev/null +++ b/i18n/cht/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "啟用 JavaScript 檔案的語意檢查。必須在檔案的最上面。", + "ts-nocheck": "停用 JavaScript 檔案的語意檢查。必須在檔案的最上面。", + "ts-ignore": "隱藏下一行@ts-check 的錯誤警告。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/cht/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..bd6ae7736e --- /dev/null +++ b/i18n/cht/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 個實作", + "manyImplementationLabel": "{0} 個實作", + "implementationsErrorLabel": "無法判斷實作數" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/cht/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..4987ca253e --- /dev/null +++ b/i18n/cht/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "JSDoc 註解" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/cht/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..8dd07315b9 --- /dev/null +++ b/i18n/cht/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 個參考", + "manyReferenceLabel": "{0} 個參考", + "referenceErrorLabel": "無法判斷參考" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/cht/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..9d78c9b812 --- /dev/null +++ b/i18n/cht/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "監看 - {0}", + "buildTscLabel": "建置 - {0}" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/typescriptMain.i18n.json b/i18n/cht/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..e300fe29a8 --- /dev/null +++ b/i18n/cht/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "請在 VS Code 中開啟資料夾,以使用 TypeScript 或 JavaScript 專案", + "typescript.projectConfigUnsupportedFile": "無法判斷 TypeScript 或 JavaScript 專案。不支援的檔案類型", + "typescript.projectConfigCouldNotGetInfo": "無法判斷 TypeScript 或 JavaScript 專案", + "typescript.noTypeScriptProjectConfig": "檔案不是 TypeScript 專案的一部份", + "typescript.noJavaScriptProjectConfig": "檔案不是 JavaScript 專案的一部份", + "typescript.configureTsconfigQuickPick": "設定 tsconfig.json", + "typescript.configureJsconfigQuickPick": "設定 jsconfig.json", + "typescript.projectConfigLearnMore": "深入了解" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/cht/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..bf16211877 --- /dev/null +++ b/i18n/cht/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "路徑 {0} 未指向有效的 tsserver 安裝。即將回復為配套的 TypeScript 版本。", + "serverCouldNotBeStarted": "無法啟動 TypeScript 語言伺服器。錯誤訊息為: {0}", + "typescript.openTsServerLog.notSupported": "TS 伺服器的記錄功能需要 TS 2.2.2+", + "typescript.openTsServerLog.loggingNotEnabled": "TS 伺服器記錄功能已關閉。請設定 `typescript.tsserver.log` 並重新啟動 TS 伺服器,以啟用記錄功能", + "typescript.openTsServerLog.enableAndReloadOption": "啟用記錄功能並重新啟動 TS 伺服器", + "typescript.openTsServerLog.noLogFile": "TS 伺服器尚未開始記錄。", + "openTsServerLog.openFileFailedFailed": "無法開啟 TS 伺服器記錄檔", + "serverDiedAfterStart": "TypeScript 語言服務在啟動後立即中止 5 次。服務將不會重新啟動。", + "serverDiedReportIssue": "回報問題", + "serverDied": "TypeScript 語言服務在過去 5 分鐘內意外中止 5 次。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/utils/api.i18n.json b/i18n/cht/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..3af81b1b4f --- /dev/null +++ b/i18n/cht/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "無效的版本" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/utils/logger.i18n.json b/i18n/cht/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/cht/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/cht/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..4e46c94684 --- /dev/null +++ b/i18n/cht/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "若要讓整個專案都能使用 JavaScript/TypeScript 語言功能,請排除內含許多檔案的資料夾,例如: {0}", + "hintExclude.generic": "若要讓整個專案都能使用 JavaScript/TypeScript 語言功能,請排除內含您未使用之來源檔案的大型資料夾。", + "large.label": "設定排除項目", + "hintExclude.tooltip": "若要讓整個專案都能使用 JavaScript/TypeScript 語言功能,請排除內含您未使用之來源檔案的大型資料夾。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/cht/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..8295db585e --- /dev/null +++ b/i18n/cht/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "正在擷取資料以改善 TypeScript IntelliSense", + "typesInstallerInitializationFailed.title": "無法安裝typings檔案Javascript語言功能,請確認NPM是否已安裝且配置'typescript.npm'", + "typesInstallerInitializationFailed.moreInformation": "詳細資訊", + "typesInstallerInitializationFailed.doNotCheckAgain": "不要再檢查", + "typesInstallerInitializationFailed.close": "關閉" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/cht/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..9511e70d1a --- /dev/null +++ b/i18n/cht/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "使用 VS Code 的版本", + "useWorkspaceVersionOption": "使用工作區版本", + "learnMore": "深入了解", + "selectTsVersion": "選取 JavaScript 與 TypeScript 功能使用的 TypeScript 版本" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/cht/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..66a0d7c5ee --- /dev/null +++ b/i18n/cht/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "無法在此路徑載入 TypeScript 版本", + "noBundledServerFound": "其他應用程式已刪除了 VS Code 的 tsserver,例如行為不當的病毒偵測工具。請重新安裝 VS Code。" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/package.i18n.json b/i18n/cht/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..8fa2be4a66 --- /dev/null +++ b/i18n/cht/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "重新載入專案", + "javascript.reloadProjects.title": "重新載入專案", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "使用其參數簽章完成函式。", + "typescript.tsdk.desc": "指定資料夾路徑,其中包含要使用的 tsserver 和 lib*.d.ts 檔案。", + "typescript.disableAutomaticTypeAcquisition": "停用自動類型取得。需要 TypeScript >= 2.0.6。", + "typescript.tsserver.log": "允許 TS 伺服器記錄到檔案。此記錄可用來診斷 TS 伺服器問題。記錄可能包含檔案路徑、原始程式碼及您專案中可能具有敏感性的其他資訊。", + "typescript.tsserver.trace": "允許將訊息追蹤傳送到 TS 伺服器。此追蹤可用來診斷 TS 伺服器問題。追蹤可能包含檔案路徑、原始程式碼及您專案中可能具有敏感性的其他資訊。", + "typescript.validate.enable": "啟用/停用 TypeScript 驗證。", + "typescript.format.enable": "啟用/停用預設 TypeScript 格式器。", + "javascript.format.enable": "啟用/停用預設 JavaScript 格式器。", + "format.insertSpaceAfterCommaDelimiter": "定義逗號分隔符號後的空格處理。", + "format.insertSpaceAfterConstructor": "定義 constructor 關鍵字後空格處理。需要 TypeScript >= 2.3.0。", + "format.insertSpaceAfterSemicolonInForStatements": " 定義 for 陳述式內分號後的空格處理。", + "format.insertSpaceBeforeAndAfterBinaryOperators": "定義二元運算子後的空格處理。", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "定義控制流程陳述式內關鍵字後的空格處理方式。", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "定義匿名函式之函式關鍵字後的空格處理。", + "format.insertSpaceBeforeFunctionParenthesis": "定義如何處理函式引數括號之前的空格。TypeScript 必須 >= 2.1.5。", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "定義左右非空白括弧間的空格處理。", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "定義左右非空白中括弧間的空格處理。", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "定義範非空白左右大括弧間的空格處理。需要 TypeScript >= 2.3.0。", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "定義範本字串左右大括弧間的空格處理。需要 TypeScript >= 2.0.6。", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "定義 JSX 運算式左右大括弧間的空格處理。需要 TypeScript >= 2.0.6。", + "format.insertSpaceAfterTypeAssertion": "定義在 TypeScript 內類型宣告後空格處理。需要 TypeScript >= 2.4。", + "format.placeOpenBraceOnNewLineForFunctions": "定義是否將左大括弧放入函式的新行。", + "format.placeOpenBraceOnNewLineForControlBlocks": "定義是否將左大括弧放入控制區塊的新行。", + "javascript.validate.enable": "啟用/停用 JavaScript 驗證。", + "typescript.goToProjectConfig.title": "移至專案組態", + "javascript.goToProjectConfig.title": "移至專案組態", + "javascript.referencesCodeLens.enabled": "在JavaScript檔案啟用/停用 參考CodeLens ", + "typescript.referencesCodeLens.enabled": "在TypeScript檔案啟用/停用CodeLens參考。需要TypeScript>=2.0.6。", + "typescript.implementationsCodeLens.enabled": "啟用/停用實作 CodeLens。需要 TypeScript >= 2.2.0。", + "typescript.openTsServerLog.title": "開啟 TS 伺服器記錄", + "typescript.restartTsServer": "重新啟動TS伺服器", + "typescript.selectTypeScriptVersion.title": "選取 TypeScript 版本", + "jsDocCompletion.enabled": "啟用/停用自動 JSDoc 註解", + "javascript.implicitProjectConfig.checkJs": "啟用/停用 JavaScript 檔案的語意檢查。現有的 jsconfig.json 或 tsconfig.json 檔案會覆寫此設定。需要 TypeScript >=2.3.1。", + "typescript.npm": "指定用於自動類型取得的 NPM 可執行檔路徑。TypeScript 必須 >= 2.3.4.", + "typescript.check.npmIsInstalled": "檢查是否已安裝NPM用以取得自動類型擷取.", + "javascript.nameSuggestions": "從JavaScript推薦表檔案中啟用/停用包含唯一檔名", + "typescript.tsc.autoDetect": "控制 tsc 工作的自動偵測為開啟或關閉。", + "typescript.problemMatchers.tsc.label": "TypeScript 問題", + "typescript.problemMatchers.tscWatch.label": " TypeScript 問題 (監看模式)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/cht/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/cht/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/cht/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..9d5400ff10 --- /dev/null +++ b/i18n/cht/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (再次出現)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/cht/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..c54670e603 --- /dev/null +++ b/i18n/cht/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "輸入" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/cht/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..b17f0ac39d --- /dev/null +++ b/i18n/cht/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "大小寫須相符", + "wordsDescription": "全字拼寫須相符", + "regexDescription": "使用規則運算式" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/cht/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..e6e5701ecd --- /dev/null +++ b/i18n/cht/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "錯誤: {0}", + "alertWarningMessage": "警告: {0}", + "alertInfoMessage": "資訊: {0}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/cht/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..49d25589c3 --- /dev/null +++ b/i18n/cht/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "因為影像太大,無法在編輯器中顯示。", + "resourceOpenExternalButton": "要使用外部程式打開影像嗎?", + "nativeBinaryError": "檔案為二進位檔、非常大或使用不支援的文字編碼,因此將不會顯示於編輯器中。", + "sizeB": "{0}B", + "sizeKB": "{0}KB", + "sizeMB": "{0}MB", + "sizeGB": "{0}GB", + "sizeTB": "{0}TB" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/cht/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..1e5fa29d9f --- /dev/null +++ b/i18n/cht/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "其他" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/common/errorMessage.i18n.json b/i18n/cht/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..0a4f49e472 --- /dev/null +++ b/i18n/cht/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}。錯誤碼: {1}", + "error.permission.verbose": "使用權限遭拒 (HTTP {0})", + "error.permission": "使用權限遭拒", + "error.http.verbose": "{0} (HTTP {1}: {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "未知的連線錯誤 ({0})", + "error.connection.unknown": "發生未知的連接錯誤。可能是您已經沒有連線到網際網路,或是您連接的伺服器已離線。", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "發生未知的錯誤。如需詳細資訊,請參閱記錄檔。", + "nodeExceptionMessage": "發生系統錯誤 ({0})", + "error.moreErrors": "{0} (總計 {1} 個錯誤)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/common/json.i18n.json b/i18n/cht/src/vs/base/common/json.i18n.json new file mode 100644 index 0000000000..715eec0159 --- /dev/null +++ b/i18n/cht/src/vs/base/common/json.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "符號無效", + "error.invalidNumberFormat": "數字格式無效", + "error.propertyNameExpected": "必須有屬性名稱", + "error.valueExpected": "必須有值", + "error.colonExpected": "必須為冒號", + "error.commaExpected": "必須為逗號", + "error.closeBraceExpected": "必須為右大括號", + "error.closeBracketExpected": "必須為右中括號", + "error.endOfFileExpected": "必須為檔案結尾" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/cht/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..1124e3e787 --- /dev/null +++ b/i18n/cht/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "無效的符號", + "error.invalidNumberFormat": "無效的數字格式", + "error.propertyNameExpected": "須有屬性名稱", + "error.valueExpected": "必須有值", + "error.colonExpected": "必須有冒號", + "error.commaExpected": "必須有逗號", + "error.closeBraceExpected": "必須為右大括號", + "error.closeBracketExpected": "必須為右中括號", + "error.endOfFileExpected": "必須有檔案結尾" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/common/keybindingLabels.i18n.json b/i18n/cht/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..b234e36af1 --- /dev/null +++ b/i18n/cht/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Shift", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Shift", + "altKey.long": "Alt", + "cmdKey.long": "Command", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/common/processes.i18n.json b/i18n/cht/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..39d13ec00d --- /dev/null +++ b/i18n/cht/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "錯誤: 可執行檔資訊必須定義字串類型的命令。", + "ExecutableParser.isShellCommand": "警告: isShellCommand 必須屬於布林值類型。即將忽略值 {0}。", + "ExecutableParser.args": "警告: args 必須屬於 string[] 類型。即將忽略值 {0}。", + "ExecutableParser.invalidCWD": "警告: options.cwd 必須屬於字串類型。即將忽略值 {0}。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/common/severity.i18n.json b/i18n/cht/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..53fcf81f1b --- /dev/null +++ b/i18n/cht/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "錯誤", + "sev.warning": "警告", + "sev.info": "資訊" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/node/processes.i18n.json b/i18n/cht/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..86884091af --- /dev/null +++ b/i18n/cht/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "無法在 UNC 磁碟機上執行殼層命令。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/node/zip.i18n.json b/i18n/cht/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..d6422d1133 --- /dev/null +++ b/i18n/cht/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "在 ZIP 中找不到 {0}。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/cht/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..dbffd7d064 --- /dev/null +++ b/i18n/cht/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0},選擇器", + "quickOpenAriaLabel": "選擇器" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/cht/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..b6b9f547e4 --- /dev/null +++ b/i18n/cht/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "快速選擇器。輸入以縮小結果範圍。", + "treeAriaLabel": "快速選擇器" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/cht/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..32b517e77a --- /dev/null +++ b/i18n/cht/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "摺疊" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/code/electron-main/auth.i18n.json b/i18n/cht/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..b69db66de4 --- /dev/null +++ b/i18n/cht/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "必須進行 Proxy 驗證 ", + "proxyauth": "Proxy {0} 必須進行驗證。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/code/electron-main/menus.i18n.json b/i18n/cht/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..67a1fb58bd --- /dev/null +++ b/i18n/cht/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "檔案 (&&F)", + "mEdit": "編輯(&&E)", + "mSelection": "選取項目(&&S)", + "mView": "檢視 (&&V)", + "mGoto": "前往(&&G)", + "mDebug": "偵錯 (&&D)", + "mWindow": "視窗", + "mHelp": "說明 (&&H)", + "mTask": "工作(&&T)", + "miNewWindow": "開新視窗(&&W)", + "mAbout": "關於 {0}", + "mServices": "服務", + "mHide": "隱藏 {0}", + "mHideOthers": "隱藏其他", + "mShowAll": "全部顯示", + "miQuit": "結束 {0}", + "miNewFile": "新增檔案(&&N)", + "miOpen": "開啟(&&O)...", + "miOpenWorkspace": "開啟工作區(&&O)...", + "miOpenFolder": "開啟資料夾(&&F)...", + "miOpenFile": "開啟檔案(&&O)...", + "miOpenRecent": "開啟最近的檔案(&&R)", + "miSaveWorkspaceAs": "將工作區另存為(&&S)...", + "miAddFolderToWorkspace": "將資料夾新增到工作區(&&A)", + "miSave": "儲存(&&S)", + "miSaveAs": "另存新檔(&&A)...", + "miSaveAll": "全部儲存(&&L)", + "miAutoSave": "自動儲存", + "miRevert": "還原檔案(&&V)", + "miCloseWindow": "關閉視窗(&&E)", + "miCloseWorkspace": "關閉工作區(&&W)", + "miCloseFolder": "關閉資料夾(&&F)", + "miCloseEditor": "關閉編輯器(&&C)", + "miExit": "結束(&&X)", + "miOpenSettings": "設定(&&S)", + "miOpenKeymap": "鍵盤快速鍵(&&K)", + "miOpenKeymapExtensions": "按鍵對應延伸模組(&&K)", + "miOpenSnippets": "使用者程式碼片段(&&S)", + "miSelectColorTheme": "色彩佈景主題(&&C)", + "miSelectIconTheme": "檔案圖示佈景主題(&&I)", + "miPreferences": "喜好設定(&&P)", + "miReopenClosedEditor": "重新開啟已關閉的編輯器(&&R)", + "miMore": "更多(&&M)...", + "miClearRecentOpen": "清除最近開啟的(&&C)", + "miUndo": "復原(&&U)", + "miRedo": "重做(&&R)", + "miCut": "剪下(&&T)", + "miCopy": "複製(&&C)", + "miPaste": "貼上(&&P)", + "miFind": "尋找(&&F)", + "miReplace": "取代(&&R)", + "miFindInFiles": "在檔案中尋找(&&I)", + "miReplaceInFiles": "檔案中取代(&&I)", + "miEmmetExpandAbbreviation": "Emmet: 展開縮寫(&&X)", + "miShowEmmetCommands": "Emmet(&&M)...", + "miToggleLineComment": "切換行註解(&&T)", + "miToggleBlockComment": "切換區塊註解(&&B)", + "miMultiCursorAlt": "切換到 Alt+ 按一下啟用多重游標", + "miMultiCursorCmd": "切換到 Cmd+ 按一下啟用多重游標", + "miMultiCursorCtrl": "切換到 Ctrl+ 按一下啟用多重游標", + "miInsertCursorAbove": "在上方新增游標(&&A)", + "miInsertCursorBelow": "在下方新增游標(&&D)", + "miInsertCursorAtEndOfEachLineSelected": "在行尾新增游標(&&U)", + "miAddSelectionToNextFindMatch": "新增下一個項目(&&N)", + "miAddSelectionToPreviousFindMatch": "新增上一個項目(&&R)", + "miSelectHighlights": "選取所有項目(&&O)", + "miCopyLinesUp": "將行向上複製(&&C)", + "miCopyLinesDown": "將行向下複製(&&P)", + "miMoveLinesUp": "上移一行(&&V)", + "miMoveLinesDown": "下移一行(&&L)", + "miSelectAll": "全選(&&S)", + "miSmartSelectGrow": "展開選取範圍(&&E)", + "miSmartSelectShrink": "壓縮選取範圍(&&S)", + "miViewExplorer": "檔案總管(&&E)", + "miViewSearch": "搜尋(&&S)", + "miViewSCM": "SCM", + "miViewDebug": "偵錯 (&&D)", + "miViewExtensions": "擴充功能(&&X)", + "miToggleOutput": "輸出(&&O)", + "miToggleDebugConsole": "偵錯主控台(&&B)", + "miToggleIntegratedTerminal": "整合式終端機(&&I)", + "miMarker": "問題(&&P)", + "miAdditionalViews": "其他檢視(&&V)", + "miCommandPalette": "命令選擇區(&&C)...", + "miToggleFullScreen": "切換全螢幕(&&F)", + "miToggleZenMode": "切換無干擾模式", + "miToggleMenuBar": "切換功能表列(&&B)", + "miSplitEditor": "分割編輯器(&&E)", + "miToggleEditorLayout": "切換編輯器群組配置(&&L)", + "miToggleSidebar": "切換提要欄位(&&T)", + "miMoveSidebarRight": "將提要欄位右移(&&M)", + "miMoveSidebarLeft": "將提要欄位左移(&&M)", + "miTogglePanel": "切換面板(&&P)", + "miHideStatusbar": "隱藏狀態列(&&H)", + "miShowStatusbar": "顯示狀態列(&&S)", + "miHideActivityBar": "隱藏活動列(&&A)", + "miShowActivityBar": "顯示活動列(&&A)", + "miToggleWordWrap": "切換自動換行(&&W)", + "miToggleMinimap": "切換迷你地圖(&M)", + "miToggleRenderWhitespace": "切換轉譯空白字元(&&R)", + "miToggleRenderControlCharacters": "切換&&控制字元", + "miZoomIn": "放大(&&Z)", + "miZoomOut": "縮小(&&U)", + "miZoomReset": "重設縮放(&&R)", + "miBack": "上一步(&B)", + "miForward": "轉寄(&&F)", + "miNextEditor": "下一個編輯器(&&N)", + "miPreviousEditor": "上一個編輯器(&&P)", + "miNextEditorInGroup": "群組中下一個已使用的編輯器(&&N)", + "miPreviousEditorInGroup": "群組中上一個已使用的編輯器(&&P)", + "miSwitchEditor": "切換編輯器(&&E)", + "miFocusFirstGroup": "第一個群組(&&F)", + "miFocusSecondGroup": "第二個群組(&&S)", + "miFocusThirdGroup": "第三個群組(&&T)", + "miNextGroup": "下一個群組(&&N)", + "miPreviousGroup": "上一個群組(&&P)", + "miSwitchGroup": "切換群組(&&G)", + "miGotoFile": "移至檔案(&&F)...", + "miGotoSymbolInFile": "前往檔案中的符號(&&S)...", + "miGotoSymbolInWorkspace": "前往工作區中的符號(&&W)...", + "miGotoDefinition": "移至定義(&&D)", + "miGotoTypeDefinition": "移至類型定義(&&T)", + "miGotoImplementation": "前往實作(&&I)", + "miGotoLine": "移至行(&&L)...", + "miStartDebugging": "啟動偵錯(&&S)", + "miStartWithoutDebugging": "只啟動但不偵錯(&&W)", + "miStopDebugging": "停止偵錯(&&S)", + "miRestart Debugging": "重新啟動偵錯(&&R)", + "miOpenConfigurations": "開啟設定(&&C)", + "miAddConfiguration": "新增組態...", + "miStepOver": "不進入函式(&&O)", + "miStepInto": "逐步執行(&&I)", + "miStepOut": "跳離函式(&&U)", + "miContinue": "繼續(&&C)", + "miToggleBreakpoint": "切換中斷點(&&B)", + "miConditionalBreakpoint": "條件式中斷點(&&C)...", + "miColumnBreakpoint": "資料行中斷點(&&O)", + "miFunctionBreakpoint": "函式中斷點(&&F}...", + "miNewBreakpoint": "新增中斷點(&&N)", + "miEnableAllBreakpoints": "啟用所有中斷點", + "miDisableAllBreakpoints": "停用所有中斷點(&&L)", + "miRemoveAllBreakpoints": "移除所有中斷點(&&R)", + "miInstallAdditionalDebuggers": "安裝其他偵錯工具(&&I)...", + "mMinimize": "最小化", + "mZoom": "縮放", + "mBringToFront": "全部提到最上層", + "miSwitchWindow": "切換視窗(&&W)", + "miToggleDevTools": "切換開發人員工具(&&T)", + "miAccessibilityOptions": "協助工具選項(&&O)", + "miReportIssues": "回報問題(&&I)", + "miWelcome": "歡迎使用(&&W)", + "miInteractivePlayground": "Interactive Playground(&&I)", + "miDocumentation": "文件(&&D)", + "miReleaseNotes": "版本資訊(&&R)", + "miKeyboardShortcuts": "鍵盤快速鍵參考(&&K)", + "miIntroductoryVideos": "簡介影片(&&V)", + "miTipsAndTricks": "秘訣與提示(&&T)", + "miTwitter": "加入我們的 Twitter(&&J)", + "miUserVoice": "搜尋功能要求(&&S)", + "miLicense": "檢視授權(&&L)", + "miPrivacyStatement": "隱私權聲明(&&P)", + "miAbout": "關於(&&A)", + "miRunTask": "執行工作(&&R)", + "miBuildTask": " 執行建置工作(&&B)...", + "miRunningTask": "顯示執行中的工作(&&G)...", + "miRestartTask": "重新開始執行工作(&&E)...", + "miTerminateTask": "終止工作(&&T)...", + "miConfigureTask": "設定工作(&&C)", + "miConfigureBuildTask": "設定預設建置工作(&&F)", + "accessibilityOptionsWindowTitle": "協助工具選項", + "miRestartToUpdate": "重新啟動以更新...", + "miCheckingForUpdates": "正在查看是否有更新...", + "miDownloadUpdate": "下載可用更新", + "miDownloadingUpdate": "正在下載更新...", + "miInstallingUpdate": "正在安裝更新...", + "miCheckForUpdates": "查看是否有更新", + "aboutDetail": "\n版本 {0}\n認可 {1}\n日期 {2}\nShell {3}\n轉譯器 {4}\n節點 {5}\n架構 {6}", + "okButton": "確定" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/code/electron-main/window.i18n.json b/i18n/cht/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..06ac4d41fc --- /dev/null +++ b/i18n/cht/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "您仍然可以按 **Alt** 鍵來存取功能表列。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/code/electron-main/windows.i18n.json b/i18n/cht/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..d82b7dfd42 --- /dev/null +++ b/i18n/cht/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "確定", + "pathNotExistTitle": "路徑不存在", + "pathNotExistDetail": "磁碟上似乎已沒有路徑 '{0}'。", + "openWorkspace": "開啟(&&O)...", + "openWorkspaceTitle": "開啟工作區", + "save": "儲存(&&S)", + "doNotSave": "不要儲存(&&N)", + "cancel": "取消", + "saveWorkspaceMessage": "要將工作區組態儲存為檔案嗎?", + "saveWorkspaceDetail": "如果您預計再次開啟工作區,請儲存工作區。", + "saveWorkspace": "儲存工作區", + "reopen": "重新開啟", + "wait": "繼續等候", + "close": "關閉", + "appStalled": "視窗已沒有回應", + "appStalledDetail": "您可以重新開啟或關閉視窗,或是繼續等候。", + "appCrashed": "視窗已損毀", + "appCrashedDetail": "很抱歉造成您的不便! 您可以重新開啟視窗,從您離開的地方繼續進行。", + "open": "開啟", + "openFolder": "開啟資料夾", + "openFile": "開啟檔案" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/code/node/cliProcessMain.i18n.json b/i18n/cht/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..57aa9b8345 --- /dev/null +++ b/i18n/cht/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "找不到擴充功能 '{0}'。", + "notInstalled": "未安裝擴充功能 '{0}'。", + "useId": "請確定您使用完整擴充功能識別碼 (包括發行者),例如: {0}", + "successVsixInstall": "已成功安裝延伸模組 '{0}'!", + "alreadyInstalled": "已安裝過擴充功能 '{0}'。", + "foundExtension": "在市集中找到 '{0}'。", + "installing": "正在安裝...", + "successInstall": "已成功安裝擴充功能 '{0}' v{1}!", + "uninstalling": "正在將 {0} 解除安裝...", + "successUninstall": "已成功將擴充功能 '{0}' 解除安裝!" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/cht/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..98f68cc6a3 --- /dev/null +++ b/i18n/cht/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "因其中一個檔案過大而無法比較。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/cht/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..d866fd45f2 --- /dev/null +++ b/i18n/cht/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "關閉", + "header": "差異 {0} / {1}: 原始 {2},{3} 行,修改後 {4},{5} 行", + "blankLine": "空白", + "equalLine": "原始 {0},修改後{1}: {2", + "insertLine": "+ 修改後 {0}: {1}", + "deleteLine": "- 原始 {0}: {1}", + "editor.action.diffReview.next": "移至下一個差異", + "editor.action.diffReview.prev": "移至上一個差異" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/cht/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..71a289f12b --- /dev/null +++ b/i18n/cht/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "編輯器", + "fontFamily": "控制字型家族。", + "fontWeight": "控制字型寬度。", + "fontSize": "控制字型大小 (以像素為單位)。", + "lineHeight": "控制行高。使用 0 會從 fontSize 計算 lineHeight。", + "letterSpacing": "控制字元間距 (以像素為單位)", + "lineNumbers": "控制行號顯示。可能的值有 'on'、'off' 及 'relative'。'relative' 會從目前的資料指標位置顯示行數。", + "rulers": "要在其中顯示垂直尺規的資料行", + "wordSeparators": "執行文字相關導覽或作業時將作為文字分隔符號的字元", + "tabSize": "與 Tab 相等的空格數量。當 `editor.detectIndentation` 已開啟時,會根據檔案內容覆寫此設定。", + "tabSize.errorMessage": "必須是 'number'。請注意,值 \"auto\" 已由 `editor.detectIndentation` 設定取代。", + "insertSpaces": "在按 Tab 時插入空格。當 `editor.detectIndentation` 已開啟時,會根據檔案內容覆寫此設定。", + "insertSpaces.errorMessage": "必須是 'boolean'。請注意,值 \"auto\" 已由 `editor.detect Indentation` 設定取代。", + "detectIndentation": "開啟檔案時,會依據檔案內容來偵測 `editor.tabSize` 及 `editor.insertSpaces`。", + "roundedSelection": "控制選取範圍是否有圓角", + "scrollBeyondLastLine": "控制編輯器是否會捲動到最後一行之後", + "smoothScrolling": "控制編輯器是否會使用動畫捲動", + "minimap.enabled": "控制是否會顯示迷你地圖", + "minimap.showSlider": "控制是否會自動隱藏迷你地圖滑桿。可能的值為 'always' 與 'mouseover'", + "minimap.renderCharacters": "呈現行內的實際字元 (而不是彩色區塊)", + "minimap.maxColumn": "限制迷你地圖的寬度,以呈現最多的資料行", + "find.seedSearchStringFromSelection": "控制編譯器選取範圍是否預設為尋找工具的搜尋字串", + "find.autoFindInSelection": "控制編譯器內選取多字元或多行內文是否開啟選取範圍尋找功能", + "wordWrap.off": "一律不換行。", + "wordWrap.on": "依檢視區寬度換行。", + "wordWrap.wordWrapColumn": "於 'editor.wordWrapColumn' 換行。", + "wordWrap.bounded": "當檢視區縮至最小並設定 'editor.wordWrapColumn' 時換行。", + "wordWrap": "控制是否自動換行。可以是:\n - 'off' (停用換行),\n - 'on' (檢視區換行),\n - 'wordWrapColumn' (於 'editor.wordWrapColumn' 換行`) 或\n - 'bounded' (當檢視區縮至最小並設定 'editor.wordWrapColumn' 時換行).", + "wordWrapColumn": "當 `editor.wordWrap` 為 [wordWrapColumn] 或 [bounded] 時,控制編輯器中的資料行換行。", + "wrappingIndent": "控制換行的縮排。可以是 [無]、[相同] 或 [縮排]。", + "mouseWheelScrollSensitivity": "滑鼠滾輪捲動事件的 'deltaX' 與 'deltaY' 所使用的乘數", + "multiCursorModifier.ctrlCmd": "對應Windows和Linux的'Control'與對應OSX的'Command'", + "multiCursorModifier.alt": "對應Windows和Linux的'Alt'與對應OSX的'Option'", + "multiCursorModifier": "用於新增多個滑鼠游標的修改程式。`ctrlCmd` 會對應到 Windows 及 Linux 上的 `Control` 以及 OSX 上的 `Command`。[移至定義] 及 [開啟連結] 滑鼠手勢將會適應以避免和 multicursor 修改程式衝突。", + "quickSuggestions.strings": "允許在字串內顯示即時建議。", + "quickSuggestions.comments": "允許在註解中顯示即時建議。", + "quickSuggestions.other": "允許在字串與註解以外之處顯示即時建議。", + "quickSuggestions": "控制是否應在輸入時自動顯示建議", + "quickSuggestionsDelay": "控制延遲顯示快速建議的毫秒數", + "parameterHints": "當您輸入時啟用彈出視窗,顯示參數文件與類型資訊", + "autoClosingBrackets": "控制編輯器是否應在左括號後自動插入右括號", + "formatOnType": "控制編輯器是否應在輸入一行後自動格式化", + "formatOnPaste": "控制編輯器是否應自動設定貼上的內容格式。格式器必須可供使用,而且格式器應該能夠設定文件中一個範圍的格式。", + "autoIndent": "控制當使用者輸入, 貼上或移行時,編輯器必須自動調整縮排\n語言的縮排規則必須可用", + "suggestOnTriggerCharacters": "控制輸入觸發字元時,是否應自動顯示建議", + "acceptSuggestionOnEnter": "控制除了 'Tab' 外,是否也藉由按下 'Enter' 接受建議。如此可避免混淆要插入新行或接受建議。設定值'smart'表示在文字變更同時,只透過Enter接受建議。", + "acceptSuggestionOnCommitCharacter": "控制認可字元是否應接受建議。例如在 JavaScript 中,分號 (';') 可以是接受建議並鍵入該字元的認可字元。", + "snippetSuggestions.top": "將程式碼片段建議顯示於其他建議的頂端。", + "snippetSuggestions.bottom": "將程式碼片段建議顯示於其他建議的下方。", + "snippetSuggestions.inline": "將程式碼片段建議與其他建議一同顯示。", + "snippetSuggestions.none": "不顯示程式碼片段建議。", + "snippetSuggestions": "控制程式碼片段是否隨其他建議顯示,以及其排序方式。", + "emptySelectionClipboard": "控制複製時不選取任何項目是否會複製目前程式行。", + "wordBasedSuggestions": "控制是否應根據文件中的單字計算自動完成。", + "suggestFontSize": "建議小工具的字型大小", + "suggestLineHeight": "建議小工具的行高", + "selectionHighlight": "控制編輯器是否應反白顯示與選取範圍相似的符合項", + "occurrencesHighlight": "控制編輯器是否應反白顯示出現的語意符號", + "overviewRulerLanes": "控制可在概觀尺規中相同位置顯示的裝飾項目數", + "overviewRulerBorder": "控制是否應在概觀尺規周圍繪製邊框。", + "cursorBlinking": "控制游標動畫樣式,可能的值為 'blink'、'smooth'、'phase'、'expand' 和 'solid'", + "mouseWheelZoom": "使用滑鼠滾輪並按住 Ctrl 時,縮放編輯器的字型", + "cursorStyle": "控制游標樣式。接受的值為 'block'、'block-outline'、'line'、'line-thin'、'underline' 及 'underline-thin'", + "fontLigatures": "啟用連字字型", + "hideCursorInOverviewRuler": "控制游標是否應隱藏在概觀尺規中。", + "renderWhitespace": "控制編輯器轉譯空白字元的方式,可能為 'none'、'boundary' 及 'all'。'boundary' 選項不會轉譯字組間的單一空格。", + "renderControlCharacters": "控制編輯器是否應顯示控制字元", + "renderIndentGuides": "控制編輯器是否應顯示縮排輔助線", + "renderLineHighlight": "控制編輯器應如何轉譯目前反白的行,可能的值有 'none'、'gutter'、'line' 和 'all'。", + "codeLens": "控制編輯器是否顯示程式碼濾鏡", + "folding": "控制編輯器是否已啟用程式碼摺疊功能", + "showFoldingControls": "自動隱藏摺疊控制向", + "matchBrackets": "當選取某側的括號時,強調顯示另一側的配對括號。", + "glyphMargin": "控制編輯器是否應轉譯垂直字符邊界。字符邊界最常用來進行偵錯。", + "useTabStops": "插入和刪除接在定位停駐點後的空白字元", + "trimAutoWhitespace": "移除尾端自動插入的空白字元", + "stablePeek": "讓預覽編輯器在使用者按兩下其內容或點擊 Escape 時保持開啟。", + "dragAndDrop": "控制編輯器是否允許透過拖放動作移動選取範圍。", + "accessibilitySupport.auto": "編輯器將使用平台 API 以偵測螢幕助讀程式附加。", + "accessibilitySupport.on": "編輯器將會為螢幕助讀程式的使用方式永久地最佳化。", + "accessibilitySupport.off": "編輯器不會為螢幕助讀程式的使用方式進行最佳化。", + "accessibilitySupport": "控制編輯器是否應於已為螢幕助讀程式最佳化的模式中執行。", + "links": "控制編輯器是否應偵測連結且讓它可點擊", + "colorDecorators": "控制編輯器是否應轉譯內嵌色彩裝飾項目與色彩選擇器。", + "sideBySide": "控制 Diff 編輯器要並排或內嵌顯示差異", + "ignoreTrimWhitespace": "控制 Diff 編輯器是否將開頭或尾端空白字元的變更顯示為差異", + "renderIndicators": "控制 Diff 編輯器是否要為新增的/移除的變更顯示 +/- 標記", + "selectionClipboard": "控制是否應支援 Linux 主要剪貼簿。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/config/defaultConfig.i18n.json b/i18n/cht/src/vs/editor/common/config/defaultConfig.i18n.json new file mode 100644 index 0000000000..1c421254f8 --- /dev/null +++ b/i18n/cht/src/vs/editor/common/config/defaultConfig.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorViewAccessibleLabel": "編輯器內容" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/cht/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..6f84b7502b --- /dev/null +++ b/i18n/cht/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "編輯器現在無法存取。按Alt+F1尋求選項", + "editorViewAccessibleLabel": "編輯器內容" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/controller/cursor.i18n.json b/i18n/cht/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..f49f5b7a7c --- /dev/null +++ b/i18n/cht/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "執行命令時發生未預期的例外狀況。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/cht/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..9e9152af42 --- /dev/null +++ b/i18n/cht/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "將輸入語彙基元化時,模式失敗。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/cht/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..db22312fc7 --- /dev/null +++ b/i18n/cht/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "純文字" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/cht/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..5bd2179561 --- /dev/null +++ b/i18n/cht/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "這些檔案已同時變更: {0}", + "summary.0": "未進行任何編輯", + "summary.nm": "在 {1} 個檔案中進行了 {0} 項文字編輯", + "summary.n0": "在一個檔案中進行了 {0} 項文字編輯" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/cht/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..2d59074193 --- /dev/null +++ b/i18n/cht/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "提供語言宣告。", + "vscode.extension.contributes.languages.id": "語言的識別碼。", + "vscode.extension.contributes.languages.aliases": "語言的別名名稱。", + "vscode.extension.contributes.languages.extensions": "與語言相關聯的副檔名。", + "vscode.extension.contributes.languages.filenames": "與語言相關聯的檔案名稱。", + "vscode.extension.contributes.languages.filenamePatterns": "與語言相關聯的檔案名稱 Glob 模式。", + "vscode.extension.contributes.languages.mimetypes": "與語言相關聯的 MIME 類型。", + "vscode.extension.contributes.languages.firstLine": "規則運算式,符合語言檔案的第一行。", + "vscode.extension.contributes.languages.configuration": "檔案的相對路徑,其中該檔案包含語言組態選項。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/cht/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/cht/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/cht/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..ad78e6ca7b --- /dev/null +++ b/i18n/cht/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "目前游標位置行的反白顯示背景色彩。", + "lineHighlightBorderBox": "目前游標位置行之周圍框線的背景色彩。", + "rangeHighlight": "反白顯示範圍的背景色彩,例如 Quick Open 與尋找功能。", + "caret": "編輯器游標的色彩。", + "editorCursorBackground": "編輯器游標的背景色彩。允許自訂區塊游標重疊的字元色彩。", + "editorWhitespaces": "編輯器中空白字元的色彩。", + "editorIndentGuides": "編輯器縮排輔助線的色彩。", + "editorLineNumbers": "編輯器行號的色彩。", + "editorRuler": "編輯器尺規的色彩", + "editorCodeLensForeground": "編輯器程式碼濾鏡的前景色彩", + "editorBracketMatchBackground": "成對括號背景色彩", + "editorBracketMatchBorder": "成對括號邊框色彩", + "editorOverviewRulerBorder": "預覽檢視編輯器尺規的邊框色彩.", + "editorGutter": "編輯器邊框的背景顏色,包含行號與字形圖示的邊框.", + "errorForeground": "編輯器內錯誤提示線的前景色彩.", + "errorBorder": "編輯器內錯誤提示線的邊框色彩.", + "warningForeground": "編輯器內警告提示線的前景色彩.", + "warningBorder": "編輯器內警告提示線的邊框色彩.", + "overviewRulerRangeHighlight": "範圍醒目提示的概觀尺規標記色彩。", + "overviewRuleError": "錯誤的概觀尺規標記色彩。", + "overviewRuleWarning": "警示的概觀尺規標記色彩。", + "overviewRuleInfo": "資訊的概觀尺規標記色彩。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json b/i18n/cht/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json new file mode 100644 index 0000000000..fcc8e0e481 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "introMsg": "感謝您試用 VS Code 的協助工具選項。", + "status": "狀態:", + "tabFocusModeOnMsg": "在目前的編輯器中按 Tab 鍵會將焦點移至下一個可設定焦點的元素。按 {0} 可切換此行為。", + "tabFocusModeOnMsgNoKb": "在目前的編輯器中按 Tab 鍵會將焦點移至下一個可設定焦點的元素。命令 {0} 目前無法由按鍵繫結關係觸發。", + "tabFocusModeOffMsg": "在目前的編輯器中按 Tab 鍵會插入定位字元。按 {0} 可切換此行為。", + "tabFocusModeOffMsgNoKb": "在目前的編輯器中按 Tab 鍵會插入定位字元。命令 {0} 目前無法由按鍵繫結關係觸發。", + "outroMsg": "您可以按 Esc 鍵來解除此工具提示並返回編輯器。", + "ShowAccessibilityHelpAction": "顯示協助工具說明" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/cht/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..9ffaf163bc --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "移至方括弧" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/cht/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..f3b173e402 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "將插入點左移", + "caret.moveRight": "將插入點右移" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/cht/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..4844b88072 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "調換字母" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/cht/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..7dc6c215ce --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "剪下", + "actions.clipboard.copyLabel": "複製", + "actions.clipboard.pasteLabel": "貼上", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "隨語法醒目提示複製" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/cht/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..5fb31b364e --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "切換行註解", + "comment.line.add": "加入行註解", + "comment.line.remove": "移除行註解", + "comment.block": "切換區塊註解" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/cht/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..2b08c4695e --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "顯示編輯器內容功能表" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/cht/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..6759e20ec0 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "尋找", + "placeholder.find": "尋找", + "label.previousMatchButton": "上一個符合項", + "label.nextMatchButton": "下一個相符項", + "label.toggleSelectionFind": "在選取範圍中尋找", + "label.closeButton": "關閉", + "label.replace": "取代", + "placeholder.replace": "取代", + "label.replaceButton": "取代", + "label.replaceAllButton": "全部取代", + "label.toggleReplaceButton": "切換取代模式", + "title.matchesCountLimit": "只會將前 999 筆結果醒目提示,但所有尋找作業會在完整文字上執行。", + "label.matchesLocation": "{0} / {1}", + "label.noResults": "沒有結果" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/cht/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..6b199c0c05 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "尋找", + "placeholder.find": "尋找", + "label.previousMatchButton": "上一個符合項", + "label.nextMatchButton": "下一個相符項", + "label.closeButton": "關閉" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/cht/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..e85fe96d4a --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "尋找", + "findNextMatchAction": "尋找下一個", + "findPreviousMatchAction": "尋找上一個", + "nextSelectionMatchFindAction": "尋找下一個選取項目", + "previousSelectionMatchFindAction": "尋找上一個選取項目", + "startReplace": "取代", + "addSelectionToNextFindMatch": "將選取項目加入下一個找到的相符項", + "addSelectionToPreviousFindMatch": "將選取項目加入前一個找到的相符項中", + "moveSelectionToNextFindMatch": "將最後一個選擇項目移至下一個找到的相符項", + "moveSelectionToPreviousFindMatch": "將最後一個選擇項目移至前一個找到的相符項", + "selectAllOccurrencesOfFindMatch": "選取所有找到的相符項目", + "changeAll.label": "變更所有發生次數", + "showNextFindTermAction": "顯示下一個尋找字詞", + "showPreviousFindTermAction": "顯示上一個尋找字詞" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/cht/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..8b0ea11606 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "展開", + "unFoldRecursivelyAction.label": "以遞迴方式展開", + "foldAction.label": "摺疊", + "foldRecursivelyAction.label": "以遞迴方式摺疊", + "foldAllAction.label": "全部摺疊", + "unfoldAllAction.label": "全部展開", + "foldLevelAction.label": "摺疊層級 {0}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/cht/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..1ca729f33c --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "在行 {0} 編輯了 1 項格式", + "hintn1": "在行 {1} 編輯了 {0} 項格式", + "hint1n": "在行 {0} 與行 {1} 之間編輯了 1 項格式", + "hintnn": "在行 {1} 與行 {2} 之間編輯了 {0} 項格式", + "formatDocument.label": "將文件格式化", + "formatSelection.label": "將選取項目格式化" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json b/i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json new file mode 100644 index 0000000000..3ede42556c --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "找不到 '{0}' 的定義", + "generic.noResults": "找不到任何定義", + "meta.title": " - {0} 個定義", + "actions.goToDecl.label": "移至定義", + "actions.goToDeclToSide.label": "在一側開啟定義", + "actions.previewDecl.label": "預覽定義", + "goToImplementation.noResultWord": "找不到 '{0}' 的任何實作", + "goToImplementation.generic.noResults": "找不到任何實作", + "meta.implementations.title": " – {0} 個實作", + "actions.goToImplementation.label": "前往實作", + "actions.peekImplementation.label": "預覽實作", + "goToTypeDefinition.noResultWord": "找不到 '{0}' 的任何類型定義", + "goToTypeDefinition.generic.noResults": "找不到任何類型定義", + "meta.typeDefinitions.title": " – {0} 個定義", + "actions.goToTypeDefinition.label": "移至類型定義", + "actions.peekTypeDefinition.label": "預覽類型定義", + "multipleResults": "按一下以顯示 {0} 項定義。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..1e136528ab --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "找不到 '{0}' 的定義", + "generic.noResults": "找不到任何定義", + "meta.title": " - {0} 個定義", + "actions.goToDecl.label": "移至定義", + "actions.goToDeclToSide.label": "在一側開啟定義", + "actions.previewDecl.label": "預覽定義", + "goToImplementation.noResultWord": "找不到 '{0}' 的任何實作", + "goToImplementation.generic.noResults": "找不到任何實作", + "meta.implementations.title": " – {0} 個實作", + "actions.goToImplementation.label": "前往實作", + "actions.peekImplementation.label": "預覽實作", + "goToTypeDefinition.noResultWord": "找不到 '{0}' 的任何類型定義", + "goToTypeDefinition.generic.noResults": "找不到任何類型定義", + "meta.typeDefinitions.title": " – {0} 個定義", + "actions.goToTypeDefinition.label": "移至類型定義", + "actions.peekTypeDefinition.label": "預覽類型定義" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..6f1882ea87 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "按一下以顯示 {0} 項定義。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/cht/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..7ba2efd212 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "移至下一個錯誤或警告", + "markerAction.previous.label": "移至上一個錯誤或警告", + "editorMarkerNavigationError": "編輯器標記導覽小工具錯誤的色彩。", + "editorMarkerNavigationWarning": "編輯器標記導覽小工具警告的色彩。", + "editorMarkerNavigationBackground": "編輯器標記導覽小工具的背景。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/cht/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..0ce0b4993a --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "動態顯示" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/cht/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..6ee35574c0 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "正在載入..." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/cht/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..ac8745643b --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "以上一個值取代", + "InPlaceReplaceAction.next.label": "以下一個值取代" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/cht/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..7716fd3ed8 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "將縮排轉換成空格", + "indentationToTabs": "將縮排轉換成定位點", + "configuredTabSize": "已設定的定位點大小", + "selectTabWidth": "選取目前檔案的定位點大小", + "indentUsingTabs": "使用 Tab 進行縮排", + "indentUsingSpaces": "使用空格鍵進行縮排", + "detectIndentation": "偵測內容中的縮排", + "editor.reindentlines": "重新將行縮排" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json b/i18n/cht/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f007088833 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "開發人員: 檢查 TM 範圍", + "inspectTMScopesWidget.loading": "正在載入..." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/cht/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..b959c31736 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "將行向上複製", + "lines.copyDown": "將行向下複製", + "lines.moveUp": "上移一行", + "lines.moveDown": "下移一行", + "lines.sortAscending": "遞增排序行", + "lines.sortDescending": "遞減排序行", + "lines.trimTrailingWhitespace": "修剪尾端空白", + "lines.delete": "刪除行", + "lines.indent": "縮排行", + "lines.outdent": "凸排行", + "lines.insertBefore": "在上方插入行", + "lines.insertAfter": "在下方插入行", + "lines.deleteAllLeft": "左邊全部刪除", + "lines.deleteAllRight": "刪除所有右方項目", + "lines.joinLines": "連接線", + "editor.transpose": "轉置游標周圍的字元數", + "editor.transformToUppercase": "轉換到大寫", + "editor.transformToLowercase": "轉換到小寫" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/cht/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..000387c994 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "按住 Cmd 並按一下按鍵以追蹤連結", + "links.navigate": "按住 Ctrl 並按一下滑鼠按鈕可連入連結", + "links.command.mac": "按住 Cmd 並按一下滑鼠以執行命令", + "links.command": "按住 Ctrl 並按一下滑鼠以執行命令", + "links.navigate.al": "按住Alt並點擊以追蹤連結", + "links.command.al": "按住 Alt 並按一下滑鼠以執行命令", + "invalid.url": "抱歉,因為此連結的語式不正確,所以無法加以開啟: {0}", + "missing.url": "抱歉,因為此連結遺失目標,所以無法加以開啟。", + "label": "開啟連結" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/cht/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..8ca68a5d65 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "在上方加入游標", + "mutlicursor.insertBelow": "在下方加入游標", + "mutlicursor.insertAtEndOfEachLineSelected": "在行尾新增游標" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/cht/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..63bd268b7a --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "觸發參數提示" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/cht/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..594503e6de --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0},提示" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/cht/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..4c88a3b4bb --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "顯示修正 ({0})", + "quickFix": "顯示修正", + "quickfix.trigger.label": "Quick Fix" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..f2be52d38b --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": " - {0} 個參考", + "references.action.label": "尋找所有參考" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..b09b09b759 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "正在載入..." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..af8b8cc859 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "個符號位於 {0} 中的第 {1} 行第 {2} 欄", + "aria.fileReferences.1": "1 個符號位於 {0}, 完整路徑 {1}", + "aria.fileReferences.N": "{0} 個符號位於 {1}, 完整路徑 {2}", + "aria.result.0": "找不到結果", + "aria.result.1": "在 {0} 中找到 1 個符號", + "aria.result.n1": "在 {1} 中找到 {0} 個符號", + "aria.result.nm": "在 {1} 個檔案中找到 {0} 個符號" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..3966d7dc38 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "無法解析檔案。", + "referencesCount": "{0} 個參考", + "referenceCount": "{0} 個參考", + "missingPreviewMessage": "無法預覽", + "treeAriaLabel": "參考", + "noResults": "沒有結果", + "peekView.alternateTitle": "參考", + "peekViewTitleBackground": "預覽檢視標題區域的背景色彩。", + "peekViewTitleForeground": "預覽檢視標題的色彩。", + "peekViewTitleInfoForeground": "預覽檢視標題資訊的色彩。", + "peekViewBorder": "預覽檢視之框線與箭頭的色彩。", + "peekViewResultsBackground": "預覽檢視中結果清單的背景色彩。", + "peekViewResultsMatchForeground": "預覽檢視結果列表中行節點的前景色彩", + "peekViewResultsFileForeground": "預覽檢視結果列表中檔案節點的前景色彩", + "peekViewResultsSelectionBackground": "在預覽檢視之結果清單中選取項目時的背景色彩。", + "peekViewResultsSelectionForeground": "在預覽檢視之結果清單中選取項目時的前景色彩。", + "peekViewEditorBackground": "預覽檢視編輯器的背景色彩。", + "peekViewEditorGutterBackground": "預覽檢視編輯器邊框(含行號或字形圖示)的背景色彩。", + "peekViewResultsMatchHighlight": "在預覽檢視編輯器中比對時的反白顯示色彩。", + "peekViewEditorMatchHighlight": "預覽檢視編輯器中比對時的反白顯示色彩。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/cht/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..61b630ba4c --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "沒有結果。", + "aria": "已成功將 '{0}' 重新命名為 '{1}'。摘要: {2}", + "rename.failed": "抱歉,無法執行重新命名。", + "rename.label": "重新命名符號" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/cht/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..532e9df39d --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "為輸入重新命名。請鍵入新名稱,然後按 Enter 以認可。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/cht/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..e8aec47515 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "展開選取", + "smartSelect.shrink": "縮小選取" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/cht/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..b6c2e0b78c --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "接受 '{0}' 時接受了插入下列文字: {1}", + "suggest.trigger.label": "觸發建議" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/cht/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..49b8f832a4 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "建議小工具的背景色彩。", + "editorSuggestWidgetBorder": "建議小工具的邊界色彩。", + "editorSuggestWidgetForeground": "建議小工具的前景色彩。", + "editorSuggestWidgetSelectedBackground": "建議小工具中所選項目的背景色彩。", + "editorSuggestWidgetHighlightForeground": "建議小工具中相符醒目提示的色彩。", + "readMore": "進一步了解...{0}", + "suggestionWithDetailsAriaLabel": "{0},建議,有詳細資料", + "suggestionAriaLabel": "{0},建議", + "readLess": "簡易說明...{0}", + "suggestWidget.loading": "正在載入...", + "suggestWidget.noSuggestions": "無建議。", + "suggestionAriaAccepted": "{0},接受", + "ariaCurrentSuggestionWithDetails": "{0},建議,有詳細資料", + "ariaCurrentSuggestion": "{0},建議" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/cht/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..5e976f3c6c --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "切換 TAB 鍵移動焦點" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/cht/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..f0c948b5a2 --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "讀取存取期間 (例如讀取變數時) 符號的背景色彩。", + "wordHighlightStrong": "寫入存取期間 (例如寫入變數時) 符號的背景色彩。", + "overviewRulerWordHighlightForeground": "符號醒目提示的概觀尺規標記色彩。", + "overviewRulerWordHighlightStrongForeground": "寫入權限符號醒目提示的概觀尺規標記色彩。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/cht/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..94928a538f --- /dev/null +++ b/i18n/cht/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "關閉" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/cht/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..8cfa898545 --- /dev/null +++ b/i18n/cht/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "`contributes.{0}.language` 中的不明語言。提供的值: {1}", + "invalid.scopeName": "`contributes.{0}.scopeName` 中的預期字串。提供的值: {1}", + "invalid.path.0": "Expected string in `contributes.{0}.path`. Provided value: {1}", + "invalid.injectTo": "`contributes.{0}.injectTo` 中的值無效。必須是語言範圍名稱的陣列。提供的值: {1}", + "invalid.embeddedLanguages": "`contributes.{0}.embeddedLanguages` 中的值無效。必須是從範圍名稱到語言的物件對應。提供的值: {1}", + "invalid.path.1": "要包含在擴充功能資料夾 ({2}) 中的預期 `contributes.{0}.path` ({1})。這可能會使擴充功能無法移植。", + "no-tm-grammar": "此語言未註冊任何 TM 文法。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/cht/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..70b6c2cb30 --- /dev/null +++ b/i18n/cht/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "剖析 {0} 時發生錯誤: {1}", + "schema.openBracket": "左括弧字元或字串順序。", + "schema.closeBracket": "右括弧字元或字串順序。", + "schema.comments": "定義註解符號", + "schema.blockComments": "定義標記區塊註解的方式。", + "schema.blockComment.begin": "區塊註解開頭的字元順序。", + "schema.blockComment.end": "區塊註解結尾的字元順序。", + "schema.lineComment": "行註解開頭的字元順序。", + "schema.brackets": "定義增加或減少縮排的括弧符號。", + "schema.autoClosingPairs": "定義成對括弧。輸入左括弧時,即自動插入右括弧。", + "schema.autoClosingPairs.notIn": "定義停用自動配對的範圍清單。", + "schema.surroundingPairs": "定義可用以括住所選字串的成對括弧。", + "schema.wordPattern": "定義語言的文字", + "schema.wordPattern.pattern": "使用正規表示式進行文字比對", + "schema.wordPattern.flags": "使用正規表示式標記進行文字比對", + "schema.wordPattern.flags.errorMessage": "必須符合樣式 `/^([gimuy]+)$/`" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/cht/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..3d75f41300 --- /dev/null +++ b/i18n/cht/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "提供 textmate 權杖化工具。", + "vscode.extension.contributes.grammars.language": "要提供此語法的目標語言識別碼。", + "vscode.extension.contributes.grammars.scopeName": "tmLanguage 檔案所使用的 textmate 範圍名稱。", + "vscode.extension.contributes.grammars.path": "tmLanguage 檔案的路徑。此路徑是擴充功能資料夾的相對路徑,而且一般會以 './syntaxes/' 開頭。", + "vscode.extension.contributes.grammars.embeddedLanguages": "如果此文法包含內嵌語言,即為範圍名稱到語言識別碼的對應。", + "vscode.extension.contributes.grammars.injectTo": "要插入此文法的語言範圍名稱清單。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/cht/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/cht/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/cht/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..a806822d06 --- /dev/null +++ b/i18n/cht/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "功能表項目必須為陣列", + "requirestring": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "optstring": "屬性 `{0}` 可以省略或必須屬於 `string` 類型", + "vscode.extension.contributes.menuItem.command": "所要執行命令的識別碼。命令必須在 'commands' 區段中宣告", + "vscode.extension.contributes.menuItem.alt": "所要執行替代命令的識別碼。命令必須在 'commands' 區段中宣告", + "vscode.extension.contributes.menuItem.when": "必須為 true 以顯示此項目的條件", + "vscode.extension.contributes.menuItem.group": "此命令所屬群組", + "vscode.extension.contributes.menus": "將功能表項目提供給編輯器", + "menus.commandPalette": "命令選擇區", + "menus.editorTitle": "編輯器標題功能表", + "menus.editorContext": "編輯器操作功能表", + "menus.explorerContext": "檔案總管操作功能表", + "menus.editorTabContext": "編輯器索引標籤操作功能表", + "menus.debugCallstackContext": "偵錯呼叫堆疊操作功能表", + "menus.scmTitle": "原始檔控制標題功能表", + "menus.resourceGroupContext": "原始檔控制資源群組操作功能表", + "menus.resourceStateContext": "原始檔控制資源群組狀態操作功能表", + "view.viewTitle": "這有助於查看標題功能表", + "view.itemContext": "這有助於查看項目內容功能表", + "nonempty": "必須是非空白值。", + "opticon": "屬性 `icon` 可以省略,否則必須為字串或類似 `{dark, light}` 的常值", + "requireStringOrObject": "'{0}' 為必要屬性,且其類型必須是 'string' 或 'object'", + "requirestrings": "'{0}' 與 '{1}' 為必要屬性,且其類型必須是 'string'", + "vscode.extension.contributes.commandType.command": "所要執行命令的識別碼", + "vscode.extension.contributes.commandType.title": "UI 中用以代表命令的標題", + "vscode.extension.contributes.commandType.category": "(選用) UI 中用以將命令分組的分類字串", + "vscode.extension.contributes.commandType.icon": "(選用) UI 中用以代表命令的圖示。會是檔案路徑或可設定佈景主題的組態", + "vscode.extension.contributes.commandType.icon.light": "使用淺色佈景主題時的圖示路徑", + "vscode.extension.contributes.commandType.icon.dark": "使用深色佈景主題時的圖示路徑", + "vscode.extension.contributes.commands": "將命令提供給命令選擇區。", + "dup": "命令 `{0}` 在 `commands` 區段中出現多次。", + "menuId.invalid": "`{0}` 不是有效的功能表識別碼", + "missing.command": "功能表項目參考了 'commands' 區段中未定義的命令 `{0}`。", + "missing.altCommand": "功能表項目參考了 'commands' 區段中未定義的替代命令 `{0}`。", + "dupe.command": "功能表項目參考了與預設相同的命令和替代命令", + "nosupport.altCommand": "很抱歉,目前只有 [編輯器/標題] 功能表的 [導覽] 群組支援替代命令" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/cht/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..2f1f34912d --- /dev/null +++ b/i18n/cht/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "預設組態覆寫", + "overrideSettings.description": "設定要針對 {0} 語言覆寫的編輯器設定。", + "overrideSettings.defaultDescription": "設定要針對語言覆寫的編輯器設定。", + "config.property.languageDefault": "無法註冊 '{0}'。這符合用於描述語言專用編輯器設定的屬性模式 '\\\\[.*\\\\]$'。請使用 'configurationDefaults' 貢獻。", + "config.property.duplicate": "無法註冊 '{0}'。此屬性已經註冊。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/environment/node/argv.i18n.json b/i18n/cht/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..2388174559 --- /dev/null +++ b/i18n/cht/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "`--goto` 模式中的引數格式應為 `FILE(:LINE(:CHARACTER))`。", + "diff": "互相比較兩個檔案。", + "add": "將資料夾新增至上一個使用中的視窗。", + "goto": "在路徑上的指定行與字元位置開啟檔案。", + "locale": "要使用的地區設定 (例如 en-US 或 zh-TW)。", + "newWindow": "強制執行 Code 的新執行個體。", + "performance": "在已啟用 'Developer: Startup Performance' 命令的情況下開始。", + "prof-startup": "啟動時執行 CPU 分析工具", + "reuseWindow": "強制在最近使用的視窗中開啟檔案或資料夾。", + "userDataDir": "指定保留使用者資料的目錄,這在以根目錄身分執行時有用。", + "verbose": "列印詳細資訊輸出 (表示 --wait)。", + "wait": "等候視窗在傳回前關閉。", + "extensionHomePath": "設定擴充功能的根路徑。", + "listExtensions": "列出已安裝的擴充功能。", + "showVersions": "使用 --list-extension 時,顯示安裝的擴充功能版本。", + "installExtension": "安裝擴充功能。", + "uninstallExtension": "解除安裝擴充功能。", + "experimentalApis": "為延伸模組啟用建議的 API 功能。", + "disableExtensions": "停用所有已安裝的擴充功能。", + "disableGPU": "停用 GPU 硬體加速。", + "version": "列印版本。", + "help": "列印使用方式。", + "usage": "使用方式", + "options": "選項", + "paths": "路徑", + "optionsUpperCase": "選項" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/cht/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..731b6c8e7c --- /dev/null +++ b/i18n/cht/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "沒有任何工作區。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/cht/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..846fada301 --- /dev/null +++ b/i18n/cht/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "延伸模組", + "preferences": "喜好設定" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/cht/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..87afa0206d --- /dev/null +++ b/i18n/cht/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "找不到擴充功能", + "noCompatible": "找不到與此 Code 版本相容的 {0} 版本。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/cht/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..c657fd4faa --- /dev/null +++ b/i18n/cht/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "擴充功能無效: package.json 不是 JSON 檔案。", + "restartCode": "請先重新啟動 Code,再重新安裝 {0}。", + "installDependeciesConfirmation": "安裝 '{0}' 也會安裝其相依性。要繼續嗎?", + "install": "是", + "doNotInstall": "否", + "uninstallDependeciesConfirmation": "只要將 '{0}' 解除安裝,或要包含其相依性?", + "uninstallOnly": "只有", + "uninstallAll": "全部", + "cancel": "取消", + "uninstallConfirmation": "確定要將 '{0}' 解除安裝嗎?", + "ok": "確定", + "singleDependentError": "無法將延伸模組 '{0}' 解除安裝。其為延伸模組 '{1}' 的相依對象。", + "twoDependentsError": "無法將延伸模組 '{0}' 解除安裝。其為延伸模組 '{1}' 及 '{2}' 的相依對象。", + "multipleDependentsError": "無法將延伸模組 '{0}' 解除安裝。其為 '{1}'、'{2}' 及其他延伸模組的相依對象。", + "notExists": "找不到擴充功能" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/cht/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..d581fec88e --- /dev/null +++ b/i18n/cht/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "擴充功能 `{1}` 無法啟動。原因: 未知的相依性 `{0}`。", + "failedDep1": "擴充功能 `{1}` 無法啟動。原因: 相依性 `{0}` 無法啟動。", + "failedDep2": "擴充功能 `{0}` 無法啟動。原因: 相依性超過 10 個層級 (很可能是相依性迴圈)。", + "activationError": "啟動擴充功能 `{0}` 失敗: {1}。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/cht/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..2996ac254c --- /dev/null +++ b/i18n/cht/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "若是 VS Code 延伸模組,則指定與延伸模組相容的 VS Code 版本。不得為 *。例如: ^0.10.5 表示與最低 VS Code 版本 0.10.5 相容。", + "vscode.extension.publisher": "VS Code 擴充功能的發行者。", + "vscode.extension.displayName": "VS Code 資源庫中使用的擴充功能顯示名稱。", + "vscode.extension.categories": "VS Code 資源庫用來將擴充功能歸類的分類。", + "vscode.extension.galleryBanner": "用於 VS Code Marketplace 的橫幅。", + "vscode.extension.galleryBanner.color": "VS Code Marketplace 頁首的橫幅色彩。", + "vscode.extension.galleryBanner.theme": "橫幅中使用的字型色彩佈景主題。", + "vscode.extension.contributes": "此封裝所代表的所有 VS Code 擴充功能比重。", + "vscode.extension.preview": "將延伸模組設為在 Marketplace 中標幟為 [預覽]。", + "vscode.extension.activationEvents": "VS Code 擴充功能的啟動事件。", + "vscode.extension.activationEvents.onLanguage": "當指定語言檔案開啟時激發該事件", + "vscode.extension.activationEvents.onCommand": "當指定的命令被調用時激發該事件", + "vscode.extension.activationEvents.onDebug": "當指定的工作偵錯階段開始時激發該事件", + "vscode.extension.activationEvents.workspaceContains": "當開啟指定的文件夾包含glob模式匹配的文件時激發該事件", + "vscode.extension.activationEvents.onView": "當指定的檢視被擴展時激發該事件", + "vscode.extension.activationEvents.star": "當VS Code啟動時激發該事件,為了確保最好的使用者體驗,當您的擴充功能沒有其他組合作業時,請激活此事件.", + "vscode.extension.badges": "要顯示於 Marketplace 擴充頁面資訊看板的徽章陣列。", + "vscode.extension.badges.url": "徽章映像 URL。", + "vscode.extension.badges.href": "徽章連結。", + "vscode.extension.badges.description": "徽章描述。", + "vscode.extension.extensionDependencies": "其它擴充功能的相依性。擴充功能的識別碼一律為 ${publisher}.${name}。例如: vscode.csharp。", + "vscode.extension.scripts.prepublish": "在封裝作為 VS Code 擴充功能發行前所執行的指令碼。", + "vscode.extension.icon": "128 x 128 像素圖示的路徑。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/cht/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..994436e96f --- /dev/null +++ b/i18n/cht/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "無法剖析 'engines.vscode` 值 {0}。例如,請使用:^0.10.0、^1.2.3、^0.11.0、^0.10.x 等。", + "versionSpecificity1": "在 `engines.vscode` ({0}) 中指定的版本不夠具體。對於 1.0.0 之前的 vscode 版本,請至少定義所需的主要和次要版本。 例如 ^0.10.0、0.10.x、0.11.0 等。", + "versionSpecificity2": "在 `engines.vscode` ({0}) 中指定的版本不夠具體。對於 1.0.0 之後的 vscode 版本,請至少定義所需的主要和次要版本。 例如 ^1.10.0、1.10.x、1.x.x、2.x.x 等。", + "versionMismatch": "擴充功能與 Code {0} 不相容。擴充功能需要: {1}。", + "extensionDescription.empty": "得到空白擴充功能描述", + "extensionDescription.publisher": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "extensionDescription.name": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "extensionDescription.version": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "extensionDescription.engines": "屬性 '{0}' 為強制項目且必須屬於 `object` 類型", + "extensionDescription.engines.vscode": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "extensionDescription.extensionDependencies": "屬性 `{0}` 可以省略或必須屬於 `string[]` 類型", + "extensionDescription.activationEvents1": "屬性 `{0}` 可以省略或必須屬於 `string[]` 類型", + "extensionDescription.activationEvents2": "屬性 `{0}` 和 `{1}` 必須同時指定或同時忽略", + "extensionDescription.main1": "屬性 `{0}` 可以省略或必須屬於 `string` 類型", + "extensionDescription.main2": "`main` ({0}) 必須包含在擴充功能的資料夾 ({1}) 中。這可能會使擴充功能無法移植。", + "extensionDescription.main3": "屬性 `{0}` 和 `{1}` 必須同時指定或同時忽略", + "notSemver": "擴充功能版本與 semver 不相容。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/cht/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..0fbffe0ca3 --- /dev/null +++ b/i18n/cht/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "開新視窗", + "newWindowDesc": "開啟新視窗", + "recentFolders": "最近使用的工作區", + "folderDesc": "{0} {1}", + "codeWorkspace": "Code 工作區" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/cht/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..7aa8ed83ce --- /dev/null +++ b/i18n/cht/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "確定", + "integrity.dontShowAgain": "不要再顯示", + "integrity.moreInfo": "詳細資訊", + "integrity.prompt": "您的 {0} 安裝似乎已損毀。請重新安裝。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/cht/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..56c24d8731 --- /dev/null +++ b/i18n/cht/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "提供 JSON 結構描述組態。", + "contributes.jsonValidation.fileMatch": "要比對的檔案模式,例如 \"package.json\" 或 \"*.launch\"。", + "contributes.jsonValidation.url": "結構描述 URL ('http:'、'https:') 或擴充功能資料夾的相對路徑 ('./')。", + "invalid.jsonValidation": "'configuration.jsonValidation' 必須是陣列", + "invalid.fileMatch": "必須定義 'configuration.jsonValidation.fileMatch'", + "invalid.url": "'configuration.jsonValidation.url' 必須是 URL 或相對路徑", + "invalid.url.fileschema": "'configuration.jsonValidation.url' 是無效的相對 URL: {0}", + "invalid.url.schema": "'configuration.jsonValidation.url' 必須以 'http:'、'https:' 或 './' 開頭,以參考位於擴充功能中的結構描述" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/cht/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..1d3cabae7a --- /dev/null +++ b/i18n/cht/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "已按下 ({0})。請等待第二個套索鍵...", + "missing.chord": "按鍵組合 ({0}, {1}) 不是命令。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/keybinding/common/keybindingLabels.i18n.json b/i18n/cht/src/vs/platform/keybinding/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..b234e36af1 --- /dev/null +++ b/i18n/cht/src/vs/platform/keybinding/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Shift", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Shift", + "altKey.long": "Alt", + "cmdKey.long": "Command", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/cht/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..e97bbfaadf --- /dev/null +++ b/i18n/cht/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "只有最後一行比對器才支援迴圈屬性。", + "ProblemPatternParser.problemPattern.missingRegExp": "此問題模式缺少規則運算式。", + "ProblemPatternParser.problemPattern.missingProperty": "問題模式無效。其必須至少有一個檔案、訊息以及行或位置符合群組。", + "ProblemPatternParser.invalidRegexp": "錯誤: 字串 {0} 不是有效的規則運算式。\n", + "ProblemPatternSchema.regexp": "規則運算式,用來在輸出中尋找錯誤、警告或資訊。", + "ProblemPatternSchema.file": "檔案名稱的符合群組索引。如果省略,則會使用 1。", + "ProblemPatternSchema.location": "問題之位置的符合群組索引。有效的位置模式為: (line)、(line,column) 和 (startLine,startColumn,endLine,endColumn)。如果省略,則會假設 (line,column)。", + "ProblemPatternSchema.line": "問題之行的符合群組索引。預設為 2", + "ProblemPatternSchema.column": "問題行中字元的符合群組索引。預設為 3", + "ProblemPatternSchema.endLine": "問題之結尾行的符合群組索引。預設為未定義", + "ProblemPatternSchema.endColumn": "問題之結尾行字元的符合群組索引。預設為未定義", + "ProblemPatternSchema.severity": "問題之嚴重性的符合群組索引。預設為未定義", + "ProblemPatternSchema.code": "問題之代碼的符合群組索引。預設為未定義", + "ProblemPatternSchema.message": "訊息的符合群組索引。如果省略並指定位置,預設為 4。否則預設為 5。", + "ProblemPatternSchema.loop": "在多行比對器迴圈中,指出此模式是否只要相符就會以迴圈執行。只能在多行模式中的最後一個模式指定。", + "NamedProblemPatternSchema.name": "問題模式的名稱。", + "NamedMultiLineProblemPatternSchema.name": "多行問題模式的名稱。", + "NamedMultiLineProblemPatternSchema.patterns": "實際的模式。", + "ProblemPatternExtPoint": "提供問題模式", + "ProblemPatternRegistry.error": "問題模式無效。此模式將予忽略。", + "ProblemMatcherParser.noProblemMatcher": "錯誤: 無法將描述轉換成問題比對器:\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "錯誤: 描述未定義有效的問題樣式:\n{0}\n", + "ProblemMatcherParser.noOwner": "錯誤: 描述未定義擁有者:\n{0}\n", + "ProblemMatcherParser.noFileLocation": "錯誤: 描述未定義檔案位置:\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "資訊: 嚴重性 {0} 不明。有效值為錯誤、警告和資訊。\n", + "ProblemMatcherParser.noDefinedPatter": "錯誤: 沒有識別碼為 {0} 的樣式。", + "ProblemMatcherParser.noIdentifier": "錯誤: 樣式屬性參考了空的識別碼。", + "ProblemMatcherParser.noValidIdentifier": "錯誤: 樣式屬性 {0} 不是有效的樣式變數名稱。", + "ProblemMatcherParser.problemPattern.watchingMatcher": "問題比對器必須同時定義監控的開始模式和結束模式。", + "ProblemMatcherParser.invalidRegexp": "錯誤: 字串 {0} 不是有效的規則運算式。\n", + "WatchingPatternSchema.regexp": "用來查看偵測背景工作開始或結束的正規表達式.", + "WatchingPatternSchema.file": "檔案名稱的符合群組索引。可以省略。", + "PatternTypeSchema.name": "所提供或預先定義之模式的名稱", + "PatternTypeSchema.description": "問題模式或所提供或預先定義之問題模式的名稱。如有指定基底,即可發出。", + "ProblemMatcherSchema.base": "要使用之基底問題比對器的名稱。", + "ProblemMatcherSchema.owner": "Code 內的問題擁有者。如果指定基底,則可以省略。如果省略且未指定基底,預設為 [外部]。", + "ProblemMatcherSchema.severity": "擷取項目問題的預設嚴重性。如果模式未定義嚴重性的符合群組,就會加以使用。", + "ProblemMatcherSchema.applyTo": "控制文字文件上所回報的問題僅會套用至開啟的文件、關閉的文件或所有文件。", + "ProblemMatcherSchema.fileLocation": "定義問題模式中所回報檔案名稱的解譯方式。", + "ProblemMatcherSchema.background": "偵測後台任務中匹配程序模式的開始與結束.", + "ProblemMatcherSchema.background.activeOnStart": "如果設置為 True,背景監控程式在工作啟動時處於主動模式。這相當於符合起始樣式的行。", + "ProblemMatcherSchema.background.beginsPattern": "如果於輸出中相符,則會指示背景程式開始。", + "ProblemMatcherSchema.background.endsPattern": "如果於輸出中相符,則會指示背景程式結束。", + "ProblemMatcherSchema.watching.deprecated": "關注屬性已被淘汰,請改用背景取代。", + "ProblemMatcherSchema.watching": "追蹤匹配程序的開始與結束。", + "ProblemMatcherSchema.watching.activeOnStart": "如果設定為 True,監控程式在工作啟動時處於主動模式。這相當於發出符合 beginPattern 的行", + "ProblemMatcherSchema.watching.beginsPattern": "如果在輸出中相符,則會指示監看工作開始。", + "ProblemMatcherSchema.watching.endsPattern": "如果在輸出中相符,則會指示監看工作結束。", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "此屬性即將淘汰。請改用關注的屬性。", + "LegacyProblemMatcherSchema.watchedBegin": "規則運算式,指示監看的工作開始執行 (透過檔案監看觸發)。", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "此屬性即將淘汰。請改用關注的屬性。", + "LegacyProblemMatcherSchema.watchedEnd": "規則運算式,指示監看的工作結束執行。", + "NamedProblemMatcherSchema.name": "用來參考其問題比對器的名稱。", + "NamedProblemMatcherSchema.label": "易讀的問題比對器標籤。", + "ProblemMatcherExtPoint": "提供問題比對器", + "msCompile": "Microsoft 編譯器問題 ", + "lessCompile": "較少的問題", + "gulp-tsc": "Gulp TSC 問題", + "jshint": "JSHint 問題", + "jshint-stylish": "JSHint 樣式問題", + "eslint-compact": "ESLint 壓縮問題", + "eslint-stylish": "ESLint 樣式問題", + "go": "前往問題" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/message/common/message.i18n.json b/i18n/cht/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..7ac26f1811 --- /dev/null +++ b/i18n/cht/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "關閉", + "later": "稍後", + "cancel": "取消" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/request/node/request.i18n.json b/i18n/cht/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..efa8c03b7d --- /dev/null +++ b/i18n/cht/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "要使用的 Proxy 設定。如果未設定,會從 http_proxy 與 https_proxy 環境變數取得設定。", + "strictSSL": "是否應該針對提供的 CA 清單驗證 Proxy 伺服器憑證。", + "proxyAuthorization": "要傳送作為每個網路要求 'Proxy-Authorization' 標頭的值。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/cht/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..b06de34d35 --- /dev/null +++ b/i18n/cht/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "遙測", + "telemetry.enableTelemetry": "允許將使用狀況資料和錯誤傳送給 Microsoft。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/cht/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..9373322b72 --- /dev/null +++ b/i18n/cht/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "提供延伸模組定義的可設定佈景主題色彩", + "contributes.color.id": "可設定佈景主題色彩的識別碼 ", + "contributes.color.id.format": "識別碼的格式應為 aa[.bb]*", + "contributes.color.description": "可設定佈景主題色彩的描述 ", + "contributes.defaults.light": "淺色佈景主題的預設色彩。應為十六進位 (#RRGGBB[AA]) 的色彩值,或提供預設的可設定佈景主題色彩。", + "contributes.defaults.dark": "深色佈景主題的預設色彩。應為十六進位 (#RRGGBB[AA]) 的色彩值,或提供預設的可設定佈景主題色彩。 ", + "contributes.defaults.highContrast": "高對比佈景主題的預設色彩。應為十六進位 (#RRGGBB[AA]) 的色彩值,或提供預設的可設定佈景主題色彩。", + "invalid.colorConfiguration": "'configuration.colors' 必須是陣列", + "invalid.default.colorType": "{0} 必須是十六進位 (#RRGGBB[AA] or #RGB[A]) 的色彩值,或是提供預設的可設定佈景主題色彩之識別碼。", + "invalid.id": "'configuration.colors.id' 必須定義且不得為空白", + "invalid.id.format": "'configuration.colors.id' 必須依照 word[.word]*", + "invalid.description": "'configuration.colors.description' 必須定義且不得為空白", + "invalid.defaults": "'configuration.colors.defaults' 必須定義,且必須包含 'light'、'dark' 及 'highContrast'" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/cht/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..85961682a2 --- /dev/null +++ b/i18n/cht/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "色彩格式無效。請使用 #RGB、#RGBA、#RRGGBB 或 #RRGGBBAA", + "schema.colors": "工作台中使用的色彩。", + "foreground": "整體的前景色彩。僅當未被任何元件覆疊時,才會使用此色彩。", + "errorForeground": "整體錯誤訊息的前景色彩。僅當未被任何元件覆蓋時,才會使用此色彩。", + "descriptionForeground": "提供附加訊息的前景顏色,例如標籤", + "focusBorder": "焦點項目的整體框線色彩。只在沒有任何元件覆寫此色彩時,才會加以使用。", + "contrastBorder": "項目周圍的額外框線,可將項目從其他項目中區隔出來以提高對比。", + "activeContrastBorder": "使用中項目周圍的額外邊界,可將項目從其他項目中區隔出來以提高對比。", + "selectionBackground": "作業區域選取的背景顏色(例如輸入或文字區域)。請注意,這不適用於編輯器中的選取。", + "textSeparatorForeground": "文字分隔符號的顏色。", + "textLinkForeground": "內文連結的前景色彩", + "textLinkActiveForeground": "內文使用連結的前景色彩", + "textPreformatForeground": "提示及建議文字的前景色彩。", + "textBlockQuoteBackground": "文內引用區塊背景色彩。", + "textBlockQuoteBorder": "引用文字的框線顏色。", + "textCodeBlockBackground": "文字區塊的背景顏色。", + "widgetShadow": "小工具的陰影色彩,例如編輯器中的尋找/取代。", + "inputBoxBackground": "輸入方塊的背景。", + "inputBoxForeground": "輸入方塊的前景。", + "inputBoxBorder": "輸入方塊的框線。", + "inputBoxActiveOptionBorder": "輸入欄位中可使用之項目的框線色彩。", + "inputPlaceholderForeground": "文字輸入替代字符的前景顏色。", + "inputValidationInfoBackground": "資訊嚴重性的輸入驗證背景色彩。", + "inputValidationInfoBorder": "資訊嚴重性的輸入驗證邊界色彩。", + "inputValidationWarningBackground": "資訊警告的輸入驗證背景色彩。", + "inputValidationWarningBorder": "警告嚴重性的輸入驗證邊界色彩。", + "inputValidationErrorBackground": "錯誤嚴重性的輸入驗證背景色彩。", + "inputValidationErrorBorder": "錯誤嚴重性的輸入驗證邊界色彩。", + "dropdownBackground": "下拉式清單的背景。", + "dropdownForeground": "下拉式清單的前景。", + "dropdownBorder": "下拉式清單的框線。", + "listFocusBackground": "當清單/樹狀為使用中狀態時,焦點項目的清單/樹狀背景色彩。使用中的清單/樹狀有鍵盤焦點,非使用中者則沒有。", + "listFocusForeground": "當清單/樹狀為使用中狀態時,焦點項目的清單/樹狀前景色彩。使用中的清單/樹狀有鍵盤焦點,非使用中者則沒有。", + "listActiveSelectionBackground": "當清單/樹狀為使用中狀態時,所選項目的清單/樹狀背景色彩。使用中的清單/樹狀有鍵盤焦點,非使用中者則沒有。", + "listActiveSelectionForeground": "當清單/樹狀為使用中狀態時,所選項目的清單/樹狀前景色彩。使用中的清單/樹狀有鍵盤焦點,非使用中者則沒有。", + "listInactiveSelectionBackground": "當清單/樹狀為非使用中狀態時,所選項目的清單/樹狀背景色彩。使用中的清單/樹狀有鍵盤焦點,非使用中者則沒有。", + "listInactiveSelectionForeground": "當清單/樹狀為使用中狀態時,所選項目的清單/樹狀前景色彩。使用中的清單/樹狀有鍵盤焦點,非使用中則沒有。", + "listHoverBackground": "使用滑鼠暫留在項目時的清單/樹狀背景。", + "listHoverForeground": "滑鼠暫留在項目時的清單/樹狀前景。", + "listDropBackground": "使用滑鼠四處移動項目時的清單/樹狀拖放背景。", + "highlight": "在清單/樹狀內搜尋時,相符醒目提示的清單/樹狀前景色彩。", + "pickerGroupForeground": "分組標籤的快速選擇器色彩。", + "pickerGroupBorder": "分組邊界的快速選擇器色彩。", + "buttonForeground": "按鈕前景色彩。", + "buttonBackground": "按鈕背景色彩。", + "buttonHoverBackground": "暫留時的按鈕背景色彩。", + "badgeBackground": "標記的背景顏色。標記為小型的訊息標籤,例如搜尋結果的數量。", + "badgeForeground": "標記的前景顏色。標記為小型的訊息標籤,例如搜尋結果的數量。", + "scrollbarShadow": "指出在捲動該檢視的捲軸陰影。", + "scrollbarSliderBackground": "捲軸滑桿的背景顏色。", + "scrollbarSliderHoverBackground": "動態顯示時捲軸滑桿的背景顏色。", + "scrollbarSliderActiveBackground": "使用中狀態下捲軸滑桿的背景顏色。", + "progressBarBackground": "長時間運行進度條的背景色彩.", + "editorBackground": "編輯器的背景色彩。", + "editorForeground": "編輯器的預設前景色彩。", + "editorWidgetBackground": "編輯器小工具的背景色彩,例如尋找/取代。", + "editorWidgetBorder": "編輯器小工具的邊界色彩。小工具選擇擁有邊界或色彩未被小工具覆寫時,才會使用色彩。", + "editorSelectionBackground": "編輯器選取範圍的色彩。", + "editorSelectionForeground": "為選取的文字顏色高對比化", + "editorInactiveSelection": "非使用中之編輯器選取範圍的色彩。", + "editorSelectionHighlight": "選取時,內容相同之區域的色彩。", + "editorFindMatch": "符合目前搜尋的色彩。", + "findMatchHighlight": "符合其他搜尋的色彩。", + "findRangeHighlight": "限制搜尋之範圍的色彩。", + "hoverHighlight": "在顯示了動態顯示的單字下方醒目提示。", + "hoverBackground": "編輯器動態顯示的背景色彩。", + "hoverBorder": "編輯器動態顯示的框線色彩。", + "activeLinkForeground": "使用中之連結的色彩。", + "diffEditorInserted": "插入文字的背景色彩。", + "diffEditorRemoved": "移除文字的背景色彩。", + "diffEditorInsertedOutline": "插入的文字外框色彩。", + "diffEditorRemovedOutline": "移除的文字外框色彩。", + "mergeCurrentHeaderBackground": "目前內嵌合併衝突中的深色標題背景。", + "mergeCurrentContentBackground": "目前內嵌合併衝突中的內容背景。", + "mergeIncomingHeaderBackground": "傳入內嵌合併衝突中的深色標題背景。", + "mergeIncomingContentBackground": "傳入內嵌合併衝突中的內容背景。", + "mergeCommonHeaderBackground": "內嵌合併衝突中的共同始祖標題背景", + "mergeCommonContentBackground": "內嵌合併衝突中的共同始祖內容背景。", + "mergeBorder": "內嵌合併衝突中標頭及分隔器的邊界色彩。", + "overviewRulerCurrentContentForeground": "目前內嵌合併衝突的概觀尺規前景。", + "overviewRulerIncomingContentForeground": "傳入內嵌合併衝突的概觀尺規前景。", + "overviewRulerCommonContentForeground": "內嵌合併衝突中的共同上階概觀尺規前景。", + "overviewRulerFindMatchForeground": "尋找比對的概觀尺規標記色彩。", + "overviewRulerSelectionHighlightForeground": "選取項目醒目提示的概觀尺規標記色彩。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/cht/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..53ec70b3ed --- /dev/null +++ b/i18n/cht/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "Code 工作區", + "untitledWorkspace": "未命名 (工作區)", + "workspaceNameVerbose": "{0} (工作區)", + "workspaceName": "{0} (工作區)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/cht/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..57bd581399 --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "正在以 {1} 覆寫延伸模組 {0}。", + "extensionUnderDevelopment": "正在載入位於 {0} 的開發延伸模組" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/cht/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..e59398847c --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "關閉", + "cancel": "取消", + "ok": "確定" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/cht/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..58f30fd504 --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "未顯示另外 {0} 個錯誤與警告。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/node/extHostExplorerView.i18n.json b/i18n/cht/src/vs/workbench/api/node/extHostExplorerView.i18n.json new file mode 100644 index 0000000000..2aed331b6b --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/node/extHostExplorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "未註冊識別碼為 '{0}' 的 TreeExplorerNodeProvider。", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider '{0}' 無法提供根節點。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/cht/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..d581fec88e --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "擴充功能 `{1}` 無法啟動。原因: 未知的相依性 `{0}`。", + "failedDep1": "擴充功能 `{1}` 無法啟動。原因: 相依性 `{0}` 無法啟動。", + "failedDep2": "擴充功能 `{0}` 無法啟動。原因: 相依性超過 10 個層級 (很可能是相依性迴圈)。", + "activationError": "啟動擴充功能 `{0}` 失敗: {1}。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/cht/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json b/i18n/cht/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json new file mode 100644 index 0000000000..6085fd7918 --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "未註冊識別碼為 '{0}' 的 TreeExplorerNodeProvider。", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider '{0}' 無法提供根節點。", + "treeExplorer.failedToResolveChildren": "TreeExplorerNodeProvider '{0}' 無法 resolveChildren。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/node/extHostTreeView.i18n.json b/i18n/cht/src/vs/workbench/api/node/extHostTreeView.i18n.json new file mode 100644 index 0000000000..2aed331b6b --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/node/extHostTreeView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "未註冊識別碼為 '{0}' 的 TreeExplorerNodeProvider。", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider '{0}' 無法提供根節點。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/cht/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..a9ce7695a5 --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "未註冊識別碼為 '{0}' 的樹狀檢視。", + "treeItem.notFound": "找不到識別碼為 '{0}' 的樹狀檢視。", + "treeView.duplicateElement": "元件{0}已被註冊" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json b/i18n/cht/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..57bd581399 --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "正在以 {1} 覆寫延伸模組 {0}。", + "extensionUnderDevelopment": "正在載入位於 {0} 的開發延伸模組" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/api/node/mainThreadMessageService.i18n.json b/i18n/cht/src/vs/workbench/api/node/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..e59398847c --- /dev/null +++ b/i18n/cht/src/vs/workbench/api/node/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "關閉", + "cancel": "取消", + "ok": "確定" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..c087a45df7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "設定語言", + "displayLanguage": "定義 VSCode 的顯示語言。", + "doc": "如需支援的語言清單,請參閱 {0}。", + "restart": "改變設定值後需要重新啟動 VSCode.", + "fail.createSettings": "無法建立 '{0}' ({1})。", + "JsonSchema.locale": "要使用的 UI 語言。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..4f201dbbf5 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "開啟資料夾...", + "openFileFolder": "開啟..." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..abae96bb28 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "切換活動列可見度", + "view": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..61775fc2af --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "切換編輯器群組垂直/水平配置", + "horizontalLayout": "水平編輯器群組配置", + "verticalLayout": "垂直編輯器群組配置", + "view": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..16d820ccc7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "切換提要欄位位置", + "view": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..bb3845a83c --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "切換提要欄位可見度", + "view": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..d2abcd1673 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "切換狀態列可見度", + "view": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..5db01f515a --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "切換無干擾模式", + "view": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..2bf63a1fe8 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "開啟資料夾...", + "openFileFolder": "開啟...", + "addFolderToWorkspace": "將資料夾新增到工作區...", + "add": "新增", + "addFolderToWorkspaceTitle": "將資料夾新增到工作區", + "newWorkspace": "新增工作區...", + "select": "選取(&&S)", + "selectWorkspace": "為工作區選取資料夾", + "removeFolderFromWorkspace": "將資料夾從工作區移除", + "saveWorkspaceAsAction": "另存工作區為...", + "saveEmptyWorkspaceNotSupported": "請先開啟工作區以進行儲存。", + "save": "儲存(&&S)", + "saveWorkspace": "儲存工作區", + "openWorkspaceAction": "開啟工作區...", + "openWorkspaceConfigFile": "開啟工作區組態檔" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..15b89f5b58 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "從活動列移除", + "keepInActivityBar": "保留在活動列", + "titleKeybinding": "{0} ({1})", + "additionalViews": "其他檢視", + "numberBadge": "{0} ({1})", + "manageExtension": "管理延伸模組", + "toggle": "切換釘選的檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..fe99122e6a --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "隱藏活動列", + "activityBarAriaLabel": "即時檢視切換器", + "globalActions": "全域動作" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..c1d2c768a5 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "{0} 個動作", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..ade285cbf2 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "二進位檢視器" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..41573a05f4 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "文字編輯器", + "textDiffEditor": "文字 Diff 編輯器", + "binaryDiffEditor": "二進位 Diff 編輯器", + "sideBySideEditor": "並排編輯器", + "groupOnePicker": "在第一個群組顯示編輯器", + "groupTwoPicker": "在第二個群組顯示編輯器", + "groupThreePicker": "在第三個群組顯示編輯器", + "allEditorsPicker": "顯示所有開啟的編輯器", + "view": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..c91ca2bb8c --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "分割編輯器", + "joinTwoGroups": "聯結兩個群組的編輯器", + "navigateEditorGroups": "在編輯器群組間導覽", + "focusActiveEditorGroup": "聚焦使用中的編輯器群組", + "focusFirstEditorGroup": "聚焦第一個編輯器群組", + "focusSecondEditorGroup": "聚焦第二個編輯器群組", + "focusThirdEditorGroup": "聚焦第三個編輯器群組", + "focusPreviousGroup": "聚焦上一個群組", + "focusNextGroup": "聚焦下一個群組", + "openToSide": "開至側邊", + "closeEditor": "關閉編輯器", + "revertAndCloseActiveEditor": "還原並關閉編輯器", + "closeEditorsToTheLeft": "將編輯器關到左側", + "closeEditorsToTheRight": "將編輯器關到右側", + "closeAllEditors": "關閉所有編輯器", + "closeUnmodifiedEditors": "在群組中關閉未修改的編輯器", + "closeEditorsInOtherGroups": "關閉其他群組中的編輯器", + "closeOtherEditorsInGroup": "關閉其他編輯器", + "closeEditorsInGroup": "關閉群組中的所有編輯器", + "moveActiveGroupLeft": "將編輯器群組向左移", + "moveActiveGroupRight": "將編輯器群組向右移", + "minimizeOtherEditorGroups": "將其他編輯器群組最小化", + "evenEditorGroups": "均分編輯器群組寬度", + "maximizeEditor": "將編輯器群組最大化並隱藏側邊欄", + "keepEditor": "保留編輯器", + "openNextEditor": "開啟下一個編輯器", + "openPreviousEditor": "開啟上一個編輯器", + "nextEditorInGroup": "開啟群組中下一個編輯器", + "openPreviousEditorInGroup": "開啟群組中上一個編輯器", + "navigateNext": "向前", + "navigatePrevious": "向後", + "reopenClosedEditor": "重新開啟已關閉的編輯器", + "clearRecentFiles": "清理最近開啟的", + "showEditorsInFirstGroup": "在第一個群組顯示編輯器", + "showEditorsInSecondGroup": "在第二個群組顯示編輯器", + "showEditorsInThirdGroup": "在第三個群組顯示編輯器", + "showEditorsInGroup": "在群組顯示編輯器", + "showAllEditors": "顯示所有編輯器", + "openPreviousRecentlyUsedEditorInGroup": "開啟群組中上一個最近使用的編輯器", + "openNextRecentlyUsedEditorInGroup": "開啟群組中下一個最近使用的編輯器", + "navigateEditorHistoryByInput": "從記錄中開啟上一個編輯器", + "openNextRecentlyUsedEditor": "開啟下一個最近使用的編輯器", + "openPreviousRecentlyUsedEditor": "開啟上一個最近使用的編輯器", + "clearEditorHistory": "清除編輯器記錄", + "focusLastEditorInStack": "開啟群組中最後一個編輯器", + "moveEditorLeft": "將編輯器左移", + "moveEditorRight": "將編輯器右移", + "moveEditorToPreviousGroup": "將編輯器移入上一個群組", + "moveEditorToNextGroup": "將編輯器移入下一個群組" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..d87220a12b --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "以 tab 或群組為單位移動使用中的編輯器", + "editorCommand.activeEditorMove.arg.name": "使用中編輯器的移動引數", + "editorCommand.activeEditorMove.arg.description": "引數屬性:\n\t\t\t\t\t\t* 'to': 提供移動目標位置的字串值。\n\t\t\t\t\t\t* 'by': 提供移動單位的字串值。\n\t\t\t\t\t\t* 'value': 提供移動單位的字串值。可依索引標籤或群組作為單位。\n\t\t\t\t\t", + "commandDeprecated": "已移除命令 **{0}**。您可以改用 **{1}**", + "openKeybindings": "設定鍵盤快速鍵" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..575ed40ec7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "左", + "groupTwoVertical": "置中", + "groupThreeVertical": "右", + "groupOneHorizontal": "上", + "groupTwoHorizontal": "置中", + "groupThreeHorizontal": "下", + "editorOpenError": "無法開啟 '{0}': {1}。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..5369b2e290 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},編輯器群組選擇器", + "groupLabel": "群組: {0}", + "noResultsFoundInGroup": "在群組中找不到任何相符的已開啟編輯器", + "noOpenedEditors": "開啟的編輯器清單在群組中目前為空白", + "noResultsFound": "找不到任何相符的已開啟編輯器", + "noOpenedEditorsAllGroups": "開啟的編輯器清單目前為空白" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..5459a8047c --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "第 {0} 行,第 {1} 欄 (已選取 {2})", + "singleSelection": "第 {0} 行,第 {1} 欄", + "multiSelectionRange": "{0} 個選取項目 (已選取 {1} 個字元)", + "multiSelection": "{0} 個選取項目", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "用 Tab 鍵移動焦點", + "screenReaderDetected": "偵測到螢幕助讀程式", + "screenReaderDetectedExtra": "若您不打算使用螢幕助讀程式,請將設定 `editor.accessibilitySupport` 變更為 \"off\"。", + "disableTabMode": "停用協助工具模式", + "gotoLine": "移至行", + "indentation": "縮排", + "selectEncoding": "選取編碼", + "selectEOL": "選取行尾順序", + "selectLanguageMode": "選取語言模式", + "fileInfo": "檔案資訊", + "spacesSize": "空格: {0}", + "tabSize": "定位點大小: {0}", + "showLanguageExtensions": "在 Marketplace 搜尋延伸模組 '{0}'...", + "changeMode": "變更語言模式", + "noEditor": "目前沒有使用中的文字編輯器", + "languageDescription": "({0}) - 設定的語言", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "語言 (識別碼)", + "configureModeSettings": "進行以 '{0}' 語言為基礎的設定...", + "configureAssociationsExt": "為 '{0}' 設定檔案關聯...", + "autoDetect": "自動偵測", + "pickLanguage": "選取語言模式", + "currentAssociation": "目前的關聯", + "pickLanguageToConfigure": "選取要與 '{0}' 建立關聯的語言模式", + "changeIndentation": "變更縮排", + "noWritableCodeEditor": "使用中的程式碼編輯器為唯讀。", + "indentView": "變更檢視", + "indentConvert": "轉換檔案", + "pickAction": "選取動作", + "changeEndOfLine": "變更行尾順序", + "pickEndOfLine": "選取行尾順序", + "changeEncoding": "變更檔案的編碼", + "noFileEditor": "目前沒有使用中的檔案", + "saveWithEncoding": "以編碼儲存", + "reopenWithEncoding": "以編碼重新開啟", + "guessedEncoding": "已從內容猜測", + "pickEncodingForReopen": "選取檔案的編碼以重新開啟檔案", + "pickEncodingForSave": "選取用來儲存的檔案編碼" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..f2159a414b --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "索引標籤動作" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..5f053bdfc9 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "文字 Diff 編輯器", + "readonlyEditorWithInputAriaLabel": "{0}。唯讀文字比較編輯器。", + "readonlyEditorAriaLabel": "唯讀文字比較編輯器。", + "editableEditorWithInputAriaLabel": "{0}。文字檔案比較編輯器。", + "editableEditorAriaLabel": "文字檔比較編輯器。", + "navigate.next.label": "下一個變更", + "navigate.prev.label": "上一個變更", + "inlineDiffLabel": "切換至內嵌檢視", + "sideBySideDiffLabel": "切換至並排檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..7fa314e4a4 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0},群組 {1}。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..6475417259 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "文字編輯器", + "readonlyEditorWithInputAriaLabel": "{0}。唯讀文字編輯器。", + "readonlyEditorAriaLabel": "唯讀文字編輯器。", + "untitledFileEditorWithInputAriaLabel": "{0}。無標題檔案文字編輯器。", + "untitledFileEditorAriaLabel": "無標題檔案文字編輯器。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..8f7a9dc181 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "關閉", + "closeOthers": "關閉其他", + "closeRight": "關到右側", + "closeAll": "全部關閉", + "closeAllUnmodified": "關閉未變更的檔案", + "keepOpen": "保持開啟", + "showOpenedEditors": "顯示開啟的編輯器", + "araLabelEditorActions": "編輯器動作" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..e7e3cd1ded --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "關閉面板", + "togglePanel": "切換面板", + "focusPanel": "將焦點移至面板", + "toggleMaximizedPanel": "切換最大化面板", + "maximizePanel": "最大化面板大小", + "minimizePanel": "還原面板大小", + "view": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..4c5625b6a3 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "使用中面板切換器" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..890deed163 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} (按 'Enter' 鍵確認或按 'Esc' 鍵取消)", + "inputModeEntry": "按 'Enter' 鍵確認您的輸入或按 'Esc' 鍵取消", + "emptyPicks": "沒有可挑選的項目", + "quickOpenInput": "輸入 '?' 即可取得相關說明來了解您可以在這裡執行的動作", + "historyMatches": "最近開啟的", + "noResultsFound1": "找不到結果", + "canNotRunPlaceholder": "目前內容無法使用這個 Quick Open 處理常式", + "entryAriaLabel": "{0},最近開啟的項目", + "removeFromEditorHistory": "從記錄中移除", + "pickHistory": "選取編輯器輸入即可從歷程記錄移除" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..69f0772e7f --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "移至檔案...", + "quickNavigateNext": "在 Quick Open 中導覽至下一項", + "quickNavigatePrevious": "在 Quick Open 中導覽至上一項", + "quickSelectNext": "在 Quick Open 中選取下一個", + "quickSelectPrevious": "在 Quick Open 中選取上一個" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..69f0772e7f --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "移至檔案...", + "quickNavigateNext": "在 Quick Open 中導覽至下一項", + "quickNavigatePrevious": "在 Quick Open 中導覽至上一項", + "quickSelectNext": "在 Quick Open 中選取下一個", + "quickSelectPrevious": "在 Quick Open 中選取上一個" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..65a2ecb741 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "隱藏提要欄位", + "focusSideBar": "瀏覽至提要欄位", + "viewCategory": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..70358286ad --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "命令 '{0}' 目前未啟用,因此無法執行。", + "manageExtension": "管理延伸模組" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..205ecfe67a --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[不支援]", + "devExtensionWindowTitlePrefix": "[Extension Development Host]" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/quickopen.i18n.json b/i18n/cht/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..492c6d04c7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "沒有相符的結果", + "noResultsFound2": "找不到結果", + "entryAriaLabel": "{0},命令" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/viewlet.i18n.json b/i18n/cht/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..2d8b981267 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "全部摺疊" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/common/theme.i18n.json b/i18n/cht/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..5e3eccb9e3 --- /dev/null +++ b/i18n/cht/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "使用中之索引標籤的背景色彩。索引標籤是編輯器在編輯器區域中的容器。同一個編輯器群組中的多個索引標籤可以同時開啟。可能會有多個編輯器群組。", + "tabInactiveBackground": "非使用中之索引標籤的背景色彩。索引標籤是編輯器在編輯器區域中的容器。同一個編輯器群組中的多個索引標籤可以同時開啟。可能會有多個編輯器群組。", + "tabBorder": "用以分隔索引標籤彼此的框線。索引標籤是編輯器在編輯器區域中的容器。同一個編輯器群組中的多個索引標籤可以同時開啟。可能會有多個編輯器群組。", + "tabActiveBorder": "用以醒目提示使用中索引標籤的框線。索引標籤是編輯器在編輯器區域中的容器。同一個編輯器群組中的多個索引標籤可以同時開啟。可能會有多個編輯器群組。", + "tabActiveUnfocusedBorder": "用以在非焦點群組中醒目提示使用中索引標籤的框線。索引標籤是編輯器在編輯器區域中的容器。同一個編輯器群組中的多個索引標籤可以同時開啟。可能會有多個編輯器群組。 ", + "tabActiveForeground": "使用中的群組內,使用中之索引標籤的前景色彩。索引標籤是編輯器在編輯器區域中的容器。同一個編輯器群組中的多個索引標籤可以同時開啟。可能會有多個編輯器群組。", + "tabInactiveForeground": "使用中的群組內,非使用中之索引標籤的前景色彩。索引標籤是編輯器在編輯器區域中的容器。同一個編輯器群組中的多個索引標籤可以同時開啟。可能會有多個編輯器群組。", + "tabUnfocusedActiveForeground": "非焦點群組中的使用中索引標籤前景色彩。索引標籤是編輯器在編輯器區域中的容器。同一個編輯器群組中的多個索引標籤可以同時開啟。可能會有多個編輯器群組。", + "tabUnfocusedInactiveForeground": "非焦點群組中的非使用中索引標籤前景色彩。索引標籤是編輯器在編輯器區域中的容器。同一個編輯器群組中的多個索引標籤可以同時開啟。可能會有多個編輯器群組。", + "editorGroupBackground": "編輯器群組的背景色彩。編輯器群組是編輯器的容器。當拖曳編輯器群組時會顯示背景色彩。", + "tabsContainerBackground": "當索引標籤啟用的時候編輯器群組標題的背景色彩。編輯器群組是編輯器的容器。", + "tabsContainerBorder": "當索引標籤啟用時,編輯器群組標題的框線色彩。編輯器群組是編輯器的容器。", + "editorGroupHeaderBackground": "當索引標籤禁用的時候編輯器群組標題的背景顏色。編輯器群組是編輯器的容器。", + "editorGroupBorder": "用以分隔多個編輯器群組彼此的色彩。編輯器群組是編輯器的容器。", + "editorDragAndDropBackground": "拖拉編輯器時的背景顏色,可設置透明度讓內容穿透顯示.", + "panelBackground": "面板的前景色彩。面板會顯示在編輯器區域的下方,其中包含諸如輸出與整合式終端機等檢視。", + "panelBorder": "面板頂端用以分隔編輯器的邊框色彩。面板會顯示在編輯器區域的下方,其中包含諸如輸出與整合式終端機等檢視。", + "panelActiveTitleForeground": "使用中之面板標題的標題色彩。面板會顯示在編輯器區域的下方,其中包含諸如輸出與整合式終端機等檢視。", + "panelInactiveTitleForeground": "非使用中之面板標題的標題色彩。面板會顯示在編輯器區域的下方,其中包含諸如輸出與整合式終端機等檢視。", + "panelActiveTitleBorder": "使用中之面板標題的框線色彩。面板會顯示在編輯器區域的下方,其中包含諸如輸出與整合式終端機等檢視。", + "statusBarForeground": "當一個工作區被開啟時,狀態列的前景色彩。狀態列會顯示在視窗的底部。", + "statusBarNoFolderForeground": "當未開啟任何資料夾時,狀態列的前景色彩。狀態列會顯示在視窗的底部。", + "statusBarBackground": "當一個工作區被開啟時,狀態列的背景色彩。狀態列會顯示在視窗的底部。", + "statusBarNoFolderBackground": "當未開啟任何資料夾時,狀態列的背景色彩。狀態列會顯示在視窗的底部。", + "statusBarBorder": "用以分隔資訊看板與編輯器的狀態列框線色彩。狀態列會顯示在視窗的底部。", + "statusBarNoFolderBorder": "未開啟資料夾時,用以分隔資訊看板與編輯器的狀態列框線色彩。狀態列會顯示在視窗的底部。 ", + "statusBarItemActiveBackground": "按下滑鼠按鈕時,狀態列項目的背景色彩。狀態列會顯示在視窗的底部。", + "statusBarItemHoverBackground": "動態顯示時,狀態列項目的背景色彩。狀態列會顯示在視窗的底部。", + "statusBarProminentItemBackground": "狀態列突出項目的背景顏色。突出項目比狀態列的其他項目更顯眼,用於表示重要性更高。狀態列會顯示在視窗的底部。", + "statusBarProminentItemHoverBackground": "狀態列突出項目暫留時的背景顏色。突出項目比狀態列的其他項目更顯眼,用於表示重要性更高。狀態列會顯示在視窗的底部。", + "activityBarBackground": "活動列背景的色彩。活動列會顯示在最左側或最右側,並可切換不同的提要欄位檢視。", + "activityBarForeground": "活動列的前背景色彩(例如用於圖示)。此活動列會顯示在最左側或最右側,讓您可以切換提要欄位的不同檢視。", + "activityBarBorder": "用以分隔提要欄位的活動列框線色彩。此活動列會顯示在最左側或最右側,讓您可以切換提要欄位的不同檢視。", + "activityBarDragAndDropBackground": "拖拉活動徽章項目時的色彩.顏色可設置透明度讓原活動徽章可穿透顯示.活動徽章列表會出現在最左側或最右側並允許切換不同的檢視.", + "activityBarBadgeBackground": "活動通知徽章的背景色彩。此活動列會顯示在最左側或最右側,讓您可以切換提要欄位的不同檢視。", + "activityBarBadgeForeground": "活動通知徽章的前背景色彩。此活動列會顯示在最左側或最右側,讓您可以切換提要欄位的不同檢視。", + "sideBarBackground": "提要欄位的背景色彩。提要欄位是檢視 (例如 Explorer 與搜尋) 的容器。", + "sideBarForeground": "側欄的前景顏色.側欄包含Explorer與搜尋.", + "sideBarBorder": "用以分隔編輯器的側邊提要欄位框線色彩。該提要欄位是檢視 (例如 Explorer 及搜尋) 的容器。", + "sideBarTitleForeground": "提要欄位標題的前景色彩。提要欄位是檢視 (例如 Explorer 與搜尋) 的容器。", + "sideBarDragAndDropBackground": "拖放提要欄位區段的意見回應色彩。色彩應具透明度,以便讓提要欄位區段穿透顯示。提要欄位是總管和搜尋等檢視用的容器。", + "sideBarSectionHeaderBackground": "提要欄位區段標頭的背景色彩。提要欄位是檢視 (例如 Explorer 與搜尋) 的容器。", + "sideBarSectionHeaderForeground": "提要欄位區段標頭的前景色彩。提要欄位是檢視 (例如 Explorer 與搜尋) 的容器。", + "titleBarActiveForeground": "作用中視窗之標題列的前景。請注意,目前只有 macOS 支援此色彩。", + "titleBarInactiveForeground": "非作用中視窗之標題列的前景。請注意,目前只有 macOS 支援此色彩。", + "titleBarActiveBackground": "作用中視窗之標題列的背景。請注意,目前只有 macOS 支援此色彩。", + "titleBarInactiveBackground": "非作用中視窗之標題列的背景。請注意,目前只有 macOS 支援此色彩。", + "titleBarBorder": "標題列框線色彩。請注意,目前只有 macOS 支援此色彩。", + "notificationsForeground": "通知的前景色彩。通知從視窗的上方滑入。", + "notificationsBackground": "通知的背景色彩。通知從視窗的上方滑入。", + "notificationsButtonBackground": "通知按鈕的背景色彩。通知會從視窗上方滑入。", + "notificationsButtonHoverBackground": "游標停留時通知按鈕的背景色彩。通知會從螢幕上方滑入。", + "notificationsButtonForeground": "通知按鈕的前景色彩。通知會從螢幕上方滑入。", + "notificationsInfoBackground": "通知資訊的背景色彩。通知會從螢幕上方滑入。", + "notificationsInfoForeground": "通知資訊的前景色彩。通知會從螢幕上方滑入。", + "notificationsWarningBackground": "通知警告的背景色彩。通知會從螢幕上方滑入。", + "notificationsWarningForeground": "通知警告的前景色彩。通知會從螢幕上方滑入。", + "notificationsErrorBackground": "通知錯誤的背景色彩。通知會從螢幕上方滑入。", + "notificationsErrorForeground": "通知錯誤的前景色彩。通知會從螢幕上方滑入。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/cht/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..0c39510b40 --- /dev/null +++ b/i18n/cht/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "關閉編輯器", + "closeWindow": "關閉視窗", + "closeWorkspace": "關閉工作區", + "noWorkspaceOpened": "此執行個體中目前沒有開啟的工作區可以關閉。", + "newWindow": "開新視窗", + "toggleFullScreen": "切換全螢幕", + "toggleMenuBar": "切換功能表列", + "toggleDevTools": "切換開發人員工具", + "zoomIn": "放大", + "zoomOut": "縮小", + "zoomReset": "重設縮放", + "appPerf": "啟動效能", + "reloadWindow": "重新載入視窗", + "switchWindowPlaceHolder": "選取要切換的視窗", + "current": "目前視窗", + "close": "關閉視窗", + "switchWindow": "切換視窗...", + "quickSwitchWindow": "快速切換視窗...", + "workspaces": "工作區", + "files": "檔案", + "openRecentPlaceHolderMac": "選取以開啟 (按住 Cmd 鍵以在新視窗中開啟)", + "openRecentPlaceHolder": "選取以開啟 (按住 Ctrl 鍵以在新視窗中開啟)", + "remove": "從最近開啟的檔案中移除", + "openRecent": "開啟最近使用的檔案...", + "quickOpenRecent": "快速開啟最近使用的檔案...", + "closeMessages": "關閉通知訊息", + "reportIssues": "回報問題", + "reportPerformanceIssue": "回報效能問題", + "keybindingsReference": "鍵盤快速鍵參考", + "openDocumentationUrl": "文件", + "openIntroductoryVideosUrl": "簡介影片", + "openTipsAndTricksUrl": "秘訣與提示", + "toggleSharedProcess": "切換共用處理序", + "navigateLeft": "導覽至 [檢視左側]", + "navigateRight": "導覽至 [檢視右側]", + "navigateUp": "導覽至 [檢視上方]", + "navigateDown": "導覽至 [檢視下方]", + "increaseViewSize": "增加目前的檢視大小", + "decreaseViewSize": "縮小目前的檢視大小" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/cht/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/cht/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/electron-browser/crashReporter.i18n.json b/i18n/cht/src/vs/workbench/electron-browser/crashReporter.i18n.json new file mode 100644 index 0000000000..ba18e6d709 --- /dev/null +++ b/i18n/cht/src/vs/workbench/electron-browser/crashReporter.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "遙測", + "telemetry.enableCrashReporting": "允許將損毀報告傳送給 Microsoft。\n此選項需要重新啟動才會生效。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/cht/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..de01e687bf --- /dev/null +++ b/i18n/cht/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "延伸主機未於 10 秒內開始,可能在第一行就已停止,並需要偵錯工具才能繼續。", + "extensionHostProcess.startupFail": "延伸主機未在 10 秒內啟動,可能發生了問題。", + "extensionHostProcess.error": "延伸主機發生錯誤: {0}", + "devTools": "開發人員工具", + "extensionHostProcess.crash": "延伸主機意外終止。請重新載入視窗以復原。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/cht/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..2c804dddca --- /dev/null +++ b/i18n/cht/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "檢視", + "help": "說明", + "file": "檔案", + "workspaces": "工作區", + "developer": "開發人員", + "showEditorTabs": "控制已開啟的編輯器是否應顯示在索引標籤中。", + "editorTabCloseButton": "控制編輯器的索引標籤關閉按鈕位置,或在設為 'off' 時將其停用。", + "showIcons": "控制開啟的編輯器是否搭配圖示顯示。這需要同時啟用圖示佈景主題。", + "enablePreview": "控制已開啟的編輯器是否顯示為預覽。預覽編輯器會重複使用到被保留 (例如按兩下或進行編輯) 為止。", + "enablePreviewFromQuickOpen": "控制透過 Quick Open 所開啟的編輯器是否顯示為預覽。預覽編輯器會重複使用到被保留 (例如按兩下或進行編輯) 為止。", + "editorOpenPositioning": "控制要在何處開啟編輯器。選取 [左] 或 [右] 在目前使用中編輯器的左側或右側開啟編輯器。選取 [先] 或 [後] 從目前使用中編輯器另外開啟編輯器。", + "revealIfOpen": "控制編輯器是否要在任何顯示的群組開啟時,在其中顯示。若啟用此選項,已經開啟的編輯器將會繼續顯示,而不會在目前使用中的編輯器群組中再開啟一次。請注意,有一些情況會忽略此設定,例如當強制編輯器在特定群組中開啟,或強制編輯器在目前使用中的群組旁開啟等情況。", + "commandHistory": "控制是否要保留在命令選擇區歷程記錄中最近所使用的命令數目。設定為 0 以停用命令歷程記錄。", + "preserveInput": "控制下次開啟命令選擇區時,最後鍵入的輸入是否應該還原。", + "closeOnFocusLost": "控制是否在 Quick Open 失去焦點時自動關閉。", + "openDefaultSettings": "控制開啟設定時是否也會開啟顯示所有預設設定的編輯器。", + "sideBarLocation": "控制項資訊看板的位置。可顯示於 Workbench 的左方或右方。", + "statusBarVisibility": "控制 Workbench 底端狀態列的可視性。", + "activityBarVisibility": "控制活動列在 workbench 中的可見度。", + "closeOnFileDelete": "控制顯示檔案的編輯器是否應在其他處理序刪除或重新命名該檔案時自動關閉。若停用此選項,當發生前述狀況時,編輯器會保持開啟,並呈現已變更的狀態。請注意,從應用程式內刪除一律會關閉編輯器,但已變更的檔案在資料未儲存前一律不會關閉。", + "fontAliasing": "在 Workbench 中控制字型鋸齒化的方法。- 預設: 子像素字型平滑處理。在大部分非 Retina 顯示器上會顯示出最銳利的文字- 已消除鋸齒: 相對於子像素,根據像素層級平滑字型。可讓字型整體顯得較細- 無: 停用字型平滑處理。文字會以鋸齒狀的銳邊顯示 ", + "workbench.fontAliasing.default": "子像素字型平滑處理。在大部分非 Retina 顯示器上會顯示出最銳利的文字。", + "workbench.fontAliasing.antialiased": "相對於子像素,根據像素層級平滑字型。可以讓字型整體顯得較細。", + "workbench.fontAliasing.none": "禁用字體平滑.文字將會顯示鋸齒狀與鋒利的邊緣.", + "swipeToNavigate": "利用三指水平撥動在開啟的檔案間瀏覽。", + "workbenchConfigurationTitle": "工作台", + "window.openFilesInNewWindow.on": "檔案會在新視窗中開啟", + "window.openFilesInNewWindow.off": "檔案會在開啟了檔案資料夾的視窗,或在上一個使用中的視窗中開啟", + "window.openFilesInNewWindow.default": "除非從擴充座或 Finder 中開啟,否則檔案會在開啟了檔案資料夾的視窗,或在上一個使用中的視窗中開啟 (僅限 macOS)", + "openFilesInNewWindow": "控制檔案是否應在新視窗中開啟。\n- default: 檔案會在視窗中隨檔案的資料夾開啟或在上一個使用中視窗開啟,除非透過擴充座或從尋找工具開啟 (僅限 macOS)\n- on: 檔案會在新視窗開啟\n- off: 檔案會在視窗中隨檔案的資料夾開啟或在上一個使用中視窗開啟\n請注意,在某些情況下會略過此設定 (例如,使用了 -new-window 或 -reuse-window 命令列選項時)。", + "window.openFoldersInNewWindow.on": "資料夾會在新視窗中開啟", + "window.openFoldersInNewWindow.off": "資料夾會取代上一個使用中的視窗", + "window.openFoldersInNewWindow.default": "除非已從應用程式內挑選了資料夾 (例如透過 [檔案] 功能表),否則資料夾會在新視窗中開啟", + "openFoldersInNewWindow": "控制資料夾應在新視窗中開啟或取代上一個使用中的視窗。\n- default: 除非已從應用程式內挑選資料夾 (例如,透過 [檔案] 功能表),否則會在新視窗開啟\n- on: 資料夾會在新視窗開啟\n- off: 資料夾會取代上一個使用中視窗\n請注意,在某些情況下會略過此設定 (例如,使用了 -new-window 或 -reuse-window 命令列選項時)。", + "window.reopenFolders.all": "重新開啟所有視窗。", + "window.reopenFolders.folders": "重新開啟所有資料夾。空白工作區將不會還原。", + "window.reopenFolders.one": "重新開啟上一個使用中的視窗。", + "window.reopenFolders.none": "一律不重新開啟視窗。永遠從空白的視窗開始。", + "restoreWindows": "控制重新啟動後視窗重新開啟的方式。選取 [無] 一律以空白工作區開始、選取 [一] 從上一個編輯的視窗重新開啟、選取 [資料夾] 重新開啟所有資料夾曾經開啟的視窗,或選取 [全部] 重新開啟上一個工作階段的所有視窗。", + "restoreFullscreen": "控制當視窗在全螢幕模式下結束後,下次是否仍以全螢幕模式開啟。", + "zoomLevel": "調整視窗的縮放比例。原始大小為 0,而且每個向上增量 (例如 1) 或向下增量 (例如 -1) 代表放大或縮小 20%。您也可以輸入小數,更細微地調整縮放比例。", + "title": "依據使用中的編輯器控制視窗標題。變數會依據內容替換:\n${activeEditorShort}: 例如 myFile.txt\n${activeEditorMedium}: 例如 myFolder/myFile.txt\n${activeEditorLong}: 例如 /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: 例如 myFolder\n${folderPath}: 例如 /Users/Development/myFolder\n${rootName}: 例如 myFolder1,myFolder2,myFolder3\n${rootPath}: 例如 /Users/Development/myWorkspace\n${appName}: 例如 VS Code\n${dirty}: 用以指出編輯器經過變更的指標\n${separator}: 條件式分隔符號 (' - '),只會在前後為具有值的變數時顯示", + "window.newWindowDimensions.default": "在螢幕中央開啟新視窗。", + "window.newWindowDimensions.inherit": "以相同於上一個使用中之視窗的維度開啟新視窗。", + "window.newWindowDimensions.maximized": "開啟並最大化新視窗。", + "window.newWindowDimensions.fullscreen": "在全螢幕模式下開啟新視窗。", + "newWindowDimensions": "控制當至少一個視窗已打開的情況下開啟新視窗的維度。根據預設,新視窗會以小型維度在畫面中央開啟。設為 'inherit' 時,視窗的維度會和最後開啟的視窗相同。設為 'maximized' 時,視窗會開到最大,若設為 'fullscreen' 則全螢幕開啟。", + "closeWhenEmpty": "控制在關閉最後一個編輯器時,是否也應關閉視窗。此設定僅適用於未顯示資料夾的視窗。", + "window.menuBarVisibility.default": "只在全螢幕模式時隱藏功能表。", + "window.menuBarVisibility.visible": "一律顯示功能表,即使在全螢幕模式時亦然。", + "window.menuBarVisibility.toggle": "隱藏功能表,但可經由 Alt 鍵加以顯示。", + "window.menuBarVisibility.hidden": "一律隱藏功能表。", + "menuBarVisibility": "控制功能表列的可見度。[切換] 設定表示會隱藏功能表列,按一下 Alt 鍵則會顯示。除非視窗是全螢幕,否則預設會顯示功能表列。", + "enableMenuBarMnemonics": "啟用後可以利用Alt快捷鍵打開主選單.關閉記憶選單將Alt快捷鍵綁定至替代的命令區塊.", + "autoDetectHighContrast": "若啟用,如果 Windows 使用高對比佈景主題,就會自動變更為高對比佈景主題,切換掉 Windows 高對比佈景主題時則變更為深色佈景主題。", + "titleBarStyle": "調整視窗標題列的外觀。變更需要完整重新啟動才會套用。", + "window.nativeTabs": "啟用 macOS Sierra 視窗索引標籤。請注意需要完全重新啟動才能套用變更,並且完成設定後原始索引標籤將會停用自訂標題列樣式。", + "windowConfigurationTitle": "視窗", + "zenModeConfigurationTitle": "Zen Mode", + "zenMode.fullScreen": "控制開啟 Zen Mode 是否也會將 Workbench 轉換為全螢幕模式。", + "zenMode.hideTabs": "控制開啟 Zen Mode 是否也會隱藏 Workbench 索引標籤。", + "zenMode.hideStatusBar": "控制開啟 Zen Mode 是否也會隱藏 Workbench 底部的狀態列。", + "zenMode.hideActivityBar": "控制開啟 Zen Mode 是否也會隱藏 Workbench 左方的活動列。", + "zenMode.restore": "控制視窗如果在 Zen Mode 下結束,是否應還原為 Zen Mode。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/electron-browser/main.i18n.json b/i18n/cht/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..de7e8f0ff9 --- /dev/null +++ b/i18n/cht/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "無法載入需要的檔案。可能是您已經沒有連線到網際網路,或是您連接的伺服器已離線。請重新整理瀏覽器,再試一次。", + "loaderErrorNative": "無法載入必要的檔案。請重新啟動該應用程式,然後再試一次。詳細資料: {0}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/cht/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..068a0c8ad7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "建議不要以 'root' 身分執行 Code。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/electron-browser/window.i18n.json b/i18n/cht/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..3e4595bc3d --- /dev/null +++ b/i18n/cht/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "復原", + "redo": "重做", + "cut": "剪下", + "copy": "複製", + "paste": "貼上", + "selectAll": "全選" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/cht/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..fc825a0d38 --- /dev/null +++ b/i18n/cht/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "開發人員", + "file": "檔案" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/cht/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..1a269bae1b --- /dev/null +++ b/i18n/cht/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "路徑 {0} 並未指向有效的擴充功能測試執行器。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/cht/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..3b61969d5e --- /dev/null +++ b/i18n/cht/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "無法剖析 {0}: {1}。", + "fileReadFail": "無法讀取檔案 {0}: {1}。", + "jsonsParseFail": "無法剖析 {0} 或 {1}: {2}。", + "missingNLSKey": "找不到金鑰 {0} 的訊息。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..25521f2d92 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "在 PATH 中安裝 '{0}' 命令", + "not available": "此命令無法使用", + "successIn": "已成功在 PATH 中安裝殼層命令 '{0}'。", + "warnEscalation": "Code 現在會提示輸入 'osascript' 取得系統管理員權限,以便安裝殼層命令。", + "ok": "確定", + "cantCreateBinFolder": "無法建立 '/usr/local/bin'。", + "cancel2": "取消", + "aborted": "已中止", + "uninstall": "從 PATH 將 '{0}' 命令解除安裝 ", + "successFrom": "已成功從 PATH 解除安裝殼層命令 '{0}'。", + "shellCommand": "殼層命令" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..9f8f7da31d --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "現在請將設定 `editor.accessibilitySupport` 變更為 'on'。", + "openingDocs": "現在請開啟 VS Code 協助工具文件頁面。", + "introMsg": "感謝您試用 VS Code 的協助工具選項。", + "status": "狀態:", + "changeConfigToOnMac": "若要將編輯器為螢幕助讀程式的使用方式設定為永久地最佳化,現在請按 Command+E。", + "changeConfigToOnWinLinux": "若要將編輯器為螢幕助讀程式的使用方式設定為永久地最佳化,現在請按 Control+E。", + "auto_unknown": "編輯器已設定為使用平台 API 以偵測螢幕助讀程式附加,但是目前的執行階段不支援。", + "auto_on": "編輯器已自動偵測到螢幕助讀程式附加。", + "auto_off": "編輯器已設定為自動偵測螢幕助讀程式附加,但目前的實際狀況卻不是如此。", + "configuredOn": "編輯器已為螢幕助讀程式的使用方式設定為永久地更新 - 您可以藉由編輯設定 `editor.accessibilitySupport` 以變更這項設定。", + "configuredOff": "編輯器已設定為不會為螢幕助讀程式的使用方式進行最佳化。", + "tabFocusModeOnMsg": "在目前的編輯器中按 Tab 鍵會將焦點移至下一個可設定焦點的元素。按 {0} 可切換此行為。", + "tabFocusModeOnMsgNoKb": "在目前的編輯器中按 Tab 鍵會將焦點移至下一個可設定焦點的元素。命令 {0} 目前無法由按鍵繫結關係觸發。", + "tabFocusModeOffMsg": "在目前的編輯器中按 Tab 鍵會插入定位字元。按 {0} 可切換此行為。", + "tabFocusModeOffMsgNoKb": "在目前的編輯器中按 Tab 鍵會插入定位字元。命令 {0} 目前無法由按鍵繫結關係觸發。", + "openDocMac": "現在請按 Command+H 以開啟具有更多與協助工具相關 VS Code 資訊的瀏覽器視窗。", + "openDocWinLinux": "現在請按 Control+H 以開啟具有更多與協助工具相關 VS Code 資訊的瀏覽器視窗。", + "outroMsg": "您可以按 Esc 鍵或 Shift+Esc 鍵來解除此工具提示並返回編輯器。", + "ShowAccessibilityHelpAction": "顯示協助工具說明" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..099ab4a21e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "開發人員: 檢查按鍵對應" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f007088833 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "開發人員: 檢查 TM 範圍", + "inspectTMScopesWidget.loading": "正在載入..." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..42d9ce87d9 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "剖析 {0} 時發生錯誤: {1}", + "schema.openBracket": "左括弧字元或字串順序。", + "schema.closeBracket": "右括弧字元或字串順序。", + "schema.comments": "定義註解符號", + "schema.blockComments": "定義標記區塊註解的方式。", + "schema.blockComment.begin": "區塊註解開頭的字元順序。", + "schema.blockComment.end": "區塊註解結尾的字元順序。", + "schema.lineComment": "行註解開頭的字元順序。", + "schema.brackets": "定義增加或減少縮排的括弧符號。", + "schema.autoClosingPairs": "定義成對括弧。輸入左括弧時,即自動插入右括弧。", + "schema.autoClosingPairs.notIn": "定義停用自動配對的範圍清單。", + "schema.surroundingPairs": "定義可用以括住所選字串的成對括弧。", + "schema.wordPattern": "定義語言的文字", + "schema.wordPattern.pattern": "使用正規表示式進行文字比對", + "schema.wordPattern.flags": "使用正規表示式標記進行文字比對", + "schema.wordPattern.flags.errorMessage": "必須符合樣式 `/^([gimuy]+)$/`", + "schema.indentationRules": "語言的縮排設定。", + "schema.indentationRules.increaseIndentPattern": "若有符合此模式的行,則其後的所有行都應縮排一次,直到符合另一條規則為止。", + "schema.indentationRules.increaseIndentPattern.pattern": "適用於 increaseIndentPattern 的 RegExp 模式。", + "schema.indentationRules.increaseIndentPattern.flags": "適用於 increaseIndentPattern 的 RegExp 旗標。", + "schema.indentationRules.increaseIndentPattern.errorMessage": "必須符合樣式 `/^([gimuy]+)$/`", + "schema.indentationRules.decreaseIndentPattern": "若有符合此模式的行,則其後的所有行都應取消縮排一次,直到符合另一條規則為止。", + "schema.indentationRules.decreaseIndentPattern.pattern": "適用於 decreaseIndentPattern 的 RegExp 模式。", + "schema.indentationRules.decreaseIndentPattern.flags": "適用於 decreaseIndentPattern 的 RegExp 旗標。", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "必須符合樣式 `/^([gimuy]+)$/`", + "schema.indentationRules.indentNextLinePattern": "若有符合此模式的行,則**僅有下一行**應縮排一次,直到符合另一條規則為止。", + "schema.indentationRules.indentNextLinePattern.pattern": "適用於 indentNextLinePattern 的 RegExp 模式。", + "schema.indentationRules.indentNextLinePattern.flags": "適用於 indentNextLinePattern 的 RegExp 旗標。", + "schema.indentationRules.indentNextLinePattern.errorMessage": "必須符合樣式 `/^([gimuy]+)$/`", + "schema.indentationRules.unIndentedLinePattern": "若有符合此模式的行,則不應該變更其縮排,並且不使用其他規則比對。", + "schema.indentationRules.unIndentedLinePattern.pattern": "適用於 unIndentedLinePattern 的 RegExp 模式。", + "schema.indentationRules.unIndentedLinePattern.flags": "適用於 unIndentedLinePattern 的 RegExp 旗標。", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "必須符合樣式 `/^([gimuy]+)$/`" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f007088833 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "開發人員: 檢查 TM 範圍", + "inspectTMScopesWidget.loading": "正在載入..." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..cebdd4a935 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "檢視: 切換迷你地圖" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..96ec8d8368 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "切換至多游標修改程式" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..b4f8989748 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "檢視: 切換控制字元" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..51cd713dd1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "檢視: 切換轉譯空白字元" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..dea8e3424f --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "檢視: 切換自動換行", + "wordWrap.notInDiffEditor": "無法在不同的編輯器中切換文字換行。", + "unwrapMinified": "停用此檔案的換行", + "wrapMinified": "啟用此檔案的換行" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..d9edfc1ad1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "確定", + "wordWrapMigration.dontShowAgain": "不要再顯示", + "wordWrapMigration.openSettings": "開啟設定", + "wordWrapMigration.prompt": "設定 [editor.wrappingColumn] 已標示為即將淘汰,將改為 [editor.wordWrap]。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..f0ac473fbd --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "在運算式評估為 true 時中斷。按 'Enter' 鍵接受,按 'esc' 鍵取消。", + "breakpointWidgetAriaLabel": "程式只有在此條件為 true 時才會在此停止。請按 Enter 鍵接受,或按 Esc 鍵取消。", + "breakpointWidgetHitCountPlaceholder": "符合叫用次數條件時中斷。按 'Enter' 鍵接受,按 'esc' 鍵取消。", + "breakpointWidgetHitCountAriaLabel": "程式只會在符合叫用次數時於此處停止。按 Enter 鍵接受,或按 Escape 鍵取消。", + "expression": "運算式", + "hitCount": "叫用次數" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..d1931f0e70 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "沒有組態", + "addConfigTo": "新增組態 ({0})...", + "addConfiguration": "新增組態..." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..437c668cf6 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "開啟 {0}", + "launchJsonNeedsConfigurtion": "設定或修正 'launch.json'", + "noFolderDebugConfig": "請先打開一個資料夾以便設定進階偵錯組態。", + "startDebug": "開始偵錯", + "startWithoutDebugging": "開始但不偵錯", + "selectAndStartDebugging": "選取並開始偵錯", + "restartDebug": "重新啟動", + "reconnectDebug": "重新連接", + "stepOverDebug": "不進入函式", + "stepIntoDebug": "逐步執行", + "stepOutDebug": "跳離函式", + "stopDebug": "停止", + "disconnectDebug": "中斷連接", + "continueDebug": "繼續", + "pauseDebug": "暫停", + "restartFrame": "重新啟動框架", + "removeBreakpoint": "移除中斷點", + "removeAllBreakpoints": "移除所有中斷點", + "enableBreakpoint": "啟用中斷點", + "disableBreakpoint": "停用中斷點", + "enableAllBreakpoints": "啟用所有中斷點", + "disableAllBreakpoints": "停用所有中斷點", + "activateBreakpoints": "啟動中斷點", + "deactivateBreakpoints": "停用中斷點", + "reapplyAllBreakpoints": "重新套用所有中斷點", + "addFunctionBreakpoint": "加入函式中斷點", + "renameFunctionBreakpoint": "重新命名函式中斷點", + "addConditionalBreakpoint": "新增條件中斷點...", + "editConditionalBreakpoint": "編輯中斷點...", + "setValue": "設定值", + "addWatchExpression": "加入運算式", + "editWatchExpression": "編輯運算式", + "addToWatchExpressions": "加入監看", + "removeWatchExpression": "移除運算式", + "removeAllWatchExpressions": "移除所有運算式", + "clearRepl": "清除主控台", + "debugConsoleAction": "偵錯主控台", + "unreadOutput": "偵錯主控台中的新輸出", + "debugFocusConsole": "焦點偵錯主控台", + "focusProcess": "焦點處理序", + "stepBackDebug": "倒退", + "reverseContinue": "反向" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..96cca9eb48 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "偵錯工具列背景色彩。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..5c73fea6b0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "不使用偵錯工作階段無法解析此資源" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..85d10b9298 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "偵錯: 切換中斷點", + "columnBreakpointAction": "偵錯: 資料行中斷點", + "columnBreakpoint": "新增資料行中斷點", + "conditionalBreakpointEditorAction": "偵錯: 新增條件中斷點...", + "runToCursor": "執行至游標處", + "debugEvaluate": "偵錯: 評估", + "debugAddToWatch": "偵錯: 加入監看", + "showDebugHover": "偵錯: 動態顯示" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..a11e8a207f --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "停用的中斷點", + "breakpointUnverifieddHover": "未驗證的中斷點", + "breakpointDirtydHover": "未驗證的中斷點。檔案已修改,請重新啟動偵錯工作階段。", + "breakpointUnsupported": "此偵錯類型不支援的條件式中斷點" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..98bcaa9d88 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},偵錯", + "debugAriaLabel": "輸入要執行之啟動組態的名稱。", + "noConfigurationsMatching": "沒有任何相符的偵錯組態", + "noConfigurationsFound": "找不到任何偵錯組態。請建立'launch.json' 檔案。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..e81e4457a7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "例外狀況小工具的框線色彩。", + "debugExceptionWidgetBackground": "例外狀況小工具的背景色彩。", + "exceptionThrownWithId": "發生例外狀況: {0}", + "exceptionThrown": "發生了例外狀況" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..6f2a51fd67 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "按一下以追蹤 (Cmd + 按一下滑鼠左鍵開至側邊)", + "fileLink": "按一下以追蹤 (Ctrl + 按一下滑鼠左鍵開至側邊)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..66969aaaa1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "內部偵錯主控台的控制項行為。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..1be652c772 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "無法使用", + "startDebugFirst": "請啟動偵錯工作階段進行評估" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..fa63f66cad --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "未知的來源" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..e545814ddb --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "顯示偵錯", + "toggleDebugPanel": "偵錯主控台", + "debug": "偵錯", + "debugPanel": "偵錯主控台", + "variables": "變數", + "watch": "監看", + "callStack": "呼叫堆疊", + "breakpoints": "中斷點", + "view": "檢視", + "debugCategory": "偵錯", + "debugCommands": "偵錯組態", + "debugConfigurationTitle": "偵錯", + "allowBreakpointsEverywhere": "允許在任何檔案設定中斷點", + "openExplorerOnEnd": "自動於偵錯工作階段結束時開啟總管檢視", + "inlineValues": "在偵錯時於編輯器以內嵌方式顯示變數值", + "hideActionBar": "控制是否應隱藏浮點偵錯動作列", + "launch": "全域偵錯啟動組態。應當做在工作區之間共用的 'launch.json' 替代方案使用" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..bffb961942 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "請先打開一個資料夾以便設定進階偵錯組態。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..9a51ee179b --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "提供偵錯配接器。", + "vscode.extension.contributes.debuggers.type": "此偵錯配接器的唯一識別碼。", + "vscode.extension.contributes.debuggers.label": "此偵錯配接器的顯示名稱。", + "vscode.extension.contributes.debuggers.program": "偵錯配接器程式的路徑。可以是擴充功能資料夾的絕對或相對路徑。", + "vscode.extension.contributes.debuggers.args": "要傳遞至配接器的選擇性引數。", + "vscode.extension.contributes.debuggers.runtime": "程式屬性不是可執行檔但需要執行階段時的選擇性執行階段。", + "vscode.extension.contributes.debuggers.runtimeArgs": "選擇性執行階段引數。", + "vscode.extension.contributes.debuggers.variables": "從 `launch.json` 中的互動式變數 (例如 ${action.pickProcess}) 對應到命令。", + "vscode.extension.contributes.debuggers.initialConfigurations": "組態,用於產生初始 'launch.json'。", + "vscode.extension.contributes.debuggers.languages": "可將偵錯延伸模組視為「預設偵錯工具」的語言清單。", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "如有指定,VS Code 會呼叫此命令以決定偵錯配接器的可執行檔路徑及要傳遞的引數。", + "vscode.extension.contributes.debuggers.startSessionCommand": "如有指定,VS Code 會為以此延伸模組為目標的「偵錯」或「執行」動作呼叫此命令。", + "vscode.extension.contributes.debuggers.configurationSnippets": "用於在 'launch.json' 中新增組態的程式碼片段。", + "vscode.extension.contributes.debuggers.configurationAttributes": "JSON 結構描述組態,用於驗證 'launch.json'。", + "vscode.extension.contributes.debuggers.windows": "Windows 特定設定。", + "vscode.extension.contributes.debuggers.windows.runtime": "用於 Windows 的執行階段。", + "vscode.extension.contributes.debuggers.osx": "OS X 特定設定。", + "vscode.extension.contributes.debuggers.osx.runtime": "用於 OSX 的執行階段。", + "vscode.extension.contributes.debuggers.linux": "Linux 特定設定。", + "vscode.extension.contributes.debuggers.linux.runtime": "用於 Linux 的執行階段。", + "vscode.extension.contributes.breakpoints": "提供中斷點。", + "vscode.extension.contributes.breakpoints.language": "允許此語言使用中斷點。", + "app.launch.json.title": "啟動", + "app.launch.json.version": "此檔案格式的版本。", + "app.launch.json.configurations": "組態清單。請使用 IntelliSense 新增新的組態或編輯現有的組態。", + "app.launch.json.compounds": "複合的清單。每個複合都參考將會同時啟動的多重組態。", + "app.launch.json.compound.name": "複合的名稱。顯示於啟動組態下拉式功能表。", + "app.launch.json.compounds.configurations": "將會作為此複合一部份而啟動之組態的名稱。", + "debugNoType": "偵錯配接器 'type' 不能省略且必須屬於 'string' 類型。", + "selectDebug": "選取環境", + "DebugConfig.failed": "無法在 '.vscode' 資料夾 ({0}) 中建立 'launch.json' 檔案。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..a70c3b0238 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "移除中斷點", + "removeBreakpointOnColumn": "移除資料行 {0} 的中斷點", + "removeLineBreakpoint": "移除行中斷點", + "editBreakpoints": "編輯中斷點", + "editBreakpointOnColumn": "編輯資料行 {0} 的中斷點", + "editLineBrekapoint": "編輯行中斷點", + "enableDisableBreakpoints": "啟用/停用中斷點", + "disableColumnBreakpoint": "停用資料行 {0} 的中斷點", + "disableBreakpointOnLine": "停用行中斷點", + "enableBreakpoints": "啟用資料行 {0} 的中斷點", + "enableBreakpointOnLine": "啟用行中斷點", + "addBreakpoint": "加入中斷點", + "addConfiguration": "新增組態..." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..621b78b51b --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "偵錯暫留" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..f03973bf61 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "只會顯示此物件的基本值。", + "debuggingPaused": "偵錯已暫停,原因 {0},{1} {2}", + "debuggingStarted": "偵錯已開始。", + "debuggingStopped": "偵錯已停止。", + "breakpointAdded": "已新增中斷點,行 {0},檔案 {1}", + "breakpointRemoved": "已移除中斷點,行 {0},檔案 {1}", + "compoundMustHaveConfigurations": "複合必須設有 \"configurations\" 屬性,才能啟動多個組態。", + "configMissing": "'launch.json' 中遺漏組態 '{0}'。", + "debugTypeNotSupported": "不支援設定的偵錯類型 '{0}'。", + "debugTypeMissing": "遺漏所選啟動設定的屬性 'type'。", + "preLaunchTaskErrors": "執行 preLaunchTask '{0}' 期間偵測到建置錯誤。", + "preLaunchTaskError": "執行 preLaunchTask '{0}' 期間偵測到建置錯誤。", + "preLaunchTaskExitCode": "preLaunchTask '{0}' 已終止,結束代碼為 {1}。", + "debugAnyway": "仍要偵錯", + "noFolderWorkspaceDebugError": "無法對使用中的檔案偵錯。請確認檔案已儲存在磁碟上,而且您已經為該檔案類型安裝偵錯延伸模組。", + "NewLaunchConfig": "請為您的應用程式設定啟動組態檔。{0}", + "DebugTaskNotFound": "找不到 preLaunchTask '{0}'。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..363fa9124c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "處理序", + "paused": "已暫停", + "running": "正在執行", + "thread": "執行緒", + "pausedOn": "於 {0} 暫停", + "loadMoreStackFrames": "載入更多堆疊框架", + "threadAriaLabel": "執行緒 {0},呼叫堆疊,偵錯", + "stackFrameAriaLabel": "堆疊框架 {0} 第 {1} {2} 行,呼叫堆疊,偵錯", + "variableValueAriaLabel": "輸入新的變數值", + "variableScopeAriaLabel": "範圍 {0},變數,偵錯", + "variableAriaLabel": "{0} 值 {1},變數,偵錯", + "watchExpressionPlaceholder": "要監看的運算式", + "watchExpressionInputAriaLabel": "輸入監看運算式", + "watchExpressionAriaLabel": "{0} 值 {1},監看式,偵錯", + "watchVariableAriaLabel": "{0} 值 {1},監看式,偵錯", + "functionBreakpointPlaceholder": "要中斷的函式", + "functionBreakPointInputAriaLabel": "輸入函式中斷點", + "functionBreakpointsNotSupported": "此偵錯類型不支援函式中斷點", + "breakpointAriaLabel": "中斷點第 {0} {1} 行,中斷點,偵錯", + "functionBreakpointAriaLabel": "函式中斷點 {0},中斷點,偵錯", + "exceptionBreakpointAriaLabel": "例外狀況中斷點 {0},中斷點,偵錯" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..34b27dddd8 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "變數區段", + "variablesAriaTreeLabel": "偵錯變數", + "expressionsSection": "運算式區段", + "watchAriaTreeLabel": "對監看運算式執行偵錯", + "callstackSection": "呼叫堆疊區段", + "debugStopped": "於 {0} 暫停", + "callStackAriaLabel": "偵錯呼叫堆疊", + "breakpointsSection": "中斷點區段", + "breakpointsAriaTreeLabel": "偵錯中斷點" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..6b40657faf --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "複製值", + "copy": "複製", + "copyAll": "全部複製", + "copyStackTrace": "複製呼叫堆疊" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..6e68b5c519 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "詳細資訊", + "unableToLaunchDebugAdapter": "無法從 '{0}' 啟動偵錯配接器。", + "unableToLaunchDebugAdapterNoArgs": "無法啟動偵錯配接器。", + "stoppingDebugAdapter": "{0}。正在停止偵錯配接器。", + "debugAdapterCrash": "偵錯配接器處理序已意外終止" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..dd3659fc92 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "「讀取、求值、輸出」迴圈面板", + "actions.repl.historyPrevious": "上一筆記錄", + "actions.repl.historyNext": "下一筆記錄", + "actions.repl.acceptInput": "REPL 接受輸入", + "actions.repl.copyAll": "偵錯: 主控台全部複製" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..eb183a2e97 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "第一次評估會擷取物件狀態", + "replVariableAriaLabel": "變數 {0} 具有值 {1},「讀取、求值、輸出」迴圈,偵錯", + "replExpressionAriaLabel": "運算式 {0} 具有值 {1},「讀取、求值、輸出」迴圈,偵錯", + "replValueOutputAriaLabel": "{0},「讀取、求值、輸出」迴圈,偵錯", + "replKeyValueOutputAriaLabel": "輸出變數 {0} 具有值 {1},「讀取、求值、輸出」迴圈,偵錯" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..7f8c34583b --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "對程式執行偵錯時狀態列的背景色彩。狀態列會顯示在視窗的底部", + "statusBarDebuggingForeground": "對程式執行偵錯時狀態列的前景色彩。狀態列會顯示在視窗的底部", + "statusBarDebuggingBorder": "正在偵錯用以分隔資訊看板與編輯器的狀態列框線色彩。狀態列會顯示在視窗的底部。 " +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..d6c9331c96 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "偵錯項目", + "debug.terminal.not.available.error": "整合式終端機無法使用" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..0839b3f2f2 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "偵錯配接器可執行檔 '{0}' 不存在。", + "debugAdapterCannotDetermineExecutable": "無法判斷偵錯配接器 '{0}' 的可執行檔。", + "launch.config.comment1": "使用 IntelliSense 以得知可用的屬性。", + "launch.config.comment2": "暫留以檢視現有屬性的描述。", + "launch.config.comment3": "如需詳細資訊,請瀏覽: {0}", + "debugType": "組態的類型。", + "debugTypeNotRecognised": "無法辨識此偵錯類型.請確認已有安裝並啟用相對應的偵錯擴充功能.", + "node2NotSupported": "\"node2\" 已不再支援,請改用 \"node\",並將 \"protocol\" 屬性設為 \"inspector\"。", + "debugName": "組態的名稱; 出現在啟動組態下拉式功能表中。", + "debugRequest": "要求組態的類型。可以是 [啟動] 或 [附加]。", + "debugServer": "僅限偵錯延伸模組開發: 如果指定了連接埠,VS Code 會嘗試連線至以伺服器模式執行的偵錯配接器", + "debugPrelaunchTask": "偵錯工作階段啟動前要執行的工作。", + "debugWindowsConfiguration": "Windows 特定的啟動設定屬性。", + "debugOSXConfiguration": "OS X 特定的啟動設定屬性。", + "debugLinuxConfiguration": "Linux 特定的啟動設定屬性。", + "deprecatedVariables": "'env.'、'config.' 及 'command.' 已標示為即將淘汰,請改用 'env:'、'config:' 及 'command:'。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..1cade6b7e2 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "顯示 Emmet 命令" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..c2bd46534c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: 平衡 (向內)", + "balanceOutward": "Emmet: 平衡 (向外)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..eb3e5965ed --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: 前往上一個編輯端點", + "nextEditPoint": "Emmet: 前往下一個編輯端點" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..52956804d7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: 評估數學運算式" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..f3c6b1274d --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: 展開縮寫" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..08eb94a05d --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: 依 0.1 遞增", + "incrementNumberByOne": "Emmet: 依 1 遞增", + "incrementNumberByTen": "Emmet: 依 10 遞增", + "decrementNumberByOneTenth": "Emmet: 依 0.1 遞減", + "decrementNumberByOne": "Emmet: 依 1 遞減", + "decrementNumberByTen": "Emmet: 依 10 遞減" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..00f492c87a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: 前往相符的配對" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..0a39aec795 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: 合併行" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..2c70530e1b --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: 反射 CSS 值" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..056e44dfce --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: 移除標記" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..55f7d663fd --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: 選取上一個項目", + "selectNextItem": "Emmet: 選取下一個項目" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..211e75fcf2 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: 分割/聯結標記" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..cc14f8d899 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: 切換註解" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..dcfc4398e8 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: 更新影像大小" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..39dd4aa5ae --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: 更新標記", + "enterTag": "輸入標記", + "tag": "標記" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..95fd40bf3c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: 以縮寫包裝", + "enterAbbreviation": "輸入縮寫", + "abbreviation": "縮寫" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..9ac0987282 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "啟用時,按下 TAB 即可展開 Emmet 縮寫。當 emmet.useNewemmet 設定為 true 時不適用。", + "emmetPreferences": "用以修改部分 Emmet 動作及解析程式之行為的喜好設定。emmet.useNewemmet 設定為 ture 時不適用。", + "emmetSyntaxProfiles": "為指定的語法定義設定檔,或透過特定規則使用自己的設定檔。", + "emmetExclude": "不應展開 Emmet 縮寫的語言陣列。", + "emmetExtensionsPath": "包含 Emmet 設定檔、片段及喜好設定的資料夾路徑。emmet.useNewEmmet 設定為 true 時僅會從延伸模組路徑接受設定檔。", + "useNewEmmet": "試試所有 Emmet 功能的新 Emmet 模組 (最終會取代舊的單一 Emmet 程式庫)。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json new file mode 100644 index 0000000000..c2bd46534c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: 平衡 (向內)", + "balanceOutward": "Emmet: 平衡 (向外)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json new file mode 100644 index 0000000000..29fac9b1d0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: 上一個編輯端點", + "nextEditPoint": "Emmet: 下一個編輯端點" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..52956804d7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: 評估數學運算式" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..f3c6b1274d --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: 展開縮寫" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..08eb94a05d --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: 依 0.1 遞增", + "incrementNumberByOne": "Emmet: 依 1 遞增", + "incrementNumberByTen": "Emmet: 依 10 遞增", + "decrementNumberByOneTenth": "Emmet: 依 0.1 遞減", + "decrementNumberByOne": "Emmet: 依 1 遞減", + "decrementNumberByTen": "Emmet: 依 10 遞減" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..00f492c87a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: 前往相符的配對" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..0a39aec795 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: 合併行" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..2c70530e1b --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: 反射 CSS 值" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json new file mode 100644 index 0000000000..056e44dfce --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: 移除標記" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json new file mode 100644 index 0000000000..55f7d663fd --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: 選取上一個項目", + "selectNextItem": "Emmet: 選取下一個項目" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..211e75fcf2 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: 分割/聯結標記" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..cc14f8d899 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: 切換註解" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..dcfc4398e8 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: 更新影像大小" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json new file mode 100644 index 0000000000..39dd4aa5ae --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: 更新標記", + "enterTag": "輸入標記", + "tag": "標記" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..95fd40bf3c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: 以縮寫包裝", + "enterAbbreviation": "輸入縮寫", + "abbreviation": "縮寫" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json new file mode 100644 index 0000000000..910fd09613 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "如有啟用,只要按 Tab 鍵就能展開 Emmet 縮寫。", + "emmetPreferences": "喜好設定,用以修改某些動作的行為及 Emmet 的解析程式。", + "emmetSyntaxProfiles": "為指定的語法定義設定檔,或透過特定規則使用自己的設定檔。", + "emmetExclude": "不應展開 Emmet 縮寫的語言陣列。", + "emmetExtensionsPath": "包含 Emmet 設定檔、程式碼片段及參考的資料夾路徑" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..7b96176d32 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "外部終端機", + "explorer.openInTerminalKind": "自訂啟動的終端機類型。", + "terminal.external.windowsExec": "自訂要在 Windows 上執行的終端機。", + "terminal.external.osxExec": "自訂要在 OS X 上執行的終端機應用程式。", + "terminal.external.linuxExec": "自訂要在 Linux 上執行的終端機。", + "globalConsoleActionWin": "開啟新的命令提示字元", + "globalConsoleActionMacLinux": "開啟新的終端機", + "scopedConsoleActionWin": "在命令提示字元中開啟", + "scopedConsoleActionMacLinux": "在終端機中開啟", + "openFolderInIntegratedTerminal": "在終端機中開啟" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..1eceb775f7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "外部終端機", + "terminal.external.windowsExec": "自訂要在 Windows 上執行的終端機。", + "terminal.external.osxExec": "自訂要在 OS X 上執行的終端機應用程式。", + "terminal.external.linuxExec": "自訂要在 Linux 上執行的終端機。", + "globalConsoleActionWin": "開啟新的命令提示字元", + "globalConsoleActionMacLinux": "開啟新的終端機", + "scopedConsoleActionWin": "在命令提示字元中開啟", + "scopedConsoleActionMacLinux": "在終端機中開啟" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/cht/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..7c8e557213 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "VS Code 主控台", + "mac.terminal.script.failed": "指令碼 '{0}' 失敗,結束代碼為 {1}", + "mac.terminal.type.not.supported": "不支援 '{0}'", + "press.any.key": "請按任意鍵繼續...", + "linux.term.failed": "'{0}' 失敗,結束代碼為 {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..7206c34003 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "提供自訂檢視", + "vscode.extension.contributes.view.id": "用以識別透過 vscode.workspace.createTreeView 建立之檢視的唯一識別碼", + "vscode.extension.contributes.view.label": "用以轉譯檢視的易讀字串", + "vscode.extension.contributes.view.icon": "連到檢視圖示的路徑", + "vscode.extension.contributes.views": "提供自訂檢視", + "showViewlet": "顯示 {0}", + "view": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..1f123e3bf7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "重新整理" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..f55e107320 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "未註冊識別碼為 {providerId} 的 TreeExplorerNodeProvider。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/cht/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..34b446c45e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "Tree Explorer 區段" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..150afdf64a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "錯誤", + "Unknown Dependency": "未知的相依性:" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..fa360a5a74 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "延伸模組名稱", + "extension id": "延伸模組識別碼", + "publisher": "發行者名稱", + "install count": "安裝計數", + "rating": "評等", + "license": "授權", + "details": "詳細資料", + "contributions": "貢獻", + "changelog": "變更記錄", + "dependencies": "相依性", + "noReadme": "沒有可用的讀我檔案。", + "noChangelog": "沒有可用的 Changelog。", + "noContributions": "沒有比重", + "noDependencies": "沒有相依性", + "settings": "設定 ({0})", + "setting name": "名稱", + "description": "描述", + "default": "預設", + "debuggers": "偵錯工具 ({0})", + "debugger name": "名稱", + "debugger type": "型別", + "views": "瀏覽次數 ({0})", + "view id": "識別碼", + "view name": "名稱", + "view location": "位置", + "themes": "佈景主題 ({0})", + "JSON Validation": "JSON 驗證 ({0})", + "commands": "命令 ({0})", + "command name": "名稱", + "keyboard shortcuts": "鍵盤快速鍵(&&K)", + "menuContexts": "功能表內容", + "languages": "語言 ({0})", + "language id": "識別碼", + "language name": "名稱", + "file extensions": "副檔名", + "grammar": "文法", + "snippets": "程式碼片段" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..8e2d12ad5e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "安裝", + "installing": "安裝中", + "uninstallAction": "解除安裝", + "Uninstalling": "正在解除安裝", + "updateAction": "更新", + "updateTo": "更新至 {0}", + "enableForWorkspaceAction.label": "啟用 (工作區)", + "enableAlwaysAction.label": "啟用 (永遠)", + "disableForWorkspaceAction.label": "停用 (工作區)", + "disableAlwaysAction.label": "停用 (永遠)", + "ManageExtensionAction.uninstallingTooltip": "正在解除安裝", + "enableForWorkspaceAction": "工作區", + "enableGloballyAction": "永遠", + "enableAction": "啟用", + "disableForWorkspaceAction": "工作區", + "disableGloballyAction": "永遠", + "disableAction": "停用", + "checkForUpdates": "查看是否有更新", + "enableAutoUpdate": "啟用自動更新延伸模組", + "disableAutoUpdate": "停用自動更新延伸模組", + "updateAll": "更新所有延伸模組", + "reloadAction": "重新載入", + "postUpdateTooltip": "重新載入以更新", + "postUpdateMessage": "要重新載入此視窗以啟動已更新的延伸模組 '{0}' 嗎?", + "postEnableTooltip": "重新載入以啟動", + "postEnableMessage": "要重新載入此視窗以啟動延伸模組 '{0}' 嗎?", + "postDisableTooltip": "重新載入以停用", + "postDisableMessage": "要重新載入此視窗以停用延伸模組 '{0}' 嗎?", + "postUninstallTooltip": "重新載入以停用", + "postUninstallMessage": "要重新載入此視窗以停用已解除安裝的延伸模組 '{0}' 嗎?", + "reload": "重新載入視窗(&&R)", + "toggleExtensionsViewlet": "顯示擴充功能", + "installExtensions": "安裝擴充功能", + "showEnabledExtensions": "顯示啟用的延伸模組", + "showInstalledExtensions": "顯示安裝的擴充功能", + "showDisabledExtensions": "顯示停用的延伸模組", + "clearExtensionsInput": "清除擴充功能輸入", + "showOutdatedExtensions": "顯示過期的擴充功能", + "showPopularExtensions": "顯示熱門擴充功能", + "showRecommendedExtensions": "顯示建議的擴充功能", + "showWorkspaceRecommendedExtensions": "顯示工作區的建議擴充功能", + "showRecommendedKeymapExtensions": "顯示建議的按鍵對應", + "showRecommendedKeymapExtensionsShort": "按鍵對應", + "showLanguageExtensions": "顯示語言擴充功能", + "showLanguageExtensionsShort": "語言擴充功能", + "showAzureExtensions": "顯示 Azure 延伸模組", + "showAzureExtensionsShort": "Azure 延伸模組", + "configureWorkspaceRecommendedExtensions": "設定建議的延伸模組 (工作區)", + "ConfigureWorkspaceRecommendations.noWorkspace": "只有在工作區資料夾中才能使用建議。", + "OpenExtensionsFile.failed": "無法在 '.vscode' 資料夾 ({0}) 中建立 'extensions.json' 檔案。", + "builtin": "內建", + "disableAll": "停用所有已安裝的延伸模組", + "disableAllWorkspace": "停用此工作區的所有已安裝延伸模組", + "enableAll": "啟用所有已安裝的延伸模組", + "enableAllWorkspace": "啟用此工作區的所有已安裝延伸模組", + "extensionButtonProminentBackground": "突出的動作延伸模組按鈕背景色彩 (例如,[安裝] 按鈕)。", + "extensionButtonProminentForeground": "突出的動作延伸模組按鈕前景色彩 (例如,[安裝] 按鈕)。", + "extensionButtonProminentHoverBackground": "突出的動作延伸模組按鈕背景暫留色彩 (例如,[安裝] 按鈕)。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..6976b08c77 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "按 Enter 即可管理您的擴充功能。", + "searchFor": "按 Enter 即可在 Marketplace 中搜尋 '{0}'。", + "noExtensionsToInstall": "輸入擴充功能名稱" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..e4aa773a15 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "擴充功能", + "app.extensions.json.recommendations": "擴充功能的建議清單。擴充功能的識別碼一律為 '${publisher}.${name}'。例如: 'vscode.csharp'。", + "app.extension.identifier.errorMessage": "格式應為 '${publisher}.${name}'。範例: 'vscode.csharp'。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..081398a3d2 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "擴充功能: {0}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..c81028d71d --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "建議對此檔案類型使用 '{0}' 延伸模組。", + "showRecommendations": "顯示建議", + "neverShowAgain": "不要再顯示", + "close": "關閉", + "workspaceRecommended": "此工作區具有擴充功能建議。", + "ignoreExtensionRecommendations": "是否略過所有建議的擴充功能?", + "ignoreAll": "是,略過全部", + "no": "否", + "cancel": "取消" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..fa4100b24b --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "管理擴充功能", + "galleryExtensionsCommands": "安裝資源庫擴充功能", + "extension": "擴充功能", + "extensions": "擴充功能", + "view": "檢視", + "extensionsConfigurationTitle": "擴充功能", + "extensionsAutoUpdate": "自動更新擴充功能", + "extensionsIgnoreRecommendations": "忽略延伸模組建議" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..f2abe6c062 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "開啟延伸模組資料夾", + "installVSIX": "從 VSIX 安裝...", + "InstallVSIXAction.success": "已成功安裝延伸模組。請重新啟動以啟用。", + "InstallVSIXAction.reloadNow": "立即重新載入" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..e45d487e4c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "要停用其他按鍵對應 ({0}),以避免按鍵繫結關係間的衝突嗎?", + "yes": "是", + "no": "否", + "betterMergeDisabled": "目前已內建 Better Merge 延伸模組,安裝的延伸模組已停用並且可以移除。", + "uninstall": "解除安裝", + "later": "稍後" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..31f8fbdb76 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "市集", + "installedExtensions": "已安裝", + "searchInstalledExtensions": "已安裝", + "recommendedExtensions": " 推薦項目", + "searchExtensions": "在 Marketplace 中搜尋擴充功能", + "sort by installs": "排序依據: 安裝計數", + "sort by rating": "排序依據: 評等", + "sort by name": "排序依據: 名稱", + "suggestProxyError": "Marketplace 傳回 'ECONNREFUSED'。請檢查 'http.proxy' 設定。", + "extensions": "擴充功能", + "outdatedExtensions": "{0} 過期的擴充功能" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..0c2079d94d --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "延伸模組", + "no extensions found": "找不到延伸模組。", + "suggestProxyError": "Marketplace 傳回 'ECONNREFUSED'。請檢查 'http.proxy' 設定。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json new file mode 100644 index 0000000000..b516b5efd7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "要停用其他按鍵對應,以避免按鍵繫結關係發生衝突嗎?", + "yes": "是", + "no": "否" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..970a3642f7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "啟用 '{0}' 也會啟用其相依性。仍要繼續嗎?", + "enable": "是", + "doNotEnable": "否", + "disableDependeciesConfirmation": "只要停用 '{0}',還是一併停用其相依性?", + "disableOnly": "只有", + "disableAll": "全部", + "cancel": "取消", + "singleDependentError": "無法停用延伸模組 '{0}'。'{1}' 延伸模組相依於此項。", + "twoDependentsError": "無法停用延伸模組 '{0}'。'{1}' 及 '{2}' 延伸模組相依於此項。", + "multipleDependentsError": "無法停用延伸模組 '{0}'。'{1}'、'{2}' 及其他延伸模組相依於此項。", + "installConfirmation": "您要安裝 '{0}' 延伸模組嗎?", + "install": "安裝" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/cht/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..49f728ec76 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "推文意見反應", + "label.sendASmile": "請將您的意見反應推文提供給我們。", + "patchedVersion1": "您的安裝已損毀。", + "patchedVersion2": "如果您要提交 Bug,請指定此項。", + "sentiment": "您的使用經驗如何?", + "smileCaption": "快樂", + "frownCaption": "傷心", + "other ways to contact us": "其他與我們連絡的方式", + "submit a bug": "提交 Bug", + "request a missing feature": "要求遺漏的功能", + "tell us why?": "請告訴我們原因", + "commentsHeader": "註解", + "tweet": "推文", + "character left": "剩餘字元", + "characters left": "剩餘字元", + "feedbackSending": "正在傳送", + "feedbackSent": "謝謝", + "feedbackSendingError": "請再試一次" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..0e93649d08 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "二進位檔案檢視器" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..07f00fac4d --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "文字檔編輯器", + "createFile": "建立檔案", + "fileEditorWithInputAriaLabel": "{0}。文字檔編輯器。", + "fileEditorAriaLabel": "文字檔編輯器。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..01979987ea --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "資料夾" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..84134f4f2a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "檔案", + "revealInSideBar": "在提要欄位中顯示", + "acceptLocalChanges": "使用您的變更並覆寫磁碟內容 ", + "revertLocalChanges": "捨棄您的變更並還原成磁碟上的內容" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..7586305f4e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "重試", + "rename": "重新命名", + "newFile": "新增檔案", + "newFolder": "新增資料夾", + "openFolderFirst": "先開啟資料夾,以在其中建立檔案或資料夾。", + "newUntitledFile": "新增未命名檔案", + "createNewFile": "新增檔案", + "createNewFolder": "新增資料夾", + "deleteButtonLabelRecycleBin": "移至資源回收筒(&&M)", + "deleteButtonLabelTrash": "移至垃圾筒(&&M)", + "deleteButtonLabel": "刪除(&&D)", + "dirtyMessageFolderOneDelete": "您要刪除的資料夾中 1 個檔案有未儲存的變更。要繼續嗎?", + "dirtyMessageFolderDelete": "您要刪除的資料夾中 {0} 個檔案有未儲存的變更。要繼續嗎?", + "dirtyMessageFileDelete": "您要刪除的檔案有未儲存的變更。要繼續嗎?", + "dirtyWarning": "如果您不儲存變更,這些變更將會遺失。", + "confirmMoveTrashMessageFolder": "您確定要刪除 '{0}' 及其內容嗎?", + "confirmMoveTrashMessageFile": "您確定要刪除 '{0}' 嗎?", + "undoBin": "您可以從資源回收筒還原。", + "undoTrash": "您可以從垃圾筒還原。", + "confirmDeleteMessageFolder": "您確定要永久刪除 '{0}' 和其中的內容嗎?", + "confirmDeleteMessageFile": "您確定要永久刪除 '{0}' 嗎?", + "irreversible": "此動作無法回復!", + "permDelete": "永久刪除", + "delete": "刪除", + "importFiles": "匯入檔案", + "confirmOverwrite": "目的資料夾中已有同名的檔案或資料夾。要取代它嗎?", + "replaceButtonLabel": "取代(&&R)", + "copyFile": "複製", + "pasteFile": "貼上", + "duplicateFile": "複製", + "openToSide": "開至側邊", + "compareSource": "選取用以比較", + "globalCompareFile": "使用中檔案的比較對象...", + "pickHistory": "選取先前開啟的檔案以相比較", + "unableToFileToCompare": "選取的檔案無法與 '{0}' 進行比較。", + "openFileToCompare": "先開啟檔案以與其他檔案進行比較", + "compareWith": "比較 '{0}' 與 '{1}'", + "compareFiles": "比較檔案", + "refresh": "重新整理", + "save": "儲存", + "saveAs": "另存新檔...", + "saveAll": "全部儲存", + "saveAllInGroup": "全部儲存在群組中", + "saveFiles": "儲存已變更的檔案", + "revert": "還原檔案", + "focusOpenEditors": "聚焦在 [開放式編輯器] 檢視", + "focusFilesExplorer": "將焦點設在檔案總管上", + "showInExplorer": "在提要欄位中顯示使用中的檔案", + "openFileToShow": "先開啟檔案,以在總管中加以顯示", + "collapseExplorerFolders": "摺疊 Explorer 中的資料夾", + "refreshExplorer": "重新整理 Explorer", + "openFile": "開啟檔案...", + "openFileInNewWindow": "在新視窗中開啟使用中的檔案", + "openFileToShowInNewWindow": "先開啟檔案以在新視窗中開啟", + "revealInWindows": "在檔案總管中顯示", + "revealInMac": "在 Finder 中顯示", + "openContainer": "開啟收納資料夾", + "revealActiveFileInWindows": "在 Windows 檔案總管中顯示使用中的檔案", + "revealActiveFileInMac": "在 Finder 中顯示使用中的檔案", + "openActiveFileContainer": "開啟使用中檔案的收納資料夾", + "copyPath": "複製路徑", + "copyPathOfActive": "複製使用中檔案的路徑", + "emptyFileNameError": "必須提供檔案或資料夾名稱。", + "fileNameExistsError": "這個位置已存在檔案或資料夾 **{0}**。請選擇不同的名稱。", + "invalidFileNameError": "名稱 **{0}** 不能作為檔案或資料夾名稱。請選擇不同的名稱。", + "filePathTooLongError": "名稱 **{0}** 導致路徑太長。請選擇較短的名稱。", + "compareWithSaved": "比較使用中的檔案和已儲存的檔案", + "modifiedLabel": "{0} (在磁碟上) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..e91383ddd3 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "先開啟檔案以複製其路徑", + "openFileToReveal": "先開啟檔案以顯示" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..f968af7b79 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "顯示檔案總管", + "explore": "檔案總管", + "view": "檢視", + "textFileEditor": "文字檔編輯器", + "binaryFileEditor": "二進位檔案編輯器", + "filesConfigurationTitle": "檔案", + "exclude": "設定 Glob 模式可包含檔案及資料夾。", + "files.exclude.boolean": "要符合檔案路徑的 Glob 模式。設為 True 或 False 可啟用或停用模式。", + "files.exclude.when": "在相符檔案同層級上額外的檢查。請使用 $(basename) 作為相符檔案名稱的變數。", + "associations": "將檔案關聯設定為語言 (例如 \"*.extension\": \"html\")。這些語言優先於已安裝語言的預設關聯。", + "encoding": "讀取與寫入檔案時要使用的預設字元集編碼。", + "autoGuessEncoding": "如有啟用,將會在開啟檔案時,嘗試猜測字元集編碼", + "eol": "預設結尾斷行字元.LF使用 \\n , CRLF使用\\r\\n ", + "trimTrailingWhitespace": "若啟用,將在儲存檔案時修剪尾端空白。", + "insertFinalNewline": "啟用時,請在儲存檔案時在其結尾插入最後一個新行。", + "files.autoSave.off": "已變更的檔案一律不會自動儲存。", + "files.autoSave.afterDelay": "已變更的檔案會在設定的 'files.autoSaveDelay' 之後自動儲存。", + "files.autoSave.onFocusChange": "已變更的檔案會在編輯器失去焦點時自動儲存。", + "files.autoSave.onWindowChange": "已變更的檔案會在視窗失去焦點時自動儲存。", + "autoSave": "控制已變更之檔案的自動儲存。接受的值: '{0}'、'{1}、'{2}' (編輯器失去焦點)、'{3}' (視窗失去焦點)。若設為 '{4}',可以在 \"files.autoSaveDelay\" 中設定延遲。", + "autoSaveDelay": "控制要自動儲存已變更之檔案前必須經過的延遲時間 (毫秒)。僅當 'files.autoSave' 設為 \"{0}\" 時才適用。", + "watcherExclude": "設定檔案路徑的 Glob 模式已將其自檔案監看排除。模式必須符合絕對路徑 (例如使用 ** 或完整路徑前置詞以正確相符)。必須先重新開機才能變更這項設定。若是發生 Code 在啟動時取用大量 CPU 時間的情況,可以排除較大的資料夾以降低起始負載。", + "hotExit.off": "停用 Hot Exit。", + "hotExit.onExit": "Hot Exit 將會在關閉應用程式時觸發,也就是在 Windows/Linux 上關閉上一個視窗,或是觸發 workbench.action.quit 命令 (命令選擇區、按鍵繫結關係、功能表) 時觸發。具有備份的所有視窗都會在下次啟動時還原。", + "hotExit.onExitAndWindowClose": "當應用程式關閉時會觸發 Hot Exit,也就是說,當在 Windows/Linux 上關閉最後一個視窗,或是觸發 workbench.action.quit 命令 (命令選擇區、按鍵繫結關係及功能表) 時會觸發,同時也針對所有開啟資料夾的視窗,不論其是否為最後一個視窗也會觸發。下次啟動時會還原所有未開啟資料夾的視窗。若要將資料夾視窗還原到關機前的狀態,請將 \"window.restoreWindows\" 設定為 \"all\"。", + "hotExit": "控制是否讓不同工作階段記住未儲存的檔案,並允許在結束編輯器時跳過儲存提示。", + "useExperimentalFileWatcher": "使用新的實驗性檔案監看員。", + "defaultLanguage": "指派給新檔案的預設語言模式。", + "editorConfigurationTitle": "編輯器", + "formatOnSave": "在儲存時設定檔案格式。格式器必須處於可用狀態、檔案不得自動儲存,且編輯器不得關機。", + "explorerConfigurationTitle": "檔案總管", + "openEditorsVisible": "[開放式編輯器] 窗格中顯示的編輯器數目。將其設定為 0 以隱藏窗格。", + "dynamicHeight": "控制 [開放式編輯器] 區段的高度是否應依元素數目動態調整。", + "autoReveal": "控制總管是否在開啟檔案時自動加以顯示及選取。", + "enableDragAndDrop": "控制總管是否應該允許透過拖放功能移動檔案和資料夾。", + "sortOrder.default": "檔案與資料夾會依照名稱以字母順序排序。資料夾會顯示在檔案前。", + "sortOrder.mixed": "檔案與資料夾會依照名稱以字母順序排序。檔案與資料夾會交錯排列。", + "sortOrder.filesFirst": "檔案與資料夾會依照名稱以字母順序排序。檔案會顯示在資料夾前。", + "sortOrder.type": "檔案與資料夾會依照延伸模組以字母順序排序。資料夾會顯示在檔案前。", + "sortOrder.modified": "檔案與資料夾會依照最後修改日期以字母順序排序。資料夾會顯示在檔案前。", + "sortOrder": "控制檔案與資料夾在總管中的排列順序。除了預設排序外,您也可以將順序設定為 'mixed' (檔案與資料夾)、'type' (依檔案類型)、'modified' (依最後修改日期) 或 'filesFirst' (將檔案排序在資料夾前)。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..a36462cb17 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "捨棄", + "overwrite": "覆寫", + "retry": "重試", + "readonlySaveError": "無法儲存 '{0}': 檔案有防寫保護。請選取 [覆寫] 以移除保護。", + "genericSaveError": "無法儲存 '{0}': {1}", + "staleSaveError": "無法儲存 '{0}': 磁碟上的內容較新。請按一下 [比較],比較您的版本與磁碟上的版本。", + "compareChanges": "比較", + "saveConflictDiffLabel": "{0} (位於磁碟) ↔ {1} (在 {2} 中) - 解決儲存衝突", + "userGuide": "在右方使用編輯器工具列中的動作來 **復原** 您的變更,或以您的變更 **覆寫** 磁碟上的內容" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..f49c2e4cfc --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "沒有開啟的資料夾", + "explorerSection": "檔案總管區段", + "noWorkspaceHelp": "您尚未開啟資料夾。", + "openFolder": "開啟資料夾" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..a92a48a0b0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "檔案總管區段", + "treeAriaLabel": "檔案總管" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..b61b5139fb --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "輸入檔案名稱。請按 Enter 鍵確認或按 Esc 鍵取消。", + "filesExplorerViewerAriaLabel": "{0},檔案總管", + "dropFolders": "要在工作區新增資料夾嗎?", + "dropFolder": "要在工作區新增資料夾嗎?", + "addFolders": "新增資料夾(&A)", + "addFolder": "新增資料夾(&A)", + "confirmOverwriteMessage": "目的資料夾中已存在 '{0}'。要取代它嗎?", + "irreversible": "此動作無法回復!", + "replaceButtonLabel": "取代(&&R)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..ed48a48651 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "已開啟的編輯器", + "openEditosrSection": "開放式編輯器區段", + "treeAriaLabel": "開啟的編輯器: 使用中檔案的清單", + "dirtyCounter": "{0} 未儲存" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..c210925c0f --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0},編輯器群組", + "openEditorAriaLabel": "{0},開啟編輯器", + "saveAll": "全部儲存", + "closeAllUnmodified": "關閉未變更的檔案", + "closeAll": "全部關閉", + "compareWithSaved": "與已儲存的檔案比較", + "close": "關閉", + "closeOthers": "關閉其他" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/cht/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..d27356929a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0} 個未儲存的檔案" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/cht/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..32c2e3e166 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (deleted from disk)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json new file mode 100644 index 0000000000..7dddeeb009 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "switchToChangesView": "切換至變更檢視", + "openInEditor": "切換至編輯器檢視", + "workbenchStage": "分段", + "workbenchUnstage": "取消分段", + "stageSelectedLines": "將選取的行分段", + "unstageSelectedLines": "取消分段選取的資料行", + "revertSelectedLines": "還原選取的行", + "confirmRevertMessage": "確定要還原選取的變更嗎?", + "irreversible": "此動作無法回復!", + "revertChangesLabel": "還原變更(&&R)", + "openChange": "開啟變更", + "openFile": "開啟檔案", + "git": "Git" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/gitActions.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/gitActions.i18n.json new file mode 100644 index 0000000000..506ec5bcf9 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/gitActions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openChange": "開啟變更", + "openFile": "開啟檔案", + "init": "Init", + "refresh": "重新整理", + "stageChanges": "分段", + "stageAllChanges": "全部分段", + "confirmUndoMessage": "確定要清除所有變更嗎?", + "confirmUndoAllOne": "{0} 個檔案中有取消暫存的變更。\n\n此動作無法回復!", + "confirmUndoAllMultiple": "{0} 個檔案中有取消暫存的變更。\n\n此動作無法回復!", + "cleanChangesLabel": "清除變更(&&C)", + "confirmUndo": "確定要清除 '{0}' 中的變更嗎?", + "irreversible": "此動作無法回復!", + "undoChanges": "清除", + "undoAllChanges": "全部清除", + "unstage": "取消分段", + "unstageAllChanges": "全部取消分段", + "dirtyTreeCheckout": "無法簽出。請先認可或隱藏您的工作。", + "commitStaged": "認可暫存", + "commitStagedAmend": "認可暫存 (修改)", + "commitStagedSignedOff": "認可暫存 (已登出)", + "commit": "Commit", + "commitMessage": "認可訊息", + "commitAll": "全部認可", + "commitAllSignedOff": "全部認可 (已登出)", + "commitAll2": "全部認可", + "commitStaged2": "認可暫存", + "dirtyTreePull": "無法提取。請先認可或隱藏您的工作。", + "authFailed": "Git 遠端的驗證失敗。", + "pushToRemote": "推送至...", + "pushToRemotePickMessage": "挑選遠端作為分支 '{0}' 的發送目標:", + "publish": "發行", + "confirmPublishMessage": "確定要將 '{0}' 發行至 '{1}' 嗎?", + "confirmPublishMessageButton": "發行(&&P)", + "publishPickMessage": "挑選要發行分支 '{0}' 的目標遠端:", + "sync is unpredictable": "此動作會將認可發送至 '{0}' 及從中提取認可。", + "ok": "確定", + "cancel": "取消", + "never again": "確定,不要再顯示", + "undoLastCommit": "復原上次認可" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json new file mode 100644 index 0000000000..841df9e9ab --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refAriaLabel": "{0},Git", + "checkoutBranch": "位於 {0} 的分支", + "checkoutRemoteBranch": "位於 {0} 的遠端分支", + "checkoutTag": "位於 {0} 的標記", + "alreadyCheckedOut": "分支 {0} 已是目前的分支", + "branchAriaLabel": "{0},Git 分支", + "createBranch": "建立分支 {0}", + "noBranches": "沒有其他分支", + "notValidBranchName": "請提供有效的分支名稱" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/gitServices.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/gitServices.i18n.json new file mode 100644 index 0000000000..1265377405 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/gitServices.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cantOpen": "無法開啟這個 Git 資源。", + "gitIndexChanges": "{0} (index) ↔ {1}", + "gitIndexChangesDesc": "{0} - 索引上的變更", + "gitIndexChangesRenamed": "{0} ← {1}", + "gitIndexChangesRenamedDesc": "{0} - 已重新命名 - 索引上的變更", + "workingTreeChanges": "{0} (HEAD) ↔ {1}", + "workingTreeChangesDesc": "{0} - 工作樹狀上的變更", + "gitMergeChanges": "{0} (merge) ↔ {1}", + "gitMergeChangesDesc": "{0} - 合併變更", + "updateGit": "您似乎已經安裝 GIT {0}。Code 搭配 GIT >=2.0.0 的成效最佳。", + "download": "下載", + "neverShowAgain": "不要再顯示", + "configureUsernameEmail": "請設定您的 Git 使用者名稱及電子郵件。", + "badConfigFile": "Git {0}", + "unmergedChanges": "您在認可變更前應先解決未合併的變更。", + "showOutput": "顯示輸出", + "cancel": "取消", + "checkNativeConsole": "執行 Git 作業時發生問題。請檢閱輸出或使用主控台來查看儲存機制的狀態。", + "changesFromIndex": "{0} (index)", + "changesFromIndexDesc": "{0} - 索引上的變更", + "changesFromTree": "{0} ({1})", + "changesFromTreeDesc": "{0} - {1} 上的變更", + "cantOpenResource": "無法開啟這個 Git 資源。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json new file mode 100644 index 0000000000..ff1a807b86 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "publishBranch": "發行分支", + "syncBranch": "同步處理變更", + "gitNotEnabled": "此工作區中未啟用 Git。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json new file mode 100644 index 0000000000..ca56b1ed68 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gitProgressBadge": "正在執行 Git 狀態", + "gitPendingChangesBadge": "{0} 個暫止的變更", + "toggleGitViewlet": "顯示 Git", + "git": "Git", + "view": "檢視", + "gitCommands": "Git 命令", + "gitConfigurationTitle": "Git", + "gitEnabled": "已啟用 Git", + "gitPath": "Git 可執行檔的路徑", + "gitAutoRefresh": "是否啟用自動重新整理", + "gitAutoFetch": "是否啟用自動擷取。", + "gitLongCommit": "是否對長認可訊息發出警告。", + "gitLargeRepos": "一律允許 Code 管理大型儲存機制。", + "confirmSync": "請先確認再同步處理 GIT 存放庫。", + "countBadge": "控制 GIT 徽章計數器。", + "checkoutType": "控制要列出哪種類型的分支。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json new file mode 100644 index 0000000000..8679d22bc1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "needMessage": "請提供認可訊息。您可以隨時按下 **[{0}]** 來認可變更。如有任何已分段的變更,將只有那些分段的變更會獲得認可,否則,所有變更都會獲得認可。", + "nothingToCommit": "一旦有些變更要進行認可,請在認可訊息中輸入,或者按下 **[{0}]** 來認可變更。如有任何已分段的變更,將只有那些分段的變更會獲得認可,否則,所有變更都會獲得認可。", + "longCommit": "建議讓認可訊息的第一行文字少於 50 個字元。但您可在額外的資訊中使用多行文字。", + "commitMessage": "訊息 (按 {0} 認可)", + "commitMessageAriaLabel": "Git: 輸入認可訊息並按 {0} 認可", + "treeAriaLabel": "Git 變更檢視", + "showOutput": "顯示 Git 輸出" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json new file mode 100644 index 0000000000..3f2f017b28 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stagedChanges": "已分段的變更", + "allChanges": "變更", + "mergeChanges": "合併變更", + "outsideOfWorkspace": "此檔案位於目前的工作區外。", + "modified-char": "M", + "added-char": "A", + "deleted-char": "D", + "renamed-char": "R", + "copied-char": "C", + "untracked-char": "U", + "ignored-char": "!", + "title-index-modified": "已在索引中修改", + "title-modified": "已修改", + "title-index-added": "已新增至索引", + "title-index-deleted": "已在索引中刪除", + "title-deleted": "已刪除", + "title-index-renamed": "已在索引中重新命名", + "title-index-copied": "已在索引中複製", + "title-untracked": "已取消追蹤", + "title-ignored": "已忽略", + "title-conflict-both-deleted": "衝突: 兩者皆刪除", + "title-conflict-added-by-us": "衝突: 由我們新增", + "title-conflict-deleted-by-them": "衝突: 由他們刪除", + "title-conflict-added-by-them": "衝突: 由他們新增", + "title-conflict-deleted-by-us": "衝突: 由我們刪除", + "title-conflict-both-added": "衝突: 兩者皆新增", + "title-conflict-both-modified": "衝突: 兩者皆修改", + "fileStatusAriaLabel": "資料夾 {1} 中的檔案 {0} 具有狀態: {2},Git", + "ariaLabelStagedChanges": "暫存的變更,Git", + "ariaLabelChanges": "變更,Git", + "ariaLabelMerge": "合併,Git" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json new file mode 100644 index 0000000000..80a7d916e1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disabled": "已停用設定中的 Git。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json new file mode 100644 index 0000000000..d9fabd64bc --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noGit": "此工作區尚未受 Git 原始檔控制。", + "gitinit": "將 Git 存放庫初始化" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json new file mode 100644 index 0000000000..f0c325096c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "macInstallWith": "您可以使用 {0} 進行安裝、從 {1} 下載,或直接在終端機提示時輸入 {3},以安裝 {2} 命令列開發人員工具。", + "winInstallWith": "您可以使用 {0} 進行安裝或從 {1} 下載。", + "linuxDownloadFrom": "您可以從 {0} 下載。", + "downloadFrom": "您可以從 {0} 下載。", + "looksLike": "您的系統上似乎未安裝 Git。", + "pleaseRestart": "安裝 Git 之後,請重新啟動 VSCode。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json new file mode 100644 index 0000000000..ac11c2032a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "huge": "您的存放庫似乎有多個進行中的變更。\n會造成 Code 的速度非常緩慢。", + "setting": "您可以使用下列設定永久停用此警告:", + "allo": "允許大型存放庫" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json new file mode 100644 index 0000000000..87edc652c0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrongRoot": "此目錄似乎包含在 Git 儲存機制中。", + "pleaseRestart": "開啟儲存機制的根目錄,以便存取 Git 功能。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json b/i18n/cht/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json new file mode 100644 index 0000000000..f0f6068f5a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspaceHelp": "您尚未開啟資料夾。", + "pleaseRestart": "以 Git 儲存機制開啟資料夾,以便存取 Git 功能。", + "openFolder": "開啟資料夾" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json new file mode 100644 index 0000000000..a8eb7df6eb --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSCMViewlet": "顯示 SCM", + "git": "Git" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json b/i18n/cht/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json new file mode 100644 index 0000000000..03950f30d0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "valid": "請提供有效的 Git 存放庫 URL", + "url": "存放庫 URL", + "directory": "目的地複製目錄", + "cloning": "正在複製存放庫 '{0}'...", + "already exists": "目的地存放庫已存在,請選擇另一個目錄作為複製目標。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json b/i18n/cht/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json new file mode 100644 index 0000000000..91fbae2148 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "git": "Git" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/git/node/git.lib.i18n.json b/i18n/cht/src/vs/workbench/parts/git/node/git.lib.i18n.json new file mode 100644 index 0000000000..00b0659cd4 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/git/node/git.lib.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorBuffer": "無法從 git 開啟檔案", + "fileBinaryError": "檔案似乎是二進位檔,因此無法當做文字開啟" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..ca10a7f5ce --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "HTML 預覽", + "devtools.webview": "開發人員: Webview 工具" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/cht/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..ba7ffeaef4 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "編輯器輸入無效。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/cht/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..b678665ce0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "開發人員: Webview 工具" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/cht/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..c81fefeec3 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "檢視", + "problems.view.show.label": "顯示問題", + "problems.panel.configuration.title": "[問題] 檢視", + "problems.panel.configuration.autoreveal": "控制 [問題] 檢視是否應自動在開啟檔案時加以顯示", + "markers.panel.title.problems": "問題", + "markers.panel.aria.label.problems.tree": "依檔案分組的問題", + "markers.panel.no.problems.build": "目前在工作區中未偵測到任何問題。", + "markers.panel.no.problems.filters": "使用提供的篩選準則找不到任何結果", + "markers.panel.action.filter": "篩選問題", + "markers.panel.filter.placeholder": "依類型或文字篩選", + "markers.panel.filter.errors": "錯誤", + "markers.panel.filter.warnings": "警告", + "markers.panel.filter.infos": "資訊", + "markers.panel.single.error.label": "1 個錯誤", + "markers.panel.multiple.errors.label": "{0} 個錯誤", + "markers.panel.single.warning.label": "1 個警告", + "markers.panel.multiple.warnings.label": "{0} 個警告", + "markers.panel.single.info.label": "1 個資訊", + "markers.panel.multiple.infos.label": "{0} 個資訊", + "markers.panel.single.unknown.label": "1 個未知", + "markers.panel.multiple.unknowns.label": "{0} 個未知", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "發生 {1} 個問題的 {0}", + "problems.tree.aria.label.error.marker": "{0} 產生的錯誤: 在行 {2} 與字元 {3} 的 {1}", + "problems.tree.aria.label.error.marker.nosource": "錯誤: 在行 {1} 與字元 {2} 的 {0}", + "problems.tree.aria.label.warning.marker": "{0} 產生的警告: 在行 {2} 與字元 {3} 的 {1}", + "problems.tree.aria.label.warning.marker.nosource": "警告: 在行 {1} 與字元 {2} 的 {0}", + "problems.tree.aria.label.info.marker": "{0} 產生的資訊: 在行 {2} 與字元 {3} 的 {1}", + "problems.tree.aria.label.info.marker.nosource": "資訊: 在行 {1} 與字元 {2} 的 {0}", + "problems.tree.aria.label.marker": "{0} 產生的問題: 在行 {2} 與字元 {3} 的 {1}", + "problems.tree.aria.label.marker.nosource": "問題: 在行 {1} 與字元 {2} 的 {0}", + "errors.warnings.show.label": "顯示錯誤和警告" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/cht/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..16ac095ea8 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "複製" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..cdcf919870 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "您願意填寫簡短的意見反應問卷嗎?", + "takeSurvey": "填寫問卷", + "remindLater": "稍後再提醒我", + "neverAgain": "不要再顯示" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..d0cbbe2b8b --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "輸出", + "viewCategory": "檢視", + "clearOutput.label": "清除輸出" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/cht/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..c6174ad44f --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "切換輸出", + "clearOutput": "清除輸出", + "toggleOutputScrollLock": "切換輸出 SCROLL LOCK", + "switchToOutput.label": "切換至輸出" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/cht/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..a15b35e630 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0},輸出面板", + "outputPanelAriaLabel": "輸出面板" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/cht/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..4eb1e5caef --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "輸出", + "channel": "針對 '{0}'" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..3d78201f2c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "偵測到啟動速度慢", + "slow.detail": "抱歉! 先前的啟動速度過慢。請重新啟動 '{0}' 並啟用剖析功能,同時將設定檔提供給我們,我們將努力提升啟動的品質。", + "prof.message": "已成功建立設定檔。", + "prof.detail": "請建立問題,並手動附加下列檔案:\n{0}", + "prof.restartAndFileIssue": "建立問題並重新啟動", + "prof.restart": "重新啟動", + "prof.thanks": "感謝您的協助", + "prof.detail.restart": "需要重新啟動才能夠繼續使用'{0}‘.再次感謝您的回饋." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..0339e3df83 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "按下所需按鍵組合及 ENTER。ESCAPE 可取消。", + "defineKeybinding.chordsTo": "同步到" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..98e6e7986c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "鍵盤快速鍵(&&K)", + "SearchKeybindings.AriaLabel": "搜尋按鍵繫結關係", + "SearchKeybindings.Placeholder": "搜尋按鍵繫結關係", + "sortByPrecedene": "依優先順序排序", + "header-message": "開啟及編輯進階自訂項目時使用", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "按鍵繫結關係", + "changeLabel": "變更按鍵繫結關係", + "addLabel": "新增按鍵繫結關係", + "removeLabel": "移除按鍵繫結關係", + "resetLabel": "重設按鍵繫結關係", + "showConflictsLabel": "顯示衝突", + "copyLabel": "複製", + "error": "編輯按鍵繫結關係時發生錯誤 '{0}'。請開啟 'keybindings.json' 檔案加以檢查。", + "command": "Command", + "keybinding": "按鍵繫結關係", + "source": "來源", + "when": "當", + "editKeybindingLabelWithKey": "變更按鍵繫結關係{0}", + "editKeybindingLabel": "變更按鍵繫結關係", + "addKeybindingLabelWithKey": "新增按鍵繫結關係 {0}", + "addKeybindingLabel": "新增按鍵繫結關係", + "commandAriaLabel": "命令為 {0}。", + "keybindingAriaLabel": "按鍵繫結關係為 {0}。", + "noKeybinding": "未指派任何按鍵繫結關係。", + "sourceAriaLabel": "來源為 {0}。", + "whenAriaLabel": "時間為 {0}。", + "noWhen": "沒有時間內容。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..a337c987c9 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "定義按鍵繫結關係", + "defineKeybinding.kbLayoutErrorMessage": "您無法在目前的鍵盤配置下產生此按鍵組合。", + "defineKeybinding.kbLayoutLocalAndUSMessage": "**{0}**針對您目前的按鍵配置(**{1}**為美國標準)", + "defineKeybinding.kbLayoutLocalMessage": "**{0}**針對您目前的鍵盤配置" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..1fc66b372f --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "預設喜好設定編輯器", + "keybindingsEditor": "按鍵繫結關係編輯器", + "preferences": "喜好設定" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..426ec5842e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "開啟使用者設定", + "openGlobalKeybindings": "開啟鍵盤快速鍵", + "openGlobalKeybindingsFile": "開啟鍵盤快速鍵檔案", + "openWorkspaceSettings": "開啟工作區設定", + "openFolderSettings": "開啟資料夾設定", + "pickFolder": "選取資料夾", + "configureLanguageBasedSettings": "設定語言專屬設定...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "選取語言" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..20293c46a0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "預設設定", + "SearchSettingsWidget.AriaLabel": "搜尋設定", + "SearchSettingsWidget.Placeholder": "搜尋設定", + "totalSettingsMessage": "共 {0} 項設定", + "noSettingsFound": "沒有結果", + "oneSettingFound": "1 項相符設定", + "settingsFound": "{0} 項相符設定", + "fileEditorWithInputAriaLabel": "{0}。文字檔編輯器。", + "fileEditorAriaLabel": "文字檔編輯器。", + "preferencesAriaLabel": "預設喜好設定。唯讀文字編輯器。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..ea3ac9ed95 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "將您的設定放置在此以覆寫預設設定。", + "errorInvalidConfiguration": "無法寫入設定.請開啟使用者設定並修正檔案中的錯誤/警告後再試一次.", + "emptyWorkspaceSettingsHeader": "將您的設定放置在此以覆寫使用者設定。", + "emptyFolderSettingsHeader": "將您的資料夾設定放置在此以覆寫工作區設定的資料夾設定。", + "defaultFolderSettingsTitle": "預設資料夾設定", + "defaultSettingsTitle": "預設設定", + "noSettingsFound": "找不到任何設定。", + "editTtile": "編輯", + "replaceDefaultValue": "在設定中取代", + "copyDefaultValue": "複製到設定", + "unsupportedPHPExecutablePathSetting": "這項設定必須是使用者設定。若要設定工作區的 PHP,請開啟 PHP 檔案並按一下狀態列中的 [PHP 路徑]。", + "unsupportedWorkspaceSetting": "這項設定必須是使用者設定。", + "unsupportedWorkbenchSetting": "目前無法套用此設定。要在您直接開啟此資料夾時才會套用。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..8749b68deb --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "先開啟資料夾以建立工作區設定", + "emptyKeybindingsHeader": "將您的按鍵組合放入此檔案中以覆寫預設值", + "defaultKeybindings": "預設按鍵繫結關係", + "folderSettingsName": "{0} (資料夾設定)", + "fail.createSettings": "無法建立 '{0}' ({1})。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..a81720773c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "資料夾設定" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..3730d36476 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "預設", + "user": "使用者", + "meta": "中繼", + "option": "選項" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..7a85abca2e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "使用者設定", + "workspaceSettingsTarget": "工作區設定" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..d09b33c665 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "經常使用的", + "noSettings": "沒有任何設定", + "defaultKeybindingsHeader": "將按鍵組合放入您的按鍵組合檔案中加以覆寫。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..16e9a312b7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "顯示所有命令", + "clearCommandHistory": "清除命令歷程記錄", + "showCommands.label": "命令選擇區...", + "entryAriaLabelWithKey": "{0}、{1}、命令", + "entryAriaLabel": "{0},命令", + "canNotRun": "無法從這裡執行命令 '{0}'。", + "actionNotEnabled": "目前內容中未啟用命令 '{0}'。", + "recentlyUsed": "最近使用的", + "morecCommands": "其他命令", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "沒有相符的命令" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..130ccbaa34 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "移至行...", + "gotoLineLabelEmptyWithLimit": "輸入介於 1 到 {0} 之間要瀏覽的行號", + "gotoLineLabelEmpty": "輸入要瀏覽的行號", + "gotoLineColumnLabel": "移至行 {0} 和字元 {1}", + "gotoLineLabel": "移至第 {0} 行", + "gotoLineHandlerAriaLabel": "輸入要瀏覽的行號。", + "cannotRunGotoLine": "先開啟文字檔以移至某一行" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..bec0655a1c --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "前往檔案中的符號...", + "symbols": "符號 ({0})", + "method": "方法 ({0})", + "function": "函式 ({0})", + "_constructor": "建構函式 ({0})", + "variable": "變數 ({0})", + "class": "類別 ({0})", + "interface": "介面 ({0})", + "namespace": "命名空間 ({0})", + "package": "封裝 ({0})", + "modules": "模組 ({0})", + "property": "屬性 ({0})", + "enum": "列舉 ({0})", + "string": "字串 ({0})", + "rule": "規則 ({0})", + "file": "檔案 ({0})", + "array": "陣列 ({0})", + "number": "數字 ({0})", + "boolean": "布林值 ({0})", + "object": "物件 ({0})", + "key": "索引鍵 ({0})", + "entryAriaLabel": "{0},符號", + "noSymbolsMatching": "沒有相符的符號", + "noSymbolsFound": "找不到符號", + "gotoSymbolHandlerAriaLabel": "輸入以縮小目前使用中編輯器的符號範圍。", + "cannotRunGotoSymbolInFile": "檔案沒有任何符號資訊", + "cannotRunGotoSymbol": "先開啟文字檔以移至某一個符號" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..68bbbfb196 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},選擇器說明", + "globalCommands": "全域命令", + "editorCommands": "編輯器命令" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..1e17885423 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "顯示並執行命令", + "gotoLineDescriptionMac": "移至行", + "gotoLineDescriptionWin": "移至行", + "gotoSymbolDescription": "前往檔案中的符號", + "gotoSymbolDescriptionScoped": "前往檔案中的符號 (依分類)", + "helpDescription": "顯示說明", + "viewPickerDescription": "開啟檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..cbe441a0b6 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},檢視選擇器", + "views": "檢視", + "panels": "面板", + "terminals": "終端機", + "terminalTitle": "{0}: {1}", + "channels": "輸出", + "openView": "開啟檢視", + "quickOpenView": "Quick Open 檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..57017151f0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "設定已經變更,必須重新啟動才會生效。", + "relaunchSettingDetail": "請按 [重新啟動] 按鈕以重新啟動 {0} 並啟用設定。", + "restart": "重新啟動", + "relaunchWorkspaceMessage": "必須重新載入延伸模組系統才可變更此工作區。", + "reload": "重新載入" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/cht/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..f31a35988e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "修改中的行於編輯器邊框的背景色彩", + "editorGutterAddedBackground": "新增後的行於編輯器邊框的背景色彩", + "editorGutterDeletedBackground": "刪除後的行於編輯器邊框的背景色彩", + "overviewRulerModifiedForeground": "已修改內容的概觀尺規色彩。", + "overviewRulerAddedForeground": "已新增內容的的概觀尺規色彩。", + "overviewRulerDeletedForeground": "已刪除內容的的概觀尺規色彩。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..85b01d3c8b --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "顯示 Git", + "source control": "原始檔控制", + "toggleSCMViewlet": "顯示 SCM", + "view": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..fe0f40081e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0} 個暫止的變更" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..b29611d38d --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "安裝額外SCM提供者...", + "switch provider": "切換 SCM 提供者..." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..fc1ebd2fd3 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Message (press {0} to commit)", + "installAdditionalSCMProviders": "安裝額外SCM提供者...", + "no open repo": "沒有使用中的原始檔控制。", + "source control": "原始檔控制", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..b16b3476bb --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "檔案和符號結果", + "fileResults": "檔案結果" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..ad29ab8ae4 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},檔案選擇器", + "searchResults": "搜尋結果" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..da54ac687f --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},符號選擇器", + "symbols": "符號結果", + "noSymbolsMatching": "沒有相符的符號", + "noSymbolsWithoutInput": "輸入以搜尋符號" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..9b27be4024 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "輸入", + "useIgnoreFilesDescription": "使用忽略檔案", + "useExcludeSettingsDescription": "使用排除設定" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..6a3a04581b --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (Replace Preview)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..0112e72f51 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "前往工作區中的符號...", + "name": "搜尋", + "showSearchViewlet": "顯示搜尋", + "view": "檢視", + "findInFiles": "在檔案中尋找", + "openAnythingHandlerDescription": "前往檔案", + "openSymbolDescriptionNormal": "前往工作區中的符號", + "searchOutputChannelTitle": "搜尋", + "searchConfigurationTitle": "搜尋", + "exclude": "設定 Glob 模式,以排除不要搜尋的檔案及資料夾。請從 file.exclude 設定繼承所有的 Glob 模式。", + "exclude.boolean": "要符合檔案路徑的 Glob 模式。設為 True 或 False 可啟用或停用模式。", + "exclude.when": "在相符檔案同層級上額外的檢查。請使用 $(basename) 作為相符檔案名稱的變數。", + "useRipgrep": "控制是否要在文字搜尋中使用 ripgrep", + "useIgnoreFilesByDefault": "控制在搜尋新的工作區時,是否要根據預設使用 .gitignore 及 .ignore 檔案。", + "search.quickOpen.includeSymbols": "設定以將全域符號搜尋的結果納入 Quick Open 的檔案結果中。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..f011a56fff --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "顯示下一個搜尋包含模式", + "previousSearchIncludePattern": "顯示上一個搜尋包含模式", + "nextSearchExcludePattern": "顯示下一個搜尋排除模式", + "previousSearchExcludePattern": "顯示上一個搜尋排除模式", + "nextSearchTerm": "顯示下一個搜尋字詞", + "previousSearchTerm": "顯示上一個搜尋字詞", + "focusNextInputBox": "聚焦於下一個輸入方塊", + "focusPreviousInputBox": "聚焦於上一個輸入方塊", + "replaceInFiles": "檔案中取代", + "findInWorkspace": "在工作區中尋找...", + "findInFolder": "在資料夾中尋找...", + "RefreshAction.label": "重新整理", + "ClearSearchResultsAction.label": "清除搜尋結果", + "FocusNextSearchResult.label": "聚焦於下一個搜尋結果", + "FocusPreviousSearchResult.label": "聚焦於上一個搜尋結果", + "RemoveAction.label": "移除", + "file.replaceAll.label": "全部取代", + "match.replace.label": "取代" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..3cf5a99e73 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "其他檔案", + "searchFileMatches": "找到 {0} 個檔案", + "searchFileMatch": "找到 {0} 個檔案", + "searchMatches": "找到 {0} 個相符", + "searchMatch": "找到 {0} 個相符", + "folderMatchAriaLabel": "資料夾根目錄 {1} 中有 {0} 個相符,搜尋結果", + "fileMatchAriaLabel": "資料夾 {2} 的檔案 {1} 中有 {0} 個相符,搜尋結果", + "replacePreviewResultAria": "根據文字({3})在({2})欄位列表中將({1})替代為文字{{0}}", + "searchResultAria": "根據文字({2})並在({1})欄位列表中找到符合({0})的項目" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..aa15631ec8 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "切換搜尋詳細資料", + "searchScope.includes": "要包含的檔案", + "label.includes": "搜尋包含模式", + "searchScope.excludes": "要排除的檔案", + "label.excludes": "搜尋排除模式", + "replaceAll.confirmation.title": "全部取代", + "replaceAll.confirm.button": "取代", + "replaceAll.occurrence.file.message": "已將 {1} 個檔案中的 {0} 個相符項目取代為 '{2}'。", + "removeAll.occurrence.file.message": "已取代 {1} 個檔案中的 {0} 個相符項目。", + "replaceAll.occurrence.files.message": "已將 {1} 個檔案中的 {0} 個相符項目取代為 '{2}'。", + "removeAll.occurrence.files.message": "已取代 {1} 個檔案中的 {0} 個相符項目。", + "replaceAll.occurrences.file.message": "已將 {1} 個檔案中的 {0} 個相符項目取代為 '{2}'。", + "removeAll.occurrences.file.message": "已取代 {1} 個檔案中的 {0} 個相符項目。", + "replaceAll.occurrences.files.message": "已將 {1} 個檔案中的 {0} 個相符項目取代為 '{2}'。", + "removeAll.occurrences.files.message": "已取代 {1} 個檔案中的 {0} 個相符項目。", + "removeAll.occurrence.file.confirmation.message": "要將 {1} 個檔案中的 {0} 個相符項目取代為 '{2}' 嗎?", + "replaceAll.occurrence.file.confirmation.message": "要取代 {1} 個檔案中的 {0} 個相符項目嗎?", + "removeAll.occurrence.files.confirmation.message": "要將 {1} 個檔案中的 {0} 個相符項目取代為 '{2}' 嗎?", + "replaceAll.occurrence.files.confirmation.message": "要取代 {1} 個檔案中的 {0} 個相符項目嗎?", + "removeAll.occurrences.file.confirmation.message": "要將 {1} 個檔案中的 {0} 個相符項目取代為 '{2}' 嗎?", + "replaceAll.occurrences.file.confirmation.message": "要取代 {1} 個檔案中的 {0} 個相符項目嗎?", + "removeAll.occurrences.files.confirmation.message": "要將 {1} 個檔案中的 {0} 個相符項目取代為 '{2}' 嗎?", + "replaceAll.occurrences.files.confirmation.message": "要取代 {1} 個檔案中的 {0} 個相符項目嗎?", + "treeAriaLabel": "搜尋結果", + "searchPathNotFoundError": "找不到搜尋路徑: {0}", + "searchMaxResultsWarning": "結果集只包含所有符合項的子集。請提供更具體的搜尋條件以縮小結果範圍。", + "searchCanceled": "在可能找到任何結果之前已取消搜尋 - ", + "noResultsIncludesExcludes": "在 '{0}' 中找不到排除 '{1}' 的結果 - ", + "noResultsIncludes": "在 '{0}' 中找不到結果 - ", + "noResultsExcludes": "找不到排除 '{0}' 的結果 - ", + "noResultsFound": "找不到結果。請檢閱所設定的排除和忽略檔案之設定 - ", + "rerunSearch.message": "再次搜尋", + "rerunSearchInAll.message": "在所有檔案中再次搜尋", + "openSettings.message": "開啟設定", + "openSettings.learnMore": "深入了解", + "ariaSearchResultsStatus": "搜尋傳回 {1} 個檔案中的 {0} 個結果", + "search.file.result": "{1} 個檔案中有 {0} 個結果", + "search.files.result": "{1} 個檔案中有 {0} 個結果", + "search.file.results": "{1} 個檔案中有 {0} 個結果", + "search.files.results": "{1} 個檔案中有 {0} 個結果", + "searchWithoutFolder": "您尚未開啟資料夾。只會開啟目前搜尋的檔案 - ", + "openFolder": "開啟資料夾" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..430c0cb70a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "全部取代 (提交搜尋以啟用)", + "search.action.replaceAll.enabled.label": "全部取代", + "search.replace.toggle.button.title": "切換取代", + "label.Search": "搜尋: 輸入搜尋字詞,然後按 Enter 鍵搜尋或按 Esc 鍵取消", + "search.placeHolder": "搜尋", + "label.Replace": "取代: 輸入取代字詞後按下 Enter 以預覽,或按 Escape 取消", + "search.replace.placeHolder": "取代", + "regexp.validationFailure": "運算式符合所有項目" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/cht/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..a08805346e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "工作區內無此名稱資料夾: {0}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..a12de92606 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "提供程式碼片段。", + "vscode.extension.contributes.snippets-language": "要予以提供此程式碼片段的語言識別碼。", + "vscode.extension.contributes.snippets-path": "程式碼片段檔案的路徑。此路徑是擴充功能資料夾的相對路徑,而且一般會以 './snippets/' 開頭。", + "invalid.language": "`contributes.{0}.language` 中的不明語言。提供的值: {1}", + "invalid.path.0": "`contributes.{0}.path` 中的預期字串。提供的值: {1}", + "invalid.path.1": "要包含在擴充功能資料夾 ({2}) 中的預期 `contributes.{0}.path` ({1})。這可能會使擴充功能無法移植。", + "badVariableUse": "程式碼片段 \"{0}\" 很可能會混淆 snippet-variables 及 snippet-placeholders。如需詳細資料,請參閱 https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..b0a2ae2b19 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "插入程式碼片段" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..c52025c036 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "為程式碼片段選取語言", + "openSnippet.errorOnCreate": "無法建立 {0}", + "openSnippet.label": "開啟使用者程式碼片段", + "preferences": "喜好設定", + "snippetSchema.json.default": "空白程式碼片段", + "snippetSchema.json": "使用者程式碼片段組態", + "snippetSchema.json.prefix": "在 Intellisense 中選取程式碼片段時要使用的前置詞", + "snippetSchema.json.body": "程式碼片段內容。請使用 '$1', '${1:defaultText}' 以定義游標位置,並使用 '$0' 定義最終游標位置。將 '${varName}' and '${varName:defaultText}' 插入變數值,例如 'This is file: $TM_FILENAME'。", + "snippetSchema.json.description": "程式碼片段描述。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..61a3582474 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "使用者程式碼片段", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0},{1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..93eef5e4e1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "在前置詞相符時插入程式碼片段。最適用於未啟用 'quickSuggestions' 時。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..e0924154a1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "協助我們改善{0}", + "takeShortSurvey": "填寫簡短調查問卷", + "remindLater": "稍後再提醒我", + "neverAgain": "不要再顯示" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..cdcf919870 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "您願意填寫簡短的意見反應問卷嗎?", + "takeSurvey": "填寫問卷", + "remindLater": "稍後再提醒我", + "neverAgain": "不要再顯示" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..662f197de8 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "沒有工作相符" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..8c2819e623 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0},工作", + "recentlyUsed": "最近使用的工作", + "configured": "設定的工作", + "detected": "偵測到的工作", + "customizeTask": "設定工作" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..39a14d4145 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "請鍵入要重新啟動的工作名稱", + "noTasksMatching": "沒有工作相符", + "noTasksFound": "找不到工作可重新啟動" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..26cfbf24dd --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "輸入要執行的工作名稱", + "noTasksMatching": "No tasks matching", + "noTasksFound": "找不到工作" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..d937f2ef99 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Type the name of a task to terminate", + "noTasksMatching": "沒有相符的工作", + "noTasksFound": "No tasks to terminate found" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..662f197de8 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "沒有工作相符" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..27c4b234a1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "警告: options.cwd 必須屬於字串類型。即將忽略值 {0}。", + "ConfigurationParser.noargs": "錯誤: 命令引數必須是字串陣列。提供的值為:\n{0}", + "ConfigurationParser.noShell": "警告: 只有在終端機中執行工作時才支援殼層組態。", + "ConfigurationParser.noName": "錯誤: 宣告範圍中的問題比對器必須有名稱:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "警告: 定義的問題比對器未知。支援的類型為 string | ProblemMatcher | (string | ProblemMatcher)[]。\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "錯誤: problemMatcher 參考無效: {0}\n", + "ConfigurationParser.noTaskName": "錯誤: 工作必須提供 taskName 屬性。即將忽略此工作。\n{0}\n", + "taskConfiguration.shellArgs": "警告: 工作 '{0}' 是殼層命令,但命令名稱或其中一個引數有的未逸出的空格。若要確保命令列正確引述,請將引數合併到命令中。", + "taskConfiguration.noCommandOrDependsOn": "錯誤: 工作 '{0}' 未指定命令與 dependsOn 屬性。將會略過該工作。其定義為: \n{1}", + "taskConfiguration.noCommand": "錯誤: 工作 '{0}' 未定義命令。即將略過該工作。其定義為:\n{1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..9def7bd376 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "實際的工作類型", + "TaskDefinition.properties": "工作類型的其他屬性", + "TaskTypeConfiguration.noType": "工作類型組態遺失需要的 'taskType' 屬性", + "TaskDefinitionExtPoint": "提供工作種類" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..c01185eebb --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": "執行 .NET Core 建置命令", + "msbuild": "執行建置目標", + "externalCommand": "執行任意外部命令的範例", + "Maven": "執行一般 maven 命令" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..cfb03c7ab1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "其他命令選項", + "JsonSchema.options.cwd": "所執行程式或指令碼的目前工作目錄。如果省略,則會使用 Code 的目前工作區根目錄。", + "JsonSchema.options.env": "所執行程式或殼層的環境。如果省略,則會使用父處理序的環境。", + "JsonSchema.shellConfiguration": "設定要使用的殼層。", + "JsonSchema.shell.executable": "要使用的殼層。", + "JsonSchema.shell.args": "殼層引數。", + "JsonSchema.command": "要執行的命令。可以是外部程式或殼層命令。", + "JsonSchema.tasks.args": "叫用此工作時,傳遞至命令的引數。", + "JsonSchema.tasks.taskName": "工作的名稱", + "JsonSchema.tasks.windows": "Windows 特定命令組態", + "JsonSchema.tasks.mac": "Mac 特定命令組態", + "JsonSchema.tasks.linux": "Linux 特定命令組態", + "JsonSchema.tasks.suppressTaskName": "控制是否將工作名稱當做引數加入命令中。如果省略,則會使用全域定義的值。", + "JsonSchema.tasks.showOutput": "控制是否顯示執行中工作的輸出。如果省略,則會使用全域定義的值。", + "JsonSchema.echoCommand": "控制是否將執行的命令傳到輸出。預設為 False。", + "JsonSchema.tasks.watching.deprecation": "已被取代。請改用 isBackground。", + "JsonSchema.tasks.watching": "執行的工作是否保持運作且正在監看檔案系統。", + "JsonSchema.tasks.background": "執行的工作是否保持運作且正在背景執行。", + "JsonSchema.tasks.promptOnClose": "是否在 VS Code 有執行中工作的狀態下關閉時提示使用者。", + "JsonSchema.tasks.build": "將此工作對應至 Code 的預設建置命令。", + "JsonSchema.tasks.test": "將此工作對應至 Code 的預設測試命令。", + "JsonSchema.tasks.matchers": "要使用的問題比對器。可以是字串或問題比對器定義,或是字串和問題比對器陣列。", + "JsonSchema.args": "傳遞至命令的其他引數。", + "JsonSchema.showOutput": "控制是否顯示執行中工作的輸出。如果省略,則會使用 [永遠]。", + "JsonSchema.watching.deprecation": "已被取代。請改用 isBackground。", + "JsonSchema.watching": "執行的工作是否保持運作且正在監看檔案系統。", + "JsonSchema.background": "執行的工作是否保持運作且正在背景執行。", + "JsonSchema.promptOnClose": "是否在 VSCode 以執行中的背景工作關閉時提示使用者。", + "JsonSchema.suppressTaskName": "控制是否將工作名稱當做引數加入命令中。預設為 False。", + "JsonSchema.taskSelector": "前置詞,表示引數是工作。", + "JsonSchema.matchers": "要使用的問題比對器。可以是字串或問題比對器定義,或是字串和問題比對器陣列。", + "JsonSchema.tasks": "工作組態。這些通常是在外部工作執行器中已定義的工作擴充。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..45ee2836f1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "組態的版本號碼", + "JsonSchema._runner": "執行器已結束支援.請參考官方執行器屬性", + "JsonSchema.runner": "定義工作是否作為處理序執行,以及輸出會顯示在輸出視窗或終端機內。", + "JsonSchema.windows": "Windows 特定命令組態", + "JsonSchema.mac": "Mac 特定命令組態", + "JsonSchema.linux": "Linux 特定命令組態", + "JsonSchema.shell": "指定此命令是殼層命令或外部程式。如果省略,預設為 False。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..fbe3bda6f4 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "指定此命令是殼層命令或外部程式。如果省略,預設為 False。", + "JsonSchema.tasks.isShellCommand.deprecated": "已淘汰屬性 isShellCommand。請改為在 [選項] 中使用型別屬性及殼層屬性。此外也請參閱 1.14 版本資訊。", + "JsonSchema.tasks.dependsOn.string": "此工作相依的另一個工作。", + "JsonSchema.tasks.dependsOn.array": "此工作相依的其他工作。", + "JsonSchema.tasks.presentation": "設定面板,用於呈現工作的輸出並讀取其輸入。", + "JsonSchema.tasks.presentation.echo": "控制是否會將執行的命令回應給面板。預設為 true。", + "JsonSchema.tasks.presentation.focus": "控制面板是否要接受焦點。預設為 true。若設定為 true,也會使顯示面板。", + "JsonSchema.tasks.presentation.reveal.always": "執行此工作時,一律顯示終端機。", + "JsonSchema.tasks.presentation.reveal.silent": "沒有與工作相關的問題比對器,且執行未發生錯誤時,才顯示終端機。", + "JsonSchema.tasks.presentation.reveal.never": "執行此工作時,永不顯示終端機。", + "JsonSchema.tasks.presentation.reveals": "控制是否要顯示執行工作的面板。預設為 [永遠]。", + "JsonSchema.tasks.presentation.instance": "控制面板是否會在工作之間共用、專屬於此工作或是在每個回合建立一個新的面板。", + "JsonSchema.tasks.terminal": "已淘汰終端機屬性。請改用簡報", + "JsonSchema.tasks.group.kind": "該工作的執行群組。", + "JsonSchema.tasks.group.isDefault": "定義此工作在群組中是否為預設工作。", + "JsonSchema.tasks.group.defaultBuild": "將工作標記為預設組建工作。", + "JsonSchema.tasks.group.defaultTest": "將工作標記為預設測試工作。", + "JsonSchema.tasks.group.build": "將工作標記為組建工作,並且能使用 [執行組建工作] 命令存取。", + "JsonSchema.tasks.group.test": "將工作標記為組建工作,並且能使用 [執行測試工作] 命令存取。", + "JsonSchema.tasks.group.none": "指派工作到沒有群組", + "JsonSchema.tasks.group": "定義工作屬於哪個執行群組。支援將 「組建」新增到組建群組,以及將「測試」新增到測試群組。", + "JsonSchema.tasks.type": "定義工作是作為處理序或殼層中的命令執行。預設為處理序。", + "JsonSchema.version": "組態版本號碼", + "JsonSchema.tasks.identifier": "用以參考在 launch.json 或 dependsOn 子句中工作的使用者定義識別碼。", + "JsonSchema.tasks.taskLabel": "工作的標籤", + "JsonSchema.tasks.taskName": "工作的名稱", + "JsonSchema.tasks.taskName.deprecated": "已淘汰工作的名稱屬性。請改用標籤屬性。", + "JsonSchema.tasks.background": "執行的工作是否保持運作且正在背景執行。", + "JsonSchema.tasks.promptOnClose": "是否在 VS Code 有執行中工作的狀態下關閉時提示使用者。", + "JsonSchema.tasks.matchers": "要使用的問題比對器。可以是字串或問題比對器定義,或是字串和問題比對器陣列。", + "JsonSchema.customizations.customizes.type": "要自訂的工作類型", + "JsonSchema.tasks.customize.deprecated": "已淘汰自訂屬性。請參閱 1.14 版本資訊,以了解如何遷移到新的工作自訂方法", + "JsonSchema.tasks.showOputput.deprecated": "已淘汰屬性 showOutput。請改用簡報屬性中的顯示屬性。此外也請參閱 1.14 版本資訊。", + "JsonSchema.tasks.echoCommand.deprecated": "已淘汰屬性 echoCommand。請改用簡報屬性中的回應屬性。此外也請參閱 1.14 版本資訊。 ", + "JsonSchema.tasks.suppressTaskName.deprecated": "屬性 suppressTaskName 已淘汰。請改為將命令與其引數內嵌至工作。另請參閱 1.14 版本資訊。", + "JsonSchema.tasks.isBuildCommand.deprecated": "已淘汰屬性 isBuildCommand。請改用群組屬性。此外也請參閱 1.14 版本資訊。", + "JsonSchema.tasks.isTestCommand.deprecated": "已淘汰屬性 isTestCommand。請改用群組屬性。此外也請參閱 1.14 版本資訊。", + "JsonSchema.tasks.taskSelector.deprecated": "屬性 taskSelector 已淘汰。請改為將命令與其引數內嵌至工作。另請參閱 1.14 版本資訊。 ", + "JsonSchema.windows": "Windows 特定命令組態", + "JsonSchema.mac": "Mac 特定命令組態", + "JsonSchema.linux": "Linux 特定命令組態" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..97e5539555 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "工作", + "ConfigureTaskRunnerAction.noWorkspace": "這些工作只會出現在工作區資料夾中。", + "ConfigureTaskRunnerAction.quickPick.template": "選取工作執行器", + "ConfigureTaskRunnerAction.autoDetecting": "自動偵測 {0} 的工作", + "ConfigureTaskRunnerAction.autoDetect": "自動偵測工作系統失敗。正在使用預設範本。如需詳細資料,請參閱工作輸出。", + "ConfigureTaskRunnerAction.autoDetectError": "自動偵測工作系統產生的錯誤。如需詳細資料,請查看工作輸出。", + "ConfigureTaskRunnerAction.failed": "無法在 '.vscode' 資料夾中建立 'tasks.json' 檔案。如需詳細資訊,請參閱工作輸出。", + "ConfigureTaskRunnerAction.label": "設定工作執行器", + "ConfigureBuildTaskAction.label": "設定建置工作", + "CloseMessageAction.label": "關閉", + "ShowTerminalAction.label": "檢視終端機", + "problems": "問題", + "manyMarkers": "99+", + "runningTasks": "顯示執行中的工作", + "tasks": "工作", + "TaskSystem.noHotSwap": "必須重新載入視窗才能變更工作執行引擎", + "TaskService.noBuildTask1": "未定義任何建置工作。請使用 'isBuildCommand' 標記 tasks.json 檔案中的工作。", + "TaskService.noBuildTask2": "未定義任何組建工作,請在 tasks.json 檔案中將工作標記為 'build' 群組。", + "TaskService.noTestTask1": "未定義任何建置工作。請使用 'isTestCommand' 標記 tasks.json 檔案中的工作。", + "TaskService.noTestTask2": "未定義任何測試工作,請在 tasks.json 檔案中將工作標記為 'test' 群組。", + "TaskServer.noTask": "找不到所要求要執行的工作 {0}。", + "TaskService.attachProblemMatcher.continueWithout": "在不掃描工作輸出的情況下繼續", + "TaskService.attachProblemMatcher.never": "永不掃描工作輸出", + "TaskService.attachProblemMatcher.learnMoreAbout": "深入了解掃描工作輸出", + "selectProblemMatcher": "選取錯誤和警告的種類以掃描工作輸出", + "customizeParseErrors": "當前的工作組態存在錯誤.請更正錯誤再執行工作.", + "moreThanOneBuildTask": "定義了很多建置工作於tasks.json.執行第一個.", + "TaskSystem.activeSame.background": "工作 '{0}' 已在使用中,且處於背景模式中。請使用工作功能表的 [終止工作...] 將其終止。", + "TaskSystem.activeSame.noBackground": "工作 '{0}' 已在使用中。請使用工作功能表的 [終止工作...] 將其終止。 ", + "TaskSystem.active": "已有工作在執行。請先終止該工作,然後再執行其他工作。", + "TaskSystem.restartFailed": "無法終止再重新啟動工作 {0}", + "TaskSystem.configurationErrors": "錯誤: 提供的工作組態具有驗證錯誤而無法使用。請先更正這些錯誤。", + "TaskSystem.invalidTaskJson": "錯誤: tasks.json 檔案的內容具有語法錯誤。請更正錯誤,再執行工作\n", + "TaskSystem.runningTask": "有一個工作正在執行。要終止工作嗎?", + "TaskSystem.terminateTask": "終止工作(&&T)", + "TaskSystem.noProcess": "啟動的工作已不存在。如果工作繁衍的背景處理序結束,VS Code 可能會產生孤立的處理序。若要避免此情況,請啟動有等候旗標的最後一個背景處理序。", + "TaskSystem.exitAnyways": "仍要結束(&&E)", + "TerminateAction.label": "終止工作", + "TaskSystem.unknownError": "執行工作時發生錯誤。如需詳細資訊,請參閱工作記錄檔。", + "TaskService.noWorkspace": "這些工作只會出現在工作區資料夾中。", + "recentlyUsed": "最近使用的工作", + "configured": "設定的工作", + "detected": "偵測到的工作", + "TaskService.fetchingBuildTasks": "正在擷取組建工作...", + "TaskService.noBuildTaskTerminal": "找不到任何組建工作。請按一下 [設定組建工作] 以加以定義。", + "TaskService.pickBuildTask": "請選取要執行的組建工作", + "TaskService.fetchingTestTasks": "正在擷取測試工作...", + "TaskService.noTestTaskTerminal": "找不到任何測試工作。請按一下 [設定測試執行器] 以加以定義。", + "TaskService.pickTestTask": "請選取要執行的測試工作", + "TaskService.noTaskRunning": "目前沒有執行中的工作。", + "TaskService.tastToTerminate": "請選取要終止的工作", + "TerminateAction.noProcess": "啟動的處理序已不存在。如果工作繁衍的背景工作結束,VS Code 可能會產生孤立的處理序。", + "TerminateAction.failed": "無法終止執行中的工作", + "TaskService.noTaskToRestart": "沒有任何要重新啟動的工作。", + "TaskService.tastToRestart": "請選取要重新啟動的工作", + "TaskService.defaultBuildTaskExists": "已經將 {0} 標記為預設組建工作。", + "TaskService.pickDefaultBuildTask": "請選取要用作預設組建工作的工作", + "TaskService.defaultTestTaskExists": "已經將 {0} 標記為預設測試工作。", + "TaskService.pickDefaultTestTask": "請選取要用作預設測試工作的工作", + "TaskService.noTaskIsRunning": "沒有執行中的工作。", + "TaskService.pickShowTask": "選取要顯示輸出的工作", + "ShowLogAction.label": "顯示工作記錄檔", + "RunTaskAction.label": "執行工作", + "RestartTaskAction.label": "重新開始執行工作", + "ShowTasksAction.label": "顯示執行中的工作", + "BuildAction.label": "執行建置工作", + "TestAction.label": "執行測試工作", + "ConfigureDefaultBuildTask.label": "設定預設組建工作", + "ConfigureDefaultTestTask.label": "設定預設測試工作", + "quickOpen.task": "執行工作" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..5c3bcecfb0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "工作" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..7f583c14ad --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "執行工作時發生不明錯誤。如需詳細資訊,請參閱工作輸出記錄檔。", + "TerminalTaskSystem.terminalName": "工作 - {0}", + "reuseTerminal": "工作將被重新啟用.按任意鍵關閉.", + "TerminalTaskSystem": "無法在 UNC 磁碟機上執行殼層命令。", + "unkownProblemMatcher": "問題比對器 {0} 無法解析,比對器將予忽略。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..632d544f8a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "執行 Gulp --tasks-simple 未列出任何工作。是否已執行 npm 安裝?", + "TaskSystemDetector.noJakeTasks": "執行 Jake --tasks 未列出任何工作。是否已執行 npm 安裝?", + "TaskSystemDetector.noGulpProgram": "您的系統尚未安裝 Gulp。請執行 npm install -g gulp 進行安裝。", + "TaskSystemDetector.noJakeProgram": "您的系統尚未安裝 Jake。請執行 npm install -g jake 進行安裝。", + "TaskSystemDetector.noGruntProgram": "您的系統尚未安裝 Grunt。請執行 npm install -g grunt 進行安裝。", + "TaskSystemDetector.noProgram": "找不到程式 {0}。訊息為 {1}", + "TaskSystemDetector.buildTaskDetected": "偵測到名為 '{0}' 的建置工作。", + "TaskSystemDetector.testTaskDetected": "偵測到名為 '{0}' 的測試工作。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..a5ba86fb6f --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "執行工作時發生不明錯誤。如需詳細資訊,請參閱工作輸出記錄檔。", + "TaskRunnerSystem.watchingBuildTaskFinished": "\n監看建置工作已完成。", + "TaskRunnerSystem.childProcessError": "Failed to launch external program {0} {1}.", + "TaskRunnerSystem.cancelRequested": "\n根據使用者要求已終止工作 '{0}'。", + "unkownProblemMatcher": "問題比對器 {0} 無法解析,比對器將予忽略。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..53f4095104 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "警告: options.cwd 必須屬於字串類型。即將忽略值 {0}。", + "ConfigurationParser.noargs": "錯誤: 命令引數必須是字串陣列。提供的值為:\n{0}", + "ConfigurationParser.noShell": "警告: 只有在終端機中執行工作時才支援殼層組態。", + "ConfigurationParser.noName": "錯誤: 宣告範圍中的問題比對器必須有名稱:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "警告: 定義的問題比對器未知。支援的類型為 string | ProblemMatcher | (string | ProblemMatcher)[]。\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "錯誤: problemMatcher 參考無效: {0}\n", + "ConfigurationParser.noTaskType": "錯誤: 工作組態必須具有型別屬性。將會忽略該組態。\n{0}\n", + "ConfigurationParser.noTypeDefinition": "錯誤: 沒有已註冊工作類型 '{0}'。您是否忘記安裝提供相應工作提供者的延伸模組?", + "ConfigurationParser.notCustom": "錯誤: 未將工作宣告為自訂工作。將會忽略該組態。\n{0}\n", + "ConfigurationParser.noTaskName": "錯誤: 工作必須提供 taskName 屬性。即將忽略此工作。\n{0}\n", + "taskConfiguration.shellArgs": "警告: 工作 '{0}' 是殼層命令,但命令名稱或其中一個引數有的未逸出的空格。若要確保命令列正確引述,請將引數合併到命令中。", + "taskConfiguration.noCommandOrDependsOn": "錯誤: 工作 '{0}' 未指定命令與 dependsOn 屬性。將會略過該工作。其定義為: \n{1}", + "taskConfiguration.noCommand": "錯誤: 工作 '{0}' 未定義命令。即將略過該工作。其定義為:\n{1}", + "TaskParse.noOsSpecificGlobalTasks": "工作版本 2.0.0 不支援全域 OS 特定工作。請使用 OS 特定命令來轉換這些工作。受影響的工作為:\n{0}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..d5bd8262bf --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0} , 終端機選擇器", + "termCreateEntryAriaLabel": "{0},建立新的終端機", + "'workbench.action.terminal.newplus": "$(plus) 建立新的整合式終端機", + "noTerminalsMatching": "無相符的終端機", + "noTerminalsFound": "無開啟的終端機" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..4fb60d76e1 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "顯示所有已開啟的終端機", + "terminalIntegratedConfigurationTitle": "整合式終端機", + "terminal.integrated.shell.linux": "終端機在 Linux 上使用的殼層路徑。", + "terminal.integrated.shellArgs.linux": "在 Linux 終端機要使用的命令列引數。", + "terminal.integrated.shell.osx": "終端機在 OS X 上使用的殼層路徑。", + "terminal.integrated.shellArgs.osx": "在 OS X 終端機要使用的命令列引數。", + "terminal.integrated.shell.windows": "終端機在 Windows 上使用的殼層路徑。使用隨附於 Windows 的殼層 (cmd、PowerShell 或 Bash on Ubuntu) 時。", + "terminal.integrated.shellArgs.windows": "在 Windows 終端機上時要使用的命令列引數。", + "terminal.integrated.rightClickCopyPaste": "如有設定,這會防止在終端機內按滑鼠右鍵時顯示操作功能表,而是在有選取項目時複製、沒有選取項目時貼上。", + "terminal.integrated.fontFamily": "控制終端機的字型家族,預設為 editor.fontFamily 的值。", + "terminal.integrated.fontLigatures": "控制是否在終端機中啟用連字字型。", + "terminal.integrated.fontSize": "控制終端機的字型大小 (以像素為單位)。", + "terminal.integrated.lineHeight": "控制終端機的行高,此數字會乘上終端機字型大小,以取得以像素為單位的實際行高。", + "terminal.integrated.enableBold": "是否要在終端機內啟用粗體文字。此動作需要終端機殼層的支援。", + "terminal.integrated.cursorBlinking": "控制終端機資料指標是否閃爍。", + "terminal.integrated.cursorStyle": "控制終端機資料指標的樣式。", + "terminal.integrated.scrollback": "控制終端機保留在其緩衝區中的行數上限。", + "terminal.integrated.setLocaleVariables": "控制是否在終端機啟動時設定地區設定變數,這在 OS X 上預設為 true,在其他平台則為 false。", + "terminal.integrated.cwd": "終端機啟動位置的明確起始路徑,這會用作殼層處理序的目前工作目錄 (cwd)。當根目錄不是方便的 cwd 時,這在工作區設定中特別好用。", + "terminal.integrated.confirmOnExit": "當有使用中的終端機工作階段時,是否要在結束時確認。", + "terminal.integrated.commandsToSkipShell": "一組命令識別碼,其按鍵繫結關係將不會傳送到殼層,而會一律由 Code 處理。這讓通常由殼層取用的按鍵繫結關係,在使用上與未聚焦於終端機時行為相同,例如 Ctrl+P 可啟動 Quick Open。", + "terminal.integrated.env.osx": "OS X 上的終端機要使用之具有將新增至 VS Code 流程之環境變數的物件", + "terminal.integrated.env.linux": "Linux 上的終端機要使用之具有將新增至 VS Code 流程之環境變數的物件", + "terminal.integrated.env.windows": "Windows 上的終端機要使用之具有將新增至 VS Code 流程之環境變數的物件", + "terminal": "終端機", + "terminalCategory": "終端機", + "viewCategory": "檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..c8b487bf84 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "切換整合式終端機", + "workbench.action.terminal.kill": "終止使用中的終端機執行個體", + "workbench.action.terminal.kill.short": "終止終端機", + "workbench.action.terminal.quickKill": "終止終端機執行個體", + "workbench.action.terminal.copySelection": "複製選取項目", + "workbench.action.terminal.selectAll": "全選", + "workbench.action.terminal.deleteWordLeft": "刪除左方文字", + "workbench.action.terminal.deleteWordRight": "刪除右方文字", + "workbench.action.terminal.new": "建立新的整合式終端機", + "workbench.action.terminal.new.short": "新增終端機", + "workbench.action.terminal.focus": "聚焦終端機", + "workbench.action.terminal.focusNext": "聚焦下一個終端機", + "workbench.action.terminal.focusAtIndex": "聚焦終端機 {0}", + "workbench.action.terminal.focusPrevious": "聚焦上一個終端機", + "workbench.action.terminal.paste": "貼入使用中的終端機", + "workbench.action.terminal.DefaultShell": "選取預設殼層", + "workbench.action.terminal.runSelectedText": "在使用中的終端機執行選取的文字", + "workbench.action.terminal.runActiveFile": "在使用中的終端機執行使用中的檔案", + "workbench.action.terminal.runActiveFile.noFile": "只有磁碟上的檔案可在終端機執行", + "workbench.action.terminal.switchTerminalInstance": "切換終端機執行個體", + "workbench.action.terminal.scrollDown": "向下捲動 (行)", + "workbench.action.terminal.scrollDownPage": "向下捲動 (頁)", + "workbench.action.terminal.scrollToBottom": "捲動至底端", + "workbench.action.terminal.scrollUp": "向上捲動 (行)", + "workbench.action.terminal.scrollUpPage": "向上捲動 (頁)", + "workbench.action.terminal.scrollToTop": "捲動至頂端", + "workbench.action.terminal.clear": "清除", + "workbench.action.terminal.allowWorkspaceShell": "允許工作區外觀配置", + "workbench.action.terminal.disallowWorkspaceShell": "不允許工作區外觀設置", + "workbench.action.terminal.rename": "重新命名", + "workbench.action.terminal.rename.prompt": "輸入終端機名稱", + "workbench.action.terminal.focusFindWidget": "焦點尋找小工具", + "workbench.action.terminal.hideFindWidget": "隱藏尋找小工具", + "nextTerminalFindTerm": "顯示下一個尋找字詞", + "previousTerminalFindTerm": "顯示上一個尋找字詞", + "quickOpenTerm": "終端機: 切換使用中的終端機" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..d5113b4ed0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "終端機的背景色彩,允許終端機和面板的色彩不同。", + "terminal.foreground": "終端機的前景色彩。", + "terminalCursor.foreground": "終端機游標的前景色彩。", + "terminalCursor.background": "終端機游標的背景色彩。允許區塊游標重疊於自訂字元色彩。", + "terminal.ansiColor": "終端機中的 '{0}' ANSI 色彩。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..4f84de7345 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "要允許 {0} (定義為工作區設定) 在終端機中啟動嗎?", + "allow": "Allow", + "disallow": "Disallow" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..6b199c0c05 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "尋找", + "placeholder.find": "尋找", + "label.previousMatchButton": "上一個符合項", + "label.nextMatchButton": "下一個相符項", + "label.closeButton": "關閉" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..666ac1e834 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "終端機沒有任何選取項目可以複製", + "terminal.integrated.exitedWithCode": "終端機處理序已終止,結束代碼為: {0}", + "terminal.integrated.waitOnExit": "按任意鍵關閉終端機", + "terminal.integrated.launchFailed": "無法啟動終端機處理序命令 `{0}{1}` (結束代碼: {2})" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..26dc11779a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "按住Alt並點擊以追蹤連結", + "terminalLinkHandler.followLinkCmd": "按住 Cmd 並按一下滑鼠按鈕可連入連結", + "terminalLinkHandler.followLinkCtrl": "按住 Ctrl 並按一下滑鼠按鈕可連入連結" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..d022214001 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "複製", + "createNewTerminal": "新增終端機", + "paste": "貼上", + "selectAll": "全選", + "clear": "清除" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..bd9e212a9f --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "您可以選取 [自訂] 按鈕變更預設的終端機殼層。", + "customize": "自訂", + "cancel": "取消", + "never again": "確定,不要再顯示", + "terminal.integrated.chooseWindowsShell": "請選取所需的終端機殼層。您之後可以在設定中變更此選擇", + "terminalService.terminalCloseConfirmationSingular": "仍有一個使用中的終端機工作階段。要予以終止嗎?", + "terminalService.terminalCloseConfirmationPlural": "目前共有 {0} 個使用中的終端機工作階段。要予以終止嗎?", + "yes": "是" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..d9baa69cc4 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "色彩佈景主題", + "installColorThemes": "安裝其他的色彩佈景主題...", + "themes.selectTheme": "選取色彩主題(上/下鍵預覽)", + "selectIconTheme.label": "檔案圖示佈景主題", + "installIconThemes": "安裝其他的檔案圖示主題...", + "noIconThemeLabel": "無", + "noIconThemeDesc": "停用檔案圖示", + "problemChangingIconTheme": "設定圖示佈景主題時發生問題: {0}", + "themes.selectIconTheme": "選取檔案圖示佈景主題", + "generateColorTheme.label": "依目前的設定產生色彩佈景主題", + "preferences": "喜好設定", + "developer": "開發人員" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..7ca3c4a80d --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "此工作區包含只能在 [使用者設定] 中進行的設定。({0})", + "openWorkspaceSettings": "開啟工作區設定", + "openDocumentation": "深入了解", + "ignore": "略過" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/cht/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..d96158c19f --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "版本資訊: {0}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..0f0a1b9ff5 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "版本資訊", + "updateConfigurationTitle": "更新", + "updateChannel": "設定是否要從更新頻道接收自動更新。變更後需要重新啟動。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..5abb87520a --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "立即更新", + "later": "稍後", + "unassigned": "未指派", + "releaseNotes": "版本資訊", + "showReleaseNotes": "顯示版本資訊", + "downloadNow": "立即下載", + "read the release notes": "歡迎使用 {0} v{1}! 您要閱讀版本資訊嗎?", + "licenseChanged": "授權條款已有所變更,請仔細閱讀。", + "license": "閱讀授權", + "neveragain": "不要再顯示", + "64bitisavailable": "適用於 64 位元 Windows 的 {0} 現已推出! ", + "learn more": "深入了解", + "updateIsReady": "新的 {0} 更新已可用。", + "thereIsUpdateAvailable": "已有更新可用。", + "updateAvailable": "{0} 重新啟動後將會更新。", + "noUpdatesAvailable": "目前沒有可用的更新。", + "commandPalette": "命令選擇區...", + "settings": "設定", + "keyboardShortcuts": "鍵盤快速鍵(&&K)", + "selectTheme.label": "色彩佈景主題", + "themes.selectIconTheme.label": "檔案圖示佈景主題", + "not available": "無可用更新", + "checkingForUpdates": "正在查看是否有更新...", + "DownloadUpdate": "下載可用更新", + "DownloadingUpdate": "正在下載更新...", + "InstallingUpdate": "正在安裝更新...", + "restartToUpdate": "請重新啟動以更新...", + "checkForUpdates": "查看是否有更新..." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/cht/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..a823341cf2 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} 個動作", + "hideView": "從提要欄位隱藏" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/cht/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..62b0a3f9c2 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "項目必須為陣列", + "requirestring": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "optstring": "屬性 `{0}` 可以省略或必須屬於 `string` 類型", + "vscode.extension.contributes.view.id": "檢視的識別碼。請使用此識別碼透過 `vscode.window.registerTreeDataProviderForView` API 登錄資料提供者。並藉由將 `onView:${id}` 事件登錄至 `activationEvents` 以觸發啟用您的延伸模組。", + "vscode.extension.contributes.view.name": "使用人性化顯示名稱.", + "vscode.extension.contributes.view.when": "必須為 true 以顯示此檢視的條件", + "vscode.extension.contributes.views": "提供意見給編輯者", + "views.explorer": "檔案總管檢視", + "views.debug": "偵錯檢視", + "locationId.invalid": "`{0}`不是有效的識別位置" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/cht/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..b63e126846 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "顯示所有命令", + "watermark.quickOpen": "前往檔案", + "watermark.openFile": "開啟檔案", + "watermark.openFolder": "開啟資料夾", + "watermark.openFileFolder": "開啟檔案或資料夾", + "watermark.openRecent": "開啟最近使用的檔案", + "watermark.newUntitledFile": "新增未命名檔案", + "watermark.toggleTerminal": "切換終端機", + "watermark.findInFiles": "在檔案中尋找", + "watermark.startDebugging": "開始偵錯", + "watermark.unboundCommand": "未繫結", + "workbenchConfigurationTitle": "工作台", + "tips.enabled": "如有啟用,將會在編輯器未開啟時以浮水印方式顯示提示。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/cht/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..839ffecfe9 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "檔案總管", + "welcomeOverlay.search": "跨檔案搜尋", + "welcomeOverlay.git": "原始程式碼管理", + "welcomeOverlay.debug": "啟動並偵錯", + "welcomeOverlay.extensions": "管理延伸模組", + "welcomeOverlay.problems": "檢視錯誤和警告", + "welcomeOverlay.commandPalette": "尋找及執行所有命令", + "welcomeOverlay": "使用者介面概觀", + "hideWelcomeOverlay": "隱藏介面概觀", + "help": "說明" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..71b4fab186 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "編輯進化了", + "welcomePage.start": "開始", + "welcomePage.newFile": "新增檔案", + "welcomePage.openFolder": "開啟資料夾...", + "welcomePage.cloneGitRepository": "複製 Git 存放庫...", + "welcomePage.recent": "最近使用", + "welcomePage.moreRecent": "更多...", + "welcomePage.noRecentFolders": "沒有最近使用的資料夾", + "welcomePage.help": "說明", + "welcomePage.keybindingsCheatsheet": "閱覽鍵盤快速鍵", + "welcomePage.introductoryVideos": "簡介影片", + "welcomePage.tipsAndTricks": "秘訣與提示", + "welcomePage.productDocumentation": "產品文件", + "welcomePage.gitHubRepository": "GitHub 存放庫", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "啟動時顯示歡迎頁面", + "welcomePage.customize": "自訂", + "welcomePage.installExtensionPacks": "工具與語言", + "welcomePage.installExtensionPacksDescription": "安裝{0}與{1}的支援功能。", + "welcomePage.moreExtensions": "更多", + "welcomePage.installKeymapDescription": "安裝鍵盤快速鍵", + "welcomePage.installKeymapExtension": "安裝鍵盤快速鍵{0}與{1}", + "welcomePage.others": "其他", + "welcomePage.colorTheme": "彩色佈景主題", + "welcomePage.colorThemeDescription": "將編輯器及您的程式碼設定成您喜愛的外觀", + "welcomePage.learn": "深入了解", + "welcomePage.showCommands": "尋找及執行所有命令", + "welcomePage.showCommandsDescription": "從命令選擇區快速存取及搜尋命令 ({0})", + "welcomePage.interfaceOverview": "介面概觀", + "welcomePage.interfaceOverviewDescription": "使用視覺覆疊效果強調顯示 UI 的主要元件", + "welcomePage.deployToAzure": "佈署應用程式到雲端", + "welcomePage.deployToAzureDescription": "了解如何將結點應用程式部署至 Azure App Service", + "welcomePage.interactivePlayground": "Interactive Playground", + "welcomePage.interactivePlaygroundDescription": "嘗試使用逐步解說短片中的一些基本編輯器功能" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..d2f8fa95ac --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "工作台", + "workbench.startupEditor.none": "不使用編輯器開始。", + "workbench.startupEditor.welcomePage": "開啟歡迎頁面 (預設)。", + "workbench.startupEditor.newUntitledFile": "開啟新的無標題檔案。", + "workbench.startupEditor": "若未從之前的工作階段還原任何編輯器,會控制啟動時要顯示的編輯器。選取 'none' 不使用編輯器啟動,選取 'welcomePage' 開啟歡迎頁面 (預設),選取 'newUntitledFile' 開啟新的無標題檔案 (僅正在開啟空白工作區適用)。", + "help": "說明" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..11b3060b9b --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "歡迎使用", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "顯示 Azure 擴充功能", + "welcomePage.docker": "Docker", + "welcomePage.vim": "活力", + "welcomePage.sublime": "壯麗", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "支援功能{0}已被安裝。", + "welcomePage.willReloadAfterInstallingExtensionPack": "{0} 的其他支援安裝完成後,將會重新載入此視窗。", + "welcomePage.installingExtensionPack": "正在安裝 {0} 的其他支援...", + "welcomePage.extensionPackNotFound": "找不到ID為{1}的{0}支援功能.", + "welcomePage.keymapAlreadyInstalled": "已安裝 {0} 鍵盤快速鍵。", + "welcomePage.willReloadAfterInstallingKeymap": "{0} 鍵盤快速鍵安裝完成後,將會重新載入此視窗。", + "welcomePage.installingKeymap": "正在安裝 {0} 鍵盤快速鍵...", + "welcomePage.keymapNotFound": "找不到識別碼為 {1} 的 {0} 鍵盤快速鍵。", + "welcome.title": "歡迎使用", + "welcomePage.openFolderWithPath": "透過路徑 {1} 開啟資料夾 {0}", + "welcomePage.extensionListSeparator": ",", + "welcomePage.installKeymap": "安裝 {0} 按鍵對應", + "welcomePage.installExtensionPack": "安裝 {0} 的其他支援", + "welcomePage.installedKeymap": "已安裝 {0} 按鍵對應", + "welcomePage.installedExtensionPack": "已安裝 {0} 支援", + "ok": "確定", + "details": "詳細資料", + "cancel": "取消", + "welcomePage.buttonBackground": "起始頁面按鈕的背景色彩.", + "welcomePage.buttonHoverBackground": "起始頁面暫留於按鈕的背景色彩" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..b86cecf942 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "Interactive Playground", + "editorWalkThrough": "Interactive Playground" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..1ab50a5842 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "Interactive Playground", + "help": "說明", + "interactivePlayground": "Interactive Playground" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..35ca4016af --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "向上捲動 (行)", + "editorWalkThrough.arrowDown": "向下捲動 (行)", + "editorWalkThrough.pageUp": "向上捲動 (頁)", + "editorWalkThrough.pageDown": "向下捲動 (頁)" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..9321fd8e42 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "未繫結", + "walkThrough.gitNotFound": "您的系統上似乎未安裝 Git。", + "walkThrough.embeddedEditorBackground": "編輯器互動區塊的背景色彩." +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/cht/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..f806dc6457 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "提供組態設定。", + "vscode.extension.contributes.configuration.title": "設定的摘要。此標籤將會在設定檔中作為分隔註解使用。", + "vscode.extension.contributes.configuration.properties": "組態屬性的描述。", + "scope.window.description": "視窗特定組態,可在使用者或工作區設定中予以設定。", + "scope.resource.description": "資源特定設定,可在使用者、工作區或資料夾設定中予以設定。", + "scope.description": "組態適用的範圍。可用的範圍為「視窗」和「資源」。", + "invalid.type": "如果已設定,'configuration.type' 必須設定為物件", + "invalid.title": "'configuration.title' 必須是字串", + "vscode.extension.contributes.defaultConfiguration": "依語言貢獻預設編輯器組態設定。", + "invalid.properties": "'configuration.properties' 必須是物件", + "workspaceConfig.folders.description": "要在工作區中載入之資料夾的清單。必須是檔案路徑,例如 `/root/folderA` 或 `./folderA` 即為會對工作區檔案位置解析的相關路徑。", + "workspaceConfig.folder.description": "檔案路徑,例如 `/root/folderA` 或 `./folderA` 即為會對工作區檔案位置解析的相關路徑。", + "workspaceConfig.settings.description": "工作區設定" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/cht/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..e4b30811da --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "開啟設定", + "close": "關閉", + "saveAndRetry": "儲存設定並重啟", + "errorUnknownKey": "因為 {1} 非已註冊的組態,所以無法寫入至 {0}。", + "errorInvalidFolderConfiguration": "因為 {0} 不支援資料夾資源範圍,所以無法寫入至資料夾設定。", + "errorInvalidUserTarget": "因為 {0} 不支援全域範圍,所以無法寫入至使用者設定。", + "errorInvalidFolderTarget": "因為未提供資源,所以無法寫入至資料夾設定。", + "errorNoWorkspaceOpened": "因為未開啟工作區,所以無法寫入至 {0}。請先開啟工作區,再試一次。", + "errorInvalidConfiguration": "無法寫入設定.請開啟**使用者設定**並修正錯誤/警告後再試一次.", + "errorInvalidConfigurationWorkspace": "無法寫入設定.請開啟**工作區設定**並修正檔案中的錯誤/警告後再試一次.", + "errorConfigurationFileDirty": "無法寫入設定,因為檔案已變更.請儲存**使用者設定**後再試一次", + "errorConfigurationFileDirtyWorkspace": "無法寫入設定,因檔案已變更.請儲存**工作區設定**後再試一次.", + "userTarget": "使用者設定", + "workspaceTarget": "工作區設定", + "folderTarget": "資料夾設定" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/cht/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..fe8c6b25e7 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "無法寫入檔案.請開啟檔案並修正錯誤/警告後再試一次.", + "errorFileDirty": "無法寫入檔案,因為檔案已變更.請儲存檔案後再試一次" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/cht/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..ba18e6d709 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "遙測", + "telemetry.enableCrashReporting": "允許將損毀報告傳送給 Microsoft。\n此選項需要重新啟動才會生效。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/cht/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..1fe49d178e --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "延伸主機未於 10 秒內開始,可能在第一行就已停止,並需要偵錯工具才能繼續。", + "extensionHostProcess.startupFail": "延伸主機未在 10 秒內啟動,可能發生了問題。", + "extensionHostProcess.error": "延伸主機發生錯誤: {0}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..3b61969d5e --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "無法剖析 {0}: {1}。", + "fileReadFail": "無法讀取檔案 {0}: {1}。", + "jsonsParseFail": "無法剖析 {0} 或 {1}: {2}。", + "missingNLSKey": "找不到金鑰 {0} 的訊息。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..d03efd94dd --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "開發人員工具", + "restart": "重新啟動延伸主機", + "extensionHostProcess.crash": "延伸主機意外終止。", + "extensionHostProcess.unresponsiveCrash": "因為延伸主機沒有回應,所以意外終止。", + "overwritingExtension": "正在以 {1} 覆寫延伸模組 {0}。", + "extensionUnderDevelopment": "正在載入位於 {0} 的開發延伸模組" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/cht/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..b035bba833 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "需要 Microsoft .NET Framework 4.5。請連入此連結進行安裝。", + "installNet": "下載 .NET Framework 4.5", + "neverShowAgain": "不要再顯示", + "trashFailed": "無法將 '{0}' 移動至垃圾" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/cht/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..0a0ab92afc --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "檔案資源 ({0}) 無效", + "fileIsDirectoryError": "檔案是目錄 ({0})", + "fileNotModifiedError": "未修改檔案的時間", + "fileTooLargeError": "檔案太大無法開啟", + "fileBinaryError": "檔案似乎是二進位檔,因此無法當做文字開啟", + "fileNotFoundError": "找不到檔案 ({0})", + "fileMoveConflict": "無法移動/複製。目的地已存在檔案。", + "unableToMoveCopyError": "無法移動/複製。檔案會取代其所在的資料夾。", + "foldersCopyError": "資料夾不能複製到工作區。請選取個別檔案進行複製。", + "fileModifiedError": "修改檔案的時間", + "fileReadOnlyError": "檔案為唯讀" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/cht/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..0296c2d55a --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "因為檔案已變更,所以無法寫入。請儲存按鍵繫結檔案,然後再試一次。", + "parseErrors": "無法寫入按鍵繫結關係。請開啟「按鍵繫結檔案」更正其中的錯誤/警告,然後再試一次。", + "errorInvalidConfiguration": "無法寫入按鍵繫結關係。**按鍵繫結關係檔案** 包含了類型不是 Array 的物件。請開啟檔案加以清除,然後再試一次。", + "emptyKeybindingsHeader": "將您的按鍵組合放入此檔案中以覆寫預設值" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/cht/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..cc4287b60d --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "必須是非空白值。", + "requirestring": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "optstring": "屬性 `{0}` 可以省略或必須屬於 `string` 類型", + "vscode.extension.contributes.keybindings.command": "觸發按鍵繫結關係時所要執行命令的識別碼。", + "vscode.extension.contributes.keybindings.key": "按鍵或按鍵順序 (以加號分隔按鍵並以空格分隔順序,例如 Ctrl+O 及 Ctrl+L L 進行同步選取)。", + "vscode.extension.contributes.keybindings.mac": "Mac 特定按鍵或按鍵順序。", + "vscode.extension.contributes.keybindings.linux": "Linux 特定按鍵或按鍵順序。", + "vscode.extension.contributes.keybindings.win": "Windows 特定按鍵或按鍵順序。", + "vscode.extension.contributes.keybindings.when": "按鍵為使用中時的條件。", + "vscode.extension.contributes.keybindings": "提供按鍵繫結關係。", + "invalid.keybindings": "`contributes.{0}` 無效: {1}", + "unboundCommands": "其他可用命令如下: ", + "keybindings.json.title": "按鍵繫結關係組態", + "keybindings.json.key": "按鍵或按鍵順序 (以空格分隔)", + "keybindings.json.command": "所要執行命令的名稱", + "keybindings.json.when": "按鍵為使用中時的條件。", + "keybindings.json.args": "要傳遞至命令加以執行的引數。", + "keyboardConfigurationTitle": "鍵盤", + "dispatch": "控制按下按鍵時的分派邏輯 (使用 'keydown.code' (建議使用) 或 'keydown.keyCode')。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/cht/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..bd5d71da3c --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "錯誤: {0}", + "alertWarningMessage": "警告: {0}", + "alertInfoMessage": "資訊: {0}", + "error": "錯誤", + "warning": "警告", + "info": "資訊", + "close": "關閉" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/cht/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..600844ef96 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "是(&&Y)", + "cancelButton": "取消" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/cht/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..6442bcb2ca --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "提供語言宣告。", + "vscode.extension.contributes.languages.id": "語言的識別碼。", + "vscode.extension.contributes.languages.aliases": "語言的別名名稱。", + "vscode.extension.contributes.languages.extensions": "與語言相關聯的副檔名。", + "vscode.extension.contributes.languages.filenames": "與語言相關聯的檔案名稱。", + "vscode.extension.contributes.languages.filenamePatterns": "與語言相關聯的檔案名稱 Glob 模式。", + "vscode.extension.contributes.languages.mimetypes": "與語言相關聯的 MIME 類型。", + "vscode.extension.contributes.languages.firstLine": "規則運算式,符合語言檔案的第一行。", + "vscode.extension.contributes.languages.configuration": "檔案的相對路徑,其中該檔案包含語言組態選項。", + "invalid": "`contributes.{0}` 無效。必須是陣列。", + "invalid.empty": "`contributes.{0}` 值為空值", + "require.id": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "opt.extensions": "屬性 '{0}' 可以省略且必須屬於 `string[]` 類型", + "opt.filenames": "屬性 '{0}' 可以省略且必須屬於 `string[]` 類型", + "opt.firstLine": "屬性 '{0}' 可以省略且必須屬於 `string` 類型", + "opt.configuration": "屬性 '{0}' 可以省略且必須屬於 `string` 類型", + "opt.aliases": "屬性 '{0}' 可以省略且必須屬於 `string[]` 類型", + "opt.mimetypes": "屬性 '{0}' 可以省略且必須屬於 `string[]` 類型" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/cht/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..441530a01b --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1}", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/cht/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..3d75f41300 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "提供 textmate 權杖化工具。", + "vscode.extension.contributes.grammars.language": "要提供此語法的目標語言識別碼。", + "vscode.extension.contributes.grammars.scopeName": "tmLanguage 檔案所使用的 textmate 範圍名稱。", + "vscode.extension.contributes.grammars.path": "tmLanguage 檔案的路徑。此路徑是擴充功能資料夾的相對路徑,而且一般會以 './syntaxes/' 開頭。", + "vscode.extension.contributes.grammars.embeddedLanguages": "如果此文法包含內嵌語言,即為範圍名稱到語言識別碼的對應。", + "vscode.extension.contributes.grammars.injectTo": "要插入此文法的語言範圍名稱清單。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/cht/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..7b99c479e3 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "`contributes.{0}.language` 中的不明語言。提供的值: {1}", + "invalid.scopeName": "`contributes.{0}.scopeName` 中的預期字串。提供的值: {1}", + "invalid.path.0": "'contributes.{0}.path' 中應有字串。提供的值: {1}", + "invalid.injectTo": "`contributes.{0}.injectTo` 中的值無效。必須是語言範圍名稱的陣列。提供的值: {1}", + "invalid.embeddedLanguages": "`contributes.{0}.embeddedLanguages` 中的值無效。必須是從範圍名稱到語言的物件對應。提供的值: {1}", + "invalid.path.1": "擴充功能資料夾 ({2}) 應包含 'contributes.{0}.path' ({1})。這可能會導致擴充功能無法移植。", + "no-tm-grammar": "此語言未註冊任何 TM 文法。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/cht/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..2e1ce811ad --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "檔案已變更。請先儲存,再以其他編碼重新開啟。", + "genericSaveError": "無法儲存 '{0}': {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/cht/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..87ebd8db84 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "無法備份檔案 (錯誤: {0}),請嘗試儲存您的檔案再結束。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/cht/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..3590b06856 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "要儲存對 {0} 所做的變更嗎?", + "saveChangesMessages": "要儲存對下列 {0} 個檔案所做的變更嗎?", + "moreFile": "...另外 1 個檔案未顯示", + "moreFiles": "...另外 {0} 個檔案未顯示", + "saveAll": "全部儲存(&&S)", + "save": "儲存(&&S)", + "dontSave": "不要儲存(&&N)", + "cancel": "取消", + "saveChangesDetail": "如果您不儲存變更,這些變更將會遺失。", + "allFiles": "所有檔案", + "noExt": "無擴充功能" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/cht/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..3985cbc1a0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "權杖的色彩與樣式。", + "schema.token.foreground": "權杖的前景色彩。", + "schema.token.fontStyle": "規則的字型樣式:「斜體」、「粗體」或「底線」之一或其組合", + "schema.fontStyle.error": "字形樣式必須是「斜體」、「粗體」及「底線」的組合", + "schema.properties.name": "規則的描述。", + "schema.properties.scope": "針對此規則符合的範圍選取器。", + "schema.tokenColors.path": "tmTheme 檔案的路徑 (相對於目前檔案)。", + "schema.colors": "反白顯示語法時的色彩" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/cht/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..a3bdaee4c2 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "展開資料夾的資料夾圖示。展開資料夾圖示是選擇性的。如果未設定,即顯示為資料夾定義的圖示。", + "schema.folder": "摺疊資料夾的資料夾圖示,如果未設定 folderExpanded,就也是展開資料夾的圖示。", + "schema.file": "預設檔案圖示,顯示於所有不符合任何副檔名、檔案名稱或語言識別碼的檔案。", + "schema.folderNames": "建立資料夾名稱與圖示的關聯。物件索引鍵為資料夾名稱,但不包括任何路徑區段。不允許任何圖樣或萬用字元。資料夾名稱比對不區分大小寫。", + "schema.folderName": "關聯的圖示定義識別碼。", + "schema.folderNamesExpanded": "針對展開的資料夾建立資料夾名稱與圖示的關聯。物件索引鍵為資料夾名稱,但不包括任何路徑區段。不允許任何圖樣或萬用字元。資料夾名稱比對不區分大小寫。", + "schema.folderNameExpanded": "關聯的圖示定義識別碼。", + "schema.fileExtensions": "建立副檔名與圖示的關聯。物件索引鍵為副檔名。副檔名為檔案名稱的最後一個區段,在最後一個點之後 (不包括點) 。比較副檔名時不區分大小寫。", + "schema.fileExtension": "關聯的圖示定義識別碼。", + "schema.fileNames": "建立檔案名稱與圖示的關聯。物件索引鍵為完整檔案名稱,但不包括任何路徑區段。檔案名稱可以包含點和副檔名。不允許任何圖樣或萬用字元。檔案名稱比對不區分大小寫。", + "schema.fileName": "關聯的圖示定義識別碼。", + "schema.languageIds": "關聯語言與圖示。物件索引鍵為語言貢獻點中定義的語言識別碼。", + "schema.languageId": "關聯的圖示定義識別碼。", + "schema.fonts": "用在圖示定義中的字型。", + "schema.id": "字型識別碼。", + "schema.src": "字型位置。", + "schema.font-path": "字型路徑,相對於目前的圖示佈景主題檔案。", + "schema.font-format": "字型格式。", + "schema.font-weight": "字型粗細。", + "schema.font-sstyle": "字型樣式。", + "schema.font-size": "字型預設大小。", + "schema.iconDefinitions": "建立檔案與圖示關聯時可使用的所有圖示描述。", + "schema.iconDefinition": "圖示定義。物件索引鍵為定義識別碼。", + "schema.iconPath": "使用 SVG 或 PNG 時: 影像路徑。路徑相對於圖示集檔案。", + "schema.fontCharacter": "使用字符字型時: 字型中要使用的字元。", + "schema.fontColor": "使用字符字型時: 要使用的色彩。", + "schema.fontSize": "使用字型時: 文字字型的字型大小 (百分比)。如果未設定,預設為字型定義中的大小。", + "schema.fontId": "使用字型時: 字型識別碼。如果未設定,預設為第一個字型定義。", + "schema.light": "以淺色色彩佈景主題顯示檔案圖示的選擇性關聯。", + "schema.highContrast": "以高對比色彩佈景主題顯示檔案圖示的選擇性關聯。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/cht/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..1f4effb6ca --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "剖析 JSON 佈景主題檔案時發生問題: {0}", + "error.invalidformat.colors": "剖析彩色佈景主題檔案 {0} 時出現問題。屬性 'settings' 不是 'object' 類型。", + "error.invalidformat.tokenColors": "剖析色彩佈景主題檔案 {0} 時發生問題。屬性 'tokenColors' 應是指定色彩的陣列或是 TextMate 佈景主題檔案的路徑。", + "error.plist.invalidformat": "剖析 tmTheme 檔案 {0} 時出現問題。'settings' 不是陣列。", + "error.cannotparse": "剖析 tmTheme 檔案 {0} 時發生問題", + "error.cannotload": "載入 tmTheme 檔案 {0} 時發生問題: {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/cht/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..3d581c28cb --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "用在使用者設定中的圖示佈景主題識別碼。", + "vscode.extension.contributes.themes.label": "如 UI 中所示的彩色佈景主題標籤。", + "vscode.extension.contributes.themes.uiTheme": "基底佈景主題定義編輯器的色彩: 'vs' 是淺色佈景主題,'vs-dark' 是深色佈景主題。'hc-black' 是深色高對比佈景主題。", + "vscode.extension.contributes.themes.path": "tmTheme 檔案的路徑。此路徑是擴充功能資料夾的相對路徑,通常為 './themes/themeFile.tmTheme'。", + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "用在使用者設定中的圖示佈景主題識別碼。", + "vscode.extension.contributes.iconThemes.label": "以 UI 顯示的圖示佈景主題標籤。", + "vscode.extension.contributes.iconThemes.path": "圖示佈景主題定義檔案。路徑相對於擴充功能資料夾,通常為 './icons/awesome-icon-theme.json'。", + "migration.completed": "已將新的佈景主題設定新增到使用者設定。備份位於 {0}。", + "error.cannotloadtheme": "Unable to load {0}: {1}", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "`contributes.{0}.path` 中的預期字串。提供的值: {1}", + "invalid.path.1": "要包含在擴充功能資料夾 ({2}) 中的預期 `contributes.{0}.path` ({1})。這可能會使擴充功能無法移植。", + "reqid": "`contributes.{0}.id` 中的預期字串。提供的值: {1}", + "error.cannotloadicontheme": "Unable to load {0}", + "error.cannotparseicontheme": "Problems parsing file icons file: {0}", + "colorTheme": "Specifies the color theme used in the workbench.", + "colorThemeError": "Theme is unknown or not installed.", + "iconTheme": "指定在工作台中使用的圖示主題,或設定為 'null' 不顯示任何檔案圖示。", + "noIconThemeDesc": "No file icons", + "iconThemeError": "File icon theme is unknown or not installed.", + "workbenchColors": "依目前選擇的彩色佈景主題覆寫顏色", + "workbenchColors.deprecated": "此設定不再為實驗性,且已被重新命名為 'workbench.colorCustomizations'", + "workbenchColors.deprecatedDescription": "改用'workbench.colorCustomizations'", + "editorColors": "依目前選取的色彩佈景主題覆寫編輯器色彩與字型樣式。", + "editorColors.comments": "設定註解的色彩與樣式", + "editorColors.strings": "設定字串常值的色彩與樣式。", + "editorColors.keywords": "設定關鍵字的色彩與樣式。", + "editorColors.numbers": "設定數字常值的色彩與樣式。", + "editorColors.types": "設定型別宣告與參考的色彩與樣式。", + "editorColors.functions": "設定函式宣告與參考的色彩與樣式。", + "editorColors.variables": "設定變數宣告與參考的色彩與樣式。", + "editorColors.textMateRules": "使用 TextMate 佈景主題規則設定色彩與樣式 (進階)。" +} \ No newline at end of file diff --git a/i18n/deu/extensions/configuration-editing/out/extension.i18n.json b/i18n/deu/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..8815f96b43 --- /dev/null +++ b/i18n/deu/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "Beispiel" +} \ No newline at end of file diff --git a/i18n/deu/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/deu/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..f45c3434c0 --- /dev/null +++ b/i18n/deu/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "z. B. myFile.txt", + "activeEditorMedium": "e.g. myFolder/myFile.txt", + "activeEditorLong": "e.g. /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "z. B. meinOrdner1, meinOrdner2, meinOrdner3", + "rootPath": "z. B. /Users/Development/myProject", + "folderName": "z. B. meinOrdner", + "folderPath": "z. B. /Users/Development/meinOrdner", + "appName": "z. B. VS Code", + "dirty": "Ein geänderter Indikator, wenn der aktive Editor geändert wurde", + "separator": "Ein bedingtes Trennzeichen (' - '), das nur in der Umgebung von Variablen mit Werten angezeigt wird", + "assocLabelFile": "Dateien mit Erweiterung", + "assocDescriptionFile": "Ordnet alle Dateien, deren Dateinamen mit dem Globmuster übereinstimmen, der Sprache mit dem angegebenen Bezeichner zu.", + "assocLabelPath": "Dateien mit Pfad", + "assocDescriptionPath": "Ordnet alle Dateien, die mit dem absoluten Pfad des Globmusters in ihrem Pfad übereinstimmen, der Sprache mit dem angegebenen Bezeichner zu.", + "fileLabel": "Dateien nach Erweiterung", + "fileDescription": "Ordnet alle Dateien mit einer bestimmten Erweiterung zu.", + "filesLabel": "Dateien mit mehreren Erweiterungen", + "filesDescription": "Ordnet alle Dateien mit einer der Dateierweiterungen zu.", + "derivedLabel": "Dateien mit gleichgeordneten Elementen nach Name", + "derivedDescription": "Ordnet Dateien zu, die gleichgeordnete Elemente mit dem gleichen Namen und einer anderen Erweiterung besitzen.", + "topFolderLabel": "Ordner nach Name (oberste Ebene)", + "topFolderDescription": "Ordnet einen Ordner auf oberster Ebene einem bestimmten Namen zu.", + "topFoldersLabel": "Ordner mit mehreren Namen (oberste Ebene)", + "topFoldersDescription": "Ordnet mehrere Ordner auf oberster Ebene zu.", + "folderLabel": "Ordner nach Name (beliebiger Speicherort)", + "folderDescription": "Zuordnen eines Ordners mit einem bestimmten Namen an einem beliebigen Speicherort.", + "falseDescription": "Deaktiviert das Muster.", + "trueDescription": "Aktiviert das Muster.", + "siblingsDescription": "Ordnet Dateien zu, die gleichgeordnete Elemente mit dem gleichen Namen und einer anderen Erweiterung besitzen.", + "languageSpecificEditorSettings": "Sprachspezifische Editor-Einstellungen", + "languageSpecificEditorSettingsDescription": "Editor-Einstellungen für Sprache überschreiben" +} \ No newline at end of file diff --git a/i18n/deu/extensions/css/client/out/cssMain.i18n.json b/i18n/deu/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..b2a3b44c98 --- /dev/null +++ b/i18n/deu/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "CSS-Sprachserver" +} \ No newline at end of file diff --git a/i18n/deu/extensions/css/package.i18n.json b/i18n/deu/extensions/css/package.i18n.json new file mode 100644 index 0000000000..cb3094adee --- /dev/null +++ b/i18n/deu/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "Ungültige Parameteranzahl.", + "css.lint.boxModel.desc": "Verwenden Sie width oder height nicht beim Festlegen von padding oder border", + "css.lint.compatibleVendorPrefixes.desc": "Stellen Sie beim Verwenden vom anbieterspezifischen Präfix sicher, dass alle anderen anbieterspezifischen Eigenschaften miteinbezogen werden", + "css.lint.duplicateProperties.desc": "Keine doppelten Formatvorlagendefinitionen verwenden", + "css.lint.emptyRules.desc": "Keine leeren Regelsätze verwenden", + "css.lint.float.desc": "Die Verwendung von \"float\" vermeiden. float-Eigenschaften führen zu anfälligem CSS, das schnell nicht mehr funktioniert, wenn sich ein Aspekt des Layouts ändert.", + "css.lint.fontFaceProperties.desc": "@font-face-Regel muss die Eigenschaften \"src\" und \"font-family\" definieren.", + "css.lint.hexColorLength.desc": "Hexfarben müssen aus drei oder sechs Hexzahlen bestehen", + "css.lint.idSelector.desc": "Selektoren sollten keine IDs enthalten, da diese Regeln zu eng mit HTML verknüpft sind.", + "css.lint.ieHack.desc": "IE-Hacks sind nur für die Unterstützung von IE7 und älter erforderlich", + "css.lint.important.desc": "Verwendung von „!important“ vermeiden. Damit wird angegeben, dass die Spezifität vom gesamten CSS außer Kontrolle geraten ist und umgestaltet werden muss.", + "css.lint.importStatement.desc": "Importanweisungen können nicht parallel geladen werden.", + "css.lint.propertyIgnoredDueToDisplay.desc": "Die Eigenschaft wird aufgrund der Anzeige ignoriert. Mit \"display: inline\" besitzen die width-, height-, margin-top-, margin-bottom- und float-Eigenschaften z. B. keine Auswirkung.", + "css.lint.universalSelector.desc": "Es ist bekannt, dass der Universalselektor (*) langsam ist.", + "css.lint.unknownProperties.desc": "Unbekannte Eigenschaft.", + "css.lint.unknownVendorSpecificProperties.desc": "Unbekannte anbieterspezifische Eigenschaft.", + "css.lint.vendorPrefix.desc": "Beim Verwenden von anbieterspezifischen Präfix auch die Standardeigenschaft miteinbeziehen", + "css.lint.zeroUnits.desc": "Keine Einheit für Null erforderlich", + "css.trace.server.desc": "Verfolgt die Kommunikation zwischen VS Code und dem CSS-Sprachserver.", + "css.validate.title": "Steuert die CSS-Validierung und Problemschweregrade.", + "css.validate.desc": "Aktiviert oder deaktiviert alle Überprüfungen.", + "less.lint.argumentsInColorFunction.desc": "Ungültige Parameteranzahl.", + "less.lint.boxModel.desc": "Verwenden Sie width oder height nicht beim Festlegen von padding oder border", + "less.lint.compatibleVendorPrefixes.desc": "Stellen Sie beim Verwenden vom anbieterspezifischen Präfix sicher, dass alle anderen anbieterspezifischen Eigenschaften miteinbezogen werden", + "less.lint.duplicateProperties.desc": "Keine doppelten Formatvorlagendefinitionen verwenden", + "less.lint.emptyRules.desc": "Keine leeren Regelsätze verwenden", + "less.lint.float.desc": "Die Verwendung von \"float\" vermeiden. float-Eigenschaften führen zu anfälligem CSS, das schnell nicht mehr funktioniert, wenn sich ein Aspekt des Layouts ändert.", + "less.lint.fontFaceProperties.desc": "@font-face-Regel muss die Eigenschaften \"src\" und \"font-family\" definieren.", + "less.lint.hexColorLength.desc": "Hexfarben müssen aus drei oder sechs Hexzahlen bestehen", + "less.lint.idSelector.desc": "Selektoren sollten keine IDs enthalten, da diese Regeln zu eng mit HTML verknüpft sind.", + "less.lint.ieHack.desc": "IE-Hacks sind nur für die Unterstützung von IE7 und älter erforderlich", + "less.lint.important.desc": "Verwendung von „!important“ vermeiden. Damit wird angegeben, dass die Spezifität vom gesamten CSS außer Kontrolle geraten ist und umgestaltet werden muss.", + "less.lint.importStatement.desc": "Importanweisungen können nicht parallel geladen werden.", + "less.lint.propertyIgnoredDueToDisplay.desc": "Die Eigenschaft wird aufgrund der Anzeige ignoriert. Mit \"display: inline\" besitzen die width-, height-, margin-top-, margin-bottom- und float-Eigenschaften z. B. keine Auswirkung.", + "less.lint.universalSelector.desc": "Es ist bekannt, dass der Universalselektor (*) langsam ist.", + "less.lint.unknownProperties.desc": "Unbekannte Eigenschaft.", + "less.lint.unknownVendorSpecificProperties.desc": "Unbekannte anbieterspezifische Eigenschaft.", + "less.lint.vendorPrefix.desc": "Beim Verwenden von anbieterspezifischen Präfix auch die Standardeigenschaft miteinbeziehen", + "less.lint.zeroUnits.desc": "Keine Einheit für Null erforderlich", + "less.validate.title": "Steuert die LESS-Validierung und Problemschweregrade.", + "less.validate.desc": "Aktiviert oder deaktiviert alle Überprüfungen.", + "scss.lint.argumentsInColorFunction.desc": "Ungültige Parameteranzahl.", + "scss.lint.boxModel.desc": "Verwenden Sie width oder height nicht beim Festlegen von padding oder border", + "scss.lint.compatibleVendorPrefixes.desc": "Stellen Sie beim Verwenden vom anbieterspezifischen Präfix sicher, dass alle anderen anbieterspezifischen Eigenschaften miteinbezogen werden", + "scss.lint.duplicateProperties.desc": "Keine doppelten Formatvorlagendefinitionen verwenden", + "scss.lint.emptyRules.desc": "Keine leeren Regelsätze verwenden", + "scss.lint.float.desc": "Die Verwendung von \"float\" vermeiden. float-Eigenschaften führen zu anfälligem CSS, das schnell nicht mehr funktioniert, wenn sich ein Aspekt des Layouts ändert.", + "scss.lint.fontFaceProperties.desc": "@font-face-Regel muss die Eigenschaften \"src\" und \"font-family\" definieren.", + "scss.lint.hexColorLength.desc": "Hexfarben müssen aus drei oder sechs Hexzahlen bestehen", + "scss.lint.idSelector.desc": "Selektoren sollten keine IDs enthalten, da diese Regeln zu eng mit HTML verknüpft sind.", + "scss.lint.ieHack.desc": "IE-Hacks sind nur für die Unterstützung von IE7 und älter erforderlich", + "scss.lint.important.desc": "Verwendung von „!important“ vermeiden. Damit wird angegeben, dass die Spezifität vom gesamten CSS außer Kontrolle geraten ist und umgestaltet werden muss.", + "scss.lint.importStatement.desc": "Importanweisungen können nicht parallel geladen werden.", + "scss.lint.propertyIgnoredDueToDisplay.desc": "Die Eigenschaft wird aufgrund der Anzeige ignoriert. Mit \"display: inline\" besitzen die width-, height-, margin-top-, margin-bottom- und float-Eigenschaften z. B. keine Auswirkung.", + "scss.lint.universalSelector.desc": "Es ist bekannt, dass der Universalselektor (*) langsam ist.", + "scss.lint.unknownProperties.desc": "Unbekannte Eigenschaft.", + "scss.lint.unknownVendorSpecificProperties.desc": "Unbekannte anbieterspezifische Eigenschaft.", + "scss.lint.vendorPrefix.desc": "Beim Verwenden von anbieterspezifischen Präfix auch die Standardeigenschaft miteinbeziehen", + "scss.lint.zeroUnits.desc": "Keine Einheit für Null erforderlich", + "scss.validate.title": "Steuert die SCSS-Überprüfung und Problemschweregrade.", + "scss.validate.desc": "Aktiviert oder deaktiviert alle Überprüfungen.", + "less.colorDecorators.enable.desc": "Aktiviert oder deaktiviert Farb-Decorators", + "scss.colorDecorators.enable.desc": "Aktiviert oder deaktiviert Farb-Decorators", + "css.colorDecorators.enable.desc": "Aktiviert oder deaktiviert Farb-Decorators", + "css.colorDecorators.enable.deprecationMessage": "Die Einstellung \"css.colorDecorators.enable\" ist veraltet und wurde durch \"editor.colorDecorators\" ersetzt.", + "scss.colorDecorators.enable.deprecationMessage": "Die Einstellung \"scss.colorDecorators.enable\" ist veraltet und wurde durch \"editor.colorDecorators\" ersetzt.", + "less.colorDecorators.enable.deprecationMessage": "Die Einstellung \"less.colorDecorators.enable\" ist veraltet und wurde durch \"editor.colorDecorators\" ersetzt." +} \ No newline at end of file diff --git a/i18n/deu/extensions/emmet/package.i18n.json b/i18n/deu/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..2460cdd4de --- /dev/null +++ b/i18n/deu/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "Mit Abkürzung umschließen", + "command.wrapIndividualLinesWithAbbreviation": "Einzelne Zeilen mit Abkürzung umschließen", + "command.removeTag": "Tag entfernen", + "command.updateTag": "Tag aktualisieren", + "command.matchTag": "Gehe zu übereinstimmendem Paar", + "command.balanceIn": "Ausgleichen (einwärts)", + "command.balanceOut": "Ausgleichen (auswärts)", + "command.prevEditPoint": "Gehe zum vorherigen Bearbeitungspunkt", + "command.nextEditPoint": "Gehe zum nächsten Bearbeitungspunkt", + "command.mergeLines": "Zeilen mergen", + "command.selectPrevItem": "Vorheriges Element auswählen", + "command.selectNextItem": "Nächstes Element auswählen", + "command.splitJoinTag": "Tag teilen/verknüpfen", + "command.toggleComment": "Kommentar ein-/ausschalten", + "command.evaluateMathExpression": "Mathematischen Ausdruck auswerten", + "command.updateImageSize": "Bildgröße aktualisieren", + "command.reflectCSSValue": "CSS-Wert reflektieren", + "command.incrementNumberByOne": "Um 1 erhöhen", + "command.decrementNumberByOne": "Um 1 verringern", + "command.incrementNumberByOneTenth": "Um 0,1 erhöhen", + "command.decrementNumberByOneTenth": "Um 0,1 verringern", + "command.incrementNumberByTen": "Um 10 erhöhen", + "command.decrementNumberByTen": "Um 10 verringern", + "emmetSyntaxProfiles": "Definieren Sie das Profil für die angegebene Syntax, oder verwenden Sie Ihr eigenes Profil mit bestimmten Regeln.", + "emmetExclude": "Ein Array von Sprachen, in dem Emmet-Abkürzungen nicht erweitert werden sollen.", + "emmetExtensionsPath": "Pfad zu einem Ordner mit Emmet-Profilen und Ausschnitten.", + "emmetShowExpandedAbbreviation": "Zeigt erweiterte Emmet-Abkürzungen als Vorschläge an.\nDie Option inMarkupAndStylesheetFilesOnly gilt für HTML, HAML, Jade, Slim, XML, XSL, CSS, SCSS, Sass, Less und Stylus.\nDie Option \"always\" gilt unabhängig vom Markup/CSS für alle Teile der Datei.", + "emmetShowAbbreviationSuggestions": "Zeigt mögliche Emmet-Abkürzungen als Vorschläge an. Diese Option gilt nicht in Stylesheets oder wenn emmet.showExpandedAbbreviation auf \"never\" festgelegt ist.", + "emmetIncludeLanguages": "Aktivieren Sie Emmet-Abkürzungen in Sprachen, die nicht standardmäßig unterstützt werden. Fügen Sie hier ein Mapping zwischen der Sprache und der von Emmet unterstützten Sprache hinzu.\nBeispiel: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "In Emmet-Ausschnitten zu verwendende Variablen", + "emmetTriggerExpansionOnTab": "Wenn aktiviert, werden Emmet-Abkürzungen beim Drücken der TAB-TASTE erweitert.", + "emmetPreferences": "Einstellungen, die zum Ändern des Verhaltens einiger Aktionen und Konfliktlöser von Emmet verwendet werden.", + "emmetPreferencesIntUnit": "Standardeinheit für Integerwerte", + "emmetPreferencesFloatUnit": "Standardeinheit für Floatwerte", + "emmetPreferencesCssAfter": "Symbol, das beim Erweitern von CSS-Abkürzungen am Ende der CSS-Eigenschaft eingefügt werden soll", + "emmetPreferencesSassAfter": "Symbol, das beim Erweitern von CSS-Abkürzungen in Sass-Dateien am Ende der CSS-Eigenschaft eingefügt werden soll", + "emmetPreferencesStylusAfter": "Symbol, das beim Erweitern von CSS-Abkürzungen in Stylus-Dateien am Ende der CSS-Eigenschaft eingefügt werden soll", + "emmetPreferencesCssBetween": "Symbol, das beim Erweitern von CSS-Abkürzungen zwischen der CSS-Eigenschaft und dem Wert eingefügt werden soll", + "emmetPreferencesSassBetween": "Symbol, das beim Erweitern von CSS-Abkürzungen in Sass-Dateien zwischen der CSS-Eigenschaft und dem Wert eingefügt werden soll", + "emmetPreferencesStylusBetween": "Symbol, das beim Erweitern von CSS-Abkürzungen in Stylus-Dateien zwischen der CSS-Eigenschaft und dem Wert eingefügt werden soll", + "emmetShowSuggestionsAsSnippets": "Bei TRUE werden die Emmet-Vorschläge als Ausschnitte angezeigt, mit denen Sie sie der editor.snippetSuggestions-Einstellung entsprechend anordnen können." +} \ No newline at end of file diff --git a/i18n/deu/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/deu/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..79d4c68b0f --- /dev/null +++ b/i18n/deu/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "Für Bilder muss das HTTPS-Protokoll verwendet werden.", + "svgsNotValid": "SVGs sind keine gültige Bildquelle.", + "embeddedSvgsNotValid": "Eingebettete SVGs sind keine gültige Bildquelle.", + "dataUrlsNotValid": "Daten-URLs sind keine gültige Bildquelle.", + "relativeUrlRequiresHttpsRepository": "Relative Bild-URLs erfordern die Angabe eines Repositorys mit dem HTTPS-Protokoll in der Datei \"package.json\".", + "relativeIconUrlRequiresHttpsRepository": "Ein Symbol erfordert die Angabe eines Repositorys mit dem HTTPS-Protokoll in dieser Datei \"package.json\".", + "relativeBadgeUrlRequiresHttpsRepository": "Relative Badge-URLs erfordern die Angabe eines Repositorys mit dem HTTPS-Protokoll in dieser Datei \"package.json\"." +} \ No newline at end of file diff --git a/i18n/deu/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/deu/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..0530c1929d --- /dev/null +++ b/i18n/deu/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "Sprachspezifische Editor-Einstellungen", + "languageSpecificEditorSettingsDescription": "Editor-Einstellungen für Sprache überschreiben" +} \ No newline at end of file diff --git a/i18n/deu/extensions/git/out/askpass-main.i18n.json b/i18n/deu/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..a1f15af12b --- /dev/null +++ b/i18n/deu/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "Fehlende oder ungültige Anmeldeinformationen." +} \ No newline at end of file diff --git a/i18n/deu/extensions/git/out/commands.i18n.json b/i18n/deu/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..516578ca1e --- /dev/null +++ b/i18n/deu/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "Tag bei {0}", + "remote branch at": "Remotebranch unter {0}", + "create branch": "$(plus) Neuen Branch erstellen", + "repourl": "Repository-URL", + "parent": "Übergeordnetes Verzeichnis", + "cloning": "Git-Repository wird geklont...", + "openrepo": "Repository öffnen", + "proposeopen": "Möchten Sie das geklonte Repository öffnen?", + "path to init": "Ordnerpfad", + "provide path": "Geben Sie einen Ordnerpfad zum Initialisieren eines Git-Repositorys an.", + "HEAD not available": "Es ist keine HEAD-Version von \"{0}\" verfügbar.", + "confirm stage files with merge conflicts": "Möchten Sie {0} Dateien mit Mergingkonflikten bereitstellen?", + "confirm stage file with merge conflicts": "Möchten Sie {0} mit Mergingkonflikten bereitstellen?", + "yes": "Ja", + "confirm revert": "Möchten Sie ausgewählten Änderungen in {0} wirklich zurücksetzen?", + "revert": "Änderungen zurücksetzen", + "discard": "Änderungen verwerfen", + "confirm delete": "Möchten Sie \"{0}\" wirklich LÖSCHEN?", + "delete file": "Datei löschen", + "confirm discard": "Möchten Sie die Änderungen in {0} wirklich verwerfen?", + "confirm discard multiple": "Möchten Sie wirklich Änderungen in {0} Dateien verwerfen?", + "warn untracked": "Dies wird {0} nicht verfolgte Dateien LÖSCHEN!", + "confirm discard all single": "Möchten Sie die Änderungen in {0} wirklich verwerfen?", + "confirm discard all": "Möchten Sie ALLE Änderungen in {0} Dateien verwerfen?\nDies kann NICHT rückgängig gemacht werden.\nIhr aktueller Arbeitssatz geht dadurch DAUERHAFT verloren.", + "discardAll multiple": "Eine Datei verwerfen", + "discardAll": "Alle {0} Dateien verwerfen", + "confirm delete multiple": "Möchten Sie {0} Dateien LÖSCHEN?", + "delete files": "Dateien löschen", + "there are untracked files single": "Die folgende nicht verfolgte Datei wird VOM DATENTRÄGER GELÖSCHT, wenn sie verworfen wird: {0}.", + "there are untracked files": "Es sind {0} nicht verfolgte Dateien vorhanden, die VOM DATENTRÄGER GELÖSCHT werden, wenn sie verworfen werden.", + "confirm discard all 2": "{0}\n\nDies kann NICHT rückgängig gemacht werden, und Ihr aktueller Arbeitssatz geht DAUERHAFT verloren.", + "yes discard tracked": "1 verfolgte Datei verwerfen", + "yes discard tracked multiple": "{0} verfolgte Dateien verwerfen", + "no staged changes": "Es sind keine Änderungen bereitgestellt.\n\nMöchten Sie alle Ihre Änderungen automatisch bereitstellen und direkt committen?", + "always": "Immer", + "no changes": "Keine Änderungen zum Speichern vorhanden.", + "commit message": "Commit-Nachricht", + "provide commit message": "Geben Sie eine Commit-Nachrichte ein.", + "select a ref to checkout": "Referenz zum Auschecken auswählen", + "branch name": "Branchname", + "provide branch name": "Geben Sie einen Branchnamen an.", + "select branch to delete": "Wählen Sie einen Branch zum Löschen aus", + "confirm force delete branch": "Der Branch '{0}' ist noch nicht vollständig zusammengeführt. Trotzdem löschen?", + "delete branch": "Branch löschen", + "select a branch to merge from": "Branch für die Zusammenführung auswählen", + "merge conflicts": "Es liegen Zusammenführungskonflikte vor. Beheben Sie die Konflikte vor dem Committen.", + "tag name": "Tag-Name", + "provide tag name": "Geben Sie einen Tagnamen an.", + "tag message": "Nachricht", + "provide tag message": "Geben Sie eine Meldung ein, um das Tag mit einer Anmerkung zu versehen.", + "no remotes to pull": "In Ihrem Repository wurden keine Remoteelemente für den Pull konfiguriert.", + "pick remote pull repo": "Remoteelement zum Pullen des Branch auswählen", + "no remotes to push": "In Ihrem Repository wurden keine Remoteelemente für den Push konfiguriert.", + "push with tags success": "Push mit Tags erfolgreich ausgeführt.", + "nobranch": "Wählen Sie ein Branch für den Push zu einem Remoteelement aus.", + "pick remote": "Remotespeicherort auswählen, an dem der Branch \"{0}\" veröffentlicht wird:", + "sync is unpredictable": "Mit dieser Aktion werden Commits per Push und Pull an und von \"{0}\" übertragen.", + "ok": "OK", + "never again": "OK, nicht mehr anzeigen", + "no remotes to publish": "In Ihrem Repository wurden keine Remoteelemente für die Veröffentlichung konfiguriert.", + "no changes stash": "Es sind keine Änderungen vorhanden, für die ein Stash ausgeführt werden kann.", + "provide stash message": "Geben Sie optional eine Stash-Nachricht ein.", + "stash message": "Stash-Nachricht", + "no stashes": "Es ist kein Stash zum Wiederherstellen vorhanden.", + "pick stash to pop": "Wählen Sie einen Stash aus, für den ein Pop ausgeführt werden soll.", + "clean repo": "Bereinigen Sie Ihre Repository-Arbeitsstruktur vor Auftragsabschluss.", + "cant push": "Verweise können nicht per Push an einen Remotespeicherort übertragen werden. Führen Sie zuerst \"Pull\" aus, um Ihre Änderungen zu integrieren.", + "git error details": "Git: {0}", + "git error": "Git-Fehler", + "open git log": "Git-Protokoll öffnen" +} \ No newline at end of file diff --git a/i18n/deu/extensions/git/out/main.i18n.json b/i18n/deu/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..30220a6a11 --- /dev/null +++ b/i18n/deu/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "Verwenden von Git {0} von {1}", + "updateGit": "Git aktualisieren", + "neverShowAgain": "Nicht mehr anzeigen", + "git20": "Sie haben anscheinend Git {0} installiert. Code funktioniert am besten mit Git 2 oder neuer" +} \ No newline at end of file diff --git a/i18n/deu/extensions/git/out/model.i18n.json b/i18n/deu/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..ceb078dedb --- /dev/null +++ b/i18n/deu/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "Es sind keine verfügbaren Repositorys vorhanden.", + "pick repo": "Repository auswählen" +} \ No newline at end of file diff --git a/i18n/deu/extensions/git/out/repository.i18n.json b/i18n/deu/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..cc55e96514 --- /dev/null +++ b/i18n/deu/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Öffnen", + "index modified": "Index geändert", + "modified": "Geändert am", + "index added": "Index hinzugefügt", + "index deleted": "Index gelöscht", + "deleted": "Gelöscht", + "index renamed": "Index umbenannt", + "index copied": "Index kopiert", + "untracked": "Nicht verfolgt", + "ignored": "Ignoriert", + "both deleted": "Beide gelöscht", + "added by us": "Hinzugefügt von uns", + "deleted by them": "Gelöscht von anderen", + "added by them": "Hinzugefügt von anderen", + "deleted by us": "Gelöscht von uns", + "both added": "Beide hinzugefügt", + "both modified": "Beide geändert", + "commit": "Commit", + "merge changes": "Änderungen zusammenführen", + "staged changes": "Bereitgestellte Änderungen", + "changes": "Änderungen", + "ok": "OK", + "neveragain": "Nie wieder anzeigen", + "huge": "Das Git-Repository unter {0} umfasst zu viele aktive Änderungen. Nur ein Teil der Git-Features wird aktiviert." +} \ No newline at end of file diff --git a/i18n/deu/extensions/git/out/scmProvider.i18n.json b/i18n/deu/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..a7e7efcb61 --- /dev/null +++ b/i18n/deu/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "Commit" +} \ No newline at end of file diff --git a/i18n/deu/extensions/git/out/statusbar.i18n.json b/i18n/deu/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..c483d3bc12 --- /dev/null +++ b/i18n/deu/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "Auschecken...", + "sync changes": "Änderungen synchronisieren", + "publish changes": "Änderungen veröffentlichen", + "syncing changes": "Änderungen werden synchronisiert..." +} \ No newline at end of file diff --git a/i18n/deu/extensions/git/package.i18n.json b/i18n/deu/extensions/git/package.i18n.json new file mode 100644 index 0000000000..11dee0a8f1 --- /dev/null +++ b/i18n/deu/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "Klonen", + "command.init": "Repository initialisieren", + "command.close": "Repository schließen", + "command.refresh": "Aktualisieren", + "command.openChange": "Änderungen öffnen", + "command.openFile": "Datei öffnen", + "command.openHEADFile": "Datei öffnen (HEAD)", + "command.stage": "Änderungen bereitstellen", + "command.stageAll": "Alle Änderungen bereitstellen", + "command.stageSelectedRanges": "Gewählte Bereiche bereitstellen", + "command.revertSelectedRanges": "Ausgewählte Bereiche zurücksetzen", + "command.unstage": "Bereitstellung der Änderungen aufheben", + "command.unstageAll": "Bereitstellung aller Änderungen aufheben", + "command.unstageSelectedRanges": "Bereitstellung gewählter Bereiche aufheben", + "command.clean": "Änderungen verwerfen", + "command.cleanAll": "Alle Änderungen verwerfen", + "command.commit": "Commit", + "command.commitStaged": "Commit bereitgestellt", + "command.commitStagedSigned": "Bereitgestelltes committen (unterzeichnet)", + "command.commitStagedAmend": "Gestaffelt committen (Ändern)", + "command.commitAll": "Commit für alle ausführen", + "command.commitAllSigned": "Alle committen (unterzeichnet)", + "command.commitAllAmend": "Commit für alle ausführen (Ändern)", + "command.undoCommit": "Letzten Commit rückgängig machen", + "command.checkout": "Auschecken an...", + "command.branch": "Branch erstellen...", + "command.deleteBranch": "Branch löschen...", + "command.merge": "Branch zusammenführen...", + "command.createTag": "Tag erstellen", + "command.pull": "Pull", + "command.pullRebase": "Pull (Rebase)", + "command.pullFrom": "Pullen von...", + "command.push": "Push", + "command.pushTo": "Push zu...", + "command.pushWithTags": "Push mit Tags ausführen", + "command.sync": "Synchronisierung", + "command.publish": "Branch veröffentlichen", + "command.showOutput": "Git-Ausgabe anzeigen", + "command.ignore": "Datei zu .gitignore hinzufügen", + "command.stash": " Stash ausführen", + "command.stashPop": "Pop für Stash ausführen...", + "command.stashPopLatest": "Pop für letzten Stash ausführen", + "config.enabled": "Gibt an, ob Git aktiviert ist.", + "config.path": "Der Pfad zur ausführbaren Git-Datei.", + "config.autorefresh": "Gibt an, ob die automatische Aktualisierung aktiviert ist.", + "config.autofetch": "Gibt an, ob automatischer Abruf aktiviert ist.", + "config.enableLongCommitWarning": "Gibt an, ob Warnungen zu langen Commitnachrichten erfolgen sollen.", + "config.confirmSync": "Vor dem Synchronisieren von Git-Repositorys bestätigen.", + "config.countBadge": "Steuert die Git-Badgeanzahl. \"Alle\" zählt alle Änderungen. \"tracked\" (Nachverfolgt) zählt nur die nachverfolgten Änderungen. \"off\" (Aus) deaktiviert dies.", + "config.checkoutType": "Steuert, welcher Branchtyp beim Ausführen von \"Auschecken an...\" aufgelistet wird. \"Alle\" zeigt alle Verweise an, \"Lokal\" nur die lokalen Branches, \"Tags\" zeigt nur Tags an, und \"Remote\" zeigt nur Remotebranches an.", + "config.ignoreLegacyWarning": "Ignoriert die Legacy-Git-Warnung.", + "config.ignoreLimitWarning": "Ignoriert Warnung bei zu hoher Anzahl von Änderungen in einem Repository", + "config.defaultCloneDirectory": "Das Standard-Verzeichnis für einen Klon eines Git-Repositorys", + "config.enableSmartCommit": "Alle Änderungen committen, wenn keine bereitgestellten Änderungen vorhanden sind.", + "config.enableCommitSigning": "Aktiviert das Signieren von Commits per GPG.", + "config.discardAllScope": "Legt fest, welche Änderungen vom Befehl \"Alle Änderungen verwerfen\" verworfen werden. \"all\" verwirft alle Änderungen. \"tracked\" verwirft nur verfolgte Dateien. \"prompt\" zeigt immer eine Eingabeaufforderung an, wenn die Aktion ausgeführt wird." +} \ No newline at end of file diff --git a/i18n/deu/extensions/grunt/out/main.i18n.json b/i18n/deu/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..e0b317ad1c --- /dev/null +++ b/i18n/deu/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Fehler bei der automatischen Grunt-Erkennung. Fehlermeldung: {0}" +} \ No newline at end of file diff --git a/i18n/deu/extensions/grunt/package.i18n.json b/i18n/deu/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..8107fa0aa4 --- /dev/null +++ b/i18n/deu/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "Steuert, ob die automatische Erkennung von Grunt-Tasks aktiviert oder deaktiviert ist. Standardmäßig ist die Funktion aktiviert." +} \ No newline at end of file diff --git a/i18n/deu/extensions/gulp/out/main.i18n.json b/i18n/deu/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..55990f2e6f --- /dev/null +++ b/i18n/deu/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Fehler bei der automatischen Gulp-Erkennung. Fehlermeldung: {0}" +} \ No newline at end of file diff --git a/i18n/deu/extensions/gulp/package.i18n.json b/i18n/deu/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..a1091a0146 --- /dev/null +++ b/i18n/deu/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "Steuert, ob die automatische Erkennung von Gulp-Tasks aktiviert oder deaktiviert ist. Standardmäßig ist die Funktion aktiviert." +} \ No newline at end of file diff --git a/i18n/deu/extensions/html/client/out/htmlMain.i18n.json b/i18n/deu/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..f491bca7a1 --- /dev/null +++ b/i18n/deu/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "HTML-Sprachserver" +} \ No newline at end of file diff --git a/i18n/deu/extensions/html/package.i18n.json b/i18n/deu/extensions/html/package.i18n.json new file mode 100644 index 0000000000..fc7de6e7af --- /dev/null +++ b/i18n/deu/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "Standard-HTML-Formatierer aktivieren/deaktivieren", + "html.format.wrapLineLength.desc": "Die maximale Anzahl von Zeichen pro Zeile (0 = deaktiviert).", + "html.format.unformatted.desc": "Die Liste der Tags (durch Kommas getrennt), die nicht erneut formatiert werden sollen. \"null\" bezieht sich standardmäßig auf alle Tags, die unter https://www.w3.org/TR/html5/dom.html#phrasing-content aufgeführt werden.", + "html.format.contentUnformatted.desc": "Liste der Tags (durch Trennzeichen getrennt), in der der Inhalt nicht neu formatiert werden muss. \"null\" entspricht standardmäßig dem Tag \"pre\".", + "html.format.indentInnerHtml.desc": "Nimmt einen Einzug für - und -Abschnitte vor.", + "html.format.preserveNewLines.desc": "Gibt an, ob vorhandene Zeilenumbrüche vor Elemente beibehalten werden sollen. Funktioniert nur vor Elementen, nicht in Tags oder für Text.", + "html.format.maxPreserveNewLines.desc": "Die maximale Anzahl von Zeilenumbrüchen, die in einem Block beibehalten werden soll. Verwenden Sie \"null\" für eine unbegrenzte Anzahl.", + "html.format.indentHandlebars.desc": "Formatiert {{#foo}} und {{/foo}} und nimmt einen Einzug vor.", + "html.format.endWithNewline.desc": "Endet mit einer neuen Zeile.", + "html.format.extraLiners.desc": "Die Liste der Tags (durch Kommas getrennt), vor denen eine zusätzliche neue Zeile eingefügt werden soll. \"null\" verwendet standardmäßig \"head, body, /HTML\".", + "html.format.wrapAttributes.desc": "Attribute umschließen.", + "html.format.wrapAttributes.auto": "Attribute nur dann umschließen, wenn die Zeilenlänge überschritten wird.", + "html.format.wrapAttributes.force": "Jedes Attribut mit Ausnahme des ersten umschließen.", + "html.format.wrapAttributes.forcealign": "Jedes Attribut mit Ausnahme des ersten umschließen und Ausrichtung beibehalten.", + "html.format.wrapAttributes.forcemultiline": "Jedes Attribut umschließen.", + "html.suggest.angular1.desc": "Konfiguriert, ob die integrierte HTML-Sprachuntersützung Angular V1-Tags und -Eigenschaften vorschlägt.", + "html.suggest.ionic.desc": "Konfiguriert, ob die integrierte HTML-Sprachuntersützung Ionic-Tags, -Eigenschaften und -Werte vorschlägt.", + "html.suggest.html5.desc": "Konfiguriert, ob die integrierte HTML-Sprachuntersützung HTML5-Tags, -Eigenschaften und -Werte vorschlägt.", + "html.trace.server.desc": "Verfolgt die Kommunikation zwischen VS Code und dem HTML-Sprachserver.", + "html.validate.scripts": "Konfiguriert, ob die integrierte HTML-Sprachunterstützung eingebettete Skripts unterstützt.", + "html.validate.styles": "Konfiguriert, ob die integrierte HTML-Sprachunterstützung eingebettete Skripts validiert.", + "html.autoClosingTags": "Automatisches Schließen von HTML-Tags aktivieren/deaktivieren." +} \ No newline at end of file diff --git a/i18n/deu/extensions/jake/out/main.i18n.json b/i18n/deu/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..36e3521f49 --- /dev/null +++ b/i18n/deu/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Fehler bei der automatischen Jake-Erkennung. Fehlermeldung: {0}" +} \ No newline at end of file diff --git a/i18n/deu/extensions/jake/package.i18n.json b/i18n/deu/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..2a754d0098 --- /dev/null +++ b/i18n/deu/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "Steuert, ob die automatische Erkennung von Jake-Tasks aktiviert oder deaktiviert ist. Standardmäßig ist die Funktion aktiviert." +} \ No newline at end of file diff --git a/i18n/deu/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/deu/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..8e5565fdfb --- /dev/null +++ b/i18n/deu/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "Standarddatei \"bower.json\"", + "json.bower.error.repoaccess": "Fehler bei der Anforderung des Bower-Repositorys: {0}", + "json.bower.latest.version": "Neueste" +} \ No newline at end of file diff --git a/i18n/deu/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/deu/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..5e90dbab05 --- /dev/null +++ b/i18n/deu/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "Standarddatei \"package.json\"", + "json.npm.error.repoaccess": "Fehler bei der Anforderung des NPM-Repositorys: {0}", + "json.npm.latestversion": "Die zurzeit neueste Version des Pakets.", + "json.npm.majorversion": "Ordnet die aktuelle Hauptversion (1.x.x) zu.", + "json.npm.minorversion": "Ordnet die aktuelle Nebenversion (1.2.x) zu.", + "json.npm.version.hover": "Aktuelle Version: {0}" +} \ No newline at end of file diff --git a/i18n/deu/extensions/json/client/out/jsonMain.i18n.json b/i18n/deu/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..2e73628f57 --- /dev/null +++ b/i18n/deu/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "JSON-Sprachserver" +} \ No newline at end of file diff --git a/i18n/deu/extensions/json/package.i18n.json b/i18n/deu/extensions/json/package.i18n.json new file mode 100644 index 0000000000..c283c1caac --- /dev/null +++ b/i18n/deu/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "Schemas zu JSON-Dateien im aktuellen Projekt zuordnen", + "json.schemas.url.desc": "Eine URL zu einem Schema oder ein relativer Pfad zu einem Schema im aktuellen Verzeichnis", + "json.schemas.fileMatch.desc": "Ein Array von Dateimustern, mit dem beim Auflösen von JSON-Dateien zu Schemas ein Abgleich erfolgt.", + "json.schemas.fileMatch.item.desc": "Ein Dateimuster, das \"*\" enthalten kann, mit dem beim Auflösen von JSON-Dateien zu Schemas ein Abgleich erfolgt.", + "json.schemas.schema.desc": "Die Schemadefinition für die angegebene URL. Das Schema muss nur angegeben werden, um Zugriffe auf die Schema-URL zu vermeiden.", + "json.format.enable.desc": "Standard-JSON-Formatierer aktivieren/deaktivieren (Neustart erforderlich)", + "json.tracing.desc": "Verfolgt die Kommunikation zwischen VS Code und JSON-Sprachserver nach.", + "json.colorDecorators.enable.desc": "Aktiviert oder deaktiviert Farb-Decorators", + "json.colorDecorators.enable.deprecationMessage": "Die Einstellung \"json.colorDecorators.enable\" ist veraltet und wurde durch \"editor.colorDecorators\" ersetzt." +} \ No newline at end of file diff --git a/i18n/deu/extensions/markdown/out/extension.i18n.json b/i18n/deu/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..99a3db437e --- /dev/null +++ b/i18n/deu/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "'markdown.styles' konnte nicht geladen werden: {0}" +} \ No newline at end of file diff --git a/i18n/deu/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/deu/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..60e1324551 --- /dev/null +++ b/i18n/deu/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "In diesem Dokument wurden einige Inhalte deaktiviert.", + "preview.securityMessage.title": "Potenziell unsichere Inhalte wurden in der Markdown-Vorschau deaktiviert. Ändern Sie die Sicherheitseinstellung der Markdown-Vorschau, um unsichere Inhalte zuzulassen oder Skripts zu aktivieren.", + "preview.securityMessage.label": "Sicherheitswarnung – Inhalt deaktiviert" +} \ No newline at end of file diff --git a/i18n/deu/extensions/markdown/out/security.i18n.json b/i18n/deu/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..2772778c74 --- /dev/null +++ b/i18n/deu/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "Strict", + "strict.description": "Nur sicheren Inhalt laden", + "insecureContent.title": "Unsicheren Inhalt zulassen", + "insecureContent.description": "Laden von Inhalten über HTTP aktivieren", + "disable.title": "Deaktivieren", + "disable.description": "Alle Inhalte und Skriptausführung zulassen. Nicht empfohlen.", + "moreInfo.title": "Weitere Informationen", + "preview.showPreviewSecuritySelector.title": "Sicherheitseinstellungen für die Markdown-Vorschau in diesem Arbeitsbereich auswählen" +} \ No newline at end of file diff --git a/i18n/deu/extensions/markdown/package.i18n.json b/i18n/deu/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..cc8f1ae1b3 --- /dev/null +++ b/i18n/deu/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "Legt fest, wie Zeilenumbrüche in der Markdown-Vorschau gerendert werden. Die Einstellung 'true' erzeugt ein
für jede neue Zeile.", + "markdown.preview.linkify": "Aktiviert oder deaktiviert die Konvertierung von URL-ähnlichem Text in Links in der Markdown-Vorschau.", + "markdown.preview.doubleClickToSwitchToEditor.desc": "Doppelklicken Sie in die Markdown-Vorschau, um zum Editor zu wechseln.", + "markdown.preview.fontFamily.desc": "Steuert die Schriftfamilie, die in der Markdownvorschau verwendet wird.", + "markdown.preview.fontSize.desc": "Steuert den Schriftgrad in Pixeln, der in der Markdownvorschau verwendet wird.", + "markdown.preview.lineHeight.desc": "Steuert die Zeilenhöhe, die in der Markdownvorschau verwendet wird. Diese Zahl ist relativ zum Schriftgrad.", + "markdown.preview.markEditorSelection.desc": "Markieren Sie die aktuelle Editor-Auswahl in der Markdown-Vorschau.", + "markdown.preview.scrollEditorWithPreview.desc": "Wenn für die Markdown-Vorschau ein Bildlauf durchgeführt wird, aktualisieren Sie die Ansicht des Editors.", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "Führt einen Bildlauf für die Markdown-Vorschau durch, um die aktuell ausgewählte Zeile im Editor anzuzeigen.", + "markdown.preview.title": "Vorschau öffnen", + "markdown.previewFrontMatter.dec": "Legt fest, wie die YAML-Titelei in der Markdownvorschau gerendert werden soll. Durch \"hide\" wird die Titelei entfernt. Andernfalls wird die Titelei wie Markdowninhalt verarbeitet.", + "markdown.previewSide.title": "Vorschau an der Seite öffnen", + "markdown.showSource.title": "Quelle anzeigen", + "markdown.styles.dec": "Eine Liste von URLs oder lokalen Pfaden zu CSS-Stylesheets aus der Markdownvorschau, die verwendet werden sollen. Relative Pfade werden relativ zu dem Ordner interpretiert, der im Explorer geöffnet ist. Wenn kein Ordner geöffnet ist, werden sie relativ zum Speicherort der Markdowndatei interpretiert. Alle '\\' müssen als '\\\\' geschrieben werden.", + "markdown.showPreviewSecuritySelector.title": "Sicherheitseinstellungen für Vorschau ändern", + "markdown.trace.desc": "Aktiviert die Debugprotokollierung für die Markdown-Erweiterung.", + "markdown.refreshPreview.title": "Vorschau aktualisieren" +} \ No newline at end of file diff --git a/i18n/deu/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/deu/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..1eb8d584d5 --- /dev/null +++ b/i18n/deu/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "Aktuelle Änderung akzeptieren", + "acceptIncomingChange": "Eingehende Änderung akzeptieren", + "acceptBothChanges": "Beide Änderungen akzeptieren", + "compareChanges": "Änderungen vergleichen" +} \ No newline at end of file diff --git a/i18n/deu/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/deu/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..c88694278c --- /dev/null +++ b/i18n/deu/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "Der Editor-Cursor ist nicht innerhalb eines Mergingkonflikts", + "compareChangesTitle": "{0}: Aktuelle Änderungen ⟷ Eingehende Änderungen", + "cursorOnCommonAncestorsRange": "Der Editor-Cursor ist innerhalb des Blocks gemeinsamer Vorgänger, verschieben Sie ihn entweder in den Block \"aktuell\" oder \"eingehend\". ", + "cursorOnSplitterRange": "Der Editor-Cursor ist innerhalb der Mergingkonfliktaufteilung, verschieben Sie ihn entweder in den Block \"aktuell\" oder \"eingehend\".", + "noConflicts": "Keine Merge-Konflikte in dieser Datei gefunden", + "noOtherConflictsInThisFile": "Keine weiteren Merge-Konflikte in dieser Datei" +} \ No newline at end of file diff --git a/i18n/deu/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/deu/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..838b0714d8 --- /dev/null +++ b/i18n/deu/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(Aktuelle Änderung)", + "incomingChange": "(Eingehende Änderung)" +} \ No newline at end of file diff --git a/i18n/deu/extensions/merge-conflict/package.i18n.json b/i18n/deu/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..815ed066a3 --- /dev/null +++ b/i18n/deu/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "Merge-Konflikt", + "command.accept.all-incoming": "Alle eingehenden akzeptieren", + "command.accept.all-both": "Alle beide akzeptieren", + "command.accept.current": "Aktuelles akzeptieren", + "command.accept.incoming": "Eingehendes akzeptieren", + "command.accept.selection": "Auswahl akzeptieren", + "command.accept.both": "Beides akzeptieren", + "command.next": "Nächster Konflikt", + "command.previous": "Vorheriger Konflikt", + "command.compare": "Aktuellen Konflikt vergleichen", + "config.title": "Merge-Konflikt", + "config.codeLensEnabled": "CodeLens-Mergingkonfliktblock im Editor aktivieren/deaktivieren", + "config.decoratorsEnabled": "Mergingkonflikt-Decorators im Editor aktivieren/deaktivieren" +} \ No newline at end of file diff --git a/i18n/deu/extensions/npm/package.i18n.json b/i18n/deu/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..49e0e5ee21 --- /dev/null +++ b/i18n/deu/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "Steuert, ob die automatische Erkennung von NPM-Skripts aktiviert oder deaktiviert ist. Standardmäßig ist die Funktion aktiviert.", + "config.npm.runSilent": "npm-Befehle mit der Option \"--silent\" ausführen" +} \ No newline at end of file diff --git a/i18n/deu/extensions/php/out/features/validationProvider.i18n.json b/i18n/deu/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..0b6f8be291 --- /dev/null +++ b/i18n/deu/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "Möchten Sie zulassen, dass {0} (als Arbeitsbereichseinstellung definiert) zum Bereinigen von PHP-Dateien ausgeführt wird?", + "php.yes": "Zulassen", + "php.no": "Nicht zulassen", + "wrongExecutable": "Eine Überprüfung ist nicht möglich, da {0} keine gültige ausführbare PHP-Datei ist. Verwenden Sie die Einstellung \"'php.validate.executablePath\", um die ausführbare PHP-Datei zu konfigurieren.", + "noExecutable": "Eine Überprüfung ist nicht möglich, da keine ausführbare PHP-Datei festgelegt ist. Verwenden Sie die Einstellung \"php.validate.executablePath\", um die ausführbare PHP-Datei zu konfigurieren.", + "unknownReason": "Fehler beim Ausführen von PHP mithilfe des Pfads \"{0}\". Die Ursache ist unbekannt." +} \ No newline at end of file diff --git a/i18n/deu/extensions/php/package.i18n.json b/i18n/deu/extensions/php/package.i18n.json new file mode 100644 index 0000000000..b8e7fd7fe5 --- /dev/null +++ b/i18n/deu/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "Konfiguriert, ob die integrierten PHP-Sprachvorschläge aktiviert sind. Die Unterstützung schlägt globale und variable PHP-Elemente vor.", + "configuration.validate.enable": "Integrierte PHP-Überprüfung aktivieren/deaktivieren.", + "configuration.validate.executablePath": "Zeigt auf die ausführbare PHP-Datei.", + "configuration.validate.run": "Gibt an, ob der Linter beim Speichern oder bei der Eingabe ausgeführt wird.", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "Ausführbare Datei für PHP-Überprüfung nicht zulassen (als Arbeitsbereicheinstellung definiert)" +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/deu/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..9a748d5d44 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "TypeScript ({1)} wird für Editor-Funktionen verwenden. TypeScript ({0}) ist global auf Ihrem Computer installiert. Fehler in VS Code können sich von TSC-Fehlern unterscheiden", + "moreInformation": "Weitere Informationen", + "doNotCheckAgain": "Nicht erneut überprüfen", + "close": "Schließen", + "updateTscCheck": "Die Benutzereinstellung \"typescript.check.tscVersion\" wurde zu FALSE aktualisiert." +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/deu/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..70c12796c8 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "Eingaben werden abgerufen...", + "acquiringTypingsDetail": "Eingabedefinitionen für IntelliSense werden abgerufen." +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/deu/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..0617b2d2d1 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "Aktiviert die Semantiküberprüfung in einer JavaScript-Datei. Muss sich oben in einer Datei befinden.", + "ts-nocheck": "Deaktiviert die Semantiküberprüfung in einer JavaScript-Datei. Muss sich oben in einer Datei befinden.", + "ts-ignore": "Unterdrückt @ts-check-Fehler in der nächsten Zeile einer Datei." +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/deu/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..1b2f3bfbf7 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 Implementierung", + "manyImplementationLabel": "{0}-Implementierungen", + "implementationsErrorLabel": "Implementierungen konnten nicht bestimmt werden" +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/deu/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..0e502d5bcc --- /dev/null +++ b/i18n/deu/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "JSDoc-Kommentar" +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/deu/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..7cc68c3920 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 Verweis", + "manyReferenceLabel": "{0} Verweise", + "referenceErrorLabel": "Verweise konnten nicht bestimmt werden" +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/deu/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..7675612306 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "Überwachen – {0}", + "buildTscLabel": "Erstellen – {0}" +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/typescriptMain.i18n.json b/i18n/deu/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..4584312780 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "Öffnen Sie einen Ordner in VS Code, um ein TypeScript- oder JavaScript-Projekt zu verwenden.", + "typescript.projectConfigUnsupportedFile": "TypeScript- oder JavaScript-Projekt konnte nicht ermittelt werden. Nicht unterstützter Dateityp.", + "typescript.projectConfigCouldNotGetInfo": "TypeScript- oder JavaScript-Projekt konnte nicht ermittelt werden.", + "typescript.noTypeScriptProjectConfig": "Datei ist nicht Teil eines TypeScript-Projekts.", + "typescript.noJavaScriptProjectConfig": "Datei ist nicht Teil eines JavaScript-Projekts.", + "typescript.configureTsconfigQuickPick": "tsconfig.json konfigurieren", + "typescript.configureJsconfigQuickPick": "jsconfig.json konfigurieren", + "typescript.projectConfigLearnMore": "Weitere Informationen" +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/deu/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..6b1cda8b85 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "Der Pfad \"{0}\" zeigt nicht auf eine gültige tsserver-Installation. Fallback auf gebündelte TypeScript-Version wird durchgeführt.", + "serverCouldNotBeStarted": "Der TypeScript-Sprachserver konnte nicht gestartet werden. Fehlermeldung: {0}", + "typescript.openTsServerLog.notSupported": "Die TS Server-Protokollierung erfordert TS 2.2.2+.", + "typescript.openTsServerLog.loggingNotEnabled": "Die TS Server-Protokollierung ist deaktiviert. Legen Sie \"typescript.tsserver.log\" fest, und laden Sie VS Code erneut, um die Protokollierung zu aktivieren.", + "typescript.openTsServerLog.enableAndReloadOption": "Aktiviert die Protokollierung und startet den TS-Server neu.", + "typescript.openTsServerLog.noLogFile": "TS Server hat noch nicht mit der Protokollierung begonnen.", + "openTsServerLog.openFileFailedFailed": "Die TS-Server-Protokolldatei konnte nicht geöffnet werden.", + "serverDiedAfterStart": "Der TypeScript-Sprachdienst wurde direkt nach seinem Start fünfmal beendet. Der Dienst wird nicht neu gestartet.", + "serverDiedReportIssue": "Problem melden", + "serverDied": "Der TypeScript-Sprachdienst wurde während der letzten fünf Minuten fünfmal unerwartet beendet." +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/utils/api.i18n.json b/i18n/deu/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..5517082674 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "Ungültige Version" +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/utils/logger.i18n.json b/i18n/deu/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/deu/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/deu/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..afa076562f --- /dev/null +++ b/i18n/deu/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "Um die JavaScript/TypeScript-Sprachfunktionen für das gesamte Projekt zu aktivieren, schließen Sie Ordner mit vielen Dateien aus. Beispiel: {0}", + "hintExclude.generic": "Um JavaScript/TypeScript-Sprachfunktionen für das gesamte Projekt zu aktivieren, schließen Sie große Ordner mit Quelldateien aus, an denen Sie nicht arbeiten.", + "large.label": "Auszuschließende Elemente konfigurieren", + "hintExclude.tooltip": "Um JavaScript/TypeScript-Sprachfunktionen für das gesamte Projekt zu aktivieren, schließen Sie große Ordner mit Quelldateien aus, an denen Sie nicht arbeiten." +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/deu/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..3be32dc820 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "Daten werden zum Optimieren von TypeScript IntelliSense abgerufen", + "typesInstallerInitializationFailed.title": "Typisierungsdateien für JavaScript-Sprachfunktionen konnten nicht installiert werden. Stellen Sie sicher, das NPM installiert ist, oder konfigurieren Sie \"typescript.npm\" in Ihren Benutzereinstellungen.", + "typesInstallerInitializationFailed.moreInformation": "Weitere Informationen", + "typesInstallerInitializationFailed.doNotCheckAgain": "Nicht erneut überprüfen", + "typesInstallerInitializationFailed.close": "Schließen" +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/deu/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..a99f9bcb42 --- /dev/null +++ b/i18n/deu/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "Version von VS Code verwenden", + "useWorkspaceVersionOption": "Arbeitsbereichsversion verwenden", + "learnMore": "Weitere Informationen", + "selectTsVersion": "Wählen Sie die für die JavaScript- und TypeScript-Sprachfunktionen verwendete TypeScript-Version aus." +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/deu/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..364297a06f --- /dev/null +++ b/i18n/deu/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "Die TypeScript-Version konnte unter diesem Pfad nicht geladen werden.", + "noBundledServerFound": "Der tsserver von VS Code wurde von einer anderen Anwendung wie etwa einem fehlerhaften Tool zur Viruserkennung gelöscht. Führen Sie eine Neuinstallation von VS Code durch." +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/package.i18n.json b/i18n/deu/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..34eb698b20 --- /dev/null +++ b/i18n/deu/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "Projekt erneut laden", + "javascript.reloadProjects.title": "Projekt erneut laden", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "Vervollständigen Sie Funktionen mit deren Parametersignatur.", + "typescript.tsdk.desc": "Gibt den Ordnerpfad mit den zu verwendenden tsserver- und lib*.d.ts-Dateien an.", + "typescript.disableAutomaticTypeAcquisition": "Deaktiviert die automatische Typerfassung. Erfordert TypeScript >= 2.0.6.", + "typescript.tsserver.log": "Aktiviert die Protokollierung des TS-Servers in eine Datei. Mithilfe der Protokolldatei lassen sich Probleme beim TS-Server diagnostizieren. Die Protokolldatei kann Dateipfade, Quellcode und weitere potenziell sensible Informationen aus Ihrem Projekt enthalten.", + "typescript.tsserver.trace": "Aktiviert die Ablaufverfolgung von an den TS-Server gesendeten Nachrichten. Mithilfe der Ablaufverfolgung lassen sich Probleme beim TS-Server diagnostizieren. Die Ablaufverfolgung kann Dateipfade, Quellcode und weitere potenziell sensible Informationen aus Ihrem Projekt enthalten.", + "typescript.validate.enable": "TypeScript-Überprüfung aktivieren/deaktivieren.", + "typescript.format.enable": "Standardmäßigen TypeScript-Formatierer aktivieren/deaktivieren.", + "javascript.format.enable": "Standardmäßigen JavaScript-Formatierer aktivieren/deaktivieren.", + "format.insertSpaceAfterCommaDelimiter": "Definiert die Verarbeitung von Leerzeichen nach einem Kommatrennzeichen.", + "format.insertSpaceAfterConstructor": "Definiert die Verarbeitung von Leerzeichen nach dem Konstruktor-Schlüsselwort. Erfordert TypeScript 2.3.0 oder höher.", + "format.insertSpaceAfterSemicolonInForStatements": " Definiert die Verarbeitung von Leerzeichen nach einem Semikolon in einer for-Anweisung.", + "format.insertSpaceBeforeAndAfterBinaryOperators": "Definiert die Verarbeitung von Leerzeichen nach einem binären Operator.", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "Definiert die Verarbeitung von Leerzeichen nach Schlüsselwörtern in einer Flusssteuerungsanweisung.", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "Definiert die Verarbeitung von Leerzeichen nach einem Funktionsschlüsselwort für anonyme Funktionen.", + "format.insertSpaceBeforeFunctionParenthesis": "Definiert die Verarbeitung von Leerzeichen vor Funktionsargumentklammern. Erfordert TypeScript >= 2.1.5.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "Definiert die Verarbeitung von Leerzeichen nach öffnenden und vor schließenden nicht leeren runden Klammern.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "Definiert die Verarbeitung von Leerzeichen nach öffnenden und vor schließenden nicht leeren eckigen Klammern.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Definiert die Verarbeitung von Leerzeichen nach öffnenden und vor schließenden geschweiften Klammern. Erfordert TypeScript 2.3.0 oder höher.", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "Definiert die Verarbeitung von Leerzeichen nach öffnenden und vor schließenden geschweiften Klammern für Vorlagenzeichenfolgen. Erfordert TypeScript >= 2.0.6.", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Definiert die Verarbeitung von Leerzeichen nach öffnenden und vor schließenden geschweiften Klammern für JSX-Ausdrücke. Erfordert TypeScript >= 2.0.6.", + "format.insertSpaceAfterTypeAssertion": "Definiert die Verarbeitung von Leerzeichen nach Typassertionen in TypeScript. Erfordert TypeScript >= 2.4.", + "format.placeOpenBraceOnNewLineForFunctions": "Definiert, ob eine öffnende geschweifte Klammer für Funktionen in eine neue Zeile eingefügt wird.", + "format.placeOpenBraceOnNewLineForControlBlocks": "Definiert, ob eine öffnende geschweifte Klammer für Kontrollblöcke in eine neue Zeile eingefügt wird.", + "javascript.validate.enable": "JavaScript-Überprüfung aktivieren/deaktivieren.", + "typescript.goToProjectConfig.title": "Zur Projektkonfiguration wechseln", + "javascript.goToProjectConfig.title": "Zur Projektkonfiguration wechseln", + "javascript.referencesCodeLens.enabled": "Aktiviert oder deaktiviert CodeLens-Verweise in JavaScript Dateien. Erfordert TypeScript 2.0.6 oder höher.", + "typescript.referencesCodeLens.enabled": "Aktiviert oder deaktiviert CodeLens-Verweise in TypeScript Dateien. Erfordert TypeScript 2.0.6 oder höher.", + "typescript.implementationsCodeLens.enabled": "Aktiviert oder deaktiviert CodeLens-Implementierungen. Erfordert TypeScript 2.2.0 oder höher.", + "typescript.openTsServerLog.title": "TS Server-Protokolldatei öffnen", + "typescript.restartTsServer": "TS Server neu starten", + "typescript.selectTypeScriptVersion.title": "TypeScript-Version wählen", + "jsDocCompletion.enabled": "Automatische JSDoc-Kommentare aktivieren/deaktivieren", + "javascript.implicitProjectConfig.checkJs": "Aktiviert/deaktiviert die Semantikprüfung bei JavaScript-Dateien. Diese Einstellung wird von vorhandenen \"jsconfig.json\"- oder \"tsconfig.json\"-Dateien außer Kraft gesetzt. Erfordert TypeScript 2.3.1 oder höher.", + "typescript.npm": "Gibt den Pfad zur ausführbaren NPM-Datei an, die für die automatische Typerfassung verwendet wird. Hierfür ist TypeScript 2.3.4 oder höher erforderlich.", + "typescript.check.npmIsInstalled": "Überprüfen Sie, ob NPM für die automatische Typerfassung installiert ist.", + "javascript.nameSuggestions": "Das Einbeziehen eindeutiger Namen von der Datei in der JavaScript-Vorschlagsliste aktivieren/deaktivieren.", + "typescript.tsc.autoDetect": "Steuert, ob die automatische Erkennung von tsc-Tasks aktiviert oder deaktiviert ist.\n", + "typescript.problemMatchers.tsc.label": "TypeScript-Probleme", + "typescript.problemMatchers.tscWatch.label": "TypeScript-Probleme (Überwachungsmodus)" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/deu/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/deu/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/deu/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..e7776f3245 --- /dev/null +++ b/i18n/deu/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (erneut aufgetreten)" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/deu/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..e747bfa45a --- /dev/null +++ b/i18n/deu/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "Eingabe" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/deu/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..812e329538 --- /dev/null +++ b/i18n/deu/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "Groß-/Kleinschreibung beachten", + "wordsDescription": "Nur ganzes Wort suchen", + "regexDescription": "Regulären Ausdruck verwenden" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/deu/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..4b55f2c684 --- /dev/null +++ b/i18n/deu/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Fehler: {0}", + "alertWarningMessage": "Warnung: {0}", + "alertInfoMessage": "Info: {0}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/deu/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..eb53c82fc7 --- /dev/null +++ b/i18n/deu/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "Das Bild ist zu groß für den Editor. ", + "resourceOpenExternalButton": "Bild mit externem Programm öffnen?", + "nativeBinaryError": "Die Datei wird nicht im Editor angezeigt, weil sie binär oder sehr groß ist oder eine nicht unterstützte Textcodierung verwendet.", + "sizeB": "{0} B", + "sizeKB": "{0} KB", + "sizeMB": "{0} MB", + "sizeGB": "{0} GB", + "sizeTB": "{0} TB" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/deu/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..ec546c801a --- /dev/null +++ b/i18n/deu/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "Weitere Informationen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/common/errorMessage.i18n.json b/i18n/deu/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..fc89604c16 --- /dev/null +++ b/i18n/deu/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}. Fehlercode: {1}", + "error.permission.verbose": "Berechtigung verweigert (HTTP {0})", + "error.permission": "Berechtigung verweigert", + "error.http.verbose": "{0} (HTTP {1}: {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "Unbekannter Verbindungsfehler ({0})", + "error.connection.unknown": "Es ist ein unbekannter Verbindungsfehler aufgetreten. Entweder besteht keine Internetverbindung mehr, oder der verbundene Server ist offline.", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "Ein unbekannter Fehler ist aufgetreten. Weitere Details dazu finden Sie im Protokoll.", + "nodeExceptionMessage": "Systemfehler ({0})", + "error.moreErrors": "{0} ({1} Fehler gesamt)" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/common/json.i18n.json b/i18n/deu/src/vs/base/common/json.i18n.json new file mode 100644 index 0000000000..ce63772414 --- /dev/null +++ b/i18n/deu/src/vs/base/common/json.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Ungültiges Symbol", + "error.invalidNumberFormat": "Ungültiges Zahlenformat.", + "error.propertyNameExpected": "Ein Eigenschaftenname wurde erwartet.", + "error.valueExpected": "Ein Wert wurde erwartet.", + "error.colonExpected": "Ein Doppelpunkt wurde erwartet.", + "error.commaExpected": "Ein Komma wurde erwartet.", + "error.closeBraceExpected": "Eine schließende geschweifte Klammer wurde erwartet.", + "error.closeBracketExpected": "Eine schließende Klammer wurde erwartet.", + "error.endOfFileExpected": "Das Dateiende wurde erwartet." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/deu/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..ce63772414 --- /dev/null +++ b/i18n/deu/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Ungültiges Symbol", + "error.invalidNumberFormat": "Ungültiges Zahlenformat.", + "error.propertyNameExpected": "Ein Eigenschaftenname wurde erwartet.", + "error.valueExpected": "Ein Wert wurde erwartet.", + "error.colonExpected": "Ein Doppelpunkt wurde erwartet.", + "error.commaExpected": "Ein Komma wurde erwartet.", + "error.closeBraceExpected": "Eine schließende geschweifte Klammer wurde erwartet.", + "error.closeBracketExpected": "Eine schließende Klammer wurde erwartet.", + "error.endOfFileExpected": "Das Dateiende wurde erwartet." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/common/keybindingLabels.i18n.json b/i18n/deu/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..a6c3f59142 --- /dev/null +++ b/i18n/deu/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "STRG", + "shiftKey": "UMSCHALTTASTE", + "altKey": "ALT", + "windowsKey": "Windows", + "ctrlKey.long": "STRG", + "shiftKey.long": "UMSCHALTTASTE", + "altKey.long": "ALT", + "cmdKey.long": "Befehlstaste", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/common/processes.i18n.json b/i18n/deu/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..746d693396 --- /dev/null +++ b/i18n/deu/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "Fehler: Die Informationen zur ausführbaren Datei müssen einen Befehl vom Typ \"string\" definieren.", + "ExecutableParser.isShellCommand": "Warnung: \"isShellCommand\" muss vom Typ \"boolean\" sein. Der Wert {0} wird ignoriert.", + "ExecutableParser.args": "Warnung: \"args\" muss vom Typ \"string[]\" sein. Der Wert {0} wird ignoriert.", + "ExecutableParser.invalidCWD": "Warnung: \"options.cwd\" muss vom Typ \"string\" sein. Der Wert {0} wird ignoriert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/common/severity.i18n.json b/i18n/deu/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..d2a6370289 --- /dev/null +++ b/i18n/deu/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "Fehler", + "sev.warning": "Warnung", + "sev.info": "Info" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/node/processes.i18n.json b/i18n/deu/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..6c4b2d3cd6 --- /dev/null +++ b/i18n/deu/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "Ein Shell-Befehl kann nicht auf einem UNC-Laufwerk ausgeführt werden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/node/zip.i18n.json b/i18n/deu/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..90e8a074c7 --- /dev/null +++ b/i18n/deu/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "{0} wurde im ZIP nicht gefunden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/deu/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..d8d94ca44e --- /dev/null +++ b/i18n/deu/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0}, Auswahl", + "quickOpenAriaLabel": "Auswahl" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/deu/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..33c95089ca --- /dev/null +++ b/i18n/deu/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "Schnellauswahl. Nehmen Sie eine Eingabe vor, um die Ergebnisse einzugrenzen.", + "treeAriaLabel": "Schnellauswahl" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/deu/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..480fc44ce0 --- /dev/null +++ b/i18n/deu/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Zuklappen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/code/electron-main/auth.i18n.json b/i18n/deu/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..34cb0917a1 --- /dev/null +++ b/i18n/deu/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "Proxyauthentifizierung erforderlich", + "proxyauth": "Der Proxy {0} erfordert eine Authentifizierung." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/code/electron-main/menus.i18n.json b/i18n/deu/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..b78bad502a --- /dev/null +++ b/i18n/deu/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "&&Datei", + "mEdit": "&&Bearbeiten", + "mSelection": "&&Auswahl", + "mView": "&&Anzeigen", + "mGoto": "Los", + "mDebug": "&&Debuggen", + "mWindow": "Fenster", + "mHelp": "&&Hilfe", + "mTask": "&&Aufgaben", + "miNewWindow": "Neues &&Fenster", + "mAbout": "Informationen zu {0}", + "mServices": "Dienste", + "mHide": "{0} ausblenden", + "mHideOthers": "Andere ausblenden", + "mShowAll": "Alle anzeigen", + "miQuit": "{0} beenden", + "miNewFile": "&&Neue Datei", + "miOpen": "&&Öffnen...", + "miOpenWorkspace": "&&Arbeitsbereich öffnen...", + "miOpenFolder": "&&Ordner öffnen...", + "miOpenFile": "Datei &&öffnen...", + "miOpenRecent": "&&Zuletzt verwendete öffnen", + "miSaveWorkspaceAs": "Arbeitsbereich &&speichern unter...", + "miAddFolderToWorkspace": "&&Ordner zum Arbeitsbereich hinzufügen...", + "miSave": "&&Speichern", + "miSaveAs": "Speichern &&unter...", + "miSaveAll": "A&&lles speichern", + "miAutoSave": "Automatisch speichern", + "miRevert": "D&&atei wiederherstellen", + "miCloseWindow": "&&Fenster schließen", + "miCloseWorkspace": "A&&rbeitsbereich schließen", + "miCloseFolder": "&&Ordner schließen", + "miCloseEditor": "Editor s&&chließen", + "miExit": "Beenden", + "miOpenSettings": "&&Einstellungen", + "miOpenKeymap": "&&Tastenkombinationen", + "miOpenKeymapExtensions": "&&Erweiterte Tastenzuordnungen", + "miOpenSnippets": "&&Benutzercodeausschnitte", + "miSelectColorTheme": "&&Farbdesign", + "miSelectIconTheme": "Datei-&&Symboldesign", + "miPreferences": "&&Einstellungen", + "miReopenClosedEditor": "&&Geschlossenen Editor erneut öffnen", + "miMore": "&&Mehr...", + "miClearRecentOpen": "Zul&&etzt geöffnete löschen", + "miUndo": "&&Rückgängig", + "miRedo": "&&Wiederholen", + "miCut": "Ausschneiden", + "miCopy": "&&Kopieren", + "miPaste": "&&Einfügen", + "miFind": "&&Suchen", + "miReplace": "&&Ersetzen", + "miFindInFiles": "&&In Dateien suchen", + "miReplaceInFiles": "&&In Dateien ersetzen", + "miEmmetExpandAbbreviation": "Emmet: Abkür&&zung erweitern", + "miShowEmmetCommands": "E&&mmet...", + "miToggleLineComment": "Zeilenkommen&&tar umschalten", + "miToggleBlockComment": "&&Blockkommentar umschalten", + "miMultiCursorAlt": "Für Multi-Cursor zu ALT+Mausklick wechseln", + "miMultiCursorCmd": "Für Multi-Cursor zu Befehlstaste+Mausklick wechseln", + "miMultiCursorCtrl": "Für Multi-Cursor zu STRG+Mausklick wechseln", + "miInsertCursorAbove": "Cursor oberh&&alb hinzufügen", + "miInsertCursorBelow": "Cursor unterhal&&b hinzufügen", + "miInsertCursorAtEndOfEachLineSelected": "C&&ursor an Zeilenenden hinzufügen", + "miAddSelectionToNextFindMatch": "&&Nächstes Vorkommen hinzufügen", + "miAddSelectionToPreviousFindMatch": "Vo&&rheriges Vorkommen hinzufügen", + "miSelectHighlights": "Alle V&&orkommen auswählen", + "miCopyLinesUp": "Zeile nach oben &&kopieren", + "miCopyLinesDown": "Zeile nach unten ko&&pieren", + "miMoveLinesUp": "Zeile nach oben &&verschieben", + "miMoveLinesDown": "Zeile nach &&unten verschieben", + "miSelectAll": "&&Alles auswählen", + "miSmartSelectGrow": "Auswahl &&erweitern", + "miSmartSelectShrink": "Au&&swahl verkleinern", + "miViewExplorer": "&&Explorer", + "miViewSearch": "&&Suchen", + "miViewSCM": "S&&CM", + "miViewDebug": "&&Debuggen", + "miViewExtensions": "E&&xtensions", + "miToggleOutput": "&&Ausgabe", + "miToggleDebugConsole": "De&&bugkonsole", + "miToggleIntegratedTerminal": "&&Integriertes Terminal", + "miMarker": "&&Probleme", + "miAdditionalViews": "Z&&usätzliche Ansichten", + "miCommandPalette": "&&Befehlspalette...", + "miToggleFullScreen": "&&Vollbild umschalten", + "miToggleZenMode": "Zen-Modus umschalten", + "miToggleMenuBar": "Men&&üleiste umschalten", + "miSplitEditor": "&&Editor teilen", + "miToggleEditorLayout": "&&Layout für Editor-Gruppe umschalten", + "miToggleSidebar": "&&Randleiste umschalten", + "miMoveSidebarRight": "&&Seitenleiste nach rechts verschieben", + "miMoveSidebarLeft": "&&Seitenleiste nach links verschieben", + "miTogglePanel": "&&Bereich umschalten", + "miHideStatusbar": "&&Statusleiste ausblenden", + "miShowStatusbar": "&&Statusleiste anzeigen", + "miHideActivityBar": "&&Aktivitätsleiste ausblenden", + "miShowActivityBar": "&&Aktivitätsleiste anzeigen", + "miToggleWordWrap": "&&Zeilenumbruch umschalten", + "miToggleMinimap": "&&Minikarte umschalten", + "miToggleRenderWhitespace": "&&Rendern von Leerzeichen umschalten", + "miToggleRenderControlCharacters": "&&Steuerzeichen umschalten", + "miZoomIn": "&&Vergrößern", + "miZoomOut": "Ver&&kleinern", + "miZoomReset": "&&Zoom zurücksetzen", + "miBack": "&&Zurück", + "miForward": "&&Weiterleiten", + "miNextEditor": "&&Nächster Editor", + "miPreviousEditor": "&&Vorheriger Editor", + "miNextEditorInGroup": "&&Nächster verwendeter Editor in der Gruppe", + "miPreviousEditorInGroup": "&&Zuvor verwendeter Editor in der Gruppe", + "miSwitchEditor": "&&Editor wechseln", + "miFocusFirstGroup": "&&Erste Gruppe", + "miFocusSecondGroup": "&&Zweite Gruppe", + "miFocusThirdGroup": "&&Dritte Gruppe", + "miNextGroup": "&&Nächste Gruppe", + "miPreviousGroup": "&&Vorherige Gruppe", + "miSwitchGroup": "&&Gruppe wechseln", + "miGotoFile": "Gehe zu &&Datei...", + "miGotoSymbolInFile": "Gehe zu &&Symbol in Datei...", + "miGotoSymbolInWorkspace": "Zu Symbol im &&Arbeitsbereich wechseln...", + "miGotoDefinition": "Gehe &&zu Definition", + "miGotoTypeDefinition": "Wechsle zu &&Typdefinition", + "miGotoImplementation": "Wechsle zur &&Implementierung", + "miGotoLine": "Gehe zu &&Zeile...", + "miStartDebugging": "&&Debugging starten", + "miStartWithoutDebugging": "&&Ohne Debugging starten", + "miStopDebugging": "&&Debugging beenden", + "miRestart Debugging": "&&Debugging erneut starten", + "miOpenConfigurations": "&&Konfigurationen öffnen", + "miAddConfiguration": "Konfiguration hinzufügen...", + "miStepOver": "Prozedur&&schritt", + "miStepInto": "Einzelschr&&itt", + "miStepOut": "Rückspr&&ung", + "miContinue": "&&Fortfahren", + "miToggleBreakpoint": "Haltepunkt &&umschalten", + "miConditionalBreakpoint": "&&Bedingter Haltepunkt...", + "miColumnBreakpoint": "S&&paltenhaltepunkt", + "miFunctionBreakpoint": "&&Funktionshaltepunkt...", + "miNewBreakpoint": "&&Neuer Haltepunkt", + "miEnableAllBreakpoints": "Alle Haltepunkte aktivieren", + "miDisableAllBreakpoints": "A&&lle Haltepunkte deaktivieren", + "miRemoveAllBreakpoints": "&&Alle Haltepunkte entfernen", + "miInstallAdditionalDebuggers": "&&Zusätzliche Debugger installieren...", + "mMinimize": "Minimieren", + "mZoom": "Zoom", + "mBringToFront": "Alle in den Vordergrund", + "miSwitchWindow": "Fenster &&wechseln...", + "miToggleDevTools": "&&Entwicklertools umschalten", + "miAccessibilityOptions": "&&Optionen für erleichterte Bedienung", + "miReportIssues": "&&Probleme melden", + "miWelcome": "&&Willkommen", + "miInteractivePlayground": "&&Interactive Spielwiese", + "miDocumentation": "&&Dokumentation", + "miReleaseNotes": "&&Anmerkungen zu dieser Version", + "miKeyboardShortcuts": "&&Referenz für Tastenkombinationen", + "miIntroductoryVideos": "&&Einführungsvideos", + "miTipsAndTricks": "&&Tipps und Tricks", + "miTwitter": "&&Twitter", + "miUserVoice": "&&Featureanforderungen suchen", + "miLicense": "&&Lizenz anzeigen", + "miPrivacyStatement": "&&Datenschutzerklärung", + "miAbout": "&&Info", + "miRunTask": "Aufgabe ausfüh&&ren...", + "miBuildTask": "&&Buildaufgabe ausführen...", + "miRunningTask": "Aktive Auf&&gaben anzeigen...", + "miRestartTask": "Aktuell&&e Aufgabe neu starten...", + "miTerminateTask": "&&Aufgabe beenden...", + "miConfigureTask": "Aufgaben &&konfigurieren", + "miConfigureBuildTask": "Standardbuildaufgabe kon&&figurieren", + "accessibilityOptionsWindowTitle": "Optionen für erleichterte Bedienung", + "miRestartToUpdate": "Zum Aktualisieren neu starten...", + "miCheckingForUpdates": "Überprüfen auf Updates...", + "miDownloadUpdate": "Verfügbares Update herunterladen", + "miDownloadingUpdate": "Das Update wird heruntergeladen...", + "miInstallingUpdate": "Update wird installiert...", + "miCheckForUpdates": "Nach Aktualisierungen suchen...", + "aboutDetail": "\nVersion {0}\nCommit {1}\nDatum {2}\nShell {3}\nRenderer {4}\nKnoten {5}\nArchitektur {6}", + "okButton": "OK" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/code/electron-main/window.i18n.json b/i18n/deu/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..45a3292b3b --- /dev/null +++ b/i18n/deu/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "Sie können weiterhin auf die Menüleiste zugreifen, in dem Sie die **ALT**-Taste drücken." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/code/electron-main/windows.i18n.json b/i18n/deu/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..b4dd631209 --- /dev/null +++ b/i18n/deu/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "OK", + "pathNotExistTitle": "Der Pfad ist nicht vorhanden.", + "pathNotExistDetail": "Der Pfad \"{0}\" scheint auf dem Datenträger nicht mehr vorhanden zu sein.", + "openWorkspace": "&&Öffnen", + "openWorkspaceTitle": "Arbeitsbereich öffnen", + "save": "&&Speichern", + "doNotSave": "&&Nicht speichern", + "cancel": "Abbrechen", + "saveWorkspaceMessage": "Möchten Sie Ihre Arbeitsbereichskonfiguration als Datei speichern?", + "saveWorkspaceDetail": "Speichern Sie Ihren Arbeitsbereich, wenn Sie ihn erneut öffnen möchten.", + "saveWorkspace": "Arbeitsbereich speichern", + "reopen": "Erneut öffnen", + "wait": "Bitte warten.", + "close": "Schließen", + "appStalled": "Das Fenster reagiert nicht mehr.", + "appStalledDetail": "Sie können das Fenster erneut öffnen oder schließen oder weiterhin warten.", + "appCrashed": "Das Fenster ist abgestürzt.", + "appCrashedDetail": "Bitte entschuldigen Sie die Unannehmlichkeiten. Sie können das Fenster erneut öffnen und dort weitermachen, wo Sie aufgehört haben.", + "open": "Öffnen", + "openFolder": "Ordner öffnen", + "openFile": "Datei öffnen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/code/node/cliProcessMain.i18n.json b/i18n/deu/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..d43e6defaf --- /dev/null +++ b/i18n/deu/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Die Erweiterung '{0}' wurde nicht gefunden.", + "notInstalled": "Die Erweiterung \"{0}\" ist nicht installiert.", + "useId": "Stellen Sie sicher, dass Sie die vollständige Erweiterungs-ID (einschließlich des Herausgebers) verwenden. Beispiel: {0}", + "successVsixInstall": "Die Erweiterung \"{0}\" wurde erfolgreich installiert.", + "alreadyInstalled": "Die Erweiterung \"{0}\" ist bereits installiert.", + "foundExtension": "\"{0}\" wurde in Marketplace gefunden.", + "installing": "Wird installiert...", + "successInstall": "Die Erweiterung \"{0}\" v{1} wurde erfolgreich installiert.", + "uninstalling": "{0} wird deinstalliert...", + "successUninstall": "Die Erweiterung \"{0}\" wurde erfolgreich deinstalliert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/deu/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..9d7be32687 --- /dev/null +++ b/i18n/deu/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "Kann die Dateien nicht vergleichen, da eine Datei zu groß ist." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/deu/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..9755cf6a6e --- /dev/null +++ b/i18n/deu/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Schließen", + "header": "Unterschied von {0} zu {1}: Original {2}, {3} Zeilen, Geändert {4}, {5} Zeilen", + "blankLine": "leer", + "equalLine": "Original {0}, geändert {1}: {2}", + "insertLine": "+ geändert {0}: {1}", + "deleteLine": "- Original {0}: {1}", + "editor.action.diffReview.next": "Zum nächsten Unterschied wechseln", + "editor.action.diffReview.prev": "Zum vorherigen Unterschied wechseln" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/deu/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..da3f21ee69 --- /dev/null +++ b/i18n/deu/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "Editor", + "fontFamily": "Steuert die Schriftfamilie.", + "fontWeight": "Steuert die Schriftbreite.", + "fontSize": "Steuert den Schriftgrad in Pixeln.", + "lineHeight": "Steuert die Zeilenhöhe. Verwenden Sie 0, um LineHeight aus der FontSize-Angabe zu berechnen.", + "letterSpacing": "Steuert den Zeichenabstand in Pixeln.", + "lineNumbers": "Steuert die Anzeige von Zeilennummern. Mögliche Werte sind \"Ein\", \"Aus\" und \"Relativ\". \"Relativ\" zeigt die Zeilenanzahl ab der aktuellen Cursorposition.", + "rulers": "Spalten, an denen vertikale Lineale angezeigt werden sollen", + "wordSeparators": "Zeichen, die als Worttrennzeichen verwendet werden, wenn wortbezogene Navigationen oder Vorgänge ausgeführt werden.", + "tabSize": "Die Anzahl der Leerzeichen, denen ein Tabstopp entspricht. Diese Einstellung wird basierend auf dem Inhalt der Datei überschrieben, wenn \"editor.detectIndentation\" aktiviert ist.", + "tabSize.errorMessage": "\"number\" wurde erwartet. Beachten Sie, dass der Wert \"auto\" durch die Einstellung \"editor.detectIndentation\" ersetzt wurde.", + "insertSpaces": "Fügt beim Drücken der TAB-TASTE Leerzeichen ein. Diese Einstellung wird basierend auf dem Inhalt der Datei überschrieben, wenn \"editor.detectIndentation\" aktiviert ist.", + "insertSpaces.errorMessage": "\"boolean\" wurde erwartet. Beachten Sie, dass der Wert \"auto\" durch die Einstellung \"editor.detectIndentation\" ersetzt wurde.", + "detectIndentation": "Beim Öffnen einer Datei werden \"editor.tabSize\" und \"editor.insertSpaces\" basierend auf den Dateiinhalten erkannt.", + "roundedSelection": "Steuert, ob die Auswahl runde Ecken aufweist.", + "scrollBeyondLastLine": "Legt fest, ob der Editor Bildläufe über die letzte Zeile hinaus ausführt.", + "smoothScrolling": "Legt fest, ob der Editor Bildläufe animiert ausführt.", + "minimap.enabled": "Steuert, ob die Minikarte angezeigt wird", + "minimap.showSlider": "Steuert, ob der Minimap-Schieberegler automatisch ausgeblendet wird. Mögliche Werte sind \"always\" und \"mouseover\".", + "minimap.renderCharacters": "Die tatsächlichen Zeichen in einer Zeile rendern (im Gegensatz zu Farbblöcken)", + "minimap.maxColumn": "Breite der Minikarte beschränken, um höchstens eine bestimmte Anzahl von Spalten zu rendern", + "find.seedSearchStringFromSelection": "Steuert, ob wir für die Suchzeichenfolge im Suchwidget aus der Editorauswahl ein Seeding ausführen.", + "find.autoFindInSelection": "Steuert, ob die Kennzeichnung \"In Auswahl suchen\" aktiviert ist, wenn mehrere Zeichen oder Textzeilen im Editor ausgewählt wurden.", + "wordWrap.off": "Zeilenumbrüche erfolgen nie.", + "wordWrap.on": "Der Zeilenumbruch erfolgt an der Breite des Anzeigebereichs.", + "wordWrap.wordWrapColumn": "Der Zeilenbereich erfolgt bei \"editor.wordWrapColumn\".", + "wordWrap.bounded": "Der Zeilenumbruch erfolgt beim Mindestanzeigebereich und \"editor.wordWrapColumn\".", + "wordWrap": "Steuert den Zeilenumbruch. Mögliche Einstellungen sind:\n - \"off\" (Umbruch deaktivieren),\n - \"on\" (Anzeigebereichsumbruch),\n - \"wordWrapColumn\" (Umbruch bei \"editor.wordWrapColumn\") oder\n - \"bounded\" (der Zeilenumbruch erfolgt beim Mindestanzeigebereich und \"editor.wordWrapColumn\").", + "wordWrapColumn": "Steuert die Umbruchspalte des Editors, wenn für \"editor.wordWrap\" die Option \"wordWrapColumn\" oder \"bounded\" festgelegt ist.", + "wrappingIndent": "Steuert den Einzug der umbrochenen Zeilen. Der Wert kann \"none\", \"same\" oder \"indent\" sein.", + "mouseWheelScrollSensitivity": "Ein Multiplikator, der für die Mausrad-Bildlaufereignisse \"deltaX\" und \"deltaY\" verwendet werden soll.", + "multiCursorModifier.ctrlCmd": "Ist unter Windows und Linux der Taste \"STRG\" und unter OSX der Befehlstaste zugeordnet.", + "multiCursorModifier.alt": "Ist unter Windows und Linux der Taste \"Alt\" und unter OSX der Wahltaste zugeordnet. ", + "multiCursorModifier": "Der Modifizierer, der zum Hinzufügen mehrerer Cursor mit der Maus verwendet wird. \"ctrlCmd\" wird unter Windows und Linux der Taste \"STRG\" und unter OSX der Befehlstaste zugeordnet. Die Mausbewegungen \"Gehe zu Definition\" und \"Link öffnen\" werden so angepasst, dass kein Konflikt mit dem Multi-Cursor-Modifizierer entsteht.", + "quickSuggestions.strings": "Schnellvorschläge innerhalb von Zeichenfolgen aktivieren.", + "quickSuggestions.comments": "Schnellvorschläge innerhalb von Kommentaren aktivieren.", + "quickSuggestions.other": "Schnellvorschläge außerhalb von Zeichenfolgen und Kommentaren aktivieren.", + "quickSuggestions": "Steuert, ob Vorschläge während der Eingabe automatisch angezeigt werden sollen.", + "quickSuggestionsDelay": "Steuert die Verzögerung in ms für die Anzeige der Schnellvorschläge.", + "parameterHints": "Aktiviert ein Pop-Up, das Parameter-Dokumentation und Typ-Information während des Tippens anzeigt", + "autoClosingBrackets": "Steuert, ob der Editor Klammern automatisch nach dem Öffnen schließt.", + "formatOnType": "Steuert, ob der Editor Zeilen automatisch nach der Eingabe formatiert.", + "formatOnPaste": "Steuert, ob der Editor den eingefügten Inhalt automatisch formatiert.", + "autoIndent": "Steuert, ob der Editor die Einzüge automatisch anpasst, wenn Benutzer Text eingeben oder Zeilen einfügen oder verschieben. Einzugsregeln der Sprache müssen verfügbar sein. ", + "suggestOnTriggerCharacters": "Steuert, ob Vorschläge automatisch bei der Eingabe von Triggerzeichen angezeigt werden.", + "acceptSuggestionOnEnter": "Steuert, ob Vorschläge über die Eingabetaste (zusätzlich zur TAB-Taste) angenommen werden sollen. Vermeidet Mehrdeutigkeit zwischen dem Einfügen neuer Zeilen oder dem Annehmen von Vorschlägen. Der Wert \"smart\" bedeutet, dass ein Vorschlag nur über die Eingabetaste akzeptiert wird, wenn eine Textänderung vorgenommen wird.", + "acceptSuggestionOnCommitCharacter": "Steuert, ob Vorschläge über Commitzeichen angenommen werden sollen. In JavaScript kann ein Semikolon (\";\") beispielsweise ein Commitzeichen sein, das einen Vorschlag annimmt und dieses Zeichen eingibt.", + "snippetSuggestions.top": "Zeige Snippet Vorschläge über den anderen Vorschlägen.", + "snippetSuggestions.bottom": "Snippet Vorschläge unter anderen Vorschlägen anzeigen.", + "snippetSuggestions.inline": "Zeige Snippet Vorschläge mit anderen Vorschlägen.", + "snippetSuggestions.none": "Snippet Vorschläge nicht anzeigen.", + "snippetSuggestions": "Steuert, ob Codeausschnitte mit anderen Vorschlägen angezeigt und wie diese sortiert werden.", + "emptySelectionClipboard": "Steuert, ob ein Kopiervorgang ohne Auswahl die aktuelle Zeile kopiert.", + "wordBasedSuggestions": "Steuert, ob Vervollständigungen auf Grundlage der Wörter im Dokument berechnet werden sollen.", + "suggestFontSize": "Schriftgröße für Vorschlagswidget", + "suggestLineHeight": "Zeilenhöhe für Vorschlagswidget", + "selectionHighlight": "Steuert, ob der Editor der Auswahl ähnelnde Übereinstimmungen hervorheben soll.", + "occurrencesHighlight": "Steuert, ob der Editor das Vorkommen semantischer Symbole markieren soll.", + "overviewRulerLanes": "Steuert die Anzahl von Dekorationen, die an derselben Position im Übersichtslineal angezeigt werden.", + "overviewRulerBorder": "Steuert, ob um das Übersichtslineal ein Rahmen gezeichnet werden soll.", + "cursorBlinking": "Steuert den Cursoranimationsstil. Gültige Werte sind \"blink\", \"smooth\", \"phase\", \"expand\" und \"solid\".", + "mouseWheelZoom": "Schriftart des Editors vergrößern, wenn das Mausrad verwendet und die STRG-TASTE gedrückt wird", + "cursorStyle": "Steuert den Cursorstil. Gültige Werte sind \"block\", \"block-outline\", \"line\", \"line-thin\", \"underline\" und \"underline-thin\".", + "fontLigatures": "Aktiviert Schriftartligaturen.", + "hideCursorInOverviewRuler": "Steuert die Sichtbarkeit des Cursors im Übersichtslineal.", + "renderWhitespace": "Steuert, wie der Editor Leerzeichen rendert. Mögliche Optionen: \"none\", \"boundary\" und \"all\". Die Option \"boundary\" rendert keine einzelnen Leerzeichen zwischen Wörtern.", + "renderControlCharacters": "Steuert, ob der Editor Steuerzeichen rendern soll.", + "renderIndentGuides": "Steuert, ob der Editor Einzugsführungslinien rendern soll.", + "renderLineHighlight": "Steuert, wie der Editor die aktuelle Zeilenhervorhebung rendern soll. Mögliche Werte sind \"none\", \"gutter\", \"line\" und \"all\".", + "codeLens": "Steuert, ob der Editor CodeLenses anzeigt.", + "folding": "Steuert, ob für den Editor Codefaltung aktiviert ist.", + "showFoldingControls": "Steuert, ob die Falt-Steuerelemente an der Leiste automatisch ausgeblendet werden.", + "matchBrackets": "Übereinstimmende Klammern hervorheben, wenn eine davon ausgewählt wird.", + "glyphMargin": "Steuert, ob der Editor den vertikalen Glyphenrand rendert. Der Glyphenrand wird hauptsächlich zum Debuggen verwendet.", + "useTabStops": "Das Einfügen und Löschen von Leerzeichen folgt auf Tabstopps.", + "trimAutoWhitespace": "Nachfolgendes automatisch eingefügtes Leerzeichen entfernen", + "stablePeek": "Peek-Editoren geöffnet lassen, auch wenn auf ihren Inhalt doppelgeklickt oder die ESC-TASTE gedrückt wird.", + "dragAndDrop": "Steuert, ob der Editor das Verschieben einer Auswahl per Drag and Drop zulässt.", + "accessibilitySupport.auto": "Der Editor verwendet Plattform-APIs, um zu erkennen, wenn eine Sprachausgabe angefügt wird.", + "accessibilitySupport.on": "Der Editor wird durchgehend für die Verwendung mit einer Sprachausgabe optimiert.", + "accessibilitySupport.off": "Der Editor wird nie für die Verwendung mit einer Sprachausgabe optimiert. ", + "accessibilitySupport": "Steuert, ob der Editor in einem Modus ausgeführt werden soll, in dem er für die Sprachausgabe optimiert wird.", + "links": "Steuert, ob der Editor Links erkennen und anklickbar machen soll", + "colorDecorators": "Steuert, ob der Editor die Inline-Farbdecorators und die Farbauswahl rendern soll.", + "sideBySide": "Steuert, ob der Diff-Editor das Diff nebeneinander oder inline anzeigt.", + "ignoreTrimWhitespace": "Steuert, ob der Diff-Editor Änderungen in führenden oder nachgestellten Leerzeichen als Diffs anzeigt.", + "renderIndicators": "Steuert, ob der Diff-Editor die Indikatoren \"+\" und \"-\" für hinzugefügte/entfernte Änderungen anzeigt.", + "selectionClipboard": "Steuert, ob die primäre Linux-Zwischenablage unterstützt werden soll." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/common/config/defaultConfig.i18n.json b/i18n/deu/src/vs/editor/common/config/defaultConfig.i18n.json new file mode 100644 index 0000000000..bfb7a927a5 --- /dev/null +++ b/i18n/deu/src/vs/editor/common/config/defaultConfig.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorViewAccessibleLabel": "Editor-Inhalt" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/deu/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..ecc0b34a95 --- /dev/null +++ b/i18n/deu/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "Der Editor ist zurzeit nicht verfügbar. Drücken Sie Alt+F1 für Optionen.", + "editorViewAccessibleLabel": "Editor-Inhalt" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/common/controller/cursor.i18n.json b/i18n/deu/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..670f1b94ef --- /dev/null +++ b/i18n/deu/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "Unerwartete Ausnahme beim Ausführen des Befehls." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/deu/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..6f13a8d3d3 --- /dev/null +++ b/i18n/deu/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "Fehler des Modus bei der Tokenumwandlung der Eingabe." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/deu/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..a3954f8096 --- /dev/null +++ b/i18n/deu/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "Nur-Text" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/deu/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..110b870300 --- /dev/null +++ b/i18n/deu/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "Die folgenden Dateien wurden in der Zwischenzeit geändert: {0}", + "summary.0": "Keine Änderungen vorgenommen", + "summary.nm": "{0} Änderungen am Text in {1} Dateien vorgenommen", + "summary.n0": "{0} Änderungen am Text in einer Datei vorgenommen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/deu/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..c0b3d41aab --- /dev/null +++ b/i18n/deu/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Contributes-Sprachdeklarationen", + "vscode.extension.contributes.languages.id": "Die ID der Sprache.", + "vscode.extension.contributes.languages.aliases": "Namealiase für die Sprache.", + "vscode.extension.contributes.languages.extensions": "Dateierweiterungen, die der Sprache zugeordnet sind.", + "vscode.extension.contributes.languages.filenames": "Dateinamen, die der Sprache zugeordnet sind.", + "vscode.extension.contributes.languages.filenamePatterns": "Dateinamen-Globmuster, die Sprache zugeordnet sind.", + "vscode.extension.contributes.languages.mimetypes": "MIME-Typen, die der Sprache zugeordnet sind.", + "vscode.extension.contributes.languages.firstLine": "Ein regulärer Ausdruck, der mit der ersten Zeile einer Datei der Sprache übereinstimmt.", + "vscode.extension.contributes.languages.configuration": "Ein relativer Pfad zu einer Datei mit Konfigurationsoptionen für die Sprache." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/deu/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/deu/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/deu/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..fdb215e646 --- /dev/null +++ b/i18n/deu/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "Hintergrundfarbe zur Hervorhebung der Zeile an der Cursorposition.", + "lineHighlightBorderBox": "Hintergrundfarbe für den Rahmen um die Zeile an der Cursorposition.", + "rangeHighlight": "Hintergrundfarbe hervorgehobener Bereiche (beispielsweise durch Features wie Quick Open und Suche).", + "caret": "Farbe des Cursors im Editor.", + "editorCursorBackground": "Hintergrundfarbe vom Editor-Cursor. Erlaubt die Anpassung der Farbe von einem Zeichen, welches von einem Block-Cursor überdeckt wird.", + "editorWhitespaces": "Farbe der Leerzeichen im Editor.", + "editorIndentGuides": "Farbe der Führungslinien für Einzüge im Editor.", + "editorLineNumbers": "Zeilennummernfarbe im Editor.", + "editorRuler": "Farbe des Editor-Lineals.", + "editorCodeLensForeground": "Vordergrundfarbe der CodeLens-Links im Editor", + "editorBracketMatchBackground": "Hintergrundfarbe für zusammengehörige Klammern", + "editorBracketMatchBorder": "Farbe für zusammengehörige Klammern", + "editorOverviewRulerBorder": "Farbe des Rahmens für das Übersicht-Lineal.", + "editorGutter": "Hintergrundfarbe der Editorleiste. Die Leiste enthält die Glyphenränder und die Zeilennummern.", + "errorForeground": "Vordergrundfarbe von Fehlerunterstreichungen im Editor.", + "errorBorder": "Rahmenfarbe von Fehlerunterstreichungen im Editor.", + "warningForeground": "Vordergrundfarbe von Warnungsunterstreichungen im Editor.", + "warningBorder": "Rahmenfarbe von Warnungsunterstreichungen im Editor.", + "overviewRulerRangeHighlight": "Übersichtslineal-Markierungsfarbe für Bereichshervorhebungen.", + "overviewRuleError": "Übersichtslineal-Markierungsfarbe für Fehler.", + "overviewRuleWarning": "Übersichtslineal-Markierungsfarbe für Warnungen.", + "overviewRuleInfo": "Übersichtslineal-Markierungsfarbe für Informationen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json b/i18n/deu/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json new file mode 100644 index 0000000000..dae0ed38e1 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "introMsg": "Vielen Dank, dass Sie die Optionen für Barrierefreiheit von VS Code testen.", + "status": "Status:", + "tabFocusModeOnMsg": "Durch Drücken der TAB-TASTE im aktuellen Editor wird der Fokus in das nächste Element verschoben, das den Fokus erhalten kann. Schalten Sie dieses Verhalten um, indem Sie {0} drücken.", + "tabFocusModeOnMsgNoKb": "Durch Drücken der TAB-TASTE im aktuellen Editor wird der Fokus in das nächste Element verschoben, das den Fokus erhalten kann. Der {0}-Befehl kann zurzeit nicht durch eine Tastenzuordnung ausgelöst werden.", + "tabFocusModeOffMsg": "Durch Drücken der TAB-TASTE im aktuellen Editor wird das Tabstoppzeichen eingefügt. Schalten Sie dieses Verhalten um, indem Sie {0} drücken.", + "tabFocusModeOffMsgNoKb": "Durch Drücken der TAB-TASTE im aktuellen Editor wird das Tabstoppzeichen eingefügt. Der {0}-Befehl kann zurzeit nicht durch eine Tastenzuordnung ausgelöst werden.", + "outroMsg": "Sie können diese QuickInfo schließen und durch Drücken von ESC zum Editor zurückkehren.", + "ShowAccessibilityHelpAction": "Hilfe zur Barrierefreiheit anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/deu/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..7e42a3b689 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "Gehe zu Klammer" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/deu/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..d8ebb8aa8d --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "Caretzeichen nach links verschieben", + "caret.moveRight": "Caretzeichen nach rechts verschieben" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/deu/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..f87f2cc87c --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "Buchstaben austauschen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/deu/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..5c1753028e --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "Ausschneiden", + "actions.clipboard.copyLabel": "Kopieren", + "actions.clipboard.pasteLabel": "Einfügen", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "Mit Syntaxhervorhebung kopieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/deu/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..063db93f9a --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "Zeilenkommentar umschalten", + "comment.line.add": "Zeilenkommentar hinzufügen", + "comment.line.remove": "Zeilenkommentar entfernen", + "comment.block": "Blockkommentar umschalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/deu/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..e60aec023a --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "Editor-Kontextmenü anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/deu/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..3ac72a9944 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Suchen", + "placeholder.find": "Suchen", + "label.previousMatchButton": "Vorherige Übereinstimmung", + "label.nextMatchButton": "Nächste Übereinstimmung", + "label.toggleSelectionFind": "In Auswahl suchen", + "label.closeButton": "Schließen", + "label.replace": "Ersetzen", + "placeholder.replace": "Ersetzen", + "label.replaceButton": "Ersetzen", + "label.replaceAllButton": "Alle ersetzen", + "label.toggleReplaceButton": "Ersetzen-Modus wechseln", + "title.matchesCountLimit": "Nur die ersten 999 Ergebnisse werden hervorgehoben, alle Suchvorgänge beziehen sich aber auf den gesamten Text.", + "label.matchesLocation": "{0} von {1}", + "label.noResults": "Keine Ergebnisse" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/deu/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..ee2488c6f6 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Suchen", + "placeholder.find": "Suchen", + "label.previousMatchButton": "Vorherige Übereinstimmung", + "label.nextMatchButton": "Nächste Übereinstimmung", + "label.closeButton": "Schließen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/deu/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..3a855f911e --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "Suchen", + "findNextMatchAction": "Nächstes Element suchen", + "findPreviousMatchAction": "Vorheriges Element suchen", + "nextSelectionMatchFindAction": "Nächste Auswahl suchen", + "previousSelectionMatchFindAction": "Vorherige Auswahl suchen", + "startReplace": "Ersetzen", + "addSelectionToNextFindMatch": "Auswahl zur nächsten Übereinstimmungssuche hinzufügen", + "addSelectionToPreviousFindMatch": "Letzte Auswahl zu vorheriger Übereinstimmungssuche hinzufügen", + "moveSelectionToNextFindMatch": "Letzte Auswahl in nächste Übereinstimmungssuche verschieben", + "moveSelectionToPreviousFindMatch": "Letzte Auswahl in vorherige Übereinstimmungssuche verschieben", + "selectAllOccurrencesOfFindMatch": "Alle Vorkommen auswählen und Übereinstimmung suchen", + "changeAll.label": "Alle Vorkommen ändern", + "showNextFindTermAction": "Nächsten Suchbegriff anzeigen", + "showPreviousFindTermAction": "Vorherigen Suchbegriff anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/deu/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..a5fbd6a577 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "Auffalten", + "unFoldRecursivelyAction.label": "Faltung rekursiv aufheben", + "foldAction.label": "Falten", + "foldRecursivelyAction.label": "Rekursiv falten", + "foldAllAction.label": "Alle falten", + "unfoldAllAction.label": "Alle auffalten", + "foldLevelAction.label": "Faltebene {0}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/deu/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..98889f7c53 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "1 Formatierung in Zeile {0} vorgenommen", + "hintn1": "{0} Formatierungen in Zeile {1} vorgenommen", + "hint1n": "1 Formatierung zwischen Zeilen {0} und {1} vorgenommen", + "hintnn": "{0} Formatierungen zwischen Zeilen {1} und {2} vorgenommen", + "formatDocument.label": "Format Document", + "formatSelection.label": "Format Selection" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json b/i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json new file mode 100644 index 0000000000..06e1bd4df6 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "Keine Definition gefunden für \"{0}\".", + "generic.noResults": "Keine Definition gefunden", + "meta.title": " – {0} Definitionen", + "actions.goToDecl.label": "Gehe zu Definition", + "actions.goToDeclToSide.label": "Definition an der Seite öffnen", + "actions.previewDecl.label": "Peek-Definition", + "goToImplementation.noResultWord": "Keine Implementierung gefunden für \"{0}\"", + "goToImplementation.generic.noResults": "Keine Implementierung gefunden", + "meta.implementations.title": "{0} Implementierungen", + "actions.goToImplementation.label": "Zur Implementierung wechseln", + "actions.peekImplementation.label": "Vorschau der Implementierung anzeigen", + "goToTypeDefinition.noResultWord": "Keine Typendefinition gefunden für \"{0}\"", + "goToTypeDefinition.generic.noResults": "Keine Typendefinition gefunden", + "meta.typeDefinitions.title": "{0} Typdefinitionen", + "actions.goToTypeDefinition.label": "Zur Typdefinition wechseln", + "actions.peekTypeDefinition.label": "Vorschau der Typdefinition anzeigen", + "multipleResults": "Klicken Sie, um {0} Definitionen anzuzeigen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..64cece8a0c --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "Keine Definition gefunden für \"{0}\".", + "generic.noResults": "Keine Definition gefunden", + "meta.title": " – {0} Definitionen", + "actions.goToDecl.label": "Gehe zu Definition", + "actions.goToDeclToSide.label": "Definition an der Seite öffnen", + "actions.previewDecl.label": "Peek-Definition", + "goToImplementation.noResultWord": "Keine Implementierung gefunden für \"{0}\"", + "goToImplementation.generic.noResults": "Keine Implementierung gefunden", + "meta.implementations.title": "{0} Implementierungen", + "actions.goToImplementation.label": "Zur Implementierung wechseln", + "actions.peekImplementation.label": "Vorschau der Implementierung anzeigen", + "goToTypeDefinition.noResultWord": "Keine Typendefinition gefunden für \"{0}\"", + "goToTypeDefinition.generic.noResults": "Keine Typendefinition gefunden", + "meta.typeDefinitions.title": "{0} Typdefinitionen", + "actions.goToTypeDefinition.label": "Zur Typdefinition wechseln", + "actions.peekTypeDefinition.label": "Vorschau der Typdefinition anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..c14b4e17de --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "Klicken Sie, um {0} Definitionen anzuzeigen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/deu/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..df23051086 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "Gehe zum nächsten Fehler oder zur nächsten Warnung", + "markerAction.previous.label": "Gehe zum vorherigen Fehler oder zur vorherigen Warnung", + "editorMarkerNavigationError": "Editormarkierung: Farbe bei Fehler des Navigationswidgets.", + "editorMarkerNavigationWarning": "Editormarkierung: Farbe bei Warnung des Navigationswidgets.", + "editorMarkerNavigationBackground": "Editormarkierung: Hintergrund des Navigationswidgets." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/deu/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..8c089575ec --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "Hovern anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/deu/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..a336411a2b --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "Wird geladen..." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/deu/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..5a9e0f8dbd --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "Durch vorherigen Wert ersetzen", + "InPlaceReplaceAction.next.label": "Durch nächsten Wert ersetzen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/deu/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..57feb98461 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "Einzug in Leerzeichen konvertieren", + "indentationToTabs": "Einzug in Tabstopps konvertieren", + "configuredTabSize": "Konfigurierte Tabulatorgröße", + "selectTabWidth": "Tabulatorgröße für aktuelle Datei auswählen", + "indentUsingTabs": "Einzug mithilfe von Tabstopps", + "indentUsingSpaces": "Einzug mithilfe von Leerzeichen", + "detectIndentation": "Einzug aus Inhalt erkennen", + "editor.reindentlines": "Neuen Einzug für Zeilen festlegen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json b/i18n/deu/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..ba378c8154 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Entwickler: TM-Bereiche untersuchen", + "inspectTMScopesWidget.loading": "Wird geladen..." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/deu/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..83346034bc --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "Zeile nach oben kopieren", + "lines.copyDown": "Zeile nach unten kopieren", + "lines.moveUp": "Zeile nach oben verschieben", + "lines.moveDown": "Zeile nach unten verschieben", + "lines.sortAscending": "Zeilen aufsteigend sortieren", + "lines.sortDescending": "Zeilen absteigend sortieren", + "lines.trimTrailingWhitespace": "Nachgestelltes Leerzeichen kürzen", + "lines.delete": "Zeile löschen", + "lines.indent": "Zeileneinzug", + "lines.outdent": "Zeile ausrücken", + "lines.insertBefore": "Zeile oben einfügen", + "lines.insertAfter": "Zeile unten einfügen", + "lines.deleteAllLeft": "Alle übrigen löschen", + "lines.deleteAllRight": "Alle rechts löschen", + "lines.joinLines": "Zeilen verknüpfen", + "editor.transpose": "Zeichen um den Cursor herum transponieren", + "editor.transformToUppercase": "In Großbuchstaben umwandeln", + "editor.transformToLowercase": "In Kleinbuchstaben umwandeln" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/deu/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..c6bf3aee52 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "BEFEHLSTASTE + Mausklick zum Aufrufen des Links", + "links.navigate": "STRG + Mausklick zum Aufrufen des Links", + "links.command.mac": "Cmd + Klick um Befehl auszuführen", + "links.command": "Ctrl + Klick um Befehl auszuführen.", + "links.navigate.al": "ALT + Mausklick zum Aufrufen des Links", + "links.command.al": "Alt + Klick um Befehl auszuführen.", + "invalid.url": "Fehler beim Öffnen dieses Links, weil er nicht wohlgeformt ist: {0}", + "missing.url": "Fehler beim Öffnen dieses Links, weil das Ziel fehlt.", + "label": "Link öffnen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/deu/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..bca569c148 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "Cursor oberhalb hinzufügen", + "mutlicursor.insertBelow": "Cursor unterhalb hinzufügen", + "mutlicursor.insertAtEndOfEachLineSelected": "Cursor an Zeilenenden hinzufügen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/deu/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..b3c0af4796 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "Parameterhinweise auslösen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/deu/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..ef4790143a --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0}, Hinweis" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/deu/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..e4e5f16454 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "Korrekturen anzeigen ({0})", + "quickFix": "Korrekturen anzeigen", + "quickfix.trigger.label": "Schnelle Problembehebung" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..0c466f1d1c --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": " – {0} Verweise", + "references.action.label": "Alle Verweise suchen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..b9ebed9f28 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "Wird geladen..." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..bf29d7bd55 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "Symbol in {0} in Zeile {1}, Spalte {2}", + "aria.fileReferences.1": "1 Symbol in {0}, vollständiger Pfad {1}", + "aria.fileReferences.N": "{0} Symbole in {1}, vollständiger Pfad {2}", + "aria.result.0": "Es wurden keine Ergebnisse gefunden.", + "aria.result.1": "1 Symbol in {0} gefunden", + "aria.result.n1": "{0} Symbole in {1} gefunden", + "aria.result.nm": "{0} Symbole in {1} Dateien gefunden" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..164f4a8623 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "Fehler beim Auflösen der Datei.", + "referencesCount": "{0} Verweise", + "referenceCount": "{0} Verweis", + "missingPreviewMessage": "Keine Vorschau verfügbar.", + "treeAriaLabel": "Verweise", + "noResults": "Keine Ergebnisse", + "peekView.alternateTitle": "Verweise", + "peekViewTitleBackground": "Hintergrundfarbe des Titelbereichs der Peek-Ansicht.", + "peekViewTitleForeground": "Farbe des Titels in der Peek-Ansicht.", + "peekViewTitleInfoForeground": "Farbe der Titelinformationen in der Peek-Ansicht.", + "peekViewBorder": "Farbe der Peek-Ansichtsränder und des Pfeils.", + "peekViewResultsBackground": "Hintergrundfarbe der Ergebnisliste in der Peek-Ansicht.", + "peekViewResultsMatchForeground": "Vordergrundfarbe für Zeilenknoten in der Ergebnisliste der Peek-Ansicht.", + "peekViewResultsFileForeground": "Vordergrundfarbe für Dateiknoten in der Ergebnisliste der Peek-Ansicht.", + "peekViewResultsSelectionBackground": "Hintergrundfarbe des ausgewählten Eintrags in der Ergebnisliste der Peek-Ansicht.", + "peekViewResultsSelectionForeground": "Vordergrundfarbe des ausgewählten Eintrags in der Ergebnisliste der Peek-Ansicht.", + "peekViewEditorBackground": "Hintergrundfarbe des Peek-Editors.", + "peekViewEditorGutterBackground": "Hintergrundfarbe der Leiste im Peek-Editor.", + "peekViewResultsMatchHighlight": "Farbe für Übereinstimmungsmarkierungen in der Ergebnisliste der Peek-Ansicht.", + "peekViewEditorMatchHighlight": "Farbe für Übereinstimmungsmarkierungen im Peek-Editor." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/deu/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..eaa72719bd --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "Kein Ergebnis.", + "aria": "\"{0}\" erfolgreich in \"{1}\" umbenannt. Zusammenfassung: {2}", + "rename.failed": "Fehler bei der Ausführung der Umbenennung.", + "rename.label": "Symbol umbenennen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/deu/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..ae21bf2927 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "Benennen Sie die Eingabe um. Geben Sie einen neuen Namen ein, und drücken Sie die EINGABETASTE, um den Commit auszuführen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/deu/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..8f5739ebde --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "Auswahl erweitern", + "smartSelect.shrink": "Auswahl verkleinern" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/deu/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..a276c12c15 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "Durch Annahme von \"{0}\" wurde folgender Text eingefügt: {1}", + "suggest.trigger.label": "Vorschlag auslösen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/deu/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..fabf97964d --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "Hintergrundfarbe des Vorschlagswidgets.", + "editorSuggestWidgetBorder": "Rahmenfarbe des Vorschlagswidgets.", + "editorSuggestWidgetForeground": "Vordergrundfarbe des Vorschlagswidgets.", + "editorSuggestWidgetSelectedBackground": "Hintergrundfarbe des ausgewählten Eintrags im Vorschlagswidget.", + "editorSuggestWidgetHighlightForeground": "Farbe der Trefferhervorhebung im Vorschlagswidget.", + "readMore": "Mehr anzeigen...{0}", + "suggestionWithDetailsAriaLabel": "{0}, Vorschlag, hat Details", + "suggestionAriaLabel": "{0}, Vorschlag", + "readLess": "Weniger anzeigen...{0}", + "suggestWidget.loading": "Wird geladen...", + "suggestWidget.noSuggestions": "Keine Vorschläge.", + "suggestionAriaAccepted": "{0}, angenommen", + "ariaCurrentSuggestionWithDetails": "{0}, Vorschlag, hat Details", + "ariaCurrentSuggestion": "{0}, Vorschlag" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/deu/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..22a9d08a54 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "TAB-Umschalttaste verschiebt Fokus" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/deu/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..082b04073d --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "Hintergrundfarbe eines Symbols beim Lesezugriff (beispielsweise beim Lesen einer Variablen).", + "wordHighlightStrong": "Hintergrundfarbe eines Symbols beim Schreibzugriff (beispielsweise beim Schreiben in eine Variable).", + "overviewRulerWordHighlightForeground": "Übersichtslineal-Markierungsfarbe für Symbolhervorhebungen.", + "overviewRulerWordHighlightStrongForeground": "Übersichtslineal-Markierungsfarbe für Schreibzugriffs-Symbolhervorhebungen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/deu/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..e2befd5419 --- /dev/null +++ b/i18n/deu/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Schließen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/deu/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..5d27448c2f --- /dev/null +++ b/i18n/deu/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Unbekannte Sprache in \"contributes.{0}.language\". Bereitgestellter Wert: {1}", + "invalid.scopeName": "In \"contributes.{0}.scopeName\" wurde eine Zeichenfolge erwartet. Bereitgestellter Wert: {1}", + "invalid.path.0": "Expected string in `contributes.{0}.path`. Provided value: {1}", + "invalid.injectTo": "Ungültiger Wert in \"contributes.{0}.injectTo\". Es muss sich um ein Array von Sprachbereichsnamen handeln. Bereitgestellter Wert: {1}", + "invalid.embeddedLanguages": "Ungültiger Wert in \"contributes.{0}.embeddedLanguages\". Muss eine Objektzuordnung von Bereichsname zu Sprache sein. Angegebener Wert: {1}", + "invalid.path.1": "Es wurde erwartet, dass \"contributes.{0}.path\" ({1}) im Ordner ({2}) der Extension enthalten ist. Dies führt ggf. dazu, dass die Extension nicht portierbar ist.", + "no-tm-grammar": "Keine TM-Grammatik für diese Sprache registriert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/deu/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..95abd702f5 --- /dev/null +++ b/i18n/deu/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Fehler beim Analysieren von {0}: {1}", + "schema.openBracket": "Das öffnende Klammerzeichen oder die Zeichenfolgensequenz.", + "schema.closeBracket": "Das schließende Klammerzeichen oder die Zeichenfolgensequenz.", + "schema.comments": "Definiert die Kommentarsymbole.", + "schema.blockComments": "Definiert, wie Blockkommentare markiert werden.", + "schema.blockComment.begin": "Die Zeichenfolge, mit der ein Blockkommentar beginnt.", + "schema.blockComment.end": "Die Zeichenfolge, die einen Blockkommentar beendet.", + "schema.lineComment": "Die Zeichenfolge, mit der ein Zeilenkommentar beginnt.", + "schema.brackets": "Definiert die Klammersymbole, die den Einzug vergrößern oder verkleinern.", + "schema.autoClosingPairs": "Definiert die Klammerpaare. Wenn eine öffnende Klammer eingegeben wird, wird die schließende Klammer automatisch eingefügt.", + "schema.autoClosingPairs.notIn": "Definiert eine Liste von Bereichen, in denen die automatischen Paare deaktiviert sind.", + "schema.surroundingPairs": "Definiert die Klammerpaare, in die eine ausgewählte Zeichenfolge eingeschlossen werden kann.", + "schema.wordPattern": "Die Worddefinition für die Sprache.", + "schema.wordPattern.pattern": "RegExp Muster für Wortübereinstimmungen.", + "schema.wordPattern.flags": "RegExp Kennzeichen für Wortübereinstimmungen", + "schema.wordPattern.flags.errorMessage": "Muss mit dem Muster `/^([gimuy]+)$/` übereinstimmen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/deu/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..2e12621a8b --- /dev/null +++ b/i18n/deu/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Trägt TextMate-Tokenizer bei.", + "vscode.extension.contributes.grammars.language": "Der Sprachbezeichner, für den diese Syntax beigetragen wird.", + "vscode.extension.contributes.grammars.scopeName": "Der TextMate-Bereichsname, der von der tmLanguage-Datei verwendet wird.", + "vscode.extension.contributes.grammars.path": "Der Pfad der tmLanguage-Datei. Der Pfad ist relativ zum Extensionordner und beginnt normalerweise mit \". /syntaxes/\".", + "vscode.extension.contributes.grammars.embeddedLanguages": "Eine Zuordnung zwischen Bereichsname und Sprach-ID, wenn diese Grammatik eingebettete Sprachen enthält.", + "vscode.extension.contributes.grammars.injectTo": "Die Liste der Sprachbereichsnamen, in die diese Grammatik injiziert wird." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/deu/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/deu/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/deu/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..b46c6405d4 --- /dev/null +++ b/i18n/deu/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "Menüelemente müssen ein Array sein.", + "requirestring": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "optstring": "Die Eigenschaft \"{0}\" kann ausgelassen werden oder muss vom Typ \"string\" sein.", + "vscode.extension.contributes.menuItem.command": "Der Bezeichner des auszuführenden Befehls. Der Befehl muss im Abschnitt \"commands\" deklariert werden.", + "vscode.extension.contributes.menuItem.alt": "Der Bezeichner eines alternativ auszuführenden Befehls. Der Befehl muss im Abschnitt \"commands\" deklariert werden.", + "vscode.extension.contributes.menuItem.when": "Eine Bedingung, die TRUE sein muss, damit dieses Element angezeigt wird.", + "vscode.extension.contributes.menuItem.group": "Die Gruppe, zu der dieser Befehl gehört.", + "vscode.extension.contributes.menus": "Trägt Menüelemente zum Editor bei.", + "menus.commandPalette": "Die Befehlspalette ", + "menus.editorTitle": "Das Editor-Titelmenü.", + "menus.editorContext": "Das Editor-Kontextmenü.", + "menus.explorerContext": "Das Kontextmenü des Datei-Explorers.", + "menus.editorTabContext": "Das Kontextmenü für die Editor-Registerkarten", + "menus.debugCallstackContext": "Das Kontextmenü für den Debug-Callstack", + "menus.scmTitle": "Das Titelmenü der Quellcodeverwaltung", + "menus.resourceGroupContext": "Das Ressourcengruppen-Kontextmenü der Quellcodeverwaltung", + "menus.resourceStateContext": "Das Ressourcenstatus-Kontextmenü der Quellcodeverwaltung", + "view.viewTitle": "Das beigetragene Editor-Titelmenü.", + "view.itemContext": "Das beigetragene Anzeigeelement-Kontextmenü.", + "nonempty": "Es wurde ein nicht leerer Wert erwartet.", + "opticon": "Die Eigenschaft \"icon\" kann ausgelassen werden oder muss eine Zeichenfolge oder ein Literal wie \"{dark, light}\" sein.", + "requireStringOrObject": "Die Eigenschaft \"{0}\" ist obligatorisch und muss vom Typ \"Zeichenfolge\" oder \"Objekt\" sein.", + "requirestrings": "Die Eigenschaften \"{0}\" und \"{1}\" sind obligatorisch und müssen vom Typ \"Zeichenfolge\" sein.", + "vscode.extension.contributes.commandType.command": "Der Bezeichner des auszuführenden Befehls.", + "vscode.extension.contributes.commandType.title": "Der Titel, durch den der Befehl in der Benutzeroberfläche dargestellt wird.", + "vscode.extension.contributes.commandType.category": "(Optionale) Kategoriezeichenfolge, nach der der Befehl in der Benutzeroberfläche gruppiert wird.", + "vscode.extension.contributes.commandType.icon": "(Optional) Das Symbol, das verwendet wird, um den Befehl in der Benutzeroberfläche darzustellen. Es handelt sich um einen Dateipfad oder eine designfähige Konfiguration.", + "vscode.extension.contributes.commandType.icon.light": "Der Symbolpfad, wenn ein helles Design verwendet wird.", + "vscode.extension.contributes.commandType.icon.dark": "Der Symbolpfad, wenn ein dunkles Design verwendet wird.", + "vscode.extension.contributes.commands": "Trägt Befehle zur Befehlspalette bei.", + "dup": "Der Befehl \"{0}\" ist mehrmals im Abschnitt \"commands\" vorhanden.", + "menuId.invalid": "\"{0}\" ist kein gültiger Menübezeichner.", + "missing.command": "Das Menüelement verweist auf einen Befehl \"{0}\", der im Abschnitt \"commands\" nicht definiert ist.", + "missing.altCommand": "Das Menüelement verweist auf einen Alternativbefehl \"{0}\", der im Abschnitt \"commands\" nicht definiert ist.", + "dupe.command": "Das Menüelement verweist auf den gleichen Befehl wie der Standard- und der Alternativbefehl.", + "nosupport.altCommand": "Leider unterstützt zurzeit nur die Gruppe \"navigation\" des Menüs \"editor/title\" Alternativbefehle." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/deu/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..5b2dad7ec6 --- /dev/null +++ b/i18n/deu/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "Standard-Konfiguration überschreibt", + "overrideSettings.description": "Zu überschreibende Einstellungen für Sprache {0} konfigurieren.", + "overrideSettings.defaultDescription": "Zu überschreibende Editor-Einstellungen für eine Sprache konfigurieren.", + "config.property.languageDefault": "\"{0}\" kann nicht registriert werden. Die Eigenschaft stimmt mit dem Eigenschaftsmuster '\\\\[.*\\\\]$' zum Beschreiben sprachspezifischer Editor-Einstellungen überein. Verwenden Sie den Beitrag \"configurationDefaults\".", + "config.property.duplicate": "\"{0}\" kann nicht registriert werden. Diese Eigenschaft ist bereits registriert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/environment/node/argv.i18n.json b/i18n/deu/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..2cc369659b --- /dev/null +++ b/i18n/deu/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "Argumente im Modus \"--goto\" müssen im Format \"DATEI(:ZEILE(:ZEICHEN))\" vorliegen.", + "diff": "Vergleicht zwei Dateien.", + "add": "Fügt einen oder mehrere Ordner zum letzten aktiven Fenster hinzu.", + "goto": "Öffnet eine Datei im Pfad in der angegebenen Zeile und an der Zeichenposition.", + "locale": "Das zu verwendende Gebietsschema (z. B. en-US oder zh-TW).", + "newWindow": "Erzwingt eine neue Instanz des Codes.", + "performance": "Startet mit aktiviertem Befehl \"Developer: Startup Performance\".", + "prof-startup": "CPU-Profiler beim Start ausführen", + "reuseWindow": "Erzwingt das Öffnen einer Datei oder eines Ordners im letzten aktiven Fenster.", + "userDataDir": "Gibt das Verzeichnis an, in dem Benutzerdaten gespeichert werden. Nützlich, wenn die Ausführung als \"root\" erfolgt.", + "verbose": "Ausführliche Ausgabe (impliziert \"-wait\").", + "wait": "Wartet, bis das Fenster geschlossen wurde, bevor die Rückgabe erfolgt.", + "extensionHomePath": "Legen Sie den Stammpfad für Extensions fest.", + "listExtensions": "Listet die installierten Extensions auf.", + "showVersions": "Zeigt Versionen der installierten Erweiterungen an, wenn \"--list-extension\" verwendet wird.", + "installExtension": "Installiert eine Extension.", + "uninstallExtension": "Deinstalliert eine Extension.", + "experimentalApis": "Aktiviert vorgeschlagene API-Features für eine Erweiterung.", + "disableExtensions": "Deaktiviert alle installierten Extensions.", + "disableGPU": "Deaktiviert die GPU-Hardwarebeschleunigung.", + "version": "Gibt die Version aus.", + "help": "Gibt die Syntax aus.", + "usage": "Verwendung", + "options": "Optionen", + "paths": "Pfade", + "optionsUpperCase": "Optionen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/deu/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..676c1ea0ab --- /dev/null +++ b/i18n/deu/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Kein Arbeitsbereich." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/deu/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..a23d461a5b --- /dev/null +++ b/i18n/deu/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Extensions", + "preferences": "Einstellungen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/deu/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..15638e4f20 --- /dev/null +++ b/i18n/deu/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Die Extension wurde nicht gefunden.", + "noCompatible": "Eine kompatible Version von {0} mit dieser Version des Codes wurde nicht gefunden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/deu/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..63a4865524 --- /dev/null +++ b/i18n/deu/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "Die Erweiterung ist ungültig: \"package.json\" ist keine JSON-Datei.", + "restartCode": "Bitte starten Sie Code vor der Neuinstallation von {0} neu.", + "installDependeciesConfirmation": "Durch das Installieren von \"{0}\" werden auch die abhängigen Komponenten installiert. Möchten Sie den Vorgang fortsetzen?", + "install": "Ja", + "doNotInstall": "Nein", + "uninstallDependeciesConfirmation": "Möchten Sie nur \"{0}\" oder auch die zugehörigen Abhängigkeiten deinstallieren?", + "uninstallOnly": "Nur", + "uninstallAll": "Alle", + "cancel": "Abbrechen", + "uninstallConfirmation": "Möchten Sie \"{0}\" deinstallieren?", + "ok": "OK", + "singleDependentError": "Die Erweiterung \"{0}\" kann nicht deinstalliert werden. Die Erweiterung \"{1}\" hängt von dieser Erweiterung ab.", + "twoDependentsError": "Die Erweiterung \"{0}\" kann nicht deinstalliert werden. Die Erweiterungen \"{1}\" und \"{2}\" hängen von dieser Erweiterung ab.", + "multipleDependentsError": "Die Erweiterung \"{0}\" kann nicht deinstalliert werden. Die Erweiterungen \"{1}\" und \"{2}\" sowie weitere hängen von dieser Erweiterung ab.", + "notExists": "Die Erweiterung wurde nicht gefunden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/deu/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..42223871cb --- /dev/null +++ b/i18n/deu/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "Fehler beim Aktivieren der Extension \"{1}\". Ursache: unbekannte Abhängigkeit \"{0}\".", + "failedDep1": "Fehler beim Aktivieren der Extension \"{1}\". Ursache: Fehler beim Aktivieren der Extension \"{0}\".", + "failedDep2": "Fehler beim Aktivieren der Extension \"{0}\". Ursache: mehr als 10 Ebenen von Abhängigkeiten (wahrscheinlich eine Abhängigkeitsschleife).", + "activationError": "Fehler beim Aktivieren der Extension \"{0}\": {1}." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/deu/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..9458f4b0c7 --- /dev/null +++ b/i18n/deu/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "Gibt für VS Code-Erweiterungen die VS Code-Version an, mit der die Erweiterung kompatibel ist. Darf nicht \"*\" sein. Beispiel: ^0.10.5 gibt die Kompatibilität mit mindestens VS Code-Version 0.10.5 an.", + "vscode.extension.publisher": "Der Herausgeber der VS Code-Extension.", + "vscode.extension.displayName": "Der Anzeigename für die Extension, der im VS Code-Katalog verwendet wird.", + "vscode.extension.categories": "Die vom VS Code-Katalog zum Kategorisieren der Extension verwendeten Kategorien.", + "vscode.extension.galleryBanner": "Das in VS Code Marketplace verwendete Banner.", + "vscode.extension.galleryBanner.color": "Die Bannerfarbe für die Kopfzeile der VS Code Marketplace-Seite.", + "vscode.extension.galleryBanner.theme": "Das Farbdesign für die Schriftart, die im Banner verwendet wird.", + "vscode.extension.contributes": "Alle Beiträge der VS Code-Extension, die durch dieses Paket dargestellt werden.", + "vscode.extension.preview": "Legt die Erweiterung fest, die im Marketplace als Vorschau gekennzeichnet werden soll.", + "vscode.extension.activationEvents": "Aktivierungsereignisse für die VS Code-Extension.", + "vscode.extension.activationEvents.onLanguage": "Ein Aktivierungsereignis wird beim Öffnen einer Datei ausgegeben, die in die angegebene Sprache aufgelöst wird.", + "vscode.extension.activationEvents.onCommand": "Ein Aktivierungsereignis wird beim Aufrufen des angegebenen Befehls ausgegeben.", + "vscode.extension.activationEvents.onDebug": "Ein Aktivierungsereignis wird beim Starten einer Debugsitzung des angegebenen Typs ausgegeben.", + "vscode.extension.activationEvents.workspaceContains": "Ein Aktivierungsereignis wird beim Öffnen eines Ordners ausgegeben, der mindestens eine Datei enthält, die mit dem angegebenen Globmuster übereinstimmt.", + "vscode.extension.activationEvents.onView": "Ein Aktivierungsereignis wird beim Erweitern der angegebenen Ansicht ausgegeben.", + "vscode.extension.activationEvents.star": "Ein Aktivierungsereignis wird beim Start von VS Code ausgegeben. Damit für die Endbenutzer eine bestmögliche Benutzerfreundlichkeit sichergestellt ist, verwenden Sie dieses Aktivierungsereignis in Ihrer Erweiterung nur dann, wenn in Ihrem Anwendungsfall keine andere Kombination an Aktivierungsereignissen funktioniert.", + "vscode.extension.badges": "Array aus Badges, die im Marketplace in der Seitenleiste auf der Seite mit den Erweiterungen angezeigt werden.", + "vscode.extension.badges.url": "Die Bild-URL für den Badge.", + "vscode.extension.badges.href": "Der Link für den Badge.", + "vscode.extension.badges.description": "Eine Beschreibung für den Badge.", + "vscode.extension.extensionDependencies": "Abhängigkeiten von anderen Erweiterungen. Der Bezeichner einer Erweiterung ist immer ${publisher}.${name}, beispielsweise \"vscode.csharp\".", + "vscode.extension.scripts.prepublish": "Ein Skript, das ausgeführt wird, bevor das Paket als VS Code-Extension veröffentlicht wird.", + "vscode.extension.icon": "Der Pfad zu einem 128x128-Pixel-Symbol." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/deu/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..673c089435 --- /dev/null +++ b/i18n/deu/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "Der engines.vscode-Wert {0} konnte nicht analysiert werden. Verwenden Sie z. B. ^0.10.0, ^1.2.3, ^0.11.0, ^0.10.x usw.", + "versionSpecificity1": "Die in \"engines.vscode\" ({0}) angegebene Version ist nicht spezifisch genug. Definieren Sie für VS Code-Versionen vor Version 1.0.0 bitte mindestens die gewünschte Haupt- und Nebenversion, z. B. ^0.10.0, 0.10.x, 0.11.0 usw.", + "versionSpecificity2": "Die in \"engines.vscode\" ({0}) angegebene Version ist nicht spezifisch genug. Definieren Sie für VS Code-Versionen nach Version 1.0.0 bitte mindestens die gewünschte Hauptversion, z. B. ^1.10.0, 1.10.x, 1.x.x, 2.x.x usw.", + "versionMismatch": "Die Extension ist nicht mit dem Code {0} kompatibel. Die Extension erfordert {1}.", + "extensionDescription.empty": "Es wurde eine leere Extensionbeschreibung abgerufen.", + "extensionDescription.publisher": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "extensionDescription.name": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "extensionDescription.version": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "extensionDescription.engines": "Die Eigenschaft \"{0}\" ist erforderlich und muss vom Typ \"object\" sein.", + "extensionDescription.engines.vscode": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "extensionDescription.extensionDependencies": "Die Eigenschaft \"{0}\" kann ausgelassen werden oder muss vom Typ \"string[]\" sein.", + "extensionDescription.activationEvents1": "Die Eigenschaft \"{0}\" kann ausgelassen werden oder muss vom Typ \"string[]\" sein.", + "extensionDescription.activationEvents2": "Die Eigenschaften \"{0}\" und \"{1}\" müssen beide angegeben oder beide ausgelassen werden.", + "extensionDescription.main1": "Die Eigenschaft \"{0}\" kann ausgelassen werden oder muss vom Typ \"string\" sein.", + "extensionDescription.main2": "Es wurde erwartet, dass \"main\" ({0}) im Ordner ({1}) der Extension enthalten ist. Dies führt ggf. dazu, dass die Extension nicht portierbar ist.", + "extensionDescription.main3": "Die Eigenschaften \"{0}\" und \"{1}\" müssen beide angegeben oder beide ausgelassen werden.", + "notSemver": "Die Extensionversion ist nicht mit \"semver\" kompatibel." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/deu/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..8db4d4ad0b --- /dev/null +++ b/i18n/deu/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "Neues Fenster", + "newWindowDesc": "Öffnet ein neues Fenster.", + "recentFolders": "Aktueller Arbeitsbereich", + "folderDesc": "{0} {1}", + "codeWorkspace": "Codearbeitsbereich" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/deu/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..e4f85f829b --- /dev/null +++ b/i18n/deu/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "OK", + "integrity.dontShowAgain": "Nicht mehr anzeigen", + "integrity.moreInfo": "Weitere Informationen", + "integrity.prompt": "Ihre {0}-Installation ist offenbar beschädigt. Führen Sie eine Neuinstallation durch." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/deu/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..f2a3ea3621 --- /dev/null +++ b/i18n/deu/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "Trägt zur JSON-Schemakonfiguration bei.", + "contributes.jsonValidation.fileMatch": "Das Dateimuster, mit dem eine Übereinstimmung vorliegen soll, z. B. \"package.json\" oder \"*.launch\".", + "contributes.jsonValidation.url": "Eine Schema-URL (\"http:\", \"Https:\") oder der relative Pfad zum Extensionordner (\". /\").", + "invalid.jsonValidation": "configuration.jsonValidation muss ein Array sein.", + "invalid.fileMatch": "configuration.jsonValidation.fileMatch muss definiert sein.", + "invalid.url": "configuration.jsonValidation.url muss eine URL oder ein relativer Pfad sein.", + "invalid.url.fileschema": "configuration.jsonValidation.url ist eine ungültige relative URL: {0}", + "invalid.url.schema": "\"configuration.jsonValidation.url\" muss mit \"http:\", \"https:\" oder \"./\" starten, um auf Schemas zu verweisen, die in der Extension gespeichert sind." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/deu/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..5452ac16f0 --- /dev/null +++ b/i18n/deu/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "({0}) wurde gedrückt. Es wird auf die zweite Taste der Kombination gewartet...", + "missing.chord": "Die Tastenkombination ({0}, {1}) ist kein Befehl." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/keybinding/common/keybindingLabels.i18n.json b/i18n/deu/src/vs/platform/keybinding/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..a6c3f59142 --- /dev/null +++ b/i18n/deu/src/vs/platform/keybinding/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "STRG", + "shiftKey": "UMSCHALTTASTE", + "altKey": "ALT", + "windowsKey": "Windows", + "ctrlKey.long": "STRG", + "shiftKey.long": "UMSCHALTTASTE", + "altKey.long": "ALT", + "cmdKey.long": "Befehlstaste", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/deu/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..9c600e5e9a --- /dev/null +++ b/i18n/deu/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "Die loop-Eigenschaft wird nur für Matcher für die letzte Zeile unterstützt.", + "ProblemPatternParser.problemPattern.missingRegExp": "Im Problemmuster fehlt ein regulärer Ausdruck.", + "ProblemPatternParser.problemPattern.missingProperty": "Das Problemmuster ist ungültig. Es muss mindestens eine Datei, Nachricht und Zeile oder eine Speicherort-Übereinstimmungsgruppe aufweisen.", + "ProblemPatternParser.invalidRegexp": "Fehler: Die Zeichenfolge {0} ist kein gültiger regulärer Ausdruck.\n", + "ProblemPatternSchema.regexp": "Der reguläre Ausdruck zum Ermitteln eines Fehlers, einer Warnung oder von Informationen in der Ausgabe.", + "ProblemPatternSchema.file": "Der Übereinstimmungsgruppenindex des Dateinamens. Wenn keine Angabe erfolgt, wird 1 verwendet.", + "ProblemPatternSchema.location": "Der Übereinstimmungsgruppenindex der Position des Problems. Gültige Positionsmuster: (line), (line,column) und (startLine,startColumn,endLine,endColumn). Wenn keine Angabe erfolgt, wird (line,column) angenommen.", + "ProblemPatternSchema.line": "Der Übereinstimmungsgruppenindex der Zeile des Problems. Der Standardwert ist 2.", + "ProblemPatternSchema.column": "Der Übereinstimmungsgruppenindex des Zeilenzeichens des Problems. Der Standardwert ist 3.", + "ProblemPatternSchema.endLine": "Der Übereinstimmungsgruppenindex der Endzeile des Problems. Der Standardwert ist undefiniert.", + "ProblemPatternSchema.endColumn": "Der Übereinstimmungsgruppenindex des Zeilenendezeichens des Problems. Der Standardwert ist undefiniert.", + "ProblemPatternSchema.severity": "Der Übereinstimmungsgruppenindex des Schweregrads des Problems. Der Standardwert ist undefiniert.", + "ProblemPatternSchema.code": "Der Übereinstimmungsgruppenindex des Codes des Problems. Der Standardwert ist undefiniert.", + "ProblemPatternSchema.message": "Der Übereinstimmungsgruppenindex der Nachricht. Wenn keine Angabe erfolgt, ist der Standardwert 4, wenn die Position angegeben wird. Andernfalls ist der Standardwert 5.", + "ProblemPatternSchema.loop": "Gibt in einer mehrzeiligen Abgleichschleife an, ob dieses Muster in einer Schleife ausgeführt wird, wenn es übereinstimmt. Kann nur für ein letztes Muster in einem mehrzeiligen Muster angegeben werden.", + "NamedProblemPatternSchema.name": "Der Name des Problemmusters.", + "NamedMultiLineProblemPatternSchema.name": "Der Name des mehrzeiligen Problemmusters.", + "NamedMultiLineProblemPatternSchema.patterns": "Die aktuellen Muster.", + "ProblemPatternExtPoint": "Trägt Problemmuster bei", + "ProblemPatternRegistry.error": "Ungültiges Problemmuster. Das Muster wird ignoriert.", + "ProblemMatcherParser.noProblemMatcher": "Fehler: Die Beschreibung kann nicht in einen Problemabgleich konvertiert werden:\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "Fehler: Die Beschreibung definiert kein gültiges Problemmuster:\n{0}\n", + "ProblemMatcherParser.noOwner": "Fehler: Die Beschreibung definiert keinen Besitzer:\n{0}\n", + "ProblemMatcherParser.noFileLocation": "Fehler: Die Beschreibung definiert keinen Dateispeicherort:\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "Info: unbekannter Schweregrad {0}. Gültige Werte sind Fehler, Warnung und Info.\n", + "ProblemMatcherParser.noDefinedPatter": "Fehler: Das Muster mit dem Bezeichner {0} ist nicht vorhanden.", + "ProblemMatcherParser.noIdentifier": "Fehler: Die Mustereigenschaft verweist auf einen leeren Bezeichner.", + "ProblemMatcherParser.noValidIdentifier": "Fehler: Die Mustereigenschaft {0} ist kein gültiger Name für eine Mustervariable.", + "ProblemMatcherParser.problemPattern.watchingMatcher": "Ein Problemmatcher muss ein Anfangsmuster und ein Endmuster für die Überwachung definieren.", + "ProblemMatcherParser.invalidRegexp": "Fehler: Die Zeichenfolge {0} ist kein gültiger regulärer Ausdruck.\n", + "WatchingPatternSchema.regexp": "Der reguläre Ausdruck zum Erkennen des Anfangs oder Endes eines Hintergrundtasks.", + "WatchingPatternSchema.file": "Der Übereinstimmungsgruppenindex des Dateinamens. Kann ausgelassen werden.", + "PatternTypeSchema.name": "Der Name eines beigetragenen oder vordefinierten Musters", + "PatternTypeSchema.description": "Ein Problemmuster oder der Name eines beigetragenen oder vordefinierten Problemmusters. Kann ausgelassen werden, wenn die Basis angegeben ist.", + "ProblemMatcherSchema.base": "Der Name eines zu verwendenden Basisproblemabgleichers.", + "ProblemMatcherSchema.owner": "Der Besitzer des Problems im Code. Kann ausgelassen werden, wenn \"base\" angegeben wird. Der Standardwert ist \"external\", wenn keine Angabe erfolgt und \"base\" nicht angegeben wird.", + "ProblemMatcherSchema.severity": "Der Standardschweregrad für Erfassungsprobleme. Dieser wird verwendet, wenn das Muster keine Übereinstimmungsgruppe für den Schweregrad definiert.", + "ProblemMatcherSchema.applyTo": "Steuert, ob ein für ein Textdokument gemeldetes Problem nur auf geöffnete, geschlossene oder alle Dokumente angewendet wird.", + "ProblemMatcherSchema.fileLocation": "Definiert, wie Dateinamen interpretiert werden sollen, die in einem Problemmuster gemeldet werden.", + "ProblemMatcherSchema.background": "Muster zum Nachverfolgen des Beginns und Endes eines Abgleichers, der für eine Hintergrundaufgabe aktiv ist.", + "ProblemMatcherSchema.background.activeOnStart": "Wenn dieser Wert auf \"true\" festgelegt wird, befindet sich die Hintergrundüberwachung im aktiven Modus, wenn die Aufgabe gestartet wird. Dies entspricht dem Ausgeben einer Zeile, die mit dem \"beginPattern\" übereinstimmt.", + "ProblemMatcherSchema.background.beginsPattern": "Wenn eine Übereinstimmung mit der Ausgabe vorliegt, wird der Start einer Hintergrundaufgabe signalisiert.", + "ProblemMatcherSchema.background.endsPattern": "Wenn eine Übereinstimmung mit der Ausgabe vorliegt, wird das Ende einer Hintergrundaufgabe signalisiert.", + "ProblemMatcherSchema.watching.deprecated": "Die Überwachungseigenschaft ist veraltet. Verwenden Sie stattdessen den Hintergrund.", + "ProblemMatcherSchema.watching": "Muster zum Nachverfolgen des Beginns und Endes eines Problemabgleicher.", + "ProblemMatcherSchema.watching.activeOnStart": "Wenn dieser Wert auf \"true\" festgelegt wird, befindet sich die Überwachung im aktiven Modus, wenn der Task gestartet wird. Dies entspricht dem Ausgeben einer Zeile, die mit dem \"beginPattern\" übereinstimmt.", + "ProblemMatcherSchema.watching.beginsPattern": "Wenn eine Übereinstimmung mit der Ausgabe vorliegt, wird der Start eines Überwachungstasks signalisiert.", + "ProblemMatcherSchema.watching.endsPattern": "Wenn eine Übereinstimmung mit der Ausgabe vorliegt, wird das Ende eines Überwachungstasks signalisiert.", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "Diese Eigenschaft ist veraltet. Verwenden Sie stattdessen die Überwachungseigenschaft.", + "LegacyProblemMatcherSchema.watchedBegin": "Ein regulärer Ausdruck, der signalisiert, dass die Ausführung eines überwachten Tasks (ausgelöst durch die Dateiüberwachung) beginnt.", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "Diese Eigenschaft ist veraltet. Verwenden Sie stattdessen die Überwachungseigenschaft.", + "LegacyProblemMatcherSchema.watchedEnd": "Ein regulärer Ausdruck, der signalisiert, dass die Ausführung eines überwachten Tasks beendet wird.", + "NamedProblemMatcherSchema.name": "Der Name des Problemabgleichers, anhand dessen auf ihn verwiesen wird.", + "NamedProblemMatcherSchema.label": "Eine lesbare Bezeichnung für den Problemabgleicher.", + "ProblemMatcherExtPoint": "Trägt Problemabgleicher bei", + "msCompile": "Microsoft-Compilerprobleme", + "lessCompile": "LESS Probleme", + "gulp-tsc": "Gulp-TSC-Probleme", + "jshint": "JSHint-Probleme", + "jshint-stylish": "JSHint-Stilprobleme", + "eslint-compact": "ESLint-Komprimierungsprobleme", + "eslint-stylish": "ESLint-Stilprobleme", + "go": "Go Probleme" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/message/common/message.i18n.json b/i18n/deu/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..bbcb92590e --- /dev/null +++ b/i18n/deu/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Schließen", + "later": "Später", + "cancel": "Abbrechen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/request/node/request.i18n.json b/i18n/deu/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..807a88e19b --- /dev/null +++ b/i18n/deu/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "Die zu verwendende Proxyeinstellung. Wenn diese Option nicht festgelegt wird, wird der Wert aus den Umgebungsvariablen \"http_proxy\" und \"https_proxy\" übernommen.", + "strictSSL": "Gibt an, ob das Proxyserverzertifikat anhand der Liste der bereitgestellten Zertifizierungsstellen überprüft werden soll.", + "proxyAuthorization": "Der Wert, der als Proxy-Authorization-Header für jede Netzwerkanforderung gesendet werden soll." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/deu/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..9a5361e7e1 --- /dev/null +++ b/i18n/deu/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetrie", + "telemetry.enableTelemetry": "Aktivieren Sie das Senden von Nutzungsdaten und Fehlern an Microsoft." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/deu/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..653a3c0194 --- /dev/null +++ b/i18n/deu/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "Fügt in Erweiterung definierte verwendbare Farben hinzu", + "contributes.color.id": "Der Bezeichner der verwendbaren Farbe", + "contributes.color.id.format": "Bezeichner sollten in folgendem Format vorliegen: aa [.bb] *", + "contributes.color.description": "Die Beschreibung der verwendbaren Farbe", + "contributes.defaults.light": "Die Standardfarbe für helle Themen. Entweder eine Farbe als Hex-Code (#RRGGBB[AA]) oder der Bezeichner einer verwendbaren Farbe, der eine Standardeinstellung bereitstellt.", + "contributes.defaults.dark": "Die Standardfarbe für dunkle Themen. Entweder eine Farbe als Hex-Code (#RRGGBB[AA]) oder der Bezeichner einer verwendbaren Farbe, der eine Standardeinstellung bereitstellt.", + "contributes.defaults.highContrast": "Die Standardfarbe für Themen mit hohem Kontrast. Entweder eine Farbe als Hex-Code (#RRGGBB[AA]) oder der Bezeichner einer verwendbaren Farbe, der eine Standardeinstellung bereitstellt.", + "invalid.colorConfiguration": "\"configuration.colors\" muss ein Array sein.", + "invalid.default.colorType": "{0} muss entweder eine Farbe als Hex-Code (#RRGGBB[AA] oder #RGB[A]) sein oder der Bezeichner einer verwendbaren Farbe, der eine Standardeinstellung bereitstellt.", + "invalid.id": "\"configuration.colors.id\" muss definiert und nicht leer sein", + "invalid.id.format": "\"configuration.colors.id\" muss auf das Wort[.word]* folgen", + "invalid.description": "\"configuration.colors.description\" muss definiert und nicht leer sein", + "invalid.defaults": "\"configuration.colors.defaults\" muss definiert sein, und \"light\", \"dark\" und \"highContrast\" enthalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/deu/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..25c2b4e80d --- /dev/null +++ b/i18n/deu/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "Ungültiges Farbformat. Verwenden Sie #RGB, #RGBA, #RRGGBB oder #RRGGBBAA.", + "schema.colors": "In der Workbench verwendete Farben.", + "foreground": "Allgemeine Vordergrundfarbe. Diese Farbe wird nur verwendet, wenn sie nicht durch eine Komponente überschrieben wird.", + "errorForeground": "Allgemeine Vordergrundfarbe. Diese Farbe wird nur verwendet, wenn sie nicht durch eine Komponente überschrieben wird.", + "descriptionForeground": "Vordergrundfarbe für Beschreibungstexte, die weitere Informationen anzeigen, z. B. für ein Label.", + "focusBorder": "Allgemeine Rahmenfarbe für fokussierte Elemente. Diese Farbe wird nur verwendet, wenn sie nicht durch eine Komponente überschrieben wird.", + "contrastBorder": "Ein zusätzlicher Rahmen um Elemente, mit dem diese von anderen getrennt werden, um einen größeren Kontrast zu erreichen.", + "activeContrastBorder": "Ein zusätzlicher Rahmen um aktive Elemente, mit dem diese von anderen getrennt werden, um einen größeren Kontrast zu erreichen.", + "selectionBackground": "Hintergrundfarbe der Textauswahl in der Workbench (z. B. für Eingabefelder oder Textbereiche). Diese Farbe gilt nicht für die Auswahl im Editor. ", + "textSeparatorForeground": "Farbe für Text-Trennzeichen.", + "textLinkForeground": "Vordergrundfarbe für Links im Text.", + "textLinkActiveForeground": "Vordergrundfarbe für aktive Links im Text.", + "textPreformatForeground": "Vordergrundfarbe für vorformatierte Textsegmente.", + "textBlockQuoteBackground": "Hintergrundfarbe für block quotes im Text.", + "textBlockQuoteBorder": "Rahmenfarbe für block quotes im Text.", + "textCodeBlockBackground": "Hintergrundfarbe für Code-Blöcke im Text.", + "widgetShadow": "Schattenfarbe von Widgets wie zum Beispiel Suchen/Ersetzen innerhalb des Editors.", + "inputBoxBackground": "Hintergrund für Eingabefeld.", + "inputBoxForeground": "Vordergrund für Eingabefeld.", + "inputBoxBorder": "Rahmen für Eingabefeld.", + "inputBoxActiveOptionBorder": "Rahmenfarbe für aktivierte Optionen in Eingabefeldern.", + "inputPlaceholderForeground": "Input box - Vordergrundfarbe für Platzhalter-Text.", + "inputValidationInfoBackground": "Hintergrundfarbe bei der Eingabevalidierung für den Schweregrad der Information.", + "inputValidationInfoBorder": "Rahmenfarbe bei der Eingabevalidierung für den Schweregrad der Information.", + "inputValidationWarningBackground": "Hintergrundfarbe bei der Eingabevalidierung für eine Warnung zur Information.", + "inputValidationWarningBorder": "Rahmenfarbe bei der Eingabevalidierung für den Schweregrad der Warnung.", + "inputValidationErrorBackground": "Hintergrundfarbe bei der Eingabevalidierung für den Schweregrad des Fehlers.", + "inputValidationErrorBorder": "Rahmenfarbe bei der Eingabevalidierung für den Schweregrad des Fehlers.", + "dropdownBackground": "Hintergrund für Dropdown.", + "dropdownForeground": "Vordergrund für Dropdown.", + "dropdownBorder": "Rahmen für Dropdown.", + "listFocusBackground": "Hintergrundfarbe der Liste/Struktur für das fokussierte Element, wenn die Liste/Struktur aktiv ist. Eine aktive Liste/Struktur hat Tastaturfokus, eine inaktive hingegen nicht.", + "listFocusForeground": "Vordergrundfarbe der Liste/Struktur für das fokussierte Element, wenn die Liste/Struktur aktiv ist. Eine aktive Liste/Struktur hat Tastaturfokus, eine inaktive hingegen nicht.", + "listActiveSelectionBackground": "Hintergrundfarbe der Liste/Struktur für das ausgewählte Element, wenn die Liste/Struktur aktiv ist. Eine aktive Liste/Struktur hat Tastaturfokus, eine inaktive hingegen nicht.", + "listActiveSelectionForeground": "Vordergrundfarbe der Liste/Struktur für das ausgewählte Element, wenn die Liste/Struktur aktiv ist. Eine aktive Liste/Struktur hat Tastaturfokus, eine inaktive hingegen nicht.", + "listInactiveSelectionBackground": "Hintergrundfarbe der Liste/Struktur für das ausgewählte Element, wenn die Liste/Struktur inaktiv ist. Eine aktive Liste/Struktur hat Tastaturfokus, eine inaktive hingegen nicht.", + "listInactiveSelectionForeground": "Liste/Baumstruktur - Vordergrundfarbe für das ausgewählte Element, wenn die Liste/Baumstruktur inaktiv ist. Eine aktive Liste/Baumstruktur hat Tastaturfokus, eine inaktive hingegen nicht.", + "listHoverBackground": "Hintergrund der Liste/Struktur, wenn mit der Maus auf Elemente gezeigt wird.", + "listHoverForeground": "Vordergrund der Liste/Struktur, wenn mit der Maus auf Elemente gezeigt wird.", + "listDropBackground": "Drag & Drop-Hintergrund der Liste/Struktur, wenn Elemente mithilfe der Maus verschoben werden.", + "highlight": "Vordergrundfarbe der Liste/Struktur zur Trefferhervorhebung beim Suchen innerhalb der Liste/Struktur.", + "pickerGroupForeground": "Schnellauswahlfarbe für das Gruppieren von Bezeichnungen.", + "pickerGroupBorder": "Schnellauswahlfarbe für das Gruppieren von Rahmen.", + "buttonForeground": "Vordergrundfarbe der Schaltfläche.", + "buttonBackground": "Hintergrundfarbe der Schaltfläche.", + "buttonHoverBackground": "Hintergrundfarbe der Schaltfläche, wenn darauf gezeigt wird.", + "badgeBackground": "Badge - Hintergrundfarbe. Badges sind kurze Info-Texte, z. B. für Anzahl Suchergebnisse.", + "badgeForeground": "Badge - Vordergrundfarbe. Badges sind kurze Info-Texte, z. B. für Anzahl Suchergebnisse.", + "scrollbarShadow": "Schatten der Scrollleiste, um anzuzeigen, dass die Ansicht gescrollt wird.", + "scrollbarSliderBackground": "Hintergrundfarbe vom Scrollbar-Schieber", + "scrollbarSliderHoverBackground": "Hintergrundfarbe des Schiebereglers, wenn darauf gezeigt wird.", + "scrollbarSliderActiveBackground": "Hintergrundfarbe des Schiebereglers, wenn dieser aktiv ist.", + "progressBarBackground": "Hintergrundfarbe des Fortschrittbalkens, der für lang ausgeführte Vorgänge angezeigt werden kann.", + "editorBackground": "Hintergrundfarbe des Editors.", + "editorForeground": "Standardvordergrundfarbe des Editors.", + "editorWidgetBackground": "Hintergrundfarbe von Editor-Widgets wie zum Beispiel Suchen/Ersetzen.", + "editorWidgetBorder": "Rahmenfarbe von Editorwigdets. Die Farbe wird nur verwendet, wenn für das Widget ein Rahmen verwendet wird und die Farbe nicht von einem Widget überschrieben wird.", + "editorSelectionBackground": "Farbe der Editor-Auswahl.", + "editorSelectionForeground": "Farbe des gewählten Text für einen hohen Kontrast", + "editorInactiveSelection": "Farbe der Auswahl in einem inaktiven Editor.", + "editorSelectionHighlight": "Farbe für Bereiche, deren Inhalt der Auswahl entspricht.", + "editorFindMatch": "Farbe des aktuellen Suchergebnisses.", + "findMatchHighlight": "Farbe der anderen Suchtreffer.", + "findRangeHighlight": "Farbe des Bereichs zur Einschränkung der Suche.", + "hoverHighlight": "Hervorhebung eines Worts, unter dem ein Mauszeiger angezeigt wird.", + "hoverBackground": "Background color of the editor hover.", + "hoverBorder": "Rahmenfarbe des Editor-Mauszeigers.", + "activeLinkForeground": "Farbe der aktiven Links.", + "diffEditorInserted": "Hintergrundfarbe für eingefügten Text.", + "diffEditorRemoved": "Hintergrundfarbe für entfernten Text.", + "diffEditorInsertedOutline": "Konturfarbe für eingefügten Text.", + "diffEditorRemovedOutline": "Konturfarbe für entfernten Text.", + "mergeCurrentHeaderBackground": "Aktueller Kopfzeilenhintergrund in Inline-Mergingkonflikten.", + "mergeCurrentContentBackground": "Aktueller Inhaltshintergrund in Inline-Mergingkonflikten.", + "mergeIncomingHeaderBackground": "Eingehender Kopfzeilenhintergrund in Inline-Mergingkonflikten. ", + "mergeIncomingContentBackground": "Eingehender Inhaltshintergrund in Inline-Mergingkonflikten.", + "mergeCommonHeaderBackground": "Kopfzeilenhintergrund des gemeinsamen übergeordneten Elements bei Inlinezusammenführungskonflikten. ", + "mergeCommonContentBackground": "Inhaltshintergrund des gemeinsamen übergeordneten Elements bei Inlinezusammenführungskonflikten.", + "mergeBorder": "Rahmenfarbe für Kopfzeilen und die Aufteilung in Inline-Mergingkonflikten.", + "overviewRulerCurrentContentForeground": "Aktueller Übersichtslineal-Vordergrund für Inline-Mergingkonflikte.", + "overviewRulerIncomingContentForeground": "Eingehender Übersichtslineal-Vordergrund für Inline-Mergingkonflikte. ", + "overviewRulerCommonContentForeground": "Hintergrund des Übersichtslineals des gemeinsamen übergeordneten Elements bei Inlinezusammenführungskonflikten.", + "overviewRulerFindMatchForeground": "Übersichtslineal-Markierungsfarbe für Suchübereinstimmungen.", + "overviewRulerSelectionHighlightForeground": "Übersichtslineal-Markierungsfarbe für Auswahlhervorhebungen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/deu/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..b102169a5b --- /dev/null +++ b/i18n/deu/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "Codearbeitsbereich", + "untitledWorkspace": "Ohne Titel (Arbeitsbereich)", + "workspaceNameVerbose": "{0} (Arbeitsbereich)", + "workspaceName": "{0} (Arbeitsbereich)" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/deu/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..c2b0be8b76 --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Die Erweiterung \"{0}\" wird mit \"{1}\" überschrieben.", + "extensionUnderDevelopment": "Die Entwicklungserweiterung unter \"{0}\" wird geladen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/deu/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..33d287989e --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Schließen", + "cancel": "Abbrechen", + "ok": "OK" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/deu/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..88248fbb02 --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "{0} weitere Fehler und Warnungen werden nicht angezeigt." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/node/extHostExplorerView.i18n.json b/i18n/deu/src/vs/workbench/api/node/extHostExplorerView.i18n.json new file mode 100644 index 0000000000..3915fafa61 --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/node/extHostExplorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "Es ist kein TreeExplorerNodeProvider mit ID \"{0}\" registriert.", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider \"{0}\" hat keinen Stammknoten bereitgestellt." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/deu/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..42223871cb --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "Fehler beim Aktivieren der Extension \"{1}\". Ursache: unbekannte Abhängigkeit \"{0}\".", + "failedDep1": "Fehler beim Aktivieren der Extension \"{1}\". Ursache: Fehler beim Aktivieren der Extension \"{0}\".", + "failedDep2": "Fehler beim Aktivieren der Extension \"{0}\". Ursache: mehr als 10 Ebenen von Abhängigkeiten (wahrscheinlich eine Abhängigkeitsschleife).", + "activationError": "Fehler beim Aktivieren der Extension \"{0}\": {1}." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/deu/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json b/i18n/deu/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json new file mode 100644 index 0000000000..1d2c252768 --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "Es ist kein TreeExplorerNodeProvider mit ID \"{0}\" registriert.", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider \"{0}\" hat keinen Stammknoten bereitgestellt.", + "treeExplorer.failedToResolveChildren": "TreeExplorerNodeProvider \"{0}\" hat \"resolveChildren\" nicht ausgeführt." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/node/extHostTreeView.i18n.json b/i18n/deu/src/vs/workbench/api/node/extHostTreeView.i18n.json new file mode 100644 index 0000000000..3915fafa61 --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/node/extHostTreeView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "Es ist kein TreeExplorerNodeProvider mit ID \"{0}\" registriert.", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider \"{0}\" hat keinen Stammknoten bereitgestellt." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/deu/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..b2cf2b97a2 --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "Kein Treeviw mit der id '{0}' registriert.", + "treeItem.notFound": "Kein Tree-Eintrag mit der id '{0}' gefunden.", + "treeView.duplicateElement": "Element {0} ist bereit registriert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json b/i18n/deu/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..c2b0be8b76 --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Die Erweiterung \"{0}\" wird mit \"{1}\" überschrieben.", + "extensionUnderDevelopment": "Die Entwicklungserweiterung unter \"{0}\" wird geladen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/api/node/mainThreadMessageService.i18n.json b/i18n/deu/src/vs/workbench/api/node/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..33d287989e --- /dev/null +++ b/i18n/deu/src/vs/workbench/api/node/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Schließen", + "cancel": "Abbrechen", + "ok": "OK" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..4933ee8042 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "Sprache konfigurieren", + "displayLanguage": "Definiert die Anzeigesprache von VSCode.", + "doc": "Unter {0} finden Sie eine Liste der unterstützten Sprachen.", + "restart": "Das Ändern dieses Wertes erfordert einen Neustart von VSCode.", + "fail.createSettings": "{0} ({1}) kann nicht erstellt werden.", + "JsonSchema.locale": "Die zu verwendende Sprache der Benutzeroberfläche." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..9ea85596c3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Ordner öffnen...", + "openFileFolder": "Öffnen..." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..37a1c9fd17 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "Sichtbarkeit der Aktivitätsleiste umschalten", + "view": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..4b99a899f7 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "Horizontales/Vertikales Layout für Editor-Gruppe umschalten", + "horizontalLayout": "Horizontales Layout für Editor-Gruppe", + "verticalLayout": "Vertikales Layout für Editor-Gruppe", + "view": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..f2ea2b89c9 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Position der Seitenleiste wechseln", + "view": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..f9781347c6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "Randleistensichtbarkeit umschalten", + "view": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..ab5eecc133 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "Sichtbarkeit der Statusleiste umschalten", + "view": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..3ab2000932 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "Zen-Modus umschalten", + "view": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..a1df1e6bc0 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Ordner öffnen...", + "openFileFolder": "Öffnen...", + "addFolderToWorkspace": "Ordner zum Arbeitsbereich hinzufügen...", + "add": "&&Hinzufügen", + "addFolderToWorkspaceTitle": "Ordner zum Arbeitsbereich hinzufügen", + "newWorkspace": "Neuer Arbeitsbereich...", + "select": "Au&&swählen", + "selectWorkspace": "Ordner für den Arbeitsbereich auswählen", + "removeFolderFromWorkspace": "Ordner aus dem Arbeitsbereich entfernen", + "saveWorkspaceAsAction": "Arbeitsbereich speichern unter...", + "saveEmptyWorkspaceNotSupported": "Öffnen Sie zum Speichern zunächst einen Arbeitsbereich.", + "save": "&&Speichern", + "saveWorkspace": "Arbeitsbereich speichern", + "openWorkspaceAction": "Arbeitsbereich öffnen...", + "openWorkspaceConfigFile": "Konfigurationsdatei des Arbeitsbereichs öffnen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..efe75f958c --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "Aus Aktivitätsleiste entfernen", + "keepInActivityBar": "In Aktivitätsleiste behalten", + "titleKeybinding": "{0} ({1})", + "additionalViews": "Zusätzliche Ansichten", + "numberBadge": "{0} ({1})", + "manageExtension": "Erweiterung verwalten", + "toggle": "Ansichtsfixierung umschalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..393ce70a69 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "Aktivitätsleiste ausblenden", + "activityBarAriaLabel": "Umschaltung der aktiven Ansicht", + "globalActions": "Globale Aktionen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..367f132212 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "{0}-Aktionen", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..c72db3415d --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "Binärdateien-Viewer" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..badc901153 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Text-Editor", + "textDiffEditor": "Textdiff-Editor", + "binaryDiffEditor": "Binärdiff-Editor", + "sideBySideEditor": "Editor mit Ansicht \"Nebeneinander\"", + "groupOnePicker": "Editoren in erster Gruppe anzeigen", + "groupTwoPicker": "Editoren in zweiter Gruppe anzeigen", + "groupThreePicker": "Editoren in dritter Gruppe anzeigen", + "allEditorsPicker": "Alle geöffneten Editoren anzeigen", + "view": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..f6d4522487 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "Editor teilen", + "joinTwoGroups": "Editors von zwei Gruppen verknüpfen", + "navigateEditorGroups": "Zwischen Editor-Gruppen navigieren", + "focusActiveEditorGroup": "Fokus in aktiver Editor-Gruppe", + "focusFirstEditorGroup": "Fokus in erster Editor-Gruppe", + "focusSecondEditorGroup": "Fokus in zweiter Editor-Gruppe", + "focusThirdEditorGroup": "Fokus in dritter Editor-Gruppe", + "focusPreviousGroup": "Fokus in vorheriger Gruppe", + "focusNextGroup": "Fokus in nächster Gruppe", + "openToSide": "Zur Seite öffnen", + "closeEditor": "Editor schließen", + "revertAndCloseActiveEditor": "Wiederherstellen und Editor schließen", + "closeEditorsToTheLeft": "Editoren links schließen", + "closeEditorsToTheRight": "Editoren rechts schließen", + "closeAllEditors": "Alle Editoren schließen", + "closeUnmodifiedEditors": "Nicht geänderte Editoren in der Gruppe schließen", + "closeEditorsInOtherGroups": "Editoren in anderen Gruppen schließen", + "closeOtherEditorsInGroup": "Andere Editoren schließen", + "closeEditorsInGroup": "Alle Editoren in der Gruppe schließen", + "moveActiveGroupLeft": "Editor-Gruppe nach links verschieben", + "moveActiveGroupRight": "Editor-Gruppe nach rechts verschieben", + "minimizeOtherEditorGroups": "Andere Editor-Gruppen minimieren", + "evenEditorGroups": "Gleichmäßige Breite der Editor-Gruppe", + "maximizeEditor": "Editor-Gruppe maximieren und Randleiste ausblenden", + "keepEditor": "Editor beibehalten", + "openNextEditor": "Nächsten Editor öffnen", + "openPreviousEditor": "Vorherigen Editor öffnen", + "nextEditorInGroup": "Nächsten Editor in der Gruppe öffnen", + "openPreviousEditorInGroup": "Vorherigen Editor in der Gruppe öffnen", + "navigateNext": "Weiter", + "navigatePrevious": "Zurück", + "reopenClosedEditor": "Geschlossenen Editor erneut öffnen", + "clearRecentFiles": "Zuletzt geöffnete löschen", + "showEditorsInFirstGroup": "Editoren in erster Gruppe anzeigen", + "showEditorsInSecondGroup": "Editoren in zweiter Gruppe anzeigen", + "showEditorsInThirdGroup": "Editoren in dritter Gruppe anzeigen", + "showEditorsInGroup": "Editoren in der Gruppe anzeigen", + "showAllEditors": "Alle Editoren anzeigen", + "openPreviousRecentlyUsedEditorInGroup": "Vorherigen zuletzt verwendeten Editor in der Gruppe öffnen", + "openNextRecentlyUsedEditorInGroup": "Nächsten zuletzt verwendeten Editor in der Gruppe öffnen", + "navigateEditorHistoryByInput": "Vorherigen Editor aus Verlauf öffnen", + "openNextRecentlyUsedEditor": "Nächsten zuletzt verwendeten Editor öffnen", + "openPreviousRecentlyUsedEditor": "Vorherigen zuletzt verwendeten Editor öffnen", + "clearEditorHistory": "Editor-Verlauf löschen", + "focusLastEditorInStack": "Letzten Editor in der Gruppe öffnen", + "moveEditorLeft": "Editor nach links verschieben", + "moveEditorRight": "Editor nach rechts verschieben", + "moveEditorToPreviousGroup": "Editor in vorherige Gruppe verschieben", + "moveEditorToNextGroup": "Editor in nächste Gruppe verschieben" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..37511aa9c5 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "Aktiven Editor nach Tabstopps oder Gruppen verschieben", + "editorCommand.activeEditorMove.arg.name": "Argument zum Verschieben des aktiven Editors", + "editorCommand.activeEditorMove.arg.description": "Argumenteigenschaften:\n\t\t\t\t\t\t* \"to\": Ein Zeichenfolgenwert, der das Ziel des Verschiebungsvorgangs angibt.\n\t\t\t\t\t\t* \"by\": Ein Zeichenfolgenwert, der die Einheit für die Verschiebung angibt (nach Registerkarte oder nach Gruppe).\n\t\t\t\t\t\t* \"value\": Ein Zahlenwert, der angibt, um wie viele Positionen verschoben wird. Es kann auch die absolute Position für die Verschiebung angegeben werden.\n\t\t\t\t\t", + "commandDeprecated": "Der Befehl **{0}** wurde entfernt. Sie können stattdessen **{1}** verwenden.", + "openKeybindings": "Tastenkombinationen konfigurieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..1bc3bcdc21 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "Links", + "groupTwoVertical": "Zentriert", + "groupThreeVertical": "Rechts", + "groupOneHorizontal": "Oben", + "groupTwoHorizontal": "Zentriert", + "groupThreeHorizontal": "Unten", + "editorOpenError": "{0} kann nicht geöffnet werden: {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..16e6ec0af1 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, Editor-Gruppenauswahl", + "groupLabel": "Gruppe: {0}", + "noResultsFoundInGroup": "Es wurde kein übereinstimmender geöffneter Editor in der Gruppe gefunden.", + "noOpenedEditors": "Die Liste der geöffneten Editoren in der Gruppe ist zurzeit leer.", + "noResultsFound": "Es wurde kein übereinstimmender geöffneter Editor gefunden.", + "noOpenedEditorsAllGroups": "Die Liste der geöffneten Editoren ist zurzeit leer." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..0761f6aed5 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "Zeile {0}, Spalte {1} ({2} ausgewählt)", + "singleSelection": "Zeile {0}, Spalte {1}", + "multiSelectionRange": "{0} Auswahlen ({1} Zeichen ausgewählt)", + "multiSelection": "{0} Auswahlen", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "TAB-TASTE verschiebt Fokus", + "screenReaderDetected": "Sprachausgabe erkannt", + "screenReaderDetectedExtra": "Wenn Sie keine Sprachausgabe verwenden, ändern Sie die Einstellung \"editor.accessibilitySupport\" in \"Aus\".", + "disableTabMode": "Barrierefreiheitsmodus deaktivieren", + "gotoLine": "Gehe zu Zeile", + "indentation": "Einzug", + "selectEncoding": "Codierung auswählen", + "selectEOL": "Zeilenendesequenz auswählen", + "selectLanguageMode": "Sprachmodus auswählen", + "fileInfo": "Dateiinformationen", + "spacesSize": "Leerzeichen: {0}", + "tabSize": "Tabulatorgröße: {0}", + "showLanguageExtensions": "Marketplace-Erweiterungen für \"{0}\" durchsuchen...", + "changeMode": "Sprachmodus ändern", + "noEditor": "Zurzeit ist kein Text-Editor aktiv.", + "languageDescription": "({0}): konfigurierte Sprache", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "Sprachen (Bezeichner)", + "configureModeSettings": "\"{0}\" sprachbasierte Einstellungen konfigurieren...", + "configureAssociationsExt": "Dateizuordnung für \"{0}\" konfigurieren...", + "autoDetect": "Automatische Erkennung", + "pickLanguage": "Sprachmodus auswählen", + "currentAssociation": "Aktuelle Zuordnung", + "pickLanguageToConfigure": "Sprachmodus auswählen, der \"{0}\" zugeordnet werden soll", + "changeIndentation": "Einzug ändern", + "noWritableCodeEditor": "Der aktive Code-Editor ist schreibgeschützt.", + "indentView": "Ansicht wechseln", + "indentConvert": "Datei konvertieren", + "pickAction": "Aktion auswählen", + "changeEndOfLine": "Zeilenendesequenz ändern", + "pickEndOfLine": "Zeilenendesequenz auswählen", + "changeEncoding": "Dateicodierung ändern", + "noFileEditor": "Zurzeit ist keine Datei aktiv.", + "saveWithEncoding": "Mit Codierung speichern", + "reopenWithEncoding": "Mit Codierung erneut öffnen", + "guessedEncoding": "Vom Inhalt abgeleitet", + "pickEncodingForReopen": "Dateicodierung zum erneuten Öffnen der Datei auswählen", + "pickEncodingForSave": "Dateicodierung auswählen, mit der gespeichert werden soll" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..c2e471cba3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "Registerkartenaktionen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..07d9c471cd --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "Textdiff-Editor", + "readonlyEditorWithInputAriaLabel": "{0}. Schreibgeschützter Textvergleichs-Editor.", + "readonlyEditorAriaLabel": "Schreibgeschützter Textvergleichs-Editor.", + "editableEditorWithInputAriaLabel": "{0}. Textdateivergleichs-Editor.", + "editableEditorAriaLabel": "Textdateivergleichs-Editor", + "navigate.next.label": "Nächste Änderung", + "navigate.prev.label": "Vorherige Änderung", + "inlineDiffLabel": "Zur Inlineansicht wechseln", + "sideBySideDiffLabel": "Zur Parallelansicht wechseln" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..22b1a5063a --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0} Gruppe {1}." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..c849279b19 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Text-Editor", + "readonlyEditorWithInputAriaLabel": "{0}. Schreibgeschützter Text-Editor.", + "readonlyEditorAriaLabel": "Schreibgeschützter Text-Editor.", + "untitledFileEditorWithInputAriaLabel": "{0}. Unbenannter Dateitext-Editor.", + "untitledFileEditorAriaLabel": "Unbenannter Dateitext-Editor." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..9132c15fba --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Schließen", + "closeOthers": "Andere schließen", + "closeRight": "Rechts schließen", + "closeAll": "Alle schließen", + "closeAllUnmodified": "Nicht geänderte schließen", + "keepOpen": "Geöffnet lassen", + "showOpenedEditors": "Geöffnete Editoren anzeigen", + "araLabelEditorActions": "Editor-Aktionen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..df6b88650b --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "Bereich schließen", + "togglePanel": "Bereich umschalten", + "focusPanel": "Fokus im Bereich", + "toggleMaximizedPanel": "Maximierten Bereich umschalten", + "maximizePanel": "Panelgröße maximieren", + "minimizePanel": "Panelgröße wiederherstellen", + "view": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..9a81ce2c67 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "Umschaltung für aktiven Bereich" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..d16efd88cc --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} (Drücken Sie die EINGABETASTE zur Bestätigung oder ESC, um den Vorgang abzubrechen.)", + "inputModeEntry": "Drücken Sie die EINGABETASTE, um Ihre Eingabe zu bestätigen, oder ESC, um den Vorgang abzubrechen.", + "emptyPicks": "Es sind keine Einträge zur Auswahl verfügbar.", + "quickOpenInput": "Geben Sie \"?\" ein, um Hilfe zu den Aktionen zu erhalten, die hier zur Verfügung stehen.", + "historyMatches": "zuletzt geöffnet", + "noResultsFound1": "Es wurden keine Ergebnisse gefunden.", + "canNotRunPlaceholder": "Dieser Quick Open-Handler kann im aktuellen Kontext nicht verwendet werden.", + "entryAriaLabel": "{0}, zuletzt geöffnet", + "removeFromEditorHistory": "Aus Verlauf entfernen", + "pickHistory": "Editor-Eintrag auswählen, der aus dem Verlauf entfernt werden soll" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..f53f25f86c --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Gehe zu Datei...", + "quickNavigateNext": "Zum nächsten Element in Quick Open navigieren", + "quickNavigatePrevious": "Zum vorherigen Element in Quick Open navigieren", + "quickSelectNext": "Nächstes Element in Quick Open auswählen", + "quickSelectPrevious": "Vorheriges Element in Quick Open auswählen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..f53f25f86c --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Gehe zu Datei...", + "quickNavigateNext": "Zum nächsten Element in Quick Open navigieren", + "quickNavigatePrevious": "Zum vorherigen Element in Quick Open navigieren", + "quickSelectNext": "Nächstes Element in Quick Open auswählen", + "quickSelectPrevious": "Vorheriges Element in Quick Open auswählen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..f77f8cb09e --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "Randleiste ausblenden", + "focusSideBar": "Fokus in Randleiste", + "viewCategory": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..2bacb78a6c --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "Der Befehl \"{0}\" ist zurzeit nicht aktiviert und kann nicht ausgeführt werden.", + "manageExtension": "Erweiterung verwalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..f4ea9546a3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[Nicht unterstützt]", + "devExtensionWindowTitlePrefix": "[Erweiterungsentwicklungshost]" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/quickopen.i18n.json b/i18n/deu/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..55239af82b --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "Keine übereinstimmenden Ergebnisse.", + "noResultsFound2": "Es wurden keine Ergebnisse gefunden.", + "entryAriaLabel": "{0}, Befehl" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/viewlet.i18n.json b/i18n/deu/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..d4bbb68e41 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Alle zuklappen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/common/theme.i18n.json b/i18n/deu/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..5682d05d94 --- /dev/null +++ b/i18n/deu/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "Hintergrundfarbe der aktiven Registerkarte. Registerkarten sind die Container für Editors im Editorbereich. In einer Editorgruppe können mehrere Registerkarten geöffnet werden. Mehrere Editorgruppen können vorhanden sein.", + "tabInactiveBackground": "Hintergrundfarbe der inaktiven Registerkarte. Registerkarten sind die Container für Editors im Editorbereich. In einer Editorgruppe können mehrere Registerkarten geöffnet werden. Mehrere Editorgruppen können vorhanden sein.", + "tabBorder": "Rahmen zum Trennen von Registerkarten. Registerkarten sind die Container für Editoren im Editor-Bereich. In einer Editor-Gruppe können mehrere Registerkarten geöffnet werden. Mehrere Editor-Gruppen sind möglich.", + "tabActiveBorder": "Rahmen zum Hervorheben aktiver Registerkarten. Registerkarten sind die Container für Editoren im Editor-Bereich. In einer Editor-Gruppe können mehrere Registerkarten geöffnet werden. Mehrere Editor-Gruppen sind möglich.", + "tabActiveUnfocusedBorder": "Rahmen zum Hervorheben aktiver Registerkarten in einer unfokussierten Gruppe. Registerkarten sind die Container für Editoren im Editor-Bereich. In einer Editor-Gruppe können mehrere Registerkarten geöffnet werden. Mehrere Editor-Gruppen sind möglich.", + "tabActiveForeground": "Vordergrundfarbe der aktiven Registerkarte in einer aktiven Gruppe. Registerkarten sind die Container für Editors im Editorbereich. In einer Editorgruppe können mehrere Registerkarten geöffnet werden. Mehrere Editorgruppen können vorhanden sein.", + "tabInactiveForeground": "Vordergrundfarbe der inaktiven Registerkarte in einer aktiven Gruppe. Registerkarten sind die Container für Editors im Editorbereich. In einer Editorgruppe können mehrere Registerkarten geöffnet werden. Mehrere Editorgruppen können vorhanden sein.", + "tabUnfocusedActiveForeground": "Vordergrundfarbe für aktive Registerkarten in einer unfokussierten Gruppe. Registerkarten sind die Container für Editoren im Editor-Bereich. In einer Editor-Gruppe können mehrere Registerkarten geöffnet werden. Mehrere Editor-Gruppen sind möglich.", + "tabUnfocusedInactiveForeground": "Vordergrundfarbe für inaktive Registerkarten in einer unfokussierten Gruppe. Registerkarten sind die Container für Editoren im Editor-Bereich. In einer Editor-Gruppe können mehrere Registerkarten geöffnet werden. Mehrere Editor-Gruppen sind möglich.", + "editorGroupBackground": "Hintergrundfarbe einer Editor-Gruppe. Editor-Gruppen sind die Container der Editoren. Die Hintergrundfarbe wird beim Ziehen von Editoren angezeigt.", + "tabsContainerBackground": "Hintergrundfarbe der Titelüberschrift der Editor-Gruppe, wenn die Registerkarten deaktiviert sind. Editor-Gruppen sind die Container der Editoren.", + "tabsContainerBorder": "Rahmenfarbe der Titelüberschrift der Editor-Gruppe, wenn die Registerkarten deaktiviert sind. Editor-Gruppen sind die Container der Editoren.", + "editorGroupHeaderBackground": "Hintergrundfarbe der Titelüberschrift des Editors, wenn die Registerkarten deaktiviert sind. Editor-Gruppen sind die Container der Editoren.", + "editorGroupBorder": "Farbe zum Trennen mehrerer Editor-Gruppen. Editor-Gruppen sind die Container der Editoren.", + "editorDragAndDropBackground": " Hintergrundfarbe beim Ziehen von Editoren. Die Farbe muss transparent sein, damit der Editor-Inhalt noch sichtbar sind.", + "panelBackground": "Hintergrundfarbe des Panels. Panels werden unter dem Editorbereich angezeigt und enthalten Ansichten wie die Ausgabe und das integrierte Terminal.", + "panelBorder": "Farbe des oberen Panelrahmens, der das Panel vom Editor abtrennt. Panels werden unter dem Editorbereich angezeigt und enthalten Ansichten wie die Ausgabe und das integrierten Terminal.", + "panelActiveTitleForeground": "Titelfarbe für den aktiven Bereich. Bereiche werden unter dem Editorbereich angezeigt und enthalten Ansichten wie Ausgabe und integriertes Terminal.", + "panelInactiveTitleForeground": "Titelfarbe für den inaktiven Bereich. Bereiche werden unter dem Editorbereich angezeigt und enthalten Ansichten wie Ausgabe und integriertes Terminal.", + "panelActiveTitleBorder": "Rahmenfarbe für den Titel des aktiven Bereichs. Bereiche werden unter dem Editorbereich angezeigt und enthalten Ansichten wie Ausgabe und integriertes Terminal.", + "statusBarForeground": "Vordergrundfarbe der Statusleiste beim Öffnen eines Arbeitsbereichs. Die Statusleiste wird unten im Fenster angezeigt.", + "statusBarNoFolderForeground": "Vordergrundfarbe der Statusleiste, wenn kein Ordner geöffnet ist. Die Statusleiste wird unten im Fenster angezeigt.", + "statusBarBackground": "Hintergrundfarbe der Statusleiste beim Öffnen eines Arbeitsbereichs. Die Statusleiste wird unten im Fenster angezeigt.", + "statusBarNoFolderBackground": "Hintergrundfarbe der Statusleiste, wenn kein Ordner geöffnet ist. Die Statusleiste wird unten im Fenster angezeigt.", + "statusBarBorder": "Rahmenfarbe der Statusleiste für die Abtrennung von der Seitenleiste und dem Editor. Die Statusleiste wird unten im Fenster angezeigt.", + "statusBarNoFolderBorder": "Rahmenfarbe der Statusleiste zur Abtrennung von der Randleiste und dem Editor, wenn kein Ordner geöffnet ist. Die Statusleiste wird unten im Fenster angezeigt.", + "statusBarItemActiveBackground": "Hintergrundfarbe für Statusleistenelemente beim Klicken. Die Statusleiste wird am unteren Rand des Fensters angezeigt.", + "statusBarItemHoverBackground": "Hintergrundfarbe der Statusleistenelemente beim Daraufzeigen. Die Statusleiste wird am unteren Seitenrand angezeigt.", + "statusBarProminentItemBackground": "Hintergrundfarbe für markante Elemente der Statusleiste. Markante Elemente sind im Vergleich zu anderen Statusleisteneinträgen hervorgehoben, um auf ihre Bedeutung hinzuweisen. Die Statusleiste wird unten im Fenster angezeigt.", + "statusBarProminentItemHoverBackground": "Hintergrundfarbe für markante Elemente der Statusleiste, wenn auf diese gezeigt wird. Markante Elemente sind im Vergleich zu anderen Statusleisteneinträgen hervorgehoben, um auf ihre Bedeutung hinzuweisen. Die Statusleiste wird unten im Fenster angezeigt.", + "activityBarBackground": "Hintergrundfarbe der Aktivitätsleiste. Die Aktivitätsleiste wird ganz links oder rechts angezeigt und ermöglicht das Wechseln zwischen verschiedenen Ansichten der Seitenleiste.", + "activityBarForeground": "Vordergrundfarbe der Aktivitätsleiste (z. B. für Symbole). Die Aktivitätsleiste wird ganz links oder rechts angezeigt und ermöglicht das Wechseln zwischen verschiedenen Ansichten der Seitenleiste.", + "activityBarBorder": "Rahmenfarbe der Aktivitätsleiste für die Abtrennung von der Seitenleiste. Die Aktivitätsleiste wird ganz links oder rechts angezeigt und ermöglicht das Wechseln zwischen verschiedenen Ansichten der Seitenleiste.", + "activityBarDragAndDropBackground": "Drag & Drop-Feedbackfarbe für Elemente der Aktivitätsleiste. Die Farbe muss transparent sein, damit die Einträge der Aktivitätsleiste noch sichtbar sind. Die Aktivitätsleiste wird ganz links oder ganz rechts angezeigt und ermöglicht den Wechsel zwischen Ansichten der Seitenleiste.", + "activityBarBadgeBackground": "Hintergrundfarbe für Aktivitätsinfobadge. Die Aktivitätsleiste wird ganz links oder ganz rechts angezeigt und ermöglicht den Wechsel zwischen Ansichten der Seitenleiste.", + "activityBarBadgeForeground": "Vordergrundfarbe für Aktivitätsinfobadge. Die Aktivitätsleiste wird ganz links oder ganz rechts angezeigt und ermöglicht den Wechsel zwischen Ansichten der Seitenleiste.", + "sideBarBackground": "Hintergrundfarbe der Seitenleiste. Die Seitenleiste ist der Container für Ansichten wie den Explorer und die Suche.", + "sideBarForeground": "Vordergrundfarbe der Seitenleiste. Die Seitenleiste ist der Container für Ansichten wie den Explorer und die Suche.", + "sideBarBorder": "Rahmenfarbe der Seitenleiste zum Abtrennen an der Seite zum Editor. Die Seitenleiste ist der Container für Ansichten wie den Explorer und die Suche.", + "sideBarTitleForeground": "Vordergrundfarbe der Seitenleiste. Die Seitenleiste ist der Container für Ansichten wie den Explorer und die Suche.", + "sideBarDragAndDropBackground": "Drag & Drop-Feedbackfarbe für die Abschnitte der Randleiste. Die Farbe muss transparent sein, damit die Abschnitte der Randleiste noch sichtbar sind. Die Randleiste ist der Container für Ansichten wie den Explorer und die Suche. ", + "sideBarSectionHeaderBackground": "Hintergrundfarbe der Abschnittsüberschrift der Seitenleiste. Die Seitenleiste ist der Container für Ansichten wie den Explorer und die Suche.", + "sideBarSectionHeaderForeground": "Vordergrundfarbe der Abschnittsüberschrift der Seitenleiste. Die Seitenleiste ist der Container für Ansichten wie den Explorer und die Suche.", + "titleBarActiveForeground": "Vordergrund der Titelleiste, wenn das Fenster aktiv ist. Diese Farbe wird derzeit nur von MacOS unterstützt.", + "titleBarInactiveForeground": "Vordergrund der Titelleiste, wenn das Fenster inaktiv ist. Diese Farbe wird derzeit nur von MacOS unterstützt.", + "titleBarActiveBackground": "Hintergrund der Titelleiste, wenn das Fenster aktiv ist. Diese Farbe wird derzeit nur von MacOS unterstützt.", + "titleBarInactiveBackground": "Hintergrund der Titelleiste, wenn das Fenster inaktiv ist. Diese Farbe wird derzeit nur von MacOS unterstützt.", + "titleBarBorder": "Rahmenfarbe der Titelleiste. Diese Farbe wird derzeit nur unter MacOS unterstützt.", + "notificationsForeground": "Vordergrundfarbe für Benachrichtigungen. Benachrichtigungen werden oben im Fenster eingeblendet.", + "notificationsBackground": "Hintergrundfarbe für Benachrichtigungen. Benachrichtigungen werden oben im Fenster eingeblendet.", + "notificationsButtonBackground": "Hintergrundfarbe der Benachrichtigungsschaltfläche. Benachrichtigungen werden oben im Fenster eingeblendet.", + "notificationsButtonHoverBackground": "Hintergrundfarbe der Benachrichtigungsschaltfläche, wenn darauf gezeigt wird. Benachrichtigungen werden oben im Fenster eingeblendet.", + "notificationsButtonForeground": "Vordergrundfarbe der Benachrichtigungsschaltfläche. Benachrichtigungen werden oben im Fenster eingeblendet.", + "notificationsInfoBackground": "Hintergrundfarbe von Benachrichtigungsinformationen. Benachrichtigungen werden oben im Fenster eingeblendet.", + "notificationsInfoForeground": "Vordergrundfarbe von Benachrichtigungsinformationen. Benachrichtigungen werden oben im Fenster eingeblendet.", + "notificationsWarningBackground": "Hintergrundfarbe von Benachrichtigungswarnungen. Benachrichtigungen werden oben im Fenster eingeblendet.", + "notificationsWarningForeground": "Vordergrundfarbe von Benachrichtigungswarnungen. Benachrichtigungen werden oben im Fenster eingeblendet.", + "notificationsErrorBackground": "Hintergrundfarbe von Benachrichtigungsfehlern. Benachrichtigungen werden oben im Fenster eingeblendet.", + "notificationsErrorForeground": "Vordergrundfarbe von Benachrichtigungsfehlern. Benachrichtigungen werden oben im Fenster eingeblendet." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/deu/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..84b1683280 --- /dev/null +++ b/i18n/deu/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "Editor schließen", + "closeWindow": "Fenster schließen", + "closeWorkspace": "Arbeitsbereich schließen", + "noWorkspaceOpened": "Zurzeit ist kein Arbeitsbereich in dieser Instanz geöffnet, der geschlossen werden kann.", + "newWindow": "Neues Fenster", + "toggleFullScreen": "Vollbild umschalten", + "toggleMenuBar": "Menüleiste umschalten", + "toggleDevTools": "Entwicklertools umschalten", + "zoomIn": "Vergrößern", + "zoomOut": "Verkleinern", + "zoomReset": "Zoom zurücksetzen", + "appPerf": "Startleistung", + "reloadWindow": "Fenster erneut laden", + "switchWindowPlaceHolder": "Fenster auswählen, zu dem Sie wechseln möchten", + "current": "Aktuelles Fenster", + "close": "Fenster schließen", + "switchWindow": "Fenster wechseln...", + "quickSwitchWindow": "Fenster schnell wechseln...", + "workspaces": "Arbeitsbereiche", + "files": "Dateien", + "openRecentPlaceHolderMac": "Zum Öffnen auswählen (zum Öffnen in einem neuen Fenster die BEFEHLSTASTE gedrückt halten)", + "openRecentPlaceHolder": "Zum Öffnen auswählen (zum Öffnen in einem neuen Fenster die STRG-TASTE gedrückt halten)", + "remove": "Aus zuletzt geöffneten entfernen", + "openRecent": "Zuletzt benutzt...", + "quickOpenRecent": "Zuletzt benutzte schnell öffnen...", + "closeMessages": "Benachrichtigungs-E-Mail schließen", + "reportIssues": "Probleme melden", + "reportPerformanceIssue": "Leistungsproblem melden", + "keybindingsReference": "Referenz für Tastenkombinationen", + "openDocumentationUrl": "Dokumentation", + "openIntroductoryVideosUrl": "Einführungsvideos", + "openTipsAndTricksUrl": "Tipps und Tricks", + "toggleSharedProcess": "Freigegebenen Prozess umschalten", + "navigateLeft": "Zur Ansicht auf der linken Seite navigieren", + "navigateRight": "Zur Ansicht auf der rechten Seite navigieren", + "navigateUp": "Zur Ansicht darüber navigieren", + "navigateDown": "Zur Ansicht darunter navigieren", + "increaseViewSize": "Aktuelle Ansicht vergrößern", + "decreaseViewSize": "Aktuelle Ansicht verkleinern" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/deu/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/electron-browser/crashReporter.i18n.json b/i18n/deu/src/vs/workbench/electron-browser/crashReporter.i18n.json new file mode 100644 index 0000000000..ad7c98bf4e --- /dev/null +++ b/i18n/deu/src/vs/workbench/electron-browser/crashReporter.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetrie", + "telemetry.enableCrashReporting": "Aktiviert Absturzberichte, die an Microsoft gesendet werden.\nDiese Option erfordert einen Neustart, damit sie wirksam wird." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/deu/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..0750fcb395 --- /dev/null +++ b/i18n/deu/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "Der Erweiterungshost wurde nicht innerhalb von 10 Sekunden gestartet. Möglicherweise wurde er in der ersten Zeile beendet und benötigt einen Debugger, um die Ausführung fortzusetzen.", + "extensionHostProcess.startupFail": "Der Erweiterungshost wurde nicht innerhalb von 10 Sekunden gestartet. Dies stellt ggf. ein Problem dar.", + "extensionHostProcess.error": "Fehler vom Erweiterungshost: {0}", + "devTools": "Entwicklertools", + "extensionHostProcess.crash": "Der Erweiterungshost wurde unerwartet beendet. Bitte laden Sie das Fenster erneut, um ihn wiederherzustellen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/deu/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..62e9f4c4c4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "Anzeigen", + "help": "Hilfe", + "file": "Datei", + "workspaces": "Arbeitsbereiche", + "developer": "Entwickler", + "showEditorTabs": "Steuert, ob geöffnete Editoren auf Registerkarten angezeigt werden sollen.", + "editorTabCloseButton": "Steuert die Position der Schließen-Schaltflächen der Editor-Registerkarten oder deaktiviert sie bei der Einstellung \"off\".", + "showIcons": "Steuert, ob geöffnete Editoren mit einem Symbol angezeigt werden sollen. Hierzu muss auch ein Symboldesign aktiviert werden.", + "enablePreview": "Steuert, ob geöffnete Editoren als Vorschau angezeigt werden. Vorschau-Editoren werden wiederverwendet, bis sie gespeichert werden (z. B. über Doppelklicken oder Bearbeiten).", + "enablePreviewFromQuickOpen": "Steuert, ob geöffnete Editoren aus Quick Open als Vorschau angezeigt werden. Vorschau-Editoren werden wiederverwendet, bis sie gespeichert werden (z. B. über Doppelklicken oder Bearbeiten).", + "editorOpenPositioning": "Steuert, wo Editoren geöffnet werden. Wählen Sie \"Links\" oder \"Rechts\" aus, um Editoren links oder rechts vom aktuellen aktiven Editor zu öffnen. Wählen Sie \"Erster\" oder \"Letzter\" aus, um Editoren unabhängig vom aktuell aktiven Editor zu öffnen.", + "revealIfOpen": "Steuert, ob ein geöffneter Editor in einer der sichtbaren Gruppen angezeigt wird. Ist diese Option deaktiviert, wird ein Editor vorzugsweise in der aktuell aktiven Editorgruppe geöffnet. Ist diese Option aktiviert, wird ein bereits geöffneter Editor angezeigt und nicht in der aktuell aktiven Editorgruppe erneut geöffnet. In einigen Fällen wird diese Einstellung ignoriert, z. B. wenn das Öffnen eines Editors in einer bestimmten Gruppe oder neben der aktuell aktiven Gruppe erzwungen wird.", + "commandHistory": "Steuert, ob die Anzahl zuletzt verwendeter Befehle im Verlauf für die Befehlspalette gespeichert wird. Legen Sie diese Option auf 0 fest, um den Befehlsverlauf zu deaktivieren.", + "preserveInput": "Steuert, ob die letzte typisierte Eingabe in die Befehlspalette beim nächsten Öffnen wiederhergestellt wird.", + "closeOnFocusLost": "Steuert, ob Quick Open automatisch geschlossen werden soll, sobald das Feature den Fokus verliert.", + "openDefaultSettings": "Steuert, ob beim Öffnen der Einstellungen auch ein Editor geöffnet wird, der alle Standardeinstellungen anzeigt.", + "sideBarLocation": "Steuert die Position der Seitenleiste. Diese kann entweder links oder rechts von der Workbench angezeigt werden.", + "statusBarVisibility": "Steuert die Sichtbarkeit der Statusleiste im unteren Bereich der Workbench.", + "activityBarVisibility": "Steuert die Sichtbarkeit der Aktivitätsleiste in der Workbench.", + "closeOnFileDelete": "Steuert, ob Editoren, die eine Datei anzeigen, automatisch geschlossen werden sollen, wenn die Datei von einem anderen Prozess umbenannt oder gelöscht wird. Wenn Sie diese Option deaktivieren, bleibt der Editor bei einem solchen Ereignis als geändert offen. Bei Löschvorgängen innerhalb der Anwendung wird der Editor immer geschlossen, und geänderte Dateien werden nie geschlossen, damit Ihre Daten nicht verloren gehen.", + "fontAliasing": "Steuert die Schriftartaliasingmethode in der Workbench.\n- default: Subpixel-Schriftartglättung. Auf den meisten Nicht-Retina-Displays wird Text bei dieser Einstellung am schärfsten dargestellt.\n- antialiased: Glättet die Schriftart auf der Pixelebene (im Gegensatz zur Subpixelebene). Bei dieser Einstellung kann die Schriftart insgesamt heller wirken.\n- none: Deaktiviert die Schriftartglättung. Text wird mit gezackten scharfen Kanten dargestellt.\n", + "workbench.fontAliasing.default": "Subpixel-Schriftartglättung. Auf den meisten Nicht-Retina-Displays wird Text bei dieser Einstellung am schärfsten dargestellt.", + "workbench.fontAliasing.antialiased": "Glättet die Schriftart auf der Pixelebene (im Gegensatz zur Subpixelebene). Bei dieser Einstellung kann die Schriftart insgesamt heller wirken.", + "workbench.fontAliasing.none": "Deaktiviert die Schriftartglättung. Text wird mit gezackten scharfen Kanten dargestellt.", + "swipeToNavigate": "Hiermit navigieren Sie per waagrechtem Wischen mit drei Fingen zwischen geöffneten Dateien.", + "workbenchConfigurationTitle": "Workbench", + "window.openFilesInNewWindow.on": "Dateien werden in einem neuen Fenster geöffnet.", + "window.openFilesInNewWindow.off": "Dateien werden im Fenster mit dem geöffneten Dateiordner oder im letzten aktiven Fenster geöffnet.", + "window.openFilesInNewWindow.default": "Dateien werden im Fenster mit dem geöffneten Dateiordner oder im letzten aktiven Fenster geöffnet, sofern sie nicht über das Dock oder den Finder geöffnet werden (nur MacOS).", + "openFilesInNewWindow": "Steuert, ob Dateien in einem neuen Fenster oder im letzten aktiven Fenster geöffnet werden.\n- default: Die Dateien werden im letzten aktiven Fenster geöffnet, sofern sie nicht über das Dock oder den Finder geöffnet werden (nur macOS).\n- on: Die Dateien werden in einem neuen Fenster geöffnet.\n- off: Die Dateien werden im letzten aktiven Fenster geöffnet.\nIn einigen Fällen wird diese Einstellung unter Umständen ignoriert (z. B. bei der Befehlszeilenoption \"-new-window\" oder \"-reuse-window\").", + "window.openFoldersInNewWindow.on": "Ordner werden in einem neuen Fenster geöffnet.", + "window.openFoldersInNewWindow.off": "Ordner ersetzen das letzte aktive Fenster.", + "window.openFoldersInNewWindow.default": "Ordner werden in einem neuen Fenster geöffnet, sofern kein Ordner innerhalb der Anwendung ausgewählt wird (z. B. über das Dateimenü).", + "openFoldersInNewWindow": "Steuert, ob Ordner in einem neuen Fenster geöffnet werden oder das letzte aktive Fenster ersetzen.\n- default: Die Ordner werden in einem neuen Fenster geöffnet, sofern kein Ordner innerhalb der Anwendung ausgewählt wird (z. B. über das Dateimenü).\n- on: Die Ordner werden in einem neuen Fenster geöffnet.\n- off: Die Ordner ersetzen das letzte aktive Fenster.\nIn einigen Fällen wird diese Einstellung unter Umständen ignoriert (z. B. bei der Befehlszeilenoption \"-new-window\" oder \"-reuse-window\").", + "window.reopenFolders.all": "Alle Fenster erneut öffnen.", + "window.reopenFolders.folders": "Alle Ordner erneut öffnen. Leere Arbeitsbereiche werden nicht wiederhergestellt.", + "window.reopenFolders.one": "Das letzte aktive Fenster erneut öffnen. ", + "window.reopenFolders.none": "Fenster nie erneut öffnen und immer mit einem leeren Fenster beginnen.", + "restoreWindows": "Steuert, wie Fenster nach einem Neustart erneut geöffnet werden. Wählen Sie \"none\", um immer mit einem leeren Arbeitsbereich zu beginnen, \"one\", um das zuletzt verwendete Fenster erneut zu öffnen, \"folders\", um alle Fenster, in denen Ordner geöffnet waren, erneut zu öffnen, oder \"all\", um alle Fenster der letzten Sitzung erneut zu öffnen.", + "restoreFullscreen": "Steuert, ob ein Fenster im Vollbildmodus wiederhergestellt wird, wenn es im Vollbildmodus beendet wurde.", + "zoomLevel": "Passen Sie den Zoomfaktor des Fensters an. Die ursprüngliche Größe ist 0. Jede Inkrementierung nach oben (z. B. 1) oder unten (z. B. -1) stellt eine Vergrößerung bzw. Verkleinerung um 20 % dar. Sie können auch Dezimalwerte eingeben, um den Zoomfaktor genauer anzupassen.", + "title": "Steuert den Fenstertitel basierend auf dem aktiven Editor. Variablen werden abhängig vom Kontext ersetzt:\n${activeEditorShort}: z. B. meineDatei.txt\n${activeEditorMedium}: z. B. meinOrdner/meineDatei.txt\n${activeEditorLong}: z. B. /Users/Development/meinProjekt/meinOrdner/meineDatei.txt\n${folderName}: z. B. \nmeinOrdner${folderPath}: z. B. /Users/Development/meinOrdner\n${rootName}: z. B. meinOrdner1, meinOrdner2, meinOrdner3\n${rootPath}: z. B. /Users/Development/meinArbeitsbereich\n${appName}: z. B. VS Code\n${dirty}: ein Änderungsindikator, wenn der aktive Editor geändert wurde\n${separator}: ein bedingtes Trennzeichen (\" - \"), das nur angezeigt wird, wenn es zwischen Variablen mit Werten steht", + "window.newWindowDimensions.default": "Öffnet neue Fenster in der Mitte des Bildschirms.", + "window.newWindowDimensions.inherit": "Öffnet neue Fenster mit den gleichen Abmessungen wie das letzte aktive Fenster.", + "window.newWindowDimensions.maximized": "Öffnet neue Fenster maximiert.", + "window.newWindowDimensions.fullscreen": "Öffnet neue Fenster im Vollbildmodus.", + "newWindowDimensions": "Steuert die Abmessungen beim Öffnen eines neuen Fensters. Standardmäßig wird in der Mitte des Bildschirms ein neues Fenster mit kleinen Abmessungen geöffnet. Bei der Einstellung \"inherit\" erhält das Fenster die gleichen Abmessungen wie das letzte aktive Fenster. Bei der Einstellung \"maximized\" wird das Fenster maximiert geöffnet, und bei \"fullscreen\" wird es im Vollbildmodus geöffnet. Die Einstellung hat keine Auswirkungen auf das zuerst geöffnete Fenster. Größe und Position des ersten Fensters werden immer so wiederhergestellt, wie sie vor dem Schließen waren.", + "closeWhenEmpty": "Steuert, ob das Fenster beim Schließen des letzten Editors geschlossen wird. Diese Einstellung gilt nur für Fenster, in denen keine Ordner angezeigt werden.", + "window.menuBarVisibility.default": "Das Menü ist nur im Vollbildmodus ausgeblendet.", + "window.menuBarVisibility.visible": "Das Menu wird immer angezeigt, auch im Vollbildmodus.", + "window.menuBarVisibility.toggle": "Das Menu ist ausgeblendet, kann aber mit der Alt-Taste angezeigt werden.", + "window.menuBarVisibility.hidden": "Das Menü ist immer ausgeblendet.", + "menuBarVisibility": "Steuert die Sichtbarkeit der Menüleiste. Die Einstellung \"Umschalten\" bedeutet, dass die Menüleiste durch einfaches Betätigen der ALT-Taste angezeigt und ausgeblendet wird. Die Menüleite wird standardmäßig angezeigt, sofern sich das Fenster nicht im Vollbildmodus befindet.", + "enableMenuBarMnemonics": "Ist dies aktiviert, können die Hauptmenüs mithilfe von Tastenkombinationen mit der Alt-Taste geöffnet werden. Wenn mnemonische Zeichen deaktiviert werden, können diese Tastenkombinationen mit der Alt-Taste stattdessen an Editor-Befehle gebunden werden.", + "autoDetectHighContrast": "Ist diese Option aktiviert, erfolgt automatisch ein Wechsel zu einem Design mit hohem Kontrast, wenn Windows ein Design mit hohem Kontrast verwendet, und zu einem dunklen Design, wenn Sie für Windows kein Design mit hohem Kontrast mehr verwenden.", + "titleBarStyle": "Passt das Aussehen der Titelleiste des Fensters an. Zum Anwenden der Änderungen ist ein vollständiger Neustart erforderlich.", + "window.nativeTabs": "Aktiviert MacOS Sierra-Fensterregisterkarten. Beachten Sie, dass zum Übernehmen von Änderungen ein vollständiger Neustart erforderlich ist und durch ggf. konfigurierte native Registerkarten ein benutzerdefinierter Titelleistenstil deaktiviert wird.", + "windowConfigurationTitle": "Fenster", + "zenModeConfigurationTitle": "Zen-Modus", + "zenMode.fullScreen": "Steuert, ob die Workbench durch das Aktivieren des Zen-Modus in den Vollbildmodus wechselt.", + "zenMode.hideTabs": "Steuert, ob die Workbench-Registerkarten durch Aktivieren des Zen-Modus ebenfalls ausgeblendet werden.", + "zenMode.hideStatusBar": "Steuert, ob die Statusleiste im unteren Bereich der Workbench durch Aktivieren des Zen-Modus ebenfalls ausgeblendet wird.", + "zenMode.hideActivityBar": "Steuert, ob die Aktivitätsleiste im linken Bereich der Workbench durch Aktivieren des Zen-Modus ebenfalls ausgeblendet wird.", + "zenMode.restore": "Steuert, ob ein Fenster im Zen-Modus wiederhergestellt werden soll, wenn es im Zen-Modus beendet wurde." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/electron-browser/main.i18n.json b/i18n/deu/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..d533ef28d3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "Eine erforderliche Datei konnte nicht geladen werden. Entweder sind Sie nicht mehr mit dem Internet verbunden oder der verbundene Server ist offline. Aktualisieren Sie den Browser, und wiederholen Sie den Vorgang.", + "loaderErrorNative": "Fehler beim Laden einer erforderlichen Datei. Bitte starten Sie die Anwendung neu, und versuchen Sie es dann erneut. Details: {0}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/deu/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..a06ceb0e30 --- /dev/null +++ b/i18n/deu/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "Es wird empfohlen, Code nicht als \"root\" auszuführen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/electron-browser/window.i18n.json b/i18n/deu/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..4e674907ec --- /dev/null +++ b/i18n/deu/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "Rückgängig", + "redo": "Wiederholen", + "cut": "Ausschneiden", + "copy": "Kopieren", + "paste": "Einfügen", + "selectAll": "Alles auswählen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/deu/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..d653223210 --- /dev/null +++ b/i18n/deu/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "Entwickler", + "file": "Datei" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/deu/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..20384341c9 --- /dev/null +++ b/i18n/deu/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "Der Pfad \"{0}\" verweist nicht auf einen gültigen Test Runner für eine Erweiterung." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/deu/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..461f496af1 --- /dev/null +++ b/i18n/deu/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Fehler beim Analysieren von {0}: {1}.", + "fileReadFail": "Die Datei \"{0}\" kann nicht gelesen werden: {1}", + "jsonsParseFail": "Fehler beim Analysieren von {0} oder {1}: {2}.", + "missingNLSKey": "Die Nachricht für den Schlüssel {0} wurde nicht gefunden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..a8982e1f4e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "Befehl \"{0}\" in \"PATH\" installieren", + "not available": "Dieser Befehl ist nicht verfügbar.", + "successIn": "Der Shellbefehl \"{0}\" wurde erfolgreich in \"PATH\" installiert.", + "warnEscalation": "Der Code fordert nun mit \"osascript\" zur Eingabe von Administratorberechtigungen auf, um den Shellbefehl zu installieren.", + "ok": "OK", + "cantCreateBinFolder": "/usr/local/bin kann nicht erstellt werden.", + "cancel2": "Abbrechen", + "aborted": "Abgebrochen", + "uninstall": "Befehl \"{0}\" aus \"PATH\" deinstallieren", + "successFrom": "Der Shellbefehl \"{0}\" wurde erfolgreich aus \"PATH\" deinstalliert.", + "shellCommand": "Shellbefehl" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..7cc1fb758f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "Die Einstellung \"editor.accessibilitySupport\" wird in \"Ein\" geändert.", + "openingDocs": "Die Dokumentationsseite zur Barrierefreiheit von VS Code wird jetzt geöffnet.", + "introMsg": "Vielen Dank, dass Sie die Optionen für Barrierefreiheit von VS Code testen.", + "status": "Status:", + "changeConfigToOnMac": "Betätigen Sie jetzt die Befehlstaste+E, um den Editor zu konfigurieren, sodass er permanent für die Verwendung mit einer Sprachausgabe optimiert wird.", + "changeConfigToOnWinLinux": "Betätigen Sie jetzt die Befehlstaste+E, um den Editor zu konfigurieren, sodass er permanent für die Verwendung mit einer Sprachausgabe optimiert wird.", + "auto_unknown": "Der Editor ist für die Verwendung von Plattform-APIs konfiguriert, um zu erkennen, wenn eine Sprachausgabe angefügt wird, die aktuelle Laufzeit unterstützt dies jedoch nicht.", + "auto_on": "Der Editor hat automatisch erkannt, dass eine Sprachausgabe angefügt wurde.", + "auto_off": "Der Editor ist so konfiguriert, dass er automatisch erkennt, wenn eine Sprachausgabe angefügt wird, was momentan nicht der Fall ist.", + "configuredOn": "Der Editor ist so konfiguriert, dass er für die Verwendung mit einer Sprachausgabe durchgehend optimiert wird – Sie können dies ändern, indem Sie die Einstellung \"editor.accessibilitySupport\" bearbeiten.", + "configuredOff": "Der Editor ist so konfiguriert, dass er für die Verwendung mit einer Sprachausgabe nie optimiert wird.", + "tabFocusModeOnMsg": "Durch Drücken der TAB-TASTE im aktuellen Editor wird der Fokus in das nächste Element verschoben, das den Fokus erhalten kann. Schalten Sie dieses Verhalten um, indem Sie {0} drücken.", + "tabFocusModeOnMsgNoKb": "Durch Drücken der TAB-TASTE im aktuellen Editor wird der Fokus in das nächste Element verschoben, das den Fokus erhalten kann. Der {0}-Befehl kann zurzeit nicht durch eine Tastenzuordnung ausgelöst werden.", + "tabFocusModeOffMsg": "Durch Drücken der TAB-TASTE im aktuellen Editor wird das Tabstoppzeichen eingefügt. Schalten Sie dieses Verhalten um, indem Sie {0} drücken.", + "tabFocusModeOffMsgNoKb": "Durch Drücken der TAB-TASTE im aktuellen Editor wird das Tabstoppzeichen eingefügt. Der {0}-Befehl kann zurzeit nicht durch eine Tastenzuordnung ausgelöst werden.", + "openDocMac": "Drücken Sie die Befehlstaste+H, um ein Browserfenster mit zusätzlichen VS Code-Informationen zur Barrierefreiheit zu öffnen.", + "openDocWinLinux": "Drücken Sie STRG+H, um ein Browserfenster mit zusätzlichen VS Code-Informationen zur Barrierefreiheit zu öffnen.", + "outroMsg": "Sie können diese QuickInfo schließen und durch Drücken von ESC oder UMSCHALT+ESC zum Editor zurückkehren.", + "ShowAccessibilityHelpAction": "Hilfe zur Barrierefreiheit anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..33a63968e0 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "Entwickler: Wichtige Zuordnungen prüfen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..ba378c8154 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Entwickler: TM-Bereiche untersuchen", + "inspectTMScopesWidget.loading": "Wird geladen..." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..0d7cafb067 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Fehler beim Analysieren von {0}: {1}", + "schema.openBracket": "Das öffnende Klammerzeichen oder die Zeichenfolgensequenz.", + "schema.closeBracket": "Das schließende Klammerzeichen oder die Zeichenfolgensequenz.", + "schema.comments": "Definiert die Kommentarsymbole.", + "schema.blockComments": "Definiert, wie Blockkommentare markiert werden.", + "schema.blockComment.begin": "Die Zeichenfolge, mit der ein Blockkommentar beginnt.", + "schema.blockComment.end": "Die Zeichenfolge, die einen Blockkommentar beendet.", + "schema.lineComment": "Die Zeichenfolge, mit der ein Zeilenkommentar beginnt.", + "schema.brackets": "Definiert die Klammersymbole, die den Einzug vergrößern oder verkleinern.", + "schema.autoClosingPairs": "Definiert die Klammerpaare. Wenn eine öffnende Klammer eingegeben wird, wird die schließende Klammer automatisch eingefügt.", + "schema.autoClosingPairs.notIn": "Definiert eine Liste von Bereichen, in denen die automatischen Paare deaktiviert sind.", + "schema.surroundingPairs": "Definiert die Klammerpaare, in die eine ausgewählte Zeichenfolge eingeschlossen werden kann.", + "schema.wordPattern": "Die Worddefinition für die Sprache.", + "schema.wordPattern.pattern": "RegExp Muster für Wortübereinstimmungen.", + "schema.wordPattern.flags": "RegExp Kennzeichen für Wortübereinstimmungen", + "schema.wordPattern.flags.errorMessage": "Muss mit dem Muster `/^([gimuy]+)$/` übereinstimmen.", + "schema.indentationRules": "Die Einzugseinstellungen der Sprache.", + "schema.indentationRules.increaseIndentPattern": "Wenn eine Zeile diesem Muster entspricht, sollten alle Zeilen nach dieser Zeile einmal eingerückt werden (bis eine andere Regel übereinstimmt).", + "schema.indentationRules.increaseIndentPattern.pattern": "Das RegExp-Muster für increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.flags": "Die RegExp-Flags für increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.errorMessage": "Muss mit dem Muster `/^([gimuy]+)$/` übereinstimmen.", + "schema.indentationRules.decreaseIndentPattern": "Wenn eine Zeile diesem Muster entspricht, sollte der Einzug aller Zeilen nach dieser Zeile einmal aufgehoben werden (bis eine andere Regel übereinstimmt).", + "schema.indentationRules.decreaseIndentPattern.pattern": "Das RegExp-Muster für decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.flags": "Die RegExp-Flags für decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "Muss mit dem Muster `/^([gimuy]+)$/` übereinstimmen.", + "schema.indentationRules.indentNextLinePattern": "Wenn eine Zeile diesem Muster entspricht, sollte **nur die nächste Zeile** nach dieser Zeile einmal eingerückt werden.", + "schema.indentationRules.indentNextLinePattern.pattern": "Das RegExp-Muster für indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.flags": "Die RegExp-Flags für indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.errorMessage": "Muss mit dem Muster `/^([gimuy]+)$/` übereinstimmen.", + "schema.indentationRules.unIndentedLinePattern": "Wenn eine Zeile diesem Muster entspricht, sollte ihr Einzug nicht geändert und die Zeile nicht mit den anderen Regeln ausgewertet werden.", + "schema.indentationRules.unIndentedLinePattern.pattern": "Das RegExp-Muster für unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.flags": "Die RegExp-Flags für unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "Muss mit dem Muster `/^([gimuy]+)$/` übereinstimmen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..ba378c8154 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Entwickler: TM-Bereiche untersuchen", + "inspectTMScopesWidget.loading": "Wird geladen..." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..0b0f04822d --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "Ansicht: Minikarte umschalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..dc92bbf450 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Multi-Curosor-Modifizierer umschalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..6d424d3595 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "Ansicht: Steuerzeichen umschalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..b7cc9b8a13 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "Ansicht: Rendern von Leerzeichen umschalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..1ac071e3a6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "Ansicht: Zeilenumbruch umschalten", + "wordWrap.notInDiffEditor": "In einem Diff-Editor kann der Zeilenumbruch nicht umgeschaltet werden.", + "unwrapMinified": "Umbruch für diese Datei deaktivieren", + "wrapMinified": "Umbruch für diese Datei aktivieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..6a5ffeff5f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "OK", + "wordWrapMigration.dontShowAgain": "Nicht mehr anzeigen", + "wordWrapMigration.openSettings": "Einstellungen öffnen", + "wordWrapMigration.prompt": "Die Einstellung \"editor.wrappingColumn\" ist veraltet und wurde durch \"editor.wordWrap\" ersetzt." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..1563cacf8b --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "Unterbrechen, wenn der Ausdruck als TRUE ausgewertet wird. EINGABETASTE zum Akzeptieren, ESC-TASTE zum Abbrechen.", + "breakpointWidgetAriaLabel": "Das Programm wird nur angehalten, wenn diese Bedingung erfüllt ist. Drücken Sie zum Akzeptieren die EINGABETASTE oder ESC, um den Vorgang abzubrechen.", + "breakpointWidgetHitCountPlaceholder": "Unterbrechen, wenn die Bedingung für die Trefferanzahl erfüllt ist. EINGABETASTE zum Akzeptieren, ESC-TASTE zum Abbrechen.", + "breakpointWidgetHitCountAriaLabel": "Das Programm wird nur angehalten, wenn die Bedingung für die Trefferanzahl erfüllt ist. Drücken Sie zum Akzeptieren die EINGABETASTE oder ESC, um den Vorgang abzubrechen.", + "expression": "Ausdruck", + "hitCount": "Trefferanzahl" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..fdeeb2eaa6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "Keine Konfigurationen", + "addConfigTo": "Konfiguration hinzufügen ({0})...", + "addConfiguration": "Konfiguration hinzufügen..." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..f6bf6a7948 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "{0} öffnen", + "launchJsonNeedsConfigurtion": "Konfigurieren oder reparieren Sie \"launch.json\".", + "noFolderDebugConfig": "Öffnen Sie bitte einen Ordner, um erweitertes Debuggen zu konfigurieren.", + "startDebug": "Debuggen starten", + "startWithoutDebugging": "Ohne Debuggen starten", + "selectAndStartDebugging": "Debugging auswählen und starten", + "restartDebug": "Neu starten", + "reconnectDebug": "Erneut verbinden", + "stepOverDebug": "Prozedurschritt", + "stepIntoDebug": "Einzelschritt", + "stepOutDebug": "Prozedurschritt", + "stopDebug": "Beenden", + "disconnectDebug": "Trennen", + "continueDebug": "Weiter", + "pauseDebug": "Anhalten", + "restartFrame": "Frame neu starten", + "removeBreakpoint": "Haltepunkt entfernen", + "removeAllBreakpoints": "Alle Haltepunkte entfernen", + "enableBreakpoint": "Haltepunkt aktivieren", + "disableBreakpoint": "Haltepunkt deaktivieren", + "enableAllBreakpoints": "Alle Haltepunkte aktivieren", + "disableAllBreakpoints": "Alle Haltepunkte deaktivieren", + "activateBreakpoints": "Haltepunkte aktivieren", + "deactivateBreakpoints": "Haltepunkte deaktivieren", + "reapplyAllBreakpoints": "Alle Haltepunkte erneut anwenden", + "addFunctionBreakpoint": "Funktionshaltepunkt hinzufügen", + "renameFunctionBreakpoint": "Funktionshaltepunkt umbenennen", + "addConditionalBreakpoint": "Bedingten Haltepunkt hinzufügen...", + "editConditionalBreakpoint": "Haltepunkt bearbeiten...", + "setValue": "Wert festlegen", + "addWatchExpression": "Ausdruck hinzufügen", + "editWatchExpression": "Ausdruck bearbeiten", + "addToWatchExpressions": "Zur Überwachung hinzufügen", + "removeWatchExpression": "Ausdruck entfernen", + "removeAllWatchExpressions": "Alle Ausdrücke entfernen", + "clearRepl": "Konsole löschen", + "debugConsoleAction": "Debugging-Konsole", + "unreadOutput": "Neue Ausgabe in Debugging-Konsole", + "debugFocusConsole": "Focus-Debugging-Konsole", + "focusProcess": "Fokus auf Prozess", + "stepBackDebug": "Schritt zurück", + "reverseContinue": "Umkehren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..534c7c00a4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "Hintergrundfarbe der Debug-Symbolleiste." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..a949134137 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "Die Ressource konnte ohne eine Debugsitzung nicht aufgelöst werden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..bd17d2ad00 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "Debuggen: Haltepunkt umschalten", + "columnBreakpointAction": "Debuggen: Spaltenhaltepunkt", + "columnBreakpoint": "Spaltenhaltepunkt hinzufügen", + "conditionalBreakpointEditorAction": "Debuggen: Bedingten Haltepunkt hinzufügen...", + "runToCursor": "Ausführen bis Cursor", + "debugEvaluate": "Debuggen: Auswerten", + "debugAddToWatch": "Debuggen: Zur Überwachung hinzufügen", + "showDebugHover": "Debuggen: Hover anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..d203a05de8 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "Deaktivierter Haltepunkt", + "breakpointUnverifieddHover": "Nicht überprüfter Haltepunkt", + "breakpointDirtydHover": "Nicht überprüfter Haltepunkt. Die Datei wurde geändert. Bitte starten Sie die Debugsitzung neu.", + "breakpointUnsupported": "Bedingte Haltepunkte werden für diesen Debugtyp nicht unterstützt." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..4ebf845228 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, Debugging", + "debugAriaLabel": "Geben Sie den Namen einer auszuführenden Startkonfiguration ein.", + "noConfigurationsMatching": "Keine übereinstimmenden Debugkonfigurationen", + "noConfigurationsFound": "Keine Debugkonfiguration gefunden. Erstellen Sie die Datei \"launch.json\"." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..22b6fb29db --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "Widget-Rahmenfarbe bei einer Ausnahme. ", + "debugExceptionWidgetBackground": "Widget-Hintergrundfarbe bei einer Ausnahme. ", + "exceptionThrownWithId": "Ausnahme: {0}", + "exceptionThrown": "Ausnahme aufgetreten." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..aba38ee503 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "Zum Öffnen klicken (CMD+KLICKEN für seitliche Anzeige)", + "fileLink": "Zum Öffnen klicken (STRG+KLICKEN für seitliche Anzeige)" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..c9ede1f4be --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "Steuert das Verhalten der internen Debugging-Konsole." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..df71ea6492 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "Nicht verfügbar", + "startDebugFirst": "Bitte starten Sie eine Debugsitzung, um die Auswertung vorzunehmen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..85bab1b356 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "Unbekannte Quelle" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..cbaa3cf7ef --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "Debuggen anzeigen", + "toggleDebugPanel": "Debugging-Konsole", + "debug": "Debuggen", + "debugPanel": "Debugging-Konsole", + "variables": "Variablen", + "watch": "Überwachen", + "callStack": "Aufrufliste", + "breakpoints": "Haltepunkte", + "view": "Anzeigen", + "debugCategory": "Debuggen", + "debugCommands": "Debugkonfiguration", + "debugConfigurationTitle": "Debuggen", + "allowBreakpointsEverywhere": "Ermöglicht das Festlegen von Haltepunkten für alle Dateien.", + "openExplorerOnEnd": "Hiermit wird am Ende einer Debugsitzung automatisch eine Explorer-Ansicht geöffnet.", + "inlineValues": "Variablenwerte beim Debuggen in Editor eingebunden anzeigen", + "hideActionBar": "Steuert, ob die unverankerte Debugaktionsleiste ausgeblendet werden soll", + "launch": "Startkonfiguration für globales Debuggen. Sollte als Alternative zu \"launch.json\" verwendet werden, das übergreifend von mehreren Arbeitsbereichen genutzt wird" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..1b65760c9e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "Öffnen Sie bitte einen Ordner, um erweitertes Debuggen zu konfigurieren." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..d311be125f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "Trägt Debugadapter bei.", + "vscode.extension.contributes.debuggers.type": "Der eindeutige Bezeichner für diese Debugadapter.", + "vscode.extension.contributes.debuggers.label": "Der Anzeigename für diese Debugadapter.", + "vscode.extension.contributes.debuggers.program": "Der Pfad zum Debugadapterprogramm. Der Pfad ist absolut oder relativ zum Erweiterungsordner.", + "vscode.extension.contributes.debuggers.args": "Optionale Argumente, die an den Adapter übergeben werden sollen.", + "vscode.extension.contributes.debuggers.runtime": "Optionale Laufzeit für den Fall, dass das Programmattribut keine ausführbare Datei ist und eine Laufzeit erfordert.", + "vscode.extension.contributes.debuggers.runtimeArgs": "Optionale Laufzeitargumente.", + "vscode.extension.contributes.debuggers.variables": "Eine Zuordnung von interaktiven Variablen (z. B. \"${action.pickProcess}\") in \"launch.json\" zu einem Befehl.", + "vscode.extension.contributes.debuggers.initialConfigurations": "Konfigurationen zum Generieren der anfänglichen Datei \"launch.json\".", + "vscode.extension.contributes.debuggers.languages": "Liste der Sprachen, für die die Debugerweiterung als \"Standarddebugger\" angesehen werden kann", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Wenn dies festgelegt ist, ruft der VS Code diesen Befehl auf, um den ausführbaren Pfad des Debugadapters und die zu übergebenden Argumente zu bestimmen.", + "vscode.extension.contributes.debuggers.startSessionCommand": "Wenn dies festgelegt ist, ruft der VS Code diesen Befehl für die Debug- oder Ausführungsaktionen aus, die für diese Erweiterung bestimmt sind.", + "vscode.extension.contributes.debuggers.configurationSnippets": "Snippets zum Hinzufügen neuer Konfigurationen in \"launch.json\".", + "vscode.extension.contributes.debuggers.configurationAttributes": "JSON-Schemakonfigurationen zum Überprüfen von \"launch.json\".", + "vscode.extension.contributes.debuggers.windows": "Windows-spezifische Einstellungen.", + "vscode.extension.contributes.debuggers.windows.runtime": "Die für Windows verwendete Laufzeit.", + "vscode.extension.contributes.debuggers.osx": "OS X-spezifische Einstellungen.", + "vscode.extension.contributes.debuggers.osx.runtime": "Die für OS X verwendete Laufzeit.", + "vscode.extension.contributes.debuggers.linux": "Linux-spezifische Einstellungen.", + "vscode.extension.contributes.debuggers.linux.runtime": "Die für Linux verwendete Laufzeit.", + "vscode.extension.contributes.breakpoints": "Trägt Haltepunkte bei.", + "vscode.extension.contributes.breakpoints.language": "Lässt Haltepunkte für diese Sprache zu.", + "app.launch.json.title": "Starten", + "app.launch.json.version": "Die Version dieses Dateiformats.", + "app.launch.json.configurations": "Die Liste der Konfigurationen. Fügen Sie neue Konfigurationen hinzu, oder bearbeiten Sie vorhandene Konfigurationen mit IntelliSense.", + "app.launch.json.compounds": "Liste der Verbundelemente. Jeder Verbund verweist auf mehrere Konfigurationen, die zusammen gestartet werden.", + "app.launch.json.compound.name": "Name des Verbunds. Wird im Dropdownmenü der Startkonfiguration angezeigt.", + "app.launch.json.compounds.configurations": "Namen von Konfigurationen, die als Bestandteil dieses Verbunds gestartet werden.", + "debugNoType": "Der \"type\" des Debugadapters kann nicht ausgelassen werden und muss vom Typ \"string\" sein.", + "selectDebug": "Umgebung auswählen", + "DebugConfig.failed": "Die Datei \"launch.json\" kann nicht im Ordner \".vscode\" erstellt werden ({0})." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..47884c6864 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "Haltepunkte entfernen", + "removeBreakpointOnColumn": "Haltepunkt in Spalte {0} entfernen", + "removeLineBreakpoint": "Zeilenhaltepunkt entfernen", + "editBreakpoints": "Haltepunkte bearbeiten", + "editBreakpointOnColumn": "Haltepunkt in Spalte {0} bearbeiten", + "editLineBrekapoint": "Zeilenhaltepunkt bearbeiten", + "enableDisableBreakpoints": "Haltepunkte aktivieren/deaktivieren", + "disableColumnBreakpoint": "Haltepunkt in Spalte {0} deaktivieren", + "disableBreakpointOnLine": "Zeilenhaltepunkt deaktivieren", + "enableBreakpoints": "Haltepunkt in Spalte {0} aktivieren", + "enableBreakpointOnLine": "Zeilenhaltepunkt aktivieren", + "addBreakpoint": "Haltepunkt hinzufügen", + "addConfiguration": "Konfiguration hinzufügen..." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..49d2e19f1c --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "Debughover" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..b67bfb601c --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "Nur primitive Werte werden für dieses Objekt angezeigt.", + "debuggingPaused": "Das Debuggen wurde angehalten. Ursache {0}, {1}{2}", + "debuggingStarted": "Das Debuggen wurde gestartet.", + "debuggingStopped": "Das Debuggen wurde beendet.", + "breakpointAdded": "Der Haltepunkt wurde hinzugefügt. Zeile {0}, Datei \"{1}\".", + "breakpointRemoved": "Der Haltepunkt wurde entfernt. Zeile {0}, Datei \"{1}\".", + "compoundMustHaveConfigurations": "Für den Verbund muss das Attribut \"configurations\" festgelegt werden, damit mehrere Konfigurationen gestartet werden können.", + "configMissing": "Konfiguration \"{0}\" fehlt in \"launch.json\".", + "debugTypeNotSupported": "Der konfigurierte Debugtyp \"{0}\" wird nicht unterstützt.", + "debugTypeMissing": "Fehlende Eigenschaft \"type\" für die ausgewählte Startkonfiguration.", + "preLaunchTaskErrors": "Buildfehler während preLaunchTask \"{0}\".", + "preLaunchTaskError": "Buildfehler während preLaunchTask \"{0}\".", + "preLaunchTaskExitCode": "Der preLaunchTask \"{0}\" wurde mit dem Exitcode {1} beendet.", + "debugAnyway": "Trotzdem debuggen", + "noFolderWorkspaceDebugError": "Debuggen der aktiven Datei ist nicht möglich. Stellen Sie sicher, dass sie auf einem Datenträger gespeichert ist und dass Sie die Debugerweiterung für diesen Dateityp installiert haben.", + "NewLaunchConfig": "Richten Sie die Startkonfigurationsdatei für Ihre Anwendung ein. {0}", + "DebugTaskNotFound": "Der preLaunchTask \"{0}\" wurde nicht gefunden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..f457bb5647 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "Prozess", + "paused": "Angehalten", + "running": "Wird ausgeführt", + "thread": "Thread", + "pausedOn": "Angehalten bei {0}", + "loadMoreStackFrames": "Weitere Stapelrahmen laden", + "threadAriaLabel": "Thread {0}, Aufrufliste, Debuggen", + "stackFrameAriaLabel": "Stapelrahmen {0} Zeile {1} {2}, Aufrufliste, Debuggen", + "variableValueAriaLabel": "Geben Sie einen neuen Variablenwert ein.", + "variableScopeAriaLabel": "Bereich {0}, Variablen, Debuggen", + "variableAriaLabel": "{0} Wert {1}, Variablen, Debuggen", + "watchExpressionPlaceholder": "Zu überwachender Ausdruck", + "watchExpressionInputAriaLabel": "Geben Sie den Überwachungsausdruck ein.", + "watchExpressionAriaLabel": "{0} Wert {1}, Überwachen, Debuggen", + "watchVariableAriaLabel": "{0} Wert {1}, Überwachen, Debuggen", + "functionBreakpointPlaceholder": "Funktion mit Haltepunkt", + "functionBreakPointInputAriaLabel": "Geben Sie den Funktionshaltepunkt ein.", + "functionBreakpointsNotSupported": "Funktionshaltepunkte werden von diesem Debugtyp nicht unterstützt.", + "breakpointAriaLabel": "Haltepunktzeile {0} {1}, Haltepunkte, Debuggen", + "functionBreakpointAriaLabel": "Funktionshaltepunkt {0}, Haltepunkte, Debuggen", + "exceptionBreakpointAriaLabel": "Ausnahmehaltepunkt {0}, Haltepunkte, Debuggen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..c9465f0f73 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "Variablenabschnitt", + "variablesAriaTreeLabel": "Variablen debuggen", + "expressionsSection": "Ausdrucksabschnitt", + "watchAriaTreeLabel": "Überwachungsausdrücke debuggen", + "callstackSection": "Aufruflistenabschnitt", + "debugStopped": "Angehalten bei {0}", + "callStackAriaLabel": "Aufrufliste debuggen", + "breakpointsSection": "Haltepunkteabschnitt", + "breakpointsAriaTreeLabel": "Haltepunkte debuggen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..c3275aa2e2 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "Wert kopieren", + "copy": "Kopieren", + "copyAll": "Alles kopieren", + "copyStackTrace": "Aufrufliste kopieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..38afead095 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "Weitere Informationen", + "unableToLaunchDebugAdapter": "Der Debugadapter kann nicht aus {0} gestartet werden.", + "unableToLaunchDebugAdapterNoArgs": "Debugadapter kann nicht gestartet werden.", + "stoppingDebugAdapter": "{0}. Der Debugadapter wird beendet.", + "debugAdapterCrash": "Der Debugadapterprozess wurde unerwartet beendet." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..9de49f4a96 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "REPL-Bereich (Read Eval Print-Loop)", + "actions.repl.historyPrevious": "Verlauf vorherige", + "actions.repl.historyNext": "Verlauf nächste", + "actions.repl.acceptInput": "REPL-Eingaben akzeptieren", + "actions.repl.copyAll": "Debuggen: Konsole – alle kopieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..fc5c5a19af --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "Der Objektstatus wird aus der ersten Auswertung erfasst.", + "replVariableAriaLabel": "Variable {0} besitzt den Wert {1}, Read Eval Print-Loop, Debuggen", + "replExpressionAriaLabel": "Ausdruck {0} besitzt den Wert {1}, Read Eval Print-Loop, Debuggen", + "replValueOutputAriaLabel": "{0}, Read Eval Print-Loop, Debuggen", + "replKeyValueOutputAriaLabel": "Ausgabevariable {0} besitzt den Wert {1}, Read Eval Print-Loop, Debuggen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..795d5614d4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "Hintergrundfarbe der Statusleiste beim Debuggen eines Programms. Die Statusleiste wird unten im Fenster angezeigt.", + "statusBarDebuggingForeground": "Vordergrundfarbe der Statusleiste beim Debuggen eines Programms. Die Statusleiste wird unten im Fenster angezeigt.", + "statusBarDebuggingBorder": "Rahmenfarbe der Statusleiste zur Abtrennung von der Randleiste und dem Editor, wenn ein Programm debuggt wird. Die Statusleiste wird unten im Fenster angezeigt." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..322ab71d7c --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "Zu debuggende Komponente", + "debug.terminal.not.available.error": "Integriertes Terminal nicht verfügbar" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..088cfc158b --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "Die ausführbare Datei \"{0}\" des Debugadapters ist nicht vorhanden.", + "debugAdapterCannotDetermineExecutable": "Die ausführbare Datei \"{0}\" des Debugadapters kann nicht bestimmt werden.", + "launch.config.comment1": "Verwendet IntelliSense zum Ermitteln möglicher Attribute.", + "launch.config.comment2": "Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.", + "launch.config.comment3": "Weitere Informationen finden Sie unter {0}", + "debugType": "Der Typ der Konfiguration.", + "debugTypeNotRecognised": "Dieser Debugging-Typ wurde nicht erkannt. Bitte installieren und aktivieren Sie die dazugehörige Debugging-Erweiterung.", + "node2NotSupported": "\"node2\" wird nicht mehr unterstützt, verwenden Sie stattdessen \"node\", und legen Sie das Attribut \"protocol\" auf \"inspector\" fest.", + "debugName": "Der Name der Konfiguration. Er wird im Dropdownmenü der Startkonfiguration angezeigt.", + "debugRequest": "Der Anforderungstyp der Konfiguration. Der Wert kann \"launch\" oder \"attach\" sein.", + "debugServer": "Nur für die Entwicklung von Debugerweiterungen: Wenn ein Port angegeben ist, versucht der VS-Code, eine Verbindung mit einem Debugadapter herzustellen, der im Servermodus ausgeführt wird.", + "debugPrelaunchTask": "Ein Task, der ausgeführt werden soll, bevor die Debugsitzung beginnt.", + "debugWindowsConfiguration": "Windows-spezifische Startkonfigurationsattribute.", + "debugOSXConfiguration": "OS X-spezifische Startkonfigurationsattribute.", + "debugLinuxConfiguration": "Linux-spezifische Startkonfigurationsattribute.", + "deprecatedVariables": "\"env.\", \"config.\" und \"command.\" sind veraltet, verwenden Sie stattdessen \"env:\", \"config:\" und \"command:\"." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..3604fe6aca --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "Emmet-Befehle anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..46a2e9c250 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: Ausgleichen (einwärts)", + "balanceOutward": "Emmet: Ausgleichen (auswärts)" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..283db2a921 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: Zum vorherigen Bearbeitungspunkt wechseln", + "nextEditPoint": "Emmet: Zum nächsten Bearbeitungspunkt wechseln" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..1c36df5441 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: Mathematischen Ausdruck auswerten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..ac1c3eee63 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: Abkürzung erweitern" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..6b09612a80 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: Um 0,1 inkrementieren", + "incrementNumberByOne": "Emmet: Um 1 inkrementieren", + "incrementNumberByTen": "Emmet: Um 10 inkrementieren", + "decrementNumberByOneTenth": "Emmet: Um 0,1 dekrementieren", + "decrementNumberByOne": "Emmet: Um 1 dekrementieren", + "decrementNumberByTen": "Emmet: Um 10 dekrementieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..1a90c34cee --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: Gehe zu übereinstimmendem Paar" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..9fcd005df1 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: Zeilen mergen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..0cb4c14c8f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: CSS-Wert reflektieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..03a84f4fff --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: Tag entfernen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..1c325c755d --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: Vorheriges Element auswählen", + "selectNextItem": "Emmet: Nächstes Element auswählen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..1e73a3262e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: Tag teilen/verknüpfen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..6bbe22a53f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: Kommentar umschalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..04f31979c4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: Bildgröße aktualisieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..582b014e92 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: Tag aktualisieren", + "enterTag": "Tag eingeben", + "tag": "Tag" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..4fe913fb48 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: Mit Abkürzung umschließen", + "enterAbbreviation": "Abkürzung eingeben", + "abbreviation": "Abkürzung" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..11bf0a5473 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Wenn diese Option aktiviert ist, werden Emmet-Abkürzungen beim Drücken der TAB-TASTE erweitert. Nicht zutreffend, wenn emmet.useNewemmet auf \"true\" festgelegt ist.", + "emmetPreferences": "Einstellungen, die zum Ändern des Verhaltens einiger Aktionen und Resolver von Emmet verwendet werden. Nicht zutreffend, wenn emmet.useNewemmet auf \"true\" festgelegt ist.", + "emmetSyntaxProfiles": "Definieren Sie das Profil für die angegebene Syntax, oder verwenden Sie Ihr eigenes Profil mit bestimmten Regeln.", + "emmetExclude": "Ein Array von Sprachen, in dem Emmet-Abkürzungen nicht erweitert werden sollen.", + "emmetExtensionsPath": "Pfad zu einem Ordner mit Emmet-Profilen, -Ausschnitten und -Einstellungen. Wenn emmet.useNewEmmet auf \"true\" festgelegt ist, werden nur Profile vom Erweiterungspfad berücksichtigt.", + "useNewEmmet": "Testen Sie die neuen Emmet-Module (die letztendlich die alte einzelne Emmet-Bibliothek ersetzen) für alle Emmet-Funktionen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json new file mode 100644 index 0000000000..46a2e9c250 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: Ausgleichen (einwärts)", + "balanceOutward": "Emmet: Ausgleichen (auswärts)" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json new file mode 100644 index 0000000000..e5c1f7fc85 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: Vorheriger Bearbeitungspunkt", + "nextEditPoint": "Emmet: Nächster Bearbeitungspunkt" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..1c36df5441 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: Mathematischen Ausdruck auswerten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..ac1c3eee63 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: Abkürzung erweitern" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..6b09612a80 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: Um 0,1 inkrementieren", + "incrementNumberByOne": "Emmet: Um 1 inkrementieren", + "incrementNumberByTen": "Emmet: Um 10 inkrementieren", + "decrementNumberByOneTenth": "Emmet: Um 0,1 dekrementieren", + "decrementNumberByOne": "Emmet: Um 1 dekrementieren", + "decrementNumberByTen": "Emmet: Um 10 dekrementieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..1a90c34cee --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: Gehe zu übereinstimmendem Paar" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..9fcd005df1 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: Zeilen mergen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..0cb4c14c8f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: CSS-Wert reflektieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json new file mode 100644 index 0000000000..03a84f4fff --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: Tag entfernen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json new file mode 100644 index 0000000000..1c325c755d --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: Vorheriges Element auswählen", + "selectNextItem": "Emmet: Nächstes Element auswählen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..1e73a3262e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: Tag teilen/verknüpfen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..6bbe22a53f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: Kommentar umschalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..04f31979c4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: Bildgröße aktualisieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json new file mode 100644 index 0000000000..582b014e92 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: Tag aktualisieren", + "enterTag": "Tag eingeben", + "tag": "Tag" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..4fe913fb48 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: Mit Abkürzung umschließen", + "enterAbbreviation": "Abkürzung eingeben", + "abbreviation": "Abkürzung" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json new file mode 100644 index 0000000000..d0268f68c9 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Wenn aktiviert, werden Emmet-Abkürzungen beim Drücken der TAB-TASTE erweitert.", + "emmetPreferences": "Einstellungen, die zum Ändern des Verhaltens einiger Aktionen und Konfliktlöser von Emmet verwendet werden.", + "emmetSyntaxProfiles": "Definieren Sie das Profil für die angegebene Syntax, oder verwenden Sie Ihr eigenes Profil mit bestimmten Regeln.", + "emmetExclude": "Ein Array von Sprachen, in dem Emmet-Abkürzungen nicht erweitert werden sollen.", + "emmetExtensionsPath": "Pfad zu einem Ordner mit Emmet-Profilen, Ausschnitten und Voreinstellungen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..9272c58cb4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Externes Terminal", + "explorer.openInTerminalKind": "Passt an, welches Terminal ausgeführt werden soll.", + "terminal.external.windowsExec": "Passt an, welches Terminal für Windows ausgeführt werden soll.", + "terminal.external.osxExec": "Passt an, welche Terminalanwendung unter OS X ausgeführt werden soll.", + "terminal.external.linuxExec": "Passt an, welches Terminal unter Linux ausgeführt werden soll.", + "globalConsoleActionWin": "Neue Eingabeaufforderung öffnen", + "globalConsoleActionMacLinux": "Neues Terminal öffnen", + "scopedConsoleActionWin": "In Eingabeaufforderung öffnen", + "scopedConsoleActionMacLinux": "In Terminal öffnen", + "openFolderInIntegratedTerminal": "In Terminal öffnen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..1abee0b760 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Externes Terminal", + "terminal.external.windowsExec": "Passt an, welches Terminal für Windows ausgeführt werden soll.", + "terminal.external.osxExec": "Passt an, welche Terminalanwendung unter OS X ausgeführt werden soll.", + "terminal.external.linuxExec": "Passt an, welches Terminal unter Linux ausgeführt werden soll.", + "globalConsoleActionWin": "Neue Eingabeaufforderung öffnen", + "globalConsoleActionMacLinux": "Neues Terminal öffnen", + "scopedConsoleActionWin": "In Eingabeaufforderung öffnen", + "scopedConsoleActionMacLinux": "In Terminal öffnen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/deu/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..6378ef3146 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "VS Code-Konsole", + "mac.terminal.script.failed": "Fehler bei Skript \"{0}\" mit Exitcode {1}.", + "mac.terminal.type.not.supported": "\"{0}\" wird nicht unterstützt.", + "press.any.key": "Drücken Sie eine beliebige Taste, um fortzufahren...", + "linux.term.failed": "Fehler bei \"{0}\" mit Exitcode {1}." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..ef9e20d0a8 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "Trägt eine benutzerdefinierte Ansicht bei", + "vscode.extension.contributes.view.id": "Eindeutige ID zum Identifizieren der über vscode.workspace.createTreeView erstellten Ansicht", + "vscode.extension.contributes.view.label": "Visuell lesbare Zeichenfolge zum Rendern der Ansicht", + "vscode.extension.contributes.view.icon": "Pfad zum Ansichtssymbol", + "vscode.extension.contributes.views": "Trägt benutzerdefinierte Ansichten bei", + "showViewlet": "{0} anzeigen", + "view": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..3c997db71b --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "Aktualisieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..e0bf4c8caf --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "Es ist kein TreeExplorerNodeProvider mit ID \"{providerId}\" registriert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/deu/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..7e03963c2b --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "Tree Explorer-Abschnitt" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..b83fbb4464 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "Fehler", + "Unknown Dependency": "Unbekannte Abhängigkeit:" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..62df1773d4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "Erweiterungsname", + "extension id": "Erweiterungsbezeichner", + "publisher": "Name des Herausgebers", + "install count": "Installationsanzahl", + "rating": "Bewertung", + "license": "Lizenz", + "details": "Details", + "contributions": "Beiträge", + "changelog": "ChangeLog", + "dependencies": "Abhängigkeiten", + "noReadme": "Keine INFODATEI verfügbar.", + "noChangelog": "Es ist kein ChangeLog verfügbar.", + "noContributions": "Keine Beiträge", + "noDependencies": "Keine Abhängigkeiten", + "settings": "Einstellungen ({0})", + "setting name": "Name", + "description": "Beschreibung", + "default": "Standard", + "debuggers": "Debugger ({0})", + "debugger name": "Name", + "debugger type": "Typ", + "views": "Ansichten ({0})", + "view id": "ID", + "view name": "Name", + "view location": "Wo", + "themes": "Designs ({0})", + "JSON Validation": "JSON-Validierung ({0})", + "commands": "Befehle ({0})", + "command name": "Name", + "keyboard shortcuts": "Tastenkombinationen", + "menuContexts": "Menükontexte", + "languages": "Sprachen ({0})", + "language id": "ID", + "language name": "Name", + "file extensions": "Dateierweiterungen", + "grammar": "Grammatik", + "snippets": "Codeausschnitte" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..2958d6d6be --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "Installieren", + "installing": "Wird installiert.", + "uninstallAction": "Deinstallieren", + "Uninstalling": "Wird deinstalliert", + "updateAction": "Aktualisieren", + "updateTo": "Auf \"{0}\" aktualisieren", + "enableForWorkspaceAction.label": "Aktivieren (Arbeitsbereich)", + "enableAlwaysAction.label": "Aktivieren (immer)", + "disableForWorkspaceAction.label": "Deaktivieren (Arbeitsbereich)", + "disableAlwaysAction.label": "Deaktivieren (immer)", + "ManageExtensionAction.uninstallingTooltip": "Wird deinstalliert", + "enableForWorkspaceAction": "Arbeitsbereich", + "enableGloballyAction": "Immer", + "enableAction": "Aktivieren", + "disableForWorkspaceAction": "Arbeitsbereich", + "disableGloballyAction": "Immer", + "disableAction": "Deaktivieren", + "checkForUpdates": "Nach Updates suchen", + "enableAutoUpdate": "Aktivere die automatische Aktualisierung von Erweiterungen", + "disableAutoUpdate": "Deaktivere die automatische Aktualisierung von Erweiterungen", + "updateAll": "Alle Erweiterungen aktualisieren", + "reloadAction": "Neu starten", + "postUpdateTooltip": "Zum Aktualisieren erneut laden", + "postUpdateMessage": "Dieses Fenster erneut laden, um die aktualisierte Erweiterung \"{0}\" zu aktivieren?", + "postEnableTooltip": "Zum Aktivieren erneut laden", + "postEnableMessage": "Dieses Fenster erneut laden, um die Erweiterung \"{0}\" zu aktivieren?", + "postDisableTooltip": "Zum Deaktivieren erneut laden", + "postDisableMessage": "Dieses Fenster erneut laden, um die Erweiterung \"{0}\" zu deaktivieren?", + "postUninstallTooltip": "Zum Deaktivieren erneut laden", + "postUninstallMessage": "Dieses Fenster erneut laden, um die deinstallierte Erweiterung \"{0}\" zu deaktivieren?", + "reload": "Fenster &&erneut laden", + "toggleExtensionsViewlet": "Erweiterungen anzeigen", + "installExtensions": "Erweiterungen installieren", + "showEnabledExtensions": "Aktivierte Erweiterungen anzeigen", + "showInstalledExtensions": "Installierte Erweiterungen anzeigen", + "showDisabledExtensions": "Deaktivierte Erweiterungen anzeigen", + "clearExtensionsInput": "Extensioneingabe löschen", + "showOutdatedExtensions": "Veraltete Erweiterungen anzeigen", + "showPopularExtensions": "Beliebte Erweiterungen anzeigen", + "showRecommendedExtensions": "Empfohlene Erweiterungen anzeigen", + "showWorkspaceRecommendedExtensions": "Für den Arbeitsbereich empfohlene Erweiterungen anzeigen", + "showRecommendedKeymapExtensions": "Empfohlene Tastenzuordnungen anzeigen", + "showRecommendedKeymapExtensionsShort": "Tastenzuordnungen", + "showLanguageExtensions": "Spracherweiterungen anzeigen", + "showLanguageExtensionsShort": "Spracherweiterungen", + "showAzureExtensions": "Azure-Erweiterungen anzeigen", + "showAzureExtensionsShort": "Azure-Erweiterungen", + "configureWorkspaceRecommendedExtensions": "Empfohlene Erweiterungen konfigurieren (Arbeitsbereich)", + "ConfigureWorkspaceRecommendations.noWorkspace": "Empfehlungen sind nur für einen Arbeitsbereichsordner verfügbar.", + "OpenExtensionsFile.failed": "Die Datei \"extensions.json\" kann nicht im Ordner \".vscode\" erstellt werden ({0}).", + "builtin": "Integriert", + "disableAll": "Alle installierten Erweiterungen löschen", + "disableAllWorkspace": "Alle installierten Erweiterungen für diesen Arbeitsbereich deaktivieren", + "enableAll": "Alle installierten Erweiterungen aktivieren", + "enableAllWorkspace": "Alle installierten Erweiterungen für diesen Arbeitsbereich aktivieren", + "extensionButtonProminentBackground": "Hintergrundfarbe für markante Aktionenerweiterungen (z. B. die Schaltfläche zum Installieren).", + "extensionButtonProminentForeground": "Vordergrundfarbe für markante Aktionenerweiterungen (z. B. die Schaltfläche zum Installieren).", + "extensionButtonProminentHoverBackground": "Hoverhintergrundfarbe für markante Aktionenerweiterungen (z. B. die Schaltfläche zum Installieren)." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..aa058e017c --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "Drücken Sie die EINGABETASTE, um Ihre Erweiterungen zu verwalten.", + "searchFor": "Drücken Sie die EINGABETASTE, um nach \"{0}\" in Marketplace zu suchen.", + "noExtensionsToInstall": "Geben Sie einen Erweiterungsnamen ein." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..507a9fe3b3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "Erweiterungen", + "app.extensions.json.recommendations": "Liste der Erweiterungsempfehlungen. Der Bezeichner einer Erweiterung lautet immer \"${publisher}.${name}\". Beispiel: \"vscode.csharp\".", + "app.extension.identifier.errorMessage": "Erwartetes Format: \"${publisher}.${name}\". Beispiel: \"vscode.csharp\"." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..545a0f7776 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "Erweiterung: {0}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..eb886d8bdd --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "Für diesen Dateityp wird die Erweiterung \"{0}\" empfohlen.", + "showRecommendations": "Empfehlungen anzeigen", + "neverShowAgain": "Nicht mehr anzeigen", + "close": "Schließen", + "workspaceRecommended": "Für diesen Arbeitsbereich sind Erweiterungsempfehlungen verfügbar.", + "ignoreExtensionRecommendations": "Möchten Sie alle Erweiterungsempfehlungen ignorieren?", + "ignoreAll": "Ja, alles ignorieren", + "no": "Nein", + "cancel": "Abbrechen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..ef1eef96fa --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "Erweiterungen verwalten", + "galleryExtensionsCommands": "Katalogerweiterungen installieren", + "extension": "Erweiterung", + "extensions": "Erweiterungen", + "view": "Anzeigen", + "extensionsConfigurationTitle": "Erweiterungen", + "extensionsAutoUpdate": "Erweiterungen automatisch aktualisieren", + "extensionsIgnoreRecommendations": "Erweiterungsempfehlungen ignorieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..bc48f59dcc --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "Erweiterungsordner öffnen", + "installVSIX": "Aus VSIX installieren...", + "InstallVSIXAction.success": "Die Erweiterung wurde erfolgreich installiert. Führen Sie einen Neustart aus, um sie zu aktivieren.", + "InstallVSIXAction.reloadNow": "Jetzt erneut laden" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..29a07ed267 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Deaktivere Tastenzuordnungen ({0}) um Konfilkte mit anderen zu vermeiden?", + "yes": "Ja", + "no": "Nein", + "betterMergeDisabled": "Die \"Better Merge\" Erweiterung ist jetzt integriert, die alte Erweiterung wurde deaktiviert und kann deinstalliert werden.", + "uninstall": "Deinstallieren", + "later": "Später" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..e6f69a3088 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "Marketplace", + "installedExtensions": "Installiert", + "searchInstalledExtensions": "Installiert", + "recommendedExtensions": "Empfohlen", + "searchExtensions": "Nach Erweiterungen im Marketplace suchen", + "sort by installs": "Sortieren nach: Installationsanzahl", + "sort by rating": "Sortieren nach: Bewertung", + "sort by name": "Sortieren nach: Name", + "suggestProxyError": "Marketplace hat \"ECONNREFUSED\" zurückgegeben. Überprüfen Sie die Einstellung \"http.proxy\".", + "extensions": "Erweiterungen", + "outdatedExtensions": "{0} veraltete Erweiterungen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..6be576e717 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Erweiterungen", + "no extensions found": "Es wurden keine Erweiterungen gefunden.", + "suggestProxyError": "Marketplace hat \"ECONNREFUSED\" zurückgegeben. Überprüfen Sie die Einstellung \"http.proxy\"." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json new file mode 100644 index 0000000000..06cf392b57 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Andere Tastenzuordnungen deaktivieren, um Konflikte zwischen Tastenzuordnungen zu vermeiden?", + "yes": "Ja", + "no": "Nein" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..395aec4e83 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "Durch Aktivieren von \"{0}\" werden auch die dazugehörigen Abhängigkeiten aktiviert. Möchten Sie fortfahren?", + "enable": "Ja", + "doNotEnable": "Nein", + "disableDependeciesConfirmation": "Möchten Sie nur \"{0}\" oder auch die dazugehörigen Abhängigkeiten deaktivieren?", + "disableOnly": "Nur", + "disableAll": "Alle", + "cancel": "Abbrechen", + "singleDependentError": "Die Erweiterung \"{0}\" kann nicht deaktiviert werden. Die Erweiterung \"{1}\" ist davon abhängig.", + "twoDependentsError": "Die Erweiterung \"{0}\" kann nicht deaktiviert werden. Die Erweiterungen \"{1}\" und \"{2}\" sind davon abhängig.", + "multipleDependentsError": "Die Erweiterung \"{0}\" kann nicht deaktiviert werden. Die Erweiterungen \"{1}\", \"{2}\" und andere sind davon abhängig.", + "installConfirmation": "Möchten sie die Erweiterung \"{0}\" installieren?", + "install": "Installieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/deu/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..719024b0c4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "Feedback als Tweet senden", + "label.sendASmile": "Senden Sie uns Ihr Feedback als Tweet.", + "patchedVersion1": "Ihre Installation ist beschädigt.", + "patchedVersion2": "Geben Sie diese Information an, wenn Sie einen Fehler melden.", + "sentiment": "Welche Erfahrungen haben Sie gemacht?", + "smileCaption": "Glücklich", + "frownCaption": "Traurig", + "other ways to contact us": "Weitere Möglichkeiten der Kontaktaufnahme", + "submit a bug": "Fehler senden", + "request a missing feature": "Fehlendes Feature anfordern", + "tell us why?": "Warum?", + "commentsHeader": "Kommentare", + "tweet": "Tweet", + "character left": "verbleibende Zeichen", + "characters left": "verbleibende Zeichen", + "feedbackSending": "Wird gesendet.", + "feedbackSent": "Vielen Dank", + "feedbackSendingError": "Wiederholen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..af681852d6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "Binärdateianzeige" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..5580fb5c8f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "Textdatei-Editor", + "createFile": "Datei erstellen", + "fileEditorWithInputAriaLabel": "{0}. Textdatei-Editor.", + "fileEditorAriaLabel": "Textdatei-Editor" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..e012ee7844 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "Ordner" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..6ac07f9b3c --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "Dateien", + "revealInSideBar": "In Seitenleiste anzeigen", + "acceptLocalChanges": "Änderungen verwenden und Datenträgerinhalte überschreiben", + "revertLocalChanges": "Änderungen verwerfen und Datenträgerinhalte wiederherstellen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..7694e4188a --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "Wiederholen", + "rename": "Umbenennen", + "newFile": "Neue Datei", + "newFolder": "Neuer Ordner", + "openFolderFirst": "Öffnet zuerst einen Ordner, in dem Dateien oder Ordner erstellt werden.", + "newUntitledFile": "Neue unbenannte Datei", + "createNewFile": "Neue Datei", + "createNewFolder": "Neuer Ordner", + "deleteButtonLabelRecycleBin": "&&In Papierkorb verschieben", + "deleteButtonLabelTrash": "&&In Papierkorb verschieben", + "deleteButtonLabel": "&&Löschen", + "dirtyMessageFolderOneDelete": "Sie löschen einen Ordner mit nicht gespeicherten Änderungen in einer Datei. Möchten Sie den Vorgang fortsetzen?", + "dirtyMessageFolderDelete": "Sie löschen einen Ordner mit nicht gespeicherten Änderungen in {0} Dateien. Möchten Sie den Vorgang fortsetzen?", + "dirtyMessageFileDelete": "Sie löschen eine Datei mit nicht gespeicherten Änderungen. Möchten Sie den Vorgang fortsetzen?", + "dirtyWarning": "Ihre Änderungen gehen verloren, wenn Sie diese nicht speichern.", + "confirmMoveTrashMessageFolder": "Möchten Sie \"{0}\" samt Inhalt wirklich löschen?", + "confirmMoveTrashMessageFile": "Möchten Sie \"{0}\" wirklich löschen?", + "undoBin": "Die Wiederherstellung kann aus dem Papierkorb erfolgen.", + "undoTrash": "Die Wiederherstellung kann aus dem Papierkorb erfolgen.", + "confirmDeleteMessageFolder": "Möchten Sie \"{0}\" samt Inhalt wirklich endgültig löschen?", + "confirmDeleteMessageFile": "Möchten Sie \"{0}\" wirklich endgültig löschen?", + "irreversible": "Diese Aktion kann nicht rückgängig gemacht werden.", + "permDelete": "Endgültig löschen", + "delete": "Löschen", + "importFiles": "Dateien importieren", + "confirmOverwrite": "Im Zielordner ist bereits eine Datei oder ein Ordner mit dem gleichen Namen vorhanden. Möchten Sie sie bzw. ihn ersetzen?", + "replaceButtonLabel": "&&Ersetzen", + "copyFile": "Kopieren", + "pasteFile": "Einfügen", + "duplicateFile": "Duplikat", + "openToSide": "Zur Seite öffnen", + "compareSource": "Für Vergleich auswählen", + "globalCompareFile": "Aktive Datei vergleichen mit...", + "pickHistory": "Zuvor geöffnete Datei für den Vergleich auswählen", + "unableToFileToCompare": "Die ausgewählte Datei kann nicht mit \"{0}\" verglichen werden.", + "openFileToCompare": "Zuerst eine Datei öffnen, um diese mit einer anderen Datei zu vergleichen", + "compareWith": "'{0}' mit '{1}' vergleichen", + "compareFiles": "Dateien vergleichen", + "refresh": "Aktualisieren", + "save": "Speichern", + "saveAs": "Speichern unter...", + "saveAll": "Alle speichern", + "saveAllInGroup": "Alle in der Gruppe speichern", + "saveFiles": "Geänderte Dateien speichern", + "revert": "Datei wiederherstellen", + "focusOpenEditors": "Fokus auf Ansicht \"Geöffnete Editoren\"", + "focusFilesExplorer": "Fokus auf Datei-Explorer", + "showInExplorer": "Aktive Datei in Seitenleiste anzeigen", + "openFileToShow": "Öffnet zuerst eine Datei, um sie im Explorer anzuzeigen.", + "collapseExplorerFolders": "Ordner im Explorer zuklappen", + "refreshExplorer": "Explorer aktualisieren", + "openFile": "Datei öffnen...", + "openFileInNewWindow": "Aktive Datei in neuem Fenster öffnen", + "openFileToShowInNewWindow": "Datei zuerst öffnen, um sie in einem neuen Fenster zu öffnen", + "revealInWindows": "Im Explorer anzeigen", + "revealInMac": "Im Finder anzeigen", + "openContainer": "Enthaltenden Ordner öffnen", + "revealActiveFileInWindows": "Aktive Datei im Windows-Explorer anzeigen", + "revealActiveFileInMac": "Aktive Datei im Finder anzeigen", + "openActiveFileContainer": "Enthaltenden Ordner der aktiven Datei öffnen", + "copyPath": "Pfad kopieren", + "copyPathOfActive": "Pfad der aktiven Datei kopieren", + "emptyFileNameError": "Es muss ein Datei- oder Ordnername angegeben werden.", + "fileNameExistsError": "Eine Datei oder ein Ordner **{0}** ist an diesem Ort bereits vorhanden. Wählen Sie einen anderen Namen.", + "invalidFileNameError": "Der Name **{0}** ist als Datei- oder Ordnername ungültig. Bitte wählen Sie einen anderen Namen aus.", + "filePathTooLongError": "Der Name **{0}** führt zu einem Pfad, der zu lang ist. Wählen Sie einen kürzeren Namen.", + "compareWithSaved": "Aktive Datei mit gespeicherter Datei vergleichen", + "modifiedLabel": "{0} (auf Datenträger) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..f48900f72f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "Datei zuerst öffnen, um ihren Pfad zu kopieren", + "openFileToReveal": "Datei zuerst öffnen, um sie anzuzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..3e197b066e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "Explorer anzeigen", + "explore": "Explorer", + "view": "Anzeigen", + "textFileEditor": "Textdatei-Editor", + "binaryFileEditor": "Binärdatei-Editor", + "filesConfigurationTitle": "Dateien", + "exclude": "Konfiguriert die Globmuster zum Ausschließen von Dateien und Ordnern.", + "files.exclude.boolean": "Das Globmuster, mit dem Dateipfade verglichen werden sollen. Legen Sie diesen Wert auf \"true\" oder \"false\" fest, um das Muster zu aktivieren bzw. zu deaktivieren.", + "files.exclude.when": "Zusätzliche Überprüfung der gleichgeordneten Elemente einer entsprechenden Datei. Verwenden Sie \"$(basename)\" als Variable für den entsprechenden Dateinamen.", + "associations": "Konfigurieren Sie Dateizuordnungen zu Sprachen (beispielsweise \"*.extension\": \"html\"). Diese besitzen Vorrang vor den Standardzuordnungen der installierten Sprachen.", + "encoding": "Die Standardzeichensatz-Codierung, die beim Lesen und Schreiben von Dateien verwendet werden soll.", + "autoGuessEncoding": "Ist diese Option aktiviert, wird beim Öffnen von Dateien versucht, die Zeichensatzcodierung automatisch zu ermitteln.", + "eol": "Das Zeilenende-Standardzeichen. Verwenden Sie \\n für LF und \\r\\n für CRLF.", + "trimTrailingWhitespace": "Bei Aktivierung werden nachgestellte Leerzeichen beim Speichern einer Datei gekürzt.", + "insertFinalNewline": "Bei Aktivierung wird beim Speichern einer Datei eine abschließende neue Zeile am Dateiende eingefügt.", + "files.autoSave.off": "Eine geänderte Datei wird nie automatisch gespeichert.", + "files.autoSave.afterDelay": "Eine geänderte Datei wird automatisch nach der konfigurierten \"files.autoSaveDelay\" gespeichert.", + "files.autoSave.onFocusChange": "Eine geänderte Datei wird automatisch gespeichert, wenn der Editor den Fokus verliert.", + "files.autoSave.onWindowChange": "Eine geänderte Datei wird automatisch gespeichert, wenn das Fenster den Fokus verliert.", + "autoSave": "Steuert die automatische Speicherung geänderter Dateien. Zulässige Werte: \"{0}\", \"{1}\", \"{2}\" (Editor verliert den Fokus), \"{3}\" (Fenster verliert den Fokus). Wenn diese Angabe auf \"{4}\" festgelegt ist, können Sie die Verzögerung in \"files.autoSaveDelay\" konfigurieren.", + "autoSaveDelay": "Steuert die Verzögerung in Millisekunden, nach der eine geänderte Datei automatisch gespeichert wird. Nur gültig, wenn \"files.autoSave\" auf \"{0}\" festgelegt ist.", + "watcherExclude": "Konfigurieren Sie Globmuster von Dateipfaden, die von der Dateiüberwachung ausgeschlossen werden sollen. Muster müssen in absoluten Pfaden übereinstimmen (d. h. für eine korrekte Überstimmung muss das Präfix ** oder der vollständige Pfad verwendet werden). Das Ändern dieser Einstellung erfordert einen Neustart. Wenn Ihr Code beim Start viel CPU-Zeit beansprucht, können Sie große Ordner ausschließen, um die anfängliche Last zu verringern.", + "hotExit.off": "Hot Exit deaktivieren.", + "hotExit.onExit": "Hot Exit wird beim Schließen der Anwendung ausgelöst, d. h. wenn unter Windows/Linux das letzte Fenster geschlossen wird oder wenn der Befehl \"workbench.action.quit\" ausgelöst wird (Befehlspalette, Tastenzuordnung, Menü). Alle Fenster mit Sicherungen werden beim nächsten Start wiederhergestellt.", + "hotExit.onExitAndWindowClose": "Hot Exit wird beim Schließen der Anwendung, d. h. wenn unter Windows/Linux das letzte Fenster geschlossen wird, oder beim Auslösen des Befehls \"workbench.action.quit\" (Befehlspalette, Tastenzuordnung, Menü) sowie für jedes Fenster mit einem geöffneten Ordner ausgelöst, unabhängig davon, ob es das letzte Fenster ist. Alle Fenster ohne geöffnete Ordner werden beim nächsten Start wiederhergestellt. Legen Sie window.restoreWindows auf \"all\" fest, um Ordnerfenster im Zustand vor dem Herunterfahren wiederherzustellen. ", + "hotExit": "Steuert, ob nicht gespeicherten Dateien zwischen den Sitzungen beibehlten werden, die Aufforderung zum Speichern wird beim Beenden des Editors übersprungen.", + "useExperimentalFileWatcher": "Verwenden Sie die neue experimentelle Dateiüberwachung.", + "defaultLanguage": "Der Standardsprachmodus, der neuen Dateien zugewiesen wird.", + "editorConfigurationTitle": "Editor", + "formatOnSave": "Hiermit wird eine Datei beim Speichern formatiert. Es muss ein Formatierer vorhanden sein, die Datei darf nicht automatisch gespeichert werden, und der Editor darf nicht geschlossen werden.", + "explorerConfigurationTitle": "Datei-Explorer", + "openEditorsVisible": "Die Anzahl der Editoren, die im Bereich \"Geöffnete Editoren\" angezeigt werden. Legen Sie diesen Wert auf 0 fest, um den Bereich auszublenden.", + "dynamicHeight": "Steuert, ob sich die Höhe des Abschnitts \"Geöffnete Editoren\" dynamisch an die Anzahl der Elemente anpassen soll.", + "autoReveal": "Steuert, ob der Explorer Dateien beim Öffnen automatisch anzeigen und auswählen soll.", + "enableDragAndDrop": "Steuert, ob der Explorer das Verschieben von Dateien und Ordnern mithilfe von Drag Drop zulassen soll.", + "sortOrder.default": "Dateien und Ordner werden nach ihren Namen in alphabetischer Reihenfolge sortiert. Ordner werden vor Dateien angezeigt. ", + "sortOrder.mixed": "Dateien und Ordner werden nach ihren Namen in alphabetischer Reihenfolge sortiert. Dateien und Ordner werden vermischt angezeigt.", + "sortOrder.filesFirst": "Dateien und Ordner werden nach ihren Namen in alphabetischer Reihenfolge sortiert. Dateien werden vor Ordnern angezeigt.", + "sortOrder.type": "Dateien und Ordner werden nach ihren Erweiterungen in alphabetischer Reihenfolge sortiert. Ordner werden vor Dateien angezeigt.", + "sortOrder.modified": "Dateien und Ordner werden nach dem letzten Änderungsdatum in absteigender Reihenfolge sortiert. Ordner werden vor Dateien angezeigt.", + "sortOrder": "Steuert die Sortierreihenfolge von Dateien und Ordnern im Explorer. Zusätzlich zur Standardsortierreihenfolge können Sie die Reihenfolge auf \"mixed\" (kombinierte Sortierung von Dateien und Ordnern), \"type\" (nach Dateityp), \"modified\" (nach letztem Änderungsdatum) oder \"filesFirst\" (Dateien vor Ordnern anzeigen) festlegen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..279dfa6cef --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "Verwerfen", + "overwrite": "Überschreiben", + "retry": "Wiederholen", + "readonlySaveError": "Fehler beim Speichern von \"{0}\": Die Datei ist schreibgeschützt. Wählen Sie 'Überschreiben' aus, um den Schutz aufzuheben.", + "genericSaveError": "Fehler beim Speichern von \"{0}\": {1}.", + "staleSaveError": "Fehler beim Speichern von \"{0}\": Der Inhalt auf dem Datenträger ist neuer. Klicken Sie auf **Vergleichen**, um Ihre Version mit der Version auf dem Datenträger zu vergleichen.", + "compareChanges": "Vergleichen", + "saveConflictDiffLabel": "{0} (auf Datenträger) ↔ {1} (in {2}): Speicherkonflikt lösen", + "userGuide": "Verwenden Sie die Aktionen auf der Editor-Symbolleiste auf der rechten Seite, um Ihre Änderungen **rückgängig zu machen** oder den Inhalt auf dem Datenträger mit Ihren Änderungen zu **überschreiben**." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..81f537ec70 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Es ist kein Ordner geöffnet.", + "explorerSection": "Datei-Explorer-Abschnitt", + "noWorkspaceHelp": "Sie haben noch keinen Ordner geöffnet.", + "openFolder": "Ordner öffnen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..31c60034ed --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "Datei-Explorer-Abschnitt", + "treeAriaLabel": "Datei-Explorer" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..f118d32214 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "Geben Sie den Dateinamen ein. Drücken Sie zur Bestätigung die EINGABETASTE oder ESC, um den Vorgang abzubrechen.", + "filesExplorerViewerAriaLabel": "{0}, Datei-Explorer", + "dropFolders": "Möchten Sie die Ordner zum Arbeitsbereich hinzufügen?", + "dropFolder": "Möchten Sie den Ordner zum Arbeitsbereich hinzufügen?", + "addFolders": "&&Ordner hinzufügen", + "addFolder": "&&Ordner hinzufügen", + "confirmOverwriteMessage": "{0} ist im Zielordner bereits vorhanden. Möchten Sie das Element ersetzen?", + "irreversible": "Diese Aktion kann nicht rückgängig gemacht werden.", + "replaceButtonLabel": "&&Ersetzen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..a026464980 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "Geöffnete Editoren", + "openEditosrSection": "Abschnitt \"Geöffnete Editoren\"", + "treeAriaLabel": "Geöffnete Editoren: Liste der aktiven Dateien", + "dirtyCounter": "{0} nicht gespeichert" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..11321cf828 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}, Editor-Gruppe", + "openEditorAriaLabel": "{0}, geöffnete Editoren", + "saveAll": "Alle speichern", + "closeAllUnmodified": "Nicht geänderte schließen", + "closeAll": "Alle schließen", + "compareWithSaved": "Mit gespeicherter Datei vergleichen", + "close": "Schließen", + "closeOthers": "Andere schließen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/deu/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..b920db2783 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0} nicht gespeicherte Dateien" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/deu/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..32c2e3e166 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (deleted from disk)" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json new file mode 100644 index 0000000000..31dcbca4d8 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "switchToChangesView": "Zur Änderungsansicht wechseln", + "openInEditor": "Zur Editor-Ansicht wechseln", + "workbenchStage": "Phase", + "workbenchUnstage": "Bereitstellung aufheben", + "stageSelectedLines": "Ausgewählte Zeilen bereitstellen", + "unstageSelectedLines": "Bereitstellung ausgewählter Zeilen aufheben", + "revertSelectedLines": "Gewählte Zeilen zurücksetzen", + "confirmRevertMessage": "Möchten Sie die ausgewählten Änderungen wirklich zurücksetzen?", + "irreversible": "Diese Aktion kann nicht rückgängig gemacht werden.", + "revertChangesLabel": "&&Änderungen zurücksetzen", + "openChange": "Änderung öffnen", + "openFile": "Datei öffnen", + "git": "Git" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/gitActions.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/gitActions.i18n.json new file mode 100644 index 0000000000..92f71abcdd --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/gitActions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openChange": "Änderung öffnen", + "openFile": "Datei öffnen", + "init": "Init", + "refresh": "Aktualisieren", + "stageChanges": "Phase", + "stageAllChanges": "Alle bereitstellen", + "confirmUndoMessage": "Möchten Sie alle Änderungen bereinigen?", + "confirmUndoAllOne": "Es sind nicht bereitgestellte Änderungen in {0} Datei vorhanden.\n\nDiese Aktion kann nicht rückgängig gemacht werden.", + "confirmUndoAllMultiple": "Es sind Änderungen in {0} Dateien vorhanden, deren Bereitstellung nicht aufgehoben wurde.\n\nDiese Aktion kann nicht rückgängig gemacht werden.", + "cleanChangesLabel": "&&Änderungen bereinigen", + "confirmUndo": "Sollen die Änderungen in \"{0}\" bereinigt werden?", + "irreversible": "Diese Aktion kann nicht rückgängig gemacht werden.", + "undoChanges": "Bereinigen", + "undoAllChanges": "Alle bereinigen", + "unstage": "Bereitstellung aufheben", + "unstageAllChanges": "Bereitstellung für alle aufheben", + "dirtyTreeCheckout": "Auschecken nicht möglich. Sie müssen für Ihre Arbeit zunächst einen Commit oder einen Stash ausführen.", + "commitStaged": "Commit bereitgestellt", + "commitStagedAmend": "Gestaffelt committen (Ändern)", + "commitStagedSignedOff": "Gestaffelt committen (abgemeldet)", + "commit": "Commit", + "commitMessage": "Commit der Nachricht", + "commitAll": "Commit für alle ausführen", + "commitAllSignedOff": "Alle committen (abgemeldet)", + "commitAll2": "Commit für alle ausführen", + "commitStaged2": "Commit bereitgestellt", + "dirtyTreePull": "Pull nicht möglich. Sie müssen für Ihre Arbeit zunächst einen Commit oder einen Stash ausführen.", + "authFailed": "Fehler bei der Authentifizierung für den Git-Remotespeicherort.", + "pushToRemote": "Push zu...", + "pushToRemotePickMessage": "Remoteauswahl zum Pushen des Branches \"{0}\":", + "publish": "Veröffentlichen", + "confirmPublishMessage": "Möchten Sie \"{0}\" unter \"{1}\" veröffentlichen?", + "confirmPublishMessageButton": "&&Veröffentlichen", + "publishPickMessage": "Remotespeicherort auswählen, an dem der Branch \"{0}\" veröffentlicht wird:", + "sync is unpredictable": "Mit dieser Aktion werden Commits per Push und Pull an und von \"{0}\" übertragen.", + "ok": "OK", + "cancel": "Abbrechen", + "never again": "OK, nicht mehr anzeigen", + "undoLastCommit": "Letzten Commit rückgängig machen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json new file mode 100644 index 0000000000..059da79d1d --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refAriaLabel": "{0}, Git", + "checkoutBranch": "Branch unter {0}", + "checkoutRemoteBranch": "Remotebranch unter {0}", + "checkoutTag": "Tag bei {0}", + "alreadyCheckedOut": "Branch {0} ist bereits der aktuelle Branch.", + "branchAriaLabel": "{0}, Git-Branch", + "createBranch": "Branch {0} erstellen", + "noBranches": "Keine anderen Branches", + "notValidBranchName": "Bitte geben Sie einen gültigen Branchnamen an." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/gitServices.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/gitServices.i18n.json new file mode 100644 index 0000000000..61799d077f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/gitServices.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cantOpen": "Diese Git-Ressource kann nicht geöffnet werden.", + "gitIndexChanges": "{0} (index) ↔ {1}", + "gitIndexChangesDesc": "{0} – Änderungen am Index", + "gitIndexChangesRenamed": "{0} ← {1}", + "gitIndexChangesRenamedDesc": "{0} – Umbenannt – Änderungen für Index", + "workingTreeChanges": "{0} (HEAD) ↔ {1}", + "workingTreeChangesDesc": "{0} – Änderungen am Arbeitsbaum", + "gitMergeChanges": "{0} (merge) ↔ {1}", + "gitMergeChangesDesc": "{0} – Änderungen mergen", + "updateGit": "Git {0} scheint installiert zu sein. Code funktioniert am besten mit Git >= 2.0.0.", + "download": "Herunterladen", + "neverShowAgain": "Nicht mehr anzeigen", + "configureUsernameEmail": "Konfigurieren Sie Ihren Git-Benutzernamen und Ihre E-Mail-Adresse.", + "badConfigFile": "Git {0}", + "unmergedChanges": "Sie müssen nicht zusammengeführte Änderungen lösen, bevor Sie Ihre Änderungen bestätigen.", + "showOutput": "Ausgabe anzeigen", + "cancel": "Abbrechen", + "checkNativeConsole": "Fehler beim Ausführen eines Git-Vorgangs. Bitte überprüfen Sie die Ausgabe, oder verwenden Sie eine Konsole, um den Repositorystatus zu überprüfen.", + "changesFromIndex": "{0} (index)", + "changesFromIndexDesc": "{0} – Änderungen am Index", + "changesFromTree": "{0} ({1})", + "changesFromTreeDesc": "{0} – Änderungen in {1}", + "cantOpenResource": "Diese Git-Ressource kann nicht geöffnet werden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json new file mode 100644 index 0000000000..2eaae0c1a0 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "publishBranch": "Branch veröffentlichen", + "syncBranch": "Änderungen synchronisieren", + "gitNotEnabled": "Git ist in diesem Arbeitsbereich nicht aktiviert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json new file mode 100644 index 0000000000..dc36117afe --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gitProgressBadge": "Ausführungsstatus Git", + "gitPendingChangesBadge": "{0} ausstehende Änderungen", + "toggleGitViewlet": "Git anzeigen", + "git": "Git", + "view": "Anzeigen", + "gitCommands": "Git-Befehle", + "gitConfigurationTitle": "Git", + "gitEnabled": "Ist für Git aktiviert", + "gitPath": "Der Pfad zur ausführbaren Git-Datei.", + "gitAutoRefresh": "Gibt an, ob die automatische Aktualisierung aktiviert ist.", + "gitAutoFetch": "Gibt an, ob automatischer Abruf aktiviert ist.", + "gitLongCommit": "Gibt an, ob Warnungen zu langen Commitnachrichten erfolgen sollen.", + "gitLargeRepos": "Die Verwaltung großer Repositorys durch Code immer zulassen.", + "confirmSync": "Vor dem Synchronisieren von Git-Repositorys bestätigen.", + "countBadge": "Steuert die Git-Badgeanzahl.", + "checkoutType": "Steuert, welcher Branchtyp aufgelistet wird." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json new file mode 100644 index 0000000000..aaf0145760 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "needMessage": "Geben Sie eine Commit-Meldung an. Sie können jederzeit **{0}** drücken, um einen Commit für die Änderungen auszuführen. Wenn bereitgestellte Änderungen vorliegen, wird nur für diese ein Commit ausgeführt; andernfalls für alle Änderungen.", + "nothingToCommit": "Sobald Änderungen für einen Commit vorliegen, geben Sie die Commit-Meldung ein, und drücken Sie **{0}**, um den Commit für die Änderungen auszuführen. Wenn bereitgestellte Änderungen vorliegen, wird nur für diese ein Commit ausgeführt; andernfalls für alle Änderungen.", + "longCommit": "Es wird empfohlen, dass die erste Zeile des Commits weniger als 50 Zeichen lang ist. Sie können weitere Zeilen für zusätzliche Informationen verwenden.", + "commitMessage": "Nachricht (drücken Sie {0}, um den Commit auszuführen)", + "commitMessageAriaLabel": "Git: Geben Sie die Commitnachricht ein, und drücken Sie {0}, um den Commit auszuführen.", + "treeAriaLabel": "Ansicht \"Git-Änderungen\"", + "showOutput": "Git-Ausgabe anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json new file mode 100644 index 0000000000..e6146878f6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stagedChanges": "Bereitgestellte Änderungen", + "allChanges": "Änderungen", + "mergeChanges": "Änderungen zusammenführen", + "outsideOfWorkspace": "Diese Datei befindet sich außerhalb des aktuellen Arbeitsbereichs.", + "modified-char": "M", + "added-char": "A", + "deleted-char": "D", + "renamed-char": "R", + "copied-char": "C", + "untracked-char": "U", + "ignored-char": "!", + "title-index-modified": "Im Index geändert", + "title-modified": "Geändert am", + "title-index-added": "Zum Index hinzugefügt", + "title-index-deleted": "Aus dem Index gelöscht", + "title-deleted": "Gelöscht", + "title-index-renamed": "Im Index umbenannt", + "title-index-copied": "In den Index kopiert", + "title-untracked": "Nicht verfolgt", + "title-ignored": "Ignoriert", + "title-conflict-both-deleted": "Konflikt: beide gelöscht", + "title-conflict-added-by-us": "Konflikt: hinzugefügt von uns", + "title-conflict-deleted-by-them": "Konflikt: gelöscht von diesen", + "title-conflict-added-by-them": "Konflikt: hinzugefügt von diesen", + "title-conflict-deleted-by-us": "Konflikt: gelöscht von uns", + "title-conflict-both-added": "Konflikt: beide hinzugefügt", + "title-conflict-both-modified": "Konflikt: beide geändert", + "fileStatusAriaLabel": "Die Datei \"{0}\" im Ordner \"{1}\" weist den Status {2} auf, Git.", + "ariaLabelStagedChanges": "Gestaffelte Änderungen, Git", + "ariaLabelChanges": "Änderungen, Git", + "ariaLabelMerge": "Mergen, Git" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json new file mode 100644 index 0000000000..bf5079cdb0 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disabled": "Git ist in den Einstellungen deaktiviert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json new file mode 100644 index 0000000000..8cf1e663bd --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noGit": "Dieser Arbeitsbereich unterliegt noch nicht der Git-Quellcodeverwaltung.", + "gitinit": "Git-Repository initialisieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json new file mode 100644 index 0000000000..04222bf184 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "macInstallWith": "Sie können die Anwendung mit {0} installieren, von {1} herunterladen oder die Befehlszeilen-Entwicklertools von {2} installieren, indem Sie einfach {3} in einer Terminaleingabeaufforderung eingeben.", + "winInstallWith": "Sie können die Anwendung mit {0} installieren oder von {1} herunterladen.", + "linuxDownloadFrom": "Sie können die Anwendung von {0} herunterladen.", + "downloadFrom": "Sie können die Anwendung von {0} herunterladen.", + "looksLike": "Git scheint auf Ihrem System nicht installiert zu sein.", + "pleaseRestart": "Starten Sie VSCode nach der Installation von Git neu." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json new file mode 100644 index 0000000000..1954498f3a --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "huge": "Das Repository scheint viele aktive Änderungen aufzuweisen.\nDies kann bewirken, dass Code sehr langsam wird.", + "setting": "Sie können diese Warnung mit der folgenden Einstellung dauerhaft deaktivieren:", + "allo": "Große Repositorys zulassen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json new file mode 100644 index 0000000000..68bef1bbc9 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrongRoot": "Dieses Verzeichnis scheint in einem Git-Repository enthalten zu sein.", + "pleaseRestart": "Öffnen Sie das Stammverzeichnis des Repositorys, um auf Git-Features zuzugreifen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json b/i18n/deu/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json new file mode 100644 index 0000000000..e9a8b297f7 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspaceHelp": "Sie haben noch keinen Ordner geöffnet.", + "pleaseRestart": "Öffnen Sie einen Ordner mit einem Git-Repository, um auf Git-Features zuzugreifen.", + "openFolder": "Ordner öffnen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json new file mode 100644 index 0000000000..c228566bf0 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSCMViewlet": "SCM anzeigen", + "git": "Git" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json b/i18n/deu/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json new file mode 100644 index 0000000000..00e6ba8fa4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "valid": "Geben Sie eine gültige URL für das Git-Repository an.", + "url": "Repository-URL", + "directory": "Zielklonverzeichnis", + "cloning": "Das Repository \"{0}\" wird geklont...", + "already exists": "Das Zielverzeichnis ist bereits vorhanden, wählen Sie ein anderes Verzeichnis für den Klonvorgang aus." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json b/i18n/deu/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json new file mode 100644 index 0000000000..91fbae2148 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "git": "Git" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/git/node/git.lib.i18n.json b/i18n/deu/src/vs/workbench/parts/git/node/git.lib.i18n.json new file mode 100644 index 0000000000..56898e24f6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/git/node/git.lib.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorBuffer": "Datei aus Git kann nicht geöffnet werden.", + "fileBinaryError": "Die Datei scheint eine Binärdatei zu sein und kann nicht als Text geöffnet werden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..0645b87ced --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "HTML-Vorschau", + "devtools.webview": "Developer: Webview-Tools" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/deu/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..bfe491de33 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "Ungültige Editor-Eingabe." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/deu/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..f2a6f29f0e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "Developer: Webview-Tools" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/deu/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..cf3e58161c --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "Anzeigen", + "problems.view.show.label": "Probleme anzeigen", + "problems.panel.configuration.title": "Ansicht \"Probleme\"", + "problems.panel.configuration.autoreveal": "Steuert, ob die Ansicht \"Probleme\" automatisch Dateien anzeigen sollte, wenn diese geöffnet werden.", + "markers.panel.title.problems": "Probleme", + "markers.panel.aria.label.problems.tree": "Probleme nach Dateien gruppiert", + "markers.panel.no.problems.build": "Es wurden bisher keine Probleme im Arbeitsbereich erkannt.", + "markers.panel.no.problems.filters": "Es wurden keine Ergebnisse mit den angegebenen Filterkriterien gefunden.", + "markers.panel.action.filter": "Probleme filtern", + "markers.panel.filter.placeholder": "Nach Typ oder Text filtern", + "markers.panel.filter.errors": "Fehler", + "markers.panel.filter.warnings": "Warnungen", + "markers.panel.filter.infos": "Informationen", + "markers.panel.single.error.label": "1 Fehler", + "markers.panel.multiple.errors.label": "{0} Fehler", + "markers.panel.single.warning.label": "1 Warnung", + "markers.panel.multiple.warnings.label": "{0} Warnungen", + "markers.panel.single.info.label": "1 Information", + "markers.panel.multiple.infos.label": "{0}-Informationen", + "markers.panel.single.unknown.label": "1 Unbekannte", + "markers.panel.multiple.unknowns.label": "{0} Unbekannte", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "{0} mit {1} Problemen", + "problems.tree.aria.label.error.marker": "Von \"{0}\" generierter Fehler: \"{1}\" in Zeile {2} bei Zeichen {3}", + "problems.tree.aria.label.error.marker.nosource": "Fehler: \"{0}\" in Zeile {1} bei Zeichen {2}", + "problems.tree.aria.label.warning.marker": "Von \"{0}\" generierte Warnung: \"{1}\" in Zeile {2} bei Zeichen {3}", + "problems.tree.aria.label.warning.marker.nosource": "Warnung: \"{0}\" in Zeile {1} bei Zeichen {2}", + "problems.tree.aria.label.info.marker": "Von \"{0}\" generierte Informationen: \"{1}\" in Zeile {2} bei Zeichen {3}", + "problems.tree.aria.label.info.marker.nosource": "Informationen: \"{0}\" in Zeile {1} bei Zeichen {2}", + "problems.tree.aria.label.marker": "Von \"{0}\" generiertes Problem: \"{1}\" in Zeile {2} bei Zeichen {3}", + "problems.tree.aria.label.marker.nosource": "Problem: \"{0}\" in Zeile {1} bei Zeichen {2}", + "errors.warnings.show.label": "Fehler und Warnungen anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/deu/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..a35412352a --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "Kopieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..4678965134 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Wir würden uns freuen, wenn Sie an einer schnellen Umfrage teilnehmen.", + "takeSurvey": "An Umfrage teilnehmen", + "remindLater": "Später erinnern", + "neverAgain": "Nicht mehr anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..52526f0073 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Ausgabe", + "viewCategory": "Anzeigen", + "clearOutput.label": "Ausgabe löschen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/deu/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..a8dea9c7b9 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "Ausgabe umschalten", + "clearOutput": "Ausgabe löschen", + "toggleOutputScrollLock": "Ausgabe-Bildlaufsperre umschalten", + "switchToOutput.label": "Zur Ausgabe wechseln" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/deu/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..289fbc3db3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0}, Ausgabebereich", + "outputPanelAriaLabel": "Ausgabebereich" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/deu/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..566fcd6184 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Ausgabe", + "channel": "für \"{0}\"" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..542dd227d2 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "Langsamer Start erkannt", + "slow.detail": "Es tut uns leid, dass Ihr Start so langsam war. Starten Sie \"{0}\" mit aktivierter Profilerstellung neu, geben Sie die Profile für uns frei, und wir tun unser Bestes, damit der Start bald wieder perfekt funktioniert.", + "prof.message": "Profile wurden erfolgreich erstellt.", + "prof.detail": "Erstellen Sie ein Problem, und fügen Sie die folgenden Dateien manuell an:\n{0}", + "prof.restartAndFileIssue": "Problem erstellen und neu starten", + "prof.restart": "Neu starten", + "prof.thanks": "Danke für Ihre Hilfe.", + "prof.detail.restart": "Ein abschließender Neustart ist erforderlich um '{0}' nutzen zu können. Danke für Ihre Hilfe." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..ac9d49c92e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "Betätigen Sie die gewünschte Tastenkombination und die Eingabetaste. Drücken Sie zum Abbrechen auf ESC.", + "defineKeybinding.chordsTo": "Tastenkombination zu" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..5012e1deac --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "Tastenkombinationen", + "SearchKeybindings.AriaLabel": "Tastenzuordnungen suchen", + "SearchKeybindings.Placeholder": "Tastenzuordnungen suchen", + "sortByPrecedene": "Nach Priorität sortieren", + "header-message": "Öffnen und bearbeiten Sie die folgende Datei, um erweiterte Anpassungen vorzunehmen:", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "Tastenzuordnungen", + "changeLabel": "Tastenzuordnung ändern", + "addLabel": "Tastenzuordnung hinzufügen", + "removeLabel": "Tastenzuordnung entfernen", + "resetLabel": "Tastenbindung zurücksetzen", + "showConflictsLabel": "Konflikte anzeigen", + "copyLabel": "Kopieren", + "error": "Fehler '{0}' beim Bearbeiten der Tastenzuordnung. Überprüfen Sie die Datei 'keybindings.json'.", + "command": "Befehlstaste", + "keybinding": "Tastenzuordnung", + "source": "Quelle", + "when": "Zeitpunkt", + "editKeybindingLabelWithKey": "Tastenbindung ändern {0}", + "editKeybindingLabel": "Tastenzuordnung ändern", + "addKeybindingLabelWithKey": "Tastenbindung hinzufügen {0}", + "addKeybindingLabel": "Tastenzuordnung hinzufügen", + "commandAriaLabel": "Befehl: {0}.", + "keybindingAriaLabel": "Tastenzuordnung: {0}.", + "noKeybinding": "Keine Tastenzuordnung zugewiesen.", + "sourceAriaLabel": "Quelle: {0}.", + "whenAriaLabel": "When: {0}.", + "noWhen": "Kein when-Kontext." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..9bb5b7a0bb --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "Tastenbindung definieren", + "defineKeybinding.kbLayoutErrorMessage": "Sie können diese Tastenkombination mit Ihrem aktuellen Tastaturlayout nicht generieren.", + "defineKeybinding.kbLayoutLocalAndUSMessage": "**{0}** für Ihr aktuelles Tastaturlayout (**{1}** für USA, Standard).", + "defineKeybinding.kbLayoutLocalMessage": "**{0}** für Ihr aktuelles Tastaturlayout." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..1d12f77a24 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "Standardeditor für Einstellungen", + "keybindingsEditor": "Editor für Tastenzuordnungen", + "preferences": "Einstellungen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..e605553578 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "Benutzereinstellungen öffnen", + "openGlobalKeybindings": "Tastaturkurzbefehle öffnen", + "openGlobalKeybindingsFile": "Datei mit Tastaturkurzbefehlen öffnen", + "openWorkspaceSettings": "Arbeitsbereichseinstellungen öffnen", + "openFolderSettings": "Ordnereinstellungen öffnen", + "pickFolder": "Ordner auswählen", + "configureLanguageBasedSettings": "Sprachspezifische Einstellungen konfigurieren...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "Sprache auswählen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..010ed6d3e4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "Standardeinstellungen", + "SearchSettingsWidget.AriaLabel": "Einstellungen suchen", + "SearchSettingsWidget.Placeholder": "Einstellungen suchen", + "totalSettingsMessage": "Insgesamt {0} Einstellungen", + "noSettingsFound": "Keine Ergebnisse", + "oneSettingFound": "1 Einstellung zugeordnet", + "settingsFound": "{0} Einstellungen zugeordnet", + "fileEditorWithInputAriaLabel": "{0}. Textdatei-Editor.", + "fileEditorAriaLabel": "Textdatei-Editor", + "preferencesAriaLabel": "Standardeinstellungen. Schreibgeschützter Text-Editor." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..407a43077f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "Platzieren Sie Ihre Einstellungen hier, um die Standardeinstellungen zu überschreiben.", + "errorInvalidConfiguration": "Einstellungen können nicht geschrieben werden. Öffnen Sie die Datei, um Fehler/Warnungen in der Datei zu korrigieren. Versuchen Sie es anschließend noch mal.", + "emptyWorkspaceSettingsHeader": "Platzieren Sie Ihre Einstellungen hier, um die Benutzereinstellungen zu überschreiben.", + "emptyFolderSettingsHeader": "Platzieren Sie Ihre Ordnereinstellungen hier, um die Einstellungen in den Arbeitsbereichseinstellungen zu überschreiben.", + "defaultFolderSettingsTitle": "Standardordnereinstellungen", + "defaultSettingsTitle": "Standardeinstellungen", + "noSettingsFound": "Keine Einstellungen gefunden.", + "editTtile": "Bearbeiten", + "replaceDefaultValue": "In Einstellungen ersetzen", + "copyDefaultValue": "In Einstellungen kopieren", + "unsupportedPHPExecutablePathSetting": "Diese Einstellung muss eine Benutzereinstellung sein. Öffnen Sie zum Konfigurieren von PHP für den Arbeitsbereich eine PHP-Datei, und klicken Sie in der Statusleiste auf \"PHP-Pfad\".", + "unsupportedWorkspaceSetting": "Diese Einstellung muss eine Benutzereinstellung sein.", + "unsupportedWorkbenchSetting": "Diese Einstellung kann jetzt nicht angewendet werden. Sie wird angewendet, wenn Sie den Ordner direkt öffnen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..3a416d714a --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "Zuerst einen Ordner öffnen, um Arbeitsbereicheinstellungen zu erstellen", + "emptyKeybindingsHeader": "Platzieren Sie Ihre Tastenzuordnungen in dieser Datei, um die Standardwerte zu überschreiben.", + "defaultKeybindings": "Standardtastenzuordnungen", + "folderSettingsName": "{0} (Ordnereinstellungen)", + "fail.createSettings": "{0} ({1}) kann nicht erstellt werden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..72638b33da --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "Ordnereinstellungen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..efa624050d --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "Standard", + "user": "Benutzer", + "meta": "meta", + "option": "option" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..aaabf136db --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "Benutzereinstellungen", + "workspaceSettingsTarget": "Arbeitsbereichseinstellungen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..2e9b519d20 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "Am häufigsten verwendet", + "noSettings": "Keine Einstellungen", + "defaultKeybindingsHeader": "Überschreiben Sie Tastenzuordnungen, indem Sie sie in die Tastenzuordnungsdatei einfügen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..1344d88af2 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Alle Befehle anzeigen", + "clearCommandHistory": "Befehlsverlauf löschen", + "showCommands.label": "Befehlspalette...", + "entryAriaLabelWithKey": "{0}, {1}, Befehle", + "entryAriaLabel": "{0}, Befehle", + "canNotRun": "Der Befehl '{0}' kann nicht an dieser Stelle ausgeführt werden.", + "actionNotEnabled": "Der Befehl \"{0}\" ist im aktuellen Kontext nicht aktiviert.", + "recentlyUsed": "zuletzt verwendet", + "morecCommands": "andere Befehle", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "Keine übereinstimmenden Befehle." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..e5bdceb3a5 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "Gehe zu Zeile...", + "gotoLineLabelEmptyWithLimit": "Zeilennummer zwischen 1 und {0} eingeben, zu der navigiert werden soll", + "gotoLineLabelEmpty": "Zeilennummer eingeben, zu der navigiert werden soll", + "gotoLineColumnLabel": "Zu Zeile {0} und Zeichen {1} wechseln", + "gotoLineLabel": "Gehe zu Zeile {0}", + "gotoLineHandlerAriaLabel": "Geben Sie eine Zeilennummer ein, zu der navigiert werden soll.", + "cannotRunGotoLine": "Zuerst eine Textdatei öffnen, um zu einer Zeile zu navigieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..508d1ccdd1 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "Zu Symbol in Datei wechseln...", + "symbols": "Symbole ({0})", + "method": "Methoden ({0})", + "function": "Funktionen ({0})", + "_constructor": "Konstruktoren ({0})", + "variable": "Variablen ({0})", + "class": "Klassen ({0})", + "interface": "Schnittstellen ({0})", + "namespace": "Namespaces ({0})", + "package": "Pakete ({0})", + "modules": "Module ({0})", + "property": "Eigenschaften ({0})", + "enum": "Enumerationen ({0})", + "string": "Zeichenfolgen ({0})", + "rule": "Regeln ({0})", + "file": "Dateien ({0})", + "array": "Arrays ({0})", + "number": "Zahlen ({0})", + "boolean": "Boolesche Werte ({0})", + "object": "Objekte ({0})", + "key": "Schlüssel ({0})", + "entryAriaLabel": "{0}, Symbole", + "noSymbolsMatching": "Keine übereinstimmenden Symbole", + "noSymbolsFound": "Es wurden keine Symbole gefunden.", + "gotoSymbolHandlerAriaLabel": "Nehmen Sie eine Eingabe vor, um Symbole des aktuell aktiven Editors einzuschränken.", + "cannotRunGotoSymbolInFile": "Keine Symbolinformationen für die Datei vorhanden.", + "cannotRunGotoSymbol": "Zuerst eine Textdatei öffnen, um zu einem Symbol zu navigieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..cc05de40a6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, Auswahlhilfe", + "globalCommands": "Globale Befehle", + "editorCommands": "Editor-Befehle" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..1fb5551f11 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "Befehle anzeigen und ausführen", + "gotoLineDescriptionMac": "Gehe zu Zeile", + "gotoLineDescriptionWin": "Gehe zu Zeile", + "gotoSymbolDescription": "Gehe zu Symbol in Datei", + "gotoSymbolDescriptionScoped": "Gehe zu Symbol in Datei nach Kategorie", + "helpDescription": "Hilfe anzeigen", + "viewPickerDescription": "Ansicht öffnen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..f80ebac4f9 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, Ansichtsauswahl", + "views": "Ansichten", + "panels": "Bereiche", + "terminals": "Terminal", + "terminalTitle": "{0}: {1}", + "channels": "Ausgabe", + "openView": "Ansicht öffnen", + "quickOpenView": "Schnellansicht öffnen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..fb8e1a733f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "Eine Einstellung wurde geändert, welche einen Neustart benötigt.", + "relaunchSettingDetail": "Drücke den Neu starten-Button, um {0} neuzustarten und die Einstellung zu aktivieren.", + "restart": "Neu starten", + "relaunchWorkspaceMessage": "Dieser Arbeitsbereich erfordert das erneute Laden des Erweiterungssystems.", + "reload": "Neu starten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..a9cf753f3b --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "Hintergrundfarbe für die Editor-Leiste für Zeilen, die geändert wurden.", + "editorGutterAddedBackground": "Hintergrundfarbe für die Editor-Leiste für Zeilen, die hinzugefügt wurden.", + "editorGutterDeletedBackground": "Hintergrundfarbe für die Editor-Leiste für Zeilen, die gelöscht wurden.", + "overviewRulerModifiedForeground": "Übersichtslineal-Markierungsfarbe für geänderte Inhalte.", + "overviewRulerAddedForeground": "Übersichtslineal-Markierungsfarbe für hinzugefügte Inhalte.", + "overviewRulerDeletedForeground": "Übersichtslineal-Markierungsfarbe für gelöschte Inhalte." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..a32abd828b --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "Git anzeigen", + "source control": "Quellcodeverwaltung", + "toggleSCMViewlet": "SCM anzeigen", + "view": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..60e81c5e68 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0} ausstehende Änderungen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..28e4ed2f00 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "Installiere weiter SCM Provider...", + "switch provider": "SCM-Anbieter wechseln..." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..8df6b40e34 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Message (press {0} to commit)", + "installAdditionalSCMProviders": "Installiere weiter SCM Provider...", + "no open repo": "Keine Quellsteuerelemente sind aktiv.", + "source control": "Quellcodeverwaltung", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..b3083fdd39 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "Datei- und Symbolergebnisse", + "fileResults": "Dateiergebnisse" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..f5558c584e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, Dateiauswahl", + "searchResults": "Suchergebnisse" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..2531f58c6d --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, Symbolauswahl", + "symbols": "Symbolergebnisse", + "noSymbolsMatching": "Keine übereinstimmenden Symbole", + "noSymbolsWithoutInput": "Nehmen Sie eine Eingabe vor, um nach Symbolen zu suchen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..b514313a25 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "Eingabe", + "useIgnoreFilesDescription": "Ignorieren von Dateien verwenden", + "useExcludeSettingsDescription": "Ausschlusseinstellungen verwenden" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..6a3a04581b --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (Replace Preview)" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..1cb8ed01f6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Zu Symbol im Arbeitsbereich wechseln...", + "name": "Suchen", + "showSearchViewlet": "Suche anzeigen", + "view": "Anzeigen", + "findInFiles": "In Dateien suchen", + "openAnythingHandlerDescription": "Zu Datei wechseln", + "openSymbolDescriptionNormal": "Zu Symbol im Arbeitsbereich wechseln", + "searchOutputChannelTitle": "Suchen", + "searchConfigurationTitle": "Suchen", + "exclude": "Konfigurieren Sie Globmuster zum Ausschließen von Dateien und Ordnern in Suchvorgängen. Alle Globmuster werden von der files.exclude-Einstellung geerbt.", + "exclude.boolean": "Das Globmuster, mit dem Dateipfade verglichen werden sollen. Legen Sie diesen Wert auf \"true\" oder \"false\" fest, um das Muster zu aktivieren bzw. zu deaktivieren.", + "exclude.when": "Zusätzliche Überprüfung der gleichgeordneten Elemente einer entsprechenden Datei. Verwenden Sie \"$(basename)\" als Variable für den entsprechenden Dateinamen.", + "useRipgrep": "Steuert, ob \"ripgrep\" in der Textsuche verwendet wird", + "useIgnoreFilesByDefault": "Steuert, ob bei der Suche in einem neuen Arbeitsbereich standardmäßig GITIGNORE- und IGNORE-Dateien verwendet werden sollen.", + "search.quickOpen.includeSymbols": "Konfigurieren Sie diese Option, um Ergebnisse aus einer globalen Symbolsuche in die Dateiergebnisse für Quick Open einzuschließen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..7d19f11761 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "Nächstes Sucheinschlussmuster anzeigen", + "previousSearchIncludePattern": "Vorheriges Sucheinschlussmuster anzeigen", + "nextSearchExcludePattern": "Nächstes Suchausschlussmuster anzeigen", + "previousSearchExcludePattern": "Vorheriges Suchausschlussmuster anzeigen", + "nextSearchTerm": "Nächsten Suchbegriff anzeigen", + "previousSearchTerm": "Vorherigen Suchbegriff anzeigen", + "focusNextInputBox": "Fokus im nächsten Eingabefeld", + "focusPreviousInputBox": "Fokus im vorherigen Eingabefeld", + "replaceInFiles": "In Dateien ersetzen", + "findInWorkspace": "In Arbeitsbereich suchen...", + "findInFolder": "In Ordner suchen...", + "RefreshAction.label": "Aktualisieren", + "ClearSearchResultsAction.label": "Suchergebnisse löschen", + "FocusNextSearchResult.label": "Fokus auf nächstes Suchergebnis", + "FocusPreviousSearchResult.label": "Fokus auf vorheriges Suchergebnis", + "RemoveAction.label": "Entfernen", + "file.replaceAll.label": "Alle ersetzen", + "match.replace.label": "Ersetzen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..c4ae258399 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "Andere Dateien", + "searchFileMatches": "{0} Dateien gefunden", + "searchFileMatch": "{0} Datei gefunden", + "searchMatches": "{0} Übereinstimmungen gefunden", + "searchMatch": "{0} Übereinstimmung gefunden", + "folderMatchAriaLabel": "{0} Übereinstimmungen im Ordnerstamm {1}, Suchergebnis", + "fileMatchAriaLabel": "{0} Übereinstimmungen in der Datei \"{1}\" des Ordners \"{2}\", Suchergebnis", + "replacePreviewResultAria": "Ersetze Term {0} mit {1} an Spaltenposition {2} in Zeile mit Text {3}", + "searchResultAria": "Term {0} an Spaltenposition {1} in Zeile mit Text {2} gefunden" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..7a62f6cf8c --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "Suchdetails umschalten", + "searchScope.includes": "Einzuschließende Dateien", + "label.includes": "Sucheinschlussmuster", + "searchScope.excludes": "Auszuschließende Dateien", + "label.excludes": "Suchausschlussmuster", + "replaceAll.confirmation.title": "Alle ersetzen", + "replaceAll.confirm.button": "Ersetzen", + "replaceAll.occurrence.file.message": "{0} Vorkommen in {1} Datei durch \"{2}\" ersetzt.", + "removeAll.occurrence.file.message": "{0} Vorkommen in {1} Datei ersetzt.", + "replaceAll.occurrence.files.message": "{0} Vorkommen in {1} Dateien durch \"{2}\" ersetzt.", + "removeAll.occurrence.files.message": "{0} Vorkommen in {1} Dateien ersetzt.", + "replaceAll.occurrences.file.message": "{0} Vorkommen in {1} Datei durch \"{2}\" ersetzt.", + "removeAll.occurrences.file.message": "{0} Vorkommen in {1} Datei ersetzt.", + "replaceAll.occurrences.files.message": "{0} Vorkommen in {1} Dateien wurden durch \"{2}\" ersetzt.", + "removeAll.occurrences.files.message": "{0} Vorkommen in {1} Dateien ersetzt.", + "removeAll.occurrence.file.confirmation.message": "{0} Vorkommen in {1} Datei durch \"{2}\" ersetzen?", + "replaceAll.occurrence.file.confirmation.message": "{0} Vorkommen in {1} Datei ersetzen?", + "removeAll.occurrence.files.confirmation.message": "{0} Vorkommen in {1} Dateien durch \"{2}\" ersetzen?", + "replaceAll.occurrence.files.confirmation.message": "{0} Vorkommen in {1} Dateien ersetzen?", + "removeAll.occurrences.file.confirmation.message": "{0} Vorkommen in {1} Datei durch \"{2}\" ersetzen?", + "replaceAll.occurrences.file.confirmation.message": "{0} Vorkommen in {1} Datei ersetzen?", + "removeAll.occurrences.files.confirmation.message": "{0} Vorkommen in {1} Dateien durch \"{2}\" ersetzen?", + "replaceAll.occurrences.files.confirmation.message": "{0} Vorkommen in {1} Dateien ersetzen?", + "treeAriaLabel": "Suchergebnisse", + "searchPathNotFoundError": "Der Suchpfad wurde nicht gefunden: {0}.", + "searchMaxResultsWarning": "Das Resultset enthält nur eine Teilmenge aller Übereinstimmungen. Verfeinern Sie Ihre Suche, um die Ergebnisse einzugrenzen.", + "searchCanceled": "Die Suche wurde abgebrochen, bevor Ergebnisse gefunden werden konnten – ", + "noResultsIncludesExcludes": "Keine Ergebnisse in \"{0}\" unter Ausschluss von \"{1}\" gefunden – ", + "noResultsIncludes": "Keine Ergebnisse in \"{0}\" gefunden – ", + "noResultsExcludes": "Keine Ergebnisse gefunden, die \"{0}\" ausschließen – ", + "noResultsFound": "Es wurden keine Ergebnisse gefunden. Überprüfen Sie Ihre Einstellungen für konfigurierte Ausschlüsse und das Ignorieren von Dateien –", + "rerunSearch.message": "Erneut suchen", + "rerunSearchInAll.message": "Erneut in allen Dateien suchen", + "openSettings.message": "Einstellungen öffnen", + "openSettings.learnMore": "Weitere Informationen", + "ariaSearchResultsStatus": "Die Suche hat {0} Ergebnisse in {1} Dateien zurückgegeben.", + "search.file.result": "{0} Ergebnis in {1} Datei", + "search.files.result": "{0} Ergebnis in {1} Dateien", + "search.file.results": "{0} Ergebnisse in {1} Datei", + "search.files.results": "{0} Ergebnisse in {1} Dateien", + "searchWithoutFolder": "Sie haben noch keinen Ordner geöffnet. Nur offene Dateien werden momentan durchsucht - ", + "openFolder": "Ordner öffnen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..6aca4a5a63 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "Alle ersetzen (Suche zum Aktivieren übermitteln)", + "search.action.replaceAll.enabled.label": "Alle ersetzen", + "search.replace.toggle.button.title": "Ersetzung umschalten", + "label.Search": "Suche: Geben Sie den Suchbegriff ein, und drücken Sie die EINGABETASTE, um zu suchen, oder ESC, um den Vorgang abzubrechen.", + "search.placeHolder": "Suchen", + "label.Replace": "Ersetzen: Geben Sie den Begriff ein, der zum Ersetzen verwendet werden soll, und drücken Sie die EINGABETASTE, um eine Vorschau anzuzeigen, oder ESC, um den Vorgang abzubrechen.", + "search.replace.placeHolder": "Ersetzen", + "regexp.validationFailure": "Der Ausdruck stimmt mit allem überein." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/deu/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..25208d3617 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "Kein Ordner im Arbeitsbereich mit Namen: {0}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..73f087bef4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "Trägt Codeausschnitte bei.", + "vscode.extension.contributes.snippets-language": "Der Sprachbezeichner, für den dieser Codeausschnitt beigetragen wird.", + "vscode.extension.contributes.snippets-path": "Der Pfad der Codeausschnittdatei. Der Pfad ist relativ zum Erweiterungsordner und beginnt normalerweise mit \". /snippets/\".", + "invalid.language": "Unbekannte Sprache in \"contributes.{0}.language\". Bereitgestellter Wert: {1}", + "invalid.path.0": "In \"contributes.{0}.path\" wurde eine Zeichenfolge erwartet. Bereitgestellter Wert: {1}", + "invalid.path.1": "Es wurde erwartet, dass \"contributes.{0}.path\" ({1}) im Ordner ({2}) der Erweiterung enthalten ist. Dies führt ggf. dazu, dass die Erweiterung nicht portierbar ist.", + "badVariableUse": "Das \"{0}\"-Snippet verwirrt wahrscheinlich Snippet-Variablen und Snippet-Paltzhalter. Schaue https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax für weitere Informationen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..180d43609b --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "Codeausschnitt einfügen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..7bd632f2f4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "Sprache für Codeausschnitt auswählen", + "openSnippet.errorOnCreate": "\"{0}\" kann nicht erstellt werden.", + "openSnippet.label": "Benutzercodeausschnitte öffnen", + "preferences": "Einstellungen", + "snippetSchema.json.default": "Leerer Codeausschnitt", + "snippetSchema.json": "Benutzerkonfiguration des Codeausschnitts", + "snippetSchema.json.prefix": "Das Präfix, das beim Auswählen des Codeausschnitts in IntelliSense verwendet werden soll.", + "snippetSchema.json.body": "Der Inhalt des Codeausschnitts. Verwenden Sie \"$1\", \"${1:defaultText}\", um Cursorpositionen zu definieren, und \"$0\" für die finale Cursorposition. Fügen Sie mit \"${varName}\" und \"${varName:defaultText}\" Variablenwerte ein, z. B. \"This is file: $TM_FILENAME\".", + "snippetSchema.json.description": "Die Beschreibung des Codeausschnitts." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..9ca1ef8b6e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "Benutzercodeausschnitt", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0}, {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..bb00f4cddf --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "Fügt Codeausschnitte ein, wenn ihr Präfix übereinstimmt. Funktioniert am besten, wenn \"quickSuggestions\" nicht aktiviert sind." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..31308e9700 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "Helfen Sie uns die Unterstützung für {0} zu verbessern", + "takeShortSurvey": "An kurzer Umfrage teilnehmen", + "remindLater": "Später erinnern", + "neverAgain": "Nicht mehr anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..4678965134 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Wir würden uns freuen, wenn Sie an einer schnellen Umfrage teilnehmen.", + "takeSurvey": "An Umfrage teilnehmen", + "remindLater": "Später erinnern", + "neverAgain": "Nicht mehr anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..648eeb7c1e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "Keine übereinstimmenden Aufgaben" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..2b579942af --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, tasks", + "recentlyUsed": "zuletzt verwendete Aufgaben", + "configured": "konfigurierte Aufgaben", + "detected": "erkannte Aufgaben", + "customizeTask": "Aufgabe konfigurieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..5c5b463492 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Name einer neu zu startenden Aufgabe eingeben", + "noTasksMatching": "Keine übereinstimmenden Aufgaben", + "noTasksFound": "Keine neu zu startenden Aufgaben gefunden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..0f1aeaf2fd --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Geben Sie den Namen des auszuführenden Tasks ein.", + "noTasksMatching": "Keine übereinstimmenden Tasks", + "noTasksFound": "Es wurden keine Tasks gefunden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..03c69fbf25 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Type the name of a task to terminate", + "noTasksMatching": "Keine übereinstimmenden Tasks", + "noTasksFound": "No tasks to terminate found" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..648eeb7c1e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "Keine übereinstimmenden Aufgaben" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..f91da4f375 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Warnung: \"options.cwd\" muss vom Typ \"string\" sein. Der Wert {0} wird ignoriert.\n", + "ConfigurationParser.noargs": "Fehler: Befehlsargumente müssen ein Array aus Zeichenfolgen sein. Angegebener Wert:\n{0}", + "ConfigurationParser.noShell": "Warnung: Die Shell-Konfiguration wird nur beim Ausführen von Tasks im Terminal unterstützt.", + "ConfigurationParser.noName": "Fehler: Der Problemabgleich im Deklarationsbereich muss einen Namen besitzen:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Warnung: Der definierte Problemabgleich ist unbekannt. Die folgenden Typen werden unterstützt: string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Fehler: Ungültiger ProblemMatcher-Verweis: {0}\n", + "ConfigurationParser.noTaskName": "Fehler: Tasks müssen eine Eigenschaft \"TaskName\" angeben. Der Task wird ignoriert.\n{0}\n", + "taskConfiguration.shellArgs": "Warnung: Die Aufgabe \"{0}\" ist ein Shellbefehl, und der Befehlsname oder eines seiner Argumente enthält Leerzeichen ohne Escapezeichen. Führen Sie Argumente im Befehl zusammen, um eine korrekte Angabe der Befehlszeile sicherzustellen.", + "taskConfiguration.noCommandOrDependsOn": "Fehler: Aufgabe \"{0}\" definiert keinen Befehl bzw. keine depondsOn-Eigenschaft. Die Aufgabe wird ignoriert. Die Definition lautet:\n{1}", + "taskConfiguration.noCommand": "Fehler: Aufgabe \"{0}\" definiert keinen Befehl. Die Aufgabe wird ignoriert. Die Definition lautet:\n{1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..366030dd3e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "Der tatsächliche Aufgabentyp", + "TaskDefinition.properties": "Zusätzliche Eigenschaften des Aufgabentyps", + "TaskTypeConfiguration.noType": "In der Konfiguration des Aufgabentyps fehlt die erforderliche taskType-Eigenschaft.", + "TaskDefinitionExtPoint": "Trägt Aufgabenarten bei" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..05ae3bb68f --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": "Führt den .NET Core-Buildbefehl aus.", + "msbuild": "Führt das Buildziel aus.", + "externalCommand": "Ein Beispiel für das Ausführen eines beliebigen externen Befehls.", + "Maven": "Führt allgemeine Maven-Befehle aus." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..939f5fc3d0 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "Weitere Befehlsoptionen", + "JsonSchema.options.cwd": "Das aktuelle Arbeitsverzeichnis des ausgeführten Programms oder Skripts. Wenn keine Angabe erfolgt, wird das aktuelle Arbeitsbereich-Stammverzeichnis des Codes verwendet.", + "JsonSchema.options.env": "Die Umgebung des ausgeführten Programms oder der Shell. Wenn keine Angabe erfolgt, wird Umgebung des übergeordneten Prozesses verwendet.", + "JsonSchema.shellConfiguration": "Konfiguriert die zu verwendende Shell.", + "JsonSchema.shell.executable": "Die zu verwendende Shell.", + "JsonSchema.shell.args": "Die Shell-Argumente.", + "JsonSchema.command": "Der auszuführende Befehl. Es kann sich um ein externes Programm oder einen Shellbefehl handeln.", + "JsonSchema.tasks.args": "Argumente, die an den Befehl übergeben werden, wenn diese Aufgabe aufgerufen wird.", + "JsonSchema.tasks.taskName": "Der Name des Tasks.", + "JsonSchema.tasks.windows": "Windows-spezifische Befehlskonfiguration", + "JsonSchema.tasks.mac": "Mac-spezifische Befehlskonfiguration", + "JsonSchema.tasks.linux": "Linux-spezifische Befehlskonfiguration", + "JsonSchema.tasks.suppressTaskName": "Steuert, ob der Taskname dem Befehl als Argument hinzugefügt wird. Wenn keine Angabe erfolgt, wird der global definierte Wert verwendet.", + "JsonSchema.tasks.showOutput": "Steuert, ob die Ausgabe des aktuell ausgeführten Tasks angezeigt wird. Wenn keine Angabe erfolgt, wird der global definierte Wert verwendet.", + "JsonSchema.echoCommand": "Steuert, ob der ausgeführte Befehl in der Ausgabe angezeigt wird. Der Standardwert ist \"false\".", + "JsonSchema.tasks.watching.deprecation": "Veraltet. Verwenden Sie stattdessen \"isBackground\".", + "JsonSchema.tasks.watching": "Gibt an, ob der ausgeführte Task aktiv bleibt, und überwacht das Dateisystem.", + "JsonSchema.tasks.background": "Ob die ausgeführte Aufgabe weiterhin besteht und im Hintergrund ausgeführt wird.", + "JsonSchema.tasks.promptOnClose": "Ob der Benutzer aufgefordert wird, wenn VS Code bei einer aktuell ausgeführten Aufgabe geschlossen wird.", + "JsonSchema.tasks.build": "Ordnet diesen Task dem Standardbuildbefehl des Codes zu.", + "JsonSchema.tasks.test": "Ordnet diesen Task dem Standardtestbefehl des Codes zu.", + "JsonSchema.tasks.matchers": "Die zu verwendenden Problemabgleicher. Es kann sich um eine Zeichenfolge, eine Problemabgleicherdefinition oder ein Array aus Zeichenfolgen und Problemabgleichern handeln.", + "JsonSchema.args": "Weitere Argumente, die an den Befehl übergeben werden.", + "JsonSchema.showOutput": "Steuert, ob die Ausgabe des aktuell ausgeführten Tasks angezeigt wird. Wenn keine Angabe erfolgt, wird \"always\" verwendet.", + "JsonSchema.watching.deprecation": "Veraltet. Verwenden Sie stattdessen \"isBackground\".", + "JsonSchema.watching": "Gibt an, ob der ausgeführte Task aktiv bleibt, und überwacht das Dateisystem.", + "JsonSchema.background": "Ob die ausgeführte Aufgabe weiterhin besteht und im Hintergrund ausgeführt wird.", + "JsonSchema.promptOnClose": "Gibt an, ob dem Benutzer eine Eingabeaufforderung angezeigt wird, wenn VS Code mit einem aktuell ausgeführten Hintergrundtask geschlossen wird.", + "JsonSchema.suppressTaskName": "Steuert, ob der Taskname dem Befehl als Argument hinzugefügt wird. Der Standardwert ist \"false\".", + "JsonSchema.taskSelector": "Ein Präfix zum Angeben, dass ein Argument ein Task ist.", + "JsonSchema.matchers": "Die zu verwendenden Problemabgleicher. Es kann sich um eine Zeichenfolge, eine Problemabgleicherdefinition oder ein Array aus Zeichenfolgen und Problemabgleichern handeln.", + "JsonSchema.tasks": "Die Taskkonfigurationen. Normalerweise sind diese Erweiterungen der bereits in der externen Taskausführung definierten Tasks." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..482fc53892 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "Die Versionsnummer der Konfiguration.", + "JsonSchema._runner": "Die Ausführung ist abgestuft. Verwenden Sie die offizielle Ausführungseigenschaft.", + "JsonSchema.runner": "Definiert, ob die Aufgabe als Prozess ausgeführt wird, und die Ausgabe wird im Ausgabefenster oder innerhalb des Terminals angezeigt.", + "JsonSchema.windows": "Windows-spezifische Befehlskonfiguration", + "JsonSchema.mac": "Mac-spezifische Befehlskonfiguration", + "JsonSchema.linux": "Linux-spezifische Befehlskonfiguration", + "JsonSchema.shell": "Gibt an, ob der Befehl ein Shellbefehl oder ein externes Programm ist. Wenn keine Angabe erfolgt, ist der Standardwert \"false\"." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..372cce95ed --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "Gibt an, ob der Befehl ein Shellbefehl oder ein externes Programm ist. Wenn keine Angabe erfolgt, ist der Standardwert \"false\".", + "JsonSchema.tasks.isShellCommand.deprecated": "Die isShellCommand-Eigenschaft ist veraltet. Verwenden Sie stattdessen die type-Eigenschaft der Aufgabe und die Shell-Eigenschaft in den Optionen. Weitere Informationen finden Sie auch in den Anmerkungen zur Version 1.14. ", + "JsonSchema.tasks.dependsOn.string": "Eine weitere Aufgabe, von der diese Aufgabe abhängt.", + "JsonSchema.tasks.dependsOn.array": "Die anderen Aufgaben, von denen diese Aufgabe abhängt.", + "JsonSchema.tasks.presentation": "Konfiguriert das zum Darstellen der Aufgabenausgabe verwendete Panel und liest die Eingabe.", + "JsonSchema.tasks.presentation.echo": "Steuert, ob der ausgeführte Befehl im Panel angezeigt wird. Der Standardwert ist \"true\". ", + "JsonSchema.tasks.presentation.focus": "Steuert, ob das Panel den Fokus hat. der Standardwert ist \"false\". Bei Einstellung auf \"true\" wird das Panel ebenfalls angezeigt.", + "JsonSchema.tasks.presentation.reveal.always": "Zeigt immer das Terminal an, wenn diese Aufgabe ausgeführt wird.", + "JsonSchema.tasks.presentation.reveal.silent": "Zeigt das Terminal nur an, wenn der Aufgabe kein Problemabgleicher zugeordnet ist und bei der Ausführung Fehler auftreten.", + "JsonSchema.tasks.presentation.reveal.never": "Zeigt das Terminal beim Ausführen dieser Aufgabe nie an.", + "JsonSchema.tasks.presentation.reveals": "Steuert, ob das Panel, das die Aufgabe ausführt, angezeigt wird. Der Standardwert ist \"always\".", + "JsonSchema.tasks.presentation.instance": "Steuert, ob das Panel von Aufgaben gemeinsam genutzt wird, ob es dieser Aufgabe zugewiesen wird oder ob bei jeder Ausführung ein neues Panel erstellt wird.", + "JsonSchema.tasks.terminal": "Die terminal-Eigenschaft ist veraltet. Verwenden Sie stattdessen \"presentation\". ", + "JsonSchema.tasks.group.kind": "Die Ausführungsgruppe der Aufgabe.", + "JsonSchema.tasks.group.isDefault": "Definiert, ob diese Aufgabe die Standardaufgabe in der Gruppe ist.", + "JsonSchema.tasks.group.defaultBuild": "Markiert die Aufgaben als Standardbuildaufgabe.", + "JsonSchema.tasks.group.defaultTest": "Markiert die Aufgaben als Standardtestaufgabe.", + "JsonSchema.tasks.group.build": "Markiert die Aufgaben als eine Buildaufgabe, die über den Befehl \"Buildtask ausführen\" zugänglich ist.", + "JsonSchema.tasks.group.test": "Markiert die Aufgaben als eine Testaufgabe, die über den Befehl \"Testtask ausführen\" zugänglich ist.", + "JsonSchema.tasks.group.none": "Weist die Aufgabe keiner Gruppe zu.", + "JsonSchema.tasks.group": "Definiert die Ausführungsgruppe, zu der diese Aufgabe gehört. Zum Hinzufügen der Aufgabe zur Buildgruppe wird \"build\" unterstützt und zum Hinzufügen zur Testgruppe \"test\".", + "JsonSchema.tasks.type": "Definiert, ob die Aufgabe als Prozess oder als Befehl innerhalb einer Shell ausgeführt wird. Standardmäßig wird sie als Prozess ausgeführt.", + "JsonSchema.version": "Die Versionsnummer der Konfiguration.", + "JsonSchema.tasks.identifier": "Ein vom Benutzer definierter Bezeichner, mit dem in \"launch.json\" oder in einer dependsOn-Klausel auf die Aufgabe verwiesen wird.", + "JsonSchema.tasks.taskLabel": "Die Bezeichnung der Aufgabe", + "JsonSchema.tasks.taskName": "Der Name des Tasks.", + "JsonSchema.tasks.taskName.deprecated": "Die name-Eigenschaft der Aufgabe ist veraltet. Verwenden Sie stattdessen die label-Eigenschaft.", + "JsonSchema.tasks.background": "Ob die ausgeführte Aufgabe weiterhin besteht und im Hintergrund ausgeführt wird.", + "JsonSchema.tasks.promptOnClose": "Ob der Benutzer aufgefordert wird, wenn VS Code bei einer aktuell ausgeführten Aufgabe geschlossen wird.", + "JsonSchema.tasks.matchers": "Die zu verwendenden Problemabgleicher. Es kann sich um eine Zeichenfolge, eine Problemabgleicherdefinition oder ein Array aus Zeichenfolgen und Problemabgleichern handeln.", + "JsonSchema.customizations.customizes.type": "Der anzupassende Aufgabentyp", + "JsonSchema.tasks.customize.deprecated": "Die customize-Eigenschaft ist veraltet. Informationen zur Migration zum neuen Ansatz für die Aufgabenanpassung finden Sie in den Anmerkungen zur Version 1.14.", + "JsonSchema.tasks.showOputput.deprecated": "Die showOutput-Eigenschaft ist veraltet. Verwenden Sie stattdessen die reveal-Eigenschaft innerhalb der presentation-Eigenschaft. Weitere Informationen finden Sie auch in den Anmerkungen zur Version 1.14.", + "JsonSchema.tasks.echoCommand.deprecated": "Die echoCommand-Eigenschaft ist veraltet. Verwenden Sie stattdessen die echo-Eigenschaft innerhalb der presentation-Eigenschaft. Weitere Informationen finden Sie auch in den Anmerkungen zur Version 1.14. ", + "JsonSchema.tasks.suppressTaskName.deprecated": "Die suppressTaskName-Eigenschaft ist veraltet. Binden Sie den Befehl mit den zugehörigen Argumenten stattdessen in die Aufgabe ein. Weitere Informationen finden Sie auch in den Anmerkungen zur Version 1.14.", + "JsonSchema.tasks.isBuildCommand.deprecated": "Die isBuildCommand-Eigenschaft ist veraltet. Verwenden Sie stattdessen die group-Eigenschaft. Weitere Informationen finden Sie auch in den Anmerkungen zur Version 1.14. ", + "JsonSchema.tasks.isTestCommand.deprecated": "Die isTestCommand-Eigenschaft ist veraltet. Verwenden Sie stattdessen die group-Eigenschaft. Weitere Informationen finden Sie auch in den Anmerkungen zur Version 1.14.", + "JsonSchema.tasks.taskSelector.deprecated": "Die taskSelector-Eigenschaft ist veraltet. Binden Sie den Befehl mit den zugehörigen Argumenten stattdessen in die Aufgabe ein. Weitere Informationen finden Sie auch in den Anmerkungen zur Version 1.14.", + "JsonSchema.windows": "Windows-spezifische Befehlskonfiguration", + "JsonSchema.mac": "Mac-spezifische Befehlskonfiguration", + "JsonSchema.linux": "Linux-spezifische Befehlskonfiguration" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..7a0887a7d3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "Aufgaben", + "ConfigureTaskRunnerAction.noWorkspace": "Aufgaben sind nur für einen Arbeitsbereichsordner verfügbar.", + "ConfigureTaskRunnerAction.quickPick.template": "Taskausführung auswählen", + "ConfigureTaskRunnerAction.autoDetecting": "Tasks für {0} werden automatisch erkannt.", + "ConfigureTaskRunnerAction.autoDetect": "Fehler bei der automatischen Erkennung des Tasksystems. Die Standardvorlage wird verwendet. Einzelheiten finden Sie in der Taskausgabe.", + "ConfigureTaskRunnerAction.autoDetectError": "Bei der automatischen Erkennung des Tasksystems sind Fehler aufgetreten. Einzelheiten finden Sie in der Taskausgabe.", + "ConfigureTaskRunnerAction.failed": "Die Datei \"tasks.json\" kann nicht im Ordner \".vscode\" erstellt werden. Einzelheiten finden Sie in der Taskausgabe.", + "ConfigureTaskRunnerAction.label": "Taskausführung konfigurieren", + "ConfigureBuildTaskAction.label": "Buildtask konfigurieren", + "CloseMessageAction.label": "Schließen", + "ShowTerminalAction.label": "Terminal anzeigen", + "problems": "Probleme", + "manyMarkers": "mehr als 99", + "runningTasks": "Aktive Aufgaben anzeigen", + "tasks": "Aufgaben", + "TaskSystem.noHotSwap": "Zum Ändern des Aufgabenausführungsmoduls muss das Fenster erneut geladen werden.", + "TaskService.noBuildTask1": "Keine Buildaufgabe definiert. Markieren Sie eine Aufgabe mit 'isBuildCommand' in der tasks.json-Datei.", + "TaskService.noBuildTask2": "Es ist keine Buildaufgabe definiert. Markieren Sie eine Aufgabe in der Datei \"tasks.json\" als \"Buildgruppe\". ", + "TaskService.noTestTask1": "Keine Testaufgabe definiert. Markieren Sie eine Aufgabe mit 'isTestCommand' in der tasks.json-Datei.", + "TaskService.noTestTask2": "Es ist keine Testaufgabe definiert. Markieren Sie eine Aufgabe in der Datei \"tasks.json\" als \"Testgruppe\". ", + "TaskServer.noTask": "Die angeforderte auszuführende Aufgabe {0} wurde nicht gefunden.", + "TaskService.attachProblemMatcher.continueWithout": "Ohne Überprüfung der Aufgabenausgabe fortsetzen", + "TaskService.attachProblemMatcher.never": "Aufgabenausgabe nie überprüfen", + "TaskService.attachProblemMatcher.learnMoreAbout": "Weitere Informationen zur Überprüfung der Aufgabenausgabe", + "selectProblemMatcher": "Fehler- und Warnungsarten auswählen, auf die die Aufgabenausgabe überprüft werden soll", + "customizeParseErrors": "Die aktuelle Aufgabenkonfiguration weist Fehler auf. Beheben Sie die Fehler, bevor Sie eine Aufgabe anpassen.", + "moreThanOneBuildTask": "In \"tasks.json\" sind mehrere Buildaufgaben definiert. Die erste wird ausgeführt.\n", + "TaskSystem.activeSame.background": "Die Aufgabe \"{0}\" ist bereits im Hintergrundmodus aktiv. Klicken Sie zum Beenden der Aufgabe im Menü \"Aufgaben\" auf \"Aufgabe beenden\".", + "TaskSystem.activeSame.noBackground": "Die Aufgabe \"{0}\" ist bereits aktiv. Klicken Sie zum Beenden der Aufgabe im Menü \"Aufgaben\" auf \"Aufgabe beenden\".", + "TaskSystem.active": "Eine aktive Aufgabe wird bereits ausgeführt. Beenden Sie diese, bevor Sie eine andere Aufgabe ausführen.", + "TaskSystem.restartFailed": "Fehler beim Beenden und Neustarten der Aufgabe \"{0}\".", + "TaskSystem.configurationErrors": "Fehler: Die angegebene Aufgabenkonfiguration weist Validierungsfehler auf und kann nicht verwendet werden. Beheben Sie zuerst die Fehler.", + "TaskSystem.invalidTaskJson": "Fehler: Der Inhalt der Datei \"tasks.json\" weist Syntaxfehler auf. Bitte korrigieren sie diese, bevor Sie einen Task ausführen.\n", + "TaskSystem.runningTask": "Es wird ein Task ausgeführt wird. Möchten Sie ihn beenden?", + "TaskSystem.terminateTask": "&&Task beenden", + "TaskSystem.noProcess": "Der gestartete Task ist nicht mehr vorhanden. Wenn der Task Hintergrundprozesse erzeugt hat, kann das Beenden von VS Code ggf. zu verwaisten Prozessen führen. Starten Sie den letzten Hintergrundprozess mit einer wait-Kennzeichnung, um dies zu vermeiden.", + "TaskSystem.exitAnyways": "&&Trotzdem beenden", + "TerminateAction.label": "Aufgabe beenden", + "TaskSystem.unknownError": "Fehler beim Ausführen eines Tasks. Details finden Sie im Taskprotokoll.", + "TaskService.noWorkspace": "Aufgaben sind nur für einen Arbeitsbereichsordner verfügbar.", + "recentlyUsed": "zuletzt verwendete Aufgaben", + "configured": "konfigurierte Aufgaben", + "detected": "erkannte Aufgaben", + "TaskService.fetchingBuildTasks": "Buildaufgaben werden abgerufen...", + "TaskService.noBuildTaskTerminal": "Es wurde keine Buildaufgabe gefunden. Klicken Sie auf \"Buildtask konfigurieren\", um eine Aufgabe zu definieren.", + "TaskService.pickBuildTask": "Auszuführende Buildaufgabe auswählen", + "TaskService.fetchingTestTasks": "Testaufgaben werden abgerufen...", + "TaskService.noTestTaskTerminal": "Es wurde keine Testaufgabe gefunden. Klicken Sie auf \"Taskausführung konfigurieren\", um eine Aufgabe zu definieren.", + "TaskService.pickTestTask": "Auszuführende Testaufgabe auswählen", + "TaskService.noTaskRunning": "Zurzeit wird keine Aufgabe ausgeführt.", + "TaskService.tastToTerminate": "Zu beendende Aufgabe auswählen", + "TerminateAction.noProcess": "Der gestartete Prozess ist nicht mehr vorhanden. Wenn der Task Hintergrundtasks erzeugt hat, kann das Beenden von VS Code ggf. zu verwaisten Prozessen führen.", + "TerminateAction.failed": "Fehler beim Beenden des ausgeführten Tasks.", + "TaskService.noTaskToRestart": "Es ist keine neu zu startende Aufgabe vorhanden.", + "TaskService.tastToRestart": "Neu zu startende Aufgabe auswählen", + "TaskService.defaultBuildTaskExists": "{0} ist bereits als Standardbuildaufgabe markiert.", + "TaskService.pickDefaultBuildTask": "Als Standardbuildaufgabe zu verwendende Aufgabe auswählen", + "TaskService.defaultTestTaskExists": "{0} ist bereits als Standardtestaufgabe markiert.", + "TaskService.pickDefaultTestTask": "Als Standardtestaufgabe zu verwendende Aufgabe auswählen", + "TaskService.noTaskIsRunning": "Es wird keine Aufgabe ausgeführt.", + "TaskService.pickShowTask": "Aufgabe zum Anzeigen der Ausgabe auswählen", + "ShowLogAction.label": "Taskprotokoll anzeigen", + "RunTaskAction.label": "Task ausführen", + "RestartTaskAction.label": "Ausgeführte Aufgabe neu starten", + "ShowTasksAction.label": "Aktive Aufgaben anzeigen", + "BuildAction.label": "Buildtask ausführen", + "TestAction.label": "Testtask ausführen", + "ConfigureDefaultBuildTask.label": "Standardbuildaufgabe konfigurieren ", + "ConfigureDefaultTestTask.label": "Standardtestaufgabe konfigurieren", + "quickOpen.task": "Task ausführen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..caead96cb2 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "Aufgaben" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..001bbf7eb3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "Unbekannter Fehler beim Ausführen eines Tasks. Details finden Sie im Taskausgabeprotokoll.", + "TerminalTaskSystem.terminalName": "Aufgabe - {0}", + "reuseTerminal": "Das Terminal wird von Aufgaben wiederverwendet, drücken Sie zum Schließen eine beliebige Taste.", + "TerminalTaskSystem": "Ein Shell-Befehl kann nicht auf einem UNC-Laufwerk ausgeführt werden.", + "unkownProblemMatcher": "Der Problemabgleicher {0} kann nicht aufgelöst werden. Der Abgleicher wird ignoriert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..f904ff3215 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "Die Ausführung von \"gulp -tasks-simple\" hat keine Tasks aufgelistet. Haben Sie \"npm install\" ausgeführt?", + "TaskSystemDetector.noJakeTasks": "Die Ausführung von \"jake -tasks\" hat keine Tasks aufgelistet. Haben Sie \"npm install\" ausgeführt?", + "TaskSystemDetector.noGulpProgram": "Gulp ist auf Ihrem System nicht installiert. Führen Sie \"npm install -g gulp\" aus, um die Anwendung zu installieren.", + "TaskSystemDetector.noJakeProgram": "Jake ist auf Ihrem System nicht installiert. Führen Sie \"npm install -g jake\" aus, um die Anwendung zu installieren.", + "TaskSystemDetector.noGruntProgram": "Grunt ist auf Ihrem System nicht installiert. Führen Sie \"npm install -g grunt\" aus, um die Anwendung zu installieren.", + "TaskSystemDetector.noProgram": "Das Programm {0} wurde nicht gefunden. Die Meldung lautet: {1}", + "TaskSystemDetector.buildTaskDetected": "Ein Buildtask namens \"{0}\" wurde erkannt.", + "TaskSystemDetector.testTaskDetected": "Ein Testtask namens \"{0}\" wurde erkannt." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..ff790d32b2 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "Unbekannter Fehler beim Ausführen eines Tasks. Details finden Sie im Taskausgabeprotokoll.", + "TaskRunnerSystem.watchingBuildTaskFinished": "\nDie Überwachung der Buildtasks wurde beendet.", + "TaskRunnerSystem.childProcessError": "Failed to launch external program {0} {1}.", + "TaskRunnerSystem.cancelRequested": "\nDer Task \"{0}\" wurde durch eine Benutzeranforderung beendet.", + "unkownProblemMatcher": "Der Problemabgleicher {0} kann nicht aufgelöst werden. Der Abgleicher wird ignoriert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..1a6938cb12 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Warnung: \"options.cwd\" muss vom Typ \"string\" sein. Der Wert {0} wird ignoriert.\n", + "ConfigurationParser.noargs": "Fehler: Befehlsargumente müssen ein Array aus Zeichenfolgen sein. Angegebener Wert:\n{0}", + "ConfigurationParser.noShell": "Warnung: Die Shell-Konfiguration wird nur beim Ausführen von Tasks im Terminal unterstützt.", + "ConfigurationParser.noName": "Fehler: Der Problemabgleich im Deklarationsbereich muss einen Namen besitzen:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Warnung: Der definierte Problemabgleich ist unbekannt. Die folgenden Typen werden unterstützt: string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Fehler: Ungültiger ProblemMatcher-Verweis: {0}\n", + "ConfigurationParser.noTaskType": "Fehler: Die Aufgabenkonfiguration muss eine type-Eigenschaft enthalten. Die Konfiguration wird ignoriert.\n{0}\n", + "ConfigurationParser.noTypeDefinition": "Fehler: Der registrierte Aufgabentyp \"{0}\" ist nicht vorhanden. Wurde möglicherweise eine Erweiterung nicht installiert, die den entsprechenden Aufgabenanbieter bereitstellt?", + "ConfigurationParser.notCustom": "Fehler: Die Aufgabe ist nicht als benutzerdefinierte Aufgabe deklariert. Die Konfiguration wird ignoriert.\n{0}\n", + "ConfigurationParser.noTaskName": "Fehler: Tasks müssen eine Eigenschaft \"TaskName\" angeben. Der Task wird ignoriert.\n{0}\n", + "taskConfiguration.shellArgs": "Warnung: Die Aufgabe \"{0}\" ist ein Shellbefehl, und der Befehlsname oder eines seiner Argumente enthält Leerzeichen ohne Escapezeichen. Führen Sie Argumente im Befehl zusammen, um eine korrekte Angabe der Befehlszeile sicherzustellen.", + "taskConfiguration.noCommandOrDependsOn": "Fehler: Aufgabe \"{0}\" definiert keinen Befehl bzw. keine depondsOn-Eigenschaft. Die Aufgabe wird ignoriert. Die Definition lautet:\n{1}", + "taskConfiguration.noCommand": "Fehler: Aufgabe \"{0}\" definiert keinen Befehl. Die Aufgabe wird ignoriert. Die Definition lautet:\n{1}", + "TaskParse.noOsSpecificGlobalTasks": "Die Aufgabenversion 2.0.0 unterstützt globale betriebssystemspezifische Aufgaben nicht. Konvertieren Sie sie in eine Aufgabe mit einem betriebssystemspezifischen Befehl. Folgende Aufgaben sind hiervon betroffen:\n{0}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..c28ef36915 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0}, Terminalauswahl", + "termCreateEntryAriaLabel": "{0}, neues Terminal erstellen", + "'workbench.action.terminal.newplus": "$(plus) Neues integriertes Terminal erstellen", + "noTerminalsMatching": "Keine übereinstimmenden Terminals", + "noTerminalsFound": "Keine geöffneten Terminals" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..6c51e361e3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "Alle geöffneten Terminals anzeigen", + "terminalIntegratedConfigurationTitle": "Integriertes Terminal", + "terminal.integrated.shell.linux": "Der Pfad der Shell, den das Terminal unter Linux verwendet.", + "terminal.integrated.shellArgs.linux": "Die Befehlszeilenargumente, die für das Linux-Terminal verwendet werden sollen.", + "terminal.integrated.shell.osx": "Der Pfad der Shell, den das Terminal unter OS X verwendet.", + "terminal.integrated.shellArgs.osx": "Die Befehlszeilenargumente, die für das OS X-Terminal verwendet werden sollen.", + "terminal.integrated.shell.windows": "Der Pfad der Shell, den das Terminal unter Windows verwendet, wenn in Windows enthaltene Terminals verwendet werden (cmd, PowerShell oder Bash unter Ubuntu).", + "terminal.integrated.shellArgs.windows": "Die Befehlszeilenargumente, die im Windows-Terminal verwendet werden sollen.", + "terminal.integrated.rightClickCopyPaste": "Wenn dies festgelegt ist, erscheint das Kontextmenü bei einem Rechtsklick im Terminal nicht mehr. Stattdessen erfolgen die Vorgänge Kopieren, wenn eine Auswahl vorgenommen wurde, sowie Einfügen, wenn keine Auswahl vorgenommen wurde.", + "terminal.integrated.fontFamily": "Steuert die Schriftartfamilie des Terminals. Der Standardwert ist \"editor.fontFamily\".", + "terminal.integrated.fontLigatures": "Steuert, ob Schriftartligaturen im Terminal aktiviert sind.", + "terminal.integrated.fontSize": "Steuert den Schriftgrad des Terminals in Pixeln.", + "terminal.integrated.lineHeight": "Steuert die Zeilenhöhe für das Terminal. Dieser Wert wird mit dem Schriftgrad des Terminals multipliziert, um die tatsächliche Zeilenhöhe in Pixeln zu erhalten.", + "terminal.integrated.enableBold": "Gibt an, ob Fettdruck im Terminal aktiviert werden soll. Dies muss durch die Terminalshell unterstützt werden.", + "terminal.integrated.cursorBlinking": "Steuert, ob der Terminalcursor blinkt.", + "terminal.integrated.cursorStyle": "Steuert den Stil des Terminalcursors.", + "terminal.integrated.scrollback": "Steuert die maximale Anzahl von Zeilen, die das Terminal im Puffer beibehält.", + "terminal.integrated.setLocaleVariables": "Steuert, ob Gebietsschemavariablen beim Start des Terminals festgelegt werden. Der Standardwert ist unter OS X TRUE und FALSE auf anderen Plattformen.", + "terminal.integrated.cwd": "Ein expliziter Startpfad zum Starten des Terminals, dies dient als das aktuelle Arbeitsverzeichnis (CWD) für den Shellprozess. Dies ist insbesondere in Arbeitsbereichseinstellungen praktisch, wenn das Stammverzeichnis kein passendes CWD ist.", + "terminal.integrated.confirmOnExit": "Ob aktive Terminalsitzungen beim Beenden bestätigt werden sollen.", + "terminal.integrated.commandsToSkipShell": "Eine Sammlung von Befehls-IDs, deren Tastenzuordnungen nicht an die Shell gesendet und die stattdessen immer durch Code verarbeitet werden. Dies ermöglicht die Verwendung von Tastenzuordnungen, die normalerweise von der Shell verwendet würden, um das gleiche Verhalten wie bei einem Terminal ohne Fokus zu erzielen, z. B. STRG+P zum Starten von Quick Open.", + "terminal.integrated.env.osx": "Objekt mit Umgebungsvariablen, das dem unter OS X vom Terminal zu verwendenden VS Code-Prozess hinzugefügt wird", + "terminal.integrated.env.linux": "Objekt mit Umgebungsvariablen, das dem unter Linux vom Terminal zu verwendenden VS Code-Prozess hinzugefügt wird", + "terminal.integrated.env.windows": "Objekt mit Umgebungsvariablen, das dem unter Windows vom Terminal zu verwendenden VS Code-Prozess hinzugefügt wird", + "terminal": "Terminal", + "terminalCategory": "Terminal", + "viewCategory": "Anzeigen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..eda3d50889 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "Integriertes Terminal umschalten", + "workbench.action.terminal.kill": "Aktive Terminalinstanz beenden", + "workbench.action.terminal.kill.short": "Terminal beenden", + "workbench.action.terminal.quickKill": "Terminalinstanz beenden", + "workbench.action.terminal.copySelection": "Auswahl kopieren", + "workbench.action.terminal.selectAll": "Alles auswählen", + "workbench.action.terminal.deleteWordLeft": "Wort links löschen", + "workbench.action.terminal.deleteWordRight": "Wort rechts löschen", + "workbench.action.terminal.new": "Neues integriertes Terminal erstellen", + "workbench.action.terminal.new.short": "Neues Terminal", + "workbench.action.terminal.focus": "Fokus im Terminal", + "workbench.action.terminal.focusNext": "Fokus im nächsten Terminal", + "workbench.action.terminal.focusAtIndex": "Terminal {0} im Fokus", + "workbench.action.terminal.focusPrevious": "Fokus im vorherigen Terminal", + "workbench.action.terminal.paste": "In aktives Terminal einfügen", + "workbench.action.terminal.DefaultShell": "Standardshell auswählen", + "workbench.action.terminal.runSelectedText": "Ausgewählten Text im aktiven Terminal ausführen", + "workbench.action.terminal.runActiveFile": "Aktive Datei im aktiven Terminal ausführen", + "workbench.action.terminal.runActiveFile.noFile": "Nur Dateien auf der Festplatte können im Terminal ausgeführt werden.", + "workbench.action.terminal.switchTerminalInstance": "Terminalinstanz umschalten", + "workbench.action.terminal.scrollDown": "Nach unten scrollen (Zeile)", + "workbench.action.terminal.scrollDownPage": "Nach unten scrollen (Seite)", + "workbench.action.terminal.scrollToBottom": "Bildlauf nach unten", + "workbench.action.terminal.scrollUp": "Nach oben scrollen (Zeile)", + "workbench.action.terminal.scrollUpPage": "Nach oben scrollen (Seite)", + "workbench.action.terminal.scrollToTop": "Bildlauf nach oben", + "workbench.action.terminal.clear": "Löschen", + "workbench.action.terminal.allowWorkspaceShell": "Shell-Konfiguration des Arbeitsbereichs zulassen", + "workbench.action.terminal.disallowWorkspaceShell": "Verbiete Workspace Shell Konfiguration", + "workbench.action.terminal.rename": "Umbenennen", + "workbench.action.terminal.rename.prompt": "Terminalnamen eingeben", + "workbench.action.terminal.focusFindWidget": "Widget zum Anzeigen der Suche mit Fokus", + "workbench.action.terminal.hideFindWidget": "Widget zum Ausblenden der Suche", + "nextTerminalFindTerm": "Nächsten Suchbegriff anzeigen", + "previousTerminalFindTerm": "Vorherigen Suchbegriff anzeigen", + "quickOpenTerm": "Terminal: Aktives Terminal wechseln" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..df72fe2f5d --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "Die Hintergrundfarbe des Terminals, dies ermöglicht eine unterschiedliche Färbung des Terminals im Panel.", + "terminal.foreground": "Die Vordergrundfarbe des Terminal.", + "terminalCursor.foreground": "Die Vordergrundfarbe des Terminalcursors.", + "terminalCursor.background": "Die Hintergrundfarbe des Terminalcursors. Ermöglicht das Anpassen der Farbe eines Zeichens, das von einem Blockcursor überdeckt wird.", + "terminal.ansiColor": "\"{0}\": ANSI-Farbe im Terminal" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..722eb9f783 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "Möchten Sie zulassen, dass {0} (als Arbeitsbereichseinstellung definiert) im Terminal gestartet wird?", + "allow": "Allow", + "disallow": "Disallow" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..ee2488c6f6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Suchen", + "placeholder.find": "Suchen", + "label.previousMatchButton": "Vorherige Übereinstimmung", + "label.nextMatchButton": "Nächste Übereinstimmung", + "label.closeButton": "Schließen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..74ef571483 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "Das Terminal enthält keine Auswahl zum Kopieren.", + "terminal.integrated.exitedWithCode": "Der Terminalprozess wurde mit folgendem Exitcode beendet: {0}", + "terminal.integrated.waitOnExit": "Betätigen Sie eine beliebige Taste, um das Terminal zu schließen.", + "terminal.integrated.launchFailed": "Fehler beim Starten des Terminalprozessbefehls \"{0}{1}\" (Exitcode: {2})." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..94400a811b --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "ALT + Mausklick zum Aufrufen des Links", + "terminalLinkHandler.followLinkCmd": "BEFEHLSTASTE + Mausklick zum Aufrufen des Links", + "terminalLinkHandler.followLinkCtrl": "STRG + Mausklick zum Aufrufen des Links" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..2fcd6e0bed --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "Kopieren", + "createNewTerminal": "Neues Terminal", + "paste": "Einfügen", + "selectAll": "Alles auswählen", + "clear": "Löschen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..004219acbe --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "Sie können die Standardterminalshell über die Schaltfläche \"Anpassen\" ändern.", + "customize": "Anpassen", + "cancel": "Abbrechen", + "never again": "OK, nicht mehr anzeigen", + "terminal.integrated.chooseWindowsShell": "Wählen Sie Ihre bevorzugte Terminalshell. Sie können diese später in Ihren Einstellungen ändern.", + "terminalService.terminalCloseConfirmationSingular": "Eine aktive Terminalsitzung ist vorhanden. Möchten Sie sie beenden?", + "terminalService.terminalCloseConfirmationPlural": "{0} aktive Terminalsitzungen sind vorhanden. Möchten Sie sie beenden?", + "yes": "Ja" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..a846c1c8da --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "Farbdesign", + "installColorThemes": "Zusätzliche Farbschemas installieren...", + "themes.selectTheme": "Farbdesign auswählen (eine Vorschau wird mit den Tasten NACH OBEN/NACH UNTEN angezeigt)", + "selectIconTheme.label": "Dateisymboldesign", + "installIconThemes": "Zusätzliche Dateisymbolschemas installieren...", + "noIconThemeLabel": "Keine", + "noIconThemeDesc": "Dateisymbole deaktivieren", + "problemChangingIconTheme": "Problem beim Festlegen des Symboldesigns: {0}", + "themes.selectIconTheme": "Dateisymboldesign auswählen", + "generateColorTheme.label": "Farbdesign aus aktuellen Einstellungen erstellen", + "preferences": "Einstellungen", + "developer": "Entwickler" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..ab3a66d368 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "Dieser Arbeitsbereich enthält Einstellungen, die nur in den Benutzereinstellungen festgelegt werden können ({0}).", + "openWorkspaceSettings": "Arbeitsbereichseinstellungen öffnen", + "openDocumentation": "Weitere Informationen", + "ignore": "Ignorieren" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/deu/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..4871d0b178 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "Anmerkungen zu dieser Version: {0}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..568f0788b4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "Anmerkungen zu dieser Version", + "updateConfigurationTitle": "Aktualisieren", + "updateChannel": "Konfiguriert, ob automatische Updates aus einem Updatekanal empfangen werden sollen. Erfordert einen Neustart nach der Änderung." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..1140cff2af --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "Jetzt aktualisieren", + "later": "Später", + "unassigned": "Nicht zugewiesen", + "releaseNotes": "Anmerkungen zu dieser Version", + "showReleaseNotes": "Anmerkungen zu dieser Version anzeigen", + "downloadNow": "Jetzt herunterladen", + "read the release notes": "Willkommen bei {0} v{1}! Möchten Sie die Hinweise zu dieser Version lesen?", + "licenseChanged": "Unsere Lizenzbedingungen haben sich geändert. Bitte lesen Sie die neuen Bedingungen.", + "license": "Lizenz lesen", + "neveragain": "Nie wieder anzeigen", + "64bitisavailable": "{0} für 64-Bit-Windows ist jetzt verfügbar!", + "learn more": "Weitere Informationen", + "updateIsReady": "Neues {0}-Update verfügbar.", + "thereIsUpdateAvailable": "Ein Update ist verfügbar.", + "updateAvailable": "{0} wird nach dem Neustart aktualisiert.", + "noUpdatesAvailable": "Zurzeit sind keine Updates verfügbar.", + "commandPalette": "Befehlspalette...", + "settings": "Einstellungen", + "keyboardShortcuts": "Tastenkombinationen", + "selectTheme.label": "Farbdesign", + "themes.selectIconTheme.label": "Dateisymboldesign", + "not available": "Aktualisierungen nicht verfügbar", + "checkingForUpdates": "Überprüfen auf Updates...", + "DownloadUpdate": "Verfügbares Update herunterladen", + "DownloadingUpdate": "Das Update wird heruntergeladen...", + "InstallingUpdate": "Update wird installiert...", + "restartToUpdate": "Für Update neu starten...", + "checkForUpdates": "Nach Aktualisierungen suchen..." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/deu/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..c85523a6b1 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0}-Aktionen", + "hideView": "Auf Randleiste ausblenden" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/deu/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..a11160ec43 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "Ansichten müssen ein Array sein.", + "requirestring": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "optstring": "Die Eigenschaft \"{0}\" kann ausgelassen werden oder muss vom Typ \"string\" sein.", + "vscode.extension.contributes.view.id": "Bezeichner der Ansicht. Damit können Sie einen Datenanbieter über die API \"vscode.window.registerTreeDataProviderForView\" registrieren. Er dient auch zum Aktivieren Ihrer Erweiterung, indem Sie das Ereignis \"onView:${id}\" für \"activationEvents\" registrieren.", + "vscode.extension.contributes.view.name": "Der visuell lesbare Name der Ansicht. Wird angezeigt", + "vscode.extension.contributes.view.when": "Bedingung, die zum Anzeigen dieser Ansicht erfüllt sein muss", + "vscode.extension.contributes.views": "Trägt Ansichten zum Editor bei.", + "views.explorer": "Explorer-Ansicht", + "views.debug": "Debugansicht", + "locationId.invalid": "{0}\" ist kein gültiger Ansichtenspeicherort" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/deu/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..7d805fe30b --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "Alle Befehle anzeigen", + "watermark.quickOpen": "Zu Datei wechseln", + "watermark.openFile": "Datei öffnen", + "watermark.openFolder": "Ordner öffnen", + "watermark.openFileFolder": "Datei oder Ordner öffnen", + "watermark.openRecent": "Zuletzt verwendete öffnen", + "watermark.newUntitledFile": "Neue unbenannte Datei", + "watermark.toggleTerminal": "Terminal umschalten", + "watermark.findInFiles": "In Dateien suchen", + "watermark.startDebugging": "Debuggen starten", + "watermark.unboundCommand": "Ungebunden", + "workbenchConfigurationTitle": "Workbench", + "tips.enabled": "Wenn diese Option aktiviert ist, werden Tipps zu Wasserzeichen angezeigt, wenn kein Editor geöffnet ist." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/deu/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..7c91394fee --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "Datei-Explorer", + "welcomeOverlay.search": "In Dateien suchen", + "welcomeOverlay.git": "Quellcodeverwaltung", + "welcomeOverlay.debug": "Starten und debuggen", + "welcomeOverlay.extensions": "Erweiterungen verwalten", + "welcomeOverlay.problems": "Fehler und Warnungen anzeigen", + "welcomeOverlay.commandPalette": "Alle Befehle suchen und ausführen", + "welcomeOverlay": "Benutzeroberflächenüberblick", + "hideWelcomeOverlay": "Schnittstellenüberblick ausblenden", + "help": "Hilfe" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..2d773a066d --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "Fortschrittliche Bearbeitung", + "welcomePage.start": "Starten", + "welcomePage.newFile": "Neue Datei", + "welcomePage.openFolder": "Ordner öffnen...", + "welcomePage.cloneGitRepository": "Git-Repository klonen...", + "welcomePage.recent": "Zuletzt verwendet", + "welcomePage.moreRecent": "Weitere Informationen...", + "welcomePage.noRecentFolders": "Keine kürzlich verwendeten Ordner", + "welcomePage.help": "Hilfe", + "welcomePage.keybindingsCheatsheet": "Druckbare Tastaturübersicht", + "welcomePage.introductoryVideos": "Einführungsvideos", + "welcomePage.tipsAndTricks": "Tipps und Tricks", + "welcomePage.productDocumentation": "Produktdokumentation", + "welcomePage.gitHubRepository": "GitHub-Repository", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "Willkommensseite beim Start anzeigen", + "welcomePage.customize": "Anpassen", + "welcomePage.installExtensionPacks": "Tools und Sprachen", + "welcomePage.installExtensionPacksDescription": "Unterstützung für {0} und {1} installieren", + "welcomePage.moreExtensions": "mehr", + "welcomePage.installKeymapDescription": "Tastenkombinationen installieren", + "welcomePage.installKeymapExtension": "Installieren Sie die Tastenkombinationen von {0} und {1}.", + "welcomePage.others": "Andere", + "welcomePage.colorTheme": "Farbdesign", + "welcomePage.colorThemeDescription": "Passen Sie das Aussehen des Editors und Ihres Codes an Ihre Wünsche an.", + "welcomePage.learn": "Lernen", + "welcomePage.showCommands": "Alle Befehle suchen und ausführen", + "welcomePage.showCommandsDescription": "Über die Befehlspalette ({0}) schnell auf Befehle zugreifen und nach Befehlen suchen", + "welcomePage.interfaceOverview": "Überblick über die Schnittstelle", + "welcomePage.interfaceOverviewDescription": "Erhalten Sie eine visuelle Überlagerung, die die wichtigsten Komponenten der Benutzeroberfläche hervorhebt.", + "welcomePage.deployToAzure": "Anwendungen in der Cloud bereitstellen", + "welcomePage.deployToAzureDescription": "Hier erfahren Sie, wie Sie Ihre Node-Apps in Azure App Service bereitstellen.", + "welcomePage.interactivePlayground": "Interaktiver Playground", + "welcomePage.interactivePlaygroundDescription": "Testen Sie die wichtigsten Editorfunktionen in einer kurzen exemplarischen Vorgehensweise." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..bf88fca078 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "Workbench", + "workbench.startupEditor.none": "Ohne Editor starten.", + "workbench.startupEditor.welcomePage": "Willkommensseite öffnen (Standard).", + "workbench.startupEditor.newUntitledFile": "Eine neue unbenannte Datei öffnen.", + "workbench.startupEditor": "Steuert, welcher Editor beim Start angezeigt wird, sofern kein Editor aus der vorherigen Sitzung wiederhergestellt wird. Wählen Sie \"none\" zum Starten ohne Editor, \"welcomePage\" zum Öffnen der Startseite (Standard) oder \"newUntitledFile\" zum Öffnen einer neuen unbenannten Datei (nur beim Öffnen eines leeren Arbeitsbereichs).", + "help": "Hilfe" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..4e447e75e6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "Willkommen", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "Azure-Erweiterungen anzeigen", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "Unterstützung für {0} ist bereits installiert.", + "welcomePage.willReloadAfterInstallingExtensionPack": "Nach dem Installieren zusätzlicher Unterstützung für {0} wird das Fenster neu geladen.", + "welcomePage.installingExtensionPack": "Zusätzliche Unterstützung für {0} wird installiert...", + "welcomePage.extensionPackNotFound": "Unterstützung für {0} mit der ID {1} wurde nicht gefunden.", + "welcomePage.keymapAlreadyInstalled": "Die {0} Tastenkombinationen sind bereits installiert.", + "welcomePage.willReloadAfterInstallingKeymap": "Das Fenster wird nach der Installation der {0}-Tastaturbefehle neu geladen.", + "welcomePage.installingKeymap": "Die {0}-Tastenkombinationen werden installiert...", + "welcomePage.keymapNotFound": "Die {0} Tastenkombinationen mit der ID {1} wurden nicht gefunden.", + "welcome.title": "Willkommen", + "welcomePage.openFolderWithPath": "Ordner {0} mit Pfad {1} öffnen", + "welcomePage.extensionListSeparator": ",", + "welcomePage.installKeymap": "Tastenzuordnung {0} öffnen", + "welcomePage.installExtensionPack": "Zusätzliche Unterstützung für {0} installieren", + "welcomePage.installedKeymap": "Die Tastaturzuordnung {0} ist bereits installiert.", + "welcomePage.installedExtensionPack": "Unterstützung für {0} ist bereits installiert.", + "ok": "OK", + "details": "Details", + "cancel": "Abbrechen", + "welcomePage.buttonBackground": "Hintergrundfarbe für die Schaltflächen auf der Willkommensseite.", + "welcomePage.buttonHoverBackground": "Hoverhintergrundfarbe für die Schaltflächen auf der Willkommensseite." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..6141b0ffa1 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "Interaktiver Playground", + "editorWalkThrough": "Interaktiver Playground" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..c14443ded9 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "Interaktiver Playground", + "help": "Hilfe", + "interactivePlayground": "Interaktiver Playground" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..cae4b78cbd --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "Nach oben scrollen (Zeile)", + "editorWalkThrough.arrowDown": "Nach unten scrollen (Zeile)", + "editorWalkThrough.pageUp": "Nach oben scrollen (Seite)", + "editorWalkThrough.pageDown": "Nach unten scrollen (Seite)" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..44ac813d74 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "Ungebunden", + "walkThrough.gitNotFound": "Git scheint auf Ihrem System nicht installiert zu sein.", + "walkThrough.embeddedEditorBackground": "Hintergrundfarbe für die eingebetteten Editoren im Interaktiven Playground." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/deu/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..28b11e38e3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "Trägt Konfigurationseigenschaften bei.", + "vscode.extension.contributes.configuration.title": "Eine Zusammenfassung der Einstellungen. Diese Bezeichnung wird in der Einstellungsdatei als trennender Kommentar verwendet.", + "vscode.extension.contributes.configuration.properties": "Die Beschreibung der Konfigurationseigenschaften.", + "scope.window.description": "Fensterspezifische Konfiguration, die in den Benutzer- oder Arbeitsbereichseinstellungen konfiguriert werden kann.", + "scope.resource.description": "Ressourcenspezifische Konfiguration, die in den Benutzer-, Arbeitsbereichs- oder Ordnereinstellungen konfiguriert werden kann.", + "scope.description": "Bereich, in dem die Konfiguration gültig ist. Verfügbare Gültigkeitsbereiche sind \"window\" und \"resource\".", + "invalid.type": "Wenn eine Festlegung erfolgt, muss \"configuration.type\" auf \"object\" festgelegt werden.", + "invalid.title": "configuration.title muss eine Zeichenfolge sein.", + "vscode.extension.contributes.defaultConfiguration": "Trägt zu Konfigurationeinstellungen des Standard-Editors für die jeweilige Sprache bei.", + "invalid.properties": "\"configuration.properties\" muss ein Objekt sein.", + "workspaceConfig.folders.description": "Liste von Ordnern, die in den Arbeitsbereich geladen werden. Hierbei muss es sich um einen Dateipfad handeln, z. B. \" /root/folderA\" oder \"./folderA\" bei einem relativen Pfad, der in Bezug auf den Speicherort der Arbeitsbereichsdatei aufgelöst wird.", + "workspaceConfig.folder.description": "Ein Dateipfad, z. B. \" /root/folderA\" oder \"./folderA\" bei einem relativen Pfad, der in Bezug auf den Speicherort der Arbeitsbereichsdatei aufgelöst wird.", + "workspaceConfig.settings.description": "Arbeitsbereichseinstellungen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/deu/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..a696dc01c2 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Einstellungen öffnen", + "close": "Schließen", + "saveAndRetry": "Änderungen Speichern und Wiederholen", + "errorUnknownKey": "In {0} kann nicht geschrieben werden, weil {1} keine registrierte Konfiguration ist.", + "errorInvalidFolderConfiguration": "In die Ordnereinstellungen kann nicht geschrieben werden, weil {0} den Gültigkeitsbereich für Ordnerressourcen nicht unterstützt.", + "errorInvalidUserTarget": "In die Benutzereinstellungen kann nicht geschrieben werden, weil {0} den globalen Gültigkeitsbereich nicht unterstützt.", + "errorInvalidFolderTarget": "In die Ordnereinstellungen kann nicht geschrieben werden, weil keine Ressource angegeben ist.", + "errorNoWorkspaceOpened": "In {0} kann nicht geschrieben werden, weil kein Arbeitsbereich geöffnet ist. Öffnen Sie zuerst einen Arbeitsbereich, und versuchen Sie es noch mal.", + "errorInvalidConfiguration": "In die Einstellungen kann nicht geschrieben werden. Öffnen Sie **Benutzereinstellungen**, um Fehler/Warnungen in der Datei zu korrigieren, und versuchen Sie es noch mal.", + "errorInvalidConfigurationWorkspace": "In die Einstellungen kann nicht geschrieben werden. Öffnen Sie die **Arbeitsbereichseinstellungen**, um Fehler/Warnungen in der Datei zu korrigieren, und versuchen Sie es noch mal.", + "errorConfigurationFileDirty": "In die Einstellungen kann nicht geschrieben werden, weil die Datei geändert wurde. Speichern Sie die Datei **Benutzereinstellungen**, und versuchen Sie es noch mal.", + "errorConfigurationFileDirtyWorkspace": "In die Einstellungen kann nicht geschrieben werden, weil die Datei geändert wurde. Speichern Sie die Datei **Arbeitsbereichseinstellungen**, und versuchen Sie es noch mal.", + "userTarget": "Benutzereinstellungen", + "workspaceTarget": "Arbeitsbereichseinstellungen", + "folderTarget": "Ordnereinstellungen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/deu/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..04d30c75cc --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "In die Datei kann nicht geschrieben werden. Öffnen Sie die Datei, um Fehler/Warnungen in der Datei zu beheben, und versuchen Sie es noch mal.", + "errorFileDirty": "In die Datei kann nicht geschrieben werden, weil sie geändert wurde. Speichern Sie die Datei, und versuchen Sie es noch mal." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/deu/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..ad7c98bf4e --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetrie", + "telemetry.enableCrashReporting": "Aktiviert Absturzberichte, die an Microsoft gesendet werden.\nDiese Option erfordert einen Neustart, damit sie wirksam wird." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/deu/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..bd799af8b6 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "Der Erweiterungshost wurde nicht innerhalb von 10 Sekunden gestartet. Möglicherweise wurde er in der ersten Zeile beendet und benötigt einen Debugger, um die Ausführung fortzusetzen.", + "extensionHostProcess.startupFail": "Der Erweiterungshost wurde nicht innerhalb von 10 Sekunden gestartet. Dies stellt ggf. ein Problem dar.", + "extensionHostProcess.error": "Fehler vom Erweiterungshost: {0}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..461f496af1 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Fehler beim Analysieren von {0}: {1}.", + "fileReadFail": "Die Datei \"{0}\" kann nicht gelesen werden: {1}", + "jsonsParseFail": "Fehler beim Analysieren von {0} oder {1}: {2}.", + "missingNLSKey": "Die Nachricht für den Schlüssel {0} wurde nicht gefunden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..2a8f58a562 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "Entwicklertools", + "restart": "Erweiterungshost neu starten", + "extensionHostProcess.crash": "Der Erweiterungshost wurde unerwartet beendet.", + "extensionHostProcess.unresponsiveCrash": "Der Erweiterungshost wurde beendet, weil er nicht reagiert hat.", + "overwritingExtension": "Die Erweiterung \"{0}\" wird mit \"{1}\" überschrieben.", + "extensionUnderDevelopment": "Die Entwicklungserweiterung unter \"{0}\" wird geladen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/deu/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..e4c622e9b1 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "Microsoft .NET Framework 4.5 ist erforderlich. Klicken Sie auf den Link, um die Anwendung zu installieren.", + "installNet": ".NET Framework 4.5 herunterladen", + "neverShowAgain": "Nicht mehr anzeigen", + "trashFailed": "Fehler beim Verschieben von \"{0}\" in den Papierkorb." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/deu/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..3635e35151 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "Ungültige Dateiressource ({0})", + "fileIsDirectoryError": "Die Datei ist ein Verzeichnis ({0}).", + "fileNotModifiedError": "Datei nicht geändert seit", + "fileTooLargeError": "Die Datei ist zu groß, um sie zu öffnen.", + "fileBinaryError": "Die Datei scheint eine Binärdatei zu sein und kann nicht als Text geöffnet werden.", + "fileNotFoundError": "Die Datei wurde nicht gefunden ({0}).", + "fileMoveConflict": "Verschieben/Kopieren kann nicht ausgeführt werden. Die Datei ist am Ziel bereits vorhanden.", + "unableToMoveCopyError": "Der Verschiebe-/Kopiervorgang kann nicht ausgeführt werden. Die Datei würde den Ordner ersetzen, in dem sie enthalten ist.", + "foldersCopyError": "Ordner können nicht in den Arbeitsbereich kopiert werden. Bitte wählen Sie einzelne Dateien aus, um sie zu kopieren.", + "fileModifiedError": "Datei geändert seit", + "fileReadOnlyError": "Die Datei ist schreibgeschützt." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/deu/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..6d427128ea --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "Schreiben ist nicht möglich, da die Datei geändert wurde. Speichern Sie die Datei **Tastenzuordnungen**, und versuchen Sie es noch mal.", + "parseErrors": "Tastenzuordnungen können nicht geschrieben werden. Öffnen Sie die Datei **Tastenzuordnungen**, um Fehler/Warnungen in der Datei zu korrigieren. Versuchen Sie es anschließend noch mal.", + "errorInvalidConfiguration": "Tastenzuordnungen konnten nicht geschrieben werden. Die Datei mit den Tastenzuordnungen enthält ein Objekt, bei dem es sich nicht um ein Array handelt. Öffnen Sie die Datei, um das Problem zu beheben, und versuchen Sie es dann nochmal.", + "emptyKeybindingsHeader": "Platzieren Sie Ihre Tastenzuordnungen in dieser Datei, um die Standardwerte zu überschreiben." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/deu/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..4cd8e58f76 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "Es wurde ein nicht leerer Wert erwartet.", + "requirestring": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "optstring": "Die Eigenschaft \"{0}\" kann ausgelassen werden oder muss vom Typ \"string\" sein.", + "vscode.extension.contributes.keybindings.command": "Der Bezeichner des Befehls, der ausgeführt werden soll, wenn die Tastenbindung ausgelöst wird.", + "vscode.extension.contributes.keybindings.key": "Der Schlüssel oder die Schlüsselsequenz (separate Schlüssel mit Pluszeichen und Sequenzen mit Leerzeichen, z. B. STRG+O und STRG+L L für einen Akkord).", + "vscode.extension.contributes.keybindings.mac": "Der Mac-spezifische Schlüssel oder die Schlüsselsequenz.", + "vscode.extension.contributes.keybindings.linux": "Der Linux-spezifische Schlüssel oder die Schlüsselsequenz.", + "vscode.extension.contributes.keybindings.win": "Der Windows-spezifische Schlüssel oder die Schlüsselsequenz.", + "vscode.extension.contributes.keybindings.when": "Die Bedingung, wann der Schlüssel aktiv ist.", + "vscode.extension.contributes.keybindings": "Trägt Tastenbindungen bei.", + "invalid.keybindings": "Ungültige Angabe \"contributes.{0}\": {1}", + "unboundCommands": "Die folgenden weiteren Befehle sind verfügbar: ", + "keybindings.json.title": "Tastenbindungskonfiguration", + "keybindings.json.key": "Der Schlüssel oder die Schlüsselsequenz (durch Leerzeichen getrennt)", + "keybindings.json.command": "Der Name des auszuführenden Befehls.", + "keybindings.json.when": "Die Bedingung, wann der Schlüssel aktiv ist.", + "keybindings.json.args": "Argumente, die an den auszuführenden Befehl übergeben werden sollen.", + "keyboardConfigurationTitle": "Tastatur", + "dispatch": "Steuert die Abgangslogik, sodass bei einem Tastendruck entweder \"keydown.code\" (empfohlen) oder \"keydown.keyCode\" verwendet wird." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/deu/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..1085c2d5f7 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Fehler: {0}", + "alertWarningMessage": "Warnung: {0}", + "alertInfoMessage": "Info: {0}", + "error": "Fehler", + "warning": "Warnung", + "info": "Info", + "close": "Schließen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/deu/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..70f54b24cd --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "&&Ja", + "cancelButton": "Abbrechen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/deu/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..cbca6d9f8b --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Contributes-Sprachdeklarationen", + "vscode.extension.contributes.languages.id": "Die ID der Sprache.", + "vscode.extension.contributes.languages.aliases": "Namealiase für die Sprache.", + "vscode.extension.contributes.languages.extensions": "Dateierweiterungen, die der Sprache zugeordnet sind.", + "vscode.extension.contributes.languages.filenames": "Dateinamen, die der Sprache zugeordnet sind.", + "vscode.extension.contributes.languages.filenamePatterns": "Dateinamen-Globmuster, die Sprache zugeordnet sind.", + "vscode.extension.contributes.languages.mimetypes": "MIME-Typen, die der Sprache zugeordnet sind.", + "vscode.extension.contributes.languages.firstLine": "Ein regulärer Ausdruck, der mit der ersten Zeile einer Datei der Sprache übereinstimmt.", + "vscode.extension.contributes.languages.configuration": "Ein relativer Pfad zu einer Datei mit Konfigurationsoptionen für die Sprache.", + "invalid": "Ungültige Angabe \"contributes.{0}\". Es wurde ein Array erwartet.", + "invalid.empty": "Leerer Wert für \"contributes.{0}\".", + "require.id": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "opt.extensions": "Die Eigenschaft \"{0}\" kann ausgelassen werden. Sie muss vom Typ \"string[]\" sein.", + "opt.filenames": "Die Eigenschaft \"{0}\" kann ausgelassen werden. Sie muss vom Typ \"string[]\" sein.", + "opt.firstLine": "Die Eigenschaft \"{0}\" kann ausgelassen werden. Sie muss vom Typ \"string\" sein.", + "opt.configuration": "Die Eigenschaft \"{0}\" kann ausgelassen werden. Sie muss vom Typ \"string\" sein.", + "opt.aliases": "Die Eigenschaft \"{0}\" kann ausgelassen werden. Sie muss vom Typ \"string[]\" sein.", + "opt.mimetypes": "Die Eigenschaft \"{0}\" kann ausgelassen werden. Sie muss vom Typ \"string[]\" sein." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/deu/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..441530a01b --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1}", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/deu/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..2e12621a8b --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Trägt TextMate-Tokenizer bei.", + "vscode.extension.contributes.grammars.language": "Der Sprachbezeichner, für den diese Syntax beigetragen wird.", + "vscode.extension.contributes.grammars.scopeName": "Der TextMate-Bereichsname, der von der tmLanguage-Datei verwendet wird.", + "vscode.extension.contributes.grammars.path": "Der Pfad der tmLanguage-Datei. Der Pfad ist relativ zum Extensionordner und beginnt normalerweise mit \". /syntaxes/\".", + "vscode.extension.contributes.grammars.embeddedLanguages": "Eine Zuordnung zwischen Bereichsname und Sprach-ID, wenn diese Grammatik eingebettete Sprachen enthält.", + "vscode.extension.contributes.grammars.injectTo": "Die Liste der Sprachbereichsnamen, in die diese Grammatik injiziert wird." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/deu/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..11cd1fd091 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Unbekannte Sprache in \"contributes.{0}.language\". Bereitgestellter Wert: {1}", + "invalid.scopeName": "In \"contributes.{0}.scopeName\" wurde eine Zeichenfolge erwartet. Bereitgestellter Wert: {1}", + "invalid.path.0": "Expected string in `contributes.{0}.path`. Provided value: {1}", + "invalid.injectTo": "Ungültiger Wert in \"contributes.{0}.injectTo\". Es muss sich um ein Array von Sprachbereichsnamen handeln. Bereitgestellter Wert: {1}", + "invalid.embeddedLanguages": "Ungültiger Wert in \"contributes.{0}.embeddedLanguages\". Muss eine Objektzuordnung von Bereichsname zu Sprache sein. Angegebener Wert: {1}", + "invalid.path.1": "Es wurde erwartet, dass \"contributes.{0}.path\" ({1}) im Ordner ({2}) der Erweiterung enthalten ist. Dies führt ggf. dazu, dass die Erweiterung nicht portierbar ist.", + "no-tm-grammar": "Keine TM-Grammatik für diese Sprache registriert." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/deu/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..bc7f79a144 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "Die Datei wurde geändert. Speichern Sie sie zuerst, bevor Sie sie mit einer anderen Codierung erneut öffnen.", + "genericSaveError": "Fehler beim Speichern von \"{0}\": {1}." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/deu/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..93287cdee5 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "Die Dateien konnten nicht gesichert werden (Fehler: {0}). Versuchen Sie, Ihre Dateien zu speichern, um den Vorgang zu beenden." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/deu/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..058c2712f3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "Möchten Sie die Änderungen speichern, die Sie an \"{0}\" vorgenommen haben?", + "saveChangesMessages": "Möchten Sie die an den folgenden {0}-Dateien vorgenommenen Änderungen speichern?", + "moreFile": "...1 weitere Datei wird nicht angezeigt", + "moreFiles": "...{0} weitere Dateien werden nicht angezeigt", + "saveAll": "&&Alle speichern", + "save": "&&Speichern", + "dontSave": "&&Nicht speichern", + "cancel": "Abbrechen", + "saveChangesDetail": "Ihre Änderungen gehen verloren, wenn Sie diese nicht speichern.", + "allFiles": "Alle Dateien", + "noExt": "Keine Erweiterung" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/deu/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..4adf5ae6a0 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "Farben und Stile für das Token.", + "schema.token.foreground": "Vordergrundfarbe für das Token.", + "schema.token.fontStyle": "Schriftschnitt der Regel: kursiv, fett und unterstrichen (einzeln oder in Kombination)", + "schema.fontStyle.error": "Der Schriftschnitt muss eine Kombination aus \"kursiv\", \"fett\" und \"unterstrichen\" sein.", + "schema.properties.name": "Beschreibung der Regel.", + "schema.properties.scope": "Bereichsauswahl, mit der diese Regel einen Abgleich ausführt.", + "schema.tokenColors.path": "Pfad zu einer tmTheme-Designdatei (relativ zur aktuellen Datei).", + "schema.colors": "Farben für die Syntaxhervorhebung" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/deu/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..d165392dbd --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "Das Ordnersymbol für aufgeklappte Ordner. Das Symbol für aufgeklappte Ordner ist optional. Wenn diese Angabe nicht festgelegt wird, wird das für Ordner definierte Symbol angezeigt.", + "schema.folder": "Das Ordnersymbol für zugeklappte Ordner. Gilt, wenn folderExpanded nicht festgelegt ist, auch für aufgeklappte Ordner.", + "schema.file": "Das Standarddateisymbol, das für alle Dateien angezeigt wird, die nicht mit einer Erweiterung, einem Dateinamen oder einer Sprach-ID übereinstimmen.", + "schema.folderNames": "Ordnet Ordnernamen Symbolen zu. Der Objektschlüssel ist der Ordnername ohne jedes Pfadsegment. Muster oder Platzhalter sind unzulässig. Bei der Zuordnung von Ordnernamens wird nicht zwischen Groß-/Kleinschreibung unterschieden.", + "schema.folderName": "Die ID der Symboldefinition für die Zuordnung.", + "schema.folderNamesExpanded": "Ordnet Ordnernamen Symbolen für aufgeklappte Ordner zu. Der Objektschlüssel ist der Ordnername ohne jedes Pfadsegment. Muster oder Platzhalter sind unzulässig. Bei der Zuordnung von Ordnernamens wird nicht zwischen Groß-/Kleinschreibung unterschieden.", + "schema.folderNameExpanded": "Die ID der Symboldefinition für die Zuordnung.", + "schema.fileExtensions": "Ordnet Dateierweiterungen Symbolen zu. Der Objektschlüssel ist der Name der Dateierweiterung. Der Erweiterungsname ist das letzte Segment eines Dateinamen nach dem letzten Punkt (ohne den Punkt). Erweiterungen werden ohne Berücksichtigung von Groß-/Kleinschreibung verglichen.", + "schema.fileExtension": "Die ID der Symboldefinition für die Zuordnung.", + "schema.fileNames": "Ordnet Dateinamen Symbolen zu. Der Objektschlüssel ist der vollständige Dateiname ohne Pfadsegmente. Der Dateiname kann Punkte und eine mögliche Dateierweiterung enthalten. Muster oder Platzhalter sind unzulässig. Bei der Dateinamenzuordnung wird nicht zwischen Groß-/Kleinschreibung unterschieden.", + "schema.fileName": "Die ID der Symboldefinition für die Zuordnung.", + "schema.languageIds": "Ordnet Sprachen Symbolen zu. Der Objektschlüssel ist die Sprach-ID wie im Sprachbeitragspunkt definiert.", + "schema.languageId": "Die ID der Symboldefinition für die Zuordnung.", + "schema.fonts": "Schriftarten, die in den Symboldefinitionen verwendet werden.", + "schema.id": "Die ID der Schriftart.", + "schema.src": "Die Speicherorte der Schriftart.", + "schema.font-path": "Der Schriftartpfad relativ zur aktuellen Symboldesigndatei.", + "schema.font-format": "Das Format der Schriftart.", + "schema.font-weight": "Die Gewichtung der Schriftart.", + "schema.font-sstyle": "Der Stil der Schriftart.", + "schema.font-size": "Die Standardgröße der Schriftart.", + "schema.iconDefinitions": "Beschreibung aller Symbole, die beim Zuordnen von Dateien zu Symbolen verwendet werden können.", + "schema.iconDefinition": "Eine Symboldefinition. Der Objektschlüssel ist die ID der Definition.", + "schema.iconPath": "Bei Verwendung eines SVG- oder PNG-Datei: der Pfad zum Bild. Der Pfad ist relativ zur Symbolsammlungsdatei.", + "schema.fontCharacter": "Bei Verwendung einer Glyphenschriftart: das zu verwendende Zeichen in der Schriftart.", + "schema.fontColor": "Bei Verwendung einer Glyphenschriftart: die zu verwendende Farbe.", + "schema.fontSize": "Wenn eine Schriftart verwendet wird: der Schriftgrad als Prozentsatz der Textschriftart. Wenn diese Angabe nicht festgelegt wird, wird standardmäßig die Größe in der Schriftartdefinition verwendet.", + "schema.fontId": "Bei Verwendung einer Schriftart: die ID der Schriftart. Wenn diese Angabe nicht festgelegt wird, wird standardmäßig die erste Schriftartdefinition verwendet.", + "schema.light": "Optionale Zuordnungen für Dateisymbole in hellen Farbdesigns.", + "schema.highContrast": "Optionale Zuordnungen für Dateisymbole in Farbdesigns mit hohem Kontrast." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/deu/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..11757e3eac --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "Probleme beim Analysieren der JSON-Designdatei: {0}", + "error.invalidformat.colors": "Probleme beim Analysieren der Farbdesigndatei: {0}. Die Eigenschaft \"colors\" ist nicht vom Typ \"object\".", + "error.invalidformat.tokenColors": "Problem beim Analysieren der Farbdesigndatei: {0}. Die Eigenschaft \"tokenColors\" muss entweder ein Array sein, das Farben festlegt, oder ein Pfad zu einer TextMate-Designdatei.", + "error.plist.invalidformat": "Probleme beim Analysieren der tmTheme-Designdatei: {0}. \"settings\" ist kein Array", + "error.cannotparse": "Probleme beim Analysieren der tmTheme-Designdatei: {0}", + "error.cannotload": "Probleme beim Laden der tmTheme-Designdatei {0}: {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/deu/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..c77514ddb8 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "Die ID des Symboldesigns wie in den Benutzereinstellungen verwendet.", + "vscode.extension.contributes.themes.label": "Die Bezeichnung des Farbdesigns wie in der Benutzeroberfläche angezeigt.", + "vscode.extension.contributes.themes.uiTheme": "Das Basisdesign, das die Farben um den Editor definiert: \"vs\" ist das helle Farbdesign, \"vs-dark\" das dunkle Farbdesign. \"hc-black\" ist das dunkle Design mit hohem Kontrast.", + "vscode.extension.contributes.themes.path": "Der Pfad der TMTHEME-Datei. Der Pfad ist relativ zum Erweiterungsordner und lautet normalerweise \"./themes/themeFile.tmTheme\".", + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "Die ID des Symboldesigns wie in den Benutzereinstellungen verwendet.", + "vscode.extension.contributes.iconThemes.label": "Die Bezeichnung des Symboldesigns wie in der Benutzeroberfläche angezeigt.", + "vscode.extension.contributes.iconThemes.path": "Der Pfad der Symboldesign-Definitionsdatei. Der Pfad ist relativ zum Erweiterungsordner und lautet normalerweise \"./icons/awesome-icon-theme.json\".", + "migration.completed": "Den Benutzereinstellungen wurden neue Designeinstellungen hinzugefügt. Sicherung verfügbar unter {0}.", + "error.cannotloadtheme": "Unable to load {0}: {1}", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "In \"contributes.{0}.path\" wurde eine Zeichenfolge erwartet. Bereitgestellter Wert: {1}", + "invalid.path.1": "Es wurde erwartet, dass \"contributes.{0}.path\" ({1}) im Ordner ({2}) der Erweiterung enthalten ist. Dies führt ggf. dazu, dass die Erweiterung nicht portierbar ist.", + "reqid": "In \"contributes.{0}.id\" wurde eine Zeichenfolge erwartet. Bereitgestellter Wert: {1}", + "error.cannotloadicontheme": "Unable to load {0}", + "error.cannotparseicontheme": "Problems parsing file icons file: {0}", + "colorTheme": "Specifies the color theme used in the workbench.", + "colorThemeError": "Theme is unknown or not installed.", + "iconTheme": "Gibt das in der Workbench verwendete Symboldesign oder \"null\", um keine Dateisymbole anzuzeigen, an.", + "noIconThemeDesc": "No file icons", + "iconThemeError": "File icon theme is unknown or not installed.", + "workbenchColors": "Überschreibt Farben aus dem derzeit ausgewählte Farbdesign.", + "workbenchColors.deprecated": "Diese Einstellung ist nicht mehr experimentell und wurde in \"workbench.colorCustomizations\" umbenannt.", + "workbenchColors.deprecatedDescription": "Verwenden Sie stattdessen \"workbench.colorCustomizations\".", + "editorColors": "Überschreibt Editorfarben und den Schriftschnitt aus dem momentan ausgewählten Farbdesign.", + "editorColors.comments": "Legt die Farben und Stile für Kommentare fest.", + "editorColors.strings": "Legt die Farben und Stile für Zeichenfolgenliterale fest.", + "editorColors.keywords": "Legt die Farben und Stile für Schlüsselwörter fest.", + "editorColors.numbers": "Legt die Farben und Stile für Nummernliterale fest.", + "editorColors.types": "Legt die Farben und Stile für Typdeklarationen und Verweise fest.", + "editorColors.functions": "Legt die Farben und Stile für Funktionsdeklarationen und Verweise fest.", + "editorColors.variables": "Legt die Farben und Stile für Variablendeklarationen und Verweise fest.", + "editorColors.textMateRules": "Legt Farben und Stile mithilfe von Textmate-Designregeln fest (erweitert)." +} \ No newline at end of file diff --git a/i18n/esn/extensions/configuration-editing/out/extension.i18n.json b/i18n/esn/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..34cf770861 --- /dev/null +++ b/i18n/esn/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "Ejemplo" +} \ No newline at end of file diff --git a/i18n/esn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/esn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..baaf62dcd5 --- /dev/null +++ b/i18n/esn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "p. ej. myFile.txt", + "activeEditorMedium": "e.g. myFolder/myFile.txt", + "activeEditorLong": "e.g. /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "p. ej. myFolder1, myFolder2, myFolder3", + "rootPath": "p. ej. /Users/Development/myProject", + "folderName": "p. ej. myFolder", + "folderPath": "p. ej. /Users/Development/myFolder", + "appName": "p. ej. VS Code", + "dirty": "un indicador con modificaciones si el editor activo tiene modificaciones", + "separator": "un separador condicional (\"-\") que aparece solo cuando está rodeado de variables con valores", + "assocLabelFile": "Archivos con extensión", + "assocDescriptionFile": "Asigna todos los archivos cuyo nombre coincide con el patrón global al lenguaje con el identificador especificado.", + "assocLabelPath": "Archivos con ruta de acceso", + "assocDescriptionPath": "Asigna todos los archivos cuya ruta de acceso al lenguaje con el identificador especificado coincide con el patrón global de ruta de acceso absoluta.", + "fileLabel": "Archivos por extensión", + "fileDescription": "Hacer coincidir todos los archivos que tengan una extensión de archivo determinada.", + "filesLabel": "Archivos con varias extensiones", + "filesDescription": "Hacer coincidir todos los archivos con cualquiera de las extensiones de archivo.", + "derivedLabel": "Archivos con elementos del mismo nivel por nombre", + "derivedDescription": "Hacer coincidir archivos que tienen elementos del mismo nivel con el mismo nombre pero con extensión diferente.", + "topFolderLabel": "Carpeta por nombre (nivel superior)", + "topFolderDescription": "Hacer coincidir una carpeta de nivel superior con un nombre específico.", + "topFoldersLabel": "Carpetas con varios nombres (nivel superior)", + "topFoldersDescription": "Hacer coincidir varias carpetas de nivel superior.", + "folderLabel": "Carpeta por nombre (cualquier ubicación)", + "folderDescription": "Hacer coincidir una carpeta con un nombre determinado en cualquier ubicación.", + "falseDescription": "Deshabilitar el patrón.", + "trueDescription": "Habilitar el patrón.", + "siblingsDescription": "Hacer coincidir archivos que tienen elementos del mismo nivel con el mismo nombre pero con extensión diferente.", + "languageSpecificEditorSettings": "Configuración del editor específica del lenguaje", + "languageSpecificEditorSettingsDescription": "Reemplazar configuración del editor para lenguaje" +} \ No newline at end of file diff --git a/i18n/esn/extensions/css/client/out/cssMain.i18n.json b/i18n/esn/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..493348efd7 --- /dev/null +++ b/i18n/esn/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "Servidor de lenguaje CSS" +} \ No newline at end of file diff --git a/i18n/esn/extensions/css/package.i18n.json b/i18n/esn/extensions/css/package.i18n.json new file mode 100644 index 0000000000..9d5b25a78c --- /dev/null +++ b/i18n/esn/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "Número de parámetros no válido", + "css.lint.boxModel.desc": "No use ancho o alto con el relleno o los bordes.", + "css.lint.compatibleVendorPrefixes.desc": "Cuando use un prefijo específico del proveedor, compruebe que también haya incluido el resto de propiedades específicas del proveedor.", + "css.lint.duplicateProperties.desc": "No use definiciones de estilo duplicadas", + "css.lint.emptyRules.desc": "No use conjuntos de reglas vacíos", + "css.lint.float.desc": "Le recomendamos no usar 'float'. Los floats producen CSS frágiles, fáciles de corromper si cambia cualquier aspecto del diseño.", + "css.lint.fontFaceProperties.desc": "La regla @font-face debe definir las propiedades \"src\" y \"font-family\"", + "css.lint.hexColorLength.desc": "Los colores hexadecimales deben estar formados por tres o seis números hexadecimales.", + "css.lint.idSelector.desc": "Los selectores no deben contener identificadores porque estas reglas están estrechamente ligadas a HTML.", + "css.lint.ieHack.desc": "Las modificaciones de IE solo son necesarias cuando admiten IE7 y anteriores", + "css.lint.important.desc": "Le recomendamos no usar !important. Esto indica que la especificidad de todo el CSS está fuera de control y que debe refactorizarse.", + "css.lint.importStatement.desc": "Las instrucciones Import no se cargan en paralelo", + "css.lint.propertyIgnoredDueToDisplay.desc": "La propiedad se ignora a causa de la pantalla. Por ejemplo, con 'display: inline', el ancho, el alto, el margen superior e inferior y las propiedades de float no tienen efecto.", + "css.lint.universalSelector.desc": "Se sabe que el selector universal (*) es lento", + "css.lint.unknownProperties.desc": "Propiedad desconocida.", + "css.lint.unknownVendorSpecificProperties.desc": "Propiedad específica del proveedor desconocida.", + "css.lint.vendorPrefix.desc": "Cuando use un prefijo específico del proveedor, incluya también la propiedad estándar.", + "css.lint.zeroUnits.desc": "No se necesita una unidad para cero", + "css.trace.server.desc": "Hace un seguimiento de la comunicación entre VSCode y el servidor de lenguaje CSS.", + "css.validate.title": "Controla la validación de CSS y la gravedad de los problemas.", + "css.validate.desc": "Habilita o deshabilita todas las validaciones", + "less.lint.argumentsInColorFunction.desc": "Número de parámetros no válido", + "less.lint.boxModel.desc": "No use ancho o alto con el relleno o los bordes.", + "less.lint.compatibleVendorPrefixes.desc": "Cuando use un prefijo específico del proveedor, compruebe que también haya incluido el resto de propiedades específicas del proveedor.", + "less.lint.duplicateProperties.desc": "No use definiciones de estilo duplicadas", + "less.lint.emptyRules.desc": "No use conjuntos de reglas vacíos", + "less.lint.float.desc": "Le recomendamos no usar 'float'. Los floats producen CSS frágiles, fáciles de corromper si cambia cualquier aspecto del diseño.", + "less.lint.fontFaceProperties.desc": "La regla @font-face debe definir las propiedades \"src\" y \"font-family\"", + "less.lint.hexColorLength.desc": "Los colores hexadecimales deben estar formados por tres o seis números hexadecimales.", + "less.lint.idSelector.desc": "Los selectores no deben contener identificadores porque estas reglas están estrechamente ligadas a HTML.", + "less.lint.ieHack.desc": "Las modificaciones de IE solo son necesarias cuando admiten IE7 y anteriores", + "less.lint.important.desc": "Le recomendamos no usar !important. Esto indica que la especificidad de todo el CSS está fuera de control y que debe refactorizarse.", + "less.lint.importStatement.desc": "Las instrucciones Import no se cargan en paralelo", + "less.lint.propertyIgnoredDueToDisplay.desc": "La propiedad se ignora a causa de la pantalla. Por ejemplo, con 'display: inline', el ancho, el alto, el margen superior e inferior y las propiedades de float no tienen efecto.", + "less.lint.universalSelector.desc": "Se sabe que el selector universal (*) es lento", + "less.lint.unknownProperties.desc": "Propiedad desconocida.", + "less.lint.unknownVendorSpecificProperties.desc": "Propiedad específica del proveedor desconocida.", + "less.lint.vendorPrefix.desc": "Cuando use un prefijo específico del proveedor, incluya también la propiedad estándar.", + "less.lint.zeroUnits.desc": "No se necesita una unidad para cero", + "less.validate.title": "Controla la validación de LESS y la gravedad de los problemas.", + "less.validate.desc": "Habilita o deshabilita todas las validaciones", + "scss.lint.argumentsInColorFunction.desc": "Número de parámetros no válido", + "scss.lint.boxModel.desc": "No use ancho o alto con el relleno o los bordes.", + "scss.lint.compatibleVendorPrefixes.desc": "Cuando use un prefijo específico del proveedor, compruebe que también haya incluido el resto de propiedades específicas del proveedor.", + "scss.lint.duplicateProperties.desc": "No use definiciones de estilo duplicadas", + "scss.lint.emptyRules.desc": "No use conjuntos de reglas vacíos", + "scss.lint.float.desc": "Le recomendamos no usar 'float'. Los floats producen CSS frágiles, fáciles de corromper si cambia cualquier aspecto del diseño.", + "scss.lint.fontFaceProperties.desc": "La regla @font-face debe definir las propiedades \"src\" y \"font-family\"", + "scss.lint.hexColorLength.desc": "Los colores hexadecimales deben estar formados por tres o seis números hexadecimales.", + "scss.lint.idSelector.desc": "Los selectores no deben contener identificadores porque estas reglas están estrechamente ligadas a HTML.", + "scss.lint.ieHack.desc": "Las modificaciones de IE solo son necesarias cuando admiten IE7 y anteriores", + "scss.lint.important.desc": "Le recomendamos no usar !important. Esto indica que la especificidad de todo el CSS está fuera de control y que debe refactorizarse.", + "scss.lint.importStatement.desc": "Las instrucciones Import no se cargan en paralelo", + "scss.lint.propertyIgnoredDueToDisplay.desc": "La propiedad se ignora a causa de la pantalla. Por ejemplo, con 'display: inline', el ancho, el alto, el margen superior e inferior y las propiedades de float no tienen efecto.", + "scss.lint.universalSelector.desc": "Se sabe que el selector universal (*) es lento", + "scss.lint.unknownProperties.desc": "Propiedad desconocida.", + "scss.lint.unknownVendorSpecificProperties.desc": "Propiedad específica del proveedor desconocida.", + "scss.lint.vendorPrefix.desc": "Cuando use un prefijo específico del proveedor, incluya también la propiedad estándar.", + "scss.lint.zeroUnits.desc": "No se necesita una unidad para cero", + "scss.validate.title": "Controla la validación de SCSS y la gravedad de los problemas.", + "scss.validate.desc": "Habilita o deshabilita todas las validaciones", + "less.colorDecorators.enable.desc": "Habilita o deshabilita decoradores de color", + "scss.colorDecorators.enable.desc": "Habilita o deshabilita decoradores de color", + "css.colorDecorators.enable.desc": "Habilita o deshabilita decoradores de color", + "css.colorDecorators.enable.deprecationMessage": "El valor \"css.colorDecorators.enable\" está en desuso en favor de \"editor.colorDecorators\".", + "scss.colorDecorators.enable.deprecationMessage": "El valor \"scss.colorDecorators.enable\" está en desuso en favor de \"editor.colorDecorators\".", + "less.colorDecorators.enable.deprecationMessage": "El valor \"less.colorDecorators.enable\" está en desuso en favor de \"editor.colorDecorators\"." +} \ No newline at end of file diff --git a/i18n/esn/extensions/emmet/package.i18n.json b/i18n/esn/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..a806bdcc4c --- /dev/null +++ b/i18n/esn/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "Encapsular con abreviatura", + "command.wrapIndividualLinesWithAbbreviation": "Encapsular las líneas individuales con abreviatura", + "command.removeTag": "Quitar etiqueta", + "command.updateTag": "Actualizar etiqueta", + "command.matchTag": "Ir al par coincidente", + "command.balanceIn": "Equilibrio (entrante)", + "command.balanceOut": "Equilibrio (saliente)", + "command.prevEditPoint": "Ir al punto de edición anterior", + "command.nextEditPoint": "Ir al siguiente punto de edición", + "command.mergeLines": "Combinar líneas", + "command.selectPrevItem": "Seleccionar el elemento anterior", + "command.selectNextItem": "Seleccionar el siguiente elemento", + "command.splitJoinTag": "Dividir/Combinar etiqueta", + "command.toggleComment": "Alternar comentario", + "command.evaluateMathExpression": "Evaluar expresión matemática", + "command.updateImageSize": "Actualizar tamaño de imagen", + "command.reflectCSSValue": "Reflejar valor CSS", + "command.incrementNumberByOne": "Aumentar por 1", + "command.decrementNumberByOne": "Disminuir por 1", + "command.incrementNumberByOneTenth": "Aumentar por 0.1", + "command.decrementNumberByOneTenth": "Disminuir por 0.1", + "command.incrementNumberByTen": "Aumentar por 10", + "command.decrementNumberByTen": "Disminuir por 10", + "emmetSyntaxProfiles": "Defina el perfil de la sintaxis especificada o use su propio perfil con reglas específicas.", + "emmetExclude": "Matriz de lenguajes donde no deben expandirse la abreviación Emmet.", + "emmetExtensionsPath": "Ruta de acceso a una carpeta que contiene los perfiles de emmet y fragmentos.»", + "emmetShowExpandedAbbreviation": "\nMuestra abreviaciones Emmet expandidas como sugerencias. La opción \"inMarkupAndStylesheetFilesOnly\" se aplica a HTML, HAML, Jade, Slim, XML, XSL, CSS, SCSS, SASS, LESS y Stylus. La opción \"always\" se aplica a todas las partes del archivo, independientemente de que sea de marcado o CSS.", + "emmetShowAbbreviationSuggestions": "Muestra posibles abreviaciones Emmet como sugerencias. No se aplica a hojas de estilos ni cuando emmet.showExpandedAbbreviation está establecido en \"never\".", + "emmetIncludeLanguages": "Habilita abreviaciones Emmet en lenguajes que no se admiten de forma predeterminada. Agregue una asignación aquí entre el lenguaje y el lenguaje que admite Emmet.\n Ejemplo: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "Variables para usar en fragmentos de código Emmet", + "emmetTriggerExpansionOnTab": "Cuando se habilita, se expande la abreviación Emmet al presionar la tecla TAB.", + "emmetPreferences": "Preferencias usadas para modificar el comportamiento de algunas acciones y resoluciones de Emmet.", + "emmetPreferencesIntUnit": "Unidad predeterminada para valores enteros", + "emmetPreferencesFloatUnit": "Unidad predeterminada para valores float", + "emmetPreferencesCssAfter": "Símbolo que debe colocarse al final de una propiedad CSS cuando se expanden abreviaturas CSS", + "emmetPreferencesSassAfter": "Símbolo que debe colocarse al final de una propiedad CSS cuando se expanden abreviaturas CSS en archivos SASS", + "emmetPreferencesStylusAfter": "Símbolo que debe colocarse al final de una propiedad CSS cuando se expanden abreviaturas CSS en archivos Stylus", + "emmetPreferencesCssBetween": "Símbolo que debe colocarse entre una propiedad CSS y un valor cuando se expanden abreviaturas CSS", + "emmetPreferencesSassBetween": "Símbolo que debe colocarse entre una propiedad CSS y un valor cuando se expanden abreviaturas CSS en archivos SASS", + "emmetPreferencesStylusBetween": "Símbolo que debe colocarse entre una propiedad CSS y un valor cuando se expanden abreviaturas CSS en archivos Stylus", + "emmetShowSuggestionsAsSnippets": "Si es true, las sugerencias Emmet se muestran como fragmentos de código, de modo que puede ordenarlas por el valor editor.snippetSuggestions." +} \ No newline at end of file diff --git a/i18n/esn/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/esn/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..5dffb44b94 --- /dev/null +++ b/i18n/esn/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "Las imágenes deben utilizar el protocolo HTTPS.", + "svgsNotValid": "Los SVG no son un origen de imagen válido.", + "embeddedSvgsNotValid": "Los SGV insertados no son un origen de imagen válido.", + "dataUrlsNotValid": "Las direcciones URL de datos no son un origen de imagen válido.", + "relativeUrlRequiresHttpsRepository": "Las direcciones URL relativas de imágenes requieren un repositorio con el protocolo HTTPS especificado en el archivo package.json.", + "relativeIconUrlRequiresHttpsRepository": "Un icono requiere un repositorio con el protocolo HTTPS especificado en este archivo package.json.", + "relativeBadgeUrlRequiresHttpsRepository": "Las direcciones URL relativas de distintivos requieren un repositorio con el protocolo HTTPS especificado en este archivo package.json." +} \ No newline at end of file diff --git a/i18n/esn/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/esn/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..628d00c100 --- /dev/null +++ b/i18n/esn/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "Configuración del editor específica del lenguaje", + "languageSpecificEditorSettingsDescription": "Reemplazar configuración del editor para lenguaje" +} \ No newline at end of file diff --git a/i18n/esn/extensions/git/out/askpass-main.i18n.json b/i18n/esn/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..d8c316e33f --- /dev/null +++ b/i18n/esn/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "Faltan las credenciales o no son válidas." +} \ No newline at end of file diff --git a/i18n/esn/extensions/git/out/commands.i18n.json b/i18n/esn/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..ca0f73a143 --- /dev/null +++ b/i18n/esn/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "Etiqueta en {0}", + "remote branch at": "Rama remota en {0}", + "create branch": "$(plus) crear nueva rama", + "repourl": "URL del repositorio", + "parent": "Directorio principal", + "cloning": "Clonando el repositorio GIT...", + "openrepo": "Abrir repositorio", + "proposeopen": "¿Desea abrir el repositorio clonado?", + "path to init": "Ruta de acceso de la carpeta", + "provide path": "Proporcione una ruta de acceso de carpeta para inicializar un repositorio GIT", + "HEAD not available": "La versión HEAD de '{0}' no está disponible.", + "confirm stage files with merge conflicts": "¿Está seguro de que quiere hacer una copia intermedia de {0} archivos con conflictos de fusión mediante combinación?", + "confirm stage file with merge conflicts": "¿Está seguro de que quiere hacer una copia intermedia de {0} con conflictos de fusión mediante combinación? ", + "yes": "Sí", + "confirm revert": "¿Está seguro de que desea revertir los cambios seleccionados en {0}?", + "revert": "Revertir cambios", + "discard": "Descartar cambios", + "confirm delete": "¿Está seguro que desea eliminar {0}?", + "delete file": "Eliminar archivo", + "confirm discard": "¿Está seguro de que quiere descartar los cambios de {0}?", + "confirm discard multiple": "¿Está seguro de que quiere descartar los cambios de {0} archivos?", + "warn untracked": "¡Esto ELIMINARÁ {0} archivos sin seguimiento!", + "confirm discard all single": "¿Está seguro de que quiere descartar los cambios de {0}?", + "confirm discard all": "¿Está seguro de que quiere descartar TODOS los cambios en {0} archivos?\nEsta acción es IRREVERSIBLE.\nSu espacio de trabajo actual SE PERDERÁ PARA SIEMPRE.", + "discardAll multiple": "Descartar un archivo", + "discardAll": "Descartar todos los archivos ({0})", + "confirm delete multiple": "¿Está seguro de que quiere ELIMINAR {0} archivos?", + "delete files": "Eliminar archivos", + "there are untracked files single": "El siguiente archivo sin seguimiento se ELIMINARÁ DEL DISCO si se descarta: {0}.", + "there are untracked files": "Hay {0} archivos sin seguimiento que se ELIMINARÁN DEL DISCO si se descartan.", + "confirm discard all 2": "{0}\n\nEsta acción es IRREVERSIBLE. Su espacio de trabajo actual SE PERDERÁ PARA SIEMPRE.", + "yes discard tracked": "Descartar un archivo con seguimiento", + "yes discard tracked multiple": "Descartar {0} archivos con seguimiento", + "no staged changes": "No hay elementos almacenados provisionalmente.\n\n¿Desea almacenar de forma provisional todos sus cambios y confirmarlos directamente?", + "always": "Siempre", + "no changes": "No hay cambios para confirmar.", + "commit message": "Mensaje de confirmación", + "provide commit message": "Proporcione un mensaje de confirmación", + "select a ref to checkout": "Seleccione una referencia para desproteger", + "branch name": "Nombre de rama", + "provide branch name": "Especifique un nombre para la rama", + "select branch to delete": "Seleccione una rama para borrar", + "confirm force delete branch": "La rama '{0}' no está completamente fusionada. ¿Borrarla de todas formas?", + "delete branch": "Borrar rama...", + "select a branch to merge from": "Seleccione una rama desde la que fusionar", + "merge conflicts": "Hay conflictos de fusión. Resuelvalos antes de confirmar.", + "tag name": "Nombre de la etiqueta", + "provide tag name": "Por favor proporcione un nombre de etiqueta", + "tag message": "Mensaje", + "provide tag message": "Por favor, especifique un mensaje para anotar la etiqueta", + "no remotes to pull": "El repositorio no tiene remotos configurados de los que extraer.", + "pick remote pull repo": "Seleccione un origen remoto desde el que extraer la rama", + "no remotes to push": "El repositorio no tiene remotos configurados en los que insertar.", + "push with tags success": "Insertado con etiquetas correctamente.", + "nobranch": "Extraiga del repositorio una rama para insertar un remoto.", + "pick remote": "Seleccionar un elemento remoto para publicar la rama '{0}':", + "sync is unpredictable": "Esta acción insertará y extraerá confirmaciones en '{0}'.", + "ok": "Aceptar", + "never again": "De acuerdo, no volver a mostrar este mensaje.", + "no remotes to publish": "El repositorio no tiene remotos configurados en los que publicar.", + "no changes stash": "No existen cambios para el guardado provisional.", + "provide stash message": "Opcionalmente, proporcionar un mensaje para el guardado provisional", + "stash message": "Mensaje para el guardado provisional", + "no stashes": "No hay cambios guardados provisionalmente para restaurar.", + "pick stash to pop": "Elija un cambio guardado provisionalmente para aplicarlo y quitarlo", + "clean repo": "Limpie el árbol de trabajo del repositorio antes de la desprotección.", + "cant push": " No puede ejecutar la solicitud de inserción remotamente. Solicite un Pull para integrar los cambios.", + "git error details": "GIT: {0}", + "git error": "Error de GIT", + "open git log": "Abrir registro de GIT" +} \ No newline at end of file diff --git a/i18n/esn/extensions/git/out/main.i18n.json b/i18n/esn/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..339e0e7336 --- /dev/null +++ b/i18n/esn/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "Usando GIT {0} desde {1}", + "updateGit": "Actualizar GIT", + "neverShowAgain": "No volver a mostrar", + "git20": "Parece que tiene instalado GIT {0}. El código funciona mejor con GIT >= 2" +} \ No newline at end of file diff --git a/i18n/esn/extensions/git/out/model.i18n.json b/i18n/esn/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..60398729c1 --- /dev/null +++ b/i18n/esn/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "No hay repositorios disponibles", + "pick repo": "Elija un repositorio" +} \ No newline at end of file diff --git a/i18n/esn/extensions/git/out/repository.i18n.json b/i18n/esn/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..34813aa5e9 --- /dev/null +++ b/i18n/esn/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Abrir", + "index modified": "Índice modificado", + "modified": "Modificado", + "index added": "Índice agregado", + "index deleted": "Índice Eliminado", + "deleted": "Eliminado", + "index renamed": "Nombre de Índice Cambiado", + "index copied": "Índice copiado", + "untracked": "Sin seguimiento", + "ignored": "Omitido", + "both deleted": "Ambos eliminados", + "added by us": "Agregado por nosotros", + "deleted by them": "Eliminado por ellos", + "added by them": "Agregado por ellos", + "deleted by us": "Borrado por nosotros", + "both added": "Ambos añadidos", + "both modified": "Ambos modificados", + "commit": "Confirmar", + "merge changes": "Fusionar cambios mediante combinación", + "staged changes": "Cambios almacenados provisionalmente", + "changes": "Cambios", + "ok": "Aceptar", + "neveragain": "No volver a mostrar", + "huge": "El repositorio Git '{0}' contiene muchos cambios activos, solamente un subconjunto de las características de Git serán habilitadas." +} \ No newline at end of file diff --git a/i18n/esn/extensions/git/out/scmProvider.i18n.json b/i18n/esn/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..0049d4b22b --- /dev/null +++ b/i18n/esn/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "Confirmar" +} \ No newline at end of file diff --git a/i18n/esn/extensions/git/out/statusbar.i18n.json b/i18n/esn/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..c1bcae7478 --- /dev/null +++ b/i18n/esn/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "Extraer del repositorio...", + "sync changes": "Sincronizar cambios", + "publish changes": "Publicar cambios", + "syncing changes": "Sincronizando cambios..." +} \ No newline at end of file diff --git a/i18n/esn/extensions/git/package.i18n.json b/i18n/esn/extensions/git/package.i18n.json new file mode 100644 index 0000000000..5f7ffa6b15 --- /dev/null +++ b/i18n/esn/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "Clonar", + "command.init": "Inicializar el repositorio", + "command.close": "Cerrar repositorio", + "command.refresh": "Actualizar", + "command.openChange": "Abrir cambios", + "command.openFile": "Abrir archivo", + "command.openHEADFile": "Abrir archivo (HEAD)", + "command.stage": "Almacenar cambios provisionalmente", + "command.stageAll": "Almacenar todos los cambios", + "command.stageSelectedRanges": "Realizar copia intermedia de los intervalos seleccionados", + "command.revertSelectedRanges": "Revertir los intervalos seleccionados", + "command.unstage": "Cancelar almacenamiento provisional de los cambios", + "command.unstageAll": "Cancelar almacenamiento provisional de todos los cambios", + "command.unstageSelectedRanges": "Cancelar almacenamiento provisional de los intervalos seleccionados", + "command.clean": "Descartar cambios", + "command.cleanAll": "Descartar todos los cambios", + "command.commit": "Confirmar", + "command.commitStaged": "Confirmar almacenados provisionalmente", + "command.commitStagedSigned": "Confirmar por etapas (Aprobado)", + "command.commitStagedAmend": "Confirmar almacenados provisionalmente (modificar)", + "command.commitAll": "Confirmar todo", + "command.commitAllSigned": "Confirmar todo (aprobado)", + "command.commitAllAmend": "Confirmar todo (modificar)", + "command.undoCommit": "Deshacer última confirmación", + "command.checkout": "Desproteger en...", + "command.branch": "Crear rama...", + "command.deleteBranch": "Borrar rama...", + "command.merge": "Fusionar rama...", + "command.createTag": "Crear etiqueta", + "command.pull": "Incorporación de cambios", + "command.pullRebase": "Incorporación de cambios (fusionar mediante cambio de base)", + "command.pullFrom": "Extraer de...", + "command.push": "Insertar", + "command.pushTo": "Insertar en...", + "command.pushWithTags": "Insertar con etiquetas", + "command.sync": "Sincronizar", + "command.publish": "Publicar rama", + "command.showOutput": "Mostrar salida de GIT", + "command.ignore": "Agregar archivo a .gitignore", + "command.stash": "Guardar provisionalmente", + "command.stashPop": "Aplicar y quitar cambios guardados provisionalmente...", + "command.stashPopLatest": "Aplicar y quitar últimos cambios guardados provisionalmente...", + "config.enabled": "Si GIT está habilitado", + "config.path": "Ruta de acceso del ejecutable de GIT", + "config.autorefresh": "Indica si la actualización automática está habilitada", + "config.autofetch": "Si la búsqueda automática está habilitada", + "config.enableLongCommitWarning": "Si se debe advertir sobre los mensajes de confirmación largos", + "config.confirmSync": "Confirmar antes de sincronizar repositorios GIT", + "config.countBadge": "Controla el contador de insignia de Git. \"Todo\" cuenta todos los cambios. \"Seguimiento\" solamente cuenta los cambios realizados. \"Desactivado\" lo desconecta.", + "config.checkoutType": "Controla el tipo de ramas listadas cuando ejecuta \"Desproteger\". \"Todo\" muetra todas las referencias, \"local\" solamente las ramas locales y \"remoto\" las ramas remotas.", + "config.ignoreLegacyWarning": "Ignora las advertencias hereradas de Git", + "config.ignoreLimitWarning": "\nIgnora advertencias cuando se encuentran muchos cambios en un repositorio.", + "config.defaultCloneDirectory": "La ubicación predeterminada en la que se clona un repositorio git", + "config.enableSmartCommit": "Confirmar todos los cambios cuando no hay elementos almacenados provisionalmente.", + "config.enableCommitSigning": "Habilitar confirmar firma con GPG.", + "config.discardAllScope": "Controla qué cambios son descartados por el comando 'Descartar todos los cambios'. 'todos' descarta todos los cambios. 'seguidos' descarta sólo los ficheros en seguimiento. 'confirmar' muestra un cuadro de diálogo para confirmar cada vez la acción ejecutada." +} \ No newline at end of file diff --git a/i18n/esn/extensions/grunt/out/main.i18n.json b/i18n/esn/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..791102cd6e --- /dev/null +++ b/i18n/esn/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "La autodetección de Grunt falló con el error: {0}" +} \ No newline at end of file diff --git a/i18n/esn/extensions/grunt/package.i18n.json b/i18n/esn/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..8ae992c1e5 --- /dev/null +++ b/i18n/esn/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "Controla si la detección automática de tareas Grunt está activada o desactivada. Por defecto está activada." +} \ No newline at end of file diff --git a/i18n/esn/extensions/gulp/out/main.i18n.json b/i18n/esn/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..c64f348327 --- /dev/null +++ b/i18n/esn/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Detección automática gulp Error {0}" +} \ No newline at end of file diff --git a/i18n/esn/extensions/gulp/package.i18n.json b/i18n/esn/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..20b0db5436 --- /dev/null +++ b/i18n/esn/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "Controla si la detección automática de tareas Gulp esta activada/desactivada. El valor predeterminado es \"activada\"." +} \ No newline at end of file diff --git a/i18n/esn/extensions/html/client/out/htmlMain.i18n.json b/i18n/esn/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..74520baf85 --- /dev/null +++ b/i18n/esn/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "Servidor de lenguaje HTML" +} \ No newline at end of file diff --git a/i18n/esn/extensions/html/package.i18n.json b/i18n/esn/extensions/html/package.i18n.json new file mode 100644 index 0000000000..8d92df76ae --- /dev/null +++ b/i18n/esn/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "Habilitar o deshabilitar el formateador HTML predeterminado", + "html.format.wrapLineLength.desc": "Cantidad máxima de caracteres por línea (0 = deshabilitar).", + "html.format.unformatted.desc": "Lista de etiquetas, separadas por comas, a las que no se debe volver a aplicar formato. El valor predeterminado de \"null\" son todas las etiquetas mostradas en https://www.w3.org/TR/html5/dom.html#phrasing-content.", + "html.format.contentUnformatted.desc": "Lista de etiquetas, separadas por comas, en las que el contenido no debe volver a formatearse. \"null\" se establece de manera predeterminada en la etiqueta \"pre\".", + "html.format.indentInnerHtml.desc": "Aplicar sangría a las secciones y .", + "html.format.preserveNewLines.desc": "Indica si los saltos de línea existentes delante de los elementos deben conservarse. Solo funciona delante de los elementos, no dentro de las etiquetas o con texto.", + "html.format.maxPreserveNewLines.desc": "Número máximo de saltos de línea que deben conservarse en un fragmento. Use \"null\" para que el número sea ilimitado.", + "html.format.indentHandlebars.desc": "Formato y sangría {{#foo}} y {{/foo}}.", + "html.format.endWithNewline.desc": "Finalizar con una nueva línea.", + "html.format.extraLiners.desc": "Lista de etiquetas, separadas por comas, que deben tener una nueva línea adicional delante. \"null\" tiene como valores predeterminados \"head, body, /html\".", + "html.format.wrapAttributes.desc": "Ajustar atributos.", + "html.format.wrapAttributes.auto": "Ajustar atributos solo cuando se supera la longitud de la línea.", + "html.format.wrapAttributes.force": "Ajustar todos los atributos excepto el primero.", + "html.format.wrapAttributes.forcealign": "Ajustar todos los atributos excepto el primero y mantener la alineación.", + "html.format.wrapAttributes.forcemultiline": "Ajustar todos los atributos.", + "html.suggest.angular1.desc": "Configura si la compatibilidad con el lenguaje HTML integrada sugiere etiquetas y propiedades de Angular V1.", + "html.suggest.ionic.desc": "Configura si la compatibilidad con el lenguaje HTML integrada sugiere etiquetas, propiedades y valores de Ionic.", + "html.suggest.html5.desc": "Configura si la compatibilidad con el lenguaje HTML integrada sugiere etiquetas, propiedades y valores de HTML5.", + "html.trace.server.desc": "Hace un seguimiento de la comunicación entre VSCode y el servidor de lenguaje HTML.", + "html.validate.scripts": "Configura si la compatibilidad con el lenguaje HTML incorporado valida los scripts insertados.", + "html.validate.styles": "Configura si la compatibilidad con el lenguaje HTML incorporado valida los estilos insertados.", + "html.autoClosingTags": "Habilita o deshabilita el cierre automático de las etiquetas HTML." +} \ No newline at end of file diff --git a/i18n/esn/extensions/jake/out/main.i18n.json b/i18n/esn/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..e8b6edec82 --- /dev/null +++ b/i18n/esn/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "La detección automática de Jake falló con el error: {0}" +} \ No newline at end of file diff --git a/i18n/esn/extensions/jake/package.i18n.json b/i18n/esn/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..9633e3b2d2 --- /dev/null +++ b/i18n/esn/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "Controla si la detección automática de tareas Jake estan activada/desactivada. El valor predeterminado es \"activada\"." +} \ No newline at end of file diff --git a/i18n/esn/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/esn/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..d050a1243f --- /dev/null +++ b/i18n/esn/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "bower.json predeterminado", + "json.bower.error.repoaccess": "No se pudo ejecutar la solicitud del repositorio de Bower: {0}", + "json.bower.latest.version": "más reciente" +} \ No newline at end of file diff --git a/i18n/esn/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/esn/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..21eab7db6b --- /dev/null +++ b/i18n/esn/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "package.json predeterminado", + "json.npm.error.repoaccess": "No se pudo ejecutar la solicitud del repositorio de NPM: {0}", + "json.npm.latestversion": "Última versión del paquete en este momento", + "json.npm.majorversion": "Coincide con la versión principal más reciente (1.x.x)", + "json.npm.minorversion": "Coincide con la versión secundaria más reciente (1.2.x)", + "json.npm.version.hover": "Última versión: {0}" +} \ No newline at end of file diff --git a/i18n/esn/extensions/json/client/out/jsonMain.i18n.json b/i18n/esn/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..957dfa7ab2 --- /dev/null +++ b/i18n/esn/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "Servidor de lenguaje JSON" +} \ No newline at end of file diff --git a/i18n/esn/extensions/json/package.i18n.json b/i18n/esn/extensions/json/package.i18n.json new file mode 100644 index 0000000000..197764c3e8 --- /dev/null +++ b/i18n/esn/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "Asociar esquemas a archivos JSON en el proyecto actual", + "json.schemas.url.desc": "Una dirección URL a un esquema o una ruta de acceso relativa a un esquema en el directorio actual", + "json.schemas.fileMatch.desc": "Una matriz de patrones de archivo con los cuales coincidir cuando los archivos JSON se resuelvan en esquemas.", + "json.schemas.fileMatch.item.desc": "Un patrón de archivo que puede contener \"*\" con el cual coincidir cuando los archivos JSON se resuelvan en esquemas.", + "json.schemas.schema.desc": "La definición de esquema de la dirección URL determinada. Solo se necesita proporcionar el esquema para evitar los accesos a la dirección URL del esquema.", + "json.format.enable.desc": "Habilitar/deshabilitar formateador JSON predeterminado (requiere reiniciar)", + "json.tracing.desc": "Seguimiento de comunicación entre VS Code y el servidor de lenguaje JSON", + "json.colorDecorators.enable.desc": "Habilita o deshabilita decoradores de color", + "json.colorDecorators.enable.deprecationMessage": "El valor \"json.colorDecorators.enable\" está en desuso en favor de \"editor.colorDecorators\"." +} \ No newline at end of file diff --git a/i18n/esn/extensions/markdown/out/extension.i18n.json b/i18n/esn/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..a926c88ab6 --- /dev/null +++ b/i18n/esn/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "No se pudo cargar 'markdown.styles': {0}" +} \ No newline at end of file diff --git a/i18n/esn/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/esn/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..7adebb5480 --- /dev/null +++ b/i18n/esn/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "Se ha deshabilitado parte del contenido de este documento", + "preview.securityMessage.title": "Se ha deshabilitado el contenido potencialmente inseguro en la previsualización de Markdown. Para permitir el contenido inseguro o habilitar scripts cambie la configuración de la previsualización de Markdown", + "preview.securityMessage.label": "Alerta de seguridad de contenido deshabilitado" +} \ No newline at end of file diff --git a/i18n/esn/extensions/markdown/out/security.i18n.json b/i18n/esn/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..14b82a4d25 --- /dev/null +++ b/i18n/esn/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "Estricto", + "strict.description": "Cargar solo el contenido seguro", + "insecureContent.title": "Permitir contenido inseguro", + "insecureContent.description": "Habilitar la carga del contenido sobre http", + "disable.title": "Deshabilitar", + "disable.description": "Permitir todo el contenido y la ejecución de scripts. No se recomienda.", + "moreInfo.title": "Más información", + "preview.showPreviewSecuritySelector.title": "Seleccione configuración de seguridad para las previsualizaciones de Markdown en esta área de trabajo" +} \ No newline at end of file diff --git a/i18n/esn/extensions/markdown/package.i18n.json b/i18n/esn/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..a09d64164a --- /dev/null +++ b/i18n/esn/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "Establece cómo los saltos de línea son representados en la vista previa de markdown. Estableciendolo en 'true' crea un
para cada nueva línea.", + "markdown.preview.linkify": "Habilitar o deshabilitar la conversión de texto de tipo URL a enlaces en la vista previa de markdown.", + "markdown.preview.doubleClickToSwitchToEditor.desc": "Haga doble clic en la vista previa de Markdown para cambiar al editor.", + "markdown.preview.fontFamily.desc": "Controla la familia de la fuente utilizada en la previsualización del descuento.", + "markdown.preview.fontSize.desc": "Controla el tamaño de la fuente en píxeles utilizado en la previsualización del descuento.", + "markdown.preview.lineHeight.desc": "Controla la altura de línea utilizada en la previsualización del descuento. Este número es relativo al tamaño de la fuente.", + "markdown.preview.markEditorSelection.desc": "Marca la selección del editor actual en la vista previa de Markdown.", + "markdown.preview.scrollEditorWithPreview.desc": "Al desplazarse en la vista previa de Markdown, se actualiza la vista del editor.", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "Desplaza la vista previa de Markdown para revelar la línea del editor seleccionada actualmente.", + "markdown.preview.title": "Abrir vista previa", + "markdown.previewFrontMatter.dec": "Establece cómo se debe representar el asunto de la parte delantera de YAML en la vista previa del descuento. 'hide' quita el asunto de la parte delantera. De lo contrario, el asunto de la parte delantera se trata como contenido del descuento.", + "markdown.previewSide.title": "Abrir vista previa en el lateral", + "markdown.showSource.title": "Mostrar origen", + "markdown.styles.dec": "Una lista de direcciones URL o rutas de acceso locales a hojas de estilo CSS que utilizar desde la vista previa del descuento. Las tutas de acceso relativas se interpretan en relación con la carpeta abierta en el explorador. Si no hay ninguna carpeta abierta, se interpretan en relación con la ubicación del archivo del descuento. Todos los '\\' deben escribirse como '\\\\'.", + "markdown.showPreviewSecuritySelector.title": "Cambiar configuración de seguridad de vista previa", + "markdown.trace.desc": "Habilitar registro de depuración para las extensiones de Markdown. ", + "markdown.refreshPreview.title": "Actualizar vista previa" +} \ No newline at end of file diff --git a/i18n/esn/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/esn/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..dc2e369597 --- /dev/null +++ b/i18n/esn/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "Aceptar cambio actual", + "acceptIncomingChange": "Aceptar cambio entrante", + "acceptBothChanges": "Aceptar ambos cambios", + "compareChanges": "Comparar cambios" +} \ No newline at end of file diff --git a/i18n/esn/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/esn/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..42a4fa821d --- /dev/null +++ b/i18n/esn/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "El cursor de edición no se encuentra en un conflicto de fusión", + "compareChangesTitle": "{0}: Cambios actuales ⟷ Cambios entrantes", + "cursorOnCommonAncestorsRange": "El cursor del editor está dentro del bloque de ancestros comunes, por favor muévalo al bloque \"actual\" o al \"entrante\"", + "cursorOnSplitterRange": "El cursor del editor está dentro del separador de conflictos de fusión, muévalo al bloque \"actual\" o al \"entrante\" ", + "noConflicts": "No se encontraron conflictos en este archivo", + "noOtherConflictsInThisFile": "No hay más conflictos en este archivo" +} \ No newline at end of file diff --git a/i18n/esn/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/esn/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..0e0bd2eef4 --- /dev/null +++ b/i18n/esn/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(Cambio actual)", + "incomingChange": "(Cambio entrante)" +} \ No newline at end of file diff --git a/i18n/esn/extensions/merge-conflict/package.i18n.json b/i18n/esn/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..ec7d5698c0 --- /dev/null +++ b/i18n/esn/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "Fusionar conflicto", + "command.accept.all-incoming": "Aceptar todos los entrantes", + "command.accept.all-both": "Aceptar ambos", + "command.accept.current": "Aceptar actuales", + "command.accept.incoming": "Aceptar entrantes", + "command.accept.selection": "Aceptar selección", + "command.accept.both": "Aceptar ambos", + "command.next": "Siguiente conflicto", + "command.previous": "Conflicto anterior", + "command.compare": "Comparar conflicto actual", + "config.title": "Fusionar conflicto", + "config.codeLensEnabled": "Habilitar/deshabilitar CodeLens de fusionar bloque de conflictos en el editor", + "config.decoratorsEnabled": "Habilitar/deshabilitar decoradores de conflictos de fusión en el editor" +} \ No newline at end of file diff --git a/i18n/esn/extensions/npm/package.i18n.json b/i18n/esn/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..8bf683e85c --- /dev/null +++ b/i18n/esn/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "Controla si la detección automática de scripts npm está activada o desactivada. Por defecto está activada.", + "config.npm.runSilent": "Ejecutar comandos de npm con la opción '--silent' " +} \ No newline at end of file diff --git a/i18n/esn/extensions/php/out/features/validationProvider.i18n.json b/i18n/esn/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..810560a51d --- /dev/null +++ b/i18n/esn/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "¿Permite la ejecución de {0} (definido como valor del área de trabajo) para detectar errores en archivos PHP?", + "php.yes": "Permitir", + "php.no": "No permitir", + "wrongExecutable": "No se puede validar porque {0} no es un ejecutable PHP válido. Use el ajuste \"php.validate.executablePath\" para configurar el ejecutable PHP.", + "noExecutable": "No se puede validar porque no hay ningún ejecutable PHP establecido. Use el ajuste \"php.validate.executablePath\" para configurar el ejecutable de PHP.", + "unknownReason": "No se pudo ejecutar el archivo PHP con la ruta de acceso: {0}. Se desconoce el motivo." +} \ No newline at end of file diff --git a/i18n/esn/extensions/php/package.i18n.json b/i18n/esn/extensions/php/package.i18n.json new file mode 100644 index 0000000000..2d7f8e6518 --- /dev/null +++ b/i18n/esn/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "Configura si se habilitan las sugerencias del lenguaje PHP integradas. La asistencia sugiere variables y opciones globales de PHP.", + "configuration.validate.enable": "Habilita o deshabilita la validación integrada de PHP.", + "configuration.validate.executablePath": "Señala al ejecutable PHP.", + "configuration.validate.run": "Indica si linter se ejecuta al guardar o al escribir.", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "No permitir el ejecutable de validación de PHP (como configuración de área de trabajo)" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/esn/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..becf7a05c4 --- /dev/null +++ b/i18n/esn/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "Usando TypeScript ({1}) para las características del editor. TypeScript ({0}) es instalado globalmente en su máquina. Los errores en VS Code pueden diferir de los de TSC", + "moreInformation": "Más información", + "doNotCheckAgain": "No volver a comprobar", + "close": "Cerrar", + "updateTscCheck": "La configuración del usuario de \"typescript.check.tscVersion\" se actualizó a false." +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/esn/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..638e76b182 --- /dev/null +++ b/i18n/esn/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "Adquiriendo typings...", + "acquiringTypingsDetail": "Adquiriendo definiciones de typings para IntelliSense." +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/esn/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..a25a71767d --- /dev/null +++ b/i18n/esn/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "Habilita la verificación semántica en un archivo de JavaScript. Debe estar al principio del archivo.", + "ts-nocheck": "Deshabilita la verificación semántica en un archivo de JavaScript. Debe estar al principio del archivo.", + "ts-ignore": "Suprime los errores @ts-check en la siguiente línea de un archivo. " +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/esn/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..5799df15eb --- /dev/null +++ b/i18n/esn/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 implementación", + "manyImplementationLabel": "{0} implementaciones", + "implementationsErrorLabel": "No se pueden determinar las implementaciones" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/esn/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..ca987ab92e --- /dev/null +++ b/i18n/esn/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "Comentario de JSDoc" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/esn/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..9a594fdf06 --- /dev/null +++ b/i18n/esn/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 referencia", + "manyReferenceLabel": "{0} referencias", + "referenceErrorLabel": "No se pudieron determinar las referencias" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/esn/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..a8411365ab --- /dev/null +++ b/i18n/esn/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "seguir - {0}", + "buildTscLabel": "construir - {0}" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/typescriptMain.i18n.json b/i18n/esn/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..ae4ce65c6f --- /dev/null +++ b/i18n/esn/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "Abra una carpeta en VS Code para usar un proyecto de TypeScript o JavaScript", + "typescript.projectConfigUnsupportedFile": "No se pudo determinar el proyecto de TypeScript o JavaScript. Tipo de archivo no compatible", + "typescript.projectConfigCouldNotGetInfo": "No se pudo determinar el proyecto de TypeScript o JavaScript", + "typescript.noTypeScriptProjectConfig": "El archivo no forma parte de un proyecto de TypeScript", + "typescript.noJavaScriptProjectConfig": "El archivo no forma parte de un proyecto de JavaScript", + "typescript.configureTsconfigQuickPick": "Configurar tsconfig.json", + "typescript.configureJsconfigQuickPick": "Configurar jsconfig.json", + "typescript.projectConfigLearnMore": "Más información" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/esn/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..082e393a4e --- /dev/null +++ b/i18n/esn/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "La ruta de acceso {0} no apunta a una instalación válida de tsserver. Se usará la versión de TypeScript del paquete.", + "serverCouldNotBeStarted": "El servidor de lenguaje TypeScript no se pudo iniciar. El mensaje de error es: {0}", + "typescript.openTsServerLog.notSupported": "El registro del servidor de TS requiere TS 2.2.2+", + "typescript.openTsServerLog.loggingNotEnabled": "Los registros del servidor TS están desconectados. Establezca \"typescript.tsserver.log\" y reinicie el servidor TS para activar los registros.", + "typescript.openTsServerLog.enableAndReloadOption": "Habilite el registro y reinicie el servidor TS", + "typescript.openTsServerLog.noLogFile": "El servidor de TS no ha iniciado el registro.", + "openTsServerLog.openFileFailedFailed": "No se puede abrir el archivo de registro del servidor de TS", + "serverDiedAfterStart": "El servicio de lenguaje TypeScript finalizó de forma inesperada cinco veces después de haberse iniciado y no se reiniciará.", + "serverDiedReportIssue": "Notificar problema", + "serverDied": "El servicio de lenguaje Typescript finalizó de forma inesperada cinco veces en los últimos cinco minutos." +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/utils/api.i18n.json b/i18n/esn/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..d9273a741a --- /dev/null +++ b/i18n/esn/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "versión inválida" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/utils/logger.i18n.json b/i18n/esn/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/esn/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/esn/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..502d38a06c --- /dev/null +++ b/i18n/esn/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "Para habilitar las características de lenguaje de JavaScript/TypeScript en todo el proyecto, excluya las carpetas con muchos archivos, como: {0}", + "hintExclude.generic": "Para habilitar las características de idioma de JavaScript/TypeScript IntelliSense en todo el proyecto, excluya las carpetas de tamaño grande con archivos de origen en los que no trabaje.", + "large.label": "Configurar exclusiones", + "hintExclude.tooltip": "Para habilitar las características de idioma de JavaScript/TypeScript IntelliSense en todo el proyecto, excluya las carpetas de tamaño grande con archivos de origen en los que no trabaje." +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/esn/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..625d0dac0a --- /dev/null +++ b/i18n/esn/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "Recuperando cambios en los datos para un mejor rendimiento de TypeScript IntelliSense", + "typesInstallerInitializationFailed.title": "No se pudieron instalar archivos de términos para las características de lenguaje de JavaScript. Asegúrese de que NPM está instalado o configure \"typescript.npm\" en la configuración de usuario", + "typesInstallerInitializationFailed.moreInformation": "Más información", + "typesInstallerInitializationFailed.doNotCheckAgain": "No volver a comprobar", + "typesInstallerInitializationFailed.close": "Cerrar" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/esn/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..4cada881ad --- /dev/null +++ b/i18n/esn/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "Utilizar la versión de VS Code", + "useWorkspaceVersionOption": "Usar versión del área de trabajo", + "learnMore": "Más información", + "selectTsVersion": "Seleccionar la versión de TypeScript usada para las características del lenguaje de JavaScript y TypeScript" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/esn/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..d0284dd2bf --- /dev/null +++ b/i18n/esn/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "No se pudo cargar la versión de TypeScript en esta ruta", + "noBundledServerFound": "Otra aplicación (por ejemplo, una herramienta de detección de virus con un comportamiento erróneo) eliminó el tsserver de VSCode. Debe reinstalar el VS Code." +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/package.i18n.json b/i18n/esn/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..645e58cd7e --- /dev/null +++ b/i18n/esn/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "Volver a cargar el proyecto", + "javascript.reloadProjects.title": "Volver a cargar el proyecto", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "Complete las funciones con la signatura de parámetro.", + "typescript.tsdk.desc": "Especifica la ruta de acceso de carpeta que contiene los archivos lib*.d.ts y tsserver que se van a usar.", + "typescript.disableAutomaticTypeAcquisition": "Desactiva la adquisición automática de tipos. Requiere TypeScript >= 2.0.6.", + "typescript.tsserver.log": "votes Habilita los registros del servidor TS a un archivo. Este registro se puede utilizar para diagnosticar problemas en el servidor TS. Este registro puede contener rutas de acceso, código fuente y posiblemente otra información sensitiva acerca del proyecto.", + "typescript.tsserver.trace": "Habilita el seguimiento de mensajes al servidor TS. Este seguimiento se puede utilizar para diagnosticar problemas en el servidor TS. Este seguimiento puede contener rutas de acceso, código fuente y posiblemente otra información sensitiva acerca del proyecto.", + "typescript.validate.enable": "Habilita o deshabilita la validación de TypeScript.", + "typescript.format.enable": "Habilita o deshabilita el formateador predeterminado de TypeScript.", + "javascript.format.enable": "Habilita o deshabilita el formateador predeterminado de JavaScript.", + "format.insertSpaceAfterCommaDelimiter": "Define el tratamiento del espacio después de un delimitador de coma.", + "format.insertSpaceAfterConstructor": "Define el manejo del espacio después de la palabra clave constructor. Requiere TypeScript >= 2.3.0", + "format.insertSpaceAfterSemicolonInForStatements": " Define el tratamiento del espacio después de punto y coma en una instrucción for.", + "format.insertSpaceBeforeAndAfterBinaryOperators": "Define el tratamiento del espacio después de un operador binario.", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "Define el tratamiento del espacio después de las palabras clave en una instrucción de flujo de control.", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "Define el tratamiento del espacio después de la palabra clave function para las funciones anónimas.", + "format.insertSpaceBeforeFunctionParenthesis": "Define el tratamiento del espacio delante de los paréntesis de los argumentos de las funciones. Requiere TypeScript >= 2.1.5.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "Define el tratamiento del espacio después de los paréntesis de apertura y antes de los paréntesis de cierre con contenido.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "Define el tratamiento del espacio después de los corchetes de apertura y antes de los corchetes de cierre con contenido.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Define el tratamiento del espacio después de la llave de apertura y antes de la llave de cierre. Requiere TypeScript >= 2.0.6.", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "Define el tratamiento del espacio después de la llave de apertura y antes de la llave de cierre de cadenas de plantilla. Requiere TypeScript >= 2.0.6.", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Define el tratamiento del espacio después de la llave de apertura y antes de la llave de cierre de las expresiones JSX. Requiere TypeScript >= 2.0.6.", + "format.insertSpaceAfterTypeAssertion": "Define el control del espacio después de las aserciones de tipos en TypeScript. Requiere TypeScript >= 2.4", + "format.placeOpenBraceOnNewLineForFunctions": "Define si una llave de apertura se incluye en una nueva línea para las funciones o no.", + "format.placeOpenBraceOnNewLineForControlBlocks": "Define si una llave de apertura se incluye en una nueva línea para los bloques de control o no.", + "javascript.validate.enable": "Habilita o deshabilita la validación de JavaScript.", + "typescript.goToProjectConfig.title": "Ir a configuración del proyecto", + "javascript.goToProjectConfig.title": "Ir a configuración del proyecto", + "javascript.referencesCodeLens.enabled": "Habilitar/deshabilitar las referencias de CodeLens en los archivos de JavaScript.", + "typescript.referencesCodeLens.enabled": "Habilitar/deshabilitar las referencias de CodeLens en los archivos de TypeScript. Requiere TypeScript >= 2.0.6.", + "typescript.implementationsCodeLens.enabled": "Habilita o deshabilita implementaciones de CodeLens. Requiere TypeScript >= 2.2.0.", + "typescript.openTsServerLog.title": "Abrir registro del servidor de TS", + "typescript.restartTsServer": "Reiniciar servidor TS", + "typescript.selectTypeScriptVersion.title": "Seleccionar versión de TypeScript", + "jsDocCompletion.enabled": "Habilita o deshabilita comentarios automaticos de JSDoc", + "javascript.implicitProjectConfig.checkJs": "Habilita/deshabilita la comprobación semántica de los archivos JavaScript. Los archivos jsconfig.json o tsconfig.json reemplazan esta configuración. Se requiere TypeScript >=2.3.1.", + "typescript.npm": "Especifica la ruta de acceso al archivo ejecutable de NPM usada para la adquisición automática de tipos. Requiere TypeScript >= 2.3.4.", + "typescript.check.npmIsInstalled": "Compruebe si NPM está instalado para la adquisición automática de tipos.", + "javascript.nameSuggestions": "Habilitar/deshabilitar nombres únicos de la lista de sugerencias en los archivos de JavaScript. ", + "typescript.tsc.autoDetect": "Controla si la detección automática de tareas TSC está activada o desactivada.", + "typescript.problemMatchers.tsc.label": "Problemas de TypeScript", + "typescript.problemMatchers.tscWatch.label": "Problemas de TypeScript (modo de inspección)" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/esn/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/esn/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/esn/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..7d4f75b6d2 --- /dev/null +++ b/i18n/esn/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (ocurrió de nuevo)" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/esn/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..7bea9e8645 --- /dev/null +++ b/i18n/esn/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "entrada" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/esn/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..8050b49caa --- /dev/null +++ b/i18n/esn/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "Coincidir mayúsculas y minúsculas", + "wordsDescription": "Solo palabras completas", + "regexDescription": "Usar expresión regular" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/esn/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..404f90eb91 --- /dev/null +++ b/i18n/esn/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Error: {0}", + "alertWarningMessage": "Advertencia: {0}", + "alertInfoMessage": "Información: {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/esn/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..001e2ed2f5 --- /dev/null +++ b/i18n/esn/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0} x {1} {2}", + "largeImageError": "La imagen es muy grande para mostrar en el editor", + "resourceOpenExternalButton": "¿Abrir la imagen mediante un programa externo?", + "nativeBinaryError": "El archivo no se mostrará en el editor porque es binario, muy grande o usa una codificación de texto no compatible.", + "sizeB": "{0} B", + "sizeKB": "{0} KB", + "sizeMB": "{0} MB", + "sizeGB": "{0} GB", + "sizeTB": "{0} TB" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/esn/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..3c54ed47be --- /dev/null +++ b/i18n/esn/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "Más" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/common/errorMessage.i18n.json b/i18n/esn/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..7a08c76d25 --- /dev/null +++ b/i18n/esn/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}. Código de error: {1}", + "error.permission.verbose": "Permiso denegado (HTTP {0})", + "error.permission": "Permiso denegado", + "error.http.verbose": "{0} (HTTP {1}: {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "Error de conexión desconocido ({0})", + "error.connection.unknown": "Error de conexión desconocido. Es posible que ya no esté conectado a Internet o que el servidor al que se había conectado esté sin conexión.", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "Se ha producido un error desconocido. Consulte el registro para obtener más detalles.", + "nodeExceptionMessage": "Error del sistema ({0})", + "error.moreErrors": "{0} ({1} errores en total)" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/common/json.i18n.json b/i18n/esn/src/vs/base/common/json.i18n.json new file mode 100644 index 0000000000..4d1f7fa1b1 --- /dev/null +++ b/i18n/esn/src/vs/base/common/json.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Símbolo no válido", + "error.invalidNumberFormat": "Formato de número no válido", + "error.propertyNameExpected": "Se esperaba el nombre de la propiedad", + "error.valueExpected": "Se esperaba un valor", + "error.colonExpected": "Se esperaban dos puntos", + "error.commaExpected": "Se esperaba una coma", + "error.closeBraceExpected": "Se esperaba una llave de cierre", + "error.closeBracketExpected": "Se esperaba un corchete de cierre", + "error.endOfFileExpected": "Se esperaba un fin de archivo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/esn/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..4d1f7fa1b1 --- /dev/null +++ b/i18n/esn/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Símbolo no válido", + "error.invalidNumberFormat": "Formato de número no válido", + "error.propertyNameExpected": "Se esperaba el nombre de la propiedad", + "error.valueExpected": "Se esperaba un valor", + "error.colonExpected": "Se esperaban dos puntos", + "error.commaExpected": "Se esperaba una coma", + "error.closeBraceExpected": "Se esperaba una llave de cierre", + "error.closeBracketExpected": "Se esperaba un corchete de cierre", + "error.endOfFileExpected": "Se esperaba un fin de archivo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/common/keybindingLabels.i18n.json b/i18n/esn/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..34c8000082 --- /dev/null +++ b/i18n/esn/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Mayús", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Mayús", + "altKey.long": "Alt", + "cmdKey.long": "Comando", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/common/processes.i18n.json b/i18n/esn/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..f1be89f8d9 --- /dev/null +++ b/i18n/esn/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "Error: La información del ejecutable debe definir un comando de tipo cadena.", + "ExecutableParser.isShellCommand": "Advertencia: isShellCommand debe ser de tipo booleano. Se ignora el valor {0}.", + "ExecutableParser.args": "Advertencia: Los argumentos deben ser de tipo \"string[]\". Se ignora el valor {0}.", + "ExecutableParser.invalidCWD": "Advertencia: options.cwd debe ser de tipo cadena. Se ignora el valor {0}." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/common/severity.i18n.json b/i18n/esn/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..08f02bb11c --- /dev/null +++ b/i18n/esn/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "Error", + "sev.warning": "Advertencia", + "sev.info": "Información" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/node/processes.i18n.json b/i18n/esn/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..e951232c74 --- /dev/null +++ b/i18n/esn/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "No se puede ejecutar un comando shell en una unidad UNC." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/node/zip.i18n.json b/i18n/esn/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..402eec51e4 --- /dev/null +++ b/i18n/esn/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "{0} no se encontró dentro del archivo zip." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/esn/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..d608ec524f --- /dev/null +++ b/i18n/esn/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0}, selector", + "quickOpenAriaLabel": "selector" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/esn/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..d0185d707e --- /dev/null +++ b/i18n/esn/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "Selector rápido. Escriba para restringir los resultados.", + "treeAriaLabel": "Selector rápido" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/esn/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..a71aa9e819 --- /dev/null +++ b/i18n/esn/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Contraer" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/code/electron-main/auth.i18n.json b/i18n/esn/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..9199be754d --- /dev/null +++ b/i18n/esn/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "Autenticación de proxy requerida", + "proxyauth": "El proxy {0} requiere autenticación." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/code/electron-main/menus.i18n.json b/i18n/esn/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..354b1f3cfb --- /dev/null +++ b/i18n/esn/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "Archivo", + "mEdit": "Editar", + "mSelection": "Selección", + "mView": "Ver", + "mGoto": "Ir", + "mDebug": "&&Depurar", + "mWindow": "Ventana", + "mHelp": "Ayuda", + "mTask": "&&Tareas", + "miNewWindow": "&&Nueva ventana", + "mAbout": "Acerca de {0}", + "mServices": "Servicios", + "mHide": "Ocultar {0}", + "mHideOthers": "Ocultar otros", + "mShowAll": "Mostrar todo", + "miQuit": "Salir de {0}", + "miNewFile": "&&Nuevo archivo", + "miOpen": "Abrir...", + "miOpenWorkspace": "&& Abrir el espacio de trabajo...", + "miOpenFolder": "Abrir &&carpeta...", + "miOpenFile": "&&Abrir archivo...", + "miOpenRecent": "Abrir &&reciente", + "miSaveWorkspaceAs": "&& Guardar espacio de trabajo como...", + "miAddFolderToWorkspace": "&&Agregar carpeta al área de trabajo...", + "miSave": "&&Guardar", + "miSaveAs": "Guardar &&como...", + "miSaveAll": "Guardar t&&odo", + "miAutoSave": "Guardado automático", + "miRevert": "Revertir a&&rchivo", + "miCloseWindow": "C&&errar ventana", + "miCloseWorkspace": "Cerrar &&área de trabajo", + "miCloseFolder": "Cerrar &&carpeta", + "miCloseEditor": "&&Cerrar editor", + "miExit": "Salir", + "miOpenSettings": "Configuración", + "miOpenKeymap": "&&Métodos abreviados de teclado", + "miOpenKeymapExtensions": "&&Extensiones de distribución de teclado", + "miOpenSnippets": "&&Fragmento de código del usuario", + "miSelectColorTheme": "&&Tema de color", + "miSelectIconTheme": "&&Tema de icono de archivo", + "miPreferences": "&&Preferencias", + "miReopenClosedEditor": "&&Volver a abrir el editor cerrado", + "miMore": "&&Más...", + "miClearRecentOpen": "&&Borrar abiertos recientemente", + "miUndo": "&&Deshacer", + "miRedo": "&&Rehacer", + "miCut": "Cortar", + "miCopy": "&&Copiar", + "miPaste": "&&Pegar", + "miFind": "&&Buscar", + "miReplace": "&&Reemplazar", + "miFindInFiles": "Buscar &&en archivos", + "miReplaceInFiles": "Reemplazar &&en archivos", + "miEmmetExpandAbbreviation": "Emmet: E&&xpandir abreviatura", + "miShowEmmetCommands": "E&&mmet...", + "miToggleLineComment": "&&Alternar comentario de línea", + "miToggleBlockComment": "Alternar &&Bloquear comentario", + "miMultiCursorAlt": "Cambiar a Alt+Clic para cursor múltiple", + "miMultiCursorCmd": "Cambiar a Cmd+Clic para cursor múltiple", + "miMultiCursorCtrl": "Cambiar a Ctrl+Clic para cursor múltiple", + "miInsertCursorAbove": "&&Agregar cursor arriba", + "miInsertCursorBelow": "A&&gregar cursor abajo", + "miInsertCursorAtEndOfEachLineSelected": "Agregar c&&ursores a extremos de línea", + "miAddSelectionToNextFindMatch": "Agregar &&siguiente repetición", + "miAddSelectionToPreviousFindMatch": "Agregar r&&epetición anterior", + "miSelectHighlights": "Seleccionar todas las &&repeticiones", + "miCopyLinesUp": "&&Copiar línea arriba", + "miCopyLinesDown": "Co&&piar línea abajo", + "miMoveLinesUp": "Mo&&ver línea arriba", + "miMoveLinesDown": "Mover &&línea abajo", + "miSelectAll": "&&Seleccionar todo", + "miSmartSelectGrow": "&&Expandir selección", + "miSmartSelectShrink": "&&Reducir selección", + "miViewExplorer": "&&Explorador", + "miViewSearch": "&&Buscar", + "miViewSCM": "S&&CM", + "miViewDebug": "&&Depurar", + "miViewExtensions": "E&&xtensiones", + "miToggleOutput": "&&Salida", + "miToggleDebugConsole": "Consola de de&&puración", + "miToggleIntegratedTerminal": "&&Terminal integrado", + "miMarker": "&&Problemas", + "miAdditionalViews": "Vistas &&adicionales", + "miCommandPalette": "&&Paleta de comandos...", + "miToggleFullScreen": "Alternar &&pantalla completa", + "miToggleZenMode": "Alternar modo zen", + "miToggleMenuBar": "Alternar &&barra de menús", + "miSplitEditor": "Dividir &&editor", + "miToggleEditorLayout": "Alternar diseño del grupo de &&editores", + "miToggleSidebar": "&&Alternar barra lateral", + "miMoveSidebarRight": "&&Mover barra lateral a la derecha", + "miMoveSidebarLeft": "&&Mover barra lateral a la izquierda", + "miTogglePanel": "Alternar &&panel", + "miHideStatusbar": "&&Ocultar barra de estado", + "miShowStatusbar": "&&Mostrar barra de estado", + "miHideActivityBar": "Ocultar &&barra de actividades", + "miShowActivityBar": "Mostrar &&barra de actividades", + "miToggleWordWrap": "Alternar &&ajuste automático de línea", + "miToggleMinimap": "Alternar &&minimapa", + "miToggleRenderWhitespace": "Alternar &&representación de espacio en blanco", + "miToggleRenderControlCharacters": "Alternar caracteres de control &&", + "miZoomIn": "&&Ampliar", + "miZoomOut": "Redu&&cir", + "miZoomReset": "&&Restablecer zoom", + "miBack": "Atrás", + "miForward": "&&Reenviar", + "miNextEditor": "&&Editor siguiente", + "miPreviousEditor": "&&Editor anterior", + "miNextEditorInGroup": "&&Editor siguiente usado del grupo", + "miPreviousEditorInGroup": "&&Editor anterior usado del grupo", + "miSwitchEditor": "Cambiar &&editor", + "miFocusFirstGroup": "&&Primer grupo", + "miFocusSecondGroup": "&&Segundo grupo", + "miFocusThirdGroup": "&&Tercer grupo", + "miNextGroup": "&&Grupo siguiente", + "miPreviousGroup": "&&Grupo anterior", + "miSwitchGroup": "Cambiar &&grupo", + "miGotoFile": "Ir a &&archivo...", + "miGotoSymbolInFile": "Ir al &&símbolo en el archivo...", + "miGotoSymbolInWorkspace": "Ir al símbolo en el área de &&trabajo...", + "miGotoDefinition": "Ir a &&definición", + "miGotoTypeDefinition": "Ir a la definición de &&Tipo", + "miGotoImplementation": "Ir a la &&Implementación", + "miGotoLine": "Ir a la &&línea...", + "miStartDebugging": "I&&niciar depuración", + "miStartWithoutDebugging": "Iniciar &&sin depurar", + "miStopDebugging": "&&Detener depuración", + "miRestart Debugging": "&&Reiniciar depuración", + "miOpenConfigurations": "Abrir &&configuraciones", + "miAddConfiguration": "Agregar configuración...", + "miStepOver": "Depurar paso a paso por proce&&dimientos", + "miStepInto": "&&Depurar paso a paso por instrucciones", + "miStepOut": "Depurar paso a paso para &&salir", + "miContinue": "&&Continuar", + "miToggleBreakpoint": "Alter&&nar punto de interrupción", + "miConditionalBreakpoint": "Punto de interrupción &&condicional...", + "miColumnBreakpoint": "Punto de interrupción de c&&olumna", + "miFunctionBreakpoint": "Punto de interrupción de &&función...", + "miNewBreakpoint": "&&Nuevo punto de interrupción", + "miEnableAllBreakpoints": "Habilitar todos los puntos de interrupción", + "miDisableAllBreakpoints": "&&Deshabilitar todos los puntos de interrupción", + "miRemoveAllBreakpoints": "Quitar &&todos los puntos de interrupción", + "miInstallAdditionalDebuggers": "&&Instalar los depuradores adicionales...", + "mMinimize": "Minimizar", + "mZoom": "Zoom", + "mBringToFront": "Traer todo al frente", + "miSwitchWindow": "Cambiar &&Ventana...", + "miToggleDevTools": "&&Alternar herramientas de desarrollo", + "miAccessibilityOptions": "&&Opciones de accesibilidad", + "miReportIssues": "&&Notificar problemas", + "miWelcome": "&&Bienvenido", + "miInteractivePlayground": "Área de juegos &&interactiva", + "miDocumentation": "&&Documentación", + "miReleaseNotes": "&&Notas de la versión", + "miKeyboardShortcuts": "&&Referencia de métodos abreviados de teclado", + "miIntroductoryVideos": "&&Vídeos de introducción", + "miTipsAndTricks": "&&Sugerencias y trucos", + "miTwitter": "&&Síganos en Twitter", + "miUserVoice": "&&Buscar solicitudes de características", + "miLicense": "Ver &&licencia", + "miPrivacyStatement": "&&Declaración de privacidad", + "miAbout": "&&Acerca de", + "miRunTask": "&&Ejecutar Tarea...", + "miBuildTask": "Ejecutar &&tarea de compilación...", + "miRunningTask": "Mostrar las &&tareas en ejecución", + "miRestartTask": "R&&einiciar tarea en ejecución...", + "miTerminateTask": "&&Finalizar tarea...", + "miConfigureTask": "&&Configurar tarea", + "miConfigureBuildTask": "Configurar tarea de compilación predeterminada", + "accessibilityOptionsWindowTitle": "Opciones de accesibilidad", + "miRestartToUpdate": "Reiniciar para actualizar...", + "miCheckingForUpdates": "Buscando actualizaciones...", + "miDownloadUpdate": "Descargar actualización disponible", + "miDownloadingUpdate": "Descargando actualización...", + "miInstallingUpdate": "Instalando actualización...", + "miCheckForUpdates": "Buscar actualizaciones...", + "aboutDetail": "\nVersión: {0}\nConfirmación: {1}\nFecha: {2}\nShell: {3}\nRepresentador: {4}\nNodo {5}\nArquitectura {6}", + "okButton": "Aceptar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/code/electron-main/window.i18n.json b/i18n/esn/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..9f4ff472a8 --- /dev/null +++ b/i18n/esn/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "Para acceder a la barra de menús, también puede presionar la tecla **Alt**." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/code/electron-main/windows.i18n.json b/i18n/esn/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..73d27f3023 --- /dev/null +++ b/i18n/esn/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "Aceptar", + "pathNotExistTitle": "La ruta no existe", + "pathNotExistDetail": "Parece que la ruta '{0}' ya no existe en el disco.", + "openWorkspace": "&&Abrir...", + "openWorkspaceTitle": "Abrir área de trabajo", + "save": "&&Guardar", + "doNotSave": "&&No guardar", + "cancel": "Cancelar", + "saveWorkspaceMessage": "¿Quiere guardar la configuración del área de trabajo como un archivo?", + "saveWorkspaceDetail": "Guarde el área de trabajo si tiene pensado volverla a abrir.", + "saveWorkspace": "Guardar área de trabajo", + "reopen": "Volver a abrir", + "wait": "Siga esperando", + "close": "Cerrar", + "appStalled": "La ventana ha dejado de responder.", + "appStalledDetail": "Puede volver a abrir la ventana, cerrarla o seguir esperando.", + "appCrashed": "La ventana se bloqueó", + "appCrashedDetail": "Sentimos las molestias. Puede volver a abrir la ventana para continuar donde se detuvo.", + "open": "Abrir", + "openFolder": "Abrir carpeta", + "openFile": "Abrir archivo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/code/node/cliProcessMain.i18n.json b/i18n/esn/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..67a75345e0 --- /dev/null +++ b/i18n/esn/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "La extensión '{0}' no se encontró.", + "notInstalled": "La extensión '{0}' no está instalada.", + "useId": "Asegúrese de usar el identificador de extensión completo, incluido el publicador, por ejemplo: {0}.", + "successVsixInstall": "La extensión '{0}' se ha instalado correctamente.", + "alreadyInstalled": "La extensión '{0}' ya está instalada.", + "foundExtension": "Se encontró '{0}' en Marketplace.", + "installing": "Instalando...", + "successInstall": "La extensión '{0}' v{1} se instaló correctamente.", + "uninstalling": "Desinstalando {0}...", + "successUninstall": "La extensión '{0}' se desinstaló correctamente." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/esn/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..2b9e3b0a59 --- /dev/null +++ b/i18n/esn/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "Los archivos no se pueden comparar porque uno de ellos es demasiado grande." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/esn/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..6a9edb4d30 --- /dev/null +++ b/i18n/esn/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Cerrar", + "header": "Diferencia {0} de {1}: original {2}, {3} líneas, modificado {4}, {5} líneas", + "blankLine": "vacío", + "equalLine": "original {0}, modificado {1}: {2}", + "insertLine": "+ modificado {0}: {1}", + "deleteLine": "- original {0}: {1}", + "editor.action.diffReview.next": "Ir a la siguiente diferencia", + "editor.action.diffReview.prev": "Ir a la diferencia anterior" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/esn/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..b9ac5e0ad7 --- /dev/null +++ b/i18n/esn/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "Editor", + "fontFamily": "Controla la familia de fuentes.", + "fontWeight": "Controla el grosor de la fuente.", + "fontSize": "Controla el tamaño de fuente en píxeles.", + "lineHeight": "Controla la altura de línea. Utilice 0 para calcular el valor de lineHeight a partir de fontSize.", + "letterSpacing": "Controla el espacio entre letras en pixels.", + "lineNumbers": "Controla la presentación de los números de línea. Los valores posibles son \"on\", \"off\" y \"relative\". \"relative\" muestra el número de líneas desde la posición actual del cursor.", + "rulers": "Columnas en las que mostrar reglas verticales", + "wordSeparators": "Caracteres que se usarán como separadores de palabras al realizar operaciones o navegaciones relacionadas con palabras.", + "tabSize": "El número de espacios a los que equivale una tabulación. Este valor se invalida según el contenido del archivo cuando `editor.detectIndentation` está activado.", + "tabSize.errorMessage": "Se esperaba \"number\". Tenga en cuenta que el ajuste \"editor.detectIndentation\" ha reemplazado al valor \"auto\".", + "insertSpaces": "Insertar espacios al presionar TAB. Este valor se invalida en función del contenido del archivo cuando \"editor.detectIndentation\" está activado.", + "insertSpaces.errorMessage": "Se esperaba \"boolean\". Tenga en cuenta que el ajuste \"editor.detectIndentation\" ha reemplazado al valor \"auto\".", + "detectIndentation": "Al abrir un archivo, se detectarán `editor.tabSize` y `editor.insertSpaces` en función del contenido del archivo.", + "roundedSelection": "Controla si las selecciones tienen esquinas redondeadas", + "scrollBeyondLastLine": "Controla si el editor se seguirá desplazando después de la última línea", + "smoothScrolling": "Controla si el editor se desplaza con una animación", + "minimap.enabled": "Controla si se muestra el minimapa", + "minimap.showSlider": "Controla si se oculta automáticamente el control deslizante del minimapa. Los valores posibles son \"always\" y \"mouseover\".", + "minimap.renderCharacters": "Presentar los caracteres reales en una línea (por oposición a bloques de color)", + "minimap.maxColumn": "Limitar el ancho del minimapa para presentar como mucho un número de columnas determinado", + "find.seedSearchStringFromSelection": "Controla si se inicializa la cadena de búsqueda en Buscar widget en la selección del editor", + "find.autoFindInSelection": "Controla si el indicador Buscar en selección se activa cuando se seleccionan varios caracteres o líneas de texto en el editor", + "wordWrap.off": "Las líneas no se ajustarán nunca.", + "wordWrap.on": "Las líneas se ajustarán en el ancho de la ventanilla.", + "wordWrap.wordWrapColumn": "Las líneas se ajustarán en \"editor.wordWrapColumn\".", + "wordWrap.bounded": "Las líneas se ajustarán al valor que sea inferior: el tamaño de la ventanilla o el valor de \"editor.wordWrapColumn\".", + "wordWrap": "Controla cómo se deben ajustar las líneas. Pueden ser:\n - \"off\" (deshabilitar ajuste),\n - \"on\" (ajuste de ventanilla),\n - \"wordWrapColumn\" (ajustar en \"editor.wordWrapColumn\") o\n - \"bounded\" (ajustar en la parte mínima de la ventanilla y \"editor.wordWrapColumn\").", + "wordWrapColumn": "Controls the wrapping column of the editor when `editor.wordWrap` is 'wordWrapColumn' or 'bounded'.", + "wrappingIndent": "Controla el sangrado de las líneas ajustadas. Puede ser uno los valores 'none', 'same' o 'indent'.", + "mouseWheelScrollSensitivity": "Se utilizará un multiplicador en los eventos de desplazamiento de la rueda del mouse `deltaX` y `deltaY`", + "multiCursorModifier.ctrlCmd": "Se asigna a \"Control\" en Windows y Linux y a \"Comando\" en OSX.", + "multiCursorModifier.alt": "Se asigna a \"Alt\" en Windows y Linux y a \"Opción\" en OSX.", + "multiCursorModifier": "El modificador que se usará para agregar varios cursores con el mouse. \"ctrlCmd\" se asigna a \"Control\" en Windows y Linux y a \"Comando\" en OSX. Los gestos del mouse Ir a la definición y Abrir vínculo se adaptarán de modo que no entren en conflicto con el modificador multicursor.", + "quickSuggestions.strings": "Habilita sugerencias rápidas en las cadenas.", + "quickSuggestions.comments": "Habilita sugerencias rápidas en los comentarios.", + "quickSuggestions.other": "Habilita sugerencias rápidas fuera de las cadenas y los comentarios.", + "quickSuggestions": "Controla si las sugerencias deben mostrarse automáticamente mientras se escribe", + "quickSuggestionsDelay": "Controla el retardo en ms tras el cual aparecerán sugerencias rápidas", + "parameterHints": "Habilita el desplegable que muestra documentación de los parámetros e información de los tipos mientras escribe", + "autoClosingBrackets": "Controla si el editor debe cerrar automáticamente los corchetes después de abrirlos", + "formatOnType": "Controla si el editor debe dar formato automáticamente a la línea después de escribirla", + "formatOnPaste": "Controla si el editor debe formatear automáticamente el contenido pegado. Debe haber disponible un formateador capaz de aplicar formato a un intervalo dentro de un documento.", + "autoIndent": "Controla si el editor debería ajustar automáticamente la sangría cuando los usuarios escriben, pegan o mueven líneas. Las reglas de sangría del idioma deben estar disponibles.", + "suggestOnTriggerCharacters": "Controla si las sugerencias deben aparecer de forma automática al escribir caracteres desencadenadores", + "acceptSuggestionOnEnter": "Controla si las sugerencias deben aceptarse en \"Entrar\" (además de \"TAB\"). Ayuda a evitar la ambigüedad entre insertar nuevas líneas o aceptar sugerencias. El valor \"smart\" significa que solo se acepta una sugerencia con Entrar cuando se realiza un cambio textual.", + "acceptSuggestionOnCommitCharacter": "Controla si se deben aceptar sugerencias en los caracteres de confirmación. Por ejemplo, en Javascript, el punto y coma (\";\") puede ser un carácter de confirmación que acepta una sugerencia y escribe ese carácter.", + "snippetSuggestions.top": "Mostrar sugerencias de fragmentos de código por encima de otras sugerencias.", + "snippetSuggestions.bottom": "Mostrar sugerencias de fragmentos de código por debajo de otras sugerencias.", + "snippetSuggestions.inline": "Mostrar sugerencias de fragmentos de código con otras sugerencias.", + "snippetSuggestions.none": "No mostrar sugerencias de fragmentos de código.", + "snippetSuggestions": "Controla si se muestran los fragmentos de código con otras sugerencias y cómo se ordenan.", + "emptySelectionClipboard": "Controla si al copiar sin selección se copia la línea actual.", + "wordBasedSuggestions": "Habilita sugerencias basadas en palabras.", + "suggestFontSize": "Tamaño de fuente para el widget de sugerencias", + "suggestLineHeight": "Alto de línea para el widget de sugerencias", + "selectionHighlight": "Controla si el editor debería destacar coincidencias similares a la selección", + "occurrencesHighlight": "Controla si el editor debe resaltar los símbolos semánticos.", + "overviewRulerLanes": "Controla el número de decoraciones que pueden aparecer en la misma posición en la regla de visión general", + "overviewRulerBorder": "Controla si debe dibujarse un borde alrededor de la regla de información general.", + "cursorBlinking": "Controlar el estilo de animación del cursor. Los valores posibles son \"blink\", \"smooth\", \"phase\", \"expand\" y \"solid\".", + "mouseWheelZoom": "Ampliar la fuente del editor cuando se use la rueda del mouse mientras se presiona Ctrl", + "cursorStyle": "Controla el estilo del cursor. Los valores aceptados son \"block\", \"block-outline\", \"line\", \"line-thin\", \"underline\" y \"underline-thin\"", + "fontLigatures": "Habilita las ligaduras tipográficas.", + "hideCursorInOverviewRuler": "Controla si el cursor debe ocultarse en la regla de visión general.", + "renderWhitespace": "Controla cómo debe representar el editor los espacios en blanco. Las posibilidades son \"none\", \"boundary\" y \"all\". La opción \"boundary\" no representa los espacios individuales entre palabras.", + "renderControlCharacters": "Controla si el editor debe representar caracteres de control", + "renderIndentGuides": "Controla si el editor debe representar guías de sangría.", + "renderLineHighlight": "Controla cómo el editor debe presentar el resaltado de línea. Las posibilidades son \"ninguno\", \"margen\", \"línea\" y \"todo\".", + "codeLens": "Controla si el editor muestra lentes de código", + "folding": "Controla si el editor tiene habilitado el plegado de código.", + "showFoldingControls": "Controla cuándo los controles de plegado del margen son ocultados automáticamente.", + "matchBrackets": "Resaltar corchetes coincidentes cuando se seleccione uno de ellos.", + "glyphMargin": "Controla si el editor debe representar el margen de glifo vertical. El margen de glifo se usa, principalmente, para depuración.", + "useTabStops": "La inserción y eliminación del espacio en blanco sigue a las tabulaciones.", + "trimAutoWhitespace": "Quitar espacio en blanco final autoinsertado", + "stablePeek": "Mantiene abierto el editor interactivo incluso al hacer doble clic en su contenido o presionar Escape.", + "dragAndDrop": "Controla si el editor debe permitir mover selecciones mediante arrastrar y colocar.", + "accessibilitySupport.auto": "El editor usará API de plataforma para detectar cuándo está conectado un lector de pantalla.", + "accessibilitySupport.on": "El editor se optimizará de forma permanente para su uso con un editor de pantalla.", + "accessibilitySupport.off": "El editor nunca se optimizará para su uso con un lector de pantalla.", + "accessibilitySupport": "Controla si el editor se debe ejecutar en un modo optimizado para lectores de pantalla.", + "links": "Controla si el editor debe detectar enlaces y hacerlos cliqueables", + "colorDecorators": "Controla si el editor debe representar el Selector de colores y los elementos Decorator de color en línea.", + "sideBySide": "Controla si el editor de diferencias muestra las diferencias en paralelo o alineadas.", + "ignoreTrimWhitespace": "Controla si el editor de diferencias muestra los cambios de espacio inicial o espacio final como diferencias.", + "renderIndicators": "Controla si el editor de diff muestra indicadores +/- para cambios agregados/quitados", + "selectionClipboard": "Controla si el portapapeles principal de Linux debe admitirse." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/common/config/defaultConfig.i18n.json b/i18n/esn/src/vs/editor/common/config/defaultConfig.i18n.json new file mode 100644 index 0000000000..17250e0350 --- /dev/null +++ b/i18n/esn/src/vs/editor/common/config/defaultConfig.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorViewAccessibleLabel": "Contenido del editor" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/esn/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..36a73ce594 --- /dev/null +++ b/i18n/esn/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "No se puede acceder al editor en este momento. Presione Alt+F1 para ver opciones.", + "editorViewAccessibleLabel": "Contenido del editor" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/common/controller/cursor.i18n.json b/i18n/esn/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..b9a8c557b0 --- /dev/null +++ b/i18n/esn/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "Excepción inesperada al ejecutar el comando." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/esn/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..27c96ae589 --- /dev/null +++ b/i18n/esn/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "Error en el modo al convertir la entrada en tokens." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/esn/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..f0b8c6368a --- /dev/null +++ b/i18n/esn/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "Texto sin formato" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/esn/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..8d005dbee8 --- /dev/null +++ b/i18n/esn/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "Estos archivos han cambiado durante el proceso: {0}", + "summary.0": "No se realizaron ediciones", + "summary.nm": "{0} ediciones de texto en {1} archivos", + "summary.n0": "{0} ediciones de texto en un archivo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/esn/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..aab37e2e7c --- /dev/null +++ b/i18n/esn/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Aporta declaraciones de lenguaje.", + "vscode.extension.contributes.languages.id": "Identificador del lenguaje.", + "vscode.extension.contributes.languages.aliases": "Alias de nombre para el lenguaje.", + "vscode.extension.contributes.languages.extensions": "Extensiones de archivo asociadas al lenguaje.", + "vscode.extension.contributes.languages.filenames": "Nombres de archivo asociados al lenguaje.", + "vscode.extension.contributes.languages.filenamePatterns": "Patrones globales de nombre de archivo asociados al lenguaje.", + "vscode.extension.contributes.languages.mimetypes": "Tipos MIME asociados al lenguaje.", + "vscode.extension.contributes.languages.firstLine": "Expresión regular que coincide con la primera línea de un archivo del lenguaje.", + "vscode.extension.contributes.languages.configuration": "Ruta de acceso relativa a un archivo que contiene opciones de configuración para el lenguaje." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/esn/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/esn/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/esn/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..94311157bc --- /dev/null +++ b/i18n/esn/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "Color de fondo del resaltado de línea en la posición del cursor.", + "lineHighlightBorderBox": "Color de fondo del borde alrededor de la línea en la posición del cursor.", + "rangeHighlight": "Color de fondo de los intervalos resaltados; por ejemplo, para Apertura Rápida y Buscar.", + "caret": "Color del cursor del editor.", + "editorCursorBackground": "Color de fondo del cursor de edición. Permite personalizar el color del carácter solapado por el bloque del cursor.", + "editorWhitespaces": "Color de los caracteres de espacio en blanco del editor.", + "editorIndentGuides": "Color de las guías de sangría del editor.", + "editorLineNumbers": "Color de números de línea del editor.", + "editorRuler": "Color de las reglas del editor", + "editorCodeLensForeground": "Color principal de lentes de código en el editor", + "editorBracketMatchBackground": "Color de fondo tras corchetes coincidentes", + "editorBracketMatchBorder": "Color de bloques con corchetes coincidentes", + "editorOverviewRulerBorder": "Color del borde de la regla de visión general.", + "editorGutter": "Color de fondo del margen del editor. Este espacio contiene los márgenes de glifos y los números de línea.", + "errorForeground": "Color de primer plano de squigglies de error en el editor.", + "errorBorder": "Color de borde de squigglies de error en el editor.", + "warningForeground": "Color de primer plano de squigglies de advertencia en el editor.", + "warningBorder": "Color de borde de squigglies de advertencia en el editor.", + "overviewRulerRangeHighlight": "Color de marcador de regla de información general para intervalos resaltados.", + "overviewRuleError": "Color de marcador de regla de información general para errores. ", + "overviewRuleWarning": "Color de marcador de regla de información general para advertencias.", + "overviewRuleInfo": "Color de marcador de regla de información general para mensajes informativos. " +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json b/i18n/esn/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json new file mode 100644 index 0000000000..42f17a2235 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "introMsg": "Gracias por probar las opciones de accesibilidad de VS Code.", + "status": "Estado:", + "tabFocusModeOnMsg": "Al presionar TAB en el editor actual, el foco se mueve al siguiente elemento activable. Presione {0} para activar o desactivar este comportamiento.", + "tabFocusModeOnMsgNoKb": "Al presionar TAB en el editor actual, el foco se mueve al siguiente elemento activable. El comando {0} no se puede desencadenar actualmente mediante un enlace de teclado.", + "tabFocusModeOffMsg": "Al presionar TAB en el editor actual, se insertará el carácter de tabulación. Presione {0} para activar o desactivar este comportamiento.", + "tabFocusModeOffMsgNoKb": "Al presionar TAB en el editor actual, se insertará el carácter de tabulación. El comando {0} no se puede desencadenar actualmente mediante un enlace de teclado.", + "outroMsg": "Para descartar esta información sobre herramientas y volver al editor, presione Esc.", + "ShowAccessibilityHelpAction": "Mostrar ayuda de accesibilidad" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/esn/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..a2546f9935 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "Ir al corchete" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/esn/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..069524e084 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "Mover símbolo de inserción a la izquierda", + "caret.moveRight": "Mover símbolo de inserción a la derecha" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/esn/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..deaac22acd --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "Transponer letras" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/esn/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..7f9c55068f --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "Cortar", + "actions.clipboard.copyLabel": "Copiar", + "actions.clipboard.pasteLabel": "Pegar", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "Copiar con resaltado de sintaxis" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/esn/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..0269eab27d --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "Alternar comentario de línea", + "comment.line.add": "Agregar comentario de línea", + "comment.line.remove": "Quitar comentario de línea", + "comment.block": "Alternar comentario de bloque" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/esn/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..62563ce263 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "Mostrar menú contextual del editor" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/esn/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..2668c98925 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Buscar", + "placeholder.find": "Buscar", + "label.previousMatchButton": "Coincidencia anterior", + "label.nextMatchButton": "Coincidencia siguiente", + "label.toggleSelectionFind": "Buscar en selección", + "label.closeButton": "Cerrar", + "label.replace": "Reemplazar", + "placeholder.replace": "Reemplazar", + "label.replaceButton": "Reemplazar", + "label.replaceAllButton": "Reemplazar todo", + "label.toggleReplaceButton": "Alternar modo de reemplazar", + "title.matchesCountLimit": "Solo se resaltan los primeros 999 resultados, pero todas las operaciones de búsqueda trabajan en todo el texto.", + "label.matchesLocation": "{0} de {1}", + "label.noResults": "Sin resultados" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/esn/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..4534d09463 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Buscar", + "placeholder.find": "Buscar", + "label.previousMatchButton": "Coincidencia anterior", + "label.nextMatchButton": "Coincidencia siguiente", + "label.closeButton": "Cerrar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/esn/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..e84a7aa804 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "Buscar", + "findNextMatchAction": "Buscar siguiente", + "findPreviousMatchAction": "Buscar anterior", + "nextSelectionMatchFindAction": "Buscar selección siguiente", + "previousSelectionMatchFindAction": "Buscar selección anterior", + "startReplace": "Reemplazar", + "addSelectionToNextFindMatch": "Agregar selección hasta la siguiente coincidencia de búsqueda", + "addSelectionToPreviousFindMatch": "Agregar selección hasta la anterior coincidencia de búsqueda", + "moveSelectionToNextFindMatch": "Mover última selección hasta la siguiente coincidencia de búsqueda", + "moveSelectionToPreviousFindMatch": "Mover última selección hasta la anterior coincidencia de búsqueda", + "selectAllOccurrencesOfFindMatch": "Seleccionar todas las repeticiones de coincidencia de búsqueda", + "changeAll.label": "Cambiar todas las ocurrencias", + "showNextFindTermAction": "Mostrar el siguiente término de búsqueda", + "showPreviousFindTermAction": "Mostrar término de búsqueda anterior" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/esn/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..3d58fa5762 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "Desplegar", + "unFoldRecursivelyAction.label": "Desplegar de forma recursiva", + "foldAction.label": "Plegar", + "foldRecursivelyAction.label": "Plegar de forma recursiva", + "foldAllAction.label": "Plegar todo", + "unfoldAllAction.label": "Desplegar todo", + "foldLevelAction.label": "Nivel de plegamiento {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/esn/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..4060cf2aa6 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "1 edición de formato en la línea {0}", + "hintn1": "{0} ediciones de formato en la línea {1}", + "hint1n": "1 edición de formato entre las líneas {0} y {1}", + "hintnn": "{0} ediciones de formato entre las líneas {1} y {2}", + "formatDocument.label": "Dar formato al documento", + "formatSelection.label": "Dar formato a la selección" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json b/i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json new file mode 100644 index 0000000000..92c069a331 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "No se encontró ninguna definición para \"{0}\"", + "generic.noResults": "No se encontró ninguna definición", + "meta.title": " – {0} definiciones", + "actions.goToDecl.label": "Ir a definición", + "actions.goToDeclToSide.label": "Abrir definición en el lateral", + "actions.previewDecl.label": "Ver la definición", + "goToImplementation.noResultWord": "No se encontró ninguna implementación para \"{0}\"", + "goToImplementation.generic.noResults": "No se encontró ninguna implementación", + "meta.implementations.title": "{0} implementaciones", + "actions.goToImplementation.label": "Ir a implementación", + "actions.peekImplementation.label": "Inspeccionar implementación", + "goToTypeDefinition.noResultWord": "No se encontró ninguna definición de tipo para \"{0}\"", + "goToTypeDefinition.generic.noResults": "No se encontró ninguna definición de tipo", + "meta.typeDefinitions.title": " – {0} definiciones de tipo", + "actions.goToTypeDefinition.label": "Ir a la definición de tipo", + "actions.peekTypeDefinition.label": "Inspeccionar definición de tipo", + "multipleResults": "Haga clic para mostrar {0} definiciones." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..23cc71807a --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "No se encontró ninguna definición para \"{0}\"", + "generic.noResults": "No se encontró ninguna definición", + "meta.title": " – {0} definiciones", + "actions.goToDecl.label": "Ir a definición", + "actions.goToDeclToSide.label": "Abrir definición en el lateral", + "actions.previewDecl.label": "Ver la definición", + "goToImplementation.noResultWord": "No se encontró ninguna implementación para \"{0}\"", + "goToImplementation.generic.noResults": "No se encontró ninguna implementación", + "meta.implementations.title": "{0} implementaciones", + "actions.goToImplementation.label": "Ir a implementación", + "actions.peekImplementation.label": "Inspeccionar implementación", + "goToTypeDefinition.noResultWord": "No se encontró ninguna definición de tipo para \"{0}\"", + "goToTypeDefinition.generic.noResults": "No se encontró ninguna definición de tipo", + "meta.typeDefinitions.title": " – {0} definiciones de tipo", + "actions.goToTypeDefinition.label": "Ir a la definición de tipo", + "actions.peekTypeDefinition.label": "Inspeccionar definición de tipo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..f91e1059fb --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "Haga clic para mostrar {0} definiciones." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/esn/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..f621b38c7c --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "Ir al error o la advertencia siguiente", + "markerAction.previous.label": "Ir al error o la advertencia anterior", + "editorMarkerNavigationError": "Color de los errores del widget de navegación de marcadores del editor.", + "editorMarkerNavigationWarning": "Color de las advertencias del widget de navegación de marcadores del editor.", + "editorMarkerNavigationBackground": "Fondo del widget de navegación de marcadores del editor." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/esn/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..11169c2dbf --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "Mostrar al mantener el puntero" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/esn/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..7bf23ffc7e --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "Cargando..." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/esn/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..a88c8d0720 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "Reemplazar con el valor anterior", + "InPlaceReplaceAction.next.label": "Reemplazar con el valor siguiente" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/esn/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..49e0d3eae0 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "Convertir sangría en espacios", + "indentationToTabs": "Convertir sangría en tabulaciones", + "configuredTabSize": "Tamaño de tabulación configurado", + "selectTabWidth": "Seleccionar tamaño de tabulación para el archivo actual", + "indentUsingTabs": "Aplicar sangría con tabulaciones", + "indentUsingSpaces": "Aplicar sangría con espacios", + "detectIndentation": "Detectar sangría del contenido", + "editor.reindentlines": "Volver a aplicar sangría a líneas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json b/i18n/esn/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..a32df84414 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Desarrollador: Inspeccionar ámbitos de TM", + "inspectTMScopesWidget.loading": "Cargando..." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/esn/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..175af0b5a2 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "Copiar línea arriba", + "lines.copyDown": "Copiar línea abajo", + "lines.moveUp": "Mover línea hacia arriba", + "lines.moveDown": "Mover línea hacia abajo", + "lines.sortAscending": "Ordenar líneas en orden ascendente", + "lines.sortDescending": "Ordenar líneas en orden descendente", + "lines.trimTrailingWhitespace": "Recortar espacio final", + "lines.delete": "Eliminar línea", + "lines.indent": "Sangría de línea", + "lines.outdent": "Anular sangría de línea", + "lines.insertBefore": "Insertar línea arriba", + "lines.insertAfter": "Insertar línea debajo", + "lines.deleteAllLeft": "Eliminar todo a la izquierda", + "lines.deleteAllRight": "Eliminar todo lo que está a la derecha", + "lines.joinLines": "Unir líneas", + "editor.transpose": "Transponer caracteres alrededor del cursor", + "editor.transformToUppercase": "Transformar a mayúsculas", + "editor.transformToLowercase": "Transformar a minúsculas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/esn/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..8cda6b5c1b --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "Cmd + clic para abrir el vínculo", + "links.navigate": "Ctrl + clic para abrir el vínculo", + "links.command.mac": "CMD + click para ejecutar el comando", + "links.command": "Ctrl + click para ejecutar el comando", + "links.navigate.al": "Alt + clic para seguir el vínculo", + "links.command.al": "Alt + clic para ejecutar el comando", + "invalid.url": "No se pudo abrir este vínculo porque no tiene un formato correcto: {0}", + "missing.url": "No se pudo abrir este vínculo porque falta el destino.", + "label": "Abrir vínculo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/esn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..3458efc37b --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "Agregar cursor arriba", + "mutlicursor.insertBelow": "Agregar cursor debajo", + "mutlicursor.insertAtEndOfEachLineSelected": "Añadir cursores a finales de línea" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/esn/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..58ffad842d --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "Sugerencias para parámetros Trigger" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/esn/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..0d303e5052 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0}, sugerencia" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/esn/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..e88ab88626 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "Mostrar correcciones ({0})", + "quickFix": "Mostrar correcciones", + "quickfix.trigger.label": "Corrección rápida" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..af1832d2f2 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": " – {0} referencias", + "references.action.label": "Buscar todas las referencias" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..a3f057d8ff --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "Cargando..." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..7f5d908759 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "símbolo en {0} linea {1} en la columna {2}", + "aria.fileReferences.1": "1 símbolo en {0}, ruta de acceso completa {1}", + "aria.fileReferences.N": "{0} símbolos en {1}, ruta de acceso completa {2}", + "aria.result.0": "No se encontraron resultados", + "aria.result.1": "Encontró 1 símbolo en {0}", + "aria.result.n1": "Encontró {0} símbolos en {1}", + "aria.result.nm": "Encontró {0} símbolos en {1} archivos" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..e4e1d313af --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "Error al resolver el archivo.", + "referencesCount": "{0} referencias", + "referenceCount": "{0} referencia", + "missingPreviewMessage": "vista previa no disponible", + "treeAriaLabel": "Referencias", + "noResults": "No hay resultados.", + "peekView.alternateTitle": "Referencias", + "peekViewTitleBackground": "Color de fondo del área de título de la vista de inspección.", + "peekViewTitleForeground": "Color del título de la vista de inpección.", + "peekViewTitleInfoForeground": "Color de la información del título de la vista de inspección.", + "peekViewBorder": "Color de los bordes y la flecha de la vista de inspección.", + "peekViewResultsBackground": "Color de fondo de la lista de resultados de vista de inspección.", + "peekViewResultsMatchForeground": "Color de primer plano de los nodos de inspección en la lista de resultados.", + "peekViewResultsFileForeground": "Color de primer plano de los archivos de inspección en la lista de resultados.", + "peekViewResultsSelectionBackground": "Color de fondo de la entrada seleccionada en la lista de resultados de vista de inspección.", + "peekViewResultsSelectionForeground": "Color de primer plano de la entrada seleccionada en la lista de resultados de vista de inspección.", + "peekViewEditorBackground": "Color de fondo del editor de vista de inspección.", + "peekViewEditorGutterBackground": "Color de fondo del margen en el editor de vista de inspección.", + "peekViewResultsMatchHighlight": "Buscar coincidencia con el color de resaltado de la lista de resultados de vista de inspección.", + "peekViewEditorMatchHighlight": "Buscar coincidencia del color de resultado del editor de vista de inspección." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/esn/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..33ab41d264 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "No hay ningún resultado.", + "aria": "Nombre cambiado correctamente de '{0}' a '{1}'. Resumen: {2}", + "rename.failed": "No se pudo cambiar el nombre.", + "rename.label": "Cambiar el nombre del símbolo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/esn/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..7aa88a6da9 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "Cambie el nombre de la entrada. Escriba el nuevo nombre y presione Entrar para confirmar." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/esn/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..462962b7b7 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "Expandir selección", + "smartSelect.shrink": "Reducir selección" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/esn/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..37aec68c7d --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "Aceptando '{0}' Insertó el siguente texto : {1}", + "suggest.trigger.label": "Sugerencias para Trigger" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/esn/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..a32ffacbef --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "Color de fondo del widget sugerido.", + "editorSuggestWidgetBorder": "Color de borde del widget sugerido.", + "editorSuggestWidgetForeground": "Color de primer plano del widget sugerido.", + "editorSuggestWidgetSelectedBackground": "Color de fondo de la entrada seleccionada del widget sugerido.", + "editorSuggestWidgetHighlightForeground": "Color del resaltado coincidido en el widget sugerido.", + "readMore": "Leer más...{0}", + "suggestionWithDetailsAriaLabel": "{0}, sugerencia, con detalles", + "suggestionAriaLabel": "{0}, sugerencia", + "readLess": "Leer menos...{0}", + "suggestWidget.loading": "Cargando...", + "suggestWidget.noSuggestions": "No hay sugerencias.", + "suggestionAriaAccepted": "{0}, aceptada", + "ariaCurrentSuggestionWithDetails": "{0}, sugerencia, con detalles", + "ariaCurrentSuggestion": "{0}, sugerencia" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/esn/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..a7c4cc38e1 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "Alternar tecla de tabulación para mover el punto de atención" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/esn/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..5c973ba381 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "Color de fondo de un símbolo durante el acceso de lectura; por ejemplo, cuando se lee una variable.", + "wordHighlightStrong": "Color de fondo de un símbolo durante el acceso de escritura; por ejemplo, cuando se escribe una variable.", + "overviewRulerWordHighlightForeground": "Color de marcador de regla de información general para símbolos resaltados.", + "overviewRulerWordHighlightStrongForeground": "Color de marcador de regla de información general para símbolos de acceso de escritura resaltados. " +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/esn/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..937f220824 --- /dev/null +++ b/i18n/esn/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Cerrar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/esn/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..e04f613e95 --- /dev/null +++ b/i18n/esn/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Lenguaje desconocido en \"contributes.{0}.language\". Valor proporcionado: {1}", + "invalid.scopeName": "Se esperaba una cadena en \"contributes.{0}.scopeName\". Valor proporcionado: {1}", + "invalid.path.0": "Se esperaba una cadena en \"contributes.{0}.path\". Valor proporcionado: {1}", + "invalid.injectTo": "Valor no válido en `contributes.{0}.injectTo`. Debe ser una matriz de nombres de ámbito de lenguaje. Valor proporcionado: {1}", + "invalid.embeddedLanguages": "Valor no válido en \"contributes.{0}.embeddedLanguages\". Debe ser una asignación de objeto del nombre del ámbito al lenguaje. Valor proporcionado: {1}", + "invalid.path.1": "Se esperaba que \"contributes.{0}.path\" ({1}) se incluyera en la carpeta de la extensión ({2}). Esto puede hacer que la extensión no sea portátil.", + "no-tm-grammar": "No hay ninguna gramática de TM registrada para este lenguaje." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/esn/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..76656d3c1c --- /dev/null +++ b/i18n/esn/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Errores al analizar {0}: {1}", + "schema.openBracket": "Secuencia de cadena o corchete de apertura.", + "schema.closeBracket": "Secuencia de cadena o corchete de cierre.", + "schema.comments": "Define los símbolos de comentario", + "schema.blockComments": "Define cómo se marcan los comentarios de bloque.", + "schema.blockComment.begin": "Secuencia de caracteres que inicia un comentario de bloque.", + "schema.blockComment.end": "Secuencia de caracteres que finaliza un comentario de bloque.", + "schema.lineComment": "Secuencia de caracteres que inicia un comentario de línea.", + "schema.brackets": "Define los corchetes que aumentan o reducen la sangría.", + "schema.autoClosingPairs": "Define el par de corchetes. Cuando se escribe un corchete de apertura, se inserta automáticamente el corchete de cierre.", + "schema.autoClosingPairs.notIn": "Define una lista de ámbitos donde los pares automáticos están deshabilitados.", + "schema.surroundingPairs": "Define los pares de corchetes que se pueden usar para encerrar una cadena seleccionada.", + "schema.wordPattern": " La definición de la palabra en el idioma.", + "schema.wordPattern.pattern": "El patrón de expresión regular utilizado para localizar palabras.", + "schema.wordPattern.flags": "Los flags de expresión regular utilizados para localizar palabras.", + "schema.wordPattern.flags.errorMessage": "Debe coincidir con el patrón `/^([gimuy]+)$/`." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/esn/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..a1f1307b47 --- /dev/null +++ b/i18n/esn/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Aporta tokenizadores de TextMate.", + "vscode.extension.contributes.grammars.language": "Identificador del lenguaje para el que se aporta esta sintaxis.", + "vscode.extension.contributes.grammars.scopeName": "Nombre del ámbito de TextMate que usa el archivo tmLanguage.", + "vscode.extension.contributes.grammars.path": "Ruta de acceso del archivo tmLanguage. La ruta es relativa a la carpeta de extensión y normalmente empieza por \"./syntaxes/\".", + "vscode.extension.contributes.grammars.embeddedLanguages": "Asignación de un nombre de ámbito al identificador de lenguaje si esta gramática contiene lenguajes incrustados.", + "vscode.extension.contributes.grammars.injectTo": "Lista de nombres de ámbito de lenguaje al que se inyecta esta gramática." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/esn/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/esn/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/esn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..ecdf3bd8d1 --- /dev/null +++ b/i18n/esn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "los elementos de menú deben ser una colección", + "requirestring": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "optstring": "la propiedad `{0}` se puede omitir o debe ser de tipo \"string\"", + "vscode.extension.contributes.menuItem.command": "El identificador del comando que se ejecutará. El comando se debe declarar en la sección 'commands'", + "vscode.extension.contributes.menuItem.alt": "El identificador de un comando alternativo que se usará. El comando se debe declarar en la sección 'commands'", + "vscode.extension.contributes.menuItem.when": "Condición que se debe cumplir para mostrar este elemento", + "vscode.extension.contributes.menuItem.group": "Grupo al que pertenece este comando", + "vscode.extension.contributes.menus": "Contribuye con elementos de menú al editor", + "menus.commandPalette": "La paleta de comandos", + "menus.editorTitle": "El menú de título del editor", + "menus.editorContext": "El menú contextual del editor", + "menus.explorerContext": "El menú contextual del explorador de archivos", + "menus.editorTabContext": "El menú contextual de pestañas del editor", + "menus.debugCallstackContext": "El menú contextual de la pila de llamadas de depuración", + "menus.scmTitle": "El menú del título Control de código fuente", + "menus.resourceGroupContext": "El menú contextual del grupo de recursos de Control de código fuente", + "menus.resourceStateContext": "El menú contextual de estado de recursos de Control de código fuente", + "view.viewTitle": "El menú de título de vista contribuida", + "view.itemContext": "El menú contextual del elemento de vista contribuida", + "nonempty": "se esperaba un valor no vacío.", + "opticon": "la propiedad `icon` se puede omitir o debe ser una cadena o un literal como `{dark, light}`", + "requireStringOrObject": "La propiedad \"{0}\" es obligatoria y debe ser de tipo \"string\" u \"object\"", + "requirestrings": "Las propiedades \"{0}\" y \"{1}\" son obligatorias y deben ser de tipo \"string\"", + "vscode.extension.contributes.commandType.command": "Identificador del comando que se va a ejecutar", + "vscode.extension.contributes.commandType.title": "Título con el que se representa el comando en la interfaz de usuario", + "vscode.extension.contributes.commandType.category": "(Opcional) la cadena de categoría se agrupa por el comando en la interfaz de usuario", + "vscode.extension.contributes.commandType.icon": "(Opcional) El icono que se usa para representar el comando en la UI. Ya sea una ruta de acceso al archivo o una configuración con temas", + "vscode.extension.contributes.commandType.icon.light": "Ruta del icono cuando se usa un tema claro", + "vscode.extension.contributes.commandType.icon.dark": "Ruta del icono cuando se usa un tema oscuro", + "vscode.extension.contributes.commands": "Aporta comandos a la paleta de comandos.", + "dup": "El comando `{0}` aparece varias veces en la sección 'commands'.", + "menuId.invalid": "`{0}` no es un identificador de menú válido", + "missing.command": "El elemento de menú hace referencia a un comando `{0}` que no está definido en la sección 'commands'.", + "missing.altCommand": "El elemento de menú hace referencia a un comando alternativo `{0}` que no está definido en la sección 'commands'.", + "dupe.command": "El elemento de menú hace referencia al mismo comando que el comando predeterminado y el comando alternativo", + "nosupport.altCommand": "Actualmente, solo el grupo 'navigation' del menú 'editor/title' es compatible con los comandos alternativos" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/esn/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..fa2ac72a05 --- /dev/null +++ b/i18n/esn/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "La configuración predeterminada se reemplaza", + "overrideSettings.description": "Establecer los valores de configuración que se reemplazarán para el lenguaje {0}.", + "overrideSettings.defaultDescription": "Establecer los valores de configuración que se reemplazarán para un lenguaje.", + "config.property.languageDefault": "No se puede registrar \"{0}\". Coincide con el patrón de propiedad '\\\\[.*\\\\]$' para describir la configuración del editor específica del lenguaje. Utilice la contribución \"configurationDefaults\".", + "config.property.duplicate": "No se puede registrar \"{0}\". Esta propiedad ya está registrada." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/environment/node/argv.i18n.json b/i18n/esn/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..041051ccb2 --- /dev/null +++ b/i18n/esn/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "Los argumentos del modo \"--goto\" deben tener el formato \"ARCHIVO(:LÍNEA(:CARÁCTER))\".", + "diff": "Comparar dos archivos entre sí.", + "add": "Agregar carpetas a la última ventana activa.", + "goto": "Abrir un archivo en la ruta de acceso de la línea y posición de carácter especificadas.", + "locale": "La configuración regional que se usará (por ejemplo, en-US o zh-TW).", + "newWindow": "Fuerce una nueva instancia de Code.", + "performance": "Comience con el comando 'Developer: Startup Performance' habilitado.", + "prof-startup": "Ejecutar generador de perfiles de CPU durante el inicio", + "reuseWindow": "Fuerce la apertura de un archivo o carpeta en la última ventana activa.", + "userDataDir": "Especifica el directorio en que se conservan los datos de usuario; es útil cuando se ejecuta como raíz.", + "verbose": "Imprima salidas detalladas (implica --wait).", + "wait": "Espere que se cierre la ventana antes de volver.", + "extensionHomePath": "Establezca la ruta de acceso raíz para las extensiones.", + "listExtensions": "Enumere las extensiones instaladas.", + "showVersions": "Muestra las versiones de las extensiones instaladas cuando se usa --list-extension.", + "installExtension": "Instala una extensión.", + "uninstallExtension": "Desinstala una extensión.", + "experimentalApis": "Habilita características de API propuestas para una extensión.", + "disableExtensions": "Deshabilite todas las extensiones instaladas.", + "disableGPU": "Deshabilita la aceleración de hardware de GPU.", + "version": "Versión de impresión.", + "help": "Imprima el uso.", + "usage": "Uso", + "options": "opciones", + "paths": "rutas de acceso", + "optionsUpperCase": "Opciones" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/esn/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..318a732ada --- /dev/null +++ b/i18n/esn/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "No hay ningún área de trabajo." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/esn/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..c4b63f56cc --- /dev/null +++ b/i18n/esn/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Extensiones", + "preferences": "Preferencias" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/esn/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..504e2702eb --- /dev/null +++ b/i18n/esn/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Extensión no encontrada", + "noCompatible": "No se encontró una versión de {0} compatible con esta versión de Code." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/esn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..9585353636 --- /dev/null +++ b/i18n/esn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "Extensión no válida: package.json no es un archivo JSON.", + "restartCode": "Reinicie Code antes de volver a instalar {0}.", + "installDependeciesConfirmation": "Al instalar '{0}', se instalan también sus dependencias. ¿Quiere continuar?", + "install": "Sí", + "doNotInstall": "No", + "uninstallDependeciesConfirmation": "¿Quiere desinstalar solo '{0}' o también sus dependencias?", + "uninstallOnly": "Solo", + "uninstallAll": "Todo", + "cancel": "Cancelar", + "uninstallConfirmation": "¿Seguro que quiere desinstalar '{0}'?", + "ok": "Aceptar", + "singleDependentError": "No se puede desinstalar la extensión '{0}'. La extensión '{1}' depende de esta.", + "twoDependentsError": "No se puede desinstalar la extensión '{0}'. Las extensiones '{1}' y '{2}' dependen de esta.", + "multipleDependentsError": "No se puede desinstalar la extensión '{0}'. Las extensiones '{1}' y '{2}', entre otras, dependen de esta.", + "notExists": "No se encontró la extensión." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/esn/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..cda2721ec2 --- /dev/null +++ b/i18n/esn/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "La extensión `{1}` no se pudo activar. Motivo: dependencia `{0}` desconocida.", + "failedDep1": "La extensión `{1}` no se pudo activar. Motivo: La dependencia `{0}` no se pudo activar.", + "failedDep2": "La extensión `{0}` no se pudo activar. Motivo: más de 10 niveles de dependencias (probablemente sea un bucle de dependencias).", + "activationError": "Error al activar la extensión `{0}`: {1}." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/esn/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..2d311813d2 --- /dev/null +++ b/i18n/esn/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "Para las extensiones de VS Code, especifica la versión de VS Code con la que la extensión es compatible. No puede ser *. Por ejemplo: ^0.10.5 indica compatibilidad con una versión de VS Code mínima de 0.10.5.", + "vscode.extension.publisher": "El publicador de la extensión VS Code.", + "vscode.extension.displayName": "Nombre para mostrar de la extensión que se usa en la galería de VS Code.", + "vscode.extension.categories": "Categorías que usa la galería de VS Code para clasificar la extensión.", + "vscode.extension.galleryBanner": "Banner usado en VS Code Marketplace.", + "vscode.extension.galleryBanner.color": "Color del banner en el encabezado de página de VS Code Marketplace.", + "vscode.extension.galleryBanner.theme": "Tema de color de la fuente que se usa en el banner.", + "vscode.extension.contributes": "Todas las contribuciones de la extensión VS Code representadas por este paquete.", + "vscode.extension.preview": "Establece la extensión que debe marcarse como versión preliminar en Marketplace.", + "vscode.extension.activationEvents": "Eventos de activación de la extensión VS Code.", + "vscode.extension.activationEvents.onLanguage": "Un evento de activación emitido cada vez que se abre un archivo que se resuelve en el idioma especificado.", + "vscode.extension.activationEvents.onCommand": "Un evento de activación emitido cada vez que se invoca el comando especificado.", + "vscode.extension.activationEvents.onDebug": "Un evento de activación emitido cada vez que se inicia una sesión de depuración del tipo especificado.", + "vscode.extension.activationEvents.workspaceContains": "Un evento de activación emitido cada vez que se abre una carpeta que contiene al menos un archivo que coincide con el patrón global especificado.", + "vscode.extension.activationEvents.onView": "Un evento de activación emitido cada vez que se expande la vista especificada.", + "vscode.extension.activationEvents.star": "Un evento de activación emitido al inicio de VS Code. Para garantizar una buena experiencia para el usuario final, use este evento de activación en su extensión solo cuando no le sirva ninguna otra combinación de eventos de activación en su caso.", + "vscode.extension.badges": "Matriz de distintivos que se muestran en la barra lateral de la página de extensiones de Marketplace.", + "vscode.extension.badges.url": "URL de la imagen del distintivo.", + "vscode.extension.badges.href": "Vínculo del distintivo.", + "vscode.extension.badges.description": "Descripción del distintivo.", + "vscode.extension.extensionDependencies": "Dependencias a otras extensiones. El identificador de una extensión siempre es ${publisher}.${name}. Por ejemplo: vscode.csharp.", + "vscode.extension.scripts.prepublish": "Script que se ejecuta antes de publicar el paquete como extensión VS Code.", + "vscode.extension.icon": "Ruta de acceso a un icono de 128 x 128 píxeles." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/esn/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..17eeff350a --- /dev/null +++ b/i18n/esn/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "No se pudo analizar el valor {0} de \"engines.vscode\". Por ejemplo, use: ^0.10.0, ^1.2.3, ^0.11.0, ^0.10.x, etc.", + "versionSpecificity1": "La versión indicada en \"engines.vscode\" ({0}) no es suficientemente específica. Para las versiones de vscode anteriores a la 1.0.0, defina como mínimo la versión principal y secundaria deseadas. Por ejemplo: ^0.10.0, 0.10.x, 0.11.0, etc.", + "versionSpecificity2": "La versión indicada en \"engines.vscode\" ({0}) no es suficientemente específica. Para las versiones de vscode posteriores a la 1.0.0, defina como mínimo la versión principal deseada. Por ejemplo: ^1.10.0, 1.10.x, 1.x.x, 2.x.x, etc.", + "versionMismatch": "La extensión no es compatible con {0} de Code y requiere: {1}.", + "extensionDescription.empty": "Se obtuvo una descripción vacía de la extensión.", + "extensionDescription.publisher": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "extensionDescription.name": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "extensionDescription.version": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "extensionDescription.engines": "la propiedad `{0}` es obligatoria y debe ser de tipo \"object\"", + "extensionDescription.engines.vscode": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "extensionDescription.extensionDependencies": "la propiedad `{0}` se puede omitir o debe ser de tipo \"string[]\"", + "extensionDescription.activationEvents1": "la propiedad `{0}` se puede omitir o debe ser de tipo \"string[]\"", + "extensionDescription.activationEvents2": "las propiedades `{0}` y `{1}` deben especificarse u omitirse conjuntamente", + "extensionDescription.main1": "la propiedad `{0}` se puede omitir o debe ser de tipo \"string\"", + "extensionDescription.main2": "Se esperaba que \"main\" ({0}) se hubiera incluido en la carpeta de la extensión ({1}). Esto puede hacer que la extensión no sea portátil.", + "extensionDescription.main3": "las propiedades `{0}` y `{1}` deben especificarse u omitirse conjuntamente", + "notSemver": "La versión de la extensión no es compatible con semver." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/esn/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..34f984e34c --- /dev/null +++ b/i18n/esn/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "Nueva ventana", + "newWindowDesc": "Abre una ventana nueva.", + "recentFolders": "áreas de trabajo recientes", + "folderDesc": "{0} {1}", + "codeWorkspace": "Código de área de trabajo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/esn/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..5fb7b62db1 --- /dev/null +++ b/i18n/esn/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "Aceptar", + "integrity.dontShowAgain": "No volver a mostrar", + "integrity.moreInfo": "Más información", + "integrity.prompt": "La instalación de {0} parece estar dañada. Vuelva a instalar." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/esn/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..637132b07e --- /dev/null +++ b/i18n/esn/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "Aporta la configuración del esquema JSON.", + "contributes.jsonValidation.fileMatch": "Patrón de archivo para buscar coincidencias, por ejemplo, \"package.json\" o \"*.launch\".", + "contributes.jsonValidation.url": "Dirección URL de esquema ('http:', 'https:') o ruta de acceso relativa a la carpeta de extensión ('./').", + "invalid.jsonValidation": "configuration.jsonValidation debe ser una matriz", + "invalid.fileMatch": "configuration.jsonValidation.fileMatch debe haberse definido", + "invalid.url": "configuration.jsonValidation.url debe ser una dirección URL o una ruta de acceso relativa", + "invalid.url.fileschema": "configuration.jsonValidation.url es una dirección URL relativa no válida: {0}", + "invalid.url.schema": "configuration.jsonValidation.url debe empezar por \"http:\", \"https:\" o \"./\" para hacer referencia a los esquemas ubicados en la extensión" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/esn/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..55ef6c17b9 --- /dev/null +++ b/i18n/esn/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "Se presionó ({0}). Esperando la siguiente tecla...", + "missing.chord": "La combinación de teclas ({0}, {1}) no es ningún comando." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/keybinding/common/keybindingLabels.i18n.json b/i18n/esn/src/vs/platform/keybinding/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..34c8000082 --- /dev/null +++ b/i18n/esn/src/vs/platform/keybinding/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Mayús", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Mayús", + "altKey.long": "Alt", + "cmdKey.long": "Comando", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/esn/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..c834cc4f55 --- /dev/null +++ b/i18n/esn/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "La propiedad loop solo se admite en el buscador de coincidencias de la última línea.", + "ProblemPatternParser.problemPattern.missingRegExp": "Falta una expresión regular en el patrón de problema.", + "ProblemPatternParser.problemPattern.missingProperty": "El patrón de problema no es válido. Debe tener al menos un archivo, un mensaje y un grupo de coincidencias de ubicación o línea.", + "ProblemPatternParser.invalidRegexp": "Error: La cadena {0} no es una expresión regular válida.\n", + "ProblemPatternSchema.regexp": "Expresión regular para encontrar un error, una advertencia o información en la salida.", + "ProblemPatternSchema.file": "Índice de grupo de coincidencias del nombre de archivo. Si se omite, se usa 1.", + "ProblemPatternSchema.location": "Índice de grupo de coincidencias de la ubicación del problema. Los patrones de ubicación válidos son: (line), (line,column) y (startLine,startColumn,endLine,endColumn). Si se omite, se asume el uso de (line,column).", + "ProblemPatternSchema.line": "Índice de grupo de coincidencias de la línea del problema. Valor predeterminado: 2.", + "ProblemPatternSchema.column": "Índice de grupo de coincidencias del carácter de línea del problema. Valor predeterminado: 3", + "ProblemPatternSchema.endLine": "Índice de grupo de coincidencias de la línea final del problema. Valor predeterminado como no definido.", + "ProblemPatternSchema.endColumn": "Índice de grupo de coincidencias del carácter de línea final del problema. Valor predeterminado como no definido", + "ProblemPatternSchema.severity": "Índice de grupo de coincidencias de la gravedad del problema. Valor predeterminado como no definido.", + "ProblemPatternSchema.code": "Índice de grupo de coincidencias del código del problema. Valor predeterminado como no definido.", + "ProblemPatternSchema.message": "Índice de grupo de coincidencias del mensaje. Si se omite, el valor predeterminado es 4 en caso de definirse la ubicación. De lo contrario, el valor predeterminado es 5.", + "ProblemPatternSchema.loop": "En un bucle de buscador de coincidencias multilínea, indica si este patrón se ejecuta en un bucle siempre que haya coincidencias. Solo puede especificarse en el último patrón de un patrón multilínea.", + "NamedProblemPatternSchema.name": "Nombre del patrón de problema.", + "NamedMultiLineProblemPatternSchema.name": "Nombre del patrón de problema de varias líneas.", + "NamedMultiLineProblemPatternSchema.patterns": "The actual patterns.", + "ProblemPatternExtPoint": "Aporta patrones de problemas", + "ProblemPatternRegistry.error": "Patrón de problema no válido. Se omitirá.", + "ProblemMatcherParser.noProblemMatcher": "Error: La descripción no se puede convertir en un buscador de coincidencias de problemas:\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "Error: La descripción no define un patrón de problema válido:\n{0}\n", + "ProblemMatcherParser.noOwner": "Error: La descripción no define un propietario:\n{0}\n", + "ProblemMatcherParser.noFileLocation": "Error: La descripción no define una ubicación de archivo:\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "Información: Gravedad {0} desconocida. Los valores válidos son \"error\", \"advertencia\" e \"información\".\n", + "ProblemMatcherParser.noDefinedPatter": "Error: El patrón con el identificador {0} no existe.", + "ProblemMatcherParser.noIdentifier": "Error: La propiedad pattern hace referencia a un identificador vacío.", + "ProblemMatcherParser.noValidIdentifier": "Error: La propiedad pattern {0} no es un nombre de variable de patrón válido.", + "ProblemMatcherParser.problemPattern.watchingMatcher": "Un buscador de coincidencias de problemas debe definir tanto un patrón de inicio como un patrón de finalización para la inspección.", + "ProblemMatcherParser.invalidRegexp": "Error: La cadena {0} no es una expresión regular válida.\n", + "WatchingPatternSchema.regexp": "Expresión regular para detectar el principio o el final de una tarea en segundo plano.", + "WatchingPatternSchema.file": "Índice de grupo de coincidencias del nombre de archivo. Se puede omitir.", + "PatternTypeSchema.name": "Nombre de un patrón aportado o predefinido", + "PatternTypeSchema.description": "Patrón de problema o nombre de un patrón de problema que se ha aportado o predefinido. Se puede omitir si se especifica la base.", + "ProblemMatcherSchema.base": "Nombre de un buscador de coincidencias de problemas base que se va a usar.", + "ProblemMatcherSchema.owner": "Propietario del problema dentro de Code. Se puede omitir si se especifica \"base\". Si se omite y no se especifica \"base\", el valor predeterminado es \"external\".", + "ProblemMatcherSchema.severity": "Gravedad predeterminada para los problemas de capturas. Se usa si el patrón no define un grupo de coincidencias para \"severity\".", + "ProblemMatcherSchema.applyTo": "Controla si un problema notificado en un documento de texto se aplica solamente a los documentos abiertos, cerrados o a todos los documentos.", + "ProblemMatcherSchema.fileLocation": "Define cómo deben interpretarse los nombres de archivo notificados en un patrón de problema.", + "ProblemMatcherSchema.background": "Patrones para hacer seguimiento del comienzo y el final en un comprobador activo de la tarea en segundo plano.", + "ProblemMatcherSchema.background.activeOnStart": "Si se establece en True, el monitor está en modo activo cuando la tarea empieza. Esto es equivalente a emitir una línea que coincide con beginPattern", + "ProblemMatcherSchema.background.beginsPattern": "Si se encuentran coincidencias en la salida, se señala el inicio de una tarea en segundo plano.", + "ProblemMatcherSchema.background.endsPattern": "Si se encuentran coincidencias en la salida, se señala el fin de una tarea en segundo plano.", + "ProblemMatcherSchema.watching.deprecated": "Esta propiedad está en desuso. Use la propiedad en segundo plano.", + "ProblemMatcherSchema.watching": "Patrones para hacer un seguimiento del comienzo y el final de un patrón de supervisión.", + "ProblemMatcherSchema.watching.activeOnStart": "Si se establece en true, el monitor está en modo activo cuando la tarea empieza. Esto es equivalente a emitir una línea que coincide con beginPattern", + "ProblemMatcherSchema.watching.beginsPattern": "Si se encuentran coincidencias en la salida, se señala el inicio de una tarea de inspección.", + "ProblemMatcherSchema.watching.endsPattern": "Si se encuentran coincidencias en la salida, se señala el fin de una tarea de inspección", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "This property is deprecated. Use the watching property instead.", + "LegacyProblemMatcherSchema.watchedBegin": "Expresión regular que señala que una tarea inspeccionada comienza a ejecutarse desencadenada a través de la inspección de archivos.", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "This property is deprecated. Use the watching property instead.", + "LegacyProblemMatcherSchema.watchedEnd": "Expresión regular que señala que una tarea inspeccionada termina de ejecutarse.", + "NamedProblemMatcherSchema.name": "Nombre del buscador de coincidencias de problemas usado para referirse a él.", + "NamedProblemMatcherSchema.label": "Etiqueta en lenguaje natural del buscador de coincidencias de problemas. ", + "ProblemMatcherExtPoint": "Aporta buscadores de coincidencias de problemas", + "msCompile": "Problemas del compilador de Microsoft", + "lessCompile": "Menos problemas", + "gulp-tsc": "Problemas de Gulp TSC", + "jshint": "Problemas de JSHint", + "jshint-stylish": "Problemas de estilismo de JSHint", + "eslint-compact": "Problemas de compactación de ESLint", + "eslint-stylish": "Problemas de estilismo de ESLint", + "go": "Ir a problemas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/message/common/message.i18n.json b/i18n/esn/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..7231a694d4 --- /dev/null +++ b/i18n/esn/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Cerrar", + "later": "Más tarde", + "cancel": "Cancelar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/request/node/request.i18n.json b/i18n/esn/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..2d55078a0c --- /dev/null +++ b/i18n/esn/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "El valor del proxy que se debe utilizar. Si no se establece, se tomará de las variables de entorno http_proxy y https_proxy", + "strictSSL": "Indica si el certificado del servidor proxy debe comprobarse en la lista de entidades de certificación proporcionada.", + "proxyAuthorization": "Valor que debe enviarse como encabezado de 'Proxy-Authorization' para cada solicitud de red." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/esn/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..40585b84b1 --- /dev/null +++ b/i18n/esn/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetría", + "telemetry.enableTelemetry": "Habilite los datos de uso y los errores para enviarlos a Microsoft." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/esn/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..445bb7488a --- /dev/null +++ b/i18n/esn/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "Contribuye a la extensión definida para los colores de los temas", + "contributes.color.id": "El identificador de los colores de los temas", + "contributes.color.id.format": "Los identificadores deben estar en la forma aa [.bb] *", + "contributes.color.description": "La descripción de los colores de los temas", + "contributes.defaults.light": "El color predeterminado para los temas claros. Un valor de color en hexadecimal (#RRGGBB [AA]) o el identificador de un color para los temas que proporciona el valor predeterminado.", + "contributes.defaults.dark": "El color predeterminado para los temas oscuros. Un valor de color en hexadecimal (#RRGGBB [AA]) o el identificador de un color para los temas que proporciona el valor predeterminado.", + "contributes.defaults.highContrast": "El color predeterminado para los temas con constraste. Un valor de color en hexadecimal (#RRGGBB [AA]) o el identificador de un color para los temas que proporciona el valor predeterminado.", + "invalid.colorConfiguration": "'configuration.colors' debe ser una matriz", + "invalid.default.colorType": "{0} debe ser un valor de color en hexadecimal (#RRGGBB [AA] o #RGB [A]) o el identificador de un color para los temas que puede ser el valor predeterminado.", + "invalid.id": "'configuration.colors.id' debe ser definida y no puede estar vacía", + "invalid.id.format": "'configuration.colors.id' debe seguir la palabra [.word]*", + "invalid.description": "'configuration.colors.description' debe ser definida y no puede estar vacía", + "invalid.defaults": "'configuration.colors.defaults' debe ser definida y contener 'light', 'dark' y 'highContrast'" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/esn/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..81b7010c24 --- /dev/null +++ b/i18n/esn/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "Formato de color no válido. Use #TGB, #RBGA, #RRGGBB o #RRGGBBAA", + "schema.colors": "Colores usados en el área de trabajo.", + "foreground": "Color de primer plano general. Este color solo se usa si un componente no lo invalida.", + "errorForeground": "Color de primer plano general para los mensajes de erroe. Este color solo se usa si un componente no lo invalida.", + "descriptionForeground": "Color de primer plano para el texto descriptivo que proporciona información adicional, por ejemplo para una etiqueta.", + "focusBorder": "Color de borde de los elementos con foco. Este color solo se usa si un componente no lo invalida.", + "contrastBorder": "Un borde adicional alrededor de los elementos para separarlos unos de otros y así mejorar el contraste.", + "activeContrastBorder": "Un borde adicional alrededor de los elementos activos para separarlos unos de otros y así mejorar el contraste.", + "selectionBackground": "El color de fondo del texto seleccionado en el área de trabajo (por ejemplo, campos de entrada o áreas de texto). Esto no se aplica a las selecciones dentro del editor.", + "textSeparatorForeground": "Color para los separadores de texto.", + "textLinkForeground": "Color de primer plano para los vínculos en el texto.", + "textLinkActiveForeground": "Color de primer plano para los vínculos activos en el texto.", + "textPreformatForeground": "Color de primer plano para los segmentos de texto con formato previo.", + "textBlockQuoteBackground": "Color de fondo para los bloques en texto.", + "textBlockQuoteBorder": "Color de borde para los bloques en texto.", + "textCodeBlockBackground": "Color de fondo para los bloques de código en el texto.", + "widgetShadow": "Color de sombra de los widgets dentro del editor, como buscar/reemplazar", + "inputBoxBackground": "Fondo de cuadro de entrada.", + "inputBoxForeground": "Primer plano de cuadro de entrada.", + "inputBoxBorder": "Borde de cuadro de entrada.", + "inputBoxActiveOptionBorder": "Color de borde de opciones activadas en campos de entrada.", + "inputPlaceholderForeground": "Color de primer plano para el marcador de posición de texto", + "inputValidationInfoBackground": "Color de fondo de validación de entrada para gravedad de información.", + "inputValidationInfoBorder": "Color de borde de validación de entrada para gravedad de información.", + "inputValidationWarningBackground": "Color de fondo de validación de entrada para advertencia de información.", + "inputValidationWarningBorder": "Color de borde de validación de entrada para gravedad de advertencia.", + "inputValidationErrorBackground": "Color de fondo de validación de entrada para gravedad de error.", + "inputValidationErrorBorder": "Color de borde de valdación de entrada para gravedad de error.", + "dropdownBackground": "Fondo de lista desplegable.", + "dropdownForeground": "Primer plano de lista desplegable.", + "dropdownBorder": "Borde de lista desplegable.", + "listFocusBackground": "Color de fondo de la lista o el árbol del elemento con el foco cuando la lista o el árbol están activos. Una lista o un árbol tienen el foco del teclado cuando están activos, cuando están inactivos no.", + "listFocusForeground": "Color de fondo de la lista o el árbol del elemento con el foco cuando la lista o el árbol están activos. Una lista o un árbol tienen el foco del teclado cuando están activos, cuando están inactivos no.", + "listActiveSelectionBackground": "Color de fondo de la lista o el árbol del elemento seleccionado cuando la lista o el árbol están activos. Una lista o un árbol tienen el foco del teclado cuando están activos, cuando están inactivos no.", + "listActiveSelectionForeground": "Color de primer plano de la lista o el árbol del elemento con el foco cuando la lista o el árbol están activos. Una lista o un árbol tienen el foco del teclado cuando están activos, cuando están inactivos no.", + "listInactiveSelectionBackground": "Color de fondo de la lista o el árbol del elemento seleccionado cuando la lista o el árbol están inactivos. Una lista o un árbol tienen el foco del teclado cuando están activos, cuando están inactivos no.", + "listInactiveSelectionForeground": "Color de primer plano de la lista o el árbol del elemento con el foco cuando la lista o el árbol esta inactiva. Una lista o un árbol tiene el foco del teclado cuando está activo, cuando esta inactiva no.", + "listHoverBackground": "Fondo de la lista o el árbol al mantener el mouse sobre los elementos.", + "listHoverForeground": "Color de primer plano de la lista o el árbol al pasar por encima de los elementos con el ratón.", + "listDropBackground": "Fondo de arrastrar y colocar la lista o el árbol al mover los elementos con el mouse.", + "highlight": "Color de primer plano de la lista o el árbol de las coincidencias resaltadas al buscar dentro de la lista o el ábol.", + "pickerGroupForeground": "Selector de color rápido para la agrupación de etiquetas.", + "pickerGroupBorder": "Selector de color rápido para la agrupación de bordes.", + "buttonForeground": "Color de primer plano del botón.", + "buttonBackground": "Color de fondo del botón.", + "buttonHoverBackground": "Color de fondo del botón al mantener el puntero.", + "badgeBackground": "Color de fondo de la insignia. Las insignias son pequeñas etiquetas de información, por ejemplo los resultados de un número de resultados.", + "badgeForeground": "Color de fondo de la insignia. Las insignias son pequeñas etiquetas de información, por ejemplo los resultados de un número de resultados.", + "scrollbarShadow": "Sombra de la barra de desplazamiento indica que la vista se ha despazado.", + "scrollbarSliderBackground": "Color de fondo de control deslizante de barra de desplazamiento.", + "scrollbarSliderHoverBackground": "Color de fondo de barra de desplazamiento cursor cuando se pasar sobre el control.", + "scrollbarSliderActiveBackground": "Color de fondo de barra de desplazamiento cursor cuando está activo.", + "progressBarBackground": "Color de fondo para la barra de progreso que se puede mostrar para las operaciones de larga duración.", + "editorBackground": "Color de fondo del editor.", + "editorForeground": "Color de primer plano predeterminado del editor.", + "editorWidgetBackground": "Color de fondo del editor de widgets como buscar/reemplazar", + "editorWidgetBorder": "Color de borde de los widgets del editor. El color solo se usa si el widget elige tener un borde y no invalida el color.", + "editorSelectionBackground": "Color de la selección del editor.", + "editorSelectionForeground": "Color del texto seleccionado para alto contraste.", + "editorInactiveSelection": "Color de la selección en un editor inactivo.", + "editorSelectionHighlight": "Color de las regiones con el mismo contenido que la selección.", + "editorFindMatch": "Color de la coincidencia de búsqueda actual.", + "findMatchHighlight": "Color de las demás coincidencias de búsqueda.", + "findRangeHighlight": "Color del intervalo que limita la búsqueda.", + "hoverHighlight": "Resaltado debajo de la palabra para la que se muestra un recuadro al mantener el puntero.", + "hoverBackground": "Color de fondo al mantener el puntero en el editor.", + "hoverBorder": "Color del borde al mantener el puntero en el editor.", + "activeLinkForeground": "Color de los vínculos activos.", + "diffEditorInserted": "Color de fondo para el texto insertado.", + "diffEditorRemoved": "Color de fondo para el texto quitado.", + "diffEditorInsertedOutline": "Color de contorno para el texto insertado.", + "diffEditorRemovedOutline": "Color de contorno para el texto quitado.", + "mergeCurrentHeaderBackground": "Fondo del encabezado actual en conflictos de combinación alineados.", + "mergeCurrentContentBackground": "Fondo del contenido actual en conflictos de combinación alineados.", + "mergeIncomingHeaderBackground": "Fondo del encabezado de entrada en conflictos de combinación alineados.", + "mergeIncomingContentBackground": "Fondo del contenido de entrada en conflcitos de combinación alineados.", + "mergeCommonHeaderBackground": "Fondo del encabezado de ancestros comunes en conflictos de combinación alineados.", + "mergeCommonContentBackground": "Fondo del contenido de ancestros comunes en conflictos de combinación alineados.", + "mergeBorder": "Color del borde en los encabezados y el divisor en conflictos de combinación alineados.", + "overviewRulerCurrentContentForeground": "Primer plano de la regla de visión general actual para conflictos de combinación alineados.", + "overviewRulerIncomingContentForeground": "Primer plano de regla de visión general de entrada para conflictos de combinación alineados.", + "overviewRulerCommonContentForeground": "Primer plano de la regla de visión general de ancestros comunes para conflictos de combinación alineados.", + "overviewRulerFindMatchForeground": "Color de marcador de regla de información general para coincidencias encontradas.", + "overviewRulerSelectionHighlightForeground": "Color de marcador de regla de información general para elementos seleccionados resaltados." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/esn/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..aa2f613190 --- /dev/null +++ b/i18n/esn/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "Espacio de trabajo de código", + "untitledWorkspace": "Sin título (Espacio de trabajo)", + "workspaceNameVerbose": "{0} (espacio de trabajo)", + "workspaceName": "{0} (espacio de trabajo)" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/esn/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..022c61c75e --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Sobrescribiendo la extensión {0} con {1}.", + "extensionUnderDevelopment": "Cargando la extensión de desarrollo en {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/esn/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..9b8fe8da3f --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Cerrar", + "cancel": "Cancelar", + "ok": "Aceptar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/esn/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..809d6362e8 --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "No se mostrarán {0} errores y advertencias adicionales." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/node/extHostExplorerView.i18n.json b/i18n/esn/src/vs/workbench/api/node/extHostExplorerView.i18n.json new file mode 100644 index 0000000000..bfabcdeca8 --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/node/extHostExplorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "No hay registrado ningún TreeExplorerNodeProvider con el identificador \"{0}\".", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider \"{0}\" no pudo proporcionar el nodo raíz." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/esn/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..cda2721ec2 --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "La extensión `{1}` no se pudo activar. Motivo: dependencia `{0}` desconocida.", + "failedDep1": "La extensión `{1}` no se pudo activar. Motivo: La dependencia `{0}` no se pudo activar.", + "failedDep2": "La extensión `{0}` no se pudo activar. Motivo: más de 10 niveles de dependencias (probablemente sea un bucle de dependencias).", + "activationError": "Error al activar la extensión `{0}`: {1}." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/esn/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json b/i18n/esn/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json new file mode 100644 index 0000000000..d6a8a2f94c --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "No hay registrado ningún TreeExplorerNodeProvider con el identificador \"{0}\".", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider \"{0}\" no pudo proporcionar el nodo raíz.", + "treeExplorer.failedToResolveChildren": "TreeExplorerNodeProvider \"{0}\" no pudo resolver los elementos secundarios." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/node/extHostTreeView.i18n.json b/i18n/esn/src/vs/workbench/api/node/extHostTreeView.i18n.json new file mode 100644 index 0000000000..bfabcdeca8 --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/node/extHostTreeView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "No hay registrado ningún TreeExplorerNodeProvider con el identificador \"{0}\".", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider \"{0}\" no pudo proporcionar el nodo raíz." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/esn/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..886efa5fc2 --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "No se ha registrado ninga vista del árbol con id '{0}'.", + "treeItem.notFound": "No se encontró ningún item del árbol con id '{0}'.", + "treeView.duplicateElement": "El elemento '{0}' ya está registrado" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json b/i18n/esn/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..022c61c75e --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Sobrescribiendo la extensión {0} con {1}.", + "extensionUnderDevelopment": "Cargando la extensión de desarrollo en {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/api/node/mainThreadMessageService.i18n.json b/i18n/esn/src/vs/workbench/api/node/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..9b8fe8da3f --- /dev/null +++ b/i18n/esn/src/vs/workbench/api/node/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Cerrar", + "cancel": "Cancelar", + "ok": "Aceptar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..6ba5d5df00 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "Configurar idioma", + "displayLanguage": "Define el lenguaje para mostrar de VSCode.", + "doc": "Consulte {0} para obtener una lista de idiomas compatibles.", + "restart": "Al cambiar el valor se requiere reiniciar VSCode.", + "fail.createSettings": "No se puede crear '{0}' ({1}).", + "JsonSchema.locale": "Idioma de la interfaz de usuario que debe usarse." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..dd281adbed --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Abrir carpeta...", + "openFileFolder": "Abrir..." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..65c0aeb575 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "Alternar visibilidad de la barra de actividades", + "view": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..1fa2f00e9d --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "Alternar diseño vertical/horizontal del grupo de editores", + "horizontalLayout": "Diseño horizontal del grupo de editores", + "verticalLayout": "Diseño vertical del grupo de editores", + "view": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..e60276c5f8 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Alternar la ubicación de la barra lateral", + "view": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..616089b789 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "Alternar visibilidad de la barra lateral", + "view": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..02702bb986 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "Alternar visibilidad de la barra de estado", + "view": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..f236db4a4c --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "Alternar modo zen", + "view": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..aef65feaad --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Abrir carpeta...", + "openFileFolder": "Abrir...", + "addFolderToWorkspace": "Agregar carpeta al área de trabajo...", + "add": "&& Agregar", + "addFolderToWorkspaceTitle": "Agregar carpeta al área de trabajo", + "newWorkspace": "Nueva área de trabajo...", + "select": "&&Seleccionar", + "selectWorkspace": "Seleccionar carpetas para el área de trabajo", + "removeFolderFromWorkspace": "Quitar carpeta del área de trabajo", + "saveWorkspaceAsAction": "Guardar área de trabajo como...", + "saveEmptyWorkspaceNotSupported": "Abra un área de trabajo antes de guardar.", + "save": "&&Guardar", + "saveWorkspace": "Guardar área de trabajo", + "openWorkspaceAction": "Abrir área de trabajo...", + "openWorkspaceConfigFile": "Abrir archivo de configuración del área de trabajo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..25c714d04e --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "Quitar de la barra de actividades", + "keepInActivityBar": "Guardar en la barra de la actividad", + "titleKeybinding": "{0} ({1})", + "additionalViews": "Vistas adicionales", + "numberBadge": "{0} ({1})", + "manageExtension": "Administrar extensión", + "toggle": "Alternar vista fijada" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..94ce75a946 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "Ocultar barra de actividades", + "activityBarAriaLabel": "Modificador de vista activa", + "globalActions": "Acciones globales" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..732cac40fb --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "{0} acciones", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..a0ce789620 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "Visor binario" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..09d96e72dc --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Editor de texto", + "textDiffEditor": "Editor de diferencias de texto", + "binaryDiffEditor": "Editor de diferencias binario", + "sideBySideEditor": "Editor de lado a lado", + "groupOnePicker": "Mostrar editores del primer grupo", + "groupTwoPicker": "Mostrar editores del segundo grupo", + "groupThreePicker": "Mostrar editores del tercer grupo", + "allEditorsPicker": "Mostrar todos los editores abiertos", + "view": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..ada42c7a29 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "Dividir editor", + "joinTwoGroups": "Combinar editores de dos grupos", + "navigateEditorGroups": "Navegar entre los grupos de editores", + "focusActiveEditorGroup": "Enfocar grupo de editores activo", + "focusFirstEditorGroup": "Enfocar primer grupo de editores", + "focusSecondEditorGroup": "Enfocar segundo grupo de editores", + "focusThirdEditorGroup": "Enfocar tercer grupo de editores", + "focusPreviousGroup": "Enfocar el grupo anterior", + "focusNextGroup": "Enfocar el grupo siguiente", + "openToSide": "Abrir en el lateral", + "closeEditor": "Cerrar editor", + "revertAndCloseActiveEditor": "Revertir y cerrar el editor", + "closeEditorsToTheLeft": "Cerrar los editores a la izquierda", + "closeEditorsToTheRight": "Cerrar los editores a la derecha", + "closeAllEditors": "Cerrar todos los editores", + "closeUnmodifiedEditors": "Cerrar los editores en el grupo sin modificar", + "closeEditorsInOtherGroups": "Cerrar los editores de otros grupos", + "closeOtherEditorsInGroup": "Cerrar otros editores", + "closeEditorsInGroup": "Cerrar todos los editores del grupo", + "moveActiveGroupLeft": "Mover el grupo de editores a la izquierda", + "moveActiveGroupRight": "Mover el grupo de editores a la derecha", + "minimizeOtherEditorGroups": "Minimizar otros grupos de editores", + "evenEditorGroups": "Uniformar anchos del grupo de editores", + "maximizeEditor": "Maximizar el grupo de editores y ocultar la barra lateral", + "keepEditor": "Mantener editor", + "openNextEditor": "Abrir el editor siguiente", + "openPreviousEditor": "Abrir el editor anterior", + "nextEditorInGroup": "Abrir el siguiente editor del grupo", + "openPreviousEditorInGroup": "Abrir el editor anterior en el grupo", + "navigateNext": "Hacia delante", + "navigatePrevious": "Hacia atrás", + "reopenClosedEditor": "Volver a abrir el editor cerrado", + "clearRecentFiles": "Borrar abiertos recientemente", + "showEditorsInFirstGroup": "Mostrar editores del primer grupo", + "showEditorsInSecondGroup": "Mostrar editores del segundo grupo", + "showEditorsInThirdGroup": "Mostrar editores del tercer grupo", + "showEditorsInGroup": "Mostrar los editores del grupo", + "showAllEditors": "Mostrar todos los editores", + "openPreviousRecentlyUsedEditorInGroup": "Abrir el editor recientemente usado anterior en el grupo", + "openNextRecentlyUsedEditorInGroup": "Abrir el siguiente editor recientemente usado en el grupo", + "navigateEditorHistoryByInput": "Abrir el editor anterior desde el historial", + "openNextRecentlyUsedEditor": "Abrir el siguiente editor recientemente usado", + "openPreviousRecentlyUsedEditor": "Abrir el anterior editor recientemente usado", + "clearEditorHistory": "Borrar historial del editor", + "focusLastEditorInStack": "Abrir el último editor del grupo", + "moveEditorLeft": "Mover el editor a la izquierda", + "moveEditorRight": "Mover el editor a la derecha", + "moveEditorToPreviousGroup": "Mover editor al grupo anterior", + "moveEditorToNextGroup": "Mover editor al grupo siguiente" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..a39e332857 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "Mover el editor activo por tabulaciones o grupos", + "editorCommand.activeEditorMove.arg.name": "Argumento para mover el editor activo", + "editorCommand.activeEditorMove.arg.description": "Propiedades del argumento:\n\t\t\t\t\t\t* 'to': valor de cadena que indica el lugar al que mover.\n\t\t\t\t\t\t* 'by': valor de cadena que proporciona la unidad para mover. Por pestaña o por grupo.\n\t\t\t\t\t\t* 'value': valor numérico que indica el número de posiciones o una posición absoluta para mover.\n\t\t\t\t\t", + "commandDeprecated": "El comando **{0}** se ha quitado. Puede usar en su lugar **{1}**", + "openKeybindings": "Configurar métodos abreviados de teclado" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..9bd0439f8f --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "Izquierda", + "groupTwoVertical": "Centro", + "groupThreeVertical": "Derecha", + "groupOneHorizontal": "Superior", + "groupTwoHorizontal": "Centro", + "groupThreeHorizontal": "Inferior", + "editorOpenError": "No se puede abrir '{0}': {1}." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..ad8b289729 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, selector del grupo de editores", + "groupLabel": "Grupo: {0}", + "noResultsFoundInGroup": "No se encontró un editor abierto coincidente en el grupo", + "noOpenedEditors": "La lista de editores abiertos está vacía actualmente en el grupo.", + "noResultsFound": "No se encontró un editor abierto coincidente", + "noOpenedEditorsAllGroups": "La lista de editores abiertos está vacía actualmente." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..c33f2ef843 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "Lín. {0}, Col. {1} ({2} seleccionada)", + "singleSelection": "Lín. {0}, Col. {1}", + "multiSelectionRange": "{0} selecciones ({1} caracteres seleccionados)", + "multiSelection": "{0} selecciones", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "Tabulación Mueve el Foco", + "screenReaderDetected": "Lector de Pantalla Detectado", + "screenReaderDetectedExtra": "Si no va a usar un lector de pantalla, cambie el valor de configuración \"editor.accessibilitySupport\" a \"desactivado\".", + "disableTabMode": "Deshabilitar modo de accesibilidad", + "gotoLine": "Ir a la línea", + "indentation": "Sangría", + "selectEncoding": "Seleccionar Encoding", + "selectEOL": "Seleccionar secuencia de fin de línea", + "selectLanguageMode": "Seleccionar modo de lenguaje", + "fileInfo": "Información del archivo", + "spacesSize": "Espacios: {0}", + "tabSize": "Tamaño de tabulación: {0}", + "showLanguageExtensions": "Buscar extensiones de Marketplace para '{0}'...", + "changeMode": "Cambiar modo de lenguaje", + "noEditor": "No hay ningún editor de texto activo en este momento.", + "languageDescription": "({0}): lenguaje configurado", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "lenguajes (identificador)", + "configureModeSettings": "Configurar los parámetros basados en el lenguaje \"{0}\"...", + "configureAssociationsExt": "Configurar asociación de archivos para '{0}'...", + "autoDetect": "Detectar automáticamente", + "pickLanguage": "Seleccionar modo de lenguaje", + "currentAssociation": "Asociación actual", + "pickLanguageToConfigure": "Seleccionar modo de lenguaje para asociar con '{0}'", + "changeIndentation": "Cambiar sangría", + "noWritableCodeEditor": "El editor de código activo es de solo lectura.", + "indentView": "cambiar vista", + "indentConvert": "convertir archivo", + "pickAction": "Seleccionar acción", + "changeEndOfLine": "Cambiar secuencia de fin de línea", + "pickEndOfLine": "Seleccionar secuencia de fin de línea", + "changeEncoding": "Cambiar codificación de archivo", + "noFileEditor": "No hay ningún archivo activo en este momento.", + "saveWithEncoding": "Guardar con Encoding", + "reopenWithEncoding": "Volver a abrir con Encoding", + "guessedEncoding": "Adivinado por el contenido", + "pickEncodingForReopen": "Seleccionar codificación de archivo para reabrir archivo", + "pickEncodingForSave": "Seleccionar codificación de archivo para guardar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..179805c392 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "Acciones de pestaña" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..e3128d13a3 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "Editor de diferencias de texto", + "readonlyEditorWithInputAriaLabel": "{0}. Editor de comparación de textos de solo lectura.", + "readonlyEditorAriaLabel": "Editor de comparación de textos de solo lectura.", + "editableEditorWithInputAriaLabel": "{0}. Editor de comparación de archivos de texto.", + "editableEditorAriaLabel": "Editor de comparación de archivos de texto.", + "navigate.next.label": "Cambio siguiente", + "navigate.prev.label": "Cambio anterior", + "inlineDiffLabel": "Cambiar a vista alineada", + "sideBySideDiffLabel": "Cambiar a vista en paralelo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..fabfb074a0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0}, Grupo {1}." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..afe3f30eac --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Editor de texto", + "readonlyEditorWithInputAriaLabel": "{0}. Editor de texto de solo lectura.", + "readonlyEditorAriaLabel": "Editor de texto de solo lectura.", + "untitledFileEditorWithInputAriaLabel": "{0}. Editor de texto de archivos sin título.", + "untitledFileEditorAriaLabel": "Editor de texto de archivos sin título." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..607a5f07ab --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Cerrar", + "closeOthers": "Cerrar otros", + "closeRight": "Cerrar a la derecha", + "closeAll": "Cerrar todo", + "closeAllUnmodified": "Cerrar los no modificados", + "keepOpen": "Mantener abierto", + "showOpenedEditors": "Mostrar editores abiertos", + "araLabelEditorActions": "Acciones del editor" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..5aaf452b51 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "Cerrar panel", + "togglePanel": "Alternar panel", + "focusPanel": "Centrarse en el panel", + "toggleMaximizedPanel": "Alternar el panel maximizado", + "maximizePanel": "Maximizar el tamaño del panel", + "minimizePanel": "Restaurar el tamaño del panel", + "view": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..d009a64d66 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "Activar el conmutador de panel" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..cb8b4c4436 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} (Presione \"Entrar\" para confirmar o \"Esc\" para cancelar)", + "inputModeEntry": "Presione \"Entrar\" para confirmar su entrada o \"Esc\" para cancelar", + "emptyPicks": "No hay entradas para seleccionar", + "quickOpenInput": "Escriba '?' para obtener ayuda con las acciones que puede realizar desde aquí", + "historyMatches": "abiertos recientemente", + "noResultsFound1": "No se encontraron resultados", + "canNotRunPlaceholder": "Este controlador de Quick Open no se puede usar en el contexto actual", + "entryAriaLabel": "{0}, abiertos recientemente", + "removeFromEditorHistory": "Quitar del historial", + "pickHistory": "Seleccione una entrada del editor para quitarla del historial" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..4df2c13ef2 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Ir al archivo...", + "quickNavigateNext": "Navegar a siguiente en Quick Open", + "quickNavigatePrevious": "Navegar a anterior en Quick Open", + "quickSelectNext": "Seleccionar Siguiente en Quick Open", + "quickSelectPrevious": "Seleccionar Anterior en Quick Open" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..4df2c13ef2 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Ir al archivo...", + "quickNavigateNext": "Navegar a siguiente en Quick Open", + "quickNavigatePrevious": "Navegar a anterior en Quick Open", + "quickSelectNext": "Seleccionar Siguiente en Quick Open", + "quickSelectPrevious": "Seleccionar Anterior en Quick Open" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..d0fbbfc107 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "Ocultar barra lateral", + "focusSideBar": "Enfocar la barra lateral", + "viewCategory": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..57ea668e88 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "El comando \"{0}\" no está habilitado actualmente y no se puede ejecutar.", + "manageExtension": "Administrar extensión" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..3ca371ba58 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": " [No se admite]", + "devExtensionWindowTitlePrefix": "[Host de desarrollo de la extensión]" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/quickopen.i18n.json b/i18n/esn/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..e666357f81 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "No hay resultados coincidentes", + "noResultsFound2": "No se encontraron resultados", + "entryAriaLabel": "{0}, comando" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/viewlet.i18n.json b/i18n/esn/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..fb257f6096 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Contraer todo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/common/theme.i18n.json b/i18n/esn/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..f76668292a --- /dev/null +++ b/i18n/esn/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "Color de fondo de la pestaña activa. Las pestañas son los contenedores de los editores en el área de editores. Se pueden abrir varias pestañas en un grupo de editores. Puede haber varios grupos de editores.", + "tabInactiveBackground": "Color de fondo de la pestaña inactiva. Las pestañas son los contenedores de los editores en el área de editores. Se pueden abrir varias pestañas en un grupo de editores. Puede haber varios grupos de editores.", + "tabBorder": "Borde para separar las pestañas entre sí. Las pestañas son contenedores de editores en el área de editores. Se pueden abrir varias pestañas en un grupo de editores. Puede haber varios grupos de editores.", + "tabActiveBorder": "Borde para resaltar las fichas activas. Las fichas son los contenedores de los editores en el área de editores. Se pueden abrir varias fichas en un grupo de editores. Puede haber varios grupos de editores.", + "tabActiveUnfocusedBorder": "Borde para resaltar las fichas activas en un grupo que no tiene el foco. Las fichas son los contenedores de los editores en el área de editores. Se pueden abrir varias fichas en un grupo de editores. Puede haber varios grupos de editores. ", + "tabActiveForeground": "Color de primer plano de la pestaña activa en un grupo activo. Las pestañas son los contenedores de los editores en el área de editores. Se pueden abrir varias pestañas en un grupo de editores. Puede haber varios grupos de editores.", + "tabInactiveForeground": "Color de primer plano de la pestaña inactiva en un grupo activo. Las pestañas son los contenedores de los editores en el área de editores. Se pueden abrir varias pestañas en un grupo de editores. Puede haber varios grupos de editores.", + "tabUnfocusedActiveForeground": "Color de primer plano de la ficha activa en un grupo que no tiene el foco. Las fichas son los contenedores de los editores en el área de editores. Se pueden abrir varias fichas en un grupo de editores. Puede haber varios grupos de editores. ", + "tabUnfocusedInactiveForeground": "Color de primer plano de las fichas inactivas en un grupo que no tiene el foco. Las fichas son los contenedores de los editores en el área de editores. Se pueden abrir varias fichas en un grupo de editores. Puede haber varios grupos de editores. ", + "editorGroupBackground": "Color de fondo de un grupo de editores. Los grupos de editores son los contenedores de los editores. El color de fondo se ve cuando se mueven arrastrando los grupos de editores.", + "tabsContainerBackground": "Color de fondo del encabezado del título del grupo de editores cuando las fichas están habilitadas. Los grupos de editores son contenedores de editores.", + "tabsContainerBorder": "Color de borde del encabezado del título del grupo de editores cuando las fichas están habilitadas. Los grupos de editores son contenedores de editores.", + "editorGroupHeaderBackground": "Color de fondo del encabezado del título del grupo de editores cuando las fichas están deshabilitadas. Los grupos de editores son contenedores de editores.", + "editorGroupBorder": "Color para separar varios grupos de editores entre sí. Los grupos de editores son los contenedores de los editores.", + "editorDragAndDropBackground": "Color de fondo cuando se arrastran los editores. El color debería tener transparencia para que el contenido del editor pueda brillar a su través.", + "panelBackground": "Color de fondo del panel. Los paneles se muestran debajo del área de editores y contienen vistas, como Salida y Terminal integrado.", + "panelBorder": "Color del borde superior del panel que lo separa del editor. Los paneles se muestran debajo del área de editores y contienen vistas, como Salida y Terminal integrado.", + "panelActiveTitleForeground": "Color del título del panel activo. Los paneles se muestran debajo del área del editor y contienen vistas como Salida y Terminal integrado.", + "panelInactiveTitleForeground": "Color del título del panel inactivo. Los paneles se muestran debajo del área del editor y contienen vistas como Salida y Terminal integrado.", + "panelActiveTitleBorder": "Color de borde del título del panel activo. Los paneles se muestran debajo del área del editor y contienen vistas como Salida y Terminal integrado.", + "statusBarForeground": "Color de primer plano de la barra de estado cuando se abre un espacio de trabajo. La barra de estado se muestra en la parte inferior de la ventana.", + "statusBarNoFolderForeground": "Color de primer plano de la barra de estado cuando no hay ninguna carpeta abierta. La barra de estado se muestra en la parte inferior de la ventana.", + "statusBarBackground": "Color de fondo de la barra de estado cuando se abre un espacio de trabajo. La barra de estado se muestra en la parte inferior de la ventana.", + "statusBarNoFolderBackground": "Color de fondo de la barra de estado cuando no hay ninguna carpeta abierta. La barra de estado se muestra en la parte inferior de la ventana.", + "statusBarBorder": "Color de borde de la barra de estado que separa la barra lateral y el editor. La barra de estado se muestra en la parte inferior de la ventana.", + "statusBarNoFolderBorder": "Color de borde de la barra de estado que separa la barra lateral y el editor cuando no hay ninguna carpeta abierta. La barra de estado se muestra en la parte inferior de la ventana.", + "statusBarItemActiveBackground": "Color de fondo de un elemento de la barra de estado al hacer clic. La barra de estado se muestra en la parte inferior de la ventana.", + "statusBarItemHoverBackground": "Color de fondo de un elemento de la barra de estado al mantener el puntero. La barra de estado se muestra en la parte inferior de la ventana.", + "statusBarProminentItemBackground": "Color de fondo de los elementos destacados en la barra de estado. Estos elementos sobresalen para indicar importancia. La barra de estado está ubicada en la parte inferior de la ventana.", + "statusBarProminentItemHoverBackground": "Color de fondo de los elementos destacados en la barra de estado cuando se mantiene el mouse sobre estos elementos. Estos elementos sobresalen para indicar importancia. La barra de estado está ubicada en la parte inferior de la ventana.", + "activityBarBackground": "Color de fondo de la barra de actividad, que se muestra en el lado izquierdo o derecho y que permite cambiar entre diferentes vistas de la barra lateral.", + "activityBarForeground": "Color de fondo de la barra de actividad (por ejemplo utilizado por los iconos). La barra de actividad muestra en el lado izquierdo o derecho y permite cambiar entre diferentes vistas de la barra lateral.", + "activityBarBorder": "Color de borde de la barra de actividad que separa la barra lateral. La barra de actividad se muestra en el extremo derecho o izquierdo y permite cambiar entre las vistas de la barra lateral.", + "activityBarDragAndDropBackground": "Arrastre y suelte el color de las sugerencias para los elementos de la barra de actividad. El color debería tener transparencias, de forma que los elementos de la barra continúen siendo visibles a través de él. La barra de actividad se muestra en el extremo izquierdo o derecho y permite cambiar entre distintas vistas de la barra lateral.", + "activityBarBadgeBackground": "Color de fondo de distintivo de notificación de actividad. La barra de actividad se muestra en el extremo izquierdo o derecho y permite cambiar entre vistas de la barra lateral.", + "activityBarBadgeForeground": "Color de primer plano de distintivo de notificación de actividad. La barra de actividad se muestra en el extremo izquierdo o derecho y permite cambiar entre vistas de la barra lateral.", + "sideBarBackground": "Color de fondo de la barra lateral, que es el contenedor de vistas como Explorador y Búsqueda.", + "sideBarForeground": "Color de primer plano de la barra lateral, que es el contenedor de vistas como Explorador y Búsqueda.", + "sideBarBorder": "Color de borde de la barra lateral en el lado que separa el editor. La barra lateral es el contenedor de vistas como Explorador y Búsqueda.", + "sideBarTitleForeground": "Color de primer plano del título de la barra lateral, que es el contenedor de vistas como Explorador y Búsqueda.", + "sideBarDragAndDropBackground": "Color de arrastrar y colocar comentarios para las secciones de la barra lateral. El color debe tener transparencia para permitir que se vean las secciones de la barra lateral, que es el contenedor para vistas como la del explorador o la de búsqueda.", + "sideBarSectionHeaderBackground": "Color de fondo del encabezado de sección de la barra lateral, que es el contenedor de vistas como Explorador y Búsqueda.", + "sideBarSectionHeaderForeground": "Color de primer plano del encabezado de sección de la barra lateral, que es el contenedor de vistas como Explorador y Búsqueda.", + "titleBarActiveForeground": "Color de primer plano de la barra de título cuando la ventana está activa. Tenga en cuenta que, actualmente, este clor solo se admite en macOS.", + "titleBarInactiveForeground": "Color de primer plano de la barra de título cuando la ventana está inactiva. Tenga en cuenta que, actualmente, este color solo se admite en macOS.", + "titleBarActiveBackground": "Fondo de la barra de título cuando la ventana está activa. Tenga en cuenta que, actualmente, este color solo se admite en macOS.", + "titleBarInactiveBackground": "Color de fondo de la barra de título cuando la ventana está inactiva. Tenga en cuenta que, actualmente, este color solo se admite en macOS.", + "titleBarBorder": "Color de borde de la barra de título. Tenga en cuenta que, actualmente, este color se admite solo en macOS.", + "notificationsForeground": "Color de primer plano de las notificaciones. Estas se deslizan en la parte superior de la ventana. ", + "notificationsBackground": "Color de fondo de las notificaciones. Estas se deslizan en la parte superior de la ventana. ", + "notificationsButtonBackground": "Color de fondo del botón de notificaciones. Estas se deslizan en la parte superior de la ventana.", + "notificationsButtonHoverBackground": "Color de fondo del botón de notificaciones al desplazar el ratón sobre él. Estas se deslizan en la parte superior de la ventana.", + "notificationsButtonForeground": "Color de primer plano del botón de notificaciones. Estas se deslizan en la parte superior de la ventana.", + "notificationsInfoBackground": "Color de fondo de la información de notificaciones. Estas se deslizan en la parte superior de la ventana.", + "notificationsInfoForeground": "Color de primer plano de la información de notificaciones. Estas se deslizan en la parte superior de la ventana.", + "notificationsWarningBackground": "Color de fondo del aviso de notificaciones. Estas se deslizan en la parte superior de la ventana.", + "notificationsWarningForeground": "Color de primer plano del aviso de notificaciones. Estas se deslizan en la parte superior de la ventana.", + "notificationsErrorBackground": "Color de fondo del error de notificaciones. Estas se deslizan en la parte superior de la ventana.", + "notificationsErrorForeground": "Color de primer plano del error de notificaciones. Estas se deslizan en la parte superior de la ventana." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/esn/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..eab44cbf9c --- /dev/null +++ b/i18n/esn/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "Cerrar editor", + "closeWindow": "Cerrar ventana", + "closeWorkspace": "Cerrar área de trabajo", + "noWorkspaceOpened": "No hay ninguna área de trabajo abierta en esta instancia para cerrarla.", + "newWindow": "Nueva ventana", + "toggleFullScreen": "Alternar pantalla completa", + "toggleMenuBar": "Alternar barra de menús", + "toggleDevTools": "Alternar herramientas de desarrollo", + "zoomIn": "Acercar", + "zoomOut": "Alejar", + "zoomReset": "Restablecer zoom", + "appPerf": "Rendimiento de inicio", + "reloadWindow": "Recargar ventana", + "switchWindowPlaceHolder": "Seleccionar una ventana a la que cambiar", + "current": "Ventana actual", + "close": "Cerrar ventana", + "switchWindow": "Cambiar de Ventana...", + "quickSwitchWindow": "Cambio Rápido de Ventana...", + "workspaces": "áreas de trabajo", + "files": "archivos", + "openRecentPlaceHolderMac": "Seleccione para abrir (mantenga presionada la tecla Comando para abrir en una ventana nueva)", + "openRecentPlaceHolder": "Seleccione para abrir (mantenga presionada la tecla Ctrl para abrir en una ventana nueva)", + "remove": "Quitar de abiertos recientemente", + "openRecent": "Abrir Reciente...", + "quickOpenRecent": "Abrir Reciente Rapidamente...", + "closeMessages": "Cerrar mensajes de notificación", + "reportIssues": "Notificar problemas", + "reportPerformanceIssue": "Notificar problema de rendimiento", + "keybindingsReference": "Referencia de métodos abreviados de teclado", + "openDocumentationUrl": "Documentación", + "openIntroductoryVideosUrl": "Vídeos de introducción", + "openTipsAndTricksUrl": "Sugerencias y trucos", + "toggleSharedProcess": "Alternar proceso compartido", + "navigateLeft": "Navegar a la Vista de la Izquierda", + "navigateRight": "Navegar a la Vista de la Derecha", + "navigateUp": "Navegar a la Vista Superior", + "navigateDown": "Navegar a la Vista Inferior", + "increaseViewSize": "Aumentar tamaño de vista actual", + "decreaseViewSize": "Reducir tamaño de vista actual" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/esn/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/esn/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/electron-browser/crashReporter.i18n.json b/i18n/esn/src/vs/workbench/electron-browser/crashReporter.i18n.json new file mode 100644 index 0000000000..db9fd677ca --- /dev/null +++ b/i18n/esn/src/vs/workbench/electron-browser/crashReporter.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetría", + "telemetry.enableCrashReporting": "Habilite los informes de bloqueo para enviarlos a Microsoft. Esta opción requiere reiniciar para que tenga efecto." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/esn/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..835953b0fd --- /dev/null +++ b/i18n/esn/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "El host de extensiones no se inició en 10 segundos, puede que se detenga en la primera línea y necesita un depurador para continuar.", + "extensionHostProcess.startupFail": "El host de extensiones no se inició en 10 segundos, lo cual puede ser un problema.", + "extensionHostProcess.error": "Error del host de extensiones: {0}", + "devTools": "Herramientas de desarrollo", + "extensionHostProcess.crash": "El host de extensiones finalizó inesperadamente. Recargue la ventana para recuperarlo." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/esn/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..f1d6a60e08 --- /dev/null +++ b/i18n/esn/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "Ver", + "help": "Ayuda", + "file": "Archivo", + "workspaces": "Áreas de trabajo", + "developer": "Desarrollador", + "showEditorTabs": "Controla si los editores abiertos se deben mostrar o no en pestañas.", + "editorTabCloseButton": "Controla la posición de los botones de cierre de pestañas del editor o los deshabilita si se establece en \"off\".", + "showIcons": "Controla si los editores abiertos deben mostrarse o no con un icono. Requiere que también se habilite un tema de icono.", + "enablePreview": "Controla si los editores abiertos se muestran en vista previa. Los editores en vista previa se reutilizan hasta que se guardan (por ejemplo, mediante doble clic o editándolos).", + "enablePreviewFromQuickOpen": "Controla si los editores abiertos mediante Quick Open se muestran en modo de vista previa. Los editores en modo de vista previa se reutilizan hasta que se conservan (por ejemplo, mediante doble clic o editándolos).", + "editorOpenPositioning": "Controla dónde se abren los editores. Seleccione 'izquierda' o 'derecha' para abrir los editores situados a la izquierda o la derecha del que está actualmente activo. Seleccione 'primero' o 'último' para abrir los editores con independencia del que esté actualmente activo.", + "revealIfOpen": "Controla si un editor se muestra en alguno de los grupos visibles cuando se abre. Si se deshabilita esta opción, un editor preferirá abrirse en el grupo de editores activo en ese momento. Si se habilita, un editor ya abierto se mostrará en lugar de volver a abrirse en el grupo de editores activo. Tenga en cuenta que hay casos en los que esta opción se omite; por ejemplo, cuando se fuerza la apertura de un editor en un grupo específico o junto al grupo activo actual.", + "commandHistory": "Controla el número de comandos usados recientemente a almacenar en el historial para la paleta de comandos. Establezca a 0 para deshabilitar el historial de comandos.", + "preserveInput": "Controla si la última entrada introducida en la paleta de comandos debería ser restaurada cuando sea abierta la próxima vez.", + "closeOnFocusLost": "Controla si Quick Open debe cerrarse automáticamente cuando pierde el foco.", + "openDefaultSettings": "Controla si la configuración de apertura también abre un editor que muestra todos los valores predeterminados.", + "sideBarLocation": "Controla la ubicación de la barra lateral. Puede mostrarse a la izquierda o a la derecha del área de trabajo.", + "statusBarVisibility": "Controla la visibilidad de la barra de estado en la parte inferior del área de trabajo.", + "activityBarVisibility": "Controla la visibilidad de la barra de actividades en el área de trabajo.", + "closeOnFileDelete": "Controla si los editores que muestran un archivo deben cerrarse automáticamente cuando otro proceso elimina el archivo o le cambia el nombre. Si se deshabilita esta opción y se da alguna de estas circunstancias, se mantiene el editor abierto con modificaciones. Tenga en cuenta que, cuando se eliminan archivos desde la aplicación, siempre se cierra el editor y que los archivos con modificaciones no se cierran nunca para preservar los datos.", + "fontAliasing": "Controla el método de suavizado de fuentes en el área de trabajo.\n- default: suavizado de fuentes en subpíxeles. En la mayoría de las pantallas que no son Retina, esta opción muestra el texto más nítido.\n- antialiased: suaviza las fuentes en píxeles, en lugar de subpíxeles. Puede hacer que las fuentes se vean más claras en general\n- none: deshabilita el suavizado de fuentes. El texto se muestra con bordes nítidos irregulares.", + "workbench.fontAliasing.default": "Suavizado de fuentes en subpíxeles. En la mayoría de las pantallas que no son Retina, esta opción muestra el texto más nítido.", + "workbench.fontAliasing.antialiased": "Suaviza las fuentes en píxeles, en lugar de subpíxeles. Puede hacer que las fuentes se vean más claras en general.", + "workbench.fontAliasing.none": "Deshabilita el suavizado de fuentes. El texto se muestra con bordes nítidos irregulares.", + "swipeToNavigate": "Navegar entre achivos abiertos utlizando la pulsación de tres dedos para deslizar horizontalmante.", + "workbenchConfigurationTitle": "Área de trabajo", + "window.openFilesInNewWindow.on": "Los archivos se abrirán en una nueva ventana", + "window.openFilesInNewWindow.off": "Los archivos se abrirán en la ventana con la carpeta de archivos abierta o en la última ventana activa", + "window.openFilesInNewWindow.default": "Los archivos se abrirán en la ventana con la carpeta de los archivos abierta o en la última ventana activa a menos que se abran mediante el Dock o el Finder (solo macOS)", + "openFilesInNewWindow": "Controla si los archivos deben abrirse en una ventana nueva.\n- default: los archivos se abrirán en la ventana con la carpeta de archivos abierta o en la última ventana activa, a menos que se abran mediante el Dock o desde el Finder (solo macOS)\n- on: los archivos se abrirán en una ventana nueva\n- off: los archivos se abrirán en la ventana con la carpeta de archivos abierta o en la última ventana activa\nTenga en cuenta que aún puede haber casos en los que este parámetro se ignore (por ejemplo, al usar la opción de la línea de comandos -new-window o -reuse-window).", + "window.openFoldersInNewWindow.on": "Las carpetas se abrirán en una nueva ventana", + "window.openFoldersInNewWindow.off": "Las carpetas reemplazarán la ventana activa más reciente", + "window.openFoldersInNewWindow.default": "Las carpetas se abrirán en una nueva ventana a menos que se seleccione una carpeta desde la aplicación (p. ej. mediante el menú Archivo)", + "openFoldersInNewWindow": "Controla si las carpetas deben abrirse en una ventana nueva o reemplazar la última ventana activa.\n- default: las carpetas se abrirán en una ventana nueva, a menos que se seleccione una carpeta desde la aplicación (por ejemplo, desde el menú Archivo)\n- on: las carpetas se abrirán en una ventana nueva\n- off: las carpetas reemplazarán la última ventana activa\nTenga en cuenta que aún puede haber casos en los que este parámetro se ignore (por ejemplo, al usar la opción de la línea de comandos -new-window o -reuse-window).", + "window.reopenFolders.all": "Reabrir todas las ventanas.", + "window.reopenFolders.folders": "Reabrir todas las carpetas. Las áreas de trabajo vacías no se restaurarán.", + "window.reopenFolders.one": "Reabrir la última ventana activa.", + "window.reopenFolders.none": "Nunca reabrir una ventana. Empezar siempre con una vacía.", + "restoreWindows": "Controla cómo se vuelven a abrir las ventanas tras un reinicio. Seleccione \"none\" para comenzar siempre con un área de trabajo vacía, \"one\" para volver a abrir la última ventana en la que trabajó, \"folders\" para volver a abrir todas las ventanas que tenían carpetas abiertas o \"all\" para volver a abrir todas las ventanas de la última sesión.", + "restoreFullscreen": "Controla si una ventana se debe restaurar al modo de pantalla completa si se salió de ella en dicho modo.", + "zoomLevel": "Ajuste el nivel de zoom de la ventana. El tamaño original es 0 y cada incremento (por ejemplo, 1) o disminución (por ejemplo, -1) representa una aplicación de zoom un 20 % más grande o más pequeño. También puede especificar decimales para ajustar el nivel de zoom con una granularidad más precisa.", + "title": "Controla el título de la ventana según el editor activo. Las variables se sustituyen según el contexto:\n${activeEditorShort}: e.g. myFile.txt\n${activeEditorMedium}: e.g. myFolder/myFile.txt\n${activeEditorLong}: por ejemplo, /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: por ejemplo, myFolder\n${folderPath}: por ejemplo, /Users/Development/myFolder\n${rootName}: por ejemplo, myFolder1, myFolder2, myFolder3\n${rootPath}: por ejemplo, /Users/Development/myWorkspace\n${appName}: por ejemplo, VS Code\n${dirty}: un indicador con modificaciones si el editor activo está desfasado\n${separator}: separador condicional (\" - \") que solo se muestra cuando está rodeado de variables con valores", + "window.newWindowDimensions.default": "Abrir las nuevas ventanas en el centro de la pantalla.", + "window.newWindowDimensions.inherit": "Abrir las nuevas ventanas con la misma dimensión que la última activa.", + "window.newWindowDimensions.maximized": "Abrir las nuevas ventanas maximizadas.", + "window.newWindowDimensions.fullscreen": "Abrir las nuevas ventanas en modo de pantalla completa.", + "newWindowDimensions": "Controla las dimensiones de la nueva ventana cuando ya existe alguna ventana abierta. Por defecto, una nueva ventana se abrirá en el centro de la pantalla con dimensiones pequeñas. Cuando se establece como 'heredar', la ventana tomará las dimensiones de la última ventana activa. Cuando se establece a 'maximizar', la ventana se abrirá maximizada, y a pantalla completa si fue configurada como 'pantalla completa'. Tenga en cuenta que esta configuración no afecta a la primera ventana abierta. La primera ventana siempre restaurará el tamaño y posición en la que quedó antes de ser cerrada.", + "closeWhenEmpty": "Controla si, al cerrar el último editor, debe cerrarse también la ventana. Esta configuración se aplica solo a ventanas que no muestran carpetas.", + "window.menuBarVisibility.default": "El menú solo está oculto en modo de pantalla completa.", + "window.menuBarVisibility.visible": "El menú está siempre visible incluso en modo de pantalla completa.", + "window.menuBarVisibility.toggle": "El menú está oculto pero se puede mostrar mediante la tecla Alt.", + "window.menuBarVisibility.hidden": "El menú está siempre oculto.", + "menuBarVisibility": "Controla la visibilidad de la barra de menús. El valor \"alternar\" significa que la barra de menús está oculta y que se mostrará al presionar una sola vez la tecla Alt. La barra de menús estará visible de forma predeterminada, a menos que se use el modo de pantalla completa para la ventana.", + "enableMenuBarMnemonics": "Si se activa, los menús principales se pueden abrir por mediación de los métodos abreviados Alt-tecla. Desactivar los mnemónicos permite vincular estos métodos Alt-tecla a comandos del editor.", + "autoDetectHighContrast": "Si está habilitado, se cambiará automáticamente al tema de contraste alto si Windows utiliza un tema de contraste alto, y al tema oscuro si cambia desde un tema de contraste alto de Windows.", + "titleBarStyle": "Ajuste la apariencia de la barra de título de la ventana. Se debe realizar un reinicio completo para aplicar los cambios.", + "window.nativeTabs": "Habilita las fichas de ventana en macOS Sierra. Note que los cambios requieren que reinicie el equipo y las fichas nativas deshabilitan cualquier estilo personalizado que haya configurado.", + "windowConfigurationTitle": "Ventana", + "zenModeConfigurationTitle": "Modo zen", + "zenMode.fullScreen": "Controla si activar el modo Zen pone también el trabajo en modo de pantalla completa.", + "zenMode.hideTabs": "Controla si la activación del modo zen también oculta las pestañas del área de trabajo.", + "zenMode.hideStatusBar": "Controla si la activación del modo zen oculta también la barra de estado en la parte inferior del área de trabajo.", + "zenMode.hideActivityBar": "Controla si la activación del modo zen oculta también la barra de estado en la parte izquierda del área de trabajo.", + "zenMode.restore": "Controla si una ventana debe restaurarse a modo zen si se cerró en modo zen." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/electron-browser/main.i18n.json b/i18n/esn/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..0ed724a66e --- /dev/null +++ b/i18n/esn/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "No se pudo cargar un archivo necesario. O bien no está conectado a Internet o el servidor al que se había conectado está sin conexión. Actualice el explorador y vuelva a intentarlo.", + "loaderErrorNative": "No se pudo cargar un archivo requerido. Reinicie la aplicación para intentarlo de nuevo. Detalles: {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/esn/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..f69679eca2 --- /dev/null +++ b/i18n/esn/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "Se recomienda no ejecutar Code como 'root'." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/electron-browser/window.i18n.json b/i18n/esn/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..2398b3aead --- /dev/null +++ b/i18n/esn/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "Deshacer", + "redo": "Rehacer", + "cut": "Cortar", + "copy": "Copiar", + "paste": "Pegar", + "selectAll": "Seleccionar todo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/esn/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..7b3fa2ab93 --- /dev/null +++ b/i18n/esn/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "Desarrollador", + "file": "Archivo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/esn/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..3bfa95da9d --- /dev/null +++ b/i18n/esn/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "La ruta de acceso {0} no apunta a un ejecutor de pruebas de extensión." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/esn/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..09083691aa --- /dev/null +++ b/i18n/esn/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "No se pudo analizar {0}: {1}.", + "fileReadFail": "No se puede leer el archivo {0}: {1}.", + "jsonsParseFail": "No se pudo analizar {0} o {1}:{2}.", + "missingNLSKey": "No se encontró un mensaje para la clave {0}." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..33b72b9caa --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "Instalar el comando '{0}' en PATH", + "not available": "Este comando no está disponible", + "successIn": "El comando shell '{0}' se instaló correctamente en PATH.", + "warnEscalation": "Ahora el código solicitará privilegios de administrador con \"osascript\" para instalar el comando shell.", + "ok": "Aceptar", + "cantCreateBinFolder": "No se puede crear \"/usr/local/bin\".", + "cancel2": "Cancelar", + "aborted": "Anulado", + "uninstall": "Desinstalar el comando '{0}' de PATH", + "successFrom": "El comando shell '{0}' se desinstaló correctamente de PATH.", + "shellCommand": "Comando shell" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..c3025d309f --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "Se cambiará ahora el valor de configuración \"editor.accessibilitySupport\" a \"activado\".", + "openingDocs": "Se abrirá ahora la página de documentación de accesibilidad de VS Code.", + "introMsg": "Gracias por probar las opciones de accesibilidad de VS Code.", + "status": "Estado:", + "changeConfigToOnMac": "Para configurar el editor de forma que esté optimizado de permanentemente para su uso con un lector de pantalla, presione ahora Comando+E.", + "changeConfigToOnWinLinux": "Para configurar el editor de forma que esté optimizado permanentemente para su uso con un lector de pantalla, presione ahora Control+E.", + "auto_unknown": "El editor está configurado para usar API de plataforma para detectar cuándo está conectado un lector de pantalla, pero el entorno actual de tiempo de ejecución no admite esta característica.", + "auto_on": "El editor ha detectado automáticamente un lector de pantalla conectado.", + "auto_off": "El editor está configurado para detectar automáticamente cuándo está conectado un lector de pantalla, lo que no es el caso en este momento.", + "configuredOn": "El editor está configurado para optimizarse permanentemente para su uso con un lector de pantalla; para cambiar este comportamiento, edite el valor de configuración \"editor.accessibilitySupport\".", + "configuredOff": "El editor está configurado de forma que no esté nunca optimizado para su uso con un lector de pantalla.", + "tabFocusModeOnMsg": "Al presionar TAB en el editor actual, el foco se mueve al siguiente elemento activable. Presione {0} para activar o desactivar este comportamiento.", + "tabFocusModeOnMsgNoKb": "Al presionar TAB en el editor actual, el foco se mueve al siguiente elemento activable. El comando {0} no se puede desencadenar actualmente mediante un enlace de teclado.", + "tabFocusModeOffMsg": "Al presionar TAB en el editor actual, se insertará el carácter de tabulación. Presione {0} para activar o desactivar este comportamiento.", + "tabFocusModeOffMsgNoKb": "Al presionar TAB en el editor actual, se insertará el carácter de tabulación. El comando {0} no se puede desencadenar actualmente mediante un enlace de teclado.", + "openDocMac": "Presione Comando+H ahora para abrir una ventana de explorador con más información de VS Code relacionada con la accesibilidad.", + "openDocWinLinux": "Presione Control+H ahora para abrir una ventana de explorador con más información de VS Code relacionada con la accesibilidad.", + "outroMsg": "Para descartar esta información sobre herramientas y volver al editor, presione Esc o Mayús+Escape.", + "ShowAccessibilityHelpAction": "Mostrar ayuda de accesibilidad" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..3fc0a87da6 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "Desarrollador: inspeccionar asignaciones de teclas " +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..a32df84414 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Desarrollador: Inspeccionar ámbitos de TM", + "inspectTMScopesWidget.loading": "Cargando..." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..aaa91a2327 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Errores al analizar {0}: {1}", + "schema.openBracket": "Secuencia de cadena o corchete de apertura.", + "schema.closeBracket": "Secuencia de cadena o corchete de cierre.", + "schema.comments": "Define los símbolos de comentario", + "schema.blockComments": "Define cómo se marcan los comentarios de bloque.", + "schema.blockComment.begin": "Secuencia de caracteres que inicia un comentario de bloque.", + "schema.blockComment.end": "Secuencia de caracteres que finaliza un comentario de bloque.", + "schema.lineComment": "Secuencia de caracteres que inicia un comentario de línea.", + "schema.brackets": "Define los corchetes que aumentan o reducen la sangría.", + "schema.autoClosingPairs": "Define el par de corchetes. Cuando se escribe un corchete de apertura, se inserta automáticamente el corchete de cierre.", + "schema.autoClosingPairs.notIn": "Define una lista de ámbitos donde los pares automáticos están deshabilitados.", + "schema.surroundingPairs": "Define los pares de corchetes que se pueden usar para encerrar una cadena seleccionada.", + "schema.wordPattern": " La definición de la palabra en el idioma.", + "schema.wordPattern.pattern": "El patrón de expresión regular utilizado para localizar palabras.", + "schema.wordPattern.flags": "Los flags de expresión regular utilizados para localizar palabras.", + "schema.wordPattern.flags.errorMessage": "Debe coincidir con el patrón `/^([gimuy]+)$/`.", + "schema.indentationRules": "Configuración de sangría del idioma.", + "schema.indentationRules.increaseIndentPattern": "Si una línea coincide con este patrón, todas las líneas después de ella deben sangrarse una vez (hasta que otra regla coincida). ", + "schema.indentationRules.increaseIndentPattern.pattern": "El patrón de RegExp para increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.flags": "Las marcas de RegExp para increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.errorMessage": "Debe coincidir con el patrón `/^([gimuy]+)$/`.", + "schema.indentationRules.decreaseIndentPattern": "Si una línea coincide con este patrón, se deben quitar la sangría de todas las líneas después de ella una vez (hasta que otra regla coincida).", + "schema.indentationRules.decreaseIndentPattern.pattern": "El patrón de RegExp para decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.flags": "Las marcas de RegExp para decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "Debe coincidir con el patrón `/^([gimuy]+)$/`.", + "schema.indentationRules.indentNextLinePattern": "Si una línea coincide con este patrón **solo la línea siguiente** después de ella se debe sangrar una vez.", + "schema.indentationRules.indentNextLinePattern.pattern": "El patrón de RegExp para indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.flags": "El patrón de RegExp para indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.errorMessage": "Debe coincidir con el patrón `/^([gimuy]+)$/`.", + "schema.indentationRules.unIndentedLinePattern": "Si una línea coincide con este patrón, su sangría no se debe cambiar y no se debe evaluar utilizando las otras reglas.", + "schema.indentationRules.unIndentedLinePattern.pattern": "El patrón de RegExp para unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.flags": "Las marcas de RegExp para unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "Debe coincidir con el patrón `/^([gimuy]+)$/`." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..a32df84414 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Desarrollador: Inspeccionar ámbitos de TM", + "inspectTMScopesWidget.loading": "Cargando..." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..4c1e33966d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "Ver: Alternar minimapa" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..770bd4b39e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Alternar modificador multicursor" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..9f89ea814f --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "Ver: Alternar caracteres de control" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..3705333f50 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "Ver: Alternar representación de espacio en blanco" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..2a5eb735ad --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "Ver: Alternar ajuste de línea", + "wordWrap.notInDiffEditor": "No se puede alternar ajuste de línea en un editor de diferencias.", + "unwrapMinified": "Deshabilitar ajuste para este archivo", + "wrapMinified": "Habilitar ajuste para este archivo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..0bf3763906 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "Aceptar", + "wordWrapMigration.dontShowAgain": "No volver a mostrar", + "wordWrapMigration.openSettings": "Abrir configuración", + "wordWrapMigration.prompt": "El valor de configuración \"editor.wrappingColumn\" se ha dejado de usar en favor de \"editor.wordWrap\"." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..c5eb442f14 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "Interrumpir cuando la expresión se evalúa como true. Presione \"ENTRAR\" para aceptar o \"Esc\" para cancelar.", + "breakpointWidgetAriaLabel": "El programa solo se detendrá aquí si esta condición es true. Presione ENTRAR para aceptar o Esc para cancelar.", + "breakpointWidgetHitCountPlaceholder": "Interrumpir cuando se alcance el número de llamadas. Presione \"ENTRAR\" para aceptar o \"Esc\" para cancelar.", + "breakpointWidgetHitCountAriaLabel": "El programa solo se detendrá aquí si se alcanza el número de llamadas. Presione ENTRAR para aceptar o Esc para cancelar.", + "expression": "Expresión", + "hitCount": "Número de llamadas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..6e0634705d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "No hay configuraciones", + "addConfigTo": "Agregar configuración ({0})...", + "addConfiguration": "Agregar configuración..." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..8fb008c2ff --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "Abrir {0}", + "launchJsonNeedsConfigurtion": "Configurar o reparar 'launch.json'", + "noFolderDebugConfig": "Abra una carpeta para trabajar con la configuración avanzada de depuración.", + "startDebug": "Iniciar depuración", + "startWithoutDebugging": "Iniciar sin depurar", + "selectAndStartDebugging": "Seleccionar e iniciar la depuración", + "restartDebug": "Reiniciar", + "reconnectDebug": "Volver a conectar", + "stepOverDebug": "Depurar paso a paso por procedimientos", + "stepIntoDebug": "Depurar paso a paso por instrucciones", + "stepOutDebug": "Salir de la depuración", + "stopDebug": "Detener", + "disconnectDebug": "Desconectar", + "continueDebug": "Continuar", + "pauseDebug": "Pausar", + "restartFrame": "Reiniciar marco", + "removeBreakpoint": "Quitar punto de interrupción", + "removeAllBreakpoints": "Quitar todos los puntos de interrupción", + "enableBreakpoint": "Habilitar punto de interrupción", + "disableBreakpoint": "Deshabilitar punto de interrupción", + "enableAllBreakpoints": "Habilitar todos los puntos de interrupción", + "disableAllBreakpoints": "Deshabilitar todos los puntos de interrupción", + "activateBreakpoints": "Activar puntos de interrupción", + "deactivateBreakpoints": "Desactivar puntos de interrupción", + "reapplyAllBreakpoints": "Volver a aplicar todos los puntos de interrupción", + "addFunctionBreakpoint": "Agregar punto de interrupción de función", + "renameFunctionBreakpoint": "Cambiar nombre de punto de interrupción de función", + "addConditionalBreakpoint": "Agregar punto de interrupción condicional...", + "editConditionalBreakpoint": "Editar punto de interrupción...", + "setValue": "Establecer valor", + "addWatchExpression": "Agregar expresión", + "editWatchExpression": "Editar expresión", + "addToWatchExpressions": "Agregar a inspección", + "removeWatchExpression": "Quitar expresión", + "removeAllWatchExpressions": "Quitar todas las expresiones", + "clearRepl": "Borrar consola", + "debugConsoleAction": "Consola de depuración", + "unreadOutput": "Nueva salida en la consola de depuración", + "debugFocusConsole": "Enfocar consola de depuración", + "focusProcess": "Proceso Foco", + "stepBackDebug": "Retroceder", + "reverseContinue": "Invertir" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..1c03b3514a --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "Color de fondo de la barra de herramientas de depuración" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..ba0b7aac87 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "No se puede resolver el recurso sin una sesión de depuración" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..c5997a9b56 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "Depuración: Alternar punto de interrupción", + "columnBreakpointAction": "Depurar: punto de interrupción de columna", + "columnBreakpoint": "Agregar punto de interrupción de columna", + "conditionalBreakpointEditorAction": "Depuración: agregar punto de interrupción condicional...", + "runToCursor": "Ejecutar hasta el cursor", + "debugEvaluate": "Depuración: Evaluar", + "debugAddToWatch": "Depuración: Agregar a inspección", + "showDebugHover": "Depuración: Mostrar al mantener el puntero" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..9c78838a1e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "Punto de interrupción deshabilitado", + "breakpointUnverifieddHover": "Punto de interrupción no comprobado", + "breakpointDirtydHover": "Punto de interrupción no comprobado. El archivo se ha modificado, reinicie la sesión de depuración.", + "breakpointUnsupported": "Este tipo de depuración no es compatible con los puntos de interrupción condicionales." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..ea72f703c5 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, depurar", + "debugAriaLabel": "Escriba un nombre de una configuración de inicio para ejecutar.", + "noConfigurationsMatching": "No hay ninguna configuración de depuración coincidente", + "noConfigurationsFound": "No se encontró ninguna configuración de inicio. Cree un archivo \"launch.json\"." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..97f0154dc8 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "Color de borde del widget de excepciones.", + "debugExceptionWidgetBackground": "Color de fondo del widget de excepciones.", + "exceptionThrownWithId": "Se produjo una excepción: {0}", + "exceptionThrown": "Se produjo una excepción." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..42ded7a296 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "Clic para seguir (Cmd + clic se abre en el lateral)", + "fileLink": "Clic para seguir (Ctrl + clic se abre en el lateral)" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..1a1159dee0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "Controla el comportamiento de la consola de depuración interna." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..22e360ddc0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "no disponible", + "startDebugFirst": "Inicie una sesión de depuración para evaluar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..e39ca3c3e8 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "Origen desconocido" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..6360c5546e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "Mostrar depuración", + "toggleDebugPanel": "Consola de depuración", + "debug": "Depurar", + "debugPanel": "Consola de depuración", + "variables": "Variables", + "watch": "Inspección", + "callStack": "Pila de llamadas", + "breakpoints": "Puntos de interrupción", + "view": "Ver", + "debugCategory": "Depurar", + "debugCommands": "Configuración de depuración", + "debugConfigurationTitle": "Depurar", + "allowBreakpointsEverywhere": "Permite establecer un punto de interrupción en cualquier archivo.", + "openExplorerOnEnd": "Abrir automáticamente la vista del explorador al final de una sesión de depuración", + "inlineValues": "Mostrar valores de variable en línea en el editor durante la depuración", + "hideActionBar": "Controla si debe ocultarse la barra flotante de acciones de depuración", + "launch": "Configuración de lanzamiento para depuración global. Debe utilizarse como una alternativa a “launch.json” en espacios de trabajo compartidos." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..dca942a20e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "Abra una carpeta para trabajar con la configuración avanzada de depuración." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..1939abb876 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "Aporta adaptadores de depuración.", + "vscode.extension.contributes.debuggers.type": "Identificador único de este adaptador de depuración.", + "vscode.extension.contributes.debuggers.label": "Nombre para mostrar del adaptador de depuración.", + "vscode.extension.contributes.debuggers.program": "Ruta de acceso al programa de adaptadores de depuración, que puede ser absoluta o relativa respecto a la carpeta de extensión.", + "vscode.extension.contributes.debuggers.args": "Argumentos opcionales que se pasarán al adaptador.", + "vscode.extension.contributes.debuggers.runtime": "Entorno de ejecución opcional en caso de que el atributo del programa no sea un ejecutable pero requiera un entorno de ejecución.", + "vscode.extension.contributes.debuggers.runtimeArgs": "Argumentos de entorno de ejecución opcionales.", + "vscode.extension.contributes.debuggers.variables": "Asignación de variables interactivas (por ejemplo, ${action.pickProcess}) en `launch.json` a un comando.", + "vscode.extension.contributes.debuggers.initialConfigurations": "Configuraciones para generar el archivo \"launch.json\" inicial.", + "vscode.extension.contributes.debuggers.languages": "Lista de lenguajes para los que la extensión de depuración podría considerarse el \"depurador predeterminado\".", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Si se especifica, VS Code llamará a este comando para determinar la ruta de acceso ejecutable del adaptador de depuración y los argumentos que se deben pasar.", + "vscode.extension.contributes.debuggers.startSessionCommand": "Si se especifica, VS Code llamará a este comando para las acciones de \"depuración\" o \"ejecución\" destinadas para esta extensión.", + "vscode.extension.contributes.debuggers.configurationSnippets": "Fragmentos de código para agregar nuevas configuraciones a \"launch.json\".", + "vscode.extension.contributes.debuggers.configurationAttributes": "Configuraciones de esquema JSON para validar \"launch.json\".", + "vscode.extension.contributes.debuggers.windows": "Configuración específica de Windows.", + "vscode.extension.contributes.debuggers.windows.runtime": "Entorno de ejecución que se usa para Windows.", + "vscode.extension.contributes.debuggers.osx": "Configuración específica de OS X.", + "vscode.extension.contributes.debuggers.osx.runtime": "Entorno de ejecución que se usa para OSX.", + "vscode.extension.contributes.debuggers.linux": "Configuración específica de Linux.", + "vscode.extension.contributes.debuggers.linux.runtime": "Entorno de ejecución que se usa para Linux.", + "vscode.extension.contributes.breakpoints": "Aporta puntos de interrupción.", + "vscode.extension.contributes.breakpoints.language": "Permite puntos de interrupción para este lenguaje.", + "app.launch.json.title": "Iniciar", + "app.launch.json.version": "Versión de este formato de archivo.", + "app.launch.json.configurations": "Lista de configuraciones. Agregue configuraciones nuevas o edite las ya existentes con IntelliSense.", + "app.launch.json.compounds": "Lista de elementos compuestos. Cada elemento compuesto hace referencia a varias configuraciones, que se iniciarán conjuntamente.", + "app.launch.json.compound.name": "Nombre del elemento compuesto. Aparece en el menú desplegable de la configuración de inicio.", + "app.launch.json.compounds.configurations": "Nombres de las configuraciones que se iniciarán como parte de este elemento compuesto.", + "debugNoType": "El valor \"type\" del adaptador de depuración no se puede omitir y debe ser de tipo \"string\".", + "selectDebug": "Seleccionar entorno", + "DebugConfig.failed": "No se puede crear el archivo \"launch.json\" dentro de la carpeta \".vscode\" ({0})." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..2e797a1810 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "Quitar puntos de interrupción", + "removeBreakpointOnColumn": "Quitar punto de interrupción en la columna {0}", + "removeLineBreakpoint": "Quitar punto de interrupción de línea", + "editBreakpoints": "Editar puntos de interrupción", + "editBreakpointOnColumn": "Editar punto de interrupción en la columna {0}", + "editLineBrekapoint": "Editar punto de interrupción de línea", + "enableDisableBreakpoints": "Habilitar o deshabilitar puntos de interrupción", + "disableColumnBreakpoint": "Deshabilitar punto de interrupción en la columna {0}", + "disableBreakpointOnLine": "Deshabilitar punto de interrupción de línea", + "enableBreakpoints": "Habilitar punto de interrupción en la columna {0}", + "enableBreakpointOnLine": "Habilitar punto de interrupción de línea", + "addBreakpoint": "Agregar punto de interrupción", + "addConfiguration": "Agregar configuración..." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..152fb3492e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "Mantener puntero durante depuración" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..99701ea6f5 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "Solo se muestran valores primitivos para este objeto.", + "debuggingPaused": "La depuración se ha pausado. Motivo: {0}, {1} {2}", + "debuggingStarted": "La depuración se ha iniciado.", + "debuggingStopped": "La depuración se ha detenido.", + "breakpointAdded": "Punto de interrupción agregado, línea {0}, archivo {1}", + "breakpointRemoved": "Punto de interrupción quitado, línea {0}, archivo {1}", + "compoundMustHaveConfigurations": "El compuesto debe tener configurado el atributo \"configurations\" a fin de iniciar varias configuraciones.", + "configMissing": "La configuración \"{0}\" falta en \"launch.json\".", + "debugTypeNotSupported": "El tipo de depuración '{0}' configurado no es compatible.", + "debugTypeMissing": "Falta la propiedad \"type\" en la configuración de inicio seleccionada.", + "preLaunchTaskErrors": "Errores de compilación durante la tarea preLaunchTask '{0}'.", + "preLaunchTaskError": "Error de compilación durante la tarea preLaunchTask '{0}'.", + "preLaunchTaskExitCode": "La tarea preLaunchTask '{0}' finalizó con el código de salida {1}.", + "debugAnyway": "Depurar de todos modos", + "noFolderWorkspaceDebugError": "El archivo activo no se puede depurar. Compruebe que se ha guardado en el disco y que tiene una extensión de depuración instalada para ese tipo de archivo.", + "NewLaunchConfig": "Defina los valores del archivo de configuración de inicio para la aplicación. {0}", + "DebugTaskNotFound": "No se encontró el elemento preLaunchTask '{0}'." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..540a8fde0a --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "Proceso", + "paused": "En pausa", + "running": "En ejecución", + "thread": "Subproceso", + "pausedOn": "En pausa en {0}", + "loadMoreStackFrames": "Cargar más marcos de pila", + "threadAriaLabel": "Subproceso {0}, pila de llamadas, depuración", + "stackFrameAriaLabel": "Marco de pila {0} línea {1} {2}, pila de llamadas, depuración", + "variableValueAriaLabel": "Escribir un nuevo valor de variable", + "variableScopeAriaLabel": "Ámbito {0}, variables, depuración", + "variableAriaLabel": "{0} valor {1}, variables, depuración", + "watchExpressionPlaceholder": "Expresión para inspeccionar", + "watchExpressionInputAriaLabel": "Escribir expresión de inspección", + "watchExpressionAriaLabel": "{0} valor {1}, inspección, depuración", + "watchVariableAriaLabel": "{0} valor {1}, inspección, depuración", + "functionBreakpointPlaceholder": "Función donde interrumpir", + "functionBreakPointInputAriaLabel": "Escribir punto de interrupción de función", + "functionBreakpointsNotSupported": "Este tipo de depuración no admite puntos de interrupción en funciones", + "breakpointAriaLabel": "Línea de punto de interrupción {0} {1}, puntos de interrupción, depuración", + "functionBreakpointAriaLabel": "Punto de interrupción de función {0}, puntos de interrupción, depuración", + "exceptionBreakpointAriaLabel": "Punto de interrupción de excepción {0}, puntos de interrupción, depuración" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..8d6cc92762 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "Sección de variables", + "variablesAriaTreeLabel": "Variables de depuración", + "expressionsSection": "Sección de expresiones", + "watchAriaTreeLabel": "Expresiones de inspección de la depuración", + "callstackSection": "Sección de la pila de llamadas", + "debugStopped": "En pausa en {0}", + "callStackAriaLabel": "Pila de llamadas de la depuración", + "breakpointsSection": "Sección de puntos de interrupción", + "breakpointsAriaTreeLabel": "Puntos de interrupción de la depuración" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..47d9c6ae10 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "Copiar valor", + "copy": "Copiar", + "copyAll": "Copiar todo", + "copyStackTrace": "Copiar pila de llamadas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..0e8741e51d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "Más información", + "unableToLaunchDebugAdapter": "No se puede iniciar el adaptador de depuración desde '{0}'.", + "unableToLaunchDebugAdapterNoArgs": "No se puede iniciar el adaptador de depuración.", + "stoppingDebugAdapter": "{0}. Deteniendo el adaptador de depuración.", + "debugAdapterCrash": "El proceso de adaptación del depurador finalizó inesperadamente" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..e98955a199 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "Panel de read–eval–print loop", + "actions.repl.historyPrevious": "Historial anterior", + "actions.repl.historyNext": "Historial siguiente", + "actions.repl.acceptInput": "REPL - Aceptar entrada", + "actions.repl.copyAll": "Depuración: Consola Copiar Todo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..ce283d2e90 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "El estado del objeto se captura desde la primera evaluación", + "replVariableAriaLabel": "La variable {0} tiene el valor {1}, read–eval–print loop, depuración", + "replExpressionAriaLabel": "La expresión {0} tiene el valor {1}, read–eval–print loop, depuración", + "replValueOutputAriaLabel": "{0}, read–eval–print loop, depuración", + "replKeyValueOutputAriaLabel": "La variable de salida {0} tiene el valor {1}, read–eval–print loop, depuración" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..6e783b3359 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "Color de fondo de la barra de estado cuando se está depurando un programa. La barra de estado se muestra en la parte inferior de la ventana", + "statusBarDebuggingForeground": "Color de primer plano de la barra de estado cuando se está depurando un programa. La barra de estado se muestra en la parte inferior de la ventana", + "statusBarDebuggingBorder": "Color de borde de la barra de estado que separa la barra lateral y el editor cuando se está depurando un programa. La barra de estado se muestra en la parte inferior de la ventana." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..6aae62bfeb --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "depurado", + "debug.terminal.not.available.error": "Terminal integrado no disponible" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..54e78e73e7 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "El ejecutable del adaptador de depuración \"{0}\" no existe.", + "debugAdapterCannotDetermineExecutable": "No se puede determinar el ejecutable para el adaptador de depuración \"{0}\".", + "launch.config.comment1": "Utilizar IntelliSense para aprender acerca de los posibles atributos.", + "launch.config.comment2": "Mantenga el puntero para ver las descripciones de los existentes atributos ", + "launch.config.comment3": "Para más información, visite: {0}", + "debugType": "Tipo de configuración.", + "debugTypeNotRecognised": "Este tipo de depuración no se reconoce. Compruebe que tiene instalada la correspondiente extensión de depuración y que está habilitada.", + "node2NotSupported": "\"node2\" ya no se admite; use \"node\" en su lugar y establezca el atributo \"protocol\" en \"inspector\".", + "debugName": "Nombre de la configuración. Aparece en el menú desplegable de la configuración de inicio.", + "debugRequest": "Tipo de solicitud de la configuración. Puede ser \"launch\" o \"attach\".", + "debugServer": "Solo para el desarrollo de extensiones de depuración: si se especifica un puerto, VS Code intenta conectarse a un adaptador de depuración que se ejecuta en modo servidor", + "debugPrelaunchTask": "Tarea que se va a ejecutar antes de iniciarse la sesión de depuración.", + "debugWindowsConfiguration": "Atributos de configuración de inicio específicos de Windows.", + "debugOSXConfiguration": "Atributos de configuración de inicio específicos de OS X.", + "debugLinuxConfiguration": "Atributos de configuración de inicio específicos de Linux.", + "deprecatedVariables": "\"env.\", \"config.\" y \"command.\" están en desuso, utilice en su lugar \"env:\", \"config:\" y \"command:\"." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..05b24286e7 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "Mostrar los comandos de Emmet" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..75b63cb27d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: Equilibrio (entrante)", + "balanceOutward": "Emmet: Equilibrio (saliente)" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..9eb53c85ca --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: Ir al punto de edición anterior", + "nextEditPoint": "Emmet: Ir al punto de edición siguiente" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..9b44d96ddc --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: Evaluar expresión matemática" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..2cbc9a0682 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: Expandir abreviatura" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..e5e32df516 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: Aumentar por 0.1", + "incrementNumberByOne": "Emmet: Aumentar por 1", + "incrementNumberByTen": "Emmet: Aumentar por 10", + "decrementNumberByOneTenth": "Emmet: Disminuir por 0.1", + "decrementNumberByOne": "Emmet: Disminuir por 1", + "decrementNumberByTen": "Emmet: Disminuir por 10" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..90e4fd7474 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: Ir al par coincidente" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..669b9c6b71 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: Combinar líneas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..be3486c4e3 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: Reflejar valor CSS" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..ec8ece9272 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: Quitar etiqueta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..3e77b3d28d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: Seleccionar elemento anterior", + "selectNextItem": "Emmet: Seleccionar elemento siguiente" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..b4278f85d0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: Dividir/combinar etiqueta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..44bb8009dd --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: Alternar comentario" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..cfa7e1d11d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: Actualizar tamaño de la imagen" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..e803ad54c6 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: Actualizar etiqueta", + "enterTag": "Ingresar etiqueta", + "tag": "Etiqueta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..2f3211c732 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: Encapsular con abreviatura", + "enterAbbreviation": "Ingresar abreviatura", + "abbreviation": "Abreviatura" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..8f37271b24 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Cuando se habilita, se expanden las abreviaciones Emmet al presionar la tecla TAB. No disponible cuando emmet.useNewemmet se establece en true.", + "emmetPreferences": "Preferencias usadas para modificar el comportamiento de algunas acciones y resoluciones de Emmet. No aplicable cuando emmet.useNewemmet se establece en true.", + "emmetSyntaxProfiles": "Defina el perfil de la sintaxis especificada o use su propio perfil con reglas específicas.", + "emmetExclude": "Matriz de lenguajes donde no deben expandirse la abreviación Emmet.", + "emmetExtensionsPath": "Ruta de acceso a una carpeta que contiene perfiles de Emmet, fragmentos de código y preferencias. Solo se respetan los perfiles de la ruta de acceso de extensiones cuando emmet.useNewEmmet se establece en true.", + "useNewEmmet": "Pruebe los nuevos módulos de Emmet (que finalmente sustituirán a la biblioteca anterior de Emmet) para conocer todas las características de Emmet." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json new file mode 100644 index 0000000000..75b63cb27d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: Equilibrio (entrante)", + "balanceOutward": "Emmet: Equilibrio (saliente)" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json new file mode 100644 index 0000000000..e7ac049baa --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: Punto de edición anterior", + "nextEditPoint": "Emmet: Punto de edición siguiente" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..9b44d96ddc --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: Evaluar expresión matemática" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..2cbc9a0682 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: Expandir abreviatura" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..e5e32df516 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: Aumentar por 0.1", + "incrementNumberByOne": "Emmet: Aumentar por 1", + "incrementNumberByTen": "Emmet: Aumentar por 10", + "decrementNumberByOneTenth": "Emmet: Disminuir por 0.1", + "decrementNumberByOne": "Emmet: Disminuir por 1", + "decrementNumberByTen": "Emmet: Disminuir por 10" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..90e4fd7474 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: Ir al par coincidente" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..669b9c6b71 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: Combinar líneas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..be3486c4e3 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: Reflejar valor CSS" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json new file mode 100644 index 0000000000..ec8ece9272 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: Quitar etiqueta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json new file mode 100644 index 0000000000..3e77b3d28d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: Seleccionar elemento anterior", + "selectNextItem": "Emmet: Seleccionar elemento siguiente" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..b4278f85d0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: Dividir/combinar etiqueta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..44bb8009dd --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: Alternar comentario" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..cfa7e1d11d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: Actualizar tamaño de la imagen" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json new file mode 100644 index 0000000000..e803ad54c6 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: Actualizar etiqueta", + "enterTag": "Ingresar etiqueta", + "tag": "Etiqueta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..2f3211c732 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: Encapsular con abreviatura", + "enterAbbreviation": "Ingresar abreviatura", + "abbreviation": "Abreviatura" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json new file mode 100644 index 0000000000..5c6d6e0c3e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Cuando se habilita, se expande la abreviación Emmet al presionar la tecla TAB.", + "emmetPreferences": "Preferencias usadas para modificar el comportamiento de algunas acciones y resoluciones de Emmet.", + "emmetSyntaxProfiles": "Defina el perfil de la sintaxis especificada o use su propio perfil con reglas específicas.", + "emmetExclude": "Matriz de lenguajes donde no deben expandirse la abreviación Emmet.", + "emmetExtensionsPath": "Ruta de acceso a una carpeta que contiene perfiles de Emmet, fragmentos de código y preferencias" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..d4ffa0db86 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Terminal externo", + "explorer.openInTerminalKind": "Personaliza el tipo de terminal para iniciar.", + "terminal.external.windowsExec": "Personaliza qué terminal debe ejecutarse en Windows.", + "terminal.external.osxExec": "Personaliza qué aplicación terminal se ejecutará en OS X.", + "terminal.external.linuxExec": "Personaliza qué terminal debe ejecutarse en Linux.", + "globalConsoleActionWin": "Abrir nuevo símbolo del sistema", + "globalConsoleActionMacLinux": "Abrir nuevo terminal", + "scopedConsoleActionWin": "Abrir en símbolo del sistema", + "scopedConsoleActionMacLinux": "Abrir en terminal", + "openFolderInIntegratedTerminal": "Abrir en terminal" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..cb0e11b487 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Terminal externo", + "terminal.external.windowsExec": "Personaliza qué terminal debe ejecutarse en Windows.", + "terminal.external.osxExec": "Personaliza qué aplicación terminal se ejecutará en OS X.", + "terminal.external.linuxExec": "Personaliza qué terminal debe ejecutarse en Linux.", + "globalConsoleActionWin": "Abrir nuevo símbolo del sistema", + "globalConsoleActionMacLinux": "Abrir nuevo terminal", + "scopedConsoleActionWin": "Abrir en símbolo del sistema", + "scopedConsoleActionMacLinux": "Abrir en terminal" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/esn/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..1435fa1965 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "Consola de VS Code", + "mac.terminal.script.failed": "No se pudo ejecutar el script '{0}'. Código de salida: {1}.", + "mac.terminal.type.not.supported": "No se admite '{0}'", + "press.any.key": "Presione cualquier tecla para continuar...", + "linux.term.failed": "Error de '{0}' con el código de salida {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..99fb38900e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "Aporta una vista personalizada", + "vscode.extension.contributes.view.id": "Identificador único usado para identificar la vista creada a través de vscode.workspace.createTreeView", + "vscode.extension.contributes.view.label": "Cadena en lenguaje natural usada para representar la vista.", + "vscode.extension.contributes.view.icon": "Ruta de acceso al icono de la vista", + "vscode.extension.contributes.views": "Aporta vistas personalizada", + "showViewlet": "Mostrar {0}", + "view": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..fcab0084d0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "Actualizar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..7b431d63f2 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "No hay registrado ningún TreeExplorerNodeProvider con el identificador {providerId}." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/esn/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..855dd7f7e6 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "Sección de Tree Explorer" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..09ae27ac68 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "Error", + "Unknown Dependency": "Dependencia desconocida:" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..e494d8b83d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "Nombre de la extensión", + "extension id": "Identificador de la extensión", + "publisher": "Nombre del editor", + "install count": "Número de instalaciones", + "rating": "Clasificación", + "license": "Licencia", + "details": "Detalles", + "contributions": "Contribuciones", + "changelog": "Registro de cambios", + "dependencies": "Dependencias", + "noReadme": "No hay ningún archivo LÉAME disponible.", + "noChangelog": "No hay ningún objeto CHANGELOG disponible.", + "noContributions": "No hay contribuciones.", + "noDependencies": "No hay dependencias.", + "settings": "Configuración ({0})", + "setting name": "Nombre", + "description": "Descripción", + "default": "Predeterminado", + "debuggers": "Depuradores ({0})", + "debugger name": "Nombre", + "debugger type": "Tipo", + "views": "Vistas ({0})", + "view id": "Id.", + "view name": "Nombre", + "view location": "Donde", + "themes": "Temas ({0})", + "JSON Validation": "Validación JSON ({0})", + "commands": "Comandos ({0})", + "command name": "Nombre", + "keyboard shortcuts": "Métodos abreviados de teclado", + "menuContexts": "Contextos de menú", + "languages": "Lenguajes ({0})", + "language id": "Id.", + "language name": "Nombre", + "file extensions": "Extensiones de archivo", + "grammar": "Gramática", + "snippets": "Fragmentos" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..0e9cbae9ba --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "Instalar", + "installing": "Instalando", + "uninstallAction": "Desinstalación", + "Uninstalling": "Desinstalando", + "updateAction": "Actualizar", + "updateTo": "Actualizar a {0}", + "enableForWorkspaceAction.label": "Habilitar (área de trabajo)", + "enableAlwaysAction.label": "Habilitar (siempre)", + "disableForWorkspaceAction.label": "Deshabilitar (área de trabajo)", + "disableAlwaysAction.label": "Deshabilitar (siempre)", + "ManageExtensionAction.uninstallingTooltip": "Desinstalando", + "enableForWorkspaceAction": "Área de trabajo", + "enableGloballyAction": "Siempre", + "enableAction": "Habilitar", + "disableForWorkspaceAction": "Área de trabajo", + "disableGloballyAction": "Siempre", + "disableAction": "Deshabilitar", + "checkForUpdates": "Buscar actualizaciones", + "enableAutoUpdate": "Habilitar extensiones de actualización automática", + "disableAutoUpdate": "Deshabilitar extensiones de actualización automática", + "updateAll": "Actualizar todas las extensiones", + "reloadAction": "Recargar", + "postUpdateTooltip": "Recargar para actualizar", + "postUpdateMessage": "¿Quiere recargar esta ventana para activar la extensión actualizada '{0}'?", + "postEnableTooltip": "Recargar para activar", + "postEnableMessage": "¿Quiere recargar esta ventana para activar la extensión '{0}'?", + "postDisableTooltip": "Recargar para desactivar", + "postDisableMessage": "¿Quiere recargar esta ventana para desactivar la extensión '{0}'?", + "postUninstallTooltip": "Recargar para desactivar", + "postUninstallMessage": "¿Quiere recargar esta ventana para desactivar la extensión desinstalada '{0}'?", + "reload": "&&Volver a cargar Window", + "toggleExtensionsViewlet": "Mostrar extensiones", + "installExtensions": "Instalar extensiones", + "showEnabledExtensions": "Mostrar extensiones habilitadas", + "showInstalledExtensions": "Mostrar extensiones instaladas", + "showDisabledExtensions": "Mostrar extensiones deshabilitadas", + "clearExtensionsInput": "Borrar entrada de extensiones", + "showOutdatedExtensions": "Mostrar extensiones obsoletas", + "showPopularExtensions": "Mostrar extensiones conocidas", + "showRecommendedExtensions": "Mostrar extensiones recomendadas", + "showWorkspaceRecommendedExtensions": "Mostrar extensiones recomendadas del área de trabajo", + "showRecommendedKeymapExtensions": "Mostrar asignaciones de teclado recomendadas", + "showRecommendedKeymapExtensionsShort": "Asignaciones de teclado", + "showLanguageExtensions": "Mostrar extensiones del lenguaje", + "showLanguageExtensionsShort": "Extensiones del lenguaje", + "showAzureExtensions": "Mostrar extensiones de Azure", + "showAzureExtensionsShort": "Extensiones de Azure", + "configureWorkspaceRecommendedExtensions": "Configurar extensiones recomendadas (área de trabajo)", + "ConfigureWorkspaceRecommendations.noWorkspace": "Las recomendaciones solo están disponibles en una carpeta de área de trabajo.", + "OpenExtensionsFile.failed": "No se puede crear el archivo \"extensions.json\" dentro de la carpeta \".vscode\" ({0}).", + "builtin": "Integrada", + "disableAll": "Deshabilitar todas las extensiones instaladas", + "disableAllWorkspace": "Deshabilitar todas las extensiones instaladas para esta área de trabajo", + "enableAll": "Habilitar todas las extensiones instaladas", + "enableAllWorkspace": "Habilitar todas las extensiones instaladas para esta área de trabajo", + "extensionButtonProminentBackground": "Color de fondo del botón para la extensión de acciones que se destacan (por ejemplo, el botón de instalación).", + "extensionButtonProminentForeground": "Color de primer plano del botón para la extensión de acciones que se destacan (por ejemplo, botón de instalación).", + "extensionButtonProminentHoverBackground": "Color de fondo del botón al mantener el mouse para la extensión de acciones que se destacan (por ejemplo, el botón de instalación)." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..ee91f33ce0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "Presione ENTRAR para administrar sus extensiones.", + "searchFor": "Presione ENTRAR para buscar '{0}' en el catálogo de soluciones.", + "noExtensionsToInstall": "Escriba un nombre de extensión" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..63d9831932 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "Extensiones", + "app.extensions.json.recommendations": "Lista de recomendaciones de extensiones. El identificador de una extensión es siempre '${publisher}.${name}'. Por ejemplo: 'vscode.csharp'.", + "app.extension.identifier.errorMessage": "Se esperaba el formato '${publisher}.${name}'. Ejemplo: 'vscode.csharp'." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..5fd522d573 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "Extensión: {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..4b40c701e0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "Para este tipo de archivo, se recomineda la extensión '{0}'.", + "showRecommendations": "Mostrar recomendaciones", + "neverShowAgain": "No volver a mostrar", + "close": "Cerrar", + "workspaceRecommended": "Esta área de trabajo tiene recomendaciones de extensión.", + "ignoreExtensionRecommendations": "¿Desea despreciar todas las recomendaciones de extensión?", + "ignoreAll": "Sí, despreciar todas.", + "no": "No", + "cancel": "Cancelar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..c76c7624f3 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "Administrar extensiones", + "galleryExtensionsCommands": "Instalar extensiones de la galería", + "extension": "Extensión", + "extensions": "Extensiones", + "view": "Ver", + "extensionsConfigurationTitle": "Extensiones", + "extensionsAutoUpdate": "Actualizar extensiones automáticamente", + "extensionsIgnoreRecommendations": "No tener en cuenta las recomendaciones de extensión." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..c486b8e373 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "Abrir carpeta de extensiones", + "installVSIX": "Instalar desde VSIX...", + "InstallVSIXAction.success": "La extensión se instaló correctamente. Reinicie para habilitarla.", + "InstallVSIXAction.reloadNow": "Recargar ahora" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..26f4ecff51 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "¿Quiere deshabilitar otras asignaciones de teclas ({0}) para evitar conflictos entre enlaces de teclado?", + "yes": "Sí", + "no": "No", + "betterMergeDisabled": "La extensión Mejor combinación está ahora integrada, la extensión instalada se deshabilitó y no se puede desinstalar.", + "uninstall": "Desinstalación", + "later": "Más tarde" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..bd88180430 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "Marketplace", + "installedExtensions": "Instalado", + "searchInstalledExtensions": "Instalado", + "recommendedExtensions": "Recomendado", + "searchExtensions": "Buscar extensiones en Marketplace", + "sort by installs": "Criterio de ordenación: Número de instalaciones", + "sort by rating": "Criterio de ordenación: Clasificación", + "sort by name": "Ordenar por: Nombre", + "suggestProxyError": "Marketplace devolvió 'ECONNREFUSED'. Compruebe la configuración de 'http.proxy'.", + "extensions": "Extensiones", + "outdatedExtensions": "{0} extensiones obsoletas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..5bfc6dffda --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Extensiones", + "no extensions found": "No se encontraron extensiones.", + "suggestProxyError": "Marketplace devolvió 'ECONNREFUSED'. Compruebe la configuración de 'http.proxy'." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json new file mode 100644 index 0000000000..08d1dab724 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "¿Quiere deshabilitar otras asignaciones de teclado para evitar conflictos entre los enlaces de teclado?", + "yes": "Sí", + "no": "No" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..b8c167af02 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "Si habilita \"{0}\", también se habilitarán sus dependencias. ¿Desea continuar?", + "enable": "Sí", + "doNotEnable": "No", + "disableDependeciesConfirmation": "¿Desea deshabilitar solo \"{0}\" o también sus dependencias?", + "disableOnly": "Solo", + "disableAll": "Todo", + "cancel": "Cancelar", + "singleDependentError": "No se puede deshabilitar la extensión \"{0}\". La extensión \"{1}\" depende de ella.", + "twoDependentsError": "No se puede deshabilitar la extensión \"{0}\". Las extensiones \"{1}\" y \"{2}\" dependen de ella.", + "multipleDependentsError": "No se puede deshabilitar la extensión \"{0}\". Las extensiones \"{1}\", \"{2}\" y otras dependen de ella.", + "installConfirmation": "Desea instalar la extensión '{0}' ?", + "install": "Instalar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/esn/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..f3744719a1 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "Enviar tweet con comentarios", + "label.sendASmile": "Envíanos un tweet con tus comentarios.", + "patchedVersion1": "La instalación está dañada.", + "patchedVersion2": "Especifique este dato si envía un error.", + "sentiment": "¿Cómo fue su experiencia?", + "smileCaption": "Feliz", + "frownCaption": "Triste", + "other ways to contact us": "Otras formas de ponerse en contacto con nosotros", + "submit a bug": "Enviar un error", + "request a missing feature": "Solicitar una característica que falta", + "tell us why?": "Indícanos por qué", + "commentsHeader": "Comentarios", + "tweet": "Tweet", + "character left": "carácter restante", + "characters left": "caracteres restantes", + "feedbackSending": "Enviando", + "feedbackSent": "Gracias", + "feedbackSendingError": "Intentar de nuevo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..0f20c961a1 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "Visor de archivos binarios" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..747d1d4489 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "Editor de archivos de texto", + "createFile": "Crear archivo", + "fileEditorWithInputAriaLabel": "{0}. Editor de archivos de texto.", + "fileEditorAriaLabel": "Editor de archivos de texto." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..0056dd8352 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "Carpetas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..55a720c862 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "Archivos", + "revealInSideBar": "Mostrar en barra lateral", + "acceptLocalChanges": "Usar los cambios y sobrescribir el contenido del disco", + "revertLocalChanges": "Descartar los cambios y volver al contenido del disco" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..236e3a45d0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "Reintentar", + "rename": "Cambiar nombre", + "newFile": "Nuevo archivo", + "newFolder": "Nueva carpeta", + "openFolderFirst": "Abra primero una carpeta para crear archivos o carpetas en ella.", + "newUntitledFile": "Nuevo archivo sin título", + "createNewFile": "Nuevo archivo", + "createNewFolder": "Nueva carpeta", + "deleteButtonLabelRecycleBin": "&&Mover a la papelera de reciclaje", + "deleteButtonLabelTrash": "&&Mover a la papelera", + "deleteButtonLabel": "&&Eliminar", + "dirtyMessageFolderOneDelete": "Va a eliminar una carpeta con cambios sin guardar en 1 archivo. ¿Desea continuar?", + "dirtyMessageFolderDelete": "Va a eliminar una carpeta con cambios sin guardar en {0} archivos. ¿Desea continuar?", + "dirtyMessageFileDelete": "Va a eliminar un archivo con cambios sin guardar. ¿Desea continuar?", + "dirtyWarning": "Los cambios se perderán si no se guardan.", + "confirmMoveTrashMessageFolder": "¿Está seguro de que desea eliminar '{0}' y su contenido?", + "confirmMoveTrashMessageFile": "¿Está seguro de que desea eliminar '{0}'?", + "undoBin": "Puede restaurar desde la papelera de reciclaje.", + "undoTrash": "Puede restaurar desde la papelera.", + "confirmDeleteMessageFolder": "¿Está seguro de que desea eliminar '{0}' y su contenido de forma permanente?", + "confirmDeleteMessageFile": "¿Está seguro de que desea eliminar '{0}' de forma permanente?", + "irreversible": "Esta acción es irreversible.", + "permDelete": "Eliminar permanentemente", + "delete": "Eliminar", + "importFiles": "Importar archivos", + "confirmOverwrite": "Ya existe un archivo o carpeta con el mismo nombre en la carpeta de destino. ¿Quiere reemplazarlo?", + "replaceButtonLabel": "&&Reemplazar", + "copyFile": "Copiar", + "pasteFile": "Pegar", + "duplicateFile": "Duplicado", + "openToSide": "Abrir en el lateral", + "compareSource": "Seleccionar para comparar", + "globalCompareFile": "Comparar archivo activo con...", + "pickHistory": "Seleccione un archivo abierto anteriormente para comparar", + "unableToFileToCompare": "El archivo seleccionado no se puede comparar con '{0}'.", + "openFileToCompare": "Abrir un archivo antes para compararlo con otro archivo.", + "compareWith": "Comparar \"{0}\" con \"{1}\"", + "compareFiles": "Comparar archivos", + "refresh": "Actualizar", + "save": "Guardar", + "saveAs": "Guardar como...", + "saveAll": "Guardar todos", + "saveAllInGroup": "Guardar todo en el grupo", + "saveFiles": "Guardar archivos con modificaciones", + "revert": "Revertir archivo", + "focusOpenEditors": "Foco sobre la vista de editores abiertos", + "focusFilesExplorer": "Enfocar Explorador de archivos", + "showInExplorer": "Mostrar el archivo activo en la barra lateral", + "openFileToShow": "Abra primero un archivo para mostrarlo en el explorador.", + "collapseExplorerFolders": "Contraer carpetas en el Explorador", + "refreshExplorer": "Actualizar Explorador", + "openFile": "Abrir archivo...", + "openFileInNewWindow": "Abrir archivo activo en nueva ventana", + "openFileToShowInNewWindow": "Abrir un archivo antes para abrirlo en una nueva ventana", + "revealInWindows": "Mostrar en el Explorador", + "revealInMac": "Mostrar en Finder", + "openContainer": "Abrir carpeta contenedora", + "revealActiveFileInWindows": "Mostrar archivo activo en el Explorador de Windows", + "revealActiveFileInMac": "Mostrar archivo activo en Finder", + "openActiveFileContainer": "Abrir carpeta contenedora del archivo activo", + "copyPath": "Copiar ruta de acceso", + "copyPathOfActive": "Copiar ruta del archivo activo", + "emptyFileNameError": "Debe especificarse un nombre de archivo o carpeta.", + "fileNameExistsError": "Ya existe el archivo o carpeta **{0}** en esta ubicación. Elija un nombre diferente.", + "invalidFileNameError": "El nombre **{0}** no es válido para el archivo o la carpeta. Elija un nombre diferente.", + "filePathTooLongError": "El nombre **{0}** da como resultado una ruta de acceso demasiado larga. Elija un nombre más corto.", + "compareWithSaved": "Comparar el archivo activo con el guardado", + "modifiedLabel": "{0} (en disco) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..3dac2e08c7 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "Abrir un archivo antes para copiar su ruta de acceso", + "openFileToReveal": "Abrir un archivo antes para mostrarlo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..1e51bed588 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "Mostrar explorador", + "explore": "Explorador", + "view": "Ver", + "textFileEditor": "Editor de archivos de texto", + "binaryFileEditor": "Editor de archivos binarios", + "filesConfigurationTitle": "Archivos", + "exclude": "Configurar patrones globales para excluir archivos y carpetas.", + "files.exclude.boolean": "El patrón global con el que se harán coincidir las rutas de acceso de los archivos. Establézcalo en true o false para habilitarlo o deshabilitarlo.", + "files.exclude.when": "Comprobación adicional de los elementos del mismo nivel de un archivo coincidente. Use $(nombreBase) como variable para el nombre de archivo que coincide.", + "associations": "Configure asociaciones de archivo para los lenguajes (por ejemplo, \"*.extension\": \"html\"). Estas asociaciones tienen prioridad sobre las asociaciones predeterminadas de los lenguajes instalados.", + "encoding": "La codificación del juego de caracteres predeterminada que debe utilizarse al leer y escribir archivos.", + "autoGuessEncoding": "Si está opción está habilitada, se intentará adivinar la codificación del juego de caracteres al abrir los archivos", + "eol": "Carácter predeterminado de final de línea. Utilice \\n para LF y \\r\\n para CRLF.", + "trimTrailingWhitespace": "Si se habilita, se recortará el espacio final cuando se guarde un archivo.", + "insertFinalNewline": "Si se habilita, inserte una nueva línea final al final del archivo cuando lo guarde.", + "files.autoSave.off": "Un archivo con modificaciones no se guarda nunca automáticamente.", + "files.autoSave.afterDelay": "Un archivo con modificaciones se guarda automáticamente tras la propiedad \"files.autoSaveDelay\" configurada.", + "files.autoSave.onFocusChange": "Un archivo con modificaciones se guarda automáticamente cuando el editor deja de fijarse en él.", + "files.autoSave.onWindowChange": "Un archivo con modificaciones se guarda automáticamente cuando la ventana deja de fijarse en él.", + "autoSave": "Controla el guardado automático de los archivos modificados. Valores aceptados: \"{0}\", \"{1}\", \"{2}\" (el editor pierde el foco), \"{3}\" (la ventana pierde el foco) . Si se establece en \"{4}\", puede configurar el retraso en \"files.autoSaveDelay\".", + "autoSaveDelay": "Controla el retraso en MS tras el cual un archivo con modificaciones se guarda automáticamente. Solo se aplica si \"files.autoSave\" está establecido en \"{0}\"", + "watcherExclude": "Configure patrones globales de las rutas de acceso de archivo que se van a excluir de la inspección de archivos. Los patrones deben coincidir con rutas de acceso absolutas (por ejemplo, prefijo ** o la ruta de acceso completa para que la coincidencia sea correcta). Al cambiar esta configuración, es necesario reiniciar. Si observa que Code consume mucho tiempo de CPU al iniciarse, puede excluir las carpetas grandes para reducir la carga inicial. ", + "hotExit.off": "Deshabilita la salida rápida.", + "hotExit.onExit": "hotExit se desencadena al cerrar la aplicación, es decir, al cerrarse la última ventana en Windows/Linux o cuando se desencadena el comando workbench.action.quit (paleta de comandos, enlace de teclado, menú). Todas las ventanas con copias de seguridad se restaurarán la próxima vez que se inicie.", + "hotExit.onExitAndWindowClose": "La salida rápida se desencadena al cerrar la aplicación, es decir, al cerrarse la última ventana en Windows/Linux o cuando se desencadena el comando workbench.action.quit (paleta de comandos, enlace de teclado, menú), y también para cualquier ventana que tenga una carpeta abierta, independientemente de que sea o no la última ventana. Todas las ventanas sin carpetas abiertas se restaurarán la próxima vez que se inicie. Para restaurar las ventanas con carpetas tal cual estaban antes de cerrarse, establezca \"window.restoreWindows\" a \"all\".", + "hotExit": "Controla si los archivos no guardados se recuerdan entre las sesiones, lo que permite omitir el mensaje para guardar al salir del editor.", + "useExperimentalFileWatcher": "Utilice el nuevo monitor de archivo experimental.", + "defaultLanguage": "El modo de lenguaje predeterminado que se asigna a nuevos archivos.", + "editorConfigurationTitle": "Editor", + "formatOnSave": "Formatea un archivo al guardarlo. Debe haber un formateador disponible, el archivo no debe guardarse automáticamente y el editor no debe estar cerrándose.", + "explorerConfigurationTitle": "Explorador de archivos", + "openEditorsVisible": "Número de editores mostrados en el panel Editores abiertos. Establezca este valor en 0 para ocultar el panel.", + "dynamicHeight": "Controla si la altura de la sección de editores abiertos debería adaptarse o no de forma dinámica al número de elementos.", + "autoReveal": "Controla si el explorador debe mostrar y seleccionar automáticamente los archivos al abrirlos.", + "enableDragAndDrop": "Controla si el explorador debe permitir mover archivos y carpetas mediante la función arrastrar y colocar.", + "sortOrder.default": "Los archivos y las carpetas se ordenan por nombre alfabéticamente. Las carpetas se muestran antes que los archivos.", + "sortOrder.mixed": "Los archivos y las carpetas se ordenan por nombre alfabéticamente. Los archivos se entrelazan con las carpetas.", + "sortOrder.filesFirst": "Los archivos y las carpetas se ordenan por nombre alfabéticamente. Los archivos se muestran antes que las carpetas.", + "sortOrder.type": "Los archivos y las carpetas se ordenan por extensión. Las carpetas se muestran antes que los archivos.", + "sortOrder.modified": "Los archivos y las carpetas se ordenan por fecha de última modificación. Las carpetas se muestran antes que los archivos.", + "sortOrder": "Controla el criterio de ordenación de los archivos y las carpetas en el explorador. Además del orden \"default\", puede establecer el orden en \"mixed\" (los archivos y las carpetas se ordenan combinados), \"type\" (por tipo de archivo), \"modified\" (por fecha de última modificación) o \"filesFirst\" (los archivos se colocan antes que las carpetas)." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..72c5ea029e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "Descartar", + "overwrite": "Sobrescribir", + "retry": "Reintentar", + "readonlySaveError": "No se pudo guardar '{0}': El archivo está protegido contra escritura. Seleccione \"Sobrescribir\" para quitar la protección.", + "genericSaveError": "No se pudo guardar '{0}': {1}", + "staleSaveError": "No se pudo guardar '{0}': El contenido del disco es más reciente. Haga clic en **Comparar** para comparar su versión con la que hay en el disco.", + "compareChanges": "Comparar", + "saveConflictDiffLabel": "0} (on disk) ↔ {1} (in {2}) - Resolver conflicto guardado", + "userGuide": "Use las acciones de la barra de herramientas del editor situada a la derecha para **deshacer** los cambios o **sobrescribir** el contenido del disco con sus cambios" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..904b886370 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "No hay ninguna carpeta abierta", + "explorerSection": "Sección del Explorador de archivos", + "noWorkspaceHelp": "Todavía no ha abierto ninguna carpeta.", + "openFolder": "Abrir carpeta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..5b921ff08c --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "Sección del Explorador de archivos", + "treeAriaLabel": "Explorador de archivos" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..dcfc800183 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "Escriba el nombre de archivo. Presione ENTRAR para confirmar o Esc para cancelar", + "filesExplorerViewerAriaLabel": "{0}, Explorador de archivos", + "dropFolders": "¿Quiere agregar las carpetas al área de trabajo?", + "dropFolder": "¿Quiere agregar la carpeta al área de trabajo?", + "addFolders": "&&Agregar carpetas", + "addFolder": "&&Agregar carpeta", + "confirmOverwriteMessage": "'{0}' ya existe en la carpeta de destino. ¿Desea reemplazarlo?", + "irreversible": "Esta acción es irreversible.", + "replaceButtonLabel": "Reemplazar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..1e1098be3d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "Editores abiertos", + "openEditosrSection": "Sección Editores abiertos", + "treeAriaLabel": "Editores abiertos: lista de archivos activos", + "dirtyCounter": "{0} sin guardar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..14aafb2824 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}, grupo de editores", + "openEditorAriaLabel": "{0}, abrir editor", + "saveAll": "Guardar todos", + "closeAllUnmodified": "Cerrar los que no se han modificado", + "closeAll": "Cerrar todo", + "compareWithSaved": "Comparar con el guardado", + "close": "Cerrar", + "closeOthers": "Cerrar otros" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/esn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..e57caaafee --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0} archivos no guardados" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/esn/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..32c2e3e166 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (deleted from disk)" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json new file mode 100644 index 0000000000..be49464f03 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "switchToChangesView": "Cambiar a la vista de cambios", + "openInEditor": "Cambiar a la vista de editor", + "workbenchStage": "Almacenar provisionalmente", + "workbenchUnstage": "Cancelar almacenamiento provisional", + "stageSelectedLines": "Almacenar provisionalmente las líneas seleccionadas", + "unstageSelectedLines": "Deshacer la preparación de las líneas seleccionadas", + "revertSelectedLines": "Revertir líneas seleccionadas", + "confirmRevertMessage": "¿Seguro que desea revertir los cambios seleccionados?", + "irreversible": "Esta acción es irreversible.", + "revertChangesLabel": "&&Revertir cambios", + "openChange": "Abrir cambio", + "openFile": "Abrir archivo", + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/gitActions.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/gitActions.i18n.json new file mode 100644 index 0000000000..dfef2412ca --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/gitActions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openChange": "Abrir cambio", + "openFile": "Abrir archivo", + "init": "Iniciar", + "refresh": "Actualizar", + "stageChanges": "Almacenar provisionalmente", + "stageAllChanges": "Almacenar todo provisionalmente", + "confirmUndoMessage": "¿Seguro que quiere limpiar todos los cambios?", + "confirmUndoAllOne": "Hay cambios que no se almacenaron provisionalmente en {0} archivo. Esta acción es irreversible.", + "confirmUndoAllMultiple": "Hay cambios que no se almacenaron provisionalmente en {0} archivos. Esta acción es irreversible.", + "cleanChangesLabel": "&&Limpiar cambios", + "confirmUndo": "¿Seguro que quiere limpiar los cambios de '{0}'?", + "irreversible": "Esta acción es irreversible.", + "undoChanges": "Limpiar", + "undoAllChanges": "Limpiar todo", + "unstage": "Cancelar almacenamiento provisional", + "unstageAllChanges": "Cancelar almacenamiento provisional de todo", + "dirtyTreeCheckout": "No se puede desproteger. Confirme o guarde provisionalmente su trabajo primero.", + "commitStaged": "Confirmar almacenados provisionalmente", + "commitStagedAmend": "Confirmar almacenados provisionalmente (modificar)", + "commitStagedSignedOff": "Confirmar por etapas (Aprobado)", + "commit": "Confirmar", + "commitMessage": "Mensaje de confirmación", + "commitAll": "Confirmar todo", + "commitAllSignedOff": "Confirmar todo (aprobado)", + "commitAll2": "Confirmar todo", + "commitStaged2": "Confirmar almacenados provisionalmente", + "dirtyTreePull": "No se puede extraer. Confirme o guarde provisionalmente su trabajo primero.", + "authFailed": "Error de autenticación en GIT remoto.", + "pushToRemote": "Insertar en...", + "pushToRemotePickMessage": "Elija un origen remoto donde insertar la rama '{0}':", + "publish": "Publicar", + "confirmPublishMessage": "¿Seguro que quiere publicar '{0}' en '{1}'?", + "confirmPublishMessageButton": "&&Publicar", + "publishPickMessage": "Seleccionar un elemento remoto para publicar la rama '{0}':", + "sync is unpredictable": "Esta acción insertará y extraerá confirmaciones en '{0}'.", + "ok": "Aceptar", + "cancel": "Cancelar", + "never again": "De acuerdo, no volver a mostrar este mensaje", + "undoLastCommit": "Deshacer última confirmación" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json new file mode 100644 index 0000000000..e5490a5775 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refAriaLabel": "{0}, GIT", + "checkoutBranch": "Rama en {0}", + "checkoutRemoteBranch": "Rama remota en {0}", + "checkoutTag": "Etiqueta en {0}", + "alreadyCheckedOut": "{0} ya es la rama actual", + "branchAriaLabel": "{0}, rama de GIT", + "createBranch": "Crear la rama {0}", + "noBranches": "No hay otras ramas", + "notValidBranchName": "Proporcione un nombre de rama válido" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/gitServices.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/gitServices.i18n.json new file mode 100644 index 0000000000..cb6dea972b --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/gitServices.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cantOpen": "No se puede abrir este recurso de Git.", + "gitIndexChanges": "{0} (index) ↔ {1}", + "gitIndexChangesDesc": "{0} - Cambios del índice", + "gitIndexChangesRenamed": "{0} ← {1}", + "gitIndexChangesRenamedDesc": "{0} - Cambiado de nombre - Cambios en el índice", + "workingTreeChanges": "{0} (HEAD) ↔ {1}", + "workingTreeChangesDesc": "{0} - Cambios del árbol de trabajo", + "gitMergeChanges": "{0} (merge) ↔ {1}", + "gitMergeChangesDesc": "{0} - Combinar cambios", + "updateGit": "Parece que tiene GIT {0} instalado. Code funciona mejor con GIT >=2.0.0.", + "download": "Descargar", + "neverShowAgain": "No volver a mostrar", + "configureUsernameEmail": "Configure su nombre de usuario de GIT y su correo electrónico.", + "badConfigFile": "Git {0}", + "unmergedChanges": "Debe resolver los cambios sin combinar antes de confirmar sus cambios.", + "showOutput": "Mostrar salida", + "cancel": "Cancelar", + "checkNativeConsole": "Se ha producido un problema al ejecutar la operación GIT. Revise la salida o use una consola para comprobar el estado del repositorio.", + "changesFromIndex": "{0} (index)", + "changesFromIndexDesc": "{0} - Cambios del índice", + "changesFromTree": "{0} ({1})", + "changesFromTreeDesc": "{0} - Cambios de {1}", + "cantOpenResource": "No se puede abrir este recurso de Git." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json new file mode 100644 index 0000000000..107311bf11 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "publishBranch": "Publicar rama", + "syncBranch": "Sincronizar cambios", + "gitNotEnabled": "Git no está habilitado en esta área de trabajo." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json new file mode 100644 index 0000000000..c9e355455e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gitProgressBadge": "Estado del Git en ejecución", + "gitPendingChangesBadge": "{0} cambios pendientes", + "toggleGitViewlet": "Mostrar GIT", + "git": "GIT", + "view": "Ver", + "gitCommands": "Comandos Git", + "gitConfigurationTitle": "GIT", + "gitEnabled": "Habilitado para GIT", + "gitPath": "Ruta de acceso del ejecutable de GIT", + "gitAutoRefresh": "Indica si la actualización automática está habilitada", + "gitAutoFetch": "Si la búsqueda automática está habilitada.", + "gitLongCommit": "Si se debe advertir sobre los mensajes de confirmación largos.", + "gitLargeRepos": "Permitir siempre que Code administre grandes repositorios.", + "confirmSync": "Confirmar antes de sincronizar repositorios GIT.", + "countBadge": "Controla el contador de señales git.", + "checkoutType": "Controla qué tipo de ramas figuran en la lista." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json new file mode 100644 index 0000000000..0708807116 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "needMessage": "Especifique un mensaje de confirmación. Siempre puede presionar **{0}** para confirmar los cambios. Si hay cambios almacenados provisionalmente, solo se confirmarán dichos cambios; de lo contrario, se confirmarán todos los cambios.", + "nothingToCommit": "Cuando haya cambios para confirmar, escriba el mensaje de confirmación y presione **{0}** para confirmarlos. Si hay cambios almacenados provisionalmente, solo se confirmarán estos cambios; de lo contrario, se confirmarán todos los cambios.", + "longCommit": "Se recomienda mantener la primera línea de la confirmación por debajo de 50 caracteres. Puede usar más líneas para información adicional.", + "commitMessage": "Message (press {0} to commit)", + "commitMessageAriaLabel": "GIT: Escriba un mensaje de confirmación y presione {0} para confirmar", + "treeAriaLabel": "Vista de cambios de GIT", + "showOutput": "Mostrar salida de GIT" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json new file mode 100644 index 0000000000..dc5fcd8c74 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stagedChanges": "Cambios almacenados provisionalmente", + "allChanges": "Cambios", + "mergeChanges": "Fusionar cambios mediante combinación", + "outsideOfWorkspace": "Este archivo se encuentra fuera del área de trabajo actual.", + "modified-char": "M", + "added-char": "A", + "deleted-char": "D", + "renamed-char": "R", + "copied-char": "C", + "untracked-char": "U", + "ignored-char": "!", + "title-index-modified": "Modificado en índice", + "title-modified": "Modificado", + "title-index-added": "Agregado a índice", + "title-index-deleted": "Eliminado de índice", + "title-deleted": "Eliminado", + "title-index-renamed": "Nombre modificado en índice", + "title-index-copied": "Copiado en índice", + "title-untracked": "Sin seguimiento", + "title-ignored": "Omitido", + "title-conflict-both-deleted": "Conflicto: eliminado por los dos", + "title-conflict-added-by-us": "Conflicto: agregado por nosotros", + "title-conflict-deleted-by-them": "Conflicto: eliminado por ellos", + "title-conflict-added-by-them": "Conflicto: agregado por ellos", + "title-conflict-deleted-by-us": "Conflicto: eliminado por nosotros", + "title-conflict-both-added": "Conflicto: agregado por los dos", + "title-conflict-both-modified": "Conflicto: modificado por los dos", + "fileStatusAriaLabel": "El archivo {0} de la carpeta {1} tiene el estado: {2}, GIT", + "ariaLabelStagedChanges": "Cambios almacenados provisionalmente, GIT", + "ariaLabelChanges": "Cambios, GIT", + "ariaLabelMerge": "Fusión mediante combinación, GIT" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json new file mode 100644 index 0000000000..dcdc76a88b --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disabled": "GIT está deshabilitado en la configuración." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json new file mode 100644 index 0000000000..2e0765d3cf --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noGit": "Esta área de trabajo aún no está bajo el control de código fuente de Git.", + "gitinit": "Inicializar repositorio de GIT" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json new file mode 100644 index 0000000000..71ffc16f43 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "macInstallWith": "Puede instalarlo con {0}, descargarlo de {1} o instalar las herramientas de desarrollo de la línea de comandos de {2} con solo escribir {3} en el símbolo del sistema de un terminal.", + "winInstallWith": "Puede instalarlo con {0} o descargarlo desde {1}.", + "linuxDownloadFrom": "Se puede descargar de {0}.", + "downloadFrom": "Se puede descargar de {0}.", + "looksLike": "Parece que GIT no está instalado en el sistema.", + "pleaseRestart": "Una vez instalado GIT, reinicie VSCode." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json new file mode 100644 index 0000000000..8e03030e2e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "huge": "El repositorio parece tener muchos cambios activos. Esto puede hacer que Code se ejecute muy lento.", + "setting": "Puede deshabilitar permanentemente esta advertencia con el valor siguiente:", + "allo": "Permitir repositorios de gran tamaño" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json new file mode 100644 index 0000000000..1b595eb478 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrongRoot": "Este directorio parece estar incluido en un repositorio GIT.", + "pleaseRestart": "Abra el directorio raíz del repositorio para acceder a las características de GIT." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json b/i18n/esn/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json new file mode 100644 index 0000000000..42b5c5e74c --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspaceHelp": "Todavía no ha abierto ninguna carpeta.", + "pleaseRestart": "Abra una carpeta que tenga un repositorio GIT para acceder a las características de GIT.", + "openFolder": "Abrir carpeta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json new file mode 100644 index 0000000000..64bd7f1d8e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSCMViewlet": "Mostrar SCM", + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json b/i18n/esn/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json new file mode 100644 index 0000000000..07c462bac7 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "valid": "Proporcione una dirección URL válida de un repositorio de GIT", + "url": "URL del repositorio", + "directory": "Directorio de destino de la clonación", + "cloning": "Clonando el repositorio '{0}'...", + "already exists": "El repositorio de destino ya existe. Elija otro directorio para la clonación." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json b/i18n/esn/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json new file mode 100644 index 0000000000..6f2dc5416f --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/git/node/git.lib.i18n.json b/i18n/esn/src/vs/workbench/parts/git/node/git.lib.i18n.json new file mode 100644 index 0000000000..001b72da47 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/git/node/git.lib.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorBuffer": "No se puede abrir el archivo de GIT", + "fileBinaryError": "El archivo parece ser binario y no se puede abrir como texto" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..c1e02cc0e7 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "Vista previa de HTML", + "devtools.webview": "Desarrollador: Herramientas Webview" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/esn/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..0e5bda2b35 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "Entrada del editor no válida." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/esn/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..8a52fec03f --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "Desarrollador: Herramientas Webview" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/esn/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..3316c71da0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "Ver", + "problems.view.show.label": "Mostrar problemas", + "problems.panel.configuration.title": "Vista Problemas", + "problems.panel.configuration.autoreveal": "Controla si la vista Problemas debe revelar automáticamente los archivos cuando los abre", + "markers.panel.title.problems": "Problemas", + "markers.panel.aria.label.problems.tree": "Problemas agrupados por archivos", + "markers.panel.no.problems.build": "Hasta el momento, no se encontraron problemas en el área de trabajo.", + "markers.panel.no.problems.filters": "No se encontraron resultados con los criterios de filtro proporcionados", + "markers.panel.action.filter": "Filtrar problemas", + "markers.panel.filter.placeholder": "Filtrar por tipo o texto", + "markers.panel.filter.errors": "errores", + "markers.panel.filter.warnings": "advertencias", + "markers.panel.filter.infos": "informaciones", + "markers.panel.single.error.label": "1 error", + "markers.panel.multiple.errors.label": "{0} errores", + "markers.panel.single.warning.label": "1 advertencia", + "markers.panel.multiple.warnings.label": "{0} advertencias", + "markers.panel.single.info.label": "1 información", + "markers.panel.multiple.infos.label": "{0} informaciones", + "markers.panel.single.unknown.label": "1 desconocido", + "markers.panel.multiple.unknowns.label": "{0} desconocidos", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "{0} con {1} problemas", + "problems.tree.aria.label.error.marker": "Se generó un error en {0}: {1} en la línea {2} y el carácter {3}", + "problems.tree.aria.label.error.marker.nosource": "Error: {0} en la línea {1} y el carácter {2}", + "problems.tree.aria.label.warning.marker": "Se generó una advertencia en {0}: {1} en la línea {2} y el carácter {3}", + "problems.tree.aria.label.warning.marker.nosource": "Advertencia: {0} en la línea {1} y el carácter {2}", + "problems.tree.aria.label.info.marker": "Se generó información en {0}: {1} en la línea {2} y el carácter {3}", + "problems.tree.aria.label.info.marker.nosource": "Información: {0} en la línea {1} y el carácter {2}", + "problems.tree.aria.label.marker": "Se generó un problema en {0}: {1} en la línea {2} y el carácter {3}", + "problems.tree.aria.label.marker.nosource": "Problema: {0} en la línea {1} y el carácter{2}", + "errors.warnings.show.label": "Mostrar errores y advertencias" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/esn/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..3a8d521776 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "Copiar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..4c6b92bb19 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "¿Le importaría realizar una breve encuesta de opinión?", + "takeSurvey": "Realizar encuesta", + "remindLater": "Recordármelo más tarde", + "neverAgain": "No volver a mostrar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..9ad3961e83 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Salida", + "viewCategory": "Ver", + "clearOutput.label": "Borrar salida" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/esn/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..fedd9225f0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "Alternar salida", + "clearOutput": "Borrar salida", + "toggleOutputScrollLock": "Alternar Bloq Despl salida", + "switchToOutput.label": "Cambiar a salida" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/esn/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..74202f955e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0}, panel de salida", + "outputPanelAriaLabel": "Panel de salida" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/esn/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..d76fd26e63 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Salida", + "channel": "para \"{0}\"" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..2bb7d1bf9e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "Inicio lento detectado", + "slow.detail": "Lamentamos que haya tenido un inicio lento. Reinicie \"{0}\" con la generación de perfiles habilitada, comparta los perfiles con nosotros y trabajaremos a fondo para que vuelva a disfrutar de un inicio increíble.", + "prof.message": "Los perfiles se crearon correctamente.", + "prof.detail": "Cree un problema y asóciele manualmente los siguientes archivos: {0}", + "prof.restartAndFileIssue": "Crear problema y reiniciar", + "prof.restart": "Reiniciar", + "prof.thanks": "Gracias por ayudarnos.", + "prof.detail.restart": "Se necesita un reinicio final para continuar utilizando '{0}'. De nuevo, gracias por su aportación." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..7f8d690358 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "Presione la combinación de teclas que desee y después ENTRAR. Presione ESCAPE para cancelar.", + "defineKeybinding.chordsTo": "chord to" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..2c02548f94 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "Métodos abreviados de teclado", + "SearchKeybindings.AriaLabel": "Buscar enlaces de teclado", + "SearchKeybindings.Placeholder": "Buscar enlaces de teclado", + "sortByPrecedene": "Ordenar por procedimiento", + "header-message": "Para personalizaciones avanzadas, abra y edite el archivo ", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "Enlaces de teclado", + "changeLabel": "Cambiar enlace de teclado", + "addLabel": "Agregar enlace de teclado", + "removeLabel": "Quitar enlace de teclado", + "resetLabel": "Restablecer enlaces de teclado", + "showConflictsLabel": "Mostrar conflictos", + "copyLabel": "Copiar", + "error": "Error \"{0}\" al editar el enlace de teclado. Abra el archivo \"keybindings.json\" y compruébelo.", + "command": "Comando", + "keybinding": "Enlace de teclado", + "source": "Origen", + "when": "Cuando", + "editKeybindingLabelWithKey": "Cambiar enlace de teclado {0}", + "editKeybindingLabel": "Cambiar enlace de teclado", + "addKeybindingLabelWithKey": "Agregar enlace de teclado {0}", + "addKeybindingLabel": "Agregar enlace de teclado", + "commandAriaLabel": "El comando es {0}.", + "keybindingAriaLabel": "El enlace de teclado es {0}.", + "noKeybinding": "No se ha asignado ningún enlace de teclado.", + "sourceAriaLabel": "El origen es {0}.", + "whenAriaLabel": "Cuando es {0}.", + "noWhen": "No, cuando hay contexto." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..700f8969c7 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "Definir enlace de teclado", + "defineKeybinding.kbLayoutErrorMessage": "La distribución del teclado actual no permite reproducir esta combinación de teclas.", + "defineKeybinding.kbLayoutLocalAndUSMessage": "**{0}** para su distribución de teclado actual (**{1}** para EE. UU. estándar).", + "defineKeybinding.kbLayoutLocalMessage": "**{0}** para su distribución de teclado actual." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..f849a70cc7 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "Editor de preferencias predeterminado", + "keybindingsEditor": "Editor de enlaces de teclado", + "preferences": "Preferencias" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..f8a2a6c1e6 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "Abrir configuración de usuario", + "openGlobalKeybindings": "Abrir métodos abreviados de teclado", + "openGlobalKeybindingsFile": "Abrir el archivo de métodos abreviados de teclado", + "openWorkspaceSettings": "Abrir configuración del área de trabajo", + "openFolderSettings": "Abrir Configuración de carpeta", + "pickFolder": "Seleccionar carpeta", + "configureLanguageBasedSettings": "Configurar opciones específicas del lenguaje...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "Seleccionar lenguaje" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..f306254ab5 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "Configuración predeterminada", + "SearchSettingsWidget.AriaLabel": "Buscar configuración", + "SearchSettingsWidget.Placeholder": "Buscar configuración", + "totalSettingsMessage": "{0} configuraciones en total", + "noSettingsFound": "Sin resultados", + "oneSettingFound": "Coincide 1 configuración", + "settingsFound": "{0} configuraciones coincidentes", + "fileEditorWithInputAriaLabel": "{0}. Editor de archivos de texto.", + "fileEditorAriaLabel": "Editor de archivos de texto.", + "preferencesAriaLabel": "Preferencias predeterminadas. Editor de texto de solo lectura." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..6e8420d8cd --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "Coloque aquí su configuración para sobrescribir la configuración predeterminada.", + "errorInvalidConfiguration": "No se puede escribir la configuración. Corrija los errores o advertencias del archivo y vuelva a intentarlo.", + "emptyWorkspaceSettingsHeader": "Coloque aquí su configuración para sobrescribir la configuración de usuario.", + "emptyFolderSettingsHeader": "Coloque aquí su configuración de carpeta para sobrescribir la que se especifica en la configuración de área de trabajo.", + "defaultFolderSettingsTitle": "Configuración de carpeta predeterminada", + "defaultSettingsTitle": "Configuración predeterminada", + "noSettingsFound": "No se encontró ninguna configuración.", + "editTtile": "Editar", + "replaceDefaultValue": "Reemplazar en Configuración", + "copyDefaultValue": "Copiar en Configuración", + "unsupportedPHPExecutablePathSetting": "Este valor debe estar en la configuración de usuario. Si quiere configurar PHP para el área de trabajo, abra un archivo PHP y haga clic en \"Ruta de acceso PHP\" en la barra de estado.", + "unsupportedWorkspaceSetting": "Este valor debe estar en Configuración de usuario.", + "unsupportedWorkbenchSetting": "Esta configuración no se puede aplicar ahora. Se aplicará al abrir esta carpeta directamente." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..c222166c1f --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "Abrir una carpeta antes de crear la configuración del área de trabajo", + "emptyKeybindingsHeader": "Coloque sus enlaces de teclado en este archivo para sobrescribir los valores predeterminados.", + "defaultKeybindings": "Enlaces de teclado predeterminados", + "folderSettingsName": "{0} (Configuración de carpeta)", + "fail.createSettings": "No se puede crear '{0}' ({1})." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..dffd2ce188 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "Configuración de carpeta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..d0878e28da --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "Predeterminado", + "user": "Usuario", + "meta": "meta", + "option": "opción" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..e03dbe77a6 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "Configuración de usuario", + "workspaceSettingsTarget": "Configuración de área de trabajo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..985bfdb82d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "Más utilizada", + "noSettings": "No hay configuración", + "defaultKeybindingsHeader": "Coloque los enlaces de teclado en el archivo de enlaces de teclado para sobrescribirlos." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..2122ffc05f --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Mostrar todos los comandos", + "clearCommandHistory": "Borrar historial de comandos", + "showCommands.label": "Paleta de comandos...", + "entryAriaLabelWithKey": "{0}, {1}, comandos", + "entryAriaLabel": "{0}, comandos", + "canNotRun": "El comando '{0}' no puede ejecutarse desde aquí.", + "actionNotEnabled": "El comando '{0}' no está habilitado en el contexto actual.", + "recentlyUsed": "usado recientemente", + "morecCommands": "otros comandos", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "No hay comandos coincidentes" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..29fbb92900 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "Ir a la línea...", + "gotoLineLabelEmptyWithLimit": "Escriba un número de línea comprendido entre 1 y {0} a la cual quiera navegar.", + "gotoLineLabelEmpty": "Escriba el número de línea a la cual quiera navegar.", + "gotoLineColumnLabel": "Ir a la línea {0} y al carácter {1}", + "gotoLineLabel": "Ir a la línea {0}", + "gotoLineHandlerAriaLabel": "Escriba el número de la línea a la que quiere navegar.", + "cannotRunGotoLine": "Abrir un archivo de texto antes de ir a una línea" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..6d4f8759d9 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "Ir al símbolo en el archivo...", + "symbols": "símbolos ({0})", + "method": "métodos ({0})", + "function": "funciones ({0})", + "_constructor": "constructores ({0})", + "variable": "variables ({0})", + "class": "clases ({0})", + "interface": "interfaces ({0})", + "namespace": "espacios de nombres ({0})", + "package": "paquetes ({0})", + "modules": "módulos ({0})", + "property": "propiedades ({0})", + "enum": "enumeraciones ({0})", + "string": "cadenas ({0})", + "rule": "reglas ({0})", + "file": "archivos ({0})", + "array": "matrices ({0})", + "number": "números ({0})", + "boolean": "booleanos ({0})", + "object": "objetos ({0})", + "key": "claves ({0})", + "entryAriaLabel": "{0}, símbolos", + "noSymbolsMatching": "No hay símbolos coincidentes", + "noSymbolsFound": "No se encontraron símbolos", + "gotoSymbolHandlerAriaLabel": "Escriba para restringir los símbolos del editor activo.", + "cannotRunGotoSymbolInFile": "No hay información de símbolos para el archivo.", + "cannotRunGotoSymbol": "Abrir un archivo de texto antes de ir a un símbolo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..8f0ad25bc1 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, ayuda del selector", + "globalCommands": "comandos globales", + "editorCommands": "comandos del editor" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..826b2e15e3 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "Mostrar y ejecutar comandos", + "gotoLineDescriptionMac": "Ir a la línea", + "gotoLineDescriptionWin": "Ir a la línea", + "gotoSymbolDescription": "Ir al símbolo en el archivo", + "gotoSymbolDescriptionScoped": "Ir al símbolo en el archivo por categoría", + "helpDescription": "Mostrar Ayuda", + "viewPickerDescription": "Abrir vista" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..c65307385a --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, selector de vista", + "views": "Vistas", + "panels": "Paneles", + "terminals": "Terminal", + "terminalTitle": "{0}: {1}", + "channels": "Salida", + "openView": "Abrir vista", + "quickOpenView": "Abrir vista rápidamente" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..ff5ca6716d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "Ha cambiado un ajuste que requiere un reinicio para ser efectivo.", + "relaunchSettingDetail": "Pulse el botón de reinicio para reiniciar {0} y habilitar el ajuste.", + "restart": "Reiniciar", + "relaunchWorkspaceMessage": "Este cambio de área de trabajo requiere recargar nuestro sistema de extensiones.", + "reload": "Recargar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..f0fad08da3 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "Color de fondo del medianil del editor para las líneas modificadas.", + "editorGutterAddedBackground": "Color de fondo del medianil del editor para las líneas agregadas.", + "editorGutterDeletedBackground": "Color de fondo del medianil del editor para las líneas eliminadas.", + "overviewRulerModifiedForeground": "Color de marcador de regla de información general para contenido modificado.", + "overviewRulerAddedForeground": "Color de marcador de regla de información general para contenido agregado.", + "overviewRulerDeletedForeground": "Color de marcador de regla de información general para contenido eliminado." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..ff4abb0564 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "Mostrar GIT", + "source control": "Control de código fuente", + "toggleSCMViewlet": "Mostrar SCM", + "view": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..aede7d930a --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0} cambios pendientes" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..691d79a7fe --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "Instalar proveedores adicionales de SCM...", + "switch provider": "Cambiar proveedor de SCM..." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..c01080dddb --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Message (press {0} to commit)", + "installAdditionalSCMProviders": "Instalar proveedores adicionales de SCM...", + "no open repo": "No hay controles de código fuente activos.", + "source control": "Control de código fuente", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..20f0f4355a --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "resultados de archivos y símbolos", + "fileResults": "resultados de archivos" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..8154550f9e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, selector de archivos", + "searchResults": "resultados de búsqueda" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..4e2612fab0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, selector de símbolos", + "symbols": "resultados de símbolos", + "noSymbolsMatching": "No hay símbolos coincidentes", + "noSymbolsWithoutInput": "Escribir para buscar símbolos" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..98f07e59e6 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "entrada", + "useIgnoreFilesDescription": "Usar archivos ignore", + "useExcludeSettingsDescription": "Usar Excluir configuración" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..a9d33649b0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (Reemplazar vista previa) " +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..3a9e5d6bde --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Ir al símbolo en el área de trabajo...", + "name": "Buscar", + "showSearchViewlet": "Mostrar búsqueda", + "view": "Ver", + "findInFiles": "Buscar en archivos", + "openAnythingHandlerDescription": "Ir al archivo", + "openSymbolDescriptionNormal": "Ir al símbolo en el área de trabajo", + "searchOutputChannelTitle": "Buscar", + "searchConfigurationTitle": "Buscar", + "exclude": "Configure patrones globales para excluir archivos y carpetas de las búsquedas. Hereda todos los patrones globales de la configuración files.exclude.", + "exclude.boolean": "El patrón global con el que se harán coincidir las rutas de acceso de los archivos. Establézcalo en true o false para habilitarlo o deshabilitarlo.", + "exclude.when": "Comprobación adicional de los elementos del mismo nivel de un archivo coincidente. Use $(nombreBase) como variable para el nombre de archivo que coincide.", + "useRipgrep": "Controla si debe utilizarse ripgrep en la búsqueda de texto", + "useIgnoreFilesByDefault": "Controla si usar los archivos .gitignore y .ignore de forma predeterminada al buscar en una nueva área de trabajo.", + "search.quickOpen.includeSymbols": "Configurar para incluir los resultados de una búsqueda global de símbolos en los resultados de archivos de Quick Open." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..22ef430f44 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "Mostrar siguiente búsqueda de patrón include", + "previousSearchIncludePattern": "Mostrar búsqueda anterior de patrón include ", + "nextSearchExcludePattern": "Mostrar siguiente búsqueda de patrón exclude ", + "previousSearchExcludePattern": "Mostrar búsqueda anterior de patrón exclude ", + "nextSearchTerm": "Mostrar siguiente término de búsqueda", + "previousSearchTerm": "Mostrar anterior término de búsqueda", + "focusNextInputBox": "Centrarse en el siguiente cuadro de entrada", + "focusPreviousInputBox": "Centrarse en el anterior cuadro de entrada", + "replaceInFiles": "Reemplazar en archivos", + "findInWorkspace": "Buscar en área de trabajo...", + "findInFolder": "Buscar en carpeta...", + "RefreshAction.label": "Actualizar", + "ClearSearchResultsAction.label": "Borrar resultados de la búsqueda", + "FocusNextSearchResult.label": "Centrarse en el siguiente resultado de la búsqueda", + "FocusPreviousSearchResult.label": "Centrarse en el anterior resultado de la búsqueda", + "RemoveAction.label": "Quitar", + "file.replaceAll.label": "Reemplazar todo", + "match.replace.label": "Reemplazar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..356745603a --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "Otros archivos", + "searchFileMatches": "{0} archivos encontrados", + "searchFileMatch": "{0} archivo encontrado", + "searchMatches": "{0} coincidencias encontradas", + "searchMatch": "{0} coincidencia encontrada", + "folderMatchAriaLabel": "{0} coincidencias en la carpeta raíz {1}, resultados de la búsqueda", + "fileMatchAriaLabel": "{0} coincidencias en el archivo {1} de la carpeta {2}, resultados de la búsqueda", + "replacePreviewResultAria": "Reemplazar el termino {0} con {1} en la columna con posición {2} en la línea de texto {3}", + "searchResultAria": "Encontró el término {0} en la columna de posición {1} en la línea con el texto {2}." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..444601bc55 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "Alternar detalles de la búsqueda", + "searchScope.includes": "archivos para incluir", + "label.includes": "Buscar patrones de inclusión", + "searchScope.excludes": "archivos para excluir", + "label.excludes": "Buscar patrones de exclusión", + "replaceAll.confirmation.title": "Reemplazar todo", + "replaceAll.confirm.button": "Reemplazar", + "replaceAll.occurrence.file.message": "{0} aparición reemplazada en {1} archivo por \"{2}\".", + "removeAll.occurrence.file.message": "{0} aparición reemplazada en {1} archivo.", + "replaceAll.occurrence.files.message": "{0} aparición reemplazada en {1} archivos por \"{2}\".", + "removeAll.occurrence.files.message": "{0} aparición reemplazada en {1} archivos.", + "replaceAll.occurrences.file.message": "{0} apariciones reemplazadas en {1} archivo por \"{2}\".", + "removeAll.occurrences.file.message": "{0} apariciones reemplazadas en {1} archivo.", + "replaceAll.occurrences.files.message": "{0} apariciones reemplazadas en {1} archivos por \"{2}\".", + "removeAll.occurrences.files.message": "{0} apariciones reemplazadas en {1} archivos.", + "removeAll.occurrence.file.confirmation.message": "¿Reemplazar {0} aparición en {1} archivo por \"{2}\"?", + "replaceAll.occurrence.file.confirmation.message": "¿Reemplazar {0} aparición en {1} archivo?", + "removeAll.occurrence.files.confirmation.message": "¿Reemplazar {0} aparición en {1} archivos por \"{2}\"?", + "replaceAll.occurrence.files.confirmation.message": "¿Reemplazar {0} aparición en {1} archivos?", + "removeAll.occurrences.file.confirmation.message": "¿Reemplazar {0} apariciones en {1} archivo por \"{2}\"?", + "replaceAll.occurrences.file.confirmation.message": "¿Reemplazar {0} apariciones en {1} archivo?", + "removeAll.occurrences.files.confirmation.message": "¿Reemplazar {0} apariciones en {1} archivos por \"{2}\"?", + "replaceAll.occurrences.files.confirmation.message": "¿Reemplazar {0} apariciones en {1} archivos?", + "treeAriaLabel": "Resultados de la búsqueda", + "searchPathNotFoundError": "No se encuentra la ruta de búsqueda: {0}", + "searchMaxResultsWarning": "El conjunto de resultados solo contiene un subconjunto de todas las coincidencias. Sea más específico en la búsqueda para acotar los resultados.", + "searchCanceled": "La búsqueda se canceló antes de poder encontrar resultados - ", + "noResultsIncludesExcludes": "No se encontraron resultados en '{0}' con exclusión de '{1}' - ", + "noResultsIncludes": "No se encontraron resultados en '{0}' - ", + "noResultsExcludes": "No se encontraron resultados con exclusión de '{0}' - ", + "noResultsFound": "No se encontraron resultados. Revise la configuración de exclusiones y archivos omitidos - ", + "rerunSearch.message": "Buscar de nuevo", + "rerunSearchInAll.message": "Buscar de nuevo en todos los archivos", + "openSettings.message": "Abrir configuración", + "openSettings.learnMore": "Más información", + "ariaSearchResultsStatus": "La búsqueda devolvió {0} resultados en {1} archivos", + "search.file.result": "{0} resultado en {1} archivo", + "search.files.result": "{0} resultado en {1} archivos", + "search.file.results": "{0} resultados en {1} archivo", + "search.files.results": "{0} resultados en {1} archivos", + "searchWithoutFolder": "Aún no ha abierto una carpeta. Solo se busca en los archivos abiertos en este momento - ", + "openFolder": "Abrir carpeta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..b9899342f0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "Reemplazar todo (Enviar búsqueda para habilitar)", + "search.action.replaceAll.enabled.label": "Reemplazar todo", + "search.replace.toggle.button.title": "Alternar reemplazar", + "label.Search": "Búsqueda: Escriba el término de búsqueda y presione Entrar para buscar o Esc para cancelar", + "search.placeHolder": "Buscar", + "label.Replace": "Reemplazar: Escriba el término de reemplazo y presione Intro para obtener una vista previa o Escape para cancelar la acción", + "search.replace.placeHolder": "Reemplazar", + "regexp.validationFailure": "La expresión coincide con todo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/esn/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..030bc78f1c --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "Ninguna carpeta en el espacio de trabajo tiene el nombre: {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..1c01226bee --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "Aporta fragmentos de código.", + "vscode.extension.contributes.snippets-language": "Identificador del lenguaje al que se aporta este fragmento de código.", + "vscode.extension.contributes.snippets-path": "Ruta de acceso del archivo de fragmentos de código. La ruta es relativa a la carpeta de extensión y normalmente empieza por \"./snippets/\".", + "invalid.language": "Lenguaje desconocido en \"contributes.{0}.language\". Valor proporcionado: {1}", + "invalid.path.0": "Se esperaba una cadena en \"contributes.{0}.path\". Valor proporcionado: {1}", + "invalid.path.1": "Se esperaba que \"contributes.{0}.path\" ({1}) se incluyera en la carpeta de la extensión ({2}). Esto puede hacer que la extensión no sea portátil.", + "badVariableUse": "Es muy probable que el fragmento de código \"{0}\" confunda las variables de fragmento de código y los marcadores de posición de fragmento de código. Consulte https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax para más informacion." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..226a5b8f21 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "Insertar fragmento de código" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..88e240479b --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "Seleccione lenguaje para el fragmento", + "openSnippet.errorOnCreate": "No se puede crear {0}", + "openSnippet.label": "Abrir fragmentos de código del usuario", + "preferences": "Preferencias", + "snippetSchema.json.default": "Fragmento de código vacío", + "snippetSchema.json": "Configuración de fragmento de código del usuario", + "snippetSchema.json.prefix": "El prefijo que se debe usar al seleccionar el fragmento de código en Intellisense", + "snippetSchema.json.body": "El contenido del fragmento de código. Use \"$1', \"${1:defaultText}\" para definir las posiciones del cursor, use \"$0\" para la posición final del cursor. Inserte valores de variable con \"${varName}\" y \"${varName:defaultText}\", por ejemplo, \"This is file: $TM_FILENAME\".", + "snippetSchema.json.description": "La descripción del fragmento de código." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..cbb8ef07ec --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "Fragmento de código del usuario", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0}, {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..14023880ce --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "Inserta fragmentos de código cuando el prefijo coincide. Funciona mejor si la opción 'quickSuggestions' no está habilitada." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..6e8bcc773a --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "Ayúdenos a mejorar nuestro soporte para {0}", + "takeShortSurvey": "Realizar una breve encuesta", + "remindLater": "Recordármelo más tarde", + "neverAgain": "No volver a mostrar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..4c6b92bb19 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "¿Le importaría realizar una breve encuesta de opinión?", + "takeSurvey": "Realizar encuesta", + "remindLater": "Recordármelo más tarde", + "neverAgain": "No volver a mostrar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..42bf1197e8 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Escribir el nombre de una tarea de compilación", + "noTasksMatching": "No tasks matching", + "noTasksFound": "No se encontraron tareas de compilación" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..926984e637 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, tareas", + "recentlyUsed": "Tareas usadas recientemente", + "configured": "tareas configuradas", + "detected": "tareas detectadas", + "customizeTask": "Configurar tarea" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..0f2bc3d084 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Escriba el nombre de una tarea para reiniciar", + "noTasksMatching": "No tasks matching", + "noTasksFound": "No se encontró ninguna tarea para reiniciar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..5019dae191 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Escribir el nombre de una tarea para ejecutar", + "noTasksMatching": "No tasks matching", + "noTasksFound": "No se encontraron tareas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..f167964ff5 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Type the name of a task to terminate", + "noTasksMatching": "No tasks matching", + "noTasksFound": "No tasks to terminate found" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..e8227c2d8c --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Escribir el nombre de una tarea de prueba", + "noTasksMatching": "No tasks matching", + "noTasksFound": "No se encontraron tareas de prueba" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..6d8124e66f --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Advertencia: options.cwd debe ser de tipo cadena. Se ignora el valor {0}.", + "ConfigurationParser.noargs": "Error: Los argumentos de comando deben ser una matriz de cadenas. El valor proporcionado es: {0}", + "ConfigurationParser.noShell": "Advertencia: La configuración del shell solo se admite al ejecutar tareas en el terminal.", + "ConfigurationParser.noName": "Error: El buscador de coincidencias de problemas del ámbito de declaración debe tener un nombre: {0}", + "ConfigurationParser.unknownMatcherKind": "Advertencia: El buscador de coincidencias de problemas definido se desconoce. Los tipos admitidos son string | ProblemMatcher | (string | ProblemMatcher). {0}", + "ConfigurationParser.invalidVaraibleReference": "Error: Referencia a problemMatcher no válida: {0}", + "ConfigurationParser.noTaskName": "Error: Las tareas deben proporcionar una propiedad taskName. La tarea se ignorará. {0}", + "taskConfiguration.shellArgs": "Advertencia: La tarea \"{0}\" es un comando de shell y su nombre de comando o uno de sus argumentos tiene espacios sin escape. Para asegurarse de que la línea de comandos se cite correctamente, combine mediante fusión los argumentos en el comando.", + "taskConfiguration.noCommandOrDependsOn": "Error: La tarea \"{0}\" no especifica un comando ni una propiedad dependsOn. La tarea se ignorará. Su definición es: \n{1}", + "taskConfiguration.noCommand": "Error: La tarea \"{0}\" no define un comando. La tarea se ignorará. Su definición es: {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..a5513d02af --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "Tipo de tarea real", + "TaskDefinition.properties": "Propiedades adicionales del tipo de tarea", + "TaskTypeConfiguration.noType": "La configuración del tipo de tarea no tiene la propiedad \"taskType\" requerida.", + "TaskDefinitionExtPoint": "Aporta tipos de tarea" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..2e4f28b650 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": "Ejecuta el comando de compilación de .NET Core", + "msbuild": "Ejecuta el destino de compilación", + "externalCommand": "Ejemplo para ejecutar un comando arbitrario externo", + "Maven": "Ejecuta los comandos comunes de Maven." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..80ee734213 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "Opciones de comando adicionales", + "JsonSchema.options.cwd": "Directorio de trabajo actual del script o el programa ejecutado. Si se omite, se usa la raíz del área de trabajo actual de Code.", + "JsonSchema.options.env": "Entorno del shell o el programa ejecutado. Si se omite, se usa el entorno del proceso primario.", + "JsonSchema.shellConfiguration": "Configura el shell que se usará.", + "JsonSchema.shell.executable": "Shell que se va a usar.", + "JsonSchema.shell.args": "Argumentos de shell.", + "JsonSchema.command": "El comando que se va a ejecutar. Puede ser un programa externo o un comando shell.", + "JsonSchema.tasks.args": "Argumentos que se pasan al comando cuando se invoca esta tarea.", + "JsonSchema.tasks.taskName": "Nombre de la tarea", + "JsonSchema.tasks.windows": "Configuración de comando específico de Windows", + "JsonSchema.tasks.mac": "Configuración de comando específico de Mac", + "JsonSchema.tasks.linux": "Configuración de comando específico de Linux", + "JsonSchema.tasks.suppressTaskName": "Controla si el nombre de la tarea se agrega como argumento al comando. Si se omite, se usa el valor definido globalmente.", + "JsonSchema.tasks.showOutput": "Controla si la salida de la tarea en ejecución se muestra o no. Si se omite, se usa el valor definido globalmente.", + "JsonSchema.echoCommand": "Controla si el comando ejecutado se muestra en la salida. El valor predeterminado es false.", + "JsonSchema.tasks.watching.deprecation": "En desuso. Utilice isBackground en su lugar.", + "JsonSchema.tasks.watching": "Indica si la tarea ejecutada se mantiene activa e inspecciona el sistema de archivos.", + "JsonSchema.tasks.background": "Indica si la tarea ejecutada se mantiene y está en ejecución en segundo plano.", + "JsonSchema.tasks.promptOnClose": "Indica si se pregunta al usuario cuando VS Code se cierra con una tarea en ejecución.", + "JsonSchema.tasks.build": "Asigna esta tarea al comando de compilación predeterminado de Code.", + "JsonSchema.tasks.test": "Asigna esta tarea al comando de prueba predeterminado de Code.", + "JsonSchema.tasks.matchers": "Buscadores de coincidencias de problemas que se van a usar. Puede ser una definición de cadena o de buscador de coincidencias de problemas, o bien una matriz de cadenas y de buscadores de coincidencias de problemas.", + "JsonSchema.args": "Argumentos adicionales que se pasan al comando.", + "JsonSchema.showOutput": "Controla si la salida de la tarea en ejecución se muestra o no. Si se omite, se usa \"always\".", + "JsonSchema.watching.deprecation": "En desuso. Utilice isBackground en su lugar.", + "JsonSchema.watching": "Indica si la tarea ejecutada se mantiene activa e inspecciona el sistema de archivos.", + "JsonSchema.background": "Indica si la tarea ejecutada se mantiene y está en ejecución en segundo plano.", + "JsonSchema.promptOnClose": "Indica si se pregunta al usuario cuando VS Code se cierra con una tarea en ejecución en segundo plano.", + "JsonSchema.suppressTaskName": "Controla si el nombre de la tarea se agrega como argumento al comando. El valor predeterminado es false.", + "JsonSchema.taskSelector": "Prefijo para indicar que un argumento es una tarea.", + "JsonSchema.matchers": "Buscadores de coincidencias de problemas que se van a usar. Puede ser una definición de cadena o de buscador de coincidencias de problemas, o bien una matriz de cadenas y de buscadores de coincidencias de problemas.", + "JsonSchema.tasks": "Configuraciones de tarea. Suele enriquecerse una tarea ya definida en el ejecutor de tareas externo." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..f87e3c28c9 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "Número de versión de la configuración", + "JsonSchema._runner": "El ejecutador se ha graduado. Use el ejecutador oficial correctamente", + "JsonSchema.runner": "Define si la tarea se ejecuta como un proceso y la salida se muestra en la ventana de salida o dentro del terminal.", + "JsonSchema.windows": "Configuración de comando específico de Windows", + "JsonSchema.mac": "Configuración de comando específico de Mac", + "JsonSchema.linux": "Configuración de comando específico de Linux", + "JsonSchema.shell": "Especifica si el comando es un comando shell o un programa externo. Si se omite, el valor predeterminado es false." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..af4faa0cee --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "Especifica si el comando es un comando shell o un programa externo. Si se omite, el valor predeterminado es false.", + "JsonSchema.tasks.isShellCommand.deprecated": "La propiedad isShellCommand está en desuso. En su lugar, utilice la propiedad type de la tarea y la propiedad shell de las opciones. Vea también las notas de versión 1.14. ", + "JsonSchema.tasks.dependsOn.string": "Otra tarea de la que depende esta tarea.", + "JsonSchema.tasks.dependsOn.array": "Las otras tareas de las que depende esta tarea.", + "JsonSchema.tasks.presentation": "Configura el panel que se utiliza para presentar las salidas de la tarea y lee sus entradas.", + "JsonSchema.tasks.presentation.echo": "Controla si se presenta en el panel un eco del comando ejecutado. El valor predeterminado es verdadero.", + "JsonSchema.tasks.presentation.focus": "Controla si el panel recibe el foco. El valor predeterminado es falso. Si se establece a verdadero, el panel además se revela.", + "JsonSchema.tasks.presentation.reveal.always": "Revela siempre el terminal cuando se ejecuta esta tarea.", + "JsonSchema.tasks.presentation.reveal.silent": "Solo revela el terminal si no hay ningún buscador de coincidencias de problemas asociado con la tarea y se producen errores al ejecutarla.", + "JsonSchema.tasks.presentation.reveal.never": "No revela nunca el teminal cuando se ejecuta la tarea.", + "JsonSchema.tasks.presentation.reveals": "Controla si el panel que ejecuta la tarea se revela o no. El valor predeterminado es \"siempre\".", + "JsonSchema.tasks.presentation.instance": "Controla si el panel se comparte entre tareas, está dedicado a esta tarea, o se crea uno nuevo por cada ejecución.", + "JsonSchema.tasks.terminal": "La propiedad terminal está en desuso. En su lugar, utilice presentation.", + "JsonSchema.tasks.group.kind": "El grupo de ejecución de la tarea.", + "JsonSchema.tasks.group.isDefault": "Define si la tarea es la tarea predeterminada del grupo.", + "JsonSchema.tasks.group.defaultBuild": "Marca las tareas como tarea de compilación predeterminada.", + "JsonSchema.tasks.group.defaultTest": "Marca las tareas como la tarea de prueba predeterminada.", + "JsonSchema.tasks.group.build": "Marca las tareas como tarea de compilación a través del comando 'Ejecutar tarea de compilación'.", + "JsonSchema.tasks.group.test": "Marca las tareas como tarea de prueba accesible a través del comando \"Ejecutar tarea de compilación\". ", + "JsonSchema.tasks.group.none": "No asigna la tarea a ningún grupo", + "JsonSchema.tasks.group": "Define a qué grupo de ejecución pertenece esta tarea. Admite \"compilación\" para agregarla al grupo de compilación y \"prueba\" para agregarla al grupo de prueba.", + "JsonSchema.tasks.type": "Define si la tarea se ejecuta como un proceso o como un comando dentro de un shell. El valor predeterminado es proceso.", + "JsonSchema.version": "El número de versión de la configuración.", + "JsonSchema.tasks.identifier": "Un identificador definido por el usuario para hacer referencia a la tarea en launch.json o una cláusula dependsOn.", + "JsonSchema.tasks.taskLabel": "La etiqueta de la tarea", + "JsonSchema.tasks.taskName": "Nombre de la tarea", + "JsonSchema.tasks.taskName.deprecated": "La propiedad name de la tarea está en desuso. En su lugar, utilice la propiedad label. ", + "JsonSchema.tasks.background": "Indica si la tarea ejecutada se mantiene y está en ejecución en segundo plano.", + "JsonSchema.tasks.promptOnClose": "Indica si se pregunta al usuario cuando VS Code se cierra con una tarea en ejecución.", + "JsonSchema.tasks.matchers": "Buscadores de coincidencias de problemas que se van a usar. Puede ser una definición de cadena o de buscador de coincidencias de problemas, o bien una matriz de cadenas y de buscadores de coincidencias de problemas.", + "JsonSchema.customizations.customizes.type": "El tipo de tarea que se va a personalizar", + "JsonSchema.tasks.customize.deprecated": "La propiedad customize está en desuso. Consulte las notas de la versión 1.14 sobre cómo migrar al nuevo enfoque de personalización de tareas.", + "JsonSchema.tasks.showOputput.deprecated": "La propiedad showOutput está en desuso. Utilice la propiedad reveal dentro de la propiedad presentation en su lugar. Vea también las notas de la versión 1.14.", + "JsonSchema.tasks.echoCommand.deprecated": "La propiedad echoCommand está en desuso. Utilice la propiedad echo dentro de la propiedad presentation en su lugar. Vea también las notas de la versión 1.14.", + "JsonSchema.tasks.suppressTaskName.deprecated": "La propiedad suppressTaskName está en desuso. En lugar de usar esta propiedad, inserte el comando con los argumentos en la tarea. Vea también las notas de la versión 1.14.", + "JsonSchema.tasks.isBuildCommand.deprecated": "La propiedad isBuildCommand está en desuso. Utilice la propiedad group en su lugar. Vea también las notas de la versión 1.14.", + "JsonSchema.tasks.isTestCommand.deprecated": "La propiedad isTestCommand está en desuso. Utilice la propiedad group en su lugar. Vea también las notas de la versión 1.14.", + "JsonSchema.tasks.taskSelector.deprecated": "La propiedad taskSelector está en desuso. En lugar de usar esta propiedad, inserte el comando con los argumentos en la tarea. Vea también las notas de la versión 1.14.", + "JsonSchema.windows": "Configuración de comando específico de Windows", + "JsonSchema.mac": "Configuración de comando específico de Mac", + "JsonSchema.linux": "Configuración de comando específico de Linux" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..33f08c9e2b --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "Tareas", + "ConfigureTaskRunnerAction.noWorkspace": "Las tareas solo están disponibles en una carpeta del área de trabajo.", + "ConfigureTaskRunnerAction.quickPick.template": "Seleccionar un ejecutador de tareas", + "ConfigureTaskRunnerAction.autoDetecting": "Detectando tareas automáticamente para {0}", + "ConfigureTaskRunnerAction.autoDetect": "Error de detección automática del sistema de tareas. Se usa la plantilla predeterminada. Consulte el resultado de la tarea para obtener más detalles", + "ConfigureTaskRunnerAction.autoDetectError": "Error de detección automática del sistema de tareas. Consulte el resultado de la tarea para obtener más detalles", + "ConfigureTaskRunnerAction.failed": "No se puede crear el archivo \"tasks.json\" dentro de la carpeta \".vscode\". Consulte el resultado de la tarea para obtener más detalles.", + "ConfigureTaskRunnerAction.label": "Configurar ejecutor de tareas", + "ConfigureBuildTaskAction.label": "Configurar tarea de compilación", + "CloseMessageAction.label": "Cerrar", + "ShowTerminalAction.label": "Ver terminal", + "problems": "Problemas", + "manyMarkers": "Más de 99", + "runningTasks": "Mostrar tareas en ejecución", + "tasks": "Tareas", + "TaskSystem.noHotSwap": "Para cambiar el motor de ejecución de tareas, es necesario recargar la ventana", + "TaskService.noBuildTask1": "No se ha definido ninguna tarea de compilación. Marque una tarea con \"isBuildCommand\" en el archivo tasks.json.", + "TaskService.noBuildTask2": "No se ha definido ninguna tarea de compilación. Marque una tarea con un grupo \"build\" en el archivo tasks.json. ", + "TaskService.noTestTask1": "No se ha definido ninguna tarea de prueba. Marque una tarea con \"isTestCommand\" en el archivo tasks.json.", + "TaskService.noTestTask2": "No se ha definido ninguna tarea de prueba. Marque una tarea con \"test\" en el archivo tasks.json.", + "TaskServer.noTask": "No se encuentra la tarea {0} que se ha solicitado para ejecutarla.", + "TaskService.attachProblemMatcher.continueWithout": "Continuar sin examinar la salida de la tarea", + "TaskService.attachProblemMatcher.never": "No examinar nunca la salida de la tarea", + "TaskService.attachProblemMatcher.learnMoreAbout": "Más información acerca del examen de la salida de la tarea", + "selectProblemMatcher": "Seleccione qué tipo de errores y advertencias deben buscarse durante el examen de la salida de la tarea", + "customizeParseErrors": "La configuración actual de tareas contiene errores. Antes de personalizar una tarea, corrija los errores.", + "moreThanOneBuildTask": "Hay muchas tareas de compilación definidas en el archivo tasks.json. Se ejecutará la primera.\n", + "TaskSystem.activeSame.background": "La tarea \"{0}\" ya está activa en segundo plano. Para terminarla, use la opción \"Terminar tarea\" del menú Tareas.", + "TaskSystem.activeSame.noBackground": "La tarea \"{0}\" ya está activa. Para terminarla, use la opción \"Terminar tarea\" del menú Tareas.", + "TaskSystem.active": "Ya hay una tarea en ejecución. Finalícela antes de ejecutar otra tarea.", + "TaskSystem.restartFailed": "No se pudo terminar y reiniciar la tarea {0}", + "TaskSystem.configurationErrors": "Error: La configuración de la tarea proporcionada tiene errores de validación y no se puede usar. Corrija los errores primero.", + "TaskSystem.invalidTaskJson": "Error: El contenido del archivo tasks.json tiene errores de sintaxis. Corríjalos antes de ejecutar una tarea.", + "TaskSystem.runningTask": "Hay una tarea en ejecución. ¿Quiere finalizarla?", + "TaskSystem.terminateTask": "&&Finalizar tarea", + "TaskSystem.noProcess": "La tarea iniciada ya no existe. Si la tarea generó procesos en segundo plano al salir de VS Code, puede dar lugar a procesos huérfanos. Para evitarlo, inicie el último proceso en segundo plano con una marca de espera.", + "TaskSystem.exitAnyways": "&&Salir de todos modos", + "TerminateAction.label": "Finalizar tarea", + "TaskSystem.unknownError": "Error durante la ejecución de una tarea. Consulte el registro de tareas para obtener más detalles.", + "TaskService.noWorkspace": "Las tareas solo están disponibles en una carpeta del área de trabajo.", + "recentlyUsed": "Tareas usadas recientemente", + "configured": "tareas configuradas", + "detected": "tareas detectadas", + "TaskService.fetchingBuildTasks": "Obteniendo tareas de compilación...", + "TaskService.noBuildTaskTerminal": "No se encontraron Tareas de Compilación. Pulse 'Configurar Tarea de Compilación' para definir una.", + "TaskService.pickBuildTask": "Seleccione la tarea de compilación para ejecutar", + "TaskService.fetchingTestTasks": "Capturando tareas de prueba...", + "TaskService.noTestTaskTerminal": "No se encontraron tareas de prueba. Presione \"Configurar ejecutor de tareas\" para definir una.", + "TaskService.pickTestTask": "Seleccione la tarea de prueba para ejecutar", + "TaskService.noTaskRunning": "Ninguna tarea se está ejecutando actualmente.", + "TaskService.tastToTerminate": "Seleccione la tarea para finalizar", + "TerminateAction.noProcess": "El proceso iniciado ya no existe. Si la tarea generó procesos en segundo plano al salir de VS Code, puede dar lugar a procesos huérfanos.", + "TerminateAction.failed": "No se pudo finalizar la tarea en ejecución", + "TaskService.noTaskToRestart": "No hay tareas para reiniciar.", + "TaskService.tastToRestart": "Seleccione la tarea para reiniciar", + "TaskService.defaultBuildTaskExists": "{0} ya se ha marcado como la tarea de compilación predeterminada.", + "TaskService.pickDefaultBuildTask": "Seleccione la tarea que se va a utilizar como tarea de compilación predeterminada", + "TaskService.defaultTestTaskExists": "{0} ya se ha marcado como la tarea de prueba predeterminada.", + "TaskService.pickDefaultTestTask": "Seleccione la tarea que se va a usar como la tarea de prueba predeterminada ", + "TaskService.noTaskIsRunning": "No hay ninguna tarea en ejecución.", + "TaskService.pickShowTask": "Seleccione la tarea de la que desea ver la salida", + "ShowLogAction.label": "Mostrar registro de tareas", + "RunTaskAction.label": "Ejecutar tarea", + "RestartTaskAction.label": "Reiniciar la tarea en ejecución", + "ShowTasksAction.label": "Mostrar tareas en ejecución", + "BuildAction.label": "Ejecutar tarea de compilación", + "TestAction.label": "Ejecutar tarea de prueba", + "ConfigureDefaultBuildTask.label": "Configurar tarea de compilación predeterminada", + "ConfigureDefaultTestTask.label": "Configurar tarea de prueba predeterminada", + "quickOpen.task": "Ejecutar tarea" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..6ace83cd22 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "Tareas" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..4493239b1f --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "Error desconocido durante la ejecución de una tarea. Vea el registro de resultados de la tarea para obtener más detalles.", + "TerminalTaskSystem.terminalName": "Tarea - {0}", + "reuseTerminal": "Las tareas reutilizarán el terminal, presione cualquier tecla para cerrarlo.", + "TerminalTaskSystem": "No se puede ejecutar un comando shell en una unidad UNC.", + "unkownProblemMatcher": "No puede resolver el comprobador de problemas {0}. Será omitido." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..7fa2604bbd --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "Al ejecutar --tasks-simple de Gulp no se enumera ninguna tarea. ¿Ha ejecutado \"npm install\"?", + "TaskSystemDetector.noJakeTasks": "Al ejecutar --tasks de jake no se enumera ninguna tarea. ¿Ha ejecutado \"npm install\"?", + "TaskSystemDetector.noGulpProgram": "Gulp no está instalado en el sistema. Ejecute \"npm install -g gulp\" para instalarlo.", + "TaskSystemDetector.noJakeProgram": "Jake no está instalado en el sistema. Ejecute \"npm install -g jake\" para instalarlo.", + "TaskSystemDetector.noGruntProgram": "Grunt no está instalado en el sistema. Ejecute \"npm install -g grunt\" para instalarlo.", + "TaskSystemDetector.noProgram": "El programa {0} no se encontró. El mensaje es {1}", + "TaskSystemDetector.buildTaskDetected": "Se detectó una tarea de compilación llamada '{0}'.", + "TaskSystemDetector.testTaskDetected": "Se detectó una tarea de prueba llamada '{0}'." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..ef8665cc49 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "Error desconocido durante la ejecución de una tarea. Vea el registro de resultados de la tarea para obtener más detalles.", + "TaskRunnerSystem.watchingBuildTaskFinished": "La inspección de las tareas de compilación ha finalizado.", + "TaskRunnerSystem.childProcessError": "Failed to launch external program {0} {1}.", + "TaskRunnerSystem.cancelRequested": "La tarea '{0}' se finalizó por solicitud del usuario.", + "unkownProblemMatcher": "No puede resolver el comprobador de problemas {0}. Será omitido" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..a2a1a3460c --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Advertencia: options.cwd debe ser de tipo cadena. Se ignora el valor {0}.", + "ConfigurationParser.noargs": "Error: Los argumentos de comando deben ser una matriz de cadenas. El valor proporcionado es: {0}", + "ConfigurationParser.noShell": "Advertencia: La configuración del shell solo se admite al ejecutar tareas en el terminal.", + "ConfigurationParser.noName": "Error: El buscador de coincidencias de problemas del ámbito de declaración debe tener un nombre: {0}", + "ConfigurationParser.unknownMatcherKind": "Advertencia: El buscador de coincidencias de problemas definido se desconoce. Los tipos admitidos son string | ProblemMatcher | (string | ProblemMatcher). {0}", + "ConfigurationParser.invalidVaraibleReference": "Error: Referencia a problemMatcher no válida: {0}", + "ConfigurationParser.noTaskType": "Error: Las tareas deben tener una propiedad type. La configuración se omitirá.\n{0}\n", + "ConfigurationParser.noTypeDefinition": "Error: No hay ningún tipo de tarea \"{0}\" registrado. ¿Omitió la instalación de una extensión que proporciona un proveedor de tareas correspondiente?", + "ConfigurationParser.notCustom": "Error: Las tareas no se declaran como una tarea personalizada. La configuración se omitirá.\n{0}\n", + "ConfigurationParser.noTaskName": "Error: Las tareas deben proporcionar una propiedad taskName. La tarea se ignorará. {0}", + "taskConfiguration.shellArgs": "Advertencia: La tarea \"{0}\" es un comando de shell y su nombre de comando o uno de sus argumentos tiene espacios sin escape. Para asegurarse de que la línea de comandos se cite correctamente, combine mediante fusión los argumentos en el comando.", + "taskConfiguration.noCommandOrDependsOn": "Error: La tarea \"{0}\" no especifica un comando ni una propiedad dependsOn. La tarea se ignorará. Su definición es: \n{1}", + "taskConfiguration.noCommand": "Error: La tarea \"{0}\" no define un comando. La tarea se ignorará. Su definición es: {1}", + "TaskParse.noOsSpecificGlobalTasks": "La versión de tarea 2.0.0 no admite tareas específicas de SO globales. Conviértalas en una tarea con un comando específico de SO. Estas son las tareas afectadas:\n{0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..53c7a8ebaa --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0}, selector de terminal", + "termCreateEntryAriaLabel": "{0}, crear nueva terminal", + "'workbench.action.terminal.newplus": "$(plus) crear nueva Terminal integrada", + "noTerminalsMatching": "No hay terminales coincidentes", + "noTerminalsFound": "No hay terminales abiertos" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..74a3eb1d81 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "Mostrar todos los terminales abiertos", + "terminalIntegratedConfigurationTitle": "Terminal integrado", + "terminal.integrated.shell.linux": "La ruta de acceso del shell que el terminal usa en Linux.", + "terminal.integrated.shellArgs.linux": "Los argumentos de la línea de comandos que se usarán en el terminal de Linux.", + "terminal.integrated.shell.osx": "La ruta de acceso del shell que el terminal usa en OS X.", + "terminal.integrated.shellArgs.osx": "Los argumentos de la línea de comandos que se usarán en el terminal de OS X.", + "terminal.integrated.shell.windows": "Ruta de acceso del shell que el terminal utiliza en Windows. Cuando se usan shells distribuidos con Windows (cmd, PowerShell o Bash en Ubuntu).", + "terminal.integrated.shellArgs.windows": "Argumentos de la línea de comandos que se usan cuando se utiliza el terminal Windows.", + "terminal.integrated.rightClickCopyPaste": "Si está configurado, impedirá que el menú contextual aparezca al hacer clic con el botón derecho dentro del terminal; en su lugar, copiará si hay una selección y pegará si no hay ninguna selección.", + "terminal.integrated.fontFamily": "Controla la familia de fuentes del terminal, que está establecida de manera predeterminada en el valor de editor.fontFamily.", + "terminal.integrated.fontLigatures": "Controla si las ligaduras tipográficas están habilitadas en el terminal.", + "terminal.integrated.fontSize": "Controla el tamaño de la fuente en píxeles del terminal.", + "terminal.integrated.lineHeight": "Controla el alto de línea del terminal. Este número se multiplica por el tamaño de fuente del terminal para obtener el alto de línea real en píxeles.", + "terminal.integrated.enableBold": "Indica si debe habilitarse el texto en negrita en el terminal. Requiere soporte del terminal Shell.", + "terminal.integrated.cursorBlinking": "Controla si el cursor del terminal parpadea.", + "terminal.integrated.cursorStyle": "Controla el estilo de cursor del terminal.", + "terminal.integrated.scrollback": "Controla la cantidad máxima de líneas que mantiene el terminal en su búfer.", + "terminal.integrated.setLocaleVariables": "Controla si las variables de configuración regional se definen al inicio del terminal. El valor predeterminado es true en OS X y false en las demás plataformas.", + "terminal.integrated.cwd": "Una ruta de acceso de inicio explícita en la que se iniciará el terminal; se utiliza como el directorio de trabajo actual (cwd) para el proceso de shell. Puede resultar especialmente útil en una configuración de área de trabajo si la raíz de directorio no es un cwd práctico.", + "terminal.integrated.confirmOnExit": "Indica si debe confirmarse a la salida si hay sesiones de terminal activas.", + "terminal.integrated.commandsToSkipShell": "Conjunto de identificadores de comando cuyos enlaces de teclado no se enviarán al shell, sino que siempre se controlarán con Code. Esto permite el uso de enlaces de teclado que normalmente consumiría el shell para funcionar igual que cuando el terminal no tiene el foco; por ejemplo, Ctrl+P para iniciar Quick Open.", + "terminal.integrated.env.osx": "Objeto con variables de entorno que se agregarán al proceso de VS Code para que las use el terminal en OS X", + "terminal.integrated.env.linux": "Objeto con variables de entorno que se agregarán al proceso de VS Code para que las use el terminal en Linux", + "terminal.integrated.env.windows": "Objeto con variables de entorno que se agregarán al proceso de VS Code para que las use el terminal en Windows", + "terminal": "Terminal", + "terminalCategory": "Terminal", + "viewCategory": "Ver" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..b58b58d4ba --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "Alternar terminal integrado", + "workbench.action.terminal.kill": "Terminar la instancia del terminal activo", + "workbench.action.terminal.kill.short": "Terminar el terminal", + "workbench.action.terminal.quickKill": "Terminar la instancia del terminal", + "workbench.action.terminal.copySelection": "Copiar selección", + "workbench.action.terminal.selectAll": "Seleccionar todo", + "workbench.action.terminal.deleteWordLeft": "Eliminar una palabra a la izquierda", + "workbench.action.terminal.deleteWordRight": "Eliminar una palabra a la derecha", + "workbench.action.terminal.new": "Crear nuevo terminal integrado", + "workbench.action.terminal.new.short": "Nuevo terminal", + "workbench.action.terminal.focus": "Enfocar terminal", + "workbench.action.terminal.focusNext": "Enfocar terminal siguiente", + "workbench.action.terminal.focusAtIndex": "Enfocar terminal {0}", + "workbench.action.terminal.focusPrevious": "Enfocar terminal anterior", + "workbench.action.terminal.paste": "Pegar en el terminal activo", + "workbench.action.terminal.DefaultShell": "Seleccionar el shell predeterminado", + "workbench.action.terminal.runSelectedText": "Ejecutar texto seleccionado en el terminal activo", + "workbench.action.terminal.runActiveFile": "Ejecutar el archivo activo en la terminal activa", + "workbench.action.terminal.runActiveFile.noFile": "Solo se pueden ejecutar en la terminal los archivos en disco", + "workbench.action.terminal.switchTerminalInstance": "Cambiar instancia del terminal", + "workbench.action.terminal.scrollDown": "Desplazar hacia abajo (línea)", + "workbench.action.terminal.scrollDownPage": "Desplazar hacia abajo (página)", + "workbench.action.terminal.scrollToBottom": "Desplazar al final", + "workbench.action.terminal.scrollUp": "Desplazar hacia arriba (línea)", + "workbench.action.terminal.scrollUpPage": "Desplazar hacia arriba (página)", + "workbench.action.terminal.scrollToTop": "Desplazar al principio", + "workbench.action.terminal.clear": "Borrar", + "workbench.action.terminal.allowWorkspaceShell": "Permitir la configuración del área de trabajo Shell", + "workbench.action.terminal.disallowWorkspaceShell": "No permitir la configuración del área de trabajo Shell", + "workbench.action.terminal.rename": "Cambiar nombre", + "workbench.action.terminal.rename.prompt": "Introducir nombre del terminal", + "workbench.action.terminal.focusFindWidget": "Foco Encontrar Widget", + "workbench.action.terminal.hideFindWidget": "Ocultar Encontrar Widget", + "nextTerminalFindTerm": "Mostrar siguiente término de búsqueda", + "previousTerminalFindTerm": "Mostrar término de búsqueda anterior", + "quickOpenTerm": "Terminal: Cambiar Terminal Activo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..aec1fb8be9 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "El color de fondo del terminal, esto permite colorear el terminal de forma diferente al panel.", + "terminal.foreground": "El color de primer plano del terminal.", + "terminalCursor.foreground": "Color de primer plano del cursor del terminal.", + "terminalCursor.background": "Color de fondo del cursor del terminal. Permite personalizar el color de un carácter solapado por un cursor de bloque.", + "terminal.ansiColor": "Color ANSI \"{0}\" en el terminal." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..f5a284e01b --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "¿Permite {0} (definido como valor del área de trabajo) que sea lanzado en el terminal?", + "allow": "Allow", + "disallow": "Disallow" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..4534d09463 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Buscar", + "placeholder.find": "Buscar", + "label.previousMatchButton": "Coincidencia anterior", + "label.nextMatchButton": "Coincidencia siguiente", + "label.closeButton": "Cerrar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..f4cd5c1717 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "El terminal no tiene ninguna selección para copiar", + "terminal.integrated.exitedWithCode": "El proceso del terminal finalizó con el código de salida: {0}", + "terminal.integrated.waitOnExit": "Presione cualquier tecla para cerrar el terminar", + "terminal.integrated.launchFailed": "No se pudo iniciar el comando de proceso terminal \"{0}{1}\" (código de salida: {2})" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..db739266e1 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "Alt + clic para seguir el vínculo", + "terminalLinkHandler.followLinkCmd": "Cmd + clic para abrir el vínculo", + "terminalLinkHandler.followLinkCtrl": "Ctrl + clic para abrir el vínculo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..58fbc7a8ba --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "Copiar", + "createNewTerminal": "Nuevo terminal", + "paste": "Pegar", + "selectAll": "Seleccionar todo", + "clear": "Borrar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..e0047fd9c9 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "Para cambiar el shell de terminal predeterminado, seleccione el botón Personalizar.", + "customize": "Personalizar", + "cancel": "Cancelar", + "never again": "De acuerdo, no volver a mostrar este mensaje", + "terminal.integrated.chooseWindowsShell": "Seleccione el shell de terminal que desee, puede cambiarlo más adelante en la configuración", + "terminalService.terminalCloseConfirmationSingular": "Hay una sesión de terminal activa, ¿quiere terminarla?", + "terminalService.terminalCloseConfirmationPlural": "Hay {0} sesiones de terminal activas, ¿quiere terminarlas?", + "yes": "Sí" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..7d84cd00c3 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "Tema de color", + "installColorThemes": "Instalar temas de color adicionales...", + "themes.selectTheme": "Seleccione el tema de color (flecha arriba/abajo para vista previa)", + "selectIconTheme.label": "Tema de icono de archivo", + "installIconThemes": "Instalar temas de icono de archivo adicionles...", + "noIconThemeLabel": "Ninguno", + "noIconThemeDesc": "Deshabilitar iconos de archivo", + "problemChangingIconTheme": "Problema al configurar el tema de icono: {0}", + "themes.selectIconTheme": "Seleccionar tema de icono de archivo", + "generateColorTheme.label": "General el tema de color desde la configuración actual", + "preferences": "Preferencias", + "developer": "Desarrollador" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..dccfc218b8 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "El área de trabajo contiene valores que solo pueden establecerse en Configuración de usuario. ({0})", + "openWorkspaceSettings": "Abrir configuración del área de trabajo", + "openDocumentation": "Más información", + "ignore": "Omitir" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/esn/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..b88c9a5a6c --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "Notas de la versión: {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..5e381206f6 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "Notas de la versión", + "updateConfigurationTitle": "Actualización", + "updateChannel": "Configure si recibirá actualizaciones automáticas de un canal de actualización. Es necesario reiniciar tras el cambio." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..a3bd74764d --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "Actualizar ahora", + "later": "Más tarde", + "unassigned": "sin asignar", + "releaseNotes": "Notas de la versión", + "showReleaseNotes": "Mostrar las notas de la versión", + "downloadNow": "Descargar ahora", + "read the release notes": "{0} v{1}. ¿Quiere leer las notas de la versión?", + "licenseChanged": "Los términos de licencia han cambiado, revíselos.", + "license": "Leer licencia", + "neveragain": "No volver a mostrar", + "64bitisavailable": "Ya está disponible {0} para Windows de 64 bits.", + "learn more": "Más información", + "updateIsReady": "Nueva actualización de {0} disponible.", + "thereIsUpdateAvailable": "Hay una actualización disponible.", + "updateAvailable": "{0} se actualizará después de reiniciarse.", + "noUpdatesAvailable": "Actualmente no hay actualizaciones disponibles.", + "commandPalette": "Paleta de comandos...", + "settings": "Configuración", + "keyboardShortcuts": "Métodos abreviados de teclado", + "selectTheme.label": "Tema de color", + "themes.selectIconTheme.label": "Tema de icono de archivo", + "not available": "Actualizaciones no disponibles", + "checkingForUpdates": "Buscando actualizaciones...", + "DownloadUpdate": "Descargar actualización disponible", + "DownloadingUpdate": "Descargando actualización...", + "InstallingUpdate": "Instalando actualización...", + "restartToUpdate": "Reiniciar para actualizar...", + "checkForUpdates": "Buscar actualizaciones..." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/esn/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..23a3e76afb --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} acciones", + "hideView": "Ocultar en la barra lateral" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/esn/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..908fadde98 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "views debe ser una mariz", + "requirestring": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "optstring": "la propiedad `{0}` se puede omitir o debe ser de tipo \"string\"", + "vscode.extension.contributes.view.id": "Identificador de la vista. Úselo para registrar un proveedor de datos mediante la API \"vscode.window.registerTreeDataProviderForView\". También para desencadenar la activación de su extensión al registrar el evento \"onView:${id}\" en \"activationEvents\".", + "vscode.extension.contributes.view.name": "Nombre de la vista en lenguaje natural. Será mostrado", + "vscode.extension.contributes.view.when": "Condición que se debe cumplir para mostrar esta vista", + "vscode.extension.contributes.views": "Aporta vistas al editor", + "views.explorer": "Vista del explorador", + "views.debug": "Vista de depuración", + "locationId.invalid": "`{0}` no es una ubicación de vista válida" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/esn/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..b9e0dde1b1 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "Mostrar todos los comandos", + "watermark.quickOpen": "Ir al archivo", + "watermark.openFile": "Abrir archivo", + "watermark.openFolder": "Abrir carpeta", + "watermark.openFileFolder": "Abrir archivo o carpeta", + "watermark.openRecent": "Abrir recientes", + "watermark.newUntitledFile": "Nuevo archivo sin título", + "watermark.toggleTerminal": "Alternar terminal", + "watermark.findInFiles": "Buscar en archivos", + "watermark.startDebugging": "Iniciar depuración", + "watermark.unboundCommand": "sin enlazar", + "workbenchConfigurationTitle": "Área de trabajo", + "tips.enabled": "Si esta opción está habilitada, se muestran sugerencias de marca de agua cuando no hay ningún editor abierto." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/esn/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..8820b72407 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "Explorador de archivos", + "welcomeOverlay.search": "Buscar en todos los archivos", + "welcomeOverlay.git": "Administración de código fuente", + "welcomeOverlay.debug": "Iniciar y depurar", + "welcomeOverlay.extensions": "Administrar extensiones", + "welcomeOverlay.problems": "Ver errores y advertencias", + "welcomeOverlay.commandPalette": "Buscar y ejecutar todos los comandos", + "welcomeOverlay": "Información general de la interfaz de usuario", + "hideWelcomeOverlay": "Ocultar información general de la interfaz", + "help": "Ayuda" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..3a40827c31 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "Edición mejorada", + "welcomePage.start": "Iniciar", + "welcomePage.newFile": "Nuevo archivo", + "welcomePage.openFolder": "Abrir carpeta...", + "welcomePage.cloneGitRepository": "Clonar el repositorio GIT...", + "welcomePage.recent": "Reciente", + "welcomePage.moreRecent": "Más...", + "welcomePage.noRecentFolders": "No hay ninguna carpeta reciente", + "welcomePage.help": "Ayuda", + "welcomePage.keybindingsCheatsheet": "Hoja imprimible con ayudas de teclado", + "welcomePage.introductoryVideos": "Vídeos de introducción", + "welcomePage.tipsAndTricks": "Sugerencias y trucos", + "welcomePage.productDocumentation": "Documentación del producto", + "welcomePage.gitHubRepository": "Repositorio de GitHub", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "Mostrar página principal al inicio", + "welcomePage.customize": "Personalizar", + "welcomePage.installExtensionPacks": "Herramientas y lenguajes", + "welcomePage.installExtensionPacksDescription": "Instalar soporte para {0} y {1}", + "welcomePage.moreExtensions": "más", + "welcomePage.installKeymapDescription": "Instalar los métodos abreviados de teclado", + "welcomePage.installKeymapExtension": "Instalar los métodos abreviados de teclado de {0} y {1}", + "welcomePage.others": "otros", + "welcomePage.colorTheme": "Tema de color", + "welcomePage.colorThemeDescription": "Modifique a su gusto la apariencia del editor y el código", + "welcomePage.learn": "Más información", + "welcomePage.showCommands": "Buscar y ejecutar todos los comandos", + "welcomePage.showCommandsDescription": "Acceda rápidamente y busque comandos desde la Paleta de Comandos ({0})", + "welcomePage.interfaceOverview": "Introducción a la interfaz", + "welcomePage.interfaceOverviewDescription": "Obtenga una superposición que resalta los componentes principales de la interfaz de usuario", + "welcomePage.deployToAzure": "Implementar aplicaciones en la nube", + "welcomePage.deployToAzureDescription": "Vea cómo implementar sus aplicaciones de Node en Azure App Service", + "welcomePage.interactivePlayground": "Área de juegos interactiva", + "welcomePage.interactivePlaygroundDescription": "Pruebe las características esenciales del editor en un tutorial corto" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..cb1298e8ac --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "Área de trabajo", + "workbench.startupEditor.none": "Iniciar sin un editor.", + "workbench.startupEditor.welcomePage": "Abra la página de bienvenida (predeterminado).", + "workbench.startupEditor.newUntitledFile": "Abrir un nuevo archivo sin título.", + "workbench.startupEditor": "Controla qué editor se muestra al inicio, si no se restaura ninguno de la sesión anterior. Seleccione \"none\" para iniciar sin editor, \"welcomePage\" para abrir la página principal (opción predeterminada), \"newUntitledFile\" para abrir un archivo nuevo sin título (solo cuando se abre un área de trabajo vacía).", + "help": "Ayuda" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..58f5d81a2c --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "Bienvenido", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "Mostrar extensiones de Azure", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "El soporte para '{0}' ya está instalado.", + "welcomePage.willReloadAfterInstallingExtensionPack": "La ventana se volverá a cargar después de instalar compatibilidad adicional con {0}.", + "welcomePage.installingExtensionPack": "Instalando compatibilidad adicional con {0}...", + "welcomePage.extensionPackNotFound": "No se pudo encontrar el soporte para {0} con id {1}.", + "welcomePage.keymapAlreadyInstalled": "Los métodos abreviados de teclado {0} ya están instalados.", + "welcomePage.willReloadAfterInstallingKeymap": "La ventana se volverá a cargar después de instalar los métodos abreviados de teclado {0}.", + "welcomePage.installingKeymap": "Instalando los métodos abreviados de teclado de {0}...", + "welcomePage.keymapNotFound": "No se pudieron encontrar los métodos abreviados de teclado {0} con el identificador {1}.", + "welcome.title": "Bienvenido", + "welcomePage.openFolderWithPath": "Abrir la carpeta {0} con la ruta de acceso {1}", + "welcomePage.extensionListSeparator": ", ", + "welcomePage.installKeymap": "Instalar mapa de teclas de {0}", + "welcomePage.installExtensionPack": "Instalar compatibilidad adicional con {0}", + "welcomePage.installedKeymap": "El mapa de teclas de {0} ya está instalado", + "welcomePage.installedExtensionPack": "La compatibilidad con {0} ya está instalada", + "ok": "Aceptar", + "details": "Detalles", + "cancel": "Cancelar", + "welcomePage.buttonBackground": "Color de fondo de los botones en la página principal.", + "welcomePage.buttonHoverBackground": "Color de fondo al mantener el mouse en los botones de la página principal." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..05fc8c19e9 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "Área de juegos interactiva", + "editorWalkThrough": "Área de juegos interactiva" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..ba6a1f7626 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "Área de juegos interactiva", + "help": "Ayuda", + "interactivePlayground": "Área de juegos interactiva" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..6af1d3c656 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "Desplazar hacia arriba (línea)", + "editorWalkThrough.arrowDown": "Desplazar hacia abajo (línea)", + "editorWalkThrough.pageUp": "Desplazar hacia arriba (página)", + "editorWalkThrough.pageDown": "Desplazar hacia abajo (página)" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..a3dd08a5fc --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "sin enlazar", + "walkThrough.gitNotFound": "Parece que GIT no está instalado en el sistema.", + "walkThrough.embeddedEditorBackground": "Color de fondo de los editores incrustrados en la área de juegos" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/esn/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..919a7c9122 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "Aporta opciones de configuración.", + "vscode.extension.contributes.configuration.title": "Resumen de la configuración. Esta etiqueta se usará en el archivo de configuración como comentario divisor.", + "vscode.extension.contributes.configuration.properties": "Descripción de las propiedades de configuración.", + "scope.window.description": "Configuración específica para ventanas, que se puede definir en la configuración de usuario o de área de trabajo.", + "scope.resource.description": "Configuración específica para recursos, que se puede definir en la configuración de usuario, de área de trabajo o de carpeta.", + "scope.description": "Ámbito donde es aplicable la configuración. Los ámbitos disponibles son \"window\" y \"resource\".", + "invalid.type": "si se establece, \"configuration.type\" debe establecerse en \"object\"", + "invalid.title": "configuration.title debe ser una cadena", + "vscode.extension.contributes.defaultConfiguration": "Contribuye a la configuración de los parámetros del editor predeterminados por lenguaje.", + "invalid.properties": "configuration.properties debe ser un objeto", + "workspaceConfig.folders.description": "Lista de carpetas que debe cargarse en el área de trabajo. Debe ser una ruta de acceso de archivo; por ejemplo, \"/raíz/carpetaA\" o \"./carpetaA\" para una ruta de acceso relativa que se resolverá respecto a la ubicación del archivo del área de trabajo.", + "workspaceConfig.folder.description": "Ruta de acceso de archivo; por ejemplo, \"/raíz/carpetaA\" o \"./carpetaA\" para una ruta de acceso de archivo que se resolverá respecto a la ubicación del archivo del área de trabajo.", + "workspaceConfig.settings.description": "Configuración de área de trabajo" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/esn/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..be30d1eaa6 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Abrir configuración", + "close": "Cerrar", + "saveAndRetry": "Guardar la configuración y reintentar. ", + "errorUnknownKey": "No se puede escribir en {0} porque {1} no es una configuración registrada.", + "errorInvalidFolderConfiguration": "No se puede escribir en Configuración de carpeta porque {0} no admite el ámbito del recurso de carpeta.", + "errorInvalidUserTarget": "No se puede escribir en Configuración de usuario porque {0} no admite el ámbito global.", + "errorInvalidFolderTarget": "No se puede escribir en Configuración de carpeta porque no se ha proporcionado ningún recurso.", + "errorNoWorkspaceOpened": "No se puede escribir en {0} porque no hay ninguna área de trabajo abierta. Abra un área de trabajo y vuelva a intentarlo.", + "errorInvalidConfiguration": "No se pudo guardar la configuración. Abra el archivo **Configuración de usuario** para corregir los errores o advertencias del archivo y vuelva a intentarlo.", + "errorInvalidConfigurationWorkspace": "No se pudo guardar la configuración. Abra **Configuración de área de trabajo** para corregir los errores o advertencias del archivo y vuelva a intentarlo.", + "errorConfigurationFileDirty": "No se pudo guardar la configuración porque el archivo se ha modificado. Guarde el archivo **Configuración de usuario** y vuelva a intentarlo.", + "errorConfigurationFileDirtyWorkspace": "No se pudo guardar la configuración porque el archivo se ha modificado. Guarde el archivo **Configuración de área de trabajo** y vuelva a intentarlo.", + "userTarget": "Configuración de usuario", + "workspaceTarget": "Configuración de área de trabajo", + "folderTarget": "Configuración de carpeta" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/esn/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..3e891c0e15 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "No se puede escribir en el archivo. Abra el archivo para corregir los errores o advertencias y vuelva a intentarlo.", + "errorFileDirty": "No se puede escribir en el archivo porque se ha modificado. Guarde el archivo y vuelva a intentarlo." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/esn/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..db9fd677ca --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetría", + "telemetry.enableCrashReporting": "Habilite los informes de bloqueo para enviarlos a Microsoft. Esta opción requiere reiniciar para que tenga efecto." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/esn/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..0051102a33 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "El host de extensiones no se inició en 10 segundos, puede que se detenga en la primera línea y necesita un depurador para continuar.", + "extensionHostProcess.startupFail": "El host de extensiones no se inició en 10 segundos, lo cual puede ser un problema.", + "extensionHostProcess.error": "Error del host de extensiones: {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..71594f4c37 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "No se pudo analizar {0}: {1}.", + "fileReadFail": "No se puede leer el archivo {0}: {1}.", + "jsonsParseFail": "No se pudo analizar {0} o {1}: {2}.", + "missingNLSKey": "No se encontró un mensaje para la clave {0}." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..0256c81ab7 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "Herramientas de desarrollo", + "restart": "Reiniciar el host de extensiones", + "extensionHostProcess.crash": "El host de extensiones finalizó inesperadamente.", + "extensionHostProcess.unresponsiveCrash": "Se terminó el host de extensiones porque no respondía.", + "overwritingExtension": "Sobrescribiendo la extensión {0} con {1}.", + "extensionUnderDevelopment": "Cargando la extensión de desarrollo en {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/esn/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..8ac98ff9e5 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "Requiere Microsoft .NET Framework 4.5. Siga el vínculo para instalarlo.", + "installNet": "Descargar .NET Framework 4.5", + "neverShowAgain": "No volver a mostrar", + "trashFailed": "No se pudo mover '{0}' a la papelera" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/esn/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..1172f6df57 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "Recurso de archivo no válido ({0})", + "fileIsDirectoryError": "El archivo es un directorio ({0})", + "fileNotModifiedError": "Archivo no modificado desde", + "fileTooLargeError": "Archivo demasiado grande para abrirlo", + "fileBinaryError": "El archivo parece ser binario y no se puede abrir como texto", + "fileNotFoundError": "Archivo no encontrado ({0})", + "fileMoveConflict": "No se puede mover o copiar. El archivo ya existe en la ubicación de destino. ", + "unableToMoveCopyError": "No se puede mover o copiar. El archivo reemplazaría a la carpeta que lo contiene.", + "foldersCopyError": "No se pueden copiar carpetas en el área de trabajo. Seleccione archivos individuales para copiarlos.", + "fileModifiedError": "Archivo Modificado Desde", + "fileReadOnlyError": "El archivo es de solo lectura" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/esn/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..d3dc5992c1 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "Unable to write because the file is dirty. Please save the **Keybindings** file and try again.", + "parseErrors": "No se pueden escribir enlaces de teclado. Abra el **archivo de enlaces de teclado** para corregir los errores o advertencias del archivo y vuelva a intentarlo.", + "errorInvalidConfiguration": "No se pueden escribir enlaces de teclado. El archivo de **enlaces de teclado** tiene un objeto que no es de tipo matriz. Abra el archivo para limpiarlo y vuelva a intentarlo.", + "emptyKeybindingsHeader": "Coloque sus enlaces de teclado en este archivo para sobrescribir los valores predeterminados." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/esn/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..5cf5b87bcd --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "se esperaba un valor no vacío.", + "requirestring": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "optstring": "la propiedad `{0}` se puede omitir o debe ser de tipo \"string\"", + "vscode.extension.contributes.keybindings.command": "Identificador del comando que se va a ejecutar cuando se desencadena el enlace de teclado.", + "vscode.extension.contributes.keybindings.key": "Tecla o secuencia de teclas (las teclas se separan con el signo más y las secuencias con un espacio, por ejemplo, Ctrl+O y Ctrl+L L para una presión simultánea).", + "vscode.extension.contributes.keybindings.mac": "Tecla o secuencia de teclas específica de Mac.", + "vscode.extension.contributes.keybindings.linux": "Tecla o secuencia de teclas específica de Linux.", + "vscode.extension.contributes.keybindings.win": "Tecla o secuencia de teclas específica de Windows.", + "vscode.extension.contributes.keybindings.when": "Condición cuando la tecla está activa.", + "vscode.extension.contributes.keybindings": "Aporta enlaces de teclado.", + "invalid.keybindings": "Valor de \"contributes.{0}\" no válido: {1}", + "unboundCommands": "Aquí hay otros comandos disponibles: ", + "keybindings.json.title": "Configuración de enlaces de teclado", + "keybindings.json.key": "Tecla o secuencia de teclas (separadas por un espacio)", + "keybindings.json.command": "Nombre del comando que se va a ejecutar", + "keybindings.json.when": "Condición cuando la tecla está activa.", + "keybindings.json.args": "Argumentos que se pasan al comando para ejecutar.", + "keyboardConfigurationTitle": "Teclado", + "dispatch": "Controla la lógica de distribución de las pulsaciones de teclas para usar `keydown.code` (recomendado) o `keydown.keyCode`." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/esn/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..2d21bf04fa --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Error: {0}", + "alertWarningMessage": "Advertencia: {0}", + "alertInfoMessage": "Información: {0}", + "error": "Error", + "warning": "Advertencia", + "info": "Información", + "close": "Cerrar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/esn/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..6f613208df --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "&&Sí", + "cancelButton": "Cancelar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/esn/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..8796c49406 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Aporta declaraciones de lenguaje.", + "vscode.extension.contributes.languages.id": "Identificador del lenguaje.", + "vscode.extension.contributes.languages.aliases": "Alias de nombre para el lenguaje.", + "vscode.extension.contributes.languages.extensions": "Extensiones de archivo asociadas al lenguaje.", + "vscode.extension.contributes.languages.filenames": "Nombres de archivo asociados al lenguaje.", + "vscode.extension.contributes.languages.filenamePatterns": "Patrones globales de nombre de archivo asociados al lenguaje.", + "vscode.extension.contributes.languages.mimetypes": "Tipos MIME asociados al lenguaje.", + "vscode.extension.contributes.languages.firstLine": "Expresión regular que coincide con la primera línea de un archivo del lenguaje.", + "vscode.extension.contributes.languages.configuration": "Ruta de acceso relativa a un archivo que contiene opciones de configuración para el lenguaje.", + "invalid": "Elemento \"contributes.{0}\" no válido. Se esperaba una matriz.", + "invalid.empty": "Valor vacío para \"contributes.{0}\"", + "require.id": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "opt.extensions": "la propiedad `{0}` se puede omitir y debe ser de tipo \"string[]\"", + "opt.filenames": "la propiedad `{0}` se puede omitir y debe ser de tipo \"string[]\"", + "opt.firstLine": "la propiedad `{0}` se puede omitir y debe ser de tipo \"string\"", + "opt.configuration": "la propiedad `{0}` se puede omitir y debe ser de tipo \"string\"", + "opt.aliases": "la propiedad `{0}` se puede omitir y debe ser de tipo \"string[]\"", + "opt.mimetypes": "la propiedad `{0}` se puede omitir y debe ser de tipo \"string[]\"" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/esn/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..26fc91786e --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1} ", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/esn/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..a1f1307b47 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Aporta tokenizadores de TextMate.", + "vscode.extension.contributes.grammars.language": "Identificador del lenguaje para el que se aporta esta sintaxis.", + "vscode.extension.contributes.grammars.scopeName": "Nombre del ámbito de TextMate que usa el archivo tmLanguage.", + "vscode.extension.contributes.grammars.path": "Ruta de acceso del archivo tmLanguage. La ruta es relativa a la carpeta de extensión y normalmente empieza por \"./syntaxes/\".", + "vscode.extension.contributes.grammars.embeddedLanguages": "Asignación de un nombre de ámbito al identificador de lenguaje si esta gramática contiene lenguajes incrustados.", + "vscode.extension.contributes.grammars.injectTo": "Lista de nombres de ámbito de lenguaje al que se inyecta esta gramática." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/esn/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..e04f613e95 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Lenguaje desconocido en \"contributes.{0}.language\". Valor proporcionado: {1}", + "invalid.scopeName": "Se esperaba una cadena en \"contributes.{0}.scopeName\". Valor proporcionado: {1}", + "invalid.path.0": "Se esperaba una cadena en \"contributes.{0}.path\". Valor proporcionado: {1}", + "invalid.injectTo": "Valor no válido en `contributes.{0}.injectTo`. Debe ser una matriz de nombres de ámbito de lenguaje. Valor proporcionado: {1}", + "invalid.embeddedLanguages": "Valor no válido en \"contributes.{0}.embeddedLanguages\". Debe ser una asignación de objeto del nombre del ámbito al lenguaje. Valor proporcionado: {1}", + "invalid.path.1": "Se esperaba que \"contributes.{0}.path\" ({1}) se incluyera en la carpeta de la extensión ({2}). Esto puede hacer que la extensión no sea portátil.", + "no-tm-grammar": "No hay ninguna gramática de TM registrada para este lenguaje." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/esn/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..b75acba6b2 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "Este es un archivo con modificaciones. Guárdelo antes de volver a abrirlo con otra codificación.", + "genericSaveError": "No se pudo guardar '{0}': {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/esn/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..917af93929 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "No se pudo hacer una copia de seguridad de los archivos (Error: {0}). Intente guardar los archivos para salir." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/esn/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..be51319a0f --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "¿Quiere guardar los cambios efectuados en {0}?", + "saveChangesMessages": "¿Desea guardar los cambios en los siguientes {0} archivos?", + "moreFile": "...1 archivo más que no se muestra", + "moreFiles": "...{0} archivos más que no se muestran", + "saveAll": "&&Guardar todo", + "save": "Guardar", + "dontSave": "&&No guardar", + "cancel": "Cancelar", + "saveChangesDetail": "Los cambios se perderán si no se guardan.", + "allFiles": "Todos los archivos", + "noExt": "Sin extensión" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/esn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..e2c60930ed --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "Colores y estilos para el token.", + "schema.token.foreground": "Color de primer plano para el token.", + "schema.token.fontStyle": "Estilo de fuente de la regla: una opción de \"italic\", \"bold\" y \"underline\", o una combinación de ellas.", + "schema.fontStyle.error": "Estilo de fuente debe ser una combinación de 'cursiva', 'negrita' y 'subrayado'", + "schema.properties.name": "Descripción de la regla.", + "schema.properties.scope": "Selector de ámbito con el que se compara esta regla.", + "schema.tokenColors.path": "Ruta a un archivo tmTheme (relativa al archivo actual).", + "schema.colors": "Colores para resaltado de sintaxis" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/esn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..e19fa7ae9b --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "Icono de las carpetas expandidas. El icono de carpeta expandida es opcional. Si no establece, se muestra el icono definido para la carpeta.", + "schema.folder": "Icono de las carpetas contraídas y, si folderExpanded no se ha establecido, también de las carpetas expandidas.", + "schema.file": "Icono de archivo predeterminado, que se muestra para todos los archivos que no coinciden con ninguna extensión, nombre de archivo o identificador de lenguaje.", + "schema.folderNames": "Asocia los nombres de carpeta a iconos. La clave del objeto es el nombre de la carpeta, sin incluir segmentos de ruta de acceso. No se admiten patrones ni comodines. La correspondencia de nombres de carpeta no distingue mayúsculas de minúsculas.", + "schema.folderName": "Identificador de la definición de icono para la asociación.", + "schema.folderNamesExpanded": "Asocia los nombres de carpeta a iconos para las carpetas expandidas. La clave del objeto es el nombre de la carpeta, sin incluir segmentos de ruta de acceso. No se admiten patrones ni comodines. La correspondencia de nombres de carpeta no distingue mayúsculas de minúsculas.", + "schema.folderNameExpanded": "Identificador de la definición de icono para la asociación.", + "schema.fileExtensions": "Asocia las extensiones de archivo a iconos. La clave del objeto es el nombre de la extensión de archivo. El nombre de la extensión es el último segmento de un nombre de archivo después del último punto (sin incluir el punto). Las extensiones se comparan sin distinguir mayúsculas de minúsculas.", + "schema.fileExtension": "Identificador de la definición de icono para la asociación.", + "schema.fileNames": "Asocia los nombres de archivo a iconos. La clave del objeto es el nombre de archivo completo, sin incluir segmentos de ruta de acceso. El nombre de archivo puede incluir puntos y una extensión de archivo posible. No se admiten patrones ni comodines. La correspondencia de nombres de archivo no distingue mayúsculas de minúsculas.", + "schema.fileName": "Identificador de la definición de icono para la asociación.", + "schema.languageIds": "Asocia los lenguajes a iconos. La clave del objeto es el identificador de lenguaje como se define en el punto de contribución del lenguaje.", + "schema.languageId": "Identificador de la definición de icono para la asociación.", + "schema.fonts": "Fuentes que se usan en las definiciones de icono.", + "schema.id": "Identificador de la fuente.", + "schema.src": "Ubicación de la fuente.", + "schema.font-path": "Ruta de acceso de la fuente, relativa al archivo de temas de icono actual.", + "schema.font-format": "Formato de la fuente.", + "schema.font-weight": "Espesor de la fuente.", + "schema.font-sstyle": "Estilo de la fuente.", + "schema.font-size": "Tamaño predeterminado de la fuente.", + "schema.iconDefinitions": "Descripción de todos los iconos que se pueden usar al asociar archivos a iconos.", + "schema.iconDefinition": "Definición de icono. La clave del objeto es el identificador de la definición.", + "schema.iconPath": "Cuando se usa SVG o PNG: la ruta de acceso a la imagen. La ruta es relativa al archivo del conjunto de iconos.", + "schema.fontCharacter": "Cuando se usa una fuente de glifo: el carácter de la fuente que se va a usar.", + "schema.fontColor": "Cuando se usa una fuente de glifo: el color que se va a usar.", + "schema.fontSize": "Cuando se usa una fuente: porcentaje del tamaño de fuente para la fuente del texto. Si no se ha establecido, el valor predeterminado es el tamaño de la definición de fuente.", + "schema.fontId": "Cuando se usa una fuente: el identificador de la fuente. Si no se ha establecido, el valor predeterminado es la primera definición de fuente.", + "schema.light": "Asociaciones opcionales para iconos de archivo en temas de colores claros.", + "schema.highContrast": "Asociaciones opcionales para iconos de archivo en temas de color de contraste alto." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/esn/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..c0f43214ad --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "Problemas al analizar el archivo de tema JSON: {0}", + "error.invalidformat.colors": "Problema al analizar el archivo de tema: {0}. La propiedad \"colors\" no es tipo \"object\".", + "error.invalidformat.tokenColors": "Problema al analizar el archivo de tema de color: {0}. La propiedad 'tokenColors' debe ser un array especificando colores o una ruta a un archivo de tema de TextMate", + "error.plist.invalidformat": "Problema al analizar el archivo de tema: {0}. \"settings\" no es una matriz.", + "error.cannotparse": "Problemas al analizar el archivo de tema: {0}", + "error.cannotload": "Problemas al analizar el archivo de tema: {0}:{1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/esn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..b7cd9de6d2 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "Identificador del tema de icono como se usa en la configuración de usuario.", + "vscode.extension.contributes.themes.label": "Etiqueta del tema de color tal como se muestra en la interfaz de usuario.", + "vscode.extension.contributes.themes.uiTheme": "Tema base que define los colores que se usan en el editor: 'vs' es el tema de color claro, 'vs-dark' es el tema de color oscuro, 'hc-black' es el tema oscuro de alto contraste.", + "vscode.extension.contributes.themes.path": "Ruta de acceso del archivo tmTheme. La ruta de acceso es relativa a la carpeta de extensión y suele ser './themes/themeFile.tmTheme'.", + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "Identificador del tema de icono como se usa en la configuración de usuario.", + "vscode.extension.contributes.iconThemes.label": "Etiqueta del tema de icono como se muestra en la interfaz de usuario.", + "vscode.extension.contributes.iconThemes.path": "Ruta de acceso del archivo de definición de temas de icono. La ruta de acceso es relativa a la carpeta de extensión y suele ser './icons/awesome-icon-theme.json'.", + "migration.completed": "Se han agregado nuevos valores de tema a la configuración de usuario. Hay una copia de seguridad disponible en {0}.", + "error.cannotloadtheme": "Unable to load {0}: {1}", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "Se esperaba una cadena en \"contributes.{0}.path\". Valor proporcionado: {1}", + "invalid.path.1": "Se esperaba que \"contributes.{0}.path\" ({1}) se incluyera en la carpeta de la extensión ({2}). Esto puede hacer que la extensión no sea portátil.", + "reqid": "Se esperaba una cadena en `contributes.{0}.id`. Valor proporcionado: {1}", + "error.cannotloadicontheme": "Unable to load {0}", + "error.cannotparseicontheme": "Problems parsing file icons file: {0}", + "colorTheme": "Specifies the color theme used in the workbench.", + "colorThemeError": "Theme is unknown or not installed.", + "iconTheme": "Especifica el tema de icono utilizado en el área de trabajo o \"null\" para no mostrar ningún icono de archivo.", + "noIconThemeDesc": "No file icons", + "iconThemeError": "File icon theme is unknown or not installed.", + "workbenchColors": "Reemplaza los colores del tema de color actual", + "workbenchColors.deprecated": "Esta configuracion no es experimental y se ha cambiado de nombre a 'workbench.colorCustomizations'", + "workbenchColors.deprecatedDescription": "Utilice 'workbench.colorCustomizations' en su lugar", + "editorColors": "Reemplaza los colores y el estilo de fuente del editor del tema de color seleccionado.", + "editorColors.comments": "Establece los colores y estilos para los comentarios", + "editorColors.strings": "Establece los colores y estilos para los literales de cadena.", + "editorColors.keywords": "Establece los colores y estilos para las palabras clave.", + "editorColors.numbers": "Establece los colores y estilos para números literales.", + "editorColors.types": "Establece los colores y estilos para las declaraciones y referencias de tipos.", + "editorColors.functions": "Establece los colores y estilos para las declaraciones y referencias de funciones.", + "editorColors.variables": "Establece los colores y estilos para las declaraciones y referencias de variables.", + "editorColors.textMateRules": "Establece colores y estilos utilizando las reglas de la tematización de textmate (avanzadas)." +} \ No newline at end of file diff --git a/i18n/fra/extensions/configuration-editing/out/extension.i18n.json b/i18n/fra/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..8260f123de --- /dev/null +++ b/i18n/fra/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "Exemple" +} \ No newline at end of file diff --git a/i18n/fra/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/fra/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..e8369252e0 --- /dev/null +++ b/i18n/fra/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "exemple : myFile.txt", + "activeEditorMedium": "exemple : myFolder/myFile.txt", + "activeEditorLong": "exemple : /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "par ex., myFolder1, myFolder2, myFolder3", + "rootPath": "exemple : /Users/Development/myProject", + "folderName": "par ex., myFolder", + "folderPath": "par ex., /Users/Development/myFolder", + "appName": "exemple : VS Code", + "dirty": "indicateur d'intégrité si l'intégrité de l'éditeur actif est compromise", + "separator": "séparateur conditionnel (' - ') qui s'affiche uniquement quand il est entouré de variables avec des valeurs", + "assocLabelFile": "Fichiers avec extension", + "assocDescriptionFile": "Mappez au langage ayant l'identificateur spécifié tous les fichiers dont le nom correspond au modèle Glob.", + "assocLabelPath": "Fichiers avec chemin", + "assocDescriptionPath": "Mappez au langage ayant l'identificateur spécifié tous les fichiers dont le chemin correspond au modèle Glob de chemin absolu.", + "fileLabel": "Fichiers par extension", + "fileDescription": "Faites correspondre tous les fichiers ayant une extension de fichier spécifique.", + "filesLabel": "Fichiers avec plusieurs extensions", + "filesDescription": "Faites correspondre tous les fichiers, indépendamment de leurs extensions.", + "derivedLabel": "Fichiers avec frères par nom", + "derivedDescription": "Faites correspondre les fichiers ayant des frères portant le même nom, mais avec une extension distincte.", + "topFolderLabel": "Dossier par nom (premier niveau)", + "topFolderDescription": "Faites correspondre un dossier de premier niveau portant un nom spécifique.", + "topFoldersLabel": "Dossiers avec plusieurs noms (premier niveau)", + "topFoldersDescription": "Faites correspondre plusieurs dossiers de premier niveau.", + "folderLabel": "Dossier par nom (tous les emplacements)", + "folderDescription": "Faites correspondre un dossier portant un nom spécifique, indépendamment de son emplacement.", + "falseDescription": "Désactivez le modèle.", + "trueDescription": "Activez le modèle.", + "siblingsDescription": "Faites correspondre les fichiers ayant des frères portant le même nom, mais avec une extension distincte.", + "languageSpecificEditorSettings": "Paramètres d'éditeur spécifiques au langage", + "languageSpecificEditorSettingsDescription": "Remplacer les paramètres de l'éditeur pour le langage" +} \ No newline at end of file diff --git a/i18n/fra/extensions/css/client/out/cssMain.i18n.json b/i18n/fra/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..af0dda97ef --- /dev/null +++ b/i18n/fra/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "Serveur de langage CSS" +} \ No newline at end of file diff --git a/i18n/fra/extensions/css/package.i18n.json b/i18n/fra/extensions/css/package.i18n.json new file mode 100644 index 0000000000..3831e992b1 --- /dev/null +++ b/i18n/fra/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "Nombre de paramètres non valide", + "css.lint.boxModel.desc": "Ne pas utiliser la largeur ou la hauteur avec une marge intérieure ou une bordure", + "css.lint.compatibleVendorPrefixes.desc": "Lors de l'utilisation d'un préfixe spécifique à un fabricant, toujours inclure également toutes les propriétés spécifiques au fabricant", + "css.lint.duplicateProperties.desc": "Ne pas utiliser de définitions de style en double", + "css.lint.emptyRules.desc": "Ne pas utiliser d'ensembles de règles vides", + "css.lint.float.desc": "N'utilisez pas 'float'. Les éléments Float peuvent fragiliser le code CSS qui est ainsi plus vulnérable si un aspect de la disposition change.", + "css.lint.fontFaceProperties.desc": "la règle @font-face doit définir les propriétés 'src' et 'font-family'", + "css.lint.hexColorLength.desc": "Les couleurs Hex doivent contenir trois ou six chiffres hex", + "css.lint.idSelector.desc": "Les sélecteurs ne doivent pas contenir d'ID, car ces règles sont trop fortement couplées au code HTML.", + "css.lint.ieHack.desc": "Les hacks IE ne sont nécessaires que si IE7 et versions antérieures sont pris en charge", + "css.lint.important.desc": "N'utilisez pas !important. Cela indique que la spécificité de l'intégralité du code CSS est incorrecte et qu'il doit être refactorisé.", + "css.lint.importStatement.desc": "Les instructions d'importation ne sont pas chargées en parallèle", + "css.lint.propertyIgnoredDueToDisplay.desc": "Propriété ignorée en raison de l'affichage. Par exemple, avec 'display: inline', les propriétés width, height, margin-top, margin-bottom et float sont sans effet", + "css.lint.universalSelector.desc": "Le sélecteur universel (*) est connu pour sa lenteur", + "css.lint.unknownProperties.desc": "Propriété inconnue.", + "css.lint.unknownVendorSpecificProperties.desc": "Propriété spécifique à un fournisseur inconnue.", + "css.lint.vendorPrefix.desc": "Lors de l'utilisation d'un préfixe spécifique à un fournisseur, ajouter également la propriété standard", + "css.lint.zeroUnits.desc": "Aucune unité nécessaire pour zéro", + "css.trace.server.desc": "Trace la communication entre VS Code et le serveur de langage CSS.", + "css.validate.title": "Contrôle la validation CSS et la gravité des problèmes.", + "css.validate.desc": "Active ou désactive toutes les validations", + "less.lint.argumentsInColorFunction.desc": "Nombre de paramètres non valide", + "less.lint.boxModel.desc": "Ne pas utiliser la largeur ou la hauteur avec une marge intérieure ou une bordure", + "less.lint.compatibleVendorPrefixes.desc": "Lors de l'utilisation d'un préfixe spécifique à un fabricant, toujours inclure également toutes les propriétés spécifiques au fabricant", + "less.lint.duplicateProperties.desc": "Ne pas utiliser de définitions de style en double", + "less.lint.emptyRules.desc": "Ne pas utiliser d'ensembles de règles vides", + "less.lint.float.desc": "N'utilisez pas 'float'. Les éléments Float peuvent fragiliser le code CSS qui est ainsi plus vulnérable si un aspect de la disposition change.", + "less.lint.fontFaceProperties.desc": "la règle @font-face doit définir les propriétés 'src' et 'font-family'", + "less.lint.hexColorLength.desc": "Les couleurs Hex doivent contenir trois ou six chiffres hex", + "less.lint.idSelector.desc": "Les sélecteurs ne doivent pas contenir d'ID, car ces règles sont trop fortement couplées au code HTML.", + "less.lint.ieHack.desc": "Les hacks IE ne sont nécessaires que si IE7 et versions antérieures sont pris en charge", + "less.lint.important.desc": "N'utilisez pas !important. Cela indique que la spécificité de l'intégralité du code CSS est incorrecte et qu'il doit être refactorisé.", + "less.lint.importStatement.desc": "Les instructions d'importation ne sont pas chargées en parallèle", + "less.lint.propertyIgnoredDueToDisplay.desc": "Propriété ignorée en raison de l'affichage. Par exemple, avec 'display: inline', les propriétés width, height, margin-top, margin-bottom et float sont sans effet", + "less.lint.universalSelector.desc": "Le sélecteur universel (*) est connu pour sa lenteur", + "less.lint.unknownProperties.desc": "Propriété inconnue.", + "less.lint.unknownVendorSpecificProperties.desc": "Propriété spécifique à un fournisseur inconnue.", + "less.lint.vendorPrefix.desc": "Lors de l'utilisation d'un préfixe spécifique à un fournisseur, ajouter également la propriété standard", + "less.lint.zeroUnits.desc": "Aucune unité nécessaire pour zéro", + "less.validate.title": "Contrôle la validation LESS et la gravité des problèmes.", + "less.validate.desc": "Active ou désactive toutes les validations", + "scss.lint.argumentsInColorFunction.desc": "Nombre de paramètres non valide", + "scss.lint.boxModel.desc": "Ne pas utiliser la largeur ou la hauteur avec une marge intérieure ou une bordure", + "scss.lint.compatibleVendorPrefixes.desc": "Lors de l'utilisation d'un préfixe spécifique à un fabricant, toujours inclure également toutes les propriétés spécifiques au fabricant", + "scss.lint.duplicateProperties.desc": "Ne pas utiliser de définitions de style en double", + "scss.lint.emptyRules.desc": "Ne pas utiliser d'ensembles de règles vides", + "scss.lint.float.desc": "N'utilisez pas 'float'. Les éléments Float peuvent fragiliser le code CSS qui est ainsi plus vulnérable si un aspect de la disposition change.", + "scss.lint.fontFaceProperties.desc": "la règle @font-face doit définir les propriétés 'src' et 'font-family'", + "scss.lint.hexColorLength.desc": "Les couleurs Hex doivent contenir trois ou six chiffres hex", + "scss.lint.idSelector.desc": "Les sélecteurs ne doivent pas contenir d'ID, car ces règles sont trop fortement couplées au code HTML.", + "scss.lint.ieHack.desc": "Les hacks IE ne sont nécessaires que si IE7 et versions antérieures sont pris en charge", + "scss.lint.important.desc": "N'utilisez pas !important. Cela indique que la spécificité de l'intégralité du code CSS est incorrecte et qu'il doit être refactorisé.", + "scss.lint.importStatement.desc": "Les instructions d'importation ne sont pas chargées en parallèle", + "scss.lint.propertyIgnoredDueToDisplay.desc": "Propriété ignorée en raison de l'affichage. Par exemple, avec 'display: inline', les propriétés width, height, margin-top, margin-bottom et float sont sans effet", + "scss.lint.universalSelector.desc": "Le sélecteur universel (*) est connu pour sa lenteur", + "scss.lint.unknownProperties.desc": "Propriété inconnue.", + "scss.lint.unknownVendorSpecificProperties.desc": "Propriété spécifique à un fournisseur inconnue.", + "scss.lint.vendorPrefix.desc": "Lors de l'utilisation d'un préfixe spécifique à un fournisseur, ajouter également la propriété standard", + "scss.lint.zeroUnits.desc": "Aucune unité nécessaire pour zéro", + "scss.validate.title": "Contrôle la validation SCSS et la gravité des problèmes.", + "scss.validate.desc": "Active ou désactive toutes les validations", + "less.colorDecorators.enable.desc": "Active ou désactive les éléments décoratifs de couleurs", + "scss.colorDecorators.enable.desc": "Active ou désactive les éléments décoratifs de couleurs", + "css.colorDecorators.enable.desc": "Active ou désactive les éléments décoratifs de couleurs", + "css.colorDecorators.enable.deprecationMessage": "Le paramètre 'css.colorDecorators.enable' a été déprécié en faveur de 'editor.colorDecorators'.", + "scss.colorDecorators.enable.deprecationMessage": "Le paramètre 'scss.colorDecorators.enable' a été déprécié en faveur de 'editor.colorDecorators'.", + "less.colorDecorators.enable.deprecationMessage": "Le paramètre 'less.colorDecorators.enable' a été déprécié en faveur de 'editor.colorDecorators'." +} \ No newline at end of file diff --git a/i18n/fra/extensions/emmet/package.i18n.json b/i18n/fra/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..c30b1f9fa8 --- /dev/null +++ b/i18n/fra/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "Envelopper avec une abréviation", + "command.wrapIndividualLinesWithAbbreviation": "Envelopper les lignes individuelles avec une abréviation", + "command.removeTag": "Supprimer le Tag", + "command.updateTag": "Mettre à jour le Tag", + "command.matchTag": "Aller à la paire correspondante", + "command.balanceIn": "Equilibrer (vers l'intérieur)", + "command.balanceOut": "Equilibrer (vers l'extérieur)", + "command.prevEditPoint": "Aller au Point d'édition précédent", + "command.nextEditPoint": "Aller au Point d’édition suivant", + "command.mergeLines": "Fusionner les lignes", + "command.selectPrevItem": "Sélectionner l’élément précédent", + "command.selectNextItem": "Sélectionner l’élément suivant", + "command.splitJoinTag": "Séparer / Joindre le Tag", + "command.toggleComment": "Activer/désactiver le commentaire", + "command.evaluateMathExpression": "Évaluer l’Expression mathématique", + "command.updateImageSize": "Mettre à jour la taille de l’image", + "command.reflectCSSValue": "Reflèter la valeur CSS", + "command.incrementNumberByOne": "Incrémenter de 1", + "command.decrementNumberByOne": "Décrémenter de 1", + "command.incrementNumberByOneTenth": "Incrémenter de 0,1", + "command.decrementNumberByOneTenth": "Décrémenter de 0,1", + "command.incrementNumberByTen": "Incrémenter de 10", + "command.decrementNumberByTen": "Décrémenter de 10", + "emmetSyntaxProfiles": "Définissez le profil pour la syntaxe spécifiée ou utilisez votre propre profil avec des règles spécifiques.", + "emmetExclude": "Ensemble de langages où les abréviations emmet ne doivent pas être développées.", + "emmetExtensionsPath": "Chemin d'un dossier contenant les profils et les extraits Emmet.", + "emmetShowExpandedAbbreviation": "Affiche les abréviations Emmet développées sous forme de suggestions.\nL’option \"inMarkupAndStylesheetFilesOnly\" s’applique à html, haml, jade, slim, xml, xsl, css, scss, sass, less et un stylet.\nL’option \"toujours\" s’applique à toutes les parties du fichier indépendamment du balisage/css.", + "emmetShowAbbreviationSuggestions": "Affiche les abréviations Emmet possibles sous forme de suggestions. Non applicable dans les feuilles de style ou quand emmet.showExpandedAbbreviation est défini sur \"jamais\".", + "emmetIncludeLanguages": "Active les abréviations Emmet dans les langages qui ne sont pas pris en charge par défaut. Ajoute un mappage ici entre le langage et le langage Emmet pris en charge .\n Par exemple : {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "Variables à utiliser dans les extraits Emmet", + "emmetTriggerExpansionOnTab": "Une fois les abréviations Emmet activées, elles se développent quand vous appuyez sur la touche Tab.", + "emmetPreferences": "Préférences utilisées pour modifier le comportement de certaines actions et résolveurs d'Emmet.", + "emmetPreferencesIntUnit": "Unité par défaut pour les valeurs entières", + "emmetPreferencesFloatUnit": "Unité par défaut pour les valeurs float", + "emmetPreferencesCssAfter": "Symbole à placer à la fin de la propriété CSS pendant le développement des abréviations CSS", + "emmetPreferencesSassAfter": "Symbole à placer à la fin de la propriété CSS pendant le développement des abréviations CSS dans les fichiers Sass", + "emmetPreferencesStylusAfter": "Symbole à placer à la fin de la propriété CSS pendant le développement des abréviations CSS dans les fichiers Stylus", + "emmetPreferencesCssBetween": "Symbole à placer entre la propriété CSS et la valeur pendant le développement des abréviations CSS", + "emmetPreferencesSassBetween": "Symbole à placer entre la propriété CSS et la valeur pendant le développement des abréviations CSS dans les fichiers Sass", + "emmetPreferencesStylusBetween": "Symbole à placer entre la propriété CSS et la valeur pendant le développement des abréviations CSS dans les fichiers Stylus", + "emmetShowSuggestionsAsSnippets": "Si la valeur est True, les suggestions Emmet s'affichent sous forme d'extraits, ce qui vous permet de les ordonner conformément au paramètre editor.snippetSuggestions." +} \ No newline at end of file diff --git a/i18n/fra/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/fra/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..5ecdad3504 --- /dev/null +++ b/i18n/fra/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "Les images doivent utiliser le protocole HTTPS.", + "svgsNotValid": "Les SVG ne sont pas une source d'images valide.", + "embeddedSvgsNotValid": "Les SVG incorporés ne sont pas une source d'images valide.", + "dataUrlsNotValid": "Les URL de données ne sont pas une source d'images valide.", + "relativeUrlRequiresHttpsRepository": "Les URL d'image relatives nécessitent un dépôt avec le protocole HTTPS dans package.json.", + "relativeIconUrlRequiresHttpsRepository": "Une icône nécessite un référentiel avec le protocole HTTPS spécifié dans ce package.json.", + "relativeBadgeUrlRequiresHttpsRepository": "Les URL d'image relatives nécessitent un dépôt avec le protocole HTTPS dans package.json." +} \ No newline at end of file diff --git a/i18n/fra/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/fra/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..48f804f5cf --- /dev/null +++ b/i18n/fra/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "Paramètres d'éditeur spécifiques au langage", + "languageSpecificEditorSettingsDescription": "Remplacer les paramètres de l'éditeur pour le langage" +} \ No newline at end of file diff --git a/i18n/fra/extensions/git/out/askpass-main.i18n.json b/i18n/fra/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..b588b3d516 --- /dev/null +++ b/i18n/fra/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "Informations d'identification manquantes ou non valides." +} \ No newline at end of file diff --git a/i18n/fra/extensions/git/out/commands.i18n.json b/i18n/fra/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..13084a1c6e --- /dev/null +++ b/i18n/fra/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "Étiquette à {0}", + "remote branch at": "Branche distante à {0}", + "create branch": "$(plus) Créer nouvelle branche", + "repourl": "URL du dépôt", + "parent": "Répertoire parent", + "cloning": "Clonage du dépôt git...", + "openrepo": "Ouvrir le dépôt", + "proposeopen": "Voulez-vous ouvrir le dépôt cloné ?", + "path to init": "Chemin du dossier", + "provide path": "Veuillez fournir un chemin de dossier pour initialiser un dépôt Git", + "HEAD not available": "La version HEAD de '{0}' n'est pas disponible.", + "confirm stage files with merge conflicts": "Voulez-vous vraiment créer {0} fichiers avec des conflits de fusion ?", + "confirm stage file with merge conflicts": "Voulez-vous vraiment créer {0} avec des conflits de fusion ?", + "yes": "Oui", + "confirm revert": "Voulez-vous vraiment restaurer les changements sélectionnés dans {0} ?", + "revert": "Restaurer les modifications", + "discard": "Ignorer les modifications", + "confirm delete": "Êtes-vous sûr de vouloir SUPPRIMER {0} ?", + "delete file": "Supprimer le fichier", + "confirm discard": "Voulez-vous vraiment abandonner les changements apportés à {0} ?", + "confirm discard multiple": "Voulez-vous vraiment abandonner les changements apportés à {0} fichiers ?", + "warn untracked": "Ceci effacera les fichiers {0} non suivis !", + "confirm discard all single": "Voulez-vous vraiment abandonner les changements apportés à {0} ?", + "confirm discard all": "Voulez-vous vraiment ignorer TOUTES les modifications dans {0} fichiers ?\nCette opération est IRRÉVERSIBLE.\nVotre plage de travail actuelle sera DÉFINITIVEMENT PERDUE.", + "discardAll multiple": "Ignorer 1 fichier", + "discardAll": "Ignorer les {0} fichiers", + "confirm delete multiple": "Voulez-vous vraiment SUPPRIMER {0} fichiers ?", + "delete files": "Supprimer des fichiers", + "there are untracked files single": "Le fichier non suivi suivant sera SUPPRIMÉ DU DISQUE s'il est ignoré : {0}.", + "there are untracked files": "{0} fichiers non suivis seront SUPPRIMÉS DU DISQUE s'ils sont ignorés.", + "confirm discard all 2": "{0}\n\nCette opération est IRRÉVERSIBLE, votre plage de travail actuelle sera DÉFINITIVEMENT PERDUE.", + "yes discard tracked": "Ignorer 1 fichier suivi", + "yes discard tracked multiple": "Ignorer {0} fichiers suivis", + "no staged changes": "Aucune modification en attente à valider.\n\nVoulez-vous automatiquement mettre en attente toutes vos modifications et les valider directement ?", + "always": "Toujours", + "no changes": "Il n'existe aucun changement à valider.", + "commit message": "Message de validation", + "provide commit message": "Indiquez un message de validation", + "select a ref to checkout": "Sélectionner une référence à extraire", + "branch name": "Nom de la branche", + "provide branch name": "Fournissez un nom de branche", + "select branch to delete": "Sélectionner une branche à supprimer", + "confirm force delete branch": "La branche '{0}' n'est pas complètement fusionnée. Supprimer quand même ?", + "delete branch": "Supprimer la branche", + "select a branch to merge from": "Sélectionner une branche à fusionner", + "merge conflicts": "Il existe des conflits de fusion. Corrigez-les avant la validation.", + "tag name": "Nom de la balise", + "provide tag name": "Spécifiez un nom de balise", + "tag message": "Message", + "provide tag message": "Spécifiez un message pour annoter la balise", + "no remotes to pull": "Votre dépôt n'a aucun dépôt distant configuré pour un Pull.", + "pick remote pull repo": "Choisir un dépôt distant duquel extraire la branche", + "no remotes to push": "Votre dépôt n'a aucun dépôt distant configuré pour un Push.", + "push with tags success": "Envoyé (push) avec des balises.", + "nobranch": "Vous devez extraire une branche dont vous souhaitez effectuer le Push vers un emplacement distant.", + "pick remote": "Choisissez un dépôt distant où publier la branche '{0}' :", + "sync is unpredictable": "Cette action effectue un transfert (Push) et un tirage (Pull) des validations à destination et en provenance de '{0}'.", + "ok": "OK", + "never again": "OK, ne plus afficher", + "no remotes to publish": "Votre dépôt n'a aucun dépôt distant configuré pour une publication.", + "no changes stash": "Aucune modification à remiser (stash).", + "provide stash message": "Spécifier éventuellement un message pour la remise (stash)", + "stash message": "Message pour la remise (stash)", + "no stashes": "Aucune remise (stash) à restaurer.", + "pick stash to pop": "Choisir une remise (stash) à appliquer et supprimer", + "clean repo": "Nettoyez l'arborescence de travail de votre dépôt avant l'extraction.", + "cant push": "Push impossible des références vers la branche distante. Exécutez d'abord 'Extraire' pour intégrer vos modifications.", + "git error details": "Git : {0}", + "git error": "Erreur Git", + "open git log": "Ouvrir le journal Git" +} \ No newline at end of file diff --git a/i18n/fra/extensions/git/out/main.i18n.json b/i18n/fra/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..c2348d298f --- /dev/null +++ b/i18n/fra/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "Utilisation de git {0} à partir de {1}", + "updateGit": "Mettre à jour Git", + "neverShowAgain": "Ne plus afficher", + "git20": "Git {0} semble installé. Le code fonctionne mieux avec git >= 2" +} \ No newline at end of file diff --git a/i18n/fra/extensions/git/out/model.i18n.json b/i18n/fra/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..e937187934 --- /dev/null +++ b/i18n/fra/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "Aucun dépôt disponible", + "pick repo": "Choisir un dépôt" +} \ No newline at end of file diff --git a/i18n/fra/extensions/git/out/repository.i18n.json b/i18n/fra/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..feaf655b1f --- /dev/null +++ b/i18n/fra/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Ouvrir", + "index modified": "Index modifié", + "modified": "Modifié", + "index added": "Index ajouté", + "index deleted": "Index supprimé", + "deleted": "Supprimé", + "index renamed": "Index renommé", + "index copied": "Index copié", + "untracked": "Non suivi", + "ignored": "Ignoré", + "both deleted": "Tous deux supprimés", + "added by us": "Ajouté par nos soins", + "deleted by them": "Supprimé par eux", + "added by them": "Ajouté par eux", + "deleted by us": "Supprimé par nos soins", + "both added": "Tous deux ajoutés", + "both modified": "Tous deux modifiés", + "commit": "Commit", + "merge changes": "Fusionner les modifications", + "staged changes": "Modifications en zone de transit", + "changes": "Modifications", + "ok": "OK", + "neveragain": "Ne plus afficher", + "huge": "Le dépôt Git dans '{0}' a trop de modifications actives, seul un sous-ensemble de fonctionnalités Git sera activé." +} \ No newline at end of file diff --git a/i18n/fra/extensions/git/out/scmProvider.i18n.json b/i18n/fra/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..a7e7efcb61 --- /dev/null +++ b/i18n/fra/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "Commit" +} \ No newline at end of file diff --git a/i18n/fra/extensions/git/out/statusbar.i18n.json b/i18n/fra/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..41b08a37d6 --- /dev/null +++ b/i18n/fra/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "Extraire...", + "sync changes": "Synchroniser les modifications", + "publish changes": "Publier les modifications", + "syncing changes": "Synchronisation des modifications..." +} \ No newline at end of file diff --git a/i18n/fra/extensions/git/package.i18n.json b/i18n/fra/extensions/git/package.i18n.json new file mode 100644 index 0000000000..6253e6cf5f --- /dev/null +++ b/i18n/fra/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "Cloner", + "command.init": "Initialiser le dépôt", + "command.close": "Fermer le dépôt", + "command.refresh": "Actualiser", + "command.openChange": "Ouvrir les modifications", + "command.openFile": "Ouvrir le fichier", + "command.openHEADFile": "Ouvrir le fichier (HEAD)", + "command.stage": "Mettre en attente les modifications", + "command.stageAll": "Mettre en attente toutes les modifications", + "command.stageSelectedRanges": "Mettre en attente les plages sélectionnées", + "command.revertSelectedRanges": "Restaurer les portées sélectionnées", + "command.unstage": "Annuler la mise en attente des modifications", + "command.unstageAll": "Annuler la mise en attente de toutes les modifications", + "command.unstageSelectedRanges": "Annuler la mise en attente des plages sélectionnées", + "command.clean": "Ignorer les modifications", + "command.cleanAll": "Ignorer toutes les modifications", + "command.commit": "Commit", + "command.commitStaged": "Valider le contenu en zone de transit", + "command.commitStagedSigned": "Valider les modifications en attente (signé)", + "command.commitStagedAmend": "Valider les modifications en attente (modifier)", + "command.commitAll": "Valider tout", + "command.commitAllSigned": "Valider tout (signé)", + "command.commitAllAmend": "Tout Valider (Modifier)", + "command.undoCommit": "Annuler la dernière validation", + "command.checkout": "Extraire vers...", + "command.branch": "Créer une branche...", + "command.deleteBranch": "Supprimer la branche...", + "command.merge": "Fusionner la branche...", + "command.createTag": "Créer une étiquette", + "command.pull": "Pull", + "command.pullRebase": "Pull (rebaser)", + "command.pullFrom": "Extraire de...", + "command.push": "Push", + "command.pushTo": "Transfert (Push) vers...", + "command.pushWithTags": "Transférer (Push) avec les étiquettes", + "command.sync": "Synchroniser", + "command.publish": "Publier la branche", + "command.showOutput": "Afficher la sortie Git", + "command.ignore": "Ajouter un fichier à .gitignore", + "command.stash": "Remiser (stash)", + "command.stashPop": "Appliquer et supprimer la remise...", + "command.stashPopLatest": "Appliquer et supprimer la dernière remise", + "config.enabled": "Indique si git est activé", + "config.path": "Chemin d'accès à l'exécutable git", + "config.autorefresh": "Indique si l'actualisation automatique est activée", + "config.autofetch": "Indique si la récupération automatique est activée", + "config.enableLongCommitWarning": "Indique si les longs messages de validation doivent faire l'objet d'un avertissement", + "config.confirmSync": "Confirmer avant de synchroniser des dépôts git", + "config.countBadge": "Contrôle le compteur de badges Git. La valeur 'toutes' compte toutes les modifications. La valeur 'suivies' compte uniquement les modifications suivies. La valeur 'désactivé' désactive le compteur.", + "config.checkoutType": "Contrôle le type des branches répertoriées pendant l'exécution de 'Extraire vers...'. La valeur 'toutes' montre toutes les références, la valeur 'locales' montre uniquement les branches locales, la valeur 'balises' montre uniquement les balises et la valeur 'distantes' montre uniquement les branches distantes.", + "config.ignoreLegacyWarning": "Ignore l'avertissement Git hérité", + "config.ignoreLimitWarning": "Ignore l'avertissement quand il y a trop de modifications dans un dépôt", + "config.defaultCloneDirectory": "Emplacement par défaut où cloner un dépôt git", + "config.enableSmartCommit": "Validez toutes les modifications en l'absence de modifications en attente.", + "config.enableCommitSigning": "Permet de valider en signant avec GPG.", + "config.discardAllScope": "Contrôle les modifications ignorées par la commande 'Ignorer toutes les modifications'. 'all' ignore toutes les modifications. 'tracked' ignore uniquement les fichiers suivis. 'prompt' affiche un message d'invite chaque fois que l’action est exécutée." +} \ No newline at end of file diff --git a/i18n/fra/extensions/grunt/out/main.i18n.json b/i18n/fra/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..3435414af6 --- /dev/null +++ b/i18n/fra/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Échec de la détection automatique des tâches Grunt avec l'erreur : {0}" +} \ No newline at end of file diff --git a/i18n/fra/extensions/grunt/package.i18n.json b/i18n/fra/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..e4c30c0336 --- /dev/null +++ b/i18n/fra/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "Contrôle si la détection automatique des tâches Grunt est activée ou désactivée. La valeur par défaut est activée." +} \ No newline at end of file diff --git a/i18n/fra/extensions/gulp/out/main.i18n.json b/i18n/fra/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..aa8dd1f40c --- /dev/null +++ b/i18n/fra/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Échec de la détection automatique des tâches Gulp avec l'erreur : {0}" +} \ No newline at end of file diff --git a/i18n/fra/extensions/gulp/package.i18n.json b/i18n/fra/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..d70b3ac3b0 --- /dev/null +++ b/i18n/fra/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "Contrôle si la détection automatique des tâches Gulp est activée ou désactivée. La valeur par défaut est activée." +} \ No newline at end of file diff --git a/i18n/fra/extensions/html/client/out/htmlMain.i18n.json b/i18n/fra/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..bcfe490686 --- /dev/null +++ b/i18n/fra/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "Serveur de langage HTML" +} \ No newline at end of file diff --git a/i18n/fra/extensions/html/package.i18n.json b/i18n/fra/extensions/html/package.i18n.json new file mode 100644 index 0000000000..84a5f9e169 --- /dev/null +++ b/i18n/fra/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "Activer/désactiver le formateur HTML par défaut.", + "html.format.wrapLineLength.desc": "Nombre maximal de caractères par ligne (0 = désactiver).", + "html.format.unformatted.desc": "Liste des balises, séparées par des virgules, qui ne doivent pas être remises en forme. 'null' correspond par défaut à toutes les balises répertoriées à l'adresse https://www.w3.org/TR/html5/dom.html#phrasing-content.", + "html.format.contentUnformatted.desc": "Liste des balises, séparées par des virgules, dont le contenu ne doit pas être remis en forme. 'null' correspond par défaut à toutes les balises 'pre'.", + "html.format.indentInnerHtml.desc": "Mettez en retrait les sections et .", + "html.format.preserveNewLines.desc": "Spécifie si les sauts de ligne existants qui précèdent les éléments doivent être conservés. Fonctionne uniquement devant les éléments, pas dans les balises, ni pour du texte.", + "html.format.maxPreserveNewLines.desc": "Nombre maximal de sauts de ligne à conserver dans un bloc. Utilisez 'null' pour indiquer une valeur illimitée.", + "html.format.indentHandlebars.desc": "Mettez en forme et en retrait {{#foo}}, ainsi que {{/foo}}.", + "html.format.endWithNewline.desc": "Finissez par un caractère de nouvelle ligne.", + "html.format.extraLiners.desc": "Liste de balises, séparées par une virgule, qui doivent être précédées d'une nouvelle ligne. 'null' prend par défaut la valeur \"head, body, /html\".", + "html.format.wrapAttributes.desc": "Retour à la ligne des attributs.", + "html.format.wrapAttributes.auto": "Retour automatique à la ligne des attributs uniquement en cas de dépassement de la longueur de la ligne.", + "html.format.wrapAttributes.force": "Retour automatique à la ligne de chaque attribut, sauf le premier.", + "html.format.wrapAttributes.forcealign": "Retour automatique à la ligne de chaque attribut, sauf le premier, avec maintien de l'alignement.", + "html.format.wrapAttributes.forcemultiline": "Retour automatique à la ligne de chaque attribut.", + "html.suggest.angular1.desc": "Permet de configurer la prise en charge intégrée du langage HTML pour suggérer des balises et propriétés Angular V1.", + "html.suggest.ionic.desc": "Permet de configurer la prise en charge intégrée du langage HTML pour suggérer des balises, des propriétés et des valeurs Ionic.", + "html.suggest.html5.desc": "Permet de configurer la prise en charge intégrée du langage HTML pour suggérer des balises, des propriétés et des valeurs HTML5.", + "html.trace.server.desc": "Trace la communication entre VS Code et le serveur de langage HTML.", + "html.validate.scripts": "Configure la validation des scripts incorporés par la prise en charge du langage HTML intégré.", + "html.validate.styles": "Configure la validation des styles incorporés par la prise en charge du langage HTML intégré.", + "html.autoClosingTags": "Activez/désactivez la fermeture automatique des balises HTML." +} \ No newline at end of file diff --git a/i18n/fra/extensions/jake/out/main.i18n.json b/i18n/fra/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..63fb03566f --- /dev/null +++ b/i18n/fra/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Échec de la détection automatique des tâches Jake avec l'erreur : {0}" +} \ No newline at end of file diff --git a/i18n/fra/extensions/jake/package.i18n.json b/i18n/fra/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..4140ba062a --- /dev/null +++ b/i18n/fra/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "Contrôle si la détection automatique des tâches Jake est activée ou désactivée. La valeur par défaut est activée." +} \ No newline at end of file diff --git a/i18n/fra/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/fra/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..c0de3c8895 --- /dev/null +++ b/i18n/fra/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "Fichier bower.json par défaut", + "json.bower.error.repoaccess": "Échec de la requête destinée au dépôt bower : {0}", + "json.bower.latest.version": "dernière" +} \ No newline at end of file diff --git a/i18n/fra/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/fra/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..d117b812d9 --- /dev/null +++ b/i18n/fra/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "Fichier package.json par défaut", + "json.npm.error.repoaccess": "Échec de la requête destinée au dépôt NPM : {0}", + "json.npm.latestversion": "Dernière version du package", + "json.npm.majorversion": "Correspond à la version principale la plus récente (1.x.x)", + "json.npm.minorversion": "Correspond à la version mineure la plus récente (1.2.x)", + "json.npm.version.hover": "Dernière version : {0}" +} \ No newline at end of file diff --git a/i18n/fra/extensions/json/client/out/jsonMain.i18n.json b/i18n/fra/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..a6798bd696 --- /dev/null +++ b/i18n/fra/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "Serveur de langage JSON" +} \ No newline at end of file diff --git a/i18n/fra/extensions/json/package.i18n.json b/i18n/fra/extensions/json/package.i18n.json new file mode 100644 index 0000000000..989dc2f94b --- /dev/null +++ b/i18n/fra/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "Associer les schémas aux fichiers JSON dans le projet actif", + "json.schemas.url.desc": "URL de schéma ou chemin relatif d'un schéma dans le répertoire actif", + "json.schemas.fileMatch.desc": "Tableau de modèles de fichiers pour la recherche de correspondances durant la résolution de fichiers JSON en schémas.", + "json.schemas.fileMatch.item.desc": "Modèle de fichier pouvant contenir '*' pour la recherche de correspondances durant la résolution de fichiers JSON en schémas.", + "json.schemas.schema.desc": "Définition de schéma pour l'URL indiquée. Le schéma doit être fourni uniquement pour éviter les accès à l'URL du schéma.", + "json.format.enable.desc": "Activer/désactiver le formateur JSON par défaut (nécessite un redémarrage)", + "json.tracing.desc": "Trace la communication entre VS Code et le serveur de langage JSON.", + "json.colorDecorators.enable.desc": "Active ou désactive les éléments décoratifs de couleurs", + "json.colorDecorators.enable.deprecationMessage": "Le paramètre 'json.colorDecorators.enable' a été déprécié en faveur de 'editor.colorDecorators'." +} \ No newline at end of file diff --git a/i18n/fra/extensions/markdown/out/extension.i18n.json b/i18n/fra/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..3db7105d32 --- /dev/null +++ b/i18n/fra/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "Impossible de charger 'markdown.styles' : {0}" +} \ No newline at end of file diff --git a/i18n/fra/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/fra/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..fb59880fba --- /dev/null +++ b/i18n/fra/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "Du contenu a été désactivé dans ce document", + "preview.securityMessage.title": "Le contenu potentiellement dangereux ou précaire a été désactivé dans l’aperçu du format markdown. Modifier le paramètre de sécurité Aperçu Markdown afin d’autoriser les contenus non sécurisés ou activer les scripts", + "preview.securityMessage.label": "Avertissement de sécurité de contenu désactivé" +} \ No newline at end of file diff --git a/i18n/fra/extensions/markdown/out/security.i18n.json b/i18n/fra/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..b4f5bdfc19 --- /dev/null +++ b/i18n/fra/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "Strict", + "strict.description": "Charger uniquement le contenu sécurisé", + "insecureContent.title": "Autoriser le contenu non sécurisé", + "insecureContent.description": "Activer le chargement de contenu sur http", + "disable.title": "Désactiver", + "disable.description": "Autorisez tout le contenu et l’exécution des scripts. Non recommandé", + "moreInfo.title": "Informations", + "preview.showPreviewSecuritySelector.title": "Sélectionner les paramètres de sécurité pour les aperçus Markdown dans cet espace de travail" +} \ No newline at end of file diff --git a/i18n/fra/extensions/markdown/package.i18n.json b/i18n/fra/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..ad8be1f06c --- /dev/null +++ b/i18n/fra/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "Définit l'affichage des sauts de ligne dans l'aperçu Markdown. Si la valeur est 'true', crée un
pour chaque nouvelle ligne.", + "markdown.preview.linkify": "Activez ou désactivez la conversion de texte de type URL en liens dans l’aperçu Markdown.", + "markdown.preview.doubleClickToSwitchToEditor.desc": "Double-cliquez dans l'aperçu Markdown pour passer à l'éditeur.", + "markdown.preview.fontFamily.desc": "Contrôle la famille de polices utilisée dans l'aperçu Markdown.", + "markdown.preview.fontSize.desc": "Contrôle la taille de police en pixels utilisée dans l'aperçu Markdown.", + "markdown.preview.lineHeight.desc": "Contrôle la hauteur de ligne utilisée dans l'aperçu Markdown. Ce nombre est relatif à la taille de police.", + "markdown.preview.markEditorSelection.desc": "Permet de marquer la sélection actuelle de l'éditeur dans l'aperçu Markdown.", + "markdown.preview.scrollEditorWithPreview.desc": "Quand l'aperçu Markdown défile, l'affichage de l'éditeur est mis à jour.", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "Fait défiler l'aperçu Markdown pour afficher la ligne sélectionnée dans l'éditeur.", + "markdown.preview.title": "Ouvrir l'aperçu", + "markdown.previewFrontMatter.dec": "Définit comment les pages liminaires YAML doivent être affichées dans l'aperçu Markdown. L'option 'hide' supprime les pages liminaires. Sinon, elles sont traitées comme du contenu Markdown.", + "markdown.previewSide.title": "Ouvrir l'aperçu sur le côté", + "markdown.showSource.title": "Afficher la source", + "markdown.styles.dec": "Liste d'URL ou de chemins locaux de feuilles de style CSS à utiliser dans l'aperçu Markdown. Les chemins relatifs sont interprétés par rapport au dossier ouvert dans l'explorateur. S'il n'y a aucun dossier ouvert, ils sont interprétés par rapport à l'emplacement du fichier Markdown. Tous les signes '\\' doivent être écrits sous la forme '\\\\'.", + "markdown.showPreviewSecuritySelector.title": "Changer les paramètres de sécurité de l'aperçu", + "markdown.trace.desc": "Active la journalisation du débogage pour l'extension Markdown.", + "markdown.refreshPreview.title": "Actualiser l’aperçu" +} \ No newline at end of file diff --git a/i18n/fra/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/fra/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..f899255a8f --- /dev/null +++ b/i18n/fra/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "Accepter la modification actuelle", + "acceptIncomingChange": "Accepter la modification entrante", + "acceptBothChanges": "Accepter les deux modifications", + "compareChanges": "Comparer les modifications" +} \ No newline at end of file diff --git a/i18n/fra/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/fra/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..be205c03a6 --- /dev/null +++ b/i18n/fra/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "Le curseur de l'éditeur ne se trouve pas dans un conflit de fusion", + "compareChangesTitle": "{0} : Modifications actuelles ⟷ Modifications entrantes", + "cursorOnCommonAncestorsRange": "Le curseur de l'éditeur se trouve dans le bloc d'ancêtres commun, déplacez-le dans le bloc \"current\" ou \"incoming\"", + "cursorOnSplitterRange": "Le curseur de l'éditeur se trouve dans le séparateur du conflit de fusion, déplacez-le dans le bloc \"actuelles\" ou \"entrantes\"", + "noConflicts": "Aucun conflit de fusion dans ce fichier", + "noOtherConflictsInThisFile": "Aucun autre conflit de fusion dans ce fichier" +} \ No newline at end of file diff --git a/i18n/fra/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/fra/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..4c06dfdd21 --- /dev/null +++ b/i18n/fra/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(Modification actuelle)", + "incomingChange": "(Modification entrante)" +} \ No newline at end of file diff --git a/i18n/fra/extensions/merge-conflict/package.i18n.json b/i18n/fra/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..023f087831 --- /dev/null +++ b/i18n/fra/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "Conflit de fusion", + "command.accept.all-incoming": "Accepter toutes les modifications entrantes", + "command.accept.all-both": "Accepter les deux", + "command.accept.current": "Accepter les modifications actuelles", + "command.accept.incoming": "Accepter les modifications entrantes", + "command.accept.selection": "Accepter la sélection", + "command.accept.both": "Accepter les deux", + "command.next": "Conflit suivant", + "command.previous": "Conflit précédent", + "command.compare": "Conflit de comparaison des modifications actuelles", + "config.title": "Conflit de fusion", + "config.codeLensEnabled": "Activer/désactiver le bloc CodeLens du conflit de fusion dans l'éditeur", + "config.decoratorsEnabled": "Activer/désactiver les éléments décoratifs du conflit de fusion dans l'éditeur" +} \ No newline at end of file diff --git a/i18n/fra/extensions/npm/package.i18n.json b/i18n/fra/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..5abd4d9dbf --- /dev/null +++ b/i18n/fra/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "Contrôle si la détection automatique des scripts npm est activée ou désactivée. La valeur par défaut est activée.", + "config.npm.runSilent": "Exécuter les commandes npm avec l'option `--silent`" +} \ No newline at end of file diff --git a/i18n/fra/extensions/php/out/features/validationProvider.i18n.json b/i18n/fra/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..f8771201fb --- /dev/null +++ b/i18n/fra/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "Autorisez-vous l'exécution de {0} (défini en tant que paramètre d'espace de travail) pour effectuer une validation lint sur les fichiers PHP ?", + "php.yes": "Autoriser", + "php.no": "Interdire", + "wrongExecutable": "Impossible d'effectuer la validation, car {0} n'est pas un exécutable PHP valide. Utilisez le paramètre 'php.validate.executablePath' pour configurer l'exécutable PHP.", + "noExecutable": "Impossible d'effectuer la validation, car aucun exécutable PHP n'est défini. Utilisez le paramètre 'php.validate.executablePath' pour configurer l'exécutable PHP.", + "unknownReason": "Échec de l'exécution de php avec le chemin : {0}. Raison inconnue." +} \ No newline at end of file diff --git a/i18n/fra/extensions/php/package.i18n.json b/i18n/fra/extensions/php/package.i18n.json new file mode 100644 index 0000000000..9acda1f026 --- /dev/null +++ b/i18n/fra/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "Configure l'activation ou la désactivation des suggestions de langage PHP intégrées. La fonctionnalité de prise en charge suggère les variables globales et les variables de session PHP.", + "configuration.validate.enable": "Activez/désactivez la validation PHP intégrée.", + "configuration.validate.executablePath": "Pointe vers l'exécutable PHP.", + "configuration.validate.run": "Spécifie si linter est exécuté au moment de l'enregistrement ou de la saisie.", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "Interdire l'exécutable de validation PHP (défini comme paramètre d'espace de travail)" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/fra/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..f4d535a28e --- /dev/null +++ b/i18n/fra/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInformation": "Informations", + "doNotCheckAgain": "Ne plus vérifier", + "close": "Fermer", + "updateTscCheck": "Mise à jour du paramètre utilisateur 'typescript.check.tscVersion' avec la valeur false" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/fra/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..5f646a5d36 --- /dev/null +++ b/i18n/fra/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "Acquisition des typings...", + "acquiringTypingsDetail": "Acquisition des définitions typings pour IntelliSense." +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/fra/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..9edaac3a78 --- /dev/null +++ b/i18n/fra/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "Active la vérification sémantique dans un fichier JavaScript. Doit se trouver au début d'un fichier.", + "ts-nocheck": "Désactive la vérification sémantique dans un fichier JavaScript. Doit se trouver au début d'un fichier.", + "ts-ignore": "Supprime les erreurs @ts-check sur la ligne suivante d'un fichier." +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/fra/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..68debddc0f --- /dev/null +++ b/i18n/fra/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 implémentation", + "manyImplementationLabel": "{0} implémentations", + "implementationsErrorLabel": "Impossible de déterminer les implémentations" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/fra/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..7887b8849b --- /dev/null +++ b/i18n/fra/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "Commentaire JSDoc" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/fra/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..03a3300b7c --- /dev/null +++ b/i18n/fra/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 référence", + "manyReferenceLabel": "{0} références", + "referenceErrorLabel": "Impossible de déterminer les références" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/fra/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..452df89df1 --- /dev/null +++ b/i18n/fra/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "watch - {0}", + "buildTscLabel": "build - {0}" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/typescriptMain.i18n.json b/i18n/fra/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..4e18ca735b --- /dev/null +++ b/i18n/fra/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "Ouvrez un dossier dans VS Code pour utiliser un projet TypeScript ou JavaScript", + "typescript.projectConfigUnsupportedFile": "Impossible de déterminer le projet TypeScript ou JavaScript. Type de fichier non pris en charge", + "typescript.projectConfigCouldNotGetInfo": "Impossible de déterminer le projet TypeScript ou JavaScript", + "typescript.noTypeScriptProjectConfig": "Le fichier ne fait pas partie d'un projet TypeScript", + "typescript.noJavaScriptProjectConfig": "Le fichier ne fait pas partie d'un projet JavaScript", + "typescript.configureTsconfigQuickPick": "Configurer tsconfig.json", + "typescript.configureJsconfigQuickPick": "Configurer jsconfig.json", + "typescript.projectConfigLearnMore": "En savoir plus" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/fra/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..5f65b0753c --- /dev/null +++ b/i18n/fra/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "Le chemin {0} ne pointe pas vers une installation tsserver valide. Utilisation par défaut de la version TypeScript groupée.", + "serverCouldNotBeStarted": "Impossible de démarrer le serveur de langage TypeScript. Message d'erreur : {0}", + "typescript.openTsServerLog.notSupported": "La journalisation du serveur TS nécessite TS 2.2.2+", + "typescript.openTsServerLog.loggingNotEnabled": "La journalisation du serveur TS est désactivée. Définissez 'typescript.tsserver.log' et redémarrez le serveur TS pour activer la journalisation", + "typescript.openTsServerLog.enableAndReloadOption": "Activer la journalisation et redémarrer le serveur TS", + "typescript.openTsServerLog.noLogFile": "Le serveur TS n'a pas démarré la journalisation.", + "openTsServerLog.openFileFailedFailed": "Impossible d'ouvrir le fichier journal du serveur TS", + "serverDiedAfterStart": "Le service de langage TypeScript s'est subitement arrêté 5 fois juste après avoir démarré. Il n'y aura pas d'autres redémarrages.", + "serverDiedReportIssue": "Signaler un problème", + "serverDied": "Le service de langage TypeScript s'est subitement arrêté 5 fois au cours des 5 dernières minutes." +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/utils/api.i18n.json b/i18n/fra/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..a51505df5e --- /dev/null +++ b/i18n/fra/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "version non valide" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/utils/logger.i18n.json b/i18n/fra/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/fra/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/fra/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..0d83223cb2 --- /dev/null +++ b/i18n/fra/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "Pour activer les fonctionnalités de langage JavaScript/TypeScript à l'échelle du projet, excluez les dossiers contenant de nombreux fichiers, par exemple : {0}", + "hintExclude.generic": "Pour activer les fonctionnalités de langage JavaScript/TypeScript à l'échelle du projet, excluez les dossiers volumineux contenant des fichiers sources inutilisés.", + "large.label": "Configurer les exclusions", + "hintExclude.tooltip": "Pour activer les fonctionnalités de langage JavaScript/TypeScript à l'échelle du projet, excluez les dossiers volumineux contenant des fichiers sources inutilisés." +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/fra/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..8bae8720ce --- /dev/null +++ b/i18n/fra/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "Récupération (fetch) des données pour l'amélioration de TypeScript IntelliSense", + "typesInstallerInitializationFailed.title": "Impossible d'installer des fichiers de typages pour les fonctionnalités de langage JavaScript. Vérifiez que NPM est installé ou configurez 'typescript.npm' dans vos paramètres utilisateur", + "typesInstallerInitializationFailed.moreInformation": "Informations", + "typesInstallerInitializationFailed.doNotCheckAgain": "Ne plus vérifier", + "typesInstallerInitializationFailed.close": "Fermer" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/fra/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..a4e375c101 --- /dev/null +++ b/i18n/fra/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "Utiliser la version de VS Code", + "useWorkspaceVersionOption": "Utiliser la version de l'espace de travail", + "learnMore": "En savoir plus", + "selectTsVersion": "Sélectionner la version TypeScript utilisée pour les fonctionnalités de langage JavaScript et TypeScript" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/fra/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..1af05d9448 --- /dev/null +++ b/i18n/fra/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "Impossible de charger la version TypeScript dans ce chemin", + "noBundledServerFound": "Le tsserver de VSCode a été supprimé par une autre application, par exemple, un outil de détection de virus mal configuré. Réinstallez VS Code." +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/package.i18n.json b/i18n/fra/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..e85b26c33d --- /dev/null +++ b/i18n/fra/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "Recharger le projet", + "javascript.reloadProjects.title": "Recharger le projet", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "Fonctions complètes avec leur signature de paramètre.", + "typescript.tsdk.desc": "Spécifie le chemin de dossier contenant les fichiers tsserver et lib*.d.ts à utiliser.", + "typescript.disableAutomaticTypeAcquisition": "Désactive l'acquisition de type automatique. Nécessite TypeScript >= 2.0.6.", + "typescript.tsserver.log": "Active la journalisation du serveur TS dans un fichier. Ce journal peut être utilisé pour diagnostiquer les problèmes du serveur TS. Il peut contenir des chemins de fichier, du code source et d'autres informations potentiellement sensibles de votre projet.", + "typescript.tsserver.trace": "Active le traçage des messages envoyés au serveur TS. Cette trace peut être utilisée pour diagnostiquer les problèmes du serveur TS. Elle peut contenir des chemins de fichier, du code source et d'autres informations potentiellement sensibles de votre projet.", + "typescript.validate.enable": "Activez/désactivez la validation TypeScript.", + "typescript.format.enable": "Activez/désactivez le formateur TypeScript par défaut.", + "javascript.format.enable": "Activez/désactivez le formateur JavaScript par défaut.", + "format.insertSpaceAfterCommaDelimiter": "Définit le traitement des espaces après une virgule de délimitation.", + "format.insertSpaceAfterConstructor": "Définit le traitement des espaces après le mot clé constructor. Nécessite TypeScript >= 2.3.0.", + "format.insertSpaceAfterSemicolonInForStatements": " Définit le traitement des espaces après un point-virgule dans une instruction for.", + "format.insertSpaceBeforeAndAfterBinaryOperators": "Définit le traitement des espaces après un opérateur binaire.", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "Définit la gestion des espaces après les mots clés dans une instruction de flux de contrôle.", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "Définit le traitement des espaces après le mot clé function pour les fonctions anonymes.", + "format.insertSpaceBeforeFunctionParenthesis": "Définit la gestion des espaces avant les parenthèses des arguments d'une fonction. Nécessite TypeScript >= 2.1.5.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "Définit le traitement des espaces après l'ouverture et avant la fermeture de parenthèses non vides.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "Définit le traitement des espaces après l'ouverture et avant la fermeture de crochets non vides.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Définit la gestion de l'espace après l'ouverture et avant la fermeture des accolades non vides. Nécessite TypeScript >= 2.3.0.", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "Définit la gestion de l'espace après l'ouverture et avant la fermeture des accolades de la chaîne de modèle. Nécessite TypeScript >= 2.0.6.", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Définit la gestion de l'espace après l'ouverture et avant la fermeture des accolades de l'expression JSX. Nécessite TypeScript >= 2.0.6.", + "format.insertSpaceAfterTypeAssertion": "Définit le traitement des espaces après les assertions de type dans TypeScript. Nécessite TypeScript >= 2.4.", + "format.placeOpenBraceOnNewLineForFunctions": "Définit si une accolade ouvrante dans une fonction est placée ou non sur une nouvelle ligne.", + "format.placeOpenBraceOnNewLineForControlBlocks": "Définit si une accolade ouvrante dans un bloc de contrôle est placée ou non sur une nouvelle ligne.", + "javascript.validate.enable": "Activez/désactivez la validation JavaScript.", + "typescript.goToProjectConfig.title": "Accéder à la configuration du projet", + "javascript.goToProjectConfig.title": "Accéder à la configuration du projet", + "javascript.referencesCodeLens.enabled": "Activez/désactivez les références CodeLens dans les fichiers JavaScript.", + "typescript.referencesCodeLens.enabled": "Activez/désactivez les références CodeLens dans les fichiers TypeScript. Nécessite TypeScript >= 2.0.6.", + "typescript.implementationsCodeLens.enabled": "Activer/désactiver CodeLens dans les implémentations. Nécessite TypeScript >= 2.2.0.", + "typescript.openTsServerLog.title": "Ouvrir le journal du serveur TS", + "typescript.restartTsServer": "Redémarrer le serveur TS", + "typescript.selectTypeScriptVersion.title": "Sélectionner la version de TypeScript", + "jsDocCompletion.enabled": "Activer/désactiver les commentaires JSDoc automatiques", + "javascript.implicitProjectConfig.checkJs": "Activer/désactiver la vérification sémantique des fichiers JavaScript. Les fichiers jsconfig.json ou tsconfig.json existants remplacent ce paramètre. Nécessite TypeScript >=2.3.1.", + "typescript.npm": "Spécifie le chemin de l'exécutable NPM utilisé pour l'acquisition de type automatique. Nécessite TypeScript >= 2.3.4.", + "typescript.check.npmIsInstalled": "Vérifie si NPM est installé pour l'acquisition de type automatique.", + "javascript.nameSuggestions": "Activez/désactivez l'inclusion de noms uniques à partir du fichier dans les listes de suggestions JavaScript.", + "typescript.tsc.autoDetect": "Contrôle si la détection automatique des tâches tsc est activée ou désactivée.", + "typescript.problemMatchers.tsc.label": "Problèmes liés à TypeScript", + "typescript.problemMatchers.tscWatch.label": "Problèmes liés à TypeScript (mode espion)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/fra/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/fra/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/fra/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..b2983b7eb6 --- /dev/null +++ b/i18n/fra/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (s'est reproduit)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/fra/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..41446c2943 --- /dev/null +++ b/i18n/fra/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "entrée" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/fra/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..fcf22863cd --- /dev/null +++ b/i18n/fra/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "Respecter la casse", + "wordsDescription": "Mot entier", + "regexDescription": "Utiliser une expression régulière" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/fra/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..b27b906427 --- /dev/null +++ b/i18n/fra/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Erreur : {0}", + "alertWarningMessage": "Avertissement : {0}", + "alertInfoMessage": "Information : {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/fra/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..ebe6601be5 --- /dev/null +++ b/i18n/fra/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "L'image est trop grande pour être affichée dans l'éditeur. ", + "resourceOpenExternalButton": " Ouvrir l'image en utilisant un programme externe ?", + "nativeBinaryError": "Impossible d'afficher le fichier dans l'éditeur : soit il est binaire, soit il est très volumineux, soit il utilise un encodage de texte non pris en charge.", + "sizeB": "{0} o", + "sizeKB": "{0} Ko", + "sizeMB": "{0} Mo", + "sizeGB": "{0} Go", + "sizeTB": "{0} To" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/fra/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..69b3f733d5 --- /dev/null +++ b/i18n/fra/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "Plus" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/common/errorMessage.i18n.json b/i18n/fra/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..494d7908d8 --- /dev/null +++ b/i18n/fra/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}. Code d'erreur : {1}", + "error.permission.verbose": "Autorisation refusée (HTTP {0})", + "error.permission": "Autorisation refusée", + "error.http.verbose": "{0} (HTTP {1} : {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "Erreur de connexion inconnue ({0})", + "error.connection.unknown": "Une erreur de connexion inconnue s'est produite. Soit vous n'êtes plus connecté à Internet, soit le serveur auquel vous êtes connecté est hors connexion.", + "stackTrace.format": "{0} : {1}", + "error.defaultMessage": "Une erreur inconnue s’est produite. Veuillez consulter le journal pour plus de détails.", + "nodeExceptionMessage": "Une erreur système s'est produite ({0})", + "error.moreErrors": "{0} ({1} erreurs au total)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/common/json.i18n.json b/i18n/fra/src/vs/base/common/json.i18n.json new file mode 100644 index 0000000000..40287f2b33 --- /dev/null +++ b/i18n/fra/src/vs/base/common/json.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Symbole non valide", + "error.invalidNumberFormat": "Format de nombre non valide", + "error.propertyNameExpected": "Nom de propriété attendu", + "error.valueExpected": "Valeur attendue", + "error.colonExpected": "Signe des deux points attendu", + "error.commaExpected": "Virgule attendue", + "error.closeBraceExpected": "Accolade fermante attendue", + "error.closeBracketExpected": "Crochet fermant attendu", + "error.endOfFileExpected": "Fin de fichier attendue" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/fra/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..40287f2b33 --- /dev/null +++ b/i18n/fra/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Symbole non valide", + "error.invalidNumberFormat": "Format de nombre non valide", + "error.propertyNameExpected": "Nom de propriété attendu", + "error.valueExpected": "Valeur attendue", + "error.colonExpected": "Signe des deux points attendu", + "error.commaExpected": "Virgule attendue", + "error.closeBraceExpected": "Accolade fermante attendue", + "error.closeBracketExpected": "Crochet fermant attendu", + "error.endOfFileExpected": "Fin de fichier attendue" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/common/keybindingLabels.i18n.json b/i18n/fra/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..071cf15fd7 --- /dev/null +++ b/i18n/fra/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Maj", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Contrôle", + "shiftKey.long": "Maj", + "altKey.long": "Alt", + "cmdKey.long": "Commande", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/common/processes.i18n.json b/i18n/fra/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..d531a857fe --- /dev/null +++ b/i18n/fra/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "Erreur : les informations de l'exécutable doivent définir une commande de type chaîne.", + "ExecutableParser.isShellCommand": "Avertissement : isShellCommand doit être de type booléen. Valeur {0} ignorée.", + "ExecutableParser.args": "Avertissement : les arguments doivent être de type string[]. Valeur {0} ignorée.", + "ExecutableParser.invalidCWD": "Avertissement : options.cwd doit être de type chaîne. Valeur {0} ignorée." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/common/severity.i18n.json b/i18n/fra/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..65c2c54c41 --- /dev/null +++ b/i18n/fra/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "Erreur", + "sev.warning": "Avertissement", + "sev.info": "Informations" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/node/processes.i18n.json b/i18n/fra/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..790d460807 --- /dev/null +++ b/i18n/fra/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "Impossible d'exécuter une commande d'interpréteur de commandes sur un lecteur UNC." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/node/zip.i18n.json b/i18n/fra/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..b2aa079a25 --- /dev/null +++ b/i18n/fra/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "{0} introuvable dans le zip." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/fra/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..01fa8a02c8 --- /dev/null +++ b/i18n/fra/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0}, sélecteur", + "quickOpenAriaLabel": "sélecteur" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/fra/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..2c99b80cb1 --- /dev/null +++ b/i18n/fra/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "Sélecteur rapide. Tapez pour réduire les résultats.", + "treeAriaLabel": "Sélecteur rapide" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/fra/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..879c0d9ad2 --- /dev/null +++ b/i18n/fra/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Réduire" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/code/electron-main/auth.i18n.json b/i18n/fra/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..7b6c6887ca --- /dev/null +++ b/i18n/fra/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "Authentification proxy requise", + "proxyauth": "Le proxy {0} nécessite une authentification." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/code/electron-main/menus.i18n.json b/i18n/fra/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..88ed4823ea --- /dev/null +++ b/i18n/fra/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "&&Fichier", + "mEdit": "&&Modifier", + "mSelection": "&&Sélection", + "mView": "&&Afficher", + "mGoto": "&&Accéder", + "mDebug": "&&Déboguer", + "mWindow": "Fenêtre", + "mHelp": "&&Aide", + "mTask": "&&Tâches", + "miNewWindow": "Nouvelle &&fenêtre", + "mAbout": "À propos de {0}", + "mServices": "Services", + "mHide": "Masquer {0}", + "mHideOthers": "Masquer les autres", + "mShowAll": "Afficher tout", + "miQuit": "Quitter {0}", + "miNewFile": "&&Nouveau fichier", + "miOpen": "&&Ouvrir...", + "miOpenWorkspace": "&&Ouvrir un espace de travail...", + "miOpenFolder": "Ouvrir le &&dossier", + "miOpenFile": "&&Ouvrir un fichier...", + "miOpenRecent": "Ouvrir les éléments &&récents", + "miSaveWorkspaceAs": "&&Enregistrer l’espace de travail sous...", + "miAddFolderToWorkspace": "&&Ajouter un dossier à l'espace de travail...", + "miSave": "&&Enregistrer", + "miSaveAs": "Enregistrer &&sous...", + "miSaveAll": "Enregistrer to&&ut", + "miAutoSave": "Enregistrement automatique", + "miRevert": "Réta&&blir le fichier", + "miCloseWindow": "Ferm&&er la fenêtre", + "miCloseWorkspace": "&&Fermer l'espace de travail", + "miCloseFolder": "&&Fermer le dossier", + "miCloseEditor": "Fermer l'édit&&eur", + "miExit": "Q&&uitter", + "miOpenSettings": "&&Paramètres", + "miOpenKeymap": "Racco&&urcis clavier", + "miOpenKeymapExtensions": "&&Extensions de mappage de touches", + "miOpenSnippets": "E&&xtraits de code utilisateur", + "miSelectColorTheme": "Thème de &&couleur", + "miSelectIconTheme": "Thème d'&&icône de fichier", + "miPreferences": "&&Préférences", + "miReopenClosedEditor": "&&Rouvrir l'éditeur fermé", + "miMore": "&&Plus...", + "miClearRecentOpen": "Effacer les fichiers &&récents", + "miUndo": "&&Annuler", + "miRedo": "&&Rétablir", + "miCut": "Coupe&&r", + "miCopy": "&&Copier", + "miPaste": "&&Coller", + "miFind": "&&Rechercher", + "miReplace": "&&Remplacer", + "miFindInFiles": "Rechercher dans les f&&ichiers", + "miReplaceInFiles": "Remplacer &&dans les fichiers", + "miEmmetExpandAbbreviation": "Emmet : dé&&velopper l'abréviation", + "miShowEmmetCommands": "E&&mmet...", + "miToggleLineComment": "Afficher/masquer le commen&&taire de ligne", + "miToggleBlockComment": "Afficher/masquer le commentaire de &&bloc", + "miMultiCursorAlt": "Utiliser Alt+Clic pour l'option multicurseur", + "miMultiCursorCmd": "Utiliser Cmd+Clic pour l'option multicurseur", + "miMultiCursorCtrl": "Utiliser Ctrl+Clic pour l'option multicurseur", + "miInsertCursorAbove": "&&Ajouter un curseur au-dessus", + "miInsertCursorBelow": "Aj&&outer un curseur en dessous", + "miInsertCursorAtEndOfEachLineSelected": "Ajouter des c&&urseurs à la fin des lignes", + "miAddSelectionToNextFindMatch": "Ajouter l'occurrence suiva&&nte", + "miAddSelectionToPreviousFindMatch": "Ajouter l'occurrence p&&récédente", + "miSelectHighlights": "Sélectionner toutes les &&occurrences", + "miCopyLinesUp": "&&Copier la ligne en haut", + "miCopyLinesDown": "Co&&pier la ligne en bas", + "miMoveLinesUp": "Déplacer la ligne &&vers le haut", + "miMoveLinesDown": "Déplacer la &&ligne vers le bas", + "miSelectAll": "&&Sélectionner tout", + "miSmartSelectGrow": "Dév&&elopper la sélection", + "miSmartSelectShrink": "&&Réduire la sélection", + "miViewExplorer": "&&Explorateur", + "miViewSearch": "&&Rechercher", + "miViewSCM": "S&&CM", + "miViewDebug": "&&Déboguer", + "miViewExtensions": "E&&xtensions", + "miToggleOutput": "&&Sortie", + "miToggleDebugConsole": "Console de dé&&bogage", + "miToggleIntegratedTerminal": "&&Terminal intégré", + "miMarker": "&&Problèmes", + "miAdditionalViews": "Affic&&hages supplémentaires", + "miCommandPalette": "Palette de &&commandes...", + "miToggleFullScreen": "Plei&&n écran", + "miToggleZenMode": "Activer/désactiver le mode zen", + "miToggleMenuBar": "Activer/désactiver la &&barre de menus", + "miSplitEditor": "Fractionner l'édit&&eur", + "miToggleEditorLayout": "Activer/désactiver la &&disposition du groupe d'éditeurs", + "miToggleSidebar": "Activer/désactiver la &&barre latérale", + "miMoveSidebarRight": "Déplacer la &&barre latérale vers la droite", + "miMoveSidebarLeft": "Déplacer la &&barre latérale vers la gauche", + "miTogglePanel": "Activer/désactiver le &&panneau", + "miHideStatusbar": "&&Masquer la barre d'état", + "miShowStatusbar": "Affic&&her la barre d'état", + "miHideActivityBar": "Masquer la &&Barre d'activités", + "miShowActivityBar": "Afficher la &&Barre d'activités", + "miToggleWordWrap": "Activer/désactiver le retour automatique à la &&ligne", + "miToggleMinimap": "Activer/désactiver la &&minicarte", + "miToggleRenderWhitespace": "Activer/désactiver &&Restituer l'espace", + "miToggleRenderControlCharacters": "Activer/désactiver les &&caractères de contrôle", + "miZoomIn": "&&Zoom avant", + "miZoomOut": "Zoo&&m arrière", + "miZoomReset": "&&Réinitialiser le zoom", + "miBack": "&&Précédent", + "miForward": "&&Suivant", + "miNextEditor": "Éditeur &&suivant", + "miPreviousEditor": "Éditeur pré&&cédent", + "miNextEditorInGroup": "Éditeur &&utilisé suivant dans le groupe", + "miPreviousEditorInGroup": "É&&diteur utilisé précédent dans le groupe", + "miSwitchEditor": "Changer d'é&&diteur", + "miFocusFirstGroup": "&&Premier groupe", + "miFocusSecondGroup": "&&Deuxième groupe", + "miFocusThirdGroup": "&&Troisième groupe", + "miNextGroup": "Groupe &&suivant", + "miPreviousGroup": "Groupe pré&&cédent", + "miSwitchGroup": "Changer de gr&&oupe", + "miGotoFile": "Atteindre le &&fichier...", + "miGotoSymbolInFile": "Atteindre le &&symbole dans le fichier...", + "miGotoSymbolInWorkspace": "Atteindre le symbole dans l'espace de &&travail...", + "miGotoDefinition": "Atteindre la &&définition", + "miGotoTypeDefinition": "Accéder à la définition de &&type", + "miGotoImplementation": "Accéder à l'&&implémentation", + "miGotoLine": "Atteindre la &&ligne...", + "miStartDebugging": "&&Démarrer le débogage", + "miStartWithoutDebugging": "Démarrer &&sans débogage", + "miStopDebugging": "&&Arrêter le débogage", + "miRestart Debugging": "&&Redémarrer le débogage", + "miOpenConfigurations": "Ouvrir les &&configurations", + "miAddConfiguration": "Ajouter une configuration...", + "miStepOver": "Effect&&uer un pas à pas principal", + "miStepInto": "Effectuer un pas à pas déta&&illé", + "miStepOut": "Effectuer un pas à pas s&&ortant", + "miContinue": "&&Continuer", + "miToggleBreakpoint": "Activer/désactiver le poi&&nt d'arrêt", + "miConditionalBreakpoint": "Point d'arrêt &&conditionnel...", + "miColumnBreakpoint": "P&&oint d'arrêt de la colonne", + "miFunctionBreakpoint": "Point d'arrêt sur &&fonction...", + "miNewBreakpoint": "&&Nouveau point d'arrêt", + "miEnableAllBreakpoints": "Activer tous les points d'arrêt", + "miDisableAllBreakpoints": "Désacti&&ver tous les points d'arrêt", + "miRemoveAllBreakpoints": "Supprimer &&tous les points d'arrêt", + "miInstallAdditionalDebuggers": "&&Installer des débogueurs supplémentaires...", + "mMinimize": "Réduire", + "mZoom": "Zoom", + "mBringToFront": "Mettre tout au premier plan", + "miSwitchWindow": "Changer de &&fenêtre...", + "miToggleDevTools": "Activer/désactiver les ou&&tils de développement", + "miAccessibilityOptions": "&&Options d'accessibilité", + "miReportIssues": "S&&ignaler les problèmes", + "miWelcome": "&&Bienvenue", + "miInteractivePlayground": "Terrain de jeu &&interactif", + "miDocumentation": "&&Documentation", + "miReleaseNotes": "Notes de pu&&blication", + "miKeyboardShortcuts": "Référence des racco&&urcis clavier", + "miIntroductoryVideos": "&&Vidéos d'introduction", + "miTipsAndTricks": "&&Conseils et astuces", + "miTwitter": "Re&&joignez-nous sur Twitter", + "miUserVoice": "&&Rechercher parmi les requêtes de fonctionnalités", + "miLicense": "Affic&&her la licence", + "miPrivacyStatement": "Déc&&laration de confidentialité", + "miAbout": "À pr&&opos de", + "miRunTask": "&&Exécuter la tâche...", + "miBuildTask": "Exécuter la &&tâche de génération...", + "miRunningTask": "Afficher les &&tâches en cours...", + "miRestartTask": "R&&edémarrer la tâche en cours d'exécution...", + "miTerminateTask": "&&Terminer la tâche...", + "miConfigureTask": "&&Configurer les tâches", + "miConfigureBuildTask": "Configurer la tâche de génération par dé&&faut", + "accessibilityOptionsWindowTitle": "Options d'accessibilité", + "miRestartToUpdate": "Redémarrer pour mettre à jour...", + "miCheckingForUpdates": "Recherche des mises à jour...", + "miDownloadUpdate": "Télécharger la mise à jour disponible", + "miDownloadingUpdate": "Téléchargement de la mise à jour...", + "miInstallingUpdate": "Installation de la mise à jour...", + "miCheckForUpdates": "Rechercher les mises à jour...", + "aboutDetail": "\nVersion {0}\nCommit {1}\nDate {2}\nShell {3}\nRenderer {4}\nNode {5}\nArchitecture {6}", + "okButton": "OK" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/code/electron-main/window.i18n.json b/i18n/fra/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..0805a8b99d --- /dev/null +++ b/i18n/fra/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "Vous pouvez toujours accéder à la barre de menus en appuyant sur la touche **Alt**." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/code/electron-main/windows.i18n.json b/i18n/fra/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..be630c2ee7 --- /dev/null +++ b/i18n/fra/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "OK", + "pathNotExistTitle": "Le chemin d'accès n'existe pas", + "pathNotExistDetail": "Le chemin d'accès '{0}' ne semble plus exister sur le disque.", + "openWorkspace": "&&Ouvrir...", + "openWorkspaceTitle": "Ouvrir un espace de travail", + "save": "&&Enregistrer", + "doNotSave": "&&Ne pas enregistrer", + "cancel": "Annuler", + "saveWorkspaceMessage": "Voulez-vous enregistrer la configuration de votre espace de travail dans un fichier ?", + "saveWorkspaceDetail": "Enregistrez votre espace de travail si vous avez l’intention de le rouvrir.", + "saveWorkspace": "Enregistrer l’espace de travail", + "reopen": "Rouvrir", + "wait": "Continuer à attendre", + "close": "Fermer", + "appStalled": "La fenêtre ne répond plus", + "appStalledDetail": "Vous pouvez rouvrir ou fermer la fenêtre, ou continuer à patienter.", + "appCrashed": "La fenêtre s'est bloquée", + "appCrashedDetail": "Nous vous prions de nous excuser pour ce désagrément. Vous pouvez rouvrir la fenêtre pour reprendre l'action au moment où elle a été interrompue.", + "open": "Ouvrir", + "openFolder": "Ouvrir le dossier", + "openFile": "Ouvrir le fichier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/code/node/cliProcessMain.i18n.json b/i18n/fra/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..acf76585c2 --- /dev/null +++ b/i18n/fra/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Extension '{0}' introuvable.", + "notInstalled": "L'extension '{0}' n'est pas installée.", + "useId": "Veillez à utiliser l'ID complet de l'extension (serveur de publication inclus). Exemple : {0}", + "successVsixInstall": "L'extension '{0}' a été installée correctement !", + "alreadyInstalled": "L'extension '{0}' est déjà installée.", + "foundExtension": "'{0}' trouvé dans le Marketplace.", + "installing": "Installation...", + "successInstall": "L'extension '{0}' v{1} a été correctement installée !", + "uninstalling": "Désinstallation de {0}...", + "successUninstall": "L'extension '{0}' a été correctement désinstallée !" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/fra/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..e71fbd0a09 --- /dev/null +++ b/i18n/fra/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "Impossible de comparer les fichiers car l'un d'eux est trop volumineux." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/fra/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..de2b23adc5 --- /dev/null +++ b/i18n/fra/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Fermer", + "header": "Différence {0} sur {1} : {2} d'origine, {3} lignes, {4} modifiées, {5} lignes", + "blankLine": "vide", + "equalLine": "{0} d'origine, {1} modifiées : {2}", + "insertLine": "+ {0} modifiées : {1}", + "deleteLine": "- {0} d'origine : {1}", + "editor.action.diffReview.next": "Accéder à la différence suivante", + "editor.action.diffReview.prev": "Accéder la différence précédente" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/fra/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..35c7266bf5 --- /dev/null +++ b/i18n/fra/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "Éditeur", + "fontFamily": "Contrôle la famille de polices.", + "fontWeight": "Contrôle l'épaisseur de police.", + "fontSize": "Contrôle la taille de police en pixels.", + "lineHeight": "Contrôle la hauteur de ligne. Utilisez 0 pour calculer lineHeight à partir de fontSize.", + "letterSpacing": "Définit l'espacement des caractères en pixels.", + "lineNumbers": "Contrôle l'affichage des numéros de ligne. Les valeurs possibles sont 'activé', 'désactivé' et 'relatif'. La valeur 'relatif' indique le numéro de ligne à partir de la position actuelle du curseur.", + "rulers": "Colonnes où afficher les règles verticales", + "wordSeparators": "Caractères utilisés comme séparateurs de mots durant la navigation ou les opérations basées sur les mots", + "tabSize": "Nombre d'espaces correspondant à une tabulation. Ce paramètre est remplacé en fonction du contenu du fichier quand 'editor.detectIndentation' est activé.", + "tabSize.errorMessage": "'number' attendu. Notez que la valeur \"auto\" a été remplacée par le paramètre 'editor.detectIndentation'.", + "insertSpaces": "Des espaces sont insérés quand vous appuyez sur la touche Tab. Ce paramètre est remplacé en fonction du contenu du fichier quand 'editor.detectIndentation' est activé.", + "insertSpaces.errorMessage": "'boolean' attendu. Notez que la valeur \"auto\" a été remplacée par le paramètre 'editor.detectIndentation'.", + "detectIndentation": "Quand vous ouvrez un fichier, 'editor.tabSize' et 'editor.insertSpaces' sont détectés en fonction du contenu du fichier.", + "roundedSelection": "Contrôle si les sélections ont des angles arrondis", + "scrollBeyondLastLine": "Contrôle si l'éditeur défile au-delà de la dernière ligne", + "smoothScrolling": "Contrôle si l'éditeur défilera en utilisant une animation", + "minimap.enabled": "Contrôle si la minicarte est affichée", + "minimap.showSlider": "Contrôle si le curseur de la minicarte est automatiquement masqué. Les valeurs possibles sont 'always' et 'mouseover'", + "minimap.renderCharacters": "Afficher les caractères réels sur une ligne (par opposition aux blocs de couleurs)", + "minimap.maxColumn": "Limiter la largeur de la minicarte pour afficher au maximum un certain nombre de colonnes", + "find.seedSearchStringFromSelection": "Contrôle si nous remplissons la chaîne à rechercher dans le Widget Recherche à partir de la sélection de l'éditeur", + "find.autoFindInSelection": "Contrôle si l'indicateur Rechercher dans la sélection est activé quand plusieurs caractères ou lignes de texte sont sélectionnés dans l'éditeur", + "wordWrap.off": "Le retour automatique à la ligne n'est jamais effectué.", + "wordWrap.on": "Le retour automatique à la ligne s'effectue en fonction de la largeur de la fenêtre d'affichage.", + "wordWrap.wordWrapColumn": "Le retour automatique à la ligne s'effectue en fonction de 'editor.wordWrapColumn'.", + "wordWrap.bounded": "Retour automatique à la ligne au minimum en fonction de la fenêtre d'affichage et de 'editor.wordWrapColumn'.", + "wordWrap": "Contrôle le retour automatique à la ligne. Valeurs possibles :\n - 'off' (désactive le retour automatique à la ligne) ;\n - 'on' (retour automatique à la ligne dans la fenêtre d'affichage) ;\n - 'wordWrapColumn' (retour automatique à la ligne en fonction de 'editor.wordWrapColumn') ou ;\n - 'bounded' (retour automatique à la ligne au minimum en fonction de la fenêtre d'affichage et de 'editor.wordWrapColumn').", + "wordWrapColumn": "Contrôle la colonne de retour automatique à la ligne de l'éditeur quand 'editor.wordWrap' a la valeur 'wordWrapColumn' ou 'bounded'.", + "wrappingIndent": "Contrôle le retrait des lignes renvoyées. La valeur peut être 'none', 'same' ou 'indent'.", + "mouseWheelScrollSensitivity": "Multiplicateur à utiliser pour le 'deltaX' et le 'deltaY' des événements de défilement de la roulette de la souris", + "multiCursorModifier.ctrlCmd": "Mappe vers 'Contrôle' dans Windows et Linux, et vers 'Commande' dans OSX.", + "multiCursorModifier.alt": "Mappe vers 'Alt' dans Windows et Linux, et vers 'Option' dans OSX.", + "multiCursorModifier": "Modificateur à utiliser pour ajouter plusieurs curseurs avec la souris. 'ctrlCmd' mappe vers 'Contrôle' dans Windows et Linux, et vers 'Commande' dans OSX. Les mouvements de souris Accéder à la définition et Ouvrir le lien s'adaptent pour ne pas entrer en conflit avec le modificateur multicurseur.", + "quickSuggestions.strings": "Activez les suggestions rapides dans les chaînes.", + "quickSuggestions.comments": "Activez les suggestions rapides dans les commentaires.", + "quickSuggestions.other": "Activez les suggestions rapides en dehors des chaînes et des commentaires.", + "quickSuggestions": "Contrôle si les suggestions doivent s'afficher automatiquement en cours de frappe", + "quickSuggestionsDelay": "Contrôle le délai en ms au bout duquel les suggestions rapides s'affichent", + "parameterHints": "Active la pop up qui affiche la documentation des paramètres et écrit de l'information pendant que vous écrivez", + "autoClosingBrackets": "Contrôle si l'éditeur doit automatiquement fermer les crochets après les avoir ouverts", + "formatOnType": "Contrôle si l'éditeur doit automatiquement mettre en forme la ligne après la saisie", + "formatOnPaste": "Contrôle si l'éditeur doit automatiquement mettre en forme le contenu collé. Un formateur doit être disponible et doit pouvoir mettre en forme une plage dans un document.", + "autoIndent": "Contrôle si l'éditeur doit automatiquement ajuster l'indentation quand les utilisateurs écrivent, collent ou déplacent des lignes. Les règles d'indentation du langage doivent être disponibles.", + "suggestOnTriggerCharacters": "Contrôle si les suggestions doivent s'afficher automatiquement durant la saisie de caractères de déclenchement", + "acceptSuggestionOnEnter": "Contrôle si les suggestions doivent être acceptées avec 'Entrée', en plus de 'Tab'. Cela permet d'éviter toute ambiguïté entre l'insertion de nouvelles lignes et l'acceptation de suggestions. La valeur 'smart' signifie que vous acceptez uniquement une suggestion avec Entrée quand elle applique une modification de texte", + "acceptSuggestionOnCommitCharacter": "Contrôle si les suggestions doivent être acceptées avec des caractères de validation. Par exemple, en JavaScript, le point-virgule (';') peut être un caractère de validation qui permet d'accepter une suggestion et de taper ce caractère.", + "snippetSuggestions.top": "Afficher des suggestions d’extraits au-dessus d’autres suggestions.", + "snippetSuggestions.bottom": "Afficher des suggestions d’extraits en-dessous d’autres suggestions.", + "snippetSuggestions.inline": "Afficher des suggestions d’extraits avec d’autres suggestions.", + "snippetSuggestions.none": "Ne pas afficher de suggestions d’extrait de code.", + "snippetSuggestions": "Contrôle si les extraits de code s'affichent en même temps que d'autres suggestions, ainsi que leur mode de tri.", + "emptySelectionClipboard": "Contrôle si la copie sans sélection permet de copier la ligne actuelle.", + "wordBasedSuggestions": "Contrôle si la saisie semi-automatique doit être calculée en fonction des mots présents dans le document.", + "suggestFontSize": "Taille de police du widget de suggestion", + "suggestLineHeight": "Hauteur de ligne du widget de suggestion", + "selectionHighlight": "Détermine si l'éditeur doit surligner les correspondances similaires à la sélection", + "occurrencesHighlight": "Contrôle si l'éditeur doit mettre en surbrillance les occurrences de symboles sémantiques", + "overviewRulerLanes": "Contrôle le nombre d'ornements pouvant s'afficher à la même position dans la règle d'aperçu", + "overviewRulerBorder": "Contrôle si une bordure doit être dessinée autour de la règle d'aperçu.", + "cursorBlinking": "Contrôle le style d'animation du curseur. Valeurs possibles : 'blink', 'smooth', 'phase', 'expand' et 'solid'", + "mouseWheelZoom": "Agrandir ou réduire la police de l'éditeur quand l'utilisateur fait tourner la roulette de la souris tout en maintenant la touche Ctrl enfoncée", + "cursorStyle": "Contrôle le style du curseur. Les valeurs acceptées sont 'block', 'block-outline', 'line', 'line-thin', 'underline' et 'underline-thin'", + "fontLigatures": "Active les ligatures de police", + "hideCursorInOverviewRuler": "Contrôle si le curseur doit être masqué dans la règle d'aperçu.", + "renderWhitespace": "Contrôle la façon dont l'éditeur affiche les espaces blancs. Il existe trois options possibles : 'none', 'boundary' et 'all'. L'option 'boundary' n'affiche pas les espaces uniques qui séparent les mots.", + "renderControlCharacters": "Contrôle si l'éditeur doit afficher les caractères de contrôle", + "renderIndentGuides": "Contrôle si l'éditeur doit afficher les repères de mise en retrait", + "renderLineHighlight": "Contrôle la façon dont l'éditeur doit afficher la surbrillance de la ligne active. Les différentes possibilités sont 'none', 'gutter', 'line' et 'all'.", + "codeLens": "Contrôle si l'éditeur affiche les indicateurs CodeLens", + "folding": "Contrôle si le pliage de code est activé dans l'éditeur", + "showFoldingControls": "Définit si les contrôles de réduction sur la bordure sont cachés automatiquement", + "matchBrackets": "Met en surbrillance les crochets correspondants quand l'un d'eux est sélectionné.", + "glyphMargin": "Contrôle si l'éditeur doit afficher la marge de glyphes verticale. La marge de glyphes sert principalement au débogage.", + "useTabStops": "L'insertion et la suppression d'un espace blanc suit les taquets de tabulation", + "trimAutoWhitespace": "Supprimer l'espace blanc de fin inséré automatiquement", + "stablePeek": "Garder les éditeurs d'aperçu ouverts même si l'utilisateur double-clique sur son contenu ou appuie sur la touche Échap.", + "dragAndDrop": "Contrôle si l'éditeur autorise le déplacement des sélections par glisser-déplacer.", + "accessibilitySupport.auto": "L'éditeur utilise les API de la plateforme pour détecter si un lecteur d'écran est attaché.", + "accessibilitySupport.on": "L'éditeur est optimisé en permanence pour une utilisation avec un lecteur d'écran.", + "accessibilitySupport.off": "L'éditeur n'est jamais optimisé pour une utilisation avec un lecteur d'écran.", + "accessibilitySupport": "Contrôle si l'éditeur doit s'exécuter dans un mode optimisé pour les lecteurs d'écran.", + "links": "Contrôle si l'éditeur doit détecter les liens et les rendre cliquables", + "colorDecorators": "Contrôle si l'éditeur doit afficher les éléments décoratifs de couleurs inline et le sélecteur de couleurs.", + "sideBySide": "Contrôle si l'éditeur de différences affiche les différences en mode côte à côte ou inline", + "ignoreTrimWhitespace": "Contrôle si l'éditeur de différences affiche les changements liés aux espaces blancs de début ou de fin comme des différences", + "renderIndicators": "Contrôle si l'éditeur de différences affiche les indicateurs +/- pour les modifications ajoutées/supprimées", + "selectionClipboard": "Contrôle si le presse-papiers primaire Linux doit être pris en charge." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/common/config/defaultConfig.i18n.json b/i18n/fra/src/vs/editor/common/config/defaultConfig.i18n.json new file mode 100644 index 0000000000..8090cdc65c --- /dev/null +++ b/i18n/fra/src/vs/editor/common/config/defaultConfig.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorViewAccessibleLabel": "Contenu d'éditeur" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/fra/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..f1df5ec90f --- /dev/null +++ b/i18n/fra/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "L'éditeur n'est pas accessible pour le moment. Appuyez sur Alt+F1 pour connaître les options.", + "editorViewAccessibleLabel": "Contenu d'éditeur" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/common/controller/cursor.i18n.json b/i18n/fra/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..7d66a24f65 --- /dev/null +++ b/i18n/fra/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "Exception inattendue pendant l'exécution de la commande." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/fra/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..19f1bb11ee --- /dev/null +++ b/i18n/fra/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "Le mode a échoué lors de la création de jetons de l’entrée." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/fra/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..ffedd812b2 --- /dev/null +++ b/i18n/fra/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "Texte brut" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/fra/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..da126e2676 --- /dev/null +++ b/i18n/fra/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "Ces fichiers ont changé pendant ce temps : {0}", + "summary.0": "Aucune modification effectuée", + "summary.nm": "{0} modifications de texte effectuées dans {1} fichiers", + "summary.n0": "{0} modifications de texte effectuées dans un fichier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/fra/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..83c7b82fef --- /dev/null +++ b/i18n/fra/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Ajoute des déclarations de langage.", + "vscode.extension.contributes.languages.id": "ID du langage.", + "vscode.extension.contributes.languages.aliases": "Alias de nom du langage.", + "vscode.extension.contributes.languages.extensions": "Extensions de fichier associées au langage.", + "vscode.extension.contributes.languages.filenames": "Noms de fichiers associés au langage.", + "vscode.extension.contributes.languages.filenamePatterns": "Modèles Glob de noms de fichiers associés au langage.", + "vscode.extension.contributes.languages.mimetypes": "Types MIME associés au langue.", + "vscode.extension.contributes.languages.firstLine": "Expression régulière correspondant à la première ligne d'un fichier du langage.", + "vscode.extension.contributes.languages.configuration": "Chemin relatif d'un fichier contenant les options de configuration du langage." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/fra/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/fra/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/fra/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..3650c05312 --- /dev/null +++ b/i18n/fra/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "Couleur d'arrière-plan de la mise en surbrillance de la ligne à la position du curseur.", + "lineHighlightBorderBox": "Couleur d'arrière-plan de la bordure autour de la ligne à la position du curseur.", + "rangeHighlight": "Couleur d'arrière-plan des plages mises en surbrillance, par exemple par les fonctionnalités de recherche et Quick Open.", + "caret": "Couleur du curseur de l'éditeur.", + "editorCursorBackground": "La couleur de fond du curseur de l'éditeur. Permet de personnaliser la couleur d'un caractère survolé par un curseur de bloc.", + "editorWhitespaces": "Couleur des espaces blancs dans l'éditeur.", + "editorIndentGuides": "Couleur des repères de retrait de l'éditeur.", + "editorLineNumbers": "Couleur des numéros de ligne de l'éditeur.", + "editorRuler": "Couleur des règles de l'éditeur", + "editorCodeLensForeground": "Couleur pour les indicateurs CodeLens", + "editorBracketMatchBackground": "Couleur d'arrière-plan pour les accolades associées", + "editorBracketMatchBorder": "Couleur pour le contour des accolades associées", + "editorOverviewRulerBorder": "Couleur de la bordure de la règle d'apperçu.", + "editorGutter": "Couleur de fond pour la bordure de l'éditeur. La bordure contient les marges pour les symboles et les numéros de ligne.", + "errorForeground": "Couleur de premier plan de la ligne ondulée marquant les erreurs dans l'éditeur.", + "errorBorder": "Couleur de bordure de la ligne ondulée marquant les erreurs dans l'éditeur.", + "warningForeground": "Couleur de premier plan de la ligne ondulée marquant les avertissements dans l'éditeur.", + "warningBorder": "Couleur de bordure de la ligne ondulée marquant les avertissements dans l'éditeur.", + "overviewRulerRangeHighlight": "Couleur du marqueur de la règle d'aperçu pour la mise en évidence de plage.", + "overviewRuleError": "Couleur du marqueur de la règle d'aperçu pour les erreurs.", + "overviewRuleWarning": "Couleur du marqueur de la règle d'aperçu pour les avertissements.", + "overviewRuleInfo": "Couleur du marqueur de la règle d'aperçu pour les informations." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json b/i18n/fra/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json new file mode 100644 index 0000000000..269b62385f --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "introMsg": "Nous vous remercions de tester les options d'accessibilité de VS Code.", + "status": "État :", + "tabFocusModeOnMsg": "Appuyez sur Tab dans l'éditeur pour déplacer le focus vers le prochain élément pouvant être désigné comme élément actif. Activez ou désactivez ce comportement en appuyant sur {0}.", + "tabFocusModeOnMsgNoKb": "Appuyez sur Tab dans l'éditeur pour déplacer le focus vers le prochain élément pouvant être désigné comme élément actif. La commande {0} ne peut pas être déclenchée par une combinaison de touches.", + "tabFocusModeOffMsg": "Appuyez sur Tab dans l'éditeur pour insérer le caractère de tabulation. Activez ou désactivez ce comportement en appuyant sur {0}.", + "tabFocusModeOffMsgNoKb": "Appuyez sur Tab dans l'éditeur pour insérer le caractère de tabulation. La commande {0} ne peut pas être déclenchée par une combinaison de touches.", + "outroMsg": "Vous pouvez masquer cette info-bulle et revenir à l'éditeur en appuyant sur Échap.", + "ShowAccessibilityHelpAction": "Afficher l'aide sur l'accessibilité" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/fra/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..4abb4cad86 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "Atteindre le crochet" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/fra/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..5521c02064 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "Déplacer le point d'insertion vers la gauche", + "caret.moveRight": "Déplacer le point d'insertion vers la droite" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/fra/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..696bfb0c4c --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "Transposer les lettres" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/fra/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..3a99289eee --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "Couper", + "actions.clipboard.copyLabel": "Copier", + "actions.clipboard.pasteLabel": "Coller", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "Copier avec la coloration syntaxique" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/fra/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..3268eaa2a2 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "Activer/désactiver le commentaire de ligne", + "comment.line.add": "Ajouter le commentaire de ligne", + "comment.line.remove": "Supprimer le commentaire de ligne", + "comment.block": "Activer/désactiver le commentaire de bloc" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/fra/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..550f795869 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "Afficher le menu contextuel de l'éditeur" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/fra/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..4fc3506957 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Rechercher", + "placeholder.find": "Rechercher", + "label.previousMatchButton": "Correspondance précédente", + "label.nextMatchButton": "Correspondance suivante", + "label.toggleSelectionFind": "Rechercher dans la sélection", + "label.closeButton": "Fermer", + "label.replace": "Remplacer", + "placeholder.replace": "Remplacer", + "label.replaceButton": "Remplacer", + "label.replaceAllButton": "Tout remplacer", + "label.toggleReplaceButton": "Changer le mode de remplacement", + "title.matchesCountLimit": "Seuls les 999 premiers résultats sont mis en surbrillance. Cependant, toutes les opérations de recherche sont appliquées à l'ensemble du texte.", + "label.matchesLocation": "{0} sur {1}", + "label.noResults": "Aucun résultat" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/fra/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..3e40e91275 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Rechercher", + "placeholder.find": "Rechercher", + "label.previousMatchButton": "Correspondance précédente", + "label.nextMatchButton": "Correspondance suivante", + "label.closeButton": "Fermer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/fra/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..d7324fe727 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "Rechercher", + "findNextMatchAction": "Rechercher suivant", + "findPreviousMatchAction": "Rechercher précédent", + "nextSelectionMatchFindAction": "Sélection suivante", + "previousSelectionMatchFindAction": "Sélection précédente", + "startReplace": "Remplacer", + "addSelectionToNextFindMatch": "Ajouter la sélection à la correspondance de recherche suivante", + "addSelectionToPreviousFindMatch": "Ajouter la sélection à la correspondance de recherche précédente", + "moveSelectionToNextFindMatch": "Déplacer la dernière sélection vers la correspondance de recherche suivante", + "moveSelectionToPreviousFindMatch": "Déplacer la dernière sélection à la correspondance de recherche précédente", + "selectAllOccurrencesOfFindMatch": "Sélectionner toutes les occurrences des correspondances de la recherche", + "changeAll.label": "Modifier toutes les occurrences", + "showNextFindTermAction": "Afficher le terme de recherche suivant", + "showPreviousFindTermAction": "Afficher le terme de recherche précédent" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/fra/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..6e0f5d6ec1 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "Déplier", + "unFoldRecursivelyAction.label": "Déplier de manière récursive", + "foldAction.label": "Plier", + "foldRecursivelyAction.label": "Plier de manière récursive", + "foldAllAction.label": "Plier tout", + "unfoldAllAction.label": "Déplier tout", + "foldLevelAction.label": "Niveau de pliage {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/fra/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..a39ddbe556 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "1 modification de format effectuée à la ligne {0}", + "hintn1": "{0} modifications de format effectuées à la ligne {1}", + "hint1n": "1 modification de format effectuée entre les lignes {0} et {1}", + "hintnn": "{0} modifications de format effectuées entre les lignes {1} et {2}", + "formatDocument.label": "Mettre en forme le document", + "formatSelection.label": "Mettre en forme la sélection" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json b/i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json new file mode 100644 index 0000000000..ff80158719 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "Définition introuvable pour '{0}'", + "generic.noResults": "Définition introuvable", + "meta.title": " – {0} définitions", + "actions.goToDecl.label": "Atteindre la définition", + "actions.goToDeclToSide.label": "Ouvrir la définition sur le côté", + "actions.previewDecl.label": "Apercu de définition", + "goToImplementation.noResultWord": "Implémentation introuvable pour '{0}'", + "goToImplementation.generic.noResults": "Implémentation introuvable", + "meta.implementations.title": "– Implémentations {0}", + "actions.goToImplementation.label": "Accéder à l'implémentation", + "actions.peekImplementation.label": "Aperçu de l'implémentation", + "goToTypeDefinition.noResultWord": "Définition de type introuvable pour '{0}'", + "goToTypeDefinition.generic.noResults": "Définition de type introuvable", + "meta.typeDefinitions.title": " – Définitions de type {0}", + "actions.goToTypeDefinition.label": "Atteindre la définition de type", + "actions.peekTypeDefinition.label": "Aperçu de la définition du type", + "multipleResults": "Cliquez pour afficher {0} définitions." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..71895a487b --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "Définition introuvable pour '{0}'", + "generic.noResults": "Définition introuvable", + "meta.title": " – {0} définitions", + "actions.goToDecl.label": "Atteindre la définition", + "actions.goToDeclToSide.label": "Ouvrir la définition sur le côté", + "actions.previewDecl.label": "Apercu de définition", + "goToImplementation.noResultWord": "Implémentation introuvable pour '{0}'", + "goToImplementation.generic.noResults": "Implémentation introuvable", + "meta.implementations.title": "– Implémentations {0}", + "actions.goToImplementation.label": "Accéder à l'implémentation", + "actions.peekImplementation.label": "Aperçu de l'implémentation", + "goToTypeDefinition.noResultWord": "Définition de type introuvable pour '{0}'", + "goToTypeDefinition.generic.noResults": "Définition de type introuvable", + "meta.typeDefinitions.title": " – Définitions de type {0}", + "actions.goToTypeDefinition.label": "Atteindre la définition de type", + "actions.peekTypeDefinition.label": "Aperçu de la définition du type" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..231c9ee79b --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "Cliquez pour afficher {0} définitions." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/fra/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..a8b18664ff --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "Accéder à l'erreur ou l'avertissement suivant", + "markerAction.previous.label": "Accéder à l'erreur ou l'avertissement précédent", + "editorMarkerNavigationError": "Couleur d'erreur du widget de navigation dans les marqueurs de l'éditeur.", + "editorMarkerNavigationWarning": "Couleur d'avertissement du widget de navigation dans les marqueurs de l'éditeur.", + "editorMarkerNavigationBackground": "Arrière-plan du widget de navigation dans les marqueurs de l'éditeur." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/fra/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..6b0aecd440 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "Afficher par pointage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/fra/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..47828c86af --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "Chargement..." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/fra/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..a968e17216 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "Remplacer par la valeur précédente", + "InPlaceReplaceAction.next.label": "Remplacer par la valeur suivante" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/fra/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..900db1cd2d --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "Convertir les retraits en espaces", + "indentationToTabs": "Convertir les retraits en tabulations", + "configuredTabSize": "Taille des tabulations configurée", + "selectTabWidth": "Sélectionner la taille des tabulations pour le fichier actuel", + "indentUsingTabs": "Mettre en retrait avec des tabulations", + "indentUsingSpaces": "Mettre en retrait avec des espaces", + "detectIndentation": "Détecter la mise en retrait à partir du contenu", + "editor.reindentlines": "Remettre en retrait les lignes" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json b/i18n/fra/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f9ee0497f5 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Développeur : inspecter les portées TextMate", + "inspectTMScopesWidget.loading": "Chargement..." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/fra/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..de8642e71b --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "Copier la ligne en haut", + "lines.copyDown": "Copier la ligne en bas", + "lines.moveUp": "Déplacer la ligne vers le haut", + "lines.moveDown": "Déplacer la ligne vers le bas", + "lines.sortAscending": "Trier les lignes dans l'ordre croissant", + "lines.sortDescending": "Trier les lignes dans l'ordre décroissant", + "lines.trimTrailingWhitespace": "Découper l'espace blanc de fin", + "lines.delete": "Supprimer la ligne", + "lines.indent": "Mettre en retrait la ligne", + "lines.outdent": "Ajouter un retrait négatif à la ligne", + "lines.insertBefore": "Insérer une ligne au-dessus", + "lines.insertAfter": "Insérer une ligne sous", + "lines.deleteAllLeft": "Supprimer tout ce qui est à gauche", + "lines.deleteAllRight": "Supprimer tout ce qui est à droite", + "lines.joinLines": "Joindre les lignes", + "editor.transpose": "Transposer les caractères autour du curseur", + "editor.transformToUppercase": "Transformer en majuscule", + "editor.transformToLowercase": "Transformer en minuscule" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/fra/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..fe61570f81 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "Commande + clic pour suivre le lien", + "links.navigate": "Ctrl + clic pour suivre le lien", + "links.command.mac": "Cmd + clic pour exécuter la commande", + "links.command": "Ctrl + clic pour exécuter la commande", + "links.navigate.al": "Alt + clic pour suivre le lien", + "links.command.al": "Alt + clic pour exécuter la commande", + "invalid.url": "Échec de l'ouverture de ce lien, car il n'est pas bien formé : {0}", + "missing.url": "Échec de l'ouverture de ce lien, car sa cible est manquante.", + "label": "Ouvrir le lien" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/fra/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..84a57ab506 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "Ajouter un curseur au-dessus", + "mutlicursor.insertBelow": "Ajouter un curseur en dessous", + "mutlicursor.insertAtEndOfEachLineSelected": "Ajouter des curseurs à la fin des lignes" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/fra/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..0048395789 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "Indicateurs des paramètres Trigger" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/fra/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..bfc059fde9 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0}, conseil" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/fra/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..a573a363c0 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "Afficher les correctifs ({0})", + "quickFix": "Afficher les correctifs", + "quickfix.trigger.label": "Correctif rapide" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..78b33bb64f --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": " – {0} références", + "references.action.label": "Rechercher toutes les références" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..dba3f63159 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "Chargement..." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..4bf169a94b --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "symbole dans {0} sur la ligne {1}, colonne {2}", + "aria.fileReferences.1": "1 symbole dans {0}, chemin complet {1}", + "aria.fileReferences.N": "{0} symboles dans {1}, chemin complet {2}", + "aria.result.0": "Résultats introuvables", + "aria.result.1": "1 symbole dans {0}", + "aria.result.n1": "{0} symboles dans {1}", + "aria.result.nm": "{0} symboles dans {1} fichiers" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..fb68d4d65b --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "Échec de la résolution du fichier.", + "referencesCount": "{0} références", + "referenceCount": "{0} référence", + "missingPreviewMessage": "aperçu non disponible", + "treeAriaLabel": "Références", + "noResults": "Aucun résultat", + "peekView.alternateTitle": "Références", + "peekViewTitleBackground": "Couleur d'arrière-plan de la zone de titre de l'affichage d'aperçu.", + "peekViewTitleForeground": "Couleur du titre de l'affichage d'aperçu.", + "peekViewTitleInfoForeground": "Couleur des informations sur le titre de l'affichage d'aperçu.", + "peekViewBorder": "Couleur des bordures et de la flèche de l'affichage d'aperçu.", + "peekViewResultsBackground": "Couleur d'arrière-plan de la liste des résultats de l'affichage d'aperçu.", + "peekViewResultsMatchForeground": "Couleur de premier plan des noeuds de lignes dans la liste des résultats de l'affichage d'aperçu.", + "peekViewResultsFileForeground": "Couleur de premier plan des noeuds de fichiers dans la liste des résultats de l'affichage d'aperçu.", + "peekViewResultsSelectionBackground": "Couleur d'arrière-plan de l'entrée sélectionnée dans la liste des résultats de l'affichage d'aperçu.", + "peekViewResultsSelectionForeground": "Couleur de premier plan de l'entrée sélectionnée dans la liste des résultats de l'affichage d'aperçu.", + "peekViewEditorBackground": "Couleur d'arrière-plan de l'éditeur d'affichage d'aperçu.", + "peekViewEditorGutterBackground": "Couleur d'arrière-plan de la bordure de l'éditeur d'affichage d'aperçu.", + "peekViewResultsMatchHighlight": "Couleur de mise en surbrillance d'une correspondance dans la liste des résultats de l'affichage d'aperçu.", + "peekViewEditorMatchHighlight": "Couleur de mise en surbrillance d'une correspondance dans l'éditeur de l'affichage d'aperçu." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/fra/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..f0164f41d0 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "Aucun résultat.", + "aria": "'{0}' renommé en '{1}'. Récapitulatif : {2}", + "rename.failed": "Échec de l'exécution du renommage.", + "rename.label": "Renommer le symbole" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/fra/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..a98cc4136c --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "Renommez l'entrée. Tapez le nouveau nom et appuyez sur Entrée pour valider." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/fra/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..a7b4624c75 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "Développer la sélection", + "smartSelect.shrink": "Réduire la sélection" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/fra/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..9d78b5f0c5 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "L'acceptation de '{0}' a inséré le texte suivant : {1}", + "suggest.trigger.label": "Suggestions pour Trigger" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/fra/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..870e40b4ab --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "Couleur d'arrière-plan du widget de suggestion.", + "editorSuggestWidgetBorder": "Couleur de bordure du widget de suggestion.", + "editorSuggestWidgetForeground": "Couleur de premier plan du widget de suggestion.", + "editorSuggestWidgetSelectedBackground": "Couleur d'arrière-plan de l'entrée sélectionnée dans le widget de suggestion.", + "editorSuggestWidgetHighlightForeground": "Couleur de la surbrillance des correspondances dans le widget de suggestion.", + "readMore": "En savoir plus...{0}", + "suggestionWithDetailsAriaLabel": "{0}, suggestion, avec détails", + "suggestionAriaLabel": "{0}, suggestion", + "readLess": "En savoir moins...{0}", + "suggestWidget.loading": "Chargement...", + "suggestWidget.noSuggestions": "Pas de suggestions.", + "suggestionAriaAccepted": "{0}, accepté", + "ariaCurrentSuggestionWithDetails": "{0}, suggestion, avec détails", + "ariaCurrentSuggestion": "{0}, suggestion" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/fra/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..421763a8b5 --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "Activer/désactiver l'utilisation de la touche Tab pour déplacer le focus" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/fra/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..6904f3f56c --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "Couleur d'arrière-plan d'un symbole durant l'accès en lecture, par exemple la lecture d'une variable.", + "wordHighlightStrong": "Couleur d'arrière-plan d'un symbole durant l'accès en écriture, par exemple l'écriture dans une variable.", + "overviewRulerWordHighlightForeground": "Couleur du marqueur de la règle d'aperçu pour la mise en évidence de symbole.", + "overviewRulerWordHighlightStrongForeground": "Couleur du marqueur de la règle d'aperçu la mise en évidence de symbole d’accès en écriture." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/fra/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..e5c16c10da --- /dev/null +++ b/i18n/fra/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Fermer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/fra/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..0ef45c1787 --- /dev/null +++ b/i18n/fra/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Langage inconnu dans 'contributes.{0}.language'. Valeur fournie : {1}", + "invalid.scopeName": "Chaîne attendue dans 'contributes.{0}.scopeName'. Valeur fournie : {1}", + "invalid.path.0": "Chaîne attendue dans 'contributes.{0}.path'. Valeur fournie : {1}", + "invalid.injectTo": "Valeur non valide dans 'contributes.{0}.injectTo'. Il doit s'agir d'un tableau de noms de portées de langage. Valeur fournie : {1}", + "invalid.embeddedLanguages": "Valeur non valide dans 'contributes.{0}.embeddedLanguages'. Il doit s'agir d'un mappage d'objets entre le nom de portée et le langage. Valeur fournie : {1}", + "invalid.path.1": "'contributes.{0}.path' ({1}) est censé être inclus dans le dossier ({2}) de l'extension. Cela risque de rendre l'extension non portable.", + "no-tm-grammar": "Aucune grammaire TM n'est inscrite pour ce langage." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/fra/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..061221580d --- /dev/null +++ b/i18n/fra/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Erreurs durant l'analyse de {0} : {1}", + "schema.openBracket": "Séquence de chaînes ou de caractères de crochets ouvrants.", + "schema.closeBracket": "Séquence de chaînes ou de caractères de crochets fermants.", + "schema.comments": "Définit les symboles de commentaire", + "schema.blockComments": "Définit le marquage des commentaires de bloc.", + "schema.blockComment.begin": "Séquence de caractères au début d'un commentaire de bloc.", + "schema.blockComment.end": "Séquence de caractères à la fin d'un commentaire de bloc.", + "schema.lineComment": "Séquence de caractères au début d'un commentaire de ligne.", + "schema.brackets": "Définit les symboles de type crochet qui augmentent ou diminuent le retrait.", + "schema.autoClosingPairs": "Définit les paires de crochets. Quand vous entrez un crochet ouvrant, le crochet fermant est inséré automatiquement.", + "schema.autoClosingPairs.notIn": "Définit une liste d'étendues où les paires automatiques sont désactivées.", + "schema.surroundingPairs": "Définit les paires de crochets qui peuvent être utilisées pour entourer la chaîne sélectionnée.", + "schema.wordPattern": "La définition du mot dans le langage", + "schema.wordPattern.pattern": "L'expression régulière utilisée pour la recherche", + "schema.wordPattern.flags": "Les options d'expression régulière utilisées pour la recherche", + "schema.wordPattern.flags.errorMessage": "Doit valider l'expression régulière `/^([gimuy]+)$/`." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/fra/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..a26733d7c2 --- /dev/null +++ b/i18n/fra/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Ajoute des générateurs de jetons TextMate.", + "vscode.extension.contributes.grammars.language": "Identificateur de langue pour lequel cette syntaxe est ajoutée.", + "vscode.extension.contributes.grammars.scopeName": "Nom de portée TextMate utilisé par le fichier tmLanguage.", + "vscode.extension.contributes.grammars.path": "Chemin du fichier tmLanguage. Le chemin est relatif au dossier d'extensions et commence généralement par './syntaxes/'.", + "vscode.extension.contributes.grammars.embeddedLanguages": "Mappage du nom de portée à l'ID de langage si cette grammaire contient des langages incorporés.", + "vscode.extension.contributes.grammars.injectTo": "Liste de noms des portées de langage auxquelles cette grammaire est injectée." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/fra/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/fra/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/fra/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..bbd4696a02 --- /dev/null +++ b/i18n/fra/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "les éléments de menu doivent figurer dans un tableau", + "requirestring": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "optstring": "La propriété '{0}' peut être omise ou doit être de type 'string'", + "vscode.extension.contributes.menuItem.command": "Identificateur de la commande à exécuter. La commande doit être déclarée dans la section 'commands'", + "vscode.extension.contributes.menuItem.alt": "Identificateur d'une commande alternative à exécuter. La commande doit être déclarée dans la section 'commands'", + "vscode.extension.contributes.menuItem.when": "Condition qui doit être vraie pour afficher cet élément", + "vscode.extension.contributes.menuItem.group": "Groupe auquel cette commande appartient", + "vscode.extension.contributes.menus": "Contribue à fournir des éléments de menu à l'éditeur", + "menus.commandPalette": "Palette de commandes", + "menus.editorTitle": "Menu de titre de l'éditeur", + "menus.editorContext": "Menu contextuel de l'éditeur", + "menus.explorerContext": "Menu contextuel de l'Explorateur de fichiers", + "menus.editorTabContext": "Menu contextuel des onglets de l'éditeur", + "menus.debugCallstackContext": "Menu contextuel de la pile d'appels de débogage", + "menus.scmTitle": "Menu du titre du contrôle de code source", + "menus.resourceGroupContext": "Menu contextuel du groupe de ressources du contrôle de code source", + "menus.resourceStateContext": "Menu contextuel de l'état des ressources du contrôle de code source", + "view.viewTitle": "Menu de titre de la vue ajoutée", + "view.itemContext": "Menu contextuel de l'élément de vue ajoutée", + "nonempty": "valeur non vide attendue.", + "opticon": "la propriété 'icon' peut être omise, ou bien elle doit être une chaîne ou un littéral tel que '{dark, light}'", + "requireStringOrObject": "la propriété `{0}` est obligatoire et doit être de type `string` ou `object`", + "requirestrings": "les propriétés `{0}` et `{1}` sont obligatoires et doivent être de type `string`", + "vscode.extension.contributes.commandType.command": "Identificateur de la commande à exécuter", + "vscode.extension.contributes.commandType.title": "Titre en fonction duquel la commande est représentée dans l'IU", + "vscode.extension.contributes.commandType.category": "(Facultatif) chaîne de catégorie en fonction de laquelle la commande est regroupée dans l'IU", + "vscode.extension.contributes.commandType.icon": "(Facultatif) Icône utilisée pour représenter la commande dans l'IU. Il s'agit d'un chemin de fichier ou d'une configuration dont le thème peut être changé", + "vscode.extension.contributes.commandType.icon.light": "Chemin d'icône quand un thème clair est utilisé", + "vscode.extension.contributes.commandType.icon.dark": "Chemin d'icône quand un thème sombre est utilisé", + "vscode.extension.contributes.commands": "Ajoute des commandes à la palette de commandes.", + "dup": "La commande '{0}' apparaît plusieurs fois dans la section 'commands'.", + "menuId.invalid": "'{0}' est un identificateur de menu non valide", + "missing.command": "L'élément de menu fait référence à une commande '{0}' qui n'est pas définie dans la section 'commands'.", + "missing.altCommand": "L'élément de menu fait référence à une commande alt '{0}' qui n'est pas définie dans la section 'commands'.", + "dupe.command": "L'élément de menu fait référence à la même commande que la commande par défaut et la commande alt", + "nosupport.altCommand": "Actuellement, seul le groupe 'navigation' du menu 'editor/title' prend en charge les commandes alt" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/fra/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..7f4b0f3f3d --- /dev/null +++ b/i18n/fra/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "Substitutions de configuration par défaut", + "overrideSettings.description": "Configurez les paramètres d'éditeur à remplacer pour le langage {0}.", + "overrideSettings.defaultDescription": "Configurez les paramètres d'éditeur à remplacer pour un langage.", + "config.property.languageDefault": "Impossible d'inscrire '{0}'. Ceci correspond au modèle de propriété '\\\\[.*\\\\]$' permettant de décrire les paramètres d'éditeur spécifiques à un langage. Utilisez la contribution 'configurationDefaults'.", + "config.property.duplicate": "Impossible d'inscrire '{0}'. Cette propriété est déjà inscrite." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/environment/node/argv.i18n.json b/i18n/fra/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..a7da7ca027 --- /dev/null +++ b/i18n/fra/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "Les arguments en mode '--goto' doivent être au format 'FILE(:LINE(:CHARACTER))'.", + "diff": "Comparez deux fichiers entre eux.", + "add": "Ajoutez un ou plusieurs dossiers à la dernière fenêtre active.", + "goto": "Ouvrez un fichier dans le chemin, à la ligne et la position de caractère spécifiées.", + "locale": "Paramètres régionaux à utiliser (exemple : fr-FR ou en-US).", + "newWindow": "Forcez l'utilisation d'une nouvelle instance de Code.", + "performance": "Démarrez avec la commande 'Développeur : performance de démarrage' activée.", + "prof-startup": "Exécuter le profileur d'UC au démarrage", + "reuseWindow": "Forcez l'ouverture d'un fichier ou dossier dans la dernière fenêtre active.", + "userDataDir": "Spécifie le répertoire où sont conservées les données des utilisateurs. S'avère utile pour une exécution en tant que root.", + "verbose": "Affichez la sortie détaillée (implique --wait).", + "wait": "Attendez la fermeture de la fenêtre avant un retour.", + "extensionHomePath": "Définissez le chemin racine des extensions.", + "listExtensions": "Listez les extensions installées.", + "showVersions": "Affichez les versions des extensions installées, quand --list-extension est utilisé.", + "installExtension": "Installe une extension.", + "uninstallExtension": "Désinstalle une extension.", + "experimentalApis": "Active les fonctionnalités d'API proposées pour une extension.", + "disableExtensions": "Désactivez toutes les extensions installées.", + "disableGPU": "Désactivez l'accélération matérielle du GPU.", + "version": "Affichez la version.", + "help": "Affichez le mode d'utilisation.", + "usage": "Utilisation", + "options": "options", + "paths": "chemins", + "optionsUpperCase": "Options" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/fra/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..70c59613b0 --- /dev/null +++ b/i18n/fra/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Aucun espace de travail." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/fra/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..649fe4b238 --- /dev/null +++ b/i18n/fra/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Extensions", + "preferences": "Préférences" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/fra/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..527f2f5ef2 --- /dev/null +++ b/i18n/fra/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Extension introuvable", + "noCompatible": "Version compatible de {0} introuvable avec cette version de Code." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/fra/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..36e5b3cf32 --- /dev/null +++ b/i18n/fra/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "Extension non valide : package.json n'est pas un fichier JSON.", + "restartCode": "Redémarrez Code avant de réinstaller {0}.", + "installDependeciesConfirmation": "L'installation de '{0}' entraîne également l'installation de ses dépendances. Voulez-vous continuer ?", + "install": "Oui", + "doNotInstall": "Non", + "uninstallDependeciesConfirmation": "Voulez-vous désinstaller uniquement '{0}' ou également ses dépendances ?", + "uninstallOnly": "Uniquement", + "uninstallAll": "Tout", + "cancel": "Annuler", + "uninstallConfirmation": "Voulez-vous vraiment désinstaller '{0}' ?", + "ok": "OK", + "singleDependentError": "Impossible de désinstaller l'extension '{0}'. L'extension '{1}' en dépend.", + "twoDependentsError": "Impossible de désinstaller l'extension '{0}'. Les extensions '{1}' et '{2}' en dépendent.", + "multipleDependentsError": "Impossible de désinstaller l'extension '{0}'. Les extensions '{1}', '{2}' et d'autres extensions en dépendent.", + "notExists": "Extension introuvable" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/fra/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..80f8bacc07 --- /dev/null +++ b/i18n/fra/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "Échec de l'activation de l'extension '{1}'. Raison : dépendance '{0}' inconnue.", + "failedDep1": "Échec de l'activation de l'extension '{1}'. Raison : échec de l'activation de la dépendance '{0}'.", + "failedDep2": "Échec de l'activation de l'extension '{0}'. Raison : plus de 10 niveaux de dépendances (probablement une boucle de dépendance).", + "activationError": "Échec de l'activation de l'extension '{0}' : {1}." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/fra/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..9b4fd26041 --- /dev/null +++ b/i18n/fra/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "Pour les extensions VS Code, spécifie la version de VS Code avec laquelle l'extension est compatible. Ne peut pas être *. Exemple : ^0.10.5 indique une compatibilité avec la version minimale 0.10.5 de VS Code.", + "vscode.extension.publisher": "Éditeur de l'extension VS Code.", + "vscode.extension.displayName": "Nom d'affichage de l'extension utilisée dans la galerie VS Code.", + "vscode.extension.categories": "Catégories utilisées par la galerie VS Code pour catégoriser l'extension.", + "vscode.extension.galleryBanner": "Bannière utilisée dans le marketplace VS Code.", + "vscode.extension.galleryBanner.color": "Couleur de la bannière de l'en-tête de page du marketplace VS Code.", + "vscode.extension.galleryBanner.theme": "Thème de couleur de la police utilisée dans la bannière.", + "vscode.extension.contributes": "Toutes les contributions de l'extension VS Code représentées par ce package.", + "vscode.extension.preview": "Définit l'extension à marquer en tant que préversion dans Marketplace.", + "vscode.extension.activationEvents": "Événements d'activation pour l'extension VS Code.", + "vscode.extension.activationEvents.onLanguage": "Événement d'activation envoyé quand un fichier résolu dans le langage spécifié est ouvert.", + "vscode.extension.activationEvents.onCommand": "Événement d'activation envoyé quand la commande spécifiée est appelée.", + "vscode.extension.activationEvents.onDebug": "Événement d'activation envoyé quand une session de débogage du type spécifié est démarrée.", + "vscode.extension.activationEvents.workspaceContains": "Événement d'activation envoyé quand un dossier ouvert contient au moins un fichier correspondant au modèle glob spécifié.", + "vscode.extension.activationEvents.onView": "Événement d'activation envoyé quand la vue spécifiée est développée.", + "vscode.extension.activationEvents.star": "Événement d'activation envoyé au démarrage de VS Code. Pour garantir la qualité de l'expérience utilisateur, utilisez cet événement d'activation dans votre extension uniquement quand aucune autre combinaison d'événements d'activation ne fonctionne dans votre cas d'utilisation.", + "vscode.extension.badges": "Ensemble de badges à afficher dans la barre latérale de la page d'extensions de Marketplace.", + "vscode.extension.badges.url": "URL de l'image du badge.", + "vscode.extension.badges.href": "Lien du badge.", + "vscode.extension.badges.description": "Description du badge.", + "vscode.extension.extensionDependencies": "Dépendances envers d'autres extensions. L'identificateur d'une extension est toujours ${publisher}.${name}. Exemple : vscode.csharp.", + "vscode.extension.scripts.prepublish": "Le script exécuté avant le package est publié en tant qu'extension VS Code.", + "vscode.extension.icon": "Chemin d'une icône de 128 x 128 pixels." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/fra/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..e54a7116a4 --- /dev/null +++ b/i18n/fra/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "Impossible d'analyser la valeur {0} de 'engines.vscode'. Utilisez, par exemple, ^0.10.0, ^1.2.3, ^0.11.0, ^0.10.x, etc.", + "versionSpecificity1": "La version spécifiée dans 'engines.vscode' ({0}) n'est pas assez précise. Pour les versions de vscode antérieures à 1.0.0, définissez au minimum les versions majeure et mineure souhaitées. Par exemple : ^0.10.0, 0.10.x, 0.11.0, etc.", + "versionSpecificity2": "La version spécifiée dans 'engines.vscode' ({0}) n'est pas assez précise. Pour les versions de vscode ultérieures à 1.0.0, définissez au minimum la version majeure souhaitée. Par exemple : ^1.10.0, 1.10.x, 1.x.x, 2.x.x, etc.", + "versionMismatch": "L'extension n'est pas compatible avec le code {0}. L'extension nécessite {1}.", + "extensionDescription.empty": "Description d'extension vide obtenue", + "extensionDescription.publisher": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "extensionDescription.name": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "extensionDescription.version": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "extensionDescription.engines": "la propriété '{0}' est obligatoire et doit être de type 'object'", + "extensionDescription.engines.vscode": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "extensionDescription.extensionDependencies": "la propriété '{0}' peut être omise ou doit être de type 'string[]'", + "extensionDescription.activationEvents1": "la propriété '{0}' peut être omise ou doit être de type 'string[]'", + "extensionDescription.activationEvents2": "les propriétés '{0}' et '{1}' doivent être toutes les deux spécifiées ou toutes les deux omises", + "extensionDescription.main1": "La propriété '{0}' peut être omise ou doit être de type 'string'", + "extensionDescription.main2": "'main' ({0}) est censé être inclus dans le dossier ({1}) de l'extension. Cela risque de rendre l'extension non portable.", + "extensionDescription.main3": "les propriétés '{0}' et '{1}' doivent être toutes les deux spécifiées ou toutes les deux omises", + "notSemver": "La version de l'extension n'est pas compatible avec SemVer." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/fra/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..79f8a141b8 --- /dev/null +++ b/i18n/fra/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "Nouvelle fenêtre", + "newWindowDesc": "Ouvre une nouvelle fenêtre", + "recentFolders": "Espaces de travail récents", + "folderDesc": "{0} {1}", + "codeWorkspace": "Espace de travail de code" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/fra/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..f23cff99fd --- /dev/null +++ b/i18n/fra/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "OK", + "integrity.dontShowAgain": "Ne plus afficher", + "integrity.moreInfo": "Informations supplémentaires", + "integrity.prompt": "Votre installation de {0} semble être endommagée. Effectuez une réinstallation." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/fra/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..4dc5949427 --- /dev/null +++ b/i18n/fra/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "Ajoute une configuration de schéma json.", + "contributes.jsonValidation.fileMatch": "Modèle de fichier correspondant recherché, par exemple \"package.json\" ou \"*.launch\".", + "contributes.jsonValidation.url": "URL de schéma ('http:', 'https:') ou chemin relatif du dossier d'extensions ('./').", + "invalid.jsonValidation": "'configuration.jsonValidation' doit être un tableau", + "invalid.fileMatch": "'configuration.jsonValidation.fileMatch' doit être défini", + "invalid.url": "'configuration.jsonValidation.url' doit être une URL ou un chemin relatif", + "invalid.url.fileschema": "'configuration.jsonValidation.url' est une URL relative non valide : {0}", + "invalid.url.schema": "'configuration.jsonValidation.url' doit commencer par 'http:', 'https:' ou './' pour référencer les schémas situés dans l'extension" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/fra/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..b20be981d5 --- /dev/null +++ b/i18n/fra/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "Touche ({0}) utilisée. En attente de la seconde touche pour la pression simultanée...", + "missing.chord": "La combinaison de touches ({0}, {1}) n'est pas une commande." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/keybinding/common/keybindingLabels.i18n.json b/i18n/fra/src/vs/platform/keybinding/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..071cf15fd7 --- /dev/null +++ b/i18n/fra/src/vs/platform/keybinding/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Maj", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Contrôle", + "shiftKey.long": "Maj", + "altKey.long": "Alt", + "cmdKey.long": "Commande", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/fra/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..1a01ac9608 --- /dev/null +++ b/i18n/fra/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "La propriété loop est uniquement prise en charge dans le détecteur de problèmes de correspondance de dernière ligne.", + "ProblemPatternParser.problemPattern.missingRegExp": "Il manque une expression régulière dans le modèle de problème.", + "ProblemPatternParser.problemPattern.missingProperty": "Le modèle de problème est non valide. Il doit contenir au moins un groupe de correspondance pour un fichier, un message et une ligne ou un emplacement.", + "ProblemPatternParser.invalidRegexp": "Erreur : la chaîne {0} est une expression régulière non valide.\n", + "ProblemPatternSchema.regexp": "Expression régulière permettant de trouver une erreur, un avertissement ou une information dans la sortie.", + "ProblemPatternSchema.file": "Index de groupe de correspondance du nom de fichier. En cas d'omission, 1 est utilisé.", + "ProblemPatternSchema.location": "Index de groupe de correspondance de l'emplacement du problème. Les modèles d'emplacement valides sont : (line), (line,column) et (startLine,startColumn,endLine,endColumn). En cas d'omission, (line,column) est choisi par défaut.", + "ProblemPatternSchema.line": "Index de groupe de correspondance de la ligne du problème. La valeur par défaut est 2", + "ProblemPatternSchema.column": "Index de groupe de correspondance du caractère de ligne du problème. La valeur par défaut est 3", + "ProblemPatternSchema.endLine": "Index de groupe de correspondance de la ligne de fin du problème. La valeur par défaut est non définie", + "ProblemPatternSchema.endColumn": "Index de groupe de correspondance du caractère de ligne de fin du problème. La valeur par défaut est non définie", + "ProblemPatternSchema.severity": "Index de groupe de correspondance de la gravité du problème. La valeur par défaut est non définie", + "ProblemPatternSchema.code": "Index de groupe de correspondance du code du problème. La valeur par défaut est non définie", + "ProblemPatternSchema.message": "Index de groupe de correspondance du message. En cas d'omission, la valeur par défaut est 4 si l'emplacement est spécifié. Sinon, la valeur par défaut est 5.", + "ProblemPatternSchema.loop": "Dans une boucle de détecteur de problèmes de correspondance multiligne, indique si le modèle est exécuté en boucle tant qu'il correspond. Peut uniquement être spécifié dans le dernier modèle d'un modèle multiligne.", + "NamedProblemPatternSchema.name": "Nom du modèle de problème.", + "NamedMultiLineProblemPatternSchema.name": "Nom du modèle de problème multiligne.", + "NamedMultiLineProblemPatternSchema.patterns": "Modèles réels.", + "ProblemPatternExtPoint": "Contribue aux modèles de problèmes", + "ProblemPatternRegistry.error": "Modèle de problème non valide. Le modèle va être ignoré.", + "ProblemMatcherParser.noProblemMatcher": "Erreur : impossible de convertir la description en détecteur de problèmes de correspondance :\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "Erreur : la description ne définit pas un modèle de problème valide :\n{0}\n", + "ProblemMatcherParser.noOwner": "Erreur : la description ne définit pas un propriétaire :\n{0}\n", + "ProblemMatcherParser.noFileLocation": "Erreur : la description ne définit pas un emplacement de fichier :\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "Information : gravité inconnue {0}. Les valeurs valides sont erreur, avertissement et information.\n", + "ProblemMatcherParser.noDefinedPatter": "Erreur : le modèle ayant pour identificateur {0} n'existe pas.", + "ProblemMatcherParser.noIdentifier": "Erreur : la propriété du modèle référence un identificateur vide.", + "ProblemMatcherParser.noValidIdentifier": "Erreur : la propriété de modèle {0} n'est pas un nom de variable de modèle valide.", + "ProblemMatcherParser.problemPattern.watchingMatcher": "Un détecteur de problèmes de correspondance doit définir un modèle de début et un modèle de fin à observer.", + "ProblemMatcherParser.invalidRegexp": "Erreur : la chaîne {0} est une expression régulière non valide.\n", + "WatchingPatternSchema.regexp": "Expression régulière permettant de détecter le début ou la fin d'une tâche en arrière-plan.", + "WatchingPatternSchema.file": "Index de groupe de correspondance du nom de fichier. Peut être omis.", + "PatternTypeSchema.name": "Nom d'un modèle faisant l'objet d'une contribution ou prédéfini", + "PatternTypeSchema.description": "Modèle de problème ou bien nom d'un modèle de problème faisant l'objet d'une contribution ou prédéfini. Peut être omis si base est spécifié.", + "ProblemMatcherSchema.base": "Nom d'un détecteur de problèmes de correspondance de base à utiliser.", + "ProblemMatcherSchema.owner": "Propriétaire du problème dans Code. Peut être omis si base est spécifié. Prend la valeur 'external' par défaut en cas d'omission et si base n'est pas spécifié.", + "ProblemMatcherSchema.severity": "Gravité par défaut des problèmes de capture. Est utilisé si le modèle ne définit aucun groupe de correspondance pour la gravité.", + "ProblemMatcherSchema.applyTo": "Contrôle si un problème signalé pour un document texte s'applique uniquement aux documents ouverts ou fermés, ou bien à l'ensemble des documents.", + "ProblemMatcherSchema.fileLocation": "Définit la façon dont les noms de fichiers signalés dans un modèle de problème doivent être interprétés.", + "ProblemMatcherSchema.background": "Modèles de suivi du début et de la fin d'un détecteur de problèmes de correspondance actif sur une tâche en arrière-plan.", + "ProblemMatcherSchema.background.activeOnStart": "Si la valeur est true, le moniteur d'arrière-plan est actif au démarrage de la tâche. Cela revient à envoyer une ligne qui correspond à beginPattern", + "ProblemMatcherSchema.background.beginsPattern": "En cas de correspondance dans la sortie, le début d'une tâche en arrière-plan est signalé.", + "ProblemMatcherSchema.background.endsPattern": "En cas de correspondance dans la sortie, la fin d'une tâche en arrière-plan est signalée.", + "ProblemMatcherSchema.watching.deprecated": "La propriété espion est déconseillée. Utilisez l'arrière-plan à la place.", + "ProblemMatcherSchema.watching": "Modèles de suivi du début et de la fin d'un détecteur de problèmes de correspondance espion.", + "ProblemMatcherSchema.watching.activeOnStart": "Si la valeur est true, le mode espion est actif au démarrage de la tâche. Cela revient à émettre une ligne qui correspond à beginPattern", + "ProblemMatcherSchema.watching.beginsPattern": "En cas de correspondance dans la sortie, le début d'une tâche de suivi est signalé.", + "ProblemMatcherSchema.watching.endsPattern": "En cas de correspondance dans la sortie, la fin d'une tâche de suivi est signalée.", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "Cette propriété est déconseillée. Utilisez la propriété espion à la place.", + "LegacyProblemMatcherSchema.watchedBegin": "Expression régulière signalant qu'une tâche faisant l'objet d'un suivi commence à s'exécuter via le suivi d'un fichier.", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "Cette propriété est déconseillée. Utilisez la propriété espion à la place.", + "LegacyProblemMatcherSchema.watchedEnd": "Expression régulière signalant qu'une tâche faisant l'objet d'un suivi a fini de s'exécuter.", + "NamedProblemMatcherSchema.name": "Nom du détecteur de problèmes de correspondance utilisé comme référence.", + "NamedProblemMatcherSchema.label": "Étiquette contrôlable de visu du détecteur de problèmes de correspondance.", + "ProblemMatcherExtPoint": "Contribue aux détecteurs de problèmes de correspondance", + "msCompile": "Problèmes du compilateur Microsoft", + "lessCompile": "Moins de problèmes", + "gulp-tsc": "Problèmes liés à Gulp TSC", + "jshint": "Problèmes liés à JSHint", + "jshint-stylish": "Problèmes liés au formateur stylish de JSHint", + "eslint-compact": "Problèmes liés au formateur compact d'ESLint", + "eslint-stylish": "Problèmes liés au formateur stylish d'ESLint", + "go": "Problèmes liés à Go" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/message/common/message.i18n.json b/i18n/fra/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..e072e017ec --- /dev/null +++ b/i18n/fra/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Fermer", + "later": "Plus tard", + "cancel": "Annuler" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/request/node/request.i18n.json b/i18n/fra/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..d7ab821f15 --- /dev/null +++ b/i18n/fra/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "Paramètre de proxy à utiliser. S'il n'est pas défini, il est récupéré à partir des variables d'environnement http_proxy et https_proxy", + "strictSSL": "Spécifie si le certificat de serveur proxy doit être vérifié par rapport à la liste des autorités de certification fournies.", + "proxyAuthorization": "Valeur à envoyer en tant qu'en-tête 'Proxy-Authorization' pour chaque requête réseau." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/fra/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..92a76e61dd --- /dev/null +++ b/i18n/fra/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Télémétrie", + "telemetry.enableTelemetry": "Activez l'envoi des données d'utilisation et d'erreurs à Microsoft." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/fra/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..2889b1d6c1 --- /dev/null +++ b/i18n/fra/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "Contribue à des couleurs définies pour des extensions dont le thème peut être changé", + "contributes.color.id": "L’identifiant de la couleur dont le thème peut être changé", + "contributes.color.id.format": "Les identifiants doivent être sous la forme aa[.bb]*", + "contributes.color.description": "La description de la couleur dont le thème peut être changé", + "contributes.defaults.light": "La couleur par défaut pour les thèmes clairs. Soit une valeur de couleur en hexadécimal (#RRGGBB[AA]) ou l’identifiant d’une couleur dont le thème peut être changé qui fournit la valeur par défaut.", + "contributes.defaults.dark": "La couleur par défaut pour les thèmes sombres. Soit une valeur de couleur en hexadécimal (#RRGGBB[AA]) ou l’identifiant d’une couleur dont le thème peut être changé qui fournit la valeur par défaut.", + "contributes.defaults.highContrast": "La couleur par défaut pour les thèmes de contraste élevé. Soit une valeur de couleur en hexadécimal (#RRGGBB[AA]) ou l’identifiant d’une couleur dont le thème peut être changé qui fournit la valeur par défaut.", + "invalid.colorConfiguration": "'configuration.colors' doit être un tableau", + "invalid.default.colorType": "{0} doit être soit une valeur de couleur en hexadécimal (#RRGGBB[AA] ou #RGB[A]) ou l’identifiant d’une couleur dont le thème peut être changé qui fournit la valeur par défaut.", + "invalid.id": "'configuration.colors.id' doit être défini et ne peut pas être vide", + "invalid.id.format": "'configuration.colors.id' doit suivre le word[.word]*", + "invalid.description": "'configuration.colors.description' doit être défini et ne peut pas être vide", + "invalid.defaults": "'configuration.colors.defaults' doit être défini et doit contenir 'light', 'dark' et 'highContrast'" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/fra/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..eb8246dddc --- /dev/null +++ b/i18n/fra/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "Format de couleur non valide. Utilisez #RGB, #RGBA, #RRGGBB ou #RRGGBBAA", + "schema.colors": "Couleurs utilisées dans le banc d'essai.", + "foreground": "Couleur de premier plan globale. Cette couleur est utilisée si elle n'est pas remplacée par un composant.", + "errorForeground": "Couleur principale de premier plan pour les messages d'erreur. Cette couleur est utilisée uniquement si elle n'est pas redéfinie par un composant.", + "descriptionForeground": "Couleur de premier plan du texte descriptif fournissant des informations supplémentaires, par exemple pour un label.", + "focusBorder": "Couleur de bordure globale des éléments ayant le focus. Cette couleur est utilisée si elle n'est pas remplacée par un composant.", + "contrastBorder": "Bordure supplémentaire autour des éléments pour les séparer des autres et obtenir un meilleur contraste.", + "activeContrastBorder": "Bordure supplémentaire autour des éléments actifs pour les séparer des autres et obtenir un meilleur contraste.", + "selectionBackground": "La couleur d'arrière-plan des sélections de texte dans le banc d'essai (par ex., pour les champs d'entrée ou les zones de texte). Notez que cette couleur ne s'applique pas aux sélections dans l'éditeur et le terminal.", + "textSeparatorForeground": "Couleur pour les séparateurs de texte.", + "textLinkForeground": "Couleur des liens dans le texte.", + "textLinkActiveForeground": "Couleur des liens actifs dans le texte.", + "textPreformatForeground": "Couleur des segments de texte préformatés.", + "textBlockQuoteBackground": "Couleur d'arrière-plan des citations dans le texte.", + "textBlockQuoteBorder": "Couleur de bordure des citations dans le texte.", + "textCodeBlockBackground": "Couleur d'arrière-plan des blocs de code dans le texte.", + "widgetShadow": "Couleur de l'ombre des widgets, comme rechercher/remplacer, au sein de l'éditeur.", + "inputBoxBackground": "Arrière-plan de la zone d'entrée.", + "inputBoxForeground": "Premier plan de la zone d'entrée.", + "inputBoxBorder": "Bordure de la zone d'entrée.", + "inputBoxActiveOptionBorder": "Couleur de la bordure des options activées dans les champs d'entrée.", + "inputPlaceholderForeground": "Couleur de premier plan de la zone d'entrée pour le texte d'espace réservé.", + "inputValidationInfoBackground": "Couleur d'arrière-plan de la validation d'entrée pour la gravité des informations.", + "inputValidationInfoBorder": "Couleur de bordure de la validation d'entrée pour la gravité des informations.", + "inputValidationWarningBackground": "Couleur d'arrière-plan de la validation d'entrée pour l'avertissement sur les informations.", + "inputValidationWarningBorder": "Couleur de bordure de la validation d'entrée pour la gravité de l'avertissement.", + "inputValidationErrorBackground": "Couleur d'arrière-plan de la validation d'entrée pour la gravité de l'erreur.", + "inputValidationErrorBorder": "Couleur de bordure de la validation d'entrée pour la gravité de l'erreur. ", + "dropdownBackground": "Arrière-plan de la liste déroulante.", + "dropdownForeground": "Premier plan de la liste déroulante.", + "dropdownBorder": "Bordure de la liste déroulante.", + "listFocusBackground": "Couleur d'arrière-plan de la liste/l'arborescence pour l'élément ayant le focus quand la liste/l'arborescence est active. Une liste/aborescence active peut être sélectionnée au clavier, elle ne l'est pas quand elle est inactive.", + "listFocusForeground": "Couleur de premier plan de la liste/l'arborescence pour l'élément ayant le focus quand la liste/l'arborescence est active. Une liste/aborescence active peut être sélectionnée au clavier, elle ne l'est pas quand elle est inactive.", + "listActiveSelectionBackground": "Couleur d'arrière-plan de la liste/l'arborescence de l'élément sélectionné quand la liste/l'arborescence est active. Une liste/arborescence active peut être sélectionnée au clavier, elle ne l'est pas quand elle est inactive.", + "listActiveSelectionForeground": "Couleur de premier plan de la liste/l'arborescence pour l'élément sélectionné quand la liste/l'arborescence est active. Une liste/aborescence active peut être sélectionnée au clavier, elle ne l'est pas quand elle est inactive.", + "listInactiveSelectionBackground": "Couleur d'arrière-plan de la liste/l'arborescence pour l'élément sélectionné quand la liste/l'arborescence est inactive. Une liste/aborescence active peut être sélectionnée au clavier, elle ne l'est pas quand elle est inactive.", + "listInactiveSelectionForeground": "Couleur de premier plan de la liste/l'arborescence pour l'élément sélectionné quand la liste/l'arborescence est active. Une liste/aborescence active peut être sélectionnée au clavier, elle ne l'est pas quand elle est inactive.", + "listHoverBackground": "Arrière-plan de la liste/l'arborescence pendant le pointage sur des éléments avec la souris.", + "listHoverForeground": "Premier plan de la liste/l'arborescence pendant le pointage sur des éléments avec la souris.", + "listDropBackground": "Arrière-plan de l'opération de glisser-déplacer dans une liste/arborescence pendant le déplacement d'éléments avec la souris.", + "highlight": "Couleur de premier plan dans la liste/l'arborescence pour la surbrillance des correspondances pendant la recherche dans une liste/arborescence.", + "pickerGroupForeground": "Couleur du sélecteur rapide pour les étiquettes de regroupement.", + "pickerGroupBorder": "Couleur du sélecteur rapide pour les bordures de regroupement.", + "buttonForeground": "Couleur de premier plan du bouton.", + "buttonBackground": "Couleur d'arrière-plan du bouton.", + "buttonHoverBackground": "Couleur d'arrière-plan du bouton pendant le pointage.", + "badgeBackground": "Couleur de fond des badges. Les badges sont de courts libelés d'information, ex. le nombre de résultats de recherche.", + "badgeForeground": "Couleur des badges. Les badges sont de courts libelés d'information, ex. le nombre de résultats de recherche.", + "scrollbarShadow": "Ombre de la barre de défilement pour indiquer que la vue défile.", + "scrollbarSliderBackground": "Couleur de fond du curseur de la barre de défilement.", + "scrollbarSliderHoverBackground": "Couleur de fond du curseur de la barre de défilement lors du survol.", + "scrollbarSliderActiveBackground": "Couleur de fond du curseur de la barre de défilement en utilisation.", + "progressBarBackground": "Couleur de fond pour la barre de progression qui peut s'afficher lors d'opérations longues.", + "editorBackground": "Couleur d'arrière-plan de l'éditeur.", + "editorForeground": "Couleur de premier plan par défaut de l'éditeur.", + "editorWidgetBackground": "Couleur d'arrière-plan des gadgets de l'éditeur tels que rechercher/remplacer.", + "editorWidgetBorder": "Couleur de bordure des widgets de l'éditeur. La couleur est utilisée uniquement si le widget choisit d'avoir une bordure et si la couleur n'est pas remplacée par un widget.", + "editorSelectionBackground": "Couleur de la sélection de l'éditeur.", + "editorSelectionForeground": "Couleur du texte sélectionné pour le contraste élevé.", + "editorInactiveSelection": "Couleur de la sélection dans un éditeur inactif.", + "editorSelectionHighlight": "Couleur des régions dont le contenu est identique à la sélection.", + "editorFindMatch": "Couleur du résultat de recherche actif.", + "findMatchHighlight": "Couleur des autres résultats de recherche.", + "findRangeHighlight": "Couleur de la plage limitant la recherche.", + "hoverHighlight": "Mettez en surbrillance ci-dessous le mot pour lequel un pointage s'affiche.", + "hoverBackground": "Couleur d'arrière-plan du pointage de l'éditeur.", + "hoverBorder": "Couleur de bordure du pointage de l'éditeur.", + "activeLinkForeground": "Couleur des liens actifs.", + "diffEditorInserted": "Couleur d'arrière-plan du texte inséré.", + "diffEditorRemoved": "Couleur d'arrière-plan du texte supprimé.", + "diffEditorInsertedOutline": "Couleur de contour du texte inséré.", + "diffEditorRemovedOutline": "Couleur de contour du texte supprimé.", + "mergeCurrentHeaderBackground": "Arrière-plan de l'en-tête actuel dans les conflits de fusion inline.", + "mergeCurrentContentBackground": "Arrière-plan du contenu actuel dans les conflits de fusion inline.", + "mergeIncomingHeaderBackground": "Arrière-plan de l'en-tête entrant dans les conflits de fusion inline.", + "mergeIncomingContentBackground": "Arrière-plan du contenu entrant dans les conflits de fusion inline.", + "mergeCommonHeaderBackground": "Arrière-plan de l'en-tête de l'ancêtre commun dans les conflits de fusion inline.", + "mergeCommonContentBackground": "Arrière-plan du contenu de l'ancêtre commun dans les conflits de fusion inline.", + "mergeBorder": "Couleur de bordure des en-têtes et du séparateur dans les conflits de fusion inline.", + "overviewRulerCurrentContentForeground": "Premier plan de la règle d'aperçu actuelle pour les conflits de fusion inline.", + "overviewRulerIncomingContentForeground": "Premier plan de la règle d'aperçu entrante pour les conflits de fusion inline.", + "overviewRulerCommonContentForeground": "Arrière-plan de la règle d'aperçu de l'ancêtre commun dans les conflits de fusion inline.", + "overviewRulerFindMatchForeground": "Couleur du marqueur de la règle d'aperçu pour rechercher des correspondances.", + "overviewRulerSelectionHighlightForeground": "Couleur du marqueur de la règle d'aperçu pour la mise en évidence de la sélection." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/fra/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..4f8cd25558 --- /dev/null +++ b/i18n/fra/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "Espace de travail de code", + "untitledWorkspace": "Sans titre(Espace de travail)", + "workspaceNameVerbose": "{0} (Espace de travail)", + "workspaceName": "{0} (Espace de travail)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/fra/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..d44101c695 --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Remplacement de l'extension {0} par {1}.", + "extensionUnderDevelopment": "Chargement de l'extension de développement sur {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/fra/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..84147f2405 --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Fermer", + "cancel": "Annuler", + "ok": "OK" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/fra/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..a819a7fbfa --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "Les {0} erreurs et avertissements supplémentaires ne sont pas affichés." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/node/extHostExplorerView.i18n.json b/i18n/fra/src/vs/workbench/api/node/extHostExplorerView.i18n.json new file mode 100644 index 0000000000..37ca00cfc7 --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/node/extHostExplorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "Aucun TreeExplorerNodeProvider ayant l'ID '{0}' n'est inscrit.", + "treeExplorer.failedToProvideRootNode": "Le TreeExplorerNodeProvider '{0}' n'a pas pu fournir le nœud racine." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/fra/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..80f8bacc07 --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "Échec de l'activation de l'extension '{1}'. Raison : dépendance '{0}' inconnue.", + "failedDep1": "Échec de l'activation de l'extension '{1}'. Raison : échec de l'activation de la dépendance '{0}'.", + "failedDep2": "Échec de l'activation de l'extension '{0}'. Raison : plus de 10 niveaux de dépendances (probablement une boucle de dépendance).", + "activationError": "Échec de l'activation de l'extension '{0}' : {1}." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/fra/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..1865c04664 --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0} : {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json b/i18n/fra/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json new file mode 100644 index 0000000000..bb2eccd7fc --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "Aucun TreeExplorerNodeProvider ayant l'ID '{0}' n'est inscrit.", + "treeExplorer.failedToProvideRootNode": "Le TreeExplorerNodeProvider '{0}' n'a pas pu fournir le nœud racine.", + "treeExplorer.failedToResolveChildren": "Le TreeExplorerNodeProvider '{0}' n'a pas pu résoudre resolveChildren." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/node/extHostTreeView.i18n.json b/i18n/fra/src/vs/workbench/api/node/extHostTreeView.i18n.json new file mode 100644 index 0000000000..37ca00cfc7 --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/node/extHostTreeView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "Aucun TreeExplorerNodeProvider ayant l'ID '{0}' n'est inscrit.", + "treeExplorer.failedToProvideRootNode": "Le TreeExplorerNodeProvider '{0}' n'a pas pu fournir le nœud racine." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/fra/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..f79eff3ebb --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "Aucune arborescence avec l'ID '{0}' n'est inscrite.", + "treeItem.notFound": "L'élément d'arborescence avec l'ID '{0}' est introuvable.", + "treeView.duplicateElement": "L'élément '{0}' est déjà inscrit" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json b/i18n/fra/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..d44101c695 --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Remplacement de l'extension {0} par {1}.", + "extensionUnderDevelopment": "Chargement de l'extension de développement sur {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/api/node/mainThreadMessageService.i18n.json b/i18n/fra/src/vs/workbench/api/node/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..84147f2405 --- /dev/null +++ b/i18n/fra/src/vs/workbench/api/node/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Fermer", + "cancel": "Annuler", + "ok": "OK" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..d7dda81352 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "Configurer la langue", + "displayLanguage": "Définit le langage affiché par VSCode.", + "doc": "Consultez {0} pour connaître la liste des langues prises en charge.", + "restart": "Le changement de la valeur nécessite le redémarrage de VS Code.", + "fail.createSettings": "Impossible de créer '{0}' ({1}).", + "JsonSchema.locale": "Langue d'interface utilisateur (IU) à utiliser." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..7b16d8fe9e --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Ouvrir un dossier...", + "openFileFolder": "Ouvrir..." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..e7179be253 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "Activer/désactiver la visibilité de la barre d'activités", + "view": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..5d90e697ad --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "Activer/désactiver la disposition horizontale/verticale du groupe d'éditeurs", + "horizontalLayout": "Disposition horizontale du groupe d'éditeurs", + "verticalLayout": "Disposition verticale du groupe d'éditeurs", + "view": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..27bb4956c6 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Activer/désactiver l'emplacement de la barre latérale", + "view": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..bd4322a0f1 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "Activer/désactiver la visibilité de la barre latérale", + "view": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..fb47128c92 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "Activer/désactiver la visibilité de la barre d'état", + "view": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..661ef79988 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "Activer/désactiver le mode zen", + "view": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..e32cf5cb0a --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Ouvrir un dossier...", + "openFileFolder": "Ouvrir...", + "addFolderToWorkspace": "Ajouter un dossier à l'espace de travail...", + "add": "&&Ajouter", + "addFolderToWorkspaceTitle": "Ajouter un dossier à l'espace de travail", + "newWorkspace": "Nouvel espace de travail...", + "select": "&&Sélectionner", + "selectWorkspace": "Sélectionner les dossiers pour l’espace de travail", + "removeFolderFromWorkspace": "Supprimer le dossier de l'espace de travail", + "saveWorkspaceAsAction": "Enregistrer l’espace de travail sous...", + "saveEmptyWorkspaceNotSupported": "Veuillez d’abord ouvrir un espace de travail pour enregistrer.", + "save": "&&Enregistrer", + "saveWorkspace": "Enregistrer l’espace de travail", + "openWorkspaceAction": "Ouvrir un espace de travail...", + "openWorkspaceConfigFile": "Ouvrir le Fichier de Configuration d’espace de travail" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..fcbd35f6f5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "Supprimer de la barre d'activités", + "keepInActivityBar": "Conserver dans la barre d'activités", + "titleKeybinding": "{0} ({1})", + "additionalViews": "Vues supplémentaires", + "numberBadge": "{0} ({1})", + "manageExtension": "Gérer l'extension", + "toggle": "Afficher/masquer la vue épinglée" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..a3c30d59b0 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "Masquer la barre d'activités", + "activityBarAriaLabel": "Sélecteur d'affichage actif", + "globalActions": "Actions globales" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..31762b420f --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "{0} actions", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..3cf3889b5c --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "Visionneuse binaire" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..5d70ee61f9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Éditeur de texte", + "textDiffEditor": "Éditeur de différences textuelles", + "binaryDiffEditor": "Éditeur de différences binaires", + "sideBySideEditor": "Éditeur côte à côte", + "groupOnePicker": "Afficher les éditeurs du premier groupe", + "groupTwoPicker": "Afficher les éditeurs du deuxième groupe", + "groupThreePicker": "Afficher les éditeurs du troisième groupe", + "allEditorsPicker": "Afficher tous les éditeurs ouverts", + "view": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..b619d6b5d8 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "Fractionner l'éditeur", + "joinTwoGroups": "Joindre les éditeurs de deux groupes", + "navigateEditorGroups": "Naviguer entre les groupes d'éditeurs", + "focusActiveEditorGroup": "Placer le focus sur le groupe d'éditeurs d'actifs", + "focusFirstEditorGroup": "Focus sur le premier groupe d'éditeurs", + "focusSecondEditorGroup": "Focus sur le deuxième groupe d'éditeurs", + "focusThirdEditorGroup": "Focus sur le troisième groupe d'éditeurs", + "focusPreviousGroup": "Focus sur le groupe précédent", + "focusNextGroup": "Focus sur le groupe suivant", + "openToSide": "Ouvrir sur le côté", + "closeEditor": "Fermer l'éditeur", + "revertAndCloseActiveEditor": "Restaurer et fermer l'éditeur", + "closeEditorsToTheLeft": "Fermer les éditeurs situés à gauche", + "closeEditorsToTheRight": "Fermer les éditeurs situés à droite", + "closeAllEditors": "Fermer tous les éditeurs", + "closeUnmodifiedEditors": "Fermer les éditeurs non modifiés du groupe", + "closeEditorsInOtherGroups": "Fermer les éditeurs des autres groupes", + "closeOtherEditorsInGroup": "Fermer les autres éditeurs", + "closeEditorsInGroup": "Fermer tous les éditeurs du groupe", + "moveActiveGroupLeft": "Déplacer le groupe d'éditeurs vers la gauche", + "moveActiveGroupRight": "Déplacer le groupe d'éditeurs vers la droite", + "minimizeOtherEditorGroups": "Réduire les autres groupes d'éditeurs", + "evenEditorGroups": "Même largeur pour le groupe d'éditeurs", + "maximizeEditor": "Agrandir le groupe d'éditeurs et masquer la barre latérale", + "keepEditor": "Conserver l'éditeur", + "openNextEditor": "Ouvrir l'éditeur suivant", + "openPreviousEditor": "Ouvrir l'éditeur précédent", + "nextEditorInGroup": "Ouvrir l'éditeur suivant du groupe", + "openPreviousEditorInGroup": "Ouvrir l'éditeur précédent du groupe", + "navigateNext": "Suivant", + "navigatePrevious": "Précédent", + "reopenClosedEditor": "Rouvrir l'éditeur fermé", + "clearRecentFiles": "Effacer les fichiers récemment ouverts", + "showEditorsInFirstGroup": "Afficher les éditeurs du premier groupe", + "showEditorsInSecondGroup": "Afficher les éditeurs du deuxième groupe", + "showEditorsInThirdGroup": "Afficher les éditeurs du troisième groupe", + "showEditorsInGroup": "Afficher les éditeurs du groupe", + "showAllEditors": "Afficher tous les éditeurs", + "openPreviousRecentlyUsedEditorInGroup": "Ouvrir l'éditeur précédent du groupe", + "openNextRecentlyUsedEditorInGroup": "Ouvrir l'éditeur suivant du groupe", + "navigateEditorHistoryByInput": "Ouvrir l'éditeur précédent dans l'historique", + "openNextRecentlyUsedEditor": "Ouvrir l'éditeur suivant", + "openPreviousRecentlyUsedEditor": "Ouvrir l'éditeur précédent", + "clearEditorHistory": "Effacer l'historique de l'éditeur", + "focusLastEditorInStack": "Ouvrir le dernier éditeur du groupe", + "moveEditorLeft": "Déplacer l'éditeur vers la gauche", + "moveEditorRight": "Déplacer l'éditeur vers la droite", + "moveEditorToPreviousGroup": "Déplacer l'éditeur vers le groupe précédent", + "moveEditorToNextGroup": "Déplacer l'éditeur vers le groupe suivant" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..4f9f8defd5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "Déplacer l'éditeur actif par onglets ou par groupes", + "editorCommand.activeEditorMove.arg.name": "Argument de déplacement de l'éditeur actif", + "editorCommand.activeEditorMove.arg.description": "Propriétés de l'argument :\n\\t\\t\\t\\t\\t\\t* 'to' : valeur de chaîne indiquant la direction du déplacement.\n\\t\\t\\t\\t\\t\\t* 'by' : valeur de chaîne indiquant l'unité de déplacement. Par onglet ou par groupe.\n\\t\\t\\t\\t\\t\\t* 'value' : valeur numérique indiquant le nombre de positions ou la position absolue du déplacement.\n\\t\\t\\t\\t\\t", + "commandDeprecated": "La commande **{0}** a été supprimée. Vous pouvez utiliser **{1}** à la place", + "openKeybindings": "Configurer les raccourcis clavier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..0ef47405e7 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "Gauche", + "groupTwoVertical": "Centre", + "groupThreeVertical": "Droite", + "groupOneHorizontal": "Haut", + "groupTwoHorizontal": "Centre", + "groupThreeHorizontal": "Bas", + "editorOpenError": "Impossible d'ouvrir '{0}' : {1}." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..15896b7525 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, sélecteur de groupes d'éditeurs", + "groupLabel": "Groupe : {0}", + "noResultsFoundInGroup": "Éditeur ouvert correspondant introuvable dans le groupe", + "noOpenedEditors": "La liste des éditeurs ouverts dans le groupe est vide", + "noResultsFound": "Éditeur ouvert correspondant introuvable", + "noOpenedEditorsAllGroups": "La liste des éditeurs ouverts est vide" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..052bc5f151 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "Li {0}, Col {1} ({2} sélectionné)", + "singleSelection": "Li {0}, Col {1}", + "multiSelectionRange": "{0} sélections ({1} caractères sélectionnés)", + "multiSelection": "{0} sélections", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "La touche Tab déplace le focus", + "screenReaderDetected": "Lecteur d'écran détecté", + "screenReaderDetectedExtra": "Si vous n'utilisez pas de lecteur d'écran, définissez le paramètre 'editor.accessibilitySupport' sur \"désactivé\".", + "disableTabMode": "Désactiver le mode d'accessibilité", + "gotoLine": "Atteindre la ligne", + "indentation": "Retrait", + "selectEncoding": "Sélectionner l'encodage", + "selectEOL": "Sélectionner la séquence de fin de ligne", + "selectLanguageMode": "Sélectionner le mode de langage", + "fileInfo": "Informations sur le fichier", + "spacesSize": "Espaces : {0}", + "tabSize": "Taille des tabulations : {0}", + "showLanguageExtensions": "Rechercher '{0}' dans les extensions Marketplace...", + "changeMode": "Changer le mode de langage", + "noEditor": "Aucun éditeur de texte actif actuellement", + "languageDescription": "({0}) - Langage configuré", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "langages (identificateur)", + "configureModeSettings": "Configurer les paramètres du langage '{0}'...", + "configureAssociationsExt": "Configurer l'association de fichier pour '{0}'...", + "autoDetect": "Détection automatique", + "pickLanguage": "Sélectionner le mode de langage", + "currentAssociation": "Association actuelle", + "pickLanguageToConfigure": "Sélectionnez le mode de langage à associer à '{0}'", + "changeIndentation": "Changer le retrait", + "noWritableCodeEditor": "L'éditeur de code actif est en lecture seule.", + "indentView": "modifier la vue", + "indentConvert": "convertir le fichier", + "pickAction": "Sélectionner une action", + "changeEndOfLine": "Changer la séquence de fin de ligne", + "pickEndOfLine": "Sélectionner la séquence de fin de ligne", + "changeEncoding": "Changer l'encodage des fichiers", + "noFileEditor": "Aucun fichier actif actuellement", + "saveWithEncoding": "Enregistrer avec l'encodage", + "reopenWithEncoding": "Rouvrir avec l'encodage", + "guessedEncoding": "Deviné à partir du contenu", + "pickEncodingForReopen": "Sélectionner l'encodage du fichier pour rouvrir le fichier", + "pickEncodingForSave": "Sélectionner l'encodage du fichier à utiliser pour l'enregistrement" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..3ac5753cd2 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "Actions d'onglet" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..d842154709 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "Éditeur de différences textuelles", + "readonlyEditorWithInputAriaLabel": "{0}. Éditeur de comparaison de texte en lecture seule.", + "readonlyEditorAriaLabel": "Éditeur de comparaison de texte en lecture seule.", + "editableEditorWithInputAriaLabel": "{0}. Éditeur de comparaison de fichier texte.", + "editableEditorAriaLabel": "Éditeur de comparaison de fichier texte.", + "navigate.next.label": "Modification suivante", + "navigate.prev.label": "Modification précédente", + "inlineDiffLabel": "Passer au mode inline", + "sideBySideDiffLabel": "Passer au mode Côte à côte" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..aa3ada97bc --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0}, groupe {1}." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..6db58016ee --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Éditeur de texte", + "readonlyEditorWithInputAriaLabel": "{0}. Éditeur de texte en lecture seule.", + "readonlyEditorAriaLabel": "Éditeur de texte en lecture seule.", + "untitledFileEditorWithInputAriaLabel": "{0}. Éditeur de texte de fichier sans titre.", + "untitledFileEditorAriaLabel": "Éditeur de texte de fichier sans titre." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..7f4e264d54 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Fermer", + "closeOthers": "Fermer les autres", + "closeRight": "Fermer à droite", + "closeAll": "Tout fermer", + "closeAllUnmodified": "Fermer les éléments non modifiés", + "keepOpen": "Garder ouvert", + "showOpenedEditors": "Afficher les éditeurs ouverts", + "araLabelEditorActions": "Actions de l'éditeur" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..74a3ae91a2 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "Fermer le panneau", + "togglePanel": "Activer/désactiver le panneau", + "focusPanel": "Focus dans le panneau", + "toggleMaximizedPanel": "Activer/désactiver le panneau agrandi", + "maximizePanel": "Agrandir la taille du panneau", + "minimizePanel": "Restaurer la taille du panneau", + "view": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..a0dac128dc --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "Sélecteur de panneau actif" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..7997ef78b1 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} (Appuyez sur 'Entrée' pour confirmer ou sur 'Échap' pour annuler)", + "inputModeEntry": "Appuyez sur 'Entrée' pour confirmer votre saisie, ou sur 'Échap' pour l'annuler", + "emptyPicks": "Aucune entrée à sélectionner", + "quickOpenInput": "Tapez '?' pour obtenir de l'aide sur les actions que vous pouvez effectuer ici", + "historyMatches": "récemment ouvert", + "noResultsFound1": "Résultats introuvables", + "canNotRunPlaceholder": "Ce gestionnaire Quick Open ne peut pas être utilisé dans le contexte actuel", + "entryAriaLabel": "{0}, ouvert récemment", + "removeFromEditorHistory": "Supprimer de l'historique", + "pickHistory": "Sélectionnez une entrée de l'éditeur à supprimer de l'historique" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..b3b1eaf1ba --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Atteindre le fichier...", + "quickNavigateNext": "Naviguer vers l'élément suivant dans Quick Open", + "quickNavigatePrevious": "Naviguer vers l'élément précédent dans Quick Open", + "quickSelectNext": "Sélectionner l'élément suivant dans Quick Open", + "quickSelectPrevious": "Sélectionner l'élément précédent dans Quick Open" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..b3b1eaf1ba --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Atteindre le fichier...", + "quickNavigateNext": "Naviguer vers l'élément suivant dans Quick Open", + "quickNavigatePrevious": "Naviguer vers l'élément précédent dans Quick Open", + "quickSelectNext": "Sélectionner l'élément suivant dans Quick Open", + "quickSelectPrevious": "Sélectionner l'élément précédent dans Quick Open" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..40e9447792 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "Masquer la barre latérale", + "focusSideBar": "Focus sur la barre latérale", + "viewCategory": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..35a46bc94e --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "La commande '{0}' n'est pas activée et ne peut pas être exécutée.", + "manageExtension": "Gérer l'extension" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..923ae6c684 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[Non prise en charge]", + "devExtensionWindowTitlePrefix": "[Hôte de développement d'extension]" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/quickopen.i18n.json b/i18n/fra/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..fde1bc5087 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "Aucun résultat correspondant", + "noResultsFound2": "Résultats introuvables", + "entryAriaLabel": "{0}, commande" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/viewlet.i18n.json b/i18n/fra/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..572e3f4086 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Réduire tout" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/common/theme.i18n.json b/i18n/fra/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..b5724fdccc --- /dev/null +++ b/i18n/fra/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "Couleur d'arrière-plan de l'onglet actif. Les onglets sont les conteneurs des éditeurs dans la zone d'éditeurs. Vous pouvez ouvrir plusieurs onglets dans un groupe d'éditeurs. Il peut exister plusieurs groupes d'éditeurs.", + "tabInactiveBackground": "Couleur d'arrière-plan de l'onglet inactif. Les onglets sont les conteneurs des éditeurs dans la zone d'éditeurs. Vous pouvez ouvrir plusieurs onglets dans un groupe d'éditeurs. Il peut exister plusieurs groupes d'éditeurs.", + "tabBorder": "Bordure séparant les onglets les uns des autres. Les onglets sont les conteneurs des éditeurs dans la zone d'éditeurs. Vous pouvez ouvrir plusieurs onglets dans un groupe d'éditeurs. Il peut exister plusieurs groupes d'éditeurs.", + "tabActiveBorder": "Bordure pour mettre en évidence les onglets actifs. Les onglets sont les conteneurs des éditeurs dans la zone d'édition. Vous pouvez ouvrir plusieurs onglets dans un groupe d'éditeurs. Il peut exister plusieurs groupes d'éditeurs.", + "tabActiveUnfocusedBorder": "Bordure pour mettre en évidence les onglets actifs dans un groupe inactif. Les onglets sont les conteneurs des éditeurs dans la zone d'édition. Vous pouvez ouvrir plusieurs onglets dans un groupe d'éditeurs. Il peut exister plusieurs groupes d'éditeurs.", + "tabActiveForeground": "Couleur de premier plan de l'onglet actif dans un groupe actif. Les onglets sont les conteneurs des éditeurs dans la zone d'éditeurs. Vous pouvez ouvrir plusieurs onglets dans un groupe d'éditeurs. Il peut exister plusieurs groupes d'éditeurs.", + "tabInactiveForeground": "Couleur de premier plan de l'onglet inactif dans un groupe actif. Les onglets sont les conteneurs des éditeurs dans la zone d'éditeurs. Vous pouvez ouvrir plusieurs onglets dans un groupe d'éditeurs. Il peut exister plusieurs groupes d'éditeurs.", + "tabUnfocusedActiveForeground": "Couleur de premier plan de l'onglet actif dans un groupe inactif. Les onglets sont les conteneurs des éditeurs dans la zone d'éditeurs. Vous pouvez ouvrir plusieurs onglets dans un groupe d'éditeurs. Il peut exister plusieurs groupes d'éditeurs.", + "tabUnfocusedInactiveForeground": "Couleur de premier plan de l'onglet inactif dans un groupe inactif. Les onglets sont les conteneurs des éditeurs dans la zone d'éditeurs. Vous pouvez ouvrir plusieurs onglets dans un groupe d'éditeurs. Il peut exister plusieurs groupes d'éditeurs.", + "editorGroupBackground": "Couleur d'arrière-plan d'un groupe d'éditeurs. Les groupes d'éditeurs sont les conteneurs des éditeurs. La couleur d'arrière-plan s'affiche pendant le glissement de groupes d'éditeurs.", + "tabsContainerBackground": "Couleur d'arrière-plan de l'en-tête du titre du groupe d'éditeurs quand les onglets sont activés. Les groupes d'éditeurs sont les conteneurs des éditeurs.", + "tabsContainerBorder": "Couleur de bordure de l'en-tête du titre du groupe d'éditeurs quand les onglets sont activés. Les groupes d'éditeurs sont les conteneurs des éditeurs.", + "editorGroupHeaderBackground": "Couleur d'arrière-plan de l'en-tête du titre du groupe d'éditeurs quand les onglets sont désactivés. Les groupes d'éditeurs sont les conteneurs des éditeurs.", + "editorGroupBorder": "Couleur séparant plusieurs groupes d'éditeurs les uns des autres. Les groupes d'éditeurs sont les conteneurs des éditeurs.", + "editorDragAndDropBackground": "Couleur d'arrière-plan lors du déplacement des éditeurs par glissement. La couleur doit avoir une transparence pour que le contenu de l'éditeur soit visible à travers.", + "panelBackground": "Couleur d'arrière-plan du panneau. Les panneaux s'affichent sous la zone d'éditeurs et contiennent des affichages tels que la sortie et le terminal intégré.", + "panelBorder": "Couleur de bordure de panneau dans la partie supérieure de séparation de l'éditeur. Les panneaux s'affichent sous la zone d'éditeurs et contiennent des affichages tels que la sortie et le terminal intégré.", + "panelActiveTitleForeground": "Couleur du titre du panneau actif. Les panneaux se situent sous la zone de l'éditeur et contiennent des affichages comme la sortie et le terminal intégré.", + "panelInactiveTitleForeground": "Couleur du titre du panneau inactif. Les panneaux se situent sous la zone de l'éditeur et contiennent des affichages comme la sortie et le terminal intégré.", + "panelActiveTitleBorder": "Couleur de la bordure du titre du panneau actif. Les panneaux se situent sous la zone de l'éditeur et contiennent des affichages comme la sortie et le terminal intégré.", + "statusBarForeground": "Couleur de premier plan de la barre d'état quand l'espace de travail est ouvert. La barre d'état est affichée en bas de la fenêtre.", + "statusBarNoFolderForeground": "Couleur de premier plan de la barre d'état quand aucun dossier n'est ouvert. La barre d'état est affichée en bas de la fenêtre.", + "statusBarBackground": "Couleur d'arrière-plan de la barre d'état quand l'espace de travail est ouvert. La barre d'état est affichée en bas de la fenêtre.", + "statusBarNoFolderBackground": "Couleur d'arrière-plan de la barre d'état quand aucun dossier n'est ouvert. La barre d'état est affichée en bas de la fenêtre.", + "statusBarBorder": "Couleur de bordure de la barre d'état faisant la séparation avec la barre latérale et l'éditeur. La barre d'état est affichée en bas de la fenêtre.", + "statusBarNoFolderBorder": "Couleur de la bordure qui sépare la barre latérale et l’éditeur lorsque aucun dossier ne s’ouvre la barre d’état. La barre d’état s’affiche en bas de la fenêtre.", + "statusBarItemActiveBackground": "Couleur d'arrière-plan de l'élément de la barre d'état durant un clic. La barre d'état est affichée en bas de la fenêtre.", + "statusBarItemHoverBackground": "Couleur d'arrière-plan de l'élément de la barre d'état durant un pointage. La barre d'état est affichée en bas de la fenêtre.", + "statusBarProminentItemBackground": "Couleur d'arrière-plan des éléments importants de la barre d'état. Les éléments importants se différencient des autres entrées de la barre d'état pour indiquer l'importance. La barre d'état est affichée en bas de la fenêtre.", + "statusBarProminentItemHoverBackground": "Couleur d'arrière-plan des éléments importants de la barre d'état pendant le pointage. Les éléments importants se différencient des autres entrées de la barre d'état pour indiquer l'importance. La barre d'état est affichée en bas de la fenêtre.", + "activityBarBackground": "Couleur d'arrière-plan de la barre d'activités. La barre d'activités s'affiche complètement à gauche ou à droite, et permet de naviguer entre les affichages de la barre latérale.", + "activityBarForeground": "Couleur de premier plan de la barre d'activités (par ex., utilisée pour les icônes). La barre d'activités s'affiche complètement à gauche ou à droite, et permet de parcourir les vues de la barre latérale.", + "activityBarBorder": "Couleur de bordure de la barre d'activités faisant la séparation avec la barre latérale. La barre d'activités, située à l'extrême droite ou gauche, permet de parcourir les vues de la barre latérale.", + "activityBarDragAndDropBackground": "Couleur des commentaires sur une opération de glisser-déplacer pour les éléments de la barre d'activités. La couleur doit avoir une transparence pour que les entrées de la barre d'activités soient visibles à travers. La barre d'activités, située à l'extrême droite ou gauche, permet de parcourir les vues de la barre latérale.", + "activityBarBadgeBackground": "Couleur d'arrière-plan du badge de notification d'activité. La barre d'activités, située à l'extrême gauche ou droite, permet de basculer entre les affichages de la barre latérale.", + "activityBarBadgeForeground": "Couleur de premier plan du badge de notification d'activité. La barre d'activités, située à l'extrême gauche ou droite, permet de basculer entre les affichages de la barre latérale.", + "sideBarBackground": "Couleur d'arrière-plan de la barre latérale. La barre latérale est le conteneur des affichages tels que ceux de l'exploration et la recherche.", + "sideBarForeground": "Couleur de premier plan de la barre latérale. La barre latérale est le conteneur des vues comme celles de l'explorateur et de la recherche.", + "sideBarBorder": "Couleur de bordure de la barre latérale faisant la séparation avec l'éditeur. La barre latérale est le conteneur des vues comme celles de l'explorateur et de la recherche.", + "sideBarTitleForeground": "Couleur de premier plan du titre de la barre latérale. La barre latérale est le conteneur des affichages tels que ceux de l'exploration et la recherche.", + "sideBarDragAndDropBackground": "Glisser-déposer la couleur pour les sections de barre latérale. La couleur devrait avoir de la transparence afin que les sections de barre latérale puissent encore briller à travers. La barre latérale est le conteneur des vues comme l'explorateur et la recherche.", + "sideBarSectionHeaderBackground": "Couleur d'arrière-plan de l'en-tête de section de la barre latérale. La barre latérale est le conteneur des vues comme celles de l'explorateur et la recherche.", + "sideBarSectionHeaderForeground": "Couleur de premier plan de l'en-tête de section de la barre latérale. La barre latérale est le conteneur des vues comme celles de l'explorateur et de la recherche.", + "titleBarActiveForeground": "Premier plan de la barre de titre quand la fenêtre est active. Notez que cette couleur est uniquement prise en charge sur macOS.", + "titleBarInactiveForeground": "Premier plan de la barre de titre quand la fenêtre est inactive. Notez que cette couleur est uniquement prise en charge sur macOS.", + "titleBarActiveBackground": "Arrière-plan de la barre de titre quand la fenêtre est active. Notez que cette couleur est uniquement prise en charge sur macOS.", + "titleBarInactiveBackground": "Arrière-plan de la barre de titre quand la fenêtre est inactive. Notez que cette couleur est uniquement prise en charge sur macOS.", + "titleBarBorder": "Couleur de bordure de la barre titre. Notez que cette couleur est actuellement uniquement pris en charge sur macOS.", + "notificationsForeground": "Couleur de premier plan des notifications. Les notifications défilent à partir du haut de la fenêtre.", + "notificationsBackground": "Couleur d'arrière-plan des notifications. Les notifications défilent à paritr du haut de la fenêtre.", + "notificationsButtonBackground": "Couleur d'arrière-plan du bouton des notifications. Les notifications défilent à partir du haut de la fenêtre.", + "notificationsButtonHoverBackground": "Couleur d'arrière-plan du bouton des notifications pendant le pointage. Les notifications défilent à partir du haut de la fenêtre.", + "notificationsButtonForeground": "Couleur de premier plan du bouton des notifications. Les notifications défilent à partir du haut de la fenêtre.", + "notificationsInfoBackground": "Couleur d'arrière-plan des informations des notifications. Les notifications défilent à partir du haut de la fenêtre.", + "notificationsInfoForeground": "Couleur de premier plan des informations des notifications. Les notifications défilent à partir du haut de la fenêtre.", + "notificationsWarningBackground": "Couleur d'arrière-plan de l'avertissement des notifications. Les notifications défilent à partir du haut de la fenêtre.", + "notificationsWarningForeground": "Couleur de premier plan de l'avertissement des notifications. Les notifications défilent à partir du haut de la fenêtre.", + "notificationsErrorBackground": "Couleur d'arrière-plan de l'erreur des notifications. Les notifications défilent à partir du haut de la fenêtre.", + "notificationsErrorForeground": "Couleur de premier plan de l'erreur des notifications. Les notifications défilent à partir du haut de la fenêtre." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..8be0fefcb6 --- /dev/null +++ b/i18n/fra/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "Fermer l'éditeur", + "closeWindow": "Fermer la fenêtre", + "closeWorkspace": "Fermer l’espace de travail", + "noWorkspaceOpened": "Il n’y a actuellement aucun espace de travail ouvert dans cette instance à fermer.", + "newWindow": "Nouvelle fenêtre", + "toggleFullScreen": "Plein écran", + "toggleMenuBar": "Activer/désactiver la barre de menus", + "toggleDevTools": "Activer/désactiver les outils de développement", + "zoomIn": "Zoom avant", + "zoomOut": "Zoom arrière", + "zoomReset": "Réinitialiser le zoom", + "appPerf": "Performance de démarrage", + "reloadWindow": "Recharger la fenêtre", + "switchWindowPlaceHolder": "Sélectionner une fenêtre vers laquelle basculer", + "current": "Fenêtre active", + "close": "Fermer la fenêtre", + "switchWindow": "Changer de fenêtre...", + "quickSwitchWindow": "Changement rapide de fenêtre...", + "workspaces": "espaces de travail", + "files": "fichiers", + "openRecentPlaceHolderMac": "Sélectionner pour ouvrir (maintenir Cmd-key pour ouvrir dans une nouvelle fenêtre)", + "openRecentPlaceHolder": "Sélectionner pour ouvrir (maintenir Ctrl-key pour ouvrir dans une nouvelle fenêtre)", + "remove": "Supprimer des récemment ouverts", + "openRecent": "Ouvrir les éléments récents...", + "quickOpenRecent": "Ouverture rapide des éléments récents...", + "closeMessages": "Fermer les messages de notification", + "reportIssues": "Signaler des problèmes", + "reportPerformanceIssue": "Signaler un problème de performance", + "keybindingsReference": "Référence des raccourcis clavier", + "openDocumentationUrl": "Documentation", + "openIntroductoryVideosUrl": "Vidéos d'introduction", + "openTipsAndTricksUrl": "Conseils et astuces", + "toggleSharedProcess": "Activer/désactiver le processus partagé", + "navigateLeft": "Naviguer vers l'affichage à gauche", + "navigateRight": "Naviguer vers l'affichage à droite", + "navigateUp": "Naviguer vers l'affichage au-dessus", + "navigateDown": "Naviguer vers l'affichage en dessous", + "increaseViewSize": "Augmenter la taille de l'affichage actuel", + "decreaseViewSize": "Diminuer la taille de l'affichage actuel" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/fra/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/crashReporter.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/crashReporter.i18n.json new file mode 100644 index 0000000000..ab87801aab --- /dev/null +++ b/i18n/fra/src/vs/workbench/electron-browser/crashReporter.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Télémétrie", + "telemetry.enableCrashReporting": "Activez l'envoi de rapports d'incidents à Microsoft.\nCette option nécessite un redémarrage pour être prise en compte." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..2ba46fa828 --- /dev/null +++ b/i18n/fra/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "L'hôte d'extension n'a pas démarré en moins de 10 secondes. Il est peut-être arrêté à la première ligne et a besoin d'un débogueur pour continuer.", + "extensionHostProcess.startupFail": "L'hôte d'extension n'a pas démarré en moins de 10 secondes. Il existe peut-être un problème.", + "extensionHostProcess.error": "Erreur de l'hôte d'extension : {0}", + "devTools": "Outils de développement", + "extensionHostProcess.crash": "L'hôte d'extension s'est terminé de façon inattendue. Rechargez la fenêtre pour reprendre l'exécution." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..d391e1b44a --- /dev/null +++ b/i18n/fra/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "Affichage", + "help": "Aide", + "file": "Fichier", + "workspaces": "Espaces de travail", + "developer": "Développeur", + "showEditorTabs": "Contrôle si les éditeurs ouverts doivent s'afficher ou non sous des onglets.", + "editorTabCloseButton": "Contrôle la position des boutons de fermeture des onglets de l'éditeur, ou les désactive quand le paramètre a la valeur 'off'.", + "showIcons": "Contrôle si les éditeurs ouverts doivent s'afficher ou non avec une icône. Cela implique notamment l'activation d'un thème d'icône.", + "enablePreview": "Contrôle si les éditeurs ouverts s'affichent en mode aperçu. Les éditeurs en mode aperçu sont réutilisés jusqu'à ce qu'ils soient conservés (par exemple, après un double-clic ou une modification).", + "enablePreviewFromQuickOpen": "Contrôle si les éditeurs de Quick Open s'affichent en mode aperçu. Les éditeurs en mode aperçu sont réutilisés jusqu'à ce qu'ils soient conservés (par exemple, après un double-clic ou une modification).", + "editorOpenPositioning": "Contrôle l'emplacement de l'ouverture des éditeurs. Sélectionnez 'left' ou 'right' pour ouvrir les éditeurs à gauche ou à droite de l'éditeur actuellement actif. Sélectionnez 'first' ou 'last' pour ouvrir les éditeurs indépendamment de l'éditeur actuellement actif.", + "revealIfOpen": "Contrôle si un éditeur est affiché dans l'un des groupes visibles, s'il est ouvert. Si cette option est désactivée, l'éditeur s'ouvre de préférence dans le groupe d'éditeurs actif. Si cette option est activée, tout éditeur déjà ouvert est affiché au lieu d'être rouvert dans le groupe d'éditeurs actif. Notez que dans certains cas, ce paramètre est ignoré, par exemple quand vous forcez un éditeur à s'ouvrir dans un groupe spécifique ou à côté du groupe actif.", + "commandHistory": "Contrôle le nombre de commandes récemment utilisées à conserver dans l'historique de la palette de commandes. Définissez cette valeur sur 0 pour désactiver l'historique de commandes.", + "preserveInput": "Contrôle si la dernière entrée tapée dans la palette de commandes doit être restaurée à la prochaine ouverture.", + "closeOnFocusLost": "Contrôle si Quick Open doit se fermer automatiquement, une fois qu'il a perdu le focus.", + "openDefaultSettings": "Contrôle si l'ouverture des paramètres entraîne également l'ouverture d'un éditeur qui affiche tous les paramètres par défaut.", + "sideBarLocation": "Contrôle l'emplacement de la barre latérale. Elle peut s'afficher à gauche ou à droite du banc d'essai.", + "statusBarVisibility": "Contrôle la visibilité de la barre d'état au bas du banc d'essai.", + "activityBarVisibility": "Contrôle la visibilité de la barre d'activités dans le banc d'essai.", + "closeOnFileDelete": "Contrôle si les éditeurs qui affichent un fichier doivent se fermer automatiquement quand ce fichier est supprimé ou renommé par un autre processus. Si vous désactivez cette option, l'éditeur reste ouvert dans un état indiquant une intégrité compromise. Notez que la suppression de fichiers à partir de l'application entraîne toujours la fermeture de l'éditeur, et que les fichiers à l'intégrité compromise ne sont jamais fermés pour permettre la conservation de vos données.", + "fontAliasing": "Contrôle la méthode de font aliasing dans le workbench.\n- par défaut : Lissage des polices de sous-pixel. Sur la plupart des affichages non-ratina, cela vous donnera le texte le plus vif\n- crénelées : Lisse les polices au niveau du pixel, plutôt que les sous-pixels. Peut faire en sorte que la police apparaisse plus légère dans l’ensemble \n- none : désactive le lissage des polices. Le texte s'affichera avec des bordures dentelées", + "workbench.fontAliasing.default": "Lissage de sous-pixel des polices. Sur la plupart des affichages non-retina, cela vous donnera le texte le plus vif.", + "workbench.fontAliasing.antialiased": "Lisser les polices au niveau du pixel, plutôt que les sous-pixels. Peut faire en sorte que la police apparaisse plus légère dans l’ensemble.", + "workbench.fontAliasing.none": "Désactive le lissage des polices. Le texte s'affichera avec des bordures dentelées.", + "swipeToNavigate": "Parcourez les fichiers ouverts en faisant glisser trois doigts horizontalement. ", + "workbenchConfigurationTitle": "Banc d'essai", + "window.openFilesInNewWindow.on": "Les fichiers s'ouvrent dans une nouvelle fenêtre", + "window.openFilesInNewWindow.off": "Les fichiers s'ouvrent dans la fenêtre du dossier conteneur ouvert ou dans la dernière fenêtre active", + "window.openFilesInNewWindow.default": "Les fichiers s'ouvrent dans la fenêtre du dossier conteneur ouvert ou dans la dernière fenêtre active, sauf s'ils sont ouverts via le Dock ou depuis le Finder (macOS uniquement)", + "openFilesInNewWindow": "Contrôle si les fichiers doivent s'ouvrir dans une nouvelle fenêtre.\n- default : les fichiers s'ouvrent dans la fenêtre du dossier conteneur ouvert ou dans la dernière fenêtre active, sauf s'ils sont ouverts via le Dock ou depuis le Finder (macOS uniquement)\n- on : les fichiers s'ouvrent dans une nouvelle fenêtre\n- off : les fichiers s'ouvrent dans la fenêtre du dossier conteneur ouvert ou dans la dernière fenêtre active\nNotez que dans certains cas, ce paramètre est ignoré (par exemple, quand vous utilisez l'option de ligne de commande -new-window ou -reuse-window).", + "window.openFoldersInNewWindow.on": "Les dossiers s'ouvrent dans une nouvelle fenêtre", + "window.openFoldersInNewWindow.off": "Les dossiers remplacent la dernière fenêtre active", + "window.openFoldersInNewWindow.default": "Les dossiers s'ouvrent dans une nouvelle fenêtre, sauf si un dossier est sélectionné depuis l'application (par exemple, via le menu Fichier)", + "openFoldersInNewWindow": "Contrôle si les dossiers doivent s'ouvrir dans une nouvelle fenêtre ou remplacer la dernière fenêtre active.\n- default : les dossiers s'ouvrent dans une nouvelle fenêtre, sauf si un dossier est sélectionné depuis l'application (par exemple, via le menu Fichier)\n- on : les dossiers s'ouvrent dans une nouvelle fenêtre\n- off : les dossiers remplacent la dernière fenêtre active\nNotez que dans certains cas, ce paramètre est ignoré (par exemple, quand vous utilisez l'option de ligne de commande -new-window ou -reuse-window).", + "window.reopenFolders.all": "Rouvre toutes les fenêtres.", + "window.reopenFolders.folders": "Rouvrir tous les dossiers. Les espaces de travail vides ne seront pas restaurées.", + "window.reopenFolders.one": "Rouvre la dernière fenêtre active.", + "window.reopenFolders.none": "Ne jamais rouvrir de fenêtre. Toujours démarrer avec une fenêtre vide.", + "restoreWindows": "Contrôle comment les fenêtres seront rouvertes après un redémarrage. Sélectionner 'none' pour toujours démarrer avec un espace de travail vide, 'one' pour rouvrir la dernière fenêtre avec laquelle vous avez travaillé, 'folders' pour rouvrir toutes les fenêtres qui avaient des dossiers ouverts ou 'all' pour rouvrir toutes les fenêtres de votre dernière session.", + "restoreFullscreen": "Contrôle si une fenêtre doit être restaurée en mode plein écran si elle a été fermée dans ce mode.", + "zoomLevel": "Modifiez le niveau de zoom de la fenêtre. La taille d'origine est 0. Chaque incrément supérieur (exemple : 1) ou inférieur (exemple : -1) représente un zoom 20 % plus gros ou plus petit. Vous pouvez également entrer des décimales pour changer le niveau de zoom avec une granularité plus fine.", + "title": "Contrôle le titre de la fenêtre en fonction de l'éditeur actif. Les variables sont remplacées selon le contexte :\n${activeEditorShort} : par ex., myFile.txt\n${activeEditorMedium} : par ex., myFolder/myFile.txt\n${activeEditorLong} : par ex., /Users/Development/myProject/myFolder/myFile.txt\n${folderName} : par ex., myFolder\n${folderPath} : par ex., /Users/Development/myFolder\n${rootName} : par ex., myFolder1, myFolder2, myFolder3\n${rootPath} : par ex., /Users/Development/myWorkspace\n${appName} : par ex., VS Code\n${dirty} : indicateur d'intégrité si l'intégrité de l'éditeur actif est compromise\n${separator} : séparateur conditionnel (\" - \") qui s'affiche uniquement quand il est entouré de variables avec des valeurs", + "window.newWindowDimensions.default": "Permet d'ouvrir les nouvelles fenêtres au centre de l'écran.", + "window.newWindowDimensions.inherit": "Permet d'ouvrir les nouvelles fenêtres avec la même dimension que la dernière fenêtre active.", + "window.newWindowDimensions.maximized": "Permet d'ouvrir les nouvelles fenêtres de manière agrandie.", + "window.newWindowDimensions.fullscreen": "Permet d'ouvrir les nouvelles fenêtres en mode plein écran.", + "newWindowDimensions": "Contrôle les dimensions d'ouverture d'une nouvelle fenêtre quand au moins une fenêtre est déjà ouverte. Par défaut, une nouvelle fenêtre s'ouvre au centre de l'écran avec des dimensions réduites. Quand la valeur est 'inherit', la fenêtre a les mêmes dimensions que la dernière fenêtre active. Quand la valeur est 'maximized', la fenêtre s'ouvre dans sa taille maximale et quand la valeur est 'fullscreen', elle s'ouvre en mode plein écran. Notez que ce paramètre n'a aucun impact sur la première fenêtre ouverte, laquelle est toujours restaurée à la taille et l'emplacement définis au moment de sa fermeture.", + "closeWhenEmpty": "Contrôle si le dernier éditeur de clôture devrait également fermer la fenêtre. Ce paramètre s’applique uniquement pour les fenêtres qui ne présentent pas de dossiers.", + "window.menuBarVisibility.default": "Le menu n'est masqué qu'en mode plein écran.", + "window.menuBarVisibility.visible": "Le menu est toujours visible même en mode plein écran.", + "window.menuBarVisibility.toggle": "Le menu est masqué mais il peut être affiché via la touche Alt.", + "window.menuBarVisibility.hidden": "Le menu est toujours masqué.", + "menuBarVisibility": "Contrôle la visibilité de la barre de menus. Le paramètre 'toggle' signifie que la barre de menus est masquée, et qu'une seule pression sur la touche Alt permet de l'afficher. Par défaut, la barre de menus est visible, sauf si la fenêtre est en mode plein écran.", + "enableMenuBarMnemonics": "S'ils sont activés, les menus principaux peuvent être ouverts via des raccourcis avec la touche Alt. La désactivation des mnémoniques permet plutôt de lier ces raccourcis avec la touche Alt aux commandes de l'éditeur.", + "autoDetectHighContrast": "Si cette option est activée, le thème à contraste élevé est automatiquement choisi quand Windows utilise un thème à contraste élevé. À l'inverse, le thème sombre est automatiquement choisi quand Windows n'utilise plus le thème à contraste élevé.", + "titleBarStyle": "Ajustez l'apparence de la barre de titre de la fenêtre. Vous devez effectuer un redémarrage complet pour que les changements soient appliqués.", + "window.nativeTabs": "Active les onglets macOS Sierra. Notez que vous devez redémarrer l'ordinateur pour appliquer les modifications et que les onglets natifs désactivent tout style de barre de titre personnalisé configuré, le cas échéant.", + "windowConfigurationTitle": "Fenêtre", + "zenModeConfigurationTitle": "Mode Zen", + "zenMode.fullScreen": "Contrôle si l'activation de Zen Mode met également le banc d'essai en mode plein écran.", + "zenMode.hideTabs": "Contrôle si l'activation du mode Zen masque également les onglets du banc d'essai.", + "zenMode.hideStatusBar": "Contrôle si l'activation du mode Zen masque également la barre d'état au bas du banc d'essai.", + "zenMode.hideActivityBar": "Contrôle si l'activation du mode Zen masque également la barre d'activités à gauche du banc d'essai.", + "zenMode.restore": "Contrôle si une fenêtre doit être restaurée en mode zen, si elle a été fermée en mode zen." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/main.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..589a35f666 --- /dev/null +++ b/i18n/fra/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "Échec du chargement d'un fichier requis. Soit vous n'êtes plus connecté à Internet, soit le serveur auquel vous êtes connecté est hors connexion. Actualisez le navigateur pour réessayer.", + "loaderErrorNative": "Échec du chargement d'un fichier obligatoire. Redémarrez l'application pour réessayer. Détails : {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..f7c034043b --- /dev/null +++ b/i18n/fra/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "Il est déconseillé d'exécuter du code en tant qu'utilisateur 'root'." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/window.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..dfd038349f --- /dev/null +++ b/i18n/fra/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "Annuler", + "redo": "Rétablir", + "cut": "Couper", + "copy": "Copier", + "paste": "Coller", + "selectAll": "Tout Sélectionner" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..aaeaf3e6b7 --- /dev/null +++ b/i18n/fra/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "Développeur", + "file": "Fichier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/fra/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..518bfcf904 --- /dev/null +++ b/i18n/fra/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "Le chemin {0} ne pointe pas vers un Test Runner d'extension valide." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/fra/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..97f83d57ff --- /dev/null +++ b/i18n/fra/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Échec de l'analyse de {0} : {1}.", + "fileReadFail": "Impossible de lire le fichier {0} : {1}.", + "jsonsParseFail": "Échec de l'analyse de {0} ou de {1} : {2}.", + "missingNLSKey": "Le message est introuvable pour la clé {0}." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..f2f7f99980 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "Installer la commande '{0}' dans PATH", + "not available": "Cette commande n'est pas disponible", + "successIn": "La commande d'interpréteur de commandes '{0}' a été correctement installée dans PATH.", + "warnEscalation": "Code va maintenant demander avec 'osascript' des privilèges d'administrateur pour installer la commande d'interpréteur de commandes.", + "ok": "OK", + "cantCreateBinFolder": "Impossible de créer '/usr/local/bin'.", + "cancel2": "Annuler", + "aborted": "Abandonné", + "uninstall": "Désinstaller la commande '{0}' de PATH", + "successFrom": "La commande d'interpréteur de commandes '{0}' a été correctement désinstallée à partir de PATH.", + "shellCommand": "Commande d'interpréteur de commandes" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..2b87e3def9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "Définition du paramètre 'editor.accessibilitySupport' sur 'activé'.", + "openingDocs": "Ouverture de la page de documentation sur l'accessibilité dans VS Code.", + "introMsg": "Nous vous remercions de tester les options d'accessibilité de VS Code.", + "status": "État :", + "changeConfigToOnMac": "Pour configurer l'éditeur de sorte qu'il soit optimisé en permanence pour une utilisation avec un lecteur d'écran, appuyez sur Commande+E.", + "changeConfigToOnWinLinux": "Pour configurer l'éditeur de sorte qu'il soit optimisé en permanence pour une utilisation avec un lecteur d'écran, appuyez sur Ctrl+E.", + "auto_unknown": "L'éditeur est configuré pour utiliser les API de la plateforme afin de détecter si un lecteur d'écran est attaché, mais le runtime actuel ne prend pas en charge cette configuration.", + "auto_on": "L'éditeur a automatiquement détecté qu'un lecteur d'écran est attaché.", + "auto_off": "L'éditeur est configuré pour détecter automatiquement si un lecteur d'écran est attaché, ce qui n'est pas le cas pour le moment.", + "configuredOn": "L'éditeur est configuré de sorte qu'il soit optimisé en permanence pour une utilisation avec un lecteur d'écran. Vous pouvez changer ce comportement en modifiant le paramètre 'editor.accessibilitySupport'.", + "configuredOff": "L'éditeur est configuré de sorte à ne jamais être optimisé pour une utilisation avec un lecteur d'écran.", + "tabFocusModeOnMsg": "Appuyez sur Tab dans l'éditeur pour déplacer le focus vers le prochain élément pouvant être désigné comme élément actif. Activez ou désactivez ce comportement en appuyant sur {0}.", + "tabFocusModeOnMsgNoKb": "Appuyez sur Tab dans l'éditeur pour déplacer le focus vers le prochain élément pouvant être désigné comme élément actif. La commande {0} ne peut pas être déclenchée par une combinaison de touches.", + "tabFocusModeOffMsg": "Appuyez sur Tab dans l'éditeur pour insérer le caractère de tabulation. Activez ou désactivez ce comportement en appuyant sur {0}.", + "tabFocusModeOffMsgNoKb": "Appuyez sur Tab dans l'éditeur pour insérer le caractère de tabulation. La commande {0} ne peut pas être déclenchée par une combinaison de touches.", + "openDocMac": "Appuyez sur Commande+H pour ouvrir une fenêtre de navigateur contenant plus d'informations sur l'accessibilité dans VS Code.", + "openDocWinLinux": "Appuyez sur Ctrl+H pour ouvrir une fenêtre de navigateur contenant plus d'informations sur l'accessibilité dans VS Code.", + "outroMsg": "Vous pouvez masquer cette info-bulle et revenir à l'éditeur en appuyant sur Échap ou Maj+Échap.", + "ShowAccessibilityHelpAction": "Afficher l'aide sur l'accessibilité" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..3926781f8b --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "Développeur : Inspecter les mappages de touches" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f9ee0497f5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Développeur : inspecter les portées TextMate", + "inspectTMScopesWidget.loading": "Chargement..." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..ad98e260ad --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Erreurs durant l'analyse de {0} : {1}", + "schema.openBracket": "Séquence de chaînes ou de caractères de crochets ouvrants.", + "schema.closeBracket": "Séquence de chaînes ou de caractères de crochets fermants.", + "schema.comments": "Définit les symboles de commentaire", + "schema.blockComments": "Définit le marquage des commentaires de bloc.", + "schema.blockComment.begin": "Séquence de caractères au début d'un commentaire de bloc.", + "schema.blockComment.end": "Séquence de caractères à la fin d'un commentaire de bloc.", + "schema.lineComment": "Séquence de caractères au début d'un commentaire de ligne.", + "schema.brackets": "Définit les symboles de type crochet qui augmentent ou diminuent le retrait.", + "schema.autoClosingPairs": "Définit les paires de crochets. Quand vous entrez un crochet ouvrant, le crochet fermant est inséré automatiquement.", + "schema.autoClosingPairs.notIn": "Définit une liste d'étendues où les paires automatiques sont désactivées.", + "schema.surroundingPairs": "Définit les paires de crochets qui peuvent être utilisées pour entourer la chaîne sélectionnée.", + "schema.wordPattern": "La définition du mot dans le langage", + "schema.wordPattern.pattern": "L'expression régulière utilisée pour la recherche", + "schema.wordPattern.flags": "Les options d'expression régulière utilisées pour la recherche", + "schema.wordPattern.flags.errorMessage": "Doit valider l'expression régulière `/^([gimuy]+)$/`.", + "schema.indentationRules": "Paramètres de mise en retrait du langage.", + "schema.indentationRules.increaseIndentPattern": "Si une ligne correspond à ce modèle, toutes les lignes qui la suivent doivent être mises en retrait une fois (jusqu'à ce qu'une autre règle corresponde).", + "schema.indentationRules.increaseIndentPattern.pattern": "Modèle RegExp pour increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.flags": "Indicateurs RegExp pour increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.errorMessage": "Doit valider l'expression régulière `/^([gimuy]+)$/`.", + "schema.indentationRules.decreaseIndentPattern": "Si une ligne correspond à ce modèle, pour toutes les lignes qui la suivent le retrait doit être réduit une fois (jusqu'à ce qu'une autre règle corresponde).", + "schema.indentationRules.decreaseIndentPattern.pattern": "Modèle RegExp pour decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.flags": "Indicateurs RegExp pour decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "Doit valider l'expression régulière `/^([gimuy]+)$/`.", + "schema.indentationRules.indentNextLinePattern": "Si une ligne correspond à ce modèle, **seule la ligne suivante** doit être mise en retrait une fois.", + "schema.indentationRules.indentNextLinePattern.pattern": "Modèle RegExp pour indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.flags": "Indicateurs RegExp pour indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.errorMessage": "Doit valider l'expression régulière `/^([gimuy]+)$/`.", + "schema.indentationRules.unIndentedLinePattern": "Si une ligne correspond à ce modèle, sa mise en retrait ne doit pas être changée et la ligne ne doit pas être évaluée par rapport aux autres règles.", + "schema.indentationRules.unIndentedLinePattern.pattern": "Modèle RegExp pour unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.flags": "Indicateurs RegExp pour unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "Doit valider l'expression régulière `/^([gimuy]+)$/`." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f9ee0497f5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Développeur : inspecter les portées TextMate", + "inspectTMScopesWidget.loading": "Chargement..." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..a9b4b840b8 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "Affichage : Activer/désactiver la minicarte" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..bf8a0c1179 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Changer le modificateur multicurseur" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..12f5b80c9d --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "Affichage : Activer/désactiver les caractères de contrôle" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..1550b65805 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "Affichage : Activer/désactiver l'affichage des espaces" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..e6f0d0eee1 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "Afficher : activer/désactiver le retour automatique à la ligne", + "wordWrap.notInDiffEditor": "Impossible d'activer/désactiver le retour automatique à la ligne dans un éditeur de différences.", + "unwrapMinified": "Désactiver le retour automatique à la ligne pour ce fichier", + "wrapMinified": "Activer le retour à la ligne pour ce fichier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..6371dfb87a --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "OK", + "wordWrapMigration.dontShowAgain": "Ne plus afficher", + "wordWrapMigration.openSettings": "Ouvrir les paramètres", + "wordWrapMigration.prompt": "Le paramètre 'editor.wrappingColumn' est déconseillé et doit être remplacé par 'editor.wordWrap'." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..2974a9d196 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "Arrêt quand l'expression prend la valeur true. 'Entrée' pour accepter ou 'Échap' pour annuler.", + "breakpointWidgetAriaLabel": "Le programme s'arrête ici uniquement si cette condition a la valeur true. Appuyez sur Entrée pour accepter, ou sur Échap pour annuler.", + "breakpointWidgetHitCountPlaceholder": "Arrêt quand le nombre d'accès est atteint. 'Entrée' pour accepter ou 'Échap' pour annuler.", + "breakpointWidgetHitCountAriaLabel": "Le programme s'arrête ici uniquement si le nombre d'accès est atteint. Appuyez sur Entrée pour accepter, ou sur Échap pour annuler.", + "expression": "Expression", + "hitCount": "Nombre d'accès" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..7b3be6c136 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "Aucune configuration", + "addConfigTo": "Ajouter une configuration ({0})...", + "addConfiguration": "Ajouter une configuration..." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..7b880ca253 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "Ouvrir {0}", + "launchJsonNeedsConfigurtion": "Configurer ou corriger 'launch.json'", + "noFolderDebugConfig": "Ouvrez d'abord un dossier pour effectuer une configuration de débogage avancée.", + "startDebug": "Démarrer le débogage", + "startWithoutDebugging": "Exécuter sans débogage", + "selectAndStartDebugging": "Sélectionner et démarrer le débogage", + "restartDebug": "Redémarrer", + "reconnectDebug": "Se reconnecter", + "stepOverDebug": "Pas à pas principal", + "stepIntoDebug": "Pas à pas détaillé", + "stepOutDebug": "Pas à pas sortant", + "stopDebug": "Arrêter", + "disconnectDebug": "Déconnecter", + "continueDebug": "Continuer", + "pauseDebug": "Suspendre", + "restartFrame": "Redémarrer le frame", + "removeBreakpoint": "Supprimer un point d'arrêt", + "removeAllBreakpoints": "Supprimer tous les points d'arrêt", + "enableBreakpoint": "Activer le point d'arrêt", + "disableBreakpoint": "Désactiver le point d'arrêt", + "enableAllBreakpoints": "Activer tous les points d'arrêt", + "disableAllBreakpoints": "Désactiver tous les points d'arrêt", + "activateBreakpoints": "Activer les points d'arrêt", + "deactivateBreakpoints": "Désactiver les points d'arrêt", + "reapplyAllBreakpoints": "Réappliquer tous les points d'arrêt", + "addFunctionBreakpoint": "Ajouter un point d'arrêt sur fonction", + "renameFunctionBreakpoint": "Renommer un point d'arrêt sur fonction", + "addConditionalBreakpoint": "Ajouter un point d'arrêt conditionnel...", + "editConditionalBreakpoint": "Modifier un point d'arrêt...", + "setValue": "Définir la valeur", + "addWatchExpression": "Ajouter une expression", + "editWatchExpression": "Modifier l'expression", + "addToWatchExpressions": "Ajouter à la fenêtre Espion", + "removeWatchExpression": "Supprimer une expression", + "removeAllWatchExpressions": "Supprimer toutes les expressions", + "clearRepl": "Effacer la console", + "debugConsoleAction": "Console de débogage", + "unreadOutput": "Nouvelle sortie dans la console de débogage", + "debugFocusConsole": "Focus sur la console de débogage", + "focusProcess": "Focus du processus", + "stepBackDebug": "Revenir en arrière", + "reverseContinue": "Inverser" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..a8695ad48e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "Couleur d'arrière-plan de la barre d'outils de débogage." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..8adb4b80c8 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "Impossible de résoudre la ressource sans session de débogage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..f208b931d8 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "Déboguer : activer/désactiver un point d'arrêt", + "columnBreakpointAction": "Déboguer : point d'arrêt de colonne", + "columnBreakpoint": "Ajouter un point d'arrêt de colonne", + "conditionalBreakpointEditorAction": "Déboguer : ajouter un point d'arrêt conditionnel...", + "runToCursor": "Exécuter jusqu'au curseur", + "debugEvaluate": "Déboguer : évaluer", + "debugAddToWatch": "Déboguer : ajouter à la fenêtre Espion", + "showDebugHover": "Déboguer : afficher par pointage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..bae02d3806 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "Point d'arrêt désactivé", + "breakpointUnverifieddHover": "Point d'arrêt non vérifié", + "breakpointDirtydHover": "Point d'arrêt non vérifié. Fichier modifié. Redémarrez la session de débogage.", + "breakpointUnsupported": "Les points d'arrêt conditionnels ne sont pas pris en charge par ce type de débogage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..57dc0c7aa4 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, débogage", + "debugAriaLabel": "Tapez le nom d'une configuration de lancement à exécuter.", + "noConfigurationsMatching": "Aucune configuration de débogage correspondante", + "noConfigurationsFound": "Configurations de débogage introuvables. Créez un fichier 'launch.json'." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..2dc00a2e02 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "Couleur de bordure du widget d'exception.", + "debugExceptionWidgetBackground": "Couleur d'arrière-plan du widget d'exception.", + "exceptionThrownWithId": "Une exception s'est produite : {0}", + "exceptionThrown": "Une exception s'est produite" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..0c33b4acff --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "Cliquez pour suivre (Commande + clic permet d'ouvrir sur le côté)", + "fileLink": "Cliquez pour suivre (Ctrl + clic permet d'ouvrir sur le côté)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..1d2f7bf72c --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "Contrôle le comportement de la console de débogage interne." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..fa914316af --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "non disponible", + "startDebugFirst": "Démarrez une session de débogage pour évaluation" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..c3c798fa13 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "Source inconnue" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..d33878c485 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "Afficher le débogage", + "toggleDebugPanel": "Console de débogage", + "debug": "Déboguer", + "debugPanel": "Console de débogage", + "variables": "Variables", + "watch": "Espion", + "callStack": "Pile des appels", + "breakpoints": "Points d'arrêt", + "view": "Affichage", + "debugCategory": "Déboguer", + "debugCommands": "Configuration de débogage", + "debugConfigurationTitle": "Déboguer", + "allowBreakpointsEverywhere": "Permet de définir un point d'arrêt dans un fichier", + "openExplorerOnEnd": "Ouvrir automatiquement le mode explorateur à la fin d'une session de débogage", + "inlineValues": "Afficher les valeurs des variables inline dans l'éditeur pendant le débogage", + "hideActionBar": "Contrôle si la barre d'action de débogage flottante doit être masquée", + "launch": "Configuration du lancement du débogage global. Doit être utilisée comme alternative à 'launch.json' qui est partagé entre les espaces de travail" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..84265ffbbe --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "Ouvrez d'abord un dossier pour effectuer une configuration de débogage avancée." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..713ba57ef6 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "Ajoute des adaptateurs de débogage.", + "vscode.extension.contributes.debuggers.type": "Identificateur unique de cet adaptateur de débogage.", + "vscode.extension.contributes.debuggers.label": "Nom complet de cet adaptateur de débogage.", + "vscode.extension.contributes.debuggers.program": "Chemin du programme de l'adaptateur de débogage. Le chemin est absolu ou relatif par rapport au dossier d'extensions.", + "vscode.extension.contributes.debuggers.args": "Arguments facultatifs à passer à l'adaptateur.", + "vscode.extension.contributes.debuggers.runtime": "Runtime facultatif, si l'attribut de programme n'est pas un exécutable, mais qu'il nécessite un exécutable.", + "vscode.extension.contributes.debuggers.runtimeArgs": "Arguments du runtime facultatif.", + "vscode.extension.contributes.debuggers.variables": "Mappage à partir de variables interactives (par exemple, ${action.pickProcess}) dans 'launch.json' vers une commande.", + "vscode.extension.contributes.debuggers.initialConfigurations": "Configurations pour la génération du fichier 'launch.json' initial.", + "vscode.extension.contributes.debuggers.languages": "Liste de langages pour lesquels l'extension de débogage peut être considérée comme \"débogueur par défaut\".", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Si l'extension VS Code spécifiée appelle cette commande pour déterminer le chemin de l'exécutable de l'adaptateur de débogage et les arguments à passer.", + "vscode.extension.contributes.debuggers.startSessionCommand": "Si l'extension VS Code spécifiée appelle cette commande pour les actions \"debug\" ou \"run\" ciblées pour cette extension.", + "vscode.extension.contributes.debuggers.configurationSnippets": "Extraits pour l'ajout de nouvelles configurations à 'launch.json'.", + "vscode.extension.contributes.debuggers.configurationAttributes": "Configurations de schéma JSON pour la validation de 'launch.json'.", + "vscode.extension.contributes.debuggers.windows": "Paramètres spécifiques à Windows.", + "vscode.extension.contributes.debuggers.windows.runtime": "Runtime utilisé pour Windows.", + "vscode.extension.contributes.debuggers.osx": "Paramètres spécifiques à OS X.", + "vscode.extension.contributes.debuggers.osx.runtime": "Runtime utilisé pour OS X.", + "vscode.extension.contributes.debuggers.linux": "Paramètres spécifiques à Linux.", + "vscode.extension.contributes.debuggers.linux.runtime": "Runtime utilisé pour Linux.", + "vscode.extension.contributes.breakpoints": "Ajoute des points d'arrêt.", + "vscode.extension.contributes.breakpoints.language": "Autorisez les points d'arrêt pour ce langage.", + "app.launch.json.title": "Lancer", + "app.launch.json.version": "Version de ce format de fichier.", + "app.launch.json.configurations": "Liste des configurations. Ajoutez de nouvelles configurations, ou modifiez celles qui existent déjà à l'aide d'IntelliSense.", + "app.launch.json.compounds": "Liste des composés. Chaque composé référence plusieurs configurations qui sont lancées ensemble.", + "app.launch.json.compound.name": "Nom du composé. Apparaît dans le menu déroulant de la configuration de lancement.", + "app.launch.json.compounds.configurations": "Noms des configurations qui sont lancées dans le cadre de ce composé.", + "debugNoType": "Le 'type' de l'adaptateur de débogage ne peut pas être omis. Il doit s'agir du type 'string'.", + "selectDebug": "Sélectionner l'environnement", + "DebugConfig.failed": "Impossible de créer le fichier 'launch.json' dans le dossier '.vscode' ({0})." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..38ae422233 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "Supprimer les points d'arrêt", + "removeBreakpointOnColumn": "Supprimer le point d'arrêt de la colonne {0}", + "removeLineBreakpoint": "Supprimer le point d'arrêt de la ligne", + "editBreakpoints": "Modifier les points d'arrêt", + "editBreakpointOnColumn": "Modifier le point d'arrêt de la colonne {0}", + "editLineBrekapoint": "Modifier le point d'arrêt de la ligne", + "enableDisableBreakpoints": "Activer/désactiver les points d'arrêt", + "disableColumnBreakpoint": "Désactiver le point d'arrêt de la colonne {0}", + "disableBreakpointOnLine": "Désactiver le point d'arrêt de la ligne", + "enableBreakpoints": "Activer le point d'arrêt de la colonne {0}", + "enableBreakpointOnLine": "Activer le point d'arrêt de la ligne", + "addBreakpoint": "Ajouter un point d'arrêt", + "addConfiguration": "Ajouter une configuration..." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..1bc00f393e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "Déboguer par pointage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..3945a227e7 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "Seules les valeurs primitives sont affichées pour cet objet.", + "debuggingPaused": "Débogage en pause. Raison : {0}, {1} {2}", + "debuggingStarted": "Débogage démarré.", + "debuggingStopped": "Débogage arrêté.", + "breakpointAdded": "Point d'arrêt ajouté, ligne {0}, fichier {1}", + "breakpointRemoved": "Point d'arrêt supprimé, ligne {0}, fichier {1}", + "compoundMustHaveConfigurations": "L'attribut \"configurations\" du composé doit être défini pour permettre le démarrage de plusieurs configurations.", + "configMissing": "Il manque la configuration '{0}' dans 'launch.json'.", + "debugTypeNotSupported": "Le type de débogage '{0}' configuré n'est pas pris en charge.", + "debugTypeMissing": "Propriété 'type' manquante pour la configuration de lancement choisie.", + "preLaunchTaskErrors": "Des erreurs de build ont été détectées durant le preLaunchTask '{0}'.", + "preLaunchTaskError": "Une erreur de build a été détectée durant le preLaunchTask '{0}'.", + "preLaunchTaskExitCode": "Le preLaunchTask '{0}' s'est terminé avec le code de sortie {1}.", + "debugAnyway": "Déboguer quand même", + "noFolderWorkspaceDebugError": "Impossible de déboguer le fichier actif. Vérifiez qu'il est enregistré sur le disque et qu'une extension de débogage est installée pour ce type de fichier.", + "NewLaunchConfig": "Configurez le fichier config de lancement de votre application. {0}", + "DebugTaskNotFound": "preLaunchTask '{0}' introuvable." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..59da542747 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "Processus", + "paused": "Suspendu", + "running": "En cours d'exécution", + "thread": "Thread", + "pausedOn": "En pause sur {0}", + "loadMoreStackFrames": "Charger plus de frames de pile", + "threadAriaLabel": "Thread {0}, pile des appels, débogage", + "stackFrameAriaLabel": "Frame de pile {0}, ligne {1} {2}, pile des appels, débogage", + "variableValueAriaLabel": "Tapez une nouvelle valeur de variable", + "variableScopeAriaLabel": "Portée {0}, variables, débogage", + "variableAriaLabel": "{0} valeur {1}, variables, débogage", + "watchExpressionPlaceholder": "Expression à espionner", + "watchExpressionInputAriaLabel": "Tapez l'expression à espionner", + "watchExpressionAriaLabel": "{0} valeur {1}, espion, débogage", + "watchVariableAriaLabel": "{0} valeur {1}, espion, débogage", + "functionBreakpointPlaceholder": "Fonction où effectuer un point d'arrêt", + "functionBreakPointInputAriaLabel": "Point d'arrêt sur fonction de type", + "functionBreakpointsNotSupported": "Les points d'arrêt de fonction ne sont pas pris en charge par ce type de débogage", + "breakpointAriaLabel": "Ligne de point d'arrêt {0} {1}, points d'arrêt, débogage", + "functionBreakpointAriaLabel": "Point d'arrêt sur fonction {0}, points d'arrêt, débogage", + "exceptionBreakpointAriaLabel": "Point d'arrêt d'exception {0}, points d'arrêt, débogage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..cb2a04e66e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "Section des variables", + "variablesAriaTreeLabel": "Déboguer les variables", + "expressionsSection": "Section des expressions", + "watchAriaTreeLabel": "Déboguer les expressions espionnées", + "callstackSection": "Section de pile des appels", + "debugStopped": "En pause sur {0}", + "callStackAriaLabel": "Déboguer la pile des appels", + "breakpointsSection": "Section des points d'arrêt", + "breakpointsAriaTreeLabel": "Déboguer les points d'arrêt" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..1344e863e9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "Copier la valeur", + "copy": "Copier", + "copyAll": "Copier tout", + "copyStackTrace": "Copier la pile des appels" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..be3d529e65 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "Informations", + "unableToLaunchDebugAdapter": "Impossible de lancer l'adaptateur de débogage à partir de '{0}'.", + "unableToLaunchDebugAdapterNoArgs": "Impossible de lancer l'adaptateur de débogage.", + "stoppingDebugAdapter": "{0}. Arrêt de l'adaptateur de débogage.", + "debugAdapterCrash": "Le processus de l'adaptateur de débogage s'est terminé de manière inattendue" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..d5dfb7eb5f --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "Panneau REPL (Read Eval Print Loop)", + "actions.repl.historyPrevious": "Historique précédent", + "actions.repl.historyNext": "Historique suivant", + "actions.repl.acceptInput": "Accepter l'entrée REPL", + "actions.repl.copyAll": "Débogage : Tout copier (console)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..1ff0167de1 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "L'état de l'objet est capturé à partir de la première évaluation", + "replVariableAriaLabel": "La variable {0} a la valeur {1}, boucle REPL (Read Eval Print Loop), débogage", + "replExpressionAriaLabel": "L'expression {0} a la valeur {1}, boucle REPL (Read Eval Print Loop), débogage", + "replValueOutputAriaLabel": "{0}, boucle REPL (Read Eval Print Loop), débogage", + "replKeyValueOutputAriaLabel": "La variable de sortie {0} a la valeur {1}, boucle REPL (Read Eval Print Loop), débogage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..1a943886d5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "Couleur d'arrière-plan de la barre d'état quand un programme est en cours de débogage. La barre d'état est affichée en bas de la fenêtre", + "statusBarDebuggingForeground": "Couleur de premier plan de la barre d'état quand un programme est en cours de débogage. La barre d'état est affichée en bas de la fenêtre", + "statusBarDebuggingBorder": "Couleur de la bordure qui sépare à l’éditeur et la barre latérale quand un programme est en cours de débogage. La barre d’état s’affiche en bas de la fenêtre" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..206ad78221 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "élément débogué", + "debug.terminal.not.available.error": "Terminal intégré non disponible" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..f834a88bda --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "L'exécutable d'adaptateur de débogage '{0}' n'existe pas.", + "debugAdapterCannotDetermineExecutable": "Impossible de déterminer l'exécutable pour l'adaptateur de débogage '{0}'.", + "launch.config.comment1": "Utilisez IntelliSense pour en savoir plus sur les attributs possibles.", + "launch.config.comment2": "Pointez pour afficher la description des attributs existants.", + "launch.config.comment3": "Pour plus d'informations, visitez : {0}", + "debugType": "Type de configuration.", + "debugTypeNotRecognised": "Le type de débogage n'est pas reconnu. Vérifiez que vous avez installé l'extension de débogage correspondante et qu'elle est activée.", + "node2NotSupported": "\"node2\" n'est plus pris en charge. Utilisez \"node\" à la place, et affectez la valeur \"inspector\" à l'attribut \"protocol\".", + "debugName": "Le nom de la configuration s'affiche dans le menu déroulant de la configuration de lancement.", + "debugRequest": "Type de requête de configuration. Il peut s'agir de \"launch\" ou \"attach\".", + "debugServer": "Pour le développement d'une extension de débogage uniquement : si un port est spécifié, VS Code tente de se connecter à un adaptateur de débogage s'exécutant en mode serveur", + "debugPrelaunchTask": "Tâche à exécuter avant le démarrage de la session de débogage.", + "debugWindowsConfiguration": "Attributs de configuration de lancement spécifiques à Windows.", + "debugOSXConfiguration": "Attributs de configuration de lancement spécifiques à OS X.", + "debugLinuxConfiguration": "Attributs de configuration de lancement spécifiques à Linux.", + "deprecatedVariables": "env.', 'config.' et 'command.' sont déconseillés. Utilisez 'env:', 'config:' et 'command:' à la place." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..de0e2e3de9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "Afficher les commandes Emmet" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..3ccee69a06 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet : Balance (inward)", + "balanceOutward": "Emmet : Balance (outward)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..631dc3b5db --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet : Go to Previous Edit Point", + "nextEditPoint": "Emmet : Go to Next Edit Point" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..b6e212d1aa --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet : Evaluate Math Expression" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..7cbfeb6951 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet : Expand Abbreviation" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..2dc3d0b84d --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet : Increment by 0.1", + "incrementNumberByOne": "Emmet : Increment by 1", + "incrementNumberByTen": "Emmet : Increment by 10", + "decrementNumberByOneTenth": "Emmet : Decrement by 0.1", + "decrementNumberByOne": "Emmet : Decrement by 1", + "decrementNumberByTen": "Emmet : Decrement by 10" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..de012a3457 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet : Go to Matching Pair" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..385dc8d754 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet : Merge Lines" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..b7bc1e213d --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet : Reflect CSS Value" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..28c761596c --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet : Remove Tag" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..4fc4fe76d3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet : Select Previous Item", + "selectNextItem": "Emmet : Select Next Item" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..46592a58fe --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet : Split/Join Tag" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..7ca839a21b --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet : Toggle Comment" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..b4615b9482 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet : Update Image Size" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..788646c1e1 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet : Update Tag", + "enterTag": "Entrer une balise", + "tag": "Balise" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..5283af60fd --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet : Wrap with Abbreviation", + "enterAbbreviation": "Entrer une abréviation", + "abbreviation": "Abréviation" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..a6e6ba1831 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Une fois les abréviations Emmet activées, elles se développent quand vous appuyez sur la touche Tab. Non applicable quand emmet.useNewemmet est défini sur true.", + "emmetPreferences": "Préférences utilisées pour modifier le comportement de certaines actions et résolutions d'Emmet. Non applicable quand emmet.useNewemmet est défini sur true.", + "emmetSyntaxProfiles": "Définissez le profil pour la syntaxe spécifiée ou utilisez votre propre profil avec des règles spécifiques.", + "emmetExclude": "Ensemble de langages où les abréviations emmet ne doivent pas être développées.", + "emmetExtensionsPath": "Chemin d'un dossier contenant les profils, extraits de code et préférences Emmet. Seuls les profils sont honorés à partir du chemin des extensions quand emmet.useNewEmmet est défini sur true.", + "useNewEmmet": "Essayez les nouveaux modules Emmet (qui remplaceront à terme l'ancienne bibliothèque Emmet) pour toutes les fonctionnalités Emmet." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json new file mode 100644 index 0000000000..3ccee69a06 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet : Balance (inward)", + "balanceOutward": "Emmet : Balance (outward)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json new file mode 100644 index 0000000000..f8c2fad2a3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet : Previous Edit Point", + "nextEditPoint": "Emmet : Next Edit Point" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..b6e212d1aa --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet : Evaluate Math Expression" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..7cbfeb6951 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet : Expand Abbreviation" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..2dc3d0b84d --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet : Increment by 0.1", + "incrementNumberByOne": "Emmet : Increment by 1", + "incrementNumberByTen": "Emmet : Increment by 10", + "decrementNumberByOneTenth": "Emmet : Decrement by 0.1", + "decrementNumberByOne": "Emmet : Decrement by 1", + "decrementNumberByTen": "Emmet : Decrement by 10" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..de012a3457 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet : Go to Matching Pair" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..385dc8d754 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet : Merge Lines" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..b7bc1e213d --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet : Reflect CSS Value" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json new file mode 100644 index 0000000000..28c761596c --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet : Remove Tag" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json new file mode 100644 index 0000000000..4fc4fe76d3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet : Select Previous Item", + "selectNextItem": "Emmet : Select Next Item" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..46592a58fe --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet : Split/Join Tag" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..7ca839a21b --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet : Toggle Comment" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..b4615b9482 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet : Update Image Size" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json new file mode 100644 index 0000000000..788646c1e1 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet : Update Tag", + "enterTag": "Entrer une balise", + "tag": "Balise" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..5283af60fd --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet : Wrap with Abbreviation", + "enterAbbreviation": "Entrer une abréviation", + "abbreviation": "Abréviation" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json new file mode 100644 index 0000000000..786229663b --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Une fois les abréviations Emmet activées, elles se développent quand vous appuyez sur la touche Tab.", + "emmetPreferences": "Préférences utilisées pour modifier le comportement de certaines actions et résolveurs d'Emmet.", + "emmetSyntaxProfiles": "Définissez le profil pour la syntaxe spécifiée ou utilisez votre propre profil avec des règles spécifiques.", + "emmetExclude": "Ensemble de langages où les abréviations emmet ne doivent pas être développées.", + "emmetExtensionsPath": "Chemin d'un dossier contenant les profils, extraits et préférences Emmet" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..2ef0888c5b --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Terminal externe", + "explorer.openInTerminalKind": "Personnalise le type de terminal à lancer.", + "terminal.external.windowsExec": "Personnalise le terminal à exécuter sur Windows.", + "terminal.external.osxExec": "Personnalise l'application de terminal à exécuter sur OS X.", + "terminal.external.linuxExec": "Personnalise le terminal à exécuter sur Linux.", + "globalConsoleActionWin": "Ouvrir une nouvelle invite de commandes", + "globalConsoleActionMacLinux": "Ouvrir un nouveau Terminal", + "scopedConsoleActionWin": "Ouvrir dans l'invite de commandes", + "scopedConsoleActionMacLinux": "Ouvrir dans Terminal", + "openFolderInIntegratedTerminal": "Ouvrir dans Terminal" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..d3bfab7d5c --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Terminal externe", + "terminal.external.windowsExec": "Personnalise le terminal à exécuter sur Windows.", + "terminal.external.osxExec": "Personnalise l'application de terminal à exécuter sur OS X.", + "terminal.external.linuxExec": "Personnalise le terminal à exécuter sur Linux.", + "globalConsoleActionWin": "Ouvrir une nouvelle invite de commandes", + "globalConsoleActionMacLinux": "Ouvrir un nouveau Terminal", + "scopedConsoleActionWin": "Ouvrir dans l'invite de commandes", + "scopedConsoleActionMacLinux": "Ouvrir dans Terminal" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/fra/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..918ef44421 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "Console VS Code", + "mac.terminal.script.failed": "Échec du script '{0}'. Code de sortie : {1}", + "mac.terminal.type.not.supported": "'{0}' non pris en charge", + "press.any.key": "Appuyez sur une touche pour continuer...", + "linux.term.failed": "Échec de '{0}'. Code de sortie : {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..118a4b1403 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "Ajoute une vue personnalisée", + "vscode.extension.contributes.view.id": "ID unique utilisé pour identifier la vue créée avec vscode.workspace.createTreeView", + "vscode.extension.contributes.view.label": "Chaîne contrôlable de visu permettant d'afficher la vue", + "vscode.extension.contributes.view.icon": "Chemin de l'icône de la vue", + "vscode.extension.contributes.views": "Ajoute des vues personnalisées", + "showViewlet": "Afficher {0}", + "view": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..56ee97c726 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "Actualiser" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..39bde14e36 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "Aucun TreeExplorerNodeProvider ayant l'ID {providerId} n'est inscrit." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/fra/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..4f53cf19b7 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "Section Tree Explorer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..f29bd90fd1 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "Erreur", + "Unknown Dependency": "Dépendance inconnue :" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..92daefdec9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "Nom de l'extension", + "extension id": "Identificateur d'extension", + "publisher": "Nom de l'éditeur", + "install count": "Nombre d'installations", + "rating": "Évaluation", + "license": "Licence", + "details": "Détails", + "contributions": "Contributions", + "changelog": "Journal des modifications", + "dependencies": "Dépendances", + "noReadme": "Aucun fichier README disponible.", + "noChangelog": "Aucun Changelog disponible.", + "noContributions": "Aucune contribution", + "noDependencies": "Aucune dépendance", + "settings": "Paramètres ({0})", + "setting name": "Nom", + "description": "Description", + "default": "Par défaut", + "debuggers": "Débogueurs ({0})", + "debugger name": "Nom", + "debugger type": "Type", + "views": "Vues ({0})", + "view id": "ID", + "view name": "Nom", + "view location": "Emplacement", + "themes": "Thèmes ({0})", + "JSON Validation": "Validation JSON ({0})", + "commands": "Commandes ({0})", + "command name": "Nom", + "keyboard shortcuts": "Raccourcis clavier", + "menuContexts": "Contextes de menu", + "languages": "Langages ({0})", + "language id": "ID", + "language name": "Nom", + "file extensions": "Extensions de fichier", + "grammar": "Grammaire", + "snippets": "Extraits" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..2426b2a8d3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "Installer", + "installing": "Installation", + "uninstallAction": "Désinstaller", + "Uninstalling": "Désinstallation en cours", + "updateAction": "Mettre à jour", + "updateTo": "Mettre à jour vers {0}", + "enableForWorkspaceAction.label": "Activer (espace de travail)", + "enableAlwaysAction.label": "Activer (toujours)", + "disableForWorkspaceAction.label": "Désactiver (espace de travail)", + "disableAlwaysAction.label": "Désactiver (toujours)", + "ManageExtensionAction.uninstallingTooltip": "Désinstallation en cours", + "enableForWorkspaceAction": "Espace de travail", + "enableGloballyAction": "Toujours", + "enableAction": "Activer", + "disableForWorkspaceAction": "Espace de travail", + "disableGloballyAction": "Toujours", + "disableAction": "Désactiver", + "checkForUpdates": "Rechercher les mises à jour", + "enableAutoUpdate": "Activer la mise à jour automatique des extensions", + "disableAutoUpdate": "Désactiver la mise à jour automatique des extensions", + "updateAll": "Mettre à jour toutes les extensions", + "reloadAction": "Recharger", + "postUpdateTooltip": "Recharger pour mettre à jour", + "postUpdateMessage": "Recharger cette fenêtre pour activer l'extension mise à jour '{0}' ?", + "postEnableTooltip": "Recharger pour activer", + "postEnableMessage": "Recharger cette fenêtre pour activer l'extension '{0}' ?", + "postDisableTooltip": "Recharger pour désactiver", + "postDisableMessage": "Recharger cette fenêtre pour désactiver l'extension '{0}' ?", + "postUninstallTooltip": "Recharger pour désactiver", + "postUninstallMessage": "Recharger cette fenêtre pour désactiver l'extension désinstallée '{0}' ?", + "reload": "&&Recharger la fenêtre", + "toggleExtensionsViewlet": "Afficher les extensions", + "installExtensions": "Installer les extensions", + "showEnabledExtensions": "Afficher les Extensions activées", + "showInstalledExtensions": "Afficher les extensions installées", + "showDisabledExtensions": "Afficher les extensions désactivées", + "clearExtensionsInput": "Effacer l'entrée des extensions", + "showOutdatedExtensions": "Afficher les extensions obsolètes", + "showPopularExtensions": "Afficher les extensions les plus demandées", + "showRecommendedExtensions": "Afficher les extensions recommandées", + "showWorkspaceRecommendedExtensions": "Afficher les extensions recommandées pour l'espace de travail", + "showRecommendedKeymapExtensions": "Afficher les mappages de touches recommandés", + "showRecommendedKeymapExtensionsShort": "Mappages de touches", + "showLanguageExtensions": "Afficher les extensions de langage", + "showLanguageExtensionsShort": "Extensions de langage", + "showAzureExtensions": "Afficher les Extensions Azure", + "showAzureExtensionsShort": "Extensions Azure", + "configureWorkspaceRecommendedExtensions": "Configurer les extensions recommandées (espace de travail)", + "ConfigureWorkspaceRecommendations.noWorkspace": "Les recommandations ne sont disponibles que pour un dossier d'espace de travail.", + "OpenExtensionsFile.failed": "Impossible de créer le fichier 'extensions.json' dans le dossier '.vscode' ({0}).", + "builtin": "Intégrée", + "disableAll": "Désactiver toutes les extensions installées", + "disableAllWorkspace": "Désactiver toutes les extensions installées pour cet espace de travail", + "enableAll": "Activer toutes les extensions installées", + "enableAllWorkspace": "Activer toutes les extensions installées pour cet espace de travail", + "extensionButtonProminentBackground": "Couleur d'arrière-plan du bouton pour les extension d'actions importantes (par ex., le bouton d'installation).", + "extensionButtonProminentForeground": "Couleur d'arrière-plan du bouton pour l'extension d'actions importantes (par ex., le bouton d'installation).", + "extensionButtonProminentHoverBackground": "Couleur d'arrière-plan du pointage de bouton pour l'extension d'actions importantes (par ex., le bouton d'installation)." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..cf34b1e6a7 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "Appuyez sur Entrée pour gérer vos extensions.", + "searchFor": "Appuyez sur Entrée pour rechercher '{0}' dans le Marketplace.", + "noExtensionsToInstall": "Tapez un nom d'extension" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..6d70b61eea --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "Extensions", + "app.extensions.json.recommendations": "Liste des recommandations d'extensions. L'identificateur d'une extension est toujours '${publisher}.${name}'. Exemple : 'vscode.csharp'.", + "app.extension.identifier.errorMessage": "Format attendu : '${publisher}.${name}'. Exemple : 'vscode.csharp'." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..433dfeb7a5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "Extension : {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..98da6753e1 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "L'extension '{0}' est recommandée pour ce type de fichier.", + "showRecommendations": "Afficher les recommandations", + "neverShowAgain": "Ne plus afficher", + "close": "Fermer", + "workspaceRecommended": "Cet espace de travail a des recommandations d'extension.", + "ignoreExtensionRecommendations": "Voulez-vous ignorer toutes les recommandations d'extension ?", + "ignoreAll": "Oui, ignorer tout", + "no": "Non", + "cancel": "Annuler" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..74df417c20 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "Gérer les extensions", + "galleryExtensionsCommands": "Installer les extensions de la galerie", + "extension": "Extension", + "extensions": "Extensions", + "view": "Affichage", + "extensionsConfigurationTitle": "Extensions", + "extensionsAutoUpdate": "Mettre à jour automatiquement les extensions", + "extensionsIgnoreRecommendations": "Ignorer les recommandations d'extension" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..258432b585 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "Ouvrir le dossier d'extensions", + "installVSIX": "Installer depuis un VSIX...", + "InstallVSIXAction.success": "Installation réussie de l'extension. Effectuez un redémarrage pour l'activer.", + "InstallVSIXAction.reloadNow": "Recharger maintenant" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..7229cd151e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Désactiver les autres mappages de touches ({0}) pour éviter les conflits de combinaisons de touches ?", + "yes": "Oui", + "no": "Non", + "betterMergeDisabled": "L'extension Better Merge est désormais intégrée, l'extension installée est désactivée et peut être désinstallée.", + "uninstall": "Désinstaller", + "later": "Plus tard" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..a08e273d13 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "Marketplace", + "installedExtensions": "Installées", + "searchInstalledExtensions": "Installées", + "recommendedExtensions": "Recommandées", + "searchExtensions": "Rechercher des extensions dans Marketplace", + "sort by installs": "Trier par : nombre d'installations", + "sort by rating": "Trier par : évaluation", + "sort by name": "Trier par : Nom", + "suggestProxyError": "Marketplace a retourné 'ECONNREFUSED'. Vérifiez le paramètre 'http.proxy'.", + "extensions": "Extensions", + "outdatedExtensions": "{0} extensions obsolètes" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..3ab29e17c4 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Extensions", + "no extensions found": "Extensions introuvables.", + "suggestProxyError": "Marketplace a retourné 'ECONNREFUSED'. Vérifiez le paramètre 'http.proxy'." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json new file mode 100644 index 0000000000..3096f3db2c --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Désactiver les autres mappages de touches pour éviter les conflits de combinaisons de touches ?", + "yes": "Oui", + "no": "Non" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..ee2c85b73f --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "L'activation de '{0}' entraîne également l'activation de ses dépendances. Voulez-vous continuer ?", + "enable": "Oui", + "doNotEnable": "Non", + "disableDependeciesConfirmation": "Voulez-vous désactiver uniquement '{0}' ou également ses dépendances ?", + "disableOnly": "Uniquement", + "disableAll": "Tout", + "cancel": "Annuler", + "singleDependentError": "Impossible de désactiver l'extension '{0}'. L'extension '{1}' en dépend.", + "twoDependentsError": "Impossible de désactiver l'extension '{0}'. Les extensions '{1}' et '{2}' en dépendent.", + "multipleDependentsError": "Impossible de désactiver l'extension '{0}'. Les extensions '{1}', '{2}' et d'autres extensions en dépendent.", + "installConfirmation": "Voulez-vous installer l'extension '{0}' ?", + "install": "Installer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/fra/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..671b6834a9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "Tweeter des commentaires", + "label.sendASmile": "Tweetez-nous vos commentaires.", + "patchedVersion1": "Votre installation est endommagée.", + "patchedVersion2": "Spécifiez cela, si vous soumettez un bogue.", + "sentiment": "Quelles sont vos impressions ?", + "smileCaption": "Satisfait", + "frownCaption": "Déçu", + "other ways to contact us": "Autres façons de nous contacter", + "submit a bug": "Soumettre un bogue", + "request a missing feature": "Demander une fonctionnalité manquante", + "tell us why?": "Pourquoi ?", + "commentsHeader": "Commentaires", + "tweet": "Tweet", + "character left": "caractère restant", + "characters left": "caractères restants", + "feedbackSending": "Envoi", + "feedbackSent": "Merci", + "feedbackSendingError": "Réessayer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..ac763c3087 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "Visionneuse de fichier binaire" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..502000f07e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "Éditeur de fichier texte", + "createFile": "Créer un fichier", + "fileEditorWithInputAriaLabel": "{0}. Éditeur de fichier texte.", + "fileEditorAriaLabel": "Éditeur de fichier texte." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..99931e2ac2 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "Dossiers" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..d621dcf205 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "Fichiers", + "revealInSideBar": "Afficher dans la barre latérale", + "acceptLocalChanges": "Utiliser vos modifications et écraser les contenus du disque", + "revertLocalChanges": "Ignorer les modifications et revenir au contenu sur le disque" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..b602fb859b --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "Réessayer", + "rename": "Renommer", + "newFile": "Nouveau fichier", + "newFolder": "Nouveau dossier", + "openFolderFirst": "Ouvrez d'abord un dossier pour y créer des fichiers ou des dossiers.", + "newUntitledFile": "Nouveau fichier sans titre", + "createNewFile": "Nouveau fichier", + "createNewFolder": "Nouveau dossier", + "deleteButtonLabelRecycleBin": "&&Déplacer vers la Corbeille", + "deleteButtonLabelTrash": "&&Déplacer vers la Poubelle", + "deleteButtonLabel": "S&&upprimer", + "dirtyMessageFolderOneDelete": "Vous supprimez un dossier contenant 1 fichier dont les changements n'ont pas été enregistrés. Voulez-vous continuer ?", + "dirtyMessageFolderDelete": "Vous supprimez un dossier contenant {0} fichiers dont les changements n'ont pas été enregistrés. Voulez-vous continuer ?", + "dirtyMessageFileDelete": "Vous supprimez un fichier dont les changements n'ont pas été enregistrés. Voulez-vous continuer ?", + "dirtyWarning": "Vous perdrez vos modifications, si vous ne les enregistrez pas.", + "confirmMoveTrashMessageFolder": "Voulez-vous vraiment supprimer '{0}' et son contenu ?", + "confirmMoveTrashMessageFile": "Voulez-vous vraiment supprimer '{0}' ?", + "undoBin": "Vous pouvez effectuer une restauration à partir de la Corbeille.", + "undoTrash": "Vous pouvez effectuer une restauration à partir de la Poubelle.", + "confirmDeleteMessageFolder": "Voulez-vous vraiment supprimer définitivement '{0}' et son contenu ?", + "confirmDeleteMessageFile": "Voulez-vous vraiment supprimer définitivement '{0}' ?", + "irreversible": "Cette action est irréversible !", + "permDelete": "Supprimer définitivement", + "delete": "Supprimer", + "importFiles": "Importer des fichiers", + "confirmOverwrite": "Un fichier ou dossier portant le même nom existe déjà dans le dossier de destination. Voulez-vous le remplacer ?", + "replaceButtonLabel": "&&Remplacer", + "copyFile": "Copier", + "pasteFile": "Coller", + "duplicateFile": "Doublon", + "openToSide": "Ouvrir sur le côté", + "compareSource": "Sélectionner pour comparer", + "globalCompareFile": "Comparer le fichier actif à...", + "pickHistory": "Sélectionnez un fichier ouvert à comparer", + "unableToFileToCompare": "Impossible de comparer le fichier sélectionné à '{0}'.", + "openFileToCompare": "Ouvrez d'abord un fichier pour le comparer à un autre fichier.", + "compareWith": "Comparer '{0}' à '{1}'", + "compareFiles": "Comparer des fichiers", + "refresh": "Actualiser", + "save": "Enregistrer", + "saveAs": "Enregistrer sous...", + "saveAll": "Enregistrer tout", + "saveAllInGroup": "Enregistrer tout dans le groupe", + "saveFiles": "Enregistrer les fichiers à l'intégrité compromise", + "revert": "Rétablir le fichier", + "focusOpenEditors": "Mettre le focus sur la vue des éditeurs ouverts", + "focusFilesExplorer": "Focus sur l'Explorateur de fichiers", + "showInExplorer": "Révéler le fichier actif dans la barre latérale", + "openFileToShow": "Ouvrir d'abord un fichier pour l'afficher dans l'Explorateur", + "collapseExplorerFolders": "Réduire les dossiers dans l'explorateur", + "refreshExplorer": "Actualiser l'explorateur", + "openFile": "Ouvrir un fichier...", + "openFileInNewWindow": "Ouvrir le fichier actif dans une nouvelle fenêtre", + "openFileToShowInNewWindow": "Ouvrir d'abord un fichier à ouvrir dans une nouvelle fenêtre", + "revealInWindows": "Révéler dans l'Explorateur", + "revealInMac": "Révéler dans le Finder", + "openContainer": "Ouvrir le dossier contenant", + "revealActiveFileInWindows": "Révéler le fichier actif dans l'Explorateur Windows", + "revealActiveFileInMac": "Révéler le fichier actif dans le Finder", + "openActiveFileContainer": "Ouvrir le dossier contenant le fichier actif", + "copyPath": "Copier le chemin", + "copyPathOfActive": "Copier le chemin du fichier actif", + "emptyFileNameError": "Un nom de fichier ou de dossier doit être fourni.", + "fileNameExistsError": "Un fichier ou dossier **{0}** existe déjà à cet emplacement. Choisissez un autre nom.", + "invalidFileNameError": "Le nom **{0}** est non valide en tant que nom de fichier ou de dossier. Choisissez un autre nom.", + "filePathTooLongError": "Le nom **{0}** correspond à un chemin d'accès trop long. Choisissez un nom plus court.", + "compareWithSaved": "Compare le fichier actif avec celui enregistré", + "modifiedLabel": "{0} (sur le disque) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..387cb9168e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "Ouvrir d'abord un fichier pour copier son chemin", + "openFileToReveal": "Ouvrir d'abord un fichier à révéler" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..33035dcbe3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "Afficher l'Explorateur", + "explore": "Explorateur", + "view": "Affichage", + "textFileEditor": "Éditeur de fichier texte", + "binaryFileEditor": "Éditeur de fichier binaire", + "filesConfigurationTitle": "Fichiers", + "exclude": "Configurez les modèles Glob pour l'exclusion des fichiers et des dossiers.", + "files.exclude.boolean": "Modèle Glob auquel les chemins de fichiers doivent correspondre. Affectez la valeur true ou false pour activer ou désactiver le modèle.", + "files.exclude.when": "Vérification supplémentaire des frères d'un fichier correspondant. Utilisez $(basename) comme variable pour le nom de fichier correspondant.", + "associations": "Configurez les associations entre les fichiers et les langages (par exemple, \"*.extension\": \"html\"). Celles-ci ont priorité sur les associations par défaut des langages installés.", + "encoding": "Encodage du jeu de caractères par défaut à utiliser durant la lecture et l'écriture des fichiers.", + "autoGuessEncoding": "Quand cette option est activée, tente de deviner l'encodage du jeu de caractères à l'ouverture des fichiers", + "eol": "Caractère de fin de ligne par défaut. Utilisez \\n pour LF et \\r\\n pour CRLF.", + "trimTrailingWhitespace": "Si l'option est activée, l'espace blanc de fin est supprimé au moment de l'enregistrement d'un fichier.", + "insertFinalNewline": "Quand l'option est activée, une nouvelle ligne finale est insérée à la fin du fichier au moment de son enregistrement.", + "files.autoSave.off": "Un fichier dont l'intégrité est compromise n'est jamais enregistré automatiquement.", + "files.autoSave.afterDelay": "Un fichier dont l'intégrité est compromise est automatiquement enregistré après la configuration de 'files.autoSaveDelay'.", + "files.autoSave.onFocusChange": "Un fichier dont l'intégrité est compromise est automatiquement enregistré quand l'éditeur perd le focus.", + "files.autoSave.onWindowChange": "Un fichier dont l'intégrité est compromise est automatiquement enregistré quand la fenêtre perd le focus.", + "autoSave": "Contrôle l'enregistrement automatique des fichiers dont l'intégrité est compromise. Valeurs acceptées : '{0}', '{1}', '{2}' (l'éditeur perd le focus), '{3}' (la fenêtre perd le focus). Si la valeur est '{4}', vous pouvez configurer le délai dans 'files.autoSaveDelay'.", + "autoSaveDelay": "Contrôle le délai en ms au bout duquel un fichier à l'intégrité compromise est enregistré automatiquement. S'applique uniquement quand 'files.autoSave' a la valeur '{0}'", + "watcherExclude": "Configurez les modèles Glob des chemins de fichier à exclure de la surveillance des fichiers. Les modèles doivent correspondre à des chemins absolus (par ex., utilisez le préfixe ** ou le chemin complet pour une correspondance appropriée). Le changement de ce paramètre nécessite un redémarrage. Si vous constatez que le code consomme beaucoup de temps processeur au démarrage, excluez les dossiers volumineux pour réduire la charge initiale.", + "hotExit.off": "Désactivez la sortie à chaud.", + "hotExit.onExit": "La sortie à chaud se déclenche à la fermeture de l'application, c'est-à-dire quand la dernière fenêtre est fermée dans Windows/Linux, ou quand la commande workbench.action.quit est déclenchée (palette de commandes, combinaison de touches, menu). Toutes les fenêtres avec des sauvegardes sont restaurées au prochain lancement.", + "hotExit.onExitAndWindowClose": "La sortie à chaud est déclenchée à la fermeture de l'application, c'est-à-dire quand la dernière fenêtre est fermée sous Windows/Linux ou quand la commande workbench.action.quit est déclenchée (palette de commandes, combinaison de touches, menu), ainsi que pour toute fenêtre avec un dossier ouvert, qu'il s'agisse de la dernière fenêtre ou non. Toutes les fenêtres sans dossier ouvert sont restaurées au prochain lancement. Pour restaurer une fenêtre de dossiers telle qu'elle était avant l'arrêt, définissez \"window.restoreWindows\" sur \"all\".", + "hotExit": "Contrôle si les fichiers non enregistrés sont mémorisés entre les sessions, ce qui permet d'ignorer la demande d'enregistrement à la sortie de l'éditeur.", + "useExperimentalFileWatcher": "Utilisez le nouvel observateur de fichiers expérimental.", + "defaultLanguage": "Mode de langage par défaut affecté aux nouveaux fichiers.", + "editorConfigurationTitle": "Éditeur", + "formatOnSave": "Met en forme un fichier au moment de l'enregistrement. Un formateur doit être disponible, le fichier ne doit pas être enregistré automatiquement, et l'éditeur ne doit pas être en cours d'arrêt.", + "explorerConfigurationTitle": "Explorateur de fichiers", + "openEditorsVisible": "Nombre d'éditeurs affichés dans le volet Éditeurs ouverts. Définissez la valeur 0 pour masquer le volet.", + "dynamicHeight": "Contrôle si la hauteur de la section des éditeurs ouverts doit s'adapter dynamiquement ou non au nombre d'éléments.", + "autoReveal": "Contrôle si l'Explorateur doit automatiquement afficher et sélectionner les fichiers à l'ouverture.", + "enableDragAndDrop": "Contrôle si l'explorateur doit autoriser le déplacement de fichiers et de dossiers par glisser-déplacer.", + "sortOrder.default": "Les fichiers et dossiers sont triés par nom, dans l’ordre alphabétique. Les dossiers sont affichés avant les fichiers.", + "sortOrder.mixed": "Les fichiers et dossiers sont triés par nom, dans l’ordre alphabétique. Les fichiers sont imbriqués dans les dossiers.", + "sortOrder.filesFirst": "Les fichiers et dossiers sont triés par nom, dans l’ordre alphabétique. Les fichiers sont affichés avant les dossiers.", + "sortOrder.type": "Les fichiers et dossiers sont triés par extension, dans l’ordre alphabétique. Les dossiers sont affichés avant les fichiers.", + "sortOrder.modified": "Les fichiers et dossiers sont triés par date de dernière modification, dans l’ordre décroissant. Les dossiers sont affichés avant les fichiers.", + "sortOrder": "Contrôle l'ordre de tri des fichiers et dossiers dans l'explorateur. En plus du tri par défaut, vous pouvez définir l'ordre sur 'mixed' (fichiers et dossiers triés combinés), 'type' (par type de fichier), 'modified' (par date de dernière modification) ou 'fileFirst' (trier les fichiers avant les dossiers)." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..cf0436eea2 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "Abandonner", + "overwrite": "Remplacer", + "retry": "Réessayer", + "readonlySaveError": "Échec de l'enregistrement de '{0}' : le fichier est protégé en écriture. Sélectionnez 'Remplacer' pour supprimer la protection.", + "genericSaveError": "Échec d'enregistrement de '{0}' ({1}).", + "staleSaveError": "Échec de l'enregistrement de '{0}' : le contenu sur disque est plus récent. Cliquez sur **Comparer** pour comparer votre version à celle située sur le disque.", + "compareChanges": "Comparer", + "saveConflictDiffLabel": "{0} (sur le disque) ↔ {1} (dans {2}) - Résoudre le conflit d'enregistrement", + "userGuide": "Utiliser les actions dans la barre d’outils de l’éditeur vers la droite pour soit **annuler** vos modifications ou **écraser** le contenu sur le disque avec vos modifications" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..1433bcf6df --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Aucun dossier ouvert", + "explorerSection": "Section de l'Explorateur de fichiers", + "noWorkspaceHelp": "Vous n'avez pas encore ouvert de dossier.", + "openFolder": "Ouvrir le dossier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..c028659509 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "Section de l'Explorateur de fichiers", + "treeAriaLabel": "Explorateur de fichiers" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..092d6522af --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "Tapez le nom du fichier. Appuyez sur Entrée pour confirmer ou sur Échap pour annuler.", + "filesExplorerViewerAriaLabel": "{0}, Explorateur de fichiers", + "dropFolders": "Voulez-vous ajouter les dossiers à l’espace de travail ?", + "dropFolder": "Voulez-vous ajouter le dossier à l’espace de travail ?", + "addFolders": "&&Ajouter les dossiers", + "addFolder": "&&Ajouter le dossier", + "confirmOverwriteMessage": "{0}' existe déjà dans le dossier de destination. Voulez-vous le remplacer ?", + "irreversible": "Cette action est irréversible !", + "replaceButtonLabel": "&&Remplacer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..efc83a6a32 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "Éditeurs ouverts", + "openEditosrSection": "Section des éditeurs ouverts", + "treeAriaLabel": "Éditeurs ouverts : liste des fichiers actifs", + "dirtyCounter": "{0} non enregistré(s)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..b2e3f30bc9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}, groupe d'éditeurs", + "openEditorAriaLabel": "{0}, Ouvrir l'éditeur", + "saveAll": "Enregistrer tout", + "closeAllUnmodified": "Fermer les éléments non modifiés", + "closeAll": "Tout fermer", + "compareWithSaved": "Comparer avec celui enregistré", + "close": "Fermer", + "closeOthers": "Fermer les autres" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/fra/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..48cbe0a90d --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0} fichiers non enregistrés" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/fra/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..32c2e3e166 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (deleted from disk)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json new file mode 100644 index 0000000000..4af738ee3a --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "switchToChangesView": "Basculer vers le mode Modifications", + "openInEditor": "Basculer vers le mode Éditeur", + "workbenchStage": "Indexer", + "workbenchUnstage": "Annuler le stockage en zone de transit", + "stageSelectedLines": "Stocker les lignes sélectionnées en zone de transit", + "unstageSelectedLines": "Désindexer les lignes sélectionnées", + "revertSelectedLines": "Restaurer les lignes sélectionnées", + "confirmRevertMessage": "Voulez-vous vraiment restaurer les changements sélectionnés ?", + "irreversible": "Cette action est irréversible !", + "revertChangesLabel": "&&Restaurer les modifications", + "openChange": "Ouvrir la modification", + "openFile": "Ouvrir le fichier", + "git": "Git" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/gitActions.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/gitActions.i18n.json new file mode 100644 index 0000000000..2cfce7725f --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/gitActions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openChange": "Ouvrir la modification", + "openFile": "Ouvrir le fichier", + "init": "Init", + "refresh": "Actualiser", + "stageChanges": "Indexer", + "stageAllChanges": "Stocker tout en zone de transit", + "confirmUndoMessage": "Voulez-vous vraiment supprimer toutes les modifications ?", + "confirmUndoAllOne": "Il existe des modifications non prêtes à être validées dans {0} fichier.\n\nCette action est irréversible !", + "confirmUndoAllMultiple": "Il existe des modifications non prêtes à être validées dans {0} fichiers.\n\nCette action est irréversible !", + "cleanChangesLabel": "&&Supprimer les modifications", + "confirmUndo": "Voulez-vous vraiment supprimer les modifications apportées à '{0}' ?", + "irreversible": "Cette action est irréversible !", + "undoChanges": "Nettoyer", + "undoAllChanges": "Nettoyer tout", + "unstage": "Annuler le stockage en zone de transit", + "unstageAllChanges": "Annuler tout le stockage en zone de transit", + "dirtyTreeCheckout": "Extraction impossible. Validez ou remisez (stash) d'abord votre travail.", + "commitStaged": "Valider le contenu en zone de transit", + "commitStagedAmend": "Valider les modifications en attente (modifier)", + "commitStagedSignedOff": "Valider les modifications en attente (signé)", + "commit": "Commit", + "commitMessage": "Valider le message", + "commitAll": "Valider tout", + "commitAllSignedOff": "Valider tout (signé)", + "commitAll2": "Valider tout", + "commitStaged2": "Valider le contenu en zone de transit", + "dirtyTreePull": "Extraction impossible. Validez ou remisez (stash) d'abord votre travail.", + "authFailed": "Échec de l'authentification durant l'opération git remote.", + "pushToRemote": "Transfert (Push) vers...", + "pushToRemotePickMessage": "Choisissez un dépôt distant où effectuer un transfert (Push) de la branche '{0}' :", + "publish": "Publier", + "confirmPublishMessage": "Voulez-vous vraiment publier '{0}' sur '{1}' ?", + "confirmPublishMessageButton": "&&Publier", + "publishPickMessage": "Choisissez un dépôt distant où publier la branche '{0}' :", + "sync is unpredictable": "Cette action effectue un transfert (Push) et un tirage (Pull) des validations à destination et en provenance de '{0}'.", + "ok": "OK", + "cancel": "Annuler", + "never again": "OK, ne plus afficher", + "undoLastCommit": "Annuler la dernière validation" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json new file mode 100644 index 0000000000..bb39c36307 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refAriaLabel": "{0}, git", + "checkoutBranch": "Branche sur {0}", + "checkoutRemoteBranch": "Branche distante à {0}", + "checkoutTag": "Étiquette à {0}", + "alreadyCheckedOut": "La branche {0} est déjà la branche active", + "branchAriaLabel": "{0}, branche git", + "createBranch": "Créer une branche {0}", + "noBranches": "Aucune autre branche", + "notValidBranchName": "Fournissez un nom de branche valide" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/gitServices.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/gitServices.i18n.json new file mode 100644 index 0000000000..4d2bab0c50 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/gitServices.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cantOpen": "Impossible d'ouvrir cette ressource git.", + "gitIndexChanges": "{0} (index) ↔ {1}", + "gitIndexChangesDesc": "{0} - Modifications de l'index", + "gitIndexChangesRenamed": "{0} ← {1}", + "gitIndexChangesRenamedDesc": "{0} - Renommé - Modifications de l'index", + "workingTreeChanges": "{0} (HEAD) ↔ {1}", + "workingTreeChangesDesc": "{0} - Modifications de l'arborescence de travail", + "gitMergeChanges": "{0} (merge) ↔ {1}", + "gitMergeChangesDesc": "{0} - Fusionner les modifications", + "updateGit": "Il semble que git {0} soit installé. Code fonctionne mieux avec git >=2.0.0.", + "download": "Télécharger", + "neverShowAgain": "Ne plus afficher", + "configureUsernameEmail": "Configurez votre nom d'utilisateur et votre adresse de messagerie git.", + "badConfigFile": "Git {0}", + "unmergedChanges": "Vous devez tout d'abord résoudre les modifications non fusionnées avant de valider vos modifications.", + "showOutput": "Afficher la sortie", + "cancel": "Annuler", + "checkNativeConsole": "Un problème s'est produit durant l'exécution d'une opération git. Examinez la sortie, ou utilisez une console pour vérifier l'état de votre dépôt.", + "changesFromIndex": "{0} (index)", + "changesFromIndexDesc": "{0} - Modifications de l'index", + "changesFromTree": "{0} ({1})", + "changesFromTreeDesc": "{0} - Modifications sur {1}", + "cantOpenResource": "Impossible d'ouvrir cette ressource git." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json new file mode 100644 index 0000000000..105a0c22f8 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "publishBranch": "Publier la branche", + "syncBranch": "Synchroniser les modifications", + "gitNotEnabled": "Git n'est pas activé dans cet espace de travail." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json new file mode 100644 index 0000000000..f26399d6e8 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gitProgressBadge": "État d'exécution de git", + "gitPendingChangesBadge": "{0} changements en attente", + "toggleGitViewlet": "Afficher Git", + "git": "Git", + "view": "Affichage", + "gitCommands": "Commandes Git", + "gitConfigurationTitle": "Git", + "gitEnabled": "Vérification de l'activation de git", + "gitPath": "Chemin d'accès à l'exécutable git", + "gitAutoRefresh": "Indique si l'actualisation automatique est activée", + "gitAutoFetch": "Indique si l'extraction automatique est activée.", + "gitLongCommit": "Si les messages de validation longs doivent faire l'objet d'un avertissement.", + "gitLargeRepos": "Toujours autoriser la gestion des grands référentiels par le code.", + "confirmSync": "Confirmez avant de synchroniser des dépôts git.", + "countBadge": "Contrôle le compteur de badges git.", + "checkoutType": "Contrôle le type des branches listées." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json new file mode 100644 index 0000000000..53f44b8c42 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "needMessage": "Indiquez un message de validation. Vous pouvez toujours appuyer sur **{0}** pour valider les modifications. S'il existe des modifications en zone de transit, seules ces dernières sont validées ; sinon, toutes les modifications sont validées.", + "nothingToCommit": "Quand il existe des modifications à valider, tapez le message de validation et appuyez sur **{0}** pour valider les modifications. S'il existe des modifications en zone de transit, seules ces dernières sont validées ; sinon, toutes les modifications sont validées.", + "longCommit": "Il est recommandé de limiter la première ligne de la validation à 50 caractères. N'hésitez pas à utiliser d'autres lignes pour ajouter des informations.", + "commitMessage": "Message (press {0} to commit)", + "commitMessageAriaLabel": "Git : tapez le message de validation, puis appuyez sur {0} pour valider", + "treeAriaLabel": "Affichage des modifications Git", + "showOutput": "Afficher la sortie Git" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json new file mode 100644 index 0000000000..d778bc2b10 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stagedChanges": "Modifications en zone de transit", + "allChanges": "Modifications", + "mergeChanges": "Fusionner les modifications", + "outsideOfWorkspace": "Ce fichier se trouve en dehors de l'espace de travail actuel.", + "modified-char": "M", + "added-char": "A", + "deleted-char": "D", + "renamed-char": "R", + "copied-char": "C", + "untracked-char": "U", + "ignored-char": "!", + "title-index-modified": "Modifié dans l'index", + "title-modified": "Modifié", + "title-index-added": "Ajouté à l'index", + "title-index-deleted": "Supprimé dans l'index", + "title-deleted": "Supprimé", + "title-index-renamed": "Renommé dans l'index", + "title-index-copied": "Copié dans l'index", + "title-untracked": "Non suivi", + "title-ignored": "Ignoré", + "title-conflict-both-deleted": "Conflit : supprimé par eux et nous", + "title-conflict-added-by-us": "Conflit : ajouté par nous", + "title-conflict-deleted-by-them": "Conflit : supprimé par eux", + "title-conflict-added-by-them": "Conflit : ajouté par eux", + "title-conflict-deleted-by-us": "Conflit : supprimé par nous", + "title-conflict-both-added": "Conflit : ajouté par eux et nous", + "title-conflict-both-modified": "Conflit : modifié par eux et nous", + "fileStatusAriaLabel": "Le fichier {0} dans le dossier {1} est à l'état {2}, Git", + "ariaLabelStagedChanges": "Modifications en attente de validation, Git", + "ariaLabelChanges": "Modifications, Git", + "ariaLabelMerge": "Fusion, Git" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json new file mode 100644 index 0000000000..4ccec2987e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disabled": "Git est désactivé dans les paramètres." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json new file mode 100644 index 0000000000..c93c59f9e3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noGit": "Cet espace de travail n'est pas encore sous contrôle de code source git.", + "gitinit": "Initialiser le dépôt Git" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json new file mode 100644 index 0000000000..e77322321f --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "macInstallWith": "Vous pouvez l'installer avec {0}, le télécharger à partir de {1} ou installer les outils de développement en ligne de commande {2}, en tapant simplement {3} à une invite de Terminal.", + "winInstallWith": "Vous pouvez l'installer avec {0} ou le télécharger à partir de {1}.", + "linuxDownloadFrom": "Vous pouvez le télécharger à partir de {0}.", + "downloadFrom": "Vous pouvez le télécharger à partir de {0}.", + "looksLike": "Il semble que git ne soit pas installé sur votre système.", + "pleaseRestart": "Une fois git installé, redémarrez VSCode." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json new file mode 100644 index 0000000000..5ca42ee752 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "huge": "Votre dépôt semble comporter de nombreux changements actifs.\nCela peut entraîner un ralentissement important de Code.", + "setting": "Vous pouvez désactiver définitivement cet avertissement avec le paramètre suivant :", + "allo": "Autoriser les dépôts de grande taille" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json new file mode 100644 index 0000000000..67ac216144 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrongRoot": "Ce répertoire semble être contenu dans un dépôt git.", + "pleaseRestart": "Ouvrez le répertoire racine du dépôt pour accéder aux fonctionnalités Git." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json b/i18n/fra/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json new file mode 100644 index 0000000000..da22de2ed3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspaceHelp": "Vous n'avez pas encore ouvert de dossier.", + "pleaseRestart": "Ouvrez un dossier avec un dépôt Git pour accéder aux fonctionnalités Git.", + "openFolder": "Ouvrir le dossier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json new file mode 100644 index 0000000000..e67bcb221d --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSCMViewlet": "Afficher SCM", + "git": "Git" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json b/i18n/fra/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json new file mode 100644 index 0000000000..14552bc390 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "valid": "Indiquez une URL de dépôt Git valide", + "url": "URL du dépôt", + "directory": "Répertoire du clone de destination", + "cloning": "Clonage du dépôt '{0}'...", + "already exists": "Le dépôt de destination existe déjà. Choisissez un autre répertoire dans lequel enregistrer le clone." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json b/i18n/fra/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json new file mode 100644 index 0000000000..91fbae2148 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "git": "Git" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/git/node/git.lib.i18n.json b/i18n/fra/src/vs/workbench/parts/git/node/git.lib.i18n.json new file mode 100644 index 0000000000..9b2eebe554 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/git/node/git.lib.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorBuffer": "Impossible d'ouvrir le fichier à partir de git", + "fileBinaryError": "Il semble que le fichier soit binaire. Impossible de l'ouvrir en tant que texte" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..6c2c4788c4 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "Aperçu HTML", + "devtools.webview": "Développeur : outils Webview" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/fra/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..6986755648 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "Entrée d'éditeur non valide." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/fra/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..7e9408822c --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "Développeur : outils Webview" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/fra/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..13e7f864c9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "Affichage", + "problems.view.show.label": "Afficher les problèmes", + "problems.panel.configuration.title": "Affichage des problèmes", + "problems.panel.configuration.autoreveal": "Contrôle si l'affichage des problèmes doit automatiquement montrer les fichiers quand il les ouvre", + "markers.panel.title.problems": "Problèmes", + "markers.panel.aria.label.problems.tree": "Problèmes regroupés par fichiers", + "markers.panel.no.problems.build": "Aucun problème n'a été détecté dans l'espace de travail jusqu'à présent.", + "markers.panel.no.problems.filters": "Résultats introuvables avec les critères de filtre fournis", + "markers.panel.action.filter": "Filtrer les problèmes", + "markers.panel.filter.placeholder": "Filtrer par type ou texte", + "markers.panel.filter.errors": "erreurs", + "markers.panel.filter.warnings": "avertissements", + "markers.panel.filter.infos": "infos", + "markers.panel.single.error.label": "1 erreur", + "markers.panel.multiple.errors.label": "{0} erreurs", + "markers.panel.single.warning.label": "1 avertissement", + "markers.panel.multiple.warnings.label": "{0} avertissements", + "markers.panel.single.info.label": "1 info", + "markers.panel.multiple.infos.label": "{0} infos", + "markers.panel.single.unknown.label": "1 inconnu", + "markers.panel.multiple.unknowns.label": "{0} inconnus", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "{0} avec {1} problèmes", + "problems.tree.aria.label.error.marker": "Erreur générée par {0} : {1} à la ligne {2} et au caractère {3}", + "problems.tree.aria.label.error.marker.nosource": "Erreur : {0} à la ligne {1} et au caractère {2}", + "problems.tree.aria.label.warning.marker": "Avertissement généré par {0} : {1} à la ligne {2} et au caractère {3}", + "problems.tree.aria.label.warning.marker.nosource": "Avertissement : {0} à la ligne {1} et au caractère {2}", + "problems.tree.aria.label.info.marker": "Information générée par {0} : {1} à la ligne {2} et au caractère {3}", + "problems.tree.aria.label.info.marker.nosource": "Information : {0} à la ligne {1} et au caractère {2}", + "problems.tree.aria.label.marker": "Problème généré par {0} : {1} à la ligne {2} et au caractère {3}", + "problems.tree.aria.label.marker.nosource": "Problème : {0} à la ligne {1} et au caractère {2}", + "errors.warnings.show.label": "Afficher les erreurs et les avertissements" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/fra/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..ca4766dbfe --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "Copier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..6b412e4bd6 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Acceptez-vous de répondre à une enquête rapide ?", + "takeSurvey": "Répondre à l'enquête", + "remindLater": "Me le rappeler plus tard", + "neverAgain": "Ne plus afficher" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..d70b0eccc3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Sortie", + "viewCategory": "Affichage", + "clearOutput.label": "Effacer la sortie" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/fra/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..36acc0f81a --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "Activer/désactiver la sortie", + "clearOutput": "Effacer la sortie", + "toggleOutputScrollLock": "Activer/désactiver l'arrêt du défilement de la sortie", + "switchToOutput.label": "Passer à la sortie" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/fra/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..e60430ef58 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0}, Panneau de sortie", + "outputPanelAriaLabel": "Panneau de sortie" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/fra/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..1a478c5841 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Sortie", + "channel": "pour '{0}'" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..a86c080ca8 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "Démarrage lent détecté", + "slow.detail": "Le démarrage a été très lent. Redémarrez '{0}' en ayant activé le profilage, partagez les profils avec nous, et nous ferons en sorte que le démarrage retrouve sa rapidité d'exécution.", + "prof.message": "Création réussie des profils.", + "prof.detail": "Créez un problème et joignez manuellement les fichiers suivants :\n{0}", + "prof.restartAndFileIssue": "Créer le problème et redémarrer", + "prof.restart": "Redémarrer", + "prof.thanks": "Merci de votre aide.", + "prof.detail.restart": "Un redémarrage final est nécessaire pour continuer à utiliser '{0}'. Nous vous remercions une fois de plus pour votre contribution." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..f3ef77dcae --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "Appuyez sur la combinaison de touches souhaitée, puis sur Entrée. Appuyez sur Échap pour annuler.", + "defineKeybinding.chordsTo": "pression simultanée avec" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..8614be6215 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "Raccourcis clavier", + "SearchKeybindings.AriaLabel": "Rechercher dans les combinaisons de touches", + "SearchKeybindings.Placeholder": "Rechercher dans les combinaisons de touches", + "sortByPrecedene": "Trier par priorité", + "header-message": "Pour des personnalisations avancées, ouvrez et modifiez", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "Combinaisons de touches", + "changeLabel": "Changer de combinaison de touches", + "addLabel": "Ajouter une combinaison de touches", + "removeLabel": "Supprimer la combinaison de touches", + "resetLabel": "Définir une combinaison de touches", + "showConflictsLabel": "Afficher les conflits", + "copyLabel": "Copier", + "error": "Erreur '{0}' durant la modification de la combinaison de touches. Ouvrez le fichier 'keybindings.json', puis effectuez la vérification.", + "command": "Commande", + "keybinding": "Combinaison de touches", + "source": "Source", + "when": "Quand", + "editKeybindingLabelWithKey": "Changer de combinaison de touches {0}", + "editKeybindingLabel": "Changer de combinaison de touches", + "addKeybindingLabelWithKey": "Ajouter une combinaison de touches {0}", + "addKeybindingLabel": "Ajouter une combinaison de touches", + "commandAriaLabel": "Commande : {0}.", + "keybindingAriaLabel": "Combinaison de touches : {0}.", + "noKeybinding": "Aucune combinaison de touches n'est affectée.", + "sourceAriaLabel": "Source : {0}.", + "whenAriaLabel": "When : {0}.", + "noWhen": "Pas de contexte when." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..e204f9df4a --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "Définir une combinaison de touches", + "defineKeybinding.kbLayoutErrorMessage": "Vous ne pouvez pas produire cette combinaison de touches avec la disposition actuelle du clavier.", + "defineKeybinding.kbLayoutLocalAndUSMessage": "**{0}** pour votre disposition actuelle du clavier (**{1}** pour le clavier États-Unis standard).", + "defineKeybinding.kbLayoutLocalMessage": "**{0}** pour votre disposition actuelle du clavier." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..a58ca676bd --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "Éditeur de préférences par défaut", + "keybindingsEditor": "Éditeur de combinaisons de touches", + "preferences": "Préférences" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..d5ceee5fd1 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "Ouvrir les paramètres utilisateur", + "openGlobalKeybindings": "Ouvrir les raccourcis clavier", + "openGlobalKeybindingsFile": "Ouvrir le fichier des raccourcis clavier", + "openWorkspaceSettings": "Ouvrir les paramètres d'espace de travail", + "openFolderSettings": "Ouvrir le dossier Paramètres", + "pickFolder": "Sélectionner le dossier", + "configureLanguageBasedSettings": "Configurer les paramètres spécifiques au langage...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "Sélectionner un langage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..4e68a9775f --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "Paramètres par défaut", + "SearchSettingsWidget.AriaLabel": "Paramètres de recherche", + "SearchSettingsWidget.Placeholder": "Paramètres de recherche", + "totalSettingsMessage": "Total de {0} paramètres", + "noSettingsFound": "Aucun résultat", + "oneSettingFound": "1 paramètre correspondant", + "settingsFound": "{0} paramètres correspondants", + "fileEditorWithInputAriaLabel": "{0}. Éditeur de fichier texte.", + "fileEditorAriaLabel": "Éditeur de fichier texte.", + "preferencesAriaLabel": "Préférences par défaut. Éditeur de texte en lecture seule." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..742c076422 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "Placer vos paramètres ici pour remplacer les paramètres par défaut.", + "errorInvalidConfiguration": "Impossible d'écrire les paramètres. Corrigez les erreurs/avertissements présents dans le fichier, puis réessayez.", + "emptyWorkspaceSettingsHeader": "Placer vos paramètres ici pour remplacer les paramètres utilisateur.", + "emptyFolderSettingsHeader": "Placer les paramètres de votre dossier ici pour remplacer ceux des paramètres de l’espace de travail.", + "defaultFolderSettingsTitle": "Paramètres de dossier par défaut", + "defaultSettingsTitle": "Paramètres par défaut", + "noSettingsFound": "Aucun paramètre.", + "editTtile": "Modifier", + "replaceDefaultValue": "Remplacer dans les paramètres", + "copyDefaultValue": "Copier dans Paramètres", + "unsupportedPHPExecutablePathSetting": "Ce paramètre doit être un paramètre utilisateur. Pour configurer PHP pour l'espace de travail, ouvrez un fichier PHP, puis cliquez sur 'Chemin PHP' dans la barre d'état.", + "unsupportedWorkspaceSetting": "Ce paramètre doit être un paramètre utilisateur.", + "unsupportedWorkbenchSetting": "Ce paramètre ne peut pas être appliqué maintenant. Il est appliqué quand vous ouvrez ce dossier directement." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..01af2df500 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "Ouvrir d'abord un dossier pour créer les paramètres d'espace de travail", + "emptyKeybindingsHeader": "Placez vos combinaisons de touches dans ce fichier pour remplacer les valeurs par défaut", + "defaultKeybindings": "Combinaisons de touches par défaut", + "folderSettingsName": "{0} (Paramètres du dossier)", + "fail.createSettings": "Impossible de créer '{0}' ({1})." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..77dcfdbd42 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "Paramètres de dossier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..7c59bb7ebc --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "Par défaut", + "user": "Utilisateur", + "meta": "méta", + "option": "option" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..8ebe09c16b --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "Paramètres utilisateur", + "workspaceSettingsTarget": "Paramètres de l'espace de travail" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..92ac18a7db --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "Utilisés le plus souvent", + "noSettings": "Aucun paramètre", + "defaultKeybindingsHeader": "Remplacez les combinaisons de touches dans votre fichier de combinaisons de touches." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..f2503d0fdc --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Afficher toutes les commandes", + "clearCommandHistory": "Effacer l'historique de commandes", + "showCommands.label": "Palette de commandes...", + "entryAriaLabelWithKey": "{0}, {1}, commandes", + "entryAriaLabel": "{0}, commandes", + "canNotRun": "La commande '{0}' ne peut pas être exécutée à partir d'ici.", + "actionNotEnabled": "La commande '{0}' n'est pas activée dans le contexte actuel.", + "recentlyUsed": "récemment utilisées", + "morecCommands": "autres commandes", + "commandLabel": "{0} : {1}", + "cat.title": "{0} : {1}", + "noCommandsMatching": "Aucune commande correspondante" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..bf6f2734c9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "Atteindre la ligne...", + "gotoLineLabelEmptyWithLimit": "Tapez un numéro de ligne à atteindre entre 1 et {0}", + "gotoLineLabelEmpty": "Tapez un numéro de ligne à atteindre.", + "gotoLineColumnLabel": "Atteindre la ligne {0} et le caractère {1}", + "gotoLineLabel": "Atteindre la ligne {0}", + "gotoLineHandlerAriaLabel": "Tapez un numéro de ligne à atteindre.", + "cannotRunGotoLine": "Ouvrir d'abord un fichier texte pour atteindre une ligne" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..e3590eb53a --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "Atteindre le symbole dans le fichier...", + "symbols": "symboles ({0})", + "method": "méthodes ({0})", + "function": "fonctions ({0})", + "_constructor": "constructeurs ({0})", + "variable": "variables ({0})", + "class": "classes ({0})", + "interface": "interfaces ({0})", + "namespace": "espaces de noms ({0})", + "package": "packages ({0})", + "modules": "modules ({0})", + "property": "propriétés ({0})", + "enum": "énumérations ({0})", + "string": "chaînes ({0})", + "rule": "règles ({0})", + "file": "fichiers ({0})", + "array": "tableaux ({0})", + "number": "nombres ({0})", + "boolean": "booléens ({0})", + "object": "objets ({0})", + "key": "touches ({0})", + "entryAriaLabel": "{0}, symboles", + "noSymbolsMatching": "Aucun symbole correspondant", + "noSymbolsFound": "Symboles introuvables", + "gotoSymbolHandlerAriaLabel": "Tapez pour réduire le nombre de symboles de l'éditeur actif.", + "cannotRunGotoSymbolInFile": "Aucune information sur les symboles pour le fichier", + "cannotRunGotoSymbol": "Ouvrir d'abord un fichier texte pour atteindre un symbole" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..5a3e572ba3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, aide sur le sélecteur", + "globalCommands": "commandes globales", + "editorCommands": "commandes de l'éditeur" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..463f287460 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "Commandes d'affichage et d'exécution", + "gotoLineDescriptionMac": "Atteindre la ligne", + "gotoLineDescriptionWin": "Atteindre la ligne", + "gotoSymbolDescription": "Atteindre le symbole dans le fichier", + "gotoSymbolDescriptionScoped": "Atteindre le symbole dans le fichier par catégorie", + "helpDescription": "Afficher l'aide", + "viewPickerDescription": "Ouvrir la vue" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..01560d353c --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, sélecteur de vues", + "views": "Vues", + "panels": "Panneaux", + "terminals": "Terminal", + "terminalTitle": "{0} : {1}", + "channels": "Sortie", + "openView": "Ouvrir la vue", + "quickOpenView": "Mode Quick Open" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..60b89a0306 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "Un paramètre a changé et nécessite un redémarrage pour être appliqué.", + "relaunchSettingDetail": "Appuyez sur le bouton de redémarrage pour redémarrer {0} et activer le paramètre.", + "restart": "Redémarrer", + "relaunchWorkspaceMessage": "Ce changement d’espace de travail nécessite un rechargement de notre système d’extension.", + "reload": "Recharger" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..ec678c132a --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "Couleur d'arrière-plan de la reliure de l'éditeur pour les lignes modifiées.", + "editorGutterAddedBackground": "Couleur d'arrière-plan de la reliure de l'éditeur pour les lignes ajoutées.", + "editorGutterDeletedBackground": "Couleur d'arrière-plan de la reliure de l'éditeur pour les lignes supprimées.", + "overviewRulerModifiedForeground": "Couleur du marqueur de la règle d'aperçu pour le contenu modifié.", + "overviewRulerAddedForeground": "Couleur du marqueur de la règle d'aperçu pour le contenu ajouté.", + "overviewRulerDeletedForeground": "Couleur du marqueur de la règle d'aperçu pour le contenu supprimé." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..b973bc6bd2 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "Afficher Git", + "source control": "Contrôle de code source", + "toggleSCMViewlet": "Afficher SCM", + "view": "Afficher" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..e4a01c7375 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0} changements en attente" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..3aac6bc222 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "Installer des fournisseurs SCM supplémentaires...", + "switch provider": "Changer de fournisseur GCL..." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..725e5c5e62 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Message (press {0} to commit)", + "installAdditionalSCMProviders": "Installer des fournisseurs SCM supplémentaires...", + "no open repo": "Aucun contrôle de code source actif.", + "source control": "Contrôle de code source", + "viewletTitle": "{0} : {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..f4e6954050 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "Résultats des fichiers et des symboles", + "fileResults": "fichier de résultats" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..6196ad3af4 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, sélecteur de fichiers", + "searchResults": "résultats de la recherche" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..c71bacf852 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, sélecteur de symboles", + "symbols": "résultats des symboles", + "noSymbolsMatching": "Aucun symbole correspondant", + "noSymbolsWithoutInput": "Tapez pour rechercher des symboles" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..4388070906 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "entrée", + "useIgnoreFilesDescription": "Utiliser Ignorer les fichiers", + "useExcludeSettingsDescription": "Utiliser Exclure les paramètres" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..6a3a04581b --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (Replace Preview)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..5178a29021 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Atteindre le symbole dans l'espace de travail...", + "name": "Rechercher", + "showSearchViewlet": "Afficher la zone de recherche", + "view": "Affichage", + "findInFiles": "Chercher dans les fichiers", + "openAnythingHandlerDescription": "Accéder au fichier", + "openSymbolDescriptionNormal": "Atteindre le symbole dans l'espace de travail", + "searchOutputChannelTitle": "Rechercher", + "searchConfigurationTitle": "Rechercher", + "exclude": "Configurez les modèles Glob pour exclure les fichiers et les dossiers des recherches. Hérite de tous les modèles Glob à partir du paramètre files.exclude.", + "exclude.boolean": "Modèle Glob auquel les chemins de fichiers doivent correspondre. Affectez la valeur true ou false pour activer ou désactiver le modèle.", + "exclude.when": "Vérification supplémentaire des frères d'un fichier correspondant. Utilisez $(basename) comme variable pour le nom de fichier correspondant.", + "useRipgrep": "Contrôle si ripgrep doit être utilisé dans la recherche de texte", + "useIgnoreFilesByDefault": "Indique s'il faut utiliser les fichiers .gitignore et .ignore par défaut en cas de recherche dans un nouvel espace de travail.", + "search.quickOpen.includeSymbols": "Configurez l'ajout des résultats d'une recherche de symboles globale dans le fichier de résultats pour Quick Open." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..5988fbb634 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "Afficher le prochain Include Pattern de recherche", + "previousSearchIncludePattern": "Afficher le précédent Include Pattern de recherche", + "nextSearchExcludePattern": "Afficher le prochain Exclude Pattern de recherche", + "previousSearchExcludePattern": "Afficher le précédent Exclude Pattern de recherche", + "nextSearchTerm": "Afficher le terme de recherche suivant", + "previousSearchTerm": "Afficher le terme de recherche précédent", + "focusNextInputBox": "Focus sur la zone d'entrée suivante", + "focusPreviousInputBox": "Focus sur la zone d'entrée précédente", + "replaceInFiles": "Remplacer dans les fichiers", + "findInWorkspace": "Trouver dans l’espace de travail...", + "findInFolder": "Trouver dans le dossier...", + "RefreshAction.label": "Actualiser", + "ClearSearchResultsAction.label": "Effacer les résultats de la recherche", + "FocusNextSearchResult.label": "Focus sur le résultat de la recherche suivant", + "FocusPreviousSearchResult.label": "Focus sur le résultat de la recherche précédent", + "RemoveAction.label": "Supprimer", + "file.replaceAll.label": "Tout remplacer", + "match.replace.label": "Remplacer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..9d31c3a3e5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "Autres fichiers", + "searchFileMatches": "{0} fichiers trouvés", + "searchFileMatch": "{0} fichier trouvé", + "searchMatches": "{0} correspondances trouvées", + "searchMatch": "{0} correspondance trouvée", + "folderMatchAriaLabel": "{0} correspondances dans le dossier racine {1}, Résultat de la recherche", + "fileMatchAriaLabel": "{0} correspondances dans le fichier {1} du dossier {2}, Résultat de la recherche", + "replacePreviewResultAria": "Remplacer le terme {0} par {1} à la position de colonne {2} dans la ligne avec le texte {3}", + "searchResultAria": "Terme {0} trouvé à la position de colonne {1} dans la ligne avec le texte {2}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..c59fa10eb5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "Activer/désactiver les détails de la recherche", + "searchScope.includes": "fichiers à inclure", + "label.includes": "Modèles d'inclusion de recherche", + "searchScope.excludes": "fichiers à exclure", + "label.excludes": "Modèles d'exclusion de recherche", + "replaceAll.confirmation.title": "Tout remplacer", + "replaceAll.confirm.button": "Remplacer", + "replaceAll.occurrence.file.message": "{0} occurrence remplacée dans {1} fichier par '{2}'.", + "removeAll.occurrence.file.message": "{0} occurrence remplacée dans {1} fichier.", + "replaceAll.occurrence.files.message": "{0} occurrence remplacée dans {1} fichiers par '{2}'.", + "removeAll.occurrence.files.message": "{0} occurrence remplacée dans {1} fichiers.", + "replaceAll.occurrences.file.message": "{0} occurrences remplacées dans {1} fichier par '{2}'.", + "removeAll.occurrences.file.message": "{0} occurrences remplacées dans {1} fichier.", + "replaceAll.occurrences.files.message": "{0} occurrences remplacées dans {1} fichiers par '{2}'.", + "removeAll.occurrences.files.message": "{0} occurrences remplacées dans {1} fichiers.", + "removeAll.occurrence.file.confirmation.message": "Remplacer {0} occurrence dans {1} fichier par '{2}' ?", + "replaceAll.occurrence.file.confirmation.message": "Remplacer {0} occurrence dans {1} fichier ?", + "removeAll.occurrence.files.confirmation.message": "Remplacer {0} occurrence dans {1} fichiers par '{2}' ?", + "replaceAll.occurrence.files.confirmation.message": "Remplacer {0} occurrence dans {1} fichiers ?", + "removeAll.occurrences.file.confirmation.message": "Remplacer {0} occurrences dans {1} fichier par '{2}' ?", + "replaceAll.occurrences.file.confirmation.message": "Remplacer {0} occurrences dans {1} fichier ?", + "removeAll.occurrences.files.confirmation.message": "Remplacer {0} occurrences dans {1} fichiers par '{2}' ?", + "replaceAll.occurrences.files.confirmation.message": "Remplacer {0} occurrences dans {1} fichiers ?", + "treeAriaLabel": "Résultats de la recherche", + "searchPathNotFoundError": "Chemin de recherche introuvable : {0}", + "searchMaxResultsWarning": "Le jeu de résultats contient uniquement un sous-ensemble de toutes les correspondances. Soyez plus précis dans votre recherche de façon à limiter les résultats retournés.", + "searchCanceled": "La recherche a été annulée avant l'obtention de résultats - ", + "noResultsIncludesExcludes": "Résultats introuvables pour '{0}' excluant '{1}' - ", + "noResultsIncludes": "Résultats introuvables dans '{0}' - ", + "noResultsExcludes": "Résultats introuvables avec l'exclusion de '{0}' - ", + "noResultsFound": "Aucun résultat trouvé. Vérifiez vos paramètres pour les exclusions configurées et les fichiers ignorés - ", + "rerunSearch.message": "Rechercher à nouveau", + "rerunSearchInAll.message": "Rechercher à nouveau dans tous les fichiers", + "openSettings.message": "Ouvrir les paramètres", + "openSettings.learnMore": "En savoir plus", + "ariaSearchResultsStatus": "La recherche a retourné {0} résultats dans {1} fichiers", + "search.file.result": "{0} résultat dans {1} fichier", + "search.files.result": "{0} résultat dans {1} fichiers", + "search.file.results": "{0} résultats dans {1} fichier", + "search.files.results": "{0} résultats dans {1} fichiers", + "searchWithoutFolder": "Vous n'avez pas encore ouvert de dossier. Seuls les fichiers ouverts font l'objet de recherches - ", + "openFolder": "Ouvrir le dossier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..ff18f29c09 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "Tout remplacer (soumettre la recherche pour activer)", + "search.action.replaceAll.enabled.label": "Tout remplacer", + "search.replace.toggle.button.title": "Activer/désactiver le remplacement", + "label.Search": "Rechercher : tapez le terme de recherche, puis appuyez sur Entrée pour lancer la recherche, ou sur Échap pour l'annuler", + "search.placeHolder": "Rechercher", + "label.Replace": "Remplacer : tapez le terme de remplacement, puis appuyez sur Entrée pour avoir un aperçu, ou sur Échap pour l'annuler", + "search.replace.placeHolder": "Remplacer", + "regexp.validationFailure": "L'expression correspond à tout" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/fra/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..fca68649cb --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "Aucun dossier dans l’espace de travail avec le nom {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..e693aa8526 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "Ajoute des extraits de code.", + "vscode.extension.contributes.snippets-language": "Identificateur de langage pour lequel cet extrait de code est ajouté.", + "vscode.extension.contributes.snippets-path": "Chemin du fichier d'extraits de code. Le chemin est relatif au dossier d'extensions et commence généralement par './snippets/'.", + "invalid.language": "Langage inconnu dans 'contributes.{0}.language'. Valeur fournie : {1}", + "invalid.path.0": "Chaîne attendue dans 'contributes.{0}.path'. Valeur fournie : {1}", + "invalid.path.1": "'contributes.{0}.path' ({1}) est censé être inclus dans le dossier ({2}) de l'extension. Cela risque de rendre l'extension non portable.", + "badVariableUse": "L'extrait de code \"{0}\" confond très probablement les variables et les espaces réservés d'extrait de code. Consultez https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax pour plus d'informations." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..20268f478d --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "Insérer un extrait de code" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..9dd85a849f --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "Sélectionner le langage de l'extrait de code", + "openSnippet.errorOnCreate": "Impossible de créer {0}", + "openSnippet.label": "Ouvrir les extraits de code utilisateur", + "preferences": "Préférences", + "snippetSchema.json.default": "Extrait de code vide", + "snippetSchema.json": "Configuration de l'extrait de code utilisateur", + "snippetSchema.json.prefix": "Préfixe à utiliser durant la sélection de l'extrait de code dans IntelliSense", + "snippetSchema.json.body": "Contenu de l'extrait de code. Utilisez '$1', '${1:defaultText}' pour définir les positions du curseur, utilisez '$0' pour la position finale du curseur. Insérez les valeurs de variable avec '${varName}' et '${varName:defaultText}', par ex., 'Il s'agit du fichier : $TM_FILENAME'.", + "snippetSchema.json.description": "Description de l'extrait de code." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..c95cc7d2ec --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "Extrait de code utilisateur", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0}, {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..0b7118ed90 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "Insérez les extraits de code quand leurs préfixes correspondent. Fonctionne mieux quand la fonctionnalité 'quickSuggestions' n'est pas activée." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..5ba7ea6da9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "Aidez-nous à améliorer le support de {0}", + "takeShortSurvey": "Répondre à une enquête rapide", + "remindLater": "Me le rappeler plus tard", + "neverAgain": "Ne plus afficher" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..6b412e4bd6 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Acceptez-vous de répondre à une enquête rapide ?", + "takeSurvey": "Répondre à l'enquête", + "remindLater": "Me le rappeler plus tard", + "neverAgain": "Ne plus afficher" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..6848f3a98c --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "Aucune tâche correspondante" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..b639a79b36 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, tasks", + "recentlyUsed": "tâches récemment utilisées", + "configured": "tâches configurées", + "detected": "tâches détectées", + "customizeTask": "Configurer une tâche" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..7adaa57de7 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Tapez le nom d'une tâche à redémarrer", + "noTasksMatching": "Aucune tâche correspondante", + "noTasksFound": "Aucune tâche à recommencer n'a été trouvée" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..9fd4910bef --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Tapez le nom d'une tâche à exécuter", + "noTasksMatching": "No tasks matching", + "noTasksFound": "Tâches introuvables" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..0f8335eda9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Type the name of a task to terminate", + "noTasksMatching": "Aucune tâche correspondante", + "noTasksFound": "No tasks to terminate found" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..6848f3a98c --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "Aucune tâche correspondante" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..9cb9708819 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Warning: options.cwd must be of type string. Ignoring value {0}\n", + "ConfigurationParser.noargs": "Erreur : les arguments de commande doivent correspondre à un tableau de chaînes. La valeur fournie est :\n{0}", + "ConfigurationParser.noShell": "Avertissement : La configuration de l'interpréteur de commandes n'est prise en charge que durant l'exécution des tâches dans le terminal.", + "ConfigurationParser.noName": "Erreur : le détecteur de problèmes de correspondance dans la portée de déclaration doit avoir un nom :\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Avertissement : le détecteur de problèmes de correspondance défini est inconnu. Les types pris en charge sont string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Erreur : référence à problemMatcher non valide : {0}\n", + "ConfigurationParser.noTaskName": "Erreur : les tâches doivent fournir une propriété taskName. La tâche va être ignorée.\n{0}\n", + "taskConfiguration.shellArgs": "Avertissement : La tâche '{0}' est une commande d'interpréteur de commandes, et le nom de la commande ou l'un de ses arguments contient des espaces non précédés d'un caractère d'échappement. Pour garantir une ligne de commande correcte, fusionnez les arguments dans la commande.", + "taskConfiguration.noCommandOrDependsOn": "Erreur : La tâche '{0}' ne spécifie ni une commande, ni une propriété dependsOn. La tâche est ignorée. Sa définition est :\n{1}", + "taskConfiguration.noCommand": "Erreur : La tâche '{0}' ne définit aucune commande. La tâche va être ignorée. Sa définition est :\n{1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..d9fb222398 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "Type de tâche réel", + "TaskDefinition.properties": "Propriétés supplémentaires du type de tâche", + "TaskTypeConfiguration.noType": "La propriété 'taskType' obligatoire est manquante dans la configuration du type de tâche", + "TaskDefinitionExtPoint": "Ajoute des types de tâche" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..639dbbbecc --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": "Exécute une commande de génération .NET Core", + "msbuild": "Exécute la cible de génération", + "externalCommand": "Exemple d'exécution d'une commande externe arbitraire", + "Maven": "Exécute les commandes Maven courantes" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..66f1bb9efc --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "Options de commande supplémentaires", + "JsonSchema.options.cwd": "Répertoire de travail actif du programme ou script exécuté. En cas d'omission, la racine de l'espace de travail actif de Code est utilisée.", + "JsonSchema.options.env": "Environnement du programme ou de l'interpréteur de commandes exécuté. En cas d'omission, l'environnement du processus parent est utilisé.", + "JsonSchema.shellConfiguration": "Configure l'interpréteur de commandes à utiliser.", + "JsonSchema.shell.executable": "Interpréteur de commandes à utiliser.", + "JsonSchema.shell.args": "Arguments de l'interpréteur de commandes.", + "JsonSchema.command": "Commande à exécuter. Il peut s'agir d'un programme externe ou d'une commande d'interpréteur de commandes.", + "JsonSchema.tasks.args": "Arguments passés à la commande quand cette tâche est appelée.", + "JsonSchema.tasks.taskName": "Nom de la tâche", + "JsonSchema.tasks.windows": "Configuration de commande spécifique à Windows", + "JsonSchema.tasks.mac": "Configuration de commande spécifique à Mac", + "JsonSchema.tasks.linux": "Configuration de commande spécifique à Linux", + "JsonSchema.tasks.suppressTaskName": "Contrôle si le nom de la tâche est ajouté en tant qu'argument de la commande. En cas d'omission, la valeur définie globalement est utilisée.", + "JsonSchema.tasks.showOutput": "Contrôle si la sortie de la tâche en cours d'exécution est affichée ou non. En cas d'omission, la valeur définie globalement est utilisée.", + "JsonSchema.echoCommand": "Contrôle si la commande exécutée fait l'objet d'un écho dans la sortie. La valeur par défaut est false.", + "JsonSchema.tasks.watching.deprecation": "Déconseillé. Utilisez isBackground à la place.", + "JsonSchema.tasks.watching": "Spécifie si la tâche exécutée est persistante, et si elle surveille le système de fichiers.", + "JsonSchema.tasks.background": "Spécifie si la tâche exécutée est persistante, et si elle s'exécute en arrière-plan.", + "JsonSchema.tasks.promptOnClose": "Spécifie si l'utilisateur est prévenu quand VS Code se ferme avec une tâche en cours d'exécution.", + "JsonSchema.tasks.build": "Mappe cette tâche à la commande de génération par défaut de Code.", + "JsonSchema.tasks.test": "Mappe cette tâche à la commande de test par défaut de Code.", + "JsonSchema.tasks.matchers": "Détecteur(s) de problèmes de correspondance à utiliser. Il peut s'agir d'une chaîne ou d'une définition de détecteur de problèmes de correspondance, ou encore d'un tableau de chaînes et de détecteurs de problèmes de correspondance.", + "JsonSchema.args": "Arguments supplémentaires passés à la commande.", + "JsonSchema.showOutput": "Contrôle si la sortie de la tâche en cours d'exécution est affichée ou non. En cas d'omission, 'always' est utilisé.", + "JsonSchema.watching.deprecation": "Déconseillé. Utilisez isBackground à la place.", + "JsonSchema.watching": "Spécifie si la tâche exécutée est persistante, et si elle surveille le système de fichiers.", + "JsonSchema.background": "Spécifie si la tâche exécutée est persistante, et si elle s'exécute en arrière-plan.", + "JsonSchema.promptOnClose": "Spécifie si l'utilisateur est prévenu quand VS Code se ferme avec une tâche s'exécutant en arrière-plan.", + "JsonSchema.suppressTaskName": "Contrôle si le nom de la tâche est ajouté en tant qu'argument de la commande. La valeur par défaut est false.", + "JsonSchema.taskSelector": "Préfixe indiquant qu'un argument est une tâche.", + "JsonSchema.matchers": "Détecteur(s) de problèmes de correspondance à utiliser. Il peut s'agir d'une chaîne ou d'une définition de détecteur de problèmes de correspondance, ou encore d'un tableau de chaînes et de détecteurs de problèmes de correspondance.", + "JsonSchema.tasks": "Configurations de la tâche. Il s'agit généralement d'enrichissements d'une tâche déjà définie dans l'exécuteur de tâches externe." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..628110ae70 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "Numéro de version de la configuration", + "JsonSchema._runner": "Le runner est dégradé. Utiliser la propriété runner officielle", + "JsonSchema.runner": "Définit si la tâche est exécutée sous forme de processus, et si la sortie s'affiche dans la fenêtre de sortie ou dans le terminal.", + "JsonSchema.windows": "Configuration de commande spécifique à Windows", + "JsonSchema.mac": "Configuration de commande spécifique à Mac", + "JsonSchema.linux": "Configuration de commande spécifique à Linux", + "JsonSchema.shell": "Spécifie si la commande est une commande d'interpréteur de commandes ou un programme externe. La valeur par défaut est false, en cas d'omission." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..64c449c29e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "Spécifie si la commande est une commande d'interpréteur de commandes ou un programme externe. La valeur par défaut est false, en cas d'omission.", + "JsonSchema.tasks.isShellCommand.deprecated": "La propriété isShellCommand est dépréciée. Utilisez à la place la propriété de type de la tâche et la propriété d'interpréteur de commandes dans les options. Consultez également les notes de publication 1.14.", + "JsonSchema.tasks.dependsOn.string": "Autre tâche dont cette tâche dépend.", + "JsonSchema.tasks.dependsOn.array": "Autres tâches dont cette tâche dépend.", + "JsonSchema.tasks.presentation": "Configure le panneau utilisé pour présenter la sortie de la tâche et lit son entrée.", + "JsonSchema.tasks.presentation.echo": "Contrôle si la commande exécutée est répercutée dans le panneau. La valeur par défaut est true.", + "JsonSchema.tasks.presentation.focus": "Contrôle si le panneau reçoit le focus. La valeur par défaut est false. Si la valeur est true, le panneau est également affiché.", + "JsonSchema.tasks.presentation.reveal.always": "Toujours afficher le terminal quand cette tâche est exécutée.", + "JsonSchema.tasks.presentation.reveal.silent": "Afficher uniquement le terminal si aucun détecteur de problèmes de correspondance n’est associé à la tâche et qu'une erreur se produit pendant son exécution.", + "JsonSchema.tasks.presentation.reveal.never": "Ne jamais afficher le terminal quand cette tâche est exécutée.", + "JsonSchema.tasks.presentation.reveals": "Contrôle si le panneau qui exécute la tâche est affiché ou non. La valeur par défaut est \"always\".", + "JsonSchema.tasks.presentation.instance": "Contrôle si le panneau est partagé entre les tâches, dédié à cette tâche ou si un panneau est créé à chaque exécution.", + "JsonSchema.tasks.terminal": "La propriété de terminal est dépréciée. Utilisez la présentation à la place", + "JsonSchema.tasks.group.kind": "Groupe d'exécution de la tâche.", + "JsonSchema.tasks.group.isDefault": "Définit si cette tâche est la tâche par défaut du groupe.", + "JsonSchema.tasks.group.defaultBuild": "Marque la tâche comme tâche de génération par défaut.", + "JsonSchema.tasks.group.defaultTest": "Marque la tâche comme tâche de test par défaut.", + "JsonSchema.tasks.group.build": "Marque la tâche comme tâche de génération accessible par la commande 'Exécuter la tâche de génération'.", + "JsonSchema.tasks.group.test": "Marque la tâche comme tâche de test accessible par la commande 'Exécuter la tâche de test'.", + "JsonSchema.tasks.group.none": "N'assigne la tâche à aucun groupe", + "JsonSchema.tasks.group": "Définit le groupe d'exécution auquel la tâche appartient. Prend en charge \"build\" pour l'ajouter au groupe de génération et \"test\" pour l'ajouter au groupe de test.", + "JsonSchema.tasks.type": "Définit si la tâche est exécutée sous forme de processus ou d'une commande d'un interpréteur de commandes. La valeur par défaut est un processus.", + "JsonSchema.version": "Numéro de version de la configuration.", + "JsonSchema.tasks.identifier": "Identificateur défini par l'utilisateur pour référencer la tâche dans launch.json ou une clause dependsOn.", + "JsonSchema.tasks.taskLabel": "Étiquette de la tâche", + "JsonSchema.tasks.taskName": "Nom de la tâche", + "JsonSchema.tasks.taskName.deprecated": "La propriété de nom de la tâche est dépréciée. Utilisez la propriété d'étiquette à la place.", + "JsonSchema.tasks.background": "Spécifie si la tâche exécutée est persistante, et si elle s'exécute en arrière-plan.", + "JsonSchema.tasks.promptOnClose": "Spécifie si l'utilisateur est prévenu quand VS Code se ferme avec une tâche en cours d'exécution.", + "JsonSchema.tasks.matchers": "Détecteur(s) de problèmes de correspondance à utiliser. Il peut s'agir d'une chaîne ou d'une définition de détecteur de problèmes de correspondance, ou encore d'un tableau de chaînes et de détecteurs de problèmes de correspondance.", + "JsonSchema.customizations.customizes.type": "Type de tâche à personnaliser", + "JsonSchema.tasks.customize.deprecated": "La propriété de personnalisation est dépréciée. Consultez les notes de publication 1.14 pour savoir comment migrer vers la nouvelle approche de personnalisation des tâches", + "JsonSchema.tasks.showOputput.deprecated": "La propriété showOutput est dépréciée. Utilisez à la place la propriété d'affichage au sein de la propriété de présentation. Consultez également les notes de publication 1.14.", + "JsonSchema.tasks.echoCommand.deprecated": "La propriété echoCommand est dépréciée. Utilisez à la place la propriété d'écho au sein de la propriété de présentation. Consultez également les notes de publication 1.14.", + "JsonSchema.tasks.suppressTaskName.deprecated": "La propriété suppressTaskName est obsolète. Utiliser la ligne de commande avec ses arguments dans la tâche à la place. Voir aussi les notes de version 1.14.", + "JsonSchema.tasks.isBuildCommand.deprecated": "La propriété isBuildCommand est dépréciée. Utilisez la propriété de groupe à la place. Consultez également les notes de publication 1.14.", + "JsonSchema.tasks.isTestCommand.deprecated": "La propriété isTestCommand est dépréciée. Utilisez la propriété de groupe à la place. Consultez également les notes de publication 1.14.", + "JsonSchema.tasks.taskSelector.deprecated": "La propriété taskSelector est obsolète. Utiliser la ligne de commande avec ses arguments dans la tâche à la place. Voir aussi les notes de version 1.14.", + "JsonSchema.windows": "Configuration de commande spécifique à Windows", + "JsonSchema.mac": "Configuration de commande spécifique à Mac", + "JsonSchema.linux": "Configuration de commande spécifique à Linux" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..80eef44afa --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "Tâches", + "ConfigureTaskRunnerAction.noWorkspace": "Les tâches ne sont disponibles que dans un dossier d'espace de travail.", + "ConfigureTaskRunnerAction.quickPick.template": "Sélectionner un exécuteur de tâches", + "ConfigureTaskRunnerAction.autoDetecting": "Détection automatique des tâches pour {0}", + "ConfigureTaskRunnerAction.autoDetect": "En raison de l'échec de la détection automatique du système de tâche, le modèle par défaut va être utilisé. Pour plus d'informations, consultez la sortie de la tâche.", + "ConfigureTaskRunnerAction.autoDetectError": "La détection automatique du système de tâche a produit des erreurs. Consultez la sortie de la tâche pour plus d'informations.", + "ConfigureTaskRunnerAction.failed": "Impossible de créer le fichier 'tasks.json' dans le dossier '.vscode'. Pour plus d'informations, consultez la sortie de la tâche.", + "ConfigureTaskRunnerAction.label": "Configurer l'exécuteur de tâches", + "ConfigureBuildTaskAction.label": "Configurer une tâche de build", + "CloseMessageAction.label": "Fermer", + "ShowTerminalAction.label": "Afficher le terminal", + "problems": "Problèmes", + "manyMarkers": "99", + "runningTasks": "Afficher les tâches en cours d'exécution", + "tasks": "Tâches", + "TaskSystem.noHotSwap": "Le changement du moteur d’exécution de tâches nécessite de recharger la fenêtre", + "TaskService.noBuildTask1": "Aucune tâche de build définie. Marquez une tâche avec 'isBuildCommand' dans le fichier tasks.json.", + "TaskService.noBuildTask2": "Aucune tâche de génération définie. Marquez une tâche comme groupe 'build' dans le fichier tasks.json.", + "TaskService.noTestTask1": "Aucune tâche de test définie. Marquez une tâche avec 'isTestCommand' dans le fichier tasks.json.", + "TaskService.noTestTask2": "Aucune tâche de test définie. Marquez une tâche comme groupe 'test' dans le fichier tasks.json.", + "TaskServer.noTask": "La tâche {0} à exécuter est introuvable.", + "TaskService.attachProblemMatcher.continueWithout": "Continuer sans analyser la sortie de la tâche", + "TaskService.attachProblemMatcher.never": "Ne jamais vérifier la sortie de la tâche", + "TaskService.attachProblemMatcher.learnMoreAbout": "En savoir plus sur la sortie de la tâche de numérisation", + "selectProblemMatcher": "Sélectionner pour quel type d’erreurs et d’avertissements analyser la sortie de la tâche", + "customizeParseErrors": "La configuration de tâche actuelle contient des erreurs. Corrigez-les avant de personnaliser une tâche. ", + "moreThanOneBuildTask": "De nombreuses tâches de génération sont définies dans le fichier tasks.json. Exécution de la première.\n", + "TaskSystem.activeSame.background": "La tâche '{0}' est déjà active et en mode arrière-plan. Pour la terminer, utiliser `Terminer la Tâche...` » dans le menu Tâches.", + "TaskSystem.activeSame.noBackground": "La tâche '{0}' est déjà active. Pour la terminer, il utilise `Terminer la Tâche...` dans le menu Tâches.", + "TaskSystem.active": "Une tâche est déjà en cours d'exécution. Terminez-la avant d'exécuter une autre tâche.", + "TaskSystem.restartFailed": "Échec de la fin de l'exécution de la tâche {0}", + "TaskSystem.configurationErrors": "Erreur : la configuration de tâche fournie comporte des erreurs de validation et ne peut pas être utilisée. Corrigez d'abord les erreurs.", + "TaskSystem.invalidTaskJson": "Erreur : le fichier tasks.json contient des erreurs de syntaxe. Corrigez-les avant d'exécuter une tâche.\n", + "TaskSystem.runningTask": "Une tâche est en cours d'exécution. Voulez-vous la terminer ?", + "TaskSystem.terminateTask": "&&Terminer la tâche", + "TaskSystem.noProcess": "La tâche lancée n'existe plus. Si la tâche a engendré des processus en arrière-plan, la sortie de VS Code risque de donner lieu à des processus orphelins. Pour éviter ce problème, démarrez le dernier processus en arrière-plan avec un indicateur d'attente.", + "TaskSystem.exitAnyways": "&&Quitter quand même", + "TerminateAction.label": "Terminer la tâche", + "TaskSystem.unknownError": "Une erreur s'est produite durant l'exécution d'une tâche. Pour plus d'informations, consultez le journal des tâches.", + "TaskService.noWorkspace": "Les tâches ne sont disponibles que dans un dossier d'espace de travail.", + "recentlyUsed": "tâches récemment utilisées", + "configured": "tâches configurées", + "detected": "tâches détectées", + "TaskService.fetchingBuildTasks": "Récupération des tâches de génération...", + "TaskService.noBuildTaskTerminal": "Aucune tâche de génération. Appuyez sur 'Configurer une tâche de génération' pour en définir une.", + "TaskService.pickBuildTask": "Sélectionner la tâche de génération à exécuter", + "TaskService.fetchingTestTasks": "Récupération des tâches de test...", + "TaskService.noTestTaskTerminal": "Aucune tâche de test. Appuyez sur 'Configurer un exécuteur de tâches' pour en définir un.", + "TaskService.pickTestTask": "Sélectionner la tâche de test à exécuter", + "TaskService.noTaskRunning": "Aucune tâche en cours d'exécution.", + "TaskService.tastToTerminate": "Sélectionner une tâche à terminer", + "TerminateAction.noProcess": "Le processus lancé n'existe plus. Si la tâche a engendré des tâches en arrière-plan, la sortie de VS Code risque de donner lieu à des processus orphelins.", + "TerminateAction.failed": "Échec de la fin de l'exécution de la tâche", + "TaskService.noTaskToRestart": "Aucune tâche à redémarrer.", + "TaskService.tastToRestart": "Sélectionner la tâche à redémarrer", + "TaskService.defaultBuildTaskExists": "{0} est déjà marquée comme tâche de génération par défaut.", + "TaskService.pickDefaultBuildTask": "Sélectionner la tâche à utiliser comme tâche de génération par défaut", + "TaskService.defaultTestTaskExists": "{0} est déjà marquée comme tâche de test par défaut.", + "TaskService.pickDefaultTestTask": "Sélectionner la tâche à utiliser comme tâche de test par défaut", + "TaskService.noTaskIsRunning": "Aucune tâche n’est en cours d’exécution.", + "TaskService.pickShowTask": "Sélectionner la tâche pour montrer sa sortie", + "ShowLogAction.label": "Afficher le journal des tâches", + "RunTaskAction.label": "Exécuter la tâche", + "RestartTaskAction.label": "Redémarrer la tâche en cours d'exécution", + "ShowTasksAction.label": "Afficher les tâches en cours d'exécution", + "BuildAction.label": "Exécuter la tâche de génération", + "TestAction.label": "Exécuter la tâche de test", + "ConfigureDefaultBuildTask.label": "Configurer la tâche de génération par défaut", + "ConfigureDefaultTestTask.label": "Configurer la tâche de test par défaut", + "quickOpen.task": "Exécuter la tâche" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..a912292a48 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "Tâches" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..24a921cd58 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "Une erreur inconnue s'est produite durant l'exécution d'une tâche. Pour plus d'informations, consultez le journal de sortie des tâches.", + "TerminalTaskSystem.terminalName": "Tâche - {0}", + "reuseTerminal": "Le terminal sera réutilisé par les tâches, appuyez sur une touche pour le fermer.", + "TerminalTaskSystem": "Impossible d'exécuter une commande d'interpréteur de commandes sur un lecteur UNC.", + "unkownProblemMatcher": "Impossible de résoudre le détecteur de problèmes {0}. Le détecteur est ignoré" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..2a5698b6ad --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "L'exécution de gulp --tasks-simple n'a listé aucune tâche. Avez-vous exécuté npm install ?", + "TaskSystemDetector.noJakeTasks": "L'exécution de jake --tasks n'a listé aucune tâche. Avez-vous exécuté npm install ?", + "TaskSystemDetector.noGulpProgram": "Gulp n'est pas installé sur votre système. Exécutez npm install -g gulp pour l'installer.", + "TaskSystemDetector.noJakeProgram": "Jake n'est pas installé sur votre système. Exécutez npm install -g jake pour l'installer.", + "TaskSystemDetector.noGruntProgram": "Grunt n'est pas installé sur votre système. Exécutez npm install -g grunt pour l'installer.", + "TaskSystemDetector.noProgram": "Le programme {0} est introuvable. Message : {1}", + "TaskSystemDetector.buildTaskDetected": "La tâche de génération nommée '{0}' a été détectée.", + "TaskSystemDetector.testTaskDetected": "La tâche de test nommée '{0}' a été détectée." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..19311d3aad --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "Une erreur inconnue s'est produite durant l'exécution d'une tâche. Pour plus d'informations, consultez le journal de sortie des tâches.", + "TaskRunnerSystem.watchingBuildTaskFinished": "\nFin du suivi des tâches de génération.", + "TaskRunnerSystem.childProcessError": "Failed to launch external program {0} {1}.", + "TaskRunnerSystem.cancelRequested": "\nThe task '{0}' was terminated per user request.", + "unkownProblemMatcher": "Impossible de résoudre le détecteur de problèmes {0}. Le détecteur est ignoré" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..605e0d50c4 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Warning: options.cwd must be of type string. Ignoring value {0}\n", + "ConfigurationParser.noargs": "Erreur : les arguments de commande doivent correspondre à un tableau de chaînes. La valeur fournie est :\n{0}", + "ConfigurationParser.noShell": "Avertissement : La configuration de l'interpréteur de commandes n'est prise en charge que durant l'exécution des tâches dans le terminal.", + "ConfigurationParser.noName": "Erreur : le détecteur de problèmes de correspondance dans la portée de déclaration doit avoir un nom :\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Avertissement : le détecteur de problèmes de correspondance défini est inconnu. Les types pris en charge sont string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Erreur : référence à problemMatcher non valide : {0}\n", + "ConfigurationParser.noTaskType": "Erreur : la configuration des tâches doit avoir une propriété de type. La configuration est ignorée.\n{0}\n", + "ConfigurationParser.noTypeDefinition": "Erreur : aucun type de tâche '{0}' enregistré. Avez-vous oublié d'installer une extension incluant le fournisseur de tâches correspondant ?", + "ConfigurationParser.notCustom": "Erreur : la tâche n'est pas déclarée comme une tâche personnalisée. La configuration est ignorée.\n{0}\n", + "ConfigurationParser.noTaskName": "Erreur : les tâches doivent fournir une propriété taskName. La tâche va être ignorée.\n{0}\n", + "taskConfiguration.shellArgs": "Avertissement : La tâche '{0}' est une commande d'interpréteur de commandes, et le nom de la commande ou l'un de ses arguments contient des espaces non précédés d'un caractère d'échappement. Pour garantir une ligne de commande correcte, fusionnez les arguments dans la commande.", + "taskConfiguration.noCommandOrDependsOn": "Erreur : La tâche '{0}' ne spécifie ni une commande, ni une propriété dependsOn. La tâche est ignorée. Sa définition est :\n{1}", + "taskConfiguration.noCommand": "Erreur : La tâche '{0}' ne définit aucune commande. La tâche va être ignorée. Sa définition est :\n{1}", + "TaskParse.noOsSpecificGlobalTasks": "Task Version 2.0.0 ne supporte pas les tâches spécifiques globales du système d'exploitation. Convertissez-les en une tâche en une commande spécifique du système d'exploitation. Les tâches concernées sont : {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..2e22add4ee --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0}, sélecteur de terminaux", + "termCreateEntryAriaLabel": "{0}, créer un terminal", + "'workbench.action.terminal.newplus": "$(plus) Créer un terminal intégré", + "noTerminalsMatching": "Aucun terminal correspondant", + "noTerminalsFound": "Aucun terminal ouvert" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..a16871adc9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "Afficher tous les terminaux ouverts", + "terminalIntegratedConfigurationTitle": "Terminal intégré", + "terminal.integrated.shell.linux": "Chemin de l'interpréteur de commandes utilisé par le terminal sur Linux.", + "terminal.integrated.shellArgs.linux": "Arguments de ligne de commande à utiliser sur le terminal Linux.", + "terminal.integrated.shell.osx": "Chemin de l'interpréteur de commandes utilisé par le terminal sur OS X.", + "terminal.integrated.shellArgs.osx": "Arguments de ligne de commande à utiliser sur le terminal OS X.", + "terminal.integrated.shell.windows": "Le chemin du shell que le terminal utilise sous Windows. Lors de l’utilisation de shells fournies avec Windows (cmd, PowerShell ou Bash sur Ubuntu).", + "terminal.integrated.shellArgs.windows": "Arguments de ligne de commande à utiliser sur le terminal Windows.", + "terminal.integrated.rightClickCopyPaste": "Une fois le paramètre défini, le menu contextuel cesse de s'afficher quand l'utilisateur clique avec le bouton droit dans le terminal. À la place, une opération de copie est effectuée quand il existe une sélection, et une opération de collage est effectuée en l'absence de sélection.", + "terminal.integrated.fontFamily": "Contrôle la famille de polices du terminal. La valeur par défaut est la valeur associée à editor.fontFamily.", + "terminal.integrated.fontLigatures": "Contrôle si les ligatures de police sont activées sur le terminal.", + "terminal.integrated.fontSize": "Contrôle la taille de police en pixels du terminal.", + "terminal.integrated.lineHeight": "Contrôle la hauteur de ligne du terminal. La multiplication de ce nombre par la taille de police du terminal permet d'obtenir la hauteur de ligne réelle en pixels.", + "terminal.integrated.enableBold": "Indique s'il faut activer ou non le texte en gras dans le terminal, ce qui nécessite une prise en charge de l'interpréteur de commandes du terminal.", + "terminal.integrated.cursorBlinking": "Contrôle si le curseur du terminal clignote.", + "terminal.integrated.cursorStyle": "Contrôle le style du curseur du terminal.", + "terminal.integrated.scrollback": "Contrôle la quantité maximale de lignes que le terminal conserve dans sa mémoire tampon.", + "terminal.integrated.setLocaleVariables": "Contrôle si les variables locales sont définies au démarrage du terminal. La valeur par défaut est true sur OS X, false sur les autres plateformes.", + "terminal.integrated.cwd": "Chemin explicite de lancement du terminal. Il est utilisé comme répertoire de travail actif du processus d'interpréteur de commandes. Cela peut être particulièrement utile dans les paramètres d'espace de travail, si le répertoire racine n'est pas un répertoire de travail actif adéquat.", + "terminal.integrated.confirmOnExit": "Indique s'il est nécessaire de confirmer l'existence de sessions de terminal actives au moment de quitter.", + "terminal.integrated.commandsToSkipShell": "Ensemble d'ID de commandes dont les combinaisons de touches sont gérées par Code au lieu d'être envoyées à l'interpréteur de commandes. Cela permet d'utiliser des combinaisons de touches qui sont normalement consommées par l'interpréteur de commandes et d'obtenir le même résultat quand le terminal n'a pas le focus, par exemple Ctrl+P pour lancer Quick Open.", + "terminal.integrated.env.osx": "Objet avec les variables d’environnement qui seront ajoutées au processus VS Code pour être utilisées par le terminal sous OS X", + "terminal.integrated.env.linux": "Objet avec les variables d’environnement qui seront ajoutées au processus VS Code pour être utilisées par le terminal sous Linux", + "terminal.integrated.env.windows": "Objet avec les variables d’environnement qui seront ajoutées au processus VS Code pour être utilisées par le terminal sous Windows", + "terminal": "Terminal", + "terminalCategory": "Terminal", + "viewCategory": "Affichage" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..245c9c6eef --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "Activer/désactiver le terminal intégré", + "workbench.action.terminal.kill": "Tuer l'instance active du terminal", + "workbench.action.terminal.kill.short": "Tuer le terminal", + "workbench.action.terminal.quickKill": "Tuer l'instance de terminal", + "workbench.action.terminal.copySelection": "Copier la sélection", + "workbench.action.terminal.selectAll": "Tout Sélectionner", + "workbench.action.terminal.deleteWordLeft": "Supprimer le mot à gauche", + "workbench.action.terminal.deleteWordRight": "Supprimer le mot à droite", + "workbench.action.terminal.new": "Créer un terminal intégré", + "workbench.action.terminal.new.short": "Nouveau terminal", + "workbench.action.terminal.focus": "Focus sur le terminal", + "workbench.action.terminal.focusNext": "Focus sur le terminal suivant", + "workbench.action.terminal.focusAtIndex": "Focus sur le terminal {0}", + "workbench.action.terminal.focusPrevious": "Focus sur le terminal précédent", + "workbench.action.terminal.paste": "Coller dans le terminal actif", + "workbench.action.terminal.DefaultShell": "Sélectionner l'interpréteur de commandes par défaut", + "workbench.action.terminal.runSelectedText": "Exécuter le texte sélectionné dans le terminal actif", + "workbench.action.terminal.runActiveFile": "Exécuter le fichier actif dans le terminal actif", + "workbench.action.terminal.runActiveFile.noFile": "Seuls les fichiers sur disque peuvent être exécutés dans le terminal", + "workbench.action.terminal.switchTerminalInstance": "Changer d'instance de terminal", + "workbench.action.terminal.scrollDown": "Faire défiler vers le bas (ligne)", + "workbench.action.terminal.scrollDownPage": "Faire défiler vers le bas (page)", + "workbench.action.terminal.scrollToBottom": "Faire défiler jusqu'en bas", + "workbench.action.terminal.scrollUp": "Faire défiler vers le haut (ligne)", + "workbench.action.terminal.scrollUpPage": "Faire défiler vers le haut (page)", + "workbench.action.terminal.scrollToTop": "Faire défiler jusqu'en haut", + "workbench.action.terminal.clear": "Effacer", + "workbench.action.terminal.allowWorkspaceShell": "Autoriser la configuration de l'interpréteur de commandes de l'espace de travail", + "workbench.action.terminal.disallowWorkspaceShell": "Interdire la configuration de l'interpréteur de commandes de l'espace de travail", + "workbench.action.terminal.rename": "Renommer", + "workbench.action.terminal.rename.prompt": "Entrer le nom du terminal", + "workbench.action.terminal.focusFindWidget": "Focus sur le widget de recherche", + "workbench.action.terminal.hideFindWidget": "Masquer le widget de recherche", + "nextTerminalFindTerm": "Afficher le terme de recherche suivant", + "previousTerminalFindTerm": "Afficher le terme de recherche précédent", + "quickOpenTerm": "Terminal : Changer de terminal actif" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..5fd7e1420e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "Couleur d'arrière-plan du terminal, permet d'appliquer au terminal une couleur différente de celle du panneau.", + "terminal.foreground": "Couleur de premier plan du terminal.", + "terminalCursor.foreground": "La couleur de premier plan du curseur du terminal.", + "terminalCursor.background": "La couleur d’arrière-plan du curseur terminal. Permet de personnaliser la couleur d’un caractère recouvert par un curseur de bloc.", + "terminal.ansiColor": "Couleur ansi '{0}' dans le terminal." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..853b6611e2 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "Autorisez-vous le lancement de {0} (défini comme paramètre d'espace de travail) dans le terminal ?", + "allow": "Allow", + "disallow": "Disallow" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..3e40e91275 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Rechercher", + "placeholder.find": "Rechercher", + "label.previousMatchButton": "Correspondance précédente", + "label.nextMatchButton": "Correspondance suivante", + "label.closeButton": "Fermer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..1e76ceebd5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "Le terminal n'a aucune sélection à copier", + "terminal.integrated.exitedWithCode": "Le processus du terminal s'est achevé avec le code de sortie {0}", + "terminal.integrated.waitOnExit": "Appuyez sur une touche pour fermer le terminal", + "terminal.integrated.launchFailed": "Échec du lancement de la commande de traitement du terminal '{0}{1}' (code de sortie : {2})" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..08c5f1f76e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "Alt + clic pour suivre le lien", + "terminalLinkHandler.followLinkCmd": "Commande + clic pour suivre le lien", + "terminalLinkHandler.followLinkCtrl": "Ctrl + clic pour suivre le lien" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..d9307bd1ae --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "Copier", + "createNewTerminal": "Nouveau terminal", + "paste": "Coller", + "selectAll": "Tout Sélectionner", + "clear": "Effacer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..4301accf7c --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "Vous pouvez changer l'interpréteur de commandes par défaut du terminal en sélectionnant le bouton Personnaliser.", + "customize": "Personnaliser", + "cancel": "Annuler", + "never again": "OK, ne plus afficher", + "terminal.integrated.chooseWindowsShell": "Sélectionnez votre interpréteur de commandes de terminal favori. Vous pouvez le changer plus tard dans vos paramètres", + "terminalService.terminalCloseConfirmationSingular": "Il existe une session de terminal active. Voulez-vous la tuer ?", + "terminalService.terminalCloseConfirmationPlural": "Il existe {0} sessions de terminal actives. Voulez-vous les tuer ?", + "yes": "Oui" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..daf836a766 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "Thème de couleur", + "installColorThemes": "Installer des thèmes de couleurs supplémentaires...", + "themes.selectTheme": "Sélectionner un thème de couleur (flèches bas/haut pour afficher l'aperçu)", + "selectIconTheme.label": "Thème d'icône de fichier", + "installIconThemes": "Installer des thèmes d'icônes de fichiers supplémentaires...", + "noIconThemeLabel": "Aucun", + "noIconThemeDesc": "Désactiver les icônes de fichiers", + "problemChangingIconTheme": "Problème de définition du thème d'icône : {0}", + "themes.selectIconTheme": "Sélectionner un thème d'icône de fichier", + "generateColorTheme.label": "Générer le thème de couleur à partir des paramètres actuels", + "preferences": "Préférences", + "developer": "Développeur" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..28c96b590d --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "Cet espace de travail contient des paramètres qui ne peuvent être définis que dans les paramètres utilisateur. ({0})", + "openWorkspaceSettings": "Ouvrir les paramètres d'espace de travail", + "openDocumentation": "En savoir plus", + "ignore": "Ignorer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/fra/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..fa88ff3d03 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "Notes de publication : {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..6f5b732450 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "Notes de publication", + "updateConfigurationTitle": "Mettre à jour", + "updateChannel": "Indiquez si vous recevez des mises à jour automatiques en provenance d'un canal de mises à jour. Un redémarrage est nécessaire en cas de modification." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..418674acc5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "Mettre à jour maintenant", + "later": "Plus tard", + "unassigned": "non assigné", + "releaseNotes": "Notes de publication", + "showReleaseNotes": "Afficher les notes de publication", + "downloadNow": "Télécharger maintenant", + "read the release notes": "Bienvenue dans {0} v{1} ! Voulez-vous lire les notes de publication ?", + "licenseChanged": "Nos termes du contrat de licence ont changé. Prenez un instant pour les consulter.", + "license": "Lire la licence", + "neveragain": "Ne plus afficher", + "64bitisavailable": "{0} pour Windows 64 bits est maintenant disponible !", + "learn more": "En savoir plus", + "updateIsReady": "Nouvelle mise à jour de {0} disponible.", + "thereIsUpdateAvailable": "Une mise à jour est disponible.", + "updateAvailable": "{0} sera mis à jour après avoir redémarré.", + "noUpdatesAvailable": "Aucune mise à jour n'est disponible actuellement.", + "commandPalette": "Palette de commandes...", + "settings": "Paramètres", + "keyboardShortcuts": "Raccourcis clavier", + "selectTheme.label": "Thème de couleur", + "themes.selectIconTheme.label": "Thème d'icône de fichier", + "not available": "Mises à jour non disponibles", + "checkingForUpdates": "Recherche des mises à jour...", + "DownloadUpdate": "Télécharger la mise à jour disponible", + "DownloadingUpdate": "Téléchargement de la mise à jour...", + "InstallingUpdate": "Installation de la mise à jour...", + "restartToUpdate": "Redémarrer pour mettre à jour...", + "checkForUpdates": "Rechercher les mises à jour..." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/fra/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..cd9d2982f2 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} actions", + "hideView": "Masquer dans la barre latérale" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/fra/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..cf6c3afecb --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "les vues doivent figurer dans un tableau", + "requirestring": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "optstring": "La propriété '{0}' peut être omise ou doit être de type 'string'", + "vscode.extension.contributes.view.id": "Identificateur de la vue. Utilisez-le pour inscrire un fournisseur de données au moyen de l'API 'vscode.window.registerTreeDataProviderForView', ainsi que pour déclencher l'activation de votre extension en inscrivant l'événement 'onView:${id}' dans 'activationEvents'.", + "vscode.extension.contributes.view.name": "Nom de la vue, contrôlable de visu. Affiché", + "vscode.extension.contributes.view.when": "Condition qui doit être vraie pour afficher cette vue", + "vscode.extension.contributes.views": "Ajoute des vues à l'éditeur", + "views.explorer": "Mode Explorateur", + "views.debug": "Debug View", + "locationId.invalid": "'{0}' n'est pas un emplacement de vue valide" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/fra/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..e51d5b967b --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "Afficher toutes les commandes", + "watermark.quickOpen": "Accéder au fichier", + "watermark.openFile": "Ouvrir le fichier", + "watermark.openFolder": "Ouvrir le dossier", + "watermark.openFileFolder": "Ouvrir un fichier ou un dossier", + "watermark.openRecent": "Ouvrir les éléments récents", + "watermark.newUntitledFile": "Nouveau fichier sans titre", + "watermark.toggleTerminal": "Activer/désactiver le terminal", + "watermark.findInFiles": "Chercher dans les fichiers", + "watermark.startDebugging": "Démarrer le débogage", + "watermark.unboundCommand": "indépendant", + "workbenchConfigurationTitle": "Banc d'essai", + "tips.enabled": "Si cette option est activée, les conseils en filigrane s'affichent quand aucun éditeur n'est ouvert." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/fra/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..6acfbd0167 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "Explorateur de fichiers", + "welcomeOverlay.search": "Rechercher dans les fichiers", + "welcomeOverlay.git": "Gestion du code source", + "welcomeOverlay.debug": "Lancer et déboguer", + "welcomeOverlay.extensions": "Gérer les extensions", + "welcomeOverlay.problems": "Afficher les erreurs et avertissements", + "welcomeOverlay.commandPalette": "Rechercher et exécuter toutes les commandes", + "welcomeOverlay": "Vue d'ensemble de l'interface utilisateur", + "hideWelcomeOverlay": "Masquer la vue d'ensemble de l'interface", + "help": "Aide" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..5eb2eec78d --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "Édition évoluée", + "welcomePage.start": "Démarrer", + "welcomePage.newFile": "Nouveau fichier", + "welcomePage.openFolder": "Ouvrir un dossier...", + "welcomePage.cloneGitRepository": "Cloner le dépôt Git...", + "welcomePage.recent": "Récent", + "welcomePage.moreRecent": "Plus...", + "welcomePage.noRecentFolders": "Aucun dossier récent", + "welcomePage.help": "Aide", + "welcomePage.keybindingsCheatsheet": "Fiche de révision du clavier imprimable", + "welcomePage.introductoryVideos": "Vidéos d'introduction", + "welcomePage.tipsAndTricks": "Conseils et astuces", + "welcomePage.productDocumentation": "Documentation du produit", + "welcomePage.gitHubRepository": "Dépôt GitHub", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "Afficher la page d'accueil au démarrage", + "welcomePage.customize": "Personnaliser", + "welcomePage.installExtensionPacks": "Outils et langages", + "welcomePage.installExtensionPacksDescription": "Installer un support pour {0} et {1}", + "welcomePage.moreExtensions": "plus", + "welcomePage.installKeymapDescription": "Installer les raccourcis clavier", + "welcomePage.installKeymapExtension": "Installer les raccourcis clavier de {0} et {1}", + "welcomePage.others": "autres", + "welcomePage.colorTheme": "Thème de couleur", + "welcomePage.colorThemeDescription": "Personnalisez l'apparence de l'éditeur et de votre code", + "welcomePage.learn": "Apprendre", + "welcomePage.showCommands": "Rechercher et exécuter toutes les commandes", + "welcomePage.showCommandsDescription": "La palette de commandes ({0}) permet d'accéder rapidement aux commandes pour en rechercher une", + "welcomePage.interfaceOverview": "Vue d'ensemble de l'interface", + "welcomePage.interfaceOverviewDescription": "Obtenez une superposition visuelle mettant en évidence les principaux composants de l'IU", + "welcomePage.deployToAzure": "Déployer des applications sur le cloud", + "welcomePage.deployToAzureDescription": "Découvrir comment déployer vos applications Node sur Azure App Service", + "welcomePage.interactivePlayground": "Terrain de jeu interactif", + "welcomePage.interactivePlaygroundDescription": "Essayez les fonctionnalités essentielles de l'éditeur en suivant une brève procédure pas à pas" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..6c142020aa --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "Banc d'essai", + "workbench.startupEditor.none": "Démarrage sans éditeur.", + "workbench.startupEditor.welcomePage": "Ouvre la page de bienvenue (par défaut).", + "workbench.startupEditor.newUntitledFile": "Ouverture d'un nouveau fichier sans titre.", + "workbench.startupEditor": "Définit quel éditeur s’affiche au démarrage, si aucun n’est restauré à partir de la session précédente. Sélectionnez 'none' pour démarrer sans éditeur, 'welcomePage' pour ouvrir la page d’accueil (par défaut), 'newUntitledFile' pour ouvrir un nouveau fichier sans titre (seulement lors de l'ouverture d'un espace de travail vide).", + "help": "Aide" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..ea51a3e7a2 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "Bienvenue", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "Afficher les extensions Azure", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "Le support pour {0} est déjà installé.", + "welcomePage.willReloadAfterInstallingExtensionPack": "La fenêtre se recharge après l'installation d'un support supplémentaire pour {0}.", + "welcomePage.installingExtensionPack": "Installation d'un support supplémentaire pour {0}...", + "welcomePage.extensionPackNotFound": "Le support pour {0} avec l'ID {1} est introuvable.", + "welcomePage.keymapAlreadyInstalled": "Les raccourcis clavier {0} sont déjà installés.", + "welcomePage.willReloadAfterInstallingKeymap": "La fenêtre se recharge après l'installation des raccourcis clavier {0}.", + "welcomePage.installingKeymap": "Installation des raccourcis clavier de {0}...", + "welcomePage.keymapNotFound": "Les raccourcis clavier {0} ayant l'ID {1} sont introuvables.", + "welcome.title": "Bienvenue", + "welcomePage.openFolderWithPath": "Ouvrir le dossier {0} avec le chemin {1}", + "welcomePage.extensionListSeparator": ", ", + "welcomePage.installKeymap": "Installer le mappage de touches {0}", + "welcomePage.installExtensionPack": "Installer un support supplémentaire pour {0} ", + "welcomePage.installedKeymap": "Le mappage de touches '{0}' est déjà installé", + "welcomePage.installedExtensionPack": "Le support {0} est déjà installé.", + "ok": "OK", + "details": "Détails", + "cancel": "Annuler", + "welcomePage.buttonBackground": "Couleur d'arrière-plan des boutons de la page d'accueil.", + "welcomePage.buttonHoverBackground": "Couleur d'arrière-plan du pointage des boutons de la page d'accueil." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..e0558933c0 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "Terrain de jeu interactif", + "editorWalkThrough": "Terrain de jeu interactif" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..85242ba786 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "Terrain de jeu interactif", + "help": "Aide", + "interactivePlayground": "Terrain de jeu interactif" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..b5bcd66f68 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "Faire défiler vers le haut (ligne)", + "editorWalkThrough.arrowDown": "Faire défiler vers le bas (ligne)", + "editorWalkThrough.pageUp": "Faire défiler vers le haut (page)", + "editorWalkThrough.pageDown": "Faire défiler vers le bas (page)" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..673657e42e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "indépendant", + "walkThrough.gitNotFound": "Git semble ne pas être installé sur votre système.", + "walkThrough.embeddedEditorBackground": "Couleur d'arrière-plan des éditeurs incorporés dans le terrain de jeu interactif." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/fra/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..96bf32faf6 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "Ajoute des paramètres de configuration.", + "vscode.extension.contributes.configuration.title": "Résumé des paramètres. Cette étiquette va être utilisée dans le fichier de paramètres en tant que commentaire de séparation.", + "vscode.extension.contributes.configuration.properties": "Description des propriétés de configuration.", + "scope.window.description": "Configuration spécifique de la fenêtre, qui peut être configurée dans les paramètres utilisateur ou de l'espace de travail.", + "scope.resource.description": "Configuration spécifique de la ressource, qui peut être configurée dans les paramètres utilisateur, de l'espace de travail ou du dossier.", + "scope.description": "Portée dans laquelle la configuration s’applique. Les portées disponibles sont `window` et `resource`.", + "invalid.type": "s'il est défini, 'configuration.type' doit avoir la valeur 'object", + "invalid.title": "'configuration.title' doit être une chaîne", + "vscode.extension.contributes.defaultConfiguration": "Contribue aux paramètres de configuration d'éditeur par défaut en fonction du langage.", + "invalid.properties": "'configuration.properties' doit être un objet", + "workspaceConfig.folders.description": "Liste des dossiers à charger dans l’espace de travail. Doit être un chemin de fichier, par exemple, '/root/folderA' ou './folderA' pour un chemin relatif résolu selon l’emplacement du fichier d’espace de travail.", + "workspaceConfig.folder.description": "Un chemin de fichier, par exemple, '/root/folderA' ou './folderA' pour un chemin relatif résolu selon l’emplacement du fichier d’espace de travail.", + "workspaceConfig.settings.description": "Paramètres de l’espace de travail" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/fra/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..f643853e6e --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Ouvrir les paramètres", + "close": "Fermer", + "saveAndRetry": "Enregistrer les paramètres et recommencer", + "errorUnknownKey": "Impossible d’écrire dans {0} car {1} n’est pas une configuration recommandée.", + "errorInvalidFolderConfiguration": "Impossible d’écrire dans les paramètres de dossier parce que {0} ne supporte pas la portée de ressource de dossier.", + "errorInvalidUserTarget": "Impossible d’écrire dans les paramètres utilisateur parce que {0} ne supporte pas de portée globale.", + "errorInvalidFolderTarget": "Impossible d’écrire dans les paramètres de dossier car aucune ressource n’est fournie.", + "errorNoWorkspaceOpened": "Impossible d’écrire dans {0} car aucun espace de travail n’est ouvert. Veuillez ouvrir un espace de travail et essayer à nouveau.", + "errorInvalidConfiguration": "Impossible d'écrire les paramètres. Ouvrez les **Paramètres utilisateur** pour corriger les erreurs/avertissements présents dans le fichier, puis réessayez.", + "errorInvalidConfigurationWorkspace": "Impossible d'écrire les paramètres. Ouvrez les **Paramètres d'espace de travail** pour corriger les erreurs/avertissements présents dans le fichier, puis réessayez.", + "errorConfigurationFileDirty": "Impossible d'écrire les paramètres, car l'intégrité du fichier est compromise. Enregistrez le fichier des **Paramètres utilisateur**, puis réessayez.", + "errorConfigurationFileDirtyWorkspace": "Impossible d'écrire les paramètres, car l'intégrité du fichier est compromise. Enregistrez le fichier des **Paramètres d'espace de travail**, puis réessayez.", + "userTarget": "Paramètres utilisateur", + "workspaceTarget": "Paramètres de l'espace de travail", + "folderTarget": "Paramètres de dossier" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/fra/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..67b9ba32b6 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "Impossible d’écrire dans le fichier. Veuillez ouvrir le fichier pour corriger les erreurs/avertissements dans le fichier et réessayer.", + "errorFileDirty": "Impossible d’écrire dans le fichier parce que le fichier a été modifié. Veuillez enregistrer le fichier et réessayer." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/fra/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..ab87801aab --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Télémétrie", + "telemetry.enableCrashReporting": "Activez l'envoi de rapports d'incidents à Microsoft.\nCette option nécessite un redémarrage pour être prise en compte." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/fra/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..9d4b48e455 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "L'hôte d'extension n'a pas démarré en moins de 10 secondes. Il est peut-être arrêté à la première ligne et a besoin d'un débogueur pour continuer.", + "extensionHostProcess.startupFail": "L'hôte d'extension n'a pas démarré en moins de 10 secondes. Il existe peut-être un problème.", + "extensionHostProcess.error": "Erreur de l'hôte d'extension : {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..97f83d57ff --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Échec de l'analyse de {0} : {1}.", + "fileReadFail": "Impossible de lire le fichier {0} : {1}.", + "jsonsParseFail": "Échec de l'analyse de {0} ou de {1} : {2}.", + "missingNLSKey": "Le message est introuvable pour la clé {0}." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..71cd1da325 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "Outils de développement", + "restart": "Redémarrer l’hôte d'extension", + "extensionHostProcess.crash": "L'hôte d’extension s'est arrêté de manière inattendue.", + "extensionHostProcess.unresponsiveCrash": "L'hôte d'extension s'est arrêté, car il ne répondait pas.", + "overwritingExtension": "Remplacement de l'extension {0} par {1}.", + "extensionUnderDevelopment": "Chargement de l'extension de développement sur {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/fra/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..c076994d48 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "Microsoft .NET Framework 4.5 est obligatoire. Suivez le lien pour l'installer.", + "installNet": "Télécharger .NET Framework 4.5", + "neverShowAgain": "Ne plus afficher", + "trashFailed": "Échec du déplacement de '{0}' vers la corbeille" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/fra/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..cfb5bee1fc --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "Ressource de fichier non valide ({0})", + "fileIsDirectoryError": "Le fichier est un répertoire ({0})", + "fileNotModifiedError": "Fichier non modifié depuis", + "fileTooLargeError": "Fichier trop volumineux pour être ouvert", + "fileBinaryError": "Il semble que le fichier soit binaire. Impossible de l'ouvrir en tant que texte", + "fileNotFoundError": "Fichier introuvable ({0})", + "fileMoveConflict": "Déplacement/copie impossible. Le fichier existe déjà dans la destination.", + "unableToMoveCopyError": "Impossible de déplacer/copier. Le fichier remplace le dossier qui le contient.", + "foldersCopyError": "Impossible de copier des dossiers dans l'espace de travail. Sélectionnez les fichiers à copier individuellement.", + "fileModifiedError": "Fichier modifié depuis", + "fileReadOnlyError": "Fichier en lecture seule" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/fra/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..53afa3d816 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "Impossible d'écrire, car l'intégrité du fichier est compromise. Enregistrez le fichier de **Combinaisons de touches**, puis réessayez.", + "parseErrors": "Impossible d'écrire les combinaisons de touches. Ouvrez le **Fichier de combinaisons de touches** pour corriger les erreurs/avertissements présents dans le fichier, puis réessayez.", + "errorInvalidConfiguration": "Impossible d'écrire les combinaisons de touches. Le **fichier de combinaisons de touches** contient un objet qui n'est pas de type Array. Ouvrez le fichier pour le nettoyer, puis réessayez.", + "emptyKeybindingsHeader": "Placez vos combinaisons de touches dans ce fichier pour remplacer les valeurs par défaut" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/fra/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..37e0b34d09 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "Valeur non vide attendue.", + "requirestring": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "optstring": "La propriété '{0}' peut être omise ou doit être de type 'string'", + "vscode.extension.contributes.keybindings.command": "Identificateur de la commande à exécuter quand la combinaison de touches est déclenchée.", + "vscode.extension.contributes.keybindings.key": "Touche ou séquence de touches (séparez les touches avec le signe plus et les séquences de touches avec un espace. Exemple : Ctrl+O et Ctrl+L L pour une pression simultanée).", + "vscode.extension.contributes.keybindings.mac": "Touche ou séquence de touches spécifique à Mac.", + "vscode.extension.contributes.keybindings.linux": "Touche ou séquence de touches spécifique à Linux.", + "vscode.extension.contributes.keybindings.win": "Touche ou séquence de touches spécifique à Windows.", + "vscode.extension.contributes.keybindings.when": "Condition quand la touche est active.", + "vscode.extension.contributes.keybindings": "Ajoute des combinaisons de touches.", + "invalid.keybindings": "'contributes.{0}' non valide : {1}", + "unboundCommands": "Voici d'autres commandes disponibles : ", + "keybindings.json.title": "Configuration des combinaisons de touches", + "keybindings.json.key": "Touche ou séquence de touches (séparées par un espace)", + "keybindings.json.command": "Nom de la commande à exécuter", + "keybindings.json.when": "Condition quand la touche est active.", + "keybindings.json.args": "Arguments à passer à la commande à exécuter.", + "keyboardConfigurationTitle": "Clavier", + "dispatch": "Spécifie l'utilisation de `keydown.code` (recommandé) ou de `keydown.keyCode` dans le cadre de la logique de dispatch associée aux appuis sur les touches." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/fra/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..c010c80a09 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Erreur : {0}", + "alertWarningMessage": "Avertissement : {0}", + "alertInfoMessage": "Information : {0}", + "error": "Erreur", + "warning": "Avertir", + "info": "Informations", + "close": "Fermer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/fra/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..10cf92e25f --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "&&Oui", + "cancelButton": "Annuler" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/fra/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..82fc1fa4ab --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Ajoute des déclarations de langage.", + "vscode.extension.contributes.languages.id": "ID du langage.", + "vscode.extension.contributes.languages.aliases": "Alias de nom du langage.", + "vscode.extension.contributes.languages.extensions": "Extensions de fichier associées au langage.", + "vscode.extension.contributes.languages.filenames": "Noms de fichiers associés au langage.", + "vscode.extension.contributes.languages.filenamePatterns": "Modèles Glob de noms de fichiers associés au langage.", + "vscode.extension.contributes.languages.mimetypes": "Types MIME associés au langue.", + "vscode.extension.contributes.languages.firstLine": "Expression régulière correspondant à la première ligne d'un fichier du langage.", + "vscode.extension.contributes.languages.configuration": "Chemin relatif d'un fichier contenant les options de configuration du langage.", + "invalid": "'contributes.{0}' non valide. Tableau attendu.", + "invalid.empty": "Valeur vide pour 'contributes.{0}'", + "require.id": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "opt.extensions": "la propriété '{0}' peut être omise et doit être de type 'string[]'", + "opt.filenames": "la propriété '{0}' peut être omise et doit être de type 'string[]'", + "opt.firstLine": "la propriété '{0}' peut être omise et doit être de type 'string'", + "opt.configuration": "la propriété '{0}' peut être omise et doit être de type 'string'", + "opt.aliases": "la propriété '{0}' peut être omise et doit être de type 'string[]'", + "opt.mimetypes": "la propriété '{0}' peut être omise et doit être de type 'string[]'" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/fra/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..05b7ea9ed3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1}", + "progress.title": "{0} : {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/fra/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..a26733d7c2 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Ajoute des générateurs de jetons TextMate.", + "vscode.extension.contributes.grammars.language": "Identificateur de langue pour lequel cette syntaxe est ajoutée.", + "vscode.extension.contributes.grammars.scopeName": "Nom de portée TextMate utilisé par le fichier tmLanguage.", + "vscode.extension.contributes.grammars.path": "Chemin du fichier tmLanguage. Le chemin est relatif au dossier d'extensions et commence généralement par './syntaxes/'.", + "vscode.extension.contributes.grammars.embeddedLanguages": "Mappage du nom de portée à l'ID de langage si cette grammaire contient des langages incorporés.", + "vscode.extension.contributes.grammars.injectTo": "Liste de noms des portées de langage auxquelles cette grammaire est injectée." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/fra/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..0ef45c1787 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Langage inconnu dans 'contributes.{0}.language'. Valeur fournie : {1}", + "invalid.scopeName": "Chaîne attendue dans 'contributes.{0}.scopeName'. Valeur fournie : {1}", + "invalid.path.0": "Chaîne attendue dans 'contributes.{0}.path'. Valeur fournie : {1}", + "invalid.injectTo": "Valeur non valide dans 'contributes.{0}.injectTo'. Il doit s'agir d'un tableau de noms de portées de langage. Valeur fournie : {1}", + "invalid.embeddedLanguages": "Valeur non valide dans 'contributes.{0}.embeddedLanguages'. Il doit s'agir d'un mappage d'objets entre le nom de portée et le langage. Valeur fournie : {1}", + "invalid.path.1": "'contributes.{0}.path' ({1}) est censé être inclus dans le dossier ({2}) de l'extension. Cela risque de rendre l'extension non portable.", + "no-tm-grammar": "Aucune grammaire TM n'est inscrite pour ce langage." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/fra/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..3c41f252c5 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "L'intégrité du fichier est compromise. Enregistrez-le avant de le rouvrir avec un autre encodage.", + "genericSaveError": "Échec d'enregistrement de '{0}' ({1})." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/fra/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..c7c5078521 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "Les fichiers n'ont pas pu être sauvegardés (Erreur : {0}), essayez d'enregistrer vos fichiers pour quitter." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/fra/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..94f5d0f06d --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "Voulez-vous enregistrer les modifications apportées à {0} ?", + "saveChangesMessages": "Voulez-vous enregistrer les modifications apportées aux {0} fichiers suivants ?", + "moreFile": "...1 fichier supplémentaire non affiché", + "moreFiles": "...{0} fichiers supplémentaires non affichés", + "saveAll": "&&Enregistrer tout", + "save": "&&Enregistrer", + "dontSave": "&&Ne pas enregistrer", + "cancel": "Annuler", + "saveChangesDetail": "Vous perdrez vos modifications, si vous ne les enregistrez pas.", + "allFiles": "Tous les fichiers", + "noExt": "Aucune extension" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/fra/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..85d87984e6 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "Couleurs et styles du jeton.", + "schema.token.foreground": "Couleur de premier plan du jeton.", + "schema.token.fontStyle": "Style de police de la règle : 'italique', 'gras' ou 'souligné', ou une combinaison de ces styles", + "schema.fontStyle.error": "Le style de police doit être une combinaison de 'italic', 'bold' et 'underline'", + "schema.properties.name": "Description de la règle.", + "schema.properties.scope": "Sélecteur de portée qui correspond à cette règle.", + "schema.tokenColors.path": "Chemin d'un ficher tmTheme (relatif au fichier actuel).", + "schema.colors": "Couleurs de la coloration syntaxique" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/fra/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..615ad1e27b --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "Icône de dossier des dossiers développés. L'icône du dossier développé est facultative. En l'absence de définition, l'icône définie pour le dossier est affichée.", + "schema.folder": "Icône de dossier des dossiers réduits. Si folderExpanded n'est pas défini, s'applique aussi aux dossiers développés.", + "schema.file": "Icône de fichier par défaut, affichée pour tous les fichiers qui ne correspondent à aucune extension, aucun nom de fichier ou aucun ID de langue.", + "schema.folderNames": "Associe des noms de dossiers à des icônes. La clé de l'objet est le nom de dossier, à l'exclusion des segments du chemin. Aucun modèle ou caractère générique n'est autorisé. La correspondance du nom de dossier ne respecte pas la casse.", + "schema.folderName": "ID de la définition d'icône de l'association.", + "schema.folderNamesExpanded": "Associe des noms de dossiers à des icônes pour les dossiers développés. La clé de l'objet est le nom de dossier, à l'exclusion des segments du chemin. Aucun modèle ou caractère générique n'est autorisé. La correspondance du nom de dossier ne respecte pas la casse.", + "schema.folderNameExpanded": "ID de la définition d'icône de l'association.", + "schema.fileExtensions": "Associe des extensions de fichier à des icônes. La clé de l'objet est le nom de l'extension de fichier. Le nom d'extension est le dernier segment d'un nom de fichier situé après le dernier point (à l'exclusion du point). Les extensions sont comparées sans respect de la casse.", + "schema.fileExtension": "ID de la définition d'icône de l'association.", + "schema.fileNames": "Associe des noms de fichiers à des icônes. La clé de l'objet est le nom de fichier complet, à l'exclusion des segments du chemin. Le nom de fichier peut inclure des points et une éventuelle extension de fichier. Aucun modèle ou caractère générique n'est autorisé. La correspondance du nom de fichier ne respecte pas la casse.", + "schema.fileName": "ID de la définition d'icône de l'association.", + "schema.languageIds": "Associe des langages à des icônes. La clé de l'objet est l'ID de langage défini dans le point de contribution du langage.", + "schema.languageId": "ID de la définition d'icône de l'association.", + "schema.fonts": "Polices utilisées dans les définitions d'icônes.", + "schema.id": "ID de la police.", + "schema.src": "Emplacements de la police.", + "schema.font-path": "Chemin de la police, par rapport au fichier de thème d'icône actuel.", + "schema.font-format": "Format de la police.", + "schema.font-weight": "Poids de la police.", + "schema.font-sstyle": "Style de la police.", + "schema.font-size": "Taille par défaut de la police.", + "schema.iconDefinitions": "Description de toutes les icônes utilisables durant l'association de fichiers à des icônes.", + "schema.iconDefinition": "Définition d'icône. La clé de l'objet est l'ID de la définition.", + "schema.iconPath": "Quand le format SVG ou PNG est utilisé : chemin de l'image. Le chemin est relatif au fichier du jeu d'icônes.", + "schema.fontCharacter": "Quand une police de type glyphe est employée : caractère de police à utiliser.", + "schema.fontColor": "Quand une police de type glyphe est employée : couleur à utiliser.", + "schema.fontSize": "Quand une police est utilisée : taille de police en pourcentage par rapport à la police du texte. En l'absence de définition, la taille de la définition de police est utilisée par défaut.", + "schema.fontId": "Quand une police est employée : ID de la police. En l'absence de définition, la première définition de police est utilisée par défaut.", + "schema.light": "Associations facultatives des icônes de fichiers dans les thèmes de couleur claire.", + "schema.highContrast": "Associations facultatives des icônes de fichiers dans les thèmes de couleur à contraste élevé." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/fra/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..e69fbfdebc --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "Problèmes durant l'analyse du fichier de thème JSON : {0}", + "error.invalidformat.colors": "Problème pendant l'analyse du fichier de thème de couleur : {0}. La propriété 'colors' n'est pas de type 'object'.", + "error.invalidformat.tokenColors": "Problème pendant l'analyse du fichier de thème de couleur : {0}. La propriété 'tokenColors' doit être un tableau spécifiant des couleurs ou le chemin d'un fichier de thème TextMate", + "error.plist.invalidformat": "Problème pendant l'analyse du fichier tmTheme : {0}. 'settings' n'est pas un tableau.", + "error.cannotparse": "Problèmes pendant l'analyse du fichier tmTheme : {0}", + "error.cannotload": "Problèmes pendant le chargement du fichier tmTheme {0} : {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/fra/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..3b18685755 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "ID du thème d'icône utilisé dans les paramètres utilisateur.", + "vscode.extension.contributes.themes.label": "Étiquette du thème de couleur comme indiqué dans l'interface utilisateur (IU).", + "vscode.extension.contributes.themes.uiTheme": "Thème de base définissant les couleurs autour de l'éditeur : 'vs' est le thème de couleur clair, 'vs-dark' est le thème de couleur sombre. 'hc-black' est le thème sombre à contraste élevé.", + "vscode.extension.contributes.themes.path": "Chemin du fichier tmTheme. Le chemin est relatif au dossier d'extensions et correspond généralement à './themes/themeFile.tmTheme'.", + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "ID du thème d'icône utilisé dans les paramètres utilisateur.", + "vscode.extension.contributes.iconThemes.label": "Étiquette du thème d'icône indiqué dans l'IU (interface utilisateur).", + "vscode.extension.contributes.iconThemes.path": "Chemin du fichier de définitions de thèmes d'icônes. Le chemin est relatif au dossier d'extensions et correspond généralement à './icons/awesome-icon-theme.json'.", + "migration.completed": "De nouveaux paramètres de thème ont été ajoutés aux paramètres utilisateur. Sauvegarde disponible sur {0}.", + "error.cannotloadtheme": "Unable to load {0}: {1}", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "Chaîne attendue dans 'contributes.{0}.path'. Valeur fournie : {1}", + "invalid.path.1": "'contributes.{0}.path' ({1}) est censé être inclus dans le dossier ({2}) de l'extension. Cela risque de rendre l'extension non portable.", + "reqid": "Chaîne attendue dans 'contributes.{0}.id'. Valeur fournie : {1}", + "error.cannotloadicontheme": "Unable to load {0}", + "error.cannotparseicontheme": "Problems parsing file icons file: {0}", + "colorTheme": "Specifies the color theme used in the workbench.", + "colorThemeError": "Theme is unknown or not installed.", + "iconTheme": "Spécifie le thème d'icône utilisé dans le banc d'essai ou 'null' pour n'afficher aucune icône de fichier.", + "noIconThemeDesc": "No file icons", + "iconThemeError": "File icon theme is unknown or not installed.", + "workbenchColors": "Remplace les couleurs du thème de couleur sélectionné.", + "workbenchColors.deprecated": "Le paramètre n'est plus expérimental et a été renommé 'workbench.colorCustomizations'", + "workbenchColors.deprecatedDescription": "Utiliser 'workbench.colorCustomizations' à la place", + "editorColors": "Remplace les couleurs et le style de la police de l’éditeur du thème par la couleur actuellement sélectionnée.", + "editorColors.comments": "Définit les couleurs et les styles des commentaires", + "editorColors.strings": "Définit les couleurs et les styles des littéraux de chaînes.", + "editorColors.keywords": "Définit les couleurs et les styles des mots clés.", + "editorColors.numbers": "Définit les couleurs et les styles des littéraux de nombre.", + "editorColors.types": "Définit les couleurs et les styles des déclarations et références de type.", + "editorColors.functions": "Définit les couleurs et les styles des déclarations et références de fonctions.", + "editorColors.variables": "Définit les couleurs et les styles des déclarations et références de variables.", + "editorColors.textMateRules": "Définit les couleurs et les styles à l’aide de règles de thème textmate (avancé)." +} \ No newline at end of file diff --git a/i18n/hun/extensions/configuration-editing/out/extension.i18n.json b/i18n/hun/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..1c2c17cd44 --- /dev/null +++ b/i18n/hun/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "Példa" +} \ No newline at end of file diff --git a/i18n/hun/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/hun/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..c4a22d64ec --- /dev/null +++ b/i18n/hun/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "pl.: myFile.txt", + "activeEditorMedium": "pl.: myFolder/myFile.txt", + "activeEditorLong": "pl.: /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "pl.: myFolder1, myFolder2, myFolder3", + "rootPath": "pl.: /Users/Development/myProject", + "folderName": "pl.: myFolder", + "folderPath": "pl.: /Users/Development/myProject", + "appName": "pl.: VS Code", + "dirty": "módosításjelző, ami akkor jelenik meg, ha az aktív szerkesztőablak tartalma módosítva lett", + "separator": "egy feltételes elválasztó (' - '), ami akkor jelenik meg, ha olyan változókkal van körülvéve, amelyeknek van értéke", + "assocLabelFile": "Fájlok kiterjesztésekkel", + "assocDescriptionFile": "A megadott azonosítójú nyelvhez tartozó glob mintának megfelelő fájlok feltérképezése.", + "assocLabelPath": "Fájlok elérési úttal", + "assocDescriptionPath": "A megadott azonosítójú nyelvhez tartozó abszolút elérési utas glob mintának megfelelő fájlok feltérképezése.", + "fileLabel": "Fájlok kiterjesztés szerint", + "fileDescription": "Adott kiterjesztéssel rendelkező fájlok keresése.", + "filesLabel": "Fájlok több kiterjesztés szerint", + "filesDescription": "A megadott kiterjesztések bármelyikével rendelkező fájlok keresése.", + "derivedLabel": "Testvérekkel rendelkező fájlok név szerint", + "derivedDescription": "Olyan fájlok keresése, amelyek azonos nevű, de különböző kiterjesztésű testvérekkel rendelkeznek.", + "topFolderLabel": "Mappák név szerint (legfelső szinten)", + "topFolderDescription": "Adott névvel rendelkező, legfelső szintű mappák keresése", + "topFoldersLabel": "Fájlok több név szerint (legfelső szinten)", + "topFoldersDescription": "Több legfelső szintű mappa keresése.", + "folderLabel": "Mappa név szerint (bármely helyen)", + "folderDescription": "Adott névvel rendelkező mappa keresése helytől függetlenül.", + "falseDescription": "A minta letiltása.", + "trueDescription": "A minta engedélyezése.", + "siblingsDescription": "Olyan fájlok keresése, amelyek azonos nevű, de különböző kiterjesztésű testvérekkel rendelkeznek.", + "languageSpecificEditorSettings": "Nyelvspecifikus szerkesztőbeállítások", + "languageSpecificEditorSettingsDescription": "A szerkesztő beállításainak felülírása az adott nyelvre vonatkozóan" +} \ No newline at end of file diff --git a/i18n/hun/extensions/css/client/out/cssMain.i18n.json b/i18n/hun/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..f4947d60a1 --- /dev/null +++ b/i18n/hun/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "CSS nyelvi szerver" +} \ No newline at end of file diff --git a/i18n/hun/extensions/css/package.i18n.json b/i18n/hun/extensions/css/package.i18n.json new file mode 100644 index 0000000000..0765c03fee --- /dev/null +++ b/i18n/hun/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "Nem megfelelő számú paraméter", + "css.lint.boxModel.desc": "A width és a height tulajdonság kerülése a padding és a border tulajdonság használata esetén", + "css.lint.compatibleVendorPrefixes.desc": "Gyártóspecifikus előtag használata esetén minden más gyártóspecifikus tulajdonságot is meg kell adni", + "css.lint.duplicateProperties.desc": "Duplikált stílusdefiníciók kerülése", + "css.lint.emptyRules.desc": "Üres szabályhalmazok kerülése", + "css.lint.float.desc": "A float tulajdonságérték kerülése, mivel könnyen váratlan eredményt idézhet elő az elrendezés változásakor.", + "css.lint.fontFaceProperties.desc": "A @font-face szabályokban az src és a font-family tulajdonságot is definiálni kell", + "css.lint.hexColorLength.desc": "A hexadecimális formában megadott színeknek három vagy hat hexadecimális számjegyből kell állniuk", + "css.lint.idSelector.desc": "A szelektorok nem tartalmazhatnak azonosítókat, mivel az ilyen szabályok túl szorosan kötődnek a HTML-hez.", + "css.lint.ieHack.desc": "Az IE hangolása csak az IE7 vagy régebbi verziók támogatása esetén szükséges", + "css.lint.important.desc": "Az !important attribútum mellőzése. Az attribútum jelenléte arra utal, hogy a CSS-struktúra átláthatatlanná vált, és refaktorálásra szorul.", + "css.lint.importStatement.desc": "Ne töltődjenek párhuzamosan az importálási utasítások", + "css.lint.propertyIgnoredDueToDisplay.desc": "A megjelenítési mód miatt a megjelenítőkomponensek nem fogják figyelembe venni a tulajdonságot. Ha például a display tulajdonság értéke inline, akkor a megjelenítők figyelmen kívül hagyják a width, a height, a margin-top, a margin-bottom és a float tulajdonságot.", + "css.lint.universalSelector.desc": "Az univerzális szelektor (*) lassú működést eredményez", + "css.lint.unknownProperties.desc": "Ismeretlen tulajdonság.", + "css.lint.unknownVendorSpecificProperties.desc": "Ismeretlen gyártóspecifikus tulajdonság.", + "css.lint.vendorPrefix.desc": "Gyártóspecifikus előtagok használata esetén az adott tulajdonság szabványos változatát is meg kell adni", + "css.lint.zeroUnits.desc": "A 0 értékhez nem szükséges mértékegység", + "css.trace.server.desc": "A VS Code és a CSS nyelvi szerver közötti kommunikáció naplózása.", + "css.validate.title": "Meghatározza a CSS-validáció működését és a problémák súlyosságát.", + "css.validate.desc": "Összes validálás engedélyezése vagy letiltása", + "less.lint.argumentsInColorFunction.desc": "Nem megfelelő számú paraméter", + "less.lint.boxModel.desc": "A width és a height tulajdonság kerülése a padding és a border tulajdonság használata esetén", + "less.lint.compatibleVendorPrefixes.desc": "Gyártóspecifikus előtag használata esetén minden más gyártóspecifikus tulajdonságot is meg kell adni", + "less.lint.duplicateProperties.desc": "Duplikált stílusdefiníciók kerülése", + "less.lint.emptyRules.desc": "Üres szabályhalmazok kerülése", + "less.lint.float.desc": "A float tulajdonságérték kerülése, mivel könnyen váratlan eredményt idézhet elő az elrendezés változásakor.", + "less.lint.fontFaceProperties.desc": "A @font-face szabályokban az src és a font-family tulajdonságot is definiálni kell", + "less.lint.hexColorLength.desc": "A hexadecimális formában megadott színeknek három vagy hat hexadecimális számjegyből kell állniuk", + "less.lint.idSelector.desc": "A szelektorok nem tartalmazhatnak azonosítókat, mivel az ilyen szabályok túl szorosan kötődnek a HTML-hez.", + "less.lint.ieHack.desc": "Az IE hangolása csak az IE7 vagy régebbi verziók támogatása esetén szükséges", + "less.lint.important.desc": "Az !important attribútum mellőzése. Az attribútum jelenléte arra utal, hogy a CSS-struktúra átláthatatlanná vált, és refaktorálásra szorul.", + "less.lint.importStatement.desc": "Ne töltődjenek párhuzamosan az importálási utasítások", + "less.lint.propertyIgnoredDueToDisplay.desc": "A megjelenítési mód miatt a megjelenítőkomponensek nem fogják figyelembe venni a tulajdonságot. Ha például a display tulajdonság értéke inline, akkor a megjelenítők figyelmen kívül hagyják a width, a height, a margin-top, a margin-bottom és a float tulajdonságot.", + "less.lint.universalSelector.desc": "Az univerzális szelektor (*) lassú működést eredményez", + "less.lint.unknownProperties.desc": "Ismeretlen tulajdonság.", + "less.lint.unknownVendorSpecificProperties.desc": "Ismeretlen gyártóspecifikus tulajdonság.", + "less.lint.vendorPrefix.desc": "Gyártóspecifikus előtagok használata esetén az adott tulajdonság szabványos változatát is meg kell adni", + "less.lint.zeroUnits.desc": "A 0 értékhez nem szükséges mértékegység", + "less.validate.title": "Meghatározza a LESS-validáció működését és a problémák súlyosságát.", + "less.validate.desc": "Összes validálás engedélyezése vagy letiltása", + "scss.lint.argumentsInColorFunction.desc": "Nem megfelelő számú paraméter", + "scss.lint.boxModel.desc": "A width és a height tulajdonság kerülése a padding és a border tulajdonság használata esetén", + "scss.lint.compatibleVendorPrefixes.desc": "Gyártóspecifikus előtag használata esetén minden más gyártóspecifikus tulajdonságot is meg kell adni", + "scss.lint.duplicateProperties.desc": "Duplikált stílusdefiníciók kerülése", + "scss.lint.emptyRules.desc": "Üres szabályhalmazok kerülése", + "scss.lint.float.desc": "A float tulajdonságérték kerülése, mivel könnyen váratlan eredményt idézhet elő az elrendezés változásakor.", + "scss.lint.fontFaceProperties.desc": "A @font-face szabályokban az src és a font-family tulajdonságot is definiálni kell", + "scss.lint.hexColorLength.desc": "A hexadecimális formában megadott színeknek három vagy hat hexadecimális számjegyből kell állniuk", + "scss.lint.idSelector.desc": "A szelektorok nem tartalmazhatnak azonosítókat, mivel az ilyen szabályok túl szorosan kötődnek a HTML-hez.", + "scss.lint.ieHack.desc": "Az IE hangolása csak az IE7 vagy régebbi verziók támogatása esetén szükséges", + "scss.lint.important.desc": "Az !important attribútum mellőzése. Az attribútum jelenléte arra utal, hogy a CSS-struktúra átláthatatlanná vált, és refaktorálásra szorul.", + "scss.lint.importStatement.desc": "Ne töltődjenek párhuzamosan az importálási utasítások", + "scss.lint.propertyIgnoredDueToDisplay.desc": "A megjelenítési mód miatt a megjelenítőkomponensek nem fogják figyelembe venni a tulajdonságot. Ha például a display tulajdonság értéke inline, akkor a megjelenítők figyelmen kívül hagyják a width, a height, a margin-top, a margin-bottom és a float tulajdonságot.", + "scss.lint.universalSelector.desc": "Az univerzális szelektor (*) lassú működést eredményez", + "scss.lint.unknownProperties.desc": "Ismeretlen tulajdonság.", + "scss.lint.unknownVendorSpecificProperties.desc": "Ismeretlen gyártóspecifikus tulajdonság.", + "scss.lint.vendorPrefix.desc": "Gyártóspecifikus előtagok használata esetén az adott tulajdonság szabványos változatát is meg kell adni", + "scss.lint.zeroUnits.desc": "A 0 értékhez nem szükséges mértékegység", + "scss.validate.title": "Meghatározza az SCSS-validáció működését és a problémák súlyosságát.", + "scss.validate.desc": "Összes validálás engedélyezése vagy letiltása", + "less.colorDecorators.enable.desc": "Színdekorátorok engedélyezése vagy letiltása", + "scss.colorDecorators.enable.desc": "Színdekorátorok engedélyezése vagy letiltása", + "css.colorDecorators.enable.desc": "Színdekorátorok engedélyezése vagy letiltása", + "css.colorDecorators.enable.deprecationMessage": "Az `css.colorDecorators.enable` beállítás elavult az `editor.colorDecorators` bevezetése miatt.", + "scss.colorDecorators.enable.deprecationMessage": "Az `scss.colorDecorators.enable` beállítás elavult az `editor.colorDecorators` bevezetése miatt.", + "less.colorDecorators.enable.deprecationMessage": "A `less.colorDecorators.enable` beállítás elavult az `editor.colorDecorators` bevezetése miatt." +} \ No newline at end of file diff --git a/i18n/hun/extensions/emmet/package.i18n.json b/i18n/hun/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..b2ea9ee252 --- /dev/null +++ b/i18n/hun/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "Becsomagolás rövidítéssel", + "command.wrapIndividualLinesWithAbbreviation": "Egyedi sorok becsomagolása rövidítéssel", + "command.removeTag": "Elem eltávolítása", + "command.updateTag": "Elem módosítása", + "command.matchTag": "Ugrás az illeszkedő párra", + "command.balanceIn": "Egyensúlyozás (belefé)", + "command.balanceOut": "Egyensúlyozás (kifelé)", + "command.prevEditPoint": "Ugrás az előző szerkesztési pontra", + "command.nextEditPoint": "Ugrás a következő szerkesztési pontra", + "command.mergeLines": "Sorok összeolvasztása", + "command.selectPrevItem": "Előző elem kiválasztása", + "command.selectNextItem": "Következő elem kiválasztása", + "command.splitJoinTag": "Elem szétbontása/összeolvasztása", + "command.toggleComment": "Megjegyzés ki-/bekapcsolása", + "command.evaluateMathExpression": "Matematikai kifejezés kiértékelése", + "command.updateImageSize": "Képméret módosítása", + "command.reflectCSSValue": "CSS-érték tükrözése", + "command.incrementNumberByOne": "Növelés 1-gyel", + "command.decrementNumberByOne": "Csökkentés 1-gyel", + "command.incrementNumberByOneTenth": "Növelés 0,1-gyel", + "command.decrementNumberByOneTenth": "Csökkentés 0,1-gyel", + "command.incrementNumberByTen": "Növelés 10-zel", + "command.decrementNumberByTen": "Csökkentés 10-zel", + "emmetSyntaxProfiles": "Konkrét szintaktika profiljának meghatározása vagy saját profil használata adott szabályokkal.", + "emmetExclude": "Azon nyelvek listája, ahol az Emmet-rövidítések ne legyenek kibontva.", + "emmetExtensionsPath": "Egy Emmet-profilokat és -kódtöredékeket tartalmazó mappa elérési útja.", + "emmetShowExpandedAbbreviation": "Kibontott emmet-rövidítések megjelenítése javaslatként. Az \"inMarkupAndStylesheetFilesOnly\" beállítás csak a html, haml, jade, slim, xml, xsl, css, scss, sass, less és stylus típusú tartalmat jelenti. Az \"always\" beállítás a fájl összes részére vonatkozik a jelölőnyelvtől/css-től függetlenül.", + "emmetShowAbbreviationSuggestions": "A lehetséges emmet-rövidítések megjelenítése javaslatként. Nem használható a stíluslapokon vagy ha az emmet.showExpandedAbbreviation értéke \"never\".", + "emmetIncludeLanguages": "Emmet-rövidítések engedélyezése olyan nyelvek esetében, amelyek alapértelmezés szerint nem támogatottak. Egy megfeleltetést kell felvenni a nyelv és egy emmet által támogatott nyelv között.\nPl.: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "Az emmet-kódrészletekben használt változók", + "emmetTriggerExpansionOnTab": "Ha engedélyezve van, akkor az Emmet-rövidítések a Tab billentyű lenyomásával bonthatók ki.", + "emmetPreferences": "Beállítások, melyek módosítják az Emmet műveleteinek és feloldó algoritmusainak viselkedését.", + "emmetPreferencesIntUnit": "Az egész számok alapértelmezett mértékegysége", + "emmetPreferencesFloatUnit": "A lebegőpontos számok alapértelmezett mértékegysége", + "emmetPreferencesCssAfter": "A CSS-tulajdonság végére helyezett szimbólum CSS-rövidítések kibontásánál", + "emmetPreferencesSassAfter": "A CSS-tulajdonság végére helyezett szimbólum CSS-rövidítések kibontásánál Sass-fájlokban", + "emmetPreferencesStylusAfter": "A CSS-tulajdonság végére helyezett szimbólum CSS-rövidítések kibontásánál Stylus-fájlokban", + "emmetPreferencesCssBetween": "A CSS-tulajdonság és az érték közé helyezett szimbólum CSS-rövidítések kibontásánál", + "emmetPreferencesSassBetween": "A CSS-tulajdonság és az érték közé helyezett szimbólum CSS-rövidítések kibontásánál Sass-fájlokban", + "emmetPreferencesStylusBetween": "A CSS-tulajdonság és az érték közé helyezett szimbólum CSS-rövidítések kibontásánál Stylus-fájlokban", + "emmetShowSuggestionsAsSnippets": "Ha az értéke igaz, akkor az emmetes javaslatok kódrészletekként jelennek meg, ami lehetővé teszi az editor.snippetSuggestions beállítás alapján történő rendezésüket." +} \ No newline at end of file diff --git a/i18n/hun/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/hun/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..2cf06fc75f --- /dev/null +++ b/i18n/hun/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "A képek csak HTTPS-protokollt használhatnak.", + "svgsNotValid": "Az SVG-k nem érvényes képforrások.", + "embeddedSvgsNotValid": "A beágyazott SVG-k nem érvényes képforrások.", + "dataUrlsNotValid": "A data URL-ek nem érvényes képforrások.", + "relativeUrlRequiresHttpsRepository": "A relatív kép URL-ekhez egy HTTPS-protokollal rendelkező forráskódtárat kell megadni a package.json-ban.", + "relativeIconUrlRequiresHttpsRepository": "Az ikonhoz egy HTTPS-protokollal rendelkező forráskódtárat kell megadni a package.json-ban.", + "relativeBadgeUrlRequiresHttpsRepository": "A relatív jelvény URL-ekhez egy HTTPS-protokollal rendelkező forráskódtárat kell megadni a package.json-ban." +} \ No newline at end of file diff --git a/i18n/hun/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/hun/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..fdc9b1502e --- /dev/null +++ b/i18n/hun/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "Nyelvspecifikus szerkesztőbeállítások", + "languageSpecificEditorSettingsDescription": "A szerkesztő beállításainak felülírása az adott nyelvre vonatkozóan" +} \ No newline at end of file diff --git a/i18n/hun/extensions/git/out/askpass-main.i18n.json b/i18n/hun/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..9c9eaff695 --- /dev/null +++ b/i18n/hun/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "Hiányzó vagy érvénytelen hitelesítési adatok." +} \ No newline at end of file diff --git a/i18n/hun/extensions/git/out/commands.i18n.json b/i18n/hun/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..c28dad9bbf --- /dev/null +++ b/i18n/hun/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "Címke, a következőre mutat: {0}", + "remote branch at": "Távoli ág, a következőre mutat: {0}", + "create branch": "$(plus) Új ág létrehozása", + "repourl": "Forráskódtár URL-címe", + "parent": "Szülőkönyvtár", + "cloning": "Git-forráskódtár klónozása...", + "openrepo": "Forráskódtár megnyitása", + "proposeopen": "Szeretné megnyitni a klónozott forráskódtárat?", + "path to init": "Mappa elérési útja", + "provide path": "Git-forráskódtár inicializálásához adjon meg egy elérési utat!", + "HEAD not available": "A(z) '{0}' HEAD-verziója nem elérhető.", + "confirm stage files with merge conflicts": "Biztosan elő szeretne jegyezni {0} ütközési konfliktussal rendelkező fájlt?", + "confirm stage file with merge conflicts": "Biztosan elő szeretne jegyezni a következő, ütközési konfliktussal rendelkező fájlt: {0}?", + "yes": "Igen", + "confirm revert": "Visszaállítja a kijelölt módosításokat a következő helyen: {0}?", + "revert": "Módosítások visszaállítása", + "discard": "Módosítások elvetése", + "confirm delete": "Biztosan TÖRLI a következőt: {0}?", + "delete file": "Fájl törlése", + "confirm discard": "Elveti a módosításokat a következő helyen: {0}?", + "confirm discard multiple": "Elveti {0} fájl módosításait?", + "warn untracked": "A művelet {0} nem követett fájl TÖRLÉSÉT fogja eredményezni!", + "confirm discard all single": "Elveti a módosításokat a következő helyen: {0}?", + "confirm discard all": "Elveti az ÖSSZES módosítást {0} fájlban?\nA művelet NEM VONHATÓ VISSZA!\nAz aktuális munka ÖRÖKRE EL FOG VESZNI.", + "discardAll multiple": "Egy fájl elvetése", + "discardAll": "Mind a(z) {0} fájl elvetése", + "confirm delete multiple": "Biztosan TÖRÖLNI akar {0} fájlt?", + "delete files": "Fájlok törlése", + "there are untracked files single": "A következő, nem követett fájl TÖRÖLVE LESZ A LEMEZRŐL, ha elvetésre kerül: {0}.", + "there are untracked files": "{0} nem követett fájl TÖRÖLVE LESZ A LEMEZRŐL, ha elvetésre kerülnek. ", + "confirm discard all 2": "{0}\n\nA művelet NEM VONHATÓ VISSZA, az aktuális munka ÖRÖKRE EL FOG VESZNI.", + "yes discard tracked": "Egy követett fájl elvetése", + "yes discard tracked multiple": "{0} követett fájl elvetése", + "no staged changes": "Nincs beadáshoz (commithoz) előjegyzett módosítás. Szeretné automatikusan előjegyeztetni a módosításokat és közvetlenül beadni őket?", + "always": "Mindig", + "no changes": "Nincs beadandó módosítás.", + "commit message": "Beadási (commit) üzenet", + "provide commit message": "Adja meg a beadási (commit) üzenetet", + "select a ref to checkout": "Válassza ki a refet a checkouthoz", + "branch name": "Ág neve", + "provide branch name": "Adja meg az ág nevét", + "select branch to delete": "Válassza ki a törlendő ágat", + "confirm force delete branch": "A(z) '{0}' ág nincs teljesen beolvasztva. Mégis törli?", + "delete branch": "Ág törlése", + "select a branch to merge from": "Válassza ki az ágat, amit olvasztani szeretne", + "merge conflicts": "Összeolvasztási konfliktusok keletkeztek. Oldja fel őket a beadás (commit) előtt!", + "tag name": "Címke neve", + "provide tag name": "Adja meg a címke nevét", + "tag message": "Üzenet", + "provide tag message": "Adja meg a címke leírását tartalmazó üzenetet", + "no remotes to pull": "A forráskódtárhoz nincsenek távoli szerverek konfigurálva, ahonnan pullozni lehetne.", + "pick remote pull repo": "Válassza ki a távoli szervert, ahonnan pullozni szeretné az ágat", + "no remotes to push": "A forráskódtárhoz nincsenek távoli szerverek konfigurálva, ahová pusholni lehetne.", + "push with tags success": "A címkékkel együtt történő pusholás sikeresen befejeződött.", + "nobranch": "Válasszon egy ágat a távoli szerverre való pusholáshot!", + "pick remote": "Válassza ki a távoli szervert, ahová publikálni szeretné a(z) '{0}' ágat:", + "sync is unpredictable": "Ez a művelet pusholja és pullozza a commitokat a következő helyről: '{0}'.", + "ok": "OK", + "never again": "Rendben, ne jelenítse meg újra", + "no remotes to publish": "A forráskódtárhoz nincsenek távoli szerverek konfigurálva, ahová publikálni lehetne.", + "no changes stash": "Nincs elrakandó módosítás.", + "provide stash message": "Adja meg a stash-hez tartozó üzenet (nem kötelező)", + "stash message": "Stash-üzenet", + "no stashes": "Nincs visszaállítható stash.", + "pick stash to pop": "Válassza ki a visszaállítandó stash-t", + "clean repo": "Takarítsa ki a forráskódtár munkafáját, mielőtt checkoutolna!", + "cant push": "Nem lehet pusholni a távoli szerverre. Futtassa a 'Pull' parancsot a módosításai integrálásához!", + "git error details": "Git: {0}", + "git error": "Git-hiba", + "open git log": "Git-napló megnyitása" +} \ No newline at end of file diff --git a/i18n/hun/extensions/git/out/main.i18n.json b/i18n/hun/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..96fad87635 --- /dev/null +++ b/i18n/hun/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "Git {0} használata a következő helyről: {1}", + "updateGit": "Git frissítése", + "neverShowAgain": "Ne jelenjen meg újra", + "git20": "Úgy tűnik, hogy a git {0} van telepítve. A Code a git >= 2 verzióival működik együtt a legjobban." +} \ No newline at end of file diff --git a/i18n/hun/extensions/git/out/model.i18n.json b/i18n/hun/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..e9a5601aa1 --- /dev/null +++ b/i18n/hun/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "Nem található forráskódtár.", + "pick repo": "Válasszon forráskódtárat!" +} \ No newline at end of file diff --git a/i18n/hun/extensions/git/out/repository.i18n.json b/i18n/hun/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..2ee3dc6085 --- /dev/null +++ b/i18n/hun/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Megnyitás", + "index modified": "Index, módosítva", + "modified": "Módosítva", + "index added": "Index, hozzáadva", + "index deleted": "Index, törölve", + "deleted": "Törölve", + "index renamed": "Index, átnevezve", + "index copied": "Index, másolva", + "untracked": "Nem követett", + "ignored": "Figyelmen kívül hagyva", + "both deleted": "Mindkettő törölte", + "added by us": "Általunk hozzáadott", + "deleted by them": "Általuk törölt", + "added by them": "Általuk hozzáadott", + "deleted by us": "Általunk törölt", + "both added": "Mindkettő hozzáadta", + "both modified": "Mindkettő módosította", + "commit": "Commit", + "merge changes": "Módosítások összeolvasztása", + "staged changes": "Beadásra előjegyzett módosítások", + "changes": "Módosítások", + "ok": "OK", + "neveragain": "Soha ne jelenítse meg újra", + "huge": "A(z) '{0}' forráskódtárban túl sok aktív módosítás van. A Git-funkciók csak egy része lesz engedélyezve." +} \ No newline at end of file diff --git a/i18n/hun/extensions/git/out/scmProvider.i18n.json b/i18n/hun/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..a7e7efcb61 --- /dev/null +++ b/i18n/hun/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "Commit" +} \ No newline at end of file diff --git a/i18n/hun/extensions/git/out/statusbar.i18n.json b/i18n/hun/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..10618e0c42 --- /dev/null +++ b/i18n/hun/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "Checkout...", + "sync changes": "Módosítások szinkronizálása", + "publish changes": "Módosítások publikálása", + "syncing changes": "Módosítások szinkronizálása..." +} \ No newline at end of file diff --git a/i18n/hun/extensions/git/package.i18n.json b/i18n/hun/extensions/git/package.i18n.json new file mode 100644 index 0000000000..e1cc906228 --- /dev/null +++ b/i18n/hun/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "Klónozás", + "command.init": "Forráskódtár előkészítése", + "command.close": "Forráskódtár bezárása", + "command.refresh": "Frissítés", + "command.openChange": "Módosítások megnyitása", + "command.openFile": "Fájl megnyitása", + "command.openHEADFile": "Fájl megnyitása (HEAD)", + "command.stage": "Módosítások előjegyzése beadásra", + "command.stageAll": "Összes módosítás előjegyzése beadásra", + "command.stageSelectedRanges": "Kijelölt területek előjegyzése beadásra", + "command.revertSelectedRanges": "Kijelölt területek visszaállítása", + "command.unstage": "Módosítások előjegyzésének törlése", + "command.unstageAll": "Minden módosítás előjegyzésének törlése", + "command.unstageSelectedRanges": "Kijelölt területek előjegyzésének törlése", + "command.clean": "Módosítások elvetése", + "command.cleanAll": "Összes módosítás elvetése", + "command.commit": "Commit", + "command.commitStaged": "Előjegyzettek beadása (commit)", + "command.commitStagedSigned": "Előjegyzettek beadása (commit) aláírással", + "command.commitStagedAmend": "Előjegyzettek beadása (commit) javítással (amend)", + "command.commitAll": "Összes beadása (commit)", + "command.commitAllSigned": "Összes beadása (commit) aláírással", + "command.commitAllAmend": "Összes beadása (commint) javítással", + "command.undoCommit": "Legutolsó beadás (commit) visszavonása", + "command.checkout": "Checkout adott helyre...", + "command.branch": "Ág létrehozása...", + "command.deleteBranch": "Új ág létrehozása", + "command.merge": "Ág beolvasztása...", + "command.createTag": "Címke létrehozása", + "command.pull": "Pull", + "command.pullRebase": "Pull (Rebase)", + "command.pullFrom": "Pullozás...", + "command.push": "Push", + "command.pushTo": "Push adott helyre...", + "command.pushWithTags": "Push címkékkel", + "command.sync": "Szinkronizálás", + "command.publish": "Ág publikálása", + "command.showOutput": "Git-kimenet megjelenítése", + "command.ignore": "Fájl hozzáadása a .gitignore-hoz", + "command.stash": "Stash", + "command.stashPop": "Stash visszaállítása...", + "command.stashPopLatest": "Legutóbbi stash visszaállítása", + "config.enabled": "Meghatározza, hogy a git engedélyezve van-e", + "config.path": "A git végrehajtható fájl elérési útja", + "config.autorefresh": "Meghatározza, hogy engedélyezve van-e az automatikus frissítés", + "config.autofetch": "Meghatározza, hogy engedélyezve van-e az automatikus lekérés", + "config.enableLongCommitWarning": "Figyelmeztessen-e az alkalmazás hosszú beadási üzenet esetén", + "config.confirmSync": "Megerősítés kérése git forráskódtárak szinkronizálása előtt", + "config.countBadge": "Meghatározza a git jelvényen megjelenő számláló működését. Az `all` minden módosítást számol, a `tracked` csak a követkett változtatásokat. Az `off` kikapcsolja a jelvényt.", + "config.checkoutType": "Meghatározza, hogy milyen típusú ágak jelenjenek meg a `Checkout adott helyről... ` parancs futtatása esetén. Az `all` esetén az összes ref megjelenik, `local` esetén csak a helyi ágak, `tags` esetén csak a címkék, `remote` esetén pedig csak a távoli ágak.", + "config.ignoreLegacyWarning": "Régi gittel kapcsolatos figyelmeztetés figyelmen kívül hagyása", + "config.ignoreLimitWarning": "Túl sok módosítás esetén megjelenő figyelmeztetés figyelmen kívül hagyása", + "config.defaultCloneDirectory": "Git-forráskódtárak klónozásának alapértelmezett helye.", + "config.enableSmartCommit": "Összes módosítás beadása (commit), ha nincsenek előjegyzett módosítások.", + "config.enableCommitSigning": "Commit GPG-vel történő aláírásának engedélyezése.", + "config.discardAllScope": "Meghatározza, hogy milyen módosítások kerülnek elvetésre az \"Összes módosítás elvetése\" parancs kiadása esetén. `all` esetén minden módosítás elvetődik. `tracked` esetén csak a követett fájlok módosításai vetődnek el. `prompt` esetén a parancs minden futtatásánál felugrik egy párbeszédablak." +} \ No newline at end of file diff --git a/i18n/hun/extensions/grunt/out/main.i18n.json b/i18n/hun/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..9fc0ca8678 --- /dev/null +++ b/i18n/hun/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "A Grunt automatikus felderítése nem sikerült a következő hiba miatt: {0}" +} \ No newline at end of file diff --git a/i18n/hun/extensions/grunt/package.i18n.json b/i18n/hun/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..bc2147df17 --- /dev/null +++ b/i18n/hun/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "Meghatározza, hogy a Grunt feladatok automatikus felderításe be van-e kapcsolva. A beállítás alapértelmezetten aktív." +} \ No newline at end of file diff --git a/i18n/hun/extensions/gulp/out/main.i18n.json b/i18n/hun/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..04bfd993b5 --- /dev/null +++ b/i18n/hun/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "A gulp automatikus felderítése nem sikerült a következő hiba miatt: {0}" +} \ No newline at end of file diff --git a/i18n/hun/extensions/gulp/package.i18n.json b/i18n/hun/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..8c039b907b --- /dev/null +++ b/i18n/hun/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "Meghatározza, hogy a Gulp feladatok automatikus felderításe be van-e kapcsolva. A beállítás alapértelmezetten aktív." +} \ No newline at end of file diff --git a/i18n/hun/extensions/html/client/out/htmlMain.i18n.json b/i18n/hun/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..dd72664ded --- /dev/null +++ b/i18n/hun/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "HTML nyelvi szerver" +} \ No newline at end of file diff --git a/i18n/hun/extensions/html/package.i18n.json b/i18n/hun/extensions/html/package.i18n.json new file mode 100644 index 0000000000..8952543ef4 --- /dev/null +++ b/i18n/hun/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "Alapértelmezett HTML-formázó engedélyezése vagy letiltása.", + "html.format.wrapLineLength.desc": "Maximális karakterszám soronként (0 = letiltás)", + "html.format.unformatted.desc": "Azon elemek vesszővel elválasztott listája, melyek ne legyenek újraformázva. 'null' érték esetén a https://www.w3.org/TR/html5/dom.html#phrasing-content oldalon listázott elemek lesznek használva.", + "html.format.contentUnformatted.desc": "Azon elemek vesszővel elválasztott listája, melyek ne legyenek újraformázva. 'null' érték esetén a 'pre' tag lesz használva.", + "html.format.indentInnerHtml.desc": "- és -szakaszok indentálása.", + "html.format.preserveNewLines.desc": "Az elemek előtt lévő sortörések meg legyenek-e hagyva. Csak elemek előtt működik, elemek belsejében vagy szövegben nem.", + "html.format.maxPreserveNewLines.desc": "Az egymás után megőrzött sortörések maximális száma. Ha nem szeretné korlátozni, használja a 'null' értéket!", + "html.format.indentHandlebars.desc": "{{#foo}} és {{/foo}} formázása és indentálása.", + "html.format.endWithNewline.desc": "Lezárás új sorral.", + "html.format.extraLiners.desc": "Azon elemek veszővel elválasztott listája, amelyek előtt lennie kell egy extra új sornak. 'null' érték esetén a \"head,body,/html\" érték van használva.", + "html.format.wrapAttributes.desc": "Attribútumok tördelése.", + "html.format.wrapAttributes.auto": "Az attribútumok csak akkor vannak tördelve, ha a sorhossz túl lett lépve.", + "html.format.wrapAttributes.force": "Minden egyes attribútum tördelve van, kivéve az elsőt.", + "html.format.wrapAttributes.forcealign": "Minden egyes attribútum tördelve van, kivéve az elsőt, és igazítva vannak.", + "html.format.wrapAttributes.forcemultiline": "Minden egyes attribútum tördelve van.", + "html.suggest.angular1.desc": "Meghatározza, hogy a beépített HTML nyelvi támogatás ajánl-e Angular V1 elemeket és tulajdonságokat.", + "html.suggest.ionic.desc": "Meghatározza, hogy a beépített HTML nyelvi támogatás ajánl-e Ionic elemeket, tulajdonságokat és értékeket.", + "html.suggest.html5.desc": "Meghatározza, hogy a beépített HTML nyelvi támogatás ajánl-e HTML5-ös elemeket, tulajdonságokat és értékeket.", + "html.trace.server.desc": "A VS Code és a HTML nyelvi szerver közötti kommunikáció naplózása.", + "html.validate.scripts": "Meghatározza, hogy a beépített HTML nyelvi támogatás validálja-e a beágyazott parancsafájlokat.", + "html.validate.styles": "Meghatározza, hogy a beépített HTML nyelvi támogatás validálja-e a beágyazott stílusfájlokat.", + "html.autoClosingTags": "HTML-elemek automatikus lezárásának engedélyezése vagy tetiltása." +} \ No newline at end of file diff --git a/i18n/hun/extensions/jake/out/main.i18n.json b/i18n/hun/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..d7a51617e3 --- /dev/null +++ b/i18n/hun/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "A Jake automatikus felderítése nem sikerült a következő hiba miatt: {0}" +} \ No newline at end of file diff --git a/i18n/hun/extensions/jake/package.i18n.json b/i18n/hun/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..8afe03a999 --- /dev/null +++ b/i18n/hun/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "Meghatározza, hogy a Jake-feladatok automatikus felderításe be van-e kapcsolva. A beállítás alapértelmezetten aktív." +} \ No newline at end of file diff --git a/i18n/hun/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/hun/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..c6ded77106 --- /dev/null +++ b/i18n/hun/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "Alapértelmezett bower.json", + "json.bower.error.repoaccess": "A bower-adattár lekérdezése nem sikerült: {0}", + "json.bower.latest.version": "legutóbbi" +} \ No newline at end of file diff --git a/i18n/hun/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/hun/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..03f427ba9f --- /dev/null +++ b/i18n/hun/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "Alapértelmezett package.json", + "json.npm.error.repoaccess": "Az NPM-adattár lekérdezése nem sikerült: {0}", + "json.npm.latestversion": "A csomag jelenlegi legújabb verziója", + "json.npm.majorversion": "A legfrissebb főverzió keresése (1.x.x)", + "json.npm.minorversion": "A legfrissebb alverzió keresése (1.2.x)", + "json.npm.version.hover": "Legújabb verzió: {0}" +} \ No newline at end of file diff --git a/i18n/hun/extensions/json/client/out/jsonMain.i18n.json b/i18n/hun/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..17ee222f99 --- /dev/null +++ b/i18n/hun/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "JSON nyelvi szerver" +} \ No newline at end of file diff --git a/i18n/hun/extensions/json/package.i18n.json b/i18n/hun/extensions/json/package.i18n.json new file mode 100644 index 0000000000..46d8e372f8 --- /dev/null +++ b/i18n/hun/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "Sémák hozzárendelése JSON-fájlokhoz a jelenlegi projektben", + "json.schemas.url.desc": "Egy séma URL-címe vagy egy séma relatív elérési útja az aktuális könyvtárban", + "json.schemas.fileMatch.desc": "Fájlminták tömbje, amely a JSON-fájlok sémákhoz való rendelésénél van használva.", + "json.schemas.fileMatch.item.desc": "Fájlminták tömbje, amely a JSON-fájlok sémákhoz való rendelésénél van használva. Tartalmazhat '*'-ot.", + "json.schemas.schema.desc": "Az adott URL-cím sémadefiníciója. A sémát csak a séma URL-címéhez való fölösleges lekérdezések megakadályozása érdekében kell megadni.", + "json.format.enable.desc": "Alapértelmezett JSON-formázó engedélyezése vagy letiltása (újraindítást igényel)", + "json.tracing.desc": "A VS Code és a JSON nyelvi szerver közötti kommunikáció naplózása.", + "json.colorDecorators.enable.desc": "Színdekorátorok engedélyezése vagy letiltása", + "json.colorDecorators.enable.deprecationMessage": "A `json.colorDecorators.enable` beállítás elavult az `editor.colorDecorators` bevezetése miatt." +} \ No newline at end of file diff --git a/i18n/hun/extensions/markdown/out/extension.i18n.json b/i18n/hun/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..6053304805 --- /dev/null +++ b/i18n/hun/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "A 'markdown.styles' nem tölthető be: {0}" +} \ No newline at end of file diff --git a/i18n/hun/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/hun/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..4ec2dadc55 --- /dev/null +++ b/i18n/hun/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "A tartalom egy része le van tiltva az aktuális dokumentumban", + "preview.securityMessage.title": "Potencionálisan veszélyes vagy nem biztonságos tartalom lett letiltva a markdown-előnézetben. Módosítsa a markdown-előnézet biztonsági beállításait a nem biztonságos tartalmak vagy parancsfájlok engedélyezéséhez!", + "preview.securityMessage.label": "Biztonsági figyelmeztetés: tartalom le van tiltva" +} \ No newline at end of file diff --git a/i18n/hun/extensions/markdown/out/security.i18n.json b/i18n/hun/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..339ab408fc --- /dev/null +++ b/i18n/hun/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "Szigorú", + "strict.description": "Csak biztonságos tartalmak betöltése", + "insecureContent.title": "Nem biztonságos tartalom engedélyezése", + "insecureContent.description": "Tartalom betöltésének engedélyezése HTTP-n keresztül.", + "disable.title": "Letiltás", + "disable.description": "Minden tartalom és parancsfájl futtatásának engedélyezése. Nem ajánlott.", + "moreInfo.title": "További információ", + "preview.showPreviewSecuritySelector.title": "Válassza ki a munkaterület Markdown-előnézeteinek biztonsági beállítását!" +} \ No newline at end of file diff --git a/i18n/hun/extensions/markdown/package.i18n.json b/i18n/hun/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..67f7eacb7a --- /dev/null +++ b/i18n/hun/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "Meghatározza, hogy a sortörések hogyan vannak megjelenítve a markdown-előnézetben. Ha az értéke 'true', akkor minden egyes újsor esetén
jön létre.", + "markdown.preview.linkify": "URL-szerű szövegek hivatkozássá alakításának engedélyezése vagy letiltása a markdown-előnézetben.", + "markdown.preview.doubleClickToSwitchToEditor.desc": "Kattintson duplán a markdown-előnézetre a szerkesztőre való átváltáshoz.", + "markdown.preview.fontFamily.desc": "Meghatározza a markdown-előnézeten használt betűkészletet.", + "markdown.preview.fontSize.desc": "Meghatározza a markdown-előnézet betűméretét, pixelekben.", + "markdown.preview.lineHeight.desc": "Meghatározza a markdown-előnézeten használt sormagasságot. Az érték relatív a betűmérethez képest.", + "markdown.preview.markEditorSelection.desc": "Az aktuális kijelölés megjelölése a markdown-előnézeten", + "markdown.preview.scrollEditorWithPreview.desc": "Amikor a markdown-előnézetet görgetik, a szerkesztőnézet is aktualizálódik", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "A markdown-előnézetet úgy görgeti, hogy látni lehessen az aktuálisan kijelölt sort", + "markdown.preview.title": "Előnézet megnyitása", + "markdown.previewFrontMatter.dec": "Meghatározza, hogy a YAML konfiguráció (front matter) hogyan legyen megjelenítve a markdown-előnézetben. A 'hide' elrejti a konfigurációt, minden más esetben a front matter markdown-tartalomként van kezelve.", + "markdown.previewSide.title": "Előnézet megnyitása oldalt", + "markdown.showSource.title": "Forrás megjelenítése", + "markdown.styles.dec": "CSS-stíluslapok URL-címeinek vagy helyi elérési útjainak listája, amelyek a markdown-előnézeten használva vannak. A relatív elérési utak az intézőben megnyitott mappához képest vannak relatívan értelmezve. Ha nincs mappa megnyitva, akkor a markdown-fájl elréséi útjához képest. Minden '\\' karaktert '\\\\' formában kell megadni.", + "markdown.showPreviewSecuritySelector.title": "Az előnézet biztonsági beállításainak módosítása", + "markdown.trace.desc": "Hibakeresési napló engedélyezése a markdown kiterjesztésben.", + "markdown.refreshPreview.title": "Előnézet frissítése" +} \ No newline at end of file diff --git a/i18n/hun/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/hun/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..67a2930a80 --- /dev/null +++ b/i18n/hun/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "Helyi változtatás elfogadása", + "acceptIncomingChange": "Beérkező változtatás elfogadása", + "acceptBothChanges": "Változtatások elfogadása mindkét oldalról", + "compareChanges": "Változtatások összehasonlítása" +} \ No newline at end of file diff --git a/i18n/hun/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/hun/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..deb12db607 --- /dev/null +++ b/i18n/hun/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "A szerkesztőablak kurzora nem egy összeolvasztási konfliktuson belül van.", + "compareChangesTitle": "{0}: Helyi változtatások ⟷ Beérkező változtatások", + "cursorOnCommonAncestorsRange": "A szerkesztőablak kurzora a közös ős blokkján van. Vigye vagy a \"helyi\" vagy a \"beérkező\" blokkra.", + "cursorOnSplitterRange": "A szerkesztőablak kurzora az összeolvasztási konfliktus elválasztójánál van. Vigye vagy a \"helyi\" vagy a \"beérkező\" blokkra.", + "noConflicts": "Ebben a fájlban nincsenek összeolvasztási konfliktusok", + "noOtherConflictsInThisFile": "Ebben a fájlban nincsenek további összeolvasztási konfliktusok" +} \ No newline at end of file diff --git a/i18n/hun/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/hun/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..8bb736b2e2 --- /dev/null +++ b/i18n/hun/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(Helyi változtatás)", + "incomingChange": "(Beérkező változtatás)" +} \ No newline at end of file diff --git a/i18n/hun/extensions/merge-conflict/package.i18n.json b/i18n/hun/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..22a1a90e66 --- /dev/null +++ b/i18n/hun/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "Összeolvasztási konfliktus", + "command.accept.all-incoming": "Összes beérkező változás elfogadása", + "command.accept.all-both": "Változások elfogadása mindkét oldalról", + "command.accept.current": "Helyi változtatás elfogadása", + "command.accept.incoming": "Beérkező változtatás elfogadása", + "command.accept.selection": "Kijelölt változtatás elfogadása", + "command.accept.both": "Változás elfogadása mindkét oldalról", + "command.next": "Következő konfliktus", + "command.previous": "Előző konfliktus", + "command.compare": "Aktuális konfliktus összehasonlítása", + "config.title": "Összeolvasztási konfliktus", + "config.codeLensEnabled": "Összeolvasztási konfliktust jelző kódlencsék engedélyezése vagy letiltása a szerkesztőablakban.", + "config.decoratorsEnabled": "Összeolvasztási konfliktust jelző dekorátorok engedélyezése vagy letiltása a szerkesztőablakban." +} \ No newline at end of file diff --git a/i18n/hun/extensions/npm/package.i18n.json b/i18n/hun/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..c78234465d --- /dev/null +++ b/i18n/hun/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "Meghatározza, hogy az npm-parancsfájlok automatikus felderításe be van-e kapcsolva. A beállítás alapértelmezetten aktív.", + "config.npm.runSilent": "Npm parancsok futtatása a `--silent` kapcsolóval" +} \ No newline at end of file diff --git a/i18n/hun/extensions/php/out/features/validationProvider.i18n.json b/i18n/hun/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..953ae816bb --- /dev/null +++ b/i18n/hun/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "Engedélyezi a(z) {0} (munkaterületi beállításként megadott) végrehajtását a PHP-fájlok linteléséhez?", + "php.yes": "Engedélyezés", + "php.no": "Tiltás", + "wrongExecutable": "A validáció nem sikerült, mivel a(z) {0} nem egy érvényes php végrehajtható fájl. Használja a 'php.validate.executablePath' beállítást a PHP végrehajtható fájl konfigurálásához!", + "noExecutable": "A validáció nem sikerült, mivel nincs beállítva PHP végrehajtható fájl. Használja a 'php.validate.executablePath' beállítást a PHP végrehajtható fájl konfigurálásához!", + "unknownReason": "Nem sikerült futtatni a PHP-t a következő elérési út használatával: {0}. Az ok ismeretlen." +} \ No newline at end of file diff --git a/i18n/hun/extensions/php/package.i18n.json b/i18n/hun/extensions/php/package.i18n.json new file mode 100644 index 0000000000..f2660eb449 --- /dev/null +++ b/i18n/hun/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "Meghatározza, hogy a beépített PHP nyelvi támogatás ajánl-e PHP globálisokat és változókat.", + "configuration.validate.enable": "Beépített PHP-validáció engedélyezése vagy letiltása", + "configuration.validate.executablePath": "A PHP végrehajtható fájljának elérési útja.", + "configuration.validate.run": "A linter mentéskor vagy gépeléskor fut-e.", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "PHP-validációs végrehajtható fájl letiltása (munkaterületi beállításként megadva)" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/hun/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..d5568d4ba7 --- /dev/null +++ b/i18n/hun/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "TypeScript ({1}) használata a szerkesztőfunkciókhoz. A számítógére TypeScript (01}) van globálisan telepítve. A VS Code-ban látható hibák eltérhetnek a TSC által visszaadott hibáktól.", + "moreInformation": "További információ", + "doNotCheckAgain": "Ne ellenőrizze újra", + "close": "Bezárás", + "updateTscCheck": "A 'typescript.check.tscVersion' felhasználói beállítás értéke módosítva false-ra" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/hun/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..73cb392dd0 --- /dev/null +++ b/i18n/hun/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "Típusdefiníciók letöltése...", + "acquiringTypingsDetail": "Típusdefiníciók letöltése az IntelliSense-hez." +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/hun/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..ef33b0338e --- /dev/null +++ b/i18n/hun/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "Engedélyezi a JavaScript-fájlok szemantikai ellenőrzését. A fájl tetején kell szerepelnie.", + "ts-nocheck": "Letiltja a JavaScript-fájlok szemantikai ellenőrzését. A fájl tetején kell szerepelnie.", + "ts-ignore": "Elfedi a fájl következő sorában található @ts-check-hibákat." +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/hun/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..342159e277 --- /dev/null +++ b/i18n/hun/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 implementáció", + "manyImplementationLabel": "{0} implementáció", + "implementationsErrorLabel": "Nem sikerült meghatározni az implementációkat" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/hun/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..32f9269b21 --- /dev/null +++ b/i18n/hun/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "JSDoc-megjegyzés" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/hun/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..4a2f65bb33 --- /dev/null +++ b/i18n/hun/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 referencia", + "manyReferenceLabel": "{0} referencia", + "referenceErrorLabel": "Nem sikerült meghatározni a referenciákat" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/hun/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..85477778e6 --- /dev/null +++ b/i18n/hun/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "figyelés – {0}", + "buildTscLabel": "buildelés – {0}" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/typescriptMain.i18n.json b/i18n/hun/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..940ce90952 --- /dev/null +++ b/i18n/hun/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "Nyisson meg egy mappát a VS Code-ban typescriptes vagy javascriptes projekt használatához!", + "typescript.projectConfigUnsupportedFile": "Nem sikerült meghatározni a TypeScript- vagy JavaScript-projektet. Nem támogatott fájltípus", + "typescript.projectConfigCouldNotGetInfo": "Nem sikerült meghatározni a TypeScript- vagy JavaScript-projektet", + "typescript.noTypeScriptProjectConfig": "A fájl nem része egy TypeScript-projektnek", + "typescript.noJavaScriptProjectConfig": "A fájl nem része egy JavaScript-projektnek", + "typescript.configureTsconfigQuickPick": "tsconfig.json konfigurálása", + "typescript.configureJsconfigQuickPick": "jsconfig.json konfigurálása", + "typescript.projectConfigLearnMore": "További információ" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/hun/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..e9872a329c --- /dev/null +++ b/i18n/hun/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "A(z) {0} elérési út nem egy érvényes tsserver-telepítésre mutat. A beépített TypeScript-verzió lesz használva.", + "serverCouldNotBeStarted": "Nem sikerült elindítani a TypeScript nyelvi szervert. Hibaüzenet: {0}", + "typescript.openTsServerLog.notSupported": "A TS-szerver naplózáshoz TS 2.2.2+ szükséges", + "typescript.openTsServerLog.loggingNotEnabled": "A TS-szervernaplózás ki van kapcsolva. Állítsa be a `typescript.tsserver.log` beállítást, majd indítsa újra a TS-szervert a naplózás engedélyezéséhez!", + "typescript.openTsServerLog.enableAndReloadOption": "Naplózás engedélyezése és TS-szerver újraindítása", + "typescript.openTsServerLog.noLogFile": "A TS-szerver nem kezdett el naplózni", + "openTsServerLog.openFileFailedFailed": "A TS-szervernapló nem nyitható meg", + "serverDiedAfterStart": "A TypeScript nyelvi szolgáltatás öt alkalommal omlott össze rögtön azután, hogy el lett indítva. A szolgáltatás nem lesz újraindítva.", + "serverDiedReportIssue": "Probléma jelentése", + "serverDied": "A TypeScript nyelvi szolgáltatás öt alkalommal omlott össze az elmúlt öt percben." +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/utils/api.i18n.json b/i18n/hun/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..0e6e5d161c --- /dev/null +++ b/i18n/hun/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "érvénytelen verzió" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/utils/logger.i18n.json b/i18n/hun/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/hun/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/hun/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..0e4c50c8d5 --- /dev/null +++ b/i18n/hun/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "A JavaScript/TypeScript funkciók teljes projektre való engedélyezéséhez zárja ki a sok fájlt tartalmazó mappákat. Például: {0}", + "hintExclude.generic": "A JavaScript/TypeScript funkciók teljes projektre való engedélyezéséhez zárja ki azokat a mappákat, amelyben olyan forrásfájlok találhatók, melyen nem dolgozik.", + "large.label": "Kivételek konfigurálása", + "hintExclude.tooltip": "A JavaScript/TypeScript funkciók teljes projektre való engedélyezéséhez zárja ki azokat a mappákat, amelyben olyan forrásfájlok találhatók, melyen nem dolgozik. " +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/hun/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..34150c190e --- /dev/null +++ b/i18n/hun/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "Adatok lekérése a jobb typescriptes IntelliSense-hez", + "typesInstallerInitializationFailed.title": "Nem sikerült telepíteni a típusdefiníciós fájlokat a javascriptes nyelvi funkciókhoz. Győződjön meg róla, hogy az NPM telepítve van vagy módosítsa a 'typescript.npm' beállítás értékét a felhasználói beállításokban", + "typesInstallerInitializationFailed.moreInformation": "További információ", + "typesInstallerInitializationFailed.doNotCheckAgain": "Ne ellenőrizze újra", + "typesInstallerInitializationFailed.close": "Bezárás" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/hun/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..37c056e9f2 --- /dev/null +++ b/i18n/hun/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "A VSCode verziójának használata", + "useWorkspaceVersionOption": "A munkaterület verziójának használata", + "learnMore": "További információ", + "selectTsVersion": "Válassza ki a javascriptes és typescriptes nyelvi funkciókhoz használt TypeScript-verziót" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/hun/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..b4234571cc --- /dev/null +++ b/i18n/hun/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "Nem sikerült meghatározni a TypeScript-verziót ezen az elérési úton", + "noBundledServerFound": "A VSCode beépített tsserverét törölte egy másik alkalmazás, például egy hibásan viselkedő víruskereső eszköz. Telepítse újra a VSCode-ot!" +} \ No newline at end of file diff --git a/i18n/hun/extensions/typescript/package.i18n.json b/i18n/hun/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..d4f946f636 --- /dev/null +++ b/i18n/hun/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "Projekt újratöltése", + "javascript.reloadProjects.title": "Projekt újratöltése", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "Függvények kiegészítése paraméterdefiníciójukkal.", + "typescript.tsdk.desc": "A tsservert és a lib*.d.ts fájlokat tartalmazó mappa elérési útja.", + "typescript.disableAutomaticTypeAcquisition": "Automatikus típusdefiníció-letöltés letiltása. Legalább 2.0.6-os TypeScriptet igényel.", + "typescript.tsserver.log": "Engedélyezi a TS-szerver naplózását egy fájlba. Ez a napló a TS-szerverrel kapcsolatos problémák diagnosztizálására használható. A napló tartalmazhat elérési utakat, forráskódot és más potenciálisan érzékeny, projekttel kapcsolatos adatot.", + "typescript.tsserver.trace": "Engedélyezi a TS-szervernek küldött üzenetek naplózását. Ez a napló a TS-szerverrel kapcsolatos problémák diagnosztizálására használható. A napló tartalmazhat elérési utakat, forráskódot és más potenciálisan érzékeny, projekttel kapcsolatos adatot. ", + "typescript.validate.enable": "TypeScript-validálás engedélyezése vagy letiltása.", + "typescript.format.enable": "Alapértelmezett TypeScript-formázó engedélyezése vagy letiltása.", + "javascript.format.enable": "Alapértelmezett JavaScript-formázó engedélyezése vagy letiltása.", + "format.insertSpaceAfterCommaDelimiter": "Meghatározza a szóközök kezelését vessző elválasztókarakter után.", + "format.insertSpaceAfterConstructor": "Meghatározza a szóközök kezelését a constructor kulcsszó után. TypeScript >= 2.3.0-t igényel.", + "format.insertSpaceAfterSemicolonInForStatements": "Meghatározza a szóközök kezelését pontosvessző után a for ciklusban.", + "format.insertSpaceBeforeAndAfterBinaryOperators": "Meghatározza a szóközök kezelését bináris operátorok után.", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "Meghatározza a szóközök kezelését vezérlési szerkezetek kulcsszavai után.", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "Meghatározza a szóközök kezelését a névtelen függvényekben található function kulcsszó után.", + "format.insertSpaceBeforeFunctionParenthesis": "Meghatározza a szóközök kezelését a függvényargumentumokat tartalmazó zárójel előtt. TypeScript >= 2.1.5-öt igényel.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "Meghatározza a szóközök kezelését nem üres zárójelek nyitása után és zárása előtt.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "Meghatározza a szóközök kezelését nem üres szögletes zárójelek nyitása után és zárása előtt.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Meghatározza a szóközök kezelését nem üres kapcsos zárójelek nyitása után és zárása előtt. TypeScript >= 2.3.0-t igényel.", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "Meghatározza a szóközök kezelését a sablonkarakterláncok (template stringek) kapcsos zárójeleinek nyitása után és zárása előtt. TypeScript >= 2.0.6-ot igényel.", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Meghatározza a szóközök kezelését JSX-kifejezések kapcsos zárójeleinek nyitása után és zárása előtt. TypeScript >= 2.0.6-ot igényel.", + "format.insertSpaceAfterTypeAssertion": "Meghatározza a szóközök kezelését a típuskényszerítések utn. TypeScript >= 2.4-t igényel.", + "format.placeOpenBraceOnNewLineForFunctions": "Meghatározza, hogy a függvények nyitó kapcsos zárójelei új sorba kerüljenek-e vagy sem.", + "format.placeOpenBraceOnNewLineForControlBlocks": "Meghatározza, hogy a vezérlőblokkok nyitó kapcsos zárójelei új sorba kerüljenek-e vagy sem.", + "javascript.validate.enable": "JavaScript-validálás engedélyezése vagy letiltása.", + "typescript.goToProjectConfig.title": "Projektkonfiguráció megkeresése", + "javascript.goToProjectConfig.title": "Projektkonfiguráció megkeresése", + "javascript.referencesCodeLens.enabled": "Referencia kódlencsék engedélyezése vagy letiltása a JavaScript-fájlokban.", + "typescript.referencesCodeLens.enabled": "Referencia kódlencsék engedélyezése vagy letiltása a TypeScript-fájlokban. TypeScript >= 2.0.6-ot igényel.", + "typescript.implementationsCodeLens.enabled": "Implementációs kódlencsék engedélyezése vagy letiltása. TypeScript >= 2.2.0-t igényel.", + "typescript.openTsServerLog.title": "TS-szervernapló megnyitása", + "typescript.restartTsServer": "TS-szerver újraindítása", + "typescript.selectTypeScriptVersion.title": "TypeScript-verzió kiválasztása", + "jsDocCompletion.enabled": "Automatikus JSDoc-megjegyzések engedélyezése vagy letiltása", + "javascript.implicitProjectConfig.checkJs": "JavaScript-fájlok szemantikai ellenőrzésének engedélyezése vagy letiltása. A meglévő jsconfig.json vagy tsconfig.json fájlok felülírják ezt a beállítást. TypeScript >= 2.3.1-et igényel.", + "typescript.npm": "Az automatikus típusdefiníció-letöltéshez használt NPM végrehajtható fájl elérési útja. TypeScript 2.3.4-et igényel.", + "typescript.check.npmIsInstalled": "Ellenőrizze, hogy az NPM telepítve van-e az automatikus típusdefiníció-letöltéshez.", + "javascript.nameSuggestions": "Egyedi nevek listázásának engedélyezése a javascriptes javaslati listákban.", + "typescript.tsc.autoDetect": "Meghatározza, hogy a tsc-feladatok automatikus felderítése be van-e kapcsolva.", + "typescript.problemMatchers.tsc.label": "TypeScript-problémák", + "typescript.problemMatchers.tscWatch.label": "TypeScript-problémák (figyelő módban)" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/hun/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/hun/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/hun/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..52b794fe7e --- /dev/null +++ b/i18n/hun/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (ismét előfordult)" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/hun/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..d347bb67b7 --- /dev/null +++ b/i18n/hun/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "bemeneti adat" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/hun/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..91ed54c318 --- /dev/null +++ b/i18n/hun/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "Kis- és nagybetűk megkülönböztetése", + "wordsDescription": "Csak teljes szavas egyezés", + "regexDescription": "Reguláris kifejezés használata" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/hun/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..7dc3bb3bdc --- /dev/null +++ b/i18n/hun/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Hiba: {0}", + "alertWarningMessage": "Figyelmeztetés: {0}", + "alertInfoMessage": "Információ: {0}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/hun/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..23391ab36c --- /dev/null +++ b/i18n/hun/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "A kép túl nagy a szerkesztőben való megjelenítéshez.", + "resourceOpenExternalButton": "Kép megnyitása külső program használatával?", + "nativeBinaryError": "A fájl nem jeleníthető meg a szerkesztőben, mert bináris adatokat tartalmaz, túl nagy vagy nem támogatott szövegkódolást használ.", + "sizeB": "{0} B", + "sizeKB": "{0} KB", + "sizeMB": "{0} MB", + "sizeGB": "{0} GB", + "sizeTB": "{0} TB" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/hun/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..75ddb80eb8 --- /dev/null +++ b/i18n/hun/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "Tovább" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/common/errorMessage.i18n.json b/i18n/hun/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..e36ebeb773 --- /dev/null +++ b/i18n/hun/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}: {1}", + "error.permission.verbose": "Engedélyhiány (HTTP {0})", + "error.permission": "Engedélyhiány", + "error.http.verbose": "{0} (HTTP {1}: {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "Ismeretlen csatlakozási hiba ({0})", + "error.connection.unknown": "Ismeretlen csatlakozási hiba történt. Vagy megszakadt az internetkapcsolat, vagy a kiszolgáló vált offline-ná.", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "Ismeretlen hiba történt. Részletek a naplóban.", + "nodeExceptionMessage": "Rendszerhiba történt ({0})", + "error.moreErrors": "{0} (összesen {1} hiba)" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/hun/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..f05485ffe7 --- /dev/null +++ b/i18n/hun/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Érvénytelen szimbólum", + "error.invalidNumberFormat": "Érvénytelen számformátum.", + "error.propertyNameExpected": "Hiányzó tulajdonságnév", + "error.valueExpected": "Hiányzó érték.", + "error.colonExpected": "Hiányzó kettőspont.", + "error.commaExpected": "Hiányzó vessző", + "error.closeBraceExpected": "Hiányzó záró kapcsos zárójel", + "error.closeBracketExpected": "Hiányzó záró szögletes zárójel", + "error.endOfFileExpected": "Itt fájlvége jelnek kellene szerepelnie." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/common/keybindingLabels.i18n.json b/i18n/hun/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..7a9053d2f7 --- /dev/null +++ b/i18n/hun/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Shift", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Shift", + "altKey.long": "Alt", + "cmdKey.long": "Parancs", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/common/processes.i18n.json b/i18n/hun/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..9d8873cbe4 --- /dev/null +++ b/i18n/hun/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "Hiba: a végrehajtási információnak definiálnia kell egy karakterlánc típusú parancsot.", + "ExecutableParser.isShellCommand": "Figyelmeztetés: az isShellCommand értékének boolean típusúnak kell lennie. A következő érték figyelmen kívül van hagyva: {0}.", + "ExecutableParser.args": "Figyelmeztetés: az args értékének string[] típusúnak kell lennie. A következő érték figyelmen kívül van hagyva: {0}.", + "ExecutableParser.invalidCWD": "Figyelmeztetés: az options.cwd értékének string típusúnak kell lennie. A következő érték figyelmen kívül van hagyva: {0}." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/common/severity.i18n.json b/i18n/hun/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..2aba7437eb --- /dev/null +++ b/i18n/hun/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "Hiba", + "sev.warning": "Figyelmeztetés", + "sev.info": "Információ" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/node/processes.i18n.json b/i18n/hun/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..89f8f3d651 --- /dev/null +++ b/i18n/hun/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "Rendszerparancsok UNC-meghajtókon nem hajthatók végre." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/node/zip.i18n.json b/i18n/hun/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..0e0959d8ea --- /dev/null +++ b/i18n/hun/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "{0} nem található a zipen belül." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/hun/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..53504cd63e --- /dev/null +++ b/i18n/hun/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0}, választó", + "quickOpenAriaLabel": "választó" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/hun/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..9347a3e2c9 --- /dev/null +++ b/i18n/hun/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "Gyorsválasztó. Kezdjen el gépelni a találati lista szűkítéséhez!", + "treeAriaLabel": "Gyorsválasztó" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/hun/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..26fd485851 --- /dev/null +++ b/i18n/hun/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Összecsukás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/code/electron-main/auth.i18n.json b/i18n/hun/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..57c5f1ea72 --- /dev/null +++ b/i18n/hun/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "A proxy hitelesítést igényel", + "proxyauth": "A(z) {0} proxy használatához hitelesítés szükséges." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/code/electron-main/menus.i18n.json b/i18n/hun/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..155d23d435 --- /dev/null +++ b/i18n/hun/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "&&Fájl", + "mEdit": "Sz&&erkesztés", + "mSelection": "Kijelölé&&s", + "mView": "&&Nézet", + "mGoto": "U&&grás", + "mDebug": "Hi&&bakeresés", + "mWindow": "Ablak", + "mHelp": "&&Súgó", + "mTask": "&&Feladatok", + "miNewWindow": "Új &&ablak", + "mAbout": "A(z) {0} névjegye", + "mServices": "Szolgáltatások", + "mHide": "{0} elrejtése", + "mHideOthers": "Egyebek elrejtése", + "mShowAll": "Az összes megjelenítése", + "miQuit": "Kilépés innen: {0}", + "miNewFile": "Ú&&j fájl", + "miOpen": "&&Megnyitás", + "miOpenWorkspace": "&&Munkaterület megnyitása...", + "miOpenFolder": "Ma&&ppa megnyitása", + "miOpenFile": "&&Fájl megnyitása", + "miOpenRecent": "&&Legutóbbi megnyitása", + "miSaveWorkspaceAs": "Munkaterület menté&&se másként...", + "miAddFolderToWorkspace": "&&Mappa hozzáadása a munkaterülethez...", + "miSave": "Menté&&s", + "miSaveAs": "M&&entés másként", + "miSaveAll": "Összes men&&tése", + "miAutoSave": "Automatikus mentés", + "miRevert": "Fájl &&visszaállítása", + "miCloseWindow": "Ablak be&&zárása", + "miCloseWorkspace": "Munkaterület be&&zárása", + "miCloseFolder": "Mappa &&bezárása", + "miCloseEditor": "Szer&&kesztőablak bezárása", + "miExit": "&&Kilépés", + "miOpenSettings": "&&Beállítások", + "miOpenKeymap": "&&Billentyűparancsok", + "miOpenKeymapExtensions": "Billentyűparancs-kiegészítő&&k", + "miOpenSnippets": "Felhasználói kód&&részletek", + "miSelectColorTheme": "Szín&&téma", + "miSelectIconTheme": "Fájl&&ikonok témája", + "miPreferences": "&&Beállítások", + "miReopenClosedEditor": "Bezárt szerkesztőablak ú&&jranyitása", + "miMore": "&&Továbbiak...", + "miClearRecentOpen": "Leg&&utóbb megnyitottak listájának ürítése", + "miUndo": "&&Visszavonás", + "miRedo": "Ú&&jra", + "miCut": "&&Kivágás", + "miCopy": "&&Másolás", + "miPaste": "&&Beillesztés", + "miFind": "K&&eresés", + "miReplace": "&&Csere", + "miFindInFiles": "Keresés a &&fájlokban", + "miReplaceInFiles": "Csere a fá&&jlokban", + "miEmmetExpandAbbreviation": "Emmet: Rövidítés k&&ibontása", + "miShowEmmetCommands": "E&&mmet...", + "miToggleLineComment": "&&Egysoros megjegyzés ki-/bekapcsolása", + "miToggleBlockComment": "Me&&gjegyzésblokk ki-/bekapcsolása", + "miMultiCursorAlt": "Váltás Alt+kattintásra több kurzorhoz", + "miMultiCursorCmd": "Váltás Cmd+kattintásra több kurzorhoz", + "miMultiCursorCtrl": "Váltás Ctrl+kattintásra több kurzorhoz", + "miInsertCursorAbove": "&&Kurzor beszúrása egy sorral feljebb", + "miInsertCursorBelow": "Ku&&rzor beszúrása egy sorral feljebb", + "miInsertCursorAtEndOfEachLineSelected": "K&&urzor beszúrása a sorok végére", + "miAddSelectionToNextFindMatch": "&&Következő találat kijelölése", + "miAddSelectionToPreviousFindMatch": "&&Előző találat kijelölése", + "miSelectHighlights": "Az összes keresési találat &&kijelölése", + "miCopyLinesUp": "Sor másolása eggyel &&feljebb", + "miCopyLinesDown": "Sor másolása eggyel &&lejjebb", + "miMoveLinesUp": "Sor feljebb &&helyezése", + "miMoveLinesDown": "Sor lejje&&bb helyezése", + "miSelectAll": "Az össze&&s kijelölése", + "miSmartSelectGrow": "Kijelölés &&bővítése", + "miSmartSelectShrink": "Ki&&jelölés szűkítése", + "miViewExplorer": "Fájlk&&ezelő", + "miViewSearch": "&&Keresés", + "miViewSCM": "&&Verziókezelés", + "miViewDebug": "&&Hibakeresés", + "miViewExtensions": "Kiegé&&szítők", + "miToggleOutput": "&&Kimenet", + "miToggleDebugConsole": "Hi&&bakeresési konzol", + "miToggleIntegratedTerminal": "Beépített term&&inál", + "miMarker": "&&Problémák", + "miAdditionalViews": "További &&nézetek", + "miCommandPalette": "Paran&&cskatalógus...", + "miToggleFullScreen": "&&Teljes képernyő be- és kikapcsolása", + "miToggleZenMode": "Zen mód be- és kikapcsolása", + "miToggleMenuBar": "Menüsáv &&be- és kikapcsolása", + "miSplitEditor": "Szerkesztőablak k&&ettéosztása", + "miToggleEditorLayout": "Szerkesztőablak-csoport e&&lrendezésének váltása", + "miToggleSidebar": "&&Oldalsáv be- és kikapcsolása", + "miMoveSidebarRight": "Oldalsáv áthelyezése &&jobbra", + "miMoveSidebarLeft": "Oldalsáv áthelyezése &&balra", + "miTogglePanel": "&&Panel be- és kikapcsolása", + "miHideStatusbar": "Állapotsor &&elrejtése", + "miShowStatusbar": "Állapotsor &&megjelenítése", + "miHideActivityBar": "&&Tevékenységsáv elrejtése", + "miShowActivityBar": "&&Tevékenységsáv megjelenítése", + "miToggleWordWrap": "&&Sortörés be- és kikapcsolása", + "miToggleMinimap": "&&Kódtérkép be- és kikapcsolása", + "miToggleRenderWhitespace": "S&&zóközök kirajzolásának be- és kikapcsolása", + "miToggleRenderControlCharacters": "&&Vezérlőkarakterek kirajzolásának be- és kikapcsolása", + "miZoomIn": "&&Nagyítás", + "miZoomOut": "&&Kicsinyítés", + "miZoomReset": "&&Nagyítási szint alaphelyzetbe állítása", + "miBack": "&&Vissza", + "miForward": "&&Előre", + "miNextEditor": "&&Következő szerkesztőablak", + "miPreviousEditor": "&&Előző szerkesztőablak", + "miNextEditorInGroup": "Következő &&használt szerkesztőablak a csoportban", + "miPreviousEditorInGroup": "Következő h&&asznált szerkesztőablak a csoportban", + "miSwitchEditor": "Sz&&szerkesztőablak váltása", + "miFocusFirstGroup": "&&Első csoport", + "miFocusSecondGroup": "&&Második csoport", + "miFocusThirdGroup": "&&Harmadik csoport", + "miNextGroup": "&&Következő csoport", + "miPreviousGroup": "&&Előző csoport", + "miSwitchGroup": "Csoport &&váltása", + "miGotoFile": "Ugrás &&fájlhoz...", + "miGotoSymbolInFile": "Ugrás szim&&bólumhoz egy fájlban...", + "miGotoSymbolInWorkspace": "Ugrás szimbólumhoz a &&munkaterületen...", + "miGotoDefinition": "Ugrás a &&definícióra", + "miGotoTypeDefinition": "Ugrás a &&típusdefinícióra", + "miGotoImplementation": "Ugrás az &&implementációra", + "miGotoLine": "&&Sor megkeresáse...", + "miStartDebugging": "Hibakeresés indítá&&sa", + "miStartWithoutDebugging": "Indítás hibakeresés &&nélkül", + "miStopDebugging": "Hibakeresés leállítá&&sa", + "miRestart Debugging": "Hibakeresés új&&raindítása", + "miOpenConfigurations": "&&Konfigurációk megnyitása", + "miAddConfiguration": "Konfiguráció hozzáadása...", + "miStepOver": "Á&&tugrás", + "miStepInto": "&&Belépés", + "miStepOut": "&&Kilépés", + "miContinue": "&&Folytatás", + "miToggleBreakpoint": "&&Töréspont be- és kikapcsolása", + "miConditionalBreakpoint": "Feltételes törés&&pont", + "miColumnBreakpoint": "Töréspont &&oszlopnál", + "miFunctionBreakpoint": "Töréspont&&funkció...", + "miNewBreakpoint": "Ú&&j töréspont", + "miEnableAllBreakpoints": "Összes töréspont engedélyezése", + "miDisableAllBreakpoints": "Összes töréspont leti&<ása", + "miRemoveAllBreakpoints": "Összes töréspont eltávolítás&&a", + "miInstallAdditionalDebuggers": "Tovább&&i hibakeresők telepítése", + "mMinimize": "Kis méret", + "mZoom": "Nagyítás", + "mBringToFront": "Legyen az összes előtérben", + "miSwitchWindow": "&&Ablak váltása...", + "miToggleDevTools": "&&Fejlesztői eszközök be- és kikapcsolása", + "miAccessibilityOptions": "&&Kisegítő lehetőségek", + "miReportIssues": "&&Problémák jelentése", + "miWelcome": "Üdvözlő&&oldal", + "miInteractivePlayground": "&&Interaktív játszótér", + "miDocumentation": "&&Dokumentáció", + "miReleaseNotes": "&&Kiadási jegyzék", + "miKeyboardShortcuts": "Billentyűparancs-&&referencia", + "miIntroductoryVideos": "Bemutató&&videók", + "miTipsAndTricks": "&&Tippek és trükkök", + "miTwitter": "&&Csatlakozzon hozzánk a Twitteren", + "miUserVoice": "Funkcióigények keresé&&se", + "miLicense": "&&Licenc megtekintése", + "miPrivacyStatement": "&&Adatvédelmi nyilatkozat", + "miAbout": "&&Névjegy", + "miRunTask": "&&Feladat futtatása...", + "miBuildTask": "&&Buildelési feladat futtatása...", + "miRunningTask": "&&Futó feladatok megjelenítése...", + "miRestartTask": "Futó f&&eladat újraindítása...", + "miTerminateTask": "Felada&&t megszakítása...", + "miConfigureTask": "Feladatok &&konfigurálása", + "miConfigureBuildTask": "Alapértelmezett buildelési &&feladat beállítása", + "accessibilityOptionsWindowTitle": "Kisegítő lehetőségek beállításai", + "miRestartToUpdate": "Újraindítás a frissítéshez...", + "miCheckingForUpdates": "Frissítések keresése...", + "miDownloadUpdate": "Elérhető frissítés letöltése", + "miDownloadingUpdate": "Frissítés letöltése...", + "miInstallingUpdate": "Frissítés telepítése...", + "miCheckForUpdates": "Frissítések keresése...", + "aboutDetail": "\nVerzió: {0}\nCommit: {1}\nDátum: {2}\nShell: {3}\nRendelő: {4}\nNode: {5}\nArchitektúra: {6}", + "okButton": "OK" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/code/electron-main/window.i18n.json b/i18n/hun/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..0c8a363683 --- /dev/null +++ b/i18n/hun/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "A menüsáv továbbra is elréhető az **Alt** billentyű leütésével." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/code/electron-main/windows.i18n.json b/i18n/hun/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..1a29d09e85 --- /dev/null +++ b/i18n/hun/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "OK", + "pathNotExistTitle": "Az elérési út nem létezik", + "pathNotExistDetail": "Úgy tűnik, hogy a(z) „{0}” elérési út már nem létezik a lemezen.", + "openWorkspace": "&&Megnyitás", + "openWorkspaceTitle": "Munkaterület megnyitása", + "save": "Menté&&s", + "doNotSave": "&&Ne mentse", + "cancel": "Mégse", + "saveWorkspaceMessage": "Szeretné menteni a munkaterület konfigurációját egy fájlba?", + "saveWorkspaceDetail": "Mentse el a munkaterületet, ha meg szeretné nyitni újra!", + "saveWorkspace": "Munkaterület mentése", + "reopen": "Újranyitás", + "wait": "Várakozás tovább", + "close": "Bezárás", + "appStalled": "Az ablak nem válaszol", + "appStalledDetail": "Bezárhatja vagy újranyithatja az ablakot vagy várakozhat tovább.", + "appCrashed": "Az ablak összeomlott", + "appCrashedDetail": "Elnézést kérünk az okozott kellemetlenségért. Nyissa újra az ablakot, ha onnan szeretné folytatni a munkát, ahol abbahagyta.", + "open": "Megnyitás", + "openFolder": "Mappa megnyitása", + "openFile": "Fájl megnyitása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/code/node/cliProcessMain.i18n.json b/i18n/hun/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..e508a14479 --- /dev/null +++ b/i18n/hun/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "A(z) '{0}' kiegészítő nem található.", + "notInstalled": "A(z) '{0}' kiegészítő nincs telepítve.", + "useId": "Bizonyosodjon meg róla, hogy a kiegészítő teljes azonosítóját használja, beleértve a kiadót, pl.: {0}", + "successVsixInstall": "A(z) '{0}' kiegszítő sikeresen telepítve lett.", + "alreadyInstalled": "A(z) '{0}' kiegészítő már telepítve van.", + "foundExtension": "A(z) '{0}' kiegészítő megtalálva a piactéren.", + "installing": "Telepítés...", + "successInstall": "A(z) '{0}' v{1} kiegészítő sikeresen telepítve lett.", + "uninstalling": "{0} eltávolítása...", + "successUninstall": "A(z) '{0}' kiegészítő sikeresen el lett távolítva." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/hun/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..154d2581d0 --- /dev/null +++ b/i18n/hun/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "A fájlok nem hasonlíthatók össze, mert az egyik fájl túl nagy." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/hun/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..7414ca8b53 --- /dev/null +++ b/i18n/hun/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Bezárás", + "header": "{0}. eltérés, összesen: {1}. Eredeti: {2}., {3}. sorok, módosított: {4}., {5}. sorok", + "blankLine": "üres", + "equalLine": "eredeti {0}., módosított {1}.: {2}", + "insertLine": "+ módosított {0}.: {1}", + "deleteLine": "- eredeti {0}.: {1}", + "editor.action.diffReview.next": "Ugrás a következő eltérésre", + "editor.action.diffReview.prev": "Ugrás az előző eltérésre" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/hun/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..0c1fe7f677 --- /dev/null +++ b/i18n/hun/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "Szerkesztőablak", + "fontFamily": "Ez a beállítás a betűkészletet határozza meg.", + "fontWeight": "Meghatározza a betűvastagságot.", + "fontSize": "Meghatározza a betű méretét, pixelekben.", + "lineHeight": "Meghatározza a sormagasságot. A 0 érték használata esetén a sormagasság a fontSize értékéből van számolva.", + "letterSpacing": "Meghatározza a betűközt, pixelekben.", + "lineNumbers": "Meghatározza, hogy megjelenjenek-e a sorszámok. A lehetséges értékek 'on', 'off' és 'relative'. A 'relative' érték használata esetén a kurzor aktuális pozíciójához képest számított sorszám jelenik meg.", + "rulers": "Azon oszlopok listája, ahol függőleges segédvonal jelenjen meg.", + "wordSeparators": "Azon karakterek listája, amelyek szóelválasztónak vannak tekintve szavakkal kapcsolatos navigáció vagy műveletek során.", + "tabSize": "Egy tabulátor hány szóköznek felel meg. Ez a beállítás felülírásra kerül a fájl tartalma alapján, ha az 'editor.detectIndentation' beállítás aktív.", + "tabSize.errorMessage": "A várt érték 'number' típusú. Megjegyzés: az \"auto\" értéket az 'editor.detectIndentation' beállítás helyettesíti.", + "insertSpaces": "Tabulátor billentyű lenyomásánál szóközök legyenek-e beszúrva. Ez a beállítás felülírásra kerül a fájl tartalma alapján, ha az 'editor.detectIndentation' beállítás aktív.", + "insertSpaces.errorMessage": "A várt érték 'boolean' típusú. Megjegyzés: az \"auto\" értéket az 'editor.detectIndentation' beállítás helyettesíti.", + "detectIndentation": "Fájl megnyitásakor az `editor.tabSize` és az `editor.insertSpaces` értéke a fájl tartalma alapján lesz meghatározva.", + "roundedSelection": "Itt adható meg, hogy a kijelölt elemek sarkai lekerekítettek legyenek-e", + "scrollBeyondLastLine": "Meghatározza, hogy a szerkesztőablak görgethető-e az utolsó sor után.", + "smoothScrolling": "Meghatározza, hogy a szerkesztőablak animálva van-e görgetve.", + "minimap.enabled": "Meghatározza, hogy megjelenjen-e a kódtérkép.", + "minimap.showSlider": "Meghatározza, hogy automatikusan el legyen-e rejtve a kódtérképes görgetősáv. Lehetséges értékek: 'always' és 'mouseover'.", + "minimap.renderCharacters": "Meghatározza, hogy a tényleges karakterek legyenek-e megjelenítve (színes téglalapok helyett)", + "minimap.maxColumn": "Meghatározza, hogy a kódtérképen legfeljebb hány oszlop legyen kirajzolva.", + "find.seedSearchStringFromSelection": "Meghatározza, hogy a keresés modulba automatikusan bekerüljön-e a szerkesztőablakban kiválasztott szöveg.", + "find.autoFindInSelection": "Meghatározza, hogy a keresés a kijelölésben beállítás be van-e kapcsolva, ha több karakternyi vagy sornyi szöveg ki van jelölve a szerkesztőablakban.", + "wordWrap.off": "A sorok soha nem lesznek tördelve.", + "wordWrap.on": "A sorok tördelve lesznek a nézetablak szélességénél.", + "wordWrap.wordWrapColumn": "A sorok tördelve lesznek az `editor.wordWrapColumn` oszlopnál.", + "wordWrap.bounded": "A sorok tördelve lesznek a nézetablak szélességének és az `editor.wordWrapColumn` értékének minimumánál.", + "wordWrap": "Ez a beállítás meghatározza, hogy a sorok hogyan legyenek tördelve. Lehetséges értékek:\n- 'off' (nincs sortörés)\n- 'on' (sortörés a nézetablakban)\n- 'wordWrapColumn' (sortörés az `editor.wordWrapColumn` oszlopnál) vagy\n- 'bounded' (sortörés az `editor.wordWrapColumn` és a nézetablak minimumánál)", + "wordWrapColumn": "Meghatározza a sortöréshez használt oszlopszámot a szerkesztőablakban, ha az `editor.wordWrap` értéke 'wordWrapColumn' vagy 'bounded'.", + "wrappingIndent": "Meghatározza a tördelt sorok behúzását. Értéke 'none', 'same' vagy 'indent' lehet.", + "mouseWheelScrollSensitivity": "Az egér görgetési eseményeinél keletkező `deltaX` és `deltaY` paraméterek szorzója", + "multiCursorModifier.ctrlCmd": "Windows és Linux alatt a `Control`, OSX alatt a `Command` billentyűt jelenti.", + "multiCursorModifier.alt": "Windows és Linux alatt az `Alt`, OSX alatt az `Option` billentyűt jelenti.", + "multiCursorModifier": "Több kurzor hozzáadásához használt módosítóbillentyű. A `ctrlCmd` Windows és Linux alatt a `Control`, OSX alatt a `Command` billentyűt jelenti. A Definíció megkeresése és Hivatkozás megnyitása egérgesztusok automatikusan alkalmazkodnak úgy, hogy ne ütközzenek a többkurzorhoz tartozó módosítóval.", + "quickSuggestions.strings": "Kiegészítési javaslatok engedélyezése karakterláncokban (stringekben)", + "quickSuggestions.comments": "Kiegészítési javaslatok engedélyezése megjegyzésekben", + "quickSuggestions.other": "Kiegészítési javaslatok engedélyezése karakterláncokon (stringeken) és megjegyzéseken kívül", + "quickSuggestions": "Meghatározza, hogy automatikusan megjelenjenek-e a javaslatok gépelés közben", + "quickSuggestionsDelay": "Meghatározza, hogy hány ezredmásodperc késleltetéssel jelenjenek meg a kiegészítési javaslatok", + "parameterHints": "Paraméterinformációkat és típusinformációkat tartalmazó felugró ablak engedélyezése gépelés közben", + "autoClosingBrackets": "Meghatározza, hogy a szerkesztő automatikusan beszúrja-e a nyitó zárójelek záró párját", + "formatOnType": "Meghatározza, hogy a szerkesztő automatikusan formázza-e a sort a gépelés után", + "formatOnPaste": "Meghatározza, hogy a szerkesztő automatikusan formázza-e a beillesztett tartalmat. Ehhez szükség van egy formázóra, illetve a formázónak tudnia kell a dokumentum egy részét formázni.", + "autoIndent": "Meghatározza, hogy a szerkesztőablak automatikusan állítsa-e az indentálást miközben a felhasználó gépel, beilleszt vagy mozgatja a sorokat. Az adott nyelv indentálási szabályainak rendelkezésre kell állnia.", + "suggestOnTriggerCharacters": "Itt adható meg, hogy eseményindító karakterek beírásakor automatikusan megjelenjenek-e a javaslatok", + "acceptSuggestionOnEnter": "Meghatározza, hogy a javaslatok az 'Enter' gomb leütésére is el legyenek fogadva a 'Tab' mellett. Segít feloldani a bizonytalanságot az új sorok beillesztése és a javaslatok elfogadása között. A 'smart' érték azt jelenti, hogy csak akkor fogadja el a javaslatot az Enter leütése esetén, ha az módosítja a szöveget.", + "acceptSuggestionOnCommitCharacter": "Meghatározza, hogy a javaslaok a zárókarakterek leütésére is el legyenek fogadva. A JavaScriptben például a pontosvessző (';') számít zárókarakternek, leütésére a javaslat elfogadásra kerül és beillesztődik az adott karakter. ", + "snippetSuggestions.top": "A javasolt kódrészletek a többi javaslat előtt jelenjenek meg.", + "snippetSuggestions.bottom": "A javasolt kódrészletek a többi javaslat után jelenjenek meg.", + "snippetSuggestions.inline": "A javasolt kódrészletek a többi javaslattal együtt jelenjenek meg.", + "snippetSuggestions.none": "Ne jelenjenek meg a javasolt kódrészletek.", + "snippetSuggestions": "Meghatározza, hogy a kódtöredékek megjelenjenek-e a javaslatok között, illetve hogy hogyan legyenek rendezve.", + "emptySelectionClipboard": "Meghatározza, hogy kijelölés nélküli másolás esetén a teljes sor legyen-e másolva.", + "wordBasedSuggestions": "Meghatározza, hogy a kiegészítések listája a dokumentumban lévő szövegek alapján legyen-e meghatározva.", + "suggestFontSize": "Az ajánlásokat tartalmazó modul betűmérete", + "suggestLineHeight": "Az ajánlásokat tartalmazó modul sormagassága", + "selectionHighlight": "Itt adható meg, hogy a szerkesztő kiemelje-e a kijelöléshez hasonló találatokat", + "occurrencesHighlight": "Meghatározza, hogy a szerkesztőablakban ki legyenek-e emelve a szimbólum szemantikailag hozzá tartozó előfordulásai.", + "overviewRulerLanes": "Meghatározza, hogy hány dekoráció jelenhet meg azonos pozícióban az áttekintő sávon.", + "overviewRulerBorder": "Meghatározza, hogy legyen-e kerete az áttekintő sávnak.", + "cursorBlinking": "Meghatározza a kurzor animációjának stílusát. Lehetséges értékek: 'blink', 'smooth', 'phase', 'expand' vagy 'solid'", + "mouseWheelZoom": "A szerkesztőablak betűtípusának nagyítása vagy kicsinyítése az egérgörgő Ctrl lenyomása mellett történő használata esetén", + "cursorStyle": "Meghatározza a kurzor stílusát. Lehetséges értékek: 'block', 'block-outline', 'line', 'line-thin', 'underline' vagy 'underline-thin'", + "fontLigatures": "Engedélyezi a betűtípusban található ligatúrák használatát", + "hideCursorInOverviewRuler": "Meghatározza, hogy a kurzor pozíciója el legyen-e rejtve az áttekintő sávon.", + "renderWhitespace": "Meghatározza, hogy a szerkesztőablakban hogyan legyenek kirajzolva a szóköz karakterek. Lehetséges értékek: 'none', 'boundary', vagy 'all'. A 'boundary' beállítás esetén, ha szavak között egyetlen szóköz található, akkor az nem lesz kirajzolva.", + "renderControlCharacters": "Meghatározza, hogy a szerkesztőablakban ki legyenek-e rajzolva a vezérlőkarakterek.", + "renderIndentGuides": "Meghatározza, hogy a szerkesztőablakban ki legyenek-e rajzolva az indentálási segédvonalak.", + "renderLineHighlight": "Meghatározza, hogy a szerkesztőablakban hogyan legyen kirajzolva az aktuális sor kiemelése. Lehetséges értékek: 'none', 'gutter', 'line', vagy 'all'.", + "codeLens": "Meghatározza, hogy megjelenjenek-e a kódlencsék", + "folding": "Meghatározza, hogy engedélyezve van-e a kódrészletek bezárása a szerkesztőablakban.", + "showFoldingControls": "Meghatározza, hogy a kódrészletek bezárásához tartozó vezérlőelemek automatikusan el legyenek-e rejtve.", + "matchBrackets": "Zárójel kiválasztása esetén a hozzátartozó zárójel kiemelése.", + "glyphMargin": "Meghatározza, hogy legyen-e vertikális szimbólummargó a szerkesztőablakban. A szimbólummargó elsősorban hibakeresésnél van használva.", + "useTabStops": "Szóközök beillesztése és törlése során követve vannak a tabulátorok.", + "trimAutoWhitespace": "A sorok végén lévő, automatikusan beillesztett szóközök eltávolítása", + "stablePeek": "A betekintőablakok maradjanak nyitva akkor is, ha duplán kattintanak a tartalmára vagy megnyomják az Escape gombot.", + "dragAndDrop": "Meghatározza, hogy a szerkesztőablakban engedélyezett-e a kijelölt szövegrészletek áhelyezése húzással.", + "accessibilitySupport.auto": "A szerkesztő a platform által biztosított API-kat használja annak megállapításához, hogy van-e képernyőolvasó csatlakoztatva.", + "accessibilitySupport.on": "A szerkesztő folyamatos képernyőolvasóval való használatára van optimalizálva.", + "accessibilitySupport.off": "A szerkesztő soha nincs képernyőolvasó használatára optimalizálva.", + "accessibilitySupport": "Meghatározza, hogy a szerkesztő olyan módban fusson-e, ami optimalizálva van képernyőolvasóval való használathoz.", + "links": "Meghatározza, hogy a szerkesztőablak érzékelje-e a hivatkozásokat, és kattinthatóvá tegye-e őket.", + "colorDecorators": "Meghatározza, hogy a szerkesztőablakban ki legyenek-e rajzolva a színdekorátorok és színválasztók.", + "sideBySide": "Meghatározza, hogy a differenciaszerkesztő ablakban egymás mellett vagy a sorban jelenjenek meg az eltérések", + "ignoreTrimWhitespace": "Meghatározza, hogy a differenciaszerkesztő ablakban megjelenjenek-e a sor elején vagy végén a szóközökben talált különbségek", + "renderIndicators": "Meghatározza, hogy a differenciaszerkesztő ablakban megjelenjenek-e a +/- jelzők az hozzáadott/eltávolított változásoknál", + "selectionClipboard": "Meghatározza-e, hogy támogatva van-e az elsődleges vágólap Linux alatt" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/hun/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..655238edf1 --- /dev/null +++ b/i18n/hun/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "A szerkesztőablak jelenleg nem elérhető. Nyomja meg az Alt+F1-et a beállítási lehetőségek megjelenítéséhez!", + "editorViewAccessibleLabel": "Szerkesztőablak tartalma" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/common/controller/cursor.i18n.json b/i18n/hun/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..85a08702be --- /dev/null +++ b/i18n/hun/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "Váratlan kivétel egy parancs végrehajtása során." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/hun/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..b762b4a10b --- /dev/null +++ b/i18n/hun/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "Ebben az üzemmódban nem sikerült lexikális elemekre bontani a bemenetet." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/hun/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..69ff94939e --- /dev/null +++ b/i18n/hun/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "Egyszerű szöveg" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/hun/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..8e8dc02a3f --- /dev/null +++ b/i18n/hun/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "A következő fájlok módosultak időközben: {0}", + "summary.0": "Nem történtek változtatások", + "summary.nm": "{0} változtatást végzett {0} fájlban", + "summary.n0": "{0} változtatást végzett egy fájlban" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/hun/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..a72abd0d85 --- /dev/null +++ b/i18n/hun/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Nyelvdeklarációkat szolgáltat.", + "vscode.extension.contributes.languages.id": "A nyelv azonosítója", + "vscode.extension.contributes.languages.aliases": "A nyelv kiegészítő nevei.", + "vscode.extension.contributes.languages.extensions": "A nyelvhez hozzárendelt fájlkiterjesztések.", + "vscode.extension.contributes.languages.filenames": "A nyelvhez hozzárendelt fájlnevek.", + "vscode.extension.contributes.languages.filenamePatterns": "A nyelvhez hozzárendelt globális minták.", + "vscode.extension.contributes.languages.mimetypes": "A nyelvhez hozzárendelt MIME-típusok.", + "vscode.extension.contributes.languages.firstLine": "Reguláris kifejezés, ami az adott nyelven írt fájl első sorára illeszkedik.", + "vscode.extension.contributes.languages.configuration": "A nyelvhez tartozó konfigurációkat tartalmazó fájl relatív elérési útja." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/hun/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/hun/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/hun/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..3cd54b7b2d --- /dev/null +++ b/i18n/hun/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "A kurzor pozícióján található sor kiemelési háttérszíne.", + "lineHighlightBorderBox": "A kurzor pozícióján található sor keretszíne.", + "rangeHighlight": "A kiemelt területek háttérszíne, pl. a gyors megnyitás és keresés funkcióknál.", + "caret": "A szerkesztőablak kurzorának színe.", + "editorCursorBackground": "A szerkesztőablak kurzorának háttérszíne. Lehetővé teszik az olyan karakterek színének módosítását, amelyek fölött egy blokk-típusú kurzor áll.", + "editorWhitespaces": "A szerkesztőablakban található szóköz karakterek színe.", + "editorIndentGuides": "A szerkesztőablak segédvonalainak színe.", + "editorLineNumbers": "A szerkesztőablak sorszámainak színe.", + "editorRuler": "A szerkesztőablak sávjainak színe.", + "editorCodeLensForeground": "A szerkesztőablakban található kódlencsék előtérszíne", + "editorBracketMatchBackground": "Hozzátartozó zárójelek háttérszíne", + "editorBracketMatchBorder": "Az összetartozó zárójelek dobozának színe", + "editorOverviewRulerBorder": "Az áttekintő sáv keretszíne.", + "editorGutter": "A szerkesztőablag margójának háttérszíne. A margón található a szimbólummargó és a sorszámok.", + "errorForeground": "A hibákat jelző hullámvonal előtérszíne a szerkesztőablakban.", + "errorBorder": "A hibákat jelző hullámvonal keretszíne a szerkesztőablakban.", + "warningForeground": "A figyelmeztetéseket jelző hullámvonal előtérszíne a szerkesztőablakban.", + "warningBorder": "A figyelmeztetéseket jelző hullámvonal keretszíne a szerkesztőablakban.", + "overviewRulerRangeHighlight": "A kiemelt tartományokat jelölő jelzések színe az áttekintősávon.", + "overviewRuleError": "A hibákat jelölő jelzések színe az áttekintősávon.", + "overviewRuleWarning": "A figyelmeztetéseket jelölő jelzések színe az áttekintősávon.", + "overviewRuleInfo": "Az információkat jelölő jelzések színe az áttekintősávon." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/hun/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..456d9f2954 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "Ugrás a zárójelre" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/hun/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..ce688ab505 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "Kurzor mozgatása balra", + "caret.moveRight": "Kurzor mozgatása jobbra" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/hun/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..fe934075d0 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "Betűk megcserélése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/hun/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..6b8e5b1999 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "Kivágás", + "actions.clipboard.copyLabel": "Másolás", + "actions.clipboard.pasteLabel": "Beillesztés", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "Másolás szintaktikai kiemeléssel" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/hun/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..047e622a98 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "Egysoros megjegyzés ki-/bekapcsolása", + "comment.line.add": "Egysoros megjegyzés hozzáadása", + "comment.line.remove": "Egysoros megjegyzés eltávolítása", + "comment.block": "Megjegyzésblokk ki-/bekapcsolása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/hun/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..d645443aa1 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "Szerkesztőablak helyi menüjének megjelenítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/hun/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..e0381c4418 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Keresés", + "placeholder.find": "Keresés", + "label.previousMatchButton": "Előző találat", + "label.nextMatchButton": "Következő találat", + "label.toggleSelectionFind": "Keresés kijelölésben", + "label.closeButton": "Bezárás", + "label.replace": "Csere", + "placeholder.replace": "Csere", + "label.replaceButton": "Csere", + "label.replaceAllButton": "Az összes előfordulás cseréje", + "label.toggleReplaceButton": "Váltás csere módra", + "title.matchesCountLimit": "Csak az első 999 találat van kiemelve, de minden keresési művelet a teljes szöveggel dolgozik.", + "label.matchesLocation": "{0} (összesen {1})", + "label.noResults": "Nincs eredmény" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/hun/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..884b3750a4 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Keresés", + "placeholder.find": "Keresés", + "label.previousMatchButton": "Előző találat", + "label.nextMatchButton": "Következő találat", + "label.closeButton": "Bezárás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/hun/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..b4e0293902 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "Keresés", + "findNextMatchAction": "Következő találat", + "findPreviousMatchAction": "Előző találat", + "nextSelectionMatchFindAction": "Következő kijelölés", + "previousSelectionMatchFindAction": "Előző kijelölés", + "startReplace": "Csere", + "addSelectionToNextFindMatch": "Kijelölés hozzáadása a következő keresési találathoz", + "addSelectionToPreviousFindMatch": "Kijelölés hozzáadása az előző keresési találathoz", + "moveSelectionToNextFindMatch": "Utolsó kijelölés áthelyezése a következő keresési találatra", + "moveSelectionToPreviousFindMatch": "Utolsó kijelölés áthelyezése az előző keresési találatra", + "selectAllOccurrencesOfFindMatch": "Az összes keresési találat kijelölése", + "changeAll.label": "Minden előfordulás módosítása", + "showNextFindTermAction": "Következő keresési kifejezés megjelenítése", + "showPreviousFindTermAction": "Előző keresési kifejezés megjelenítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/hun/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..de64ff76a6 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "Kibontás", + "unFoldRecursivelyAction.label": "Kibontás rekurzívan", + "foldAction.label": "Bezárás", + "foldRecursivelyAction.label": "Bezárás rekurzívan", + "foldAllAction.label": "Az összes bezárása", + "unfoldAllAction.label": "Az összes kinyitása", + "foldLevelAction.label": "{0} szintű blokkok bezárása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/hun/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..79dba5bdfc --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "Egy formázást végzett a(z) {0}. sorban", + "hintn1": "{0} formázást végzett a(z) {1}. sorban", + "hint1n": "Egy formázást végzett a(z) {0}. és {1}. sorok között", + "hintnn": "{0} formázást végzett a(z) {1}. és {2}. sorok között", + "formatDocument.label": "Dokumentum formázása", + "formatSelection.label": "Kijelölt tartalom formázása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/hun/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..9d8893b7cc --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "Nem található a(z) '{0}' definíciója", + "generic.noResults": "Definíció nem található", + "meta.title": " – {0} definíció", + "actions.goToDecl.label": "Ugrás a definícióra", + "actions.goToDeclToSide.label": "Definíció megnyitása oldalt", + "actions.previewDecl.label": "Betekintés a definícióba", + "goToImplementation.noResultWord": "Nem található a(z) '{0}' implementációja", + "goToImplementation.generic.noResults": "Implementáció nem található", + "meta.implementations.title": " – {0} implementáció", + "actions.goToImplementation.label": "Ugrás az implementációra", + "actions.peekImplementation.label": "Betekintés az implementációba", + "goToTypeDefinition.noResultWord": "Nem található a(z) '{0}' típusdefiníciója", + "goToTypeDefinition.generic.noResults": "Típusdefiníció nem található", + "meta.typeDefinitions.title": " – {0} típusdefiníció", + "actions.goToTypeDefinition.label": "Ugrás a típusdefinícióra", + "actions.peekTypeDefinition.label": "Betekintés a típusdefinícióba" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/hun/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..cc204ab379 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "Kattintson {0} definíció megjelenítéséhez." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/hun/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..4e96352d83 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "Következő hiba vagy figyelmeztetés", + "markerAction.previous.label": "Előző hiba vagy figyelmeztetés", + "editorMarkerNavigationError": "A szerkesztőablak jelzőnavigációs moduljának színe hiba esetén.", + "editorMarkerNavigationWarning": "A szerkesztőablak jelzőnavigációs moduljának színe figyelmeztetés esetén.", + "editorMarkerNavigationBackground": "A szerkesztőablak jelzőnavigációs moduljának háttérszíne." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/hun/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..b80ef81ec7 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "Súgószöveg megjelenítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/hun/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..0988bd1eee --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "Betöltés..." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/hun/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..244f6755f9 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "Csere az előző értékre", + "InPlaceReplaceAction.next.label": "Csere a következő értékre" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/hun/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..3e799662fd --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "Indentálások átalakítása szóközökké", + "indentationToTabs": "Indentálások átalakítása tabulátorokká", + "configuredTabSize": "Beállított tabulátorméret", + "selectTabWidth": "Tabulátorméret kiválasztása az aktuális fájlhoz", + "indentUsingTabs": "Indentálás tabulátorral", + "indentUsingSpaces": "Indentálás szóközzel", + "detectIndentation": "Indentálás felismerése a tartalom alapján", + "editor.reindentlines": "Sorok újraindentálása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/hun/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..dd76244b2c --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "Sor másolása eggyel feljebb", + "lines.copyDown": "Sor másolása eggyel lejjebb", + "lines.moveUp": "Sor feljebb helyezése", + "lines.moveDown": "Sor lejjebb helyezése", + "lines.sortAscending": "Rendezés növekvő sorrendben", + "lines.sortDescending": "Rendezés csökkenő sorrendben", + "lines.trimTrailingWhitespace": "Sor végén található szóközök levágása", + "lines.delete": "Sor törlése", + "lines.indent": "Sor behúzása", + "lines.outdent": "Sor kihúzása", + "lines.insertBefore": "Sor beszúrása eggyel feljebb", + "lines.insertAfter": "Sor beszúrása eggyel lejjebb", + "lines.deleteAllLeft": "Balra lévő tartalom törlése", + "lines.deleteAllRight": "Jobbra lévő tartalom törlése", + "lines.joinLines": "Sorok egyesítése", + "editor.transpose": "A kurzor körüli karakterek felcserélése", + "editor.transformToUppercase": "Átalakítás nagybetűssé", + "editor.transformToLowercase": "Átalakítás kisbetűssé" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/hun/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..c8294d8f8c --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "Hivatkozott oldal megnyitása Cmd + kattintás paranccsal", + "links.navigate": "Hivatkozott oldal megnyitása Ctrl + kattintás paranccsal", + "links.command.mac": "Cmd + kattintás a parancs végrehajtásához", + "links.command": "Ctrl + kattintás a parancs végrehajtásához", + "links.navigate.al": "Hivatkozás megnyitása Alt + kattintás paranccsal", + "links.command.al": "Alt + kattintás a parancs végrehajtásához", + "invalid.url": "A hivatkozást nem sikerült megnyitni, mert nem jól formázott: {0}", + "missing.url": "A hivatkozást nem sikerült megnyitni, hiányzik a célja.", + "label": "Hivatkozás megnyitása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/hun/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..c29ce833e7 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "Kurzor beszúrása egy sorral feljebb", + "mutlicursor.insertBelow": "Kurzor beszúrása egy sorral lejjebb", + "mutlicursor.insertAtEndOfEachLineSelected": "Kurzor beszúrása a sorok végére" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/hun/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..e89b24803c --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "Paraméterinformációk megjelenítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/hun/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..fb583cb4c2 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0}, információ" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/hun/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..fc234ac279 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "Javítások megjelenítése ({0})", + "quickFix": "Javítások megjelenítése", + "quickfix.trigger.label": "Gyorsjavítás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..7bea40d862 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": " – {0} referencia", + "references.action.label": "Minden hivatkozás megkeresése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..8c724a66e3 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "Betöltés..." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..b3c6acaea2 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "szimbólum a következő helyen: {0}, sor: {1}, oszlop: {2}", + "aria.fileReferences.1": "Egy szimbólum a következő helyen: {0}, teljes elérési út: {1}", + "aria.fileReferences.N": "{0} szimbólum a következő helyen: {1}, teljes elérési út: {2}", + "aria.result.0": "Nincs találat", + "aria.result.1": "Egy szimbólum a következő helyen: {0}", + "aria.result.n1": "{0} szimbólum a következő helyen: {1}", + "aria.result.nm": "{0} szimbólum {1} fájlban" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..dcff265ee0 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "Nem sikerült feloldani a fájlt.", + "referencesCount": "{0} referencia", + "referenceCount": "{0} referencia", + "missingPreviewMessage": "előnézet nem érhető el", + "treeAriaLabel": "Referenciák", + "noResults": "Nincs eredmény", + "peekView.alternateTitle": "Referenciák", + "peekViewTitleBackground": "A betekintőablak címsorának háttérszíne.", + "peekViewTitleForeground": "A betekintőablak címének színe.", + "peekViewTitleInfoForeground": "A betekintőablak címsorában található információ színe.", + "peekViewBorder": "A betekintőablak keretének és nyilainak színe.", + "peekViewResultsBackground": "A betekintőablak eredménylistájának háttérszíne.", + "peekViewResultsMatchForeground": "A betekintőablak eredménylistájában található sorhivatkozások előtérszíne.", + "peekViewResultsFileForeground": "A betekintőablak eredménylistájában található fájlhivatkozások előtérszíne.", + "peekViewResultsSelectionBackground": "A betekintőablak eredménylistájában kiválaszott elem háttérszíne.", + "peekViewResultsSelectionForeground": "A betekintőablak eredménylistájában kiválaszott elem előtérszíne.", + "peekViewEditorBackground": "A betekintőablak szerkesztőablakának háttérszíne.", + "peekViewEditorGutterBackground": "A betekintőablak szerkesztőablakában található margó háttérszíne.", + "peekViewResultsMatchHighlight": "Kiemelt keresési eredmények színe a betekintőablak eredménylistájában.", + "peekViewEditorMatchHighlight": "Kiemelt keresési eredmények színe a betekintőablak szerkesztőablakában." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/hun/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..6059de6fb5 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "Nincs eredmény.", + "aria": "'{0}' sikeresen át lett nevezve a következőre: '{1}'. Összefoglaló: {2}", + "rename.failed": "Az átnevezést nem sikerült végrehajtani.", + "rename.label": "Szimbólum átnevezése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/hun/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..ffa4e54661 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "Átnevezésre szolgáló beviteli mező. Adja meg az új nevet, majd nyomja meg az Enter gombot a változtatások elvégzéséhez." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/hun/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..f82c88edc8 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "Kijelölés bővítése", + "smartSelect.shrink": "Kijelölés szűkítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/hun/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..132adf3e48 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "A(z) '{0}' elfogadása a következő szöveg beszúrását eredményezte: {1}", + "suggest.trigger.label": "Javaslatok megjelenítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/hun/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..5b55d8ff79 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "A javaslatokat tartalmazó modul háttérszíne.", + "editorSuggestWidgetBorder": "A javaslatokat tartalmazó modul keretszíne.", + "editorSuggestWidgetForeground": "A javaslatokat tartalmazó modul előtérszíne.", + "editorSuggestWidgetSelectedBackground": "A javaslatokat tartalmazó modulban kiválasztott elem háttérszíne.", + "editorSuggestWidgetHighlightForeground": "Az illeszkedő szövegrészletek kiemelése a javaslatok modulban.", + "readMore": "További információk megjelenítése...{0}", + "suggestionWithDetailsAriaLabel": "{0}, javaslat, részletekkel", + "suggestionAriaLabel": "{0}, javaslat", + "readLess": "Kevesebb információ megjelenítése...{0}", + "suggestWidget.loading": "Betöltés...", + "suggestWidget.noSuggestions": "Nincsenek javaslatok.", + "suggestionAriaAccepted": "{0}, elfogadva", + "ariaCurrentSuggestionWithDetails": "{0}, javaslat, részletekkel", + "ariaCurrentSuggestion": "{0}, javaslat" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/hun/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..cb41016548 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "Tab billentyűvel mozgatott fókusz ki- és bekapcsolása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/hun/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..fb0ddf258e --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "Szimbólumok háttérszíne olvasási hozzáférés, páldául változó olvasása esetén.", + "wordHighlightStrong": "Szimbólumok háttérszíne írási hozzáférés, páldául változó írása esetén.", + "overviewRulerWordHighlightForeground": "A kiemelt szimbólumokat jelölő jelzések színe az áttekintősávon.", + "overviewRulerWordHighlightStrongForeground": "A kiemelt, írási hozzáférésű szimbólumokat jelölő jelzések színe az áttekintősávon." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/hun/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..8c24387886 --- /dev/null +++ b/i18n/hun/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Bezárás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/hun/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..a24b57115c --- /dev/null +++ b/i18n/hun/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Ismeretlen nyelv található a következőben: `contributes.{0}.language`. A megadott érték: {1}", + "invalid.scopeName": "Hiányzó karakterlánc a `contributes.{0}.scopeName`-ben. A megadott érték: {1}", + "invalid.path.0": "Hiányzó karakterlánc a `contributes.{0}.path`-ban. A megadott érték: {1}", + "invalid.injectTo": "A `contributes.{0}.injectTo` értéke érvénytelen. Az értéke egy tömb lehet, ami nyelvhatókörök neveit tartalmazza. A megadott érték: {1}", + "invalid.embeddedLanguages": "A `contributes.{0}.embeddedLanguages` értéke érvénytelen. Az értéke egy hatókörnév-nyelv kulcs-érték párokat tartalmazó objektum lehet. A megadott érték: {1}", + "invalid.path.1": "A `contributes.{0}.path` ({1}) nem a kiegészítő mappáján belül található ({2}). Emiatt előfordulhat, hogy a kiegészítő nem lesz hordozható.", + "no-tm-grammar": "Nincs TM Grammar regisztrálva ehhez a nyelvhez." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/hun/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..a7160e16bc --- /dev/null +++ b/i18n/hun/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Hiba a(z) {0} feldolgozása közben: {1}", + "schema.openBracket": "A nyitó zárójelet definiáló karakter vagy karaktersorozat", + "schema.closeBracket": "A záró zárójelet definiáló karakter vagy karaktersorozat", + "schema.comments": "Meghatározza a megjegyzésszimbólumokat", + "schema.blockComments": "Meghatározza, hogyan vannak jelölve a megjegyzésblokkok.", + "schema.blockComment.begin": "A megjegyzésblokk kezdetét definiáló karaktersorozat.", + "schema.blockComment.end": "A megjegyzésblokk végét definiáló karaktersorozat.", + "schema.lineComment": "A megjegyzéssor kezdetét definiáló karaktersorozat.", + "schema.brackets": "Meghatározza azokat a zárójelszimbólumokat, amelyek növeik vagy csökkentik az indentálást.", + "schema.autoClosingPairs": "Meghatározza a zárójelpárokat. Ha egy nyitó zárójelet írnak be a szerkesztőbe, a záró párja automatikusan be lesz illesztve.", + "schema.autoClosingPairs.notIn": "Azon hatókörök listája, ahol az automatikus zárójelek automatikus párosítása le van tiltve.", + "schema.surroundingPairs": "Meghatározza azok zárójelpárok listáját, melyek használhatók a kijelölt szöveg körbezárására.", + "schema.wordPattern": "A nyelvben található szavak definíciója.", + "schema.wordPattern.pattern": "A szavak illesztésére használt reguláris kifejezés.", + "schema.wordPattern.flags": "A szavak illesztésére használt reguláris kifejezés beállításai.", + "schema.wordPattern.flags.errorMessage": "Illeszkednie kell a következő mintára: `/^([gimuy]+)$/`." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/hun/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..26a2906b3f --- /dev/null +++ b/i18n/hun/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "TextMate-tokenizálókat szolgáltat.", + "vscode.extension.contributes.grammars.language": "Annak a nyelvnek az azonosítója, amely számára szolgáltatva van ez a szintaxis.", + "vscode.extension.contributes.grammars.scopeName": "A tmLanguage-fájl által használt TextMate-hatókör neve.", + "vscode.extension.contributes.grammars.path": "A tmLanguage-fájl elérési útja. Az elérési út relatív a kiegészítő mappájához képest, és általában './syntaxes/'-zal kezdődik.", + "vscode.extension.contributes.grammars.embeddedLanguages": "Hatókörnév-nyelvazonosító kulcs-érték párokat tartalmazó objektum, ha a nyelvtan tartalmaz beágyazott nyelveket.", + "vscode.extension.contributes.grammars.injectTo": "Azon nyelvi hatókörök nevei, ahová be lesz ágyazva ez a nyelvtan." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/hun/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/hun/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/hun/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..39c43f534f --- /dev/null +++ b/i18n/hun/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "a menüelemeket tömbként kell megadni", + "requirestring": "a(z) `{0}` tulajdonság kötelező és `string` típusúnak kell lennie", + "optstring": "a(z) `{0}` tulajdonság elhagyható vagy `string` típusúnak kell lennie", + "vscode.extension.contributes.menuItem.command": "A végrehajtandó parancs azonosítója. A parancsot a 'commands'-szakaszban kell deklarálni", + "vscode.extension.contributes.menuItem.alt": "Egy alternatív végrehajtandó parancs azonosítója. A parancsot a 'commands'-szakaszban kell deklarálni", + "vscode.extension.contributes.menuItem.when": "A feltételnek igaznak kell lennie az elem megjelenítéséhez", + "vscode.extension.contributes.menuItem.group": "A csoport, amibe a parancs tartozik", + "vscode.extension.contributes.menus": "Menüket szolgáltat a szerkesztőhöz", + "menus.commandPalette": "A parancskatalógus", + "menus.editorTitle": "A szerkesztőablak címsora menüje", + "menus.editorContext": "A szerkesztőablak helyi menüje", + "menus.explorerContext": "A fájlkezelő helyi menüje", + "menus.editorTabContext": "A szerkesztőablak füleinek helyi menüje", + "menus.debugCallstackContext": "A hibakeresési hívási verem helyi menüje", + "menus.scmTitle": "A verziókezelő címsora menüje", + "menus.resourceGroupContext": "A verziókezelő erőforráscsoportja helyi menüje", + "menus.resourceStateContext": "A verziókzeleő erőforrásállapot helyi menüje", + "view.viewTitle": "A szolgáltatott nézet címsorának menüje", + "view.itemContext": "A szolgáltatott nézet elemének helyi menüje", + "nonempty": "az érték nem lehet üres.", + "opticon": "a(z) `icon` tulajdonság elhagyható vagy ha van értéke, akkor string vagy literál (pl. `{dark, light}`) típusúnak kell lennie", + "requireStringOrObject": "a(z) `{0}` tulajdonság kötelező és `string` vagy `object` típusúnak kell lennie", + "requirestrings": "a(z) `{0}` és `{1}` tulajdonságok kötelezők és `string` típusúnak kell lenniük", + "vscode.extension.contributes.commandType.command": "A végrehajtandó parancs azonosítója", + "vscode.extension.contributes.commandType.title": "A cím, amivel a parancs meg fog jelenni a felhasználói felületen", + "vscode.extension.contributes.commandType.category": "(Nem kötelező) Kategória neve, amibe a felületen csoportosítva lesz a parancs", + "vscode.extension.contributes.commandType.icon": "(Nem kötelező) Ikon, ami reprezentálni fogja a parancsot a felhasználói felületen. Egy fájl elérési útja vagy egy színtéma-konfiguráció", + "vscode.extension.contributes.commandType.icon.light": "Az ikon elérési útja, ha világos téma van használatban", + "vscode.extension.contributes.commandType.icon.dark": "Az ikon elérési útja, ha sötét téma van használatban", + "vscode.extension.contributes.commands": "Parancsokat szolgáltat a parancskatalógushoz.", + "dup": "A(z) `{0}` parancs többször szerepel a `commands`-szakaszban.", + "menuId.invalid": "A(z) `{0}` nem érvényes menüazonosító", + "missing.command": "A menüpont a(z) `{0}` parancsra hivatkozik, ami nincs deklarálva a 'commands'-szakaszban.", + "missing.altCommand": "A menüpont a(z) `{0}` alternatív parancsra hivatkozik, ami nincs deklarálva a 'commands'-szakaszban.", + "dupe.command": "A menüpont ugyanazt a parancsot hivatkozza alapértelmezett és alternatív parancsként", + "nosupport.altCommand": "Jelenleg csak az 'editor/title' menü 'navigation' csoportja támogatja az alternatív parancsokat" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/hun/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..34f78afadd --- /dev/null +++ b/i18n/hun/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "Felülírt alapértelmezett konfigurációk", + "overrideSettings.description": "A szerkesztő beállításainak felülírása a(z) {0} nyelvre vonatkozóan", + "overrideSettings.defaultDescription": "A szerkesztő beállításainak felülírása egy adott nyelvre vonatkozóan", + "config.property.languageDefault": "A(z) '{0}' nem regisztrálható. Ez a beállítás illeszkedik a '\\\\[.*\\\\]$' mintára, ami a nyelvspecifikus szerkesztőbeállításokhoz van használva. Használja a 'configurationDefaults' szolgáltatási lehetőséget.", + "config.property.duplicate": "A(z) '{0}' nem regisztrálható: ez a tulajdonság már regisztrálva van." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/environment/node/argv.i18n.json b/i18n/hun/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..24ba01bd36 --- /dev/null +++ b/i18n/hun/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "`--goto` mód esetén az argumentumokat a következő formában kell megadni: `FÁJL(:SOR(:OSZLOP))`.", + "diff": "Két fájl összehasonlítása egymással.", + "add": "Mappá(k) hozzáadása a legutolsó aktív ablakhoz.", + "goto": "Megnyitja a megadott elérési úton található fájlt a megadott sornál és oszlopnál.", + "locale": "A használt lokalizáció (pl. en-US vagy zh-TW)", + "newWindow": "Mindenképp induljon új példány a Code-ból.", + "performance": "Indítás a 'Developer: Startup Performance' parancs engedélyezésével.", + "prof-startup": "Processzorhasználat profilozása induláskor", + "reuseWindow": "Fájl vagy mappa megnyitása a legutoljára aktív ablakban.", + "userDataDir": "Meghatározza a könyvtárat, ahol a felhasználói adatok vannak tárolva. Hasznás, ha rootként van futtatva.", + "verbose": "Részletes kimenet kiírása (magába foglalja a --wait kapcsolót)", + "wait": "Várjon az ablak bezárására a visszatérés előtt.", + "extensionHomePath": "A kiegészítők gyökérkönyvtárának beállítása.", + "listExtensions": "Telepített kiegészítők listázása.", + "showVersions": "Telepített kiegészítők verziójának megjelenítése a --list-extension kapcsoló használata esetén.", + "installExtension": "Kiegészítő telepítése.", + "uninstallExtension": "Kiegészítő eltávolítása.", + "experimentalApis": "Tervezett API-funkciók engedélyezése egy kiegészítő számára.", + "disableExtensions": "Összes telepített kiegészítő letiltása.", + "disableGPU": "Hardveres gyorsítás letiltása.", + "version": "Verzió kiírása.", + "help": "Használati útmutató kiírása.", + "usage": "Használat", + "options": "beállítások", + "paths": "elérési utak", + "optionsUpperCase": "Beálítások" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/hun/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..d36f8f5b5b --- /dev/null +++ b/i18n/hun/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Nincs munkaterület." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/hun/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..c5b7af29a9 --- /dev/null +++ b/i18n/hun/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Kiegészítők", + "preferences": "Beállítások" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/hun/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..98ad07c5cb --- /dev/null +++ b/i18n/hun/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Kiegészítő nem található", + "noCompatible": "A(z) {0} kiegészítőnek nincs ezzel a Code-verzióval kompatibilis változata." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/hun/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..003c8cda4b --- /dev/null +++ b/i18n/hun/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "A kiegészítő érvénytelen: a package.json nem egy JSON-fájl.", + "restartCode": "Indítsa újra a Code-ot a(z) {0} újratelepítése előtt.", + "installDependeciesConfirmation": "A(z) '{0}' teleítése során annak függőségei is telepítve lesznek. Szeretné folytatni?", + "install": "Igen", + "doNotInstall": "Nem", + "uninstallDependeciesConfirmation": "Csak a(z) '{0}' kiegészítőt szeretné eltávolítani vagy annak függőségeit is?", + "uninstallOnly": "Csak ezt", + "uninstallAll": "Mindent", + "cancel": "Mégse", + "uninstallConfirmation": "Biztosan szeretné eltávolítani a(z) '{0}' kiegészítőt?", + "ok": "OK", + "singleDependentError": "Nem sikerült eltávolítani a(z) '{0}' kiegészítőt: a(z) '{1}' kiegészítő függ tőle.", + "twoDependentsError": "Nem sikerült eltávolítani a(z) '{0}' kiegészítőt: a(z) '{1}' és '{2}' kiegészítők függnek tőle.", + "multipleDependentsError": "Nem sikerült eltávolítani a(z) '{0}' kiegészítőt: a(z) '{1}', '{2}' és más kiegészítők függnek tőle.", + "notExists": "Nem sikerült megtalálni a kiegészítőt" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/hun/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..8ac621e3d7 --- /dev/null +++ b/i18n/hun/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "A(z) `{1}` kiegészítőt nem sikerült aktiválni. Oka: ismeretlen függőség: `{0}`.", + "failedDep1": "A(z) `{1}` kiegészítőt nem sikerült aktiválni. Oka: a(z) `{0}` függőséget nem sikerült aktiválni.", + "failedDep2": "A(z) `{0}` kiegészítőt nem sikerült aktiválni. Oka: több, mint 10 szintnyi függőség van (nagy valószínűséggel egy függőségi hurok miatt).", + "activationError": "A(z) `{0}` kiegészítő aktiválása nem sikerült: {1}." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/hun/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..223bc3ffa8 --- /dev/null +++ b/i18n/hun/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "VS Code kiegészítőkhöz. Meghatározza azt a VS Code-verziót, amivel a kiegészítő kompatibilis. Nem lehet *. Például a ^0.10.5 a VS Code minimum 0.10.5-ös verziójával való kompatibilitást jelzi.", + "vscode.extension.publisher": "A VS Code-kiegészítő kiadója.", + "vscode.extension.displayName": "A kiegészítő VS Code galériában megjelenített neve.", + "vscode.extension.categories": "A VS Code-galériában való kategorizálásra használt kategóriák.", + "vscode.extension.galleryBanner": "A VS Code piactéren használt szalagcím.", + "vscode.extension.galleryBanner.color": "A VS Code piactéren használt szalagcím színe.", + "vscode.extension.galleryBanner.theme": "A szalagcímben használt betűtípus színsémája.", + "vscode.extension.contributes": "A csomagban található összes szolgáltatás, amit ez a VS Code kiterjesztés tartalmaz.", + "vscode.extension.preview": "A kiegészítő előnézetesnek jelölése a piactéren.", + "vscode.extension.activationEvents": "A VS Code kiegészítő aktiválási eseményei.", + "vscode.extension.activationEvents.onLanguage": "Aktiváló esemény, ami akkor fut le, ha az adott nyelvhez társított fájl kerül megnyitásra.", + "vscode.extension.activationEvents.onCommand": "Aktiváló esemény, ami akkor fut le, amikor a megadott parancsot meghívják.", + "vscode.extension.activationEvents.onDebug": "Aktiváló esemény, ami akkor fut le, amikor elindul az adott típusú hibakeresési folyamat.", + "vscode.extension.activationEvents.workspaceContains": "Aktiváló esemény, ami akkor fut le, ha egy olyan mappa kerül megnyitásra, amiben legalább egy olyan fájl van, amely illeszkedik a megadott globális mintára.", + "vscode.extension.activationEvents.onView": "Aktiváló esemény, ami akkor fut le, amikor a megadott nézetet kiterjesztik.", + "vscode.extension.activationEvents.star": "Aktiváló esemény, ami a VS Code indításakor fut le. A jó felhasználói élmény érdekében csak akkor használja ezt az eseményt, ha más aktiváló események nem alkalmasak az adott kiegészítő esetében.", + "vscode.extension.badges": "A kiegészítő piactéren található oldalának oldalsávjában megjelenő jelvények listája.", + "vscode.extension.badges.url": "A jelvény kép URL-je.", + "vscode.extension.badges.href": "A jelvény hivatkozása.", + "vscode.extension.badges.description": "A jelvény leírása.", + "vscode.extension.extensionDependencies": "Más kiegészítők, melyek függőségei ennek a kiegészítőnek. A kiegészítők azonosítója mindig ${publisher}.${name} formájú. Például: vscode.csharp.", + "vscode.extension.scripts.prepublish": "A VS Code kiegészítő publikálása előtt végrehajtott parancsfájl.", + "vscode.extension.icon": "Egy 128x128 pixeles ikon elérési útja." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/hun/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..65ae455609 --- /dev/null +++ b/i18n/hun/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "Nem sikerült feldolgozni az `engines.vscode` beállítás értékét ({0}). Használja például a következők egyikét: ^0.10.0, ^1.2.3, ^0.11.0, ^0.10.x stb.", + "versionSpecificity1": "Az `engines.vscode` beállításban megadott érték ({0}) nem elég konkrét. A vscode 1.0.0 előtti verzióihoz legalább a kívánt fő- és alverziót is meg kell adni. Pl.: ^0.10.0, 0.10.x, 0.11.0 stb.", + "versionSpecificity2": "Az `engines.vscode` beállításban megadott érték ({0}) nem elég konkrét. A vscode 1.0.0 utáni verzióihoz legalább a kívánt főverziót meg kell adni. Pl.: ^1.10.0, 1.10.x, 1.x.x, 2.x.x stb.", + "versionMismatch": "A kiegészítő nem kompatibilis a Code {0} verziójával. A következő szükséges hozzá: {1}.", + "extensionDescription.empty": "A kiegészítő leírása üres", + "extensionDescription.publisher": "a(z) `{0}` tulajdonság kötelező és `string` típusúnak kell lennie", + "extensionDescription.name": "a(z) `{0}` tulajdonság kötelező és `string` típusúnak kell lennie", + "extensionDescription.version": "a(z) `{0}` tulajdonság kötelező és `string` típusúnak kell lennie", + "extensionDescription.engines": "a(z) `{0}` tulajdonság kötelező és `object` típusúnak kell lennie", + "extensionDescription.engines.vscode": "a(z) `{0}` tulajdonság kötelező és `string` típusúnak kell lennie", + "extensionDescription.extensionDependencies": "a(z) `{0}` tulajdonság elhagyható vagy `string[]` típusúnak kell lennie", + "extensionDescription.activationEvents1": "a(z) `{0}` tulajdonság elhagyható vagy `string[]` típusúnak kell lennie", + "extensionDescription.activationEvents2": "a(z) `{0}` és `{1}` megadása kötelező vagy mindkettőt el kell hagyni", + "extensionDescription.main1": "a(z) `{0}` tulajdonság elhagyható vagy `string` típusúnak kell lennie", + "extensionDescription.main2": "A `main` ({0}) nem a kiegészítő mappáján belül található ({1}). Emiatt előfordulhat, hogy a kiegészítő nem lesz hordozható.", + "extensionDescription.main3": "a(z) `{0}` és `{1}` megadása kötelező vagy mindkettőt el kell hagyni", + "notSemver": "A kiegészítő verziója nem semver-kompatibilis." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/hun/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..43c4462f8b --- /dev/null +++ b/i18n/hun/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "Új ablak", + "newWindowDesc": "Nyit egy új ablakot", + "recentFolders": "Legutóbbi munkaterületek", + "folderDesc": "{0} {1}", + "codeWorkspace": "Code-munkaterület" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/hun/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..f48525e834 --- /dev/null +++ b/i18n/hun/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "OK", + "integrity.dontShowAgain": "Ne jelenítse meg újra", + "integrity.moreInfo": "További információ", + "integrity.prompt": "A feltelepített {0} hibásnak tűnik. Telepítse újra!" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/hun/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..29a75e421b --- /dev/null +++ b/i18n/hun/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "JSON-sémakonfigurációkat szolgáltat.", + "contributes.jsonValidation.fileMatch": "Az illesztendő fájlok mintája, például \"package.json\" vagy \"*.launch\".", + "contributes.jsonValidation.url": "A séma URL-címe ('http:', 'https:') vagy relatív elérési útja a kiegészítő mappájához képest ('./').", + "invalid.jsonValidation": "a 'configuration.jsonValidation' értékét tömbként kell megadni", + "invalid.fileMatch": "a 'configuration.jsonValidation.fileMatch' tulajdonság kötelező", + "invalid.url": "a 'configuration.jsonValidation.url' értéke URL-cím vagy relatív elérési út lehet", + "invalid.url.fileschema": "a 'configuration.jsonValidation.url' érvénytelen relatív elérési utat tartalmaz: {0}", + "invalid.url.schema": "a 'configuration.jsonValidation.url' érténének 'http:'-tal, 'https:'-tal, vagy a kiegészítőben elhelyezett sémák hivatkozása esetén './'-rel kell kezdődnie." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/hun/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..7969f5fce5 --- /dev/null +++ b/i18n/hun/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "Lenyomta a következőt: ({0}). Várakozás a kombináció második billentyűjére...", + "missing.chord": "A(z) ({0}, {1}) billentyűkombináció nem egy parancs." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/hun/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..f7c46432e1 --- /dev/null +++ b/i18n/hun/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "A loop tulajdonság csak az utolsó, sorra illesztő kifejezésnél támogatott.", + "ProblemPatternParser.problemPattern.missingRegExp": "A problémamintából hiányzik egy reguláris kifejezés.", + "ProblemPatternParser.problemPattern.missingProperty": "A probléma mintája érvénytelen. Mindenképp tartalmaznia kell egy fájlra, egy üzenetre és egy sorra vagy helyre illesztő csoportot.", + "ProblemPatternParser.invalidRegexp": "Hiba: A(z) {0} karakterlánc nem érvényes reguláris kifejezés.\n", + "ProblemPatternSchema.regexp": "A kimenetben található hibák, figyelmeztetések és információk megkeresésére használt reguláris kifejezés.", + "ProblemPatternSchema.file": "Annak az illesztési csoportnak az indexe, amely tartalmazza azt, hogy a probléma melyik fájlban található. Ha nincs megadva, akkor az alapértelmezett érték, 1 van használva.", + "ProblemPatternSchema.location": "Annak az illesztési csoportnak az indexe, amely tartalmazza a probléma helyét. Az érvényes minták helyek illesztésére: (line), (line,column) és (startLine,startColumn,endLine,endColumn). Ha nincs megadva, akkor a (line,column) van feltételezve.", + "ProblemPatternSchema.line": "Annak az illesztési csoportnak az indexe, amely tartalmazza azt, hogy a probléma hanyadik sorban található. Alapértelmezett értéke 2.", + "ProblemPatternSchema.column": "Annak az illesztési csoportnak az indexe, amely tartalmazza azt, hogy a probléma az adott soron belül mely oszlopban található. Alapértelmezett értéke 3.", + "ProblemPatternSchema.endLine": "Annak az illesztési csoportnak az indexe, amely tartalmazza azt, hogy a probléma mely sorban ér véget. Alapértelmezett értéke határozatlan.", + "ProblemPatternSchema.endColumn": "Annak az illesztési csoportnak az indexe, amely tartalmazza azt, hogy a probléma vége a zárósoron belül mely oszlopban található. Alapértelmezett értéke határozatlan.", + "ProblemPatternSchema.severity": "Annak az illesztési csoportnak az indexe, amely tartalmazza a probléma súlyosságát. Alapértelmezett értéke határozatlan.", + "ProblemPatternSchema.code": "Annak az illesztési csoportnak az indexe, amely tartalmazza a problémás kódrészletet. Alapértelmezett értéke határozatlan.", + "ProblemPatternSchema.message": "Annak az illesztési csoportnak az indexe, amely tartalmazza az üzenetet. Ha nincs megadva, és a location paraméternek van értéke, akkor a 4, minden más esetben 5 az alapértelmezett érték.", + "ProblemPatternSchema.loop": "Több soros illesztés esetén meghatározza, hogy az aktuális minta mindaddig végre legyen-e hajtva, amíg eredményt talál. Csak többsoros minta esetén használható, utolsóként.", + "NamedProblemPatternSchema.name": "A problémaminta neve.", + "NamedMultiLineProblemPatternSchema.name": "A többsoros problémaminta neve.", + "NamedMultiLineProblemPatternSchema.patterns": "A konkrét minkák.", + "ProblemPatternExtPoint": "Problémamintákat szolgáltat.", + "ProblemPatternRegistry.error": "Érvénytelen problémaminta. A minta figyelmen kívül lesz hagyva.", + "ProblemMatcherParser.noProblemMatcher": "Hiba: a leírást nem sikerült problémaillesztővé alakítani:\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "Hiba: a leírás nem definiál érvényes problémamintát:\n{0}\n", + "ProblemMatcherParser.noOwner": "Hiba: a leírás nem határoz meg tulajdonost:\n{0}\n", + "ProblemMatcherParser.noFileLocation": "Hiba: a leírás nem határoz meg fájlhelyszínt:\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "Információ: ismeretlen súlyosság: {0}. Az érvényes értékek: error, warning és info.\n", + "ProblemMatcherParser.noDefinedPatter": "Hiba: nem létezik {0} azonosítóval rendelkező minta.", + "ProblemMatcherParser.noIdentifier": "Hiba: a minta tulajdonság egy üres azonosítóra hivatkozik.", + "ProblemMatcherParser.noValidIdentifier": "Hiba: a minta {0} tulajdonsága nem érvényes mintaváltozónév.", + "ProblemMatcherParser.problemPattern.watchingMatcher": "A problémaillesztőnek definiálnia kell a kezdőmintát és a zárómintát is a figyeléshez.", + "ProblemMatcherParser.invalidRegexp": "Hiba: A(z) {0} karakterlánc nem érvényes reguláris kifejezés.\n", + "WatchingPatternSchema.regexp": "Reguláris kifejezés a háttérben futó feladat indulásának vagy befejeződésének detektálására.", + "WatchingPatternSchema.file": "Annak az illesztési csoportnak az indexe, amely tartalmazza azt, hogy a probléma melyik fájlban található. Elhagyható.", + "PatternTypeSchema.name": "Egy szolgáltatott vagy elődefiniált minta neve", + "PatternTypeSchema.description": "Egy problémaminta vagy egy szolgáltatott vagy elődefiniált problémaminta neve. Elhagyható, ha az alapként használandó minta meg van adva.", + "ProblemMatcherSchema.base": "A alapként használni kívánt problémaillesztő neve.", + "ProblemMatcherSchema.owner": "A probléma tulajdonosa a Code-on belül. Elhagyható, ha az alapként használt minta meg van adva. Alapértelmezett értéke 'external', ha nem létezik és az alapként használt minta nincs meghatározva.", + "ProblemMatcherSchema.severity": "Az elkapott problémák alapértelmezett súlyossága. Ez az érték van használva, ha a minta nem definiál illesztési csoportot a súlyossághoz.", + "ProblemMatcherSchema.applyTo": "Meghatározza, hogy a szöveges dokumentumhoz jelentett probléma megnyitott, bezárt vagy minden dokumentumra legyen alkalmazva.", + "ProblemMatcherSchema.fileLocation": "Meghatározza, hogy a problémamintában talált fájlnevek hogyan legyenek értelmezve.", + "ProblemMatcherSchema.background": "Minták, melyekkel követhető egy háttérben futó feladaton aktív illesztő indulása és befejeződése.", + "ProblemMatcherSchema.background.activeOnStart": "Ha értéke igaz, akkor a háttérfeladat aktív módban van, amikor a feladat indul. Ez egyenlő egy olyan sor kimenetre történő kiírásával, ami illeszkedik a beginPatternre.", + "ProblemMatcherSchema.background.beginsPattern": "Ha illeszkedik a kimenetre, akkor a háttérben futó feladat elindulása lesz jelezve.", + "ProblemMatcherSchema.background.endsPattern": "Ha illeszkedik a kimenetre, akkor a háttérben futó feladat befejeződése lesz jelezve.", + "ProblemMatcherSchema.watching.deprecated": "A watching tulajdonság elavult. Használja a backgroundot helyette.", + "ProblemMatcherSchema.watching": "Minták, melyekkel következő a figyelő illesztők indulása és befejeződése.", + "ProblemMatcherSchema.watching.activeOnStart": "Ha értéke igaz, akkor a figyelő aktív módban van, amikor a feladat indul. Ez egyenlő egy olyan sor kimenetre történő kiírásával, ami illeszkedik a beginPatternre.", + "ProblemMatcherSchema.watching.beginsPattern": "Ha illeszkedik a kimenetre, akkor a figyelő feladat elindulása lesz jelezve.", + "ProblemMatcherSchema.watching.endsPattern": "Ha illeszkedik a kimenetre, akkor a figyelő feladat befejeződése lesz jelezve.", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "Ez a tulajdonság elavult. Használja a watching tulajdonságot helyette.", + "LegacyProblemMatcherSchema.watchedBegin": "Reguláris kifejezés, mely jelzi, hogy a figyeltő feladatok fájlmódosítás miatt éppen műveletet hajtanak végre.", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "Ez a tulajdonság elavult. Használja a watching tulajdonságot helyette.", + "LegacyProblemMatcherSchema.watchedEnd": "Reguláros kifejezés, ami jelzi, hogy a figyelő feladat befejezte a végrehajtást.", + "NamedProblemMatcherSchema.name": "A problémaillesztő neve, amivel hivatkozni lehet rá.", + "NamedProblemMatcherSchema.label": "A problémaillesztő leírása emberek számára.", + "ProblemMatcherExtPoint": "Problémaillesztőket szolgáltat.", + "msCompile": "Microsoft fordítói problémák", + "lessCompile": "Less-problémák", + "gulp-tsc": "Gulp TSC-problémák", + "jshint": "JSHint-problémák", + "jshint-stylish": "JSHint stylish-problémák", + "eslint-compact": "ESLint compact-problémák", + "eslint-stylish": "ESLint stylish-problémák", + "go": "Go-problémák" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/message/common/message.i18n.json b/i18n/hun/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..8baa97abf5 --- /dev/null +++ b/i18n/hun/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Bezárás", + "later": "Később", + "cancel": "Mégse" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/request/node/request.i18n.json b/i18n/hun/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..629921c30d --- /dev/null +++ b/i18n/hun/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "A használni kívánt proxybeállítás. Ha nincs beállítva, a http_proxy és a https_proxy környezeti változókból lesz átvéve", + "strictSSL": "A proxyszerver tanúsítványa hitelesítve legyen-e a megadott hitelesítésszolgáltatóknál.", + "proxyAuthorization": "Minden hálózati kérés 'Proxy-Authorization' fejlécében küldendő érték." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/hun/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..0a11e79244 --- /dev/null +++ b/i18n/hun/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetria", + "telemetry.enableTelemetry": "Használati adatok és hibák küldésének engedélyezése a Microsoft felé." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/hun/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..20cde17649 --- /dev/null +++ b/i18n/hun/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "Kiegészítők által definiált, témázható színeket szolgáltat.", + "contributes.color.id": "A témázható szín azonosítója.", + "contributes.color.id.format": "Az azonosítókat az aa[.bb]* formában kell megadni.", + "contributes.color.description": "A témázható szín leírása.", + "contributes.defaults.light": "Az alapértelmezett szín világos témák esetén. Vagy egy szín hex formátumban (#RRGGBB[AA]) vagy egy témázható szín azonosítója, ami meghatározza az alapértelmezett értéket.", + "contributes.defaults.dark": "Az alapértelmezett szín sötét témák esetén. Vagy egy szín hex formátumban (#RRGGBB[AA]) vagy egy témázható szín azonosítója, ami meghatározza az alapértelmezett értéket.", + "contributes.defaults.highContrast": "Az alapértelmezett szín nagy kontrasztú témák esetén. Vagy egy szín hex formátumban (#RRGGBB[AA]) vagy egy témázható szín azonosítója, ami meghatározza az alapértelmezett értéket.", + "invalid.colorConfiguration": "a 'configuration.colors' értékét tömbként kell megadni", + "invalid.default.colorType": "A(z) {0} értéke egy szín hex formátumban (#RRGGBB[AA]) vagy egy témázható szín azonosítója, ami meghatározza az alapértelmezett értéket.", + "invalid.id": "A 'configuration.colors.id' értékét meg kell adni, és nem lehet üres", + "invalid.id.format": "A 'configuration.colors.id' értékét a word[.word]* formátumban kell megadni.", + "invalid.description": "A 'configuration.colors.description' értékét meg kell adni, és nem lehet üres", + "invalid.defaults": "A 'configuration.colors.defaults' értékét meg kell adni, és tartalmaznia kell 'light', 'dark' és 'highContrast' tulajdonságokat" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/hun/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..025212c0b7 --- /dev/null +++ b/i18n/hun/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "Érvénytelen színformátum. Az #RGB, #RGBA, #RRGGBB vagy #RRGGBBAA formátum használható.", + "schema.colors": "A munkaterületen használt színek.", + "foreground": "Általános előtérszín. Csak akkor van használva, ha nem írja felül az adott komponens.", + "errorForeground": "A hibaüzenetek általános előtérszíne. Csak akkor van használva, ha nem írja felül az adott komponens.", + "descriptionForeground": "A további információkat szolgáltató leíró szövegek, pl. a címkék előtérszíne.", + "focusBorder": "Fókuszált elemek keretének általános színe. Csak akkor van használva, ha nem írja felül az adott komponens.", + "contrastBorder": "Az elemek körüli extra keret, mely arra szolgál, hogy elválassza egymástól őket, így növelve a kontrasztot.", + "activeContrastBorder": "Az aktív elemek körüli extra keret, mely arra szolgál, hogy elválassza egymástól őket, így növelve a kontrasztot.", + "selectionBackground": "A munkaterületen kijelölt szövegek háttérszíne (pl. beviteli mezők vagy szövegmezők esetén). Ez a beállítás nem vonatkozik a szerkesztőablakban végzett kijelölésekre. ", + "textSeparatorForeground": "A szövegelválasztók színe.", + "textLinkForeground": "A szövegben található hivatkozások előtérszíne.", + "textLinkActiveForeground": "A szövegben található aktív hivatkozások előtérszíne.", + "textPreformatForeground": "Az előformázott szövegrészek előtérszíne.", + "textBlockQuoteBackground": "A szövegben található idézetblokkok háttérszíne.", + "textBlockQuoteBorder": "A szövegben található idézetblokkok keretszíne.", + "textCodeBlockBackground": "A szövegben található kódblokkok háttérszíne.", + "widgetShadow": "A szerkesztőablakon belül található modulok, pl. a keresés/csere árnyékának színe.", + "inputBoxBackground": "A beviteli mezők háttérszíne.", + "inputBoxForeground": "A beviteli mezők előtérszíne.", + "inputBoxBorder": "A beviteli mezők kerete.", + "inputBoxActiveOptionBorder": "A beviteli mezőben található aktivált beállítások keretszíne.", + "inputPlaceholderForeground": "A beviteli mezőkben használt helykitöltő szövegek előtérszíne.", + "inputValidationInfoBackground": "Beviteli mezők háttérszíne információs szintű validációs állapot esetén.", + "inputValidationInfoBorder": "Beviteli mezők keretszíne információs szintű validációs állapot esetén.", + "inputValidationWarningBackground": "Beviteli mezők háttérszíne figyelmeztetés szintű validációs állapot esetén.", + "inputValidationWarningBorder": "Beviteli mezők keretszíne figyelmeztetés szintű validációs állapot esetén.", + "inputValidationErrorBackground": "Beviteli mezők háttérszíne hiba szintű validációs állapot esetén.", + "inputValidationErrorBorder": "Beviteli mezők keretszíne hiba szintű validációs állapot esetén.", + "dropdownBackground": "A legördülő menük háttérszíne.", + "dropdownForeground": "A legördülő menük előtérszíne.", + "dropdownBorder": "A legördülő menük kerete.", + "listFocusBackground": "Listák/fák fókuszált elemének háttérszine, amikor a lista aktív. Egy aktív listának/fának van billentyűfőkusza, míg egy inaktívnak nincs.", + "listFocusForeground": "Listák/fák fókuszált elemének előtérszíne, amikor a lista aktív. Egy aktív listának/fának van billentyűfőkusza, míg egy inaktívnak nincs.", + "listActiveSelectionBackground": "Listák/fák kiválasztott elemének háttérszíne, amikor a lista aktív. Egy aktív listának/fának van billentyűfőkusza, míg egy inaktívnak nincs.", + "listActiveSelectionForeground": "Listák/fák kiválasztott elemének előtérszíne, amikor a lista aktív. Egy aktív listának/fának van billentyűfőkusza, míg egy inaktívnak nincs.", + "listInactiveSelectionBackground": "Listák/fák kiválasztott elemének háttérszíne, amikor a lista inaktív. Egy aktív listának/fának van billentyűfőkusza, míg egy inaktívnak nincs.", + "listInactiveSelectionForeground": "Listák/fák kiválasztott elemének előtérszíne, amikor a lista inaktív. Egy aktív listának/fának van billentyűfőkusza, míg egy inaktívnak nincs.", + "listHoverBackground": "A lista/fa háttérszíne, amikor az egérkurzor egy adott elem fölé kerül.", + "listHoverForeground": "A lista/fa előtérszíne, amikor az egérkurzor egy adott elem fölé kerül.", + "listDropBackground": "A lista/fa háttérszíne, amikor az elemek az egérkurzorral vannak mozgatva egyik helyről a másikra.", + "highlight": "Kiemelt találatok előtérszíne a listában/fában való keresés esetén.", + "pickerGroupForeground": "Csoportcímkék színe a gyorsválasztóban.", + "pickerGroupBorder": "Csoportok keretszíne a gyorsválasztóban.", + "buttonForeground": "A gombok előtérszíne.", + "buttonBackground": "A gombok háttérszíne.", + "buttonHoverBackground": "A gomb háttérszine, ha az egérkurzor fölötte van.", + "badgeBackground": "A jelvények háttérszíne. A jelvények apró információs címkék, pl. a keresési eredmények számának jelzésére.", + "badgeForeground": "A jelvények előtérszíne. A jelvények apró információs címkék, pl. a keresési eredmények számának jelzésére.", + "scrollbarShadow": "A görgetősáv árnyéka, ami jelzi, hogy a nézet el van görgetve.", + "scrollbarSliderBackground": "A görgetősáv csúszkájának háttérszíne.", + "scrollbarSliderHoverBackground": "A görgetősáv csúszkájának háttérszíne, ha az egérkurzor fölötte van.", + "scrollbarSliderActiveBackground": "A görgetősáv csúszkájának háttérszíne, ha aktív.", + "progressBarBackground": "A hosszú ideig tartó folyamatok esetén megjelenített folyamatjelző háttérszíne.", + "editorBackground": "A szerkesztőablak háttérszíne.", + "editorForeground": "A szerkesztőablak alapértelmezett előtérszíne.", + "editorWidgetBackground": "A szerkesztőablak moduljainak háttérszíne, pl. a keresés/cserének.", + "editorWidgetBorder": "A szerkesztőablak-modulok keretszíne. A szín csak akkor van használva, ha a modul beállítása alapján rendelkezik kerettel, és a színt nem írja felül a modul.", + "editorSelectionBackground": "A szerkesztőablak-szakasz színe.", + "editorSelectionForeground": "A kijelölt szöveg színe nagy kontrasztú téma esetén.", + "editorInactiveSelection": "Az inaktív szerkesztőablakban található kijelölések színe.", + "editorSelectionHighlight": "A kijelöléssel megegyező tartalmú területek színe.", + "editorFindMatch": "A keresés jelenlegi találatának színe.", + "findMatchHighlight": "A keresés további találatainak színe.", + "findRangeHighlight": "A keresést korlátozó terület színe.", + "hoverHighlight": "Kiemelés azon szó alatt, amely fölött lebegő elem jelenik meg.", + "hoverBackground": "A szerkesztőablakban lebegő elemek háttérszíne.", + "hoverBorder": "A szerkesztőablakban lebegő elemek keretszíne.", + "activeLinkForeground": "Az aktív hivatkozások háttérszíne.", + "diffEditorInserted": "A beillesztett szövegek háttérszíne.", + "diffEditorRemoved": "Az eltávolított szövegek háttérszíne.", + "diffEditorInsertedOutline": "A beillesztett szövegek körvonalának színe.", + "diffEditorRemovedOutline": "Az eltávolított szövegek körvonalának színe.", + "mergeCurrentHeaderBackground": "A helyi tartalom fejlécének háttérszíne sorok között megjelenített összeolvasztási konfliktusok esetén.", + "mergeCurrentContentBackground": "A helyi tartalom háttérszíne sorok között megjelenített összeolvasztási konfliktusok esetén.", + "mergeIncomingHeaderBackground": "A beérkező tartalom fejlécének háttérszíne sorok között megjelenített összeolvasztási konfliktusok esetén.", + "mergeIncomingContentBackground": "A beérkező tartalom háttérszíne sorok között megjelenített összeolvasztási konfliktusok esetén.", + "mergeCommonHeaderBackground": "A közös ős tartalom fejlécének háttérszíne sorok között megjelenített összeolvasztási konfliktusok esetén. ", + "mergeCommonContentBackground": "A közös ős tartalom háttérszíne sorok között megjelenített összeolvasztási konfliktusok esetén. ", + "mergeBorder": "A fejlécek és az elválasztó sáv keretszíne a sorok között megjelenített összeolvasztási konfliktusok esetén.", + "overviewRulerCurrentContentForeground": "A helyi tartalom előtérszíne az áttekintő sávon összeolvasztási konfliktusok esetén.", + "overviewRulerIncomingContentForeground": "A beérkező tartalom előtérszíne az áttekintő sávon összeolvasztási konfliktusok esetén.", + "overviewRulerCommonContentForeground": "A közös ős tartalom előtérszíne az áttekintő sávon összeolvasztási konfliktusok esetén. ", + "overviewRulerFindMatchForeground": "A keresési találatokat jelölő jelzések színe az áttekintősávon.", + "overviewRulerSelectionHighlightForeground": "A kiemelt kijelöléseket jelölő jelzések színe az áttekintősávon." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/hun/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..2a24776cd3 --- /dev/null +++ b/i18n/hun/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "Code-munkaterület", + "untitledWorkspace": "Névtelen (munkaterület)", + "workspaceNameVerbose": "{0} (munkaterület)", + "workspaceName": "{0} (munkaterület)" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/hun/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..638bf2f22c --- /dev/null +++ b/i18n/hun/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "A(z) {0} kiegészítő felülírása a következővel: {1}.", + "extensionUnderDevelopment": "A(z) {0} elérési úton található fejlesztői kiegészítő betöltése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/hun/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..6e18d1f39b --- /dev/null +++ b/i18n/hun/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Bezárás", + "cancel": "Mégse", + "ok": "OK" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/hun/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..51e25edfbc --- /dev/null +++ b/i18n/hun/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "{0} további hiba és figyelmeztetés nem jelenik meg." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/hun/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..8ac621e3d7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "A(z) `{1}` kiegészítőt nem sikerült aktiválni. Oka: ismeretlen függőség: `{0}`.", + "failedDep1": "A(z) `{1}` kiegészítőt nem sikerült aktiválni. Oka: a(z) `{0}` függőséget nem sikerült aktiválni.", + "failedDep2": "A(z) `{0}` kiegészítőt nem sikerült aktiválni. Oka: több, mint 10 szintnyi függőség van (nagy valószínűséggel egy függőségi hurok miatt).", + "activationError": "A(z) `{0}` kiegészítő aktiválása nem sikerült: {1}." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/hun/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/hun/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/hun/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..0ff010a59e --- /dev/null +++ b/i18n/hun/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "Nincs '{0}' azonosítóval regisztrált fanézet.", + "treeItem.notFound": "Nincs '{0}' azonosítójú elem a fában.", + "treeView.duplicateElement": "A(z) {0} elem már regisztrálva van" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/hun/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..64a2f423e8 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "Nyelv beállítása", + "displayLanguage": "Meghatározza a VSCode felületének nyelvét.", + "doc": "Az elérhető nyelvek listája a következő címen tekinthető meg: {0}", + "restart": "Az érték módosítása után újra kell indítani a VSCode-ot.", + "fail.createSettings": "A(z) '{0}' nem hozható létre ({1})", + "JsonSchema.locale": "A felhasználói felületen használt nyelv." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/hun/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..50e57f769c --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Mappa megnyitása...", + "openFileFolder": "Megnyitás...", + "addFolderToWorkspace": "Mappa hozzáadása a munkaterülethez...", + "add": "Hozzáadás", + "addFolderToWorkspaceTitle": "Mappa hozzáadása a munkaterülethez", + "removeFolderFromWorkspace": "Mappa eltávolítása a munkaterületről" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/hun/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..945f19749c --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "Tevékenységsáv be- és kikapcsolása", + "view": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/hun/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..eb2dc57539 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "Szerkesztőablak-csoport vízszintes/függőleges elrendezésének váltása", + "horizontalLayout": "Szerkesztőablak-csoport elrendezése vízszintesen", + "verticalLayout": "Szerkesztőablak-csoport elrendezése függőlegesen", + "view": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/hun/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..6dc5a31fd0 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Oldalsáv helyének váltása", + "view": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/hun/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..e38a1179f2 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "Oldalsáv be- és kikapcsolása", + "view": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/hun/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..878705ee51 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "Állapotsor be- és kikapcsolása", + "view": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/hun/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..31950c95b6 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "Zen mód be- és kikapcsolása", + "view": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/hun/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..6a9ac0f739 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Mappa megnyitása...", + "openFileFolder": "Megnyitás...", + "addFolderToWorkspace": "Mappa hozzáadása a munkaterülethez...", + "add": "&&Hozzáadás", + "addFolderToWorkspaceTitle": "Mappa hozzáadása a munkaterülethez", + "newWorkspace": "Új munkaterület...", + "select": "&&Kiválasztás", + "selectWorkspace": "Válassza ki a munkaterület mappáit!", + "removeFolderFromWorkspace": "Mappa eltávolítása a munkaterületről", + "saveWorkspaceAsAction": "Munkaterület mentése másként...", + "saveEmptyWorkspaceNotSupported": "A mentéshez először nyisson meg egy munkaterületet.", + "save": "Menté&&s", + "saveWorkspace": "Munkaterület mentése", + "openWorkspaceAction": "Munkaterület megnyitása...", + "openWorkspaceConfigFile": "Munkaterület konfigurációs fájljának megnyitása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..4ef9e30350 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "Eltávolítás a tevékenységsávról", + "keepInActivityBar": "Megtartás a tevékenységsávon", + "titleKeybinding": "{0} ({1})", + "additionalViews": "További nézetek", + "numberBadge": "{0} ({1})", + "manageExtension": "Kiegészítő kezelése", + "toggle": "Nézet rögzítésének be- és kikapcsolása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..bb21655acd --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "Tevékenységsáv elrejtése", + "activityBarAriaLabel": "Az aktív nézet váltása", + "globalActions": "Globális műveletek" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..dc4bd51c68 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "{0} művelet", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..491de55205 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "Bináris megjelenítő" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..e8a670a43c --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Szövegszerkesztő", + "textDiffEditor": "Szöveges tartalmak differenciaszerkesztő ablaka", + "binaryDiffEditor": "Bináris tartalmak differenciaszerkesztő ablaka", + "sideBySideEditor": "Párhuzamos szerkesztőablakok", + "groupOnePicker": "Az első csoportban található szerkesztőablakok megjelenítése", + "groupTwoPicker": "A második csoportban található szerkesztőablakok megjelenítése", + "groupThreePicker": "A harmadik csoportban található szerkesztőablakok megjelenítése", + "allEditorsPicker": "Összes megnyitott szerkesztőablak megjelenítése", + "view": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..e0449dcddf --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "Szerkesztő kettéosztása", + "joinTwoGroups": "Két szerkesztőcsoport összevonása", + "navigateEditorGroups": "Váltás szerkesztőcsoportok között", + "focusActiveEditorGroup": "Váltás az aktív szerkesztőcsoportra", + "focusFirstEditorGroup": "Váltás az első szerkesztőcsoportra", + "focusSecondEditorGroup": "Váltás a második szerkesztőcsoportra", + "focusThirdEditorGroup": "Váltás a harmadik szerkesztőcsoportra", + "focusPreviousGroup": "Váltás az előző csoportra", + "focusNextGroup": "Váltás a következő csoportra", + "openToSide": "Megnyitás oldalt", + "closeEditor": "Szerkesztőablak bezárása", + "revertAndCloseActiveEditor": "Visszaállítás és szerkesztőablak bezárása", + "closeEditorsToTheLeft": "Balra lévő szerkesztőablakok bezárása", + "closeEditorsToTheRight": "Jobbra lévő szerkesztőablakok bezárása", + "closeAllEditors": "Összes szerkesztőablak bezárása", + "closeUnmodifiedEditors": "Nem módosult szerkesztőablakok bezárása a csoportban", + "closeEditorsInOtherGroups": "A többi csoport szerkesztőablakainak bezárása", + "closeOtherEditorsInGroup": "Többi szerkesztőablak bezárása", + "closeEditorsInGroup": "A csoportban lévő összes szerkesztőablak bezárása", + "moveActiveGroupLeft": "Szerkesztőablak-csoport mozgatása balra", + "moveActiveGroupRight": "Szerkesztőablak-csoport mozgatása jobbra", + "minimizeOtherEditorGroups": "Többi szerkesztőablak-csoport kis méretűvé tétele", + "evenEditorGroups": "Szerkesztőablak-csoportok egyenlő méretűvé tétele", + "maximizeEditor": "Szerkesztőablak-csoport nagy méretűvé tétele és oldalsáv elrejtése", + "keepEditor": "Szerkesztőablak nyitva tartása", + "openNextEditor": "Következő szerkesztőablak megnyitása", + "openPreviousEditor": "Előző szerkesztőablak megnyitása", + "nextEditorInGroup": "A csoport következő szerkesztőablakának megnyitása", + "openPreviousEditorInGroup": "A csoport előző szerkesztőablakának megnyitása", + "navigateNext": "Ugrás előre", + "navigatePrevious": "Ugrás vissza", + "reopenClosedEditor": "Bezárt szerkesztőablak újranyitása", + "clearRecentFiles": "Legutóbb megnyitottak listájának ürítése", + "showEditorsInFirstGroup": "Az első csoportban található szerkesztőablakok megjelenítése", + "showEditorsInSecondGroup": "A második csoportban található szerkesztőablakok megjelenítése", + "showEditorsInThirdGroup": "A harmadik csoportban található szerkesztőablakok megjelenítése", + "showEditorsInGroup": "A csoportban található szerkesztőablakok megjelenítése", + "showAllEditors": "Összes szerkesztőablak megjelenítése", + "openPreviousRecentlyUsedEditorInGroup": "A csoportban előző legutoljára használt szerksztőablak megnyitása", + "openNextRecentlyUsedEditorInGroup": "A csoportban következő legutoljára használt szerksztőablak megnyitása", + "navigateEditorHistoryByInput": "Előző szerkesztőablak menyitása az előzményekből", + "openNextRecentlyUsedEditor": "A következő legutoljára használt szerksztőablak megnyitása", + "openPreviousRecentlyUsedEditor": "Az előző legutoljára használt szerksztőablak megnyitása", + "clearEditorHistory": "Szerkesztőablak-előzmények törlése", + "focusLastEditorInStack": "Csoport utolsó szerkesztőablakának megnyitása", + "moveEditorLeft": "Szerkesztőablak mozgatása balra", + "moveEditorRight": "Szerkesztőablak mozgatása jobbra", + "moveEditorToPreviousGroup": "Szerkesztőablak mozgatása az előző csoportba", + "moveEditorToNextGroup": "Szerkesztőablak mozgatása a következő csoportba" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..fe48d66576 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "Aktív szerkesztőablak mozgatása fülek vagy csoportok között", + "editorCommand.activeEditorMove.arg.name": "Aktív szerkesztőablak mozgatási argumentum", + "editorCommand.activeEditorMove.arg.description": "Argumentumtulajdonságok:\n\t\t\t\t\t\t* 'to': karakterlánc, a mozgatás célpontja.\n\t\t\t\t\t\t* 'by': karakterlánc, a mozgatás egysége. Fülek (tab) vagy csoportok (group) alapján.\n\t\t\t\t\t\t* 'value': szám, ami meghatározza, hogy hány pozíciót kell mozgatni, vagy egy abszolút pozíciót, ahová mozgatni kell.\n\t\t\t\t\t", + "commandDeprecated": "A(z) **{0}** parancs el lett távolítva. A(z) **{1}** használható helyette", + "openKeybindings": "Billentyűparancsok beállítása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..41ddde984f --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "Bal", + "groupTwoVertical": "Középső", + "groupThreeVertical": "Jobb", + "groupOneHorizontal": "Felső", + "groupTwoHorizontal": "Középső", + "groupThreeHorizontal": "Alsó", + "editorOpenError": "Nem sikerült megnyitni a(z) '{0}' fájlt: {1}." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..b272b0a004 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, szerkesztőcsoport-választó", + "groupLabel": "Csoport: {0}", + "noResultsFoundInGroup": "A csoportban nem található ilyen nyitott szerkesztőablak", + "noOpenedEditors": "A csoportban jelenleg nincs megnyitott szerkesztőablak", + "noResultsFound": "Nem található ilyen nyitott szerkesztőablak", + "noOpenedEditorsAllGroups": "Jelenleg nincs megnyitott szerkesztőablak" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..0adc811e1b --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "{0}. sor, {1}. oszlop ({2} kijelölve)", + "singleSelection": "{0}. sor, {1}. oszlop", + "multiSelectionRange": "{0} kijelölés ({1} karakter kijelölve)", + "multiSelection": "{0} kijelölés", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "Tab fókuszt vált", + "screenReaderDetected": "Képernyőolvasó érzékelve", + "screenReaderDetectedExtra": "Ha nem használ képernyőolvasót, állítsa az `editor.accessibilitySupport` értékét \"off\"-ra.", + "disableTabMode": "Kisegítő mód letiltása", + "gotoLine": "Sor megkeresése", + "indentation": "Indentálás", + "selectEncoding": "Kódolás kiválasztása", + "selectEOL": "Sorvégjel kiválasztása", + "selectLanguageMode": "Nyelvmód kiválasztása", + "fileInfo": "Fájlinformáció", + "spacesSize": "Szóközök: {0}", + "tabSize": "Tabulátorméret: {0}", + "showLanguageExtensions": "'{0}' kiegészítő keresése a piactéren...", + "changeMode": "Nyelvmód váltása", + "noEditor": "Jelenleg nincs aktív szerkesztőablak", + "languageDescription": "({0}) - Beállított nyelv", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "nyelvek (azonosító)", + "configureModeSettings": "'{0}' nyelvi beállítások módosítása...", + "configureAssociationsExt": "'{0}' fájlhozzárendelések módosítása...", + "autoDetect": "Automatikus felderítés", + "pickLanguage": "Nyelvmód kiválasztása", + "currentAssociation": "Jelenlegi társítás", + "pickLanguageToConfigure": "A(z) '{0}' kiterjesztéshez társított nyelvmód kiválasztása", + "changeIndentation": "Indentálás módosítása", + "noWritableCodeEditor": "Az aktív kódszerkesztő-ablak írásvédett módban van.", + "indentView": "nézet váltása", + "indentConvert": "fájl konvertálása", + "pickAction": "Művelet kiválasztása", + "changeEndOfLine": "Sorvégjel módosítása", + "pickEndOfLine": "Sorvégjel kiválasztása", + "changeEncoding": "Fájlkódolás módosítása", + "noFileEditor": "Jelenleg nincs aktív fájl", + "saveWithEncoding": "Mentés adott kódolással", + "reopenWithEncoding": "Újranyitás adott kódolással", + "guessedEncoding": "Kitalálva a tartalomból", + "pickEncodingForReopen": "Válassza ki a kódolást a fájl újranyitásához", + "pickEncodingForSave": "Válassza ki a mentéshez használandó kódolást" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..6fa6f2e549 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "Fülműveletek" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..04046fc346 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "Szöveges tartalmak differenciaszerkesztő ablaka", + "readonlyEditorWithInputAriaLabel": "{0}. Írásvédett szövegösszehasonlító.", + "readonlyEditorAriaLabel": "Írásvédett szövegösszehasonlító.", + "editableEditorWithInputAriaLabel": "{0}. Szövegfájl-összehasonlító.", + "editableEditorAriaLabel": "Szövegfájl-összehasonlító.", + "navigate.next.label": "Következő módosítás", + "navigate.prev.label": "Előző módosítás", + "inlineDiffLabel": "Váltás inline nézetre", + "sideBySideDiffLabel": "Váltás párhuzamos nézetre" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..97d7f7fe18 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0}, {1}. csoport" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..7ba5ddd8a7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Szövegszerkesztő", + "readonlyEditorWithInputAriaLabel": "{0}. Írásvédett szövegszerkesztő.", + "readonlyEditorAriaLabel": "Írásvédett szövegszerkesztő.", + "untitledFileEditorWithInputAriaLabel": "{0}. Névtelen szövegszerkesztő.", + "untitledFileEditorAriaLabel": "Névtelen szövegszerkesztő." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..c8bb52a673 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Bezárás", + "closeOthers": "Többi bezárása", + "closeRight": "Jobbra lévők bezárása", + "closeAll": "Összes bezárása", + "closeAllUnmodified": "Nem módosultak bezárása", + "keepOpen": "Maradjon nyitva", + "showOpenedEditors": "Megnyitott szerkesztőablak megjelenítése", + "araLabelEditorActions": "Szerkesztőablak-műveletek" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..0203755656 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "Panel bezárása", + "togglePanel": "Panel be- és kikapcsolása", + "focusPanel": "Váltás a panelra", + "toggleMaximizedPanel": "Teljes méretű panel be- és kikapcsolása", + "maximizePanel": "Panel teljes méretűvé tétele", + "minimizePanel": "Panel méretének visszaállítása", + "view": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..611eada015 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "Az aktív panel váltása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..5d6026255f --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} (Nyomjon 'Enter'-t a megerősítéshez vagy 'Escape'-et a megszakításhoz)", + "inputModeEntry": "Nyomjon 'Enter'-t a megerősítéshez vagy 'Escape'-et a megszakításhoz", + "emptyPicks": "Nincs választható elem", + "quickOpenInput": "A végrehajtható műveletek körét a ? karakter beírásával tekintheti meg", + "historyMatches": "legutóbb megnyitott", + "noResultsFound1": "Nincs találat", + "canNotRunPlaceholder": "A jelenlegi kontextusban nem használható a gyorsmegnyitási funkció", + "entryAriaLabel": "{0}, legutóbb megnyitott", + "removeFromEditorHistory": "Eltávolítás az előzményekből", + "pickHistory": "Válassza ki azt a szerkesztőablakot, amit el szeretne távolítani az előzményekből" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..9583f895ce --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "File megkeresése...", + "quickNavigateNext": "Ugrás a következőre a fájlok gyors megnyitásánál", + "quickNavigatePrevious": "Ugrás az előzőre a fájlok gyors megnyitásánál", + "quickSelectNext": "Következő kiválasztása a fájlok gyors megnyitásánál", + "quickSelectPrevious": "Előző kiválasztása a fájlok gyors megnyitásánál" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..3a90cdc328 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "Oldalsáv elrejtése", + "focusSideBar": "Váltás az oldalsávra", + "viewCategory": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..a7191f77c8 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "A(z) '{0}' parancs jelenleg nem engedélyezett és nem futtatható.", + "manageExtension": "Kiegészítő kezelése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..bf8ab6b86f --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[Nem támogatott]", + "devExtensionWindowTitlePrefix": "[Kiegészítő fejlesztői példány]" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/quickopen.i18n.json b/i18n/hun/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..c760aab0e2 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "Nincs eredmény", + "noResultsFound2": "Nincs találat", + "entryAriaLabel": "{0}, parancs" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/viewlet.i18n.json b/i18n/hun/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..5012172606 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Összes bezárása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/common/theme.i18n.json b/i18n/hun/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..2efc9eb4b8 --- /dev/null +++ b/i18n/hun/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "Az aktív fül háttérszíne. A fülek tartalmazzák a szerkesztőablakokat a szerkesztőterületen. Egy szerkesztőablak-csoportban több fül is megnyitható. Több szerkesztőablak-csoportot is létre lehet hozni.", + "tabInactiveBackground": "Az inaktív fülek háttérszíne. A fülek tartalmazzák a szerkesztőablakokat a szerkesztőterületen. Egy szerkesztőablak-csoportban több fül is megnyitható. Több szerkesztőablak-csoportot is létre lehet hozni.", + "tabBorder": "A füleket egymástól elválasztó keret színe. A fülek tartalmazzák a szerkesztőablakokat a szerkesztőterületen. Egy szerkesztőablak-csoportban több fül is megnyitható. Több szerkesztőablak-csoportot is létre lehet hozni.", + "tabActiveBorder": "Az aktív fülek kiemelésére használt keretszín. A fülek tartalmazzák a szerkesztőablakokat a szerkesztőterületen. Egy szerkesztőablak-csoportban több fül is megnyitható. Több szerkesztőablak-csoportot is létre lehet hozni.", + "tabActiveUnfocusedBorder": "Az aktív fülek kiemelésére használt keretszín egy fókusz nélküli csoportban. A fülek tartalmazzák a szerkesztőablakokat a szerkesztőterületen. Egy szerkesztőablak-csoportban több fül is megnyitható. Több szerkesztőablak-csoportot is létre lehet hozni. ", + "tabActiveForeground": "Az aktív fül előtérszíne az aktív csoportban. A fülek tartalmazzák a szerkesztőablakokat a szerkesztőterületen. Egy szerkesztőablak-csoportban több fül is megnyitható. Több szerkesztőablak-csoportot is létre lehet hozni.", + "tabInactiveForeground": "Az inaktív fülek előtérszíne az aktív csoportban. A fülek tartalmazzák a szerkesztőablakokat a szerkesztőterületen. Egy szerkesztőablak-csoportban több fül is megnyitható. Több szerkesztőablak-csoportot is létre lehet hozni.", + "tabUnfocusedActiveForeground": "Az aktív fül előtérszíne egy fókusz nélküli csoportban. A fülek tartalmazzák a szerkesztőablakokat a szerkesztőterületen. Egy szerkesztőablak-csoportban több fül is megnyitható. Több szerkesztőablak-csoportot is létre lehet hozni.", + "tabUnfocusedInactiveForeground": "Az inaktív fülek előtérszíne egy fókusz nélküli csoportban. A fülek tartalmazzák a szerkesztőablakokat a szerkesztőterületen. Egy szerkesztőablak-csoportban több fül is megnyitható. Több szerkesztőablak-csoportot is létre lehet hozni.", + "editorGroupBackground": "A szerkesztőcsoportok háttérszíne. A szerkesztőcsoportok szerkesztőablakokat tartalmaznak. A háttérszín akkor jelenik meg, ha a szerkesztőcsoportok mozgatva vannak.", + "tabsContainerBackground": "A szerkesztőcsoport címsorának háttérszíne, ha a fülek engedélyezve vannak. A szerkesztőcsoportok szerkesztőablakokat tartalmaznak.", + "tabsContainerBorder": "A szerkesztőcsoport címsorának keretszíne, ha a fülek engedélyezve vannak. A szerkesztőcsoportok szerkesztőablakokat tartalmaznak.", + "editorGroupHeaderBackground": "A szerkesztőcsoport címsorának keretszíne, ha a fülek le vannak tiltva. A szerkesztőcsoportok szerkesztőablakokat tartalmaznak.", + "editorGroupBorder": "A szerkesztőcsoportokat elválasztó vonal színe. A szerkesztőcsoportok szerkesztőablakokat tartalmaznak.", + "editorDragAndDropBackground": "A szerkesztőablakok mozgatásánál használt háttérszín. Érdemes átlátszó színt választani, hogy a szerkesztőablak tartalma továbbra is látszódjon.", + "panelBackground": "A panelek háttérszíne. A panelek a szerkesztőterület alatt jelennek meg, és pl. itt található a kimenetet és az integrált terminál.", + "panelBorder": "A panelek keretszíne, ami elválasztja őket a szerkesztőablakoktól. A panelek a szerkesztőterület alatt jelennek meg, és pl. itt található a kimenetet és az integrált terminál.", + "panelActiveTitleForeground": "Az aktív panel címsorának színe. A panelek a szerkesztőterület alatt jelennek meg, és pl. itt található a kimenetet és az integrált terminál.", + "panelInactiveTitleForeground": "Az inaktív panelek címsorának színe. A panelek a szerkesztőterület alatt jelennek meg, és pl. itt található a kimenetet és az integrált terminál.", + "panelActiveTitleBorder": "Az aktív panel címsorának keretszíne. A panelek a szerkesztőterület alatt jelennek meg, és pl. itt található a kimenetet és az integrált terminál.", + "statusBarForeground": "Az állapotsor előtérszíne, ha egy munkaterület van megnyitva. Az állapotsor az ablak alján jelenik meg.", + "statusBarNoFolderForeground": "Az állapotsor előtérszíne, ha nincs mappa megnyitva. Az állapotsor az ablak alján jelenik meg.", + "statusBarBackground": "Az állapotsor háttérszíne, ha egy munkaterület van megnyitva. Az állapotsor az ablak alján jelenik meg.", + "statusBarNoFolderBackground": "Az állapotsor háttérszíne, ha nincs mappa megnyitva. Az állapotsor az ablak alján jelenik meg.", + "statusBarBorder": "Az állapotsort az oldalsávtól és a szerkesztőablakoktól elválasztó keret színe. Az állapotsor az ablak alján jelenik meg.", + "statusBarNoFolderBorder": "Az állapotsort az oldalsávtól és a szerkesztőablakoktól elválasztó keret színe, ha nincs mappa megnyitva. Az állapotsor az ablak alján jelenik meg. ", + "statusBarItemActiveBackground": "Az állapotsor elemének háttérszíne kattintás esetén. Az állapotsor az ablak alján jelenik meg.", + "statusBarItemHoverBackground": "Az állapotsor elemének háttérszíne, ha az egérkurzor fölötte van. Az állapotsor az ablak alján jelenik meg.", + "statusBarProminentItemBackground": "Az állapotsor kiemelt elemeinek háttérszíne. A kiemelt elemek kitűnnek az állapotsor többi eleme közül, így jelezve a fontosságukat. Az állapotsor az ablak alján jelenik meg.", + "statusBarProminentItemHoverBackground": "Az állapotsor kiemelt elemeinek háttérszíne, ha az egérkurzor fölöttük van. A kiemelt elemek kitűnnek az állapotsor többi eleme közül, így jelezve a fontosságukat. Az állapotsor az ablak alján jelenik meg.", + "activityBarBackground": "A tevékenységsáv háttérszíne. A tevékenységsáv az ablak legszélén jelenik meg bal vagy jobb oldalon, segítségével lehet váltani az oldalsáv nézetei között.", + "activityBarForeground": "A tevékenységsáv előtérszíne (pl. az ikonok színe). A tevékenységsáv az ablak legszélén jelenik meg bal vagy jobb oldalon, segítségével lehet váltani az oldalsáv nézetei között.", + "activityBarBorder": "A tevékenyésgsáv keretszíne, ami elválasztja az oldalsávtól. A tevékenységsáv az ablak legszélén jelenik meg bal vagy jobb oldalon, segítségével lehet váltani az oldalsáv nézetei között.", + "activityBarDragAndDropBackground": "A tevékenységsáv elemeinek mozgatásánál használt visszajelzési szín. Érdemes átlátszó színt választani, hogy a tevékenységsáv elemei láthatóak maradjanak. A tevékenységsáv az ablak legszélén jelenik meg bal vagy jobb oldalon, segítségével lehet váltani az oldalsáv nézetei között.", + "activityBarBadgeBackground": "A tevékenységsáv értesítési jelvényeinek háttérszíne. A tevékenységsáv az ablak legszélén jelenik meg bal vagy jobb oldalon, segítségével lehet váltani az oldalsáv nézetei között.", + "activityBarBadgeForeground": "A tevékenységsáv értesítési jelvényeinek előtérszíne. A tevékenységsáv az ablak legszélén jelenik meg bal vagy jobb oldalon, segítségével lehet váltani az oldalsáv nézetei között.", + "sideBarBackground": "Az oldalsáv háttérszíne. Az oldalsávon található például a fájlkezelő és a keresés nézet.", + "sideBarForeground": "Az oldalsáv előtérszíne. Az oldalsávon található például a fájlkezelő és a keresés nézet.", + "sideBarBorder": "Az oldalsáv keretszíne, ami elválasztja a szerkesztőablaktól. Az oldalsávon található például a fájlkezelő és a keresés nézet.", + "sideBarTitleForeground": "Az oldalsáv címsorának előtérszíne. Az oldalsávon található például a fájlkezelő és a keresés nézet.", + "sideBarDragAndDropBackground": "Az oldalsáv szakaszainak mozgatásánál használt visszajelzési szín. Érdemes átlátszó színt választani, hogy az oldalsáv szakaszai láthatóak maradjanak. Az oldalsávon található például a fájlkezelő és a keresés nézet.", + "sideBarSectionHeaderBackground": "Az oldalsáv szakaszfejlécének háttérszíne. Az oldalsávon található például a fájlkezelő és a keresés nézet.", + "sideBarSectionHeaderForeground": "Az oldalsáv szakaszfejlécének előtérszíne. Az oldalsávon található például a fájlkezelő és a keresés nézet.", + "titleBarActiveForeground": "A címsor előtérszíne, ha az ablak aktív. Megjegyzés: ez a beállítás jelenleg csak macOS-en támogatott.", + "titleBarInactiveForeground": "A címsor előtérszíne, ha az ablak inaktív. Megjegyzés: ez a beállítás jelenleg csak macOS-en támogatott.", + "titleBarActiveBackground": "A címsor háttérszíne, ha az ablak aktív. Megjegyzés: ez a beállítás jelenleg csak macOS-en támogatott.", + "titleBarInactiveBackground": "A címsor háttérszíne, ha az ablak inaktív. Megjegyzés: ez a beállítás jelenleg csak macOS-en támogatott.", + "titleBarBorder": "A címsor keretszíne, ha az ablak aktív. Megjegyzés: ez a beállítás jelenleg csak macOS-en támogatott.", + "notificationsForeground": "Az értesítések előtérszíne. Az értesítések az ablak tetején ugranak fel.", + "notificationsBackground": "Az értesítések háttérszíne. Az értesítések az ablak tetején ugranak fel.", + "notificationsButtonBackground": "Az értesítések gombjainak háttérszíne. Az értesítések az ablak tetején ugranak fel.", + "notificationsButtonHoverBackground": "Az értesítések gombjainak háttérszíne, ha az egérkurzor fölöttük van. Az értesítések az ablak tetején ugranak fel.", + "notificationsButtonForeground": "Az értesítések gombjainak előtérszíne. Az értesítések az ablak tetején ugranak fel.", + "notificationsInfoBackground": "Az információs értesítések háttérszíne. Az értesítések az ablak tetején ugranak fel.", + "notificationsInfoForeground": "Az információs értesítések előtérszíne. Az értesítések az ablak tetején ugranak fel.", + "notificationsWarningBackground": "A figyelmeztető értesítések háttérszíne. Az értesítések az ablak tetején ugranak fel.", + "notificationsWarningForeground": "A figyelmeztető értesítések előtérszíne. Az értesítések az ablak tetején ugranak fel.", + "notificationsErrorBackground": "A hibajelző értesítések háttérszíne. Az értesítések az ablak tetején ugranak fel.", + "notificationsErrorForeground": "A hibajelző értesítések előtérszíne. Az értesítések az ablak tetején ugranak fel." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/hun/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..b807de9ced --- /dev/null +++ b/i18n/hun/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "Szerkesztőablak bezárása", + "closeWindow": "Ablak bezárása", + "closeWorkspace": "Munkaterület bezárása", + "noWorkspaceOpened": "Az aktuális példányban nincs egyetlen munkaterület sem nyitva, amit be lehetne zárni.", + "newWindow": "Új ablak", + "toggleFullScreen": "Teljes képernyő be- és kikapcsolása", + "toggleMenuBar": "Menüsáv be- és kikapcsolása", + "toggleDevTools": "Fejlesztői eszközök be- és kikapcsolása", + "zoomIn": "Nagyítás", + "zoomOut": "Kicsinyítés", + "zoomReset": "Nagyítási szint alaphelyzetbe állítása", + "appPerf": "Indulási teljesítmény", + "reloadWindow": "Ablak újratöltése", + "switchWindowPlaceHolder": "Válassza ki az ablakot, amire váltani szeretne", + "current": "Aktuális ablak", + "close": "Ablak bezárása", + "switchWindow": "Ablak váltása...", + "quickSwitchWindow": "Gyors ablakváltás...", + "workspaces": "munkaterületek", + "files": "fájlok", + "openRecentPlaceHolderMac": "Válasszon a megnyitáshoz! (Nyomja le a Cmd-billentyűt az új ablakban való megnyitáshoz!)", + "openRecentPlaceHolder": "Válasszon a megnyitáshoz! (Nyomja le a Ctrl-billentyűt az új ablakban való megnyitáshoz!)", + "remove": "Eltávolítás a legutóbb megnyitottak listájáról", + "openRecent": "Legutóbbi megnyitása...", + "quickOpenRecent": "Legutóbbi gyors megnyitása...", + "closeMessages": "Értesítések törlése", + "reportIssues": "Problémák jelentése", + "reportPerformanceIssue": "Teljesítményproblémák jelentése", + "keybindingsReference": "Billentyűparancs-referencia", + "openDocumentationUrl": "Dokumentáció", + "openIntroductoryVideosUrl": "Bemutatóvideók", + "openTipsAndTricksUrl": "Tippek és trükkök", + "toggleSharedProcess": "Megosztott folyamat be- és klikapcsolása", + "navigateLeft": "Navigálás a balra lévő nézetre", + "navigateRight": "Navigálás a jobbra lévő nézetre", + "navigateUp": "Navigálás a felül lévő nézetre", + "navigateDown": "Navigálás az alul lévő nézetre", + "increaseViewSize": "Jelenlegi nézet méretének növelése", + "decreaseViewSize": "Jelenlegi nézet méretének csökkentése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/hun/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/hun/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/hun/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..ac13c73201 --- /dev/null +++ b/i18n/hun/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "A kiegészítő gazdafolyamata nem idult el 10 másodperben belül. Elképzelhető, hogy megállt az első soron, és szüksége van a hibakeresőre a folytatáshoz.", + "extensionHostProcess.startupFail": "A kiegészítő gazdafolyamata nem idult el 10 másodperben belül. Ez probléma lehet.", + "extensionHostProcess.error": "A kiegészítő gazdafolyamatától hiba érkezett: {0}", + "devTools": "Fejlesztői eszközök", + "extensionHostProcess.crash": "A kiegészítő gazdafolyamata váratlanul leállt. Töltse újra az ablakot a visszaállításhoz." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/hun/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..3451a953a6 --- /dev/null +++ b/i18n/hun/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "Nézet", + "help": "Súgó", + "file": "Fájl", + "workspaces": "Munkaterületek", + "developer": "Fejlesztői", + "showEditorTabs": "Meghatározza, hogy a megnyitott szerkesztőablakok telején megjelenjenek-e a fülek", + "editorTabCloseButton": "Meghatározza a szerkesztőablakok fülein található bezárógomb pozícióját vagy eltávolítja őket, ha a beállítás értéke 'off'.", + "showIcons": "Meghatározza, hogy a megnyitott szerkesztőablakok ikonnal együtt jelenjenek-e meg. A működéshez szükséges egy ikontéma engedélyezése is.", + "enablePreview": "Meghatározza, hogy a megnyitott szerkesztőablakok előnézetként jelenjenek-e meg. Az előnézetként használt szerkesztőablakok újra vannak hasznosítva, amíg meg nem tartja őket a felhasználó (pl. dupla kattintás vagy szerkesztés esetén).", + "enablePreviewFromQuickOpen": "Meghatározza, hogy a gyors megnyitás során megnyitott szerkesztőablakok előnézetként jelenjenek-e meg. Az előnézetként használt szerkesztőablakok újra vannak hasznosítva, amíg meg nem tartja őket a felhasználó (pl. dupla kattintás vagy szerkesztés esetén).", + "editorOpenPositioning": "Meghatározza, hogy hol nyíljanak meg a szerkesztőablakok. A 'left' vagy 'right' használata esetén az aktív szerkesztőablaktól jobbra vagy balra nyílnak meg az újak. A 'first' vagy 'last' esetén a szerkesztőablakok a jelenleg aktív ablaktól függetlenül nyílnak meg.", + "revealIfOpen": "Meghatározza, hogy egy szerkesztőablak fel legyen-e fedve, ha már meg van nyitva a látható csoportok bármelyiképben. Ha le van tiltva, akkor egy új szerkesztőablak nyílik az aktív szerkesztőablak-csoportban. Ha engedélyezve van, akkor a már megnyitott szerkesztőablak lesz felfedve egy új megnyitása helyett. Megjegyzés: vannak esetek, amikor ez a beállítás figyelmen kívül van hagyva, pl. ha egy adott szerkesztőablak egy konkrét csoportban vagy a jelenleg aktív csoport mellett van menyitva.", + "commandHistory": "Meghatározza, hogy mennyi legutóbb használt parancs jelenjen meg a parancskatalógus előzményeinek listájában. Az előzmények kikapcsolásához állítsa az értéket 0-ra.", + "preserveInput": "Meghatározza, hogy a legutóbb beírt parancs automatikusan helyre legyen-e állítva a parancskatalógus következő megnyitása során.", + "closeOnFocusLost": "Meghatározza, hogy a gyors megnyitás automatikusan bezáródjon-e amint elveszíti a fókuszt.", + "openDefaultSettings": "Meghatározza, hogy a beállítások megnyitásakor megnyíljon-e egy szerkesztő az összes alapértelmezett beállítással.", + "sideBarLocation": "Meghatározza az oldalsáv helyét. Az oldalsáv megjelenhet a munkaterület bal vagy jobb oldalán.", + "statusBarVisibility": "Meghatározza, hogy megjelenjen-e az állapotsor a munkaterület alján.", + "activityBarVisibility": "Meghatározza, hogy megjelenjen-e a tevékenységsáv a munkaterületen.", + "closeOnFileDelete": "Meghatározza, hogy bezáródjanak-e azok a szerkesztőablakok, melyekben olyan fájl van megnyitva, amelyet töröl vagy átnevez egy másik folyamat. A beállítás letiltása esetén a szerkesztőablak nyitva marad módosított állapotban ilyen esemény után. Megjegyzés: az alkalmazáson belüli törlések esetén mindig bezáródik a szerkesztőablakok, a módosított fájlok pedig soha nem záródnak be, hogy az adatok megmaradjanak.", + "fontAliasing": "Meghatározza a munkaterületen megjelenő betűtípusok élsimítási módszerét.\n- default: Szubpixeles betűsimítás. A legtöbb nem-retina típusú kijelzőn ez adja a legélesebb szöveget.\n- antialiased: A betűket pixelek, és nem szubpixelek szintjén simítja. A betűtípus vékonyabbnak tűnhet összességében.\n- none: Letiltja a betűtípusok élsimítését. A szövegek egyenetlen, éles szélekkel jelennek meg.", + "workbench.fontAliasing.default": "Szubpixeles betűsimítás. A legtöbb nem-retina típusú kijelzőn ez adja a legélesebb szöveget.", + "workbench.fontAliasing.antialiased": "A betűket pixelek, és nem szubpixelek szintjén simítja. A betűtípus vékonyabbnak tűnhet összességében.", + "workbench.fontAliasing.none": "Letiltja a betűtípusok élsimítését. A szövegek egyenetlen, éles szélekkel jelennek meg.", + "swipeToNavigate": "Navigálás a nyitott fájlok között háromujjas, vízszintes húzással.", + "workbenchConfigurationTitle": "Munkaterület", + "window.openFilesInNewWindow.on": "A fájlok új ablakban nyílnak meg", + "window.openFilesInNewWindow.off": "A fájlok abban az ablakban nyílnak meg, ahol a mappájuk meg van nyitva vagy a legutoljára aktív ablakban", + "window.openFilesInNewWindow.default": "A fájlok abban az ablakban nyílnak meg, ahol a mappájuk meg van nyitva vagy a legutoljára aktív ablakban, kivéve, ha a dokkról vagy a Finderből lettek megnyitva (csak macOS-en)", + "openFilesInNewWindow": "Meghatározza, hogy a fájlok új ablakban legyenek-e megnyitva.\n- default: A fájlok abban az ablakban nyílnak meg, ahol a mappájuk meg van nyitva vagy a legutoljára aktív ablakban, kivéve, ha a dokkról vagy a Finderből lettek megnyitva (csak macOS-en)\n- on: A fájlok új ablakban nyílnak meg.\n- off: A fájlok abban az ablakban nyílnak meg, ahol a mappájuk meg van nyitva vagy a legutoljára aktív ablakban\nMegjegyzés: vannak esetek, amikor ez a beállítás figyelmen kívül van hagyva (pl. a -new-window vagy a -reuse-window parancssori beállítás használata esetén).", + "window.openFoldersInNewWindow.on": "A mappák új ablakban nyílnak meg", + "window.openFoldersInNewWindow.off": "A mappák lecserélik a legutoljára aktív ablakot", + "window.openFoldersInNewWindow.default": "A mappák új ablakban nyílnak meg, kivéve akkor, ha a mappát az alkalmazáson belül lett kiválasztva (pl. a Fájl menüből)", + "openFoldersInNewWindow": "Meghatározza, hogy a mappák új ablakban legyenek-e megnyitva.\n- alapértelmezett: A mappák új ablakban nyílnak meg, kivéve akkor, ha a mappát az alkalmazáson belül lett kiválasztva (pl. a Fájl menüből)\n- on: A mappák új ablakban nyílnak meg\n- off: A mappák lecserélik a legutoljára aktív ablakot\nMegjegyzés: vannak esetek, amikor ez a beállítás figyelmen kívül van hagyva (pl. a -new-window vagy a -reuse-window parancssori beállítás használata esetén).", + "window.reopenFolders.all": "Összes ablak újranyitása.", + "window.reopenFolders.folders": "Összes mappa újranyitása. Az üres munkaterületek nem lesznek helyreállítva.", + "window.reopenFolders.one": "A legutóbbi aktív ablak újranyitása.", + "window.reopenFolders.none": "Soha ne nyisson meg újra ablakot. Mindig üresen induljon.", + "restoreWindows": "Meghatározza, hogy újraindítás után hogyan vannak ismét megnyitva az ablakok. A 'none' választása esetén mindig üres munkaterület indul, 'one' esetén a legutóbb használt ablak nyílik meg újra, a 'folders' megnyitja az összes megnyitott mappát, míg az 'all' újranyitja az összes ablakot az előző munkamenetből.", + "restoreFullscreen": "Meghatározza, hogy az ablak teljesképernyős módban nyíljon-e meg, ha kilépéskor teljes képernyős módban volt.", + "zoomLevel": "Meghatározza az ablak nagyítási szintjét. Az eredei méret 0, és minden egyes plusz (pl. 1) vagy mínusz (pl. -1) 20%-kal nagyobb vagy kisebb nagyítási szintet jelent. Tizedestört megadása esetén a nagyítási szint finomabban állítható.", + "title": "Meghatározza az ablak címét az aktív szerkesztőablak alapján. A változók a környezet alapján vannak behelyettesítve:\n${activeEditorShort}: pl. myFile.txt\n${activeEditorMedium}: pl. myFolder/myFile.txt\n${activeEditorLong}: pl. /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: pl. myFolder\n${folderPath}: pl. /Users/Development/myFolder\n${rootName}: pl. myFolder1, myFolder2, myFolder3\n${rootPath}: pl. /Users/Development/myWorkspace\n${appName}: pl. VS Code\n${dirty}: módosításjelző, ami jelzi, ha az aktív szerkesztőablak tartalma módosítva lett\n${separator}: feltételes elválasztó (\" - \"), ami akkor jelenik meg, ha olyan változókkal van körülvéve, amelyeknek van értéke\n", + "window.newWindowDimensions.default": "Az új ablakok a képernyő közepén nyílnak meg.", + "window.newWindowDimensions.inherit": "Az új ablakok ugyanolyan méretben és ugyanazon a helyen jelennek meg, mint a legutoljára aktív ablak.", + "window.newWindowDimensions.maximized": "Az új ablakok teljes méretben nyílnak meg.", + "window.newWindowDimensions.fullscreen": "Az új ablakok teljes képernyős módban nyílnak meg.", + "newWindowDimensions": "Meghatározza az új ablakok méretét és pozícióját, ha már legalább egy ablak meg van nyitva. Az új ablakok alapértlmezetten a képernyő közepén, kis mérettel nyílnak meg. Ha az értéke 'inherit', az ablak ugyanazon méretben és pozícióban nyílik meg, mint a legutoljára aktív. Ha az értéke 'maximized', teljes méretben, ha pedig 'fullscreen' akkor teljes képernyős módban nyílik meg. Megjegyzés: a beállítás nincs hatással az első megnyitott ablakra. Az első ablak mindig a bezárás előtti mérettel és pozícióban nyílik meg.", + "closeWhenEmpty": "Meghatározza, hogy az utolsó szerkesztőablak bezárása esetén az ablak is bezáródjon-e. Ez a beállítás csak az olyan ablakokra vonatkozik, amelyekben nincs mappa megnyitva.", + "window.menuBarVisibility.default": "A menü csak teljes képernyős mód esetén van elrejtve.", + "window.menuBarVisibility.visible": "A menü mindig látható, még teljes képernyő módban is.", + "window.menuBarVisibility.toggle": "A menü rejtett, de megjeleníthető az Alt billentyű lenyomásával.", + "window.menuBarVisibility.hidden": "A menü mindig el van rejtve.", + "menuBarVisibility": "Meghatározza a menüsáv láthatóságát. A 'toggle' érték azt jelenti, hogy a menüsáv rejtett, és az Alt billentyű lenyomására megjelenik. A menüsáv alapértelmezetten látható, kivéve, ha az ablak teljes képernyős módban van.", + "enableMenuBarMnemonics": "Ha engedélyezve van, a főmenük megnyithatók Alt-billentyűs billentyűparancsokkal. Letiltás esetén ezek az Alt-billentyűparancsok más parancsokhoz rendelhetők.", + "autoDetectHighContrast": "Ha engedélyezve van, az alkalmazás automatikusan átvált a nagy kontrasztos témára, ha a WIndows a nagy kontrasztos témát használ, és a sötét témára, ha a Windows átvált a nagy kontrasztos témáról.", + "titleBarStyle": "Módosítja az ablak címsorának megjelenését. A változtatás teljes újraindítást igényel.", + "window.nativeTabs": "Engedélyezi a macOS Sierra ablakfüleket. Megjegyzés: a változtatás teljes újraindítást igényel, és a natív fülek letiltják az egyedi címsorstílust, ha azok be vannak konfigurálva.", + "windowConfigurationTitle": "Ablak", + "zenModeConfigurationTitle": "Zen-mód", + "zenMode.fullScreen": "Meghatározza, hogy zen-módban a munakterület teljes képernyős módba vált-e.", + "zenMode.hideTabs": "Meghatározza, hogy zen-módban el vannak-e rejtve a munkaterület fülei.", + "zenMode.hideStatusBar": "Meghatározza, hogy zen-módban el van-e rejtve a munkaterület alján található állapotsor.", + "zenMode.hideActivityBar": "Meghatározza, hogy zen-módban el van-e rejtve a munkaterület bal oldalán található tevékenységsáv.", + "zenMode.restore": "Meghatározza, hogy az ablak zen-módban induljon-e, ha kilépéskor zen-módban volt." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/electron-browser/main.i18n.json b/i18n/hun/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..c658bd4f2e --- /dev/null +++ b/i18n/hun/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "Az egyik szükséges fájlt nem sikerült betölteni. Vagy megszakadt az internetkapcsolat, vagy a kiszolgáló vált offline-ná. Frissítse az oldalt a böngészőben, és próbálkozzon újra.", + "loaderErrorNative": "Egy szükséges fájl betöltése nem sikerült. Indítsa újra az alkalmazást, és próbálkozzon újra. Részletek: {0}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/hun/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..0fab3894d5 --- /dev/null +++ b/i18n/hun/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "Nem ajánlott a Code-ot 'root'-ként futtatni." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/electron-browser/window.i18n.json b/i18n/hun/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..d36a8319d0 --- /dev/null +++ b/i18n/hun/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "Visszavonás", + "redo": "Újra", + "cut": "Kivágás", + "copy": "Másolás", + "paste": "Beillesztés", + "selectAll": "Összes kijelölése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/hun/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..0cd61fa074 --- /dev/null +++ b/i18n/hun/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "Fejlesztői", + "file": "Fájl" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/hun/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..39bffd7a1e --- /dev/null +++ b/i18n/hun/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "Az {0} elérési út nem érvényes kiegészítő tesztfuttató alkalmazásra mutat." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/hun/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..ab0de4efaf --- /dev/null +++ b/i18n/hun/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Hiba a(z) {0} feldolgozása közben: {1}.", + "fileReadFail": "A(z) ({0}) fájl nem olvasható: {1}.", + "jsonsParseFail": "Hiba a(z) {0} vagy {1} feldolgozása közben: {2}.", + "missingNLSKey": "A(z) {0} kulcshoz tartozó üzenet nem található." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..f83fc61c00 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "'{0}' parancs telepítése a PATH-ba", + "not available": "Ez a parancs nem érhető el.", + "successIn": "A(z) '{0}' rendszerparancs sikeresen telepítve lett a PATH-ba.", + "warnEscalation": "A Code adminisztrátori jogosultságot fog kérni az 'osascript'-tel a rendszerparancs telepítéséhez.", + "ok": "OK", + "cantCreateBinFolder": "Nem sikerült létrehozni az '/usr/local/bin' könyvtárat.", + "cancel2": "Mégse", + "aborted": "Megszakítva", + "uninstall": "'{0}' parancs eltávolítása a PATH-ból", + "successFrom": "A(z) '{0}' rendszerparancs sikeresen el lett a PATH-ból.", + "shellCommand": "Rendszerparancs" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..b60fb843e7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "Az `editor.accessibilitySupport` beállítás értékének beállítása a következőre: 'on'.", + "openingDocs": "A VS Code kisegítő lehetőségei dokumentációjának megnyitása.", + "introMsg": "Köszönjük, hogy kipróbálta a VS Code kisegítő lehetőségeit.", + "status": "Állapot:", + "changeConfigToOnMac": "A szerkesztő folyamatos képernyőolvasóval való használatára optimalizálásához nyomja meg a Command+E gombot!", + "changeConfigToOnWinLinux": "A szerkesztő folyamatos képernyőolvasóval való használatára optimalizálásához nyomja meg a Control+E gombot!", + "auto_unknown": "A szerkesztő úgy van konfigurálva, hogy a platform által biztosított API-kat használja annak megállapításához, hogy van-e képernyőolvasó csatlakoztatva, azonban a jelenlegi futtatókörnyezet ezt nem támogatja.", + "auto_on": "A szerkesztő automatikusan észlelte a csatlakoztatott képernyőolvasót.", + "auto_off": "A szerkesztő úgy van konfigurálva, hogy automatikusan érzékelkje, ha képernyőolvasó van csatlakoztatva. Jelenleg nincs csatlakoztatva.", + "configuredOn": "A szerkesztő folyamatos képernyőolvasóval való használatára van optimalizálva – ez az `editor.accessibilitySupport` beállítás módosításával változtatható.", + "configuredOff": "A szerkesztő úgy van konfigurálva, hogy soha nincs képernyőolvasó használatára optimalizálva.", + "tabFocusModeOnMsg": "Az aktuális szerkesztőablakban a Tab billentyű lenyomása esetén a fókusz a következő fókuszálható elemre kerül. Ez a viselkedés a(z) {0} leütésével módosítható.", + "tabFocusModeOnMsgNoKb": "Az aktuális szerkesztőablakban a Tab billentyű lenyomása esetén a fókusz a következő fókuszálható elemre kerül. A(z) {0} parancs jelenleg nem aktiválható billentyűkombinációval.", + "tabFocusModeOffMsg": "Az aktuális szerkesztőablakban a Tab billentyű lenyomása esetén beszúrásra kerül egy tabulátor karakter. Ez a viselkedés a(z) {0} leütésével módosítható.", + "tabFocusModeOffMsgNoKb": "Az aktuális szerkesztőablakban a Tab billentyű lenyomása esetén beszúrásra kerül egy tabulátor karakter. A(z) {0} parancs jelenleg nem aktiválható billentyűkombinációval.", + "openDocMac": "VS Code kisegítő lehetőségeivel kapcsolatos információk böngészőben való megjelenítéséhez nyomja meg a Command+H billentyűkombinációt!", + "openDocWinLinux": "VS Code kisegítő lehetőségeivel kapcsolatos információk böngészőben való megjelenítéséhez nyomja meg a Control+H billentyűkombinációt!", + "outroMsg": "A súgószöveg eltüntetéséhez és a szerkesztőablakba való visszatéréshez nyomja meg az Escape billentyűt vagy a Shift+Escape billentyűkombinációt!", + "ShowAccessibilityHelpAction": "Kisegítő lehetőségek súgó megjelenítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..0c7d108f81 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "Fejlesztői: Billentyűkiosztás vizsgálata" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..742e69f603 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Fejlesztői: TM-hatókörök vizsgálata", + "inspectTMScopesWidget.loading": "Betöltés..." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..0166c773ad --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Hiba a(z) {0} feldolgozása közben: {1}", + "schema.openBracket": "A nyitó zárójelet definiáló karakter vagy karaktersorozat", + "schema.closeBracket": "A záró zárójelet definiáló karakter vagy karaktersorozat", + "schema.comments": "Meghatározza a megjegyzésszimbólumokat", + "schema.blockComments": "Meghatározza, hogyan vannak jelölve a megjegyzésblokkok.", + "schema.blockComment.begin": "A megjegyzésblokk kezdetét definiáló karaktersorozat.", + "schema.blockComment.end": "A megjegyzésblokk végét definiáló karaktersorozat.", + "schema.lineComment": "A megjegyzéssor kezdetét definiáló karaktersorozat.", + "schema.brackets": "Meghatározza azokat a zárójelszimbólumokat, amelyek növeik vagy csökkentik az indentálást.", + "schema.autoClosingPairs": "Meghatározza a zárójelpárokat. Ha egy nyitó zárójelet írnak be a szerkesztőbe, a záró párja automatikusan be lesz illesztve.", + "schema.autoClosingPairs.notIn": "Azon hatókörök listája, ahol az automatikus zárójelek automatikus párosítása le van tiltve.", + "schema.surroundingPairs": "Meghatározza azok zárójelpárok listáját, melyek használhatók a kijelölt szöveg körbezárására.", + "schema.wordPattern": "A nyelvben található szavak definíciója.", + "schema.wordPattern.pattern": "A szavak illesztésére használt reguláris kifejezés.", + "schema.wordPattern.flags": "A szavak illesztésére használt reguláris kifejezés beállításai.", + "schema.wordPattern.flags.errorMessage": "Illeszkednie kell a következő mintára: `/^([gimuy]+)$/`.", + "schema.indentationRules": "A nyelv indentálási beállításai.", + "schema.indentationRules.increaseIndentPattern": "Ha egy sor illeszkedik erre a mintára, akkor minden utána következő sor eggyel beljebb lesz indentálva (amíg egy újabb szabály nem illeszkedik).", + "schema.indentationRules.increaseIndentPattern.pattern": "Az increaseIndentPatternhöz tartozó reguláris kifejezés.", + "schema.indentationRules.increaseIndentPattern.flags": "Az increaseIndentPatternhöz tartozó reguláris kifejezés beállításai.", + "schema.indentationRules.increaseIndentPattern.errorMessage": "Illeszkednie kell a következő mintára: `/^([gimuy]+)$/`.", + "schema.indentationRules.decreaseIndentPattern": "Ha egy sor illeszkedik erre a mintára, akkor minden utána következő sor eggyel kijjebb lesz indentálva (amíg egy újabb szabály nem illeszkedik).", + "schema.indentationRules.decreaseIndentPattern.pattern": "A decreaseIndentPatternhöz tartozó reguláris kifejezés.", + "schema.indentationRules.decreaseIndentPattern.flags": "A decreaseIndentPatternhöz tartozó reguláris kifejezés beállításai.", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "Illeszkednie kell a következő mintára: `/^([gimuy]+)$/`.", + "schema.indentationRules.indentNextLinePattern": "Ha egy sor illeszkedik erre a mintára, akkor **csak a következő sor** eggyel beljebb lesz indentálva.", + "schema.indentationRules.indentNextLinePattern.pattern": "Az indentNextLinePatternhöz tartozó reguláris kifejezés.", + "schema.indentationRules.indentNextLinePattern.flags": "Az indentNextLinePatternhöz tartozó reguláris kifejezés beállításai.", + "schema.indentationRules.indentNextLinePattern.errorMessage": "Illeszkednie kell a következő mintára: `/^([gimuy]+)$/`.", + "schema.indentationRules.unIndentedLinePattern": "Ha egy sor illeszkedik erre a mintára, akkor az indentálása nem változik, és nem lesz kiértékelve más szabályok alapján.", + "schema.indentationRules.unIndentedLinePattern.pattern": "Az unIndentedLinePatternhöz tartozó reguláris kifejezés.", + "schema.indentationRules.unIndentedLinePattern.flags": "Az unIndentedLinePatternhöz tartozó reguláris kifejezés beállításai.", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "Illeszkednie kell a következő mintára: `/^([gimuy]+)$/`." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..742e69f603 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Fejlesztői: TM-hatókörök vizsgálata", + "inspectTMScopesWidget.loading": "Betöltés..." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..d743922c85 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "Nézet: Kódtérkép be- és kikapcsolása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..ac399dd947 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Többkurzoros módosító be- és kikapcsolása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..11c0efb686 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "Nézet: Vezérlőkarakterek be- és kikapcsolása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..16fc789078 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "Nézet: Szóközök kirajzolásának be- és kikapcsolása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..1d694f4aa8 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "Nézet: Sortörés be- és kikapcsolása", + "wordWrap.notInDiffEditor": "A sortörés nem kapcsolható be vagy ki differenciaszerkesztőben.", + "unwrapMinified": "Sortörés letiltása ebben a fájlban", + "wrapMinified": "Sortörés engedélyezése ebben a fájlban" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..1c8cfaf213 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "OK", + "wordWrapMigration.dontShowAgain": "Ne jelenjen meg újra", + "wordWrapMigration.openSettings": "Beállítások megnyitása", + "wordWrapMigration.prompt": "Az `editor.wrappingColumn` beállítás elavult az `editor.wordWrap` bevezetése miatt." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..7ef71103da --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "Futás megállítása, ha a kifejezés értéke igazra értékelődik ki. 'Enter' a megerősítéshez vagy 'Escape' a megszakításhoz.", + "breakpointWidgetAriaLabel": "A program csak akkor áll meg itt, ha a feltétel igaz. Nyomjon 'Enter'-t a megerősítéshez vagy 'Escape'-et a megszakításhoz.", + "breakpointWidgetHitCountPlaceholder": "Futás megállítása, ha adott alkalommal érintve lett. 'Enter' a megerősítéshez vagy 'Escape' a megszakításhoz.", + "breakpointWidgetHitCountAriaLabel": "A program akkor fog megállni itt, ha adott alkalommal érintette ezt a pontot. Nyomjon 'Enter'-t a megerősítéshez vagy 'Escape'-et a megszakításhoz.", + "expression": "Kifejezés", + "hitCount": "Érintések száma" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..3cc2d40ff6 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "Nincs konfiguráció", + "addConfigTo": "Konfiguráció hozzáadása ({0})...", + "addConfiguration": "Konfiguráció hozzáadása..." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..0a1ff696bc --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "{0} megnyitása", + "launchJsonNeedsConfigurtion": "'launch.json' konfigurálása vagy javítása", + "noFolderDebugConfig": "Fejlettebb hibakeresési konfigurációk használatához nyisson meg egy mappát!", + "startDebug": "Hibakeresés indítása", + "startWithoutDebugging": "Indítás hibakeresés nélkül", + "selectAndStartDebugging": "Hibakeresés kiválasztása és indítása", + "restartDebug": "Újraindítás", + "reconnectDebug": "Újracsatlakozás", + "stepOverDebug": "Átugrás", + "stepIntoDebug": "Belépés", + "stepOutDebug": "Kilépés", + "stopDebug": "Leállítás", + "disconnectDebug": "Kapcsolat bontása", + "continueDebug": "Folytatás", + "pauseDebug": "Szüneteltetés", + "restartFrame": "Keret újraindítása", + "removeBreakpoint": "Töréspont eltávolítása", + "removeAllBreakpoints": "Összes töréspont eltávolítása", + "enableBreakpoint": "Töréspont engedélyezése", + "disableBreakpoint": "Töréspont letiltása", + "enableAllBreakpoints": "Összes töréspont engedélyezése", + "disableAllBreakpoints": "Összes töréspont letiltása", + "activateBreakpoints": "Töréspontok aktiválása", + "deactivateBreakpoints": "Töréspontok deaktiválása", + "reapplyAllBreakpoints": "Töréspontok felvétele ismét", + "addFunctionBreakpoint": "Függvénytöréspont hozzáadása", + "renameFunctionBreakpoint": "Függvénytöréspont átnevezése", + "addConditionalBreakpoint": "Feltételes töréspont hozzáadása...", + "editConditionalBreakpoint": "Töréspont szerkesztése...", + "setValue": "Érték beállítása", + "addWatchExpression": "Kifejezés hozzáadása", + "editWatchExpression": "Kifejezés szerkesztése", + "addToWatchExpressions": "Hozzáadás a figyelőlistához", + "removeWatchExpression": "Kifejezés eltávolítása", + "removeAllWatchExpressions": "Összes kifejezés eltávolítása", + "clearRepl": "Konzoltartalom törlése", + "debugConsoleAction": "Hibakeresési konzol", + "unreadOutput": "Új kimenet a hibakeresési konzolban", + "debugFocusConsole": "Váltás a hibakeresési konzolra", + "focusProcess": "Váltás a folyamatra", + "stepBackDebug": "Visszalépés", + "reverseContinue": "Visszafordítás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..cc9198e4de --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "A hibakeresési eszköztár háttérszíne." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..56d9e511d6 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "Az erőforrás nem oldható fel hibakeresési munkamenet nélkül" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..e697be6790 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "Hibakeresés: Töréspont be- és kikapcsolása", + "columnBreakpointAction": "Hibakeresés: Töréspont oszlopnál", + "columnBreakpoint": "Oszlop töréspont hozzáadása", + "conditionalBreakpointEditorAction": "Hibakeresés: Feltételes töréspont...", + "runToCursor": "Futtatás a kurzorig", + "debugEvaluate": "Hibakeresés: Kiértékelés", + "debugAddToWatch": "Hibakeresés: Hozzáadás a figyelőlistához", + "showDebugHover": "Hibakeresés: Súgószöveg megjelenítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..9fe94c9368 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "Letiltott töréspont", + "breakpointUnverifieddHover": "Nem megerősített töréspont", + "breakpointDirtydHover": "Nem megerősített töréspont. A fájl módosult, indítsa újra a hibakeresési munkamenetet.", + "breakpointUnsupported": "Ez a hibakereső nem támogatja a feltételes töréspontokat" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..4738278bee --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, hibakeresés", + "debugAriaLabel": "Írja be a futtatandó konfiguráció nevét.", + "noConfigurationsMatching": "Nincs illeszkedő hibakeresési konfiguráció", + "noConfigurationsFound": "Nem található hibakeresési konfiguráció. Készítsen egy 'launch.json' fájlt." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..84c9f304fa --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "A kivételmodul keretszíne.", + "debugExceptionWidgetBackground": "A kivételmodul háttérszíne.", + "exceptionThrownWithId": "Kivétel következett be: {0}", + "exceptionThrown": "Kivétel következett be." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..3e71dcf1b0 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "Megnyitás kattintásra (Cmd + kattintásra oldalt nyitja meg)", + "fileLink": "Megnyitás kattintásra (Ctrl + kattintásra oldalt nyitja meg)" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..17668a63f7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "Meghatározza a belső hibakeresési konzol viselkedését." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..83b2b40cbc --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "nem elérhető", + "startDebugFirst": "Indítson egy hibakeresési folyamatot a kiértékeléshez" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..0889fb393d --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "Ismeretlen forrás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..14d7fa99ed --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "Hibakeresés megjelenítése", + "toggleDebugPanel": "Hibakeresési konzol", + "debug": "Hibakeresés", + "debugPanel": "Hibakeresési konzol", + "variables": "Változók", + "watch": "Figyelőlista", + "callStack": "Hívási verem", + "breakpoints": "Töréspontok", + "view": "Nézet", + "debugCategory": "Hibakeresés", + "debugCommands": "Hibakeresési konfiguráció", + "debugConfigurationTitle": "Hibakeresés", + "allowBreakpointsEverywhere": "Bármelyik fájlban helyezhető el töréspont", + "openExplorerOnEnd": "Hibakeresési munkamenet végén automatikusan nyíljon meg a fájlkezelő nézet", + "inlineValues": "Változók értékének megjelenítése a sorok között hibakeresés közben", + "hideActionBar": "Meghatározza, hogy megjelenjen-e a lebegő hibakeresési műveletsáv", + "launch": "Globális hibakeresés indítási konfiguráció. Használható a 'launch.json' alternatívájaként, ami meg van osztva több munkaterület között" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..ba34cca122 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "Fejlettebb hibakeresési konfigurációk használatához nyisson meg egy mappát!" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..80eb1e8389 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "Hibakeresési illesztőket szolgáltat.", + "vscode.extension.contributes.debuggers.type": "A hibakeresési illesztő egyedi azonosítója.", + "vscode.extension.contributes.debuggers.label": "A hibakeresési illesztő megjelenített neve.", + "vscode.extension.contributes.debuggers.program": "A hibakeresési illesztő program elérési útja. Az elérési út lehet abszolút vagy relatív a kiegészítő mappájához képest.", + "vscode.extension.contributes.debuggers.args": "Az illesztő számára átadott argumentumok.", + "vscode.extension.contributes.debuggers.runtime": "Kiegészítő futtatókörnyezet arra az esetre, ha a program attribútum nem egy futtatható fájl, és futtatókörnyezetre van szüksége.", + "vscode.extension.contributes.debuggers.runtimeArgs": "Kiegészítő argumentumok a futtatókörnyezet számára.", + "vscode.extension.contributes.debuggers.variables": "A `launch.json`-ban található interaktív változók (pl. ${action.pickProcess}) hozzárendelése parancsokhoz.", + "vscode.extension.contributes.debuggers.initialConfigurations": "Konfigurációk a 'launch.json' első változatának elkészítéséhez.", + "vscode.extension.contributes.debuggers.languages": "Azon nyelvek listája, amelyeknél ez a hibakeresési kiegészítő alapértelmezett hibakeresőnek tekinthető.", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Ha meg van adva, a VS Code ezt a parancsot fogja hívni a hibakeresési illesztő futtatható állománya elérési útjának és az átadandó argumentumok meghatározásához.", + "vscode.extension.contributes.debuggers.startSessionCommand": "Ha meg van határozva, a VS Code ezt a parancsot fogja hívni az ennek a kiegészítőnek küldött \"debug\" vagy \"run\" parancsok esetén.", + "vscode.extension.contributes.debuggers.configurationSnippets": "Kódtöredékek új 'launch.json'-konfigurációk hozzáadásához.", + "vscode.extension.contributes.debuggers.configurationAttributes": "JSON-sémakonfigurációk a 'launch.json' validálásához.", + "vscode.extension.contributes.debuggers.windows": "Windows-specifikus beállítások.", + "vscode.extension.contributes.debuggers.windows.runtime": "A Windows által használt futtatókörnyezet.", + "vscode.extension.contributes.debuggers.osx": "OS X-specifikus beállítások.", + "vscode.extension.contributes.debuggers.osx.runtime": "Az OSX által használt futtatókörnyezet.", + "vscode.extension.contributes.debuggers.linux": "Linux-specifikus beállítások.", + "vscode.extension.contributes.debuggers.linux.runtime": "A Linux által használt futtatókörnyezet.", + "vscode.extension.contributes.breakpoints": "Töréspontokat szolgáltat.", + "vscode.extension.contributes.breakpoints.language": "Töréspontok engedélyezése ennél a nyelvnél.", + "app.launch.json.title": "Indítás", + "app.launch.json.version": "A fájlformátum verziója.", + "app.launch.json.configurations": "A konfigurációk listája. Új konfigurációk hozzáadhatók vagy a meglévők szerkeszthetők az IntelliSense használatával.", + "app.launch.json.compounds": "A kombinációk listája. Minden kombináció több konfigurációt hivatkozik meg, melyek együtt indulnak el.", + "app.launch.json.compound.name": "A kombináció neve. Az indítási konfiguráció lenyíló menüjében jelenik meg.", + "app.launch.json.compounds.configurations": "Azon konfigurációk neve, melyek elindulnak ezen kombináció részeként.", + "debugNoType": "A hibakeresési illesztő 'type' tulajdonsága kötelező, és 'string' típusúnak kell lennie.", + "selectDebug": "Környezet kiválasztása", + "DebugConfig.failed": "Nem sikerült létrehozni a 'launch.json' fájlt a '.vscode' mappánan ({0})." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..c720b5cc90 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "Töréspontok eltávolítása", + "removeBreakpointOnColumn": "{0}. oszlopban található töréspont eltávolítása", + "removeLineBreakpoint": "Sorra vonatkozó töréspont eltávolítása", + "editBreakpoints": "Töréspontok szerkesztése", + "editBreakpointOnColumn": "{0}. oszlopban található töréspont szerkesztése", + "editLineBrekapoint": "Sorra vonatkozó töréspont szerkesztése", + "enableDisableBreakpoints": "Töréspontok engedélyezése/letiltása", + "disableColumnBreakpoint": "{0}. oszlopban található töréspont letiltása", + "disableBreakpointOnLine": "Sorszintű töréspont letiltása", + "enableBreakpoints": "{0}. oszlopban található töréspont engedélyezése", + "enableBreakpointOnLine": "Sorszintű töréspont engedélyezése", + "addBreakpoint": "Töréspont hozzáadása", + "addConfiguration": "Konfiguráció hozzáadása..." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..5e94bb9530 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "Hibakeresési súgószöveg" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..9eb14ef489 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "Ennél az objektumhoz csak a primitív értékek vannak megjelenítve.", + "debuggingPaused": "Hibakeresés szüneteltetve, oka: {0}, {1} {2}", + "debuggingStarted": "Hibakeresés elindítva.", + "debuggingStopped": "Hibakeresés leállítva.", + "breakpointAdded": "Töréspont hozzáadva, {0}. sor, fájl: {1}", + "breakpointRemoved": "Töréspont eltávoíltva, {0}. sor, fájl: {1}", + "compoundMustHaveConfigurations": "A kombinációk \"configurations\" tulajdonságát be kell állítani több konfiguráció elindításához.", + "configMissing": "A(z) '{0}' konfiguráció hiányzik a 'launch.json'-ból.", + "debugTypeNotSupported": "A megadott hibakeresési típus ('{0}') nem támogatott.", + "debugTypeMissing": "A kiválasztott indítási konfigurációnak hiányzik a 'type' tulajdonsága.", + "preLaunchTaskErrors": "Buildelési hibák léptek fel a(z) '{0}' preLaunchTask futása közben.", + "preLaunchTaskError": "Buildelési hiba lépett fel a(z) '{0}' preLaunchTask futása közben.", + "preLaunchTaskExitCode": "A(z) '{0}' preLaunchTask a következő hibakóddal fejeződött be: {1}.", + "debugAnyway": "Hibakeresés indítása mindenképp", + "noFolderWorkspaceDebugError": "Az aktív fájlon nem lehet hibakeresést végezni. Bizonyosodjon meg róla, hogy el van mentve a lemezre, és hogy az adott fájltípushoz telepítve van a megfelelő hibakeresési kiegészítő.", + "NewLaunchConfig": "Állítson be egy indítási konfigurációt az alkalmazása számára. {0}", + "DebugTaskNotFound": "A(z) '{0}' preLaunchTask nem található." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..430ac9de9d --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "Folyamat", + "paused": "Szüneteltetve", + "running": "Fut", + "thread": "Szál", + "pausedOn": "Szüneteltetve a következő helyen: {0}", + "loadMoreStackFrames": "További veremkeretek betöltése", + "threadAriaLabel": "Szál: {0}, hívási verem, hibakeresés", + "stackFrameAriaLabel": "{0} veremkeret, {0}. sor {1} {2}, hívási verem, hibakeresés", + "variableValueAriaLabel": "Adja meg a változó új nevét", + "variableScopeAriaLabel": "{0} hatókör, változók, hibakeresés", + "variableAriaLabel": "{0} értéke {1}, változók, hibakeresés", + "watchExpressionPlaceholder": "Figyelendő kifejezés", + "watchExpressionInputAriaLabel": "Adja meg a figyelendő kifejezést", + "watchExpressionAriaLabel": "{0} értéke {1}, figyelt, hibakeresés", + "watchVariableAriaLabel": "{0} értéke {1}, figyelt, hibakeresés", + "functionBreakpointPlaceholder": "A függvény, amin meg kell állni", + "functionBreakPointInputAriaLabel": "Adja meg a függvénytöréspontot", + "functionBreakpointsNotSupported": "Ez a hibakereső nem támogatja a függvénytöréspontokat", + "breakpointAriaLabel": "Töréspont a(z) {0}. sorban {1}, töréspontok, hibakeresés", + "functionBreakpointAriaLabel": "{0} függvénytöréspont, töréspontok, hibakeresés", + "exceptionBreakpointAriaLabel": "{0} kivételtöréspont, töréspontok, hibakeresés" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..5bf0b45e72 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "Változók szakasz", + "variablesAriaTreeLabel": "Hibakeresési változók", + "expressionsSection": "Kifejezések szaszasz", + "watchAriaTreeLabel": "Hibakeresési figyelőkifejezések", + "callstackSection": "Hívási verem szakasz", + "debugStopped": "Szüneteltetve a következő helyen: {0}", + "callStackAriaLabel": "Hibakeresési hívási verem", + "breakpointsSection": "Töréspontok szakasz", + "breakpointsAriaTreeLabel": "Hibakeresési töréspontok" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..45181483bc --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "Érték másolása", + "copy": "Másolás", + "copyAll": "Összes másolása", + "copyStackTrace": "Hívási verem másolása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..c6947b89d5 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "További információ", + "unableToLaunchDebugAdapter": "Nem sikerült elindítani a hibakeresési illesztőt a következő helyről: '{0}'.", + "unableToLaunchDebugAdapterNoArgs": "Nem sikerült elindítani a hibakeresési illesztőt.", + "stoppingDebugAdapter": "{0}. Hibakeresési illesztő leállítása.", + "debugAdapterCrash": "A hibakeresési illesztő folyamata váratlanul leállt" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..4b7c48a63b --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "REPL-panel", + "actions.repl.historyPrevious": "Előző az előzményekből", + "actions.repl.historyNext": "Következő az előzményekből", + "actions.repl.acceptInput": "REPL bemenet elfogadása", + "actions.repl.copyAll": "Hibakeresés: Összes másolása a konzolból" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..098a39ba04 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "Az objekum állapota az első kiértékelés idején", + "replVariableAriaLabel": "A(z) {0} változó értéke: {1}, REPL, hibakeresés", + "replExpressionAriaLabel": "A(z) {0} kifejezés értéke: {1}, REPL, hibakeresés", + "replValueOutputAriaLabel": "{0}, REPL, hibakeresés", + "replKeyValueOutputAriaLabel": "A(z) {0} kimeneti változó értéke: {1}, REPL, hibakeresés" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..daee90d273 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "Az állapotsor háttérszíne, ha a programon hibakeresés folyik. Az állapotsor az ablak alján jelenik meg.", + "statusBarDebuggingForeground": "Az állapotsor előtérszíne, ha a programon hibakeresés folyik. Az állapotsor az ablak alján jelenik meg.", + "statusBarDebuggingBorder": "Az állapotsort az oldalsávtól és a szerkesztőablakoktól elválasztó keret színe, ha egy programon hibakeresés történik. Az állapotsor az ablak alján jelenik meg." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..03408cf0d0 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "hibakereső", + "debug.terminal.not.available.error": "Az integrált terminál nem elérhető" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..f0bf37f02e --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "A hibakeresési illesztő futtatható állománya ('{0}') nem létezik.", + "debugAdapterCannotDetermineExecutable": "Nem határozható meg a(z) '{0}' hibakeresési illesztő futtatható állománya.", + "launch.config.comment1": "IntelliSense használata a lehetséges attribútumok listázásához", + "launch.config.comment2": "Húzza fölé az egeret a létező attribútumok leírásának megtekintéséhez!", + "launch.config.comment3": "További információért látogassa meg a következőt: {0}", + "debugType": "A konfiguráció típusa.", + "debugTypeNotRecognised": "Ez a hibakeresési típus nem ismert. Bizonyosodjon meg róla, hogy telepítve és engedélyezve van a megfelelő hibakeresési kiegészítő.", + "node2NotSupported": "A \"node2\" már nem támogatott. Használja helyette a \"node\"-ot, és állítsa a \"protocol\" attribútum értékét \"inspector\"-ra.", + "debugName": "A konfiguráció neve. Az indítási konfiguráció lenyíló menüjében jelenik meg.", + "debugRequest": "A konfiguráció kérési típusa. Lehet \"launch\" vagy \"attach\".", + "debugServer": "Csak hibakeresési kiegészítők fejlesztéséhez: ha a port meg van adva, akkor a VS Code egy szerver módban futó hibakeresési illesztőhöz próbál meg csatlakozni.", + "debugPrelaunchTask": "A hibakeresési folyamat előtt futtatandó feladat.", + "debugWindowsConfiguration": "Windows-specifikus indítási konfigurációs attribútumok.", + "debugOSXConfiguration": "OS X-specifikus indítási konfigurációs attribútumok.", + "debugLinuxConfiguration": "Linux-specifikus indítási konfigurációs attribútumok.", + "deprecatedVariables": "Az 'env.', 'config.' és 'command.' tujdonságok elavultak, használja helyette az 'env:', 'config:' és 'command:' tulajdonságokat." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..14b88d2be3 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "Emmet-parancsok megjelenítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..b9af713f0e --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: Egyensúlyozás (belefé)", + "balanceOutward": "Emmet: Egyensúlyozás (kifelé)" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..b029ca5554 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: Ugrás az előző szerkesztési pontra", + "nextEditPoint": "Emmet: Ugrás a következő szerkesztési pontra" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..8fe0e0eb8a --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: Matematikai kifejezés kiértékelése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..7e30c00175 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: Rövidítés kibontása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..399c5b3555 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: Növelés 0,1-gyel", + "incrementNumberByOne": "Emmet: Növelés 1-gyel", + "incrementNumberByTen": "Emmet: Növelés 10-zel", + "decrementNumberByOneTenth": "Emmet: Csökkentés 0,1-gyel", + "decrementNumberByOne": "Emmet: Csökkentés 1-gyel", + "decrementNumberByTen": "Emmet: Csökkentés 10-zel" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..77d3ef2e4b --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: Ugrás az illeszkedő párra" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..819fc11eb1 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: Sorok összeolvasztása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..52df32abb9 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: CSS-érték tükrözése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..8e8330dbdd --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: Elem eltávolítása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..2dba09b8f8 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: Előző elem kiválasztása", + "selectNextItem": "Emmet: Következő elem kiválasztása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..e52366422e --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: Elem szétbontása/összeolvasztása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..532022f932 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: Megjegyzés be- és kikapcsolása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..a3a00f1001 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: Képméret frissítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..28bbfa384a --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: Elem frissítése", + "enterTag": "Adja meg az elemet", + "tag": "Elem" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..ca64030da6 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: Becsomagolás rövidítéssel", + "enterAbbreviation": "Adja meg a rövidítést", + "abbreviation": "Rövidítés" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..b3c023012f --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Ha engedélyezve van, akkor az Emmet-rövidítések a Tab billentyű lenyomásával oldhatók fel. Nem használható, ha az emmet.useNewemmet beállítás értéke true.", + "emmetPreferences": "Beállítások, melyek módosítják az Emmet műveleteinek és feloldó algoritmusainak viselkedését. Nem használható, ha az emmet.useNewemmet beállítás értéke true.", + "emmetSyntaxProfiles": "Konkrét szintaktika profiljának meghatározása vagy saját profil használata adott szabályokkal.", + "emmetExclude": "Azon nyelvek listája, ahol az Emmet-rövidítések ne legyenek kibontva.", + "emmetExtensionsPath": "Az Emmet-profilokat, -kódtöredékeket és -beállításokat tartalmazó mappa. Csak a profilok vannak használva a kiegészítőben, ha az emmet.useNewEmmet beállítás értéke true.", + "useNewEmmet": "Új Emmet-modulok használata az összes Emmet-funkcióhoz (ami előbb-utóbb helyettesíteni fogja az egyedülálló Emmet-könyvtárat)." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..dae7f932f0 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Külső terminál", + "explorer.openInTerminalKind": "Meghatározza, hogy milyen típusú terminál legyen indítva.", + "terminal.external.windowsExec": "Meghatározza, hogy mely terminál fusson Windowson.", + "terminal.external.osxExec": "Meghatározza, hogy mely terminál fusson OS X-en.", + "terminal.external.linuxExec": "Meghatározza, hogy mely terminál fusson Linuxon.", + "globalConsoleActionWin": "Új parancssor megnyitása", + "globalConsoleActionMacLinux": "Új terminál megnyitása", + "scopedConsoleActionWin": "Megnyitás a parancssorban", + "scopedConsoleActionMacLinux": "Megnyitás a terminálban", + "openFolderInIntegratedTerminal": "Megnyitás a terminálban" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..f8b8a4ddbb --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Külső terminál", + "terminal.external.windowsExec": "Meghatározza, hogy mely terminál fusson Windowson.", + "terminal.external.osxExec": "Meghatározza, hogy mely terminál fusson OS X-en.", + "terminal.external.linuxExec": "Meghatározza, hogy mely terminál fusson Linuxon.", + "globalConsoleActionWin": "Új parancssor megnyitása", + "globalConsoleActionMacLinux": "Új terminál megnyitása", + "scopedConsoleActionWin": "Megnyitás a parancssorban", + "scopedConsoleActionMacLinux": "Megnyitás a terminálban" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/hun/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..4fd0fb58c3 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "VS Code-konzol", + "mac.terminal.script.failed": "A(z) '{0}' parancsfájl a következő hibakóddal lépett ki: {1}", + "mac.terminal.type.not.supported": "A(z) '{0}' nem támogatott", + "press.any.key": "A folytatáshoz nyomjon meg egy billentyűt...", + "linux.term.failed": "A(z) '{0}' a következő hibakóddal lépett ki: {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..059cb4c755 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "Egyedi nézetet szolgáltat", + "vscode.extension.contributes.view.id": "A vscode.workspace.createTreeView-n keresztül létrehozott nézet azonosítására szolgáló egyedi azonosító", + "vscode.extension.contributes.view.label": "A nézet kirajzolásához használt, emberek által olvasható szöveg", + "vscode.extension.contributes.view.icon": "A nézet ikonjának elérési útja", + "vscode.extension.contributes.views": "Egyedi nézeteket szolgáltat", + "showViewlet": "{0} megjelenítése", + "view": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..246671efc8 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "Frissítés" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..353f02e300 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "Nincs {0} azonosítójú TreeExplorerNodeProvider regisztrálva" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/hun/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..35f87f2b2b --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "Fa-alapú kezelőszakasz" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..299a682431 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "Hiba", + "Unknown Dependency": "Ismeretlen függőség:" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..d6f47c3165 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "Kiegészítő neve", + "extension id": "Kiegészítő azonosítója", + "publisher": "Kiadó neve", + "install count": "Telepítések száma", + "rating": "Értékelés", + "license": "Licenc", + "details": "Részletek", + "contributions": "Szolgáltatások", + "changelog": "Változtatási napló", + "dependencies": "Függőségek", + "noReadme": "Leírás nem található.", + "noChangelog": "Változtatási napló nem található.", + "noContributions": "Nincsenek szolgáltatások", + "noDependencies": "Nincsenek függőségek", + "settings": "Beállítások ({0})", + "setting name": "Név", + "description": "Leírás", + "default": "Alapértelmezett érték", + "debuggers": "Hibakeresők ({0})", + "debugger name": "Név", + "debugger type": "Típus", + "views": "Nézetek ({0})", + "view id": "Azonosító", + "view name": "Név", + "view location": "Hol?", + "themes": "Témák ({0})", + "JSON Validation": "JSON-validációk ({0})", + "commands": "Parancsok ({0})", + "command name": "Név", + "keyboard shortcuts": "Billentyűparancsok", + "menuContexts": "Helyi menük", + "languages": "Nyelvek ({0})", + "language id": "Azonosító", + "language name": "Név", + "file extensions": "Fájlkiterjesztések", + "grammar": "Nyelvtan", + "snippets": "Kódtöredékek" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..8a99d190ea --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "Telepítés", + "installing": "Telepítés...", + "uninstallAction": "Eltávolítás", + "Uninstalling": "Eltávolítás...", + "updateAction": "Frissítés", + "updateTo": "Frissítés ({0})", + "enableForWorkspaceAction.label": "Engedélyezés a munkaterületen", + "enableAlwaysAction.label": "Engedélyezés mindig", + "disableForWorkspaceAction.label": "Letiltás a munkaterületen", + "disableAlwaysAction.label": "Letiltás mindig", + "ManageExtensionAction.uninstallingTooltip": "Eltávolítás", + "enableForWorkspaceAction": "Munkaterület", + "enableGloballyAction": "Mindig", + "enableAction": "Engedélyezés", + "disableForWorkspaceAction": "Munkaterület", + "disableGloballyAction": "Mindig", + "disableAction": "Letiltás", + "checkForUpdates": "Frissítések keresése", + "enableAutoUpdate": "Kiegészítők automatikus frissítésének engedélyezése", + "disableAutoUpdate": "Kiegészítők automatikus frissítésének letiltása", + "updateAll": "Összes kiegészítő frissítése", + "reloadAction": "Újratöltés", + "postUpdateTooltip": "Újratöltés a frissítéshez", + "postUpdateMessage": "Újratölti az ablakot a frissített kiegészítő ('{0}') aktiválásához?", + "postEnableTooltip": "Újratöltés az aktiváláshoz", + "postEnableMessage": "Újratölti az ablakot a kiegészítő ('{0}') aktiválásához?", + "postDisableTooltip": "Újratöltés a kikapcsoláshoz", + "postDisableMessage": "Újratölti az ablakot a kiegészítő ('{0}') kikapcsolásához?", + "postUninstallTooltip": "Újratöltés a kikapcsoláshoz", + "postUninstallMessage": "Újratölti az ablakot az eltávolított kiegészítő ('{0}') kikapcsolásához?", + "reload": "Ablak új&&ratöltése", + "toggleExtensionsViewlet": "Kiegészítők megjelenítése", + "installExtensions": "Kiegészítők telepítése", + "showEnabledExtensions": "Engedélyezett kiegészítők megjelenítése", + "showInstalledExtensions": "Telepített kiegészítők megjelenítése", + "showDisabledExtensions": "Letiltott kiegészítők megjelenítése", + "clearExtensionsInput": "Kiegészítők beviteli mező tartalmának törlése", + "showOutdatedExtensions": "Elavult kiegészítők megjelenítése", + "showPopularExtensions": "Népszerű kiegészítők megjelenítése", + "showRecommendedExtensions": "Ajánlott kiegészítők megjelenítése", + "showWorkspaceRecommendedExtensions": "Munkaterülethez ajánlott kiegészítők megjelenítése", + "showRecommendedKeymapExtensions": "Ajánlott billentyűkonfigurációk megjelenítése", + "showRecommendedKeymapExtensionsShort": "Billentyűkonfigurációk", + "showLanguageExtensions": "Nyelvi kiegészítők megjelenítése", + "showLanguageExtensionsShort": "Nyelvi kiegészítők", + "showAzureExtensions": "Azure-kiegészítők megjelenítése", + "showAzureExtensionsShort": "Azure-kiegészítők", + "configureWorkspaceRecommendedExtensions": "Ajánlott kiegészítők konfigurálása (munkaterületre vonatkozóan)", + "ConfigureWorkspaceRecommendations.noWorkspace": "Az ajánlatok csak egy munkaterület mappájára vonatkozóan érhetők el.", + "OpenExtensionsFile.failed": "Nem sikerült létrehozni az 'extensions.json' fájlt a '.vscode' mappánan ({0}).", + "builtin": "Beépített", + "disableAll": "Összes telepített kiegészítő letiltása", + "disableAllWorkspace": "Összes telepített kiegészítő letiltása a munkaterületre vonatkozóan", + "enableAll": "Összes telepített kiegészítő engedélyezése", + "enableAllWorkspace": "Összes telepített kiegészítő engedélyezése a munkaterületre vonatkozóan", + "extensionButtonProminentBackground": "A kiegészítőkhöz tartozó kiemelt műveletgombok (pl. a Telepítés gomb) háttérszíne.", + "extensionButtonProminentForeground": "A kiegészítőkhöz tartozó kiemelt műveletgombok (pl. a Telepítés gomb) előtérszíne.", + "extensionButtonProminentHoverBackground": "A kiegészítőkhöz tartozó kiemelt műveletgombok (pl. a Telepítés gomb) háttérszíne, ha az egér fölötte van." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..3baf3819cc --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "Nyomjon Entert a kiegészítők kezeléséhez.", + "searchFor": "Nyomja meg az Enter gombot a(z) '{0}' kiegészítő kereséséhez a piactéren.", + "noExtensionsToInstall": "Adja meg a kiegészítő nevét" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..a044071f0b --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "Kiegészítők", + "app.extensions.json.recommendations": "Ajánlott kiegészítők listája. A kiegészítők azonosítója mindig '${publisher}.${name}' formában van. Példa: 'vscode.csharp'.", + "app.extension.identifier.errorMessage": "Az elvárt formátum: '${publisher}.${name}'. Példa: 'vscode.csharp'." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..b2265349dc --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "Kiegészítő: {0}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..33860e57c8 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "Ehhez a fájltípushoz a(z) '{0}' kiegészítő ajánlott", + "showRecommendations": "Ajánlatok megjelenítése", + "neverShowAgain": "Ne jelenítse meg újra", + "close": "Bezárás", + "workspaceRecommended": "A munkaterülethez vannak javasolt kiegészítők", + "ignoreExtensionRecommendations": "Figyelmen kívül akarja hagyni az összes javasolt kiegészítőt?", + "ignoreAll": "Igen, az összes figyelmen kívül hagyása", + "no": "Nem", + "cancel": "Mégse" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..585acb40e2 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "Kiegészítők kezelése", + "galleryExtensionsCommands": "Kiegészítők telepítése a galériából", + "extension": "Kiegészítő", + "extensions": "Kiegészítők", + "view": "Nézet", + "extensionsConfigurationTitle": "Kiegészítők", + "extensionsAutoUpdate": "Kiegészítők automatikus frissítése", + "extensionsIgnoreRecommendations": "Ajánlott kiegészítők figyelmen kívül hagyása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..f133e9e99a --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "Kiegészítők mappájának megnyitása", + "installVSIX": "Telepítés VSIX-ből...", + "InstallVSIXAction.success": "A kiegészítő sikeresen fel lett telepítve. Indítsa újra az engedélyezéshez.", + "InstallVSIXAction.reloadNow": "Újratöltés most" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..cf953939fb --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Letiltja a többi billentyűkonfigurációt ({0}) a billentyűparancsok közötti konfliktusok megelőzése érdekében?", + "yes": "Igen", + "no": "Nem", + "betterMergeDisabled": "A Better Merge kiegészítő most már be van építve. A telepített kiegészítő le lett tiltva és eltávolítható.", + "uninstall": "Eltávolítás", + "later": "Később" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..6f1026e6bf --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "Piactér", + "installedExtensions": "Telepítve", + "searchInstalledExtensions": "Telepítve", + "recommendedExtensions": "Ajánlott", + "searchExtensions": "Kiegészítők keresése a piactéren", + "sort by installs": "Rendezés a telepítések száma szerint", + "sort by rating": "Rendezés értékelés szerint", + "sort by name": "Rendezés név szerint", + "suggestProxyError": "A piactér 'ECONNREFUSED' hibával tért vissza. Ellenőrizze a 'http.proxy' beállítást!", + "extensions": "Kiegészítők", + "outdatedExtensions": "{0} elavult kiegészítő" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..7b00aa6d1d --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Kiegészítők", + "no extensions found": "Kiegészítő nem található.", + "suggestProxyError": "A piactér 'ECONNREFUSED' hibával tért vissza. Ellenőrizze a 'http.proxy' beállítást!" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..66f30b24ee --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "A(z) '{0}' engedélyezésével annak függőségei is engedélyezve lesznek. Szeretné folytatni?", + "enable": "Igen", + "doNotEnable": "Nem", + "disableDependeciesConfirmation": "Csak a(z) '{0}' kiegészítőt szeretné letiltani vagy annak függőségeit is?", + "disableOnly": "Csak ezt", + "disableAll": "Az összeset", + "cancel": "Mégse", + "singleDependentError": "Nem sikerült letiltani a(z) '{0}' kiegészítőt: a(z) '{1}' kiegészítő függ tőle.", + "twoDependentsError": "Nem sikerült letiltani a(z) '{0}' kiegészítőt: a(z) '{1}' és '{2}' kiegészítők függnek tőle.", + "multipleDependentsError": "Nem sikerült letiltani a(z) '{0}' kiegészítőt: a(z) '{1}', '{2}' és más kiegészítők függnek tőle.", + "installConfirmation": "Szeretné telepíteni a(z) '{0}' kiegészítőt?", + "install": "Telepítés" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/hun/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..92d9fcacf5 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "Visszajelzés tweetelése", + "label.sendASmile": "Küldje el nekünk egy tweetben a visszajelzését!", + "patchedVersion1": "A telepítés hibás.", + "patchedVersion2": "Az alábbiakat adja meg, ha hibát akar beküldeni.", + "sentiment": "Milyennek találja az alkalmazást?", + "smileCaption": "Elégedett", + "frownCaption": "Elégedetlen vagyok vele", + "other ways to contact us": "Más értesítési módok", + "submit a bug": "Hibajelentés küldése", + "request a missing feature": "Hiányzó funkció kérése", + "tell us why?": "Mondja el, hogy miért", + "commentsHeader": "Visszajelzés", + "tweet": "Tweer", + "character left": "karakter maradt", + "characters left": "karakter maradt", + "feedbackSending": "Küldés", + "feedbackSent": "Köszönjük!", + "feedbackSendingError": "Újrapróbálkozás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..68288ad833 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "Bináris megjelenítő" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..5a7cad6d05 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "Szövegfájlszerkesztő", + "createFile": "Fájl létrehozása", + "fileEditorWithInputAriaLabel": "{0}. Szövegfájlszerkesztő.", + "fileEditorAriaLabel": "Szövegfájlszerkesztő" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..aaef03dbad --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "Mappák" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..4dfaad1136 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "Fájlok", + "revealInSideBar": "Megjelenítés az oldalsávon", + "acceptLocalChanges": "A lemezen lévő tartalom felülírása a saját módosításokkal", + "revertLocalChanges": "Saját módosítások elvetése és a lemezen lévő tartalom visszaállítása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..35ea620790 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "Újra", + "rename": "Átnevezés", + "newFile": "Új fájl", + "newFolder": "Új mappa", + "openFolderFirst": "Mappák vagy fájlok létrehozásához először nyisson meg egy mappát!", + "newUntitledFile": "Új, névtelen fájl", + "createNewFile": "Új fájl", + "createNewFolder": "Új mappa", + "deleteButtonLabelRecycleBin": "Áthelyezés a lo&&mtárba", + "deleteButtonLabelTrash": "Áthelyezés a &&kukába", + "deleteButtonLabel": "&&Törlés", + "dirtyMessageFolderOneDelete": "Törölni készül egy olyan mappát, melyben egy nem mentett változtatásokat tartalmazó fájl van. Folytatja?", + "dirtyMessageFolderDelete": "Törölni készül egy olyan mappát, melyben {0} nem mentett változtatásokat tartalmazó fájl van. Folytatja?", + "dirtyMessageFileDelete": "Törölni készül egy olyan fájlt, amely nem mentett változtatásokat tartalmaz. Folytatja?", + "dirtyWarning": "A módosítások elvesznek, ha nem menti őket.", + "confirmMoveTrashMessageFolder": "Törli a(z) '{0}' nevű mappát és a teljes tartalmát?", + "confirmMoveTrashMessageFile": "Törli a(z) '{0}' nevű fájlt?", + "undoBin": "Helyreállíthatja a lomtárból.", + "undoTrash": "Helyreállíthatja a kukából.", + "confirmDeleteMessageFolder": "Törli a(z) {0} mappát és a teljes tartalmát?", + "confirmDeleteMessageFile": "Véglegesen törli a következőt: {0}?", + "irreversible": "A művelet nem vonható vissza!", + "permDelete": "Végleges törlés", + "delete": "Törlés", + "importFiles": "Fájlok importálása", + "confirmOverwrite": "A célmappában már van ilyen nevű mappa vagy fájl. Le szeretné cserélni?", + "replaceButtonLabel": "&&Csere", + "copyFile": "Másolás", + "pasteFile": "Beillesztés", + "duplicateFile": "Duplikálás", + "openToSide": "Megnyitás oldalt", + "compareSource": "Kijelölés összehasonlításhoz", + "globalCompareFile": "Aktív fájl összehasonlítása...", + "pickHistory": "Válasszon egy korábban megnyitott fájlt az összehasonlításhoz", + "unableToFileToCompare": "A kiválasztott fájl nem hasonlítható össze a következővel: '{0}'.", + "openFileToCompare": "Fájlok összehasonlításához elősször nyisson meg egy fájlt.", + "compareWith": "'{0}' összehasonlítása a következővel: '{1}'", + "compareFiles": "Fájlok összehasonlítása", + "refresh": "Frissítés", + "save": "Mentés", + "saveAs": "Mentés másként...", + "saveAll": "Összes mentése", + "saveAllInGroup": "Összes mentése a csoportban", + "saveFiles": "Módosított fájlok mentése", + "revert": "Fájl visszaállítása", + "focusOpenEditors": "Váltás a megnyitott szerkesztőablakok nézetre", + "focusFilesExplorer": "Váltás a fájlkezelőre", + "showInExplorer": "Aktív fájl megjelenítése az oldalsávon", + "openFileToShow": "Fájl fájlkezelőben történő megjelenítéséhez először nyisson meg egy fájlt", + "collapseExplorerFolders": "Mappák összecsukása a fájlkezelőben", + "refreshExplorer": "Fájlkezelő frissítése", + "openFile": "Fájl megnyitása...", + "openFileInNewWindow": "Aktív fájl megnyitása új ablakban", + "openFileToShowInNewWindow": "Fájl új ablakban történő megnyitásához először nyisson meg egy fájlt", + "revealInWindows": "Megjelenítés a fájlkezelőben", + "revealInMac": "Megjelenítés a Finderben", + "openContainer": "Tartalmazó mappa megnyitása", + "revealActiveFileInWindows": "Aktív fájl megjelenítése a Windows Intézőben", + "revealActiveFileInMac": "Aktív fájl megjelenítése a Finderben", + "openActiveFileContainer": "Aktív fájlt tartalmazó mappa megnyitása", + "copyPath": "Elérési út másolása", + "copyPathOfActive": "Aktív fájl elérési útjának másolása", + "emptyFileNameError": "Meg kell adni egy fájl vagy mappa nevét.", + "fileNameExistsError": "Már létezik **{0}** nevű fájl vagy mappa ezen a helyszínen. Adjon meg egy másik nevet!", + "invalidFileNameError": "A(z) **{0}** név nem érvényes fájl- vagy mappanév. Adjon meg egy másik nevet!", + "filePathTooLongError": "A(z) **{0}** név egy olyan elérési utat eredményez, ami túl hosszú. Adjon meg egy másik nevet!", + "compareWithSaved": "Aktív fájl összehasonlítása a mentett változattal", + "modifiedLabel": "{0} (a lemezen) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..5f5ce08d4e --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "Fájlok elérési útjának másolásához elősször nyisson meg egy fájlt", + "openFileToReveal": "Fájlok felfedéséhez elősször nyisson meg egy fájlt" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..53e1347443 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "Fájlkezelő megjelenítése", + "explore": "Fájlkezelő", + "view": "Nézet", + "textFileEditor": "Szövegfájlszerkesztő", + "binaryFileEditor": "Bináris fájlszerkesztő", + "filesConfigurationTitle": "Fájlok", + "exclude": "Globális minták konfigurálása fájlok és mappák kiszűréséhez.", + "files.exclude.boolean": "A globális minta, amire illesztve lesznek a fájlok elérési útjai. A minta engedélyezéséhez vagy letiltásához állítsa igaz vagy hamis értékre.", + "files.exclude.when": "További ellenőrzés elvégzése egy egyező fájl testvérein. Az egyező fájlnévhez használja a $(basename) változót.", + "associations": "Rendeljen nyelveket a fájlokhoz (pl: \"*.kiterjesztés\": \"html\"). Ezek a hozzárendelések elsőbbséget élveznek a telepített nyelvek által definiált alapértelmezett beállításokkal szemben.", + "encoding": "A fájlok olvasásakor és írásakor használt alapértelmezett karakterkódolás.", + "autoGuessEncoding": "Ha engedélyezve van, az alkalmazás automatikusan megpróbálja kitalálni a fájlok kódolását megnyitáskor", + "eol": "Az alapértelmezett sorvégjel. LF-hez használjon \\n-t, CRLF-hez pedig \\r\\n-t.", + "trimTrailingWhitespace": "Ha engedélyezve van, a fájl mentésekor levágja a sor végén található szóközöket.", + "insertFinalNewline": "Ha engedélyezve van, mentéskor beszúr egy záró újsort a fájl végére.", + "files.autoSave.off": "A módosított fájlok soha nincsenek automatikusan mentve.", + "files.autoSave.afterDelay": "A módosított fájlok automatikusan mentésre kerülnek a 'files.autoSaveDelay' beállításban meghatározott időközönként.", + "files.autoSave.onFocusChange": "A módosított fájlok automatikusan mentésre kerülnek, ha a szerkesztőablak elveszíti a fókuszt.", + "files.autoSave.onWindowChange": "A módosított fájlok automatikusan mentésre kerülnek, ha az ablak elveszíti a fókuszt.", + "autoSave": "Meghatározza a módosított fájlok automatikus mentési stratégiáját. Elfogadott értékek: '{0}', '{1}', '{2}' (a szerkesztőablak elveszíti a fókuszt), '{3}' (az ablak elveszíti a fókuszt). Ha az értéke '{4}', megadható a késleltetés a 'files.autoSaveDelay' beállításban.", + "autoSaveDelay": "Meghatározza ezredmásodpercben a késleltetést, ami után a módosított fájlok automatikusan mentésre kerülnek. Csak akkor van hatása, ha a 'files.autoSave' beállítás értéke '{0}'.", + "watcherExclude": "Globális minta, ami meghatározza azoknak a fájloknak a listáját, amelyek ki vannak szűrve a figyelésből. A mintáknak abszolút elérési utakra kell illeszkedniük (azaz előtagként adja hozzá a **-t vagy a teljes elérési utat a megfelelő illeszkedéshez). A beállítás módosítása újraindítást igényel. Ha úgy észleli, hogy a Code túl sok processzort használ indításnál, ki tudja szűrni a nagy mappákat a kezdeti terhelés csökkentés érdekében.", + "hotExit.off": "Gyors kilépés letiltása.", + "hotExit.onExit": "Gyors kilépésről akkor van szó, ha az utolsó ablakot bezárják Windowson és Linuxon, vagy ha a workbench.action.quit parancs van futtatva (a parancskatalógusból, billentyűkombinációval vagy a menüből). Az összes biztonsági mentéssel rendelkező ablak helyre lesz állítva a következő indítás során.", + "hotExit.onExitAndWindowClose": "Gyors kilépésről akkor van szó, ha az utolsó ablakot bezárják Windowson és Linuxon, ha a workbench.action.quit parancs van futtatva (a parancskatalógusból, billentyűkombinációval vagy a menüből), vagy bármely ablak, amelyben mappa van megnyitva, függetlenül attól, hogy az az utolsó ablak-e. Az összes megnyitott, mappa nélküli ablak helyre lesz állítva a következő indítás során. A megnyitott mappát tartalmazó ablakok helyreállításához állítsa a \"window.restoreWindows\" értékét \"all\"-ra.", + "hotExit": "Meghatározza, hogy a nem mentett fájlokra emlékezzen-e az alkalmazás a munkamenetek között, így ki lehet hagyni a mentéssel kapcsolatos felugró ablakokat kilépésnél.", + "useExperimentalFileWatcher": "Új, kísérleti fájlfigyelő használata.", + "defaultLanguage": "Az új fájlokhoz alapértelmezetten hozzárendelt nyelv.", + "editorConfigurationTitle": "Szerkesztőablak", + "formatOnSave": "Fájlok formázása mentéskor. Az adott nyelvhez rendelkezésre kell állni formázónak, nem lehet beállítva automatikus mentés, és a szerkesztő nem állhat éppen lefelé.", + "explorerConfigurationTitle": "Fájlkezelő", + "openEditorsVisible": "A megnyitott szerkesztőablakok panelen megjelenített szerkesztőablakok száma. Állítsa 0-ra, ha el szeretné rejteni a panelt.", + "dynamicHeight": "Meghatározza, hogy a megnyitott szerkesztőablakok szakasz magassága automatikusan illeszkedjen a megnyitott elemek számához vagy sem.", + "autoReveal": "Meghatározza, hogy a fájlkezelőben automatikusan fel legyenek fedve és ki legyenek jelölve a fájlok, amikor megnyitják őket.", + "enableDragAndDrop": "Meghatározza, hogy a fájlkezelőben áthelyezhetők-e a fájlok és mappák húzással.", + "sortOrder.default": "A fájlok és mappák név szerint vannak rendezve, ABC-sorrendben. A mappák a fájlok előtt vannak listázva.", + "sortOrder.mixed": "A fájlok és mappák név szerint vannak rendezve, ABC-sorrendben. A fájlok és a mappák közösen vannak rendezve.", + "sortOrder.filesFirst": "A fájlok és mappák név szerint vannak rendezve, ABC-sorrendben. A fájlok a mappák előtt vannak listázva.", + "sortOrder.type": "A fájlok és mappák a kiterjesztésük szerint vannak rendezve, ABC-sorrendben. A mappák a fájlok előtt vannak listázva.", + "sortOrder.modified": "A fájlok és mappák a legutolsó módosítás dátuma szerint vannak rendezve, csökkenő sorrendben. A mappák a fájlok előtt vannak listázva.", + "sortOrder": "Meghatározza a fájlok és mappák rendezési módját a fájlkezelőben. Az alapértelmezett rendezésen túl beállítható 'mixed' (a fájlok és mappák közösen vannak rendezve), 'type' (rendezés fájltípus szerint), 'modified' (rendezés utolsó módosítási dátum szerint) vagy 'filesFirst' (fájlok a mappák elé vannak rendezve) is." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..fa8bccf0dd --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "Elvetés", + "overwrite": "Felülírás", + "retry": "Újrapróbálkozás", + "readonlySaveError": "Nem sikerült menteni a(z) '{0}' fájlt: a fájl írásvédett. Válassza a 'Felülírás' lehetőséget a védelem eltávolításához.", + "genericSaveError": "Hiba a(z) '{0}' mentése közben: {1}", + "staleSaveError": "Nem sikerült menteni a(z) '{0}' fájlt: a lemezen lévő tartalom újabb. Kattintson az **Összehasonlítás*** gombra a helyi és a lemezen lévő változat összehasonlításához.", + "compareChanges": "Összehasonlítás", + "saveConflictDiffLabel": "{0} (a lemezen) ↔ {1} ({2}) – Mentési konfliktus feloldása", + "userGuide": "Használja a jobbra lévő szerkesztői eszköztáron található műveleteket a saját módosítások **visszavonására** vagy **írja felül** a lemezen lévő tartalmat a változtatásokkal" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..9fbdf8ad94 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Nincs mappa megnyitva", + "explorerSection": "Fájlkezelő szakasz", + "noWorkspaceHelp": "Még nem nyitott meg mappát", + "openFolder": "Mappa megnyitása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..eb8e04b632 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "Fájlkezelő szakasz", + "treeAriaLabel": "Fájlkezelő" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..bd80f50970 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "Adja meg a fájl nevét. Nyomjon 'Enter'-t a megerősítéshez vagy 'Escape'-et a megszakításhoz.", + "filesExplorerViewerAriaLabel": "{0}, Fájlkezelő", + "dropFolders": "Szeretné hozzáadni a mappákat a munkaterülethez?", + "dropFolder": "Szeretné hozzáadni a mappát a munkaterülethez?", + "addFolders": "Mappák hozzá&&adása", + "addFolder": "Mappa hozzá&&adása", + "confirmOverwriteMessage": "A célmappában már létezik '{0}' nevű elem. Le szeretné cserélni?", + "irreversible": "A művelet nem vonható vissza!", + "replaceButtonLabel": "&&Csere" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..312805a384 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "Megnyitott szerkesztőablakok", + "openEditosrSection": "Megnyitott szerkesztőablakok szakasz", + "treeAriaLabel": "Megnyitott szerkesztőablakok: az aktív fájlok listája", + "dirtyCounter": "{0} nincs mentve" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..12c4a38f82 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}, Szerkesztőcsoport", + "openEditorAriaLabel": "{0}, megnyitott szerkesztőablak", + "saveAll": "Összes mentése", + "closeAllUnmodified": "Nem módosultak bezárása", + "closeAll": "Összes bezárása", + "compareWithSaved": "Összehasonlítás a mentett változattal", + "close": "Bezárás", + "closeOthers": "Többi bezárása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/hun/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..42f507210d --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0} nem mentett fájl" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/hun/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..b6730e516e --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (törölve a lemezről)" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..dbb1dc0853 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "HTML-előnézet", + "devtools.webview": "Fejlesztői: Webview-eszközök" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/hun/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..a8c5af6f8d --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "Érvénytelen bemenet a szerkesztőablakból." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/hun/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..b97939ae21 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "Fejlesztői: Webview-eszközök" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/hun/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..8357756c06 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "Nézet", + "problems.view.show.label": "Problémák megjelenítése", + "problems.panel.configuration.title": "Problémák-nézet", + "problems.panel.configuration.autoreveal": "Meghatározza, hogy a problémák nézet automatikusan felfedje-e a fájlokat, amikor megnyitja őket.", + "markers.panel.title.problems": "Problémák", + "markers.panel.aria.label.problems.tree": "Problémák fájlonként csoportosítva", + "markers.panel.no.problems.build": "A munkaterületen eddig egyetlen hiba sem lett érzékelve.", + "markers.panel.no.problems.filters": "A megadott szűrőfeltételnek egyetlen elem sem felel meg.", + "markers.panel.action.filter": "Problémák szűrése", + "markers.panel.filter.placeholder": "Szűrés típus vagy szöveg alapján", + "markers.panel.filter.errors": "hibák", + "markers.panel.filter.warnings": "figyelmeztetések", + "markers.panel.filter.infos": "információk", + "markers.panel.single.error.label": "1 hiba", + "markers.panel.multiple.errors.label": "{0} hiba", + "markers.panel.single.warning.label": "1 figyelmeztetés", + "markers.panel.multiple.warnings.label": "{0} figyelmeztetés", + "markers.panel.single.info.label": "1 információ", + "markers.panel.multiple.infos.label": "{0} információ", + "markers.panel.single.unknown.label": "1 ismeretlen", + "markers.panel.multiple.unknowns.label": "{0} ismeretlen", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "{0} {1} problémával", + "problems.tree.aria.label.error.marker": "{0} által generált hiba: {1}, sor: {2}, oszlop: {3}", + "problems.tree.aria.label.error.marker.nosource": "Hiba: {0}, sor: {1}, oszlop: {2}", + "problems.tree.aria.label.warning.marker": "{0} által generált figyelmeztetés: {1}, sor: {2}, oszlop: {3}", + "problems.tree.aria.label.warning.marker.nosource": "Figyelmeztetés: {0}, sor: {1}, oszlop: {2}", + "problems.tree.aria.label.info.marker": "{0} által generált információ: {1}, sor: {2}, oszlop: {3}", + "problems.tree.aria.label.info.marker.nosource": "Információ: {0}, sor: {1}, oszlop: {2}", + "problems.tree.aria.label.marker": "{0} által generált probléma: {1}, sor: {2}, oszlop: {3}", + "problems.tree.aria.label.marker.nosource": "Probléma: {0}, sor: {1}, oszlop: {2}", + "errors.warnings.show.label": "Hibák és figyelmezetések megjelenítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/hun/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..7d076dc050 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "Másolás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..3a011338d0 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Lenne kedve egy gyors elégedettségi felméréshez?", + "takeSurvey": "Felmérés kitöltése", + "remindLater": "Emlékeztessen később", + "neverAgain": "Ne jelenjen meg újra" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..04cb923786 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Kimenet", + "viewCategory": "Nézet", + "clearOutput.label": "Kimenet törlése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/hun/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..5d6a5247e0 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "Kimenet be- és kikapcsolása", + "clearOutput": "Kimenet törlése", + "toggleOutputScrollLock": "Kimenet görgetési zárának be- és kikapcsolása", + "switchToOutput.label": "Váltás a kimenetre" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/hun/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..6c30735aef --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0}, kimenetpanel", + "outputPanelAriaLabel": "Kimenetpanel" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/hun/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..d5cb46a75b --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Kimenet", + "channel": "a következőhöz: '{0}'" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..d6a3fa15ca --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "Lassú indulás érzékelve", + "slow.detail": "Sajnáljuk, hogy lassú volt az alkalmazás indulása. Indítsa újra a(z) '{0}' alkalmazást bekapcsolt profilozással, ossza meg a profilt velünk, és keményen fogunk dolgozni azon, hogy az indulás ismét gyors legyen.", + "prof.message": "Profil sikeresen elkészítve.", + "prof.detail": "Készítsen egy hibajelentést, és manuálisan csatolja a következő fájlokat:\n{0}", + "prof.restartAndFileIssue": "Hibajelentés létrehozása és újraindítás", + "prof.restart": "Újraindítás", + "prof.thanks": "Köszönjük a segítséget!", + "prof.detail.restart": "Egy utolsó újraindítás szükséges a(z) '{0}' használatához. Ismételten köszönjük a közreműködését!" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..26f72b2045 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "Üsse le a kívánt billentyűkombinációt, majd nyomjon ENTER-t. ESCAPE a megszakításhoz.", + "defineKeybinding.chordsTo": "kombináció a következőhöz:" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..3c7b456fee --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "Billentyűparancsok", + "SearchKeybindings.AriaLabel": "Billentyűparancsok keresése", + "SearchKeybindings.Placeholder": "Billentyűparancsok keresése", + "sortByPrecedene": "Rendezés precedencia szerint", + "header-message": "Haladó beállításokhoz nyissa meg és szerkessze a következőt:", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "Billentyűparancsok", + "changeLabel": "Billentyűparancs módosítása", + "addLabel": "Billentyűparancs hozzáadása", + "removeLabel": "Billentyűparancs eltávolítása", + "resetLabel": "Billentyűparancs visszaállítása", + "showConflictsLabel": "Konfliktusok megjelenítése", + "copyLabel": "Másolás", + "error": "'{0}' hiba a billentyűparancsok szerkesztése közben. Nyissa meg a 'keybindings.json' fájlt, és ellenőrizze!", + "command": "Parancs", + "keybinding": "Billentyűparancs", + "source": "Forrás", + "when": "Mikor?", + "editKeybindingLabelWithKey": "{0} billentyűparancs módosítása", + "editKeybindingLabel": "Billentyűparancs módosítása", + "addKeybindingLabelWithKey": "{0} billentyűparancs hozzáadása", + "addKeybindingLabel": "Billentyűparancs hozzáadása", + "commandAriaLabel": "Parancs: {0}.", + "keybindingAriaLabel": "Billentyűparancs: {0}.", + "noKeybinding": "Nincs billentyűparancs hozzárendelve.", + "sourceAriaLabel": "Forrás: {0}.", + "whenAriaLabel": "Mikor: {0}.", + "noWhen": "Nincs 'mikor'-kontextus." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..8016a5ccff --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "Billentyűparancs megadása", + "defineKeybinding.kbLayoutErrorMessage": "A jelenlegi billentyűkiosztással nem használható ez a billentyűkombináció.", + "defineKeybinding.kbLayoutLocalAndUSMessage": "**{0}** a jelenlegi billentyűkiosztással (**{1}** az alapértelmezett amerikaival.", + "defineKeybinding.kbLayoutLocalMessage": "**{0}** a jelenlegi billentyűkiosztással." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..f3ed1a172a --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "Alapértelmezett beállításszerkesztő", + "keybindingsEditor": "Billentyűparancs-szerkesztő", + "preferences": "Beállítások" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..30a0b6df8f --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "Felhasználói beállítások megnyitása", + "openGlobalKeybindings": "Billentyűparancsok megnyitása", + "openGlobalKeybindingsFile": "Billentyűparancsfájl megnyitása", + "openWorkspaceSettings": "Munkaterület beállításainak megnyitása", + "openFolderSettings": "Mappa beállításainak megnyitása", + "pickFolder": "Válasszon mappát!", + "configureLanguageBasedSettings": "Nyelvspecifikus beállítások konfigurálása...", + "languageDescriptionConfigured": "(({0})", + "pickLanguage": "Nyelv kiválasztása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..c068cc2e51 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "Alapértelmezett beállítások", + "SearchSettingsWidget.AriaLabel": "Beállítások keresése", + "SearchSettingsWidget.Placeholder": "Beállítások keresése", + "totalSettingsMessage": "Összesen {0} beállítás", + "noSettingsFound": "Nincs eredmény", + "oneSettingFound": "1 illeszkedő beállítás", + "settingsFound": "{0} illeszkedő beállítás", + "fileEditorWithInputAriaLabel": "{0}. Szövegfájlszerkesztő.", + "fileEditorAriaLabel": "Szövegfájlszerkesztő", + "preferencesAriaLabel": "Az alapértelmezett beállítások. Írásvédett szerkesztőablak." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..51ee968904 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "Az ebben a fájlban elhelyezett beállítások felülírják az alapértelmezett beállításokat.", + "errorInvalidConfiguration": "Nem sikerült írni a beállításokba. Javítsa a fájlban található hibákat/figyelmeztetéseket, majd próbálja újra!", + "emptyWorkspaceSettingsHeader": "Az ebben a fájlban elhelyezett beállítások felülírják a felhasználói beállításokat.", + "emptyFolderSettingsHeader": "Az ebben a fájlban elhelyezett beállítások felülírják a munkaterületre vonatkozó beállításokat.", + "defaultFolderSettingsTitle": "Alapértelmezett mappabeállítások", + "defaultSettingsTitle": "Alapértelmezett beállítások", + "noSettingsFound": "Beállítás nem található.", + "editTtile": "Szerkesztés", + "replaceDefaultValue": "Csere a beállításokban", + "copyDefaultValue": "Másolás a beállításokba", + "unsupportedPHPExecutablePathSetting": "Ez a beállítás csak a felhasználói beállításokban szerepelhet. A PHP munkaterülethez történő konfigurálásához nyisson meg egy PHP-fájlt, majd kattintson a PHP-elérési útra az állapotsoron!", + "unsupportedWorkspaceSetting": "Ez a beállítás csak a felhasználói beállításokban szerepelhet.", + "unsupportedWorkbenchSetting": "Ez a beállítás jelenleg nem alkalmazható. Akkor van használatban, ha közvetlenül nyitja meg ezt a mappát." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..39658961b2 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "Munkaterületspecifikus beállítások létrehozásához nyisson meg egy mappát", + "emptyKeybindingsHeader": "Az ebben a fájlban elhelyezett billentyűparancsok felülírják az alapértelmezett beállításokat", + "defaultKeybindings": "Alapértelmezett billentyűparancsok", + "folderSettingsName": "{0} (mappabeállítások)", + "fail.createSettings": "Nem sikerült a(z) '{0}' létrehozás ({1})." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..c44afcea8c --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "Mappabeállítások" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..40537a2dac --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "Alapértelmezett", + "user": "Felhasználói", + "meta": "meta", + "option": "beállítás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..d5bf9e0ef3 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "Felhasználói beállítások", + "workspaceSettingsTarget": "Munkaterület-beállítások" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..ff5b58fb87 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "Gyakran használt", + "noSettings": "Nincsenek beállítások.", + "defaultKeybindingsHeader": "A billentyűparancsok fájlban elhelyezett billentyűparancsok felülírják az alapértelmezett beállításokat" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..fde2fbfde7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Minden parancs megjelenítése", + "clearCommandHistory": "Parancselőzmények törlése", + "showCommands.label": "Parancskatalógus...", + "entryAriaLabelWithKey": "{0}, {1}, parancsok", + "entryAriaLabel": "{0}, parancs", + "canNotRun": "Innen nem futtatható a(z) {0} parancs.", + "actionNotEnabled": "Ebben a kontextusban nem engedélyezett a(z) {0} parancs futtatása.", + "recentlyUsed": "legutóbb használt", + "morecCommands": "további parancsok", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "Parancs nem található" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..33d4fe0600 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "Sor megkeresése...", + "gotoLineLabelEmptyWithLimit": "A keresett sor 1 és {0} közötti sorszáma", + "gotoLineLabelEmpty": "A keresett sor száma", + "gotoLineColumnLabel": "Ugrás a(z) {0}. sor {1}. oszlopára", + "gotoLineLabel": "Sor megkeresése {0}", + "gotoLineHandlerAriaLabel": "Adja meg a keresett sor számát!", + "cannotRunGotoLine": "Sor megkereséséhez nyisson meg egy fájlt" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..61fa22b18c --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "Ugrás szimbólumhoz egy fájlban...", + "symbols": "szimbólumok ({0})", + "method": "metódusok ({0})", + "function": "függvények ({0})", + "_constructor": "konstruktorok ({0})", + "variable": "változók ({0})", + "class": "osztályok ({0})", + "interface": "interfészek ({0})", + "namespace": "névterek ({0})", + "package": "csomagok ({0})", + "modules": "modulok ({0})", + "property": "tulajdonságok ({0})", + "enum": "enumerátorok ({0n)", + "string": "karakterláncok ({0})", + "rule": "szabályok ({0})", + "file": "fájlok ({0})", + "array": "tömbök ({0})", + "number": "számok ({0})", + "boolean": "logikai értékek ({0})", + "object": "objektumok ({0})", + "key": "kulcsok ({0})", + "entryAriaLabel": "{0}, szimbólumok", + "noSymbolsMatching": "Nincs illeszkedő szimbólum", + "noSymbolsFound": "Szimbólum nem található", + "gotoSymbolHandlerAriaLabel": "Írjon az aktív szerkesztőablakban található szimbólumok szűréséhez.", + "cannotRunGotoSymbolInFile": "Ehhez a fájlhoz nincs szimbóluminformáció", + "cannotRunGotoSymbol": "Szimbólum megkereséséhez nyisson meg egy szövegfájlt" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..59453cbd02 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, választó súgó", + "globalCommands": "globális parancsok", + "editorCommands": "szerkesztőablak parancsai" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..f126d409bc --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "Parancsok megjelenítése és futtatása", + "gotoLineDescriptionMac": "Sor megkeresése", + "gotoLineDescriptionWin": "Sor megkeresése", + "gotoSymbolDescription": "Ugrás szimbólumhoz egy fájlban...", + "gotoSymbolDescriptionScoped": "Szimbólum megkeresése kategória alapján", + "helpDescription": "Súgó megjelenítése", + "viewPickerDescription": "Nézet megnyitása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..68df525128 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, nézetválasztó", + "views": "Nézetek", + "panels": "Panelek", + "terminals": "Terminál", + "terminalTitle": "{0}: {1}", + "channels": "Kimenet", + "openView": "Nézet megnyitása", + "quickOpenView": "Gyors megnyitás nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..42f385cc25 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "Egy olyan beállítás változott, melynek hatályba lépéséhez újraindítás szükséges.", + "relaunchSettingDetail": "A beállítás engedélyezéséhez nyomja meg az újraindítás gombot a {0} újraindításához.", + "restart": "Újraindítás", + "relaunchWorkspaceMessage": "A munkaterület módosítása miatt a kiegészítőrendszer újratöltésére van szükség.", + "reload": "Újratöltés" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..837df13683 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "A szerkesztőablak margójának háttérszíne a módosított soroknál.", + "editorGutterAddedBackground": "A szerkesztőablak margójának háttérszíne a hozzáadott soroknál.", + "editorGutterDeletedBackground": "A szerkesztőablak margójának háttérszíne a törölt soroknál.", + "overviewRulerModifiedForeground": "A módosított tartalmat jelölő jelzések színe az áttekintősávon.", + "overviewRulerAddedForeground": "A hozzáadott tartalmat jelölő jelzések színe az áttekintősávon.", + "overviewRulerDeletedForeground": "A törölt tartalmat jelölő jelzések színe az áttekintősávon." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..3e45a215a2 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "Git megjelenítése", + "source control": "Verziókezelő rendszer", + "toggleSCMViewlet": "Verziókezelő megjelenítése", + "view": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..62dbc5a6a2 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0} függőben lévő módosítás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..6d8fbcabe8 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "További verziókezelő rendszerek telepítése...", + "switch provider": "Váltás más verziókezelő rendszerre..." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..7d2545de24 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Üzenet (nyomja meg a következőt a commithoz: {0})", + "installAdditionalSCMProviders": "További verziókezelő rendszerek telepítése...", + "no open repo": "Nincs aktív verziókezelő rendszer.", + "source control": "Verziókezelő rendszer", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..64427b00b5 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "fájl- és szimbólumkeresés eredménye", + "fileResults": "fájlkeresés eredménye" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..c52f4f1228 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, fájlválasztó", + "searchResults": "keresési eredmények" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..3e47266704 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, szimbólumválasztó", + "symbols": "szimbólumkeresési eredmények", + "noSymbolsMatching": "Nincs illeszkedő szimbólum", + "noSymbolsWithoutInput": "Adja meg a keresett szimbólumot" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..0795fde834 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "bemeneti adat", + "useIgnoreFilesDescription": "Ignore-fájlok használata", + "useExcludeSettingsDescription": "Kizárási beállítások használata" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..4fdd450503 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (csere előnézete)" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..3b1a1492b0 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Szimbólum megkeresése a munkaterületen...", + "name": "Keresés", + "showSearchViewlet": "Keresés megjelenítése", + "view": "Nézet", + "findInFiles": "Keresés a fájlokban", + "openAnythingHandlerDescription": "Fájl megkeresése", + "openSymbolDescriptionNormal": "Szimbólum megkeresése a munkaterületen", + "searchOutputChannelTitle": "Keresés", + "searchConfigurationTitle": "Keresés", + "exclude": "Globális minták konfigurálása fájlok és mappák keresésből való kizárásához. Örökli az összes globális mintát a fliex.exclude beállításból.", + "exclude.boolean": "A globális minta, amire illesztve lesznek a fájlok elérési útjai. A minta engedélyezéséhez vagy letiltásához állítsa igaz vagy hamis értékre.", + "exclude.when": "További ellenőrzés elvégzése az illeszkedő fájlok testvérein. Az illeszkedő fájl nevéhez használja a $(basename) változót!", + "useRipgrep": "Meghatározza, hogy a szövegben való kereséshez a ripgrep van-e használva.", + "useIgnoreFilesByDefault": "Meghatározza, hogy a .gitignore és .ignore fájlok használva vannak-e az új munkaterületeken a kereséshez.", + "search.quickOpen.includeSymbols": "Meghatározza, hogy a fájlok gyors megnyitásánál megjelenjenek-e a globális szimbólumkereső találatai." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..da6a31d560 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "Következő bele foglalt keresési minta megjelenítése", + "previousSearchIncludePattern": "Előző bele foglalt keresési minta megjelenítése", + "nextSearchExcludePattern": "Következő kizáró keresési minta megjelenítése", + "previousSearchExcludePattern": "Előző kizáró keresési minta megjelenítése", + "nextSearchTerm": "Következő keresőkifejezés megjelenítése", + "previousSearchTerm": "Előző keresőkifejezés megjelenítése", + "focusNextInputBox": "Váltás a következő beviteli mezőre", + "focusPreviousInputBox": "Váltás az előző beviteli mezőre", + "replaceInFiles": "Csere a fájlokban", + "findInWorkspace": "Keresés a munkaterületen...", + "findInFolder": "Keresés mappában...", + "RefreshAction.label": "Frissítés", + "ClearSearchResultsAction.label": "Keresési eredmények törlése", + "FocusNextSearchResult.label": "Váltás a következő keresési eredményre", + "FocusPreviousSearchResult.label": "Váltás az előző keresési eredményre", + "RemoveAction.label": "Eltávolítás", + "file.replaceAll.label": "Összes cseréje", + "match.replace.label": "Csere" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..fc3167df1a --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "További fájlok", + "searchFileMatches": "{0} fájl található", + "searchFileMatch": "{0} fájl található", + "searchMatches": "{0} találat", + "searchMatch": "{0} találat", + "folderMatchAriaLabel": "{0} találat a(z) {2} gyökérmappában. Keresési eredmény ", + "fileMatchAriaLabel": "{0} találat a(z) {2} mappa {1} fájljában. Keresési eredmény", + "replacePreviewResultAria": "{0} kifejezés cseréje a következőre: {1}, a(z) {2}. oszlopban, a következő szöveget tartalmazó sorban: {3}", + "searchResultAria": "Találat a(z) {0} kifejezésre a(z) {1}. oszlopban, a következő szöveget tartalmazó sorban: {2}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..402df7561a --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "Keresési részletek be- és kikapcsolása", + "searchScope.includes": "bele foglalt fájlok", + "label.includes": "Keresésbe bele foglalt fájlok", + "searchScope.excludes": "kizárt fájlok", + "label.excludes": "Keresésből kizárt fájlok", + "replaceAll.confirmation.title": "Minden előfordulás cseréje", + "replaceAll.confirm.button": "Csere", + "replaceAll.occurrence.file.message": "{0} előfordulás cserélve {1} fájlban a következőre: '{2}'.", + "removeAll.occurrence.file.message": "{0} előfordulás cserélve {1} fájlban.", + "replaceAll.occurrence.files.message": "{0} előfordulás cserélve {1} fájlban a következőre: '{2}'.", + "removeAll.occurrence.files.message": "{0} előfordulás cserélve {1} fájlban.", + "replaceAll.occurrences.file.message": "{0} előfordulás cserélve {1} fájlban a következőre: '{2}'.", + "removeAll.occurrences.file.message": "{0} előfordulás cserélve {1} fájlban.", + "replaceAll.occurrences.files.message": "{0} előfordulás cserélve {1} fájlban a következőre: '{2}'.", + "removeAll.occurrences.files.message": "{0} előfordulás cserélve {1} fájlban.", + "removeAll.occurrence.file.confirmation.message": "Cserél {0} előfordulás {1} fájlban a következőre: '{2}'?", + "replaceAll.occurrence.file.confirmation.message": "Cserél {0} előfordulást {1} fájlban?", + "removeAll.occurrence.files.confirmation.message": "Cserél {0} előfordulás {1} fájlban a következőre: '{2}'?", + "replaceAll.occurrence.files.confirmation.message": "Cserél {0} előfordulást {1} fájlban?", + "removeAll.occurrences.file.confirmation.message": "Cserél {0} előfordulás {1} fájlban a következőre: '{2}'?", + "replaceAll.occurrences.file.confirmation.message": "Cserél {0} előfordulást {1} fájlban?", + "removeAll.occurrences.files.confirmation.message": "Cserél {0} előfordulás {1} fájlban a következőre: '{2}'?", + "replaceAll.occurrences.files.confirmation.message": "Cserél {0} előfordulást {1} fájlban?", + "treeAriaLabel": "Keresési eredmények", + "searchPathNotFoundError": "Keresési elérési út nem található: {0}", + "searchMaxResultsWarning": "Az eredményhalmaz csak a találatok egy részét tartalmazza. Pontosítsa a keresést a keresési eredmények halmazának szűkítéséhez!", + "searchCanceled": "A keresés meg lett szakítva, mielőtt eredményt hozott volna –", + "noResultsIncludesExcludes": "Nincs találat a következő helyen: '{0}', '{1}' kivételével –", + "noResultsIncludes": "Nincs találat a következő helyen: '{0}' –", + "noResultsExcludes": "Nincs találat '{1}' kivételével –", + "noResultsFound": "Nincs találat. Ellenőrizze a kizárási beállításokat és az ignore-fájlokat –", + "rerunSearch.message": "Keresés megismétlése", + "rerunSearchInAll.message": "Keresés megismétlése az összes fájlban", + "openSettings.message": "Beállítások megnyitása", + "openSettings.learnMore": "További információ", + "ariaSearchResultsStatus": "A keresés {0} találatot eredményezett {1} fájlban", + "search.file.result": "{0} találat {1} fájlban", + "search.files.result": "{0} találat {1} fájlban", + "search.file.results": "{0} találat {1} fájlban", + "search.files.results": "{0} találat {1} fájlban", + "searchWithoutFolder": "Még nincs mappa megnyitva. Jelenleg csak a nyitott fájlokban történik keresés –", + "openFolder": "Mappa megnyitása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..9ce4f84fc8 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "Összes cseréje (küldje el a keresést az engedélyezéshez)", + "search.action.replaceAll.enabled.label": "Összes cseréje", + "search.replace.toggle.button.title": "Cseremd be- és kikapcsolása", + "label.Search": "Keresés: adja meg a keresőkifejezést, majd nyomjon Entert a kereséshez vagy Escape-et a megszakításhoz", + "search.placeHolder": "Keresés", + "label.Replace": "Csere: adja meg a cerekifejezést, majd nyomjon Entert a kereséshez vagy Escape-et a megszakításhoz", + "search.replace.placeHolder": "Csere", + "regexp.validationFailure": "A kifejezés mindenre illeszkedik" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/hun/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..07b8e7ef49 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "Nincs {0} nevű mappa a munkaterületen" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..aedc4d9c7b --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "Kódrészleteket szolgáltat.", + "vscode.extension.contributes.snippets-language": "Azon nyelv azonosítója, amely számára szolgáltatva van ez a kódrészlet.", + "vscode.extension.contributes.snippets-path": "A kódrészlet-fájl elérési útja. Az elérési út relatív a kiegészítő mappájához, és általában a következővel kezdődik: './snippets/',", + "invalid.language": "Ismeretlen nyelv található a következőben: `contributes.{0}.language`. A megadott érték: {1}", + "invalid.path.0": "Hiányzó karakterlánc a `contributes.{0}.path`-ban. A megadott érték: {1}", + "invalid.path.1": "A `contributes.{0}.path` ({1}) nem a kiegészítő mappáján belül található ({2}). Emiatt előfordulhat, hogy a kiegészítő nem lesz hordozható.", + "badVariableUse": "A(z) \"{0}\" kódrészlet nagy valószínűséggel keveri a kódrészletváltozók és a kódrészlet-helyjelölők fogalmát. További információ a következő oldalon található: https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..4f61c70284 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "Kódrészlet beszúrása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..c6e4db56e3 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "Kódrészlet nyelvének kiválasztása", + "openSnippet.errorOnCreate": "A(z) {0} nem hozható létre", + "openSnippet.label": "Felhasználói kódrészletek megnyitása", + "preferences": "Beállítások", + "snippetSchema.json.default": "Üres kódrészlet", + "snippetSchema.json": "Felhasználói kódrészlet-konfiguráció", + "snippetSchema.json.prefix": "A kódrészlet IntelliSense-ben történő kiválasztásánál használt előtag", + "snippetSchema.json.body": "A kódrészlet tartalma. Kurzorpozíciók definiálásához használja a '$1' és '${1:defaultText}' jelölőket, a '$0' pedig a végső kurzorpozíció. Változónevek a '${varName}' és '${varName:defaultText}' formában definiálhatók, pl.: 'Ez a fájl: $TM_FILENAME'.", + "snippetSchema.json.description": "A kódrészlet leírása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..b08b77551e --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "Felhasználói kódrészlet", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0}, {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..b98aa081da --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "Kódrészletek beszúrása, ha az előtagjuk illeszkedik. Legjobban akkor működik, ha a 'quickSuggestions' nincs engedélyezve." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..8cb97f7e00 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "Segítsen javítani a {0}-támogatásunkat", + "takeShortSurvey": "Rövid felmérés kitöltése", + "remindLater": "Emlékeztessen később", + "neverAgain": "Ne jelenítse meg újra" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..d5452900b7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Lenne kedve egy gyors elégedettségi felméréshez?", + "takeSurvey": "Felmérés kitöltése", + "remindLater": "Emlékeztessen később", + "neverAgain": "Ne jelenítse meg újra" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..cbd9452c83 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Adja meg a buildelési feladat nevét!", + "noTasksMatching": "Nincs ilyen feladat", + "noTasksFound": "Buildelési feladat nem található" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..216c49bfcc --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, feladatok", + "recentlyUsed": "legutóbb futtatott feladatok", + "configured": "konfigurált feladatok", + "detected": "talált feladatok", + "customizeTask": "Feladat beállítása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..36d9508854 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Adja meg az újraindítandó feladat nevét!", + "noTasksMatching": "Nincs ilyen feladat", + "noTasksFound": "Újraindítandó feladat nem található" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..9ed65f91a1 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Adja meg a futtatandó feladat nevét!", + "noTasksMatching": "Nincs ilyen feladat", + "noTasksFound": "Feladat nem található" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..eda4d75910 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Adja meg a megszakítandó feladat nevét!", + "noTasksMatching": "Nincs ilyen feladat", + "noTasksFound": "Megszakítandó feladat nem található" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..8d42d3ec16 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Adja meg a tesztelési feladat nevét!", + "noTasksMatching": "Nincs ilyen feladat", + "noTasksFound": "Tesztelési feladat nem találhat" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..5f3016da41 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Figyelmeztetés: az options.cwd értékének string típusúnak kell lennie. A következő érték figyelmen kívül van hagyva: {0}.", + "ConfigurationParser.noargs": "Hiba: a parancssori argumentumokat string típusú tömbként kell megadni. A megadott érték:\n{0}", + "ConfigurationParser.noShell": "Figyelmeztetés: a shellkonfiguráció csak akkor támogatott, ha a feladat a terminálban van végrehajtva.", + "ConfigurationParser.noName": "Hiba: a deklarációs hatókörben lévő problémailleszőnek kötelező nevet adni:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Figyelem: a megadott problémaillesztő ismeretlen. A támogatott típusok: string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Hiba: érvénytelen problemMatcher-referencia: {0}\n", + "ConfigurationParser.noTaskName": "Hiba: a feladathoz meg kell adni a taskName tulajdonságot. A feladat figyelmen kívül lesz hagyva.\n{0}\n", + "taskConfiguration.shellArgs": "Figyelmeztetés: a(z) '{0}' feladat egy rendszerparancs, és vagy a parancs nevében vagy az argumentumok egyikében escape nélküli szóköz található. A megfelelő idézőjelezés érdekében olvassza bele az argumentumokat a parancsba.", + "taskConfiguration.noCommandOrDependsOn": "Hiba: a(z) '{0}' feladat nem ad meg parancsot, és nem definiálja a dependsOn tulajdonságot sem. A feladat figyelmen kívül lesz hagyva. A definíciója:\n{1}", + "taskConfiguration.noCommand": "Hiba: a(z) '{0}' feladathoz nincs definiálva a parancs. A feladat figyelmen kívül lesz hagyva. A definíciója:\n{1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..b7a90d2ba2 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "A feladat konkrét típusa", + "TaskDefinition.properties": "A feladattípus további tulajdonságai", + "TaskTypeConfiguration.noType": "A feladattípus-konfigurációból hiányzik a kötelező 'taskType' tulajdonság", + "TaskDefinitionExtPoint": "Feladattípusokat szolgáltat" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..6dba8a283c --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": "Végrehajt egy .NET Core buildelési parancsot", + "msbuild": "Végrehajtja a buildelés célpontját", + "externalCommand": "Példa egy tetszőleges külső parancs futtatására", + "Maven": "Általános maven parancsokat hajt végre" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..33ccc3691b --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "További parancsbeálíltások", + "JsonSchema.options.cwd": "A végrehajtott program vagy parancsfájl munkakönyvtára. Ha nincs megadva, akkor a Code aktuális munkaterületének gyökérkönyvtára van használva.", + "JsonSchema.options.env": "A végrehajtott parancs vagy shell környezete. Ha nincs megadva, akkor a szülőfolyamat környezete van használva.", + "JsonSchema.shellConfiguration": "Meghatározza a használt shellt.", + "JsonSchema.shell.executable": "A használt shell.", + "JsonSchema.shell.args": "Shellargumentumok.", + "JsonSchema.command": "A végrehajtandó parancs. Lehet egy külső parancs vagy egy rendszerparancs.", + "JsonSchema.tasks.args": "A parancs meghívásakor átadott argumentumok.", + "JsonSchema.tasks.taskName": "A feladat neve.", + "JsonSchema.tasks.windows": "Windows-specifikus parancskonfiguráció", + "JsonSchema.tasks.mac": "Mac-specifikus parancskonfiguráció", + "JsonSchema.tasks.linux": "Linux-specifikus parancskonfiguráció", + "JsonSchema.tasks.suppressTaskName": "Meghatározza, hogy a feladat neve hozzá van adva argumentumként a parancshoz. Ha nincs megadva, akkor a globálisan meghatározot érték van használva.", + "JsonSchema.tasks.showOutput": "Meghatározza, hogy a futó feladat kimenete meg van-e jelenítve, vagy sem. Ha nincs megadva, akkor a globálisan meghatározot érték van használva.", + "JsonSchema.echoCommand": "Meghatározza, hogy a végrehajtott parancs ki van-e írva a kimenetre. Alapértelmezett értéke hamis.", + "JsonSchema.tasks.watching.deprecation": "Elavult. Használja helyette az isBackground beállítást.", + "JsonSchema.tasks.watching": "A feladat folyamatosan fut-e és figyeli-e a fájlrendszert.", + "JsonSchema.tasks.background": "A feladat folyamatosan fut-e és a háttérben fut-e.", + "JsonSchema.tasks.promptOnClose": "A felhasználó figyelmeztetve van-e, ha a VS Code egy futó feladat közben záródik be.", + "JsonSchema.tasks.build": "A parancsot a Code alapértelmezett buildelési parancsához rendeli.", + "JsonSchema.tasks.test": "A parancsot a Code alapértelmezett tesztelési parancsához rendeli.", + "JsonSchema.tasks.matchers": "A használt problémaillesztők. Lehet karakterlánc, problémaillesztő, vagy egy tömb, ami karakterláncokat és problémaillesztőket tartalmaz.", + "JsonSchema.args": "A parancsnak átadott további argumentumok.", + "JsonSchema.showOutput": "Meghatározza, hogy a futó feladat kimenete megjelenjen-e vagy sem. Ha nincs megadva, az 'always' érték van használva.", + "JsonSchema.watching.deprecation": "Elavult. Használja helyette az isBackground beállítást.", + "JsonSchema.watching": "A feladat folyamatosan fut-e és figyeli-e a fájlrendszert.", + "JsonSchema.background": "A feladat folyamatosan fut-e és a háttérben fut-e.", + "JsonSchema.promptOnClose": "A felhasználó figyelmeztetve van-e, ha a VS Code egy háttérben futó feladat közben záródik be.", + "JsonSchema.suppressTaskName": "Meghatározza, hogy a feladat neve hozzá van adva argumentumként a parancshoz. Alapértelmezett értéke hamis.", + "JsonSchema.taskSelector": "Előtag, ami jelzi, hogy az argumentum a feladat.", + "JsonSchema.matchers": "A használt problémaillesztők. Lehet karakterlánc, problémaillesztő, vagy egy tömb, ami karakterláncokat és problémaillesztőket tartalmaz.", + "JsonSchema.tasks": "Feladatkonfigurációk. Általában egy külső feladatfuttató rendszerben definiált feladatok kiegészítő beállításokkal ellátott változatai." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..6049722b15 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "A konfiguráció verziószáma", + "JsonSchema._runner": "A futtató kikerült a kísérleti állapotból. Használja a hivatalos runner tulajdonságot!", + "JsonSchema.runner": "Meghatározza, hogy a feladat folyamatként van-e végrehajtva, és a kimenet a kimeneti ablakban jelenjen-e meg, vagy a terminálban.", + "JsonSchema.windows": "Windows-specifikus parancskonfiguráció", + "JsonSchema.mac": "Mac-specifikus parancskonfiguráció", + "JsonSchema.linux": "Linux-specifikus parancskonfiguráció", + "JsonSchema.shell": "Meghatározza, hogy a parancs egy rendszerparancs vagy egy külső program. Alapértelmezett értéke hamis, ha nincs megadva." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..465794e765 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "Meghatározza, hogy a parancs egy rendszerparancs vagy egy külső program. Alapértelmezett értéke hamis, ha nincs megadva.", + "JsonSchema.tasks.isShellCommand.deprecated": "Az isShellCommand tulajdonság elavult. Használja helyette a feladat type tulajdonságát és a shell tulajdonságot a beállításoknál. További információt az 1.14-es verzió kiadási jegyzékében talál.", + "JsonSchema.tasks.dependsOn.string": "Egy másik feladat, amitől ez a feladat függ.", + "JsonSchema.tasks.dependsOn.array": "Más feladatok, amiktől ez a feladat függ.", + "JsonSchema.tasks.presentation": "A feladat kimenetének megjelenítésére és adatbevitelére használt panelt konfigurálja.", + "JsonSchema.tasks.presentation.echo": "Meghatározza, hogy a végrehajtott parancs ki van-e írva a terminálban. Alapértelmezett értéke true.", + "JsonSchema.tasks.presentation.focus": "Meghatározza, hogy a panel fókuszt kap-e. Az alapértelmezett értéke true. Ha true-ra van állítva, akkor a panel fel is lesz fedve.", + "JsonSchema.tasks.presentation.reveal.always": "A feladat végrehajtásakor mindig legyen felfedve a terminál.", + "JsonSchema.tasks.presentation.reveal.silent": "Csak akkor legyen felfedve a terminál, ha nincs problémaillesztő társítva a feladathoz, és probléma történik a végrehajtás során.", + "JsonSchema.tasks.presentation.reveal.never": "Soha ne legyen felfedve a terminál a feladat végrehajtása során.", + "JsonSchema.tasks.presentation.reveals": "Meghatározza, hogy a feladatot futtató panel fel van-e fedve vagy sem. Alapértelmezett értéke \"always\".", + "JsonSchema.tasks.presentation.instance": "Meghatározza, hogy a panel meg van-e osztva a feladatok között, ennek a feladatnak van-e dedikálva, vagy új készül minden egyes futtatás során.", + "JsonSchema.tasks.terminal": "A terminal tulajdonság elavult. Használja helyette a presentation tulajdonságot!", + "JsonSchema.tasks.group.kind": "A feladat végrehajtási csoportja.", + "JsonSchema.tasks.group.isDefault": "Meghatározza, hogy ez a feladat egy elsődleges feladat-e a csoportban.", + "JsonSchema.tasks.group.defaultBuild": "A feladatot az alapértelmezett buildelési feladatnak jelöli meg.", + "JsonSchema.tasks.group.defaultTest": "A feladatot az alapértelmezett tesztelési feladatnak jelöli meg.", + "JsonSchema.tasks.group.build": "A feladatot a 'Buildelési feladat futtatása' parancson keresztül elérhető buildelési feladatnak jelöli meg.", + "JsonSchema.tasks.group.test": "A feladatot a 'Tesztelési feladat futtatása' parancson keresztül elérhető tesztelési feladatnak jelöli meg.", + "JsonSchema.tasks.group.none": "A feladatot egyetlen csoporthoz sem rendeli", + "JsonSchema.tasks.group": "Meghatározza a feladat végrehajtási csoportját. A \"build\" esetén a buildelési csoportba, a \"test\" esetén a tesztelési csoportba kerül bele a feladat.", + "JsonSchema.tasks.type": "Meghatározza, hogy a feladat folyamatként van-e végrehajtva vagy egy parancsként a shellben. Alapértelmetten folyamatként vannak végrehajtva.", + "JsonSchema.version": "A konfiguráció verziószáma", + "JsonSchema.tasks.identifier": "A feladat felhasználó által definiált azonosítója, amivel hivatkozni lehet a feladatra a lauch.json-ban vagy egy dependsOn-utasításban.", + "JsonSchema.tasks.taskLabel": "A feladat címkéje", + "JsonSchema.tasks.taskName": "A feladat neve.", + "JsonSchema.tasks.taskName.deprecated": "A feladat name tulajdonsága elavult. Használja a label tulajdonságot helyette!", + "JsonSchema.tasks.background": "A feladat folyamatosan fut-e és a háttérben fut-e.", + "JsonSchema.tasks.promptOnClose": "A felhasználó figyelmeztetve van-e, ha a VS Code egy futó feladat közben záródik be.", + "JsonSchema.tasks.matchers": "A használt problémaillesztők. Lehet karakterlánc, problémaillesztő, vagy egy tömb, ami karakterláncokat és problémaillesztőket tartalmaz.", + "JsonSchema.customizations.customizes.type": "Az egyedi konfigurációhoz használt feladattípus", + "JsonSchema.tasks.customize.deprecated": "A customize tulajdonság elavult. A feladat egyedi konfigurálásának új megközelítésével kapcsolatban további információt az 1.14-es verzió kiadási jegyzékében talál.", + "JsonSchema.tasks.showOputput.deprecated": "A showOutput tulajdonság elavult. Használja helyette a presentation tulajdonságon belül a reveal tulajdonságot! További információt az 1.14-es verzió kiadási jegyzékében talál.", + "JsonSchema.tasks.echoCommand.deprecated": "Az echoCommand tulajdonság elavult. Használja helyette a presentation tulajdonságon belül az echo tulajdonságot! További információt az 1.14-es verzió kiadási jegyzékében talál.", + "JsonSchema.tasks.suppressTaskName.deprecated": "Az suppressTaskName tulajdonság elavult. Helyette olvassza be a parancsot az argumentumaival együtt a feladatba! További információt az 1.14-es verzió kiadási jegyzékében talál.", + "JsonSchema.tasks.isBuildCommand.deprecated": "Az isBuildCommand tulajdonság elavult. Használja helyette a group tulajdonságot! További információt az 1.14-es verzió kiadási jegyzékében talál.", + "JsonSchema.tasks.isTestCommand.deprecated": "Az isTestCommand tulajdonság elavult. Használja helyette a group tulajdonságot! További információt az 1.14-es verzió kiadási jegyzékében talál.", + "JsonSchema.tasks.taskSelector.deprecated": "A taskSelector tulajdonság elavult. Helyette olvassza be a parancsot az argumentumaival együtt a feladatba! További információt az 1.14-es verzió kiadási jegyzékében talál.", + "JsonSchema.windows": "Windows-specifikus parancskonfiguráció", + "JsonSchema.mac": "Mac-specifikus parancskonfiguráció", + "JsonSchema.linux": "Linux-specifikus parancskonfiguráció" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..6f17e3e015 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "Feladatok", + "ConfigureTaskRunnerAction.noWorkspace": "A feladatok csak egy munkaterület mappájára vonatkozóan érhetők el.", + "ConfigureTaskRunnerAction.quickPick.template": "Feladatfuttató rendszer választása", + "ConfigureTaskRunnerAction.autoDetecting": "Feladatok automatikus felderítése a következőhöz: {0}", + "ConfigureTaskRunnerAction.autoDetect": "A feladatfuttató rendszer automatikus felderítése nem sikerült. Az alapértelmezett sablon használata. Tekintse meg a feladatkimenetet a részletekért.", + "ConfigureTaskRunnerAction.autoDetectError": "A feladatfuttató rendszer automatikus felderítése hibákat eredményezett. Tekintse meg a feladatkimenetet a részletekért.", + "ConfigureTaskRunnerAction.failed": "Nem sikerült létrehozni a 'tasks.json' fájlt a '.vscode' mappában. Tekintse meg a feladatkimenetet a részletekért.", + "ConfigureTaskRunnerAction.label": "Feladatfuttató rendszer beállítása", + "ConfigureBuildTaskAction.label": "Buildelési feladat beállítása", + "CloseMessageAction.label": "Bezárás", + "ShowTerminalAction.label": "Terminál megtekintése", + "problems": "Problémák", + "manyMarkers": "99+", + "runningTasks": "Futó feladatok megjelenítése", + "tasks": "Feladatok", + "TaskSystem.noHotSwap": "A feladatvégrehajtó motor megváltoztatása az ablak újraindítását igényli.", + "TaskService.noBuildTask1": "Nincs buildelési feladat definiálva. Jelöljön meg egy feladatot az 'isBuildCommand' tulajdonsággal a tasks.json fájlban!", + "TaskService.noBuildTask2": "Nincs buildelési feladat definiálva. Jelöljön meg egy feladatot a 'build' csoporttal a tasks.json fájlban!", + "TaskService.noTestTask1": "Nincs tesztelési feladat definiálva. Jelöljön meg egy feladatot az 'isTestCommand' tulajdonsággal a tasks.json fájlban!", + "TaskService.noTestTask2": "Nincs tesztelési feladat definiálva. Jelöljön meg egy feladatot a 'test' csoporttal a tasks.json fájlban!", + "TaskServer.noTask": "A futtatni kívánt feladat ({0}) nem található.", + "TaskService.attachProblemMatcher.continueWithout": "Folytatás a feladat kimenetének átkutatása nélkül", + "TaskService.attachProblemMatcher.never": "Soha ne kutassa át a feladat kimenetét", + "TaskService.attachProblemMatcher.learnMoreAbout": "További információk a feladat kimenetének átkutatásáról", + "selectProblemMatcher": "Válassza ki, milyen típusú hibák és figyelmeztetések legyenek keresve a feladat kimenetében!", + "customizeParseErrors": "A jelenlegi feladatkonfigurációban hibák vannak. Feladat egyedivé tétele előtt javítsa a hibákat!", + "moreThanOneBuildTask": "Túl sok buildelési feladat van definiálva a tasks.json-ban. Az első lesz végrehajtva.\n", + "TaskSystem.activeSame.background": "A(z) '{0}' feladat már aktív és a háttérben fut. A feladat befejezéséhez használja az `Feladat megszakítása` parancsot a Feladatok menüből!", + "TaskSystem.activeSame.noBackground": "A(z) '{0}' feladat már aktív. A feladat befejezéséhez használja `Feladat megszakítása` parancsot a Feladatok menüből!", + "TaskSystem.active": "Már fut egy feladat. Szakítsa meg, mielőtt egy másik feladatot futtatna.", + "TaskSystem.restartFailed": "Nem sikerült a(z) {0} feladat befejezése és újraindítása.", + "TaskSystem.configurationErrors": "Hiba: a megadott feladatkonfigurációban validációs hibák vannak, és nem használható. Először javítsa ezeket a hibákat!", + "TaskSystem.invalidTaskJson": "Hiba. A tasks.json fájlban szintaktikai hibák találhatók. Javítsa ezeket a hibákat feladatvégrehajtás előtt.\n", + "TaskSystem.runningTask": "Már fut egy feladat. Szeretné megszakítani?", + "TaskSystem.terminateTask": "Felada&&t megszakítása", + "TaskSystem.noProcess": "Az elindított feladat már nem létezik. Ha a feladat egy háttérfolyamatot indított, a VS Code-ból való kilépés árva folyamatokat eredményezhet. Ennek megakadályozása érdekében indítsa el a legutóbbi háttérfolyamatot a wait kapcsolóval!", + "TaskSystem.exitAnyways": "Kilépés mind&&enképp", + "TerminateAction.label": "Feladat megszakítása", + "TaskSystem.unknownError": "Hiba történt a feladat futtatása közben. További részletek a feladatnaplóban.", + "TaskService.noWorkspace": "A feladatok csak egy munkaterület mappájára vonatkozóan érhetők el.", + "recentlyUsed": "legutóbb futtatott feladatok", + "configured": "konfigurált feladatok", + "detected": "talált feladatok", + "TaskService.fetchingBuildTasks": "Buildelési feladatok lekérése...", + "TaskService.noBuildTaskTerminal": "Buildelési feladat nem található. Futtassa a 'Buildelési feladat beállítása' parancsot a létrehozáshoz!", + "TaskService.pickBuildTask": "Válassza ki a futtatandó buildelési feladatot!", + "TaskService.fetchingTestTasks": "Tesztelési feladatok lekérése...", + "TaskService.noTestTaskTerminal": "Buildelési feladat nem található. Futtassa a 'Feladatfuttató rendszer beállítása' parancsot a létrehozáshoz!", + "TaskService.pickTestTask": "Válassza ki a futtatandó tesztelési feladatot", + "TaskService.noTaskRunning": "Jelenleg nem fut feladat.", + "TaskService.tastToTerminate": "Válassza ki a megszakítandó feladatot!", + "TerminateAction.noProcess": "Az elindított folyamat már nem létezik. Ha a feladat háttérfeladatokat indított, a VS Code-ból való kilépés árva folyamatokat eredményezhet. ", + "TerminateAction.failed": "Nem sikerült megszakítani a futó feladatot", + "TaskService.noTaskToRestart": "Nincs újraindítható feladat.", + "TaskService.tastToRestart": "Válassza ki az újraindítandó feladatot!", + "TaskService.defaultBuildTaskExists": "A(z) {0} már meg van jelölve alapértelmezett buildelési feladatként.", + "TaskService.pickDefaultBuildTask": "Válassza ki az alpértelmezett buildelési feladatként használt feladatot!", + "TaskService.defaultTestTaskExists": "A(z) {0} már meg van jelölve alapértelmezett tesztelési feladatként.", + "TaskService.pickDefaultTestTask": "Válassza ki az alpértelmezett tesztelési feladatként használt feladatot!", + "TaskService.noTaskIsRunning": "Nem fut feladat.", + "TaskService.pickShowTask": "Válassza ki a feladatot a kimenet megjelenítéséhez!", + "ShowLogAction.label": "Feladatnapló megtekintése", + "RunTaskAction.label": "Feladat futtatása", + "RestartTaskAction.label": "Futó feladat újraindítása...", + "ShowTasksAction.label": "Futó feladatok megjelenítése", + "BuildAction.label": "Buildelési feladat futtatása", + "TestAction.label": "Tesztelési feladat futtatása", + "ConfigureDefaultBuildTask.label": "Alapértelmezett buildelési feladat beállítása", + "ConfigureDefaultTestTask.label": "Alapértelmezett tesztelési feladat beállítása", + "quickOpen.task": "Feladat futtatása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..7e98627470 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "Feladatok" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..6b1531381f --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "Ismeretlen hiba történt a feladat végrehajtása közben. Részletek a feladat kimeneti naplójában találhatók.", + "TerminalTaskSystem.terminalName": "Feladat – {0}", + "reuseTerminal": "A terminál újra lesz hasznosítva a feladatok által. Nyomjon meg egy billentyűt a bezáráshoz.", + "TerminalTaskSystem": "Rendszerparancsok nem hajthatók végre UNC-meghajtókon.", + "unkownProblemMatcher": "A(z) {0} problémaillesztő nem található. Az illesztő figyelmen kívül lesz hagyva." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..3a3a49d888 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "A gulp --tasks-simple futtatása nem listázott egyetlen feladatot sem. Futtatta az npm install parancsot?", + "TaskSystemDetector.noJakeTasks": "A jake --tasks futtatása nem listázott egyetlen feladatot sem. Futtatta az npm install parancsot?", + "TaskSystemDetector.noGulpProgram": "A Gulp nincs telepítve a rendszerre. Futtassa az npm install -g gulp parancsot a telepítéshez!", + "TaskSystemDetector.noJakeProgram": "A Jake nincs telepítve a rendszerre. Futtassa az npm install -g jake parancsot a telepítéshez!", + "TaskSystemDetector.noGruntProgram": "A Grunt nincs telepítve a rendszerre. Futtassa az npm install -g grunt parancsot a telepítéshez!", + "TaskSystemDetector.noProgram": "Az) {0} program nem található. Az üzenet: {1}", + "TaskSystemDetector.buildTaskDetected": "Felderítésre került a következő buildelési feladat: '{0}'.", + "TaskSystemDetector.testTaskDetected": "Felderítésre került a következő tesztelési feladat: '{0}'. " +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..7251cf69ee --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "Ismeretlen hiba történt a feladat végrehajtása közben. Részletek a kimeneti naplóban találhatók.", + "TaskRunnerSystem.watchingBuildTaskFinished": "A figyelő buildelési feladat befejeződött.", + "TaskRunnerSystem.childProcessError": "Nem sikerült elindítani a külső programot: {0} {1}.", + "TaskRunnerSystem.cancelRequested": "Az) '{0}' feladat a felhasználó kérésére lett megszakítva.", + "unkownProblemMatcher": "A(z) {0} problémaillesztőt nem lehet feloldani. Az illesztő figyelmen kívül lesz hagyva." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..77a2b87d0a --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Figyelmeztetés: az options.cwd értékének string típusúnak kell lennie. A következő érték figyelmen kívül van hagyva: {0}.", + "ConfigurationParser.noargs": "Hiba: a parancssori argumentumokat string típusú tömbként kell megadni. A megadott érték:\n{0}", + "ConfigurationParser.noShell": "Figyelmeztetés: a shellkonfiguráció csak akkor támogatott, ha a feladat a terminálban van végrehajtva.", + "ConfigurationParser.noName": "Hiba: a deklarációs hatókörben lévő problémailleszőnek kötelező nevet adni:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Figyelem: a megadott problémaillesztő ismeretlen. A támogatott típusok: string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Hiba: érvénytelen problemMatcher-referencia: {0}\n", + "ConfigurationParser.noTaskType": "Hiba: a feladatkonfigurációnak rendelkeznie kell type tulajdonsággal. A konfiguráció figyelmen kívül lesz hagyva.\n{0}\n", + "ConfigurationParser.noTypeDefinition": "Hiba: nincs '{0}' azonosítójú feladattípus regisztrálva. Elfelejtett telepíteni egy kiegészítőt, ami a feladat szolgáltatásáért felelős?", + "ConfigurationParser.notCustom": "Hiba: a feladat nem egyedi feladatként van definiálva. A konfiguráció figyelmen kívül lesz hagyva.\n{0}\n", + "ConfigurationParser.noTaskName": "Hiba: a feladathoz meg kell adni a taskName tulajdonságot. A feladat figyelmen kívül lesz hagyva.\n{0}\n", + "taskConfiguration.shellArgs": "Figyelmeztetés: a(z) '{0}' feladat egy rendszerparancs, és vagy a parancs nevében vagy az argumentumok egyikében escape nélküli szóköz található. A megfelelő idézőjelezés érdekében olvassza bele az argumentumokat a parancsba.", + "taskConfiguration.noCommandOrDependsOn": "Hiba: a(z) '{0}' feladat nem ad meg parancsot, és nem definiálja a dependsOn tulajdonságot sem. A feladat figyelmen kívül lesz hagyva. A definíciója:\n{1}", + "taskConfiguration.noCommand": "Hiba: a(z) '{0}' feladathoz nincs definiálva a parancs. A feladat figyelmen kívül lesz hagyva. A definíciója:\n{1}", + "TaskParse.noOsSpecificGlobalTasks": "A feladatok 2.0.0-s verziója nem támogatja a globális, operációs rendszer-specifikus feladatokat. Alakítsa át őket operációs rendszer-specifikus parancsot tartalmazó feladattá. Az érintett feladatok:\n{0}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..0cd70ed90c --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0}, terminálválasztó", + "termCreateEntryAriaLabel": "{0}, új terminál létrehozása", + "'workbench.action.terminal.newplus": "$(plus) Új integrált terminál létrehozása", + "noTerminalsMatching": "Nincs ilyen terminál", + "noTerminalsFound": "Nincs megnyitott terminál" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..0a2e531d3a --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "Összes megnyitott terminál megjelenítése", + "terminalIntegratedConfigurationTitle": "Beépített terminál", + "terminal.integrated.shell.linux": "A terminál által használt shell elérési útja Linuxon.", + "terminal.integrated.shellArgs.linux": "Linux-terminál esetén használt parancssori argumentumok.", + "terminal.integrated.shell.osx": "A terminál által használt shell elérési útja OS X-en.", + "terminal.integrated.shellArgs.osx": "OS X-terminál esetén használt parancssori argumentumok.", + "terminal.integrated.shell.windows": "A terminál által használt shell elérési útja Windowson. A Windows beépített shelljei (cmd, PowerShell vagy Bash on Ubuntu) használata esetén kell megadni.", + "terminal.integrated.shellArgs.windows": "Windows-terminál esetén használt parancssori argumentumok.", + "terminal.integrated.rightClickCopyPaste": "Ha be van állítva, megakadályozza a helyi menü megjelenését a terminálon történő jobb kattintás esetén. Helyette másol, ha van kijelölés, és beilleszt, ha nincs.", + "terminal.integrated.fontFamily": "Meghatározza a terminál betűtípusát. Alapértelmezett értéke az editor.fontFamily értéke.", + "terminal.integrated.fontLigatures": "Meghatározza, hogy a terminálban engedélyezve vannak-e a betűtípus-ligatúrák.", + "terminal.integrated.fontSize": "Meghatározza a terminálban használt betű méretét, pixelekben.", + "terminal.integrated.lineHeight": "Meghatározza a sormagasságot a terminálban. A tényleges méret a megadott szám és a terminál betűméretének szorzatából jön ki.", + "terminal.integrated.enableBold": "Engedélyezve van-e a félkövér szöveg a terminálban. A működéshez szükséges, hogy a terminál shellje támogassa a félkövér betűket.", + "terminal.integrated.cursorBlinking": "Meghatározza, hogy a terminál kurzora villog-e.", + "terminal.integrated.cursorStyle": "Meghatározza a terminál kurzorának stílusát.", + "terminal.integrated.scrollback": "Meghatározza, hogy a terminál legfeljebb hány sort tárol a pufferben.", + "terminal.integrated.setLocaleVariables": "Meghatározza, hogy a lokálváltozók be vannak-e állítva a terminál indításánál. Alapértelmezett értéke igaz OS X-en, hamis más platformokon.", + "terminal.integrated.cwd": "Explicit elérési út, ahol a terminál indítva lesz. Ez a shellfolyamat munkakönyvtára (cwd) lesz. Ez a beállítás nagyon hasznos olyan munkaterületeken, ahol a gyökérkönyvtár nem felel meg munkakönyvtárnak.", + "terminal.integrated.confirmOnExit": "Meghatározza, hogy megerősítést kér-e az alkalamzás, ha van aktív terminál-munkafolyamat.", + "terminal.integrated.commandsToSkipShell": "Olyan parancsazonosítók listája, melyek nem lesznek elküldve a shellnek, és ehelyett mindig a Code kezeli le őket. Ez lehetővé teszi, hogy az olyan billentyűparancsok, melyeket normál esetben a shell dolgozna fel, ugyanúgy működjenek, mint mikor a terminálon nincs fókusz. Például ilyen a gyorsmegnyitás indításához használt Ctrl+P.", + "terminal.integrated.env.osx": "A VS Code folyamatához hozzáadott környezeti változókat tartalmazó objektum, amit az OS X-es terminál használ.", + "terminal.integrated.env.linux": "A VS Code folyamatához hozzáadott környezeti változókat tartalmazó objektum, amit a linuxos terminál használ.", + "terminal.integrated.env.windows": "A VS Code folyamatához hozzáadott környezeti változókat tartalmazó objektum, amit a windowsos terminál használ.", + "terminal": "Terminál", + "terminalCategory": "Terminál", + "viewCategory": "Nézet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..5149e8e1f7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "Integrált terminál be- és kikapcsolása", + "workbench.action.terminal.kill": "Az aktív terminálpéldány leállítása", + "workbench.action.terminal.kill.short": "Terminál leállítása", + "workbench.action.terminal.quickKill": "Terminálpéldány leállítása", + "workbench.action.terminal.copySelection": "Kijelölés másolása", + "workbench.action.terminal.selectAll": "Összes kijelölése", + "workbench.action.terminal.deleteWordLeft": "Balra lévő szó törlése", + "workbench.action.terminal.deleteWordRight": "Jobbra lévő szó törlése", + "workbench.action.terminal.new": "Új integrált terminál létrehozása", + "workbench.action.terminal.new.short": "Új terminál", + "workbench.action.terminal.focus": "Váltás a terminálra", + "workbench.action.terminal.focusNext": "Váltás a következő terminálra", + "workbench.action.terminal.focusAtIndex": "Váltás a(z) {0}. terminálra", + "workbench.action.terminal.focusPrevious": "Váltás az előző terminálra", + "workbench.action.terminal.paste": "Beillesztés az aktív terminálba", + "workbench.action.terminal.DefaultShell": "Alapértelmezett shell kiválasztása", + "workbench.action.terminal.runSelectedText": "Kijelölt szöveg futtatása az aktív terminálban", + "workbench.action.terminal.runActiveFile": "Aktív fájl futtatása az az aktív terminálban", + "workbench.action.terminal.runActiveFile.noFile": "Csak a lemezen lévő fájlok futtathatók a terminálban", + "workbench.action.terminal.switchTerminalInstance": "Terminálpéldány váltása", + "workbench.action.terminal.scrollDown": "Görgetés lefelé (soronként)", + "workbench.action.terminal.scrollDownPage": "Görgetés lefelé (oldalanként)", + "workbench.action.terminal.scrollToBottom": "Görgetés az aljára", + "workbench.action.terminal.scrollUp": "Görgetés felfelé (soronként)", + "workbench.action.terminal.scrollUpPage": "G9rgetés felfelé (oldalanként)", + "workbench.action.terminal.scrollToTop": "Görgetés a tetejére", + "workbench.action.terminal.clear": "Törlés", + "workbench.action.terminal.allowWorkspaceShell": "Munkaterületspecifikus shellkonfiguráció engedélyezése", + "workbench.action.terminal.disallowWorkspaceShell": "Munkaterületspecifikus shellkonfiguráció letiltása", + "workbench.action.terminal.rename": "Átnevezés", + "workbench.action.terminal.rename.prompt": "Adja meg a terminál nevét!", + "workbench.action.terminal.focusFindWidget": "Váltás a keresőmodulra", + "workbench.action.terminal.hideFindWidget": "Keresőmodul elrejtése", + "nextTerminalFindTerm": "Következő keresési kifejezés megjelenítése", + "previousTerminalFindTerm": "Előző keresési kifejezés megjelenítése", + "quickOpenTerm": "Terminál: Aktív terminál váltása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..d3e4c6909d --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "A terminál háttérszíne. Ez lehetővé teszi a terminál paneltől eltérő színezését.", + "terminal.foreground": "A terminál előtérszíne.", + "terminalCursor.foreground": "A terminál kurzorának előtérszíne.", + "terminalCursor.background": "A terminál kurzorának háttérszíne. Lehetővé teszik az olyan karakterek színének módosítását, amelyek fölött egy blokk-típusú kurzor áll.", + "terminal.ansiColor": "A(z) '{0}' ANSI-szín a terminálban." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..1d00561c57 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "Engedélyezi a(z) {0} (a munkaterületi beállításokban definiálva) terminálról történő indítását?", + "allow": "Engedélyezés", + "disallow": "Tiltás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..884b3750a4 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Keresés", + "placeholder.find": "Keresés", + "label.previousMatchButton": "Előző találat", + "label.nextMatchButton": "Következő találat", + "label.closeButton": "Bezárás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..437ab98627 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "A terminálban nincs semmi kijelölve a másoláshoz", + "terminal.integrated.exitedWithCode": "A terminálfolyamat a következő kilépési kóddal állt le: {0}", + "terminal.integrated.waitOnExit": "A folytatáshoz nyomjon meg egy billentyűt...", + "terminal.integrated.launchFailed": "A(z) `{0}{1}` terminálfolyamat-parancsot nem sikerült elindítani (kilépési kód: {2})" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..f1b0748ff0 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "Hivatkozás megnyitása Alt + kattintás paranccsal", + "terminalLinkHandler.followLinkCmd": "Hivatkozott oldal megnyitása Cmd + kattintás paranccsal", + "terminalLinkHandler.followLinkCtrl": "Hivatkozott oldal megnyitása Ctrl + kattintás paranccsal" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..a4908bce76 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "Másolás", + "createNewTerminal": "Új terminál", + "paste": "Beillesztés", + "selectAll": "Összes kijelölése", + "clear": "Törlés" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..818eafec41 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "Megváltoztathatja az alapértelmezett terminált a testreszabás gomb választásával.", + "customize": "Testreszabás", + "cancel": "Mégse", + "never again": "Rendben, ne jelenítse meg újra", + "terminal.integrated.chooseWindowsShell": "Válassza ki a preferált terminál shellt! Ez később módosítható a beállításokban.", + "terminalService.terminalCloseConfirmationSingular": "Van egy aktív terminálmunkamenet. Szeretné megszakítani?", + "terminalService.terminalCloseConfirmationPlural": "{0} aktív terminálmunkamenet van. Szeretné megszakítani?", + "yes": "Igen" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..f4a0e19f0e --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "Színtéma", + "installColorThemes": "További színtémák telepítése...", + "themes.selectTheme": "Válasszon színtémát! (Előnézet a fel/le billentyűvel.)", + "selectIconTheme.label": "Fájlikontéma", + "installIconThemes": "További fájlikontémák telepítése...", + "noIconThemeLabel": "Nincs", + "noIconThemeDesc": "Fájlikonok letiltása", + "problemChangingIconTheme": "Hiba történt az ikontéma beállítása közben: {0}", + "themes.selectIconTheme": "Válasszon fájlikontémát!", + "generateColorTheme.label": "Színtéma generálása az aktuális beállítások alapján", + "preferences": "Beállítások", + "developer": "Fejlesztői" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..4784476525 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "A munkaterület olyan beállításokat tartalmaz, amelyet csak a felhasználói beállításoknál lehet megadni ({0})", + "openWorkspaceSettings": "Munkaterület beállításainak megnyitása", + "openDocumentation": "További információ", + "ignore": "Figyelmen kívül hagyás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/hun/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..c232214e41 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "Kiadási jegyzék: {0}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..4995dc34b2 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "Kiadási jegyzék", + "updateConfigurationTitle": "Frissítés", + "updateChannel": "Meghatározza, hogy érkeznek-e automatikus frissítések a frissítési csatornáról. A beállítás módosítása után újraindítás szükséges." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/hun/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..43a0ac473e --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "Frissítés most", + "later": "Később", + "unassigned": "nincs hozzárendelve", + "releaseNotes": "Kiadási jegyzék", + "showReleaseNotes": "Kiadási jegyzék megjelenítése", + "downloadNow": "Letöltés most", + "read the release notes": "Üdvözöljük a {0} v{1} verziójában. Szeretné megtekinteni a kiadási jegyzéket?", + "licenseChanged": "A licencfeltételek változtak. Olvassa végig!", + "license": "Licenc elolvasása", + "neveragain": "Soha ne jelenítse meg újra", + "64bitisavailable": "Elérhető a {0} 64-bites Windowsra készült változata!", + "learn more": "További információ", + "updateIsReady": "Új {0}-frissítés érhető el.", + "thereIsUpdateAvailable": "Van elérhető frissítés.", + "updateAvailable": "A {0} frissül az újraindítás után.", + "noUpdatesAvailable": "Jelenleg nincs elérhető frissítés.", + "commandPalette": "Parancskatalógus...", + "settings": "Beállítások", + "keyboardShortcuts": "Billentyűparancsok", + "selectTheme.label": "Színtéma", + "themes.selectIconTheme.label": "Fájlikontéma", + "not available": "A frissítések nem érhetők el", + "checkingForUpdates": "Frissítések keresése...", + "DownloadUpdate": "Elérhető frissítés letöltése", + "DownloadingUpdate": "Frissítés letöltése...", + "InstallingUpdate": "Frissítés telepítése...", + "restartToUpdate": "Újraindítás a frissítéshez...", + "checkForUpdates": "Frissítések keresése..." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/hun/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..3ce517a42e --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} művelet", + "hideView": "Elrejtés az oldalsávról" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/hun/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..65e40f3dc2 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "a nézeteket tömbként kell megadni", + "requirestring": "a(z) `{0}` tulajdonság kötelező és `string` típusúnak kell lennie", + "optstring": "a(z) `{0}` tulajdonság elhagyható vagy `string` típusúnak kell lennie", + "vscode.extension.contributes.view.id": "A nézet azonosítója. Ez használható az adatszolgáltató regisztrálásához a `vscode.window.registerTreeDataProviderForView` API-n keresztül. Ezen túl a kiegészítő aktiválásához regisztrálni kell az `onView:${id}` eseményt az `activationEvents`-nél.", + "vscode.extension.contributes.view.name": "A nézet emberek számára olvasható neve. Meg fog jelenni", + "vscode.extension.contributes.view.when": "A nézet megjelenítésének feltétele", + "vscode.extension.contributes.views": "Nézeteket szolgáltat a szerkesztőhöz", + "views.explorer": "Fájlkezelő-nézet", + "views.debug": "Hibakeresési nézet", + "locationId.invalid": "A(z) `{0}` nem érvényes nézethelyszín" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/hun/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..cd9fc59c31 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "Minden parancs megjelenítése", + "watermark.quickOpen": "Fájl megkeresése", + "watermark.openFile": "Fájl megnyitása", + "watermark.openFolder": "Mappa megnyitása", + "watermark.openFileFolder": "Fájl vagy mappa megnyitása", + "watermark.openRecent": "Legutóbbi megnyitása", + "watermark.newUntitledFile": "Új, névtelen fájl", + "watermark.toggleTerminal": "Terminál be- és kikapcsolása", + "watermark.findInFiles": "Keresés a fájlokban", + "watermark.startDebugging": "Hibakeresés indítása", + "watermark.unboundCommand": "nincs hozzárendelve", + "workbenchConfigurationTitle": "Munkaterület", + "tips.enabled": "Ha engedélyezve van, tippek jelennek meg vízjelként, ha nincs egyetlen szerkesztőablak sem nyitva." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/hun/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..55f98e1ea9 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "Fájlkezelő", + "welcomeOverlay.search": "Keresés a fájlok között", + "welcomeOverlay.git": "Forráskódkezelés", + "welcomeOverlay.debug": "Indítás és hibakeresés", + "welcomeOverlay.extensions": "Kiterjesztések kezelése", + "welcomeOverlay.problems": "Hibák és figyelmeztetések megtekintése", + "welcomeOverlay.commandPalette": "Összes parancs megkeresése és futtatása", + "welcomeOverlay": "Felhasználói felület áttekintése", + "hideWelcomeOverlay": "Felület áttekintésének elrejtése", + "help": "Segítség" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..cc85f63130 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "Szerkesztés, továbbfejlesztve", + "welcomePage.start": "Start", + "welcomePage.newFile": "Új fájl", + "welcomePage.openFolder": "Mappa megnyitása...", + "welcomePage.cloneGitRepository": "Git forráskódtár klónozása...", + "welcomePage.recent": "Legutóbbi", + "welcomePage.moreRecent": "Tovább...", + "welcomePage.noRecentFolders": "Nincsenek megnyitott mappák", + "welcomePage.help": "Segítség", + "welcomePage.keybindingsCheatsheet": "Nyomatható billentyűparancs-referencia", + "welcomePage.introductoryVideos": "Bemutatóvideók", + "welcomePage.tipsAndTricks": "Tippek és trükkök", + "welcomePage.productDocumentation": "Termékdokumentáció", + "welcomePage.gitHubRepository": "GitHub-forráskódtár", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "Üdvözlőlap megjelenítése induláskor", + "welcomePage.customize": "Testreszabás", + "welcomePage.installExtensionPacks": "Eszközök és nyelvek", + "welcomePage.installExtensionPacksDescription": "{0} és {1} fejlesztőkörnyezetek telepítése ", + "welcomePage.moreExtensions": "további", + "welcomePage.installKeymapDescription": "Billentyűparancsok telepítése", + "welcomePage.installKeymapExtension": "{0} és {1} billentyűparancsok telepítése ", + "welcomePage.others": "további", + "welcomePage.colorTheme": "Színtéma", + "welcomePage.colorThemeDescription": "Alakítsa át szeretett szerkesztőjét úgy, ahogyan szeretné!", + "welcomePage.learn": "További információ", + "welcomePage.showCommands": "Összes parancs megkeresése és futtatása", + "welcomePage.showCommandsDescription": "Parancsok gyors listázása és keresése a parancskatalógusban ({0})", + "welcomePage.interfaceOverview": "Felhasználói felület áttekintése", + "welcomePage.interfaceOverviewDescription": "Fedvény, ami vizuálisan bemutatja a felhasználói felület legfőbb részeit.", + "welcomePage.deployToAzure": "Alkalmazások telepítése a felhőbe", + "welcomePage.deployToAzureDescription": "Tudja meg, hogyan telepítheti Node-alkalmazásait az Azure App Service-re", + "welcomePage.interactivePlayground": "Interaktív játszótér", + "welcomePage.interactivePlaygroundDescription": "Próbálja ki a szerkesztő funkcióit egy rövid bemutató keretében!" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..3c8b6027fc --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "Munkaterület", + "workbench.startupEditor.none": "Indítás szerkesztőablak nélkül.", + "workbench.startupEditor.welcomePage": "Üdvözlőlap megnyitása (alapértelmezett).", + "workbench.startupEditor.newUntitledFile": "Új, névtelen fájl megnyitása.", + "workbench.startupEditor": "Meghatározza, melyik szerkesztőablak jelenjen meg induláskor, ha egyetlen sem lett helyreállítva a korábbi munkamenetből. Válassza a 'none' értéket, ha szerkesztőablak nélkül induljon, a 'welcomePage'-t, ha az üdvözlőlap nyíljon meg (alapértelmezett), a 'newUntitledFile'-t, ha egy új, névtelen fájl nyíljon meg (csak egy üres munkaterület nyílik meg).", + "help": "Segítség" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..dccec4e428 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "Üdvözöljük!", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "Azure-kiegészítők megjelenítése", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "A(z) {0}-környezet már telepítve van.", + "welcomePage.willReloadAfterInstallingExtensionPack": "Az ablak újratölt a(z) {0} kiegészítő környezet telepítése után.", + "welcomePage.installingExtensionPack": "{0} kiegészítő környezet telepítése...", + "welcomePage.extensionPackNotFound": "A(z) {1} azonosítójú {0}-környezet nem található.", + "welcomePage.keymapAlreadyInstalled": "A(z) {0} billentyűparancsok már telepítve vannak.", + "welcomePage.willReloadAfterInstallingKeymap": "Az ablak újratölt a(z) {0} billentyűparancsok telepítése után.", + "welcomePage.installingKeymap": "{0} billentyűparancsok telepítése...", + "welcomePage.keymapNotFound": "A(z) {1} azonosítójú {0} billentyűparancsok nem találhatók.", + "welcome.title": "Üdvözöljük!", + "welcomePage.openFolderWithPath": "{1} elérési úton található {0} mappa megnyitása", + "welcomePage.extensionListSeparator": ", ", + "welcomePage.installKeymap": "{0}-billentyűkonfiguráció telepítése", + "welcomePage.installExtensionPack": "{0} kiegészítő környezet telepítése", + "welcomePage.installedKeymap": "A(z) {0}-billenyűkiosztás már telepítve van", + "welcomePage.installedExtensionPack": "A(z) {0}-környezet már telepítve van.", + "ok": "OK", + "details": "Részletek", + "cancel": "Mégse", + "welcomePage.buttonBackground": "Az üdvözlőlapon található gombok háttérszíne", + "welcomePage.buttonHoverBackground": "Az üdvözlőlapon található gombok háttérszíne, amikor a mutató fölöttük áll." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..e5516a9188 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "Interaktív játszótér", + "editorWalkThrough": "Interaktív játszótér" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..d8d3efce5f --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "Interaktív játszótér", + "help": "Segítség", + "interactivePlayground": "Interaktív játszótér" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..aa03914e20 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "Görgetés felfelé (soronként)", + "editorWalkThrough.arrowDown": "Görgetés lefelé (soronként)", + "editorWalkThrough.pageUp": "Görgetés felfelé (oldalanként)", + "editorWalkThrough.pageDown": "Görgetés lefelé (oldalanként)" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..f70c880e54 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "nincs hozzárendelve", + "walkThrough.gitNotFound": "Úgy tűnik, hogy a Git nincs telepítve a rendszerre.", + "walkThrough.embeddedEditorBackground": "Az interaktív játszótér szerkesztőablakainak háttérszíne." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/hun/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..81af8dff34 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "Konfigurációs beállításokat szolgáltat.", + "vscode.extension.contributes.configuration.title": "A beállítások összefoglaló leírása. Ez a címke jelenik meg a beállítások fájlban egy különálló megjegyzésként.", + "vscode.extension.contributes.configuration.properties": "A konfigurációs tulajdonságok leírása.", + "scope.window.description": "Ablakspecifikus beállítás, ami konfigurálható a felhasználói vagy munkaterületi beállításokban.", + "scope.resource.description": "Erőforrásspecifikus beállítás, ami beállítható a felhasználói, munkaterületi és mappaszintű beállításokban.", + "scope.description": "A hatókör, amire a beállítás vonatkozik. Az elérhető hatókörök: `window` és `resource`.", + "invalid.type": "ha meg van adva, a 'configuration.type' értékét egy objektumként kell megadnii", + "invalid.title": "a 'configuration.title' értékét karakterláncként kell megadni", + "vscode.extension.contributes.defaultConfiguration": "Adott nyelvre vonatkozóan szerkesztőbeállításokat szolgáltat.", + "invalid.properties": "A 'configuration.properties' értékét egy objektumként kell megadni", + "workspaceConfig.folders.description": "A munkaterületre betöltött mappák listája. Elérési útnak kell lennie, pl. `/root/folderA` vagy `./folderA` relatív elérési út esetén, ami a munkaterületfájl helye alapján lesz feloldva.", + "workspaceConfig.folder.description": "Egy fájl elérési útja, pl. `/root/folderA` vagy `./folderA` relatív elérési út esetén, ami a munkaterületfájl helye alapján lesz feloldva.", + "workspaceConfig.settings.description": "Munkaterület-beállítások" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/hun/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..5ec939cf02 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Beállítások megnyitása", + "close": "Bezárás", + "saveAndRetry": "Beállítások mentése és újrapróbálkozás", + "errorUnknownKey": "Nem sikerült írni a következőbe: {0}. A(z) {1} nem regisztrált beállítás.", + "errorInvalidFolderConfiguration": "Nem sikerült írni a mappa beállításaiba, mert a(z) {0} nem támogatott mappa típusú erőforrások hatókörében.", + "errorInvalidUserTarget": "Nem sikerült írni a felhasználói beállításokba, mert a(z) {0} nem támogatott globális hatókörben.", + "errorInvalidFolderTarget": "Nem sikerült írni a mappa beállításaiba, mert nincs erőforrás megadva.", + "errorNoWorkspaceOpened": "Nem sikerült írni a következőbe: {0}. Nincs munkaterület megnyitva. Nyisson meg egy munkaterületet, majd próbálja újra!", + "errorInvalidConfiguration": "Nem sikerült írni a beállításokba. Nyissa meg a **Felhasználói beállításokat**, javítsa a hibákat és figyelmeztetéseket a fájlban, majd próbálja újra!", + "errorInvalidConfigurationWorkspace": "Nem sikerült írni a beállításokba. Nyissa meg a **Munkaterült beállításait**, javítsa a hibákat és figyelmeztetéseket a fájlban, majd próbálja újra!", + "errorConfigurationFileDirty": "Nem sikerült írni a beállításokba, mert a fájl módosítva lett. Mentse a **Felhasználói beállítások** fájlt, majd próbálja újra!", + "errorConfigurationFileDirtyWorkspace": "Nem sikerült írni a beállításokba, mert a fájl módosítva lett. Mentse a **Munkaterület beállításai** fájlt, majd próbálja újra!", + "userTarget": "Felhasználói beállítások", + "workspaceTarget": "Munkaterület-beállítások", + "folderTarget": "Mappabeálíltások" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/hun/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..dd828a48cf --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "Nem sikerült írni a fájlba. Nyissa meg a fájlt, javítsa a hibákat és figyelmeztetéseket a fájlban, majd próbálja újra!", + "errorFileDirty": "Nem sikerült írni a fájlba, mert a fájl módosítva lett. Mentse a fájlt, majd próbálja újra!" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/hun/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..a87b167864 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetria", + "telemetry.enableCrashReporting": "Összeomlási jelentések küldésének engedélyezése a Microsofthoz.\nA beállítás érvénybe lépéséhez újraindítás szükséges." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/hun/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..8091275893 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "A kiegészítő gazdafolyamata nem idult el 10 másodperben belül. Elképzelhető, hogy megállt az első soron, és szüksége van a hibakeresőre a folytatáshoz.", + "extensionHostProcess.startupFail": "A kiegészítő gazdafolyamata nem idult el 10 másodperben belül. Ez probléma lehet.", + "extensionHostProcess.error": "A kiegészítő gazdafolyamatától hiba érkezett: {0}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..ab0de4efaf --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Hiba a(z) {0} feldolgozása közben: {1}.", + "fileReadFail": "A(z) ({0}) fájl nem olvasható: {1}.", + "jsonsParseFail": "Hiba a(z) {0} vagy {1} feldolgozása közben: {2}.", + "missingNLSKey": "A(z) {0} kulcshoz tartozó üzenet nem található." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..8e3ff13793 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "Fejlesztői eszközök", + "restart": "Kiegészítő gazdafolyamatának újraindítása", + "extensionHostProcess.crash": "A kiegészítő gazdafolyamata váratlanul leállt.", + "extensionHostProcess.unresponsiveCrash": "A kiegészítő gazdafolyamata le lett állítva, mert nem válaszolt.", + "overwritingExtension": "A(z) {0} kiegészítő felülírása a következővel: {1}.", + "extensionUnderDevelopment": "A(z) {0} elérési úton található fejlesztői kiegészítő betöltése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/hun/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..d88d35f252 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "A működéshez Microsoft .NET-keretrendszer 4.5 szükséges. A telepítéshez kövesse az alábbi hivatkozást!", + "installNet": ".NET Framework 4.5 letöltése", + "neverShowAgain": "Ne jelenítse meg újra", + "trashFailed": "A(z) {0} kukába helyezése nem sikerült" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/hun/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..838c08b22c --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "Érvénytelen fájlerőforrás ({0})", + "fileIsDirectoryError": "A fájl egy kövtár ({0})", + "fileNotModifiedError": "A fájl azóta nem módosult", + "fileTooLargeError": "A fájl túl nagy a megnyitáshoz", + "fileBinaryError": "A fájl binárisnak tűnik és nem nyitható meg szövegként", + "fileNotFoundError": "Fájl nem található ({0})", + "fileMoveConflict": "Nem lehet áthelyezni vagy másolni. A fájl már létezik a célhelyen.", + "unableToMoveCopyError": "Nem lehet áthelyezni vagy másolni. A fájl felülírná a mappát, amiben található.", + "foldersCopyError": "A munkaterületre nem másolhatók mappák. Válasszon ki egyedi fájlokat a másoláshoz.", + "fileModifiedError": "A fájl azóta módosult", + "fileReadOnlyError": "A fájl csak olvasható" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/hun/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..7bf016dbe3 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "Nem lehet írni, mert a fájl módosult. Mentse a **Billentyűparancsok** fájlt, majd próbálja újra.", + "parseErrors": "Nem lehet írni a billentyűparancsokat. Nyissa meg a**Billentyűparancsok fájl**t, javítsa a benne található hibákat vagy figyelmeztetéseket, majd próbálja újra.", + "errorInvalidConfiguration": "Nem lehet írni a billentyűparancsokat. A **Billentyűparancsok fájlban** vagy egy objektum, ami nem tömb típusú. Nyissa meg a fájlt a helyreállításhoz, majd próbálja újra.", + "emptyKeybindingsHeader": "Az ebben a fájlban elhelyezett billentyűparancsok felülírják az alapértelmezett beállításokat" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/hun/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..c7e131111d --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "az érték nem lehet üres.", + "requirestring": "a(z) `{0}` tulajdonság kötelező és `string` típusúnak kell lennie", + "optstring": "a(z) `{0}` tulajdonság elhagyható vagy `string` típusúnak kell lennie", + "vscode.extension.contributes.keybindings.command": "A billentyűparancs aktiválása esetén futtatandó parancs azonosítója.", + "vscode.extension.contributes.keybindings.key": "Billenty vagy billentyűparancs (különálló billentyűk plusz jellel és sorozatok szóközzel, pl.: Crtl + O és Ctrl+L L).", + "vscode.extension.contributes.keybindings.mac": "Mac-specifikus billentyű vagy billentyűsorozat.", + "vscode.extension.contributes.keybindings.linux": "Linux-specifikus billentyű vagy billentyűsorozat.", + "vscode.extension.contributes.keybindings.win": "Windows-specifikus billentyű vagy billentyűsorozat.", + "vscode.extension.contributes.keybindings.when": "A billentyűparancs aktiválási feltétele.", + "vscode.extension.contributes.keybindings": "Billentyűparancsok kezelését teszi lehetővé.", + "invalid.keybindings": "Érvénytelen `contributes.{0}`: {1}", + "unboundCommands": "A további elérhető parancsok a következők: ", + "keybindings.json.title": "Billentyűparancsok konfigurációja", + "keybindings.json.key": "Billentyű vagy billentyűsorozat (szóközzel elválasztva)", + "keybindings.json.command": "A végrehajtandó parancs neve", + "keybindings.json.when": "A billentyűparancs aktiválási feltétele.", + "keybindings.json.args": "A végrehajtandó parancs számára átadott argumentumok", + "keyboardConfigurationTitle": "Billentyűzet", + "dispatch": "Meghatározza, hogy a billentyűleütések észleléséhez a `keydown.code` (ajánlott) vagy `keydown.keyCode` esemény legyen használva." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/hun/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..413321b966 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Hiba: {0}", + "alertWarningMessage": "Figyelmeztetés: {0}", + "alertInfoMessage": "Információ: {0}", + "error": "Hiba", + "warning": "Figyelmeztetés", + "info": "Információ", + "close": "Bezárás" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/hun/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..3788aff226 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "&&Igen", + "cancelButton": "Mégse" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/hun/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..9dee303c7a --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Nyelvdeklarációkat definiál.", + "vscode.extension.contributes.languages.id": "A nyelv azonosítója", + "vscode.extension.contributes.languages.aliases": "A nyelv kiegészítő nevei.", + "vscode.extension.contributes.languages.extensions": "A nyelvhez hozzárendelt fájlkiterjesztések.", + "vscode.extension.contributes.languages.filenames": "A nyelvhez hozzárendelt fájlnevek.", + "vscode.extension.contributes.languages.filenamePatterns": "A nyelvhez hozzárendelt globális minták.", + "vscode.extension.contributes.languages.mimetypes": "A nyelvhez hozzárendelt MIME-típusok.", + "vscode.extension.contributes.languages.firstLine": "Reguláris kifejezés, ami az adott nyelven írt fájl első sorára illeszkedik.", + "vscode.extension.contributes.languages.configuration": "A nyelvhez tartozó konfigurációkat tartalmazó fájl relatív elérési útja.", + "invalid": "Érvénytelen `contributes.{0}`: a várt érték egy tömb.", + "invalid.empty": "A `contributes.{0}` értéke üres", + "require.id": "a(z) `{0}` tulajdonság kötelező és `string` típusúnak kell lennie", + "opt.extensions": "a(z) `{0}` tulajdonság elhagyható és `string[]` típusúnak kell lennie", + "opt.filenames": "a(z) `{0}` tulajdonság elhagyható és `string[]` típusúnak kell lennie", + "opt.firstLine": "a(z) `{0}` tulajdonság elhagyható és `string` típusúnak kell lennie", + "opt.configuration": "a(z) `{0}` tulajdonság elhagyható és `string` típusúnak kell lennie", + "opt.aliases": "a(z) `{0}` tulajdonság elhagyható és `string[]` típusúnak kell lennie", + "opt.mimetypes": "a(z) `{0}` tulajdonság elhagyható és `string[]` típusúnak kell lennie" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/hun/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..df1adc4288 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} – {1}", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/hun/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..26a2906b3f --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "TextMate-tokenizálókat szolgáltat.", + "vscode.extension.contributes.grammars.language": "Annak a nyelvnek az azonosítója, amely számára szolgáltatva van ez a szintaxis.", + "vscode.extension.contributes.grammars.scopeName": "A tmLanguage-fájl által használt TextMate-hatókör neve.", + "vscode.extension.contributes.grammars.path": "A tmLanguage-fájl elérési útja. Az elérési út relatív a kiegészítő mappájához képest, és általában './syntaxes/'-zal kezdődik.", + "vscode.extension.contributes.grammars.embeddedLanguages": "Hatókörnév-nyelvazonosító kulcs-érték párokat tartalmazó objektum, ha a nyelvtan tartalmaz beágyazott nyelveket.", + "vscode.extension.contributes.grammars.injectTo": "Azon nyelvi hatókörök nevei, ahová be lesz ágyazva ez a nyelvtan." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/hun/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..a24b57115c --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Ismeretlen nyelv található a következőben: `contributes.{0}.language`. A megadott érték: {1}", + "invalid.scopeName": "Hiányzó karakterlánc a `contributes.{0}.scopeName`-ben. A megadott érték: {1}", + "invalid.path.0": "Hiányzó karakterlánc a `contributes.{0}.path`-ban. A megadott érték: {1}", + "invalid.injectTo": "A `contributes.{0}.injectTo` értéke érvénytelen. Az értéke egy tömb lehet, ami nyelvhatókörök neveit tartalmazza. A megadott érték: {1}", + "invalid.embeddedLanguages": "A `contributes.{0}.embeddedLanguages` értéke érvénytelen. Az értéke egy hatókörnév-nyelv kulcs-érték párokat tartalmazó objektum lehet. A megadott érték: {1}", + "invalid.path.1": "A `contributes.{0}.path` ({1}) nem a kiegészítő mappáján belül található ({2}). Emiatt előfordulhat, hogy a kiegészítő nem lesz hordozható.", + "no-tm-grammar": "Nincs TM Grammar regisztrálva ehhez a nyelvhez." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/hun/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..ba2d39d72b --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "A fájl módosítva lett. Mentse, mielőtt megnyitná egy másik kódolással.", + "genericSaveError": "Hiba a(z) {0} mentése közben ({1})." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/hun/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..23584475e4 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "A fájlokról nem sikerült biztonsági másolatot készíteni (Hiba: {0}). Próbálja meg menteni a fájlokat a kilépéshez." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/hun/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..eff4e7a3b1 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "Szeretné menteni a(z) {0} fájlban elvégzett módosításokat?", + "saveChangesMessages": "Szeretné menteni a következő {0} fájlban elvégzett módosításokat?", + "moreFile": "...1 további fájl nincs megjelenítve", + "moreFiles": "...{0} további fájl nincs megjelenítve", + "saveAll": "Ö&&sszes mentése", + "save": "Menté&&s", + "dontSave": "&&Ne mentse", + "cancel": "Mégse", + "saveChangesDetail": "A módosítások elvesznek, ha nem menti őket.", + "allFiles": "Összes fájl", + "noExt": "Nincs kiterjesztés" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/hun/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..092ebb4e4e --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "A token színe és stílusa.", + "schema.token.foreground": "A token előtérszíne.", + "schema.token.fontStyle": "A szabály betűtípusának stílusa: 'italic', 'bold', 'underline', vagy ezek kombinációja", + "schema.fontStyle.error": "A betűtípus stílusa 'italic', 'bold', 'underline', vagy ezek kombinációja lehet.", + "schema.properties.name": "A szabály leírása.", + "schema.properties.scope": "Hatókörszelektor, amire ez a szabály illeszkedik.", + "schema.tokenColors.path": "Egy tmTheme-fájl elérési útja (az aktuális fájlhoz képest relatívan).", + "schema.colors": "A szintaktikai kiemeléshez használt színek" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/hun/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..5169c004a7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "Kinyitott mappánál használt ikon. A kinyitott mappa ikonját nem kötelező megadni. Ha nincs megadva, akkor a mappaikon lesz megjelenítve.", + "schema.folder": "A bezárt mappák ikonja, illetve ha a folderExpanded nincs megadva, akkor a kinyitott mappáké is.", + "schema.file": "Az alapértelmezett fájlikon, ami minden olyan fájlnál megjelenik, ami nem illeszkedik egyetlen kiterjesztésre, fájlnévre vagy nyelvazonosítóra sem.", + "schema.folderNames": "Ikonokat társít mappanevekhez. Az objektum kulcsa a mappa neve elérési útvonalrészletek nélkül. Nem tartalmazhat mintákat és helyettesítő karaktereket. A mappa nevének vizsgálatánál a kis- és nagybetűk nincsenek megkülönböztetve.", + "schema.folderName": "A társításhoz tartozó ikondefiníció azonosítója.", + "schema.folderNamesExpanded": "Ikonokat társít mappanevekhez kinyitott mappák esetén. Az objektum kulcsa a mappa neve elérési útvonalrészletek nélkül. Nem tartalmazhat mintákat és helyettesítő karaktereket. A mappa nevének vizsgálatánál a kis- és nagybetűk nincsenek megkülönböztetve.", + "schema.folderNameExpanded": "A társításhoz tartozó ikondefiníció azonosítója. ", + "schema.fileExtensions": "Ikonokat társít fájlkiterjesztésekhez. Az objektum kulcsa a fájlkiterjesztés neve. A kiterjesztés neve a fájl nevének utolsó része az utolsó pont után (a pont nélkül). A kiterjesztések vizsgálatánál a kis- és nagybetűk nincsenek megkülönböztetve. ", + "schema.fileExtension": "A társításhoz tartozó ikondefiníció azonosítója. ", + "schema.fileNames": "Ikonokat társít fájlnevekhez. Az objektum kulcsa a fájl teljes neve, az elérési út többi része nélkül. A fájlnév tartalmazhat pontokat és fájlkiterjesztést. Nem tartalmazhat mintákat és helyettesítő karaktereket. A fájlnevek vizsgálatánál a kis- és nagybetűk nincsenek megkülönböztetve.", + "schema.fileName": "A társításhoz tartozó ikondefiníció azonosítója. ", + "schema.languageIds": "Ikonokat társít nyelvekhez. Az objektum kulcsa a nyelvet szolgáltató komponens által definiált nyelvazonosító.", + "schema.languageId": "A társításhoz tartozó ikondefiníció azonosítója. ", + "schema.fonts": "Az ikondefiníciókban használt betűkészletek.", + "schema.id": "A betűkészlet azonosítója.", + "schema.src": "A betűkészlet elérési útjai.", + "schema.font-path": "A betűkészlet elérési útja, relatívan az aktuális ikontémafájlhoz képest.", + "schema.font-format": "A betűkészlet formátuma.", + "schema.font-weight": "A betűkészlet betűvastagsága.", + "schema.font-sstyle": "A betűkészlet stílusa.", + "schema.font-size": "A betűkészlet alapértelmezett mérete.", + "schema.iconDefinitions": "A fájlok ikonokhoz történő rendelésénél használható ikonok leírása.", + "schema.iconDefinition": "Egy ikondefiníció. Az objektum kulcsa a definíció azonosítója.", + "schema.iconPath": "SVG vagy PNG használata esetén a kép elérési útja. Az elérési út relatív az ikonkészletfájlhoz képest.", + "schema.fontCharacter": "Betűkészlet használata esetén a betűkészletből használandó karakter.", + "schema.fontColor": "Betűkészlet használata esetén a használt szín.", + "schema.fontSize": "Betűkészlet használata esetén a betűkészlet mérete a szöveg betűkészletének méretéhez képest, százalékban. Ha nincs megadva, akkor a betűkészlet-definícióban megadott érték van használva.", + "schema.fontId": "Betűkészlet használata esetén a betűkészlet azonosítója. Ha nincs megadva, akkor az első betűkészlet-definíció van használva.", + "schema.light": "Fájlikon-társítások világos témák használata esetén. Nem kötelező megadni.", + "schema.highContrast": "Fájlikon-társítások nagy kontrasztú témák használata esetén. Nem kötelező megadni." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/hun/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..9824dfdf42 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "Hiba a JSON témafájl feldolgozása közben: {0}", + "error.invalidformat.colors": "Hiba a színtémafájl feldolgozása közben: {0}. A 'colors' értéke nem 'object' típusú.", + "error.invalidformat.tokenColors": "Hiba a színtémafájl feldolgozása közben: {0}. A 'tokenColors' tulajdonság vagy egy színeket tartalmazó tömb legyen vagy egy TextMate témafájl elérési útja.", + "error.plist.invalidformat": "Hiba a tmTheme-fájl feldolgozása közben: {0}. A 'settings' nem egy tömb.", + "error.cannotparse": "Hiba a tmTheme-fájl feldolgozása közben: {0}", + "error.cannotload": "Hiba a(z) {0} tmTheme fájl betöltése közben: {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/hun/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..030f81ffea --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "TextMate-színtémákat szolgáltat.", + "vscode.extension.contributes.themes.id": "A téma felhasználói beállításokban használt azonosítója.", + "vscode.extension.contributes.themes.label": "A színtéma felhasználói felületen megjelenő neve.", + "vscode.extension.contributes.themes.uiTheme": "A szerkesztőablak körül megjelenő elemek alaptémája. A 'vs' a világos, a 'vs-dark' a sötét színtéma, a 'hc-black' pedig a sötét, nagy kontrasztú téma.", + "vscode.extension.contributes.themes.path": "A tmTheme-fájl elérési útja. Az elérési út relatív a kiegészítő mappájához képest, és általában './themes/themeFile.tmTheme'.", + "vscode.extension.contributes.iconThemes": "Fájlikontémákat szolgáltat.", + "vscode.extension.contributes.iconThemes.id": "Az ikontéma felhasználói beállításokban használt azonosítója.", + "vscode.extension.contributes.iconThemes.label": "Az ikontéma felhasználói felületen megjelenő neve.", + "vscode.extension.contributes.iconThemes.path": "A témadefiníciós fájl elérési útja. Az elérési út relatív a kiegészítő mappájához képest, és általában ./icons/awesome-icon-theme.json'.", + "migration.completed": "Új témabeállítások lettek hozzáadva a felhasználói beállításokhoz. Biztonsági mentés a következő helyen érhető el: {0}.", + "error.cannotloadtheme": "Nem sikerült betölteni a(z) '{0}' témát: {1}.", + "reqarray": "a(z) `{0}` kiegszítési pontot tömbként kell megadni", + "reqpath": "Hiányzó karakterlánc a `contributes.{0}.path`-ban. A megadott érték: {1}", + "invalid.path.1": "A `contributes.{0}.path` ({1}) nem a kiegészítő mappáján belül található ({2}). Emiatt előfordulhat, hogy a kiegészítő nem lesz hordozható.", + "reqid": "Hiányzó karakterlánc a `contributes.{0}.id`-ben. A megadott érték: {1}", + "error.cannotloadicontheme": "Nem sikerült megnyitni a(z) '{0}' témát", + "error.cannotparseicontheme": "Hiba a fájlikonokat leíró fájl feldolgozása közben: {0}", + "colorTheme": "Meghatározza a munkaterületen használt színtémát.", + "colorThemeError": "A téma ismeretlen vagy nincs telepítve.", + "iconTheme": "Meghatározza a munkaterületen használt ikontémát. 'null' érték esetén nem jelenik meg egyetlen fájlikon sem.", + "noIconThemeDesc": "Nincsenek fájlikonok", + "iconThemeError": "A fájlikontéma ismeretlen vagy nincs telepítve.", + "workbenchColors": "Felülírja az aktuális színtémában definiált színeket.", + "workbenchColors.deprecated": "A beállítás már nem kísérleti, és át lett nevezve 'workbench.colorCustomizations'-re.", + "workbenchColors.deprecatedDescription": "Használja a 'workbench.colorCustomizations' tulajdonságot helyette.", + "editorColors": "Felülírja az aktuális színtémában definiált, szerkesztőablakhoz kapcsolódó színeket és betűstílusokat.", + "editorColors.comments": "Meghatározza a megjegyzések színét és stílusát.", + "editorColors.strings": "Meghatározza a sztringliterálok színét és stílusát.", + "editorColors.keywords": "Meghatározza a kulcsszavak színét és stílusát.", + "editorColors.numbers": "Meghatározza a számliterálok színét és stílusát.", + "editorColors.types": "Meghatározza a típusdeklarációk és -referenciák színét és stílusát.", + "editorColors.functions": "Meghatározza a függvénydeklarációk és -referenciák színét és stílusát.", + "editorColors.variables": "Meghatározza a változódeklarációk és -referenciák színét és stílusát.", + "editorColors.textMateRules": "Színek és stílusok beállítása textmate témázási szabályok alapján (haladó)." +} \ No newline at end of file diff --git a/i18n/ita/extensions/configuration-editing/out/extension.i18n.json b/i18n/ita/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..b64236bc23 --- /dev/null +++ b/i18n/ita/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "Esempio" +} \ No newline at end of file diff --git a/i18n/ita/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/ita/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..7d9710d12c --- /dev/null +++ b/i18n/ita/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "ad esempio myFile.txt", + "activeEditorMedium": "ad esempio myFolder/myFile.txt", + "activeEditorLong": "ad esempio /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "ad es. myFolder1, myFolder2, myFolder3", + "rootPath": "ad esempio /Users/Development/myProject", + "folderName": "ad es. myFolder", + "folderPath": "ad es. /Users/Development/myFolder", + "appName": "ad esempio VS Code", + "dirty": "un indicatore dirty se l'editor attivo è dirty", + "separator": "un separatore condizionale (' - ') visualizzato solo se circondato da variabili con valori", + "assocLabelFile": "File con estensione", + "assocDescriptionFile": "Esegue il mapping di tutti i file il cui nome file corrisponde al criterio GLOB alla lingua con l'identificatore specificato.", + "assocLabelPath": "File con percorso", + "assocDescriptionPath": "Esegue il mapping di tutti i file il cui percorso assoluto corrisponde al criterio GLOB alla lingua con l'identificatore specificato.", + "fileLabel": "File in base all'estensione", + "fileDescription": "Trova tutti i file di un'estensione di file specifica.", + "filesLabel": "File con più estensioni", + "filesDescription": "Trova tutti i file con qualsiasi estensione di file.", + "derivedLabel": "File con elementi di pari livello in base al nome", + "derivedDescription": "Trova file con elementi di pari livello e nome identico ma estensione diversa.", + "topFolderLabel": "Cartella in base al nome (primo livello)", + "topFolderDescription": "Trova una cartella di primo livello con un nome specifico.", + "topFoldersLabel": "Cartella con più nomi (primo livello)", + "topFoldersDescription": "Trova più cartelle di primo livello.", + "folderLabel": "Cartella in base al nome (qualsiasi percorso)", + "folderDescription": "Trova una cartella con un nome specifico in qualsiasi percorso.", + "falseDescription": "Disabilita il criterio.", + "trueDescription": "Abilita il criterio.", + "siblingsDescription": "Trova file con elementi di pari livello e nome identico ma estensione diversa.", + "languageSpecificEditorSettings": "Impostazioni dell'editor specifiche del linguaggio", + "languageSpecificEditorSettingsDescription": "Esegue l'override delle impostazioni dell'editor per il linguaggio" +} \ No newline at end of file diff --git a/i18n/ita/extensions/css/client/out/cssMain.i18n.json b/i18n/ita/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..2cab5cf618 --- /dev/null +++ b/i18n/ita/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "Server di linguaggio CSS" +} \ No newline at end of file diff --git a/i18n/ita/extensions/css/package.i18n.json b/i18n/ita/extensions/css/package.i18n.json new file mode 100644 index 0000000000..d129704370 --- /dev/null +++ b/i18n/ita/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "Numero di parametri non valido", + "css.lint.boxModel.desc": "Non usare width o height con padding o border", + "css.lint.compatibleVendorPrefixes.desc": "Quando si usa un prefisso specifico del fornitore, assicurarsi di includere anche tutte le altre proprietà specifiche del fornitore", + "css.lint.duplicateProperties.desc": "Non usare definizioni di stile duplicate", + "css.lint.emptyRules.desc": "Non usare set di regole vuoti", + "css.lint.float.desc": "Evitare di usare 'float'. Con gli elementi float si ottiene codice CSS che causa facilmente interruzioni in caso di modifica di un aspetto del layout.", + "css.lint.fontFaceProperties.desc": "La regola @font-face deve definire le proprietà 'src' e 'font-family'", + "css.lint.hexColorLength.desc": "I colori esadecimali devono essere composti da tre o sei numeri esadecimali", + "css.lint.idSelector.desc": "I selettori non devono contenere ID perché queste regole sono strettamente accoppiate al codice HTML.", + "css.lint.ieHack.desc": "Gli hack IE sono necessari solo per il supporto di IE7 e versioni precedenti", + "css.lint.important.desc": "Evitare di usare !important perché indica che la specificità dell'intero codice CSS non è più controllabile ed è necessario effettuarne il refactoring.", + "css.lint.importStatement.desc": "Le istruzioni Import non vengono caricate in parallelo", + "css.lint.propertyIgnoredDueToDisplay.desc": "La proprietà viene ignorata a causa della visualizzazione. Ad esempio, con 'display: inline', le proprietà width, height, margin-top, margin-bottom e float non hanno effetto", + "css.lint.universalSelector.desc": "Il selettore universale (*) è notoriamente lento", + "css.lint.unknownProperties.desc": "Proprietà sconosciuta.", + "css.lint.unknownVendorSpecificProperties.desc": "Proprietà specifica del fornitore sconosciuta.", + "css.lint.vendorPrefix.desc": "Quando si usa un prefisso specifico del fornitore, includere anche la proprietà standard", + "css.lint.zeroUnits.desc": "Non è necessaria alcuna unità per lo zero", + "css.trace.server.desc": "Traccia la comunicazione tra VS Code e il server del linguaggio CSS.", + "css.validate.title": "Controlla la convalida CSS e le gravità dei problemi.", + "css.validate.desc": "Abilita o disabilita tutte le convalide", + "less.lint.argumentsInColorFunction.desc": "Numero di parametri non valido", + "less.lint.boxModel.desc": "Non usare width o height con padding o border", + "less.lint.compatibleVendorPrefixes.desc": "Quando si usa un prefisso specifico del fornitore, assicurarsi di includere anche tutte le altre proprietà specifiche del fornitore", + "less.lint.duplicateProperties.desc": "Non usare definizioni di stile duplicate", + "less.lint.emptyRules.desc": "Non usare set di regole vuoti", + "less.lint.float.desc": "Evitare di usare 'float'. Con gli elementi float si ottiene codice CSS che causa facilmente interruzioni in caso di modifica di un aspetto del layout.", + "less.lint.fontFaceProperties.desc": "La regola @font-face deve definire le proprietà 'src' e 'font-family'", + "less.lint.hexColorLength.desc": "I colori esadecimali devono essere composti da tre o sei numeri esadecimali", + "less.lint.idSelector.desc": "I selettori non devono contenere ID perché queste regole sono strettamente accoppiate al codice HTML.", + "less.lint.ieHack.desc": "Gli hack IE sono necessari solo per il supporto di IE7 e versioni precedenti", + "less.lint.important.desc": "Evitare di usare !important perché indica che la specificità dell'intero codice CSS non è più controllabile ed è necessario effettuarne il refactoring.", + "less.lint.importStatement.desc": "Le istruzioni Import non vengono caricate in parallelo", + "less.lint.propertyIgnoredDueToDisplay.desc": "La proprietà viene ignorata a causa della visualizzazione. Ad esempio, con 'display: inline', le proprietà width, height, margin-top, margin-bottom e float non hanno effetto", + "less.lint.universalSelector.desc": "Il selettore universale (*) è notoriamente lento", + "less.lint.unknownProperties.desc": "Proprietà sconosciuta.", + "less.lint.unknownVendorSpecificProperties.desc": "Proprietà specifica del fornitore sconosciuta.", + "less.lint.vendorPrefix.desc": "Quando si usa un prefisso specifico del fornitore, includere anche la proprietà standard", + "less.lint.zeroUnits.desc": "Non è necessaria alcuna unità per lo zero", + "less.validate.title": "Controlla la convalida LESS e le gravità dei problemi.", + "less.validate.desc": "Abilita o disabilita tutte le convalide", + "scss.lint.argumentsInColorFunction.desc": "Numero di parametri non valido", + "scss.lint.boxModel.desc": "Non usare width o height con padding o border", + "scss.lint.compatibleVendorPrefixes.desc": "Quando si usa un prefisso specifico del fornitore, assicurarsi di includere anche tutte le altre proprietà specifiche del fornitore", + "scss.lint.duplicateProperties.desc": "Non usare definizioni di stile duplicate", + "scss.lint.emptyRules.desc": "Non usare set di regole vuoti", + "scss.lint.float.desc": "Evitare di usare 'float'. Con gli elementi float si ottiene codice CSS che causa facilmente interruzioni in caso di modifica di un aspetto del layout.", + "scss.lint.fontFaceProperties.desc": "La regola @font-face deve definire le proprietà 'src' e 'font-family'", + "scss.lint.hexColorLength.desc": "I colori esadecimali devono essere composti da tre o sei numeri esadecimali", + "scss.lint.idSelector.desc": "I selettori non devono contenere ID perché queste regole sono strettamente accoppiate al codice HTML.", + "scss.lint.ieHack.desc": "Gli hack IE sono necessari solo per il supporto di IE7 e versioni precedenti", + "scss.lint.important.desc": "Evitare di usare !important perché indica che la specificità dell'intero codice CSS non è più controllabile ed è necessario effettuarne il refactoring.", + "scss.lint.importStatement.desc": "Le istruzioni Import non vengono caricate in parallelo", + "scss.lint.propertyIgnoredDueToDisplay.desc": "La proprietà viene ignorata a causa della visualizzazione. Ad esempio, con 'display: inline', le proprietà width, height, margin-top, margin-bottom e float non hanno effetto", + "scss.lint.universalSelector.desc": "Il selettore universale (*) è notoriamente lento", + "scss.lint.unknownProperties.desc": "Proprietà sconosciuta.", + "scss.lint.unknownVendorSpecificProperties.desc": "Proprietà specifica del fornitore sconosciuta.", + "scss.lint.vendorPrefix.desc": "Quando si usa un prefisso specifico del fornitore, includere anche la proprietà standard", + "scss.lint.zeroUnits.desc": "Non è necessaria alcuna unità per lo zero", + "scss.validate.title": "Controlla la convalida SCSS e le gravità dei problemi.", + "scss.validate.desc": "Abilita o disabilita tutte le convalide", + "less.colorDecorators.enable.desc": "Abilita o disabilita gli elementi Decorator di tipo colore", + "scss.colorDecorators.enable.desc": "Abilita o disabilita gli elementi Decorator di tipo colore", + "css.colorDecorators.enable.desc": "Abilita o disabilita gli elementi Decorator di tipo colore", + "css.colorDecorators.enable.deprecationMessage": "L'impostazione `css.colorDecorators.enable` è stata deprecata e sostituita da `editor.colorDecorators`.", + "scss.colorDecorators.enable.deprecationMessage": "L'impostazione `scss.colorDecorators.enable` è stata deprecata e sostituita da `editor.colorDecorators`.", + "less.colorDecorators.enable.deprecationMessage": "L'impostazione `less.colorDecorators.enable` è stata deprecata e sostituita da `editor.colorDecorators`." +} \ No newline at end of file diff --git a/i18n/ita/extensions/emmet/package.i18n.json b/i18n/ita/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..23b835d337 --- /dev/null +++ b/i18n/ita/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "Esegui il wrapping con l'abbreviazione", + "command.wrapIndividualLinesWithAbbreviation": "Esegui il wrapping di singole righe con l'abbreviazione", + "command.removeTag": "Rimuovi Tag", + "command.updateTag": "Aggiorna tag", + "command.matchTag": "Vai alla coppia corrispondente", + "command.balanceIn": "Corrispondenza (interna)", + "command.balanceOut": "Corrispondenza (esterna)", + "command.prevEditPoint": "Vai al punto di modifica precedente", + "command.nextEditPoint": "Vai al punto di modifica successivo", + "command.mergeLines": "Esegui merge delle righe", + "command.selectPrevItem": "Seleziona l'elemento precedente", + "command.selectNextItem": "Seleziona l'elemento successivo", + "command.splitJoinTag": "Dividi/Unisci tag", + "command.toggleComment": "Attiva/Disattiva commento", + "command.evaluateMathExpression": "Valuta espressione matematica", + "command.updateImageSize": "Aggiorna dimensioni immagine", + "command.reflectCSSValue": "Ricopia il valore CSS", + "command.incrementNumberByOne": "Aumenta di 1", + "command.decrementNumberByOne": "Riduci di 1", + "command.incrementNumberByOneTenth": "Aumenta di 0,1", + "command.decrementNumberByOneTenth": "Riduci di 0,1", + "command.incrementNumberByTen": "Aumenta di 10", + "command.decrementNumberByTen": "Riduci di 10", + "emmetSyntaxProfiles": "Consente di definire il profilo per la sintassi specificata oppure di usare un profilo personalizzato con regole specifiche.", + "emmetExclude": "Matrice di linguaggi in cui le abbreviazioni Emmet non devono essere espanse.", + "emmetExtensionsPath": "Percorso di una cartella contenente snippet e profili Emmet.'", + "emmetShowExpandedAbbreviation": "Mostra le abbreviazioni emmet espanse come suggerimenti.\nL'opzione \"inMarkupAndStylesheetFilesOnly\" si applica a html, haml, jade, slim, xml, xsl, css, scss, sass, less e stylus.\nL'opzione \"sempre\" (always) si applica a tutte le parti del file indipendentemente dal markup/css.", + "emmetShowAbbreviationSuggestions": "Mostra possibili abbreviazioni emmet come suggerimenti. Non si applica a fogli di stile o quando emmet.showExpandedAbbreviation è impostata a \"mai\" (never).", + "emmetIncludeLanguages": "Abilita le abbreviazioni emmet in linguaggi che non sono normalmente supportati. Aggiungere un mapping tra il linguaggio ed il linguaggio supportato da emmet.\n Ad esempio: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "Variabili da utilizzare nei frammenti di emmet", + "emmetTriggerExpansionOnTab": "Se abilitate, le abbreviazioni Emmet vengono espanse quando si preme TAB.", + "emmetPreferences": "Preferenze usate per modificare il comportamento di alcune azioni e i resolver di Emmet.", + "emmetPreferencesIntUnit": "Unità di misura predefinita per i valori integer", + "emmetPreferencesFloatUnit": "Unità di misura predefinita per i valori float", + "emmetPreferencesCssAfter": "Simbolo da inserire alla fine della proprietà CSS quando si espandono le abbreviazioni CSS", + "emmetPreferencesSassAfter": "Simbolo da inserire alla fine della proprietà CSS quando si espandono le abbreviazioni CSS nei file Sass", + "emmetPreferencesStylusAfter": "Simbolo da inserire alla fine della proprietà CSS quando si espandono le abbreviazioni CSS nei file Stylus", + "emmetPreferencesCssBetween": "Simbolo da inserire tra la proprietà CSS e il valore quando si espandono le abbreviazioni CSS", + "emmetPreferencesSassBetween": "Simbolo da inserire tra la proprietà CSS e il valore quando si espandono le abbreviazioni CSS nei file Sass", + "emmetPreferencesStylusBetween": "Simbolo da inserire tra la proprietà CSS e il valore quando si espandono le abbreviazioni CSS nei file Stylus", + "emmetShowSuggestionsAsSnippets": "Se è true, i suggerimenti Emmet verranno visualizzati come frammenti consentendo di ordinarli in base all'impostazione editor.snippetSuggestions." +} \ No newline at end of file diff --git a/i18n/ita/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/ita/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..f6c55eabc7 --- /dev/null +++ b/i18n/ita/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "Le immagini devono utilizzare il protocollo HTTPS.", + "svgsNotValid": "Immagini di tipo SVG non sono una fonte valida.", + "embeddedSvgsNotValid": "Immagini SVG incorporate non sono una fonte valida.", + "dataUrlsNotValid": "URL di dati non sono una fonte valida per le immagini.", + "relativeUrlRequiresHttpsRepository": "Immagini con URL relative richiedono di specificare un repository con protocollo HTTPS in package.json.", + "relativeIconUrlRequiresHttpsRepository": "Un'icona richiede di specificare un repository con protocollo HTTPS in questo package.json.", + "relativeBadgeUrlRequiresHttpsRepository": "Notifiche con URL relativo richiedono di specificare un repository con protocollo HTTPS in questo package.json." +} \ No newline at end of file diff --git a/i18n/ita/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/ita/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..9de1d83f9c --- /dev/null +++ b/i18n/ita/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "Impostazioni dell'editor specifiche del linguaggio", + "languageSpecificEditorSettingsDescription": "Esegue l'override delle impostazioni dell'editor per il linguaggio" +} \ No newline at end of file diff --git a/i18n/ita/extensions/git/out/askpass-main.i18n.json b/i18n/ita/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..28d40131e8 --- /dev/null +++ b/i18n/ita/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "Credenziali mancanti o non valide." +} \ No newline at end of file diff --git a/i18n/ita/extensions/git/out/commands.i18n.json b/i18n/ita/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..471b36c3ed --- /dev/null +++ b/i18n/ita/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "Tag in {0}", + "remote branch at": "Ramo remoto in {0}", + "create branch": "$(plus) Crea nuovo branch", + "repourl": "URL del repository", + "parent": "Directory padre", + "cloning": "Clonazione del repository GIT...", + "openrepo": "Apri repository", + "proposeopen": "Aprire il repository clonato?", + "path to init": "Percorso della cartella", + "provide path": "Specificare un percorso di cartella per inizializzare un repository GIT", + "HEAD not available": "La versione HEAD di '{0}' non è disponibile.", + "confirm stage files with merge conflicts": "Preparare per il commit {0} file con conflitti di merge?", + "confirm stage file with merge conflicts": "Preparare per il commit {0} con conflitti di merge?", + "yes": "Sì", + "confirm revert": "Ripristinare le modifiche selezionate in {0}?", + "revert": "Annulla modifiche", + "discard": "Rimuovi modifiche", + "confirm delete": "ELIMINARE {0}?", + "delete file": "Elimina file", + "confirm discard": "Rimuovere le modifiche in {0}?", + "confirm discard multiple": "Rimuovere le modifiche in {0} file?", + "warn untracked": "Verranno ELIMINATI {0} file di cui non viene tenuta traccia.", + "confirm discard all single": "Rimuovere le modifiche in {0}?", + "confirm discard all": "Rimuovere TUTTE le modifiche apportate in {0} file?\nQuesta operazione è IRREVERSIBILE.\nIl working set corrente andrà PERSO PER SEMPRE.", + "discardAll multiple": "Rimuovi 1 file", + "discardAll": "Rimuovi tutti i {0} file", + "confirm delete multiple": "ELIMINARE {0} file?", + "delete files": "Elimina file", + "there are untracked files single": "Se rimosso, il file seguente di cui non viene tenuta traccia verrà ELIMINATO DAL DISCO: {0}.", + "there are untracked files": "Se rimossi, {0} file di cui non viene tenuta traccia verranno ELIMINATI DAL DISCO.", + "confirm discard all 2": "{0}\n\nQuesta operazione è IRREVERSIBILE. Il working set corrente andrà PERSO PER SEMPRE.", + "yes discard tracked": "Rimuovi 1 file di cui viene tenuta traccia", + "yes discard tracked multiple": "Rimuovi {0} file di cui viene tenuta traccia", + "no staged changes": "Non ci sono modifiche in stage di cui eseguire il commit.\n\nSI desidera mettere in stage automaticamente tutte le modifiche ed eseguirne il commit direttamente?", + "always": "Sempre", + "no changes": "Non ci sono modifiche di cui eseguire il commit.", + "commit message": "Messaggio di commit", + "provide commit message": "Specificare un messaggio di commit", + "select a ref to checkout": "Selezionare un ref di cui eseguire checkout", + "branch name": "Nome ramo", + "provide branch name": "Specificare un nome di ramo", + "select branch to delete": "Seleziona un ramo da cancellare", + "confirm force delete branch": "Il merge del ramo '{0}' non è completo. Elimina comunque?", + "delete branch": "Elimina ramo", + "select a branch to merge from": "Selezionare un ramo da cui eseguire il merge", + "merge conflicts": "Ci sono conflitti di merge. Risolverli prima di eseguire commit.", + "tag name": "Nome del tag", + "provide tag name": "Specificare un nome di tag", + "tag message": "Messaggio", + "provide tag message": "Specificare un messaggio per aggiungere un'annotazione per il tag", + "no remotes to pull": "Il repository non contiene elementi remoti configurati come origini del pull.", + "pick remote pull repo": "Selezionare un repository remoto da cui effettuare il pull del ramo", + "no remotes to push": "Il repository non contiene elementi remoti configurati come destinazione del push.", + "push with tags success": "Il push con tag è riuscito.", + "nobranch": "Estrarre un ramo per eseguire il push in un elemento remoto.", + "pick remote": "Selezionare un repository remoto in cui pubblicare il ramo '{0}':", + "sync is unpredictable": "Questa azione consentirà di effettuare il push e il pull di commit da e verso '{0}'.", + "ok": "OK", + "never again": "OK, non visualizzare più", + "no remotes to publish": "Il repository non contiene elementi remoti configurati come destinazione della pubblicazione.", + "no changes stash": "Non ci sono modifiche da accantonare.", + "provide stash message": "Specificare un messaggio di accantonamento (facoltativo)", + "stash message": "Messaggio di accantonamento", + "no stashes": "Non ci sono accantonamenti da ripristinare.", + "pick stash to pop": "Scegli un accantonamento da prelevare", + "clean repo": "Pulire l'albero di lavoro del repository prima dell'estrazione.", + "cant push": "Impossibile effettuare il push in remoto. Effettua prima un 'pull' per integrare le tue modifiche.", + "git error details": "GIT: {0}", + "git error": "Errore GIT", + "open git log": "Apri log GIT" +} \ No newline at end of file diff --git a/i18n/ita/extensions/git/out/main.i18n.json b/i18n/ita/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..726c3e19ea --- /dev/null +++ b/i18n/ita/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "Uso di GIT {0} da {1}", + "updateGit": "Aggiorna GIT", + "neverShowAgain": "Non visualizzare più questo messaggio", + "git20": "La versione installata di GIT è la {0}. Per il corretto funzionamento di Code è consigliabile usare una versione di GIT non inferiore alla 2." +} \ No newline at end of file diff --git a/i18n/ita/extensions/git/out/model.i18n.json b/i18n/ita/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..aa51209cfb --- /dev/null +++ b/i18n/ita/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "Non ci sono repository disponibili", + "pick repo": "Scegli un repository" +} \ No newline at end of file diff --git a/i18n/ita/extensions/git/out/repository.i18n.json b/i18n/ita/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..511bd47246 --- /dev/null +++ b/i18n/ita/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Apri", + "index modified": "Indice modificato", + "modified": "Modificato", + "index added": "Indice aggiunto", + "index deleted": "Indice eliminato", + "deleted": "Eliminato", + "index renamed": "Indice rinominato", + "index copied": "Indice copiato", + "untracked": "Non registrato", + "ignored": "Ignorato", + "both deleted": "Entrambi eliminati", + "added by us": "Aggiunto da noi", + "deleted by them": "Eliminato da loro", + "added by them": "Aggiunto da loro", + "deleted by us": "Eliminato da noi", + "both added": "Entrambi aggiunti", + "both modified": "Entrambi modificati", + "commit": "Esegui commit", + "merge changes": "Esegui merge delle modifiche", + "staged changes": "Modifiche preparate per il commit", + "changes": "Modifiche", + "ok": "OK", + "neveragain": "Non visualizzare più questo messaggio", + "huge": "Il repository git '{0}' ha troppe modifiche attive - verrà attivato solo un sottoinsieme delle funzionalità di Git." +} \ No newline at end of file diff --git a/i18n/ita/extensions/git/out/scmProvider.i18n.json b/i18n/ita/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..305c4c965a --- /dev/null +++ b/i18n/ita/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "Esegui commit" +} \ No newline at end of file diff --git a/i18n/ita/extensions/git/out/statusbar.i18n.json b/i18n/ita/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..06222b8427 --- /dev/null +++ b/i18n/ita/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "Estrai...", + "sync changes": "Sincronizza modifiche", + "publish changes": "Pubblica modifiche", + "syncing changes": "Sincronizzazione delle modifiche in corso..." +} \ No newline at end of file diff --git a/i18n/ita/extensions/git/package.i18n.json b/i18n/ita/extensions/git/package.i18n.json new file mode 100644 index 0000000000..66e9ab4c1e --- /dev/null +++ b/i18n/ita/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "Clona", + "command.init": "Inizializza repository", + "command.close": "Chiudi repository", + "command.refresh": "Aggiorna", + "command.openChange": "Apri modifiche", + "command.openFile": "Apri file", + "command.openHEADFile": "Apri File (HEAD)", + "command.stage": "Prepara modifiche per commit", + "command.stageAll": "Prepara tutte le modifiche per commit", + "command.stageSelectedRanges": "Prepara per il commit intervalli selezionati", + "command.revertSelectedRanges": "Ripristina intervalli selezionati", + "command.unstage": "Annulla preparazione modifiche per commit", + "command.unstageAll": "Annulla preparazione di tutte le modifiche per commit", + "command.unstageSelectedRanges": "Annulla preparazione per il commit di intervalli selezionati", + "command.clean": "Rimuovi modifiche", + "command.cleanAll": "Rimuovi tutte le modifiche", + "command.commit": "Commit", + "command.commitStaged": "Esegui commit dei file preparati", + "command.commitStagedSigned": "Esegui commit dei file preparati (approvazione)", + "command.commitStagedAmend": "Esegui commit dei file preparati (modifica)", + "command.commitAll": "Esegui commit di tutto", + "command.commitAllSigned": "Esegui commit di tutto (approvazione)", + "command.commitAllAmend": "Esegui commit di tutto (modifica)", + "command.undoCommit": "Annulla ultimo commit", + "command.checkout": "Estrai in...", + "command.branch": "Crea ramo...", + "command.deleteBranch": "Elimina ramo...", + "command.merge": "Merge ramo...", + "command.createTag": "Crea tag", + "command.pull": "Esegui pull", + "command.pullRebase": "Esegui pull (Riassegna)", + "command.pullFrom": "Pull da...", + "command.push": "Esegui push", + "command.pushTo": "Esegui push in...", + "command.pushWithTags": "Esegui push con tag", + "command.sync": "Sincronizza", + "command.publish": "Pubblica ramo", + "command.showOutput": "Mostra output GIT", + "command.ignore": "Aggiungi file a .gitignore", + "command.stash": "Accantona", + "command.stashPop": "Preleva accantonamento...", + "command.stashPopLatest": "Preleva accantonamento più recente", + "config.enabled": "Indica se GIT è abilitato", + "config.path": "Percorso dell'eseguibile di GIT", + "config.autorefresh": "Indica se l'aggiornamento automatico è abilitato", + "config.autofetch": "Indica se il recupero automatico è abilitato", + "config.enableLongCommitWarning": "Indica se visualizzare un avviso in caso di messaggi di commit lunghi", + "config.confirmSync": "Conferma prima di sincronizzare i repository GIT", + "config.countBadge": "Controlla il contatore delle notifiche git. Con `all` vengono conteggiate tutte le modifiche. Con `tracked` vengono conteggiate solo le revisioni. Con `off` il contatore è disattivato.", + "config.checkoutType": "Controlla il tipo di branch mostrati eseguendo il comando `Checkout in...`. `all` mostra tutti i refs, `local` mostra solamente i branch locali, `tags` mostra solamente i tag e `remote` mostra solamente i branch remoti.", + "config.ignoreLegacyWarning": "Ignora l'avvertimento legacy di Git", + "config.ignoreLimitWarning": "Ignora il messaggio di avviso quando ci sono troppi cambiamenti in un repository", + "config.defaultCloneDirectory": "Il percorso predefinito dove clonare un repository GIT", + "config.enableSmartCommit": "Eseguire il commit di tutte le modifiche quando non ci sono modifiche preparate.", + "config.enableCommitSigning": "Abilita la firma del commit con GPG.", + "config.discardAllScope": "Controlla quali modifiche vengono rimosse tramite il comando `Rimuovi tutte le modifiche`. Con `all` vengono rimosse tutte le modifiche. Con `tracked` vengono rimossi solo i file di cui viene tenuta traccia. Con `prompt` viene visualizzata una finestra di dialogo ogni volta che si esegue l'azione." +} \ No newline at end of file diff --git a/i18n/ita/extensions/grunt/out/main.i18n.json b/i18n/ita/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..67f5c6aa69 --- /dev/null +++ b/i18n/ita/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Rilevamento automatico di Grunt non riuscito - errore: {0}" +} \ No newline at end of file diff --git a/i18n/ita/extensions/grunt/package.i18n.json b/i18n/ita/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..6cd84a26d6 --- /dev/null +++ b/i18n/ita/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "Controlla se la rilevazione automatica delle attività Grunt è on/off. L'impostazione predefinita è 'on'." +} \ No newline at end of file diff --git a/i18n/ita/extensions/gulp/out/main.i18n.json b/i18n/ita/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..c8996e6fe3 --- /dev/null +++ b/i18n/ita/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Rilevamento automatico di gulp non riuscito - errore: {0}" +} \ No newline at end of file diff --git a/i18n/ita/extensions/gulp/package.i18n.json b/i18n/ita/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..8173cc557a --- /dev/null +++ b/i18n/ita/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "Controlla se la rilevazione automatica delle attività gulp è on/off. L'impostazione predefinita è 'on'." +} \ No newline at end of file diff --git a/i18n/ita/extensions/html/client/out/htmlMain.i18n.json b/i18n/ita/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..2682eca9d7 --- /dev/null +++ b/i18n/ita/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "Server di linguaggio HTML" +} \ No newline at end of file diff --git a/i18n/ita/extensions/html/package.i18n.json b/i18n/ita/extensions/html/package.i18n.json new file mode 100644 index 0000000000..067b466164 --- /dev/null +++ b/i18n/ita/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "Abilita/Disabilita il formattatore HTML predefinito", + "html.format.wrapLineLength.desc": "Numero massimo di caratteri per riga (0 = disabilita).", + "html.format.unformatted.desc": "Elenco di tag, separati da virgole, che non devono essere riformattati. Con 'null' viene usata l'impostazione predefinita che prevede l'uso di tutti i tag elencati in https://www.w3.org/TR/html5/dom.html#phrasing-content.", + "html.format.contentUnformatted.desc": "Elenco di tag, separati da virgole, in cui il contenuto non deve essere riformattato. Per impostazione predefinita, con 'null' viene usato il tag 'pre'.", + "html.format.indentInnerHtml.desc": "Imposta un rientro per le sezioni e .", + "html.format.preserveNewLines.desc": "Indica se è necessario mantenere interruzioni di riga esistenti prima degli elementi. Funziona solo prima degli elementi e non all'interno di tag o per il testo.", + "html.format.maxPreserveNewLines.desc": "Numero massimo di interruzioni di riga da mantenere in un unico blocco. Per non impostare un numero massimo, usare 'null'.", + "html.format.indentHandlebars.desc": "Applica la formattazione e imposta un rientro per {{#foo}} e {{/foo}}.", + "html.format.endWithNewline.desc": "Termina con un carattere di nuova riga.", + "html.format.extraLiners.desc": "Elenco di tag, separati da virgole, che devono essere preceduti da un carattere di nuova riga. Con 'null' viene usata l'impostazione predefinita \"head, body, /html\".", + "html.format.wrapAttributes.desc": "Esegue il wrapping degli attributi.", + "html.format.wrapAttributes.auto": "Esegue il wrapping degli attributi solo quando viene superata la lunghezza di riga.", + "html.format.wrapAttributes.force": "Esegue il wrapping di ogni attributo ad eccezione del primo.", + "html.format.wrapAttributes.forcealign": "Esegue il wrapping di ogni attributo ad eccezione del primo e mantiene l'allineamento.", + "html.format.wrapAttributes.forcemultiline": "Esegue il wrapping di ogni attributo.", + "html.suggest.angular1.desc": "Consente di configurare se il supporto del linguaggio HTML predefinito suggerisce tag e proprietà di Angular V1.", + "html.suggest.ionic.desc": "Consente di configurare se il supporto del linguaggio HTML predefinito suggerisce tag, proprietà e valori di Ionic.", + "html.suggest.html5.desc": "Consente di configurare se il supporto del linguaggio HTML predefinito suggerisce tag, proprietà e valori di HTML5.", + "html.trace.server.desc": "Traccia la comunicazione tra VS Code e il server del linguaggio HTML.", + "html.validate.scripts": "Consente di configurare se il supporto del linguaggio HTML predefinito convalida gli script incorporati.", + "html.validate.styles": "Consente di configurare se il supporto del linguaggio HTML predefinito convalida gli stili incorporati.", + "html.autoClosingTags": "Abilita/Disabilita la chiusura automatica dei tag HTML." +} \ No newline at end of file diff --git a/i18n/ita/extensions/jake/out/main.i18n.json b/i18n/ita/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..5ddac694f5 --- /dev/null +++ b/i18n/ita/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Rilevamento automatico di Jake non riuscito - errore: {0}" +} \ No newline at end of file diff --git a/i18n/ita/extensions/jake/package.i18n.json b/i18n/ita/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..cb81e4c3bf --- /dev/null +++ b/i18n/ita/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "Controlla se la rilevazione automatica delle attività Jake è on/off. L'impostazione predefinita è 'on'." +} \ No newline at end of file diff --git a/i18n/ita/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/ita/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..78cc345809 --- /dev/null +++ b/i18n/ita/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "bower.json predefinito", + "json.bower.error.repoaccess": "La richiesta al repository Bower non è riuscita: {0}", + "json.bower.latest.version": "più recente" +} \ No newline at end of file diff --git a/i18n/ita/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/ita/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..5af7518183 --- /dev/null +++ b/i18n/ita/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "package.json predefinito", + "json.npm.error.repoaccess": "La richiesta al repository NPM non è riuscita: {0}", + "json.npm.latestversion": "Ultima versione attualmente disponibile del pacchetto", + "json.npm.majorversion": "Trova la versione principale più recente (1.x.x)", + "json.npm.minorversion": "Trova la versione secondaria più recente (1.2.x)", + "json.npm.version.hover": "Ultima versione: {0}" +} \ No newline at end of file diff --git a/i18n/ita/extensions/json/client/out/jsonMain.i18n.json b/i18n/ita/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..353c28413a --- /dev/null +++ b/i18n/ita/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "Server di linguaggio JSON" +} \ No newline at end of file diff --git a/i18n/ita/extensions/json/package.i18n.json b/i18n/ita/extensions/json/package.i18n.json new file mode 100644 index 0000000000..fde7d09398 --- /dev/null +++ b/i18n/ita/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "Associa schemi a file JSON nel progetto corrente", + "json.schemas.url.desc": "URL di uno schema o percorso relativo di uno schema nella directory corrente", + "json.schemas.fileMatch.desc": "Matrice di criteri dei file da usare per la ricerca durante la risoluzione di file JSON in schemi.", + "json.schemas.fileMatch.item.desc": "Criteri dei file che possono contenere '*' da usare per la ricerca durante la risoluzione di file JSON in schemi.", + "json.schemas.schema.desc": "Definizione dello schema per l'URL specificato. È necessario specificare lo schema per evitare accessi all'URL dello schema.", + "json.format.enable.desc": "Abilita/Disabilita il formattatore JSON predefinito (richiede il riavvio)", + "json.tracing.desc": "Traccia la comunicazione tra VS Code e il server del linguaggio JSON.", + "json.colorDecorators.enable.desc": "Abilita o disabilita gli elementi Decorator di tipo colore", + "json.colorDecorators.enable.deprecationMessage": "L'impostazione `json.colorDecorators.enable` è stata deprecata e sostituita da `editor.colorDecorators`." +} \ No newline at end of file diff --git a/i18n/ita/extensions/markdown/out/extension.i18n.json b/i18n/ita/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..cf55e6b88e --- /dev/null +++ b/i18n/ita/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "Impossibile caricare 'markdown.styles': {0}" +} \ No newline at end of file diff --git a/i18n/ita/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/ita/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..775be0f804 --- /dev/null +++ b/i18n/ita/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "Alcuni contenuti sono stati disabilitati in questo documento", + "preview.securityMessage.title": "Contenuti potenzialmente non sicuri sono stati disattivati nell'anteprima del Markdown. Modificare l'impostazione di protezione dell'anteprima del Markdown per consentire la visualizzazione di contenuto insicuro o abilitare gli script", + "preview.securityMessage.label": "Avviso di sicurezza contenuto disabilitato" +} \ No newline at end of file diff --git a/i18n/ita/extensions/markdown/out/security.i18n.json b/i18n/ita/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..b77b5dcc6f --- /dev/null +++ b/i18n/ita/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "Strict", + "strict.description": "Carica solo contenuto protetto", + "insecureContent.title": "Consenti contenuto non protetto", + "insecureContent.description": "Consente il caricamento di contenuti tramite HTTP", + "disable.title": "Disabilita", + "disable.description": "Consente l'esecuzione di tutti i contenuti e script. Scelta non consigliata", + "moreInfo.title": "Altre informazioni", + "preview.showPreviewSecuritySelector.title": "Seleziona impostazioni di protezione per le anteprime Markdown in questa area di lavoro" +} \ No newline at end of file diff --git a/i18n/ita/extensions/markdown/package.i18n.json b/i18n/ita/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..17625d7db9 --- /dev/null +++ b/i18n/ita/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "Imposta come le interruzioni di riga vengono visualizzate nell'anteprima di markdown. Impostarlo a 'true' crea un
per ogni carattere di nuova riga.", + "markdown.preview.linkify": "Abilita o disabilita la conversione di testo simile a URL in collegamenti nell'anteprima markdown.", + "markdown.preview.doubleClickToSwitchToEditor.desc": "Fare doppio clic nell'anteprima markdown per passare all'editor.", + "markdown.preview.fontFamily.desc": "Consente di controllare la famiglia di caratteri usata nell'anteprima markdown.", + "markdown.preview.fontSize.desc": "Consente di controllare le dimensioni del carattere in pixel usate nell'anteprima markdown.", + "markdown.preview.lineHeight.desc": "Consente di controllare l'altezza della riga usata nell'anteprima markdown. Questo numero è relativo alle dimensioni del carattere.", + "markdown.preview.markEditorSelection.desc": "Contrassegna la selezione dell'editor corrente nell'anteprima markdown.", + "markdown.preview.scrollEditorWithPreview.desc": "Quando si scorre l'anteprima markdown, aggiorna la visualizzazione dell'editor.", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "Scorre l'anteprima markdown in modo da visualizzare la riga attualmente selezionata dall'editor.", + "markdown.preview.title": "Apri anteprima", + "markdown.previewFrontMatter.dec": "Consente di impostare il rendering del front matter YAML nell'anteprima markdown. Con 'hide' il front matter viene rimosso; altrimenti il front matter viene considerato come contenuto markdown.", + "markdown.previewSide.title": "Apri anteprima lateralmente", + "markdown.showSource.title": "Mostra origine", + "markdown.styles.dec": "Elenco di URL o percorsi locali dei fogli di stile CSS da usare dall'anteprima markdown. I percorsi relativi vengono interpretati come relativi alla cartella aperta nella finestra di esplorazione. Se non è presente alcuna cartella aperta, vengono interpretati come relativi al percorso del file markdown. Tutti i caratteri '\\' devono essere scritti come '\\\\'.", + "markdown.showPreviewSecuritySelector.title": "Modifica impostazioni di sicurezza anteprima", + "markdown.trace.desc": "Abilitare la registrazione debug per l'estensione markdown.", + "markdown.refreshPreview.title": "Aggiorna anteprima" +} \ No newline at end of file diff --git a/i18n/ita/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/ita/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..6cf7b39add --- /dev/null +++ b/i18n/ita/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "Accetta modifica corrente", + "acceptIncomingChange": "Accetta modifica in ingresso", + "acceptBothChanges": "Accetta entrambe le modifiche", + "compareChanges": "Confronta le modifiche" +} \ No newline at end of file diff --git a/i18n/ita/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/ita/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..af693a8ac3 --- /dev/null +++ b/i18n/ita/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "Il cursore dell'editor non si trova all'interno di un conflitto merge", + "compareChangesTitle": "{0}: modifiche correnti ⟷ modifiche in ingresso", + "cursorOnCommonAncestorsRange": "Il cursore dell'editor si trova all'interno del blocco di antenati comuni, si prega di passare al blocco \"corrente\" o \"in arrivo\"", + "cursorOnSplitterRange": "Il cursore si trova sulla barra di divisione di merge conflitti, si prega di spostarlo o al blocco \"corrente\" o a quello \"in ricezione\"", + "noConflicts": "Conflitti merge non trovati in questo file", + "noOtherConflictsInThisFile": "Nessun altro conflitto merge trovato in questo file" +} \ No newline at end of file diff --git a/i18n/ita/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/ita/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..ce8e817073 --- /dev/null +++ b/i18n/ita/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(modifica corrente)", + "incomingChange": "(modifica in ingresso)" +} \ No newline at end of file diff --git a/i18n/ita/extensions/merge-conflict/package.i18n.json b/i18n/ita/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..a9495af553 --- /dev/null +++ b/i18n/ita/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "Esegui merge del conflitto", + "command.accept.all-incoming": "Accettare tutte le modifiche in ingresso", + "command.accept.all-both": "Accettare tutte in entrambe", + "command.accept.current": "Accettare corrente", + "command.accept.incoming": "Accettare modifiche in ingresso", + "command.accept.selection": "Accettare selezione", + "command.accept.both": "Accettare entrambe", + "command.next": "Conflitto successivo", + "command.previous": "Conflitto precedente", + "command.compare": "Confronta il conflitto corrente", + "config.title": "Esegui merge del conflitto", + "config.codeLensEnabled": "Abilita/Disabilita le finestre CodeLens del blocco merge di conflitti all'interno di editor", + "config.decoratorsEnabled": "Abilita/Disabilita gli elementi Decorator sul blocco merge di conflitti all'interno di editor" +} \ No newline at end of file diff --git a/i18n/ita/extensions/npm/package.i18n.json b/i18n/ita/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..64f3c76055 --- /dev/null +++ b/i18n/ita/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "Controlla se la rilevazione automatica degli script npm è on/off. L'impostazione predefinita è 'on'.", + "config.npm.runSilent": "Esegue i comandi di npm con l'opzione `--silent`" +} \ No newline at end of file diff --git a/i18n/ita/extensions/php/out/features/validationProvider.i18n.json b/i18n/ita/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..bda042d35b --- /dev/null +++ b/i18n/ita/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "Consentire l'esecuzione di {0} (definito come impostazione dell'area di lavoro) per il lint dei file PHP?", + "php.yes": "Consenti", + "php.no": "Non consentire", + "wrongExecutable": "Non è possibile eseguire la convalida perché {0} non è un file eseguibile di PHP valido. Usare l'impostazione 'php.validate.executablePath' per convalidare il file eseguibile di PHP.", + "noExecutable": "Non è possibile eseguire la convalida perché non è impostato alcun file eseguibile di PHP. Usare l'impostazione 'php.validate.executablePath' per convalidare il file eseguibile di PHP.", + "unknownReason": "Non è stato possibile eseguire php con il percorso {0}. Il motivo è sconosciuto." +} \ No newline at end of file diff --git a/i18n/ita/extensions/php/package.i18n.json b/i18n/ita/extensions/php/package.i18n.json new file mode 100644 index 0000000000..f61727595c --- /dev/null +++ b/i18n/ita/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "Consente di configurare l'abilitazione dei suggerimenti predefiniti per il linguaggio PHP. Il supporto suggerisce variabili e variabili globali PHP.", + "configuration.validate.enable": "Abilita/Disabilita la convalida PHP predefinita.", + "configuration.validate.executablePath": "Punta all'eseguibile di PHP.", + "configuration.validate.run": "Indica se il linter viene eseguito durante il salvataggio o la digitazione.", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "Non consentire la convalida di PHP eseguibile (definito come impostazione dell'area di lavoro)" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/ita/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..0c71ce1648 --- /dev/null +++ b/i18n/ita/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInformation": "Altre informazioni", + "doNotCheckAgain": "Non eseguire più la verifica", + "close": "Chiudi", + "updateTscCheck": "L'impostazione utente 'typescript.check.tscVersion' è stata aggiornata ed è ora false" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/ita/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..fef76ddce3 --- /dev/null +++ b/i18n/ita/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "Acquisizione dei file typings...", + "acquiringTypingsDetail": "Acquisizione delle definizioni dei file typings per IntelliSense." +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/ita/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..d61c98c1ee --- /dev/null +++ b/i18n/ita/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "Attiva il controllo semantico in un file JavaScript. Deve essere all'inizio del file.", + "ts-nocheck": "Disattiva il controllo semantico in un file JavaScript. Deve essere all'inizio del file.", + "ts-ignore": "Elimina errori di @ts-check sulla riga successiva di un file." +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/ita/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..d1592ade28 --- /dev/null +++ b/i18n/ita/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 implementazione", + "manyImplementationLabel": "{0} implementazioni", + "implementationsErrorLabel": "Non è stato possibile determinare le implementazioni" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/ita/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..32413506bd --- /dev/null +++ b/i18n/ita/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "Commento JSDoc" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/ita/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..b7d04f0cb3 --- /dev/null +++ b/i18n/ita/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 riferimento", + "manyReferenceLabel": "{0} riferimenti", + "referenceErrorLabel": "Non è stato possibile determinare i riferimenti" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/ita/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..c42540824a --- /dev/null +++ b/i18n/ita/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "Osserva - {0}", + "buildTscLabel": "Compila - {0}" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/typescriptMain.i18n.json b/i18n/ita/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..66edda3027 --- /dev/null +++ b/i18n/ita/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "Aprire una cartella in Visual Studio Code per usare un progetto TypeScript o JavaScript", + "typescript.projectConfigUnsupportedFile": "Non è stato possibile determinare il progetto TypeScript o JavaScript. Il tipo di file non è supportato", + "typescript.projectConfigCouldNotGetInfo": "Non è stato possibile determinare il progetto TypeScript o JavaScript", + "typescript.noTypeScriptProjectConfig": "Il file non fa parte di un progetto TypeScript", + "typescript.noJavaScriptProjectConfig": "Il file non fa parte di un progetto JavaScript", + "typescript.configureTsconfigQuickPick": "Configura tsconfig.json", + "typescript.configureJsconfigQuickPick": "Configura jsconfig.json", + "typescript.projectConfigLearnMore": "Altre informazioni" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/ita/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..984cfead4c --- /dev/null +++ b/i18n/ita/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "Il percorso {0} non punta a un'installazione valida di tsserver. Verrà eseguito il fallback alla versione in bundle di TypeScript.", + "serverCouldNotBeStarted": "Non è stato possibile avviare il server di linguaggio TypeScript. Messaggio di errore: {0}", + "typescript.openTsServerLog.notSupported": "Per la registrazione del server TypeScript è necessario almeno TypeScript 2.2.2", + "typescript.openTsServerLog.loggingNotEnabled": "La registrazione del server TypeScript è disattivata. Per abilitarla, impostare `typescript.tsserver.log` e riavviare il server TypeScript", + "typescript.openTsServerLog.enableAndReloadOption": "Abilita la registrazione e riavvia il server TypeScript", + "typescript.openTsServerLog.noLogFile": "Il server TypeScript non ha avviato la registrazione.", + "openTsServerLog.openFileFailedFailed": "Non è stato possibile aprire il file di log del server TypeScript", + "serverDiedAfterStart": "Il servizio di linguaggio TypeScript è stato arrestato in modo imprevisto per cinque volte dopo che è stato avviato e non verrà riavviato.", + "serverDiedReportIssue": "Segnala problema", + "serverDied": "Il servizio di linguaggio Typescript è stato arrestato in modo imprevisto per cinque volte negli ultimi cinque minuti." +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/utils/api.i18n.json b/i18n/ita/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..c767fee82e --- /dev/null +++ b/i18n/ita/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "versione non valida" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/utils/logger.i18n.json b/i18n/ita/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/ita/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/ita/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..9e65c48fcf --- /dev/null +++ b/i18n/ita/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "Per abilitare le funzionalità del linguaggio JavaScript/TypeScript a livello di progetto, escludere le cartelle che contengono molti file, come {0}", + "hintExclude.generic": "Per abilitare le funzionalità del linguaggio JavaScript/TypeScript a livello di progetto, escludere le cartelle di grandi dimensioni che contengono file di origine su cui non si lavora.", + "large.label": "Configura esclusioni", + "hintExclude.tooltip": "Per abilitare le funzionalità del linguaggio JavaScript/TypeScript a livello di progetto, escludere le cartelle di grandi dimensioni che contengono file di origine su cui non si lavora." +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/ita/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..72a5be22c2 --- /dev/null +++ b/i18n/ita/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "Recupero dei dati per ottimizzare IntelliSense in TypeScript", + "typesInstallerInitializationFailed.title": "Non è stato possibile installare i file di definizione tipi per le funzionalità del linguaggio JavaScript. Verificare che NPM sia installato e o configurare 'typescript.npm' nelle impostazioni utente", + "typesInstallerInitializationFailed.moreInformation": "Altre informazioni", + "typesInstallerInitializationFailed.doNotCheckAgain": "Non eseguire più la verifica", + "typesInstallerInitializationFailed.close": "Chiudi" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/ita/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..c0635c860d --- /dev/null +++ b/i18n/ita/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "Usa versione di VS Code", + "useWorkspaceVersionOption": "Usa versione dell'area di lavoro", + "learnMore": "Altre informazioni", + "selectTsVersion": "Selezionare la versione di TypeScript usata per le funzionalità del linguaggio JavaScript e TypeScript" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/ita/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..db6240c4c2 --- /dev/null +++ b/i18n/ita/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "Non è stato possibile caricare la versione di TypeScript in questo percorso", + "noBundledServerFound": "Il file tsserver di VS Code è stato eliminato da un'altra applicazione, ad esempio uno strumento di rilevamento virus che non funziona correttamente. Reinstallare VS Code." +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/package.i18n.json b/i18n/ita/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..cbde5cc4c8 --- /dev/null +++ b/i18n/ita/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "Ricarica progetto", + "javascript.reloadProjects.title": "Ricarica progetto", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "Completare le funzioni con la relativa firma del parametro.", + "typescript.tsdk.desc": "Specifica il percorso della cartella che contiene i file tsserver e lib*.d.ts da usare.", + "typescript.disableAutomaticTypeAcquisition": "Disabilita l'acquisizione automatica del tipo. Richiede TypeScript >= 2.0.6.", + "typescript.tsserver.log": "Abilita la registrazione del server TypeScript in un file. Questo registro può essere utilizzato per diagnosticare problemi del server TypeScript. Il registro può contenere percorsi di file, codice sorgente e altre informazioni del progetto potenzialmente riservate. ", + "typescript.tsserver.trace": "Abilita la traccia dei messaggi inviati al server TypeScript. Questa traccia può essere utilizzata per diagnosticare problemi del server TypeScript. La traccia può contenere percorsi di file, codice sorgente e altre informazioni del progetto potenzialmente riservate.", + "typescript.validate.enable": "Abilita/Disabilita la convalida TypeScript.", + "typescript.format.enable": "Abilita/Disabilita il formattatore TypeScript predefinito.", + "javascript.format.enable": "Abilita/Disabilita il formattatore JavaScript predefinito.", + "format.insertSpaceAfterCommaDelimiter": "Consente di definire la gestione dello spazio dopo una virgola di delimitazione6", + "format.insertSpaceAfterConstructor": "Definisce la gestione dello spazio dopo la parola chiave constructor. Richiede TypeScript >= 2.3.0.", + "format.insertSpaceAfterSemicolonInForStatements": " Consente di definire la gestione dello spazio dopo un punto e virgola in un'istruzione for.", + "format.insertSpaceBeforeAndAfterBinaryOperators": "Consente di definire la gestione dello spazio dopo un operatore binario.", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "Consente di definire la gestione dello spazio dopo le parole chiave in un'istruzione del flusso di controllo.", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "Consente di definire la gestione dello spazio dopo la parola chiave function per funzioni anonime.", + "format.insertSpaceBeforeFunctionParenthesis": "Consente di definire la gestione dello spazio prima delle parentesi dell'argomento della funzione. Richiede TypeScript >= 2.1.5.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "Consente di definire la gestione dello spazio dopo le parentesi tonde di apertura e di chiusura non vuote.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "Consente di definire la gestione dello spazio dopo le parentesi quadre di apertura e di chiusura non vuote.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Consente di definire la gestione dello spazio dopo l'apertura e prima della chiusura di parentesi graffe non vuote. Richiede TypeScript >= 2.3.0.", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "Consente di definire la gestione dello spazio dopo la parentesi graffa iniziale e prima della parentesi graffa finale della stringa del modello. Richiede TypeScript >= 2.0.6", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Consente di definire la gestione dello spazio dopo la parentesi graffa iniziale e prima della parentesi graffa finale dell'espressione JSX. Richiede TypeScript >= 2.0.6", + "format.insertSpaceAfterTypeAssertion": "Definisce la gestione dello spazio dopo le asserzioni di tipo in TypeScript. Richiede TypeScript >= 2.4.", + "format.placeOpenBraceOnNewLineForFunctions": "Consente di definire se una parentesi graffa di apertura viene o meno inserita su una riga per le funzioni.", + "format.placeOpenBraceOnNewLineForControlBlocks": "Consente di definire se una parentesi graffa di apertura viene o meno inserita su una riga per i blocchi di controllo.", + "javascript.validate.enable": "Abilita/Disabilita la convalida JavaScript.", + "typescript.goToProjectConfig.title": "Passa a Configurazione progetto", + "javascript.goToProjectConfig.title": "Passa a Configurazione progetto", + "javascript.referencesCodeLens.enabled": "Abilita/disabilita riferimenti CodeLens nei file JavaScript.", + "typescript.referencesCodeLens.enabled": "Abilita/disabilita riferimenti CodeLens nei file TypeScript. Richiede TypeScript >= 2.0.6.", + "typescript.implementationsCodeLens.enabled": "Abilita/Disabilita le finestre CodeLens per le implementazioni. Richiede una versione di TypeScript uguale o successiva alla 2.2.0.", + "typescript.openTsServerLog.title": "Apri il log del server TypeScript", + "typescript.restartTsServer": "Riavvia server TS", + "typescript.selectTypeScriptVersion.title": "Seleziona la versione di TypeScript", + "jsDocCompletion.enabled": "Abilita/Disabilita commenti automatici JSDoc", + "javascript.implicitProjectConfig.checkJs": "Abilita/disabilita il controllo semantico di file JavaScript. File jsconfig.json o tsconfig.json esistenti sovrascrivono su questa impostazione. Richiede TypeScript >= 2.3.1.", + "typescript.npm": "Specifica il percorso dell'eseguibile NPM utilizzato per l'acquisizione automatica delle definizioni di tipi. Richiede TypeScript >= 2.3.4.", + "typescript.check.npmIsInstalled": "Controlla se NPM è installato per l'acquisizione automatica delle definizioni di tipi", + "javascript.nameSuggestions": "Abilita/disabilita l'inclusione di nomi univoci dal file negli elenchi di suggerimento di JavaScript.", + "typescript.tsc.autoDetect": "Controlla se la rilevazione automatica di attività tsc è on/off.", + "typescript.problemMatchers.tsc.label": "Problemi TypeScript", + "typescript.problemMatchers.tscWatch.label": "Problemi TypeScript (modalità espressione di controllo)" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/ita/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/ita/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/ita/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..c3f4a59e4a --- /dev/null +++ b/i18n/ita/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (nuova occorrenza)" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/ita/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..a1cd1f7797 --- /dev/null +++ b/i18n/ita/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "input" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/ita/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..b94d763e33 --- /dev/null +++ b/i18n/ita/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "Maiuscole/minuscole", + "wordsDescription": "Parola intera", + "regexDescription": "Usa espressione regolare" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/ita/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..c46ac868f5 --- /dev/null +++ b/i18n/ita/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Errore: {0}", + "alertWarningMessage": "Avviso: {0}", + "alertInfoMessage": "Info: {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/ita/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..d0e16f2242 --- /dev/null +++ b/i18n/ita/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "L'immagine è troppo grande per essere visualizzata nell'editor", + "resourceOpenExternalButton": "Aprire immagine utilizzando un programma esterno?", + "nativeBinaryError": "Il file non verrà visualizzato nell'editor perché è binario, è molto grande o usa una codifica testo non supportata.", + "sizeB": "{0} B", + "sizeKB": "{0} KB", + "sizeMB": "{0} MB", + "sizeGB": "{0} GB", + "sizeTB": "{0} TB" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/ita/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..1193860a31 --- /dev/null +++ b/i18n/ita/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "Altro" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/common/errorMessage.i18n.json b/i18n/ita/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..1017e84b07 --- /dev/null +++ b/i18n/ita/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}. Codice errore: {1}", + "error.permission.verbose": "Autorizzazione negata (HTTP {0})", + "error.permission": "Autorizzazione negata", + "error.http.verbose": "{0} (HTTP {1}: {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "Errore di connessione sconosciuto ({0})", + "error.connection.unknown": "Si è verificato un errore di connessione sconosciuto. La connessione a Internet è stata interrotta oppure il server al quale si è connessi è offline.", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "Si è verificato un errore sconosciuto. Per altri dettagli, vedere il log.", + "nodeExceptionMessage": "Si è verificato un errore di sistema ({0})", + "error.moreErrors": "{0} ({1} errori in totale)" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/common/json.i18n.json b/i18n/ita/src/vs/base/common/json.i18n.json new file mode 100644 index 0000000000..8444fd9e61 --- /dev/null +++ b/i18n/ita/src/vs/base/common/json.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Simbolo non valido", + "error.invalidNumberFormat": "Formato di numero non valido", + "error.propertyNameExpected": "È previsto un nome di proprietà", + "error.valueExpected": "È previsto un valore", + "error.colonExpected": "Sono previsti i due punti", + "error.commaExpected": "È prevista la virgola", + "error.closeBraceExpected": "È prevista la parentesi graffa di chiusura", + "error.closeBracketExpected": "È prevista la parentesi quadra di chiusura", + "error.endOfFileExpected": "È prevista la fine del file" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/ita/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..8444fd9e61 --- /dev/null +++ b/i18n/ita/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Simbolo non valido", + "error.invalidNumberFormat": "Formato di numero non valido", + "error.propertyNameExpected": "È previsto un nome di proprietà", + "error.valueExpected": "È previsto un valore", + "error.colonExpected": "Sono previsti i due punti", + "error.commaExpected": "È prevista la virgola", + "error.closeBraceExpected": "È prevista la parentesi graffa di chiusura", + "error.closeBracketExpected": "È prevista la parentesi quadra di chiusura", + "error.endOfFileExpected": "È prevista la fine del file" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/common/keybindingLabels.i18n.json b/i18n/ita/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..c50a331c3b --- /dev/null +++ b/i18n/ita/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "CTRL", + "shiftKey": "MAIUSC", + "altKey": "ALT", + "windowsKey": "Windows", + "ctrlKey.long": "CTRL", + "shiftKey.long": "MAIUSC", + "altKey.long": "ALT", + "cmdKey.long": "Comando", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/common/processes.i18n.json b/i18n/ita/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..a5d4a082ad --- /dev/null +++ b/i18n/ita/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "Errore: nelle informazioni sull'eseguibile deve essere definito un comando di tipo string.", + "ExecutableParser.isShellCommand": "Avviso: isShellCommand deve essere di tipo boolean. Il valore {0} verrà ignorato.", + "ExecutableParser.args": "Avviso: gli argomenti devono essere di tipo string[]. Il valore {0} verrà ignorato.", + "ExecutableParser.invalidCWD": "Avviso: options.cwd deve essere di tipo string. Il valore {0} verrà ignorato." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/common/severity.i18n.json b/i18n/ita/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..43f156bc9a --- /dev/null +++ b/i18n/ita/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "Errore", + "sev.warning": "Avviso", + "sev.info": "Informazioni" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/node/processes.i18n.json b/i18n/ita/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..87a24d1733 --- /dev/null +++ b/i18n/ita/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "Non è possibile eseguire un comando della shell su un'unità UNC." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/node/zip.i18n.json b/i18n/ita/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..928bce2a62 --- /dev/null +++ b/i18n/ita/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "{0} non è stato trovato all'interno del file ZIP." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/ita/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..4a1ae0d86e --- /dev/null +++ b/i18n/ita/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0}, selezione", + "quickOpenAriaLabel": "selezione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/ita/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..3374418ab8 --- /dev/null +++ b/i18n/ita/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "Selezione rapida. Digitare per ridurre il numero di risultati.", + "treeAriaLabel": "Selezione rapida" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/ita/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..e223668cd3 --- /dev/null +++ b/i18n/ita/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Comprimi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/code/electron-main/auth.i18n.json b/i18n/ita/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..f90a2a0d2d --- /dev/null +++ b/i18n/ita/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "Autenticazione proxy necessaria", + "proxyauth": "Il proxy {0} richiede l'autenticazione." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/code/electron-main/menus.i18n.json b/i18n/ita/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..4d31012405 --- /dev/null +++ b/i18n/ita/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "&&File", + "mEdit": "&&Modifica", + "mSelection": "&&Selezione", + "mView": "&&Visualizza", + "mGoto": "&&Esegui", + "mDebug": "&&Debug", + "mWindow": "Finestra", + "mHelp": "&&Guida", + "mTask": "A&&ttività", + "miNewWindow": "&&Nuova finestra", + "mAbout": "Informazioni su {0}", + "mServices": "Servizi", + "mHide": "Nascondi {0}", + "mHideOthers": "Nascondi altri", + "mShowAll": "Mostra tutto", + "miQuit": "Chiudi {0}", + "miNewFile": "&&Nuovo file", + "miOpen": "&&Apri...", + "miOpenWorkspace": "&&Apri area di lavoro...", + "miOpenFolder": "Apri &&cartella...", + "miOpenFile": "&&Apri file...", + "miOpenRecent": "Apri &&recenti", + "miSaveWorkspaceAs": "&&Salva area di lavoro con nome...", + "miAddFolderToWorkspace": "&&Aggiungi cartella all'area di lavoro...", + "miSave": "&&Salva", + "miSaveAs": "Salva con nome...", + "miSaveAll": "Salva &&tutto", + "miAutoSave": "Salvataggio automatico", + "miRevert": "Ripristina file", + "miCloseWindow": "Chiudi finestra", + "miCloseWorkspace": "Chiudi &&area di lavoro", + "miCloseFolder": "Chiudi &&cartella", + "miCloseEditor": "Chiudi &&editor", + "miExit": "&&Uscita", + "miOpenSettings": "&&Impostazioni", + "miOpenKeymap": "Tasti di scelta &&rapida", + "miOpenKeymapExtensions": "Estensioni &&mappature tastiera", + "miOpenSnippets": "&&Frammenti utente", + "miSelectColorTheme": "Tema &&colori", + "miSelectIconTheme": "Tema &&icona file", + "miPreferences": "&&Preferenze", + "miReopenClosedEditor": "&&Riapri editor chiuso", + "miMore": "&&Altro...", + "miClearRecentOpen": "&&Cancella elementi aperti di recente", + "miUndo": "Annulla", + "miRedo": "Ripristina", + "miCut": "Taglia (&&X)", + "miCopy": "&&Copia", + "miPaste": "Incolla (&&V)", + "miFind": "Trova (&&F)", + "miReplace": "&&Sostituisci", + "miFindInFiles": "Cerca &&nei file", + "miReplaceInFiles": "Sostituisci nei &&file", + "miEmmetExpandAbbreviation": "Emmet: &&Espandi abbreviazione", + "miShowEmmetCommands": "E&&mmet...", + "miToggleLineComment": "Attiva/Disattiva commento per &&riga", + "miToggleBlockComment": "Attiva/Disattiva commento per &&blocco", + "miMultiCursorAlt": "Passare ad ALT+clic per multi-cursore", + "miMultiCursorCmd": "Passare a Cmd+clic per multi-cursore", + "miMultiCursorCtrl": "Passare a CTRL+clic per multi-cursore", + "miInsertCursorAbove": "&&Aggiungi cursore sopra", + "miInsertCursorBelow": "A&&ggiungi cursore sotto", + "miInsertCursorAtEndOfEachLineSelected": "Aggiungi c&&ursori a fine riga", + "miAddSelectionToNextFindMatch": "Aggiungi &&occorrenza successiva", + "miAddSelectionToPreviousFindMatch": "Aggiungi occorrenza &&precedente", + "miSelectHighlights": "Seleziona &&tutte le occorrenze", + "miCopyLinesUp": "&&Copia riga in alto", + "miCopyLinesDown": "Co&&pia riga in basso", + "miMoveLinesUp": "Sposta riga in &&alto", + "miMoveLinesDown": "Sposta riga in &&basso", + "miSelectAll": "Seleziona &&tutto", + "miSmartSelectGrow": "Espan&&di selezione", + "miSmartSelectShrink": "&&Riduci selezione", + "miViewExplorer": "&&Esplora risorse", + "miViewSearch": "Cerca", + "miViewSCM": "S&&CM", + "miViewDebug": "&&Debug", + "miViewExtensions": "E&&stensioni", + "miToggleOutput": "&&Output", + "miToggleDebugConsole": "Console di de&&bug", + "miToggleIntegratedTerminal": "&&Terminale integrato", + "miMarker": "&&Problemi", + "miAdditionalViews": "&&Visualizzazioni aggiuntive", + "miCommandPalette": "&&Riquadro comandi...", + "miToggleFullScreen": "Attiva/Disattiva sc&&hermo intero", + "miToggleZenMode": "Attiva/Disattiva modalità Zen", + "miToggleMenuBar": "Attiva/Disattiva &&barra dei menu", + "miSplitEditor": "Dividi &&editor", + "miToggleEditorLayout": "Attiva/Disattiva &&layout gruppi dell'editor", + "miToggleSidebar": "Attiva/Disattiva &&barra laterale", + "miMoveSidebarRight": "Sposta barra laterale a &&destra", + "miMoveSidebarLeft": "Sp&&osta barra laterale a sinistra", + "miTogglePanel": "Attiva/Disattiva &&pannello", + "miHideStatusbar": "&&Nascondi barra di stato", + "miShowStatusbar": "&&Mostra barra di stato", + "miHideActivityBar": "Nascondi &&barra attività", + "miShowActivityBar": "Mostra &&barra attività", + "miToggleWordWrap": "Attiva/Disattiva &&ritorno a capo automatico", + "miToggleMinimap": "Attiva/Disattiva &&mini mappa", + "miToggleRenderWhitespace": "Attiva/Disattiva rendering &&spazi vuoti", + "miToggleRenderControlCharacters": "Attiva/Disattiva &&caratteri di controllo", + "miZoomIn": "&&Zoom avanti", + "miZoomOut": "Zoom indi&&etro", + "miZoomReset": "&&Reimposta zoom", + "miBack": "&&Indietro", + "miForward": "&&Avanti", + "miNextEditor": "&&Editor successivo", + "miPreviousEditor": "Editor &&precedente", + "miNextEditorInGroup": "&&Editor successivo usato nel gruppo", + "miPreviousEditorInGroup": "Editor &&precedente usato nel gruppo", + "miSwitchEditor": "Cambia &&editor", + "miFocusFirstGroup": "&&Primo gruppo", + "miFocusSecondGroup": "&&Secondo gruppo", + "miFocusThirdGroup": "&&Terzo gruppo", + "miNextGroup": "&&Gruppo successivo", + "miPreviousGroup": "Gruppo &&precedente", + "miSwitchGroup": "Cambia &&gruppo", + "miGotoFile": "Vai al &&file...", + "miGotoSymbolInFile": "Vai al &&simbolo nel file...", + "miGotoSymbolInWorkspace": "Vai al &&simbolo nell'area di lavoro...", + "miGotoDefinition": "Vai alla &&definizione", + "miGotoTypeDefinition": "Vai alla &&definizione di tipo", + "miGotoImplementation": "Vai all'&&implementazione", + "miGotoLine": "Vai alla riga...", + "miStartDebugging": "&&Avvia debug", + "miStartWithoutDebugging": "Avvia &&senza debug", + "miStopDebugging": "A&&rresta debug", + "miRestart Debugging": "&&Riavvia debug", + "miOpenConfigurations": "Apri &&configurazioni", + "miAddConfiguration": "Aggiungi configurazione...", + "miStepOver": "Ese&&gui istruzione/routine", + "miStepInto": "&&Esegui istruzione", + "miStepOut": "Esci da &&istruzione/routine", + "miContinue": "&&Continua", + "miToggleBreakpoint": "Attiva/Disattiva &&punto di interruzione", + "miConditionalBreakpoint": "Punto di interruzione &&condizionale...", + "miColumnBreakpoint": "Punto di interruzione &&colonna", + "miFunctionBreakpoint": "Punto di interruzione &&funzione...", + "miNewBreakpoint": "&&Nuovo punto di interruzione", + "miEnableAllBreakpoints": "Abilita tutti i punti di interruzione", + "miDisableAllBreakpoints": "Disabilita tutti i &&punti di interruzione", + "miRemoveAllBreakpoints": "Rimuovi &&tutti i punti di interruzione", + "miInstallAdditionalDebuggers": "&&Installa debugger aggiuntivi...", + "mMinimize": "Riduci a icona", + "mZoom": "Zoom", + "mBringToFront": "Porta tutto in primo piano", + "miSwitchWindow": "Cambia &&finestra...", + "miToggleDevTools": "&&Attiva/Disattiva strumenti di sviluppo", + "miAccessibilityOptions": "&&Opzioni accessibilità", + "miReportIssues": "&&Segnala problemi", + "miWelcome": "&&Benvenuti", + "miInteractivePlayground": "Playground &&interattivo", + "miDocumentation": "&&Documentazione", + "miReleaseNotes": "&&Note sulla versione", + "miKeyboardShortcuts": "&&Riferimento per tasti di scelta rapida", + "miIntroductoryVideos": "&&Video introduttivi", + "miTipsAndTricks": "&&Suggerimenti e trucchi", + "miTwitter": "Seguici su T&&witter", + "miUserVoice": "&&Cerca in richieste di funzionalità", + "miLicense": "&&Visualizza licenza", + "miPrivacyStatement": "&&Informativa sulla privacy", + "miAbout": "&&Informazioni su", + "miRunTask": "&&Esegui attività...", + "miBuildTask": "Esegui attività di &&compilazione...", + "miRunningTask": "Mostra attività in esec&&uzione...", + "miRestartTask": "Ria&&vvia attività in esecuzione...", + "miTerminateTask": "&&Termina attività...", + "miConfigureTask": "&&Configura attività", + "miConfigureBuildTask": "Configura atti&&vità di compilazione predefinita", + "accessibilityOptionsWindowTitle": "Opzioni accessibilità", + "miRestartToUpdate": "Riavvia per aggiornare...", + "miCheckingForUpdates": "Verifica della disponibilità di aggiornamenti...", + "miDownloadUpdate": "Scarica l'aggiornamento disponibile", + "miDownloadingUpdate": "Download dell'aggiornamento...", + "miInstallingUpdate": "Installazione dell'aggiornamento...", + "miCheckForUpdates": "Verifica disponibilità aggiornamenti...", + "aboutDetail": "\nVersione {0}\nCommit {1}\nData {2}\nShell {3}\nRenderer {4}\nNodo {5}\nArchitettura {6}", + "okButton": "OK" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/code/electron-main/window.i18n.json b/i18n/ita/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..1de016538b --- /dev/null +++ b/i18n/ita/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "È comunque possibile accedere alla barra dei menu premendo **ALT**." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/code/electron-main/windows.i18n.json b/i18n/ita/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..fed24674db --- /dev/null +++ b/i18n/ita/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "OK", + "pathNotExistTitle": "Il percorso non esiste", + "pathNotExistDetail": "Il percorso '{0}' sembra non esistere più sul disco.", + "openWorkspace": "&&Apri", + "openWorkspaceTitle": "Apri area di lavoro", + "save": "&&Salva", + "doNotSave": "&&Non salvare", + "cancel": "Annulla", + "saveWorkspaceMessage": "Salvare la configurazione dell'area di lavoro in un file?", + "saveWorkspaceDetail": "Salvare l'area di lavoro se si prevede di aprirla di nuovo.", + "saveWorkspace": "Salva area di lavoro", + "reopen": "Riapri", + "wait": "Continua ad attendere", + "close": "Chiudi", + "appStalled": "La finestra non risponde", + "appStalledDetail": "È possibile riaprire la finestra, chiuderla oppure attendere.", + "appCrashed": "Si è verificato un arresto anomalo della finestra", + "appCrashedDetail": "Ci scusiamo per l'inconveniente. Per riprendere dal punto in cui si è verificata l'interruzione, riaprire la finestra.", + "open": "Apri", + "openFolder": "Apri cartella", + "openFile": "Apri file" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/code/node/cliProcessMain.i18n.json b/i18n/ita/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..87fedc29e4 --- /dev/null +++ b/i18n/ita/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "L'estensione '{0}' non è stata trovata.", + "notInstalled": "L'estensione '{0}' non è installata.", + "useId": "Assicurarsi di usare l'ID estensione completo, incluso l'editore, ad esempio {0}", + "successVsixInstall": "L'estensione '{0}' è stata installata.", + "alreadyInstalled": "L'estensione '{0}' è già installata.", + "foundExtension": "L'estensione '{0}' è stata trovata nel Marketplace.", + "installing": "Installazione...", + "successInstall": "L'estensione '{0}' versione {1} è stata installata.", + "uninstalling": "Disinstallazione di {0}...", + "successUninstall": "L'estensione '{0}' è stata disinstallata." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/ita/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..1324fb6d3e --- /dev/null +++ b/i18n/ita/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "Non è possibile confrontare i file perché uno è troppo grande." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/ita/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..d8082a2e20 --- /dev/null +++ b/i18n/ita/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Chiudi", + "header": "Differenza {0} di {1}: originali {2}, {3} righe, modificate {4}, righe {5}", + "blankLine": "vuota", + "equalLine": "originali {0}, modificate {1}: {2}", + "insertLine": "+ modificate {0}: {1}", + "deleteLine": "- originali {0}: {1}", + "editor.action.diffReview.next": "Vai alla differenza successiva", + "editor.action.diffReview.prev": "Vai alla differenza precedente" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/ita/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..ea52d82025 --- /dev/null +++ b/i18n/ita/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "Editor", + "fontFamily": "Controlla la famiglia di caratteri.", + "fontWeight": "Controlla lo spessore del carattere.", + "fontSize": "Controlla le dimensioni del carattere in pixel.", + "lineHeight": "Controlla l'altezza della riga. Usare 0 per calcolare l'altezza della riga dalle dimensioni del carattere.", + "letterSpacing": "Controlla la spaziatura tra le lettere in pixel.", + "lineNumbers": "Consente di controllare la visualizzazione dei numeri di riga. I valori possibili sono 'on', 'off' e 'relative'. Con 'relative' viene visualizzato il conteggio delle righe a partire dalla posizione corrente del cursore.", + "rulers": "Colonne in corrispondenza delle quali visualizzare i righelli verticali", + "wordSeparators": "Caratteri che verranno usati come separatori di parola quando si eseguono operazioni o spostamenti correlati a parole", + "tabSize": "Numero di spazi a cui equivale una tabulazione. Quando `editor.detectIndentation` è attivo, questa impostazione viene sostituita in base al contenuto del file.", + "tabSize.errorMessage": "È previsto 'number'. Nota: il valore \"auto\" è stato sostituito dall'impostazione `editor.detectIndentation`.", + "insertSpaces": "Inserisce spazi quando viene premuto TAB. Quando `editor.detectIndentation` è attivo, questa impostazione viene sostituita in base al contenuto del file.", + "insertSpaces.errorMessage": "È previsto 'boolean'. Nota: il valore \"auto\" è stato sostituito dall'impostazione `editor.detectIndentation`.", + "detectIndentation": "All'apertura di un file, `editor.tabSize` e `editor.insertSpaces` verranno rilevati in base al contenuto del file.", + "roundedSelection": "Controlla se gli angoli delle selezioni sono arrotondati", + "scrollBeyondLastLine": "Controlla se l'editor scorrerà oltre l'ultima riga", + "smoothScrolling": "Controlla se per lo scorrimento dell'editor verrà usata un'animazione.", + "minimap.enabled": "Controlla se la mini mappa è visualizzata", + "minimap.showSlider": "Controlla se il dispositivo di scorrimento della mini mappa viene nascosto automaticamente. I valori possibili sono 'always' e 'mouseover'", + "minimap.renderCharacters": "Esegue il rendering dei caratteri effettivi di una riga (in contrapposizione ai blocchi colore)", + "minimap.maxColumn": "Limita la larghezza della mini mappa in modo da eseguire il rendering al massimo di un certo numero di colonne", + "find.seedSearchStringFromSelection": "Controlla se inizializzare la stringa di ricerca nel Widget Trova con il testo selezionato nell'editor", + "find.autoFindInSelection": "Controlla se l'impostazione Trova nella selezione è attivata quando vengono selezionati più caratteri o righe di testo nell'editor", + "wordWrap.off": "Il wrapping delle righe non viene eseguito.", + "wordWrap.on": "Verrà eseguito il wrapping delle righe in base alla larghezza del viewport.", + "wordWrap.wordWrapColumn": "Verrà eseguito il wrapping delle righe alla posizione corrispondente a `editor.wordWrapColumn`.", + "wordWrap.bounded": "Verrà eseguito il wrapping delle righe alla posizione minima del viewport e di `editor.wordWrapColumn`.", + "wordWrap": "Controlla il wrapping delle righe. Valori possibili:\n - 'off' (disabilita il wrapping),\n - 'on' (wrapping del viewport),\n - 'wordWrapColumn' (esegue il wrapping alla posizione corrispondente a `editor.wordWrapColumn`) o\n - 'bounded' (esegue il wrapping alla posizione minima del viewport e di `editor.wordWrapColumn`).", + "wordWrapColumn": "Controlla la colonna di wrapping dell'editor quando il valore di `editor.wordWrap` è 'wordWrapColumn' o 'bounded'.", + "wrappingIndent": "Controlla il rientro delle righe con ritorno a capo. Può essere uno dei valori seguenti: 'none', 'same' o 'indent'.", + "mouseWheelScrollSensitivity": "Moltiplicatore da usare sui valori `deltaX` e `deltaY` degli eventi di scorrimento della rotellina del mouse", + "multiCursorModifier.ctrlCmd": "Rappresenta il tasto 'Control' (ctrl) su Windows e Linux e il tasto 'Comando' (cmd) su OSX.", + "multiCursorModifier.alt": "Rappresenta il tasto 'Alt' su Windows e Linux e il tasto 'Opzione' su OSX.", + "multiCursorModifier": "Il modificatore da utilizzare per aggiungere molteplici cursori con il mouse. 'ctrlCmd' rappresenta il tasto 'Control' su Windows e Linux e il tasto 'Comando' su OSX. I gesti del mouse Vai a definizione e Apri il Link si adatteranno in modo da non entrare in conflitto con il modificatore multi-cursore.", + "quickSuggestions.strings": "Abilita i suggerimenti rapidi all'interno di stringhe.", + "quickSuggestions.comments": "Abilita i suggerimenti rapidi all'interno di commenti.", + "quickSuggestions.other": "Abilita i suggerimenti rapidi all'esterno di stringhe e commenti.", + "quickSuggestions": "Controlla se visualizzare automaticamente i suggerimenti durante la digitazione", + "quickSuggestionsDelay": "Controlla il ritardo in ms dopo il quale verranno visualizzati i suggerimenti rapidi", + "parameterHints": "Abilita un popup che mostra documentazione sui parametri e informazioni sui tipi mentre si digita", + "autoClosingBrackets": "Controlla se l'editor deve chiudere automaticamente le parentesi quadre dopo che sono state aperte", + "formatOnType": "Controlla se l'editor deve formattare automaticamente la riga dopo la digitazione", + "formatOnPaste": "Controlla se l'editor deve formattare automaticamente il contenuto incollato. Deve essere disponibile un formattatore che deve essere in grado di formattare un intervallo in un documento.", + "autoIndent": "Controlla se l'editor deve correggere automaticamente l'indentazione mentre l'utente digita, incolla o sposta delle righe. Devono essere disponibili le regole di indentazione del linguaggio.", + "suggestOnTriggerCharacters": "Controlla se i suggerimenti devono essere visualizzati automaticamente durante la digitazione dei caratteri trigger", + "acceptSuggestionOnEnter": "Controlla se i suggerimenti devono essere accettati con 'INVIO' in aggiunta a 'TAB'. In questo modo è possibile evitare ambiguità tra l'inserimento di nuove righe e l'accettazione di suggerimenti. Il valore 'smart' indica di accettare un suggerimento con 'INVIO' quando comporta una modifica al testo", + "acceptSuggestionOnCommitCharacter": "Controlla se accettare i suggerimenti con i caratteri di commit. Ad esempio, in JavaScript il punto e virgola (';') può essere un carattere di commit che accetta un suggerimento e digita tale carattere.", + "snippetSuggestions.top": "Visualizza i suggerimenti dello snippet sopra gli altri suggerimenti.", + "snippetSuggestions.bottom": "Visualizza i suggerimenti dello snippet sotto gli altri suggerimenti.", + "snippetSuggestions.inline": "Visualizza i suggerimenti degli snippet insieme agli altri suggerimenti.", + "snippetSuggestions.none": "Non mostrare i suggerimenti sugli snippet.", + "snippetSuggestions": "Controlla se i frammenti di codice sono visualizzati con altri suggerimenti e il modo in cui sono ordinati.", + "emptySelectionClipboard": "Consente di controllare se, quando si copia senza aver effettuato una selezione, viene copiata la riga corrente.", + "wordBasedSuggestions": "Controlla se calcolare i completamenti in base alle parole presenti nel documento.", + "suggestFontSize": "Dimensioni del carattere per il widget dei suggerimenti", + "suggestLineHeight": "Altezza della riga per il widget dei suggerimenti", + "selectionHighlight": "Controlla se l'editor deve evidenziare gli elementi corrispondenti simili alla selezione", + "occurrencesHighlight": "Controlla se l'editor deve evidenziare le occorrenze di simboli semantici", + "overviewRulerLanes": "Controlla il numero di effetti che possono essere visualizzati nella stessa posizione nel righello delle annotazioni", + "overviewRulerBorder": "Controlla se deve essere disegnato un bordo intorno al righello delle annotazioni.", + "cursorBlinking": "Controlla lo stile di animazione del cursore. I valori possibili sono: 'blink', 'smooth', 'phase', 'expand' e 'solid'", + "mouseWheelZoom": "Ingrandisce il carattere dell'editor quando si usa la rotellina del mouse e si tiene premuto CTRL", + "cursorStyle": "Controlla lo stile del cursore. I valori accettati sono 'block', 'block-outline', 'line', 'line-thin', 'underline' e 'underline-thin'", + "fontLigatures": "Abilita i caratteri legatura", + "hideCursorInOverviewRuler": "Controlla se il cursore deve essere nascosto nel righello delle annotazioni.", + "renderWhitespace": "Consente di controllare in che modo l'editor deve eseguire il rendering dei caratteri di spazio vuoto. Le opzioni possibili sono: 'none', 'boundary' e 'all'. Con l'opzione 'boundary' non viene eseguito il rendering di singoli spazi tra le parole.", + "renderControlCharacters": "Controlla se l'editor deve eseguire il rendering dei caratteri di controllo", + "renderIndentGuides": "Controlla se l'editor deve eseguire il rendering delle guide con rientro", + "renderLineHighlight": "Consente di controllare in che modo l'editor deve eseguire il rendering dell'evidenziazione di riga corrente. Le opzioni possibili sono 'none', 'gutter', 'line' e 'all'.", + "codeLens": "Controlla se nell'editor sono visualizzate le finestre di CodeLens", + "folding": "Controlla se per l'editor è abilitata la riduzione del codice", + "showFoldingControls": "Controlla se i controlli di riduzione sul margine della barra di scorrimento sono automaticamente nascosti.", + "matchBrackets": "Evidenzia le parentesi corrispondenti quando se ne seleziona una.", + "glyphMargin": "Controlla se l'editor deve eseguire il rendering del margine verticale del glifo. Il margine del glifo viene usato principalmente per il debug.", + "useTabStops": "Inserimento ed eliminazione dello spazio vuoto dopo le tabulazioni", + "trimAutoWhitespace": "Rimuovi lo spazio vuoto finale inserito automaticamente", + "stablePeek": "Mantiene aperti gli editor rapidi anche quando si fa doppio clic sul contenuto o si preme ESC.", + "dragAndDrop": "Controlla se l'editor consentire lo spostamento di selezioni tramite trascinamento della selezione.", + "accessibilitySupport.auto": "L'editor utilizzerà API della piattaforma per rilevare quando è collegata un'utilità per la lettura dello schermo.", + "accessibilitySupport.on": "L'editor sarà definitivamente ottimizzato per l'utilizzo con un'utilità per la lettura dello schermo.", + "accessibilitySupport.off": "L'editor non sarà mai ottimizzato per l'utilizzo con un'utilità per la lettura dello schermo.", + "accessibilitySupport": "Controlla se l'editor deve essere eseguito in una modalità ottimizzata per le utilità per la lettura dello schermo.", + "links": "Controlla se l'editor deve individuare i collegamenti e renderli cliccabili", + "colorDecorators": "Controlla se l'editor deve eseguire il rendering del selettore di colore e degli elementi Decorator di tipo colore inline.", + "sideBySide": "Controlla se l'editor diff mostra le differenze affiancate o incorporate", + "ignoreTrimWhitespace": "Controlla se l'editor diff mostra come differenze le modifiche relative a spazi vuoti iniziali e finali", + "renderIndicators": "Consente di controllare se l'editor diff mostra gli indicatori +/- per le modifiche aggiunte/rimosse", + "selectionClipboard": "Controlla se gli appunti primari di Linux devono essere supportati." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/common/config/defaultConfig.i18n.json b/i18n/ita/src/vs/editor/common/config/defaultConfig.i18n.json new file mode 100644 index 0000000000..9a151cfcfa --- /dev/null +++ b/i18n/ita/src/vs/editor/common/config/defaultConfig.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorViewAccessibleLabel": "Contenuto editor" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/ita/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..4fa78696ca --- /dev/null +++ b/i18n/ita/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "L'editor non è accessibile in questo momento. Premere Alt+F1 per le opzioni.", + "editorViewAccessibleLabel": "Contenuto editor" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/common/controller/cursor.i18n.json b/i18n/ita/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..f1ecf95147 --- /dev/null +++ b/i18n/ita/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "Eccezione imprevista durante l'esecuzione del comando." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/ita/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..6a5de1486a --- /dev/null +++ b/i18n/ita/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "Si è verificato un errore della modalità durante la suddivisione in token dell'input." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/ita/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..9ff43ae613 --- /dev/null +++ b/i18n/ita/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "Testo normale" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/ita/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..295064eb48 --- /dev/null +++ b/i18n/ita/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "Nel frattempo questi file sono stati modificati: {0}", + "summary.0": "Non sono state effettuate modifiche", + "summary.nm": "Effettuate {0} modifiche al testo in {1} file", + "summary.n0": "Effettuate {0} modifiche al testo in un file" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/ita/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..76c267782c --- /dev/null +++ b/i18n/ita/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Dichiarazioni del linguaggio per contributes.", + "vscode.extension.contributes.languages.id": "ID del linguaggio.", + "vscode.extension.contributes.languages.aliases": "Alias di nome per il linguaggio.", + "vscode.extension.contributes.languages.extensions": "Estensioni di file associate al linguaggio.", + "vscode.extension.contributes.languages.filenames": "Nomi file associati al linguaggio.", + "vscode.extension.contributes.languages.filenamePatterns": "Criteri GLOB dei nomi file associati al linguaggio.", + "vscode.extension.contributes.languages.mimetypes": "Tipi MIME associati al linguaggio.", + "vscode.extension.contributes.languages.firstLine": "Espressione regolare corrispondente alla prima riga di un file del linguaggio.", + "vscode.extension.contributes.languages.configuration": "Percorso relativo di un file che contiene le opzioni di configurazione per il linguaggio." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/ita/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/ita/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/ita/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..f9e6583c76 --- /dev/null +++ b/i18n/ita/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "Colore di sfondo per l'evidenziazione della riga alla posizione del cursore.", + "lineHighlightBorderBox": "Colore di sfondo per il bordo intorno alla riga alla posizione del cursore.", + "rangeHighlight": "Colore di sfondo degli intervalli evidenziati, ad esempio dalle funzionalità Quick Open e Trova.", + "caret": "Colore del cursore dell'editor.", + "editorCursorBackground": "Colore di sfondo del cursore editor. Permette di personalizzare il colore di un carattere quando sovrapposto da un blocco cursore.", + "editorWhitespaces": "Colore dei caratteri di spazio vuoto nell'editor.", + "editorIndentGuides": "Colore delle guide per i rientri dell'editor.", + "editorLineNumbers": "Colore dei numeri di riga dell'editor.", + "editorRuler": "Colore dei righelli dell'editor.", + "editorCodeLensForeground": "Colore primo piano delle finestre di CodeLens dell'editor", + "editorBracketMatchBackground": "Colore di sfondo delle parentesi corrispondenti", + "editorBracketMatchBorder": "Colore delle caselle di parentesi corrispondenti", + "editorOverviewRulerBorder": "Colore del bordo del righello delle annotazioni.", + "editorGutter": "Colore di sfondo della barra di navigazione dell'editor. La barra contiene i margini di glifo e i numeri di riga.", + "errorForeground": "Colore primo piano degli squiggle di errore nell'editor.", + "errorBorder": "Colore del bordo degli squiggle di errore nell'editor.", + "warningForeground": "Colore primo piano degli squiggle di avviso nell'editor", + "warningBorder": "Colore del bordo degli squggle di avviso nell'editor.", + "overviewRulerRangeHighlight": "Colore del marcatore del righello delle annotazioni per le evidenziazioni degli intervalli.", + "overviewRuleError": "Colore del marcatore del righello delle annotazioni per gli errori.", + "overviewRuleWarning": "Colore del marcatore del righello delle annotazioni per gli avvisi.", + "overviewRuleInfo": "Colore del marcatore del righello delle annotazioni per i messaggi di tipo informativo." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json b/i18n/ita/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json new file mode 100644 index 0000000000..cb49c332a5 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "introMsg": "Grazie per aver provato le opzioni di accessibilità di Visual Studio Code.", + "status": "Stato:", + "tabFocusModeOnMsg": "Premere TAB nell'editor corrente per spostare lo stato attivo sull'elemento con stato attivabile successivo. Per attivare/disattivare questo comportamento, premere {0}.", + "tabFocusModeOnMsgNoKb": "Premere TAB nell'editor corrente per spostare lo stato attivo sull'elemento con stato attivabile successivo. Il comando {0} non può essere attualmente attivato con un tasto di scelta rapida.", + "tabFocusModeOffMsg": "Premere TAB nell'editor corrente per inserire il carattere di tabulazione. Per attivare/disattivare questo comportamento, premere {0}.", + "tabFocusModeOffMsgNoKb": "Premere TAB nell'editor corrente per inserire il carattere di tabulazione. Il comando {0} non può essere attualmente attivato con un tasto di scelta rapida.", + "outroMsg": "Per chiudere questa descrizione comando e tornare all'editor, premere ESC.", + "ShowAccessibilityHelpAction": "Visualizza la Guida sull'accessibilità" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/ita/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..2160823385 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "Vai alla parentesi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/ita/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..234543fcec --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "Sposta il punto di inserimento a sinistra", + "caret.moveRight": "Sposta il punto di inserimento a destra" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/ita/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..eb5d9ea6b0 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "Trasponi lettere" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/ita/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..3bf135369a --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "Taglia", + "actions.clipboard.copyLabel": "Copia", + "actions.clipboard.pasteLabel": "Incolla", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "Copia con evidenziazione sintassi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/ita/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..ee68f6ac28 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "Attiva/Disattiva commento per la riga", + "comment.line.add": "Aggiungi commento per la riga", + "comment.line.remove": "Rimuovi commento per la riga", + "comment.block": "Attiva/Disattiva commento per il blocco" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/ita/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..e28f86ab9d --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "Mostra il menu di scelta rapida editor" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/ita/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..0fd447f7c9 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Trova", + "placeholder.find": "Trova", + "label.previousMatchButton": "Risultato precedente", + "label.nextMatchButton": "Risultato successivo", + "label.toggleSelectionFind": "Trova nella selezione", + "label.closeButton": "Chiudi", + "label.replace": "Sostituisci", + "placeholder.replace": "Sostituisci", + "label.replaceButton": "Sostituisci", + "label.replaceAllButton": "Sostituisci tutto", + "label.toggleReplaceButton": "Attiva/Disattiva modalità sostituzione", + "title.matchesCountLimit": "Vengono evidenziati solo i primi 999 risultati, ma tutte le operazioni di ricerca funzionano sull'intero testo.", + "label.matchesLocation": "{0} di {1}", + "label.noResults": "Nessuna impostazione corrispondente" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/ita/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..a9b06424c9 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Trova", + "placeholder.find": "Trova", + "label.previousMatchButton": "Risultato precedente", + "label.nextMatchButton": "Risultato successivo", + "label.closeButton": "Chiudi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/ita/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..9bb861766f --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "Trova", + "findNextMatchAction": "Trova successivo", + "findPreviousMatchAction": "Trova precedente", + "nextSelectionMatchFindAction": "Trova selezione successiva", + "previousSelectionMatchFindAction": "Trova selezione precedente", + "startReplace": "Sostituisci", + "addSelectionToNextFindMatch": "Aggiungi selezione a risultato ricerca successivo", + "addSelectionToPreviousFindMatch": "Aggiungi selezione a risultato ricerca precedente", + "moveSelectionToNextFindMatch": "Sposta ultima selezione a risultato ricerca successivo", + "moveSelectionToPreviousFindMatch": "Sposta ultima selezione a risultato ricerca precedente", + "selectAllOccurrencesOfFindMatch": "Seleziona tutte le occorrenze del risultato ricerca", + "changeAll.label": "Cambia tutte le occorrenze", + "showNextFindTermAction": "Mostra il termine di ricerca successivo", + "showPreviousFindTermAction": "Mostra il termine di ricerca precedente" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/ita/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..e27590bf8c --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "Espandi", + "unFoldRecursivelyAction.label": "Espandi in modo ricorsivo", + "foldAction.label": "Riduci", + "foldRecursivelyAction.label": "Riduci in modo ricorsivo", + "foldAllAction.label": "Riduci tutto", + "unfoldAllAction.label": "Espandi tutto", + "foldLevelAction.label": "Livello riduzione {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/ita/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..b9f2120e7e --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "È stata apportata 1 modifica di formattazione a riga {0}", + "hintn1": "Sono state apportate {0} modifiche di formattazione a riga {1}", + "hint1n": "È stata apportata 1 modifica di formattazione tra le righe {0} e {1}", + "hintnn": "Sono state apportate {0} modifiche di formattazione tra le righe {1} e {2}", + "formatDocument.label": "Formatta documento", + "formatSelection.label": "Formatta selezione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json b/i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json new file mode 100644 index 0000000000..332f00e0b1 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "Non è stata trovata alcuna definizione per '{0}'", + "generic.noResults": "Non è stata trovata alcuna definizione", + "meta.title": " - Definizioni di {0}", + "actions.goToDecl.label": "Vai alla definizione", + "actions.goToDeclToSide.label": "Apri definizione lateralmente", + "actions.previewDecl.label": "Visualizza la definizione", + "goToImplementation.noResultWord": "Non sono state trovate implementazioni per '{0}'", + "goToImplementation.generic.noResults": "Non sono state trovate implementazioni", + "meta.implementations.title": "- {0} implementazioni", + "actions.goToImplementation.label": "Vai all'implementazione", + "actions.peekImplementation.label": "Anteprima implementazione", + "goToTypeDefinition.noResultWord": "Non sono state trovate definizioni di tipi per '{0}'", + "goToTypeDefinition.generic.noResults": "Non sono state trovate definizioni di tipi", + "meta.typeDefinitions.title": " - {0} definizioni di tipo", + "actions.goToTypeDefinition.label": "Vai alla definizione di tipo", + "actions.peekTypeDefinition.label": "Anteprima definizione di tipo", + "multipleResults": "Fare clic per visualizzare {0} definizioni." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..abbfbb3636 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "Non è stata trovata alcuna definizione per '{0}'", + "generic.noResults": "Non è stata trovata alcuna definizione", + "meta.title": " - Definizioni di {0}", + "actions.goToDecl.label": "Vai alla definizione", + "actions.goToDeclToSide.label": "Apri definizione lateralmente", + "actions.previewDecl.label": "Visualizza la definizione", + "goToImplementation.noResultWord": "Non sono state trovate implementazioni per '{0}'", + "goToImplementation.generic.noResults": "Non sono state trovate implementazioni", + "meta.implementations.title": "- {0} implementazioni", + "actions.goToImplementation.label": "Vai all'implementazione", + "actions.peekImplementation.label": "Anteprima implementazione", + "goToTypeDefinition.noResultWord": "Non sono state trovate definizioni di tipi per '{0}'", + "goToTypeDefinition.generic.noResults": "Non sono state trovate definizioni di tipi", + "meta.typeDefinitions.title": " - {0} definizioni di tipo", + "actions.goToTypeDefinition.label": "Vai alla definizione di tipo", + "actions.peekTypeDefinition.label": "Anteprima definizione di tipo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..07f9d44123 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "Fare clic per visualizzare {0} definizioni." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/ita/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..fabddb62da --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "Vai a errore o avviso successivo", + "markerAction.previous.label": "Vai a errore o avviso precedente", + "editorMarkerNavigationError": "Colore per gli errori del widget di spostamento tra marcatori dell'editor.", + "editorMarkerNavigationWarning": "Colore per gli avvisi del widget di spostamento tra marcatori dell'editor.", + "editorMarkerNavigationBackground": "Sfondo del widget di spostamento tra marcatori dell'editor." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/ita/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..c09d333775 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "Visualizza passaggio del mouse" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/ita/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..4e481c25cc --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "Caricamento..." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/ita/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..42648c3954 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "Sostituisci con il valore precedente", + "InPlaceReplaceAction.next.label": "Sostituisci con il valore successivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/ita/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..955ba273ce --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "Converti rientro in spazi", + "indentationToTabs": "Converti rientro in tabulazioni", + "configuredTabSize": "Dimensione tabulazione configurata", + "selectTabWidth": "Seleziona dimensione tabulazione per il file corrente", + "indentUsingTabs": "Imposta rientro con tabulazioni", + "indentUsingSpaces": "Imposta rientro con spazi", + "detectIndentation": "Rileva rientro dal contenuto", + "editor.reindentlines": "Imposta nuovo rientro per righe" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json b/i18n/ita/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f13440e131 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Sviluppatore: controlla ambiti TextMate", + "inspectTMScopesWidget.loading": "Caricamento..." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/ita/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..319459b35f --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "Copia la riga in alto", + "lines.copyDown": "Copia la riga in basso", + "lines.moveUp": "Sposta la riga in alto", + "lines.moveDown": "Sposta la riga in basso", + "lines.sortAscending": "Ordinamento righe crescente", + "lines.sortDescending": "Ordinamento righe decrescente", + "lines.trimTrailingWhitespace": "Taglia spazio vuoto finale", + "lines.delete": "Elimina la riga", + "lines.indent": "Imposta un rientro per la riga", + "lines.outdent": "Riduci il rientro per la riga", + "lines.insertBefore": "Inserisci la riga sopra", + "lines.insertAfter": "Inserisci la riga sotto", + "lines.deleteAllLeft": "Elimina tutto a sinistra", + "lines.deleteAllRight": "Elimina tutto a destra", + "lines.joinLines": "Unisci righe", + "editor.transpose": "Trasponi caratteri intorno al cursore", + "editor.transformToUppercase": "Converti in maiuscolo", + "editor.transformToLowercase": "Converti in minuscolo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/ita/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..088756efa5 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "Cmd + clic per seguire il collegamento", + "links.navigate": "CTRL + clic per seguire il collegamento", + "links.command.mac": "Cmd + click per eseguire il comando", + "links.command": "Ctrl + clic per eseguire il comando", + "links.navigate.al": "Alt + clic per seguire il collegamento", + "links.command.al": "Alt + clic per eseguire il comando", + "invalid.url": "Non è stato possibile aprire questo collegamento perché il formato non è valido: {0}", + "missing.url": "Non è stato possibile aprire questo collegamento perché manca la destinazione.", + "label": "Apri il collegamento" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/ita/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..6189262c47 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "Aggiungi cursore sopra", + "mutlicursor.insertBelow": "Aggiungi cursore sotto", + "mutlicursor.insertAtEndOfEachLineSelected": "Aggiungi cursore alla fine delle righe" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/ita/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..cb8e8536d1 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "Attiva i suggerimenti per i parametri" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/ita/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..e8ba0be041 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0}, suggerimento" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/ita/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..820d45c261 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "Mostra correzioni ({0})", + "quickFix": "Mostra correzioni", + "quickfix.trigger.label": "Correzione rapida" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..b025d33f5b --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": " - Riferimenti di {0}", + "references.action.label": "Trova tutti i riferimenti" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..4b70c5716f --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "Caricamento..." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..50b4ea40d0 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "simbolo in {0} alla riga {1} colonna {2}", + "aria.fileReferences.1": "1 simbolo in {0}, percorso completo {1}", + "aria.fileReferences.N": "{0} simboli in {1}, percorso completo {2}", + "aria.result.0": "Non sono stati trovati risultati", + "aria.result.1": "Trovato 1 simbolo in {0}", + "aria.result.n1": "Trovati {0} simboli in {1}", + "aria.result.nm": "Trovati {0} simboli in {1} file" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..84730d2900 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "Non è stato possibile risolvere il file.", + "referencesCount": "{0} riferimenti", + "referenceCount": "{0} riferimento", + "missingPreviewMessage": "anteprima non disponibile", + "treeAriaLabel": "Riferimenti", + "noResults": "Nessun risultato", + "peekView.alternateTitle": "Riferimenti", + "peekViewTitleBackground": "Colore di sfondo dell'area del titolo della visualizzazione rapida.", + "peekViewTitleForeground": "Colore del titolo della visualizzazione rapida.", + "peekViewTitleInfoForeground": "Colore delle informazioni del titolo della visualizzazione rapida.", + "peekViewBorder": "Colore dei bordi e della freccia della visualizzazione rapida.", + "peekViewResultsBackground": "Colore di sfondo dell'elenco risultati della visualizzazione rapida.", + "peekViewResultsMatchForeground": "Colore primo piano dei nodi riga nell'elenco risultati della visualizzazione rapida.", + "peekViewResultsFileForeground": "Colore primo piano dei nodi file nell'elenco risultati della visualizzazione rapida.", + "peekViewResultsSelectionBackground": "Colore di sfondo della voce selezionata nell'elenco risultati della visualizzazione rapida.", + "peekViewResultsSelectionForeground": "Colore primo piano della voce selezionata nell'elenco risultati della visualizzazione rapida.", + "peekViewEditorBackground": "Colore di sfondo dell'editor di visualizzazioni rapide.", + "peekViewEditorGutterBackground": "Colore di sfondo della barra di navigazione nell'editor visualizzazione rapida.", + "peekViewResultsMatchHighlight": "Colore dell'evidenziazione delle corrispondenze nell'elenco risultati della visualizzazione rapida.", + "peekViewEditorMatchHighlight": "Colore dell'evidenziazione delle corrispondenze nell'editor di visualizzazioni rapide." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/ita/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..08bc0b0b38 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "Nessun risultato.", + "aria": "Correttamente rinominato '{0}' in '{1}'. Sommario: {2}", + "rename.failed": "L'esecuzione dell'operazione di ridenominazione non è riuscita.", + "rename.label": "Rinomina simbolo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/ita/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..78051e4cfd --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "Consente di rinominare l'input. Digitare il nuovo nome e premere INVIO per eseguire il commit." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/ita/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..88914b42e7 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "Espandi SELECT", + "smartSelect.shrink": "Comprimi SELECT" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/ita/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..4b695508fa --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "L'accettazione di '{0}' ha inserito il seguente testo: {1}", + "suggest.trigger.label": "Attiva suggerimento" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/ita/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..4e6a9227cc --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "Colore di sfondo del widget dei suggerimenti.", + "editorSuggestWidgetBorder": "Colore del bordo del widget dei suggerimenti.", + "editorSuggestWidgetForeground": "Colore primo piano del widget dei suggerimenti.", + "editorSuggestWidgetSelectedBackground": "Colore di sfondo della voce selezionata del widget dei suggerimenti.", + "editorSuggestWidgetHighlightForeground": "Colore delle evidenziazioni corrispondenze nel widget dei suggerimenti.", + "readMore": "Altre informazioni...{0}", + "suggestionWithDetailsAriaLabel": "{0}, suggerimento, con dettagli", + "suggestionAriaLabel": "{0}, suggerimento", + "readLess": "Meno informazioni... {0}", + "suggestWidget.loading": "Caricamento...", + "suggestWidget.noSuggestions": "Non ci sono suggerimenti.", + "suggestionAriaAccepted": "{0}, accettato", + "ariaCurrentSuggestionWithDetails": "{0}, suggerimento, con dettagli", + "ariaCurrentSuggestion": "{0}, suggerimento" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/ita/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..da60bf6aa1 --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "Attiva/Disattiva l'uso di TAB per spostare lo stato attivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/ita/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..766db1d09f --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "Colore di sfondo di un simbolo durante l'accesso in lettura, ad esempio durante la lettura di una variabile.", + "wordHighlightStrong": "Colore di sfondo di un simbolo durante l'accesso in scrittura, ad esempio durante la scrittura in una variabile.", + "overviewRulerWordHighlightForeground": "Colore del marcatore del righello delle annotazioni per le evidenziazioni dei simboli.", + "overviewRulerWordHighlightStrongForeground": "Colore del marcatore del righello delle annotazioni per le evidenziazioni dei simboli di accesso in scrittura." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/ita/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..e9423570ae --- /dev/null +++ b/i18n/ita/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Chiudi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/ita/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..a4ac5acb7e --- /dev/null +++ b/i18n/ita/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Il linguaggio in `contributes.{0}.language` è sconosciuto. Valore specificato: {1}", + "invalid.scopeName": "È previsto un valore stringa in `contributes.{0}.scopeName`. Valore specificato: {1}", + "invalid.path.0": "È previsto un valore stringa in `contributes.{0}.path`. Valore specificato: {1}", + "invalid.injectTo": "Il valore in `contributes.{0}.injectTo` non è valido. Deve essere una matrice di nomi di ambito del linguaggio. Valore specificato: {1}", + "invalid.embeddedLanguages": "Il valore in `contributes.{0}.embeddedLanguages` non è valido. Deve essere un mapping di oggetti tra nome ambito e linguaggio. Valore specificato: {1}", + "invalid.path.1": "Valore previsto di `contributes.{0}.path` ({1}) da includere nella cartella dell'estensione ({2}). L'estensione potrebbe non essere più portatile.", + "no-tm-grammar": "Non è stata registrata alcuna grammatica TM per questo linguaggio." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/ita/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..3c22385374 --- /dev/null +++ b/i18n/ita/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Errori durante l'analisi di {0}: {1}", + "schema.openBracket": "Sequenza di stringa o carattere parentesi quadra di apertura.", + "schema.closeBracket": "Sequenza di stringa o carattere parentesi quadra di chiusura.", + "schema.comments": "Definisce i simboli di commento", + "schema.blockComments": "Definisce il modo in cui sono contrassegnati i commenti per il blocco.", + "schema.blockComment.begin": "Sequenza di caratteri che indica l'inizio di un commento per il blocco.", + "schema.blockComment.end": "Sequenza di caratteri che termina i commenti per il blocco.", + "schema.lineComment": "Sequenza di caratteri che indica l'inizio di un commento per la riga.", + "schema.brackets": "Definisce i simboli di parentesi quadra che aumentano o riducono il rientro.", + "schema.autoClosingPairs": "Definisce le coppie di parentesi quadre. Quando viene immessa una parentesi quadra di apertura, quella di chiusura viene inserita automaticamente.", + "schema.autoClosingPairs.notIn": "Definisce un elenco di ambiti in cui la corrispondenza automatica delle coppie è disabilitata.", + "schema.surroundingPairs": "Definisce le coppie di parentesi quadre che possono essere usate per racchiudere una stringa selezionata.", + "schema.wordPattern": "La definizione di parola per il linguaggio.", + "schema.wordPattern.pattern": "Il modello di RegExp utilizzato per trovare parole.", + "schema.wordPattern.flags": "I flag di RegExp utilizzati per trovare parole.", + "schema.wordPattern.flags.errorMessage": "Deve corrispondere al modello `/^([gimuy]+)$/`." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/ita/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..9b5724a490 --- /dev/null +++ b/i18n/ita/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Tokenizer TextMate per contributes.", + "vscode.extension.contributes.grammars.language": "Identificatore di linguaggio per cui si aggiunge come contributo questa sintassi.", + "vscode.extension.contributes.grammars.scopeName": "Nome dell'ambito TextMate usato dal file tmLanguage.", + "vscode.extension.contributes.grammars.path": "Percorso del file tmLanguage. È relativo alla cartella delle estensioni e in genere inizia con './syntaxes/'.", + "vscode.extension.contributes.grammars.embeddedLanguages": "Mapping tra nome ambito e ID linguaggio se questa grammatica contiene linguaggi incorporati.", + "vscode.extension.contributes.grammars.injectTo": "Elenco di nomi di ambito del linguaggio in cui viene inserita questa grammatica." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/ita/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/ita/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/ita/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..064cce9f7e --- /dev/null +++ b/i18n/ita/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "le voci di menu devono essere una matrice", + "requirestring": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "optstring": "la proprietà `{0}` può essere omessa o deve essere di tipo `string`", + "vscode.extension.contributes.menuItem.command": "Identificatore del comando da eseguire. Il comando deve essere dichiarato nella sezione 'commands'", + "vscode.extension.contributes.menuItem.alt": "Identificatore di un comando alternativo da eseguire. Il comando deve essere dichiarato nella sezione 'commands'", + "vscode.extension.contributes.menuItem.when": "Condizione che deve essere vera per mostrare questo elemento", + "vscode.extension.contributes.menuItem.group": "Gruppo a cui appartiene questo comando", + "vscode.extension.contributes.menus": "Aggiunge voci del menu all'editor come contributo", + "menus.commandPalette": "Riquadro comandi", + "menus.editorTitle": "Menu del titolo dell'editor", + "menus.editorContext": "Menu di scelta rapida dell'editor", + "menus.explorerContext": "Menu di scelta rapida Esplora file", + "menus.editorTabContext": "Menu di scelta rapida delle schede dell'editor", + "menus.debugCallstackContext": "Menu di scelta rapida dello stack di chiamate di debug", + "menus.scmTitle": "Menu del titolo del controllo del codice sorgente", + "menus.resourceGroupContext": "Menu di scelta rapida del gruppo di risorse del controllo del codice sorgente", + "menus.resourceStateContext": "Menu di scelta rapida dello stato delle risorse del controllo del codice sorgente", + "view.viewTitle": "Menu del titolo della visualizzazione contribuita", + "view.itemContext": "Menu di contesto dell'elemento visualizzazione contribuita", + "nonempty": "è previsto un valore non vuoto.", + "opticon": "la proprietà `icon` può essere omessa o deve essere una stringa o un valore letterale come `{dark, light}`", + "requireStringOrObject": "la proprietà `{0}` è obbligatoria e deve essere di tipo `object` o `string`", + "requirestrings": "le proprietà `{0}` e `{1}` sono obbligatorie e devono essere di tipo `string`", + "vscode.extension.contributes.commandType.command": "Identificatore del comando da eseguire", + "vscode.extension.contributes.commandType.title": "Titolo con cui è rappresentato il comando nell'interfaccia utente", + "vscode.extension.contributes.commandType.category": "(Facoltativo) Stringa di categoria in base a cui è raggruppato il comando nell'interfaccia utente", + "vscode.extension.contributes.commandType.icon": "(Facoltativa) Icona usata per rappresentare il comando nell'interfaccia utente. Percorso di file o configurazione che supporta i temi", + "vscode.extension.contributes.commandType.icon.light": "Percorso dell'icona quando viene usato un tema chiaro", + "vscode.extension.contributes.commandType.icon.dark": "Percorso dell'icona quando viene usato un tema scuro", + "vscode.extension.contributes.commands": "Comandi di contributes per il riquadro comandi.", + "dup": "Il comando `{0}` è presente più volte nella sezione `commands`.", + "menuId.invalid": "`{0}` non è un identificatore di menu valido", + "missing.command": "La voce di menu fa riferimento a un comando `{0}` che non è definito nella sezione 'commands'.", + "missing.altCommand": "La voce di menu fa riferimento a un comando alternativo `{0}` che non è definito nella sezione 'commands'.", + "dupe.command": "La voce di menu fa riferimento allo stesso comando come comando predefinito e come comando alternativo", + "nosupport.altCommand": "I comandi alternativi sono attualmente supportati solo nel gruppo 'navigation' del menu 'editor/title'" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/ita/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..561d2caeca --- /dev/null +++ b/i18n/ita/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "Override configurazione predefinita", + "overrideSettings.description": "Consente di configurare le impostazioni dell'editor di cui eseguire l'override per il linguaggio {0}.", + "overrideSettings.defaultDescription": "Consente di configurare le impostazioni dell'editor di cui eseguire l'override per un linguaggio.", + "config.property.languageDefault": "Non è possibile registrare '{0}'. Corrisponde al criterio di proprietà '\\\\[.*\\\\]$' per la descrizione delle impostazioni dell'editor specifiche del linguaggio. Usare il contributo 'configurationDefaults'.", + "config.property.duplicate": "Non è possibile registrare '{0}'. Questa proprietà è già registrata." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/environment/node/argv.i18n.json b/i18n/ita/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..ddf04486ab --- /dev/null +++ b/i18n/ita/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "Gli argomenti nella modalità `--goto` devono essere espressi nel formato `FILE(:LINE(:CHARACTER))`.", + "diff": "Confronta due file tra loro.", + "add": "Aggiunge la cartella o le cartelle all'ultima finestra attiva.", + "goto": "Apre un file nel percorso alla posizione specificata di riga e carattere.", + "locale": "Impostazioni locali da usare, ad esempio en-US o it-IT.", + "newWindow": "Forza una nuova istanza di Code.", + "performance": "Eseguire l'avvio con il comando 'Developer: Startup Performance' abilitato.", + "prof-startup": "Esegui il profiler della CPU durante l'avvio", + "reuseWindow": "Forza l'apertura di un file o di una cartella nell'ultima finestra attiva.", + "userDataDir": "Consente di specificare la directory in cui si trovano i dati utente. Utile quando viene eseguito come root.", + "verbose": "Visualizza l'output dettagliato (implica --wait).", + "wait": "Attende la chiusura della finestra prima della restituzione.", + "extensionHomePath": "Impostare il percorso radice per le estensioni.", + "listExtensions": "Elenca le estensioni installate.", + "showVersions": "Mostra le versioni delle estensioni installate, quando si usa --list-extension.", + "installExtension": "Installa un'estensione.", + "uninstallExtension": "Disinstalla un'estensione.", + "experimentalApis": "Abilita funzionalità di API proposte per un'estensione specifica.", + "disableExtensions": "Disabilita tutte le estensioni installate.", + "disableGPU": "Disabilita l'accelerazione hardware della GPU.", + "version": "Visualizza la versione.", + "help": "Visualizza la sintassi.", + "usage": "Utilizzo", + "options": "opzioni", + "paths": "percorsi", + "optionsUpperCase": "Opzioni" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/ita/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..c381b15923 --- /dev/null +++ b/i18n/ita/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Non esiste alcuna area di lavoro." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/ita/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..613baf949e --- /dev/null +++ b/i18n/ita/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Estensioni", + "preferences": "Preferenze" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/ita/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..5378cdd043 --- /dev/null +++ b/i18n/ita/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "L'estensione non è stata trovata", + "noCompatible": "Non è stata trovata una versione di {0} compatibile con questa versione di Visual Studio Code." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/ita/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..4bfecafaef --- /dev/null +++ b/i18n/ita/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "Estensione non valida: package.json non è un file JSON.", + "restartCode": "Riavviare Code prima di reinstallare {0}.", + "installDependeciesConfirmation": "Se si installa '{0}', verranno installate anche le relative dipendenze. Continuare?", + "install": "Sì", + "doNotInstall": "No", + "uninstallDependeciesConfirmation": "Disinstallare solo '{0}' o anche le relative dipendenze?", + "uninstallOnly": "Solo", + "uninstallAll": "Tutto", + "cancel": "Annulla", + "uninstallConfirmation": "Disinstallare '{0}'?", + "ok": "OK", + "singleDependentError": "Non è possibile disinstallare l'estensione '{0}'. L'estensione '{1}' dipende da tale estensione.", + "twoDependentsError": "Non è possibile disinstallare l'estensione '{0}'. Le estensioni '{1}' e '{2}' dipendono da tale estensione.", + "multipleDependentsError": "Non è possibile disinstallare l'estensione '{0}'. Alcune estensioni, tra cui '{1}' e '{2}' dipendono da tale estensione.", + "notExists": "L'estensione non è stata trovata" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/ita/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..793874b41b --- /dev/null +++ b/i18n/ita/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "L'attivazione dell'estensione `{1}` non è riuscita. Motivo: la dipendenza `{0}` è sconosciuta.", + "failedDep1": "L'attivazione dell'estensione `{1}` non è riuscita. Motivo: non è stato possibile attivare la dipendenza `{0}`.", + "failedDep2": "L'attivazione dell'estensione `{0}` non è riuscita. Motivo: sono presenti più di 10 livelli di dipendenze (molto probabilmente un ciclo di dipendenze).", + "activationError": "L'attivazione dell'estensione `{0}` non è riuscita: {1}." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/ita/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..53b8f5ab1c --- /dev/null +++ b/i18n/ita/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "Per le estensioni di Visual Studio Code consente di specificare la versione di Visual Studio Code con cui è compatibile l'estensione. Non può essere *. Ad esempio: ^0.10.5 indica la compatibilità con la versione minima 0.10.5 di Visual Studio Code.", + "vscode.extension.publisher": "Editore dell'estensione Visual Studio Code.", + "vscode.extension.displayName": "Nome visualizzato per l'estensione usato nella raccolta di Visual Studio Code.", + "vscode.extension.categories": "Categorie usate dalla raccolta di Visual Studio Code per definire la categoria dell'estensione.", + "vscode.extension.galleryBanner": "Banner usato nel marketplace di Visual Studio Code.", + "vscode.extension.galleryBanner.color": "Colore del banner nell'intestazione pagina del marketplace di Visual Studio Code.", + "vscode.extension.galleryBanner.theme": "Tema colori per il tipo di carattere usato nel banner.", + "vscode.extension.contributes": "Tutti i contributi dell'estensione Visual Studio Code rappresentati da questo pacchetto.", + "vscode.extension.preview": "Imposta l'estensione in modo che venga contrassegnata come Anteprima nel Marketplace.", + "vscode.extension.activationEvents": "Eventi di attivazione per l'estensione Visual Studio Code.", + "vscode.extension.activationEvents.onLanguage": "Un evento di attivazione emesso ogni volta che viene aperto un file che risolve nella lingua specificata.", + "vscode.extension.activationEvents.onCommand": "Un evento di attivazione emesso ogni volta che viene invocato il comando specificato.", + "vscode.extension.activationEvents.onDebug": "Un evento di attivazione emesso ogni volta che viene iniziata una sessione di debug del tipo specificato.", + "vscode.extension.activationEvents.workspaceContains": "Un evento di attivazione emesso ogni volta che si apre una cartella che contiene almeno un file corrispondente al criterio GLOB specificato.", + "vscode.extension.activationEvents.onView": "Un evento di attivazione emesso ogni volta che la visualizzazione specificata viene espansa.", + "vscode.extension.activationEvents.star": "Un evento di attivazione emesso all'avvio di VS Code. Per garantire la migliore esperienza per l'utente finale, sei pregato di utilizzare questo evento di attivazione nella tua estensione solo quando nessun'altra combinazione di eventi di attivazione funziona nel tuo caso.", + "vscode.extension.badges": "Matrice di notifiche da visualizzare nella barra laterale della pagina delle estensioni del Marketplace.", + "vscode.extension.badges.url": "URL di immagine della notifica.", + "vscode.extension.badges.href": "Collegamento della notifica.", + "vscode.extension.badges.description": "Descrizione della notifica.", + "vscode.extension.extensionDependencies": "Dipendenze ad altre estensioni. L'identificatore di un'estensione è sempre ${publisher}.${name}. Ad esempio: vscode.csharp.", + "vscode.extension.scripts.prepublish": "Script eseguito prima che il pacchetto venga pubblicato come estensione Visual Studio Code.", + "vscode.extension.icon": "Percorso di un'icona da 128x128 pixel." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/ita/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..9219e7e14b --- /dev/null +++ b/i18n/ita/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "Non è stato possibile analizzare il valore {0} di `engines.vscode`. Usare ad esempio: ^0.10.0, ^1.2.3, ^0.11.0, ^0.10.x e così via.", + "versionSpecificity1": "La versione specificata in `engines.vscode` ({0}) non è abbastanza specifica. Per le versioni di vscode precedenti alla 1.0.0, definire almeno le versioni principale e secondaria desiderate, ad esempio ^0.10.0, 0.10.x, 0.11.0 e così via.", + "versionSpecificity2": "La versione specificata in `engines.vscode` ({0}) non è abbastanza specifica. Per le versioni di vscode successive alla 1.0.0, definire almeno la versione principale desiderata, ad esempio ^1.10.0, 1.10.x, 1.x.x, 2.x.x e così via.", + "versionMismatch": "L'estensione non è compatibile con Visual Studio Code {0}. Per l'estensione è richiesto: {1}.", + "extensionDescription.empty": "La descrizione dell'estensione restituita è vuota", + "extensionDescription.publisher": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "extensionDescription.name": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "extensionDescription.version": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "extensionDescription.engines": "la proprietà `{0}` è obbligatoria e deve essere di tipo `object`", + "extensionDescription.engines.vscode": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "extensionDescription.extensionDependencies": "la proprietà `{0}` può essere omessa o deve essere di tipo `string[]`", + "extensionDescription.activationEvents1": "la proprietà `{0}` può essere omessa o deve essere di tipo `string[]`", + "extensionDescription.activationEvents2": "le proprietà `{0}` e `{1}` devono essere specificate o omesse entrambi", + "extensionDescription.main1": "la proprietà `{0}` può essere omessa o deve essere di tipo `string`", + "extensionDescription.main2": "Valore previsto di `main` ({0}) da includere nella cartella dell'estensione ({1}). L'estensione potrebbe non essere più portatile.", + "extensionDescription.main3": "le proprietà `{0}` e `{1}` devono essere specificate o omesse entrambi", + "notSemver": "La versione dell'estensione non è compatibile con semver." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/ita/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..b2dea4ff5e --- /dev/null +++ b/i18n/ita/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "Nuova finestra", + "newWindowDesc": "Apre una nuova finestra", + "recentFolders": "Aree di lavoro recenti", + "folderDesc": "{0} {1}", + "codeWorkspace": "Area di lavoro del codice" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/ita/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..9eab2cb1f6 --- /dev/null +++ b/i18n/ita/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "OK", + "integrity.dontShowAgain": "Non visualizzare più questo messaggio", + "integrity.moreInfo": "Altre informazioni", + "integrity.prompt": "L'installazione di {0} sembra danneggiata. Reinstallare." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/ita/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..653eec71a4 --- /dev/null +++ b/i18n/ita/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "Configurazione dello schema JSON per contributes.", + "contributes.jsonValidation.fileMatch": "Criteri dei file da soddisfare, ad esempio \"package.json\" o \"*.launch\".", + "contributes.jsonValidation.url": "URL dello schema ('http:', 'https:') o percorso relativo della cartella delle estensioni ('./').", + "invalid.jsonValidation": "'configuration.jsonValidation' deve essere una matrice", + "invalid.fileMatch": "'configuration.jsonValidation.fileMatch' deve essere definito", + "invalid.url": "'configuration.jsonValidation.url' deve essere un URL o un percorso relativo", + "invalid.url.fileschema": "'configuration.jsonValidation.url' è un URL relativo non valido: {0}", + "invalid.url.schema": "'configuration.jsonValidation.url' deve iniziare con 'http:', 'https:' o './' per fare riferimento agli schemi presenti nell'estensione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/ita/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..5b1523a7be --- /dev/null +++ b/i18n/ita/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "È stato premuto ({0}). In attesa del secondo tasto...", + "missing.chord": "La combinazione di tasti ({0}, {1}) non è un comando." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/keybinding/common/keybindingLabels.i18n.json b/i18n/ita/src/vs/platform/keybinding/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..c50a331c3b --- /dev/null +++ b/i18n/ita/src/vs/platform/keybinding/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "CTRL", + "shiftKey": "MAIUSC", + "altKey": "ALT", + "windowsKey": "Windows", + "ctrlKey.long": "CTRL", + "shiftKey.long": "MAIUSC", + "altKey.long": "ALT", + "cmdKey.long": "Comando", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/ita/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..e79220f1b5 --- /dev/null +++ b/i18n/ita/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "La proprietà loop è supportata solo sul matcher dell'ultima riga.", + "ProblemPatternParser.problemPattern.missingRegExp": "Nel criterio del problema manca un'espressione regolare.", + "ProblemPatternParser.problemPattern.missingProperty": "Il criterio del problema non è valido. Deve includere almeno un gruppo di corrispondenze di tipo file, messaggio e riga o posizione.", + "ProblemPatternParser.invalidRegexp": "Errore: la stringa {0} non è un'espressione regolare valida.\n", + "ProblemPatternSchema.regexp": "Espressione regolare per trovare un messaggio di tipo errore, avviso o info nell'output.", + "ProblemPatternSchema.file": "Indice del gruppo di corrispondenze del nome file. Se omesso, viene usato 1.", + "ProblemPatternSchema.location": "Indice del gruppo di corrispondenze della posizione del problema. I criteri di posizione validi sono: (line), (line,column) e (startLine,startColumn,endLine,endColumn). Se omesso, si presuppone che sia impostato su (line,column).", + "ProblemPatternSchema.line": "Indice del gruppo di corrispondenze della riga del problema. Il valore predefinito è 2", + "ProblemPatternSchema.column": "Indice del gruppo di corrispondenze del carattere di riga del problema. Il valore predefinito è 3", + "ProblemPatternSchema.endLine": "Indice del gruppo di corrispondenze della riga finale del problema. Il valore predefinito è undefined", + "ProblemPatternSchema.endColumn": "Indice del gruppo di corrispondenze del carattere di fine riga del problema. Il valore predefinito è undefined", + "ProblemPatternSchema.severity": "Indice del gruppo di corrispondenze della gravità del problema. Il valore predefinito è undefined", + "ProblemPatternSchema.code": "Indice del gruppo di corrispondenze del codice del problema. Il valore predefinito è undefined", + "ProblemPatternSchema.message": "Indice del gruppo di corrispondenze del messaggio. Se omesso, il valore predefinito è 4 se si specifica la posizione; in caso contrario, il valore predefinito è 5.", + "ProblemPatternSchema.loop": "In un matcher di più righe il ciclo indica se questo criterio viene eseguito in un ciclo finché esiste la corrispondenza. Può essere specificato solo come ultimo criterio in un criterio su più righe.", + "NamedProblemPatternSchema.name": "Nome del criterio di problema.", + "NamedMultiLineProblemPatternSchema.name": "Nome del criterio di problema a più righe.", + "NamedMultiLineProblemPatternSchema.patterns": "Criteri effettivi.", + "ProblemPatternExtPoint": "Aggiunge come contributo i criteri di problema", + "ProblemPatternRegistry.error": "Il criterio di problema non è valido e verrà ignorato.", + "ProblemMatcherParser.noProblemMatcher": "Errore: la descrizione non può essere convertita in un matcher problemi:\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "Errore: la descrizione non definisce un criterio problema valido:\n{0}\n", + "ProblemMatcherParser.noOwner": "Errore: la descrizione non definisce un proprietario:\n{0}\n", + "ProblemMatcherParser.noFileLocation": "Errore: la descrizione non definisce un percorso file:\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "Info: gravità {0} sconosciuta. I valori validi sono errore, avviso e info.\n", + "ProblemMatcherParser.noDefinedPatter": "Errore: il criterio con identificatore {0} non esiste.", + "ProblemMatcherParser.noIdentifier": "Errore: la proprietà del criterio fa riferimento a un identificatore vuoto.", + "ProblemMatcherParser.noValidIdentifier": "Errore: la proprietà {0} del criterio non è un nome di variabile criterio valido.", + "ProblemMatcherParser.problemPattern.watchingMatcher": "Un matcher problemi deve definire un criterio di inizio e un criterio di fine per il controllo.", + "ProblemMatcherParser.invalidRegexp": "Errore: la stringa {0} non è un'espressione regolare valida.\n", + "WatchingPatternSchema.regexp": "L'espressione regolare per rilevare l'inizio o la fine di un'attività in background.", + "WatchingPatternSchema.file": "Indice del gruppo di corrispondenze del nome file. Può essere omesso.", + "PatternTypeSchema.name": "Nome di un criterio predefinito o aggiunto come contributo", + "PatternTypeSchema.description": "Criterio di problema o nome di un criterio di problema predefinito o aggiunto come contributo. Può essere omesso se si specifica base.", + "ProblemMatcherSchema.base": "Nome di un matcher problemi di base da usare.", + "ProblemMatcherSchema.owner": "Proprietario del problema in Visual Studio Code. Può essere omesso se si specifica base. Se è omesso e non si specifica base, viene usato il valore predefinito 'external'.", + "ProblemMatcherSchema.severity": "Gravità predefinita per i problemi di acquisizione. Viene usato se il criterio non definisce un gruppo di corrispondenze per la gravità.", + "ProblemMatcherSchema.applyTo": "Controlla se un problema segnalato in un documento di testo è valido solo per i documenti aperti o chiusi oppure per tutti i documenti.", + "ProblemMatcherSchema.fileLocation": "Consente di definire come interpretare i nomi file indicati in un criterio di problema.", + "ProblemMatcherSchema.background": "Criteri per tenere traccia dell'inizio e della fine di un matcher attivo su un'attività in background.", + "ProblemMatcherSchema.background.activeOnStart": "Se impostato a true, il monitor in backbround è in modalità attiva quando l'attività inizia. Equivale a inviare una riga che corrisponde al beginPattern", + "ProblemMatcherSchema.background.beginsPattern": "Se corrisponde nell'output, viene segnalato l'avvio di un'attività in background.", + "ProblemMatcherSchema.background.endsPattern": "Se corrisponde nell'output, viene segnalata la fine di un'attività in background.", + "ProblemMatcherSchema.watching.deprecated": "La proprietà watching è deprecata. In alternativa, utilizzare background (sfondo).", + "ProblemMatcherSchema.watching": "Criteri per tenere traccia dell'inizio e della fine di un matcher watching.", + "ProblemMatcherSchema.watching.activeOnStart": "Se impostato su true, indica che il watcher è in modalità attiva all'avvio dell'attività. Equivale a inviare una riga che corrisponde al criterio di avvio", + "ProblemMatcherSchema.watching.beginsPattern": "Se corrisponde nell'output, viene segnalato l'avvio di un'attività di controllo.", + "ProblemMatcherSchema.watching.endsPattern": "Se corrisponde nell'output, viene segnalata la fine di un'attività di controllo.", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "Questa proprietà è deprecata. In alternativa, usare la proprietà watching.", + "LegacyProblemMatcherSchema.watchedBegin": "Espressione regolare con cui viene segnalato l'avvio dell'esecuzione di un'attività controllata attivato tramite il controllo dei file.", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "Questa proprietà è deprecata. In alternativa, usare la proprietà watching.", + "LegacyProblemMatcherSchema.watchedEnd": "Espressione regolare con cui viene segnalato il termine dell'esecuzione di un'attività controllata.", + "NamedProblemMatcherSchema.name": "Nome del matcher problemi utilizzato per riferirsi ad esso.", + "NamedProblemMatcherSchema.label": "Un'etichetta leggibile del matcher problemi.", + "ProblemMatcherExtPoint": "Aggiunge come contributo i matcher problemi", + "msCompile": "Problemi del compilatore di Microsoft", + "lessCompile": "Problemi Less", + "gulp-tsc": "Problemi TSC Gulp", + "jshint": "Problemi JSHint", + "jshint-stylish": "Problemi di stile di JSHint", + "eslint-compact": "Problemi di compattazione di ESLint", + "eslint-stylish": "Problemi di stile di ESLint", + "go": "Problemi di Go" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/message/common/message.i18n.json b/i18n/ita/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..f9112d2a85 --- /dev/null +++ b/i18n/ita/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Chiudi", + "later": "In seguito", + "cancel": "Annulla" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/request/node/request.i18n.json b/i18n/ita/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..b4be6f5897 --- /dev/null +++ b/i18n/ita/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "Impostazione proxy da usare. Se non è impostata, verrà ottenuta dalle variabili di ambiente http_proxy e https_proxy", + "strictSSL": "Indica se il certificato del server proxy deve essere verificato in base all'elenco di CA specificate.", + "proxyAuthorization": "Valore da inviare come intestazione 'Proxy-Authorization' per ogni richiesta di rete." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/ita/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..eb113135cc --- /dev/null +++ b/i18n/ita/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetria", + "telemetry.enableTelemetry": "Consente l'invio di errori e dati sull'utilizzo a Microsoft." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/ita/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..3f32613669 --- /dev/null +++ b/i18n/ita/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "Aggiunge colori tematizzabili alle estensioni definite", + "contributes.color.id": "Identificatore del colore che supporta i temi", + "contributes.color.id.format": "Gli identificativi devono rispettare il formato aa[.bb]*", + "contributes.color.description": "Descrizione del colore che supporta i temi", + "contributes.defaults.light": "Colore predefinito per i temi chiari. Può essere un valore di colore in formato esadecimale (#RRGGBB[AA]) oppure l'identificativo di un colore che supporta i temi e fornisce l'impostazione predefinita.", + "contributes.defaults.dark": "Colore predefinito per i temi scuri. Può essere un valore di colore in formato esadecimale (#RRGGBB[AA]) oppure l'identificativo di un colore che supporta i temi e fornisce l'impostazione predefinita.", + "contributes.defaults.highContrast": "Colore predefinito per i temi a contrasto elevato. Può essere un valore di colore in formato esadecimale (#RRGGBB[AA]) oppure l'identificativo di un colore che supporta i temi e fornisce l'impostazione predefinita.", + "invalid.colorConfiguration": "'configuration.colors' deve essere un array", + "invalid.default.colorType": "{0} deve essere un valore di colore in formato esadecimale (#RRGGBB [AA] o #RGB[A]) o l'identificativo di un colore che supporta i temi e che fornisce il valore predefinito. ", + "invalid.id": "'configuration.colors.id' deve essere definito e non può essere vuoto", + "invalid.id.format": "'configuration.colors.id' deve essere specificato dopo parola[.parola]*", + "invalid.description": "'configuration.colors.description' deve essere definito e non può essere vuoto", + "invalid.defaults": "'configuration.colors.defaults' deve essere definito e deve contenere 'light', 'dark' e 'highContrast'" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/ita/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..879ac74772 --- /dev/null +++ b/i18n/ita/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "Formato colore non valido. Usare #RGB, #RGBA, #RRGGBB o #RRGGBBAA", + "schema.colors": "Colori usati nell'area di lavoro.", + "foreground": "Colore primo piano. Questo colore è utilizzato solo se non viene sovrascritto da un componente.", + "errorForeground": "Colore primo piano globale per i messaggi di errore. Questo colore è utilizzato solamente se non viene sottoposto a override da un componente.", + "descriptionForeground": "Colore primo piano del testo che fornisce informazioni aggiuntive, ad esempio per un'etichetta di testo.", + "focusBorder": "Colore dei bordi degli elementi evidenziati. Questo colore è utilizzato solo se non viene sovrascritto da un componente.", + "contrastBorder": "Un bordo supplementare attorno agli elementi per contrastarli maggiormente rispetto agli altri.", + "activeContrastBorder": "Un bordo supplementare intorno agli elementi attivi per contrastarli maggiormente rispetto agli altri.", + "selectionBackground": "Il colore di sfondo delle selezioni di testo nel workbench (ad esempio per i campi di input o aree di testo). Si noti che questo non si applica alle selezioni all'interno dell'editor.", + "textSeparatorForeground": "Colore dei separatori di testo.", + "textLinkForeground": "Colore primo piano dei link nel testo.", + "textLinkActiveForeground": "Colore primo piano dei link attivi nel testo.", + "textPreformatForeground": "Colore primo piano dei segmenti di testo preformattato.", + "textBlockQuoteBackground": "Colore di sfondo per le citazioni nel testo.", + "textBlockQuoteBorder": "Colore bordo per citazioni nel testo.", + "textCodeBlockBackground": "Colore sfondo per blocchi di codice nel testo.", + "widgetShadow": "Colore ombreggiatura dei widget, ad es. Trova/Sostituisci all'interno dell'editor.", + "inputBoxBackground": "Sfondo della casella di input.", + "inputBoxForeground": "Primo piano della casella di input.", + "inputBoxBorder": "Bordo della casella di input.", + "inputBoxActiveOptionBorder": "Colore del bordo di opzioni attivate nei campi di input.", + "inputPlaceholderForeground": "Colore primo piano di casella di input per il testo segnaposto.", + "inputValidationInfoBackground": "Colore di sfondo di convalida dell'input di tipo Informazione.", + "inputValidationInfoBorder": "Colore bordo di convalida dell'input di tipo Informazione.", + "inputValidationWarningBackground": "Colore di sfondo di convalida dell'input di tipo Avviso.", + "inputValidationWarningBorder": "Colore bordo di convalida dell'input di tipo Avviso.", + "inputValidationErrorBackground": "Colore di sfondo di convalida dell'input di tipo Errore.", + "inputValidationErrorBorder": "Colore bordo di convalida dell'input di tipo Errore.", + "dropdownBackground": "Sfondo dell'elenco a discesa.", + "dropdownForeground": "Primo piano dell'elenco a discesa.", + "dropdownBorder": "Bordo dell'elenco a discesa.", + "listFocusBackground": "Colore sfondo Elenco/Struttura ad albero per l'elemento evidenziato quando l'Elenco/Struttura ad albero è attivo. Un Elenco/Struttura ad albero attivo\nha il focus della tastiera, uno inattivo no.", + "listFocusForeground": "Colore primo piano Elenco/Struttura ad albero per l'elemento con stato attivo quando l'Elenco/Struttura ad albero è attivo. Un Elenco/Struttura ad albero attivo\nha il focus della tastiera, uno inattivo no.", + "listActiveSelectionBackground": "Colore sfondo Elenco/Struttura ad albero per l'elemento selezionato quando l'Elenco/Struttura ad albero è attivo. Un Elenco/Struttura ad albero attivo\nha il focus della tastiera, uno inattivo no.", + "listActiveSelectionForeground": "Colore primo piano Elenco/Struttura ad albero per l'elemento selezionato quando l'Elenco/Struttura ad albero è attivo. Un Elenco/Struttura ad albero attivo\nha il focus della tastiera, uno inattivo no.", + "listInactiveSelectionBackground": "Colore sfondo Elenco/Struttura ad albero per l'elemento selezionato quando l'Elenco/Struttura ad albero è inattivo. Un Elenco/Struttura ad albero attivo\nha il focus della tastiera, uno inattivo no.", + "listInactiveSelectionForeground": "Colore primo piano Elenco/Struttura ad albero per l'elemento selezionato quando l'Elenco/Struttura ad albero è inattivo. Un Elenco/Struttura ad albero attivo\nha il focus della tastiera, uno inattivo no.", + "listHoverBackground": "Sfondo Elenco/Struttura ad albero al passaggio del mouse sugli elementi.", + "listHoverForeground": "Primo piano Elenco/Struttura ad albero al passaggio del mouse sugli elementi.", + "listDropBackground": "Sfondo Elenco/Struttura ad albero durante il trascinamento degli elementi selezionati.", + "highlight": "Colore primo piano Elenco/Struttura ad albero delle occorrenze trovate durante la ricerca nell'Elenco/Struttura ad albero.", + "pickerGroupForeground": "Colore di selezione rapida per il raggruppamento delle etichette.", + "pickerGroupBorder": "Colore di selezione rapida per il raggruppamento dei bordi.", + "buttonForeground": "Colore primo piano del pulsante.", + "buttonBackground": "Colore di sfondo del pulsante.", + "buttonHoverBackground": "Colore di sfondo del pulsante al passaggio del mouse.", + "badgeBackground": "Colore di sfondo del badge. I badge sono piccole etichette informative, ad esempio per mostrare il conteggio dei risultati di una ricerca.", + "badgeForeground": "Colore primo piano del badge. I badge sono piccole etichette informative, ad esempio per mostrare il conteggio dei risultati di una ricerca.", + "scrollbarShadow": "Ombra di ScrollBar per indicare lo scorrimento della visualizzazione.", + "scrollbarSliderBackground": "Colore di sfondo dello slider della barra di scorrimento.", + "scrollbarSliderHoverBackground": "Colore di sfondo dello Slider della Barra di scorrimento al passaggio del mouse.", + "scrollbarSliderActiveBackground": "Colore di sfondo dello Slider della Barra di scorrimento quando è attivo.", + "progressBarBackground": "Colore di sfondo dell'indicatore di stato che può essere mostrato durante l'esecuzione di operazioni lunghe.", + "editorBackground": "Colore di sfondo dell'editor.", + "editorForeground": "Colore primo piano predefinito dell'editor.", + "editorWidgetBackground": "Colore di sfondo dei widget dell'editor, ad esempio Trova/Sostituisci.", + "editorWidgetBorder": "Colore bordo dei widget dell'editor. Il colore viene utilizzato solo se il widget sceglie di avere un bordo e se il colore non è sottoposto a override da un widget.", + "editorSelectionBackground": "Colore della selezione dell'editor.", + "editorSelectionForeground": "Colore del testo selezionato per il contrasto elevato.", + "editorInactiveSelection": "Colore della selezione in un editor inattivo.", + "editorSelectionHighlight": "Colore delle aree con lo stesso contenuto della selezione.", + "editorFindMatch": "Colore della corrispondenza di ricerca corrente.", + "findMatchHighlight": "Colore delle altre corrispondenze di ricerca.", + "findRangeHighlight": "Colore dell'intervallo di ricerca.", + "hoverHighlight": "Evidenziazione sotto la parola per cui è visualizzata un'area sensibile al passaggio del mouse.", + "hoverBackground": "Colore di sfondo dell'area sensibile al passaggio del mouse dell'editor.", + "hoverBorder": "Colore del bordo dell'area sensibile al passaggio del mouse dell'editor.", + "activeLinkForeground": "Colore dei collegamenti attivi.", + "diffEditorInserted": "Colore di sfondo del testo che è stato inserito.", + "diffEditorRemoved": "Colore di sfondo del testo che è stato rimosso.", + "diffEditorInsertedOutline": "Colore del contorno del testo che è stato inserito.", + "diffEditorRemovedOutline": "Colore del contorno del testo che è stato rimosso.", + "mergeCurrentHeaderBackground": "Sfondo intestazione corrente in conflitti di merge in linea.", + "mergeCurrentContentBackground": "Sfondo contenuto corrente in conflitti di merge in linea.", + "mergeIncomingHeaderBackground": "Sfondo intestazione modifica in ingresso in conflitti di merge in linea.", + "mergeIncomingContentBackground": "Sfondo contenuto modifica in ingresso in conflitti di merge in linea.", + "mergeCommonHeaderBackground": "Sfondo dell'intestazione dell'antenato comune nei conflitti di merge in linea.", + "mergeCommonContentBackground": "Sfondo del contenuto dell'antenato comune nei conflitti di merge in linea.", + "mergeBorder": "Colore bordo su intestazioni e sulla barra di divisione di conflitti di merge in linea.", + "overviewRulerCurrentContentForeground": "Colore primo piano righello panoramica attuale per i conflitti di merge in linea.", + "overviewRulerIncomingContentForeground": "Colore primo piano del righello panoramica modifiche in arrivo per i conflitti di merge in linea.", + "overviewRulerCommonContentForeground": "Colore primo piano righello panoramica dell'antenato comune per i conflitti di merge in linea.", + "overviewRulerFindMatchForeground": "Colore del marcatore del righello delle annotazioni per le corrispondenze della ricerca.", + "overviewRulerSelectionHighlightForeground": "Colore del marcatore del righello delle annotazioni per le evidenziazioni delle selezioni." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/ita/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..6900a47357 --- /dev/null +++ b/i18n/ita/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "Area di lavoro del codice", + "untitledWorkspace": "Senza titolo (Area di lavoro)", + "workspaceNameVerbose": "{0} (Area di lavoro)", + "workspaceName": "{0} (Area di lavoro)" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/ita/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..811340104e --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Sovrascrittura dell'estensione {0} con {1}.", + "extensionUnderDevelopment": "Caricamento dell'estensione di sviluppo in {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/ita/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..017507d433 --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Chiudi", + "cancel": "Annulla", + "ok": "OK" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/ita/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..afb2273556 --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "Non verranno visualizzati altri {0} errori e avvisi." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/node/extHostExplorerView.i18n.json b/i18n/ita/src/vs/workbench/api/node/extHostExplorerView.i18n.json new file mode 100644 index 0000000000..b1eb2aa255 --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/node/extHostExplorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "Non è stato registrato alcun elemento TreeExplorerNodeProvider con ID '{0}'.", + "treeExplorer.failedToProvideRootNode": "Con l'elemento TreeExplorerNodeProvider '{0}' non è stato possibile fornire il nodo radice." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/ita/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..793874b41b --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "L'attivazione dell'estensione `{1}` non è riuscita. Motivo: la dipendenza `{0}` è sconosciuta.", + "failedDep1": "L'attivazione dell'estensione `{1}` non è riuscita. Motivo: non è stato possibile attivare la dipendenza `{0}`.", + "failedDep2": "L'attivazione dell'estensione `{0}` non è riuscita. Motivo: sono presenti più di 10 livelli di dipendenze (molto probabilmente un ciclo di dipendenze).", + "activationError": "L'attivazione dell'estensione `{0}` non è riuscita: {1}." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/ita/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json b/i18n/ita/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json new file mode 100644 index 0000000000..90a96f49a2 --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "Non è stato registrato alcun elemento TreeExplorerNodeProvider con ID '{0}'.", + "treeExplorer.failedToProvideRootNode": "Con l'elemento TreeExplorerNodeProvider '{0}' non è stato possibile fornire il nodo radice.", + "treeExplorer.failedToResolveChildren": "Con l'elemento TreeExplorerNodeProvider '{0}' non è stato possibile risolvere gli elementi figlio." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/node/extHostTreeView.i18n.json b/i18n/ita/src/vs/workbench/api/node/extHostTreeView.i18n.json new file mode 100644 index 0000000000..b1eb2aa255 --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/node/extHostTreeView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "Non è stato registrato alcun elemento TreeExplorerNodeProvider con ID '{0}'.", + "treeExplorer.failedToProvideRootNode": "Con l'elemento TreeExplorerNodeProvider '{0}' non è stato possibile fornire il nodo radice." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/ita/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..066d5b2a84 --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "Nessuna visualizzazione di struttura ad albero con ID '{0}' registrata.", + "treeItem.notFound": "Nessun elemento di struttura ad albero con id '{0}' trovato.", + "treeView.duplicateElement": "L'elemento {0} è già registrato" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json b/i18n/ita/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..811340104e --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Sovrascrittura dell'estensione {0} con {1}.", + "extensionUnderDevelopment": "Caricamento dell'estensione di sviluppo in {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/api/node/mainThreadMessageService.i18n.json b/i18n/ita/src/vs/workbench/api/node/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..017507d433 --- /dev/null +++ b/i18n/ita/src/vs/workbench/api/node/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Chiudi", + "cancel": "Annulla", + "ok": "OK" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..7c9405d89a --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "Configura lingua", + "displayLanguage": "Definisce la lingua visualizzata di VSCode.", + "doc": "Per un elenco delle lingue supportate, vedere {0}.", + "restart": "Se si modifica il valore, è necessario riavviare VSCode.", + "fail.createSettings": "Non è possibile creare '{0}' ({1}).", + "JsonSchema.locale": "Linguaggio dell'interfaccia utente da usare." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..b5a4176a69 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Apri cartella...", + "openFileFolder": "Apri..." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..d8a1e909f3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "Attiva/Disattiva visibilità della barra attività", + "view": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..a1fa8fbe72 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "Attiva/Disattiva layout orizzontale/verticale gruppi di editor", + "horizontalLayout": "Layout orizzontale gruppi di editor", + "verticalLayout": "Layout verticale gruppi di editor", + "view": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..1b023d2b4e --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Attiva/Disattiva posizione della barra laterale", + "view": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..d9122f82d4 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "Attiva/Disattiva visibilità della barra laterale", + "view": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..03111ec1e0 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "Attiva/Disattiva visibilità della barra di stato", + "view": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..c83baa98b2 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "Attiva/Disattiva modalità Zen", + "view": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..2e35d69d4e --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Apri cartella...", + "openFileFolder": "Apri...", + "addFolderToWorkspace": "Aggiungi cartella all'area di lavoro...", + "add": "&&Aggiungi", + "addFolderToWorkspaceTitle": "Aggiungi cartella all'area di lavoro", + "newWorkspace": "Nuova area di lavoro...", + "select": "&&Seleziona", + "selectWorkspace": "Seleziona cartelle per l'area di lavoro", + "removeFolderFromWorkspace": "Rimuovi cartella dall'area di lavoro", + "saveWorkspaceAsAction": "Salva area di lavoro come...", + "saveEmptyWorkspaceNotSupported": "Aprire prima un'area di lavoro da salvare.", + "save": "&&Salva", + "saveWorkspace": "Salva area di lavoro", + "openWorkspaceAction": "Apri area di lavoro...", + "openWorkspaceConfigFile": "Apri file di configurazione dell'area di lavoro" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..55fa0366ba --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "Rimuovi da barra attività", + "keepInActivityBar": "Mantieni in barra attività", + "titleKeybinding": "{0} ({1})", + "additionalViews": "Visualizzazioni aggiuntive", + "numberBadge": "{0} ({1})", + "manageExtension": "Gestisci estensione", + "toggle": "Attiva/Disattiva visualizzazione bloccata" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..897e3392be --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "Nascondi barra attività", + "activityBarAriaLabel": "Cambio visualizzazione attiva", + "globalActions": "Azioni globali" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..42940b65be --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "Azioni di {0}", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..b9e5a7b1f3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "Visualizzatore file binari" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..43f6078f5f --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Editor di testo", + "textDiffEditor": "Editor diff file di testo", + "binaryDiffEditor": "Editor diff file binari", + "sideBySideEditor": "Editor affiancato", + "groupOnePicker": "Mostra editor nel primo gruppo", + "groupTwoPicker": "Mostra editor nel secondo gruppo", + "groupThreePicker": "Mostra editor nel terzo gruppo", + "allEditorsPicker": "Mostra tutti gli editor aperti", + "view": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..012b4aaef7 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "Dividi editor", + "joinTwoGroups": "Unisci editor di due gruppi", + "navigateEditorGroups": "Esplora gruppi di editor", + "focusActiveEditorGroup": "Sposta stato attivo sul gruppo di editor attivo", + "focusFirstEditorGroup": "Sposta stato attivo sul primo gruppo di editor", + "focusSecondEditorGroup": "Sposta stato attivo sul secondo gruppo di editor", + "focusThirdEditorGroup": "Sposta stato attivo sul terzo gruppo di editor", + "focusPreviousGroup": "Sposta stato attivo sul gruppo precedente", + "focusNextGroup": "Sposta stato attivo sul gruppo successivo", + "openToSide": "Apri lateralmente", + "closeEditor": "Chiudi editor", + "revertAndCloseActiveEditor": "Ripristina e chiudi editor", + "closeEditorsToTheLeft": "Chiudi editor a sinistra", + "closeEditorsToTheRight": "Chiudi editor a destra", + "closeAllEditors": "Chiudi tutti gli editor", + "closeUnmodifiedEditors": "Chiudi editor non modificati del gruppo", + "closeEditorsInOtherGroups": "Chiudi editor in altri gruppi", + "closeOtherEditorsInGroup": "Chiudi gli altri editor", + "closeEditorsInGroup": "Chiudi tutti gli editor del gruppo", + "moveActiveGroupLeft": "Sposta gruppo di editor a sinistra", + "moveActiveGroupRight": "Sposta gruppo di editor a destra", + "minimizeOtherEditorGroups": "Riduci a icona gli altri gruppi di editor", + "evenEditorGroups": "Imposta stessa larghezza per gruppo di editor", + "maximizeEditor": "Ingrandisci gruppo di editor e nascondi barra laterale", + "keepEditor": "Mantieni editor", + "openNextEditor": "Apri editor successivo", + "openPreviousEditor": "Apri editor precedente", + "nextEditorInGroup": "Apri editor successivo del gruppo", + "openPreviousEditorInGroup": "Apri editor precedente del gruppo", + "navigateNext": "Avanti", + "navigatePrevious": "Indietro", + "reopenClosedEditor": "Riapri editor chiuso", + "clearRecentFiles": "Cancella elementi aperti di recente", + "showEditorsInFirstGroup": "Mostra editor nel primo gruppo", + "showEditorsInSecondGroup": "Mostra editor nel secondo gruppo", + "showEditorsInThirdGroup": "Mostra editor nel terzo gruppo", + "showEditorsInGroup": "Mostra editor nel gruppo", + "showAllEditors": "Mostra tutti gli editor", + "openPreviousRecentlyUsedEditorInGroup": "Apri editor precedente usato di recente nel gruppo", + "openNextRecentlyUsedEditorInGroup": "Apri editor successivo usato di recente nel gruppo", + "navigateEditorHistoryByInput": "Apri editor precedente dalla cronologia", + "openNextRecentlyUsedEditor": "Apri editor successivo usato di recente", + "openPreviousRecentlyUsedEditor": "Apri editor precedente usato di recente", + "clearEditorHistory": "Cancella cronologia degli editor", + "focusLastEditorInStack": "Apri ultimo editor del gruppo", + "moveEditorLeft": "Sposta editor a sinistra", + "moveEditorRight": "Sposta editor a destra", + "moveEditorToPreviousGroup": "Sposta editor nel gruppo precedente", + "moveEditorToNextGroup": "Sposta editor nel gruppo successivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..eca93471fd --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "Consente di spostare l'editor attivo per schede o gruppi", + "editorCommand.activeEditorMove.arg.name": "Argomento per spostamento editor attivo", + "editorCommand.activeEditorMove.arg.description": "Proprietà degli argomenti:\n\t\t\t\t\t\t* 'to': valore stringa che specifica dove eseguire lo spostamento.\n\t\t\t\t\t\t* 'by': valore stringa che specifica l'unità per lo spostamento, ovvero per scheda o per gruppo.\n\t\t\t\t\t\t* 'value': valore numerico che specifica il numero di posizioni o una posizione assoluta per lo spostamento.\n\t\t\t\t\t", + "commandDeprecated": "Il comando **{0}** è stato rimosso. In alternativa, usare **{1}**", + "openKeybindings": "Configura tasti di scelta rapida" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..aa1098e089 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "A sinistra", + "groupTwoVertical": "Al centro", + "groupThreeVertical": "A destra", + "groupOneHorizontal": "In alto", + "groupTwoHorizontal": "Al centro", + "groupThreeHorizontal": "In basso", + "editorOpenError": "Non è possibile aprire '{0}': {1}." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..19a05320b4 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, selezione gruppo di editor", + "groupLabel": "Gruppo: {0}", + "noResultsFoundInGroup": "Nel gruppo non è stato trovato alcun editor aperto corrispondente", + "noOpenedEditors": "L'elenco degli editor aperti è attualmente vuoto nel gruppo", + "noResultsFound": "Non è stato trovato alcun editor aperto corrispondente", + "noOpenedEditorsAllGroups": "L'elenco degli editor aperti è attualmente vuoto" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..b1d0663011 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "Ri {0}, col {1} ({2} selezionate)", + "singleSelection": "Ri {0}, col {1}", + "multiSelectionRange": "{0} selezioni ({1} caratteri selezionati)", + "multiSelection": "{0} selezioni", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "TAB per spostare lo stato attivo", + "screenReaderDetected": "Rilevata utilità per la lettura dello schermo", + "screenReaderDetectedExtra": "Se non si utilizza un'utilità per la lettura dello schermo, si prega di impostare 'editor.accessibilitySupport' a \"off\".", + "disableTabMode": "Disabilita modalità accessibilità", + "gotoLine": "Vai alla riga", + "indentation": "Rientro", + "selectEncoding": "Seleziona codifica", + "selectEOL": "Seleziona sequenza di fine riga", + "selectLanguageMode": "Seleziona modalità linguaggio", + "fileInfo": "Informazioni sul file", + "spacesSize": "Spazi: {0}", + "tabSize": "Dimensione tabulazione: {0}", + "showLanguageExtensions": "Cerca '{0}' nelle estensioni del Marketplace...", + "changeMode": "Cambia modalità linguaggio", + "noEditor": "Al momento non ci sono editor di testo attivi", + "languageDescription": "({0}) - Linguaggio configurato", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "linguaggi (identificatore)", + "configureModeSettings": "Configura impostazioni basate su linguaggio '{0}'...", + "configureAssociationsExt": "Configura associazione file per '{0}'...", + "autoDetect": "Rilevamento automatico", + "pickLanguage": "Seleziona modalità linguaggio", + "currentAssociation": "Associazione corrente", + "pickLanguageToConfigure": "Seleziona la modalità linguaggio da associare a '{0}'", + "changeIndentation": "Cambia rientro", + "noWritableCodeEditor": "L'editor di testo attivo è di sola lettura.", + "indentView": "cambia visualizzazione", + "indentConvert": "converti file", + "pickAction": "Seleziona azione", + "changeEndOfLine": "Cambia sequenza di fine riga", + "pickEndOfLine": "Seleziona sequenza di fine riga", + "changeEncoding": "Cambia codifica file", + "noFileEditor": "Al momento non ci sono file attivi", + "saveWithEncoding": "Salva con codifica", + "reopenWithEncoding": "Riapri con codifica", + "guessedEncoding": "Ipotizzata dal contenuto", + "pickEncodingForReopen": "Seleziona codifica per la riapertura del file", + "pickEncodingForSave": "Seleziona codifica per il salvataggio del file" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..2b667805d5 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "Azioni delle schede" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..4fae1458c7 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "Editor diff file di testo", + "readonlyEditorWithInputAriaLabel": "{0}. Editor di confronto testo di sola lettura.", + "readonlyEditorAriaLabel": "Editor di confronto testo di sola lettura.", + "editableEditorWithInputAriaLabel": "{0}. Editor di confronto file di testo", + "editableEditorAriaLabel": "Editor di confronto file di testo.", + "navigate.next.label": "Revisione successiva", + "navigate.prev.label": "Revisione precedente", + "inlineDiffLabel": "Passa alla visualizzazione inline", + "sideBySideDiffLabel": "Passa alla visualizzazione affiancata" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..cac815ecdb --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0}, gruppo {1}." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..2ef2c2b185 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Editor di testo", + "readonlyEditorWithInputAriaLabel": "{0}. Editor di testo di sola lettura.", + "readonlyEditorAriaLabel": "Editor di testo di sola lettura.", + "untitledFileEditorWithInputAriaLabel": "{0}. Editor di testo file senza titolo.", + "untitledFileEditorAriaLabel": "Editor di testo file senza titolo." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..0b9ad3de1d --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Chiudi", + "closeOthers": "Chiudi altri", + "closeRight": "Chiudi a destra", + "closeAll": "Chiudi tutto", + "closeAllUnmodified": "Chiudi non modificati", + "keepOpen": "Mantieni aperto", + "showOpenedEditors": "Mostra editor aperti", + "araLabelEditorActions": "Azioni editor" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..ac50b3543f --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "Chiudi pannello", + "togglePanel": "Attiva/Disattiva pannello", + "focusPanel": "Sposta lo stato attivo nel pannello", + "toggleMaximizedPanel": "Attiva/Disattiva pannello ingrandito", + "maximizePanel": "Ingrandisci dimensioni del pannello", + "minimizePanel": "Ripristina dimensioni del pannello", + "view": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..27500dc546 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "Cambio pannello attivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..cbb9054de9 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} (premere 'INVIO' per confermare oppure 'ESC' per annullare)", + "inputModeEntry": "Premere 'INVIO' per confermare l'input oppure 'ESC' per annullare", + "emptyPicks": "Non ci sono voci selezionabili", + "quickOpenInput": "Digitare '?' per visualizzare la Guida relativa alle azioni che è possibile eseguire qui", + "historyMatches": "aperti di recente", + "noResultsFound1": "Non sono stati trovati risultati", + "canNotRunPlaceholder": "Questo gestore per Quick Open non può essere usato nel contesto corrente", + "entryAriaLabel": "{0}, aperti di recente", + "removeFromEditorHistory": "Rimuovi dalla cronologia", + "pickHistory": "Selezionare una voce dell'editor da rimuovere dalla cronologia" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..d6e9b690c1 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Vai al file...", + "quickNavigateNext": "Passa a successiva in Quick Open", + "quickNavigatePrevious": "Passa a precedente in Quick Open", + "quickSelectNext": "Seleziona successiva in Quick Open", + "quickSelectPrevious": "Seleziona precedente in Quick Open" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..d6e9b690c1 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Vai al file...", + "quickNavigateNext": "Passa a successiva in Quick Open", + "quickNavigatePrevious": "Passa a precedente in Quick Open", + "quickSelectNext": "Seleziona successiva in Quick Open", + "quickSelectPrevious": "Seleziona precedente in Quick Open" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..0bae9c3c97 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "Nascondi barra laterale", + "focusSideBar": "Sposta lo stato attivo nella barra laterale", + "viewCategory": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..7d4f2475e6 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "Il comando '{0}' non è attualmente abilitato e non può essere eseguito.", + "manageExtension": "Gestisci estensione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..1f621c1bdb --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[Non supportata]", + "devExtensionWindowTitlePrefix": "[Host di sviluppo estensione]" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/quickopen.i18n.json b/i18n/ita/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..3bc96406a5 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "Non ci sono risultati corrispondenti", + "noResultsFound2": "Non sono stati trovati risultati", + "entryAriaLabel": "{0}, comando" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/viewlet.i18n.json b/i18n/ita/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..7ba7a517b5 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Comprimi tutto" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/common/theme.i18n.json b/i18n/ita/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..2f578576ed --- /dev/null +++ b/i18n/ita/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "Colore di sfondo delle schede attive. Le schede sono i contenitori degli editor nell'area degli editor. È possibile aprire più schede in un gruppo di editor e possono esistere più gruppi di editor.", + "tabInactiveBackground": "Colore di sfondo delle schede inattive. Le schede sono i contenitori degli editor nell'area degli editor. È possibile aprire più schede in un gruppo di editor e possono esistere più gruppi di editor.", + "tabBorder": "Bordo per separare le schede l'una dall'altra. Le schede sono i contenitori degli editor nell'area degli editor. È possibile aprire più schede in un gruppo di editor e possono esistere più gruppi di editor.", + "tabActiveBorder": "Bordo per evidenziare le schede attive. Le schede sono i contenitori degli editor nell'area degli editor. È possibile aprire più schede in un gruppo di editor e possono esistere più gruppi di editor.", + "tabActiveUnfocusedBorder": "Bordo per evidenziare le schede attive in un gruppo con stato non attivo. Le schede sono i contenitori degli editor nell'area degli editor. È possibile aprire più schede in un gruppo di editor e possono esistere più gruppi di editor.", + "tabActiveForeground": "Colore di primo piano delle schede attive in un gruppo attivo. Le schede sono i contenitori degli editor nell'area degli editor. È possibile aprire più schede in un gruppo di editor e possono esistere più gruppi di editor.", + "tabInactiveForeground": "Colore di primo piano delle schede inattive in un gruppo attivo. Le schede sono i contenitori degli editor nell'area degli editor. È possibile aprire più schede in un gruppo di editor e possono esistere più gruppi di editor.", + "tabUnfocusedActiveForeground": "Colore primo piano delle schede attive in un gruppo con stato non attivo. Le schede sono i contenitori degli editor nell'area degli editor. È possibile aprire più schede in un gruppo di editor e possono esistere più gruppi di editor.", + "tabUnfocusedInactiveForeground": "Colore primo piano delle schede inattiva in un gruppo con stato non attivo. Le schede sono i contenitori degli editor nell'area degli editor. È possibile aprire più schede in un gruppo di editor e possono esistere più gruppi di editor.", + "editorGroupBackground": "Colore di sfondo di un gruppo di editor. I gruppi di editor sono contenitori di editor. Il colore di sfondo viene visualizzato quando si trascinano i gruppi di editor in un'altra posizione.", + "tabsContainerBackground": "Colore di sfondo dell'intestazione del titolo di gruppo di editor, quando le schede sono abilitate. I gruppi di editor sono i contenitori degli editor.", + "tabsContainerBorder": "Colore del bordo dell'intestazione del titolo di gruppo di editor, quando le schede sono abilitate. I gruppi di editor sono i contenitori degli editor.", + "editorGroupHeaderBackground": "Colore di sfondo dell'intestazione del titolo dell'editor quando le schede sono disabilitate. I gruppi di editor sono contenitori di editor.", + "editorGroupBorder": "Colore per separare più gruppi di editor l'uno dall'altro. I gruppi di editor sono i contenitori degli editor.", + "editorDragAndDropBackground": "Colore di sfondo quando si trascinano gli editor. Il colore dovrebbe avere una trasparenza impostata in modo che il contenuto dell'editor sia ancora visibile.", + "panelBackground": "Colore di sfondo dei pannelli. I pannelli sono visualizzati sotto l'area degli editor e contengono visualizzazioni quali quella di output e del terminale integrato.", + "panelBorder": "Colore del bordo dei pannelli nella parte superiore di separazione dall'editor. I pannelli sono visualizzati sotto l'area degli editor e contengono visualizzazioni quali quella di output e del terminale integrato.", + "panelActiveTitleForeground": "Colore del titolo del pannello attivo. I pannelli sono visualizzati sotto l'area degli editor e contengono visualizzazioni quali quella di output e quella del terminale integrato.", + "panelInactiveTitleForeground": "Colore del titolo del pannello inattivo. I pannelli sono visualizzati sotto l'area degli editor e contengono visualizzazioni quali quella di output e quella del terminale integrato.", + "panelActiveTitleBorder": "Colore del bordo del titolo del pannello attivo. I pannelli sono visualizzati sotto l'area degli editor e contengono visualizzazioni quali quella di output e del terminale integrato.", + "statusBarForeground": "Colore primo piano quando viene aperta un'area di lavoro. La barra di stato è visualizzata nella parte inferiore della finestra.", + "statusBarNoFolderForeground": "Colore primo piano quando non ci sono cartelle aperte. La barra di stato è visualizzata nella parte inferiore della finestra.", + "statusBarBackground": "Colore di sfondo della barra di stato quando viene aperta un'area di lavoro. La barra di stato è visualizzata nella parte inferiore della finestra.", + "statusBarNoFolderBackground": "Colore di sfondo della barra di stato quando non ci sono cartelle aperte. La barra di stato è visualizzata nella parte inferiore della finestra.", + "statusBarBorder": "Colore del bordo della barra di stato che la separa dalla sidebar e dall'editor. La barra di stato è visualizzata nella parte inferiore della finestra.", + "statusBarNoFolderBorder": "Colore del bordo della barra di stato che la separa dalla barra laterale e dall'editor quando non ci sono cartelle aperte. La barra di stato è visualizzata nella parte inferiore della finestra.", + "statusBarItemActiveBackground": "Colore di sfondo degli elementi della barra di stato quando si fa clic. La barra di stato è visualizzata nella parte inferiore della finestra.", + "statusBarItemHoverBackground": "Colore di sfondo degli elementi della barra di stato al passaggio del mouse. La barra di stato è visualizzata nella parte inferiore della finestra.", + "statusBarProminentItemBackground": "Colore di sfondo degli elementi rilevanti della barra di stato. Gli elementi rilevanti spiccano rispetto ad altre voci della barra di stato. La barra di stato è visualizzata nella parte inferiore della finestra.", + "statusBarProminentItemHoverBackground": "Colore di sfondo degli elementi rilevanti della barra di stato al passaggio del mouse. Gli elementi rilevanti spiccano rispetto ad altre voci della barra di stato. La barra di stato è visualizzata nella parte inferiore della finestra.", + "activityBarBackground": "Colore di sfondo della barra attività. La barra attività viene visualizzata nella parte inferiore sinistra/destra e consente il passaggio tra diverse visualizzazioni della barra laterale", + "activityBarForeground": "Colore primo piano della barra attività (ad es. quello utilizzato per le icone). La barra attività viene mostrata all'estrema sinistra o destra e permette di alternare le visualizzazioni della barra laterale.", + "activityBarBorder": "Colore del bordo della barra attività che la separa dalla barra laterale. La barra di attività viene mostrata all'estrema sinistra o destra e permette di alternare le visualizzazioni della barra laterale.", + "activityBarDragAndDropBackground": "Colore feedback drag and drop per gli elementi della barra di attività. Il colore dovrebbe avere una trasparenza impostata in modo che le voci della barra di attività possano ancora essere visibili. La barra di attività viene mostrata all'estrema sinistra o destra e permette di alternare le visualizzazioni della barra laterale.", + "activityBarBadgeBackground": "Colore di sfondo della notifica utente dell'attività. La barra attività viene visualizzata all'estrema sinistra o all'estrema destra e consente di spostarsi tra le visualizzazioni della barra laterale.", + "activityBarBadgeForeground": "Colore primo piano della notifica utente dell'attività. La barra attività viene visualizzata all'estrema sinistra o all'estrema destra e consente di spostarsi tra le visualizzazioni della barra laterale.", + "sideBarBackground": "Colore di sfondo della barra laterale. La barra laterale è il contenitore per visualizzazioni come Explorer e ricerca.", + "sideBarForeground": "Colore primo piano della barra laterale. La barra laterale è il contenitore per le visualizzazioni come Esplora risorse e Cerca.", + "sideBarBorder": "Colore del bordo della barra laterale che la separa all'editor. La barra laterale è il contenitore per visualizzazioni come Esplora risorse e Cerca.", + "sideBarTitleForeground": "Colore primo piano del titolo della barra laterale. La barra laterale è il contenitore per visualizzazioni come Explorer e ricerca.", + "sideBarDragAndDropBackground": "Colore di retroazione di trascinamento della selezione per le sezioni della barra laterale. Il colore dovrebbe avere una trasparenza impostata in modo che le sezioni della barra laterale siano ancora visibili. La barra laterale è il contenitore di visualizzazioni come Esplora risorse e Cerca.", + "sideBarSectionHeaderBackground": "Colore di sfondo dell'intestazione di sezione della barra laterale. La barra laterale è il contenitore di visualizzazioni quali Esplora risorse e Cerca.", + "sideBarSectionHeaderForeground": "Colore primo piano dell'intestazione di sezione della barra laterale. La barra laterale è il contenitore di visualizzazioni come Esplora risorse e Cerca.", + "titleBarActiveForeground": "Colore primo piano della barra del titolo quando la finestra è attiva. Si noti che questo colore è attualmente supportato solo su macOS.", + "titleBarInactiveForeground": "Colore primo piano della barra del titolo quando la finestra è inattiva. Si noti che questo colore è attualmente supportato solo su macOS.", + "titleBarActiveBackground": "Colore di sfondo della barra di titolo quando la finestra è attiva. Si noti che questo colore è attualmente solo supportati su macOS.", + "titleBarInactiveBackground": "Colore di sfondo della barra del titolo quando la finestra è inattiva. Si noti che questo colore è attualmente supportato solo su macOS.", + "titleBarBorder": "Colore del bordo della barra di stato. Si noti che questo colore è attualmente supportato solo su macOS.", + "notificationsForeground": "Colore primo piano delle notifiche. Le notifiche scorrono dalla parte superiore della finestra.", + "notificationsBackground": "Colore di sfondo delle notifiche. Le notifiche scorrono dalla parte superiore della finestra.", + "notificationsButtonBackground": "Colore di sfondo del pulsante delle notifiche. Le notifiche scorrono dalla parte superiore della finestra.", + "notificationsButtonHoverBackground": "Colore di sfondo del pulsante delle notifiche al passaggio del mouse. Le notifiche scorrono dalla parte superiore della finestra.", + "notificationsButtonForeground": "Colore primo piano del pulsante delle notifiche. Le notifiche scorrono dalla parte superiore della finestra.", + "notificationsInfoBackground": "Colore di sfondo delle notifiche informative. Le notifiche scorrono dalla parte superiore della finestra.", + "notificationsInfoForeground": "Colore primo piano delle notifiche informative. Le notifiche scorrono dalla parte superiore della finestra.", + "notificationsWarningBackground": "Colore di sfondo delle notifiche di avviso. Le notifiche scorrono dalla parte superiore della finestra.", + "notificationsWarningForeground": "Colore primo piano delle notifiche di avviso. Le notifiche scorrono dalla parte superiore della finestra.", + "notificationsErrorBackground": "Colore di sfondo delle notifiche di errore. Le notifiche scorrono dalla parte superiore della finestra.", + "notificationsErrorForeground": "Colore primo piano delle notifiche di errore. Le notifiche scorrono dalla parte superiore della finestra." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/ita/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..c98b095872 --- /dev/null +++ b/i18n/ita/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "Chiudi editor", + "closeWindow": "Chiudi finestra", + "closeWorkspace": "Chiudi area di lavoro", + "noWorkspaceOpened": "In questa istanza non ci sono attualmente aree di lavoro aperte da chiudere.", + "newWindow": "Nuova finestra", + "toggleFullScreen": "Attiva/Disattiva schermo intero", + "toggleMenuBar": "Attiva/Disattiva barra dei menu", + "toggleDevTools": "Attiva/Disattiva strumenti di sviluppo", + "zoomIn": "Zoom avanti", + "zoomOut": "Zoom indietro", + "zoomReset": "Reimposta zoom", + "appPerf": "Prestazioni all'avvio", + "reloadWindow": "Ricarica finestra", + "switchWindowPlaceHolder": "Selezionare una finestra a cui passare", + "current": "Finestra corrente", + "close": "Chiudi finestra", + "switchWindow": "Cambia finestra...", + "quickSwitchWindow": "Cambio rapido finestra...", + "workspaces": "aree di lavoro", + "files": "file", + "openRecentPlaceHolderMac": "Selezionare per aprirlo (tenere premuto CMD per aprire l'elemento in una nuova finestra)", + "openRecentPlaceHolder": "Selezionare per aprirlo (tenere premuto CTRL per aprire l'elemento in una nuova finestra)", + "remove": "Rimuovi dagli elementi aperti di recente", + "openRecent": "Apri recenti...", + "quickOpenRecent": "Apertura rapida recenti...", + "closeMessages": "Chiudi messaggi di notifica", + "reportIssues": "Segnala problemi", + "reportPerformanceIssue": "Segnala problema di prestazioni", + "keybindingsReference": "Riferimento per tasti di scelta rapida", + "openDocumentationUrl": "Documentazione", + "openIntroductoryVideosUrl": "Video introduttivi", + "openTipsAndTricksUrl": "Suggerimenti e trucchi", + "toggleSharedProcess": "Attiva/Disattiva processo condiviso", + "navigateLeft": "Passa alla visualizzazione a sinistra", + "navigateRight": "Passa alla visualizzazione a destra", + "navigateUp": "Passa alla visualizzazione in alto", + "navigateDown": "Passa alla visualizzazione in basso", + "increaseViewSize": "Aumenta la dimensione della visualizzazione corrente", + "decreaseViewSize": "Diminuisce la dimensione della visualizzazione corrente" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/ita/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/ita/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/electron-browser/crashReporter.i18n.json b/i18n/ita/src/vs/workbench/electron-browser/crashReporter.i18n.json new file mode 100644 index 0000000000..168c138f47 --- /dev/null +++ b/i18n/ita/src/vs/workbench/electron-browser/crashReporter.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetria", + "telemetry.enableCrashReporting": "Consente l'invio di segnalazioni di arresto anomalo del sistema a Microsoft.\nPer rendere effettiva questa opzione, è necessario riavviare." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/ita/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..56da4192d9 --- /dev/null +++ b/i18n/ita/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "L'host dell'estensione non è stato avviato entro 10 secondi. Potrebbe essersi arrestato alla prima riga e richiedere un debugger per continuare.", + "extensionHostProcess.startupFail": "L'host dell'estensione non è stato avviato entro 10 secondi. Potrebbe essersi verificato un problema.", + "extensionHostProcess.error": "Errore restituito dall'host dell'estensione: {0}", + "devTools": "Strumenti di sviluppo", + "extensionHostProcess.crash": "L'host dell'estensione è stato terminato in modo imprevisto. Ricaricare la finestra per ripristinare." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/ita/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..c24731bf5a --- /dev/null +++ b/i18n/ita/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "Visualizza", + "help": "Guida", + "file": "File", + "workspaces": "Aree di lavoro", + "developer": "Sviluppatore", + "showEditorTabs": "Controlla se visualizzare o meno gli editor aperti in schede.", + "editorTabCloseButton": "Controlla la posizione dei pulsanti di chiusura delle schede dell'editor oppure li disabilita quando è impostata su 'off'.", + "showIcons": "Controlla se visualizzare o meno un'icona per gli editor aperti. Richiede l'abilitazione anche di un tema dell'icona.", + "enablePreview": "Controlla se gli editor aperti vengono visualizzati come anteprima. Le anteprime editor vengono riutilizzate finché vengono mantenute, ad esempio tramite doppio clic o modifica.", + "enablePreviewFromQuickOpen": "Controlla se gli editor aperti da Quick Open vengono visualizzati come anteprima. Le anteprime editor vengono riutilizzate finché vengono mantenute, ad esempio tramite doppio clic o modifica.", + "editorOpenPositioning": "Controlla la posizione in cui vengono aperti gli editor. Selezionare 'left' o 'right' per aprire gli editor a sinistra o a destra di quello attualmente attivo. Selezionare 'first' o 'last' per aprire gli editor indipendentemente da quello attualmente attivo.", + "revealIfOpen": "Controlla se un editor viene visualizzato in uno qualsiasi dei gruppi visibili se viene aperto. Se l'opzione è disabilitata, un editor verrà aperto preferibilmente nel gruppo di editor attualmente attivo. Se è abilitata, un editor già aperto verrà visualizzato e non aperto di nuovo nel gruppo di editor attualmente attivo. Nota: in alcuni casi questa impostazione viene ignorata, ad esempio quando si forza l'apertura di un editor in un gruppo specifico oppure a lato del gruppo attualmente attivo.", + "commandHistory": "Controlla il numero di comandi usati di recente da mantenere nella cronologia per il riquadro comandi. Impostare su 0 per disabilitare la cronologia dei comandi.", + "preserveInput": "Controlla se l'ultimo input digitato nel riquadro comandi deve essere ripristinato alla successiva riapertura del riquadro.", + "closeOnFocusLost": "Controlla se Quick Open deve essere chiuso automaticamente quando perde lo stato attivo.", + "openDefaultSettings": "Controlla se all'apertura delle impostazioni viene aperto anche un editor che mostra tutte le impostazioni predefinite.", + "sideBarLocation": "Controlla la posizione della barra laterale. Può essere visualizzata a sinistra o a destra del workbench.", + "statusBarVisibility": "Controlla la visibilità della barra di stato nella parte inferiore del workbench.", + "activityBarVisibility": "Controlla la visibilità della barra attività nel workbench.", + "closeOnFileDelete": "Controlla se gli editor che visualizzano un file devono chiudersi automaticamente quando il file viene eliminato o rinominato da un altro processo. Se si disabilita questa opzione, in una simile circostanza l'editor verrà aperto e i file risulteranno modificati ma non salvati. Nota: se si elimina il file dall'interno dell'applicazione, l'editor verrà sempre chiuso e i file modificati ma non salvati non verranno mai chiusi allo scopo di salvaguardare i dati.", + "fontAliasing": "Controlla il metodo di aliasing dei caratteri nell'area di lavoro.\n- impostazione predefinita: anti-aliasing dei caratteri a livello di sub-pixel. Nella maggior parte delle visualizzazioni non retina consentirà di ottenere un testo con il massimo contrasto.\n- anti-aliasing: anti-aliasing dei caratteri a livello di pixel, invece che a livello di sub-pixel. Consente di visualizzare i caratteri più chiari.\n- nessuno: disabilita l'anti-aliasing dei caratteri. Il testo verrà visualizzato con contorni irregolari.", + "workbench.fontAliasing.default": "Anti-aliasing dei caratteri a livello di sub-pixel. Nella maggior parte delle visualizzazioni non retina consentirà di ottenere un testo con il massimo contrasto.", + "workbench.fontAliasing.antialiased": "Anti-aliasing dei caratteri a livello di pixel, invece che a livello di sub-pixel. Consente di visualizzare i caratteri più chiari.", + "workbench.fontAliasing.none": "Disabilita l'anti-aliasing dei caratteri. Il testo verrà visualizzato con contorni irregolari. ", + "swipeToNavigate": "Scorrere orizzontalmente con tre dita per spostarsi tra i file aperti.", + "workbenchConfigurationTitle": "Area di lavoro", + "window.openFilesInNewWindow.on": "I file verranno aperti in una nuova finestra", + "window.openFilesInNewWindow.off": "I file verranno aperti nella finestra con la cartella dei file aperta o nell'ultima finestra attiva", + "window.openFilesInNewWindow.default": "I file verranno aperti nella finestra con la cartella dei file aperta o nell'ultima finestra attiva a meno che non vengano aperti tramite il pannello Dock o da Finder (solo MacOS)", + "openFilesInNewWindow": "Controlla se i file devono essere aperti in una nuova finestra.\n- default: i file verranno aperti nella finestra con la cartella dei file aperta o nell'ultima finestra attiva a meno che non vengano aperti tramite il pannello Dock o da Finder (solo MacOS)\n- on: i file verranno aperti in una nuova finestra\n- off: i file verranno aperti nella finestra con la cartella dei file aperta o nell'ultima finestra attiva\nNota: possono comunque verificarsi casi in cui questa impostazione viene ignorata, ad esempio quando si usa l'opzione della riga di comando -new-window o -reuse-window.", + "window.openFoldersInNewWindow.on": "Le cartelle verranno aperte in una nuova finestra", + "window.openFoldersInNewWindow.off": "Le cartelle sostituiranno l'ultima finestra attiva", + "window.openFoldersInNewWindow.default": "Le cartelle verranno aperte in una nuova finestra a meno che non si selezioni una cartella dall'interno dell'applicazione, ad esempio tramite il menu File", + "openFoldersInNewWindow": "Controlla se le cartelle devono essere aperte in una nuova finestra o sostituire l'ultima finestra attiva.\n- default: le cartelle verranno aperte in una nuova finestra a meno che non si selezioni una cartella dall'interno dell'applicazione, ad esempio tramite il menu File\n- on: le cartelle verranno aperte in una nuova finestra\n- off: le cartelle sostituiranno l'ultima finestra attiva\nNota: possono comunque verificarsi casi in cui questa impostazione viene ignorata, ad esempio quando si usa l'opzione della riga di comando -new-window o -reuse-window.", + "window.reopenFolders.all": "Riapre tutte le finestre.", + "window.reopenFolders.folders": "Riapre tutte le cartelle. Le aree di lavoro vuote non verranno ripristinate.", + "window.reopenFolders.one": "Riapre l'ultima finestra attiva.", + "window.reopenFolders.none": "Non riapre mai una finestra. Inizia sempre con una finestra vuota.", + "restoreWindows": "Controlla la modalità di riapertura delle finestre dopo un riavvio. Selezionare 'none' per iniziare sempre con un'area di lavoro vuota, 'one' per riaprire l'ultima finestra usata, 'folders' per riaprire tutte le finestre con cartelle aperte oppure 'all' per riaprire tutte le finestre dell'ultima sessione.", + "restoreFullscreen": "Controlla se una finestra deve essere ripristinata a schermo intero se è stata chiusa in questa modalità.", + "zoomLevel": "Consente di modificare il livello di zoom della finestra. Il valore originale è 0 e ogni incremento superiore (ad esempio 1) o inferiore (ad esempio -1) rappresenta un aumento o una diminuzione del 20% della percentuale di zoom. È anche possibile immettere valori decimali per modificare il livello di zoom con maggiore granularità.", + "title": "Controlla il titolo della finestra in base all'editor attivo. Le variabili vengono sostituite in base al contesto:\n${activeEditorShort}: ad esempio myFile.txt\n${activeEditorMedium}: ad esempio myFolder/myFile.txt\n${activeEditorLong}: ad esempio /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: ad esempio myFolder\n${folderPath}: ad esempio /Users/Development/myFolder\n${rootName}: ad esempio myFolder1, myFolder2, myFolder3\n${rootPath}: ad esempio /Users/Development/myWorkspace\n${appName}: ad esempio VS Code\n${dirty}: indicatore dirty se l'editor attivo è dirty\n${separator}: separatore condizionale (\" - \") visualizzato solo quando è racchiuso tra variabili con valori", + "window.newWindowDimensions.default": "Apre nuove finestre al centro della schermata.", + "window.newWindowDimensions.inherit": "Apre nuove finestre le cui dimensioni sono uguali a quelle dell'ultima finestra attiva.", + "window.newWindowDimensions.maximized": "Apre nuove finestre ingrandite a schermo intero.", + "window.newWindowDimensions.fullscreen": "Apre nuove finestre nella modalità a schermo intero.", + "newWindowDimensions": "Controlla le dimensioni relative all'apertura di una nuova finestra quando almeno un'altra finestra è già aperta. Per impostazione predefinita, una nuova finestra di dimensioni ridotte viene aperta al centro della schermata. Se è impostata su 'inherit', la finestra assumerà le stesse dimensioni dell'ultima finestra attiva. Se è impostata su 'maximized', la finestra aperta risulterà ingrandita, mentre con 'fullscreen' verrà visualizzata a schermo intero. Sia noti che questa impostazione non impatta sulla prima finestra che era stata aperta. La prima finestra si riaprirà sempre con la dimensione e la posizione che aveva prima della chiusura.", + "closeWhenEmpty": "Controlla se con la chiusura dell'ultimo editor deve essere chiusa ancge la finestra. Questa impostazione viene applicata solo alle finestre che non contengono cartelle.", + "window.menuBarVisibility.default": "Il menu è nascosto solo nella modalità a schermo intero.", + "window.menuBarVisibility.visible": "Il menu è sempre visibile, anche nella modalità a schermo intero.", + "window.menuBarVisibility.toggle": "Il menu è nascosto ma può essere visualizzato premendo ALT.", + "window.menuBarVisibility.hidden": "Il menu è sempre nascosto.", + "menuBarVisibility": "Controlla la visibilità della barra dei menu. L'impostazione 'toggle' indica che la barra dei menu è nascosta e che per visualizzarla è necessario premere una sola volta il tasto ALT. Per impostazione predefinita, la barra dei menu è visibile a meno che la finestra non sia a schermo intero.", + "enableMenuBarMnemonics": "Se abilitato, i menu principali possono essere aperti tramite tasti di scelta rapida Alt + tasto. Disattivare i tasti di scelta permette invece di associare questi tasti di scelta rapida Alt + tasto ai comandi dell'editor.", + "autoDetectHighContrast": "Se è abilitata, passa automaticamente a un tema a contrasto elevato se Windows usa un tema di questo tipo e al tipo scuro quando non si usa più un tema a contrasto elevato Windows.", + "titleBarStyle": "Consente di modificare l'aspetto della barra del titolo della finestra. Per applicare le modifiche, è necessario un riavvio completo.", + "window.nativeTabs": "Abilita le finestre di tab per macOS Sierra. La modifica richiede un riavvio. Eventuali personalizzazioni della barra del titolo verranno disabilitate", + "windowConfigurationTitle": "Finestra", + "zenModeConfigurationTitle": "Modalità Zen", + "zenMode.fullScreen": "Consente di controllare se attivando la modalità Zen anche l'area di lavoro passa alla modalità schermo intero.", + "zenMode.hideTabs": "Controlla se attivando la modalità Zen vengono nascoste anche le schede del workbench.", + "zenMode.hideStatusBar": "Controlla se attivando la modalità Zen viene nascosta anche la barra di stato nella parte inferiore del workbench.", + "zenMode.hideActivityBar": "Controlla se attivando la modalità Zen viene nascosta anche la barra di stato alla sinistra del workbench", + "zenMode.restore": "Controlla se una finestra deve essere ripristinata nella modalità Zen se è stata chiusa in questa modalità." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/electron-browser/main.i18n.json b/i18n/ita/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..ca3a02bdea --- /dev/null +++ b/i18n/ita/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "Non è stato possibile caricare un file obbligatorio. Non si è più connessi a Internet oppure il server a cui si è connessi è offline. Per riprovare, aggiornare il browser.", + "loaderErrorNative": "Non è stato possibile caricare un file obbligatorio. Riavviare l'applicazione e riprovare. Dettagli: {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/ita/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..082923a548 --- /dev/null +++ b/i18n/ita/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "È consigliabile non eseguire il codice come 'radice'." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/electron-browser/window.i18n.json b/i18n/ita/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..a7ade55473 --- /dev/null +++ b/i18n/ita/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "Annulla", + "redo": "Ripristina", + "cut": "Taglia", + "copy": "Copia", + "paste": "Incolla", + "selectAll": "Seleziona tutto" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/ita/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..7c609373b5 --- /dev/null +++ b/i18n/ita/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "Sviluppatore", + "file": "File" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/ita/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..3a35a349a9 --- /dev/null +++ b/i18n/ita/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "Il percorso {0} non punta a un Test Runner di estensioni valido." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/ita/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..38120c726c --- /dev/null +++ b/i18n/ita/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Non è stato possibile analizzare {0}: {1}.", + "fileReadFail": "Non è possibile leggere il file {0}: {1}.", + "jsonsParseFail": "Non è stato possibile analizzare {0} o {1}: {2}.", + "missingNLSKey": "Il messaggio per la chiave {0} non è stato trovato." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..9accc3960f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "Installa il comando '{0}' in PATH", + "not available": "Questo comando non è disponibile", + "successIn": "Il comando della shell '{0}' è stato installato in PATH.", + "warnEscalation": "Visual Studio Code eseguirà 'osascript' per richiedere i privilegi di amministratore per installare il comando della shell.", + "ok": "OK", + "cantCreateBinFolder": "Non è possibile creare '/usr/local/bin'.", + "cancel2": "Annulla", + "aborted": "Operazione interrotta", + "uninstall": "Disinstalla il comando '{0}' da PATH", + "successFrom": "Il comando della shell '{0}' è stato disinstallato da PATH.", + "shellCommand": "Comando della shell" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..f356a56c5e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "Modifica dell'impostazione 'editor.accessibilitySupport' a 'on' in corso.", + "openingDocs": "Apertura della pagina di documentazione sull'accessibilità di VS Code in corso.", + "introMsg": "Grazie per aver provato le opzioni di accessibilità di Visual Studio Code.", + "status": "Stato:", + "changeConfigToOnMac": "Premere Comando+E per configurare l'editor per essere definitivamente ottimizzato per l'utilizzo con un un'utilità per la lettura dello schermo.", + "changeConfigToOnWinLinux": "Premere Control+E per configurare l'editor per essere definitivamente ottimizzato per l'utilizzo con un un'utilità per la lettura dello schermo.", + "auto_unknown": "L'editor è configurato per utilizzare le API della piattaforma per rilevare quando è collegata un'utilità per la lettura dello schermo ma il runtime corrente non lo supporta.", + "auto_on": "L'editor ha rilevato automaticamente che è collegata un'utilità per la lettura dello schermo.", + "auto_off": "L'editor è configurato per rilevare automaticamente quando è collegata un'utilità per la lettura dello schermo, che non è collegata in questo momento.", + "configuredOn": "L'editor è configurato per essere definitivamente ottimizzato per l'utilizzo con un'utilità per la lettura dello schermo - è possibile modificare questo modificando l'impostazione 'editor.accessibilitySupport'.", + "configuredOff": "L'editor è configurato per non essere ottimizzato per l'utilizzo con un'utilità per la lettura dello schermo.", + "tabFocusModeOnMsg": "Premere TAB nell'editor corrente per spostare lo stato attivo sull'elemento con stato attivabile successivo. Per attivare/disattivare questo comportamento, premere {0}.", + "tabFocusModeOnMsgNoKb": "Premere TAB nell'editor corrente per spostare lo stato attivo sull'elemento con stato attivabile successivo. Il comando {0} non può essere attualmente attivato con un tasto di scelta rapida.", + "tabFocusModeOffMsg": "Premere TAB nell'editor corrente per inserire il carattere di tabulazione. Per attivare/disattivare questo comportamento, premere {0}.", + "tabFocusModeOffMsgNoKb": "Premere TAB nell'editor corrente per inserire il carattere di tabulazione. Il comando {0} non può essere attualmente attivato con un tasto di scelta rapida.", + "openDocMac": "Premere Comando+H per aprire una finestra del browser con maggiori informazioni relative all'accessibilità di VS Code.", + "openDocWinLinux": "Premere Control+H per aprire una finestra del browser con maggiori informazioni relative all'accessibilità di VS Code.", + "outroMsg": "Per chiudere questa descrizione comando e tornare all'editor, premere ESC o MAIUSC+ESC.", + "ShowAccessibilityHelpAction": "Visualizza la Guida sull'accessibilità" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..8a5a807b69 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "Sviluppatore: controlla mapping tasti" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f13440e131 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Sviluppatore: controlla ambiti TextMate", + "inspectTMScopesWidget.loading": "Caricamento..." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..155a6e5a44 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Errori durante l'analisi di {0}: {1}", + "schema.openBracket": "Sequenza di stringa o carattere parentesi quadra di apertura.", + "schema.closeBracket": "Sequenza di stringa o carattere parentesi quadra di chiusura.", + "schema.comments": "Definisce i simboli di commento", + "schema.blockComments": "Definisce il modo in cui sono contrassegnati i commenti per il blocco.", + "schema.blockComment.begin": "Sequenza di caratteri che indica l'inizio di un commento per il blocco.", + "schema.blockComment.end": "Sequenza di caratteri che termina i commenti per il blocco.", + "schema.lineComment": "Sequenza di caratteri che indica l'inizio di un commento per la riga.", + "schema.brackets": "Definisce i simboli di parentesi quadra che aumentano o riducono il rientro.", + "schema.autoClosingPairs": "Definisce le coppie di parentesi quadre. Quando viene immessa una parentesi quadra di apertura, quella di chiusura viene inserita automaticamente.", + "schema.autoClosingPairs.notIn": "Definisce un elenco di ambiti in cui la corrispondenza automatica delle coppie è disabilitata.", + "schema.surroundingPairs": "Definisce le coppie di parentesi quadre che possono essere usate per racchiudere una stringa selezionata.", + "schema.wordPattern": "La definizione di parola per il linguaggio.", + "schema.wordPattern.pattern": "Il modello di RegExp utilizzato per trovare parole.", + "schema.wordPattern.flags": "I flag di RegExp utilizzati per trovare parole.", + "schema.wordPattern.flags.errorMessage": "Deve corrispondere al modello `/^([gimuy]+)$/`.", + "schema.indentationRules": "Impostazioni di rientro del linguaggio.", + "schema.indentationRules.increaseIndentPattern": "Se una riga corrisponde a questo criterio, tutte le linee successive devono essere rientrate una volta (fino alla corrispondenza di un'altra regola).", + "schema.indentationRules.increaseIndentPattern.pattern": "Criterio di RegExp per increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.flags": "Flag di RegExp per increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.errorMessage": "Deve corrispondere al modello `/^([gimuy]+)$/`.", + "schema.indentationRules.decreaseIndentPattern": "Se una riga corrisponde a questo criterio, il rientro di tutte le linee successive verrà ridotto una volta (fino alla corrispondenza di un'altra regola).", + "schema.indentationRules.decreaseIndentPattern.pattern": "Criterio di RegExp per decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.flags": "Flag di RegExp per decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "Deve corrispondere al modello `/^([gimuy]+)$/`.", + "schema.indentationRules.indentNextLinePattern": "Se una riga corrisponde a questo criterio, il rientro verrà applicato una sola volta **solo alla riga successiva**.", + "schema.indentationRules.indentNextLinePattern.pattern": "Criterio di RegExp per indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.flags": "Flag di RegExp per indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.errorMessage": "Deve corrispondere al modello `/^([gimuy]+)$/`.", + "schema.indentationRules.unIndentedLinePattern": "Se una riga corrisponde a questo criterio, il rientro non deve essere modificato e la riga non deve essere valutata rispetto alle altre regole.", + "schema.indentationRules.unIndentedLinePattern.pattern": "Criterio di RegExp per unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.flags": "Flag di RegExp per unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "Deve corrispondere al modello `/^([gimuy]+)$/`." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f13440e131 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Sviluppatore: controlla ambiti TextMate", + "inspectTMScopesWidget.loading": "Caricamento..." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..c2d215ea16 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "Visualizza: Attiva/Disattiva mini mappa" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..b6f1008132 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Modificatore per l'attivazione/disattivazione multi-cursore" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..1bac09fb27 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "Visualizza: Attiva/Disattiva caratteri di controllo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..552d273045 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "Visualizza: Attiva/Disattiva rendering spazi vuoti" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..a4c959958e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "Visualizza: Attiva/Disattiva ritorno a capo automatico", + "wordWrap.notInDiffEditor": "Non è possibile attivare/disattivare il ritorno a capo automatico in un editor diff.", + "unwrapMinified": "Disabilita il ritorno a capo automatico per questo file", + "wrapMinified": "Abilita il ritorno a capo automatico per questo file" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..140b9b8328 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "OK", + "wordWrapMigration.dontShowAgain": "Non visualizzare più questo messaggio", + "wordWrapMigration.openSettings": "Apri impostazioni", + "wordWrapMigration.prompt": "L'impostazione `editor.wrappingColumn` è stata deprecata e sostituita da `editor.wordWrap`." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..cf843c2920 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "Interrompe quando l'espressione restituisce true. Premere 'INVIO' per accettare oppure 'ESC' per annullare.", + "breakpointWidgetAriaLabel": "Il programma si arresterà in questo punto solo se la condizione è vera. Premere INVIO per accettare oppure ESC per annullare.", + "breakpointWidgetHitCountPlaceholder": "Interrompe quando viene soddisfatta la condizione del numero di passaggi. Premere 'INVIO' per accettare oppure 'ESC' per annullare.", + "breakpointWidgetHitCountAriaLabel": "Il programma si arresterà in questo punto solo se viene raggiunto il numero di passaggi. Premere INVIO per accettare oppure ESC per annullare.", + "expression": "Espressione", + "hitCount": "Numero di passaggi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..805a776da7 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "Non ci sono configurazioni", + "addConfigTo": "Aggiungi configurazione ({0})...", + "addConfiguration": "Aggiungi configurazione..." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..629019ed1f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "Apri {0}", + "launchJsonNeedsConfigurtion": "Configurare o correggere 'launch.json'", + "noFolderDebugConfig": "Si prega di aprire prima una cartella per consentire una configurazione di debug avanzato.", + "startDebug": "Avvia debug", + "startWithoutDebugging": "Avvia senza eseguire debug", + "selectAndStartDebugging": "Seleziona e avvia il debug", + "restartDebug": "Riavvia", + "reconnectDebug": "Riconnetti", + "stepOverDebug": "Esegui istruzione/routine", + "stepIntoDebug": "Esegui istruzione", + "stepOutDebug": "Esci da istruzione/routine", + "stopDebug": "Arresta", + "disconnectDebug": "Disconnetti", + "continueDebug": "Continua", + "pauseDebug": "Sospendi", + "restartFrame": "Riavvia frame", + "removeBreakpoint": "Rimuovi punto di interruzione", + "removeAllBreakpoints": "Rimuovi tutti i punti di interruzione", + "enableBreakpoint": "Abilita punto di interruzione", + "disableBreakpoint": "Disabilita punto di interruzione", + "enableAllBreakpoints": "Abilita tutti i punti di interruzione", + "disableAllBreakpoints": "Disabilita tutti i punti di interruzione", + "activateBreakpoints": "Attiva punti di interruzione", + "deactivateBreakpoints": "Disattiva punti di interruzione", + "reapplyAllBreakpoints": "Riapplica tutti i punti di interruzione", + "addFunctionBreakpoint": "Aggiungi punto di interruzione della funzione", + "renameFunctionBreakpoint": "Rinomina punto di interruzione della funzione", + "addConditionalBreakpoint": "Aggiungi punto di interruzione condizionale...", + "editConditionalBreakpoint": "Modifica punto di interruzione...", + "setValue": "Imposta valore", + "addWatchExpression": "Aggiungi espressione", + "editWatchExpression": "Modifica espressione", + "addToWatchExpressions": "Aggiungi a espressione di controllo", + "removeWatchExpression": "Rimuovi espressione", + "removeAllWatchExpressions": "Rimuovi tutte le espressioni", + "clearRepl": "Cancella console", + "debugConsoleAction": "Console di debug", + "unreadOutput": "Nuovo output nella console di debug", + "debugFocusConsole": "Console di debug stato attivo", + "focusProcess": "Sposta stato attivo su processo", + "stepBackDebug": "Torna indietro", + "reverseContinue": "Inverti" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..57cbe5e82a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "Colore di sfondo della barra degli strumenti di debug." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..0af270dbef --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "Non è possibile risolvere la risorsa senza una sessione di debug" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..954bdfd5ee --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "Debug: Attiva/Disattiva punto di interruzione", + "columnBreakpointAction": "Debug: Punto di interruzione colonna", + "columnBreakpoint": "Aggiungi punto di interruzione colonna", + "conditionalBreakpointEditorAction": "Debug: Aggiungi Punto di interruzione condizionale...", + "runToCursor": "Esegui fino al cursore", + "debugEvaluate": "Debug: Valuta", + "debugAddToWatch": "Debug: Aggiungi a espressione di controllo", + "showDebugHover": "Debug: Visualizza passaggio del mouse" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..e61e5afaeb --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "Punto di interruzione disabilitato", + "breakpointUnverifieddHover": "Punto di interruzione non verificato", + "breakpointDirtydHover": "Punto di interruzione non verificato. Il file è stato modificato. Riavviare la sessione di debug.", + "breakpointUnsupported": "Punti di interruzione condizionali non supportati da questo tipo di debug" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..3e7bbad0e8 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, debug", + "debugAriaLabel": "Digitare il nome di una configurazione di avvio da eseguire.", + "noConfigurationsMatching": "Non esistono configurazioni di debug corrispondenti", + "noConfigurationsFound": "Non è stata trovata alcuna configurazione di debug. Creare un file 'launch.json'." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..a37211f6d4 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "Colore del bordo del widget Eccezione.", + "debugExceptionWidgetBackground": "Colore di sfondo del widget Eccezione.", + "exceptionThrownWithId": "Si è verificata un'eccezione: {0}", + "exceptionThrown": "Si è verificata un'eccezione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..dec482f406 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "Fare clic per aprire (CMD+clic apre lateralmente)", + "fileLink": "Fare clic per aprire (CTRL+clic apre lateralmente)" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..1eada9e5d3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "Controlla il comportamento della console di debug interna." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..4de986b65a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "non disponibile", + "startDebugFirst": "Per eseguire la valutazione, avviare una sessione di debug" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..456b82bff2 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "Origine sconosciuta" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..e6efa5059c --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "Mostra debug", + "toggleDebugPanel": "Console di debug", + "debug": "Debug", + "debugPanel": "Console di debug", + "variables": "Variabili", + "watch": "Espressione di controllo", + "callStack": "Stack di chiamate", + "breakpoints": "Punti di interruzione", + "view": "Visualizza", + "debugCategory": "Debug", + "debugCommands": "Configurazione di debug", + "debugConfigurationTitle": "Debug", + "allowBreakpointsEverywhere": "Consente l'impostazione del punto di interruzione in qualsiasi file", + "openExplorerOnEnd": "Apre automaticamente la visualizzazione di esplorazione al termine di una sessione di debug", + "inlineValues": "Mostra i valori delle variabili inline nell'editor durante il debug", + "hideActionBar": "Controlla se nascondere la barra delle azioni mobile di debug", + "launch": "Configurazione globale per l'esecuzione del debug. Può essere usata come un'alternativa a \"launch.json\" " +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..d0a9f566b6 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "Si prega di aprire prima una cartella per consentire una configurazione di debug avanzato." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..53fc8bdab9 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "Adattatori di debug per contributes.", + "vscode.extension.contributes.debuggers.type": "Identificatore univoco per questo adattatore di debug.", + "vscode.extension.contributes.debuggers.label": "Nome visualizzato per questo adattatore di debug.", + "vscode.extension.contributes.debuggers.program": "Percorso del programma dell'adattatore di debug. Il percorso è assoluto o relativo alla cartella delle estensioni.", + "vscode.extension.contributes.debuggers.args": "Argomenti facoltativi da passare all'adattatore.", + "vscode.extension.contributes.debuggers.runtime": "Runtime facoltativo nel caso in cui l'attributo del programma non sia un eseguibile ma richieda un runtime.", + "vscode.extension.contributes.debuggers.runtimeArgs": "Argomenti del runtime facoltativo.", + "vscode.extension.contributes.debuggers.variables": "Mapping tra le variabili interattive, ad esempio ${action.pickProcess}, in `launch.json` e un comando.", + "vscode.extension.contributes.debuggers.initialConfigurations": "Configurazioni per generare la versione iniziale di 'launch.json'.", + "vscode.extension.contributes.debuggers.languages": "Elenco dei linguaggi. per cui l'estensione di debug può essere considerata il \"debugger predefinito\".", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Se è specificato, Visual Studio Code chiamerà questo comando per determinare il percorso eseguibile della scheda di debug e gli argomenti da passare.", + "vscode.extension.contributes.debuggers.startSessionCommand": "Se è specificato, Visual Studio Code chiamerà questo comando per le azioni \"debug\" o \"run\" previste come destinazione di questa estensione.", + "vscode.extension.contributes.debuggers.configurationSnippets": "Frammenti per l'aggiunta di nuove configurazioni in 'launch.json'.", + "vscode.extension.contributes.debuggers.configurationAttributes": "Configurazioni dello schema JSON per la convalida di 'launch.json'.", + "vscode.extension.contributes.debuggers.windows": "Impostazioni specifiche di Windows.", + "vscode.extension.contributes.debuggers.windows.runtime": "Runtime usato per Windows.", + "vscode.extension.contributes.debuggers.osx": "Impostazioni specifiche di OS X.", + "vscode.extension.contributes.debuggers.osx.runtime": "Runtime usato per OS X.", + "vscode.extension.contributes.debuggers.linux": "Impostazioni specifiche di Linux.", + "vscode.extension.contributes.debuggers.linux.runtime": "Runtime usato per Linux.", + "vscode.extension.contributes.breakpoints": "Punti di interruzione per contributes.", + "vscode.extension.contributes.breakpoints.language": "Consente i punti di interruzione per questo linguaggio.", + "app.launch.json.title": "Launch", + "app.launch.json.version": "Versione di questo formato di file.", + "app.launch.json.configurations": "Elenco delle configurazioni. Aggiungere nuove configurazioni o modificare quelle esistenti con IntelliSense.", + "app.launch.json.compounds": "Elenco degli elementi compounds. Ogni elemento compounds fa riferimento a più configurazioni che verranno avviate insieme.", + "app.launch.json.compound.name": "Nome dell'elemento compounds. Viene visualizzato nel menu a discesa della configurazione di avvio.", + "app.launch.json.compounds.configurations": "Nomi delle configurazioni che verranno avviate per questo elemento compounds.", + "debugNoType": "L'adattatore di debug 'type' non può essere omesso e deve essere di tipo 'string'.", + "selectDebug": "Seleziona ambiente", + "DebugConfig.failed": "Non è possibile creare il file 'launch.json' all'interno della cartella '.vscode' ({0})." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..6fe496e3c3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "Rimuovi punti di interruzione", + "removeBreakpointOnColumn": "Rimuovi punto di interruzione a colonna {0}", + "removeLineBreakpoint": "Rimuovi punto di interruzione riga", + "editBreakpoints": "Modifica punti di interruzione", + "editBreakpointOnColumn": "Modifica punto di interruzione a colonna {0}", + "editLineBrekapoint": "Modifica punto di interruzione riga", + "enableDisableBreakpoints": "Abilita/Disabilita punti di interruzione", + "disableColumnBreakpoint": "Disabilita punto di interruzione a colonna {0}", + "disableBreakpointOnLine": "Disabilita punto di interruzione riga", + "enableBreakpoints": "Abilita punto di interruzione a colonna {0}", + "enableBreakpointOnLine": "Abilita punto di interruzione riga", + "addBreakpoint": "Aggiungi punto di interruzione", + "addConfiguration": "Aggiungi configurazione..." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..be7ffd078e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "Esegui debug al passaggio del mouse" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..c182785da2 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "Per questo oggetto vengono visualizzati solo i valori primitivi.", + "debuggingPaused": "Il debug è stato sospeso. Motivo: {0}, {1} {2}", + "debuggingStarted": "Il debug è stato avviato.", + "debuggingStopped": "Il debug è stato arrestato.", + "breakpointAdded": "Aggiunto un punto di interruzione a riga {0} del file {1}", + "breakpointRemoved": "Rimosso un punto di interruzione a riga {0} del file {1}", + "compoundMustHaveConfigurations": "Per avviare più configurazioni, deve essere impostato l'attributo \"configurations\" dell'elemento compounds.", + "configMissing": "In 'launch.json' manca la configurazione '{0}'.", + "debugTypeNotSupported": "Il tipo di debug configurato '{0}' non è supportato.", + "debugTypeMissing": "Manca la proprietà 'type' per la configurazione di avvio scelta.", + "preLaunchTaskErrors": "Sono stati rilevati errori di compilazione durante preLaunchTask '{0}'.", + "preLaunchTaskError": "È stato rilevato un errore di compilazione durante preLaunchTask '{0}'.", + "preLaunchTaskExitCode": "L'attività di preavvio '{0}' è stata terminata ed è stato restituito il codice di uscita {1}.", + "debugAnyway": "Eseguire comunque il debug", + "noFolderWorkspaceDebugError": "Non è possibile eseguire il debug del file attivo. Assicurarsi che sia salvato su disco e che sia installata un'estensione di debug per tale tipo di file.", + "NewLaunchConfig": "Impostare il file di configurazione di avvio per l'applicazione. {0}", + "DebugTaskNotFound": "L'attività di preavvio '{0}' non è stata trovata." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..8939c52c83 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "Processo", + "paused": "In pausa", + "running": "In esecuzione", + "thread": "Thread", + "pausedOn": "In pausa su {0}", + "loadMoreStackFrames": "Carica altri stack frame", + "threadAriaLabel": "Thread {0}, stack di chiamate, debug", + "stackFrameAriaLabel": "Riga{1} {2} dello stack frame {0}, stack di chiamate, debug", + "variableValueAriaLabel": "Digitare il nuovo valore della variabile", + "variableScopeAriaLabel": "Ambito {0}, variabili, debug", + "variableAriaLabel": "Valore {1} di {0}, variabili, debug", + "watchExpressionPlaceholder": "Espressione da controllare", + "watchExpressionInputAriaLabel": "Digitare l'espressione di controllo", + "watchExpressionAriaLabel": "Valore {1} di {0}, espressione di controllo, debug", + "watchVariableAriaLabel": "Valore {1} di {0}, espressione di controllo, debug", + "functionBreakpointPlaceholder": "Funzione per cui inserire il punto di interruzione", + "functionBreakPointInputAriaLabel": "Digitare il punto di interruzione della funzione", + "functionBreakpointsNotSupported": "Punti di interruzione delle funzioni non sono supportati da questo tipo di debug", + "breakpointAriaLabel": "Riga {0} {1} del punto di interruzione, punti di interruzione, debug", + "functionBreakpointAriaLabel": "Punto di interruzione {0} della funzione, punti di interruzione, debug", + "exceptionBreakpointAriaLabel": "Punto di interruzione {0} dell'eccezione, punti di interruzione, debug" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..4f6db5e169 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "Sezione Variabili", + "variablesAriaTreeLabel": "Esegui debug variabili", + "expressionsSection": "Sezione Espressioni", + "watchAriaTreeLabel": "Esegui debug espressioni di controllo", + "callstackSection": "Sezione Stack di chiamate", + "debugStopped": "In pausa su {0}", + "callStackAriaLabel": "Esegui debug stack di chiamate", + "breakpointsSection": "Sezione Punti di interruzione", + "breakpointsAriaTreeLabel": "Esegui debug punti di interruzione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..d855ad362e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "Copia valore", + "copy": "Copia", + "copyAll": "Copia tutti", + "copyStackTrace": "Copia stack di chiamate" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..38411c675a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "Altre info", + "unableToLaunchDebugAdapter": "Non è possibile avviare l'adattatore di debug da '{0}'.", + "unableToLaunchDebugAdapterNoArgs": "Non è possibile avviare l'adattatore di debug.", + "stoppingDebugAdapter": "{0}. L'adattatore di debug verrà arrestato.", + "debugAdapterCrash": "Il processo dell'adattatore di debug è stato terminato in modo imprevisto" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..88f31e351f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "Pannello del ciclo Read Eval Print", + "actions.repl.historyPrevious": "Cronologia indietro", + "actions.repl.historyNext": "Cronologia avanti", + "actions.repl.acceptInput": "Accetta input da REPL", + "actions.repl.copyAll": "Debug: copia tutto in console" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..1354b0cceb --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "Lo stato dell'oggetto viene acquisito dalla prima valutazione", + "replVariableAriaLabel": "Il valore della variabile {0} è {1}, ciclo Read Eval Print, debug", + "replExpressionAriaLabel": "Il valore dell'espressione {0} è {1}, ciclo Read Eval Print, debug", + "replValueOutputAriaLabel": "{0}, ciclo Read Eval Print, debug", + "replKeyValueOutputAriaLabel": "Il valore della variabile di output {0} è {1}, ciclo Read Eval Print, debug" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..dc78090996 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "Colore di sfondo della barra di stato quando è in corso il debug di un programma. La barra di stato è visualizzata nella parte inferiore della finestra", + "statusBarDebuggingForeground": "Colore primo piano della barra di stato quando è in corso il debug di un programma. La barra di stato è visualizzata nella parte inferiore della finestra", + "statusBarDebuggingBorder": "Colore del bordo della barra di stato che la separa dalla barra laterale e dall'editor durante il debug di un programma. La barra di stato è visualizzata nella parte inferiore della finestra." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..5399050943 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "oggetto del debug", + "debug.terminal.not.available.error": "Il terminale integrato non è disponibile" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..614a0d1f68 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "Il file eseguibile '{0}' dell'adattatore di debug non esiste.", + "debugAdapterCannotDetermineExecutable": "Non è possibile determinare il file eseguibile per l'adattatore di debug '{0}'.", + "launch.config.comment1": "Usare IntelliSense per informazioni sui possibili attributi.", + "launch.config.comment2": "Al passaggio del mouse vengono visualizzate le descrizioni degli attributi esistenti.", + "launch.config.comment3": "Per ulteriori informazioni, visitare: {0}", + "debugType": "Tipo di configurazione.", + "debugTypeNotRecognised": "Il tipo di debug non è riconosciuto. Assicurarsi di avere un'estensione appropriata per il debug installata e che sia abilitata.", + "node2NotSupported": "\"node2\" non è più supportato. In alternativa, usare \"node\" e impostare l'attributo \"protocol\" su \"inspector\".", + "debugName": "Nome della configurazione. Viene visualizzato nel menu a discesa della configurazione di avvio.", + "debugRequest": "Tipo della richiesta di configurazione. Può essere \"launch\" o \"attach\".", + "debugServer": "Solo per lo sviluppo dell'estensione di debug: se si specifica una porta, Visual Studio Code prova a connettersi a un adattatore di debug in esecuzione in modalità server", + "debugPrelaunchTask": "Attività da eseguire prima dell'avvio della sessione di debug.", + "debugWindowsConfiguration": "Attributi della configurazione di avvio specifici di Windows.", + "debugOSXConfiguration": "Attributi della configurazione di avvio specifici di OS X.", + "debugLinuxConfiguration": "Attributi della configurazione di avvio specifici di Linux.", + "deprecatedVariables": "'env.', 'config.' e 'command.' sono deprecati. In alternativa, usare 'env:', 'config:' e 'command:'." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..f1e47bb0e5 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "Mostra comandi Emmet" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..61480becac --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: Saldo (in ingresso)", + "balanceOutward": "Emmet: Saldo (in uscita)" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..fc6d434944 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: andare al punto di modifica precedente", + "nextEditPoint": "Emmet: andare al punto di modifica successivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..147edd90b3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: Valuta espressione matematica" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..b0b91c2143 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: Espandi abbreviazione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..8189de3ebf --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: Aumenta di 0,1", + "incrementNumberByOne": "Emmet: Aumenta di 1", + "incrementNumberByTen": "Emmet: Aumenta di 10", + "decrementNumberByOneTenth": "Emmet: Riduci di 0,1", + "decrementNumberByOne": "Emmet: Riduci di 1", + "decrementNumberByTen": "Emmet: Riduci di 10" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..c6ec03418b --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: Vai alla coppia corrispondente" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..5d0e995969 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: Esegui merge delle righe" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..968b333b74 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: Effettua reflection del valore CSS" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..d50af51779 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: Rimuovi tag" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..f261361ff3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: Seleziona elemento precedente", + "selectNextItem": "Emmet: Seleziona elemento successivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..3f6550ad74 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: Dividi/Unisci tag" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..d8a73c3c30 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: Attiva/Disattiva commento" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..b181582a6f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: Aggiorna dimensioni immagine" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..cbb8ade63a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: Aggiorna tag", + "enterTag": "Immetti tag", + "tag": "Tag" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..ce6db1244e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: Esegui il wrapping con l'abbreviazione", + "enterAbbreviation": "Immetti abbreviazione", + "abbreviation": "Abbreviazione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..da861a266d --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Se abilitate, le abbreviazioni Emmet vengono espanse quando si preme TAB. Non applicabile quando emmet.useNewemmet è impostato su true.", + "emmetPreferences": "Preferenze usate per modificare il comportamento di alcune azioni e i resolver di Emmet. Non applicabile quando emmet.useNewemmet è impostato su true.", + "emmetSyntaxProfiles": "Consente di definire il profilo per la sintassi specificata oppure di usare un profilo personalizzato con regole specifiche.", + "emmetExclude": "Matrice di linguaggi in cui le abbreviazioni Emmet non devono essere espanse.", + "emmetExtensionsPath": "Percorso di una cartella contenente snippet, preferenze e profili Emmet. Quando emmet.useNewEmmet è impostato su true, vengono gestiti solo i profili di questo percorso di estensione.", + "useNewEmmet": "Prova i nuovi moduli emmet (che andrà a sostituire la vecchia libreria singola emmet) per tutte le funzionalità emmet." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json new file mode 100644 index 0000000000..61480becac --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: Saldo (in ingresso)", + "balanceOutward": "Emmet: Saldo (in uscita)" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json new file mode 100644 index 0000000000..f33f368e6e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: Punto di modifica precedente", + "nextEditPoint": "Emmet: Punto di modifica successivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..147edd90b3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: Valuta espressione matematica" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..b0b91c2143 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: Espandi abbreviazione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..8189de3ebf --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: Aumenta di 0,1", + "incrementNumberByOne": "Emmet: Aumenta di 1", + "incrementNumberByTen": "Emmet: Aumenta di 10", + "decrementNumberByOneTenth": "Emmet: Riduci di 0,1", + "decrementNumberByOne": "Emmet: Riduci di 1", + "decrementNumberByTen": "Emmet: Riduci di 10" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..c6ec03418b --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: Vai alla coppia corrispondente" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..5d0e995969 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: Esegui merge delle righe" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..968b333b74 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: Effettua reflection del valore CSS" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json new file mode 100644 index 0000000000..d50af51779 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: Rimuovi tag" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json new file mode 100644 index 0000000000..f261361ff3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: Seleziona elemento precedente", + "selectNextItem": "Emmet: Seleziona elemento successivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..3f6550ad74 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: Dividi/Unisci tag" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..d8a73c3c30 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: Attiva/Disattiva commento" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..b181582a6f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: Aggiorna dimensioni immagine" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json new file mode 100644 index 0000000000..cbb8ade63a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: Aggiorna tag", + "enterTag": "Immetti tag", + "tag": "Tag" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..ce6db1244e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: Esegui il wrapping con l'abbreviazione", + "enterAbbreviation": "Immetti abbreviazione", + "abbreviation": "Abbreviazione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json new file mode 100644 index 0000000000..c20ced2dbb --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Se abilitate, le abbreviazioni Emmet vengono espanse quando si preme TAB.", + "emmetPreferences": "Preferenze usate per modificare il comportamento di alcune azioni e i resolver di Emmet.", + "emmetSyntaxProfiles": "Consente di definire il profilo per la sintassi specificata oppure di usare un profilo personalizzato con regole specifiche.", + "emmetExclude": "Matrice di linguaggi in cui le abbreviazioni Emmet non devono essere espanse.", + "emmetExtensionsPath": "Percorso di una cartella contenente snippet, preferenze e profili Emmet" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..2133d9bc6f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Terminale esterno", + "explorer.openInTerminalKind": "Personalizza il tipo di terminale da avviare.", + "terminal.external.windowsExec": "Personalizza il terminale da eseguire in Windows.", + "terminal.external.osxExec": "Personalizza l'applicazione di terminale da eseguire in OS X.", + "terminal.external.linuxExec": "Personalizza il terminale da eseguire in Linux.", + "globalConsoleActionWin": "Apri nuovo prompt dei comandi", + "globalConsoleActionMacLinux": "Apri nuovo terminale", + "scopedConsoleActionWin": "Apri nel prompt dei comandi", + "scopedConsoleActionMacLinux": "Apri nel terminale", + "openFolderInIntegratedTerminal": "Apri nel terminale" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..b73effa937 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Terminale esterno", + "terminal.external.windowsExec": "Personalizza il terminale da eseguire in Windows.", + "terminal.external.osxExec": "Personalizza l'applicazione di terminale da eseguire in OS X.", + "terminal.external.linuxExec": "Personalizza il terminale da eseguire in Linux.", + "globalConsoleActionWin": "Apri nuovo prompt dei comandi", + "globalConsoleActionMacLinux": "Apri nuovo terminale", + "scopedConsoleActionWin": "Apri nel prompt dei comandi", + "scopedConsoleActionMacLinux": "Apri nel terminale" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/ita/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..bc8b8155a0 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "Console di Visual Studio Code", + "mac.terminal.script.failed": "Lo script '{0}' non è riuscito. Codice di uscita: {1}", + "mac.terminal.type.not.supported": "'{0}' non supportato", + "press.any.key": "Premere un tasto qualsiasi per continuare...", + "linux.term.failed": "'{0}' non riuscito. Codice di uscita: {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..555f05600a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "Visualizzazione personalizzata per contributes", + "vscode.extension.contributes.view.id": "ID univoco usato per identificare la visualizzazione creata tramite vscode.workspace.createTreeView", + "vscode.extension.contributes.view.label": "Stringa leggibile usata per il rendering della visualizzazione", + "vscode.extension.contributes.view.icon": "Percorso dell'icona della visualizzazione", + "vscode.extension.contributes.views": "Visualizzazioni personalizzate per contributes", + "showViewlet": "Mostra {0}", + "view": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..d436fd3b3a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "Aggiorna" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..acfb9aa067 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "Non è stato registrato alcun elemento TreeExplorerNodeProvider con ID {providerId}." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/ita/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..a92f82fa1b --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "Sezione Tree Explorer" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..3ad466c6bd --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "Errore", + "Unknown Dependency": "Dipendenza sconosciuta:" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..24e1986da0 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "Nome dell'estensione", + "extension id": "Identificatore dell'estensione", + "publisher": "Nome dell'editore", + "install count": "Conteggio delle installazioni", + "rating": "Valutazione", + "license": "Licenza", + "details": "Dettagli", + "contributions": "Contributi", + "changelog": "Log delle modifiche", + "dependencies": "Dipendenze", + "noReadme": "File LEGGIMI non disponibile.", + "noChangelog": "Changelog non disponibile.", + "noContributions": "Nessun contributo", + "noDependencies": "Nessuna dipendenza", + "settings": "Impostazioni ({0})", + "setting name": "Nome", + "description": "Descrizione", + "default": "Impostazione predefinita", + "debuggers": "Debugger ({0})", + "debugger name": "Nome", + "debugger type": "Tipo", + "views": "Visualizzazioni ({0})", + "view id": "ID", + "view name": "Nome", + "view location": "Dove", + "themes": "Temi ({0})", + "JSON Validation": "Convalida JSON ({0})", + "commands": "Comandi ({0})", + "command name": "Nome", + "keyboard shortcuts": "Tasti di scelta rapida", + "menuContexts": "Contesti menu", + "languages": "Linguaggi ({0})", + "language id": "ID", + "language name": "Nome", + "file extensions": "Estensioni di file", + "grammar": "Grammatica", + "snippets": "Frammenti" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..d38133a657 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "Installa", + "installing": "Installazione", + "uninstallAction": "Disinstalla", + "Uninstalling": "Disinstallazione", + "updateAction": "Aggiorna", + "updateTo": "Aggiorna a {0}", + "enableForWorkspaceAction.label": "Abilita (area di lavoro)", + "enableAlwaysAction.label": "Abilita (sempre)", + "disableForWorkspaceAction.label": "Disabilita (area di lavoro)", + "disableAlwaysAction.label": "Disabilita (sempre)", + "ManageExtensionAction.uninstallingTooltip": "Disinstallazione", + "enableForWorkspaceAction": "Area di lavoro", + "enableGloballyAction": "Sempre", + "enableAction": "Abilita", + "disableForWorkspaceAction": "Area di lavoro", + "disableGloballyAction": "Sempre", + "disableAction": "Disabilita", + "checkForUpdates": "Controlla la disponibilità di aggiornamenti", + "enableAutoUpdate": "Abilita l'aggiornamento automatico delle estensioni", + "disableAutoUpdate": "Disabilita l'aggiornamento automatico delle estensioni", + "updateAll": "Aggiorna tutte le estensioni", + "reloadAction": "Ricarica", + "postUpdateTooltip": "Ricaricare per aggiornare", + "postUpdateMessage": "Ricaricare questa finestra per attivare l'estensione aggiornata '{0}'?", + "postEnableTooltip": "Ricaricare per attivare", + "postEnableMessage": "Ricaricare questa finestra per attivare l'estensione '{0}'?", + "postDisableTooltip": "Ricaricare per disattivare", + "postDisableMessage": "Ricaricare questa finestra per disattivare l'estensione '{0}'?", + "postUninstallTooltip": "Ricaricare per disattivare", + "postUninstallMessage": "Ricaricare questa finestra per disattivare l'estensione disinstallata '{0}'?", + "reload": "&&Ricarica finestra", + "toggleExtensionsViewlet": "Mostra estensioni", + "installExtensions": "Installa estensioni", + "showEnabledExtensions": "Mostra estensioni abilitate", + "showInstalledExtensions": "Mostra estensioni installate", + "showDisabledExtensions": "Mostra estensioni disabilitate", + "clearExtensionsInput": "Cancella input estensioni", + "showOutdatedExtensions": "Mostra estensioni obsolete", + "showPopularExtensions": "Mostra estensioni più richieste", + "showRecommendedExtensions": "Mostra estensioni consigliate", + "showWorkspaceRecommendedExtensions": "Mostra estensioni consigliate per l'area di lavoro", + "showRecommendedKeymapExtensions": "Mostra mappature tastiera consigliate", + "showRecommendedKeymapExtensionsShort": "Mappature tastiera", + "showLanguageExtensions": "Mostra estensioni del linguaggio", + "showLanguageExtensionsShort": "Estensioni del linguaggio", + "showAzureExtensions": "Mostra estensioni di Azure", + "showAzureExtensionsShort": "Estensioni di Azure", + "configureWorkspaceRecommendedExtensions": "Configura estensioni consigliate (area di lavoro)", + "ConfigureWorkspaceRecommendations.noWorkspace": "Gli elementi consigliati sono disponibili solo per una cartella dell'area di lavoro.", + "OpenExtensionsFile.failed": "Non è possibile creare il file 'extensions.json' all'interno della cartella '.vscode' ({0}).", + "builtin": "Predefinita", + "disableAll": "Disabilita tutte le estensioni installate", + "disableAllWorkspace": "Disabilita tutte le estensioni installate per questa area di lavoro", + "enableAll": "Abilita tutte le estensioni installate", + "enableAllWorkspace": "Abilita tutte le estensioni installate per questa area di lavoro", + "extensionButtonProminentBackground": "Colore di sfondo delle azioni di estensioni che si distinguono (es. pulsante Installa).", + "extensionButtonProminentForeground": "Colore primo piano di pulsanti per azioni di estensioni che si distinguono (es. pulsante Installa).", + "extensionButtonProminentHoverBackground": "Colore di sfondo al passaggio del mouse dei pulsanti per azioni di estensione che si distinguono (es. pulsante Installa)." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..50a55c7a85 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "Premere INVIO per gestire le estensioni.", + "searchFor": "Premere INVIO per cercare '{0}' nel Marketplace.", + "noExtensionsToInstall": "Digitare un nome di estensione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..cf80872d2b --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "Estensioni", + "app.extensions.json.recommendations": "Elenco delle estensioni consigliate. L'identificatore di un'estensione è sempre '${publisher}.${name}'. Ad esempio: 'vscode.csharp'.", + "app.extension.identifier.errorMessage": "Formato imprevisto '${publisher}.${name}'. Esempio: 'vscode.csharp'." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..e456380286 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "Estensione: {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..9c0ef804cf --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "Per questo tipo di file è consigliabile utilizzare l'estensione '{0}'.", + "showRecommendations": "Mostra gli elementi consigliati", + "neverShowAgain": "Non visualizzare più questo messaggio", + "close": "Chiudi", + "workspaceRecommended": "Per questa area di lavoro sono disponibili estensioni consigliate.", + "ignoreExtensionRecommendations": "Si desidera ignorare tutte le raccomandazioni di estensioni?", + "ignoreAll": "Sì, ignora tutti", + "no": "No", + "cancel": "Annulla" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..ba49096faa --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "Gestisci le estensioni", + "galleryExtensionsCommands": "Installa estensioni della raccolta", + "extension": "Estensione", + "extensions": "Estensioni", + "view": "Visualizza", + "extensionsConfigurationTitle": "Estensioni", + "extensionsAutoUpdate": "Aggiorna automaticamente le estensioni", + "extensionsIgnoreRecommendations": "Ignora le raccomandazioni di estensioni" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..e60b7721bd --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "Apri cartella estensioni", + "installVSIX": "Installa da VSIX...", + "InstallVSIXAction.success": "L'estensione è stata installata. Riavviare per abilitarla.", + "InstallVSIXAction.reloadNow": "Ricarica ora" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..4de7e1ef94 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Disabilitare altre mappature tastiera ({0}) per evitare conflitti tra tasti di scelta rapida?", + "yes": "Sì", + "no": "No", + "betterMergeDisabled": "L'estensione Better Merge (miglior merge) è ora incorporata: l'estensione installata è stata disattivata e può essere disinstallata.", + "uninstall": "Disinstalla", + "later": "In seguito" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..fff1e4814d --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "Marketplace", + "installedExtensions": "Installate", + "searchInstalledExtensions": "Installate", + "recommendedExtensions": "Consigliate", + "searchExtensions": "Cerca le estensioni nel Marketplace", + "sort by installs": "Ordina per: conteggio installazioni", + "sort by rating": "Ordina per: classificazione", + "sort by name": "Ordina per: Nome", + "suggestProxyError": "Marketplace ha restituito 'ECONNREFUSED'. Controllare l'impostazione 'http.proxy'.", + "extensions": "Estensioni", + "outdatedExtensions": "{0} estensioni obsolete" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..b895e32b7e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Estensioni", + "no extensions found": "Non sono state trovate estensioni.", + "suggestProxyError": "Marketplace ha restituito 'ECONNREFUSED'. Controllare l'impostazione 'http.proxy'." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json new file mode 100644 index 0000000000..968a4bd7fc --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Disabilitare altre mappature tastiera per evitare conflitti tra tasti di scelta rapida?", + "yes": "Sì", + "no": "No" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..3223dc53f2 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "Se si abilita '{0}', verranno abilitate anche le relative dipendenze. Continuare?", + "enable": "Sì", + "doNotEnable": "No", + "disableDependeciesConfirmation": "Disabilitare solo '{0}' o anche le relative dipendenze?", + "disableOnly": "Solo", + "disableAll": "Tutto", + "cancel": "Annulla", + "singleDependentError": "Non è possibile disabilitare l'estensione '{0}'. L'estensione '{1}' dipende da tale estensione.", + "twoDependentsError": "Non è possibile disabilitare l'estensione '{0}'. Le estensioni '{1}' e '{2}' dipendono da tale estensione.", + "multipleDependentsError": "Non è possibile disabilitare l'estensione '{0}'. Alcune estensioni, tra cui '{1}' e '{2}' dipendono da tale estensione.", + "installConfirmation": "Installare l'estensione '{0}'?", + "install": "Installa" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/ita/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..a87fbc34d6 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "Invia commenti e suggerimenti tramite Twitter", + "label.sendASmile": "Invia commenti e suggerimenti tramite Twitter.", + "patchedVersion1": "L'installazione è danneggiata.", + "patchedVersion2": "Specificare questo fattore se si invia una segnalazione di bug.", + "sentiment": "Grado di soddisfazione dell'esperienza", + "smileCaption": "Felice", + "frownCaption": "Triste", + "other ways to contact us": "Altri modi per contattare Microsoft", + "submit a bug": "Segnala un bug", + "request a missing feature": "Richiedi una funzionalità mancante", + "tell us why?": "Motivo", + "commentsHeader": "Commenti", + "tweet": "Invia un tweet", + "character left": "carattere rimasto", + "characters left": "caratteri rimasti", + "feedbackSending": "Invio", + "feedbackSent": "Grazie", + "feedbackSendingError": "Riprovare" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..d1bb157703 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "Visualizzatore file binari" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..c4d72f9945 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "Editor file di testo", + "createFile": "Crea file", + "fileEditorWithInputAriaLabel": "{0}. Editor file di testo.", + "fileEditorAriaLabel": "Editor file di testo." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..d554e122f8 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "Cartelle" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..b52b6daa85 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "File", + "revealInSideBar": "Visualizza nella barra laterale", + "acceptLocalChanges": "Utilizzare le modifiche e sovrascrivere il contenuto del disco", + "revertLocalChanges": "Annullare le modifiche e tornare al contenuto sul disco" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..7642056fc4 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "Riprova", + "rename": "Rinomina", + "newFile": "Nuovo file", + "newFolder": "Nuova cartella", + "openFolderFirst": "Aprire prima di tutto una cartella per creare file o cartelle al suo interno.", + "newUntitledFile": "Nuovo file senza nome", + "createNewFile": "Nuovo file", + "createNewFolder": "Nuova cartella", + "deleteButtonLabelRecycleBin": "&&Sposta nel Cestino", + "deleteButtonLabelTrash": "&&Sposta nel cestino", + "deleteButtonLabel": "&&Elimina", + "dirtyMessageFolderOneDelete": "Si sta per eliminare una cartella con modifiche non salvate in un file. Continuare?", + "dirtyMessageFolderDelete": "Si sta per eliminare una cartella con modifiche non salvate in {0} file. Continuare?", + "dirtyMessageFileDelete": "Si sta per eliminare un file con modifiche non salvate. Continuare?", + "dirtyWarning": "Le modifiche apportate andranno perse se non vengono salvate.", + "confirmMoveTrashMessageFolder": "Eliminare '{0}' e il relativo contenuto?", + "confirmMoveTrashMessageFile": "Eliminare '{0}'?", + "undoBin": "È possibile ripristinare dal Cestino.", + "undoTrash": "È possibile ripristinare dal cestino.", + "confirmDeleteMessageFolder": "Eliminare definitivamente '{0}' e il relativo contenuto?", + "confirmDeleteMessageFile": "Eliminare definitivamente '{0}'?", + "irreversible": "Questa azione è irreversibile.", + "permDelete": "Elimina definitivamente", + "delete": "Elimina", + "importFiles": "Importa file", + "confirmOverwrite": "Nella cartella di destinazione esiste già un file o una cartella con lo stesso nome. Sovrascrivere?", + "replaceButtonLabel": "&&Sostituisci", + "copyFile": "Copia", + "pasteFile": "Incolla", + "duplicateFile": "Duplicato", + "openToSide": "Apri lateralmente", + "compareSource": "Seleziona per il confronto", + "globalCompareFile": "Confronta file attivo con...", + "pickHistory": "Selezionare un file aperto in precedenza per il confronto", + "unableToFileToCompare": "Non è possibile confrontare il file selezionato con '{0}'.", + "openFileToCompare": "Aprire prima un file per confrontarlo con un altro file.", + "compareWith": "Confronta '{0}' con '{1}'", + "compareFiles": "Confronta file", + "refresh": "Aggiorna", + "save": "Salva", + "saveAs": "Salva con nome...", + "saveAll": "Salva tutto", + "saveAllInGroup": "Salva tutto nel gruppo", + "saveFiles": "Salva file modificati ma non salvati", + "revert": "Ripristina file", + "focusOpenEditors": "Stato attivo su visualizzazione editor aperti", + "focusFilesExplorer": "Stato attivo su Esplora file", + "showInExplorer": "Visualizza file attivo nella barra laterale", + "openFileToShow": "Aprire prima di tutto un file per visualizzarlo in Esplora risorse", + "collapseExplorerFolders": "Comprimi cartelle in Explorer", + "refreshExplorer": "Aggiorna Explorer", + "openFile": "Apri file...", + "openFileInNewWindow": "Apri file attivo in un'altra finestra", + "openFileToShowInNewWindow": "Aprire prima un file per visualizzarlo in un'altra finestra", + "revealInWindows": "Visualizza in Esplora risorse", + "revealInMac": "Visualizza in Finder", + "openContainer": "Apri cartella superiore", + "revealActiveFileInWindows": "Visualizza file attivo in Esplora risorse", + "revealActiveFileInMac": "Visualizza file attivo in Finder", + "openActiveFileContainer": "Apri cartella che contiene il file attivo", + "copyPath": "Copia percorso", + "copyPathOfActive": "Copia percorso del file attivo", + "emptyFileNameError": "È necessario specificare un nome file o un nome di cartella.", + "fileNameExistsError": "In questo percorso esiste già un file o una cartella **{0}**. Scegliere un nome diverso.", + "invalidFileNameError": "Il nome **{0}** non è valido per un nome file o un nome di cartella. Scegliere un nome diverso.", + "filePathTooLongError": "Con il nome **{0}** il percorso diventa troppo lungo. Scegliere un nome più breve.", + "compareWithSaved": "Confronta file attivo con file salvato", + "modifiedLabel": "{0} (su disco) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..557c1fe951 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "Aprire prima un file per copiarne il percorso", + "openFileToReveal": "Aprire prima un file per visualizzarlo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..eaad384e5f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "Mostra Esplora risorse", + "explore": "Esplora risorse", + "view": "Visualizza", + "textFileEditor": "Editor file di testo", + "binaryFileEditor": "Editor file binari", + "filesConfigurationTitle": "File", + "exclude": "Consente di configurare i criteri di ricerca GLOB per escludere file e cartelle.", + "files.exclude.boolean": "Criterio GLOB da usare per trovare percorsi file. Impostare su True o False per abilitare o disabilitare il criterio.", + "files.exclude.when": "Controllo aggiuntivo sugli elementi di pari livello di un file corrispondente. Usare $(basename) come variabile del nome file corrispondente.", + "associations": "Consente di configurare le associazioni tra file e linguaggi, ad esempio \"*.extension\": \"html\". Queste hanno la precedenza sulle associazioni predefinite dei linguaggi installate.", + "encoding": "Codifica del set di caratteri predefinita da usare durante la lettura e la scrittura di file.", + "autoGuessEncoding": "Quando questa opzione è abilitata, la codifica del set di caratteri viene ipotizzata all'apertura dei file", + "eol": "Il carattere di fine riga predefinito. Utilizzare \\n per LF e \\r\\n per CRLF.", + "trimTrailingWhitespace": "Se è abilitato, taglierà lo spazio vuoto quando si salva un file.", + "insertFinalNewline": "Se è abilitato, inserisce un carattere di nuova riga finale alla fine del file durante il salvataggio.", + "files.autoSave.off": "Un file dirty non viene mai salvato automaticamente.", + "files.autoSave.afterDelay": "Un file dirty viene salvato automaticamente in base al valore configurato di 'files.autoSaveDelay'.", + "files.autoSave.onFocusChange": "Un file dirty viene salvato automaticamente quando l'editor perde lo stato attivo.", + "files.autoSave.onWindowChange": "Un file dirty viene salvato automaticamente quando la finestra perde lo stato attivo.", + "autoSave": "Controlla il salvataggio automatico dei file dirty. Valori accettati: '{0}', '{1}', '{2}' (l'editor perde lo stato attivo), '{3}' (la finestra perde lo stato attivo). Se è impostato su '{4}', è possibile configurare il ritardo in 'files.autoSaveDelay'.", + "autoSaveDelay": "Controlla il ritardo in ms dopo il quale un file dirty viene salvato automaticamente. Si applica solo quando 'files.autoSave' è impostato su '{0}'", + "watcherExclude": "Consente di configurare i criteri GLOB dei percorsi file da escludere dal controllo dei file. I criteri devono corrispondere in percorsi assoluti (per una corretta corrispondenza aggiungere come prefisso ** il percorso completo). Se si modifica questa impostazione, è necessario riavviare. Quando si nota che Code consuma troppo tempo della CPU all'avvio, è possibile escludere le cartelle di grandi dimensioni per ridurre il carico iniziale.", + "hotExit.off": "Disabilita Hot Exit.", + "hotExit.onExit": "La funzionalità Hot Exit verrà attivata alla chiusura dell'applicazione, ovvero quando si chiude l'ultima finestra in Windows/Linux o quando si attiva il comando workbench.action.quit (riquadro comandi, tasto di scelta rapida, menu). Tutte le finestre con backup verranno ripristinate al successivo avvio.", + "hotExit.onExitAndWindowClose": "La funzionalità Hot Exit verrà attivata alla chiusura dell'applicazione, ovvero quando si chiude l'ultima finestra in Windows/Linux o quando si attiva il comando workbench.action.quit (riquadro comandi, tasto di scelta rapida, menu), nonché per qualsiasi finestra con una cartella aperta indipendentemente dal fatto che sia l'ultima. Tutte le finestre senza cartelle aperte verranno ripristinate al successivo avvio. Per riportare le finestre di cartelle allo stato in cui si trovavano prima dell'arresto, impostare \"window.restoreFolders\" su \"all\".", + "hotExit": "Controlla se i file non salvati verranno memorizzati tra una sessione e l'altra, consentendo di ignorare il prompt di salvataggio alla chiusura dell'editor.", + "useExperimentalFileWatcher": "Usa il nuovo watcher di file sperimentale.", + "defaultLanguage": "Modalità linguaggio predefinita assegnata ai nuovi file.", + "editorConfigurationTitle": "Editor", + "formatOnSave": "Formatta un file durante il salvataggio. Deve essere disponibile un formattatore, il file non deve essere salvato automaticamente e l'editor non deve essere in fase di chiusura.", + "explorerConfigurationTitle": "Esplora file", + "openEditorsVisible": "Numero di editor visualizzati nel riquadro degli editor aperti. Impostarlo su 0 per nascondere il riquadro.", + "dynamicHeight": "Controlla se l'altezza della sezione degli editor aperti deve essere adattata o meno dinamicamente al numero di elementi.", + "autoReveal": "Controlla se Esplora risorse deve rivelare automaticamente e selezionare i file durante l'apertura.", + "enableDragAndDrop": "Controlla se Esplora risorse deve consentire lo spostamento di file e cartelle tramite trascinamento della selezione.", + "sortOrder.default": "I file e le cartelle vengono ordinati in ordine alfabetico in base al nome. Le cartelle vengono visualizzate prima dei file.", + "sortOrder.mixed": "I file e le cartelle vengono ordinati ordine alfabetico in base al nome, in un unico elenco ordinato.", + "sortOrder.filesFirst": "I file e le cartelle vengono ordinati in ordine alfabetico in base al nome. I file vengono visualizzati prima delle cartelle.", + "sortOrder.type": "I file e le cartelle vengono ordinati in ordine alfabetico in base all'estensione. Le cartelle vengono visualizzate prima dei file.", + "sortOrder.modified": "I file e le cartelle vengono ordinati in ordine decrescente in base alla data dell'ultima modifica. Le cartelle vengono visualizzate prima dei file.", + "sortOrder": "Controlla l'ordinamento di file e cartelle in Esplora risorse. Oltre all'ordinamento predefinito, è possibile impostare l'ordine su 'mixed' (file e cartelle vengono ordinati insieme), 'type' (in base al tipo di file), 'modified' (in base alla data dell'ultima modifica) o 'filesFirst' (i file vengono ordinati prima delle cartelle)." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..7fbd3c4cd2 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "Rimuovi", + "overwrite": "Sovrascrivi", + "retry": "Riprova", + "readonlySaveError": "Non è stato possibile salvare '{0}': il file è protetto da scrittura. Selezionare 'Sovrascrivi' per rimuovere la protezione.", + "genericSaveError": "Non è stato possibile salvare '{0}': {1}", + "staleSaveError": "Non è stato possibile salvare '{0}': il contenuto sul disco è più recente. Fare clic su **Confronta** per confrontare la versione corrente con quella sul disco.", + "compareChanges": "Confronta", + "saveConflictDiffLabel": "{0} (su disco) ↔ {1} (in {2}) - Risolvere conflitto in fase di salvataggio", + "userGuide": "Usare le azioni della barra degli strumenti dell'editor a destra per **annullare** le modifiche o per **sovrascrivere** il contenuto su disco con le modifiche" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..26f70229b0 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Nessuna cartella aperta", + "explorerSection": "Sezione Esplora file", + "noWorkspaceHelp": "Non ci sono ancora cartelle aperte.", + "openFolder": "Apri cartella" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..67fc137a84 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "Sezione Esplora file", + "treeAriaLabel": "Esplora file" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..5af661cb2a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "Digitare il nome file. Premere INVIO per confermare oppure ESC per annullare.", + "filesExplorerViewerAriaLabel": "{0}, Esplora file", + "dropFolders": "Aggiungere le cartelle all'area di lavoro?", + "dropFolder": "Aggiungere la cartella all'area di lavoro?", + "addFolders": "&& Aggiungi cartelle", + "addFolder": "&&Aggiungi cartella", + "confirmOverwriteMessage": "'{0}' esiste già nella cartella di destinazione. Sostituirlo?", + "irreversible": "Questa azione è irreversibile.", + "replaceButtonLabel": "&&Sostituisci" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..7876612d79 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "Editor aperti", + "openEditosrSection": "Sezione Editor aperti", + "treeAriaLabel": "Editor aperti: elenco di file attivi", + "dirtyCounter": "{0} non salvati" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..061ad73f4a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}, Gruppo di editor", + "openEditorAriaLabel": "{0}, Apri editor", + "saveAll": "Salva tutto", + "closeAllUnmodified": "Chiudi non modificate", + "closeAll": "Chiudi tutto", + "compareWithSaved": "Confronta con file salvato", + "close": "Chiudi", + "closeOthers": "Chiudi altri" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/ita/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..8f3a12fadd --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0} file non salvati" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/ita/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..32c2e3e166 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (deleted from disk)" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json new file mode 100644 index 0000000000..d4a0daa8fd --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "switchToChangesView": "Passa alla visualizzazione modifiche", + "openInEditor": "Passa alla visualizzazione editor", + "workbenchStage": "Prepara", + "workbenchUnstage": "Annulla preparazione", + "stageSelectedLines": "Gestisci temporaneamente le righe selezionate", + "unstageSelectedLines": "Annulla la preparazione delle righe selezionate", + "revertSelectedLines": "Ripristina righe selezionate", + "confirmRevertMessage": "Annullare le modifiche selezionate?", + "irreversible": "Questa azione è irreversibile.", + "revertChangesLabel": "&&Annulla modifiche", + "openChange": "Apri modifica", + "openFile": "Apri file", + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/gitActions.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/gitActions.i18n.json new file mode 100644 index 0000000000..f53ec441d2 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/gitActions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openChange": "Apri modifica", + "openFile": "Apri file", + "init": "Init", + "refresh": "Aggiorna", + "stageChanges": "Prepara", + "stageAllChanges": "Gestisci tutto temporaneamente", + "confirmUndoMessage": "Pulire tutte le modifiche?", + "confirmUndoAllOne": "In {0} file ci sono modifiche non preparate per il commit.\n\nQuesta azione è irreversibile.", + "confirmUndoAllMultiple": "In {0} file ci sono modifiche non preparate per il commit.\n\nQuesta azione è irreversibile.", + "cleanChangesLabel": "&&Pulisci modifiche", + "confirmUndo": "Pulire le modifiche in '{0}'?", + "irreversible": "Questa azione è irreversibile.", + "undoChanges": "Pulisci", + "undoAllChanges": "Pulisci tutto", + "unstage": "Annulla preparazione", + "unstageAllChanges": "Annulla la gestione temporanea di tutto", + "dirtyTreeCheckout": "Non è possibile eseguire l'estrazione. Eseguire prima il commit o l'accantonamento del lavoro.", + "commitStaged": "Esegui commit dei file preparati", + "commitStagedAmend": "Esegui commit dei file preparati (modifica)", + "commitStagedSignedOff": "Esegui commit dei file preparati (approvazione)", + "commit": "Commit", + "commitMessage": "Messaggio di commit", + "commitAll": "Esegui commit di tutto", + "commitAllSignedOff": "Esegui commit di tutto (approvazione)", + "commitAll2": "Esegui commit di tutto", + "commitStaged2": "Esegui commit dei file preparati", + "dirtyTreePull": "Non è possibile eseguire il pull. Eseguire prima il commit o l'accantonamento del lavoro.", + "authFailed": "L'autenticazione non è riuscita su git remote.", + "pushToRemote": "Esegui push in...", + "pushToRemotePickMessage": "Selezionare un repository remoto in cui effettuare il push del ramo '{0}':", + "publish": "Pubblica", + "confirmPublishMessage": "Pubblicare '{0}' in '{1}'?", + "confirmPublishMessageButton": "&&Pubblica", + "publishPickMessage": "Selezionare un repository remoto in cui pubblicare il ramo '{0}':", + "sync is unpredictable": "Questa azione consentirà di effettuare il push e il pull di commit da e verso '{0}'.", + "ok": "OK", + "cancel": "Annulla", + "never again": "OK, non visualizzare più", + "undoLastCommit": "Annulla ultimo commit" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json new file mode 100644 index 0000000000..85dbd1f8c7 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refAriaLabel": "{0}, GIT", + "checkoutBranch": "Ramo in {0}", + "checkoutRemoteBranch": "Ramo remoto in {0}", + "checkoutTag": "Tag in {0}", + "alreadyCheckedOut": "Il ramo {0} è già il ramo corrente", + "branchAriaLabel": "{0}, ramo GIT", + "createBranch": "Crea ramo {0}", + "noBranches": "Non ci sono altri rami", + "notValidBranchName": "Specificare un nome di ramo valido" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/gitServices.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/gitServices.i18n.json new file mode 100644 index 0000000000..b373f94764 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/gitServices.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cantOpen": "Non è possibile aprire questa risorsa GIT.", + "gitIndexChanges": "{0} (indice) ↔ {1}", + "gitIndexChangesDesc": "{0} - Modifiche nell'indice", + "gitIndexChangesRenamed": "{0} ← {1}", + "gitIndexChangesRenamedDesc": "{0} - Rinominato - Modifiche nell'indice", + "workingTreeChanges": "{0} (HEAD) ↔ {1}", + "workingTreeChangesDesc": "{0} - Modifiche nell'albero di lavoro", + "gitMergeChanges": "{0} (merge) ↔ {1}", + "gitMergeChangesDesc": "{0} - Esegui merge delle modifiche", + "updateGit": "La versione installata di GIT è la {0}. Per il corretto funzionamento di Code è consigliabile usare una versione di GIT non inferiore alla 2.0.0.", + "download": "Scarica", + "neverShowAgain": "Non visualizzare più questo messaggio", + "configureUsernameEmail": "Configurare il nome utente e l'indirizzo di posta elettronica di GIT.", + "badConfigFile": "GIT {0}", + "unmergedChanges": "Prima di eseguire il commit delle modifiche, è necessario risolvere le modifiche non sottoposte a merge.", + "showOutput": "Mostra output", + "cancel": "Annulla", + "checkNativeConsole": "Si è verificato un errore durante l'esecuzione di un'operazione git. Esaminare l'output oppure usare la console per controllare lo stato del repository.", + "changesFromIndex": "{0} (index)", + "changesFromIndexDesc": "{0} - Modifiche nell'indice", + "changesFromTree": "{0} ({1})", + "changesFromTreeDesc": "{0} - Modifiche in {1}", + "cantOpenResource": "Non è possibile aprire questa risorsa GIT." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json new file mode 100644 index 0000000000..b200319c18 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "publishBranch": "Pubblica ramo", + "syncBranch": "Sincronizza modifiche", + "gitNotEnabled": "GIT non è abilitato in quest'area di lavoro." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json new file mode 100644 index 0000000000..fad7c5d445 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gitProgressBadge": "Stato di esecuzione GIT", + "gitPendingChangesBadge": "{0} modifiche in sospeso", + "toggleGitViewlet": "Mostra GIT", + "git": "GIT", + "view": "Visualizza", + "gitCommands": "Comandi GIT", + "gitConfigurationTitle": "GIT", + "gitEnabled": "GIT abilitato", + "gitPath": "Percorso dell'eseguibile di GIT", + "gitAutoRefresh": "Indica se l'aggiornamento automatico è abilitato", + "gitAutoFetch": "Indica se il recupero automatico è abilitato.", + "gitLongCommit": "Indica se visualizzare un avviso in caso di messaggi di commit lunghi.", + "gitLargeRepos": "Consente a Code di gestire sempre i repository di grandi dimensioni.", + "confirmSync": "Confermare prima di sincronizzare i repository GIT.", + "countBadge": "Controlla il contatore dei log GIT.", + "checkoutType": "Controlla il tipo di rami elencati." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json new file mode 100644 index 0000000000..4273ba3460 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "needMessage": "Fornire un messaggio di commit. È sempre possibile premere **{0}** per eseguire il commit delle modifiche. Se sono presenti modifiche preparate per il commit, verrà eseguito il commit solo di tali modifiche. In caso contrario, verrà eseguito il commit di tutte le modifiche.", + "nothingToCommit": "Non appena sono presenti modifiche di cui eseguire il commit, immettere il messaggio di commit e premere **{0}** per eseguire il commit. Se sono presenti modifiche preparate per il commit, verrà eseguito il commit solo di tali modifiche. In caso contrario, verrà eseguito il commit di tutte le modifiche.", + "longCommit": "È consigliabile limitare la prima riga del commit a 50 caratteri. Per aggiungere informazioni, è possibile usare altre righe.", + "commitMessage": "Message (press {0} to commit)", + "commitMessageAriaLabel": "GIT: digitare il messaggio e premere {0} per eseguire il commit", + "treeAriaLabel": "Visualizzazione modifiche GIT", + "showOutput": "Mostra output GIT" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json new file mode 100644 index 0000000000..22c9953950 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stagedChanges": "Modifiche preparate per il commit", + "allChanges": "Modifiche", + "mergeChanges": "Esegui merge delle modifiche", + "outsideOfWorkspace": "Questo file non è presente nell'area di lavoro corrente.", + "modified-char": "M", + "added-char": "A", + "deleted-char": "D", + "renamed-char": "R", + "copied-char": "C", + "untracked-char": "U", + "ignored-char": "!", + "title-index-modified": "Modificato nell'indice", + "title-modified": "Modificato", + "title-index-added": "Aggiunto all'indice", + "title-index-deleted": "Eliminato nell'indice", + "title-deleted": "Eliminato", + "title-index-renamed": "Rinominato nell'indice", + "title-index-copied": "Copiato nell'indice", + "title-untracked": "Non registrato", + "title-ignored": "Ignorato", + "title-conflict-both-deleted": "Conflitto: eliminato dall'utente e da Microsoft", + "title-conflict-added-by-us": "Conflitto: aggiunto da Microsoft", + "title-conflict-deleted-by-them": "Conflitto: eliminato dall'utente", + "title-conflict-added-by-them": "Conflitto: aggiunto dall'utente", + "title-conflict-deleted-by-us": "Conflitto: eliminato da Microsoft", + "title-conflict-both-added": "Conflitto: aggiunto dall'utente e da Microsoft", + "title-conflict-both-modified": "Conflitto: modificato dall'utente e da Microsoft", + "fileStatusAriaLabel": "Lo stato del file {0} nella cartella {1} è: {2}, GIT", + "ariaLabelStagedChanges": "Modifiche preparate per il commit, GIT", + "ariaLabelChanges": "Modifiche, GIT", + "ariaLabelMerge": "Merge, GIT" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json new file mode 100644 index 0000000000..1d98804731 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disabled": "GIT è disabilitata nelle impostazioni." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json new file mode 100644 index 0000000000..d14b79a75c --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noGit": "Quest'area di lavoro non è ancora sotto il controllo del codice sorgente di GIT.", + "gitinit": "Inizializza repository GIT" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json new file mode 100644 index 0000000000..08e0646010 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "macInstallWith": "È possibile installarlo con {0}, scaricarlo da {1} oppure installare gli strumenti di sviluppo da riga di comando di {2} digitando {3} a un prompt del terminale.", + "winInstallWith": "È possibile installarlo con {0} o scaricarlo da {1}.", + "linuxDownloadFrom": "È possibile scaricarlo da {0}.", + "downloadFrom": "È possibile scaricarlo da {0}.", + "looksLike": "Sembra che GIT non sia installato nel sistema.", + "pleaseRestart": "Dopo aver installato GIT, riavviare VSCode." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json new file mode 100644 index 0000000000..998540fa08 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "huge": "Sembra che nel repository siano presenti molte modifiche attive.\nQuesto può causare un notevole rallentamento di Code.", + "setting": "Per disabilitare definitivamente questo avviso, usare l'impostazione seguente:", + "allo": "Consenti repository di grandi dimensioni" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json new file mode 100644 index 0000000000..e05150f550 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrongRoot": "Questa directory sembra essere inclusa in un repository GIT.", + "pleaseRestart": "Aprire la directory radice del repository per accedere alle funzionalità di GIT." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json b/i18n/ita/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json new file mode 100644 index 0000000000..6f284f7c17 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspaceHelp": "Non ci sono ancora cartelle aperte.", + "pleaseRestart": "Aprire una cartella con un repository GIT per accedere alle funzionalità di GIT.", + "openFolder": "Apri cartella" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json new file mode 100644 index 0000000000..2c0c983c92 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSCMViewlet": "Mostra Gestione controllo servizi", + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json b/i18n/ita/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json new file mode 100644 index 0000000000..3ccd4c30b4 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "valid": "Specificare un URL valido per il repository GIT", + "url": "URL del repository", + "directory": "Directory di clonazione di destinazione", + "cloning": "Clonazione del repository '{0}'...", + "already exists": "Il repository di destinazione esiste già. Selezionare un'altra directory in cui eseguire la clonazione." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json b/i18n/ita/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json new file mode 100644 index 0000000000..6f2dc5416f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/git/node/git.lib.i18n.json b/i18n/ita/src/vs/workbench/parts/git/node/git.lib.i18n.json new file mode 100644 index 0000000000..b5d023188d --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/git/node/git.lib.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorBuffer": "Non è possibile aprire il file da GIT", + "fileBinaryError": "Il file sembra essere binario e non può essere aperto come file di testo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..e3fede4992 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "Anteprima HTML", + "devtools.webview": "Sviluppatore: Strumenti Webview" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/ita/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..420909b84b --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "L'input dell'editor non è valido." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/ita/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..352110baf3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "Sviluppatore: Strumenti Webview" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/ita/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..a1167cd829 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "Visualizza", + "problems.view.show.label": "Mostra problemi", + "problems.panel.configuration.title": "Visualizzazione Problemi", + "problems.panel.configuration.autoreveal": "Controlla se la visualizzazione Problemi deve visualizzare automaticamente i file durante l'apertura", + "markers.panel.title.problems": "Problemi", + "markers.panel.aria.label.problems.tree": "Problemi raggruppati per file", + "markers.panel.no.problems.build": "Finora non sono stati rilevati problemi nell'area di lavoro.", + "markers.panel.no.problems.filters": "Non sono stati trovati risultati corrispondenti ai criteri di filtro specificati", + "markers.panel.action.filter": "Filtra problemi", + "markers.panel.filter.placeholder": "Filtra per tipo o testo", + "markers.panel.filter.errors": "errori", + "markers.panel.filter.warnings": "avvisi", + "markers.panel.filter.infos": "messaggi informativi", + "markers.panel.single.error.label": "1 errore", + "markers.panel.multiple.errors.label": "{0} errori", + "markers.panel.single.warning.label": "1 avviso", + "markers.panel.multiple.warnings.label": "{0} avvisi", + "markers.panel.single.info.label": "1 messaggio informativo", + "markers.panel.multiple.infos.label": "{0} messaggi informativi", + "markers.panel.single.unknown.label": "1 sconosciuto", + "markers.panel.multiple.unknowns.label": "{0} sconosciuti", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "{0} con {1} problemi", + "problems.tree.aria.label.error.marker": "Errore generato da {0}: {1} a riga {2} e carattere {3}", + "problems.tree.aria.label.error.marker.nosource": "Errore: {0} a riga {1} e carattere {2}", + "problems.tree.aria.label.warning.marker": "Avviso generato da {0}: {1} a riga {2} e carattere {3}", + "problems.tree.aria.label.warning.marker.nosource": "Avviso: {0} a riga {1} e carattere {2}", + "problems.tree.aria.label.info.marker": "Messaggio informativo generato da {0}: {1} a riga {2} e carattere {3}", + "problems.tree.aria.label.info.marker.nosource": "Messaggio informativo: {0} a riga {1} e carattere {2}", + "problems.tree.aria.label.marker": "Problema generato da {0}: {1} a riga {2} e carattere {3}", + "problems.tree.aria.label.marker.nosource": "Problema: {0} a riga {1} e carattere {2}", + "errors.warnings.show.label": "Mostra errori e avvisi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/ita/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..0e7208a9c5 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "Copia" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..835553b475 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Partecipare a un breve sondaggio?", + "takeSurvey": "Partecipa a sondaggio", + "remindLater": "Visualizza più tardi", + "neverAgain": "Non visualizzare più questo messaggio" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..5c316ab20c --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Output", + "viewCategory": "Visualizza", + "clearOutput.label": "Cancella output" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/ita/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..756611d0f2 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "Attiva/Disattiva output", + "clearOutput": "Cancella output", + "toggleOutputScrollLock": "Attiva/Disattiva blocco scorrimento per output", + "switchToOutput.label": "Passa all'output" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/ita/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..cbe582adc2 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0}, Pannello di output", + "outputPanelAriaLabel": "Pannello di output" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/ita/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..a00726e136 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Output", + "channel": "per '{0}'" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..49ed1f3e2a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "È stato rilevato un rallentamento all'avvio", + "slow.detail": "È stato appena rilevato un rallentamento all'avvio. Per consentire a Microsoft di analizzare e risolvere il problema, riavviare '{0}' con la profilatura abilitata e condividere i profili.", + "prof.message": "I profili sono stati creati.", + "prof.detail": "Creare un problema e allegare manualmente i file seguenti:\n{0}", + "prof.restartAndFileIssue": "Crea problema e riavvia", + "prof.restart": "Riavvia", + "prof.thanks": "Grazie per l'aiuto.", + "prof.detail.restart": "È necessario un riavvio alla fine per continuare a utilizzare '{0}'. Ancora una volta, grazie per il vostro contributo." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..450d876686 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "Premere la combinazione di tasti desiderata e INVIO. Premere ESC per annullare.", + "defineKeybinding.chordsTo": "premi contemporaneamente per" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..b97c572f4e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "Scelte rapide da tastiera", + "SearchKeybindings.AriaLabel": "Cerca tasti di scelta rapida", + "SearchKeybindings.Placeholder": "Cerca tasti di scelta rapida", + "sortByPrecedene": "Ordina per Precedenza", + "header-message": "Per personalizzazioni avanzate, aprire e modificare", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "Tasti di scelta rapida", + "changeLabel": "Cambia tasto di scelta rapida", + "addLabel": "Aggiungi tasto di scelta rapida", + "removeLabel": "Rimuovi tasto di scelta rapida", + "resetLabel": "Reimposta tasto di scelta rapida", + "showConflictsLabel": "Mostra conflitti", + "copyLabel": "Copia", + "error": "Errore '{0}' durante la modifica del tasto di scelta rapida. Si prega di aprire il file 'keybindings.json' e verificare.", + "command": "Comando", + "keybinding": "Tasto di scelta rapida", + "source": "Origine", + "when": "Quando", + "editKeybindingLabelWithKey": "Cambia tasto di scelta rapida {0}", + "editKeybindingLabel": "Cambia tasto di scelta rapida", + "addKeybindingLabelWithKey": "Aggiungi tasto di scelta rapida {0}", + "addKeybindingLabel": "Aggiungi tasto di scelta rapida", + "commandAriaLabel": "Il comando è {0}.", + "keybindingAriaLabel": "Il tasto di scelta rapida è {0}.", + "noKeybinding": "Non è stato assegnato alcun tasto di scelta rapida.", + "sourceAriaLabel": "L'origine è {0}.", + "whenAriaLabel": "Il valore di Quando è {0}.", + "noWhen": "Non esiste alcun contesto per Quando." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..a27d5f5b4e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "Definisci tasto di scelta rapida", + "defineKeybinding.kbLayoutErrorMessage": "Non sarà possibile produrre questa combinazione di tasti con il layout di tastiera corrente.", + "defineKeybinding.kbLayoutLocalAndUSMessage": "**{0}** per il layout di tastiera corrente (**{1}** per quello standard US).", + "defineKeybinding.kbLayoutLocalMessage": "**{0}** per il layout di tastiera corrente." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..d7e534f403 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "Editor preferenze predefinite", + "keybindingsEditor": "Editor tasti di scelta rapida", + "preferences": "Preferenze" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..c2c8fbddf5 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "Apri impostazioni utente", + "openGlobalKeybindings": "Apri tasti di scelta rapida", + "openGlobalKeybindingsFile": "Apri file dei tasti di scelta rapida", + "openWorkspaceSettings": "Apri impostazioni area di lavoro", + "openFolderSettings": "Apri impostazioni cartella", + "pickFolder": "Seleziona cartella", + "configureLanguageBasedSettings": "Configura impostazioni specifiche del linguaggio...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "Seleziona linguaggio" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..6840d24797 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "Impostazioni predefinite", + "SearchSettingsWidget.AriaLabel": "Cerca impostazioni", + "SearchSettingsWidget.Placeholder": "Cerca impostazioni", + "totalSettingsMessage": "{0} impostazioni in totale", + "noSettingsFound": "Nessuna impostazione corrispondente", + "oneSettingFound": "1 impostazione corrispondente", + "settingsFound": "{0} impostazioni corrispondenti", + "fileEditorWithInputAriaLabel": "{0}. Editor file di testo.", + "fileEditorAriaLabel": "Editor file di testo.", + "preferencesAriaLabel": "Preferenze predefinite. Editor di testo di sola lettura." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..070aa10c5f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "Inserire le impostazioni qui per sovrascrivere quelle predefinite.", + "errorInvalidConfiguration": "Impossibile scrivere nelle impostazioni. Correggere eventuali errori o avvisi nel file e riprovare.", + "emptyWorkspaceSettingsHeader": "Inserire le impostazioni qui per sovrascrivere le impostazioni utente.", + "emptyFolderSettingsHeader": "Inserire le impostazioni cartella qui per sovrascrivere quelle dell'area di lavoro.", + "defaultFolderSettingsTitle": "Impostazioni cartella predefinite", + "defaultSettingsTitle": "Impostazioni predefinite", + "noSettingsFound": "Non sono state trovate impostazioni.", + "editTtile": "Modifica", + "replaceDefaultValue": "Sostituisci nelle impostazioni", + "copyDefaultValue": "Copia nelle impostazioni", + "unsupportedPHPExecutablePathSetting": "Questa deve essere un'impostazione utente. Per configurare PHP per l'area di lavoro, aprire un file PHP e fare clic su 'Percorso PHP' nella barra di stato.", + "unsupportedWorkspaceSetting": "Questa deve essere un'impostazione utente.", + "unsupportedWorkbenchSetting": "Non è possibile applicare ora questa impostazione. Verrà applicata direttamente all'apertura della cartella." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..15703826eb --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "Aprire prima una cartella per creare le impostazioni dell'area di lavoro", + "emptyKeybindingsHeader": "Inserire i tasti di scelta rapida in questo file per sovrascrivere i valori predefiniti", + "defaultKeybindings": "Tasti di scelta rapida predefiniti", + "folderSettingsName": "{0} (Impostazioni cartella)", + "fail.createSettings": "Non è possibile creare '{0}' ({1})." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..11bbf03053 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "Impostazioni cartella" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..d944fa1f76 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "Impostazione predefinita", + "user": "Utente", + "meta": "meta", + "option": "opzione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..bfb9f35a1a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "Impostazioni utente", + "workspaceSettingsTarget": "Impostazioni area di lavoro" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..ffd2b0c6e8 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "Più usate", + "noSettings": "Nessuna impostazione", + "defaultKeybindingsHeader": "Per sovrascrivere i tasti di scelta rapida, inserirli nel file dei tasti di scelta rapida." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..eb10c39db4 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Mostra tutti i comandi", + "clearCommandHistory": "Cancella cronologia dei comandi", + "showCommands.label": "Riquadro comandi...", + "entryAriaLabelWithKey": "{0}, {1}, comandi", + "entryAriaLabel": "{0}, comandi", + "canNotRun": "Non è possibile eseguire il comando '{0}' da questa posizione.", + "actionNotEnabled": "Il comando '{0}' non è abilitato nel contesto corrente.", + "recentlyUsed": "usate di recente", + "morecCommands": "altri comandi", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "Non ci sono comandi corrispondenti" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..dda6568c8b --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "Vai alla riga...", + "gotoLineLabelEmptyWithLimit": "Digitare un numero di riga a cui passare compreso tra 1 e {0}", + "gotoLineLabelEmpty": "Digitare un numero di riga a cui passare", + "gotoLineColumnLabel": "Vai a riga {0} e carattere {1}", + "gotoLineLabel": "Vai a riga {0}", + "gotoLineHandlerAriaLabel": "Digitare un numero di riga a cui passare.", + "cannotRunGotoLine": "Aprire prima un file di testo per passare a una riga" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..0d09156cfa --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "Vai al simbolo nel file...", + "symbols": "simboli ({0})", + "method": "metodi ({0})", + "function": "funzioni ({0})", + "_constructor": "costruttori ({0})", + "variable": "variabili ({0})", + "class": "classi ({0})", + "interface": "interfacce ({0})", + "namespace": "spazi dei nomi ({0})", + "package": "pacchetti ({0})", + "modules": "moduli ({0})", + "property": "proprietà ({0})", + "enum": "enumerazioni ({0})", + "string": "stringhe ({0})", + "rule": "regole ({0})", + "file": "file ({0})", + "array": "matrici ({0})", + "number": "numeri ({0})", + "boolean": "valori booleani ({0})", + "object": "oggetti ({0})", + "key": "chiavi ({0})", + "entryAriaLabel": "{0}, simboli", + "noSymbolsMatching": "Non ci sono simboli corrispondenti", + "noSymbolsFound": "Non sono stati trovati simboli", + "gotoSymbolHandlerAriaLabel": "Digitare per ridurre il numero di simboli dell'editor attualmente attivo.", + "cannotRunGotoSymbolInFile": "Non sono disponibili informazioni relative ai simboli per il file", + "cannotRunGotoSymbol": "Aprire prima un file di testo per passare a un simbolo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..fee14a897e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, Guida per la selezione", + "globalCommands": "comandi globali", + "editorCommands": "comandi dell'editor" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..a9dc9834f3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "Mostra ed esegui comandi", + "gotoLineDescriptionMac": "Vai alla riga", + "gotoLineDescriptionWin": "Vai alla riga", + "gotoSymbolDescription": "Vai al simbolo nel file", + "gotoSymbolDescriptionScoped": "Vai al simbolo nel file per categoria", + "helpDescription": "Visualizza la Guida", + "viewPickerDescription": "Apri visualizzazione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..8427fb741c --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, selezione visualizzazione", + "views": "Visualizzazioni", + "panels": "Pannelli", + "terminals": "Terminale", + "terminalTitle": "{0}: {1}", + "channels": "Output", + "openView": "Apri visualizzazione", + "quickOpenView": "Visualizzazione Quick Open" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..24f0b3552e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "È necessario riavviare per rendere effettiva un'impostazione modificata.", + "relaunchSettingDetail": "Fare clic sul pulsante di riavvio per riavviare {0} e abilitare l'impostazione.", + "restart": "Riavvia", + "relaunchWorkspaceMessage": "Questo cambiamento di area di lavoro richiede un ricaricamento del nostro sistema di estensione.", + "reload": "Ricarica" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/ita/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..903d51b7df --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "Colore di sfondo della barra di navigazione dell'editor per le righe che sono state modificate.", + "editorGutterAddedBackground": "Colore di sfondo della barra di navigazione dell'editor per le righe che sono state aggiunte.", + "editorGutterDeletedBackground": "Colore di sfondo della barra di navigazione dell'editor per le righe che sono state cancellate.", + "overviewRulerModifiedForeground": "Colore del marcatore del righello delle annotazioni per il contenuto modificato.", + "overviewRulerAddedForeground": "Colore del marcatore del righello delle annotazioni per il contenuto aggiunto.", + "overviewRulerDeletedForeground": "Colore del marcatore del righello delle annotazioni per il contenuto eliminato." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..c0f95d8c69 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "Mostra GIT", + "source control": "Controllo del codice sorgente", + "toggleSCMViewlet": "Mostra Gestione controllo servizi", + "view": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..31a3d003c8 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0} modifiche in sospeso" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..bac80bba8b --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "Installa ulteriori provider SCM ...", + "switch provider": "Cambia provider SCM" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..c5c4cab503 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Message (press {0} to commit)", + "installAdditionalSCMProviders": "Installa ulteriori provider SCM ...", + "no open repo": "Non ci sono controlli del codice sorgente attivi.", + "source control": "Controllo del codice sorgente", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..cb5d60781d --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "risultati per file e simboli", + "fileResults": "risultati dei file" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..2dacd8712f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, selezione file", + "searchResults": "risultati ricerca" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..0fe8ef5a73 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, selezione simboli", + "symbols": "risultati per simboli", + "noSymbolsMatching": "Non ci sono simboli corrispondenti", + "noSymbolsWithoutInput": "Digitare per cercare i simboli" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..6127a1fa0f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "input", + "useIgnoreFilesDescription": "Usa file ignorati", + "useExcludeSettingsDescription": "Utilizza impostazioni di esclusione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..77e8f9e122 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (Anteprima sostituzione)" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..7d292a6500 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Vai al simbolo nell'area di lavoro...", + "name": "Cerca", + "showSearchViewlet": "Mostra Cerca", + "view": "Visualizza", + "findInFiles": "Cerca nei file", + "openAnythingHandlerDescription": "Vai al file", + "openSymbolDescriptionNormal": "Vai al simbolo nell'area di lavoro", + "searchOutputChannelTitle": "Cerca", + "searchConfigurationTitle": "Cerca", + "exclude": "Consente di configurare i criteri GLOB per escludere file e cartelle nelle ricerche. Eredita tutti i criteri GLOB dall'impostazione files.exclude.", + "exclude.boolean": "Criterio GLOB da usare per trovare percorsi file. Impostare su True o False per abilitare o disabilitare il criterio.", + "exclude.when": "Controllo aggiuntivo sugli elementi di pari livello di un file corrispondente. Usare $(basename) come variabile del nome file corrispondente.", + "useRipgrep": "Controlla se usare ripgrep durante la ricerca di testo", + "useIgnoreFilesByDefault": "Controlla se utilizzare i file .gitignore e .ignore come impostazione predefinita durante la ricerca in una nuova area di lavoro", + "search.quickOpen.includeSymbols": "Configurare questa opzione per includere i risultati di una ricerca di simboli globale nei risultati dei file per Quick Open." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..1ba2fc694d --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "Mostra i criteri di inclusione per la ricerca successivi", + "previousSearchIncludePattern": "Mostra i criteri di inclusione per la ricerca precedenti", + "nextSearchExcludePattern": "Mostra i criteri di esclusione per la ricerca successivi", + "previousSearchExcludePattern": "Mostra i criteri di esclusione per la ricerca precedenti", + "nextSearchTerm": "Mostra il termine di ricerca successivo", + "previousSearchTerm": "Mostra il termine di ricerca precedente", + "focusNextInputBox": "Sposta lo stato attivo sulla casella di input successiva", + "focusPreviousInputBox": "Sposta lo stato attivo sulla casella di input precedente", + "replaceInFiles": "Sostituisci nei file", + "findInWorkspace": "Trova nell'area di lavoro...", + "findInFolder": "Trova nella cartella...", + "RefreshAction.label": "Aggiorna", + "ClearSearchResultsAction.label": "Cancella risultati della ricerca", + "FocusNextSearchResult.label": "Sposta lo stato attivo sul risultato della ricerca successivo", + "FocusPreviousSearchResult.label": "Sposta lo stato attivo sul risultato della ricerca precedente", + "RemoveAction.label": "Rimuovi", + "file.replaceAll.label": "Sostituisci tutto", + "match.replace.label": "Sostituisci" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..f7653d7786 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "Altri file", + "searchFileMatches": "{0} file trovati", + "searchFileMatch": "{0} file trovato", + "searchMatches": "{0} corrispondenze trovate", + "searchMatch": "{0} corrispondenza trovata", + "folderMatchAriaLabel": "{0} corrispondenze nella cartella radice {1}, risultato della ricerca", + "fileMatchAriaLabel": "{0} corrispondenze nel file {1} della cartella {2}, risultato della ricerca", + "replacePreviewResultAria": "Sostituisce il termine {0} con {1} alla colonna {2} in linea con il testo {3}", + "searchResultAria": "Trovato termine {0} alla colonna {1} in linea con il testo {2}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..03cd4f4980 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "Attiva/Disattiva dettagli ricerca", + "searchScope.includes": "file da includere", + "label.includes": "Criteri di inclusione per la ricerca", + "searchScope.excludes": "file da escludere", + "label.excludes": "Criteri di esclusione per la ricerca", + "replaceAll.confirmation.title": "Sostituisci tutto", + "replaceAll.confirm.button": "Sostituisci", + "replaceAll.occurrence.file.message": "{0} occorrenza in {1} file è stata sostituita con '{2}'.", + "removeAll.occurrence.file.message": "È stata sostituita {0} occorrenza in {1} file.", + "replaceAll.occurrence.files.message": "{0} occorrenza in {1} file è stata sostituita con '{2}'.", + "removeAll.occurrence.files.message": "È stata sostituita {0} occorrenze in {1} file.", + "replaceAll.occurrences.file.message": "{0} occorrenze in {1} file sono state sostituite con '{2}'.", + "removeAll.occurrences.file.message": "Sono state sostituite {0} occorrenze in {1} file.", + "replaceAll.occurrences.files.message": "{0} occorrenze in {1} file sono state sostituite con '{2}'.", + "removeAll.occurrences.files.message": "Sono state sostituite {0} occorrenze in {1} file.", + "removeAll.occurrence.file.confirmation.message": "Sostituire {0} occorrenza in {1} file con '{2}'?", + "replaceAll.occurrence.file.confirmation.message": "Sostituire {0} occorrenza in {1} file?", + "removeAll.occurrence.files.confirmation.message": "Sostituire {0} occorrenza in {1} file con '{2}'?", + "replaceAll.occurrence.files.confirmation.message": "Sostituire {0} occorrenza in {1} file?", + "removeAll.occurrences.file.confirmation.message": "Sostituire {0} occorrenze in {1} file con '{2}'?", + "replaceAll.occurrences.file.confirmation.message": "Sostituire {0} occorrenze in {1} file?", + "removeAll.occurrences.files.confirmation.message": "Sostituire {0} occorrenze in {1} file con '{2}'?", + "replaceAll.occurrences.files.confirmation.message": "Sostituire {0} occorrenze in {1} file?", + "treeAriaLabel": "Risultati ricerca", + "searchPathNotFoundError": "Percorso di ricerca non trovato: {0}", + "searchMaxResultsWarning": "Il set di risultati contiene solo un subset di tutte le corrispondenze. Eseguire una ricerca più specifica per ridurre il numero di risultati.", + "searchCanceled": "La ricerca è stata annullata prima della visualizzazione dei risultati - ", + "noResultsIncludesExcludes": "Non sono stati trovati risultati in '{0}' escludendo '{1}' - ", + "noResultsIncludes": "Non sono stati trovati risultati in '{0}' - ", + "noResultsExcludes": "Non sono stati trovati risultati escludendo '{0}' - ", + "noResultsFound": "Nessun risultato trovato. Rivedere le impostazioni delle esclusioni configurate e file ignorati - ", + "rerunSearch.message": "Cerca di nuovo", + "rerunSearchInAll.message": "Cerca di nuovo in tutti i file", + "openSettings.message": "Apri impostazioni", + "openSettings.learnMore": "Altre informazioni", + "ariaSearchResultsStatus": "La ricerca ha restituito {0} risultati in {1} file", + "search.file.result": "{0} risultato in {1} file", + "search.files.result": "{0} risultato in {1} file", + "search.file.results": "{0} risultati in {1} file", + "search.files.results": "{0} risultati in {1} file", + "searchWithoutFolder": "Non ci sono ancora cartelle aperte. La ricerca verrà eseguita solo nei file aperti - ", + "openFolder": "Apri cartella" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..5370901993 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "Sostituisci tutto (inviare la ricerca per abilitare)", + "search.action.replaceAll.enabled.label": "Sostituisci tutto", + "search.replace.toggle.button.title": "Attiva/Disattiva sostituzione", + "label.Search": "Cerca: digitare il termine di ricerca e premere INVIO per cercare oppure ESC per annullare", + "search.placeHolder": "Cerca", + "label.Replace": "Sostituisci: digitare il termine da sostituire e premere INVIO per visualizzare l'anteprima oppure ESC per annullare", + "search.replace.placeHolder": "Sostituisci", + "regexp.validationFailure": "Espressione corrispondente a qualsiasi valore" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/ita/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..57a2ef657f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "Nell'area di lavoro non ci sono cartelle denominate {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..c44cf807da --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "Frammenti per contributes.", + "vscode.extension.contributes.snippets-language": "Identificatore di linguaggio per cui si aggiunge come contributo questo frammento.", + "vscode.extension.contributes.snippets-path": "Percorso del file snippets. È relativo alla cartella delle estensioni e in genere inizia con './snippets/'.", + "invalid.language": "Il linguaggio in `contributes.{0}.language` è sconosciuto. Valore specificato: {1}", + "invalid.path.0": "È previsto un valore stringa in `contributes.{0}.path`. Valore specificato: {1}", + "invalid.path.1": "Valore previsto di `contributes.{0}.path` ({1}) da includere nella cartella dell'estensione ({2}). L'estensione potrebbe non essere più portatile.", + "badVariableUse": "Il frammento \"{0}\" molto probabilmente confonde variabili-frammento con segnaposti-frammento. Vedere https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax per ulteriori dettagli." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..341076ba21 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "Inserisci frammento" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..4f8f32289e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "Seleziona il linguaggio per il frammento", + "openSnippet.errorOnCreate": "Non è possibile creare {0}", + "openSnippet.label": "Apri frammenti di codice utente", + "preferences": "Preferenze", + "snippetSchema.json.default": "Frammento vuoto", + "snippetSchema.json": "Configurazione del frammento utente", + "snippetSchema.json.prefix": "Prefisso da usare quando si seleziona il frammento in IntelliSense", + "snippetSchema.json.body": "Il contenuto del frammento. Usare '$1', '${1:defaultText}' per definire le posizioni del cursore, utilizzare '$0' per la posizione finale del cursore. Inserire i valori delle variabili con '${varName}' e '${varName:defaultText}', ad esempio 'Nome del file: $TM_FILENAME'.", + "snippetSchema.json.description": "Descrizione del frammento." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..eadcf8438e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "Frammento utente", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0}, {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..dba03c62af --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "Inserisce frammenti di codice quando il prefisso corrisponde. Funziona in modo ottimale quando non sono abilitati i suggerimenti rapidi." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..0803bef26e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "Aiutaci a migliorare il nostro supporto all'{0}", + "takeShortSurvey": "Partecipa a un breve sondaggio", + "remindLater": "Visualizza più tardi", + "neverAgain": "Non visualizzare più questo messaggio" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..835553b475 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Partecipare a un breve sondaggio?", + "takeSurvey": "Partecipa a sondaggio", + "remindLater": "Visualizza più tardi", + "neverAgain": "Non visualizzare più questo messaggio" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..c316638462 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "No tasks matching" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..71b124fed4 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, tasks", + "recentlyUsed": "attività usate di recente", + "configured": "attività configurate", + "detected": "attività rilevate", + "customizeTask": "Configura attività" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..493d32aa84 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Digitare il nome di un'attività da riavviare", + "noTasksMatching": "No tasks matching", + "noTasksFound": "Non sono state trovate attività da riavviare" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..9f991f1d1c --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Digitare il nome di un'attività da eseguire", + "noTasksMatching": "No tasks matching", + "noTasksFound": "Non sono state trovate attività" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..f167964ff5 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Type the name of a task to terminate", + "noTasksMatching": "No tasks matching", + "noTasksFound": "No tasks to terminate found" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..c316638462 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "No tasks matching" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..e9d046800c --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Avviso: options.cwd deve essere di tipo string. Il valore {0} verrà ignorato.\n", + "ConfigurationParser.noargs": "Errore: gli argomenti del comando devono essere un array di stringhe. Il valore specificato è:\n{0}", + "ConfigurationParser.noShell": "Avviso: la configurazione della shell è supportata solo quando si eseguono attività nel terminale.", + "ConfigurationParser.noName": "Errore: è necessario specificare un nome per il matcher problemi nell'ambito di dichiarazione:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Avviso: il matcher problemi definito è sconosciuto. I tipi supportati sono string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Errore: il riferimento a problemMatcher non è valido: {0}\n", + "ConfigurationParser.noTaskName": "Errore: le attività devono specificare una proprietà taskName. L'attività verrà ignorata.\n{0}\n", + "taskConfiguration.shellArgs": "Avviso: l'attività '{0}' è un comando della shell e il nome del comando o uno dei relativi argomenti contiene spazi senza codice di escape. Per garantire la corretta indicazione della riga di comando, unire gli argomenti nel comando.", + "taskConfiguration.noCommandOrDependsOn": "Errore: l'attività '{0}' non specifica un comando né una proprietà dependsOn. L'attività verrà ignorata. La sua definizione è:\n{1}", + "taskConfiguration.noCommand": "Errore: l'attività '{0}' non definisce un comando. L'attività verrà ignorata. Definizione dell'attività:\n{1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..db50e69b92 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "Tipo di attività effettivo", + "TaskDefinition.properties": "Proprietà aggiuntive del tipo di attività", + "TaskTypeConfiguration.noType": "Nella configurazione del tipo di attività manca la proprietà obbligatoria 'taskType'", + "TaskDefinitionExtPoint": "Tipi di attività per contributes" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..459c824538 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": "Esegue il comando di compilazione di .NET Core", + "msbuild": "Esegue la destinazione di compilazione", + "externalCommand": "Esempio per eseguire un comando esterno arbitrario", + "Maven": "Consente di eseguire comandi Maven comuni" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..e6e7bfcb1b --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "Opzioni dei comandi aggiuntive", + "JsonSchema.options.cwd": "Directory di lavoro corrente del programma o dello script eseguito. Se omesso, viene usata la radice dell'area di lavoro corrente di Visual Studio Code.", + "JsonSchema.options.env": "Ambiente della shell o del programma eseguito. Se omesso, viene usato l'ambiente del processo padre.", + "JsonSchema.shellConfiguration": "Configura la shell da utilizzare.", + "JsonSchema.shell.executable": "Shell da usare.", + "JsonSchema.shell.args": "Argomenti della shell.", + "JsonSchema.command": "Comando da eseguire. Può essere un programma esterno o un comando della shell.", + "JsonSchema.tasks.args": "Argomenti passati al comando quando viene richiamata questa attività.", + "JsonSchema.tasks.taskName": "Nome dell'attività", + "JsonSchema.tasks.windows": "Configurazione dei comandi specifica di Windows", + "JsonSchema.tasks.mac": "Configurazione dei comandi specifica di Mac", + "JsonSchema.tasks.linux": "Configurazione dei comandi specifica di Linux", + "JsonSchema.tasks.suppressTaskName": "Controlla se il nome dell'attività viene aggiunto come argomento al comando. Se omesso, viene usato il valore definito globalmente.", + "JsonSchema.tasks.showOutput": "Controlla la visualizzazione dell'output dell'attività in esecuzione. Se omesso, viene usato il valore definito globalmente.", + "JsonSchema.echoCommand": "Controlla se l'eco del comando eseguito viene incluso nell'output. Il valore predefinito è false.", + "JsonSchema.tasks.watching.deprecation": "Deprecato. In alternativa, usare isBackground.", + "JsonSchema.tasks.watching": "Indica se l'attività eseguita viene mantenuta attiva e controlla il file system.", + "JsonSchema.tasks.background": "Indica se l'attività eseguita viene mantenuta attiva ed è in esecuzione in background.", + "JsonSchema.tasks.promptOnClose": "Indica se viene visualizzato un prompt utente quando Visual Studio Code viene chiuso con un'attività in esecuzione.", + "JsonSchema.tasks.build": "Esegue il mapping di questa attività al comando di compilazione predefinito di Visual Studio Code.", + "JsonSchema.tasks.test": "Esegue il mapping di questa attività al comando di test predefinito di Visual Studio Code.", + "JsonSchema.tasks.matchers": "Matcher problemi da usare. Può essere una stringa oppure una definizione di matcher problemi oppure una matrice di stringhe e matcher problemi.", + "JsonSchema.args": "Argomenti aggiuntivi passati al comando.", + "JsonSchema.showOutput": "Controlla la visualizzazione dell'output dell'attività in esecuzione. Se omesso, viene usato 'always'.", + "JsonSchema.watching.deprecation": "Deprecato. In alternativa, usare isBackground.", + "JsonSchema.watching": "Indica se l'attività eseguita viene mantenuta attiva e controlla il file system.", + "JsonSchema.background": "Indica se l'attività eseguita viene mantenuta attiva ed è in esecuzione in background.", + "JsonSchema.promptOnClose": "Indica se viene visualizzato un prompt utente quando Visual Studio Code viene chiuso con un'attività in background in esecuzione.", + "JsonSchema.suppressTaskName": "Controlla se il nome dell'attività viene aggiunto come argomento al comando. Il valore predefinito è false.", + "JsonSchema.taskSelector": "Prefisso per indicare che un argomento è l'attività.", + "JsonSchema.matchers": "Matcher problemi da usare. Può essere una stringa oppure una definizione di matcher problemi oppure una matrice di stringhe e matcher problemi.", + "JsonSchema.tasks": "Configurazioni dell'attività. In genere si tratta di miglioramenti dell'attività già definite nello strumento di esecuzione attività esterno." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..22c572b1ab --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "Numero di versione della configurazione", + "JsonSchema._runner": "Runner è stata promossa. Utilizzare la proprietà ufficiale runner", + "JsonSchema.runner": "Definisce se l'attività viene eseguita come un processo e l'output viene visualizzato nella finestra di output o all'interno del terminale.", + "JsonSchema.windows": "Configurazione dei comandi specifica di Windows", + "JsonSchema.mac": "Configurazione dei comandi specifica di Mac", + "JsonSchema.linux": "Configurazione dei comandi specifica di Linux", + "JsonSchema.shell": "Specifica se il comando è un comando della shell o un programma esterno. Se omesso, viene usato il valore predefinito false." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..b2b3198571 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "Specifica se il comando è un comando della shell o un programma esterno. Se omesso, viene usato il valore predefinito false.", + "JsonSchema.tasks.isShellCommand.deprecated": "La proprietà isShellCommand è deprecata. Usare la proprietà type dell'attività e la proprietà shell nelle opzioni. Vedere anche le note sulla versione 1.14.", + "JsonSchema.tasks.dependsOn.string": "Altra attività da cui dipende questa attività.", + "JsonSchema.tasks.dependsOn.array": "Altre attività da cui dipende questa attività.", + "JsonSchema.tasks.presentation": "Consente di configurare il pannello usato per presentare l'output dell'attività e legge il relativo input.", + "JsonSchema.tasks.presentation.echo": "Controlla se l'eco del comando eseguito viene visualizzato nel pannello. Il valore predefinito è true.", + "JsonSchema.tasks.presentation.focus": "Controlla se il pannello riceve lo stato attivo. Il valore predefinito è false. Se è impostato su true, il pannello viene anche visualizzato.", + "JsonSchema.tasks.presentation.reveal.always": "Visualizza sempre il terminale quando viene eseguita questa attività.", + "JsonSchema.tasks.presentation.reveal.silent": "Visualizza il terminale solo se all'attività non è associato alcun matcher problemi e si verifica un errore durante l'esecuzione dell'attività.", + "JsonSchema.tasks.presentation.reveal.never": "Non visualizza mai il terminale quando viene eseguita questa attività.", + "JsonSchema.tasks.presentation.reveals": "Controlla se il pannello che esegue l'attività viene visualizzato o meno. Il valore predefinito è \"always\".", + "JsonSchema.tasks.presentation.instance": "Controlli se il pannello è condiviso tra le attività, dedicato a quest'attività o se ne viene creato uno nuovo a ogni esecuzione.", + "JsonSchema.tasks.terminal": "La proprietà terminal è deprecata. In alternativa, usare presentation.", + "JsonSchema.tasks.group.kind": "Gruppo di esecuzione dell'attività.", + "JsonSchema.tasks.group.isDefault": "Definisce se questa attività è l'attività predefinita nel gruppo.", + "JsonSchema.tasks.group.defaultBuild": "Contrassegna le attività come attività di compilazione predefinita.", + "JsonSchema.tasks.group.defaultTest": "Contrassegna le attività come attività di test predefinita.", + "JsonSchema.tasks.group.build": "Contrassegna l'attività come attività di compilazione accessibile tramite il comando 'Esegui attività di compilazione'.", + "JsonSchema.tasks.group.test": "Contrassegna l'attività come attività di test accessibile tramite il comando 'Esegui attività di test'.", + "JsonSchema.tasks.group.none": "Non assegna l'attività ad alcun gruppo", + "JsonSchema.tasks.group": "Definisce il gruppo di esecuzione a cui appartiene questa attività. Supporta \"build\" per aggiungerlo al gruppo di compilazione e \"test\" per aggiungerlo al gruppo di test.", + "JsonSchema.tasks.type": "Definisce se l'attività viene eseguita come un processo o come un comando all'interno di una shell. L'impostazione predefinita è processo.", + "JsonSchema.version": "Numero di versione della configurazione", + "JsonSchema.tasks.identifier": "Identificatore definito dall'utente per fare riferimento all'attività in launch.json o in una clausola dependsOn.", + "JsonSchema.tasks.taskLabel": "Etichetta dell'attività", + "JsonSchema.tasks.taskName": "Nome dell'attività", + "JsonSchema.tasks.taskName.deprecated": "La proprietà name dell'attività è deprecata. In alternativa, usare la proprietà label.", + "JsonSchema.tasks.background": "Indica se l'attività eseguita viene mantenuta attiva ed è in esecuzione in background.", + "JsonSchema.tasks.promptOnClose": "Indica se viene visualizzato un prompt utente quando Visual Studio Code viene chiuso con un'attività in esecuzione.", + "JsonSchema.tasks.matchers": "Matcher problemi da usare. Può essere una stringa oppure una definizione di matcher problemi oppure una matrice di stringhe e matcher problemi.", + "JsonSchema.customizations.customizes.type": "Tipo di attività da personalizzare", + "JsonSchema.tasks.customize.deprecated": "La proprietà customize è deprecata. Vedere le note sulla versione 1.14 per informazioni su come eseguire la migrazione al nuovo approccio di personalizzazione delle attività", + "JsonSchema.tasks.showOputput.deprecated": "La proprietà showOutput è deprecata. In alternativa, usare invece la proprietà reveal all'interno della proprietà presentation. Vedere anche le note sulla versione 1.14.", + "JsonSchema.tasks.echoCommand.deprecated": "La proprietà echoCommand è deprecata. In alternativa, usare la proprietà echo all'interno della proprietà presentation. Vedere anche le note sulla versione 1.14.", + "JsonSchema.tasks.suppressTaskName.deprecated": "La proprietà suppressTaskName è deprecata. In alternativa, incorporare nell'attività il comando con i relativi argomenti. Vedere anche le note sulla versione 1.14.", + "JsonSchema.tasks.isBuildCommand.deprecated": "La proprietà isBuildCommand è deprecata. In alternativa, usare la proprietà group. Vedere anche le note sulla versione 1.14.", + "JsonSchema.tasks.isTestCommand.deprecated": "La proprietà isTestCommand è deprecata. In alternativa, usare la proprietà group. Vedere anche le note sulla versione 1.14.", + "JsonSchema.tasks.taskSelector.deprecated": "La proprietà taskSelector è deprecata. In alternativa, incorporare nell'attività il comando con i relativi argomenti. Vedere anche le note sulla versione 1.14. ", + "JsonSchema.windows": "Configurazione dei comandi specifica di Windows", + "JsonSchema.mac": "Configurazione dei comandi specifica di Mac", + "JsonSchema.linux": "Configurazione dei comandi specifica di Linux" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..e0de34b609 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "Attività", + "ConfigureTaskRunnerAction.noWorkspace": "Le attività sono disponibili solo per una cartella dell'area di lavoro.", + "ConfigureTaskRunnerAction.quickPick.template": "Seleziona strumento di esecuzione attività", + "ConfigureTaskRunnerAction.autoDetecting": "Rilevamento automatico delle attività per {0}", + "ConfigureTaskRunnerAction.autoDetect": "Il rilevamento automatico del sistema dell'attività non è riuscito. Verrà usato il modello predefinito. Per i dettagli, vedere l'output dell'attività.", + "ConfigureTaskRunnerAction.autoDetectError": "Il rilevamento automatico del sistema dell'attività ha restituito errori. Per i dettagli, vedere l'output dell'attività.", + "ConfigureTaskRunnerAction.failed": "Non è possibile creare il file 'tasks.json' all'interno della cartella '.vscode'. Per dettagli, vedere l'output dell'attività.", + "ConfigureTaskRunnerAction.label": "Configura esecuzione attività", + "ConfigureBuildTaskAction.label": "Configura attività di compilazione", + "CloseMessageAction.label": "Chiudi", + "ShowTerminalAction.label": "Visualizza terminale", + "problems": "Problemi", + "manyMarkers": "Più di 99", + "runningTasks": "Visualizza attività in esecuzione", + "tasks": "Attività", + "TaskSystem.noHotSwap": "Se si cambia il motore di esecuzione delle attività, è necessario ricaricare la finestra", + "TaskService.noBuildTask1": "Non è stata definita alcuna attività di compilazione. Contrassegnare un'attività con 'isBuildCommand' nel file tasks.json.", + "TaskService.noBuildTask2": "Non è stata definita alcuna attività di compilazione. Contrassegnare un'attività come gruppo 'build' nel file tasks.json.", + "TaskService.noTestTask1": "Non è stata definita alcuna attività di test. Contrassegnare un'attività con 'isTestCommand' nel file tasks.json.", + "TaskService.noTestTask2": "Non è stata definita alcuna attività di test. Contrassegnare un'attività come gruppo 'test' nel file tasks.json.", + "TaskServer.noTask": "Attività {0} richiesta per l'esecuzione non trovata", + "TaskService.attachProblemMatcher.continueWithout": "Continua senza analizzare l'output dell'attività", + "TaskService.attachProblemMatcher.never": "Mai analizzare l'output dell'attività", + "TaskService.attachProblemMatcher.learnMoreAbout": "Ulteriori informazioni sull'analisi dell'output della attività", + "selectProblemMatcher": "Selezionare il tipo di errori e di avvisi per cui analizzare l'output dell'attività", + "customizeParseErrors": "La configurazione dell'attività corrente presenta errori. Per favore correggere gli errori prima di personalizzazione un'attività.", + "moreThanOneBuildTask": "tasks.json contiene molte attività di compilazione. È in corso l'esecuzione della prima.\n", + "TaskSystem.activeSame.background": "L'attività '{0}' è già attiva ed in modalità background. Per terminarla, usare `Termina attività` dal menu Attività", + "TaskSystem.activeSame.noBackground": "L'attività '{0}' è già attiva. Per terminarla utilizzare 'Termina attività...' dal menu Attività.", + "TaskSystem.active": "Al momento c'è già un'attività in esecuzione. Terminarla prima di eseguirne un'altra.", + "TaskSystem.restartFailed": "Non è stato possibile terminare e riavviare l'attività {0}", + "TaskSystem.configurationErrors": "Errore: la configurazione delle attività specificata contiene errori di convalida e non è utilizzabile. Correggere prima gli errori.", + "TaskSystem.invalidTaskJson": "Errore: nel contenuto del file tasks.json sono presenti errori di sintassi. Correggerli prima di eseguire un'attività.\n", + "TaskSystem.runningTask": "È presente un'attività in esecuzione. Terminarla?", + "TaskSystem.terminateTask": "&&Termina attività", + "TaskSystem.noProcess": "L'attività avviata non esiste più. Se l'attività implica la generazione di processi in background, uscendo da Visual Studio Code potrebbero essere presenti processi orfani. Per evitarlo, avviare l'ultimo processo in background con un flag di attesa.", + "TaskSystem.exitAnyways": "&&Esci comunque", + "TerminateAction.label": "Termina attività", + "TaskSystem.unknownError": "Si è verificato un errore durante l'esecuzione di un'attività. Per dettagli, vedere il log attività.", + "TaskService.noWorkspace": "Le attività sono disponibili solo per una cartella dell'area di lavoro.", + "recentlyUsed": "attività usate di recente", + "configured": "attività configurate", + "detected": "attività rilevate", + "TaskService.fetchingBuildTasks": "Recupero delle attività di compilazione...", + "TaskService.noBuildTaskTerminal": "Non è stata trovata alcuna attività di compilazione. Fare clic su 'Configura attività di compilazione' per definirne una.", + "TaskService.pickBuildTask": "Selezionare l'attività di compilazione da eseguire", + "TaskService.fetchingTestTasks": "Recupero delle attività di test...", + "TaskService.noTestTaskTerminal": "Non è stata trovata alcuna attività di test. Fare clic su 'Configura Test Runner' per definirne una.", + "TaskService.pickTestTask": "Selezionare l'attività di test da eseguire", + "TaskService.noTaskRunning": "Non ci sono attività attualmente in esecuzione.", + "TaskService.tastToTerminate": "Selezionare l'attività da terminare", + "TerminateAction.noProcess": "Il processo avviato non esiste più. Se l'attività implica la generazione di attività in background, uscendo da Visual Studio Code potrebbero essere presenti processi orfani.", + "TerminateAction.failed": "Non è stato possibile terminare l'attività in esecuzione", + "TaskService.noTaskToRestart": "Non ci sono attività da riavviare.", + "TaskService.tastToRestart": "Selezionare l'attività da riavviare", + "TaskService.defaultBuildTaskExists": "{0} è già contrassegnato come attività di compilazione predefinita.", + "TaskService.pickDefaultBuildTask": "Selezionare l'attività da usare come attività di compilazione predefinita", + "TaskService.defaultTestTaskExists": "{0} è già contrassegnato come attività di test predefinita.", + "TaskService.pickDefaultTestTask": "Selezionare l'attività da usare come attività di test predefinita", + "TaskService.noTaskIsRunning": "Nessuna attività è in esecuzione.", + "TaskService.pickShowTask": "Selezionare l'attività di cui mostrare l'output", + "ShowLogAction.label": "Mostra log attività", + "RunTaskAction.label": "Esegui attività", + "RestartTaskAction.label": "Riavvia attività in esecuzione", + "ShowTasksAction.label": "Mostra attività in esecuzione", + "BuildAction.label": "Esegui attività di compilazione", + "TestAction.label": "Esegui attività di test", + "ConfigureDefaultBuildTask.label": "Configura attività di compilazione predefinita", + "ConfigureDefaultTestTask.label": "Configura attività di test predefinita", + "quickOpen.task": "Esegui attività" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..552f230a0e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "Attività" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..faf688828d --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "Si è verificato un errore sconosciuto durante l'esecuzione di un'attività. Per dettagli, vedere il log di output dell'attività.", + "TerminalTaskSystem.terminalName": "Attività - {0}", + "reuseTerminal": "Terminale verrà riutilizzato dalle attività, premere un tasto qualsiasi per chiuderlo.", + "TerminalTaskSystem": "Non è possibile eseguire un comando della shell su un'unità UNC.", + "unkownProblemMatcher": "Il matcher problemi {0} non può essere risolto. il matcher verrà ignorato" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..408ff4a402 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "Eseguendo gulp --tasks-simple non è stata elencata alcuna attività. È stato eseguito npm install?", + "TaskSystemDetector.noJakeTasks": "Eseguendo jake --tasks non è stata elencata alcuna attività. È stato eseguito npm install?", + "TaskSystemDetector.noGulpProgram": "Gulp non è installato nel sistema. Eseguire npm install -g gulp per installarlo.", + "TaskSystemDetector.noJakeProgram": "Jake non è installato nel sistema. Eseguire npm install -g jake per installarlo.", + "TaskSystemDetector.noGruntProgram": "Grunt non è installato nel sistema. Eseguire npm install -g grunt per installarlo.", + "TaskSystemDetector.noProgram": "Il programma {0} non è stato trovato. Messaggio: {1}", + "TaskSystemDetector.buildTaskDetected": "È stata rilevata l'attività di compilazione denominata '{0}'.", + "TaskSystemDetector.testTaskDetected": "È stata rilevata l'attività di test denominata '{0}'." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..d1e9db7f04 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "Si è verificato un errore sconosciuto durante l'esecuzione di un'attività. Per dettagli, vedere il log di output dell'attività.", + "TaskRunnerSystem.watchingBuildTaskFinished": "\nIl controllo delle attività di build è terminato.", + "TaskRunnerSystem.childProcessError": "Failed to launch external program {0} {1}.", + "TaskRunnerSystem.cancelRequested": "\nL'attività '{0}' è stata terminata come richiesto dall'utente.", + "unkownProblemMatcher": "Il matcher problemi {0} non può essere risolto. Il matcher verrà ignorato" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..ddea88de10 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Avviso: options.cwd deve essere di tipo string. Il valore {0} verrà ignorato.\n", + "ConfigurationParser.noargs": "Errore: gli argomenti del comando devono essere un array di stringhe. Il valore specificato è:\n{0}", + "ConfigurationParser.noShell": "Avviso: la configurazione della shell è supportata solo quando si eseguono attività nel terminale.", + "ConfigurationParser.noName": "Errore: è necessario specificare un nome per il matcher problemi nell'ambito di dichiarazione:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Avviso: il matcher problemi definito è sconosciuto. I tipi supportati sono string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Errore: il riferimento a problemMatcher non è valido: {0}\n", + "ConfigurationParser.noTaskType": "Errore: la configurazione di tasks deve contenere una proprietà di tipo. La configurazione verrà ignorata.\n{0}\n", + "ConfigurationParser.noTypeDefinition": "Errore: non ci sono attività registrate di tipo '{0}'. Non è stata installata un'estensione che fornisce un provider di task corrispondente?", + "ConfigurationParser.notCustom": "Errore: tasks non è dichiarato come un'attività personalizzata. La configurazione verrà ignorata.\n{0}\n", + "ConfigurationParser.noTaskName": "Errore: le attività devono specificare una proprietà taskName. L'attività verrà ignorata.\n{0}\n", + "taskConfiguration.shellArgs": "Avviso: l'attività '{0}' è un comando della shell e il nome del comando o uno dei relativi argomenti contiene spazi senza codice di escape. Per garantire la corretta indicazione della riga di comando, unire gli argomenti nel comando.", + "taskConfiguration.noCommandOrDependsOn": "Errore: l'attività '{0}' non specifica un comando né una proprietà dependsOn. L'attività verrà ignorata. La sua definizione è:\n{1}", + "taskConfiguration.noCommand": "Errore: l'attività '{0}' non definisce un comando. L'attività verrà ignorata. Definizione dell'attività:\n{1}", + "TaskParse.noOsSpecificGlobalTasks": "L'attività versione 2.0.0 non supporta attività specifiche globali del sistema operativo. Convertirle in un'attività con un comando specifico del sistema operativo. Attività interessate:\n{0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..8a75a232e3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0}, selettore terminale", + "termCreateEntryAriaLabel": "{0}, crea un nuovo terminale", + "'workbench.action.terminal.newplus": "$(plus) Crea nuovo terminale integrato", + "noTerminalsMatching": "Nessun terminale corrispondente", + "noTerminalsFound": "Nessun terminale aperto" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..4478e03d40 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "Mostra tutti i terminali aperti", + "terminalIntegratedConfigurationTitle": "Terminale integrato", + "terminal.integrated.shell.linux": "Percorso della shell usata dal terminale in Linux.", + "terminal.integrated.shellArgs.linux": "Argomenti della riga di comando da usare nel terminale Linux.", + "terminal.integrated.shell.osx": "Percorso della shell usata dal terminale in OS X.", + "terminal.integrated.shellArgs.osx": "Argomenti della riga di comando da usare nel terminale OS X.", + "terminal.integrated.shell.windows": "Il percorso della shell che il terminale utilizza su Windows. Quando si utilizzano shell fornite con Windows (cmd, PowerShell o Bash su Ubuntu).", + "terminal.integrated.shellArgs.windows": "Argomenti della riga di comando da usare nel terminale Windows.", + "terminal.integrated.rightClickCopyPaste": "Se impostata, impedirà la visualizzazione del menu di scelta rapida quando si fa clic con il pulsante destro del mouse all'interno del terminale, ma eseguirà il comando Copia in presenza di una selezione e il comando Incolla in assenza di una selezione.", + "terminal.integrated.fontFamily": "Controlla la famiglia di caratteri del terminale. L'impostazione predefinita è il valore di editor.fontFamily.", + "terminal.integrated.fontLigatures": "Controlla se i caratteri legatura sono abilitati nel terminale.", + "terminal.integrated.fontSize": "Consente di controllare le dimensioni del carattere in pixel del terminale.", + "terminal.integrated.lineHeight": "Controlla l'altezza della riga del terminale. Questo numero è moltiplicato dalle dimensioni del carattere del terminale per ottenere l'altezza di riga effettiva in pixel.", + "terminal.integrated.enableBold": "Indica se abilitare il testo in grassetto nella console del terminale. Richiede il supporto da parte della console", + "terminal.integrated.cursorBlinking": "Controlla se il cursore del terminale è intermittente o meno.", + "terminal.integrated.cursorStyle": "Controlla lo stile del cursore del terminale.", + "terminal.integrated.scrollback": "Consente di controllare il numero massimo di righe che il terminale mantiene nel buffer.", + "terminal.integrated.setLocaleVariables": "Controlla se le variabili delle impostazioni locali sono impostate all'avvio del terminale. Il valore predefinito è true per OS X e false per altre piattaforme.", + "terminal.integrated.cwd": "Percorso di avvio esplicito in cui verrà avviato il terminale. Viene usato come directory di lavoro corrente per il processo della shell. Può risultare particolarmente utile nelle impostazioni dell'area di lavoro se la directory radice non costituisce una directory di lavoro corrente comoda.", + "terminal.integrated.confirmOnExit": "Indica se confermare all'uscita la presenza di sessioni di terminale attive.", + "terminal.integrated.commandsToSkipShell": "Set di ID comando i cui tasti di scelta rapida non verranno inviati alla shell e verranno sempre gestiti da Code. In tal modo i tasti di scelta rapida normalmente utilizzati dalla shell avranno lo stesso effetto di quando il terminale non ha lo stato attivo, ad esempio CTRL+P per avviare Quick Open.", + "terminal.integrated.env.osx": "Oggetto con variabili di ambiente che verrà aggiunto al processo VS Code per essere utilizzato dal terminale su OS X", + "terminal.integrated.env.linux": "Oggetto con variabili di ambiente che verrà aggiunto al processo VS Code per essere utilizzato dal terminale su Linux", + "terminal.integrated.env.windows": "Oggetto con variabili di ambiente che verrà aggiunto al processo VS Code per essere utilizzato dal terminale su Windows", + "terminal": "Terminale", + "terminalCategory": "Terminale", + "viewCategory": "Visualizza" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..febed2adea --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "Attiva/Disattiva terminale integrato", + "workbench.action.terminal.kill": "Termina istanza attiva del terminale", + "workbench.action.terminal.kill.short": "Termina il terminale", + "workbench.action.terminal.quickKill": "Termina istanza del terminale", + "workbench.action.terminal.copySelection": "Copia selezione", + "workbench.action.terminal.selectAll": "Seleziona tutto", + "workbench.action.terminal.deleteWordLeft": "Elimina parola a sinistra", + "workbench.action.terminal.deleteWordRight": "Elimina la parola a destra", + "workbench.action.terminal.new": "Crea nuovo terminale integrato", + "workbench.action.terminal.new.short": "Nuovo terminale", + "workbench.action.terminal.focus": "Sposta stato attivo su terminale", + "workbench.action.terminal.focusNext": "Sposta stato attivo su terminale successivo", + "workbench.action.terminal.focusAtIndex": "Sposta stato attivo su terminale {0}", + "workbench.action.terminal.focusPrevious": "Sposta stato attivo su terminale precedente", + "workbench.action.terminal.paste": "Incolla nel terminale attivo", + "workbench.action.terminal.DefaultShell": "Selezionare la Shell di Default", + "workbench.action.terminal.runSelectedText": "Esegui testo selezionato nel terminale attivo", + "workbench.action.terminal.runActiveFile": "Esegui file attivo nel terminale attivo", + "workbench.action.terminal.runActiveFile.noFile": "Nel terminale è possibile eseguire solo file su disco", + "workbench.action.terminal.switchTerminalInstance": "Cambia istanza del terminale", + "workbench.action.terminal.scrollDown": "Scorri giù (riga)", + "workbench.action.terminal.scrollDownPage": "Scorri giù (pagina)", + "workbench.action.terminal.scrollToBottom": "Scorri alla fine", + "workbench.action.terminal.scrollUp": "Scorri su (riga)", + "workbench.action.terminal.scrollUpPage": "Scorri su (pagina)", + "workbench.action.terminal.scrollToTop": "Scorri all'inizio", + "workbench.action.terminal.clear": "Cancella", + "workbench.action.terminal.allowWorkspaceShell": "Consente la configurazione della Shell dell'area di lavoro", + "workbench.action.terminal.disallowWorkspaceShell": "Non consente la configurazione della Shell dell'area di lavoro", + "workbench.action.terminal.rename": "Rinomina", + "workbench.action.terminal.rename.prompt": "Immettere il nome del terminale", + "workbench.action.terminal.focusFindWidget": "Stato attivo su widget Trova", + "workbench.action.terminal.hideFindWidget": "Nascondi widget Trova", + "nextTerminalFindTerm": "Mostra il termine di ricerca successivo", + "previousTerminalFindTerm": "Mostra il termine di ricerca precedente", + "quickOpenTerm": "Terminale: Cambia terminale attivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..1a4265021a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "Il colore di sfondo del terminale, questo consente di colorare il terminale in modo diverso dal pannello.", + "terminal.foreground": "Il colore di primo piano del terminale.", + "terminalCursor.foreground": "Colore di primo piano del cursore del terminale.", + "terminalCursor.background": "Colore di sfondo del cursore del terminale. Permette di personalizzare il colore di un carattere quando sovrapposto da un blocco cursore.", + "terminal.ansiColor": "Colore ANSI '{0}' nel terminale." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..533fbc2f1a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "Consentire l'esecuzione di {0} (definito come impostazione dell'area di lavoro) nel terminale?", + "allow": "Allow", + "disallow": "Disallow" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..a9b06424c9 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Trova", + "placeholder.find": "Trova", + "label.previousMatchButton": "Risultato precedente", + "label.nextMatchButton": "Risultato successivo", + "label.closeButton": "Chiudi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..779aabbcad --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "Il terminale non contiene alcuna selezione da copiare", + "terminal.integrated.exitedWithCode": "Il processo del terminale è stato terminato. Codice di uscita: {0}", + "terminal.integrated.waitOnExit": "Premere un tasto qualsiasi per chiudere il terminale", + "terminal.integrated.launchFailed": "L'avvio del comando del processo di terminale `{0}{1}` non è riuscito. Codice di uscita: {2}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..28a947d124 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "Alt + clic per seguire il collegamento", + "terminalLinkHandler.followLinkCmd": "Cmd + clic per seguire il collegamento", + "terminalLinkHandler.followLinkCtrl": "CTRL + clic per seguire il collegamento" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..7fe1ee7ab7 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "Copia", + "createNewTerminal": "Nuovo terminale", + "paste": "Incolla", + "selectAll": "Seleziona tutto", + "clear": "Cancella" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..919d81b370 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "È possibile modificare la shell di terminale di default selezionando il pulsante Personalizza.", + "customize": "Personalizza", + "cancel": "Annulla", + "never again": "OK, non visualizzare più", + "terminal.integrated.chooseWindowsShell": "Seleziona la shell di terminale preferita - è possibile modificare questa impostazione dopo", + "terminalService.terminalCloseConfirmationSingular": "C'è una sessione di terminale attiva. Terminarla?", + "terminalService.terminalCloseConfirmationPlural": "Ci sono {0} sessioni di terminale attive. Terminarle?", + "yes": "Sì" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..c34647a459 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "Tema colori", + "installColorThemes": "Installa temi colori aggiuntivi...", + "themes.selectTheme": "Selezionare il Tema colori (tasti su/giù per anteprima)", + "selectIconTheme.label": "Tema icona file", + "installIconThemes": "Installa temi dell'icona file aggiuntivi...", + "noIconThemeLabel": "Nessuno", + "noIconThemeDesc": "Disabilita le icone dei file", + "problemChangingIconTheme": "Problema durante l'impostazione del tema dell'icona: {0}", + "themes.selectIconTheme": "Seleziona il tema dell'icona file", + "generateColorTheme.label": "Genera tema colore da impostazioni correnti", + "preferences": "Preferenze", + "developer": "Sviluppatore" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..e0c6a9105a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "Quest'area di lavoro contiene impostazioni che è possibile specificare solo nelle impostazioni utente. ({0})", + "openWorkspaceSettings": "Apri impostazioni area di lavoro", + "openDocumentation": "Altre informazioni", + "ignore": "Ignora" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/ita/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..502bfdf005 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "Note sulla versione: {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..98c33849a8 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "Note sulla versione", + "updateConfigurationTitle": "Aggiorna", + "updateChannel": "Consente di configurare la ricezione degli aggiornamenti automatici da un canale di aggiornamento. Richiede un riavvio dopo la modifica." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..b6dda70120 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "Aggiorna adesso", + "later": "In seguito", + "unassigned": "non assegnato", + "releaseNotes": "Note sulla versione", + "showReleaseNotes": "Mostra note sulla versione", + "downloadNow": "Scarica ora", + "read the release notes": "Benvenuti in {0} versione {1}. Leggere le note sulla versione?", + "licenseChanged": "I termini della licenza sono cambiati. Leggerli con attenzione.", + "license": "Leggi licenza", + "neveragain": "Non visualizzare più questo messaggio", + "64bitisavailable": "{0} per Windows a 64 bit è ora disponibile.", + "learn more": "Altre informazioni", + "updateIsReady": "Nuovo aggiornamento di {0} disponibile.", + "thereIsUpdateAvailable": "È disponibile un aggiornamento.", + "updateAvailable": "{0} verrà aggiornato dopo il riavvio.", + "noUpdatesAvailable": "Al momento non sono disponibili aggiornamenti.", + "commandPalette": "Riquadro comandi...", + "settings": "Impostazioni", + "keyboardShortcuts": "Scelte rapide da tastiera", + "selectTheme.label": "Tema colori", + "themes.selectIconTheme.label": "Tema icona file", + "not available": "Aggiornamenti non disponibili", + "checkingForUpdates": "Verifica della disponibilità di aggiornamenti...", + "DownloadUpdate": "Scarica l'aggiornamento disponibile", + "DownloadingUpdate": "Download dell'aggiornamento...", + "InstallingUpdate": "Installazione dell'aggiornamento...", + "restartToUpdate": "Riavvia per aggiornare...", + "checkForUpdates": "Verifica disponibilità aggiornamenti..." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/ita/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..d2142d1925 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "Azioni di {0}", + "hideView": "Nascondi da barra laterale" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/ita/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..ad21482ffe --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "Visualizzazioni devono essere una matrice", + "requirestring": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "optstring": "la proprietà `{0}` può essere omessa o deve essere di tipo `string`", + "vscode.extension.contributes.view.id": "Identificatore della vista. Utilizzare questo per registrare un provider di dati tramite l'API 'vscode.window.registerTreeDataProviderForView'. Anche per innescare l'attivazione dell'estensione tramite la registrazione dell'evento 'onView: ${id}' a 'activationEvents'.", + "vscode.extension.contributes.view.name": "Il nome della visualizzazione. Verrà mostrato", + "vscode.extension.contributes.view.when": "Condizione che deve essere vera per mostrare questa visualizzazione", + "vscode.extension.contributes.views": "Contribuisce visualizzazioni all'editor", + "views.explorer": "Visualizzazione di esplorazione", + "views.debug": "Visualizzazione Debug", + "locationId.invalid": "'{0}' non è una posizione valida per la visualizzazione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/ita/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..4ac7a4748a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "Mostra tutti i comandi", + "watermark.quickOpen": "Vai al file", + "watermark.openFile": "Apri file", + "watermark.openFolder": "Apri cartella", + "watermark.openFileFolder": "Apri file o cartella", + "watermark.openRecent": "Apri recenti", + "watermark.newUntitledFile": "Nuovo file senza nome", + "watermark.toggleTerminal": "Attiva/Disattiva terminale", + "watermark.findInFiles": "Cerca nei file", + "watermark.startDebugging": "Avvia debug", + "watermark.unboundCommand": "non associato", + "workbenchConfigurationTitle": "Area di lavoro", + "tips.enabled": "Quando questa opzione è abilitata, se non ci sono editor aperti, verranno visualizzati i suggerimenti filigrana." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/ita/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..23a4cf2b49 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "Esplora file", + "welcomeOverlay.search": "Cerca nei file", + "welcomeOverlay.git": "Gestione del codice sorgente", + "welcomeOverlay.debug": "Avvia ed esegui il debug", + "welcomeOverlay.extensions": "Gestisci le estensioni", + "welcomeOverlay.problems": "Visualizza errori e avvisi", + "welcomeOverlay.commandPalette": "Trova ed esegui tutti i comandi", + "welcomeOverlay": "Panoramica interfaccia utente", + "hideWelcomeOverlay": "Nascondi panoramica interfaccia", + "help": "Guida" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..4f07450b10 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "Evoluzione dell'editor", + "welcomePage.start": "Avvia", + "welcomePage.newFile": "Nuovo file", + "welcomePage.openFolder": "Apri cartella...", + "welcomePage.cloneGitRepository": "Clona repository GIT...", + "welcomePage.recent": "Recenti", + "welcomePage.moreRecent": "Altro...", + "welcomePage.noRecentFolders": "Non ci sono cartelle recenti", + "welcomePage.help": "Guida", + "welcomePage.keybindingsCheatsheet": "Bigino combinazione tasti stampabile", + "welcomePage.introductoryVideos": "Video introduttivi", + "welcomePage.tipsAndTricks": "Suggerimenti e trucchi", + "welcomePage.productDocumentation": "Documentazione del prodotto", + "welcomePage.gitHubRepository": "Repository GitHub", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "Mostra la pagina iniziale all'avvio", + "welcomePage.customize": "Personalizza", + "welcomePage.installExtensionPacks": "Strumenti e linguaggi", + "welcomePage.installExtensionPacksDescription": "Installare il supporto per {0} e {1}", + "welcomePage.moreExtensions": "altro", + "welcomePage.installKeymapDescription": "Installa i tasti di scelta rapida", + "welcomePage.installKeymapExtension": "Installa i tasti di scelta rapida di {0} e {1}", + "welcomePage.others": "altri", + "welcomePage.colorTheme": "Tema colori", + "welcomePage.colorThemeDescription": "Tutto quel che serve per configurare editor e codice nel modo desiderato", + "welcomePage.learn": "Impara", + "welcomePage.showCommands": "Trova ed esegui tutti i comandi", + "welcomePage.showCommandsDescription": "Accesso e ricerca rapida di comandi dal riquadro comandi ({0})", + "welcomePage.interfaceOverview": "Panoramica dell'interfaccia", + "welcomePage.interfaceOverviewDescription": "Immagine in sovrimpressione che evidenzia i principali componenti dell'interfaccia utente", + "welcomePage.deployToAzure": "Distribuisci le applicazioni nel cloud", + "welcomePage.deployToAzureDescription": "Informazioni sulla distribuzione di app Node in Servizio app di Azure", + "welcomePage.interactivePlayground": "Playground interattivo", + "welcomePage.interactivePlaygroundDescription": "Breve panoramica delle funzionalità essenziali dell'editor" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..caf5134b95 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "Area di lavoro", + "workbench.startupEditor.none": "Avvia senza un editor.", + "workbench.startupEditor.welcomePage": "Apre la pagina di benvenuto (impostazione predefinita).", + "workbench.startupEditor.newUntitledFile": "Apre un nuovo file senza nome.", + "workbench.startupEditor": "Controlla quale editor viene visualizzato all'avvio, se non ne è stato ripristinato nessuno dalla versione precedente. Selezionare 'none' per avviare senza un editor, 'welcomePage' per aprire la pagina di benvenuto (impostazione predefinita), 'newUntitledFile' per aprire un nuovo file senza nome (solo quando si apre uno spazio di lavoro vuoto).", + "help": "Guida" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..afe47e74f5 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "Benvenuti", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "Mostra estensioni di Azure", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "Il supporto per {0} è già installato.", + "welcomePage.willReloadAfterInstallingExtensionPack": "La finestra verrà ricaricata dopo l'installazione di supporto aggiuntivo per {0}.", + "welcomePage.installingExtensionPack": "Installazione di supporto aggiuntivo per {0} in corso...", + "welcomePage.extensionPackNotFound": "Il supporto per {0} con ID {1} non è stato trovato.", + "welcomePage.keymapAlreadyInstalled": "I tasti di scelta rapida di {0} sono già installati.", + "welcomePage.willReloadAfterInstallingKeymap": "La finestra verrà ricaricata dopo l'installazione dei tasti di scelta rapida di {0}.", + "welcomePage.installingKeymap": "Installazione dei tasti di scelta rapida di {0}...", + "welcomePage.keymapNotFound": "I tasti di scelta rapida di {0} con ID {1} non sono stati trovati.", + "welcome.title": "Benvenuti", + "welcomePage.openFolderWithPath": "Apri la cartella {0} con percorso {1}", + "welcomePage.extensionListSeparator": ",", + "welcomePage.installKeymap": "Installa mappatura tastiera {0}", + "welcomePage.installExtensionPack": "Installa supporto aggiuntivo per {0}", + "welcomePage.installedKeymap": "Mappatura tastiera {0} è già installata", + "welcomePage.installedExtensionPack": "Il supporto {0} è già installato", + "ok": "OK", + "details": "Dettagli", + "cancel": "Annulla", + "welcomePage.buttonBackground": "Colore di sfondo dei pulsanti nella pagina di benvenuto.", + "welcomePage.buttonHoverBackground": "Colore di sfondo al passaggio del mouse dei pulsanti nella pagina di benvenuto." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..c2745bd554 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "Playground interattivo", + "editorWalkThrough": "Playground interattivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..8bda07905f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "Playground interattivo", + "help": "Guida", + "interactivePlayground": "Playground interattivo" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..e41030bf10 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "Scorri su (riga)", + "editorWalkThrough.arrowDown": "Scorri giù (riga)", + "editorWalkThrough.pageUp": "Scorri su (pagina)", + "editorWalkThrough.pageDown": "Scorri giù (pagina)" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..dbde768be3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "non associato", + "walkThrough.gitNotFound": "Sembra che GIT non sia installato nel sistema.", + "walkThrough.embeddedEditorBackground": "Colore di sfondo degli editor incorporati nel playground interattivo." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/ita/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..94fefdbb36 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "Impostazioni di configurazione di contributes.", + "vscode.extension.contributes.configuration.title": "Riepilogo delle impostazioni. Questa etichetta verrà usata nel file di impostazioni come commento di separazione.", + "vscode.extension.contributes.configuration.properties": "Descrizione delle proprietà di configurazione.", + "scope.window.description": "Configurazione specifica della finestra, che può essere configurata nelle impostazioni dell'utente o dell'area di lavoro.", + "scope.resource.description": "Configurazione specifica di risorse, che possono essere configurate nelle impostazioni utente, in quelle dell'area di lavoro o di una cartella.", + "scope.description": "Ambito in cui la configurazione è applicabile. Gli ambiti disponibili sono 'finestra' e 'risorsa'.", + "invalid.type": "se impostato, 'configuration.type' deve essere impostato su 'object", + "invalid.title": "'configuration.title' deve essere una stringa", + "vscode.extension.contributes.defaultConfiguration": "Aggiunge come contributo le impostazioni di configurazione predefinite dell'editor in base al linguaggio.", + "invalid.properties": "'configuration.properties' deve essere un oggetto", + "workspaceConfig.folders.description": "Elenco delle cartelle da caricare nell'area di lavoro. Deve essere un percorso di file, ad esempio `/root/folderA` o `./folderA` per un percorso relativo che verrà risolto in base alla posizione del file dell'area di lavoro.", + "workspaceConfig.folder.description": "Percorso di file, ad esempio `/root/folderA` o `./folderA` per un percorso relativo che verrà risolto in base alla posizione del file dell'area di lavoro.", + "workspaceConfig.settings.description": "Impostazioni di area di lavoro" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/ita/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..817df10612 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Apri impostazioni", + "close": "Chiudi", + "saveAndRetry": "Salva le impostazioni e riprova", + "errorUnknownKey": "Impossibile scrivere {0} perché {1} non è una configurazione registrata.", + "errorInvalidFolderConfiguration": "Impossibile scrivere nella cartella impostazioni perché {0} non supporta l'ambito di risorsa della cartella.", + "errorInvalidUserTarget": "Impossibile scrivere le impostazioni utente perché {0} non supporta l'ambito globale.", + "errorInvalidFolderTarget": "Impossibile scrivere nella cartella impostazioni perché non viene fornita alcuna risorsa.", + "errorNoWorkspaceOpened": "Impossibile scrivere su {0} poiché nessuna area di lavoro è aperta. Si prega di aprire un'area di lavoro e riprovare.", + "errorInvalidConfiguration": "Impossibile scrivere nelle impostazioni. Si prega di aprire **Impostazioni utente** per correggere eventuali errori o avvisi nel file e riprovare.", + "errorInvalidConfigurationWorkspace": "Impossibile scrivere in impostazioni. Si prega di aprire **Impostazioni area di lavoro** per correggere eventuali errori o avvisi nel file e riprovare.", + "errorConfigurationFileDirty": "Impossibile scrivere nelle impostazioni perché il file è stato modificato ma non salvato. Si prega di salvare il file **impostazioni utente** e riprovare.", + "errorConfigurationFileDirtyWorkspace": "Non è possibile scrivere in impostazioni perché il file è stato modificato ma non salvato. Salvare il file delle **Impostazioni area di lavoro** e riprovare.", + "userTarget": "Impostazioni utente", + "workspaceTarget": "Impostazioni area di lavoro", + "folderTarget": "Impostazioni della cartella" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/ita/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..c73223be04 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "Impossibile scrivere nel file. Si prega di aprire il file per correggere eventuali errori o avvisi nel file e riprovare.", + "errorFileDirty": "Impossibile scrivere nel file perché il file è stato modificato. Si prega di salvare il file e riprovare." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/ita/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..168c138f47 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetria", + "telemetry.enableCrashReporting": "Consente l'invio di segnalazioni di arresto anomalo del sistema a Microsoft.\nPer rendere effettiva questa opzione, è necessario riavviare." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/ita/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..29bc196d82 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "L'host dell'estensione non è stato avviato entro 10 secondi. Potrebbe essersi arrestato alla prima riga e richiedere un debugger per continuare.", + "extensionHostProcess.startupFail": "L'host dell'estensione non è stato avviato entro 10 secondi. Potrebbe essersi verificato un problema.", + "extensionHostProcess.error": "Errore restituito dall'host dell'estensione: {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..38120c726c --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Non è stato possibile analizzare {0}: {1}.", + "fileReadFail": "Non è possibile leggere il file {0}: {1}.", + "jsonsParseFail": "Non è stato possibile analizzare {0} o {1}: {2}.", + "missingNLSKey": "Il messaggio per la chiave {0} non è stato trovato." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..2bd58294e1 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "Strumenti di sviluppo", + "restart": "Riavvia host dell'estensione", + "extensionHostProcess.crash": "L'host dell'estensione è stato terminato in modo imprevisto.", + "extensionHostProcess.unresponsiveCrash": "L'host dell'estensione è stato terminato perché non rispondeva.", + "overwritingExtension": "Sovrascrittura dell'estensione {0} con {1}.", + "extensionUnderDevelopment": "Caricamento dell'estensione di sviluppo in {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/ita/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..676eb222a6 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "Microsoft .NET Framework 4.5 è obbligatorio. Selezionare il collegamento per installarlo.", + "installNet": "Scarica .NET Framework 4.5", + "neverShowAgain": "Non visualizzare più questo messaggio", + "trashFailed": "Non è stato possibile spostare '{0}' nel Cestino" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/ita/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..f93eb5e740 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "Risorsa del file non valida ({0})", + "fileIsDirectoryError": "Il file è la directory ({0})", + "fileNotModifiedError": "File non modificato dal giorno", + "fileTooLargeError": "File troppo grande per essere aperto", + "fileBinaryError": "Il file sembra essere binario e non può essere aperto come file di testo", + "fileNotFoundError": "Il file non è stato trovato ({0})", + "fileMoveConflict": "Non è possibile eseguire operazioni di spostamento/copia. Il file esiste già nella destinazione.", + "unableToMoveCopyError": "Non è possibile eseguire operazioni di spostamento/copia. Il file sostituirebbe la cartella in cui è contenuto.", + "foldersCopyError": "Non è possibile copiare le cartelle nell'area di lavoro. Selezionare i singoli file per copiarli.", + "fileModifiedError": "File modificato da", + "fileReadOnlyError": "Il file è di sola lettura" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/ita/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..5fa3b5b94d --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "Non è possibile scrivere perché il file è stato modificato ma non salvato. Salvare il file dei **Tasti di scelta rapida** e riprovare.", + "parseErrors": "Non è possibile scrivere i tasti di scelta rapida. Aprire il file dei **Tasti di scelta rapida** per correggere errori/avvisi nel file e riprovare.", + "errorInvalidConfiguration": "Non è possibile scrivere i tasti di scelta rapida. Il **file dei tasti di scelta rapida** contiene un oggetto che non è di tipo Array. Aprire il file per correggere l'errore e riprovare.", + "emptyKeybindingsHeader": "Inserire i tasti di scelta rapida in questo file per sovrascrivere i valori predefiniti" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/ita/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..df2baf0a41 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "è previsto un valore non vuoto.", + "requirestring": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "optstring": "la proprietà `{0}` può essere omessa o deve essere di tipo `string`", + "vscode.extension.contributes.keybindings.command": "Identificatore del comando da eseguire quando si attiva il tasto di scelta rapida.", + "vscode.extension.contributes.keybindings.key": "Tasto o sequenza di tasti (separare i tasti con un segno di addizione e le sequenze con uno spazio, ad esempio CTRL+O e CTRL+L L per una combinazione).", + "vscode.extension.contributes.keybindings.mac": "Tasto o sequenza di tasti specifica di Mac.", + "vscode.extension.contributes.keybindings.linux": "Tasto o sequenza di tasti specifica di Linux.", + "vscode.extension.contributes.keybindings.win": "Tasto o sequenza di tasti specifica di Windows.", + "vscode.extension.contributes.keybindings.when": "Condizione quando il tasto è attivo.", + "vscode.extension.contributes.keybindings": "Tasti di scelta rapida per contributes.", + "invalid.keybindings": "Il valore di `contributes.{0}` non è valido: {1}", + "unboundCommands": "Altri comandi disponibili: ", + "keybindings.json.title": "Configurazione dei tasti di scelta rapida", + "keybindings.json.key": "Tasto o sequenza di tasti (separati da spazio)", + "keybindings.json.command": "Nome del comando da eseguire", + "keybindings.json.when": "Condizione quando il tasto è attivo.", + "keybindings.json.args": "Argomenti da passare al comando da eseguire.", + "keyboardConfigurationTitle": "Tastiera", + "dispatch": "Controlla la logica di invio delle pressioni di tasti da usare, tra `keydown.code` (scelta consigliata) e `keydown.keyCode`." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/ita/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..204a364894 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Errore: {0}", + "alertWarningMessage": "Avviso: {0}", + "alertInfoMessage": "Info: {0}", + "error": "Errore", + "warning": "Avviso", + "info": "Informazioni", + "close": "Chiudi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/ita/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..ae198b665e --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "&&Sì", + "cancelButton": "Annulla" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/ita/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..90431d8ab9 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Dichiarazioni del linguaggio per contributes.", + "vscode.extension.contributes.languages.id": "ID del linguaggio.", + "vscode.extension.contributes.languages.aliases": "Alias di nome per il linguaggio.", + "vscode.extension.contributes.languages.extensions": "Estensioni di file associate al linguaggio.", + "vscode.extension.contributes.languages.filenames": "Nomi file associati al linguaggio.", + "vscode.extension.contributes.languages.filenamePatterns": "Criteri GLOB dei nomi file associati al linguaggio.", + "vscode.extension.contributes.languages.mimetypes": "Tipi MIME associati al linguaggio.", + "vscode.extension.contributes.languages.firstLine": "Espressione regolare corrispondente alla prima riga di un file del linguaggio.", + "vscode.extension.contributes.languages.configuration": "Percorso relativo di un file che contiene le opzioni di configurazione per il linguaggio.", + "invalid": "Il valore di `contributes.{0}` non è valido. È prevista una matrice.", + "invalid.empty": "Il valore di `contributes.{0}` è vuoto", + "require.id": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "opt.extensions": "la proprietà `{0}` può essere omessa e deve essere di tipo `string[]`", + "opt.filenames": "la proprietà `{0}` può essere omessa e deve essere di tipo `string[]`", + "opt.firstLine": "la proprietà `{0}` può essere omessa e deve essere di tipo `string`", + "opt.configuration": "la proprietà `{0}` può essere omessa e deve essere di tipo `string`", + "opt.aliases": "la proprietà `{0}` può essere omessa e deve essere di tipo `string[]`", + "opt.mimetypes": "la proprietà `{0}` può essere omessa e deve essere di tipo `string[]`" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/ita/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..441530a01b --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1}", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/ita/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..9b5724a490 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Tokenizer TextMate per contributes.", + "vscode.extension.contributes.grammars.language": "Identificatore di linguaggio per cui si aggiunge come contributo questa sintassi.", + "vscode.extension.contributes.grammars.scopeName": "Nome dell'ambito TextMate usato dal file tmLanguage.", + "vscode.extension.contributes.grammars.path": "Percorso del file tmLanguage. È relativo alla cartella delle estensioni e in genere inizia con './syntaxes/'.", + "vscode.extension.contributes.grammars.embeddedLanguages": "Mapping tra nome ambito e ID linguaggio se questa grammatica contiene linguaggi incorporati.", + "vscode.extension.contributes.grammars.injectTo": "Elenco di nomi di ambito del linguaggio in cui viene inserita questa grammatica." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/ita/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..a4ac5acb7e --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Il linguaggio in `contributes.{0}.language` è sconosciuto. Valore specificato: {1}", + "invalid.scopeName": "È previsto un valore stringa in `contributes.{0}.scopeName`. Valore specificato: {1}", + "invalid.path.0": "È previsto un valore stringa in `contributes.{0}.path`. Valore specificato: {1}", + "invalid.injectTo": "Il valore in `contributes.{0}.injectTo` non è valido. Deve essere una matrice di nomi di ambito del linguaggio. Valore specificato: {1}", + "invalid.embeddedLanguages": "Il valore in `contributes.{0}.embeddedLanguages` non è valido. Deve essere un mapping di oggetti tra nome ambito e linguaggio. Valore specificato: {1}", + "invalid.path.1": "Valore previsto di `contributes.{0}.path` ({1}) da includere nella cartella dell'estensione ({2}). L'estensione potrebbe non essere più portatile.", + "no-tm-grammar": "Non è stata registrata alcuna grammatica TM per questo linguaggio." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/ita/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..dc33fe3043 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "Il file è modificato ma non salvato. Salvarlo prima di riaprirlo con un'altra codifica.", + "genericSaveError": "Non è stato possibile salvare '{0}': {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/ita/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..ca2ae3b199 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "Non è stato possibile eseguire il backup dei file (errore {0}). Per chiudere, provare a salvare i file." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/ita/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..558b0f929f --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "Salvare le modifiche apportate a {0}?", + "saveChangesMessages": "Salvare le modifiche apportate ai file seguenti di {0}?", + "moreFile": "...1 altro file non visualizzato", + "moreFiles": "...{0} altri file non visualizzati", + "saveAll": "&&Salva tutto", + "save": "&&Salva", + "dontSave": "&&Non salvare", + "cancel": "Annulla", + "saveChangesDetail": "Le modifiche apportate andranno perse se non vengono salvate.", + "allFiles": "Tutti i file", + "noExt": "Nessuna estensione" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/ita/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..a83f954279 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "Colori e stili per il token.", + "schema.token.foreground": "Colore primo piano per il token.", + "schema.token.fontStyle": "Stile del carattere della regola: uno o combinazione di 'italic', 'bold' e 'underline'", + "schema.fontStyle.error": "Lo stile del carattere deve essere una combinazione di 'italic', 'bold' e 'underline'", + "schema.properties.name": "Descrizione della regola.", + "schema.properties.scope": "Selettore di ambito usato per la corrispondenza della regola.", + "schema.tokenColors.path": "Percorso di un file tmTheme (relativo al file corrente).", + "schema.colors": "Colori per l'evidenziazione della sintassi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/ita/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..7f247cf9ad --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "Icona di cartella per le cartelle espanse. L'icona di cartella espansa è facoltativa. Se non è impostata, verrà visualizzata l'icona definita per la cartella.", + "schema.folder": "Icona di cartella per le cartelle compresse e anche per quelle espanse se folderExpanded non è impostato.", + "schema.file": "Icona del file predefinita, visualizzata per tutti i file che non corrispondono ad alcuna estensione, nome file o ID lingua.", + "schema.folderNames": "Associa i nomi di cartella alle icone. La chiave dell'oggetto è il nome della cartella, escludendo eventuali segmenti di percorso. Non sono consentiti criteri o caratteri jolly. Per la corrispondenza dei nomi di cartella non viene fatta distinzione tra maiuscole e minuscole.", + "schema.folderName": "ID della definizione di icona per l'associazione.", + "schema.folderNamesExpanded": "Associa i nomi di cartella alle icone per le cartelle espanse. La chiave dell'oggetto è il nome della cartella, escludendo eventuali segmenti di percorso. Non sono consentiti criteri o caratteri jolly. Per la corrispondenza dei nomi di cartella non viene fatta distinzione tra maiuscole e minuscole.", + "schema.folderNameExpanded": "ID della definizione di icona per l'associazione.", + "schema.fileExtensions": "Associa le estensioni di file alle icone. La chiave dell'oggetto è il nome dell'estensione di file. Il nome dell'estensione corrisponde all'ultimo segmento di un nome file presente dopo l'ultimo punto (escludendo il punto). Per il confronto delle estensioni non viene fatta distinzione tra maiuscole e minuscole.", + "schema.fileExtension": "ID della definizione di icona per l'associazione.", + "schema.fileNames": "Associa i nomi file alle icone. La chiave dell'oggetto è il nome file completo, escludendo eventuali segmenti di percorso. Il nome file può includere punti e una possibile estensione. Non sono consentiti criteri o caratteri jolly. Per la corrispondenza dei nomi file non viene fatta distinzione tra maiuscole e minuscole.", + "schema.fileName": "ID della definizione di icona per l'associazione.", + "schema.languageIds": "Associa i linguaggi alle icone. La chiave dell'oggetto è l'ID linguaggio definito nel punto di aggiunta contributo del linguaggio.", + "schema.languageId": "ID della definizione di icona per l'associazione.", + "schema.fonts": "Tipi di carattere usati nelle definizioni di icona.", + "schema.id": "ID del tipo di carattere.", + "schema.src": "Posizioni del tipo di carattere.", + "schema.font-path": "Percorso del tipo di carattere, relativo al file del tema dell'icona corrente.", + "schema.font-format": "Formato del tipo di carattere.", + "schema.font-weight": "Spessore del carattere.", + "schema.font-sstyle": "Stile del tipo di carattere.", + "schema.font-size": "Dimensioni predefinite del tipo di carattere.", + "schema.iconDefinitions": "Descrizione di tutte le icone utilizzabili quando si associano file a icone.", + "schema.iconDefinition": "Definizione di icona. La chiave dell'oggetto è l'ID della definizione.", + "schema.iconPath": "Quando si usa un file SVG o PNG: percorso dell'immagine. Il percorso è relativo al file impostato dell'icona.", + "schema.fontCharacter": "Quando si usa un tipo di carattere glifo: carattere nel tipo di carattere da usare.", + "schema.fontColor": "Quando si usa un tipo di carattere glifo: colore da usare.", + "schema.fontSize": "Quando si usa un tipo di carattere: dimensioni del carattere in percentuale rispetto al tipo di carattere del testo. Se non è impostato, per impostazione predefinita vengono usate le dimensioni della definizione del tipo di carattere.", + "schema.fontId": "Quando si usa un tipo di carattere: ID del tipo di carattere. Se non è impostato, per impostazione predefinita viene usata la prima definizione del tipo di carattere.", + "schema.light": "Associazioni facoltative per le icone di file in temi colore chiari.", + "schema.highContrast": "Associazioni facoltative per le icone di file in temi colore a contrasto elevato." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/ita/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..8cef1beddb --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "Problemi durante l'analisi del file di tema di JSON: {0}", + "error.invalidformat.colors": "Si è verificato un problema durante l'analisi del file di tema {0}. La proprietà 'colors' non è di tipo 'object'.", + "error.invalidformat.tokenColors": "Si è verificato un problema durante l'analisi del file del tema colori {0}. La proprietà 'tokenColors' deve essere una matrice che specifica colori oppure un percorso di un file di tema TextMate", + "error.plist.invalidformat": "Si è verificato un problema durante l'analisi del file tmTheme {0}. 'settings' non è una matrice.", + "error.cannotparse": "Si sono verificati problemi durante l'analisi del file tmTheme {0}", + "error.cannotload": "Si sono verificati problemi durante il caricamento del file tmTheme {0}: {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/ita/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..199a3f2f54 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "ID del tema dell'icona usato nelle impostazioni utente.", + "vscode.extension.contributes.themes.label": "Etichetta del tema colori visualizzata nell'interfaccia utente.", + "vscode.extension.contributes.themes.uiTheme": "Tema di base che definisce i colori nell'editor: 'vs' è il tema colori chiaro, mentre 'vs-dark' è il tema colori scuro e 'hc-black' è il tema a contrasto elevato scuro.", + "vscode.extension.contributes.themes.path": "Percorso del file tmTheme. È relativo alla cartella delle estensioni e corrisponde in genere a './themes/themeFile.tmTheme'.", + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "ID del tema dell'icona usato nelle impostazioni utente.", + "vscode.extension.contributes.iconThemes.label": "Etichetta del tema dell'icona visualizzata nell'interfaccia utente.", + "vscode.extension.contributes.iconThemes.path": "Percorso del file di definizione del tema dell'icona. È relativo alla cartella delle estensioni e corrisponde in genere a './icons/awesome-icon-theme.json'.", + "migration.completed": "Sono state aggiunte nuove impostazioni tema alle impostazioni utente. Backup disponibile in {0}.", + "error.cannotloadtheme": "Unable to load {0}: {1}", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "È previsto un valore stringa in `contributes.{0}.path`. Valore specificato: {1}", + "invalid.path.1": "Valore previsto di `contributes.{0}.path` ({1}) da includere nella cartella dell'estensione ({2}). L'estensione potrebbe non essere più portatile.", + "reqid": "È previsto un valore stringa in `contributes.{0}.id`. Valore specificato: {1}", + "error.cannotloadicontheme": "Unable to load {0}", + "error.cannotparseicontheme": "Problems parsing file icons file: {0}", + "colorTheme": "Specifies the color theme used in the workbench.", + "colorThemeError": "Theme is unknown or not installed.", + "iconTheme": "Specifica il tema dell'icona usato nell'area di lavoro oppure 'null' se non viene visualizzato alcun icona di file.", + "noIconThemeDesc": "No file icons", + "iconThemeError": "File icon theme is unknown or not installed.", + "workbenchColors": "Sostituisce i colori del tema colori attualmente selezionato.", + "workbenchColors.deprecated": "L'impostazione non è più sperimentale ed è stata rinominata in 'workbench.colorCustomizations'", + "workbenchColors.deprecatedDescription": "In alternativa, usare 'workbench.colorCustomizations'", + "editorColors": "Sostituisce i colori dell'editor e lo stile dei font nel tema colori attualmente selezionato.", + "editorColors.comments": "Imposta i colori e gli stili per i commenti", + "editorColors.strings": "Imposta i colori e gli stili per i valori letterali stringa.", + "editorColors.keywords": "Imposta i colori e gli stili per le parole chiave.", + "editorColors.numbers": "Imposta i colori e stili per i valori letterali numerici.", + "editorColors.types": "Imposta i colori e gli stili per i riferimenti e le dichiarazioni di tipo.", + "editorColors.functions": "Imposta i colori e gli stili per i riferimenti e le dichiarazioni di funzioni.", + "editorColors.variables": "Imposta i colori e gli stili per i riferimenti e le dichiarazioni di variabili.", + "editorColors.textMateRules": "Imposta i colori e gli stili usando le regole di creazione temi di TextMate (impostazione avanzata)." +} \ No newline at end of file diff --git a/i18n/jpn/extensions/configuration-editing/out/extension.i18n.json b/i18n/jpn/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..e19e81f0c5 --- /dev/null +++ b/i18n/jpn/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "例" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/jpn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..ec9dae9a3b --- /dev/null +++ b/i18n/jpn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "例: myFile.txt", + "activeEditorMedium": "例: myFolder/myFile.txt", + "activeEditorLong": "例: /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "例: myFolder1, myFolder2, myFolder3", + "rootPath": "例: /Users/Development/myProject", + "folderName": "例: myFolder", + "folderPath": "例: /Users/Development/myFolder", + "appName": "例: VS Code", + "dirty": "アクティブなエディターがダーティである場合のダーティ インジケーター", + "separator": "値のある変数で囲まれた場合にのみ表示される条件付き区切り記号 (' - ')", + "assocLabelFile": "当該拡張子のファイル", + "assocDescriptionFile": "ファイル名が glob パターンに一致するすべてのファイルを、指定された識別子の言語にマップします。", + "assocLabelPath": "当該パスのファイル", + "assocDescriptionPath": "絶対パスの glob パターンがパスに一致するすべてのファイルを、指定した識別子の言語にマップします。", + "fileLabel": "特定の拡張子のファイル", + "fileDescription": "特定のファイル拡張子を持つすべてのファイルと一致します。", + "filesLabel": "複数の拡張子のファイル", + "filesDescription": "いずれかのファイル拡張子を持つすべてのファイルと一致します。", + "derivedLabel": "同じ名前の兄弟があるファイル", + "derivedDescription": "名前が同じで拡張子が異なる兄弟を持つファイルと一致します。", + "topFolderLabel": "特定の名前のフォルダー (最上位)", + "topFolderDescription": "特定の名前の最上位にあるフォルダーと一致します。", + "topFoldersLabel": "複数の名前のフォルダー (最上位)", + "topFoldersDescription": "複数の最上位フォルダーと一致します。", + "folderLabel": "特定の名前のフォルダー (任意の場所)", + "folderDescription": "任意の場所にある特定の名前のフォルダーと一致します。", + "falseDescription": "パターンを無効にします。", + "trueDescription": "パターンを有効にします。", + "siblingsDescription": "名前が同じで拡張子が異なる兄弟を持つファイルと一致します。", + "languageSpecificEditorSettings": "言語固有のエディター設定", + "languageSpecificEditorSettingsDescription": "言語に対するエディター設定を上書きします" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/css/client/out/cssMain.i18n.json b/i18n/jpn/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..06d7939a73 --- /dev/null +++ b/i18n/jpn/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "CSS 言語サーバー" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/css/package.i18n.json b/i18n/jpn/extensions/css/package.i18n.json new file mode 100644 index 0000000000..c7b2205ecf --- /dev/null +++ b/i18n/jpn/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "正しくないパラメーターの数", + "css.lint.boxModel.desc": "パディングまたは枠線を使用する場合は幅または高さを使用しないでください", + "css.lint.compatibleVendorPrefixes.desc": "ベンダー固有のプレフィックスを使用する場合は、他のすべてのベンダー固有のプロパティも必ず含めてください", + "css.lint.duplicateProperties.desc": "重複するスタイル定義を使用しないでください", + "css.lint.emptyRules.desc": "空の規則セットを使用しないでください", + "css.lint.float.desc": "'float' は使用しないでください。float を使用すると、レイアウトの一部が変更されたときに CSS が破損しやすくなります。", + "css.lint.fontFaceProperties.desc": "@font-face 規則で 'src' プロパティと 'font-family' プロパティを定義する必要があります", + "css.lint.hexColorLength.desc": "16 進数の色には、3 つまたは 6 つの 16 進数が含まれる必要があります", + "css.lint.idSelector.desc": "セレクターには ID を含めないでください。これらの規則と HTML の結合が密接すぎます。", + "css.lint.ieHack.desc": "IE ハックは、IE7 以前をサポートする場合にのみ必要です", + "css.lint.important.desc": "!important は使用しないでください。これは CSS 全体の特定性が制御不能になり、リファクタリングが必要なことを示しています。", + "css.lint.importStatement.desc": "複数の Import ステートメントを同時に読み込むことはできません", + "css.lint.propertyIgnoredDueToDisplay.desc": "表示によりプロパティが無視されます。たとえば、'display: inline' の場合、width、height、margin-top、margin-bottom、および float のプロパティには効果がありません", + "css.lint.universalSelector.desc": "ユニバーサル セレクター (*) を使用すると処理速度が低下することが分かっています", + "css.lint.unknownProperties.desc": "不明なプロパティ。", + "css.lint.unknownVendorSpecificProperties.desc": "不明なベンダー固有のプロパティ。", + "css.lint.vendorPrefix.desc": "ベンダー固有のプレフィックスを使用する場合は、標準のプロパティも含めます", + "css.lint.zeroUnits.desc": "0 の単位は必要ありません", + "css.trace.server.desc": "VS Code と CSS 言語サーバー間の通信をトレースします。", + "css.validate.title": "CSS の検証と問題の重大度を制御します。", + "css.validate.desc": "すべての検証を有効または無効にします", + "less.lint.argumentsInColorFunction.desc": "正しくないパラメーターの数", + "less.lint.boxModel.desc": "パディングまたは枠線を使用する場合は幅または高さを使用しないでください", + "less.lint.compatibleVendorPrefixes.desc": "ベンダー固有のプレフィックスを使用する場合は、他のすべてのベンダー固有のプロパティも必ず含めてください", + "less.lint.duplicateProperties.desc": "重複するスタイル定義を使用しないでください", + "less.lint.emptyRules.desc": "空の規則セットを使用しないでください", + "less.lint.float.desc": "'float' は使用しないでください。float を使用すると、レイアウトの一部が変更されたときに CSS が破損しやすくなります。", + "less.lint.fontFaceProperties.desc": "@font-face 規則で 'src' プロパティと 'font-family' プロパティを定義する必要があります", + "less.lint.hexColorLength.desc": "16 進数の色には、3 つまたは 6 つの 16 進数が含まれる必要があります", + "less.lint.idSelector.desc": "セレクターには ID を含めないでください。これらの規則と HTML の結合が密接すぎます。", + "less.lint.ieHack.desc": "IE ハックは、IE7 以前をサポートする場合にのみ必要です", + "less.lint.important.desc": "!important は使用しないでください。これは CSS 全体の特定性が制御不能になり、リファクタリングが必要なことを示しています。", + "less.lint.importStatement.desc": "複数の Import ステートメントを同時に読み込むことはできません", + "less.lint.propertyIgnoredDueToDisplay.desc": "表示によりプロパティが無視されます。たとえば、'display: inline' の場合、width、height、margin-top、margin-bottom、および float のプロパティには効果がありません", + "less.lint.universalSelector.desc": "ユニバーサル セレクター (*) を使用すると処理速度が低下することが分かっています", + "less.lint.unknownProperties.desc": "不明なプロパティ。", + "less.lint.unknownVendorSpecificProperties.desc": "不明なベンダー固有のプロパティ。", + "less.lint.vendorPrefix.desc": "ベンダー固有のプレフィックスを使用する場合は、標準のプロパティも含めます", + "less.lint.zeroUnits.desc": "0 の単位は必要ありません", + "less.validate.title": "LESS の検証と問題の重大度を制御します。", + "less.validate.desc": "すべての検証を有効または無効にします", + "scss.lint.argumentsInColorFunction.desc": "正しくないパラメーターの数", + "scss.lint.boxModel.desc": "パディングまたは枠線を使用する場合は幅または高さを使用しないでください", + "scss.lint.compatibleVendorPrefixes.desc": "ベンダー固有のプレフィックスを使用する場合は、他のすべてのベンダー固有のプロパティも必ず含めてください", + "scss.lint.duplicateProperties.desc": "重複するスタイル定義を使用しないでください", + "scss.lint.emptyRules.desc": "空の規則セットを使用しないでください", + "scss.lint.float.desc": "'float' は使用しないでください。float を使用すると、レイアウトの一部が変更されたときに CSS が破損しやすくなります。", + "scss.lint.fontFaceProperties.desc": "@font-face 規則で 'src' プロパティと 'font-family' プロパティを定義する必要があります", + "scss.lint.hexColorLength.desc": "16 進数の色には、3 つまたは 6 つの 16 進数が含まれる必要があります", + "scss.lint.idSelector.desc": "セレクターには ID を含めないでください。これらの規則と HTML の結合が密接すぎます。", + "scss.lint.ieHack.desc": "IE ハックは、IE7 以前をサポートする場合にのみ必要です", + "scss.lint.important.desc": "!important は使用しないでください。これは CSS 全体の特定性が制御不能になり、リファクタリングが必要なことを示しています。", + "scss.lint.importStatement.desc": "複数の Import ステートメントを同時に読み込むことはできません", + "scss.lint.propertyIgnoredDueToDisplay.desc": "表示によりプロパティが無視されます。たとえば、'display: inline' の場合、width、height、margin-top、margin-bottom、および float のプロパティには効果がありません", + "scss.lint.universalSelector.desc": "ユニバーサル セレクター (*) を使用すると処理速度が低下することが分かっています", + "scss.lint.unknownProperties.desc": "不明なプロパティ。", + "scss.lint.unknownVendorSpecificProperties.desc": "不明なベンダー固有のプロパティ。", + "scss.lint.vendorPrefix.desc": "ベンダー固有のプレフィックスを使用する場合は、標準のプロパティも含めます", + "scss.lint.zeroUnits.desc": "0 の単位は必要ありません", + "scss.validate.title": "SCSS の検証と問題の重大度を制御します。", + "scss.validate.desc": "すべての検証を有効または無効にします", + "less.colorDecorators.enable.desc": "カラー デコレーターを有効または無効にします", + "scss.colorDecorators.enable.desc": "カラー デコレーターを有効または無効にします", + "css.colorDecorators.enable.desc": "カラー デコレーターを有効または無効にします", + "css.colorDecorators.enable.deprecationMessage": "設定 `css.colorDecorators.enable` は使用されなくなりました。`editor.colorDecorators` を使用してください。", + "scss.colorDecorators.enable.deprecationMessage": "設定 `scss.colorDecorators.enable` は使用されなくなりました。`editor.colorDecorators` を使用してください。", + "less.colorDecorators.enable.deprecationMessage": "設定 `less.colorDecorators.enable` は使用されなくなりました。`editor.colorDecorators` を使用してください。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/emmet/package.i18n.json b/i18n/jpn/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..9b2b2053f4 --- /dev/null +++ b/i18n/jpn/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "ラップ変換", + "command.wrapIndividualLinesWithAbbreviation": "個々の行でラップ変換", + "command.removeTag": "タグの削除", + "command.updateTag": "タグの更新", + "command.matchTag": "一致するペアに移動", + "command.balanceIn": "バランス (内側)", + "command.balanceOut": "バランス (外側)", + "command.prevEditPoint": "前の編集点に移動", + "command.nextEditPoint": "次の編集点に移動", + "command.mergeLines": "行のマージ", + "command.selectPrevItem": "前の項目を選択", + "command.selectNextItem": "次の項目を選択", + "command.splitJoinTag": "タグの分割/結合", + "command.toggleComment": "コメントの切り替え", + "command.evaluateMathExpression": "数式の評価", + "command.updateImageSize": "イメージ サイズの更新", + "command.reflectCSSValue": "CSS 値を反映", + "command.incrementNumberByOne": "1 ずつ増加", + "command.decrementNumberByOne": "1 ずつ減少", + "command.incrementNumberByOneTenth": "0.1 ずつ増加", + "command.decrementNumberByOneTenth": "0.1 ずつ減少", + "command.incrementNumberByTen": "10 ずつ増加", + "command.decrementNumberByTen": "10 ずつ減少", + "emmetSyntaxProfiles": "指定した構文に対してプロファイルを定義するか、特定の規則がある独自のプロファイルをご使用ください。", + "emmetExclude": "Emmet 省略記法を展開すべきでない言語の配列。", + "emmetExtensionsPath": "Emmet のプロファイルとスニペットを含むフォルダーへのパス。", + "emmetShowExpandedAbbreviation": "候補として、展開された Emmet 省略記法を表示します。\n\"inMarkupAndStylesheetFilesOnly\" オプションは、html、haml、jade、slim、xml、xsl、css、scss、sass、less、および stylus に適用されます。\n\"always\" オプションは、マークアップ / css に関係なく、ファイルのすべての部分に適用されます。", + "emmetShowAbbreviationSuggestions": "候補として考えられる Emmet 省略記法を表示します。 スタイルシートや emmet.showExpandedAbbreviation が \"never\" に設定されている場合は適用されません。", + "emmetIncludeLanguages": "既定でサポートされていない言語で Emmet 省略記法を有効にします。 言語と Emmet がサポートする言語の間にマッピングを追加します。\n例: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "Emmet のスニペットで使用される変数 ", + "emmetTriggerExpansionOnTab": "これをオンにすると、TAB キーを押したときに emmet 省略記法が展開されます.", + "emmetPreferences": "Emmet の一部のアクションやリゾルバーの動作の変更に使用される基本設定。", + "emmetPreferencesIntUnit": "整数値に使用する既定の単位", + "emmetPreferencesFloatUnit": "float 値に使用する既定の単位", + "emmetPreferencesCssAfter": "CSS の略語を展開するときに CSS プロパティの末尾に配置されるシンボル", + "emmetPreferencesSassAfter": "Sass ファイルで CSS 略語を展開するときに CSS プロパティの末尾に配置されるシンボル", + "emmetPreferencesStylusAfter": "Stylus ファイルで CSS 略語を展開するときに CSS プロパティの末尾に配置されるシンボル", + "emmetPreferencesCssBetween": "CSS の略語を展開するときに CSS プロパティと値の間に配置されるシンボル ", + "emmetPreferencesSassBetween": "Sass ファイルで CSS の略語を展開するときに CSS プロパティと値の間に配置されるシンボル ", + "emmetPreferencesStylusBetween": "Stylus ファイルで CSS の略語を展開するときに CSS プロパティと値の間に配置されるシンボル ", + "emmetShowSuggestionsAsSnippets": "true の場合、emmet 候補では、editor.snippetSuggestions 設定に従ってそれらを並べてスニペットとして表示されます。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/jpn/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..2117d5bf62 --- /dev/null +++ b/i18n/jpn/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "画像には HTTPS プロトコルを使用する必要があります。", + "svgsNotValid": "SVG は無効な画像のソースです。", + "embeddedSvgsNotValid": "埋め込み SVG は無効な画像のソースです。", + "dataUrlsNotValid": "Data URL は無効な画像のソースです。", + "relativeUrlRequiresHttpsRepository": "相対的な画像 URL では、HTTPS プロトコルのリポジトリが package.json で指定されている必要があります。", + "relativeIconUrlRequiresHttpsRepository": "アイコンは、HTTPS プロトコルのリポジトリがこの package.json で指定されている必要があります。 ", + "relativeBadgeUrlRequiresHttpsRepository": "相対的なバッジ URL では、HTTPS プロトコルのリポジトリが package.json で指定されている必要があります。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/jpn/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..2710c77493 --- /dev/null +++ b/i18n/jpn/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "言語固有のエディター設定", + "languageSpecificEditorSettingsDescription": "言語に対するエディター設定を上書きします" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/git/out/askpass-main.i18n.json b/i18n/jpn/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..3a847aefd6 --- /dev/null +++ b/i18n/jpn/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "資格情報が見つからないか、無効です。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/git/out/commands.i18n.json b/i18n/jpn/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..46caf83017 --- /dev/null +++ b/i18n/jpn/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "{0} のタグ", + "remote branch at": "{0} でのリモート ブランチ", + "create branch": "$(plus) 新しいブランチを作成", + "repourl": "リポジトリ URL", + "parent": "親ディレクトリ", + "cloning": "Git リポジトリを複製しています...", + "openrepo": "リポジトリを開く", + "proposeopen": "複製したリポジトリを開きますか?", + "path to init": "フォルダーのパス", + "provide path": "初期化する Git リポジトリへのフォルダー パスを入力してください", + "HEAD not available": "'{0}' の HEAD バージョンは利用できません。", + "confirm stage files with merge conflicts": "マージの競合がある {0} 個のファイルをステージしてもよろしいですか?", + "confirm stage file with merge conflicts": "マージの競合がある {0} をステージしてもよろしいですか? ", + "yes": "はい", + "confirm revert": "{0} で選択した変更を元に戻しますか?", + "revert": "変更を元に戻す", + "discard": "変更を破棄", + "confirm delete": "本当に {0} を削除してもよろしいですか?", + "delete file": "ファイルを削除", + "confirm discard": "{0} の変更を破棄しますか?", + "confirm discard multiple": "{0} 個のファイルの変更内容を破棄しますか?", + "warn untracked": "未追跡ファイル {0} が削除されます!", + "confirm discard all single": "{0} の変更を破棄しますか?", + "confirm discard all": "{0} 個のファイルのすべての変更を破棄してもよろしいですか?\nこの変更は元に戻すことができません!\n現在のワーキング セットは永久に失われます。", + "discardAll multiple": "1 つのファイルを破棄", + "discardAll": "{0} 個のファイルをすべて破棄", + "confirm delete multiple": "本当に {0} 個のファイルを削除してもよろしいですか?", + "delete files": "複数のファイルを削除", + "there are untracked files single": "破棄すると次の未追跡ファイルがディスクから削除されます: {0}。", + "there are untracked files": "破棄すると {0} 個の未追跡ファイルがディスクから削除されます。", + "confirm discard all 2": "{0}\n\nこの変更は元に戻すことはできません、現在のワーキング セットは永久に失われます。", + "yes discard tracked": "1 つの追跡ファイルを破棄", + "yes discard tracked multiple": "{0} 個の追跡ファイルを破棄", + "no staged changes": "コミットするステージされた変更がありません。\n\nすべての変更を自動的にステージして、直接コミットしますか?", + "always": "常に行う", + "no changes": "コミットする必要のある変更はありません。", + "commit message": "コミット メッセージ", + "provide commit message": "コミット メッセージを入力してください", + "select a ref to checkout": "チェックアウトする参照を選択", + "branch name": "ブランチ名", + "provide branch name": "ブランチ名を指定してください", + "select branch to delete": "削除するブランチの選択", + "confirm force delete branch": "ブランチ '{0}' はマージされていません。それでも削除しますか?", + "delete branch": "ブランチの削除", + "select a branch to merge from": "マージ元のブランチを選択", + "merge conflicts": "マージの競合があります。コミットする前にこれを解決してください。", + "tag name": "タグ名", + "provide tag name": "タグ名を入力してください。", + "tag message": "メッセージ", + "provide tag message": "注釈付きタグにつけるメッセージを入力してください", + "no remotes to pull": "リポジトリには、プル元として構成されているリモートがありません。", + "pick remote pull repo": "リモートを選んで、ブランチを次からプルします:", + "no remotes to push": "リポジトリには、プッシュ先として構成されているリモートがありません。", + "push with tags success": "タグが正常にプッシュされました。", + "nobranch": "リモートにプッシュするブランチをチェックアウトしてください。", + "pick remote": "リモートを選んで、ブランチ '{0}' を次に公開します:", + "sync is unpredictable": "このアクションはコミットを '{0}' との間でプッシュしたりプルしたりします。", + "ok": "OK", + "never again": "OK、今後は表示しない", + "no remotes to publish": "リポジトリには、発行先として構成されているリモートがありません。", + "no changes stash": "スタッシュする変更がありません。", + "provide stash message": "必要に応じてスタッシュ メッセージを入力してください", + "stash message": "スタッシュ メッセージ", + "no stashes": "復元するスタッシュがありません。", + "pick stash to pop": "適用するスタッシュを選択してください", + "clean repo": "チェックアウトの前に、リポジトリの作業ツリーを消去してください。", + "cant push": "参照仕様をリモートにプッシュできません。最初に 'Pull' を実行して変更を統合してください。", + "git error details": "Git: {0}", + "git error": "Git エラー", + "open git log": "Git ログを開く" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/git/out/main.i18n.json b/i18n/jpn/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..6a58a0b5a8 --- /dev/null +++ b/i18n/jpn/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "{1} から Git {0} を使用しています", + "updateGit": "Git の更新", + "neverShowAgain": "今後は表示しない", + "git20": "git {0} がインストールされているようです。Code は Git 2 以上で最適に動作します" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/git/out/model.i18n.json b/i18n/jpn/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..c37e36e758 --- /dev/null +++ b/i18n/jpn/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "利用可能なリポジトリがありません", + "pick repo": "リポジトリの選択" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/git/out/repository.i18n.json b/i18n/jpn/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..2bb3619dbc --- /dev/null +++ b/i18n/jpn/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "開く", + "index modified": "インデックスの変更", + "modified": "変更済み", + "index added": "インデックスの追加", + "index deleted": "インデックスの削除", + "deleted": "削除済み", + "index renamed": "インデックスの名前変更", + "index copied": "インデックスをコピー", + "untracked": "追跡対象外", + "ignored": "無視", + "both deleted": "双方とも削除", + "added by us": "こちら側による追加", + "deleted by them": "あちら側による削除", + "added by them": "あちら側による追加", + "deleted by us": "こちら側による削除", + "both added": "双方とも追加", + "both modified": "双方とも変更", + "commit": "コミット", + "merge changes": "変更のマージ", + "staged changes": "ステージング済みの変更", + "changes": "変更", + "ok": "OK", + "neveragain": "今後は表示しない", + "huge": "'{0}' のGit リポジトリにアクティブな変更が多いため、 Git 機能の一部のみが有効になります。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/git/out/scmProvider.i18n.json b/i18n/jpn/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..dcfade10b7 --- /dev/null +++ b/i18n/jpn/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "コミット" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/git/out/statusbar.i18n.json b/i18n/jpn/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..235eca63f6 --- /dev/null +++ b/i18n/jpn/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "チェックアウト...", + "sync changes": "変更の同期", + "publish changes": "変更の発行", + "syncing changes": "変更を同期しています..." +} \ No newline at end of file diff --git a/i18n/jpn/extensions/git/package.i18n.json b/i18n/jpn/extensions/git/package.i18n.json new file mode 100644 index 0000000000..d90e714d46 --- /dev/null +++ b/i18n/jpn/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "複製", + "command.init": "リポジトリの初期化", + "command.close": "リポジトリを閉じる", + "command.refresh": "最新の情報に更新", + "command.openChange": "変更を開く", + "command.openFile": "ファイルを開く", + "command.openHEADFile": "HEAD のファイルを開く", + "command.stage": "変更のステージング", + "command.stageAll": "すべての変更のステージング", + "command.stageSelectedRanges": "選択した範囲をステージする", + "command.revertSelectedRanges": "選択範囲を元に戻す", + "command.unstage": "変更のステージング解除", + "command.unstageAll": "すべての変更のステージング解除", + "command.unstageSelectedRanges": "選択した範囲をアンステージする", + "command.clean": "変更を破棄", + "command.cleanAll": "すべての変更を破棄", + "command.commit": "Commit", + "command.commitStaged": "ステージング済みをコミット", + "command.commitStagedSigned": "コミットしてステージング (サインオフ)", + "command.commitStagedAmend": "コミットしてステージング (修正)", + "command.commitAll": "すべてコミット", + "command.commitAllSigned": "すべてコミット (サインオフ)", + "command.commitAllAmend": "すべてコミット (修正)", + "command.undoCommit": "前回のコミットを元に戻す", + "command.checkout": "チェックアウト先...", + "command.branch": "ブランチを作成...", + "command.deleteBranch": "ブランチの削除...", + "command.merge": "ブランチをマージ...", + "command.createTag": "タグを作成", + "command.pull": "プル", + "command.pullRebase": "プル (リベース)", + "command.pullFrom": "指定元からプル...", + "command.push": "プッシュ", + "command.pushTo": "プッシュ先...", + "command.pushWithTags": "タグをつけてプッシュ", + "command.sync": "同期", + "command.publish": "ブランチの発行", + "command.showOutput": "Git 出力の表示", + "command.ignore": "ファイルを .gitignore に追加", + "command.stash": "スタッシュ", + "command.stashPop": "スタッシュを適用して削除...", + "command.stashPopLatest": "最新のスタッシュを適用して削除", + "config.enabled": "Git が有効になっているかどうか", + "config.path": "Git 実行可能ファイルのパス", + "config.autorefresh": "自動更新が有効かどうか", + "config.autofetch": "自動フェッチが有効かどうか", + "config.enableLongCommitWarning": "長いコミット メッセージについて警告するかどうか", + "config.confirmSync": "Git リポジトリを同期する前に確認する", + "config.countBadge": "Git バッジ カウンターを制御します。`all` はすべての変更をカウントします。 `tracked` は追跡している変更のみカウントします。 `off` はカウントをオフします。", + "config.checkoutType": "`Checkout to...` を実行するときに表示されるブランチの種類を制御します。`all` はすべての参照を表示します。`local` はローカル ブランチのみ、`tags` はタグのみ、`remote` はリモート ブランチのみを表示します。 ", + "config.ignoreLegacyWarning": "旧 Git の警告を無視します", + "config.ignoreLimitWarning": "リポジトリ内に変更が多い場合は警告を無視します", + "config.defaultCloneDirectory": "Git リポジトリをクローンする既定の場所", + "config.enableSmartCommit": "ステージされた変更がない場合はすべての変更をコミットします。", + "config.enableCommitSigning": "GPG によりデジタル署名されたコミットを有効にします。", + "config.discardAllScope": "'すべての変更を破棄' コマンドによってどの変更が破棄されるかを制御します。'all' はすべての変更を破棄します。 'tracked' は追跡されているファイルだけを破棄します。 'prompt' は、アクションが実行されるたびにプロンプ​​ト ダイアログを表示します。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/grunt/out/main.i18n.json b/i18n/jpn/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..041a5d0f1a --- /dev/null +++ b/i18n/jpn/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Grunt の自動検出が次のエラーで失敗しました: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/grunt/package.i18n.json b/i18n/jpn/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..5d96a50e3c --- /dev/null +++ b/i18n/jpn/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "Grunt タスクの自動検出をオンにするかオフにするかを制御します。既定はオンです。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/gulp/out/main.i18n.json b/i18n/jpn/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..08827a443d --- /dev/null +++ b/i18n/jpn/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "gulp のエラーによる失敗を自動検出: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/gulp/package.i18n.json b/i18n/jpn/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..76cee27cef --- /dev/null +++ b/i18n/jpn/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "Gulp タスクの自動検出をオンにするかオフにするかを制御します。既定はオンです。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/html/client/out/htmlMain.i18n.json b/i18n/jpn/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..212cd9e889 --- /dev/null +++ b/i18n/jpn/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "HTML 言語サーバー" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/html/package.i18n.json b/i18n/jpn/extensions/html/package.i18n.json new file mode 100644 index 0000000000..7a65628835 --- /dev/null +++ b/i18n/jpn/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "既定の HTML フォーマッタを有効/無効にします", + "html.format.wrapLineLength.desc": "1 行あたりの最大文字数 (0 = 無効にする)。", + "html.format.unformatted.desc": "再フォーマットしてはならないタグの、コンマ区切りの一覧。'null' の場合、既定で https://www.w3.org/TR/html5/dom.html#phrasing-content にリストされているすべてのタグになります。", + "html.format.contentUnformatted.desc": "コンテンツを再フォーマットしてはならないタグをコンマで区切ってリストにします。'null' は、既定値の 'pre' タグを表します。", + "html.format.indentInnerHtml.desc": " セクションと セクションをインデントします。", + "html.format.preserveNewLines.desc": "要素の前にある既存の改行を保持するかどうか。要素の前でのみ機能し、タグの内側やテキストに対しては機能しません。", + "html.format.maxPreserveNewLines.desc": "1 つのチャンク内に保持できる改行の最大数。無制限にするには、'null' を使います。", + "html.format.indentHandlebars.desc": "書式設定とインデント {{#foo}} および {{/foo}}。", + "html.format.endWithNewline.desc": "末尾に改行を入れます。", + "html.format.extraLiners.desc": "直前に改行を 1 つ入れるタグの、コンマで区切られたリストです。'null' は、既定値の \"head, body, /html\" を表します。", + "html.format.wrapAttributes.desc": "属性を折り返します。", + "html.format.wrapAttributes.auto": "行の長さが超過した場合のみ属性を折り返します。", + "html.format.wrapAttributes.force": "先頭以外の各属性を折り返します。", + "html.format.wrapAttributes.forcealign": "先頭以外の各属性を折り返して位置を合わせます。", + "html.format.wrapAttributes.forcemultiline": "各属性を折り返します。", + "html.suggest.angular1.desc": "ビルトイン HTML 言語サポートが Angular V1 のタグおよびプロパティを候補表示するかどうかを構成します。", + "html.suggest.ionic.desc": "ビルトイン HTML 言語サポートが Ionic のタグ、プロパティ、および値を候補表示するかどうかを構成します。", + "html.suggest.html5.desc": "ビルトイン HTML 言語サポートが HTML5 のタグ、プロパティ、および値を候補表示するかどうかを構成します。", + "html.trace.server.desc": "VS Code と HTML 言語サーバー間の通信をトレースします。", + "html.validate.scripts": "ビルトイン HTML 言語サポートが埋め込みスクリプトを検証するかどうかを構成します。", + "html.validate.styles": "ビルトイン HTML 言語サポートが埋め込みスタイルを検証するかどうかを構成します。", + "html.autoClosingTags": "HTML タグの自動クローズを有効/無効にします。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/jake/out/main.i18n.json b/i18n/jpn/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..a63ea2be66 --- /dev/null +++ b/i18n/jpn/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Jake のエラーによる失敗を自動検出: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/jake/package.i18n.json b/i18n/jpn/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..0443bc3f8e --- /dev/null +++ b/i18n/jpn/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "Jake タスクの自動検出をオンにするかオフにするかを制御します。既定はオンです。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/jpn/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..a8edeabf9c --- /dev/null +++ b/i18n/jpn/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "既定の bower.json", + "json.bower.error.repoaccess": "bower リポジトリに対する要求が失敗しました: {0}", + "json.bower.latest.version": "最新" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/jpn/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..ab3a7d7034 --- /dev/null +++ b/i18n/jpn/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "既定の package.json", + "json.npm.error.repoaccess": "NPM リポジトリに対する要求が失敗しました: {0}", + "json.npm.latestversion": "パッケージの現在の最新バージョン", + "json.npm.majorversion": "最新のメジャー バージョンと一致します (1.x.x)", + "json.npm.minorversion": "最新のマイナー バージョンと一致します (1.2.x)", + "json.npm.version.hover": "最新バージョン: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/json/client/out/jsonMain.i18n.json b/i18n/jpn/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..d76bd95881 --- /dev/null +++ b/i18n/jpn/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "JSON 言語サーバー" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/json/package.i18n.json b/i18n/jpn/extensions/json/package.i18n.json new file mode 100644 index 0000000000..e0e71db545 --- /dev/null +++ b/i18n/jpn/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "スキーマを現在のプロジェクトの JSON ファイルに関連付けます", + "json.schemas.url.desc": "スキーマへの URL または現在のディレクトリのスキーマへの相対パス", + "json.schemas.fileMatch.desc": "JSON ファイルをスキーマに解決する場合に一致するファイル パターンの配列です。", + "json.schemas.fileMatch.item.desc": "JSON ファイルをスキーマに解決する場合に一致するよう '*' を含む可能性があるファイル パターンです。", + "json.schemas.schema.desc": "指定された URL のスキーマ定義です。スキーマは、スキーマ URL へのアクセスを避けるためにのみ指定する必要があります。", + "json.format.enable.desc": "既定の JSON フォーマッタを有効/無効にします (再起動が必要です)", + "json.tracing.desc": "VS Code と JSON 言語サーバー間の通信をトレースします。", + "json.colorDecorators.enable.desc": "カラー デコレーターを有効または無効にします", + "json.colorDecorators.enable.deprecationMessage": "設定 `json.colorDecorators.enable` は使用されなくなりました。`editor.colorDecorators` を使用してください。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/markdown/out/extension.i18n.json b/i18n/jpn/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..029d78e6a3 --- /dev/null +++ b/i18n/jpn/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "'markdown.styles' を読み込むことができません: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/jpn/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..bb27135506 --- /dev/null +++ b/i18n/jpn/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "このドキュメントで一部のコンテンツが無効になっています", + "preview.securityMessage.title": "安全でない可能性があるか保護されていないコンテンツは、マークダウン プレビューで無効化されています。保護されていないコンテンツやスクリプトを有効にするには、マークダウン プレビューのセキュリティ設定を変更してください", + "preview.securityMessage.label": "セキュリティが無効なコンテンツの警告" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/markdown/out/security.i18n.json b/i18n/jpn/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..acb1439e8e --- /dev/null +++ b/i18n/jpn/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "高レベル", + "strict.description": "セキュリティで保護されたコンテンツのみを読み込む", + "insecureContent.title": "セキュリティで保護されていないコンテンツを許可する", + "insecureContent.description": "HTTP を介したコンテンツの読み込みを有効にする", + "disable.title": "無効にする", + "disable.description": "すべてのコンテンツとスクリプトの実行を許可します。推奨されません。", + "moreInfo.title": "詳細情報", + "preview.showPreviewSecuritySelector.title": "ワークスペースのマークダウン プレビューに関するセキュリティ設定を選択 " +} \ No newline at end of file diff --git a/i18n/jpn/extensions/markdown/package.i18n.json b/i18n/jpn/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..7d62a3e0e3 --- /dev/null +++ b/i18n/jpn/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "マークダウン プレビューで改行をレンダリングする方法を設定します。'true' に設定すると改行ごとに
を作成します。", + "markdown.preview.linkify": "マークダウン プレビューで URL 形式のテキストからリンクへの変換を有効または無効にします。", + "markdown.preview.doubleClickToSwitchToEditor.desc": "マークダウンのプレビューでダブルクリックすると、エディターに切り替わります。", + "markdown.preview.fontFamily.desc": "マークダウン プレビューで使用されるフォント ファミリを制御します。", + "markdown.preview.fontSize.desc": "マークダウン プレビューで使用されるフォント サイズ (ピクセル単位) を制御します。", + "markdown.preview.lineHeight.desc": "マークダウン プレビューで使用される行の高さを制御します。この数値はフォント サイズを基準とします。", + "markdown.preview.markEditorSelection.desc": "マークダウンのプレビューに、エディターの現在の選択範囲を示すマークが付きます。", + "markdown.preview.scrollEditorWithPreview.desc": "マークダウンのプレビューをスクロールすると、エディターのビューが更新されます。", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "エディターの現在の選択行を示すため、マークダウンのプレビューがスクロールします。", + "markdown.preview.title": "プレビューを開く", + "markdown.previewFrontMatter.dec": "マークダウン プレビューで YAML front matter がレンダリングされる方法を設定します。'hide' の場合、front matter が削除されます。その他の場合には、front matter はマークダウン コンテンツとして処理されます。", + "markdown.previewSide.title": "プレビューを横に表示", + "markdown.showSource.title": "ソースの表示", + "markdown.styles.dec": "マークダウン プレビューから使用する CSS スタイル シートの URL またはローカル パスの一覧。相対パスは、エクスプローラーで開かれているフォルダーへの絶対パスと解釈されます。開かれているフォルダーがない場合、マークダウン ファイルの場所を基準としていると解釈されます。'\\' はすべて '\\\\' と入力する必要があります。", + "markdown.showPreviewSecuritySelector.title": "プレビュー のセキュリティ設定を変更", + "markdown.trace.desc": "マークダウン拡張機能のデバッグログを有効にします。", + "markdown.refreshPreview.title": "プレビューを更新" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/jpn/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..921bb7e549 --- /dev/null +++ b/i18n/jpn/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "現在の変更を取り込む", + "acceptIncomingChange": "入力側の変更を取り込む", + "acceptBothChanges": "両方の変更を取り込む", + "compareChanges": "変更の比較" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/jpn/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..73a3dc3ebe --- /dev/null +++ b/i18n/jpn/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "エディターのカーソルがマージの競合の範囲内にありません", + "compareChangesTitle": "{0}: 現在の変更 ⟷ 入力側の変更", + "cursorOnCommonAncestorsRange": "エディターのカーソルが共通の祖先ブロック内にあります。”現在” または \"入力側\" のいずれかのブロックに移動してください", + "cursorOnSplitterRange": "エディターのカーソルがマージ コンフリクトのスプリッター内にあります。”現在” または \"入力側\" のいずれかのブロックに移動してください", + "noConflicts": "このファイルにマージの競合は存在しません", + "noOtherConflictsInThisFile": "このファイルに他のマージの競合は存在しません" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/jpn/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..72a1c98884 --- /dev/null +++ b/i18n/jpn/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(現在の変更)", + "incomingChange": "(入力側の変更)" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/merge-conflict/package.i18n.json b/i18n/jpn/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..b216fad0e5 --- /dev/null +++ b/i18n/jpn/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "マージの競合", + "command.accept.all-incoming": "入力側のすべてを取り込む", + "command.accept.all-both": "両方をすべて取り込む", + "command.accept.current": "現在の方を取り込む", + "command.accept.incoming": "入力側を取り込む", + "command.accept.selection": "選択項目を取り込む", + "command.accept.both": "両方を取り込む", + "command.next": "次の競合", + "command.previous": "前の競合", + "command.compare": "現在の競合を比較", + "config.title": "マージの競合", + "config.codeLensEnabled": "エディター内のマージ競合ブロックで CodeLens を有効/無効にします", + "config.decoratorsEnabled": "エディター内でマージの競合デコレーターを有効/無効にします。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/npm/package.i18n.json b/i18n/jpn/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..f276035f69 --- /dev/null +++ b/i18n/jpn/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "npm スクリプトの自動検出をオンにするかオフにするかを制御します。既定はオンです。", + "config.npm.runSilent": "`--silent` オプションを使用して npm コマンドを実行" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/php/out/features/validationProvider.i18n.json b/i18n/jpn/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..362befe50d --- /dev/null +++ b/i18n/jpn/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "PHP ファイルを lint するために {0} (ワークスペースの設定として定義されている) を実行することを許可しますか?", + "php.yes": "許可", + "php.no": "許可しない", + "wrongExecutable": "{0} が有効な PHP 実行可能ファイルではないため、検証できません。設定 'php.validate.executablePath' を使用して PHP 実行可能ファイルを構成してください。", + "noExecutable": "PHP 実行可能ファイルが設定されていないため、検証できません。設定 'php.validate.executablePath' を使用して PHP 実行可能ファイルを構成してください。", + "unknownReason": "パス {0} を使用して php を実行できませんでした。理由は不明です。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/php/package.i18n.json b/i18n/jpn/extensions/php/package.i18n.json new file mode 100644 index 0000000000..5cc25ee815 --- /dev/null +++ b/i18n/jpn/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "組み込みの PHP 言語候補機能を有効にするかどうかを設定します。このサポートによって、PHP グローバルと変数の候補が示されます。", + "configuration.validate.enable": "組み込みの PHP 検証を有効/無効にします。", + "configuration.validate.executablePath": "PHP 実行可能ファイルを指します。", + "configuration.validate.run": "リンターを保存時に実行するか、入力時に実行するか。", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "PHP の検証を無効にします (ワークスペース設定として定義)。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/jpn/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..b3399436b9 --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "エディター機能は TypeScript ({1}) を使用しています。TypeScript ({0}) がコンピューターへグローバルにインストールされているため、VS Code のエラーは TSC エラーと異なる場合があります。", + "moreInformation": "詳細情報", + "doNotCheckAgain": "今後確認しない", + "close": "閉じる", + "updateTscCheck": "ユーザー設定 'typescript.check.tscVersion' を false に更新しました" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/jpn/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..e67febbc2e --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "Typings の定義ファイルを取得中...", + "acquiringTypingsDetail": "IntelliSense の Typings の定義ファイルを取得しています。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/jpn/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..9a864c0244 --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "JavaScript ファイルのセマンティック チェックを有効にします。 ファイルの先頭にある必要があります。", + "ts-nocheck": "JavaScript ファイルのセマンティック チェックを無効にします。 ファイルの先頭にある必要があります。", + "ts-ignore": "ファイルの次の行で @ts-check エラーを抑制します。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/jpn/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..291c4dc1cb --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 個の実装", + "manyImplementationLabel": "{0} 個の実装", + "implementationsErrorLabel": "実装を特定できませんでした" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/jpn/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..9ee61f4ecb --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "JSDoc コメント" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/jpn/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..039adfc904 --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 個の参照", + "manyReferenceLabel": "{0} 個の参照", + "referenceErrorLabel": "参照を判別できませんでした" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/jpn/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..5ad88a6968 --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "ウォッチ - {0}", + "buildTscLabel": "ビルド - {0}" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/typescriptMain.i18n.json b/i18n/jpn/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..033a87617c --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "TypeScript または JavaScript プロジェクトを使用するには、VS Code でフォルダーを開いてください", + "typescript.projectConfigUnsupportedFile": "TypeScript または JavaScript のプロジェクトを判別できませんでした。サポートされていないファイルの種類です", + "typescript.projectConfigCouldNotGetInfo": "TypeScript または JavaScript のプロジェクトを判別できませんでした", + "typescript.noTypeScriptProjectConfig": "ファイルは TypeScript プロジェクトの一部ではない", + "typescript.noJavaScriptProjectConfig": "ファイルは JavaScript プロジェクトの一部ではない", + "typescript.configureTsconfigQuickPick": "tsconfig.json を構成する", + "typescript.configureJsconfigQuickPick": "jsconfig.json を構成する", + "typescript.projectConfigLearnMore": "詳細情報" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/jpn/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..fcfae6836f --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "パス {0} は、有効な tsserver インストールを指していません。バンドルされている TypeScript バージョンにフォールバックしています。", + "serverCouldNotBeStarted": "TypeScript 言語サーバーを起動できません。エラー メッセージ: {0}", + "typescript.openTsServerLog.notSupported": "TS サーバーのログには TS 2.2.2 以降が必要です", + "typescript.openTsServerLog.loggingNotEnabled": "TS サーバーのログがオフになっています。ログを有効にするには、`typescript.tsserver.log` を設定して TS サーバーを再起動してください", + "typescript.openTsServerLog.enableAndReloadOption": "ログを有効にして、TS サーバーを再起動する", + "typescript.openTsServerLog.noLogFile": "TS サーバーはログを開始していません。", + "openTsServerLog.openFileFailedFailed": "TS サーバーのログ ファイルを開くことができませんでした", + "serverDiedAfterStart": "TypeScript 言語サービスは、開始直後に 5 回停止しました。サービスは再開されません。", + "serverDiedReportIssue": "問題の報告", + "serverDied": "TypeScript 言語サービスは、直前の 5 分間に 5 回、予期せずに停止しました。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/utils/api.i18n.json b/i18n/jpn/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..c213b4eb23 --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "バージョンが無効です" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/utils/logger.i18n.json b/i18n/jpn/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/jpn/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..d966df5f7b --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "プロジェクト全体の JavaScript/TypeScript 言語機能を有効にするには、多数のファイルが含まれるフォルダーを除外します。例: {0}", + "hintExclude.generic": "プロジェクト全体の JavaScript/TypeScript 言語機能を有効にするには、作業していないソース ファイルが含まれるサイズの大きなフォルダーを除外します。", + "large.label": "除外の構成", + "hintExclude.tooltip": "プロジェクト全体の JavaScript/TypeScript 言語機能を有効にするには、作業していないソース ファイルが含まれるサイズの大きなフォルダーを除外します。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/jpn/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..a12bc3d45c --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "より適した TypeScript IntelliSense に関するデータをフェッチしています", + "typesInstallerInitializationFailed.title": "JavaScript 言語機能のための型定義ファイルをインストールできませんでした。NPM のインストールを確認するか、ユーザー設定で 'typescript.npm' を構成してください", + "typesInstallerInitializationFailed.moreInformation": "詳細情報", + "typesInstallerInitializationFailed.doNotCheckAgain": "今後確認しない", + "typesInstallerInitializationFailed.close": "閉じる" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/jpn/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..0707770b60 --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "VS Code のバージョンを使用", + "useWorkspaceVersionOption": "ワークスペースのバージョンを使用", + "learnMore": "詳細情報", + "selectTsVersion": "JavaScript および TypeScript 言語の機能に使用する TypeScript バージョンを選択します" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/jpn/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..201e104e64 --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "このパスでは TypeScript のバージョンを読み込むことができません", + "noBundledServerFound": "VS Code の tsserver が適切に動作しないウイルス検出ツールなどの他アプリケーションにより削除されました。VS Code を再インストールしてください。" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/package.i18n.json b/i18n/jpn/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..818b121c7f --- /dev/null +++ b/i18n/jpn/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "プロジェクトの再読み込み", + "javascript.reloadProjects.title": "プロジェクトの再読み込み", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "パラメーター シグネチャを含む完全な関数。", + "typescript.tsdk.desc": "使用する tsserver と lib*.d.ts ファイルが含まれているフォルダーのパスを指定します。", + "typescript.disableAutomaticTypeAcquisition": "型定義の自動取得を無効にします。TypeScript 2.0.6 以上が必要です。", + "typescript.tsserver.log": "ファイルへの TS サーバーのログを有効にします。このログは TS サーバーの問題を診断するために使用できます。ログには、プロジェクトのファイルパス、ソースコード、その他の潜在的に機密性の高い情報が含まれている場合があります。", + "typescript.tsserver.trace": "TS サーバーに送信されるメッセージのトレースを有効にします。このトレースは TS サーバーの問題を診断するために使用できます。トレースには、プロジェクトのファイルパス、ソースコード、その他の潜在的に機密性の高い情報が含まれている場合があります。", + "typescript.validate.enable": "TypeScript の検証を有効/無効にします。", + "typescript.format.enable": "既定の TypeScript フォーマッタを有効/無効にします。", + "javascript.format.enable": "既定の JavaScript フォーマッタを有効/無効にします。", + "format.insertSpaceAfterCommaDelimiter": "コンマ区切り記号の後のスペース処理を定義します。", + "format.insertSpaceAfterConstructor": "コンストラクター キーワードの後にスペース処理を定義します。TypeScript が 2.3.0 以上である必要があります。", + "format.insertSpaceAfterSemicolonInForStatements": " for ステートメント内のセミコロンの後のスペース処理を定義します。", + "format.insertSpaceBeforeAndAfterBinaryOperators": "2 項演算子の後のスペース処理を定義します。", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "制御フロー ステートメント内のキーワードの後のスペース処理を定義します。", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "匿名関数の関数キーワードの後のスペース処理を定義します。", + "format.insertSpaceBeforeFunctionParenthesis": "関数の引数のかっこの前にあるスペース処理を定義します。TypeScript が 2.1.5. 以上である必要があります。", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "左右の空でないかっこの間のスペース処理を定義します。", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "左右の空でない角かっこの間のスペース処理を定義します。", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "左右の空でないかっこの間のスペース処理を定義します。TypeScript が 2.3.0 以上である必要があります。", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "テンプレート文字列の始め波かっこの後と終わり波かっこの前のスペース処理を定義します。TypeScript が 2.0.6 以上である必要があります。", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "JSX 式の始め波かっこの後と終わり波かっこの前のスペース処理を定義します。TypeScript が 2.0.6 以上である必要があります。", + "format.insertSpaceAfterTypeAssertion": "TypeScript の型アサーションの後のスペース処理を定義します。TypeScript が 2.4 以上である必要があります。", + "format.placeOpenBraceOnNewLineForFunctions": "新しい行に関数の始め波かっこを配置するかどうかを定義します。", + "format.placeOpenBraceOnNewLineForControlBlocks": "新しい行にコントロール ブロックの始め波かっこを配置するかどうかを定義します。", + "javascript.validate.enable": "JavaScript の検証を有効/無効にします。", + "typescript.goToProjectConfig.title": "プロジェクト構成に移動", + "javascript.goToProjectConfig.title": "プロジェクト構成に移動", + "javascript.referencesCodeLens.enabled": "JavaScript ファイル内で CodeLens の参照を有効/無効にします。", + "typescript.referencesCodeLens.enabled": "TypeScript ファイル内で CodeLens の参照を有効/無効にします。TypeScript 2.0.6 以上が必要です。", + "typescript.implementationsCodeLens.enabled": "CodeLens の実装を有効/無効にします。TypeScript 2.2.0 以上が必要です。", + "typescript.openTsServerLog.title": "TS サーバーのログを開く", + "typescript.restartTsServer": "TS サーバーを再起動する", + "typescript.selectTypeScriptVersion.title": "TypeScript のバージョンの選択", + "jsDocCompletion.enabled": " 自動 JSDoc コメントを有効/無効にします", + "javascript.implicitProjectConfig.checkJs": "JavaScript ファイルのセマンティック チェックを有効/無効にします。既存の jsconfi.json や tsconfi.json ファイルの設定はこれより優先されます。TypeScript は 2.3.1 以上である必要があります。", + "typescript.npm": "型定義の自動取得に使用される NPM 実行可能ファイルへのパスを指定します。TypeScript 2.3.4 以上が必要です。", + "typescript.check.npmIsInstalled": "型定義の自動取得に NPM がインストールされているかどうかを確認します。", + "javascript.nameSuggestions": "JavaScript の候補リスト内でファイルから一意の名前を含むかどうかを有効/無効にします。", + "typescript.tsc.autoDetect": "tsc タスクの自動検出をオンにするかオフにするかを制御します。", + "typescript.problemMatchers.tsc.label": "TypeScript の問題", + "typescript.problemMatchers.tscWatch.label": "TypeScript の問題 (ウォッチ モード)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/jpn/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/jpn/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/jpn/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..b197a17e16 --- /dev/null +++ b/i18n/jpn/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (再発)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/jpn/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..15034f70b9 --- /dev/null +++ b/i18n/jpn/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "入力" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/jpn/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..50273d97ef --- /dev/null +++ b/i18n/jpn/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "大文字と小文字を区別する", + "wordsDescription": "単語単位で検索する", + "regexDescription": "正規表現を使用する" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/jpn/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..9710c0735a --- /dev/null +++ b/i18n/jpn/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "エラー: {0}", + "alertWarningMessage": "警告: {0}", + "alertInfoMessage": "情報: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/jpn/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..bd55698f27 --- /dev/null +++ b/i18n/jpn/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "画像が非常に大きいため、エディターに表示されません。 ", + "resourceOpenExternalButton": "外部のプログラムを使用して画像を開きますか?", + "nativeBinaryError": "このファイルはバイナリか、非常に大きいか、またはサポートされていないテキスト エンコードを使用しているため、エディターに表示されません。", + "sizeB": "{0}B", + "sizeKB": "{0}KB", + "sizeMB": "{0}MB", + "sizeGB": "{0}GB", + "sizeTB": "{0}TB" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/jpn/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..949320e1b6 --- /dev/null +++ b/i18n/jpn/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "その他" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/common/errorMessage.i18n.json b/i18n/jpn/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..7dc107c32c --- /dev/null +++ b/i18n/jpn/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}。エラー コード: {1}", + "error.permission.verbose": "アクセス許可が拒否されました (HTTP {0})", + "error.permission": "アクセス許可が拒否されました", + "error.http.verbose": "{0} (HTTP {1}: {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "不明な接続エラー ({0})", + "error.connection.unknown": "不明な接続エラーが発生しました。インターネット接続が切れたか、接続先のサーバーがオフラインです。", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "不明なエラーが発生しました。ログで詳細を確認してください。", + "nodeExceptionMessage": "システム エラーが発生しました ({0})", + "error.moreErrors": "{0} (合計 {1} エラー)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/common/json.i18n.json b/i18n/jpn/src/vs/base/common/json.i18n.json new file mode 100644 index 0000000000..9da82b7ce2 --- /dev/null +++ b/i18n/jpn/src/vs/base/common/json.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "シンボルが無効です", + "error.invalidNumberFormat": "数値表示形式が無効です", + "error.propertyNameExpected": "プロパティ名が必要です", + "error.valueExpected": "値が必要です", + "error.colonExpected": "コロンが必要です", + "error.commaExpected": "コンマが必要です", + "error.closeBraceExpected": "右中かっこが必要です", + "error.closeBracketExpected": "右角かっこが必要です", + "error.endOfFileExpected": "ファイルの終わりが必要です" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/jpn/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..57e35d7a68 --- /dev/null +++ b/i18n/jpn/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "シンボルが無効です", + "error.invalidNumberFormat": "数値表示形式が無効です", + "error.propertyNameExpected": "プロパティ名が必要です", + "error.valueExpected": "値が必要です", + "error.colonExpected": "コロンが必要です", + "error.commaExpected": "コンマが必要です", + "error.closeBraceExpected": "右中かっこが必要です", + "error.closeBracketExpected": "右角かっこが必要です", + "error.endOfFileExpected": "EOF が必要です" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/common/keybindingLabels.i18n.json b/i18n/jpn/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..f83b6e672a --- /dev/null +++ b/i18n/jpn/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Shift", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Shift", + "altKey.long": "Alt", + "cmdKey.long": "コマンド", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/common/processes.i18n.json b/i18n/jpn/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..c3ed37a70b --- /dev/null +++ b/i18n/jpn/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "エラー: 実行可能ファイルの情報は、型 string のコマンドを定義する必要があります。", + "ExecutableParser.isShellCommand": "警告: isShellCommand は、型ブール値でなければなりません。値 {0} を無視します。", + "ExecutableParser.args": "警告: 引数は、型 string[] でなければなりません。値 {0} を無視します。", + "ExecutableParser.invalidCWD": "警告: options.cwd は、型 string でなければなりません。値 {0} を無視します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/common/severity.i18n.json b/i18n/jpn/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..270d76aed1 --- /dev/null +++ b/i18n/jpn/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "エラー", + "sev.warning": "警告", + "sev.info": "情報" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/node/processes.i18n.json b/i18n/jpn/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..6eb8653c86 --- /dev/null +++ b/i18n/jpn/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "UNC ドライブでシェル コマンドを実行できません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/node/zip.i18n.json b/i18n/jpn/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..4b1d84f020 --- /dev/null +++ b/i18n/jpn/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "zip ファイルの中に {0} が見つかりません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/jpn/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..b74fa3e80d --- /dev/null +++ b/i18n/jpn/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0}、選択", + "quickOpenAriaLabel": "選択" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/jpn/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..343c36f99d --- /dev/null +++ b/i18n/jpn/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "クイック選択。入力すると結果が絞り込まれます。", + "treeAriaLabel": "クイック選択" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/jpn/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..f5d2562b6b --- /dev/null +++ b/i18n/jpn/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "折りたたむ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/code/electron-main/auth.i18n.json b/i18n/jpn/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..c5357a946b --- /dev/null +++ b/i18n/jpn/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "プロキシ認証が必要", + "proxyauth": "{0} プロキシには認証が必要です。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/code/electron-main/menus.i18n.json b/i18n/jpn/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..74e44a941f --- /dev/null +++ b/i18n/jpn/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "ファイル(&&F)", + "mEdit": "編集(&&E)", + "mSelection": "選択(&C)", + "mView": "表示(&&V)", + "mGoto": "移動(&G)", + "mDebug": "デバッグ(&&D)", + "mWindow": "ウィンドウ", + "mHelp": "ヘルプ(&&H)", + "mTask": "タスク(&&T)", + "miNewWindow": "新しいウィンドウ(&&W)", + "mAbout": "{0} のバージョン情報", + "mServices": "サービス", + "mHide": "{0} を非表示にする", + "mHideOthers": "その他を非表示にする", + "mShowAll": "すべて表示", + "miQuit": "{0} を終了", + "miNewFile": "新規ファイル(&&N)", + "miOpen": "開く(&&O)...", + "miOpenWorkspace": "ワークスペースを開く(&&O)...", + "miOpenFolder": "フォルダーを開く(&&F)...", + "miOpenFile": "ファイルを開く(&&O)...", + "miOpenRecent": "最近使用した項目を開く(&&R)", + "miSaveWorkspaceAs": "名前を付けてワークスペースを保存(&&S)...", + "miAddFolderToWorkspace": "ワークスペースにフォルダーを追加(&&A)...", + "miSave": "保存(&&S)", + "miSaveAs": "名前を付けて保存(&&A)...", + "miSaveAll": "すべて保存(&&L)", + "miAutoSave": "自動保存", + "miRevert": "ファイルを元に戻す(&&V)", + "miCloseWindow": "ウィンドウを閉じる(&&E)", + "miCloseWorkspace": "ワークスペースを閉じる(&&W)", + "miCloseFolder": "フォルダーを閉じる(&&F)", + "miCloseEditor": "エディターを閉じる(&&C)", + "miExit": "終了(&E)", + "miOpenSettings": "設定 (&&S)", + "miOpenKeymap": "キーボード ショートカット(&&K)", + "miOpenKeymapExtensions": "キーマップ拡張機能(&&K)", + "miOpenSnippets": "ユーザー スニペット(&&S)", + "miSelectColorTheme": "配色テーマ(&&C)", + "miSelectIconTheme": "ファイル アイコンのテーマ(&&I)", + "miPreferences": "基本設定(&&P)", + "miReopenClosedEditor": "閉じたエディターを再度開く(&&R)", + "miMore": "その他(&&M)...", + "miClearRecentOpen": "最近開いた項目をクリア(&&C)", + "miUndo": "元に戻す(&&U)", + "miRedo": "やり直し(&&R)", + "miCut": "切り取り(&C)", + "miCopy": "コピー (&&C)", + "miPaste": "貼り付け(&&P)", + "miFind": "検索(&&F)", + "miReplace": "置換(&&R)", + "miFindInFiles": "ファイル内を検索(&&I)", + "miReplaceInFiles": "複数のファイルで置換(&&I)", + "miEmmetExpandAbbreviation": "Emmet: 略語の展開(&&X)", + "miShowEmmetCommands": "Emmet(&&M)...", + "miToggleLineComment": "行コメントの切り替え(&&T)", + "miToggleBlockComment": "ブロック コメントの切り替え(&&B)", + "miMultiCursorAlt": "マルチ カーソルを Alt+Click に切り替える", + "miMultiCursorCmd": "マルチ カーソルを Cmd+Click に切り替える", + "miMultiCursorCtrl": "マルチ カーソルを Ctrl+Click に切り替える", + "miInsertCursorAbove": "カーソルを上に挿入(&&A)", + "miInsertCursorBelow": "カーソルを下に挿入(&&D)", + "miInsertCursorAtEndOfEachLineSelected": "カーソルを行末に挿入(&&U)", + "miAddSelectionToNextFindMatch": "次の出現箇所を追加(&&N)", + "miAddSelectionToPreviousFindMatch": "前の出現箇所を追加(&&R)", + "miSelectHighlights": "すべての出現箇所を選択(&&O)", + "miCopyLinesUp": "行を上へコピー(&&C)", + "miCopyLinesDown": "行を下へコピー(&&P)", + "miMoveLinesUp": "行を上へ移動(&&V)", + "miMoveLinesDown": "行を下へ移動(&&L)", + "miSelectAll": "すべて選択(&&S)", + "miSmartSelectGrow": "選択範囲の展開(&&E)", + "miSmartSelectShrink": "選択範囲の縮小(&&S)", + "miViewExplorer": "エクスプローラー(&&E)", + "miViewSearch": "検索(&&S)", + "miViewSCM": "SCM(&&S)", + "miViewDebug": "デバッグ(&&D)", + "miViewExtensions": "拡張機能(&&X)", + "miToggleOutput": "出力(&&O)", + "miToggleDebugConsole": "デバッグ コンソール(&&B)", + "miToggleIntegratedTerminal": "統合ターミナル(&&I)", + "miMarker": "問題(&&P)", + "miAdditionalViews": "その他のビュー(&&V)", + "miCommandPalette": "コマンド パレット(&&C)...", + "miToggleFullScreen": "全画面表示の切り替え(&&F)", + "miToggleZenMode": "Zen Mode の切り替え", + "miToggleMenuBar": "メニュー バーの切り替え(&&B)", + "miSplitEditor": "エディターを分割(&&E)", + "miToggleEditorLayout": "エディター グループ レイアウトの切り替え(&&L)", + "miToggleSidebar": "サイドバーの切り替え(&&T)", + "miMoveSidebarRight": "サイド バーを右へ移動(&&M)", + "miMoveSidebarLeft": "サイド バーを左へ移動(&&M)", + "miTogglePanel": "パネルの切り替え(&&P)", + "miHideStatusbar": "ステータス バーを非表示にする(&&H)", + "miShowStatusbar": "ステータス バーの表示(&&S)", + "miHideActivityBar": "アクティビティ バーを非表示にする(&&A)", + "miShowActivityBar": "アクティビティ バーを表示する(&&A)", + "miToggleWordWrap": "折り返しの切り替え(&&W)", + "miToggleMinimap": "ミニマップの切り替え(&&M)", + "miToggleRenderWhitespace": "空白文字の表示の切り替え(&&R)", + "miToggleRenderControlCharacters": "制御文字の切り替え(&&C)", + "miZoomIn": "拡大(&&Z)", + "miZoomOut": "縮小(&&U)", + "miZoomReset": "ズームのリセット(&&R)", + "miBack": "戻る(&B)", + "miForward": "進む(&&F)", + "miNextEditor": "次のエディター(&&N)", + "miPreviousEditor": "前のエディター(&&P)", + "miNextEditorInGroup": "グループ内の次の使用されているエディター(&&N)", + "miPreviousEditorInGroup": "グループ内の前の使用されているエディター(&&P)", + "miSwitchEditor": "エディターの切り替え(&&E)", + "miFocusFirstGroup": "最初のグループ(&&F)", + "miFocusSecondGroup": "2 番目のグループ(&&S)", + "miFocusThirdGroup": "3 番目のグループ(&&T)", + "miNextGroup": "次のグループ(&&N)", + "miPreviousGroup": "前のグループ(&&P)", + "miSwitchGroup": "グループの切り替え(&&G)", + "miGotoFile": "ファイルに移動(&&F)...", + "miGotoSymbolInFile": "ファイル内のシンボルへ移動(&&S)...", + "miGotoSymbolInWorkspace": "ワークスペース内のシンボルへ移動(&&W)...", + "miGotoDefinition": "定義に移動(&&D)", + "miGotoTypeDefinition": "型定義に移動(&&T)", + "miGotoImplementation": "実装に移動(&&I)", + "miGotoLine": "指定行へ移動(&&L)...", + "miStartDebugging": "デバッグの開始(&&S)", + "miStartWithoutDebugging": "デバッグなしで開始(&&W)", + "miStopDebugging": "デバッグの停止(&&S)", + "miRestart Debugging": "デバッグの再起動(&&R)", + "miOpenConfigurations": "構成を開く(&&C)", + "miAddConfiguration": "構成の追加...", + "miStepOver": "ステップ オーバーする(&&O)", + "miStepInto": "ステップ インする(&&I)", + "miStepOut": "ステップ アウトする(&&U)", + "miContinue": "続行(&&C)", + "miToggleBreakpoint": "ブレークポイントの切り替え(&&B)", + "miConditionalBreakpoint": "条件付きブレークポイント(&&C)...", + "miColumnBreakpoint": "列のブレークポイント(&&O)", + "miFunctionBreakpoint": "関数のブレークポイント(&&F)...", + "miNewBreakpoint": "新しいブレークポイント(&&N)", + "miEnableAllBreakpoints": "すべてのブレークポイントを有効にする", + "miDisableAllBreakpoints": "すべてのブレークポイントを無効にする(&&L)", + "miRemoveAllBreakpoints": "すべてのブレークポイントを削除する(&&R)", + "miInstallAdditionalDebuggers": "その他のデバッガーをインストールします(&&I)...", + "mMinimize": "最小化", + "mZoom": "ズーム", + "mBringToFront": "すべてを前面に配置", + "miSwitchWindow": "ウィンドウの切り替え(&&W)...", + "miToggleDevTools": "開発者ツールの切り替え(&&T)", + "miAccessibilityOptions": "ユーザー補助オプション(&&O)", + "miReportIssues": "問題の報告(&&I)", + "miWelcome": "ようこそ(&&W)", + "miInteractivePlayground": "対話型プレイグラウンド(&&I)", + "miDocumentation": "参照資料(&&D)", + "miReleaseNotes": "リリース ノート(&&R)", + "miKeyboardShortcuts": "キーボード ショートカットの参照(&&K)", + "miIntroductoryVideos": "紹介ビデオ(&&V)", + "miTipsAndTricks": "ヒントとコツ(&&T)", + "miTwitter": "ツイッターに参加(&&J)", + "miUserVoice": "機能要求の検索(&&S)", + "miLicense": "ライセンスの表示(&&L)", + "miPrivacyStatement": "プライバシーについて(&&P)", + "miAbout": "バージョン情報(&&A)", + "miRunTask": "タスクの実行(&&R)...", + "miBuildTask": "ビルド タスクの実行(&&B)...", + "miRunningTask": "実行中のタスクを表示(&&G)...", + "miRestartTask": "実行中のタスクの再起動(&&E)...", + "miTerminateTask": "タスクの終了(&&T)...", + "miConfigureTask": "タスクの構成(&&C)", + "miConfigureBuildTask": "既定のビルド タスクの構成(&&F)", + "accessibilityOptionsWindowTitle": "ユーザー補助オプション", + "miRestartToUpdate": "再起動して更新...", + "miCheckingForUpdates": "更新を確認しています...", + "miDownloadUpdate": "利用可能な更新プログラムをダウンロードします", + "miDownloadingUpdate": "更新をダウンロードしています...", + "miInstallingUpdate": "更新プログラムをインストールしています...", + "miCheckForUpdates": "更新の確認...", + "aboutDetail": "\nバージョン {0}\nコミット {1}\n日付 {2}\nシェル {3}\nレンダラー {4}\nNode {5}\nアーキテクチャ {6}", + "okButton": "OK" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/code/electron-main/window.i18n.json b/i18n/jpn/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..5dfd8b6866 --- /dev/null +++ b/i18n/jpn/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "引き続き **Alt** キーを押してメニュー バーにアクセスできます。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/code/electron-main/windows.i18n.json b/i18n/jpn/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..4e709e00f1 --- /dev/null +++ b/i18n/jpn/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "OK", + "pathNotExistTitle": "パスが存在しません", + "pathNotExistDetail": "パス '{0}' はディスクに存在しなくなったようです。", + "openWorkspace": "開く(&&O)", + "openWorkspaceTitle": "ワークスペースを開く", + "save": "保存(&&S)", + "doNotSave": "保存しない(&&N)", + "cancel": "キャンセル", + "saveWorkspaceMessage": "ワークスペースの構成をファイルとして保存しますか?", + "saveWorkspaceDetail": "再度開く予定があるならワークスペースを保存します。", + "saveWorkspace": "ワークスペースを保存", + "reopen": "もう一度開く", + "wait": "待機を続ける", + "close": "閉じる", + "appStalled": "ウィンドウから応答がありません", + "appStalledDetail": "ウィンドウを再度開くか、閉じるか、このまま待機できます。", + "appCrashed": "ウィンドウがクラッシュしました", + "appCrashedDetail": "ご不便をおかけして申し訳ありません。ウィンドウを再度開いて、中断したところから続行できます。", + "open": "開く", + "openFolder": "フォルダーを開く", + "openFile": "ファイルを開く" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/code/node/cliProcessMain.i18n.json b/i18n/jpn/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..91124217e1 --- /dev/null +++ b/i18n/jpn/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "拡張機能 '{0}' が見つかりませんでした。", + "notInstalled": "拡張機能 '{0}' がインストールされていません。", + "useId": "発行元などの完全な拡張機能 ID を使用していることをご確認ください。例: {0}", + "successVsixInstall": "拡張機能 '{0}' が正常にインストールされました。", + "alreadyInstalled": "拡張機能 '{0}' は既にインストールされています。", + "foundExtension": "マーケットプレースで '{0}' が見つかりました。", + "installing": "インストールしています...", + "successInstall": "拡張機能 '{0}' v{1} が正常にインストールされました!", + "uninstalling": "{0} をアンインストールしています...", + "successUninstall": "拡張機能 '{0}' が正常にアンインストールされました!" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/jpn/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..99076d3aba --- /dev/null +++ b/i18n/jpn/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "一方のファイルが大きすぎるため、ファイルを比較できません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/jpn/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..17427bfa77 --- /dev/null +++ b/i18n/jpn/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "閉じる", + "header": "{1} の差分 {0}: 変更前の {2}、{3} 行、変更後の {4}、{5} 行", + "blankLine": "空白", + "equalLine": "変更前の {0}、変更後の {1}: {2}", + "insertLine": "+ 変更後の {0}: {1}", + "deleteLine": "- 変更前の {0}: {1}", + "editor.action.diffReview.next": "次の差分に移動", + "editor.action.diffReview.prev": "前の差分に移動" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/jpn/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..213947afcc --- /dev/null +++ b/i18n/jpn/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "エディター", + "fontFamily": "フォント ファミリを制御します。", + "fontWeight": "フォントの太さを制御します。", + "fontSize": "フォント サイズをピクセル単位で制御します。", + "lineHeight": "行の高さを制御します。fontSize に基づいて lineHeight を計算する場合には、0 を使用します。", + "letterSpacing": "文字の間隔をピクセル単位で制御します。", + "lineNumbers": "行番号の表示を制御します。使用可能な値は、'on'、'off'、および 'relative' です。'relative' は現在のカーソル位置からの行数を示します。", + "rulers": "垂直ルーラーを表示する列", + "wordSeparators": "単語に関連したナビゲーションまたは操作を実行するときに、単語の区切り文字として使用される文字", + "tabSize": "1 つのタブに相当するスペースの数。`editor.detectIndentation` がオンの場合、この設定はファイル コンテンツに基づいて上書きされます。", + "tabSize.errorMessage": "'number' が必要です。`editor.detectIndentation` 設定によって値 \"auto\" が置き換えられていることに注意してください。", + "insertSpaces": "Tab キーを押すとスペースが挿入されます。`editor.detectIndentation` がオンの場合、この設定はファイル コンテンツに基づいて上書きされます。", + "insertSpaces.errorMessage": "'boolean' が必要です。`editor.detectIndentation` 設定によって値 \"auto\" が置き換えられていることに注意してください。", + "detectIndentation": "ファイルを開くと、そのファイルの内容に基づいて `editor.tabSize` と `editor.insertSpaces` が検出されます。", + "roundedSelection": "選択範囲の角を丸くするかどうかを制御します", + "scrollBeyondLastLine": "エディターで最後の行を越えてスクロールするかどうかを制御します", + "smoothScrolling": "アニメーションでエディターをスクロールするかどうかを制御します", + "minimap.enabled": "ミニマップを表示するかどうかを制御します", + "minimap.showSlider": "ミニマップのスライダーを自動的に非表示にするかどうかを制御します。指定できる値は 'always' と 'mouseover' です", + "minimap.renderCharacters": "行に (カラー ブロックではなく) 実際の文字を表示します", + "minimap.maxColumn": "表示するミニマップの最大幅を特定の桁数に制限します", + "find.seedSearchStringFromSelection": "エディターの選択から検索ウィジェット内の検索文字列を与えるかどうかを制御します", + "find.autoFindInSelection": "エディター内で複数の文字もしくは行が選択されているときに選択範囲を検索するフラグを有効にするかどうかを制御します", + "wordWrap.off": "行を折り返しません。", + "wordWrap.on": "行をビューポートの幅で折り返します。", + "wordWrap.wordWrapColumn": "行を 'editor.wordWrapColumn' で折り返します。", + "wordWrap.bounded": "ビューポートと 'editor.wordWrapColumn' の最小値で行を折り返します。", + "wordWrap": "行の折り返し方法を制御します。次の値を指定できます。\n - 'off' (折り返さない),\n - 'on' (ビューポート折り返し),\n - 'wordWrapColumn' ('editor.wordWrapColumn' で折り返し) or\n - 'bounded' (ビューポートと 'editor.wordWrapColumn' の最小値で折り返し).", + "wordWrapColumn": "'editor.wordWrap' が 'wordWrapColumn' または 'bounded' の場合に、エディターの折り返し桁を制御します。", + "wrappingIndent": "折り返し行のインデントを制御します。'none'、'same'、または 'indent' のいずれかを指定できます。", + "mouseWheelScrollSensitivity": "マウス ホイール スクロール イベントの `deltaX` と `deltaY` で使用される乗数", + "multiCursorModifier.ctrlCmd": "Windows および Linux 上の `Control` と OSX 上の `Command` にマップします。", + "multiCursorModifier.alt": "Windows および Linux 上の `Alt` と OSX 上の `Option` にマップします。", + "multiCursorModifier": "マウスで複数のカーソルを追加するときに使用する修飾キーです。`ctrlCmd` は Windows および Linux 上の `Control` キーと OSX 上の `Command` キーにマップします。「定義に移動」や「リンクを開く」のマウス操作は、マルチカーソルの修飾キーと競合しないように適用されます。", + "quickSuggestions.strings": "文字列内でクイック候補を有効にします。", + "quickSuggestions.comments": "コメント内でクイック候補を有効にします。", + "quickSuggestions.other": "文字列およびコメント外でクイック候補を有効にします。", + "quickSuggestions": "入力中に候補を自動的に表示するかどうかを制御します", + "quickSuggestionsDelay": "クイック候補が表示されるまでの待ち時間 (ミリ秒) を制御します", + "parameterHints": "入力時にパラメーター ドキュメントと型情報を表示するポップアップを有効にする", + "autoClosingBrackets": "エディターで左角かっこの後に自動的に右角かっこを挿入するかどうかを制御します", + "formatOnType": "エディターで入力後に自動的に行の書式設定を行うかどうかを制御します", + "formatOnPaste": "貼り付けた内容がエディターにより自動的にフォーマットされるかどうかを制御します。フォーマッタを使用可能にする必要があります。また、フォーマッタがドキュメント内の範囲をフォーマットできなければなりません。", + "autoIndent": "ユーザーが入力や貼り付け、行の移動をしたとき、エディターがインデントを自動的に調整するかどうかを制御します。言語のインデント ルールを使用できる必要があります。", + "suggestOnTriggerCharacters": "トリガー文字の入力時に候補が自動的に表示されるようにするかどうかを制御します", + "acceptSuggestionOnEnter": "'Tab' キーに加えて 'Enter' キーで候補を受け入れるかどうかを制御します。改行の挿入や候補の反映の間であいまいさを解消するのに役立ちます。'smart' 値は文字を変更するときに、Enter キーを押すだけで提案を反映することを意味します。", + "acceptSuggestionOnCommitCharacter": "コミット文字で候補を受け入れるかどうかを制御します。たとえば、JavaScript ではセミコロン (';') をコミット文字にして、候補を受け入れてその文字を入力することができます。", + "snippetSuggestions.top": "他の候補の上にスニペットの候補を表示します。", + "snippetSuggestions.bottom": "他の候補の下にスニペットの候補を表示します。", + "snippetSuggestions.inline": "他の候補と一緒にスニペットの候補を表示します。", + "snippetSuggestions.none": "スニペットの候補を表示しません。", + "snippetSuggestions": "他の修正候補と一緒にスニペットを表示するかどうか、およびその並び替えの方法を制御します。", + "emptySelectionClipboard": "選択範囲を指定しないでコピーする場合に現在の行をコピーするかどうかを制御します。", + "wordBasedSuggestions": "ドキュメント内の単語に基づいて入力候補を計算するかどうかを制御します。", + "suggestFontSize": "候補のウィジェットのフォント サイズ", + "suggestLineHeight": "候補のウィジェットの行の高さ", + "selectionHighlight": "エディターで選択範囲に類似する一致箇所を強調表示するかどうかを制御します", + "occurrencesHighlight": "エディターでセマンティック シンボルの出現箇所を強調表示するかどうかを制御します", + "overviewRulerLanes": "概要ルーラーの同じ位置に表示できる装飾の数を制御します", + "overviewRulerBorder": "概要ルーラーの周囲に境界線が描画されるかどうかを制御します。", + "cursorBlinking": "カーソルのアニメーション スタイルを制御します。指定できる値は 'blink'、'smooth'、'phase'、'expand'、'solid' です", + "mouseWheelZoom": "Ctrl キーを押しながらマウス ホイールを使用してエディターのフォントをズームします", + "cursorStyle": "カーソルのスタイルを制御します。指定できる値は 'block'、'block-outline'、'line'、'line-thin'、'underline'、'underline-thin' です", + "fontLigatures": "フォントの合字を使用します", + "hideCursorInOverviewRuler": "概要ルーラーでカーソルを非表示にするかどうかを制御します。", + "renderWhitespace": "エディターで空白文字を表示する方法を制御します。'none'、'boundary' および 'all' が使用可能です。'boundary' オプションでは、単語間の単一スペースは表示されません。", + "renderControlCharacters": "エディターで制御文字を表示する必要があるかどうかを制御します", + "renderIndentGuides": "エディターでインデントのガイドを表示する必要があるかどうかを制御します", + "renderLineHighlight": "エディターが現在の行をどのように強調表示するかを制御します。考えられる値は 'none'、'gutter'、'line'、'all' です。", + "codeLens": "エディターで CodeLens を表示するかどうかを制御する", + "folding": "エディターでコードの折りたたみを有効にするかどうかを制御します", + "showFoldingControls": "余白上の折りたたみコントロールを自動的に非表示にするかどうかを制御します 。", + "matchBrackets": "かっこを選択すると、対応するかっこを強調表示します。", + "glyphMargin": "エディターで縦のグリフ余白が表示されるかどうかを制御します。ほとんどの場合、グリフ余白はデバッグに使用されます。", + "useTabStops": "空白の挿入や削除はタブ位置に従って行われます", + "trimAutoWhitespace": "自動挿入された末尾の空白を削除する", + "stablePeek": "エディターのコンテンツをダブルクリックするか、Esc キーを押しても、ピーク エディターを開いたままにします。", + "dragAndDrop": "ドラッグ アンド ドロップによる選択範囲の移動をエディターが許可する必要があるかどうかを制御します。", + "accessibilitySupport.auto": "エディターはスクリーン リーダーがいつ接続されたかを検出するためにプラットフォーム API を使用します。", + "accessibilitySupport.on": "エディターは永続的にスクリーン リーダー向けに最適化されます。", + "accessibilitySupport.off": "エディターはスクリーン リーダー向けに最適化されません。", + "accessibilitySupport": "エディターをスクリーン リーダーに最適化されたモードで実行するかどうかを制御します。", + "links": "エディターがリンクを検出してクリック可能な状態にするかどうかを制御します", + "colorDecorators": "エディターでインライン カラー デコレーターと色の選択を表示する必要があるかどうかを制御します。", + "sideBySide": "差分エディターが差分を横に並べて表示するか、行内に表示するかを制御します", + "ignoreTrimWhitespace": "差分エディターが、先頭または末尾の空白の変更を差分として表示するかどうかを制御します。", + "renderIndicators": "差分エディターが追加/削除された変更に +/- インジケーターを示すかどうかを制御します", + "selectionClipboard": "Linux の PRIMARY クリップボードをサポートするかどうかを制御します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/common/config/defaultConfig.i18n.json b/i18n/jpn/src/vs/editor/common/config/defaultConfig.i18n.json new file mode 100644 index 0000000000..b378f83c17 --- /dev/null +++ b/i18n/jpn/src/vs/editor/common/config/defaultConfig.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorViewAccessibleLabel": "エディターのコンテンツ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/jpn/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..600882a471 --- /dev/null +++ b/i18n/jpn/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "現在エディターにアクセスすることはできません。 Alt + F1 キーを押してオプションを選択します。", + "editorViewAccessibleLabel": "エディターのコンテンツ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/common/controller/cursor.i18n.json b/i18n/jpn/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..b0b0ab3241 --- /dev/null +++ b/i18n/jpn/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "コマンドの実行中に予期しない例外が発生しました。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/jpn/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..28e21d1a9c --- /dev/null +++ b/i18n/jpn/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "入力のトークン化中にモードが失敗しました。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/jpn/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..9072b5c650 --- /dev/null +++ b/i18n/jpn/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "プレーンテキスト" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/jpn/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..ed49ae05f6 --- /dev/null +++ b/i18n/jpn/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "この間に次のファイルが変更されました: {0}", + "summary.0": "編集は行われませんでした", + "summary.nm": "{1} 個のファイルで {0} 件のテキスト編集を実行", + "summary.n0": "1 つのファイルで {0} 個のテキストを編集" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/jpn/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..bd0c9b100b --- /dev/null +++ b/i18n/jpn/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "言語の宣言を提供します。", + "vscode.extension.contributes.languages.id": "言語の ID。", + "vscode.extension.contributes.languages.aliases": "言語の名前のエイリアス。", + "vscode.extension.contributes.languages.extensions": "言語に関連付けられているファイルの拡張子。", + "vscode.extension.contributes.languages.filenames": "言語に関連付けられたファイル名。", + "vscode.extension.contributes.languages.filenamePatterns": "言語に関連付けられたファイル名の glob パターン。", + "vscode.extension.contributes.languages.mimetypes": "言語に関連付けられている MIME の種類。", + "vscode.extension.contributes.languages.firstLine": "言語のファイルの最初の行に一致する正規表現。", + "vscode.extension.contributes.languages.configuration": "言語の構成オプションを含むファイルへの相対パス。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/jpn/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/jpn/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/jpn/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..bc873aef8f --- /dev/null +++ b/i18n/jpn/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "カーソル位置の行を強調表示する背景色。", + "lineHighlightBorderBox": "カーソル位置の行の境界線を強調表示する背景色。", + "rangeHighlight": "Quick Open 機能や検索機能などによって強調表示された範囲の背景色。", + "caret": "エディターのカーソルの色。", + "editorCursorBackground": "選択された文字列の背景色です。選択された文字列の背景色をカスタマイズ出来ます。", + "editorWhitespaces": "エディターのスペース文字の色。", + "editorIndentGuides": "エディター インデント ガイドの色。", + "editorLineNumbers": "エディターの行番号の色。", + "editorRuler": "エディター ルーラーの色。", + "editorCodeLensForeground": "CodeLens エディターの前景色。", + "editorBracketMatchBackground": "一致するかっこの背景色", + "editorBracketMatchBorder": "一致するかっこ内のボックスの色", + "editorOverviewRulerBorder": "概要ルーラーの境界色。", + "editorGutter": "エディターの余白の背景色。余白にはグリフ マージンと行番号が含まれます。", + "errorForeground": "エディターでエラーを示す波線の前景色。", + "errorBorder": "エディターでエラーを示す波線の境界線の色。", + "warningForeground": "エディターで警告を示す波線の前景色。", + "warningBorder": "エディターで警告を示す波線の境界線の色。", + "overviewRulerRangeHighlight": "範囲を強調表示するときの概要ルーラーのマーカー色。", + "overviewRuleError": "エラーを示す概要ルーラーのマーカー色。", + "overviewRuleWarning": "警告を示す概要ルーラーのマーカー色。", + "overviewRuleInfo": "情報を示す概要ルーラーのマーカー色。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json b/i18n/jpn/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json new file mode 100644 index 0000000000..0f25ee80c2 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "introMsg": "VS Code のアクセシビリティ オプションをご利用いただき、ありがとうございます。", + "status": "ステータス:", + "tabFocusModeOnMsg": "現在のエディターで Tab キーを押すと、次のフォーカス可能な要素にフォーカスを移動します。{0} を押すと、この動作が切り替わります。", + "tabFocusModeOnMsgNoKb": "現在のエディターで Tab キーを押すと、次のフォーカス可能な要素にフォーカスを移動します。コマンド {0} は、キー バインドでは現在トリガーできません。", + "tabFocusModeOffMsg": "現在のエディターで Tab キーを押すと、タブ文字が挿入されます。{0} を押すと、この動作が切り替わります。", + "tabFocusModeOffMsgNoKb": "現在のエディターで Tab キーを押すと、タブ文字が挿入されます。コマンド {0} は、キー バインドでは現在トリガーできません。", + "outroMsg": "Esc キーを押すと、ヒントを消してエディターに戻ることができます。", + "ShowAccessibilityHelpAction": "アクセシビリティのヘルプを表示します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/jpn/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..20479477ad --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "ブラケットへ移動" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/jpn/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..d49b0bd9a3 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "キャレットを左に移動", + "caret.moveRight": "キャレットを右に移動" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/jpn/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..165f7c037a --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "文字の入れ替え" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/jpn/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..d5108a2605 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "切り取り", + "actions.clipboard.copyLabel": "コピー", + "actions.clipboard.pasteLabel": "貼り付け", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "構文を強調表示してコピー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/jpn/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..68f745ae64 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "行コメントの切り替え", + "comment.line.add": "行コメントの追加", + "comment.line.remove": "行コメントの削除", + "comment.block": "ブロック コメントの切り替え" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/jpn/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..80d4598e90 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "エディターのコンテキスト メニューの表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/jpn/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..5c696071bd --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "検索", + "placeholder.find": "検索", + "label.previousMatchButton": "前の一致項目", + "label.nextMatchButton": "次の一致項目", + "label.toggleSelectionFind": "選択範囲を検索", + "label.closeButton": "閉じる", + "label.replace": "置換", + "placeholder.replace": "置換", + "label.replaceButton": "置換", + "label.replaceAllButton": "すべて置換", + "label.toggleReplaceButton": "置換モードの切り替え", + "title.matchesCountLimit": "最初の 999 の結果だけを強調表示しますが、テキスト全体を検索します。", + "label.matchesLocation": "{1} の {0}", + "label.noResults": "結果なし" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/jpn/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..b7056be6f3 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "検索", + "placeholder.find": "検索", + "label.previousMatchButton": "前の一致項目", + "label.nextMatchButton": "次の一致項目", + "label.closeButton": "閉じる" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/jpn/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..1d407e9057 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "検索", + "findNextMatchAction": "次を検索", + "findPreviousMatchAction": "前を検索", + "nextSelectionMatchFindAction": "次の選択項目を検索", + "previousSelectionMatchFindAction": "前の選択項目を検索", + "startReplace": "置換", + "addSelectionToNextFindMatch": "選択した項目を次の一致項目に追加", + "addSelectionToPreviousFindMatch": "選んだ項目を前の一致項目に追加する", + "moveSelectionToNextFindMatch": "最後に選択した項目を次の一致項目に移動", + "moveSelectionToPreviousFindMatch": "最後に選んだ項目を前の一致項目に移動する", + "selectAllOccurrencesOfFindMatch": "一致するすべての出現箇所を選択します", + "changeAll.label": "すべての出現箇所を変更", + "showNextFindTermAction": "次の検索語句を表示", + "showPreviousFindTermAction": "前の検索語句を表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/jpn/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..78c918deab --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "展開", + "unFoldRecursivelyAction.label": "再帰的に展開", + "foldAction.label": "折りたたみ", + "foldRecursivelyAction.label": "再帰的に折りたたむ", + "foldAllAction.label": "すべて折りたたみ", + "unfoldAllAction.label": "すべて展開", + "foldLevelAction.label": "折りたたみレベル {0}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/jpn/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..21154e417f --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "行 {0} で 1 つの書式設定を編集", + "hintn1": "行 {1} で {0} 個の書式設定を編集", + "hint1n": "行 {0} と {1} の間で 1 つの書式設定を編集", + "hintnn": "行 {1} と {2} の間で {0} 個の書式設定を編集", + "formatDocument.label": "ドキュメントのフォーマット", + "formatSelection.label": "選択範囲のフォーマット" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json b/i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json new file mode 100644 index 0000000000..4f0c3d7d17 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "'{0}' の定義は見つかりません", + "generic.noResults": "定義が見つかりません", + "meta.title": " – {0} 個の定義", + "actions.goToDecl.label": "定義へ移動", + "actions.goToDeclToSide.label": "定義を横に開く", + "actions.previewDecl.label": "定義をここに表示", + "goToImplementation.noResultWord": "'{0}' の実装が見つかりません", + "goToImplementation.generic.noResults": "実装が見つかりません", + "meta.implementations.title": "– {0} 個の実装", + "actions.goToImplementation.label": "実装に移動", + "actions.peekImplementation.label": "実装のプレビュー", + "goToTypeDefinition.noResultWord": "'{0}' の型定義が見つかりません", + "goToTypeDefinition.generic.noResults": "型定義が見つかりません", + "meta.typeDefinitions.title": " – {0} 個の型定義", + "actions.goToTypeDefinition.label": "型定義へ移動", + "actions.peekTypeDefinition.label": "型定義を表示", + "multipleResults": "クリックして、{0} の定義を表示します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..8454960548 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "'{0}' の定義は見つかりません", + "generic.noResults": "定義が見つかりません", + "meta.title": " – {0} 個の定義", + "actions.goToDecl.label": "定義へ移動", + "actions.goToDeclToSide.label": "定義を横に開く", + "actions.previewDecl.label": "定義をここに表示", + "goToImplementation.noResultWord": "'{0}' の実装が見つかりません", + "goToImplementation.generic.noResults": "実装が見つかりません", + "meta.implementations.title": "– {0} 個の実装", + "actions.goToImplementation.label": "実装に移動", + "actions.peekImplementation.label": "実装のプレビュー", + "goToTypeDefinition.noResultWord": "'{0}' の型定義が見つかりません", + "goToTypeDefinition.generic.noResults": "型定義が見つかりません", + "meta.typeDefinitions.title": " – {0} 個の型定義", + "actions.goToTypeDefinition.label": "型定義へ移動", + "actions.peekTypeDefinition.label": "型定義を表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..6f2d68351a --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "クリックして、{0} の定義を表示します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/jpn/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..e05992b887 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "次のエラーまたは警告へ移動", + "markerAction.previous.label": "前のエラーまたは警告へ移動", + "editorMarkerNavigationError": "エディターのマーカー ナビゲーション ウィジェットのエラーの色。", + "editorMarkerNavigationWarning": "エディターのマーカー ナビゲーション ウィジェットの警告の色。", + "editorMarkerNavigationBackground": "エディターのマーカー ナビゲーション ウィジェットの背景。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/jpn/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..627733df9b --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "ホバーの表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/jpn/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..4f99a33743 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "読み込んでいます..." +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/jpn/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..4cbb486ff3 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "前の値に置換", + "InPlaceReplaceAction.next.label": "次の値に置換" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/jpn/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..742e356eda --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "インデントをスペースに変換", + "indentationToTabs": "インデントをタブに変換", + "configuredTabSize": "構成されたタブのサイズ", + "selectTabWidth": "現在のファイルのタブのサイズを選択", + "indentUsingTabs": "タブによるインデント", + "indentUsingSpaces": "スペースによるインデント", + "detectIndentation": "内容からインデントを検出", + "editor.reindentlines": "行の再インデント" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json b/i18n/jpn/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..d2e3900ea0 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "開発者: TM スコープの検査", + "inspectTMScopesWidget.loading": "読み込んでいます..." +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/jpn/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..6c49740d8a --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "行を上へコピー", + "lines.copyDown": "行を下へコピー", + "lines.moveUp": "行を上へ移動", + "lines.moveDown": "行を下へ移動", + "lines.sortAscending": "行を昇順に並べ替え", + "lines.sortDescending": "行を降順に並べ替え", + "lines.trimTrailingWhitespace": "末尾の空白のトリミング", + "lines.delete": "行の削除", + "lines.indent": "行のインデント", + "lines.outdent": "行のインデント解除", + "lines.insertBefore": "行を上に挿入", + "lines.insertAfter": "行を下に挿入", + "lines.deleteAllLeft": "左側をすべて削除", + "lines.deleteAllRight": "右側をすべて削除", + "lines.joinLines": "行をつなげる", + "editor.transpose": "カーソルの周囲の文字を入れ替える", + "editor.transformToUppercase": "大文字に変換", + "editor.transformToLowercase": "小文字に変換" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/jpn/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..1318f969ba --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "command キーを押しながらクリックしてリンク先を表示", + "links.navigate": "Ctrl キーを押しながらクリックしてリンク先を表示", + "links.command.mac": "command キーを押しながらクリックしてコマンドを実行", + "links.command": "Ctrl キーを押しながらクリックしてコマンドを実行", + "links.navigate.al": "Altl キーを押しながらクリックしてリンク先を表示", + "links.command.al": "Alt キーを押しながらクリックしてコマンドを実行", + "invalid.url": "申し訳ありません。このリンクは形式が正しくないため開くことができませんでした: {0}", + "missing.url": "申し訳ありません。このリンクはターゲットが存在しないため開くことができませんでした。", + "label": "リンクを開く" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/jpn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..2d6c1c29d7 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "カーソルを上に挿入", + "mutlicursor.insertBelow": "カーソルを下に挿入", + "mutlicursor.insertAtEndOfEachLineSelected": "カーソルを行末に挿入" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/jpn/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..babf831c58 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "パラメーター ヒントをトリガー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/jpn/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..b74eae128d --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0}、ヒント" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/jpn/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..947c16365c --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "修正プログラム ({0}) を表示する", + "quickFix": "修正プログラムを表示する", + "quickfix.trigger.label": "クイック修正" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..50cf2e15b6 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": "– {0} 個の参照", + "references.action.label": "すべての参照の検索" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..b8251acb50 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "読み込んでいます..." +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..3e0c49cb01 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "列 {2} の {1} 行目に {0} つのシンボル", + "aria.fileReferences.1": "{0} に 1 個のシンボル、完全なパス {1}", + "aria.fileReferences.N": "{1} に {0} 個のシンボル、完全なパス {2}", + "aria.result.0": "一致する項目はありません", + "aria.result.1": "{0} に 1 個のシンボルが見つかりました", + "aria.result.n1": "{1} に {0} 個のシンボルが見つかりました", + "aria.result.nm": "{1} 個のファイルに {0} 個のシンボルが見つかりました" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..ad46e68b06 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "ファイルを解決できませんでした。", + "referencesCount": "{0} 個の参照", + "referenceCount": "{0} 個の参照", + "missingPreviewMessage": "プレビューを表示できません", + "treeAriaLabel": "参照", + "noResults": "結果がありません", + "peekView.alternateTitle": "参照", + "peekViewTitleBackground": "ピーク ビューのタイトル領域の背景色。", + "peekViewTitleForeground": "ピーク ビュー タイトルの色。", + "peekViewTitleInfoForeground": "ピーク ビューのタイトル情報の色。", + "peekViewBorder": "ピーク ビューの境界と矢印の色。", + "peekViewResultsBackground": "ピーク ビュー結果リストの背景色。", + "peekViewResultsMatchForeground": "ピーク ビュー結果リストのライン ノードの前景色。", + "peekViewResultsFileForeground": "ピーク ビュー結果リストのファイル ノードの前景色。", + "peekViewResultsSelectionBackground": "ピーク ビュー結果リストの選択済みエントリの背景色。", + "peekViewResultsSelectionForeground": "ピーク ビュー結果リストの選択済みエントリの前景色。", + "peekViewEditorBackground": "ピーク ビュー エディターの背景色。", + "peekViewEditorGutterBackground": "ピーク ビュー エディターの余白の背景色。", + "peekViewResultsMatchHighlight": "ピーク ビュー結果リストの一致した強調表示色。", + "peekViewEditorMatchHighlight": "ピーク ビュー エディターの一致した強調表示色。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/jpn/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..e9c532b7cb --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "結果がありません。", + "aria": "'{0}' から '{1}' への名前変更が正常に完了しました。概要: {2}", + "rename.failed": "申し訳ありません。名前の変更を実行できませんでした。", + "rename.label": "シンボルの名前を変更" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/jpn/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..12282dd2a4 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "名前変更入力。新しい名前を入力し、Enter キーを押してコミットしてください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/jpn/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..c9a7e80979 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "選択範囲を拡大", + "smartSelect.shrink": "選択範囲を縮小" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/jpn/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..ae9898ded8 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "'{0}' が次のテキストを挿入したことを承認しています: {1}", + "suggest.trigger.label": "候補をトリガー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/jpn/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..013107c2cb --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "候補のウィジェットの背景色。", + "editorSuggestWidgetBorder": "候補ウィジェットの境界線色。", + "editorSuggestWidgetForeground": "候補ウィジェットの前景色。", + "editorSuggestWidgetSelectedBackground": "候補ウィジェット内で選択済みエントリの背景色。", + "editorSuggestWidgetHighlightForeground": "候補のウィジェット内で一致したハイライトの色。", + "readMore": "詳細を表示...{0}", + "suggestionWithDetailsAriaLabel": "{0}、候補、詳細あり", + "suggestionAriaLabel": "{0}、候補", + "readLess": "詳細を隠す...{0}", + "suggestWidget.loading": "読み込んでいます...", + "suggestWidget.noSuggestions": "候補はありません。", + "suggestionAriaAccepted": "{0}、受け入れ済み", + "ariaCurrentSuggestionWithDetails": "{0}、候補、詳細あり", + "ariaCurrentSuggestion": "{0}、候補" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/jpn/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..c1126834d3 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "Tab キーを切り替えるとフォーカスが移動します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/jpn/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..7f5a0ad2f0 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "変数の読み取りなど読み取りアクセス中のシンボルの背景色。", + "wordHighlightStrong": "変数への書き込みなど書き込みアクセス中のシンボルの背景色。", + "overviewRulerWordHighlightForeground": "シンボルを強調表示するときの概要ルーラーのマーカー色。", + "overviewRulerWordHighlightStrongForeground": "書き込みアクセス シンボルを強調表示するときの概要ルーラーのマーカー色。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/jpn/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..8da6a85ea8 --- /dev/null +++ b/i18n/jpn/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "閉じる" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/jpn/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..63d02ea79f --- /dev/null +++ b/i18n/jpn/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "`contributes.{0}.language` で不明な言語です。提供された値: {1}", + "invalid.scopeName": "`contributes.{0}.scopeName` には文字列が必要です。提供された値: {1}", + "invalid.path.0": "`contributes.{0}.path` に文字列が必要です。提供された値: {1}", + "invalid.injectTo": "`contributes.{0}.injectTo` の値が無効です。言語の範囲名の配列である必要があります。指定された値: {1}", + "invalid.embeddedLanguages": "`contributes.{0}.embeddedLanguages` の値が無効です。スコープ名から言語へのオブジェクト マップである必要があります。指定された値: {1}", + "invalid.path.1": "拡張機能のフォルダー ({2}) の中に `contributes.{0}.path` ({1}) が含まれている必要があります。これにより拡張を移植できなくなる可能性があります。", + "no-tm-grammar": "この言語に対して TM 文法は登録されていません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/jpn/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..16c1c61066 --- /dev/null +++ b/i18n/jpn/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "{0} を解析中のエラー: {1}", + "schema.openBracket": "左角かっこまたは文字列シーケンス。", + "schema.closeBracket": "右角かっこまたは文字列シーケンス。", + "schema.comments": "コメント記号を定義します。", + "schema.blockComments": "ブロック コメントのマーク方法を定義します。", + "schema.blockComment.begin": "ブロック コメントを開始する文字シーケンス。", + "schema.blockComment.end": "ブロック コメントを終了する文字シーケンス。", + "schema.lineComment": "行コメントを開始する文字シーケンス。", + "schema.brackets": "インデントを増減する角かっこを定義します。", + "schema.autoClosingPairs": "角かっこのペアを定義します。左角かっこが入力されると、右角かっこが自動的に挿入されます。", + "schema.autoClosingPairs.notIn": "自動ペアが無効なスコープの一覧を定義します。", + "schema.surroundingPairs": "選択文字列を囲むときに使用できる角かっこのペアを定義します。", + "schema.wordPattern": "言語のための単語の定義。", + "schema.wordPattern.pattern": "言葉の照合に使用する正規表現パターン。", + "schema.wordPattern.flags": "言葉の照合に使用する正規表現フラグ。", + "schema.wordPattern.flags.errorMessage": "`/^([gimuy]+)$/` パターンに一致する必要があります。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/jpn/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..816dd0f1ad --- /dev/null +++ b/i18n/jpn/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "TextMate トークナイザーを提供します。", + "vscode.extension.contributes.grammars.language": "この構文の提供先の言語識別子です。", + "vscode.extension.contributes.grammars.scopeName": "tmLanguage ファイルにより使用される TextMate スコープ名。", + "vscode.extension.contributes.grammars.path": "tmLanguage ファイルのパス。拡張機能フォルダーの相対パスであり、通常 './syntaxes/' で始まります。", + "vscode.extension.contributes.grammars.embeddedLanguages": "この文法に言語が埋め込まれている場合は、言語 ID に対するスコープ名のマップ。", + "vscode.extension.contributes.grammars.injectTo": "この文法が挿入される言語の範囲名の一覧。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/jpn/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/jpn/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/jpn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..8e53fe5533 --- /dev/null +++ b/i18n/jpn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "メニュー項目は配列にする必要があります", + "requirestring": "`{0}` プロパティは必須で、`string` 型でなければなりません", + "optstring": "`{0}` プロパティは省略するか、`string` 型にする必要があります", + "vscode.extension.contributes.menuItem.command": "実行するコマンドの識別子。コマンドは 'commands' セクションで宣言する必要があります", + "vscode.extension.contributes.menuItem.alt": "実行する別のコマンドの識別子。コマンドは 'commands' セクションで宣言する必要があります", + "vscode.extension.contributes.menuItem.when": "この項目を表示するために満たす必要がある条件", + "vscode.extension.contributes.menuItem.group": "このコマンドが属するグループ", + "vscode.extension.contributes.menus": "メニュー項目をエディターに提供します", + "menus.commandPalette": "コマンド パレット", + "menus.editorTitle": "エディターのタイトル メニュー", + "menus.editorContext": "エディターのコンテキスト メニュー", + "menus.explorerContext": "エクスプローラーのコンテキスト メニュー", + "menus.editorTabContext": "エディターのタブのコンテキスト メニュー", + "menus.debugCallstackContext": "デバッグの呼び出し履歴のコンテキスト メニュー", + "menus.scmTitle": "ソース管理のタイトル メニュー", + "menus.resourceGroupContext": "ソース管理リソース グループのコンテキスト メニュー", + "menus.resourceStateContext": "ソース管理リソース状態のコンテキスト メニュー", + "view.viewTitle": "提供されたビューのタイトル メニュー", + "view.itemContext": "提供されたビュー項目のコンテキスト メニュー", + "nonempty": "空でない値が必要です。", + "opticon": "`icon` プロパティは省略できます。指定する場合には、文字列または `{dark, light}` などのリテラルにする必要があります", + "requireStringOrObject": "`{0}` プロパティは必須で、`string` または `object` の型でなければなりません", + "requirestrings": "プロパティの `{0}` と `{1}` は必須で、`string` 型でなければなりません", + "vscode.extension.contributes.commandType.command": "実行するコマンドの識別子", + "vscode.extension.contributes.commandType.title": "コマンドが UI に表示される際のタイトル", + "vscode.extension.contributes.commandType.category": "(省略可能) コマンド別のカテゴリ文字列が UI でグループ分けされます", + "vscode.extension.contributes.commandType.icon": "(省略可能) UI でコマンドを表すためのアイコン。ファイル パス、またはテーマ設定可能な構成のいずれかです", + "vscode.extension.contributes.commandType.icon.light": "ライト テーマが使用される場合のアイコン パス", + "vscode.extension.contributes.commandType.icon.dark": "ダーク テーマが使用される場合のアイコン パス", + "vscode.extension.contributes.commands": "コマンド パレットにコマンドを提供します。", + "dup": "コマンド `{0}` が `commands` セクションで複数回出現します。", + "menuId.invalid": "`{0}` は有効なメニュー識別子ではありません", + "missing.command": "メニュー項目が、'commands' セクションで定義されていないコマンド `{0}` を参照しています。", + "missing.altCommand": "メニュー項目が、'commands' セクションで定義されていない alt コマンド `{0}` を参照しています。", + "dupe.command": "メニュー項目において、既定と alt コマンドが同じコマンドを参照しています", + "nosupport.altCommand": "申し訳ございません。現在、alt コマンドをサポートしているのは 'editor/title' メニューの 'navigation' グループのみです" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/jpn/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..1bdb54e311 --- /dev/null +++ b/i18n/jpn/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "既定の構成オーバーライド", + "overrideSettings.description": "{0} 言語に対して上書きされるエディター設定を構成します。", + "overrideSettings.defaultDescription": "言語に対して上書きされるエディター設定を構成します。", + "config.property.languageDefault": "'{0}' を登録できません。これは、言語固有のエディター設定を記述するプロパティ パターン '\\\\[.*\\\\]$' に一致しています。'configurationDefaults' コントリビューションを使用してください。", + "config.property.duplicate": "'{0}' を登録できません。このプロパティは既に登録されています。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/environment/node/argv.i18n.json b/i18n/jpn/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..f0f608922a --- /dev/null +++ b/i18n/jpn/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "`--goto` モードの引数は `FILE(:LINE(:CHARACTER))` の形式にする必要があります。", + "diff": "2 つのファイルを比較します。", + "add": "最後にアクティブだったウィンドウにフォルダーを追加します。", + "goto": "指定した行と文字の位置にあるパスでファイルを開きます。", + "locale": "使用する国と地域 (例:en-US や zh-TW など)。", + "newWindow": "新しい Code のインスタンスを強制します。", + "performance": "'Developer: Startup Performance' コマンドを有効にして開始します。", + "prof-startup": "起動中に CPU プロファイラーを実行する", + "reuseWindow": "最後のアクティブ ウィンドウにファイルまたはフォルダーを強制的に開きます。", + "userDataDir": "ユーザー データを保持するディレクトリを指定します。ルートで実行している場合に役立ちます。", + "verbose": "詳細出力を表示します (--wait を含みます)。", + "wait": "現在のウィンドウが閉じるまで待機します。", + "extensionHomePath": "拡張機能のルート パスを設定します。", + "listExtensions": "インストールされている拡張機能を一覧表示します。", + "showVersions": "--list-extension と使用するとき、インストールされている拡張機能のバージョンを表示します。", + "installExtension": "拡張機能をインストールします。", + "uninstallExtension": "拡張機能をアンインストールします。", + "experimentalApis": "拡張機能に対して Proposed API 機能を有効にします。", + "disableExtensions": "インストールされたすべての拡張機能を無効にします。", + "disableGPU": "GPU ハードウェア アクセラレータを無効にします。", + "version": "バージョンを表示します。", + "help": "使用法を表示します。", + "usage": "使用法", + "options": "オプション", + "paths": "パス", + "optionsUpperCase": "オプション" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/jpn/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..2b581f84e2 --- /dev/null +++ b/i18n/jpn/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "ワークスペースがありません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/jpn/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..7b96a1863c --- /dev/null +++ b/i18n/jpn/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "拡張機能", + "preferences": "基本設定" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/jpn/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..d29a724b58 --- /dev/null +++ b/i18n/jpn/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "拡張子が見つかりません", + "noCompatible": "Code のこのバージョンと互換性のある {0} のバージョンが見つかりませんでした。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/jpn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..6c6c53c3c0 --- /dev/null +++ b/i18n/jpn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "正しくない拡張機能: package.json は JSON ファイルではありません。", + "restartCode": "{0} を再インストールする前に、Code を再起動してください。", + "installDependeciesConfirmation": "'{0}' をインストールすると、その依存関係もインストールされます。続行してもよろしいですか?", + "install": "はい", + "doNotInstall": "いいえ", + "uninstallDependeciesConfirmation": "'{0}' のみをアンインストールしますか、または依存関係もアンインストールしますか?", + "uninstallOnly": "限定", + "uninstallAll": "すべて", + "cancel": "キャンセル", + "uninstallConfirmation": "'{0}' をアンインストールしてもよろしいですか?", + "ok": "OK", + "singleDependentError": "拡張機能 '{0}' をアンインストールできません。拡張機能 '{1}' がこの拡張機能に依存しています。", + "twoDependentsError": "拡張機能 '{0}' をアンインストールできません。拡張機能 '{1}' と '{2}' がこの拡張機能に依存しています。", + "multipleDependentsError": "拡張機能 '{0}' をアンインストールできません。拡張機能 '{1}'、'{2}'、その他がこの拡張機能に依存しています。", + "notExists": "拡張機能を見つけられませんでした" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/jpn/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..1a57744bb5 --- /dev/null +++ b/i18n/jpn/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "拡張機能 `{1}` のアクティブ化に失敗しました。理由: 依存関係 `{0}` が不明です。", + "failedDep1": "拡張機能 `{1}` のアクティブ化に失敗しました。理由: 依存関係 `{0}` のアクティブ化に失敗しました。", + "failedDep2": "拡張機能 `{0}` のアクティブ化に失敗しました。理由: 依存関係のレベルが 10 を超えています (依存関係のループの可能性があります)。", + "activationError": "拡張機能 `{0}` のアクティブ化に失敗しました: {1}。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/jpn/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..bb86d8235d --- /dev/null +++ b/i18n/jpn/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "VS Code 拡張機能の場合、拡張機能と互換性のある VS Code バージョンを指定します。* を指定することはできません。たとえば、^0.10.5 は最小の VS Code バージョン 0.10.5 との互換性を示します。", + "vscode.extension.publisher": "VS Code 拡張機能の公開元。", + "vscode.extension.displayName": "VS Code ギャラリーで使用される拡張機能の表示名。", + "vscode.extension.categories": "VS Code ギャラリーで拡張機能の分類に使用されるカテゴリ。", + "vscode.extension.galleryBanner": "VS Code マーケットプレースで使用されるバナー。", + "vscode.extension.galleryBanner.color": "VS Code マーケットプレース ページ ヘッダー上のバナーの色。", + "vscode.extension.galleryBanner.theme": "バナーで使用されるフォントの配色テーマ。", + "vscode.extension.contributes": "このパッケージで表される VS Code 拡張機能のすべてのコントリビューション。", + "vscode.extension.preview": "Marketplace で Preview としてフラグが付けられるように拡張機能を設定します。", + "vscode.extension.activationEvents": "VS Code 拡張機能のアクティブ化イベント。", + "vscode.extension.activationEvents.onLanguage": "指定された言語を解決するファイルが開かれるたびにアクティブ化イベントが発行されます。", + "vscode.extension.activationEvents.onCommand": "指定したコマンドが呼び出されるたびにアクティブ化イベントが発行されます。", + "vscode.extension.activationEvents.onDebug": "指定されたタイプのデバッグ セッションが開始されるたびにアクティブ化イベントが発行されます。", + "vscode.extension.activationEvents.workspaceContains": "指定した glob パターンに一致するファイルを少なくとも 1 つ以上含むフォルダーを開くたびにアクティブ化イベントが発行されます。", + "vscode.extension.activationEvents.onView": "指定したビューを展開するたびにアクティブ化イベントが発行されます。", + "vscode.extension.activationEvents.star": "VS Code 起動時にアクティブ化イベントを発行します。優れたエンドユーザー エクスペリエンスを確保するために、他のアクティブ化イベントの組み合わせでは望む動作にならないときのみ使用してください。", + "vscode.extension.badges": "Marketplace の拡張機能ページのサイドバーに表示されるバッジの配列。", + "vscode.extension.badges.url": "バッジのイメージ URL。", + "vscode.extension.badges.href": "バッジのリンク。", + "vscode.extension.badges.description": "バッジの説明。", + "vscode.extension.extensionDependencies": "他の拡張機能に対する依存関係。拡張機能の識別子は常に ${publisher}.${name} です。例: vscode.csharp。", + "vscode.extension.scripts.prepublish": "パッケージが VS Code 拡張機能として公開される前に実行されるスクリプト。", + "vscode.extension.icon": "128x128 ピクセルのアイコンへのパス。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/jpn/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..2124d4c035 --- /dev/null +++ b/i18n/jpn/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "`engines.vscode` 値 {0} を解析できませんでした。使用可能な値の例: ^0.10.0、^1.2.3、^0.11.0、^0.10.x など。", + "versionSpecificity1": "`engines.vscode` ({0}) で指定されたバージョンが十分に特定されていません。1.0.0 より前の vscode バージョンの場合は、少なくとも想定されているメジャー バージョンとマイナー バージョンを定義してください。例 ^0.10.0、0.10.x、0.11.0 など。", + "versionSpecificity2": "`engines.vscode` ({0}) で指定されたバージョンが明確ではありません。1.0.0 より後のバージョンの vscode の場合は、少なくとも、想定されているメジャー バージョンを定義してください。例 ^1.10.0、1.10.x、1.x.x、2.x.x など。", + "versionMismatch": "拡張機能が Code {0} と互換性がありません。拡張機能に必要なバージョン: {1}。", + "extensionDescription.empty": "空の拡張機能の説明を入手しました", + "extensionDescription.publisher": "`{0}` プロパティは必須で、`string` 型でなければなりません", + "extensionDescription.name": "`{0}` プロパティは必須で、`string` 型でなければなりません", + "extensionDescription.version": "`{0}` プロパティは必須で、`string` 型でなければなりません", + "extensionDescription.engines": "`{0}` プロパティは必須で、`string` 型でなければなりません", + "extensionDescription.engines.vscode": "`{0}` プロパティは必須で、`string` 型でなければなりません", + "extensionDescription.extensionDependencies": "`{0}` プロパティは省略するか、`string[]` 型にする必要があります", + "extensionDescription.activationEvents1": "`{0}` プロパティは省略するか、`string[]` 型にする必要があります", + "extensionDescription.activationEvents2": "プロパティ `{0}` と `{1}` は、両方とも指定するか両方とも省略しなければなりません", + "extensionDescription.main1": "`{0}` プロパティは省略するか、`string` 型にする必要があります", + "extensionDescription.main2": "拡張機能のフォルダー ({1}) の中に `main` ({0}) が含まれることが予期されます。これにより拡張機能を移植できなくなる可能性があります。", + "extensionDescription.main3": "プロパティ `{0}` と `{1}` は、両方とも指定するか両方とも省略しなければなりません", + "notSemver": "拡張機能のバージョンが semver と互換性がありません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/jpn/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..61831bc0f0 --- /dev/null +++ b/i18n/jpn/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "新しいウィンドウ", + "newWindowDesc": "新しいウィンドウを開く", + "recentFolders": "最近使ったワークスペース", + "folderDesc": "{0} {1}", + "codeWorkspace": "コード ワークスペース" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/jpn/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..06f83ed20f --- /dev/null +++ b/i18n/jpn/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "OK", + "integrity.dontShowAgain": "今後は表示しない", + "integrity.moreInfo": "詳細情報", + "integrity.prompt": "{0} インストールが壊れている可能性があります。再インストールしてください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/jpn/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..fd989a1eec --- /dev/null +++ b/i18n/jpn/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "JSON スキーマ構成を提供します。", + "contributes.jsonValidation.fileMatch": "一致するファイル パターン、たとえば \"package.json\" または \"*.launch\" です。", + "contributes.jsonValidation.url": "スキーマ URL ('http:', 'https:') または拡張機能フォルダーへの相対パス ('./') です。", + "invalid.jsonValidation": "'configuration.jsonValidation' は配列でなければなりません", + "invalid.fileMatch": "'configuration.jsonValidation.fileMatch' が定義されていなければなりません", + "invalid.url": "'configuration.jsonValidation.url' は、URL または相対パスでなければなりません", + "invalid.url.fileschema": "'configuration.jsonValidation.url' は正しくない相対 URL です: {0}", + "invalid.url.schema": "'configuration.jsonValidation.url' は、'http:'、'https:'、または拡張機能にあるスキーマを参照する './' で始まる必要があります" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/jpn/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..fe3f4fa057 --- /dev/null +++ b/i18n/jpn/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "({0}) が押されました。2 番目のキーを待っています...", + "missing.chord": "キーの組み合わせ ({0}、{1}) はコマンドではありません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/keybinding/common/keybindingLabels.i18n.json b/i18n/jpn/src/vs/platform/keybinding/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..f83b6e672a --- /dev/null +++ b/i18n/jpn/src/vs/platform/keybinding/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Shift", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Shift", + "altKey.long": "Alt", + "cmdKey.long": "コマンド", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/jpn/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..af8d9b36b2 --- /dev/null +++ b/i18n/jpn/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "ループ プロパティは、最終行マッチャーでのみサポートされています。", + "ProblemPatternParser.problemPattern.missingRegExp": "問題パターンに正規表現がありません。", + "ProblemPatternParser.problemPattern.missingProperty": "問題のパターンが正しくありません。少なくとも、ファイル、メッセージと行、またはロケーション一致グループがなければなりません。", + "ProblemPatternParser.invalidRegexp": "エラー: 文字列 {0} は、有効な正規表現ではありません。\n", + "ProblemPatternSchema.regexp": "出力のエラー、警告、または情報を検索する正規表現。", + "ProblemPatternSchema.file": "ファイル名の一致グループ インデックス。省略すると、1 が使用されます。", + "ProblemPatternSchema.location": "問題の場所の一致グループ インデックス。有効な場所のパターンは (line)、(line,column)、(startLine,startColumn,endLine,endColumn) です。省略すると、 (line,column) が想定されます。", + "ProblemPatternSchema.line": "問題の行の一致グループ インデックス。既定は 2 です", + "ProblemPatternSchema.column": "問題の行の文字の一致グループ インデックス。既定は 3 です", + "ProblemPatternSchema.endLine": "問題の最終行の一致グループ インデックス。既定は undefined です", + "ProblemPatternSchema.endColumn": "問題の最終行の文字の一致グループ インデックス。既定は undefined です", + "ProblemPatternSchema.severity": "問題の重大度の一致グループ インデックス。既定は undefined です", + "ProblemPatternSchema.code": "問題のコードの一致グループ インデックス。既定は undefined です", + "ProblemPatternSchema.message": "メッセージの一致グループ インデックス。省略した場合、場所を指定すると既定は 4 で、場所を指定しないと既定は 5 です。", + "ProblemPatternSchema.loop": "複数行マッチャー ループは、このパターンが一致する限りループで実行されるかどうかを示します。複数行パターン内の最後のパターンでのみ指定できます。", + "NamedProblemPatternSchema.name": "問題パターンの名前。", + "NamedMultiLineProblemPatternSchema.name": "The name of the problem multi line problem pattern.", + "NamedMultiLineProblemPatternSchema.patterns": "実際のパターン。", + "ProblemPatternExtPoint": "問題パターンを提供", + "ProblemPatternRegistry.error": "無効な問題パターンです。パターンは無視されます。", + "ProblemMatcherParser.noProblemMatcher": "エラー: 説明を問題マッチャーに変換することができません:\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "エラー: 説明に有効な問題パターンが定義されていません:\n{0}\n", + "ProblemMatcherParser.noOwner": "エラー: 説明に所有者が定義されていません:\n{0}\n", + "ProblemMatcherParser.noFileLocation": "エラー: 説明にファイルの場所が定義されていません:\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "情報: 不明な重大度 {0}。有効な値は、error、warning、info です。\n", + "ProblemMatcherParser.noDefinedPatter": "エラー: 識別子 {0} のパターンは存在しません。", + "ProblemMatcherParser.noIdentifier": "エラー: パターン プロパティが空の識別子を参照しています。", + "ProblemMatcherParser.noValidIdentifier": "エラー: パターン プロパティ {0} は有効なパターン変数名ではありません。", + "ProblemMatcherParser.problemPattern.watchingMatcher": "問題マッチャーは、ウォッチ対象の開始パターンと終了パターンの両方を定義する必要があります。", + "ProblemMatcherParser.invalidRegexp": "エラー: 文字列 {0} は、有効な正規表現ではありません。\n", + "WatchingPatternSchema.regexp": "バックグラウンド タスクの開始または終了を検出する正規表現。", + "WatchingPatternSchema.file": "ファイル名の一致グループ インデックス。省略できます。", + "PatternTypeSchema.name": "提供されたか事前定義された問題パターンの名前", + "PatternTypeSchema.description": "A problem pattern or the name of a contributed or predefined problem pattern. Can be omitted if base is specified.", + "ProblemMatcherSchema.base": "使用する基本問題マッチャーの名前。", + "ProblemMatcherSchema.owner": "Code 内の問題の所有者。base を指定すると省略できます。省略して base を指定しない場合、既定は 'external' になります。", + "ProblemMatcherSchema.severity": "キャプチャされた問題の既定の重大度。パターンが重要度の一致グループを定義していない場合に使用されます。", + "ProblemMatcherSchema.applyTo": "テキスト ドキュメントで報告された問題が、開いているドキュメントのみ、閉じられたドキュメントのみ、すべてのドキュメントのいずれに適用されるかを制御します。", + "ProblemMatcherSchema.fileLocation": "問題パターンで報告されたファイル名を解釈する方法を定義します。", + "ProblemMatcherSchema.background": "バックグラウンド タスクでアクティブなマッチャーの開始と終了を追跡するパターン。", + "ProblemMatcherSchema.background.activeOnStart": "true に設定すると、タスクの開始時にバックグラウンド モニターがアクティブ モードになります。これは beginPattern と一致する行の発行と同等です。", + "ProblemMatcherSchema.background.beginsPattern": "出力内で一致すると、バックグラウンド タスクの開始が通知されます。", + "ProblemMatcherSchema.background.endsPattern": "出力内で一致すると、バックグラウンド タスクの終了が通知されます。", + "ProblemMatcherSchema.watching.deprecated": "watching プロパティは使用されなくなりました。代わりに background をご使用ください。", + "ProblemMatcherSchema.watching": "監視パターンの開始と終了を追跡するマッチャー。", + "ProblemMatcherSchema.watching.activeOnStart": "true に設定すると、タスクの開始時にウォッチャーがアクティブ モードになります。これは beginPattern と一致する行の発行と同等です。", + "ProblemMatcherSchema.watching.beginsPattern": "出力内で一致すると、ウォッチ中のタスクの開始が通知されます。", + "ProblemMatcherSchema.watching.endsPattern": "出力内で一致すると、ウォッチ中のタスクの終了が通知されます。", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "このプロパティは非推奨です。代わりに watching プロパティをご使用ください。", + "LegacyProblemMatcherSchema.watchedBegin": "ファイル ウォッチでトリガーされた ウォッチ対象タスクの実行が開始されたことを伝達する正規表現。", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "このプロパティは非推奨です。代わりに watching プロパティをご使用ください。", + "LegacyProblemMatcherSchema.watchedEnd": "ウォッチ対象タスクの実行が終了したことを伝達する正規表現。", + "NamedProblemMatcherSchema.name": "これを参照するのに使用する問題マッチャーの名前。", + "NamedProblemMatcherSchema.label": "問題マッチャーの判読できるラベル。", + "ProblemMatcherExtPoint": "問題マッチャーを提供", + "msCompile": "Microsoft コンパイラの問題", + "lessCompile": "Less の問題", + "gulp-tsc": "Gulp TSC の問題", + "jshint": "JSHint の問題", + "jshint-stylish": "JSHint の問題 (stylish)", + "eslint-compact": "ESLint の問題 (compact)", + "eslint-stylish": "ESLint の問題 (stylish)", + "go": "問題に移動する" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/message/common/message.i18n.json b/i18n/jpn/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..bd8b5d71cd --- /dev/null +++ b/i18n/jpn/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "閉じる", + "later": "後続", + "cancel": "キャンセル" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/request/node/request.i18n.json b/i18n/jpn/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..586d08229d --- /dev/null +++ b/i18n/jpn/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "使用するプロキシ設定。設定されていない場合、環境変数 http_proxy および https_proxy から取得されます。", + "strictSSL": "提供された CA の一覧と照らしてプロキシ サーバーの証明書を確認するかどうか。", + "proxyAuthorization": "すべてのネットワーク要求に対して 'Proxy-Authorization' ヘッダーとして送信する値。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/jpn/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..f56fe0e47d --- /dev/null +++ b/i18n/jpn/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "テレメトリ", + "telemetry.enableTelemetry": "利用状況データとエラーを Microsoft に送信できるようにします。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/jpn/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..1f2106640a --- /dev/null +++ b/i18n/jpn/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "拡張機能でテーマ設定の可能な配色を提供します", + "contributes.color.id": "テーマ設定可能な配色の識別子", + "contributes.color.id.format": "識別子は aa[.bb]* の形式にする必要があります", + "contributes.color.description": "テーマ設定可能な配色の説明", + "contributes.defaults.light": "light テーマの既定の配色。配色の値は 16 進数(#RRGGBB[AA]) 、または 既定で提供されているテーマ設定可能な配色の識別子の既定値のいずれか。", + "contributes.defaults.dark": "dark テーマの既定の配色。配色の値は 16 進数(#RRGGBB[AA]) 、または 既定で提供されているテーマ設定可能な配色の識別子の既定値のいずれか。", + "contributes.defaults.highContrast": "high contrast テーマの既定の配色。配色の値は 16 進数(#RRGGBB[AA]) 、または 既定で提供されているテーマ設定可能な配色の識別子の既定値のいずれか。", + "invalid.colorConfiguration": "'configuration.colors' は配列である必要があります", + "invalid.default.colorType": "{0} は 16 進数(#RRGGBB[AA] または #RGB[A]) 、または 既定で提供されているテーマ設定可能な配色の識別子の既定値のいずれかでなければなりません。", + "invalid.id": "'configuration.colors.id' は定義する必要があり、空にできません", + "invalid.id.format": "'configuration.colors.id' は word[.word]* の形式である必要があります", + "invalid.description": "'configuration.colors.description' は定義する必要があり、空にできません \n", + "invalid.defaults": "'configuration.colors.defaults' は定義する必要があります。'light' か 'dark'、'highContrast' を含める必要があります。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/jpn/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..e9c254d027 --- /dev/null +++ b/i18n/jpn/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "無効な色形式です。 #RGB、#RGBA、#RRGGBB、#RRGGBBAA のいずれかを使用してください", + "schema.colors": "ワークベンチで使用する色。", + "foreground": "全体の前景色。この色は、コンポーネントによってオーバーライドされていない場合にのみ使用されます。", + "errorForeground": "エラー メッセージ全体の前景色。この色は、コンポーネントによって上書きされていない場合にのみ使用されます。", + "descriptionForeground": "追加情報を提供する説明文の前景色、例:ラベル。", + "focusBorder": "フォーカスされた要素の境界線全体の色。この色はコンポーネントによって上書きされていない場合にのみ使用されます。", + "contrastBorder": "コントラストを強めるために、他の要素と隔てる追加の境界線。", + "activeContrastBorder": "コントラストを強めるために、アクティブな他要素と隔てる追加の境界線。", + "selectionBackground": "ワークベンチ内のテキスト選択の背景色 (例: 入力フィールドやテキストエリア)。エディター内の選択には適用されないことに注意してください。", + "textSeparatorForeground": "テキストの区切り文字の色。", + "textLinkForeground": "テキスト内のリンクの前景色。", + "textLinkActiveForeground": "テキスト内のアクティブなリンクの前景色。", + "textPreformatForeground": "フォーマット済みテキスト セグメントの前景色。", + "textBlockQuoteBackground": "テキスト内のブロック引用の背景色。", + "textBlockQuoteBorder": "テキスト内のブロック引用の境界線色。", + "textCodeBlockBackground": "テキスト内のコード ブロックの背景色。", + "widgetShadow": "エディター内の検索/置換窓など、エディター ウィジェットの影の色。", + "inputBoxBackground": "入力ボックスの背景。", + "inputBoxForeground": "入力ボックスの前景。", + "inputBoxBorder": "入力ボックスの境界線。", + "inputBoxActiveOptionBorder": "入力フィールドのアクティブ オプションの境界線の色。", + "inputPlaceholderForeground": "入力ボックスのプレースホルダー テキストの前景色。", + "inputValidationInfoBackground": "情報の重大度を示す入力検証の背景色。", + "inputValidationInfoBorder": "情報の重大度を示す入力検証の境界線色。", + "inputValidationWarningBackground": "警告の重大度を示す入力検証の背景色。", + "inputValidationWarningBorder": "警告の重大度を示す入力検証の境界線色。", + "inputValidationErrorBackground": "エラーの重大度を示す入力検証の背景色。", + "inputValidationErrorBorder": "エラーの重大度を示す入力検証の境界線色。", + "dropdownBackground": "ドロップダウンの背景。", + "dropdownForeground": "ドロップダウンの前景。", + "dropdownBorder": "ドロップダウンの境界線。", + "listFocusBackground": "ツリーリストがアクティブのとき、フォーカスされた項目のツリーリスト背景色。アクティブなツリーリストはキーボード フォーカスがあり、非アクティブではこれがありません。", + "listFocusForeground": "ツリーリストがアクティブのとき、フォーカスされた項目のツリーリスト前景色。アクティブなツリーリストはキーボード フォーカスがあり、非アクティブではこれがありません。", + "listActiveSelectionBackground": "ツリーリストがアクティブのとき、選択された項目のツリーリスト背景色。アクティブなツリーリストはキーボード フォーカスがあり、非アクティブではこれがありません。", + "listActiveSelectionForeground": "ツリーリストがアクティブのとき、選択された項目のツリーリスト前景色。アクティブなツリーリストはキーボード フォーカスがあり、非アクティブではこれがありません。", + "listInactiveSelectionBackground": "ツリーリストが非アクティブのとき、フォーカスされた項目のツリーリスト背景色。アクティブなツリーリストはキーボード フォーカスがあり、非アクティブではこれがありません。", + "listInactiveSelectionForeground": "ツリーリストが非アクティブのとき、選択された項目のツリーリスト前景色。アクティブなツリーリストはキーボード フォーカスがあり、非アクティブではこれがありません。", + "listHoverBackground": "マウス操作で項目をホバーするときのツリーリスト背景。", + "listHoverForeground": "マウス操作で項目をホバーするときのツリーリスト前景。", + "listDropBackground": "マウス操作で項目を移動するときのツリーリスト ドラッグ アンド ドロップの背景。", + "highlight": "ツリーリスト内を検索しているとき、一致した強調のツリーリスト前景色。", + "pickerGroupForeground": "ラベルをグループ化するためのクリック選択の色。", + "pickerGroupBorder": "境界線をグループ化するためのクイック選択の色。", + "buttonForeground": "ボタンの前景色。", + "buttonBackground": "ボタンの背景色。", + "buttonHoverBackground": "ホバー時のボタン背景色。", + "badgeBackground": "バッジの背景色。バッジとは小さな情報ラベルのことです。例:検索結果の数", + "badgeForeground": "バッジの前景色。バッジとは小さな情報ラベルのことです。例:検索結果の数", + "scrollbarShadow": "ビューがスクロールされたことを示すスクロール バーの影。", + "scrollbarSliderBackground": "スクロール バーのスライダーの背景色。", + "scrollbarSliderHoverBackground": "ホバー時のスクロール バー スライダー背景色。", + "scrollbarSliderActiveBackground": "アクティブ時のスクロール バー スライダー背景色。", + "progressBarBackground": "時間のかかる操作で表示するプログレス バーの背景色。", + "editorBackground": "エディターの背景色。", + "editorForeground": "エディターの既定の前景色。", + "editorWidgetBackground": "検索/置換窓など、エディター ウィジェットの背景色。", + "editorWidgetBorder": "エディター ウィジェットの境界線色。ウィジェットに境界線があり、ウィジェットによって配色を上書きされていない場合でのみこの配色は使用されます。", + "editorSelectionBackground": "エディターの選択範囲の色。", + "editorSelectionForeground": "ハイ コントラストの選択済みテキストの色。", + "editorInactiveSelection": "非アクティブなエディターの選択範囲の色。", + "editorSelectionHighlight": "選択範囲と同じコンテンツの領域の色。", + "editorFindMatch": "現在の検索一致項目の色。", + "findMatchHighlight": "他の検索一致項目の色。", + "findRangeHighlight": "検索を制限する範囲の色。", + "hoverHighlight": "ホバーが表示されているワードの下を強調表示します。", + "hoverBackground": "エディター ホバーの背景色。", + "hoverBorder": "エディター ホバーの境界線の色。", + "activeLinkForeground": "アクティブなリンクの色。", + "diffEditorInserted": "挿入されたテキストの背景色。", + "diffEditorRemoved": "削除されたテキストの背景色。", + "diffEditorInsertedOutline": "挿入されたテキストの輪郭の色。", + "diffEditorRemovedOutline": "削除されたテキストの輪郭の色。", + "mergeCurrentHeaderBackground": "行内マージ競合の現在のヘッダー背景色。", + "mergeCurrentContentBackground": "行内マージ競合の現在のコンテンツ背景色。", + "mergeIncomingHeaderBackground": "行内マージ競合の入力側ヘッダー背景色。", + "mergeIncomingContentBackground": "行内マージ競合の入力側コンテンツ背景色。", + "mergeCommonHeaderBackground": "行内マージ競合の共通の祖先ヘッダー背景色。", + "mergeCommonContentBackground": "行内マージ競合の共通の祖先コンテンツ背景色。", + "mergeBorder": "行内マージ競合のヘッダーとスプリッターの境界線の色。", + "overviewRulerCurrentContentForeground": "行内マージ競合の現在の概要ルーラー前景色。", + "overviewRulerIncomingContentForeground": "行内マージ競合の入力側の概要ルーラー前景色。", + "overviewRulerCommonContentForeground": "行内マージ競合の共通の祖先概要ルーラー前景色。", + "overviewRulerFindMatchForeground": "一致項目を示す概要ルーラーのマーカー色。", + "overviewRulerSelectionHighlightForeground": "選択対象を強調表示するときの概要ルーラーのマーカー色。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/jpn/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..f8cab5767d --- /dev/null +++ b/i18n/jpn/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "コード ワークスペース", + "untitledWorkspace": "未設定 (ワークスペース)", + "workspaceNameVerbose": "{0} (ワークスペース)", + "workspaceName": "{0} (ワークスペース)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/jpn/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..46965d3e4b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "拡張機能 {0} を {1} で上書きしています。", + "extensionUnderDevelopment": "開発の拡張機能を {0} に読み込んでいます" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/jpn/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..7139bbadf1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "閉じる", + "cancel": "キャンセル", + "ok": "OK" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/jpn/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..9da398dfb3 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "{0} 個の追加のエラーと警告が表示されていません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/node/extHostExplorerView.i18n.json b/i18n/jpn/src/vs/workbench/api/node/extHostExplorerView.i18n.json new file mode 100644 index 0000000000..d1ed943ffb --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/node/extHostExplorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "ID '{0}' の TreeExplorerNodeProvider は登録されていません。", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider '{0}' がルート ノードの指定に失敗しました。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/jpn/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..1a57744bb5 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "拡張機能 `{1}` のアクティブ化に失敗しました。理由: 依存関係 `{0}` が不明です。", + "failedDep1": "拡張機能 `{1}` のアクティブ化に失敗しました。理由: 依存関係 `{0}` のアクティブ化に失敗しました。", + "failedDep2": "拡張機能 `{0}` のアクティブ化に失敗しました。理由: 依存関係のレベルが 10 を超えています (依存関係のループの可能性があります)。", + "activationError": "拡張機能 `{0}` のアクティブ化に失敗しました: {1}。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/jpn/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json b/i18n/jpn/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json new file mode 100644 index 0000000000..0be806cb71 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "ID '{0}' の TreeExplorerNodeProvider は登録されていません。", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider '{0}' がルート ノードの指定に失敗しました。", + "treeExplorer.failedToResolveChildren": "TreeExplorerNodeProvider '{0}' が子の解決に失敗しました。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/node/extHostTreeView.i18n.json b/i18n/jpn/src/vs/workbench/api/node/extHostTreeView.i18n.json new file mode 100644 index 0000000000..d1ed943ffb --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/node/extHostTreeView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "ID '{0}' の TreeExplorerNodeProvider は登録されていません。", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider '{0}' がルート ノードの指定に失敗しました。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/jpn/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..10e26ed94c --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "ID '{0}' のツリー ビューは登録されていません。", + "treeItem.notFound": "ID '{0}' のツリー項目は見つかりませんでした。", + "treeView.duplicateElement": " {0} 要素は既に登録されています。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json b/i18n/jpn/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..46965d3e4b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "拡張機能 {0} を {1} で上書きしています。", + "extensionUnderDevelopment": "開発の拡張機能を {0} に読み込んでいます" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/api/node/mainThreadMessageService.i18n.json b/i18n/jpn/src/vs/workbench/api/node/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..7139bbadf1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/api/node/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "閉じる", + "cancel": "キャンセル", + "ok": "OK" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..2de0b9c628 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "言語を構成する", + "displayLanguage": "VSCode の表示言語を定義します。", + "doc": "サポートされている言語の一覧については、{0} をご覧ください。", + "restart": "値を変更するには VS Code の再起動が必要です。", + "fail.createSettings": "'{0}' ({1}) を作成できません。", + "JsonSchema.locale": "使用する UI 言語。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..0e72579653 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "フォルダーを開く...", + "openFileFolder": "開く...", + "add": "追加" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..3ee235291d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "アクティビティ バーの表示の切り替え", + "view": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..d52c5c3e97 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "エディターグループの縦/横レイアウトを切り替える", + "horizontalLayout": "エディターグループの横レイアウト", + "verticalLayout": "エディターグループの縦レイアウト", + "view": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..bb59c10d24 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "サイド バーの位置の切り替え", + "view": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..05d98cd0b0 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "サイドバーの表示の切り替え", + "view": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..a82f14f3b0 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "ステータス バーの可視性の切り替え", + "view": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..8eb472e765 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "Zen Mode の切り替え", + "view": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..5a4c34fa97 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "フォルダーを開く...", + "openFileFolder": "開く...", + "addFolderToWorkspace": "ワークスペースにフォルダーを追加...", + "add": "追加(&&A)", + "addFolderToWorkspaceTitle": "ワークスペースにフォルダーを追加", + "newWorkspace": "新しいワークスペース...", + "select": "選択(&&S)", + "selectWorkspace": "ワークスペースのフォルダーを選択", + "removeFolderFromWorkspace": "ワークスペースからフォルダーを削除", + "saveWorkspaceAsAction": "名前を付けてワークスペースを保存...", + "saveEmptyWorkspaceNotSupported": "最初に保存するためのワークスペースを開いてください。", + "save": "保存(&&S)", + "saveWorkspace": "ワークスペースを保存", + "openWorkspaceAction": "ワークスペースを開く...", + "openWorkspaceConfigFile": "ワークスペースの構成ファイルを開く" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..07c3a94d49 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "アクティビティ バーから削除", + "keepInActivityBar": "アクティビティ バーに保持", + "titleKeybinding": "{0} ({1})", + "additionalViews": "その他のビュー", + "numberBadge": "{0} ({1})", + "manageExtension": "拡張機能を管理", + "toggle": "ビューのピン留めの切り替え" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..7da5844e63 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "アクティビティ バーを非表示にする", + "activityBarAriaLabel": "アクティブなビュー スイッチャー", + "globalActions": "グローバル操作" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..df5bd25587 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "{0} 個のアクション", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..a0884bf8a2 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "バイナリ ビューアー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..fabd8bb163 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "テキスト エディター", + "textDiffEditor": "テキスト差分エディター", + "binaryDiffEditor": "バイナリ差分エディター", + "sideBySideEditor": "横並びエディター", + "groupOnePicker": "最初のグループのエディターを表示する", + "groupTwoPicker": "2 番目のグループでエディターを表示する", + "groupThreePicker": "3 番目のグループのエディターを表示する", + "allEditorsPicker": "開いているエディターをすべて表示する", + "view": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..5f44000d42 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "エディターの分割", + "joinTwoGroups": "2 つのグループのエディターを結合", + "navigateEditorGroups": "エディター グループ間で移動する", + "focusActiveEditorGroup": "アクティブなエディター グループにフォーカス", + "focusFirstEditorGroup": "最初のエディター グループにフォーカス", + "focusSecondEditorGroup": "2 番目のエディター グループにフォーカス", + "focusThirdEditorGroup": "3 番目のエディター グループにフォーカス", + "focusPreviousGroup": "前のグループにフォーカス", + "focusNextGroup": "次のグループにフォーカス", + "openToSide": "横に並べて開く", + "closeEditor": "エディターを閉じる", + "revertAndCloseActiveEditor": "元に戻してエディターを閉じる", + "closeEditorsToTheLeft": "左側のエディターを閉じる", + "closeEditorsToTheRight": "右側のエディターを閉じる", + "closeAllEditors": "すべてのエディターを閉じる", + "closeUnmodifiedEditors": "グループ内の未変更のエディターを閉じる", + "closeEditorsInOtherGroups": "他のグループ内のエディターを閉じる", + "closeOtherEditorsInGroup": "その他のエディターを閉じる", + "closeEditorsInGroup": "グループ内のすべてのエディターを閉じる", + "moveActiveGroupLeft": "エディター グループを左側に移動する", + "moveActiveGroupRight": "エディター グループを右側に移動する", + "minimizeOtherEditorGroups": "他のエディター グループを最小化する", + "evenEditorGroups": "エディター グループの幅を等間隔に設定する", + "maximizeEditor": "エディター グループを最大化してサイドバーを非表示にする", + "keepEditor": "エディターを保持", + "openNextEditor": "次のエディターを開く", + "openPreviousEditor": "以前のエディターを開く", + "nextEditorInGroup": "グループ内で次のエディターを開く", + "openPreviousEditorInGroup": "グループ内で前のエディターを開く", + "navigateNext": "次に進む", + "navigatePrevious": "前に戻る", + "reopenClosedEditor": "閉じたエディターを再度開く", + "clearRecentFiles": "最近開いた項目をクリア", + "showEditorsInFirstGroup": "最初のグループのエディターを表示する", + "showEditorsInSecondGroup": "2 番目のグループでエディターを表示する", + "showEditorsInThirdGroup": "3 番目のグループのエディターを表示する", + "showEditorsInGroup": "エディターをグループに表示する", + "showAllEditors": "すべてのエディターを表示する", + "openPreviousRecentlyUsedEditorInGroup": "グループ内の最近使用したエディターのうち前のエディターを開く", + "openNextRecentlyUsedEditorInGroup": "グループ内の最近使用したエディターのうち次のエディターを開く", + "navigateEditorHistoryByInput": "履歴から以前のエディターを開く", + "openNextRecentlyUsedEditor": "最近使用したエディターのうち次のエディターを開く", + "openPreviousRecentlyUsedEditor": "最近使用したエディターのうち前のエディターを開く", + "clearEditorHistory": "エディター履歴のクリア", + "focusLastEditorInStack": "グループ内の最後のエディターを開く", + "moveEditorLeft": "エディターを左へ移動", + "moveEditorRight": "エディターを右へ移動", + "moveEditorToPreviousGroup": "エディターを前のグループに移動", + "moveEditorToNextGroup": "エディターを次のグループに移動" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..855c30fd33 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "タブまたはグループ別にアクティブ エディターを移動する", + "editorCommand.activeEditorMove.arg.name": "アクティブ エディターの Move 引数", + "editorCommand.activeEditorMove.arg.description": "引数プロパティ:\n\t\t\t\t\t\t* 'to': 引数プロパティ\n\t\t\t\t\t\t* 'by': 移動に使用する単位を指定する文字列値。タブ別またはグループ別。\n\t\t\t\t\t\t* 'value': 移動する位置数と絶対位置を指定する数値。\n\t\t\t\t\t", + "commandDeprecated": "コマンド **{0}** は削除されました。代わりに **{1}** を使用できます", + "openKeybindings": "ショートカット キーの構成" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..a8e6a47120 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "左", + "groupTwoVertical": "中央", + "groupThreeVertical": "右", + "groupOneHorizontal": "上", + "groupTwoHorizontal": "中央", + "groupThreeHorizontal": "下", + "editorOpenError": "'{0}' を開くことができません: {1}。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..5685722cd4 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}、エディター グループの選択", + "groupLabel": "グループ: {0}", + "noResultsFoundInGroup": "グループ内に一致する開いているエディターがありません", + "noOpenedEditors": "グループ内で開いているエディターの一覧は現在、空です", + "noResultsFound": "一致する開いているエディターがありません", + "noOpenedEditorsAllGroups": "開いているエディターの一覧は現在、空です" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..9495e7740b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "行 {0}、列 {1} ({2} 個選択)", + "singleSelection": "行 {0}、列 {1}", + "multiSelectionRange": "{0} 個の選択項目 ({1} 文字を選択)", + "multiSelection": "{0} 個の選択項目", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "タブによるフォーカスの移動", + "screenReaderDetected": "スクリーン リーダーを検出しました", + "screenReaderDetectedExtra": "スクリーン リーダーを使用しない場合、`editor.accessibilitySupport` を \"off\" にしてください。", + "disableTabMode": "アクセシビリティ モードを無効にする", + "gotoLine": "行へ移動", + "indentation": "インデント", + "selectEncoding": "エンコードの選択", + "selectEOL": "改行コードの選択", + "selectLanguageMode": "言語モードの選択", + "fileInfo": "ファイル情報", + "spacesSize": "スペース: {0}", + "tabSize": "タブのサイズ: {0}", + "showLanguageExtensions": "'{0}' の Marketplace の拡張機能を検索する ...", + "changeMode": "言語モードの変更", + "noEditor": "現在アクティブなテキスト エディターはありません", + "languageDescription": "({0}) - 構成済みの言語", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "言語 (識別子)", + "configureModeSettings": "'{0}' 言語ベース設定を構成します...", + "configureAssociationsExt": "'{0}' に対するファイルの関連付けの構成...", + "autoDetect": "自動検出", + "pickLanguage": "言語モードの選択", + "currentAssociation": "現在の関連付け", + "pickLanguageToConfigure": "'{0}' に関連付ける言語モードの選択", + "changeIndentation": "インデントの変更", + "noWritableCodeEditor": "アクティブなコード エディターは読み取り専用です。", + "indentView": "ビューの変更", + "indentConvert": "ファイルの変換", + "pickAction": "アクションの選択", + "changeEndOfLine": "改行コードの変更", + "pickEndOfLine": "改行コードの選択", + "changeEncoding": "ファイルのエンコードの変更", + "noFileEditor": "現在アクティブなファイルはありません", + "saveWithEncoding": "エンコード付きで保存", + "reopenWithEncoding": "エンコード付きで再度開く", + "guessedEncoding": "コンテンツから推測", + "pickEncodingForReopen": "ファイルを再度開くときのファイルのエンコードの選択", + "pickEncodingForSave": "保存時のファイルのエンコードの選択" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..91990864dd --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "タブ操作" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..bdd96b4d67 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "テキスト差分エディター", + "readonlyEditorWithInputAriaLabel": "{0}。読み取り専用のテキスト比較エディター。", + "readonlyEditorAriaLabel": "読み取り専用のテキスト比較エディター。", + "editableEditorWithInputAriaLabel": "{0}。テキスト ファイル比較エディター。", + "editableEditorAriaLabel": "テキスト ファイル比較エディター。", + "navigate.next.label": "次の変更箇所", + "navigate.prev.label": "前の変更箇所", + "inlineDiffLabel": "インライン表示に切り替え", + "sideBySideDiffLabel": "並べて表示に切り替え" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..8b15069a74 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0}、グループ {1}。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..62b48749f2 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "テキスト エディター", + "readonlyEditorWithInputAriaLabel": "{0}。読み取り専用のテキスト エディター。", + "readonlyEditorAriaLabel": "読み取り専用のテキスト エディター。", + "untitledFileEditorWithInputAriaLabel": "{0}。無題のファイル テキスト エディター。", + "untitledFileEditorAriaLabel": "無題のファイル テキスト エディター。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..c1ae8cf2de --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "閉じる", + "closeOthers": "その他を閉じる", + "closeRight": "右側を閉じる", + "closeAll": "すべて閉じる", + "closeAllUnmodified": "未変更を閉じる", + "keepOpen": "開いたままにする", + "showOpenedEditors": "開いているエディターを表示", + "araLabelEditorActions": "エディター操作" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..e5dea2d9c5 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "パネルを閉じる", + "togglePanel": "パネルの切り替え", + "focusPanel": "パネルにフォーカスする", + "toggleMaximizedPanel": "最大化されるパネルの切り替え", + "maximizePanel": "パネル サイズの最大化", + "minimizePanel": "パネル サイズを元に戻す", + "view": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..c788ae7d32 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "アクティブ パネル スイッチャー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..e6381a3e7b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} ('Enter' を押して確認するか 'Escape' を押して取り消します)", + "inputModeEntry": "'Enter' を押して入力を確認するか 'Escape' を押して取り消します", + "emptyPicks": "選べるエントリがありません", + "quickOpenInput": "'?' と入力すると、ここで実行できる処理に関するヘルプが表示されます", + "historyMatches": "最近開いたもの", + "noResultsFound1": "一致する項目はありません", + "canNotRunPlaceholder": "この Quick Open ハンドラーは現在のコンテキストでは使用できません", + "entryAriaLabel": "{0}、最近開いたもの", + "removeFromEditorHistory": "履歴から削除", + "pickHistory": "履歴から削除するエディター エントリを選ぶ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..f92a8ce725 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "ファイルに移動...", + "quickNavigateNext": "Quick Open で次に移動", + "quickNavigatePrevious": "Quick Open で前に移動", + "quickSelectNext": "Quick Open で [次へ] を選択", + "quickSelectPrevious": "Quick Open で [前へ] を選択" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..f92a8ce725 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "ファイルに移動...", + "quickNavigateNext": "Quick Open で次に移動", + "quickNavigatePrevious": "Quick Open で前に移動", + "quickSelectNext": "Quick Open で [次へ] を選択", + "quickSelectPrevious": "Quick Open で [前へ] を選択" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..a311cb4905 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "サイド バーを非表示", + "focusSideBar": "サイド バー内にフォーカス", + "viewCategory": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..629dcf2fe1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "コマンド '{0}' は現在有効ではなく、実行できません。", + "manageExtension": "拡張機能を管理" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..e444811ddb --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[サポート対象外]", + "devExtensionWindowTitlePrefix": "[拡張機能開発ホスト]" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/quickopen.i18n.json b/i18n/jpn/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..ea07d82877 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "一致する結果はありません", + "noResultsFound2": "一致する項目はありません", + "entryAriaLabel": "{0}、コマンド" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/viewlet.i18n.json b/i18n/jpn/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..dccd59a2b8 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "すべて折りたたむ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/common/theme.i18n.json b/i18n/jpn/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..f4b463472d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "アクティブ タブの背景色。タブはエディター領域におけるエディターのコンテナーです。1 つのエディター グループで複数のタブを開くことができます。エディター グループを複数にすることもできます。", + "tabInactiveBackground": "非アクティブ タブの背景色。タブはエディター領域におけるエディターのコンテナーです。1 つのエディター グループで複数のタブを開くことができます。エディター グループを複数にすることもできます。", + "tabBorder": "タブ同士を分けるための境界線。タブはエディター領域内にあるエディターのコンテナーです。複数のタブを 1 つのエディター グループで開くことができます。複数のエディター グループがある可能性があります。", + "tabActiveBorder": "アクティブなタブを強調表示するための境界線。タブはエディター領域内にあるエディターのコンテナーです。複数のタブを 1 つのエディター グループで開くことができます。複数のエディター グループがある可能性があります。", + "tabActiveUnfocusedBorder": "フォーカスされていないグループ内のアクティブ タブを強調表示するための境界線。タブはエディター領域内にあるエディターのコンテナーです。複数のタブを 1 つのエディター グループで開くことができます。複数のエディター グループがある可能性があります。", + "tabActiveForeground": "アクティブ グループ内のアクティブ タブの前景色。タブはエディター領域におけるエディターのコンテナーです。1 つのエディター グループで複数のタブを開くことができます。エディター グループを複数にすることもできます。", + "tabInactiveForeground": "アクティブ グループ内の非アクティブ タブの前景色。タブはエディター領域におけるエディターのコンテナーです。1 つのエディター グループで複数のタブを開くことができます。エディター グループを複数にすることもできます。", + "tabUnfocusedActiveForeground": "フォーカスされていないグループ内のアクティブ タブの前景色。タブはエディター領域におけるエディターのコンテナーです。1 つのエディター グループで複数のタブを開くことができます。エディター グループを複数にすることもできます。", + "tabUnfocusedInactiveForeground": "フォーカスされていないグループ内の非アクティブ タブの前景色。タブはエディター領域におけるエディターのコンテナーです。1 つのエディター グループで複数のタブを開くことができます。エディター グループを複数にすることもできます。", + "editorGroupBackground": "エディター グループの背景色。エディター グループはエディターのコンテナーです。背景色はエディター グループをドラッグすると表示されます。", + "tabsContainerBackground": "タブが有効な場合の エディター グループ タイトル ヘッダーの背景色。エディター グループはエディターのコンテナーです。", + "tabsContainerBorder": "タブが有効な場合の エディター グループ タイトル ヘッダーの境界線色。エディター グループはエディターのコンテナーです。", + "editorGroupHeaderBackground": "タブが無効な場合の エディター グループ タイトル ヘッダーの背景色。エディター グループはエディターのコンテナーです。", + "editorGroupBorder": "複数のエディター グループを互いに分離するための色。エディター グループはエディターのコンテナーです。", + "editorDragAndDropBackground": "エディターの周囲をドラッグしているときの背景色。エディターのコンテンツが最後まで輝くために、色は透過である必要があります。", + "panelBackground": "パネルの背景色。パネルはエディター領域の下に表示され、出力や統合ターミナルなどのビューを含みます。", + "panelBorder": "エディターとの区切りを示すパネル上部の罫線の色。パネルはエディター領域の下に表示され、出力や統合ターミナルなどのビューを含みます。", + "panelActiveTitleForeground": "アクティブ パネルのタイトルの色。パネルはエディター領域の下に表示され、出力や統合ターミナルなどのビューを含みます。", + "panelInactiveTitleForeground": "非アクティブ パネルのタイトルの色。パネルはエディター領域の下に表示され、出力や統合ターミナルなどのビューを含みます。", + "panelActiveTitleBorder": "アクティブ パネル タイトルの境界線の色。パネルはエディター領域の下に表示され、出力や統合ターミナルなどのビューを含みます。", + "statusBarForeground": "ワークスペースを開いていないときのステータス バーの前景色。ステータス バーはウィンドウの下部に表示されます。", + "statusBarNoFolderForeground": "フォルダーが開いていないときのステータス バーの前景色。ステータス バーはウィンドウの下部に表示されます。", + "statusBarBackground": "ワークスペースを開いていないときのステータス バーの背景色。ステータス バーはウィンドウの下部に表示されます。", + "statusBarNoFolderBackground": "フォルダーが開いていないときのステータス バーの背景色。ステータス バーはウィンドウの下部に表示されます。", + "statusBarBorder": "サイドバーとエディターを隔てるステータス バーの境界線色。ステータス バーはウィンドウの下部に表示されます。", + "statusBarNoFolderBorder": "フォルダーを開いていないときにサイドバーとエディターを隔てるワークスペースのステータス バーの境界線の色。ステータス バーはウィンドウの下部に表示されます。 ", + "statusBarItemActiveBackground": "クリック時のステータス バーの項目の背景色。ステータス バーはウィンドウの下部に表示されます。", + "statusBarItemHoverBackground": "ホバーしたときのステータス バーの項目の背景色。ステータス バーはウィンドウの下部に表示されます。", + "statusBarProminentItemBackground": "ステータス バーの重要な項目の背景色。重要な項目は、重要性を示すために他のステータスバーの項目から際立っています。 ステータス バーはウィンドウの下部に表示されます。", + "statusBarProminentItemHoverBackground": "ホバーしたときのステータス バーの重要な項目の背景色。重要な項目は、重要性を示すために他のステータスバーの項目から際立っています。 ステータス バーはウィンドウの下部に表示されます。", + "activityBarBackground": "アクティビティ バーの背景色。アクティビティ バーは左端または右端に表示され、サイド バーのビューを切り替えることができます。", + "activityBarForeground": "アクティビティ バーの前景色 (例: アイコンの色)。アクティビティ バーは左端または右端に表示され、サイド バーのビューを切り替えることができます。", + "activityBarBorder": "サイド バーと隔てるアクティビティ バーの境界線色。アクティビティ バーは左端または右端に表示され、サイド バーのビューを切り替えることができます。", + "activityBarDragAndDropBackground": "アクティビティ バーの項目のドラッグ アンド ドロップ フィードバックの色。アクティビティ バーが最後まで輝くために、色は透過である必要があります。アクティビティ バーは左端または右端に表示され、サイド バーの表示を切り替えることができます。", + "activityBarBadgeBackground": "アクティビティ通知バッジの背景色。アクティビティ バーは左端または右端に表示され、サイド バーの表示を切り替えることができます。", + "activityBarBadgeForeground": "アクティビティ通知バッジの前景色。アクティビティ バーは左端または右端に表示され、サイド バーの表示を切り替えることができます。", + "sideBarBackground": "サイド バーの背景色。サイド バーは、エクスプローラーや検索などのビューが入るコンテナーです。", + "sideBarForeground": "サイド バーの前景色。サイド バーは、エクスプローラーや検索などのビューが入るコンテナーです。", + "sideBarBorder": "エディターとの区切りを示すサイド バーの境界線の色。サイド バーは、エクスプローラーや検索などのビューが入るコンテナーです。", + "sideBarTitleForeground": "サイド バーのタイトルの前景色。サイド バーは、エクスプローラーや検索などのビューが入るコンテナーです。", + "sideBarDragAndDropBackground": "サイド バーのセクションのドラッグ アンド ドロップ フィードバックの色。サイド バーのセクションが最後まで輝くために、色は透過である必要があります。サイド バーはエクスプローラーや検索のようなビューのコンテナーです。", + "sideBarSectionHeaderBackground": "サイド バーのセクション ヘッダーの背景色。サイド バーは、エクスプローラーや検索などのビューが入るコンテナーです。", + "sideBarSectionHeaderForeground": "サイド バーのセクション ヘッダーの前景色。サイド バーは、エクスプローラーや検索などのビューが入るコンテナーです。", + "titleBarActiveForeground": "ウィンドウがアクティブな場合のタイトル バーの前景。現在、この色は macOS でのみサポートされているのでご注意ください。", + "titleBarInactiveForeground": "ウィンドウが非アクティブな場合のタイトル バーの前景。現在、この色は macOS でのみサポートされているのでご注意ください。", + "titleBarActiveBackground": "ウィンドウがアクティブな場合のタイトル バーの背景。現在、この色は macOS でのみサポートされているのでご注意ください。", + "titleBarInactiveBackground": "ウィンドウが非アクティブな場合のタイトル バーの背景。現在、この色は macOS でのみサポートされているのでご注意ください。", + "titleBarBorder": "タイトル バーの境界線色。現在、この色は macOS でのみサポートされているのでご注意ください。", + "notificationsForeground": "通知の前景色。通知はウィンドウの上部からスライド表示します。", + "notificationsBackground": "通知の背景色。通知はウィンドウの上部からスライド表示します。", + "notificationsButtonBackground": "通知ボタンの背景色。通知はウィンドウの上部からスライド表示します。", + "notificationsButtonHoverBackground": "ホバー時の通知ボタンの背景色。通知はウィンドウの上部からスライド表示します。", + "notificationsButtonForeground": "通知ボタンの前景色。通知はウィンドウの上部からスライド表示します。", + "notificationsInfoBackground": "情報通知の背景色。通知はウィンドウの上部からスライド表示します。", + "notificationsInfoForeground": "情報通知の前景色。通知はウィンドウの上部からスライド表示します。", + "notificationsWarningBackground": "警告通知の背景色。通知はウィンドウの上部からスライド表示します。", + "notificationsWarningForeground": "警告通知の前景色。通知はウィンドウの上部からスライド表示します。", + "notificationsErrorBackground": "エラー通知の背景色。通知はウィンドウの上部からスライド表示します。", + "notificationsErrorForeground": "エラー通知の前景色。通知はウィンドウの上部からスライド表示します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..bec90d6029 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "エディターを閉じる", + "closeWindow": "ウィンドウを閉じる", + "closeWorkspace": "ワークスペースを閉じる", + "noWorkspaceOpened": "このインスタンスで現在開いているワークスペースがないので、閉じられません。", + "newWindow": "新しいウィンドウ", + "toggleFullScreen": "全画面表示の切り替え", + "toggleMenuBar": "メニュー バーの切り替え", + "toggleDevTools": "開発者ツールの切り替え", + "zoomIn": "拡大", + "zoomOut": "縮小", + "zoomReset": "ズームのリセット", + "appPerf": "スタートアップ パフォーマンス", + "reloadWindow": "ウィンドウの再読み込み", + "switchWindowPlaceHolder": "切り替え先のウィンドウを選択してください", + "current": "現在のウィンドウ", + "close": "ウィンドウを閉じる", + "switchWindow": "ウィンドウの切り替え...", + "quickSwitchWindow": "ウィンドウをすぐに切り替える...", + "workspaces": "ワークスペース", + "files": "ファイル", + "openRecentPlaceHolderMac": "開くものを選択 (Cmd キーを押しながら新しいウィンドウで開く)", + "openRecentPlaceHolder": "開くものを選択 (Ctrl キーを押しながら新しいウィンドウで開く)", + "remove": "最近開いた項目から削除", + "openRecent": "最近開いた項目…", + "quickOpenRecent": "最近使用したものを開く...", + "closeMessages": "通知メッセージを閉じる", + "reportIssues": "問題の報告", + "reportPerformanceIssue": "パフォーマンスの問題のレポート", + "keybindingsReference": "キーボード ショートカットの参照", + "openDocumentationUrl": "ドキュメント", + "openIntroductoryVideosUrl": "紹介ビデオ", + "openTipsAndTricksUrl": " ヒントとコツ", + "toggleSharedProcess": "共有プロセスを切り替える", + "navigateLeft": "左のビュー部分に移動", + "navigateRight": "右のビュー部分に移動", + "navigateUp": "上のビュー部分に移動", + "navigateDown": "下のビュー部分に移動", + "increaseViewSize": "現在のビューのサイズの拡大", + "decreaseViewSize": "現在のビューのサイズの縮小" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/crashReporter.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/crashReporter.i18n.json new file mode 100644 index 0000000000..0ca17d7348 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/electron-browser/crashReporter.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "テレメトリ", + "telemetry.enableCrashReporting": "クラッシュ レポートを Microsoft に送信するように設定します。\nこのオプションを有効にするには、再起動が必要です。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..111a2a16cb --- /dev/null +++ b/i18n/jpn/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "拡張機能ホストが 10 秒以内に開始されませんでした。先頭行で停止している可能性があり、続行するにはデバッガーが必要です。", + "extensionHostProcess.startupFail": "拡張機能ホストが 10 秒以内に開始されませんでした。問題が発生している可能性があります。", + "extensionHostProcess.error": "拡張機能ホストからのエラー: {0}", + "devTools": "開発者ツール", + "extensionHostProcess.crash": "拡張機能ホストが予期せずに終了しました。回復するには、ウィンドウを再度読み込んでください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..a90623e002 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "表示", + "help": "ヘルプ", + "file": "ファイル", + "workspaces": "ワークスペース", + "developer": "開発者", + "showEditorTabs": "開いているエディターをタブに表示するかどうかを制御します。", + "editorTabCloseButton": "エディター タブの閉じるボタンの位置を制御するか、[off] に設定した場合に無効にします。", + "showIcons": "開いているエディターをアイコンで表示するかどうかを制御します。これには、アイコンのテーマを有効にする必要もあります。", + "enablePreview": "開いているエディターをプレビューとして表示するかどうかを制御します。プレビュー エディターは、保持されている間、再利用されます (ダブルクリックまたは編集などによって)。", + "enablePreviewFromQuickOpen": "Quick Open で開いたエディターをプレビューとして表示するかどうかを制御します。プレビュー エディターは、保持されている間、再利用されます (ダブルクリックまたは編集などによって)。", + "editorOpenPositioning": "エディターを開く場所を制御します。[左] または [右] を選択して、現在アクティブになっているエディターの左または右にエディターを開きます。[最初] または [最後] を選択して、現在アクティブになっているエディターとは別個にエディターを開きます。", + "revealIfOpen": "任意の表示グループが開かれた場合に、そこにエディターを表示するかどうかを制御します。無効にした場合、エディターは現在のアクティブなエディター グループに優先して開かれます。有効にした場合は、現在のアクティブなエディター グループにもう一度開くのではなく、既に開いているエディターが表示されます。特定のグループ内や現在アクティブなグループの横に強制的にエディターを開いた場合などに、この設定が無視される場合もあることにご注意ください。", + "commandHistory": "コマンド パレットで最近使用したコマンド履歴を保持する数を制御します。0 に設定するとコマンド履歴を無効にします。", + "preserveInput": "次回開いたとき、コマンド パレットの最後の入力を復元するかどうかを制御します。", + "closeOnFocusLost": "フォーカスを失ったときに Quick Open を自動的に閉じるかどうかを制御します。", + "openDefaultSettings": "設定を開くとすべての既定の設定を表示するエディターも開くかどうかを制御します。", + "sideBarLocation": "サイド バーの位置を制御します。ワークベンチの左右のいずれかに表示できます。", + "statusBarVisibility": "ワークベンチの下部にステータス バーを表示するかどうかを制御します。", + "activityBarVisibility": "ワークベンチでのアクティビティ バーの表示をコントロールします。", + "closeOnFileDelete": "ファイルを表示しているエディターを、ファイルが削除されるかその他のプロセスによって名前を変更された場合に、自動的に閉じるかどうかを制御します。これを無効にすると、このような場合にエディターはダーティで開かれたままになります。アプリケーション内で削除すると、必ずエディターは閉じられ、ダーティ ファイルは閉じられることがなく、データは保存されませんのでご注意ください。", + "fontAliasing": "ワークベンチのフォント エイリアシング方法を制御します。\n- default: サブピクセル方式でフォントを滑らかにします。ほとんどの非 Retina ディスプレイでもっとも鮮明なテキストを提供します\n- antialiased: サブピクセルとは対照的に、ピクセルのレベルでフォントを滑らかにします。フォント全体がより細く見えます\n- none: フォントのスムージングを無効にします。テキストをぎざぎざな尖ったエッジで表示します", + "workbench.fontAliasing.default": "サブピクセル方式でフォントを滑らかにします。ほとんどの非 Retina ディスプレイでもっとも鮮明なテキストを提供します。", + "workbench.fontAliasing.antialiased": "サブピクセルとは対照的に、ピクセルのレベルでフォントを滑らかにします。フォント全体がより細く見えるようになります。", + "workbench.fontAliasing.none": "フォントのスムージングを無効にします。テキストをぎざぎざな尖ったエッジで表示します。", + "swipeToNavigate": "3 本の指で横方向にスワイプすると、開いているファイル間を移動できます。", + "workbenchConfigurationTitle": "ワークベンチ", + "window.openFilesInNewWindow.on": "新しいウィンドウでファイルを開きます", + "window.openFilesInNewWindow.off": "ファイルのフォルダーが開かれていたウィンドウまたは最後のアクティブ ウィンドウでファイルを開きます", + "window.openFilesInNewWindow.default": "ファイルのフォルダーが開かれていたウィンドウでファイルを開くか、Dock または Finder を使用して開く場合以外は最後のアクティブ ウィンドウでファイルを開きます (macOS のみ)", + "openFilesInNewWindow": "ファイルを新しいウィンドウで開くかどうかを制御します。\n- default: ファイルのフォルダーが開かれていたウィンドウでファイルを開くか、Dock または Finder を使用して開く場合以外は最後のアクティブ ウィンドウでファイルを開きます (macOS のみ\n- on: 新しいウィンドウでファイルを開きます\n- off: ファイルのフォルダーが開かれていたウィンドウまたは最後のアクティブ ウィンドウでファイルを開きます\nこの設定は無視される場合もあります (-new-window または -reuse-window コマンド ライン オプションを使用する場合など)。", + "window.openFoldersInNewWindow.on": "新しいウィンドウでフォルダーを開きます", + "window.openFoldersInNewWindow.off": "フォルダーは、最後のアクティブ ウィンドウで開きます", + "window.openFoldersInNewWindow.default": "フォルダーがアプリケーション内から (たとえば、[ファイル] メニューから) 選択された場合を除いて、新しいウィンドウでフォルダーを開きます", + "openFoldersInNewWindow": "フォルダーを新しいウィンドウで開くか、最後のアクティブ ウィンドウで開くかを制御します。\n- default: アプリケーション内で ([ファイル] メニューなどから) 選択したものでなければ、新しいウィンドウでフォルダーを開く\n- on: 新しいウィンドウでフォルダーを開く\n- off: 最後のアクティブ ウィンドウでフォルダーを開く\nこの設定は無視される場合もあります (-new-window または -reuse-window コマンド ライン オプションを使用する場合など)。", + "window.reopenFolders.all": "すべてのウィンドウを再度開きます。", + "window.reopenFolders.folders": "すべてのフォルダーを再度開きます。空のワークスペースは復元されません。", + "window.reopenFolders.one": "最後にアクティブだったウィンドウを再び開きます。", + "window.reopenFolders.none": "ウィンドウ再度開きません。常に空のウィンドウで開始します。", + "restoreWindows": "再起動後にワークスペースを再度開く方法を制御します。'none' を選択すると常に空のワークスペースで開始します。'one' を選択すると最後に使用したウィンドウを再度開きます。'folders' を選択すると開かれていたフォルダーとすべてのウィンドウを再度開きます。'all' を選択すると前回のセッションのすべてのウィンドウを再度開きます。", + "restoreFullscreen": "全画面表示モードで終了した場合に、ウィンドウを全画面表示モードに復元するかどうかを制御します。", + "zoomLevel": "ウィンドウのズーム レベルを調整します。元のサイズは 0 で、1 つ上げるごとに (1 など) 20% ずつ拡大することを表し、1 つ下げるごとに (-1 など) 20% ずつ縮小することを表します。小数点以下の桁数を入力して、さらに細かくズーム レベルを調整することもできます。", + "title": "アクティブなエディターに基づいてウィンドウのタイトルを制御します。変数は、コンテキストに基づいて置換されます:\n${activeEditorShort}: 例: myFile.txt\n${activeEditorMedium}: 例: myFolder/myFile.txt\n${activeEditorLong}: 例: /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: 例: myFolder\n${folderPath}: 例: /Users/Development/myFolder\n${rootName}: 例: myFolder1, myFolder2, myFolder3\n${rootPath}: 例: /Users/Development/myWorkspace\n${appName}: 例: VS Code\n${dirty}: アクティブなエディターがダーティである場合のダーティ インジゲーター\n${separator}: 値のある変数で囲まれた場合にのみ表示される条件付き区切り記号 (\" - \")", + "window.newWindowDimensions.default": "新しいウィンドウを画面の中央に開きます。", + "window.newWindowDimensions.inherit": "新しいウィンドウを、最後にアクティブだったウィンドウと同じサイズで開きます。", + "window.newWindowDimensions.maximized": "新しいウィンドウを最大化した状態で開きます。", + "window.newWindowDimensions.fullscreen": "新しいウィンドウを全画面表示モードで開きます。", + "newWindowDimensions": "既に 1 つ以上のウィンドウを開いているとき、新しく開くウィンドウのサイズを制御します。既定では、新しいウィンドウを画面中央に小さいサイズで開きます。'inherit' に設定すると、最後のアクティブ ウィンドウと同じサイズで開きます。'maximized' に設定するとウィンドウは最大サイズで開き、'fullscreen' に設定すると全画面になります。この設定は、最初に開いたウィンドウに適用されないことに注意してください。最初のウィンドウは常に、前回閉じたサイズと位置で復元します。", + "closeWhenEmpty": "最後のエディターを閉じたときに、ウィンドウも閉じるかどうかを制御します。この設定はフォルダーを表示していないウィンドウにのみ適用されます。", + "window.menuBarVisibility.default": "メニューは全画面表示モードの場合にのみ非表示です。", + "window.menuBarVisibility.visible": "全画面表示モードの場合も含めて、常にメニューが表示されます。", + "window.menuBarVisibility.toggle": "メニューは非表示ですが、Alt キーを押すと表示できます。", + "window.menuBarVisibility.hidden": "メニューは常に非表示です。", + "menuBarVisibility": "メニュー バーの表示/非表示を制御します。'切り替え' 設定は Alt キーを 1 回押すとメニュー バーの表示/非表示が切り替わることを意味します。既定では、ウィンドウが全画面表示の場合を除き、メニュー バーは表示されます。", + "enableMenuBarMnemonics": "有効にすると、Alt キー ショートカットを使用してメイン メニューを開くことができます。ニーモニックを無効にすると、これらの Alt キー ショートカットをエディター コマンドの代わりにバインドできます。", + "autoDetectHighContrast": "有効にすると、Windows でハイ コントラスト テーマが使用されている場合にはハイ コントラスト テーマに自動的に変更され、Windows のハイ コントラスト テーマから切り替えられている場合にはダーク テーマに自動的に変更されます。", + "titleBarStyle": "ウィンドウのタイトル バーの外観を調整します。変更を適用するには、完全に再起動する必要があります。", + "window.nativeTabs": "macOS Sierra ウィンドウ タブを有効にします。この変更を適用するには完全な再起動が必要であり、ネイティブ タブでカスタムのタイトル バー スタイルが構成されていた場合はそれが無効になることに注意してください。", + "windowConfigurationTitle": "ウィンドウ", + "zenModeConfigurationTitle": "Zen Mode", + "zenMode.fullScreen": "Zen Mode をオンにするとワークベンチを自動的に全画面モードに切り替えるかどうかを制御します。", + "zenMode.hideTabs": "Zen Mode をオンにしたときにワークベンチ タブも非表示にするかどうかを制御します。", + "zenMode.hideStatusBar": "Zen Mode をオンにするとワークベンチの下部にあるステータス バーを非表示にするかどうかを制御します。", + "zenMode.hideActivityBar": "Zen Mode をオンにするとワークベンチの左側にあるアクティビティ バーを非表示にするかを制御します。", + "zenMode.restore": "Zen Mode で終了したウィンドウを Zen Mode に復元するかどうかを制御します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/main.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..dec96d6e53 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "必要なファイルを読み込みに失敗しました。インターネット接続が切れたか、接続先のサーバーがオフラインです。ブラウザーを更新して、もう一度やり直してください。", + "loaderErrorNative": "必要なファイルの読み込みに失敗しました。アプリケーションを再起動してもう一度試してください。詳細: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..1f85a87eb2 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "コードを 'root ' として実行しないことをお勧めします。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/window.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..df8af62dc7 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "元に戻す", + "redo": "やり直し", + "cut": "切り取り", + "copy": "コピー", + "paste": "貼り付け", + "selectAll": "すべて選択" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..5a7f182864 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "開発者", + "file": "ファイル" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/jpn/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..d426d123d2 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "パス {0} は有効な拡張機能テスト ランナーを指していません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/jpn/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..562f075c9d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "{0} を解析できません: {1}。", + "fileReadFail": "ファイル {0} を読み取れません: {1}。", + "jsonsParseFail": "{0} または {1} を解析できませんでした: {2}。", + "missingNLSKey": "キー {0} のメッセージが見つかりませんでした。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..d688c38048 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "PATH 内に '{0}' コマンドをインストールします", + "not available": "このコマンドは使用できません", + "successIn": "シェル コマンド '{0}' が PATH に正常にインストールされました。", + "warnEscalation": "管理者特権でシェル コマンドをインストールできるように、Code が 'osascript' のプロンプトを出します", + "ok": "OK", + "cantCreateBinFolder": "'/usr/local/bin' を作成できません。", + "cancel2": "キャンセル", + "aborted": "中止されました", + "uninstall": "'{0}' コマンドを PATH からアンインストールします", + "successFrom": "シェル コマンド '{0}' が PATH から正常にアンインストールされました。", + "shellCommand": "シェル コマンド" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..27faffb376 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "現在 `editor.accessibilitySupport` 設定を 'on' に変更しています。", + "openingDocs": "現在 VS Code のアクセシビリティ ドキュメントページを開いています。", + "introMsg": "VS Code のアクセシビリティ オプションをご利用いただき、ありがとうございます。", + "status": "ステータス:", + "changeConfigToOnMac": "スクリーン リーダーで使用するためにエディターを永続的に最適化するように設定するには、Command + E を押してください。", + "changeConfigToOnWinLinux": "スクリーン リーダーで使用するためにエディターを永続的に最適化するように設定するには、Control + E を押してください。", + "auto_unknown": "エディターは、プラットフォーム API を使用してスクリーン リーダーがいつ接続されたかを検出するように設定されていますが、現在のランタイムはこれをサポートしていません。", + "auto_on": "エディターはスクリーン リーダーの接続を自動検出しました。", + "auto_off": "エディターは、スクリーン リーダーが接続されると自動的に検出するように構成されていますが、今回は検出できませんでした。", + "configuredOn": "エディターはスクリーン リーダーで使用するために永続的に最適化されるように設定されています。これは `editor.accessibilitySupport` の設定を編集することで変更できます。", + "configuredOff": "エディターはスクリーン リーダー向けに最適化しないように構成されています。", + "tabFocusModeOnMsg": "現在のエディターで Tab キーを押すと、次のフォーカス可能な要素にフォーカスを移動します。{0} を押すと、この動作が切り替わります。", + "tabFocusModeOnMsgNoKb": "現在のエディターで Tab キーを押すと、次のフォーカス可能な要素にフォーカスを移動します。コマンド {0} は、キー バインドでは現在トリガーできません。", + "tabFocusModeOffMsg": "現在のエディターで Tab キーを押すと、タブ文字が挿入されます。{0} を押すと、この動作が切り替わります。", + "tabFocusModeOffMsgNoKb": "現在のエディターで Tab キーを押すと、タブ文字が挿入されます。コマンド {0} は、キー バインドでは現在トリガーできません。", + "openDocMac": "command + H キーを押して、ブラウザー ウィンドウを今すぐ開き、アクセシビリティに関連する他の VS Code 情報を確認します。", + "openDocWinLinux": "Ctrl + H キーを押して、ブラウザー ウィンドウを今すぐ開き、アクセシビリティに関連する他の VS Code 情報を確認します。", + "outroMsg": "Esc キー か Shift+Esc を押すと、ヒントを消してエディターに戻ることができます。", + "ShowAccessibilityHelpAction": "アクセシビリティのヘルプを表示します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..9acc60b8b1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "開発者: キー マッピングを検査する" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..d2e3900ea0 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "開発者: TM スコープの検査", + "inspectTMScopesWidget.loading": "読み込んでいます..." +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..84800c86ec --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "{0} を解析中のエラー: {1}", + "schema.openBracket": "左角かっこまたは文字列シーケンス。", + "schema.closeBracket": "右角かっこまたは文字列シーケンス。", + "schema.comments": "コメント記号を定義します。", + "schema.blockComments": "ブロック コメントのマーク方法を定義します。", + "schema.blockComment.begin": "ブロック コメントを開始する文字シーケンス。", + "schema.blockComment.end": "ブロック コメントを終了する文字シーケンス。", + "schema.lineComment": "行コメントを開始する文字シーケンス。", + "schema.brackets": "インデントを増減する角かっこを定義します。", + "schema.autoClosingPairs": "角かっこのペアを定義します。左角かっこが入力されると、右角かっこが自動的に挿入されます。", + "schema.autoClosingPairs.notIn": "自動ペアが無効なスコープの一覧を定義します。", + "schema.surroundingPairs": "選択文字列を囲むときに使用できる角かっこのペアを定義します。", + "schema.wordPattern": "言語のための単語の定義。", + "schema.wordPattern.pattern": "言葉の照合に使用する正規表現パターン。", + "schema.wordPattern.flags": "言葉の照合に使用する正規表現フラグ。", + "schema.wordPattern.flags.errorMessage": "`/^([gimuy]+)$/` パターンに一致する必要があります。", + "schema.indentationRules": "言語のインデント設定。", + "schema.indentationRules.increaseIndentPattern": "ある行がこのパターンと一致する場合は、それ以降のすべての行を一度インデントする必要があります (別のルールが一致するまで) 。", + "schema.indentationRules.increaseIndentPattern.pattern": "increaseIndentPattern に使用する正規表現パターン。", + "schema.indentationRules.increaseIndentPattern.flags": "increaseIndentPattern に使用する正規表現フラグ。", + "schema.indentationRules.increaseIndentPattern.errorMessage": "`/^([gimuy]+)$/` パターンに一致する必要があります。", + "schema.indentationRules.decreaseIndentPattern": "ある行がこのパターンに一致する場合は、それ以降のすべての行は一度アンインデントする必要があります (別のルールが一致するまで) 。", + "schema.indentationRules.decreaseIndentPattern.pattern": "decreaseIndentPattern に使用する正規表現パターン。", + "schema.indentationRules.decreaseIndentPattern.flags": "decreaseIndentPattern に使用する正規表現フラグ。", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "`/^([gimuy]+)$/` パターンに一致する必要があります。", + "schema.indentationRules.indentNextLinePattern": "ある行がこのパターンと一致する場合は、**次の行のみ** を一度インデントする必要があります。", + "schema.indentationRules.indentNextLinePattern.pattern": "indentNextLinePattern に使用する正規表現パターン。", + "schema.indentationRules.indentNextLinePattern.flags": "indentNextLinePattern に使用する正規表現フラグ。", + "schema.indentationRules.indentNextLinePattern.errorMessage": "`/^([gimuy]+)$/` パターンに一致する必要があります。", + "schema.indentationRules.unIndentedLinePattern": "ある行がこのパターンと一致する場合は、そのインデントを変更してはならず、他のルールに対して評価してもなりません。", + "schema.indentationRules.unIndentedLinePattern.pattern": "unIndentedLinePattern に使用する正規表現パターン。", + "schema.indentationRules.unIndentedLinePattern.flags": "unIndentedLinePattern に使用する正規表現フラグ。", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "`/^([gimuy]+)$/` パターンに一致する必要があります。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..d2e3900ea0 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "開発者: TM スコープの検査", + "inspectTMScopesWidget.loading": "読み込んでいます..." +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..d0ed2cc2ba --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "表示: ミニマップの切り替え" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..087987fb09 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "マルチ カーソルの修飾キーを切り替える" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..6a37557b22 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "表示: 制御文字の切り替え" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..c640de7e22 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "表示: 空白文字の表示の切り替え" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..3cbe1b34d3 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "表示: [右端で折り返す] の設定/解除", + "wordWrap.notInDiffEditor": "差分エディターで折り返しの切り替えができません。", + "unwrapMinified": "このファイルでの折り返しを無効にする", + "wrapMinified": "このファイルでの折り返しを有効にする" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..c575bf9d80 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "OK", + "wordWrapMigration.dontShowAgain": "今後は表示しない", + "wordWrapMigration.openSettings": "設定を開く", + "wordWrapMigration.prompt": "設定 'editor.wrappingColumn' は使用されなくなりました。'editor.wordWrap' を使用してください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..91cea8ea4e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "式が true と評価される場合に中断します。'Enter' を押して受け入れるか 'Esc' を押して取り消します。", + "breakpointWidgetAriaLabel": "この条件が true の場合にのみプログラムはこの位置で停止します。Enter を押して受け入れるか、Esc を押して取り消します。", + "breakpointWidgetHitCountPlaceholder": "ヒット カウント条件が満たされる場合に中断します。'Enter' を押して受け入れるか 'Esc' を押して取り消します。", + "breakpointWidgetHitCountAriaLabel": "ヒット カウントが満たされる場合にのみプログラムはこの位置で停止します。Enter を押して受け入れるか、Esc を押して取り消します。", + "expression": "式", + "hitCount": "ヒット カウント" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..fe1f873ca2 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "構成がありません", + "addConfigTo": "設定 ({0}) の追加 ...", + "addConfiguration": "構成の追加..." +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..086246b5c0 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "{0} を開く", + "launchJsonNeedsConfigurtion": "'launch.json' を構成または修正してください", + "noFolderDebugConfig": "高度なデバッグ構成を行うには、最初にフォルダーを開いてください。", + "startDebug": "デバッグの開始", + "startWithoutDebugging": "デバッグなしで開始", + "selectAndStartDebugging": "選択してデバッグを開始", + "restartDebug": "再起動", + "reconnectDebug": "再接続", + "stepOverDebug": "ステップ オーバー", + "stepIntoDebug": "ステップ インする", + "stepOutDebug": "ステップ アウト", + "stopDebug": "停止", + "disconnectDebug": "切断", + "continueDebug": "続行", + "pauseDebug": "一時停止", + "restartFrame": "フレームの再起動", + "removeBreakpoint": "ブレークポイントの削除", + "removeAllBreakpoints": "すべてのブレークポイントを削除する", + "enableBreakpoint": "ブレークポイントの有効化", + "disableBreakpoint": "ブレークポイントの無効化", + "enableAllBreakpoints": "すべてのブレークポイントを有効にする", + "disableAllBreakpoints": "すべてのブレークポイントを無効にする", + "activateBreakpoints": "ブレークポイントのアクティブ化", + "deactivateBreakpoints": "ブレークポイントの非アクティブ化", + "reapplyAllBreakpoints": "すべてのブレークポイントを再適用する", + "addFunctionBreakpoint": "関数ブレークポイントの追加", + "renameFunctionBreakpoint": "関数ブレークポイントの名前変更", + "addConditionalBreakpoint": "条件付きブレークポイントの追加...", + "editConditionalBreakpoint": "ブレークポイントの編集...", + "setValue": "値の設定", + "addWatchExpression": "式の追加", + "editWatchExpression": "式の編集", + "addToWatchExpressions": "ウォッチに追加", + "removeWatchExpression": "式の削除", + "removeAllWatchExpressions": "すべての式を削除する", + "clearRepl": "コンソールのクリア", + "debugConsoleAction": "デバッグ コンソール", + "unreadOutput": "デバッグ コンソールでの新しい出力", + "debugFocusConsole": "デバッグ コンソールにフォーカスを移動", + "focusProcess": "フォーカスのプロセス", + "stepBackDebug": "1 つ戻る", + "reverseContinue": "反転" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..6abf9ea080 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "デバッグ ツール バーの背景色。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..3a159f16f8 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "デバッグ セッションなしでリソースを解決できません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..f81168e900 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "デバッグ: ブレークポイントの切り替え", + "columnBreakpointAction": "デバッグ: 列ブレークポイント", + "columnBreakpoint": "列ブレークポイントの追加", + "conditionalBreakpointEditorAction": "デバッグ: 条件付きブレークポイントの追加...", + "runToCursor": "カーソル行の前まで実行", + "debugEvaluate": "デバッグ: 評価", + "debugAddToWatch": "デバッグ: ウォッチに追加", + "showDebugHover": "デバッグ: ホバーの表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..e6096017bb --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "無効なブレークポイント", + "breakpointUnverifieddHover": "未確認のブレークポイント", + "breakpointDirtydHover": "未確認のブレークポイント。ファイルは変更されているので、デバッグ セッションを再起動してください。", + "breakpointUnsupported": "このデバッグの種類では、条件付きブレークポイントはサポートされていません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..a3cb90fbb8 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}、デバッグ", + "debugAriaLabel": "実行する起動構成の名前を入力してください。", + "noConfigurationsMatching": "一致するデバッグ構成はありません", + "noConfigurationsFound": "デバッグ構成が見つかりません。'launch.json' ファイルを作成してください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..3a578e2399 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "例外ウィジェットの境界線の色。", + "debugExceptionWidgetBackground": "例外ウィジェットの背景色。", + "exceptionThrownWithId": "例外が発生しました: {0}", + "exceptionThrown": "例外が発生しました" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..7e017ca9c5 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "クリックして従う (Cmd を押しながらクリックすると横に開きます)", + "fileLink": "クリックして従う (Ctrl を押しながらクリックすると横に開きます)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..5eca41746a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "内部デバッグ コンソールの動作を制御します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..256ae78cf9 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "使用できません", + "startDebugFirst": "デバッグ セッションを開始して評価してください" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..5f9464c889 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "不明なソース" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..4ccfb35fb2 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "デバッグの表示", + "toggleDebugPanel": "デバッグ コンソール", + "debug": "デバッグ", + "debugPanel": "デバッグ コンソール", + "variables": "変数", + "watch": "ウォッチ式", + "callStack": "コール スタック", + "breakpoints": "ブレークポイント", + "view": "表示", + "debugCategory": "デバッグ", + "debugCommands": "デバッグ構成", + "debugConfigurationTitle": "デバッグ", + "allowBreakpointsEverywhere": "任意のファイルにブレークポイントを設定できるようにする", + "openExplorerOnEnd": "デバッグ セッションの終わりにエクスプローラ ビューを自動的に開きます", + "inlineValues": "デバッグ中にエディターの行内に変数値を表示します", + "hideActionBar": "浮動デバッグ操作バーを非表示にするかどうかを制御します", + "launch": "グローバル デバッグ起動構成。ワークスペース間で共有される 'launch.json' の代わりとして使用する必要があります" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..e0215bd802 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "高度なデバッグ構成を行うには、最初にフォルダーを開いてください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..7f95935590 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "デバッグ アダプターを提供します。", + "vscode.extension.contributes.debuggers.type": "このデバッグ アダプターの一意識別子。", + "vscode.extension.contributes.debuggers.label": "このデバッグ アダプターの表示名。", + "vscode.extension.contributes.debuggers.program": "デバッグ アダプター プログラムへのパス。絶対パスか拡張機能フォルダーへの相対パスです。", + "vscode.extension.contributes.debuggers.args": "アダプターに渡すオプションの引数。", + "vscode.extension.contributes.debuggers.runtime": "プログラム属性が実行可能でなく、ランタイムが必要な場合のオプション ランタイム。", + "vscode.extension.contributes.debuggers.runtimeArgs": "オプションのランタイム引数。", + "vscode.extension.contributes.debuggers.variables": "`launch.json` 内の対話型の変数 (例: ${action.pickProcess}) からコマンドへマッピングしています。", + "vscode.extension.contributes.debuggers.initialConfigurations": "初期 'launch.json' を生成するための構成。", + "vscode.extension.contributes.debuggers.languages": "デバッグ拡張機能が \"既定のデバッガー\" とされる言語の一覧。", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "指定されている場合、VS Code はこのコマンドを呼び出し、デバッグ アダプターの実行可能パスと、渡す引数を決定します。", + "vscode.extension.contributes.debuggers.startSessionCommand": "VS Code が指定されている場合、この拡張機能を対象とする \"デバッグ\" または \"実行\" アクションにこのコマンドが呼び出されます。", + "vscode.extension.contributes.debuggers.configurationSnippets": "'launch.json' に新しい構成を追加するためのスニペット。", + "vscode.extension.contributes.debuggers.configurationAttributes": "'launch.json' を検証するための JSON スキーマ構成。", + "vscode.extension.contributes.debuggers.windows": "Windows 固有の設定。", + "vscode.extension.contributes.debuggers.windows.runtime": "Windows で使用されるランタイム。", + "vscode.extension.contributes.debuggers.osx": "OS X 固有の設定。", + "vscode.extension.contributes.debuggers.osx.runtime": "OSX で使用されるランタイム。", + "vscode.extension.contributes.debuggers.linux": "Linux 固有の設定。", + "vscode.extension.contributes.debuggers.linux.runtime": "Linux で使用されるランタイム。", + "vscode.extension.contributes.breakpoints": "ブレークポイントを提供します。", + "vscode.extension.contributes.breakpoints.language": "この言語でブレークポイントを許可します。", + "app.launch.json.title": "起動", + "app.launch.json.version": "このファイル形式のバージョン。", + "app.launch.json.configurations": "構成の一覧。IntelliSense を使用して、新しい構成を追加したり、既存の構成を編集したります。", + "app.launch.json.compounds": "複合の一覧。各複合は、同時に起動される複数の構成を参照します。", + "app.launch.json.compound.name": "複合の名前。起動構成のドロップダウン メニューに表示されます。", + "app.launch.json.compounds.configurations": "この複合の一部として開始される構成の名前。", + "debugNoType": "デバッグ アダプター 'type' は省略不可で、'string' 型でなければなりません。", + "selectDebug": "環境の選択", + "DebugConfig.failed": "'launch.json' ファイルを '.vscode' フォルダー ({0}) 内に作成できません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..694a37e53a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "ブレークポイントの削除", + "removeBreakpointOnColumn": "列 {0} のブレークポイントの削除", + "removeLineBreakpoint": "行のブレークポイントの削除", + "editBreakpoints": "ブレークポイントの編集", + "editBreakpointOnColumn": "列 {0} のブレークポイントの編集", + "editLineBrekapoint": "行のブレークポイントの編集", + "enableDisableBreakpoints": "ブレークポイントの有効化/無効化", + "disableColumnBreakpoint": "列 {0} のブレークポイントの無効化", + "disableBreakpointOnLine": "行のブレークポイントの無効化", + "enableBreakpoints": "列 {0} のブレークポイントの有効化", + "enableBreakpointOnLine": "行のブレークポイントの有効化", + "addBreakpoint": "ブレークポイントの追加", + "addConfiguration": "構成の追加..." +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..120e388e4f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "デバッグ ホバー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..8162e1f863 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "このオブジェクトのプリミティブ値のみ表示されます。", + "debuggingPaused": "デバッグは一時停止されました、理由 {0}、{1} {2}", + "debuggingStarted": "デバッグは開始されました。", + "debuggingStopped": "デバッグは停止されました。", + "breakpointAdded": "ブレークポイントを追加しました。行 {0}、ファイル {1}", + "breakpointRemoved": "ブレークポイントを削除しました。行 {0}、ファイル {1}", + "compoundMustHaveConfigurations": "複合構成を開始するには、複合に \"configurations\" 属性が設定されている必要があります。", + "configMissing": "構成 '{0}' が 'launch.json' 内にありません。", + "debugTypeNotSupported": "構成されているデバッグの種類 '{0}' はサポートされていません。", + "debugTypeMissing": "選択された起動構成のプロパティ 'type' がありません。", + "preLaunchTaskErrors": "preLaunchTask '{0}' の実行中にビルド エラーが検出されました。", + "preLaunchTaskError": "preLaunchTask '{0}' の実行中にビルド エラーが検出されました。", + "preLaunchTaskExitCode": "preLaunchTask '{0}' が終了コード {1} で終了しました。", + "debugAnyway": "このままデバッグを続ける", + "noFolderWorkspaceDebugError": "アクティブ ファイルをデバッグできません。ファイルがディスクに保存されており、そのファイル タイプのデバッグ拡張機能がインストールされていることを確認してください。", + "NewLaunchConfig": "アプリケーションの起動構成ファイルをセットアップしてください。{0}", + "DebugTaskNotFound": "preLaunchTask '{0}' が見つかりませんでした。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..c52753e530 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "プロセス", + "paused": "一時停止", + "running": "実行しています", + "thread": "スレッド", + "pausedOn": "{0} で一時停止", + "loadMoreStackFrames": "スタック フレームをさらに読み込む", + "threadAriaLabel": "スレッド {0}、呼び出しスタック、デバッグ", + "stackFrameAriaLabel": "スタック フレーム {0} 行 {1} {2}、呼び出しスタック、デバッグ", + "variableValueAriaLabel": "新しい変数値を入力する", + "variableScopeAriaLabel": "範囲 {0}、変数、デバッグ", + "variableAriaLabel": "{0} 値 {1}、変数、デバッグ", + "watchExpressionPlaceholder": "ウォッチ対象の式", + "watchExpressionInputAriaLabel": "ウォッチ式を入力します", + "watchExpressionAriaLabel": "{0} 値 {1}、ウォッチ、デバッグ", + "watchVariableAriaLabel": "{0} 値 {1}、ウォッチ、デバッグ", + "functionBreakpointPlaceholder": "中断対象の関数", + "functionBreakPointInputAriaLabel": "関数ブレークポイントを入力します", + "functionBreakpointsNotSupported": "このデバッグの種類では関数ブレークポイントはサポートされていません", + "breakpointAriaLabel": "ブレークポイント行 {0} {1}、ブレークポイント、デバッグ", + "functionBreakpointAriaLabel": "関数ブレークポイント {0}、ブレークポイント、デバッグ", + "exceptionBreakpointAriaLabel": "例外ブレークポイント {0}、ブレークポイント、デバッグ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..b6a4ffd941 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "変数セクション", + "variablesAriaTreeLabel": "変数のデバッグ", + "expressionsSection": "式セクション", + "watchAriaTreeLabel": "ウォッチ式のデバッグ", + "callstackSection": "コール スタック セクション", + "debugStopped": "{0} で一時停止", + "callStackAriaLabel": "コール スタックのデバッグ", + "breakpointsSection": "ブレークポイント セクション", + "breakpointsAriaTreeLabel": "デバッグ ブレークポイント" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..7e27ea6639 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "値のコピー", + "copy": "コピー", + "copyAll": "すべてコピー", + "copyStackTrace": "呼び出し履歴のコピー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..ea1bd084f9 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "詳細情報", + "unableToLaunchDebugAdapter": "デバッグ アダプターを {0} から起動できません。", + "unableToLaunchDebugAdapterNoArgs": "デバッグ アダプターを起動できません。", + "stoppingDebugAdapter": "{0}。デバッグ アダプターを停止しています。", + "debugAdapterCrash": "デバッグ アダプター プロセスが予期せず終了しました" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..adfc18ec22 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "Read Eval Print Loop パネル", + "actions.repl.historyPrevious": "前の履歴", + "actions.repl.historyNext": "次の履歴", + "actions.repl.acceptInput": "REPL での入力を反映", + "actions.repl.copyAll": "デバッグ: コンソールをすべてコピーする" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..dc5cc84fb4 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "最初の評価からオブジェクトの状態がキャプチャされます", + "replVariableAriaLabel": "変数 {0} に値 {1} があります、Read Eval Print Loop、デバッグ", + "replExpressionAriaLabel": "式 {0} に値 {1} があります、Read Eval Print Loop、デバッグ", + "replValueOutputAriaLabel": "{0}、Read Eval Print Loop、デバッグ", + "replKeyValueOutputAriaLabel": "出力変数 {0} に値 {1} があります、Read Eval Print Loop、デバッグ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..12276b8646 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "プログラムをデバッグしているときのステータス バーの背景色。ステータス バーはウィンドウの下部に表示されます", + "statusBarDebuggingForeground": "プログラムをデバッグしているときのステータス バーの前景色。ステータス バーはウィンドウの下部に表示されます", + "statusBarDebuggingBorder": "プログラムをデバッグしているときのサイドバーおよびエディターを隔てるステータス バーの境界線の色。ステータス バーはウィンドウの下部に表示されます" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..f643dd4f30 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "デバッグ対象", + "debug.terminal.not.available.error": "統合ターミナルを使用できません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..2663fce701 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "デバッグ アダプターの実行可能ファイル '{0}' がありません。", + "debugAdapterCannotDetermineExecutable": "デバッグ アダプター '{0}' の実行可能ファイルを判別できません。", + "launch.config.comment1": "IntelliSense を使用して利用可能な属性を学べます。", + "launch.config.comment2": "既存の属性の説明をホバーして表示します。", + "launch.config.comment3": "詳細情報は次を確認してください: {0}", + "debugType": "構成の種類。", + "debugTypeNotRecognised": "デバッグの種類は認識されませんでした。対応するデバッグの拡張機能がインストールされており、有効になっていることを確認してください。", + "node2NotSupported": "\"node2\" はサポートされていません。代わりに \"node\" を使用し、\"protocol\" 属性を \"inspector\" に設定してください。", + "debugName": "構成の名前。起動構成のドロップダウン メニューに表示されます。", + "debugRequest": "構成の要求の種類。\"launch\" または \"attach\" です。", + "debugServer": "デバッグ拡張機能の開発のみ。ポートが指定の VS Code の場合、サーバー モードで実行中のデバッグ アダプターへの接続が試行されます。", + "debugPrelaunchTask": "デバッグ セッションの開始前に実行するタスク。", + "debugWindowsConfiguration": "Windows 固有の起動構成の属性。", + "debugOSXConfiguration": "OS X 固有の起動構成の属性。", + "debugLinuxConfiguration": "Linux 固有の起動構成の属性。", + "deprecatedVariables": "'env.'、'config.'、'command.' は使用されていません。代わりに、'env:'、'config:'、'command:' を使用してください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..c09c2074f4 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "Emmet コマンドの表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..62b0a5b0c9 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: バランス (内側)", + "balanceOutward": "Emmet: バランス (外側)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..8bb92cb94d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: 前の編集点に移動する", + "nextEditPoint": "Emmet: 次の編集点に移動する" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..02ebed8c72 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: 数式の評価" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..5693192b95 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: 略語の展開" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..4e46a5969a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: 0.1 ずつ増加", + "incrementNumberByOne": "Emmet: 1 ずつ増加", + "incrementNumberByTen": "Emmet: 10 ずつ増加", + "decrementNumberByOneTenth": "Emmet: 0.1 ずつ減少", + "decrementNumberByOne": "Emmet: 1 減少", + "decrementNumberByTen": "Emmet: 10 ずつ減少" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..558626176f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: 一致するペアに移動" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..20ef21baa5 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: 行のマージ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..45a02c1555 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: CSS 値の反転" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..e2689aaa17 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: タグの削除" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..28e819bfb4 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: 前の項目の選択", + "selectNextItem": "Emmet: 次の項目の選択" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..2784d2ddfe --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: タグの分割/結合" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..ffb83036e1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: コメントの表示/非表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..7819f9cb92 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: イメージ サイズの更新" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..46d0a93d3a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: タグの更新", + "enterTag": "タグの入力", + "tag": "タグ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..24609fe471 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: 省略形でラップ", + "enterAbbreviation": "省略形の入力", + "abbreviation": "省略形" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..e90948a414 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "有効にすると、TAB キーを押したときに emmet 省略記法が展開されます。emmet.useNewemmet が true に設定されているときは適用されません。", + "emmetPreferences": "Emmet の一部のアクションやリゾルバーの動作の変更に使用される基本設定。emmet.useNewemmet が true に設定されているときは適用されません。", + "emmetSyntaxProfiles": "指定した構文に対してプロファイルを定義するか、特定の規則がある独自のプロファイルをご使用ください。", + "emmetExclude": "emmet 省略記法を展開すべきでない言語の配列。", + "emmetExtensionsPath": "Emmet のプロファイル、スニペット、基本設定を含むフォルダーへのパス。emmet.useNewEmmet が true に設定されている場合、プロファイルのみが拡張パスから受け入れられます。", + "useNewEmmet": "すべての emmet 機能に対して、新しい emmet モジュールをお試しください (最終的に、以前の単一 emmet ライブラリは置き換えられます)。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json new file mode 100644 index 0000000000..62b0a5b0c9 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: バランス (内側)", + "balanceOutward": "Emmet: バランス (外側)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json new file mode 100644 index 0000000000..c814eeeea1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: 前の編集点", + "nextEditPoint": "Emmet: 次の編集点" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..02ebed8c72 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: 数式の評価" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..5693192b95 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: 略語の展開" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..4e46a5969a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: 0.1 ずつ増加", + "incrementNumberByOne": "Emmet: 1 ずつ増加", + "incrementNumberByTen": "Emmet: 10 ずつ増加", + "decrementNumberByOneTenth": "Emmet: 0.1 ずつ減少", + "decrementNumberByOne": "Emmet: 1 減少", + "decrementNumberByTen": "Emmet: 10 ずつ減少" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..558626176f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: 一致するペアに移動" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..20ef21baa5 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: 行のマージ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..45a02c1555 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: CSS 値の反転" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json new file mode 100644 index 0000000000..e2689aaa17 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: タグの削除" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json new file mode 100644 index 0000000000..28e819bfb4 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: 前の項目の選択", + "selectNextItem": "Emmet: 次の項目の選択" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..2784d2ddfe --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: タグの分割/結合" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..ffb83036e1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: コメントの表示/非表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..7819f9cb92 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: イメージ サイズの更新" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json new file mode 100644 index 0000000000..46d0a93d3a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: タグの更新", + "enterTag": "タグの入力", + "tag": "タグ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..24609fe471 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: 省略形でラップ", + "enterAbbreviation": "省略形の入力", + "abbreviation": "省略形" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json new file mode 100644 index 0000000000..1e84ed7766 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "これをオンにすると、TAB キーを押したときに emmet 省略記法が展開されます.", + "emmetPreferences": "Emmet の一部のアクションやリゾルバーの動作の変更に使用される基本設定。", + "emmetSyntaxProfiles": "指定した構文に対してプロファイルを定義するか、特定の規則がある独自のプロファイルをご使用ください。", + "emmetExclude": "emmet 省略記法を展開すべきでない言語の配列。", + "emmetExtensionsPath": "Emmet のプロファイル、スニペット、基本設定を含むフォルダーへのパス" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..86eeb537bb --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "外部ターミナル", + "explorer.openInTerminalKind": "起動するターミナルの種類をカスタマイズします。", + "terminal.external.windowsExec": "どのターミナルを Windows で実行するかをカスタマイズします。", + "terminal.external.osxExec": "どのターミナル アプリケーションを OS X で実行するかをカスタマイズします。", + "terminal.external.linuxExec": "どのターミナルを Linux で実行するかをカスタマイズします。", + "globalConsoleActionWin": "新しいコマンド プロンプトを開く", + "globalConsoleActionMacLinux": "新しいターミナルを開く", + "scopedConsoleActionWin": "コマンド プロンプトで開く", + "scopedConsoleActionMacLinux": "ターミナルで開く", + "openFolderInIntegratedTerminal": "ターミナルで開く" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..7a151c0637 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "外部ターミナル", + "terminal.external.windowsExec": "どのターミナルを Windows で実行するかをカスタマイズします。", + "terminal.external.osxExec": "どのターミナル アプリケーションを OS X で実行するかをカスタマイズします。", + "terminal.external.linuxExec": "どのターミナルを Linux で実行するかをカスタマイズします。", + "globalConsoleActionWin": "新しいコマンド プロンプトを開く", + "globalConsoleActionMacLinux": "新しいターミナルを開く", + "scopedConsoleActionWin": "コマンド プロンプトで開く", + "scopedConsoleActionMacLinux": "ターミナルで開く" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/jpn/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..c7747d7d26 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "VS Code コンソール", + "mac.terminal.script.failed": "スクリプト '{0}' が終了コード {1} で失敗しました", + "mac.terminal.type.not.supported": "'{0}' はサポートされていません", + "press.any.key": "続行するには、任意のキーを押してください...", + "linux.term.failed": "'{0}' が終了コード {1} で失敗しました" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..c6c1b26b63 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "カスタム ビューを提供します", + "vscode.extension.contributes.view.id": "vscode.workspace.createTreeView を介して生成した、ビューを認識するための一意の ID", + "vscode.extension.contributes.view.label": "ビューの表示に使用する、他人が解釈できる文字列", + "vscode.extension.contributes.view.icon": "ビュー アイコンへのパス", + "vscode.extension.contributes.views": "複数のカスタム ビューを提供します", + "showViewlet": "{0} を表示", + "view": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..6fb629c305 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "最新の情報に更新" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..15bd5b422d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "ID {providerId} の TreeExplorerNodeProvider は登録されていません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/jpn/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..55ef7d887f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "Tree Explorer セクション" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..5d0c7a8ffa --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "エラー", + "Unknown Dependency": "不明な依存関係:" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..548b81c889 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "拡張機能名", + "extension id": "拡張機能の識別子", + "publisher": "発行者名", + "install count": "インストール数", + "rating": "評価", + "license": "ライセンス", + "details": "詳細", + "contributions": "コントリビューション", + "changelog": "変更ログ", + "dependencies": "依存関係", + "noReadme": "利用できる README はありません。", + "noChangelog": "使用可能な変更ログはありません。", + "noContributions": "コントリビューションはありません", + "noDependencies": "依存関係はありません", + "settings": "設定 ({0})", + "setting name": "名前", + "description": "説明", + "default": "既定", + "debuggers": "デバッガー ({0})", + "debugger name": "名前", + "debugger type": "種類", + "views": "ビュー ({0})", + "view id": "ID", + "view name": "名前", + "view location": "場所", + "themes": "テーマ ({0})", + "JSON Validation": "JSON 検証 ({0})", + "commands": "コマンド ({0})", + "command name": "名前", + "keyboard shortcuts": "キーボード ショートカット", + "menuContexts": "メニュー コンテキスト", + "languages": "言語 ({0})", + "language id": "ID", + "language name": "名前", + "file extensions": "ファイル拡張子", + "grammar": "文章校正", + "snippets": "スニペット" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..2bb955f9e8 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "インストール", + "installing": "インストールしています", + "uninstallAction": "アンインストール", + "Uninstalling": "アンインストールしています", + "updateAction": "更新", + "updateTo": "{0} に更新します", + "enableForWorkspaceAction.label": "有効にする (ワークスペース)", + "enableAlwaysAction.label": "常に有効にする", + "disableForWorkspaceAction.label": "無効にする (ワークスペース)", + "disableAlwaysAction.label": "常に無効にする", + "ManageExtensionAction.uninstallingTooltip": "アンインストールしています", + "enableForWorkspaceAction": "ワークスペース", + "enableGloballyAction": "常に行う", + "enableAction": "有効", + "disableForWorkspaceAction": "ワークスペース", + "disableGloballyAction": "常に行う", + "disableAction": "無効にする", + "checkForUpdates": "更新の確認", + "enableAutoUpdate": "拡張機能の自動更新を有効にする", + "disableAutoUpdate": "拡張機能の自動更新を無効にする", + "updateAll": "すべての拡張機能を更新します", + "reloadAction": "再読み込み", + "postUpdateTooltip": "再読み込みして更新する", + "postUpdateMessage": "このウィンドウを再読み込みして、更新済みの拡張機能 '{0}' をアクティブ化しますか?", + "postEnableTooltip": "再読み込みしてアクティブにする", + "postEnableMessage": "このウィンドウを再度読み込んで、拡張機能 '{0}' をアクティブ化しますか?", + "postDisableTooltip": "再読み込みして非アクティブ化する", + "postDisableMessage": "このウィンドウを再読み込みして、拡張機能 '{0}' を非アクティブ化しますか?", + "postUninstallTooltip": "再読み込みして非アクティブ化する", + "postUninstallMessage": "このウィンドウを再度読み込んで、アンインストール済みの拡張機能 '{0}' を非アクティブ化しますか?", + "reload": "ウィンドウの再読み込み(&&R)", + "toggleExtensionsViewlet": "拡張機能を表示する", + "installExtensions": "拡張機能のインストール", + "showEnabledExtensions": "有効な拡張機能の表示", + "showInstalledExtensions": "インストール済みの拡張機能の表示", + "showDisabledExtensions": "無効な拡張機能の表示", + "clearExtensionsInput": "拡張機能の入力のクリア", + "showOutdatedExtensions": "古くなった拡張機能の表示", + "showPopularExtensions": "人気の拡張機能の表示", + "showRecommendedExtensions": "お勧めの拡張機能を表示", + "showWorkspaceRecommendedExtensions": "ワークスペースのおすすめの拡張機能を表示", + "showRecommendedKeymapExtensions": "推奨のキーマップを表示する", + "showRecommendedKeymapExtensionsShort": "キーマップ", + "showLanguageExtensions": "言語の拡張機能を表示", + "showLanguageExtensionsShort": "言語の拡張機能", + "showAzureExtensions": "Azure 拡張機能の表示", + "showAzureExtensionsShort": "Azure 拡張機能", + "configureWorkspaceRecommendedExtensions": "お勧めの拡張機能の構成 (ワークスペース)", + "ConfigureWorkspaceRecommendations.noWorkspace": "推奨事項はワークスペース フォルダーでのみ利用可能です。", + "OpenExtensionsFile.failed": "'.vscode' ファルダー ({0}) 内に 'extensions.json' ファイルを作成できません。", + "builtin": "ビルトイン", + "disableAll": "インストール済みのすべての拡張機能を無効にする", + "disableAllWorkspace": "このワークスペースのインストール済みの拡張機能をすべて無効にする", + "enableAll": "インストール済みの拡張機能をすべて有効にする", + "enableAllWorkspace": "このワークスペースのインストール済みの拡張機能をすべて有効にする", + "extensionButtonProminentBackground": "際立っているアクション拡張機能のボタンの背景色(例: インストールボタン)。", + "extensionButtonProminentForeground": "際立っているアクション拡張機能のボタンの前景色(例: インストールボタン)。", + "extensionButtonProminentHoverBackground": "際立っているアクション拡張機能のボタンのホバー背景色(例: インストールボタン)。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..d949ed983c --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "拡張機能を管理するには Enter キーを押してください。", + "searchFor": "マーケットプレース内で '{0}' を検索するには、Enter キーを押してください。", + "noExtensionsToInstall": "拡張機能名を入力してください" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..6ccd17a2ef --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "拡張機能", + "app.extensions.json.recommendations": "拡張機能のおすすめ候補の一覧。拡張機能の ID は常に '${publisher}.${name}' です。例: 'vscode.csharp'。", + "app.extension.identifier.errorMessage": "予期される形式 '${publisher}.${name}'。例: 'vscode.csharp'。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..f31bafc25e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "拡張機能: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..de9eafc403 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "このファイルの種類には拡張機能 '{0}' が推奨されます。", + "showRecommendations": "推奨事項を表示", + "neverShowAgain": "今後は表示しない", + "close": "閉じる", + "workspaceRecommended": "このワークスペースには拡張機能の推奨事項があります。", + "ignoreExtensionRecommendations": "すべての拡張機能の推奨事項を無視しますか?", + "ignoreAll": "はい、すべて無視します", + "no": "いいえ", + "cancel": "キャンセル" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..d51dd37331 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "拡張機能の管理", + "galleryExtensionsCommands": "ギャラリー拡張機能のインストール", + "extension": "拡張機能", + "extensions": "拡張機能", + "view": "表示", + "extensionsConfigurationTitle": "拡張機能", + "extensionsAutoUpdate": "拡張機能を自動的に更新します", + "extensionsIgnoreRecommendations": "拡張機能の推奨事項を無視する" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..43e46030a9 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "拡張機能フォルダーを開く", + "installVSIX": "VSIX からのインストール...", + "InstallVSIXAction.success": "拡張機能が正常にインストールされました。有効にするには再起動します。", + "InstallVSIXAction.reloadNow": "今すぐ再度読み込む" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..6f775ebb26 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "キーバインド間の競合を回避するために、他のキーマップ ({0}) を無効にしますか?", + "yes": "はい", + "no": "いいえ", + "betterMergeDisabled": "拡張機能 Better Merge は現在ビルトインです。インストール済みの拡張機能は無効化され、アンインストールできます。", + "uninstall": "アンインストール", + "later": "後続" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..4b9fcad2e1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "Marketplace", + "installedExtensions": "インストール済み", + "searchInstalledExtensions": "インストール済み", + "recommendedExtensions": "推奨", + "searchExtensions": "Marketplace で拡張機能を検索する", + "sort by installs": "並べ替え: インストール数", + "sort by rating": "並べ替え: 評価", + "sort by name": "並べ替え: 名前", + "suggestProxyError": "Marketplace が 'ECONNREFUSED' を返しました。'http.proxy' 設定を確認してください。", + "extensions": "拡張機能", + "outdatedExtensions": "{0} 古くなった拡張機能" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..36e6fdd2df --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "拡張機能", + "no extensions found": "拡張機能が見つかりません", + "suggestProxyError": "Marketplace が 'ECONNREFUSED' を返しました。'http.proxy' 設定を確認してください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json new file mode 100644 index 0000000000..75a86ab00e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "キーバインド間の競合を回避するために、他のキーマップを無効にしますか?", + "yes": "はい", + "no": "いいえ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..c6ec858742 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "'{0}' を有効にするとその依存関係も有効になります。続行しますか?", + "enable": "はい", + "doNotEnable": "いいえ", + "disableDependeciesConfirmation": "'{0}' のみ、またはその依存関係も無効にしますか?", + "disableOnly": "限定", + "disableAll": "すべて", + "cancel": "キャンセル", + "singleDependentError": "拡張機能 '{0}' を無効にできません。これに拡張機能 '{1}' が依存しています。", + "twoDependentsError": "拡張機能 '{0}' を無効にできません。これに拡張機能 '{1}' と '{2}' が依存しています。", + "multipleDependentsError": "拡張機能 '{0}' を無効にできません。これに拡張機能 '{1}'、'{2}'、その他が依存しています。", + "installConfirmation": "'{0}' 拡張機能をインストールしますか?", + "install": "インストール" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/jpn/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..c81b4caf57 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "フィードバックをツイートする", + "label.sendASmile": "フィードバックをツイートしてください。", + "patchedVersion1": "インストールが壊れています。", + "patchedVersion2": "バグを送信する場合には、これを指定してください。", + "sentiment": "ご感想をお聞かせください。", + "smileCaption": "ハッピー", + "frownCaption": "悲しい", + "other ways to contact us": "その他の連絡方法", + "submit a bug": "バグを送信する", + "request a missing feature": "欠落している機能を要求する", + "tell us why?": "理由をお知らせください", + "commentsHeader": "コメント", + "tweet": "ツイートする", + "character left": "文字入力可", + "characters left": "文字入力可", + "feedbackSending": "送信中", + "feedbackSent": "ありがとうございました", + "feedbackSendingError": "もう一度やり直してください" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..03fec62ab0 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "バイナリ ファイル ビューアー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..afdf9b49aa --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "テキスト ファイル エディター", + "createFile": "ファイルの作成", + "fileEditorWithInputAriaLabel": "{0}。テキスト ファイル エディター。", + "fileEditorAriaLabel": "テキスト ファイル エディター。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..281a451bed --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "フォルダー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..53903c41cd --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "ファイル", + "revealInSideBar": "サイド バーに表示", + "acceptLocalChanges": "変更を使用してディスクの内容を上書き", + "revertLocalChanges": "変更を破棄してディスク上の内容に戻る" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..6961978047 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "再試行", + "rename": "名前変更", + "newFile": "新しいファイル", + "newFolder": "新しいフォルダー", + "openFolderFirst": "フォルダー内にファイルやフォルダーを作成するには、フォルダーをまず開く必要があります。", + "newUntitledFile": "無題の新規ファイル", + "createNewFile": "新しいファイル", + "createNewFolder": "新しいフォルダー", + "deleteButtonLabelRecycleBin": "ごみ箱に移動(&&M)", + "deleteButtonLabelTrash": "ゴミ箱に移動(&&M)", + "deleteButtonLabel": "削除(&&D)", + "dirtyMessageFolderOneDelete": "保存されていない変更がある 1 個のファイルを含むフォルダーを削除します。続行しますか?", + "dirtyMessageFolderDelete": "保存されていない変更があるファイルを {0} 個含むフォルダーを削除します。続行しますか?", + "dirtyMessageFileDelete": "保存されていない変更があるファイルを削除します。続行しますか?", + "dirtyWarning": "保存しないと変更内容が失われます。", + "confirmMoveTrashMessageFolder": "'{0}' とその内容を削除しますか?", + "confirmMoveTrashMessageFile": "'{0}' を削除しますか?", + "undoBin": "ごみ箱から復元できます。", + "undoTrash": "ゴミ箱から復元できます。", + "confirmDeleteMessageFolder": "'{0}' とその内容を完全に削除してもよろしいですか?", + "confirmDeleteMessageFile": "'{0}' を完全に削除してもよろしいですか?", + "irreversible": "このアクションは元に戻すことができません。", + "permDelete": "完全に削除", + "delete": "削除", + "importFiles": "ファイルのインポート", + "confirmOverwrite": "保存先のフォルダーに同じ名前のファイルまたはフォルダーが既に存在します。置き換えてもよろしいですか?", + "replaceButtonLabel": "置換(&&R)", + "copyFile": "コピー", + "pasteFile": "貼り付け", + "duplicateFile": "重複", + "openToSide": "横に並べて開く", + "compareSource": "比較対象の選択", + "globalCompareFile": "アクティブ ファイルを比較しています...", + "pickHistory": "比較対象として、以前に開いたファイルを選択する", + "unableToFileToCompare": "選択されたファイルを '{0}' と比較できません。", + "openFileToCompare": "まずファイルを開いてから別のファイルと比較してください", + "compareWith": "'{0}' と '{1}' を比較", + "compareFiles": "ファイルの比較", + "refresh": "最新の情報に更新", + "save": "保存", + "saveAs": "名前を付けて保存...", + "saveAll": "すべて保存", + "saveAllInGroup": "グループ内のすべてを保存する", + "saveFiles": "ダーティ ファイルを保存", + "revert": "ファイルを元に戻す", + "focusOpenEditors": "開いているエディターのビューにフォーカスする", + "focusFilesExplorer": "ファイル エクスプローラーにフォーカスを置く", + "showInExplorer": "アクティブ ファイルをサイド バーに表示", + "openFileToShow": "エクスプローラーでファイルを表示するには、ファイルをまず開く必要があります", + "collapseExplorerFolders": "エクスプローラーのフォルダーを折りたたむ", + "refreshExplorer": "エクスプローラーを最新表示する", + "openFile": "ファイルを開く...", + "openFileInNewWindow": "新しいウィンドウでアクティブ ファイルを開く", + "openFileToShowInNewWindow": "まずファイルを開いてから新しいウィンドウで開きます", + "revealInWindows": "エクスプローラーで表示します", + "revealInMac": "Finder で表示します", + "openContainer": "このアイテムのフォルダーを開く", + "revealActiveFileInWindows": "Windows エクスプローラーでアクティブ ファイルを表示する", + "revealActiveFileInMac": "Finder でアクティブ ファイルを表示する", + "openActiveFileContainer": "アクティブ ファイルを含んでいるフォルダーを開く", + "copyPath": "パスのコピー", + "copyPathOfActive": "アクティブ ファイルのパスのコピー", + "emptyFileNameError": "ファイルまたはフォルダーの名前を指定する必要があります。", + "fileNameExistsError": "**{0}** というファイルまたはフォルダーはこの場所に既に存在します。別の名前を指定してください。", + "invalidFileNameError": "名前 **{0}** がファイル名またはフォルダー名として無効です。別の名前を指定してください。", + "filePathTooLongError": "名前 **{0}** のパスが長すぎます。名前を短くしてください。", + "compareWithSaved": "保存済みファイルと作業中のファイルを比較", + "modifiedLabel": "{0} (ローカル) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..3109d754fd --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "まずファイルを開いてからそのパスをコピーします", + "openFileToReveal": "まずファイルを開いてから表示します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..e2ae5a7fc7 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "エクスプローラーを表示", + "explore": "エクスプローラー", + "view": "表示", + "textFileEditor": "テキスト ファイル エディター", + "binaryFileEditor": "バイナリ ファイル エディター", + "filesConfigurationTitle": "ファイル", + "exclude": "ファイルとフォルダーを除外するための glob パターンを構成します。", + "files.exclude.boolean": "ファイル パスの照合基準となる glob パターン。これを true または false に設定すると、パターンがそれぞれ有効/無効になります。", + "files.exclude.when": "一致するファイルの兄弟をさらにチェックします。一致するファイル名の変数として $(basename) を使用します。", + "associations": "言語に対するファイルの関連付け (例 \"*.extension\": \"html\") を構成します。これらの関連付けは、インストールされている言語の既定の関連付けより優先されます。", + "encoding": "ファイルの読み取り/書き込みで使用する既定の文字セット エンコーディング。", + "autoGuessEncoding": "有効な場合、ファイルを開くときに文字セット エンコードを推測します", + "eol": "既定の改行文字。LF の場合には \\n を CRLF の場合には \\r\\n を使用してください。", + "trimTrailingWhitespace": "有効にすると、ファイルの保存時に末尾の空白をトリミングします。", + "insertFinalNewline": "有効にすると、ファイルの保存時に最新の行を末尾に挿入します。", + "files.autoSave.off": "ダーティ ファイルを自動的に保存することはしません。", + "files.autoSave.afterDelay": "'files.autoSaveDelay' で構成された時間の経過後に、ダーティ ファイルを自動的に保存します。", + "files.autoSave.onFocusChange": "エディターがフォーカスを失った時点で、ダーティ ファイルを自動的に保存します。", + "files.autoSave.onWindowChange": "ウィンドウがフォーカスを失った時点で、ダーティ ファイルを自動的に保存します。", + "autoSave": "ダーティ ファイルの自動保存を制御します。有効な値: '{0}'、'{1}'、'{2}' (エディターがフォーカスを失います)、'{3}' (ウィンドウがフォーカスを失います)。'{4}' に設定すると、'files.autoSaveDelay' で遅延を構成できます。", + "autoSaveDelay": "ダーティ ファイルの自動保存の遅延をミリ秒単位で制御します。'files.autoSave' が '{0}' に設定されている場合のみ適用されます", + "watcherExclude": "ファイル監視から除外するファイル パスの glob パターンを設定します。パターンは絶対パスで一致する必要があります (つまり、適切に一致するには、プレフィックス ** を指定するか、完全パスを指定します\n)。この設定を変更した場合は、再起動が必要になります。始動時に Code が消費する CPU 時間が多い場合は、大きいフォルダーを除外すれば初期の負荷を減らすことができます。", + "hotExit.off": "Hot Exit を無効にします。", + "hotExit.onExit": "アプリケーションが閉じると (Windows/Linux で最後のウィンドウが閉じるとき、または workbench.action.quit コマンドがトリガーされるとき (コマンド パレット、キー バインド、メニュー))、Hot Exit がトリガーされます。バックアップされているすべてのウィンドウは、次の起動時に復元されます。", + "hotExit.onExitAndWindowClose": "アプリケーションが閉じると (Windows/Linux で最後のウィンドウが閉じるとき、または workbench.action.quit コマンドがトリガーするとき (コマンド パレット、キー バインド、メニュー))、Hot Exit がトリガーされます。また、フォルダーが開かれているウィンドウについても、それが最後のウィンドウかどうかに関係なく、Hot Exit がトリガーされます。フォルダーが開かれていないウィンドウはすべて、次回の起動時に復元されます。フォルダーのウィンドウをシャットダウン前と同じ状態に復元するには、\"window.restoreWindows\" を \"all\" に設定します。", + "hotExit": "エディターを終了するときに保存を確認するダイアログを省略し、保存されていないファイルをセッション後も保持するかどうかを制御します。", + "useExperimentalFileWatcher": "新しい試験的な File Watcher を使用します。", + "defaultLanguage": "新しいファイルに割り当てられる既定の言語モード。", + "editorConfigurationTitle": "エディター", + "formatOnSave": "ファイルを保存するときにフォーマットしてください。フォーマッタを使用可能にして、ファイルを自動保存せず、エディターをシャットダウンしないでください。", + "explorerConfigurationTitle": "エクスプローラー", + "openEditorsVisible": "[開いているエディター] ウィンドウに表示されているエディターの数。0 に設定するとウィンドウが非表示になります。", + "dynamicHeight": "開いているエディターのセクションの高さを要素の数に合わせて動的に調整するかどうかを制御します。", + "autoReveal": "エクスプローラーでファイルを開くとき、自動的にファイルの内容を表示して選択するかどうかを制御します。", + "enableDragAndDrop": "ドラッグ アンド ドロップを使用したファイルとフォルダーの移動をエクスプローラーが許可するかどうかを制御します。", + "sortOrder.default": "ファイルとフォルダーをアルファベット順に名前で並び替えます。フォルダーはファイルの前に表示されます。", + "sortOrder.mixed": "ファイルとフォルダーをアルファベット順に名前で並び替えます。ファイルはフォルダーと混交して表示されます。", + "sortOrder.filesFirst": "ファイルとフォルダーをアルファベット順に名前で並び替えます。ファイルはフォルダーの前に表示されます。", + "sortOrder.type": "ファイルとフォルダーをアルファベット順に拡張子で並び替えます。フォルダーはファイルの前に表示されます。", + "sortOrder.modified": "ファイルとフォルダーを降順に最終更新日で並び替えます。フォルダーはファイルの前に表示されます。", + "sortOrder": "エクスプローラー内のファイルとフォルダーの並び順を制御します。既定の並び順に加えて、'mixed' (ファイルとフォルダーを混交した並び順)、' type' (ファイルの種類順)、' modified' (最終更新日時順)、または 'filesFirst' (フォルダーの前にファイルを並べる) のいずれかの並び順に設定できます。 " +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..30b121fe4f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "破棄", + "overwrite": "上書き", + "retry": "再試行", + "readonlySaveError": "'{0}' の保存に失敗しました。ファイルが書き込み禁止になっています。[上書き] を選択して保護を解除してください。", + "genericSaveError": "'{0}' の保存に失敗しました: {1}", + "staleSaveError": "'{0} の保存に失敗しました。ディスクの内容の方が新しくなっています。[比較] をクリックしてご使用のバージョンをディスク上のバージョンと比較してください。", + "compareChanges": "比較", + "saveConflictDiffLabel": "{0} (ディスク上) ↔ {1} ({2} 内) - 保存の競合を解決", + "userGuide": "右側のエディター ツール バーの操作で、変更を [元に戻す] か、ディスクの内容を変更内容で [上書き] します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..cbcae1e2a7 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "開いているフォルダーがありません", + "explorerSection": "ファイル エクスプローラー セクション", + "noWorkspaceHelp": "まだフォルダーを開いていません。", + "openFolder": "フォルダーを開く" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..98d39732b6 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "ファイル エクスプローラー セクション", + "treeAriaLabel": "ファイル エクスプローラー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..a13e15d37c --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "ファイル名を入力します。Enter キーを押して確認するか、Esc キーを押して取り消します。", + "filesExplorerViewerAriaLabel": "{0}、ファイル エクスプローラー", + "dropFolders": "ワークスペースにフォルダーを追加しますか?", + "dropFolder": "ワークスペースにフォルダーを追加しますか?", + "addFolders": "フォルダーの追加(&&A)", + "addFolder": "フォルダーの追加(&&A)", + "confirmOverwriteMessage": "'{0}' は保存先フォルダーに既に存在します。置き換えてもよろしいですか。", + "irreversible": "このアクションは元に戻すことができません。", + "replaceButtonLabel": "置換(&&R)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..cf2df10f2a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "開いているエディター", + "openEditosrSection": "[開いているエディター] セクション", + "treeAriaLabel": "開いているエディター: アクティブなファイルのリスト", + "dirtyCounter": "未保存 ({0})" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..053d1ed731 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}、エディター グループ", + "openEditorAriaLabel": "{0}、開いているエディター", + "saveAll": "すべて保存", + "closeAllUnmodified": "未変更を閉じる", + "closeAll": "すべて閉じる", + "compareWithSaved": "保存済みと比較", + "close": "閉じる", + "closeOthers": "その他を閉じる" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..da6d7ebd9e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0} 個の未保存のファイル" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..32c2e3e166 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (deleted from disk)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json new file mode 100644 index 0000000000..cd710ca0f2 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "switchToChangesView": "変更の表示に切り替え", + "openInEditor": "エディター ビューに切り替える", + "workbenchStage": "ステージ", + "workbenchUnstage": "ステージング解除", + "stageSelectedLines": "選択した行のステージング", + "unstageSelectedLines": "選択した行のステージング解除", + "revertSelectedLines": "選択した行を元に戻す", + "confirmRevertMessage": "選択した変更を元に戻しますか?", + "irreversible": "このアクションは元に戻すことができません。", + "revertChangesLabel": "変更を元に戻す(&&R)", + "openChange": "変更を開く", + "openFile": "ファイルを開く", + "git": "Git" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/gitActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/gitActions.i18n.json new file mode 100644 index 0000000000..9c1c52f1bc --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/gitActions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openChange": "変更を開く", + "openFile": "ファイルを開く", + "init": "初期化", + "refresh": "最新の情報に更新", + "stageChanges": "ステージ", + "stageAllChanges": "すべてステージング", + "confirmUndoMessage": "変更をすべて取り除いてもよろしいですか?", + "confirmUndoAllOne": "{0} ファイルにステージングされていない変更があります。\n\nこのアクションは元に戻すことができません。", + "confirmUndoAllMultiple": "{0} ファイルにステージングされていない変更があります。\n\nこのアクションは元に戻すことができません", + "cleanChangesLabel": "変更を取り除く(&&C)", + "confirmUndo": "'{0}' の変更を取り除いてよろしいですか?", + "irreversible": "このアクションは元に戻すことができません。", + "undoChanges": "取り除く", + "undoAllChanges": "すべて取り除く", + "unstage": "ステージング解除", + "unstageAllChanges": "すべてステージング解除", + "dirtyTreeCheckout": "チェックアウトできません。まず作業をコミットまたは一時退避してください。", + "commitStaged": "ステージング済みをコミット", + "commitStagedAmend": "コミットしてステージング (修正)", + "commitStagedSignedOff": "コミットしてステージング (サインオフ)", + "commit": "Commit", + "commitMessage": "コミット メッセージ", + "commitAll": "すべてコミット", + "commitAllSignedOff": "すべてコミット (サインオフ)", + "commitAll2": "すべてコミット", + "commitStaged2": "ステージング済みをコミット", + "dirtyTreePull": "プルできません。まず作業をコミットまたは一時退避してください。", + "authFailed": "Git リモートで認証が失敗しました。", + "pushToRemote": "プッシュ先...", + "pushToRemotePickMessage": "リモートを選んで、ブランチ '{0}' を次にプッシュします:", + "publish": "公開", + "confirmPublishMessage": "'{0}' を '{1}' に発行してよろしいですか?", + "confirmPublishMessageButton": "公開(&&P)", + "publishPickMessage": "リモートを選んで、ブランチ '{0}' を次に公開します:", + "sync is unpredictable": "このアクションはコミットを '{0}' との間でプッシュしたりプルしたりします。", + "ok": "OK", + "cancel": "キャンセル", + "never again": "OK、今後は表示しない", + "undoLastCommit": "前回のコミットを元に戻す" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json new file mode 100644 index 0000000000..33a3ab822c --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refAriaLabel": "{0}、Git", + "checkoutBranch": "{0} のブランチ", + "checkoutRemoteBranch": "{0} でのリモート ブランチ", + "checkoutTag": "{0} のタグ", + "alreadyCheckedOut": "ブランチ {0} は既に現在のブランチになっています", + "branchAriaLabel": "{0}、Git ブランチ", + "createBranch": "ブランチ {0} を作成します", + "noBranches": "他のブランチがありません", + "notValidBranchName": "有効なブランチ名を指定してください" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/gitServices.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/gitServices.i18n.json new file mode 100644 index 0000000000..8acfeca37b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/gitServices.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cantOpen": "この Git リソースを開けません。", + "gitIndexChanges": "{0} (index) ↔ {1}", + "gitIndexChangesDesc": "{0} - インデックスでの変更", + "gitIndexChangesRenamed": "{0} ← {1}", + "gitIndexChangesRenamedDesc": "{0} - 名前変更済み - インデックスでの変更", + "workingTreeChanges": "{0} (HEAD) ↔ {1}", + "workingTreeChangesDesc": "{0} - 作業ツリーでの変更", + "gitMergeChanges": "{0} (merge) ↔ {1}", + "gitMergeChangesDesc": "{0} - 変更のマージ", + "updateGit": "git {0} がインストールされているようです。Code は 2.0.0 以上の git で最適に動作します。", + "download": "ダウンロード", + "neverShowAgain": "今後は表示しない", + "configureUsernameEmail": "Git ユーザー名と電子メールを構成してください。", + "badConfigFile": "Git {0}", + "unmergedChanges": "変更をコミットする前に、まずマージされていない変更を解決する必要があります。", + "showOutput": "出力の表示", + "cancel": "キャンセル", + "checkNativeConsole": "Git の操作を実行しているときに問題が発生しました。出力を確認するか、コンソールを使用してリポジトリの状態を確認してください。", + "changesFromIndex": "{0} (index)", + "changesFromIndexDesc": "{0} - インデックスでの変更", + "changesFromTree": "{0} ({1})", + "changesFromTreeDesc": "{0} - {1} での変更", + "cantOpenResource": "この Git リソースを開けません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json new file mode 100644 index 0000000000..251aeb8274 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "publishBranch": "ブランチの発行", + "syncBranch": "変更の同期", + "gitNotEnabled": "このワークスペースでは Git が有効になっていません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json new file mode 100644 index 0000000000..b19b13746b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gitProgressBadge": "実行中の Git の状態", + "gitPendingChangesBadge": "{0} 個の保留中の変更", + "toggleGitViewlet": "Git を表示", + "git": "Git", + "view": "表示", + "gitCommands": "Git コマンド", + "gitConfigurationTitle": "Git", + "gitEnabled": "は Git 対応です", + "gitPath": "Git 実行可能ファイルのパス", + "gitAutoRefresh": "自動更新が有効かどうか", + "gitAutoFetch": "自動フェッチの有効/無効。", + "gitLongCommit": "長いコミット メッセージを警告するかどうか。", + "gitLargeRepos": "Code による大規模なリポジトリの管理を常に許可します。", + "confirmSync": "Git リポジトリを同期する前に確認します。", + "countBadge": "Git バッジ カウンターを制御します。", + "checkoutType": "一覧表示する分岐の種類を制御します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json new file mode 100644 index 0000000000..161d0c8c9a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "needMessage": "コミット メッセージを入力してください。**{0}** を押すといつでも変更をコミットできます。ステージング済みの変更がある場合は、それらの変更だけがコミットされます。それ以外の場合は、すべての変更がコミットされます。", + "nothingToCommit": "コミットする変更があった時点で、コミット メッセージを入力するか、**{0}** を押して変更をコミットしてください。ステージング済みの変更がある場合は、それらの変更だけがコミットされます。それ以外の場合は、すべての変更がコミットされます。", + "longCommit": "コミットの最初の行は 50 文字を超えない長さにすることをお勧めします。補足情報は追加の行を使用して記載できます。", + "commitMessage": "Message (press {0} to commit)", + "commitMessageAriaLabel": "Git: コミット メッセージを入力し、{0} を押してコミットしてください", + "treeAriaLabel": "Git 変更の表示", + "showOutput": "Git 出力の表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json new file mode 100644 index 0000000000..1a8029c516 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stagedChanges": "ステージング済みの変更", + "allChanges": "変更", + "mergeChanges": "変更のマージ", + "outsideOfWorkspace": "このファイルは現在のワークスペースの外にあります。", + "modified-char": "M", + "added-char": "A", + "deleted-char": "D", + "renamed-char": "R", + "copied-char": "C", + "untracked-char": "U", + "ignored-char": "!", + "title-index-modified": "インデックスで変更済み", + "title-modified": "変更済み", + "title-index-added": "インデックスに追加済み", + "title-index-deleted": "インデックスで削除済み", + "title-deleted": "削除済み", + "title-index-renamed": "インデックスで名前を変更済み", + "title-index-copied": "インデックスでコピー済み", + "title-untracked": "追跡対象外", + "title-ignored": "無視", + "title-conflict-both-deleted": "競合: 両方削除", + "title-conflict-added-by-us": "競合: こちらが追加", + "title-conflict-deleted-by-them": "競合: 他者が削除", + "title-conflict-added-by-them": "競合: 他者が追加", + "title-conflict-deleted-by-us": "競合: こちらが削除", + "title-conflict-both-added": "競合: 両方追加", + "title-conflict-both-modified": "競合: 両方変更", + "fileStatusAriaLabel": "フォルダー {1} 内のファイル {0} の状態: {2}、Git", + "ariaLabelStagedChanges": "ステージング済みの変更、Git", + "ariaLabelChanges": "変更、Git", + "ariaLabelMerge": "マージ、Git" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json new file mode 100644 index 0000000000..05660929cb --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disabled": "設定で Git が無効になっています。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json new file mode 100644 index 0000000000..3ad3830acb --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noGit": "このワークスペースはまだ Git ソース管理下にありません。", + "gitinit": "Git リポジトリの初期化" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json new file mode 100644 index 0000000000..0a4b79ea67 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "macInstallWith": "{0} を使用してインストールするか、{1} からダウンロードするか、ターミナルのプロンプトで単に {3} と入力して {2} コマンド ライン開発者ツールをインストールできます。", + "winInstallWith": "{0} を使用してインストールするか、{1} からダウンロードできます。", + "linuxDownloadFrom": "{0} からダウンロードできます。", + "downloadFrom": "{0} からダウンロードできます。", + "looksLike": "システムに Git がインストールされていない可能性があります。", + "pleaseRestart": "Git をインストールし終えたら、VSCode を再起動してください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json new file mode 100644 index 0000000000..e5434d737f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "huge": "リポジトリに有効な変更が数多くある可能性があります。\nこれにより、コードの速度が大幅に低下することがあります。", + "setting": "次の設定を使用してこの警告を完全に無効にできます:", + "allo": "大規模なリポジトリを許可します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json new file mode 100644 index 0000000000..619d8ed72b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrongRoot": "このディレクトリは Git リポジトリに含まれているようです。", + "pleaseRestart": "Git 機能にアクセスするには、リポジトリのルート ディレクトリを開きます。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json new file mode 100644 index 0000000000..b03cff9ec8 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspaceHelp": "まだフォルダーを開いていません。", + "pleaseRestart": "Git 機能にアクセスするには、Git リポジトリのあるフォルダーを開きます。", + "openFolder": "フォルダーを開く" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json new file mode 100644 index 0000000000..63ae704762 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSCMViewlet": "SCM を表示", + "git": "Git" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json new file mode 100644 index 0000000000..a5c6d1fc8f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "valid": "有効な Git リポジトリの URL を入力してください", + "url": "リポジトリ URL", + "directory": "宛先の複製ディレクトリ", + "cloning": "リポジトリ '{0}' を複製しています...", + "already exists": "宛先レポジトリは既に存在しています。複製するための別のディレクトリを選択してください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json new file mode 100644 index 0000000000..91fbae2148 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "git": "Git" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/git/node/git.lib.i18n.json b/i18n/jpn/src/vs/workbench/parts/git/node/git.lib.i18n.json new file mode 100644 index 0000000000..e44d47bfda --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/git/node/git.lib.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorBuffer": "Git からファイルを開くことができません", + "fileBinaryError": "ファイルはバイナリのようなので、テキストとして開くことができません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..20dca8d502 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "HTML プレビュー", + "devtools.webview": "開発者: Web ビュー ツール" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/jpn/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..5ec40210e1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "エディター入力が正しくありません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/jpn/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..41b4b5e687 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "開発者: Web ビュー ツール" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/jpn/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..0bd0d9c32d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "表示", + "problems.view.show.label": "問題を表示する", + "problems.panel.configuration.title": "問題ビュー", + "problems.panel.configuration.autoreveal": "ファイルを開くときに問題ビューに自動的にそのファイルを表示するかどうかを制御します", + "markers.panel.title.problems": "問題", + "markers.panel.aria.label.problems.tree": "ファイル別にグループ化した問題", + "markers.panel.no.problems.build": "現時点で問題はワークスペースで検出されていません。", + "markers.panel.no.problems.filters": "指定されたフィルター条件による結果はありません", + "markers.panel.action.filter": "問題のフィルター処理", + "markers.panel.filter.placeholder": "種類またはテキストでフィルター処理", + "markers.panel.filter.errors": "エラー", + "markers.panel.filter.warnings": "警告", + "markers.panel.filter.infos": "情報", + "markers.panel.single.error.label": "エラー 1", + "markers.panel.multiple.errors.label": "エラー {0}", + "markers.panel.single.warning.label": "警告 1", + "markers.panel.multiple.warnings.label": "警告 {0}", + "markers.panel.single.info.label": "情報 1", + "markers.panel.multiple.infos.label": "情報 {0}", + "markers.panel.single.unknown.label": "不明 1", + "markers.panel.multiple.unknowns.label": "不明 {0}", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "{0} (問題あり {1})", + "problems.tree.aria.label.error.marker": "{0}: {1} によって生成されたエラー (行 {2}、文字 {3})", + "problems.tree.aria.label.error.marker.nosource": "エラー: {0} (行 {1}、文字 {2})", + "problems.tree.aria.label.warning.marker": "{0}: {1} によって生成された警告 (行 {2}、文字 {3})", + "problems.tree.aria.label.warning.marker.nosource": "警告: {0} (行 {1}、文字 {2})", + "problems.tree.aria.label.info.marker": "{0}: {1} によって生成された情報 (行 {2}、文字 {3})", + "problems.tree.aria.label.info.marker.nosource": "情報: {0} (行 {1}、文字 {2})", + "problems.tree.aria.label.marker": "{0} によって生成された問題: {1} (行 {2}、文字 {3})", + "problems.tree.aria.label.marker.nosource": "問題: {0} (行 {1}、文字 {2})", + "errors.warnings.show.label": "エラーと警告の表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/jpn/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..a512a433b4 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "コピー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..95e3924869 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "短いフィードバック アンケートにご協力をお願いできますか?", + "takeSurvey": "アンケートの実施", + "remindLater": "後で通知する", + "neverAgain": "今後は表示しない" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..36ec8b4c17 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "出力", + "viewCategory": "表示", + "clearOutput.label": "出力のクリア" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..bb71517ef3 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "出力の切り替え", + "clearOutput": "出力のクリア", + "toggleOutputScrollLock": "出力スクロール ロックの切り替え", + "switchToOutput.label": "出力に切り替え" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/jpn/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..12f5689095 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0}、出力パネル", + "outputPanelAriaLabel": "出力パネル" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/jpn/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..f734648395 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "出力", + "channel": "'{0}' の" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..2dfbca9c94 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "スタートアップの遅延が検出されました", + "slow.detail": "スタートアップが遅かったとのこと、申し訳ございません。プロファイルを有効にして、'{0}' を再起動し、プロファイルを共有してください。スタートアップの改善のために参考にさせていただきます。", + "prof.message": "プロファイルが正常に作成されました。", + "prof.detail": "案件を作成し、手動で次のファイルを添付してください:\\n{0}", + "prof.restartAndFileIssue": "問題を作成して再起動", + "prof.restart": "再起動", + "prof.thanks": "ご協力いただき、ありがとうございます。", + "prof.detail.restart": "'{0}' を引き続き使用するには、最後の再起動が必要です。 改めてあなたの貢献に感謝します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..01397ce6ce --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "任意のキーの組み合わせを押し、Enter キーを押します。キャンセルするには Esc キーを押してください。", + "defineKeybinding.chordsTo": "次へのコード:" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..ab62d7168f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "キーボード ショートカット", + "SearchKeybindings.AriaLabel": "キー バインドの検索", + "SearchKeybindings.Placeholder": "キー バインドの検索", + "sortByPrecedene": "優先順位で並べ替え", + "header-message": "高度なカスタマイズを行うには、次を開いて編集:", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "キー バインド", + "changeLabel": "キー バインドの変更", + "addLabel": "キー バインドの追加", + "removeLabel": "キー バインドの削除", + "resetLabel": "キー バインドのリセット", + "showConflictsLabel": "競合の表示", + "copyLabel": "コピー", + "error": "キー バインドの編集中にエラー '{0}' が発生しました。'keybindings.json' ファイルを開いてご確認ください。", + "command": "コマンド", + "keybinding": "キー バインド", + "source": "ソース", + "when": "タイミング", + "editKeybindingLabelWithKey": "キー バインドの変更 {0}", + "editKeybindingLabel": "キー バインドの変更", + "addKeybindingLabelWithKey": "キー バインドの追加 {0}", + "addKeybindingLabel": "キー バインドの追加", + "commandAriaLabel": "コマンドは {0} です。", + "keybindingAriaLabel": "キー バインドは {0} です。", + "noKeybinding": "キー バインドが割り当てられていません。", + "sourceAriaLabel": "ソースは {0} です。", + "whenAriaLabel": "時間は {0} です。", + "noWhen": "時間コンテキストがありません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..31ff01bea3 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "キー バインドの定義", + "defineKeybinding.kbLayoutErrorMessage": "現在のキーボード レイアウトでは、このキーの組み合わせを生成することはできません。", + "defineKeybinding.kbLayoutLocalAndUSMessage": "現在のキーボード レイアウトで示すと **{0}** です。(US 標準: **{1}**)", + "defineKeybinding.kbLayoutLocalMessage": "現在のキーボード レイアウトで示すと **{0}** です。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..09ba4ec7f2 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "既定の基本設定エディター", + "keybindingsEditor": "キー バインド エディター", + "preferences": "基本設定" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..db7cf4c0e1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "ユーザー設定を開く", + "openGlobalKeybindings": "キーボード ショートカットを開く", + "openGlobalKeybindingsFile": "キーボード ショートカット ファイルを開く", + "openWorkspaceSettings": "ワークスペース設定を開く", + "openFolderSettings": "フォルダーの設定を開く", + "pickFolder": "フォルダーの選択", + "configureLanguageBasedSettings": "言語固有の設定を構成します...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "言語の選択" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..57fc56e03f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "既定の設定", + "SearchSettingsWidget.AriaLabel": "設定の検索", + "SearchSettingsWidget.Placeholder": "設定の検索", + "totalSettingsMessage": "合計 {0} 個の設定", + "noSettingsFound": "結果なし", + "oneSettingFound": "1 つの設定が一致します", + "settingsFound": "{0} 個の設定が一致します", + "fileEditorWithInputAriaLabel": "{0}。テキスト ファイル エディター。", + "fileEditorAriaLabel": "テキスト ファイル エディター。", + "preferencesAriaLabel": "既定の基本設定。読み取り専用のテキスト エディター。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..b427a56651 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "既定の設定を上書きするには、このファイル内に設定を挿入します。", + "errorInvalidConfiguration": "設定を書き込めません。ファイル内のエラー/警告を修正してからもう一度お試しください。", + "emptyWorkspaceSettingsHeader": "ユーザー設定を上書きするには、このファイル内に設定を挿入します。", + "emptyFolderSettingsHeader": "ワークスペースの設定を上書きするには、このファイル内にフォルダーの設定を挿入します。", + "defaultFolderSettingsTitle": "既定のフォルダー設定", + "defaultSettingsTitle": "既定の設定", + "noSettingsFound": "設定が見つかりません。", + "editTtile": "編集", + "replaceDefaultValue": "設定を置換", + "copyDefaultValue": "設定にコピー", + "unsupportedPHPExecutablePathSetting": "この設定はユーザー設定でなければなりません。ワークスペースのために PHP を構成するには、PHP ファイルを開き、ステータス バーの [PHP パス] をクリックします。", + "unsupportedWorkspaceSetting": "この設定はユーザー設定でなければなりません。", + "unsupportedWorkbenchSetting": "この設定は現在適用できません。このフォルダーを直接開いたときに適用されます。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..8eb930f29e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "ワークスペースの設定を作成するには、まずフォルダーを開いてください", + "emptyKeybindingsHeader": "既定値を上書きするには、このファイル内にキー バインドを挿入します", + "defaultKeybindings": "既定のキー バインド", + "folderSettingsName": "{0} (フォルダーの設定) ", + "fail.createSettings": "'{0}' ({1}) を作成できません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..ff8f1d77a3 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "フォルダーの設定" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..be2e47ebe9 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "既定", + "user": "ユーザー", + "meta": "meta", + "option": "オプション" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..6f0fe8e8a0 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "ユーザー設定", + "workspaceSettingsTarget": "ワークスペースの設定" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..ea6689e7ee --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "よく使用するもの", + "noSettings": "設定はありません", + "defaultKeybindingsHeader": "キー バインド ファイル内にキー バインドを挿入して、キー バインドを上書きします。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..26d01e835b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "すべてのコマンドの表示", + "clearCommandHistory": "コマンド履歴のクリア", + "showCommands.label": "コマンド パレット...", + "entryAriaLabelWithKey": "{0}、{1}、コマンド", + "entryAriaLabel": "{0}、コマンド", + "canNotRun": "コマンド '{0}' はここからは実行できません。", + "actionNotEnabled": "コマンド '{0}' は現在のコンテキストでは無効です。", + "recentlyUsed": "最近使用したもの", + "morecCommands": "その他のコマンド", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "一致するコマンドはありません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..a9e760e167 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "指定行へ移動...", + "gotoLineLabelEmptyWithLimit": "移動先の行番号を 1 ~ {0} の範囲で入力してください", + "gotoLineLabelEmpty": "移動先の行番号を入力してください", + "gotoLineColumnLabel": "行 {0} 文字 {1} へ移動", + "gotoLineLabel": "行 {0} へ移動", + "gotoLineHandlerAriaLabel": "移動先の行番号を入力してください。", + "cannotRunGotoLine": "まずテキスト ファイルを開いてから指定行に移動します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..7a5cb02ac3 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "ファイル内のシンボルへ移動...", + "symbols": "シンボル ({0})", + "method": "メソッド ({0})", + "function": "関数 ({0})", + "_constructor": "コンストラクター ({0})", + "variable": "変数 ({0})", + "class": "クラス ({0})", + "interface": "インターフェイス ({0})", + "namespace": "名前空間 ({0})", + "package": "パッケージ ({0})", + "modules": "モジュール ({0})", + "property": "プロパティ ({0})", + "enum": "列挙型 ({0})", + "string": "文字列 ({0})", + "rule": "ルール ({0})", + "file": "ファイル ({0})", + "array": "配列 ({0})", + "number": "数字 ({0})", + "boolean": "ブール値 ({0})", + "object": "オブジェクト ({0})", + "key": "キー ({0})", + "entryAriaLabel": "{0}、シンボル", + "noSymbolsMatching": "一致するシンボルはありません。", + "noSymbolsFound": "シンボルが見つかりません", + "gotoSymbolHandlerAriaLabel": "入力すると現在アクティブなエディターのシンボルが絞り込まれます。", + "cannotRunGotoSymbolInFile": "ファイルのシンボル情報がありません", + "cannotRunGotoSymbol": "まずテキスト ファイルを開いてからシンボルへ移動します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..f602062866 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}、ピッカーのヘルプ", + "globalCommands": "グローバル コマンド", + "editorCommands": "エディター コマンド" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..ce42c8164c --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "コマンドの表示と実行", + "gotoLineDescriptionMac": "行へ移動", + "gotoLineDescriptionWin": "行へ移動", + "gotoSymbolDescription": "ファイル内のシンボルへ移動", + "gotoSymbolDescriptionScoped": "カテゴリを指定してファイル内のシンボルへ移動", + "helpDescription": "ヘルプの表示", + "viewPickerDescription": "ビューを開く" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..c41fb28c40 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}、ビューの選択", + "views": "ビュー", + "panels": "パネル", + "terminals": "ターミナル", + "terminalTitle": "{0}: {1}", + "channels": "出力", + "openView": "ビューを開く", + "quickOpenView": "Quick Open ビュー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..59461d23cb --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "再起動が必要な設定を変更しました。", + "relaunchSettingDetail": "{0} を再起動ボタンで再起動して、設定を有効にしてください。", + "restart": "再起動", + "relaunchWorkspaceMessage": "このワークスペースの変更には、拡張システムの再読み込みが必要です。", + "reload": "再読み込み" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..bea5fabe3d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "編集された行を示すエディター余白の背景色。", + "editorGutterAddedBackground": "追加された行を示すエディター余白の背景色。", + "editorGutterDeletedBackground": "削除された行を示すエディター余白の背景色。", + "overviewRulerModifiedForeground": "変更されたコンテンツを示す概要ルーラーのマーカー色。", + "overviewRulerAddedForeground": "追加されたコンテンツを示す概要ルーラーのマーカー色。", + "overviewRulerDeletedForeground": "削除されたコンテンツを示す概要ルーラーのマーカー色。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..c2535879d7 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "Git を表示", + "source control": "ソース管理", + "toggleSCMViewlet": "SCM を表示", + "view": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..21a3a9de47 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0} 個の保留中の変更" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..1228268629 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "その他の SCM プロバイダーをインストール...", + "switch provider": "SCM プロバイダーの切り替え..." +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..5a46b50f77 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Message (press {0} to commit)", + "installAdditionalSCMProviders": "その他の SCM プロバイダーをインストール...", + "no open repo": "有効なソース管理がありません。", + "source control": "ソース管理", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..229df1245f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "ファイルとシンボルの結果", + "fileResults": "結果ファイル" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..4207986ab4 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}、ファイル ピッカー", + "searchResults": "検索結果" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..27ae96d177 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}、シンボル ピッカー", + "symbols": "シンボルの結果", + "noSymbolsMatching": "一致するシンボルはありません。", + "noSymbolsWithoutInput": "入力してシンボルを検索します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..8b08b0fee2 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "入力", + "useIgnoreFilesDescription": "無視設ファイルを使用します", + "useExcludeSettingsDescription": "除外設定を使用する" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..3748d1b588 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (置換のプレビュー)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..5042d70c7b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "ワークスペース内のシンボルへ移動...", + "name": "検索", + "showSearchViewlet": "検索の表示", + "view": "表示", + "findInFiles": "フォルダーを指定して検索", + "openAnythingHandlerDescription": "ファイルに移動する", + "openSymbolDescriptionNormal": "ワークスペース内のシンボルへ移動", + "searchOutputChannelTitle": "検索", + "searchConfigurationTitle": "検索", + "exclude": "検索でファイルとフォルダーを除外するために glob パターンを構成します。files.exclude 設定からすべての glob パターンを継承します。", + "exclude.boolean": "ファイル パスの照合基準となる glob パターン。これを true または false に設定すると、パターンがそれぞれ有効/無効になります。", + "exclude.when": "一致するファイルの兄弟をさらにチェックします。一致するファイル名の変数として $(basename) を使用します。", + "useRipgrep": "テキスト検索で ripgrep を使用するかどうかを制御します", + "useIgnoreFilesByDefault": "新しいワークスペースで検索するときに、既定で .gitignore ファイルを使用するか .ignore ファイルを使用するかを制御します。", + "search.quickOpen.includeSymbols": "グローバル シンボル検索の結果を、Quick Open の結果ファイルに含めるように構成します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..e14b3954ef --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "次の検索包含パターンを表示", + "previousSearchIncludePattern": "前の検索包含パターンを表示", + "nextSearchExcludePattern": "次の検索除外パターンを表示", + "previousSearchExcludePattern": "前の検索除外パターンを表示", + "nextSearchTerm": "次の検索語句を表示", + "previousSearchTerm": "前の検索語句を表示", + "focusNextInputBox": "次の入力ボックスにフォーカス", + "focusPreviousInputBox": "前の入力ボックスにフォーカス", + "replaceInFiles": "複数のファイルで置換", + "findInWorkspace": "ワークスペース内を検索...", + "findInFolder": "フォルダー内を検索...", + "RefreshAction.label": "最新の情報に更新", + "ClearSearchResultsAction.label": "検索結果のクリア", + "FocusNextSearchResult.label": "次の検索結果にフォーカス", + "FocusPreviousSearchResult.label": "前の検索結果にフォーカス", + "RemoveAction.label": "削除", + "file.replaceAll.label": "すべて置換", + "match.replace.label": "置換" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..58478e3821 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "その他のファイル", + "searchFileMatches": "{0} 個のファイルが見つかりました", + "searchFileMatch": "{0} 個のファイルが見つかりました", + "searchMatches": "一致する項目が {0} 件見つかりました", + "searchMatch": "一致する項目が {0} 件見つかりました", + "folderMatchAriaLabel": "{1} フォルダー ルート内で {0} 件の一致、検索結果", + "fileMatchAriaLabel": "フォルダー {2} のファイル {1} 内で {0} 件の一致、検索結果", + "replacePreviewResultAria": "テキスト {3} の {2} 列目の {0} を {1} に置換します", + "searchResultAria": "テキスト {2} の {1} 列目に {0} が見つかりました" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..2ab202e27d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "詳細検索の切り替え", + "searchScope.includes": "含めるファイル", + "label.includes": "検索包含パターン", + "searchScope.excludes": "除外するファイル", + "label.excludes": "検索除外パターン", + "replaceAll.confirmation.title": "すべて置換", + "replaceAll.confirm.button": "置換", + "replaceAll.occurrence.file.message": "{1} 個のファイルで {0} 件の出現箇所を '{2}' に置換しました。", + "removeAll.occurrence.file.message": "{1} 個のファイルで {0} 件の出現箇所を置換しました。", + "replaceAll.occurrence.files.message": "{1} 個のファイルで {0} 件の出現箇所を '{2}' に置換しました。", + "removeAll.occurrence.files.message": "{1} 個のファイルで {0} 件の出現箇所を置換しました。", + "replaceAll.occurrences.file.message": "{1} 個のファイルで {0} 件の出現箇所を '{2}' に置換しました。", + "removeAll.occurrences.file.message": "{1} 個のファイルで {0} 件の出現箇所を置換しました。", + "replaceAll.occurrences.files.message": "{1} 個のファイルで {0} 件の出現箇所を '{2}' に置換しました。", + "removeAll.occurrences.files.message": "{1} 個のファイルで {0} 件の出現箇所を置換しました。", + "removeAll.occurrence.file.confirmation.message": "{1} 個のファイルで {0} 件の出現箇所を '{2}' に置換しますか?", + "replaceAll.occurrence.file.confirmation.message": "{1} 個のファイルで {0} 件の出現箇所を置換しますか?", + "removeAll.occurrence.files.confirmation.message": "{1} 個のファイルで {0} 件の出現箇所を '{2}' に置換しますか?", + "replaceAll.occurrence.files.confirmation.message": "{1} 個のファイルで {0} 件の出現箇所を置換しますか?", + "removeAll.occurrences.file.confirmation.message": "{1} 個のファイルで {0} 件の出現箇所を '{2}' に置換しますか?", + "replaceAll.occurrences.file.confirmation.message": "{1} 個のファイルで {0} 件の出現箇所を置換しますか?", + "removeAll.occurrences.files.confirmation.message": "{1} 個のファイルで {0} 件の出現箇所を '{2}' に置換しますか?", + "replaceAll.occurrences.files.confirmation.message": "{1} 個のファイルで {0} 件の出現箇所を置換しますか?", + "treeAriaLabel": "検索結果", + "searchPathNotFoundError": "検索パスが見つかりません: {0}", + "searchMaxResultsWarning": "結果セットにはすべての一致項目のサブセットのみが含まれています。より限定的な検索条件を入力して、検索結果を絞り込んでください。", + "searchCanceled": "結果が見つかる前に検索が取り消されました - ", + "noResultsIncludesExcludes": "'{0}' に '{1}' を除外した結果はありません - ", + "noResultsIncludes": "'{0}' に結果はありません - ", + "noResultsExcludes": "'{0}' を除外した結果はありませんでした - ", + "noResultsFound": "結果はありません。構成された除外と無視するファイルの設定を確認してください -", + "rerunSearch.message": "もう一度検索してください", + "rerunSearchInAll.message": "すべてのファイルでもう一度検索してください", + "openSettings.message": "設定を開く", + "openSettings.learnMore": "詳細情報", + "ariaSearchResultsStatus": "検索により {1} 個のファイル内の {0} 件の結果が返されました", + "search.file.result": "{1} 個のファイルに {0} 件の結果", + "search.files.result": "{1} 個のファイルに {0} 件の結果", + "search.file.results": "{1} 個のファイルに {0} 件の結果", + "search.files.results": "{1} 個のファイルに {0} 件の結果", + "searchWithoutFolder": "まだフォルダーを開いていません。開いているファイルのみを検索しています - ", + "openFolder": "フォルダーを開く" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..15c4f248d2 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "すべて置換 (有効にする検索を実行)", + "search.action.replaceAll.enabled.label": "すべて置換", + "search.replace.toggle.button.title": "置換の切り替え", + "label.Search": "検索: 検索語句を入力し Enter を押して検索するか、Esc を押して取り消します", + "search.placeHolder": "検索", + "label.Replace": "置換: 置換用語を入力し、Enter を押してプレビューするか、Escape を押してキャンセルします", + "search.replace.placeHolder": "置換", + "regexp.validationFailure": "この式はすべてに一致します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..50e34a2f2c --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "ワークスペースに次の名前のフォルダーはありません: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..a6cc7883df --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "スニペットを提供します。", + "vscode.extension.contributes.snippets-language": "このスニペットの提供先の言語識別子です。", + "vscode.extension.contributes.snippets-path": "スニペット ファイルのパス。拡張機能フォルダーの相対パスであり、通常 './snippets/' で始まります。", + "invalid.language": "`contributes.{0}.language` で不明な言語です。提供された値: {1}", + "invalid.path.0": "`contributes.{0}.path` に文字列が必要です。提供された値: {1}", + "invalid.path.1": "拡張機能のフォルダー ({2}) の中に `contributes.{0}.path` ({1}) が含まれている必要があります。これにより拡張を移植できなくなる可能性があります。", + "badVariableUse": "スニペット \"{0}\" は、スニペット変数とスニペット プレースホルダーを混乱させる可能性が非常にあります。詳細については https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax をご覧ください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..2ccde612e4 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "スニペットの挿入" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..3d0b4a101d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "スニペットの言語を選択", + "openSnippet.errorOnCreate": "{0} を作成できません", + "openSnippet.label": "ユーザー スニペットを開く", + "preferences": "基本設定", + "snippetSchema.json.default": "空のスニペット", + "snippetSchema.json": "ユーザー スニペット構成", + "snippetSchema.json.prefix": "intellisense でスニペットを選択するときに使用するプレフィックス", + "snippetSchema.json.body": "スニペットのコンテンツです。カーソルの位置を定義するには '$1', '${1:defaultText}' を使用し、最後のカーソルの位置には '$0' を使用します。'${varName}' と '${varName:defaultText}' を使用すると変数値を挿入します。例: 'This is file: $TM_FILENAME'.", + "snippetSchema.json.description": "スニペットについての記述。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..6f6b3d609a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "ユーザー スニペット", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0}, {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..e1cb84607e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "プレフィックスが一致する場合にスニペットを挿入します。'quickSuggestions' が無効な場合に最適です。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..9cd47039c8 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "{0} のサポートの改善にご協力ください", + "takeShortSurvey": "簡単なアンケートの実施", + "remindLater": "後で通知する", + "neverAgain": "今後は表示しない" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..95e3924869 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "短いフィードバック アンケートにご協力をお願いできますか?", + "takeSurvey": "アンケートの実施", + "remindLater": "後で通知する", + "neverAgain": "今後は表示しない" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..d102ce74e4 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "ビルド タスクの名前を入力してください", + "noTasksMatching": "一致するタスクがありません", + "noTasksFound": "ビルド タスクが見つかりません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..bd7b7a4897 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, tasks", + "recentlyUsed": "最近使用したタスク", + "configured": "構成済みのタスク", + "detected": "検出されたタスク", + "customizeTask": "タスクの構成" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..08b476c972 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "再開するタスクの名前を入力します", + "noTasksMatching": "一致するタスクがありません", + "noTasksFound": "再開するタスクはありません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..82548c2375 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "実行するタスクの名前を入力します", + "noTasksMatching": "一致するタスクがありません", + "noTasksFound": "タスクが見つかりません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..f167964ff5 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Type the name of a task to terminate", + "noTasksMatching": "No tasks matching", + "noTasksFound": "No tasks to terminate found" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..3e0e807b26 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "テスト タスクの名前を入力してください", + "noTasksMatching": "一致するタスクがありません", + "noTasksFound": "テスト タスクが見つかりません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..f84e44a249 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "警告: options.cwd は、string 型でなければなりません。値 {0} を無視します", + "ConfigurationParser.noargs": "エラー: コマンド引数は文字列の配列でなければなりません。指定された値:\n{0}", + "ConfigurationParser.noShell": "警告: シェル構成がサポートされるのは、ターミナルでタスクを実行している場合のみです。", + "ConfigurationParser.noName": "エラー: 宣言スコープ内の問題マッチャーに次の名前がなければなりません:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "警告: 定義されている問題マッチャーが不明です。サポートされている型は string | ProblemMatcher | (string | ProblemMatcher)[] です。\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "エラー: 正しくない problemMatcher 参照 {0}\n", + "ConfigurationParser.noTaskName": "エラー: タスクが taskName プロパティを提供しなければなりません。このタスクは無視されます。\n{0}\n", + "taskConfiguration.shellArgs": "警告: タスク '{0}' はシェル コマンドです。コマンド名または引数の 1 つに、エスケープされていないスペースが含まれています。コマンド ラインの引用が正しく解釈されるように、引数をコマンドにマージしてください。", + "taskConfiguration.noCommandOrDependsOn": "エラー: タスク '{0}' は、コマンドも dependsOn プロパティも指定していません。このタスクは無視されます。定義は次のとおりです:\n{1}", + "taskConfiguration.noCommand": "エラー: タスク '{0}' はコマンドを定義していません。このタスクは無視されます。定義は次のとおりです:\n{1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..08be457a11 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "実際のタスクの種類", + "TaskDefinition.properties": "タスクの種類の追加プロパティ", + "TaskTypeConfiguration.noType": "タスクの種類を構成するのに必要な 'taskType' プロパティがありません", + "TaskDefinitionExtPoint": "タスクの種類を提供" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..6ecb6c4f4e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": ".NET Core ビルド コマンドの実行", + "msbuild": "ビルド ターゲットを実行", + "externalCommand": "任意の外部コマンドを実行する例", + "Maven": "共通の maven コマンドを実行する" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..af391970b1 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "追加のコマンド オプション", + "JsonSchema.options.cwd": "実行されるプログラムまたはスクリプトの現在の作業ディレクトリ。省略すると、Code の現在のワークスペースのルートが使用されます。", + "JsonSchema.options.env": "実行されるプログラムまたはシェルの環境。省略すると、親プロセスの環境が使用されます。", + "JsonSchema.shellConfiguration": "使用するシェルを構成します。", + "JsonSchema.shell.executable": "使用するシェル。", + "JsonSchema.shell.args": "シェル引数。", + "JsonSchema.command": "実行されるコマンド。外部プログラムかシェル コマンドです。", + "JsonSchema.tasks.args": "このタスクの起動時にコマンドに渡される引数。", + "JsonSchema.tasks.taskName": "タスクの名前", + "JsonSchema.tasks.windows": "Windows 固有のコマンド構成", + "JsonSchema.tasks.mac": "Mac 固有のコマンド構成", + "JsonSchema.tasks.linux": "Linux 固有のコマンド構成", + "JsonSchema.tasks.suppressTaskName": "タスク名を引数としてコマンドに追加するかどうかを制御します。省略すると、グローバルに定義された値が使用されます。", + "JsonSchema.tasks.showOutput": "実行中のタスクの出力が表示されるかどうかを制御します。省略すると、グローバルに定義された値が使用されます。", + "JsonSchema.echoCommand": "実行されるコマンドが出力にエコーされるかどうかを制御します。既定は false です。", + "JsonSchema.tasks.watching.deprecation": "使用しないでください。代わりに isBackground をご使用ください。", + "JsonSchema.tasks.watching": "実行済みのタスクが維持され、ファイル システムをウォッチしているかどうか。", + "JsonSchema.tasks.background": "実行済みのタスクが維持され、バッググラウンドで実行されているかどうか。", + "JsonSchema.tasks.promptOnClose": "タスクの実行中に VS Code を閉じるときにユーザーにダイアログを表示するかどうか。", + "JsonSchema.tasks.build": "このタスクを Code の既定のビルド コマンドにマップします。", + "JsonSchema.tasks.test": "このタスクを Code の既定のテスト コマンドにマップします。", + "JsonSchema.tasks.matchers": "使用する問題マッチャー。1 つの文字列または問題マッチャー定義か、文字列と問題マッチャーの配列です。", + "JsonSchema.args": "さらにコマンドに渡される引数。", + "JsonSchema.showOutput": "実行中のタスクの出力が表示されるかどうかを制御します。省略すると、'always' が使用されます。", + "JsonSchema.watching.deprecation": "使用しないでください。代わりに isBackground をご使用ください。", + "JsonSchema.watching": "実行済みのタスクが維持され、ファイル システムをウォッチしているかどうか。", + "JsonSchema.background": "実行済みのタスクが維持され、バッググラウンドで実行されているかどうか。", + "JsonSchema.promptOnClose": "バックグラウンド タスクの実行中に VS Code を閉じる時に、ユーザーに対してプロンプトが表示されるかどうか。", + "JsonSchema.suppressTaskName": "タスク名を引数としてコマンドに追加するかどうかを制御します。既定は false です。", + "JsonSchema.taskSelector": "引数がタスクであることを示すプレフィックス。", + "JsonSchema.matchers": "使用する問題マッチャー。1 つの文字列または問題マッチャー定義か、文字列と問題マッチャーの配列です。", + "JsonSchema.tasks": "タスクの構成。普通は外部タスク ランナーで既に定義されているタスクのエンリッチメントです。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..c8e204df91 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "構成のバージョン番号", + "JsonSchema._runner": "ランナーが新しくなります。正式なランナープロパティーを使用してください", + "JsonSchema.runner": "タスクをプロセスとして実行して、出力が出力ウィンドウまたは端末内に表示されるかどうかを定義します。", + "JsonSchema.windows": "Windows 固有のコマンド構成", + "JsonSchema.mac": "Mac 固有のコマンド構成", + "JsonSchema.linux": "Linux 固有のコマンド構成", + "JsonSchema.shell": "コマンドがシェル コマンドか外部プログラムかを指定します。省略すると、既定は false になります。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..a0b03a4f58 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "コマンドがシェル コマンドか外部プログラムかを指定します。省略すると、既定は false になります。", + "JsonSchema.tasks.isShellCommand.deprecated": "isShellCommand プロパティは使用されていません。代わりに、タスクの type プロパティとオプションの shell プロパティをご使用ください。また 1.14 リリース ノートをご確認ください。", + "JsonSchema.tasks.dependsOn.string": "このタスクが依存している別のタスク。", + "JsonSchema.tasks.dependsOn.array": "このタスクが依存している他の複数のタスク。", + "JsonSchema.tasks.presentation": "タスクの出力の表示と入力の読み取りに使用するようにパネルを構成します。", + "JsonSchema.tasks.presentation.echo": "実行されたコマンドがパネルにエコーされるかどうかを制御します。既定は trueです。", + "JsonSchema.tasks.presentation.focus": "パネルがフォーカスされるかどうかを制御します。既定は false です。true に設定した場合、パネルも表示されます。", + "JsonSchema.tasks.presentation.reveal.always": "タスクを実行したとき常にターミナルを表示します。", + "JsonSchema.tasks.presentation.reveal.silent": "タスクに関連付けられた問題マッチャーがなく、実行時にエラーが発生した場合のみターミナルを表示します。", + "JsonSchema.tasks.presentation.reveal.never": "このタスクを実行するときに、今後ターミナルを表示しません。", + "JsonSchema.tasks.presentation.reveals": "タスクを実行しているパネルを表示するかどうかを制御します。既定は \"always\" です。", + "JsonSchema.tasks.presentation.instance": "タスク間でパネルを共有するか、またはこのタスクで占有するか、実行ごとに新しいパネルを作成するかどうかを制御します。", + "JsonSchema.tasks.terminal": "terminal プロパティは非推奨です。代わりに presentation をご使用ください", + "JsonSchema.tasks.group.kind": "タスクの実行グループ。", + "JsonSchema.tasks.group.isDefault": "このタスクがグループ内の既定のタスクであるかどうかを定義します。", + "JsonSchema.tasks.group.defaultBuild": "このタスクを既定のビルド タスクとしてマークします。", + "JsonSchema.tasks.group.defaultTest": "このタスクを既定のテスト タスクとしてマークします。", + "JsonSchema.tasks.group.build": "タスクを 'Run Build Task' (ビルド タスクの実行) コマンドを介してアクセス可能なビルド タスクとしてマークします。", + "JsonSchema.tasks.group.test": "タスクを 'Run Test Task' (テスト タスクの実行) コマンドを介してアクセス可能なテスト タスクとしてマークします。", + "JsonSchema.tasks.group.none": "タスクをグループに割り当てない", + "JsonSchema.tasks.group": "このタスクが属する実行グループを定義します。ビルド グループに追加する \"build\" とテスト グループに追加する \"test\" をサポートしています。", + "JsonSchema.tasks.type": "タスクをプロセスとして実行するか、またはシェル内部でコマンドとして実行するかどうかを定義します。既定は process です。", + "JsonSchema.version": "構成のバージョン番号", + "JsonSchema.tasks.identifier": "launch.json または dependsOn 句のタスクを参照するユーザー定義の識別子。", + "JsonSchema.tasks.taskLabel": "タスクのラベル", + "JsonSchema.tasks.taskName": "タスクの名前", + "JsonSchema.tasks.taskName.deprecated": "タスクの name プロパティは非推奨です。代わりに label プロパティをご使用ください。", + "JsonSchema.tasks.background": "実行済みのタスクが維持され、バッググラウンドで実行されているかどうか。", + "JsonSchema.tasks.promptOnClose": "タスクの実行中に VS Code を閉じるときにユーザーにダイアログを表示するかどうか。", + "JsonSchema.tasks.matchers": "使用する問題マッチャー。1 つの文字列または問題マッチャー定義か、文字列と問題マッチャーの配列です。", + "JsonSchema.customizations.customizes.type": "カスタマイズするタスクの種類", + "JsonSchema.tasks.customize.deprecated": "customize プロパティは非推奨です。新しいタスクのカスタマイズ方法に移行する方法については 1.14 リリース ノートをご確認ください。", + "JsonSchema.tasks.showOputput.deprecated": "showOutputプロパティは非推奨です。代わりに presentation プロパティ内の reveal プロパティを使用してください。また 1.14 リリース ノートをご確認ください。", + "JsonSchema.tasks.echoCommand.deprecated": "echoCommand プロパティは使用されていません。代わりに presentation プロパティ内の echo プロパティを使用してください。また 1.14 リリース ノートをご確認ください。", + "JsonSchema.tasks.suppressTaskName.deprecated": "suppressTaskName プロパティは非推奨です。代わりに、その引数を含むコマンドをタスクにインライン展開してください。1.14 リリース ノートも参照してください。", + "JsonSchema.tasks.isBuildCommand.deprecated": "isBuildCommand プロパティは非推奨です。代わりに group プロパティを使用してください。また 1.14 リリース ノートをご確認ください。", + "JsonSchema.tasks.isTestCommand.deprecated": "isTestCommand プロパティは非推奨です。代わりに group プロパティを使用してください。また 1.14 リリース ノートをご確認ください。", + "JsonSchema.tasks.taskSelector.deprecated": "taskSelector プロパティは非推奨です。代わりに、その引数を含むコマンドをタスクにインライン展開してください。1.14 リリース ノートも参照してください。", + "JsonSchema.windows": "Windows 固有のコマンド構成", + "JsonSchema.mac": "Mac 固有のコマンド構成", + "JsonSchema.linux": "Linux 固有のコマンド構成" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..bdd5d709e8 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "タスク", + "ConfigureTaskRunnerAction.noWorkspace": "タスクはワークスペース フォルダーでのみ利用可能です。", + "ConfigureTaskRunnerAction.quickPick.template": "タスク ランナーを選択", + "ConfigureTaskRunnerAction.autoDetecting": "{0} のタスクを自動検出", + "ConfigureTaskRunnerAction.autoDetect": "タスク システムの自動検出が失敗しました。既定のテンプレートを使用しています。詳細については、タスク出力を参照してください", + "ConfigureTaskRunnerAction.autoDetectError": "タスク システムの自動検出でエラーが発生しました。詳細については、タスク出力を参照してください。", + "ConfigureTaskRunnerAction.failed": "'.vscode' フォルダー内に 'tasks.json' ファイルを作成できません。詳細については、タスク出力を参照してください。", + "ConfigureTaskRunnerAction.label": "タスク ランナーの構成", + "ConfigureBuildTaskAction.label": "ビルド タスクを構成します", + "CloseMessageAction.label": "閉じる", + "ShowTerminalAction.label": "ターミナルの表示", + "problems": "問題", + "manyMarkers": "99+", + "runningTasks": "実行中のタスクを表示", + "tasks": "タスク", + "TaskSystem.noHotSwap": "タスクの実行エンジンを変更するにはウィンドウの再読み込みが必要です", + "TaskService.noBuildTask1": "ビルド タスクが定義されていません。tasks.json ファイルでタスクに 'isBuildCommand' というマークを付けてください。", + "TaskService.noBuildTask2": "ビルド タスクが定義されていません。tasks.json ファイルでタスクに 'build' グループとしてマークを付けてください。", + "TaskService.noTestTask1": "テスト タスクが定義されていません。tasks.json ファイルでタスクに 'isTestCommand' というマークを付けてください。", + "TaskService.noTestTask2": "テスト タスクが定義されていません。tasks.json ファイルでタスクに 'test' グループとしてマークを付けてください。", + "TaskServer.noTask": "実行が要求されたタスク {0} が見つかりません。", + "TaskService.attachProblemMatcher.continueWithout": "タスクの出力をスキャンせずに続行", + "TaskService.attachProblemMatcher.never": "今後このタスクの出力をスキャンしない", + "TaskService.attachProblemMatcher.learnMoreAbout": "タスク出力のスキャンについての詳細", + "selectProblemMatcher": "スキャンするタスク出力のエラーと警告の種類を選択", + "customizeParseErrors": "現在のタスクの構成にはエラーがあります。タスクをカスタマイズする前にエラーを修正してください。", + "moreThanOneBuildTask": "tasks.json で複数のビルド タスクが定義されています。最初のタスクのみを実行します。\\n", + "TaskSystem.activeSame.background": "'{0}' タスクは既にバックグラウンド モードでアクティブです。タスクを終了するにはタスク メニューから `タスクの終了...` を使用します。", + "TaskSystem.activeSame.noBackground": "'{0}' タスクは既にアクティブです。タスクを終了するにはタスク メニューから`タスクの終了...` を使用してください。 ", + "TaskSystem.active": "既に実行中のタスクがあります。まずこのタスクを終了してから、別のタスクを実行してください。", + "TaskSystem.restartFailed": "タスク {0} を終了して再開できませんでした", + "TaskSystem.configurationErrors": "エラー: 指定したタスク構成に検証エラーがあり、使用できません。最初にエラーを修正してください。", + "TaskSystem.invalidTaskJson": "エラー: tasks.json ファイルの内容に構文エラーがあります。訂正してからタスクを実行してください。\n", + "TaskSystem.runningTask": "実行中のタスクがあります。終了しますか?", + "TaskSystem.terminateTask": "タスクの終了(&&T)", + "TaskSystem.noProcess": "起動したタスクは既に存在しません。タスクを起動したバックグラウンド プロセスが VS コードで終了すると、プロセスが孤立することがあります。これを回避するには、待機フラグを設定して最後のバックグラウンド プロセスを開始します。", + "TaskSystem.exitAnyways": "常に終了(&&E)", + "TerminateAction.label": "タスクの終了", + "TaskSystem.unknownError": "タスクの実行中にエラーが発生しました。詳細については、タスク ログを参照してください。", + "TaskService.noWorkspace": "タスクはワークスペース フォルダーでのみ利用可能です。", + "recentlyUsed": "最近使用したタスク", + "configured": "構成済みのタスク", + "detected": "検出されたタスク", + "TaskService.fetchingBuildTasks": "ビルド タスクをフェッチしています...", + "TaskService.noBuildTaskTerminal": "ビルド タスクが見つかりません。定義するには 'Configure Build Task' (ビルド タスクを構成します) を押してください。", + "TaskService.pickBuildTask": "実行するビルド タスクを選択", + "TaskService.fetchingTestTasks": "テスト タスクをフェッチしています...", + "TaskService.noTestTaskTerminal": "テスト タスクが見つかりません。定義するには 'Configure Build Task' (タスク ランナーの構成) を押してください。", + "TaskService.pickTestTask": "実行するテスト タスクを選択してください", + "TaskService.noTaskRunning": "現在実行中のタスクはありません。", + "TaskService.tastToTerminate": "終了するタスクを選択", + "TerminateAction.noProcess": "起動したプロセスは既に存在しません。タスクを起動したバックグラウンド タスクが VS コードで終了すると、プロセスが孤立することがあります。", + "TerminateAction.failed": "実行中のタスクの終了に失敗しました", + "TaskService.noTaskToRestart": "再起動するタスクがありません。", + "TaskService.tastToRestart": "再起動するタスクを選択してください", + "TaskService.defaultBuildTaskExists": "{0} は既に既定のビルド タスクとしてマークされています。", + "TaskService.pickDefaultBuildTask": "既定のビルド タスクとして使用するタスクを選択", + "TaskService.defaultTestTaskExists": "{0} は既に既定のテスト タスクとしてマークされています。", + "TaskService.pickDefaultTestTask": "既定のテスト タスクとして使用するタスクを選択", + "TaskService.noTaskIsRunning": "実行中のタスクはありません。", + "TaskService.pickShowTask": "出力を表示するタスクを選択", + "ShowLogAction.label": "タスク ログの表示", + "RunTaskAction.label": "タスクの実行", + "RestartTaskAction.label": "実行中のタスクの再起動", + "ShowTasksAction.label": "実行中のタスクを表示", + "BuildAction.label": "ビルド タスクの実行", + "TestAction.label": "テスト タスクの実行", + "ConfigureDefaultBuildTask.label": "既定のビルド タスクを構成する", + "ConfigureDefaultTestTask.label": "既定のテスト タスクを構成する", + "quickOpen.task": "タスクの実行" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..2d3bf30bfa --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "タスク" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..58781df404 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "タスクの実行中に不明なエラーが発生しました。詳細については、タスク出力ログを参照してください。", + "TerminalTaskSystem.terminalName": "タスク - {0}", + "reuseTerminal": "ターミナルはタスクで再利用されます、閉じるには任意のキーを押してください。", + "TerminalTaskSystem": "UNC ドライブでシェル コマンドを実行できません。", + "unkownProblemMatcher": "問題マッチャー {0} は解決できませんでした。マッチャーは無視されます" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..7eb92eda1a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "gulp --tasks-simple が実行されましたがタスクの一覧は表示されませんでした。npm install を実行しましたか?", + "TaskSystemDetector.noJakeTasks": "jake --tasks が実行されましたがタスクの一覧は表示されませんでした。npm install を実行しましたか?", + "TaskSystemDetector.noGulpProgram": "システムに Gulp がインストールされていません。npm install -g gulp を実行してインストールしてください。", + "TaskSystemDetector.noJakeProgram": "システムに Jake がインストールされていません。npm install -g jake を実行してインストールしてください。", + "TaskSystemDetector.noGruntProgram": "システムに Grunt がインストールされていません。npm install -g grunt を実行してインストールしてください。", + "TaskSystemDetector.noProgram": "プログラム {0} が見つかりませんでした。メッセージは {1} です", + "TaskSystemDetector.buildTaskDetected": "名前 '{0}' のビルド タスクが検出されました。", + "TaskSystemDetector.testTaskDetected": "名前 '{0}' のテスト タスクが検出されました。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..10f86c2cd8 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "タスクの実行中に不明なエラーが発生しました。詳細については、タスク出力ログを参照してください。", + "TaskRunnerSystem.watchingBuildTaskFinished": "\nビルド タスクのウォッチが終了しました。", + "TaskRunnerSystem.childProcessError": "Failed to launch external program {0} {1}.", + "TaskRunnerSystem.cancelRequested": "\nユーザー要求ごとにタスク '{0}' が終了しました。", + "unkownProblemMatcher": "問題マッチャー {0} は解決できませんでした。マッチャーは無視されます" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..d9f9b3968d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "警告: options.cwd は、string 型でなければなりません。値 {0} を無視します", + "ConfigurationParser.noargs": "エラー: コマンド引数は文字列の配列でなければなりません。指定された値:\n{0}", + "ConfigurationParser.noShell": "警告: シェル構成がサポートされるのは、ターミナルでタスクを実行している場合のみです。", + "ConfigurationParser.noName": "エラー: 宣言スコープ内の問題マッチャーに次の名前がなければなりません:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "警告: 定義されている問題マッチャーが不明です。サポートされている型は string | ProblemMatcher | (string | ProblemMatcher)[] です。\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "エラー: 正しくない problemMatcher 参照 {0}\n", + "ConfigurationParser.noTaskType": "エラー: タスクの構成には type プロパティが必要です。この構成は無視されます。\n{0}\n", + "ConfigurationParser.noTypeDefinition": "Error: タスク タイプ '{0}' は登録されていません。対応するタスク プロバイダーを提供する拡張機能をインストールしましたか?", + "ConfigurationParser.notCustom": "エラー: タスクがカスタム タスクとして定義されていません。この構成は無視されます。\n{0}\n", + "ConfigurationParser.noTaskName": "エラー: タスクが taskName プロパティを提供しなければなりません。このタスクは無視されます。\n{0}\n", + "taskConfiguration.shellArgs": "警告: タスク '{0}' はシェル コマンドです。コマンド名または引数の 1 つに、エスケープされていないスペースが含まれています。コマンド ラインの引用が正しく解釈されるように、引数をコマンドにマージしてください。", + "taskConfiguration.noCommandOrDependsOn": "エラー: タスク '{0}' は、コマンドも dependsOn プロパティも指定していません。このタスクは無視されます。定義は次のとおりです:\n{1}", + "taskConfiguration.noCommand": "エラー: タスク '{0}' はコマンドを定義していません。このタスクは無視されます。定義は次のとおりです:\n{1}", + "TaskParse.noOsSpecificGlobalTasks": "バージョン 2.0.0 のタスクでは、OS に固有のグローバル タスクはサポートされません。OS に固有のコマンドを使用したタスクに変換してください。影響を受けるタスクは次のとおりです。\n{0}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..0f8b3cda53 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0}、ターミナル ピッカー", + "termCreateEntryAriaLabel": "{0} 、新しいターミナルの作成", + "'workbench.action.terminal.newplus": "$(plus) 新しい統合ターミナルの作成", + "noTerminalsMatching": "一致するターミナルがありません", + "noTerminalsFound": "開いているターミナルがありません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..09b3e15b29 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "開いているすべてのターミナルを表示", + "terminalIntegratedConfigurationTitle": "統合ターミナル", + "terminal.integrated.shell.linux": "ターミナルが Linux で使用するシェルのパス。", + "terminal.integrated.shellArgs.linux": "Linux のターミナルで使用するコマンド ライン引数。", + "terminal.integrated.shell.osx": "ターミナルが OS X で使用するシェルのパス。", + "terminal.integrated.shellArgs.osx": "OS X 端末で使用するコマンド ライン引数。", + "terminal.integrated.shell.windows": "Windows でターミナルが使用するシェルのパス。 Windows に同梱されているシェルを使用する場合 (cmd、PowerShell、または Bash on Ubuntu) 。", + "terminal.integrated.shellArgs.windows": "Windows ターミナル上の場合に使用されるコマンド ライン引数。", + "terminal.integrated.rightClickCopyPaste": "設定している場合、ターミナル内で右クリックしたときにコンテキスト メニューを表示させず、選択範囲がある場合はコピー、選択範囲がない場合は貼り付けの操作を行います。", + "terminal.integrated.fontFamily": "端末のフォント ファミリを制御します。既定値は editor.fontFamily になります。", + "terminal.integrated.fontLigatures": "ターミナルでフォントの合字が有効かどうかを制御します。", + "terminal.integrated.fontSize": "ターミナルのフォント サイズをピクセル単位で制御します。", + "terminal.integrated.lineHeight": "ターミナルの行の高さを制御します。この数値にターミナルのフォント サイズを乗算すると、実際の行の高さ (ピクセル単位) になります。", + "terminal.integrated.enableBold": "ターミナル内で太字を有効にするかどうか。これにはターミナルシェルからのサポートがひつようです。", + "terminal.integrated.cursorBlinking": "ターミナルのカーソルを点滅させるかどうかを制御します。", + "terminal.integrated.cursorStyle": "端末のカーソルのスタイルを制御します。", + "terminal.integrated.scrollback": "端末がそのバッファーに保持できる最大行数を制御します。", + "terminal.integrated.setLocaleVariables": "ターミナルの開始時にロケール変数を設定するかどうかを制御します。OS X では既定で true になり、その他のプラットフォームでは false です。", + "terminal.integrated.cwd": "端末を起動する明示的な開始パスです。これはシェル プロセスの現在の作業ディレクトリ (cwd) として使用されます。特にルート ディレクトリが cwd に適していない場合に、ワークスペースの設定で役立ちます。", + "terminal.integrated.confirmOnExit": "アクティブなターミナル セッションがある場合に終了の確認をするかどうか。", + "terminal.integrated.commandsToSkipShell": "キーバインドがシェルに送信されず、代わりに常に Code で処理されるコマンド ID のセット。これにより、ターミナルがフォーカスされていない場合と同じ動作をするシェルによって通常使用されるキーバインドを使用できるようになります。例: Ctrl+p で Quick Open を起動します。", + "terminal.integrated.env.osx": "OS X のターミナルで使用される VS Code のプロセスに追加される環境変数を持つオブジェクト", + "terminal.integrated.env.linux": "Linux のターミナルで使用される VS Code のプロセスに追加される環境変数を持つオブジェクト", + "terminal.integrated.env.windows": "Windows のターミナルで使用される VS Code のプロセスに追加される環境変数を持つオブジェクト", + "terminal": "ターミナル", + "terminalCategory": "ターミナル", + "viewCategory": "表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..c90715a1ab --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "統合ターミナルの切り替え", + "workbench.action.terminal.kill": "アクティブな端末インスタンスを強制終了", + "workbench.action.terminal.kill.short": "ターミナルの強制終了", + "workbench.action.terminal.quickKill": "ターミナル インスタンスの停止", + "workbench.action.terminal.copySelection": "選択内容のコピー", + "workbench.action.terminal.selectAll": "すべて選択", + "workbench.action.terminal.deleteWordLeft": "左の文字を削除", + "workbench.action.terminal.deleteWordRight": "右の文字を削除", + "workbench.action.terminal.new": "新しい統合ターミナルの作成", + "workbench.action.terminal.new.short": "新しいターミナル", + "workbench.action.terminal.focus": "端末にフォーカス", + "workbench.action.terminal.focusNext": "次の端末にフォーカス", + "workbench.action.terminal.focusAtIndex": "ターミナル {0} にフォーカス", + "workbench.action.terminal.focusPrevious": "前のターミナルにフォーカス", + "workbench.action.terminal.paste": "アクティブなターミナルに貼り付け", + "workbench.action.terminal.DefaultShell": "既定のシェルの選択", + "workbench.action.terminal.runSelectedText": "アクティブなターミナルで選択したテキストを実行", + "workbench.action.terminal.runActiveFile": "アクティブなファイルをアクティブなターミナルで実行", + "workbench.action.terminal.runActiveFile.noFile": "ターミナルで実行できるのは、ディスク上のファイルのみです", + "workbench.action.terminal.switchTerminalInstance": "ターミナル インスタンスの切り替え", + "workbench.action.terminal.scrollDown": "下にスクロール (行)", + "workbench.action.terminal.scrollDownPage": "スクロール ダウン (ページ)", + "workbench.action.terminal.scrollToBottom": "一番下にスクロール", + "workbench.action.terminal.scrollUp": "上にスクロール (行)", + "workbench.action.terminal.scrollUpPage": "スクロール アップ (ページ)", + "workbench.action.terminal.scrollToTop": "一番上にスクロール", + "workbench.action.terminal.clear": "クリア", + "workbench.action.terminal.allowWorkspaceShell": "ワークスペースでシェルを構成することを許可する", + "workbench.action.terminal.disallowWorkspaceShell": "ワークスペースでシェルを構成することを許可しない", + "workbench.action.terminal.rename": "名前変更", + "workbench.action.terminal.rename.prompt": "ターミナルの名前を入力してください", + "workbench.action.terminal.focusFindWidget": "検索ウィジェットにフォーカスする", + "workbench.action.terminal.hideFindWidget": "検索ウィジェットを非表示にする", + "nextTerminalFindTerm": "次の検索語句を表示", + "previousTerminalFindTerm": "前の検索語句を表示", + "quickOpenTerm": "ターミナル: アクティブなターミナルの切り替え" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..9adb35566a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "ターミナルの背景色。パネルごとに異なる色を指定できます。", + "terminal.foreground": "ターミナルの前景色。", + "terminalCursor.foreground": "ターミナルのカーソル前景色。", + "terminalCursor.background": "ターミナルのカーソルの背景色。ブロックカーソルで重ねた文字の色をカスタマイズできます。", + "terminal.ansiColor": "ターミナルの '{0}' ANSI カラー。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..d934e6abcc --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "{0} (ワークスペースの設定として定義されている) をターミナルで起動することを許可しますか?", + "allow": "Allow", + "disallow": "Disallow" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..b7056be6f3 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "検索", + "placeholder.find": "検索", + "label.previousMatchButton": "前の一致項目", + "label.nextMatchButton": "次の一致項目", + "label.closeButton": "閉じる" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..3f8a1fee44 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "ターミナルにコピー対象の選択範囲がありません", + "terminal.integrated.exitedWithCode": "ターミナルの処理が終了しました (終了コード: {0})", + "terminal.integrated.waitOnExit": "任意のキーを押して端末を終了します", + "terminal.integrated.launchFailed": "ターミナル プロセス コマンド `{0}{1}` を起動できませんでした (終了コード: {2})" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..21b8a65c22 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "Altl キーを押しながらクリックしてリンク先を表示", + "terminalLinkHandler.followLinkCmd": "command キーを押しながらクリックしてリンク先を表示", + "terminalLinkHandler.followLinkCtrl": "Ctrl キーを押しながらクリックしてリンク先を表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..0c55392de0 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "コピー", + "createNewTerminal": "新しいターミナル", + "paste": "貼り付け", + "selectAll": "すべて選択", + "clear": "クリア" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..99435c8682 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "カスタマイズ ボタンを選択して、既定のターミナル シェルを変更できます。", + "customize": "カスタマイズする", + "cancel": "キャンセル", + "never again": "OK、今後は表示しない", + "terminal.integrated.chooseWindowsShell": "優先するターミナル シェルを選択します。これは後で設定から変更できます", + "terminalService.terminalCloseConfirmationSingular": "アクティブなターミナル セッションが 1 つあります。中止しますか?", + "terminalService.terminalCloseConfirmationPlural": "アクティブなターミナル セッションが {0} 個あります。中止しますか?", + "yes": "はい" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..a463dde026 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "配色テーマ", + "installColorThemes": "その他の配色テーマをインストール...", + "themes.selectTheme": "配色テーマの選択 (上/下キーでプレビュー可能)", + "selectIconTheme.label": "ファイル アイコンのテーマ", + "installIconThemes": "その他のファイル アイコンのテーマをインストール...", + "noIconThemeLabel": "なし", + "noIconThemeDesc": "ファイル アイコンを無効にする", + "problemChangingIconTheme": "アイコン テーマの設定で問題が発生しました: {0}", + "themes.selectIconTheme": "ファイル アイコンのテーマを選択します", + "generateColorTheme.label": "現在の設定から配色テーマを生成する", + "preferences": "基本設定", + "developer": "Developer" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..436a3ec58d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "このワークスペースには、ユーザー設定でのみ設定可能な設定が含まれています。({0})", + "openWorkspaceSettings": "ワークスペース設定を開く", + "openDocumentation": "詳細情報", + "ignore": "無視" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/jpn/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..9129f61680 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "リリース ノート: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..5026cf626a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "リリース ノート", + "updateConfigurationTitle": "更新", + "updateChannel": "更新チャネルから自動更新を受信するかどうかを構成します。変更後に再起動が必要です。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..699e680d34 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "今すぐ更新", + "later": "後で", + "unassigned": "未割り当て", + "releaseNotes": "リリース ノート", + "showReleaseNotes": "リリース ノートの表示", + "downloadNow": "今すぐダウンロード", + "read the release notes": "{0} v{1} へようこそ! リリース ノートを確認しますか?", + "licenseChanged": "ライセンス条項が変更されました。内容をご確認ください。", + "license": "ライセンスの閲覧", + "neveragain": "今後は表示しない", + "64bitisavailable": "64 ビット Windows 用の {0} が利用可能になりました!", + "learn more": "詳細情報", + "updateIsReady": "新しい更新 {0} が利用可能です。", + "thereIsUpdateAvailable": "利用可能な更新プログラムがあります。", + "updateAvailable": "{0} は再起動後に更新されます。", + "noUpdatesAvailable": "現在入手可能な更新はありません。", + "commandPalette": "コマンド パレット...", + "settings": "設定", + "keyboardShortcuts": "キーボード ショートカット", + "selectTheme.label": "配色テーマ", + "themes.selectIconTheme.label": "ファイル アイコンのテーマ", + "not available": "更新は利用できません", + "checkingForUpdates": "更新を確認しています...", + "DownloadUpdate": "利用可能な更新プログラムをダウンロードします", + "DownloadingUpdate": "更新をダウンロードしています...", + "InstallingUpdate": "更新プログラムをインストールしています...", + "restartToUpdate": "再起動して更新...", + "checkForUpdates": "更新の確認..." +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/jpn/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..ff489bacbc --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} 個のアクション", + "hideView": "サイド バーから非表示" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/jpn/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..de137f2f87 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "ビューは配列にする必要があります", + "requirestring": " `{0}` プロパティは必須で、`string` 型でなければなりません", + "optstring": "`{0}` プロパティは省略するか、`string` 型にする必要があります", + "vscode.extension.contributes.view.id": "ビューの識別子。`vscode.window.registerTreeDataProviderForView` API を介してデータ プロバイダーを登録するには、これを使用します。また、`onView:${id}` イベントを `activationEvents` に登録することによって、拡張機能のアクティブ化をトリガーするためにも使用できます。", + "vscode.extension.contributes.view.name": "ビューの判読できる名前。表示されます", + "vscode.extension.contributes.view.when": "このビューを表示するために満たす必要がある条件", + "vscode.extension.contributes.views": "ビューをエディターに提供します", + "views.explorer": "エクスプローラー ビュー", + "views.debug": "デバッグ ビュー", + "locationId.invalid": "`{0}` は有効なビューの場所ではありません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/jpn/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..bfa0e6244f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "すべてのコマンドの表示", + "watermark.quickOpen": "ファイルに移動する", + "watermark.openFile": "ファイルを開く", + "watermark.openFolder": "フォルダーを開く", + "watermark.openFileFolder": "ファイルまたはフォルダーを開く", + "watermark.openRecent": "最近開いた項目", + "watermark.newUntitledFile": "無題の新規ファイル", + "watermark.toggleTerminal": "ターミナルの切り替え", + "watermark.findInFiles": "フォルダーを指定して検索", + "watermark.startDebugging": "デバッグの開始", + "watermark.unboundCommand": "バインドなし", + "workbenchConfigurationTitle": "ワークベンチ", + "tips.enabled": "有効にすると、エディターを 1 つも開いていないときに透かしのヒントが表示されます。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/jpn/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..2b07240fcc --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "エクスプローラー", + "welcomeOverlay.search": "複数ファイルの検索", + "welcomeOverlay.git": "ソース コード管理", + "welcomeOverlay.debug": "起動およびデバッグ", + "welcomeOverlay.extensions": "拡張機能の管理", + "welcomeOverlay.problems": "エラーおよび警告の表示", + "welcomeOverlay.commandPalette": "すべてのコマンドの検索と実行", + "welcomeOverlay": "ユーザー インターフェイスの概要", + "hideWelcomeOverlay": "インターフェイスの概要を非表示にします", + "help": "ヘルプ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..0d957e5827 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "進化した編集", + "welcomePage.start": "開始", + "welcomePage.newFile": "新しいファイル", + "welcomePage.openFolder": "フォルダーを開く...", + "welcomePage.cloneGitRepository": "Git リポジトリを複製...", + "welcomePage.recent": "最近", + "welcomePage.moreRecent": "その他", + "welcomePage.noRecentFolders": "最近使用したフォルダーなし", + "welcomePage.help": "ヘルプ", + "welcomePage.keybindingsCheatsheet": "印刷可能なキーボードのチートシート", + "welcomePage.introductoryVideos": "紹介ビデオ", + "welcomePage.tipsAndTricks": "ヒントとコツ", + "welcomePage.productDocumentation": "製品ドキュメント", + "welcomePage.gitHubRepository": "GitHub リポジトリ", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "起動時にウェルカム ページを表示", + "welcomePage.customize": "カスタマイズする", + "welcomePage.installExtensionPacks": "ツールと言語", + "welcomePage.installExtensionPacksDescription": "{0} と {1} のサポートをインストールする ", + "welcomePage.moreExtensions": "その他", + "welcomePage.installKeymapDescription": "キーボード ショートカットをインストールします", + "welcomePage.installKeymapExtension": "{0} と {1} のキーボード ショートカットをインストール", + "welcomePage.others": "その他", + "welcomePage.colorTheme": "配色テーマ", + "welcomePage.colorThemeDescription": "エディターとコードの外観を自由に設定します", + "welcomePage.learn": "学ぶ", + "welcomePage.showCommands": "すべてのコマンドの検索と実行", + "welcomePage.showCommandsDescription": "コマンド パレットからコマンドを検索してすばやくアクセスします ({0})", + "welcomePage.interfaceOverview": "インターフェイスの概要", + "welcomePage.interfaceOverviewDescription": "UI の主要コンポーネントを解説した視覚オーバーレイを表示します", + "welcomePage.deployToAzure": "クラウドにアプリケーションをデプロイ", + "welcomePage.deployToAzureDescription": "Node アプリケーションを Azure App Service にデプロイする方法を学ぶ", + "welcomePage.interactivePlayground": "対話型プレイグラウンド", + "welcomePage.interactivePlaygroundDescription": "エディターの基本機能を簡潔なチュートリアルで体験します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..3d8f77fd61 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "ワークベンチ", + "workbench.startupEditor.none": "エディターなしで開始", + "workbench.startupEditor.welcomePage": "ウェルカムページを開きます (既定)。", + "workbench.startupEditor.newUntitledFile": "無題の新規ファイルを開きます。", + "workbench.startupEditor": "前のセッションからエディターが復元されていない場合に、起動時に表示するかどうかを制御します。'none' を選択するとエディターなしで開始します。'welcomePage' を選択するとウェルカム ページを開きます (既定)。'newUntitledFile' を選択すると新しい無題のファイルを開きます (空のワークスペースを開いているときのみ)。", + "help": "ヘルプ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..841f69dc47 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "ようこそ", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "Azure 拡張機能の表示", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "{0} のサポートは既にインストールされています。", + "welcomePage.willReloadAfterInstallingExtensionPack": "{0} に追加のサポートをインストールしたあと、ウィンドウが再度読み込まれます。", + "welcomePage.installingExtensionPack": "{0} に追加のサポートをインストールしています...", + "welcomePage.extensionPackNotFound": "ID {1} のサポート {0} は見つかりませんでした。", + "welcomePage.keymapAlreadyInstalled": "キーボード ショートカット {0} は既にインストールされています。", + "welcomePage.willReloadAfterInstallingKeymap": "キーボード ショートカット {0} をインストールした後、ウィンドウが再度読み込まれます。", + "welcomePage.installingKeymap": "{0} のキーボード ショートカットをインストールしています...", + "welcomePage.keymapNotFound": "ID {1} のキーボード ショートカット {0} は見つかりませんでした。", + "welcome.title": "ようこそ", + "welcomePage.openFolderWithPath": "パス {1} のフォルダー {0} を開く", + "welcomePage.extensionListSeparator": ",", + "welcomePage.installKeymap": "{0} キーマップをインストールする", + "welcomePage.installExtensionPack": "{0} に追加のサポートをインストールする", + "welcomePage.installedKeymap": "{0} キーマップは既にインストールされています", + "welcomePage.installedExtensionPack": "{0} のサポートは既にインストールされています", + "ok": "OK", + "details": "詳細", + "cancel": "キャンセル", + "welcomePage.buttonBackground": "ウェルカム ページのボタンの背景色。", + "welcomePage.buttonHoverBackground": "ウェルカム ページのボタンのホバー背景色。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..06d699f597 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "対話型プレイグラウンド", + "editorWalkThrough": "対話型プレイグラウンド" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..e5851e6b83 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "対話型プレイグラウンド", + "help": "ヘルプ", + "interactivePlayground": "対話型プレイグラウンド" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..db040ea5ce --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "上にスクロール (行)", + "editorWalkThrough.arrowDown": "下にスクロール (行)", + "editorWalkThrough.pageUp": "スクロール アップ (ページ)", + "editorWalkThrough.pageDown": "スクロール ダウン (ページ)" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..0326fabde6 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "バインドなし", + "walkThrough.gitNotFound": "システムに Git がインストールされていない可能性があります。", + "walkThrough.embeddedEditorBackground": "対話型プレイグラウンドの埋め込みエディターの背景色。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/jpn/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..da6cf2e53a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "構成の設定を提供します。", + "vscode.extension.contributes.configuration.title": "設定の概要です。このラベルは、設定ファイルでコメントの区切り文字として使用します。", + "vscode.extension.contributes.configuration.properties": "構成のプロパティの説明です。", + "scope.window.description": "ウィンドウ固有の構成。ユーザーまたはワークスペースの設定で構成できます。", + "scope.resource.description": "リソース固有の構成。ユーザー、ワークスペース、またはフォルダーの設定で構成できます。", + "scope.description": "構成が適用される範囲。 使用可能なスコープは `window` と ` resource` です。", + "invalid.type": "設定すると、'configuration.type' は 'オブジェクトに設定されなければなりません", + "invalid.title": "'configuration.title' は、文字列である必要があります", + "vscode.extension.contributes.defaultConfiguration": "言語ごとに既定のエディター構成の設定を提供します。", + "invalid.properties": "'configuration.properties' は、オブジェクトである必要があります", + "workspaceConfig.folders.description": "ワークスペースで読み込まれるフォルダーのリスト。ファイル パスである必要があります。例: `/root/folderA` または `./folderA` のようなワークスペース ファイルの場所に対して解決される相対パス。", + "workspaceConfig.folder.description": "ファイルパス。例: `/root/folderA` または `./folderA` のようなワークスペース ファイルの場所に対して解決される相対パス。", + "workspaceConfig.settings.description": "ワークスペースの設定" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/jpn/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..92c1da7925 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "設定を開く", + "close": "閉じる", + "saveAndRetry": "設定を保存して再試行", + "errorUnknownKey": "{1} は登録済みの構成ではないため、{0} に書き込むことができません。", + "errorInvalidFolderConfiguration": "{0} はフォルダーのリソース スコープをサポートしていないため、フォルダー設定に書き込むことができません。", + "errorInvalidUserTarget": "{0} はグローバル スコープをサポートしていないため、ユーザー設定に書き込むことができません。", + "errorInvalidFolderTarget": "リソースが指定されていないため、フォルダー設定に書き込むことができません。", + "errorNoWorkspaceOpened": "開いているワークスペースがないため、{0} に書き込むことができません。最初にワークスペースを開いてから、もう一度お試しください。", + "errorInvalidConfiguration": "設定を書き込めません。**User Settings** を開いて、ファイル内のエラー/警告を修正してからもう一度お試しください。", + "errorInvalidConfigurationWorkspace": "設定を書き込めません。**Workspace Settings** を開いて、ファイル内のエラー/警告を修正してからもう一度お試しください。", + "errorConfigurationFileDirty": "ファイルが変更されているため、設定を書き込めません。**User Settings** ファイルを保存してから、もう一度お試しください。", + "errorConfigurationFileDirtyWorkspace": "ファイルが変更されているため、設定を書き込めません。**Workspace Settings** ファイルを保存してから、もう一度お試しください。", + "userTarget": "ユーザー設定", + "workspaceTarget": "ワークスペースの設定", + "folderTarget": "フォルダーの設定" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/jpn/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..f1d19db6f0 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "ファイルに書き込めません。ファイルを開いて、ファイル内のエラー/警告を修正してからもう一度お試しください。", + "errorFileDirty": "ファイルがダーティ状態でありファイルに書き込めません。ファイルを保存してからもう一度お試しください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/jpn/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..0ca17d7348 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "テレメトリ", + "telemetry.enableCrashReporting": "クラッシュ レポートを Microsoft に送信するように設定します。\nこのオプションを有効にするには、再起動が必要です。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/jpn/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..25a9170d16 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "拡張機能ホストが 10 秒以内に開始されませんでした。先頭行で停止している可能性があり、続行するにはデバッガーが必要です。", + "extensionHostProcess.startupFail": "拡張機能ホストが 10 秒以内に開始されませんでした。問題が発生している可能性があります。", + "extensionHostProcess.error": "拡張機能ホストからのエラー: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..562f075c9d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "{0} を解析できません: {1}。", + "fileReadFail": "ファイル {0} を読み取れません: {1}。", + "jsonsParseFail": "{0} または {1} を解析できませんでした: {2}。", + "missingNLSKey": "キー {0} のメッセージが見つかりませんでした。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..f33d6c75a5 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "開発者ツール", + "restart": "拡張機能のホストを再起動", + "extensionHostProcess.crash": "拡張機能のホストが予期せずに終了しました。", + "extensionHostProcess.unresponsiveCrash": "拡張機能のホストが応答しないため終了しました。", + "overwritingExtension": "拡張機能 {0} を {1} で上書きしています。", + "extensionUnderDevelopment": "開発の拡張機能を {0} に読み込んでいます" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/jpn/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..f5fad9dfed --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "Microsoft .NET Framework 4.5 が必要です。リンクに移動してインストールしてください。", + "installNet": ".NET Framework 4.5 をダウンロードします", + "neverShowAgain": "今後は表示しない", + "trashFailed": "'{0}' をごみ箱に移動できませんでした" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/jpn/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..f28357ebc8 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "ファイルのリソース ({0}) が無効です", + "fileIsDirectoryError": "ファイルがディレクトリです ({0})", + "fileNotModifiedError": "ファイルは次の時点以後に変更されていません:", + "fileTooLargeError": "開くファイルが大きすぎます", + "fileBinaryError": "ファイルはバイナリのようなので、テキストとして開くことができません", + "fileNotFoundError": "ファイルが見つかりません ({0})", + "fileMoveConflict": "移動/コピーできません。移動/コピー先にファイルが既に存在します。", + "unableToMoveCopyError": "移動/コピーできません。ファイルが含まれるフォルダーが置き換わることになります。", + "foldersCopyError": "フォルダーをワークスペース内にコピーできません。個々のファイルを選択してコピーしてください。", + "fileModifiedError": "ファイルは次の時点以後に更新されました:", + "fileReadOnlyError": "ファイルは読み取り専用です" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/jpn/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..36855d03da --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "Unable to write because the file is dirty. Please save the **Keybindings** file and try again.", + "parseErrors": "キー バインドを書き込めません。**キー バインド ファイル**を開いて、ファイル内のエラー/警告を修正してからもう一度お試しください。", + "errorInvalidConfiguration": "キー バインドを書き込めません。**キー バインド ファイル**には、配列型ではないオブジェクトが存在します。クリーン アップするファイルを開いてからもう一度お試しください。", + "emptyKeybindingsHeader": "既定値を上書きするには、このファイル内にキー バインドを挿入します" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/jpn/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..a09b508f9d --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "空以外の値が必要です。", + "requirestring": "`{0}` プロパティは必須で、`string` 型でなければなりません", + "optstring": "`{0}` プロパティは省略するか、`string` 型にする必要があります", + "vscode.extension.contributes.keybindings.command": "キー バインドのトリガー時に実行するコマンドの識別子。", + "vscode.extension.contributes.keybindings.key": "キーまたはキー シーケンス (キーは + で区切り、シーケンスはスペースで区切る。例: Ctrl+O や Ctrl+L L のようにキーを同時に押す)。", + "vscode.extension.contributes.keybindings.mac": "Mac 固有のキーまたはキー シーケンス。", + "vscode.extension.contributes.keybindings.linux": "Linux 固有のキーまたはキー シーケンス。", + "vscode.extension.contributes.keybindings.win": "Windows 固有のキーまたはキー シーケンス。", + "vscode.extension.contributes.keybindings.when": "キーがアクティブの場合の条件。", + "vscode.extension.contributes.keybindings": "キー バインドを提供します。", + "invalid.keybindings": "正しくない `contributes.{0}`: {1}", + "unboundCommands": "他に使用できるコマンドは次のとおりです: ", + "keybindings.json.title": "キー バインドの構成", + "keybindings.json.key": "キーまたはキー シーケンス (スペースで区切る) を押します", + "keybindings.json.command": "実行するコマンドの名前", + "keybindings.json.when": "キーがアクティブの場合の条件。", + "keybindings.json.args": "実行するコマンドに渡す引数。", + "keyboardConfigurationTitle": "キーボード", + "dispatch": "`keydown.code` (推奨) または `keydown.keyCode` のいずれかを使用するキー操作のディスパッチ ロジックを制御します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/jpn/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..7744e996ef --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "エラー: {0}", + "alertWarningMessage": "警告: {0}", + "alertInfoMessage": "情報: {0}", + "error": "エラー", + "warning": "警告", + "info": "情報", + "close": "閉じる" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/jpn/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..a3b9fc836b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "はい(&&Y)", + "cancelButton": "キャンセル" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/jpn/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..4eb3c7d9f5 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "言語の宣言を提供します。", + "vscode.extension.contributes.languages.id": "言語の ID。", + "vscode.extension.contributes.languages.aliases": "言語の名前のエイリアス。", + "vscode.extension.contributes.languages.extensions": "言語に関連付けられているファイルの拡張子。", + "vscode.extension.contributes.languages.filenames": "言語に関連付けられたファイル名。", + "vscode.extension.contributes.languages.filenamePatterns": "言語に関連付けられたファイル名の glob パターン。", + "vscode.extension.contributes.languages.mimetypes": "言語に関連付けられている MIME の種類。", + "vscode.extension.contributes.languages.firstLine": "言語のファイルの最初の行に一致する正規表現。", + "vscode.extension.contributes.languages.configuration": "言語の構成オプションを含むファイルへの相対パス。", + "invalid": "`contributes.{0}` が無効です。配列が必要です。", + "invalid.empty": "`contributes.{0}` に対する空の値", + "require.id": "`{0}` プロパティは必須で、`string` 型でなければなりません", + "opt.extensions": "`{0}` プロパティを省略するか、`string[]` 型にする必要があります", + "opt.filenames": "`{0}` プロパティを省略するか、`string[]` 型にする必要があります", + "opt.firstLine": "`{0}` プロパティを省略するか、`string` 型にする必要があります", + "opt.configuration": "`{0}` プロパティを省略するか、`string` 型にする必要があります", + "opt.aliases": "`{0}` プロパティを省略するか、`string[]` 型にする必要があります", + "opt.mimetypes": "`{0}` プロパティを省略するか、`string[]` 型にする必要があります" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/jpn/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..441530a01b --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1}", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/jpn/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..816dd0f1ad --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "TextMate トークナイザーを提供します。", + "vscode.extension.contributes.grammars.language": "この構文の提供先の言語識別子です。", + "vscode.extension.contributes.grammars.scopeName": "tmLanguage ファイルにより使用される TextMate スコープ名。", + "vscode.extension.contributes.grammars.path": "tmLanguage ファイルのパス。拡張機能フォルダーの相対パスであり、通常 './syntaxes/' で始まります。", + "vscode.extension.contributes.grammars.embeddedLanguages": "この文法に言語が埋め込まれている場合は、言語 ID に対するスコープ名のマップ。", + "vscode.extension.contributes.grammars.injectTo": "この文法が挿入される言語の範囲名の一覧。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/jpn/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..63d02ea79f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "`contributes.{0}.language` で不明な言語です。提供された値: {1}", + "invalid.scopeName": "`contributes.{0}.scopeName` には文字列が必要です。提供された値: {1}", + "invalid.path.0": "`contributes.{0}.path` に文字列が必要です。提供された値: {1}", + "invalid.injectTo": "`contributes.{0}.injectTo` の値が無効です。言語の範囲名の配列である必要があります。指定された値: {1}", + "invalid.embeddedLanguages": "`contributes.{0}.embeddedLanguages` の値が無効です。スコープ名から言語へのオブジェクト マップである必要があります。指定された値: {1}", + "invalid.path.1": "拡張機能のフォルダー ({2}) の中に `contributes.{0}.path` ({1}) が含まれている必要があります。これにより拡張を移植できなくなる可能性があります。", + "no-tm-grammar": "この言語に対して TM 文法は登録されていません。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/jpn/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..d1dc0a2ba0 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "ファイルがダーティです。まず保存してから、別のエンコードで再度開いてください。", + "genericSaveError": "'{0}' の保存に失敗しました: {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/jpn/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..3343e0ce25 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "ファイルをバックアップできませんでした (エラー: {0})。ファイルを保存しなおして終了してください。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/jpn/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..b766b76319 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "{0} に加えた変更を保存しますか?", + "saveChangesMessages": "次の {0} ファイルに対する変更を保存しますか?", + "moreFile": "...1 つの追加ファイルが表示されていません", + "moreFiles": "...{0} 個の追加ファイルが表示されていません", + "saveAll": "すべて保存(&&S)", + "save": "保存(&&S)", + "dontSave": "保存しない(&&N)", + "cancel": "キャンセル", + "saveChangesDetail": "保存しないと変更内容が失われます。", + "allFiles": "すべてのファイル", + "noExt": "拡張子なし" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/jpn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..6d599e2a87 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "トークンの色とスタイル。", + "schema.token.foreground": "トークンの前景色。", + "schema.token.fontStyle": "ルールのフォント スタイル: '斜体'、'太字'、'下線' のいずれかまたはこれらの組み合わせ", + "schema.fontStyle.error": "フォント スタイルは '斜体'、'太字'、'下線'を組み合わせる必要があります。", + "schema.properties.name": "ルールの説明。", + "schema.properties.scope": "このルールに一致するスコープ セレクター。", + "schema.tokenColors.path": "tmTheme ファイルへのパス (現在のファイルとの相対パス)。", + "schema.colors": "構文の強調表示をする色" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/jpn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..bb0368bd17 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "折りたたんだフォルダーのフォルダー アイコン。展開したフォルダー アイコンは省略可能です。設定していない場合は、フォルダーに定義したアイコンが表示されます。", + "schema.folder": "折りたたんだフォルダー、または folderExpanded が設定されていない場合は展開したフォルダーのフォルダー アイコン。", + "schema.file": "どの拡張子、ファイル名、または言語 ID とも一致しないファイルすべてに表示される既定のファイル アイコン。", + "schema.folderNames": "フォルダー名をアイコンに関連付けます。オブジェクト キーはフォルダー名ですが、パスの部分は含みません。パターンやワイルドカードは使用できません。フォルダー名の一致では大文字と小文字を区別しません。", + "schema.folderName": "関連付けのためのアイコン定義の ID。", + "schema.folderNamesExpanded": "フォルダー名を展開したフォルダーのアイコンに関連付けます。オブジェクト キーはフォルダー名ですが、パスの部分は含みません。パターンやワイルドカードは使用できません。フォルダー名の一致では大文字と小文字を区別しません。", + "schema.folderNameExpanded": "関連付けのためのアイコン定義の ID。", + "schema.fileExtensions": "ファイル拡張子をアイコンに関連付けます。オブジェクト キーはファイル拡張子名です。拡張子名は、最後のドットに続くファイル名の最後の部分です (ドットは含みません)。拡張子の比較は大文字と小文字が区別しないで行われます。", + "schema.fileExtension": "関連付けのためのアイコン定義の ID。", + "schema.fileNames": "ファイル名をアイコンに関連付けます。オブジェクト キーは完全なファイル名ですが、パスの部分は含みません。ファイル名にはドットおよび可能なファイル拡張子が含まれる場合があります。パターンやワイルドカードは使用できません。ファイル名の一致では大文字と小文字を区別しません。", + "schema.fileName": "関連付けのためのアイコン定義の ID。", + "schema.languageIds": "言語をアイコンに関連付けます。オブジェクト キーは言語のコントリビューション ポイントで定義された言語 ID です。", + "schema.languageId": "関連付けのためのアイコン定義の ID。", + "schema.fonts": "アイコンの定義に使用されるフォント。", + "schema.id": "フォントの ID。", + "schema.src": "フォントの場所。", + "schema.font-path": "現在のアイコン テーマ ファイルへのフォントの相対パス。", + "schema.font-format": "フォントの書式。", + "schema.font-weight": "フォントの太さ。", + "schema.font-sstyle": "フォント スタイル。", + "schema.font-size": "フォントの既定サイズ。", + "schema.iconDefinitions": "ファイルをアイコンに関連付けるときに使用できるすべてのアイコンの説明です。", + "schema.iconDefinition": "アイコンの定義です。オブジェクト キーは定義の ID です。", + "schema.iconPath": "SVG または PNG を使用する場合: イメージへのパス。アイコン設定ファイルへの相対パスです。", + "schema.fontCharacter": "グリフ フォントを使用する場合: 使用するフォントの文字。", + "schema.fontColor": "グリフ フォントを使用する場合: 使用する色。", + "schema.fontSize": "フォントを使用する場合: テキスト フォントに対するフォントサイズの割合。設定されていない場合、既定値はフォント定義のサイズになります。", + "schema.fontId": "フォントを使用する場合: フォントの ID。設定されていない場合、既定値は最初のフォント定義になります。", + "schema.light": "明るい配色テーマでのファイル アイコンの任意の関連付け。", + "schema.highContrast": "ハイ コントラスト配色テーマでのファイル アイコンの任意の関連付け。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..4b984fe96e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "JSON テーマ ファイルの解析中に問題が発生しました: {0}", + "error.invalidformat.colors": "配色テーマ ファイルの解析中に問題が発生しました: {0}。'colors' プロパティは 'object' 型ではありません。", + "error.invalidformat.tokenColors": "配色テーマ ファイルを解析中に問題が発生しました: {0}。'tokenColors' プロパティには配色を指定した配列、または TextMate テーマ ファイルへのパスを指定してください。", + "error.plist.invalidformat": "tmTheme ファイルの解析中に問題が発生しました: {0}。'settings' は配列ではありません。", + "error.cannotparse": "tmTheme ファイルの解析中に問題が発生しました: {0}", + "error.cannotload": "tmTheme ファイル {0} の読み込み中に問題が発生しました: {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..f831416ea8 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "TextMate の配色テーマを提供します。", + "vscode.extension.contributes.themes.id": "ユーザー設定で使用されるアイコン テーマの ID。", + "vscode.extension.contributes.themes.label": "UI で表示される配色テーマのラベル。", + "vscode.extension.contributes.themes.uiTheme": "エディターの周囲の色を定義する基本テーマ: 'vs' は明るい色のテーマで、'vs-dark' は濃い色のテーマです。'hc-black' は濃い色のハイ コントラストのテーマです。", + "vscode.extension.contributes.themes.path": "tmTheme ファイルのパス。拡張機能フォルダーに対する相対パスで、通常 './themes/themeFile.tmTheme' です。", + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "ユーザー設定で使用されるアイコン テーマの ID。", + "vscode.extension.contributes.iconThemes.label": "UI に表示されるアイコン テーマのラベル。", + "vscode.extension.contributes.iconThemes.path": "アイコン テーマの定義ファイルのパス。このパスは拡張フォルダーの相対パスであり、通常は './icons/awesome-icon-theme.json' です。", + "migration.completed": "ユーザー設定に新しいテーマの設定が追加されました。{0} に利用可能なバックアップがあります。", + "error.cannotloadtheme": "Unable to load {0}: {1}", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "`contributes.{0}.path` に文字列が必要です。提供された値: {1}", + "invalid.path.1": "拡張機能のフォルダー ({2}) の中に `contributes.{0}.path` ({1}) が含まれている必要があります。これにより拡張を移植できなくなる可能性があります。", + "reqid": "`contributes.{0}.id` に文字列が必要です。提供された値: {1}", + "error.cannotloadicontheme": "Unable to load {0}", + "error.cannotparseicontheme": "Problems parsing file icons file: {0}", + "colorTheme": "ワークベンチで使用する配色テーマを指定します。", + "colorThemeError": "Theme is unknown or not installed.", + "iconTheme": "ワークベンチで使用するアイコンのテーマを指定します。'null' を指定するとファイル アイコンが表示されなくなります。", + "noIconThemeDesc": "ファイル アイコンがありません", + "iconThemeError": "ファイル アイコンのテーマが不明、またはインストールされていません。", + "workbenchColors": "現在選択している配色テーマで配色を上書きします。", + "workbenchColors.deprecated": "この設定はもう試験的なものではなく、名前が 'workbench.colorCustomizations' に変更されています", + "workbenchColors.deprecatedDescription": "代わりに 'workbench.colorCustomizations' を使用してください", + "editorColors": "現在選択している配色テーマで配色とフォント スタイルを上書きします。", + "editorColors.comments": "コメントの色とスタイルを設定します", + "editorColors.strings": "文字列リテラルの色とスタイルを設定します。", + "editorColors.keywords": "キーワードの色とスタイルを設定します。", + "editorColors.numbers": "数値リテラルの色とスタイルを設定します。", + "editorColors.types": "型定義と参照の色とスタイルを設定します。", + "editorColors.functions": "関数定義と参照の色とスタイルを設定します。", + "editorColors.variables": "変数定義と参照の色とスタイルを設定します。", + "editorColors.textMateRules": "textmate テーマ規則 (高度) を使っての色とスタイルを設定します。" +} \ No newline at end of file diff --git a/i18n/kor/extensions/configuration-editing/out/extension.i18n.json b/i18n/kor/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..ab3696ed97 --- /dev/null +++ b/i18n/kor/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "예" +} \ No newline at end of file diff --git a/i18n/kor/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/kor/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..17175ffc9a --- /dev/null +++ b/i18n/kor/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "예: myFile.txt", + "activeEditorMedium": "예: myFolder/myFile.txt", + "activeEditorLong": "예: /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "예: myFolder1, myFolder2, myFolder3", + "rootPath": "예: /Users/Development/myProject", + "folderName": "예: myFolder", + "folderPath": "예: /Users/Development/myFolder", + "appName": "예: VS Code", + "dirty": "활성 편집기가 더티인 경우 더티 표시기", + "separator": "값이 있는 변수로 둘러싸인 경우에만 표시되는 조건부 구분 기호 (' - ')", + "assocLabelFile": "확장명이 있는 파일", + "assocDescriptionFile": "파일 이름에서 GLOB 패턴과 일치하는 모든 파일을 지정된 ID를 사용하는 언어에 매핑합니다.", + "assocLabelPath": "경로가 있는 파일", + "assocDescriptionPath": "경로에서 절대 경로 GLOB 패턴과 일치하는 모든 파일을 지정된 ID를 사용하는 언어에 매핑합니다.", + "fileLabel": "확장명별 파일", + "fileDescription": "특정 파일 확장명이 있는 모든 파일을 일치시킵니다.", + "filesLabel": "여러 확장명이 있는 파일", + "filesDescription": "파일 확장명이 있는 모든 파일을 일치시킵니다.", + "derivedLabel": "이름별 형제가 있는 파일", + "derivedDescription": "동일한 이름의 형제가 있지만 확장명이 다른 파일을 일치시킵니다.", + "topFolderLabel": "이름별 폴더(최상위)", + "topFolderDescription": "특정 이름의 최상위 폴더를 일치시킵니다.", + "topFoldersLabel": "이름이 여러 개 있는 폴더(최상위)", + "topFoldersDescription": "여러 최상위 폴더를 일치시킵니다.", + "folderLabel": "이름별 폴더(모든 위치)", + "folderDescription": "모든 위치에 있는 특정 이름의 폴더를 일치시킵니다.", + "falseDescription": "패턴을 사용하지 않도록 설정합니다.", + "trueDescription": "패턴을 사용하도록 설정합니다.", + "siblingsDescription": "동일한 이름의 형제가 있지만 확장명이 다른 파일을 일치시킵니다.", + "languageSpecificEditorSettings": "언어별 편집기 설정", + "languageSpecificEditorSettingsDescription": "언어용 편집기 설정 재정의" +} \ No newline at end of file diff --git a/i18n/kor/extensions/css/client/out/cssMain.i18n.json b/i18n/kor/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..15b1c4d449 --- /dev/null +++ b/i18n/kor/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "CSS 언어 서버" +} \ No newline at end of file diff --git a/i18n/kor/extensions/css/package.i18n.json b/i18n/kor/extensions/css/package.i18n.json new file mode 100644 index 0000000000..cc31d9b801 --- /dev/null +++ b/i18n/kor/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "잘못된 매개 변수 수", + "css.lint.boxModel.desc": "패딩 또는 테두리를 사용하는 경우 너비 또는 높이를 사용하지 마세요.", + "css.lint.compatibleVendorPrefixes.desc": "공급업체 관련 접두사를 사용할 경우 다른 모든 공급업체 관련 속성도 포함합니다.", + "css.lint.duplicateProperties.desc": "중복된 스타일 정의를 사용하지 마세요.", + "css.lint.emptyRules.desc": "빈 규칙 집합을 사용하지 마세요.", + "css.lint.float.desc": "'float'를 사용하지 않도록 합니다. Float를 사용하면 레이아웃의 한쪽이 바뀔 경우 CSS가 쉽게 깨질 수 있습니다.", + "css.lint.fontFaceProperties.desc": "@font-face 규칙에서 'src' 및 'font-family' 속성을 정의해야 합니다.", + "css.lint.hexColorLength.desc": "16진수 색은 3개 또는 6개의 16진수로 구성되어야 합니다.", + "css.lint.idSelector.desc": "이러한 규칙은 HTML과 긴밀하게 결합되므로 선택기에 ID를 포함하면 안 됩니다.", + "css.lint.ieHack.desc": "IE 핵(Hack)은 IE7 이상을 지원할 때만 필요합니다.", + "css.lint.important.desc": "!important는 사용하지 않도록 합니다. 이것은 전체 CSS의 특정성에 문제가 있어서 리팩터링해야 함을 나타냅니다.", + "css.lint.importStatement.desc": "Import 문은 병렬로 로드되지 않습니다.", + "css.lint.propertyIgnoredDueToDisplay.desc": "display 때문에 속성이 무시됩니다. 예를 들어 'display: inline'을 사용할 경우 width, height, margin-top, margin-bottom 및 float 속성은 적용되지 않습니다.", + "css.lint.universalSelector.desc": "범용 선택기 (*)는 느린 것으로 알려져 있습니다.", + "css.lint.unknownProperties.desc": "알 수 없는 속성입니다.", + "css.lint.unknownVendorSpecificProperties.desc": "알 수 없는 공급업체 관련 속성입니다.", + "css.lint.vendorPrefix.desc": "공급업체 관련 접두사를 사용할 때 표준 속성도 포함합니다.", + "css.lint.zeroUnits.desc": "0에는 단위가 필요하지 않습니다.", + "css.trace.server.desc": "VS Code와 CSS 언어 서버 간 통신을 추적합니다.", + "css.validate.title": "CSS 유효성 검사 및 문제 심각도를 제어합니다.", + "css.validate.desc": "모든 유효성 검사 사용 또는 사용 안 함", + "less.lint.argumentsInColorFunction.desc": "잘못된 매개 변수 수", + "less.lint.boxModel.desc": "패딩 또는 테두리를 사용하는 경우 너비 또는 높이를 사용하지 마세요.", + "less.lint.compatibleVendorPrefixes.desc": "공급업체 관련 접두사를 사용할 경우 다른 모든 공급업체 관련 속성도 포함합니다.", + "less.lint.duplicateProperties.desc": "중복된 스타일 정의를 사용하지 마세요.", + "less.lint.emptyRules.desc": "빈 규칙 집합을 사용하지 마세요.", + "less.lint.float.desc": "'float'를 사용하지 않도록 합니다. Float를 사용하면 레이아웃의 한쪽이 바뀔 경우 CSS가 쉽게 깨질 수 있습니다.", + "less.lint.fontFaceProperties.desc": "@font-face 규칙에서 'src' 및 'font-family' 속성을 정의해야 합니다.", + "less.lint.hexColorLength.desc": "16진수 색은 3개 또는 6개의 16진수로 구성되어야 합니다.", + "less.lint.idSelector.desc": "이러한 규칙은 HTML과 긴밀하게 결합되므로 선택기에 ID를 포함하면 안 됩니다.", + "less.lint.ieHack.desc": "IE 핵(Hack)은 IE7 이상을 지원할 때만 필요합니다.", + "less.lint.important.desc": "!important는 사용하지 않도록 합니다. 이것은 전체 CSS의 특정성에 문제가 있어서 리팩터링해야 함을 나타냅니다.", + "less.lint.importStatement.desc": "Import 문은 병렬로 로드되지 않습니다.", + "less.lint.propertyIgnoredDueToDisplay.desc": "display 때문에 속성이 무시됩니다. 예를 들어 'display: inline'을 사용할 경우 width, height, margin-top, margin-bottom 및 float 속성은 적용되지 않습니다.", + "less.lint.universalSelector.desc": "범용 선택기 (*)는 느린 것으로 알려져 있습니다.", + "less.lint.unknownProperties.desc": "알 수 없는 속성입니다.", + "less.lint.unknownVendorSpecificProperties.desc": "알 수 없는 공급업체 관련 속성입니다.", + "less.lint.vendorPrefix.desc": "공급업체 관련 접두사를 사용할 때 표준 속성도 포함합니다.", + "less.lint.zeroUnits.desc": "0에는 단위가 필요하지 않습니다.", + "less.validate.title": "LESS 유효성 검사 및 문제 심각도를 제어합니다.", + "less.validate.desc": "모든 유효성 검사 사용 또는 사용 안 함", + "scss.lint.argumentsInColorFunction.desc": "잘못된 매개 변수 수", + "scss.lint.boxModel.desc": "패딩 또는 테두리를 사용하는 경우 너비 또는 높이를 사용하지 마세요.", + "scss.lint.compatibleVendorPrefixes.desc": "공급업체 관련 접두사를 사용할 경우 다른 모든 공급업체 관련 속성도 포함합니다.", + "scss.lint.duplicateProperties.desc": "중복된 스타일 정의를 사용하지 마세요.", + "scss.lint.emptyRules.desc": "빈 규칙 집합을 사용하지 마세요.", + "scss.lint.float.desc": "'float'를 사용하지 않도록 합니다. Float를 사용하면 레이아웃의 한쪽이 바뀔 경우 CSS가 쉽게 깨질 수 있습니다.", + "scss.lint.fontFaceProperties.desc": "@font-face 규칙에서 'src' 및 'font-family' 속성을 정의해야 합니다.", + "scss.lint.hexColorLength.desc": "16진수 색은 3개 또는 6개의 16진수로 구성되어야 합니다.", + "scss.lint.idSelector.desc": "이러한 규칙은 HTML과 긴밀하게 결합되므로 선택기에 ID를 포함하면 안 됩니다.", + "scss.lint.ieHack.desc": "IE 핵(Hack)은 IE7 이상을 지원할 때만 필요합니다.", + "scss.lint.important.desc": "!important는 사용하지 않도록 합니다. 이것은 전체 CSS의 특정성에 문제가 있어서 리팩터링해야 함을 나타냅니다.", + "scss.lint.importStatement.desc": "Import 문은 병렬로 로드되지 않습니다.", + "scss.lint.propertyIgnoredDueToDisplay.desc": "display 때문에 속성이 무시됩니다. 예를 들어 'display: inline'을 사용할 경우 width, height, margin-top, margin-bottom 및 float 속성은 적용되지 않습니다.", + "scss.lint.universalSelector.desc": "범용 선택기 (*)는 느린 것으로 알려져 있습니다.", + "scss.lint.unknownProperties.desc": "알 수 없는 속성입니다.", + "scss.lint.unknownVendorSpecificProperties.desc": "알 수 없는 공급업체 관련 속성입니다.", + "scss.lint.vendorPrefix.desc": "공급업체 관련 접두사를 사용할 때 표준 속성도 포함합니다.", + "scss.lint.zeroUnits.desc": "0에는 단위가 필요하지 않습니다.", + "scss.validate.title": "SCSS 유효성 검사 및 문제 심각도를 제어합니다.", + "scss.validate.desc": "모든 유효성 검사 사용 또는 사용 안 함", + "less.colorDecorators.enable.desc": "색 데코레이터 사용 또는 사용 안 함", + "scss.colorDecorators.enable.desc": "색 데코레이터 사용 또는 사용 안 함", + "css.colorDecorators.enable.desc": "색 데코레이터 사용 또는 사용 안 함", + "css.colorDecorators.enable.deprecationMessage": "`css.colorDecorators.enable` 설정은 `editor.colorDecorators`를 위해 사용되지 않습니다.", + "scss.colorDecorators.enable.deprecationMessage": "`scss.colorDecorators.enable` 설정은 `editor.colorDecorators`를 위해 사용되지 않습니다.", + "less.colorDecorators.enable.deprecationMessage": "`less.colorDecorators.enable` 설정은 `editor.colorDecorators`를 위해 사용되지 않습니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/emmet/package.i18n.json b/i18n/kor/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..fda6a642d3 --- /dev/null +++ b/i18n/kor/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "약어로 래핑", + "command.wrapIndividualLinesWithAbbreviation": "약어로 개별 줄 래핑", + "command.removeTag": "태그 제거", + "command.updateTag": "태그 업데이트", + "command.matchTag": "일치하는 쌍으로 이동", + "command.balanceIn": "균형있게(안쪽으로)", + "command.balanceOut": "균형있게(바깥쪽으로)", + "command.prevEditPoint": "이전 편집 점으로 이동", + "command.nextEditPoint": "다음 편집 점으로 이동", + "command.mergeLines": "줄 병합", + "command.selectPrevItem": "이전 항목 선택", + "command.selectNextItem": "다음 항목 선택", + "command.splitJoinTag": "태그 분할/조인", + "command.toggleComment": "주석 토글", + "command.evaluateMathExpression": "수식 평가", + "command.updateImageSize": "이미지 크기 업데이트", + "command.reflectCSSValue": "CSS 값 반영", + "command.incrementNumberByOne": "1씩 증가", + "command.decrementNumberByOne": "1씩 감소", + "command.incrementNumberByOneTenth": "0.1씩 증가", + "command.decrementNumberByOneTenth": "0.1씩 감소", + "command.incrementNumberByTen": "10씩 증가", + "command.decrementNumberByTen": "10씩 감소", + "emmetSyntaxProfiles": "지정된 구문에 대한 프로필을 정의하거나 특정 규칙이 포함된 고유한 프로필을 사용하세요.", + "emmetExclude": "Emmet 약어를 확장하면 안 되는 언어의 배열입니다.", + "emmetExtensionsPath": "Emmet 프로필 및 코드 조각이 포함된 폴더의 경로입니다.'", + "emmetShowExpandedAbbreviation": "확장된 emmet 약어를 제안으로 표시합니다.\n\"inMarkupAndStylesheetFilesOnly\" 옵션이 html, haml, jade, slim, xml, xsl, css, scss, sass, less 및 stylus에 적용됩니다.\n\"always\" 옵션이 마크업/css에 관계없이 파일의 모든 부분에 적용됩니다.", + "emmetShowAbbreviationSuggestions": "가능한 emmet 약어를 제안으로 표시합니다. 스타일시트에는 적용되지 않고 emmet.showExpandedAbbreviation이 \"never\"로 설정되어 있을 때도 적용되지 않습니다.", + "emmetIncludeLanguages": "기본 지원되지 않는 언어에서 emmet 약어를 사용합니다. 언어와 emmet 지원 언어 사이에 매핑을 추가합니다.\n예: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "emmet 조각에 사용되는 변수", + "emmetTriggerExpansionOnTab": "사용하도록 설정하면 emmet 약어는 키를 눌렀을 때 확장됩니다.", + "emmetPreferences": "Emmet의 일부 작업 및 해결 프로그램의 동작을 수정하는 데 사용되는 기본 설정입니다.", + "emmetPreferencesIntUnit": "정수 값의 기본 단위", + "emmetPreferencesFloatUnit": "부동 소수점 값의 기본 단위", + "emmetPreferencesCssAfter": "CSS 약어를 확장할 때 CSS 속성의 끝에 배치할 기호", + "emmetPreferencesSassAfter": "Sass 파일에서 CSS 약어를 확장할 때 CSS 속성의 끝에 배치할 기호", + "emmetPreferencesStylusAfter": "Stylus 파일에서 CSS 약어를 확장할 때 CSS 속성의 끝에 배치할 기호", + "emmetPreferencesCssBetween": "CSS 약어를 확장할 때 CSS 속성 및 값 사이에 배치할 기호", + "emmetPreferencesSassBetween": "Sass 파일에서 CSS 약어를 확장할 때 CSS 속성 및 값 사이에 배치할 기호", + "emmetPreferencesStylusBetween": "Stylus 파일에서 CSS 약어를 확장할 때 CSS 속성 및 값 사이에 배치할 기호", + "emmetShowSuggestionsAsSnippets": "True이면 emmet 제안이 코드 조각으로 표시되며 editor.snippetSuggestions 설정에 따라 코드 조각을 정렬할 수 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/kor/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..58a2f2e161 --- /dev/null +++ b/i18n/kor/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "이미지는 HTTPS 프로토콜을 사용해야 합니다.", + "svgsNotValid": "SVG는 올바른 이미지 소스가 아닙니다.", + "embeddedSvgsNotValid": "내장 SVG는 올바른 이미지 소스가 아닙니다.", + "dataUrlsNotValid": "데이터 URL은 올바른 이미지 소스가 아닙니다.", + "relativeUrlRequiresHttpsRepository": "관계형 이미지 URL은 package.json에 HTTPS 프로토콜이 지정된 저장소가 필요합니다.", + "relativeIconUrlRequiresHttpsRepository": "아이콘은 package.json에 HTTPS 프로토콜이 지정된 저장소가 필요합니다.", + "relativeBadgeUrlRequiresHttpsRepository": "관계형 배지 URL은 package.json에 HTTPS 프로토콜이 지정된 저장소가 필요합니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/kor/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..e54fadcbf3 --- /dev/null +++ b/i18n/kor/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "언어별 편집기 설정", + "languageSpecificEditorSettingsDescription": "언어용 편집기 설정 재정의" +} \ No newline at end of file diff --git a/i18n/kor/extensions/git/out/askpass-main.i18n.json b/i18n/kor/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..073a4b685f --- /dev/null +++ b/i18n/kor/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "자격 증명이 없거나 잘못되었습니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/git/out/commands.i18n.json b/i18n/kor/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..91f6b9a29b --- /dev/null +++ b/i18n/kor/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "{0}의 태그", + "remote branch at": "{0}에서 원격 분기", + "create branch": "$(plus) 새 분기 생성", + "repourl": "리포지토리 URL", + "parent": "부모 디렉터리", + "cloning": "Git 리포지토리를 복제하는 중...", + "openrepo": "리포지토리 열기", + "proposeopen": "복제된 리포지토리를 여시겠습니까?", + "path to init": "폴더 경로", + "provide path": "Git 리포지토리를 초기화할 폴더 경로를 입력하세요.", + "HEAD not available": "'{0}'의 HEAD 버전이 없습니다.", + "confirm stage files with merge conflicts": "병합 충돌이 있는 {0} 파일을 스테이징하시겠습니까?", + "confirm stage file with merge conflicts": "병합 충돌이 있는 {0}을(를) 스테이징하시겠습니까?", + "yes": "예", + "confirm revert": "{0}에서 선택한 변경 내용을 되돌리시겠습니까?", + "revert": "변경 내용 되돌리기", + "discard": "변경 내용 취소", + "confirm delete": "{0}을(를) 삭제하시겠습니까?", + "delete file": "파일 삭제", + "confirm discard": "{0}의 변경 내용을 취소하시겠습니까?", + "confirm discard multiple": "{0}개 파일의 변경 내용을 취소하시겠습니까?", + "warn untracked": "{0}개의 추적되지 않은 파일을 삭제합니다.", + "confirm discard all single": "{0}의 변경 내용을 취소하시겠습니까?", + "confirm discard all": "{0} 파일에서 변경 내용을 모두 취소하시겠습니까?\n이 작업은 되돌릴 수 없습니다.\n현재 작업 설정이 영구적으로 손실됩니다.", + "discardAll multiple": "1개 파일 취소", + "discardAll": "{0}개 파일 모두 버리기", + "confirm delete multiple": "{0}개 파일을 삭제하시겠습니까?", + "delete files": "파일 삭제", + "there are untracked files single": "취소한 경우 다음 추적되지 않은 파일이 디스크에서 삭제됩니다. {0}.", + "there are untracked files": "취소하는 경우 {0}개의 추적되지 않은 파일이 디스크에서 삭제됩니다.", + "confirm discard all 2": "{0}\n\n이 작업은 되돌릴 수 없으며, 현재 작업 설정이 영구적으로 손실됩니다.", + "yes discard tracked": "1개의 추적된 파일 취소", + "yes discard tracked multiple": "{0}개의 추적된 파일 취소", + "no staged changes": "저장할 단계적 변경 사항이 없습니다.\n\n모든 변경 사항을 자동으로 스테이징하고 직접 저장하시겠습니까?", + "always": "항상", + "no changes": "커밋할 변경 내용이 없습니다.", + "commit message": "커밋 메시지", + "provide commit message": "커밋 메시지를 제공하세요.", + "select a ref to checkout": "체크아웃할 참조 선택", + "branch name": "분기 이름", + "provide branch name": "분기 이름을 입력하세요.", + "select branch to delete": "삭제할 분기 선택", + "confirm force delete branch": "'{0}' 분기가 완벽히 병합되지 않았습니다. 그래도 삭제할까요?", + "delete branch": "분기 삭제", + "select a branch to merge from": "병합할 분기 선택", + "merge conflicts": "병합 충돌이 있습니다. 해결한 후 계속하십시오.", + "tag name": "태그 이름", + "provide tag name": "태그 이름을 입력하세요.", + "tag message": "메시지", + "provide tag message": "태그에 주석을 달 메시지를 입력하세요.", + "no remotes to pull": "리포지토리에 풀하도록 구성된 원격 항목이 없습니다.", + "pick remote pull repo": "분기를 가져올 원격 선택", + "no remotes to push": "리포지토리에 푸시하도록 구성된 원격이 없습니다.", + "push with tags success": "태그와 함께 푸시되었습니다.", + "nobranch": "원격에 푸시할 분기를 체크 아웃하세요.", + "pick remote": "'{0}' 분기를 다음에 게시하려면 원격을 선택하세요.", + "sync is unpredictable": "이 작업은 '{0}' 간에 커밋을 푸시하고 풀합니다.", + "ok": "확인", + "never again": "다시 표시 안 함", + "no remotes to publish": "리포지토리에 게시하도록 구성된 원격이 없습니다.", + "no changes stash": "스태시할 변경 내용이 없습니다.", + "provide stash message": "필요한 경우 스태시 메시지를 입력하세요.", + "stash message": "스태시 메시지", + "no stashes": "복원할 스태시가 없습니다.", + "pick stash to pop": "표시할 스태시 선택", + "clean repo": "체크 아웃하기 전에 리포지토리 작업 트리를 정리하세요.", + "cant push": "참조를 원격에 푸시할 수 없습니다. 먼저 '풀'을 실행하여 변경 내용을 통합하세요.", + "git error details": "Git: {0}", + "git error": "Git 오류", + "open git log": "Git 로그 열기" +} \ No newline at end of file diff --git a/i18n/kor/extensions/git/out/main.i18n.json b/i18n/kor/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..ad71cfb593 --- /dev/null +++ b/i18n/kor/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "{1}에서 git {0}을(를) 사용하는 중", + "updateGit": "Git 업데이트", + "neverShowAgain": "다시 표시 안 함", + "git20": "Git {0}이(가) 설치된 것 같습니다. 코드는 2 이하의 Git에서 최적으로 작동합니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/git/out/model.i18n.json b/i18n/kor/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..b8d8b7ca3e --- /dev/null +++ b/i18n/kor/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "사용 가능한 리포지토리가 없습니다.", + "pick repo": "리포지토리 선택" +} \ No newline at end of file diff --git a/i18n/kor/extensions/git/out/repository.i18n.json b/i18n/kor/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..6597ee74cd --- /dev/null +++ b/i18n/kor/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "열기", + "index modified": "인덱스 수정됨", + "modified": "수정됨", + "index added": "인덱스 추가됨", + "index deleted": "인덱스 삭제됨", + "deleted": "삭제됨", + "index renamed": "인덱스 이름 변경됨", + "index copied": "인덱스 복사됨", + "untracked": "추적되지 않음", + "ignored": "무시됨", + "both deleted": "둘 다 삭제됨", + "added by us": "본인이 추가함", + "deleted by them": "타인이 삭제함", + "added by them": "타인이 추가함", + "deleted by us": "본인이 삭제함", + "both added": "둘 다 추가됨", + "both modified": "둘 다 수정됨", + "commit": "커밋", + "merge changes": "변경 내용 병합", + "staged changes": "스테이징된 변경 내용", + "changes": "변경 내용", + "ok": "확인", + "neveragain": "다시 표시 안 함", + "huge": "'{0}'의 Git 리포지토리에 활성 변경 내용이 너무 많습니다. Git 기능의 하위 집합만 사용할 수 있도록 설정됩니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/git/out/scmProvider.i18n.json b/i18n/kor/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..525bb2c93a --- /dev/null +++ b/i18n/kor/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "커밋" +} \ No newline at end of file diff --git a/i18n/kor/extensions/git/out/statusbar.i18n.json b/i18n/kor/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..7d89477c39 --- /dev/null +++ b/i18n/kor/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "체크 아웃...", + "sync changes": "변경 내용 동기화", + "publish changes": "변경 내용 게시", + "syncing changes": "변경 내용을 동기화하는 중..." +} \ No newline at end of file diff --git a/i18n/kor/extensions/git/package.i18n.json b/i18n/kor/extensions/git/package.i18n.json new file mode 100644 index 0000000000..feb25360cd --- /dev/null +++ b/i18n/kor/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "복제", + "command.init": "리포지토리 초기화", + "command.close": "리포지토리 닫기", + "command.refresh": "새로 고침", + "command.openChange": "변경 내용 열기", + "command.openFile": "파일 열기", + "command.openHEADFile": "파일 열기(HEAD)", + "command.stage": "변경 내용 스테이징", + "command.stageAll": "모든 변경 내용 스테이징", + "command.stageSelectedRanges": "선택한 범위 스테이징", + "command.revertSelectedRanges": "선택한 범위 되돌리기", + "command.unstage": "변경 내용 스테이징 취소", + "command.unstageAll": "모든 변경 내용 스테이징 취소", + "command.unstageSelectedRanges": "선택한 범위 스테이징 취소", + "command.clean": "변경 내용 취소", + "command.cleanAll": "모든 변경 내용 취소", + "command.commit": "Commit", + "command.commitStaged": "스테이징된 항목 커밋", + "command.commitStagedSigned": "스테이징된 항목 커밋(로그오프됨)", + "command.commitStagedAmend": "스테이징된 항목 커밋(수정)", + "command.commitAll": "모두 커밋", + "command.commitAllSigned": "모두 커밋(로그오프됨)", + "command.commitAllAmend": "모두 커밋 (수정)", + "command.undoCommit": "마지막 커밋 실행 취소", + "command.checkout": "다음으로 체크 아웃...", + "command.branch": "분기 만들기...", + "command.deleteBranch": "분기 삭제...", + "command.merge": "분기 병합...", + "command.createTag": "태그 생성", + "command.pull": "풀", + "command.pullRebase": "풀(다시 지정)", + "command.pullFrom": "가져올 위치...", + "command.push": "푸시", + "command.pushTo": "다음으로 푸시...", + "command.pushWithTags": "태그로 푸시", + "command.sync": "동기화", + "command.publish": "분기 게시", + "command.showOutput": "Git 출력 표시", + "command.ignore": ".gitignore에 파일 추가", + "command.stash": "스태시", + "command.stashPop": "스태시 표시...", + "command.stashPopLatest": "최신 슬래시 표시", + "config.enabled": "Git 사용 여부", + "config.path": "Git 실행 파일의 경로", + "config.autorefresh": "자동 새로 고침 사용 여부", + "config.autofetch": "자동 가져오기 사용 여부", + "config.enableLongCommitWarning": "긴 커밋 메시지에 대해 경고할지 여부입니다.", + "config.confirmSync": "Git 리포지토리를 동기화하기 전에 확인합니다.", + "config.countBadge": "Git 배지 카운터를 제어합니다. `all`이면 변경 내용을 모두 계산하고, `tracked`이면 추적된 변경 내용만 계산하고, `off`이면 해제합니다.", + "config.checkoutType": "`다음으로 체크 아웃...`을 실행할 때 나열되는 분기 유형을 제어합니다. `all`이면 모든 참조를 표시하고, `local`이면 로컬 분기만 표시하고, `tags`이면 태그만 표시하고, `remote`이면 원격 분기만 표시합니다.", + "config.ignoreLegacyWarning": "레거시 Git 경고를 무시합니다.", + "config.ignoreLimitWarning": "리포지토리에 변경 내용이 너무 많으면 경고를 무시합니다.", + "config.defaultCloneDirectory": "git 리포지토리를 복제할 기본 위치", + "config.enableSmartCommit": "단계적 변경 사항이 없는 경우 모든 변경 사항을 저장합니다.", + "config.enableCommitSigning": "GPG를 사용한 커밋 서명을 사용하도록 설정합니다.", + "config.discardAllScope": "`모든 변경 내용 취소` 명령으로 취소되는 변경 내용을 제어합니다. `모두`이면 모든 변경 내용을 취소합니다. `추적됨`이면 추적된 파일만 취소합니다. `프롬프트`이면 작업을 실행할 때마다 프롬프트 대화 상자를 표시합니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/grunt/out/main.i18n.json b/i18n/kor/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..ab1d8cf19a --- /dev/null +++ b/i18n/kor/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Grunt 자동 검색에 실패하고 [0} 오류가 발생했습니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/grunt/package.i18n.json b/i18n/kor/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..293c820a17 --- /dev/null +++ b/i18n/kor/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "Grunt 작업의 자동 검색을 사용할지 여부를 제어합니다. 기본값은 [켜기]입니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/gulp/out/main.i18n.json b/i18n/kor/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..ae3d306347 --- /dev/null +++ b/i18n/kor/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Gulp 자동 검색에 실패하고 {0} 오류가 발생했습니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/gulp/package.i18n.json b/i18n/kor/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..f4e91e0885 --- /dev/null +++ b/i18n/kor/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "Gulp 작업의 자동 검색을 사용할지 여부를 제어합니다. 기본값은 [켜기]입니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/html/client/out/htmlMain.i18n.json b/i18n/kor/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..6510e3a31e --- /dev/null +++ b/i18n/kor/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "HTML 언어 서버" +} \ No newline at end of file diff --git a/i18n/kor/extensions/html/package.i18n.json b/i18n/kor/extensions/html/package.i18n.json new file mode 100644 index 0000000000..a1a1272872 --- /dev/null +++ b/i18n/kor/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "기본 HTML 포맷터를 사용하거나 사용하지 않습니다.", + "html.format.wrapLineLength.desc": "한 줄당 최대 문자 수입니다(0 = 사용 안 함).", + "html.format.unformatted.desc": "쉼표로 분리된 태그 목록으로, 서식을 다시 지정해서는 안 됩니다. https://www.w3.org/TR/html5/dom.html#phrasing-content에 나열된 모든 태그의 기본값은 'null'로 설정됩니다.", + "html.format.contentUnformatted.desc": "쉼표로 분리된 태그 목록으로, 콘텐츠의 서식을 다시 지정해서는 안 됩니다. 'pre' 태그의 기본값은 'null'로 설정됩니다.", + "html.format.indentInnerHtml.desc": " 및 섹션을 들여쓰기합니다.", + "html.format.preserveNewLines.desc": "요소 앞에 있는 기존 줄 바꿈의 유지 여부입니다. 요소 앞에만 적용되며 태그 안이나 텍스트에는 적용되지 않습니다.", + "html.format.maxPreserveNewLines.desc": "청크 한 개에 유지할 수 있는 최대 줄 바꿈 수입니다. 무제한일 때는 'null'을 사용합니다.", + "html.format.indentHandlebars.desc": "{{#foo}} 및 {{/foo}}를 서식 지정하고 들여쓰기합니다.", + "html.format.endWithNewline.desc": "줄 바꿈으로 끝납니다.", + "html.format.extraLiners.desc": "쉼표로 분리된 태그 목록으로 앞에 줄 바꿈을 추가로 넣어야 합니다. \"head, body, /html\"의 기본값은 'null'로 설정됩니다.", + "html.format.wrapAttributes.desc": "특성을 래핑합니다.", + "html.format.wrapAttributes.auto": "줄 길이를 초과하는 경우에만 특성을 래핑합니다.", + "html.format.wrapAttributes.force": "첫 번째 특성을 제외한 각 특성을 래핑합니다.", + "html.format.wrapAttributes.forcealign": "첫 번째 특성을 제외한 각 특성을 래핑하고 정렬된 상태를 유지합니다.", + "html.format.wrapAttributes.forcemultiline": "각 특성을 래핑합니다.", + "html.suggest.angular1.desc": "기본 제공 HTML 언어 지원에서 Angular V1 태그 및 속성을 제안하는지 여부를 구성합니다.", + "html.suggest.ionic.desc": "기본 제공 HTML 언어 지원에서 Ionic 태그, 속성 및 값을 제안하는지 여부를 구성합니다.", + "html.suggest.html5.desc": "기본 제공 HTML 언어 지원에서 HTML5 태그, 속성 및 값을 제안하는지 여부를 구성합니다.", + "html.trace.server.desc": "VS Code와 HTML 언어 서버 간 통신을 추적합니다.", + "html.validate.scripts": "기본 제공 HTML 언어 지원에서 포함 스크립트의 유효성을 검사하는지 여부를 구성합니다.", + "html.validate.styles": "기본 제공 HTML 언어 지원에서 포함 스타일의 유효성을 검사하는지 여부를 구성합니다.", + "html.autoClosingTags": "HTML 태그의 자동 닫기를 사용하거나 사용하지 않습니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/jake/out/main.i18n.json b/i18n/kor/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..4027a186e3 --- /dev/null +++ b/i18n/kor/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Jake 자동 검색 실패 오류: {0}" +} \ No newline at end of file diff --git a/i18n/kor/extensions/jake/package.i18n.json b/i18n/kor/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..1728ce9b6b --- /dev/null +++ b/i18n/kor/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "Jake 작업에 대한 자동 검색 사용 여부를 설정합니다. 기본값은 [켜기]입니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/kor/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..1999b57768 --- /dev/null +++ b/i18n/kor/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "기본 bower.json", + "json.bower.error.repoaccess": "Bower 리포지토리 요청 실패: {0}", + "json.bower.latest.version": "최신" +} \ No newline at end of file diff --git a/i18n/kor/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/kor/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..8a5b77c719 --- /dev/null +++ b/i18n/kor/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "기본 package.json", + "json.npm.error.repoaccess": "NPM 리포지토리 요청 실패: {0}", + "json.npm.latestversion": "패키지의 현재 최신 버전", + "json.npm.majorversion": "최신 주 버전(1.x.x)을 일치시킵니다.", + "json.npm.minorversion": "최신 부 버전(1.2.x)을 일치시킵니다.", + "json.npm.version.hover": "최신 버전: {0}" +} \ No newline at end of file diff --git a/i18n/kor/extensions/json/client/out/jsonMain.i18n.json b/i18n/kor/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..0b53bb8511 --- /dev/null +++ b/i18n/kor/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "JSON 언어 서버" +} \ No newline at end of file diff --git a/i18n/kor/extensions/json/package.i18n.json b/i18n/kor/extensions/json/package.i18n.json new file mode 100644 index 0000000000..970ef8471e --- /dev/null +++ b/i18n/kor/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "현재 프로젝트에서 스키마를 JSON 파일에 연결", + "json.schemas.url.desc": "현재 디렉터리에 있는 스키마의 URL 또는 상대 경로", + "json.schemas.fileMatch.desc": "스키마에 대한 JSON 파일을 확인할 때 일치할 파일 패턴의 배열입니다.", + "json.schemas.fileMatch.item.desc": "스키마에 대한 JSON 파일을 확인할 때 일치할 '*'를 포함할 수 있는 파일 패턴입니다.", + "json.schemas.schema.desc": "지정된 URL에 대한 스키마 정의입니다. 스키마 URL에 대한 액세스 방지를 위해서만 스키마를 제공해야 합니다.", + "json.format.enable.desc": "기본 JSON 포맷터 사용/사용 안 함(다시 시작해야 함)", + "json.tracing.desc": "VS Code와 JSON 언어 서버 간 통신을 추적합니다.", + "json.colorDecorators.enable.desc": "색 데코레이터 사용 또는 사용 안 함", + "json.colorDecorators.enable.deprecationMessage": "`json.colorDecorators.enable` 설정은 `editor.colorDecorators`를 위해 사용되지 않습니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/markdown/out/extension.i18n.json b/i18n/kor/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..046543ff37 --- /dev/null +++ b/i18n/kor/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "'markdown.styles': {0}을 불러올 수 없음" +} \ No newline at end of file diff --git a/i18n/kor/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/kor/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..e1e0049cee --- /dev/null +++ b/i18n/kor/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "이 문서에서 일부 콘텐츠가 사용하지 않도록 설정되었습니다.", + "preview.securityMessage.title": "Markdown 미리 보기에서 잠재적으로 안전하지 않거나 보안되지 않은 콘텐츠가 사용하지 않도록 설정되어 있습니다. 이 콘텐츠나 스크립트를 허용하려면 Markdown 미리 보기 보안 설정을 변경하세요.", + "preview.securityMessage.label": "콘텐츠 사용할 수 없음 보안 경고" +} \ No newline at end of file diff --git a/i18n/kor/extensions/markdown/out/security.i18n.json b/i18n/kor/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..b9764f68ed --- /dev/null +++ b/i18n/kor/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "Strict", + "strict.description": "보안 콘텐츠만 로드", + "insecureContent.title": "보아되지 않은 콘텐츠 허용", + "insecureContent.description": "http를 통한 콘텐츠 로드 사용", + "disable.title": "사용 안 함", + "disable.description": "모든 콘텐츠 및 스크립트 실행을 허용합니다. 권장하지 않습니다.", + "moreInfo.title": "추가 정보", + "preview.showPreviewSecuritySelector.title": "이 작업 영역에 대해 Markdown 미리 보기의 보안 설정 선택" +} \ No newline at end of file diff --git a/i18n/kor/extensions/markdown/package.i18n.json b/i18n/kor/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..ea9ed976fc --- /dev/null +++ b/i18n/kor/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "마크다운 미리 보기에서 줄바꿈 렌더링 방식을 설정합니다. 'true'로 설정하면 모든 행에 대해
이(가) 생성됩니다.", + "markdown.preview.linkify": "Markdown 미리 보기에서 URL 같은 텍스트를 링크로 변환을 사용하거나 사용하지 않도록 설정합니다.", + "markdown.preview.doubleClickToSwitchToEditor.desc": "markdown 미리 보기에서 두 번 클릭하여 편집기로 전환합니다.", + "markdown.preview.fontFamily.desc": "markdown 미리 보기에서 사용되는 글꼴 패밀리를 제어합니다.", + "markdown.preview.fontSize.desc": "markdown 미리 보기에서 사용되는 글꼴 크기(픽셀)를 제어합니다.", + "markdown.preview.lineHeight.desc": "markdown 미리 보기에 사용되는 줄 높이를 제어합니다. 이 숫자는 글꼴 크기에 상대적입니다.", + "markdown.preview.markEditorSelection.desc": "markdown 미리 보기에 현재 편집기 선택을 표시합니다.", + "markdown.preview.scrollEditorWithPreview.desc": "markdown 미리 보기를 스크롤할 때 편집기의 보기를 업데이트합니다.", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "markdown 미리 보기를 스크롤하여 편집기에서 현재 선택한 줄을 표시합니다.", + "markdown.preview.title": "미리 보기 열기", + "markdown.previewFrontMatter.dec": "markdown 미리 보기에서 YAML 전문을 렌더링할 방법을 설정합니다. '숨기기' 기능을 사용하면 전문이 제거되고, 그러지 않으면 전문이 markdown 콘텐츠로 처리됩니다.", + "markdown.previewSide.title": "측면에서 미리 보기 열기", + "markdown.showSource.title": "소스 표시", + "markdown.styles.dec": "markdown 미리 보기에서 사용할 CSS 스타일시트의 URL 또는 로컬 경로 목록입니다. 상대 경로는 탐색기에서 열린 폴더를 기준으로 해석됩니다. 열린 폴더가 없으면 markdown 파일의 위치를 기준으로 해석됩니다. 모든 '\\'는 '\\\\'로 써야 합니다.", + "markdown.showPreviewSecuritySelector.title": "미리 보기 보안 설정 변경", + "markdown.trace.desc": "Markdown 확장에 대해 디버그 로깅을 사용하도록 설정합니다.", + "markdown.refreshPreview.title": "미리 보기 새로 고침" +} \ No newline at end of file diff --git a/i18n/kor/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/kor/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..030bfeab40 --- /dev/null +++ b/i18n/kor/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "현재 변경 사항 수락", + "acceptIncomingChange": "수신 변경 사항 수락", + "acceptBothChanges": "두 변경 사항 모두 수락", + "compareChanges": "변경 사항 비교" +} \ No newline at end of file diff --git a/i18n/kor/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/kor/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..374d8ebb5f --- /dev/null +++ b/i18n/kor/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "편집기 커서가 병합 충돌 내에 없음", + "compareChangesTitle": "{0}: 현재 변경 사항 ⟷ 수신 변경 사항", + "cursorOnCommonAncestorsRange": "편집기 커서가 공통 과거 블록 내에 있습니다. \"현재\" 또는 \"수신\" 블록으로 옮기세요.", + "cursorOnSplitterRange": "편집기 커서가 병합 충돌 스플리터 내에 있습니다. \"현재\" 또는 \"수신\" 블록으로 옮기세요.", + "noConflicts": "이 파일에서 발견된 병합 충돌 없음", + "noOtherConflictsInThisFile": "이 파일 내에 다른 병합 충돌 없음" +} \ No newline at end of file diff --git a/i18n/kor/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/kor/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..4fec970ebb --- /dev/null +++ b/i18n/kor/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(현재 변경 사항)", + "incomingChange": "(수신 변경 사항)" +} \ No newline at end of file diff --git a/i18n/kor/extensions/merge-conflict/package.i18n.json b/i18n/kor/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..9cc422e402 --- /dev/null +++ b/i18n/kor/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "충돌 병합", + "command.accept.all-incoming": "수신 모두 수락", + "command.accept.all-both": "둘 다 모두 수락", + "command.accept.current": "현재 수락", + "command.accept.incoming": "수신 수락", + "command.accept.selection": "선택 수락", + "command.accept.both": "둘 다 수락", + "command.next": "다음 충돌", + "command.previous": "이전 충돌", + "command.compare": "현재 충돌 비교", + "config.title": "충돌 병합", + "config.codeLensEnabled": "편집기 내에서 충돌 블록 CodeLense 병합 사용/사용 안 함", + "config.decoratorsEnabled": "편집기 내에서 충돌 병합 사용/사용 안 함" +} \ No newline at end of file diff --git a/i18n/kor/extensions/npm/package.i18n.json b/i18n/kor/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..ede78ec60f --- /dev/null +++ b/i18n/kor/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "npm 스크립트에 대한 자동 검색 여부를 설정합니다. 기본값은 [켜기]입니다.", + "config.npm.runSilent": " `--silent` 옵션으로 npm 명령 실행" +} \ No newline at end of file diff --git a/i18n/kor/extensions/php/out/features/validationProvider.i18n.json b/i18n/kor/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..36d95fdcfe --- /dev/null +++ b/i18n/kor/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "PHP 파일을 lint하기 위해 {0}(작업 영역 설정으로 정의됨)의 실행을 허용하시겠습니까?", + "php.yes": "허용", + "php.no": "허용 안 함", + "wrongExecutable": "{0}은(는) 유효한 PHP 실행 파일이 아니기 때문에 유효성을 검사할 수 없습니다. 'php.validate.executablePath' 설정을 사용하여 PHP 실행 파일을 구성하세요.", + "noExecutable": "PHP 실행 파일이 설정되지 않았기 때문에 유효성을 검사할 수 없습니다. 'php.validate.executablePath' 설정을 사용하여 PHP 실행 파일을 구성하세요.", + "unknownReason": "{0} 경로를 사용하여 php를 실행하지 못했습니다. 이유를 알 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/php/package.i18n.json b/i18n/kor/extensions/php/package.i18n.json new file mode 100644 index 0000000000..56b9ca7e50 --- /dev/null +++ b/i18n/kor/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "기본 제공 PHP 언어 제안을 사용하는지 여부를 구성합니다. 지원에서는 PHP 전역 및 변수를 제안합니다.", + "configuration.validate.enable": "기본 제공 PHP 유효성 검사를 사용하거나 사용하지 않습니다.", + "configuration.validate.executablePath": "PHP 실행 파일을 가리킵니다.", + "configuration.validate.run": "저장 시 또는 입력 시 Linter의 실행 여부입니다.", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "PHP 유효성 검사 실행 파일을 허용하지 않음(작업\n 영역 설정으로 정의됨)" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/kor/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..12cc94e49c --- /dev/null +++ b/i18n/kor/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInformation": "추가 정보", + "doNotCheckAgain": "다시 확인 안 함", + "close": "닫기", + "updateTscCheck": "사용자 설정 'typescript.check.tscVersion'을 false로 업데이트했습니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/kor/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..13890e9aea --- /dev/null +++ b/i18n/kor/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "typings를 가져오는 중...", + "acquiringTypingsDetail": "IntelliSense에 대한 typings 정의를 가져오는 중입니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/kor/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..0d973147e0 --- /dev/null +++ b/i18n/kor/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "JavaScript 파일에서 의미 검사를 사용합니다. 파일의 최상단에 있어야 합니다.", + "ts-nocheck": "JavaScript 파일에서 의미 검사를 사용하지 않습니다. 파일의 최상단에 있어야 합니다.", + "ts-ignore": "파일의 다음 행에서 @ts-check 오류를 억제합니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/kor/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..1ae81c4d7a --- /dev/null +++ b/i18n/kor/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1개 구현", + "manyImplementationLabel": "{0}개 구현", + "implementationsErrorLabel": "구현을 확인할 수 없음" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/kor/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..fcb9e6d5e3 --- /dev/null +++ b/i18n/kor/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "JSDoc 주석" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/kor/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..d61143feb1 --- /dev/null +++ b/i18n/kor/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "참조 1개", + "manyReferenceLabel": "참조 {0}개", + "referenceErrorLabel": "참조를 확인할 수 없음" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/kor/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..09ff669b35 --- /dev/null +++ b/i18n/kor/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "보기 - {0}", + "buildTscLabel": "빌드 - {0}" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/typescriptMain.i18n.json b/i18n/kor/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..dfbca3bef2 --- /dev/null +++ b/i18n/kor/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "TypeScript 또는 JavaScript 프로젝트를 사용하려면 VS Code의 폴더를 여세요.", + "typescript.projectConfigUnsupportedFile": "TypeScript 또는 JavaScript 프로젝트를 확인할 수 없습니다. 지원되지 않는 파일 형식", + "typescript.projectConfigCouldNotGetInfo": "TypeScript 또는 JavaScript 프로젝트를 확인할 수 없습니다.", + "typescript.noTypeScriptProjectConfig": "파일이 TypeScript 프로젝트의 일부가 아닙니다.", + "typescript.noJavaScriptProjectConfig": "파일이 JavaScript 프로젝트의 일부가 아닙니다.", + "typescript.configureTsconfigQuickPick": "tsconfig.json 구성", + "typescript.configureJsconfigQuickPick": "jsconfig.json 구성", + "typescript.projectConfigLearnMore": "자세한 정보" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/kor/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..b4554346de --- /dev/null +++ b/i18n/kor/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "경로 {0}이(가) 올바른 tsserver 설치를 가리키지 않습니다. 포함된 TypeScript 버전을 대신 사용합니다.", + "serverCouldNotBeStarted": "TypeScript 언어 서버를 시작할 수 없습니다. 오류 메시지: {0}", + "typescript.openTsServerLog.notSupported": "TS 서버 로깅을 사용하려면 TS 2.2.2 이상이 필요합니다.", + "typescript.openTsServerLog.loggingNotEnabled": "TS 서버 로깅이 꺼져 있습니다. `typescript.tsserver.log`를 설정하고 TS 서버를 다시 시작하여 로깅을 사용하도록 설정하세요.", + "typescript.openTsServerLog.enableAndReloadOption": "로깅 사용 및 TS 서버 다시 시작", + "typescript.openTsServerLog.noLogFile": "TS 서버에서 로깅을 시작하지 않았습니다.", + "openTsServerLog.openFileFailedFailed": "TS 서버 로그 파일을 열 수 없습니다.", + "serverDiedAfterStart": "TypeScript 언어 서비스가 시작된 직후 5번 종료되었습니다. 서비스가 다시 시작되지 않습니다.", + "serverDiedReportIssue": "문제 보고", + "serverDied": "TypeScript 언어 서비스가 지난 5분 동안 예기치 않게 5번 종료되었습니다." +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/utils/api.i18n.json b/i18n/kor/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..e7319098b5 --- /dev/null +++ b/i18n/kor/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "잘못된 버전" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/utils/logger.i18n.json b/i18n/kor/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/kor/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/kor/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..cdead695a3 --- /dev/null +++ b/i18n/kor/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "프로젝트 전체에서 JavaScript/TypeScript 언어 기능을 사용하도록 설정하려면 {0}과(와) 같이 파일이 많은 폴더를 제외하세요.", + "hintExclude.generic": "프로젝트 전체에서 JavaScript/TypeScript 언어 기능을 사용하도록 설정하려면 사용하지 않는 소스 파일이 포함된 큰 폴더를 제외하세요.", + "large.label": "제외 구성", + "hintExclude.tooltip": "프로젝트 전체에서 JavaScript/TypeScript 언어 기능을 사용하도록 설정하려면 사용하지 않는 소스 파일이 포함된 큰 폴더를 제외하세요." +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/kor/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..26422a8212 --- /dev/null +++ b/i18n/kor/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "TypeScript IntelliSense를 향상하기 위해 데이터를 페치하는 중", + "typesInstallerInitializationFailed.title": "JavaScript 언어 기능에 대한 입력 파일을 설치할 수 없습니다. NPM이 설치되어 있는지 확인하거나 사용자 설정에서 'typescript.npm'을 구성하세요.", + "typesInstallerInitializationFailed.moreInformation": "추가 정보", + "typesInstallerInitializationFailed.doNotCheckAgain": "다시 확인 안 함", + "typesInstallerInitializationFailed.close": "닫기" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/kor/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..de3217bc96 --- /dev/null +++ b/i18n/kor/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "VS Code의 버전 사용", + "useWorkspaceVersionOption": "작업 영역 버전 사용", + "learnMore": "자세한 정보", + "selectTsVersion": "JavaScript 및 TypeScript 언어 기능에 사용되는 TypeScript 버전 선택" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/kor/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..208607a32a --- /dev/null +++ b/i18n/kor/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "이 경로에서 TypeScript 버전을 로드할 수 없습니다.", + "noBundledServerFound": "잘못 동작하는 바이러스 감지 도구와 같은 다른 응용 프로그램에서 VS Code의 tsserver가 삭제되었습니다. VS Code를 다시 설치하세요." +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/package.i18n.json b/i18n/kor/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..c2119bf7e6 --- /dev/null +++ b/i18n/kor/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "프로젝트 다시 로드", + "javascript.reloadProjects.title": "프로젝트 다시 로드", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "매개 변수 서명으로 함수를 완료하세요.", + "typescript.tsdk.desc": "사용할 tsserver 및 lib*.d.ts 파일이 들어 있는 폴더 경로를 지정합니다.", + "typescript.disableAutomaticTypeAcquisition": "자동 형식 인식을 사용하지 않습니다. TypeScript >= 2.0.6이 필요합니다.", + "typescript.tsserver.log": "파일에 대해 TS 서버 로깅을 사용하도록 설정합니다. 이 로그는 TS 서버 문제를 진단하는 데 사용될 수 있습니다. 로그에는 파일 경로, 소스 코드 및 프로젝트에서 잠재적으로 중요한 기타 정보가 포함될 수 있습니다.", + "typescript.tsserver.trace": "TS 서버로 전송한 메시지 추적을 사용하도록 설정합니다. 이\n 추적은 TS 서버 문제를 진단하는 데 사용될 수 있습니다. 추적에는 파일 경로, 소스 코드 및 프로젝트에서 잠재적으로 중요한\n 기타 정보가 포함될 수 있습니다.", + "typescript.validate.enable": "TypeScript 유효성 검사를 사용하거나 사용하지 않습니다.", + "typescript.format.enable": "기본 TypeScript 포맷터를 사용하거나 사용하지 않습니다.", + "javascript.format.enable": "기본 JavaScript 포맷터를 사용하거나 사용하지 않습니다.", + "format.insertSpaceAfterCommaDelimiter": "쉼표 구분 기호 뒤에 오는 공백 처리를 정의합니다.", + "format.insertSpaceAfterConstructor": "컨스트럭터 키워드 뒤 공백 처리를 정의합니다. TypeScript >= 2.3.0이 필요합니다.", + "format.insertSpaceAfterSemicolonInForStatements": " for 문에서 세미콜론 뒤에 오는 공백 처리를 정의합니다.", + "format.insertSpaceBeforeAndAfterBinaryOperators": "이항 연산자 뒤에 오는 공백 처리를 정의합니다.", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "제어 흐름 문의 키워드 뒤에 오는 공백 처리를 정의합니다.", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "익명 함수의 function 키워드 뒤에 오는 공백 처리를 정의합니다.", + "format.insertSpaceBeforeFunctionParenthesis": "함수 인수 괄호 앞에 오는 공백 처리를 정의합니다. TypeScript 2.1.5 이상이 필요합니다.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "비어 있지 않은 여는 괄호 뒤와 닫는 괄호 앞에 오는 공백 처리를 정의합니다.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "비어 있지 않은 여는 대괄호 뒤와 닫는 대괄호 앞에 오는 공백 처리를 정의합니다.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "비어 있지 않은 여는 중괄호 뒤와 닫는 중괄호 앞의 공백 처리를 정의합니다. TypeScript >= 2.3.0이 필요합니다.", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "템플릿 문자열의 여는 중괄호 뒤와 닫는 중괄호 앞의 공백 처리를 정의합니다. TypeScript >= 2.0.6이 필요합니다.", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "JSX 식의 여는 중괄호 뒤와 닫는 중괄호 앞의 공백 처리를 정의합니다. TypeScript >= 2.0.6이 필요합니다.", + "format.insertSpaceAfterTypeAssertion": "TypeScript에서 유형 어설션 이후 공백 처리를 정의합니다. TypeScript >= 2.4가 필요합니다.", + "format.placeOpenBraceOnNewLineForFunctions": "함수의 새 줄에 여는 중괄호를 넣을지 정의합니다.", + "format.placeOpenBraceOnNewLineForControlBlocks": "제어 블록의 새 줄에 여는 중괄호를 넣을지 정의합니다.", + "javascript.validate.enable": "JavaScript 유효성 검사를 사용하거나 사용하지 않습니다.", + "typescript.goToProjectConfig.title": "프로젝트 구성으로 이동", + "javascript.goToProjectConfig.title": "프로젝트 구성으로 이동", + "javascript.referencesCodeLens.enabled": "JavaScript 파일에서 CodeLense 참조를 사용/사용 안 함으로 설정합니다.", + "typescript.referencesCodeLens.enabled": "TypeScript 파일에서 참조 CodeLense를 사용/사용 안 함으로 설정합니다. TypeScript >= 2.0.6이 필요합니다.", + "typescript.implementationsCodeLens.enabled": "구현 CodeLens를 사용하거나 사용하지 않도록 설정합니다. TypeScript >= 2.2.0이 필요합니다.", + "typescript.openTsServerLog.title": "TS 서버 로그 열기", + "typescript.restartTsServer": "TS 서버 다시 시작", + "typescript.selectTypeScriptVersion.title": "TypeScript 버전 선택", + "jsDocCompletion.enabled": "자동 JSDoc 주석 사용/사용 안 함", + "javascript.implicitProjectConfig.checkJs": "JavaScript 파일의 의미 체계 검사를 사용/사용하지 않습니다. 기존 jsconfig.json 또는 tsconfig.json 파일은 이 설정을 재정의합니다. TypeScript >=2.3.1이 필요합니다. ", + "typescript.npm": "자동 입력 인식에 사용된 NPM 실행 파일 경로를 지정합니다. TypeScript >= 2.3.4가 필요합니다.", + "typescript.check.npmIsInstalled": "자동 입력 인식에 대해 NPM이 설치되어 있는지 확인합니다.", + "javascript.nameSuggestions": "JavaScript 제안 목록의 파일에서 고유한 이름 포함을 사용/사용 안 함으로 설정합니다.", + "typescript.tsc.autoDetect": "tsc 작업의 자동 검색을 켜거나 끕니다.", + "typescript.problemMatchers.tsc.label": "TypeScript 문제", + "typescript.problemMatchers.tscWatch.label": "TypeScript 문제(감시 모드)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/kor/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e20c69e656 --- /dev/null +++ b/i18n/kor/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0}({1})" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/kor/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..0544c3fe97 --- /dev/null +++ b/i18n/kor/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0}(다시 발생함)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/kor/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..85ac0668ec --- /dev/null +++ b/i18n/kor/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "입력" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/kor/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..a5829326b8 --- /dev/null +++ b/i18n/kor/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "대/소문자 구분", + "wordsDescription": "단어 단위로", + "regexDescription": "정규식 사용" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/kor/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..3ea60c1cc5 --- /dev/null +++ b/i18n/kor/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "오류: {0}", + "alertWarningMessage": "경고: {0}", + "alertInfoMessage": "정보: {0}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/kor/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..08ec6eb44a --- /dev/null +++ b/i18n/kor/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "이미지가 너무 커서 편집기에 표시할 수 없습니다. ", + "resourceOpenExternalButton": " 외부 프로그램으로 이미지를 열까요?", + "nativeBinaryError": "파일이 이진이거나 매우 크거나 지원되지 않는 텍스트 인코딩을 사용하기 때문에 편집기에서 표시되지 않습니다.", + "sizeB": "{0}B", + "sizeKB": "{0}KB", + "sizeMB": "{0}MB", + "sizeGB": "{0}GB", + "sizeTB": "{0}TB" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/kor/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..fd11e97f7c --- /dev/null +++ b/i18n/kor/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "자세히" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/common/errorMessage.i18n.json b/i18n/kor/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..53b0fb689b --- /dev/null +++ b/i18n/kor/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}. 오류 코드: {1}", + "error.permission.verbose": "사용 권한이 거부되었습니다(HTTP {0}).", + "error.permission": "사용 권한이 거부되었습니다.", + "error.http.verbose": "{0}(HTTP {1}: {2})", + "error.http": "{0}(HTTP {1})", + "error.connection.unknown.verbose": "알 수 없는 연결 오류({0})", + "error.connection.unknown": "알 수 없는 연결 오류가 발생했습니다. 인터넷에 연결되지 않았거나 연결된 서버가 오프라인 상태입니다.", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "알 수 없는 오류가 발생했습니다. 자세한 내용은 로그를 참조하세요.", + "nodeExceptionMessage": "시스템 오류가 발생했습니다({0}).", + "error.moreErrors": "{0}(총 {1}개의 오류)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/common/json.i18n.json b/i18n/kor/src/vs/base/common/json.i18n.json new file mode 100644 index 0000000000..45ab57d753 --- /dev/null +++ b/i18n/kor/src/vs/base/common/json.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "잘못된 기호", + "error.invalidNumberFormat": "잘못된 숫자 형식", + "error.propertyNameExpected": "속성 이름 필요", + "error.valueExpected": "값 필요", + "error.colonExpected": "콜론이 필요합니다.", + "error.commaExpected": "쉼표가 필요합니다.", + "error.closeBraceExpected": "닫는 괄호 필요", + "error.closeBracketExpected": "닫는 대괄호 필요", + "error.endOfFileExpected": "파일 끝 필요" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/kor/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..45ab57d753 --- /dev/null +++ b/i18n/kor/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "잘못된 기호", + "error.invalidNumberFormat": "잘못된 숫자 형식", + "error.propertyNameExpected": "속성 이름 필요", + "error.valueExpected": "값 필요", + "error.colonExpected": "콜론이 필요합니다.", + "error.commaExpected": "쉼표가 필요합니다.", + "error.closeBraceExpected": "닫는 괄호 필요", + "error.closeBracketExpected": "닫는 대괄호 필요", + "error.endOfFileExpected": "파일 끝 필요" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/common/keybindingLabels.i18n.json b/i18n/kor/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..73f9bf599a --- /dev/null +++ b/i18n/kor/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "컨트롤", + "shiftKey.long": "", + "altKey.long": "Alt", + "cmdKey.long": "명령", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/common/processes.i18n.json b/i18n/kor/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..67ecb0034f --- /dev/null +++ b/i18n/kor/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "오류: 실행 파일 정보에서는 문자열 형식의 명령을 정의해야 합니다.", + "ExecutableParser.isShellCommand": "경고: isShellCommand는 boolean 형식이어야 합니다. {0} 값을 무시합니다.", + "ExecutableParser.args": "경고: args는 string[] 형식이어야 합니다. {0} 값을 무시합니다.", + "ExecutableParser.invalidCWD": "경고: options.cwd는 string 형식이어야 합니다. {0} 값을 무시합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/common/severity.i18n.json b/i18n/kor/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..8bd45f5145 --- /dev/null +++ b/i18n/kor/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "오류", + "sev.warning": "경고", + "sev.info": "정보" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/node/processes.i18n.json b/i18n/kor/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..d9dedab019 --- /dev/null +++ b/i18n/kor/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "UNC 드라이브에서 셸 명령을 실행할 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/node/zip.i18n.json b/i18n/kor/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..95670373a3 --- /dev/null +++ b/i18n/kor/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "zip 파일 내에 {0}이(가) 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/kor/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..b85561e24c --- /dev/null +++ b/i18n/kor/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0}, 선택기", + "quickOpenAriaLabel": "선택기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/kor/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..f173f30c7e --- /dev/null +++ b/i18n/kor/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "빠른 선택기입니다. 결과의 범위를 축소하려면 입력합니다.", + "treeAriaLabel": "빠른 선택기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/kor/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..c43be1603d --- /dev/null +++ b/i18n/kor/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "축소" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/code/electron-main/auth.i18n.json b/i18n/kor/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..6948b5cb31 --- /dev/null +++ b/i18n/kor/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "프록시 인증 필요", + "proxyauth": "프록시 {0}에 인증이 필요합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/code/electron-main/menus.i18n.json b/i18n/kor/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..24b6120159 --- /dev/null +++ b/i18n/kor/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "파일(&&F)", + "mEdit": "편집(&&E)", + "mSelection": "선택 영역(&S)", + "mView": "보기(&&V)", + "mGoto": "이동(&G)", + "mDebug": "디버그(&&D)", + "mWindow": "창", + "mHelp": "도움말(&&H)", + "mTask": "작업(&T)", + "miNewWindow": "새 창(&&W)", + "mAbout": "{0} 정보", + "mServices": "서비스", + "mHide": "{0} 숨기기", + "mHideOthers": "기타 숨기기", + "mShowAll": "모두 표시", + "miQuit": "{0} 종료", + "miNewFile": "새 파일(&&N)", + "miOpen": "열기(&&O)...", + "miOpenWorkspace": "작업 영역 열기(&O)...", + "miOpenFolder": "폴더 열기(&&F)...", + "miOpenFile": "파일 열기(&&O)...", + "miOpenRecent": "최근 항목 열기(&&R)", + "miSaveWorkspaceAs": "다른 이름으로 작업 영역 저장(&S)...", + "miAddFolderToWorkspace": "작업 영역에 폴더 추가(&&A)", + "miSave": "저장(&&S)", + "miSaveAs": "다른 이름으로 저장(&&A)...", + "miSaveAll": "모두 저장(&&L)", + "miAutoSave": "자동 저장", + "miRevert": "파일 되돌리기(&&V)", + "miCloseWindow": "창 닫기(&&E)", + "miCloseWorkspace": "작업 영역 닫기(&W)", + "miCloseFolder": "폴더 닫기(&&F)", + "miCloseEditor": "편집기 닫기(&&C)", + "miExit": "끝내기(&X)", + "miOpenSettings": "설정(&S)", + "miOpenKeymap": "바로 가기 키(&&K)", + "miOpenKeymapExtensions": "키맵 확장(&&K)", + "miOpenSnippets": "사용자 코드 조각(&&S)", + "miSelectColorTheme": "색 테마(&&C)", + "miSelectIconTheme": "파일 아이콘 테마(&&I)", + "miPreferences": "기본 설정(&&P)", + "miReopenClosedEditor": "닫힌 편집기 다시 열기(&&R)", + "miMore": "자세히...(&M)", + "miClearRecentOpen": "최근에 연 항목 지우기(&&C)", + "miUndo": "실행 취소(&&U)", + "miRedo": "다시 실행(&&R)", + "miCut": "자르기(&T)", + "miCopy": "복사(&&C)", + "miPaste": "붙여넣기(&&P)", + "miFind": "찾기(&&F)", + "miReplace": "바꾸기(&&R)", + "miFindInFiles": "파일에서 찾기(&&I)", + "miReplaceInFiles": "파일에서 바꾸기(&&I)", + "miEmmetExpandAbbreviation": "Emmet: 약어 확장(&&X)", + "miShowEmmetCommands": "Emmet(&&M)...", + "miToggleLineComment": "줄 주석 설정/해제(&&T)", + "miToggleBlockComment": "블록 주석 설정/해제(&&B)", + "miMultiCursorAlt": "다중 커서를 위해 Alt+Click으로 전환", + "miMultiCursorCmd": "다중 커서에 Cmd+Click 사용 ", + "miMultiCursorCtrl": "다중 커서에 Ctrl+클릭 사용", + "miInsertCursorAbove": "위에 커서 추가(&&A)", + "miInsertCursorBelow": "아래에 커서 추가(&&D)", + "miInsertCursorAtEndOfEachLineSelected": "줄 끝에 커서 추가(&&U)", + "miAddSelectionToNextFindMatch": "다음 항목 추가(&&N)", + "miAddSelectionToPreviousFindMatch": "이전 항목 추가(&&R)", + "miSelectHighlights": "모든 항목 선택(&&O)", + "miCopyLinesUp": "위에 줄 복사(&&C)", + "miCopyLinesDown": "아래에 줄 복사(&&P)", + "miMoveLinesUp": "줄 위로 이동(&&V)", + "miMoveLinesDown": "줄 아래로 이동(&&L)", + "miSelectAll": "모두 선택(&&S)", + "miSmartSelectGrow": "선택 영역 확장(&&E)", + "miSmartSelectShrink": "선택 영역 축소(&&S)", + "miViewExplorer": "탐색기(&&E)", + "miViewSearch": "검색(&&S)", + "miViewSCM": "SCM(&&C)", + "miViewDebug": "디버그(&&D)", + "miViewExtensions": "확장(&&X)", + "miToggleOutput": "출력(&&O)", + "miToggleDebugConsole": "디버그 콘솔(&&B)", + "miToggleIntegratedTerminal": "통합 터미널(&&I)", + "miMarker": "문제(&&P)", + "miAdditionalViews": "추가 뷰(&&V)", + "miCommandPalette": "명령 팔레트(&&C)...", + "miToggleFullScreen": "전체 화면 설정/해제(&&F)", + "miToggleZenMode": "Zen 모드 설정/해제", + "miToggleMenuBar": "메뉴 모음 설정/해제(&&B)", + "miSplitEditor": "편집기 분할(&&E)", + "miToggleEditorLayout": "편집기 그룹 레이아웃 설정/해제(&&L)", + "miToggleSidebar": "사이드바 설정/해제(&&T)", + "miMoveSidebarRight": "사이드바를 오른쪽으로 이동(&&M)", + "miMoveSidebarLeft": "사이드바를 왼쪽으로 이동(&&M)", + "miTogglePanel": "패널 설정/해제(&&P)", + "miHideStatusbar": "상태 표시줄 숨기기(&&H)", + "miShowStatusbar": "상태 표시줄 표시(&&S)", + "miHideActivityBar": "작업 막대 숨기기(&&A)", + "miShowActivityBar": "작업 막대 표시(&&A)", + "miToggleWordWrap": "자동 줄 바꿈 설정/해제(&&W)", + "miToggleMinimap": "미니맵 토글(&&M)", + "miToggleRenderWhitespace": "공백 설정/해제 및 렌더링(&&R)", + "miToggleRenderControlCharacters": "제어 문자 설정/해제(&&C)", + "miZoomIn": "확대(&&Z)", + "miZoomOut": "축소(&&U)", + "miZoomReset": "확대/축소 다시 설정(&&R)", + "miBack": "뒤로(&B)", + "miForward": "앞으로(&&F)", + "miNextEditor": "다음 편집기(&&N)", + "miPreviousEditor": "이전 편집기(&&P)", + "miNextEditorInGroup": "그룹에서 다음에 사용되는 편집기(&&N)", + "miPreviousEditorInGroup": "그룹에서 이전에 사용된 편집기(&&P)", + "miSwitchEditor": "편집기 전환(&&E)", + "miFocusFirstGroup": "첫 번째 그룹(&&F)", + "miFocusSecondGroup": "두 번째 그룹(&&S)", + "miFocusThirdGroup": "세 번째 그룹(&&T)", + "miNextGroup": "다음 그룹(&&N)", + "miPreviousGroup": "이전 그룹(&&P)", + "miSwitchGroup": "그룹 전환(&&G)", + "miGotoFile": "파일로 이동(&&F)...", + "miGotoSymbolInFile": "파일의 기호로 이동(&&S)...", + "miGotoSymbolInWorkspace": "작업 영역의 기호로 이동(&&W)...", + "miGotoDefinition": "정의로 이동(&&D)", + "miGotoTypeDefinition": "형식 정의로 이동( &&T)", + "miGotoImplementation": "구현으로 이동( &&I)", + "miGotoLine": "줄 이동(&&L)...", + "miStartDebugging": "디버깅 시작(&&S)", + "miStartWithoutDebugging": "디버깅하지 않고 시작(&&W)", + "miStopDebugging": "디버깅 중지(&&S)", + "miRestart Debugging": "디버깅 다시 시작(&&R)", + "miOpenConfigurations": "구성 열기(&&C)", + "miAddConfiguration": "구성 추가...", + "miStepOver": "프로시저 단위 실행(&&O)", + "miStepInto": "한 단계씩 코드 실행(&&I)", + "miStepOut": "프로시저 나가기(&&U)", + "miContinue": "계속(&&C)", + "miToggleBreakpoint": "중단점 설정/해제(&&B)", + "miConditionalBreakpoint": "조건부 중단점(&&C)...", + "miColumnBreakpoint": "열 중단점(&&O)", + "miFunctionBreakpoint": "함수 중단점(&&F)...", + "miNewBreakpoint": "새 중단점(&&N)", + "miEnableAllBreakpoints": "모든 중단점 설정", + "miDisableAllBreakpoints": "모든 중단점 사용 안 함(&&L)", + "miRemoveAllBreakpoints": "모든 중단점 제거(&&A)", + "miInstallAdditionalDebuggers": "추가 디버거 설치(&&I)...", + "mMinimize": "최소화", + "mZoom": "확대/축소", + "mBringToFront": "모두 맨 앞으로 가져오기", + "miSwitchWindow": "창 전환(&&W)", + "miToggleDevTools": "개발자 도구 설정/해제(&&T)", + "miAccessibilityOptions": "접근성 옵션(&&O)", + "miReportIssues": "문제 보고(&&I)", + "miWelcome": "시작(&&W)", + "miInteractivePlayground": "대화형 실습(&&I)", + "miDocumentation": "설명서(&&D)", + "miReleaseNotes": "릴리스 정보(&&R)", + "miKeyboardShortcuts": "바로 가기 키 참조(&&K)", + "miIntroductoryVideos": "소개 비디오(&&V)", + "miTipsAndTricks": "팁과 요령(&T)", + "miTwitter": "Twitter에서 참여(&&J)", + "miUserVoice": "검색 기능 요청(&&S)", + "miLicense": "라이선스 보기(&&L)", + "miPrivacyStatement": "개인정보처리방침(&&P)", + "miAbout": "정보(&&A)", + "miRunTask": "작업 실행(&&R)...", + "miBuildTask": "빌드 작업 실행(&&B)...", + "miRunningTask": "실행 중인 작업 표시(&&G)...", + "miRestartTask": "실행 중인 작업 다시 시작(&&E)...", + "miTerminateTask": "작업 종료(&&T)...", + "miConfigureTask": "작업 구성(&&C)", + "miConfigureBuildTask": "기본 빌드 작업 구성(&&F)", + "accessibilityOptionsWindowTitle": "접근성 옵션", + "miRestartToUpdate": "다시 시작하여 업데이트...", + "miCheckingForUpdates": "업데이트를 확인하는 중...", + "miDownloadUpdate": "사용 가능한 업데이트 다운로드", + "miDownloadingUpdate": "업데이트를 다운로드하는 중...", + "miInstallingUpdate": "업데이트를 설치하는 중...", + "miCheckForUpdates": "업데이트 확인...", + "aboutDetail": "\n버전 {0}\n커밋 {1}\n날짜 {2}\n셸 {3}\n렌더러 {4}\n노드 {5}\n아키텍처 {6}", + "okButton": "확인" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/code/electron-main/window.i18n.json b/i18n/kor/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..4ea651c7b6 --- /dev/null +++ b/i18n/kor/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "**Alt** 키를 눌러 메뉴 모음에 계속 액세스할 수 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/code/electron-main/windows.i18n.json b/i18n/kor/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..729bfa0414 --- /dev/null +++ b/i18n/kor/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "확인", + "pathNotExistTitle": "경로가 없습니다.", + "pathNotExistDetail": "'{0}' 경로가 디스크에 더 이상 없는 것 같습니다.", + "openWorkspace": "열기(&&O)", + "openWorkspaceTitle": "작업 영역 열기", + "save": "저장(&&S)", + "doNotSave": "저장 안 함(&&N)", + "cancel": "취소", + "saveWorkspaceMessage": "작업 영역 구성을 파일로 저장하시겠습니까?", + "saveWorkspaceDetail": "작업 영역을 다시 열려면 작업 영역을 저장하세요.", + "saveWorkspace": "작업 영역 저장", + "reopen": "다시 열기", + "wait": "계속 대기", + "close": "닫기", + "appStalled": "창이 더 이상 응답하지 않습니다.", + "appStalledDetail": "창을 다시 열거나, 닫거나, 계속 기다릴 수 있습니다.", + "appCrashed": "창이 충돌했습니다.", + "appCrashedDetail": "불편을 드려서 죄송합니다. 창을 다시 열면 중단된 위치에서 계속할 수 있습니다.", + "open": "열기", + "openFolder": "폴더 열기", + "openFile": "파일 열기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/code/node/cliProcessMain.i18n.json b/i18n/kor/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..5fac800b7a --- /dev/null +++ b/i18n/kor/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "'{0}' 확장을 찾을 수 없습니다.", + "notInstalled": "'{0}' 확장이 설치되어 있지 않습니다.", + "useId": "게시자를 포함한 전체 확장 ID(예: {0})를 사용하세요.", + "successVsixInstall": "확장 '{0}'이(가) 설치되었습니다!", + "alreadyInstalled": "'{0}' 확장이 이미 설치되어 있습니다.", + "foundExtension": "마켓플레이스에서 '{0}'을(를) 찾았습니다.", + "installing": "설치 중...", + "successInstall": "확장 '{0}' v{1}이(가) 성공적으로 설치되었습니다!", + "uninstalling": "{0} 제거 중...", + "successUninstall": "'{0}' 확장이 성공적으로 제거되었습니다!" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/kor/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..870097235f --- /dev/null +++ b/i18n/kor/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "파일 1개가 너무 커서 파일을 비교할 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/kor/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..d37f48a5e8 --- /dev/null +++ b/i18n/kor/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "닫기", + "header": "다른 항목 {0} / {1}: 원본 {2}, {3}행, 수정 {4}, {5}행", + "blankLine": "비어 있음", + "equalLine": "원본 {0}, 수정 {1}: {2}", + "insertLine": "+ 수정됨 {0}: {1}", + "deleteLine": "- 원본 {0}: {1}", + "editor.action.diffReview.next": "다음 다른 항목으로 이동", + "editor.action.diffReview.prev": "다음 다른 항목으로 이동" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/kor/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..c3537da1f3 --- /dev/null +++ b/i18n/kor/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "편집기", + "fontFamily": "글꼴 패밀리를 제어합니다.", + "fontWeight": "글꼴 두께를 제어합니다.", + "fontSize": "글꼴 크기(픽셀)를 제어합니다.", + "lineHeight": "줄 높이를 제어합니다. fontSize의 lineHeight를 계산하려면 0을 사용합니다.", + "letterSpacing": "글자 간격을 픽셀 단위로 조정합니다.", + "lineNumbers": "줄 번호의 표시 여부를 제어합니다. 가능한 값은 'on', 'off', 'relative'입니다. 'relative'는 현재 커서 위치에서 줄 수를 표시합니다.", + "rulers": "세로 눈금자를 표시할 열", + "wordSeparators": "단어 관련 탐색 또는 작업을 수행할 때 단어 구분 기호로 사용되는 문자입니다.", + "tabSize": "탭 한 개에 해당하는 공백 수입니다. `editor.detectIndentation`이 켜져 있는 경우 이 설정은 파일 콘텐츠에 따라 재정의됩니다.", + "tabSize.errorMessage": "'number'가 필요합니다. 값 \"auto\"는 `editor.detectIndentation` 설정에 의해 바뀌었습니다.", + "insertSpaces": " 키를 누를 때 공백을 삽입합니다. `editor.detectIndentation`이 켜져 있는 경우 이 설정은 파일 콘텐츠에 따라 재정의됩니다.", + "insertSpaces.errorMessage": "'boolean'이 필요합니다. 값 \"auto\"는 `editor.detectIndentation` 설정에 의해 바뀌었습니다.", + "detectIndentation": "파일을 열면 파일 콘텐츠를 기반으로 하여 'editor.tabSize'와 'editor.insertSpaces'가 검색됩니다.", + "roundedSelection": "선택 항목의 모서리를 둥글게 할지 여부를 제어합니다.", + "scrollBeyondLastLine": "편집기에서 마지막 줄 이후로 스크롤할지 여부를 제어합니다.", + "smoothScrolling": "편집기에서 애니메이션을 사용하여 스크롤할지 여부를 제어합니다.", + "minimap.enabled": "미니맵 표시 여부를 제어합니다.", + "minimap.showSlider": "미니맵 슬라이더를 자동으로 숨길지 결정합니다. 가능한 값은 'always' 및 'mouseover'입니다.", + "minimap.renderCharacters": "줄의 실제 문자(색 블록 아님) 렌더링", + "minimap.maxColumn": "최대 특정 수의 열을 렌더링하도록 미니맵의 너비를 제한합니다.", + "find.seedSearchStringFromSelection": "편집기 선택에서 Find Widget의 검색 문자열을 시딩할지 설정합니다.", + "find.autoFindInSelection": "편집기에서 여러 글자 또는 행을 선택했을 때 Find in Selection 플래그를 켤지 설정합니다.", + "wordWrap.off": "줄이 바뀌지 않습니다.", + "wordWrap.on": "뷰포트 너비에서 줄이 바뀝니다.", + "wordWrap.wordWrapColumn": "`editor.wordWrapColumn`에서 줄이 바뀝니다.", + "wordWrap.bounded": "뷰포트의 최소값 및 `editor.wordWrapColumn`에서 줄이 바뀝니다.", + "wordWrap": "줄 바꿈 여부를 제어합니다. 다음 중 하나일 수 있습니다.\n - 'off' (줄 바꿈 사용 안 함),\n - 'on' (뷰포트 줄 바꿈),\n - 'wordWrapColumn' (`editor.wordWrapColumn`에서 줄 바꿈) 또는\n - 'bounded' (뷰포트의 최소값 및 `editor.wordWrapColumn`에서 줄 바꿈).", + "wordWrapColumn": "`editor.wordWrap`이 'wordWrapColumn' 또는 'bounded'인 경우 편집기의 열 줄 바꿈을 제어합니다.", + "wrappingIndent": "줄 바꿈 행의 들여쓰기를 제어합니다. 'none', 'same' 또는 'indent' 중 하나일 수 있습니다.", + "mouseWheelScrollSensitivity": "마우스 휠 스크롤 이벤트의 `deltaX` 및 `deltaY`에서 사용할 승수", + "multiCursorModifier.ctrlCmd": "Windows와 Linux의 'Control'을 OSX의 'Command'로 매핑합니다.", + "multiCursorModifier.alt": "Windows와 Linux의 'Alt'를 OSX의 'Option'으로 매핑합니다.", + "multiCursorModifier": "마우스로 여러 커서를 추가할 때 사용할 수정자입니다. `ctrlCmd`는 Windows와 Linux에서 `Control`로 매핑되고 OSX에서 `Command`로 매핑됩니다. Go To Definition 및 Open Link 마우스 제스처가 멀티커서 수정자와 충돌하지 않도록 조정됩니다.", + "quickSuggestions.strings": "문자열 내에서 빠른 제안을 사용합니다.", + "quickSuggestions.comments": "주석 내에서 빠른 제안을 사용합니다.", + "quickSuggestions.other": "문자열 및 주석 외부에서 빠른 제안을 사용합니다.", + "quickSuggestions": "입력하는 동안 제안을 자동으로 표시할지 여부를 제어합니다.", + "quickSuggestionsDelay": "빠른 제안을 표시할 지연 시간(ms)을 제어합니다.", + "parameterHints": "입력과 동시에 매개변수 문서와 유형 정보를 표시하는 팝업을 사용", + "autoClosingBrackets": "괄호를 연 다음에 편집기에서 괄호를 자동으로 닫을지 여부를 제어합니다.", + "formatOnType": "입력 후 편집기에서 자동으로 줄의 서식을 지정할지 여부를 제어합니다.", + "formatOnPaste": "붙여넣은 콘텐츠의 서식을 편집기에서 자동으로 지정할지 여부를 제어합니다. 포맷터는 반드시 사용할 수 있어야 하며 문서에서 범위의 서식을 지정할 수 있어야 합니다.", + "autoIndent": "사용자가 입력을 하거나 행을 붙여넣기 또는 이동할 때 편집기가 자동으로 들여쓰기를 적용할지 결정합니다. 해당 언어의 들여쓰기 규칙이 있어야 합니다.", + "suggestOnTriggerCharacters": "트리거 문자를 입력할 때 제안을 자동으로 표시할지 여부를 제어합니다.", + "acceptSuggestionOnEnter": "'Tab' 키 외에 'Enter' 키에 대한 제안도 허용할지를 제어합니다. 새 줄을 삽입하는 동작과 제안을 허용하는 동작 간의 모호함을 없앨 수 있습니다.", + "acceptSuggestionOnCommitCharacter": "커밋 문자에 대한 제안을 허용할지를 제어합니다. 예를 들어 JavaScript에서는 세미콜론(';')이 제안을 허용하고 해당 문자를 입력하는 커밋 문자일 수 있습니다.", + "snippetSuggestions.top": "다른 제안 위에 조각 제안을 표시합니다.", + "snippetSuggestions.bottom": "다른 제안 아래에 조각 제안을 표시합니다.", + "snippetSuggestions.inline": "다른 제안과 함께 조각 제안을 표시합니다.", + "snippetSuggestions.none": "코드 조각 제안을 표시하지 않습니다.\n", + "snippetSuggestions": "코드 조각이 다른 추천과 함께 표시되는지 여부 및 정렬 방법을 제어합니다.", + "emptySelectionClipboard": "선택 영역 없이 현재 줄 복사 여부를 제어합니다.", + "wordBasedSuggestions": "문서 내 단어를 기반으로 완성을 계산할지 여부를 제어합니다.", + "suggestFontSize": "제안 위젯의 글꼴 크기", + "suggestLineHeight": "제안 위젯의 줄 높이", + "selectionHighlight": "편집기에서 선택 항목과 유사한 일치 항목을 강조 표시할지 여부를 제어합니다.", + "occurrencesHighlight": "편집기에서 의미 체계 기호 항목을 강조 표시할지 여부를 제어합니다.", + "overviewRulerLanes": "개요 눈금자에서 동일한 위치에 표시될 수 있는 장식 수를 제어합니다.", + "overviewRulerBorder": "개요 눈금자 주위에 테두리를 그릴지 여부를 제어합니다.", + "cursorBlinking": "커서 애니메이션 스타일을 제어합니다. 가능한 값은 'blink', 'smooth', 'phase', 'expand' 및 'solid'입니다.", + "mouseWheelZoom": "마우스 휠을 사용할 때 Ctrl 키를 누르고 있으면 편집기의 글꼴 확대/축소", + "cursorStyle": "커서 스타일을 제어합니다. 허용되는 값은 '블록', '블록-윤곽', '줄', '줄-가늘게', '밑줄' 및 '밑줄-가늘게'입니다.", + "fontLigatures": "글꼴 합자 사용", + "hideCursorInOverviewRuler": "커서가 개요 눈금자에서 가려져야 하는지 여부를 제어합니다.", + "renderWhitespace": "편집기에서 공백 문자를 렌더링하는 방법을 제어합니다. 가능한 값은 'none', 'boundary' 및 'all'입니다. 'boundary' 옵션은 단어 사이의 한 칸 공백을 렌더링하지 않습니다.", + "renderControlCharacters": "편집기에서 제어 문자를 렌더링할지를 제어합니다.", + "renderIndentGuides": "편집기에서 들여쓰기 가이드를 렌더링할지를 제어합니다.", + "renderLineHighlight": "편집기가 현재 줄 강조 표시를 렌더링하는 방식을 제어합니다. 가능한 값은 'none', 'gutter', 'line' 및 'all'입니다.", + "codeLens": "편집기에서 코드 필터를 표시하는지 여부를 제어합니다.", + "folding": "편집기에서 코드 접기를 사용할지 여부를 제어합니다.", + "showFoldingControls": "거터의 폴드 컨트롤을 자동으로 숨길지 결정합니다.", + "matchBrackets": "대괄호 중 하나를 선택할 때 일치하는 대괄호를 강조 표시합니다.", + "glyphMargin": "편집기에서 세로 문자 모양 여백을 렌더링할지 여부를 제어합니다. 문자 모양 여백은 주로 디버깅에 사용됩니다.", + "useTabStops": "탭 정지 뒤에 공백 삽입 및 삭제", + "trimAutoWhitespace": "끝에 자동 삽입된 공백 제거", + "stablePeek": "해당 콘텐츠를 두 번 클릭하거나 키를 누르더라도 Peek 편집기를 열린 상태로 유지합니다.", + "dragAndDrop": "편집기에서 끌어서 놓기로 선택 영역을 이동할 수 있는지 여부를 제어합니다.", + "accessibilitySupport.auto": "편집기가 스크린 리더가 연결되면 플랫폼 API를 사용하여 감지합니다.", + "accessibilitySupport.on": "편집기가 스크린 리더 사용을 위해 영구적으로 최적화됩니다.", + "accessibilitySupport.off": "편집기가 스크린 리더 사용을 위해 최적화되지 않습니다.", + "accessibilitySupport": "편집기를 스크린 리더를 위해 최적화된 모드로 실행할지 결정합니다.", + "links": "편집기에서 링크를 감지하고 클릭할 수 있게 만들지 결정합니다.", + "colorDecorators": "편집기에서 인라인 색 데코레이터 및 색 선택을 렌더링할지를 제어합니다.", + "sideBySide": "diff 편집기에서 diff를 나란히 표시할지 인라인으로 표시할지 여부를 제어합니다.", + "ignoreTrimWhitespace": "diff 편집기에서 선행 공백 또는 후행 공백 변경을 diffs로 표시할지 여부를 제어합니다.", + "renderIndicators": "diff 편집기에서 추가/제거된 변경 내용에 대해 +/- 표시기를 표시하는지 여부를 제어합니다.", + "selectionClipboard": "Linux 주 클립보드의 지원 여부를 제어합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/common/config/defaultConfig.i18n.json b/i18n/kor/src/vs/editor/common/config/defaultConfig.i18n.json new file mode 100644 index 0000000000..4f827773f5 --- /dev/null +++ b/i18n/kor/src/vs/editor/common/config/defaultConfig.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorViewAccessibleLabel": "편집기 콘텐츠" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/kor/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..07b49afc61 --- /dev/null +++ b/i18n/kor/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "지금은 편집기를 사용할 수 없습니다. Alt+F1을 눌러 옵션을 보세요.", + "editorViewAccessibleLabel": "편집기 콘텐츠" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/common/controller/cursor.i18n.json b/i18n/kor/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..3f2e60c8b2 --- /dev/null +++ b/i18n/kor/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "명령을 실행하는 동안 예기치 않은 예외가 발생했습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/kor/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..63a95bf49d --- /dev/null +++ b/i18n/kor/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "입력을 토큰화하는 동안 모드에서 오류가 발생했습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/kor/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..308414e750 --- /dev/null +++ b/i18n/kor/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "일반 텍스트" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/kor/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..d02d2e4f99 --- /dev/null +++ b/i18n/kor/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "이러한 파일이 동시에 변경되었습니다. {0}", + "summary.0": "편집하지 않음", + "summary.nm": "{1}개 파일에서 {0}개 텍스트 편집을 수행함", + "summary.n0": "1개 파일에서 {0}개 텍스트 편집을 수행함" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/kor/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..ee0686818c --- /dev/null +++ b/i18n/kor/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "언어 선언을 적용합니다.", + "vscode.extension.contributes.languages.id": "언어의 ID입니다.", + "vscode.extension.contributes.languages.aliases": "언어에 대한 이름 별칭입니다.", + "vscode.extension.contributes.languages.extensions": "파일 확장이 언어에 연결되어 있습니다.", + "vscode.extension.contributes.languages.filenames": "파일 이름이 언어에 연결되어 있습니다.", + "vscode.extension.contributes.languages.filenamePatterns": "파일 이름 GLOB 패턴이 언어에 연결되어 있습니다.", + "vscode.extension.contributes.languages.mimetypes": "Mime 형식이 언어에 연결되어 있습니다.", + "vscode.extension.contributes.languages.firstLine": "언어 파일의 첫 번째 줄과 일치하는 정규식입니다.", + "vscode.extension.contributes.languages.configuration": "언어에 대한 구성 옵션을 포함하는 파일에 대한 상대 경로입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/kor/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/kor/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/kor/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..88166e17f5 --- /dev/null +++ b/i18n/kor/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "커서 위치의 줄 강조 표시에 대한 배경색입니다.", + "lineHighlightBorderBox": "커서 위치의 줄 테두리에 대한 배경색입니다.", + "rangeHighlight": "빠른 열기 및 찾기 기능 등을 통해 강조 표시된 영역의 배경색입니다.", + "caret": "편집기 커서 색입니다.", + "editorCursorBackground": "편집기 커서의 배경색입니다. 블록 커서와 겹치는 글자의 색상을 사용자 정의할 수 있습니다.", + "editorWhitespaces": "편집기의 공백 문자 색입니다.", + "editorIndentGuides": "편집기 들여쓰기 안내선 색입니다.", + "editorLineNumbers": "편집기 줄 번호 색입니다.", + "editorRuler": "편집기 눈금의 색상입니다.", + "editorCodeLensForeground": "편집기 코드 렌즈의 전경색입니다.", + "editorBracketMatchBackground": "일치하는 브래킷 뒤의 배경색입니다.", + "editorBracketMatchBorder": "일치하는 브래킷 박스의 색상", + "editorOverviewRulerBorder": "개요 눈금 경계의 색상입니다.", + "editorGutter": "편집기 거터의 배경색입니다. 거터에는 글리프 여백과 행 수가 있습니다.", + "errorForeground": "편집기 내 오류 표시선의 전경색입니다.", + "errorBorder": "편집기 내 오류 표시선의 테두리 색입니다.", + "warningForeground": "편집기 내 경고 표시선의 전경색입니다.", + "warningBorder": "편집기 내 경고 표시선의 테두리 색입니다.", + "overviewRulerRangeHighlight": "범위 강조 표시의 개요 눈금자 마커 색입니다.", + "overviewRuleError": "오류의 개요 눈금자 마커 색입니다.", + "overviewRuleWarning": "경고의 개요 눈금자 마커 색입니다.", + "overviewRuleInfo": "정보의 개요 눈금자 마커 색입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json b/i18n/kor/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json new file mode 100644 index 0000000000..38ac1ae68d --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "introMsg": "VS Code의 접근성 옵션을 사용해 주셔서 감사합니다.", + "status": "상태:", + "tabFocusModeOnMsg": "현재 편집기에서 키를 누르면 포커스가 다음 포커스 가능한 요소로 이동합니다. {0}을(를) 눌러서 이 동작을 설정/해제합니다.", + "tabFocusModeOnMsgNoKb": "현재 편집기에서 키를 누르면 포커스가 다음 포커스 가능한 요소로 이동합니다. {0} 명령은 현재 키 바인딩으로 트리거할 수 없습니다.", + "tabFocusModeOffMsg": "현재 편집기에서 키를 누르면 탭 문자가 삽입됩니다. {0}을(를) 눌러서 이 동작을 설정/해제합니다.", + "tabFocusModeOffMsgNoKb": "현재 편집기에서 키를 누르면 탭 문자가 삽입됩니다. {0} 명령은 현재 키 바인딩으로 트리거할 수 없습니다.", + "outroMsg": "이 도구 설명을 해제하고 Esc 키를 눌러서 편집기로 돌아갈 수 있습니다.", + "ShowAccessibilityHelpAction": "접근성 도움말 표시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/kor/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..0c3b50879d --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "대괄호로 이동" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/kor/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..4f2bbb888f --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "캐럿을 왼쪽으로 이동", + "caret.moveRight": "캐럿을 오른쪽으로 이동" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/kor/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..fdf08c1173 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "문자 바꾸기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/kor/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..2afb3fab81 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "잘라내기", + "actions.clipboard.copyLabel": "복사", + "actions.clipboard.pasteLabel": "붙여넣기", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "구문을 강조 표시하여 복사" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/kor/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..297a673251 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "줄 주석 설정/해제", + "comment.line.add": "줄 주석 추가", + "comment.line.remove": "줄 주석 제거", + "comment.block": "블록 주석 설정/해제" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/kor/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..4c4da3f91e --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "편집기 상황에 맞는 메뉴 표시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/kor/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..73122c4e2b --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "찾기", + "placeholder.find": "찾기", + "label.previousMatchButton": "이전 검색 결과", + "label.nextMatchButton": "다음 검색 결과", + "label.toggleSelectionFind": "선택 항목에서 찾기", + "label.closeButton": "닫기", + "label.replace": "바꾸기", + "placeholder.replace": "바꾸기", + "label.replaceButton": "바꾸기", + "label.replaceAllButton": "모두 바꾸기", + "label.toggleReplaceButton": "바꾸기 모드 설정/해제", + "title.matchesCountLimit": "처음 999개의 결과가 강조 표시되지만 모든 찾기 작업은 전체 텍스트에 대해 수행됩니다.", + "label.matchesLocation": "{0}/{1}", + "label.noResults": "결과 없음" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/kor/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..3ddd3d3081 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "찾기", + "placeholder.find": "찾기", + "label.previousMatchButton": "이전 검색 결과", + "label.nextMatchButton": "다음 검색 결과", + "label.closeButton": "닫기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/kor/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..418d829d0c --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "찾기", + "findNextMatchAction": "다음 찾기", + "findPreviousMatchAction": "이전 찾기", + "nextSelectionMatchFindAction": "다음 선택 찾기", + "previousSelectionMatchFindAction": "이전 선택 찾기", + "startReplace": "바꾸기", + "addSelectionToNextFindMatch": "다음 일치 항목 찾기에 선택 항목 추가", + "addSelectionToPreviousFindMatch": "이전 일치 항목 찾기에 선택 항목 추가", + "moveSelectionToNextFindMatch": "다음 일치 항목 찾기로 마지막 선택 항목 이동", + "moveSelectionToPreviousFindMatch": "마지막 선택 항목을 이전 일치 항목 찾기로 이동", + "selectAllOccurrencesOfFindMatch": "일치 항목 찾기의 모든 항목 선택", + "changeAll.label": "모든 항목 변경", + "showNextFindTermAction": "다음 검색어 표시", + "showPreviousFindTermAction": "이전 검색어 표시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/kor/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..8cacaf60fe --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "펼치기", + "unFoldRecursivelyAction.label": "재귀적으로 펼치기", + "foldAction.label": "접기", + "foldRecursivelyAction.label": "재귀적으로 접기", + "foldAllAction.label": "모두 접기", + "unfoldAllAction.label": "모두 펼치기", + "foldLevelAction.label": "수준 {0} 접기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/kor/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..6628f62225 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "줄 {0}에서 1개 서식 편집을 수행했습니다.", + "hintn1": "줄 {1}에서 {0}개 서식 편집을 수행했습니다.", + "hint1n": "줄 {0}과(와) {1} 사이에서 1개 서식 편집을 수행했습니다.", + "hintnn": "줄 {1}과(와) {2} 사이에서 {0}개 서식 편집을 수행했습니다.", + "formatDocument.label": "문서 서식", + "formatSelection.label": "선택 영역 서식" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json b/i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json new file mode 100644 index 0000000000..f339ec1d30 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "'{0}'에 대한 정의를 찾을 수 없습니다.", + "generic.noResults": "정의를 찾을 수 없음", + "meta.title": "– {0} 정의", + "actions.goToDecl.label": "정의로 이동", + "actions.goToDeclToSide.label": "측면에서 정의 열기", + "actions.previewDecl.label": "정의 피킹(Peeking)", + "goToImplementation.noResultWord": "'{0}'에 대한 구현을 찾을 수 없습니다.", + "goToImplementation.generic.noResults": "구현을 찾을 수 없습니다.", + "meta.implementations.title": " – {0} 개 구현", + "actions.goToImplementation.label": "구현으로 이동", + "actions.peekImplementation.label": "구현 미리 보기", + "goToTypeDefinition.noResultWord": "'{0}'에 대한 형식 정의를 찾을 수 없습니다.", + "goToTypeDefinition.generic.noResults": "형식 정의를 찾을 수 없습니다.", + "meta.typeDefinitions.title": "– {0} 형식 정의", + "actions.goToTypeDefinition.label": "형식 정의로 이동", + "actions.peekTypeDefinition.label": "형식 정의 미리 보기", + "multipleResults": "{0}개 정의를 표시하려면 클릭하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..e989e44ad7 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "'{0}'에 대한 정의를 찾을 수 없습니다.", + "generic.noResults": "정의를 찾을 수 없음", + "meta.title": "– {0} 정의", + "actions.goToDecl.label": "정의로 이동", + "actions.goToDeclToSide.label": "측면에서 정의 열기", + "actions.previewDecl.label": "정의 피킹(Peeking)", + "goToImplementation.noResultWord": "'{0}'에 대한 구현을 찾을 수 없습니다.", + "goToImplementation.generic.noResults": "구현을 찾을 수 없습니다.", + "meta.implementations.title": " – {0} 개 구현", + "actions.goToImplementation.label": "구현으로 이동", + "actions.peekImplementation.label": "구현 미리 보기", + "goToTypeDefinition.noResultWord": "'{0}'에 대한 형식 정의를 찾을 수 없습니다.", + "goToTypeDefinition.generic.noResults": "형식 정의를 찾을 수 없습니다.", + "meta.typeDefinitions.title": "– {0} 형식 정의", + "actions.goToTypeDefinition.label": "형식 정의로 이동", + "actions.peekTypeDefinition.label": "형식 정의 미리 보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..fbc8d762d0 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "{0}개 정의를 표시하려면 클릭하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/kor/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..6ea023424a --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "다음 오류 또는 경고로 이동", + "markerAction.previous.label": "이전 오류 또는 경고로 이동", + "editorMarkerNavigationError": "편집기 표식 탐색 위젯 오류 색입니다.", + "editorMarkerNavigationWarning": "편집기 표식 탐색 위젯 경고 색입니다.", + "editorMarkerNavigationBackground": "편집기 표식 탐색 위젯 배경입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/kor/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..65f234bb94 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "가리키기 표시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/kor/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..1c210a480b --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "로드 중..." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/kor/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..7505c87e22 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "이전 값으로 바꾸기", + "InPlaceReplaceAction.next.label": "다음 값으로 바꾸기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/kor/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..a81f4598ba --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "들여쓰기를 공백으로 변환", + "indentationToTabs": "들여쓰기를 탭으로 변환", + "configuredTabSize": "구성된 탭 크기", + "selectTabWidth": "현재 파일의 탭 크기 선택", + "indentUsingTabs": "탭을 사용한 들여쓰기", + "indentUsingSpaces": "공백을 사용한 들여쓰기", + "detectIndentation": "콘텐츠에서 들여쓰기 감지", + "editor.reindentlines": "줄 다시 들여쓰기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json b/i18n/kor/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f891a70fbe --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "개발자: TM 범위 검사", + "inspectTMScopesWidget.loading": "로드 중..." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/kor/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..ccdadc73ed --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "위에 줄 복사", + "lines.copyDown": "아래에 줄 복사", + "lines.moveUp": "줄 위로 이동", + "lines.moveDown": "줄 아래로 이동", + "lines.sortAscending": "줄을 오름차순 정렬", + "lines.sortDescending": "줄을 내림차순으로 정렬", + "lines.trimTrailingWhitespace": "후행 공백 자르기", + "lines.delete": "줄 삭제", + "lines.indent": "줄 들여쓰기", + "lines.outdent": "줄 내어쓰기", + "lines.insertBefore": "위에 줄 삽입", + "lines.insertAfter": "아래에 줄 삽입", + "lines.deleteAllLeft": "왼쪽 모두 삭제", + "lines.deleteAllRight": "우측에 있는 항목 삭제", + "lines.joinLines": "줄 연결", + "editor.transpose": "커서 주위 문자 바꾸기", + "editor.transformToUppercase": "대문자로 변환", + "editor.transformToLowercase": "소문자로 변환" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/kor/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..8482a78310 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "Cmd 키를 누르고 클릭하여 링크로 이동", + "links.navigate": "Ctrl 키를 누르고 클릭하여 링크로 이동", + "links.command.mac": "명령을 실행하려면 Cmd+클릭", + "links.command": "명령을 실행하려면 Ctrl+클릭", + "links.navigate.al": "Alt 키를 누르고 클릭하여 링크로 이동", + "links.command.al": "명령을 실행하려면 Alt+클릭", + "invalid.url": "죄송합니다. 이 링크는 형식이 올바르지 않으므로 열지 못했습니다. {0}", + "missing.url": "죄송합니다. 대상이 없으므로 이 링크를 열지 못했습니다.", + "label": "링크 열기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/kor/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..8b239a5e83 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "위에 커서 추가", + "mutlicursor.insertBelow": "아래에 커서 추가", + "mutlicursor.insertAtEndOfEachLineSelected": "줄 끝에 커서 추가" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/kor/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..c2191a95a1 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "매개 변수 힌트 트리거" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/kor/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..010ddaa150 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0}, 힌트" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/kor/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..2811b0b572 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "수정 사항 표시({0})", + "quickFix": "수정 사항 표시", + "quickfix.trigger.label": "빠른 수정" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..37957c14b5 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": "–참조 {0}개", + "references.action.label": "모든 참조 찾기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..201297ab0a --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "로드 중..." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..145ba5d300 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "{2}열, {1}줄, {0}의 기호", + "aria.fileReferences.1": "{0}의 기호 1개, 전체 경로 {1}", + "aria.fileReferences.N": "{1}의 기호 {0}개, 전체 경로 {2}", + "aria.result.0": "결과 없음", + "aria.result.1": "{0}에서 기호 1개를 찾았습니다.", + "aria.result.n1": "{1}에서 기호 {0}개를 찾았습니다.", + "aria.result.nm": "{1}개 파일에서 기호 {0}개를 찾았습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..ce1055925b --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "파일을 확인하지 못했습니다.", + "referencesCount": "참조 {0}개", + "referenceCount": "참조 {0}개", + "missingPreviewMessage": "미리 보기를 사용할 수 없음", + "treeAriaLabel": "참조", + "noResults": "결과 없음", + "peekView.alternateTitle": "참조", + "peekViewTitleBackground": "Peek 뷰 제목 영역의 배경색입니다.", + "peekViewTitleForeground": "Peek 뷰 제목 색입니다.", + "peekViewTitleInfoForeground": "Peek 뷰 제목 정보 색입니다.", + "peekViewBorder": "Peek 뷰 테두리 및 화살표 색입니다.", + "peekViewResultsBackground": "Peek 뷰 결과 목록의 배경색입니다.", + "peekViewResultsMatchForeground": "Peek 뷰 결과 목록에서 라인 노드의 전경색입니다.", + "peekViewResultsFileForeground": "Peek 뷰 결과 목록에서 파일 노드의 전경색입니다.", + "peekViewResultsSelectionBackground": "Peek 뷰 결과 목록에서 선택된 항목의 배경색입니다.", + "peekViewResultsSelectionForeground": "Peek 뷰 결과 목록에서 선택된 항목의 전경색입니다.", + "peekViewEditorBackground": "Peek 뷰 편집기의 배경색입니다.", + "peekViewEditorGutterBackground": "Peek 뷰 편집기의 거터 배경색입니다.", + "peekViewResultsMatchHighlight": "Peek 뷰 결과 목록의 일치 항목 강조 표시 색입니다.", + "peekViewEditorMatchHighlight": "Peek 뷰 편집기의 일치 항목 강조 표시 색입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/kor/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..52cdcfa509 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "결과가 없습니다.", + "aria": "'{0}'을(를) '{1}'(으)로 이름을 변경했습니다. 요약: {2}", + "rename.failed": "죄송합니다. 이름 바꾸기를 실행하지 못했습니다.", + "rename.label": "기호 이름 바꾸기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/kor/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..436106783d --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "입력 이름을 바꾸세요. 새 이름을 입력한 다음 [Enter] 키를 눌러 커밋하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/kor/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..8c1fca4d1b --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "선택 확장", + "smartSelect.shrink": "선택 축소" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/kor/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..0124fd42bd --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "'{0}'을(를) 적용하여 다음 텍스트가 삽입되었습니다.\n {1}", + "suggest.trigger.label": "제안 항목 트리거" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/kor/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..30efc5edd9 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "제안 위젯의 배경색입니다.", + "editorSuggestWidgetBorder": "제안 위젯의 테두리 색입니다.", + "editorSuggestWidgetForeground": "제안 위젯의 전경색입니다.", + "editorSuggestWidgetSelectedBackground": "제한 위젯에서 선택된 항목의 배경색입니다.", + "editorSuggestWidgetHighlightForeground": "제안 위젯의 일치 항목 강조 표시 색입니다.", + "readMore": "자세히 알아보기...{0}", + "suggestionWithDetailsAriaLabel": "{0}, 제안, 세부 정보 있음", + "suggestionAriaLabel": "{0}, 제안", + "readLess": "간단히 보기...{0}", + "suggestWidget.loading": "로드 중...", + "suggestWidget.noSuggestions": "제안 항목이 없습니다.", + "suggestionAriaAccepted": "{0}, 수락됨", + "ariaCurrentSuggestionWithDetails": "{0}, 제안, 세부 정보 있음", + "ariaCurrentSuggestion": "{0}, 제안" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/kor/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..37a4d0a9bc --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": " 키로 포커스 이동 설정/해제" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/kor/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..6e6cabef34 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "변수 읽기와 같은 읽기 액세스 중 기호의 배경색입니다.", + "wordHighlightStrong": "변수에 쓰기와 같은 쓰기 액세스 중 기호의 배경색입니다.", + "overviewRulerWordHighlightForeground": "기호 강조 표시의 개요 눈금자 마커 색입니다.", + "overviewRulerWordHighlightStrongForeground": "쓰기 권한 기호 강조 표시의 개요 눈금자 마커 색입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/kor/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..15cd818bd6 --- /dev/null +++ b/i18n/kor/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "닫기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/kor/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..9a33dc507b --- /dev/null +++ b/i18n/kor/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "`contributes.{0}.language`에 알 수 없는 언어가 있습니다. 제공된 값: {1}", + "invalid.scopeName": "`contributes.{0}.scopeName`에 문자열이 필요합니다. 제공된 값: {1}", + "invalid.path.0": "`contributes.{0}.path`에 문자열이 필요합니다. 제공된 값: {1}", + "invalid.injectTo": "`contributes.{0}.injectTo`의 값이 잘못되었습니다. 언어 범위 이름 배열이어야 합니다. 제공된 값: {1}", + "invalid.embeddedLanguages": "`contributes.{0}.embeddedLanguages` 값이 잘못되었습니다. 범위 이름에서 언어까지의 개체 맵이어야 합니다. 제공된 값: {1}", + "invalid.path.1": "확장 폴더({2})에 포함할 `contributes.{0}.path`({1})가 필요합니다. 확장이 이식 불가능해질 수 있습니다.", + "no-tm-grammar": "이 언어에 대해 등록된 TM 문법이 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/kor/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..02b2156a93 --- /dev/null +++ b/i18n/kor/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "{0}을(를) 구문 분석하는 동안 오류가 발생했습니다. {1}", + "schema.openBracket": "여는 대괄호 문자 또는 문자열 시퀀스입니다.", + "schema.closeBracket": "닫는 대괄호 문자 또는 문자열 시퀀스입니다.", + "schema.comments": "주석 기호를 정의합니다.", + "schema.blockComments": "블록 주석이 표시되는 방법을 정의합니다.", + "schema.blockComment.begin": "블록 주석을 시작하는 문자 시퀀스입니다.", + "schema.blockComment.end": "블록 주석을 끝내는 문자 시퀀스입니다.", + "schema.lineComment": "줄 주석을 시작하는 문자 시퀀스입니다.", + "schema.brackets": "들여쓰기를 늘리거나 줄이는 대괄호 기호를 정의합니다.", + "schema.autoClosingPairs": "대괄호 쌍을 정의합니다. 여는 대괄호를 입력하면 닫는 대괄호가 자동으로 삽입됩니다.", + "schema.autoClosingPairs.notIn": "자동 쌍을 사용하지 않도록 설정된 범위 목록을 정의합니다.", + "schema.surroundingPairs": "선택한 문자열을 둘러싸는 데 사용할 수 있는 대괄호 쌍을 정의합니다.", + "schema.wordPattern": "해당 언어에 대한 단어 정의입니다.", + "schema.wordPattern.pattern": "단어 일치에 사용하는 RegEXP 패턴입니다.", + "schema.wordPattern.flags": "단어 일치에 사용하는 RegExp 플래그입니다.", + "schema.wordPattern.flags.errorMessage": "`/^([gimuy]+)$/` 패턴과 일치해야 합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/kor/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..6673bdbee2 --- /dev/null +++ b/i18n/kor/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "textmate 토크나이저를 적용합니다.", + "vscode.extension.contributes.grammars.language": "이 구문이 적용되는 언어 식별자입니다.", + "vscode.extension.contributes.grammars.scopeName": "tmLanguage 파일에 사용되는 Textmate 범위 이름입니다.", + "vscode.extension.contributes.grammars.path": "tmLanguage 파일의 경로입니다. 확장 폴더의 상대 경로이며 일반적으로 './syntaxes/'로 시작합니다.", + "vscode.extension.contributes.grammars.embeddedLanguages": "이 문법에 포함된 언어가 있는 경우 언어 ID에 대한 범위 이름의 맵입니다.", + "vscode.extension.contributes.grammars.injectTo": "이 문법이 삽입되는 언어 범위 이름 목록입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/kor/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..4d5643e37c --- /dev/null +++ b/i18n/kor/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0}({1})" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/kor/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..6c44896e5a --- /dev/null +++ b/i18n/kor/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "메뉴 항목은 배열이어야 합니다.", + "requirestring": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "optstring": "속성 `{0}`은(는) 생략할 수 있으며 `string` 형식이어야 합니다.", + "vscode.extension.contributes.menuItem.command": "실행할 명령의 식별자입니다. 명령은 '명령' 섹션에 선언되어야 합니다.", + "vscode.extension.contributes.menuItem.alt": "실행할 대체 명령의 식별자입니다. 명령은 '명령' 섹션에 선언되어야 합니다.", + "vscode.extension.contributes.menuItem.when": "이 항목을 표시하기 위해 true여야 하는 조건입니다.", + "vscode.extension.contributes.menuItem.group": "이 명령이 속하는 그룹입니다.", + "vscode.extension.contributes.menus": "편집기에 메뉴 항목을 적용합니다.", + "menus.commandPalette": "명령 팔레트", + "menus.editorTitle": "편집기 제목 메뉴", + "menus.editorContext": "편집기 상황에 맞는 메뉴", + "menus.explorerContext": "파일 탐색기 상황에 맞는 메뉴", + "menus.editorTabContext": "편집기 탭 상황에 맞는 메뉴", + "menus.debugCallstackContext": "디버그 호출 스택 상황에 맞는 메뉴", + "menus.scmTitle": "소스 제어 제목 메뉴", + "menus.resourceGroupContext": "소스 제어 리소스 그룹 상황에 맞는 메뉴", + "menus.resourceStateContext": "소스 제어 리소스 상태 상황에 맞는 메뉴", + "view.viewTitle": "기여 조회 제목 메뉴", + "view.itemContext": "기여 조회 항목 상황에 맞는 메뉴", + "nonempty": "비어 있지 않은 값이 필요합니다.", + "opticon": "`icon` 속성은 생략할 수 있거나 문자열 또는 리터럴(예: `{dark, light}`)이어야 합니다.", + "requireStringOrObject": "`{0}` 속성은 필수이며 `string` 또는 `object` 형식이어야 합니다.", + "requirestrings": "`{0}` 및 `{1}` 속성은 필수이며 `string` 형식이어야 합니다.", + "vscode.extension.contributes.commandType.command": "실행할 명령의 식별자", + "vscode.extension.contributes.commandType.title": "명령이 UI에 표시되는 제목입니다.", + "vscode.extension.contributes.commandType.category": "(선택 사항) UI에서 명령별 범주 문자열을 그룹화합니다.", + "vscode.extension.contributes.commandType.icon": "(선택 사항) UI에서 명령을 나타내는 데 사용되는 아이콘입니다. 파일 경로 또는 테마 지정 가능 구성입니다.", + "vscode.extension.contributes.commandType.icon.light": "밝은 테마가 사용될 경우의 아이콘 경로입니다.", + "vscode.extension.contributes.commandType.icon.dark": "어두운 테마가 사용될 경우의 아이콘 경로입니다.", + "vscode.extension.contributes.commands": "명령 팔레트에 명령을 적용합니다.", + "dup": "`명령` 섹션에 `{0}` 명령이 여러 번 나타납니다.", + "menuId.invalid": "`{0}`은(는) 유효한 메뉴 식별자가 아닙니다.", + "missing.command": "메뉴 항목이 '명령' 섹션에 정의되지 않은 `{0}` 명령을 참조합니다.", + "missing.altCommand": "메뉴 항목이 '명령' 섹션에 정의되지 않은 alt 명령 `{0}`을(를) 참조합니다.", + "dupe.command": "메뉴 항목이 동일한 명령을 기본값과 alt 명령으로 참조합니다.", + "nosupport.altCommand": "죄송합니다. 현재 '편집기/제목' 메뉴의 '탐색' 그룹만 alt 명령을 지원합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/kor/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..78db99a956 --- /dev/null +++ b/i18n/kor/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "기본 구성 재정의", + "overrideSettings.description": "{0} 언어에 대해 재정의할 편집기 설정을 구성합니다.", + "overrideSettings.defaultDescription": "언어에 대해 재정의할 편집기 설정을 구성합니다.", + "config.property.languageDefault": "'{0}'을(를) 등록할 수 없습니다. 이는 언어별 편집기 설정을 설명하는 속성 패턴인 '\\\\[.*\\\\]$'과(와) 일치합니다. 'configurationDefaults' 기여를 사용하세요.", + "config.property.duplicate": "'{0}'을(를) 등록할 수 없습니다. 이 속성은 이미 등록되어 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/environment/node/argv.i18n.json b/i18n/kor/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..c6763311ff --- /dev/null +++ b/i18n/kor/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "`--goto` 모드에서 인수는 `FILE(:LINE(:CHARACTER))` 형식이어야 합니다.", + "diff": "두 파일을 서로 비교합니다.", + "add": "마지막 활성 창에 폴더를 추가합니다.", + "goto": "지정된 줄과 문자 위치에 있는 경로의 파일을 엽니다.", + "locale": "사용할 로캘(예: en-US 또는 zh-TW)입니다.", + "newWindow": "Code의 새 인스턴스를 강제 적용합니다.", + "performance": "'Developer: Startup Performance' 명령을 사용하여 시작합니다.", + "prof-startup": "시작하는 동안 CPU 프로파일러 실행", + "reuseWindow": "마지막 활성 창에서 파일 또는 폴더를 강제로 엽니다.", + "userDataDir": "사용자 데이터가 저장되는 디렉터리를 지정합니다(루트로 실행할 경우 유용함).", + "verbose": "자세한 정보 표시를 출력합니다(--wait를 의미).", + "wait": "창이 닫힐 때까지 기다린 후 돌아갑니다.", + "extensionHomePath": "확장의 루트 경로를 설정합니다.", + "listExtensions": "설치된 확장을 나열합니다.", + "showVersions": "#NAME?", + "installExtension": "확장을 설치합니다.", + "uninstallExtension": "확장을 제거합니다.", + "experimentalApis": "확장에 대해 제안된 API 기능을 사용하도록 설정합니다.", + "disableExtensions": "설치된 모든 확장을 사용하지 않도록 설정합니다.", + "disableGPU": "GPU 하드웨어 가속을 사용하지 않도록 설정합니다.", + "version": "버전을 출력합니다.", + "help": "사용법을 출력합니다.", + "usage": "사용법", + "options": "옵션", + "paths": "경로", + "optionsUpperCase": "옵션" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/kor/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..2adad9c2d4 --- /dev/null +++ b/i18n/kor/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "작업 영역이 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/kor/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..2c19a8cce3 --- /dev/null +++ b/i18n/kor/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "확장", + "preferences": "기본 설정" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/kor/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..cc3d8a4f79 --- /dev/null +++ b/i18n/kor/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "확장을 찾을 수 없습니다.", + "noCompatible": "이 버전의 Code에서 {0}의 호환 버전을 찾을 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/kor/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..5e69ba67a3 --- /dev/null +++ b/i18n/kor/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "잘못된 확장: package.json이 JSON 파일이 아닙니다.", + "restartCode": "{0}을(를) 다시 설치하기 전에 Code를 다시 시작하세요.", + "installDependeciesConfirmation": "'{0}'을(를) 설치하면 종속성도 설치됩니다. 계속할까요?", + "install": "예", + "doNotInstall": "아니요", + "uninstallDependeciesConfirmation": "'{0}'만 제거할까요, 아니면 종속성도 제거할까요?", + "uninstallOnly": "만", + "uninstallAll": "모두", + "cancel": "취소", + "uninstallConfirmation": "'{0}'을(를) 제거할까요?", + "ok": "확인", + "singleDependentError": "확장 '{0}'을(를) 제거할 수 없습니다. 확장 '{1}'이(가) 이 확장에 종속됩니다.", + "twoDependentsError": "확장 '{0}'을(를) 제거할 수 없습니다. 확장 '{1}' 및 '{2}'이(가) 이 확장에 종속됩니다.", + "multipleDependentsError": "확장 '{0}'을(를) 제거할 수 없습니다. 확장 '{1}', '{2}' 등이 이 확장에 종속됩니다.", + "notExists": "확장을 찾을 수 없음" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/kor/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..0b96059408 --- /dev/null +++ b/i18n/kor/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "확장 `{1}`을(를) 활성화하지 못했습니다. 이유: 알 수 없는 종속성 `{0}`.", + "failedDep1": "확장 `{1}`을(를) 활성화하지 못했습니다. 이유: 종속성 `{0}`이(가) 활성화되지 않았습니다.", + "failedDep2": "확장 `{0}`을(를) 활성화하지 못했습니다. 이유: 종속성 수준이 10개가 넘음(종속성 루프일 가능성이 높음).", + "activationError": "확장 `{0}` 활성화 실패: {1}." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/kor/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..c2743a1741 --- /dev/null +++ b/i18n/kor/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "VS Code 확장의 경우, 확장이 호환되는 VS Code 버전을 지정합니다. *일 수 없습니다. 예를 들어 ^0.10.5는 최소 VS Code 버전인 0.10.5와 호환됨을 나타냅니다.", + "vscode.extension.publisher": "VS Code 확장의 게시자입니다.", + "vscode.extension.displayName": "VS Code 갤러리에 사용되는 확장의 표시 이름입니다.", + "vscode.extension.categories": "확장을 분류하기 위해 VS Code 갤러리에서 사용하는 범주입니다.", + "vscode.extension.galleryBanner": "VS Code 마켓플레이스에 사용되는 배너입니다.", + "vscode.extension.galleryBanner.color": "VS Code 마켓플레이스 페이지 머리글의 배너 색상입니다.", + "vscode.extension.galleryBanner.theme": "배너에 사용되는 글꼴의 색상 테마입니다.", + "vscode.extension.contributes": "이 패키지에 표시된 VS Code 확장의 전체 기여입니다.", + "vscode.extension.preview": "마켓플레이스에서 Preview로 플래그 지정할 확장을 설정합니다.", + "vscode.extension.activationEvents": "VS Code 확장에 대한 활성화 이벤트입니다.", + "vscode.extension.activationEvents.onLanguage": "지정된 언어로 확인되는 파일을 열 때마다 활성화 이벤트가 발송됩니다.", + "vscode.extension.activationEvents.onCommand": "지정된 명령을 호출할 때마다 활성화 이벤트가 발송됩니다.", + "vscode.extension.activationEvents.onDebug": "지정된 유형의 디버깅 세션을 시작할 때마다 활성화 알림이 발송됩니다.", + "vscode.extension.activationEvents.workspaceContains": "지정된 glob 패턴과 일치하는 파일이 하나 이상 있는 폴더를 열 때마다 활성화 알림이 발송됩니다.", + "vscode.extension.activationEvents.onView": "지정된 뷰가 확장될 때마다 활성화 이벤트가 내보내 집니다.", + "vscode.extension.activationEvents.star": "VS Code 시작 시 활성화 이벤트가 발송됩니다. 훌륭한 최종 사용자 경험을 보장하려면 사용 케이스에서 다른 활성화 이벤트 조합이 작동하지 않을 때에만 확장에서 이 활성화 이벤트를 사용하세요.", + "vscode.extension.badges": "마켓플레이스 확장 페이지의 사이드바에 표시할 배지의 배열입니다.", + "vscode.extension.badges.url": "배지 이미지 URL입니다.", + "vscode.extension.badges.href": "배지 링크입니다.", + "vscode.extension.badges.description": "배지 설명입니다.", + "vscode.extension.extensionDependencies": "다른 확장에 대한 종속성입니다. 확장 식별자는 항상 ${publisher}.${name}입니다(예: vscode.csharp).", + "vscode.extension.scripts.prepublish": "패키지가 VS Code 확장 형태로 게시되기 전에 스크립트가 실행되었습니다.", + "vscode.extension.icon": "128x128 픽셀 아이콘의 경로입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/kor/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..3035761804 --- /dev/null +++ b/i18n/kor/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "`engines.vscode` 값 {0}을(를) 구문 분석할 수 없습니다. ^0.10.0, ^1.2.3, ^0.11.0, ^0.10.x 등을 사용하세요.", + "versionSpecificity1": "`engines.vscode`({0})에 지정된 버전이 명확하지 않습니다. vscode 버전이 1.0.0 이전이면 최소한 원하는 주 버전과 부 버전을 정의하세요( 예: ^0.10.0, 0.10.x, 0.11.0 등).", + "versionSpecificity2": "`engines.vscode`({0})에 지정된 버전이 명확하지 않습니다. vscode 버전이 1.0.0 이후이면 최소한 원하는 주 버전을 정의하세요(예: ^1.10.0, 1.10.x, 1.x.x, 2.x.x 등).", + "versionMismatch": "확장이 Code {0}과(와) 호환되지 않습니다. 확장에 {1}이(가) 필요합니다.", + "extensionDescription.empty": "가져온 확장 설명이 비어 있습니다.", + "extensionDescription.publisher": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "extensionDescription.name": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "extensionDescription.version": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "extensionDescription.engines": "속성 `{0}`은(는) 필수이며 `object` 형식이어야 합니다.", + "extensionDescription.engines.vscode": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "extensionDescription.extensionDependencies": "속성 `{0}`은(는) 생략할 수 있으며 `string[]` 형식이어야 합니다.", + "extensionDescription.activationEvents1": "속성 `{0}`은(는) 생략할 수 있으며 `string[]` 형식이어야 합니다.", + "extensionDescription.activationEvents2": "속성 `{0}` 및 `{1}`은(는) 둘 다 지정하거나 둘 다 생략해야 합니다.", + "extensionDescription.main1": "속성 `{0}`은(는) 생략할 수 있으며 `string` 형식이어야 합니다.", + "extensionDescription.main2": "확장의 폴더({1}) 내에 포함할 `main`({0})이 필요합니다. 이로 인해 확장이 이식 불가능한 상태가 될 수 있습니다.", + "extensionDescription.main3": "속성 `{0}` 및 `{1}`은(는) 둘 다 지정하거나 둘 다 생략해야 합니다.", + "notSemver": "확장 버전이 semver와 호환되지 않습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/kor/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..8275d7d273 --- /dev/null +++ b/i18n/kor/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "새 창", + "newWindowDesc": "새 창을 엽니다.", + "recentFolders": "최근 작업 영역", + "folderDesc": "{0} {1}", + "codeWorkspace": "코드 작업 영역" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/kor/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..360e7c92d9 --- /dev/null +++ b/i18n/kor/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "확인", + "integrity.dontShowAgain": "다시 표시 안 함", + "integrity.moreInfo": "추가 정보", + "integrity.prompt": "{0} 설치가 손상된 것 같습니다. 다시 설치하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/kor/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..e0d067032a --- /dev/null +++ b/i18n/kor/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "json 스키마 구성을 적용합니다.", + "contributes.jsonValidation.fileMatch": "일치할 파일 패턴(예: \"package.json\" 또는 \"*.launch\")입니다.", + "contributes.jsonValidation.url": "스키마 URL('http:', 'https:') 또는 확장 폴더에 대한 상대 경로('./')입니다.", + "invalid.jsonValidation": "'configuration.jsonValidation'은 배열이어야 합니다.", + "invalid.fileMatch": "'configuration.jsonValidation.fileMatch'를 정의해야 합니다.", + "invalid.url": "'configuration.jsonValidation.url'은 URL 또는 상대 경로여야 합니다.", + "invalid.url.fileschema": "'configuration.jsonValidation.url'이 잘못된 상대 URL입니다. {0}", + "invalid.url.schema": "확장에 있는 스키마를 참조하려면 'configuration.jsonValidation.url'이 'http:', 'https:' 또는 './'로 시작해야 합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/kor/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..b8b7ae7ab8 --- /dev/null +++ b/i18n/kor/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "({0})을(를) 눌렀습니다. 둘째 키는 잠시 기다렸다가 누르세요.", + "missing.chord": "키 조합({0}, {1})은 명령이 아닙니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/keybinding/common/keybindingLabels.i18n.json b/i18n/kor/src/vs/platform/keybinding/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..27c0805867 --- /dev/null +++ b/i18n/kor/src/vs/platform/keybinding/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "", + "shiftKey": "", + "altKey": "", + "windowsKey": "Windows", + "ctrlKey.long": "컨트롤", + "shiftKey.long": "", + "altKey.long": "", + "cmdKey.long": "명령", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/kor/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..d2e5b513ea --- /dev/null +++ b/i18n/kor/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "loop 속성은 마지막 줄 검사기에서만 지원됩니다.", + "ProblemPatternParser.problemPattern.missingRegExp": "문제 패턴에 정규식이 없습니다.", + "ProblemPatternParser.problemPattern.missingProperty": "문제 패턴이 잘못되었습니다. 하나 이상의 파일, 메시지 및 줄 또는 위치 일치 그룹을 포함해야 합니다.", + "ProblemPatternParser.invalidRegexp": "오류: {0} 문자열은 유효한 정규식이 아닙니다.\n", + "ProblemPatternSchema.regexp": "출력에서 오류, 경고 또는 정보를 찾는 정규식입니다.", + "ProblemPatternSchema.file": "파일 이름의 일치 그룹 인덱스입니다. 생략된 경우 1이 사용됩니다.", + "ProblemPatternSchema.location": "문제 위치의 일치 그룹 인덱스입니다. 유효한 위치 패턴은 (line), (line,column) 및 (startLine,startColumn,endLine,endColumn)입니다. 생략하면 (line,column)이 사용됩니다.", + "ProblemPatternSchema.line": "문제 줄의 일치 그룹 인덱스입니다. 기본값은 2입니다.", + "ProblemPatternSchema.column": "문제의 줄바꿈 문자의 일치 그룹 인덱스입니다. 기본값은 3입니다.", + "ProblemPatternSchema.endLine": "문제 끝 줄의 일치 그룹 인덱스입니다. 기본적으로 정의되지 않습니다.", + "ProblemPatternSchema.endColumn": "문제의 끝 줄바꿈 문자의 일치 그룹 인덱스입니다. 기본값은 정의되지 않았습니다.", + "ProblemPatternSchema.severity": "문제 심각도의 일치 그룹 인덱스입니다. 기본적으로 정의되지 않습니다.", + "ProblemPatternSchema.code": "문제 코드의 일치 그룹 인덱스입니다. 기본적으로 정의되지 않습니다.", + "ProblemPatternSchema.message": "메시지의 일치 그룹 인덱스입니다. 생략된 경우 기본값은 위치가 지정된 경우 4이고, 그렇지 않으면 5입니다.", + "ProblemPatternSchema.loop": "여러 줄 선택기 루프에서는 이 패턴이 일치할 경우 루프에서 패턴을 실행할지 여부를 나타냅니다. 여러 줄 패턴의 마지막 패턴에 대해서만 지정할 수 있습니다.", + "NamedProblemPatternSchema.name": "문제 패턴의 이름입니다.", + "NamedMultiLineProblemPatternSchema.name": "여러 줄 문제 패턴의 이름입니다.", + "NamedMultiLineProblemPatternSchema.patterns": "실제 패턴입니다.", + "ProblemPatternExtPoint": "문제 패턴을 제공합니다.", + "ProblemPatternRegistry.error": "잘못된 문제 패턴입니다. 패턴이 무시됩니다.", + "ProblemMatcherParser.noProblemMatcher": "오류: 설명을 문제 선택기로 변환할 수 없습니다.\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "오류: 설명에서 유효한 문제 패턴을 정의하지 않습니다.\n{0}\n", + "ProblemMatcherParser.noOwner": "오류: 설명에서 소유자를 정의하지 않습니다.\n{0}\n", + "ProblemMatcherParser.noFileLocation": "오류: 설명에서 파일 위치를 정의하지 않습니다.\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "정보: 알 수 없는 심각도 {0}. 유효한 값은 오류, 경고 및 정보입니다.\n", + "ProblemMatcherParser.noDefinedPatter": "오류: 식별자가 {0}인 패턴이 없습니다.", + "ProblemMatcherParser.noIdentifier": "오류: 패턴 속성이 빈 식별자를 참조합니다.", + "ProblemMatcherParser.noValidIdentifier": "오류: 패턴 속성 {0}이(가) 유효한 패턴 변수 이름이 아닙니다.", + "ProblemMatcherParser.problemPattern.watchingMatcher": "문제 검사기에서 감시 시작 패턴과 종료 패턴을 모두 정의해야 합니다.", + "ProblemMatcherParser.invalidRegexp": "오류: {0} 문자열은 유효한 정규식이 아닙니다.\n", + "WatchingPatternSchema.regexp": "백그라운드 작업의 시작 또는 종료를 감지하는 정규식입니다.", + "WatchingPatternSchema.file": "파일 이름의 일치 그룹 인덱스이며 생략할 수 있습니다.", + "PatternTypeSchema.name": "제공되거나 미리 정의된 패턴의 이름", + "PatternTypeSchema.description": "문제 패턴 또는 제공되거나 미리 정의된 문제 패턴의 이름입니다. 기본이 지정된 경우 생략할 수 있습니다.", + "ProblemMatcherSchema.base": "사용할 기본 문제 선택기의 이름입니다.", + "ProblemMatcherSchema.owner": "Code 내부의 문제 소유자입니다. 기본값을 지정한 경우 생략할 수 있습니다. 기본값을 지정하지 않고 생략한 경우 기본값은 '외부'입니다.", + "ProblemMatcherSchema.severity": "캡처 문제에 대한 기본 심각도입니다. 패턴에서 심각도에 대한 일치 그룹을 정의하지 않은 경우에 사용됩니다.", + "ProblemMatcherSchema.applyTo": "텍스트 문서에 복된 문제가 열린 문서, 닫힌 문서 또는 모든 문서에 적용되는지를 제어합니다.", + "ProblemMatcherSchema.fileLocation": "문제 패턴에 보고된 파일 이름을 해석하는 방법을 정의합니다.", + "ProblemMatcherSchema.background": "백그라운드 작업에서 활성 상태인 matcher의 시작과 끝을 추적하는 패턴입니다.", + "ProblemMatcherSchema.background.activeOnStart": "true로 설정한 경우 작업이 시작되면 백그라운드 모니터가 활성 모드로 전환됩니다. 이는 beginPattern과 일치하는 줄을 실행하는 것과 같습니다.", + "ProblemMatcherSchema.background.beginsPattern": "출력이 일치하는 경우 백그라운드 작업을 시작할 때 신호를 받습니다.", + "ProblemMatcherSchema.background.endsPattern": "출력이 일치하는 경우 백그라운드 작업을 끝날 때 신호를 받습니다.", + "ProblemMatcherSchema.watching.deprecated": "조사 속성은 사용되지 않습니다. 백그라운드 속성을 대신 사용하세요.", + "ProblemMatcherSchema.watching": "조사 matcher의 시작과 끝을 추적하는 패턴입니다.", + "ProblemMatcherSchema.watching.activeOnStart": "true로 설정한 경우 작업이 시작되면 선택기가 활성 모드로 전환됩니다. 이는 beginPattern과 일치하는 줄을 실행하는 것과 같습니다.", + "ProblemMatcherSchema.watching.beginsPattern": "출력이 일치하는 경우 조사 작업을 시작할 때 신호를 받습니다.", + "ProblemMatcherSchema.watching.endsPattern": "출력이 일치하는 경우 조사 작업을 끝날 때 신호를 받습니다.", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "이 속성은 사용되지 않습니다. 대신 감시 속성을 사용하세요.", + "LegacyProblemMatcherSchema.watchedBegin": "파일 감시를 통해 트리거되는 감시되는 작업이 시작됨을 나타내는 정규식입니다.", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "이 속성은 사용되지 않습니다. 대신 감시 속성을 사용하세요.", + "LegacyProblemMatcherSchema.watchedEnd": "감시되는 작업이 종료됨을 나타내는 정규식입니다.", + "NamedProblemMatcherSchema.name": "참조를 위한 문제 선택기의 이름입니다.", + "NamedProblemMatcherSchema.label": "사람이 읽을 수 있는 문제 일치기의 레이블입니다.", + "ProblemMatcherExtPoint": "문제 선택기를 제공합니다.", + "msCompile": "Microsoft 컴파일러 문제", + "lessCompile": "문제 적게 보기", + "gulp-tsc": "Gulp TSC 문제", + "jshint": "JSHint 문제", + "jshint-stylish": "JSHint 스타일 문제", + "eslint-compact": "ESLint 컴팩트 문제", + "eslint-stylish": "ESLint 스타일 문제", + "go": "이동 문제" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/message/common/message.i18n.json b/i18n/kor/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..8270222672 --- /dev/null +++ b/i18n/kor/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "닫기", + "later": "나중에", + "cancel": "취소" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/request/node/request.i18n.json b/i18n/kor/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..ada1e6c87e --- /dev/null +++ b/i18n/kor/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "사용할 프록시 설정입니다. 설정되지 않으면 http_proxy 및 https_proxy 환경 변수에서 가져옵니다.", + "strictSSL": "제공된 CA 목록에 대해 프록시 서버 인증서를 확인해야 하는지 여부를 나타냅니다.", + "proxyAuthorization": "모든 네트워크 요청에 대해 'Proxy-Authorization' 헤더로 보낼 값입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/kor/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..3ed9e6572a --- /dev/null +++ b/i18n/kor/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "원격 분석", + "telemetry.enableTelemetry": "사용 데이터와 오류를 Microsoft에 전송할 수 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/kor/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..e733a650bf --- /dev/null +++ b/i18n/kor/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "확장 정의 테마 지정 가능 색을 적용합니다.", + "contributes.color.id": "테마 지정 가능 색의 식별자입니다.", + "contributes.color.id.format": "식별자는 aa[.bb]* 형식이어야 합니다.", + "contributes.color.description": "테마 지정 가능 색에 대한 설명입니다.", + "contributes.defaults.light": "밝은 테마의 기본 색입니다. 16진수의 색 값(#RRGGBB[AA]) 또는 기본값을 제공하는 테마 지정 가능 색의 식별자입니다.", + "contributes.defaults.dark": "어두운 테마의 기본 색입니다. 16진수의 색 값(#RRGGBB[AA]) 또는 기본값을 제공하는 테마 지정 가능 색의 식별자입니다.", + "contributes.defaults.highContrast": "고대비 테마의 기본 색상입니다. 기본값을 제공하는 16진수(#RRGGBB[AA])의 색상 값 또는 테마 지정 가능 색의 식별자입니다.", + "invalid.colorConfiguration": "'configuration.colors'는 배열이어야 합니다.", + "invalid.default.colorType": "{0}은(는) 16진수의 색 값(#RRGGBB[AA] 또는 #RGB[A]) 또는 기본값을 제공하는 테마 지정 가능 색의 식별자입니다.", + "invalid.id": "'configuration.colors.id'를 정의해야 하여 비워 둘 수 없습니다.", + "invalid.id.format": "'configuration.colors.id'는 단어[.word]* 다음에 와야 합니다.", + "invalid.description": "'configuration.colors.description'를 정의해야 하며 비워 둘 수 없습니다.", + "invalid.defaults": "'configuration.colors.defaults'를 정의해야 하며 'light', 'dark' 및 'highContrast'를 포함해야 합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/kor/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..02047c60c1 --- /dev/null +++ b/i18n/kor/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "잘못된 색 형식입니다. #RGB, #RGBA, #RRGGBB 또는 #RRGGBBAA를 사용하세요.", + "schema.colors": "워크벤치에서 사용되는 색입니다.", + "foreground": "전체 전경색입니다. 이 색은 구성 요소에서 재정의하지 않은 경우에만 사용됩니다.", + "errorForeground": "오류 메시지에 대한 전체 전경색입니다. 이 색은 구성 요소에서 재정의하지 않은 경우에만 사용됩니다.", + "descriptionForeground": "레이블과 같이 추가 정보를 제공하는 설명 텍스트의 전경색입니다.", + "focusBorder": "포커스가 있는 요소의 전체 테두리 색입니다. 이 색은 구성 요소에서 재정의하지 않은 경우에만 사용됩니다.", + "contrastBorder": "더 뚜렷이 대비되도록 요소를 다른 요소와 구분하는 요소 주위의 추가 테두리입니다.", + "activeContrastBorder": "더 뚜렷이 대비되도록 요소를 다른 요소와 구분하는 활성 요소 주위의 추가 테두리입니다.", + "selectionBackground": "워크벤치의 텍스트 선택(예: 입력 필드 또는 텍스트 영역) 전경색입니다. 편집기 내의 선택에는 적용되지 않습니다.", + "textSeparatorForeground": "텍스트 구분자 색상입니다.", + "textLinkForeground": "텍스트 내 링크의 전경색입니다.", + "textLinkActiveForeground": "텍스트 내 활성 링크의 전경색입니다.", + "textPreformatForeground": "미리 서식이 지정된 텍스트 세그먼트의 전경색입니다.", + "textBlockQuoteBackground": "텍스트 내 블록 인용의 전경색입니다.", + "textBlockQuoteBorder": "텍스트 내 블록 인용의 테두리 색입니다.", + "textCodeBlockBackground": "텍스트 내 코드 블록의 전경색입니다.", + "widgetShadow": "편집기 내에서 찾기/바꾸기 같은 위젯의 그림자 색입니다.", + "inputBoxBackground": "입력 상자 배경입니다.", + "inputBoxForeground": "입력 상자 전경입니다.", + "inputBoxBorder": "입력 상자 테두리입니다.", + "inputBoxActiveOptionBorder": "입력 필드에서 활성화된 옵션의 테두리 색입니다.", + "inputPlaceholderForeground": "위치 표시자 텍스트에 대한 입력 상자 전경색입니다.", + "inputValidationInfoBackground": "정보 심각도의 입력 유효성 검사 배경색입니다.", + "inputValidationInfoBorder": "정보 심각도의 입력 유효성 검사 테두리 색입니다.", + "inputValidationWarningBackground": "정보 경고의 입력 유효성 검사 배경색입니다.", + "inputValidationWarningBorder": "경고 심각도의 입력 유효성 검사 테두리 색입니다.", + "inputValidationErrorBackground": "오류 심각도의 입력 유효성 검사 배경색입니다.", + "inputValidationErrorBorder": "오류 심각도의 입력 유효성 검사 테두리 색입니다.", + "dropdownBackground": "드롭다운 배경입니다.", + "dropdownForeground": "드롭다운 전경입니다.", + "dropdownBorder": "드롭다운 테두리입니다.", + "listFocusBackground": "목록/트리가 활성 상태인 경우 포커스가 있는 항목의 목록/트리 배경색입니다. 목록/트리가 활성 상태이면 키보드 포커스를 가지며, 비활성 상태이면 포커스가 없습니다.", + "listFocusForeground": "목록/트리가 활성 상태인 경우 포커스가 있는 항목의 목록/트리 전경색입니다. 목록/트리가 활성 상태이면 키보드 포커스를 가지며, 비활성 상태이면 포커스가 없습니다.", + "listActiveSelectionBackground": "목록/트리가 활성 상태인 경우 선택한 항목의 목록/트리 배경색입니다. 목록/트리가 활성 상태이면 키보드 포커스를 가지며, 비활성 상태이면 포커스가 없습니다.", + "listActiveSelectionForeground": "목록/트리가 활성 상태인 경우 선택한 항목의 목록/트리 전경색입니다. 목록/트리가 활성 상태이면 키보드 포커스를 가지며, 비활성 상태이면 포커스가 없습니다.", + "listInactiveSelectionBackground": "목록/트리가 비활성 상태인 경우 선택한 항목의 목록/트리 배경색입니다. 목록/트리가 활성 상태이면 키보드 포커스를 가지며, 비활성 상태이면 포커스가 없습니다.", + "listInactiveSelectionForeground": "목록/트리가 비활성 상태인 경우 선택한 항목의 목록/트리 전경색입니다. 목록/트리가 활성 상태이면 키보드 포커스를 가지며, 비활성 상태이면 포커스가 없습니다.", + "listHoverBackground": "마우스로 항목을 가리킬 때 목록/트리 배경입니다.", + "listHoverForeground": "마우스로 항목을 가리킬 때 목록/트리 전경입니다.", + "listDropBackground": "마우스로 항목을 이동할 때 목록/트리 끌어서 놓기 배경입니다.", + "highlight": "목록/트리 내에서 검색할 때 일치 항목 강조 표시의 목록/트리 전경색입니다.", + "pickerGroupForeground": "그룹화 레이블에 대한 빠른 선택기 색입니다.", + "pickerGroupBorder": "그룹화 테두리에 대한 빠른 선택기 색입니다.", + "buttonForeground": "단추 기본 전경색입니다.", + "buttonBackground": "단추 배경색입니다.", + "buttonHoverBackground": "마우스로 가리킬 때 단추 배경색입니다.", + "badgeBackground": "배지 배경색입니다. 배지는 검색 결과 수와 같은 소량의 정보 레이블입니다.", + "badgeForeground": "배지 전경색입니다. 배지는 검색 결과 수와 같은 소량의 정보 레이블입니다.", + "scrollbarShadow": "스크롤되는 보기를 나타내는 스크롤 막대 그림자입니다.", + "scrollbarSliderBackground": "스크롤 막대 슬라이버 배경색입니다.", + "scrollbarSliderHoverBackground": "마우스로 가리킬 때 스크롤 막대 슬라이더 배경색입니다.", + "scrollbarSliderActiveBackground": "활성 상태일 때 스크롤 막대 슬라이더 배경색입니다.", + "progressBarBackground": "오래 실행 중인 작업에 대해 표시되는 진행률 표시 막대의 배경색입니다.", + "editorBackground": "편집기 배경색입니다.", + "editorForeground": "편집기 기본 전경색입니다.", + "editorWidgetBackground": "찾기/바꾸기 같은 편집기 위젯의 배경색입니다.", + "editorWidgetBorder": "편집기 위젯의 테두리 색입니다. 위젯에 테두리가 있고 위젯이 색상을 무시하지 않을 때만 사용됩니다.", + "editorSelectionBackground": "편집기 선택 영역의 색입니다.", + "editorSelectionForeground": "고대비를 위한 선택 텍스트의 색입니다.", + "editorInactiveSelection": "비활성 편집기 선택 영역의 색입니다.", + "editorSelectionHighlight": "선택 영역과 동일한 콘텐츠가 있는 영역의 색입니다.", + "editorFindMatch": "현재 검색 일치 항목의 색입니다.", + "findMatchHighlight": "기타 검색 일치 항목의 색입니다.", + "findRangeHighlight": "검색을 제한하는 영역의 색을 지정합니다.", + "hoverHighlight": "호버가 표시된 단어 아래를 강조 표시합니다.", + "hoverBackground": "편집기 호버의 배경색.", + "hoverBorder": "편집기 호버의 테두리 색입니다.", + "activeLinkForeground": "활성 링크의 색입니다.", + "diffEditorInserted": "삽입된 텍스트의 배경색입니다.", + "diffEditorRemoved": "제거된 텍스트의 배경색입니다.", + "diffEditorInsertedOutline": "삽입된 텍스트의 윤곽선 색입니다.", + "diffEditorRemovedOutline": "제거된 텍스트의 윤곽선 색입니다.", + "mergeCurrentHeaderBackground": "인라인 병합 충돌의 현재 헤더 배경입니다.", + "mergeCurrentContentBackground": "인라인 병합 충돌의 현재 콘텐츠 배경입니다.", + "mergeIncomingHeaderBackground": "인라인 병합 충돌에서 수신 헤더 배경입니다.", + "mergeIncomingContentBackground": "인라인 병합 충돌에서 수신 콘텐츠 배경입니다.", + "mergeCommonHeaderBackground": "인라인 병합 충돌의 공통 과거 헤더 배경입니다.", + "mergeCommonContentBackground": "인라인 병합 충돌의 공통 과거 콘텐츠 배경입니다.", + "mergeBorder": "인라인 병합 충돌에서 헤더 및 스플리터의 테두리 색입니다.", + "overviewRulerCurrentContentForeground": "인라인 병합 충돌에서 현재 개요 눈금 전경색입니다.", + "overviewRulerIncomingContentForeground": "인라인 병합 충돌에서 수신 개요 눈금 전경색입니다.", + "overviewRulerCommonContentForeground": "인라인 병합 충돌에서 공통 과거 개요 눈금 전경색입니다.", + "overviewRulerFindMatchForeground": "찾기 일치 항목의 개요 눈금자 마커 색입니다.", + "overviewRulerSelectionHighlightForeground": "선택 영역 강조 표시의 개요 눈금자 마커 색입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/kor/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..04a1b3f7e9 --- /dev/null +++ b/i18n/kor/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "코드 작업 영역", + "untitledWorkspace": "제목 없음(작업 영역)", + "workspaceNameVerbose": "{0}(작업 영역)", + "workspaceName": "{0}(작업 영역)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/kor/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..0a41150d7b --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "확장 {0}을(를) {1}(으)로 덮어쓰는 중입니다.", + "extensionUnderDevelopment": "{0}에서 개발 확장 로드 중" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/kor/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..3c89aa476d --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "닫기", + "cancel": "취소", + "ok": "확인" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/kor/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..62b6c1eb23 --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "{0}개의 추가 오류 및 경고를 표시하지 않습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/node/extHostExplorerView.i18n.json b/i18n/kor/src/vs/workbench/api/node/extHostExplorerView.i18n.json new file mode 100644 index 0000000000..a124b8d7a0 --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/node/extHostExplorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "ID가 '{0}'인 등록된 TreeExplorerNodeProvider가 없습니다.", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider '{0}'에서 루트 노드를 제공하지 못했습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/kor/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..0b96059408 --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "확장 `{1}`을(를) 활성화하지 못했습니다. 이유: 알 수 없는 종속성 `{0}`.", + "failedDep1": "확장 `{1}`을(를) 활성화하지 못했습니다. 이유: 종속성 `{0}`이(가) 활성화되지 않았습니다.", + "failedDep2": "확장 `{0}`을(를) 활성화하지 못했습니다. 이유: 종속성 수준이 10개가 넘음(종속성 루프일 가능성이 높음).", + "activationError": "확장 `{0}` 활성화 실패: {1}." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/kor/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json b/i18n/kor/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json new file mode 100644 index 0000000000..7d71b7830d --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "ID가 '{0}'인 등록된 TreeExplorerNodeProvider가 없습니다.", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider '{0}'에서 루트 노드를 제공하지 못했습니다.", + "treeExplorer.failedToResolveChildren": "TreeExplorerNodeProvider '{0}'에서 자식을 확인하지 못했습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/node/extHostTreeView.i18n.json b/i18n/kor/src/vs/workbench/api/node/extHostTreeView.i18n.json new file mode 100644 index 0000000000..a124b8d7a0 --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/node/extHostTreeView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "ID가 '{0}'인 등록된 TreeExplorerNodeProvider가 없습니다.", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider '{0}'에서 루트 노드를 제공하지 못했습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/kor/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..31a81fc010 --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "ID가 '{0}'인 등록된 트리 뷰가 없습니다.", + "treeItem.notFound": "ID가 '{0}'인 트리 항목을 찾을 수 없습니다.", + "treeView.duplicateElement": "{0} 요소가 이미 등록되어 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json b/i18n/kor/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..0a41150d7b --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "확장 {0}을(를) {1}(으)로 덮어쓰는 중입니다.", + "extensionUnderDevelopment": "{0}에서 개발 확장 로드 중" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/api/node/mainThreadMessageService.i18n.json b/i18n/kor/src/vs/workbench/api/node/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..3c89aa476d --- /dev/null +++ b/i18n/kor/src/vs/workbench/api/node/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "닫기", + "cancel": "취소", + "ok": "확인" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..7311317e5a --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "언어 구성", + "displayLanguage": "VSCode의 표시 언어를 정의합니다.", + "doc": "지원되는 언어 목록은 {0} 을(를) 참조하세요.", + "restart": "값을 변경하려면 VSCode를 다시 시작해야 합니다.", + "fail.createSettings": "'{0}'({1})을(를) 만들 수 없습니다.", + "JsonSchema.locale": "사용할 UI 언어입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..1d69ca16db --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "폴더 열기...", + "openFileFolder": "열기..." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..903c9a8ae3 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "작업 막대 표시 유형 전환", + "view": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..06b5697e41 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "편집기 그룹 세로/가로 레이아웃 설정/해제", + "horizontalLayout": "가로 편집기 그룹 레이아웃", + "verticalLayout": "세로 편집기 그룹 레이아웃", + "view": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..e7bea50111 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "사이드바 위치 설정/해제", + "view": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..026aed0ffa --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "사이드바 표시 유형 설정/해제", + "view": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..523bbdda9a --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "상태 표시줄 표시 설정/해제", + "view": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..b34c988fe0 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "Zen 모드 설정/해제", + "view": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..2fe2ffc99a --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "폴더 열기...", + "openFileFolder": "열기...", + "addFolderToWorkspace": "작업 영역에 폴더 추가...", + "add": "추가(&&A)", + "addFolderToWorkspaceTitle": "작업 영역에 폴더 추가", + "newWorkspace": "새 작업 영역...", + "select": "선택(&&S)", + "selectWorkspace": "작업 영역 폴더 선택", + "removeFolderFromWorkspace": "작업 영역에서 폴더 삭제", + "saveWorkspaceAsAction": "작업 영역을 다른 이름으로 저장", + "saveEmptyWorkspaceNotSupported": "저장하려면 먼저 작업 영역을 여세요.", + "save": "저장(&&S)", + "saveWorkspace": "작업 영역 저장", + "openWorkspaceAction": "작업 영역 열기...", + "openWorkspaceConfigFile": "작업 영역 구성 파일 열기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..0a57aa3b46 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "작업 막대에서 제거", + "keepInActivityBar": "작업 막대에 유지", + "titleKeybinding": "{0}({1})", + "additionalViews": "추가 뷰", + "numberBadge": "{0}({1})", + "manageExtension": "확장 관리", + "toggle": "뷰 고정 전환" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..f66aa50d6f --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "작업 막대 숨기기", + "activityBarAriaLabel": "활성 뷰 전환기", + "globalActions": "전역 작업" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..7dc53d8d83 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "{0} 동작", + "titleTooltip": "{0}({1})" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..1e00d99b4b --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "이진 뷰어" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..8cc08b7a6f --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "텍스트 편집기", + "textDiffEditor": "텍스트 Diff 편집기", + "binaryDiffEditor": "이진 Diff 편집기", + "sideBySideEditor": "병렬 편집기", + "groupOnePicker": "첫 번째 그룹에 편집기 표시", + "groupTwoPicker": "두 번째 그룹에 편집기 표시", + "groupThreePicker": "세 번째 그룹에 편집기 표시", + "allEditorsPicker": "열려 있는 모든 편집기 표시", + "view": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..a6be57cfa6 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "편집기 분할", + "joinTwoGroups": "두 그룹의 편집기 조인", + "navigateEditorGroups": "편집기 그룹 간 탐색", + "focusActiveEditorGroup": "활성 편집기 그룹에 포커스", + "focusFirstEditorGroup": "첫 번째 편집기 그룹에 포커스", + "focusSecondEditorGroup": "두 번째 편집기 그룹에 포커스", + "focusThirdEditorGroup": "세 번째 편집기 그룹에 포커스", + "focusPreviousGroup": "이전 그룹에 포커스", + "focusNextGroup": "다음 그룹에 포커스", + "openToSide": "측면에서 열기", + "closeEditor": "편집기 닫기", + "revertAndCloseActiveEditor": "편집기 되돌리기 및 닫기", + "closeEditorsToTheLeft": "왼쪽에 있는 편집기 닫기", + "closeEditorsToTheRight": "오른쪽에 있는 편집기 닫기", + "closeAllEditors": "모든 편집기 닫기", + "closeUnmodifiedEditors": "그룹의 수정되지 않은 편집기 닫기", + "closeEditorsInOtherGroups": "다른 그룹의 편집기 닫기", + "closeOtherEditorsInGroup": "다른 편집기 닫기", + "closeEditorsInGroup": "그룹의 모든 편집기 닫기", + "moveActiveGroupLeft": "편집기 그룹을 왼쪽으로 이동", + "moveActiveGroupRight": "편집기 그룹을 오른쪽으로 이동", + "minimizeOtherEditorGroups": "다른 편집기 그룹 최소화", + "evenEditorGroups": "균등한 편집기 그룹 너비", + "maximizeEditor": "편집기 그룹 최대화 및 사이드바 숨기기", + "keepEditor": "편집기 유지", + "openNextEditor": "다음 편집기 열기", + "openPreviousEditor": "이전 편집기 열기", + "nextEditorInGroup": "그룹에서 다음 편집기 열기", + "openPreviousEditorInGroup": "그룹에서 이전 편집기 열기", + "navigateNext": "앞으로 이동", + "navigatePrevious": "뒤로 이동", + "reopenClosedEditor": "닫힌 편집기 다시 열기", + "clearRecentFiles": "최근 사용 항목 지우기", + "showEditorsInFirstGroup": "첫 번째 그룹에 편집기 표시", + "showEditorsInSecondGroup": "두 번째 그룹에 편집기 표시", + "showEditorsInThirdGroup": "세 번째 그룹에 편집기 표시", + "showEditorsInGroup": "그룹의 편집기 표시", + "showAllEditors": "모든 편집기 표시", + "openPreviousRecentlyUsedEditorInGroup": "그룹에서 최근에 사용한 이전 편집기 열기", + "openNextRecentlyUsedEditorInGroup": "그룹에서 최근에 사용한 다음 편집기 열기", + "navigateEditorHistoryByInput": "기록에서 이전 편집기 열기", + "openNextRecentlyUsedEditor": "최근에 사용한 다음 편집기 열기", + "openPreviousRecentlyUsedEditor": "최근에 사용한 이전 편집기 열기", + "clearEditorHistory": "편집기 기록 지우기", + "focusLastEditorInStack": "그룹의 마지막 편집기 열기", + "moveEditorLeft": "왼쪽으로 편집기 이동", + "moveEditorRight": "오른쪽으로 편집기 이동", + "moveEditorToPreviousGroup": "편집기를 이전 그룹으로 이동", + "moveEditorToNextGroup": "편집기를 다음 그룹으로 이동" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..5c728fa9c9 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "활성 편집기를 탭 또는 그룹 단위로 이동", + "editorCommand.activeEditorMove.arg.name": "활성 편집기 이동 인수", + "editorCommand.activeEditorMove.arg.description": "인수 속성:\n\t\t\t\t\t\t* 'to': 이동할 위치를 지정하는 문자열 값입니다.\n\t\t\t\t\t\t* 'by': 이동할 단위를 지정하는 문자열 값입니다. 탭 단위 또는 그룹 단위입니다\n\t\t\t\t\t\t* 'value': 이동할 위치 수 또는 절대 위치를 지정하는 숫자 값입니다.\n\t\t\t\t\t", + "commandDeprecated": "**{0}** 명령이 제거되었습니다. 대신 **{1}** 명령을 사용할 수 있습니다.", + "openKeybindings": "바로 가기 키 구성" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..45855c7783 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "왼쪽", + "groupTwoVertical": "가운데", + "groupThreeVertical": "오른쪽", + "groupOneHorizontal": "위쪽", + "groupTwoHorizontal": "가운데", + "groupThreeHorizontal": "아래쪽", + "editorOpenError": "'{0}'을(를) 열 수 없습니다. {1}." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..0d044cede6 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, 편집기 그룹 선택기", + "groupLabel": "그룹: {0}", + "noResultsFoundInGroup": "그룹에서 일치하는 열려 있는 편집기를 찾을 수 없습니다.", + "noOpenedEditors": "열려 있는 편집기 목록이 현재 그룹에서 비어 있습니다.", + "noResultsFound": "일치하는 열려 있는 편집기를 찾을 수 없습니다.", + "noOpenedEditorsAllGroups": "열려 있는 편집기 목록이 현재 비어 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..d4db41b731 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "줄 {0}, 열 {1}({2} 선택됨)", + "singleSelection": "줄 {0}, 열 {1}", + "multiSelectionRange": "{0} 선택 항목({1}자 선택됨)", + "multiSelection": "{0} 선택 항목", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "Tab으로 포커스 이동", + "screenReaderDetected": "화면 읽기 프로그램 검색됨", + "screenReaderDetectedExtra": "화면 읽기 프로그램을 사용하지 않는 경우 `editor.accessibilitySupport` 설정을 \"off\"로 변경하세요.", + "disableTabMode": "접근성 모드 사용 안 함", + "gotoLine": "줄 이동", + "indentation": "들여쓰기", + "selectEncoding": "인코딩 선택", + "selectEOL": "줄 시퀀스의 끝 선택", + "selectLanguageMode": "언어 모드 선택", + "fileInfo": "파일 정보", + "spacesSize": "공백: {0}", + "tabSize": "Tab 크기: {0}", + "showLanguageExtensions": "'{0}'의 마켓플레이스 확장 검색...", + "changeMode": "언어 모드 변경", + "noEditor": "현재 활성 텍스트 편집기 없음", + "languageDescription": "({0}) - 구성된 언어", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "언어(식별자)", + "configureModeSettings": "'{0}' 언어 기반 설정 구성...", + "configureAssociationsExt": "'{0}'에 대한 파일 연결 구성...", + "autoDetect": "자동 감지", + "pickLanguage": "언어 모드 선택", + "currentAssociation": "현재 연결", + "pickLanguageToConfigure": "'{0}'과(와) 연결할 언어 모드 선택", + "changeIndentation": "ID 변경", + "noWritableCodeEditor": "활성 코드 편집기는 읽기 전용입니다.", + "indentView": "보기 변경", + "indentConvert": "파일 변환", + "pickAction": "작업 선택", + "changeEndOfLine": "줄 시퀀스의 끝 변경", + "pickEndOfLine": "줄 시퀀스의 끝 선택", + "changeEncoding": "파일 인코딩 변경", + "noFileEditor": "현재 활성 파일 없음", + "saveWithEncoding": "인코딩하여 저장", + "reopenWithEncoding": "인코딩하여 다시 열기", + "guessedEncoding": "콘텐츠에서 추측함", + "pickEncodingForReopen": "파일을 다시 열 파일 인코딩 선택", + "pickEncodingForSave": "파일을 저장할 파일 인코딩 선택" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..0c8e7fecab --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "탭 작업" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..9054a6a1f0 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "텍스트 Diff 편집기", + "readonlyEditorWithInputAriaLabel": "{0}. 읽기 전용 텍스트 비교 편집기입니다.", + "readonlyEditorAriaLabel": "읽기 전용 텍스트 비교 편집기입니다.", + "editableEditorWithInputAriaLabel": "{0}. 텍스트 파일 비교 편집기입니다.", + "editableEditorAriaLabel": "텍스트 파일 비교 편집기입니다.", + "navigate.next.label": "다음 변경 내용", + "navigate.prev.label": "이전 변경 내용", + "inlineDiffLabel": "인라인 보기로 전환", + "sideBySideDiffLabel": "세로 정렬 보기로 전환" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..debf692a4b --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0}, 그룹 {1}." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..1b3fc330a7 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "텍스트 편집기", + "readonlyEditorWithInputAriaLabel": "{0}. 읽기 전용 텍스트 편집기입니다.", + "readonlyEditorAriaLabel": "읽기 전용 텍스트 편집기입니다.", + "untitledFileEditorWithInputAriaLabel": "{0}. 제목 없는 파일 텍스트 편집기입니다.", + "untitledFileEditorAriaLabel": "제목 없는 파일 텍스트 편집기입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..07fc9a53b5 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "닫기", + "closeOthers": "기타 항목 닫기", + "closeRight": "오른쪽에 있는 항목 닫기", + "closeAll": "모두 닫기", + "closeAllUnmodified": "미수정 항목 닫기", + "keepOpen": "열린 상태 유지", + "showOpenedEditors": "열려 있는 편집기 표시", + "araLabelEditorActions": "편집기 작업" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..ce7ec96746 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0}({1})", + "closePanel": "패널 닫기", + "togglePanel": "패널 설정/해제", + "focusPanel": "패널로 포커스 이동", + "toggleMaximizedPanel": "최대화된 패널 설정/해제", + "maximizePanel": "패널 크기 최대화", + "minimizePanel": "패널 크기 복원", + "view": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..b21b38d84e --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "활성 패널 전환기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..d993f13f03 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0}(확인하려면 'Enter' 키를 누르고, 취소하려면 'Escape' 키를 누름)", + "inputModeEntry": "입력을 확인하려면 'Enter' 키를 누르고, 취소하려면 'Esc' 키를 누르세요.", + "emptyPicks": "선택할 항목이 없습니다.", + "quickOpenInput": "'?'를 입력하면 여기에서 수행할 수 있는 작업에 대한 도움말을 확인할 수 있습니다.", + "historyMatches": "최근에 사용한 항목", + "noResultsFound1": "결과 없음", + "canNotRunPlaceholder": "이 Quick Open 처리기는 현재 컨텍스트에서 사용할 수 없습니다.", + "entryAriaLabel": "{0}, 최근에 사용한 항목", + "removeFromEditorHistory": "기록에서 제거", + "pickHistory": "기록에서 제거할 편집기 항목 선택" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..8a7061cd31 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "파일로 이동...", + "quickNavigateNext": "Quick Open에서 다음 탐색", + "quickNavigatePrevious": "Quick Open에서 이전 탐색", + "quickSelectNext": "Quick Open에서 다음 선택", + "quickSelectPrevious": "Quick Open에서 이전 선택" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..8a7061cd31 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "파일로 이동...", + "quickNavigateNext": "Quick Open에서 다음 탐색", + "quickNavigatePrevious": "Quick Open에서 이전 탐색", + "quickSelectNext": "Quick Open에서 다음 선택", + "quickSelectPrevious": "Quick Open에서 이전 선택" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..0a0ee77947 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "사이드 막대 숨기기", + "focusSideBar": "사이드바에 포커스", + "viewCategory": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..d06ad9713e --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "'{0}' 명령은 현재 사용하도록 설정되지 않아 실행할 수 없습니다.", + "manageExtension": "확장 관리" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..7397b4b59b --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[지원되지 않음]", + "devExtensionWindowTitlePrefix": "[확장 개발 호스트]" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/quickopen.i18n.json b/i18n/kor/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..1b192e66e9 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "일치하는 결과 없음", + "noResultsFound2": "결과 없음", + "entryAriaLabel": "{0}, 명령" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/viewlet.i18n.json b/i18n/kor/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..129be5f1ea --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "모두 축소" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/common/theme.i18n.json b/i18n/kor/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..79f98474da --- /dev/null +++ b/i18n/kor/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "활성 탭 배경색입니다. 탭은 편집기 영역에서 편집기의 컨테이너입니다. 한 편집기 그룹에서 여러 탭을 열 수 있습니다. 여러 편집기 그룹이 있을 수 있습니다.", + "tabInactiveBackground": "비활성 탭 배경색입니다. 탭은 편집기 영역에서 편집기의 컨테이너입니다. 한 편집기 그룹에서 여러 탭을 열 수 있습니다. 여러 편집기 그룹이 있을 수 있습니다.", + "tabBorder": "탭을 서로 구분하기 위한 테두리입니다. 탭은 편집기 영역에서 편집기의 컨테이너입니다. 한 편집기 그룹에 여러 탭을 열 수 있습니다. 여러 편집기 그룹이 있을 수 있습니다.", + "tabActiveBorder": "활성 탭을 강조 표시하기 위한 테두리입니다. 탭은 편집기 영역에서 편집기의 컨테이너입니다. 한 편집기 그룹에 여러 탭을 열 수 있습니다. 여러 편집기 그룹이 있을 수 있습니다.", + "tabActiveUnfocusedBorder": "포커스가 없는 그룹에서 활성 탭을 강조 표시하기 위한 테두리입니다. 탭은 편집기 영역에서 편집기의 컨테이너입니다. 한 편집기 그룹에 여러 탭을 열 수 있습니다. 여러 편집기 그룹이 있을 수 있습니다.", + "tabActiveForeground": "활성 그룹의 활성 탭 전경색입니다. 탭은 편집기 영역에서 편집기의 컨테이너입니다. 한 편집기 그룹에서 여러 탭을 열 수 있습니다. 여러 편집기 그룹이 있을 수 있습니다.", + "tabInactiveForeground": "활성 그룹의 비활성 탭 전경색입니다. 탭은 편집기 영역에서 편집기의 컨테이너입니다. 한 편집기 그룹에서 여러 탭을 열 수 있습니다. 여러 편집기 그룹이 있을 수 있습니다.", + "tabUnfocusedActiveForeground": "포커스가 없는 그룹의 활성 탭 전경색입니다. 탭은 편집기 영역에서 편집기의 컨테이너입니다. 한 편집기 그룹에서 여러 탭을 열 수 있습니다. 여러 편집기 그룹이 있을 수 있습니다.", + "tabUnfocusedInactiveForeground": "포커스가 없는 그룹의 비활성 탭 전경색입니다. 탭은 편집기 영역에서 편집기의 컨테이너입니다. 한 편집기 그룹에서 여러 탭을 열 수 있습니다. 여러 편집기 그룹이 있을 수 있습니다.", + "editorGroupBackground": "편집기 그룹의 배경색입니다. 편집기 그룹은 편집기의 컨테이너입니다. 배경색은 편집기 그룹을 끌 때 표시됩니다.", + "tabsContainerBackground": "탭을 사용도록 설정한 경우 편집기 그룹 제목 머리글의 배경색입니다. 편집기 그룹은 편집기의 컨테이너입니다.", + "tabsContainerBorder": "탭을 사용하도록 설정한 경우 편집기 그룹 제목 머리글의 테두리 색입니다. 편집기 그룹은 편집기의 컨테이너입니다.", + "editorGroupHeaderBackground": "탭을 사용하지 않도록 설정한 경우 편집기 그룹 제목 머리글의 배경색입니다. 편집기 그룹은 편집기의 컨테이너입니다.", + "editorGroupBorder": "여러 편집기 그룹을 서로 구분하기 위한 색입니다. 편집기 그룹은 편집기의 컨테이너입니다.", + "editorDragAndDropBackground": "편집기를 끌 때 배경색입니다. 편집기 내용이 계속 비추어 보이도록 이 색은 투명해야 합니다.", + "panelBackground": "패널 배경색입니다. 패널은 편집기 영역 아래에 표시되며 출력 및 통합 터미널 같은 보기가 포함됩니다.", + "panelBorder": "편집기와 구분되는 맨 위의 패널 테두리 색입니다. 패널은 편집기 영역 아래에 표시되며 출력 및 통합 터미널 같은 보기가 포함됩니다.", + "panelActiveTitleForeground": "활성 패널의 제목 색입니다. 패널은 편집기 영역 아래에 표시되며 출력 및 통합 터미널 같은 보기가 포함됩니다.", + "panelInactiveTitleForeground": "비활성 패널의 제목 색입니다. 패널은 편집기 영역 아래에 표시되며 출력 및 통합 터미널 같은 보기가 포함됩니다.", + "panelActiveTitleBorder": "활성 패널 제목의 테두리 색입니다. 패널은 편집기 영역 아래에 표시되며 출력 및 통합 터미널 같은 보기가 포함됩니다.", + "statusBarForeground": "작업 영역이 열려 있을 때의 상태 표시줄 전경색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarNoFolderForeground": "폴더가 열리지 않았을 때의 상태 표시줄 전경색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarBackground": "작업 영역이 열려 있을 때의 상태 표시줄 배경색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarNoFolderBackground": "폴더가 열리지 않았을 때의 상태 표시줄 배경색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarBorder": "사이드바 및 편집기와 구분하는 상태 표시줄 테두리 색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarNoFolderBorder": "열린 폴더가 없을 때 사이드바 및 편집기와 구분하는 상태 표시줄 테두리 색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarItemActiveBackground": "클릭할 때의 상태 표시줄 항목 배경색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarItemHoverBackground": "마우스로 가리킬 때의 상태 표시줄 항목 배경색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarProminentItemBackground": "상태 표시줄 주요 항목 배경색입니다. 주요 항목은 중요도를 나타내는 다른 상태 표시줄 항목보다 눈에 잘 띕니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarProminentItemHoverBackground": "마우스로 가리킬 때의 상태 표시줄 주요 항목 배경색입니다. 주요 항목은 중요도를를 나타내는 다른 상태 표시줄 항목보다 눈에 잘 띕니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "activityBarBackground": "작업 막대 배경색입니다. 작업 막대는 맨 왼쪽이나 오른쪽에 표시되며 사이드바의 뷰 간을 전환하는 데 사용할 수 있습니다.", + "activityBarForeground": "작업 막대 전경 색(예: 아이콘에 사용됨)입니다. 작업 막대는 오른쪽이나 왼쪽 끝에 표시되며 사이드바의 보기 간을 전환할 수 있습니다.", + "activityBarBorder": "사이드바와 구분하는 작업 막대 테두리색입니다. 작업 막대는 오른쪽이나 왼쪽 끝에 표시되며 사이드바의 보기 간을 전환할 수 있습니다.", + "activityBarDragAndDropBackground": "작업 막대 항목의 끌어서 놓기 피드백 색입니다. 작업 막대 항목이 계속 비추어 보이도록 이 색은 투명해야 합니다. 작업 막대는 왼쪽이나 오른쪽 끝에 표시되며 사이드바의 보기를 전환할 수 있습니다.", + "activityBarBadgeBackground": "활동 알림 배지 배경색입니다. 작업 막대는 왼쪽이나 오른쪽 끝에 표시되며 사이드바의 보기를 전환할 수 있습니다.", + "activityBarBadgeForeground": "활동 알림 배지 전경색입니다. 작업 막대는 왼쪽이나 오른쪽 끝에 표시되며 사이드바의 보기를 전환할 수 있습니다.", + "sideBarBackground": "사이드바 배경색입니다. 사이드바는 탐색기 및 검색과 같은 뷰의 컨테이너입니다.", + "sideBarForeground": "사이드바 전경색입니다. 사이드바는 탐색기 및 검색과 같은 뷰의 컨테이너입니다.", + "sideBarBorder": "편집기와 구분하는 측면의 사이드바 테두리 색입니다. 사이드바는 탐색기 및 검색과 같은 뷰의 컨테이너입니다.", + "sideBarTitleForeground": "사이드바 제목 전경색입니다. 사이드바는 탐색기 및 검색과 같은 뷰의 컨테이너입니다.", + "sideBarDragAndDropBackground": "사이드 막대 섹션의 끌어서 놓기 피드백 색입니다. 사이드 막대 섹션이 계속 비추어 보이도록 이 색은 투명해야 합니다. 사이드 막대는 왼쪽이나 오른쪽 끝에 표시되며 사이드바의 보기를 전환할 수 있습니다.", + "sideBarSectionHeaderBackground": "사이드바 섹션 헤더 배경색입니다. 사이드바는 탐색기 및 검색과 같은 뷰의 컨테이너입니다.", + "sideBarSectionHeaderForeground": "사이드바 섹션 헤더 전경색입니다. 사이드바는 탐색기 및 검색과 같은 뷰의 컨테이너입니다.", + "titleBarActiveForeground": "창이 활성화된 경우의 제목 표시줄 전경입니다. 이 색은 현재 macOS에서만 지원됩니다.", + "titleBarInactiveForeground": "창이 비활성화된 경우의 제목 표시줄 전경입니다. 이 색은 현재 macOS에서만 지원됩니다.", + "titleBarActiveBackground": "창을 활성화할 때의 제목 표시줄 전경입니다. 이 색은 현재 macOS에서만 지원됩니다.", + "titleBarInactiveBackground": "창이 비활성화된 경우의 제목 표시줄 배경입니다. 이 색은 현재 macOS에서만 지원됩니다.", + "titleBarBorder": "제목 막대 테두리 색입니다. 현재 이 색상은 macOS에서만 지원됩니다.", + "notificationsForeground": "알림 전경색입니다. 알림은 창의 위쪽에 표시됩니다.", + "notificationsBackground": "알림 배경색입니다. 알림은 창의 위쪽에 표시됩니다.", + "notificationsButtonBackground": "알림 단추 배경색입니다. 알림은 창의 위쪽에 표시됩니다.", + "notificationsButtonHoverBackground": "떠 있는 알림 단추 배경색입니다. 알림은 창의 위쪽에 표시됩니다.", + "notificationsButtonForeground": "알림 단추 전경색입니다. 알림은 창의 위쪽에 표시됩니다.", + "notificationsInfoBackground": "알림 정보 배경색입니다. 알림은 창의 위쪽에 표시됩니다.", + "notificationsInfoForeground": "알림 정보 전경색입니다. 알림은 창의 위쪽에 표시됩니다.", + "notificationsWarningBackground": "알림 경고 배경색입니다. 알림은 창의 위쪽에 표시됩니다.", + "notificationsWarningForeground": "알림 경고 전경색입니다. 알림은 창의 위쪽에 표시됩니다.", + "notificationsErrorBackground": "알림 오류 배경색입니다. 알림은 창의 위쪽에 표시됩니다.", + "notificationsErrorForeground": "알림 오류 전경색입니다. 알림은 창의 위쪽에 표시됩니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/kor/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..4888ae199b --- /dev/null +++ b/i18n/kor/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "편집기 닫기", + "closeWindow": "창 닫기", + "closeWorkspace": "작업 영역 닫기", + "noWorkspaceOpened": "현재 이 인스턴스에 열려 있는 작업 영역이 없습니다.", + "newWindow": "새 창", + "toggleFullScreen": "전체 화면 설정/해제", + "toggleMenuBar": "메뉴 모음 설정/해제", + "toggleDevTools": "개발자 도구 설정/해제", + "zoomIn": "확대", + "zoomOut": "축소", + "zoomReset": "확대/축소 다시 설정", + "appPerf": "시작 성능", + "reloadWindow": "창 다시 로드", + "switchWindowPlaceHolder": "전환할 창 선택", + "current": "현재 창", + "close": "창 닫기", + "switchWindow": "창 전환", + "quickSwitchWindow": "빠른 창 전환", + "workspaces": "작업 영역", + "files": "파일", + "openRecentPlaceHolderMac": "선택하여 열기(새 창에서 열려면 Cmd 키를 길게 누름)", + "openRecentPlaceHolder": "선택하여 열기(새 창에서 열려면 Ctrl 키를 길게 누름)", + "remove": "최근에 사용한 항목에서 제거", + "openRecent": "최근 항목 열기...", + "quickOpenRecent": "빠른 최근 항목 열기...", + "closeMessages": "알림 메시지 닫기", + "reportIssues": "문제 보고", + "reportPerformanceIssue": "성능 문제 보고", + "keybindingsReference": "바로 가기 키 참조", + "openDocumentationUrl": "설명서", + "openIntroductoryVideosUrl": "소개 비디오", + "openTipsAndTricksUrl": "팁과 요령", + "toggleSharedProcess": "공유 프로세스 설정/해제", + "navigateLeft": "뷰 왼쪽으로 이동", + "navigateRight": "뷰 오른쪽으로 이동", + "navigateUp": "뷰 위로 이동", + "navigateDown": "뷰 아래로 이동", + "increaseViewSize": "현재 뷰 크기 늘리기", + "decreaseViewSize": "현재 뷰 크기 줄이기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/kor/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/kor/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/electron-browser/crashReporter.i18n.json b/i18n/kor/src/vs/workbench/electron-browser/crashReporter.i18n.json new file mode 100644 index 0000000000..c5d932c26d --- /dev/null +++ b/i18n/kor/src/vs/workbench/electron-browser/crashReporter.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "원격 분석", + "telemetry.enableCrashReporting": "충돌 보고서를 Microsoft에 전송할 수 있도록 설정합니다.\n이 옵션을 적용하려면 다시 시작해야 합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/kor/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..07207ea69c --- /dev/null +++ b/i18n/kor/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "확장 호스트가 10초 내에 시작되지 않았습니다. 첫 번째 줄에서 중지되었을 수 있습니다. 계속하려면 디버거가 필요합니다.", + "extensionHostProcess.startupFail": "확장 호스트가 10초 이내에 시작되지 않았습니다. 문제가 발생했을 수 있습니다.", + "extensionHostProcess.error": "확장 호스트에서 오류 발생: {0}", + "devTools": "개발자 도구", + "extensionHostProcess.crash": "확장 호스트가 예기치 않게 종료되었습니다. 복구하려면 창을 다시 로드하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/kor/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..6a11e18420 --- /dev/null +++ b/i18n/kor/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "보기", + "help": "도움말", + "file": "파일", + "workspaces": "작업 영역", + "developer": "개발자", + "showEditorTabs": "열려 있는 편집기를 탭에서 표시할지 여부를 제어합니다.", + "editorTabCloseButton": "편집기의 탭 닫기 단추의 위치를 제어하거나 'off'로 설정된 경우 이 단추를 사용하지 않도록 설정합니다.", + "showIcons": "열린 편집기를 아이콘과 함께 표시할지 여부를 제어합니다. 이를 위해서는 아이콘 테마도 사용하도록 설정해야 합니다.", + "enablePreview": "열려 있는 편집기를 미리 보기로 표시할지 여부를 제어합니다. 미리 보기 편집기는 유지된 상태까지(예: 두 번 클릭 또는 편집을 통해) 다시 사용됩니다.", + "enablePreviewFromQuickOpen": "Quick Open에서 연 편집기를 미리 보기로 표시할지 여부를 제어합니다. 미리 보기 편집기는 유지된 상태까지(예: 두 번 클릭 또는 편집을 통해) 다시 사용됩니다.", + "editorOpenPositioning": "편집기가 열리는 위치를 제어합니다. 현재 활성 편집기의 왼쪽 또는 오른쪽에서 편집기를 열려면 '왼쪽' 또는 '오른쪽'을 선택합니다. 현재 활성 편집기와 독립적으로 편집기를 열려면 '처음' 또는 '마지막'을 선택합니다.", + "revealIfOpen": "편집기를 여는 경우 보이는 그룹 중 하나에 표시할지 여부를 제어합니다. 사용하지 않도록 설정할 경우 편집기가 기본적으로 현재 활성 편집기 그룹에 열립니다. 사용하도록 설정할 경우 현재 활성 편집기 그룹에서 편집기가 다시 열리지 않고 이미 열린 편집기가 표시됩니다. 강제로 편집기가 특정 그룹에서 열리거나 현재 활성 그룹 옆에 열리도록 하는 등의 일부 경우에는 이 설정이 무시됩니다.", + "commandHistory": "명령 팔레트 기록에 최근 사용 명령을 몇 개 유지할지 결정합니다. 0으로 설정하면 명령 기록을 사용하지 않습니다.", + "preserveInput": "다음에 열 때 마지막으로 명령 팔레트에 입력한 내용을 복원할지 결정합니다.", + "closeOnFocusLost": "Quick Open가 포커스를 잃으면 자동으로 닫을지 여부를 제어합니다.", + "openDefaultSettings": "설정을 열면 모든 기본 설정을 표시하는 편집기도 열리는지 여부를 제어합니다.", + "sideBarLocation": "사이드바의 위치를 제어합니다. 워크벤치의 왼쪽이나 오른쪽에 표시될 수 있습니다.", + "statusBarVisibility": "워크벤치 아래쪽에서 상태 표시줄의 표시 유형을 제어합니다.", + "activityBarVisibility": "워크벤치에서 작업 막대의 표시 유형을 제어합니다.", + "closeOnFileDelete": "일부 다른 프로세스에서 파일을 삭제하거나 이름을 바꿀 때 파일을 표시하는 편집기를 자동으로 닫을지 여부를 제어합니다. 사용하지 않도록 설정하는 경우 이러한 이벤트가 발생하면 편집기가 더티 상태로 계속 열려 있습니다. 응용 프로그램 내에서 삭제하면 항상 편집기가 닫히고 데이터를 유지하기 위해 더티 파일은 닫히지 않습니다.", + "fontAliasing": "워크벤치에서 글꼴 앨리어싱 방식을 제어합니다.\n- 기본: 서브 픽셀 글꼴 다듬기. 대부분의 일반 디스플레이에서 가장 선명한 글꼴 제공\n- 안티앨리어싱: 서브 픽셀이 아닌 픽셀 단위에서 글꼴 다듬기. 전반적으로 더 밝은 느낌을 줄 수 있음\n- 없음: 글꼴 다듬기 사용 안 함. 텍스트 모서리가 각지게 표시됨", + "workbench.fontAliasing.default": "서브 픽셀 글꼴 다듬기. 대부분의 일반 디스플레이에서 가장 선명한 텍스트를 제공합니다. ", + "workbench.fontAliasing.antialiased": "서브 픽셀이 아닌 픽셀 수준에서 글꼴을 다듬습니다. 전반적으로 글꼴이 더 밝게 표시됩니다.", + "workbench.fontAliasing.none": "글꼴 다듬기를 사용하지 않습니다. 텍스트 가장자리가 각지게 표시됩니다.", + "swipeToNavigate": "세 손가락으로 가로로 살짝 밀어 열려 있는 파일 간을 이동합니다.", + "workbenchConfigurationTitle": "워크벤치", + "window.openFilesInNewWindow.on": "파일이 새 창에서 열립니다.", + "window.openFilesInNewWindow.off": "파일이 파일의 폴더가 열려 있는 창 또는 마지막 활성 창에서 열립니다.", + "window.openFilesInNewWindow.default": "Dock 또는 Finder(macOS 전용)를 통해 파일을 연 경우를 제외하고 파일이 파일의 폴더가 열린 창 또는 마지막 활성 창에서 열립니다.", + "openFilesInNewWindow": "파일을 새 창에서 열지 여부를 제어합니다.\n- default: Dock 또는 Finder(macOS 전용)를 통해 파일을 연 경우를 제외하고, 파일이 파일의 폴더가 열린 창 또는 마지막 활성 창에서 열립니다.\n- on: 파일이 새 창에서 열립니다.\n- off: 파일이 파일의 폴더가 열린 창 또는 마지막 활성 창에서 열립니다.\n이 설정이 무시되는 경우도 있을 수 있습니다(예: -new-window 또는 -reuse-window 명령줄 옵션을 사용할 경우).", + "window.openFoldersInNewWindow.on": "폴더가 새 창에서 열립니다.", + "window.openFoldersInNewWindow.off": "폴더가 마지막 활성 창을 바꿉니다.", + "window.openFoldersInNewWindow.default": "폴더를 응용 프로그램 내에서 선택(예: 파일 메뉴를 통해)하는 경우를 제외하고 폴더가 새 창에서 열립니다.", + "openFoldersInNewWindow": "폴더를 새 창에서 열지, 마지막 활성 창과 바꿀지를 제어합니다.\n- default: 응용 프로그램 내에서 [파일] 메뉴 등을 통해 폴더를 선택하는 경우를 제외하고, 폴더는 새 창에서 열립니다.\n- on: 폴더가 새 창에서 열립니다.\n- off: 폴더가 마지막 활성 창을 대체합니다\n이 설정이 무시되는 경우도 있을 수 있습니다(예: -new-window 또는 -reuse-window 명령줄 옵션을 사용할 경우).", + "window.reopenFolders.all": "모든 창을 다시 엽니다.", + "window.reopenFolders.folders": "모든 폴더를 다시 엽니다. 빈 작업 영역은 복원되지 않습니다.", + "window.reopenFolders.one": "마지막 활성 창을 다시 엽니다.", + "window.reopenFolders.none": "창을 다시 열지 않고 항상 빈 창으로 시작합니다.", + "restoreWindows": "다시 시작한 이후에 창을 다시 여는 방법을 설정합니다. 'none'을 선택하면 항상 빈 작업 영역으로 시작하고 'one'을 선택하면 마지막으로 작업한 창이 다시 열리고 'folders'를 선택하면 열었던 폴더가 포함된 모든 창이 다시 열리며 'all'을 선택하면 지난 세션의 모든 창이 다시 열립니다.", + "restoreFullscreen": "창이 전체 화면 모드에서 종료된 경우 창을 전체 화면 모드로 복원할지 여부를 제어합니다.", + "zoomLevel": "창의 확대/축소 수준을 조정합니다. 원래 크기는 0이고 각 상한 증분(예: 1) 또는 하한 증분(예: -1)은 20% 더 크거나 더 작게 확대/축소하는 것을 나타냅니다. 10진수를 입력하여 확대/축소 수준을 세부적으로 조정할 수도 있습니다.", + "title": "활성 편집기를 기반으로 창 제목을 제어합니다. 변수는 컨텍스트를 기반으로 대체됩니다. \n${activeEditorShort}: 예: myFile.txt\n${activeEditorMedium}: 예: myFolder/myFile.txt\n${activeEditorLong}: 예: /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: 예: myFolder\n${folderPath}: 예: /Users/Development/myFolder\n${rootName}: 예: myFolder1, myFolder2, myFolder3\n${rootPath}: 예: /Users/Development/myWorkspace\n${appName}: 예: VS Code\n${dirty}: 활성 편집기가 더티인 경우 더티 표시기\n${separator}: 값이 있는 변수로 둘러싸인 경우에만 표시되는 조건부 구분 기호(\" - \")", + "window.newWindowDimensions.default": "화면 가운데에서 새 창을 엽니다.", + "window.newWindowDimensions.inherit": "마지막 활성 창과 동일한 크기로 새 창을 엽니다.", + "window.newWindowDimensions.maximized": "최대화된 새 창을 엽니다.", + "window.newWindowDimensions.fullscreen": "전체 화면 모드에서 새 창을 엽니다.", + "newWindowDimensions": "하나 이상의 창이 이미 열려 있을 때 여는 새 새 창의 크기를 제어합니다. 기본적으로 새 창은 화면 가운데에 작은 크기로 열립니다. 'inherit'으로 설정할 경우 마지막 활성 창과 동일한 크기로 창이 열립니다. 'maximized'로 설정할 경우 창이 최대화되어 열리고 'fullscreen'으로 구성할 경우 전체 화면으로 열립니다. 이 설정은 여는 첫 번째 창에는 적용되지 않습니다. 첫 번째 창의 경우 항상 창을 닫기 전의 크기와 위치가 복원됩니다.", + "closeWhenEmpty": "undefined", + "window.menuBarVisibility.default": "메뉴가 전체 화면 모드에서만 숨겨집니다.", + "window.menuBarVisibility.visible": "메뉴가 전체 화면 모드에서도 항상 표시됩니다.", + "window.menuBarVisibility.toggle": "메뉴가 숨겨져 있지만 키를 통해 메뉴를 표시할 수 있습니다.", + "window.menuBarVisibility.hidden": "메뉴가 항상 숨겨집니다.", + "menuBarVisibility": "메뉴 모음의 표시 여부를 제어합니다. '설정/해제'를 설정함으로써 메뉴 모음이 숨겨지고 키를 누를 때마다 메뉴 모음이 표시됩니다. 기본값으로, 창이 전체 화면인 경우를 제외하고 메뉴 모음이 표시됩니다.", + "enableMenuBarMnemonics": "사용하도록 설정하는 경우 Alt 키 바로 가기를 통해 주 메뉴를 열 수 있습니다. 니모닉을 사용하지 않도록 설정하면 대신 이러한 Alt 키 바로 가기를 편집기 명령에 바인딩할 수 있습니다.", + "autoDetectHighContrast": "사용하도록 설정한 경우 Windows에서 고대비 테마를 사용 중이면 고대비 테마로 자동으로 변경되고 Windows 고대비 테마를 해제하면 어두운 테마로 변경됩니다.", + "titleBarStyle": "창 제목 표시줄의 모양을 조정합니다. 변경 내용을 적용하려면 전체 다시 시작해야 합니다.", + "window.nativeTabs": "macOS Sierra 창 탭을 사용하도록 설정합니다. 변경\n 내용을 적용하려면 전체 다시 시작해야 하고, 기본 탭에서\n 사용자 지정 제목 표시줄 스타일(구성된 경우)을 비활성화합니다.", + "windowConfigurationTitle": "창", + "zenModeConfigurationTitle": "Zen 모드", + "zenMode.fullScreen": "Zen 모드를 켜면 워크벤치도 전체 화면 모드로 전환되는지 여부를 제어합니다.", + "zenMode.hideTabs": "Zen 모드를 켜면 워크벤치 탭도 숨길지를 제어합니다.", + "zenMode.hideStatusBar": "Zen 모드를 켜면 워크벤치 하단에서 상태 표시줄도 숨길지를 제어합니다.", + "zenMode.hideActivityBar": "Zen 모드를 켜면 워크벤치의 왼쪽에 있는 작업 막대도 숨길지\n 여부를 제어합니다.", + "zenMode.restore": "창이 Zen 모드에서 종료된 경우 Zen 모드로 복원할지 제어합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/electron-browser/main.i18n.json b/i18n/kor/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..2761e0b20e --- /dev/null +++ b/i18n/kor/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "필요한 파일을 로드하지 못했습니다. 인터넷에 연결되지 않았거나 연결된 서버가 오프라인 상태입니다. 브라우저를 새로 고친 후 다시 시도해 보세요.", + "loaderErrorNative": "필요한 파일을 로드하지 못했습니다. 응용 프로그램을 다시 시작하여 다시 시도하세요. 세부 정보: {0}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/kor/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..5581e1bb61 --- /dev/null +++ b/i18n/kor/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "Code를 '루트'로 실행하지 않는 것이 좋습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/electron-browser/window.i18n.json b/i18n/kor/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..c20f887db5 --- /dev/null +++ b/i18n/kor/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "실행 취소", + "redo": "다시 실행", + "cut": "잘라내기", + "copy": "복사", + "paste": "붙여넣기", + "selectAll": "모두 선택" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/kor/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..99ee5a4d93 --- /dev/null +++ b/i18n/kor/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "개발자", + "file": "파일" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/kor/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..f066acc3f2 --- /dev/null +++ b/i18n/kor/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "경로 {0}이(가) 유효한 확장 Test Runner를 가리키지 않습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/kor/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..6a03321304 --- /dev/null +++ b/i18n/kor/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "{0}을(를) 구문 분석하지 못함: {1}.", + "fileReadFail": "파일 {0}을(를) 읽을 수 없음: {1}.", + "jsonsParseFail": "{0} 또는 {1}을(를) 구문 분석하지 못했습니다. {2}", + "missingNLSKey": "키 {0}에 대한 메시지를 찾을 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..9f0636735e --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "PATH에 '{0}' 명령 설치", + "not available": "이 명령은 사용할 수 없습니다.", + "successIn": "셸 명령 '{0}'이(가) PATH에 설치되었습니다.", + "warnEscalation": "이제 Code에서 'osascript'를 사용하여 관리자에게 셸 명령을 설치할 권한이 있는지를 묻습니다.", + "ok": "확인", + "cantCreateBinFolder": "'/usr/local/bin'을 만들 수 없습니다.", + "cancel2": "취소", + "aborted": "중단됨", + "uninstall": "PATH에서 '{0}' 명령 제거", + "successFrom": "셸 명령 '{0}'이(가) PATH에서 제거되었습니다.", + "shellCommand": "셸 명령" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..111217748f --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "이제 `editor.accessibilitySupport` 설정을 'on'으로 변경합니다.", + "openingDocs": "이제 VS Code 접근성 설명서 페이지를 엽니다.", + "introMsg": "VS Code의 접근성 옵션을 사용해 주셔서 감사합니다.", + "status": "상태:", + "changeConfigToOnMac": "화면 읽기 프로그램에서 사용에 영구적으로 최적화되도록 편집기를 구성하려면 지금 Command+E를 누르세요.", + "changeConfigToOnWinLinux": "화면 읽기 프로그램에서 사용에 영구적으로 최적화되도록 편집기를 구성하려면 지금 Command+E를 누르세요.", + "auto_unknown": "편집기는 플랫폼 API를 사용하여 화면 읽기 프로그램이 연결되는 시기를 검색하도록 구성되어 있지만 현재 런타임에서는 이 구성을 지원하지 않습니다.", + "auto_on": "편집기는 화면 읽기 프로그램이 연결되어 있음을 자동으로\n 검색했습니다.", + "auto_off": "편집기는 화면 편집기가 연결되는 시기를 자동으로 검색하도록 구성되어 있지만, 이 구성은 현재 지원되지 않습니다.", + "configuredOn": "편집기는 화면 읽기 프로그램에서 사용에 영구적으로 최적화되도록 편집기를 구성되어 있습니다. `editor.accessibilitySupport` 설정을 편집하여 이 구성을 변경할 수 있습니다.", + "configuredOff": "편집기는 화면 읽기 프로그램에서 사용에 최적화되지 않도록 구성되었습니다.", + "tabFocusModeOnMsg": "현재 편집기에서 키를 누르면 포커스가 다음 포커스 가능한 요소로 이동합니다. {0}을(를) 눌러서 이 동작을 설정/해제합니다.", + "tabFocusModeOnMsgNoKb": "현재 편집기에서 키를 누르면 포커스가 다음 포커스 가능한 요소로 이동합니다. {0} 명령은 현재 키 바인딩으로 트리거할 수 없습니다.", + "tabFocusModeOffMsg": "현재 편집기에서 키를 누르면 탭 문자가 삽입됩니다. {0}을(를) 눌러서 이 동작을 설정/해제합니다.", + "tabFocusModeOffMsgNoKb": "현재 편집기에서 키를 누르면 탭 문자가 삽입됩니다. {0} 명령은 현재 키 바인딩으로 트리거할 수 없습니다.", + "openDocMac": "브라우저 창에 접근성과 관련된 추가 VS Code 정보를 열려면 Command+H를 누르세요.", + "openDocWinLinux": "브라우저 창에 접근성과 관련된 추가 VS Code 정보를 열려면 지금 Control+H를 누르세요.", + "outroMsg": "이 도구 설명을 해제하고 Esc 키 또는 Shift+Esc를 눌러서 편집기로 돌아갈 수 있습니다.", + "ShowAccessibilityHelpAction": "접근성 도움말 표시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..04595c948e --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "개발자: 키 매핑 검사" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f891a70fbe --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "개발자: TM 범위 검사", + "inspectTMScopesWidget.loading": "로드 중..." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..e0ef6bbcbb --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "{0}을(를) 구문 분석하는 동안 오류가 발생했습니다. {1}", + "schema.openBracket": "여는 대괄호 문자 또는 문자열 시퀀스입니다.", + "schema.closeBracket": "닫는 대괄호 문자 또는 문자열 시퀀스입니다.", + "schema.comments": "주석 기호를 정의합니다.", + "schema.blockComments": "블록 주석이 표시되는 방법을 정의합니다.", + "schema.blockComment.begin": "블록 주석을 시작하는 문자 시퀀스입니다.", + "schema.blockComment.end": "블록 주석을 끝내는 문자 시퀀스입니다.", + "schema.lineComment": "줄 주석을 시작하는 문자 시퀀스입니다.", + "schema.brackets": "들여쓰기를 늘리거나 줄이는 대괄호 기호를 정의합니다.", + "schema.autoClosingPairs": "대괄호 쌍을 정의합니다. 여는 대괄호를 입력하면 닫는 대괄호가 자동으로 삽입됩니다.", + "schema.autoClosingPairs.notIn": "자동 쌍을 사용하지 않도록 설정된 범위 목록을 정의합니다.", + "schema.surroundingPairs": "선택한 문자열을 둘러싸는 데 사용할 수 있는 대괄호 쌍을 정의합니다.", + "schema.wordPattern": "해당 언어에 대한 단어 정의입니다.", + "schema.wordPattern.pattern": "단어 일치에 사용하는 RegEXP 패턴입니다.", + "schema.wordPattern.flags": "단어 일치에 사용하는 RegExp 플래그입니다.", + "schema.wordPattern.flags.errorMessage": "`/^([gimuy]+)$/` 패턴과 일치해야 합니다.", + "schema.indentationRules": "해당 언어의 들여쓰기 설정입니다.", + "schema.indentationRules.increaseIndentPattern": "라인이 이 패턴과 일치할 경우 이후의 모든 행을 한 번 들여씁니다(다른 규칙이 일치할 때까지).", + "schema.indentationRules.increaseIndentPattern.pattern": "increaseIndentPattern에 대한 RegExp 패턴입니다.", + "schema.indentationRules.increaseIndentPattern.flags": "increaseIndentPattern에 대한 RegExp 플래그입니다.", + "schema.indentationRules.increaseIndentPattern.errorMessage": "`/^([gimuy]+)$/` 패턴과 일치해야 합니다.", + "schema.indentationRules.decreaseIndentPattern": "행이 이 패턴과 일치하면 이후의 모든 행은 한 번 들여쓰지 않습니다(다른 규칙이 일치할 때까지).", + "schema.indentationRules.decreaseIndentPattern.pattern": "decreaseIndentPattern에 대한 RegExp 패턴입니다.", + "schema.indentationRules.decreaseIndentPattern.flags": "decreaseIndentPattern에 대한 RegExp 플래그입니다.", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "`/^([gimuy]+)$/` 패턴과 일치해야 합니다.", + "schema.indentationRules.indentNextLinePattern": "행이 이 패턴과 일치하면 **그 다음 행만** 한 번 들여쓰기합니다.", + "schema.indentationRules.indentNextLinePattern.pattern": "indentNextLinePattern에 대한 RegExp 패턴입니다.", + "schema.indentationRules.indentNextLinePattern.flags": "indentNextLinePattern에 대한 RegExp 플래그입니다.", + "schema.indentationRules.indentNextLinePattern.errorMessage": "`/^([gimuy]+)$/` 패턴과 일치해야 합니다.", + "schema.indentationRules.unIndentedLinePattern": "행이 이 패턴과 일치하면 들여쓰기를 수정하지 않고 다른 규칙에 대해서 평가하지도 않습니다.", + "schema.indentationRules.unIndentedLinePattern.pattern": "unIndentedLinePattern에 대한 RegExp 패턴입니다.", + "schema.indentationRules.unIndentedLinePattern.flags": "unIndentedLinePattern에 대한 RegExp 플래그입니다.", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "`/^([gimuy]+)$/` 패턴과 일치해야 합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..f891a70fbe --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "개발자: TM 범위 검사", + "inspectTMScopesWidget.loading": "로드 중..." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..446c1f2ab6 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "보기: 미니맵 토글" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..d2ab6c2b7a --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "다중 커서 한정자 설정/해제" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..911f5dad03 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "보기: 제어 문자 토글" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..42f5da0c59 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "보기: 렌더링 공백 토글" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..6cd237a92a --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "보기: 자동 줄 바꿈 설정/해제", + "wordWrap.notInDiffEditor": "diff 편집기에서 자동 줄 바꿈을 설정/해제할 수 없습니다.", + "unwrapMinified": "이 파일에 대해 줄 바꿈 사용 안 함", + "wrapMinified": "이 파일에 대해 줄 바꿈 사용" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..86c19ccd65 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "확인", + "wordWrapMigration.dontShowAgain": "다시 표시 안 함", + "wordWrapMigration.openSettings": "설정 열기", + "wordWrapMigration.prompt": "`editor.wrappingColumn` 설정은 `editor.wordWrap`을 위해 사용되지 않습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..bb97556aef --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "식이 true로 계산될 경우 중단합니다. 적용하려면 'Enter' 키를 누르고 취소하려면 'Esc' 키를 누릅니다.", + "breakpointWidgetAriaLabel": "이 조건이 true인 경우에만 프로그램이 여기서 중지됩니다. 수락하려면 키를 누르고, 취소하려면 키를 누르세요.", + "breakpointWidgetHitCountPlaceholder": "적중 횟수 조건이 충족될 경우 중단합니다. 적용하려면 'Enter' 키를 누르고 취소하려면 'Esc' 키를 누릅니다.", + "breakpointWidgetHitCountAriaLabel": "적중 횟수가 충족되는 경우에만 프로그램이 여기서 중지됩니다. 수락하려면 키를 누르고, 취소하려면 키를 누르세요.", + "expression": "식", + "hitCount": "적중 횟수" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..9dd8bca897 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "구성 없음", + "addConfigTo": "구성 추가 ({0})...", + "addConfiguration": "구성 추가..." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..5d5589ef4b --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "{0} 열기", + "launchJsonNeedsConfigurtion": "'launch.json' 구성 또는 수정", + "noFolderDebugConfig": "고급 디버그 구성을 수행하려면 먼저 폴더를 여세요.", + "startDebug": "디버깅 시작", + "startWithoutDebugging": "디버깅하지 않고 시작", + "selectAndStartDebugging": "디버깅 선택 및 시작", + "restartDebug": "다시 시작", + "reconnectDebug": "다시 연결", + "stepOverDebug": "단위 실행", + "stepIntoDebug": "단계 정보", + "stepOutDebug": "단계 출력", + "stopDebug": "중지", + "disconnectDebug": "연결 끊기", + "continueDebug": "계속", + "pauseDebug": "일시 중지", + "restartFrame": "프레임 다시 시작", + "removeBreakpoint": "중단점 제거", + "removeAllBreakpoints": "모든 중단점 제거", + "enableBreakpoint": "중단점 사용", + "disableBreakpoint": "중단점 사용 안 함", + "enableAllBreakpoints": "모든 중단점 설정", + "disableAllBreakpoints": "모든 중단점 해제", + "activateBreakpoints": "중단점 활성화", + "deactivateBreakpoints": "중단점 비활성화", + "reapplyAllBreakpoints": "모든 중단점 다시 적용", + "addFunctionBreakpoint": "함수 중단점 추가", + "renameFunctionBreakpoint": "함수 중단점 이름 바꾸기", + "addConditionalBreakpoint": "조건부 중단점 추가...", + "editConditionalBreakpoint": "중단점 편집...", + "setValue": "값 설정", + "addWatchExpression": "식 추가", + "editWatchExpression": "식 편집", + "addToWatchExpressions": "조사식에 추가", + "removeWatchExpression": "식 제거", + "removeAllWatchExpressions": "모든 식 제거", + "clearRepl": "콘솔 지우기", + "debugConsoleAction": "디버그 콘솔", + "unreadOutput": "디버그 콘솔의 새 출력", + "debugFocusConsole": "디버그 콘솔에 포커스", + "focusProcess": "프로세스에 포커스", + "stepBackDebug": "뒤로 이동", + "reverseContinue": "반전" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..d1556f41b0 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "디버그 도구 모음 배경색입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..c600933366 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "디버그 세션이 없는 리소스를 확인할 수 없음" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..b28a1bb838 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "디버그: 중단점 설정/해제", + "columnBreakpointAction": "디버그: 열 중단점", + "columnBreakpoint": "열 중단점 추가", + "conditionalBreakpointEditorAction": "디버그: 조건부 중단점 추가...", + "runToCursor": "커서까지 실행", + "debugEvaluate": "디버그: 평가", + "debugAddToWatch": "디버그: 조사식에 추가", + "showDebugHover": "디버그: 가리키기 표시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..5b6598a4d5 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "해제된 중단점", + "breakpointUnverifieddHover": "확인되지 않은 중단점", + "breakpointDirtydHover": "확인되지 않은 중단점입니다. 파일이 수정되었습니다. 디버그 세션을 다시 시작하세요.", + "breakpointUnsupported": "이 디버그 형식에서 지원되지 않는 조건부 중단점" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..c9163002ab --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, 디버그", + "debugAriaLabel": "실행할 시작 구성의 이름을 입력하세요.", + "noConfigurationsMatching": "일치하는 디버그 구성 없음", + "noConfigurationsFound": "디버그 구성을 찾을 수 없습니다. 'launch.json' 파일을 만드세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..5a5763ec4e --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "예외 위젯 테두리 색입니다.", + "debugExceptionWidgetBackground": "예외 위젯 배경색입니다.", + "exceptionThrownWithId": "예외가 발생했습니다. {0}", + "exceptionThrown": "예외가 발생했습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..873660c939 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "클릭하여 이동(측면에서 열려면 Cmd 키를 누르고 클릭)", + "fileLink": "클릭하여 이동(측면에서 열려면 Ctrl 키를 누르고 클릭)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..458879a690 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "내부 디버그 콘솔의 동작을 제어합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..be54340b7c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "사용할 수 없음", + "startDebugFirst": "평가할 디버그 세션을 시작하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..383e5f2a2f --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "알 수 없는 소스" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..b372015c7d --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "디버그 표시", + "toggleDebugPanel": "디버그 콘솔", + "debug": "디버그", + "debugPanel": "디버그 콘솔", + "variables": "변수", + "watch": "조사식", + "callStack": "호출 스택", + "breakpoints": "중단점", + "view": "보기", + "debugCategory": "디버그", + "debugCommands": "디버그 구성", + "debugConfigurationTitle": "디버그", + "allowBreakpointsEverywhere": "모든 파일에 대한 중단점을 설정할 수 있습니다.", + "openExplorerOnEnd": "디버그 세션 끝에 탐색기 뷰를 자동으로 엽니다.", + "inlineValues": "디버그하는 동안 편집기에서 변수 값을 인라인으로 표시합니다.", + "hideActionBar": "부동 디버그 작업 모음을 숨길지 여부를 제거합니다.", + "launch": "전역 디버그 시작 구성입니다. 작업 영역에서 공유되는 \n 'launch.json'에 대한 대체로 사용되어야 합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..4b3bd508e7 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "고급 디버그 구성을 수행하려면 먼저 폴더를 여세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..f828668efa --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "디버그 어댑터를 적용합니다.", + "vscode.extension.contributes.debuggers.type": "이 디버그 어댑터에 대한 고유한 식별자입니다.", + "vscode.extension.contributes.debuggers.label": "이 디버그 어댑터에 대한 이름을 표시합니다.", + "vscode.extension.contributes.debuggers.program": "디버그 어댑터 프로그램의 경로입니다. 절대 경로이거나 확장 폴더의 상대 경로입니다.", + "vscode.extension.contributes.debuggers.args": "어댑터에 전달할 선택적 인수입니다.", + "vscode.extension.contributes.debuggers.runtime": "프로그램 특성이 실행 파일이 아니지만 런타임이 필요한 경우의 선택적 런타임입니다.", + "vscode.extension.contributes.debuggers.runtimeArgs": "선택적 런타임 인수입니다.", + "vscode.extension.contributes.debuggers.variables": "`launch.json`의 대화형 변수(예: ${action.pickProcess})에서 명령으로의 매핑입니다.", + "vscode.extension.contributes.debuggers.initialConfigurations": "초기 'launch.json'을 생성하기 위한 구성입니다.", + "vscode.extension.contributes.debuggers.languages": "디버그 확장이 \"기본 디버거\"로 간주될 수 있는 언어 목록입니다.", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "지정하는 경우 VS Code에서 이 명령을 호출하여 디버그 어댑터의 실행 파일 경로 및 전달할 인수를 확인합니다.", + "vscode.extension.contributes.debuggers.startSessionCommand": "지정하는 경우 VS Code에서 이 확장을 대상으로 하는 \"디버그\" 또는 \"실행\" 작업에 대해 이 명령을 호출합니다.", + "vscode.extension.contributes.debuggers.configurationSnippets": "'launch.json'에 새 구성을 추가하는 코드 조각입니다.", + "vscode.extension.contributes.debuggers.configurationAttributes": "'launch.json'의 유효성 검사를 위한 JSON 스키마 구성입니다.", + "vscode.extension.contributes.debuggers.windows": "Windows 특정 설정", + "vscode.extension.contributes.debuggers.windows.runtime": "Windows에 사용되는 런타임입니다.", + "vscode.extension.contributes.debuggers.osx": "OS X 특정 설정입니다.", + "vscode.extension.contributes.debuggers.osx.runtime": "OSX에 사용되는 런타임입니다.", + "vscode.extension.contributes.debuggers.linux": "Linux 특정 설정", + "vscode.extension.contributes.debuggers.linux.runtime": "Linux에 사용되는 런타임입니다.", + "vscode.extension.contributes.breakpoints": "중단점을 적용합니다.", + "vscode.extension.contributes.breakpoints.language": "이 언어에 대해 중단점을 허용합니다.", + "app.launch.json.title": "시작", + "app.launch.json.version": "이 파일 형식의 버전입니다.", + "app.launch.json.configurations": "구성 목록입니다. IntelliSense를 사용하여 새 구성을 추가하거나 기존 구성을 편집합니다.", + "app.launch.json.compounds": "복합의 목록입니다. 각 복합은 함께 시작되는 여러 구성을 참조합니다.", + "app.launch.json.compound.name": "복합의 이름입니다. 구성 시작 드롭 다운 메뉴에 표시됩니다.", + "app.launch.json.compounds.configurations": "이 복합의 일부로 시작되는 구성의 이름입니다.", + "debugNoType": "디버그 어댑터 '형식'은 생략할 수 없으며 '문자열' 형식이어야 합니다.", + "selectDebug": "환경 선택", + "DebugConfig.failed": "'.vscode' 폴더({0}) 내에 'launch.json' 파일을 만들 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..415de5aebd --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "중단점 제거", + "removeBreakpointOnColumn": "{0} 열에서 중단점 제거", + "removeLineBreakpoint": "줄 중단점 제거", + "editBreakpoints": "중단점 편집", + "editBreakpointOnColumn": "{0} 열에서 중단점 편집", + "editLineBrekapoint": "줄 중단점 편집", + "enableDisableBreakpoints": "중단점 사용/사용 안 함", + "disableColumnBreakpoint": "{0} 열에서 중단점 사용 안 함", + "disableBreakpointOnLine": "줄 중단점 사용 안 함", + "enableBreakpoints": "{0} 열에서 중단점 사용", + "enableBreakpointOnLine": "줄 중단점 사용", + "addBreakpoint": "중단점 추가", + "addConfiguration": "구성 추가..." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..1336c48bd1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "가리키기 디버그" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..13734ff150 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "이 개체에 대한 기본 값만 표시됩니다.", + "debuggingPaused": "디버그가 일시 중지되었습니다. 이유 {0}, {1} {2}", + "debuggingStarted": "디버그가 시작되었습니다.", + "debuggingStopped": "디버그가 중지되었습니다.", + "breakpointAdded": "파일 {1}, 줄 {0}에 중단점이 추가되었습니다.", + "breakpointRemoved": "파일 {1}, 줄 {0}에서 중단점이 제거되었습니다.", + "compoundMustHaveConfigurations": "여러 구성을 시작하려면 복합에 \"configurations\" 특성 집합이 있어야 합니다.", + "configMissing": "'{0}' 구성이 'launch.json'에 없습니다.", + "debugTypeNotSupported": "구성된 디버그 형식 '{0}'은(는) 지원되지 않습니다.", + "debugTypeMissing": "선택한 시작 구성에 대한 'type' 속성이 없습니다.", + "preLaunchTaskErrors": "preLaunchTask '{0}' 진행 중에 빌드 오류가 감지되었습니다.", + "preLaunchTaskError": "preLaunchTask '{0}' 진행 중에 빌드 오류가 감지되었습니다.", + "preLaunchTaskExitCode": "preLaunchTask '{0}'이(가) {1} 종료 코드와 함께 종료되었습니다.", + "debugAnyway": "디버그", + "noFolderWorkspaceDebugError": "활성 파일은 디버그할 수 없습니다. 이 파일이 디스크에 저장되어 있고 해당 파일 형식에 대한 디버그 확장이 설치되어 있는지 확인하세요.", + "NewLaunchConfig": "응용 프로그램에 사용할 구성 시작 파일을 설정하세요. {0}", + "DebugTaskNotFound": "preLaunchTask '{0}'을(를) 찾을 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..c15cb7fe3f --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "프로세스", + "paused": "일시 중지됨", + "running": "실행 중", + "thread": "스레드", + "pausedOn": "{0}에서 일시 중지됨", + "loadMoreStackFrames": "더 많은 스택 프레임 로드", + "threadAriaLabel": "스레드 {0}, 호출 스택, 디버그", + "stackFrameAriaLabel": "스택 프레임 {0} 줄 {1} {2}, 호출 스택, 디버그", + "variableValueAriaLabel": "새 변수 값 입력", + "variableScopeAriaLabel": "{0} 범위, 변수, 디버그", + "variableAriaLabel": "{0} 값 {1}, 변수, 디버그", + "watchExpressionPlaceholder": "조사할 식", + "watchExpressionInputAriaLabel": "조사식 입력", + "watchExpressionAriaLabel": "{0} 값 {1}, 조사식, 디버그", + "watchVariableAriaLabel": "{0} 값 {1}, 조사식, 디버그", + "functionBreakpointPlaceholder": "중단할 함수", + "functionBreakPointInputAriaLabel": "함수 중단점 입력", + "functionBreakpointsNotSupported": "이 디버그 형식은 함수 중단점을 지원하지 않습니다.", + "breakpointAriaLabel": "중단점 줄 {0} {1}, 중단점, 디버그", + "functionBreakpointAriaLabel": "함수 중단점 {0}, 중단점, 디버그", + "exceptionBreakpointAriaLabel": "예외 중단점 {0}, 중단점, 디버그" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..06345ce28a --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "변수 섹션", + "variablesAriaTreeLabel": "변수 디버그", + "expressionsSection": "식 섹션", + "watchAriaTreeLabel": "조사식 디버그", + "callstackSection": "호출 스택 섹션", + "debugStopped": "{0}에서 일시 중지됨", + "callStackAriaLabel": "호출 스택 디버그", + "breakpointsSection": "중단점 섹션", + "breakpointsAriaTreeLabel": "중단점 디버그" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..95f8eeaab1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "값 복사", + "copy": "복사", + "copyAll": "모두 복사", + "copyStackTrace": "호출 스택 복사" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..572575201c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "추가 정보", + "unableToLaunchDebugAdapter": "'{0}'에서 디버그 어댑터를 시작할 수 없습니다.", + "unableToLaunchDebugAdapterNoArgs": "디버그 어댑터를 시작할 수 없습니다.", + "stoppingDebugAdapter": "{0}. 디버그 어댑터를 중지합니다.", + "debugAdapterCrash": "디버그 어댑터 프로세스가 예기치 않게 종료되었습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..60f2ccf463 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "읽기 평가 인쇄 루프 패널", + "actions.repl.historyPrevious": "기록 이전", + "actions.repl.historyNext": "기록 다음", + "actions.repl.acceptInput": "REPL 입력 적용", + "actions.repl.copyAll": "디버그: 모두 콘솔 복사" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..db150a901c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "개체 상태는 첫 번재 평가에서 캡처됩니다.", + "replVariableAriaLabel": "{0} 변수에 {1} 값이 있습니다. 읽기 평가 인쇄 루프, 디버그", + "replExpressionAriaLabel": "{0} 식에 {1} 값이 있습니다. 읽기 평가 인쇄 루프, 디버그", + "replValueOutputAriaLabel": "{0}, 읽기 평가 인쇄 루프, 디버그", + "replKeyValueOutputAriaLabel": "출력 변수 {0}에 {1} 값이 있습니다. 읽기 평가 인쇄 루프, 디버그" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..29bf6dc4da --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "프로그램이 디버그될 때의 상태 표시줄 배경색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarDebuggingForeground": "프로그램이 디버그될 때의 상태 표시줄 전경색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다.", + "statusBarDebuggingBorder": "프로그램 디버깅 중 사이드바 및 편집기와 구분하는 상태 표시줄 테두리 색입니다. 상태 표시줄은 창의 맨 아래에 표시됩니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..ccdb22cab5 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "디버기", + "debug.terminal.not.available.error": "통합 터미널을 사용할 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..289f64f269 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "디버그 어댑터 실행 파일 '{0}'이(가) 없습니다.", + "debugAdapterCannotDetermineExecutable": "디버그 어댑터 '{0}'에 대한 실행 파일을 확인할 수 없습니다.", + "launch.config.comment1": "IntelliSense를 사용하여 가능한 특성에 대해 알아보세요.", + "launch.config.comment2": "기존 특성에 대한 설명을 보려면 가리킵니다.", + "launch.config.comment3": "자세한 내용을 보려면 {0}을(를) 방문하세요.", + "debugType": "구성의 형식입니다.", + "debugTypeNotRecognised": "디버그 형식이 인식되지 않습니다. 해당하는 디버그 확장을 설치하고 사용하도록 설정했는지 확인하세요.", + "node2NotSupported": "\"node2\"는 더 이상 지원되지 않습니다. 대신 \"node\"를 사용하고 \"protocol\" 특성을 \"inspector\"로 설정하세요.", + "debugName": "구성 이름이며, 구성 시작 드롭다운 메뉴에 표시됩니다.", + "debugRequest": "구성 형식을 요청합니다. \"시작\" 또는 \"연결\"일 수 있습니다.", + "debugServer": "디버그 확장 배포 전용입니다. 포트가 지정된 경우 VS Code에서는 서버 모드로 실행하는 디버그 어댑터에 연결을 시도합니다.", + "debugPrelaunchTask": "디버그 세션이 시작되기 이전에 실행할 작업입니다.", + "debugWindowsConfiguration": "Windows 특정 시작 구성 특성입니다.", + "debugOSXConfiguration": "OS X 특정 시작 구성 특성입니다.", + "debugLinuxConfiguration": "Linux 특정 시작 구성 특성입니다.", + "deprecatedVariables": "'env.', 'config.' 및 'command.'는 사용되지 않습니다. 대신 'env:', 'config:' 및 'command:'를 사용하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..8487ecc5da --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "Emmet 명령 표시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..1a0fe5f8db --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: 균형있게(안쪽으로)", + "balanceOutward": "Emmet: 균형있게(바깥쪽으로)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..9e6e1db081 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: 이전 편집 점으로 이동", + "nextEditPoint": "Emmet: 다음 편집 점으로 이동" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..f7b304b38c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: 수학 식 평가" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..0c91877215 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: 약어 확장" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..b6cd90d699 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: 0.1 증가", + "incrementNumberByOne": "Emmet: 1 증가", + "incrementNumberByTen": "Emmet: 10 증가", + "decrementNumberByOneTenth": "Emmet: 0.1 감소", + "decrementNumberByOne": "Emmet: 1 감소", + "decrementNumberByTen": "Emmet: 10 감소" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..913e2888f1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: 일치하는 쌍으로 이동" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..5ef0305de6 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: 줄 병합" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..7a728e7476 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: CSS 값 반영" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..e2e16622ea --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: 태그 제거" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..66bb565676 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: 이전 항목 선택", + "selectNextItem": "Emmet: 다음 항목 선택" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..a221ac8f7d --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: 태그 분할/조인" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..9594261267 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: 주석 설정/해제" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..708576d0f7 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: 이미지 크기 업데이트" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..fdce0fff02 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: 태그 업데이트", + "enterTag": "태그 입력", + "tag": "태그" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..8ac35a87fc --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: 약어로 래핑", + "enterAbbreviation": "약어 입력", + "abbreviation": "약어" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..ed85708887 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "사용하도록 설정하면 emmet 약어는 키를 눌렀을 때 확장됩니다. emmet.useNewemmet을 true로 설정하면 적용되지 않습니다.", + "emmetPreferences": "Emmet의 일부 작업 및 해결 프로그램의 동작을 수정하는 데 사용되는 기본 설정입니다. emmet.useNewemmet을 true로 설정하면 적용되지 않습니다.", + "emmetSyntaxProfiles": "지정된 구문에 대한 프로필을 정의하거나 특정 규칙이 포함된 고유한 프로필을 사용하세요.", + "emmetExclude": "Emmet 약어를 확장하면 안 되는 언어의 배열입니다.", + "emmetExtensionsPath": "Emmet 프로필, 코드 조각 및 기본 설정이 포함된 폴더의 경로입니다. emmet.useNewEmmet이 true일 때만 확장 경로의 프로필이 인정됩니다.", + "useNewEmmet": "모든 emmet 기능에 대해 새 emmet 모듈을 사용해 보세요(이전 단일 emmet 라이브러리를 대체함)." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json new file mode 100644 index 0000000000..1a0fe5f8db --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: 균형있게(안쪽으로)", + "balanceOutward": "Emmet: 균형있게(바깥쪽으로)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json new file mode 100644 index 0000000000..75ad1de861 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: 이전 편집 점", + "nextEditPoint": "Emmet: 다음 편집 점" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..f7b304b38c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: 수학 식 평가" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..0c91877215 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: 약어 확장" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..b6cd90d699 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: 0.1 증가", + "incrementNumberByOne": "Emmet: 1 증가", + "incrementNumberByTen": "Emmet: 10 증가", + "decrementNumberByOneTenth": "Emmet: 0.1 감소", + "decrementNumberByOne": "Emmet: 1 감소", + "decrementNumberByTen": "Emmet: 10 감소" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..913e2888f1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: 일치하는 쌍으로 이동" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..5ef0305de6 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: 줄 병합" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..7a728e7476 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: CSS 값 반영" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json new file mode 100644 index 0000000000..e2e16622ea --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: 태그 제거" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json new file mode 100644 index 0000000000..66bb565676 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: 이전 항목 선택", + "selectNextItem": "Emmet: 다음 항목 선택" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..a221ac8f7d --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: 태그 분할/조인" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..9594261267 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: 주석 설정/해제" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..708576d0f7 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: 이미지 크기 업데이트" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json new file mode 100644 index 0000000000..fdce0fff02 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: 태그 업데이트", + "enterTag": "태그 입력", + "tag": "태그" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..8ac35a87fc --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: 약어로 래핑", + "enterAbbreviation": "약어 입력", + "abbreviation": "약어" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json new file mode 100644 index 0000000000..bf7e56d156 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "사용하도록 설정하면 emmet 약어는 키를 눌렀을 때 확장됩니다.", + "emmetPreferences": "Emmet의 일부 작업 및 해결 프로그램의 동작을 수정하는 데 사용되는 기본 설정입니다.", + "emmetSyntaxProfiles": "지정된 구문에 대한 프로필을 정의하거나 특정 규칙이 포함된 고유한 프로필을 사용하세요.", + "emmetExclude": "Emmet 약어를 확장하면 안 되는 언어의 배열입니다.", + "emmetExtensionsPath": "Emmet 프로필, 코드 조각 및 기본 설정이 포함된 폴더의 경로" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..d8e8f0b799 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "외부 터미널", + "explorer.openInTerminalKind": "실행할 터미널 종류를 사용자 지정합니다.", + "terminal.external.windowsExec": "Windows에서 실행할 터미널을 사용자 지정합니다.", + "terminal.external.osxExec": "OS X에서 실행할 터미널 응용 프로그램을 사용자 지정합니다.", + "terminal.external.linuxExec": "Linux에서 실행할 터미널을 사용자 지정합니다.", + "globalConsoleActionWin": "새 명령 프롬프트 열기", + "globalConsoleActionMacLinux": "새 터미널 열기", + "scopedConsoleActionWin": "명령 프롬프트에서 열기", + "scopedConsoleActionMacLinux": "터미널에서 열기", + "openFolderInIntegratedTerminal": "터미널에서 열기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..2ced6885bc --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "외부 터미널", + "terminal.external.windowsExec": "Windows에서 실행할 터미널을 사용자 지정합니다.", + "terminal.external.osxExec": "OS X에서 실행할 터미널 응용 프로그램을 사용자 지정합니다.", + "terminal.external.linuxExec": "Linux에서 실행할 터미널을 사용자 지정합니다.", + "globalConsoleActionWin": "새 명령 프롬프트 열기", + "globalConsoleActionMacLinux": "새 터미널 열기", + "scopedConsoleActionWin": "명령 프롬프트에서 열기", + "scopedConsoleActionMacLinux": "터미널에서 열기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/kor/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..8dcb0ca0ba --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "VS Code 콘솔", + "mac.terminal.script.failed": "스크립트 '{0}'이(가) 실패했습니다(종료 코드: {1}).", + "mac.terminal.type.not.supported": "'{0}'이(가) 지원되지 않습니다.", + "press.any.key": "계속하려면 아무 키나 누르세요.", + "linux.term.failed": "'{0}'에서 실패했습니다(종료 코드: {1})." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..c040573b46 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "사용자 지정 뷰를 적용합니다.", + "vscode.extension.contributes.view.id": "vscode.workspace.createTreeView를 통해 만든 뷰를 식별하는 데 사용된 고유 ID", + "vscode.extension.contributes.view.label": "뷰를 렌더링하는 데 사용되는 사람이 읽을 수 있는 문자열", + "vscode.extension.contributes.view.icon": "뷰 아이콘의 경로", + "vscode.extension.contributes.views": "사용자 지정 뷰를 적용합니다.", + "showViewlet": "{0} 표시", + "view": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..ffbd9eaa6d --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "새로 고침" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..5601ab3375 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "ID가 {providerId}인 등록된 TreeExplorerNodeProvider가 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/kor/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..3ef37895f1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "창 다시 로드" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..9ceb0b3209 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "오류", + "Unknown Dependency": "알 수 없는 종속성:" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..ad081410eb --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "확장 이름", + "extension id": "확장 ID", + "publisher": "게시자 이름", + "install count": "설치 수", + "rating": "등급", + "license": "라이선스", + "details": "세부 정보", + "contributions": "기여", + "changelog": "변경 로그", + "dependencies": "종속성", + "noReadme": "사용 가능한 추가 정보가 없습니다.", + "noChangelog": "CHANGELOG를 사용할 수 없습니다.", + "noContributions": "참여 없음", + "noDependencies": "종속성 없음", + "settings": "설정({0})", + "setting name": "이름", + "description": "설명", + "default": "기본값", + "debuggers": "디버거({0})", + "debugger name": "이름", + "debugger type": "유형", + "views": "뷰({0})", + "view id": "ID", + "view name": "이름", + "view location": "위치", + "themes": "테마({0})", + "JSON Validation": "JSON 유효성 검사({0})", + "commands": "명령({0})", + "command name": "이름", + "keyboard shortcuts": "바로 가기 키(&&K)", + "menuContexts": "메뉴 컨텍스트", + "languages": "언어({0})", + "language id": "ID", + "language name": "이름", + "file extensions": "파일 확장명", + "grammar": "문법", + "snippets": "코드 조각" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..8aec5f10f9 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "설치", + "installing": "설치 중", + "uninstallAction": "제거", + "Uninstalling": "제거하는 중", + "updateAction": "업데이트", + "updateTo": "{0}(으)로 업데이트", + "enableForWorkspaceAction.label": "사용(작업 영역)", + "enableAlwaysAction.label": "사용(항상)", + "disableForWorkspaceAction.label": "사용 안 함(작업 영역)", + "disableAlwaysAction.label": "사용 안 함(항상)", + "ManageExtensionAction.uninstallingTooltip": "제거하는 중", + "enableForWorkspaceAction": "작업 영역", + "enableGloballyAction": "항상", + "enableAction": "사용", + "disableForWorkspaceAction": "작업 영역", + "disableGloballyAction": "항상", + "disableAction": "사용 안 함", + "checkForUpdates": "업데이트 확인", + "enableAutoUpdate": "확장 자동 업데이트 사용", + "disableAutoUpdate": "확장 자동 업데이트 사용 안 함", + "updateAll": "모든 확장 업데이트", + "reloadAction": "다시 로드", + "postUpdateTooltip": "업데이트하려면 다시 로드", + "postUpdateMessage": "이 창을 다시 로드하여 업데이트된 확장 '{0}'을(를) 활성화하시겠습니까?", + "postEnableTooltip": "활성화하려면 다시 로드", + "postEnableMessage": "이 창을 다시 로드하여 '{0}' 확장을 활성화하시겠습니까?", + "postDisableTooltip": "비활성화하려면 다시 로드", + "postDisableMessage": "이 창을 다시 로드하여 '{0}' 확장을 비활성화하시겠습니까?", + "postUninstallTooltip": "비활성화하려면 다시 로드", + "postUninstallMessage": "이 창을 다시 로드하여 제거된 확장 '{0}'을(를) 비활성화하시겠습니까?", + "reload": "창 다시 로드(&&R)", + "toggleExtensionsViewlet": "확장 표시", + "installExtensions": "확장 설치", + "showEnabledExtensions": "사용 확장자 표시", + "showInstalledExtensions": "설치된 확장 표시", + "showDisabledExtensions": "사용할 수 없는 확장 표시", + "clearExtensionsInput": "확장 입력 지우기", + "showOutdatedExtensions": "만료된 확장 표시", + "showPopularExtensions": "자주 사용되는 확장 표시", + "showRecommendedExtensions": "권장되는 확장 표시", + "showWorkspaceRecommendedExtensions": "작업 영역 권장 확장 표시", + "showRecommendedKeymapExtensions": "권장되는 키 맵 표시", + "showRecommendedKeymapExtensionsShort": "키 맵", + "showLanguageExtensions": "언어 확장 표시", + "showLanguageExtensionsShort": "언어 확장", + "showAzureExtensions": "Azure 확장 표시", + "showAzureExtensionsShort": "Azure 확장", + "configureWorkspaceRecommendedExtensions": "권장 확장 구성(작업 영역)", + "ConfigureWorkspaceRecommendations.noWorkspace": "권장 사항은 작업 영역 폴더에서만 사용할 수 있습니다.", + "OpenExtensionsFile.failed": "'.vscode' 폴더({0}) 내에 'extensions.json' 파일을 만들 수 없습니다.", + "builtin": "기본 제공", + "disableAll": "설치된 모든 확장 사용 안 함", + "disableAllWorkspace": "이 작업 영역에 대해 설치된 모든 확장 사용 안 함", + "enableAll": "설치된 모든 확장 사용", + "enableAllWorkspace": "이 작업 영역에 대해 설치된 모든 확장 사용", + "extensionButtonProminentBackground": "눈에 잘 띄는 작업 확장의 단추 배경색입니다(예: 설치 단추).", + "extensionButtonProminentForeground": "눈에 잘 띄는 작업 확장의 단추 전경색입니다(예: 설치 단추).", + "extensionButtonProminentHoverBackground": "눈에 잘 띄는 작업 확장의 단추 배경 커서 올리기 색입니다(예: 설치 단추)." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..72c21adce4 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "확장을 관리하려면 키를 누르세요.", + "searchFor": "마켓플레이스에서 '{0}'을(를) 검색하려면 키를 누르세요.", + "noExtensionsToInstall": "확장 이름을 입력하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..7676be6257 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "확장", + "app.extensions.json.recommendations": "확장 권장 목록입니다. 확장의 식별자는 항상 '${publisher}.${name}'입니다. 예: 'vscode.csharp'", + "app.extension.identifier.errorMessage": "필요한 형식은 '${publisher}.${name}'입니다. 예: 'vscode.csharp'" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..284113a63b --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "확장: {0}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..81368903c1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "이 파일 형식에 대해 '{0}' 확장이 권장됩니다.", + "showRecommendations": "권장 사항 표시", + "neverShowAgain": "다시 표시 안 함", + "close": "닫기", + "workspaceRecommended": "이 작업 영역에 확장 권장 사항이 있습니다.", + "ignoreExtensionRecommendations": "확장 권장 사항을 모두 무시하시겠습니까?", + "ignoreAll": "예, 모두 무시", + "no": "아니요", + "cancel": "취소" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..b016be0017 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "확장 관리", + "galleryExtensionsCommands": "갤러리 확장 설치", + "extension": "확장", + "extensions": "확장", + "view": "보기", + "extensionsConfigurationTitle": "확장", + "extensionsAutoUpdate": "자동으로 확장 업데이트", + "extensionsIgnoreRecommendations": "확장 권장 사항 무시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..00f0122b10 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "Extensions 폴더 열기", + "installVSIX": "VSIX에서 설치...", + "InstallVSIXAction.success": "확장이 설치되었습니다. 사용하도록 설정하려면 다시 시작하세요.", + "InstallVSIXAction.reloadNow": "지금 다시 로드" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..4891a4814a --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "키 바인딩 간 충돌을 피하기 위해 다른 키 맵({0})을 사용하지 않도록 설정할까요?", + "yes": "예", + "no": "아니요", + "betterMergeDisabled": "Better Merge 확장이 이제 빌드되었습니다. 설치된 확장은 사용하지 않도록 설정되었으며 제거할 수 있습니다.", + "uninstall": "제거", + "later": "나중에" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..064930c983 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "마켓플레이스", + "installedExtensions": "설치됨", + "searchInstalledExtensions": "설치됨", + "recommendedExtensions": "권장", + "searchExtensions": "마켓플레이스에서 확장 검색", + "sort by installs": "정렬 기준: 설치 수", + "sort by rating": "정렬 기준: 등급", + "sort by name": "정렬 기준: 이름", + "suggestProxyError": "마켓플레이스에서 'ECONNREFUSED'를 반환했습니다. 'http.proxy' 설정을 확인하세요.", + "extensions": "확장", + "outdatedExtensions": "{0}개의 만료된 확장" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..cfec6265fc --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "확장", + "no extensions found": "확장을 찾을 수 없습니다.", + "suggestProxyError": "마켓플레이스에서 'ECONNREFUSED'를 반환했습니다. 'http.proxy' 설정을 확인하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json new file mode 100644 index 0000000000..29f21be1b2 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "키 바인딩 간 충돌을 피하기 위해 다른 키 맵을 사용하지 않도록 설정할까요?", + "yes": "예", + "no": "아니요" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..25de72d22a --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "'{0}'을(를) 사용하도록 설정하면 종속성도 사용하도록 설정됩니다. 계속하시겠습니까?", + "enable": "예", + "doNotEnable": "아니요", + "disableDependeciesConfirmation": "'{0}'만 사용하지 않도록 설정하시겠습니까, 아니면 종속성도 사용하지 않도록 설정하시겠습니까?", + "disableOnly": "만", + "disableAll": "모두", + "cancel": "취소", + "singleDependentError": "확장 '{0}'을(를) 사용하지 않도록 설정할 수 없습니다. 확장 '{1}'이(가) 이 확장에 종속됩니다.", + "twoDependentsError": "확장 '{0}'을(를) 사용하지 않도록 설정할 수 없습니다. 확장 '{1}' 및 '{2}'이(가) 이 확장에 종속됩니다.", + "multipleDependentsError": "확장 '{0}'을(를) 사용하지 않도록 설정할 수 없습니다. 확장 '{1}', '{2}' 등이 이 확장에 종속됩니다.", + "installConfirmation": "'{0}' 확장을 설치하시겠습니까?", + "install": "설치" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/kor/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..73ea1b4220 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "Tweet 피드백", + "label.sendASmile": "피드백을 트윗하세요.", + "patchedVersion1": "설치가 손상되었습니다.", + "patchedVersion2": "버그를 제출하는 경우 지정하세요.", + "sentiment": "사용 소감을 알려주세요.", + "smileCaption": "기쁨", + "frownCaption": "슬픔", + "other ways to contact us": "다른 문의 방법", + "submit a bug": "버그 제출", + "request a missing feature": "누락된 기능 요청", + "tell us why?": "이유를 알려 주세요.", + "commentsHeader": "설명", + "tweet": "Tweet", + "character left": "남은 문자", + "characters left": "남은 문자", + "feedbackSending": "보내는 중", + "feedbackSent": "감사합니다.", + "feedbackSendingError": "다시 시도하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..80f1acdbad --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "이진 파일 뷰어" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..0ea8615094 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "텍스트 파일 편집기", + "createFile": "파일 만들기", + "fileEditorWithInputAriaLabel": "{0}. 텍스트 파일 편집기입니다.", + "fileEditorAriaLabel": "텍스트 파일 편집기입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..8e219fd3dd --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "폴더" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..623d82f65d --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "파일", + "revealInSideBar": "세로 막대에 표시", + "acceptLocalChanges": "변경을 적용하고 디스크 콘텐츠 덮어쓰기", + "revertLocalChanges": "변경 내용을 취소하고 디스크의 콘텐츠로 되돌리기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..1c69af1b46 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "다시 시도", + "rename": "이름 바꾸기", + "newFile": "새 파일", + "newFolder": "새 폴더", + "openFolderFirst": "안에 파일이나 폴더를 만들려면 먼저 폴더를 엽니다.", + "newUntitledFile": "제목이 없는 새 파일", + "createNewFile": "새 파일", + "createNewFolder": "새 폴더", + "deleteButtonLabelRecycleBin": "휴지통으로 이동(&&M)", + "deleteButtonLabelTrash": "휴지통으로 이동(&&M)", + "deleteButtonLabel": "삭제(&&D)", + "dirtyMessageFolderOneDelete": "1개 파일에 저장되지 않은 변경 내용이 있는 폴더를 삭제하려고 합니다. 계속하시겠습니까?", + "dirtyMessageFolderDelete": "{0}개 파일에 저장되지 않은 변경 내용이 있는 폴더를 삭제하려고 합니다. 계속하시겠습니까?", + "dirtyMessageFileDelete": "저장되지 않은 변경 내용이 있는 파일을 삭제하려고 합니다. 계속하시겠습니까?", + "dirtyWarning": "변경 내용을 저장하지 않은 경우 변경 내용이 손실됩니다.", + "confirmMoveTrashMessageFolder": "'{0}'과(와) 해당 내용을 삭제할까요?", + "confirmMoveTrashMessageFile": "'{0}'을(를) 삭제할까요?", + "undoBin": "휴지통에서 복원할 수 있습니다.", + "undoTrash": "휴지통에서 복원할 수 있습니다.", + "confirmDeleteMessageFolder": "'{0}'과(와) 해당 내용을 영구히 삭제할까요?", + "confirmDeleteMessageFile": "'{0}'을(를) 영구히 삭제할까요?", + "irreversible": "이 작업은 취소할 수 없습니다.", + "permDelete": "영구히 삭제", + "delete": "삭제", + "importFiles": "파일 가져오기", + "confirmOverwrite": "이름이 같은 파일 또는 폴더가 대상 폴더에 이미 있습니다. 덮어쓸까요?", + "replaceButtonLabel": "바꾸기(&&R)", + "copyFile": "복사", + "pasteFile": "붙여넣기", + "duplicateFile": "중복", + "openToSide": "측면에서 열기", + "compareSource": "비교를 위해 선택", + "globalCompareFile": "활성 파일을 다음과 비교...", + "pickHistory": "비교할 이전에 연 파일 선택", + "unableToFileToCompare": "선택한 파일을 '{0}'과(와) 비교할 수 없습니다.", + "openFileToCompare": "첫 번째 파일을 열어서 다른 파일과 비교합니다.", + "compareWith": "'{0}'과(와) '{1}' 비교", + "compareFiles": "파일 비교", + "refresh": "새로 고침", + "save": "저장", + "saveAs": "다른 이름으로 저장...", + "saveAll": "모두 저장", + "saveAllInGroup": "그룹의 모든 항목 저장", + "saveFiles": "더티 파일 저장", + "revert": "파일 되돌리기", + "focusOpenEditors": "열려 있는 편집기 뷰에 포커스", + "focusFilesExplorer": "파일 탐색기에 포커스", + "showInExplorer": "세로 막대에서 활성 파일 표시", + "openFileToShow": "탐색기에 표시하려면 먼저 파일을 엽니다.", + "collapseExplorerFolders": "탐색기에서 폴더 축소", + "refreshExplorer": "탐색기 새로 고침", + "openFile": "파일 열기...", + "openFileInNewWindow": "새 창에서 활성 파일 열기", + "openFileToShowInNewWindow": "먼저 파일 한 개를 새 창에서 엽니다.", + "revealInWindows": "탐색기에 표시", + "revealInMac": "Finder에 표시", + "openContainer": "상위 폴더 열기", + "revealActiveFileInWindows": "Windows 탐색기에 활성 파일 표시", + "revealActiveFileInMac": "Finder에 활성 파일 표시", + "openActiveFileContainer": "활성 파일의 상위 폴더 열기", + "copyPath": "경로 복사", + "copyPathOfActive": "활성 파일의 경로 복사", + "emptyFileNameError": "파일 또는 폴더 이름을 입력해야 합니다.", + "fileNameExistsError": "파일 또는 폴더 **{0}**이(가) 이 위치에 이미 있습니다. 다른 이름을 선택하세요.", + "invalidFileNameError": "**{0}**(이)라는 이름은 파일 또는 폴더 이름으로 올바르지 않습니다. 다른 이름을 선택하세요.", + "filePathTooLongError": "**{0}**(이)라는 이름을 사용하면 경로가 너무 길어집니다. 짧은 이름을 선택하세요.", + "compareWithSaved": "활성 파일을 저장된 파일과 비교", + "modifiedLabel": "{0}(디스크) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..58ae6bd92b --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "첫 번째 파일을 열어서 경로를 복사합니다.", + "openFileToReveal": "첫 번째 파일을 열어서 나타냅니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..f7eb600f43 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "탐색기 표시", + "explore": "탐색기", + "view": "보기", + "textFileEditor": "텍스트 파일 편집기", + "binaryFileEditor": "이진 파일 편집기", + "filesConfigurationTitle": "파일", + "exclude": "파일과 폴더를 제외하기 위한 GLOB 패턴을 구성합니다.", + "files.exclude.boolean": "파일 경로를 일치시킬 GLOB 패턴입니다. 패턴을 사용하거나 사용하지 않도록 설정하려면 true 또는 false로 설정하세요.", + "files.exclude.when": "일치하는 파일의 형제에 대한 추가 검사입니다. $(basename)을 일치하는 파일 이름에 대한 변수로 사용하세요.", + "associations": "파일과 언어의 연결을 구성하세요(예: \"*.extension\": \"html\"). 이러한 구성은 설치된 언어의 기본 연결보다 우선 순위가 높습니다.", + "encoding": "파일을 읽고 쓸 때 사용할 기본 문자 집합 인코딩입니다.", + "autoGuessEncoding": "사용하도록 설정하는 경우 파일을 열 때 문자 집합 인코딩을 추측합니다.", + "eol": "줄 바꿈 문자의 기본 끝입니다. LF에는 \\n, CRLF에는 \\r\\n을 사용하세요.", + "trimTrailingWhitespace": "사용하도록 설정되면 파일을 저장할 때 후행 공백이 잘립니다.", + "insertFinalNewline": "사용하도록 설정되면 저장할 때 파일 끝에 마지막 줄바꿈을 삽입합니다.", + "files.autoSave.off": "더티 파일이 자동으로 저장되지 않습니다.", + "files.autoSave.afterDelay": "'files.autoSaveDelay' 구성 후 더티 파일이 자동으로 저장됩니다.", + "files.autoSave.onFocusChange": "편집기가 포커스를 잃으면 더티 파일이 자동으로 저장됩니다.", + "files.autoSave.onWindowChange": "창이 포커스를 잃으면 더티 파일이 자동으로 저장됩니다.", + "autoSave": "더티 파일 자동 저장을 제어합니다. 허용되는 값은 '{0}', '{1}', '{2}'(편집기가 포커스를 잃음), '{3}'(창이 포커스를 잃음)입니다. '{4}'(으)로 설정하는 경우 'files.autoSaveDelay'에서 지연을 구성할 수 있습니다.", + "autoSaveDelay": "더티 파일을 자동으로 저장할 때까지의 지연(밀리초)을 제어합니다. 'files.autoSave'를 '{0}'(으)로 설정한 경우에만 적용됩니다.", + "watcherExclude": "파일 감시에서 제외할 파일 경로의 GLOB 패턴을 구성하세요. 패턴은 절대 경로(**접두사가 있는 경로 또는 전체 경로)여야 합니다. 이 설정을 변경하려면 다시 시작해야 합니다. 시작 시 Code에서 CPU 시간을 많이 차지하면 대용량 폴더를 제외하여 초기 로드를 줄일 수 있습니다.", + "hotExit.off": "핫 종료를 사용하지 않습니다.", + "hotExit.onExit": "핫 종료는 응용 프로그램을 닫을 때 트리거됩니다. 즉 Windows/Linux에서 마지막 창을 닫을 때나 workbench.action.quit 명령이 트리거될 때(명령 팔레트, 키 바인딩, 메뉴)입니다. 다음 실행 시 백업을 포함한 모든 창이 복원됩니다.", + "hotExit.onExitAndWindowClose": "핫 종료는 응용 프로그램을 닫을 때 트리거됩니다. 즉 Windows/Linux에서 마지막 창을 닫을 때나 workbench.action.quit 명령이 트리거될 때(명령 팔레트, 키 바인딩, 메뉴), 또한 마지막 창인지 여부에 상관없이 폴더가 열린 모든 창의 경우입니다. 열린 폴더가 없는 모든 창은 다음 실행 시 복원됩니다. 종료되기 전 상태로 폴더 창을 복원하려면 \"window.restoreWindows\"를 \"all\"로 설정합니다.", + "hotExit": "저장하지 않은 파일을 세션 간에 기억하여, 편집기를 종료할 때 저장할지 묻는 메시지를 건너뛸지 여부를 제어합니다.", + "useExperimentalFileWatcher": "새로운 실험 파일 감시자를 사용하세요.", + "defaultLanguage": "새 파일에 할당되는 기본 언어 모드입니다.", + "editorConfigurationTitle": "편집기", + "formatOnSave": "파일 저장 시 서식을 지정합니다. 포맷터를 사용할 수 있어야 하며, 파일이 자동으로 저장되지 않아야 하고, 편집기가 종료되지 않아야 합니다.", + "explorerConfigurationTitle": "파일 탐색기", + "openEditorsVisible": "열려 있는 편집기 창에 표시되는 편집기 수입니다. 창을 숨기려면 0으로 설정합니다.", + "dynamicHeight": "열려 있는 편집기 섹션의 높이가 요소 수에 따라 동적으로 조정되는지 여부를 제어합니다.", + "autoReveal": "탐색기에서 파일을 열 때 자동으로 표시하고 선택할지를 제어합니다.", + "enableDragAndDrop": "탐색기에서 끌어서 놓기를 통한 파일 및 폴더 이동을 허용하는지를 제어합니다.", + "sortOrder.default": "파일 및 폴더가 이름을 기준으로 사전순으로 정렬됩니다. 폴더가 파일 앞에 표시됩니다.", + "sortOrder.mixed": "파일 및 폴더가 이름을 기준으로 사전순으로 정렬됩니다. 파일이 폴더와 결합됩니다.", + "sortOrder.filesFirst": "파일 및 폴더가 이름을 기준으로 사전순으로 정렬됩니다. 파일이 폴더 앞에 표시됩니다.", + "sortOrder.type": "파일 및 폴더가 확장명을 기준으로 사전순으로 정렬됩니다. 폴더가 파일 앞에 표시됩니다.", + "sortOrder.modified": "파일 및 폴더가 마지막으로 수정한 날짜를 기준으로 내림차순 정렬됩니다. 폴더가 파일 앞에 표시됩니다.", + "sortOrder": "탐색기에서 파일 및 폴더의 정렬 순서를 제어합니다. 기본 정렬 외에 순서를 'mixed'(파일 및 폴더가 결합되어 정렬), 'type'(파일 형식 기준), 'modified'(마지막으로 수정한 날짜 기준) 또는 'filesFirst'(파일을 폴더 앞에 정렬)로 설정할 수 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..90f1b25b05 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "삭제", + "overwrite": "덮어쓰기", + "retry": "다시 시도", + "readonlySaveError": "'{0}'을(를) 저장하지 못했습니다. 파일이 쓰기 보호되어 있습니다. 보호를 제거하려면 '덮어쓰기'를 선택하세요.", + "genericSaveError": "'{0}'을(를) 저장하지 못했습니다. {1}", + "staleSaveError": "'{0}'을(를) 저장하지 못했습니다. 디스크의 내용이 최신 버전입니다. 버전을 디스크에 있는 버전과 비교하려면 **비교**를 클릭하세요.", + "compareChanges": "비교", + "saveConflictDiffLabel": "{0}(디스크에 있음) ↔ {1}({2}에 있음) - 저장 충돌 해결", + "userGuide": "오른쪽 편집기 도구 모음의 작업을 사용하여 변경 내용을 **실행 취소**하거나 디스크의 콘텐츠를 변경 내용으로 **덮어쓰기**" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..b01e2c49ec --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "열린 폴더 없음", + "explorerSection": "파일 탐색기 섹션", + "noWorkspaceHelp": "아직 폴더를 열지 않았습니다.", + "openFolder": "폴더 열기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..341b795121 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "파일 탐색기 섹션", + "treeAriaLabel": "파일 탐색기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..4a1c86d786 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "파일 이름을 입력합니다. 확인하려면 Enter 키를 누르고, 취소하려면 Esc 키를 누릅니다.", + "filesExplorerViewerAriaLabel": "{0}, 파일 탐색기", + "dropFolders": "작업 영역에 폴더를 추가하시겠습니까?", + "dropFolder": "작업 영역에 폴더를 추가하시겠습니까?", + "addFolders": "폴더 추가(&&A)", + "addFolder": "폴더 추가(&&A)", + "confirmOverwriteMessage": "'{0}'이(가) 대상 폴더에 이미 있습니다. 바꿀까요?", + "irreversible": "이 작업은 취소할 수 없습니다.", + "replaceButtonLabel": "바꾸기(&&R)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..c79ba58148 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "열려 있는 편집기", + "openEditosrSection": "열려 있는 편집기 섹션", + "treeAriaLabel": "열린 편집기: 활성 파일 목록", + "dirtyCounter": "{0}이(가) 저장되지 않음" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..07ee243e6d --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}, 편집기 그룹", + "openEditorAriaLabel": "{0}, 편집기 열기", + "saveAll": "모두 저장", + "closeAllUnmodified": "수정되지 않은 항목 닫기", + "closeAll": "모두 닫기", + "compareWithSaved": "저장된 항목과 비교", + "close": "닫기", + "closeOthers": "기타 항목 닫기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/kor/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..d2b58993f6 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0}개의 저장되지 않은 파일" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/kor/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..32c2e3e166 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (deleted from disk)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json new file mode 100644 index 0000000000..d786c06cbe --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "switchToChangesView": "변경 내용 보기로 전환", + "openInEditor": "편집기 보기로 전환", + "workbenchStage": "스테이징", + "workbenchUnstage": "스테이징 취소", + "stageSelectedLines": "선택한 줄 스테이징", + "unstageSelectedLines": "선택한 줄 스테이징 취소", + "revertSelectedLines": "선택한 줄 되돌리기", + "confirmRevertMessage": "선택한 변경 내용을 되돌리시겠습니까?", + "irreversible": "이 작업은 취소할 수 없습니다.", + "revertChangesLabel": "변경 내용 되돌리기(&&R)", + "openChange": "변경 내용 열기", + "openFile": "파일 열기", + "git": "Git" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/gitActions.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/gitActions.i18n.json new file mode 100644 index 0000000000..73b5218904 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/gitActions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openChange": "변경 내용 열기", + "openFile": "파일 열기", + "init": "Init", + "refresh": "새로 고침", + "stageChanges": "스테이징", + "stageAllChanges": "모두 스테이징", + "confirmUndoMessage": "모든 변경 내용을 정리할까요?", + "confirmUndoAllOne": "{0} 파일에 스테이징되지 않은 변경 내용이 있습니다.\n\n이 작업은 취소할 수 없습니다.", + "confirmUndoAllMultiple": "{0}개 파일에 스테이징되지 않은 변경 내용이 있습니다.\n\n이 작업은 취소할 수 없습니다.", + "cleanChangesLabel": "변경 내용 정리(&&C)", + "confirmUndo": "'{0}'의 변경 내용을 정리할까요?", + "irreversible": "이 작업은 취소할 수 없습니다.", + "undoChanges": "정리", + "undoAllChanges": "모두 정리", + "unstage": "스테이징 취소", + "unstageAllChanges": "모두 스테이징 취소", + "dirtyTreeCheckout": "체크 아웃할 수 없습니다. 먼저 작업을 커밋하거나 스태시하세요.", + "commitStaged": "스테이징된 항목 커밋", + "commitStagedAmend": "스테이징된 항목 커밋(수정)", + "commitStagedSignedOff": "스테이징된 항목 커밋(로그오프됨)", + "commit": "Commit", + "commitMessage": "커밋 메시지", + "commitAll": "모두 커밋", + "commitAllSignedOff": "모두 커밋(로그오프됨)", + "commitAll2": "모두 커밋", + "commitStaged2": "스테이징된 항목 커밋", + "dirtyTreePull": "끌어올 수 없습니다. 먼저 작업을 커밋하거나 스태시하세요.", + "authFailed": "원격 git에 대한 인증이 실패했습니다.", + "pushToRemote": "다음으로 푸시...", + "pushToRemotePickMessage": "'{0}' 분기를 푸시할 원격 선택:", + "publish": "게시", + "confirmPublishMessage": "'{0}'을(를) '{1}'에 게시할까요?", + "confirmPublishMessageButton": "게시(&&P)", + "publishPickMessage": "'{0}' 분기를 다음에 게시하려면 원격을 선택하세요.", + "sync is unpredictable": "이 작업은 '{0}' 간에 커밋을 푸시하고 풀합니다.", + "ok": "확인", + "cancel": "취소", + "never again": "다시 표시 안 함", + "undoLastCommit": "마지막 커밋 실행 취소" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json new file mode 100644 index 0000000000..17968faf31 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refAriaLabel": "{0}, git", + "checkoutBranch": "{0}의 분기", + "checkoutRemoteBranch": "{0}에서 원격 분기", + "checkoutTag": "{0}의 태그", + "alreadyCheckedOut": "{0} 분기는 이미 현재 분기입니다.", + "branchAriaLabel": "{0}, git 분기", + "createBranch": "분기 {0} 만들기", + "noBranches": "다른 분기 없음", + "notValidBranchName": "유효한 분기 이름을 제공하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/gitServices.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/gitServices.i18n.json new file mode 100644 index 0000000000..252fb5d071 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/gitServices.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cantOpen": "이 Git 리소스를 열 수 없습니다.", + "gitIndexChanges": "{0}(index) ↔ {1}", + "gitIndexChangesDesc": "{0} - 인덱스 변경 내용", + "gitIndexChangesRenamed": "{0} ← {1}", + "gitIndexChangesRenamedDesc": "{0} - 이름 바꿈 - 인덱스 변경 내용", + "workingTreeChanges": "{0} (HEAD) ↔ {1}", + "workingTreeChangesDesc": "{0} - 작업 트리 변경 내용", + "gitMergeChanges": "{0}(병합) ↔ {1}", + "gitMergeChangesDesc": "{0} - 변경 내용 병합", + "updateGit": "Git {0}이(가) 설치된 것 같습니다. Code는 2.0.0 이하의 Git에서 최적으로 작동합니다.", + "download": "다운로드", + "neverShowAgain": "다시 표시 안 함", + "configureUsernameEmail": "git 사용자 이름 및 메일을 구성하세요.", + "badConfigFile": "Git {0}", + "unmergedChanges": "변경 내용을 커밋하기 전에 먼저 병합되지 않은 변경 내용을 확인해야 합니다.", + "showOutput": "출력 표시", + "cancel": "취소", + "checkNativeConsole": "git 작업을 실행하는 동안 문제가 발생했습니다. 출력을 검토하거나 콘솔을 사용하여 리포지토리의 상태를 확인하세요.", + "changesFromIndex": "{0}(index)", + "changesFromIndexDesc": "{0} - 인덱스 변경 내용", + "changesFromTree": "{0}({1})", + "changesFromTreeDesc": "{0} - {1} 변경 내용", + "cantOpenResource": "이 Git 리소스를 열 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json new file mode 100644 index 0000000000..065a4c7db7 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "publishBranch": "분기 게시", + "syncBranch": "변경 내용 동기화", + "gitNotEnabled": "이 작업 영역에서 Git을 사용하도록 설정되어 있지 않습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json new file mode 100644 index 0000000000..a998f6bf65 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gitProgressBadge": "실행 중인 Git 상태", + "gitPendingChangesBadge": "{0}개의 보류 중인 변경 내용", + "toggleGitViewlet": "Git 표시", + "git": "Git", + "view": "보기", + "gitCommands": "Git 명령", + "gitConfigurationTitle": "Git", + "gitEnabled": "git를 사용하도록 설정할까요?", + "gitPath": "Git 실행 파일의 경로", + "gitAutoRefresh": "자동 새로 고침 사용 여부", + "gitAutoFetch": "자동 가져오기를 사용하도록 설정했는지 여부입니다.", + "gitLongCommit": "긴 커밋 메시지에 대해 경고할지 여부입니다.", + "gitLargeRepos": "항상 대규모 리포지토리가 Code로 관리되도록 허용합니다.", + "confirmSync": "Git 리포지토리를 동기화하기 전에 확인합니다.", + "countBadge": "Git 배지 카운터를 제어합니다.", + "checkoutType": "나열되는 분기 유형을 제어합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json new file mode 100644 index 0000000000..5074693951 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "needMessage": "커밋 메시지를 입력하세요. 언제든지 **{0}**을(를) 눌러 변경 내용을 커밋할 수 있습니다. 스테이징된 변경 내용이 있으면 해당 변경 내용만 커밋되고 그렇지 않으면 모든 변경 내용이 커밋됩니다.", + "nothingToCommit": "커밋할 변경 내용이 있으면 커밋 메시지를 입력하고 **{0}**을(를) 눌러 변경 내용을 커밋하세요. 스테이징된 변경 내용이 있으면 해당 변경 내용만 커밋되고 그렇지 않으면 모든 변경 내용이 커밋됩니다.", + "longCommit": "커밋의 첫 번째 줄은 50자 미만으로 유지하는 것이 좋습니다. 추가 정보를 위해 자유롭게 더 많은 줄을 사용하세요.", + "commitMessage": "Message (press {0} to commit)", + "commitMessageAriaLabel": "Git: 커밋하려면 커밋 메시지를 입력하고 {0}을(를) 누르세요.", + "treeAriaLabel": "Git 변경 내용 보기", + "showOutput": "Git 출력 표시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json new file mode 100644 index 0000000000..3466272bea --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stagedChanges": "스테이징된 변경 내용", + "allChanges": "변경 내용", + "mergeChanges": "변경 내용 병합", + "outsideOfWorkspace": "이 파일은 현재 작업 영역의 외부에 있습니다.", + "modified-char": "M", + "added-char": "A", + "deleted-char": "D", + "renamed-char": "R", + "copied-char": "C", + "untracked-char": "U", + "ignored-char": "!", + "title-index-modified": "인덱스에서 수정", + "title-modified": "수정됨", + "title-index-added": "인덱스에 추가", + "title-index-deleted": "인덱스에서 삭제", + "title-deleted": "삭제됨", + "title-index-renamed": "인덱스에서 이름 변경", + "title-index-copied": "인덱스에 복사", + "title-untracked": "추적되지 않음", + "title-ignored": "무시됨", + "title-conflict-both-deleted": "충돌: 양쪽에서 삭제", + "title-conflict-added-by-us": "충돌: 자체 추가", + "title-conflict-deleted-by-them": "충돌: 타인이 삭제", + "title-conflict-added-by-them": "충돌: 타인이 추가", + "title-conflict-deleted-by-us": "충돌: 자체 삭제", + "title-conflict-both-added": "충돌: 양쪽에서 추가", + "title-conflict-both-modified": "충돌: 양쪽에서 수정", + "fileStatusAriaLabel": "{1} 폴더에 있는 {0} 파일의 상태는 {2}입니다. Git", + "ariaLabelStagedChanges": "스테이징된 변경 내용, Git", + "ariaLabelChanges": "변경, Git", + "ariaLabelMerge": "병합, Git" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json new file mode 100644 index 0000000000..5764829257 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disabled": "Git가 설정에서 사용하지 않도록 지정되어 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json new file mode 100644 index 0000000000..eaa540adba --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noGit": "이 작업 영역에는 아직 Git 소스 제어가 적용되지 않습니다.", + "gitinit": "Git 리포지토리 초기화" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json new file mode 100644 index 0000000000..a76faa64d9 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "macInstallWith": "터미널 프롬프트에 {3}을(를) 입력하여 {0}과(와) 함께 설치하거나 {1}에서 다운로드하거나 {2} 명령줄 개발자 도구를 설치할 수 있습니다.", + "winInstallWith": "{0}에서 설치하거나 {1}에서 다운로드할 수 있습니다.", + "linuxDownloadFrom": "{0}에서 다운로드할 수 있습니다.", + "downloadFrom": "{0}에서 다운로드할 수 있습니다.", + "looksLike": "git가 시스템에 설치되지 않은 것 같습니다.", + "pleaseRestart": "Git이 설치되면 VSCode를 다시 시작하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json new file mode 100644 index 0000000000..8ee82f8aed --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "huge": "리포지토리에 활성 변경 내용이 많은 것 같습니다.\nCode가 매우 느려질 수 있습니다.", + "setting": "다음 설정을 사용하여 이 경고를 영구적으로 사용하지 않을 수 있습니다.", + "allo": "대규모 리포지토리 허용" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json new file mode 100644 index 0000000000..b5a1f15145 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrongRoot": "이 디렉터리가 git 리포지토리에 포함되어 있는 것 같습니다.", + "pleaseRestart": "Git 기능에 액세스하려면 리포지토리의 루트 디렉터리를 엽니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json b/i18n/kor/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json new file mode 100644 index 0000000000..e98724b2c8 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspaceHelp": "아직 폴더를 열지 않았습니다.", + "pleaseRestart": "Git 기능에 액세스하려면 Git 리포지토리에서 폴더를 엽니다.", + "openFolder": "폴더 열기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json new file mode 100644 index 0000000000..f5278bb231 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSCMViewlet": "SCM 표시", + "git": "Git" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json b/i18n/kor/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json new file mode 100644 index 0000000000..8e305ef5de --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "valid": "올바른 Git 리포지토리 URL을 입력하세요.", + "url": "리포지토리 URL", + "directory": "대상 복제 디렉터리", + "cloning": "'{0}' 리포지토리를 복제하는 중...", + "already exists": "대상 리포지토리가 이미 있습니다. 복제할 다른 디렉터리를 선택하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json b/i18n/kor/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json new file mode 100644 index 0000000000..91fbae2148 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "git": "Git" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/git/node/git.lib.i18n.json b/i18n/kor/src/vs/workbench/parts/git/node/git.lib.i18n.json new file mode 100644 index 0000000000..0792f2cfff --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/git/node/git.lib.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorBuffer": "git에서 파일을 열 수 없습니다.", + "fileBinaryError": "파일이 이진인 것 같으므로 테스트로 열 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..4161660847 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "Html 미리 보기", + "devtools.webview": "개발자: Webview 도구" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/kor/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..4c9a1969af --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "잘못된 편집기 입력입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/kor/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..73ae1f9127 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "개발자: Webview 도구" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/kor/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..c101a0917e --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "보기", + "problems.view.show.label": "문제 표시", + "problems.panel.configuration.title": "문제 보기", + "problems.panel.configuration.autoreveal": "문제 보기를 열 때 문제 보기에 자동으로 파일이 표시되어야 하는지를 제어합니다.", + "markers.panel.title.problems": "문제", + "markers.panel.aria.label.problems.tree": "파일별로 그룹화된 문제", + "markers.panel.no.problems.build": "지금까지 작업 영역에서 문제가 감지되지 않았습니다.", + "markers.panel.no.problems.filters": "제공된 필터 기준으로 결과를 찾을 수 없습니다.", + "markers.panel.action.filter": "문제 필터링", + "markers.panel.filter.placeholder": "형식 또는 텍스트로 필터링", + "markers.panel.filter.errors": "오류", + "markers.panel.filter.warnings": "경고", + "markers.panel.filter.infos": "정보", + "markers.panel.single.error.label": "오류 1개", + "markers.panel.multiple.errors.label": "오류 {0}개", + "markers.panel.single.warning.label": "경고 1개", + "markers.panel.multiple.warnings.label": "경고 {0}개", + "markers.panel.single.info.label": "정보 1개", + "markers.panel.multiple.infos.label": "정보 {0}개", + "markers.panel.single.unknown.label": "알 수 없음 1개", + "markers.panel.multiple.unknowns.label": "알 수 없음 {0}개", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "{0}에 {1}개의 문제가 있음", + "problems.tree.aria.label.error.marker": "{0}에 의해 오류 발생: 줄 {2} 및 문자 {3}의 {1}", + "problems.tree.aria.label.error.marker.nosource": "오류: 줄 {1} 및 문자 {2}의 {0}", + "problems.tree.aria.label.warning.marker": "{0}에 의해 경고 발생: 줄 {2} 및 문자 {3}의 {1}", + "problems.tree.aria.label.warning.marker.nosource": "경고: 줄 {1} 및 문자 {2}의 {0}", + "problems.tree.aria.label.info.marker": "{0}에 의해 정보 생성됨: 줄 {2} 및 문자 {3}의 {1}", + "problems.tree.aria.label.info.marker.nosource": "정보: 줄 {1} 및 문자 {2}의 {0}", + "problems.tree.aria.label.marker": "{0}에 의해 문제 발생: 줄 {2} 및 문자 {3}의 {1}", + "problems.tree.aria.label.marker.nosource": "문제: 줄 {1} 및 문자 {2}의 {0}", + "errors.warnings.show.label": "오류 및 경고 표시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/kor/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..4079a384de --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "복사" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..5b42d7cf36 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "간단한 피드백 설문 조사에 참여하시겠어요?", + "takeSurvey": "설문 조사 참여", + "remindLater": "나중에 알림", + "neverAgain": "다시 표시 안 함" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..d6f9a6203c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "출력", + "viewCategory": "보기", + "clearOutput.label": "출력 내용 지우기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/kor/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..e735551eaa --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "출력 설정/해제", + "clearOutput": "출력 내용 지우기", + "toggleOutputScrollLock": "출력 스크롤 잠금 설정/해제", + "switchToOutput.label": "출력으로 전환" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/kor/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..ca1a617cac --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0}, 출력 패널", + "outputPanelAriaLabel": "출력 패널" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/kor/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..11eb29e16f --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "출력", + "channel": "'{0}'의 경우" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..25f2659a35 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "느린 시작 감지됨", + "slow.detail": "방금 느리게 시작되었습니다. 프로파일링을 사용하도록 설정한 상태로 '{0}'을(를) 다시 시작하세요. 프로필을 공유해 주시면 다시 빠르게 시작될 수 있도록 최선을 다하겠습니다.", + "prof.message": "프로필을 만들었습니다.", + "prof.detail": "문제를 발생시키고 다음 파일을 수동으로 첨부하세요.\n{0}", + "prof.restartAndFileIssue": "문제 만들기 및 다시 시작", + "prof.restart": "다시 시작", + "prof.thanks": "도움을 주셔서 감사합니다.", + "prof.detail.restart": "계속 '{0}'을(를) 사용하려면 마지막으로 다시 시작해야 합니다. 기여해 주셔서 다시 한번 감사드립니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..7d3319b35c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "원하는 키 조합을 입력하고 키를 누릅니다. 취소하려면 키를 누릅니다.", + "defineKeybinding.chordsTo": "현" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..4f67e461a3 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "바로 가기 키(&&K)", + "SearchKeybindings.AriaLabel": "키 바인딩 검색", + "SearchKeybindings.Placeholder": "키 바인딩 검색", + "sortByPrecedene": "우선 순위별 정렬", + "header-message": "고급 사용자 지정의 경우 다음 파일을 열고 편집하세요.", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "키 바인딩", + "changeLabel": "키 바인딩 변경", + "addLabel": "키 바인딩 추가", + "removeLabel": "키 바인딩 제거", + "resetLabel": "키 바인딩 다시 설정", + "showConflictsLabel": "충돌 표시", + "copyLabel": "복사", + "error": "키 바인딩을 편집하는 동안 오류 '{0}'이(가) 발생했습니다. 'keybindings.json' 파일을 열고 확인하세요.", + "command": "명령", + "keybinding": "키 바인딩", + "source": "소스", + "when": "시기", + "editKeybindingLabelWithKey": "키 바인딩 {0} 변경", + "editKeybindingLabel": "키 바인딩 변경", + "addKeybindingLabelWithKey": "키 바인딩 {0} 추가", + "addKeybindingLabel": "키 바인딩 추가", + "commandAriaLabel": "명령은 {0}입니다.", + "keybindingAriaLabel": "키 바인딩은 {0}입니다.", + "noKeybinding": "키 바인딩이 할당되지 않았습니다.", + "sourceAriaLabel": "소스는 {0}입니다.", + "whenAriaLabel": "{0}인 경우", + "noWhen": "컨텍스트인 경우 아니요" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..19f172ea6d --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "키 바인딩 정의", + "defineKeybinding.kbLayoutErrorMessage": "현재 자판 배열에서는 이 키 조합을 생성할 수 없습니다.", + "defineKeybinding.kbLayoutLocalAndUSMessage": "현재 자판 배열의 경우 **{0}**입니다(**{1}**: 미국 표준).", + "defineKeybinding.kbLayoutLocalMessage": "현재 자판 배열의 경우 **{0}**입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..e71e92cbb2 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "기본 설정 편집기", + "keybindingsEditor": "키 바인딩 편집기", + "preferences": "기본 설정" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..97376da266 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "사용자 설정 열기", + "openGlobalKeybindings": "바로 가기 키 열기", + "openGlobalKeybindingsFile": "바로 가기 키 파일 열기", + "openWorkspaceSettings": "작업 영역 설정 열기", + "openFolderSettings": "폴더 설정 열기", + "pickFolder": "폴더 선택", + "configureLanguageBasedSettings": "언어별 설정 구성...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "언어 선택" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..ee933f2f17 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "기본 설정", + "SearchSettingsWidget.AriaLabel": "설정 검색", + "SearchSettingsWidget.Placeholder": "설정 검색", + "totalSettingsMessage": "총 {0}개 설정", + "noSettingsFound": "결과 없음", + "oneSettingFound": "1개 설정 일치함", + "settingsFound": "{0}개 설정 일치함", + "fileEditorWithInputAriaLabel": "{0}. 텍스트 파일 편집기입니다.", + "fileEditorAriaLabel": "텍스트 파일 편집기입니다.", + "preferencesAriaLabel": "기본 설정. 읽기 전용 텍스트 편집기입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..c729477e5a --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "설정을 여기에 넣어서 기본 설정을 덮어씁니다.", + "errorInvalidConfiguration": "설정에 쓸 수 없습니다. 파일에서 오류/경고를 해결하고 다시 시도하세요.", + "emptyWorkspaceSettingsHeader": "설정을 여기에 넣어서 사용자 설정을 덮어씁니다.", + "emptyFolderSettingsHeader": "폴더 설정을 여기에 넣어서 작업 영역 설정을 덮어씁니다.", + "defaultFolderSettingsTitle": "기본 폴더 설정", + "defaultSettingsTitle": "기본 설정", + "noSettingsFound": "설정을 찾을 수 없습니다.", + "editTtile": "편집", + "replaceDefaultValue": "설정에서 바꾸기", + "copyDefaultValue": "설정에 복사", + "unsupportedPHPExecutablePathSetting": "이 설정은 사용자 설정이어야 합니다. 작업 영역에 대해 PHP를 구성하려면 PHP 파일을 열고 상태 표시줄에서 'PHP 경로'를 클릭합니다.", + "unsupportedWorkspaceSetting": "이 설정은 사용자 설정이어야 합니다.", + "unsupportedWorkbenchSetting": "이 설정은 지금 적용할 수 없으며 이 폴더를 직접 열 경우에 적용됩니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..8b687518cc --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "첫 번째 폴더를 열어서 작업 영역 설정을 만듭니다.", + "emptyKeybindingsHeader": "키 바인딩을 이 파일에 넣어서 기본값을 덮어씁니다.", + "defaultKeybindings": "기본 키 바인딩", + "folderSettingsName": "{0}(폴더 설정)", + "fail.createSettings": "{0}'({1})을(를) 만들 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..83d18e6d63 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "폴더 설정" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..67299507f0 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "기본값", + "user": "사용자", + "meta": "메타", + "option": "옵션" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..7788158b25 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "사용자 설정", + "workspaceSettingsTarget": "작업 영역 설정" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..9057b62831 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "일반적으로 사용되는 설정", + "noSettings": "설정 없음", + "defaultKeybindingsHeader": "키 바인딩을 키 바인딩 파일에 배치하여 덮어씁니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..6cf3ea2a77 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "모든 명령 표시", + "clearCommandHistory": "명령 기록 지우기", + "showCommands.label": "명령 팔레트...", + "entryAriaLabelWithKey": "{0}, {1}, 명령", + "entryAriaLabel": "{0}, 명령", + "canNotRun": "'{0}' 명령은 여기에서 실행할 수 없습니다.", + "actionNotEnabled": "'{0}' 명령은 현재 컨텍스트에서 사용할 수 없습니다.", + "recentlyUsed": "최근에 사용한 항목", + "morecCommands": "기타 명령", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "일치하는 명령 없음" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..70e5d60d33 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "줄 이동...", + "gotoLineLabelEmptyWithLimit": "이동할 1과 {0} 사이의 줄 번호 입력", + "gotoLineLabelEmpty": "이동할 줄 번호 입력", + "gotoLineColumnLabel": "줄 {0} 및 문자 {1}(으)로 이동", + "gotoLineLabel": "줄 {0}(으)로 이동", + "gotoLineHandlerAriaLabel": "이동할 줄 번호를 입력합니다.", + "cannotRunGotoLine": "첫 번째 텍스트 파일을 열어서 줄로 이동합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..131ec6112c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "파일의 기호로 이동...", + "symbols": "기호({0})", + "method": "메서드({0})", + "function": "함수({0})", + "_constructor": "생성자({0})", + "variable": "변수({0})", + "class": "클래스({0})", + "interface": "인터페이스({0})", + "namespace": "네임스페이스({0})", + "package": "패키지({0})", + "modules": "모듈({0})", + "property": "속성({0})", + "enum": "열거형({0})", + "string": "문자열({0})", + "rule": "규칙({0})", + "file": "파일({0})", + "array": "배열({0})", + "number": "구성원({0})", + "boolean": "부울({0})", + "object": "개체({0})", + "key": "키({0})", + "entryAriaLabel": "{0}, 기호", + "noSymbolsMatching": "일치하는 기호 없음", + "noSymbolsFound": "기호를 찾을 수 없음", + "gotoSymbolHandlerAriaLabel": "입력하여 현재 활성화된 편집기의 기호를 축소합니다.", + "cannotRunGotoSymbolInFile": "파일에 대한 기호 정보가 없습니다.", + "cannotRunGotoSymbol": "첫 번째 텍스트 파일을 열어서 기호로 이동합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..1567cc581a --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, 선택기 도움말", + "globalCommands": "전역 명령", + "editorCommands": "편집기 명령" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..89c5adc955 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "명령 표시 및 실행", + "gotoLineDescriptionMac": "줄 이동", + "gotoLineDescriptionWin": "줄 이동", + "gotoSymbolDescription": "파일의 기호로 이동", + "gotoSymbolDescriptionScoped": "범주로 파일의 기호로 이동", + "helpDescription": "도움말 표시", + "viewPickerDescription": "뷰 열기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..5d80517166 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, 뷰 선택기", + "views": "뷰", + "panels": "패널", + "terminals": "터미널", + "terminalTitle": "{0}: {1}", + "channels": "출력", + "openView": "뷰 열기", + "quickOpenView": "Quick Open 뷰" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..14bd55bf3e --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "설정이 변경되어 다시 시작해야만 적용됩니다.", + "relaunchSettingDetail": "[다시 시작] 단추를 눌러 {0}을(를) 다시 시작하고 설정을 사용하도록 설정하세요.", + "restart": "다시 시작", + "relaunchWorkspaceMessage": "이 작업 영역을 변경하려면 확장 시스템을 다시 로드해야 합니다.", + "reload": "다시 로드" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/kor/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..ace0c80275 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "수정된 줄의 편집기 여백 배경색입니다.", + "editorGutterAddedBackground": "추가된 줄의 편집기 여백 배경색입니다.", + "editorGutterDeletedBackground": "삭제된 줄의 편집기 여백 배경색입니다.", + "overviewRulerModifiedForeground": "수정된 콘텐츠의 개요 눈금자 마커 색입니다.", + "overviewRulerAddedForeground": "추가된 콘텐츠의 개요 눈금자 마커 색입니다.", + "overviewRulerDeletedForeground": "삭제된 콘텐츠의 개요 눈금자 마커 색입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..cbc4bf28c1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "Git 표시", + "source control": "소스 제어", + "toggleSCMViewlet": "SCM 표시", + "view": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..a76a136b2e --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0}개의 보류 중인 변경 내용" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..6122d4afe2 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "추가 SCM 공급자 설치...", + "switch provider": "SCM 공급자 전환..." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..10241793d4 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Message (press {0} to commit)", + "installAdditionalSCMProviders": "추가 SCM 공급자 설치...", + "no open repo": "활성 상태인 소스 제어가 없습니다.", + "source control": "소스 제어", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..0f47032c56 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "파일 및 기호 결과", + "fileResults": "파일 결과" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..accf97bd56 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, 파일 선택기", + "searchResults": "검색 결과" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..27a11a6827 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, 기호 선택기", + "symbols": "기호 결과", + "noSymbolsMatching": "일치하는 기호 없음", + "noSymbolsWithoutInput": "입력하여 기호 검색" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..0988e7d089 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "입력", + "useIgnoreFilesDescription": "파일 무시 사용", + "useExcludeSettingsDescription": "제외 설정 사용" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..456c8b9c8b --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1}(미리 보기 바꾸기)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..87e8d19426 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "작업 영역에서 기호로 이동...", + "name": "검색", + "showSearchViewlet": "검색 표시", + "view": "보기", + "findInFiles": "파일에서 찾기", + "openAnythingHandlerDescription": "파일로 이동", + "openSymbolDescriptionNormal": "작업 영역에서 기호로 이동", + "searchOutputChannelTitle": "검색", + "searchConfigurationTitle": "검색", + "exclude": "검색에서 파일 및 폴더를 제외하도록 GLOB 패턴을 구성합니다. files.exclude 설정에서 모든 GLOB 패턴을 상속합니다.", + "exclude.boolean": "파일 경로를 일치시킬 GLOB 패턴입니다. 패턴을 사용하거나 사용하지 않도록 설정하려면 true 또는 false로 설정하세요.", + "exclude.when": "일치하는 파일의 형제에 대한 추가 검사입니다. $(basename)을 일치하는 파일 이름에 대한 변수로 사용하세요.", + "useRipgrep": "텍스트 검색에서 ripgrep 사용 여부를 제어합니다.", + "useIgnoreFilesByDefault": "새 작업 영역에서 검색할 때 기본적으로 .gitignore 파일 및 .ignore 파일을 사용할지 여부를 제어합니다.", + "search.quickOpen.includeSymbols": "Quick Open에 대한 파일 결과에 전역 기호 검색 결과를 포함하도록 구성합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..439096d570 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "다음 검색 포함 패턴 표시", + "previousSearchIncludePattern": "이전 검색 포함 패턴 표시", + "nextSearchExcludePattern": "다음 검색 제외 패턴 표시", + "previousSearchExcludePattern": "이전 검색 제외 패턴 표시", + "nextSearchTerm": "다음 검색어 표시", + "previousSearchTerm": "이전 검색어 표시", + "focusNextInputBox": "다음 입력 상자에 포커스", + "focusPreviousInputBox": "이전 입력 상자에 포커스", + "replaceInFiles": "파일에서 바꾸기", + "findInWorkspace": "작업 영역에서 찾기...", + "findInFolder": "폴더에서 찾기...", + "RefreshAction.label": "새로 고침", + "ClearSearchResultsAction.label": "검색 결과 지우기", + "FocusNextSearchResult.label": "다음 검색 결과에 포커스", + "FocusPreviousSearchResult.label": "이전 검색 결과에 포커스", + "RemoveAction.label": "제거", + "file.replaceAll.label": "모두 바꾸기", + "match.replace.label": "바꾸기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..b3445ff5cc --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "다른 파일", + "searchFileMatches": "{0}개 파일 찾음", + "searchFileMatch": "{0}개 파일 찾음", + "searchMatches": "일치하는 {0}개 항목을 찾음", + "searchMatch": "일치하는 {0}개 항목을 찾음", + "folderMatchAriaLabel": "폴더 루트 {1}에서 {0}개 일치, 검색 결과", + "fileMatchAriaLabel": "{2} 폴더의 {1} 파일에 {0}개의 일치 항목이 있음, 검색 결과", + "replacePreviewResultAria": "{3} 텍스트가 있는 줄의 열 위치 {2}에서 용어 {0}을(를) {1}(으)로 바꾸기", + "searchResultAria": "{2} 텍스트가 있는 줄의 열 위치 {1}에서 {0} 용어 찾기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..77033e1525 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "검색 세부 정보 설정/해제", + "searchScope.includes": "포함할 파일", + "label.includes": "패턴 포함 검색", + "searchScope.excludes": "제외할 파일", + "label.excludes": "패턴 제외 검색", + "replaceAll.confirmation.title": "모두 바꾸기", + "replaceAll.confirm.button": "바꾸기", + "replaceAll.occurrence.file.message": "{1}개 파일에서 {0}개를 '{2}'(으)로 바꿨습니다.", + "removeAll.occurrence.file.message": "{1}개 파일에서 {0}개를 바꿨습니다.", + "replaceAll.occurrence.files.message": "{1}개 파일에서 {0}개를 '{2}'(으)로 바꿨습니다.", + "removeAll.occurrence.files.message": "{1}개 파일에서 {0}개를 바꿨습니다.", + "replaceAll.occurrences.file.message": "{1}개 파일에서 {0}개를 '{2}'(으)로 바꿨습니다.", + "removeAll.occurrences.file.message": "{1}개 파일에서 {0}개를 바꿨습니다.", + "replaceAll.occurrences.files.message": "{1}개 파일에서 {0}개를 '{2}'(으)로 바꿨습니다.", + "removeAll.occurrences.files.message": "{1}개 파일에서 {0}개를 바꿨습니다.", + "removeAll.occurrence.file.confirmation.message": "{1}개 파일에서 {0}개를 '{2}'(으)로 바꾸시겠습니까?", + "replaceAll.occurrence.file.confirmation.message": "{1}개 파일에서 {0}개를 바꾸시겠습니까?", + "removeAll.occurrence.files.confirmation.message": "{1}개 파일에서 {0}개를 '{2}'(으)로 바꾸시겠습니까?", + "replaceAll.occurrence.files.confirmation.message": "{1}개 파일에서 {0}개를 바꾸시겠습니까?", + "removeAll.occurrences.file.confirmation.message": "{1}개 파일에서 {0}개를 '{2}'(으)로 바꾸시겠습니까?", + "replaceAll.occurrences.file.confirmation.message": "{1}개 파일에서 {0}개를 바꾸시겠습니까?", + "removeAll.occurrences.files.confirmation.message": "{1}개 파일에서 {0}개를 '{2}'(으)로 바꾸시겠습니까?", + "replaceAll.occurrences.files.confirmation.message": "{1}개 파일에서 {0}개를 바꾸시겠습니까?", + "treeAriaLabel": "검색 결과", + "searchPathNotFoundError": "검색 경로를 찾을 수 없음: {0}", + "searchMaxResultsWarning": "결과 집합에는 모든 일치 항목의 하위 집합만 포함됩니다. 결과 범위를 좁히려면 검색을 더 세분화하세요.", + "searchCanceled": "결과를 찾기 이전에 검색이 취소되었습니다. -", + "noResultsIncludesExcludes": "'{0}'에 '{1}'을(를) 제외한 결과 없음 - ", + "noResultsIncludes": "'{0}'에 결과 없음 - ", + "noResultsExcludes": "'{0}'을(를) 제외하는 결과가 없음 - ", + "noResultsFound": "결과가 없습니다. 구성된 예외 설정을 검토하고 파일을 무시하세요.", + "rerunSearch.message": "다시 검색", + "rerunSearchInAll.message": "모든 파일에서 다시 검색", + "openSettings.message": "설정 열기", + "openSettings.learnMore": "자세한 정보", + "ariaSearchResultsStatus": "검색에서 {1}개의 파일에 {0}개의 결과를 반환했습니다.", + "search.file.result": "{1}개 파일에서 {0}개 결과", + "search.files.result": "{1}개 파일에서 {0}개 결과", + "search.file.results": "{1}개 파일에서 {0}개 결과", + "search.files.results": "{1}개 파일에서 {0}개 결과", + "searchWithoutFolder": "아직 폴더를 열지 않았습니다. 현재 열린 파일만 검색됩니다. ", + "openFolder": "폴더 열기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..5af6e16312 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "모두 바꾸기(사용하려면 검색 전송)", + "search.action.replaceAll.enabled.label": "모두 바꾸기", + "search.replace.toggle.button.title": "바꾸기 설정/해제", + "label.Search": "검색: 검색어를 입력하고 Enter 키를 눌러서 검색하세요. 취소하려면 Esc 키를 누르세요.", + "search.placeHolder": "검색", + "label.Replace": "바꾸기: 바꿀 용어를 입력한 후 미리 보려면 키를 누르고 취소하려면 키를 누르세요.", + "search.replace.placeHolder": "바꾸기", + "regexp.validationFailure": "식이 모든 항목과 일치" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/kor/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..d68cc3be85 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "작업 영역에 이름이 {0}인 폴더가 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..d0c80d49e6 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "코드 조각을 적용합니다.", + "vscode.extension.contributes.snippets-language": "이 코드 조각이 적용되는 언어 식별자입니다.", + "vscode.extension.contributes.snippets-path": "코드 조각 파일의 경로입니다. 이 경로는 확장 폴더의 상대 경로이며 일반적으로 './snippets/'로 시작합니다.", + "invalid.language": "`contributes.{0}.language`에 알 수 없는 언어가 있습니다. 제공된 값: {1}", + "invalid.path.0": "`contributes.{0}.path`에 문자열이 필요합니다. 제공된 값: {1}", + "invalid.path.1": "확장 폴더({2})에 포함할 `contributes.{0}.path`({1})가 필요합니다. 확장이 이식 불가능해질 수 있습니다.", + "badVariableUse": "\"{0}\"-snippet은 snippet-variables 및 snippet-placeholders와 혼동하기 쉽습니다. 자세한 내용은\n https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax를 참조하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..e2a734f1e8 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "코드 조각 삽입" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..b42948f6de --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "코드 조각의 언어 선택", + "openSnippet.errorOnCreate": "{0}을(를) 만들 수 없음", + "openSnippet.label": "사용자 코드 조각 열기", + "preferences": "기본 설정", + "snippetSchema.json.default": "빈 코드 조각", + "snippetSchema.json": "사용자 코드 조각 구성", + "snippetSchema.json.prefix": "IntelliSense에서 코드 조각을 선택할 때 사용할 접두사입니다.", + "snippetSchema.json.body": "코드 조각 콘텐츠입니다. '$1', '${1:defaultText}'를 사용하여 커서 위치를 정의하고, '$0'을 최종 커서 위치에 사용하세요. '${varName}' 및 '${varName:defaultText}'에 변수 값을 삽입하세요(예: '$TM_FILENAME 파일입니다').", + "snippetSchema.json.description": "코드 조각 설명입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..e356ecf16c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "사용자 코드 조각", + "detail.snippet": "{0}({1})", + "snippetSuggest.longLabel": "{0}, {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..7402fa28ba --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "접두사가 일치하는 경우 코드 조각을 삽입합니다. 'quickSuggestions'를 사용하지 않을 때 가장 잘 작동합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..4efe091458 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "{0}에 대한 지원을 개선하는 데 도움을 주세요.", + "takeShortSurvey": "간단한 설문 조사 참여", + "remindLater": "나중에 알림", + "neverAgain": "다시 표시 안 함" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..5b42d7cf36 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "간단한 피드백 설문 조사에 참여하시겠어요?", + "takeSurvey": "설문 조사 참여", + "remindLater": "나중에 알림", + "neverAgain": "다시 표시 안 함" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..ae52c68c1c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "일치하는 작업 없음" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..5c004f17b6 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, 작업", + "recentlyUsed": "최근에 사용한 작어", + "configured": "구성된 작업", + "detected": "감지된 작업", + "customizeTask": "작업 구성" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..845074dae4 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "다시 시작할 작업 이름 입력", + "noTasksMatching": "일치하는 작업 없음", + "noTasksFound": "다시 시작할 작업이 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..197d962a93 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "실행할 작업의 이름 입력", + "noTasksMatching": "일치하는 작업 없음", + "noTasksFound": "작업을 찾을 수 없음" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..7ab319714f --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Type the name of a task to terminate", + "noTasksMatching": "일치하는 작업 없음", + "noTasksFound": "No tasks to terminate found" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..ae52c68c1c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "일치하는 작업 없음" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..340dbd43e9 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "경고: options.cwd는 string 형식이어야 합니다. {0} 값을 무시합니다.\n", + "ConfigurationParser.noargs": "오류: 명령 인수는 문자열의 배열이어야 합니다. 제공된 값:\n{0}", + "ConfigurationParser.noShell": "경고: 셸 구성은 작업을 터미널에서 실행 중일 때에만 지원됩니다.", + "ConfigurationParser.noName": "오류: 선언 범위 내의 문제 선택기는 이름이 있어야 합니다.\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "경고: 정의된 문제 선택기를 알 수 없습니다. 지원되는 형식은 string | ProblemMatcher |(string | ProblemMatcher)[]입니다.\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "오류: 잘못된 problemMatcher 참조: {0}\n", + "ConfigurationParser.noTaskName": "오류: 작업에서 taskName 속성을 제공해야 합니다. 이 작업은 무시됩니다.\n{0}\n", + "taskConfiguration.shellArgs": "경고: 작업 '{0}'은(는) 셸 명령이며, 명령 이름이나 인수 중 하나에 이스케이프되지 않은 공백이 있습니다. 명령줄 인용을 올바르게 하려면 인수를 명령으로 병합하세요.", + "taskConfiguration.noCommandOrDependsOn": "오류: 작업 '{0}'에서 명령이나 dependsOn 속성을 지정하지 않습니다. 이 작업은 무시됩니다. 해당 작업의 정의는 {1}입니다.", + "taskConfiguration.noCommand": "오류: 작업 '{0}'에서 명령을 정의하지 않습니다. 이 작업은 무시됩니다. 해당 작업의 정의는\n{1}입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..497f2fdde8 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "실제 작업 유형", + "TaskDefinition.properties": "작업 유형의 추가 속성", + "TaskTypeConfiguration.noType": "작업 유형 구성에 필요한 'taskType' 속성이 없음", + "TaskDefinitionExtPoint": "작업 유형 적용" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..499d3e8388 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": ".NET Core 빌드 명령을 실행합니다.", + "msbuild": "빌드 대상을 실행합니다.", + "externalCommand": "임의의 외부 명령을 실행하는 예", + "Maven": "일반적인 Maven 명령을 실행합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..6ab31b830f --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "추가 명령 옵션", + "JsonSchema.options.cwd": "실행된 프로그램 또는 스크립트의 현재 작업 디렉터리입니다. 생략된 경우 Code의 현재 작업 영역 루트가 사용됩니다.", + "JsonSchema.options.env": "실행할 프로그램 또는 셸의 환경입니다. 생략하면 부모 프로세스의 환경이 사용됩니다.", + "JsonSchema.shellConfiguration": "사용할 셸을 구성합니다.", + "JsonSchema.shell.executable": "사용할 셸입니다.", + "JsonSchema.shell.args": "셸 인수입니다.", + "JsonSchema.command": "실행할 명령이며, 외부 프로그램 또는 셸 명령입니다.", + "JsonSchema.tasks.args": "이 작업이 호출될 때 명령에 전달되는 인수입니다.", + "JsonSchema.tasks.taskName": "작업 이름", + "JsonSchema.tasks.windows": "Windows 특정 명령 구성", + "JsonSchema.tasks.mac": "Mac 특정 명령 구성", + "JsonSchema.tasks.linux": "Linux 특정 명령 구성", + "JsonSchema.tasks.suppressTaskName": "작업 이름을 명령에 인수로 추가할지 여부를 제어합니다. 생략하면 전역적으로 정의된 값이 사용됩니다.", + "JsonSchema.tasks.showOutput": "실행 중인 작업에 대한 출력을 표시할지 여부를 제어합니다. 생략하면 전역적으로 정의된 값이 사용됩니다.", + "JsonSchema.echoCommand": "실행된 명령을 출력에 에코할지 여부를 제어합니다. 기본값은 false입니다.", + "JsonSchema.tasks.watching.deprecation": "사용되지 않습니다. 대신 isBackground를 사용합니다.", + "JsonSchema.tasks.watching": "실행된 작업을 활성 상태로 유지할지 파일 시스템을 조사할지 여부를 나타냅니다.", + "JsonSchema.tasks.background": "실행한 작업을 활성 상태로 유지하고 배경에서 실행하는지 여부입니다.", + "JsonSchema.tasks.promptOnClose": "실행 중인 작업이 있는 VS Code가 닫힐 때 사용자에게 메시지를 표시할지 여부입니다.", + "JsonSchema.tasks.build": "이 작업을 Code의 기본 빌드 명령에 매핑합니다.", + "JsonSchema.tasks.test": "이 작업을 Code의 기본 테스트 명령에 매핑합니다.", + "JsonSchema.tasks.matchers": "사용할 문제 선택기입니다. 문자열, 문제 선택기 정의 또는 문자열 및 문제 선택기 배열일 수 있습니다.", + "JsonSchema.args": "명령에 전달되는 추가 인수입니다.", + "JsonSchema.showOutput": "실행 중인 작업에 대한 출력을 표시할지 여부를 제어합니다. 생략하면 '항상'이 사용됩니다.", + "JsonSchema.watching.deprecation": "사용되지 않습니다. 대신 isBackground를 사용합니다.", + "JsonSchema.watching": "실행된 작업을 활성 상태로 유지할지 파일 시스템을 조사할지 여부를 나타냅니다.", + "JsonSchema.background": "실행한 작업을 활성 상태로 유지하고 배경에서 실행하는지 여부입니다.", + "JsonSchema.promptOnClose": "백그라운드 작업이 실행 중인 상태에서 VS Code가 종료될 경우 사용자에게 메시지를 표시할지 여부를 나타냅니다.", + "JsonSchema.suppressTaskName": "작업 이름을 명령에 인수로 추가할지 여부를 제어합니다. 기본값은 false입니다.", + "JsonSchema.taskSelector": "인수가 작업임을 나타내는 접두사입니다.", + "JsonSchema.matchers": "사용할 문제 선택기입니다. 문자열, 문제 선택기 정의 또는 문자열 및 문제 선택기 배열일 수 있습니다.", + "JsonSchema.tasks": "작업 구성입니다. 일반적으로 외부 Task Runner에 이미 정의되어 있는 작업을 보강합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..e1b2c1099d --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "구성의 버전 번호입니다.", + "JsonSchema._runner": "러너가 더 이상 사용되지 않습니다. 공식 러너 속성을 사용하세요.", + "JsonSchema.runner": "작업이 프로세스로 실행되는지 여부와 출력이 출력 창이나 터미널 내부 중 어디에 표시되는지를 정의합니다.", + "JsonSchema.windows": "Windows 특정 명령 구성", + "JsonSchema.mac": "Mac 특정 명령 구성", + "JsonSchema.linux": "Linux 특정 명령 구성", + "JsonSchema.shell": "명령이 셸 명령인지 외부 프로그램인지 여부를 지정합니다. 생략하면 기본값 false가 사용됩니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..73b59bdbb6 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "명령이 셸 명령인지 외부 프로그램인지 여부를 지정합니다. 생략하면 기본값 false가 사용됩니다.", + "JsonSchema.tasks.isShellCommand.deprecated": "isShellCommand 속성은 사용 중단되었습니다. 작업의 유형 속성과 셸 속성을 대신 사용하세요. 1.14 릴리스 노트를 참고하세요.", + "JsonSchema.tasks.dependsOn.string": "이 작업이 종속된 또 다른 작업입니다.", + "JsonSchema.tasks.dependsOn.array": "이 작업이 종속된 다른 여러 작업입니다.", + "JsonSchema.tasks.presentation": "작업의 출력을 제시하고 입력을 읽는 데 사용하는 패널을 구성합니다.", + "JsonSchema.tasks.presentation.echo": "실행된 명령을 패널에 에코할지 결정합니다. 기본값은 true입니다.", + "JsonSchema.tasks.presentation.focus": "패널이 포커스를 잡는지 결정합니다. 기본값은 false입니다. true로 설정하면 패널도 드러납니다.", + "JsonSchema.tasks.presentation.reveal.always": "이 작업이 호출될 때 터미널을 항상 표시합니다.", + "JsonSchema.tasks.presentation.reveal.silent": "문제 선택기가 작업과 연결되어 있지 않고 실행하는 중 오류가 발생하는 경우에만 터미널을 표시합니다.", + "JsonSchema.tasks.presentation.reveal.never": "작업을 실행할 때 터미널을 표시하지 않습니다.", + "JsonSchema.tasks.presentation.reveals": "작업을 실행 중인 패널이 표시되는지 여부를 제어합니다. 기본값은 \"항상\"입니다.", + "JsonSchema.tasks.presentation.instance": "패널을 작업 간에 공유할지 결정합니다. 이 작업 전용 패널로 사용하거나 실행할 때마다 새로 생성합니다.", + "JsonSchema.tasks.terminal": "이 터미널 속성은 사용되지 않습니다. 프레젠테이션을 대신 사용하세요.", + "JsonSchema.tasks.group.kind": "작업 실행 그룹입니다.", + "JsonSchema.tasks.group.isDefault": "작업이 그룹 내 기본 작업에 있는지 정의합니다.", + "JsonSchema.tasks.group.defaultBuild": "이 작업을 기본 빌드 작업으로 표시합니다.", + "JsonSchema.tasks.group.defaultTest": "이 작업을 기본 테스트 작업으로 표시합니다.", + "JsonSchema.tasks.group.build": "이 작업을 '빌드 작업 실행' 명령을 통해 액세스 가능한 빌드 작업으로 표시합니다.", + "JsonSchema.tasks.group.test": "이 작업을 '테스트 작업 실행' 명령을 통해 액세스할 수 있는 테스트 작업으로 표시합니다.", + "JsonSchema.tasks.group.none": "작업을 그룹에 할당 안 함", + "JsonSchema.tasks.group": "이 작업을 할당할 실행 그룹을 정의합니다. 빌드 그룹에 추가를 위한 \"build'와 테스트 그룹에 추가를 위한 \"test\"를 지원합니다.", + "JsonSchema.tasks.type": "작업이 프로세스로 실행되는지 또는 셸 내의 명령으로 실행되는지를 제어합니다. 기본값은 프로세스입니다.", + "JsonSchema.version": "구성의 버전 번호입니다.", + "JsonSchema.tasks.identifier": "작업을 launch.json 또는 dependsOn 구문에서 참조할 사용자 정의 식별자입니다.", + "JsonSchema.tasks.taskLabel": "작업 레이블", + "JsonSchema.tasks.taskName": "작업 이름", + "JsonSchema.tasks.taskName.deprecated": "이 작업 이름은 사용되지 않습니다. 레이블 속성을 대신 사용하세요.", + "JsonSchema.tasks.background": "실행한 작업을 활성 상태로 유지하고 배경에서 실행하는지 여부입니다.", + "JsonSchema.tasks.promptOnClose": "실행 중인 작업이 있는 VS Code가 닫힐 때 사용자에게 메시지를 표시할지 여부입니다.", + "JsonSchema.tasks.matchers": "사용할 문제 선택기입니다. 문자열, 문제 선택기 정의 또는 문자열 및 문제 선택기 배열일 수 있습니다.", + "JsonSchema.customizations.customizes.type": "사용자 지정할 작업 유형", + "JsonSchema.tasks.customize.deprecated": "사용자 지정 속성은 사용되지 않습니다. 새로운 작업 사용자 지정 방식으로 마이그레이션을 위해 1.14 릴리스 노트를 참고하세요.", + "JsonSchema.tasks.showOputput.deprecated": "showOutput 속성은 사용되지 않습니다. 대신 presentation 속성 내 reveal 속성을 사용하세요. 1.14 릴리스 노트도 참고하세요.", + "JsonSchema.tasks.echoCommand.deprecated": "echoCommand 속성은 사용되지 않습니다. 대신 presentation 속성 내 echo 속성을 사용하세요. 1.14 릴리스 노트도 참고하세요.", + "JsonSchema.tasks.suppressTaskName.deprecated": "suppressTaskName 속성은 사용되지 않습니다. 대신 명령을 인수와 함께 작업에 인라인으로 삽입하세요. 1.14 릴리스 노트를 참고하세요.", + "JsonSchema.tasks.isBuildCommand.deprecated": "isBuildCommand 속성은 사용되지 않습니다. 대신 group 속성을 사용하세요. 1.14 릴리스 노트도 참고하세요.", + "JsonSchema.tasks.isTestCommand.deprecated": "isTestCommand 속성은 사용되지 않습니다. 대신 group 속성을 사용하세요. 1.14 릴리스 노트를 참고하세요.", + "JsonSchema.tasks.taskSelector.deprecated": "taskSelector 속성은 사용되지 않습니다. 대신 명령을 인수와 함께 작업에 인라인으로 삽입하세요. 1.14 릴리스 노트를 참고하세요.", + "JsonSchema.windows": "Windows 특정 명령 구성", + "JsonSchema.mac": "Mac 특정 명령 구성", + "JsonSchema.linux": "Linux 특정 명령 구성" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..b5f009f7d1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "작업", + "ConfigureTaskRunnerAction.noWorkspace": "작업은 작업 영역 폴더에서만 사용할 수 있습니다.", + "ConfigureTaskRunnerAction.quickPick.template": "Task Runner 선택", + "ConfigureTaskRunnerAction.autoDetecting": "{0} 작업을 자동 검색 중", + "ConfigureTaskRunnerAction.autoDetect": "작업 시스템을 자동으로 감지하지 못했습니다. 기본 템플릿을 사용하는 중입니다. 자세한 내용은 작업 출력을 참조하세요.", + "ConfigureTaskRunnerAction.autoDetectError": "작업 시스템을 자동으로 감지하는 중 오류가 발생했습니다. 자세한 내용은 작업 출력을 참조하세요.", + "ConfigureTaskRunnerAction.failed": "'.vscode' 폴더 내에 'tasks.json' 파일을 만들 수 없습니다. 자세한 내용은 작업 출력을 참조하세요.", + "ConfigureTaskRunnerAction.label": "Task Runner 구성", + "ConfigureBuildTaskAction.label": "빌드 작업 구성", + "CloseMessageAction.label": "닫기", + "ShowTerminalAction.label": "터미널 보기", + "problems": "문제", + "manyMarkers": "99+", + "runningTasks": "실행 중인 작업 표시", + "tasks": "작업", + "TaskSystem.noHotSwap": "작업 실행 엔진을 변경하면 창을 다시 로드해야 합니다.", + "TaskService.noBuildTask1": "정의된 빌드 작업이 없습니다. tasks.json 파일에서 작업을 'isBuildCommand'로 표시하세요.", + "TaskService.noBuildTask2": "정의된 빌드 작업이 없습니다. tasks.json 파일에서 작업을 'build'로 표시하세요.", + "TaskService.noTestTask1": "정의된 테스트 작업이 없습니다. tasks.json 파일에서 작업을 'isTestCommand'로 표시하세요.", + "TaskService.noTestTask2": "정의된 테스트 작업이 없습니다. tasks.json 파일에서 작업을 'test'로 표시하세요.", + "TaskServer.noTask": "실행하도록 요청한 작업 {0}을(를) 찾을 수 없습니다.", + "TaskService.attachProblemMatcher.continueWithout": "작업 출력을 스캔하지 않고 계속", + "TaskService.attachProblemMatcher.never": "작업 출력 스캔 안 함", + "TaskService.attachProblemMatcher.learnMoreAbout": "작업 출력 스캔에 대해 자세히 알아보기", + "selectProblemMatcher": "작업 출력에서 스캔할 오류 및 경고 유형을 선택", + "customizeParseErrors": "현재 작성 구성에 오류가 있습니다. 작업을 사용자 지정하기 전에 오류를 수정하세요.\n", + "moreThanOneBuildTask": "tasks.json에 여러 빌드 작업이 정의되어 있습니다. 첫 번째 작업을 실행합니다.\n", + "TaskSystem.activeSame.background": "'{0}' 작업이 이미 활성 상태로 백그라운드 모드에 있습니다. 종료하려면 작업 메뉴에서 '작업 종료'를 사용하세요.", + "TaskSystem.activeSame.noBackground": "'{0}' 작업이 이미 활성 상태입니다. 작업을 종료하려면 작업 메뉴에서 '작업 종료'를 사용하세요.", + "TaskSystem.active": "이미 실행 중인 작업이 있습니다. 다른 작업을 실행하려면 먼저 이 작업을 종료하세요.", + "TaskSystem.restartFailed": "{0} 작업을 종료하고 다시 시작하지 못했습니다.", + "TaskSystem.configurationErrors": "오류: 제공한 작업 구성에 유효성 검사 오류가 있으며 사용할 수 없습니다. 먼저 오류를 수정하세요.", + "TaskSystem.invalidTaskJson": "오류: tasks.json 파일의 내용에 구문 오류가 있습니다. 작업을 실행하기 전에 오류를 정정하세요.\n", + "TaskSystem.runningTask": "실행 중인 작업이 있습니다. 이 작업을 종료할까요?", + "TaskSystem.terminateTask": "작업 종료(&&T)", + "TaskSystem.noProcess": "시작된 작업이 더 이상 존재하지 않습니다. 작업에서 생성된, VS Code를 끝내는 백그라운드 프로세스가 분리된 프로세스가 될 수 있습니다. 이를 방지하려면 wait 플래그를 사용하여 마지막 백그라운드 프로세스를 시작하세요.", + "TaskSystem.exitAnyways": "끝내기(&&E)", + "TerminateAction.label": "작업 종료", + "TaskSystem.unknownError": "작업을 실행하는 동안 오류가 발생했습니다. 자세한 내용은 작업 로그를 참조하세요.", + "TaskService.noWorkspace": "작업은 작업 영역 폴더에서만 사용할 수 있습니다.", + "recentlyUsed": "최근에 사용한 작업", + "configured": "구성된 작업", + "detected": "감지된 작업", + "TaskService.fetchingBuildTasks": "빌드 작업을 페치하는 중...", + "TaskService.noBuildTaskTerminal": "빌드 작업을 찾을 수 없습니다. '빌드 작업 구성'을 눌러서 빌드 작업을 정의하세요.", + "TaskService.pickBuildTask": "실행할 빌드 작업 선택", + "TaskService.fetchingTestTasks": "테스트 작업을 페치하는 중...", + "TaskService.noTestTaskTerminal": "테스트 작업을 찾을 수 없습니다. 'Task Runner 구성'을 눌러서 테스트 작업을 정의하세요.", + "TaskService.pickTestTask": "실행할 테스트 작업 선택", + "TaskService.noTaskRunning": "현재 실행 중인 작업이 없습니다.", + "TaskService.tastToTerminate": "종료할 작업 선택", + "TerminateAction.noProcess": "시작된 프로세스가 더 이상 존재하지 않습니다. 작업에서 생성된, VS Code를 끝내는 백그라운드 작업이 분리된 프로세스가 될 수 있습니다.", + "TerminateAction.failed": "실행 중인 작업을 종료하지 못했습니다.", + "TaskService.noTaskToRestart": "다시 시작할 작업이 없습니다.", + "TaskService.tastToRestart": "다시 시작할 작업 선택", + "TaskService.defaultBuildTaskExists": "{0}은(는) 이미 기본 빌드 작업으로 표시되어 있습니다.", + "TaskService.pickDefaultBuildTask": "기본 빌드 작업으로 사용할 작업을 선택", + "TaskService.defaultTestTaskExists": "{0}은(는) 이미 기본 테스트 작업으로 표시되어 있습니다.", + "TaskService.pickDefaultTestTask": "기본 테스트 작업으로 사용할 작업 선택", + "TaskService.noTaskIsRunning": "실행 중인 작업이 없습니다.", + "TaskService.pickShowTask": "출력을 표시할 작업 선택", + "ShowLogAction.label": "작업 로그 표시", + "RunTaskAction.label": "작업 실행", + "RestartTaskAction.label": "실행 중인 작업 다시 시작", + "ShowTasksAction.label": "실행 중인 작업 표시", + "BuildAction.label": "빌드 작업 실행", + "TestAction.label": "테스트 작업 실행", + "ConfigureDefaultBuildTask.label": "기본 빌드 작업 구성", + "ConfigureDefaultTestTask.label": "기본 테스트 작업 구성", + "quickOpen.task": "작업 실행" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..1625b8446d --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "작업" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..831b07e96d --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "작업을 실행하는 동안 알 수 없는 오류가 발생했습니다. 자세한 내용은 작업 출력 로그를 참조하세요.", + "TerminalTaskSystem.terminalName": "작업 - {0}", + "reuseTerminal": "터미널이 작업에서 다시 사용됩니다. 닫으려면 아무 키나 누르세요.", + "TerminalTaskSystem": "UNC 드라이브에서 셸 명령을 실행할 수 없습니다.", + "unkownProblemMatcher": "문제 선택기 {0}을(를) 확인할 수 없습니다. 이 선택기는 무시됩니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..0c8ae0cc97 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "gulp --tasks-simple을 실행해도 작업이 나열되지 않습니다. npm install을 실행했나요?", + "TaskSystemDetector.noJakeTasks": "jake --tasks를 실행해도 작업이 나열되지 않습니다. npm install을 실행했나요?", + "TaskSystemDetector.noGulpProgram": "Gulp가 시스템에 설치되어 있지 않습니다. npm install -g gulp를 실행하여 설치하세요.", + "TaskSystemDetector.noJakeProgram": "Jake가 시스템에 설치되어 있지 않습니다. npm install -g jake를 실행하여 설치하세요.", + "TaskSystemDetector.noGruntProgram": "Grunt가 시스템에 설치되어 있지 않습니다. npm install -g grunt를 실행하여 설치하세요.", + "TaskSystemDetector.noProgram": "{0} 프로그램을 찾을 수 없습니다. 메시지는 {1}입니다.", + "TaskSystemDetector.buildTaskDetected": "이름이 '{0}'인 빌드 작업이 발견되었습니다.", + "TaskSystemDetector.testTaskDetected": "이름이 '{0}'인 테스트 작업이 발견되었습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..3df3da9471 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "작업을 실행하는 동안 알 수 없는 오류가 발생했습니다. 자세한 내용은 작업 출력 로그를 참조하세요.", + "TaskRunnerSystem.watchingBuildTaskFinished": "\n빌드 감시 작업이 완료되었습니다.", + "TaskRunnerSystem.childProcessError": "Failed to launch external program {0} {1}.", + "TaskRunnerSystem.cancelRequested": "\n사용자 요청에 따라 '{0}' 작업이 종료되었습니다.", + "unkownProblemMatcher": "문제 선택기 {0}을(를) 확인할 수 없습니다. 이 선택기는 무시됩니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..3ce584da35 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "경고: options.cwd는 string 형식이어야 합니다. {0} 값을 무시합니다.\n", + "ConfigurationParser.noargs": "오류: 명령 인수는 문자열의 배열이어야 합니다. 제공된 값:\n{0}", + "ConfigurationParser.noShell": "경고: 셸 구성은 작업을 터미널에서 실행 중일 때에만 지원됩니다.", + "ConfigurationParser.noName": "오류: 선언 범위 내의 문제 선택기는 이름이 있어야 합니다.\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "경고: 정의된 문제 선택기를 알 수 없습니다. 지원되는 형식은 string | ProblemMatcher |(string | ProblemMatcher)[]입니다.\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "오류: 잘못된 problemMatcher 참조: {0}\n", + "ConfigurationParser.noTaskType": "오류: 작업 구성은 유형 속성이 필요합니다. 이 구성은 무시됩니다.\n{0}\n", + "ConfigurationParser.noTypeDefinition": "오류: 등록된 작업 형식 '{0}'이(가) 없습니다. 해당하는 작업 공급자를 제공하는 확장을 설치하지 않으셨습니까?", + "ConfigurationParser.notCustom": "오류: 작업이 사용자 지정 작업으로 선언되지 않았습니다. 이 구성은 무시됩니다.\n{0}\n", + "ConfigurationParser.noTaskName": "오류: 작업에서 taskName 속성을 제공해야 합니다. 이 작업은 무시됩니다.\n{0}\n", + "taskConfiguration.shellArgs": "경고: 작업 '{0}'은(는) 셸 명령이며, 명령 이름이나 인수 중 하나에 이스케이프되지 않은 공백이 있습니다. 명령줄 인용을 올바르게 하려면 인수를 명령으로 병합하세요.", + "taskConfiguration.noCommandOrDependsOn": "오류: 작업 '{0}'에서 명령이나 dependsOn 속성을 지정하지 않습니다. 이 작업은 무시됩니다. 해당 작업의 정의는 {1}입니다.", + "taskConfiguration.noCommand": "오류: 작업 '{0}'에서 명령을 정의하지 않습니다. 이 작업은 무시됩니다. 해당 작업의 정의는\n{1}입니다.", + "TaskParse.noOsSpecificGlobalTasks": "작업 버전 2.0.0은 글로벌 OS별 작업을 지원하지 않습니다. OS별 명령을 사용하여 작업으로 변환하세요. 영향을 받는 작업::\n{0}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..23b6b64cff --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0}, 터미널 선택기", + "termCreateEntryAriaLabel": "{0}, 새 터미널 만들기", + "'workbench.action.terminal.newplus": "$(plus) 새 통합 터미널 만들기", + "noTerminalsMatching": "일치하는 터미널 없음", + "noTerminalsFound": "열린 터미널 없음" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..06618d3331 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "모든 열려 있는 터미널 표시", + "terminalIntegratedConfigurationTitle": "통합 터미널", + "terminal.integrated.shell.linux": "터미널이 Linux에서 사용하는 셸의 경로입니다.", + "terminal.integrated.shellArgs.linux": "Linux 터미널에 있을 때 사용할 명령줄 인수입니다.", + "terminal.integrated.shell.osx": "터미널이 OS X에서 사용하는 셸의 경로입니다.", + "terminal.integrated.shellArgs.osx": "OS X 터미널에 있을 때 사용할 명령줄 인수입니다.", + "terminal.integrated.shell.windows": "터미널이 Windows에서 사용하는 셸의 경로입니다. Windows와 함께 제공되는 셸을 사용하는 경우(cmd, PowerShell 또는 Ubuntu의 Bash)", + "terminal.integrated.shellArgs.windows": "Windows 터미널에 있을 때 사용할 명령줄 인수입니다.", + "terminal.integrated.rightClickCopyPaste": "설정하는 경우 터미널 내에서 마우스 오른쪽 단추를 클릭할 때 상황에 맞는 메뉴가 표시되지 않고 대신 선택 항목이 있으면 복사하고 선택 항목이 없으면 붙여넣습니다.", + "terminal.integrated.fontFamily": "터미널의 글꼴 패밀리를 제어하며, 기본값은 editor.fontFamily의 값입니다.", + "terminal.integrated.fontLigatures": "터미널에서 글꼴 합자가 사용되는지를 제어합니다.", + "terminal.integrated.fontSize": "터미널의 글꼴 크기(픽셀)를 제어합니다.", + "terminal.integrated.lineHeight": "터미널의 줄 높이를 제어하며, 이 숫자는 터미널 글꼴 크기를 곱하여 실제 줄 높이(픽셀)를 얻습니다.", + "terminal.integrated.enableBold": "터미널 내에서 굵은 텍스트를 사용하도록 설정할지 여부이며, 터미널 셸의 지원이 필요합니다.", + "terminal.integrated.cursorBlinking": "터미널 커서 깜박임 여부를 제어합니다.", + "terminal.integrated.cursorStyle": "터미널 커서의 스타일을 제어합니다.", + "terminal.integrated.scrollback": "터미널에서 버퍼에 유지하는 최대 줄 수를 제어합니다.", + "terminal.integrated.setLocaleVariables": "로캘 변수가 터미널 시작 시 설정되는지 여부를 제어하며, 기본값은 OS X에서 true이고 기타 플랫폼에서 false입니다.", + "terminal.integrated.cwd": "터미널이 시작될 명시적 시작 경로입니다. 셸 프로세스의 현재 작업 디렉터리(cwd)로 사용됩니다. 루트 디렉터리가 편리한 cwd가 아닌 경우 작업 영역 설정에서 특히 유용하게 사용할 수 있습니다.", + "terminal.integrated.confirmOnExit": "끝낼 때 활성 터미널 세션이 있는지 확인할지 여부입니다.", + "terminal.integrated.commandsToSkipShell": "키 바인딩이 셸에 전송되지 않고 항상 Code에서 처리되는 명령 ID 집합입니다. 따라서 셸에서 정상적으로 사용되어 터미널에 포커스가 없을 때와 동일하게 작동하는 키 바인딩을 사용할 수 있습니다(예: 를 사용하여 Quick Open 시작).", + "terminal.integrated.env.osx": "OS X의 터미널이 사용하는 VS Code 프로세스에 추가될 환경 변수를 포함한 개체", + "terminal.integrated.env.linux": "Linux의 터미널에서 사용할 VS Code 프로세스에 추가되는 환경 변수를 포함한 개체", + "terminal.integrated.env.windows": "Windows의 터미널에서 사용할 VS Code 프로세스에 추가되는 환경 변수를 포함한 개체", + "terminal": "터미널", + "terminalCategory": "터미널", + "viewCategory": "보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..e760fb3208 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "통합 터미널 설정/해제", + "workbench.action.terminal.kill": "활성 터미널 인스턴스 종료", + "workbench.action.terminal.kill.short": "터미널 종료", + "workbench.action.terminal.quickKill": "터미널 인스턴스 종료", + "workbench.action.terminal.copySelection": "선택 영역 복사", + "workbench.action.terminal.selectAll": "모두 선택", + "workbench.action.terminal.deleteWordLeft": "왼쪽 단어 삭제", + "workbench.action.terminal.deleteWordRight": "오른쪽 단어 삭제", + "workbench.action.terminal.new": "새 통합 터미널 만들기", + "workbench.action.terminal.new.short": "새 터미널", + "workbench.action.terminal.focus": "터미널에 포커스", + "workbench.action.terminal.focusNext": "다음 터미널에 포커스", + "workbench.action.terminal.focusAtIndex": "{0} 터미널로 포커스 이동", + "workbench.action.terminal.focusPrevious": "이전 터미널에 포커스", + "workbench.action.terminal.paste": "활성 터미널에 붙여넣기", + "workbench.action.terminal.DefaultShell": "기본 셸 선택", + "workbench.action.terminal.runSelectedText": "활성 터미널에서 선택한 텍스트 실행", + "workbench.action.terminal.runActiveFile": "활성 터미널에서 활성 파일 실행", + "workbench.action.terminal.runActiveFile.noFile": "디스크의 파일만 터미널에서 실행할 수 있습니다.", + "workbench.action.terminal.switchTerminalInstance": "터미널 인스턴스 전환", + "workbench.action.terminal.scrollDown": "아래로 스크롤(줄)", + "workbench.action.terminal.scrollDownPage": "아래로 스크롤(페이지)", + "workbench.action.terminal.scrollToBottom": "맨 아래로 스크롤", + "workbench.action.terminal.scrollUp": "위로 스크롤(줄)", + "workbench.action.terminal.scrollUpPage": "위로 스크롤(페이지)", + "workbench.action.terminal.scrollToTop": "맨 위로 스크롤", + "workbench.action.terminal.clear": "지우기", + "workbench.action.terminal.allowWorkspaceShell": "작업 영역 셸 구성 허용", + "workbench.action.terminal.disallowWorkspaceShell": "작업 영역 셸 구성 허용 안 함", + "workbench.action.terminal.rename": "이름 바꾸기", + "workbench.action.terminal.rename.prompt": "터미널 이름 입력", + "workbench.action.terminal.focusFindWidget": "파인드 위젯 포커스", + "workbench.action.terminal.hideFindWidget": "파인드 위젯 숨기기", + "nextTerminalFindTerm": "다음 검색어 표시", + "previousTerminalFindTerm": "이전 검색어 표시", + "quickOpenTerm": "터미널: 활성 터미널에 전환" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..047334097e --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "터미널의 배경색입니다. 이 설정을 사용하면 터미널\n 색을 패널과 다르게 지정할 수 있습니다.", + "terminal.foreground": "터미널의 전경색입니다.", + "terminalCursor.foreground": "터미널 커서의 전경색입니다.", + "terminalCursor.background": "터미널 커서의 배경색입니다. 블록 커서와 겹친 문자의 색상을 사용자 정의할 수 있습니다.", + "terminal.ansiColor": "터미널의 '{0}' ANSI 색입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..dd80db75ca --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "터미널에서 {0}(작업 영역 설정으로 정의됨)이(가) 시작되도록\n 허용할까요?", + "allow": "Allow", + "disallow": "Disallow" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..3ddd3d3081 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "찾기", + "placeholder.find": "찾기", + "label.previousMatchButton": "이전 검색 결과", + "label.nextMatchButton": "다음 검색 결과", + "label.closeButton": "닫기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..7a161c9af2 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "터미널에 복사할 선택 항목이 없음", + "terminal.integrated.exitedWithCode": "터미널 프로세스가 종료 코드 {0}(으)로 종료되었습니다.", + "terminal.integrated.waitOnExit": "터미널을 닫으려면 아무 키나 누르세요.", + "terminal.integrated.launchFailed": "터미널 프로세스 명령 `{0}{1}`이(가) 시작하지 못했습니다(종료 코드: {2})." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..12db8421e1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "Alt 키를 누르고 클릭하여 링크로 이동", + "terminalLinkHandler.followLinkCmd": "Cmd 키를 누르고 클릭하여 링크로 이동", + "terminalLinkHandler.followLinkCtrl": "Ctrl 키를 누르고 클릭하여 링크로 이동" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..a1af91fff2 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "복사", + "createNewTerminal": "새 터미널", + "paste": "붙여넣기", + "selectAll": "모두 선택", + "clear": "지우기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..f19a47cd65 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "사용자 지정 단추를 선택하여 기본 터미널 셸을 변경할 수 있습니다.", + "customize": "사용자 지정", + "cancel": "취소", + "never again": "다시 표시 안 함", + "terminal.integrated.chooseWindowsShell": "기본으로 설정할 터미널 셸을 선택하세요. 나중에 설정에서 이 셸을 변경할 수 있습니다.", + "terminalService.terminalCloseConfirmationSingular": "활성 터미널 세션이 있습니다. 종료할까요?", + "terminalService.terminalCloseConfirmationPlural": "{0}개의 활성 터미널 세션이 있습니다. 종료할까요?", + "yes": "예" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..a489148fe1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "색 테마", + "installColorThemes": "추가 색 테마 설치...", + "themes.selectTheme": "색 테마 선택(미리 보려면 위로/아래로 키 사용)", + "selectIconTheme.label": "파일 아이콘 테마", + "installIconThemes": "추가 파일 아이콘 테마 설치...", + "noIconThemeLabel": "없음", + "noIconThemeDesc": "파일 아이콘 사용 안 함", + "problemChangingIconTheme": "아이콘 테마를 설정하는 동안 문제 발생: {0}", + "themes.selectIconTheme": "파일 아이콘 테마 선택", + "generateColorTheme.label": "현재 설정에서 색 테마 생성", + "preferences": "기본 설정", + "developer": "개발자" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..3688d11e34 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "이 작업 영역에는 [사용자 설정]에서만 설정할 수 있는 설정이 포함됩니다. ({0})", + "openWorkspaceSettings": "작업 영역 설정 열기", + "openDocumentation": "자세한 정보", + "ignore": "무시" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/kor/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..f199c11ed8 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "릴리스 정보: {0}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..823bab953b --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "릴리스 정보", + "updateConfigurationTitle": "업데이트", + "updateChannel": "업데이트 채널에서 자동 업데이트를 받을지 여부를 구성합니다. 변경 후 다시 시작해야 합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..a15a58380c --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "지금 업데이트", + "later": "나중에", + "unassigned": "할당되지 않음", + "releaseNotes": "릴리스 정보", + "showReleaseNotes": "릴리스 정보 표시", + "downloadNow": "지금 다운로드", + "read the release notes": "{0} v{1}을(를) 시작합니다. 릴리스 정보를 확인하시겠습니까?", + "licenseChanged": "사용 조건이 변경되었습니다. 자세히 읽어보세요.", + "license": "라이선스 읽기", + "neveragain": "다시 표시 안 함", + "64bitisavailable": "64비트 Windows용 {0}을(를) 지금 사용할 수 있습니다.", + "learn more": "자세한 정보", + "updateIsReady": "새 {0} 업데이트를 사용할 수 있습니다.", + "thereIsUpdateAvailable": "사용 가능한 업데이트가 있습니다.", + "updateAvailable": "다시 시작하면 {0}이(가) 업데이트됩니다.", + "noUpdatesAvailable": "현재 사용 가능한 업데이트가 없습니다.", + "commandPalette": "명령 팔레트...", + "settings": "설정", + "keyboardShortcuts": "바로 가기 키(&&K)", + "selectTheme.label": "색 테마", + "themes.selectIconTheme.label": "파일 아이콘 테마", + "not available": "업데이트를 사용할 수 없음", + "checkingForUpdates": "업데이트를 확인하는 중...", + "DownloadUpdate": "사용 가능한 업데이트 다운로드", + "DownloadingUpdate": "업데이트를 다운로드하는 중...", + "InstallingUpdate": "업데이트를 설치하는 중...", + "restartToUpdate": "업데이트하기 위해 다시 시작...", + "checkForUpdates": "업데이트 확인..." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/kor/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..c5405ec177 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} 동작", + "hideView": "사이드바에서 숨기기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/kor/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..b3e12a5599 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "뷰는 배열이어야 합니다.", + "requirestring": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "optstring": "속성 `{0}`은(는) 생략할 수 있으며 `string` 형식이어야 합니다.", + "vscode.extension.contributes.view.id": "뷰의 식별자입니다. 'vscode.window.registerTreeDataProviderForView` API를 통해 데이터 공급자를 등록하는 데 사용합니다. `onView:${id}` 이벤트를 `activationEvents`에 등록하여 확장 활성화를 트리거하는 데에도 사용합니다.", + "vscode.extension.contributes.view.name": "사용자가 읽을 수 있는 뷰 이름입니다. 표시됩니다.", + "vscode.extension.contributes.view.when": "이 보기를 표시하기 위해 true여야 하는 조건입니다.", + "vscode.extension.contributes.views": "뷰를 에디터에 적용합니다.", + "views.explorer": "탐색기 뷰", + "views.debug": "디버그 보기", + "locationId.invalid": "`{0}`은(는) 유효한 뷰 위치가 아닙니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/kor/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..8246477279 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "모든 명령 표시", + "watermark.quickOpen": "파일로 이동", + "watermark.openFile": "파일 열기", + "watermark.openFolder": "폴더 열기", + "watermark.openFileFolder": "파일 또는 폴더 열기", + "watermark.openRecent": "최근 파일 열기", + "watermark.newUntitledFile": "제목이 없는 새 파일", + "watermark.toggleTerminal": "터미널 설정/해제", + "watermark.findInFiles": "파일에서 찾기", + "watermark.startDebugging": "디버깅 시작", + "watermark.unboundCommand": "바인딩 안 됨", + "workbenchConfigurationTitle": "워크벤치", + "tips.enabled": "사용하도록 설정되면 편집기가 열리지 않았을 때 워터마크 팁이 표시됩니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/kor/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..50803c4803 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "파일 탐색기", + "welcomeOverlay.search": "전체 파일 검색", + "welcomeOverlay.git": "소스 코드 관리", + "welcomeOverlay.debug": "시작 및 디버그", + "welcomeOverlay.extensions": "확장 관리", + "welcomeOverlay.problems": "오류 및 경고 보기", + "welcomeOverlay.commandPalette": "모든 명령 찾기 및 실행", + "welcomeOverlay": "사용자 인터페이스 개요", + "hideWelcomeOverlay": "인터페이스 개요 숨기기", + "help": "도움말" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..b4421aade8 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "편집 향상됨", + "welcomePage.start": "시작", + "welcomePage.newFile": "새 파일", + "welcomePage.openFolder": "폴더 열기...", + "welcomePage.cloneGitRepository": "Git 리포지토리 복제...", + "welcomePage.recent": "최근 항목", + "welcomePage.moreRecent": "자세히...", + "welcomePage.noRecentFolders": "최근 폴더 없음", + "welcomePage.help": "도움말", + "welcomePage.keybindingsCheatsheet": "인쇄 가능 키보드 치트시트", + "welcomePage.introductoryVideos": "소개 비디오", + "welcomePage.tipsAndTricks": "팁과 요령", + "welcomePage.productDocumentation": "제품 설명서", + "welcomePage.gitHubRepository": "GitHub 리포지토리", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "시작 시 시작 페이지 표시", + "welcomePage.customize": "사용자 지정", + "welcomePage.installExtensionPacks": "도구 및 언어", + "welcomePage.installExtensionPacksDescription": "{0} 및 {1}에 대한 지원 설치", + "welcomePage.moreExtensions": "자세히", + "welcomePage.installKeymapDescription": "바로 가기 키 설치", + "welcomePage.installKeymapExtension": "{0} 및 {1}의 바로 가기 키 설치", + "welcomePage.others": "기타", + "welcomePage.colorTheme": "색 테마", + "welcomePage.colorThemeDescription": "편집기 및 코드가 좋아하는 방식으로 표시되게 만들기", + "welcomePage.learn": "알아보기", + "welcomePage.showCommands": "모든 명령 찾기 및 실행", + "welcomePage.showCommandsDescription": "명령 팔레트({0})에서 빠른 액세스 및 명령 검색", + "welcomePage.interfaceOverview": "인터페이스 개요", + "welcomePage.interfaceOverviewDescription": "UI의 주요 구성 요소를 강조 표시하는 시각적 오버레이 가져오기", + "welcomePage.deployToAzure": "클라우드에 응용 프로그램 배포", + "welcomePage.deployToAzureDescription": "Azure App Service에 Node 앱을 배포하는 방법 알아보기", + "welcomePage.interactivePlayground": "대화형 실습", + "welcomePage.interactivePlaygroundDescription": "짧은 연습에서 기본 편집기 기능 사용해 보기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..84bb68aa92 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "워크벤치", + "workbench.startupEditor.none": "편집기를 사용하지 않고 시작합니다.", + "workbench.startupEditor.welcomePage": "시작 페이지를 엽니다(기본값).", + "workbench.startupEditor.newUntitledFile": "제목이 없는 새 파일 열기", + "workbench.startupEditor": "시작할 때 이전 세션에서 복구하지 않을 경우 어떤 편집기가 표시될지 결정합니다. 'none'을 선택하면 편집기 없이 시작하고 'welcomePage'를 선택하면 Welcome 페이지(기본값)가 열리며 'newUntitledFile'을 선택하면 제목 없는 새 파일이 열립니다(빈 작업 영역을 열 때만).", + "help": "도움말" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..c3b0c7abf8 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "시작", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "Azure 확장 표시", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "{0}에 대한 지원이 이미 설치되어 있습니다.", + "welcomePage.willReloadAfterInstallingExtensionPack": "{0}에 대한 추가 지원을 설치한 후 창이 다시 로드됩니다.", + "welcomePage.installingExtensionPack": "{0}에 대한 추가 지원을 설치하는 중...", + "welcomePage.extensionPackNotFound": "ID가 {1}인 {0}에 대한 지원을 찾을 수 없습니다.", + "welcomePage.keymapAlreadyInstalled": "{0} 바로 가기 키가 이미 설치되어 있습니다.", + "welcomePage.willReloadAfterInstallingKeymap": "{0} 바로 가기 키를 설치한 후 창이 다시 로드됩니다.", + "welcomePage.installingKeymap": "{0} 바로 가기 키를 설치하는 중...", + "welcomePage.keymapNotFound": "ID가 {1}인 {0} 바로 가기 키를 찾을 수 없습니다.", + "welcome.title": "시작", + "welcomePage.openFolderWithPath": "경로가 {1}인 {0} 폴더 열기", + "welcomePage.extensionListSeparator": ", ", + "welcomePage.installKeymap": "{0} 키맵 설치", + "welcomePage.installExtensionPack": "{0}에 대한 추가 지원 설치", + "welcomePage.installedKeymap": "{0} 키맵이 이미 설치되어 있습니다.", + "welcomePage.installedExtensionPack": "{0} 지원이 이미 설치되어 있습니다.", + "ok": "확인", + "details": "세부 정보", + "cancel": "취소", + "welcomePage.buttonBackground": "시작 페이지에서 단추의 배경색입니다.", + "welcomePage.buttonHoverBackground": "시작 페이지에서 단추의 커서 올리기 배경색입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..8d672fa707 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "대화형 실습", + "editorWalkThrough": "대화형 실습" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..a264fa7dc1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "대화형 실습", + "help": "도움말", + "interactivePlayground": "대화형 실습" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..63ffbb2213 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "위로 스크롤(줄)", + "editorWalkThrough.arrowDown": "아래로 스크롤(줄)", + "editorWalkThrough.pageUp": "위로 스크롤(페이지)", + "editorWalkThrough.pageDown": "아래로 스크롤(페이지)" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..6e1ff07589 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "바인딩 안 됨", + "walkThrough.gitNotFound": "Git가 시스템에 설치되지 않은 것 같습니다.", + "walkThrough.embeddedEditorBackground": "대화형 실습에서 포함된 편집기의 배경색입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/kor/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..15922bfca2 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "구성 설정을 적용합니다.", + "vscode.extension.contributes.configuration.title": "설정을 요약합니다. 이 레이블은 설정 파일에서 구분 주석으로 사용됩니다.", + "vscode.extension.contributes.configuration.properties": "구성 속성에 대한 설명입니다.", + "scope.window.description": "[사용자] 설정 또는 [작업 영역] 설정에서 구성할 수 있는 창 특정 구성입니다.", + "scope.resource.description": "사용자, 작업 영역 또는 폴더 설정에서 구성할 수 있는 리소스 특정 구성", + "scope.description": "구성이 적용되는 범위입니다. 사용 가능 범위는 '창'과 '리소스'입니다.", + "invalid.type": "설정된 경우 'configuration.type'을 '개체'로 설정해야 합니다.", + "invalid.title": "'configuration.title'은 문자열이어야 합니다.", + "vscode.extension.contributes.defaultConfiguration": "언어별로 기본 편집기 구성 설정을 적용합니다.", + "invalid.properties": "'configuration.properties'는 개체여야 합니다.", + "workspaceConfig.folders.description": "작업 영역에 로드할 폴더 목록입니다. 파일 경로를 사용해야 합니다. 예: `/root/folderA` 또는 `./folderA`(작업 영역 파일의 위치를 기준으로 확인할 상대 경로인 경우)", + "workspaceConfig.folder.description": "파일 경로입니다. 예: `/root/folderA` 또는 `./folderA`(작업 영역 파일의 위치를 기준으로 확인할 상대 경로인 경우)", + "workspaceConfig.settings.description": "작업 영역 설정" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/kor/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..fce07dd635 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "설정 열기", + "close": "닫기", + "saveAndRetry": "설정 저장 및 다시 시도", + "errorUnknownKey": "{1}은(는) 등록된 구성이 아니므로 {0}에 쓸 수 없습니다.", + "errorInvalidFolderConfiguration": "{0}이(가) 폴더 리소스 범위를 지원하지 않으므로 폴더 설정에 쓸 수 없습니다.", + "errorInvalidUserTarget": "{0}이(가) 글로벌 범위를 지원하지 않으므로 사용자 설정에 쓸 수 없습니다.", + "errorInvalidFolderTarget": "리소스가 제공되지 않으므로 폴더 설정에 쓸 수 없습니다.", + "errorNoWorkspaceOpened": "작업 영역이 열려 있지 않으므로 {0}에 쓸 수 없습니다. 먼저 작업 영역을 열고 다시 시도하세요.", + "errorInvalidConfiguration": "설정에 쓸 수 없습니다. **사용자 설정**을 열어 파일에서 오류/경고를 해결하고 다시 시도하세요.", + "errorInvalidConfigurationWorkspace": "설정에 쓸 수 없습니다. **작업 영역 설정**을 열어 파일에서 오류/경고를 해결하고 다시 시도하세요.", + "errorConfigurationFileDirty": "파일이 변경되어 설정에 쓸 수 없습니다. **사용자 설정** 파일을 저장하고 다시 시도하세요.", + "errorConfigurationFileDirtyWorkspace": "파일이 변경되어 설정에 쓸 수 없습니다. **작업 영역 설정** 파일을 저장하고 다시 시도하세요.", + "userTarget": "사용자 설정", + "workspaceTarget": "작업 영역 설정", + "folderTarget": "폴더 설정" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/kor/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..281553c30b --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "파일에 쓸 수 없습니다. 파일을 열어 오류/경고를 수정한 후 다시 시도하세요.", + "errorFileDirty": "파일이 오염되어 파일에 쓸 수 없습니다. 파일을 저장하고 다시 시도하세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/kor/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..c5d932c26d --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "원격 분석", + "telemetry.enableCrashReporting": "충돌 보고서를 Microsoft에 전송할 수 있도록 설정합니다.\n이 옵션을 적용하려면 다시 시작해야 합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/kor/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..ae87f5f811 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "확장 호스트가 10초 내에 시작되지 않았습니다. 첫 번째 줄에서 중지되었을 수 있습니다. 계속하려면 디버거가 필요합니다.", + "extensionHostProcess.startupFail": "확장 호스트가 10초 이내에 시작되지 않았습니다. 문제가 발생했을 수 있습니다.", + "extensionHostProcess.error": "확장 호스트에서 오류 발생: {0}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..6a03321304 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "{0}을(를) 구문 분석하지 못함: {1}.", + "fileReadFail": "파일 {0}을(를) 읽을 수 없음: {1}.", + "jsonsParseFail": "{0} 또는 {1}을(를) 구문 분석하지 못했습니다. {2}", + "missingNLSKey": "키 {0}에 대한 메시지를 찾을 수 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..90cbf78876 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "개발자 도구", + "restart": "확장 호스트 다시 시작", + "extensionHostProcess.crash": "확장 호스트가 예기치 않게 종료되었습니다.", + "extensionHostProcess.unresponsiveCrash": "확장 호스트가 응답하지 않아서 종료되었습니다.", + "overwritingExtension": "확장 {0}을(를) {1}(으)로 덮어쓰는 중입니다.", + "extensionUnderDevelopment": "{0}에서 개발 확장 로드 중" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/kor/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..08ae30c60d --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "Microsoft .NET Framework 4.5가 필요합니다. 설치하려면 링크를 클릭하세요.", + "installNet": ".NET Framework 4.5 다운로드", + "neverShowAgain": "다시 표시 안 함", + "trashFailed": "'{0}'을(를) 휴지통으로 이동하지 못함" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/kor/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..3d9e8de8f8 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "잘못된 파일 리소스({0})", + "fileIsDirectoryError": "파일이 디렉터리({0})입니다.", + "fileNotModifiedError": "파일 수정 안 됨", + "fileTooLargeError": "파일이 너무 커서 열 수 없음", + "fileBinaryError": "파일이 이진인 것 같으므로 테스트로 열 수 없습니다.", + "fileNotFoundError": "파일을 찾을 수 없습니다({0}).", + "fileMoveConflict": "이동/복사할 수 없습니다. 대상에 파일이 이미 있습니다.", + "unableToMoveCopyError": "이동/복사할 수 없습니다. 파일이 포함된 폴더를 파일로 대체합니다.", + "foldersCopyError": "폴더를 작업 영역에 복사할 수 없습니다. 개별 파일을 선택하여 복사하세요.", + "fileModifiedError": "파일 수정됨", + "fileReadOnlyError": "파일이 읽기 전용입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/kor/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..7fec628d7c --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "파일이 변경되었기 때문에 쓸 수 없습니다. **키 바인딩** 파일을 저장하고 다시 시도하세요.", + "parseErrors": "키 바인딩을 쓸 수 없습니다. **키 바인딩 파일**을 열어 파일의 오류/경고를 수정하고 다시 시도하세요.", + "errorInvalidConfiguration": "키 바인딩을 쓸 수 없습니다. **키 바인딩 파일**에 배열 형식이 아닌 개체가 있습니다. 파일을 열어 정리하고 다시 시도하세요.", + "emptyKeybindingsHeader": "키 바인딩을 이 파일에 넣어서 기본값을 덮어씁니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/kor/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..552d30fc64 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "비어 있지 않은 값이 필요합니다.", + "requirestring": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "optstring": "속성 `{0}`은(는) 생략할 수 있으며 `string` 형식이어야 합니다.", + "vscode.extension.contributes.keybindings.command": "키 바인딩이 트리거될 때 실행할 명령의 식별자입니다.", + "vscode.extension.contributes.keybindings.key": "키 또는 키 시퀀스(더하기 기호가 있는 개별 키, 공백이 있는 시퀀스, 예: Ctrl+O 및 Ctrl+L L을 동시에 누름)", + "vscode.extension.contributes.keybindings.mac": "Mac 특정 키 또는 키 시퀀스입니다.", + "vscode.extension.contributes.keybindings.linux": "Linux 특정 키 또는 키 시퀀스", + "vscode.extension.contributes.keybindings.win": "Windows 특정 키 또는 키 시퀀스", + "vscode.extension.contributes.keybindings.when": "키가 활성화되는 조건입니다.", + "vscode.extension.contributes.keybindings": "키 바인딩을 적용합니다.", + "invalid.keybindings": "잘못된 `contributes.{0}`입니다. {1}", + "unboundCommands": "사용 가능한 다른 명령:", + "keybindings.json.title": "키 바인딩 구성", + "keybindings.json.key": "키 또는 키 시퀀스(공백으로 구분됨)", + "keybindings.json.command": "실행할 명령의 이름", + "keybindings.json.when": "키가 활성화되는 조건입니다.", + "keybindings.json.args": "실행할 명령에 전달할 인수입니다.", + "keyboardConfigurationTitle": "키보드", + "dispatch": "`keydown.code`(권장) 또는 `keydown.keyCode`를 사용하는 키 누름에 대한 디스패치 논리를 제어합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/kor/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..18328f0a8d --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "오류: {0}", + "alertWarningMessage": "경고: {0}", + "alertInfoMessage": "정보: {0}", + "error": "오류", + "warning": "경고", + "info": "정보", + "close": "닫기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/kor/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..e1093dd0d4 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "예(&&Y)", + "cancelButton": "취소" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/kor/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..4ac5c3e20f --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "언어 선언을 적용합니다.", + "vscode.extension.contributes.languages.id": "언어의 ID입니다.", + "vscode.extension.contributes.languages.aliases": "언어에 대한 이름 별칭입니다.", + "vscode.extension.contributes.languages.extensions": "파일 확장이 언어에 연결되어 있습니다.", + "vscode.extension.contributes.languages.filenames": "파일 이름이 언어에 연결되어 있습니다.", + "vscode.extension.contributes.languages.filenamePatterns": "파일 이름 GLOB 패턴이 언어에 연결되어 있습니다.", + "vscode.extension.contributes.languages.mimetypes": "Mime 형식이 언어에 연결되어 있습니다.", + "vscode.extension.contributes.languages.firstLine": "언어 파일의 첫 번째 줄과 일치하는 정규식입니다.", + "vscode.extension.contributes.languages.configuration": "언어에 대한 구성 옵션을 포함하는 파일에 대한 상대 경로입니다.", + "invalid": "잘못된 `contributes.{0}`입니다. 배열이 필요합니다.", + "invalid.empty": "`contributes.{0}`에 대한 빈 값", + "require.id": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "opt.extensions": "`{0}` 속성은 생략 가능하며 `string[]` 형식이어야 합니다.", + "opt.filenames": "`{0}` 속성은 생략 가능하며 `string[]` 형식이어야 합니다.", + "opt.firstLine": "`{0}` 속성은 생략 가능하며 `string` 형식이어야 합니다.", + "opt.configuration": "`{0}` 속성은 생략 가능하며 `string` 형식이어야 합니다.", + "opt.aliases": "`{0}` 속성은 생략 가능하며 `string[]` 형식이어야 합니다.", + "opt.mimetypes": "`{0}` 속성은 생략 가능하며 `string[]` 형식이어야 합니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/kor/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..441530a01b --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1}", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/kor/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..6673bdbee2 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "textmate 토크나이저를 적용합니다.", + "vscode.extension.contributes.grammars.language": "이 구문이 적용되는 언어 식별자입니다.", + "vscode.extension.contributes.grammars.scopeName": "tmLanguage 파일에 사용되는 Textmate 범위 이름입니다.", + "vscode.extension.contributes.grammars.path": "tmLanguage 파일의 경로입니다. 확장 폴더의 상대 경로이며 일반적으로 './syntaxes/'로 시작합니다.", + "vscode.extension.contributes.grammars.embeddedLanguages": "이 문법에 포함된 언어가 있는 경우 언어 ID에 대한 범위 이름의 맵입니다.", + "vscode.extension.contributes.grammars.injectTo": "이 문법이 삽입되는 언어 범위 이름 목록입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/kor/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..9a33dc507b --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "`contributes.{0}.language`에 알 수 없는 언어가 있습니다. 제공된 값: {1}", + "invalid.scopeName": "`contributes.{0}.scopeName`에 문자열이 필요합니다. 제공된 값: {1}", + "invalid.path.0": "`contributes.{0}.path`에 문자열이 필요합니다. 제공된 값: {1}", + "invalid.injectTo": "`contributes.{0}.injectTo`의 값이 잘못되었습니다. 언어 범위 이름 배열이어야 합니다. 제공된 값: {1}", + "invalid.embeddedLanguages": "`contributes.{0}.embeddedLanguages` 값이 잘못되었습니다. 범위 이름에서 언어까지의 개체 맵이어야 합니다. 제공된 값: {1}", + "invalid.path.1": "확장 폴더({2})에 포함할 `contributes.{0}.path`({1})가 필요합니다. 확장이 이식 불가능해질 수 있습니다.", + "no-tm-grammar": "이 언어에 대해 등록된 TM 문법이 없습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/kor/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..007b48bbbc --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "더티 파일입니다. 다른 인코딩을 사용하여 파일을 다시 열기 전에 파일을 저장하세요.", + "genericSaveError": "'{0}'을(를) 저장하지 못했습니다. {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/kor/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..667ddcd330 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "파일을 백업할 수 없습니다(오류: {0}). 종료하려면 파일을 저장해 보세요." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/kor/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..92d8e300d3 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "{0}에 대한 변경 내용을 저장할까요?", + "saveChangesMessages": "다음 {0}개 파일에 대한 변경 내용을 저장할까요?", + "moreFile": "...1개의 추가 파일이 표시되지 않음", + "moreFiles": "...{0}개의 추가 파일이 표시되지 않음", + "saveAll": "모두 저장(&&S)", + "save": "저장(&&S)", + "dontSave": "저장 안 함(&&N)", + "cancel": "취소", + "saveChangesDetail": "변경 내용을 저장하지 않은 경우 변경 내용이 손실됩니다.", + "allFiles": "모든 파일", + "noExt": "확장 없음" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/kor/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..d803352828 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "토큰의 색 및 스타일입니다.", + "schema.token.foreground": "토큰의 전경색입니다.", + "schema.token.fontStyle": "규칙의 글꼴 스타일: '기울임꼴, '굵게' 및 '밑줄' 중 하나 또는 이들의 조합", + "schema.fontStyle.error": "글꼴 스타일은 '기울임꼴, '굵게' 및 '밑줄'의 조합이어야 합니다.", + "schema.properties.name": "규칙에 대한 설명입니다.", + "schema.properties.scope": "이 규칙과 일치하는 범위 선택기입니다.", + "schema.tokenColors.path": "tmTheme 파일의 경로(현재 파일의 상대 경로)입니다.", + "schema.colors": "구문 강조 표시를 위한 색" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/kor/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..9104c95d7c --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "확장된 폴더의 폴더 아이콘입니다. 확장된 폴더 아이콘은 선택 사항입니다. 설정하지 않으면 폴더에 대해 정의된 아이콘이 표시됩니다.", + "schema.folder": "축소된 폴더의 폴더 아이콘이며, folderExpanded가 설정되지 않은 경우 확장된 폴더의 폴더 아이콘이기도 합니다.", + "schema.file": "어떤 확장명, 파일 이름 또는 언어 ID와도 일치하는 모든 파일에 대해 표시되는 기본 파일 아이콘입니다.", + "schema.folderNames": "폴더 이름을 아이콘과 연결합니다. 개체 키는 경로 세그먼트를 제외한 폴더 이름입니다. 패턴이나 와일드카드는 허용되지 않습니다. 폴더 이름 일치는 대/소문자를 구분하지 않습니다.", + "schema.folderName": "연결에 대한 아이콘 정의의 ID입니다.", + "schema.folderNamesExpanded": "폴더 이름을 확장된 폴더의 아이콘과 연결합니다. 개체 키는 경로 세그먼트를 제외한 폴더 이름입니다. 패턴이나 와일드카드는 허용되지 않습니다. 폴더 이름 일치는 대/소문자를 구분하지 않습니다.", + "schema.folderNameExpanded": "연결에 대한 아이콘 정의의 ID입니다.", + "schema.fileExtensions": "파일 확장명을 아이콘과 연결합니다. 개체 키는 파일 확장명입니다. 확장명은 파일 이름에서 마지막 점 뒤에 있는 마지막 세그먼트(점 불포함)입니다. 확장명은 대/소문자를 구분하지 않고 비교됩니다.", + "schema.fileExtension": "연결에 대한 아이콘 정의의 ID입니다.", + "schema.fileNames": "파일 이름을 아이콘과 연결합니다. 개체 키는 경로 세그먼트를 제외한 전체 파일 이름입니다. 파일 이름은 점과 파일 확장명을 포함할 수 있습니다. 패턴이나 와일드카드는 허용되지 않습니다. 파일 이름 일치는 대/소문자를 구분하지 않습니다.", + "schema.fileName": "연결에 대한 아이콘 정의의 ID입니다.", + "schema.languageIds": "언어를 아이콘과 연결합니다. 개체 키는 언어 기여 지점에 정의된 언어 ID입니다.", + "schema.languageId": "연결에 대한 아이콘 정의의 ID입니다.", + "schema.fonts": "아이콘 정의에 사용된 글꼴입니다.", + "schema.id": "글꼴의 ID입니다.", + "schema.src": "글꼴의 위치입니다.", + "schema.font-path": "현재 아이콘 테마 파일의 상대 글꼴 경로입니다.", + "schema.font-format": "글꼴의 형식입니다.", + "schema.font-weight": "글꼴의 두께입니다.", + "schema.font-sstyle": "글꼴의 스타일입니다.", + "schema.font-size": "글꼴의 기본 크기입니다.", + "schema.iconDefinitions": "파일을 아이콘과 연결할 때 사용할 수 있는 모든 아이콘의 설명입니다.", + "schema.iconDefinition": "아이콘 정의입니다. 개체 키는 정의의 ID입니다.", + "schema.iconPath": "SVG 또는 PNG를 사용하는 경우: 이미지의 경로입니다. 아이콘 집합 파일의 상대 경로입니다.", + "schema.fontCharacter": "문자 모양 글꼴을 사용하는 경우: 사용할 글꼴의 문자입니다.", + "schema.fontColor": "문자 모양 글꼴을 사용하는 경우: 사용할 색입니다.", + "schema.fontSize": "글꼴을 사용하는 경우: 텍스트 글꼴에 대한 글꼴 크기(백분율로 표시)입니다. 설정하지 않으면 기본값으로 글꼴 정의의 크기가 사용됩니다.", + "schema.fontId": "글꼴을 사용하는 경우: 글꼴의 ID입니다. 설정하지 않으면 기본값으로 첫 번째 글꼴 정의가 사용됩니다.", + "schema.light": "밝은 색 테마에서 파일 아이콘에 대한 선택적 연결입니다.", + "schema.highContrast": "고대비 색 테마에서 파일 아이콘에 대한 선택적 연결입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/kor/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..75c5e90591 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "JSON 테마 파일을 구문 분석하는 중 문제 발생: {0}", + "error.invalidformat.colors": "색 테마 파일 {0}을(를) 구문 분석하는 중 문제가 발생했습니다. 'colors' 속성이 'object' 형식이 아닙니다.", + "error.invalidformat.tokenColors": "색 테마 파일 {0}을(를) 구문 분석하는 중 문제가 발생했습니다. 'tokenColors' 속성이 색을 지정하는 배열 또는 TextMate 테마 파일의 경로여야 합니다.", + "error.plist.invalidformat": "tmTheme 파일 {0}을(를) 구문 분석하는 중 문제가 발생했습니다. 'settings'가 배열이 아닙니다.", + "error.cannotparse": "tmTheme 파일 {0}을(를) 구문 분석하는 중 문제가 발생했습니다.", + "error.cannotload": "tmTheme 파일 {0}을(를) 로드하는 중 문제가 발생했습니다. {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/kor/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..7b22fb1317 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "사용자 설정에 사용된 아이콘 테마의 ID입니다.", + "vscode.extension.contributes.themes.label": "UI에 표시되는 색 테마의 레이블입니다.", + "vscode.extension.contributes.themes.uiTheme": "편집기 주변의 색을 정의하는 기본 테마입니다. 'vs'는 밝은색 테마이고, 'vs-dark'는 어두운색 테마입니다. 'hc-black'은 어두운 고대비 테마입니다.", + "vscode.extension.contributes.themes.path": "tmTheme 파일의 경로입니다. 확장 폴더의 상대 경로이며 일반적으로 './themes/themeFile.tmTheme'입니다.", + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "사용자 설정에 사용된 아이콘 테마의 ID입니다.", + "vscode.extension.contributes.iconThemes.label": "UI에 표시된 아이콘 테마의 레이블입니다.", + "vscode.extension.contributes.iconThemes.path": "아이콘 테마 정의 파일의 경로입니다. 확장 폴더의 상대 경로이며 일반적으로 './icons/awesome-icon-theme.json'입니다.", + "migration.completed": "새 테마 설정이 사용자 설정에 추가되었습니다. {0}에서 백업을 사용할 수 있습니다.", + "error.cannotloadtheme": "Unable to load {0}: {1}", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "`contributes.{0}.path`에 문자열이 필요합니다. 제공된 값: {1}", + "invalid.path.1": "확장 폴더({2})에 포함할 `contributes.{0}.path`({1})가 필요합니다. 확장이 이식 불가능해질 수 있습니다.", + "reqid": "`contributes.{0}.id`에 문자열이 필요합니다. 제공된 값: {1}", + "error.cannotloadicontheme": "Unable to load {0}", + "error.cannotparseicontheme": "Problems parsing file icons file: {0}", + "colorTheme": "Specifies the color theme used in the workbench.", + "colorThemeError": "Theme is unknown or not installed.", + "iconTheme": "워크벤치에서 사용되는 아이콘 테마를 지정합니다. 'null'로 지정하면 파일 아이콘을 표시하지 않습니다.", + "noIconThemeDesc": "No file icons", + "iconThemeError": "File icon theme is unknown or not installed.", + "workbenchColors": "현재 선택한 색 테마에서 색을 재정의합니다.", + "workbenchColors.deprecated": "이 설정은 더 이상 실험적 설정이 아니며 이름이\n 'workbench.colorCustomizations'로 변경되었습니다.", + "workbenchColors.deprecatedDescription": "대신 'workbench.colorCustomizations'를 사용합니다.", + "editorColors": "현재 선택된 색 테마에서 편집기 색상과 글꼴 스타일을 재정의합니다.", + "editorColors.comments": "주석의 색 및 스타일을 설정합니다.", + "editorColors.strings": "문자열 리터럴의 색 및 스타일을 설정합니다.", + "editorColors.keywords": "키워드의 색과 스타일을 설정합니다.", + "editorColors.numbers": "숫자 리터럴의 색과 스타일을 설정합니다.", + "editorColors.types": "형식 선언 및 참조의 색 및 스타일을 설정합니다.", + "editorColors.functions": "함수 선언 및 참조의 색 및 스타일을 설정합니다.", + "editorColors.variables": "변수 선언 및 참조의 색 및 스타일을 설정합니다.", + "editorColors.textMateRules": "textmate 테마 설정 규칙을 사용하여 색 및 스타일을 설정합니다(고급)." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/configuration-editing/out/extension.i18n.json b/i18n/ptb/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..2dcbb6fccc --- /dev/null +++ b/i18n/ptb/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "Exemplo" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/ptb/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..a300023d8c --- /dev/null +++ b/i18n/ptb/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "por exemplo meuArquivo.txt", + "activeEditorMedium": "e.g. minhaPasta/meuArquivo.txt", + "activeEditorLong": "por exemplo /Usuários/Desenvolvimento/meuProjeto/minhaPasta/meuArquivo/txt", + "rootName": "por exemplo, myFolder1, myFolder2, myFolder3", + "rootPath": "por exemplo /Usuários/desenvolvimento/meuProjeto", + "folderName": "por exemplo, myFolder", + "folderPath": "por exemplo, /Users/Development/myFolder", + "appName": "e.g. VS Code", + "dirty": "Um indicador de alteração se o editor ativo foi alterado", + "separator": "um separador condicional (' - ') que somente é mostrado quando envolvido por variáveis com valores", + "assocLabelFile": "Arquivos com Extensão", + "assocDescriptionFile": "Mapear todos arquivos que correspondem ao padrão global no seu nome de arquivo à linguagem com o identificador dado", + "assocLabelPath": "Arquivos com Caminho", + "assocDescriptionPath": "Mapear todos os arquivos que correspondem ao caminho absoluto global no seu caminho à linguagem com o identificador dado", + "fileLabel": "Arquivos por Extensão", + "fileDescription": "Combina todos os arquivos de uma extensão de arquivo específica.", + "filesLabel": "Arquivos com Várias Extensões", + "filesDescription": "Combina todos os arquivos com qualquer uma das extensões de arquivo.", + "derivedLabel": "Arquivos com Irmãos por Nome", + "derivedDescription": "Combina arquivos que têm irmãos com o mesmo nome, mas uma extensão diferente.", + "topFolderLabel": "Pasta por Nome (Nível Superior)", + "topFolderDescription": "Combina uma pasta de nível superior com um nome específico.", + "topFoldersLabel": "Pastas com Vários Nomes (Nível Superior)", + "topFoldersDescription": "Combina várias pastas de nível superior.", + "folderLabel": "Pasta por Nome (Qualquer Local)", + "folderDescription": "Combina uma pasta com um nome específico em qualquer local.", + "falseDescription": "Desabilita o padrão.", + "trueDescription": "Habilita o padrão.", + "siblingsDescription": "Combina arquivos que têm irmãos com o mesmo nome, mas uma extensão diferente.", + "languageSpecificEditorSettings": "Configurações do editor especificas para a linguagem", + "languageSpecificEditorSettingsDescription": "Sobrescrever as configurações do editor para a linguagem" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/css/client/out/cssMain.i18n.json b/i18n/ptb/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..de06af93ad --- /dev/null +++ b/i18n/ptb/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "Servidor de linguagem CSS" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/css/package.i18n.json b/i18n/ptb/extensions/css/package.i18n.json new file mode 100644 index 0000000000..3e4b3449dd --- /dev/null +++ b/i18n/ptb/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "Número inválido de parâmetros", + "css.lint.boxModel.desc": "Não use largura ou altura ao usar preenchimento ou borda", + "css.lint.compatibleVendorPrefixes.desc": "Ao usar um prefixo específico de fornecedor, certifique-se de também incluir todas as outras propriedades específicas do fornecedor", + "css.lint.duplicateProperties.desc": "Não use as definições de estilo duplicadas", + "css.lint.emptyRules.desc": "Não use conjuntos de regra em branco", + "css.lint.float.desc": "Evite usar 'float'. Floats levam a CSS frágil, que é fácil de quebrar se um aspecto do layout for alterado.", + "css.lint.fontFaceProperties.desc": "A regra @font-face deve definir propriedades 'src' e 'font-family'", + "css.lint.hexColorLength.desc": "Cores hexadecimais devem consistir em três ou seis números hexadecimais", + "css.lint.idSelector.desc": "Seletores não devem conter IDs, pois essas regras estão firmemente acopladas ao HTML.", + "css.lint.ieHack.desc": "IE hacks somente são necessários ao dar suporte ao IE7 e mais antigos", + "css.lint.important.desc": "Evite usar !important. Esta é uma indicação de que a especificidade do CSS inteiro saiu de controle e precisa ser fatorada novamente.", + "css.lint.importStatement.desc": "Instruções de importação não carregam em paralelo", + "css.lint.propertyIgnoredDueToDisplay.desc": "Propriedade ignorada devido à exibição. Por exemplo, com 'display: inline', as propriedades width, height, margin-top, margin-bottom e float não têm efeito", + "css.lint.universalSelector.desc": "O seletor universal (*) é conhecido por ser lento", + "css.lint.unknownProperties.desc": "Propriedade desconhecida.", + "css.lint.unknownVendorSpecificProperties.desc": "Propriedade específica do fornecedor desconhecida.", + "css.lint.vendorPrefix.desc": "Ao usar um prefixo específico do fornecedor, inclua também a propriedade padrão", + "css.lint.zeroUnits.desc": "Nenhuma unidade para zero é necessária", + "css.trace.server.desc": "Rastrear a comunicação entre o VS Code e o servidor de linguagem CSS.", + "css.validate.title": "Controla a validação CSS and a gravidade dos problemas.", + "css.validate.desc": "Habilita ou desabilita todas as validações", + "less.lint.argumentsInColorFunction.desc": "Número inválido de parâmetros", + "less.lint.boxModel.desc": "Não use largura ou altura ao usar preenchimento ou borda", + "less.lint.compatibleVendorPrefixes.desc": "Ao usar um prefixo específico de fornecedor, certifique-se de também incluir todas as outras propriedades específicas do fornecedor", + "less.lint.duplicateProperties.desc": "Não use as definições de estilo duplicadas", + "less.lint.emptyRules.desc": "Não use conjuntos de regra em branco", + "less.lint.float.desc": "Evite usar 'float'. Floats levam a CSS frágil, que é fácil de quebrar se um aspecto do layout for alterado.", + "less.lint.fontFaceProperties.desc": "A regra @font-face deve definir propriedades 'src' e 'font-family'", + "less.lint.hexColorLength.desc": "Cores hexadecimais devem consistir em três ou seis números hexadecimais", + "less.lint.idSelector.desc": "Seletores não devem conter IDs, pois essas regras estão firmemente acopladas ao HTML.", + "less.lint.ieHack.desc": "IE hacks somente são necessários ao dar suporte ao IE7 e mais antigos", + "less.lint.important.desc": "Evite usar !important. Esta é uma indicação de que a especificidade do CSS inteiro saiu de controle e precisa ser fatorada novamente.", + "less.lint.importStatement.desc": "Instruções de importação não carregam em paralelo", + "less.lint.propertyIgnoredDueToDisplay.desc": "Propriedade ignorada devido à exibição. Por exemplo, com 'display: inline', as propriedades width, height, margin-top, margin-bottom e float não têm efeito", + "less.lint.universalSelector.desc": "O seletor universal (*) é conhecido por ser lento", + "less.lint.unknownProperties.desc": "Propriedade desconhecida.", + "less.lint.unknownVendorSpecificProperties.desc": "Propriedade específica do fornecedor desconhecida.", + "less.lint.vendorPrefix.desc": "Ao usar um prefixo específico do fornecedor, inclua também a propriedade padrão", + "less.lint.zeroUnits.desc": "Nenhuma unidade para zero é necessária", + "less.validate.title": "Controla MENOS as severidades de validação e problemas.", + "less.validate.desc": "Habilita ou desabilita todas as validações", + "scss.lint.argumentsInColorFunction.desc": "Número inválido de parâmetros", + "scss.lint.boxModel.desc": "Não use largura ou altura ao usar preenchimento ou borda", + "scss.lint.compatibleVendorPrefixes.desc": "Ao usar um prefixo específico de fornecedor, certifique-se de também incluir todas as outras propriedades específicas do fornecedor", + "scss.lint.duplicateProperties.desc": "Não use as definições de estilo duplicadas", + "scss.lint.emptyRules.desc": "Não use conjuntos de regra em branco", + "scss.lint.float.desc": "Evite usar 'float'. Floats levam a CSS frágil, que é fácil de quebrar se um aspecto do layout for alterado.", + "scss.lint.fontFaceProperties.desc": "A regra @font-face deve definir propriedades 'src' e 'font-family'", + "scss.lint.hexColorLength.desc": "Cores hexadecimais devem consistir em três ou seis números hexadecimais", + "scss.lint.idSelector.desc": "Seletores não devem conter IDs, pois essas regras estão firmemente acopladas ao HTML.", + "scss.lint.ieHack.desc": "IE hacks somente são necessários ao dar suporte ao IE7 e mais antigos", + "scss.lint.important.desc": "Evite usar !important. Esta é uma indicação de que a especificidade do CSS inteiro saiu de controle e precisa ser fatorada novamente.", + "scss.lint.importStatement.desc": "Instruções de importação não carregam em paralelo", + "scss.lint.propertyIgnoredDueToDisplay.desc": "Propriedade ignorada devido à exibição. Por exemplo, com 'display: inline', as propriedades width, height, margin-top, margin-bottom e float não têm efeito", + "scss.lint.universalSelector.desc": "O seletor universal (*) é conhecido por ser lento", + "scss.lint.unknownProperties.desc": "Propriedade desconhecida.", + "scss.lint.unknownVendorSpecificProperties.desc": "Propriedade específica do fornecedor desconhecida.", + "scss.lint.vendorPrefix.desc": "Ao usar um prefixo específico do fornecedor, inclua também a propriedade padrão", + "scss.lint.zeroUnits.desc": "Nenhuma unidade para zero é necessária", + "scss.validate.title": "Controla severidades de validação e problemas SCSS.", + "scss.validate.desc": "Habilita ou desabilita todas as validações", + "less.colorDecorators.enable.desc": "Habilita ou desabilita decoradores de cores", + "scss.colorDecorators.enable.desc": "Habilita ou desabilita decoradores de cores", + "css.colorDecorators.enable.desc": "Habilita ou desabilita decoradores de cores", + "css.colorDecorators.enable.deprecationMessage": "A configuração 'css.colorDecorators.enable' foi descontinuada em favor de 'editor.colorDecorators'.", + "scss.colorDecorators.enable.deprecationMessage": "A configuração 'scss.colorDecorators.enable' foi descontinuada em favor de 'editor.colorDecorators'.", + "less.colorDecorators.enable.deprecationMessage": "A configuração 'less.colorDecorators.enable' foi descontinuada em favor de 'editor.colorDecorators'." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/emmet/package.i18n.json b/i18n/ptb/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..32e89f92b2 --- /dev/null +++ b/i18n/ptb/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "Envelope com a abreviatura", + "command.wrapIndividualLinesWithAbbreviation": "Envelopar Linhas Individuais com Abreviatura", + "command.removeTag": "Remover Tag", + "command.updateTag": "Atualizar Tag", + "command.matchTag": "Ir para par de correspondência", + "command.balanceIn": "Saldo (interno)", + "command.balanceOut": "Saldo (Externo)", + "command.prevEditPoint": "Ir para Ponto de edição anterior", + "command.nextEditPoint": "Ir para o próximo ponto de edição", + "command.mergeLines": "Mesclar linhas", + "command.selectPrevItem": "Selecione o Item anterior", + "command.selectNextItem": "Selecionar o próximo Item", + "command.splitJoinTag": "Dividir/Unir Tag", + "command.toggleComment": "Alternar Comentário de Linha", + "command.evaluateMathExpression": "Avaliar a expressão matemática", + "command.updateImageSize": "Atualizar Tamanho da Imagem", + "command.reflectCSSValue": "Refletir Valor do CSS", + "command.incrementNumberByOne": "Incremento de 1", + "command.decrementNumberByOne": "Decrementar por 1", + "command.incrementNumberByOneTenth": "Incremento de 0.1", + "command.decrementNumberByOneTenth": "Decrementar por 0.1", + "command.incrementNumberByTen": "Incremento de 10", + "command.decrementNumberByTen": "Decrementar por 10", + "emmetSyntaxProfiles": "Definir o perfil para a sintaxe especificada ou usar seu próprio perfil com regras específicas.", + "emmetExclude": "Uma matriz de línguagens onde abreviaturas emmet não devem ser expandidas.", + "emmetExtensionsPath": "Caminho para uma pasta que contém os perfis de emmet e trechos de código.'", + "emmetShowExpandedAbbreviation": "Mostra abreviaturas emmet expandidas como sugestões.\nA opção \"inMarkupAndStylesheetFilesOnly\" aplica-se a html, haml, jade, slim, xml, xsl, css, scss, sass, less e stylus.\nA opção \"always\" se aplica a todas as partes do arquivo independentemente de marcação/css.", + "emmetShowAbbreviationSuggestions": "Mostra possíveis abreviaturas emmet como sugestões. Não aplicável em folhas de estilo ou quando emmet.showExpandedAbbreviation é definido como \"nunca\".", + "emmetIncludeLanguages": "Habilita as abreviaturas do emmet em idiomas que não são suportados por padrão. Adicione um mapeamento aqui entre a linguagem e a linguagem emmet suportada.\n Por exemplo: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "Variáveis a serem usadas em trechos de código emmet", + "emmetTriggerExpansionOnTab": "Quando habilitado, abreviações emmet são expandidas ao pressionar TAB.", + "emmetPreferences": "Preferências usadas para modificar o comportamento de algumas ações e resolvedores de Emmet.", + "emmetPreferencesIntUnit": "Unidade padrão para valores inteiros", + "emmetPreferencesFloatUnit": "Unidade padrão para valores float", + "emmetPreferencesCssAfter": "Símbolo a ser colocado no final da propriedade CSS quando expandir abreviaturas CSS", + "emmetPreferencesSassAfter": "Símbolo a ser colocado no final da propriedade CSS quando expandir abreviaturas CSS em arquivos Sass", + "emmetPreferencesStylusAfter": "Símbolo a ser colocado no final da propriedade CSS quando expandir abreviaturas CSS em arquivos Stylus", + "emmetPreferencesCssBetween": "Símbolo a ser colocado entre a propriedade CSS e o valor quando expandir abreviaturas CSS", + "emmetPreferencesSassBetween": "Símbolo a ser colocado entre a propriedade CSS e o valor quando expandir abreviaturas CSS em arquivos Sass", + "emmetPreferencesStylusBetween": "Símbolo a ser colocado entre a propriedade CSS e o valor quando expandir abreviaturas CSS em arquivos Stylus", + "emmetShowSuggestionsAsSnippets": "Se verdadeiro, então as sugestões emmet aparecerão como trechos, permitindo você requisitá-los conforme a configuração de editor.snippetSuggestions." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/ptb/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..ea9cca86ba --- /dev/null +++ b/i18n/ptb/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "Imagens devem usar o protocolo HTTPS.", + "svgsNotValid": "SVGs não são uma fonte de imagem válida.", + "embeddedSvgsNotValid": "SVGs embutidos não são uma fonte de imagem válida.", + "dataUrlsNotValid": "URLs de dados não são uma fonte de imagem válida.", + "relativeUrlRequiresHttpsRepository": "URLs relativas de Imagem exigem um repositório com o protocolo HTTPS para serem especificadas no pacote .json.", + "relativeIconUrlRequiresHttpsRepository": "Um ícone requer um repositório com o protocolo HTTPS para ser especificado neste package.json.", + "relativeBadgeUrlRequiresHttpsRepository": "URLs relativas de crachá exigem um repositório com o protocolo HTTPS para ser especificado neste package.json." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/ptb/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..242b7994b4 --- /dev/null +++ b/i18n/ptb/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "Configurações do editor especificas para a linguagem", + "languageSpecificEditorSettingsDescription": "Sobrescrever as configurações do editor para a linguagem" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/git/out/askpass-main.i18n.json b/i18n/ptb/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..25a8031dff --- /dev/null +++ b/i18n/ptb/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "Credenciais ausentes ou inválidas." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/git/out/commands.i18n.json b/i18n/ptb/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..e657a8131b --- /dev/null +++ b/i18n/ptb/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "Etiqueta em {0}", + "remote branch at": "Ramo remoto em {0}", + "create branch": "$(plus) criar nova ramificação", + "repourl": "URL do repositório", + "parent": "Diretório pai", + "cloning": "Clonando repositório do Git...", + "openrepo": "Abrir Repositório", + "proposeopen": "Gostaria de abrir o repositório clonado?", + "path to init": "Caminho da pasta", + "provide path": "Por favor, forneça um caminho de pasta para inicializar um repositório Git", + "HEAD not available": "Versão HEAD de '{0}' não está disponível.", + "confirm stage files with merge conflicts": "Tem certeza que deseja processar {0} arquivos com conflitos de mesclagem?", + "confirm stage file with merge conflicts": "Tem certeza que deseja processar {0} com conflitos de mesclagem?", + "yes": "Sim", + "confirm revert": "Tem certeza que deseja reverter as alterações selecionadas em {0}?", + "revert": "Reverter as alterações", + "discard": "Descartar alterações", + "confirm delete": "Tem certeza que deseja EXCLUIR {0}?", + "delete file": "Excluir arquivo", + "confirm discard": "Tem certeza que deseja descartar as alterações em {0}?", + "confirm discard multiple": "Tem certeza que deseja descartar as alterações em {0} arquivos?", + "warn untracked": "Isso irá excluir {0} registros não rastreados!", + "confirm discard all single": "Tem certeza que deseja descartar as alterações em {0}?", + "confirm discard all": "Tem certeza que deseja descartar todas as alterações em {0} arquivos?\nIsso é IRREVERSíVEL!\nO conjunto de trabalhando atual será PERDIDO PARA SEMPRE.", + "discardAll multiple": "Descartar 1 Arquivo", + "discardAll": "Descartar Todos os {0} Arquivos", + "confirm delete multiple": "Tem certeza que deseja excluir {0} arquivos?", + "delete files": "Excluir arquivos", + "there are untracked files single": "O seguinte arquivo não controlado será excluído do disco se descartado: {0}.", + "there are untracked files": "Existem {0} arquivos não controlados que serão excluídos do disco se descartados.", + "confirm discard all 2": "{0}\n\n é IRREVERSÍVEL, o conjunto de trabalho atual será PERDIDO PARA SEMPRE.", + "yes discard tracked": "Descartar 1 arquivo controlado", + "yes discard tracked multiple": "Descartar arquivos {0} controlados", + "no staged changes": "Não há nenhuma modificação escalonada para confirmar.\n\nGostaria de escalonar automaticamente todas as suas alterações e confirmá-las diretamente?", + "always": "Sempre", + "no changes": "Não há mudanças para confirmar.", + "commit message": "Confirmar mensagem", + "provide commit message": "Por favor, forneça uma mensagem de commit", + "select a ref to checkout": "Selecione uma referência para check-out", + "branch name": "Nome do Ramo", + "provide branch name": "Por favor, forneça um nome de ramo", + "select branch to delete": "Selecione uma ramificação para excluir", + "confirm force delete branch": "A ramificação '{0}' não foi totalmente mesclada. Excluir mesmo assim?", + "delete branch": "Excluir ramificação", + "select a branch to merge from": "Selecione uma ramificação para mesclar", + "merge conflicts": "Existem conflitos de mesclagem. Resolva-os antes de confirmar.", + "tag name": "Nome do rótulo", + "provide tag name": "Por favor, forneça um nome de Tag", + "tag message": "Mensagem", + "provide tag message": "Por favor, forneça uma mensagem para comentar o rótulo", + "no remotes to pull": "O seu repositório não possui remotos configurados para efetuar pull.", + "pick remote pull repo": "Selecione um remoto para efeutar o pull da ramificação", + "no remotes to push": "O seu repositório não possui remotos configurados para efetuar push.", + "push with tags success": "Envio de rótulos finalizado com sucesso.", + "nobranch": "Por favor, faça checkout em um ramo para fazer push em um remoto.", + "pick remote": "Pegue um remoto para publicar o ramo '{0}':", + "sync is unpredictable": "Esta ação vai fazer push e pull nos commits de e para '{0}'.", + "ok": "OK", + "never again": "Ok, Nunca Mostrar Novamente", + "no remotes to publish": "Seu repositório não possui remotos configurados para publicação.", + "no changes stash": "Não há nenhuma mudança para esconder.", + "provide stash message": "Opcionalmente forneça uma mensagem para esconder.", + "stash message": "Mensagem oculta", + "no stashes": "Não há esconderijos para restaurar.", + "pick stash to pop": "Escolher um esconderijo de pop", + "clean repo": "Por favor, limpe sua árvore de trabalho do repositório antes de fazer check-out.", + "cant push": "Não pode empurrar referências para remoto. Execute 'Pull' primeiro para integrar suas alterações.", + "git error details": "Git: {0}", + "git error": "Erro de Git", + "open git log": "Abrir Histórico do Git" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/git/out/main.i18n.json b/i18n/ptb/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..b156681fe7 --- /dev/null +++ b/i18n/ptb/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "Usando git {0} de {1}", + "updateGit": "Atualizar o Git", + "neverShowAgain": "Não mostrar novamente", + "git20": "Você parece ter o git {0} instalado. Code funciona melhor com git > = 2" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/git/out/model.i18n.json b/i18n/ptb/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..fdb5c669c3 --- /dev/null +++ b/i18n/ptb/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "Não existem repositórios disponíveis", + "pick repo": "Escolher um repositório" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/git/out/repository.i18n.json b/i18n/ptb/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..1bcb025f38 --- /dev/null +++ b/i18n/ptb/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Abrir", + "index modified": "Índice modificado", + "modified": "Modificado", + "index added": "Índice adicionado", + "index deleted": "Índice excluído", + "deleted": "Excluído", + "index renamed": "Índice renomeado", + "index copied": "Índice copiado", + "untracked": "Não acompanhado", + "ignored": "Ignorado", + "both deleted": "Ambos Excluídos", + "added by us": "Adicionado por nós", + "deleted by them": "Excluído por eles", + "added by them": "Adicionado por eles", + "deleted by us": "Excluído por nós", + "both added": "Ambos adicionados", + "both modified": "Ambos modificados", + "commit": "Confirmar", + "merge changes": "Mesclar Alterações", + "staged changes": "Alterações em Etapas", + "changes": "Alterações", + "ok": "OK", + "neveragain": "Nunca Mostrar Novamente", + "huge": "O repositório git em '{0}' tem muitas atualizações ativas, somente um subconjunto de funcionalidades do Git será habilitado." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/git/out/scmProvider.i18n.json b/i18n/ptb/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..0049d4b22b --- /dev/null +++ b/i18n/ptb/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "Confirmar" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/git/out/statusbar.i18n.json b/i18n/ptb/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..8334222980 --- /dev/null +++ b/i18n/ptb/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "Checkout...", + "sync changes": "Sincronizar alterações", + "publish changes": "Publicar Alterações", + "syncing changes": "Sincronizando Alterações..." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/git/package.i18n.json b/i18n/ptb/extensions/git/package.i18n.json new file mode 100644 index 0000000000..91af5f7e1e --- /dev/null +++ b/i18n/ptb/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "Clonar", + "command.init": "Inicializar Repositório", + "command.close": "Fechar o repositório", + "command.refresh": "Atualizar", + "command.openChange": "Abrir alterações", + "command.openFile": "Abrir Arquivo", + "command.openHEADFile": "Abrir arquivo (HEAD)", + "command.stage": "Estagiar Alterações", + "command.stageAll": "Estagiar Todas Alterações", + "command.stageSelectedRanges": "Estagiar Faixas Selecionadas", + "command.revertSelectedRanges": "Reverter Faixas Selecionadas", + "command.unstage": "Desestagiar Alterações", + "command.unstageAll": "Desestagiar Todas Alterações", + "command.unstageSelectedRanges": "Desestagiar Faixas Selecionadas", + "command.clean": "Descartar Alterações", + "command.cleanAll": "Descartar Todas as Alterações", + "command.commit": "Confirmar", + "command.commitStaged": "Confirmar os preparados", + "command.commitStagedSigned": "Confirmar Estagiados (Desconectado)", + "command.commitStagedAmend": "Confirmar estagiado (alterar)", + "command.commitAll": "Confirmar tudo", + "command.commitAllSigned": "Confirmar Tudo (Desconectado)", + "command.commitAllAmend": "Comitar Tudo (Corrigir)", + "command.undoCommit": "Desfazer Ultima Confirmação", + "command.checkout": "Fazer checkout para...", + "command.branch": "Criar Ramificação...", + "command.deleteBranch": "Excluir Ramificação...", + "command.merge": "Mesclar ramificação...", + "command.createTag": "Criar Tag", + "command.pull": "Efetuar pull", + "command.pullRebase": "Efetuar pull (Rebase)", + "command.pullFrom": "Fazer pull de...", + "command.push": "Enviar por push", + "command.pushTo": "Enviar por push para...", + "command.pushWithTags": "Mover com Tags", + "command.sync": "Sincronizar", + "command.publish": "Publicar Ramo", + "command.showOutput": "Mostrar Saída do Git", + "command.ignore": "Adicionar arquivo ao .gitignore", + "command.stash": "Esconder", + "command.stashPop": "Pop Stash...", + "command.stashPopLatest": "Pop mais recente Stash", + "config.enabled": "Se o git estiver habilitado", + "config.path": "Caminho para o executável do git", + "config.autorefresh": "Se a atualização automática estiver habilitada", + "config.autofetch": "Se a recuperação automática estiver habilitada", + "config.enableLongCommitWarning": "Se mensagens longas de confirmação devem ter aviso", + "config.confirmSync": "Confirmar antes de sincronizar repositórios git", + "config.countBadge": "Controla o contador de distintivos do git. 'todos' considera todas as alterações. 'rastreado' considera apenas as alterações controladas. 'desligado' desliga o contador.", + "config.checkoutType": "Controla quais tipos de ramos são listados quando executando `Checkout para... `. `todos` mostra todas as referências, `local` mostra apenas os ramos locais, `etiqueta` mostra apenas etiquetas e `remoto` mostra apenas os ramos remotos.", + "config.ignoreLegacyWarning": "Ignora o aviso de Git legado", + "config.ignoreLimitWarning": "Ignora o aviso quando houver muitas alterações em um repositório", + "config.defaultCloneDirectory": "O local padrão onde clonar um repositório git", + "config.enableSmartCommit": "Confirme todas as alterações quando não há modificações planejadas.", + "config.enableCommitSigning": "Habilitar Commit assinando com GPG.", + "config.discardAllScope": "Controla as alterações que são descartadas pelo comando 'Descartar todas as alterações'. 'todos' descarta todas as alterações. 'rastreados' descarta somente arquivos rastreados. 'prompt' mostra uma caixa de diálogo de alerta cada vez que a ação é executada." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/grunt/out/main.i18n.json b/i18n/ptb/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..25babfbb2e --- /dev/null +++ b/i18n/ptb/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Auto detecção de Grunt falhou com erro: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/grunt/package.i18n.json b/i18n/ptb/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..147ca85e76 --- /dev/null +++ b/i18n/ptb/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "Controla se a deteção automática de tarefas do Grunt está ligado ou desligado. Padrão é ligado." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/gulp/out/main.i18n.json b/i18n/ptb/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..3db09ff526 --- /dev/null +++ b/i18n/ptb/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Auto detecção de gulp falhou com erro: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/gulp/package.i18n.json b/i18n/ptb/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..fc1ce235c0 --- /dev/null +++ b/i18n/ptb/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "Controla se a detecção automática de tarefas Gulp está ativada ou desativada. Por padrão, é ativado." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/html/client/out/htmlMain.i18n.json b/i18n/ptb/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..3121aa7304 --- /dev/null +++ b/i18n/ptb/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "Servidor de Linguagem HTML" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/html/package.i18n.json b/i18n/ptb/extensions/html/package.i18n.json new file mode 100644 index 0000000000..5060872178 --- /dev/null +++ b/i18n/ptb/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "Ativar/desativar o formatador HTML padrão", + "html.format.wrapLineLength.desc": "Quantidade máxima de caracteres por linha (0 = desativar).", + "html.format.unformatted.desc": "Lista de tags, separados por vírgula, que não deveria ser reformatada. o padrão é 'nulo' para todas as tags listadas em https://www.w3.org/TR/html5/dom.html#phrasing-content.", + "html.format.contentUnformatted.desc": "Lista de tags, separada por vírgula, onde o conteúdo não deve ser reformatado. o padrão é 'nulo' para a tag 'pré'.", + "html.format.indentInnerHtml.desc": "Indentar secões e .", + "html.format.preserveNewLines.desc": "Se quebras de linha existentes antes de elementos deveriam ser preservadas. Só funciona antes de elementos, não dentro de rótulos ou para texto.", + "html.format.maxPreserveNewLines.desc": "Número máximo de quebras de linha a serem preservadas em um bloco. Use 'null' para ilimitado.", + "html.format.indentHandlebars.desc": "Formatar e indentar {{#foo}} e {{/ foo}}.", + "html.format.endWithNewline.desc": "Finalizar com uma nova linha.", + "html.format.extraLiners.desc": "Lista de rótulos, separados por vírgulas, que deveriam ter uma quebra de linha extra antes deles. 'null' admite o padrão \"head, body, /html\".", + "html.format.wrapAttributes.desc": "Agrupar atributos.", + "html.format.wrapAttributes.auto": "Agrupar atributos somente quando o tamanho da linha é excedido.", + "html.format.wrapAttributes.force": "Agrupar cada atributo exceto o primeiro.", + "html.format.wrapAttributes.forcealign": "Agrupar cada atributo, exceto o primeiro e manter alinhado.", + "html.format.wrapAttributes.forcemultiline": "Agrupar cada atributo.", + "html.suggest.angular1.desc": "Configura se o suporte da linguagem HTML interna sugere rótulos e propriedades do Angular V1.", + "html.suggest.ionic.desc": "Configura se o suporte da linguagem HTML interna sugere rótulos, propriedades e valores Ionic.", + "html.suggest.html5.desc": "Configura se o suporte da linguagem HTML interna sugere rótulos, propriedades e valores HTML5.", + "html.trace.server.desc": "Rastrear a comunicação entre o VS Code e o servidor de linguagem HTML.", + "html.validate.scripts": "Configura se o suporte da linguagem HTML interna valida scripts embutidos.", + "html.validate.styles": "Configura se o suporte da linguagem HTML interna valida estilos embutidos.", + "html.autoClosingTags": "Ativar/desativar o fechamento automático de tags HTML." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/jake/out/main.i18n.json b/i18n/ptb/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..22fafc0b7c --- /dev/null +++ b/i18n/ptb/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Auto detecção de Jake falhou com erro: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/jake/package.i18n.json b/i18n/ptb/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..8279e91b77 --- /dev/null +++ b/i18n/ptb/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "Controla se a detecção automática de tarefas Jake está ativada ou desativada. Por padrão, é ativado." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/ptb/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..ccbe666b93 --- /dev/null +++ b/i18n/ptb/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "Bower.json padrão", + "json.bower.error.repoaccess": "Falha na solicitação ao repositório bower: {0}", + "json.bower.latest.version": "último" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/ptb/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..dba1500f88 --- /dev/null +++ b/i18n/ptb/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "Package.json padrão", + "json.npm.error.repoaccess": "Falha na solicitação ao repositório NPM: {0}", + "json.npm.latestversion": "A versão do pacote mais recente no momento", + "json.npm.majorversion": "Combina com a versão principal mais recente (1.x.x)", + "json.npm.minorversion": "Combina a versão secundária mais recente (1.2.x)", + "json.npm.version.hover": "Última versão: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/json/client/out/jsonMain.i18n.json b/i18n/ptb/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..914681d31a --- /dev/null +++ b/i18n/ptb/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "Servidor de linguagem JSON" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/json/package.i18n.json b/i18n/ptb/extensions/json/package.i18n.json new file mode 100644 index 0000000000..673158e982 --- /dev/null +++ b/i18n/ptb/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "Esquemas associadas a arquivos de JSON no projeto atual", + "json.schemas.url.desc": "Um URL para um esquema ou um caminho relativo a um esquema no diretório atual", + "json.schemas.fileMatch.desc": "Uma matriz de padrões de arquivos para correspondência ao resolver arquivos JSON para esquemas.", + "json.schemas.fileMatch.item.desc": "Um padrão de arquivos que pode conter '*' para fazer a correspondência ao resolver arquivos JSON para esquemas.", + "json.schemas.schema.desc": "A definição de esquema para o URL dado. O esquema precisa ser fornecido apenas para evitar acessos ao URL do esquema.", + "json.format.enable.desc": "Habilitar/desabilitar o formatador JSON padrão (requer reinicialização)", + "json.tracing.desc": "Loga a comunicação entre o VS Code e o servidor de linguagem JSON.", + "json.colorDecorators.enable.desc": "Habilita ou desabilita os decoradores de cor", + "json.colorDecorators.enable.deprecationMessage": "A configuração 'json.colorDecorators.enable' foi descontinuada em favor de 'editor.colorDecorators'." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/markdown/out/extension.i18n.json b/i18n/ptb/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..9ec366e863 --- /dev/null +++ b/i18n/ptb/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "Não foi possível carregar o 'markdown.styles': {0}" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/ptb/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..d96218cde1 --- /dev/null +++ b/i18n/ptb/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "Algum conteúdo foi desativado neste documento", + "preview.securityMessage.title": "Conteúdo potencialmente inseguro foi desativado na visualização de remarcação. Altere a configuração de segurança de visualização do Markdown para permitir conteúdo inseguro ou habilitar scripts", + "preview.securityMessage.label": "Conteúdo do aviso de segurança desativado" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/markdown/out/security.i18n.json b/i18n/ptb/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..1dd71ae31d --- /dev/null +++ b/i18n/ptb/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "Estrita", + "strict.description": "Somente carregar conteúdo seguro", + "insecureContent.title": "Permitir conteúdo inseguro", + "insecureContent.description": "Habilitar o carregamento de conteúdo sobre http", + "disable.title": "Desabilitar", + "disable.description": "Permitir a execução de conteúdo e scripts. Não recomendado", + "moreInfo.title": "Mais informações", + "preview.showPreviewSecuritySelector.title": "Selecione as configurações de segurança para visualizações de Markdown neste espaço de trabalho" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/markdown/package.i18n.json b/i18n/ptb/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..a6be8c5994 --- /dev/null +++ b/i18n/ptb/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "Configura como quebras de linha são processadas na visualização de markdown. Configurando como 'true' cria um
para cada nova linha.", + "markdown.preview.linkify": "Habilitar ou desabilitar a conversão de texto URL para links na visualização markdown.", + "markdown.preview.doubleClickToSwitchToEditor.desc": "Duplo clique na pré-visualização markdown para alternar para o editor.", + "markdown.preview.fontFamily.desc": "Controla a família de fonte usada na pré-visualização de markdown.", + "markdown.preview.fontSize.desc": "Controla o tamanho da fonte em pixels usado na pré-visualização de markdown.", + "markdown.preview.lineHeight.desc": "Controla a altura de linha usada na pré-visualização de markdown. Este número é relativo ao tamanho de fonte.", + "markdown.preview.markEditorSelection.desc": "Marca a seleção atual do editor na pré-visualização de markdown.", + "markdown.preview.scrollEditorWithPreview.desc": "Quando a pré-visualização de markdown é rolada, atualiza a exibição do editor.", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "Rola a pré-visualização do markdown para revelar a linha atualmente selecionada do editor.", + "markdown.preview.title": "Abrir a visualização", + "markdown.previewFrontMatter.dec": "Configura como o frontispicio YAML frente questão devem ser processado na pré-visualização de markdown. 'hide' remove o frontispicio. Caso contrário, o frontispicio é tratado como conteúdo de markdown.", + "markdown.previewSide.title": "Abre pré-visualização ao lado", + "markdown.showSource.title": "Exibir Código-Fonte", + "markdown.styles.dec": "Uma lista de URLs ou caminhos locais para folhas de estilo CSS para usar na pré-visualização do markdown. Caminhos relativos são interpretados em relação à pasta aberta no explorer. Se não houver nenhuma pasta aberta, eles são interpretados em relação ao local do arquivo markdown. Todos os ' \\' precisam ser escritos como ' \\ \\ '.", + "markdown.showPreviewSecuritySelector.title": "Alterar as configurações de segurança de visualização", + "markdown.trace.desc": "Habilitar log de depuração para a extensão do markdown.", + "markdown.refreshPreview.title": "Atualizar a visualização" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/ptb/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..739ff97a30 --- /dev/null +++ b/i18n/ptb/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "Aceitar a mudança atual", + "acceptIncomingChange": "Aceitar a mudança de entrada", + "acceptBothChanges": "Aceitar as duas alterações", + "compareChanges": "Comparar as mudanças" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/ptb/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..3e670488aa --- /dev/null +++ b/i18n/ptb/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "Cursor do editor não está dentro de um conflito de mesclagem", + "compareChangesTitle": "{0}: Alterações Atuais ⟷ Alterações de Entrada", + "cursorOnCommonAncestorsRange": "Cursor do editor está dentro do bloco comum de ancestrais, favor mover para o bloco \"atual\" ou \"entrada\"", + "cursorOnSplitterRange": "Cursor do editor está dentro do separador de conflitos de mesclagem, por favor mova-o para o bloco \"atual\" ou \"entrada\"", + "noConflicts": "Nenhum conflito de mesclagem encontrado neste arquivo", + "noOtherConflictsInThisFile": "Não há outros conflitos de mesclagem dentro desse arquivo" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/ptb/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..dd8a752971 --- /dev/null +++ b/i18n/ptb/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(Mudança Atual)", + "incomingChange": "(Próxima Mudança)" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/merge-conflict/package.i18n.json b/i18n/ptb/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..b9db4e5f5e --- /dev/null +++ b/i18n/ptb/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "Conflito de Mesclagem", + "command.accept.all-incoming": "Aceitar todas entradas", + "command.accept.all-both": "Aceitar todas as duas", + "command.accept.current": "Aceitar a corrente", + "command.accept.incoming": "Aceitar entrada", + "command.accept.selection": "Aceitar a seleção", + "command.accept.both": "Aceitar Ambos", + "command.next": "Próximo conflito", + "command.previous": "Conflito anterior", + "command.compare": "Comparar o conflito atual", + "config.title": "Mesclar conflitos", + "config.codeLensEnabled": "Habilitar/Desabilitar o conflito de mesclagem no bloco CodeLens dentro do editor", + "config.decoratorsEnabled": "Habilitar/Desabilitar decoradores de mesclagem de conflitos dentro do editor" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/npm/package.i18n.json b/i18n/ptb/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..1af384ddc1 --- /dev/null +++ b/i18n/ptb/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "Controla se a deteção automática de scripts npm está ligado ou desligado. O padrão é ligado.", + "config.npm.runSilent": "Executar comandos npm com a opção '--silent'" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/php/out/features/validationProvider.i18n.json b/i18n/ptb/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..a8189cc74d --- /dev/null +++ b/i18n/ptb/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "Você permite {0} (definido como uma configuração do espaço de trabalho) a ser executado para lint de arquivos PHP?", + "php.yes": "Permitir", + "php.no": "Não permitir", + "wrongExecutable": "Não é possível validar {0} pois não é um executável php válido. Use a configuração 'php.validate.executablePath' para configurar o executável do PHP.", + "noExecutable": "Não é possível validar porque nenhum executável PHP está definido. Use a configuração 'php.validate.executablePath' para configurar o executável do PHP.", + "unknownReason": "Falha ao executar o php usando o caminho: {0}. O motivo é desconhecido." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/php/package.i18n.json b/i18n/ptb/extensions/php/package.i18n.json new file mode 100644 index 0000000000..f6f2315bc2 --- /dev/null +++ b/i18n/ptb/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "Configura se as sugestões intrínsecas da linguagem PHP estão habilitadas. O suporte sugere globais e variáveis do PHP.", + "configuration.validate.enable": "Habilita/desabilita a validação interna do PHP.", + "configuration.validate.executablePath": "Aponta para o executável do PHP.", + "configuration.validate.run": "Se o linter é executado ao salvar ou ao digitar.", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "Desabilita a validação de executável do PHP (definida como configuração do espaço de trabalho)" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/ptb/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..ebd041f156 --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "Usando o TypeScript ({1}) para recursos do editor. TypeScript ({0}) está instalado globalmente em sua máquina. Erros no VS Code podem diferir dos erros TSC", + "moreInformation": "Mais informações", + "doNotCheckAgain": "Não verificar novamente", + "close": "Fechar", + "updateTscCheck": "Atualizada configuração de usuário 'typescript.check.tscVersion' para false " +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/ptb/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..3251197b16 --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "Adquirindo digitações...", + "acquiringTypingsDetail": "Adquirindo definições de digitações para o Intellisense." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/ptb/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..e9312ee639 --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "Habilita verificação semântica em um arquivo JavaScript. Deve estar no topo de um arquivo.", + "ts-nocheck": "Desabilita verificação semântica em um arquivo JavaScript. Deve estar no topo de um arquivo.", + "ts-ignore": "Suprime erros de @ts-check na próxima linha de um arquivo." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/ptb/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..7bdd0ba9e1 --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 implementação", + "manyImplementationLabel": "{0} implementações", + "implementationsErrorLabel": "Não foi possível determinar implementações" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/ptb/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..073d380962 --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "Comentário JSDoc" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/ptb/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..fc4e893194 --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 referência", + "manyReferenceLabel": "{0} referências", + "referenceErrorLabel": "Não foi possível determinar as referências" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/ptb/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..66440cfef7 --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "monitorar - {0}", + "buildTscLabel": "compilar - {0}" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/typescriptMain.i18n.json b/i18n/ptb/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..6ff094106b --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "Favor abrir uma pasta no VS Code para usar um projeto TypeScript ou JavaScript", + "typescript.projectConfigUnsupportedFile": "Não foi possível determinar o projeto TypeScript ou JavaScript. Tipo de arquivo não suportado", + "typescript.projectConfigCouldNotGetInfo": "Não foi possível determinar o projeto TypeScript ou JavaScript", + "typescript.noTypeScriptProjectConfig": "Arquivo não é parte de um projeto TypeScript", + "typescript.noJavaScriptProjectConfig": "Arquivo não é parte de um projeto JavaScript", + "typescript.configureTsconfigQuickPick": "Configurar tsconfig.json", + "typescript.configureJsconfigQuickPick": "Configurar jsconfig.json", + "typescript.projectConfigLearnMore": "Saber Mais" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/ptb/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..719fb2075d --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "O caminho {0} não aponta para uma instalação de tsserver válida. Voltando para a versão do TypeScript empacotada.", + "serverCouldNotBeStarted": "Servidor de linguagem TypeScript não pôde ser iniciado. Mensagem de erro é: {0}", + "typescript.openTsServerLog.notSupported": "Logging de TS Server requer TS TS 2.2.2+", + "typescript.openTsServerLog.loggingNotEnabled": "Logging de TS Server está desligado. Por favor configure 'typescript.tsserver.log' e reinicie o TS Server para habilitar o log", + "typescript.openTsServerLog.enableAndReloadOption": "Habilitar logging e reniciar TS server", + "typescript.openTsServerLog.noLogFile": "O TS Server não iniciou o logging.", + "openTsServerLog.openFileFailedFailed": "Não foi possível abrir o arquivo de log do TS Server", + "serverDiedAfterStart": "O serviço de linguagem TypeScript morreu 5 vezes depois que começou. O serviço não será reiniciado.", + "serverDiedReportIssue": "Reportar Problema", + "serverDied": "O serviço TypeScript morreu inesperadamente 5 vezes nos últimos 5 minutos." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/utils/api.i18n.json b/i18n/ptb/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..0a4241e414 --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "versão inválida" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/utils/logger.i18n.json b/i18n/ptb/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/ptb/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..ec09ca3514 --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "Para habilitar os recursos de linguagem JavaScript/TypeScipt em todo o projeto, excluir pastas com muitos arquivos, como: {0}", + "hintExclude.generic": "Para habilitar os recursos de linguagem JavaScript/TypeScipt em todo o projeto, excluir pastas grandes com arquivos em que você não trabalha.", + "large.label": "Configurar exclusões", + "hintExclude.tooltip": "Para habilitar os recursos de linguagem JavaScript/TypeScipt em todo o projeto, excluir pastas grandes com arquivos em que você não trabalha." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/ptb/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..270f753a39 --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "Buscando dados para melhor IntelliSense do TypeScript", + "typesInstallerInitializationFailed.title": "Não foi possível instalar arquivos de tipagens para recursos da linguagem JavaScript. Por favor, certifique-se de que a NPM está instalado ou configure 'typescript.npm' em suas configurações de usuário", + "typesInstallerInitializationFailed.moreInformation": "Mais informações", + "typesInstallerInitializationFailed.doNotCheckAgain": "Não verificar novamente", + "typesInstallerInitializationFailed.close": "Fechar" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/ptb/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..f47c82fd6c --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "Use a versão do VS Code", + "useWorkspaceVersionOption": "Use a versão de área de trabalho", + "learnMore": "Saiba Mais", + "selectTsVersion": "Selecione a versão do TypeScript usada para os recursos de linguagem JavaScript e TypeScript" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/ptb/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..71ade1650b --- /dev/null +++ b/i18n/ptb/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "Não foi possível carregar a versão do TypeScript neste caminho", + "noBundledServerFound": "Tsserver do VS Code foi eliminado por outro aplicativo como uma ferramenta de detecção de vírus com um comportamento inadequado. Por favor reinstale o VS Code." +} \ No newline at end of file diff --git a/i18n/ptb/extensions/typescript/package.i18n.json b/i18n/ptb/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..babf16cb1b --- /dev/null +++ b/i18n/ptb/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "Recarregar Projeto", + "javascript.reloadProjects.title": "Recarregar Projeto", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "Funções completas com a assinatura do parâmetro.", + "typescript.tsdk.desc": "Especifica o caminho da pasta que contém os arquivos tsserver e lib*.d.ts para usar.", + "typescript.disableAutomaticTypeAcquisition": "Desabilita a aquisição automática de tipo. Requer TypeScript > = 2.0.6.", + "typescript.tsserver.log": "Habilita o log do servidor TS para um arquivo. Este log pode ser usado para diagnosticar problemas do servidor de TS. O log pode conter caminhos de arquivo, código-fonte e outras informações potencialmente confidenciais do seu projeto.", + "typescript.tsserver.trace": "Habilita o rastreamento de mensagens enviadas para o servidor de TS. Este rastreamento pode ser usado para diagnosticar problemas do servidor de TS. O rastreamento pode conter caminhos de arquivo, código-fonte e outras informações potencialmente confidenciais do seu projeto.", + "typescript.validate.enable": "Habilita/Desabilita a validação TypeScript.", + "typescript.format.enable": "Habilita/Desabilita o formatador padrão TypeScript.", + "javascript.format.enable": "Habilita/Desabilita o formatador padrão JavaScript.", + "format.insertSpaceAfterCommaDelimiter": "Define o tratamento de espaços após um delimitador vírgula.", + "format.insertSpaceAfterConstructor": "Define a manipulação de espaços após a palavra-chave do construtor. Requer TypeScript > = 2.3.0.", + "format.insertSpaceAfterSemicolonInForStatements": "Define o tratamento de espaços após um ponto e vírgula para um comando.", + "format.insertSpaceBeforeAndAfterBinaryOperators": "Define o tratamento de espaços após um operador binário.", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "Define o tratamento de espaços após palavras-chave em um comando de controle de fluxo.", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "Define o tratamento de espaços após uma palavra-chave de função para funções anônimas.", + "format.insertSpaceBeforeFunctionParenthesis": "Define a manipulação de espaços antes de parênteses do argumento de função. Requer TypeScript > = 2.1.5.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "Define a manipulação de espaços após abrir e antes de fechar parênteses não vazios.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "Define a manipulação de espaços após abrir e antes de fechar colchetes não vazios.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Define a manipulação de espaços após abrir e antes de fechar chaves não vazias. Requer TypeScript >= 2.3.0.", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "Define a manipulação de espaços após abrir e antes de fechar chaves de cadeias de caracteres de modelos. Requer TypeScript >= 2.0.6.", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Define a manipulação de espaços após abrir e antes de fechar chaves de expressões JSX. Requer TypeScript >= 2.0.6.", + "format.insertSpaceAfterTypeAssertion": "Define a manipulação de espaços após asserções de tipo em TypeScript. Requer TypeScript >= 2,4.", + "format.placeOpenBraceOnNewLineForFunctions": "Define-se uma chave de abertura é colocada em uma nova linha para funções ou não.", + "format.placeOpenBraceOnNewLineForControlBlocks": "Define-se uma chave de abertura é colocada em uma nova linha para blocos de controle ou não.", + "javascript.validate.enable": "Habilitar/Desabilitar validação JavaScript.", + "typescript.goToProjectConfig.title": "Ir para a Configuração do Projeto", + "javascript.goToProjectConfig.title": "Ir para a Configuração do Projeto", + "javascript.referencesCodeLens.enabled": "Habilitar/desabilitar referências CodeLens em arquivos JavaScript.", + "typescript.referencesCodeLens.enabled": "Habilitar/desabilitar referências CodeLens em arquivos TypeScript. Requer TypeScript > = 2.0.6.", + "typescript.implementationsCodeLens.enabled": "Habilitar/desabilitar implementações CodeLens. Requer TypeScript > = 2.0.6.", + "typescript.openTsServerLog.title": "Abrir arquivo de log do servidor TS", + "typescript.restartTsServer": "Reiniciar o servidor TS", + "typescript.selectTypeScriptVersion.title": "Selecionar a versão do JavaScript", + "jsDocCompletion.enabled": "Habilitar/Desabilitar comentários JSDoc automáticos.", + "javascript.implicitProjectConfig.checkJs": "Habilitar/desabilitar verificação semântica de arquivos JavaScript. Os arquivos existentes jsconfig.json ou tsconfig.json substituem essa configuração. Requer TypeScript > = 2.3.1.", + "typescript.npm": "Especifica o caminho para o executável do NPM usado para Aquisição de Tipo Automático. Requer TypeScript > = 2.3.4.", + "typescript.check.npmIsInstalled": "Verificar se o NPM está instalado para aquisição automática de tipo.", + "javascript.nameSuggestions": "Habilitar/desabilitar incluindo nomes exclusivos do arquivo nas listas de sugestão de JavaScript.", + "typescript.tsc.autoDetect": "Controla se a auto-detecção de tarefas tsc estão ligadas ou desligadas.", + "typescript.problemMatchers.tsc.label": "Problemas TypeScript", + "typescript.problemMatchers.tscWatch.label": "Problemas TypeScript (modo observação)" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/ptb/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/ptb/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/ptb/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..a411c0c547 --- /dev/null +++ b/i18n/ptb/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (ocorreu novamente)" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/ptb/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..7bea9e8645 --- /dev/null +++ b/i18n/ptb/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "entrada" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/ptb/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..bccfa4c3d7 --- /dev/null +++ b/i18n/ptb/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "Diferenciar Maiúsculas de Minúsculas", + "wordsDescription": "Coincidir Palavra Inteira", + "regexDescription": "Usar Expressão Regular" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/ptb/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..2e05fc57c0 --- /dev/null +++ b/i18n/ptb/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Erro: {0}", + "alertWarningMessage": "Aviso: {0}", + "alertInfoMessage": "Informações: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/ptb/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..d1de93ef6c --- /dev/null +++ b/i18n/ptb/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "A imagem é muito grande para ser exibida no editor.", + "resourceOpenExternalButton": "Abrir imagem usando um programa externo?", + "nativeBinaryError": "O arquivo não pode ser exibido no editor porque é binário, muito grande ou usa uma codificação de texto sem suporte.", + "sizeB": "{0}B", + "sizeKB": "{0}KB", + "sizeMB": "{0}MB", + "sizeGB": "{0}GB", + "sizeTB": "{0}TB" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/ptb/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..3d937d3fad --- /dev/null +++ b/i18n/ptb/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "Mais" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/common/errorMessage.i18n.json b/i18n/ptb/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..a98438f9bb --- /dev/null +++ b/i18n/ptb/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}. Código de erro: {1}", + "error.permission.verbose": "Permissão Negada (HTTP {0})", + "error.permission": "Permissão Negada", + "error.http.verbose": "{0} (HTTP {1}: {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "Erro de Conexão Desconhecido ({0})", + "error.connection.unknown": "Ocorreu um erro de conexão desconhecido. Você não está mais conectado à Internet ou o servidor que você está conectado está offline.", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "Ocorreu um erro desconhecido. Consulte o log para obter mais detalhes.", + "nodeExceptionMessage": "Ocorreu um erro de sistema ({0})", + "error.moreErrors": "{0} ({1} erros no total)" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/ptb/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..dbf1366652 --- /dev/null +++ b/i18n/ptb/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Símbolo inválido", + "error.invalidNumberFormat": "Formato de número inválido", + "error.propertyNameExpected": "Nome de propriedade esperado", + "error.valueExpected": "Valor esperado", + "error.colonExpected": "Dois-pontos esperado", + "error.commaExpected": "Vírgula esperada", + "error.closeBraceExpected": "Chave de fechamento esperada", + "error.closeBracketExpected": "Colchete de fechamento esperado", + "error.endOfFileExpected": "Fim do arquivo esperado" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/common/keybindingLabels.i18n.json b/i18n/ptb/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..c7ec269a77 --- /dev/null +++ b/i18n/ptb/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Shift", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Shift", + "altKey.long": "Alt", + "cmdKey.long": "Comando", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/common/processes.i18n.json b/i18n/ptb/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..8a97958952 --- /dev/null +++ b/i18n/ptb/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "Erro: informações de executável devem definir um comando do tipo string", + "ExecutableParser.isShellCommand": "Aviso: IsShellCommand deve ser to tipo booleano. Ignorando valor {0}", + "ExecutableParser.args": "Aviso: args deve ser do tipo string[]. Ignorando valor {0}.", + "ExecutableParser.invalidCWD": "Aviso: options.cwd deve ser do tipo string. Ignorando valor {0}." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/common/severity.i18n.json b/i18n/ptb/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..dbac6a613a --- /dev/null +++ b/i18n/ptb/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "Erro", + "sev.warning": "Aviso", + "sev.info": "Informações" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/node/processes.i18n.json b/i18n/ptb/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..a0eab376bb --- /dev/null +++ b/i18n/ptb/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "Não é possível executar um comando shell em uma unidade UNC." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/node/zip.i18n.json b/i18n/ptb/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..4858af0965 --- /dev/null +++ b/i18n/ptb/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "{0} não encontrado dentro do zip." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/ptb/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..ea391b97e4 --- /dev/null +++ b/i18n/ptb/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0}, seletor", + "quickOpenAriaLabel": "seletor" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/ptb/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..93a4521d16 --- /dev/null +++ b/i18n/ptb/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "Seletor rápido. Digite para filtrar resultados.", + "treeAriaLabel": "Seletor rápido" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/ptb/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..7306cd0910 --- /dev/null +++ b/i18n/ptb/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Recolher" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/code/electron-main/auth.i18n.json b/i18n/ptb/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..20acae2606 --- /dev/null +++ b/i18n/ptb/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "Autenticação de proxy necessária", + "proxyauth": "O proxy {0} requer autenticação." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/code/electron-main/menus.i18n.json b/i18n/ptb/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..77b49928ac --- /dev/null +++ b/i18n/ptb/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "&&Arquivo", + "mEdit": "&&Editar", + "mSelection": "&&Seleção", + "mView": "&&Visualizar", + "mGoto": "&&Ir", + "mDebug": "&&Depurar", + "mWindow": "Janela", + "mHelp": "&&Ajuda", + "mTask": "&&Tarefas", + "miNewWindow": "Nova &&Janela", + "mAbout": "Sobre {0}", + "mServices": "Serviços", + "mHide": "Ocultar {0}", + "mHideOthers": "Ocultar Outros", + "mShowAll": "Mostrar Tudo", + "miQuit": "Sair de {0}", + "miNewFile": "&&Novo Arquivo", + "miOpen": "&&Abrir", + "miOpenWorkspace": "&&Abrir espaço de trabalho...", + "miOpenFolder": "Abrir &&Pasta", + "miOpenFile": "&&Abrir Arquivo", + "miOpenRecent": "Abrir &&Recente", + "miSaveWorkspaceAs": "&& Salvar o espaço de trabalho como...", + "miAddFolderToWorkspace": "&&Adicionar pasta para área de trabalho...", + "miSave": "&&Salvar", + "miSaveAs": "Salvar &&Como...", + "miSaveAll": "Salvar &&Tudo", + "miAutoSave": "Salvar Automaticamente", + "miRevert": "Re&&verter Arquivo", + "miCloseWindow": "Fe&&char Janela", + "miCloseWorkspace": "Fechar &&Espaço de Trabalho", + "miCloseFolder": "Fechar &&Pasta", + "miCloseEditor": "Fechar &&Editor", + "miExit": "Sai&&r", + "miOpenSettings": "&&Configurações", + "miOpenKeymap": "Atalhos de &&Teclado", + "miOpenKeymapExtensions": "Extensões de &&Mapeamento de Teclado", + "miOpenSnippets": "Trechos de Có&&digo do Usuário", + "miSelectColorTheme": "Cor do T&&ema", + "miSelectIconTheme": "&&Ícone de Arquivo do Tema", + "miPreferences": "&&Preferências", + "miReopenClosedEditor": "&&Reabrir Editor Fechado", + "miMore": "&&Mais...", + "miClearRecentOpen": "&&Limpar Abertos Recentemente", + "miUndo": "&&Desfazer", + "miRedo": "&&Refazer", + "miCut": "Cor&&tar", + "miCopy": "&&Copiar", + "miPaste": "Co&&lar", + "miFind": "&&Localizar", + "miReplace": "&&Substituir", + "miFindInFiles": "Localizar &&nos Arquivos", + "miReplaceInFiles": "Substituir &&nos Arquivos", + "miEmmetExpandAbbreviation": "Emmet: E&&xpandir Abreviação", + "miShowEmmetCommands": "E&&mmet...", + "miToggleLineComment": "&&Alternar Comentário de Linha", + "miToggleBlockComment": "Alternar Comentário de &&Bloco", + "miMultiCursorAlt": "Troque para Alt+clique para Multi-Cursor", + "miMultiCursorCmd": "Troque para Cmd+clique para Multi-Cursor", + "miMultiCursorCtrl": "Troque para Ctrl+clique para Multi-Cursor", + "miInsertCursorAbove": "&&Inserir cursor acima", + "miInsertCursorBelow": "Inserir cursor a&&baixo", + "miInsertCursorAtEndOfEachLineSelected": "Adicionar C&&ursores ao Final das Linhas", + "miAddSelectionToNextFindMatch": "Adicionar &&próxima ocorrência", + "miAddSelectionToPreviousFindMatch": "Adicionar ocorrência a&&nterior ", + "miSelectHighlights": "Selecionar todas as &&ocorrências", + "miCopyLinesUp": "&&Copiar linha acima", + "miCopyLinesDown": "C&&opiar linha abaixo", + "miMoveLinesUp": "Mo&&ver linha para cima", + "miMoveLinesDown": "Mover &&linha para baixo", + "miSelectAll": "&&Selecionar Tudo", + "miSmartSelectGrow": "&&Expandir seleção", + "miSmartSelectShrink": "&&Reduzir seleção", + "miViewExplorer": "&&Explorador", + "miViewSearch": "&&Pesquisar", + "miViewSCM": "S&&CM", + "miViewDebug": "&&Depurar", + "miViewExtensions": "E&&xtensões", + "miToggleOutput": "&&Saída", + "miToggleDebugConsole": "Con&&sole de Depuração", + "miToggleIntegratedTerminal": "Terminal &&Integrado", + "miMarker": "&&Problemas", + "miAdditionalViews": "&&Visualizações Adicionais", + "miCommandPalette": "&&Paleta de comando", + "miToggleFullScreen": "Alternar &&Tela Inteira", + "miToggleZenMode": "Alternar modo Zen", + "miToggleMenuBar": "Alternar &&Barra de Menus", + "miSplitEditor": "Dividir &&editor", + "miToggleEditorLayout": "Alternar &&Layout do Grupo de Editor", + "miToggleSidebar": "&&Alternar Barra Lateral", + "miMoveSidebarRight": "&&Mover a barra lateral para a direita", + "miMoveSidebarLeft": "&&Mover a barra lateral para a esquerda", + "miTogglePanel": "Alternar &&Painel", + "miHideStatusbar": "&&Ocultar Barra de Status", + "miShowStatusbar": "&&Mostrar Barra de Status", + "miHideActivityBar": "Ocultar Barra de &&Atividades", + "miShowActivityBar": "Mostrar Barra de &&Atividades", + "miToggleWordWrap": "Alternar &&Quebra de Linha", + "miToggleMinimap": "Alternar &&Painel", + "miToggleRenderWhitespace": "Alternar &&Renderização de Espaços em Branco", + "miToggleRenderControlCharacters": "Alternar &&Caracteres de Controle", + "miZoomIn": "&&Ampliar", + "miZoomOut": "Red&&uzir", + "miZoomReset": "&&Reinicializar Zoom", + "miBack": "&&Voltar", + "miForward": "&&Avançar", + "miNextEditor": "&&Próximo Editor", + "miPreviousEditor": "&&Editor Anterior", + "miNextEditorInGroup": "&&Próximo Editor Usado no Grupo", + "miPreviousEditorInGroup": "&&Editor Anterior Usado no Grupo", + "miSwitchEditor": "Trocar &&Editor", + "miFocusFirstGroup": "&&Primeiro Grupo", + "miFocusSecondGroup": "&&Segundo Grupo", + "miFocusThirdGroup": "&&Terceiro Grupo", + "miNextGroup": "&&Próximo Grupo", + "miPreviousGroup": "&&Grupo Anterior", + "miSwitchGroup": "Trocar &&Grupo", + "miGotoFile": "Ir para &&Arquivo...", + "miGotoSymbolInFile": "Ir para o &&Símbolo no Arquivo...", + "miGotoSymbolInWorkspace": "Ir para o Símbolo em &&Área de Trabalho", + "miGotoDefinition": "Ir para &&Definição", + "miGotoTypeDefinition": "Ir para a &&definição de tipo", + "miGotoImplementation": "Ir para a &&implementação", + "miGotoLine": "Ir para &&Linha...", + "miStartDebugging": "Iniciar Depuração", + "miStartWithoutDebugging": "Iniciar &&Sem Depuração", + "miStopDebugging": "&&Parar Depuração", + "miRestart Debugging": "&&Reiniciar Depuração", + "miOpenConfigurations": "Abrir &&Configurações", + "miAddConfiguration": "Adicionar Configuração...", + "miStepOver": "Pular &&Sobre", + "miStepInto": "Pular &&Dentro", + "miStepOut": "Pular &&Fora", + "miContinue": "&&Continuar", + "miToggleBreakpoint": "Alternar &&Ponto de Parada", + "miConditionalBreakpoint": "Ponto de Parada &&Condicional...", + "miColumnBreakpoint": "Ponto de Parada de C&&oluna", + "miFunctionBreakpoint": "Ponto de Parada de &&Função...", + "miNewBreakpoint": "&&Novo Ponto de Parada", + "miEnableAllBreakpoints": "Habilitar Todos os Pontos de Parada", + "miDisableAllBreakpoints": "Desabilitar T&&odos os Pontos de Parada", + "miRemoveAllBreakpoints": "Remover &&Todos os Pontos de Parada", + "miInstallAdditionalDebuggers": "&&Instalar Depuradores Adicionais...", + "mMinimize": "Minimizar", + "mZoom": "Ampliar", + "mBringToFront": "Trazer Tudo para a Frente", + "miSwitchWindow": "Alternar &&Janela...", + "miToggleDevTools": "&&Alternar Ferramentas do Desenvolvedor", + "miAccessibilityOptions": "&&Opções de Acessibilidade", + "miReportIssues": "Relatar &&Problemas", + "miWelcome": "&&Bem-vindo", + "miInteractivePlayground": "Playground &&Interativo", + "miDocumentation": "&&Documentação", + "miReleaseNotes": "&&Notas de Versão", + "miKeyboardShortcuts": "Referência de &&Atalhos de Teclado", + "miIntroductoryVideos": "&&Vídeos Introdutórios", + "miTipsAndTricks": "&&Dicas e truques", + "miTwitter": "&&Junte-se a nós no Twitter", + "miUserVoice": "&&Pesquisar Solicitações de Recursos", + "miLicense": "&&Exibir Licença", + "miPrivacyStatement": "&&Política de Privacidade", + "miAbout": "&&Sobre", + "miRunTask": "&&Executar Tarefa...", + "miBuildTask": "Executar Tarefa de &&compilação", + "miRunningTask": "Mostrar &&Tarefas Em Execução...", + "miRestartTask": "R&&einiciar Tarefa em Execução", + "miTerminateTask": "&&Finalizar Tarefa", + "miConfigureTask": "&&Configurar tarefas", + "miConfigureBuildTask": "Configurar Tarefa de Compilação &&Padrão", + "accessibilityOptionsWindowTitle": "Opções de Acessibilidade", + "miRestartToUpdate": "Reinicie para Atualizar...", + "miCheckingForUpdates": "Verificando Atualizações...", + "miDownloadUpdate": "Baixar Atualização Disponível", + "miDownloadingUpdate": "Baixando Atualização...", + "miInstallingUpdate": "Instalando Atualização...", + "miCheckForUpdates": "Verificar Atualizações...", + "aboutDetail": "\nVersão {0}\nConfirmação {1}\nData {2}\nShell {3}\nRenderizador {4}\nNó {5}\nArquitetura {6}", + "okButton": "OK" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/code/electron-main/window.i18n.json b/i18n/ptb/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..d0590af4cf --- /dev/null +++ b/i18n/ptb/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "Você ainda pode acessar a barra de menu pressionando a tecla * * Alt * *." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/code/electron-main/windows.i18n.json b/i18n/ptb/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..9b3bcc5204 --- /dev/null +++ b/i18n/ptb/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "OK", + "pathNotExistTitle": "O caminho não existe", + "pathNotExistDetail": "O caminho '{0}' não parece mais existir no disco.", + "openWorkspace": "&&Abrir", + "openWorkspaceTitle": "Abrir o Espaço de Trabalho", + "save": "&&Salvar", + "doNotSave": "&&Não Salvar", + "cancel": "Cancelar", + "saveWorkspaceMessage": "Você quer salvar a sua configuração de área de trabalho como um arquivo?", + "saveWorkspaceDetail": "Salve seu espaço de trabalho se pretende abri-lo novamente.", + "saveWorkspace": "Salvar o espaço de trabalho", + "reopen": "Reabrir", + "wait": "Continuar Esperando", + "close": "Fechar", + "appStalled": "A janela não está mais respondendo", + "appStalledDetail": "Você pode reabrir, fechar a janela ou continuar esperando.", + "appCrashed": "A janela foi fechada inesperadamente", + "appCrashedDetail": "Pedimos desculpas pelo inconveniente! Você pode reabrir a janela para continuar de onde parou.", + "open": "Abrir", + "openFolder": "Abrir Pasta", + "openFile": "Abrir Arquivo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/code/node/cliProcessMain.i18n.json b/i18n/ptb/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..dff9956279 --- /dev/null +++ b/i18n/ptb/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Extensão '{0}' não encontrada.", + "notInstalled": "Extensão '{0}' não está instalada.", + "useId": "Certifique-se de usar a ID de extensão completa, incluindo o editor, por exemplo: {0}", + "successVsixInstall": "Extensão '{0}' foi instalada com sucesso!", + "alreadyInstalled": "Extensão '{0}' já está instalada.", + "foundExtension": "Encontrado '{0}' na loja VS Code.", + "installing": "Instalando...", + "successInstall": "Extensão '{0}' v {1} foi instalada com sucesso!", + "uninstalling": "Desinstalando {0}...", + "successUninstall": "Extensão '{0}' foi desinstalada com sucesso!" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/ptb/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..6066812e82 --- /dev/null +++ b/i18n/ptb/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "Não é possível comparar os arquivos pois um deles é muito grande." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/ptb/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..b2baeae97a --- /dev/null +++ b/i18n/ptb/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Fechar", + "header": "Diferença {0} de {1}: original {2}, {3} linhas, modificado {4}, {5} linhas", + "blankLine": "branco", + "equalLine": "original {0}, modificados {1}: {2}", + "insertLine": "+ modificado {0}: {1}", + "deleteLine": "-original {0}: {1}", + "editor.action.diffReview.next": "Ir para a próxima diferença", + "editor.action.diffReview.prev": "Ir para a diferença anterior" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/ptb/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..329f087847 --- /dev/null +++ b/i18n/ptb/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "Editor", + "fontFamily": "Controla a família de fontes.", + "fontWeight": "Controla o peso da fonte.", + "fontSize": "Controla o tamanho da fonte em pixels.", + "lineHeight": "Controla a altura da linha. Use 0 para computar a altura da linha a partir do tamanho da fonte.", + "letterSpacing": "Controla o espaçamento da letra em pixels.", + "lineNumbers": "Controla a exibição de números de linha. Valores possíveis são 'on', 'off' e 'relative'. 'relative' mostra a contagem de linhas a partir da posição atual do cursor.", + "rulers": "Colunas nas quais mostrar réguas verticais", + "wordSeparators": "Caracteres que serão usados como separadores de palavras ao fazer navegação relacionada a palavras ou operações", + "tabSize": "O número de espaços equivalentes a uma tabulação. Esta configuração é sobreposta no conteúdo do arquivo quando `editor.detectIndentation` está ligado.", + "tabSize.errorMessage": "Esperado 'número'. Note que o valor \"auto\" foi alterado pela configuração 'editor.detectIndentation'.", + "insertSpaces": "Insere espaços quanto pressionado Tab. Esta configuração é sobrescrita com base no conteúdo do arquivo quando 'editor.detectIndentation' está habilitado.", + "insertSpaces.errorMessage": "Esperado 'booleano'. Note que o valor \"auto\" foi alterado pela configuração 'editor.detectIndentation'.", + "detectIndentation": "Quando um arquivo está sendo aberto, 'editor.tabSize' e 'editor.insertSpace' será detectado com base no conteúdo do arquivo.", + "roundedSelection": "Controla se as seleções têm cantos arredondados", + "scrollBeyondLastLine": "Controla se o editor rolará além da última linha", + "smoothScrolling": "Controla se o editor irá rolar usando uma animação", + "minimap.enabled": "Controla se o mini mapa é exibido", + "minimap.showSlider": "Controla se o controle deslizante minimap é automaticamente escondido. Os valores possíveis são 'sempre' e 'mouseover'", + "minimap.renderCharacters": "Renderizar os caracteres em uma linha (em oposição a blocos de caracteres)", + "minimap.maxColumn": "Limitar o tamanho de um mini-mapa para renderizar no máximo um número determinado de colunas", + "find.seedSearchStringFromSelection": "Controla se nós inicializamos a string de pesquisa na Ferramenta de Pesquisa a partir da seleção do editor", + "find.autoFindInSelection": "Controla se a configuração Find in Selection deve estar ativada quando vários caracteres ou linhas de texto estão selecionados no editor", + "wordWrap.off": "As linhas nunca serão quebradas.", + "wordWrap.on": "As linhas serão quebradas na largura de visualização", + "wordWrap.wordWrapColumn": "As linhas serão quebradas em `editor.wordWrapColumn`.", + "wordWrap.bounded": "As linhas serão quebradas no mínimo entre a largura de visualização e `editor.wordWrapColumn`.", + "wordWrap": "Controla como as linhas devem ser quebradas automaticamente. Pode ser:\n- 'off' (quebra automática de linha desabilitada)\n- 'on' (quebra automática de linha na largura da janela)\n- 'wordWrapColumn' (quebra automática no numero de colunas definido em `editor.wordWrapColumn`) ou\n- 'bounded' (quebra automática em uma dimensão minima da janela e na largura configurada)", + "wordWrapColumn": "Controla a coluna de quebra de linha do editor quando editor.wordWrap` é 'wordWrapColumn' ou 'bounded'.", + "wrappingIndent": "Controla o recuo de linhas quebradas. Pode ser \"none\", \"same\" ou \"indent\".", + "mouseWheelScrollSensitivity": "Um multiplicador a ser usado em \"deltaX\" e \"deltaY\" dos eventos de rolagem do botão de rolagem do mouse", + "multiCursorModifier.ctrlCmd": "Mapeia para 'Control' no Windows e Linux e 'Command' no OSX.", + "multiCursorModifier.alt": "Mapeia para 'Alt' em Windows e Linux e 'Option' em OSX.", + "multiCursorModifier": "O modificador a ser usado para adicionar vários cursores com o mouse. `ctrlCmd` mapeia 'Control' no Windows e Linux e 'Command' no OSX. Os gestos do mouse Ir para definição e Abrir Link irão adaptar-se tal maneira que eles não entrem em conflito com o modificador multicursor.", + "quickSuggestions.strings": "Habilitar sugestões rápidas dentro de strings.", + "quickSuggestions.comments": "Habilitar sugestões rápidas dentro de comentários.", + "quickSuggestions.other": "Habilitar sugestões rápidas fora de strings e comentários.", + "quickSuggestions": "Controlar se sugestões devem aparecer automaticamente ao digitar", + "quickSuggestionsDelay": "Controla o atraso em ms após o qual sugestões rápidas serão exibidas", + "parameterHints": "Habilita pop-up que mostra documentação de parâmetros e o tipo de informação conforme você digita", + "autoClosingBrackets": "Controla se o editor deve fechar colchetes automaticamente depois de abri-los", + "formatOnType": "Controla se o editor deve formatar automaticamente a linha após a digitação", + "formatOnPaste": "Controla se o editor deve formatar automaticamente o conteúdo colado. Um formatador deve estar disponível e o formatador deve ser capaz de formatar apenas uma parte do documento.", + "autoIndent": "Controles se o editor deve ajustar automaticamente o recuo, quando os usuários digitam, colam ou movem linhas. Regras de recuo da língua devem estar disponíveis. ", + "suggestOnTriggerCharacters": "Controla se as sugestões devem aparecer automaticamente ao digitar caracteres de gatilho", + "acceptSuggestionOnEnter": "Controla se as sugestões devem ser aceitas com 'Enter' - em adição a 'Tab'. Ajuda a evitar a ambiguidade entre a inserção de novas linhas ou aceitar sugestões. O valor 'smart' significa apenas aceitar uma sugestão com Enter quando ela fizer uma mudança textual", + "acceptSuggestionOnCommitCharacter": "Controla se as sugestões devem ser aceitas em caracteres de confirmação. Por exemplo, em JavaScript, o ponto-e-vírgula (';') pode ser um caractere de confirmação que aceita uma sugestão e digita esse caractere.", + "snippetSuggestions.top": "Mostre sugestões de trecho acima de outras sugestões.", + "snippetSuggestions.bottom": "Mostre sugestões de trecho abaixo de outras sugestões.", + "snippetSuggestions.inline": "Mostre sugestões de trechos com outras sugestões.", + "snippetSuggestions.none": "Não mostre sugestões de trecho.", + "snippetSuggestions": "Controla se os snippets são exibidos juntamente com as outras sugestões e como eles são ordenados.", + "emptySelectionClipboard": "Controla se a cópia sem nenhuma seleção copia a linha atual.", + "wordBasedSuggestions": "Controla se o auto-completar deve ser calculado baseado nas palavras no documento.", + "suggestFontSize": "Tamanho da fonte para a ferramenta de sugestão", + "suggestLineHeight": "Altura de linha para a ferramenta de sugestão", + "selectionHighlight": "Controla se o editor deve realçar correspondências semelhantes à seleção", + "occurrencesHighlight": "Controla se o editor deve realçar ocorrências de símbolos semânticos.", + "overviewRulerLanes": "Controla o número de decorações que podem ser exibidas na mesma posição na régua de visão geral", + "overviewRulerBorder": "Controla se deve desenhar uma borda ao redor da régua de visão geral.", + "cursorBlinking": "Controla o estilo de animação do cursor, os valores possíveis são 'blink', 'smooth', 'phase', 'expand' e 'solid'", + "mouseWheelZoom": "Alterar o zoom da fonte editor quando utilizada a roda do mouse e pressionando Ctrl", + "cursorStyle": "Controla o estilo do cursor, os valores aceitos são 'block', 'block-outline', 'line', 'line-thin', 'underline' e 'underline-thin'", + "fontLigatures": "Habilita ligaduras de fontes", + "hideCursorInOverviewRuler": "Controla se o cursor deve ficar oculto na régua de visão geral.", + "renderWhitespace": "Controla como o editor deve rendenizar caracteres de espaços em branco, possibilidades são 'none', 'boundary' e 'all'. A opção 'boundary' não rendeniza espaços simples entre palavras.", + "renderControlCharacters": "Controla se o editor deve renderizar caracteres de controle", + "renderIndentGuides": "Controla se o editor deve renderizar guias de identação", + "renderLineHighlight": "Controla como o editor deve renderizar a linha atual, as possibilidades são 'none', 'gutter', 'line' e 'all'.", + "codeLens": "Controla se o editor exibirá a lente de códigos.", + "folding": "Controla se o editor tem codigo colapsível hablitado", + "showFoldingControls": "Controla se os controles de desdobramento na divisão são ocultas automaticamente.", + "matchBrackets": "Realça colchetes correspondente quando um deles estiver selecionado.", + "glyphMargin": "Controla se o editor deve renderizar a margem vertical de ícones. A margem vertical de ícones é usada primordialmente na depuração", + "useTabStops": "Inserção e deleção de espaço em branco seguem a tabulação", + "trimAutoWhitespace": "Remove espaços em branco inseridos automaticamente no fim da linha", + "stablePeek": "Mantém os editores de visualização abertos mesmo quando clicando seu conteúdo ou teclando Escape.", + "dragAndDrop": "Controla se o editor deve permitir mover seleções via arrastar e soltar.", + "accessibilitySupport.auto": "O editor irá utilizar a plataforma da API para detectar quando um leitor de tela está conectado.", + "accessibilitySupport.on": "O editor será permanentemente otimizado para o uso de um leitor de tela.", + "accessibilitySupport.off": "O editor nunca será otimizado para o uso de um leitor de tela.", + "accessibilitySupport": "Controla quando o editor deve executar em modo otimizado para leitores de tela.", + "links": "Controla se o editor deve detectar links e torná-los clicáveis", + "colorDecorators": "Controla se o editor deve processar os decoradores de cor inline e o seletor de cores.", + "sideBySide": "Controla se o editor de diff mostra as diff lado a lado ou inline.", + "ignoreTrimWhitespace": "Controla se o editor de diff mostra alterações nos espaços iniciais ou finais como diferenças", + "renderIndicators": "Controla se o editor de diff mostra indicadores +/- para alterações adicionadas/removidas", + "selectionClipboard": "Controla se a área primária de transferência Linux deve ser suportada." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/ptb/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..3c24d60bd9 --- /dev/null +++ b/i18n/ptb/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "O editor não está acessível neste momento. Por favor pressione Alt+F1 para opções.", + "editorViewAccessibleLabel": "Conteúdo do editor" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/common/controller/cursor.i18n.json b/i18n/ptb/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..de89ae697f --- /dev/null +++ b/i18n/ptb/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "Exceção inesperada ao executar o comando." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/ptb/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..08092b1b00 --- /dev/null +++ b/i18n/ptb/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "O modo falhou ao gerar token da entrada." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/ptb/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..642fa3cac1 --- /dev/null +++ b/i18n/ptb/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "Texto sem formatação" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/ptb/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..a2859736c2 --- /dev/null +++ b/i18n/ptb/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "Estes arquivos foram alterados nesse meio tempo: {0}", + "summary.0": "Não foram feitas edições", + "summary.nm": "Feitas {0} edições de texto em {1} arquivos", + "summary.n0": "Feitas {0} edições de texto em um arquivo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/ptb/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..b27b15a08c --- /dev/null +++ b/i18n/ptb/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Contribui às declarações de linguagem.", + "vscode.extension.contributes.languages.id": "ID da linguagem", + "vscode.extension.contributes.languages.aliases": "Aliases de nome para esta linguagem.", + "vscode.extension.contributes.languages.extensions": "Extensões de arquivos associadas a esta linguagem", + "vscode.extension.contributes.languages.filenames": "Nome dos arquivos associados a esta linguagem", + "vscode.extension.contributes.languages.filenamePatterns": "Padrão glob de nomes de arquivos associados a linguagem.", + "vscode.extension.contributes.languages.mimetypes": "Tipos Mime associados à linguagem.", + "vscode.extension.contributes.languages.firstLine": "Uma expressão regular que coincide com a primeira linha de um arquivo da linguaguem.", + "vscode.extension.contributes.languages.configuration": "Um caminho relativo para um arquivo contendo opções de configuração para a linguagem." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/ptb/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..e6d63366a6 --- /dev/null +++ b/i18n/ptb/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}] {1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/ptb/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..723c1aa2b8 --- /dev/null +++ b/i18n/ptb/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "Cor de fundo para a posição do cursor na seleção de linhas.", + "lineHighlightBorderBox": "Cor de fundo para a borda em volta da linha na posição do cursor", + "rangeHighlight": "Cor de fundo dos ranges selecionados, assim como abertura instantânea e descoberta de recursos ", + "caret": "Cor do cursor no editor.", + "editorCursorBackground": "A cor de fundo do cursor do editor. Permite customizar a cor de um caractere sobreposto pelo bloco do cursor.", + "editorWhitespaces": "Cor dos caracteres em branco no editor", + "editorIndentGuides": "Cor das guias de indentação do editor.", + "editorLineNumbers": "Cor dos números de linha do editor.", + "editorRuler": "Cor das réguas do editor.", + "editorCodeLensForeground": "Cor do primeiro plano das lentes de código do editor", + "editorBracketMatchBackground": "Cor de fundo atrás do colchetes correspondentes", + "editorBracketMatchBorder": "Cor para as caixas de colchetes correspondentes", + "editorOverviewRulerBorder": "Cor da borda da régua de visão geral.", + "editorGutter": "Cor de fundo da separação do editor.O separador contém os glifos das margens e os números de linha.", + "errorForeground": "Cor do primeiro plano das linhas onduladas de erro no editor.", + "errorBorder": "Cor da borda das linhas onduladas de erro no editor.", + "warningForeground": "Cor do primeiro plano de linhas onduladas de aviso no editor.", + "warningBorder": "Cor da borda das linhas onduladas de aviso no editor.", + "overviewRulerRangeHighlight": "Visão geral da cor do marcador da régua para intervalos de destaques.", + "overviewRuleError": "Visão geral da cor do marcador da régua para erros.", + "overviewRuleWarning": "Visão geral da cor do marcador da régua para avisos.", + "overviewRuleInfo": "Visão geral da cor do marcador da régua para informações." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json b/i18n/ptb/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json new file mode 100644 index 0000000000..0086c91982 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "introMsg": "Obrigado por testar a opção de acessibilidade do VS Code.", + "status": "Status", + "tabFocusModeOnMsg": "Pressionando Tab no editor corrente irá mover o foco para o próximo elemento focável. Mude este comportamento ao pressionar {0}.", + "tabFocusModeOnMsgNoKb": "Pressionando Tab no editor corrente irá mover o foco para o próximo elemento focável. O comando {0} não pode ser ativado atualmente por uma tecla.", + "tabFocusModeOffMsg": "Pressionando Tab no editor atual irá inserir um caractere Tab. Mude este comportamente ao pressionar {0}.", + "tabFocusModeOffMsgNoKb": "Pressionando Tab no editor atual irá inserir um caractere Tab. O comando {0} não pode ser ativado atualmente por uma tecla.", + "outroMsg": "Você pode ignorar essa dica e retornar ao editor apertando a tecla ESC", + "ShowAccessibilityHelpAction": "Mostrar ajuda de acessibilidade" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/ptb/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..645495779b --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "Ir para colchete" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/ptb/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..d2cada67c7 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "Mover cursor para a esquerda", + "caret.moveRight": "Mover cursor para a direita" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/ptb/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..31e92ee948 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "Transport letras" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/ptb/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..dee6b2f52f --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "Recortar", + "actions.clipboard.copyLabel": "Copiar", + "actions.clipboard.pasteLabel": "Colar", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "Copiar com realce de sintaxe" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/ptb/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..75b41eccea --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "Alternar Comentário de Linha", + "comment.line.add": "Adicionar Comentário de Linha", + "comment.line.remove": "Remover Comentário de Linha", + "comment.block": "Alternar Comentário de Bloco" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/ptb/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..5c168ec873 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "Mostrar o menu de contexto do editor" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/ptb/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..f6c8ebca27 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Localizar", + "placeholder.find": "Localizar", + "label.previousMatchButton": "Correspondência anterior", + "label.nextMatchButton": "Próxima correspondência", + "label.toggleSelectionFind": "Localizar na seleção", + "label.closeButton": "Fechar", + "label.replace": "Substituir", + "placeholder.replace": "Substituir", + "label.replaceButton": "Substituir", + "label.replaceAllButton": "Substituir Tudo", + "label.toggleReplaceButton": "Ativar/desativar modo Substituir", + "title.matchesCountLimit": "Somente os primeiros 999 resultados são realçados, mas todas as operações de pesquisa funcionam em todo o texto.", + "label.matchesLocation": "{0} de {1}", + "label.noResults": "Nenhum resultado" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/ptb/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..c341eb84de --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Localizar", + "placeholder.find": "Localizar", + "label.previousMatchButton": "Correspondência anterior", + "label.nextMatchButton": "Próxima correspondência", + "label.closeButton": "Fechar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/ptb/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..33571ed335 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "Localizar", + "findNextMatchAction": "Localizar Próximo", + "findPreviousMatchAction": "Localizar anterior", + "nextSelectionMatchFindAction": "Localizar Próxima Seleção", + "previousSelectionMatchFindAction": "Localizar Seleção Anterior", + "startReplace": "Substituir", + "addSelectionToNextFindMatch": "Adicionar Seleção ao Próximo Localizar Correspondência", + "addSelectionToPreviousFindMatch": "Adicionar Seleção à Correspondência de Localização Anterior", + "moveSelectionToNextFindMatch": "Mover Última Seleção para Próximo Localizar Correspondência", + "moveSelectionToPreviousFindMatch": "Mover Última Seleção para Correspondência de Localização Anterior", + "selectAllOccurrencesOfFindMatch": "Selecionar Todas as Ocorrências de Localizar Correspondência", + "changeAll.label": "Alterar todas as ocorrências", + "showNextFindTermAction": "Mostrar Próximo Termo de Busca", + "showPreviousFindTermAction": "Mostrar Termo de Busca Anterior" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/ptb/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..31678f4ee1 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "Abrir", + "unFoldRecursivelyAction.label": "Abrir recursivamente", + "foldAction.label": "Colapsar", + "foldRecursivelyAction.label": "Colapsar recursivamente", + "foldAllAction.label": "Colapsar tudo", + "unfoldAllAction.label": "Abrir tudo", + "foldLevelAction.label": "Nível de colapsamento {0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/ptb/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..805a1bb606 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "1 edição de formatação feita na linha {0}", + "hintn1": "{0} edições de formatação feitas na linha {1}", + "hint1n": "Feita 1 edição de formatação entre as linhas {0} e {1}", + "hintnn": "Feitas {0} edições de formatação entre as linhas {1} e {2}", + "formatDocument.label": "Formatar Documento", + "formatSelection.label": "Formatar Seleção" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/ptb/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..e1b5599c51 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "Não foi encontrada definição para '{0}'", + "generic.noResults": "Nenhuma definição encontrada", + "meta.title": "- {0} definições", + "actions.goToDecl.label": "Ir para Definição", + "actions.goToDeclToSide.label": "Abrir definição ao lado", + "actions.previewDecl.label": "Inspecionar definição", + "goToImplementation.noResultWord": "Nenhuma implementação encontrada para '{0}'", + "goToImplementation.generic.noResults": "Nenhuma implementação encontrada", + "meta.implementations.title": "– {0} implementações", + "actions.goToImplementation.label": "Ir para a implementação", + "actions.peekImplementation.label": "Inspecionar implementação", + "goToTypeDefinition.noResultWord": "Nenhuma definição encontrada para '{0}'", + "goToTypeDefinition.generic.noResults": "Nenhuma definição de tipo encontrada", + "meta.typeDefinitions.title": "– {0} definições de tipos", + "actions.goToTypeDefinition.label": "Ir para a definição de tipo", + "actions.peekTypeDefinition.label": "Inspecionar definição de tipo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/ptb/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..a0c68c65bf --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "Clique para mostrar {0} definições." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/ptb/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..2ad6f98a2e --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "Ir para o Próximo Erro ou Aviso", + "markerAction.previous.label": "Ir para o Erro ou Aviso Anterior", + "editorMarkerNavigationError": "Ferramenta de marcação de edição apresentando error na cor ", + "editorMarkerNavigationWarning": "Ferramenta de marcação de edição apresentando adventência na cor", + "editorMarkerNavigationBackground": "Cor de fundo da ferramenta de marcação de navegação do editor." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/ptb/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..9ac0a904a8 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "Mostrar Item Flutuante" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/ptb/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..2222025267 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "Carregando..." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/ptb/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..4a15d7d036 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "Substituir pelo valor anterior", + "InPlaceReplaceAction.next.label": "Substituir pelo próximo valor" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/ptb/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..466267f891 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "Converter indentação em espaços.", + "indentationToTabs": "Coverter Indentação a Tabulações.", + "configuredTabSize": "Tamanho de Tabulação Configurado", + "selectTabWidth": "Selecione o Tamanho de Tabulação para o Arquivo Atual", + "indentUsingTabs": "Indentar Usando Tabulações", + "indentUsingSpaces": "Indentar Usando Espaços", + "detectIndentation": "Detectar Indentação a Partir do Conteúdo", + "editor.reindentlines": "Reindentar Linhas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json b/i18n/ptb/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..bc204f7f80 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Desenvolvedor: Inspecionar escopos TM", + "inspectTMScopesWidget.loading": "Carregando..." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/ptb/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..ca8fbdc8c6 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "Copiar linha acima", + "lines.copyDown": "Copiar linha abaixo", + "lines.moveUp": "Mover linha para cima", + "lines.moveDown": "Mover linha para baixo", + "lines.sortAscending": "Classificar Linhas Ascendentemente", + "lines.sortDescending": "Classificar Linhas Descendentemente", + "lines.trimTrailingWhitespace": "Cortar Espaço em Branco à Direita", + "lines.delete": "Excluir linha", + "lines.indent": "Recuar linha", + "lines.outdent": "Recuar linha para a esquerda", + "lines.insertBefore": "Inserir linha acima", + "lines.insertAfter": "Inserir linha abaixo", + "lines.deleteAllLeft": "Excluir tudo à Esquerda", + "lines.deleteAllRight": "Excluir Tudo à Direita", + "lines.joinLines": "Unir Linhas", + "editor.transpose": "Transpor caracteres ao redor do cursor", + "editor.transformToUppercase": "Transformar para maiúsculas", + "editor.transformToLowercase": "Transformar para minúsculas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/ptb/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..6fc4d284f9 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "Cmd + clique para seguir o link", + "links.navigate": "Ctrl + clique para seguir o link", + "links.command.mac": "Cmd + clique para executar o comando", + "links.command": "CTRL + clique para executar o comando", + "links.navigate.al": "Alt + clique para seguir o link", + "links.command.al": "Alt + clique para executar o comando", + "invalid.url": "Desculpe, falha ao abrir este link porque ele não está bem formatado: {0}", + "missing.url": "Desculpe, falha ao abrir este link porque seu destino está faltando.", + "label": "Abrir link" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/ptb/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..440e21e314 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "Inserir cursor acima", + "mutlicursor.insertBelow": "Inserir cursor abaixo", + "mutlicursor.insertAtEndOfEachLineSelected": "Adicionar Cursores ao Final das Linhas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/ptb/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..210ccbe329 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "Dicas de parâmetro de gatilho" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/ptb/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..25f468ddfd --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0}, dica" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/ptb/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..ff6a62b26f --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "Mostrar correções ({0})", + "quickFix": "Mostrar correções", + "quickfix.trigger.label": "Correção Rápida" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..7d434504ac --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": "- {0} referências", + "references.action.label": "Localizar Todas as Referências" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..7bf55ae850 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "Carregando..." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..fb1411af83 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "símbolo em {0} na linha {1} e coluna {2}", + "aria.fileReferences.1": "1 símbolo em {0}, caminho completo {1}", + "aria.fileReferences.N": "{0} símbolos em {1}, caminho completo {2}", + "aria.result.0": "Nenhum resultado encontrado", + "aria.result.1": "Encontrado 1 símbolo em {0}", + "aria.result.n1": "Encontrados {0} símbolos em {1}", + "aria.result.nm": "Encontrados {0} símbolos em {1} arquivos" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..2ea6613ab3 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "Falha ao resolver arquivo.", + "referencesCount": "{0} referências", + "referenceCount": "{0} referência", + "missingPreviewMessage": "nenhuma visualização disponível", + "treeAriaLabel": "Referências", + "noResults": "Nenhum resultado", + "peekView.alternateTitle": "Referências", + "peekViewTitleBackground": "Cor de fundo da área de visualização do título.", + "peekViewTitleForeground": "Cor de visualização do título.", + "peekViewTitleInfoForeground": "Cor da visualização de informações do título.", + "peekViewBorder": "Cor das bordas e seta da área de visualização", + "peekViewResultsBackground": "Cor de fundo da área de visualização da lista de resultados.", + "peekViewResultsMatchForeground": "Cor de primeiro plano para nós de linha na lista de resultados visualizados.", + "peekViewResultsFileForeground": "Cor de primeiro plano para nós de arquivos na lista de resultados visualizados.", + "peekViewResultsSelectionBackground": "Cor de fundo da entrada selecionada na visualização da lista de resultados.", + "peekViewResultsSelectionForeground": "Cor da entrada selecionada na visualização da lista de resultados.", + "peekViewEditorBackground": "Cor de fundo da visualização do editor.", + "peekViewEditorGutterBackground": "Cor de fundo da separação na visualização rápida do editor.", + "peekViewResultsMatchHighlight": "Corresponder cor de realce com visualização da lista de resultados.", + "peekViewEditorMatchHighlight": "Corresponder cor de realce com visualização do editor." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/ptb/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..2ed3844cec --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "Nenhum resultado.", + "aria": "Renomeado '{0}' para '{1}'com sucesso. Resumo: {2}", + "rename.failed": "Desculpe, falha na execução de renomear.", + "rename.label": "Renomear Símbolo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/ptb/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..58c6b2bbb3 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "Renomear entrada. Digite o novo nome e tecle Enter para gravar." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/ptb/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..a44e542117 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "Expandir seleção", + "smartSelect.shrink": "Reduzir seleção" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/ptb/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..93aa779e48 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "Ao aceitar '{0}' foi inserido o seguinte texto: {1}", + "suggest.trigger.label": "Sugestão de gatilho" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/ptb/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..7793c2ad59 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "Cor de fundo para a ferramenta de sugestão.", + "editorSuggestWidgetBorder": "Cor da borda para a ferramenta de sugestão.", + "editorSuggestWidgetForeground": "Cor de primeiro plano para a ferramenta de sugestão.", + "editorSuggestWidgetSelectedBackground": "Cor de fundo da entrada selecionada da ferramenta de sugestões.", + "editorSuggestWidgetHighlightForeground": "Cor de realce da correspondência na ferramenta de sugestão.", + "readMore": "Ler Mais...{0}", + "suggestionWithDetailsAriaLabel": "{0}, sugestão, tem detalhes", + "suggestionAriaLabel": "{0}, sugestão", + "readLess": "Ler menos... {0}", + "suggestWidget.loading": "Carregando...", + "suggestWidget.noSuggestions": "Nenhuma sugestão.", + "suggestionAriaAccepted": "{0}, aceito", + "ariaCurrentSuggestionWithDetails": "{0}, sugestão, tem detalhes", + "ariaCurrentSuggestion": "{0}, sugestão" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/ptb/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..c313800291 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "Alterne o uso da tecla Tab para mover o foco" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/ptb/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..8101f28880 --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "Cor de fundo de um símbolo durante acesso de leitura, como ao ler uma variável.", + "wordHighlightStrong": "Cor de fundo de um símbolo durante acesso de escrita, como ao escrever uma variável.", + "overviewRulerWordHighlightForeground": "Visão geral da cor do marcador da régua para destaques de símbolos.", + "overviewRulerWordHighlightStrongForeground": "Visão geral da cor do marcador da régua para gravação de destaques de símbolos." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/ptb/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..6babd24e9a --- /dev/null +++ b/i18n/ptb/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Fechar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/ptb/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..76fd6fa1f8 --- /dev/null +++ b/i18n/ptb/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Linguagem desconhecida em `contributes.{0}.language`. Valor fornecido: {1}", + "invalid.scopeName": "Esperada uma string em 'contributes.{0}.scopeName'. Valor informado: {1}", + "invalid.path.0": "Esperada uma string em `contributes.{0}.path`. Valor informado: {1}", + "invalid.injectTo": "Valor inválido em `contributes.{0}.injectTo`. Deve ser uma matriz de nomes de escopo de idioma. Valor fornecido: {1}", + "invalid.embeddedLanguages": "Valor inválido em `contributes.{0}.embeddedLanguages`. Deve ser um objeto de mapeamento do nome do escopo para a linguagem. Valor informado: {1}", + "invalid.path.1": "É esperado que `contributes.{0}.path` ({1}) seja incluído na pasta da extensão ({2}). Isto pode tornar a extensão não portável.", + "no-tm-grammar": "Nenhuma gramática TM registrada para este idioma." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/ptb/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..baf3fa0876 --- /dev/null +++ b/i18n/ptb/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Erros parseando {0}: {1}", + "schema.openBracket": "O colchete de abertura de caractere ou sequência de caracteres.", + "schema.closeBracket": "O colchete de fechamento de caractere ou sequência de caracteres.", + "schema.comments": "Define o símbolo dos comentários", + "schema.blockComments": "Define como comentários em bloco são marcados.", + "schema.blockComment.begin": "A sequência de caracteres que inicia um comentário em bloco.", + "schema.blockComment.end": "A sequência de caracteres que termina um comentário de bloco.", + "schema.lineComment": "A sequência de caracteres que inicia um comentário de linha.", + "schema.brackets": "Define os símbolos de colchetes que aumentam ou diminuem a indentação.", + "schema.autoClosingPairs": "Define os pares de colchetes. Quando é introduzido um colchete de abertura, o colchete de fechamento é inserido automaticamente.", + "schema.autoClosingPairs.notIn": "Define uma lista de escopos onde os auto pares são desativados.", + "schema.surroundingPairs": "Define os pares de colchetes que podem ser usados para cercar uma seqüência selecionada.", + "schema.wordPattern": "A definição da palavra para a linguagem.", + "schema.wordPattern.pattern": "O padrão RegExp usado para coincidir com as palavras.", + "schema.wordPattern.flags": "Os sinalizadores RegExp usados para coincidir com as palavras.", + "schema.wordPattern.flags.errorMessage": "Deve corresponder ao padrão `/^([gimuy]+)$/`." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/ptb/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..d590e10cdf --- /dev/null +++ b/i18n/ptb/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Contibui aos toquenizadores textmate", + "vscode.extension.contributes.grammars.language": "Identificador da linguagem para qual a sintaxe contribui.", + "vscode.extension.contributes.grammars.scopeName": "Nome do escopo Textmate usado pelo arquivo tmLanguage.", + "vscode.extension.contributes.grammars.path": "Caminho para o arquivo tmLanguage. O caminho é relativo a pasta da extensão e geralmente começa com './syntaxes/'.", + "vscode.extension.contributes.grammars.embeddedLanguages": "Um mapeamento no nome do escopo para o Id da linguagem se esta gramática contenha linguagens embutidas.", + "vscode.extension.contributes.grammars.injectTo": "Lista de nomes de escopos de linguagem aos quais esta gramática é injetada." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/ptb/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/ptb/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/ptb/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..9927aafe6a --- /dev/null +++ b/i18n/ptb/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "os itens de menu devem ser um array", + "requirestring": "a propriedade `{0}` é obrigatória e deve ser do tipo `string`", + "optstring": "a propriedade `{0}` é opcional ou deve ser do tipo `string`", + "vscode.extension.contributes.menuItem.command": "Identificador do comando para ser executado. O comando deve ser declarado na seção de 'Comandos'", + "vscode.extension.contributes.menuItem.alt": "O identificador de um comando alternativo para executar. O comando deve ser declarado na sessão 'Comandos'", + "vscode.extension.contributes.menuItem.when": "Condição, que deve ser verdadeira, para mostrar esse item", + "vscode.extension.contributes.menuItem.group": "Grupo ao qual pertence este comando", + "vscode.extension.contributes.menus": "Contribui itens de menu ao editor", + "menus.commandPalette": "Paleta de comandos", + "menus.editorTitle": "Meno do título editor", + "menus.editorContext": "Mostrar o menu de contexto do editor", + "menus.explorerContext": "Menu no contexto de explorador de arquivos", + "menus.editorTabContext": "Mostrar o menu de contexto do editor", + "menus.debugCallstackContext": "O menu de contexto de pilha de chamadas de depuração", + "menus.scmTitle": "O menu de título do controle de fonte", + "menus.resourceGroupContext": "O menu de contexto do grupo de recursos de controle de fonte", + "menus.resourceStateContext": "O menu de contexto de estado de recursos do controle de fonte", + "view.viewTitle": "O menu de título da visualização contribuída", + "view.itemContext": "O menu de contexto do item da visualização contribuída", + "nonempty": "Esperado um valor não vazio", + "opticon": "a propriedade '{0}' é opcional ou pode ser do tipo 'string'", + "requireStringOrObject": "a propriedade '{0}' é obrigatória e deve ser do tipo 'string'", + "requirestrings": "a propriedade `{0}` é obrigatória e deve ser do tipo `string`", + "vscode.extension.contributes.commandType.command": "Indentificador de comando para executar", + "vscode.extension.contributes.commandType.title": "Título para o qual o comando é representado na UI", + "vscode.extension.contributes.commandType.category": "(Opcional) Sequência de categoria será agrupada na interface de usuário", + "vscode.extension.contributes.commandType.icon": "(Opcional) Icone utilizado para representar o comando na interface de usuário. Um arquivo ou configuração do tema.", + "vscode.extension.contributes.commandType.icon.light": "Caminho do Ícone quando o tema light for utilizado", + "vscode.extension.contributes.commandType.icon.dark": "Caminho do ícone quando o tema dark for utilizado", + "vscode.extension.contributes.commands": "Contribui comandos à paleta de comandos", + "dup": "Comando '{0}' aparece multiplas vezes na sessão 'comandos'\n", + "menuId.invalid": "'{0}' nao é um identificador de menu válido ", + "missing.command": "Identificador do comando para ser executado. O comando deve ser declarado na seção de 'Comandos'", + "missing.altCommand": "Referências ao item de menu no alt-command '{0}' qual nao é definido na sessão 'comandos'", + "dupe.command": "Itens de referencias do mesmo comando como padrão e alt-command", + "nosupport.altCommand": "Desculpe, mas atualmente somente o groupo 'navegação' do menu 'editor/título' suporta alt-commands" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/ptb/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..820a091cdb --- /dev/null +++ b/i18n/ptb/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "Sobreposições da Configuração Padrão", + "overrideSettings.description": "Definir que configurações do editor sejam substituídas para idioma {0}.", + "overrideSettings.defaultDescription": "Definir que configurações do editor sejam substituídas para um idioma.", + "config.property.languageDefault": "Não é possível registrar '{0}'. Isto corresponde a propriedade padrão '\\\\[.*\\\\]$' para descrever configurações do editor específico de linguagem. Use a contribuição 'configurationDefaults'.", + "config.property.duplicate": "Não é possível registrar '{0}'. Esta propriedade já está registrada." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/environment/node/argv.i18n.json b/i18n/ptb/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..dc1528c381 --- /dev/null +++ b/i18n/ptb/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "Argumentos no modo '--goto' deve ser no formato de 'Arquivo(:LINHA(:CARACTERE))'.", + "diff": "Comparar dois arquivos entre si.", + "add": "Adicione pasta(s) para a última janela ativa.", + "goto": "Abra um arquivo no caminho sobre a linha especificada e a posição do caractere.", + "locale": "Para localização utilize (ex. en-US ou zh-TW).", + "newWindow": "Força uma nova instância do Código.", + "performance": "Comece com o 'Desenvolvedor: Desempenho de inicialização' comando habilitado.", + "prof-startup": "Rodar o CPU profiler durante a inicialização", + "reuseWindow": "Forçar a abertura de um arquivo ou pasta na última janela ativa", + "userDataDir": "Especifica o diretório que os dados do usuário serão mantidos, útil quando estiver rodando como root.", + "verbose": "Imprimir a saída detalhada (Implica -- esperar).", + "wait": "Aguarde a janela ser fechada antes de retornar.", + "extensionHomePath": "Defina o caminho raíz para as extensões.", + "listExtensions": "Lista de extensões instaladas", + "showVersions": "Exibir versões de extensões instaladas, quando estiver usando --list-extension", + "installExtension": "Instala uma extensão.", + "uninstallExtension": "Desinstala uma extensão.", + "experimentalApis": "Permite recursos de api propostos para uma extensão.", + "disableExtensions": "Desabilita todas as extensões instaladas.", + "disableGPU": "Desabilita aceleração de hardware da GPU.", + "version": "Versão de impressão", + "help": "Uso de impressão.", + "usage": "Uso", + "options": "opções", + "paths": "caminhos", + "optionsUpperCase": "Opções" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/ptb/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..47d4891bfa --- /dev/null +++ b/i18n/ptb/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Não há espaço de trabalho." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/ptb/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..90b57e9027 --- /dev/null +++ b/i18n/ptb/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Extensões", + "preferences": "Preferências" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/ptb/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..0a881ddc2d --- /dev/null +++ b/i18n/ptb/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Extensão não encontrada", + "noCompatible": "Não foi possível econtrar uma versão de {0} com esta versão do Code." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/ptb/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..56b75e6237 --- /dev/null +++ b/i18n/ptb/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "Extensão inválida: pacote.json nao é um arquivo JSON válido", + "restartCode": "Por favor reinicie Code antes de reinstalar {0}.", + "installDependeciesConfirmation": "A instalação de '{0}' também inclui suas dependências. Gostaria de continuar?", + "install": "Sim", + "doNotInstall": "Não", + "uninstallDependeciesConfirmation": "Gostaria de desinstalar '{0}' somente, ou suas dependências também?", + "uninstallOnly": "Apenas", + "uninstallAll": "Todos", + "cancel": "Cancelar", + "uninstallConfirmation": "Tem certeza que deseja desinstalar '{0}'?", + "ok": "OK", + "singleDependentError": "Não foi possível desinstalar a extensão '{0}'. A extensão '{1}' depende dela.", + "twoDependentsError": "Não foi possível desinstalar a extensão '{0}'. As extensões '{1}' e '{2}' dependem dela.", + "multipleDependentsError": "Não foi possível desinstalar a extensão '{0}'. As extensões '{1}' e '{2}' e outras dependem dela.", + "notExists": "Não foi possível encontrar a extensão" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/ptb/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..4990575f1c --- /dev/null +++ b/i18n/ptb/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "Extensão '{1}' falhou ao ativar. Motivo: dependência desconhecida '{0}'.", + "failedDep1": "Extensão '{1}' falhou ao ativar. Motivo: a dependência '{0}' falhou ao ativar.", + "failedDep2": "Extensão '{0}' falhou ao ativar. Motivo: mais de 10 níveis de dependências (provavelmente um laço de dependência).", + "activationError": "Ativação da extensão `{0}` falhou: {1}." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/ptb/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..6a2795289b --- /dev/null +++ b/i18n/ptb/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "Para extensões do VS Code, especifica a versão do VS Code que a extensão é compatível. Não pode ser *. Por exemplo: ^0.10.5 indica compatibilidade com uma versão mínima de 0.10.5 para o VS Code.", + "vscode.extension.publisher": "O editor da extensão do VS Code.", + "vscode.extension.displayName": "O nome de exibição para a extensão do VS Code.", + "vscode.extension.categories": "As categorias usadas pela galeria do VS Code para categorizar a extensão.", + "vscode.extension.galleryBanner": "Banner usado na loja VS Code.", + "vscode.extension.galleryBanner.color": "A cor do banner usado no cabeçalho de página da loja VS Code.", + "vscode.extension.galleryBanner.theme": "A cor do tema usada para o fonte usado no banner.", + "vscode.extension.contributes": "Todas as contribuições da extensão VS Code representadas por este pacote.", + "vscode.extension.preview": "Configura a extensão para ser marcada como pré-visualização na Loja.", + "vscode.extension.activationEvents": "Eventos de ativação para a extensão VS Code.", + "vscode.extension.activationEvents.onLanguage": "Um evento de ativação emitido sempre que um arquivo que resolve para a linguagem especificada é aberto.", + "vscode.extension.activationEvents.onCommand": "Um evento de ativação emitido sempre que o comando especificado for invocado.", + "vscode.extension.activationEvents.onDebug": "Um evento de ativação emitido sempre que uma sessão de depuração do tipo especificado é iniciada.", + "vscode.extension.activationEvents.workspaceContains": "Um evento de ativação emitido quando uma pasta que contém pelo menos um arquivo correspondente ao padrão global especificado é aberta.", + "vscode.extension.activationEvents.onView": "Um evento de ativação emitido sempre que o modo de visualização especificado é expandido.", + "vscode.extension.activationEvents.star": "Um evento de ativação emitido na inicialização do VS Code. Para garantir uma ótima experiência de usuário, por favor, use este evento de ativação em sua extensão somente quando nenhuma outra combinação de eventos de ativação funcionar em seu caso de uso.", + "vscode.extension.badges": "Matriz de emblemas a mostrar na barra lateral da página da extensão na Loja.", + "vscode.extension.badges.url": "URL da imagem do emblema.", + "vscode.extension.badges.href": "Link do emblema.", + "vscode.extension.badges.description": "Descrição do emblema.", + "vscode.extension.extensionDependencies": "Dependências para outras extensões. O identificador de uma extensão sempre é ${publisher}. ${nome}. Por exemplo: vscode.csharp.", + "vscode.extension.scripts.prepublish": "Script a ser executado antes do pacote ser publicado como uma extensão VS Code.", + "vscode.extension.icon": "O caminho para um ícone de 128x128 pixels." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/ptb/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..f998dcb95b --- /dev/null +++ b/i18n/ptb/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "Não foi possível analisar o valor de 'engines.vscode' {0}. Por favor, utilize, por exemplo: ^ 0.10.0, ^ 1.2.3, ^ 0.11.0, ^ 0.10.x, etc.", + "versionSpecificity1": "Versão especificada em 'engines.vscode' ({0}) não é específica o suficiente. Para versões do vscode anteriores a 1.0.0, por favor defina no mínimo a versão principal e secundária desejada. Por exemplo, ^ 0.10.0, 0.10.x, 0.11.0, etc.", + "versionSpecificity2": "Versão especificada em 'engines.vscode' ({0}) não é específica o suficiente. Para as versões do vscode posteriores a 1.0.0, por favor defina no mínimo a versão principal do desejado. Por exemplo, ^ 1.10.0, 1.10.x 1. XX, 2.x.x, etc.", + "versionMismatch": "Extensão não é compatível com Code {0}. A extensão requer: {1}.", + "extensionDescription.empty": "Descrição de extensão vazia obtida", + "extensionDescription.publisher": "a propriedade `{0}` é obrigatória e deve ser do tipo `string`", + "extensionDescription.name": "a propriedade `{0}` é obrigatória e deve ser do tipo `string`", + "extensionDescription.version": "a propriedade `{0}` é obrigatória e deve ser do tipo `string`", + "extensionDescription.engines": "a propriedade `{0}` é obrigatória e deve ser do tipo `object`", + "extensionDescription.engines.vscode": "a propriedade `{0}` é obrigatória e deve ser do tipo `string`", + "extensionDescription.extensionDependencies": "a propriedade `{0}` pode ser omitida ou deve ser do tipo `string[]`", + "extensionDescription.activationEvents1": "a propriedade `{0}` pode ser omitida ou deve ser do tipo `string[]`", + "extensionDescription.activationEvents2": "Propriedades '{0}' e '{1}' devem ser especificadas ou devem ambas ser omitidas", + "extensionDescription.main1": "a propriedade `{0}` é opcional ou pode ser do tipo `string`", + "extensionDescription.main2": "Esperado 'main' ({0}) ser incluído dentro da pasta da extensão ({1}). Isto pode fazer a extensão não-portável.", + "extensionDescription.main3": "propriedades '{0}' e '{1}' devem ser especificadas ou devem ambas ser omitidas", + "notSemver": "Versão da extensão não é compatível a semver" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/ptb/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..865443d331 --- /dev/null +++ b/i18n/ptb/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "Nova Janela", + "newWindowDesc": "Abrir uma nova janela", + "recentFolders": "Espaços de trabalho recentes", + "folderDesc": "{0} {1}", + "codeWorkspace": "Área de trabalho de código" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/ptb/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..32186dd082 --- /dev/null +++ b/i18n/ptb/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "OK", + "integrity.dontShowAgain": "Não mostrar novamente", + "integrity.moreInfo": "Mais informações", + "integrity.prompt": "Sua instalação de {0} parece estar corrompida. Favor reinstalar." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/ptb/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..6dd4311162 --- /dev/null +++ b/i18n/ptb/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "Contribui à configuração do schema json.", + "contributes.jsonValidation.fileMatch": "O padrão de arquivo a corresponder, por exemplo \"package.json\" ou \"*.launch\".", + "contributes.jsonValidation.url": "Um esquema de URL ('http:', 'https:') ou caminho relativo à pasta de extensão('./').", + "invalid.jsonValidation": "'configuration.jsonValidation' deve ser uma matriz", + "invalid.fileMatch": "'configuration.jsonValidation.fileMatch' deve ser definido", + "invalid.url": "'configuration.jsonValidation.url' deve ser uma URL ou caminho relativo", + "invalid.url.fileschema": "'configuration.jsonValidation.url' é uma URL relativa inválida: {0}", + "invalid.url.schema": "'configuration.jsonValidation.url' deve começar com ' http:', ' https: 'ou'. /' para os esquemas de referência localizados na extensão" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/ptb/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..5c507547aa --- /dev/null +++ b/i18n/ptb/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "({0}) foi pressionado. Aguardando segunda tecla de pressionamento simultâneo...", + "missing.chord": "A combinação de chave ({0}, {1}) não é um comando." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/keybinding/common/keybindingLabels.i18n.json b/i18n/ptb/src/vs/platform/keybinding/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..e886e6929b --- /dev/null +++ b/i18n/ptb/src/vs/platform/keybinding/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Shift", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Controle", + "shiftKey.long": "Shift", + "altKey.long": "Alt", + "cmdKey.long": "Comando", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/ptb/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..ebddededc9 --- /dev/null +++ b/i18n/ptb/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "A propriedade loop só é suportada na última linha correspondente.", + "ProblemPatternParser.problemPattern.missingRegExp": "Está faltando uma expressão regular a problema padrão.", + "ProblemPatternParser.problemPattern.missingProperty": "O problema padrão é inválido. Ele deve ter ao menos um arquivo, mensagem e linha ou local de grupo de correspondência.", + "ProblemPatternParser.invalidRegexp": "Erro: a cadeia de caracteres {0} não é uma expressão regular válida.\n", + "ProblemPatternSchema.regexp": "A expressão regular para procurar um erro, aviso ou informação na saída.", + "ProblemPatternSchema.file": "O índice do grupo de correspondência do arquivo. Se omitido, será usado 1.", + "ProblemPatternSchema.location": "O índice de grupo de correspondência da localização do problema. Padrões de localização válidos são: (linha), (linha, coluna) e (startLine, startColumn, endLine, endColumn). Se omitido (linha, coluna) é assumido.", + "ProblemPatternSchema.line": "O índice de grupo de correspondência da linha do problema. O padrão é 2", + "ProblemPatternSchema.column": "O índice de grupo de correspondência de caractere da linha do problema. O padrão é 3", + "ProblemPatternSchema.endLine": "O índice de grupo de correspondência de linha final do problema. O padrão é indefinido", + "ProblemPatternSchema.endColumn": "O índice de grupo de correspondência de caráter final de linha do problema. O padrão é indefinido", + "ProblemPatternSchema.severity": "O índice de grupo de correspondência da gravidade do problema. O padrão é indefinido", + "ProblemPatternSchema.code": "O índice de grupo de correspondência do código do problema. O padrão é indefinido", + "ProblemPatternSchema.message": "O índice de grupo de correspondência da mensagem. Se omitido o padrão é 4 se o local for especificado. Caso contrário o padrão é 5.", + "ProblemPatternSchema.loop": "Em um loop de correspondência multi linha indica se este padrão é executado em um loop enquanto houver correspondências. Somente pode ser especificado no último padrão em um padrão de linha múltiplas.", + "NamedProblemPatternSchema.name": "O nome do modelo de problema.", + "NamedMultiLineProblemPatternSchema.name": "O nome do modelo de problema multi-linhas.", + "NamedMultiLineProblemPatternSchema.patterns": "Os padrões atuais.", + "ProblemPatternExtPoint": "Contribui aos modelos de problema", + "ProblemPatternRegistry.error": "Modelo de problema inválido. O modelo será ignorado.", + "ProblemMatcherParser.noProblemMatcher": "Erro: a descrição não pode ser convertida em uma correspondência de problema:\n{0}\n\n", + "ProblemMatcherParser.noProblemPattern": "Erro: a descrição nao define um padrão de problema válido: {0}\n", + "ProblemMatcherParser.noOwner": "Erro: a descriçao não define um proprietário:\n{0}\n", + "ProblemMatcherParser.noFileLocation": "Erro: a descrição não define uma localização de arquivo:\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "Info: severidade {0} desconhecida. Valores válidos são erro, aviso e info.\n", + "ProblemMatcherParser.noDefinedPatter": "Erro: o padrão com o identificador {0} não existe.", + "ProblemMatcherParser.noIdentifier": "Erro: a propriedade padrão se refere a um identificador vazio.", + "ProblemMatcherParser.noValidIdentifier": "Erro: a propriedade padrão {0} não é uma variável de padrões válida.", + "ProblemMatcherParser.problemPattern.watchingMatcher": "Um problema de correspondência deve obrigatoriamente definir padrão inicial e um padrão final para monitoramento.", + "ProblemMatcherParser.invalidRegexp": "Erro: a cadeia de caracteres {0} não é uma expressão regular válida.\n", + "WatchingPatternSchema.regexp": "A expressão regular para detectar o início ou o fim de uma tarefa em segundo plano.", + "WatchingPatternSchema.file": "O índice do grupo de correspondência do arquivo. Pode ser omitido.", + "PatternTypeSchema.name": "O nome de um padrão pré-definido ou contribuído.", + "PatternTypeSchema.description": "Um padrão de problema ou o nome de um padrão de problema pré-definido ou contribuído. Pode ser omitido se base for especificada.", + "ProblemMatcherSchema.base": "O nome de uma correspondência de problema base a ser utilizado.", + "ProblemMatcherSchema.owner": "O proprietário de um problema dentro do código. Pode ser omitido se base for especificada. Default para 'externo' se omitido e base não for especificada.", + "ProblemMatcherSchema.severity": "A severidade padrão para captura de problemas. É utilizada se o padrão não definir um grupo correspondente para severidade.", + "ProblemMatcherSchema.applyTo": "Controla se um problema reportado em um documento de texto é aplicado somente para aberto, fechado ou todos os documentos.", + "ProblemMatcherSchema.fileLocation": "Define como os nomes de arquivos reportados em um padrão de problema devem ser interpretados.", + "ProblemMatcherSchema.background": "Padrões para monitorar o início e o término de um pesquisador ativo em uma tarefa em segundo plano.", + "ProblemMatcherSchema.background.activeOnStart": "Se configurado para verdadeiro, o monitor em segundo plano está em modo ativo quando a tarefa inicia. Isto é igual a emissão de uma linha que corresponde ao beginPattern", + "ProblemMatcherSchema.background.beginsPattern": "Se houver correspondência na saída o início de uma tarefa em segundo plano é sinalizada.", + "ProblemMatcherSchema.background.endsPattern": "Se houver correspondência na saída o final de uma tarefa em segundo plano é sinalizada.", + "ProblemMatcherSchema.watching.deprecated": "A propriedade watching foi descontinuada. Use background no lugar dela.", + "ProblemMatcherSchema.watching": "Padrões para monitorar o início e o término de um pesquisador observando.", + "ProblemMatcherSchema.watching.activeOnStart": "Se configurado para verdadeiro, o monitoramento está em modo ativo quando a tarefa inicia. Isto é igual a emissão de uma linha que corresponde ao beginPattern", + "ProblemMatcherSchema.watching.beginsPattern": "Se houver correspondência na saída o início de uma tarefa observada é sinalizada.", + "ProblemMatcherSchema.watching.endsPattern": "Se houver correspondência na saída o final de uma tarefa observada é sinalizada.", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "Esta propriedade está descontinuada. Ao invés, use a propriedade de observação.", + "LegacyProblemMatcherSchema.watchedBegin": "Uma expressão regular sinalizando que uma tarefa observada é ativada através da observação.", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "Esta propriedade está descontinuada. Ao invés, use a propriedade de observação.", + "LegacyProblemMatcherSchema.watchedEnd": "Uma expressão regular sinalizando que uma tarefa observada terminou a execução.", + "NamedProblemMatcherSchema.name": "O nome do correspondente do problema usado para se referir a ele.", + "NamedProblemMatcherSchema.label": "Um rótulo legível para o correspondente do problema.", + "ProblemMatcherExtPoint": "Contribui aos correspondentes de problema", + "msCompile": "Problemas do compilador Microsoft", + "lessCompile": "Menos problemas", + "gulp-tsc": "Problemas do Gulp TSC", + "jshint": "Problemas JSHint", + "jshint-stylish": "Problemas de estilo JSHint", + "eslint-compact": "Problemas compactos ESLint", + "eslint-stylish": "Problemas de estilo ESLint", + "go": "Problemas Go" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/message/common/message.i18n.json b/i18n/ptb/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..1c100757fd --- /dev/null +++ b/i18n/ptb/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Fechar", + "later": "Mais tarde", + "cancel": "Cancelar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/request/node/request.i18n.json b/i18n/ptb/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..68b42e696e --- /dev/null +++ b/i18n/ptb/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "As configurações de proxy a usar. Se não forem configuradas, serão obtidas das variáveis de ambiente http_proxy e https_proxy", + "strictSSL": "Se o certificado do servidor de proxy deve ser verificado contra a lista de autoridades de certificação fornecida.", + "proxyAuthorization": "O valor para enviar como o cabeçalho de 'autorização Proxy' para cada solicitação de rede." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/ptb/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..1bad43f425 --- /dev/null +++ b/i18n/ptb/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetria", + "telemetry.enableTelemetry": "Permitir que os dados de uso e erros sejam enviados à Microsoft." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/ptb/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..f66a22c2a3 --- /dev/null +++ b/i18n/ptb/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "Contribui com a extensão de cores temáticas definidas", + "contributes.color.id": "O identificador da cor temática", + "contributes.color.id.format": "Identificadores devem estar no formato aa [.bb] *", + "contributes.color.description": "A descrição da cor temática", + "contributes.defaults.light": "A cor padrão para temas claros. Um valor de cor em hexadecimal (#RRGGBB[AA]) ou o identificador de uma cor temática que fornece o padrão.", + "contributes.defaults.dark": "A cor padrão para temas escuros. Um valor de cor em hexadecimal (#RRGGBB[AA]) ou o identificador de uma cor temática que fornece o padrão.", + "contributes.defaults.highContrast": "A cor padrão para temas de alto contraste. Um valor de cor em hexadecimal (#RRGGBB[AA]) ou o identificador de uma cor temática que fornece o padrão.", + "invalid.colorConfiguration": "'configuration.colors' deve ser uma matriz", + "invalid.default.colorType": "{0} deve ser um valor de cor em hexadecimal (#RRGGBB [AA] ou #RGB[A]) ou o identificador de uma cor temática que fornece o padrão.", + "invalid.id": "'configuration.colors.id' deve ser definido e não pode estar vazio", + "invalid.id.format": "'configuration.colors.id' deve seguir a palavra [.word] *", + "invalid.description": "'configuration.colors.description' deve ser definido e não pode estar vazio", + "invalid.defaults": "'configuration.colors.defaults' deve ser definido e deve conter 'claro', 'escuro' e 'Alto Contraste'" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/ptb/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..de0d801aea --- /dev/null +++ b/i18n/ptb/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "Formato inválido de cor. Use #RGB, #RGBA, #RRGGBB ou #RRGGBBAA", + "schema.colors": "Cores usadas no workbench.", + "foreground": "Cor de primeiro plano geral. Essa cor é só usada se não for substituída por um componente.", + "errorForeground": "Cor de primeiro plano geral para mensagens de erro. Essa cor é só usada se não for substituída por um componente.", + "descriptionForeground": "Cor de primeiro plano para a descrição do texto provendo informação adicional, por exemplo para uma etiqueta.", + "focusBorder": "Cor geral da borda para elementos focalizados. Essa cor é usada somente se não for substituída por um componente.", + "contrastBorder": "Uma borda extra em torno de elementos para separá-los dos outros de maior contraste.", + "activeContrastBorder": "Uma borda extra em torno de elementos ativos para separá-los dos outros de maior contraste.", + "selectionBackground": "A cor de fundo das seleções de texto na área de trabalho (por exemplo, para campos de entrada ou áreas de texto). Note que isto não se aplica a seleções dentro do editor.", + "textSeparatorForeground": "Cor para separadores de texto.", + "textLinkForeground": "Cor de primeiro plano para links no texto.", + "textLinkActiveForeground": "Cor de primeiro plano para links ativos no texto.", + "textPreformatForeground": "Cor de primeiro plano para segmentos de texto pré-formatados.", + "textBlockQuoteBackground": "Cor de fundo para blocos de citações no texto.", + "textBlockQuoteBorder": "Cor da borda para blocos de citações no texto.", + "textCodeBlockBackground": "Cor de fundo para blocos de código no texto.", + "widgetShadow": "Cor de sombra ferramentas como localizar/substituir dentro do editor.", + "inputBoxBackground": "Cor de fundo da caixa de entrada.", + "inputBoxForeground": "Cor de primeiro plano da caixa de entrada.", + "inputBoxBorder": "Borda da caixa de entrada.", + "inputBoxActiveOptionBorder": "Cor da borda das opções ativas em campos de entrada.", + "inputPlaceholderForeground": "Cor de primeiro plano da caixa de entrada para o texto de espaço reservado.", + "inputValidationInfoBackground": "Cor de fundo de validação de entrada para a severidade de informações.", + "inputValidationInfoBorder": "Cor da borda de validação de entrada para a severidade de informações.", + "inputValidationWarningBackground": "Cor de fundo de validação de entrada para avisos.", + "inputValidationWarningBorder": "Cor da borda de validação para a severidade de avisos.", + "inputValidationErrorBackground": "Cor de fundo de validação de entrada para a severidade do erro.", + "inputValidationErrorBorder": "Cor da borda de validação de entrada para a severidade do erro.", + "dropdownBackground": "Cor de fundo do menu suspenso.", + "dropdownForeground": "Cor de primeiro plano do menu suspenso.", + "dropdownBorder": "Borda do menu suspenso.", + "listFocusBackground": "Cor de fundo para o item focalizado de Lista/árvore quando a lista/árvore está ativa. Uma árvore/lista de ativa tem o foco do teclado, uma inativa não.", + "listFocusForeground": "Cor de fundo da Lista/árvore para o item focalizado quando a lista/árvore está ativa. Uma árvore/lista ativa tem o foco do teclado, uma inativa não.", + "listActiveSelectionBackground": "Cor de fundo para o item selecionado de Lista/árvore quando a lista/árvore está ativa. Uma lista/árvore ativa tem o foco do teclado, uma inativa não.", + "listActiveSelectionForeground": "Cor de primeiro plano para o item selecionado de Lista/árvore quando a lista/árvore está ativa. Uma lista/árvore ativa tem o foco do teclado, uma inativa não.", + "listInactiveSelectionBackground": "Cor de fundo para o item selecionado de Lista/árvore quando a lista/árvore está inativa. Uma lista/árvore ativa tem o foco do teclado, uma inativa não.", + "listInactiveSelectionForeground": "Cor de primeiro plano para Lista/árvore para o item selecionado quando a lista/árvore está inativa. Uma árvore/lista ativa tem o foco do teclado, um inativo não.", + "listHoverBackground": "Cor de fundo de Lista/árvore quando pairando sobre itens usando o mouse.", + "listHoverForeground": "Primeiro plano da Lista/Árvoce quando passar sobre itens usando o mouse.", + "listDropBackground": "Cor de fundo ao arrastar e soltar de Lista/árvore quando movendo itens usando o mouse.", + "highlight": "Cor de primeiro plano de Lista/árvore de destaques de correspondências ao pesquisar na árvore/lista.", + "pickerGroupForeground": "Seletor rápido de cor para rótulos de agrupamento.", + "pickerGroupBorder": "Seletor rápido de cor para bordas de agrupamentos.", + "buttonForeground": "Cor de primeiro plano do botão.", + "buttonBackground": "Cor de fundo do botão.", + "buttonHoverBackground": "Cor de fundo de botão quando flutuar sobre ele.", + "badgeBackground": "Cor de fundo do distintivo. Distintivos são rótulos de pequenas informações, por exemplo, para a contagem de resultados de pesquisa.", + "badgeForeground": "Cor de primeiro plano do distintivo. Distintivos são rótulos de pequenas informações, por exemplo, para a contagem de resultados de pesquisa.", + "scrollbarShadow": "Sombra da barra de rolagem para indicar que a visualização está sendo rolada.", + "scrollbarSliderBackground": "Cor de fundo da barra de rolagem.", + "scrollbarSliderHoverBackground": "Cor de fundo da barra de rolagem quando o cursor do mouse estiver sobre ela.", + "scrollbarSliderActiveBackground": "Cor de fundo da barra de rolagem quando ativa.", + "progressBarBackground": "Cor de fundo da barra de progresso que pode ser mostrada em operações de execução demorada.", + "editorBackground": "Cor de plano de fundo do editor.", + "editorForeground": "Cor de primeiro plano padrão do editor.", + "editorWidgetBackground": "Cor de plano de fundo das ferramentas de edição, como pesquisar/substituir.", + "editorWidgetBorder": "Cor da borda das ferramentas do editor. A cor é usada somente se a ferramenta escolhe ter uma borda e a cor não é substituída por uma ferramenta.", + "editorSelectionBackground": "Cor de seleção do editor.", + "editorSelectionForeground": "Cor do texto selecionado para alto contraste.", + "editorInactiveSelection": "Cor de seleção em um editor inativo.", + "editorSelectionHighlight": "Cor de regiões com o mesmo conteúdo da seleção.", + "editorFindMatch": "Cor da correspondência de pesquisa atual.", + "findMatchHighlight": "Cor dos outros resultados de pesquisa.", + "findRangeHighlight": "Cor da faixa que limita a pesquisa.", + "hoverHighlight": "Realçar abaixo da palavra onde é mostrado item flutuante", + "hoverBackground": "Cor de fundo para o item flutuante do editor", + "hoverBorder": "Cor da borda para o item flutuante do editor.", + "activeLinkForeground": "Cor dos links ativos.", + "diffEditorInserted": "Cor de fundo para texto que foi inserido.", + "diffEditorRemoved": "Cor de fundo para texto que foi removido.", + "diffEditorInsertedOutline": "Cor de contorno para o texto que foi inserido.", + "diffEditorRemovedOutline": "Cor de contorno para o texto que foi removido.", + "mergeCurrentHeaderBackground": "Cor de fundo de cabeçalho atual em conflito de mesclagem em linha.", + "mergeCurrentContentBackground": "Cor de fundo de conteúdo atual em conflito de mesclagem em linha.", + "mergeIncomingHeaderBackground": "Cor de fundo de cabeçalho de entrada em conflito de mesclagem em linha.", + "mergeIncomingContentBackground": "Cor de fundo de conteúdo de entrada em conflito de mesclagem em linha.", + "mergeCommonHeaderBackground": "Ancestral comum da cor de fundo do cabeçalho em conflitos de mesclagem inline.", + "mergeCommonContentBackground": "Ancestral comum da cor de fundo do conteúdo em conflitos de mesclagem inline. ", + "mergeBorder": "Cor da borda dos cabeçalhos e separadores estão em conflito de mesclagem em linha.", + "overviewRulerCurrentContentForeground": "Cor de fundo de régua de visuaização atual em conflito de mesclagem em linha.", + "overviewRulerIncomingContentForeground": "Cor de fundo de régua de visuaização de entrada em conflito de mesclagem em linha.", + "overviewRulerCommonContentForeground": "Ancestral comum da cor da régua de visão geral para conflitos de mesclagem inline.", + "overviewRulerFindMatchForeground": "Visão geral da cor do marcador da régua para buscas correspondentes.", + "overviewRulerSelectionHighlightForeground": "Visão geral da cor do marcador da régua para a seleção de destaques" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/ptb/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..bd884467e6 --- /dev/null +++ b/i18n/ptb/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "Área de Trabalho de Código", + "untitledWorkspace": "Sem título (espaço de trabalho)", + "workspaceNameVerbose": "{0} (Espaço de trabalho)", + "workspaceName": "{0} (Espaço de trabalho) " +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/ptb/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..05533d791f --- /dev/null +++ b/i18n/ptb/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Sobrescrevendo extensão {0} por {1}.", + "extensionUnderDevelopment": "Carregando extensão de desenvolvimento em {0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/ptb/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..f3c298a017 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Fechar", + "cancel": "Cancelar", + "ok": "OK" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/ptb/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..4b4c13ae4d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "Não apresentando {0} erros e avisos a mais." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/ptb/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..4990575f1c --- /dev/null +++ b/i18n/ptb/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "Extensão '{1}' falhou ao ativar. Motivo: dependência desconhecida '{0}'.", + "failedDep1": "Extensão '{1}' falhou ao ativar. Motivo: a dependência '{0}' falhou ao ativar.", + "failedDep2": "Extensão '{0}' falhou ao ativar. Motivo: mais de 10 níveis de dependências (provavelmente um laço de dependência).", + "activationError": "Ativação da extensão `{0}` falhou: {1}." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/ptb/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/ptb/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..3ba82c98cd --- /dev/null +++ b/i18n/ptb/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "Nenhuma visualização de árvore com id '{0}' registrado.", + "treeItem.notFound": "Nenhum item de árvore com id '{0}' encontrado.", + "treeView.duplicateElement": "Elemento {0} já está registrado" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/ptb/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..6686c6fd07 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "Configurar Idioma", + "displayLanguage": "Define o idioma de exibição do VSCode.", + "doc": "Veja {0} para obter uma lista dos idiomas suportados.", + "restart": "Modificar o valor requer reinicialização do VSCode.", + "fail.createSettings": "Não foi possível criar '{0}' ({1}).", + "JsonSchema.locale": "O idioma da interface do usuário a ser usada." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/ptb/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..f566d21a4c --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Abrir Pasta...", + "openFileFolder": "Abrir...", + "addFolderToWorkspace": "Adicionar pasta ao espaço de trabalho...", + "add": "Adicionar", + "addFolderToWorkspaceTitle": "Adicionar pasta ao espaço de trabalho", + "removeFolderFromWorkspace": "Remover pasta da área de trabalho" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/ptb/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..ad20de39d0 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "Alternar Visibilidade da Barra de Atividades", + "view": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/ptb/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..1d0464e0f0 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "Alternar Layout Vertical/Horizontal do Grupo de Editor", + "horizontalLayout": "Layout do Grupo de Editor Horizontal", + "verticalLayout": "Layout do Grupo de Editor Vertical", + "view": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/ptb/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..64b662ea7d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Alternar Localização da Barra Lateral", + "view": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/ptb/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..1bc386b9c9 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "Alternar Visibilidade da Barra Lateral", + "view": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/ptb/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..479a114f22 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "Alternar Visibilidade da Barra de Status", + "view": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/ptb/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..c52822c39b --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "Alternar Modo Zen", + "view": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/ptb/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..72d3f7e5c2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Abrir Pasta...", + "openFileFolder": "Abrir...", + "addFolderToWorkspace": "Adicionar pasta ao espaço de trabalho...", + "add": "&& Adicionar", + "addFolderToWorkspaceTitle": "Adicionar pasta ao espaço de trabalho", + "newWorkspace": "Novo espaço de trabalho...", + "select": "&&Selecionar", + "selectWorkspace": "Selecionar pastas para espaço de trabalho", + "removeFolderFromWorkspace": "Remover pasta da área de trabalho", + "saveWorkspaceAsAction": "Salvar o espaço de trabalho como...", + "saveEmptyWorkspaceNotSupported": "Por favor, abra um espaço de trabalho antes de salvar.", + "save": "&&Salvar", + "saveWorkspace": "Salvar o espaço de trabalho", + "openWorkspaceAction": "Abrir o Espaço de Trabalho...", + "openWorkspaceConfigFile": "Abrir o Arquivo de Configuração do Espaço de Trabalho" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..2436c8afa9 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "Remover da Barra de Atividades", + "keepInActivityBar": "Manter na Barra de Atividades", + "titleKeybinding": "{0} ({1})", + "additionalViews": "Visualizações Adicionais", + "numberBadge": "{0} ({1})", + "manageExtension": "Gerenciar Extensão", + "toggle": "Alternar Visualização Fixa" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..22d98f67c1 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "Ocultar a Barra de Atividades", + "activityBarAriaLabel": "Chave do Modo de exibição Ativo", + "globalActions": "Ações globais" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..cc1ec57467 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "{0} ações ", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..8767815c9c --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "Visualizador binário" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..967e454199 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Editor de Texto", + "textDiffEditor": "Editor de Diferentes Textos", + "binaryDiffEditor": "Editor de Diferença Binária", + "sideBySideEditor": "Editor Lado a lado", + "groupOnePicker": "Mostrar editores no primeiro grupo", + "groupTwoPicker": "Mostrar editores no segundo grupo", + "groupThreePicker": "Mostrar editores no terceiro grupo", + "allEditorsPicker": "Mostrar todos editores abertos", + "view": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..b9cda5019a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "Dividir editor", + "joinTwoGroups": "Juntar editores de dois grupos", + "navigateEditorGroups": "Navegar entre grupos de editores", + "focusActiveEditorGroup": "Focalizar grupo de editores ativo", + "focusFirstEditorGroup": "Focalizar o primeiro grupo de editores", + "focusSecondEditorGroup": "Focalizar o segundo grupo de editores", + "focusThirdEditorGroup": "Focalizar o terceiro grupo de editores", + "focusPreviousGroup": "Focalizar grupo anterior", + "focusNextGroup": "Focalizar próximo grupo", + "openToSide": "Aberto para o lado", + "closeEditor": "Fechar editor", + "revertAndCloseActiveEditor": "Reverter e fechar editor", + "closeEditorsToTheLeft": "Fechar editores à esquerda ", + "closeEditorsToTheRight": "Fechar editores à direita", + "closeAllEditors": "Fechar todos editores", + "closeUnmodifiedEditors": "Fechar os Editores Não Modificados no Grupo", + "closeEditorsInOtherGroups": "Fechar editores nos outros grupos", + "closeOtherEditorsInGroup": "Fechar outros editores", + "closeEditorsInGroup": "Fechar todos editores no grupo", + "moveActiveGroupLeft": "Mover grupo de editores para esquerda", + "moveActiveGroupRight": "Mover grupo de editores para direita", + "minimizeOtherEditorGroups": "Minimizar outros grupos de editores", + "evenEditorGroups": "Igualar larguras de grupos de editores", + "maximizeEditor": "Maximizar grupo de editor e ocultar barra lateral", + "keepEditor": "Manter editor", + "openNextEditor": "Abrir próximo editor", + "openPreviousEditor": "Abrir editor anterior", + "nextEditorInGroup": "Abrir próximo editor no grupo", + "openPreviousEditorInGroup": "Abrir editor anterior no grupo", + "navigateNext": "Avançar", + "navigatePrevious": "Voltar", + "reopenClosedEditor": "Reabrir Editor Fechado", + "clearRecentFiles": "Limpar Abertos Recentemente", + "showEditorsInFirstGroup": "Mostrar editores no primeiro grupo", + "showEditorsInSecondGroup": "Mostrar editores no segundo grupo", + "showEditorsInThirdGroup": "Mostrar editores no terceiro grupo", + "showEditorsInGroup": "Mostrar editores no grupo", + "showAllEditors": "Mostrar todos editores", + "openPreviousRecentlyUsedEditorInGroup": "Abrir o Editor Anterior Recentemente Usado no Grupo", + "openNextRecentlyUsedEditorInGroup": "Abrir o Próximo Editor Recentemente Usado no Grupo", + "navigateEditorHistoryByInput": "Abrir o Editor Anterior do Histórico", + "openNextRecentlyUsedEditor": "Abrir o Próximo Editor Recentemente Utilizado", + "openPreviousRecentlyUsedEditor": "Abrir o Editor Anterior Recentemente Utilizado", + "clearEditorHistory": "Limpar Histórico do Editor", + "focusLastEditorInStack": "Abrir Último Editor do Grupo", + "moveEditorLeft": "Mover o Editor para a Esquerda", + "moveEditorRight": "Mover o Editor para a Direita", + "moveEditorToPreviousGroup": "Mover o Editor para o Grupo Anterior", + "moveEditorToNextGroup": "Mover o Editor para o Próximo Grupo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..40952b6db2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "Mover o editor ativo por guias ou grupos", + "editorCommand.activeEditorMove.arg.name": "Argumento de movimento do editor ativo", + "editorCommand.activeEditorMove.arg.description": "Propriedades do argumento: \n\t\t\t\t\t\t- 'para': sequência de valor fornecendo para onde mover.\n\t\t\t\t\t\t- 'por': sequência de valor, fornecendo a unidade para o movimento. Por guia ou por grupo.\n\t\t\t\t\t\t- 'valor': valor numérico, fornecendo quantas posições ou uma posição absoluta para mover.\n\t\t\t\t\t", + "commandDeprecated": "Comando **{0}** foi removido. Você pode usar **{1}** em vez disso", + "openKeybindings": "Configurar os atalhos de teclado" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..70768228c0 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "Esquerda", + "groupTwoVertical": "Centro", + "groupThreeVertical": "Direita", + "groupOneHorizontal": "Topo", + "groupTwoHorizontal": "Centro", + "groupThreeHorizontal": "Rodapé", + "editorOpenError": "Não foi possível abrir '{0}': {1}." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..ba273b5f9a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, seletor de grupo editor", + "groupLabel": "Grupo: {0}", + "noResultsFoundInGroup": "Não foi encontrado nennhum editor aberto no grupo", + "noOpenedEditors": "Lista de editores abertos está atualmente vazia no grupo", + "noResultsFound": "Não foi encontrado editor correspondente aberto", + "noOpenedEditorsAllGroups": "A lista de editores abertos está atualmente vazia" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..687ff97316 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "Ln {0}, {1} Col ({2} selecionado)", + "singleSelection": "Ln {0}, {1} Col", + "multiSelectionRange": "{0} seleções ({1} caracteres selecionados)", + "multiSelection": "{0} seleções", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "Tabulação Move o Foco", + "screenReaderDetected": "Leitor de Tela Detectado", + "screenReaderDetectedExtra": "Se você não estiver usando um leitor de tela, por favor altere a configuração `editor.accessibilitySupport` para \"desligado\".", + "disableTabMode": "Desativar o modo de acessibilidade", + "gotoLine": "Ir para linha", + "indentation": "Indentação", + "selectEncoding": "Selecionar a codificação", + "selectEOL": "Selecionar a sequência de fim de linha", + "selectLanguageMode": "Selecionar modo de idioma", + "fileInfo": "Informações do arquivo", + "spacesSize": "Espaços: {0}", + "tabSize": "Tamanho de Tabulação: {0}", + "showLanguageExtensions": "Pesquisar extensões na loja para '{0}'...", + "changeMode": "Alterar o modo de linguagem", + "noEditor": "Nenhum editor de texto ativo neste momento", + "languageDescription": "({0}) - linguagem configurada", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "linguagens (identificador)", + "configureModeSettings": "Configurar '{0}' configurações baseadas em linguagem...", + "configureAssociationsExt": "Configurar a associação de arquivo para '{0}'...", + "autoDetect": "Detecção automática", + "pickLanguage": "Selecionar o modo do idioma", + "currentAssociation": "Associação atual", + "pickLanguageToConfigure": "Selecionar o modo de linguagem para associar a '{0}'", + "changeIndentation": "Alterar a indentação", + "noWritableCodeEditor": "O editor de código ativo é somente leitura.", + "indentView": "alterar visualização", + "indentConvert": "converter arquivo", + "pickAction": "Selecionar ação", + "changeEndOfLine": "Alterar sequência de final de linha", + "pickEndOfLine": "Selecionar sequência de final de linha", + "changeEncoding": "Alterar a codificação do arquivo", + "noFileEditor": "Nenhum arquivo ativo neste momento", + "saveWithEncoding": "Salvar com codificação", + "reopenWithEncoding": "Reabrir com codificação", + "guessedEncoding": "Adivinhado a partir do conteúdo", + "pickEncodingForReopen": "Selecione a codificaçãodo arquivo para reabrir o arquivo.", + "pickEncodingForSave": "Selecione a codificação do arquivo para Salvar Com" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..f30587e9ff --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "Ações de tablulação" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..be3594f978 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "Editor de Diferentes Textos", + "readonlyEditorWithInputAriaLabel": "{0}. Editor de comparação de texto somente leitura.", + "readonlyEditorAriaLabel": "Editor de comparação de texto somente leitura.", + "editableEditorWithInputAriaLabel": "{0}. Editor de comparação de arquivos texto.", + "editableEditorAriaLabel": "Editor de comparação de arquivos texto.", + "navigate.next.label": "Próxima Alteração", + "navigate.prev.label": "Alteração Anterior", + "inlineDiffLabel": "Alternar para exibição embutida", + "sideBySideDiffLabel": "Alternar para exibição lado a lado" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..fabfb074a0 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0}, Grupo {1}." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..74ad5190e7 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Editor de texto", + "readonlyEditorWithInputAriaLabel": "{0}. Editor de texto somente leitura.", + "readonlyEditorAriaLabel": "Editor de texto somente leitura.", + "untitledFileEditorWithInputAriaLabel": "{0}. Editor de texto de arquivo sem nome.", + "untitledFileEditorAriaLabel": "Editor de texto de arquivo sem nome." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..8b181a9617 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Fechar", + "closeOthers": "Fechar Outros", + "closeRight": "Fechar à direita", + "closeAll": "Fechar todos", + "closeAllUnmodified": "Fechar Não Modificados", + "keepOpen": "Manter aberto", + "showOpenedEditors": "Mostrar editores abertos", + "araLabelEditorActions": "Ações de editor" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..db81897bc8 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "Fechar Painel", + "togglePanel": "Alternar Painel", + "focusPanel": "Foco no Painel", + "toggleMaximizedPanel": "Alternar Painel Maximizado", + "maximizePanel": "Maximizar Tamanho do Painel", + "minimizePanel": "Restaurar tamanho do Painel", + "view": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..ec25d4be5f --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "Chave do Painel Ativo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..40099ff237 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} (Pressione 'Enter' para confirmar ou 'Esc' para cancelar)", + "inputModeEntry": "Pressione 'Enter' para confirmar o texto digitado ou 'Esc' para cancelar", + "emptyPicks": "Não há entradas a serem escolhidas", + "quickOpenInput": "Digite '?' para obter ajuda sobre as ações que você pode realizar a partir daqui", + "historyMatches": "aberto recentemente", + "noResultsFound1": "Nenhum resultado encontrado", + "canNotRunPlaceholder": "Esse manipulador de abertura rápida não pode ser usado no contexto atual", + "entryAriaLabel": "{0}, recentemente aberto", + "removeFromEditorHistory": "Remover do Histórico", + "pickHistory": "Selecionar uma entrada do editor para remover do histórico" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..9c0ae4a81d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Ir para o Arquivo...", + "quickNavigateNext": "Navegar ao próximo em modo de abertura rápida", + "quickNavigatePrevious": "Navegar ao anterior em modo de abertura rápida", + "quickSelectNext": "Selecionar próximo em modo de abertura rápida", + "quickSelectPrevious": "Selecionar anterior em modo de abertura rápida" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..9c0ae4a81d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Ir para o Arquivo...", + "quickNavigateNext": "Navegar ao próximo em modo de abertura rápida", + "quickNavigatePrevious": "Navegar ao anterior em modo de abertura rápida", + "quickSelectNext": "Selecionar próximo em modo de abertura rápida", + "quickSelectPrevious": "Selecionar anterior em modo de abertura rápida" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..1e13da3f7b --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "Ocultar a barra lateral", + "focusSideBar": "Foco na Barra Lateral", + "viewCategory": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..685caa486f --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "O comando '{0}' não está habilitado e não pode ser executado.", + "manageExtension": "Gerenciar Extensão" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..f292a7cfc8 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[Sem Suporte]", + "devExtensionWindowTitlePrefix": "[Host de Desenvolvimento de Extensão]" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/quickopen.i18n.json b/i18n/ptb/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..2789c0ba6d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "Nenhum resultado encontrado", + "noResultsFound2": "Nenhum resultado encontrado", + "entryAriaLabel": "{0}, comando" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/viewlet.i18n.json b/i18n/ptb/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..66d0140d60 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Recolher tudo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/common/theme.i18n.json b/i18n/ptb/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..f45cff138a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "Cor de fundo da guia ativa. As guias são os recipientes para editores na área do editor. Várias guias podem ser abertas em um grupo de editores. Podem haver vários grupos de editor.", + "tabInactiveBackground": "Cor de fundo da guia inativa. As guias são os recipientes para editores na área do editor. Várias guias podem ser abertas em um grupo de editores. Podem haver vários grupos de editor.", + "tabBorder": "Borda para separar uma guia das outras. As guias são os recipientes para editores na área do editor. Várias guias podem ser abertas em um grupo de editores. Podem haver vários grupos de editor.", + "tabActiveBorder": "Borda para destacar guias ativas. As guias são os recipientes para editores na área do editor. Várias guias podem ser abertas em um grupo de editores. Podem haver vários grupos de editores.", + "tabActiveUnfocusedBorder": "Borda para destacar guias ativas em um grupo fora de foco. As guias são os recipientes para editores na área do editor. Várias guias podem ser abertas em um grupo de editores. Podem haver vários grupos de editores.", + "tabActiveForeground": "Cor de primeiro plano da guia ativa em um grupo ativo. As guias são os recipientes para editores na área do editor. Várias guias podem ser abertas em um grupo de editores. Podem haver vários grupos de editor.", + "tabInactiveForeground": "Cor de primeiro plano da guia inativa em um grupo ativo. As guias são os recipientes para editores na área do editor. Várias guias podem ser abertas em um grupo de editores. Podem haver vários grupos de editor.", + "tabUnfocusedActiveForeground": "Cor de primeiro plano da aba ativa em um grupo fora de foco. As guias são os recipientes para editores na área do editor. Várias guias podem ser abertas em um grupo de editores. Podem haver vários grupos de editores.", + "tabUnfocusedInactiveForeground": "Cor de primeiro plano da aba inativa em um grupo fora de foco. As guias são os recipientes para editores na área do editor. Várias guias podem ser abertas em um grupo de editores. Podem haver vários grupos de editores.", + "editorGroupBackground": "Cor de fundo de um grupo de editor. Grupos de editor são os recipientes dos editores. A cor de fundo é mostrada ao arrastar o editor de grupos ao redor.", + "tabsContainerBackground": "Cor de fundo do cabeçalho do título do grupo de editor quando as guias são habilitadas. Grupos de editor são os recipientes dos editores.", + "tabsContainerBorder": "Cor da borda do cabeçalho do título do grupo de editor quando as guias estão habilitadas. Grupos de editor são os recipientes dos editores.", + "editorGroupHeaderBackground": "Cor de fundo do título do cabeçalho do grupo de editor quando as guias são desabilitadas. Grupos de editor são os recipientes dos editores.", + "editorGroupBorder": "Cor para separar múltiplos grupos de editor de outro. Grupos de editor são os recipientes dos editores.", + "editorDragAndDropBackground": "Cor de fundo ao arrastar editores. A cor deve ter transparência para que o conteúdo do editor ainda possa ser visto.", + "panelBackground": "Cor de fundo do painel. Os painéis são mostrados abaixo da área do editor e contém visualizações como saída e terminal integrado.", + "panelBorder": "Cor da borda do painel no topo separando do editor. Os painéis são mostrados abaixo da área do editor e contém visualizações como saída e terminal integrado.", + "panelActiveTitleForeground": "Cor do título para o painel ativo. Os painéis são mostrados abaixo da área do editor e contém visualizações como saída e terminal integrado.", + "panelInactiveTitleForeground": "Cor do título para o painel inativo. Os painéis são mostrados abaixo da área do editor e contém visualizações como saída e terminal integrado.", + "panelActiveTitleBorder": "Cor da borda para o título do painel ativo. Os painéis são mostrados abaixo da área do editor e contém visualizações como saída e terminal integrado.", + "statusBarForeground": "Cor de primeiro plano da barra de status quando um espaço de trabalho é aberto. A barra de status é mostrada na parte inferior da janela.", + "statusBarNoFolderForeground": "Cor do primeiro plano da barra de status quando nenhuma pasta está aberta. A barra de status é mostrada na parte inferior da janela.", + "statusBarBackground": "Cor de fundo da barra de status quando um espaço de trabalho é aberto. A barra de status é mostrada na parte inferior da janela.", + "statusBarNoFolderBackground": "Cor de fundo da barra de status quando nenhuma pasta está aberta. A barra de status é mostrada na parte inferior da janela.", + "statusBarBorder": "Cor da borda da barra de status que separa a barra lateral e o editor.A barra de status é mostrada na parte inferior da janela.", + "statusBarNoFolderBorder": "Cor da borda da barra de status separando para a barra lateral e editor quando nenhuma pasta é aberta. A barra de status é mostrada na parte inferior da janela.", + "statusBarItemActiveBackground": "Cor de fundo do item da barra de status quando você clicado. A barra de status é mostrada na parte inferior da janela.", + "statusBarItemHoverBackground": "Cor de fundo do item da barra de status quando estiver passando sobre ele. A barra de status é mostrada na parte inferior da janela.", + "statusBarProminentItemBackground": "Cor de fundo de itens proeminentes da barra de status. Itens proeminentes destacam-se outras entradas da barra de status para indicar a importância. A barra de status é mostrada na parte inferior da janela.", + "statusBarProminentItemHoverBackground": "Cor de fundo dos itens proeminentes de barra de status quando estiver passando sobre eles. Itens proeminentes destacam-se outras entradas de barra de status para indicar a importância. A barra de status é mostrada na parte inferior da janela.", + "activityBarBackground": "Cor de fundo da barra de atividades. Barra de atividade está visível à esquerda ou à direita e permite alternar entre as visualizações da barra lateral.", + "activityBarForeground": "Cor de primeiro plano da barra de atividades (por exemplo, usada para os ícones). A barra de atividades está visível à esquerda ou à direita e permite alternar entre as visualizações da barra lateral.", + "activityBarBorder": "Cor da borda da barra de atividades separando a barra lateral. A barra de atividade é mostrada à esquerda ou à direita e permite alternar entre as visualizações da barra lateral.", + "activityBarDragAndDropBackground": "Cor de feedback de arrastar e soltar para os itens da barra de atividades. A cor deve ter transparência para que as entradas de bar de atividade ainda possam brilhar. A barra de atividade está visível à esquerda ou à direita e permite para alternar entre as visualizações da barra lateral.", + "activityBarBadgeBackground": "Cor de fundo da notificação de atividade. A barra de atividade está visível à esquerda ou à direita e permite alternar entre as visualizações da barra lateral.", + "activityBarBadgeForeground": "Cor de primeiro plano da notificação de atividade. A barra de atividade está visível à esquerda ou à direita e permite alternar entre as visualizações da barra lateral.", + "sideBarBackground": "Cor de fundo da barra lateral. A barra lateral é o recipiente para visualizações como explorador e pesquisa.", + "sideBarForeground": "Cor de primeiro plano da barra lateral. A barra lateral é o recipiente para visualizações como o explorador e a busca.", + "sideBarBorder": "Cor da borda da barra lateral separando o editor. A barra lateral é o recipiente para visualizações como explorador e pesquisa.", + "sideBarTitleForeground": "Cor de primeiro plano do título da barra lateral. A barra lateral é o recipiente para visualizações como explorador e pesquisa.", + "sideBarDragAndDropBackground": "Cor de feedback de arrastar e soltar para as seções da barra lateral. A cor deve ter transparência para que as seções de barra lateral ainda possam se destacar. A barra lateral é o recipiente para visões como explorador e pesquisa.", + "sideBarSectionHeaderBackground": "Cor de fundo do cabeçalho de seção lateral. A barra lateral é o recipiente para visões como explorador e pesquisa.", + "sideBarSectionHeaderForeground": "Cor de primeiro plano do cabeçalho de seção da barra lateral. A barra lateral é o recipiente para visualizações como o explorador e pesquisa.", + "titleBarActiveForeground": "Cor da barra de título do primeiro plano quando a janela está ativa. Observe que essa cor atualmente somente é suportada no macOS.", + "titleBarInactiveForeground": "Cor de primeiro plano da barra de título quando a janela está inativa. Observe que essa cor atualmente somente é suportada no macOS.", + "titleBarActiveBackground": "Cor de fundo da barra de título quando a janela está ativa. Observe que essa cor atualmente somente é suportada no macOS.", + "titleBarInactiveBackground": "Cor de fundo de barra de título quando a janela está inativa. Observe que essa cor é atualmente somente suportada no macOS.", + "titleBarBorder": "Cor da borda da barra de título. Observe que essa cor é atualmente somente suportados no macOS.", + "notificationsForeground": "Cor do primeiro plano de notificações. Notificações deslizam na parte superior da janela.", + "notificationsBackground": "Cor de fundo de notificações. Notificações deslizam na parte superior da janela.", + "notificationsButtonBackground": "Cor de fundo do botão de notificações. Notificações deslizam da parte superior da janela.", + "notificationsButtonHoverBackground": "Cor de fundo do botão de notificações quando passar sobre ele. Notificações deslizam da parte superior da janela. ", + "notificationsButtonForeground": "Cor de primeiro plano do botão de notificações. Notificações deslizam da parte superior da janela. ", + "notificationsInfoBackground": "Cor de fundo da notificação de informações. Notificações deslizam da parte superior da janela. ", + "notificationsInfoForeground": "Cor de primeiro plano das notificações de informação. Notificações deslizam da parte superior da janela. ", + "notificationsWarningBackground": "Cor de fundo das notificações de aviso. Notificações deslizam da parte superior da janela. ", + "notificationsWarningForeground": "Cor de primeiro plano das notificações de aviso. Notificações deslizam da parte superior da janela.", + "notificationsErrorBackground": "Cor de fundo das notificações de erro. Notificações deslizam da parte superior da janela. ", + "notificationsErrorForeground": "Cor de primeiro plano das notificações de erro. Notificações deslizam da parte superior da janela." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/ptb/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..6b65031f8d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "Fechar Editor", + "closeWindow": "Fechar Janela", + "closeWorkspace": "Fechar o espaço de trabalho", + "noWorkspaceOpened": "Não há nenhum espaço de trabalho aberto nessa instância para ser fechado.", + "newWindow": "Nova Janela", + "toggleFullScreen": "Alternar Tela Inteira", + "toggleMenuBar": "Alternar Barra de Menus", + "toggleDevTools": "Alternar Ferramentas do Desenvolvedor", + "zoomIn": "Ampliar", + "zoomOut": "Reduzir", + "zoomReset": "Reinicializar Zoom", + "appPerf": "Desempenho de inicialização", + "reloadWindow": "Recarregar Janela", + "switchWindowPlaceHolder": "Selecionar uma janela para onde alternar", + "current": "Janela Atual", + "close": "Fechar Janela", + "switchWindow": "Alternar a janela...", + "quickSwitchWindow": "Troca Rápida de Janela...", + "workspaces": "espaços de trabalho", + "files": "arquivos", + "openRecentPlaceHolderMac": "Selecione para abrir (segure tecla Cmd para abrir em uma nova janela)", + "openRecentPlaceHolder": "Selecione para abrir (segure tecla Ctrl para abrir em uma nova janela)", + "remove": "Remover os Abertos Recentemente", + "openRecent": "Abrir Recente...", + "quickOpenRecent": "Abertura Rápida de Recente...", + "closeMessages": "Fechar mensagens de notificação", + "reportIssues": "Reportar Problemas", + "reportPerformanceIssue": "Reportar Problema de Desempenho", + "keybindingsReference": "Referência de Atalhos de Teclado", + "openDocumentationUrl": "Documentação", + "openIntroductoryVideosUrl": "Vídeos Introdutórios", + "openTipsAndTricksUrl": "Dicas e truques", + "toggleSharedProcess": "Alternar processo compartilhado", + "navigateLeft": "Navegar para a Visualização à Esquerda", + "navigateRight": "Navegar para a Visualização à Direita", + "navigateUp": "Navegar para a Visualização Acima", + "navigateDown": "Navegar para a Visualização Abaixo", + "increaseViewSize": "Aumentar o Tamanho da Visualização Atual", + "decreaseViewSize": "Diminuir o Tamanho da Visualização Atual" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/ptb/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/electron-browser/crashReporter.i18n.json b/i18n/ptb/src/vs/workbench/electron-browser/crashReporter.i18n.json new file mode 100644 index 0000000000..5570f97ef1 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/electron-browser/crashReporter.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetria", + "telemetry.enableCrashReporting": "Ativar o envio de relatórios de incidentes à Microsoft.\nEsta opção requer reinicialização para ser efetivada." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/ptb/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..4668451eee --- /dev/null +++ b/i18n/ptb/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "O host de extensão não iniciou em 10 segundos, ele pode ser interrompido na primeira linha e precisa de um depurador para continuar.", + "extensionHostProcess.startupFail": "Host de extensão não começou em 10 segundos, isso pode ser um problema.", + "extensionHostProcess.error": "Erro do host de extensão: {0}", + "devTools": "Ferramentas do Desenvolvedor", + "extensionHostProcess.crash": "Host de extensão foi encerrado inesperadamente. Por favor recarregar a janela para recuperar." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/ptb/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..ce391fd6ee --- /dev/null +++ b/i18n/ptb/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "Exibir", + "help": "Ajuda", + "file": "Arquivo", + "workspaces": "Espaços de trabalho", + "developer": "Desenvolvedor", + "showEditorTabs": "Controla se os editores abertos devem ou não serem exibidos em abas.", + "editorTabCloseButton": "Controla a posição dos botões de fechar das abas do editor ou os desabilita quando configurados para 'desligado'.", + "showIcons": "Controla se os editores abertos devem ou não ser exibidos com um ícone. Requer um tema de ícone para ser habilitado. ", + "enablePreview": "Controla se os editores abertos são exibidos como visualização. Editores de visualização são reutilizados até que eles sejam preservados (por exemplo, através de um duplo clique ou edição).", + "enablePreviewFromQuickOpen": "Controla se os editores abertos da Abertura Rápida são exibidos como visualização. Os editores de visualização são reutilizados até serem preservados (por exemplo, através de um duplo clique ou edição).", + "editorOpenPositioning": "Controla onde os editores serão abertos. Escolha 'esquerda' ou 'direita' para abrir os editores à esquerda ou à direita do \neditor ativo. Selecione 'primeiro' ou 'último' para abrir os editores independentemente do atual.", + "revealIfOpen": "Controla se um editor é exibido em qualquer um dos grupos, se aberto. Se desabilitado, um editor será aberto preferencialmente no grupo de editores ativo. Se habilitado, um editor já aberto será exibido no grupo de editores ativo, ao invés de ser aberto novamente. Note que há alguns casos onde esta configuração é ignorada, por exemplo, quando for forçada a abertura de um editor em um grupo específico ou ao lado do grupo atualmente ativo.", + "commandHistory": "Controla o número de comandos recentemente usados mantidos no histórico para a paleta de comandos. Definir como 0 para desativar o histórico de comandos.", + "preserveInput": "Controla se a última entrada digitada na paleta de comandos deve ser restaurada ao abri-la da próxima vez.", + "closeOnFocusLost": "Controla se Abertura Rápida deve fechar automaticamente caso perca o foco.", + "openDefaultSettings": "Controla se a abertura de configurações também abre um editor mostrando todas as configurações padrão.", + "sideBarLocation": "Controla a localização da barra lateral. Ele pode ser exibido à esquerda ou à direita da área de trabalho.", + "statusBarVisibility": "Controla a visibilidade da barra de status na parte inferior da área de trabalho.", + "activityBarVisibility": "Controla a visibilidade da barra de atividades na área de trabalho.", + "closeOnFileDelete": "Controla se os editores que mostram um arquivo devem fechar automaticamente quanto o arquivo é apagado ou renomeado por algum outro processo. Desativar isso manterá o editor aberto como sujo neste evento. Note que apagar do aplicativo sempre fechará o editor e os arquivos sujos nunca fecharão para preservar seus dados.", + "fontAliasing": "Controla o método de identificação de fonte no espaço de trabalho.\n- padrão: Suavização de fonte subpixel. Na maioria dos monitores não-retina isto mostrará o texto mais nítido\n- antialiased: Suaviza a fonte no nível do pixel, em oposição a subpixel. Pode fazer a fonte aparecer mais clara de um modo geral \n- nenhum: Desabilita a suavização de fonte. Texto será mostrado com bordas irregulares", + "workbench.fontAliasing.default": "Suavização de fonte subpixel. Na maioria dos monitores não-retina isto mostrará o texto mais nítido.", + "workbench.fontAliasing.antialiased": "Suavizar a fonte no nível do pixel, em oposição a subpixel. Pode fazer com que a fonte apareça mais clara de uma forma geral.", + "workbench.fontAliasing.none": "Desabilita a suavização de fonte. Texto será mostrado com bordas irregulares.", + "swipeToNavigate": "Navegue entre arquivos abertos usando o deslizamento horizontal de três dedos.", + "workbenchConfigurationTitle": "Área de Trabalho", + "window.openFilesInNewWindow.on": "Arquivos serão abertos em uma nova janela", + "window.openFilesInNewWindow.off": "Arquivos serão abertos em uma nova janela com a pasta de arquivos aberta ou com a última janela ativa.", + "window.openFilesInNewWindow.default": "Os arquivos serão abertos na janela com a pasta de arquivos aberta ou a última janela ativa, a menos que seja aberto através do dock ou do finder (somente macOS)", + "openFilesInNewWindow": "Controla se os arquivos devem ser abertos em uma nova janela\n- padrão: os arquivos serão abertos em uma nova janela com a pasta de arquivos aberta ou na última janela ativa, a menos que seja aberta através do dock ou do finder (apenas macOS)\n- ligado: os arquivos serão abertos em uma nova janela\n- desligado: os arquivos serão abertos em uma janela com a pasta de arquivos aberta ou a última janela ativa\nNote que ainda podem haver casos em que esta configuração será ignorada (por exemplo, quando estiver usando as opções de linha de comando -new-window ou -reuse-window).", + "window.openFoldersInNewWindow.on": "As pastas serão abertas em uma nova janela", + "window.openFoldersInNewWindow.off": "As pastas substituirão a última janela ativa", + "window.openFoldersInNewWindow.default": "As pastas serão abertas em uma nova janela, a menos que uma pasta seja selecionada dentro do aplicativo (por exemplo, através do menu Arquivo)", + "openFoldersInNewWindow": "Controla se as pastas devem ser abertas em uma nova janela ou substituir a última janela ativa\n- padrão: as pastas serão abertas em uma nova janela, a menos que seja selecionada dentro do aplicativo (por exemplo, através do menu Arquivo)\n- ligado: as pastas serão abertas em uma nova janela\n- desligado: as pastas substituirão a última janela ativa\nNote que ainda podem haver casos em que esta configuração será ignorada (por exemplo, quando estiver usando as opções de linha de comando -new-window ou -reuse-window).", + "window.reopenFolders.all": "Reabrir todas as janelas.", + "window.reopenFolders.folders": "Reabrir todas as pastas. Espaços de trabalho vazios não serão restaurados.", + "window.reopenFolders.one": "Reabrir a última janela ativa.", + "window.reopenFolders.none": "Nunca reabrir uma janela. Sempre começar com uma janela vazia.", + "restoreWindows": "Controla como as janelas serão reabertas após uma reinicialização. Selecione 'nenhum' para sempre iniciar com uma área de trabalho vazia, 'um' para reabrir a última janela que você trabalhou, 'pastas' para reabrir todas as janelas que tinham pastas abertas ou 'todos' para reabrir todas as janelas da sua última sessão.", + "restoreFullscreen": "Controla se uma janela deve ser restaurada em modo de tela cheia se ela foi finalizada em modo de tela cheia.", + "zoomLevel": "Ajusta o nível de zoom da janela. O tamanho original é 0 e cada aumento (por exemplo, 1) ou redução (por exemplo, -1) representa um zoom 20% maior ou menor. Você também pode digitar decimais para ajustar o nível de zoom com uma granularidade mais fina.", + "title": "Controla o título de janela baseado no editor do ativo. Variáveis são substituídas com base no contexto:\n${activeEditorShort}: por exemplo, MyFile.txt \n${activeEditorMedium}: por exemplo, myFolder/myFile.txt \n${activeEditorLong}: por exemplo, /Users/Development/myProject/myFolder/myFile.txt \n${folderName}: por exemplo myFolder \n${folderPath}: por exemplo, /Users/Development/myFolder \n${rootName}: por exemplo, myFolder1, myFolder2, myFolder3 \n${rootPath}: por exemplo, /Users/Development/myWorkspace \n${appName}: por exemplo, VS Code \n${dirty}: um indicador que mostra se o editor ativo está modificado\n${separator}: um separador condicional (\"-\") que é mostrado apenas quando cercado por variáveis com valores", + "window.newWindowDimensions.default": "Abrir novas janelas no centro da tela.", + "window.newWindowDimensions.inherit": "Abrir novas janelas com a mesma dimensão da última janela ativa.", + "window.newWindowDimensions.maximized": "Abrir novas janelas maximizadas.", + "window.newWindowDimensions.fullscreen": "Abrir novas janelas em modo de tela cheia.", + "newWindowDimensions": "Controla as dimensões ao abrir uma nova janela quando pelo menos uma janela já está aberta. Por padrão, uma nova janela será aberta no centro da tela com pequena dimensão. Quando definido como 'inherit', a janela vai ter as mesmas dimensões que a última janela que estava ativa. Quando definido como 'maximized', a janela abrirá maximizada e em tela cheia se configurado para 'fullscreen'. Observe que essa configuração não tem um impacto sobre a primeira janela que é aberta. A primeira janela sempre irá restaurar o tamanho e a localização como você deixou antes de fechar.", + "closeWhenEmpty": "Controla se o fechamento do último editor também deve fechar a janela. Essa configuração só se aplica para as janelas que não mostram pastas.", + "window.menuBarVisibility.default": "O menu está oculto apenas em modo de tela cheia.", + "window.menuBarVisibility.visible": "O menu está sempre visivel mesmo quando em modo de tela cheia.", + "window.menuBarVisibility.toggle": "O menu está oculto, mas pode ser mostrado através da tecla Alt.", + "window.menuBarVisibility.hidden": "O menu está sempre oculto.", + "menuBarVisibility": "Controla a visibilidade da barra de menu. Uma configuração 'alternar' significa que a barra de menus está oculta e pressionar a tecla Alt irá mostrá-la. Por padrão, a barra de menu será visível, a menos que a janela esteja em modo de tela cheia.", + "enableMenuBarMnemonics": "Se habilitado, os menus principais podem ser abertos através de atalhos de tecla Alt. Desativar mnemônicos permite vincular esses atalhos de tecla Alt para comandos do editor.", + "autoDetectHighContrast": "Se habilitado, irá mudar automaticamente para o tema de alto contraste se o Windows estiver utilizando um tema de alto contraste, e para o tema escuro ao mudar de um tema de alto contraste do Windows.", + "titleBarStyle": "Ajusta a aparência da barra de título da janela. As alterações exigem um reinício completo.", + "window.nativeTabs": "Habilita as abas da janela do macOS Sierra. Note que as alterações exigem um reinício completo e que as abas nativas desabilitarão um estilo de barra de título customizado, se configurado.", + "windowConfigurationTitle": "Janela", + "zenModeConfigurationTitle": "Modo Zen", + "zenMode.fullScreen": "Controla se a ativação do modo Zen também coloca o espaço de trabalho em modo de tela cheia.", + "zenMode.hideTabs": "Controla se a ativação do modo Zen também oculta as abas do espaço de trabalho.", + "zenMode.hideStatusBar": "Controla se a ativação do modo Zen também oculta a barra de status no rodapé do espaço de trabalho.", + "zenMode.hideActivityBar": "Controla se a ativação do modo Zen também oculta a barra de atividades à esquerda do espaço de trabalho.", + "zenMode.restore": "Controla se uma janela deve ser restaurada para o modo zen se ela foi finalizada no modo zen." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/electron-browser/main.i18n.json b/i18n/ptb/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..3a84043f93 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "Falha ao carregar o arquivo necessário. Você não está mais conectado à Internet ou o servidor que você está conectado está offline. Atualize o navegador e tente novamente.", + "loaderErrorNative": "Falha ao carregar um arquivo necessário. Reinicie o aplicativo para tentar novamente. Detalhes: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/ptb/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..e8e76152e2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "Não é recomendado executar Code como 'root'." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/electron-browser/window.i18n.json b/i18n/ptb/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..ff91a6f231 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "Desfazer", + "redo": "Refazer", + "cut": "Recortar", + "copy": "Copiar", + "paste": "Colar", + "selectAll": "Selecionar Tudo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/ptb/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..d42fa13dcb --- /dev/null +++ b/i18n/ptb/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "Desenvolvedor", + "file": "Arquivo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/ptb/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..508cff782f --- /dev/null +++ b/i18n/ptb/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "Caminho {0} não aponta para um executor de testes com extensão válida." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/ptb/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..f40572ecff --- /dev/null +++ b/i18n/ptb/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Falha ao analisar {0}: {1}.", + "fileReadFail": "Não foi possível ler o arquivo {0}: {1}.", + "jsonsParseFail": "Falha ao analisar {0} ou {1}: {2}.", + "missingNLSKey": "Não foi possível encontrar a mensagem para a chave {0}." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..0726636a44 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "Instalar o comando '{0}' em PATH", + "not available": "Este comando não está disponível", + "successIn": "Comando shell '{0}' instalado com sucesso em PATH.", + "warnEscalation": "O código solicitará com 'osascript' pelos privilégios de Administrador para instalar o comando shell.", + "ok": "OK", + "cantCreateBinFolder": "Não é possível criar '/usr/local/bin'.", + "cancel2": "Cancelar", + "aborted": "Abortado", + "uninstall": "Desinstalar o comando '{0}' de PATH", + "successFrom": "Comando shell '{0}' desinstalado com sucesso de PATH.", + "shellCommand": "Comando shell" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..c1361fa1fa --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "Modificando a configuração 'editor.accessibilitySupport' para 'on'.", + "openingDocs": "Abrindo a página de documentação de Acessibilidade do VS Code.", + "introMsg": "Obrigado por testar a opção de acessibilidade do VS Code.", + "status": "Status", + "changeConfigToOnMac": "Para configurar o editor para ser permanentemente otimizado para uso com um leitor de tela pressione Command+E agora.", + "changeConfigToOnWinLinux": "Para configurar o editor para ser permanentemente otimizado para uso com um leitor de tela pressione Control+E agora.", + "auto_unknown": "O editor está configurado para usar as APIs de plataforma para detectar quando está conectado a um leitor de tela, mas o tempo de execução atual não oferece suporte a isso.", + "auto_on": "O editor detectou automaticamente que foi anexado um leitor de tela.", + "auto_off": "O editor está configurado para detectar automaticamente quando um leitor de tela é anexado, o que não é o caso neste momento.", + "configuredOn": "O editor está configurado para ser permanentemente otimizado para uso com um leitor de tela - você pode mudar isso editando a configuração 'editor.accessibilitySupport'.", + "configuredOff": "O editor está configurado para nunca ser otimizado para uso com um Leitor de Tela.", + "tabFocusModeOnMsg": "Pressionando Tab no editor corrente irá mover o foco para o próximo elemento focável. Mude este comportamento ao pressionar {0}.", + "tabFocusModeOnMsgNoKb": "Pressionando Tab no editor corrente irá mover o foco para o próximo elemento focável. O comando {0} não pode ser ativado atualmente por uma tecla.", + "tabFocusModeOffMsg": "Pressionando Tab no editor atual irá inserir um caractere Tab. Mude este comportamente ao pressionar {0}.", + "tabFocusModeOffMsgNoKb": "Pressionando Tab no editor atual irá inserir um caractere Tab. O comando {0} não pode ser ativado atualmente por uma tecla.", + "openDocMac": "Pressione Command+H agora para abrir uma janela do navegador com mais informação do VS Code relacionada à Acessibilidade.", + "openDocWinLinux": "Pressione Ctrl+H para abrir uma janela do navegador com mais informação do VS Code relacionada à acessibilidade.", + "outroMsg": "Você pode ignorar esta dica de ferramenta e retornar ao editor pressionando Escape ou Shift+Escape.", + "ShowAccessibilityHelpAction": "Mostrar ajuda de acessibilidade" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..81047ea0e5 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "Desenvolvedor: Inspecionar Mapeamentos de Chave" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..bc204f7f80 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Desenvolvedor: Inspecionar escopos TM", + "inspectTMScopesWidget.loading": "Carregando..." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..9e710fb32a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Erros parseando {0}: {1}", + "schema.openBracket": "O colchete de abertura de caractere ou sequência de caracteres.", + "schema.closeBracket": "O colchete de fechamento de caractere ou sequência de caracteres.", + "schema.comments": "Define o símbolo dos comentários", + "schema.blockComments": "Define como comentários em bloco são marcados.", + "schema.blockComment.begin": "A sequência de caracteres que inicia um comentário em bloco.", + "schema.blockComment.end": "A sequência de caracteres que termina um comentário de bloco.", + "schema.lineComment": "A sequência de caracteres que inicia um comentário de linha.", + "schema.brackets": "Define os símbolos de colchetes que aumentam ou diminuem a indentação.", + "schema.autoClosingPairs": "Define os pares de colchetes. Quando é introduzido um colchete de abertura, o colchete de fechamento é inserido automaticamente.", + "schema.autoClosingPairs.notIn": "Define uma lista de escopos onde os auto pares são desativados.", + "schema.surroundingPairs": "Define os pares de colchetes que podem ser usados para cercar uma seqüência selecionada.", + "schema.wordPattern": "A definição da palavra para a linguagem.", + "schema.wordPattern.pattern": "O padrão RegExp usado para coincidir com as palavras.", + "schema.wordPattern.flags": "Os sinalizadores RegExp usados para coincidir com as palavras.", + "schema.wordPattern.flags.errorMessage": "Deve corresponder ao padrão `/^([gimuy]+)$/`.", + "schema.indentationRules": "Configurações de recuo da linguagem.", + "schema.indentationRules.increaseIndentPattern": "Se uma linha corresponder a esse padrão, então todas as linhas depois dela devem ser recuadas uma vez (até que outra regra corresponda).", + "schema.indentationRules.increaseIndentPattern.pattern": "O padrão RegExp para increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.flags": "Os rótulos RegExp para increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.errorMessage": "Deve corresponder ao padrão `/^([gimuy]+)$/`.", + "schema.indentationRules.decreaseIndentPattern": "Se uma linha corresponder a esse padrão, então todas as linhas após ela devem ser aproximadas uma vez (até que outra regra corresponda).", + "schema.indentationRules.decreaseIndentPattern.pattern": "O padrão RegExp para decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.flags": "Os rótulos RegExp para decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "Deve corresponder ao padrão `/^([gimuy]+)$/`.", + "schema.indentationRules.indentNextLinePattern": "Se uma linha corresponder a esse padrão, então **apenas a próxima linha** após ela deve ser recuada uma vez.", + "schema.indentationRules.indentNextLinePattern.pattern": "O padrão RegExp para indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.flags": "Os rótulos RegExp para indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.errorMessage": "Deve corresponder ao padrão `/^([gimuy]+)$/`.", + "schema.indentationRules.unIndentedLinePattern": "Se uma linha corresponder a esse padrão, então seu recuo não deve ser mudado e ela não deve ser avaliada contra as outras regras.", + "schema.indentationRules.unIndentedLinePattern.pattern": "O padrão RegExp para unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.flags": "Os rótulos RegExp para unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "Deve corresponder ao padrão `/^([gimuy]+)$/`." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..bc204f7f80 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Desenvolvedor: Inspecionar escopos TM", + "inspectTMScopesWidget.loading": "Carregando..." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..7806f5a9a0 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "Visualização: Ativar/Desativar Minimapa" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..b4d3418019 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Alternar Modificador de Multi-Cursor" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..cecbbb5ab3 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "Visualização: Ativar/Desativar Caracteres de Controle" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..a1648117b5 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "Visualização: Ativar/Desativar Renderização de Espaços em Branco" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..a76981a28a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "Visualizar: Alternar Quebra de Linha", + "wordWrap.notInDiffEditor": "Não pode alternar quebra de linha em um editor diff.", + "unwrapMinified": "Desabilitar empacotamento para este arquivo", + "wrapMinified": "Habilitar empacotamento para este arquivo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..8a07310dfc --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "OK", + "wordWrapMigration.dontShowAgain": "Não mostrar novamente", + "wordWrapMigration.openSettings": "Abrir configurações", + "wordWrapMigration.prompt": "A configuração `editor.wrappingColumn` foi descontinuada e substituída pela configuração `editor.wordWrap`" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..8eb37f39c7 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "Parar quando a expressão for avaliada como true. 'Enter' para aceitar, 'esc' para cancelar.", + "breakpointWidgetAriaLabel": "O programa só vai parar aqui se esta condição for verdadeira. Pressione Enter para aceitar ou Escape para cancelar.", + "breakpointWidgetHitCountPlaceholder": "Parar quando contagem de ocorrências condição for alcançada. 'Enter' para aceitar, 'esc' para cancelar.", + "breakpointWidgetHitCountAriaLabel": "O programa só vai parar aqui, se a contagem de ocorrências for alcançada. Pressione Enter para aceitar ou Escape para cancelar.", + "expression": "Expressão", + "hitCount": "Contagem de ocorrências" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..7cae082aed --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "Sem configurações", + "addConfigTo": "Adicione Config ({0})...", + "addConfiguration": "Adicionar Configuração..." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..5aaa4f3f3e --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "Abrir {0}", + "launchJsonNeedsConfigurtion": "Configurar ou corrigir 'launch.json'", + "noFolderDebugConfig": "Primeiro abra uma pasta para fazer uma configuração de depuração avançada.", + "startDebug": "Iniciar Depuração", + "startWithoutDebugging": "Iniciar Sem Depuração", + "selectAndStartDebugging": "Selecionar e Iniciar a Depuração", + "restartDebug": "Reiniciar", + "reconnectDebug": "Reconectar", + "stepOverDebug": "Pular Sobre", + "stepIntoDebug": "Pular Dentro", + "stepOutDebug": "Pular Fora", + "stopDebug": "Parar", + "disconnectDebug": "Desconectar", + "continueDebug": "Continuar", + "pauseDebug": "Pausa", + "restartFrame": "Reiniciar o Frame", + "removeBreakpoint": "Remover Ponto de Parada", + "removeAllBreakpoints": "Remover Todos os Pontos de Parada", + "enableBreakpoint": "Habilitar ponto de Parada", + "disableBreakpoint": "Desativar Ponto de Parada", + "enableAllBreakpoints": "Habilitar Todos os Pontos de Parada", + "disableAllBreakpoints": "Desabilitar Todos Pontos de Parada", + "activateBreakpoints": "Ativar Pontos de Parada", + "deactivateBreakpoints": "Desativar Pontos de Parada", + "reapplyAllBreakpoints": "Reaplicar Todos os Pontos de Parada", + "addFunctionBreakpoint": "Adicionar Ponto de Parada de Função", + "renameFunctionBreakpoint": "Renomeie o Ponto de Parada de Função", + "addConditionalBreakpoint": "Adicionar Ponto de Parada Condicional...", + "editConditionalBreakpoint": "Editar o Ponto de Parada...", + "setValue": "Definir Valor", + "addWatchExpression": "Adicionar Expressão", + "editWatchExpression": "Editar expressão", + "addToWatchExpressions": "Adicionar ao monitoramento", + "removeWatchExpression": "Remover Expressão", + "removeAllWatchExpressions": "Remover Todas as Expressões", + "clearRepl": "Limpar console", + "debugConsoleAction": "Console do Depurador", + "unreadOutput": "Nova Saída no Console de Depuração", + "debugFocusConsole": "Foco no Console de Depuração", + "focusProcess": "Foco no Processo", + "stepBackDebug": "Passo para trás", + "reverseContinue": "Reverter" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..b2f9dfb52d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "Cor de fundo da barra de ferramentas de depuração." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..d348fec7b7 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "Não é possível resolver o recurso sem uma sessão de depuração" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..62416eedad --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "Depurar: Alternar Ponto de Parada", + "columnBreakpointAction": "Depurar: Ponto de Interrupção de Coluna", + "columnBreakpoint": "Adicionar Ponto de Interrupção de Coluna", + "conditionalBreakpointEditorAction": "Depurar: Adicionar Ponto de Interrupção Condicional...", + "runToCursor": "Executar até o Cursor", + "debugEvaluate": "Depurar: Avaliar", + "debugAddToWatch": "Depurar: Adicionar ao monitoramento", + "showDebugHover": "Mostrar Item Flutuante" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..93c6025b29 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "Ponto de Parada Desativado", + "breakpointUnverifieddHover": "Ponto de Parada Não Verificado", + "breakpointDirtydHover": "Ponto de parada não verificado. O arquivo foi modificado, por favor reinicie a sessão de depuração.", + "breakpointUnsupported": "Pontos de parada condicionais não são suportados por esse tipo de depurador" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..24c164f016 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "depurar {0}", + "debugAriaLabel": "Digite um nome de uma configuração de lançamento para ser executado.", + "noConfigurationsMatching": "Não há configurações de depuração correspondentes", + "noConfigurationsFound": "Configurações de depuração não encontradas. Por favor, crie um arquivo 'launch.json'." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..0fcb3b15bc --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "Cor da borda da ferramenta de exceção.", + "debugExceptionWidgetBackground": "Cor de fundo da ferramenta de exceção.", + "exceptionThrownWithId": "Ocorreu exceção: {0}", + "exceptionThrown": "Ocorreu exceção." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..21f8723e24 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "Clique para seguir (Cmd + clique abre ao lado)", + "fileLink": "Clique para seguir (Cmd + clique abre ao lado)" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..0dd8605382 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "Controla o comportamento do console depuração interna." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..cfd7b0abad --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "não disponível", + "startDebugFirst": "Por favor, inicie uma sessão de depuração para avaliar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..42259b9c79 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "Fonte desconhecida" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..7e398fd48b --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "Visualizar Depurador", + "toggleDebugPanel": "Console do Depurador", + "debug": "Depurar", + "debugPanel": "Console do Depurador", + "variables": "Variáveis", + "watch": "Monitoramento", + "callStack": "Pilha de Chamadas", + "breakpoints": "Pontos de Parada", + "view": "Exibir", + "debugCategory": "Depurar", + "debugCommands": "Configuração do Depurador", + "debugConfigurationTitle": "Depurar", + "allowBreakpointsEverywhere": "Permite definir um ponto de interrupção em qualquer arquivo.", + "openExplorerOnEnd": "Automaticamente abre a visualização do explorador no final de uma sessão de depuração", + "inlineValues": "Mostrar valores de variáveis em linha no editor durante a depuração", + "hideActionBar": "Controlar se a barra de ação flutuante do depurador deve ser ocultada", + "launch": "Configuração global do lançamento do depurador. Deve ser usado como uma alternativa para o arquivo 'launch.json' que é compartilhado entre os espaços de trabalho" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..7cdac28bba --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "Primeiro abra uma pasta para fazer uma configuração de depuração avançada." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..22b05454f7 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "Contribui adaptadores de depuração.", + "vscode.extension.contributes.debuggers.type": "Identificador único para esse adaptador de depuração.", + "vscode.extension.contributes.debuggers.label": "Nome de exibição para esse adaptador de depuração.", + "vscode.extension.contributes.debuggers.program": "Caminho para o programa adaptador de depuração. O caminho pode ser absoluto ou relativo à pasta de extensão.", + "vscode.extension.contributes.debuggers.args": "Argumentos opcionais a serem informados para o adaptador.", + "vscode.extension.contributes.debuggers.runtime": "Runtime opcional no caso do atributo do programa não ser um executável, mas requerer um runtime.", + "vscode.extension.contributes.debuggers.runtimeArgs": "Argumentos opcionais do runtime.", + "vscode.extension.contributes.debuggers.variables": "Mapeamento de variáveis interativas (por exemplo ${action.pickProcess}) em 'launch.json' para um comando.", + "vscode.extension.contributes.debuggers.initialConfigurations": "Configurações para gerar o 'launch.json' inicial.", + "vscode.extension.contributes.debuggers.languages": "Lista de idiomas para os quais a extensão de depuração pode ser considerada o \"depurador padrão\".", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Se especificado VS Code chamará este comando para determinar o caminho do executável do adaptador de depuração e os argumentos para passar.", + "vscode.extension.contributes.debuggers.startSessionCommand": "Se especificado VS Code chamará este comando para as ações de \"depurar\" ou \"executar\" direcionadas para esta extensão.", + "vscode.extension.contributes.debuggers.configurationSnippets": "Trechos de código para adicionar novas configurações em 'launch.json'.", + "vscode.extension.contributes.debuggers.configurationAttributes": "Configurações de esquema JSON para validar 'launch.json'.", + "vscode.extension.contributes.debuggers.windows": "Configurações específicas do Windows.", + "vscode.extension.contributes.debuggers.windows.runtime": "Runtime usado para Windows.", + "vscode.extension.contributes.debuggers.osx": "Configurações específicas do OS X.", + "vscode.extension.contributes.debuggers.osx.runtime": "Runtime usado para o OS X.", + "vscode.extension.contributes.debuggers.linux": "Configurações específicas do Linux.", + "vscode.extension.contributes.debuggers.linux.runtime": "Runtime usado para o Linux.", + "vscode.extension.contributes.breakpoints": "Contribui aos pontos de interrupção.", + "vscode.extension.contributes.breakpoints.language": "Permitir pontos de parada para este idioma.", + "app.launch.json.title": "Executar", + "app.launch.json.version": "Versão deste formato de arquivo.", + "app.launch.json.configurations": "Lista de configurações. Adicionar novas configurações ou editar as existentes usando o IntelliSense.", + "app.launch.json.compounds": "Lista de compostos. Cada composto faz referência a várias configurações que vão ser executadas juntas.", + "app.launch.json.compound.name": "Nome do composto. Aparece no menu drop-down da configuração de execução.", + "app.launch.json.compounds.configurations": "Nomes das configurações que serão iniciadas como parte deste composto.", + "debugNoType": "'type' do adaptador de depuração não pode ser omitido e deve ser do tipo 'string'.", + "selectDebug": "Selecione o ambiente", + "DebugConfig.failed": "Não é possível criar o arquivo 'launch.json' dentro da pasta '.vscode' ({0})." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..4a95219a7d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "Remover pontos de interrupção", + "removeBreakpointOnColumn": "Remover ponto de interrupção na coluna {0}", + "removeLineBreakpoint": "Remover ponto de interrupção de linha", + "editBreakpoints": "Editar pontos de interrupção", + "editBreakpointOnColumn": "Editar o ponto de interrupção na coluna {0}", + "editLineBrekapoint": "Editar o ponto de interrupção de linha", + "enableDisableBreakpoints": "Habilitar/Desabilitar pontos de interrupção", + "disableColumnBreakpoint": "Desabilitar ponto de interrupção na coluna {0}", + "disableBreakpointOnLine": "Desabilitar ponto de interrupção de linha", + "enableBreakpoints": "Habilitar o ponto de interrupção na coluna {0}", + "enableBreakpointOnLine": "Habilitar o ponto de interrupção de linha", + "addBreakpoint": "Adicionar ponto de interrupção", + "addConfiguration": "Adicionar Configuração..." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..f1614bb12b --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "Depurar passando o mouse por cima" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..ffc61b3b25 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "Apenas valores primitivos são mostrados para este objeto.", + "debuggingPaused": "Depuração pausada, razão {0}, {1} {2}", + "debuggingStarted": "Depuração Iniciada.", + "debuggingStopped": "Depuração parada.", + "breakpointAdded": "Adicionado ponto de interrupção, linha {0}, arquivo {1}", + "breakpointRemoved": "Ponto de interrupção removido, linha {0}, arquivo {1}", + "compoundMustHaveConfigurations": "Composição deve ter o atributo \"configurations\" definido para iniciar várias configurações.", + "configMissing": "Configuração '{0}' não tem 'launch.json'.", + "debugTypeNotSupported": "Tipo de depuração configurado '{0}' não é suportado.", + "debugTypeMissing": "Falta a propriedade 'type' para a configuração de lançamento escolhida.", + "preLaunchTaskErrors": "Erros de build foram detectados durante a preLaunchTask '{0}'.", + "preLaunchTaskError": "Erro de build foi detectado durante a preLaunchTask '{0}'.", + "preLaunchTaskExitCode": "A preLaunchTask '{0}' encerrada com código de saída {1}.", + "debugAnyway": "Depurar mesmo assim", + "noFolderWorkspaceDebugError": "O arquivo ativo não pode ser depurado. Certifique-se de que ele está salvo no disco e que tem uma extensão de depuração instalada para esse tipo de arquivo.", + "NewLaunchConfig": "Por favor, configure o arquivo de configuração de lançamento para seu aplicativo. {0}", + "DebugTaskNotFound": "Não foi possível encontrar o preLaunchTask '{0}'." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..ce36898942 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "Processar", + "paused": "Em pausa", + "running": "Em execução", + "thread": "Thread", + "pausedOn": "Pausado em {0}", + "loadMoreStackFrames": "Carregar mais segmentos de pilha", + "threadAriaLabel": "Thread {0}, pilha de chamadas, depuração", + "stackFrameAriaLabel": "Segmento de Pilha {0} linha {1} {2}, pilha de chamadas, depuração", + "variableValueAriaLabel": "Digite o novo valor da variável", + "variableScopeAriaLabel": "Escopo {0}, variáveis, depuração", + "variableAriaLabel": "{0} valor {1}, variáveis, depuração", + "watchExpressionPlaceholder": "Expressão para monitorar", + "watchExpressionInputAriaLabel": "Digitar expressão a monitorar", + "watchExpressionAriaLabel": "{0} valor {1}, monitorar, depuração", + "watchVariableAriaLabel": "{0} valor {1}, monitorar, depuração", + "functionBreakpointPlaceholder": "Função de parada", + "functionBreakPointInputAriaLabel": "Digitar Ponto de Parada de Função", + "functionBreakpointsNotSupported": "Pontos de parada de função não são suportados por este tipo de depuração", + "breakpointAriaLabel": "Ponto de parada linha {0} {1}, pontos de parada, depuração", + "functionBreakpointAriaLabel": "Ponto de parada de função {0}, pontos de parada, depuração", + "exceptionBreakpointAriaLabel": "Ponto de parada de exceção {0}, pontos de parada, depuração" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..beb574b148 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "Seção de variáveis", + "variablesAriaTreeLabel": "Variáveis de Depuração", + "expressionsSection": "Seção de Expressões", + "watchAriaTreeLabel": "Depurar Expressões Monitoradas", + "callstackSection": "Seção de Pilha de Chamada", + "debugStopped": "Pausado em {0}", + "callStackAriaLabel": "Depurar a Pilha de Chamadas", + "breakpointsSection": "Seção de Pontos de Parada", + "breakpointsAriaTreeLabel": "Depurar os Pontos de Parada" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..68219b6ad8 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "Copiar valor", + "copy": "Copiar", + "copyAll": "Copiar todos", + "copyStackTrace": "Copiar Pilha de Chamadas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..d96f8dfa36 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "Mais Informações", + "unableToLaunchDebugAdapter": "Não é possível executar o adaptador de depuração de '{0}'.", + "unableToLaunchDebugAdapterNoArgs": "Não é possível executar o adaptador de depuração.", + "stoppingDebugAdapter": "{0}. Parando o adaptador de depuração.", + "debugAdapterCrash": "Processo do adaptador de depuração foi finalizado inesperadamente" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..8f419b19e7 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "Ler o Painel de Impressão Eval", + "actions.repl.historyPrevious": "História anterior", + "actions.repl.historyNext": "Próxima história", + "actions.repl.acceptInput": "REPL Aceitar Entrada", + "actions.repl.copyAll": "Depurar: Copiar Todos os Consoles" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..fa52765581 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "Estado do objeto é capturado na primeira avaliação", + "replVariableAriaLabel": "Variável {0} tem valor {1}, ler a impressão do valor do loop, depurar", + "replExpressionAriaLabel": "Expressão {0} tem valor {1}, ler o laço de avaliação de impressão, depurar", + "replValueOutputAriaLabel": " impressão da avaliação do laço de leitura, depurar", + "replKeyValueOutputAriaLabel": "Variável de saída {0} tem valor {1}, ler o loop de avaliação de impressão, depurar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..ff00cf8bc5 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "Cor de fundo da barra de status quando um programa está sendo depurado. A barra de status é mostrada na parte inferior da janela", + "statusBarDebuggingForeground": "Cor de primeiro plano da barra de status quando um programa está sendo depurado. A barra de status é mostrada na parte inferior da janela", + "statusBarDebuggingBorder": "Cor da borda da barra de status separando para a barra lateral e editor quando um programa está sendo depurado. A barra de status é mostrada na parte inferior da janela" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..04c3e9832a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "depurado", + "debug.terminal.not.available.error": "Terminal integrado não disponível" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..41aa200743 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "Executável do adaptador de depuração '{0}' não existe.", + "debugAdapterCannotDetermineExecutable": "Não é possível determinar o executável para o adaptador de depuração '{0}'.", + "launch.config.comment1": "Use o IntelliSense para aprender sobre possíveis atributos.", + "launch.config.comment2": "Passe o mouse para ver as descrições dos atributos existentes.", + "launch.config.comment3": "Para obter mais informações, visite: {0}", + "debugType": "Tipo de configuração.", + "debugTypeNotRecognised": "O tipo de depuração não é reconhecido. Certifique-se de que você tem uma extensão de depuração correspondente instalada e que ela está habilitada.", + "node2NotSupported": "\"node2\" não é mais suportado, use \"node\" ao invés e defina o atributo \"protocol\" para \"inspector\".", + "debugName": "Nome da configuração; aparece no menu drop-down da configuração de lançamento. ", + "debugRequest": "Requer o tipo de configuração. Pode ser \"launch\" ou \"attach\".", + "debugServer": "Somente para o desenvolvimento de extensão de depuração: se uma porta é especificada, o VS Code tenta se conectar a um adaptador de depuração executando em modo de servidor", + "debugPrelaunchTask": "Tarefa para ser executada antes de começar a sessão de depuração.", + "debugWindowsConfiguration": "Atributos de configuração de lançamento específicos do Windows.", + "debugOSXConfiguration": "Atributos de configuração de lançamento específicos do OS X.", + "debugLinuxConfiguration": "Atributos de configuração de lançamento específicos do Linux.", + "deprecatedVariables": "'env.', 'config.' e 'command.' foram descontinuados, use ' env:', ' config:' e ' command:' em vez disso." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..4881721879 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "Mostrar Comandos do Emmet" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..26a88adf3a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: Saldo (interno)", + "balanceOutward": "Emmet: Saldo (externo)" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..e14bae0e56 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: Ir para o Ponto de Edição Anterior", + "nextEditPoint": "Emmet: Ir para o Próximo Ponto de Edição" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..28835a648e --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: Avaliar a expressão matemática" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..7a94763625 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: Expandir Abreviação" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..fb087e4375 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: Incremento de 0.1", + "incrementNumberByOne": "Emmet: Incremento de 1", + "incrementNumberByTen": "Emmet: Incremento de 10", + "decrementNumberByOneTenth": "Emmet: Decréscimo por 0.1", + "decrementNumberByOne": "Emmet: Decréscimo por 1", + "decrementNumberByTen": "Emmet: Decréscimo por 10" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..16550d43d2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: Ir para o par de correspondência" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..33413a8eea --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: Mesclar linhas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..df0bca224b --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: Refletir valor CSS" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..b46a46d377 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: Remover Rótulo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..1a2a1b6e34 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: Selecione o Item anterior", + "selectNextItem": "Emmet: Selecione o próximo Item" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..460b405e17 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: Dividir/Juntar Rótulo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..1b277e741a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: Alternar Comentário" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..6d50f65926 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: Atualizar o Tamanho da Imagem" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..1ebe661a63 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: Atualizar Rótulo", + "enterTag": "Insira o Rótulo", + "tag": "Rótulo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..9e427acf14 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: Envelope com a abreviatura", + "enterAbbreviation": "Digite a abreviação", + "abbreviation": "Abreviação" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..ee53cb22ba --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Quando habilitado, abreviações emmet são expandidas ao pressionar TAB. Não aplicável quando emmet.useNewemmet é definido como true.", + "emmetPreferences": "Preferências usadas para modificar o comportamento de algumas ações e resolvedores do Emmet. Não aplicável quando emmet.useNewemmet é definido como true.", + "emmetSyntaxProfiles": "Definir o perfil para a sintaxe especificada ou usar seu próprio perfil com regras específicas.", + "emmetExclude": "Uma matriz de línguagens onde abreviaturas emmet não devem ser expandidas.", + "emmetExtensionsPath": "Caminho para uma pasta que contém perfis emmet, trechos e preferências. Somente perfis são honrados do caminho das extensões quando emmet.useNewEmmet estiver definida como true.", + "useNewEmmet": "Experimente os novos módulos emmet (que irão substituir a antiga biblioteca unica emmet) para todos os recursos emmet." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..fd4ef968a1 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Terminal Externo", + "explorer.openInTerminalKind": "Personaliza o tipo de terminal para executar.", + "terminal.external.windowsExec": "Personalizar qual terminal executar no Windows.", + "terminal.external.osxExec": "Personalizar qual aplicativo de terminal executar no OS X.", + "terminal.external.linuxExec": "Personalizar qual terminal executar no Linux.", + "globalConsoleActionWin": "Abrir Novo Prompt de Comando", + "globalConsoleActionMacLinux": "Abrir Novo Terminal", + "scopedConsoleActionWin": "Abrir no Prompt de Comando", + "scopedConsoleActionMacLinux": "Abrir no Terminal", + "openFolderInIntegratedTerminal": "Abrir no Terminal" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..6aa79433c6 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Terminal Externo", + "terminal.external.windowsExec": "Personalizar qual terminal executar no Windows.", + "terminal.external.osxExec": "Personalizar qual aplicativo de terminal executar no OS X.", + "terminal.external.linuxExec": "Personalizar qual terminal executar no Linux.", + "globalConsoleActionWin": "Abrir Novo Prompt de Comando", + "globalConsoleActionMacLinux": "Abrir Novo Terminal", + "scopedConsoleActionWin": "Abrir no Prompt de Comando", + "scopedConsoleActionMacLinux": "Abrir no Terminal" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/ptb/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..1865710208 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "Console VS Code", + "mac.terminal.script.failed": "Script '{0}' falhou com código de saída {1}", + "mac.terminal.type.not.supported": "'{0}' não suportado", + "press.any.key": "Pressione qualquer tecla para continuar...", + "linux.term.failed": "'{0}' falhou com código de saída {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..bd93c37512 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "Contribui ao modo de exibição personalizado", + "vscode.extension.contributes.view.id": "Identificação única usada para identificar a vista criada por vscode.workspace.createTreeView", + "vscode.extension.contributes.view.label": "Sequência de caracteres legível usada para processar a visualização", + "vscode.extension.contributes.view.icon": "Caminho para o ícone da visualização", + "vscode.extension.contributes.views": "Contribui com visualizações personalizadas", + "showViewlet": "Mostrar {0}", + "view": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..f88c189776 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "Atualizar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..38c302e720 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "Não há TreeExplorerNodeProvider com id {providerId} registrado." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/ptb/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..552c00ce6d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "Seção do Explorador da Árvore" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..f85f226846 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "Erro", + "Unknown Dependency": "Dependência Desconhecida:" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..ce1e76d179 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "Nome da extensão", + "extension id": "Identificador da extensão", + "publisher": "Nome do editor", + "install count": "Quantidade de Instalações", + "rating": "Avaliação", + "license": "Licença", + "details": "Detalhes", + "contributions": "Contribuições", + "changelog": "Registro de Alterações", + "dependencies": "Dependências", + "noReadme": "README não disponível.", + "noChangelog": "Registro de Alterações não disponível.", + "noContributions": "Sem Contribuições", + "noDependencies": "Sem Dependências", + "settings": "Configurações ({0})", + "setting name": "Nome", + "description": "Descrição", + "default": "Valor padrão", + "debuggers": "Depuradores ({0})", + "debugger name": "Nome", + "debugger type": "Tipo", + "views": "Visualizações ({0})", + "view id": "ID", + "view name": "Nome", + "view location": "Onde", + "themes": "Temas ({0})", + "JSON Validation": "Validação JSON ({0})", + "commands": "Comandos ({0})", + "command name": "Nome", + "keyboard shortcuts": "Atalhos de Teclado", + "menuContexts": "Contextos de Menu", + "languages": "Linguagens ({0})", + "language id": "ID", + "language name": "Nome", + "file extensions": "Extensões de Arquivo", + "grammar": "Gramática", + "snippets": "Trechos" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..ed6f762e97 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "Instalar", + "installing": "Instalando", + "uninstallAction": "Desinstalar", + "Uninstalling": "Desinstalando", + "updateAction": "Atualizar", + "updateTo": "Atualizar para {0}", + "enableForWorkspaceAction.label": "Habilitar (Espaço de Trabalho)", + "enableAlwaysAction.label": "Habilitar (Sempre)", + "disableForWorkspaceAction.label": "Desabilitar (Espaço de Trabalho)", + "disableAlwaysAction.label": "Desabilitar (Sempre)", + "ManageExtensionAction.uninstallingTooltip": "Desinstalando", + "enableForWorkspaceAction": "Espaço de trabalho", + "enableGloballyAction": "Sempre", + "enableAction": "Habilitar", + "disableForWorkspaceAction": "Espaço de trabalho", + "disableGloballyAction": "Sempre", + "disableAction": "Desabilitar", + "checkForUpdates": "Verificar Atualizações", + "enableAutoUpdate": "Habilitar Extensões Auto-Atualizáveis", + "disableAutoUpdate": "Desabilitar Extensões Auto-Atualizáveis", + "updateAll": "Atualizar Todas as Extensões", + "reloadAction": "Recarregar", + "postUpdateTooltip": "Recarregar para atualizar", + "postUpdateMessage": "Recarregar esta janela para ativar a extensão atualizada '{0}'?", + "postEnableTooltip": "Recarregar para ativar", + "postEnableMessage": "Recarregar esta janela para ativar a extensão '{0}'?", + "postDisableTooltip": "Recarregar para desativar", + "postDisableMessage": "Recarregar esta janela para desativar a extensão '{0}'?", + "postUninstallTooltip": "Recarregar para desativar", + "postUninstallMessage": "Recarregar esta janela para desativar a extensão desinstalada '{0}'?", + "reload": "&&Recarregar Janela", + "toggleExtensionsViewlet": "Mostrar Extensões", + "installExtensions": "Instalar Extensões", + "showEnabledExtensions": "Mostrar extensões habilitadas", + "showInstalledExtensions": "Mostrar Extensões Instaladas", + "showDisabledExtensions": "Mostrar Extensões Desabilitadas", + "clearExtensionsInput": "Limpar Entrada de Extensões", + "showOutdatedExtensions": "Mostrar Extensões Desatualizadas", + "showPopularExtensions": "Mostrar Extensões Populares", + "showRecommendedExtensions": "Mostrar Extensões Recomendadas", + "showWorkspaceRecommendedExtensions": "Mostrar Extensões Recomendadas para o Espaço de Trabalho", + "showRecommendedKeymapExtensions": "Mostrar Mapeamentos de Teclado Recomendados", + "showRecommendedKeymapExtensionsShort": "Mapeamentos de Teclado", + "showLanguageExtensions": "Mostrar Extensões de Linguagem", + "showLanguageExtensionsShort": "Extensões de Linguagem", + "showAzureExtensions": "Mostrar extensões para o Azure", + "showAzureExtensionsShort": "Extensões do Azure", + "configureWorkspaceRecommendedExtensions": "Configurar Extensões Recomendadas (Espaço de Trabalho)", + "ConfigureWorkspaceRecommendations.noWorkspace": "As recomendações somente estão disponíveis em uma pasta do espaço de trabalho.", + "OpenExtensionsFile.failed": "Não foi possível criar o arquivo 'extensions.json' na pasta '.vscode' ({0}).", + "builtin": "Intrínseco", + "disableAll": "Desabilitar Todas as Extensões Instaladas", + "disableAllWorkspace": "Desabilitar Todas as Extensões Instaladas para este Espaço de Trabalho", + "enableAll": "Habilitar Todas as Extensões Instaladas", + "enableAllWorkspace": "Habilitar Todas as Extensões Instaladas para este Espaço de Trabalho", + "extensionButtonProminentBackground": "Cor de fundo do botão para a ações de extensão que se destacam (por exemplo, o botão de instalar).", + "extensionButtonProminentForeground": "Cor de primeiro plano do botão para a ações de extensão que se destacam (por exemplo, o botão de instalar).", + "extensionButtonProminentHoverBackground": "Cor de fundo ao passar o mouse sobre o botão para a ações de extensão que se destacam (por exemplo, o botão de instalar)." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..3bc9b402ec --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "Pressione Enter para gerenciar suas extensões.", + "searchFor": "Pressione Enter para pesquisar por '{0}' na Loja.", + "noExtensionsToInstall": "Digite um nome de extensão" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..60a819bfa7 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "Extensões", + "app.extensions.json.recommendations": "Lista de recomendações de extensões. O identificador de uma extensão é sempre ' ${publisher}. ${nome}'. Por exemplo: 'vscode.csharp'.", + "app.extension.identifier.errorMessage": "Formato esperado '${editor}.${nome}'. Exemplo: 'vscode.csharp'." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..69ba1389da --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "Extensão: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..296ce51553 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "A extensão {0} é recomendada para este tipo de arquivo.", + "showRecommendations": "Mostrar Recomendações", + "neverShowAgain": "Não mostrar novamente", + "close": "Fechar", + "workspaceRecommended": "Este espaço de trabalho possui recomendações de extensão.", + "ignoreExtensionRecommendations": "Deseja ignorar todas as recomendações de extensão?", + "ignoreAll": "Sim, Ignorar Tudo", + "no": "Não", + "cancel": "Cancelar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..2b8ebcb9cf --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "Gerenciar Extensões", + "galleryExtensionsCommands": "Instalar Extensões da Galeria", + "extension": "Extensão", + "extensions": "Extensões", + "view": "Exibir", + "extensionsConfigurationTitle": "Extensões", + "extensionsAutoUpdate": "Atualizar extensões automaticamente", + "extensionsIgnoreRecommendations": "Ignorar recomendações de extensão" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..16203eaf3e --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "Abrir a Pasta de Extensões", + "installVSIX": "Instalar do VSIX...", + "InstallVSIXAction.success": "A extensão foi instalada com sucesso. Reinicie para habilitá-la.", + "InstallVSIXAction.reloadNow": "Recarregar Agora" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..ff7f508327 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Desabilitar outros mapeamentos de teclado ({0}) para evitar conflitos entre as combinações de teclas?", + "yes": "Sim", + "no": "Não", + "betterMergeDisabled": "A extensão Better Merge agora é intrínseca, a extensão instalada foi desabilitada e pode ser desinstalada.", + "uninstall": "Desinstalar", + "later": "Mais tarde" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..32fd161458 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "Loja", + "installedExtensions": "Instalado", + "searchInstalledExtensions": "Instalado", + "recommendedExtensions": "Recomendado", + "searchExtensions": "Pesquisar Extensões na Loja", + "sort by installs": "Ordenar por: Quantidade de Instalações", + "sort by rating": "Ordenar por: Avaliação", + "sort by name": "Ordenar por: Nome", + "suggestProxyError": "A Loja retornou 'ECONNREFUSED'. Por favor, verifique a configuração de 'http.proxy'.", + "extensions": "Extensões", + "outdatedExtensions": "{0} Extensões Desatualizadas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..945186d8d9 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Extensões", + "no extensions found": "Nenhuma extensão encontrada.", + "suggestProxyError": "A Loja retornou 'ECONNREFUSED'. Por favor, verifique a configuração de 'http.proxy'." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..dc3de1c8e0 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "Habilitando '{0}' também habilita suas dependências. Gostaria de continuar?", + "enable": "Sim", + "doNotEnable": "Não", + "disableDependeciesConfirmation": "Gostaria de desabilitar somente '{0}', ou as suas dependências também?", + "disableOnly": "Apenas", + "disableAll": "Todos", + "cancel": "Cancelar", + "singleDependentError": "Não é possível desabilitar a extensão '{0}'. A extensão '{1}' depende dela.", + "twoDependentsError": "Não é possível desabilitar a extensão '{0}'. As extensões '{1}' e '{2}' dependem dela.", + "multipleDependentsError": "Não é possível desabilitar a extensão '{0}'. As extensões '{1}', '{2}' e outras dependem dela.", + "installConfirmation": "Gostaria de instalar a extensão '{0}'?", + "install": "Instalar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/ptb/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..0afa23e65b --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "Tweetar Feedback", + "label.sendASmile": "Tweete feedback para nós", + "patchedVersion1": "Sua instalação está corrompida.", + "patchedVersion2": "Por favor especificar isso ao enviar um bug.", + "sentiment": "Como foi sua experiência?", + "smileCaption": "Feliz", + "frownCaption": "Triste", + "other ways to contact us": "Outras maneiras de nos contatar", + "submit a bug": "Submeter um bug", + "request a missing feature": "Solicitar um recurso ausente", + "tell us why?": "Diga-nos porquê?", + "commentsHeader": "Comentários", + "tweet": "Tweetar", + "character left": "caractere à esquerda", + "characters left": "caracteres à esquerda", + "feedbackSending": "Enviando", + "feedbackSent": "Obrigado", + "feedbackSendingError": "Tentar novamente" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..f79d8b8ad5 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "Visualizador de Arquivo Binário" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..66330a2e6d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "Editor de Arquivo de Texto", + "createFile": "Criar arquivo", + "fileEditorWithInputAriaLabel": "{0}. Editor de Arquivo de Texto.", + "fileEditorAriaLabel": "Editor de Arquivo de Texto" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..c038793b57 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "Pastas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..d4e8674881 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "Arquivos", + "revealInSideBar": "Revelar na Barra Lateral", + "acceptLocalChanges": "Usar suas alterações e substituir o conteúdo do disco", + "revertLocalChanges": "Descartar as alterações e reverter para o conteúdo no disco" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..2775ce5b0e --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "Tentar novamente", + "rename": "Renomear", + "newFile": "Novo Arquivo", + "newFolder": "Nova Pasta", + "openFolderFirst": "Abrir uma pasta primeiro para criar arquivos ou pastas dentro dele.", + "newUntitledFile": "Novo Arquivo Sem Título", + "createNewFile": "Novo Arquivo", + "createNewFolder": "Nova Pasta", + "deleteButtonLabelRecycleBin": "&&Mover para Lixeira", + "deleteButtonLabelTrash": "&&Mover para o Lixo", + "deleteButtonLabel": "&&Excluir", + "dirtyMessageFolderOneDelete": "Você está excluindo uma pasta com alterações não salvas em 1 arquivo. Você quer continuar?", + "dirtyMessageFolderDelete": "Você está excluindo uma pasta com alterações não salvas em {0} arquivos. Você quer continuar?", + "dirtyMessageFileDelete": "Você está excluindo um arquivo com alterações não salvas. Você quer continuar?", + "dirtyWarning": "Suas alterações serão perdidas se você não salvá-las.", + "confirmMoveTrashMessageFolder": "Tem certeza de que deseja excluir '{0}' e seu conteúdo?", + "confirmMoveTrashMessageFile": "Tem certeza de que deseja excluir '{0}'?", + "undoBin": "Você pode restaurar da lixeira.", + "undoTrash": "Você pode restaurar a partir do lixo.", + "confirmDeleteMessageFolder": "Tem certeza de que deseja excluir permanentemente '{0}' e seu conteúdo?", + "confirmDeleteMessageFile": "Tem certeza de que deseja excluir permanentemente '{0}'?", + "irreversible": "Esta ação é irreversível!", + "permDelete": "Excluir permanentemente", + "delete": "Excluir", + "importFiles": "Importar Arquivos", + "confirmOverwrite": "Um arquivo ou pasta com o mesmo nome já existe na pasta de destino. Você quer substituí-lo?", + "replaceButtonLabel": "&&Substituir", + "copyFile": "Copiar", + "pasteFile": "Colar", + "duplicateFile": "Duplicar", + "openToSide": "Aberto para o lado", + "compareSource": "Selecione para comparar", + "globalCompareFile": "Compare o Arquivo Ativo Com...", + "pickHistory": "Selecione um arquivo previamente aberto para comparar com", + "unableToFileToCompare": "O arquivo selecionado não pode ser comparado com '{0}'.", + "openFileToCompare": "Abrir um arquivo primeiro para compará-lo com outro arquivo.", + "compareWith": "Comparar '{0}' com '{1}'", + "compareFiles": "Comparar Arquivos", + "refresh": "Atualizar", + "save": "Salvar", + "saveAs": "Salvar como...", + "saveAll": "Salvar Todos", + "saveAllInGroup": "Salvar Todos no Grupo", + "saveFiles": "Salvar Arquivos Sujos", + "revert": "Reverter Arquivo", + "focusOpenEditors": "Foco na Visualização dos Editores Abertos", + "focusFilesExplorer": "Foco no Explorador de Arquivos", + "showInExplorer": "Revelar o Arquivo Ativo na Barra Lateral", + "openFileToShow": "Abrir um arquivo primeiro para mostrá-lo no explorer", + "collapseExplorerFolders": "Esconder Pastas no Explorador", + "refreshExplorer": "Atualizar Explorador", + "openFile": "Abrir arquivo...", + "openFileInNewWindow": "Abrir o Arquivo Ativo em uma Nova Janela", + "openFileToShowInNewWindow": "Abrir um arquivo primeiro para abrir em uma nova janela", + "revealInWindows": "Revelar no Explorer", + "revealInMac": "Revelar no Finder", + "openContainer": "Abrir a Pasta", + "revealActiveFileInWindows": "Revelar Arquivo Ativo no Windows Explorer", + "revealActiveFileInMac": "Revelar Arquivo Ativo no Finder", + "openActiveFileContainer": "Abrir a Pasta do Arquivo Ativo.", + "copyPath": "Copiar Caminho", + "copyPathOfActive": "Copiar Caminho do Arquivo Ativo", + "emptyFileNameError": "Um nome de arquivo ou pasta deve ser fornecido.", + "fileNameExistsError": "Um arquivo ou pasta **{0}** já existe neste local. Escolha um nome diferente.", + "invalidFileNameError": "O nome **{0}** não é válido como um nome de arquivo ou pasta. Por favor, escolha um nome diferente.", + "filePathTooLongError": "O nome **{0}** resulta em um caminho muito longo. Escolha um nome mais curto.", + "compareWithSaved": "Comparar arquivo ativo com salvo", + "modifiedLabel": "{0} (em disco) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..b0faff9190 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "Abrir um arquivo primeiro para copiar seu caminho", + "openFileToReveal": "Abrir um arquivo primeiro para revelar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..2a9833d4b2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "Mostrar Explorer", + "explore": "Explorador", + "view": "Exibir", + "textFileEditor": "Editor de Arquivo de Texto", + "binaryFileEditor": "Editor de Arquivo Binário", + "filesConfigurationTitle": "Arquivos", + "exclude": "Configure os padrões glob para excluir arquivos e pastas.", + "files.exclude.boolean": "O padrão glob com o qual combinar os caminhos de arquivo. Defina para verdadeiro ou falso para habilitar ou desabilitar o padrão.", + "files.exclude.when": "Verificação adicional nos irmãos de um arquivo correspondente. Use $(basename) como variável para o nome do arquivo correspondente.", + "associations": "Configurar as associações de arquivo para linguagens (por exemplo, \"* Extension\": \"html\"). Estas têm precedência sobre as associações padrão das linguagens instaladas.", + "encoding": "A codificação padrão do conjunto de caracteres para ser usada ao ler e gravar arquivos.", + "autoGuessEncoding": "Quando habilitado, tentará adivinhar a codificação do conjunto de caracteres ao abrir arquivos", + "eol": "O caractere padrão de fim de linha. Use \\n para LF e \\r\\n para CRLF.", + "trimTrailingWhitespace": "Quando habilitado, removerá espaços em branco à direita ao salvar um arquivo.", + "insertFinalNewline": "Quando habilitado, inseririrá uma nova linha no final do arquivo quando salvá-lo.", + "files.autoSave.off": "Um arquivo sujo nunca é automaticamente salvo.", + "files.autoSave.afterDelay": "Um arquivo sujo é salvo automaticamente após configurado em 'files.autoSaveDelay'.", + "files.autoSave.onFocusChange": "Um arquivo sujo é salvo automaticamente quando o editor perde o foco.", + "files.autoSave.onWindowChange": "Um arquivo sujo é salvo automaticamente quando a janela perde o foco.", + "autoSave": "Controla o auto-salvamento de arquivos sujos. Aceita os valores: '{0}', '{1}', '{2}' (editor perde o foco), '{3}' (janela perde o foco). Se definido como '{4}', você pode configurar o atraso em 'files.autoSaveDelay'.", + "autoSaveDelay": "Controla o atraso em milissegundos depois que um arquivo sujo é salvo automaticamente. Só se aplica quando 'files.autoSave' for definida como '{0}'", + "watcherExclude": "Configure padrões glob de caminhos de arquivo para excluir do monitoramento de arquivo. Padrões devem corresponder a caminhos absolutos (ou seja, prefixo com ** ou o caminho completo para combinar corretamente). Alterar essa configuração requer uma reinicialização. Quando o Code estiver consumindo muito tempo de cpu na inicialização, você pode excluir pastas grandes para reduzir a carga inicial.", + "hotExit.off": "Desabilitar a saída à quente.", + "hotExit.onExit": "Saída à quente será acionada quando o aplicativo for fechado, ou seja, quando a última janela é fechada no Windows/Linux ou quando o comando workbench.action.quit é acionado (paleta de comandos, keybinding, menu). Todas as janelas com backups serão restauradas na próxima execução.", + "hotExit.onExitAndWindowClose": "Saída à quente será acionada quando o aplicativo for fechado, ou seja, quando a última janela é fechada no Windows/Linux ou quando o comando workbench.action.quit é acionado (paleta de comando, keybinding, menu), e também para qualquer janela com uma pasta aberta independentemente se é a última janela. Todas as janelas sem pastas abertas serão restauradas no próximo lançamento. Para restaurar janelas de pastas como eram antes do desligamento configure \"window.restoreWindows\" para \"todos\".", + "hotExit": "Controla se os arquivos não salvos são lembrados entre as sessões, permitindo salvar alerta ao sair do editor seja ignorada.", + "useExperimentalFileWatcher": "Usar o novo monitor experimental de arquivo.", + "defaultLanguage": "O modo de linguagem padrão que é atribuída para novos arquivos.", + "editorConfigurationTitle": "Editor", + "formatOnSave": "Formata um arquivo no salvamento. Um formatador deve estar disponível, o arquivo não deve ser salvo automaticamente e editor não deve ser desligado.", + "explorerConfigurationTitle": "Explorador de arquivos", + "openEditorsVisible": "Número de editores mostrado no painel Abrir Editores. Configurá-lo para 0 irá ocultar o painel.", + "dynamicHeight": "Controla se a altura da seção de editores abertos deve adaptar-se dinamicamente para o número de elementos ou não.", + "autoReveal": "Controla se o explorador deve automaticamente revelar e selecionar arquivos ao abri-los.", + "enableDragAndDrop": "Controla se o explorador deve permitir mover arquivos e pastas através de arrastar e soltar.", + "sortOrder.default": "Arquivos e pastas são classificadas por seus nomes, em ordem alfabética. Pastas são exibidas acima dos arquivos.", + "sortOrder.mixed": "Arquivos e pastas são classificadas por seus nomes, em ordem alfabética. Arquivos são misturados com pastas.", + "sortOrder.filesFirst": "Arquivos e pastas são classificadas por seus nomes, em ordem alfabética. Os arquivos são exibidos acima das pastas.", + "sortOrder.type": "Arquivos e pastas são classificadas de acordo com suas extensões, em ordem alfabética. Pastas são exibidas acima dos arquivos.", + "sortOrder.modified": "Arquivos e pastas são classificados de acordo com a data da última modificação, em ordem decrescente. Pastas são exibidas acima dos arquivos.", + "sortOrder": "Controla a ordem de classificação dos arquivos e pastas no explorador. Além da classificação padrão, você pode definir a ordem para 'mixed' (arquivos e pastas misturados), 'type' (por tipo de arquivo), 'modified' (pela data da última modificação) ou 'filesFirst' (exibe os arquivos acima das pastas)." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..406d120452 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "Descartar", + "overwrite": "Sobrescrever", + "retry": "Tentar novamente", + "readonlySaveError": "Falha ao salvar '{0}': O arquivo está protegido contra gravação. Selecione 'Substituir' para remover a proteção.", + "genericSaveError": "Erro ao salvar '{0}': {1}", + "staleSaveError": "Falha ao salvar '{0}': O conteúdo no disco é mais recente. Clique em **Comparar** para comparar a sua versão com a do disco.", + "compareChanges": "Comparar", + "saveConflictDiffLabel": "{0} (no disco) ↔ {1} (em {2}) - Resolver conflitos de salvamento", + "userGuide": "Use as ações na barra de ferramentas de editor para a direita para **desfazer** suas alterações ou **substituir** o conteúdo no disco com as alterações" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..e69c903247 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Nenhuma Pasta Aberta", + "explorerSection": "Seção de Explorador de Arquivos", + "noWorkspaceHelp": "Você ainda não abriu uma pasta.", + "openFolder": "Abrir Pasta" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..c62fa58ed2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "Seção de Explorador de Arquivos", + "treeAriaLabel": "Explorador de Arquivos" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..900b97c7ff --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "Digite o Nome do arquivo. Pressione Enter para confirmar ou Escape para cancelar.", + "filesExplorerViewerAriaLabel": "{0}, Explorador de Arquivos", + "dropFolders": "Você quer adicionar as pastas no espaço de trabalho?", + "dropFolder": "Você quer adicionar a pasta no espaço de trabalho?", + "addFolders": "&& Adicionar pastas", + "addFolder": "&& Adicionar pasta", + "confirmOverwriteMessage": "'{0}' já existe na pasta de destino. Deseja substituí-lo?", + "irreversible": "Esta ação é irreversível!", + "replaceButtonLabel": "&&Substituir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..fbcb183878 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "Abrir Editores", + "openEditosrSection": "Abrir Seção de Editores", + "treeAriaLabel": "Abrir Editores: Lista de Arquivos Ativos", + "dirtyCounter": "{0} não salvos" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..b700fe8de1 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}, Agrupar Editor", + "openEditorAriaLabel": "{0}, Abrir Editor", + "saveAll": "Salvar Todos", + "closeAllUnmodified": "Fechar Não Modificados", + "closeAll": "Fechar todos", + "compareWithSaved": "Comparar com o salvo", + "close": "Fechar", + "closeOthers": "Fechar Outros" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..10ef7b58d7 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0} arquivos não salvos" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..fdfb6e0a12 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (excluído do disco)" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..0db592b3d5 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "Visualização Html", + "devtools.webview": "Desenvolvedor: Ferramentas Webview" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/ptb/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..0896c70280 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "Entrada inválida do editor." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/ptb/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..658b40fb59 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "Desenvolvedor: Ferramentas Webview" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/ptb/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..2d22fa9053 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "Exibir", + "problems.view.show.label": "Mostrar Problemas", + "problems.panel.configuration.title": "Visualização de Problemas", + "problems.panel.configuration.autoreveal": "Controla se a visaulização de problemas evela os arquivos automaticamente ao abri-los", + "markers.panel.title.problems": "Problemas", + "markers.panel.aria.label.problems.tree": "Problemas agrupados por arquivos", + "markers.panel.no.problems.build": "Nenhum problema foi detectado na área de trabalho até agora.", + "markers.panel.no.problems.filters": "Nenhum resultado encontrado com os critérios de filtro fornecidos", + "markers.panel.action.filter": "Problemas de Filtro", + "markers.panel.filter.placeholder": "Filtrar por tipo ou texto", + "markers.panel.filter.errors": "erros", + "markers.panel.filter.warnings": "avisos", + "markers.panel.filter.infos": "informações", + "markers.panel.single.error.label": "1 Erro", + "markers.panel.multiple.errors.label": "{0} Erros", + "markers.panel.single.warning.label": "1 Aviso", + "markers.panel.multiple.warnings.label": "{0} Avisos", + "markers.panel.single.info.label": "1 Informação", + "markers.panel.multiple.infos.label": "{0} Informações", + "markers.panel.single.unknown.label": "1 Desconhecido", + "markers.panel.multiple.unknowns.label": "{0} Desconhecidos", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "{0} com {1} problemas", + "problems.tree.aria.label.error.marker": "Erro gerado por {0}: {1} na linha {2} e caractere {3}", + "problems.tree.aria.label.error.marker.nosource": "Erro: {0} na linha {1} e caractere {2}", + "problems.tree.aria.label.warning.marker": "Aviso gerado por {0}: {1} na linha {2} e caractere {3}", + "problems.tree.aria.label.warning.marker.nosource": "Aviso: {0} na linha {1} e caractere {2}", + "problems.tree.aria.label.info.marker": "Informação gerada por {0}: {1} na linha {2} e caractere {3}", + "problems.tree.aria.label.info.marker.nosource": "Informação: {0} na linha {1} e caractere {2}", + "problems.tree.aria.label.marker": "Problema gerado por {0}: {1} na linha {2} e caractere {3}", + "problems.tree.aria.label.marker.nosource": "Problema: {0} na linha {1} e caractere {2}", + "errors.warnings.show.label": "Mostrar Erros e Avisos" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/ptb/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..3a8d521776 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "Copiar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..5cb025d2c2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Você deseja responder a uma pequena pesquisa?", + "takeSurvey": "Responder a pesquisa", + "remindLater": "Lembrar mais tarde", + "neverAgain": "Não mostrar novamente" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..4d2c35fb7a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Saída", + "viewCategory": "Exibir", + "clearOutput.label": "Limpar saída" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..5fa728065a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "Alternar Saída", + "clearOutput": "Limpar saída", + "toggleOutputScrollLock": "Alternar Scroll Lock de Saída", + "switchToOutput.label": "Mudar para Saída" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/ptb/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..ed0a1c92c8 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0}, Painel de saída", + "outputPanelAriaLabel": "Painel de saída" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/ptb/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..4b8431e054 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Saída", + "channel": "para '{0}'" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..3f72b6f0a3 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "Inicialização lenta detectada", + "slow.detail": "Pena que você teve uma inicialização lenta. Por favor reinicie '{0}' com perfil de desempenho habilitado, compartilhe os perfis conosco e nós trabalharemos duro para fazer com que a inicialização fique perfeita novamente.", + "prof.message": "Perfis criados com sucesso.", + "prof.detail": "Por favor, crie um problema e anexe manualmente os seguintes arquivos:\n{0}", + "prof.restartAndFileIssue": "Criar Problema e Reiniciar", + "prof.restart": "Reiniciar", + "prof.thanks": "Obrigado por nos ajudar.", + "prof.detail.restart": "É necessário um reinício final para continuar a usar '{0}'. Novamente, obrigado pela sua contribuição." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..f148eb5a72 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "Pressionar a combinação de teclas desejada e ENTER. ESCAPE para cancelar.", + "defineKeybinding.chordsTo": "Acorde para" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..e82e66679a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "Atalhos de Teclado", + "SearchKeybindings.AriaLabel": "Pesquisar keybindings", + "SearchKeybindings.Placeholder": "Pesquisar keybindings", + "sortByPrecedene": "Ordenar por precedência", + "header-message": "Para personalizações avançadas abrir e editar", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "Keybindings", + "changeLabel": "Alterar Keybinding", + "addLabel": "Adicionar Keybinding", + "removeLabel": "Remover Keybinding", + "resetLabel": "Redefinir Keybinding", + "showConflictsLabel": "Mostrar Conflitos", + "copyLabel": "Copiar", + "error": "Erro '{0}' enquanto edita keybinding. Por favor, abra o arquivo 'keybindings.json' e verifique.", + "command": "Comando", + "keybinding": "KeyBinding", + "source": "Fonte", + "when": "Quando", + "editKeybindingLabelWithKey": "Alterar Keybinding {0}", + "editKeybindingLabel": "Alterar Keybinding", + "addKeybindingLabelWithKey": "Adicionar Keybinding {0}", + "addKeybindingLabel": "Adicionar Keybinding", + "commandAriaLabel": "Comando é {0}.", + "keybindingAriaLabel": "KeyBinding é {0}.", + "noKeybinding": "Nenhum Keybinding atribuído.", + "sourceAriaLabel": "Fonte é {0}.", + "whenAriaLabel": "Quando é {0}.", + "noWhen": "Sem contexto Quando." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..f1c04ed107 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "Definir Keybinding", + "defineKeybinding.kbLayoutErrorMessage": "Você não será capaz de produzir esta combinação de teclas sob seu layout de teclado atual.", + "defineKeybinding.kbLayoutLocalAndUSMessage": "**{0}** para o seu layout de teclado atual (**{1}** para US padrão).", + "defineKeybinding.kbLayoutLocalMessage": "**{0}** para o seu layout de teclado atual." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..eac2cc04c7 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "Editor de Preferências Padrão", + "keybindingsEditor": "Editor de Keybindings", + "preferences": "Preferências" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..47d7b4e59c --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "Abra as Configurações de Usuário", + "openGlobalKeybindings": "Abrir Atalhos de Teclado", + "openGlobalKeybindingsFile": "Abrir Arquivo de Atalhos de Teclado", + "openWorkspaceSettings": "Abrir as configurações do espaço de trabalho", + "openFolderSettings": "Abrir configurações da pasta", + "pickFolder": "Selecionar a Pasta", + "configureLanguageBasedSettings": "Definir Configurações Específicas de Linguagem...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "Selecionar Linguagem" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..2c33d87ba3 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "Configurações Padrão", + "SearchSettingsWidget.AriaLabel": "Configurações de Pesquisa", + "SearchSettingsWidget.Placeholder": "Configurações de Pesquisa", + "totalSettingsMessage": "Total {0} Configurações", + "noSettingsFound": "Nenhum resultado", + "oneSettingFound": "1 Configuração correspondente", + "settingsFound": "{0} Configurações correspondentes", + "fileEditorWithInputAriaLabel": "{0}. Editor de Arquivo de Texto.", + "fileEditorAriaLabel": "Editor de Arquivo de Texto", + "preferencesAriaLabel": "Preferências padrão. Editor de texto somente leitura." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..aaf0356712 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "Coloque as suas configurações aqui para substituir as configurações padrão.", + "errorInvalidConfiguration": "Não é possível gravar em configurações. Corrija erros/avisos no arquivo e tente novamente.", + "emptyWorkspaceSettingsHeader": "Coloque as suas configurações aqui para substituir as configurações de usuário.", + "emptyFolderSettingsHeader": "Coloque as suas configurações de pasta aqui para substituir aqueles das configurações do espaço de trabalho.", + "defaultFolderSettingsTitle": "Configurações de pasta padrão", + "defaultSettingsTitle": "Configurações Padrão", + "noSettingsFound": "Não há configurações encontradas.", + "editTtile": "Editar", + "replaceDefaultValue": "Substituir nas Configurações", + "copyDefaultValue": "Copiar para Configurações", + "unsupportedPHPExecutablePathSetting": "Essa configuração deve ser uma Configuração de Usuário. Para configurar o PHP para o espaço de trabalho, abra um arquivo PHP e clique em 'Caminho do PHP' na barra de status.", + "unsupportedWorkspaceSetting": "Essa configuração deve ser uma Configuração de Usuário.", + "unsupportedWorkbenchSetting": "Essa configuração não pode ser aplicada agora. Será aplicada quando você abrir esta pasta diretamente." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..b36a89b6d5 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "Abrir uma pasta primeiro para criar configurações de espaço de trabalho", + "emptyKeybindingsHeader": "Coloque suas chaves de ligações neste arquivo para substituir os padrões", + "defaultKeybindings": "Keybindings Padrão", + "folderSettingsName": "{0} (configurações de pasta)", + "fail.createSettings": "Não foi possível criar '{0}' ({1})." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..beac460e1b --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "Configurações de pasta" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..3a5911020f --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "Valor padrão", + "user": "Usuário", + "meta": "meta", + "option": "opção" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..d0cc722032 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "Configurações de Usuário", + "workspaceSettingsTarget": "Configurações de Espaço de Trabalho" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..683e750a21 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "Comumente Utilizado", + "noSettings": "Nenhuma configuração", + "defaultKeybindingsHeader": "Substituir as chaves de ligações, colocando-os em seu arquivo de chave ligações." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..2794e0d797 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Mostrar todos os comandos", + "clearCommandHistory": "Limpar o Histórico de Comandos", + "showCommands.label": "Paleta de comandos...", + "entryAriaLabelWithKey": "{0}, {1}, comandos", + "entryAriaLabel": "{0}, comandos", + "canNotRun": "O comando '{0}' não pode ser executado a partir daqui.", + "actionNotEnabled": "O comando '{0}' não está habilitado no contexto atual.", + "recentlyUsed": "usados recentemente", + "morecCommands": "outros comandos", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "Não há comandos correspondentes" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..cdfb3a1f0b --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "Ir para linha...", + "gotoLineLabelEmptyWithLimit": "Digite um número de linha entre 1 e {0} para navegar para lá", + "gotoLineLabelEmpty": "Digite um número de linha para navegar para lá", + "gotoLineColumnLabel": "Ir para linha {0} e caractere {1}", + "gotoLineLabel": "Ir para linha {0}", + "gotoLineHandlerAriaLabel": "Digite um número de linha para navegar.", + "cannotRunGotoLine": "Abrir um arquivo de texto primeiro para ir a uma linha" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..92048aca13 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "Ir para o Símbolo no Arquivo...", + "symbols": "símbolos ({0})", + "method": "métodos ({0})", + "function": "funções ({0})", + "_constructor": "construtores ({0})", + "variable": "variáveis ({0})", + "class": "classes ({0})", + "interface": "interfaces ({0})", + "namespace": "namespaces ({0})", + "package": "pacotes ({0})", + "modules": "módulos ({0})", + "property": "propriedades ({0})", + "enum": "enumerações ({0})", + "string": "cadeias de caracteres ({0})", + "rule": "regras ({0})", + "file": "arquivos ({0})", + "array": "matrizes ({0})", + "number": "números ({0})", + "boolean": "booleanos ({0})", + "object": "objetos ({0})", + "key": "chaves ({0})", + "entryAriaLabel": "{0}, símbolos", + "noSymbolsMatching": "Não há símbolos correspondentes", + "noSymbolsFound": "Nenhum símbolo encontrado", + "gotoSymbolHandlerAriaLabel": "Tipo para reduzir os símbolos do editor ativo atual.", + "cannotRunGotoSymbolInFile": "Não há informações de símbolo para o arquivo", + "cannotRunGotoSymbol": "Abrir um arquivo de texto primeiro para ir a um símbolo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..6d0bd39e3e --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, ajuda do seletor", + "globalCommands": "comandos globais", + "editorCommands": "comandos do editor" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..7b8aa131b1 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "Exibir e executar comandos", + "gotoLineDescriptionMac": "Ir para linha", + "gotoLineDescriptionWin": "Ir para linha", + "gotoSymbolDescription": "Ir para o Símbolo no Arquivo", + "gotoSymbolDescriptionScoped": "Ir para o Símbolo no Arquivo Por Categoria", + "helpDescription": "Mostrar ajuda", + "viewPickerDescription": "Abrir Visualização" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..53d8a703b9 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, visualizar seletor", + "views": "Modos de exibição", + "panels": "Painéis", + "terminals": "Terminal", + "terminalTitle": "{0}: {1}", + "channels": "Saída", + "openView": "Abrir Visualização", + "quickOpenView": "Abrir Visualização Rápida" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..9c2a1a8b61 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "Uma configuração que requer uma reinicialização foi alterada.", + "relaunchSettingDetail": "Pressione o botão de reinicialização para reiniciar {0} e habilitar a configuração.", + "restart": "Reiniciar", + "relaunchWorkspaceMessage": "Esta mudança de espaço de trabalho exige o recarregamento do nosso sistema de extensão.", + "reload": "Recarregar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..3f3fe319a5 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "Cor de fundo da dobra do editor para as linhas que estão modificadas.", + "editorGutterAddedBackground": "Cor de fundo da dobra do editor para as linhas que estão adicionadas.", + "editorGutterDeletedBackground": "Cor de fundo da dobra do editor para as linhas que estão excluídas.", + "overviewRulerModifiedForeground": "Visão geral da cor do marcador da régua para conteúdo modificado.", + "overviewRulerAddedForeground": "Visão geral da cor do marcador da régua para conteúdo adicionado.", + "overviewRulerDeletedForeground": "Visão geral da cor do marcador da régua para conteúdo excluído." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..fcdf2db452 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "Mostrar Git", + "source control": "Controle de código-fonte", + "toggleSCMViewlet": "Mostrar SCM", + "view": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..74378735c9 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0} alterações pendentes" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..0b9c82973c --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "Instalar provedores de SCM adicionais...", + "switch provider": "Mudar Provedor SCM..." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..80d5c68a30 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Mensagem (tecle {0} para confirmar)", + "installAdditionalSCMProviders": "Instalar provedores de SCM adicionais...", + "no open repo": "Não há um controle de código fonte ativo", + "source control": "Controle de código-fonte", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..c84684fc24 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "resultados do arquivo e símbolo", + "fileResults": "resultados do arquivo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..989ed10273 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, seletor de arquivo", + "searchResults": "resultados da pesquisa" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..981ba718a0 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, selecionador de símbolos", + "symbols": "resultados de símbolo", + "noSymbolsMatching": "Não há símbolos correspondentes", + "noSymbolsWithoutInput": "Digitar para pesquisar símbolos" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..24b2b76866 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "entrada", + "useIgnoreFilesDescription": "Usar Ignorar Arquivos", + "useExcludeSettingsDescription": "Usar Configurações de Exclusão" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..b0aaa20081 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (Substituir Preview)" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..8f0bdfff4d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Ir para Símbolo no Espaço de Trabalho...", + "name": "Pesquisar", + "showSearchViewlet": "Mostrar Busca", + "view": "Exibir", + "findInFiles": "Localizar nos Arquivos", + "openAnythingHandlerDescription": "Ir para o Arquivo", + "openSymbolDescriptionNormal": "Ir para o Símbolo em Área de Trabalho", + "searchOutputChannelTitle": "Pesquisar", + "searchConfigurationTitle": "Pesquisar", + "exclude": "Configure os padrões glob para excluir arquivos e pastas nas pesquisas. Herda todos os padrões glob da configuração files.exclude.", + "exclude.boolean": "O padrão glob com o qual combinar os caminhos de arquivo. Defina para verdadeiro ou falso para habilitar ou desabilitar o padrão.", + "exclude.when": "Verificação adicional nos irmãos de um arquivo correspondente. Use $(basename) como variável para o nome do arquivo correspondente.", + "useRipgrep": "Controla se deve utilizar ripgrep na pesquisa de texto", + "useIgnoreFilesByDefault": "Controla se deve utilizar arquivos .gitignore e .ignore por padrão ao fazer pesquisas em um novo espaço de trabalho.", + "search.quickOpen.includeSymbols": "Configurar para incluir resultados de uma pesquisa símbolo global nos resultados do arquivo para Abertura Rápida." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..1f3e828ca9 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "Mostrar a Próxima Busca Incluindo Padrões", + "previousSearchIncludePattern": "Mostrar a Busca Anterior Incluindo Padrões", + "nextSearchExcludePattern": "Mostrar a Próxima Busca Excluindo Padrões", + "previousSearchExcludePattern": "Mostrar a Busca Anterior Excluindo Padrões", + "nextSearchTerm": "Mostrar o Próximo Termo de Pesquisa", + "previousSearchTerm": "Mostrar Termo de Pesquisa Anterior", + "focusNextInputBox": "Focalizar a Próxima Caixa de Entrada", + "focusPreviousInputBox": "Focalizar a Caixa de Entrada Anterior", + "replaceInFiles": "Substituir nos Arquivos", + "findInWorkspace": "Procurar no Espaço de Trabalho...", + "findInFolder": "Procurar na pasta...", + "RefreshAction.label": "Atualizar", + "ClearSearchResultsAction.label": "Limpar os Resultados da Pesquisa", + "FocusNextSearchResult.label": "Focalizar o Próximo Resultado da Pesquisa", + "FocusPreviousSearchResult.label": "Focalizar o Resultado da Pesquisa Anterior", + "RemoveAction.label": "Remover", + "file.replaceAll.label": "Substituir Tudo", + "match.replace.label": "Substituir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..c5fd6f7d60 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "Outros arquivos", + "searchFileMatches": "{0} arquivos encontrados", + "searchFileMatch": "arquivo {0} encontrado", + "searchMatches": "{0} correspondências encontradas", + "searchMatch": "{0} correspondência encontrada", + "folderMatchAriaLabel": "{0} correspondências na pasta raiz {1}, Resultado da pesquisa", + "fileMatchAriaLabel": "{0} correspondências no arquivo {1} da pasta {2}, Resultado da pesquisa", + "replacePreviewResultAria": "Substitua o termo {0} pelo termo {1} na coluna posição {2} correspondente ao texto {3}", + "searchResultAria": "Encontrado o termo {0} na posição da coluna {1} correspondente ao texto {2}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..f0452bbe47 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "Alternar Detalhes da Pesquisa", + "searchScope.includes": "arquivos a serem incluídos", + "label.includes": "Pesquisa Padrões de Inclusão", + "searchScope.excludes": "arquivos a serem excluídos", + "label.excludes": "Pesquisa de Padrões de Exclusão", + "replaceAll.confirmation.title": "Substituir Tudo", + "replaceAll.confirm.button": "Substituir", + "replaceAll.occurrence.file.message": "Substituída {0} ocorrência no arquivo {1} com '{2}'.", + "removeAll.occurrence.file.message": "Substituída {0} ocorrência no arquivo {1}'.", + "replaceAll.occurrence.files.message": "Substituída {0} ocorrência no arquivo {1} com '{2}'.", + "removeAll.occurrence.files.message": "Substituída {0} ocorrência nos arquivos {1}", + "replaceAll.occurrences.file.message": "Substituídas {0} ocorrências no arquivo {1} com '{2}'.", + "removeAll.occurrences.file.message": "Substituídas {0} ocorrências nos arquivo {1}.", + "replaceAll.occurrences.files.message": "Substituídas {0} ocorrências nos arquivos {1} com '{2}'.", + "removeAll.occurrences.files.message": "Substituídas {0} ocorrências nos arquivos {1}.", + "removeAll.occurrence.file.confirmation.message": "Substituir {0} ocorrências no arquivo {1} com '{2}'?", + "replaceAll.occurrence.file.confirmation.message": "Substituir {0} ocorrência no arquivo {1}?", + "removeAll.occurrence.files.confirmation.message": "Substituir {0} ocorrência nos arquivos {1} com '{2}'?", + "replaceAll.occurrence.files.confirmation.message": "Substituir {0} ocorrência nos arquivos {1}?", + "removeAll.occurrences.file.confirmation.message": "Substituir {0} ocorrências no arquivo {1} com '{2}'?", + "replaceAll.occurrences.file.confirmation.message": "Substituir {0} ocorrências no arquivo {1}?", + "removeAll.occurrences.files.confirmation.message": "Substituir {0} ocorrências nos arquivos {1} com '{2}'?", + "replaceAll.occurrences.files.confirmation.message": "Substituir {0} ocorrências nos arquivos {1}?", + "treeAriaLabel": "Resultados da Pesquisa", + "searchPathNotFoundError": "Caminho de pesquisa não encontrado: {0}", + "searchMaxResultsWarning": "O conjunto de resultados contém apenas um subconjunto de todas as correspondências. Seja mais específico na sua pesquisa para diminuir o número de resultados.", + "searchCanceled": "Pesquisa foi cancelada antes de qualquer resultado ser encontrado - ", + "noResultsIncludesExcludes": "Nenhum resultado encontrado em '{0}' excluindo '{1}' - ", + "noResultsIncludes": "Nenhum resultado encontrado em '{0}' -", + "noResultsExcludes": "Nenhum resultado encontrado excluindo '{0}' -", + "noResultsFound": "Nenhum resultado encontrado. Revise suas configurações para exclusões configuradas e arquivos ignorados - ", + "rerunSearch.message": "Pesquisar novamente", + "rerunSearchInAll.message": "Pesquisar novamente em todos os arquivos", + "openSettings.message": "Abrir configurações", + "openSettings.learnMore": "Saiba Mais", + "ariaSearchResultsStatus": "Pesquisa retornou {0} resultados em {1} arquivos", + "search.file.result": "{0} resultado no arquivo {1}", + "search.files.result": "{0} resultado nos arquivos {1}", + "search.file.results": "{0} resultados no arquivo {1}", + "search.files.results": "{0} resultados nos arquivos {1}", + "searchWithoutFolder": "Você ainda não abriu uma pasta. Somente arquivos abertos são pesquisados - ", + "openFolder": "Abrir Pasta" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..c052f2ae1b --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "Substituir Todos (Submeter Pesquisa para Habilitar)", + "search.action.replaceAll.enabled.label": "Substituir Tudo", + "search.replace.toggle.button.title": "Alternar Substituir", + "label.Search": "Pesquisar: Digite o termo de pesquisa e pressione Enter para pesquisar ou Escape para cancelar", + "search.placeHolder": "Pesquisar", + "label.Replace": "Substituir: Digite o termo a ser substituído e pressione Enter para visualizar ou Escape para cancelar", + "search.replace.placeHolder": "Substituir", + "regexp.validationFailure": "A expressão corresponde a tudo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..a6b3a4306d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "Nenhuma pasta no espaço de trabalho com o nome: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..b4db456f08 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "Contribui aos trechos de código.", + "vscode.extension.contributes.snippets-language": "Identificador de linguagem para o qual este trecho de código contribui.", + "vscode.extension.contributes.snippets-path": "Caminho do arquivo de trechos de código. O caminho é relativo à pasta de extensão e normalmente começa com '. /snippets/'.", + "invalid.language": "Linguagem desconhecida em `contributes.{0}.language`. Valor fornecido: {1}", + "invalid.path.0": "Esperada uma string em `contributes.{0}.path`. Valor informado: {1}", + "invalid.path.1": "É esperado que `contributes.{0}.path` ({1}) seja incluído na pasta da extensão ({2}). Isto pode tornar a extensão não portável.", + "badVariableUse": "O trecho de código \"{0}\" muito provavelmente confunde as variáveis de trecho de código e espaços reservados do trecho de código. Consulte https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax para obter mais detalhes." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..bfff97a654 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "Inserir trecho de código" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..bb387d78bc --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "Selecionar Idioma para o Trecho", + "openSnippet.errorOnCreate": "Não é possível criar {0}", + "openSnippet.label": "Abrir trechos de código do usuário", + "preferences": "Preferências", + "snippetSchema.json.default": "Trecho de código vazio", + "snippetSchema.json": "Configuração do trecho do usuário", + "snippetSchema.json.prefix": "O prefixo usado ao selecionar o trecho no intelliSense", + "snippetSchema.json.body": "O conteúdo do trecho. Use '$1', '${1:defaultText}' para definir as posições do cursor, use '$0' para a posição final do cursor. Insira valores de variáveis com '${varName}' e '${varName:defaultText}', por exemplo ' Este é o arquivo: $TM_FILENAME'.", + "snippetSchema.json.description": "A descrição do trecho." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..75d99c1e9f --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "Trecho de código do usuário", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0}, {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..79b3d01606 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "Inserir trechos de código quando seu prefixo corresponder. Funciona melhor quando 'quickSuggestions' não está habilitado." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..17c3530935 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "Nos ajude a melhorar o nosso apoio para {0}", + "takeShortSurvey": "Responda a uma pesquisa curta", + "remindLater": "Lembrar mais tarde", + "neverAgain": "Não mostrar novamente" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..5cb025d2c2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Você deseja responder a uma pequena pesquisa?", + "takeSurvey": "Responder a pesquisa", + "remindLater": "Lembrar mais tarde", + "neverAgain": "Não mostrar novamente" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..10c031dd6a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Digite o nome de uma tarefa de compilação", + "noTasksMatching": "Não há tarefas correspondentes", + "noTasksFound": "Tarefas de compilação não encontradas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..7dbf376636 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, tarefas", + "recentlyUsed": "tarefas recentemente utilizadas", + "configured": "tarefas configuradas", + "detected": "tarefas detectadas", + "customizeTask": "Configurar a tarefa" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..e5266e9897 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Digite o nome de uma tarefa para reiniciar", + "noTasksMatching": "Não há tarefas correspondentes", + "noTasksFound": "Não há tarefa para ser reiniciada" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..5ae33d30c7 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Digite o nome de uma tarefa para executar", + "noTasksMatching": "Não há tarefas correspondentes", + "noTasksFound": "Não há tarefas encontradas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..2f6ba04e19 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Digite o nome de uma tarefa para finalizar", + "noTasksMatching": "Não há tarefas correspondentes", + "noTasksFound": "Nenhuma tarefa para finalizar encontrada" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..c641e016f4 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Digite o nome de uma tarefa de teste", + "noTasksMatching": "Não há tarefas correspondentes", + "noTasksFound": "Tarefas de teste não encontradas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..efa1370be2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Aviso: options.cwd deve ser do tipo string. Ignorando valor {0}\n", + "ConfigurationParser.noargs": "Erro: Argumentos do comando devem ser uma matriz de strings. Valor informado é:\n{0}", + "ConfigurationParser.noShell": "Aviso: A configuração do shell somente é suportada quando estiver executando tarefas no terminal.", + "ConfigurationParser.noName": "Erro: Problem Matcher no escopo declarado deve ter um nome:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Aviso: a correspondência de problema definido é desconhecido. Tipos suportados são string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Erro: ProblemMatcher inválido referência: {0}\n", + "ConfigurationParser.noTaskName": "Erro: tarefas devem fornecer uma propriedade taskName. A tarefa será ignorada.\n{0}\n", + "taskConfiguration.shellArgs": "Aviso: a tarefa '{0}' é um comando do shell e o nome de comando ou um dos seus argumentos tem espaços sem escape. Para garantir a linha de comando correta por favor mesclar argumentos no comando.", + "taskConfiguration.noCommandOrDependsOn": "Erro: a tarefa '{0}' não especifica nem um comando nem uma propriedade dependsOn. A tarefa será ignorada. Sua definição é: \n{1}", + "taskConfiguration.noCommand": "Erro: a tarefa '{0}' não define um comando. A tarefa será ignorada. Sua definição é: {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..9bc9a53abd --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "O tipo de tarefa atual", + "TaskDefinition.properties": "Propriedades adicionais do tipo de tarefa", + "TaskTypeConfiguration.noType": "A propriedade necessária 'taskType' está faltando na configuração do tipo de tarefa ", + "TaskDefinitionExtPoint": "Contribui com os tipos de tarefa" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..951eb284e1 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": "Executa comando de compilação do .NET Core", + "msbuild": "Executa a compilação destino", + "externalCommand": "Exemplo para executar um comando externo arbitrário", + "Maven": "Executa comandos comuns específicos" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..bd5de80b23 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "Opções de comando adicionais", + "JsonSchema.options.cwd": "O diretório de trabalho atual do programa executado ou do script. Se omitido raiz de espaço de trabalho atual do código é usado.", + "JsonSchema.options.env": "O ambiente do programa executado ou comando shell. Se omitido o ambiente do processo pai é usado.", + "JsonSchema.shellConfiguration": "Configura a shell a ser usada.", + "JsonSchema.shell.executable": "O shell a ser usado.", + "JsonSchema.shell.args": "Os argumentos shell.", + "JsonSchema.command": "O comando a ser executado. Pode ser um programa externo ou um comando shell.", + "JsonSchema.tasks.args": "Argumentos passados para o comando quando esta tarefa é invocada.", + "JsonSchema.tasks.taskName": "Nome da tarefa", + "JsonSchema.tasks.windows": "Configuração de comando específica do Windows", + "JsonSchema.tasks.mac": "Configuração de comando específica do Mac", + "JsonSchema.tasks.linux": "Configuração de comando específica do Linux", + "JsonSchema.tasks.suppressTaskName": "Controla se o nome de tarefa é adicionado como um argumento para o comando. Se omitido o valor definido globalmente é usado.", + "JsonSchema.tasks.showOutput": "Controla se a saída da execução de tarefas é mostrada ou não. Se omitido o valor definido globalmente é usado.", + "JsonSchema.echoCommand": "Controla se o comando executado é enviado para a saída. O padrão é false.", + "JsonSchema.tasks.watching.deprecation": "Descontinuado. Use isBackground.", + "JsonSchema.tasks.watching": "Se a tarefa executada é mantida viva e está monitorando o sistema de arquivos.", + "JsonSchema.tasks.background": "Se a tarefa executada é mantida viva e é executado em segundo plano.", + "JsonSchema.tasks.promptOnClose": "Se o usuário é solicitado quando VS Code fecha com uma tarefa sendo executada.", + "JsonSchema.tasks.build": "Esta tarefa é mapeada para o comando de compilação padrão do código.", + "JsonSchema.tasks.test": "Esta tarefa é mapeada para o comando de teste padrão do código.", + "JsonSchema.tasks.matchers": "O problema matcher(s) a seu utilizado. Pode ser uma sequência de caracteres ou uma definição de problem matcher ou uma matriz de sequências de caracteres e problem matchers.", + "JsonSchema.args": "Argumentos adicionais passados para o comando.", + "JsonSchema.showOutput": "Controla se a saída da execução de tarefas é mostrada ou não. Se omitido 'sempre' é usado.", + "JsonSchema.watching.deprecation": "Descontinuado. Use isBackground.", + "JsonSchema.watching": "Se a tarefa executada é mantida viva e está monitorando o sistema de arquivos.", + "JsonSchema.background": "Se a tarefa executada é mantida viva e é executado em segundo plano.", + "JsonSchema.promptOnClose": "Se o usuário é solicitado quando VS Code fecha com uma tarefa de segundo plano em execução.", + "JsonSchema.suppressTaskName": "Controla se o nome de tarefa é adicionado como um argumento para o comando. O padrão é false.", + "JsonSchema.taskSelector": "Prefixo para indicar que um argumento é tarefa.", + "JsonSchema.matchers": "A correspondência de problemas a ser utilizada. Pode ser uma sequência de caracteres ou uma definição de correspondência de problemas ou uma matriz de sequências de caracteres e correspondência de problemas.", + "JsonSchema.tasks": "As configurações de tarefa. Normalmente são ampliações de tarefas já definidas na execução de tarefa externa." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..bc8c1ceab8 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "Número da versão do config", + "JsonSchema._runner": "O runner já se formou. Use a propriedade runner oficial", + "JsonSchema.runner": "Define se a tarefa é executada como um processo e a saída é mostrada na janela de saída ou dentro do terminal.", + "JsonSchema.windows": "Configuração de comando específica do Windows", + "JsonSchema.mac": "Configuração de comando específica do Mac", + "JsonSchema.linux": "Configuração de comando específica do Linux", + "JsonSchema.shell": "Especifica se o comando é um comando shell ou um programa externo. O padrão é falso se omitido." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..0e6abb36ea --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "Especifica se o comando é um comando shell ou um programa externo. O padrão é falso se omitido.", + "JsonSchema.tasks.isShellCommand.deprecated": "A propriedade isShellCommand é obsoleta. Use a propriedade type da tarefa e a propriedade shell nas opções. Veja também as notas de versão 1.14.", + "JsonSchema.tasks.dependsOn.string": "Outra tarefa da qual esta tarefa depende.", + "JsonSchema.tasks.dependsOn.array": "A outra tarefa que esta tarefa depende.", + "JsonSchema.tasks.presentation": "Configura o painel que é usado para apresentar a saída da tarefa e ler sua entrada.", + "JsonSchema.tasks.presentation.echo": "Controla se o comando executado é ecoado para o painel. Padrão é true.", + "JsonSchema.tasks.presentation.focus": "Controla se o painel ganha foco. O padrão é false. Se definido como true, o painel é revelado também.", + "JsonSchema.tasks.presentation.reveal.always": "Sempre revela o terminal quando esta tarefa é executada.", + "JsonSchema.tasks.presentation.reveal.silent": "Só revela o terminal se nenhum problema correspondente está associado com a tarefa e erros ocorrem ao executá-la.", + "JsonSchema.tasks.presentation.reveal.never": "Nunca revela o terminal quando esta tarefa é executada.", + "JsonSchema.tasks.presentation.reveals": "Controla se o painel executando a tarefa é revelado ou não. O padrão é \"always\".", + "JsonSchema.tasks.presentation.instance": "Controla se o painel é compartilhado entre tarefas, dedicado a esta tarefa ou um novo é criado em cada execução.", + "JsonSchema.tasks.terminal": "A propriedade terminal é obsoleta. Use presentation em seu lugar.", + "JsonSchema.tasks.group.kind": "Grupo de execução da tarefa.", + "JsonSchema.tasks.group.isDefault": "Define-se essa tarefa é a tarefa do padrão do grupo.", + "JsonSchema.tasks.group.defaultBuild": "Marca as tarefas como tarefas padrão de compilação.", + "JsonSchema.tasks.group.defaultTest": "Marca as tarefas como a tarefa de teste padrão.", + "JsonSchema.tasks.group.build": "Marca as tarefas como uma tarefa de compilação acessível através do comando 'Run Build Task'.", + "JsonSchema.tasks.group.test": "Marca as tarefas como uma tarefa de teste acessível através do comando 'Run Test Task'.", + "JsonSchema.tasks.group.none": "Atribui a tarefa para nenhum grupo", + "JsonSchema.tasks.group": "Define a que grupo de execução desta tarefa pertence. Suporta \"build\" para adicioná-lo ao grupo de compilação e \"test\" para adicioná-lo ao grupo de teste.", + "JsonSchema.tasks.type": "Define se a tarefa é executada como um processo ou como um comando dentro de uma shell. O padrão é processo.", + "JsonSchema.version": "O número da versão do config.", + "JsonSchema.tasks.identifier": "Um identificador definido pelo usuário para fazer referência a tarefa em launch.json ou uma cláusula dependsOn.", + "JsonSchema.tasks.taskLabel": "A etiqueta da tarefa", + "JsonSchema.tasks.taskName": "Nome da tarefa", + "JsonSchema.tasks.taskName.deprecated": "A propriedade name da tarefa é obsoleta. Use a propriedade label.", + "JsonSchema.tasks.background": "Se a tarefa executada é mantida viva e é executado em segundo plano.", + "JsonSchema.tasks.promptOnClose": "Se o usuário é solicitado quando VS Code fecha com uma tarefa sendo executada.", + "JsonSchema.tasks.matchers": "A correspondência de problemas a ser utilizada. Pode ser uma sequência de caracteres ou uma definição de correspondência de problemas ou uma matriz de sequências de caracteres e correspondência de problemas.", + "JsonSchema.customizations.customizes.type": "O tipo de tarefa a ser personalizada", + "JsonSchema.tasks.customize.deprecated": "A propriedade customize é obsoleta. Consulte as notas de versão 1.14 sobre como migrar para a nova abordagem de personalização de tarefa", + "JsonSchema.tasks.showOputput.deprecated": "A propriedade showOutput é obsoleta. Use a propriedade reveal dentro da propriedade presentation. Ver também as notas de versão 1.14.", + "JsonSchema.tasks.echoCommand.deprecated": "A propriedade echoCommand é obsoleta. Use a propriedade echo dentro da propriedade presentation. Ver também as notas de versão 1.14.", + "JsonSchema.tasks.suppressTaskName.deprecated": "A propriedade suppressTaskName é obsoleta. Ao invés, coloque o comando junto com seus argumentos dentro de uma tarefa. Veja também as notas de lançamento da versão 1.14.", + "JsonSchema.tasks.isBuildCommand.deprecated": "A propriedade isBuildCommand é obsoleta. Use a propriedade group em seu lugar. Ver também as notas de versão 1.14.", + "JsonSchema.tasks.isTestCommand.deprecated": "A propriedade isTestCommand é obsoleta. Use a propriedade group em seu lugar. Ver também as notas de versão 1.14.", + "JsonSchema.tasks.taskSelector.deprecated": "A propriedade taskSelector é obsoleta. Ao invés, coloque o comando junto com seus argumentos dentro de uma tarefa. Veja também as notas de lançamento da versão 1.14.", + "JsonSchema.windows": "Configuração de comando específica do Windows", + "JsonSchema.mac": "Configuração de comando específica do Mac", + "JsonSchema.linux": "Configuração de comando específica do Linux" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..ad9da6145a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "Tarefas", + "ConfigureTaskRunnerAction.noWorkspace": "Tarefas somente estão disponíveis em uma pasta da área de trabalho.", + "ConfigureTaskRunnerAction.quickPick.template": "Selecione um gerenciador de tarefa", + "ConfigureTaskRunnerAction.autoDetecting": "Tarefas de auto detecção para {0}", + "ConfigureTaskRunnerAction.autoDetect": "A tarefa de sistema de auto detecção falhou. Usando o modelo padrão. Consulte a saída da tarefa para detalhes.", + "ConfigureTaskRunnerAction.autoDetectError": "A tarefa de sistema de auto detecção produziu erros. Consulte a saída da tarefa para detalhes.", + "ConfigureTaskRunnerAction.failed": "Não é possível criar o arquivo 'tasks.json' na pasta '.vscode'. Consulte a saída da tarefa para detalhes.", + "ConfigureTaskRunnerAction.label": "Configure o gerenciador de tarefas", + "ConfigureBuildTaskAction.label": "Configurar Tarefa de Compilação", + "CloseMessageAction.label": "Fechar", + "ShowTerminalAction.label": "Terminal Visualização", + "problems": "Problemas", + "manyMarkers": "99+", + "runningTasks": "Mostrar tarefas em execução", + "tasks": "Tarefas", + "TaskSystem.noHotSwap": "Alterar o mecanismo de execução de tarefa exige que a Janela seja recarregada.", + "TaskService.noBuildTask1": "Nenhuma tarefa de compilação definida. Marque uma tarefa com 'isBuildCommand' no arquivo tasks.json.", + "TaskService.noBuildTask2": "Nenhuma tarefa de compilação definida. Marque uma tarefa como um grupo 'build' no arquivo tasks.json.", + "TaskService.noTestTask1": "Nenhuma tarefa de teste definida. Marque uma tarefa com 'isTestCommand' no arquivo tasks.json.", + "TaskService.noTestTask2": "Nenhuma tarefa de teste definida. Marque uma tarefa com um grupo 'teste' no arquivo tasks.json.", + "TaskServer.noTask": "Tarefa {0} requisitada para execução não encontrada.", + "TaskService.attachProblemMatcher.continueWithout": "Continuar sem examinar a saída da tarefa", + "TaskService.attachProblemMatcher.never": "Nunca verificar a saída da tarefa", + "TaskService.attachProblemMatcher.learnMoreAbout": "Saiba mais sobre como verificar a saída de uma tarefa", + "selectProblemMatcher": "Selecione quais tipos de erros e avisos da saída da tarefa você deseja verificar", + "customizeParseErrors": "A configuração da tarefa atual tem erros. Por favor, corrija os erros primeiro antes de personalizar uma tarefa.", + "moreThanOneBuildTask": "Há muitas tarefas de compilação definidas em tasks.json. Executando a primeira.\n", + "TaskSystem.activeSame.background": "A tarefa '{0}' já está ativa e executando em segundo plano. Para finalizá-la use 'Finalizar Tarefa' no menu Tarefas.", + "TaskSystem.activeSame.noBackground": "A tarefa '{0}' já está ativa. Para finalizá-la use 'Finalizar Tarefa' no menu Tarefas.", + "TaskSystem.active": "Já existe uma tarefa sendo executada. Finalize-a antes de executar outra tarefa.", + "TaskSystem.restartFailed": "Falha ao finalizar e reiniciar a tarefa {0}", + "TaskSystem.configurationErrors": "Erro: A configuração da tarefa informada possui erros de validação e não pode ser utilizada. Por favor, corrija os erros primeiro.", + "TaskSystem.invalidTaskJson": "Erro: O conteúdo do arquivo tasks.json possui erros de sintaxe. Por favor, corrija-os antes de executar uma tarefa.\n", + "TaskSystem.runningTask": "Há uma tarefa sendo executada. Deseja finalizá-la?", + "TaskSystem.terminateTask": "&&Finalizar Tarefa", + "TaskSystem.noProcess": "A tarefa executada não existe mais. Se a tarefa produziu processos em background, finalizar o VS Code pode resultar em processos órfãos. Para evitar isso inicie o último processo em background com uma flag de espera.", + "TaskSystem.exitAnyways": "&&Sair de qualquer maneira", + "TerminateAction.label": "Finalizar Tarefa", + "TaskSystem.unknownError": "Ocorreu um erro enquanto a tarefa estava sendo executada. Verifique o log de tarefas para detalhes.", + "TaskService.noWorkspace": "Tarefas somente estão disponíveis em uma pasta da área de trabalho.", + "recentlyUsed": "tarefas recentemente utilizadas", + "configured": "tarefas configuradas", + "detected": "tarefas detectadas", + "TaskService.fetchingBuildTasks": "Buscando tarefas de compilação...", + "TaskService.noBuildTaskTerminal": "Nenhuma tarefa de compilação encontrada. Pressione 'Configurar Tarefa de Compilação' para definir um.", + "TaskService.pickBuildTask": "Selecione a tarefa de compilação para executar", + "TaskService.fetchingTestTasks": "Buscando tarefas de teste...", + "TaskService.noTestTaskTerminal": "Nenhuma tarefa de teste encontrada. Pressione 'Configurar Tarefa de Execução' para definir uma.", + "TaskService.pickTestTask": "Selecione a tarefa de teste para executar", + "TaskService.noTaskRunning": "Nenhuma tarefa está sendo executada.", + "TaskService.tastToTerminate": "Selecione a tarefa para terminar", + "TerminateAction.noProcess": "O processo executado não existe mais. Se a tarefa produziu processos em background, finalizar o VS Code pode resultar em processos órfãos.", + "TerminateAction.failed": "Falha ao finalizar a tarefa sendo executada", + "TaskService.noTaskToRestart": "Não há tarefa para reiniciar.", + "TaskService.tastToRestart": "Selecione a tarefa para reiniciar", + "TaskService.defaultBuildTaskExists": "{0} já está marcado como a tarefa de compilação padrão.", + "TaskService.pickDefaultBuildTask": "Selecione a tarefa a ser usada como a tarefa de compilação padrão", + "TaskService.defaultTestTaskExists": "{0} já está marcado como a tarefa de teste padrão.", + "TaskService.pickDefaultTestTask": "Selecione a tarefa a ser usada como a tarefa de teste padrão", + "TaskService.noTaskIsRunning": "Nenhuma tarefa em execução.", + "TaskService.pickShowTask": "Selecione a tarefa para mostrar sua saída", + "ShowLogAction.label": "Visualizar o Log de Tarefas", + "RunTaskAction.label": "Executar Tarefa", + "RestartTaskAction.label": "Reiniciar Tarefa em Execução", + "ShowTasksAction.label": "Mostrar tarefas em execução", + "BuildAction.label": "Executar Tarefa de compilação", + "TestAction.label": "Executar Tarefa de Teste", + "ConfigureDefaultBuildTask.label": "Configurar Tarefa de Compilação Padrão", + "ConfigureDefaultTestTask.label": "Configurar Tarefa de Teste Padrão", + "quickOpen.task": "Executar Tarefa" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..8660081397 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "Tarefas" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..613bbeee70 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "Um erro desconhecido ocorreu durante a execução de uma tarefa. Consulte o log de saída de tarefa para obter detalhes.", + "TerminalTaskSystem.terminalName": "Tarefa - {0}", + "reuseTerminal": "Terminal será reutilizado pelas tarefas, pressione qualquer tecla para fechar.", + "TerminalTaskSystem": "Não é possível executar um comando shell em uma unidade UNC.", + "unkownProblemMatcher": "Problem matcher {0} não pode ser resolvido. O matcher será ignorado" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..41ad121a68 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "Executando gulp..-tarefas-simples não listam nenhuma tarefa. Você executou a instalação do npm?", + "TaskSystemDetector.noJakeTasks": "Executando jake..-tarefas não listam nenhuma tarefa. Você instalou o npm?", + "TaskSystemDetector.noGulpProgram": "Gulp não está instalado no seu sistema. Execute npm install -g gulp para instalá-lo.", + "TaskSystemDetector.noJakeProgram": "Jake não está instalado no seu sistema. Execute npm install -g jake para instalá-lo.", + "TaskSystemDetector.noGruntProgram": "Grunhido não está instalado no seu sistema. Execute npm install -g grunt para instalá-lo.", + "TaskSystemDetector.noProgram": "Programa {0} não foi encontrado. Mensagem é {1}", + "TaskSystemDetector.buildTaskDetected": "Tarefa de construção chamada '{0}' detectada.", + "TaskSystemDetector.testTaskDetected": "Tarefa de teste chamada '{0}' detectada." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..f494d55660 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "Um erro desconhecido ocorreu durante a execução de uma tarefa. Consulte o log de saída de tarefa para obter detalhes.", + "TaskRunnerSystem.watchingBuildTaskFinished": "\nTarefas de compilação de monitoramento terminaram.", + "TaskRunnerSystem.childProcessError": "Falha ao iniciar o programa externo {0} {1}.", + "TaskRunnerSystem.cancelRequested": "\nA tarefa '{0}' foi finalizada por solicitação do usuário.", + "unkownProblemMatcher": "Problema matcher {0} não pode ser resolvido. O matcher será ignorado" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..9a96d45fa6 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Aviso: options.cwd deve ser do tipo string. Ignorando valor {0}\n", + "ConfigurationParser.noargs": "Erro: Argumentos do comando devem ser uma matriz de strings. Valor informado é:\n{0}", + "ConfigurationParser.noShell": "Aviso: A configuração do shell somente é suportada quando estiver executando tarefas no terminal.", + "ConfigurationParser.noName": "Erro: Problem Matcher no escopo declarado deve ter um nome:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Aviso: a correspondência de problema definido é desconhecido. Tipos suportados são string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Erro: ProblemMatcher inválido referência: {0}\n", + "ConfigurationParser.noTaskType": "Erro: configuração de tarefas deve ter uma propriedade de tipo. A configuração será ignorada.\n{0}\n", + "ConfigurationParser.noTypeDefinition": "Erro: não há nenhum tipo de tarefa registrado '{0}'. Você esqueceu de instalar uma extensão que fornece um provedor de tarefa correspondente?", + "ConfigurationParser.notCustom": "Erro: tarefas não está declarada como uma tarefa personalizada. A configuração será ignorada.\n{0}\n", + "ConfigurationParser.noTaskName": "Erro: tarefas devem fornecer uma propriedade taskName. A tarefa será ignorada.\n{0}\n", + "taskConfiguration.shellArgs": "Aviso: a tarefa '{0}' é um comando do shell e o nome de comando ou um dos seus argumentos tem espaços sem escape. Para garantir a linha de comando correta por favor mesclar argumentos no comando.", + "taskConfiguration.noCommandOrDependsOn": "Erro: a tarefa '{0}' não especifica nem um comando nem uma propriedade dependsOn. A tarefa será ignorada. Sua definição é: \n{1}", + "taskConfiguration.noCommand": "Erro: a tarefa '{0}' não define um comando. A tarefa será ignorada. Sua definição é: {1}", + "TaskParse.noOsSpecificGlobalTasks": "Tarefa versão 2.0.0 não oferece suporte a tarefas globais específicas do sistema operacional. Converta-as em uma tarefa com um comando específico do sistema operacional. Tarefas afetadas:\n{0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..493247eee2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0}, seletor de terminal", + "termCreateEntryAriaLabel": "{0}, criar novo terminal", + "'workbench.action.terminal.newplus": "$(plus) criar novo Terminal Integrado", + "noTerminalsMatching": "Não há terminais correspondentes", + "noTerminalsFound": "Não há terminais abertos" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..c8b292b1d6 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "Mostrar Todos os Terminais Abertos", + "terminalIntegratedConfigurationTitle": "Terminal Integrado", + "terminal.integrated.shell.linux": "O caminho do shell que o terminal usa no Linux.", + "terminal.integrated.shellArgs.linux": "Os argumentos de linha de comando a serem usados no terminal do Linux.", + "terminal.integrated.shell.osx": "O caminho do shell que o terminal usa no OS X.", + "terminal.integrated.shellArgs.osx": "Os argumentos de linha de comando a serem usados no terminal do OS X.", + "terminal.integrated.shell.windows": "O caminho do shell que o terminal utiliza no Windows. Quando estiver usando shells fornecidos com o Windows (cmd, PowerShell ou Bash no Ubuntu).", + "terminal.integrated.shellArgs.windows": "Os argumentos de linha de comando a serem utilizados no terminal do Windows.", + "terminal.integrated.rightClickCopyPaste": "Quando configurado, isto evitará que o menu de contexto apareça quando pressionado o botão direito do mouse dentro do terminal, em vez disso vai copiar quando há uma seleção e colar quando não há nenhuma seleção.", + "terminal.integrated.fontFamily": "Controla a família de fontes do terminal, este padrão é o valor do editor.fontFamily.", + "terminal.integrated.fontLigatures": "Controla se as ligações de fonte são habilitadas no terminal.", + "terminal.integrated.fontSize": "Controla o tamanho da fonte em pixels do terminal.", + "terminal.integrated.lineHeight": "Controles a altura da linha do terminal, este número é multiplicada pelo tamanho da fonte terminal para obter a altura real da linha em pixels.", + "terminal.integrated.enableBold": "Se habilitar o texto em negrito dentro do terminal requer suporte do terminal shell.", + "terminal.integrated.cursorBlinking": "Controla se o cursor do terminal pisca.", + "terminal.integrated.cursorStyle": "Controla o estilo do cursor do terminal.", + "terminal.integrated.scrollback": "Controla a quantidade máxima de linhas que o terminal mantém em seu buffer.", + "terminal.integrated.setLocaleVariables": "Controla se as variáveis locais são definidas na inicialização do terminal, este padrão é verdadeiro no OS X e falso em outras plataformas.", + "terminal.integrated.cwd": "Um caminho de início explícito onde o terminal será lançado, isso é usado como o diretório de trabalho atual (cwd) para o processo shell. Isto pode ser particularmente útil em configurações de espaço de trabalho se o diretório raiz não é um cwd conveniente.", + "terminal.integrated.confirmOnExit": "Confirmar na saída se ainda houverem sessões de terminal ativas.", + "terminal.integrated.commandsToSkipShell": "Um conjunto de IDs de comando, cujas combinações de teclas não serão enviadas para o shell e sempre serão tratadas por código. Isto permite o uso de combinações de teclas que normalmente seriam consumidas pelo shell para agir da mesma forma quando o terminal não é focado, por exemplo ctrl+p para Execução Rápida.", + "terminal.integrated.env.osx": "Objeto com variáveis de ambiente que serão adicionadas ao VS Code e utilizadas pelo terminal no Mac OS X", + "terminal.integrated.env.linux": "Objeto com variáveis de ambiente que serão adicionadas ao VS Code e utilizadas pelo terminal no Linux", + "terminal.integrated.env.windows": "Objeto com variáveis de ambiente que serão adicionadas ao VS Code e utilizadas pelo terminal no Windows", + "terminal": "Terminal", + "terminalCategory": "Terminal", + "viewCategory": "Exibir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..b53f568546 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "Alternar Terminal Integrado", + "workbench.action.terminal.kill": "Finalizar a Instância de Terminal Ativa", + "workbench.action.terminal.kill.short": "Encerrar Terminal", + "workbench.action.terminal.quickKill": "Finalizar a instância do Terminal", + "workbench.action.terminal.copySelection": "Copiar Seleção", + "workbench.action.terminal.selectAll": "Selecionar Tudo", + "workbench.action.terminal.deleteWordLeft": "Excluir Palavra à Esquerda", + "workbench.action.terminal.deleteWordRight": "Excluir Palavra à Direita", + "workbench.action.terminal.new": "Criar Novo Terminal Integrado", + "workbench.action.terminal.new.short": "Novo Terminal", + "workbench.action.terminal.focus": "Focalizar Terminal", + "workbench.action.terminal.focusNext": "Focalizar Próximo Terminal", + "workbench.action.terminal.focusAtIndex": "Focalizar Terminal {0}", + "workbench.action.terminal.focusPrevious": "Focalizar Terminal Anterior", + "workbench.action.terminal.paste": "Colar no Terminal Ativo", + "workbench.action.terminal.DefaultShell": "Selecionar Shell Padrão", + "workbench.action.terminal.runSelectedText": "Executar Texto Selecionado no Terminal Ativo", + "workbench.action.terminal.runActiveFile": "Executar Arquivo Ativo no Terminal Ativo", + "workbench.action.terminal.runActiveFile.noFile": "Apenas arquivos em disco podem ser executados no terminal", + "workbench.action.terminal.switchTerminalInstance": "Trocar a instância de Terminal", + "workbench.action.terminal.scrollDown": "Rolar para Baixo (Linha)", + "workbench.action.terminal.scrollDownPage": "Rolar para Baixo (Página)", + "workbench.action.terminal.scrollToBottom": "Rolar para baixo", + "workbench.action.terminal.scrollUp": "Rolar para Cima (Linha)", + "workbench.action.terminal.scrollUpPage": "Rolar para Cima (Página)", + "workbench.action.terminal.scrollToTop": "Rolar para cima", + "workbench.action.terminal.clear": "Limpar", + "workbench.action.terminal.allowWorkspaceShell": "Permitir a Configuração de Shell da Área de Trabalho", + "workbench.action.terminal.disallowWorkspaceShell": "Não Permitir a Configuração de Shell da Área de Trabalho", + "workbench.action.terminal.rename": "Renomear", + "workbench.action.terminal.rename.prompt": "Digite o nome do terminal", + "workbench.action.terminal.focusFindWidget": "Focalizar Ferramenta de Pesquisa", + "workbench.action.terminal.hideFindWidget": "Ocultar Ferramenta de Pesquisa", + "nextTerminalFindTerm": "Mostrar Próximo Termo de Busca", + "previousTerminalFindTerm": "Mostrar Termo de Busca Anterior", + "quickOpenTerm": "Terminal: Trocar o Terminal Ativo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..304411e564 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "A cor de fundo do terminal, isso permite colorir o terminal com uma cor diferente do painel.", + "terminal.foreground": "A cor de primeiro plano do terminal.", + "terminalCursor.foreground": "A cor de primeiro plano do cursor do terminal.", + "terminalCursor.background": "A cor de fundo do cursor do terminal. Permite personalizar a cor de um personagem sobreposto por um cursor de bloco.", + "terminal.ansiColor": "'{0}' cor ansi no terminal." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..800f9598ba --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "Você permite {0} (definido como uma configuração de espaço de trabalho) a ser executado no terminal?", + "allow": "Permitir", + "disallow": "Não permitir" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..c341eb84de --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Localizar", + "placeholder.find": "Localizar", + "label.previousMatchButton": "Correspondência anterior", + "label.nextMatchButton": "Próxima correspondência", + "label.closeButton": "Fechar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..6e3ab060dd --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "O terminal não tem nenhuma seleção para copiar", + "terminal.integrated.exitedWithCode": "O processo terminal encerrado com código de saída: {0}", + "terminal.integrated.waitOnExit": "Pressione qualquer tecla para fechar o terminal", + "terminal.integrated.launchFailed": "O comando de processo de terminal '{0}{1}' falhou ao ser iniciado (código de saída: {2})" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..0ead9ed49a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "Alt + clique para seguir o link", + "terminalLinkHandler.followLinkCmd": "Cmd + clique para seguir o link", + "terminalLinkHandler.followLinkCtrl": "Ctrl + clique para seguir o link" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..a1a2328648 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "Copiar", + "createNewTerminal": "Novo Terminal", + "paste": "Colar", + "selectAll": "Selecionar Tudo", + "clear": "Limpar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..1072e79aa3 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "Você pode alterar o terminal shell padrão selecionando o botão Personalizar.", + "customize": "Personalizar", + "cancel": "Cancelar", + "never again": "Ok, Nunca Mostrar Novamente", + "terminal.integrated.chooseWindowsShell": "Selecione o seu terminal shell preferido, você pode alterar isso mais tarde em suas configurações", + "terminalService.terminalCloseConfirmationSingular": "Há uma sessão ativa de terminal, você quer finalizá-la?", + "terminalService.terminalCloseConfirmationPlural": "Existem {0} sessões ativas de terminal, você quer finalizá-las?", + "yes": "Sim" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..0e6a61db94 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "Tema de Cores", + "installColorThemes": "Instalar temas de cor adicionais...", + "themes.selectTheme": "Selecione o tema de cor (teclas cima/baixo para visualização)", + "selectIconTheme.label": "Arquivo de Ícone do Tema", + "installIconThemes": "Instalar Temas de Ícones de Arquivos Adicionais...", + "noIconThemeLabel": "Nenhum", + "noIconThemeDesc": "Desabilitar ícones de arquivos", + "problemChangingIconTheme": "Problema configurando tema de ícones: {0}", + "themes.selectIconTheme": "Selecionar Tema de Ícones de Arquivos", + "generateColorTheme.label": "Gerar Tema de Cores a Partir das Configurações Atuais", + "preferences": "Preferências", + "developer": "Desenvolvedor" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..8cac1ff16c --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "Esta área de trabalho contém configurações que só podem ser definidas nas configurações do usuário. ({0})", + "openWorkspaceSettings": "Abrir as configurações do espaço de trabalho", + "openDocumentation": "Saiba Mais", + "ignore": "Ignorar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/ptb/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..eb582bc119 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "Notas da Versão: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..a9c4d59f26 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "Notas da versão", + "updateConfigurationTitle": "Atualizar", + "updateChannel": "Configurar se você recebe atualizações automáticas de um canal de atualização. Requer uma reinicialização depois da mudança." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/ptb/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..da61e02b94 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "Atualizar Agora", + "later": "Mais tarde", + "unassigned": "Não atribuído", + "releaseNotes": "Notas da Versão", + "showReleaseNotes": "Mostrar Notas da Versão", + "downloadNow": "Baixar agora", + "read the release notes": "Bem-vindo a {0} v{1}! Gostaria de ler as Notas da Versão?", + "licenseChanged": "Nossos termos de licença mudaram, favor revisá-los.", + "license": "Ler Licença", + "neveragain": "Nunca Mostrar Novamente", + "64bitisavailable": "{0} para Windows de 64 bits está agora disponível!", + "learn more": "Saiba Mais", + "updateIsReady": "Nova atualização de {0} disponível.", + "thereIsUpdateAvailable": "Há uma atualização disponível.", + "updateAvailable": "{0} será atualizado após reiniciar.", + "noUpdatesAvailable": "Não há nenhuma atualização disponível.", + "commandPalette": "Paleta de comandos...", + "settings": "Configurações", + "keyboardShortcuts": "Atalhos de Teclado", + "selectTheme.label": "Tema de Cores", + "themes.selectIconTheme.label": "Arquivo de Ícone do Tema", + "not available": "Atualizações Indisponíveis", + "checkingForUpdates": "Verificando Atualizações...", + "DownloadUpdate": "Baixar Atualização Disponível", + "DownloadingUpdate": "Baixando Atualização...", + "InstallingUpdate": "Instalando Atualização...", + "restartToUpdate": "Reinicie para Atualizar...", + "checkForUpdates": "Verificar atualizações..." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/ptb/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..bd534a5abf --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} ações ", + "hideView": "Ocultar a Barra Lateral" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/ptb/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..756ffcc018 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "visualizações devem ser uma matriz", + "requirestring": "a propriedade `{0}` é obrigatória e deve ser do tipo `string`", + "optstring": "a propriedade `{0}` é opcional ou deve ser do tipo `string`", + "vscode.extension.contributes.view.id": "Identificador da visualiozação. Use isto para registrar um provedor de dados através de 'vscode.window.registerTreeDataProviderForView' API. Também para acionar ativando sua extensão registrando o evento 'onView: ${id}' para 'activationEvents'.", + "vscode.extension.contributes.view.name": "O nome legível da visualização. Será mostrado", + "vscode.extension.contributes.view.when": "Condição que deve ser verdadeira para mostrar esta visualização", + "vscode.extension.contributes.views": "Contribui visualizações ao editor", + "views.explorer": "Visualização do explorador", + "views.debug": "Visualizar Depurador", + "locationId.invalid": "'{0}' não é um local válido de visualização" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/ptb/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..f0e8604206 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "Mostrar todos os comandos", + "watermark.quickOpen": "Ir para o Arquivo", + "watermark.openFile": "Abrir Arquivo", + "watermark.openFolder": "Abrir Pasta", + "watermark.openFileFolder": "Abrir Arquivo ou Pasta", + "watermark.openRecent": "Abrir Recente", + "watermark.newUntitledFile": "Novo Arquivo Sem Título", + "watermark.toggleTerminal": "Alternar Terminal", + "watermark.findInFiles": "Localizar nos Arquivos", + "watermark.startDebugging": "Iniciar Depuração", + "watermark.unboundCommand": "não vinculado", + "workbenchConfigurationTitle": "Área de Trabalho", + "tips.enabled": "Quando habilitado, mostrará as dicas de marca d'água quando nenhum editor estiver aberto." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/ptb/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..6657abdabc --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "Explorador de arquivos", + "welcomeOverlay.search": "Pesquisar em arquivos", + "welcomeOverlay.git": "Gerenciamento de código fonte", + "welcomeOverlay.debug": "Executar e depurar", + "welcomeOverlay.extensions": "Gerenciar extensões", + "welcomeOverlay.problems": "Visualizar erros e avisos", + "welcomeOverlay.commandPalette": "Encontrar e executar todos os comandos", + "welcomeOverlay": "Visão geral da Interface do usuário", + "hideWelcomeOverlay": "Esconder a visão geral da Interface", + "help": "Ajuda" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..607e96c88f --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "Edição evoluiu", + "welcomePage.start": "Início", + "welcomePage.newFile": "Novo arquivo", + "welcomePage.openFolder": "Abrir pasta...", + "welcomePage.cloneGitRepository": "Clonar repositório Git...", + "welcomePage.recent": "Recente", + "welcomePage.moreRecent": "Mais...", + "welcomePage.noRecentFolders": "Não há pastas recentes", + "welcomePage.help": "Ajuda", + "welcomePage.keybindingsCheatsheet": "Folha de dicas de teclado para impressão", + "welcomePage.introductoryVideos": "Vídeos introdutórios", + "welcomePage.tipsAndTricks": "Dicas e truques", + "welcomePage.productDocumentation": "Documentação do produto", + "welcomePage.gitHubRepository": "Repositório GitHub", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "Mostrar a página de boas-vindas na inicialização", + "welcomePage.customize": "Personalizar", + "welcomePage.installExtensionPacks": "Ferramentas e linguagens", + "welcomePage.installExtensionPacksDescription": "Instalar o suporte para {0} e {1}", + "welcomePage.moreExtensions": "mais", + "welcomePage.installKeymapDescription": "Instalar atalhos de teclado", + "welcomePage.installKeymapExtension": "Instalar os atalhos de teclado de {0} e {1}", + "welcomePage.others": "outros", + "welcomePage.colorTheme": "Tema de cores", + "welcomePage.colorThemeDescription": "Fazer o editor e seu código parecer do jeito que você gosta", + "welcomePage.learn": "Aprender", + "welcomePage.showCommands": "Encontrar e executar todos os comandos", + "welcomePage.showCommandsDescription": "Comandos de acesso rápido e de pesquisa da paleta de comando ({0})", + "welcomePage.interfaceOverview": "Visão geral da interface", + "welcomePage.interfaceOverviewDescription": "Obter uma sobreposição visual, destacando os principais componentes da interface do usuário", + "welcomePage.deployToAzure": "Implantar aplicativos na nuvem", + "welcomePage.deployToAzureDescription": "Aprenda como implantar seus aplicativos Node no Serviço de aplicativo do Azure", + "welcomePage.interactivePlayground": "Playground interativo", + "welcomePage.interactivePlaygroundDescription": "Experimente as características essenciais do editor em um curto passo-a-passo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..9bd046abf2 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "Área de Trabalho", + "workbench.startupEditor.none": "Iniciar sem um editor.", + "workbench.startupEditor.welcomePage": "Abrir a página de boas-vindas (padrão).", + "workbench.startupEditor.newUntitledFile": "Abrir um novo arquivo sem título.", + "workbench.startupEditor": "Controla qual editor é mostrado na inicialização, se nenhum for restaurado a partir da sessão anterior. Selecione 'none' para iniciar sem um editor, 'welcomePage' para abrir a página de boas-vindas (padrão), 'newUntitledFile' para abrir um novo arquivo sem título (apenas quando estiver abrindo um espaço de trabalho vazio).", + "help": "Ajuda" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..1dbe6e4b13 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "Bem-vindo", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "Mostrar extensões de Azure", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "Suporte para {0} já está instalado.", + "welcomePage.willReloadAfterInstallingExtensionPack": "A janela irá recarregar depois de instalar o suporte adicional para {0}.", + "welcomePage.installingExtensionPack": "Instalando o suporte adicional para {0}...", + "welcomePage.extensionPackNotFound": "Suporte para {0} com o id {1} não pôde ser encontrado.", + "welcomePage.keymapAlreadyInstalled": "Os atalhos de teclado de {0} já estão instalados.", + "welcomePage.willReloadAfterInstallingKeymap": "A janela irá recarregar depois de instalar os {0} atalhos de teclado.", + "welcomePage.installingKeymap": "Instalando os {0} atalhos de teclado...", + "welcomePage.keymapNotFound": "Os {0} atalhos de teclado com o id {1} não podem ser encontrados.", + "welcome.title": "Bem-vindo", + "welcomePage.openFolderWithPath": "Abrir pasta {0} com o caminho {1}", + "welcomePage.extensionListSeparator": ", ", + "welcomePage.installKeymap": "Instalar {0} keymap", + "welcomePage.installExtensionPack": "Instalar o suporte adicional para {0}", + "welcomePage.installedKeymap": "Mapeamento de tecla '{0}' já está instalado.", + "welcomePage.installedExtensionPack": "Suporte {0} já está instalado.", + "ok": "OK", + "details": "Detalhes", + "cancel": "Cancelar", + "welcomePage.buttonBackground": "Cor de fundo para os botões na página de boas-vindas.", + "welcomePage.buttonHoverBackground": "Cor de fundo ao passar o mouse sobre os botões na página de boas-vindas." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..b9b41f7519 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "Playground Interativo", + "editorWalkThrough": "Playground Interativo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..64b8a1d34e --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "Playground Interativo", + "help": "Ajuda", + "interactivePlayground": "Playground Interativo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..c5b9f29f3c --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "Rolar para Cima (Linha)", + "editorWalkThrough.arrowDown": "Rolar para Baixo (Linha)", + "editorWalkThrough.pageUp": "Rolar para Cima (Página)", + "editorWalkThrough.pageDown": "Rolar para Baixo (Página)" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..df4d49434f --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "não vinculado", + "walkThrough.gitNotFound": "Parece que o Git não está instalado no seu sistema.", + "walkThrough.embeddedEditorBackground": "Cor de fundo para os editores incorporados no Playground Interativo." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/ptb/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..7ae53cb62a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "Contribui às definições de configuração.", + "vscode.extension.contributes.configuration.title": "Um resumo das configurações. Este rótulo será usado no arquivo de configurações como um comentário de separação.", + "vscode.extension.contributes.configuration.properties": "Descrição das propriedades de configuração.", + "scope.window.description": "Janela de configuração específica que pode ser configurada nas configurações do usuário ou área de trabalho.", + "scope.resource.description": "Configuração específica do recurso que pode ser configurada nas configurações do usuário, espaço de trabalho ou pasta.", + "scope.description": "Escopo em que a configuração é aplicável. Escopos disponíveis são 'janela' e 'recurso'.", + "invalid.type": "Se definido, 'configuration.type' deve ser do tipo 'object'", + "invalid.title": "'configuration.title' deve ser um string", + "vscode.extension.contributes.defaultConfiguration": "Contribui às definições de configuração padrão do editor por linguagem.", + "invalid.properties": "'configuration.properties' deve ser um objeto", + "workspaceConfig.folders.description": "Lista de pastas para ser carregada no espaço de trabalho. Deve ser um caminho para um arquivo. Por exemplo '/root/pastaA' ou './pastaA' para um caminho relativo que será determinado de acordo com o local do arquivo no espaço de trabalho.", + "workspaceConfig.folder.description": "Um caminho para um arquivo. Por exemplo, '/root /pastaA' ou './pastaA' para um caminho relativo que será determinado de acordo com o local do arquivo no espaço de trabalho.", + "workspaceConfig.settings.description": "Configurações de espaço de trabalho" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/ptb/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..403b262cb8 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Abrir configurações", + "close": "Fechar", + "saveAndRetry": "Salvar as configurações e tentar novamente", + "errorUnknownKey": "Não é possível gravar {0} porque {1} não é uma configuração registrada.", + "errorInvalidFolderConfiguration": "Não é possível gravar as configurações da pasta porque {0} não suporta o escopo de recurso de pasta.", + "errorInvalidUserTarget": "Não é possível gravar as configurações do usuário porque {0} não oferece suporte para o escopo global.", + "errorInvalidFolderTarget": "Não é possível gravar as configurações da pasta porque nenhum recurso é fornecido.", + "errorNoWorkspaceOpened": "Não é possível gravar {0} porque nenhum espaço de trabalho está aberto. Por favor, abra um espaço de trabalho primeiro e tente novamente.", + "errorInvalidConfiguration": "Não é possível gravar em configurações. Por favor abra **User Settings** para corrigir erros/avisos no arquivo e tente novamente.", + "errorInvalidConfigurationWorkspace": "Não é possível gravar em configurações. Por favor abra **Workspace Settings** para corrigir erros/avisos no arquivo e tente novamente.", + "errorConfigurationFileDirty": "Não é possível gravar em configurações, porque o arquivo foi alterado. Por favor, salve o arquivo **User Settings** e tente novamente.", + "errorConfigurationFileDirtyWorkspace": "Não é possível gravar em configurações, porque o arquivo foi alterado. Por favor, salve o arquivo **Workspace Settings** e tente novamente.", + "userTarget": "Configurações de Usuário", + "workspaceTarget": "Configurações de Espaço de Trabalho", + "folderTarget": "Configurações de pasta" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/ptb/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..871250e4be --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "Não é possível gravar no arquivo. Por favor, abra-o para corrigir seus erros/avisos e tente novamente.", + "errorFileDirty": "Não é possível gravar no arquivo porque ele foi alterado. Por favor, salve-o e tente novamente." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/ptb/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..874afd329c --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetria", + "telemetry.enableCrashReporting": "Ativar o envio de relatórios de incidentes à Microsoft. Esta opção requer reinicialização para ser efetiva." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/ptb/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..4aaf85278a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "O host de extensão não iniciou em 10 segundos, ele pode ser interrompido na primeira linha e precisa de um depurador para continuar.", + "extensionHostProcess.startupFail": "Host de extensão não começou em 10 segundos, isso pode ser um problema.", + "extensionHostProcess.error": "Erro do host de extensão: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..f40572ecff --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Falha ao analisar {0}: {1}.", + "fileReadFail": "Não foi possível ler o arquivo {0}: {1}.", + "jsonsParseFail": "Falha ao analisar {0} ou {1}: {2}.", + "missingNLSKey": "Não foi possível encontrar a mensagem para a chave {0}." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..57dcef65e8 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "Ferramentas do Desenvolvedor", + "restart": "Reinicie o Host de extensão", + "extensionHostProcess.crash": "Host de extensão foi encerrado inesperadamente.", + "extensionHostProcess.unresponsiveCrash": "Host de extensão encerrado porque não foi responsivo.", + "overwritingExtension": "Sobrescrevendo extensão {0} por {1}.", + "extensionUnderDevelopment": "Carregando extensão de desenvolvimento em {0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/ptb/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..fec8a37291 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "O Microsoft .NET Framework 4.5 é necessário. Por favor siga o link para instalá-lo.", + "installNet": "Baixar o .NET Framework 4.5", + "neverShowAgain": "Não mostrar novamente", + "trashFailed": "Falha em mover '{0}' para a lixeira" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/ptb/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..79d037ce63 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "Recurso de arquivo inválido ({0})", + "fileIsDirectoryError": "Arquivo é diretório ({0})", + "fileNotModifiedError": "Arquivo não modificado desde", + "fileTooLargeError": "Arquivo muito grande para abrir", + "fileBinaryError": "Arquivo parece ser binário e não pode ser aberto como texto", + "fileNotFoundError": "Arquivo não encontrado ({0})", + "fileMoveConflict": "Não é possível mover/copiar. Arquivo já existe no destino.", + "unableToMoveCopyError": "Não é possível mover/copiar. Arquivo poderia substituir a pasta em que está contida.", + "foldersCopyError": "Pastas não podem ser copiadas para a área de trabalho. Por favor selecione arquivos individuais para serem copiados.", + "fileModifiedError": "Arquivo Modificado Desde", + "fileReadOnlyError": "Arquivo é Somente Leitura" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/ptb/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..7ec2dd8441 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "Não é possível gravar porque o arquivo está sujo. Por favor, salve o arquivo **Keybindings** e tente novamente.", + "parseErrors": "Não é possível gravar as combinações de teclas. Por favor abra o **arquivo Keybindings** para corrigir erros/avisos no arquivo e tente novamente.", + "errorInvalidConfiguration": "Não é possível gravar as combinações de teclas. **Arquivo Keybindings** tem um objeto que não é do tipo Matriz. Por favor, abra o arquivo para limpar e tentar novamente.", + "emptyKeybindingsHeader": "Coloque suas combinações de teclas neste arquivo para substituir os padrões" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/ptb/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..dd62839523 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "Esperado um valor não vazio", + "requirestring": "a propriedade `{0}` é obrigatória e deve ser do tipo `string`", + "optstring": "a propriedade `{0}` é opcional ou pode ser do tipo `string`", + "vscode.extension.contributes.keybindings.command": "Identificador do comando a ser executado quando keybinding é acionado.", + "vscode.extension.contributes.keybindings.key": "Tecla ou sequência de teclas (teclas separadas com o sinal de adição e sequências com espaço, por exemplo Ctrl+O e Ctrl+L L).", + "vscode.extension.contributes.keybindings.mac": "Chave específica Mac ou sequência de teclas.", + "vscode.extension.contributes.keybindings.linux": "Chave específica Linux ou sequência de teclas.", + "vscode.extension.contributes.keybindings.win": "Chave específica Windows ou sequência de teclas.", + "vscode.extension.contributes.keybindings.when": "Condição quando a chave está ativa.", + "vscode.extension.contributes.keybindings": "Contribui para Atalhos de Teclado.", + "invalid.keybindings": "Inválido `contributes.{0}`: {1}", + "unboundCommands": "Aqui estão outros comandos disponíveis: ", + "keybindings.json.title": "Configuração de combinações de teclas", + "keybindings.json.key": "Tecla ou sequência de teclas (separados por espaço)", + "keybindings.json.command": "Nome do comando a ser executado", + "keybindings.json.when": "Condição quando a chave está ativa.", + "keybindings.json.args": "Argumentos a serem passados para o comando para executar.", + "keyboardConfigurationTitle": "Teclado", + "dispatch": "Controla a lógica de expedição para pressionamentos de teclas para usar `keydown.code` (recomendado) ou 'keydown.keyCode'." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/ptb/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..dd094706f0 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Erro: {0}", + "alertWarningMessage": "Aviso: {0}", + "alertInfoMessage": "Informações: {0}", + "error": "Erro", + "warning": "Aviso", + "info": "Informações", + "close": "Fechar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/ptb/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..5b1890b429 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "&&Sim", + "cancelButton": "Cancelar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/ptb/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..a0711e0e28 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Contribui às declarações de linguagem.", + "vscode.extension.contributes.languages.id": "ID da linguagem", + "vscode.extension.contributes.languages.aliases": "Aliases de nome para esta linguagem.", + "vscode.extension.contributes.languages.extensions": "Extensões de arquivos associadas a esta linguagem", + "vscode.extension.contributes.languages.filenames": "Nome dos arquivos associados a esta linguagem", + "vscode.extension.contributes.languages.filenamePatterns": "Padrão glob de nomes de arquivos associados a linguagem.", + "vscode.extension.contributes.languages.mimetypes": "Tipos Mime associados à linguagem.", + "vscode.extension.contributes.languages.firstLine": "Uma expressão regular que coincide com a primeira linha de um arquivo da linguaguem.", + "vscode.extension.contributes.languages.configuration": "Um caminho relativo para um arquivo contendo opções de configuração para a linguagem.", + "invalid": "Inválido 'contributes.{0}`. Matriz esperada.", + "invalid.empty": "Valor em branco para` contributes.{0}`", + "require.id": "a propriedade `{0}` é obrigatória e deve ser do tipo `string`", + "opt.extensions": "a propriedade `{0}` pode ser omitida e deve ser do tipo `string[]`", + "opt.filenames": "a propriedade `{0}` pode ser omitida e deve ser do tipo `string[]`", + "opt.firstLine": "a propriedade `{0}` pode ser omitida e deve ser do tipo `string`", + "opt.configuration": "a propriedade `{0}` pode ser omitida e deve ser do tipo `string`", + "opt.aliases": "a propriedade `{0}` pode ser omitida e deve ser do tipo `string[]`", + "opt.mimetypes": "a propriedade `{0}` pode ser omitida e deve ser do tipo `string[]`" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/ptb/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..441530a01b --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1}", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/ptb/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..d590e10cdf --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Contibui aos toquenizadores textmate", + "vscode.extension.contributes.grammars.language": "Identificador da linguagem para qual a sintaxe contribui.", + "vscode.extension.contributes.grammars.scopeName": "Nome do escopo Textmate usado pelo arquivo tmLanguage.", + "vscode.extension.contributes.grammars.path": "Caminho para o arquivo tmLanguage. O caminho é relativo a pasta da extensão e geralmente começa com './syntaxes/'.", + "vscode.extension.contributes.grammars.embeddedLanguages": "Um mapeamento no nome do escopo para o Id da linguagem se esta gramática contenha linguagens embutidas.", + "vscode.extension.contributes.grammars.injectTo": "Lista de nomes de escopos de linguagem aos quais esta gramática é injetada." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/ptb/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..76fd6fa1f8 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Linguagem desconhecida em `contributes.{0}.language`. Valor fornecido: {1}", + "invalid.scopeName": "Esperada uma string em 'contributes.{0}.scopeName'. Valor informado: {1}", + "invalid.path.0": "Esperada uma string em `contributes.{0}.path`. Valor informado: {1}", + "invalid.injectTo": "Valor inválido em `contributes.{0}.injectTo`. Deve ser uma matriz de nomes de escopo de idioma. Valor fornecido: {1}", + "invalid.embeddedLanguages": "Valor inválido em `contributes.{0}.embeddedLanguages`. Deve ser um objeto de mapeamento do nome do escopo para a linguagem. Valor informado: {1}", + "invalid.path.1": "É esperado que `contributes.{0}.path` ({1}) seja incluído na pasta da extensão ({2}). Isto pode tornar a extensão não portável.", + "no-tm-grammar": "Nenhuma gramática TM registrada para este idioma." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/ptb/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..475b11d4e4 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "O arquivo está alterado. Por favor, salvá-lo primeiro antes reabri-lo com outra codificação.", + "genericSaveError": "Erro ao salvar '{0}': {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/ptb/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..924cc06c1c --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "Arquivos não poderiam ser backupeados (erro: {0}), tente salvar seus arquivos para sair." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/ptb/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..a229b9a02d --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "Você quer salvar as alterações feitas para {0}?", + "saveChangesMessages": "Você quer salvar as alterações para os seguintes {0} arquivos?", + "moreFile": "... 1 arquivo adicional não está mostrado", + "moreFiles": "... {0} arquivos adicionais não estão mostrados", + "saveAll": "&&Salvar tudo", + "save": "&&Salvar", + "dontSave": "&&Não Salvar", + "cancel": "Cancelar", + "saveChangesDetail": "Suas alterações serão perdidas se você não salvá-las.", + "allFiles": "Todos os arquivos", + "noExt": "Sem extensão" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/ptb/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..40b9b758a4 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "Cores e estilos para o token.", + "schema.token.foreground": "Cor do primeiro plano para o token.", + "schema.token.fontStyle": "Estilo da fonte da regra: um estilo ou uma combinação de 'itálico', 'negrito' e 'sublinhado'", + "schema.fontStyle.error": "O estilo da fonte deve ser uma combinação de 'itálico', 'negrito' e 'sublinhado'", + "schema.properties.name": "Descrição da regra.", + "schema.properties.scope": "Seletor de escopo que bate com esta regra.", + "schema.tokenColors.path": "Caminho para um arquivo tmTheme (relativo ao arquivo atual).", + "schema.colors": "Cores para o realce de sintaxe" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/ptb/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..0f8ffbe1ed --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "O ícone de pasta para pastas expandidas. O ícone da pasta expandido é opcional. Se não definido, o ícone definido para a pasta será mostrado.", + "schema.folder": "O ícone de pasta para pastas colapsadas, e se folderExpanded não estiver definido, também para pastas expandidas.", + "schema.file": "O ícone de arquivo padrão, indicado para todos os arquivos que não correspondem a qualquer extensão, nome de arquivo ou idioma.", + "schema.folderNames": "Associa os nomes de pasta à ícones. A chave do objeto é o nome da pasta, não incluindo quaisquer segmentos de caminho. Nenhum padrão ou curinga são permitidos. O nome da pasta de correspondência diferencia maiusculas de minúsculas.", + "schema.folderName": "A ID da definição do ícone para associação.", + "schema.folderNamesExpanded": "Associa os nomes de pasta a ícones para pastas expandidas. A chave do objeto é o nome da pasta, não incluindo quaisquer segmentos do caminho. Padrões ou curingas não são permitidas. A correspondência do nome de pastas não diferencia maiusculas de minúsculas.", + "schema.folderNameExpanded": "A ID da definição do ícone para a associação.", + "schema.fileExtensions": "Associa as extensões de arquivo aos ícones. A chave do objeto é o nome da extensão do arquivo. O nome da extensão é o último segmento de um nome de arquivo após o último ponto (não incluindo o ponto). As extensões não diferenciam maiúsculas de minúsculas.", + "schema.fileExtension": "A ID da definição do ícone para a associação.", + "schema.fileNames": "Associa os nomes de arquivo à ícones. A chave do objeto é o nome completo do arquivo, mas não incluindo quaisquer segmentos do caminho. O nome do arquivo pode incluir pontos e uma extensão de arquivo. Nenhum padrão ou curinga é permitido. A correspondência de nome de arquivo não diferencia maiúsculas de minúsculas.", + "schema.fileName": "A ID da definição do ícone para a associação.", + "schema.languageIds": "Associa idiomas a ícones. A chave do objeto é o id de idioma definido no ponto de contribuição de linguagem.", + "schema.languageId": "O ID da definição do ícone para a associação.", + "schema.fonts": "Fontes que são usadas nas definições de ícone.", + "schema.id": "O ID da fonte.", + "schema.src": "A localização da fonte.", + "schema.font-path": "O caminho do fonte, relativo ao arquivo de tema de ícone atual.", + "schema.font-format": "O formato da fonte.", + "schema.font-weight": "O peso da fonte.", + "schema.font-sstyle": "O estilo da fonte.", + "schema.font-size": "O tamanho padrão da fonte.", + "schema.iconDefinitions": "Descrição de todos os ícones que podem ser usados quando associar arquivos de ícones.", + "schema.iconDefinition": "Uma definição de ícone. A chave do objeto é o ID da definição.", + "schema.iconPath": "Ao usar um SVG ou PNG: O caminho para a imagem. O caminho é relativo ao arquivo de configuração do ícone.", + "schema.fontCharacter": "Ao usar uma fonte glyph: O caractere na fonte para usar.", + "schema.fontColor": "Ao usar uma fonte glyph: A cor a ser utilizada.", + "schema.fontSize": "Quando estiver utilizando uma fonte: O tamanho da fonte em porcentagem para a fonte de texto. Se não for definido, o padrão é o tamanho na definição de fonte.", + "schema.fontId": "Quando estiver utilizando uma fonte: A identificação da fonte. Se não for definido, o padrão é a primeira definição de fonte.", + "schema.light": "Associações opcionais para ícones de arquivo em temas de cor clara.", + "schema.highContrast": "Associações opcionais para ícones de arquivo em temas de alto contraste." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..218ad2ce48 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "Problemas ao analisar o arquivo de tema JSON: {0}", + "error.invalidformat.colors": "Problema ao analisar o arquivo de tema de cor: {0}. A propriedade 'colors' não é do tipo 'object'.", + "error.invalidformat.tokenColors": "Problema ao ler o arquivo do tema de cores: {0}. Propriedade 'tokenColors' deve ser também uma matriz especificando cores ou um caminho para um arquivo de tema do TextMate", + "error.plist.invalidformat": "Problema ao analisar o arquivo tmTheme: {0}. 'settings' não é uma matriz.", + "error.cannotparse": "Problemas ao analisar o arquivo tmTheme: {0}", + "error.cannotload": "Problemas ao carregar o arquivo tmTheme {0}: {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..8f4756f4ea --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "Contribui com temas de cores do textmate.", + "vscode.extension.contributes.themes.id": "ID do tema do ícone conforme usado em configurações do usuário.", + "vscode.extension.contributes.themes.label": "Etiqueta da cor do tema como mostrado na interface do usuário.", + "vscode.extension.contributes.themes.uiTheme": "Tema base de definição das cores do editor: 'vs' é o tema de cor clara, 'vs-dark' é o tema de cor escura. 'hc preto' é o tema escuro de alto contraste.", + "vscode.extension.contributes.themes.path": "Caminho do arquivo tmTheme. O caminho é relativo à pasta de extensão e é normalmente './themes/themeFile.tmTheme'.", + "vscode.extension.contributes.iconThemes": "Contribui com temas de ícones de arquivo.", + "vscode.extension.contributes.iconThemes.id": "ID do tema do ícone como usado em configurações do usuário.", + "vscode.extension.contributes.iconThemes.label": "Etiqueta do tema do ícone como mostrado na interface do usuário.", + "vscode.extension.contributes.iconThemes.path": "Caminho do arquivo de definição do tema do ícone. O caminho é relativo à pasta de extensão e é normalmente './icons/awesome-icon-theme.json'.", + "migration.completed": "Foram adicionadas novas configurações de tema para as configurações de usuário. Backup está disponível em {0}.", + "error.cannotloadtheme": "Não é possível carregar {0}: {1}", + "reqarray": "Ponto de extensão '{0}' deve ser uma matriz.", + "reqpath": "Esperada uma string em `contributes.{0}.path`. Valor informado: {1}", + "invalid.path.1": "É esperado que `contributes.{0}.path` ({1}) seja incluído na pasta da extensão ({2}). Isto pode tornar a extensão não portável.", + "reqid": "Esperada sequência em 'contributes.{0}.ID'. Valor fornecido: {1}", + "error.cannotloadicontheme": "Não é possível carregar {0}", + "error.cannotparseicontheme": "Problemas de análise do arquivo de ícones: {0}", + "colorTheme": "Especifica o tema de cores usado no espaço de trabalho.", + "colorThemeError": "Tema é desconhecido ou não está instalado.", + "iconTheme": "Especifica o tema de ícones usado no espaço de trabalho ou 'null' para não mostrar qualquer arquivo de ícones.", + "noIconThemeDesc": "Nenhum arquivo de ícones", + "iconThemeError": "Arquivo de tema de ícones é desconhecido ou não está instalado.", + "workbenchColors": "Substitui as cores do tema do tema de cores atualmente selecionado.", + "workbenchColors.deprecated": "A configuração não é mais experimental e foi renomeada para 'workbench.colorCustomizations'", + "workbenchColors.deprecatedDescription": "Use 'workbench.colorCustomizations'", + "editorColors": "Substitui as cores e o estilo da fonte do editor do tema de cores atualmente selecionado.", + "editorColors.comments": "Define as cores e estilos para os comentários", + "editorColors.strings": "Define as cores e estilos para textos literais.", + "editorColors.keywords": "Define as cores e estilos para palavras-chave.", + "editorColors.numbers": "Define as cores e estilos para literais numéricos.", + "editorColors.types": "Define as cores e estilos para declarações de tipo e referências.", + "editorColors.functions": "Define as cores e estilos para declarações de funções e referências.", + "editorColors.variables": "Define as cores e estilos para declarações de variáveis e referências.", + "editorColors.textMateRules": "Define as cores e estilos usando regras de temas textmate (avançado)." +} \ No newline at end of file diff --git a/i18n/rus/extensions/configuration-editing/out/extension.i18n.json b/i18n/rus/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..f8ad4c5b5b --- /dev/null +++ b/i18n/rus/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "Пример" +} \ No newline at end of file diff --git a/i18n/rus/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/rus/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..2f40cb3b68 --- /dev/null +++ b/i18n/rus/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "например, myFile.txt", + "activeEditorMedium": "например, myFolder/myFile.txt", + "activeEditorLong": "например, /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "например, myFolder1, myFolder2, myFolder3", + "rootPath": "например, /Users/Development/myProject", + "folderName": "например, myFolder", + "folderPath": "например, /Users/Development/myFolder", + "appName": "например, VS Code", + "dirty": "индикатор dirty, если активный редактор является \"грязным\"", + "separator": "условный разделитель (-), который отображается, только если окружен переменными со значениями", + "assocLabelFile": "Файлы с расширением", + "assocDescriptionFile": "Сопоставить все файлы, соответствующие стандартной маске в имени файла, с языком с указанным идентификатором.", + "assocLabelPath": "Файлы с путем", + "assocDescriptionPath": "Сопоставить все файлы, соответствующие стандартной маске в абсолютном пути, с языком с указанным идентификатором.", + "fileLabel": "Файлы по расширению", + "fileDescription": "Сопоставление всех файлов с определенным расширением.", + "filesLabel": "Файлы с несколькими расширениями", + "filesDescription": "Сопоставление всех файлов с любым из расширений файлов.", + "derivedLabel": "Файлы с элементами того же уровня по имени", + "derivedDescription": "Сопоставление файлов с одноранговыми элементами с тем же именем, но разными расширениями.", + "topFolderLabel": "Папка по имени (верхний уровень)", + "topFolderDescription": "Сопоставление папки верхнего уровня с определенным именем.", + "topFoldersLabel": "Папки с несколькими именами (верхний уровень)", + "topFoldersDescription": "Сопоставление нескольких папок верхнего уровня.", + "folderLabel": "Папка по имени (любое расположение)", + "folderDescription": "Сопоставление папки с определенным именем в любом расположении.", + "falseDescription": "Отключение шаблона.", + "trueDescription": "Включение шаблона.", + "siblingsDescription": "Сопоставление файлов с одноранговыми элементами с тем же именем, но разными расширениями.", + "languageSpecificEditorSettings": "Параметры редактора, определяемые языком", + "languageSpecificEditorSettingsDescription": "Переопределить параметры редактора для языка" +} \ No newline at end of file diff --git a/i18n/rus/extensions/css/client/out/cssMain.i18n.json b/i18n/rus/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..dcb57f87a0 --- /dev/null +++ b/i18n/rus/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "Языковой сервер CSS" +} \ No newline at end of file diff --git a/i18n/rus/extensions/css/package.i18n.json b/i18n/rus/extensions/css/package.i18n.json new file mode 100644 index 0000000000..8f458016a0 --- /dev/null +++ b/i18n/rus/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "Недопустимое число параметров", + "css.lint.boxModel.desc": "Не использовать ширину или высоту при использовании поля или границы", + "css.lint.compatibleVendorPrefixes.desc": "При использовании зависящего от поставщика префикса также указывайте все остальные свойства поставщика", + "css.lint.duplicateProperties.desc": "Не использовать дублирующиеся определения стилей", + "css.lint.emptyRules.desc": "Не использовать пустые наборы правил", + "css.lint.float.desc": "Старайтесь не использовать \"float\". Из-за элементов \"float\" работа кода CSS может легко нарушиться, если изменить один из аспектов разметки.", + "css.lint.fontFaceProperties.desc": "Правило @font-face должно определять свойства src и font-family", + "css.lint.hexColorLength.desc": "Цвета в шестнадцатеричном формате должны содержать три или шесть шестнадцатеричных чисел", + "css.lint.idSelector.desc": "Селекторы не должны содержать идентификаторов, потому что эти правила слишком тесно связаны с HTML.", + "css.lint.ieHack.desc": "Полезные советы для Internet Explorer требуются только при поддержке Internet Explorer 7 и более ранних версий", + "css.lint.important.desc": "Старайтесь не использовать !important, так как это признак того, что весь код CSS стал неуправляемым и его надо переработать.", + "css.lint.importStatement.desc": "Операторы импорта не загружаются параллельно", + "css.lint.propertyIgnoredDueToDisplay.desc": "Свойство проигнорировано из-за значения свойства display. Например, при \"display: inline\" свойства width, height, margin-top, margin-bottom и float не работают", + "css.lint.universalSelector.desc": "Универсальный селектор (*) работает медленно", + "css.lint.unknownProperties.desc": "Неизвестное свойство.", + "css.lint.unknownVendorSpecificProperties.desc": "Неизвестное свойство поставщика.", + "css.lint.vendorPrefix.desc": "При использовании зависящего от поставщика префикса также указывайте стандартное свойство", + "css.lint.zeroUnits.desc": "Для нуля не требуется единица измерения", + "css.trace.server.desc": "Отслеживает обмен данными между VS Code и языковым сервером CSS.", + "css.validate.title": "Управляет проверкой CSS и серьезностью проблем.", + "css.validate.desc": "Включает или отключает все проверки", + "less.lint.argumentsInColorFunction.desc": "Недопустимое число параметров", + "less.lint.boxModel.desc": "Не использовать ширину или высоту при использовании поля или границы", + "less.lint.compatibleVendorPrefixes.desc": "При использовании зависящего от поставщика префикса также указывайте все остальные свойства поставщика", + "less.lint.duplicateProperties.desc": "Не использовать дублирующиеся определения стилей", + "less.lint.emptyRules.desc": "Не использовать пустые наборы правил", + "less.lint.float.desc": "Старайтесь не использовать \"float\". Из-за элементов \"float\" работа кода CSS может легко нарушиться, если изменить один из аспектов разметки.", + "less.lint.fontFaceProperties.desc": "Правило @font-face должно определять свойства src и font-family", + "less.lint.hexColorLength.desc": "Цвета в шестнадцатеричном формате должны содержать три или шесть шестнадцатеричных чисел", + "less.lint.idSelector.desc": "Селекторы не должны содержать идентификаторов, потому что эти правила слишком тесно связаны с HTML.", + "less.lint.ieHack.desc": "Полезные советы для Internet Explorer требуются только при поддержке Internet Explorer 7 и более ранних версий", + "less.lint.important.desc": "Старайтесь не использовать !important, так как это признак того, что весь код CSS стал неуправляемым и его надо переработать.", + "less.lint.importStatement.desc": "Операторы импорта не загружаются параллельно", + "less.lint.propertyIgnoredDueToDisplay.desc": "Свойство проигнорировано из-за значения свойства display. Например, при \"display: inline\" свойства width, height, margin-top, margin-bottom и float не работают", + "less.lint.universalSelector.desc": "Универсальный селектор (*) работает медленно", + "less.lint.unknownProperties.desc": "Неизвестное свойство.", + "less.lint.unknownVendorSpecificProperties.desc": "Неизвестное свойство поставщика.", + "less.lint.vendorPrefix.desc": "При использовании зависящего от поставщика префикса также указывайте стандартное свойство", + "less.lint.zeroUnits.desc": "Для нуля не требуется единица измерения", + "less.validate.title": "Управляет проверкой LESS и уровнями серьезности проблем. ", + "less.validate.desc": "Включает или отключает все проверки", + "scss.lint.argumentsInColorFunction.desc": "Недопустимое число параметров", + "scss.lint.boxModel.desc": "Не использовать ширину или высоту при использовании поля или границы", + "scss.lint.compatibleVendorPrefixes.desc": "При использовании зависящего от поставщика префикса также указывайте все остальные свойства поставщика", + "scss.lint.duplicateProperties.desc": "Не использовать дублирующиеся определения стилей", + "scss.lint.emptyRules.desc": "Не использовать пустые наборы правил", + "scss.lint.float.desc": "Старайтесь не использовать \"float\". Из-за элементов \"float\" работа кода CSS может легко нарушиться, если изменить один из аспектов разметки.", + "scss.lint.fontFaceProperties.desc": "Правило @font-face должно определять свойства src и font-family", + "scss.lint.hexColorLength.desc": "Цвета в шестнадцатеричном формате должны содержать три или шесть шестнадцатеричных чисел", + "scss.lint.idSelector.desc": "Селекторы не должны содержать идентификаторов, потому что эти правила слишком тесно связаны с HTML.", + "scss.lint.ieHack.desc": "Полезные советы для Internet Explorer требуются только при поддержке Internet Explorer 7 и более ранних версий", + "scss.lint.important.desc": "Старайтесь не использовать !important, так как это признак того, что весь код CSS стал неуправляемым и его надо переработать.", + "scss.lint.importStatement.desc": "Операторы импорта не загружаются параллельно", + "scss.lint.propertyIgnoredDueToDisplay.desc": "Свойство проигнорировано из-за значения свойства display. Например, при \"display: inline\" свойства width, height, margin-top, margin-bottom и float не работают", + "scss.lint.universalSelector.desc": "Универсальный селектор (*) работает медленно", + "scss.lint.unknownProperties.desc": "Неизвестное свойство.", + "scss.lint.unknownVendorSpecificProperties.desc": "Неизвестное свойство поставщика.", + "scss.lint.vendorPrefix.desc": "При использовании зависящего от поставщика префикса также указывайте стандартное свойство", + "scss.lint.zeroUnits.desc": "Для нуля не требуется единица измерения", + "scss.validate.title": "Управляет проверкой SCSS и уровнями серьезности проблем.", + "scss.validate.desc": "Включает или отключает все проверки", + "less.colorDecorators.enable.desc": "Включает или отключает декораторы цвета.", + "scss.colorDecorators.enable.desc": "Включает или отключает декораторы цвета.", + "css.colorDecorators.enable.desc": "Включает или отключает декораторы цвета.", + "css.colorDecorators.enable.deprecationMessage": "Параметр \"css.colorDecorators.enable\" устарел. Теперь вместо него используется параметр \"editor.colorDecorators\". ", + "scss.colorDecorators.enable.deprecationMessage": "Параметр \"scss.colorDecorators.enable\" устарел. Теперь вместо него используется параметр \"editor.colorDecorators\". ", + "less.colorDecorators.enable.deprecationMessage": "Параметр \"less.colorDecorators.enable\" устарел. Теперь вместо него используется параметр \"editor.colorDecorators\". " +} \ No newline at end of file diff --git a/i18n/rus/extensions/emmet/package.i18n.json b/i18n/rus/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..bf84c809b6 --- /dev/null +++ b/i18n/rus/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "Перенести с сокращением", + "command.wrapIndividualLinesWithAbbreviation": "Перенести отдельные строки с сокращением", + "command.removeTag": "Удалить тег", + "command.updateTag": "Обновить тег", + "command.matchTag": "Перейти к соответствующей паре", + "command.balanceIn": "Баланс (входящий)", + "command.balanceOut": "Баланс (исходящий)", + "command.prevEditPoint": "Перейти к предыдущей точке изменения", + "command.nextEditPoint": "Перейти к следующей точке изменения", + "command.mergeLines": "Объединить строки", + "command.selectPrevItem": "Выбрать предыдущий элемент", + "command.selectNextItem": "Выбрать следующий элемент", + "command.splitJoinTag": "Разделить/объединить тег", + "command.toggleComment": "Переключить комментарий", + "command.evaluateMathExpression": "Вычислить математическое выражение", + "command.updateImageSize": "Обновить размер изображения", + "command.reflectCSSValue": "Отражать значение CSS", + "command.incrementNumberByOne": "Увеличить значение на 1", + "command.decrementNumberByOne": "Уменьшить значение на 1", + "command.incrementNumberByOneTenth": "Увеличить значение на 0.1", + "command.decrementNumberByOneTenth": "Уменьшить значение на 0.1", + "command.incrementNumberByTen": "Увеличить значение на 10", + "command.decrementNumberByTen": "Уменьшить значение на 10", + "emmetSyntaxProfiles": "Задайте профиль для указанного синтаксиса или используйте свой собственный профиль с определенными правилами.", + "emmetExclude": "Массив языков, в которых не должны развертываться сокращения Emmet.", + "emmetExtensionsPath": "Путь к папке, содержащей профили Emmet и фрагменты кода.", + "emmetShowExpandedAbbreviation": "Отображает развернутые сокращения Emmet в виде предложений.\nПараметр \"inMarkupAndStylesheetFilesOnly\" применяется к html, haml, jade, slim, xml, xsl, css, scss, sass, less и stylus .\nПараметр \"always\" применяется ко всем частям файла независимо от разметки и стилей.", + "emmetShowAbbreviationSuggestions": "Отображает возможные сокращения Emmet в виде предложений. Не применяется в таблицах стилей или если параметр emmet.showExpandedAbbreviation имеет значение \"never\".", + "emmetIncludeLanguages": "Включает сокращения Emmet в языках, которые не поддерживаются по умолчанию. Здесь можно указать связь между не поддерживаемым и поддерживаемым языками.\nПример: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "Переменные, которые будут использоваться во фрагментах Emmet", + "emmetTriggerExpansionOnTab": "Если включено, сокращения Emmet разворачиваются при нажатии клавиши TAB.", + "emmetPreferences": "Настройки, которые используются для изменения поведения некоторых действий и сопоставителей Emmet.", + "emmetPreferencesIntUnit": "Единица по умолчанию для целочисленных значений", + "emmetPreferencesFloatUnit": "Единица по умолчанию для значений с плавающей запятой", + "emmetPreferencesCssAfter": "Символ, который будет помещен в конце свойства CSS при развертывании сокращений CSS", + "emmetPreferencesSassAfter": "Символ, который будет помещен в конце свойства CSS при развертывании сокращений CSS в файлах Sass", + "emmetPreferencesStylusAfter": "Символ, который будет помещен в конце свойства CSS при развертывании сокращений CSS в файлах Stylus", + "emmetPreferencesCssBetween": "Символ, который будет помещен между свойством CSS и значением при развертывании сокращений CSS", + "emmetPreferencesSassBetween": "Символ, который будет помещен между свойством CSS и значением при развертывании сокращений CSS в файлах Sass", + "emmetPreferencesStylusBetween": "Символ, который будет помещен между свойством CSS и значением при развертывании сокращений CSS в файлах Stylus", + "emmetShowSuggestionsAsSnippets": "Если этот параметр имеет значение true, предложения Emmet будут отображаться в виде фрагментов, которые можно упорядочить с помощью параметра editor.snippetSuggestions." +} \ No newline at end of file diff --git a/i18n/rus/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/rus/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..3df7818ea3 --- /dev/null +++ b/i18n/rus/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "Изображения должны использовать протокол HTTPS.", + "svgsNotValid": "Файлы SVG не являются допустимым источником изображений.", + "embeddedSvgsNotValid": "Встроенные файлы SVG не являются допустимым источником изображений.", + "dataUrlsNotValid": "URL-адреса данных не являются допустимым источником изображений.", + "relativeUrlRequiresHttpsRepository": "Для использования относительных URL-адресов изображений необходимо указать в файле package.json репозиторий, обращение к которому осуществляется по протоколу HTTPS.", + "relativeIconUrlRequiresHttpsRepository": "Для этого значка необходимо указать в файле package.json репозиторий, обращение к которому осуществляется по протоколу HTTPS.", + "relativeBadgeUrlRequiresHttpsRepository": "Для использования относительных URL-адресов эмблем необходимо указать в файле package.json репозиторий, обращение к которому осуществляется по протоколу HTTPS. " +} \ No newline at end of file diff --git a/i18n/rus/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/rus/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..f336d3cccc --- /dev/null +++ b/i18n/rus/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "Параметры редактора, определяемые языком", + "languageSpecificEditorSettingsDescription": "Переопределить параметры редактора для языка" +} \ No newline at end of file diff --git a/i18n/rus/extensions/git/out/askpass-main.i18n.json b/i18n/rus/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..4d66613730 --- /dev/null +++ b/i18n/rus/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "Учетные данные отсутствуют или недопустимы." +} \ No newline at end of file diff --git a/i18n/rus/extensions/git/out/commands.i18n.json b/i18n/rus/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..262cbc5b5b --- /dev/null +++ b/i18n/rus/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "Тег в {0}", + "remote branch at": "Удаленная ветвь в {0}", + "create branch": "$(plus) Создать новую ветвь", + "repourl": "URL-адрес репозитория", + "parent": "Родительский каталог", + "cloning": "Клонируется репозиторий Git...", + "openrepo": "Открыть репозиторий", + "proposeopen": "Вы хотите открыть клонированный репозиторий?", + "path to init": "Путь к папке", + "provide path": "Укажите путь к папке для инициализации репозитория Git", + "HEAD not available": "Версия HEAD '{0}' недоступна.", + "confirm stage files with merge conflicts": "Вы действительно хотите перенести в промежуточный этап файлы с конфликтами слияния ({0})?", + "confirm stage file with merge conflicts": "Вы действительно хотите перенести в промежуточный этап файлы с конфликтами слияния ({0})?", + "yes": "Да", + "confirm revert": "Вы действительно хотите отменить выбранные изменения в {0}?", + "revert": "Отменить изменения", + "discard": "Отменить изменения", + "confirm delete": "Вы действительно хотите удалить {0}?", + "delete file": "Удалить файл", + "confirm discard": "Вы действительно хотите отменить изменения в {0}?", + "confirm discard multiple": "Вы действительно хотите отменить изменения в файлах ({0})?", + "warn untracked": "Это приведет к удалению неотслеживаемых файлов ({0})!", + "confirm discard all single": "Вы действительно хотите отменить изменения в {0}?", + "confirm discard all": "Вы действительно хотите отменить все изменения в {0} файлах?\nОтменить эти действия нельзя!\nВаши текущие изменения будут утеряны.", + "discardAll multiple": "Отменить изменения в одном файле", + "discardAll": "Отменить изменения во всех файлах ({0})", + "confirm delete multiple": "Вы действительно хотите удалить файлы ({0})?", + "delete files": "Удалить файлы", + "there are untracked files single": "При отмене изменений для этого неотслеживаемого файла этот файл будет удален с диска: {0}.", + "there are untracked files": "Существует {0} неотслеживаемых файлов, которые будут удалены с диска в случае отмены изменений.", + "confirm discard all 2": "{0}\n\nОтменить это действие нельзя, текущие изменения будут утеряны.", + "yes discard tracked": "Отменить изменения для отслеживаемого файла", + "yes discard tracked multiple": "Отменить изменения для отслеживаемых файлов ({0})", + "no staged changes": "Отсутствуют промежуточные изменения для фиксации.\n\nВы хотите сделать все изменения промежуточными и зафиксировать их напрямую?", + "always": "Всегда", + "no changes": "Нет изменений для фиксации.", + "commit message": "Сообщение о фиксации", + "provide commit message": "Введите сообщение фиксации.", + "select a ref to checkout": "Выберите ссылку для подсчета", + "branch name": "Имя ветви", + "provide branch name": "Укажите имя ветви.", + "select branch to delete": "Выберите ветвь для удаления", + "confirm force delete branch": "Ветвь '{0}' объединена не полностью. Удалить ее?", + "delete branch": "Удалить ветвь", + "select a branch to merge from": "Выберите ветвь для слияния", + "merge conflicts": "Обнаружены конфликты слияния. Устраните их перед фиксацией.", + "tag name": "Имя тега", + "provide tag name": "Укажите имя тега", + "tag message": "Сообщение", + "provide tag message": "Укажите сообщение для аннотирования тега", + "no remotes to pull": "Для вашего репозитория не настроены удаленные репозитории для получения данных.", + "pick remote pull repo": "Выберите удаленный компьютер, с которого следует загрузить ветвь", + "no remotes to push": "Для вашего репозитория не настроены удаленные репозитории для отправки данных.", + "push with tags success": "Файлы с тегами успешно отправлены.", + "nobranch": "Извлеките ветвь, чтобы передать данные в удаленный репозиторий.", + "pick remote": "Выберите удаленный сервер, на котором нужно опубликовать ветвь \"{0}\":", + "sync is unpredictable": "Это действие отправляет фиксации в \"{0}\" и извлекает их из этого расположения.", + "ok": "ОК", + "never again": "ОК. Больше не показывать", + "no remotes to publish": "Для вашего репозитория не настроены удаленные репозитории для публикации.", + "no changes stash": "Отсутствуют изменения, которые необходимо спрятать.", + "provide stash message": "Укажите сообщение о скрытии", + "stash message": "Сообщение о скрытии", + "no stashes": "Отсутствуют спрятанные изменения, которые необходимо восстановить.", + "pick stash to pop": "Выберите спрятанное изменение для отображения", + "clean repo": "Очистите рабочее дерево репозитория перед извлечением.", + "cant push": "Не удается отправить ссылки в удаленную ветвь. Сначала выберите \"Извлечь\", чтобы интегрировать изменения.", + "git error details": "Git: {0}", + "git error": "Ошибка Git", + "open git log": "Открыть журнал GIT" +} \ No newline at end of file diff --git a/i18n/rus/extensions/git/out/main.i18n.json b/i18n/rus/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..6b0b40c2e8 --- /dev/null +++ b/i18n/rus/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "Использование GIT {0} из {1}", + "updateGit": "Обновить Git", + "neverShowAgain": "Больше не показывать", + "git20": "У вас установлен Git {0}. Код лучше всего работает с Git >= 2." +} \ No newline at end of file diff --git a/i18n/rus/extensions/git/out/model.i18n.json b/i18n/rus/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..48d93b2366 --- /dev/null +++ b/i18n/rus/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "Доступные репозитории отсутствуют", + "pick repo": "Выберите репозиторий" +} \ No newline at end of file diff --git a/i18n/rus/extensions/git/out/repository.i18n.json b/i18n/rus/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..4a120d61a4 --- /dev/null +++ b/i18n/rus/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Открыть", + "index modified": "Индекс изменен", + "modified": "Изменено", + "index added": "Индекс добавлен", + "index deleted": "Индекс удален", + "deleted": "Удалено", + "index renamed": "Индекс переименован", + "index copied": "Индекс скопирован", + "untracked": "Не отслеживается", + "ignored": "Проигнорировано", + "both deleted": "Удалено обеими сторонами", + "added by us": "Добавлено нами", + "deleted by them": "Удалено ими", + "added by them": "Добавлено ими", + "deleted by us": "Удалено нами", + "both added": "Добавлено обеими сторонами", + "both modified": "Изменено обеими сторонами", + "commit": "Commit", + "merge changes": "Объединить изменения", + "staged changes": "Промежуточно сохраненные изменения", + "changes": "Изменения", + "ok": "ОК", + "neveragain": "Больше не показывать", + "huge": "Репозиторий git в '{0}' имеет очень много активных изменений, только часть функций Git будет доступна." +} \ No newline at end of file diff --git a/i18n/rus/extensions/git/out/scmProvider.i18n.json b/i18n/rus/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..a7e7efcb61 --- /dev/null +++ b/i18n/rus/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "Commit" +} \ No newline at end of file diff --git a/i18n/rus/extensions/git/out/statusbar.i18n.json b/i18n/rus/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..c3fce76df6 --- /dev/null +++ b/i18n/rus/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "Извлечь...", + "sync changes": "Синхронизировать изменения", + "publish changes": "Опубликовать изменения", + "syncing changes": "Синхронизация изменений..." +} \ No newline at end of file diff --git a/i18n/rus/extensions/git/package.i18n.json b/i18n/rus/extensions/git/package.i18n.json new file mode 100644 index 0000000000..23a4d66312 --- /dev/null +++ b/i18n/rus/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "Клонировать", + "command.init": "Инициализировать репозиторий", + "command.close": "Закрыть репозиторий", + "command.refresh": "Обновить", + "command.openChange": "Открыть изменения", + "command.openFile": "Открыть файл", + "command.openHEADFile": "Открыть файл (заголовок)", + "command.stage": "Хранить промежуточные изменения", + "command.stageAll": "Хранить все промежуточные изменения", + "command.stageSelectedRanges": "Разместить выбранные диапазоны", + "command.revertSelectedRanges": "Отменить выбранные диапазоны", + "command.unstage": "Отменить хранение промежуточных изменений", + "command.unstageAll": "Отменить хранение всех промежуточных изменений", + "command.unstageSelectedRanges": "Отменить размещение выбранных диапазонов", + "command.clean": "Отменить изменения", + "command.cleanAll": "Отменить все изменения", + "command.commit": "Commit", + "command.commitStaged": "Зафиксировать промежуточно сохраненные изменения", + "command.commitStagedSigned": "Зафиксировать промежуточные элементы (завершено)", + "command.commitStagedAmend": "Зафиксировать промежуточные (изменение)", + "command.commitAll": "Зафиксировать все", + "command.commitAllSigned": "Зафиксировать все (завершено)", + "command.commitAllAmend": "Зафиксировать все (изменение)", + "command.undoCommit": "Отменить последнюю фиксацию", + "command.checkout": "Извлечь в...", + "command.branch": "Создать ветвь...", + "command.deleteBranch": "Удалить ветвь...", + "command.merge": "Объединить ветвь...", + "command.createTag": "Создать тег", + "command.pull": "Получить", + "command.pullRebase": "Получить (переместить изменения из одной ветви в другую)", + "command.pullFrom": "Загрузить с...", + "command.push": "Отправить", + "command.pushTo": "Отправить в:", + "command.pushWithTags": "Отправить с тегами", + "command.sync": "Синхронизация", + "command.publish": "Опубликовать ветвь", + "command.showOutput": "Показать выходные данные GIT", + "command.ignore": "Добавить файл в .gitignore", + "command.stash": "Спрятать", + "command.stashPop": "Извлечь спрятанное", + "command.stashPopLatest": "Извлечь последнее спрятанное", + "config.enabled": "Включен ли GIT", + "config.path": "Путь к исполняемому файлу GIT", + "config.autorefresh": "Включено ли автоматическое обновление", + "config.autofetch": "Включено ли автоматическое получение", + "config.enableLongCommitWarning": "Следует ли предупреждать о длинных сообщениях о фиксации", + "config.confirmSync": "Подтвердите синхронизацию репозиториев GIT.", + "config.countBadge": "\nУправляет счетчиком Git. При указании значения \"all\" подсчитываются все изменения, при указании значения \"tracked\" — только отслеживаемые изменения, при указании значения \"off\" счетчик отключается.", + "config.checkoutType": "Определяет типы ветвей, которые выводятся при выборе пункта меню \"Извлечь в...\". При указании значения \"all\" отображаются все ссылки, \"local\" — только локальные ветви, \"tags\" — только теги, а \"remote\" — только удаленные ветви.", + "config.ignoreLegacyWarning": "Игнорирует предупреждение об устаревшей версии Git", + "config.ignoreLimitWarning": "Игнорировать предупреждение, когда в репозитории слишком много изменений", + "config.defaultCloneDirectory": "Расположение по умолчанию, в которое будет клонирован репозиторий Git", + "config.enableSmartCommit": "Зафиксировать все изменения при отсутствии промежуточных изменений.", + "config.enableCommitSigning": "Включает подписывание фиксации с GPG.", + "config.discardAllScope": "Определяет, какие изменения отменяются с помощью команды \"Отменить все изменения\". При указании значения \"all\" отменяются все изменения, при указании значения \"tracked\" отменяются изменения только в отслеживаемых файлах, при указании значения \"prompt\" отображается окно подтверждения каждый раз при выполнении действия." +} \ No newline at end of file diff --git a/i18n/rus/extensions/grunt/out/main.i18n.json b/i18n/rus/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..6616f2b74e --- /dev/null +++ b/i18n/rus/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Сбой автоматического определений заданий Grunt. Ошибка: {0}" +} \ No newline at end of file diff --git a/i18n/rus/extensions/grunt/package.i18n.json b/i18n/rus/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..01c12de197 --- /dev/null +++ b/i18n/rus/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "Включает или отключает автоматическое определние заданий Grunt. Значение по умолчанию — \"включено\"." +} \ No newline at end of file diff --git a/i18n/rus/extensions/gulp/out/main.i18n.json b/i18n/rus/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..d9528cd8bc --- /dev/null +++ b/i18n/rus/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Автообнаружение галпа с ошибкой {0}" +} \ No newline at end of file diff --git a/i18n/rus/extensions/gulp/package.i18n.json b/i18n/rus/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..f0e38b2542 --- /dev/null +++ b/i18n/rus/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "Включает или отключает автоматическое определение заданий Gulp. Значение по умолчанию — \"включено\"." +} \ No newline at end of file diff --git a/i18n/rus/extensions/html/client/out/htmlMain.i18n.json b/i18n/rus/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..4c03b49adc --- /dev/null +++ b/i18n/rus/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "Языковой сервер HTML" +} \ No newline at end of file diff --git a/i18n/rus/extensions/html/package.i18n.json b/i18n/rus/extensions/html/package.i18n.json new file mode 100644 index 0000000000..11a6a2ce0c --- /dev/null +++ b/i18n/rus/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "Включить/отключить модуль форматирования HTML по умолчанию", + "html.format.wrapLineLength.desc": "Максимальное число символов на строку (0 — отключить).", + "html.format.unformatted.desc": "Список тегов, которые не следует повторно форматировать, с разделителями-запятыми. Значение \"NULL\" по умолчанию означает все теги, перечисленные на странице https://www.w3.org/TR/html5/dom.html#phrasing-content.", + "html.format.contentUnformatted.desc": "Разделенный запятыми список тегов, в которых формат содержимого не должен изменяться. Значение null задается по умолчанию для тега pre.", + "html.format.indentInnerHtml.desc": "Отступ в разделах и .", + "html.format.preserveNewLines.desc": "Следует ли сохранять разрывы строк перед элементами. Работает только перед элементами, а не внутри тегов или для текста.", + "html.format.maxPreserveNewLines.desc": "Максимальное число разрывов строк для сохранения в блоке. Чтобы указать неограниченное число строк, используйте \"null\".", + "html.format.indentHandlebars.desc": "Формат и отступ {{#foo}} и {{/foo}}.", + "html.format.endWithNewline.desc": "Завершение символом новой строки.", + "html.format.extraLiners.desc": "Список тегов с разделителями-запятыми и дополнительными новыми строками между ними. Значение \"null\" по умолчанию ставится для \"head, body, /html\".", + "html.format.wrapAttributes.desc": "Перенос атрибутов.", + "html.format.wrapAttributes.auto": "Перенос атрибутов только при превышении длины строки.", + "html.format.wrapAttributes.force": "Перенос всех атрибутов, кроме первого.", + "html.format.wrapAttributes.forcealign": "Перенос всех атрибутов, кроме первого, и сохранение выравнивания.", + "html.format.wrapAttributes.forcemultiline": "Перенос всех атрибутов.", + "html.suggest.angular1.desc": "Определяет, будет ли встроенная поддержка языка HTML предлагать теги и свойства Angular 1.", + "html.suggest.ionic.desc": "Определяет, будет ли встроенная поддержка языка HTML предлагать теги, свойства и значения Ionic.", + "html.suggest.html5.desc": "Определяет, будет ли встроенная поддержка языка HTML предлагать теги, свойства и значения HTML5.", + "html.trace.server.desc": "Отслеживает обмен данными между VS Code и языковым сервером HTML. ", + "html.validate.scripts": "Определяет, будет ли встроенная поддержка языка HTML проверять внедренные сценарии.", + "html.validate.styles": "Определяет, будет ли встроенная поддержка языка HTML проверять внедренные стили.", + "html.autoClosingTags": "Включить или отключить автоматическое закрытие тегов HTML. " +} \ No newline at end of file diff --git a/i18n/rus/extensions/jake/out/main.i18n.json b/i18n/rus/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..1c09af9f35 --- /dev/null +++ b/i18n/rus/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Сбой автоматического определений заданий Jake. Ошибка: {0}" +} \ No newline at end of file diff --git a/i18n/rus/extensions/jake/package.i18n.json b/i18n/rus/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..4201ec9310 --- /dev/null +++ b/i18n/rus/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "Включает или отключает автоматическое определение заданий Jake. Значение по умолчанию — \"включено\"." +} \ No newline at end of file diff --git a/i18n/rus/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/rus/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..c6cda36ba1 --- /dev/null +++ b/i18n/rus/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "Bower.json по умолчанию", + "json.bower.error.repoaccess": "Сбой запроса в репозиторий Bower: {0}", + "json.bower.latest.version": "последняя" +} \ No newline at end of file diff --git a/i18n/rus/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/rus/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..e443243568 --- /dev/null +++ b/i18n/rus/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "Файл package.json по умолчанию", + "json.npm.error.repoaccess": "Сбой запроса в репозиторий NPM: {0}", + "json.npm.latestversion": "Последняя версия пакета на данный момент", + "json.npm.majorversion": "Соответствует последнему основному номеру версии (1.x.x).", + "json.npm.minorversion": "Соответствует последнему дополнительному номеру версии (1.2.x).", + "json.npm.version.hover": "Последняя версия: {0}" +} \ No newline at end of file diff --git a/i18n/rus/extensions/json/client/out/jsonMain.i18n.json b/i18n/rus/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..4941f15a31 --- /dev/null +++ b/i18n/rus/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "Языковой сервер JSON" +} \ No newline at end of file diff --git a/i18n/rus/extensions/json/package.i18n.json b/i18n/rus/extensions/json/package.i18n.json new file mode 100644 index 0000000000..12698d5bf1 --- /dev/null +++ b/i18n/rus/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "Связь схем с JSON-файлами в текущем проекте", + "json.schemas.url.desc": "URL-адрес схемы или относительный путь к ней в текущем каталоге", + "json.schemas.fileMatch.desc": "Массив шаблонов файлов, с которым выполняется сравнение, при разрешении JSON-файлов в схемах.", + "json.schemas.fileMatch.item.desc": "Шаблон файла, который может содержать \"*\" и с которым выполняется сравнение, при разрешении JSON-файлов в схемах.", + "json.schemas.schema.desc": "Определение схемы для указанного URL-адреса. Схему необходимо указать только для того, чтобы не обращаться по URL-адресу схемы.", + "json.format.enable.desc": "Включение или отключение модуля форматирования JSON по умолчанию (требуется перезагрузка)", + "json.tracing.desc": "Отслеживает связь между VS Code и языковым сервером JSON.", + "json.colorDecorators.enable.desc": "Включает или отключает декораторы цвета.", + "json.colorDecorators.enable.deprecationMessage": "Параметр \"json.colorDecorators.enable\" устарел. Теперь вместо него используется параметр \"editor.colorDecorators\"." +} \ No newline at end of file diff --git a/i18n/rus/extensions/markdown/out/extension.i18n.json b/i18n/rus/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..6060641166 --- /dev/null +++ b/i18n/rus/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "Не удалось загрузить 'markdown.styles': {0}" +} \ No newline at end of file diff --git a/i18n/rus/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/rus/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..c83e6a9341 --- /dev/null +++ b/i18n/rus/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "Некоторое содержимое в этом документе было отключено", + "preview.securityMessage.title": "В предварительном просмотре Markdown было отключено потенциально опасное или ненадежное содержимое. Чтобы разрешить ненадежное содержимое или включить сценарии, измените параметры безопасности предварительного просмотра Markdown.", + "preview.securityMessage.label": "Предупреждение безопасности об отключении содержимого" +} \ No newline at end of file diff --git a/i18n/rus/extensions/markdown/out/security.i18n.json b/i18n/rus/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..5ccaac049e --- /dev/null +++ b/i18n/rus/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "Строгий", + "strict.description": "Загружать только безопасное содержимое", + "insecureContent.title": "Разрешить небезопасное содержимое", + "insecureContent.description": "Включить загрузку содержимого через HTTP", + "disable.title": "Отключить", + "disable.description": "Разрешить все содержимое и выполнение сценариев. Не рекомендуется", + "moreInfo.title": "Дополнительные сведения", + "preview.showPreviewSecuritySelector.title": "Установите параметры безопасности для предварительного просмотра Markdown в этой рабочей области" +} \ No newline at end of file diff --git a/i18n/rus/extensions/markdown/package.i18n.json b/i18n/rus/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..966dee11c3 --- /dev/null +++ b/i18n/rus/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "Определяет, как переносы строк отображаются в области предварительного просмотра файла Markdown. Если установить этот параметр в значение 'true',
будут создаваться для каждой новой строки.", + "markdown.preview.linkify": "Включить или отключить преобразование текста в URL для предварительного просмотра Markdown.", + "markdown.preview.doubleClickToSwitchToEditor.desc": "Двойной щелчок в области предварительного просмотра Markdown в редакторе.", + "markdown.preview.fontFamily.desc": "Определяет семейство шрифтов, используемое в области предварительного просмотра файла Markdown.", + "markdown.preview.fontSize.desc": "Определяет размер шрифта (в пикселях), используемый в области предварительного просмотра файла Markdown.", + "markdown.preview.lineHeight.desc": "Определяет высоту строки, используемую в области предварительного просмотра файла Markdown. Это значение задается относительно размера шрифта.", + "markdown.preview.markEditorSelection.desc": "Выделение выбранного в текущем редакторе в предпросмотре Markdown.", + "markdown.preview.scrollEditorWithPreview.desc": "При прокрутке предпросмотра Markdown обновите представление в редакторе.", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "Прокрутка предпросмотра Markdown до выбранной строки в редакторе.", + "markdown.preview.title": "Открыть область предварительного просмотра", + "markdown.previewFrontMatter.dec": "Определяет обработку титульных листов YAML в области предварительного просмотра файла Markdown. Значение \"скрыть\" удаляет титульные листы. В противном случае титульные листы обрабатываются как содержимое файла Markdown.", + "markdown.previewSide.title": "Открыть область предварительного просмотра сбоку", + "markdown.showSource.title": "Показать источник", + "markdown.styles.dec": "Список URL-адресов или локальных путей к таблицам стилей CSS, используемых из области предварительного просмотра файла Markdown. Относительные пути интерпретируются относительно папки, открытой в проводнике. Если папка не открыта, они интерпретируются относительно расположения файла Markdown. Все символы '\\' должны записываться в виде '\\\\'.", + "markdown.showPreviewSecuritySelector.title": "Изменить параметры безопасности для предварительного просмотра", + "markdown.trace.desc": "Включить ведение журнала отладки для расширения Markdown.", + "markdown.refreshPreview.title": "Обновить предварительный просмотр" +} \ No newline at end of file diff --git a/i18n/rus/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/rus/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..a9c1aea386 --- /dev/null +++ b/i18n/rus/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "Принять текущее изменение", + "acceptIncomingChange": "Принять входящее изменение", + "acceptBothChanges": "Принять оба изменения", + "compareChanges": "Сравнить изменения" +} \ No newline at end of file diff --git a/i18n/rus/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/rus/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..a205b638c4 --- /dev/null +++ b/i18n/rus/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "Курсор не находится на конфликте объединения", + "compareChangesTitle": "{0}: текущие изменения ⟷ входящие изменения", + "cursorOnCommonAncestorsRange": "Курсор редактора находится в блоке общих предков. Переместите его в блок \"Текущее\" или \"Входящее\"", + "cursorOnSplitterRange": "Курсор редактора находится на разделителе блока объединения конфликтов. Переместите его в блок \"Текущее\" или \"Входящее\"", + "noConflicts": "Конфликтов объединения в этом файле не обнаружено", + "noOtherConflictsInThisFile": "Других конфликтов объединения в этом файле не обнаружено" +} \ No newline at end of file diff --git a/i18n/rus/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/rus/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..5d119f57d1 --- /dev/null +++ b/i18n/rus/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(текущее изменение)", + "incomingChange": "(входящее изменение)" +} \ No newline at end of file diff --git a/i18n/rus/extensions/merge-conflict/package.i18n.json b/i18n/rus/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..086bfc3a66 --- /dev/null +++ b/i18n/rus/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "Объединить конфликт", + "command.accept.all-incoming": "Принять все входящие", + "command.accept.all-both": "Принять все входящие и текущие", + "command.accept.current": "Принять текущее", + "command.accept.incoming": "Принять входящее", + "command.accept.selection": "Принять выделенное", + "command.accept.both": "Принять оба", + "command.next": "Следующий конфликт", + "command.previous": "Предыдущий конфликт", + "command.compare": "Сравнить текущий конфликт", + "config.title": "Объединить конфликт", + "config.codeLensEnabled": "Включить/отключить блок объединения конфликтов CodeLens в редакторе", + "config.decoratorsEnabled": "Включить/отключить декораторы объединения конфликтов в редакторе" +} \ No newline at end of file diff --git a/i18n/rus/extensions/npm/package.i18n.json b/i18n/rus/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..d57a74231d --- /dev/null +++ b/i18n/rus/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "Включает или отключает автоматическое определение сценариев npm. Значение по умолчанию — \"включено\".", + "config.npm.runSilent": "Запускать команды npm с параметром `--silent`" +} \ No newline at end of file diff --git a/i18n/rus/extensions/php/out/features/validationProvider.i18n.json b/i18n/rus/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..ada3fffd1a --- /dev/null +++ b/i18n/rus/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "Разрешить выполнять {0} (определяется как параметр рабочей области) для обработки PHP-файлов через lint?", + "php.yes": "Разрешить", + "php.no": "Запретить", + "wrongExecutable": "Не удается проверить, так как {0} не является допустимым исполняемым PHP-файлом. Используйте параметр php.validate.executablePath, чтобы настроить исполняемый PHP-файл.", + "noExecutable": "Не удается проверить, так как не задан исполняемый PHP-файл. Используйте параметр php.validate.executablePath, чтобы настроить исполняемый PHP-файл.", + "unknownReason": "Не удалось запустить PHP-файл, используя путь {0}. Причина неизвестна." +} \ No newline at end of file diff --git a/i18n/rus/extensions/php/package.i18n.json b/i18n/rus/extensions/php/package.i18n.json new file mode 100644 index 0000000000..7c0bf821f2 --- /dev/null +++ b/i18n/rus/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "Указывает, включены ли встроенные языковые предложения для PHP. Поддержка предлагает глобальные значения и переменные PHP.", + "configuration.validate.enable": "Включение или отключение встроенной проверки PHP.", + "configuration.validate.executablePath": "Указывает на исполняемый файл PHP.", + "configuration.validate.run": "Запускается ли анализатор кода при сохранении или при печати.", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "Запретить исполняемый файл проверки PHP (определяется как параметр рабочей области)" +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/rus/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..5a5d956316 --- /dev/null +++ b/i18n/rus/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInformation": "Дополнительные сведения", + "doNotCheckAgain": "Больше не проверять", + "close": "Закрыть", + "updateTscCheck": "Значение параметра пользователя \"typescript.check.tscVersion\" изменено на false." +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/rus/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..8f34ef41df --- /dev/null +++ b/i18n/rus/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "Получение typings...", + "acquiringTypingsDetail": "Получение определений typings для IntelliSense." +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/rus/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..50ff9403b7 --- /dev/null +++ b/i18n/rus/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "Включает семантическую проверку в JavaScript файле. Необходимо расположить в самом начале файла.", + "ts-nocheck": "Отключает семантическую проверку в JavaScript файле. Необходимо расположить в самом начале файла.", + "ts-ignore": "Отключает вывод ошибок @ts-check для следующей строки файла." +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/rus/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..deeb4fe778 --- /dev/null +++ b/i18n/rus/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 реализация", + "manyImplementationLabel": "Реализации {0}", + "implementationsErrorLabel": "Не удалось определить реализации." +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/rus/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..54a86fcc08 --- /dev/null +++ b/i18n/rus/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "Комментарий JSDoc" +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/rus/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..2d00321f1f --- /dev/null +++ b/i18n/rus/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 ссылка", + "manyReferenceLabel": "Ссылок: {0}", + "referenceErrorLabel": "Не удалось определить ссылки." +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/rus/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..52c6136f9a --- /dev/null +++ b/i18n/rus/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "отслеживание – {0}", + "buildTscLabel": "сборка – {0}" +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/typescriptMain.i18n.json b/i18n/rus/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..f70445d440 --- /dev/null +++ b/i18n/rus/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "Откройте папку в VS Code, чтобы использовать проект JavaScript или TypeScript.", + "typescript.projectConfigUnsupportedFile": "Не удалось определить проект TypeScript или JavaScript. Неподдерживаемый тип файла", + "typescript.projectConfigCouldNotGetInfo": "Не удалось определить проект TypeScript или JavaScript.", + "typescript.noTypeScriptProjectConfig": "Файл не является частью проекта TypeScript.", + "typescript.noJavaScriptProjectConfig": "Файл не является частью проекта JavaScript.", + "typescript.configureTsconfigQuickPick": "Настроить tsconfig.json", + "typescript.configureJsconfigQuickPick": "Настроить jsconfig.json", + "typescript.projectConfigLearnMore": "Дополнительные сведения" +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/rus/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..8065c4ddfc --- /dev/null +++ b/i18n/rus/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "Путь {0} не указывает на допустимый файл программы установки tsserver. Выполняется откат до пакетной версии TypeScript.", + "serverCouldNotBeStarted": "Не удалось запустить языковой сервер TypeScript. Сообщение об ошибке: \"{0}\".", + "typescript.openTsServerLog.notSupported": "Для ведения журнала сервера TS требуется TS 2.2.2+", + "typescript.openTsServerLog.loggingNotEnabled": "Вход в TS Server отключен. Задайте \"typescript.tsserver.log\" и перезагрузите VS Code, чтобы включить ведение журнала", + "typescript.openTsServerLog.enableAndReloadOption": "Войдите и перезагрузите TS server", + "typescript.openTsServerLog.noLogFile": "Сервер TS не начал ведение журнала.", + "openTsServerLog.openFileFailedFailed": "Не удалось открыть файл журнала сервера TS", + "serverDiedAfterStart": "Языковая служба TypeScript пять раз завершила работу сразу после запуска. Служба не будет перезапущена.", + "serverDiedReportIssue": "Сообщить об ошибке", + "serverDied": "Языковая служба TypeScript пять раз непредвиденно завершила работу за последние пять минут." +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/utils/api.i18n.json b/i18n/rus/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..a08f6b3740 --- /dev/null +++ b/i18n/rus/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "Недопустимая версия" +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/utils/logger.i18n.json b/i18n/rus/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/rus/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/rus/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..fb8227c0b2 --- /dev/null +++ b/i18n/rus/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "Чтобы включить языковые функции JavaScript/TypeScript IntelliSense во всем проекте, исключите папки с большим числом файлов, например: {0}.", + "hintExclude.generic": "Чтобы включить языковые функции JavaScript/TypeScript IntelliSense во всем проекте, исключите большие папки с исходными файлами, с которыми вы не работаете.", + "large.label": "Настройка исключений", + "hintExclude.tooltip": "Чтобы включить языковые функции JavaScript/TypeScript IntelliSense во всем проекте, исключите большие папки с исходными файлами, с которыми вы не работаете." +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/rus/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..48bfefc901 --- /dev/null +++ b/i18n/rus/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "Получение данных для повышения эффективности IntelliSense TypeScript", + "typesInstallerInitializationFailed.title": "Не удалось установить файлы типизации для языка JavaScript. Убедитесь, что NPM установлен или укажите путь к файлу 'typescript.npm' в параметрах среды пользователя", + "typesInstallerInitializationFailed.moreInformation": "Дополнительные сведения", + "typesInstallerInitializationFailed.doNotCheckAgain": "Больше не проверять", + "typesInstallerInitializationFailed.close": "Закрыть" +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/rus/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..c5297fe1ce --- /dev/null +++ b/i18n/rus/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "Использовать версию VS Code", + "useWorkspaceVersionOption": "Использовать версию рабочей области", + "learnMore": "Дополнительные сведения", + "selectTsVersion": "Выберите версию TypeScript, используемую для языковых функций JavaScript и TypeScript." +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/rus/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..d9dc8eabf7 --- /dev/null +++ b/i18n/rus/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "Не удалось загрузить версию TypeScript по этому пути", + "noBundledServerFound": "Файл tsserver VSCode был удален другим приложением, например, в результате ошибочного срабатывания средства обнаружения вирусов. Переустановите VSCode." +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/package.i18n.json b/i18n/rus/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..d43c2e48ed --- /dev/null +++ b/i18n/rus/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "Перезагрузить проект", + "javascript.reloadProjects.title": "Перезагрузить проект", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "Дополните функции сигнатурами их параметров.", + "typescript.tsdk.desc": "Указывает путь к папке, содержащей файлы tsserver и lib*.d.ts, которые необходимо использовать.", + "typescript.disableAutomaticTypeAcquisition": "Отключает автоматическое получение типов. Требуется TypeScript 2.0.6 и более поздней версии.", + "typescript.tsserver.log": "Включает ведение журнала для сервера TS. Этот журнал можно использовать для диагностики проблем сервера TS. В журнале могут содержаться пути к файлам, исходный код и другие сведения из вашего проекта, в том числе носящие конфиденциальный характер.", + "typescript.tsserver.trace": "Включает трассировку сообщений, отправляемых на сервер TS. Эту трассировку можно использовать для диагностики проблем сервера TS. Трассировка может содержать пути к файлам, исходный код и другие сведения из вашего проекта, в том числе конфиденциальные данные.", + "typescript.validate.enable": "Включение или отключение проверки TypeScript.", + "typescript.format.enable": "Включение или отключение модуля форматирования TypeScript по умолчанию.", + "javascript.format.enable": "Включение или отключение модуля форматирования JavaScript по умолчанию.", + "format.insertSpaceAfterCommaDelimiter": "Определяет метод обработки пробелов после разделителя-запятой.", + "format.insertSpaceAfterConstructor": "Определяет метод обработки пробелов после ключевого слова constructor. Требуется TypeScript 2.3.0 или более поздней версии.", + "format.insertSpaceAfterSemicolonInForStatements": " Определяет метод обработки пробелов после точки с запятой в операторе for.", + "format.insertSpaceBeforeAndAfterBinaryOperators": "Определяет метод обработки пробелов после двоичного оператора.", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "Определяет метод обработки пробелов после ключевых слов в операторе управления потоком выполнения.", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "Определяет метод обработки пробелов после ключевого слова function у анонимных функций.", + "format.insertSpaceBeforeFunctionParenthesis": "Определяет метод обработки пробелов перед скобками аргумента функции. Требует TypeScript >= 2.1.5.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "Определяет метод обработки пробелов после открытия и до закрытия непустых круглых скобок.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "Определяет метод обработки пробелов после открытия и до закрытия непустых квадратных скобок.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Определяет метод обработки пробелов после открытия и до закрытия непустых скобок. Требуется TypeScript 2.3.0 или более поздней версии.", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "Определяет метод обработки пробелов после открытия и до закрытия скобок в строке шаблона. Требуется TypeScript 2.0.6 или более поздней версии.", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Определяет метод обработки пробелов после открытия и до закрытия скобок выражения JSX. Требуется TypeScript 2.0.6 или более поздней версии.", + "format.insertSpaceAfterTypeAssertion": "Определяет метод обработки пробелов после утверждений типа в TypeScript. Требуется TypeScript 2.4 или более поздней версии.", + "format.placeOpenBraceOnNewLineForFunctions": "Определяет, ставится ли открывающая фигурная скобка с новой строки в функциях.", + "format.placeOpenBraceOnNewLineForControlBlocks": "Определяет, ставится ли открывающая фигурная скобка с новой строки в блоках управления.", + "javascript.validate.enable": "Включение или отключение проверки JavaScript.", + "typescript.goToProjectConfig.title": "Перейти к конфигурации проекта", + "javascript.goToProjectConfig.title": "Перейти к конфигурации проекта", + "javascript.referencesCodeLens.enabled": "Включить/отключить ссылки CodeLens для файлов JavaScript.", + "typescript.referencesCodeLens.enabled": "Включить/отключить ссылки CodeLens для файлов TypeScript. Требуется TypeScript версии 2.0.6 или более поздней версии.", + "typescript.implementationsCodeLens.enabled": "Включить или отключить CodeLens для реализаций. Требуется TypeScript >= 2.2.0.", + "typescript.openTsServerLog.title": "Открыть журнал сервера TS", + "typescript.restartTsServer": "Перезапустить сервер TS", + "typescript.selectTypeScriptVersion.title": "Выберите версию TypeScript.", + "jsDocCompletion.enabled": "Включить или отключить JSDoc коментарии", + "javascript.implicitProjectConfig.checkJs": "Включает/отключает семантическую проверку файлов JavaScript. Этот параметр может переопределяться в файле jsconfig.json или tsconfig.json. Требуется TypeScript 2.3.1 или более поздней версии.", + "typescript.npm": "Указывает путь к исполняемому файлу NPM, используемому для автоматического получения типа. Требуется TypeScript версии 2.3.4 или более поздней версии.", + "typescript.check.npmIsInstalled": "Проверяет, установлен ли NPM для автоматического получения типов.", + "javascript.nameSuggestions": "Включить/отключить использование уникальных имен из файла в списках предложений JavaScript.", + "typescript.tsc.autoDetect": "Включает или отключает автоматическое определние заданий tsc.", + "typescript.problemMatchers.tsc.label": "Проблемы TypeScript", + "typescript.problemMatchers.tscWatch.label": "Проблемы TypeScript (режим наблюдения)" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/rus/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/rus/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/rus/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..063af141cd --- /dev/null +++ b/i18n/rus/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (произошло снова)" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/rus/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..5fe4f78fa5 --- /dev/null +++ b/i18n/rus/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "ввод" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/rus/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..317a8be4ca --- /dev/null +++ b/i18n/rus/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "С учетом регистра", + "wordsDescription": "Слово целиком", + "regexDescription": "Использовать регулярное выражение" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/rus/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..cc3d024d50 --- /dev/null +++ b/i18n/rus/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Ошибка: {0}", + "alertWarningMessage": "Предупреждение: {0}", + "alertInfoMessage": "Сведения: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/rus/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..ccdfbb52d5 --- /dev/null +++ b/i18n/rus/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "Изображение слишком велико для отображения в редакторе. ", + "resourceOpenExternalButton": "Открыть изображение с помощью внешней программы?", + "nativeBinaryError": "Файл не будет отображен в редакторе, так как он двоичный, очень большой или использует неподдерживаемую кодировку текста.", + "sizeB": "{0} Б", + "sizeKB": "{0} КБ", + "sizeMB": "{0} МБ", + "sizeGB": "{0} ГБ", + "sizeTB": "{0} ТБ" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/rus/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..325789250b --- /dev/null +++ b/i18n/rus/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "Еще" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/common/errorMessage.i18n.json b/i18n/rus/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..e1c1aa8cbd --- /dev/null +++ b/i18n/rus/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}. Код ошибки: {1}", + "error.permission.verbose": "Отказано в разрешении (HTTP {0})", + "error.permission": "Отказано в разрешении", + "error.http.verbose": "{0} (HTTP {1}: {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "Произошла неизвестная ошибка подключения ({0})", + "error.connection.unknown": "Произошла неизвестная ошибка подключения. Утеряно подключение к Интернету, либо сервер, к которому вы подключены, перешел в автономный режим.", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "Произошла неизвестная ошибка. Подробные сведения см. в журнале.", + "nodeExceptionMessage": "Произошла системная ошибка ({0})", + "error.moreErrors": "{0} (всего ошибок: {1})" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/common/json.i18n.json b/i18n/rus/src/vs/base/common/json.i18n.json new file mode 100644 index 0000000000..ebdde8653d --- /dev/null +++ b/i18n/rus/src/vs/base/common/json.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Недопустимый символ", + "error.invalidNumberFormat": "Недопустимый числовой формат", + "error.propertyNameExpected": "Требуется имя свойства", + "error.valueExpected": "Требуется значение", + "error.colonExpected": "Требуется двоеточие", + "error.commaExpected": "Требуется запятая", + "error.closeBraceExpected": "Требуется закрывающая фигурная скобка", + "error.closeBracketExpected": "Требуется закрывающая квадратная скобка", + "error.endOfFileExpected": "Ожидается конец файла" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/rus/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..ebdde8653d --- /dev/null +++ b/i18n/rus/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Недопустимый символ", + "error.invalidNumberFormat": "Недопустимый числовой формат", + "error.propertyNameExpected": "Требуется имя свойства", + "error.valueExpected": "Требуется значение", + "error.colonExpected": "Требуется двоеточие", + "error.commaExpected": "Требуется запятая", + "error.closeBraceExpected": "Требуется закрывающая фигурная скобка", + "error.closeBracketExpected": "Требуется закрывающая квадратная скобка", + "error.endOfFileExpected": "Ожидается конец файла" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/common/keybindingLabels.i18n.json b/i18n/rus/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..193c9d3153 --- /dev/null +++ b/i18n/rus/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "CTRL", + "shiftKey": "SHIFT", + "altKey": "ALT", + "windowsKey": "Клавиша Windows", + "ctrlKey.long": "CTRL", + "shiftKey.long": "SHIFT", + "altKey.long": "ALT", + "cmdKey.long": "Команда", + "windowsKey.long": "Клавиша Windows" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/common/processes.i18n.json b/i18n/rus/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..2ebb309679 --- /dev/null +++ b/i18n/rus/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "Ошибка: в исполняемых данных должна определяться команда типа string.", + "ExecutableParser.isShellCommand": "Предупреждение: isShellCommand должен иметь тип boolean. Игнорируется значение {0}.", + "ExecutableParser.args": "Предупреждение: аргументы должны иметь тип string[]. Игнорируется значение {0}.", + "ExecutableParser.invalidCWD": "Предупреждение: options.cwd должен иметь тип string. Игнорируется значение {0}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/common/severity.i18n.json b/i18n/rus/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..ab3d7794ad --- /dev/null +++ b/i18n/rus/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "Ошибка", + "sev.warning": "Предупреждение", + "sev.info": "Сведения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/node/processes.i18n.json b/i18n/rus/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..8a411dc33e --- /dev/null +++ b/i18n/rus/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "Невозможно выполнить команду оболочки на диске UNC." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/node/zip.i18n.json b/i18n/rus/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..734d3530ec --- /dev/null +++ b/i18n/rus/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "{0} не найдено в ZIP-архиве." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/rus/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..15dbbfe79a --- /dev/null +++ b/i18n/rus/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0}, средство выбора", + "quickOpenAriaLabel": "средство выбора" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/rus/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..776604bf9e --- /dev/null +++ b/i18n/rus/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "Средство быстрого выбора. Введите, чтобы сузить результаты.", + "treeAriaLabel": "Средство быстрого выбора" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/rus/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..bdf1ff1619 --- /dev/null +++ b/i18n/rus/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Свернуть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/code/electron-main/auth.i18n.json b/i18n/rus/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..6b9862522a --- /dev/null +++ b/i18n/rus/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "Требуется проверка подлинности прокси-сервера", + "proxyauth": "Прокси-сервер {0} требует проверки подлинности." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/code/electron-main/menus.i18n.json b/i18n/rus/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..6377b2863c --- /dev/null +++ b/i18n/rus/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "&&Файл", + "mEdit": "&&Правка", + "mSelection": "&&Выделение", + "mView": "&&Вид", + "mGoto": "&&Перейти", + "mDebug": "&&Отладка", + "mWindow": "Окно", + "mHelp": "&&Справка", + "mTask": "&&Задачи", + "miNewWindow": "&&Новое окно", + "mAbout": "О программе {0}", + "mServices": "Службы", + "mHide": "Скрыть {0}", + "mHideOthers": "Скрыть другие", + "mShowAll": "Показать все", + "miQuit": "Выйти из {0}", + "miNewFile": "&&Новый файл", + "miOpen": "Открыть...", + "miOpenWorkspace": "&&Открыть рабочую область...", + "miOpenFolder": "Открыть &&папку...", + "miOpenFile": "&&Открыть файл...", + "miOpenRecent": "Открыть &&последние", + "miSaveWorkspaceAs": "&&Сохранить рабочую область как...", + "miAddFolderToWorkspace": "&& Добавить папку в рабочую область...", + "miSave": "Сохранить", + "miSaveAs": "Сохранить &&как...", + "miSaveAll": "Сохранить &&все", + "miAutoSave": "Автосохранение", + "miRevert": "Отменить &&изменения в файле", + "miCloseWindow": "Закрыть &&окно", + "miCloseWorkspace": "Закрыть &&рабочую область", + "miCloseFolder": "Закрыть &&папку", + "miCloseEditor": "&&Закрыть редактор", + "miExit": "В&&ыход", + "miOpenSettings": "&&Параметры", + "miOpenKeymap": "&&Сочетания клавиш", + "miOpenKeymapExtensions": "&&Расширения раскладки клавиатуры", + "miOpenSnippets": "&&Фрагменты кода пользователя", + "miSelectColorTheme": "Цветовая &&тема", + "miSelectIconTheme": "Тема значка &&файла", + "miPreferences": "Параметры", + "miReopenClosedEditor": "&&Повторно открыть закрытый редактор", + "miMore": "&&Дополнительные сведения...", + "miClearRecentOpen": "&&Очистить недавно открытые", + "miUndo": "Отменить", + "miRedo": "Вернуть", + "miCut": "Вы&&резать", + "miCopy": "Копировать", + "miPaste": "Вставить", + "miFind": "Найти", + "miReplace": "Заменить", + "miFindInFiles": "Найти в &&файлах", + "miReplaceInFiles": "Заменить &&в файлах", + "miEmmetExpandAbbreviation": "Emmet: ра&&звернуть сокращение", + "miShowEmmetCommands": "E&&mmet...", + "miToggleLineComment": "Переключить комментарий &&строки", + "miToggleBlockComment": "Переключить комментарий &&блока", + "miMultiCursorAlt": "Для работы в режиме нескольких курсоров нажмите левую кнопку мыши, удерживая клавишу ALT", + "miMultiCursorCmd": "Для работы в режиме нескольких курсоров нажмите левую кнопку мыши, удерживая клавишу COMMAND ", + "miMultiCursorCtrl": "Для работы в режиме нескольких курсоров нажмите левую кнопку мыши, удерживая клавишу CTRL", + "miInsertCursorAbove": "Добавить курсор &&выше", + "miInsertCursorBelow": "Добавить курсор &&ниже", + "miInsertCursorAtEndOfEachLineSelected": "Добавить курсоры в &&окончания строк", + "miAddSelectionToNextFindMatch": "Добавить &&следующее вхождение", + "miAddSelectionToPreviousFindMatch": "Добавить &&предыдущее вхождение", + "miSelectHighlights": "Выбрать все &&вхождения", + "miCopyLinesUp": "&&Копировать на строку выше", + "miCopyLinesDown": "Копировать на строку &&ниже", + "miMoveLinesUp": "Переместить на с&&троку выше", + "miMoveLinesDown": "&&Переместить на строку ниже", + "miSelectAll": "&&Выделить все", + "miSmartSelectGrow": "&&Развернуть выделение", + "miSmartSelectShrink": "&&Сжать выделение", + "miViewExplorer": "Проводник", + "miViewSearch": "Поиск", + "miViewSCM": "S&&CM", + "miViewDebug": "Отладка", + "miViewExtensions": "Р&&асширения", + "miToggleOutput": "Вывод", + "miToggleDebugConsole": "Ко&&нсоль отладки", + "miToggleIntegratedTerminal": "&&Интегрированный терминал", + "miMarker": "Проблемы", + "miAdditionalViews": "Дополнительные &&представления", + "miCommandPalette": "&&Палитра команд...", + "miToggleFullScreen": "Включить/выключить полно&&экранный режим", + "miToggleZenMode": "Включить/отключить режим \"Дзен\"", + "miToggleMenuBar": "Показать/скрыть строку &&меню", + "miSplitEditor": "Разделить &&редактор", + "miToggleEditorLayout": "Переключить &&структуру группы редакторов", + "miToggleSidebar": "Показать/скрыть &&боковую панель", + "miMoveSidebarRight": "&&Переместить боковую панель вправо", + "miMoveSidebarLeft": "&&Переместить боковую панель влево", + "miTogglePanel": "Показать/скрыть п&&анель", + "miHideStatusbar": "&&Скрыть строку состояния", + "miShowStatusbar": "&&Показать строку состояния", + "miHideActivityBar": "Скрыть &&панель действий", + "miShowActivityBar": "Показать &&панель действий", + "miToggleWordWrap": "&&Включить/выключить перенос текста", + "miToggleMinimap": "Переключить мини-карту", + "miToggleRenderWhitespace": "Показать/&&скрыть символы пробелов", + "miToggleRenderControlCharacters": "Переключить &&управляющие символы", + "miZoomIn": "&&Увеличить", + "miZoomOut": "У&&меньшить", + "miZoomReset": "&&Сбросить масштаб", + "miBack": "&&Назад", + "miForward": "&&Вперед", + "miNextEditor": "&&Следующий редактор", + "miPreviousEditor": "&&Предыдущий редактор", + "miNextEditorInGroup": "&&Следующий используемый редактор в группе", + "miPreviousEditorInGroup": "&&Предыдущий используемый редактор в группе", + "miSwitchEditor": "Переключить р&&едактор", + "miFocusFirstGroup": "&&Первая группа", + "miFocusSecondGroup": "&&Вторая группа", + "miFocusThirdGroup": "&&Третья группа", + "miNextGroup": "&&Следующая группа", + "miPreviousGroup": "&&Предыдущая группа", + "miSwitchGroup": "Переключить &&группу", + "miGotoFile": "Перейти к &&файлу...", + "miGotoSymbolInFile": "Перейти к &&символу в файле...", + "miGotoSymbolInWorkspace": "Перейти к символу в &&рабочей области...", + "miGotoDefinition": "Перейти к &&определению", + "miGotoTypeDefinition": "Перейти к &&определению типа", + "miGotoImplementation": "Перейти к &&реализации", + "miGotoLine": "Перейти к &&строке...", + "miStartDebugging": "&&Запустить отладку", + "miStartWithoutDebugging": "Начать &&без отладки", + "miStopDebugging": "&&Остановить отладку", + "miRestart Debugging": "&&Перезапустить отладку", + "miOpenConfigurations": "От&&крыть конфигурации", + "miAddConfiguration": "Добавить конфигурацию...", + "miStepOver": "Шаг с о&&бходом", + "miStepInto": "Ш&&аг с заходом", + "miStepOut": "Шаг с &&выходом", + "miContinue": "&&Продолжить", + "miToggleBreakpoint": "Перек&&лючить точку останова", + "miConditionalBreakpoint": "У&&словная точка останова...", + "miColumnBreakpoint": "Т&&очка останова столбца", + "miFunctionBreakpoint": "&&Точка останова функции...", + "miNewBreakpoint": "&&Новая точка останова", + "miEnableAllBreakpoints": "Включить все точки останова", + "miDisableAllBreakpoints": "Отключить &&все точки останова", + "miRemoveAllBreakpoints": "&&Удалить &&все точки останова", + "miInstallAdditionalDebuggers": "У&&становить дополнительные отладчики...", + "mMinimize": "Свернуть", + "mZoom": "Изменить масштаб", + "mBringToFront": "Переместить все на передний план", + "miSwitchWindow": "Переключить &&окно...", + "miToggleDevTools": "&&Показать/скрыть средства разработчика", + "miAccessibilityOptions": "Специальные &&возможности", + "miReportIssues": "&&Сообщить о проблемах", + "miWelcome": "&&Приветствие", + "miInteractivePlayground": "&&Интерактивная площадка", + "miDocumentation": "&&Документация", + "miReleaseNotes": "&&Заметки о выпуске", + "miKeyboardShortcuts": "С&&правочник по сочетаниям клавиш", + "miIntroductoryVideos": "Вступительные в&&идео", + "miTipsAndTricks": "&&Советы и рекомендации", + "miTwitter": "&&Присоединяйтесь к нам в Twitter", + "miUserVoice": "&&Поиск запросов функций", + "miLicense": "Просмотреть &&лицензию", + "miPrivacyStatement": "&&Заявление о конфиденциальности", + "miAbout": "&&О программе", + "miRunTask": "&&Запустить задачу...", + "miBuildTask": "Запустить &&задачу сборки...", + "miRunningTask": "Показать выполняющ&&иеся задачи...", + "miRestartTask": "П&&ерезапустить запущенную задачу...", + "miTerminateTask": "&&Завершить задачу...", + "miConfigureTask": "&&Настроить задачи", + "miConfigureBuildTask": "Настроить задачу сборки по у&&молчанию", + "accessibilityOptionsWindowTitle": "Специальные возможности", + "miRestartToUpdate": "Перезапустить программу для обновления...", + "miCheckingForUpdates": "Идет проверка наличия обновлений...", + "miDownloadUpdate": "Скачать доступное обновление", + "miDownloadingUpdate": "Скачивается обновление...", + "miInstallingUpdate": "Идет установка обновления...", + "miCheckForUpdates": "Проверить наличие обновлений...", + "aboutDetail": "\nВерсия {0}\nФиксация {1}\nДата {2}\nОблочка {3}\nОтрисовщик {4}\nУзел {5}\nАрхитектура {6}", + "okButton": "ОК" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/code/electron-main/window.i18n.json b/i18n/rus/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..528f0f8d53 --- /dev/null +++ b/i18n/rus/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "Вы по-прежнему можете получить доступ к строке меню, нажав клавишу **ALT**." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/code/electron-main/windows.i18n.json b/i18n/rus/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..fe39aa00b2 --- /dev/null +++ b/i18n/rus/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "ОК", + "pathNotExistTitle": "Путь не существует.", + "pathNotExistDetail": "Путь \"{0}\" больше не существует на диске.", + "openWorkspace": "&&Открыть...", + "openWorkspaceTitle": "Открыть рабочую область", + "save": "Сохранить", + "doNotSave": "&&Не сохранять", + "cancel": "Отмена", + "saveWorkspaceMessage": "Вы хотите сохранить конфигурацию рабочей области в файле?", + "saveWorkspaceDetail": "Сохраните рабочую область, если хотите открыть ее позже.", + "saveWorkspace": "Сохранить рабочую область", + "reopen": "Открыть повторно", + "wait": "Подождать", + "close": "Закрыть", + "appStalled": "Окно не отвечает", + "appStalledDetail": "Вы можете повторно открыть окно, закрыть его или продолжить ожидание.", + "appCrashed": "Сбой окна", + "appCrashedDetail": "Приносим извинения за неудобство! Вы можете повторно открыть окно, чтобы продолжить работу с того места, на котором остановились.", + "open": "Открыть", + "openFolder": "Открыть папку", + "openFile": "Открыть файл" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/code/node/cliProcessMain.i18n.json b/i18n/rus/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..faf2cbba61 --- /dev/null +++ b/i18n/rus/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Расширение \"{0}\" не найдено.", + "notInstalled": "Расширение \"{0}\" не установлено.", + "useId": "Используйте полный идентификатор расширения, включающий издателя, например: {0}", + "successVsixInstall": "Расширение \"{0}\" успешно установлено.", + "alreadyInstalled": "Расширение \"{0}\" уже установлено.", + "foundExtension": "Найдено \"{0}\" в Marketplace.", + "installing": "Установка...", + "successInstall": "Расширение \"{0}\" версии {1} успешно установлено.", + "uninstalling": "Удаление {0}...", + "successUninstall": "Расширение \"{0}\" успешно удалено." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/rus/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..1622b3a636 --- /dev/null +++ b/i18n/rus/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "Нельзя сравнить файлы, потому что один из файлов слишком большой." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/rus/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..7de85dd08e --- /dev/null +++ b/i18n/rus/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Закрыть", + "header": "Различие {0} из {1}; исходная версия: {2}, строки: {3}, измененная версия: {4}, строки: {5}", + "blankLine": "пустой", + "equalLine": "Исходная версия: {0}, измененная версия: {1}: {2}", + "insertLine": "+ измененная версия: {0}: {1}", + "deleteLine": "- исходная версия: {0}: {1}", + "editor.action.diffReview.next": "Перейти к следующему различию", + "editor.action.diffReview.prev": "Перейти к предыдущему различию" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/rus/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..2685a42a1d --- /dev/null +++ b/i18n/rus/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "Редактор", + "fontFamily": "Определяет семейство шрифтов.", + "fontWeight": "Управляет насыщенностью шрифта.", + "fontSize": "Управляет размером шрифта в пикселях.", + "lineHeight": "Управляет высотой строк. Укажите 0 для вычисления высоты строки по размеру шрифта.", + "letterSpacing": "Управляет интервалом между буквами в пикселях.", + "lineNumbers": "Управляет видимостью номеров строк. Возможные значения: \"on\", \"off\" и \"relative\". Значение \"relative\" показывает количество строк, начиная с текущего положения курсора.", + "rulers": "Столбцы, в которых должны отображаться вертикальные линейки", + "wordSeparators": "Символы, которые будут использоваться как разделители слов при выполнении навигации или других операций, связанных со словами.", + "tabSize": "Число пробелов в табуляции. Эта настройка переопределяется на основании содержимого файла, когда включен параметр \"editor.detectIndentation\".", + "tabSize.errorMessage": "Ожидается число. Обратите внимание, что значение auto заменено параметром editor.detectIndentation.", + "insertSpaces": "Вставлять пробелы при нажатии клавиши TAB. Эта настройка переопределяется на основании содержимого файла, когда включен параметр \"editor.detectIndentation\".", + "insertSpaces.errorMessage": "Ожидается логическое значение. Обратите внимание, что значение auto заменено параметром editor.detectIndentation.", + "detectIndentation": "При открытии файла editor.tabSize и editor.insertSpaces будут определяться на основе содержимого файла.", + "roundedSelection": "Определяет, будут ли выделения иметь скругленные углы.", + "scrollBeyondLastLine": "Определяет, будет ли содержимое редактора прокручиваться за последнюю строку.", + "smoothScrolling": "Определяет, будет ли использоваться анимация при прокрутке содержимого редактора", + "minimap.enabled": "Определяет, отображается ли мини-карта", + "minimap.showSlider": "Определяет, будет ли автоматически скрываться ползунок мини-карты. Возможные значения: 'always' (всегда) и 'mouseover' (при наведении курсора мыши)", + "minimap.renderCharacters": "Отображает фактические символы в строке вместо цветных блоков.", + "minimap.maxColumn": "Ограничивает ширину мини-карты для отображения числа столбцов не больше определенного.", + "find.seedSearchStringFromSelection": "Определяет, можно ли передать строку поиска в мини-приложение поиска из текста, выделенного в редакторе", + "find.autoFindInSelection": "Определяет, будет ли снят флажок \"Поиск в выделенном\", когда в редакторе выбрано несколько символов или строк текста", + "wordWrap.off": "Строки не будут переноситься никогда.", + "wordWrap.on": "Строки будут переноситься по ширине окна просмотра.", + "wordWrap.wordWrapColumn": "Строки будут переноситься по \"editor.wordWrapColumn\".", + "wordWrap.bounded": "Строки будут перенесены по минимальному значению из двух: ширина окна просмотра и \"editor.wordWrapColumn\".", + "wordWrap": "Определяет, как должны переноситься строки. Допустимые значения:\n - \"off\" (отключить перенос);\n - \"on\" (перенос окна просмотра);\n - \"wordWrapColumn\" (перенос в \"editor.wordWrapColumn\");\n - \"bounded\" (перенос при минимальной ширине окна просмотра и \"editor.wordWrapColumn\").", + "wordWrapColumn": "Определяет столбец переноса редактора, если значение \"editor.wordWrap\" — \"wordWrapColumn\" или \"bounded\".", + "wrappingIndent": "Управляет отступом строк с переносом по словам. Допустимые значения: \"none\", \"same\" или \"indent\".", + "mouseWheelScrollSensitivity": "Множитель, используемый для параметров deltaX и deltaY событий прокрутки колесика мыши.", + "multiCursorModifier.ctrlCmd": "Соответствует клавише CTRL в Windows и Linux и клавише COMMAND в OS X.", + "multiCursorModifier.alt": "Соответствует клавише ALT в Windows и Linux и клавише OPTION в OS X.", + "multiCursorModifier": "Модификатор, который будет использоваться для добавления нескольких курсоров с помощью мыши. \"ctrlCmd\" соответствует клавише CTRL в Windows и Linux и клавише COMMAND в OS X. Жесты мыши \"Перейти к определению\" и \"Открыть ссылку\" будут изменены так, чтобы они не конфликтовали с несколькими курсорами.", + "quickSuggestions.strings": "Разрешение кратких предложений в строках.", + "quickSuggestions.comments": "Разрешение кратких предложений в комментариях.", + "quickSuggestions.other": "Разрешение кратких предложений вне строк и комментариев.", + "quickSuggestions": "Определяет, должны ли при вводе текста автоматически отображаться предложения", + "quickSuggestionsDelay": "Управляет длительностью задержки (в мс), перед отображением кратких предложений.", + "parameterHints": "Включает всплывающее окно с документацией по параметру и сведениями о типе, которое отображается во время набора", + "autoClosingBrackets": "Определяет, должен ли редактор автоматически закрывать скобки после открытия.", + "formatOnType": "Управляет параметром, определяющим, должен ли редактор автоматически форматировать строку после ввода.", + "formatOnPaste": "Определяет, будет ли редактор автоматически форматировать вставленное содержимое. Модуль форматирования должен быть доступен и иметь возможность форматировать диапазон в документе.", + "autoIndent": "Определяет, должен ли редактор автоматически изменять отступ при вводе текста, вставке текста или перемещении строк. Для использования этого параметра должны быть доступны правила отступа.", + "suggestOnTriggerCharacters": "Определяет, должны ли при вводе триггерных символов автоматически отображаться предложения.", + "acceptSuggestionOnEnter": "Определяет, будут ли предложения приниматься клавишей ВВОД в дополнение к клавише TAB. Это помогает избежать неоднозначности между вставкой новых строк и принятием предложений. Значение \"smart\" означает, что при изменении текста предложения будут приниматься только при нажатии клавиши ВВОД.", + "acceptSuggestionOnCommitCharacter": "Определяет, будут ли предложения приниматься символами фиксации. Например, в JavaScript точка с запятой (\";\") может быть символом фиксации, принимающим предложение и вводящим данный символ.", + "snippetSuggestions.top": "Отображать предложения фрагментов поверх других предложений.", + "snippetSuggestions.bottom": "Отображать предложения фрагментов под другими предложениями.", + "snippetSuggestions.inline": "Отображать предложения фрагментов рядом с другими предложениями.", + "snippetSuggestions.none": "Не отображать предложения фрагментов.", + "snippetSuggestions": "Управляет отображением фрагментов вместе с другими предложениями и их сортировкой.", + "emptySelectionClipboard": "Управляет тем, копируется ли текущая строка при копировании без выделения.", + "wordBasedSuggestions": "Определяет, следует ли оценивать завершения на основе слов в документе.", + "suggestFontSize": "Размер шрифта мини-приложения предложений", + "suggestLineHeight": "Высота строки мини-приложения с предложениями", + "selectionHighlight": "Определяет, будет ли редактор выделять фрагменты, совпадающие с выделенным текстом.", + "occurrencesHighlight": "Определяет, должен ли редактор выделять экземпляры семантических символов.", + "overviewRulerLanes": "Определяет, сколько украшений могут отображаться на одном месте в обзорной линейке.", + "overviewRulerBorder": "Определяет, следует ли рисовать границу на обзорной линейке.", + "cursorBlinking": "Управляет стилем анимации курсора. Допустимые значения: \"blink\", \"smooth\", \"phase\", \"expand\" и \"solid\"", + "mouseWheelZoom": "Изменение размера шрифта в редакторе при нажатой клавише CTRL и движении колесика мыши", + "cursorStyle": "Определяет стиль курсора. Допустимые значения: \"block\", \"block-outline\", \"line\", \"line-thin\", \"underline\" и \"underline-thin\"", + "fontLigatures": "Включает лигатуры шрифта.", + "hideCursorInOverviewRuler": "Управляет скрытием курсора в обзорной линейке.", + "renderWhitespace": "Определяет, должен ли редактор обрабатывать символы пробела; возможные значения: \"none\", \"boundary\" и \"all\". Параметр \"boundary\" не обрабатывает единичные пробелы между словами.", + "renderControlCharacters": "Определяет, должны ли в редакторе отображаться управляющие символы.", + "renderIndentGuides": "Определяет, должны ли в редакторе отображаться направляющие отступа.", + "renderLineHighlight": "Определяет, должен ли редактор выделять текущую строку. Возможные значения: none, gutter, line и all.", + "codeLens": "Управляет показом групп связанных элементов кода в редакторе", + "folding": "Определяет, включено ли сворачивание кода в редакторе.", + "showFoldingControls": "Определяет, будут ли автоматически скрываться элементы управления свертыванием на полях.", + "matchBrackets": "Выделяет соответствующие скобки при выборе одной из них.", + "glyphMargin": "Управляет отображением вертикальных полей глифа в редакторе. Поля глифа в основном используются для отладки.", + "useTabStops": "Вставка и удаление пробелов после позиции табуляции", + "trimAutoWhitespace": "Удалить автоматически вставляемый конечный пробел", + "stablePeek": "Оставлять быстрые редакторы открытыми, даже если дважды щелкнуто их содержимое или нажата клавиша ESC.", + "dragAndDrop": "Определяет, следует ли редактору разрешить перемещение выделенных элементов с помощью перетаскивания.", + "accessibilitySupport.auto": "Редактор будет определять, подключено ли средство чтения с экрана, с помощью API-интерфейсов платформы.", + "accessibilitySupport.on": "Редактор будет оптимизирован для использования со средством чтения с экрана в постоянном режиме.", + "accessibilitySupport.off": "Редактор никогда не будет оптимизироваться для использования со средством чтения с экрана.", + "accessibilitySupport": "Определяет, следует ли запустить редактор в режиме оптимизации для средства чтения с экрана.", + "links": "Определяет, должен ли редактор определять ссылки и делать их доступными для щелчка", + "colorDecorators": "Определяет, должны ли в редакторе отображаться внутренние декораторы цвета и средство выбора цвета.", + "sideBySide": "Определяет, как редактор несовпадений отображает отличия: рядом или в тексте.", + "ignoreTrimWhitespace": "Определяет, должен ли редактор несовпадений трактовать несовпадения символов-разделителей как различия.", + "renderIndicators": "Определяет отображение редактором несовпадений индикаторов +/- для добавленных или удаленных изменений", + "selectionClipboard": "Контролирует, следует ли поддерживать первичный буфер обмена Linux." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/common/config/defaultConfig.i18n.json b/i18n/rus/src/vs/editor/common/config/defaultConfig.i18n.json new file mode 100644 index 0000000000..659da146cf --- /dev/null +++ b/i18n/rus/src/vs/editor/common/config/defaultConfig.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorViewAccessibleLabel": "Содержимое редактора" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/rus/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..0b09653cb3 --- /dev/null +++ b/i18n/rus/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "Редактор сейчас недоступен. Чтобы открыть список действий, нажмите ALT+F1.", + "editorViewAccessibleLabel": "Содержимое редактора" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/common/controller/cursor.i18n.json b/i18n/rus/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..37007eb1a9 --- /dev/null +++ b/i18n/rus/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "Неожиданное исключение при выполнении команды." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/rus/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..eb5ce8821f --- /dev/null +++ b/i18n/rus/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "Не удалось разметить входные данные." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/rus/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..f28c465283 --- /dev/null +++ b/i18n/rus/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "Обычный текст" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/rus/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..2355c554a6 --- /dev/null +++ b/i18n/rus/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "Следующие файлы были изменены: {0}", + "summary.0": "Нет изменений", + "summary.nm": "Сделано изменений {0} в {1} файлах", + "summary.n0": "Сделано изменений {0} в одном файле" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/rus/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..b499c50310 --- /dev/null +++ b/i18n/rus/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Добавляет объявления языка.", + "vscode.extension.contributes.languages.id": "Идентификатор языка.", + "vscode.extension.contributes.languages.aliases": "Псевдонимы имен для языка.", + "vscode.extension.contributes.languages.extensions": "Расширения имен файлов, связанные с языком.", + "vscode.extension.contributes.languages.filenames": "Имена файлов, связанные с языком.", + "vscode.extension.contributes.languages.filenamePatterns": "Стандартные маски имен файлов, связанные с языком.", + "vscode.extension.contributes.languages.mimetypes": "Типы MIME, связанные с языком.", + "vscode.extension.contributes.languages.firstLine": "Регулярное выражение, соответствующее первой строке файла языка.", + "vscode.extension.contributes.languages.configuration": "Относительный путь к файлу, содержащему параметры конфигурации для языка." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/rus/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/rus/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/rus/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..f70bd8281b --- /dev/null +++ b/i18n/rus/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "Цвет фона для выделения строки в позиции курсора.", + "lineHighlightBorderBox": "Цвет фона границ вокруг строки в позиции курсора.", + "rangeHighlight": "Цвет фона выделенных диапазонов, например в функциях быстрого открытия и поиска.", + "caret": "Цвет курсора редактора.", + "editorCursorBackground": "Цвет фона курсора редактора. Позволяет настраивать цвет символа, перекрываемого прямоугольным курсором.", + "editorWhitespaces": "Цвет пробелов в редакторе.", + "editorIndentGuides": "Цвет направляющих для отступов редактора.", + "editorLineNumbers": "Цвет номеров строк редактора.", + "editorRuler": "Цвет линейки редактора.", + "editorCodeLensForeground": "Цвет переднего плана элемента CodeLens в редакторе", + "editorBracketMatchBackground": "Цвет фона парных скобок", + "editorBracketMatchBorder": "Цвет прямоугольников парных скобок", + "editorOverviewRulerBorder": "Цвет границы для линейки в окне просмотра.", + "editorGutter": "Цвет фона поля в редакторе. В поле размещаются отступы глифов и номера строк.", + "errorForeground": "Цвет волнистой линии для выделения ошибок в редакторе.", + "errorBorder": "Цвет границ волнистой линии для выделения ошибок в редакторе.", + "warningForeground": "Цвет волнистой линии для выделения предупреждений в редакторе.", + "warningBorder": "Цвет границ волнистой линии для выделения предупреждений в редакторе.", + "overviewRulerRangeHighlight": "Цвет метки линейки в окне просмотра для выделений диапазонов.", + "overviewRuleError": "Цвет метки линейки в окне просмотра для ошибок.", + "overviewRuleWarning": "Цвет метки линейки в окне просмотра для предупреждений.", + "overviewRuleInfo": "Цвет метки линейки в окне просмотра для информационных сообщений." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json b/i18n/rus/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json new file mode 100644 index 0000000000..01cc0d234f --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/accessibility/browser/accessibility.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "introMsg": "Благодарим за ознакомление со специальными возможностями VS Code.", + "status": "Состояние:", + "tabFocusModeOnMsg": "При нажатии клавиши TAB в текущем редакторе фокус ввода переместится на следующий элемент, способный его принять. Чтобы изменить это поведение, нажмите клавишу {0}.", + "tabFocusModeOnMsgNoKb": "При нажатии клавиши TAB в текущем редакторе фокус ввода переместится на следующий элемент, способный его принять. Команду {0} сейчас невозможно выполнить с помощью настраиваемого сочетания клавиш.", + "tabFocusModeOffMsg": "При нажатии клавиши TAB в текущем редакторе будет вставлен символ табуляции. Чтобы изменить это поведение, нажмите клавишу {0}.", + "tabFocusModeOffMsgNoKb": "При нажатии клавиши TAB в текущем редакторе будет вставлен символ табуляции. Команду {0} сейчас невозможно выполнить с помощью настраиваемого сочетания клавиш.", + "outroMsg": "Вы можете закрыть эту подсказку и вернуться в редактор, нажав клавишу ESCAPE.", + "ShowAccessibilityHelpAction": "Показать справку по специальным возможностям" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/rus/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..b6184a5c35 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "Перейти к скобке" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/rus/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..1cfcd4ddd1 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "Переместить курсор влево", + "caret.moveRight": "Переместить курсор вправо" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/rus/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..3600503129 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "Транспортировать буквы" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/rus/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..13df6c2791 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "Вырезать", + "actions.clipboard.copyLabel": "Копировать", + "actions.clipboard.pasteLabel": "Вставить", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "Копировать с выделением синтаксиса" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/rus/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..9c26a200d4 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "Закомментировать или раскомментировать строку", + "comment.line.add": "Закомментировать строку", + "comment.line.remove": "Раскомментировать строку", + "comment.block": "Закомментировать или раскомментировать блок" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/rus/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..c60ec566d3 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "Показать контекстное меню редактора" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/rus/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..c77b211bf0 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Найти", + "placeholder.find": "Найти", + "label.previousMatchButton": "Предыдущее соответствие", + "label.nextMatchButton": "Следующее соответствие", + "label.toggleSelectionFind": "Найти в выделении", + "label.closeButton": "Закрыть", + "label.replace": "Заменить", + "placeholder.replace": "Заменить", + "label.replaceButton": "Заменить", + "label.replaceAllButton": "Заменить все", + "label.toggleReplaceButton": "Режим \"Переключение замены\"", + "title.matchesCountLimit": "Отображаются только первые 999 результатов, но все операции поиска выполняются со всем текстом.", + "label.matchesLocation": "{0} из {1}", + "label.noResults": "Нет результатов" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/rus/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..83ef316f91 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Найти", + "placeholder.find": "Найти", + "label.previousMatchButton": "Предыдущее соответствие", + "label.nextMatchButton": "Следующее соответствие", + "label.closeButton": "Закрыть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/rus/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..f71fe6430d --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "Найти", + "findNextMatchAction": "Найти далее", + "findPreviousMatchAction": "Найти ранее", + "nextSelectionMatchFindAction": "Найти следующее выделение", + "previousSelectionMatchFindAction": "Найти предыдущее выделение", + "startReplace": "Заменить", + "addSelectionToNextFindMatch": "Добавить выделение в следующее найденное совпадение", + "addSelectionToPreviousFindMatch": "Добавить выделенный фрагмент в предыдущее найденное совпадение", + "moveSelectionToNextFindMatch": "Переместить последнее выделение в следующее найденное совпадение", + "moveSelectionToPreviousFindMatch": "Переместить последний выделенный фрагмент в предыдущее найденное совпадение", + "selectAllOccurrencesOfFindMatch": "Выбрать все вхождения найденных совпадений", + "changeAll.label": "Изменить все вхождения", + "showNextFindTermAction": "Показать следующий найденный термин", + "showPreviousFindTermAction": "Показать предыдущий найденный термин" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/rus/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..8276220d87 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "Развернуть", + "unFoldRecursivelyAction.label": "Развернуть рекурсивно", + "foldAction.label": "Свернуть", + "foldRecursivelyAction.label": "Свернуть рекурсивно", + "foldAllAction.label": "Свернуть все", + "unfoldAllAction.label": "Развернуть все", + "foldLevelAction.label": "Уровень папки {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/rus/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..cd371d9524 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "Внесена одна правка форматирования в строке {0}.", + "hintn1": "Внесены правки форматирования ({0}) в строке {1}.", + "hint1n": "Внесена одна правка форматирования между строками {0} и {1}.", + "hintnn": "Внесены правки форматирования ({0}) между строками {1} и {2}.", + "formatDocument.label": "Форматировать документ", + "formatSelection.label": "Форматировать выбранный фрагмент" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json b/i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json new file mode 100644 index 0000000000..65fd1afcdf --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "Определение для \"{0}\" не найдено.", + "generic.noResults": "Определения не найдены.", + "meta.title": " — определения {0}", + "actions.goToDecl.label": "Перейти к определению", + "actions.goToDeclToSide.label": "Открыть определение сбоку", + "actions.previewDecl.label": "Показать определение", + "goToImplementation.noResultWord": "Не найдена реализация для \"{0}\".", + "goToImplementation.generic.noResults": "Не найдена реализация.", + "meta.implementations.title": "— {0} реализаций", + "actions.goToImplementation.label": "Перейти к реализации", + "actions.peekImplementation.label": "Показать реализацию", + "goToTypeDefinition.noResultWord": "Не найдено определение типа для \"{0}\".", + "goToTypeDefinition.generic.noResults": "Не найдено определение типа.", + "meta.typeDefinitions.title": "— {0} определений типов", + "actions.goToTypeDefinition.label": "Перейти к определению типа", + "actions.peekTypeDefinition.label": "Показать определение типа", + "multipleResults": "Щелкните, чтобы отобразить определения ({0})." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..c8aa6e85cc --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "Определение для \"{0}\" не найдено.", + "generic.noResults": "Определения не найдены.", + "meta.title": " — определения {0}", + "actions.goToDecl.label": "Перейти к определению", + "actions.goToDeclToSide.label": "Открыть определение сбоку", + "actions.previewDecl.label": "Показать определение", + "goToImplementation.noResultWord": "Не найдена реализация для \"{0}\".", + "goToImplementation.generic.noResults": "Не найдена реализация.", + "meta.implementations.title": "— {0} реализаций", + "actions.goToImplementation.label": "Перейти к реализации", + "actions.peekImplementation.label": "Показать реализацию", + "goToTypeDefinition.noResultWord": "Не найдено определение типа для \"{0}\".", + "goToTypeDefinition.generic.noResults": "Не найдено определение типа.", + "meta.typeDefinitions.title": "— {0} определений типов", + "actions.goToTypeDefinition.label": "Перейти к определению типа", + "actions.peekTypeDefinition.label": "Показать определение типа" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..9acfe15427 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "Щелкните, чтобы отобразить определения ({0})." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/rus/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..19f0d749bf --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "Перейти к следующей ошибке или предупреждению", + "markerAction.previous.label": "Перейти к предыдущей ошибке или предупреждению", + "editorMarkerNavigationError": "Цвет ошибки в мини-приложении навигации по меткам редактора.", + "editorMarkerNavigationWarning": "Цвет предупреждения в мини-приложении навигации по меткам редактора.", + "editorMarkerNavigationBackground": "Фон мини-приложения навигации по меткам редактора." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/rus/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..1f3bc6457b --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "Показать при наведении" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/rus/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..168091750c --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "Идет загрузка..." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/rus/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..c57f8645c7 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "Заменить предыдущим значением", + "InPlaceReplaceAction.next.label": "Заменить следующим значением" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/rus/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..057ee845a6 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "Преобразовать отступ в пробелы", + "indentationToTabs": "Преобразовать отступ в шаги табуляции", + "configuredTabSize": "Настроенный размер шага табуляции", + "selectTabWidth": "Выбрать размер шага табуляции для текущего файла", + "indentUsingTabs": "Отступ с использованием табуляции", + "indentUsingSpaces": "Отступ с использованием пробелов", + "detectIndentation": "Определение отступа от содержимого", + "editor.reindentlines": "Повторно расставить отступы строк" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json b/i18n/rus/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..0008b267bd --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Разработчик: проверка областей TM", + "inspectTMScopesWidget.loading": "Идет загрузка..." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/rus/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..6df72037a0 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "Копировать строку сверху", + "lines.copyDown": "Копировать строку снизу", + "lines.moveUp": "Переместить строку вверх", + "lines.moveDown": "Переместить строку вниз", + "lines.sortAscending": "Сортировка строк по возрастанию", + "lines.sortDescending": "Сортировка строк по убыванию", + "lines.trimTrailingWhitespace": "Удалить конечные символы-разделители", + "lines.delete": "Удалить строку", + "lines.indent": "Увеличить отступ", + "lines.outdent": "Уменьшить отступ", + "lines.insertBefore": "Вставить строку выше", + "lines.insertAfter": "Вставить строку ниже", + "lines.deleteAllLeft": "Удалить все слева", + "lines.deleteAllRight": "Удалить все справа", + "lines.joinLines": "_Объединить строки", + "editor.transpose": "Транспонировать символы вокруг курсора", + "editor.transformToUppercase": "Преобразовать в верхний регистр", + "editor.transformToLowercase": "Преобразовать в нижний регистр" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/rus/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..7080c311ad --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "Щелкните с нажатой клавишей Cmd, чтобы перейти по ссылке", + "links.navigate": "Щелкните с нажатой клавишей Ctrl, чтобы перейти по ссылке", + "links.command.mac": "Для выполнения команды щелкните ее, удерживая нажатой клавишу CMD", + "links.command": "Для выполнения команды щелкните ее, удерживая нажатой клавишу CTRL", + "links.navigate.al": "Щелкните с нажатой клавишей ALT, чтобы перейти по ссылке.", + "links.command.al": "Для выполнения команды щелкните ее, удерживая нажатой клавишу ALT", + "invalid.url": "Не удалось открыть ссылку, так как она имеет неправильный формат: {0}", + "missing.url": "Не удалось открыть ссылку, у нее отсутствует целевой объект.", + "label": "Открыть ссылку" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/rus/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..6138648b59 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "Добавить курсор выше", + "mutlicursor.insertBelow": "Добавить курсор ниже", + "mutlicursor.insertAtEndOfEachLineSelected": "Добавить курсоры к окончаниям строк" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/rus/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..1e5d4fbdb4 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "Переключить подсказки к параметрам" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/rus/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..bc08fc7bc3 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0}, подсказка" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/rus/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..abad8e1fc3 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "Показать исправления ({0})", + "quickFix": "Показать исправления", + "quickfix.trigger.label": "Быстрое исправление" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..5efafa362b --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": " — ссылки {0}", + "references.action.label": "Найти все ссылки" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..64f02a89c9 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "Идет загрузка..." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..c2233988ff --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "ссылка в {0} в строке {1} и символе {2}", + "aria.fileReferences.1": "1 символ в {0}, полный путь: {1}", + "aria.fileReferences.N": "{0} символов в {1}, полный путь: {2} ", + "aria.result.0": "Результаты не найдены", + "aria.result.1": "Обнаружен 1 символ в {0}", + "aria.result.n1": "Обнаружено {0} символов в {1}", + "aria.result.nm": "Обнаружено {0} символов в {1} файлах" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..99ebbf521f --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "Не удалось разрешить файл.", + "referencesCount": "Ссылок: {0}", + "referenceCount": "{0} ссылка", + "missingPreviewMessage": "предварительный просмотр недоступен", + "treeAriaLabel": "Ссылки", + "noResults": "Результаты отсутствуют", + "peekView.alternateTitle": "Ссылки", + "peekViewTitleBackground": "Цвет фона области заголовка быстрого редактора.", + "peekViewTitleForeground": "Цвет заголовка быстрого редактора.", + "peekViewTitleInfoForeground": "Цвет сведений о заголовке быстрого редактора.", + "peekViewBorder": "Цвет границ быстрого редактора и массива.", + "peekViewResultsBackground": "Цвет фона в списке результатов представления быстрого редактора.", + "peekViewResultsMatchForeground": "Цвет переднего плана узлов строки в списке результатов быстрого редактора.", + "peekViewResultsFileForeground": "Цвет переднего плана узлов файла в списке результатов быстрого редактора.", + "peekViewResultsSelectionBackground": "Цвет фона выбранной записи в списке результатов быстрого редактора.", + "peekViewResultsSelectionForeground": "Цвет переднего плана выбранной записи в списке результатов быстрого редактора.", + "peekViewEditorBackground": "Цвет фона быстрого редактора.", + "peekViewEditorGutterBackground": "Цвет фона поля в окне быстрого редактора.", + "peekViewResultsMatchHighlight": "Цвет выделения совпадений в списке результатов быстрого редактора.", + "peekViewEditorMatchHighlight": "Цвет выделения совпадений в быстром редакторе." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/rus/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..fcd5e2dec5 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "Результаты отсутствуют.", + "aria": "«{0}» успешно переименован в «{1}». Сводка: {2}", + "rename.failed": "Не удалось переименовать.", + "rename.label": "Переименовать символ" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/rus/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..1c858daeb4 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "Введите новое имя для входных данных и нажмите клавишу ВВОД для подтверждения." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/rus/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..be82eb7ebf --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "Развернуть выделение", + "smartSelect.shrink": "Сжать выделение" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/rus/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..1df59e094c --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "При принятии \"{0}\" был добавлен следующий текст: \"{1}\"", + "suggest.trigger.label": "Переключить предложение" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/rus/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..54bad63a65 --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "Цвет фона виджета подсказок.", + "editorSuggestWidgetBorder": "Цвет границ виджета подсказок.", + "editorSuggestWidgetForeground": "Цвет переднего плана мини-приложения предложений.", + "editorSuggestWidgetSelectedBackground": "Фоновый цвет выбранной записи в мини-приложении предложений.", + "editorSuggestWidgetHighlightForeground": "Цвет выделения соответствия в мини-приложении предложений.", + "readMore": "Подробнее...{0}", + "suggestionWithDetailsAriaLabel": "{0}, предложение, содержит данные", + "suggestionAriaLabel": "{0}, предложение", + "readLess": "Кратко...{0}", + "suggestWidget.loading": "Идет загрузка...", + "suggestWidget.noSuggestions": "Предложения отсутствуют.", + "suggestionAriaAccepted": "{0}, принято", + "ariaCurrentSuggestionWithDetails": "{0}, предложение, содержит данные", + "ariaCurrentSuggestion": "{0}, предложение" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/rus/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..923362efbe --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "Переключение клавиши TAB перемещает фокус." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/rus/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..ce9cb007ab --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "Цвет фона символа при доступе на чтение, например считывании переменной.", + "wordHighlightStrong": "Цвет фона символа при доступе на запись, например записи переменной.", + "overviewRulerWordHighlightForeground": "Цвет метки линейки в окне просмотра для выделений символов.", + "overviewRulerWordHighlightStrongForeground": "Цвет метки линейки в окне просмотра для выделений символов, доступных для записи. " +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/rus/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..396a0f40bc --- /dev/null +++ b/i18n/rus/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Закрыть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/rus/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..ddef6e66b8 --- /dev/null +++ b/i18n/rus/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Неизвестный язык в contributes.{0}.language. Указанное значение: {1}", + "invalid.scopeName": "В contributes.{0}.scopeName требуется строка. Указанное значение: {1}", + "invalid.path.0": "В contributes.{0}.path требуется строка. Указанное значение: {1}", + "invalid.injectTo": "Недопустимое значение в \"contributes.{0}.injectTo\". Должен быть задан массив имен языковых областей. Указанное значение: {1}", + "invalid.embeddedLanguages": "Недопустимое значение в \"contributes.{0}.embeddedLanguages\". Оно должно быть сопоставлением объекта между именем области и языком. Указанное значение: {1}.", + "invalid.path.1": "contributes.{0}.path ({1}) должен был быть включен в папку расширения ({2}). Это может сделать расширение непереносимым.", + "no-tm-grammar": "Нет грамматики TM, зарегистрированной для этого языка." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/rus/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..8e1e928e76 --- /dev/null +++ b/i18n/rus/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Ошибок при анализе {0}: {1}", + "schema.openBracket": "Открывающий символ скобки или строковая последовательность.", + "schema.closeBracket": "Закрывающий символ скобки или строковая последовательность.", + "schema.comments": "Определяет символы комментариев", + "schema.blockComments": "Определяет способ маркировки комментариев.", + "schema.blockComment.begin": "Последовательность символов, открывающая блок комментариев.", + "schema.blockComment.end": "Последовательность символов, закрывающая блок комментариев.", + "schema.lineComment": "Последовательность символов, с которой начинается строка комментария.", + "schema.brackets": "Определяет символы скобок, увеличивающие или уменьшающие отступ.", + "schema.autoClosingPairs": "Определяет пары скобок. Когда введена открывающая скобка, автоматически добавляется закрывающая.", + "schema.autoClosingPairs.notIn": "Определяет список областей, где автоматические пары отключены.", + "schema.surroundingPairs": "Определяет пары скобок, в которые заключается выбранная строка.", + "schema.wordPattern": "Определение слова для языка.", + "schema.wordPattern.pattern": "Шаблон регулярного выражения, используемый для сопоставления слов.", + "schema.wordPattern.flags": "Флаги регулярного выражения, используемого для сопоставления слов.", + "schema.wordPattern.flags.errorMessage": "Должно соответствовать шаблону \"/^([gimuy]+)$/\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/rus/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..85c2289ef9 --- /dev/null +++ b/i18n/rus/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Добавляет разметчики TextMate.", + "vscode.extension.contributes.grammars.language": "Идентификатор языка, для которого добавляется этот синтаксис.", + "vscode.extension.contributes.grammars.scopeName": "Имя области TextMate, используемое в файле tmLanguage.", + "vscode.extension.contributes.grammars.path": "Путь к файлу tmLanguage. Путь указывается относительно папки расширения и обычно начинается с \"./syntaxes/\".", + "vscode.extension.contributes.grammars.embeddedLanguages": "Сопоставление имени области и идентификатора языка, если грамматика содержит внедренные языки.", + "vscode.extension.contributes.grammars.injectTo": "Список имен языковых областей, в которые вставляется эта грамматика." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/rus/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/rus/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/rus/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..5ffffc2d79 --- /dev/null +++ b/i18n/rus/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "элементы меню должны быть массивом", + "requirestring": "свойство \"{0}\" является обязательным и должно иметь тип string", + "optstring": "свойство \"{0}\" может быть опущено или должно иметь тип string", + "vscode.extension.contributes.menuItem.command": "Идентификатор команды, которую нужно выполнить. Эта команда должна быть объявлена в разделе commands", + "vscode.extension.contributes.menuItem.alt": "Идентификатор альтернативной команды, которую нужно выполнить. Эта команда должна быть объявлена в разделе commands", + "vscode.extension.contributes.menuItem.when": "Условие, которое должно иметь значение True, чтобы отображался этот элемент", + "vscode.extension.contributes.menuItem.group": "Группа, к которой принадлежит эта команда", + "vscode.extension.contributes.menus": "Добавляет элементы меню в редактор", + "menus.commandPalette": "Палитра команд", + "menus.editorTitle": "Главное меню редактора", + "menus.editorContext": "Контекстное меню редактора", + "menus.explorerContext": "Контекстное меню проводника", + "menus.editorTabContext": "Контекстное меню вкладок редактора", + "menus.debugCallstackContext": "Контекстное меню стека вызовов при отладке", + "menus.scmTitle": "Меню заголовков для системы управления версиями", + "menus.resourceGroupContext": "Контекстное меню группы ресурсов для системы управления версиями", + "menus.resourceStateContext": "Контекстное меню состояния ресурсов для системы управления версиями", + "view.viewTitle": "Меню заголовка для окна участников", + "view.itemContext": "Контекстное меню элемента для окна участников", + "nonempty": "требуется непустое значение.", + "opticon": "Свойство icon может быть пропущено или должно быть строкой или литералом, например \"{dark, light}\"", + "requireStringOrObject": "Свойство \"{0}\" обязательно и должно иметь тип \"string\" или \"object\"", + "requirestrings": "Свойства \"{0}\" и \"{1}\" обязательны и должны иметь тип \"string\"", + "vscode.extension.contributes.commandType.command": "Идентификатор выполняемой команды", + "vscode.extension.contributes.commandType.title": "Название команды в пользовательском интерфейсе", + "vscode.extension.contributes.commandType.category": "(Необязательно.) Строка категорий, по которым команды группируются в пользовательском интерфейсе", + "vscode.extension.contributes.commandType.icon": "(Дополнительно) Значок, который используется для представления команды в пользовательском интерфейсе. Это путь к файлу или конфигурация с возможностью применения тем", + "vscode.extension.contributes.commandType.icon.light": "Путь к значку, если используется светлая тема", + "vscode.extension.contributes.commandType.icon.dark": "Путь к значку, если используется темная тема", + "vscode.extension.contributes.commands": "Добавляет команды в палитру команд.", + "dup": "Команда \"{0}\" встречается несколько раз в разделе commands.", + "menuId.invalid": "\"{0}\" не является допустимым идентификатором меню", + "missing.command": "Элемент меню ссылается на команду \"{0}\", которая не определена в разделе commands.", + "missing.altCommand": "Элемент меню ссылается на альтернативную команду \"{0}\", которая не определена в разделе commands.", + "dupe.command": "Элемент меню ссылается на одну и ту же команду как команду по умолчанию и альтернативную команду", + "nosupport.altCommand": "Сейчас только группа navigation меню editor/title поддерживает альтернативные команды" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/rus/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..41b5f23f77 --- /dev/null +++ b/i18n/rus/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "Переопределения конфигурации по умолчанию", + "overrideSettings.description": "Настройка переопределяемых параметров редактора для языка {0}.", + "overrideSettings.defaultDescription": "Настройка параметров редактора, переопределяемых для языка.", + "config.property.languageDefault": "Невозможно зарегистрировать \"{0}\". Оно соответствует шаблону свойства '\\\\[.*\\\\]$' для описания параметров редактора, определяемых языком. Используйте участие configurationDefaults.", + "config.property.duplicate": "Невозможно зарегистрировать \"{0}\". Это свойство уже зарегистрировано." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/environment/node/argv.i18n.json b/i18n/rus/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..c5a7509ae1 --- /dev/null +++ b/i18n/rus/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "Аргументы в режиме \"--goto\" должны быть в формате \"ФАЙЛ(:СТРОКА(:СИМВОЛ))\".", + "add": "Добавление папок в последнее активное окно.", + "goto": "Открытие файла по указанному пути с выделением указанного символа в указанной строке.", + "locale": "Языковой стандарт, который следует использовать (например, en-US или zh-TW).", + "newWindow": "Принудительно запустить новый экземпляр Code.", + "performance": "Запустите с включенной командой \"Developer: Startup Performance\".", + "prof-startup": "Запустить профилировщик ЦП при запуске", + "reuseWindow": "Принудительно открыть файл или папку в последнем активном окне.", + "userDataDir": "Указывает каталог, в котором хранятся данные пользователей, используется в случае выполнения от имени привилегированного пользователя.", + "verbose": "Печать подробного вывода (подразумевает использование параметра \"--wait\").", + "wait": "Дождаться закрытия окна, прежде чем вернуть результат.", + "extensionHomePath": "Задайте корневой путь для расширений.", + "listExtensions": "Перечислить существующие расширения.", + "showVersions": "Показать версии установленных расширений при указании параметра --list-extension.", + "installExtension": "Устанавливает расширение.", + "uninstallExtension": "Удаляет расширение.", + "experimentalApis": "Включает предложенные функции API для расширения.", + "disableExtensions": "Отключить все установленные расширения.", + "disableGPU": "Отключить аппаратное ускорение GPU.", + "version": "Печать версии.", + "help": "Распечатать данные об использовании.", + "usage": "Использование", + "options": "параметры", + "paths": "пути", + "optionsUpperCase": "Параметры" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/rus/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..cbcd3f19e0 --- /dev/null +++ b/i18n/rus/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Нет рабочей области." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/rus/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..8a9b615207 --- /dev/null +++ b/i18n/rus/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Расширения", + "preferences": "Параметры" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/rus/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..a47cd79670 --- /dev/null +++ b/i18n/rus/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Расширение не найдено", + "noCompatible": "Не удалось найти версию {0}, совместимую с этой версией кода." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/rus/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..f702958338 --- /dev/null +++ b/i18n/rus/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "Недопустимое расширение: package.json не является файлом JSON.", + "restartCode": "Перезапустите код перед переустановкой {0}.", + "installDependeciesConfirmation": "При установке \"{0}\" также устанавливаются зависимости. Вы хотите продолжить?", + "install": "Да", + "doNotInstall": "Нет", + "uninstallDependeciesConfirmation": "Вы хотите удалить \"{0}\" отдельно или вместе с зависимостями?", + "uninstallOnly": "Только", + "uninstallAll": "Все", + "cancel": "Отмена", + "uninstallConfirmation": "Вы действительно хотите удалить \"{0}\"?", + "ok": "ОК", + "singleDependentError": "Не удается удалить расширение \"{0}\". От него зависит расширение \"{1}\".", + "twoDependentsError": "Не удается удалить расширение \"{0}\". От него зависят расширения \"{1}\" и \"{2}\".", + "multipleDependentsError": "Не удается удалить расширение \"{0}\". От него зависят расширения \"{1}\", \"{2}\" и другие.", + "notExists": "Не удалось найти расширение" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/rus/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..9831e698c5 --- /dev/null +++ b/i18n/rus/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "Не удалось активировать расширение \"{1}\". Причина: неизвестный зависимый компонент \"{0}\".", + "failedDep1": "Не удалось активировать расширение \"{1}\". Причина: ошибка активации зависимого компонента \"{0}\".", + "failedDep2": "Не удалось активировать расширение \"{0}\". Причина: более 10 уровней зависимостей (скорее всего, цикл зависимостей).", + "activationError": "Ошибка активации расширения \"{0}\": {1}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/rus/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..8b74c83669 --- /dev/null +++ b/i18n/rus/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "Для расширений VS Code указывает версию VS Code, с которой совместимо расширение. Она не может быть задана как \"*\". Например, ^0.10.5 сообщает о совместимости с минимальной версией VS Code 0.10.5.", + "vscode.extension.publisher": "Издатель расширения VS Code.", + "vscode.extension.displayName": "Отображаемое имя расширения, используемого в коллекции VS Code.", + "vscode.extension.categories": "Категории, используемые коллекцией VS Code для классификации расширения.", + "vscode.extension.galleryBanner": "Баннер, используемый в магазине VS Code.", + "vscode.extension.galleryBanner.color": "Цвет баннера в заголовке страницы магазина VS Code.", + "vscode.extension.galleryBanner.theme": "Цветовая тема для шрифта, используемого в баннере.", + "vscode.extension.contributes": "Все публикации расширения VS Code, представленные этим пакетом.", + "vscode.extension.preview": "Добавляет метку \"Предварительная версия\" для расширения в Marketplace.", + "vscode.extension.activationEvents": "События активации для расширения кода VS Code.", + "vscode.extension.activationEvents.onLanguage": "Событие активации выдается каждый раз, когда открывается файл, который разрешается к указанному языку.", + "vscode.extension.activationEvents.onCommand": "Событие активации выдается каждый раз при вызове указанной команды.", + "vscode.extension.activationEvents.onDebug": "Событие активации выдается каждый раз при запуске сеанса отладки указанного типа.", + "vscode.extension.activationEvents.workspaceContains": "Событие активации выдается каждый раз при открытии папки, содержащей по крайней мере один файл, который соответствует указанной стандартной маске.", + "vscode.extension.activationEvents.onView": "Событие активации выдается каждый раз при развертывании указанного окна.", + "vscode.extension.activationEvents.star": "Событие активации выдается при запуске VS Code. Для удобства пользователя используйте это событие в своем расширении только в том случае, если другие сочетания событий не подходят.", + "vscode.extension.badges": "Массив эмблем, отображаемых на боковой панели страницы расширения Marketplace.", + "vscode.extension.badges.url": "URL-адрес изображения эмблемы.", + "vscode.extension.badges.href": "Ссылка на эмблему.", + "vscode.extension.badges.description": "Описание эмблемы.", + "vscode.extension.extensionDependencies": "Зависимости от других расширений. Идентификатор расширения — всегда ${publisher}.${name}. Например: vscode.csharp.", + "vscode.extension.scripts.prepublish": "Скрипт, выполняемый перед публикацией пакета в качестве расширения VS Code.", + "vscode.extension.icon": "Путь к значку размером 128 x 128 пикселей." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/rus/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..4f0833b9d2 --- /dev/null +++ b/i18n/rus/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "Не удалось проанализировать значение engines.vscode {0}. Используйте, например, ^0.10.0, ^1.2.3, ^0.11.0, ^0.10.x и т. д.", + "versionSpecificity1": "Версия, указанная в engines.vscode ({0}), недостаточно конкретная. Для версий vscode до 1.0.0 укажите по крайней мере основной и дополнительный номер версии. Например, 0.10.0, 0.10.x, 0.11.0 и т. д.", + "versionSpecificity2": "Версия, указанная в engines.vscode ({0}), недостаточно конкретная. Для версий vscode после 1.0.0 укажите по крайней мере основной номер версии. Например, 1.10.0, 1.10.x, 1.x.x, 2.x.x и т. д.", + "versionMismatch": "Расширение несовместимо с кодом \"{0}\". Расширению требуется: {1}.", + "extensionDescription.empty": "Пустое описание расширения", + "extensionDescription.publisher": "свойство \"{0}\" является обязательным и должно иметь тип string", + "extensionDescription.name": "свойство \"{0}\" является обязательным и должно иметь тип string", + "extensionDescription.version": "свойство \"{0}\" является обязательным и должно иметь тип string", + "extensionDescription.engines": "свойство \"{0}\" является обязательным и должно быть типа object", + "extensionDescription.engines.vscode": "свойство \"{0}\" является обязательным и должно иметь тип string", + "extensionDescription.extensionDependencies": "свойство \"{0}\" может быть опущено или должно быть типа \"string []\"", + "extensionDescription.activationEvents1": "свойство \"{0}\" может быть опущено или должно быть типа \"string []\"", + "extensionDescription.activationEvents2": "оба свойства, \"{0}\" и \"{1}\", должны быть либо указаны, либо опущены", + "extensionDescription.main1": "свойство \"{0}\" может быть опущено или должно иметь тип string", + "extensionDescription.main2": "Ожидается, что функция main ({0}) будет включена в папку расширения ({1}). Из-за этого расширение может стать непереносимым.", + "extensionDescription.main3": "оба свойства, \"{0}\" и \"{1}\", должны быть либо указаны, либо опущены", + "notSemver": "Версия расширения несовместима с semver." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/rus/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..b122e98ff9 --- /dev/null +++ b/i18n/rus/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "Новое окно", + "newWindowDesc": "Открывает новое окно", + "recentFolders": "Последние рабочие области", + "folderDesc": "{0} {1}", + "codeWorkspace": "Рабочая область кода" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/rus/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..4165259b86 --- /dev/null +++ b/i18n/rus/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "ОК", + "integrity.dontShowAgain": "Больше не показывать", + "integrity.moreInfo": "Дополнительные сведения", + "integrity.prompt": "Похоже, ваша установка {0} повреждена. Повторите установку." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/rus/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..5d55bae2fc --- /dev/null +++ b/i18n/rus/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "Добавляет конфигурацию схемы JSON.", + "contributes.jsonValidation.fileMatch": "Шаблон файла для сопоставления, например \"package.json\" или \"*.launch\".", + "contributes.jsonValidation.url": "URL-адрес схемы (\"http:\", \"https:\") или относительный путь к папке расширения (\"./\").", + "invalid.jsonValidation": "configuration.jsonValidation должно быть массивом", + "invalid.fileMatch": "Необходимо определить configuration.jsonValidation.fileMatch", + "invalid.url": "Значение configuration.jsonValidation.url должно быть URL-адресом или относительным путем", + "invalid.url.fileschema": "Значение configuration.jsonValidation.url является недопустимым относительным URL-адресом: {0}", + "invalid.url.schema": "Значение configuration.jsonValidation.url должно начинаться с \"http:\", \"https:\" или \"./\" для ссылки на схемы, содержащиеся в расширении" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/rus/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..553e6b5f92 --- /dev/null +++ b/i18n/rus/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "Была нажата клавиша ({0}). Ожидание нажатия второй клавиши сочетания...", + "missing.chord": "Сочетание клавиш ({0} и {1}) не является командой." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/keybinding/common/keybindingLabels.i18n.json b/i18n/rus/src/vs/platform/keybinding/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..193c9d3153 --- /dev/null +++ b/i18n/rus/src/vs/platform/keybinding/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "CTRL", + "shiftKey": "SHIFT", + "altKey": "ALT", + "windowsKey": "Клавиша Windows", + "ctrlKey.long": "CTRL", + "shiftKey.long": "SHIFT", + "altKey.long": "ALT", + "cmdKey.long": "Команда", + "windowsKey.long": "Клавиша Windows" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/rus/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..9a813abad7 --- /dev/null +++ b/i18n/rus/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "Свойство loop поддерживается только в сопоставителе последней строки.", + "ProblemPatternParser.problemPattern.missingRegExp": "В шаблоне проблем отсутствует регулярное выражение.", + "ProblemPatternParser.problemPattern.missingProperty": "Недопустимый шаблон проблемы. Шаблон должен содержать по крайней мере файл, сообщение и группу сопоставления строк или расположений.", + "ProblemPatternParser.invalidRegexp": "Ошибка: строка {0} не является допустимым регулярным выражением.\n", + "ProblemPatternSchema.regexp": "Регулярное выражение для поиска ошибки, предупреждения или информации в выходных данных.", + "ProblemPatternSchema.file": "Индекс группы сопоставления для имени файла. Если он не указан, используется значение 1.", + "ProblemPatternSchema.location": "Индекс группы сопоставления для расположения проблемы. Допустимые шаблоны расположения: (строка), (строка,столбец) и (начальная_строка,начальный_столбец,конечная_строка,конечный_столбец). Если индекс не указан, предполагается шаблон (строка,столбец).", + "ProblemPatternSchema.line": "Индекс группы сопоставления для строки проблемы. Значение по умолчанию — 2.", + "ProblemPatternSchema.column": "Индекс группы сопоставления для символа в строке проблемы. Значение по умолчанию — 3", + "ProblemPatternSchema.endLine": "Индекс группы сопоставления для конечной строки проблемы. По умолчанию не определен.", + "ProblemPatternSchema.endColumn": "Индекс группы сопоставления для конечного символа проблемы. По умолчанию не определен.", + "ProblemPatternSchema.severity": "Индекс группы сопоставления для серьезности проблемы. По умолчанию не определен.", + "ProblemPatternSchema.code": "Индекс группы сопоставления для кода проблемы. По умолчанию не определен.", + "ProblemPatternSchema.message": "Индекс группы сопоставления для сообщения. Если он не указан, значение по умолчанию — 4 при незаданном расположении. В противном случае значение по умолчанию — 5.", + "ProblemPatternSchema.loop": "В цикле многострочного сопоставителя указывает, выполняется ли этот шаблон в цикле, пока он соответствует. Может указываться только для последнего шаблона в многострочном шаблоне.", + "NamedProblemPatternSchema.name": "Имя шаблона проблем.", + "NamedMultiLineProblemPatternSchema.name": "Имя шаблона многострочных проблем.", + "NamedMultiLineProblemPatternSchema.patterns": "Фактические шаблоны.", + "ProblemPatternExtPoint": "Публикует шаблоны проблем", + "ProblemPatternRegistry.error": "Недопустимый шаблон проблем. Он будет пропущен.", + "ProblemMatcherParser.noProblemMatcher": "Ошибка: описание невозможно преобразовать в сопоставитель проблем:\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "Ошибка: в описании не задан допустимый шаблон проблемы:\n{0}\n", + "ProblemMatcherParser.noOwner": "Ошибка: в описании не задан владелец:\n{0}\n", + "ProblemMatcherParser.noFileLocation": "Ошибка: в описании не указано расположение файла:\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "Информация: неизвестная степень серьезности {0}. Допустимые значения: ошибка, предупреждение и информация.\n", + "ProblemMatcherParser.noDefinedPatter": "Ошибка: шаблон с идентификатором {0} не существует.", + "ProblemMatcherParser.noIdentifier": "Ошибка: свойство шаблона ссылается на пустой идентификатор.", + "ProblemMatcherParser.noValidIdentifier": "Ошибка: свойство шаблона {0} не является допустимым именем переменной шаблона.", + "ProblemMatcherParser.problemPattern.watchingMatcher": "В сопоставителе проблем должны быть определены как начальный, так и конечный шаблоны для отслеживания.", + "ProblemMatcherParser.invalidRegexp": "Ошибка: строка {0} не является допустимым регулярным выражением.\n", + "WatchingPatternSchema.regexp": "Регулярное выражение для обнаружения начала или конца фоновой задачи.", + "WatchingPatternSchema.file": "Индекс группы сопоставления для имени файла. Может быть опущен.", + "PatternTypeSchema.name": "Имя добавленного или предопределенного шаблона", + "PatternTypeSchema.description": "Шаблон проблем либо имя добавленного или предопределенного шаблона проблем. Его можно опустить, если указано базовое значение.", + "ProblemMatcherSchema.base": "Имя используемого базового сопоставителя проблем.", + "ProblemMatcherSchema.owner": "Владелец проблемы в Code. Можно опустить, если указан элемент base. Если владелец опущен, а элемент base не указан, значение по умолчанию — \"внешний\".", + "ProblemMatcherSchema.severity": "Серьезность по умолчанию для выявленных проблем. Используется, если в шаблоне не определена группа сопоставления для серьезности.", + "ProblemMatcherSchema.applyTo": "Определяет, относится ли проблема, о которой сообщается для текстового документа, только к открытым, только к закрытым или ко всем документам.", + "ProblemMatcherSchema.fileLocation": "Определяет способ интерпретации имен файлов, указываемых в шаблоне проблемы.", + "ProblemMatcherSchema.background": "Шаблоны для отслеживания начала и окончания фоновой задачи.", + "ProblemMatcherSchema.background.activeOnStart": "Если задано значение true, средство мониторинга фоновых задач будет находиться в активном режиме при запуске задачи. Это аналогично выдаче строки, соответствующей шаблону начала.", + "ProblemMatcherSchema.background.beginsPattern": "При наличии соответствия в выходных данных выдается сигнал о запуске фоновой задачи.", + "ProblemMatcherSchema.background.endsPattern": "При наличии соответствия в выходных данных выдается сигнал о завершении фоновой задачи.", + "ProblemMatcherSchema.watching.deprecated": "Это свойство для отслеживания устарело. Используйте цвет фона.", + "ProblemMatcherSchema.watching": "Шаблоны для отслеживания начала и окончания шаблона отслеживания.", + "ProblemMatcherSchema.watching.activeOnStart": "Если задано значение true, наблюдатель находится в активном режиме, когда задача запускается. Это равносильно выдаче строки, соответствующей шаблону начала.", + "ProblemMatcherSchema.watching.beginsPattern": "При соответствии в выходных данных сообщает о запуске задачи наблюдения.", + "ProblemMatcherSchema.watching.endsPattern": "При соответствии в выходных данных сообщает о завершении задачи наблюдения.", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "Это свойство устарело. Используйте свойство просмотра.", + "LegacyProblemMatcherSchema.watchedBegin": "Регулярное выражение, сообщающее о том, что отслеживаемая задача начинает выполняться в результате активации отслеживания файлов.", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "Это свойство устарело. Используйте свойство просмотра.", + "LegacyProblemMatcherSchema.watchedEnd": "Регулярное выражение, сообщающее о том, что отслеживаемая задача завершает выполнение.", + "NamedProblemMatcherSchema.name": "Имя сопоставителя проблем, используемого для ссылки.", + "NamedProblemMatcherSchema.label": "Метка сопоставителя проблем в удобном для чтения формате.", + "ProblemMatcherExtPoint": "Публикует сопоставители проблем", + "msCompile": "Проблемы компилятора Microsoft", + "lessCompile": "Скрыть проблемы", + "gulp-tsc": "Проблемы TSC для Gulp", + "jshint": "Проблемы JSHint", + "jshint-stylish": "Проблемы JSHint, связанные со стилем", + "eslint-compact": "Проблемы ESLint, связанные с компактностью", + "eslint-stylish": "Проблемы ESLint, связанные со стилем", + "go": "Перейти к проблемам" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/message/common/message.i18n.json b/i18n/rus/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..4c63dd4f05 --- /dev/null +++ b/i18n/rus/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Закрыть", + "later": "Позже", + "cancel": "Отмена" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/request/node/request.i18n.json b/i18n/rus/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..627f5dfbfa --- /dev/null +++ b/i18n/rus/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "Используемый параметр прокси. Если он не задан, он будет взят из переменных среды http_proxy и https_proxy.", + "strictSSL": "Должен ли сертификат прокси-сервера проверяться по списку предоставленных ЦС.", + "proxyAuthorization": "Значение, отправляемое как заголовок \"Proxy-Authorization\" для каждого сетевого запроса." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/rus/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..e7be7b9e23 --- /dev/null +++ b/i18n/rus/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Телеметрия", + "telemetry.enableTelemetry": "Разрешить отправку сведений об использовании и ошибках в корпорацию Майкрософт." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/rus/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..a35b345883 --- /dev/null +++ b/i18n/rus/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "Добавляет цвета тем, определяемые расширением ", + "contributes.color.id": "Идентификатор цвета темы", + "contributes.color.id.format": "Идентификаторы необходимо указывать в форме aa [.bb]*", + "contributes.color.description": "Описание цвета темы", + "contributes.defaults.light": "Цвет по умолчанию для светлых тем. Укажите значение цвета в шестнадцатеричном формате (#RRGGBB[AA]) или идентификатор цвета темы.", + "contributes.defaults.dark": "Цвет по умолчанию для темных тем. Укажите значение цвета в шестнадцатеричном формате (#RRGGBB[AA]) или идентификатор цвета темы.", + "contributes.defaults.highContrast": "Цвет по умолчанию для тем с высоким контрастом. Укажите значение цвета в шестнадцатеричном формате (#RRGGBB[AA]) или идентификатор цвета темы.", + "invalid.colorConfiguration": "'configuration.colors' должен быть массивом", + "invalid.default.colorType": "{0} должен представлять собой значение цвета в шестнадцатеричном формате (#RRGGBB[AA] или #RGB[A]) или идентификатор цвета темы.", + "invalid.id": "Параметр 'configuration.colors.id' должен быть указан и не может быть пустым", + "invalid.id.format": "Параметр 'configuration.colors.id' должен следовать за word[.word]*", + "invalid.description": "Параметр 'configuration.colors.description' должен быть указан и не может быть пустым ", + "invalid.defaults": "'configuration.colors.defaults' может быть указан и может содержать значения 'light', 'dark' и 'highContrast'" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/rus/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..dd24883e84 --- /dev/null +++ b/i18n/rus/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "Недопустимый формат цвета. Используйте #RGB, #RGBA, #RRGGBB или #RRGGBBAA", + "schema.colors": "Цвета, используемые на рабочем месте.", + "foreground": "Общий цвет переднего плана. Этот цвет используется, только если его не переопределит компонент.", + "errorForeground": "Общий цвет переднего плана для сообщений об ошибках. Этот цвет используется только если его не переопределяет компонент.", + "descriptionForeground": "Цвет текста элемента, содержащего пояснения, например, для метки.", + "focusBorder": "Общий цвет границ для элементов с фокусом. Этот цвет используется только в том случае, если не переопределен в компоненте.", + "contrastBorder": "Дополнительная граница вокруг элементов, которая отделяет их от других элементов для улучшения контраста.", + "activeContrastBorder": "Дополнительная граница вокруг активных элементов, которая отделяет их от других элементов для улучшения контраста.", + "selectionBackground": "Цвет фона выделенного текста в рабочей области (например, в полях ввода или в текстовых полях). Не применяется к выделенному тексту в редакторе.", + "textSeparatorForeground": "Цвет для разделителей текста.", + "textLinkForeground": "Цвет переднего плана для ссылок в тексте.", + "textLinkActiveForeground": "Цвет переднего фона для активных ссылок в тексте.", + "textPreformatForeground": "Цвет текста фиксированного формата.", + "textBlockQuoteBackground": "Цвет фона для блоков с цитатами в тексте.", + "textBlockQuoteBorder": "Цвет границ для блоков с цитатами в тексте.", + "textCodeBlockBackground": "Цвет фона для программного кода в тексте.", + "widgetShadow": "Цвет тени мини-приложений редактора, таких как \"Найти/заменить\".", + "inputBoxBackground": "Фон поля ввода.", + "inputBoxForeground": "Передний план поля ввода.", + "inputBoxBorder": "Граница поля ввода.", + "inputBoxActiveOptionBorder": "Цвет границ активированных параметров в полях ввода.", + "inputPlaceholderForeground": "Цвет фона поясняющего текста в элементе ввода.", + "inputValidationInfoBackground": "Фоновый цвет проверки ввода для уровня серьезности \"Сведения\".", + "inputValidationInfoBorder": "Цвет границы проверки ввода для уровня серьезности \"Сведения\".", + "inputValidationWarningBackground": "Фоновый цвет проверки ввода для уровня серьезности \"Предупреждение\".", + "inputValidationWarningBorder": "Цвет границы проверки ввода для уровня серьезности \"Предупреждение\".", + "inputValidationErrorBackground": "Фоновый цвет проверки ввода для уровня серьезности \"Ошибка\".", + "inputValidationErrorBorder": "Цвет границы проверки ввода для уровня серьезности \"Ошибка\".", + "dropdownBackground": "Фон раскрывающегося списка.", + "dropdownForeground": "Передний план раскрывающегося списка.", + "dropdownBorder": "Граница раскрывающегося списка.", + "listFocusBackground": "Фоновый цвет находящегося в фокусе элемента List/Tree, когда элемент List/Tree активен. На активном элементе List/Tree есть фокус клавиатуры, на неактивном — нет.", + "listFocusForeground": "Цвет переднего плана находящегося в фокусе элемента List/Tree, когда элемент List/Tree активен. На активном элементе List/Tree есть фокус клавиатуры, на неактивном — нет.", + "listActiveSelectionBackground": "Фоновый цвет выбранного элемента List/Tree, когда элемент List/Tree активен. На активном элементе List/Tree есть фокус клавиатуры, на неактивном — нет.", + "listActiveSelectionForeground": "Цвет переднего плана выбранного элемента List/Tree, когда элемент List/Tree активен. На активном элементе List/Tree есть фокус клавиатуры, на неактивном — нет.", + "listInactiveSelectionBackground": "Фоновый цвет выбранного элемента List/Tree, когда элемент List/Tree неактивен. На активном элементе List/Tree есть фокус клавиатуры, на неактивном — нет.", + "listInactiveSelectionForeground": "Цвет текста выбранного элемента List/Tree, когда элемент List/Tree неактивен. На активном элементе List/Tree есть фокус клавиатуры, на неактивном — нет.", + "listHoverBackground": "Фоновый цвет элементов List/Tree при наведении курсора мыши.", + "listHoverForeground": "Цвет переднего плана элементов List/Tree при наведении курсора мыши.", + "listDropBackground": "Фоновый цвет элементов List/Tree при перемещении с помощью мыши.", + "highlight": "Цвет переднего плана для выделения соответствия при поиске по элементу List/Tree.", + "pickerGroupForeground": "Цвет средства быстрого выбора для группировки меток.", + "pickerGroupBorder": "Цвет средства быстрого выбора для группировки границ.", + "buttonForeground": "Цвет переднего плана кнопки.", + "buttonBackground": "Цвет фона кнопки.", + "buttonHoverBackground": "Цвет фона кнопки при наведении.", + "badgeBackground": "Цвет фона бэджа. Бэджи - небольшие информационные элементы, отображающие количество, например, результатов поиска.", + "badgeForeground": "Цвет текста бэджа. Бэджи - небольшие информационные элементы, отображающие количество, например, результатов поиска.", + "scrollbarShadow": "Цвет тени полосы прокрутки, которая свидетельствует о том, что содержимое прокручивается.", + "scrollbarSliderBackground": "Цвет фона ползунка полосы прокрутки.", + "scrollbarSliderHoverBackground": "Цвет фона ползунка полосы прокрутки при наведении курсора.", + "scrollbarSliderActiveBackground": "Цвет фона активного ползунка полосы прокрутки.", + "progressBarBackground": "Цвет фона индикатора выполнения, который может отображаться для длительных операций.", + "editorBackground": "Цвет фона редактора.", + "editorForeground": "Цвет переднего плана редактора по умолчанию.", + "editorWidgetBackground": "Цвет фона виджетов редактора, таких как найти/заменить.", + "editorWidgetBorder": "Цвет границы мини-приложений редактора. Этот цвет используется только в том случае, если у мини-приложения есть граница и если этот цвет не переопределен мини-приложением.", + "editorSelectionBackground": "Цвет выделения редактора.", + "editorSelectionForeground": "Цвет выделенного текста в режиме высокого контраста.", + "editorInactiveSelection": "Цвет выделения в неактивном редакторе.", + "editorSelectionHighlight": "Цвет регионов с тем же содержимым, что и в выделении.", + "editorFindMatch": "Цвет текущего поиска совпадений.", + "findMatchHighlight": "Цвет других совпадений поиска.", + "findRangeHighlight": "Цвет диапазона, ограничивающего поиск.", + "hoverHighlight": "Выделение под словом, для которого показано наведение.", + "hoverBackground": "Цвет фона при наведении указателя на редактор.", + "hoverBorder": "Цвет границ при наведении указателя на редактор.", + "activeLinkForeground": "Цвет активных ссылок.", + "diffEditorInserted": "Цвет фона для добавленных строк.", + "diffEditorRemoved": "Цвет фона для удаленных строк.", + "diffEditorInsertedOutline": "Цвет контура для добавленных строк.", + "diffEditorRemovedOutline": "Цвет контура для удаленных строк.", + "mergeCurrentHeaderBackground": "Цвет фона текущего заголовка во внутренних конфликтах слияния.", + "mergeCurrentContentBackground": "Цвет фона текущего содержимого во внутренних конфликтах слияния.", + "mergeIncomingHeaderBackground": "Цвет фона входящего заголовка во внутренних конфликтах слияния.", + "mergeIncomingContentBackground": "Цвет фона входящего содержимого во внутренних конфликтах слияния.", + "mergeCommonHeaderBackground": "Цвет фона заголовка для общего предка во внутренних конфликтах слияния.", + "mergeCommonContentBackground": "Цвет фона содержимого для общего предка во внутренних конфликтах слияния.", + "mergeBorder": "Цвет границы заголовков и разделителя во внутренних конфликтах слияния.", + "overviewRulerCurrentContentForeground": "Цвет переднего плана линейки текущего окна во внутренних конфликтах слияния.", + "overviewRulerIncomingContentForeground": "Цвет переднего плана линейки входящего окна во внутренних конфликтах слияния.", + "overviewRulerCommonContentForeground": "Цвет переднего плана для обзорной линейки для общего предка во внутренних конфликтах слияния. ", + "overviewRulerFindMatchForeground": "Цвет метки линейки в окне просмотра для результатов поиска.", + "overviewRulerSelectionHighlightForeground": "Цвет метки линейки в окне просмотра для выделения." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/rus/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..534c980941 --- /dev/null +++ b/i18n/rus/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "Рабочая область кода", + "untitledWorkspace": "(Рабочая область) без названия", + "workspaceNameVerbose": "{0} (рабочая область)", + "workspaceName": "{0} (рабочая область)" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/rus/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..cd0d2f647e --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Идет перезапись расширения {0} на {1}.", + "extensionUnderDevelopment": "Идет загрузка расширения разработки в {0}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/rus/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..4f32c5d2fc --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Закрыть", + "cancel": "Отмена", + "ok": "ОК" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/rus/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..67165199fc --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "Не отображается еще несколько ошибок и предупреждений ({0})." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/node/extHostExplorerView.i18n.json b/i18n/rus/src/vs/workbench/api/node/extHostExplorerView.i18n.json new file mode 100644 index 0000000000..42bf81aad2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/node/extHostExplorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "TreeExplorerNodeProvider с идентификатором \"{0}\" не зарегистрирован.", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider \"{0}\" не удалось предоставить корневой узел." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/rus/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..9831e698c5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "Не удалось активировать расширение \"{1}\". Причина: неизвестный зависимый компонент \"{0}\".", + "failedDep1": "Не удалось активировать расширение \"{1}\". Причина: ошибка активации зависимого компонента \"{0}\".", + "failedDep2": "Не удалось активировать расширение \"{0}\". Причина: более 10 уровней зависимостей (скорее всего, цикл зависимостей).", + "activationError": "Ошибка активации расширения \"{0}\": {1}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/rus/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json b/i18n/rus/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json new file mode 100644 index 0000000000..e4854b00e6 --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/node/extHostTreeExplorers.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "TreeExplorerNodeProvider с идентификатором \"{0}\" не зарегистрирован.", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider \"{0}\" не удалось предоставить корневой узел.", + "treeExplorer.failedToResolveChildren": "TreeExplorerNodeProvider \"{0}\" не удалось выполнить операцию resolveChildren." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/node/extHostTreeView.i18n.json b/i18n/rus/src/vs/workbench/api/node/extHostTreeView.i18n.json new file mode 100644 index 0000000000..42bf81aad2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/node/extHostTreeView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.notRegistered": "TreeExplorerNodeProvider с идентификатором \"{0}\" не зарегистрирован.", + "treeExplorer.failedToProvideRootNode": "TreeExplorerNodeProvider \"{0}\" не удалось предоставить корневой узел." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/rus/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..76f3378c17 --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "Отсутствует зарегистрированное представление в виде дерева с идентификатором '{0}'.", + "treeItem.notFound": "Отсутствует элемент дерева с идентификатором '{0}'.", + "treeView.duplicateElement": "Элемент {0} уже зарегистрирован" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json b/i18n/rus/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..cd0d2f647e --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/node/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "Идет перезапись расширения {0} на {1}.", + "extensionUnderDevelopment": "Идет загрузка расширения разработки в {0}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/api/node/mainThreadMessageService.i18n.json b/i18n/rus/src/vs/workbench/api/node/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..4f32c5d2fc --- /dev/null +++ b/i18n/rus/src/vs/workbench/api/node/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Закрыть", + "cancel": "Отмена", + "ok": "ОК" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..0a55719c3f --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "Настроить язык", + "displayLanguage": "Определяет язык интерфейса VSCode.", + "doc": "Список поддерживаемых языков см. в {0}.", + "restart": "Для изменения значения требуется перезапуск VSCode.", + "fail.createSettings": "Невозможно создать \"{0}\" ({1}).", + "JsonSchema.locale": "Язык пользовательского интерфейса." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..86cab0cc2d --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Открыть папку...", + "openFileFolder": "Открыть..." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..1c4d32691a --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "Показать или скрыть панель действий", + "view": "Просмотреть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..a1130928a9 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "Переключить вертикальную или горизонтальную структуру группы редакторов", + "horizontalLayout": "Горизонтальная структура группы редакторов", + "verticalLayout": "Вертикальная структура группы редакторов", + "view": "Просмотреть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..409880711e --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Расположение боковой панели", + "view": "Просмотреть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..0adadd2510 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "Изменить видимость боковой панели", + "view": "Просмотреть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..13866a7efd --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "Переключить видимость строки состояния", + "view": "Просмотреть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..a90a54b743 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "Включить/отключить режим \"Дзен\"", + "view": "Просмотреть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..f31053cda5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Открыть папку...", + "openFileFolder": "Открыть...", + "addFolderToWorkspace": "Добавить папку в рабочую область...", + "add": "&&Добавить", + "addFolderToWorkspaceTitle": "Добавить папку в рабочую область", + "newWorkspace": "Создать рабочую область...", + "select": "&&Выбрать", + "selectWorkspace": "Выбрать папки для рабочей области", + "removeFolderFromWorkspace": "Удалить папку из рабочей области", + "saveWorkspaceAsAction": "Сохранить рабочую область как...", + "saveEmptyWorkspaceNotSupported": "Перед сохранением рабочей области откройте ее. ", + "save": "Сохранить", + "saveWorkspace": "Сохранить рабочую область", + "openWorkspaceAction": "Открыть рабочую область...", + "openWorkspaceConfigFile": "Открыть файл конфигурации рабочей области" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..f6ba49b641 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "Удалить из панели действий", + "keepInActivityBar": "Хранить в панели действий", + "titleKeybinding": "{0} ({1})", + "additionalViews": "Дополнительные представления", + "numberBadge": "{0} ({1})", + "manageExtension": "Управление расширениями", + "toggle": "Переключить закрепленное представление" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..16cc0515a4 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "Скрыть панель действий", + "activityBarAriaLabel": "Переключатель активного представления", + "globalActions": "Глобальные действия" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..c64e8d6361 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "Действий: {0}", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..d7ae54fbca --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "Средство просмотра двоичных объектов" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..e89adf4a4e --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Текстовый редактор", + "textDiffEditor": "Редактор текстовых несовпадений", + "binaryDiffEditor": "Редактор двоичных несовпадений", + "sideBySideEditor": "Параллельный редактор", + "groupOnePicker": "Показать редакторы в первой группе", + "groupTwoPicker": "Показать редакторы во второй группе", + "groupThreePicker": "Показать редакторы в третьей группе", + "allEditorsPicker": "Показать все открытые редакторы", + "view": "Просмотр" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..74d8a486ac --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "Разделить редактор", + "joinTwoGroups": "Объединить редакторы из двух групп", + "navigateEditorGroups": "Переход между группами редакторов", + "focusActiveEditorGroup": "Сфокусироваться на активной группе редактора", + "focusFirstEditorGroup": "Фокус на первую группу редакторов", + "focusSecondEditorGroup": "Фокус на вторую группу редакторов", + "focusThirdEditorGroup": "Фокус на третью группу редакторов", + "focusPreviousGroup": "Фокус на предыдущую группу", + "focusNextGroup": "Фокус на следующую группу", + "openToSide": "Открыть сбоку", + "closeEditor": "Закрыть редактор", + "revertAndCloseActiveEditor": "Отменить изменения и закрыть редактор", + "closeEditorsToTheLeft": "Закрыть редакторы слева", + "closeEditorsToTheRight": "Закрыть редакторы справа", + "closeAllEditors": "Закрыть все редакторы", + "closeUnmodifiedEditors": "Закрыть редакторы без изменений в группе", + "closeEditorsInOtherGroups": "Закрыть редакторы в других группах", + "closeOtherEditorsInGroup": "Закрыть другие редакторы", + "closeEditorsInGroup": "Закрыть все редакторы в группе", + "moveActiveGroupLeft": "Переместить группу редакторов влево", + "moveActiveGroupRight": "Переместить группу редакторов вправо", + "minimizeOtherEditorGroups": "Свернуть другие группы редакторов", + "evenEditorGroups": "Уравнять ширину групп редакторов", + "maximizeEditor": "Развернуть группу редакторов и скрыть боковую панель", + "keepEditor": "Сохранить редактор", + "openNextEditor": "Открыть следующий редактор", + "openPreviousEditor": "Открыть предыдущий редактор", + "nextEditorInGroup": "Открыть следующий редактор в группе", + "openPreviousEditorInGroup": "Открыть предыдущий редактор в группе", + "navigateNext": "Далее", + "navigatePrevious": "Назад", + "reopenClosedEditor": "Открыть закрытый редактор", + "clearRecentFiles": "Очистить недавно открытые", + "showEditorsInFirstGroup": "Показать редакторы в первой группе", + "showEditorsInSecondGroup": "Показать редакторы во второй группе", + "showEditorsInThirdGroup": "Показать редакторы в третьей группе", + "showEditorsInGroup": "Показать редакторы в группе", + "showAllEditors": "Показать все редакторы", + "openPreviousRecentlyUsedEditorInGroup": "Открыть предыдущий недавно использованный редактор в группе", + "openNextRecentlyUsedEditorInGroup": "Открыть следующий недавно использованный редактор в группе", + "navigateEditorHistoryByInput": "Открыть предыдущий редактор из журнала", + "openNextRecentlyUsedEditor": "Открыть следующий недавно использованный редактор", + "openPreviousRecentlyUsedEditor": "Открыть предыдущий недавно использованный редактор", + "clearEditorHistory": "Очистить журнал редактора", + "focusLastEditorInStack": "Открыть последний редактор в группе", + "moveEditorLeft": "Переместить редактор влево", + "moveEditorRight": "Переместить редактор вправо", + "moveEditorToPreviousGroup": "Переместить редактор в предыдущую группу", + "moveEditorToNextGroup": "Переместить редактор в следующую группу" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..5884dae542 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "Перемещение активного редактора по вкладкам или группам", + "editorCommand.activeEditorMove.arg.name": "Аргумент перемещения активного редактора", + "editorCommand.activeEditorMove.arg.description": "Свойства аргумента:\n\t\t\t\t\t\t* 'to': строковое значение, указывающее направление перемещения.\n\t\t\t\t\t\t* 'by': строковое значение, указывающее единицу перемещения (вкладка или группа).\n\t\t\t\t\t\t* 'value': числовое значение, указывающее количество позиций перемещения или абсолютную позицию, на которую необходимо переместить.\n\t\t\t\t\t", + "commandDeprecated": "Команда **{0}** удалена. Вместо нее можно использовать **{1}**", + "openKeybindings": "Настройка сочетаний клавиш" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..0da5d63d8a --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "Слева", + "groupTwoVertical": "По центру", + "groupThreeVertical": "Справа", + "groupOneHorizontal": "По верхнему краю", + "groupTwoHorizontal": "По центру", + "groupThreeHorizontal": "По нижнему краю", + "editorOpenError": "Невозможно открыть \"{0}\": {1}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..d973e60f10 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, выбор группы редакторов", + "groupLabel": "Группа: {0}", + "noResultsFoundInGroup": "Соответствующие открытые редакторы не найдены в группе", + "noOpenedEditors": "Список открытых редакторов в группе сейчас пуст.", + "noResultsFound": "Соответствующие открытые редакторы не найдены", + "noOpenedEditorsAllGroups": "Список открытых редакторов сейчас пуст." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..54927da093 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "Строка {0}, столбец {1} (выбрано {2})", + "singleSelection": "Строка {0}, столбец {1}", + "multiSelectionRange": "Выделений: {0} (выделено символов: {1})", + "multiSelection": "Выделений: {0}", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "Клавиша TAB перемещает фокус", + "screenReaderDetected": "Средство чтения с экрана обнаружено", + "screenReaderDetectedExtra": "Если вы не используете средство чтения с экрана, измените значение параметра \"editor.accessibilitySupport\" на \"off\".", + "disableTabMode": "Отключить режим специальных возможностей", + "gotoLine": "Перейти к строке", + "indentation": "Отступ", + "selectEncoding": "Выберите кодировку", + "selectEOL": "Выберите последовательность конца строки", + "selectLanguageMode": "Выберите языковой режим", + "fileInfo": "Сведения о файле", + "spacesSize": "Пробелов: {0}", + "tabSize": "Размер интервала табуляции: {0}", + "showLanguageExtensions": "Поиск \"{0}\" среди расширений Marketplace...", + "changeMode": "Изменить языковой режим", + "noEditor": "В данный момент нет активного текстового редактора", + "languageDescription": "({0}) — настроенный язык", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "языки (идентификатор)", + "configureModeSettings": "Настройка параметров, определяемых языком \"{0}\"...", + "configureAssociationsExt": "Настройка сопоставлений файлов для \"{0}\"...", + "autoDetect": "Автоматическое обнаружение", + "pickLanguage": "Выберите языковой режим", + "currentAssociation": "Текущая связь", + "pickLanguageToConfigure": "Выберите языковой режим для связи с \"{0}\".", + "changeIndentation": "Изменить отступ", + "noWritableCodeEditor": "Активный редактор кода доступен только для чтения.", + "indentView": "изменить представление", + "indentConvert": "преобразовать файл", + "pickAction": "Выберите действие", + "changeEndOfLine": "Изменить последовательность конца строки", + "pickEndOfLine": "Выберите последовательность конца строки", + "changeEncoding": "Изменить кодировку файла", + "noFileEditor": "В данный момент нет активного файла", + "saveWithEncoding": "Сохранить в кодировке", + "reopenWithEncoding": "Повторно открыть в кодировке", + "guessedEncoding": "Предположение на основе содержимого", + "pickEncodingForReopen": "Выберите кодировку файла для его повторного открытия", + "pickEncodingForSave": "Выберите кодировку файла для его сохранения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..0ff928ec7e --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "Действия вкладки" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..295846a8e9 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "Редактор текстовых несовпадений", + "readonlyEditorWithInputAriaLabel": "{0}. Редактор сравнения текста только для чтения.", + "readonlyEditorAriaLabel": "Редактор сравнения текста только для чтения.", + "editableEditorWithInputAriaLabel": "{0}. Редактор сравнения текстовых файлов.", + "editableEditorAriaLabel": "Редактор сравнения текстовых файлов.", + "navigate.next.label": "Следующее исправление", + "navigate.prev.label": "Предыдущее исправление", + "inlineDiffLabel": "Переключиться на представление в строке", + "sideBySideDiffLabel": "Переключиться на параллельное представление" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..7d223797d5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0}, группа {1}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..181b8a89e8 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Текстовый редактор", + "readonlyEditorWithInputAriaLabel": "{0}. Текстовый редактор только для чтения.", + "readonlyEditorAriaLabel": "Текстовый редактор только для чтения.", + "untitledFileEditorWithInputAriaLabel": "{0}. Текстовый редактор файлов без названия.", + "untitledFileEditorAriaLabel": "Текстовый редактор файлов без названия." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..a5a37798ad --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Закрыть", + "closeOthers": "Закрыть другие", + "closeRight": "Закрыть справа", + "closeAll": "Закрыть все", + "closeAllUnmodified": "Закрыть без изменений", + "keepOpen": "Оставить открытым", + "showOpenedEditors": "Показать открытые редакторы", + "araLabelEditorActions": "Действия редактора" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..debe44ea4c --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "Закрыть панель", + "togglePanel": "Переключить панель", + "focusPanel": "Фокус на панель", + "toggleMaximizedPanel": "Переключить развернутую панель", + "maximizePanel": "Развернуть панель", + "minimizePanel": "Восстановить размер панели", + "view": "Просмотр" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..ce892ef5fd --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "Переключатель активной панели" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..51be3c5230 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} (нажмите клавишу ВВОД, чтобы подтвердить введенные данные, или ESCAPE для отмены)", + "inputModeEntry": "Нажмите клавишу ВВОД, чтобы подтвердить введенные данные, или ESCAPE для отмены", + "emptyPicks": "Нет записей для выбора", + "quickOpenInput": "Введите \"?\", чтобы узнать, какие отсюда можно выполнить действия", + "historyMatches": "недавно открывавшиеся", + "noResultsFound1": "Результаты не найдены", + "canNotRunPlaceholder": "Этот обработчик Quick Open нельзя использовать в текущем контексте.", + "entryAriaLabel": "{0}, недавно открывавшиеся", + "removeFromEditorHistory": "Удалить из журнала", + "pickHistory": "Выбор записи редактора, удаляемой из журнала" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..19d062e979 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Перейти к файлу...", + "quickNavigateNext": "Перейти к следующему элементу в Quick Open.", + "quickNavigatePrevious": "Перейти к предыдущему элементу в Quick Open.", + "quickSelectNext": "Выбрать следующее в Quick Open", + "quickSelectPrevious": "Выбрать предыдущее в Quick Open" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..19d062e979 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Перейти к файлу...", + "quickNavigateNext": "Перейти к следующему элементу в Quick Open.", + "quickNavigatePrevious": "Перейти к предыдущему элементу в Quick Open.", + "quickSelectNext": "Выбрать следующее в Quick Open", + "quickSelectPrevious": "Выбрать предыдущее в Quick Open" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..94af1e8d16 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "Скрыть боковую панель", + "focusSideBar": "Перевести фокус на боковую панель", + "viewCategory": "Просмотреть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..9c8a35333e --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "Команда \"{0}\" сейчас неактивна, и ее невозможно выполнить.", + "manageExtension": "Управление расширениями" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..07026e93c8 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[Не поддерживается]", + "devExtensionWindowTitlePrefix": "[Узел разработки расширения]" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/quickopen.i18n.json b/i18n/rus/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..473400077a --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "Нет соответствующих результатов", + "noResultsFound2": "Результаты не найдены", + "entryAriaLabel": "{0}, команда" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/viewlet.i18n.json b/i18n/rus/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..05e9ac3c25 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Свернуть все" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/common/theme.i18n.json b/i18n/rus/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..71c91dd106 --- /dev/null +++ b/i18n/rus/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "Цвет фона активной вкладки. Вкладки — это контейнеры для редакторов в области редактора. В одной группе редакторов можно открыть несколько вкладок. Может присутствовать несколько групп редакторов.", + "tabInactiveBackground": "Цвет фона неактивной вкладки. Вкладки — это контейнеры для редакторов в области редактора. В одной группе редакторов можно открыть несколько вкладок. Может присутствовать несколько групп редакторов.", + "tabBorder": "Граница для разделения вкладок. Вкладки — это контейнеры для редакторов в области редакторов. В одной группе редакторов можно открыть несколько вкладок. Может быть несколько групп редакторов.", + "tabActiveBorder": "Граница для выделения активных вкладок. Вкладки — это контейнеры для редакторов в области редакторов. В одной группе редакторов можно открыть несколько вкладок. Также можно открыть несколько групп редакторов. ", + "tabActiveUnfocusedBorder": "Граница для выделения активных вкладок в группе, не имеющей фокуса. Вкладки — это контейнеры для редакторов в области редакторов. В одной группе редакторов можно открыть несколько вкладок. Также можно открыть несколько групп редакторов.", + "tabActiveForeground": "Цвет переднего плана активной вкладки в активной группе. Вкладки — это контейнеры для редакторов в области редактора. В одной группе редакторов можно открыть несколько вкладок. Может присутствовать несколько групп редакторов.", + "tabInactiveForeground": "Цвет переднего плана неактивной вкладки в активной группе. Вкладки — это контейнеры для редакторов в области редактора. В одной группе редакторов можно открыть несколько вкладок. Может присутствовать несколько групп редакторов.", + "tabUnfocusedActiveForeground": "Цвет переднего плана активной вкладки в группе, не имеющей фокуса. Вкладки — это контейнеры для редакторов в области редактора. В одной группе редакторов можно открыть несколько вкладок. Также можно открыть несколько групп редакторов.", + "tabUnfocusedInactiveForeground": "Цвет переднего плана неактивной вкладки в группе, не имеющей фокуса. Вкладки — это контейнеры для редакторов в области редактора. В одной группе редакторов можно открыть несколько вкладок. Также можно открыть несколько групп редакторов.", + "editorGroupBackground": "Цвет фона группы редакторов. Группы редакторов представляют собой контейнеры редакторов. Цвет фона отображается при перетаскивании групп редакторов.", + "tabsContainerBackground": "Цвет фона для заголовка группы редакторов, когда вкладки включены. Группы редакторов представляют собой контейнеры редакторов.", + "tabsContainerBorder": "Цвет границы для заголовка группы редакторов, когда вкладки включены. Группы редакторов представляют собой контейнеры редакторов.", + "editorGroupHeaderBackground": "Цвет фона для заголовка группы редакторов, когда вкладки отключены. Группы редакторов представляют собой контейнеры редакторов.", + "editorGroupBorder": "Цвет для разделения нескольких групп редакторов. Группы редакторов — это контейнеры редакторов.", + "editorDragAndDropBackground": "Цвет фона при перетаскивании редакторов. Этот цвет должен обладать прозрачностью, чтобы содержимое редактора оставалось видимым.", + "panelBackground": "Цвет фона панели. Панели показаны под областью редактора и содержат такие представления, как выходные данные и встроенный терминал.", + "panelBorder": "Цвет верхней границы панели, отделяющей ее от редактора. Панели показаны под областью редактора и содержат такие представления, как выходные данные и встроенный терминал.", + "panelActiveTitleForeground": "Цвет заголовка для активной панели. Панели отображаются под областью редактора и содержат такие представления, как окно вывода и встроенный терминал.", + "panelInactiveTitleForeground": "Цвет заголовка для неактивной панели. Панели отображаются под областью редактора и содержат такие представления, как окно вывода и встроенный терминал.", + "panelActiveTitleBorder": "Цвет границ для заголовка активной панели. Панели отображаются под областью редактора и содержат такие представления, как окно вывода и встроенный терминал.", + "statusBarForeground": "Цвет переднего плана строки состояния, когда открыта рабочая область. Строка состояния отображается в нижней части окна.", + "statusBarNoFolderForeground": "Цвет переднего плана строки состояния, если папка не открыта. Строка состояния отображается в нижней части окна.", + "statusBarBackground": "Цвет фона строки состояния, когда открыта рабочая область. Строка состояния отображается в нижней части окна.", + "statusBarNoFolderBackground": "Цвет фона панели состояния, если папка не открыта. Панель состояния отображается внизу окна.", + "statusBarBorder": "Цвет границы строки состояния, который распространяется на боковую панель и редактор. Строка состояния расположена в нижней части окна.", + "statusBarNoFolderBorder": "Цвет границы строки состояния, который распространяется на боковую панель и редактор, когда открытые папки отсутствуют. Строка состояния расположена в нижней части окна.", + "statusBarItemActiveBackground": "Цвет фона элементов панели состояния при щелчке. Панель состояния отображается внизу окна.", + "statusBarItemHoverBackground": "Цвет фона элементов панели состояния при наведении. Панель состояния отображается внизу окна.", + "statusBarProminentItemBackground": "Цвет фона приоритетных элементов панели состояния. Приоритетные элементы выделяются на фоне других элементов панели состояния, чтобы подчеркнуть их значение. Панель состояния отображается в нижней части окна.", + "statusBarProminentItemHoverBackground": "Цвет фона приоритетных элементов панели состояния при наведении. Приоритетные элементы выделяются на фоне других элементов панели состояния, чтобы подчеркнуть их значение. Панель состояния отображается в нижней части окна.", + "activityBarBackground": "Цвет фона панели действий. Панель действий отображается слева или справа и позволяет переключаться между представлениями боковой панели.", + "activityBarForeground": "Цвет переднего плана панели действий (например, цвет, используемый для значков). Панель действий отображается слева или справа и позволяет переключаться между представлениями боковой панели.", + "activityBarBorder": "Цвет границы панели действий, который распространяется на боковую панель. Панель действий отображается слева или справа и позволяет переключаться между представлениями в боковой панели.", + "activityBarDragAndDropBackground": "Цвет панели обратной связи при перетаскивании для элементов панели действий. Цвет должен обладать прозрачностью, чтобы содержимое панели действий оставалось видимым. Панель действий отображается с правого или с левого края и позволяет переключаться между представлениями в боковой панели.", + "activityBarBadgeBackground": "Цвет фона значка уведомлений о действиях. Панель действий отображается слева или справа и позволяет переключаться между представлениями боковой панели.", + "activityBarBadgeForeground": "Цвет переднего плана значка уведомлений о действиях. Панель действий отображается слева или справа и позволяет переключаться между представлениями боковой панели.", + "sideBarBackground": "Цвет фона боковой панели. Боковая панель — это контейнер таких представлений, как проводник и поиск.", + "sideBarForeground": "Цвет переднего плана боковой панели. Боковая панель — это контейнер для таких представлений, как проводник и поиск.", + "sideBarBorder": "Цвет границы боковой панели со стороны редактора. Боковая панель — это контейнер для таких представлений, как проводник и поиск.", + "sideBarTitleForeground": "Цвет переднего плана заголовка боковой панели. Боковая панель — это контейнер для таких представлений, как проводник и поиск.", + "sideBarDragAndDropBackground": "Цвет элементов боковой панели при перетаскивании. Цвет должен обладать прозрачностью, чтобы содержимое элементов боковой панели оставалось видимым. Боковая панель предоставляет собой контейнер для таких представлений как проводник и поиск.", + "sideBarSectionHeaderBackground": "Цвет фона для заголовка раздела боковой панели. Боковая панель — это контейнер для таких представлений, как проводник и поиск.", + "sideBarSectionHeaderForeground": "Цвет переднего плана для заголовка раздела боковой панели. Боковая панель — это контейнер для таких представлений, как проводник и поиск.", + "titleBarActiveForeground": "Передний план панели заголовка, если окно активно. Обратите внимание, что этот цвет сейчас поддерживается только в macOS.", + "titleBarInactiveForeground": "Передний план панели заголовка, если окно неактивно. Обратите внимание, что этот цвет сейчас поддерживается только в macOS.", + "titleBarActiveBackground": "Фон панели заголовка, если окно активно. Обратите внимание, что этот цвет сейчас поддерживается только в macOS.", + "titleBarInactiveBackground": "Фон панели заголовка, если окно неактивно. Обратите внимание, что этот цвет сейчас поддерживается только в macOS.", + "titleBarBorder": "Цвет границы панели заголовка. Обратите внимание, что этот цвет сейчас поддерживается только в macOS.", + "notificationsForeground": "Цвет переднего плана для уведомлений. Уведомления отображаются в верхней части окна.", + "notificationsBackground": "Цвет фона для уведомлений. Уведомления отображаются в верхней части окна.", + "notificationsButtonBackground": "Цвет фона кнопки для уведомлений. Уведомления отображаются в верхней части окна.", + "notificationsButtonHoverBackground": "Цвет фона кнопки для уведомлений при наведении курсора мыши. Уведомления отображаются в верхней части окна.", + "notificationsButtonForeground": "Цвет переднего плана кнопки для уведомлений. Уведомления отображаются в верхней части окна.", + "notificationsInfoBackground": "Цвет фона для информационных сообщений. Уведомления отображаются в верхней части окна. ", + "notificationsInfoForeground": "Цвет переднего плана для информационных сообщений. Уведомления отображаются в верхней части окна. ", + "notificationsWarningBackground": "Цвет фона для предупреждений. Уведомления отображаются в верхней части окна. ", + "notificationsWarningForeground": "Цвет переднего плана для предупреждений. Уведомления отображаются в верхней части окна. ", + "notificationsErrorBackground": "Цвет фона для ошибок. Уведомления отображаются в верхней части окна. ", + "notificationsErrorForeground": "Цвет переднего плана для ошибок. Уведомления отображаются в верхней части окна. " +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/rus/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..9eac181649 --- /dev/null +++ b/i18n/rus/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "Закрыть редактор", + "closeWindow": "Закрыть окно", + "closeWorkspace": "Закрыть рабочую область", + "noWorkspaceOpened": "В этом экземпляре отсутствуют открытые рабочие области.", + "newWindow": "Новое окно", + "toggleFullScreen": "Полноэкранный режим", + "toggleMenuBar": "Переключить строку меню", + "toggleDevTools": "Переключить средства разработчика", + "zoomIn": "Увеличить", + "zoomOut": "Уменьшить", + "zoomReset": "Сбросить масштаб", + "appPerf": "Производительность запуска", + "reloadWindow": "Перезагрузить окно", + "switchWindowPlaceHolder": "Выберите окно, в которое нужно переключиться", + "current": "Текущее окно", + "close": "Закрыть окно", + "switchWindow": "Переключить окно...", + "quickSwitchWindow": "Быстро переключить окно...", + "workspaces": "рабочие области", + "files": "файлы", + "openRecentPlaceHolderMac": "Выберите, чтобы открыть (чтобы открыть в новом окне, удерживайте клавишу CMD)", + "openRecentPlaceHolder": "Выберите, чтобы открыть (чтобы открыть в новом окне, удерживайте клавишу CTRL)", + "remove": "Удалить из последних открытых", + "openRecent": "Открыть последние...", + "quickOpenRecent": "Быстро открыть последние...", + "closeMessages": "Закрыть уведомления", + "reportIssues": "Сообщить о проблемах", + "reportPerformanceIssue": "Сообщать о проблемах производительности", + "keybindingsReference": "Справочник по сочетаниям клавиш", + "openDocumentationUrl": "Документация", + "openIntroductoryVideosUrl": "Вступительные видео", + "openTipsAndTricksUrl": "Советы и рекомендации", + "toggleSharedProcess": "Переключить общий процесс", + "navigateLeft": "Перейти к представлению слева", + "navigateRight": "Перейти к представлению справа", + "navigateUp": "Перейти к представлению вверху", + "navigateDown": "Перейти к представлению внизу", + "increaseViewSize": "Увеличить размер текущего представления", + "decreaseViewSize": "Уменьшить размер текущего представления" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/rus/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/rus/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/electron-browser/crashReporter.i18n.json b/i18n/rus/src/vs/workbench/electron-browser/crashReporter.i18n.json new file mode 100644 index 0000000000..7cc8a5da01 --- /dev/null +++ b/i18n/rus/src/vs/workbench/electron-browser/crashReporter.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Телеметрия", + "telemetry.enableCrashReporting": "Разрешить отправку отчетов о сбоях в Майкрософт.\nЧтобы этот параметр вступил в силу, требуется перезагрузка." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/rus/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..5324f014b5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "Хост-процесс для расширений не был запущен в течение 10 секунд. Возможно, он был остановлен в первой строке, а для продолжения требуется отладчик.", + "extensionHostProcess.startupFail": "Хост-процесс для расширений не запустился спустя 10 секунд. Возможно, произошла ошибка.", + "extensionHostProcess.error": "Ошибка в хост-процессе для расширений: {0}", + "devTools": "Средства разработчика", + "extensionHostProcess.crash": "Хост-процесс для расширений неожиданно завершил работу. Загрузите окно повторно для восстановления." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/rus/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..704528922f --- /dev/null +++ b/i18n/rus/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "Просмотреть", + "help": "Справка", + "file": "Файл", + "workspaces": "Рабочие области", + "developer": "Разработчик", + "showEditorTabs": "Определяет, должны ли открытые редакторы отображаться на вкладках или нет.", + "editorTabCloseButton": "Определяет положение кнопок закрытия вкладок редактора или отключает их, если задано значение off.", + "showIcons": "Определяет, должны ли открытые редакторы отображаться со значком. Требует включить тему значков.", + "enablePreview": "Определяет, отображаются ли открытые редакторы в режиме предварительного просмотра. Редакторы с предварительным просмотром повторно используются до сохранения (например, с помощью двойного щелчка или изменения).", + "enablePreviewFromQuickOpen": "Определяет, отображаются ли редакторы из Quick Open в режиме предварительного просмотра. Редакторы в режиме предварительного просмотра повторно используются до сохранения (например, с помощью двойного щелчка или изменения).", + "editorOpenPositioning": "Определяет место открытия редакторов. Выберите \"Слева\" или \"Справа\", чтобы открывать редакторы слева или справа от активного сейчас редактора. Выберите \"Первый\" или \"Последний\", чтобы открывать редакторы независимо от активного сейчас редактора.", + "revealIfOpen": "Определяет, отображается ли редактор в какой-либо из видимых групп при открытии. Если функция отключена, редактор открывается в текущей активной группе редакторов. Если функция включена, вместо открытия уже открытый редактор будет отображен в текущей активной группе редакторов. Обратите внимание, что в некоторых случаях этот параметр игнорируется, например при принудительном открытии редактора в определенной группе или сбоку от текущей активной группы редакторов.", + "commandHistory": "Определяет количество недавно использованных команд, которые следует хранить в журнале палитры команд. Установите значение 0, чтобы отключить журнал команд.", + "preserveInput": "Определяет, следует ли восстановить последнюю введенную команду в палитре команд при следующем открытии палитры.", + "closeOnFocusLost": "Управляет автоматическим закрытием Quick Open при потере фокуса.", + "openDefaultSettings": "Управляет открытием редактора с отображением всех настроек по умолчанию при открытии настроек.", + "sideBarLocation": "Определяет расположение боковой панели: слева или справа от рабочего места.", + "statusBarVisibility": "Управляет видимостью строки состояния в нижней части рабочего места.", + "activityBarVisibility": "Управляет видимостью панели действий на рабочем месте.", + "closeOnFileDelete": "Определяет, следует ли автоматически закрывать редакторы, когда отображаемый в них файл удален или переименован другим процессом. При отключении этой функции редактор остается открытым в качестве черновика. Обратите внимание, что при удалении из приложения редактор закрывается всегда и что файлы черновиков никогда не закрываются для сохранения данных.", + "fontAliasing": "Управляет методом сглаживания шрифтов в рабочей области.-по умолчанию: субпиксельное сглаживание шрифтов; позволит добиться максимальной четкости текста на большинстве дисплеев за исключением Retina - сглаживание: сглаживание шрифтов на уровне пикселей, в отличие от субпиксельного сглаживания; позволит сделать шрифт более светлым в целом - нет: сглаживание шрифтов отключено; текст будет отображаться с неровными острыми краями ", + "workbench.fontAliasing.default": "Субпиксельное сглаживание шрифтов; позволит добиться максимальной четкости текста на большинстве дисплеев за исключением Retina.", + "workbench.fontAliasing.antialiased": "Сглаживание шрифтов на уровне пикселей, в отличие от субпиксельного сглаживания. Может сделать шрифт светлее в целом.", + "workbench.fontAliasing.none": "Отключает сглаживание шрифтов; текст будет отображаться с неровными острыми краями.", + "swipeToNavigate": "Переключайтесь между открытыми файлами, проводя по экрану по горизонтали тремя пальцами.", + "workbenchConfigurationTitle": "Workbench", + "window.openFilesInNewWindow.on": "Файлы будут открываться в новом окне.", + "window.openFilesInNewWindow.off": "Файлы будут открываться в окне с открытой папкой файлов или последнем активном окне.", + "window.openFilesInNewWindow.default": "Файлы будут открываться в окне с открытой папкой файлов или последнем активном окне, если они не открываются из панели Dock или системы поиска (только macOS).", + "openFilesInNewWindow": "Определяет, будут ли файлы открываться в новом окне.\n- default: файлы будут открываться в окне с открытой папкой файлов или последнем активном окне, если они не открываются из панели Dock или системы поиска (только macOS).\n- on: файлы будут открываться в новом окне.\n- off: файлы будут открываться в окне с открытой папкой файлов или последнем активном окне.\nОбратите внимание, что возможны случаи, когда этот параметр игнорируется (например, при использовании параметра командной строки -new-window или -reuse-window).", + "window.openFoldersInNewWindow.on": "Папки будут открываться в новом окне.", + "window.openFoldersInNewWindow.off": "Папки будут заменять последнее активное окно.", + "window.openFoldersInNewWindow.default": "Папки будут открываться в новом окне, если папка не выбрана в приложении (например, в меню \"Файл\").", + "openFoldersInNewWindow": "Определяет, будут ли папки открываться в новом окне или заменять последнее активное окно.\n- default: папки будут открываться в новом окне, если папка не выбрана из приложения (например, из меню \"Файл\").\n- on: папки будут открываться в новом окне.\n- off: папки будут заменять последнее активное окно.\nОбратите внимание, что возможны случаи, когда этот параметр игнорируется (например, при использовании параметра командной строки -new-window или -reuse-window).", + "window.reopenFolders.all": "Повторно открыть все окна.", + "window.reopenFolders.folders": "Повторно откройте все папки. Пустые рабочие области не будут восстановлены.", + "window.reopenFolders.one": "Повторно открыть последнее активное окно.", + "window.reopenFolders.none": "Никогда не открывать окно повторно. Всегда начинать с пустого окна.", + "restoreWindows": "Управляет повторным открытием окон после перезапуска. Выберите 'none', чтобы всегда начинать с пустой рабочей области; 'one', чтобы открыть последнее окно, с которым вы работали; 'folders', чтобы открыть все окна с открытыми папками, и 'all', чтобы открыть все окна последнего сеанса.", + "restoreFullscreen": "Определяет, должно ли окно восстанавливаться в полноэкранном режиме, если оно было закрыто в полноэкранном режиме.", + "zoomLevel": "Настройте масштаб окна. Исходный размер равен 0. Увеличение или уменьшение значения на 1 означает увеличение или уменьшение окна на 20 %. Чтобы более точно задать масштаб, можно также ввести десятичное число.", + "title": "Определяет заголовок окна в зависимости от активного редактора. Подстановка переменных выполняется на основе контекста:\n${activeEditorShort}: например, myFile.txt\n${activeEditorMedium}: например, myFolder/myFile.txt\n${activeEditorLong}: например, /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: например, myFolder\n${folderPath}: например, /Users/Development/myFolder\n${rootName}: например, myFolder1, myFolder2, myFolder3\n${rootPath}: например, /Users/Development/myWorkspace\n${appName}: например, VS Code\n${dirty}: индикатор dirty, если активный редактор является \"грязным\"\n${separator}: условный разделитель (\" - \"), который отображается, только если окружен переменными со значениями ", + "window.newWindowDimensions.default": "Открывать новые окна в центре экрана.", + "window.newWindowDimensions.inherit": "Открывать новые окна того же размера, что и последнее активное окно.", + "window.newWindowDimensions.maximized": "Открывать новые окна в развернутом состоянии.", + "window.newWindowDimensions.fullscreen": "Открывать новые окна в полноэкранном режиме.", + "newWindowDimensions": "Определяет размеры нового открывающегося окна, если по крайней мере одно окно уже открыто. По умолчанию новое окно будет открыто в центре экрана в уменьшенном размере. Если указано значение \"inherit\", размеры нового окна будут равны размерам последнего активного окна. Если указано значение \"maximized\", окно будет открыто в максимальном размере, а если указано значение \"fullscreen\", окно будет открыто в полноэкранном режиме. Обратите внимание, что этот параметр не влияет на первое открываемое окно. Размеры и расположение первого окна всегда будут совпдаать с размерами и расположением этого окна перед закрытием.", + "closeWhenEmpty": "Определяет, следует ли закрыть окно при закрытии последнего редактора. Этот параметр применяется только к окнам, в которых нет открытых папок.", + "window.menuBarVisibility.default": "Меню скрыто только в полноэкранном режиме.", + "window.menuBarVisibility.visible": "Меню всегда видимо, даже в полноэкранном режиме.", + "window.menuBarVisibility.toggle": "Меню скрыто, но его можно вывести с помощью клавиши ALT.", + "window.menuBarVisibility.hidden": "Меню всегда скрыто.", + "menuBarVisibility": "Определяет видимость строки меню. Значение toggle указывает, что строка меню скрыта и для ее вывода нужно один раз нажать клавишу ALT. По умолчанию строка меню не будет отображаться только в полноэкранном режиме.", + "enableMenuBarMnemonics": "Если этот параметр установлен, главные меню можно открыть с помощью сочетаний клавиш с клавишей ALT. Отключение назначенных клавиш позволит связать эти сочетания клавиш с клавишей ALT с командами редактора.", + "autoDetectHighContrast": "Если включено, будет выполняться автоматический переход к высококонтрастной теме, если в Windows используется тема высокой контрастности, или к темной теме при выходе из темы высокой контрастности Windows.", + "titleBarStyle": "Настройка внешнего вида заголовка окна. Чтобы применить изменения, потребуется полный перезапуск.", + "window.nativeTabs": "Включает вкладки окна macOS Sierra. Обратите внимание, что для применения этих изменений потребуется полная перезагрузка, и что для всех внутренних вкладок будет отключен пользовательский стиль заголовка, если он был настроен.", + "windowConfigurationTitle": "Окно", + "zenModeConfigurationTitle": "Режим Zen", + "zenMode.fullScreen": "Определяет, будет ли переключение в режим Zen переключать рабочее пространство в полноэкранный режим.", + "zenMode.hideTabs": "Определяет, будет ли включение режима Zen также скрывать вкладки рабочего места.", + "zenMode.hideStatusBar": "Определяет, будет ли включение режима Zen также скрывать строку состояния в нижней части рабочего места.", + "zenMode.hideActivityBar": "Определяет, будет ли при включении режима Zen скрыта панель действий в левой части рабочей области.", + "zenMode.restore": "Определяет, должно ли окно восстанавливаться в режиме Zen, если закрылось в режиме Zen." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/electron-browser/main.i18n.json b/i18n/rus/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..0b75d5ba35 --- /dev/null +++ b/i18n/rus/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "Сбой загрузки требуемого файла. Утеряно подключение к Интернету, либо сервер, к которому вы подключены, перешел в автономный режим. Обновите содержимое браузера, чтобы повторить попытку.", + "loaderErrorNative": "Не удалось загрузить требуемый файл. Перезапустите приложение, чтобы повторить попытку. Дополнительные сведения: {0}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/rus/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..007971a0a9 --- /dev/null +++ b/i18n/rus/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "Не рекомендуется запускать код с правами администратора." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/electron-browser/window.i18n.json b/i18n/rus/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..b76be0fd46 --- /dev/null +++ b/i18n/rus/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "Отменить", + "redo": "Вернуть", + "cut": "Вырезать", + "copy": "Копировать", + "paste": "Вставить", + "selectAll": "Выбрать все" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/rus/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..e3c2d37fd7 --- /dev/null +++ b/i18n/rus/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "Разработчик", + "file": "Файл" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/rus/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..8ab0813e5d --- /dev/null +++ b/i18n/rus/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "Путь \"{0}\" не указывает на допустимый модуль выполнения тестов расширения." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/rus/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..84bc156764 --- /dev/null +++ b/i18n/rus/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Не удалось проанализировать {0}: {1}.", + "fileReadFail": "Не удается прочитать файл {0}: {1}.", + "jsonsParseFail": "Не удалось проанализировать {0} или {1}: {2}.", + "missingNLSKey": "Не удалось найти сообщение для ключа {0}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..9f447c941e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "Установить путь к команде \"{0}\" в PATH", + "not available": "Эта команда недоступна.", + "successIn": "Путь к команде оболочки \"{0}\" успешно установлен в PATH.", + "warnEscalation": "Редактор Code запросит права администратора для установки команды оболочки с помощью osascript.", + "ok": "ОК", + "cantCreateBinFolder": "Не удается создать папку \"/usr/local/bin\".", + "cancel2": "Отмена", + "aborted": "Прервано", + "uninstall": "Удалить путь к команде \"{0}\" из PATH", + "successFrom": "Путь к команде оболочки \"{0}\" успешно удален из PATH.", + "shellCommand": "Команда оболочки" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..161959df13 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "Установка значения \"on\" для параметра \"editor.accessibilitySupport\".", + "openingDocs": "Открывается страница документации по специальным возможностям VS Code.", + "introMsg": "Благодарим за ознакомление со специальными возможностями VS Code.", + "status": "Состояние:", + "changeConfigToOnMac": "Чтобы включить постоянную оптимизацию редактора для использования со средствами чтения с экрана, нажмите COMMMAND+E.", + "changeConfigToOnWinLinux": "Чтобы включить постоянную оптимизацию редактора для использования со средствами чтения с экрана, нажмите CTRL+E.", + "auto_unknown": "В редакторе настроено определение средства чтения с экрана с помощью API платформы, но текущая среда выполнения это не поддерживает.", + "auto_on": "Редактор автоматически определил, что средство чтения с экрана подключено.", + "auto_off": "В редакторе настроено автоматическое определение средства чтения с экрана, но сейчас это средство не подключено.", + "configuredOn": "Постоянная оптимизацию редактора для использования со средствами чтения с экрана включена. Чтобы ее отключить, измените параметр \"editor.accessibilitySupport\".", + "configuredOff": "Для редактора не настроена оптимизация для использования со средствами чтения с экрана.", + "tabFocusModeOnMsg": "При нажатии клавиши TAB в текущем редакторе фокус ввода переместится на следующий элемент, способный его принять. Чтобы изменить это поведение, нажмите клавишу {0}.", + "tabFocusModeOnMsgNoKb": "При нажатии клавиши TAB в текущем редакторе фокус ввода переместится на следующий элемент, способный его принять. Команду {0} сейчас невозможно выполнить с помощью настраиваемого сочетания клавиш.", + "tabFocusModeOffMsg": "При нажатии клавиши TAB в текущем редакторе будет вставлен символ табуляции. Чтобы изменить это поведение, нажмите клавишу {0}.", + "tabFocusModeOffMsgNoKb": "При нажатии клавиши TAB в текущем редакторе будет вставлен символ табуляции. Команду {0} сейчас невозможно выполнить с помощью настраиваемого сочетания клавиш.", + "openDocMac": "Нажмите COMMAND+H, чтобы открыть окно браузера с дополнительными сведениями о специальных возможностях VS Code.", + "openDocWinLinux": "Нажмите CTRL+H, чтобы открыть окно браузера с дополнительными сведениями о специальных возможностях VS Code.", + "outroMsg": "Вы можете закрыть эту подсказку и вернуться в редактор, нажав клавиши ESCAPE или SHIFT+ESCAPE.", + "ShowAccessibilityHelpAction": "Показать справку по специальным возможностям" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..a052b973f5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "Разработчик: исследование сопоставлений ключей" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..0008b267bd --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Разработчик: проверка областей TM", + "inspectTMScopesWidget.loading": "Идет загрузка..." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..8914a57fea --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "Ошибок при анализе {0}: {1}", + "schema.openBracket": "Открывающий символ скобки или строковая последовательность.", + "schema.closeBracket": "Закрывающий символ скобки или строковая последовательность.", + "schema.comments": "Определяет символы комментариев", + "schema.blockComments": "Определяет способ маркировки комментариев.", + "schema.blockComment.begin": "Последовательность символов, открывающая блок комментариев.", + "schema.blockComment.end": "Последовательность символов, закрывающая блок комментариев.", + "schema.lineComment": "Последовательность символов, с которой начинается строка комментария.", + "schema.brackets": "Определяет символы скобок, увеличивающие или уменьшающие отступ.", + "schema.autoClosingPairs": "Определяет пары скобок. Когда введена открывающая скобка, автоматически добавляется закрывающая.", + "schema.autoClosingPairs.notIn": "Определяет список областей, где автоматические пары отключены.", + "schema.surroundingPairs": "Определяет пары скобок, в которые заключается выбранная строка.", + "schema.wordPattern": "Определение слова для языка.", + "schema.wordPattern.pattern": "Шаблон регулярного выражения, используемый для сопоставления слов.", + "schema.wordPattern.flags": "Флаги регулярного выражения, используемого для сопоставления слов.", + "schema.wordPattern.flags.errorMessage": "Должно соответствовать шаблону \"/^([gimuy]+)$/\".", + "schema.indentationRules": "Параметры отступов языка.", + "schema.indentationRules.increaseIndentPattern": "Если строка соответствует шаблону, то ко всем следующим строкам необходимо применить одинарный отступ (если не применяется другое правило).", + "schema.indentationRules.increaseIndentPattern.pattern": "Шаблон регулярного выражения для increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.flags": "Флаги регулярного выражения для increaseIndentPattern.", + "schema.indentationRules.increaseIndentPattern.errorMessage": "Должно соответствовать шаблону \"/^([gimuy]+)$/\".", + "schema.indentationRules.decreaseIndentPattern": "Если строка соответствует шаблону, то для всех следующих строк нужно уменьшить количество отступов на один (если не применяется другое правило). ", + "schema.indentationRules.decreaseIndentPattern.pattern": "Шаблон регулярного выражения для decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.flags": "Флаги регулярного выражения для decreaseIndentPattern.", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "Должно соответствовать шаблону \"/^([gimuy]+)$/\".", + "schema.indentationRules.indentNextLinePattern": "Если строка соответствует шаблону, то необходимо применить одинарный отступ **только к следующей строке**.", + "schema.indentationRules.indentNextLinePattern.pattern": "Шаблон регулярного выражения для indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.flags": "Флаги регулярного выражения для indentNextLinePattern.", + "schema.indentationRules.indentNextLinePattern.errorMessage": "Должно соответствовать шаблону \"/^([gimuy]+)$/\".", + "schema.indentationRules.unIndentedLinePattern": "Если строка соответствует шаблону, то отступ для этой строки не следует изменять и проверять на соответствие другим правилам.", + "schema.indentationRules.unIndentedLinePattern.pattern": "Шаблон регулярного выражения для unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.flags": "Флаги регулярного выражения для unIndentedLinePattern.", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "Должно соответствовать шаблону \"/^([gimuy]+)$/\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..0008b267bd --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Разработчик: проверка областей TM", + "inspectTMScopesWidget.loading": "Идет загрузка..." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..48a0ee0f1b --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "Вид: переключить мини-карту" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..20bae1c8f4 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Включить или отключить режим с несколькими курсорами" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..332187fb05 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "Вид: переключить управляющие символы" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..3a6a8ebd64 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "Вид: включить или отключить вывод пробелов" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..032e9c268e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "Вид: переключение режима переноса по словам", + "wordWrap.notInDiffEditor": "Не удается переключить перенос по словам в редакторе несовпадений.", + "unwrapMinified": "Отключить перенос для этого файла", + "wrapMinified": "Включить перенос для этого файла" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..ba8283c037 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "ОК", + "wordWrapMigration.dontShowAgain": "Больше не показывать", + "wordWrapMigration.openSettings": "Открыть параметры", + "wordWrapMigration.prompt": "Параметр \"editor.wrappingColumn\" устарел и заменен на \"editor.wordWrap\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..f4c756eacf --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "Прервать выполнение, если выражение равно true. Нажмите клавишу ВВОД, чтобы принять, или ESC для отмены.", + "breakpointWidgetAriaLabel": "Выполнение программы прервется в этом месте, только если условие выполнится. Нажмите клавишу ВВОД для принятия или ESC для отмены.", + "breakpointWidgetHitCountPlaceholder": "Прервать при определенном количестве обращений. Нажмите клавишу ВВОД, чтобы принять, или ESC для отмены.", + "breakpointWidgetHitCountAriaLabel": "Выполнение программы прервется в этом месте, только если достигнуто определенное количество обращений. Нажмите клавишу ВВОД для принятия или ESC для отмены.", + "expression": "Выражение", + "hitCount": "Количество обращений" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..cc4c366f4a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "Нет конфигураций", + "addConfigTo": "Добавить конфигурацию ({0})...", + "addConfiguration": "Добавить конфигурацию..." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..c4453beeca --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "Открыть {0}", + "launchJsonNeedsConfigurtion": "Настройте или исправьте \"launch.json\"", + "noFolderDebugConfig": "Чтобы перейти к расширенной конфигурации отладки, сначала откройте папку.", + "startDebug": "Начать отладку", + "startWithoutDebugging": "Начать без отладки", + "selectAndStartDebugging": "Выбрать и начать отладку", + "restartDebug": "Перезапустить", + "reconnectDebug": "Повторно подключить", + "stepOverDebug": "Шаг с обходом", + "stepIntoDebug": "Шаг с заходом", + "stepOutDebug": "Шаг с выходом", + "stopDebug": "Остановить", + "disconnectDebug": "Отключить", + "continueDebug": "Продолжить", + "pauseDebug": "Приостановить", + "restartFrame": "Перезапустить кадр", + "removeBreakpoint": "Удалить точку останова", + "removeAllBreakpoints": "Удалить все точки останова", + "enableBreakpoint": "Включить точку останова", + "disableBreakpoint": "Выключить точку останова", + "enableAllBreakpoints": "Включить все точки останова", + "disableAllBreakpoints": "Отключить все точки останова", + "activateBreakpoints": "Активировать точки останова", + "deactivateBreakpoints": "Отключить точки останова", + "reapplyAllBreakpoints": "Повторно применить все точки останова", + "addFunctionBreakpoint": "Добавить точку останова в функции", + "renameFunctionBreakpoint": "Переименовать точку останова в функции", + "addConditionalBreakpoint": "Добавить условную точку останова…", + "editConditionalBreakpoint": "Изменить точку останова…", + "setValue": "Задать значение", + "addWatchExpression": "Добавить выражение", + "editWatchExpression": "Изменить выражение", + "addToWatchExpressions": "Добавить контрольное значение", + "removeWatchExpression": "Удалить выражение", + "removeAllWatchExpressions": "Удалить все выражения", + "clearRepl": "Очистить консоль", + "debugConsoleAction": "Консоль отладки", + "unreadOutput": "Новые выходные данные в консоли отладки", + "debugFocusConsole": "Фокус консоли отладки", + "focusProcess": "Обработка фокуса", + "stepBackDebug": "На шаг назад", + "reverseContinue": "Обратно" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..9b8abc8643 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "Цвет фона для панели инструментов отладки." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..7c50303f27 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "Не удается разрешить ресурс без сеанса отладки." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..2fbd0929bf --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "Отладка: переключить точку останова", + "columnBreakpointAction": "Отладка: точка останова столбца", + "columnBreakpoint": "Добавить точку останова столбца", + "conditionalBreakpointEditorAction": "Отладка: добавить условную точку останова…", + "runToCursor": "Выполнить до курсора", + "debugEvaluate": "Отладка: вычисление", + "debugAddToWatch": "Отладка: добавить контрольное значение", + "showDebugHover": "Отладка: показать при наведении" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..3826936500 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "Отключенная точка останова", + "breakpointUnverifieddHover": "Непроверенная точка останова", + "breakpointDirtydHover": "Непроверенная точка останова. Файл был изменен, перезапустите сеанс отладки.", + "breakpointUnsupported": "Условные точки останова не поддерживаются этим типом отладки" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..c3c4fe3925 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "Отладка: {0}", + "debugAriaLabel": "Введите имя используемой конфигурации запуска.", + "noConfigurationsMatching": "Нет соответствующих конфигураций отладки.", + "noConfigurationsFound": "Конфигурации отладки не найдены. Создайте файл \"launch.json\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..aa75cf2c3e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "Цвет границ мини-приложения исключений.", + "debugExceptionWidgetBackground": "Цвет фона мини-приложения исключений.", + "exceptionThrownWithId": "Возникло исключение: {0}", + "exceptionThrown": "Произошло исключение." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..0b401d9526 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "Щелкните, чтобы отслеживать (чтобы открыть сбоку экрана, щелкните, удерживая клавишу CMD)", + "fileLink": "Щелкните, чтобы отслеживать (чтобы открыть сбоку экрана, щелкните, удерживая клавишу CTRL)" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..e9595ac984 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "Управляет поведением внутренней консоли отладки." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..1097b77e39 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "недоступно", + "startDebugFirst": "Чтобы произвести вычисление, начните сеанс отладки" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..daf6a19629 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "Неизвестный источник" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..3045324fc3 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "Показать отладочные сведения", + "toggleDebugPanel": "Консоль отладки", + "debug": "Отладка", + "debugPanel": "Консоль отладки", + "variables": "Переменные", + "watch": "Контрольное значение", + "callStack": "Стек вызовов", + "breakpoints": "Точки останова", + "view": "Просмотреть", + "debugCategory": "Отладка", + "debugCommands": "Конфигурация отладки", + "debugConfigurationTitle": "Отладка", + "allowBreakpointsEverywhere": "Разрешает задание точки останова в любом файле", + "openExplorerOnEnd": "Автоматически открывать представление обозревателя в конце сеанса отладки", + "inlineValues": "Показывать значения переменных в редакторе во время отладки", + "hideActionBar": "Определяет, следует ли скрыть всплывающую панель действий отладки.", + "launch": "Глобальная конфигурация запуска отладки. Должна использоваться в качестве альтернативы для конфигурации \"launch.json\", которая является общей для рабочих пространств" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..cca3fae0ef --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "Перед расширенной настройкой отладки откройте папку." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..7ccdae912a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "Добавляет адаптеры отладки.", + "vscode.extension.contributes.debuggers.type": "Уникальный идентификатор этого адаптера отладки.", + "vscode.extension.contributes.debuggers.label": "Отображаемое имя этого адаптера отладки.", + "vscode.extension.contributes.debuggers.program": "Путь к программе адаптера отладки. Путь указывается либо как абсолютный, либо относительно папки расширения.", + "vscode.extension.contributes.debuggers.args": "Необязательные аргументы для передачи адаптеру.", + "vscode.extension.contributes.debuggers.runtime": "Дополнительная среда выполнения, используемая в том случае, если атрибут program не указывает на исполняемый файл, но среда выполнения требуется.", + "vscode.extension.contributes.debuggers.runtimeArgs": "Аргументы дополнительной среды выполнения.", + "vscode.extension.contributes.debuggers.variables": "Сопоставление интерактивных переменных (например, ${action.pickProcess}) в \"launch.json\" для команды.", + "vscode.extension.contributes.debuggers.initialConfigurations": "Конфигурации для создания первоначального файла launch.json.", + "vscode.extension.contributes.debuggers.languages": "Список языков, для которых расширение отладки может считаться \"отладчиком по умолчанию\".", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Если задано, VS Code будет вызывать эту команду, чтобы определить путь к исполняемому файлу адаптера отладки и передаваемые аргументы.", + "vscode.extension.contributes.debuggers.startSessionCommand": "Если задано, VS Code будет вызывать эту команду для действий \"отладка\" или \"запуск\", предназначенных для этого расширения.", + "vscode.extension.contributes.debuggers.configurationSnippets": "Фрагменты для добавления новых конфигураций в launch.json.", + "vscode.extension.contributes.debuggers.configurationAttributes": "Конфигурации схемы JSON для проверки launch.json.", + "vscode.extension.contributes.debuggers.windows": "Параметры, связанные с Windows.", + "vscode.extension.contributes.debuggers.windows.runtime": "Среда выполнения, используемая для Windows.", + "vscode.extension.contributes.debuggers.osx": "Параметры, связанные с OS X.", + "vscode.extension.contributes.debuggers.osx.runtime": "Среда выполнения, используемая для OS X.", + "vscode.extension.contributes.debuggers.linux": "Параметры, связанные с Linux.", + "vscode.extension.contributes.debuggers.linux.runtime": "Среда выполнения, используемая для Linux.", + "vscode.extension.contributes.breakpoints": "Добавляет точки останова.", + "vscode.extension.contributes.breakpoints.language": "Разрешить точки останова для этого языка.", + "app.launch.json.title": "Запустить", + "app.launch.json.version": "Версия этого формата файла.", + "app.launch.json.configurations": "Список конфигураций. Добавьте новые конфигурации или измените существующие с помощью IntelliSense.", + "app.launch.json.compounds": "Список составных объектов. Каждый из них ссылается на несколько конфигураций, которые будут запущены вместе.", + "app.launch.json.compound.name": "Имя составного объекта. Отображается в раскрывающемся меню запуска конфигурации.", + "app.launch.json.compounds.configurations": "Имена конфигураций, которые будут запущены как часть этого составного объекта.", + "debugNoType": "Параметр type адаптера отладки не может быть опущен и должен иметь тип string.", + "selectDebug": "Выбор среды", + "DebugConfig.failed": "Не удается создать файл launch.json в папке .vscode ({0})." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..679c95230d --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "Удалить точки останова", + "removeBreakpointOnColumn": "Удалить точку останова из столбца {0}", + "removeLineBreakpoint": "Удалить точку останова из строки", + "editBreakpoints": "Изменить точки останова", + "editBreakpointOnColumn": "Изменить точку останова в столбце {0}", + "editLineBrekapoint": "Изменить точку останова в строке", + "enableDisableBreakpoints": "Включить или отключить точки останова", + "disableColumnBreakpoint": "Отключить точку останова в столбце {0}", + "disableBreakpointOnLine": "Отключить точку останова в строке", + "enableBreakpoints": "Включить точку останова в столбце {0}", + "enableBreakpointOnLine": "Включить точку останова в строке", + "addBreakpoint": "Добавить точку останова", + "addConfiguration": "Добавить конфигурацию..." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..e020cf0fa8 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "Отладка при наведении" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..95b662d67e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "Для этого объекта показаны только значения-примитивы.", + "debuggingPaused": "Отладка была приостановлена, причина {0}, {1} {2}", + "debuggingStarted": "Отладка началась.", + "debuggingStopped": "Отладка остановилась.", + "breakpointAdded": "Добавлена точка останова: строка {0}, файл {1}", + "breakpointRemoved": "Удалена точка останова: строка {0}, файл {1}", + "compoundMustHaveConfigurations": "Для составного элемента должен быть задан атрибут configurations для запуска нескольких конфигураций.", + "configMissing": "Конфигурация \"{0}\" отсутствует в launch.json.", + "debugTypeNotSupported": "Настроенный тип отладки \"{0}\" не поддерживается.", + "debugTypeMissing": "Отсутствует свойство \"type\" для выбранной конфигурации запуска.", + "preLaunchTaskErrors": "При выполнении предварительной задачи \"{0}\" обнаружены ошибки.", + "preLaunchTaskError": "При выполнении предварительной задачи \"{0}\" обнаружена ошибка.", + "preLaunchTaskExitCode": "Выполнение предварительной задачи \"{0}\" завершено с кодом выхода {1}.", + "debugAnyway": "Принудительная отладка", + "noFolderWorkspaceDebugError": "Нельзя выполнить отладку активного файла. Убедитесь, что файл сохранен на диске и установлено расширение отладки для этого типа файла.", + "NewLaunchConfig": "Настройте файл конфигурации запуска для вашего приложения. {0}", + "DebugTaskNotFound": "Не удалось найти задачу preLaunchTask \"{0}\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..62db9bf42e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "Процесс", + "paused": "Приостановлено", + "running": "Работает", + "thread": "Поток", + "pausedOn": "Приостановлено на {0}", + "loadMoreStackFrames": "Загрузить больше кадров стека", + "threadAriaLabel": "Поток {0}, стек вызовов, отладка", + "stackFrameAriaLabel": "Кадр стека {0}, строка {1} {2}, стек вызовов, отладка", + "variableValueAriaLabel": "Введите новое значение переменной", + "variableScopeAriaLabel": "Область {0}, переменные, отладка", + "variableAriaLabel": "{0} значение {1}, переменные, отладка", + "watchExpressionPlaceholder": "Выражение с контрольным значением", + "watchExpressionInputAriaLabel": "Введите выражение контрольного значения", + "watchExpressionAriaLabel": "{0} значение {1}, контрольное значение, отладка", + "watchVariableAriaLabel": "{0} значение {1}, контрольное значение, отладка", + "functionBreakpointPlaceholder": "Функция, в которой производится останов", + "functionBreakPointInputAriaLabel": "Введите точку останова в функции", + "functionBreakpointsNotSupported": "Точки останова функций не поддерживаются в этом типе отладки", + "breakpointAriaLabel": "Строка точки останова {0} {1}, точки останова, отладка", + "functionBreakpointAriaLabel": "Точка останова в функции {0}, точки останова, отладка", + "exceptionBreakpointAriaLabel": "Точка останова в исключении {0}, точки останова, отладка" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..bdc31d7262 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "Раздел переменных", + "variablesAriaTreeLabel": "Отладка переменных", + "expressionsSection": "Раздел выражений", + "watchAriaTreeLabel": "Отладка выражений контрольных значений", + "callstackSection": "Раздел стека вызовов", + "debugStopped": "Приостановлено на {0}", + "callStackAriaLabel": "Отладка стека вызовов", + "breakpointsSection": "Раздел точек останова", + "breakpointsAriaTreeLabel": "Отладка точек останова" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..48767d7b7f --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "Копировать значение", + "copy": "Копировать", + "copyAll": "Копировать все", + "copyStackTrace": "Копировать стек вызовов" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..55824fbb37 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "Подробнее", + "unableToLaunchDebugAdapter": "Не удается запустить адаптер отладки из \"{0}\".", + "unableToLaunchDebugAdapterNoArgs": "Не удается запустить адаптер отладки.", + "stoppingDebugAdapter": "{0}. Адаптер отладки останавливается.", + "debugAdapterCrash": "Процесс адаптера отладки неожиданно завершился" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..7d3acf2c12 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "Панель read–eval–print loop", + "actions.repl.historyPrevious": "Журнал — назад", + "actions.repl.historyNext": "Журнал — далее", + "actions.repl.acceptInput": "Прием входных данных REPL", + "actions.repl.copyAll": "Отладка: скопировать все содержимое консоли" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..79e8d455d1 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "Состояние объекта записывается после первого вычисления", + "replVariableAriaLabel": "Переменная \"{0}\" имеет значение \"{1}\", read–eval–print loop, отладка", + "replExpressionAriaLabel": "Выражение \"{0}\" имеет значение \"{1}\", read–eval–print loop, отладка", + "replValueOutputAriaLabel": "{0}, read–eval–print loop, отладка", + "replKeyValueOutputAriaLabel": "Выходная переменная \"{0}\" имеет значение \"{1}\", read–eval–print loop, отладка" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..7375d6ed05 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "Цвет фона панели состояния при отладке программы. Панель состояния показана внизу окна.", + "statusBarDebuggingForeground": "Цвет переднего плана строки состояния при отладке программы. Строка состояния расположена в нижней части окна.", + "statusBarDebuggingBorder": "Цвет границы строки состояния, который распространяется на боковую панель и редактор при отладке программы. Строка состояния расположена в нижней части окна." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..55266e7f75 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "отлаживаемый объект", + "debug.terminal.not.available.error": "Интегрированный терминал недоступен." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..6e54216bf8 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "Исполняемый файл адаптера отладки \"{0}\" не существует.", + "debugAdapterCannotDetermineExecutable": "Невозможно определить исполняемый файл для адаптера отладки \"{0}\".", + "launch.config.comment1": "Используйте IntelliSense, чтобы узнать о возможных атрибутах.", + "launch.config.comment2": "Наведите указатель мыши, чтобы просмотреть описания существующих атрибутов.", + "launch.config.comment3": "Для получения дополнительной информации посетите: {0}", + "debugType": "Тип конфигурации.", + "debugTypeNotRecognised": "Не удается распознать тип отладки. Убедитесь, что соответствующее расширение отладки установлено и включено.", + "node2NotSupported": "Значение \"node2\" больше не поддерживается; используйте \"node\" и задайте для атрибута \"protocol\" значение \"inspector\".", + "debugName": "Имя конфигурации; отображается в раскрывающемся меню конфигурации запуска.", + "debugRequest": "Запросите тип конфигурации. Возможные типы: \"запуск\" и \"подключение\".", + "debugServer": "Только для разработки расширений отладки: если указан порт, VS Code пытается подключиться к адаптеру отладки, запущенному в режиме сервера.", + "debugPrelaunchTask": "Задача, выполняемая перед началом сеанса отладки.", + "debugWindowsConfiguration": "Атрибуты конфигурации запуска для Windows.", + "debugOSXConfiguration": "Атрибуты конфигурации запуска для OS X.", + "debugLinuxConfiguration": "Атрибуты конфигурации запуска для Linux.", + "deprecatedVariables": "\"env.\", \"config.\" и \"command.\" устарели, используйте \"env:\", \"config:\" и \"command:\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..72d2f88e49 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "Показать команды Emmet" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..3aed93de5c --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: баланс (входящий)", + "balanceOutward": "Emmet: баланс (исходящий)" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..02f73ee231 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: перейти к предыдущей точке изменения", + "nextEditPoint": "Emmet: перейти к следующей точке изменения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..dab23d332c --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: вычисление математического выражения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..999dd52305 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: расшифровать аббревиатуру" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..0204b406c2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: увеличение значения на 0,1", + "incrementNumberByOne": "Emmet: увеличение значения на 1", + "incrementNumberByTen": "Emmet: увеличение значения на 10", + "decrementNumberByOneTenth": "Emmet: уменьшение значения на 0,1", + "decrementNumberByOne": "Emmet: уменьшение значения на 1", + "decrementNumberByTen": "Emmet: уменьшение значения на 10" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..41661861e6 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: перейти к соответствующей паре" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..311c5b2ca8 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: объединение строк" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..f76021c647 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: отражение значения CSS" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..8bad72fa4c --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: удаление тега" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..e05b53bf5b --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: выбор предыдущего элемента", + "selectNextItem": "Emmet: выбор следующего элемента" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..b57b9b8f39 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: разделение и объединение тегов" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..90a5df5006 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: переключение комментариев" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..e0bff58830 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: изменение размера изображения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..96d408c223 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: изменение тега", + "enterTag": "Ввод тега", + "tag": "Тег" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..ac8746bd9a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: перенос с сокращением", + "enterAbbreviation": "Ввод сокращения", + "abbreviation": "Сокращение" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..5620fba239 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Если этот параметр установлен, сокращения Emmet разворачиваются при нажатии клавиши TAB. Не применяется, если параметр emmet.useNewemmet имеет значение 'true'.", + "emmetPreferences": "Настройки, которые используются для изменения поведения некоторых действий и сопоставителей Emmet. Не применяется, если параметр emmet.useNewemmet имеет значение 'true'.", + "emmetSyntaxProfiles": "Задайте профиль для указанного синтаксиса или используйте свой собственный профиль с определенными правилами.", + "emmetExclude": "Массив языков, в которых не должны развертываться сокращения Emmet.", + "emmetExtensionsPath": "Путь к папке, содержащей профили Emmet, фрагменты кода и настройки. Если параметр emmet.useNewemmet имеет значение 'true', используются только профили для пути расширения.", + "useNewEmmet": "Попробуйте новые модули emmet (которые в конечном итоге заменят устаревшую библиотеку emmet), чтобы ознакомиться со всеми функциями emmet." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json new file mode 100644 index 0000000000..3aed93de5c --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: баланс (входящий)", + "balanceOutward": "Emmet: баланс (исходящий)" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json new file mode 100644 index 0000000000..df5e06f47e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: предыдущая точка изменения", + "nextEditPoint": "Emmet: следующая точка изменения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..dab23d332c --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: вычисление математического выражения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..999dd52305 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: расшифровать аббревиатуру" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..0204b406c2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: увеличение значения на 0,1", + "incrementNumberByOne": "Emmet: увеличение значения на 1", + "incrementNumberByTen": "Emmet: увеличение значения на 10", + "decrementNumberByOneTenth": "Emmet: уменьшение значения на 0,1", + "decrementNumberByOne": "Emmet: уменьшение значения на 1", + "decrementNumberByTen": "Emmet: уменьшение значения на 10" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..41661861e6 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: перейти к соответствующей паре" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..311c5b2ca8 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: объединение строк" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..f76021c647 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: отражение значения CSS" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json new file mode 100644 index 0000000000..8bad72fa4c --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: удаление тега" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json new file mode 100644 index 0000000000..e05b53bf5b --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: выбор предыдущего элемента", + "selectNextItem": "Emmet: выбор следующего элемента" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..b57b9b8f39 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: разделение и объединение тегов" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..90a5df5006 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: переключение комментариев" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..e0bff58830 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: изменение размера изображения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json new file mode 100644 index 0000000000..96d408c223 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: изменение тега", + "enterTag": "Ввод тега", + "tag": "Тег" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..ac8746bd9a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: перенос с сокращением", + "enterAbbreviation": "Ввод сокращения", + "abbreviation": "Сокращение" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json new file mode 100644 index 0000000000..186d053d0d --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/emmet/node/emmet.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Если включено, сокращения Emmet разворачиваются при нажатии клавиши TAB.", + "emmetPreferences": "Настройки, которые используются для изменения поведения некоторых действий и сопоставителей Emmet.", + "emmetSyntaxProfiles": "Задайте профиль для указанного синтаксиса или используйте свой собственный профиль с определенными правилами.", + "emmetExclude": "Массив языков, в которых не должны развертываться сокращения Emmet.", + "emmetExtensionsPath": "Путь к папке, содержащей профили Emmet, фрагменты кода и настройки" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..d887355278 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Внешний терминал", + "explorer.openInTerminalKind": "Определяет тип терминала, который следует запустить.", + "terminal.external.windowsExec": "Настройка терминала, который будет запущен в Windows.", + "terminal.external.osxExec": "Настройка приложения терминала для запуска в OS X.", + "terminal.external.linuxExec": "Настройка терминала для запуска в Linux.", + "globalConsoleActionWin": "Открыть новую командную строку", + "globalConsoleActionMacLinux": "Открыть новый терминал", + "scopedConsoleActionWin": "Открыть в командной строке", + "scopedConsoleActionMacLinux": "Открыть в терминале", + "openFolderInIntegratedTerminal": "Открыть в терминале" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..0e479458ee --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Внешний терминал", + "terminal.external.windowsExec": "Настройка терминала, который будет запущен в Windows.", + "terminal.external.osxExec": "Настройка приложения терминала для запуска в OS X.", + "terminal.external.linuxExec": "Настройка терминала для запуска в Linux.", + "globalConsoleActionWin": "Открыть новую командную строку", + "globalConsoleActionMacLinux": "Открыть новый терминал", + "scopedConsoleActionWin": "Открыть в командной строке", + "scopedConsoleActionMacLinux": "Открыть в терминале" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/rus/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..11ceee80c2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "Консоль VS Code", + "mac.terminal.script.failed": "Сбой скрипта \"{0}\" с кодом выхода {1}", + "mac.terminal.type.not.supported": "\"{0}\" не поддерживается", + "press.any.key": "Для продолжения нажмите любую клавишу...", + "linux.term.failed": "Сбой \"{0}\" с кодом выхода {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..50f63270e3 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "Добавляет пользовательское представление", + "vscode.extension.contributes.view.id": "Уникальный идентификатор представления, созданного с помощью vscode.workspace.createTreeView", + "vscode.extension.contributes.view.label": "Строка для отображения представления в понятном для пользователя формате", + "vscode.extension.contributes.view.icon": "Путь к значку представления", + "vscode.extension.contributes.views": "Добавляет пользовательские представления", + "showViewlet": "Показать {0}", + "view": "Просмотреть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..746c0d5fb1 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "Обновить" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..95b705b0f2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "TreeExplorerNodeProvider с идентификатором {providerId} не зарегистрирован." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/rus/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..ca7b212469 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "Раздел обозревателя дерева" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..f805880e4f --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "Ошибка", + "Unknown Dependency": "Неизвестная зависимость:" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..7e077f7a25 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "Имя расширения", + "extension id": "Идентификатор расширений", + "publisher": "Имя издателя", + "install count": "Число установок", + "rating": "Оценка", + "license": "Лицензия", + "details": "Подробности", + "contributions": "Вклады", + "changelog": "Журнал изменений", + "dependencies": "Зависимости", + "noReadme": "Файл сведений недоступен.", + "noChangelog": "Журнал изменений недоступен.", + "noContributions": "Нет публикаций", + "noDependencies": "Нет зависимостей", + "settings": "Параметры ({0})", + "setting name": "Имя", + "description": "Описание", + "default": "По умолчанию", + "debuggers": "Отладчики ({0})", + "debugger name": "Имя", + "debugger type": "Тип", + "views": "Представления ({0})", + "view id": "Идентификатор", + "view name": "Имя", + "view location": "Где", + "themes": "Темы ({0})", + "JSON Validation": "Проверка JSON ({0})", + "commands": "Команды ({0})", + "command name": "Имя", + "keyboard shortcuts": "Сочетания клавиш", + "menuContexts": "Контексты меню", + "languages": "Языки ({0})", + "language id": "Идентификатор", + "language name": "Имя", + "file extensions": "Расширения файлов", + "grammar": "Грамматика", + "snippets": "Фрагменты" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..d68d721cce --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "Установить", + "installing": "Идет установка", + "uninstallAction": "Удаление", + "Uninstalling": "Идет удаление", + "updateAction": "Обновить", + "updateTo": "Обновить до {0}", + "enableForWorkspaceAction.label": "Включить (рабочая область)", + "enableAlwaysAction.label": "Включать (всегда)", + "disableForWorkspaceAction.label": "Отключить (рабочая область)", + "disableAlwaysAction.label": "Отключать (всегда)", + "ManageExtensionAction.uninstallingTooltip": "Идет удаление", + "enableForWorkspaceAction": "Рабочая область", + "enableGloballyAction": "Всегда", + "enableAction": "Включить", + "disableForWorkspaceAction": "Рабочая область", + "disableGloballyAction": "Всегда", + "disableAction": "Отключить", + "checkForUpdates": "Проверка обновлений", + "enableAutoUpdate": "Включить автоматическое обновление расширений", + "disableAutoUpdate": "Отключить автоматическое обновление расширений", + "updateAll": "Обновить все расширения", + "reloadAction": "Перезагрузка", + "postUpdateTooltip": "Обновление окна для обновления", + "postUpdateMessage": "Обновить это окно, чтобы активировать обновленное расширение \"{0}\"?", + "postEnableTooltip": "Обновление для активации", + "postEnableMessage": "Обновить это окно, чтобы активировать расширение \"{0}\"?", + "postDisableTooltip": "Перезагрузка для деактивации", + "postDisableMessage": "Обновить это окно, чтобы отключить расширение \"{0}\"?", + "postUninstallTooltip": "Перезагрузка для деактивации", + "postUninstallMessage": "Обновить это окно, чтобы отключить удаленное расширение \"{0}\"?", + "reload": "&&Перезагрузить окно", + "toggleExtensionsViewlet": "Показать расширения", + "installExtensions": "Установить расширения", + "showEnabledExtensions": "Показать включенные расширения", + "showInstalledExtensions": "Показать установленные расширения", + "showDisabledExtensions": "Показать отключенные расширения", + "clearExtensionsInput": "Очистить входные данные расширений", + "showOutdatedExtensions": "Показать устаревшие расширения", + "showPopularExtensions": "Показать популярные расширения", + "showRecommendedExtensions": "Показать рекомендуемые расширения", + "showWorkspaceRecommendedExtensions": "Показать рекомендуемые расширения рабочей области", + "showRecommendedKeymapExtensions": "Показать рекомендуемые раскладки клавиатуры", + "showRecommendedKeymapExtensionsShort": "Раскладки клавиатуры", + "showLanguageExtensions": "Показать расширения языка", + "showLanguageExtensionsShort": "Расширения языка", + "showAzureExtensions": "Показать расширения Azure", + "showAzureExtensionsShort": "Расширения Azure", + "configureWorkspaceRecommendedExtensions": "Настроить рекомендуемые расширения (рабочая область)", + "ConfigureWorkspaceRecommendations.noWorkspace": "Рекомендации доступны только для папки рабочей области.", + "OpenExtensionsFile.failed": "Не удается создать файл \"extensions.json\" в папке \".vscode\" ({0}).", + "builtin": "Встроенное", + "disableAll": "Отключить все установленные расширения", + "disableAllWorkspace": "Отключить все установленные расширения для этой рабочей области", + "enableAll": "Включить все установленные расширения", + "enableAllWorkspace": "Включить все установленные расширения для этой рабочей области", + "extensionButtonProminentBackground": "Цвет фона кнопок, соответствующих основным действиям расширения (например, кнопка \"Установить\").", + "extensionButtonProminentForeground": "Цвет переднего плана кнопок, соответствующих основным действиям расширения (например, кнопка \"Установить\").", + "extensionButtonProminentHoverBackground": "Цвет фона кнопок, соответствующих основным действиям расширения, при наведении мыши (например, кнопка \"Установить\")." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..46be3b6887 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "Нажмите клавишу ВВОД для управления расширениями.", + "searchFor": "Нажмите клавишу ВВОД для поиска \"{0}\" в Marketplace.", + "noExtensionsToInstall": "Введите имя расширения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..09327ddddd --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "Расширения", + "app.extensions.json.recommendations": "Список рекомендаций по расширениям. Идентификатор расширения — всегда \"${publisher}.${name}\". Например, \"vscode.csharp\".", + "app.extension.identifier.errorMessage": "Ожидается формат \"${publisher}.${name}\". Пример: \"vscode.csharp\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..26e82a917a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "Расширение: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..c14bf8ccd9 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "Для этого типа файлов рекомендуется использовать расширение '{0}'.", + "showRecommendations": "Показать рекомендации", + "neverShowAgain": "Больше не показывать", + "close": "Закрыть", + "workspaceRecommended": "Эта рабочая область включает рекомендации по расширениям.", + "ignoreExtensionRecommendations": "Вы действительно хотите проигнорировать все рекомендации по расширениям?", + "ignoreAll": "Да, игнорировать все", + "no": "Нет", + "cancel": "Отмена" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..e069c7924b --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "Управление расширениями", + "galleryExtensionsCommands": "Установить расширения из коллекции", + "extension": "Расширение", + "extensions": "Расширения", + "view": "Просмотреть", + "extensionsConfigurationTitle": "Расширения", + "extensionsAutoUpdate": "Автоматически обновлять расширения", + "extensionsIgnoreRecommendations": "Игнорировать рекомендации по расширениям" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..929d793eed --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "Открыть папку расширений", + "installVSIX": "Установка из VSIX...", + "InstallVSIXAction.success": "Расширение установлено. Чтобы включить его, выполните перезапуск.", + "InstallVSIXAction.reloadNow": "Перезагрузить" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..9944ccce44 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Отключить другие раскладки клавиатуры ({0}), чтобы избежать конфликта между настраиваемыми сочетаниями клавиш?", + "yes": "Да", + "no": "Нет", + "betterMergeDisabled": "В текущую версию встроено средство слияния с лучшей функциональностью. Установленное расширение было отключено и не может быть удалено.", + "uninstall": "Удаление", + "later": "Позже" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..8654df2c38 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "Marketplace", + "installedExtensions": "Установлено", + "searchInstalledExtensions": "Установлено", + "recommendedExtensions": "Рекомендуемое", + "searchExtensions": "Поиск расширений в Marketplace", + "sort by installs": "Сортировать по: числу установок", + "sort by rating": "Сортировать по: рейтинг", + "sort by name": "Сортировать по: название", + "suggestProxyError": "Marketplace вернул значение \"ECONNREFUSED\". Проверьте параметр \"http.proxy\".", + "extensions": "Расширения", + "outdatedExtensions": "Устаревшие расширения: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..8e93a14b8a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Расширения", + "no extensions found": "Расширений не найдено.", + "suggestProxyError": "Marketplace вернул значение \"ECONNREFUSED\". Проверьте параметр \"http.proxy\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json new file mode 100644 index 0000000000..d26b40d8a7 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/electron-browser/keymapExtensions.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Отключить другие раскладки клавиатуры, чтобы избежать конфликта между настраиваемыми сочетаниями клавиш?", + "yes": "Да", + "no": "Нет" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..90fe3db32d --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "Включение \"{0}\" также включит соответствующие зависимости. Продолжить?", + "enable": "Да", + "doNotEnable": "Нет", + "disableDependeciesConfirmation": "Отключить только \"{0}\" или вместе с зависимостями?", + "disableOnly": "Только", + "disableAll": "Все", + "cancel": "Отмена", + "singleDependentError": "Невозможно отключить расширение \"{0}\". От него зависит расширение \"{1}\".", + "twoDependentsError": "Невозможно отключить расширение \"{0}\". От него зависят расширения \"{1}\" и \"{2}\".", + "multipleDependentsError": "Невозможно отключить расширение \"{0}\". От него зависят расширения \"{1}\", \"{2}\" и другие.", + "installConfirmation": "Вы хотите установить расширение '{0}'?", + "install": "Установить" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/rus/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..7d1b5f5eda --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "Отправить твит с отзывом", + "label.sendASmile": "Отправьте нам твит со своим отзывом.", + "patchedVersion1": "Установка повреждена.", + "patchedVersion2": "Сообщите об этом при отправке ошибки.", + "sentiment": "Каковы ваши впечатления?", + "smileCaption": "Хорошо", + "frownCaption": "Плохо", + "other ways to contact us": "Другие способы связаться с нами", + "submit a bug": "Сообщить об ошибке", + "request a missing feature": "Запросить отсутствующую возможность", + "tell us why?": "Расскажите нам о причинах", + "commentsHeader": "Комментарии", + "tweet": "Твит", + "character left": "символ остался", + "characters left": "симв. осталось", + "feedbackSending": "Отправляется", + "feedbackSent": "Спасибо", + "feedbackSendingError": "Повторите попытку" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..61559231b9 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "Средство просмотра двоичных файлов" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..06dd5d78b6 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "Редактор текстовых файлов", + "createFile": "Создать файл", + "fileEditorWithInputAriaLabel": "{0}. Редактор текстовых файлов.", + "fileEditorAriaLabel": "Редактор текстовых файлов." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..9e040c65f9 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "Папки" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..298de9912a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "Файлы", + "revealInSideBar": "Показать в боковой панели", + "acceptLocalChanges": "Использовать изменения и перезаписать содержимое диска", + "revertLocalChanges": "Отменить изменения и вернуться к содержимому на диске" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..efbc9898e7 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "Повторить попытку", + "rename": "Переименовать", + "newFile": "Создать файл", + "newFolder": "Создать папку", + "openFolderFirst": "Сначала откройте папку, в которой будут созданы файлы и папки.", + "newUntitledFile": "Новый файл без имени", + "createNewFile": "Создать файл", + "createNewFolder": "Создать папку", + "deleteButtonLabelRecycleBin": "&&Переместить в корзину", + "deleteButtonLabelTrash": "&&Переместить в удаленные", + "deleteButtonLabel": "&&Удалить", + "dirtyMessageFolderOneDelete": "Вы удаляете папку с несохраненными изменениями в одном файле. Вы хотите продолжить?", + "dirtyMessageFolderDelete": "Вы удаляете папку с несохраненными изменениями в нескольких файлах ({0}). Вы хотите продолжить?", + "dirtyMessageFileDelete": "Вы удаляете файл с несохраненными изменениями. Вы хотите продолжить?", + "dirtyWarning": "Если не сохранить изменения, они будут утеряны.", + "confirmMoveTrashMessageFolder": "Вы действительно хотите удалить папку \"{0}\" и ее содержимое?", + "confirmMoveTrashMessageFile": "Вы действительно хотите удалить \"{0}\"?", + "undoBin": "Вы можете выполнить восстановление из корзины.", + "undoTrash": "Вы можете выполнить восстановление из корзины.", + "confirmDeleteMessageFolder": "Вы действительно хотите удалить папку \"{0}\" и ее содержимое без возможности восстановления?", + "confirmDeleteMessageFile": "Вы действительно хотите удалить \"{0}\" без возможности восстановления?", + "irreversible": "Это действие необратимо!", + "permDelete": "Удалить навсегда", + "delete": "Удалить", + "importFiles": "Импорт файлов", + "confirmOverwrite": "Файл или папка с таким именем уже существует в конечной папке. Заменить их?", + "replaceButtonLabel": "Заменить", + "copyFile": "Копировать", + "pasteFile": "Вставить", + "duplicateFile": "Дублировать", + "openToSide": "Открыть сбоку", + "compareSource": "Выбрать для сравнения", + "globalCompareFile": "Сравнить активный файл с...", + "pickHistory": "Выберите предыдущий открытый файл для сравнения.", + "unableToFileToCompare": "Выбранный файл нельзя сравнить с \"{0}\".", + "openFileToCompare": "Чтобы сравнить файл с другим файлом, сначала откройте его.", + "compareWith": "Сравнить '{0}' с '{1}'", + "compareFiles": "Сравнить файлы", + "refresh": "Обновить", + "save": "Сохранить", + "saveAs": "Сохранить как...", + "saveAll": "Сохранить все", + "saveAllInGroup": "Сохранить все в группе", + "saveFiles": "Сохранить файлы с изменениями", + "revert": "Отменить изменения в файле", + "focusOpenEditors": "Фокус на представлении открытых редакторов", + "focusFilesExplorer": "Фокус на проводнике", + "showInExplorer": "Показать активный файл в боковой панели", + "openFileToShow": "Сначала откройте файл для отображения в обозревателе.", + "collapseExplorerFolders": "Свернуть папки в проводнике", + "refreshExplorer": "Обновить окно проводника", + "openFile": "Открыть файл...", + "openFileInNewWindow": "Открыть активный файл в новом окне", + "openFileToShowInNewWindow": "Чтобы открыть файл в новом окне, сначала откройте его.", + "revealInWindows": "Отобразить в проводнике", + "revealInMac": "Отобразить в Finder", + "openContainer": "Открыть содержащую папку", + "revealActiveFileInWindows": "Отобразить активный файл в проводнике", + "revealActiveFileInMac": "Отобразить активный файл в Finder", + "openActiveFileContainer": "Открыть папку, содержащую активный файл", + "copyPath": "Скопировать путь", + "copyPathOfActive": "Копировать путь к активному файлу", + "emptyFileNameError": "Необходимо указать имя файла или папки.", + "fileNameExistsError": "Файл или папка **{0}** уже существует в данном расположении. Выберите другое имя.", + "invalidFileNameError": "Имя **{0}** недопустимо для файла или папки. Выберите другое имя.", + "filePathTooLongError": "Из-за использования имени **{0}** путь слишком длинный. Выберите более короткое имя.", + "compareWithSaved": "Сравнить активный файл с сохраненным", + "modifiedLabel": "{0} (на диске) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..9da8519849 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "Чтобы скопировать путь к файлу, сначала откройте его", + "openFileToReveal": "Чтобы отобразить файл, сначала откройте его" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..4a22d06881 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "Показать проводник", + "explore": "Проводник", + "view": "Просмотреть", + "textFileEditor": "Редактор текстовых файлов", + "binaryFileEditor": "Редактор двоичных файлов", + "filesConfigurationTitle": "Файлы", + "exclude": "Настройка стандартных масок для исключения файлов и папок.", + "files.exclude.boolean": "Стандартная маска, соответствующая путям к файлам. Задайте значение true или false, чтобы включить или отключить маску.", + "files.exclude.when": "Дополнительная проверка элементов того же уровня соответствующего файла. Используйте $(basename) в качестве переменной для соответствующего имени файла.", + "associations": "Настройте сопоставления файлов с языками (например, \"*.extension\": \"html\"). У них будет приоритет перед заданными по умолчанию сопоставлениями установленных языков.", + "encoding": "Кодировка набора символов по умолчанию, используемая при чтении и записи файлов", + "autoGuessEncoding": "Если параметр включен, производится попытка определить кодировку набора символов при открытии файлов", + "eol": "Символ конца строки по умолчанию. Используйте \\n для LF и \\r\\n для CRLF.", + "trimTrailingWhitespace": "Если этот параметр включен, при сохранении файла будут удалены концевые пробелы.", + "insertFinalNewline": "Если этот параметр включен, при сохранении файла в его конец вставляется финальная новая строка.", + "files.autoSave.off": "\"Грязный\" файл не сохраняется автоматически.", + "files.autoSave.afterDelay": "\"Грязный\" файл автоматически сохраняется по истечении срока \"files.autoSaveDelay\".", + "files.autoSave.onFocusChange": "\"Грязный\" файл автоматически сохраняется при потере фокуса редактором.", + "files.autoSave.onWindowChange": "\"Грязный\" файл автоматически сохраняется при потере фокуса окном.", + "autoSave": "Управляет автоматическим сохранением \"грязных\" файлов. Допустимые значения: \"{0}\", \"{1}\", \"{2}\" (редактор теряет фокус) и \"{3}\" (окно теряет фокус). Если задано значение \"{4}\", можно настроить задержку в \"files.autoSaveDelay\".", + "autoSaveDelay": "Определяет задержку в мс, после которой измененный файл сохраняется автоматически. Действует, только если параметр \"files.autoSave\" имеет значение \"{0}\".", + "watcherExclude": "Настройте стандартные маски путей файлов, которые следует исключить из списка отслеживаемых файлов. Пути должны соответствовать полным путям (т.е. для правильного сопоставления необходимо указывать ** в начале неполного пути или указывать полные пути). После изменения этого параметра потребуется перезагрузка. Если отображается сообщение \"Код потребляет большое количество процессорного времени при запуске\" можно исключить большие папки, чтобы уменьшить начальную нагрузку.", + "hotExit.off": "Отключите \"горячий\" выход.", + "hotExit.onExit": "Функция \"горячий выход\" будет активирована при закрытии приложения, то есть при закрытии последнего окна в Windows или Linux или при активации команды workbench.action.quit (палитра команд, настраиваемое сочетание клавиш, меню). Все окна с резервными копиями будут восстановлены при следующем запуске.", + "hotExit.onExitAndWindowClose": "Функция \"горячий выход\" будет активирована при закрытии приложения, то есть при закрытии последнего окна в Windows или Linux или при активации команды workbench.action.quit (с помощью палитры команд, настраиваемого сочетания клавиш или пункта меню), а также для любых окон с открытыми папками независимо от того, является ли это окно последним. Все окна без открытых папок будут восстановлены при следующем запуске. Чтобы восстановить исходное состояние окон с папками, установите параметр \"window.restoreWindows\" в значение \"all\".", + "hotExit": "Определяет, запоминаются ли несохраненные файлы между сеансами. В этом случае приглашение на их сохранение при выходе из редактора не появляется.", + "useExperimentalFileWatcher": "Использовать новое экспериментальное средство наблюдения за файлами.", + "defaultLanguage": "Режим языка по умолчанию, который назначается новым файлам.", + "editorConfigurationTitle": "Редактор", + "formatOnSave": "Форматирование файла при сохранении. Модуль форматирования должен быть доступен, файл не должен сохраняться автоматически, а работа редактора не должна завершаться.", + "explorerConfigurationTitle": "Проводник", + "openEditorsVisible": "Число редакторов, отображаемых на панели открытых редакторов. Задайте значение 0, чтобы скрыть панель.", + "dynamicHeight": "Определяет, будет ли высота раздела открытых редакторов динамически адаптироваться к количеству элементов.", + "autoReveal": "Определяет, будет ли проводник автоматически отображать и выбирать файлы при их открытии.", + "enableDragAndDrop": "Определяет, разрешено ли перемещение файлов и папок перетаскиванием в проводнике.", + "sortOrder.default": "Файлы и папки сортируются по именам в алфавитном порядке. Папки отображаются перед файлами.", + "sortOrder.mixed": "Файлы и папки сортируются по именам в алфавитном порядке. Файлы чередуются с папками.", + "sortOrder.filesFirst": "Файлы и папки сортируются по именам в алфавитном порядке. Файлы отображаются перед папками. ", + "sortOrder.type": "Файлы и папки сортируются по расширениям в алфавитном порядке. Папки отображаются перед файлами.", + "sortOrder.modified": "Файлы и папки сортируются по дате последнего изменения в порядке убывания. Папки отображаются перед файлами.", + "sortOrder": "Управляет порядком сортировки файлов и папок в проводнике. Наряду с сортировкой по умолчанию можно установить следующие варианты сортировки: 'mixed' (файлы и папки сортируются вместе), 'type' (по типу файла), 'modified' (по дате последнего изменения) и 'filesFirst' (сортировать файлы перед папками)." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..acc03d2cae --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "Отмена", + "overwrite": "Перезаписать", + "retry": "Повторить попытку", + "readonlySaveError": "Не удалось сохранить \"{0}\": файл защищен от записи. Чтобы снять защиту, нажмите \"Перезаписать\".", + "genericSaveError": "Не удалось сохранить \"{0}\": {1}", + "staleSaveError": "Не удалось сохранить \"{0}\": содержимое на диске более новое. Чтобы сравнить свою версию с версией на диске, нажмите **Сравнить**.", + "compareChanges": "Сравнить", + "saveConflictDiffLabel": "{0} (на диске) ↔ {1} (в {2}) - Разрешить конфликт сохранения", + "userGuide": "Используйте команды на панели инструментов редактора справа для **отмены** изменений или **перезаписи** содержимого на диске с учетом этих изменений" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..22ad265cd5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Нет открытой папки", + "explorerSection": "Раздел проводника", + "noWorkspaceHelp": "Вы еще не открыли папку.", + "openFolder": "Открыть папку" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..a81792c1e3 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "Раздел проводника", + "treeAriaLabel": "Проводник" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..2edd322a9d --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "Введите имя файла. Нажмите клавишу ВВОД, чтобы подтвердить введенные данные, или ESCAPE для отмены.", + "filesExplorerViewerAriaLabel": "{0}, Проводник", + "dropFolders": "Вы хотите добавить папки в рабочую область?", + "dropFolder": "Вы хотите добавить папку в рабочую область?", + "addFolders": "&&Добавить папки", + "addFolder": "&&Добавить папку", + "confirmOverwriteMessage": "{0} уже существует в целевой папке. Заменить его?", + "irreversible": "Это действие необратимо!", + "replaceButtonLabel": "&&Заменить" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..be2740a709 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "Открытые редакторы", + "openEditosrSection": "Раздел открытых редакторов", + "treeAriaLabel": "Открытые редакторы: список активных файлов", + "dirtyCounter": "Не сохранено: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..41a1ab150a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}, группа редакторов", + "openEditorAriaLabel": "{0}, открытый редактор", + "saveAll": "Сохранить все", + "closeAllUnmodified": "Закрыть без изменений", + "closeAll": "Закрыть все", + "compareWithSaved": "Сравнить с сохраненным", + "close": "Закрыть", + "closeOthers": "Закрыть другие" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/rus/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..c32f625523 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "Несохраненных файлов: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/rus/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..32c2e3e166 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (deleted from disk)" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json new file mode 100644 index 0000000000..d0edb745fc --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/gitActions.contribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "switchToChangesView": "Переключиться в представление изменений", + "openInEditor": "Переключиться в представление редактора", + "workbenchStage": "Промежуточное сохранение", + "workbenchUnstage": "Отменить промежуточное сохранение", + "stageSelectedLines": "Промежуточно сохранить выделенные строки", + "unstageSelectedLines": "Отменить промежуточное сохранение выбранных строк", + "revertSelectedLines": "Отменить выбранные строки", + "confirmRevertMessage": "Действительно отменить выбранные изменения?", + "irreversible": "Это действие необратимо.", + "revertChangesLabel": "&&Отменить изменения", + "openChange": "Открыть изменение", + "openFile": "Открыть файл", + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/gitActions.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/gitActions.i18n.json new file mode 100644 index 0000000000..eb227eb256 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/gitActions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openChange": "Открыть изменение", + "openFile": "Открыть файл", + "init": "Инициализация", + "refresh": "Обновить", + "stageChanges": "Промежуточное сохранение", + "stageAllChanges": "Промежуточно сохранить все", + "confirmUndoMessage": "Действительно отменить все изменения?", + "confirmUndoAllOne": "Имеются изменения в файле ({0}), промежуточное сохранение которых не выполнено.\n\nЭто действие необратимо!", + "confirmUndoAllMultiple": "Имеются изменения в файлах ({0}), промежуточное сохранение которых не выполнено.\n\nЭто действие необратимо!", + "cleanChangesLabel": "&&Отменить изменения", + "confirmUndo": "Действительно отменить изменения в {0}?", + "irreversible": "Это действие необратимо.", + "undoChanges": "Очистить", + "undoAllChanges": "Очистить все", + "unstage": "Отменить промежуточное сохранение", + "unstageAllChanges": "Отменить промежуточное сохранение всего", + "dirtyTreeCheckout": "Невозможно получить для изменения. Сначала нужно зафиксировать или спрятать работу.", + "commitStaged": "Зафиксировать промежуточно сохраненные изменения", + "commitStagedAmend": "Зафиксировать промежуточные (изменение)", + "commitStagedSignedOff": "Зафиксировать промежуточные элементы (завершено)", + "commit": "Commit", + "commitMessage": "Сообщение о фиксации", + "commitAll": "Зафиксировать все", + "commitAllSignedOff": "Зафиксировать все (завершено)", + "commitAll2": "Зафиксировать все", + "commitStaged2": "Зафиксировать промежуточно сохраненные изменения", + "dirtyTreePull": "Невозможно получить. Зафиксируйте или спрячьте работу.", + "authFailed": "Сбой аутентификации для команды git remote.", + "pushToRemote": "Отправить в:", + "pushToRemotePickMessage": "Выберите удаленный компьютер, на который следует отправить ветвь \"{0}\":", + "publish": "Опубликовать", + "confirmPublishMessage": "Действительно опубликовать \"{0}\" в \"{1}\"?", + "confirmPublishMessageButton": "Опубликовать", + "publishPickMessage": "Выберите удаленный сервер, на котором нужно опубликовать ветвь \"{0}\":", + "sync is unpredictable": "Это действие отправляет фиксации в \"{0}\" и извлекает их из этого расположения.", + "ok": "ОК", + "cancel": "Отмена", + "never again": "ОК. Больше не показывать", + "undoLastCommit": "Отменить последнюю фиксацию" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json new file mode 100644 index 0000000000..5860410508 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/gitQuickOpen.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refAriaLabel": "{0}, GIT", + "checkoutBranch": "Ветвь в {0}", + "checkoutRemoteBranch": "Удаленная ветвь в {0}", + "checkoutTag": "Тег в {0}", + "alreadyCheckedOut": "Ветвь {0} уже является текущей ветвью", + "branchAriaLabel": "{0}, ветвь GIT", + "createBranch": "Создать ветвь {0}", + "noBranches": "Нет других ветвей", + "notValidBranchName": "Укажите допустимое имя ветви" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/gitServices.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/gitServices.i18n.json new file mode 100644 index 0000000000..3abbb6a8f2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/gitServices.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cantOpen": "Невозможно открыть этот ресурс GIT.", + "gitIndexChanges": "{0} (index) ↔ {1}", + "gitIndexChangesDesc": "{0} — изменения в индексе", + "gitIndexChangesRenamed": "{0} ← {1}", + "gitIndexChangesRenamedDesc": "{0} — переименовано — изменения в индексе", + "workingTreeChanges": "{0} (HEAD) ↔ {1}", + "workingTreeChangesDesc": "{0} — изменения в рабочем дереве", + "gitMergeChanges": "{0} (merge) ↔ {1}", + "gitMergeChangesDesc": "{0} — объединить изменения", + "updateGit": "Вероятно, у вас установлен GIT {0}. Код лучше всего работает с GIT >=2.0.0.", + "download": "Скачать", + "neverShowAgain": "Больше не показывать", + "configureUsernameEmail": "Настройте ваши имя пользователя и электронную почту GIT.", + "badConfigFile": "GIT {0}", + "unmergedChanges": "Перед сохранением необходимо сначала разрешить необъединенные изменения.", + "showOutput": "Показать выходные данные", + "cancel": "Отмена", + "checkNativeConsole": "Ошибка при выполнении операции GIT. Проверьте выходные данные или используйте консоль для проверки состояния репозитория.", + "changesFromIndex": "{0} (index)", + "changesFromIndexDesc": "{0} — изменения в индексе", + "changesFromTree": "{0} ({1})", + "changesFromTreeDesc": "{0} — изменения в {1}", + "cantOpenResource": "Невозможно открыть этот ресурс GIT." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json new file mode 100644 index 0000000000..8069f33e9a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/gitWidgets.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "publishBranch": "Опубликовать ветвь", + "syncBranch": "Синхронизировать изменения", + "gitNotEnabled": "GIT недоступен в этой рабочей области." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json new file mode 100644 index 0000000000..d41d773a93 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/gitWorkbenchContributions.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gitProgressBadge": "Состояние выполнения GIT", + "gitPendingChangesBadge": "Ожидающие изменения: {0}", + "toggleGitViewlet": "Показать GIT", + "git": "GIT", + "view": "Просмотреть", + "gitCommands": "Команды GIT", + "gitConfigurationTitle": "GIT", + "gitEnabled": "С поддержкой GIT", + "gitPath": "Путь к исполняемому файлу GIT", + "gitAutoRefresh": "Включено ли автоматическое обновление", + "gitAutoFetch": "Включено ли автоматическое получение.", + "gitLongCommit": "Следует ли предупреждать о длинных сообщениях о фиксации.", + "gitLargeRepos": "Всегда разрешать Code управлять большими репозиториями.", + "confirmSync": "Подтвердите синхронизацию репозиториев Git.", + "countBadge": "Управляет счетчиком эмблем Git.", + "checkoutType": "Определяет, какие типы ветвей вносятся в список." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json new file mode 100644 index 0000000000..df0f8633e8 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/views/changes/changesView.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "needMessage": "Предоставьте сообщение фиксации. В любом случае можно нажать кнопку **{0}**, чтобы зафиксировать изменения. При наличии промежуточно сохраненных изменений зафиксированы будут только они. В противном случае фиксируются все изменения.", + "nothingToCommit": "Как только появятся изменения для фиксации, введите сообщение фиксации и нажмите кнопку **{0}**, чтобы зафиксировать изменения. При наличии промежуточно сохраненных изменений зафиксированы будут только они. В противном случае фиксируются все изменения.", + "longCommit": "Рекомендуется, чтобы первая строка фиксации была короче 50 символов. Используйте новые строки для дополнительных сведений.", + "commitMessage": "Сообщение (чтобы зафиксировать, нажмите {0})", + "commitMessageAriaLabel": "GIT: введите сообщение фиксации и нажмите {0}, чтобы выполнить фиксацию", + "treeAriaLabel": "Представление изменений GIT", + "showOutput": "Показать выходные данные GIT" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json new file mode 100644 index 0000000000..ff2ff219b0 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/views/changes/changesViewer.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stagedChanges": "Промежуточно сохраненные изменения", + "allChanges": "Изменения", + "mergeChanges": "Объединить изменения", + "outsideOfWorkspace": "Этот файл находится за пределами текущей рабочей области.", + "modified-char": "M", + "added-char": "A", + "deleted-char": "D", + "renamed-char": "R", + "copied-char": "C", + "untracked-char": "U", + "ignored-char": "!", + "title-index-modified": "Изменено в индексе", + "title-modified": "Изменено", + "title-index-added": "Добавлено в индекс", + "title-index-deleted": "Удалено из индекса", + "title-deleted": "Удалено", + "title-index-renamed": "Переименовано в индексе", + "title-index-copied": "Скопировано в индекс", + "title-untracked": "Не отслеживается", + "title-ignored": "Проигнорировано", + "title-conflict-both-deleted": "Конфликт: оба удалены", + "title-conflict-added-by-us": "Конфликт: добавлено нами", + "title-conflict-deleted-by-them": "Конфликт: удалено ими", + "title-conflict-added-by-them": "Конфликт: добавлено ими", + "title-conflict-deleted-by-us": "Конфликт: удалено нами", + "title-conflict-both-added": "Конфликт: оба добавлены", + "title-conflict-both-modified": "Конфликт: оба изменены", + "fileStatusAriaLabel": "Файл {0} в папке {1} имеет состояние {2}, GIT", + "ariaLabelStagedChanges": "Промежуточно сохраненные изменения, GIT", + "ariaLabelChanges": "Изменения, GIT", + "ariaLabelMerge": "Объединение, GIT" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json new file mode 100644 index 0000000000..a631e8fe0e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/views/disabled/disabledView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disabled": "GIT отключен в параметрах." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json new file mode 100644 index 0000000000..dc1337c520 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/views/empty/emptyView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noGit": "Эта рабочая область еще не находится в системе управления версиями GIT.", + "gitinit": "Инициализировать репозиторий GIT" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json new file mode 100644 index 0000000000..96b25dad69 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/views/gitless/gitlessView.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "macInstallWith": "Вы можете установить его с {0}, скачать его с сайта {1} или установить средства разработчика командной строки {2}, просто введя {3} в окне терминала.", + "winInstallWith": "Вы можете установить его с {0} или скачать его с сайта {1}.", + "linuxDownloadFrom": "Его можно скачать по адресу {0}.", + "downloadFrom": "Его можно скачать по адресу {0}.", + "looksLike": "Похоже, GIT не установлен в вашей системе.", + "pleaseRestart": "После установки GIT перезапустите VSCode." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json new file mode 100644 index 0000000000..97fd8f6d54 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/views/huge/hugeView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "huge": "Репозиторий включает много активных изменений.\nЭто может замедлить работу Code.", + "setting": "Вы можете навсегда отключить это предупреждение с помощью следующего параметра:", + "allo": "Разрешать большие репозитории" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json new file mode 100644 index 0000000000..c00084acba --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/views/notroot/notrootView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrongRoot": "Кажется, каталог находится в репозитории GIT.", + "pleaseRestart": "Чтобы получить доступ к возможностям GIT, откройте корневой каталог репозитория." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json b/i18n/rus/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json new file mode 100644 index 0000000000..b31af07032 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/browser/views/noworkspace/noworkspaceView.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspaceHelp": "Вы еще не открыли папку.", + "pleaseRestart": "Чтобы получить доступ к возможностям GIT, откройте папку с репозиторием GIT.", + "openFolder": "Открыть папку" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json new file mode 100644 index 0000000000..5064266f4f --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/electron-browser/git.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSCMViewlet": "Показать SCM", + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json b/i18n/rus/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json new file mode 100644 index 0000000000..32b9ad795c --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/electron-browser/gitActions.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "valid": "Укажите допустимый URL-адрес репозитория GIT", + "url": "URL-адрес репозитория", + "directory": "Каталог-назначение для клонирования", + "cloning": "Клонирование репозитория \"{0}\"...", + "already exists": "Целевой репозиторий уже существует, выберите другой каталог-назначение для клонирования." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json b/i18n/rus/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json new file mode 100644 index 0000000000..6f2dc5416f --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/electron-main/askpassService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "git": "GIT" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/git/node/git.lib.i18n.json b/i18n/rus/src/vs/workbench/parts/git/node/git.lib.i18n.json new file mode 100644 index 0000000000..ce678c0993 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/git/node/git.lib.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorBuffer": "Невозможно открыть файл из GIT.", + "fileBinaryError": "Похоже, файл является двоичным, и его нельзя открыть как текстовый." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..c4af9fea38 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "Предварительный просмотр HTML", + "devtools.webview": "Разработчик: средства Webview" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/rus/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..93ddc6105b --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "Недопустимые входные данные для редактора." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/rus/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..083b9b3e1a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "Разработчик: средства Webview" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/rus/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..d21ea41406 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "Просмотреть", + "problems.view.show.label": "Показать проблемы", + "problems.panel.configuration.title": "Представление \"Проблемы\"", + "problems.panel.configuration.autoreveal": "Определяет, следует ли представлению \"Проблемы\" отображать файлы при их открытии", + "markers.panel.title.problems": "Проблемы", + "markers.panel.aria.label.problems.tree": "Проблемы, сгруппированные по файлам", + "markers.panel.no.problems.build": "В рабочей области проблемы пока не обнаружены.", + "markers.panel.no.problems.filters": "Для указанного условия фильтра результаты не обнаружены", + "markers.panel.action.filter": "Фильтр проблем", + "markers.panel.filter.placeholder": "Фильтровать по типу или тексту", + "markers.panel.filter.errors": "ошибки", + "markers.panel.filter.warnings": "предупреждения", + "markers.panel.filter.infos": "сообщения", + "markers.panel.single.error.label": "1 ошибка", + "markers.panel.multiple.errors.label": "Ошибок: {0}", + "markers.panel.single.warning.label": "1 предупреждение", + "markers.panel.multiple.warnings.label": "Предупреждения: {0}", + "markers.panel.single.info.label": "1 сообщение", + "markers.panel.multiple.infos.label": "Сообщения: {0}", + "markers.panel.single.unknown.label": "1 неизвестный", + "markers.panel.multiple.unknowns.label": "Неизвестные: {0}", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "{0} с проблемами ({1})", + "problems.tree.aria.label.error.marker": "Ошибка, созданная {0}: {1} в строке {2} и символе {3}", + "problems.tree.aria.label.error.marker.nosource": "Ошибка: {0} в строке {1} и символе {2}", + "problems.tree.aria.label.warning.marker": "Предупреждение, созданное {0}: {1} в строке {2} и символе {3}", + "problems.tree.aria.label.warning.marker.nosource": "Предупреждение: {0} в строке {1} и символе {2}", + "problems.tree.aria.label.info.marker": "Информационное сообщение, созданное {0}: {1} в строке {2} и символе {3}", + "problems.tree.aria.label.info.marker.nosource": "Информационное сообщение: {0} в строке {1} и символе {2}", + "problems.tree.aria.label.marker": "Проблема, созданная {0}: {1} в строке {2} и символе {3}", + "problems.tree.aria.label.marker.nosource": "Проблема: {0} в строке {1} и символе {2}", + "errors.warnings.show.label": "Показать ошибки и предупреждения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/rus/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..e6bd0b50a5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "Копировать" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..995427aa27 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Вас не затруднит пройти краткий опрос?", + "takeSurvey": "Пройти опрос", + "remindLater": "Напомнить мне позже", + "neverAgain": "Больше не показывать" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..504684e38d --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Вывод", + "viewCategory": "Просмотреть", + "clearOutput.label": "Очистить выходные данные" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/rus/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..195e134f8d --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "Переключить выходные данные", + "clearOutput": "Очистить выходные данные", + "toggleOutputScrollLock": "Включить/отключить SCROLL LOCK для вывода", + "switchToOutput.label": "Переключиться на выходные данные" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/rus/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..30795c5e1e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0}, панель выходных данных", + "outputPanelAriaLabel": "Панель выходных данных" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/rus/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..c63d507fb4 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Вывод", + "channel": "для \"{0}\"" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..50cd83c41c --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "Обнаружен замедленный запуск.", + "slow.detail": "Сожалеем, что у вас произошел замедленный запуск. Перезапустите \"{0}\" с включенным профилированием и отправьте профили нам, чтобы мы могли ускорить загрузку.", + "prof.message": "Профили успешно созданы.", + "prof.detail": "Создайте проблему и вручную вложите следующие файлы:\n{0}", + "prof.restartAndFileIssue": "Создать проблему и выполнить перезапуск", + "prof.restart": "Перезапустить", + "prof.thanks": "Спасибо за помощь.", + "prof.detail.restart": "Для продолжения работы с '{0}' необходимо еще раз перезагрузить систему. Благодарим вас за участие." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..7be9601a48 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "Нажмите нужное сочетание клавиш, а затем — ВВОД. Нажмите клавишу ESC для отмены.", + "defineKeybinding.chordsTo": "Аккорд для" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..3de6128baa --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "Сочетания клавиш", + "SearchKeybindings.AriaLabel": "Поиск настраиваемых сочетаний клавиш", + "SearchKeybindings.Placeholder": "Поиск настраиваемых сочетаний клавиш", + "sortByPrecedene": "Сортировать по приоритету", + "header-message": "Для использования расширенных настроек откройте и измените", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "Настраиваемые сочетания клавиш", + "changeLabel": "Изменить настраиваемое сочетание клавиш", + "addLabel": "Добавить настраиваемое сочетание клавиш", + "removeLabel": "Удаление настраиваемого сочетания клавиш", + "resetLabel": "Сбросить настраиваемое сочетание клавиш", + "showConflictsLabel": "Показать конфиликты", + "copyLabel": "Копировать", + "error": "Произошла ошибка \"{0}\" при редактировании настраиваемого сочетания клавиш. Откройте и проверьте файл \"keybindings.json\".", + "command": "Команда", + "keybinding": "Настраиваемое сочетание клавиш", + "source": "Источник", + "when": "Когда", + "editKeybindingLabelWithKey": "Изменить настраиваемое сочетание клавиш {0}", + "editKeybindingLabel": "Изменить настраиваемое сочетание клавиш", + "addKeybindingLabelWithKey": "Добавить настраиваемое сочетание клавиш {0}", + "addKeybindingLabel": "Добавить настраиваемое сочетание клавиш", + "commandAriaLabel": "Команда: {0}.", + "keybindingAriaLabel": "Настраиваемое сочетание клавиш: {0}.", + "noKeybinding": "Нет назначенных настраиваемых сочетаний клавиш.", + "sourceAriaLabel": "Источник: {0}.", + "whenAriaLabel": "Когда: {0}.", + "noWhen": "Нет контекста \"Когда\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..da4641684e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "Определить назначение клавиш", + "defineKeybinding.kbLayoutErrorMessage": "Вы не сможете нажать это сочетание клавиш в текущей раскладке клавиатуры.", + "defineKeybinding.kbLayoutLocalAndUSMessage": "**{0}** для текущей раскладки клавиатуры (**{1}** для стандартной раскладки клавиатуры \"Английский, США\")", + "defineKeybinding.kbLayoutLocalMessage": "**{0}** для текущей раскладки клавиатуры." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..51c7610189 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "Редактор настроек по умолчанию", + "keybindingsEditor": "Редактор настраиваемых сочетаний клавиш", + "preferences": "Параметры" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..51577deec0 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "Открыть пользовательские параметры", + "openGlobalKeybindings": "Открыть сочетания клавиш", + "openGlobalKeybindingsFile": "Открыть файл сочетаний клавиш", + "openWorkspaceSettings": "Открыть параметры рабочей области", + "openFolderSettings": "Открыть параметры папок", + "pickFolder": "Выбрать папку", + "configureLanguageBasedSettings": "Настроить параметры языка...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "Выбрать язык" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..5e98603568 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "Параметры по умолчанию", + "SearchSettingsWidget.AriaLabel": "Параметры поиска", + "SearchSettingsWidget.Placeholder": "Параметры поиска", + "totalSettingsMessage": "Всего параметров: {0}", + "noSettingsFound": "Нет результатов", + "oneSettingFound": "Один соответствующий параметр", + "settingsFound": "Соответствующих параметров: {0}", + "fileEditorWithInputAriaLabel": "{0}. Редактор текстовых файлов.", + "fileEditorAriaLabel": "Редактор текстовых файлов.", + "preferencesAriaLabel": "Параметры по умолчанию. Текстовый редактор только для чтения." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..eddc2494f3 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "Укажите параметры здесь, чтобы перезаписать параметры по умолчанию.", + "errorInvalidConfiguration": "Не удается записать параметры. Устраните ошибки и предупреждения в файле и повторите попытку.", + "emptyWorkspaceSettingsHeader": "Укажите параметры здесь, чтобы перезаписать параметры пользователей.", + "emptyFolderSettingsHeader": "Укажите параметры папок здесь, чтобы перезаписать параметры рабочих областей.", + "defaultFolderSettingsTitle": "Параметры папок по умолчанию", + "defaultSettingsTitle": "Параметры по умолчанию", + "noSettingsFound": "Параметры не найдены.", + "editTtile": "Изменить", + "replaceDefaultValue": "Заменить в параметрах", + "copyDefaultValue": "Копировать в параметры", + "unsupportedPHPExecutablePathSetting": "Этот параметр должен быть параметром пользователя. Чтобы настроить в рабочей области PHP, откройте PHP-файл и щелкните \"Путь PHP\" в строке состояния.", + "unsupportedWorkspaceSetting": "Этот параметр должен быть параметром пользователя.", + "unsupportedWorkbenchSetting": "Сейчас применить этот параметр нельзя. Он будет применен, когда вы откроете эту папку напрямую." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..8ae17b25f2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "Чтобы создать параметры рабочей области, сначала откройте папку", + "emptyKeybindingsHeader": "Поместите настраиваемые сочетания клавиш в этот файл, чтобы перезаписать клавиши по умолчанию.", + "defaultKeybindings": "Настраиваемые сочетания клавиш по умолчанию", + "folderSettingsName": "{0} (Параметры папок)", + "fail.createSettings": "Невозможно создать \"{0}\" ({1})." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..f2d3c91606 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "Параметры папок" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..4dafb8a62f --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "По умолчанию", + "user": "Пользователь", + "meta": "meta", + "option": "параметр" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..7721b5769b --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "Параметры пользователя", + "workspaceSettingsTarget": "Параметры рабочей области" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..cc78640921 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "Часто используемые", + "noSettings": "Параметры отсутствуют", + "defaultKeybindingsHeader": "Перезапишите настраиваемое сочетание клавиш, поместив их в файл настраиваемых сочетаний клавиш." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..36ee7227e2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Показать все команды", + "clearCommandHistory": "Очистить журнал команд", + "showCommands.label": "Палитра команд...", + "entryAriaLabelWithKey": "{0}, {1}, команды", + "entryAriaLabel": "{0}, команды", + "canNotRun": "Выполнить команду {0} отсюда невозможно.", + "actionNotEnabled": "Команда {0} не разрешена в текущем контексте.", + "recentlyUsed": "недавно использованные", + "morecCommands": "другие команды", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "Нет соответствующих команд" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..82a4443c04 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "Перейти к строке...", + "gotoLineLabelEmptyWithLimit": "Введите номер строки от 1 до {0} для перехода", + "gotoLineLabelEmpty": "Введите номер строки для перехода", + "gotoLineColumnLabel": "Перейти к строке {0} и символу {1}", + "gotoLineLabel": "Перейти к строке {0}", + "gotoLineHandlerAriaLabel": "Введите номер строки, к которой нужно перейти.", + "cannotRunGotoLine": "Чтобы перейти к строке, сначала откройте текстовый файл" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..7318a3fd74 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "Перейти к символу в файле...", + "symbols": "символы ({0})", + "method": "методы ({0})", + "function": "функции ({0})", + "_constructor": "конструкторы ({0})", + "variable": "переменные ({0})", + "class": "классы ({0})", + "interface": "интерфейсы ({0})", + "namespace": "пространства имен ({0})", + "package": "пакеты ({0})", + "modules": "модули ({0})", + "property": "свойства ({0})", + "enum": "перечисления ({0})", + "string": "строки ({0})", + "rule": "правила ({0})", + "file": "файлы ({0})", + "array": "массивы ({0})", + "number": "числа ({0})", + "boolean": "логические значения ({0})", + "object": "объекты ({0})", + "key": "ключи ({0})", + "entryAriaLabel": "{0}, символы", + "noSymbolsMatching": "Нет соответствующих символов", + "noSymbolsFound": "Символы не найдены", + "gotoSymbolHandlerAriaLabel": "Введите, чтобы ограничить символы активного в настоящий момент редактора.", + "cannotRunGotoSymbolInFile": "Нет символьной информации для файла.", + "cannotRunGotoSymbol": "Чтобы перейти к символу, сначала откройте текстовый файл" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..1d0298a254 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, справка по средству выбора", + "globalCommands": "глобальные команды", + "editorCommands": "команды редактора" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..ed33368cd9 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "Показать и выполнить команды", + "gotoLineDescriptionMac": "Перейти к строке", + "gotoLineDescriptionWin": "Перейти к строке", + "gotoSymbolDescription": "Перейти к символу в файле", + "gotoSymbolDescriptionScoped": "Перейти к символу в файле по категории", + "helpDescription": "Показать справку", + "viewPickerDescription": "Открыть представление" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..4a9a6ecad3 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, средство выбора представлений", + "views": "Представления", + "panels": "Панели", + "terminals": "Терминал", + "terminalTitle": "{0}: {1}", + "channels": "Вывод", + "openView": "Открыть представление", + "quickOpenView": "Быстрое открытие представления" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..4ff14b63ab --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "После изменения параметра необходима выполнить перезагрузку, чтобы изменения вступили в силу.", + "relaunchSettingDetail": "Нажмите кнопку \"Перезагрузить\", чтобы перезагрузить {0} и включить параметр.", + "restart": "Перезапустить", + "relaunchWorkspaceMessage": "Для изменения этой рабочей области требуется перезагрузить нашу систему расширений.", + "reload": "Перезагрузка" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/rus/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..4ace7ac908 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "Цвет фона полей редактора для измененных строк.", + "editorGutterAddedBackground": "Цвет фона полей редактора для добавленных строк.", + "editorGutterDeletedBackground": "Цвет фона полей редактора для удаленных строк.", + "overviewRulerModifiedForeground": "Цвет метки линейки в окне просмотра для измененного содержимого.", + "overviewRulerAddedForeground": "Цвет метки линейки в окне просмотра для добавленного содержимого. ", + "overviewRulerDeletedForeground": "Цвет метки линейки в окне просмотра для удаленного содержимого. " +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..1c07b9aab7 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "Показать GIT", + "source control": "Система управления версиями", + "toggleSCMViewlet": "Показать SCM", + "view": "Просмотреть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..f7f709b7dd --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "Ожидающие изменения: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..4af0773d71 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "Установить дополнительных поставщиков SCM...", + "switch provider": "Переключить поставщик SCM..." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..a75c8eafa0 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Message (press {0} to commit)", + "installAdditionalSCMProviders": "Установить дополнительных поставщиков SCM...", + "no open repo": "Отсутствуют активные системы управления версиями.", + "source control": "Система управления версиями", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..09fc2acabe --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "результаты файлов и символов", + "fileResults": "файлы по запросу" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..ce4cbc4bdc --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, средство выбора файлов", + "searchResults": "результаты поиска" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..e68c30abd2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, средство выбора символов", + "symbols": "результаты символов", + "noSymbolsMatching": "Нет соответствующих символов", + "noSymbolsWithoutInput": "Введите запрос, чтобы найти символы" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..cd4fc5dcef --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "ввод", + "useIgnoreFilesDescription": "Использование пропуска файлов", + "useExcludeSettingsDescription": "Использовать параметры исключения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..ee8ef01846 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (заменить предварительную версию)" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..ccbb45e9ba --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Перейти к символу в рабочей области...", + "name": "Поиск", + "showSearchViewlet": "Показать средство поиска", + "view": "Просмотр", + "findInFiles": "Найти в файлах", + "openAnythingHandlerDescription": "Перейти к файлу", + "openSymbolDescriptionNormal": "Перейти к символу в рабочей области", + "searchOutputChannelTitle": "Поиск", + "searchConfigurationTitle": "Поиск", + "exclude": "Настройте стандартные маски для исключения файлов и папок при поиске. Все стандартные маски наследуются от параметра file.exclude.", + "exclude.boolean": "Стандартная маска, соответствующая путям к файлам. Задайте значение true или false, чтобы включить или отключить маску.", + "exclude.when": "Дополнительная проверка элементов того же уровня соответствующего файла. Используйте $(basename) в качестве переменной для соответствующего имени файла.", + "useRipgrep": "Определяет, использовать ли ripgrep в текстовом поиске", + "useIgnoreFilesByDefault": "Определяет, следует ли использовать GITIGNORE- и IGNORE-файлы по умолчанию при поиске в новой рабочей области.", + "search.quickOpen.includeSymbols": "Настройте для включения результатов поиска глобальных символов в файлы по запросу для Quick Open." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..b5551aca58 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "Показать следующий шаблон включения в поиск", + "previousSearchIncludePattern": "Показать предыдущий шаблон включения в поиск ", + "nextSearchExcludePattern": "Показать следующий шаблон исключения из поиска", + "previousSearchExcludePattern": "Показать предыдущий шаблон исключения из поиска", + "nextSearchTerm": "Показать следующее условие поиска", + "previousSearchTerm": "Показать предыдущее условие поиска", + "focusNextInputBox": "Фокус на следующем поле ввода", + "focusPreviousInputBox": "Фокус на предыдущем поле ввода", + "replaceInFiles": "Заменить в файлах", + "findInWorkspace": "Найти в рабочей области...", + "findInFolder": "Найти в папке...", + "RefreshAction.label": "Обновить", + "ClearSearchResultsAction.label": "Очистить результаты поиска", + "FocusNextSearchResult.label": "Перейти к следующему результату поиска.", + "FocusPreviousSearchResult.label": "Перейти к предыдущему результату поиска.", + "RemoveAction.label": "Удалить", + "file.replaceAll.label": "Заменить все", + "match.replace.label": "Заменить" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..a9875ab212 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "Другие файлы", + "searchFileMatches": "Найдено файлов: {0}", + "searchFileMatch": "Найден {0} файл", + "searchMatches": "Найдено соответствий: {0}", + "searchMatch": "Найдено соответствие: {0}", + "folderMatchAriaLabel": "Совпадений в корневой папке {1}: {0}, результат поиска", + "fileMatchAriaLabel": "Совпадений в файле {1} папки {2}: {0}, результат поиска", + "replacePreviewResultAria": "Заменить термин {0} на {1} в столбце {2} и строке {3}", + "searchResultAria": "Обнаружен термин {0} в столбце {1} и строке {2}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..68bc092827 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "Переключить сведения о поиске", + "searchScope.includes": "включаемые файлы", + "label.includes": "Шаблоны включения в поиск", + "searchScope.excludes": "исключаемые файлы", + "label.excludes": "Шаблоны исключения из поиска", + "replaceAll.confirmation.title": "Заменить все", + "replaceAll.confirm.button": "Заменить", + "replaceAll.occurrence.file.message": "Вхождение {0} заменено в {1} файле на \"{2}\".", + "removeAll.occurrence.file.message": "Вхождение {0} заменено в {1} файле.", + "replaceAll.occurrence.files.message": "Вхождение {0} заменено на \"{2}\" в следующем числе файлов: {1}.", + "removeAll.occurrence.files.message": "Вхождение {0} заменено в следующем числе файлов: {1}.", + "replaceAll.occurrences.file.message": "Вхождения ({0}) заменены в {1} файле на \"{2}\".", + "removeAll.occurrences.file.message": "Вхождения ({0}) заменены в {1} файле.", + "replaceAll.occurrences.files.message": "Вхождения ({0}) заменены на \"{2}\" в следующем числе файлов: {1}.", + "removeAll.occurrences.files.message": "Вхождения ({0}) заменены в следующем числе файлов: {1}.", + "removeAll.occurrence.file.confirmation.message": "Заменить вхождение {0} в {1} файле на \"{2}\"?", + "replaceAll.occurrence.file.confirmation.message": "Заменить вхождение {0} в {1} файле?", + "removeAll.occurrence.files.confirmation.message": "Заменить вхождение {0} на \"{2}\" в следующем числе файлов: {1}?", + "replaceAll.occurrence.files.confirmation.message": "Заменить вхождение {0} в следующем числе файлов: {1}?", + "removeAll.occurrences.file.confirmation.message": "Заменить вхождения ({0}) в {1} файле на \"{2}\"?", + "replaceAll.occurrences.file.confirmation.message": "Заменить вхождения ({0}) в {1} файле?", + "removeAll.occurrences.files.confirmation.message": "Заменить вхождения ({0}) на \"{2}\" в следующем числе файлов: {1}?", + "replaceAll.occurrences.files.confirmation.message": "Заменить вхождения ({0}) в следующем числе файлов: {1}?", + "treeAriaLabel": "Результаты поиска", + "searchPathNotFoundError": "Путь поиска не найден: {0}", + "searchMaxResultsWarning": "Результирующий набор включает только подмножество всех соответствий. Чтобы уменьшить число результатов, сузьте условия поиска.", + "searchCanceled": "Поиск был отменен до того, как были найдены какие-либо результаты — ", + "noResultsIncludesExcludes": "Не найдено результатов в \"{0}\", исключая \"{1}\", — ", + "noResultsIncludes": "Результаты в \"{0}\" не найдены — ", + "noResultsExcludes": "Результаты не найдены за исключением \"{0}\" — ", + "noResultsFound": "Ничего не найдено. Проверьте параметры исключений и пропуска файлов.", + "rerunSearch.message": "Выполнить поиск еще раз", + "rerunSearchInAll.message": "Выполните поиск во всех файлах", + "openSettings.message": "Открыть параметры", + "openSettings.learnMore": "Дополнительные сведения", + "ariaSearchResultsStatus": "Поиск вернул результатов: {0} в файлах: {1}", + "search.file.result": "{0} результат в {1} файле", + "search.files.result": "{0} результат в следующем числе файлов: {1}", + "search.file.results": "Результатов: {0} в {1} файле", + "search.files.results": "Результатов: {0} в следующем числе файлов: {1}", + "searchWithoutFolder": "Папка еще не открыта. Выполняется поиск только по открытым файлам — ", + "openFolder": "Открыть папку" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..f180e5f8a3 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "Заменить все (отправить поиск для включения)", + "search.action.replaceAll.enabled.label": "Заменить все", + "search.replace.toggle.button.title": "Переключение замены", + "label.Search": "Поиск: введите условие поиска и нажмите клавишу ВВОД, чтобы выполнить поиск, или ESCAPE для отмены", + "search.placeHolder": "Поиск", + "label.Replace": "Замена: введите термин для замены и нажмите ВВОД для просмотра или ESC для отмены", + "search.replace.placeHolder": "Заменить", + "regexp.validationFailure": "Выражение соответствует чему угодно" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/rus/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..117d3d1d0a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "В рабочей области отсутствуют папки с указанным именем: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..e9a9d50524 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "Добавляет фрагменты.", + "vscode.extension.contributes.snippets-language": "Идентификатор языка, для которого добавляется этот фрагмент.", + "vscode.extension.contributes.snippets-path": "Путь к файлу фрагментов. Путь указывается относительно папки расширения и обычно начинается с \"./snippets/\".", + "invalid.language": "Неизвестный язык в contributes.{0}.language. Указанное значение: {1}", + "invalid.path.0": "В contributes.{0}.path требуется строка. Указанное значение: {1}", + "invalid.path.1": "contributes.{0}.path ({1}) должен был быть включен в папку расширения ({2}). Это может сделать расширение непереносимым.", + "badVariableUse": "Похоже, во фрагменте \"{0}\" перепутаны переменные и заполнители. Дополнительные сведения см. на странице https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..3918e944f9 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "Вставить фрагмент кода" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..5f19417eec --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "Выберите язык для фрагментов кода", + "openSnippet.errorOnCreate": "Не удалось создать {0}.", + "openSnippet.label": "Открыть пользовательские фрагменты", + "preferences": "Параметры", + "snippetSchema.json.default": "Пустой фрагмент", + "snippetSchema.json": "Настройка фрагмента пользователя", + "snippetSchema.json.prefix": "Префикс, используемый при выборе фрагмента в Intellisense.", + "snippetSchema.json.body": "Содержимое фрагмента. Используйте '$1', '${1:defaultText}' для определения положения курсора и '$0' для определения конечного положения курсора. Для вставки переменных используйте синтаксис '${varName}' и '${varName:defaultText}', например, \"Это файл: $TM_FILENAME\".", + "snippetSchema.json.description": "Описание фрагмента." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..354f99de67 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "Фрагмент кода пользователя", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0}, {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..01420c54bc --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "Вставка фрагментов при совпадении их префиксов. Функция работает оптимально, если параметр \"quickSuggestions\" отключен." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..815c53562f --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "Помогите нам улучшить поддержку {0}", + "takeShortSurvey": "Пройдите краткий опрос", + "remindLater": "Напомнить мне позже", + "neverAgain": "Больше не показывать" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..995427aa27 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Вас не затруднит пройти краткий опрос?", + "takeSurvey": "Пройти опрос", + "remindLater": "Напомнить мне позже", + "neverAgain": "Больше не показывать" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..be76ebaaff --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "Нет соответствующих задач" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..9ada99974e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, задачи", + "recentlyUsed": "недавно использованные задачи", + "configured": "настроенные задачи", + "detected": "обнаруженные задачи", + "customizeTask": "Настроить задачу" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..5e38720ebb --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Введите имя задачи для перезапуска", + "noTasksMatching": "Нет соответствующих задач", + "noTasksFound": "Задачи для перезапуска не найдены" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..69ed6e6b86 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Введите имя задачи, которую нужно выполнить", + "noTasksMatching": "Нет соответствующих задач", + "noTasksFound": "Задачи не найдены" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..9478b936ee --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Type the name of a task to terminate", + "noTasksMatching": "Нет соответствующих задач", + "noTasksFound": "No tasks to terminate found" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..be76ebaaff --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noTasksMatching": "Нет соответствующих задач" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..12954ee744 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Предупреждение: options.cwd должен иметь тип string. Игнорируется значение {0}\n", + "ConfigurationParser.noargs": "Ошибка: аргументы команды должны представлять собой массив строк. Указанное значение:\n{0}", + "ConfigurationParser.noShell": "Предупреждение: конфигурация оболочки поддерживается только при выполнении задач в терминале.", + "ConfigurationParser.noName": "Ошибка: сопоставитель проблем в области объявления должен иметь имя:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Предупреждение: определен неизвестный сопоставитель проблем. Поддерживаемые типы: строка | СопоставительПроблем | (строка | СопоставительПроблем)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Ошибка: недопустимая ссылка на problemMatcher: {0}\n", + "ConfigurationParser.noTaskName": "Ошибка: задачи должны предоставлять свойство taskName. Задача будет проигнорирована.\n{0}\n", + "taskConfiguration.shellArgs": "Предупреждение: задача \"{0}\" является командой оболочки, и имя команды или одного из ее аргументов включает пробелы без escape-последовательности. Чтобы обеспечить правильную расстановку кавычек в командной строке, объедините аргументы в команде.", + "taskConfiguration.noCommandOrDependsOn": "Ошибка: в задаче \"{0}\" не указаны ни команда, ни свойство dependsOn. Задача будет проигнорирована. Определение задачи:\n{1}", + "taskConfiguration.noCommand": "Ошибка: задача \"{0}\" не определяет команду. Задача будет игнорироваться. Ее определение:\n{1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..5d94fc74b0 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "Фактический тип задачи", + "TaskDefinition.properties": "Дополнительные свойства типа задачи", + "TaskTypeConfiguration.noType": "В конфигурации типа задачи отсутствует обязательное свойство 'taskType'", + "TaskDefinitionExtPoint": "Добавляет типы задачи" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..a1ca8d4852 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": "Выполняет команду сборки .NET Core", + "msbuild": "Выполняет целевой объект сборки", + "externalCommand": "Пример для запуска произвольной внешней команды", + "Maven": "Выполняет стандартные команды Maven" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..e9d680cc5b --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "Дополнительные параметры команды", + "JsonSchema.options.cwd": "Текущий рабочий каталог выполняемой программы или сценария. Если этот параметр опущен, используется корневой каталог текущей рабочей области Code.", + "JsonSchema.options.env": "Среда выполняемой программы или оболочки. Если этот параметр опущен, используется среда родительского процесса.", + "JsonSchema.shellConfiguration": "Задает используемую оболочку.", + "JsonSchema.shell.executable": "Используемая оболочка.", + "JsonSchema.shell.args": "Аргументы оболочки.", + "JsonSchema.command": "Выполняемая команда. Это может быть внешняя программа или команда оболочки.", + "JsonSchema.tasks.args": "Аргументы, передаваемые в команду при вызове этой задачи.", + "JsonSchema.tasks.taskName": "Имя задачи", + "JsonSchema.tasks.windows": "Настройка команд Windows", + "JsonSchema.tasks.mac": "Настройка команд Mac", + "JsonSchema.tasks.linux": "Настройка команд Linux", + "JsonSchema.tasks.suppressTaskName": "Определяет, добавляется ли имя задачи в команду в качестве аргумента. Если опущено, используется глобальное значение.", + "JsonSchema.tasks.showOutput": "Определяет, выводятся ли выходные данные выполняющейся задачи. Если опущено, используется глобальное значение.", + "JsonSchema.echoCommand": "Определяет, переносится ли выполняемая команда в выходные данные. Значение по умолчанию — false.", + "JsonSchema.tasks.watching.deprecation": "Устарело. Используйте isBackground.", + "JsonSchema.tasks.watching": "Должна ли выполняемая задача оставаться активной и наблюдать за файловой системой.", + "JsonSchema.tasks.background": "Поддерживается ли выполняющаяся задача в работающем состоянии и исполняется ли она в фоновом режиме.", + "JsonSchema.tasks.promptOnClose": "Отображается ли для пользователя приглашение, если VS Code закрывается с выполняющейся задачей.", + "JsonSchema.tasks.build": "Сопоставляет эту задачу с командой сборки Code по умолчанию.", + "JsonSchema.tasks.test": "Сопоставляет эту задачу с командой тестирования по умолчанию в Code.", + "JsonSchema.tasks.matchers": "Используемые сопоставители проблем. Это может быть строка, определение сопоставителя проблем или массив строк и сопоставителей проблем.", + "JsonSchema.args": "Дополнительные аргументы, передаваемые в команду.", + "JsonSchema.showOutput": "Определяет, выводятся ли выходные данные выполняющейся задачи. Если опущено, используется значение \"всегда\".", + "JsonSchema.watching.deprecation": "Устарело. Используйте isBackground.", + "JsonSchema.watching": "Должна ли выполняемая задача оставаться активной и наблюдать за файловой системой.", + "JsonSchema.background": "Поддерживается ли выполняющаяся задача в работающем состоянии и исполняется ли она в фоновом режиме.", + "JsonSchema.promptOnClose": "Определяет, получает ли пользователь запрос при закрытии редактора VS Code в тот момент, когда выполняется фоновая задача.", + "JsonSchema.suppressTaskName": "Определяет, добавляется ли имя задачи в команду в качестве аргумента. Значение по умолчанию — false.", + "JsonSchema.taskSelector": "Префикс, указывающий на то, что аргумент является задачей.", + "JsonSchema.matchers": "Используемые сопоставители проблем. Это может быть строка, определение сопоставителя проблем или массив строк и сопоставителей проблем.", + "JsonSchema.tasks": "Конфигурации задачи. Обычно это дополнительные параметры задачи, уже определенной во внешнем средстве запуска задач." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..4cc16bb39a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "Номер версии конфигурации", + "JsonSchema._runner": "Средство запуска завершило работу. Используйте официальное свойство средства запуска", + "JsonSchema.runner": "Определяет, следует ли запустить задачу в качестве процесса с отображением выходных данных задачи в окне вывода или в терминале.", + "JsonSchema.windows": "Настройка команд Windows", + "JsonSchema.mac": "Настройка команд Mac", + "JsonSchema.linux": "Настройка команд Linux", + "JsonSchema.shell": "Указывает, является ли команда командой оболочки или внешней программой. Если опущено, значение по умолчанию — false." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..7510522204 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "Указывает, является ли команда командой оболочки или внешней программой. Если опущено, значение по умолчанию — false.", + "JsonSchema.tasks.isShellCommand.deprecated": "Свойство isShellCommand является устаревшим. Используйте свойство типа задачи и свойство оболочки в параметрах. Также см. заметки о выпуске для версии 1.14.", + "JsonSchema.tasks.dependsOn.string": "Другая задача, от которой зависит эта задача.", + "JsonSchema.tasks.dependsOn.array": "Другие задачи, от которых зависит эта задача.", + "JsonSchema.tasks.presentation": "Настраивает панель, которая используется для представления выходных данных задачи и считывает входные данные задачи.", + "JsonSchema.tasks.presentation.echo": "Определяет, стоит ли отправлять выходные данные выполняемой команды на панель. Значение по умолчанию — true.", + "JsonSchema.tasks.presentation.focus": "Определяет, принимает ли панель фокус. По умолчанию — false. Если установлено значение true, панель также отображается.", + "JsonSchema.tasks.presentation.reveal.always": "Всегда открывать окно терминала при выполнении этой задачи.", + "JsonSchema.tasks.presentation.reveal.silent": "Отображать окно терминала только в том случае, если с этой задачей не связан сопоставитель проблем и при выполнении задачи возникли ошибки.", + "JsonSchema.tasks.presentation.reveal.never": "Никогда не открывать окно терминала при выполнении этой задачи.", + "JsonSchema.tasks.presentation.reveals": "Определяет, отображается ли панель, на которой запущена задача. Значение по умолчанию — \"always\".", + "JsonSchema.tasks.presentation.instance": "Определяет, является ли панель общей для нескольких задач, ограничена ли она только одной задачей или создается отдельно для каждого запуска задачи.", + "JsonSchema.tasks.terminal": "Свойство terminal является устаревшим. Используйте свойство presentation", + "JsonSchema.tasks.group.kind": "Группа выполнения задачи", + "JsonSchema.tasks.group.isDefault": "Определяет, является ли эта задача задачей по умолчанию в группе.", + "JsonSchema.tasks.group.defaultBuild": "Отмечает задачу как задачу сборки по умолчанию.", + "JsonSchema.tasks.group.defaultTest": "Отмечает задачу как задачу тестирования по умолчанию.", + "JsonSchema.tasks.group.build": "Отмечает задачу как задачу сборки, доступ к которой осуществляется с помощью команды \"Запустить задачу сборки\".", + "JsonSchema.tasks.group.test": "Отмечает задачу как задачу тестирования, доступ к которой осуществляется с помощью команды \"Запустить задачу тестирования\".", + "JsonSchema.tasks.group.none": "Отменяет связь задачи со всеми группами", + "JsonSchema.tasks.group": "Определяет, к какой группе выполнения принадлежит эта задача. Поддерживаемые значения: \"build\" для добавления задачи к группе сборки и \"test\" для добавления задачи к группе тестирования.", + "JsonSchema.tasks.type": "Определяет, выполняется ли задача в виде процесса или в виде команды оболочки. Значение по умолчанию — \"процесс\".", + "JsonSchema.version": "Номер версии конфигурации.", + "JsonSchema.tasks.identifier": "Пользовательский идентификатор задачи в файле launch.json или в предложении dependsOn.", + "JsonSchema.tasks.taskLabel": "Метка задачи", + "JsonSchema.tasks.taskName": "Имя задачи", + "JsonSchema.tasks.taskName.deprecated": "Свойство name задачи является устаревшим. Используйте свойство label.", + "JsonSchema.tasks.background": "Поддерживается ли выполняющаяся задача в работающем состоянии и исполняется ли она в фоновом режиме.", + "JsonSchema.tasks.promptOnClose": "Отображается ли для пользователя приглашение, если VS Code закрывается с выполняющейся задачей.", + "JsonSchema.tasks.matchers": "Используемые сопоставители проблем. Это может быть строка, определение сопоставителя проблем или массив строк и сопоставителей проблем.", + "JsonSchema.customizations.customizes.type": "Тип задачи, который будет изменен", + "JsonSchema.tasks.customize.deprecated": "Свойство customize является устаревшим. Сведения о том, как перейти на новый подход к изменению задач, см. в заметках о выпуске для версии 1.14.", + "JsonSchema.tasks.showOputput.deprecated": "Свойство showOutput является устаревшим. Используйте свойство reveal в свойстве presentation вместо этого свойства. Также см. заметки о выпуске для версии 1.14.", + "JsonSchema.tasks.echoCommand.deprecated": "Свойство echoCommand является устаревшим. Используйте свойство echo в свойстве presentation вместо этого свойства. Также см. заметки о выпуске для версии 1.14.", + "JsonSchema.tasks.suppressTaskName.deprecated": "Свойство suppressTaskName является устаревшим. Вместо использования этого свойства включите команду с аргументами в задачу. Также см. заметки о выпуске для версии 1.14.", + "JsonSchema.tasks.isBuildCommand.deprecated": "Свойство isBuildCommand является устаревшим. Используйте свойство group вместо этого свойства. Также см. заметки о выпуске для версии 1.14. ", + "JsonSchema.tasks.isTestCommand.deprecated": "Свойство isTestCommand является устаревшим. Используйте свойство group вместо этого свойства. Также см. заметки о выпуске для версии 1.14. ", + "JsonSchema.tasks.taskSelector.deprecated": "Свойство taskSelector является устаревшим. Вместо использования этого свойства включите команду с аргументами в задачу. Также см. заметки о выпуске для версии 1.14.", + "JsonSchema.windows": "Настройка команд Windows", + "JsonSchema.mac": "Настройка команд Mac", + "JsonSchema.linux": "Настройка команд Linux" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..8d681ea354 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "Задачи", + "ConfigureTaskRunnerAction.noWorkspace": "Задачи доступны только в папке рабочей области.", + "ConfigureTaskRunnerAction.quickPick.template": "Выбрать средство выполнения задач", + "ConfigureTaskRunnerAction.autoDetecting": "Автообнаружение задач для {0}", + "ConfigureTaskRunnerAction.autoDetect": "Не удалось автоматически определить систему задачи, используется шаблон по умолчанию. Подробности см. в выходных данных задачи.", + "ConfigureTaskRunnerAction.autoDetectError": "При определении системы задачи возникли ошибки. Дополнительные сведения см. в выходных данных задачи.", + "ConfigureTaskRunnerAction.failed": "Не удается создать файл tasks.json в папке .vscode. Подробности см. в выходных данных задачи.", + "ConfigureTaskRunnerAction.label": "Настроить средство выполнения задач", + "ConfigureBuildTaskAction.label": "Настроить задачу сборки", + "CloseMessageAction.label": "Закрыть", + "ShowTerminalAction.label": "Ознакомиться с терминалом", + "problems": "Проблемы", + "manyMarkers": "99+", + "runningTasks": "Показать выполняемые задачи", + "tasks": "Задачи", + "TaskSystem.noHotSwap": "Чтобы изменить подсистему выполнения задач, необходимо перезагрузить окно", + "TaskService.noBuildTask1": "Задача сборки не определена. Отметьте задачу с помощью \"isBuildCommand\" в файле tasks.json.", + "TaskService.noBuildTask2": "Задача сборки не определена. Отметьте задачу с помощью группы 'build' в файле tasks.json.", + "TaskService.noTestTask1": "Задача теста не определена. Отметьте задачу с помощью \"isTestCommand\" в файле tasks.json.", + "TaskService.noTestTask2": "Задача теста не определена. Отметьте задачу с помощью группы 'test' в файле tasks.json.", + "TaskServer.noTask": "Запрошенная задача {0} для выполнения не найдена.", + "TaskService.attachProblemMatcher.continueWithout": "Продолжить без проверки выходных данных задачи", + "TaskService.attachProblemMatcher.never": "Никогда не проверять выходные данные задачи", + "TaskService.attachProblemMatcher.learnMoreAbout": "Дополнительные сведения о проверке выходных данных задачи", + "selectProblemMatcher": "Выберите, на какие ошибки и предупреждения следует проверять выходные данные задачи", + "customizeParseErrors": "В конфигурации текущей задачи есть ошибки. Исправьте ошибки перед изменением задачи.", + "moreThanOneBuildTask": "В файле tasks.json определено несколько задач сборки. Выполняется первая задача.\n", + "TaskSystem.activeSame.background": "Задача '{0}' уже активна и находится в фоновом режиме. Чтобы завершить задачу, выберите \"Завершить задачу\" в меню \"Задачи\".", + "TaskSystem.activeSame.noBackground": "Задача '{0}' уже активна. Чтобы завершить задачу, выберите \"Завершить задачу\" из меню \"Задачи\".", + "TaskSystem.active": "Уже выполняется задача. Завершите ее, прежде чем выполнять другую задачу.", + "TaskSystem.restartFailed": "Не удалось завершить и перезапустить задачу {0}", + "TaskSystem.configurationErrors": "Ошибка: в конфигурации указанной задачи при проверке были выявлены ошибки, и ее невозможно использовать. Сначала устраните ошибки.", + "TaskSystem.invalidTaskJson": "Ошибка: в содержимом файла tasks.json есть синтаксические ошибки. Исправьте их, прежде чем выполнять задачу.\n", + "TaskSystem.runningTask": "Имеется выполняющаяся задача. Завершить ее?", + "TaskSystem.terminateTask": "&&Завершить задачу", + "TaskSystem.noProcess": "Запущенная задача больше не существует. Если задача породила фоновые процессы, выход из Visual Studio Code может привести к появлению потерянных процессов. Чтобы избежать этого, запустите последний фоновый процесс с флагом ожидания.", + "TaskSystem.exitAnyways": "&&Выйти", + "TerminateAction.label": "Завершить задачу", + "TaskSystem.unknownError": "При выполнении задачи произошла ошибка. Подробности см. в журнале задач.", + "TaskService.noWorkspace": "Задачи доступны только в папке рабочей области.", + "recentlyUsed": "недавно использованные задачи", + "configured": "настроенные задачи", + "detected": "обнаруженные задачи", + "TaskService.fetchingBuildTasks": "Получение задач сборки...", + "TaskService.noBuildTaskTerminal": "Задача сборки не найдена. Нажмите кнопку \"Настроить задачу сборки\", чтобы определить задачу сборки.", + "TaskService.pickBuildTask": "Выберите задачу сборки для запуска", + "TaskService.fetchingTestTasks": "Получение задач тестирования...", + "TaskService.noTestTaskTerminal": "Задача тестирования не найдена. Нажмите кнопку \"Настроить задачу тестирования\", чтобы определить задачу сборки. ", + "TaskService.pickTestTask": "Выберите задачу тестирования для запуска", + "TaskService.noTaskRunning": "Ни одной задачи не запущено.", + "TaskService.tastToTerminate": "Выберите задачи для завершения", + "TerminateAction.noProcess": "Запущенный процесс больше не существует. Если задача породила фоновые задачи, выход из Visual Studio Code может привести к появлению потерянных процессов.", + "TerminateAction.failed": "Не удалось завершить запущенную задачу", + "TaskService.noTaskToRestart": "Задачи для перезапуска не найдены.", + "TaskService.tastToRestart": "Выберите задачу для перезапуска", + "TaskService.defaultBuildTaskExists": "{0} уже помечена как задача сборки по умолчанию.", + "TaskService.pickDefaultBuildTask": "Выберите задачу, которая будет использоваться в качестве задачи сборки по умолчанию.", + "TaskService.defaultTestTaskExists": "{0} уже помечена как задача сборки по умолчанию. ", + "TaskService.pickDefaultTestTask": "Выберите задачу, которая будет использоваться в качестве задачи тестирования по умолчанию. ", + "TaskService.noTaskIsRunning": "Ни одной задачи не запущено.", + "TaskService.pickShowTask": "Выберите задачу, выходные данные для которой нужно отобразить", + "ShowLogAction.label": "Показать журнал задач", + "RunTaskAction.label": "Выполнить задачу", + "RestartTaskAction.label": "Перезапустить запущенную задачу", + "ShowTasksAction.label": "Показать выполняющиеся задачи", + "BuildAction.label": "Выполнить задачу сборки", + "TestAction.label": "Выполнить задачу тестирования", + "ConfigureDefaultBuildTask.label": "Настроить задачу сборки по умолчанию", + "ConfigureDefaultTestTask.label": "Настроить задачу тестирования по умолчанию", + "quickOpen.task": "Выполнить задачу" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..cd03385539 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "Задачи" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..ab75b57f91 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "При выполнении задачи произошла неизвестная ошибка. Подробности см. в журнале выходных данных задач.", + "TerminalTaskSystem.terminalName": "Задача — {0}", + "reuseTerminal": "Терминал будет повторно использоваться задачами. Чтобы закрыть его, нажмите любую клавишу.", + "TerminalTaskSystem": "Невозможно выполнить команду оболочки на диске UNC.", + "unkownProblemMatcher": "Не удается разрешить сопоставитель проблем {0}. Сопоставитель будет проигнорирован" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..63e35f9022 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "В результате выполнения команды gulp --tasks-simple не было выведено ни одной задачи. Выполнили ли вы команду npm install?", + "TaskSystemDetector.noJakeTasks": "В результате выполнения команды jake --tasks не было выведено ни одной задачи. Выполнили ли вы команду npm install?", + "TaskSystemDetector.noGulpProgram": "Gulp не установлен в вашей системе. Чтобы установить его, выполните команду npm install -g gulp.", + "TaskSystemDetector.noJakeProgram": "Jake не установлен в вашей системе. Чтобы установить его, выполните команду npm install -g jake.", + "TaskSystemDetector.noGruntProgram": "Grunt не установлен в вашей системе. Чтобы установить его, выполните команду npm install -g grunt.", + "TaskSystemDetector.noProgram": "Программа {0} не найдена. Сообщение: {1}", + "TaskSystemDetector.buildTaskDetected": "Обнаружена задача сборки \"{0}\".", + "TaskSystemDetector.testTaskDetected": "Обнаружена задача тестирования \"{0}\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..5843cb8a00 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "При выполнении задачи произошла неизвестная ошибка. Подробности см. в журнале выходных данных задач.", + "TaskRunnerSystem.watchingBuildTaskFinished": "\nОтслеживание задач сборки завершено.", + "TaskRunnerSystem.childProcessError": "Failed to launch external program {0} {1}.", + "TaskRunnerSystem.cancelRequested": "\nЗадача \"{0}\" была завершена по запросу пользователя.", + "unkownProblemMatcher": "Не удается разрешить сопоставитель проблем {0}. Сопоставитель будет проигнорирован" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..47af1d0825 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Предупреждение: options.cwd должен иметь тип string. Игнорируется значение {0}\n", + "ConfigurationParser.noargs": "Ошибка: аргументы команды должны представлять собой массив строк. Указанное значение:\n{0}", + "ConfigurationParser.noShell": "Предупреждение: конфигурация оболочки поддерживается только при выполнении задач в терминале.", + "ConfigurationParser.noName": "Ошибка: сопоставитель проблем в области объявления должен иметь имя:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Предупреждение: определен неизвестный сопоставитель проблем. Поддерживаемые типы: строка | СопоставительПроблем | (строка | СопоставительПроблем)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Ошибка: недопустимая ссылка на problemMatcher: {0}\n", + "ConfigurationParser.noTaskType": "Ошибка: в конфигурации задач должно присутствовать свойство типа. Конфигурация будет проигнорирована.\n{0}\n", + "ConfigurationParser.noTypeDefinition": "Ошибка: тип задачи '{0}' не зарегистрирован. Возможно, вы не установили расширение, которое предоставляет соответствующий поставщик задач.", + "ConfigurationParser.notCustom": "Ошибка: задачи не объявлены в качестве пользовательской задачи. Конфигурация будет проигнорирована.\n{0}\n", + "ConfigurationParser.noTaskName": "Ошибка: задачи должны предоставлять свойство taskName. Задача будет проигнорирована.\n{0}\n", + "taskConfiguration.shellArgs": "Предупреждение: задача \"{0}\" является командой оболочки, и имя команды или одного из ее аргументов включает пробелы без escape-последовательности. Чтобы обеспечить правильную расстановку кавычек в командной строке, объедините аргументы в команде.", + "taskConfiguration.noCommandOrDependsOn": "Ошибка: в задаче \"{0}\" не указаны ни команда, ни свойство dependsOn. Задача будет проигнорирована. Определение задачи:\n{1}", + "taskConfiguration.noCommand": "Ошибка: задача \"{0}\" не определяет команду. Задача будет игнорироваться. Ее определение:\n{1}", + "TaskParse.noOsSpecificGlobalTasks": "Версия задач 2.0.0 не поддерживает глобальные задачи для конкретных ОС. Преобразуйте их в задачи с помощью команд для конкретных ОС.\nЗатронутые задачи: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..c3cc3f739a --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0}, средство выбора терминалов", + "termCreateEntryAriaLabel": "{0}, создать новый терминал", + "'workbench.action.terminal.newplus": "$(plus) Создать новый интегрированный терминал", + "noTerminalsMatching": "Терминалы, соответствующие условию, отсутствуют", + "noTerminalsFound": "Открытые терминалы отсутствуют" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..9c376b2e70 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "Показать все открытые терминалы", + "terminalIntegratedConfigurationTitle": "Интегрированный терминал", + "terminal.integrated.shell.linux": "Путь оболочки, который используется терминалом в Linux.", + "terminal.integrated.shellArgs.linux": "Аргументы командной строки, которые следует использовать в терминале Linux.", + "terminal.integrated.shell.osx": "Путь оболочки, который используется терминалом в OS X.", + "terminal.integrated.shellArgs.osx": "Аргументы командной строки, которые следует использовать в терминале OS X.", + "terminal.integrated.shell.windows": "Путь к оболочке, который используется терминалом в Windows. Для оболочек, входящих в состав ОС Windows (cmd, PowerShell или Bash в Ubuntu).", + "terminal.integrated.shellArgs.windows": "Аргументы командной строки, используемые в терминале Windows.", + "terminal.integrated.rightClickCopyPaste": "Если задано, блокирует отображение контекстного меню при щелчке правой кнопкой мыши в терминале. Вместо этого будет выполняться копирование выбранного элемента и вставка в область, в которой нет выбранных элементов.", + "terminal.integrated.fontFamily": "Определяет семейство шрифтов терминала, значение по умолчанию — editor.fontFamily.", + "terminal.integrated.fontLigatures": "Определяет, будут ли включены лигатуры шрифтов для терминала.", + "terminal.integrated.fontSize": "Определяет размер шрифта (в пикселях) для терминала.", + "terminal.integrated.lineHeight": "Определяет высоту строки терминала; это число умножается на размер шрифта терминала, что дает фактическую высоту строки в пикселях.", + "terminal.integrated.enableBold": "Следует ли разрешить полужирный текст в терминале. Эта функция должна поддерживаться оболочкой терминала.", + "terminal.integrated.cursorBlinking": "Управляет миганием курсора терминала.", + "terminal.integrated.cursorStyle": "Определяет стиль курсора терминала.", + "terminal.integrated.scrollback": "Определяет предельное число строк в буфере терминала.", + "terminal.integrated.setLocaleVariables": "Управляет заданием переменных при запуске терминала, значение по умолчанию: \"True\" для OS X и \"False\" для других платформ.", + "terminal.integrated.cwd": "Путь явного запуска, по которому будет запущен терминал. Используется в качестве текущего рабочего каталога (cwd) для процесса оболочки. Это может быть особенно удобно в параметрах рабочей области, если корневой каталог не является подходящим каталогом cwd.", + "terminal.integrated.confirmOnExit": "Указывает, следует ли при выходе выводить подтверждение об имеющихся активных сеансах терминала.", + "terminal.integrated.commandsToSkipShell": "Набор идентификаторов команд, настраиваемые сочетания клавиш которых не будут передаваться в оболочку, а вместо этого будут всегда обрабатываться Code. Это позволяет использовать настраиваемые сочетания клавиш, которые при обычных условиях были бы использованы оболочкой и работали бы так же, как если бы терминал не имел фокуса, например клавиши CTRL+P запускали бы Quick Open.", + "terminal.integrated.env.osx": "Объект с переменными среды, которые будут добавлены к процессу VS Code для использования в терминале OS X", + "terminal.integrated.env.linux": "Объект с переменными среды, которые будут добавлены к процессу VS Code для использования в терминале Linux ", + "terminal.integrated.env.windows": "Объект с переменными среды, которые будут добавлены к процессу VS Code для использования в терминале Windows", + "terminal": "Терминал", + "terminalCategory": "Терминал", + "viewCategory": "Просмотреть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..935e311e7e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "Переключить интегрированный терминал", + "workbench.action.terminal.kill": "Завершить активный экземпляр терминала", + "workbench.action.terminal.kill.short": "Завершить работу терминала", + "workbench.action.terminal.quickKill": "Завершить работу экземпляра терминала", + "workbench.action.terminal.copySelection": "Скопировать выделение", + "workbench.action.terminal.selectAll": "Выбрать все", + "workbench.action.terminal.deleteWordLeft": "Удалить слово слева", + "workbench.action.terminal.deleteWordRight": "Удалить слово справа", + "workbench.action.terminal.new": "Создание нового интегрированного терминала", + "workbench.action.terminal.new.short": "Новый терминал", + "workbench.action.terminal.focus": "Фокус на терминале", + "workbench.action.terminal.focusNext": "Фокус на следующем терминале", + "workbench.action.terminal.focusAtIndex": "Фокус на терминале {0}", + "workbench.action.terminal.focusPrevious": "Фокус на предыдущем терминале", + "workbench.action.terminal.paste": "Вставить в активный терминал", + "workbench.action.terminal.DefaultShell": "Выбрать оболочку по умолчанию", + "workbench.action.terminal.runSelectedText": "Запуск выбранного текста в активном терминале", + "workbench.action.terminal.runActiveFile": "Запуск активного файла в активном терминале", + "workbench.action.terminal.runActiveFile.noFile": "Только файлы на диске можно запустить в терминале", + "workbench.action.terminal.switchTerminalInstance": "Переключить экземпляр терминала", + "workbench.action.terminal.scrollDown": "Прокрутить вниз (построчно)", + "workbench.action.terminal.scrollDownPage": "Прокрутить вниз (на страницу)", + "workbench.action.terminal.scrollToBottom": "Прокрутить до нижней границы", + "workbench.action.terminal.scrollUp": "Прокрутить вверх (построчно)", + "workbench.action.terminal.scrollUpPage": "Прокрутить вверх (страницу)", + "workbench.action.terminal.scrollToTop": "Прокрутить до верхней границы", + "workbench.action.terminal.clear": "Очистить", + "workbench.action.terminal.allowWorkspaceShell": "Разрешить настройку оболочки в рабочей области", + "workbench.action.terminal.disallowWorkspaceShell": "Запретить настройку оболочки в рабочей области", + "workbench.action.terminal.rename": "Переименовать", + "workbench.action.terminal.rename.prompt": "Введите название терминала", + "workbench.action.terminal.focusFindWidget": "Выделить мини-приложение поиска", + "workbench.action.terminal.hideFindWidget": "Скрыть мини-приложение поиска", + "nextTerminalFindTerm": "Показать следующий найденный термин", + "previousTerminalFindTerm": "Показать предыдущий найденный термин", + "quickOpenTerm": "Терминал: переключить активный терминал" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..c984df39aa --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "Цвет фона терминала. С его помощью можно указать цвет терминала, отличный от цвета панели.", + "terminal.foreground": "Цвет переднего плана терминала.", + "terminalCursor.foreground": "Цвет переднего плана курсора терминала.", + "terminalCursor.background": "Цвет фона курсора терминала. Позволяет выбрать цвет символа, который перекрывается блочным курсором.", + "terminal.ansiColor": "Цвет ANSI \"{0}\" в терминале." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..dffdc99d5e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "Вы хотите разрешить запуск {0} (определяется как параметр рабочей области) в терминале?", + "allow": "Allow", + "disallow": "Disallow" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..83ef316f91 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Найти", + "placeholder.find": "Найти", + "label.previousMatchButton": "Предыдущее соответствие", + "label.nextMatchButton": "Следующее соответствие", + "label.closeButton": "Закрыть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..aec452d0a2 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "В терминале отсутствует выделенный текст для копирования", + "terminal.integrated.exitedWithCode": "Процесс терминала завершен с кодом выхода: {0}", + "terminal.integrated.waitOnExit": "Нажмите любую клавишу, чтобы закрыть терминал.", + "terminal.integrated.launchFailed": "Не удалось запустить команду процесса терминала \"{0}{1}\" (код выхода: {2})" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..99c948b6e8 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "Щелкните с нажатой клавишей ALT, чтобы перейти по ссылке.", + "terminalLinkHandler.followLinkCmd": "Щелкните с нажатой клавишей Cmd, чтобы перейти по ссылке", + "terminalLinkHandler.followLinkCtrl": "Щелкните с нажатой клавишей Ctrl, чтобы перейти по ссылке" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..8f8d1c8583 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "Копировать", + "createNewTerminal": "Новый терминал", + "paste": "Вставить", + "selectAll": "Выбрать все", + "clear": "Очистить" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..f03ba1634e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "Вы можете изменить оболочку терминала по умолчанию, нажав кнопку \"Настроить\".", + "customize": "Настроить", + "cancel": "Отмена", + "never again": "ОК. Больше не показывать", + "terminal.integrated.chooseWindowsShell": "Выберите предпочитаемую оболочку терминала. Ее можно позже изменить в параметрах", + "terminalService.terminalCloseConfirmationSingular": "Есть активный сеанс терминала, завершить его?", + "terminalService.terminalCloseConfirmationPlural": "Есть несколько активных сеансов терминала ({0}), завершить их?", + "yes": "Да" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..1f6834b46c --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "Цветовая тема", + "installColorThemes": "Установить дополнительные цветовые темы...", + "themes.selectTheme": "Выберите цветовую тему (используйте клавиши стрелок вверх и вниз для предварительного просмотра)", + "selectIconTheme.label": "Тема значков файлов", + "installIconThemes": "Установить дополнительные темы значков файлов...", + "noIconThemeLabel": "Нет", + "noIconThemeDesc": "Отключить значки файлов", + "problemChangingIconTheme": "Проблема при задании темы значка: {0}", + "themes.selectIconTheme": "Выбрать тему значка файла", + "generateColorTheme.label": "Создать цветовую тему на основе текущих параметров", + "preferences": "Параметры", + "developer": "Разработчик" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..a122624d0b --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "Эта рабочая область содержит параметры, которые можно задать только в параметрах пользователя. ({0})", + "openWorkspaceSettings": "Открыть параметры рабочей области", + "openDocumentation": "Подробнее", + "ignore": "Игнорировать" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/rus/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..bfdbd6f078 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "Заметки о выпуске: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..c7df812156 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "Заметки о выпуске", + "updateConfigurationTitle": "Обновить", + "updateChannel": "Настройте канал обновления, по которому вы будете получать обновления. После изменения значения необходим перезапуск." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..311884ac4f --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "Обновить сейчас", + "later": "Позже", + "unassigned": "не присвоено", + "releaseNotes": "Заметки о выпуске", + "showReleaseNotes": "Показать заметки о выпуске", + "downloadNow": "Скачать сейчас", + "read the release notes": "Вас приветствует {0} v{1}! Вы хотите прочитать заметки о выпуске?", + "licenseChanged": "Условия использования лицензии изменились, ознакомьтесь с ними.", + "license": "Прочитать условия лицензии", + "neveragain": "Больше не показывать", + "64bitisavailable": "{0} для 64-разрядной версии Windows теперь доступен!", + "learn more": "Дополнительные сведения", + "updateIsReady": "Доступно новое обновление {0}.", + "thereIsUpdateAvailable": "Доступно обновление.", + "updateAvailable": "{0} будет обновлен после перезапуска.", + "noUpdatesAvailable": "В настоящее время нет доступных обновлений.", + "commandPalette": "Палитра команд...", + "settings": "Параметры", + "keyboardShortcuts": "Сочетания клавиш", + "selectTheme.label": "Цветовая тема", + "themes.selectIconTheme.label": "Тема значков файлов", + "not available": "Обновления недоступны", + "checkingForUpdates": "Идет проверка наличия обновлений...", + "DownloadUpdate": "Скачать доступное обновление", + "DownloadingUpdate": "Скачивается обновление...", + "InstallingUpdate": "Идет установка обновления...", + "restartToUpdate": "Перезапустить для обновления...", + "checkForUpdates": "Проверить наличие обновлений..." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/rus/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..dac09ddaa5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "Действий: {0}", + "hideView": "Скрыть из боковой панели" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/rus/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..de3c4d9267 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "представления должны быть массивом", + "requirestring": "свойство \"{0}\" является обязательным и должно иметь тип string", + "optstring": "свойство \"{0}\" может быть опущено или должно иметь тип string", + "vscode.extension.contributes.view.id": "Идентификатор представления. Используйте его для регистрации поставщика данных с помощью API-интерфейса \"vscode.window.registerTreeDataProviderForView\", а также для активации расширения с помощью регистрации события \"onView:${id}\" в \"activationEvents\".", + "vscode.extension.contributes.view.name": "Понятное имя представления. Будет отображаться на экране", + "vscode.extension.contributes.view.when": "Условие, которое должно иметь значение 'true', чтобы отображалось это представление", + "vscode.extension.contributes.views": "Добавляет представления в редактор", + "views.explorer": "Представление проводника", + "views.debug": "Представление отладки", + "locationId.invalid": "\"{0}\" не является допустимым расположением представления" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/rus/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..88836e3ad5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "Показать все команды", + "watermark.quickOpen": "Перейти к файлу", + "watermark.openFile": "Открыть файл", + "watermark.openFolder": "Открыть папку", + "watermark.openFileFolder": "Открыть файл или папку", + "watermark.openRecent": "Открыть последние", + "watermark.newUntitledFile": "Новый файл без имени", + "watermark.toggleTerminal": "Терминал", + "watermark.findInFiles": "Найти в файлах", + "watermark.startDebugging": "Начать отладку", + "watermark.unboundCommand": "свободный", + "workbenchConfigurationTitle": "Рабочее место", + "tips.enabled": "Если параметр включен, на подложке появляются советы, если нет открытых редакторов." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/rus/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..cec1aad9b5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "Проводник", + "welcomeOverlay.search": "Поиск по файлам", + "welcomeOverlay.git": "Управление исходным кодом", + "welcomeOverlay.debug": "Запуск и отладка", + "welcomeOverlay.extensions": "Управление расширениями", + "welcomeOverlay.problems": "Просмотр ошибок и предупреждений", + "welcomeOverlay.commandPalette": "Найти и выполнить все команды.", + "welcomeOverlay": "Обзор пользовательского интерфейса", + "hideWelcomeOverlay": "Скрыть наложение интерфейса", + "help": "Справка" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..5265062f94 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "Улучшенное редактирование", + "welcomePage.start": "Запустить", + "welcomePage.newFile": "Создать файл", + "welcomePage.openFolder": "Открыть папку...", + "welcomePage.cloneGitRepository": "Клонировать репозиторий Git...", + "welcomePage.recent": "Последние", + "welcomePage.moreRecent": "Дополнительные сведения...", + "welcomePage.noRecentFolders": "Нет последних папок.", + "welcomePage.help": "Справка", + "welcomePage.keybindingsCheatsheet": "Список сочетаний клавиш в печатном виде", + "welcomePage.introductoryVideos": "Вступительные видео", + "welcomePage.tipsAndTricks": "Советы и рекомендации", + "welcomePage.productDocumentation": "Документация по продукту", + "welcomePage.gitHubRepository": "Репозиторий GitHub", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "Отображать страницу приветствия при запуске", + "welcomePage.customize": "Настроить", + "welcomePage.installExtensionPacks": "Средства и языки", + "welcomePage.installExtensionPacksDescription": "Установить поддержку для {0} и {1}", + "welcomePage.moreExtensions": "Еще", + "welcomePage.installKeymapDescription": "Установка сочетаний клавиш", + "welcomePage.installKeymapExtension": "Настроить сочетания клавиш для {0} и {1}", + "welcomePage.others": "Другие", + "welcomePage.colorTheme": "Цветовая тема", + "welcomePage.colorThemeDescription": "Настройте редактор и код удобным образом.", + "welcomePage.learn": "Подробнее", + "welcomePage.showCommands": "Найти и выполнить все команды.", + "welcomePage.showCommandsDescription": "Быстро обращайтесь к командам и выполняйте поиск по командам с помощью палитры команд ({0})", + "welcomePage.interfaceOverview": "Общие сведения об интерфейсе", + "welcomePage.interfaceOverviewDescription": "Используйте визуальное наложение с выделением основных компонентов пользовательского интерфейса.", + "welcomePage.deployToAzure": "Развертывание приложений в облако", + "welcomePage.deployToAzureDescription": "Узнайте, как развернуть приложения Node в службу приложений Azure", + "welcomePage.interactivePlayground": "Интерактивная площадка", + "welcomePage.interactivePlaygroundDescription": "Опробуйте основные функции редактора в ходе краткого пошагового руководства." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..7af109b782 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "Рабочее место", + "workbench.startupEditor.none": "Запустить без редактора.", + "workbench.startupEditor.welcomePage": "Откройте страницу приветствия (по умолчанию).", + "workbench.startupEditor.newUntitledFile": "Открыть новый файл без названия.", + "workbench.startupEditor": "Определяет, какой редактор отображается при запуске, если редактор не был восстановлен из предыдущего сеанса. Укажите 'none', чтобы не запускать редактор, 'welcomePage', чтобы открыть страницу приветствия (по умолчанию), и 'newUntitledFile', чтобы открыть новый файл без названия (только при открытии пустой рабочей области).", + "help": "Справка" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..c0747c5687 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "Добро пожаловать", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "Показать расширения Azure", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "Поддержка {0} уже добавлена.", + "welcomePage.willReloadAfterInstallingExtensionPack": "После установки дополнительной поддержки для {0} окно будет перезагружено.", + "welcomePage.installingExtensionPack": "Установка дополнительной поддержки для {0}...", + "welcomePage.extensionPackNotFound": "Не удается найти поддержку для {0} с идентификатором {1}.", + "welcomePage.keymapAlreadyInstalled": "Сочетания клавиш {0} уже установлены.", + "welcomePage.willReloadAfterInstallingKeymap": "Окно перезагрузится после установки сочетаний клавиш {0}.", + "welcomePage.installingKeymap": "Устанавливаются сочетания клавиш {0}...", + "welcomePage.keymapNotFound": "Не удалось найти сочетания клавиш {0} с идентификатором {1}.", + "welcome.title": "Добро пожаловать", + "welcomePage.openFolderWithPath": "Открыть папку {0} с путем {1}", + "welcomePage.extensionListSeparator": ",", + "welcomePage.installKeymap": "Установить раскладку клавиатуры {0}", + "welcomePage.installExtensionPack": "Установить дополнительную поддержку для {0}", + "welcomePage.installedKeymap": "Раскладка клавиатуры {0} уже установлена", + "welcomePage.installedExtensionPack": "Поддержка {0} уже установлена", + "ok": "ОК", + "details": "Подробности", + "cancel": "Отмена", + "welcomePage.buttonBackground": "Цвет фона кнопок на странице приветствия.", + "welcomePage.buttonHoverBackground": "Цвет фона при наведении указателя для кнопок на странице приветствия." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..0b737bb3cf --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "Интерактивная площадка", + "editorWalkThrough": "Интерактивная площадка" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..87e8284b75 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "Интерактивная площадка", + "help": "Справка", + "interactivePlayground": "Интерактивная площадка" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..53a6a18e69 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "Прокрутить вверх (построчно)", + "editorWalkThrough.arrowDown": "Прокрутить вниз (построчно)", + "editorWalkThrough.pageUp": "Прокрутить вверх (на страницу)", + "editorWalkThrough.pageDown": "Прокрутить вниз (страницу)" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..2407c5eb91 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "свободный", + "walkThrough.gitNotFound": "Похоже, Git не установлен в вашей системе.", + "walkThrough.embeddedEditorBackground": "Цвет фона встроенных редакторов для интерактивных площадок." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/rus/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..1bbf2189fe --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "Добавляет параметры конфигурации.", + "vscode.extension.contributes.configuration.title": "Краткая сводка параметров. Эта метка будет использоваться в файле параметров в качестве разделяющего комментария.", + "vscode.extension.contributes.configuration.properties": "Описание свойств конфигурации.", + "scope.window.description": "Конфигурация окна, которая может быть задана в параметрах пользователя или рабочей области.", + "scope.resource.description": "Конфигурации ресурсов, которые могут быть заданы в параметрах пользователей, рабочих областей или папок.", + "scope.description": "Область, в которой применяется конфигурация. Доступные области — 'window' и 'resource'.", + "invalid.type": "Если тип configuration.type задан, то он должен иметь значение object", + "invalid.title": "configuration.title должно быть строкой", + "vscode.extension.contributes.defaultConfiguration": "Предоставляет параметры конфигурации редактора по умолчанию в соответствии с языком.", + "invalid.properties": "configuration.properties должно быть объектом", + "workspaceConfig.folders.description": "Список папок, которые будут загружены в рабочую область. Необходимо указывать пути к файлам, например, \"/root/folderA\" или \"./folderA\" для пути по отношению к файлу рабочей области.", + "workspaceConfig.folder.description": "Путь к файлу, например, \"/root/folderA\" или \"./folderA\" для пути по отношению к файлу рабочей области.", + "workspaceConfig.settings.description": "Параметры рабочей области" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/rus/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..db281e5d51 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Открыть параметры", + "close": "Закрыть", + "saveAndRetry": "Сохранить параметры и повторить попытку", + "errorUnknownKey": "Не удалось записать в {0}, так как {1} не является зарегистрированной конфигурацией.", + "errorInvalidFolderConfiguration": "Не удается изменить параметры папок, так как {0} не поддерживает область ресурсов папок. ", + "errorInvalidUserTarget": "Не удается изменить параметры пользователей, так как {0} не поддерживает глобальную область.", + "errorInvalidFolderTarget": "Не удается изменить параметры папок, так как ресурс не указан.", + "errorNoWorkspaceOpened": "Не удается записать в {0}, так как не открыта ни одна рабочая область. Откройте рабочую область и повторите попытку.", + "errorInvalidConfiguration": "Не удается записать параметры. Откройте файл **User Settings**, устраните ошибки и предупреждения в файле и повторите попытку.", + "errorInvalidConfigurationWorkspace": "Не удается записать параметры. Откройте файл **Workspace Settings**, устраните ошибки и предупреждения в файле и повторите попытку.", + "errorConfigurationFileDirty": "Не удается записать параметры, так как файл был изменен. Сохраните файл **User Settings** и повторите попытку.", + "errorConfigurationFileDirtyWorkspace": "Не удается записать параметры, так как файл был изменен. Сохраните файл **Workspace Settings** и повторите попытку.", + "userTarget": "Параметры пользователя", + "workspaceTarget": "Параметры рабочей области", + "folderTarget": "Параметры папок" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/rus/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..6c55f214fa --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "Не удается выполнить запись в файл. Откройте файл, исправьте ошибки и предупреждения в файле и повторите попытку.", + "errorFileDirty": "Не удается записать сведения в файл, так как файл был изменен. Сохраните файл и повторите попытку." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/rus/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..7cc8a5da01 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Телеметрия", + "telemetry.enableCrashReporting": "Разрешить отправку отчетов о сбоях в Майкрософт.\nЧтобы этот параметр вступил в силу, требуется перезагрузка." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/rus/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..ab83c9ec03 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "Хост-процесс для расширений не был запущен в течение 10 секунд. Возможно, он был остановлен в первой строке, а для продолжения требуется отладчик.", + "extensionHostProcess.startupFail": "Хост-процесс для расширений не запустился спустя 10 секунд. Возможно, произошла ошибка.", + "extensionHostProcess.error": "Ошибка в хост-процессе для расширений: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..84bc156764 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "Не удалось проанализировать {0}: {1}.", + "fileReadFail": "Не удается прочитать файл {0}: {1}.", + "jsonsParseFail": "Не удалось проанализировать {0} или {1}: {2}.", + "missingNLSKey": "Не удалось найти сообщение для ключа {0}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..6e3094750f --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "Средства разработчика", + "restart": "Перезапустить хост-процесс для расширений", + "extensionHostProcess.crash": "Хост-процесс для расширений неожиданно завершил работу.", + "extensionHostProcess.unresponsiveCrash": "Работа хост-процесса для расширений была завершена, так как он перестал отвечать на запросы.", + "overwritingExtension": "Идет перезапись расширения {0} на {1}.", + "extensionUnderDevelopment": "Идет загрузка расширения разработки в {0}." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/rus/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..0c24b460cf --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "Требуется платформа Microsoft .NET Framework 4.5. Нажмите ссылку, чтобы установить ее.", + "installNet": "Скачать .NET Framework 4.5", + "neverShowAgain": "Больше не показывать", + "trashFailed": "Не удалось переместить \"{0}\" в корзину." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/rus/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..178a48191f --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "Недопустимый ресурс файла ({0})", + "fileIsDirectoryError": "Файл является каталогом ({0})", + "fileNotModifiedError": "undefined", + "fileTooLargeError": "Не удается открыть файл, так как он имеет слишком большой размер", + "fileBinaryError": "Похоже, файл является двоичным, и его нельзя открыть как текстовый.", + "fileNotFoundError": "Файл не найден ({0})", + "fileMoveConflict": "Невозможно переместить или скопировать файл, так как он уже существует в папке назначения.", + "unableToMoveCopyError": "Невозможно переместить или скопировать файл, так как он заменил бы папку, в которой содержится.", + "foldersCopyError": "Папки нельзя копировать в рабочую область. Выберите отдельные файлы, чтобы скопировать их.", + "fileModifiedError": "Файл изменен с", + "fileReadOnlyError": "Файл доступен только для чтения" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/rus/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..f98bde5db7 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "Не удалось записать данные, так как это файл-черновик. Сохраните файл **настраиваемых сочетаний клавиш** и повторите попытку.", + "parseErrors": "Не удалось записать настраиваемые сочетания клавиш. Откройте файл **настраиваемых сочетаний клавиш**, чтобы исправить ошибки и предупреждения в файле, и повторите попытку.", + "errorInvalidConfiguration": "Не удалось записать настраиваемые сочетания клавиш. **Файл настраиваемых сочетаний клавиш** содержит объект, не являющийся типом Array. Откройте файл, чтобы очистить его, и повторите попытку.", + "emptyKeybindingsHeader": "Поместите настраиваемые сочетания клавиш в этот файл, чтобы перезаписать клавиши по умолчанию." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/rus/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..343599358b --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "требуется непустое значение.", + "requirestring": "свойство \"{0}\" является обязательным и должно иметь тип string", + "optstring": "свойство \"{0}\" может быть опущено или должно иметь тип string", + "vscode.extension.contributes.keybindings.command": "Идентификатор команды, выполняемой при нажатии настраиваемого сочетания клавиш.", + "vscode.extension.contributes.keybindings.key": "Клавиша или последовательность клавиш (клавиши разделяются знаком плюс, а последовательности — пробелом, например CTRL+O или CTRL+L L для сочетания).", + "vscode.extension.contributes.keybindings.mac": "Клавиша или последовательность клавиш для Mac.", + "vscode.extension.contributes.keybindings.linux": "Клавиша или последовательность клавиш для Linux.", + "vscode.extension.contributes.keybindings.win": "Клавиша или последовательность клавиш для Windows.", + "vscode.extension.contributes.keybindings.when": "Условие, когда клавиша нажата.", + "vscode.extension.contributes.keybindings": "Добавляет настраиваемые сочетания клавиш.", + "invalid.keybindings": "Недопустимое значение \"contributes.{0}\": {1}", + "unboundCommands": "Доступные команды: ", + "keybindings.json.title": "Настройка настраиваемых сочетаний клавиш", + "keybindings.json.key": "Клавиша или последовательность клавиш (через пробел)", + "keybindings.json.command": "Имя выполняемой команды", + "keybindings.json.when": "Условие, когда клавиша нажата.", + "keybindings.json.args": "Аргументы, передаваемые в выполняемую команду.", + "keyboardConfigurationTitle": "Клавиатура", + "dispatch": "Управляет логикой диспетчеризации для нажатий клавиш \"keydown.code\" (рекомендуется) или \"keydown.keyCode\"." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/rus/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..60f1479e54 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Ошибка: {0}", + "alertWarningMessage": "Предупреждение: {0}", + "alertInfoMessage": "Сведения: {0}", + "error": "Ошибка", + "warning": "Предупреждение", + "info": "Сведения", + "close": "Закрыть" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/rus/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..f04a4fb74d --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "&&Да", + "cancelButton": "Отмена" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/rus/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..9599139b29 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Добавляет объявления языка.", + "vscode.extension.contributes.languages.id": "Идентификатор языка.", + "vscode.extension.contributes.languages.aliases": "Псевдонимы имен для языка.", + "vscode.extension.contributes.languages.extensions": "Расширения имен файлов, связанные с языком.", + "vscode.extension.contributes.languages.filenames": "Имена файлов, связанные с языком.", + "vscode.extension.contributes.languages.filenamePatterns": "Стандартные маски имен файлов, связанные с языком.", + "vscode.extension.contributes.languages.mimetypes": "Типы MIME, связанные с языком.", + "vscode.extension.contributes.languages.firstLine": "Регулярное выражение, соответствующее первой строке файла языка.", + "vscode.extension.contributes.languages.configuration": "Относительный путь к файлу, содержащему параметры конфигурации для языка.", + "invalid": "Недопустимое значение contributes.{0}. Требуется массив.", + "invalid.empty": "Пустое значение contributes.{0}", + "require.id": "свойство \"{0}\" является обязательным и должно иметь тип string", + "opt.extensions": "свойство \"{0}\" может быть опущено и должно иметь тип string[]", + "opt.filenames": "свойство \"{0}\" может быть опущено и должно иметь тип string[]", + "opt.firstLine": "свойство \"{0}\" может быть опущено и должно иметь тип string", + "opt.configuration": "свойство \"{0}\" может быть опущено и должно иметь тип string", + "opt.aliases": "свойство \"{0}\" может быть опущено и должно иметь тип string[]", + "opt.mimetypes": "свойство \"{0}\" может быть опущено и должно иметь тип string[]" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/rus/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..441530a01b --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1}", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/rus/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..85c2289ef9 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "Добавляет разметчики TextMate.", + "vscode.extension.contributes.grammars.language": "Идентификатор языка, для которого добавляется этот синтаксис.", + "vscode.extension.contributes.grammars.scopeName": "Имя области TextMate, используемое в файле tmLanguage.", + "vscode.extension.contributes.grammars.path": "Путь к файлу tmLanguage. Путь указывается относительно папки расширения и обычно начинается с \"./syntaxes/\".", + "vscode.extension.contributes.grammars.embeddedLanguages": "Сопоставление имени области и идентификатора языка, если грамматика содержит внедренные языки.", + "vscode.extension.contributes.grammars.injectTo": "Список имен языковых областей, в которые вставляется эта грамматика." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/rus/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..ddef6e66b8 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "Неизвестный язык в contributes.{0}.language. Указанное значение: {1}", + "invalid.scopeName": "В contributes.{0}.scopeName требуется строка. Указанное значение: {1}", + "invalid.path.0": "В contributes.{0}.path требуется строка. Указанное значение: {1}", + "invalid.injectTo": "Недопустимое значение в \"contributes.{0}.injectTo\". Должен быть задан массив имен языковых областей. Указанное значение: {1}", + "invalid.embeddedLanguages": "Недопустимое значение в \"contributes.{0}.embeddedLanguages\". Оно должно быть сопоставлением объекта между именем области и языком. Указанное значение: {1}.", + "invalid.path.1": "contributes.{0}.path ({1}) должен был быть включен в папку расширения ({2}). Это может сделать расширение непереносимым.", + "no-tm-grammar": "Нет грамматики TM, зарегистрированной для этого языка." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/rus/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..968f76fc9e --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "Файл изменен. Сохраните его, прежде чем открыть его вновь в другой кодировке.", + "genericSaveError": "Не удалось сохранить \"{0}\": {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/rus/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..7f7c0248bf --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "Не удалось выполнить резервное копирование файлов (ошибка: {0}). Попробуйте сохранить файлы, чтобы выйти." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/rus/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..ed91a9a3c3 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "Сохранить изменения, внесенные в {0}?", + "saveChangesMessages": "Сохранить изменения в указанных файлах ({0})?", + "moreFile": "...1 дополнительный файл не показан", + "moreFiles": "...не показано дополнительных файлов: {0}", + "saveAll": "&&Сохранить все", + "save": "&&Сохранить", + "dontSave": "&&Не сохранять", + "cancel": "Отмена", + "saveChangesDetail": "Если не сохранить изменения, они будут утеряны.", + "allFiles": "Все файлы", + "noExt": "Нет расширений" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/rus/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..72497d16d4 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "Цвета и стили для маркера.", + "schema.token.foreground": "Цвет переднего плана для маркера.", + "schema.token.fontStyle": "Начертание шрифта для правила: один либо сочетание курсива, полужирного и подчеркивания.", + "schema.fontStyle.error": "Стиль шрифта должен представлять собой сочетание свойств 'italic', 'bold' и 'underline'", + "schema.properties.name": "Описание правила.", + "schema.properties.scope": "Переключатель области, для которой проверяется это правило.", + "schema.tokenColors.path": "Путь к файлу tmTheme (относительно текущего файла).", + "schema.colors": "Цвета для выделения синтаксических конструкций" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/rus/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..fabcd870fc --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "Значок папки для развернутых папок. Значок развернутой папки используется по желанию. Если он не задан, будет отображаться значок, заданный для папки.", + "schema.folder": "Значок папки для свернутых папок, а также если не задан параметр folderExpanded для развернутых папок.", + "schema.file": "Значок файла по умолчанию, отображаемый для всех файлов, которые не соответствуют известному расширению, имени файла или коду языка.", + "schema.folderNames": "Сопоставляет имена папок со значками. Ключ объекта — имя папки, не включая сегменты пути. Не допускается использование шаблонов или подстановочных знаков. Имена папок сопоставляются без учета регистра.", + "schema.folderName": "Идентификатор определения значка для сопоставления.", + "schema.folderNamesExpanded": "Сопоставляет имена папок со значками для развернутых папок. Ключ объекта — имя папки, не включая сегменты пути. Не допускается использование шаблонов или подстановочных знаков. Имена папок сопоставляются без учета регистра.", + "schema.folderNameExpanded": "Идентификатор определения значка для сопоставления.", + "schema.fileExtensions": "Сопоставляет расширения файлов со значками. Ключ объекта — имя расширения файла. Имя расширения представляет собой последний сегмент имени файла после последней точки (не включая точку). Расширения сопоставляются без учета регистра.", + "schema.fileExtension": "Идентификатор определения значка для сопоставления.", + "schema.fileNames": "Сопоставляет имена файлов со значками. Ключ объекта — полное имя файла, не включая сегменты пути. Имя файла может содержать точки и возможное расширение файла. Не допускается использование шаблонов или подстановочных знаков. Имена файлов сопоставляются без учета регистра.", + "schema.fileName": "Идентификатор определения значка для сопоставления.", + "schema.languageIds": "Сопоставляет языки и значки. Ключ объекта — идентификатор языка, как определено в точке публикации для языка.", + "schema.languageId": "Идентификатор определения значка для сопоставления.", + "schema.fonts": "Шрифты, используемые в определениях значков.", + "schema.id": "Идентификатор шрифта.", + "schema.src": "Расположения шрифта.", + "schema.font-path": "Путь к шрифту относительно текущего файла темы значка.", + "schema.font-format": "Формат шрифта.", + "schema.font-weight": "Насыщенность шрифта.", + "schema.font-sstyle": "Стиль шрифта.", + "schema.font-size": "Размер шрифта по умолчанию.", + "schema.iconDefinitions": "Описание всех значков, которые можно использовать при сопоставлении файлов и значков.", + "schema.iconDefinition": "Определение значка. Ключ объекта — идентификатор определения.", + "schema.iconPath": "При использовании SVG или PNG: путь к изображению. Путь задается относительно файла набора значков.", + "schema.fontCharacter": "При использовании шрифта с глифами: используемый символ в шрифте.", + "schema.fontColor": "При использовании шрифта с глифами: используемый цвет.", + "schema.fontSize": "При использовании шрифта: размер шрифта в процентах от шрифта текста. Если не задан, по умолчанию используется размер в определении шрифта.", + "schema.fontId": "При использовании шрифта: идентификатор шрифта. Если не задан, по умолчанию используется первое определение шрифта.", + "schema.light": "Дополнительные сопоставления для значков файлов в светлых цветных темах.", + "schema.highContrast": "Дополнительные сопоставления для значков файлов в контрастных цветных темах." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/rus/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..0a94145158 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "Возникли проблемы при анализе файла JSON THEME: {0}.", + "error.invalidformat.colors": "Ошибка при анализе файла цветовой темы: {0}. Свойство 'colors' не имеет тип 'object'.", + "error.invalidformat.tokenColors": "Ошибка при анализе файла цветовой темы: {0}. Свойство 'tokenColors' должно содержать массив цветов или путь к файлу темы TextMate", + "error.plist.invalidformat": "Ошибка при анализе файла tmTheme: {0}. 'settings' не является массивом.", + "error.cannotparse": "Ошибка при анализе файла tmTheme: {0}", + "error.cannotload": "Ошибка при загрузке файла tmTheme {0}: {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/rus/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..ddc1365a2b --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "Идентификатор темы значка, как используется в параметрах пользователя.", + "vscode.extension.contributes.themes.label": "Метка цветовой схемы, отображаемая в пользовательском интерфейсе.", + "vscode.extension.contributes.themes.uiTheme": "Базовая тема, определяющая цвета оформления редактора: \"vs\" — светлая цветовая тема, \"vs-dark\" — темная цветовая тема. \"hc-black\" — темная высококонтрастная тема.", + "vscode.extension.contributes.themes.path": "Путь к файлу tmTheme. Путь указывается относительно папки расширения и имеет вид \"./themes/themeFile.tmTheme\".", + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "Идентификатор темы значка, как используется в параметрах пользователя.", + "vscode.extension.contributes.iconThemes.label": "Метка темы значка, как отображается в пользовательском интерфейсе.", + "vscode.extension.contributes.iconThemes.path": "Путь к файлу определения темы значка. Путь задается относительно папки расширения и, как правило, имеет следующий вид: \"./icons/awesome-icon-theme.json\".", + "migration.completed": "В параметры пользователя были добавлены новые параметры темы. Резервная копия доступна в {0}.", + "error.cannotloadtheme": "Unable to load {0}: {1}", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "В contributes.{0}.path требуется строка. Указанное значение: {1}", + "invalid.path.1": "contributes.{0}.path ({1}) должен был быть включен в папку расширения ({2}). Это может сделать расширение непереносимым.", + "reqid": "Ожидалась строка в \"contributes.{0}.id\". Указанное значение: {1}", + "error.cannotloadicontheme": "Unable to load {0}", + "error.cannotparseicontheme": "Problems parsing file icons file: {0}", + "colorTheme": "Specifies the color theme used in the workbench.", + "colorThemeError": "Theme is unknown or not installed.", + "iconTheme": "Указывает тему значков, используемую в рабочей области. Чтобы значки файлов не отображались, используйте значение 'null'.", + "noIconThemeDesc": "No file icons", + "iconThemeError": "File icon theme is unknown or not installed.", + "workbenchColors": "Переопределяет цвета из выбранной цветовой темы.", + "workbenchColors.deprecated": "Параметр больше не является экспериментальным и был переименован в 'workbench.colorCustomizations'", + "workbenchColors.deprecatedDescription": "Используйте параметр 'workbench.colorCustomizations'", + "editorColors": "Переопределяет цвета редактора и стиль шрифта из текущей выбранной цветовой темы.", + "editorColors.comments": "Задает цвета и стили для комментариев", + "editorColors.strings": "Задает цвета и стили для строковых литералов.", + "editorColors.keywords": "Задает цвета и стили для ключевых слов.", + "editorColors.numbers": "Задает цвета и стили для числовых литералов. ", + "editorColors.types": "Задает цвета и стили для объявлений типов и ссылок. ", + "editorColors.functions": "Задает цвета и стили для объявлений функций и ссылок. ", + "editorColors.variables": "Задает цвета и стили для объявлений переменных и для ссылок. ", + "editorColors.textMateRules": "Задает цвета и стили с использованием правил оформления textmate (расширенный параметр)." +} \ No newline at end of file diff --git a/i18n/trk/extensions/configuration-editing/out/extension.i18n.json b/i18n/trk/extensions/configuration-editing/out/extension.i18n.json new file mode 100644 index 0000000000..5ffee30b7b --- /dev/null +++ b/i18n/trk/extensions/configuration-editing/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "exampleExtension": "Örnek" +} \ No newline at end of file diff --git a/i18n/trk/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/trk/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json new file mode 100644 index 0000000000..ccf02d78fa --- /dev/null +++ b/i18n/trk/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "activeEditorShort": "ör. myFile.txt", + "activeEditorMedium": "ör. myFolder/myFile.txt", + "activeEditorLong": "ör. /Users/Development/myProject/myFolder/myFile.txt", + "rootName": "ör: myFolder1, myFolder2, myFolder3", + "rootPath": "ör. /Users/Development/myProject", + "folderName": "ör: myFolder", + "folderPath": "ör: /Users/Development/myFolder", + "appName": "ör. VS Code", + "dirty": "değişiklik göstergesi, aktif düzenleyici kaydedilmemiş değişiklikler içeriyorsa", + "separator": "koşullu ayırıcı ('-') sadece değişkenler tarafından değerlerle çevrildiğinde gösterir", + "assocLabelFile": "Uzantılı Dosyalar", + "assocDescriptionFile": "Dosya adında glob deseniyle eşleşen tüm dosyaları belirtilen tanımlayıcıya sahip olan dile eşleyin.", + "assocLabelPath": "Yollu Dosyalar", + "assocDescriptionPath": "Dosya yolunda mutlak yol glob deseniyle eşleşen tüm dosyaları belirtilen tanımlayıcıya sahip olan dile eşleyin.", + "fileLabel": "Uzantıya Göre Dosyalar", + "fileDescription": "Belirli bir dosya uzantısına sahip tüm dosyaları eşleştirin.", + "filesLabel": "Birden Çok Uzantılı Dosyalar", + "filesDescription": "Tüm dosyaları herhangi bir dosya uzantısıyla eşleştirin.", + "derivedLabel": "Ada Göre Eşdüzeyi Olan Dosyalar", + "derivedDescription": "Aynı ada ancak farklı bir uzantıya sahip eşdüzeyi olan dosyaları eşleştirin.", + "topFolderLabel": "Ada Göre Klasör (En Üst Düzey)", + "topFolderDescription": "En üst düzeydeki bir klasörü belirli bir ad ile eşleştirin.", + "topFoldersLabel": "Birden Çok Ada Sahip Klasör (En Üst Düzey)", + "topFoldersDescription": "Birden çok en üst düzey klasörü eşleştirin.", + "folderLabel": "Ada Göre Klasör (Herhangi Bir Konum)", + "folderDescription": "Bir klasörü herhangi bir konumdaki belirli bir ad ile eşleştirin.", + "falseDescription": "Deseni devre dışı bırakın.", + "trueDescription": "Deseni etkinleştirin.", + "siblingsDescription": "Aynı ada ancak farklı bir uzantıya sahip eşdüzeyi olan dosyaları eşleştirin.", + "languageSpecificEditorSettings": "Dile özel düzenleyici ayarları", + "languageSpecificEditorSettingsDescription": "Dil için düzenleyici ayarlarını geçersiz kıl" +} \ No newline at end of file diff --git a/i18n/trk/extensions/css/client/out/cssMain.i18n.json b/i18n/trk/extensions/css/client/out/cssMain.i18n.json new file mode 100644 index 0000000000..ef84e0bc90 --- /dev/null +++ b/i18n/trk/extensions/css/client/out/cssMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cssserver.name": "CSS Dil Sunucusu" +} \ No newline at end of file diff --git a/i18n/trk/extensions/css/package.i18n.json b/i18n/trk/extensions/css/package.i18n.json new file mode 100644 index 0000000000..ae9c2f9599 --- /dev/null +++ b/i18n/trk/extensions/css/package.i18n.json @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "css.lint.argumentsInColorFunction.desc": "Geçersiz sayıda parametre", + "css.lint.boxModel.desc": "Doldurma veya kenarlık kullanırken genişlik veya yükseklik kullanmayın", + "css.lint.compatibleVendorPrefixes.desc": "Satıcıya özgü bir ön ek kullanırken satıcıya özgü diğer tüm özellikleri de dahil ettiğinizden emin olun", + "css.lint.duplicateProperties.desc": "Yinelenen stil tanımları kullanmayın", + "css.lint.emptyRules.desc": "Boş kural kümeleri kullanmayın", + "css.lint.float.desc": "'float' kullanmaktan kaçının. Float'lar, düzenin herhangi bir unsuru değiştiğinde kolayca bozulan kırılgan CSS ile sonuçlanır.", + "css.lint.fontFaceProperties.desc": "@font-face kuralı 'src' ve 'font-family' özelliklerini tanımlamalıdır", + "css.lint.hexColorLength.desc": "Onaltılık renkler üç veya altı onaltılık sayıdan oluşmalıdır", + "css.lint.idSelector.desc": "Bu kurallar HTML'ye çok sıkı bağlı olduğundan seçiciler kimlikleri içermemelidir.", + "css.lint.ieHack.desc": "IE izinsiz girişleri yalnızca IE7 ve daha eski sürümler desteklenirken gereklidir", + "css.lint.important.desc": "!important kullanmaktan kaçının. Tüm CSS'nin belirginlik düzeyi üzerindeki denetimin kaybedildiğinin ve yeniden düzenlenmesi gerektiğinin bir belirtisidir.", + "css.lint.importStatement.desc": "İçe aktarma deyimleri paralel olarak yüklenmez", + "css.lint.propertyIgnoredDueToDisplay.desc": "Özellik gösterim nedeniyle yoksayıldı. Örn. 'display: inline' ile width, height, margin-top, margin-bottom ve float özelliklerinin hiçbir etkisi olmaz", + "css.lint.universalSelector.desc": "Evrensel seçici (*) yavaş olarak bilinir", + "css.lint.unknownProperties.desc": "Bilinmeyen özellik.", + "css.lint.unknownVendorSpecificProperties.desc": "Bilinmeyen satıcıya özel özellik.", + "css.lint.vendorPrefix.desc": "Satıcıya özgü bir ön ek kullanırken standart özelliği de dahil edin", + "css.lint.zeroUnits.desc": "Sıfır için birim gerekmez", + "css.trace.server.desc": "VS Code ve CSS dil sunucusu arasındaki iletişimi izler.", + "css.validate.title": "CSS doğrulamasını ve sorunların önem derecelerini denetler.", + "css.validate.desc": "Tüm doğrulamaları etkinleştirir veya devre dışı bırakır", + "less.lint.argumentsInColorFunction.desc": "Geçersiz sayıda parametre", + "less.lint.boxModel.desc": "Doldurma veya kenarlık kullanırken genişlik veya yükseklik kullanmayın", + "less.lint.compatibleVendorPrefixes.desc": "Satıcıya özgü bir ön ek kullanırken satıcıya özgü diğer tüm özellikleri de dahil ettiğinizden emin olun", + "less.lint.duplicateProperties.desc": "Yinelenen stil tanımları kullanmayın", + "less.lint.emptyRules.desc": "Boş kural kümeleri kullanmayın", + "less.lint.float.desc": "'float' kullanmaktan kaçının. Float'lar, düzenin herhangi bir unsuru değiştiğinde kolayca bozulan kırılgan CSS ile sonuçlanır.", + "less.lint.fontFaceProperties.desc": "@font-face kuralı 'src' ve 'font-family' özelliklerini tanımlamalıdır", + "less.lint.hexColorLength.desc": "Onaltılık renkler üç veya altı onaltılık sayıdan oluşmalıdır", + "less.lint.idSelector.desc": "Bu kurallar HTML'ye çok sıkı bağlı olduğundan seçiciler kimlikleri içermemelidir.", + "less.lint.ieHack.desc": "IE izinsiz girişleri yalnızca IE7 ve daha eski sürümler desteklenirken gereklidir", + "less.lint.important.desc": "!important kullanmaktan kaçının. Tüm CSS'nin belirginlik düzeyi üzerindeki denetimin kaybedildiğinin ve yeniden düzenlenmesi gerektiğinin bir belirtisidir.", + "less.lint.importStatement.desc": "İçe aktarma deyimleri paralel olarak yüklenmez", + "less.lint.propertyIgnoredDueToDisplay.desc": "Özellik gösterim nedeniyle yoksayıldı. Örn. 'display: inline' ile width, height, margin-top, margin-bottom ve float özelliklerinin hiçbir etkisi olmaz", + "less.lint.universalSelector.desc": "Evrensel seçici (*) yavaş olarak bilinir", + "less.lint.unknownProperties.desc": "Bilinmeyen özellik.", + "less.lint.unknownVendorSpecificProperties.desc": "Bilinmeyen satıcıya özel özellik.", + "less.lint.vendorPrefix.desc": "Satıcıya özgü bir ön ek kullanırken standart özelliği de dahil edin", + "less.lint.zeroUnits.desc": "Sıfır için birim gerekmez", + "less.validate.title": "LESS doğrulamasını ve sorunların önem derecelerini denetler.", + "less.validate.desc": "Tüm doğrulamaları etkinleştirir veya devre dışı bırakır", + "scss.lint.argumentsInColorFunction.desc": "Geçersiz sayıda parametre", + "scss.lint.boxModel.desc": "Doldurma veya kenarlık kullanırken genişlik veya yükseklik kullanmayın", + "scss.lint.compatibleVendorPrefixes.desc": "Satıcıya özgü bir ön ek kullanırken satıcıya özgü diğer tüm özellikleri de dahil ettiğinizden emin olun", + "scss.lint.duplicateProperties.desc": "Yinelenen stil tanımları kullanmayın", + "scss.lint.emptyRules.desc": "Boş kural kümeleri kullanmayın", + "scss.lint.float.desc": "'float' kullanmaktan kaçının. Float'lar, düzenin herhangi bir unsuru değiştiğinde kolayca bozulan kırılgan CSS ile sonuçlanır.", + "scss.lint.fontFaceProperties.desc": "@font-face kuralı 'src' ve 'font-family' özelliklerini tanımlamalıdır", + "scss.lint.hexColorLength.desc": "Onaltılık renkler üç veya altı onaltılık sayıdan oluşmalıdır", + "scss.lint.idSelector.desc": "Bu kurallar HTML'ye çok sıkı bağlı olduğundan seçiciler kimlikleri içermemelidir.", + "scss.lint.ieHack.desc": "IE izinsiz girişleri yalnızca IE7 ve daha eski sürümler desteklenirken gereklidir", + "scss.lint.important.desc": "!important kullanmaktan kaçının. Tüm CSS'nin belirginlik düzeyi üzerindeki denetimin kaybedildiğinin ve yeniden düzenlenmesi gerektiğinin bir belirtisidir.", + "scss.lint.importStatement.desc": "İçe aktarma deyimleri paralel olarak yüklenmez", + "scss.lint.propertyIgnoredDueToDisplay.desc": "Özellik gösterim nedeniyle yoksayıldı. Örn. 'display: inline' ile width, height, margin-top, margin-bottom ve float özelliklerinin hiçbir etkisi olmaz", + "scss.lint.universalSelector.desc": "Evrensel seçici (*) yavaş olarak bilinir", + "scss.lint.unknownProperties.desc": "Bilinmeyen özellik.", + "scss.lint.unknownVendorSpecificProperties.desc": "Bilinmeyen satıcıya özel özellik.", + "scss.lint.vendorPrefix.desc": "Satıcıya özgü bir ön ek kullanırken standart özelliği de dahil edin", + "scss.lint.zeroUnits.desc": "Sıfır için birim gerekmez", + "scss.validate.title": "SCSS doğrulamasını ve sorunların önem derecelerini denetler.", + "scss.validate.desc": "Tüm doğrulamaları etkinleştirir veya devre dışı bırakır", + "less.colorDecorators.enable.desc": "Renk dekoratörlerini etkinleştirir veya devre dışı bırakır", + "scss.colorDecorators.enable.desc": "Renk dekoratörlerini etkinleştirir veya devre dışı bırakır", + "css.colorDecorators.enable.desc": "Renk dekoratörlerini etkinleştirir veya devre dışı bırakır", + "css.colorDecorators.enable.deprecationMessage": "`css.colorDecorators.enable` ayarı, `editor.colorDecorators` yüzünden kullanım dışıdır.", + "scss.colorDecorators.enable.deprecationMessage": "`scss.colorDecorators.enable` ayarı, `editor.colorDecorators` yüzünden kullanım dışıdır.", + "less.colorDecorators.enable.deprecationMessage": "`less.colorDecorators.enable` ayarı, `editor.colorDecorators` yüzünden kullanım dışıdır." +} \ No newline at end of file diff --git a/i18n/trk/extensions/emmet/package.i18n.json b/i18n/trk/extensions/emmet/package.i18n.json new file mode 100644 index 0000000000..2553576d05 --- /dev/null +++ b/i18n/trk/extensions/emmet/package.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.wrapWithAbbreviation": "Kısaltma ile Sarmala", + "command.wrapIndividualLinesWithAbbreviation": "Her Bir Satırı Kısaltma ile Sarmala", + "command.removeTag": "Etiketi Kaldır", + "command.updateTag": "Etiketi Güncelle", + "command.matchTag": "Eşleşen Çifte Git", + "command.balanceIn": "Dengele (İçe Doğru)", + "command.balanceOut": "Dengele (Dışa Doğru)", + "command.prevEditPoint": "Önceki Düzenleme Noktasına Git", + "command.nextEditPoint": "Sonraki Düzenleme Noktasına Git", + "command.mergeLines": "Satırları Birleştir", + "command.selectPrevItem": "Bir Önceki Ögeyi Seç", + "command.selectNextItem": "Bir Sonraki Ögeyi Seç", + "command.splitJoinTag": "Etiketi Böl/Birleştir", + "command.toggleComment": "Yorumu Aç/Kapat", + "command.evaluateMathExpression": "Matematik İfadesini Değerlendir", + "command.updateImageSize": "Görüntü Boyutunu Güncelle", + "command.reflectCSSValue": "CSS Değerini Yansıt", + "command.incrementNumberByOne": "1 Arttır", + "command.decrementNumberByOne": "1 Azalt", + "command.incrementNumberByOneTenth": "0.1 Arttır", + "command.decrementNumberByOneTenth": "0.1 Azalt", + "command.incrementNumberByTen": "10 Arttır", + "command.decrementNumberByTen": "10 Azalt", + "emmetSyntaxProfiles": "Belirtilen sentaks için profil tanımlayın veya kendi profilinizi belirli kurallarla kullanın.", + "emmetExclude": "Emmet kısaltmalarının genişletilmeyeceği bir diller dizisi.", + "emmetExtensionsPath": "Emmet profileri ve parçacıklarını içeren bir klasör yolu.'", + "emmetShowExpandedAbbreviation": "Genişletilmiş emmet kısaltmalarını öneriler olarak gösterir.\n\"inMarkupAndStylesheetFilesOnly\" seçeneği\" html, haml, jade, xml, xsl, css, scss, sass, less ve stylus'a uygulanır.\n\"always\" seçeneği işaretleme/css'den bağımsız olarak dosyanın tüm bölümlerine uygulanır.", + "emmetShowAbbreviationSuggestions": "Olası emmet kısaltmalarını öneriler olarak gösterir. Stil dosyalarında veya emmet.showExpandedAbbreviation, \"never\" olarak ayarlandığında uygulanamaz.", + "emmetIncludeLanguages": "Varsayılan olarak desteklenmeyen dillerde emmet kısaltmalarını etkinleştirin. Burada dil ile desteklenen emmet destekli dil arasında eşleme ekleyin.", + "emmetVariables": "Emmet parçacıklarında kullanılacak değişkenler", + "emmetTriggerExpansionOnTab": "Etkinleştirildiğinde, emmet kısaltmaları TAB tuşuna basıldığında genişletilir.", + "emmetPreferences": "Emmet'in bazı eylemleri ve çözümleyicilerinin davranışını değiştirmek için kullanılacak tercihler.", + "emmetPreferencesIntUnit": "Tam sayı değerleri için varsayılan birim", + "emmetPreferencesFloatUnit": "Ondalık sayı değerleri için varsayılan birim", + "emmetPreferencesCssAfter": "CSS kısaltmaları genişletilirken CSS özelliğinin sonuna koyulacak sembol", + "emmetPreferencesSassAfter": "Sass dosyalarında CSS kısaltmaları genişletilirken CSS özelliğinin sonuna koyulacak sembol", + "emmetPreferencesStylusAfter": "Stylus dosyalarında CSS kısaltmaları genişletilirken CSS özelliğinin sonuna koyulacak sembol", + "emmetPreferencesCssBetween": "CSS kısaltmaları genişletilirken CSS özelliği ve değerinin arasına koyulacak sembol", + "emmetPreferencesSassBetween": "Sass dosyalarında CSS kısaltmaları genişletilirken CSS özelliği ve değerinin arasına koyulacak sembol", + "emmetPreferencesStylusBetween": "Stylus dosyalarında CSS kısaltmaları genişletilirken CSS özelliği ve değerinin arasına koyulacak sembol", + "emmetShowSuggestionsAsSnippets": "Doğru ise, emmet önerileri, editor.snippetSuggestions ayarı ile sıralayabilmenizi sağlayan parçacıklar olarak gösterilir." +} \ No newline at end of file diff --git a/i18n/trk/extensions/extension-editing/out/extensionLinter.i18n.json b/i18n/trk/extensions/extension-editing/out/extensionLinter.i18n.json new file mode 100644 index 0000000000..1b1a39c126 --- /dev/null +++ b/i18n/trk/extensions/extension-editing/out/extensionLinter.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpsRequired": "Resimler HTTPS protokolünü kullanmalıdır.", + "svgsNotValid": "SVG'ler geçerli bir resim kaynağı değil.", + "embeddedSvgsNotValid": "Gömülü SVG'ler geçerli bir resim kaynağı değil.", + "dataUrlsNotValid": "Veri URL'leri geçerli bir resim kaynağı değil.", + "relativeUrlRequiresHttpsRepository": "Göreli görüntü URL'leri, HTTPS protokollü bir deponun bu package.json'da belirtilmesini gerektiriyor.", + "relativeIconUrlRequiresHttpsRepository": "Bir simge, HTTPS protokollü bir deponun bu package.json'da belirtilmesini gerektiriyor.", + "relativeBadgeUrlRequiresHttpsRepository": "Göreli gösterge URL'leri, HTTPS protokollü bir deponun bu package.json'da belirtilmesini gerektiriyor." +} \ No newline at end of file diff --git a/i18n/trk/extensions/extension-editing/out/packageDocumentHelper.i18n.json b/i18n/trk/extensions/extension-editing/out/packageDocumentHelper.i18n.json new file mode 100644 index 0000000000..8bd6a84c69 --- /dev/null +++ b/i18n/trk/extensions/extension-editing/out/packageDocumentHelper.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "languageSpecificEditorSettings": "Dile özel düzenleyici ayarları", + "languageSpecificEditorSettingsDescription": "Dil için düzenleyici ayarlarını geçersiz kıl" +} \ No newline at end of file diff --git a/i18n/trk/extensions/git/out/askpass-main.i18n.json b/i18n/trk/extensions/git/out/askpass-main.i18n.json new file mode 100644 index 0000000000..a5664d6496 --- /dev/null +++ b/i18n/trk/extensions/git/out/askpass-main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "missOrInvalid": "Eksik veya geçersiz kimlik bilgisi." +} \ No newline at end of file diff --git a/i18n/trk/extensions/git/out/commands.i18n.json b/i18n/trk/extensions/git/out/commands.i18n.json new file mode 100644 index 0000000000..bd26e91ba9 --- /dev/null +++ b/i18n/trk/extensions/git/out/commands.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tag at": "{0} üzerindeki etiket", + "remote branch at": "{0} üzerindeki uzak dal", + "create branch": "$(plus) Yeni dal oluştur", + "repourl": "Depo URL'si", + "parent": "Üst Klasör", + "cloning": "Git deposu kopyalanıyor...", + "openrepo": "Depoyu Aç", + "proposeopen": "Kopyalanan depoyu açmak ister misiniz?", + "path to init": "Klasör yolu", + "provide path": "Git deposu oluşturmak için lütfen bir klasör yolu belirtin", + "HEAD not available": "'{0}'e ait HEAD sürümü mevcut değil.", + "confirm stage files with merge conflicts": "Birleştirme çakışmaları bulunan {0} dosyayı hazırlamak istediğinizden emin misiniz?", + "confirm stage file with merge conflicts": "Birleştirme çakışmaları bulunan {0} klasörünü hazırlamak istediğinizden emin misiniz?", + "yes": "Evet", + "confirm revert": "{0} üzerindeki seçili değişiklikleri geri almak istediğinizden emin misiniz?", + "revert": "Değişiklikleri Geri Al", + "discard": "Değişiklikleri Göz Ardı Et", + "confirm delete": "{0} dosyasını SİLMEK istediğinizden emin misiniz?", + "delete file": "Dosyayı Sil", + "confirm discard": "{0} üzerindeki seçili değişiklikleri göz ardı etmek istediğinizden emin misiniz?", + "confirm discard multiple": "{0} dosyadaki değişiklikleri göz ardı etmek istediğinizden emin misiniz?", + "warn untracked": "Bu, izlenmeyen {0} dosyayı SİLECEK!", + "confirm discard all single": "{0} üzerindeki seçili değişiklikleri göz ardı etmek istediğinizden emin misiniz?", + "confirm discard all": "{0} dosyadaki TÜM değişiklikleri göz ardı etmek istediğinizden emin misiniz?\nBu GERİ DÖNDÜRÜLEMEZ!\nMevcut çalışma grubunuz TAMAMEN KAYBOLACAK.", + "discardAll multiple": "1 Dosyayı Göz Ardı Et", + "discardAll": "{0} Dosyanın Tamamını Göz Ardı Et", + "confirm delete multiple": "{0} dosyayı SİLMEK istediğinizden emin misiniz?", + "delete files": "Dosyaları Sil", + "there are untracked files single": "Şu izlenmeyen dosya göz ardı edilirse DİSKTEN SİLİNECEK: {0}.", + "there are untracked files": "Göz ardı edilirse DİSKTEN SİLİNECEK {0} izlenmeyen dosya var.", + "confirm discard all 2": "{0}\n\nBu GERİ DÖNDÜRÜLEMEZ, mevcut çalışma grubunuz TAMAMEN KAYBOLACAK.", + "yes discard tracked": "İzlenen 1 Dosyayı Göz Ardı Et", + "yes discard tracked multiple": "İzlenen {0} Dosyayı Göz Ardı Et", + "no staged changes": "Commit'lenecek hazırlanmış değişiklik yok.\n\nTüm değişikliklerinizi otomatik olarak hazırlamak ve direkt olarak commit'lemek ister misiniz?", + "always": "Her Zaman", + "no changes": "Commit'lenecek değişiklik yok.", + "commit message": "Commit mesajı", + "provide commit message": "Lütfen bir commit mesajı belirtin.", + "select a ref to checkout": "Geçiş yapılacak bir başvuru seçin", + "branch name": "Dal adı", + "provide branch name": "Lütfen bir dal adı belirtin", + "select branch to delete": "Silinecek dalı seçin", + "confirm force delete branch": "'{0}' dalı tamamen birleştirilmemiş. Yine de silinsin mi?", + "delete branch": "Dalı Sil", + "select a branch to merge from": "Birleştirilmesi için bir dal seçin", + "merge conflicts": "Birleştirme çakışmaları var. Commit'lemeden önce bunları çözün.", + "tag name": "Etiket adı", + "provide tag name": "Lütfen bir etiket adı belirtin", + "tag message": "Mesaj", + "provide tag message": "Lütfen etikete açıklama yapmak için bir mesaj belirtin", + "no remotes to pull": "Deponuzda çekme işleminin yapılacağı hiçbir uzak uçbirim yapılandırılmamış.", + "pick remote pull repo": "Dalın çekileceği bir uzak uçbirim seçin", + "no remotes to push": "Deponuzda gönderimin yapılacağı hiçbir uzak uçbirim yapılandırılmamış.", + "push with tags success": "Başarılı bir şekilde etiketlerle gönderildi.", + "nobranch": "Lütfen uzak uçbirime gönderilecek dala geçiş yapın.", + "pick remote": "'{0}' dalının yayınlanacağı bir uzak uçbirim seçin:", + "sync is unpredictable": "Bu eylem, '{0}' esas projesine commitleri gönderecek ve alacaktır.", + "ok": "Tamam", + "never again": "Tamam, Tekrar Gösterme", + "no remotes to publish": "Deponuzda yayınlamanın yapılacağı hiçbir uzak uçbirim yapılandırılmamış.", + "no changes stash": "Geçici olarak saklanacak bir değişiklik yok.", + "provide stash message": "İsteğe bağlı olarak bir geçici olarak saklama mesajı belirtin", + "stash message": "Geçici olarak saklama mesajı", + "no stashes": "Geri yüklenecek geçici değişiklik yok.", + "pick stash to pop": "Geri yüklenecek ögeyi seçin", + "clean repo": "Geçiş yapmadan önce deponuzdaki çalışma ağacınızı temizleyin.", + "cant push": "Başvurular uzak uçbirime gönderilemiyor. Değişikliklerinizi entegre etmeden, ilk olarak 'Çek'i çalıştırın. ", + "git error details": "Git: {0}", + "git error": "Git hatası", + "open git log": "Git Günlüğünü Aç" +} \ No newline at end of file diff --git a/i18n/trk/extensions/git/out/main.i18n.json b/i18n/trk/extensions/git/out/main.i18n.json new file mode 100644 index 0000000000..cf56a559f2 --- /dev/null +++ b/i18n/trk/extensions/git/out/main.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "using git": "{1} yolundaki git {0} kullanılıyor", + "updateGit": "Git'i Güncelle", + "neverShowAgain": "Tekrar gösterme", + "git20": "git {0} yüklemiş olarak görünüyorsunuz. Code, git >= 2 ile en iyi şekilde çalışır" +} \ No newline at end of file diff --git a/i18n/trk/extensions/git/out/model.i18n.json b/i18n/trk/extensions/git/out/model.i18n.json new file mode 100644 index 0000000000..21a7757627 --- /dev/null +++ b/i18n/trk/extensions/git/out/model.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no repositories": "Mevcut depo yok", + "pick repo": "Bir depo seçin" +} \ No newline at end of file diff --git a/i18n/trk/extensions/git/out/repository.i18n.json b/i18n/trk/extensions/git/out/repository.i18n.json new file mode 100644 index 0000000000..84df9e40ed --- /dev/null +++ b/i18n/trk/extensions/git/out/repository.i18n.json @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Aç", + "index modified": "Dizin Değiştirildi", + "modified": "Değiştirilmiş", + "index added": "Dizin Eklendi", + "index deleted": "Dizin Silindi", + "deleted": "Silindi", + "index renamed": "Dizin Yeniden Adlandırıldı", + "index copied": "Dizin Kopyalandı", + "untracked": "İzlenmedi", + "ignored": "Yok Sayıldı", + "both deleted": "Her İkimiz de Sildik", + "added by us": "Bizim Tarafımızdan Eklendi", + "deleted by them": "Onlar Tarafından Silindi", + "added by them": "Onlar Tarafından Eklendi", + "deleted by us": "Bizim Tarafımızdan Silindi", + "both added": "Her İkimiz de Ekledik", + "both modified": "Her İkimiz de Değiştirdik", + "commit": "Commit'le", + "merge changes": "Değişiklikleri Birleştir", + "staged changes": "Hazırlanmış Değişiklikler", + "changes": "Değişiklikler", + "ok": "Tamam", + "neveragain": "Tekrar Gösterme", + "huge": "'{0}' yolundaki git deposunda çok fazla aktif değişikliklik var, Git özelliklerinin yalnızca bir alt kümesi etkinleştirilecektir." +} \ No newline at end of file diff --git a/i18n/trk/extensions/git/out/scmProvider.i18n.json b/i18n/trk/extensions/git/out/scmProvider.i18n.json new file mode 100644 index 0000000000..9e388b7a26 --- /dev/null +++ b/i18n/trk/extensions/git/out/scmProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commit": "Commit'le" +} \ No newline at end of file diff --git a/i18n/trk/extensions/git/out/statusbar.i18n.json b/i18n/trk/extensions/git/out/statusbar.i18n.json new file mode 100644 index 0000000000..a48aac2f43 --- /dev/null +++ b/i18n/trk/extensions/git/out/statusbar.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "checkout": "Geçiş yap...", + "sync changes": "Değişiklikleri Senkronize Et", + "publish changes": "Değişiklikleri Yayınla", + "syncing changes": "Değişiklikler Senkronize Ediliyor..." +} \ No newline at end of file diff --git a/i18n/trk/extensions/git/package.i18n.json b/i18n/trk/extensions/git/package.i18n.json new file mode 100644 index 0000000000..07e34f0413 --- /dev/null +++ b/i18n/trk/extensions/git/package.i18n.json @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.clone": "Klonla", + "command.init": "Depo Oluştur", + "command.close": "Depoyu Kapat", + "command.refresh": "Yenile", + "command.openChange": "Değişiklikleri Aç", + "command.openFile": "Dosya Aç", + "command.openHEADFile": "Dosya Aç (HEAD)", + "command.stage": "Değişiklikleri Hazırla", + "command.stageAll": "Tüm Değişiklikleri Hazırla", + "command.stageSelectedRanges": "Seçili Aralığı Hazırla", + "command.revertSelectedRanges": "Seçili Aralığı Geri Al", + "command.unstage": "Değişiklikleri Hazırlık Alanından Geri Al", + "command.unstageAll": "Tüm Değişiklikleri Hazırlık Alanından Geri Al", + "command.unstageSelectedRanges": "Seçili Alanı Hazırlık Alanından Geri Al", + "command.clean": "Değişiklikleri Göz Ardı Et", + "command.cleanAll": "Tüm Değişiklikleri Göz Ardı Et", + "command.commit": "Commit'le", + "command.commitStaged": "Hazırlananları Commit'le", + "command.commitStagedSigned": "Hazırlananları Commit'le (İmzalı)", + "command.commitStagedAmend": "Hazırlananları Commit'le (Değiştir)", + "command.commitAll": "Tümünü Commit'le", + "command.commitAllSigned": "Tümünü Commit'le (İmzalı)", + "command.commitAllAmend": "Tümünü Commit'le (Değiştir)", + "command.undoCommit": "Son Commit'i Geri Al", + "command.checkout": "Geçiş yap...", + "command.branch": "Dal Oluştur...", + "command.deleteBranch": "Dalı Sil...", + "command.merge": "Dalı Birleştir...", + "command.createTag": "Etiket Oluştur", + "command.pull": "Çek", + "command.pullRebase": "Çek (Yeniden Adresle)", + "command.pullFrom": "Şuradan Çek...", + "command.push": "Gönder", + "command.pushTo": "Gönder...", + "command.pushWithTags": "Etiketlerle Gönder", + "command.sync": "Senkronize Et", + "command.publish": "Dalı Yayınla", + "command.showOutput": "Git Çıktısını Göster", + "command.ignore": ".gitignore'a Dosya Ekle", + "command.stash": "Geçici Olarak Sakla", + "command.stashPop": "Geçici Olarak Saklananı Geri Yükle...", + "command.stashPopLatest": "En Son Geçici Olarak Saklananı Geri Yükle", + "config.enabled": "Git'in etkinleştirilip etkinleştirilmediği", + "config.path": "Çalıştırılabilir Git dosyasının yolu", + "config.autorefresh": "Otomatik yenilemenin etkinleştirilip etkinleştirilmediği", + "config.autofetch": "Otomatik getirmenin etkinleştirilip etkinleştirilmediği", + "config.enableLongCommitWarning": "Uzun commit mesajları hakkında uyarıda bulunulup bulunulmayacağı", + "config.confirmSync": "Git depolarını senkronize etmeden önce onaylayın", + "config.countBadge": "Git gösterge sayacını denetler. `all` tüm değişiklikleri sayar. `tracked` sadece izlenen değişikliklikleri sayar. `off` ise kapatır.", + "config.checkoutType": "`Geçiş Yap...` çalıştırılırken listelenecek dal türlerini denetler. `all` tüm başvuruları gösterir, `local` sadece yerel dalları gösterir, `tags` sadece etiketleri gösterir ve `remote` sadece uzak uçbirim dallarını gösterir.", + "config.ignoreLegacyWarning": "Eski Git uyarısını görmezden gelir", + "config.ignoreLimitWarning": "Bir depoda çok fazla değişiklik var uyarısını görmezden gelir", + "config.defaultCloneDirectory": "Bir git deposunun kopyalanacağı varsayılan konum", + "config.enableSmartCommit": "Hazırlanan değişiklik yoksa tüm değişiklikleri commit'le.", + "config.enableCommitSigning": "GPG ile commit imzalamayı etkinleştirir.", + "config.discardAllScope": "`Tüm Değişiklikleri Göz Ardı Et` komutuyla hangi değişikliklerin göz ardı edileceğini denetler. `all` tüm değişiklikleri göz ardı eder. `tracked` sadece izlenen dosyaları göz ardı eder. `prompt` eylem her çalıştığında bir onay penceresi gösterir." +} \ No newline at end of file diff --git a/i18n/trk/extensions/grunt/out/main.i18n.json b/i18n/trk/extensions/grunt/out/main.i18n.json new file mode 100644 index 0000000000..b8bc749727 --- /dev/null +++ b/i18n/trk/extensions/grunt/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Grunt otomatik tespiti hata ile sonuçlandı: {0}" +} \ No newline at end of file diff --git a/i18n/trk/extensions/grunt/package.i18n.json b/i18n/trk/extensions/grunt/package.i18n.json new file mode 100644 index 0000000000..33bf3c0545 --- /dev/null +++ b/i18n/trk/extensions/grunt/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.grunt.autoDetect": "Grunt görevlerinin otomatik olarak algılanıp algılanmayacağını denetler. Varsayılan olarak açıktır." +} \ No newline at end of file diff --git a/i18n/trk/extensions/gulp/out/main.i18n.json b/i18n/trk/extensions/gulp/out/main.i18n.json new file mode 100644 index 0000000000..663acb14f8 --- /dev/null +++ b/i18n/trk/extensions/gulp/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Gulp otomatik tespiti hata ile sonuçlandı: {0}" +} \ No newline at end of file diff --git a/i18n/trk/extensions/gulp/package.i18n.json b/i18n/trk/extensions/gulp/package.i18n.json new file mode 100644 index 0000000000..b5945600bb --- /dev/null +++ b/i18n/trk/extensions/gulp/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.gulp.autoDetect": "Gulp görevlerinin otomatik olarak algılanıp algılanmayacağını denetler. Varsayılan olarak açıktır." +} \ No newline at end of file diff --git a/i18n/trk/extensions/html/client/out/htmlMain.i18n.json b/i18n/trk/extensions/html/client/out/htmlMain.i18n.json new file mode 100644 index 0000000000..9016532011 --- /dev/null +++ b/i18n/trk/extensions/html/client/out/htmlMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "htmlserver.name": "HTML Dil Sunucusu" +} \ No newline at end of file diff --git a/i18n/trk/extensions/html/package.i18n.json b/i18n/trk/extensions/html/package.i18n.json new file mode 100644 index 0000000000..8c22baa255 --- /dev/null +++ b/i18n/trk/extensions/html/package.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.format.enable.desc": "Varsayılan HTML biçimlendiricisini etkinleştirin/devre dışı bırakın", + "html.format.wrapLineLength.desc": "Satır başına en fazla karakter miktarı (0 = devre dışı bırak)", + "html.format.unformatted.desc": "Yeniden biçimlendirilmeyecek virgülle ayrılmış etiketler listesi. 'null' değeri, https://www.w3.org/TR/html5/dom.html#phrasing-content adresinde listelenen tüm etiketleri varsayılan olarak belirler.", + "html.format.contentUnformatted.desc": "İçeriğin yeniden biçimlendirilmeyeceği virgülle ayrılmış etiketler listesi. 'null' değeri, 'pre' etiketini varsayılan olarak belirler.", + "html.format.indentInnerHtml.desc": " ve bölümlerini girintile.", + "html.format.preserveNewLines.desc": "Ögelerden önceki mevcut satır sonlarının korunup korunmayacağı. Yalnızca ögelerden önce çalışır, etiketler içinde veya metinde çalışmaz.", + "html.format.maxPreserveNewLines.desc": "Bir öbekte korunacak maksimum satır sonu sayısı. Sınırsız için 'null' değerini kullanın.", + "html.format.indentHandlebars.desc": "{{#foo}} ve {{/foo}}'yu biçimlendir ve girintile.", + "html.format.endWithNewline.desc": "Boş bir satırla bitir.", + "html.format.extraLiners.desc": "Kendilerinden önce ek bir boş satır bulunması gereken virgülle ayrılmış etiketler listesi. 'null' değeri, \"head, body, /html\" değerini varsayılan olarak belirler.", + "html.format.wrapAttributes.desc": "Öznitelikleri sarmala.", + "html.format.wrapAttributes.auto": "Öznitelikleri sadece satır uzunluğu aşıldığında sarmala.", + "html.format.wrapAttributes.force": "İlki hariç tüm öznitelikleri sarmala.", + "html.format.wrapAttributes.forcealign": "İlki hariç tüm öznitelikleri sarmala ve hizada tut.", + "html.format.wrapAttributes.forcemultiline": "Tüm öznitelikleri sarmala.", + "html.suggest.angular1.desc": "Yerleşik HTML dili desteğinin Angular V1 etiketlerini ve özelliklerini önerip önermeyeceğini yapılandırır.", + "html.suggest.ionic.desc": "Yerleşik HTML dili desteğinin Ionic etiketlerini, özelliklerini ve değerlerini önerip önermeyeceğini yapılandırır.", + "html.suggest.html5.desc": "Yerleşik HTML dili desteğinin HTML5 etiketlerini, özelliklerini ve değerlerini önerip önermeyeceğini yapılandırır.", + "html.trace.server.desc": "VS Code ve HTML dil sunucusu arasındaki iletişimi izler.", + "html.validate.scripts": "Yerleşik HTML dili desteğinin HTML5 gömülü betikleri doğrulayıp doğrulamayacağını yapılandırır.", + "html.validate.styles": "Yerleşik HTML dili desteğinin HTML5 gömülü stilleri doğrulayıp doğrulamayacağını yapılandırır.", + "html.autoClosingTags": "HTML etiketlerinin otomatik kapatılmasını etkinleştirin/devre dışı bırakın" +} \ No newline at end of file diff --git a/i18n/trk/extensions/jake/out/main.i18n.json b/i18n/trk/extensions/jake/out/main.i18n.json new file mode 100644 index 0000000000..ad6581a12a --- /dev/null +++ b/i18n/trk/extensions/jake/out/main.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "execFailed": "Jake otomatik tespiti hata ile sonuçlandı: {0}" +} \ No newline at end of file diff --git a/i18n/trk/extensions/jake/package.i18n.json b/i18n/trk/extensions/jake/package.i18n.json new file mode 100644 index 0000000000..7847d66473 --- /dev/null +++ b/i18n/trk/extensions/jake/package.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.jake.autoDetect": "Jake görevlerinin otomatik olarak algılanıp algılanmayacağını denetler. Varsayılan olarak açıktır." +} \ No newline at end of file diff --git a/i18n/trk/extensions/javascript/out/features/bowerJSONContribution.i18n.json b/i18n/trk/extensions/javascript/out/features/bowerJSONContribution.i18n.json new file mode 100644 index 0000000000..af035f3324 --- /dev/null +++ b/i18n/trk/extensions/javascript/out/features/bowerJSONContribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.bower.default": "Varsayılan bower.json", + "json.bower.error.repoaccess": "Bower deposuna yapılan istek başarısız oldu: {0}", + "json.bower.latest.version": "en son" +} \ No newline at end of file diff --git a/i18n/trk/extensions/javascript/out/features/packageJSONContribution.i18n.json b/i18n/trk/extensions/javascript/out/features/packageJSONContribution.i18n.json new file mode 100644 index 0000000000..e50b5e0ccc --- /dev/null +++ b/i18n/trk/extensions/javascript/out/features/packageJSONContribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.package.default": "Varsayılan package.json", + "json.npm.error.repoaccess": "NPM deposuna yapılan istek başarısız oldu: {0}", + "json.npm.latestversion": "Paketin şu andaki en son sürümü", + "json.npm.majorversion": "En son birincil sürümle eşleşiyor (1.x.x)", + "json.npm.minorversion": "En son ikincil sürümle eşleşiyor (1.2.x)", + "json.npm.version.hover": "En son sürüm: {0}" +} \ No newline at end of file diff --git a/i18n/trk/extensions/json/client/out/jsonMain.i18n.json b/i18n/trk/extensions/json/client/out/jsonMain.i18n.json new file mode 100644 index 0000000000..5b25716a08 --- /dev/null +++ b/i18n/trk/extensions/json/client/out/jsonMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonserver.name": "JSON Dil Sunucusu" +} \ No newline at end of file diff --git a/i18n/trk/extensions/json/package.i18n.json b/i18n/trk/extensions/json/package.i18n.json new file mode 100644 index 0000000000..a4861f945c --- /dev/null +++ b/i18n/trk/extensions/json/package.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "json.schemas.desc": "Şemaları geçerli projedeki JSON dosyalarıyla ilişkilendir", + "json.schemas.url.desc": "Bir şemanın URL'si veya geçerli dizindeki bir şemanın göreli yolu", + "json.schemas.fileMatch.desc": "JSON dosyaları şemalara çözümlenirken eşleşme için kullanılacak bir dosya düzenleri dizisi.", + "json.schemas.fileMatch.item.desc": "JSON dosyaları şemalara çözümlenirken eşleşme için '*' içerebilen bir dosya düzeni.", + "json.schemas.schema.desc": "Verilen URL için şema tanımı. Şema, yalnızca şema URL'sine erişimi önlemek için sağlanmalıdır.", + "json.format.enable.desc": "Varsayılan JSON biçimlendiricisini etkinleştirin/devre dışı bırakın (yeniden başlatma gerektirir)", + "json.tracing.desc": "VS Code ve JSON dil sunucusu arasındaki iletişimi izler.", + "json.colorDecorators.enable.desc": "Renk dekoratörlerini etkinleştirir veya devre dışı bırakır", + "json.colorDecorators.enable.deprecationMessage": "`json.colorDecorators.enable` ayarı, `editor.colorDecorators` yüzünden kullanım dışıdır." +} \ No newline at end of file diff --git a/i18n/trk/extensions/markdown/out/extension.i18n.json b/i18n/trk/extensions/markdown/out/extension.i18n.json new file mode 100644 index 0000000000..85a2333afe --- /dev/null +++ b/i18n/trk/extensions/markdown/out/extension.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "onPreviewStyleLoadError": "'markdown.styles' yüklenemedi: {0}" +} \ No newline at end of file diff --git a/i18n/trk/extensions/markdown/out/previewContentProvider.i18n.json b/i18n/trk/extensions/markdown/out/previewContentProvider.i18n.json new file mode 100644 index 0000000000..089359d580 --- /dev/null +++ b/i18n/trk/extensions/markdown/out/previewContentProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "preview.securityMessage.text": "Bu belgedeki bazı içerikler devre dışı bırakıldı", + "preview.securityMessage.title": "Markdown önizlemesinde potansiyel olarak tehlikeli veya güvenli olmayan içerik devre dışı bırakıldı. Güvenli olmayan içeriğe izin vermek veya betikleri etkinleştirmek için Markdown önizleme güvenlik ayarını değiştirin", + "preview.securityMessage.label": "İçerik Devre Dışı Güvenlik Uyarısı" +} \ No newline at end of file diff --git a/i18n/trk/extensions/markdown/out/security.i18n.json b/i18n/trk/extensions/markdown/out/security.i18n.json new file mode 100644 index 0000000000..1677763eb4 --- /dev/null +++ b/i18n/trk/extensions/markdown/out/security.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "strict.title": "Katı", + "strict.description": "Sadece güvenli içeriği yükle", + "insecureContent.title": "Güvenli olmayan içeriğe izin ver", + "insecureContent.description": "Http üzerinden içerik yüklemeyi etkinleştir", + "disable.title": "Devre Dışı Bırak", + "disable.description": "Tüm içeriğe ve betik yürütmeye izin ver. Tavsiye edilmez", + "moreInfo.title": "Daha Fazla Bilgi", + "preview.showPreviewSecuritySelector.title": "Bu çalışma alanında Markdown önizlemeleri için güvenlik ayarlarını seçin" +} \ No newline at end of file diff --git a/i18n/trk/extensions/markdown/package.i18n.json b/i18n/trk/extensions/markdown/package.i18n.json new file mode 100644 index 0000000000..b68c629185 --- /dev/null +++ b/i18n/trk/extensions/markdown/package.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "markdown.preview.breaks.desc": "Markdown önizlemesinde satır sonlarının nasıl gösterileceğini ayarlar. 'true' olarak ayarlamak, her yeni satırda bir
oluşturur.", + "markdown.preview.linkify": "Markdown önizlemesinde URL benzeri metinlerin bağlantıya çevrilmesini etkinleştir veya devre dışı bırak.", + "markdown.preview.doubleClickToSwitchToEditor.desc": "Düzenleyiciye geçiş yapmak için Markdown önizlemesine çift tıklayın.", + "markdown.preview.fontFamily.desc": "Markdown önizlemesinde kullanılan yazı tipi ailesini denetler.", + "markdown.preview.fontSize.desc": "Markdown önizlemesinde kullanılan yazı tipi boyutunu piksel olarak denetler.", + "markdown.preview.lineHeight.desc": "Markdown önizlemesinde kullanılan satır yüksekliğini denetler. Bu sayı yazı tipi boyutuna görecelidir.", + "markdown.preview.markEditorSelection.desc": "Markdown önizlemesinde geçerli düzenleyici seçimini işaretle.", + "markdown.preview.scrollEditorWithPreview.desc": "Markdown önizlemesi kaydırıldığında, düzenleyicinin görünümünü güncelle.", + "markdown.preview.scrollPreviewWithEditorSelection.desc": "Düzenleyicide seçili satırın görünmesi için Markdown önizlemesini kaydırır.", + "markdown.preview.title": "Önizlemeyi Aç", + "markdown.previewFrontMatter.dec": "YAML ön maddesinin Markdown önizlemesinde nasıl gösterilmesi gerektiğini ayarlar. 'hide' ön maddeyi kaldırır. Diğer türlü; ön madde, Markdown içeriği olarak sayılır.", + "markdown.previewSide.title": "Önizlemeyi Yana Aç", + "markdown.showSource.title": "Kaynağı Göster", + "markdown.styles.dec": "Markdown önizlemesinde kullanılmak üzere CSS stil dosyalarını işaret eden bir URL'ler veya yerel yollar listesi. Göreli yollar, gezginde açılan klasöre göreli olarak yorumlanır.", + "markdown.showPreviewSecuritySelector.title": "Önizleme Güvenlik Ayarlarını Değiştir", + "markdown.trace.desc": "Markdown eklentisi için hata ayıklama günlüğünü etkinleştir.", + "markdown.refreshPreview.title": "Önizlemeyi Yenile" +} \ No newline at end of file diff --git a/i18n/trk/extensions/merge-conflict/out/codelensProvider.i18n.json b/i18n/trk/extensions/merge-conflict/out/codelensProvider.i18n.json new file mode 100644 index 0000000000..be28760f19 --- /dev/null +++ b/i18n/trk/extensions/merge-conflict/out/codelensProvider.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acceptCurrentChange": "Geçerli Değişikliği Kabul Et", + "acceptIncomingChange": "Gelen Değişikliği Kabul Et", + "acceptBothChanges": "Her İki Değişikliği de Kabul Et", + "compareChanges": "Değişiklikleri Karşılaştır" +} \ No newline at end of file diff --git a/i18n/trk/extensions/merge-conflict/out/commandHandler.i18n.json b/i18n/trk/extensions/merge-conflict/out/commandHandler.i18n.json new file mode 100644 index 0000000000..a6131b6859 --- /dev/null +++ b/i18n/trk/extensions/merge-conflict/out/commandHandler.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "cursorNotInConflict": "Düzenleyici imleci birleştirme çakışması içinde değil", + "compareChangesTitle": "{0}: Geçerli Değişiklikler ⟷ Gelen Değişiklikler", + "cursorOnCommonAncestorsRange": "Düzenleyici imleci ortak atalar bloğunda, imleci lütfen \"geçerli\" veya \"gelen\" bloğundan birine getirin", + "cursorOnSplitterRange": "Düzenleyici imleci birleştirme çakışması ayırıcısında, imleci lütfen \"geçerli\" veya \"gelen\" bloğundan birine getirin", + "noConflicts": "Bu dosyada birleştirme çakışması bulunamadı", + "noOtherConflictsInThisFile": "Bu dosyada başka birleştirme çakışması bulunamadı" +} \ No newline at end of file diff --git a/i18n/trk/extensions/merge-conflict/out/mergeDecorator.i18n.json b/i18n/trk/extensions/merge-conflict/out/mergeDecorator.i18n.json new file mode 100644 index 0000000000..eb813c9e27 --- /dev/null +++ b/i18n/trk/extensions/merge-conflict/out/mergeDecorator.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "currentChange": "(Geçerli Değişiklik)", + "incomingChange": "(Gelen Değişiklik)" +} \ No newline at end of file diff --git a/i18n/trk/extensions/merge-conflict/package.i18n.json b/i18n/trk/extensions/merge-conflict/package.i18n.json new file mode 100644 index 0000000000..757a2dfb86 --- /dev/null +++ b/i18n/trk/extensions/merge-conflict/package.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "command.category": "Birleştirme Çakışması", + "command.accept.all-incoming": "Gelen Tümünü Kabul Et", + "command.accept.all-both": "Tümünü Birden Kabul Et", + "command.accept.current": "Şuan Geçerli Olanı Kabul Et", + "command.accept.incoming": "Geleni Kabul Et", + "command.accept.selection": "Seçimi Kabul Et", + "command.accept.both": "Her İkisini de Kabul Et", + "command.next": "Sonraki Çakışma", + "command.previous": "Önceki Çakışma", + "command.compare": "Geçerli Çakışmayı Karşılaştır", + "config.title": "Birleştirme Çakışması", + "config.codeLensEnabled": "Düzenleyicideki birleştirme çakışması bloğu kod objektifini etkinleştir veya devre dışı bırak", + "config.decoratorsEnabled": "Düzenleyicideki birleştirme çakışması dekoratörlerini etkinleştir veya devre dışı bırak" +} \ No newline at end of file diff --git a/i18n/trk/extensions/npm/package.i18n.json b/i18n/trk/extensions/npm/package.i18n.json new file mode 100644 index 0000000000..92bb14f8cc --- /dev/null +++ b/i18n/trk/extensions/npm/package.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "config.npm.autoDetect": "Npm betiklerinin otomatik olarak algılanıp algılanmayacağını denetler. Varsayılan olarak açıktır.", + "config.npm.runSilent": "npm komutlarını `--silent` seçeneğiyle çalıştır" +} \ No newline at end of file diff --git a/i18n/trk/extensions/php/out/features/validationProvider.i18n.json b/i18n/trk/extensions/php/out/features/validationProvider.i18n.json new file mode 100644 index 0000000000..df7fea0584 --- /dev/null +++ b/i18n/trk/extensions/php/out/features/validationProvider.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "php.useExecutablePath": "{0} (çalışma alanı ayarı olarak tanımlı) yürütülebilir dosyasına PHP dosyalarını doğrulama izni veriyor musunuz?", + "php.yes": "İzin Ver", + "php.no": "İzin Verme", + "wrongExecutable": "{0} geçerli bir php yürütülebilir dosyası olmadığı için doğrulanamıyor. PHP yürütülebilir dosyasını yapılandırmak için 'php.validate.executablePath' ayarını kullanın.", + "noExecutable": "Hiçbir PHP yürütülebilir dosyası ayarlanmadığı için doğrulanamıyor. PHP yürütülebilir dosyasını yapılandırmak için 'php.validate.executablePath' ayarını kullanın.", + "unknownReason": "{0} yolu kullanılarak php çalıştırılamadı. Sebep bilinmiyor." +} \ No newline at end of file diff --git a/i18n/trk/extensions/php/package.i18n.json b/i18n/trk/extensions/php/package.i18n.json new file mode 100644 index 0000000000..70bc8c0fe5 --- /dev/null +++ b/i18n/trk/extensions/php/package.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configuration.suggest.basic": "Yerleşik PHP dili önerilerinin etkinleştirilip etkinleştirilmediğini yapılandırır. Destek, PHP globalleri ve değişkenleri önerir.", + "configuration.validate.enable": "Yerleşik PHP doğrulamasını etkinleştir/devre dışı bırak.", + "configuration.validate.executablePath": "PHP çalıştırılabilir dosyasına işaret eder.", + "configuration.validate.run": "Doğrulayıcının kayıt esnasında mı tuşlama esnasında mı çalışacağı.", + "configuration.title": "PHP", + "commands.categroy.php": "PHP", + "command.untrustValidationExecutable": "PHP doğrulama yürütülebilir dosyasına izin verme (çalışma alanı ayarı olarak tanımlanır)" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/trk/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 0000000000..bce4fce0b8 --- /dev/null +++ b/i18n/trk/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "Düzenleyici özellikleri için TypeScript ({1}) kullanılıyor. TypeScript ({0}) makinanızda global olarak yüklenmiş durumda. VS Code'daki hatalar TSC hatalarından farklı olabilir", + "moreInformation": "Daha Fazla Bilgi", + "doNotCheckAgain": "Tekrar Kontrol Etme", + "close": "Kapat", + "updateTscCheck": "Kullanıcı ayarı 'typescript.check.tscVersion', \"false\" olarak güncellendi" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/features/completionItemProvider.i18n.json b/i18n/trk/extensions/typescript/out/features/completionItemProvider.i18n.json new file mode 100644 index 0000000000..6eb03a050e --- /dev/null +++ b/i18n/trk/extensions/typescript/out/features/completionItemProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "acquiringTypingsLabel": "Tuşlamalar alınıyor...", + "acquiringTypingsDetail": "IntelliSense için tuşlama tanımları alınıyor..." +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json b/i18n/trk/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json new file mode 100644 index 0000000000..900090e58e --- /dev/null +++ b/i18n/trk/extensions/typescript/out/features/directiveCommentCompletionProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ts-check": "Bir JavaScript dosyasının anlamsal kontrolünü etkinleştirir. Bir dosyanın en üstünde olmalıdır.", + "ts-nocheck": "Bir JavaScript dosyasının anlamsal kontrolünü devre dışı bırakır. Bir dosyanın en üstünde olmalıdır.", + "ts-ignore": "Bir dosyanın sonraki satırında @ts-check hatalarını bastırır." +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json b/i18n/trk/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json new file mode 100644 index 0000000000..5786422538 --- /dev/null +++ b/i18n/trk/extensions/typescript/out/features/implementationsCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneImplementationLabel": "1 uygulama", + "manyImplementationLabel": "{0} uygulama", + "implementationsErrorLabel": "Uygulamalar belirlenemedi" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json b/i18n/trk/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json new file mode 100644 index 0000000000..0d8b8cd2b3 --- /dev/null +++ b/i18n/trk/extensions/typescript/out/features/jsDocCompletionProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.jsDocCompletionItem.documentation": "JSDoc yorumu" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json b/i18n/trk/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json new file mode 100644 index 0000000000..eefd768215 --- /dev/null +++ b/i18n/trk/extensions/typescript/out/features/referencesCodeLensProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "oneReferenceLabel": "1 başvuru", + "manyReferenceLabel": "{0} başvuru", + "referenceErrorLabel": "Başvurular belirlenemedi" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/features/taskProvider.i18n.json b/i18n/trk/extensions/typescript/out/features/taskProvider.i18n.json new file mode 100644 index 0000000000..7fb91b2b31 --- /dev/null +++ b/i18n/trk/extensions/typescript/out/features/taskProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "buildAndWatchTscLabel": "izleme - {0}", + "buildTscLabel": "derleme - {0}" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/typescriptMain.i18n.json b/i18n/trk/extensions/typescript/out/typescriptMain.i18n.json new file mode 100644 index 0000000000..59e2f563bf --- /dev/null +++ b/i18n/trk/extensions/typescript/out/typescriptMain.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.projectConfigNoWorkspace": "Bir TypeScript veya JavaScript projesini kullanmak için lütfen bir klasör açın", + "typescript.projectConfigUnsupportedFile": "TypeScript mi yoksa JavaScript mi projesi olduğu tespit edilemedi. Desteklenmeyen dosya türü", + "typescript.projectConfigCouldNotGetInfo": "TypeScript mi yoksa JavaScript mi projesi olduğu tespit edilemedi", + "typescript.noTypeScriptProjectConfig": "Dosya bir TypeScript projesinin bir parçası değil", + "typescript.noJavaScriptProjectConfig": "Dosya bir JavaScript projesinin bir parçası değil", + "typescript.configureTsconfigQuickPick": "tsconfig.json'u yapılandır", + "typescript.configureJsconfigQuickPick": "jsconfig.json'u yapılandır", + "typescript.projectConfigLearnMore": "Daha Fazla Bilgi Edin" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/typescriptServiceClient.i18n.json b/i18n/trk/extensions/typescript/out/typescriptServiceClient.i18n.json new file mode 100644 index 0000000000..a1d6f14496 --- /dev/null +++ b/i18n/trk/extensions/typescript/out/typescriptServiceClient.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noServerFound": "{0} yolu geçerli bir tsserver kurulumuna işaret etmiyor. Paketlenmiş TypeScript sürümüne geri dönülüyor.", + "serverCouldNotBeStarted": "TypeScript dil sunucusu başlatılamadı. Hata mesajı: {0}", + "typescript.openTsServerLog.notSupported": "TS Sunucu günlüğü için TS 2.2.2+ gerekiyor", + "typescript.openTsServerLog.loggingNotEnabled": "TS Sunucu günlüğü kapalı. Lütfen `typescript.tsserver.log` ögesini ayarlayın ve günlüğe yazmayı etkinleştirmek için TS sunucusunu yeniden başlatın", + "typescript.openTsServerLog.enableAndReloadOption": "Günlüğe yazmayı etkinleştir ve TS sunucusunu yeniden başlat", + "typescript.openTsServerLog.noLogFile": "TS sunucu günlüğe yazmaya başlamadı.", + "openTsServerLog.openFileFailedFailed": "TS Sunucu günlük dosyası açılamadı", + "serverDiedAfterStart": "TypeScript dil hizmeti, başladıktan hemen sonra 5 kez kapandı. Hizmet yeniden başlatılmayacaktır.", + "serverDiedReportIssue": "Sorun Bildir", + "serverDied": "TypeScript dil hizmeti, son 5 dakikada 5 kez beklenmedik şekilde kapandı." +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/utils/api.i18n.json b/i18n/trk/extensions/typescript/out/utils/api.i18n.json new file mode 100644 index 0000000000..e6167de880 --- /dev/null +++ b/i18n/trk/extensions/typescript/out/utils/api.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidVersion": "geçersiz sürüm" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/utils/logger.i18n.json b/i18n/trk/extensions/typescript/out/utils/logger.i18n.json new file mode 100644 index 0000000000..11bdc00e5e --- /dev/null +++ b/i18n/trk/extensions/typescript/out/utils/logger.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "channelName": "TypeScript" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/utils/projectStatus.i18n.json b/i18n/trk/extensions/typescript/out/utils/projectStatus.i18n.json new file mode 100644 index 0000000000..9502743ca5 --- /dev/null +++ b/i18n/trk/extensions/typescript/out/utils/projectStatus.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hintExclude": "Proje çapında JavaScript/TypeScript dil özelliklerini etkinleştirmek için, şunlar gibi birçok dosyaya sahip klasörleri hariç tutun: {0}", + "hintExclude.generic": "Proje çapında JavaScript/TypeScript dil özelliklerini etkinleştirmek için, üzerinde çalışmadığınız kaynak dosyalar içeren büyük klasörleri hariç tutun.", + "large.label": "Hariç Tutmaları Yapılandır", + "hintExclude.tooltip": "Proje çapında JavaScript/TypeScript dil özelliklerini etkinleştirmek için, üzerinde çalışmadığınız kaynak dosyalar içeren büyük klasörleri hariç tutun." +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/utils/typingsStatus.i18n.json b/i18n/trk/extensions/typescript/out/utils/typingsStatus.i18n.json new file mode 100644 index 0000000000..ea3b8bdd28 --- /dev/null +++ b/i18n/trk/extensions/typescript/out/utils/typingsStatus.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installingPackages": "Daha iyi TypeScript IntelliSense için veri alınıyor", + "typesInstallerInitializationFailed.title": "JavaScript dil özellikleri için tuşlama dosyaları yüklenemedi. Lütfen NPM'in yüklenmiş olduğundan veya kullanıcı ayarlarınızda 'typescript.npm' ögesini yapılandırın", + "typesInstallerInitializationFailed.moreInformation": "Daha Fazla Bilgi", + "typesInstallerInitializationFailed.doNotCheckAgain": "Tekrar Kontrol Etme", + "typesInstallerInitializationFailed.close": "Kapat" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/utils/versionPicker.i18n.json b/i18n/trk/extensions/typescript/out/utils/versionPicker.i18n.json new file mode 100644 index 0000000000..003397496b --- /dev/null +++ b/i18n/trk/extensions/typescript/out/utils/versionPicker.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "useVSCodeVersionOption": "VS Code'un Sürümünü Kullan", + "useWorkspaceVersionOption": "Çalışma Alanı Sürümünü Kullan", + "learnMore": "Daha Fazla Bilgi Edin", + "selectTsVersion": "JavaScript ve TypeScript dil özellikleri için kullanılacak TypeScript sürümünü seçin" +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/out/utils/versionProvider.i18n.json b/i18n/trk/extensions/typescript/out/utils/versionProvider.i18n.json new file mode 100644 index 0000000000..774821c742 --- /dev/null +++ b/i18n/trk/extensions/typescript/out/utils/versionProvider.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "couldNotLoadTsVersion": "Bu yolda TypeScript sürümü yüklenemedi", + "noBundledServerFound": "VS Code'un tsserver'ı hatalı bir virüs tespit aracı gibi bir uygulama tarafından silindi. Lütfen VS Code'u yeniden yükleyin." +} \ No newline at end of file diff --git a/i18n/trk/extensions/typescript/package.i18n.json b/i18n/trk/extensions/typescript/package.i18n.json new file mode 100644 index 0000000000..5e23fd5f26 --- /dev/null +++ b/i18n/trk/extensions/typescript/package.i18n.json @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "typescript.reloadProjects.title": "Projeyi Yeniden Yükle", + "javascript.reloadProjects.title": "Projeyi Yeniden Yükle", + "configuration.typescript": "TypeScript", + "typescript.useCodeSnippetsOnMethodSuggest.dec": "İşlevleri parametre imzalarıyla tamamlayın.", + "typescript.tsdk.desc": "Kullanılacak tsserver ve lib*.d.ts dosyalarını içeren klasör yolunu belirtir.", + "typescript.disableAutomaticTypeAcquisition": "Otomatik tür kazanımını devre dışı bırakır. TypeScript >= 2.0.6 gerektirir.", + "typescript.tsserver.log": "TS sunucusunun bir dosyaya günlük yazmasını etkinleştirir. Bu günlük, TS Sunucu sorunlarını teşhis etmek için kullanılabilir. Günlük dosya yollarını, kaynak kodunu ve projenizdeki diğer muhtemel hassas bilgileri içerebilir.", + "typescript.tsserver.trace": "TS sunucusuna gönderilen mesajları izlemeyi etkinleştirir. Bu izleme, TS Sunucu sorunlarını teşhis etmek için kullanılabilir. İzleme; dosya yollarını, kaynak kodunu ve projenizdeki diğer muhtemel hassas bilgileri içerebilir.", + "typescript.validate.enable": "TypeScript doğrulamasını etkinleştir veya devre dışı bırak.", + "typescript.format.enable": "Varsayılan TypeScript biçimlendiricisini etkinleştirin/devre dışı bırakın.", + "javascript.format.enable": "Varsayılan JavaScript biçimlendiricisini etkinleştir veya devre dışı bırak.", + "format.insertSpaceAfterCommaDelimiter": "Virgül sınırlayıcısından sonra boşluk eklenmesini tanımlar.", + "format.insertSpaceAfterConstructor": "Oluşturucu anahtar kelimesinden sonra boşluk eklenip eklenmeyeceğini tanımlar. TypeScript >= 2.3.0 gerektirir.", + "format.insertSpaceAfterSemicolonInForStatements": "Bir ifade için noktalı virgülden sonra boşluk eklenmesini tanımlar.", + "format.insertSpaceBeforeAndAfterBinaryOperators": "Bir ikili operatöründen sonra boşluk eklenmesini tanımlar.", + "format.insertSpaceAfterKeywordsInControlFlowStatements": "Bir kontrol akışı ifadesi için anahtar kelimelerden sonra boşluk eklenmesini tanımlar.", + "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "Anonim fonksiyonlar için \"function\" anahtar kelimesinden sonra boşluk eklenmesini tanımlar.", + "format.insertSpaceBeforeFunctionParenthesis": "Fonksiyon argüman parantezlerinden önce boşluk eklenmesini tanımlar. TypeScript >= 2.1.5 gerektirir.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "Boş olmayan parantezler açıldıktan sonra ve kapatılmadan önce boşluk eklenmesini tanımlar.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "Boş olmayan köşeli parantezler açıldıktan sonra ve kapatılmadan önce boşluk eklenmesini tanımlar.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Boş olmayan küme parantezleri açıldıktan sonra ve kapatılmadan önce boşluk eklenmesini tanımlar. TypeScript >= 2.3.0 gerektirir.", + "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "Şablon dizesi ayraçları açıldıktan sonra ve kapatılmadan önce boşluk eklenmesini tanımlar. TypeScript >= 2.0.6 gerektirir.", + "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "JSX ifadesi ayraçları açıldıktan sonra ve kapatılmadan önce boşluk eklenmesini tanımlar. TypeScript >= 2.0.6 gerektirir.", + "format.insertSpaceAfterTypeAssertion": "TypeScript'te tür iddialarından sonra boşluk eklenip eklenmeyeceğini tanımlar. TypeScript >= 2.4 gerektirir.", + "format.placeOpenBraceOnNewLineForFunctions": "Fonksiyonlarda bir açılış ayracının yeni satıra koyulup koyulmayacağını tanımlar.", + "format.placeOpenBraceOnNewLineForControlBlocks": "Kontrol bloklarında bir açılış ayracının yeni satıra koyulup koyulmayacağını tanımlar.", + "javascript.validate.enable": "JavaScript doğrulamasını etkinleştir veya devre dışı bırak.", + "typescript.goToProjectConfig.title": "Proje Yapılandırmasına Git", + "javascript.goToProjectConfig.title": "Proje Yapılandırmasına Git", + "javascript.referencesCodeLens.enabled": "JavaScript dosyalarında başvuru kod objektifini etkinleştir veya devre dışı bırak.", + "typescript.referencesCodeLens.enabled": "TypeScript dosyalarında başvuru kod objektifini etkinleştir veya devre dışı bırak. TypeScript >= 2.0.6 gerektirir.", + "typescript.implementationsCodeLens.enabled": "Uygulama kod objektifini etkinleştir veya devre dışı bırak. TypeScript >= 2.2.0 gerektirir.", + "typescript.openTsServerLog.title": "TS Sunucu günlüğünü aç", + "typescript.restartTsServer": "TS sunucusunu yeniden başlat", + "typescript.selectTypeScriptVersion.title": "TypeScript Sürümünü Seç", + "jsDocCompletion.enabled": "Otomatik JSDoc yorumlarını etkinleştir veya devre dışı bırak.", + "javascript.implicitProjectConfig.checkJs": "JavaScript dosyalarının anlamsal kontrolünü etkinleştir veya devre dışı bırak. Mevcut jsconfig.json veya tsconfig.json dosyaları bu ayarı geçersiz kılar. TypeScript >= 2.3.1 gerektirir.", + "typescript.npm": "Otomatik Tür Kazanımı için kullanılacak NPM yürütülebilir dosyasının yolunu belirtir. TypeScript >= 2.3.4 gerektirir.", + "typescript.check.npmIsInstalled": "Otomatik Tür Kazanımı için NPM'in yüklü olup olmadığını kontrol et.", + "javascript.nameSuggestions": "JavaScript öneri listelerindeki dosyadan benzersiz adları eklemeyi etkinleştir veya devre dışı bırak.", + "typescript.tsc.autoDetect": "Tsc görevlerinin otomatik olarak algılanıp algılanmayacağını denetler. Varsayılan olarak açıktır.", + "typescript.problemMatchers.tsc.label": "TypeScript sorunları", + "typescript.problemMatchers.tscWatch.label": "TypeScript sorunları (izleme modu)" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/browser/ui/actionbar/actionbar.i18n.json b/i18n/trk/src/vs/base/browser/ui/actionbar/actionbar.i18n.json new file mode 100644 index 0000000000..e8173bb549 --- /dev/null +++ b/i18n/trk/src/vs/base/browser/ui/actionbar/actionbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleLabel": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/browser/ui/aria/aria.i18n.json b/i18n/trk/src/vs/base/browser/ui/aria/aria.i18n.json new file mode 100644 index 0000000000..bdebab42a5 --- /dev/null +++ b/i18n/trk/src/vs/base/browser/ui/aria/aria.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "repeated": "{0} (tekrar oluştu)" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/browser/ui/findinput/findInput.i18n.json b/i18n/trk/src/vs/base/browser/ui/findinput/findInput.i18n.json new file mode 100644 index 0000000000..53aaa854ca --- /dev/null +++ b/i18n/trk/src/vs/base/browser/ui/findinput/findInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "giriş" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json b/i18n/trk/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json new file mode 100644 index 0000000000..05080bb6ad --- /dev/null +++ b/i18n/trk/src/vs/base/browser/ui/findinput/findInputCheckboxes.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caseDescription": "Büyük/Küçük Harf Eşleştir", + "wordsDescription": "Sözcüğün Tamamını Eşleştir", + "regexDescription": "Normal İfade Kullan" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/browser/ui/inputbox/inputBox.i18n.json b/i18n/trk/src/vs/base/browser/ui/inputbox/inputBox.i18n.json new file mode 100644 index 0000000000..9f42272f81 --- /dev/null +++ b/i18n/trk/src/vs/base/browser/ui/inputbox/inputBox.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Hata: {0}", + "alertWarningMessage": "Uyarı: {0}", + "alertInfoMessage": "Bilgi: {0}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json b/i18n/trk/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json new file mode 100644 index 0000000000..d875945dec --- /dev/null +++ b/i18n/trk/src/vs/base/browser/ui/resourceviewer/resourceViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "imgMeta": "{0}x{1} {2}", + "largeImageError": "Resim, düzenleyicide görüntülemek için çok büyük.", + "resourceOpenExternalButton": "Harici program kullanarak resmi aç", + "nativeBinaryError": "Dosya ikili olduğu, çok büyük olduğu veya desteklenmeyen bir metin kodlaması kullandığı için düzenleyicide görüntülenemiyor.", + "sizeB": "{0}B", + "sizeKB": "{0}KB", + "sizeMB": "{0}MB", + "sizeGB": "{0}GB", + "sizeTB": "{0}TB" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/browser/ui/toolbar/toolbar.i18n.json b/i18n/trk/src/vs/base/browser/ui/toolbar/toolbar.i18n.json new file mode 100644 index 0000000000..2855be7cc4 --- /dev/null +++ b/i18n/trk/src/vs/base/browser/ui/toolbar/toolbar.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "more": "Diğer" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/common/errorMessage.i18n.json b/i18n/trk/src/vs/base/common/errorMessage.i18n.json new file mode 100644 index 0000000000..e1f2b047a2 --- /dev/null +++ b/i18n/trk/src/vs/base/common/errorMessage.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "message": "{0}. Hata kodu: {1}", + "error.permission.verbose": "İzin Verilmedi (HTTP {0})", + "error.permission": "İzin Verilmedi", + "error.http.verbose": "{0} (HTTP {1}: {2})", + "error.http": "{0} (HTTP {1})", + "error.connection.unknown.verbose": "Bilinmeyen Bağlantı Hatası ({0})", + "error.connection.unknown": "Bilinmeyen bir bağlantı hatası oluştu. Artık İnternet'e bağlı değilsiniz veya bağlandığınız sunucu çevrimdışı.", + "stackTrace.format": "{0}: {1}", + "error.defaultMessage": "Bilinmeyen bir hata oluştu. Daha fazla ayrıntı için lütfen günlüğe başvurun.", + "nodeExceptionMessage": "Bir sistem hatası oluştu ({0})", + "error.moreErrors": "{0} (toplam {1} hata)" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/common/jsonErrorMessages.i18n.json b/i18n/trk/src/vs/base/common/jsonErrorMessages.i18n.json new file mode 100644 index 0000000000..e7ac5ed578 --- /dev/null +++ b/i18n/trk/src/vs/base/common/jsonErrorMessages.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.invalidSymbol": "Geçersiz sembol", + "error.invalidNumberFormat": "Geçersiz sayı biçimi", + "error.propertyNameExpected": "Özellik adı bekleniyor", + "error.valueExpected": "Değer bekleniyor", + "error.colonExpected": "İki nokta üst üste bekleniyor", + "error.commaExpected": "Virgül bekleniyor", + "error.closeBraceExpected": "Kapanış ayracı bekleniyor", + "error.closeBracketExpected": "Kapanış köşeli ayracı bekleniyor", + "error.endOfFileExpected": "Dosya sonu bekleniyor" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/common/keybindingLabels.i18n.json b/i18n/trk/src/vs/base/common/keybindingLabels.i18n.json new file mode 100644 index 0000000000..b234e36af1 --- /dev/null +++ b/i18n/trk/src/vs/base/common/keybindingLabels.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ctrlKey": "Ctrl", + "shiftKey": "Shift", + "altKey": "Alt", + "windowsKey": "Windows", + "ctrlKey.long": "Control", + "shiftKey.long": "Shift", + "altKey.long": "Alt", + "cmdKey.long": "Command", + "windowsKey.long": "Windows" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/common/processes.i18n.json b/i18n/trk/src/vs/base/common/processes.i18n.json new file mode 100644 index 0000000000..fc83db4978 --- /dev/null +++ b/i18n/trk/src/vs/base/common/processes.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ExecutableParser.commandMissing": "Hata: yürütülebilir bilgi dize türünde bir komut tanımlamalıdır.", + "ExecutableParser.isShellCommand": "Uyarı: isShellCommand boole türünde olmalıdır. {0} değeri yok sayıldı.", + "ExecutableParser.args": "Uyarı: argümanlar \"string[]\" türünde olmalıdır. {0} değeri yok sayıldı.", + "ExecutableParser.invalidCWD": "Uyarı: options.cwd dize türünde olmalıdır. {0} değeri yok sayıldı." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/common/severity.i18n.json b/i18n/trk/src/vs/base/common/severity.i18n.json new file mode 100644 index 0000000000..1cc1eff73a --- /dev/null +++ b/i18n/trk/src/vs/base/common/severity.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sev.error": "Hata", + "sev.warning": "Uyarı", + "sev.info": "Bilgi" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/node/processes.i18n.json b/i18n/trk/src/vs/base/node/processes.i18n.json new file mode 100644 index 0000000000..7647b5fc54 --- /dev/null +++ b/i18n/trk/src/vs/base/node/processes.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunner.UNC": "UNC sürücüsünde kabuk komutu yürütülemez." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/node/zip.i18n.json b/i18n/trk/src/vs/base/node/zip.i18n.json new file mode 100644 index 0000000000..47ee6f4c05 --- /dev/null +++ b/i18n/trk/src/vs/base/node/zip.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "{0}, zip içerisinde bulunamadı." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json b/i18n/trk/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json new file mode 100644 index 0000000000..ef5a055a18 --- /dev/null +++ b/i18n/trk/src/vs/base/parts/quickopen/browser/quickOpenModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabelEntry": "{0}, seçici", + "quickOpenAriaLabel": "seçici" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json b/i18n/trk/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json new file mode 100644 index 0000000000..78c58e99e8 --- /dev/null +++ b/i18n/trk/src/vs/base/parts/quickopen/browser/quickOpenWidget.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpenAriaLabel": "Hızlı seçici. Sonuçları daraltmak için yazmaya başlayın.", + "treeAriaLabel": "Hızlı Seçici" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/base/parts/tree/browser/treeDefaults.i18n.json b/i18n/trk/src/vs/base/parts/tree/browser/treeDefaults.i18n.json new file mode 100644 index 0000000000..4fa9c68ae2 --- /dev/null +++ b/i18n/trk/src/vs/base/parts/tree/browser/treeDefaults.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Daralt" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/code/electron-main/auth.i18n.json b/i18n/trk/src/vs/code/electron-main/auth.i18n.json new file mode 100644 index 0000000000..cce0b88b9d --- /dev/null +++ b/i18n/trk/src/vs/code/electron-main/auth.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "authRequire": "Proxy Kimlik Doğrulaması Gerekli", + "proxyauth": "{0} proxy'si kimlik doğrulama gerektiriyor." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/code/electron-main/menus.i18n.json b/i18n/trk/src/vs/code/electron-main/menus.i18n.json new file mode 100644 index 0000000000..93d5419a41 --- /dev/null +++ b/i18n/trk/src/vs/code/electron-main/menus.i18n.json @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mFile": "&&Dosya", + "mEdit": "Dü&&zen", + "mSelection": "&&Seçim", + "mView": "&&Görünüm", + "mGoto": "G&&it", + "mDebug": "&&Hata Ayıklama", + "mWindow": "Pencere", + "mHelp": "&&Yardım", + "mTask": "Gö&&revler", + "miNewWindow": "Yeni &&Pencere", + "mAbout": "{0} Hakkında", + "mServices": "Hizmetler", + "mHide": "{0} öğesini gizle", + "mHideOthers": "Diğerlerini Gizle", + "mShowAll": "Tümünü Göster", + "miQuit": "{0} Öğesinden Çık", + "miNewFile": "&&Yeni Dosya", + "miOpen": "&&Aç...", + "miOpenWorkspace": "Çalışma Alanı &&Aç...", + "miOpenFolder": "&&Klasör Aç...", + "miOpenFile": "&&Dosya Aç...", + "miOpenRecent": "&&Son Kullanılanları Aç", + "miSaveWorkspaceAs": "Çalışma Alanını &&Farklı Kaydet...", + "miAddFolderToWorkspace": "Çalışma Alanına Klasör &&Ekle...", + "miSave": "&&Kaydet", + "miSaveAs": "&&Farklı Kaydet", + "miSaveAll": "&&Tümünü Kaydet", + "miAutoSave": "Otomatik Kaydet", + "miRevert": "Dosyayı &&Geri Al", + "miCloseWindow": "Pen&&cereyi Kapat", + "miCloseWorkspace": "Ça&&lışma Alanını Kapat", + "miCloseFolder": "K&&lasörü Kapat", + "miCloseEditor": "Dü&&zenleyiciyi Kapat", + "miExit": "Çı&&kış", + "miOpenSettings": "&&Ayarlar", + "miOpenKeymap": "&&Klavye Kısayolları", + "miOpenKeymapExtensions": "&&Tuş Haritası Eklentileri", + "miOpenSnippets": "Kullanıcı &&Parçacıkları", + "miSelectColorTheme": "&&Renk Teması", + "miSelectIconTheme": "&&Dosya Simgesi Teması", + "miPreferences": "T&&ercihler", + "miReopenClosedEditor": "&&Kapatılan Düzenleyiciyi Tekrar Aç", + "miMore": "&&Daha Fazlası...", + "miClearRecentOpen": "Son Açılanları &&Temizle", + "miUndo": "&&Geri Al", + "miRedo": "&&Yinele", + "miCut": "&&Kes", + "miCopy": "K&&opyala", + "miPaste": "Y&&apıştır", + "miFind": "&&Bul", + "miReplace": "&&Değiştir", + "miFindInFiles": "Dosyalarda B&&ul", + "miReplaceInFiles": "Dosyalarda Değiş&&tir", + "miEmmetExpandAbbreviation": "Emmet: Kı&&saltmayı Genişlet", + "miShowEmmetCommands": "E&&mmet...", + "miToggleLineComment": "Satı&&r Yorumunu Aç/Kapat", + "miToggleBlockComment": "Yorum B&&loğunu Aç/Kapat", + "miMultiCursorAlt": "Birden Fazla İmleç İçin Alt+Tıklamaya Geçiş Yap", + "miMultiCursorCmd": "Birden Fazla İmleç İçin Cmd+Tıklamaya Geçiş Yap", + "miMultiCursorCtrl": "Birden Fazla İmleç İçin Ctrl+Tıklamaya Geçiş Yap", + "miInsertCursorAbove": "Yukarıya &&İmleç Ekle", + "miInsertCursorBelow": "Aşağıya İ&&mleç Ekle", + "miInsertCursorAtEndOfEachLineSelected": "&&Satır Sonlarına İmleç Ekle", + "miAddSelectionToNextFindMatch": "S&&onraki Tekrarlamayı Ekle", + "miAddSelectionToPreviousFindMatch": "Ö&&nceki Tekrarlamayı Ekle", + "miSelectHighlights": "Tüm T&&ekrarlamaları Değiştir", + "miCopyLinesUp": "Satırı &&Yukarı Kopyala", + "miCopyLinesDown": "Satırı &&Aşağı Kopyala", + "miMoveLinesUp": "Satırı Y&&ukarı Taşı", + "miMoveLinesDown": "Satı&&rı Aşağı Taşı", + "miSelectAll": "&&Tümünü Seç", + "miSmartSelectGrow": "Seçimi &&Genişlet", + "miSmartSelectShrink": "Seçimi &&Daralt", + "miViewExplorer": "&&Gezgin", + "miViewSearch": "&&Arama", + "miViewSCM": "&&SCM", + "miViewDebug": "&&Hata Ayıklama", + "miViewExtensions": "&&Eklentiler", + "miToggleOutput": "Çı&&ktı", + "miToggleDebugConsole": "Hata &&Ayıklama Konsolu", + "miToggleIntegratedTerminal": "Entegre &&Terminal", + "miMarker": "S&&orunlar", + "miAdditionalViews": "&&Ek Görünümler", + "miCommandPalette": "Komut &&Paleti...", + "miToggleFullScreen": "Tam Ekra&&nı Aç/Kapat", + "miToggleZenMode": "Zen Modunu Aç/Kapat", + "miToggleMenuBar": "&&Menü Çubuğunu Gizle/Göster", + "miSplitEditor": "Düzenleyiciyi &&Böl", + "miToggleEditorLayout": "Dü&&zenleyici Grubu Düzenini Değiştir", + "miToggleSidebar": "Ke&&nar Çubuğunu Aç/Kapat", + "miMoveSidebarRight": "Kenar Çubuğunu S&&ağa Taşı", + "miMoveSidebarLeft": "Kenar Çubuğunu S&&ola Taşı", + "miTogglePanel": "&&Paneli Aç/Kapat", + "miHideStatusbar": "&&Durum Çubuğunu Gizle", + "miShowStatusbar": "&&Durum Çubuğunu Göster", + "miHideActivityBar": "Etkinlik Ç&&ubuğunu Gizle", + "miShowActivityBar": "Etkinlik Ç&&ubuğunu Göster", + "miToggleWordWrap": "&&Sözcük Kaydırmasını Aç/Kapat", + "miToggleMinimap": "&&Mini Haritayı Aç/Kapat", + "miToggleRenderWhitespace": "&&Boşlukları Görüntülemeyi Aç/Kapat", + "miToggleRenderControlCharacters": "&&Kontrol Karakterlerini Aç/Kapat", + "miZoomIn": "&&Yakınlaştır", + "miZoomOut": "&&Uzaklaştır", + "miZoomReset": "Yakınlaştırmayı Sı&&fırla", + "miBack": "&&Geri", + "miForward": "&&İleri", + "miNextEditor": "&&Sonraki Düzenleyici", + "miPreviousEditor": "Ö&&nceki Düzenleyici", + "miNextEditorInGroup": "&&Grupta Sonraki Kullanılan Düzenleyici", + "miPreviousEditorInGroup": "G&&rupta Önceki Kullanılan Düzenleyici", + "miSwitchEditor": "&&Düzenleyici Değiştir", + "miFocusFirstGroup": "İ&&lk Grup", + "miFocusSecondGroup": "İ&&kinci Grup", + "miFocusThirdGroup": "Üçün&&cü Grup", + "miNextGroup": "Sonraki Gr&&up", + "miPreviousGroup": "Önceki Gru&&p", + "miSwitchGroup": "Grup &&Değiştir", + "miGotoFile": "D&&osyaya Git...", + "miGotoSymbolInFile": "Dosyada S&&embole Git...", + "miGotoSymbolInWorkspace": "Çalışma &&Alanında Sembole Git...", + "miGotoDefinition": "&&Tanıma Git", + "miGotoTypeDefinition": "Tü&&r Tanımına Git", + "miGotoImplementation": "U&&ygulamaya Git", + "miGotoLine": "&&Satıra Git...", + "miStartDebugging": "&&Hata Ayıklamaya Başla", + "miStartWithoutDebugging": "Hata Ayıklama &&Olmadan Başlat", + "miStopDebugging": "Hata Ayıklamayı D&&urdur", + "miRestart Debugging": "Hata Ayıklamayı &&Yeniden Başlat", + "miOpenConfigurations": "Ya&&pılandırmaları Aç", + "miAddConfiguration": "Yapı&&landırma Ekle...", + "miStepOver": "&&Adım At", + "miStepInto": "&&İçine Adımla", + "miStepOut": "&&Dışına Adımla", + "miContinue": "De&&vam Et", + "miToggleBreakpoint": "Kesme &&Noktası Ekle/Kaldır", + "miConditionalBreakpoint": "&&Koşullu Kesme Noktası...", + "miColumnBreakpoint": "&&Sütun Kesme Noktası", + "miFunctionBreakpoint": "&&Fonksiyon Kesme Noktası", + "miNewBreakpoint": "&&Yeni Kesme Noktası", + "miEnableAllBreakpoints": "Tüm Kesme Noktalarını Etkinleştir", + "miDisableAllBreakpoints": "&&Tüm Kesme Noktalarını Devre Dışı Bırak", + "miRemoveAllBreakpoints": "Tüm Kesme Noktalarını Kaldı&&r", + "miInstallAdditionalDebuggers": "&&Ek Hata Ayıklayıcıları Yükle", + "mMinimize": "Simge Durumuna Küçült", + "mZoom": "Yakınlaştırma", + "mBringToFront": "Tümünü Öne Getir", + "miSwitchWindow": "&&Pencere Değiştir...", + "miToggleDevTools": "&&Geliştirici Araçlarını Aç/Kapat", + "miAccessibilityOptions": "&&Erişilebilirlik Seçenekleri", + "miReportIssues": "So&&run Bildir", + "miWelcome": "&&Hoş Geldiniz", + "miInteractivePlayground": "&&İnteraktif Oyun Alanı", + "miDocumentation": "&&Belgeler", + "miReleaseNotes": "&&Sürüm Notları", + "miKeyboardShortcuts": "&&Klavye Kısayolları Başvurusu", + "miIntroductoryVideos": "Tanıtım &&Videoları", + "miTipsAndTricks": "&&İpuçları ve Püf noktaları", + "miTwitter": "&&Twitter'da Bize Katıl", + "miUserVoice": "Ö&&zellik İsteklerini Ara", + "miLicense": "&&Lisansı Görüntüle", + "miPrivacyStatement": "Gizlilik &&Beyanı", + "miAbout": "H&&akkında", + "miRunTask": "Görevi Ç&&alıştır", + "miBuildTask": "&&Derleme Görevini Çalıştır...", + "miRunningTask": "Ça&&lışan Görevleri Göster...", + "miRestartTask": "Çalışan Görevi &&Yeniden Başlat...", + "miTerminateTask": "&&Görevi Sonlandır...", + "miConfigureTask": "Görevleri Ya&&pılandır", + "miConfigureBuildTask": "&&Varsayılan Derleme Görevini Yapılandır", + "accessibilityOptionsWindowTitle": "Erişilebilirlik Seçenekleri", + "miRestartToUpdate": "Güncelleştirmek için Yeniden Başlat...", + "miCheckingForUpdates": "Güncelleştirmeler Denetleniyor...", + "miDownloadUpdate": "Mevcut Güncelleştirmeyi İndir", + "miDownloadingUpdate": "Güncelleştirme İndiriliyor...", + "miInstallingUpdate": "Güncelleştirme Yükleniyor...", + "miCheckForUpdates": "Güncelleştirmeleri Denetle...", + "aboutDetail": "\nSürüm {0}\nCommit {1}\nTarih {2}\nKabuk {3}\nOluşturucu {4}\nNode {5}\nMimari {6}", + "okButton": "Tamam" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/code/electron-main/window.i18n.json b/i18n/trk/src/vs/code/electron-main/window.i18n.json new file mode 100644 index 0000000000..2c376b3e51 --- /dev/null +++ b/i18n/trk/src/vs/code/electron-main/window.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hiddenMenuBar": "Menü çubuğuna **Alt** tuşuna basarak hala erişebilirsiniz." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/code/electron-main/windows.i18n.json b/i18n/trk/src/vs/code/electron-main/windows.i18n.json new file mode 100644 index 0000000000..7859968b50 --- /dev/null +++ b/i18n/trk/src/vs/code/electron-main/windows.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ok": "Tamam", + "pathNotExistTitle": "Yol yok", + "pathNotExistDetail": "'{0}' yolu artık diskte değil.", + "openWorkspace": "&&Aç", + "openWorkspaceTitle": "Çalışma Alanı Aç", + "save": "&&Kaydet", + "doNotSave": "Kaydet&&me", + "cancel": "İptal", + "saveWorkspaceMessage": "Çalışma alanı yapılandırmanızı bir dosya olarak kaydetmek istiyor musunuz?", + "saveWorkspaceDetail": "Yeniden açmayı planlıyorsanız, çalışma alanınızı kaydedin.", + "saveWorkspace": "Çalışma Alanını Kaydet", + "reopen": "Yeniden Aç", + "wait": "Beklemeye Devam Et", + "close": "Kapat", + "appStalled": "Pencere artık yanıt vermiyor", + "appStalledDetail": "Pencereyi yeniden açabilir, kapatabilir veya bekleyebilirsiniz.", + "appCrashed": "Pencere kilitlendi", + "appCrashedDetail": "Verdiğimiz rahatsızlıktan dolayı özür dileriz! Pencereyi yeniden açıp kaldığınız yerden devam edebilirsiniz.", + "open": "Aç", + "openFolder": "Klasör Aç", + "openFile": "Dosya Aç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/code/node/cliProcessMain.i18n.json b/i18n/trk/src/vs/code/node/cliProcessMain.i18n.json new file mode 100644 index 0000000000..d862993275 --- /dev/null +++ b/i18n/trk/src/vs/code/node/cliProcessMain.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "'{0}' eklentisi bulunamadı.", + "notInstalled": "'{0}' eklentisi yüklü değil.", + "useId": "Eklentinin tam ID'sini, yayıncı da dahil olmak üzere kullandığınızdan emin olun, ör: {0}", + "successVsixInstall": "'{0}' eklentisi başarıyla yüklendi.", + "alreadyInstalled": "'{0}' eklentisi zaten yüklü.", + "foundExtension": "'{0}' markette bulundu.", + "installing": "Yükleniyor...", + "successInstall": "'{0}' v{1} eklentisi başarıyla kuruldu!", + "uninstalling": "{0} kaldırılıyor...", + "successUninstall": "'{0}' eklentisi başarıyla kaldırıldı!" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/browser/widget/diffEditorWidget.i18n.json b/i18n/trk/src/vs/editor/browser/widget/diffEditorWidget.i18n.json new file mode 100644 index 0000000000..a51d84dadd --- /dev/null +++ b/i18n/trk/src/vs/editor/browser/widget/diffEditorWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diff.tooLarge": "Bir dosya çok büyük olduğu için dosyaları karşılaştıramazsınız." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/browser/widget/diffReview.i18n.json b/i18n/trk/src/vs/editor/browser/widget/diffReview.i18n.json new file mode 100644 index 0000000000..ed91f1602a --- /dev/null +++ b/i18n/trk/src/vs/editor/browser/widget/diffReview.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Kapat", + "header": "Farklılık {0}/{1}: orijinal {2}, {3} satırları, değiştirilen {4}, {5} satırları", + "blankLine": "boş", + "equalLine": "orijinal {0}, değiştirilen {1}: {2}", + "insertLine": "+ değiştirilen {0}: {1}", + "deleteLine": "- orijinal {0}: {1}", + "editor.action.diffReview.next": "Sonraki Farka Git", + "editor.action.diffReview.prev": "Sonraki Farka Git" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/trk/src/vs/editor/common/config/commonEditorConfig.i18n.json new file mode 100644 index 0000000000..f512854f5b --- /dev/null +++ b/i18n/trk/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorConfigurationTitle": "Düzenleyici", + "fontFamily": "Yazı tipi ailesini denetler.", + "fontWeight": "Yazı tipi kalınlığını denetler.", + "fontSize": "Yazı tipi boyutunu piksel olarak denetler.", + "lineHeight": "Satır yüksekliğini denetler. lineHeight değerini, fontSize değeri kullanarak hesaplamak için 0 girin.", + "letterSpacing": "Harfler arası boşluğu pixel olarak denetler.", + "lineNumbers": "Satır numaralarının görüntülenmesini denetler. Olası değerler 'on', 'off' ve 'relative'dir. 'relative' satırların geçerli imleç konumundan uzaklıklarını gösterir.", + "rulers": "Dikey cetvellerin gösterileceği sütunlar", + "wordSeparators": "Sözcüklerle ilgili gezinti veya işlem yaparken kelime ayırıcı olarak kullanılacak karakterler", + "tabSize": "Bir sekmenin eşit olduğu boşluk sayısı. Bu ayar, `editor.detectIndentation` açıkken dosya içeriğine bağlı olarak geçersiz kılınır.", + "tabSize.errorMessage": "'sayı' bekleniyor. \"auto\" değerinin `editor.detectIndentation` ile değiştirildiğini unutmayın.", + "insertSpaces": "Tab tuşuna basınca boşluk ekle. Bu ayar, `editor.detectIndentation` açıkken dosya içeriğine bağlı olarak geçersiz kılınır.", + "insertSpaces.errorMessage": "'boole' bekleniyor. \"auto\" değerinin `editor.detectIndentation` ile değiştirildiğini unutmayın.", + "detectIndentation": "Bir dosyayı açarken, `editor.tabSize` ve `editor.insertSpaces` dosya içeriğine bağlı olarak algılanır.", + "roundedSelection": "Seçimlerin köşelerinin yuvarlak olup olmayacağını denetler", + "scrollBeyondLastLine": "Düzenleyicinin son satırın ötesine ilerleyip ilerlemeyeceğini denetler", + "smoothScrolling": "Düzenleyicinin bir animasyon kullanarak kaydırıp kaydırmayacağını denetler", + "minimap.enabled": "Mini haritanın gösterilip gösterilmeyeceğini denetler", + "minimap.showSlider": "Mini harita kaydıracının otomatik olarak gizlenip gizlenmeyeceğini denetler. Alabileceği değerler 'always' ve 'mouseover'dır.", + "minimap.renderCharacters": "(Renk blokları yerine) Bir satırdaki gerçek harfleri göster", + "minimap.maxColumn": "Hazırlanacak mini haritanın azami genişliğini belirli sayıda sütunla sınırla", + "find.seedSearchStringFromSelection": "Bulma Araç Çubuğu'ndaki arama metninin, düzenleyicideki seçili alandan beslenmesini denetler", + "find.autoFindInSelection": "Seçimde bul işaretçisinin, editördeki metnin birden çok karakteri veya satırı seçildiğinde açılmasını denetler.", + "wordWrap.off": "Satırlar hiçbir zaman bir sonraki satıra kaydırılmayacak.", + "wordWrap.on": "Satırlar görüntü alanı genişliğinde bir sonraki satıra kaydırılacak.", + "wordWrap.wordWrapColumn": "Satırlar `editor.wordWrapColumn` değerinde bir sonraki satıra kaydırılacak.", + "wordWrap.bounded": "Satırlar en düşük görüntü alanı genişliğinde ve `editor.wordWrapColumn` değerinde bir sonraki satıra kaydırılacak.", + "wordWrap": "Satırların bir sonraki satıra nasıl kaydırılacağını denetler. Seçenekler:\n - 'off' (kaydırmayı devre dışı bırak),\n - 'on' (görüntü alanında kaydır),\n - 'wordWrapColumn' (`editor.wordWrapColumn` değerinde kaydır) veya\n - 'bounded' (en düşük görüntü alanı genişliğinde ve `editor.wordWrapColumn` değerinde kaydır).", + "wordWrapColumn": "`editor.wordWrap` ögesi, 'wordWrapColumn' veya 'bounded' iken düzenleyicinin kaydırma sütununu denetler.", + "wrappingIndent": "Kaydırılan satır girintisini denetler. 'none', 'same' veya 'indent' değerlerinden biri olabilir.", + "mouseWheelScrollSensitivity": "Fare tekerleği kaydırma olaylarında `deltaX` ve `deltaY` üzerinde kullanılan bir çarpan", + "multiCursorModifier.ctrlCmd": "Windows ve Linux'da `Control` ve OSX'de `Command` ile eşleşir.", + "multiCursorModifier.alt": "Windows ve Linux'da `Alt` ve OSX'de `Option` ile eşleşir.", + "multiCursorModifier": "Fare ile birden çok imleç eklenmesinde kullanılacak değiştirici. `ctrlCmd` Windows ve Linux'da `Control` ve OSX'de `Command` ile eşleşir. Tanıma Git ve Bağlantıyı Aç fare hareketleri, birden çok imleç değiştiricisi ile çakışmayacak şekilde uyum sağlarlar.", + "quickSuggestions.strings": "Dizelerin içinde hızlı önerileri etkinleştir.", + "quickSuggestions.comments": "Yorumların içinde hızlı önerileri etkinleştir.", + "quickSuggestions.other": "Dizeler ve yorumlar dışında hızlı önerileri etkinleştirin.", + "quickSuggestions": "Yazarken önerilerin otomatik olarak gösterilip gösterilmeyeceğini denetler", + "quickSuggestionsDelay": "Hızlı önerilerin gösterilmesinden önce kaç ms bekleneceğini denetler", + "parameterHints": "Siz tuşlara bastıkça parametre belgelerini ve tür bilgisini gösteren açılır pencereyi etkinleştirir.", + "autoClosingBrackets": "Düzenleyicinin köşeli ayracı açtıktan sonra otomatik olarak kapatıp kapatmayacağını denetler", + "formatOnType": "Düzenleyicinin satırı yazıldıktan sonra otomatik biçimlendirip biçimlendirmeyeceğini denetler", + "formatOnPaste": "Düzenleyicinin yapıştırılan içeriği otomatik olarak biçimlendirip biçimlendirmeyeceğini denetler. Bir biçimlendirici mevcut olmalı ve belgede bir aralığı biçimlendirebilmelidir.", + "autoIndent": "Düzenleyicinin, kullanıcılar tuşlara bastığında, satırları yapıştırdığında veya taşıdığında girintiyi otomatik olarak ayarlayıp ayarlamayacağını denetler. Dilin girintileme kuralları mevcut olmalıdır.", + "suggestOnTriggerCharacters": "Tetikleyici karakterler yazılırken otomatik olarak öneri gösterilip gösterilmeyeceğini denetler", + "acceptSuggestionOnEnter": "'Tab' tuşuna ek olarak - önerilerin 'Enter' tuşuna basıldığında kabul edilmesini denetler. Yeni satır ekleme ya da öneri kabul etme arasındaki belirsizlikten kaçınmaya yardımcı olur. 'smart' değeri, bir öneri metinsel değişiklik yapıyorsa, onu sadece Enter tuşu ile kabul etmeyi ifade eder", + "acceptSuggestionOnCommitCharacter": "Önerilerin tamamlama karakterlerinde kabul edilip edilmeyeceğini denetler. Örnek olarak; JavaScript'te noktalı virgül(';') öneri kabul eden ve o karakteri giren tamamlama karakteri olabilir.", + "snippetSuggestions.top": "Parçacık önerilerini diğer önerilerin üstünde göster.", + "snippetSuggestions.bottom": "Parçacık önerilerini diğer önerilerin altında göster.", + "snippetSuggestions.inline": "Parçacık önerilerini diğer önerilerle birlikte göster.", + "snippetSuggestions.none": "Parçacık önerilerini gösterme.", + "snippetSuggestions": "Parçacıkların diğer önerilerle gösterilip gösterilmeyeceğini ve bunların nasıl sıralanacaklarını denetler.", + "emptySelectionClipboard": "Bir seçim olmadan geçerli satırı kopyalayıp kopyalamamayı denetler.", + "wordBasedSuggestions": "Tamamlamaların belgedeki sözcüklere dayalı olarak hesaplanıp hesaplanmayacağını denetler.", + "suggestFontSize": "Öneri aracının yazı tipi boyutu", + "suggestLineHeight": "Öneri aracının satır yüksekliği", + "selectionHighlight": "Düzenleyicinin seçime benzer eşleşmeleri vurgulayıp vurgulamayacağını denetler", + "occurrencesHighlight": "Düzenleyicinin semantik sembol tekrarlamalarını vurgulayıp vurgulamayacağını denetler", + "overviewRulerLanes": "Genel bakış cetvelinde aynı konumda gösterilebilecek süsleme sayısını denetler", + "overviewRulerBorder": "Genel bakış cetvelinin etrafına bir kenarlık çizilmesi gerekip gerekmediğini denetler.", + "cursorBlinking": "İmleç animasyon stilini denetler, olası değerler 'blink', 'smooth', 'phase', 'expand' ve 'solid'dir", + "mouseWheelZoom": "Ctrl tuşuna basarken fare tekerleği ile düzenleyici yazı tipini yakınlaştırın", + "cursorStyle": "İmleç stilini denetler, kabul edilen değerler: 'block', 'block-outline', 'line', 'line-thin', 'underline' ve 'underline-thin'", + "fontLigatures": "Yazı tipi ligatürlerini etkinleştirir", + "hideCursorInOverviewRuler": "İmlecin genel bakış cetvelinde gizlenip gizlenmeyeceğini denetler.", + "renderWhitespace": "Düzenleyicinin boşluk karakterlerini nasıl göstereceğini denetler, seçenekler: 'none', 'boundary', ve 'all'. 'boundary' seçeneği sözcükler arasındaki tek boşlukları göstermez.", + "renderControlCharacters": "Düzenleyicinin kontrol karakterlerini gösterip göstermemesini denetler", + "renderIndentGuides": "Düzenleyicinin girinti kılavuzlarını gösterip göstermemesini denetler", + "renderLineHighlight": "Düzenleyicinin geçerli satır vurgusunu nasıl göstereceğini denetler, seçenekler: 'none', 'gutter', 'line', ve 'all'.", + "codeLens": "Düzenleyicinin kod objektiflerini gösterip göstermediğini denetler", + "folding": "Düzenleyicide kod katlamanın etkin olup olmadığını denetler", + "showFoldingControls": "Oluktaki kat kontrollerinin otomatik olarak gizlenip gizlenmeyeceğini denetler.", + "matchBrackets": "Eşleşen ayraçları, onlardan biri seçildiğinde vurgula.", + "glyphMargin": "Düzenleyicinin dikey glif boşluğunu oluşturup oluşturmayacağını kontrol eder. Glif boşluğu çoğunlukla hata ayıklamak için kullanılır.", + "useTabStops": "Boşluk ekleme ve silme sekme duraklarını izler", + "trimAutoWhitespace": "Sondaki otomatik eklenen boşluğu kaldır", + "stablePeek": "Gözetleme düzenleyicilerini, içeriklerine çift tıklandığında veya Escape tuşuna basıldığında bile açık tut.", + "dragAndDrop": "Düzenleyicinin seçimleri sürükleyip bırakarak taşımaya izin verip vermeyeceğini denetler.", + "accessibilitySupport.auto": "Düzenleyici, bir Ekran Okuyucu'nun ne zaman bağlandığını algılamak için platform API'larını kullanacaktır.", + "accessibilitySupport.on": "Düzenleyici bir Ekran Okuyucu ile kullanılmak üzere kalıcı olarak optimize edilecektir.", + "accessibilitySupport.off": "Düzenleyici hiçbir zaman bir Ekran Okuyucu ile kullanılmak üzere optimize edilmeyecektir.", + "accessibilitySupport": "Düzenleyicinin ekran okuyucular için optimize edilmiş bir modda çalışıp çalışmayacağını denetler.", + "links": "Düzenleyicinin bağlantıları otomatik algılayıp, onları tıklanabilir yapıp yapmayacağını denetler", + "colorDecorators": "Düzenleyicinin satır içi renk dekoratörlerini ve renk seçiciyi gösterip göstermemesini denetler.", + "sideBySide": "Karşılaştırma düzenleyicisinin farklılıkları yan yana mı yoksa satır içinde mi göstereceğini denetler", + "ignoreTrimWhitespace": "Karşılaştırma düzenleyicisinin baştaki veya sondaki boşluklardaki değişmeleri farklılık olarak gösterip göstermemesini denetler", + "renderIndicators": "Karşılaştırma düzenleyicisinin ekleme/çıkarma değişiklikleri için +/- göstergeleri gösterip göstermemesini denetler.", + "selectionClipboard": "Linux birincil panosunun desteklenip desteklenmeyeceğini denetler." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/common/config/editorOptions.i18n.json b/i18n/trk/src/vs/editor/common/config/editorOptions.i18n.json new file mode 100644 index 0000000000..54d9167ff1 --- /dev/null +++ b/i18n/trk/src/vs/editor/common/config/editorOptions.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "accessibilityOffAriaLabel": "Düzenleyici şu an erişilebilir değil. Seçenekler için lütfen Alt+F1'e basın.", + "editorViewAccessibleLabel": "Düzenleyici içeriği" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/common/controller/cursor.i18n.json b/i18n/trk/src/vs/editor/common/controller/cursor.i18n.json new file mode 100644 index 0000000000..f71412ce78 --- /dev/null +++ b/i18n/trk/src/vs/editor/common/controller/cursor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "corrupt.commands": "Komut yürütülürken beklenmeyen özel durum oluştu." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/common/model/textModelWithTokens.i18n.json b/i18n/trk/src/vs/editor/common/model/textModelWithTokens.i18n.json new file mode 100644 index 0000000000..306aedb36c --- /dev/null +++ b/i18n/trk/src/vs/editor/common/model/textModelWithTokens.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mode.tokenizationSupportFailed": "Mod, girdiyi belirteçlere ayırırken başarısız oldu." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/common/modes/modesRegistry.i18n.json b/i18n/trk/src/vs/editor/common/modes/modesRegistry.i18n.json new file mode 100644 index 0000000000..866fc30120 --- /dev/null +++ b/i18n/trk/src/vs/editor/common/modes/modesRegistry.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "plainText.alias": "Düz Metin" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/common/services/bulkEdit.i18n.json b/i18n/trk/src/vs/editor/common/services/bulkEdit.i18n.json new file mode 100644 index 0000000000..011c873e3a --- /dev/null +++ b/i18n/trk/src/vs/editor/common/services/bulkEdit.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "conflict": "Bu dosyalar bu arada değiştirildi: {0}", + "summary.0": "Düzenleme yapılmadı", + "summary.nm": "{1} dosyada {0} metin düzenlemesi yapıldı", + "summary.n0": "Bir dosyada {0} metin düzenlemesi yapıldı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/common/services/modeServiceImpl.i18n.json b/i18n/trk/src/vs/editor/common/services/modeServiceImpl.i18n.json new file mode 100644 index 0000000000..12d405c5e7 --- /dev/null +++ b/i18n/trk/src/vs/editor/common/services/modeServiceImpl.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Dil bildirimlerine ekleme yapar.", + "vscode.extension.contributes.languages.id": "Dilin ID'si.", + "vscode.extension.contributes.languages.aliases": "Dilin takma adları.", + "vscode.extension.contributes.languages.extensions": "Dil ile ilişkili dosya uzantıları.", + "vscode.extension.contributes.languages.filenames": "Dil ile ilişkili dosya adları.", + "vscode.extension.contributes.languages.filenamePatterns": "Dil ile ilişkili dosya adı glob desenleri.", + "vscode.extension.contributes.languages.mimetypes": "Dil ile ilişkili MIME türleri.", + "vscode.extension.contributes.languages.firstLine": "Dilin bir dosyasının ilk satırıyla eşleşen bir düzenli ifade.", + "vscode.extension.contributes.languages.configuration": "Dil için yapılandırma seçenekleri içeren dosyaya, bir göreli yol." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/common/services/modelServiceImpl.i18n.json b/i18n/trk/src/vs/editor/common/services/modelServiceImpl.i18n.json new file mode 100644 index 0000000000..6cf106a992 --- /dev/null +++ b/i18n/trk/src/vs/editor/common/services/modelServiceImpl.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diagAndSourceMultiline": "[{0}]\n{1}", + "diagAndSource": "[{0}] {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/trk/src/vs/editor/common/view/editorColorRegistry.i18n.json new file mode 100644 index 0000000000..af1fcb35bb --- /dev/null +++ b/i18n/trk/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lineHighlight": "İmlecin bulunduğu satırın vurgusunun arka plan rengi.", + "lineHighlightBorderBox": "İmlecin bulunduğu satırın kenarlığının arka plan rengi.", + "rangeHighlight": "Hızlı açma ve bulma özellikleri gibi vurgulanan alanların arka plan rengi.", + "caret": "Düzenleyici imlecinin rengi.", + "editorCursorBackground": "Düzenleyici imlecinin arka plan rengi. Bir blok imlecinin kapladığı bir karakterin rengini özelleştirmeyi sağlar.", + "editorWhitespaces": "Düzenleyicideki boşluk karakterlerinin rengi.", + "editorIndentGuides": "Düzenleyici girinti kılavuzlarının rengi.", + "editorLineNumbers": "Düzenleyici satır numaralarının rengi.", + "editorRuler": "Düzenleyici cetvellerinin rengi.", + "editorCodeLensForeground": "Düzenleyici kod objektiflerinin ön plan rengi", + "editorBracketMatchBackground": "Eşleşen parantezlerin arka plan rengi", + "editorBracketMatchBorder": "Eşleşen parantez kutularının rengi", + "editorOverviewRulerBorder": "Genel bakış cetvelinin kenarlık rengi.", + "editorGutter": "Düzenleyici oluğunun arka plan rengi. Oluk, glif boşluklarını ve satır numaralarını içerir.", + "errorForeground": "Düzenleyicideki hata karalamalarının ön plan rengi.", + "errorBorder": "Düzenleyicideki hata karalamalarının kenarlık rengi.", + "warningForeground": "Düzenleyicideki uyarı karalamalarının ön plan rengi.", + "warningBorder": "Düzenleyicideki uyarı karalamalarının kenarlık rengi.", + "overviewRulerRangeHighlight": "Aralık vurguları için genel bakış cetvelinin işaretleyici rengi.", + "overviewRuleError": "Hatalar için genel bakış cetvelinin işaretleyici rengi.", + "overviewRuleWarning": "Uyarılar için genel bakış cetvelinin işaretleyici rengi.", + "overviewRuleInfo": "Bilgilendirmeler için genel bakış cetvelinin işaretleyici rengi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json b/i18n/trk/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json new file mode 100644 index 0000000000..46ed3a89aa --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/bracketMatching/common/bracketMatching.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.jumpBracket": "Ayraca Git" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json b/i18n/trk/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json new file mode 100644 index 0000000000..be216da614 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/caretOperations/common/caretOperations.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "caret.moveLeft": "İmleci Sola Taşı", + "caret.moveRight": "İmleci Sağa Taşı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json b/i18n/trk/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json new file mode 100644 index 0000000000..ead2f75371 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/caretOperations/common/transpose.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "transposeLetters.label": "Harfleri Birbirleriyle Değiştir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json b/i18n/trk/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json new file mode 100644 index 0000000000..c27413d4bd --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/clipboard/browser/clipboard.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "actions.clipboard.cutLabel": "Kes", + "actions.clipboard.copyLabel": "Kopyala", + "actions.clipboard.pasteLabel": "Yapıştır", + "actions.clipboard.copyWithSyntaxHighlightingLabel": "Sentaks Vurgulaması İle Kopyala" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/comment/common/comment.i18n.json b/i18n/trk/src/vs/editor/contrib/comment/common/comment.i18n.json new file mode 100644 index 0000000000..d36f2a57eb --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/comment/common/comment.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "comment.line": "Satır Yorumunu Aç/Kapat", + "comment.line.add": "Satır Açıklaması Ekle", + "comment.line.remove": "Satır Açıklamasını Kaldır", + "comment.block": "Yorum Bloğunu Aç/Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json b/i18n/trk/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json new file mode 100644 index 0000000000..8a9ddbaa58 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/contextmenu/browser/contextmenu.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "action.showContextMenu.label": "Düzenleyici Bağlam Menüsünü Göster" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/trk/src/vs/editor/contrib/find/browser/findWidget.i18n.json new file mode 100644 index 0000000000..626d7ebda9 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Bul", + "placeholder.find": "Bul", + "label.previousMatchButton": "Önceki eşleşme", + "label.nextMatchButton": "Sonraki eşleşme", + "label.toggleSelectionFind": "Seçimde bul", + "label.closeButton": "Kapat", + "label.replace": "Değiştir", + "placeholder.replace": "Değiştir", + "label.replaceButton": "Değiştir", + "label.replaceAllButton": "Tümünü Değiştir", + "label.toggleReplaceButton": "Değiştirme modunu değiştir", + "title.matchesCountLimit": "Yalnızca ilk 999 sonuç vurgulandı, ancak tüm bulma işlemleri metnin tamamı üzerinde çalışıyor.", + "label.matchesLocation": "{0}/{1}", + "label.noResults": "Sonuç Yok" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json b/i18n/trk/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json new file mode 100644 index 0000000000..ef27920f7e --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/find/browser/simpleFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Bul", + "placeholder.find": "Bul", + "label.previousMatchButton": "Önceki eşleşme", + "label.nextMatchButton": "Sonraki eşleşme", + "label.closeButton": "Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/trk/src/vs/editor/contrib/find/common/findController.i18n.json new file mode 100644 index 0000000000..9d71b05f86 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/find/common/findController.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "startFindAction": "Bul", + "findNextMatchAction": "Sonrakini Bul", + "findPreviousMatchAction": "Öncekini Bul", + "nextSelectionMatchFindAction": "Sonraki Seçimi Bul", + "previousSelectionMatchFindAction": "Önceki Seçimi Bul", + "startReplace": "Değiştir", + "addSelectionToNextFindMatch": "Seçimi Sonraki Bulunan Eşleşmeye Ekle", + "addSelectionToPreviousFindMatch": "Seçimi Önceki Bulunan Eşleşmeye Ekle", + "moveSelectionToNextFindMatch": "Son Seçimi Sonraki Bulunan Eşleşmeye Taşı", + "moveSelectionToPreviousFindMatch": "Son Seçimi Önceki Bulunan Eşleşmeye Taşı", + "selectAllOccurrencesOfFindMatch": "Bulunan Eşleşmenin Tüm Tekrarlamalarını Seç", + "changeAll.label": "Tüm Tekrarlamaları Değiştir", + "showNextFindTermAction": "Sonraki Arama Terimini Göster", + "showPreviousFindTermAction": "Önceki Arama Terimini Göster" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/folding/browser/folding.i18n.json b/i18n/trk/src/vs/editor/contrib/folding/browser/folding.i18n.json new file mode 100644 index 0000000000..23c4b18476 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/folding/browser/folding.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unfoldAction.label": "Katlamayı Aç", + "unFoldRecursivelyAction.label": "Katlamaları Özyinelemeli Olarak Aç", + "foldAction.label": "Katla", + "foldRecursivelyAction.label": "Özyinelemeli Olarak Katla", + "foldAllAction.label": "Hepsini Katla", + "unfoldAllAction.label": "Tüm Katlamaları Aç", + "foldLevelAction.label": "{0}. Düzeyi Katla" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/format/browser/formatActions.i18n.json b/i18n/trk/src/vs/editor/contrib/format/browser/formatActions.i18n.json new file mode 100644 index 0000000000..11d6d3144d --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/format/browser/formatActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint11": "{0}. satırda 1 biçimlendirme düzenlemesi yapıldı", + "hintn1": "{1}. satırda {0} biçimlendirme düzenlemesi yapıldı", + "hint1n": "{0} ve {1} satırları arasında 1 biçimlendirme düzenlemesi yapıldı", + "hintnn": "{1} ve {2} satırları arasında {0} biçimlendirme düzenlemesi yapıldı", + "formatDocument.label": "Belgeyi Biçimlendir", + "formatSelection.label": "Seçimi Biçimlendir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json b/i18n/trk/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json new file mode 100644 index 0000000000..8115e4cf6a --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultWord": "'{0}' için tanım bulunamadı", + "generic.noResults": "Tanım bulunamadı", + "meta.title": " – {0} tanım", + "actions.goToDecl.label": "Tanıma Git", + "actions.goToDeclToSide.label": "Tanımı Yana Aç", + "actions.previewDecl.label": "Tanıma Göz At", + "goToImplementation.noResultWord": "'{0}' için uygulama bulunamadı", + "goToImplementation.generic.noResults": "Uygulama bulunamadı", + "meta.implementations.title": " – {0} uygulama", + "actions.goToImplementation.label": "Uygulamaya Git", + "actions.peekImplementation.label": "Uygulamaya Göz At", + "goToTypeDefinition.noResultWord": "'{0}' için tür tanımı bulunamadı", + "goToTypeDefinition.generic.noResults": "Tür tanımı bulunamadı", + "meta.typeDefinitions.title": " – {0} tür tanımı", + "actions.goToTypeDefinition.label": "Tür Tanımına Git", + "actions.peekTypeDefinition.label": "Tür Tanımına Göz At" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json b/i18n/trk/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json new file mode 100644 index 0000000000..4ac4fd8ca6 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "multipleResults": "{0} tanımı göstermek için tıklayın." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json b/i18n/trk/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json new file mode 100644 index 0000000000..d2b02e8dfc --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/gotoError/browser/gotoError.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "title.wo_source": "({0}/{1})", + "markerAction.next.label": "Sonraki Hata veya Uyarıya Git", + "markerAction.previous.label": "Önceki Hata veya Uyarıya Git", + "editorMarkerNavigationError": "Düzenleyicinin işaretçi gezinti aracının hata rengi.", + "editorMarkerNavigationWarning": "Düzenleyicinin işaretçi gezinti aracının uyarı rengi.", + "editorMarkerNavigationBackground": "Düzenleyicinin işaretçi gezinti aracının arka planı." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/hover/browser/hover.i18n.json b/i18n/trk/src/vs/editor/contrib/hover/browser/hover.i18n.json new file mode 100644 index 0000000000..cc9e863a76 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/hover/browser/hover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showHover": "Bağlantı Vurgusunu Göster" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json b/i18n/trk/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json new file mode 100644 index 0000000000..733ab5a295 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/hover/browser/modesContentHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "modesContentHover.loading": "Yükleniyor..." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json b/i18n/trk/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json new file mode 100644 index 0000000000..72ff855265 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "InPlaceReplaceAction.previous.label": "Önceki Değerle Değiştir", + "InPlaceReplaceAction.next.label": "Sonraki Değerle Değiştir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/indentation/common/indentation.i18n.json b/i18n/trk/src/vs/editor/contrib/indentation/common/indentation.i18n.json new file mode 100644 index 0000000000..16b116edc4 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/indentation/common/indentation.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "indentationToSpaces": "Girintiyi Boşluklara Dönüştür", + "indentationToTabs": "Girintiyi Sekmelere Dönüştür", + "configuredTabSize": "Yapılandırılmış Sekme Boyutu", + "selectTabWidth": "Geçerli Dosya İçin Sekme Boyutunu Seç", + "indentUsingTabs": "Sekme Kullanarak Girintile", + "indentUsingSpaces": "Boşluk Kullanarak Girintile", + "detectIndentation": "Girintiyi, İçeriği Kontrol Ederek Algıla", + "editor.reindentlines": "Satır Girintilerini Yeniden Ayarla" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json b/i18n/trk/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json new file mode 100644 index 0000000000..b8d2c0eae4 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/linesOperations/common/linesOperations.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "lines.copyUp": "Satırı Yukarı Kopyala", + "lines.copyDown": "Satırı Aşağı Kopyala", + "lines.moveUp": "Satırı Yukarı Taşı", + "lines.moveDown": "Satırı Aşağı Taşı", + "lines.sortAscending": "Satırları Artan Şekilde Sırala", + "lines.sortDescending": "Satırları Azalan Şekilde Sırala", + "lines.trimTrailingWhitespace": "Sondaki Boşluğu Kırp", + "lines.delete": "Satırı Sil", + "lines.indent": "Satırı Girintile", + "lines.outdent": "Satırın Girintisini Azalt", + "lines.insertBefore": "Üste Satır Ekle", + "lines.insertAfter": "Alta Satır Ekle", + "lines.deleteAllLeft": "Soldaki Her Şeyi Sil", + "lines.deleteAllRight": "Sağdaki Her Şeyi Sil", + "lines.joinLines": "Satırları Birleştir", + "editor.transpose": "İmlecin etrafındaki karakterleri birbirleriyle değiştir", + "editor.transformToUppercase": "Büyük Harfe Dönüştür", + "editor.transformToLowercase": "Küçük Harfe Dönüştür" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/links/browser/links.i18n.json b/i18n/trk/src/vs/editor/contrib/links/browser/links.i18n.json new file mode 100644 index 0000000000..57875feeab --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/links/browser/links.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "links.navigate.mac": "Bağlantıyı izlemek için Cmd tuşuna basarak tıklayın", + "links.navigate": "Bağlantıyı izlemek için Ctrl tuşuna basarak tıklayın", + "links.command.mac": "Komutu yürütmek için Cmd + tıklama yapın", + "links.command": "Komutu yürütmek için Ctrl + tıklama yapın", + "links.navigate.al": "Bağlantıyı izlemek için Alt tuşuna basarak tıklayın", + "links.command.al": "Komutu yürütmek için Alt + tıklama yapın", + "invalid.url": "Üzgünüz, bu bağlantı iyi oluşturulmamış olduğu için açılamadı: {0}", + "missing.url": "Üzgünüz; bu bağlantı, hedefi eksik olduğu için açılamadı.", + "label": "Bağlantıyı Aç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/trk/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json new file mode 100644 index 0000000000..5a5255b3d5 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mutlicursor.insertAbove": "Yukarıya İmleç Ekle", + "mutlicursor.insertBelow": "Aşağıya İmleç Ekle", + "mutlicursor.insertAtEndOfEachLineSelected": "Satır Sonlarına İmleç Ekle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json b/i18n/trk/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json new file mode 100644 index 0000000000..e58a4552d4 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/parameterHints/browser/parameterHints.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parameterHints.trigger.label": "Parametre İpuçlarını Tetikle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json b/i18n/trk/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json new file mode 100644 index 0000000000..1f6df7acf3 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hint": "{0}, ipucu" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json b/i18n/trk/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json new file mode 100644 index 0000000000..a57d09be68 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/quickFix/browser/quickFixCommands.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickFixWithKb": "Düzeltmeleri Göster ({0})", + "quickFix": "Düzeltmeleri Göster", + "quickfix.trigger.label": "Hızlı Düzeltme" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json b/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json new file mode 100644 index 0000000000..e6b96f8345 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "meta.titleReference": "– {0} başvuru", + "references.action.label": "Tüm Başvuruları Bul" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json b/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json new file mode 100644 index 0000000000..0b90727c71 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesController.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "labelLoading": "Yükleniyor..." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json b/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json new file mode 100644 index 0000000000..74e5cf75d5 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesModel.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "aria.oneReference": "{0} yolunda, {1}. satır {2}. sütundaki sembol", + "aria.fileReferences.1": "{0} içinde 1 sembol, tam yol {1}", + "aria.fileReferences.N": "{1} içinde {0} sembol, tam yol {2}", + "aria.result.0": "Sonuç bulunamadı", + "aria.result.1": "{0} yolunda 1 sembol bulundu", + "aria.result.n1": "{1} yolunda {0} sembol bulundu", + "aria.result.nm": "{1} dosyada {0} sembol bulundu" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json b/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json new file mode 100644 index 0000000000..b3430a86f0 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.i18n.json @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "referencesFailre": "Dosya çözümlenemedi.", + "referencesCount": "{0} başvuru", + "referenceCount": "{0} başvuru", + "missingPreviewMessage": "önizleme yok", + "treeAriaLabel": "Başvurular", + "noResults": "Sonuç yok", + "peekView.alternateTitle": "Başvurular", + "peekViewTitleBackground": "Gözetleme görünümü başlık alanının arka plan rengi.", + "peekViewTitleForeground": "Gözetleme görünümü başlığının rengi.", + "peekViewTitleInfoForeground": "Gözetleme görünümü başlık bilgisinin rengi.", + "peekViewBorder": "Gözetleme görünümü kenarlıkları ve ok işaretinin rengi.", + "peekViewResultsBackground": "Gözetleme görünümü sonuç listesinin arka plan rengi.", + "peekViewResultsMatchForeground": "Gözetleme görünümü sonuç listesindeki satır düğümlerinin ön plan rengi.", + "peekViewResultsFileForeground": "Gözetleme görünümü sonuç listesindeki dosya düğümlerinin ön plan rengi.", + "peekViewResultsSelectionBackground": "Gözetleme görünümü sonuç listesindeki seçilen girdinin arka plan rengi.", + "peekViewResultsSelectionForeground": "Gözetleme görünümü sonuç listesindeki seçilen girdinin ön plan rengi.", + "peekViewEditorBackground": "Gözetleme görünümü düzenleyicisinin arka plan rengi.", + "peekViewEditorGutterBackground": "Gözetleme görünümü düzenleyicisindeki oluğun arka plan rengi.", + "peekViewResultsMatchHighlight": "Gözetleme görünümü sonuç listesindeki eşleşme vurgusu rengi.", + "peekViewEditorMatchHighlight": "Gözetleme görünümü düzenleyicisindeki eşleşme vurgusu rengi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/rename/browser/rename.i18n.json b/i18n/trk/src/vs/editor/contrib/rename/browser/rename.i18n.json new file mode 100644 index 0000000000..095273fd74 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/rename/browser/rename.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "no result": "Sonuç yok.", + "aria": "'{0}', '{1}' olarak başarıyla yeniden adlandırıldı. Özet: {2}", + "rename.failed": "Üzgünüz, yeniden adlandırma işlemi başarısız oldu.", + "rename.label": "Sembolü Yeniden Adlandır" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json b/i18n/trk/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json new file mode 100644 index 0000000000..1933a3079b --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/rename/browser/renameInputField.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "renameAriaLabel": "Girdiyi yeniden adlandır. Yeni adı girin ve işlemek için Enter'a basın." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json b/i18n/trk/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json new file mode 100644 index 0000000000..f3134095a8 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/smartSelect/common/smartSelect.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "smartSelect.grow": "Seçimi Genişlet", + "smartSelect.shrink": "Seçimi Daralt" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json b/i18n/trk/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json new file mode 100644 index 0000000000..c77823f295 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/suggest/browser/suggestController.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "arai.alert.snippet": "'{0}' kabul edildiği için şu metin eklendi: {1}", + "suggest.trigger.label": "Öneriyi Tetikle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json b/i18n/trk/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json new file mode 100644 index 0000000000..df7d73c174 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/suggest/browser/suggestWidget.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorSuggestWidgetBackground": "Öneri aracının arka plan rengi.", + "editorSuggestWidgetBorder": "Öneri aracının kenarlık rengi.", + "editorSuggestWidgetForeground": "Öneri aracının ön plan rengi.", + "editorSuggestWidgetSelectedBackground": "Öneri aracındaki seçilen girdinin arka plan rengi.", + "editorSuggestWidgetHighlightForeground": "Öneri aracındaki eşleşme vurgularının rengi.", + "readMore": "Devamını Oku...{0}", + "suggestionWithDetailsAriaLabel": "{0}, öneri, detaylı", + "suggestionAriaLabel": "{0}, öneri", + "readLess": "Daha azını oku...{0}", + "suggestWidget.loading": "Yükleniyor...", + "suggestWidget.noSuggestions": "Öneri yok.", + "suggestionAriaAccepted": "{0}, kabul edildi", + "ariaCurrentSuggestionWithDetails": "{0}, öneri, detaylı", + "ariaCurrentSuggestion": "{0}, öneri" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json b/i18n/trk/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json new file mode 100644 index 0000000000..9db78fb7c1 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.tabMovesFocus": "Tab Tuşu İle Odak Değiştirmeyi Aç/Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json b/i18n/trk/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json new file mode 100644 index 0000000000..2113e4144c --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordHighlight": "Bir değişkeni okumak gibi, okuma-erişimi sırasındaki bir sembolün arka plan rengi.", + "wordHighlightStrong": "Bir değişkene yazmak gibi, yazma-erişimi sırasındaki bir sembolün arka plan rengi.", + "overviewRulerWordHighlightForeground": "Sembol vurguları için genel bakış cetvelinin işaretleyici rengi.", + "overviewRulerWordHighlightStrongForeground": "Yazma erişimli sembol vurguları için genel bakış cetvelinin işaretleyici rengi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json b/i18n/trk/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json new file mode 100644 index 0000000000..e24e66f1b1 --- /dev/null +++ b/i18n/trk/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.close": "Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json b/i18n/trk/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json new file mode 100644 index 0000000000..e0a16b35ad --- /dev/null +++ b/i18n/trk/src/vs/editor/electron-browser/textMate/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "`contributes.{0}.language` ögesinde bilinmeyen dil. Sağlanan değer: {1}", + "invalid.scopeName": "`contributes.{0}.scopeName` ögesinde dize bekleniyor. Sağlanan değer: {1}", + "invalid.path.0": "`contributes.{0}.path` ögesinde dize bekleniyor. Sağlanan değer: {1}", + "invalid.injectTo": "`contributes.{0}.injectTo` ögesinde geçersiz değer. Dil kapsam adlarından oluşan bir dizi olmalıdır. Sağlanan değer: {1}", + "invalid.embeddedLanguages": "`contributes.{0}.embeddedLanguages` ögesinde geçersiz değer. Kapsam adından dile kadar olan bir nesne haritası olmalıdır. Sağlanan değer: {1}", + "invalid.path.1": "`contributes.{0}.path` ögesinin ({1}) eklentinin klasöründe ({2}) yer alması bekleniyor. Bu, eklentiyi taşınamaz yapabilir.", + "no-tm-grammar": "Bu dil için kayıtlı bir TM Grameri yok." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json b/i18n/trk/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..a28514a050 --- /dev/null +++ b/i18n/trk/src/vs/editor/node/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "{0} için ayrıştırma hataları: {1}", + "schema.openBracket": "Açılış ayracı karakteri veya dize sırası.", + "schema.closeBracket": "Kapanış ayracı karakteri veya dize sırası.", + "schema.comments": "Yorum sembollerini tanımlar.", + "schema.blockComments": "Blok açıklamalarının nasıl işaretlendiğini tanımlar.", + "schema.blockComment.begin": "Blok açıklamasını başlatan karakter dizisi.", + "schema.blockComment.end": "Blok açıklamasını bitiren karakter dizisi.", + "schema.lineComment": "Satır açıklamasını başlatan karakter dizisi.", + "schema.brackets": "Girintiyi artıran veya azaltan ayraç sembollerini tanımlar.", + "schema.autoClosingPairs": "Ayraç çiftlerini tanımlar. Bir açılış ayracı girildiğinde, kapanış ayracı otomatik olarak eklenir.", + "schema.autoClosingPairs.notIn": "Otomatik çiftlerin devre dışı bırakıldığı bir kapsamlar listesi tanımlar.", + "schema.surroundingPairs": "Seçili bir dizeyi çevrelemek için kullanılabilecek ayraç çiftlerini tanımlar.", + "schema.wordPattern": "Dilin kelime tanımı.", + "schema.wordPattern.pattern": "Sözcükleri eşleştirmek için kullanılacak Düzenli İfade.", + "schema.wordPattern.flags": "Sözcükleri eşleştirmek için kullanılacak Düzenli İfade işaretleri.", + "schema.wordPattern.flags.errorMessage": "`/^([gimuy]+)$/` kalıbı ile eşleşmelidir." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/node/textMate/TMGrammars.i18n.json b/i18n/trk/src/vs/editor/node/textMate/TMGrammars.i18n.json new file mode 100644 index 0000000000..33f4b46b0a --- /dev/null +++ b/i18n/trk/src/vs/editor/node/textMate/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "textmate tokenizerlere ekleme yapar.", + "vscode.extension.contributes.grammars.language": "Bu söz diziminin ekleneceği dil tanımlayıcısı.", + "vscode.extension.contributes.grammars.scopeName": "tmLanguage dosyası tarafından kullanılan Textmate kapsam adı.", + "vscode.extension.contributes.grammars.path": "tmLanguage dosyasının yolu. Yol, eklenti klasörüne görecelidir ve genellikle './syntaxes/' ile başlar.", + "vscode.extension.contributes.grammars.embeddedLanguages": "Bu dil bilgisinin gömülü dilleri içermesi durumundaki bir dil kimliğinin kapsam adı haritası.", + "vscode.extension.contributes.grammars.injectTo": "Bu dil bilgisinin yerleştirileceği dil kapsam adları listesi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/actions/browser/menuItemActionItem.i18n.json b/i18n/trk/src/vs/platform/actions/browser/menuItemActionItem.i18n.json new file mode 100644 index 0000000000..ea8b67f6c6 --- /dev/null +++ b/i18n/trk/src/vs/platform/actions/browser/menuItemActionItem.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "titleAndKb": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/trk/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json new file mode 100644 index 0000000000..ff18160b8d --- /dev/null +++ b/i18n/trk/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "menü ögeleri bir dizi olmalıdır", + "requirestring": "`{0}` özelliği zorunludur ve `string` türünde olmalıdır", + "optstring": "`{0}` özelliği atlanabilir veya `string` türünde olmalıdır", + "vscode.extension.contributes.menuItem.command": "Yürütülecek komutun tanımlayıcısı. Komut 'commands' bölümünde tanımlanmalıdır", + "vscode.extension.contributes.menuItem.alt": "Yürütülecek alternatif bir komutun tanımlayıcısı. Komut 'commands' bölümünde tanımlanmalıdır", + "vscode.extension.contributes.menuItem.when": "Bu ögeyi göstermek için doğru olması gereken koşul", + "vscode.extension.contributes.menuItem.group": "Bu komutun ait olduğu gruba ekle", + "vscode.extension.contributes.menus": "Düzenleyiciye menü ögeleri ekler", + "menus.commandPalette": "Komut Paleti", + "menus.editorTitle": "Düzenleyici başlık menüsü", + "menus.editorContext": "Düzenleyici bağlam menüsü", + "menus.explorerContext": "Dosya gezgini bağlam menüsü", + "menus.editorTabContext": "Düzenleyici sekmeleri bağlam menüsü", + "menus.debugCallstackContext": "Hata ayıklama çağrı yığını bağlam menüsü", + "menus.scmTitle": "Kaynak Denetimi başlık menüsü", + "menus.resourceGroupContext": "Kaynak Denetimi kaynak grubu bağlam menüsü", + "menus.resourceStateContext": "Kaynak Denetimi kaynak durumu bağlam menüsü", + "view.viewTitle": "Katkıda bulunan görünümü başlık menüsü", + "view.itemContext": "Katkıda bulunan görünümü öge bağlam menüsü", + "nonempty": "boş olmayan değer beklendi.", + "opticon": "`icon` özelliği atlanabilir veya bir dize ya da `{dark, light}` gibi bir değişmez değer olmalıdır", + "requireStringOrObject": "`{0}` özelliği zorunludur ve `string` veya `object` türünde olmalıdır", + "requirestrings": "`{0}` ve `{1}` özellikleri zorunludur ve `string` türünde olmalıdır", + "vscode.extension.contributes.commandType.command": "Yürütülecek komutun tanımlayıcısı", + "vscode.extension.contributes.commandType.title": "Komutu kullanıcı arayüzünde temsil edecek başlık", + "vscode.extension.contributes.commandType.category": "(İsteğe Bağlı) Kullanıcı arayüzünde komutun gruplanacağı kategori dizesi", + "vscode.extension.contributes.commandType.icon": "(İsteğe Bağlı) Komutun, Kullanıcı Arayüzü'nde temsil edilmesinde kullanılacak simge. Bir dosya yolu veya tema olarak kullanılabilir bir yapılandırmadır", + "vscode.extension.contributes.commandType.icon.light": "Açık bir tema kullanıldığındaki simge yolu", + "vscode.extension.contributes.commandType.icon.dark": "Koyu bir tema kullanıldığındaki simge yolu", + "vscode.extension.contributes.commands": "Komut paletine komutlar ekler.", + "dup": "`{0}` komutu `commands` bölümünde birden çok kez görünüyor.", + "menuId.invalid": "`{0}` geçerli bir menü tanımlayıcısı değil", + "missing.command": "Menü ögesi, 'commands' bölümünde tanımlanmamış bir `{0}` komutuna başvuruyor.", + "missing.altCommand": "Menü ögesi, 'commands' bölümünde tanımlanmamış bir `{0}` alternatif komutuna başvuruyor.", + "dupe.command": "Menü ögesi, aynı varsayılan ve alternatif komutlarına başvuruyor", + "nosupport.altCommand": "Üzgünüz, fakat sadece 'editor/title' menüsünün 'navigation' grubu alternatif komutları destekliyor" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/configuration/common/configurationRegistry.i18n.json b/i18n/trk/src/vs/platform/configuration/common/configurationRegistry.i18n.json new file mode 100644 index 0000000000..6d63565196 --- /dev/null +++ b/i18n/trk/src/vs/platform/configuration/common/configurationRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultConfigurations.title": "Varsayılan Yapılandırma Geçersiz Kılmaları", + "overrideSettings.description": "{0} dili için geçersiz kılınacak düzenleyici ayarlarını yapılandırın.", + "overrideSettings.defaultDescription": "Bir dil için geçersiz kılınacak düzenleyici ayarlarını yapılandırın.", + "config.property.languageDefault": "'{0}' kaydedilemiyor. Bu, dile özgü düzenleyici ayarlarını tanımlamak için '\\\\[.*\\\\]$' özellik kalıbı ile eşleşir. 'configurationDefaults' ögesini kullanın.", + "config.property.duplicate": "'{0}' kaydedilemiyor. Bu özellik zaten kayıtlı." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/environment/node/argv.i18n.json b/i18n/trk/src/vs/platform/environment/node/argv.i18n.json new file mode 100644 index 0000000000..13e067f1be --- /dev/null +++ b/i18n/trk/src/vs/platform/environment/node/argv.i18n.json @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoValidation": "`--goto` modundaki argümanlar `FILE(:LINE(:CHARACTER))` biçiminde olmalıdır.", + "diff": "İki dosyayı birbiriyle karşılaştır.", + "add": "Son aktif pencereye klasör(ler) ekle.", + "goto": "Konumdaki bir dosyayı belirtilen satır ve sütunda aç.", + "locale": "Kullanılacak yerel dil (örnek: en-US veya zh-TW).", + "newWindow": "Yeni bir Code örneğini zorla.", + "performance": "'Geliştirici: Başlangıç Performansı' komutu etkinleştirilmiş olarak başlat.", + "prof-startup": "Başlangıç sırasında CPU profil oluşturucusunu çalıştır", + "reuseWindow": "Bir dosya veya klasörü son etkin pencerede açmaya zorlayın.", + "userDataDir": "Kullanıcı verilerinin tutulacağı klasörü belirtir, root olarak çalışırken yararlıdır.", + "verbose": "Ayrıntılı çıktı oluştur (--wait anlamına gelir).", + "wait": "Geri dönmeden önce pencerenin kapanmasını bekle.", + "extensionHomePath": "Eklentilerin kök dizinini belirle.", + "listExtensions": "Yüklü eklentileri listele.", + "showVersions": "--list-extensions'u kullanırken, yüklü eklentilerin sürümlerini gösterir.", + "installExtension": "Bir eklenti yükler.", + "uninstallExtension": "Bir eklentiyi kaldırır.", + "experimentalApis": "Bir eklenti için önerilen API özelliklerini etkinleştirir.", + "disableExtensions": "Yüklü tüm eklentileri devre dışı bırak.", + "disableGPU": "GPU donanım hızlandırmasını devre dışı bırak.", + "version": "Sürümü göster.", + "help": "Kullanımı göster.", + "usage": "Kullanım", + "options": "seçenekler", + "paths": "yollar", + "optionsUpperCase": "Seçenekler" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json b/i18n/trk/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json new file mode 100644 index 0000000000..fd8f3c7b18 --- /dev/null +++ b/i18n/trk/src/vs/platform/extensionManagement/common/extensionEnablementService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Çalışma alanı yok." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json b/i18n/trk/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json new file mode 100644 index 0000000000..9345139d88 --- /dev/null +++ b/i18n/trk/src/vs/platform/extensionManagement/common/extensionManagement.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Eklentiler", + "preferences": "Tercihler" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json b/i18n/trk/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json new file mode 100644 index 0000000000..a246ddc00e --- /dev/null +++ b/i18n/trk/src/vs/platform/extensionManagement/node/extensionGalleryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notFound": "Eklenti bulunamadı", + "noCompatible": "{0} eklentisinin Code'un bu sürümüyle uyumlu bir sürümü bulunamadı." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/trk/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json new file mode 100644 index 0000000000..03b9574815 --- /dev/null +++ b/i18n/trk/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalidManifest": "Eklenti geçersiz: package.json bir JSON dosyası değil.", + "restartCode": "{0} eklentisini yeniden yüklemeden önce lütfen Code'u yeniden başlatın.", + "installDependeciesConfirmation": "'{0}' eklentisini yüklediğinizde onun bağımlılıkları da yüklenir. Devam etmek istiyor musunuz?", + "install": "Evet", + "doNotInstall": "Hayır", + "uninstallDependeciesConfirmation": "Yalnızca '{0}' eklentisini mi yoksa bağımlılıklarını da kaldırmak ister misiniz?", + "uninstallOnly": "Sadece Eklenti", + "uninstallAll": "Tümü", + "cancel": "İptal", + "uninstallConfirmation": "'{0}' eklentisini kaldırmak istediğinizden emin misiniz?", + "ok": "Tamam", + "singleDependentError": "'{0}' eklentisi kaldırılamıyor. '{1}' eklentisi buna bağlı.", + "twoDependentsError": "'{0}' eklentisi kaldırılamıyor. '{1}' ve '{2}' eklentileri buna bağlı.", + "multipleDependentsError": "'{0}' eklentisi kaldırılamıyor. '{1}, '{2}' eklentileri ve diğerleri buna bağlı.", + "notExists": "Eklenti bulunamadı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/extensions/common/abstractExtensionService.i18n.json b/i18n/trk/src/vs/platform/extensions/common/abstractExtensionService.i18n.json new file mode 100644 index 0000000000..fe18d791c3 --- /dev/null +++ b/i18n/trk/src/vs/platform/extensions/common/abstractExtensionService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "`{1}` eklentisi etkinleştirilemedi. Neden: bilinmeyen bağımlılık `{0}`.", + "failedDep1": "`{1}` eklentisi etkinleştirilemedi. Neden: bağımlılık `{0}` etkinleştirilemedi.", + "failedDep2": "`{0}` eklentisi etkinleştirilemedi. Neden: 10'dan fazla bağımlılık düzeyi (büyük olasılıkla bağımlılık döngüsü).", + "activationError": "`{0}` eklentisi etkinleştirilemedi: {1}." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/trk/src/vs/platform/extensions/common/extensionsRegistry.i18n.json new file mode 100644 index 0000000000..99c127bc49 --- /dev/null +++ b/i18n/trk/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.engines.vscode": "Eklentinin uyumlu olduğu VS Code sürümünü belirten VS Code eklentileri için. * olamaz. Örneğin: ^0.10.5 uyumlu olduğu minimum VS Code sürümünün 0.10.5 olduğunu gösterir.", + "vscode.extension.publisher": "VS Code eklentisinin yayıncısı.", + "vscode.extension.displayName": "VS Code galerisinde kullanılan eklentinin görünen adı.", + "vscode.extension.categories": "Bu eklentiyi kategorize etmek için VS Code galerisi tarafından kullanılan kategoriler.", + "vscode.extension.galleryBanner": "VS Code markette kullanılan afiş.", + "vscode.extension.galleryBanner.color": "VS Code marketteki sayfa başlığındaki afiş rengi.", + "vscode.extension.galleryBanner.theme": "Afişte kullanılan yazı tipi için renk teması.", + "vscode.extension.contributes": "Bu paketin temsil ettiği VS Code eklentisinin tüm katkıları.", + "vscode.extension.preview": "Markette eklentinin Önizleme olarak işaretlenmesini sağlar.", + "vscode.extension.activationEvents": "VS Code eklentisi için etkinleştirme olayları.", + "vscode.extension.activationEvents.onLanguage": "Belirtilen dilde çözümlenen bir dosya her açıldığında bir etkinleştirme olayı yayınlanır.", + "vscode.extension.activationEvents.onCommand": "Belirtilen komut her çağrıldığında bir etkinleştirme olayı yayınlanır.", + "vscode.extension.activationEvents.onDebug": "Belirtilen türde bir hata ayıklama oturumu her başladığında bir etkinleştirme olayı yayınlanır.", + "vscode.extension.activationEvents.workspaceContains": "Belirtilen glob deseni ile eşleşen en az bir dosya içeren bir klasör her açıldığında bir etkinleştirme olayı yayınlanır.", + "vscode.extension.activationEvents.onView": "Belirtilen görünüm her genişletildiğinde bir etkinleştirme olayı yayınlanır.", + "vscode.extension.activationEvents.star": "VS Code başlatıldığında yayılan etkinleştirme olayı. Mükemmel bir son kullanıcı deneyimi sağlandığından emin olmak için, lütfen bu etkinleştirme olayını eklentinizde sadece kullanım durumunuzda başka hiçbir aktivasyon olayı kombinasyonu çalışmıyorsa kullanın.", + "vscode.extension.badges": "Marketin eklenti sayfasının kenar çubuğunda görüntülenecek göstergeler dizisi.", + "vscode.extension.badges.url": "Gösterge resmi URL'si.", + "vscode.extension.badges.href": "Gösterge bağlantısı.", + "vscode.extension.badges.description": "Gösterge açıklaması.", + "vscode.extension.extensionDependencies": "Diğer uzantılara bağımlılıklar. Bir uzantının tanımlayıcısı her zaman ${publisher}.${name} biçimindedir. Örneğin: vscode.csharp.", + "vscode.extension.scripts.prepublish": "Paket, bir VS Code eklentisi olarak yayımlamadan önce çalıştırılacak betik.", + "vscode.extension.icon": "128x128 piksellik bir simgenin yolu." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/trk/src/vs/platform/extensions/node/extensionValidator.i18n.json new file mode 100644 index 0000000000..32334397b8 --- /dev/null +++ b/i18n/trk/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionSyntax": "`engines.vscode` değeri {0} ayrıştırılamadı. Lütfen örnekte verilenlere benzer ifadeler kullanın: ^0.10.0, ^1.2.3, ^0.11.0, ^0.10.x, vb.", + "versionSpecificity1": "`engines.vscode`da belirtilen sürüm ({0}) yeterince belirli değil. vscode 1.0.0'dan önceki sürümler için, lütfen istenecek minimum majör ve minör sürüm numarasını tanımlayın. Örneğin: ^0.10.0, 0.10.x, 0.11.0, vb.", + "versionSpecificity2": "`engines.vscode`da belirtilen sürüm ({0}) yeterince belirli değil. vscode 1.0.0'dan sonraki sürümler için, lütfen istenecek minimum majör sürüm numarasını tanımlayın. Örneğin: ^1.10.0, 1.10.x, 1.x.x, 2.x.x, vb.", + "versionMismatch": "Eklenti, Code {0} ile uyumlu değil. Gereken sürüm: {1}.", + "extensionDescription.empty": "Boş eklenti açıklaması alındı", + "extensionDescription.publisher": "`{0}` özelliği zorunludur ve `string` türünde olmalıdır", + "extensionDescription.name": "`{0}` özelliği zorunludur ve `string` türünde olmalıdır", + "extensionDescription.version": "`{0}` özelliği zorunludur ve `string` türünde olmalıdır", + "extensionDescription.engines": "`{0}` özelliği zorunludur ve `object` türünde olmalıdır", + "extensionDescription.engines.vscode": "`{0}` özelliği zorunludur ve `string` türünde olmalıdır", + "extensionDescription.extensionDependencies": "`{0}` özelliği atlanabilir veya `string[]` türünde olmalıdır", + "extensionDescription.activationEvents1": "`{0}` özelliği atlanabilir veya `string[]` türünde olmalıdır", + "extensionDescription.activationEvents2": "`{0}` ve `{1}` özelliklerinin ikisi birden belirtilmeli veya ikisi birden atlanmalıdır", + "extensionDescription.main1": "`{0}` özelliği atlanabilir veya `string` türünde olmalıdır", + "extensionDescription.main2": "`main` ({0}) yolunun eklentinin klasörü içine ({1}) eklenmiş olacağı beklendi. Bu, eklentiyi taşınamaz yapabilir.", + "extensionDescription.main3": "`{0}` ve `{1}` özelliklerinin ikisi birden belirtilmeli veya ikisi birden atlanmalıdır", + "notSemver": "Eklenti sürümü semver ile uyumlu değil." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/history/electron-main/historyMainService.i18n.json b/i18n/trk/src/vs/platform/history/electron-main/historyMainService.i18n.json new file mode 100644 index 0000000000..4299d7d2cb --- /dev/null +++ b/i18n/trk/src/vs/platform/history/electron-main/historyMainService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "newWindow": "Yeni Pencere", + "newWindowDesc": "Yeni bir pencere açar", + "recentFolders": "Son Çalışma Alanları", + "folderDesc": "{0} {1}", + "codeWorkspace": "Code Çalışma Alanı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json b/i18n/trk/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json new file mode 100644 index 0000000000..51ba108934 --- /dev/null +++ b/i18n/trk/src/vs/platform/integrity/node/integrityServiceImpl.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "integrity.ok": "Tamam", + "integrity.dontShowAgain": "Tekrar gösterme", + "integrity.moreInfo": "Daha fazla bilgi", + "integrity.prompt": "{0} kurulumunuz bozuk görünüyor. Lütfen yeniden yükleyin." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json b/i18n/trk/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json new file mode 100644 index 0000000000..2335f2800f --- /dev/null +++ b/i18n/trk/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.jsonValidation": "json şema yapılandırmasına ekleme yapar.", + "contributes.jsonValidation.fileMatch": "Eşleşecek dosya örüntüsü, örneğin \"package.json\" veya \"*.launch\".", + "contributes.jsonValidation.url": "Bir şema URL'si ('http:', 'https:') veya eklenti klasörüne ('./') göreceli yol.", + "invalid.jsonValidation": "'configuration.jsonValidation' bir dizi olmalıdır", + "invalid.fileMatch": "'configuration.jsonValidation.fileMatch' tanımlanmalıdır", + "invalid.url": "'configuration.jsonValidation.url' bir URL veya göreli yol olmalıdır", + "invalid.url.fileschema": "'configuration.jsonValidation.url' geçersiz bir göreli URL'dir: {0}", + "invalid.url.schema": "'configuration.jsonValidation.url' ögesi eklentide bulunan şemalara başvurmak için 'http:', 'https:' veya './' ile başlamalıdır" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json b/i18n/trk/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json new file mode 100644 index 0000000000..a17f91d0e5 --- /dev/null +++ b/i18n/trk/src/vs/platform/keybinding/common/abstractKeybindingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "first.chord": "({0}) öğesine basıldı. Akorun ikinci tuşu bekleniyor...", + "missing.chord": "({0}, {1}) tuş bileşimi bir komut değil." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/markers/common/problemMatcher.i18n.json b/i18n/trk/src/vs/platform/markers/common/problemMatcher.i18n.json new file mode 100644 index 0000000000..53acc286b9 --- /dev/null +++ b/i18n/trk/src/vs/platform/markers/common/problemMatcher.i18n.json @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ProblemPatternParser.loopProperty.notLast": "Döngü özelliği yalnızca son satır eşleştiricisinde desteklenir.", + "ProblemPatternParser.problemPattern.missingRegExp": "Sorun modelinde bir düzenli ifade eksik.", + "ProblemPatternParser.problemPattern.missingProperty": "Sorun modeli hatalı. En az bir dosya, mesaj ve satır veya konum eşleşme grubu bulundurmalıdır.", + "ProblemPatternParser.invalidRegexp": "Hata: Dize {0}, geçerli bir düzenli ifade değil.\n", + "ProblemPatternSchema.regexp": "Çıktıda bir hata, uyarı veya bilgi bulan düzenli ifade.", + "ProblemPatternSchema.file": "Dosya adının eşleştirme grubu indeksi. Atlanırsa 1 kullanılır.", + "ProblemPatternSchema.location": "Sorunun bulunduğu yerin eşleşme grubu indeksi. Geçerli konum örüntüleri şunlardır: (line), (line,column) ve (startLine,startColumn,endLine,endColumn). Atlanmışsa (line,column) varsayılır.", + "ProblemPatternSchema.line": "Sorunun satırının eşleştirme grubu indeksi. Varsayılan değeri 2'dir", + "ProblemPatternSchema.column": "Sorunun satır karakterinin eşleştirme grubu indeksi. Varsayılan değeri 3'tür", + "ProblemPatternSchema.endLine": "Sorunun satır sonunun eşleştirme grubu indeksi. Varsayılan değeri tanımsızdır", + "ProblemPatternSchema.endColumn": "Sorunun satır sonu karakterinin eşleştirme grubu indeksi. Varsayılan değeri tanımsızdır", + "ProblemPatternSchema.severity": "Sorunun öneminin eşleştirme grubu indeksi. Varsayılan değeri tanımsızdır", + "ProblemPatternSchema.code": "Sorunun kodunun eşleştirme grubu indeksi. Varsayılan değeri tanımsızdır", + "ProblemPatternSchema.message": "Mesajın eşleştirme grubu indeksi. Atlanırsa konum belirtildiğinde varsayılan olarak 4 kullanılır. Aksi taktirde varsayılan olarak 5 kullanılır.", + "ProblemPatternSchema.loop": "Birden çok satırlı eşleşmede döngü, bu kalıbın eşleştiği sürece bir döngüde yürütülüp yürütülmeyeceğini gösterir. Yalnızca birden çok satırlı bir kalıbın son kalıbında belirtilebilir.", + "NamedProblemPatternSchema.name": "Sorun modelinin adı.", + "NamedMultiLineProblemPatternSchema.name": "Birden çok satırlı sorun modelinin adı.", + "NamedMultiLineProblemPatternSchema.patterns": "Gerçek modeller.", + "ProblemPatternExtPoint": "Sorun modellerine ekleme yapar", + "ProblemPatternRegistry.error": "Geçersiz sorun modeli. Model yok sayılacaktır.", + "ProblemMatcherParser.noProblemMatcher": "Hata: açıklama bir sorun eşleştiricisine dönüştürülemez:\n{0}\n", + "ProblemMatcherParser.noProblemPattern": "Hata: açıklama geçerli bir sorun modeli tanımlamıyor:\n{0}\n", + "ProblemMatcherParser.noOwner": "Hata: açıklama bir sahip tanımlamıyor:\n{0}\n", + "ProblemMatcherParser.noFileLocation": "Hata: açıklama bir dosya yolu tanımlamıyor:\n{0}\n", + "ProblemMatcherParser.unknownSeverity": "Bilgi: bilinmeyen önem derecesi {0}. Geçerli değerler: error, warning ve info.\n", + "ProblemMatcherParser.noDefinedPatter": "Hata: tanımlayıcısı {0} olan model mevcut değil.", + "ProblemMatcherParser.noIdentifier": "Hata: model özelliği boş bir tanımlayıcıya karşılık geliyor.", + "ProblemMatcherParser.noValidIdentifier": "Hata: model özelliği {0} geçerli bir model değişkeni adı değil.", + "ProblemMatcherParser.problemPattern.watchingMatcher": "Bir sorun eşleştiricisi izlenecek bir başlangıç örüntüsü ve bir bitiş örüntüsü tanımlamalıdır.", + "ProblemMatcherParser.invalidRegexp": "Hata: Dize {0}, geçerli bir düzenli ifade değil.\n", + "WatchingPatternSchema.regexp": "Bir arka plan görevinin başlangıcını ve sonunu tespit edecek düzenli ifade.", + "WatchingPatternSchema.file": "Dosya adının eşleştirme grubu indeksi. Atlanabilir.", + "PatternTypeSchema.name": "Katkıda bulunulan veya ön tanımlı modelin adı", + "PatternTypeSchema.description": "Bir sorun modeli veya bir katkıda bulunulan veya ön tanımlı sorun modelinin adı. Temel model belirtildiyse atlanabilir.", + "ProblemMatcherSchema.base": "Kullanılacak temel sorun eşleştiricisinin adı.", + "ProblemMatcherSchema.owner": "Code'un içindeki sorunun sahibi. Temel model belirtildiyse atlanabilir. Atlanırsa ve temel belirtilmemişse 'external' varsayılır.", + "ProblemMatcherSchema.severity": "Sorun yakalamanın varsayılan önem derecesi. Model, önem derecesi için bir eşleme grubu tanımlamazsa kullanılır.", + "ProblemMatcherSchema.applyTo": "Bir metin belgesinde bildirilen bir sorunun sadece açık, kapalı veya tüm belgelere uygulanıp uygulanmadığını denetler.", + "ProblemMatcherSchema.fileLocation": "Bir sorun modelinde bildirilen dosya adlarının nasıl yorumlanacağını tanımlar.", + "ProblemMatcherSchema.background": "Bir arka plan görevinde aktif bir eşleştiricinin başlangıcını ve sonunu izlemek için kullanılan kalıplar.", + "ProblemMatcherSchema.background.activeOnStart": "\"true\" olarak ayarlanırsa, görev başladığında arka plan izleyicisi etkin modda olur. Bu, beginsPattern ile başlayan bir satırın verilmesi demektir", + "ProblemMatcherSchema.background.beginsPattern": "Çıktıda eşleşmesi halinde, arka plan görevinin başlatılması sinyali verilir.", + "ProblemMatcherSchema.background.endsPattern": "Çıktıda eşleşmesi halinde, arka plan görevinin sonu sinyali verilir.", + "ProblemMatcherSchema.watching.deprecated": "İzleme özelliği kullanım dışıdır. Onun yerine arka planı kullanın.", + "ProblemMatcherSchema.watching": "Bir izleme eşleştiricisinin başlangıcını ve sonunu izlemek için kullanılan kalıplar.", + "ProblemMatcherSchema.watching.activeOnStart": "\"true\" olarak ayarlanırsa, görev başladığında izleyici etkin modda olur. Bu, beginsPattern ile başlayan bir satırın verilmesi demektir", + "ProblemMatcherSchema.watching.beginsPattern": "Çıktıda eşleşmesi halinde, izleme görevinin başlatılması sinyali verilir.", + "ProblemMatcherSchema.watching.endsPattern": "Çıktıda eşleşmesi halinde, izleme görevinin sonu sinyali verilir.", + "LegacyProblemMatcherSchema.watchedBegin.deprecated": "Bu özellik kullanım dışıdır. Bunun yerine 'watching' özelliğini kullanın.", + "LegacyProblemMatcherSchema.watchedBegin": "İzlenen görevlerin yürütülmeye başlanmasının, dosya izleyerek tetiklendiğini işaret eden bir düzenli ifade.", + "LegacyProblemMatcherSchema.watchedEnd.deprecated": "Bu özellik kullanım dışıdır. Bunun yerine 'watching' özelliğini kullanın.", + "LegacyProblemMatcherSchema.watchedEnd": "İzlenen görevlerin yürütülmesinin sona erdiğini işaret eden bir düzenli ifade.", + "NamedProblemMatcherSchema.name": "Başvuru yapılırken kullanılacak sorun eşleştiricisinin adı.", + "NamedProblemMatcherSchema.label": "Sorun eşleştiricisinin insanlar tarafından okunabilir etiketi.", + "ProblemMatcherExtPoint": "Sorun eşleştiricilerine ekleme yapar", + "msCompile": "Microsoft derleyici sorunları", + "lessCompile": "Less sorunları", + "gulp-tsc": "Gulp TSC Sorunları", + "jshint": "JSHint sorunları", + "jshint-stylish": "JSHint stylish sorunları", + "eslint-compact": "ESLint compact sorunları", + "eslint-stylish": "ESLint stylish sorunları", + "go": "Go sorunları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/message/common/message.i18n.json b/i18n/trk/src/vs/platform/message/common/message.i18n.json new file mode 100644 index 0000000000..f21365c944 --- /dev/null +++ b/i18n/trk/src/vs/platform/message/common/message.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Kapat", + "later": "Daha Sonra", + "cancel": "İptal" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/request/node/request.i18n.json b/i18n/trk/src/vs/platform/request/node/request.i18n.json new file mode 100644 index 0000000000..561083ed30 --- /dev/null +++ b/i18n/trk/src/vs/platform/request/node/request.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "httpConfigurationTitle": "HTTP", + "proxy": "Kullanılacak proxy ayarı. Ayarlanmazsa, http_proxy ve https_proxy ortam değişkenlerinden alınır", + "strictSSL": "Proxy sunucu sertifikasının verilen Sertifika Yetkilileri listesine göre doğrulanması gerekip gerekmediği.", + "proxyAuthorization": "Her ağ isteği için 'Proxy-Authorization' başlığı olarak gönderilecek değer." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/telemetry/common/telemetryService.i18n.json b/i18n/trk/src/vs/platform/telemetry/common/telemetryService.i18n.json new file mode 100644 index 0000000000..6c2160fcdc --- /dev/null +++ b/i18n/trk/src/vs/platform/telemetry/common/telemetryService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetri", + "telemetry.enableTelemetry": "Kullanım verileri ve hataların Microsoft'a gönderilmesini etkinleştirin." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/trk/src/vs/platform/theme/common/colorExtensionPoint.i18n.json new file mode 100644 index 0000000000..639af752a0 --- /dev/null +++ b/i18n/trk/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "contributes.color": "Eklenti tarafından tanımlanan tema olarak kullanılabilir renklere ekleme yapar", + "contributes.color.id": "Tema olarak kullanılabilir rengin tanımlayıcısı", + "contributes.color.id.format": "Tanımlayıcılar aa[.bb]* biçiminde olmalıdır", + "contributes.color.description": "Tema olarak kullanılabilir rengin açıklaması", + "contributes.defaults.light": "Açık temaların varsayılan rengi. Ya hex biçiminde bir renk değeri (#RRGGBB[AA]) ya da varsayılanı sağlayan bir tema olarak kullanılabilir rengin tanımlayıcısı olabilir.", + "contributes.defaults.dark": "Koyu temaların varsayılan rengi. Ya hex biçiminde bir renk değeri (#RRGGBB[AA]) ya da varsayılanı sağlayan bir tema olarak kullanılabilir rengin tanımlayıcısı olabilir.", + "contributes.defaults.highContrast": "Yüksek karşıtlık temalarının varsayılan rengi. Ya hex biçiminde bir renk değeri (#RRGGBB[AA]) ya da varsayılanı sağlayan bir tema olarak kullanılabilir rengin tanımlayıcısı olabilir.", + "invalid.colorConfiguration": "'configuration.colors' bir dizi olmalıdır", + "invalid.default.colorType": "{0}, ya hex biçiminde bir renk değeri (#RRGGBB[AA] veya #RGB[A]) ya da varsayılanı sağlayan bir tema olarak kullanılabilir rengin tanımlayıcısı olabilir.", + "invalid.id": "'configuration.colors.id' tanımlanmalı ve boş olmamalıdır", + "invalid.id.format": "'configuration.colors.id' sözcük[.sözcük]* şeklinde olmalıdır", + "invalid.description": "'configuration.colors.description' tanımlanmalı ve boş olmamalıdır", + "invalid.defaults": "'configuration.colors.defaults' tanımlanmalı ve 'light', 'dark' ve 'highContrast' değerlerini içermelidir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/trk/src/vs/platform/theme/common/colorRegistry.i18n.json new file mode 100644 index 0000000000..b74b2d4650 --- /dev/null +++ b/i18n/trk/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.color": "Geçersiz renk biçimi. #RGB, #RGBA, #RRGGBB veya #RRGGBBAA kullanın", + "schema.colors": "Çalışma ekranında kullanılan renkler.", + "foreground": "Genel ön plan rengi. Bu renk, bir bileşen tarafından geçersiz kılınmadıkça kullanılır.", + "errorForeground": "Hata mesajları için genel ön plan rengi. Bu renk, bir bileşen tarafından geçersiz kılınmadıkça kullanılır.", + "descriptionForeground": "Ek bilgi sağlayan açıklama metni(örneğin bir etiket) için ön plan rengi.", + "focusBorder": "Odaklanılan ögeler için genel kenarlık rengi. Bu renk, bir bileşen tarafından geçersiz kılınmadıkça kullanılır.", + "contrastBorder": "Daha yüksek karşıtlık için, ögelerin etrafında onları diğerlerinden ayıracak ekstra bir kenarlık.", + "activeContrastBorder": "Daha yüksek karşıtlık için, aktif ögelerin etrafında onları diğerlerinden ayıracak ekstra bir kenarlık.", + "selectionBackground": "Çalışma ekranındaki metin seçimlerinin arka plan rengi(örneğin girdi alanları veya metin alanları). Bunun, düzenleyicideki seçimlere uygulanmayacağını unutmayın.", + "textSeparatorForeground": "Metin ayırıcıların rengi.", + "textLinkForeground": "Metindeki bağlantıların ön plan rengi.", + "textLinkActiveForeground": "Metindeki aktif bağlantıların ön plan rengi.", + "textPreformatForeground": "Önceden biçimlendirilmiş metin parçalarının ön plan rengi.", + "textBlockQuoteBackground": "Metindeki alıntı bloklarının arka plan rengi.", + "textBlockQuoteBorder": "Metindeki alıntı bloklarının kenarlık rengi.", + "textCodeBlockBackground": "Metindeki kod bloklarının arka plan rengi.", + "widgetShadow": "Bul/değiştir gibi düzenleyici içindeki araçların gölge rengi.", + "inputBoxBackground": "Giriş kutusu arka planı.", + "inputBoxForeground": "Giriş kutusu ön planı.", + "inputBoxBorder": "Giriş kutusu kenarlığı.", + "inputBoxActiveOptionBorder": "Girdi alanlarındaki aktif seçeneklerin kenarlık rengi.", + "inputPlaceholderForeground": "Yer tutucu metin için girdi kutusu ön plan rengi.", + "inputValidationInfoBackground": "Bilgi önem derecesi için girdi doğrulama arka plan rengi.", + "inputValidationInfoBorder": "Bilgi önem derecesi için girdi doğrulama kenarlık rengi.", + "inputValidationWarningBackground": "Bilgi uyarısı için girdi doğrulama arka plan rengi.", + "inputValidationWarningBorder": "Uyarı önem derecesi için girdi doğrulama kenarlık rengi.", + "inputValidationErrorBackground": "Hata önem derecesi için girdi doğrulama arka plan rengi.", + "inputValidationErrorBorder": "Hata önem derecesi için girdi doğrulama kenarlık rengi.", + "dropdownBackground": "Açılır kutu arka planı.", + "dropdownForeground": "Açılır kutu ön planı.", + "dropdownBorder": "Açılır kutu kenarlığı.", + "listFocusBackground": "Liste/Ağaç aktifken odaklanılan ögenin Lise/Ağaç arka plan rengi. Bir aktif liste/ağaç, klavye odağındadır; pasif olan odakta değildir.", + "listFocusForeground": "Liste/Ağaç aktifken odaklanılan ögenin Lise/Ağaç ön plan rengi. Bir aktif liste/ağaç, klavye odağındadır; pasif olan odakta değildir.", + "listActiveSelectionBackground": "Liste/Ağaç aktifken seçilen ögenin Lise/Ağaç arka plan rengi. Bir aktif liste/ağaç, klavye odağındadır; pasif olan odakta değildir.", + "listActiveSelectionForeground": "Liste/Ağaç aktifken seçilen ögenin Lise/Ağaç ön plan rengi. Bir aktif liste/ağaç, klavye odağındadır; pasif olan odakta değildir.", + "listInactiveSelectionBackground": "Liste/Ağaç pasifken seçilen ögenin Lise/Ağaç arka plan rengi. Bir aktif liste/ağaç, klavye odağındadır; pasif olan odakta değildir.", + "listInactiveSelectionForeground": "Liste/Ağaç pasifken seçilen ögenin Lise/Ağaç ön plan rengi. Bir aktif liste/ağaç, klavye odağındadır; pasif olan odakta değildir.", + "listHoverBackground": "Fare ile ögelerin üzerine gelindiğinde Liste/Ağaç arka planı.", + "listHoverForeground": "Fare ile ögelerin üzerine gelindiğinde Liste/Ağaç ön planı.", + "listDropBackground": "Fare ile ögeler taşınırken Liste/Ağaç sürükle ve bırak arka planı.", + "highlight": "Liste/Ağaç içinde arama yaparken eşleşme vurgularının Liste/Ağaç ön plan rengi.", + "pickerGroupForeground": "Gruplama etiketleri için hızlı seçici rengi.", + "pickerGroupBorder": "Gruplama kenarlıkları için hızlı seçici rengi.", + "buttonForeground": "Buton ön plan rengi.", + "buttonBackground": "Buton arka plan rengi.", + "buttonHoverBackground": "Fareyle üzerine gelindiğinde buton arka plan rengi.", + "badgeBackground": "Gösterge arka plan rengi. Göstergeler küçük bilgi etiketleridir, ör. arama sonucu sayısı için.", + "badgeForeground": "Gösterge ön plan rengi. Göstergeler küçük bilgi etiketleridir, ör. arama sonucu sayısı için.", + "scrollbarShadow": "Görünümün kaydırıldığını belirtmek için kaydırma çubuğu gölgesi.", + "scrollbarSliderBackground": "Kaydırma çubuğu kaydıracının arka plan rengi.", + "scrollbarSliderHoverBackground": "Fareyle üzerine gelindiğinde kaydırma çubuğu kaydıracının arka plan rengi.", + "scrollbarSliderActiveBackground": "Kaydırma çubuğu kaydıracının aktif iken arka plan rengi.", + "progressBarBackground": "Uzun süren işlemleri gösterebilen ilerleme çubuğunun arka plan rengi.", + "editorBackground": "Düzenleyici arka plan rengi.", + "editorForeground": "Düzenleyici varsayılan ön plan rengi.", + "editorWidgetBackground": "Bul/değiştir gibi düzenleyici araçlarının arka plan rengi.", + "editorWidgetBorder": "Editör araçlarının kenarlık rengi. Renk, araç bir kenarlığı olmasına karar verdiğinde ve renk hiçbir eklenti tarafından geçersiz kılınmadığında kullanılır.", + "editorSelectionBackground": "Düzenleyici seçiminin rengi.", + "editorSelectionForeground": "Yüksek karşıtlık için seçilen metnin rengi.", + "editorInactiveSelection": "Bir pasif düzenleyicideki seçimin rengi.", + "editorSelectionHighlight": "Seçimle aynı içeriğe sahip bölgelerin rengi.", + "editorFindMatch": "Geçerli arama eşleşmesinin rengi.", + "findMatchHighlight": "Diğer arama eşleşmelerinin rengi.", + "findRangeHighlight": "Aramayı sınırlayan aralığı renklendirin.", + "hoverHighlight": "Bağlantı vurgusu gösterilen bir sözcüğün altını vurgulayın.", + "hoverBackground": "Düzenleyici bağlantı vurgusunun arka plan rengi.", + "hoverBorder": "Düzenleyici bağlantı vurgusunun kenarlık rengi.", + "activeLinkForeground": "Aktif bağlantıların rengi.", + "diffEditorInserted": "Eklenen metnin arka plan rengi.", + "diffEditorRemoved": "Çıkarılan metnin arka plan rengi.", + "diffEditorInsertedOutline": "Eklenen metnin ana hat rengi.", + "diffEditorRemovedOutline": "Çıkarılan metnin ana hat rengi.", + "mergeCurrentHeaderBackground": "Satır içi birleştirme çakışmalarında geçerli üstbilgi arka planı.", + "mergeCurrentContentBackground": "Satır içi birleştirme çakışmalarında geçerli içerik arka planı.", + "mergeIncomingHeaderBackground": "Satır içi birleştirme çakışmalarında gelen üstbilgi arka planı.", + "mergeIncomingContentBackground": "Satır içi birleştirme çakışmalarında gelen içerik arka planı.", + "mergeCommonHeaderBackground": "Satır içi birleştirme çakışmalarında ortak ata üstbilgisi arka planı.", + "mergeCommonContentBackground": "Satır içi birleştirme çakışmalarında ortak ata içeriği arka planı.", + "mergeBorder": "Satır içi birleştirme çakışmalarında üst bilgi ve ayırıcıdaki kenarlık rengi.", + "overviewRulerCurrentContentForeground": "Satır içi birleştirme çakışmalarında geçerli genel bakış cetveli ön planı.", + "overviewRulerIncomingContentForeground": "Satır içi birleştirme çakışmalarında gelen genel bakış cetveli ön planı.", + "overviewRulerCommonContentForeground": "Satır içi birleştirme çakışmalarında ortak ata genel bakış cetveli ön planı.", + "overviewRulerFindMatchForeground": "Bulunan eşler için genel bakış cetvelinin işaretleyici rengi.", + "overviewRulerSelectionHighlightForeground": "Seçim vurguları için genel bakış cetvelinin işaretleyici rengi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/workspaces/common/workspaces.i18n.json b/i18n/trk/src/vs/platform/workspaces/common/workspaces.i18n.json new file mode 100644 index 0000000000..0e78cd2671 --- /dev/null +++ b/i18n/trk/src/vs/platform/workspaces/common/workspaces.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "codeWorkspace": "Code Çalışma Alanı", + "untitledWorkspace": "İsimsiz (Çalışma Alanı)", + "workspaceNameVerbose": "{0} (Çalışma Alanı)", + "workspaceName": "{0} (Çalışma Alanı)" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json b/i18n/trk/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json new file mode 100644 index 0000000000..8c905946d6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/api/electron-browser/mainThreadExtensionService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "overwritingExtension": "{0} eklentisinin üzerine {1} yazılıyor.", + "extensionUnderDevelopment": "{0} konumundaki geliştirme eklentisi yükleniyor" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json b/i18n/trk/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json new file mode 100644 index 0000000000..9efe4a62c5 --- /dev/null +++ b/i18n/trk/src/vs/workbench/api/electron-browser/mainThreadMessageService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Kapat", + "cancel": "İptal", + "ok": "Tamam" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/api/node/extHostDiagnostics.i18n.json b/i18n/trk/src/vs/workbench/api/node/extHostDiagnostics.i18n.json new file mode 100644 index 0000000000..03873eb82e --- /dev/null +++ b/i18n/trk/src/vs/workbench/api/node/extHostDiagnostics.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "limitHit": "Diğer {0} hata ve uyarılar gösterilmiyor." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json b/i18n/trk/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json new file mode 100644 index 0000000000..fe18d791c3 --- /dev/null +++ b/i18n/trk/src/vs/workbench/api/node/extHostExtensionActivator.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownDep": "`{1}` eklentisi etkinleştirilemedi. Neden: bilinmeyen bağımlılık `{0}`.", + "failedDep1": "`{1}` eklentisi etkinleştirilemedi. Neden: bağımlılık `{0}` etkinleştirilemedi.", + "failedDep2": "`{0}` eklentisi etkinleştirilemedi. Neden: 10'dan fazla bağımlılık düzeyi (büyük olasılıkla bağımlılık döngüsü).", + "activationError": "`{0}` eklentisi etkinleştirilemedi: {1}." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/api/node/extHostTask.i18n.json b/i18n/trk/src/vs/workbench/api/node/extHostTask.i18n.json new file mode 100644 index 0000000000..8d6b34717d --- /dev/null +++ b/i18n/trk/src/vs/workbench/api/node/extHostTask.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "task.label": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/api/node/extHostTreeViews.i18n.json b/i18n/trk/src/vs/workbench/api/node/extHostTreeViews.i18n.json new file mode 100644 index 0000000000..4448332949 --- /dev/null +++ b/i18n/trk/src/vs/workbench/api/node/extHostTreeViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeView.notRegistered": "Kayıtlı '{0}' Id'li ağaç görünümü yok.", + "treeItem.notFound": "'{0}' Id'li ağaç ögesi yok.", + "treeView.duplicateElement": "{0} ögesi zaten kayıtlı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/actions/configureLocale.i18n.json b/i18n/trk/src/vs/workbench/browser/actions/configureLocale.i18n.json new file mode 100644 index 0000000000..3a68ed96b1 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/actions/configureLocale.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "configureLocale": "Dili Yapılandır", + "displayLanguage": "VSCode'un görüntüleme dilini tanımlar.", + "doc": "Desteklenen dillerin listesi için göz atın: {0}", + "restart": "Değeri değiştirirseniz VSCode'u yeniden başlatmanız gerekir.", + "fail.createSettings": " '{0}' oluşturulamadı ({1}).", + "JsonSchema.locale": "Kullanılacak Kullanıcı Arayüzü Dili." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/actions/fileActions.i18n.json b/i18n/trk/src/vs/workbench/browser/actions/fileActions.i18n.json new file mode 100644 index 0000000000..2b25a0370c --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/actions/fileActions.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Klasör Aç...", + "openFileFolder": "Aç...", + "addFolderToWorkspace": "Çalışma Alanına Klasör Ekle...", + "add": "Ekle", + "addFolderToWorkspaceTitle": "Çalışma Alanına Klasör Ekle", + "removeFolderFromWorkspace": "Çalışma Alanından Klasör Kaldır" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json b/i18n/trk/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json new file mode 100644 index 0000000000..58c9884410 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/actions/toggleActivityBarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleActivityBar": "Etkinlik Çubuğunu Gizle/Göster", + "view": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/trk/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 0000000000..c42a894a72 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleEditorGroupLayout": "Düzenleyici Grubunu Dikey/Yatay Düzende Değiştir", + "horizontalLayout": "Yatay Düzenleyici Grubu Düzeni", + "verticalLayout": "Dikey Düzenleyici Grubu Düzeni", + "view": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json b/i18n/trk/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json new file mode 100644 index 0000000000..68d57f2ea3 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/actions/toggleSidebarPosition.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Kenar Çubuğu Konumunu Değiştir", + "view": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json b/i18n/trk/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json new file mode 100644 index 0000000000..818239f9f9 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/actions/toggleSidebarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleSidebar": "Kenar Çubuğunu Gizle/Göster", + "view": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json b/i18n/trk/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json new file mode 100644 index 0000000000..45347546ad --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/actions/toggleStatusbarVisibility.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleStatusbar": "Durum Çubuğunu Gizle/Göster", + "view": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/actions/toggleZenMode.i18n.json b/i18n/trk/src/vs/workbench/browser/actions/toggleZenMode.i18n.json new file mode 100644 index 0000000000..59f8f3e808 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/actions/toggleZenMode.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleZenMode": "Zen Modunu Aç/Kapat", + "view": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/trk/src/vs/workbench/browser/actions/workspaceActions.i18n.json new file mode 100644 index 0000000000..f3d0d71619 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolder": "Klasör Aç...", + "openFileFolder": "Aç...", + "addFolderToWorkspace": "Çalışma Alanına Klasör Ekle...", + "add": "&&Ekle", + "addFolderToWorkspaceTitle": "Çalışma Alanına Klasör Ekle", + "newWorkspace": "Yeni Çalışma Alanı...", + "select": "&&Seç", + "selectWorkspace": "Çalışma Alanı İçin Klasörleri Seçin", + "removeFolderFromWorkspace": "Çalışma Alanından Klasör Kaldır", + "saveWorkspaceAsAction": "Çalışma Alanını Farklı Kaydet...", + "saveEmptyWorkspaceNotSupported": "Lütfen kaydetmek için ilk olarak bir çalışma alanı açın.", + "save": "&&Kaydet", + "saveWorkspace": "Çalışma Alanını Kaydet", + "openWorkspaceAction": "Çalışma Alanı Aç...", + "openWorkspaceConfigFile": "Çalışma Alanı Yapılandırma Dosyasını Aç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json new file mode 100644 index 0000000000..1e631cf075 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeFromActivityBar": "Etkinlik Çubuğundan Kaldır", + "keepInActivityBar": "Etkinlik Çubuğunda Tut", + "titleKeybinding": "{0} ({1})", + "additionalViews": "Ek Görünümler", + "numberBadge": "{0} ({1})", + "manageExtension": "Eklentiyi Yönet", + "toggle": "Görünüm Sabitlemeyi Aç/Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json new file mode 100644 index 0000000000..dabf0a9100 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "hideActivitBar": "Etkinlik Çubuğunu Gizle", + "activityBarAriaLabel": "Aktif Görünüm Değiştirici", + "globalActions": "Global Eylemler" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/compositePart.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/compositePart.i18n.json new file mode 100644 index 0000000000..20f01c9f19 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/compositePart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ariaCompositeToolbarLabel": "{0} eylem", + "titleTooltip": "{0} ({1})" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json new file mode 100644 index 0000000000..addd71e284 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/binaryDiffEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "metadataDiff": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json new file mode 100644 index 0000000000..450f6f0088 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/binaryEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryEditor": "İkili Görüntüleyici" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json new file mode 100644 index 0000000000..3259beef6f --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Metin Düzenleyicisi", + "textDiffEditor": "Metin Diff Düzenleyicisi", + "binaryDiffEditor": "İkili Diff Düzenleyicisi", + "sideBySideEditor": "Yan Yana Düzenleyici", + "groupOnePicker": "İlk Gruptaki Düzenleyicileri Göster", + "groupTwoPicker": "İkinci Gruptaki Düzenleyicileri Göster", + "groupThreePicker": "Üçüncü Gruptaki Düzenleyicileri Göster", + "allEditorsPicker": "Açık Tüm Düzenleyicileri Göster", + "view": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/editorActions.i18n.json new file mode 100644 index 0000000000..81c1e96df1 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitEditor": "Düzenleyiciyi Böl", + "joinTwoGroups": "İki Gruptaki Düzenleyicileri Birleştir", + "navigateEditorGroups": "Düzenleyici Grupları Arasında Gezin", + "focusActiveEditorGroup": "Aktif Düzenleyici Grubuna Odakla", + "focusFirstEditorGroup": "İlk Düzenleyici Grubuna Odakla", + "focusSecondEditorGroup": "İkinci Düzenleyici Grubuna Odakla", + "focusThirdEditorGroup": "Üçüncü Düzenleyici Grubuna Odakla", + "focusPreviousGroup": "Önceki Gruba Odakla", + "focusNextGroup": "Sonraki Gruba Odakla", + "openToSide": "Yana Aç", + "closeEditor": "Düzenleyiciyi Kapat", + "revertAndCloseActiveEditor": "Geri Al ve Düzenleyiciyi Kapat", + "closeEditorsToTheLeft": "Düzenleyicinin Solundakileri Kapat", + "closeEditorsToTheRight": "Düzenleyicinin Sağındakileri Kapat", + "closeAllEditors": "Tüm Düzenleyicileri Kapat", + "closeUnmodifiedEditors": "Gruptaki Değiştirilmemiş Düzenleyicileri Kapat", + "closeEditorsInOtherGroups": "Diğer Gruplardaki Tüm Düzenleyicileri Kapat", + "closeOtherEditorsInGroup": "Diğer Düzenleyicileri Kapat", + "closeEditorsInGroup": "Gruptaki Tüm Düzenleyicileri Kapat", + "moveActiveGroupLeft": "Düzenleyici Grubunu Sola Taşı", + "moveActiveGroupRight": "Düzenleyici Grubunu Sağa Taşı", + "minimizeOtherEditorGroups": "Diğer Düzenleyici Gruplarını Küçült", + "evenEditorGroups": "Düzenleyici Grup Genişliklerini Eşitle", + "maximizeEditor": "Düzenleyici Grubunu Olabildiğince Genişlet ve Kenar Çubuğunu Gizle", + "keepEditor": "Düzenleyiciyi Tut", + "openNextEditor": "Sonraki Düzenleyiciyi Aç", + "openPreviousEditor": "Önceki Düzenleyiciyi Aç", + "nextEditorInGroup": "Gruptaki Sonraki Düzenleyiciyi Aç", + "openPreviousEditorInGroup": "Gruptaki Önceki Düzenleyiciyi Aç", + "navigateNext": "İleri Git", + "navigatePrevious": "Geri Dön", + "reopenClosedEditor": "Kapatılan Düzenleyiciyi Yeniden Aç", + "clearRecentFiles": "Son Açılanları Temizle", + "showEditorsInFirstGroup": "İlk Gruptaki Düzenleyicileri Göster", + "showEditorsInSecondGroup": "İkinci Gruptaki Düzenleyicileri Göster", + "showEditorsInThirdGroup": "Üçüncü Gruptaki Düzenleyicileri Göster", + "showEditorsInGroup": "Gruptaki Düzenleyicileri Göster", + "showAllEditors": "Tüm Düzenleyicileri Göster", + "openPreviousRecentlyUsedEditorInGroup": "Gruptaki Son Kullanılan Önceki Düzenleyiciyi Aç", + "openNextRecentlyUsedEditorInGroup": "Gruptaki Son Kullanılan Sonraki Düzenleyiciyi Aç", + "navigateEditorHistoryByInput": "Geçmişteki Önceki Düzenleyiciyi Aç", + "openNextRecentlyUsedEditor": "Son Kullanılan Sonraki Düzenleyiciyi Aç", + "openPreviousRecentlyUsedEditor": "Son Kullanılan Önceki Düzenleyiciyi Aç", + "clearEditorHistory": "Düzenleyici Geçmişini Temizle", + "focusLastEditorInStack": "Gruptaki Son Düzenleyiciyi Aç", + "moveEditorLeft": "Düzenleyiciyi Sola Taşı", + "moveEditorRight": "Düzenleyiciyi Sağa Taşı", + "moveEditorToPreviousGroup": "Düzenleyiciyi Önceki Gruba Taşı", + "moveEditorToNextGroup": "Düzenleyiciyi Sonraki Gruba Taşı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json new file mode 100644 index 0000000000..4aa45a2633 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/editorCommands.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorCommand.activeEditorMove.description": "Aktif düzenleyiciyi sekmeler veya gruplar halinde taşıyın", + "editorCommand.activeEditorMove.arg.name": "Aktif düzenleyici taşıma argümanı", + "editorCommand.activeEditorMove.arg.description": "Argüman Özellikleri:\n\t\t\t\t\t\t* 'to': Nereye taşınacağını belirten dize değeri.\n\t\t\t\t\t\t* 'by': Kaç birim taşınacağını belirten dize değeri. Sekme veya grup olarak.\n\t\t\t\t\t\t* 'value': Kaç tane pozisyonun taşınacağını belirten sayı değeri.\n\t\t\t\t\t", + "commandDeprecated": "**{0}** komutu kaldırıldı. Onun yerine **{1}** komutunu kullanabilirsiniz", + "openKeybindings": "Klavye Kısayollarını Yapılandır" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/editorPart.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/editorPart.i18n.json new file mode 100644 index 0000000000..ce5c002682 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/editorPart.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "groupOneVertical": "Sol", + "groupTwoVertical": "Orta", + "groupThreeVertical": "Sağ", + "groupOneHorizontal": "En Üst", + "groupTwoHorizontal": "Orta", + "groupThreeHorizontal": "En Alt", + "editorOpenError": "'{0}' açılamadı: {1}." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json new file mode 100644 index 0000000000..8a895d40f4 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/editorPicker.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, düzenleyici grubu seçici", + "groupLabel": "Grup: {0}", + "noResultsFoundInGroup": "Grupta eşleşen açık düzenleyici bulunamadı", + "noOpenedEditors": "Gruptaki açık düzenleyiciler listesi şu an boş", + "noResultsFound": "Eşleşen açık düzenleyici bulunamadı", + "noOpenedEditorsAllGroups": "Açık düzenleyiciler listesi şu an boş" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json new file mode 100644 index 0000000000..cf90d30cc4 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/editorStatus.i18n.json @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "singleSelectionRange": "Sat {0}, Süt {1} ({2} seçili)", + "singleSelection": "Sat {0}, Süt {1}", + "multiSelectionRange": "{0} seçim ({1} karakter seçildi)", + "multiSelection": "{0} seçim", + "endOfLineLineFeed": "LF", + "endOfLineCarriageReturnLineFeed": "CRLF", + "tabFocusModeEnabled": "Tab Odak Değiştirir", + "screenReaderDetected": "Ekran Okuyucu Algılandı", + "screenReaderDetectedExtra": "Bir Ekran Okuyucu kullanmıyorsanız, lütfen `editor.accessibilitySupport` ayarını \"off\" olarak değiştirin", + "disableTabMode": "Erişilebilirlik Modunu Devre Dışı Bırak", + "gotoLine": "Satıra Git", + "indentation": "Girinti", + "selectEncoding": "Kodlamayı Seç", + "selectEOL": "Satır Sonu Sıralamasını Seç", + "selectLanguageMode": "Dil Modunu Seçin", + "fileInfo": "Dosya Bilgisi", + "spacesSize": "Boşluk: {0}", + "tabSize": "Sekme Boyutu: {0}", + "showLanguageExtensions": "'{0}' için Market Eklentilerini Ara...", + "changeMode": "Dil Modunu Değiştir", + "noEditor": "Şu an aktif metin düzenleyici yok", + "languageDescription": "({0}) - Yapılandırılan Dil", + "languageDescriptionConfigured": "({0})", + "languagesPicks": "diller (tanımlayıcı)", + "configureModeSettings": "'{0}' dili tabanlı ayarları yapılandır...", + "configureAssociationsExt": "'{0}' için Dosya İlişkilendirmesini Yapılandır...", + "autoDetect": "Otomatik Algıla", + "pickLanguage": "Dil Modunu Seçin", + "currentAssociation": "Geçerli İlişkilendirme", + "pickLanguageToConfigure": " '{0}' ile İlişkilendirilecek Dil Modunu Seçin", + "changeIndentation": "Girintiyi Değiştir", + "noWritableCodeEditor": "Aktif kod düzenleyici salt okunur.", + "indentView": "görünümü değiştir", + "indentConvert": "dosyayı dönüştür", + "pickAction": "Eylem Seçin", + "changeEndOfLine": "Satır Sonu Sıralamasını Değiştir", + "pickEndOfLine": "Satır Sonu Sıralamasını Seç", + "changeEncoding": "Dosya Kodlamasını Değiştir", + "noFileEditor": "Şu an aktif dosya yok", + "saveWithEncoding": "Kodlama ile Kaydet", + "reopenWithEncoding": "Kodlama ile Yeniden Aç", + "guessedEncoding": "İçerikten tahmin edildi", + "pickEncodingForReopen": "Dosyayı Yeniden Açmak İçin Dosya Kodlaması Seçin", + "pickEncodingForSave": "Kaydedilecek Dosya Kodlamasını Seçin" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json new file mode 100644 index 0000000000..715477c77e --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/tabsTitleControl.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "araLabelTabActions": "Sekme eylemleri" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json new file mode 100644 index 0000000000..2e151f2f0e --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/textDiffEditor.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textDiffEditor": "Metin Diff Düzenleyicisi", + "readonlyEditorWithInputAriaLabel": "{0}. Salt okunur metin dosyası karşılaştırma düzenleyicisi.", + "readonlyEditorAriaLabel": "Salt okunur metin dosyası karşılaştırma düzenleyicisi.", + "editableEditorWithInputAriaLabel": "{0}. Metin dosyası karşılaştırma düzenleyicisi.", + "editableEditorAriaLabel": "Metin dosyası karşılaştırma düzenleyicisi.", + "navigate.next.label": "Sonraki Değişiklik", + "navigate.prev.label": "Önceki Değişiklik", + "inlineDiffLabel": "Satır İçi Görünüme Geç", + "sideBySideDiffLabel": "Yan Yana Görünüme Geç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/textEditor.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/textEditor.i18n.json new file mode 100644 index 0000000000..1be35e1de1 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/textEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorLabelWithGroup": "{0}, Grup {1}." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json new file mode 100644 index 0000000000..6598304da6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/textResourceEditor.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textEditor": "Metin Düzenleyicisi", + "readonlyEditorWithInputAriaLabel": "{0}. Salt okunur metin düzenleyici.", + "readonlyEditorAriaLabel": "Salt okunur metin düzenleyici.", + "untitledFileEditorWithInputAriaLabel": "{0}. İsimsiz dosya metin düzenleyici.", + "untitledFileEditorAriaLabel": "İsimsiz dosya metin düzenleyici." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/titleControl.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/titleControl.i18n.json new file mode 100644 index 0000000000..ef5074e481 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/titleControl.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "close": "Kapat", + "closeOthers": "Diğerlerini Kapat", + "closeRight": "Sağdakileri Kapat", + "closeAll": "Tümünü Kapat", + "closeAllUnmodified": "Değiştirilmeyenleri Kapat", + "keepOpen": "Açık Tut", + "showOpenedEditors": "Açık Düzenleyicileri Göster", + "araLabelEditorActions": "Düzenleyici eylemleri" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/panel/panelActions.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/panel/panelActions.i18n.json new file mode 100644 index 0000000000..e2d60f5454 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/panel/panelActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelActionTooltip": "{0} ({1})", + "closePanel": "Paneli Kapat", + "togglePanel": "Paneli Aç/Kapat", + "focusPanel": "Panele Odakla", + "toggleMaximizedPanel": "Panelin Ekranı Kaplamasını Aç/Kapat", + "maximizePanel": "Panel Boyutunu Olabildiğince Genişlet", + "minimizePanel": "Panel Boyutunu Geri Al", + "view": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/panel/panelPart.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/panel/panelPart.i18n.json new file mode 100644 index 0000000000..f6acb19242 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/panel/panelPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "panelSwitcherBarAriaLabel": "Aktif Panel Değiştirici" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json new file mode 100644 index 0000000000..3885262346 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/quickopen/quickOpenController.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inputModeEntryDescription": "{0} (Onaylamak için 'Enter' veya iptal etmek için 'Escape' tuşuna basın)", + "inputModeEntry": "Girdinizi onaylamak için 'Enter' veya iptal etmek için 'Escape' tuşuna basın", + "emptyPicks": "Seçilecek girdi yok", + "quickOpenInput": "Buradan gerçekleştirebileceğiniz eylemler hakkında yardım almak için '?' yazın", + "historyMatches": "yakınlarda açıldı", + "noResultsFound1": "Sonuç bulunamadı", + "canNotRunPlaceholder": "Bu hızlı açma işleyicisi geçerli bağlamda kullanılamaz", + "entryAriaLabel": "{0}, yakınlarda açıldı", + "removeFromEditorHistory": "Geçmişten Kaldır", + "pickHistory": "Geçmişten kaldırmak için bir düzenleyici girdisi seçin" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json new file mode 100644 index 0000000000..d6daba78c6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/quickopen/quickopen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen": "Dosyaya Git...", + "quickNavigateNext": "Hızlı Açta Sonrakine Git", + "quickNavigatePrevious": "Hızlı Açta Öncekine Git", + "quickSelectNext": "Hızlı Açta Sonrakini Seç", + "quickSelectPrevious": "Hızlı Açta Öncekini Seç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json new file mode 100644 index 0000000000..c64bdc47ae --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/sidebar/sidebarPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compositePart.hideSideBarLabel": "Kenar Çubuğunu Gizle", + "focusSideBar": "Kenar Çubuğuna Odakla", + "viewCategory": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json new file mode 100644 index 0000000000..69303b373c --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "canNotRun": "'{0}' komutu şu an etkin değildir ve çalıştırılamaz.", + "manageExtension": "Eklentiyi Yönet" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json new file mode 100644 index 0000000000..defe4c7e80 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/titlebar/titlebarPart.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "patchedWindowTitle": "[Desteklenmiyor]", + "devExtensionWindowTitlePrefix": "[Eklenti Geliştirme Sunucusu]" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/quickopen.i18n.json b/i18n/trk/src/vs/workbench/browser/quickopen.i18n.json new file mode 100644 index 0000000000..c8e8d3c204 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/quickopen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noResultsMatching": "Eşleşen sonuç yok", + "noResultsFound2": "Sonuç bulunamadı", + "entryAriaLabel": "{0}, komut" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/viewlet.i18n.json b/i18n/trk/src/vs/workbench/browser/viewlet.i18n.json new file mode 100644 index 0000000000..808fded3a5 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/viewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "collapse": "Tümünü Daralt" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/common/theme.i18n.json b/i18n/trk/src/vs/workbench/common/theme.i18n.json new file mode 100644 index 0000000000..a2abad5775 --- /dev/null +++ b/i18n/trk/src/vs/workbench/common/theme.i18n.json @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabActiveBackground": "Aktif sekme arka plan rengi. Sekmeler, düzenleyici alanındaki düzenleyicilerin kapsayıcılarıdır. Bir düzenleyici grubunda birden fazla sekme açılabilir. Birden fazla düzenleyici grupları var olabilir.", + "tabInactiveBackground": "Pasif sekme arka plan rengi. Sekmeler, düzenleyici alanındaki düzenleyicilerin kapsayıcılarıdır. Bir düzenleyici grubunda birden fazla sekme açılabilir. Birden fazla düzenleyici grupları var olabilir.", + "tabBorder": "Sekmeleri birbirinden ayıran kenarlığın rengi. Sekmeler, düzenleyici alanındaki düzenleyicilerin kapsayıcılarıdır. Bir düzenleyici grubunda birden fazla sekme açılabilir. Birden fazla düzenleyici grupları var olabilir.", + "tabActiveBorder": "Aktif sekmeleri vurgulayacak kenarlık. Sekmeler, düzenleyici alanındaki düzenleyicilerin kapsayıcılarıdır. Bir düzenleyici grubunda birden fazla sekme açılabilir. Birden fazla düzenleyici grupları var olabilir.", + "tabActiveUnfocusedBorder": "Odaklanılmamış bir gruptaki aktif sekmeleri vurgulayacak kenarlık. Sekmeler, düzenleyici alanındaki düzenleyicilerin kapsayıcılarıdır. Bir düzenleyici grubunda birden fazla sekme açılabilir. Birden fazla düzenleyici grupları var olabilir.", + "tabActiveForeground": "Aktif bir gruptaki aktif sekmenin ön plan rengi. Sekmeler, düzenleyici alanındaki düzenleyicilerin kapsayıcılarıdır. Bir düzenleyici grubunda birden fazla sekme açılabilir. Birden fazla düzenleyici grupları var olabilir.", + "tabInactiveForeground": "Aktif bir gruptaki pasif sekmenin ön plan rengi. Sekmeler, düzenleyici alanındaki düzenleyicilerin kapsayıcılarıdır. Bir düzenleyici grubunda birden fazla sekme açılabilir. Birden fazla düzenleyici grupları var olabilir.", + "tabUnfocusedActiveForeground": "Odaklanılmamış bir gruptaki aktif sekmenin ön plan rengi. Sekmeler, düzenleyici alanındaki düzenleyicilerin kapsayıcılarıdır. Bir düzenleyici grubunda birden fazla sekme açılabilir. Birden fazla düzenleyici grupları var olabilir.", + "tabUnfocusedInactiveForeground": "Odaklanılmamış bir gruptaki pasif sekmenin ön plan rengi. Sekmeler, düzenleyici alanındaki düzenleyicilerin kapsayıcılarıdır. Bir düzenleyici grubunda birden fazla sekme açılabilir. Birden fazla düzenleyici grupları var olabilir.", + "editorGroupBackground": "Bir düzenleyici grubunun arka plan rengi. Düzenleyici grupları, düzenleyicilerin kapsayıcılarıdır. Arka plan rengi, düzenleyici grubunu sürüklerken gösterilir.", + "tabsContainerBackground": "Sekmeler etkinleştirilmiş durumdayken, düzenleyici grubu başlık üstbilgisi arka plan rengi. Düzenleyici grupları, düzenleyicilerin kapsayıcılarıdır. ", + "tabsContainerBorder": "Sekmeler etkinleştirilmiş durumdayken, düzenleyici grubu başlık üstbilgisi kenarlık rengi. Düzenleyici grupları, düzenleyicilerin kapsayıcılarıdır. ", + "editorGroupHeaderBackground": "Sekmeler devre dışı iken, düzenleyici grubu başlık üstbilgisi arka plan rengi. Düzenleyici grupları, düzenleyicilerin kapsayıcılarıdır. ", + "editorGroupBorder": "Birden fazla düzenleyici grubunu birbirinden ayıracak renk. Düzenleyici grupları, düzenleyicilerin kapsayıcılarıdır. ", + "editorDragAndDropBackground": "Düzenleyici grubunu sürüklerken gösterilecek arka plan rengi. Düzenleyici içeriğinin hâlâ iyi görünmeye devam edebilmesi için renk şeffaf olmalıdır.", + "panelBackground": "Panel arka plan rengi. Paneller düzenleyici alanının altında gösterilir ve çıktı ve entegre terminal gibi görünümler içerir.", + "panelBorder": "Paneli düzenleyiciden ayıran üstteki kenarlık rengi. Paneller düzenleyici alanının altında gösterilir ve çıktı ve entegre terminal gibi görünümler içerir.", + "panelActiveTitleForeground": "Aktif panelin başlık rengi. Paneller düzenleyici alanının altında gösterilir ve çıktı ve entegre terminal gibi görünümler içerir.", + "panelInactiveTitleForeground": "Pasif panelin başlık rengi. Paneller düzenleyici alanının altında gösterilir ve çıktı ve entegre terminal gibi görünümler içerir.", + "panelActiveTitleBorder": "Aktif başlığının kenarlık rengi. Paneller düzenleyici alanının altında gösterilir ve çıktı ve entegre terminal gibi görünümler içerir.", + "statusBarForeground": "Bir çalışma alanı açıkken durum çubuğu ön plan rengi. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarNoFolderForeground": "Hiçbir klasör açık değilken durum çubuğu ön plan rengi. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarBackground": "Bir çalışma alanı açıkken durum çubuğu arka plan rengi. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarNoFolderBackground": "Hiçbir klasör açık değilken durum çubuğu arka plan rengi. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarBorder": "Durum çubuğunu kenar çubuğundan ve düzenleyiciden ayıran kenarlık rengi. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarNoFolderBorder": "Hiçbir klasör açık olmadığında durum çubuğunu kenar çubuğundan ve düzenleyiciden ayıran kenarlık rengi. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarItemActiveBackground": "Durum çubuğu ögesi tıklanırken arka plan rengi. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarItemHoverBackground": "Durum çubuğu ögesinin mouse ile üzerine gelindiğindeki arka plan rengi. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarProminentItemBackground": "Durum çubuğu belirgin ögelerinin arka plan rengi. Belirgin ögeler, önemi belirtmek için diğer durum çubuğu girdilerinden öne çıkarılır. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarProminentItemHoverBackground": "Durum çubuğu belirgin ögelerinin mouse ile üzerine gelindiğindeki arka plan rengi. Belirgin ögeler, önemi belirtmek için diğer durum çubuğu girdilerinden öne çıkarılır. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "activityBarBackground": "Etkinlik çubuğu arka plan rengi. Etkinlik çubuğu, en sol veya en sağda gösterilir ve kenar çubuğunun görünümleriyle yer değiştirmeye izin verir.", + "activityBarForeground": "Etkinlik çubuğu ön plan rengi (ör. simgeler için kullanılır). Etkinlik çubuğu, en sol veya en sağda gösterilir ve kenar çubuğunun görünümleriyle yer değiştirmeye izin verir.", + "activityBarBorder": "Etkinlik çubuğunu kenar çubuğundan ayıran kenarlığın rengi. Etkinlik çubuğu, en sol veya en sağda gösterilir ve kenar çubuğunun görünümleriyle yer değiştirmeye izin verir.", + "activityBarDragAndDropBackground": "Etkinlik çubuğu ögeleri için sürükle bırak geri bildirim rengi. Etkinlik çubuğu girdilerinin hâlâ iyi görünmeye devam edebilmesi için renk şeffaf olmalıdır. Etkinlik çubuğu, en sol veya en sağda gösterilir ve kenar çubuğunun görünümleriyle yer değiştirmeye izin verir.", + "activityBarBadgeBackground": "Etkinlik çubuğu bildirim göstergesi arka plan rengi. Etkinlik çubuğu, en sol veya en sağda gösterilir ve kenar çubuğunun görünümleriyle yer değiştirmeye izin verir.", + "activityBarBadgeForeground": "Etkinlik çubuğu bildirim göstergesi ön plan rengi. Etkinlik çubuğu, en sol veya en sağda gösterilir ve kenar çubuğunun görünümleriyle yer değiştirmeye izin verir.", + "sideBarBackground": "Kenar çubuğu arka plan rengi. Kenar çubuğu, gezgin ve arama gibi görünümlerin kapsayıcısıdır.", + "sideBarForeground": "Kenar çubuğu ön plan rengi. Kenar çubuğu, gezgin ve arama gibi görünümlerin kapsayıcısıdır.", + "sideBarBorder": "Kenar çubuğunu düzenleyiciden ayıran taraftaki kenarlığın rengi. Kenar çubuğu, gezgin ve arama gibi görünümlerin kapsayıcısıdır.", + "sideBarTitleForeground": "Kenar çubuğu başlığı ön plan rengi. Kenar çubuğu, gezgin ve arama gibi görünümlerin kapsayıcısıdır.", + "sideBarDragAndDropBackground": "Kenar çubuğu bölümleri için sürükle bırak geri bildirim rengi. Kenar çubuğu bölümlerinin hâlâ iyi görünmeye devam edebilmesi için renk şeffaf olmalıdır. Kenar çubuğu, gezgin ve arama gibi görünümlerin kapsayıcısıdır.", + "sideBarSectionHeaderBackground": "Kenar çubuğu bölüm başlığı arka plan rengi. Kenar çubuğu, gezgin ve arama gibi görünümlerin kapsayıcısıdır.", + "sideBarSectionHeaderForeground": "Kenar çubuğu bölüm başlığı ön plan rengi. Kenar çubuğu, gezgin ve arama gibi görünümlerin kapsayıcısıdır.", + "titleBarActiveForeground": "Pencere aktifken başlık çubuğu ön planı. Bu rengin sadece macOS'da destekleneceğini unutmayın.", + "titleBarInactiveForeground": "Pencere pasifken başlık çubuğu ön planı. Bu rengin sadece macOS'da destekleneceğini unutmayın.", + "titleBarActiveBackground": "Pencere aktifken başlık çubuğu arka planı. Bu rengin sadece macOS'da destekleneceğini unutmayın.", + "titleBarInactiveBackground": "Pencere pasifken başlık çubuğu arka planı. Bu rengin sadece macOS'da destekleneceğini unutmayın.", + "titleBarBorder": "Başlık çubuğu kenarlık rengi. Bu rengin sadece macOS'da destekleneceğini unutmayın.", + "notificationsForeground": "Bildirim ön plan rengi. Bildirimler pencerenin üst kısmından içeri girer.", + "notificationsBackground": "Bildirim arka plan rengi. Bildirimler pencerenin üst kısmından içeri girer.", + "notificationsButtonBackground": "Bildirim butonu arka plan rengi. Bildirimler pencerenin üst kısmından içeri girer.", + "notificationsButtonHoverBackground": "Fareyle üzerine gelindiğinde bildirim butonu arka plan rengi. Bildirimler pencerenin üst kısmından içeri girer.", + "notificationsButtonForeground": "Bildirim butonu ön plan rengi. Bildirimler pencerenin üst kısmından içeri girer.", + "notificationsInfoBackground": "Bildirimlerdeki bilgi arka plan rengi. Bildirimler pencerenin üst kısmından içeri girer.", + "notificationsInfoForeground": "Bildirimlerdeki bilgi ön plan rengi. Bildirimler pencerenin üst kısmından içeri girer.", + "notificationsWarningBackground": "Bildirim uyarı arka plan rengi. Bildirimler pencerenin üst kısmından içeri girer.", + "notificationsWarningForeground": "Bildirim uyarı ön plan rengi. Bildirimler pencerenin üst kısmından içeri girer.", + "notificationsErrorBackground": "Bildirim hata arka plan rengi. Bildirimler pencerenin üst kısmından içeri girer.", + "notificationsErrorForeground": "Bildirim hata ön plan rengi. Bildirimler pencerenin üst kısmından içeri girer." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/trk/src/vs/workbench/electron-browser/actions.i18n.json new file mode 100644 index 0000000000..4f1e8f5833 --- /dev/null +++ b/i18n/trk/src/vs/workbench/electron-browser/actions.i18n.json @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "closeActiveEditor": "Düzenleyiciyi Kapat", + "closeWindow": "Pencereyi Kapat", + "closeWorkspace": "Çalışma Alanını Kapat", + "noWorkspaceOpened": "Şu an bu örnekte kapatmak için açık bir çalışma alanı bulunmuyor.", + "newWindow": "Yeni Pencere", + "toggleFullScreen": "Tam Ekranı Aç/Kapat", + "toggleMenuBar": "Menü Çubuğunu Gizle/Göster", + "toggleDevTools": "Geliştirici Araçlarını Aç/Kapat", + "zoomIn": "Yakınlaştır", + "zoomOut": "Uzaklaştır", + "zoomReset": "Yakınlaştırmayı Sıfırla", + "appPerf": "Başlangıç Performansı", + "reloadWindow": "Pencereyi Yeniden Yükle", + "switchWindowPlaceHolder": "Geçilecek pencereyi seçin", + "current": "Geçerli Pencere", + "close": "Pencereyi Kapat", + "switchWindow": "Pencere Değiştir...", + "quickSwitchWindow": "Hızlı Pencere Değiştir...", + "workspaces": "çalışma alanları", + "files": "dosyalar", + "openRecentPlaceHolderMac": "Açmak için seçin (yeni pencerede açmak için Cmd tuşunu basılı tutun)", + "openRecentPlaceHolder": "Açmak için seçin (yeni pencerede açmak için Ctrl tuşunu basılı tutun)", + "remove": "Son Açılanlardan Kaldır", + "openRecent": "Son Kullanılanları Aç...", + "quickOpenRecent": "Son Kullanılanları Hızlı Aç...", + "closeMessages": "Bildirim İletilerini Kapat", + "reportIssues": "Sorunları Bildir", + "reportPerformanceIssue": "Performans Sorunu Bildir", + "keybindingsReference": "Klavye Kısayolları Başvurusu", + "openDocumentationUrl": "Belgeler", + "openIntroductoryVideosUrl": "Tanıtım Videoları", + "openTipsAndTricksUrl": "İpuçları ve Püf noktaları", + "toggleSharedProcess": "Paylaşılan İşlemi Göster/Gizle", + "navigateLeft": "Soldaki Görünüme Git", + "navigateRight": "Sağdaki Görünüme Git", + "navigateUp": "Üstteki Görünüme Git", + "navigateDown": "Alttaki Görünüme Git", + "increaseViewSize": "Geçerli Görünüm Boyutunu Artır", + "decreaseViewSize": "Geçerli Görünüm Boyutunu Azalt" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/electron-browser/commands.i18n.json b/i18n/trk/src/vs/workbench/electron-browser/commands.i18n.json new file mode 100644 index 0000000000..9fd0a30bd4 --- /dev/null +++ b/i18n/trk/src/vs/workbench/electron-browser/commands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "diffLeftRightLabel": "{0} ⟷ {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/electron-browser/extensionHost.i18n.json b/i18n/trk/src/vs/workbench/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..b5dfa5a4c4 --- /dev/null +++ b/i18n/trk/src/vs/workbench/electron-browser/extensionHost.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "Eklenti sunucusu 10 saniye içinde başlamadı, ilk satırda durdurulmuş olabilir ve devam etmesi için bir hata ayıklayıcıya ihtiyacı olabilir.", + "extensionHostProcess.startupFail": "Eklenti sunucusu 10 saniye içinde başlamadı, bu bir sorun olabilir.", + "extensionHostProcess.error": "Eklenti sunucusundan hata: {0}", + "devTools": "Geliştirici Araçları", + "extensionHostProcess.crash": "Eklenti sunucusu beklenmeyen biçimde sonlandırıldı. Lütfen kurtarmak için pencereyi yeniden yükleyin." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/trk/src/vs/workbench/electron-browser/main.contribution.i18n.json new file mode 100644 index 0000000000..18f736a118 --- /dev/null +++ b/i18n/trk/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "view": "Görüntüle", + "help": "Yardım", + "file": "Dosya", + "workspaces": "Çalışma Alanları", + "developer": "Geliştirici", + "showEditorTabs": "Açık düzenleyicilerin sekmelerde gösterilip gösterilmeyeceğini denetler", + "editorTabCloseButton": "Düzenleyici sekmelerinin kapat butonlarının konumunu denetler veya 'off' olarak ayarlandığında devre dışı bırakır.", + "showIcons": "Açık düzenleyicilerin bir simge ile gösterilip gösterilmemelerini denetler. Bu, bir simge temasının etkinleştirilmesini de gerektirir.", + "enablePreview": "Açık düzenleyicilerin önizleme olarak gösterilip gösterilmeyeceğini denetler. Önizleme düzenleyicileri kalıcı olarak açılana kadar (ör. çift tıklama veya düzenleme ile) tekrar kullanılırlar.", + "enablePreviewFromQuickOpen": "Hızlı Aç'taki açık düzenleyicilerin önizleme olarak gösterilip gösterilmeyeceğini denetler. Önizleme düzenleyicileri kalıcı olarak açılana kadar (ör. çift tıklama veya düzenleme ile) tekrar kullanılırlar.", + "editorOpenPositioning": "Düzenleyicilerin nerede açılacağını denetler. Düzenleyicileri, geçerli olanın soluna veya sağına açmak için 'left' veya 'right' seçeneklerinden birini seçin. Düzenleyicileri, geçerli olandan bağımsız bir şekilde açmak için 'first' veya 'last' seçeneklerinden birini seçin.", + "revealIfOpen": "Düzenleyicinin görünen gruplardan herhangi birinde açıldıysa ortaya çıkarılıp çıkarılmayacağını denetler. Devre dışı bırakılırsa; bir düzenleyici, o an aktif düzenleyici grubunda açılmayı tercih edecektir. Etkinleştirilirse; o an aktif düzenleyici grubunda tekrar açılmak yerine, zaten açık olan düzenleyici ortaya çıkarılacaktır. Bu ayarın yok sayılacağı bazı durumların olduğunu unutmayın, ör. bir düzenleyiciyi, belirli bir grupta veya o an aktif grubun yanına açmaya zorladığınızda. ", + "commandHistory": "Komut paleti geçmişinde tutulacak son kullanılan komutların sayısını denetler. Komut geçmişini kapatmak için 0 olarak ayarlayın.", + "preserveInput": "Komut paletine son girilen girdinin, bir sonraki açılışta tekrar yer alıp almayacağını denetler.", + "closeOnFocusLost": "Hızlı Aç'ın odağını kaybettiğinde otomatik olarak kapanıp kapanmayacağını denetler.", + "openDefaultSettings": "Ayarları açmanın ayrıca tüm varsayılan ayarları gösteren bir düzenleyici açıp açmayacağını denetler.", + "sideBarLocation": "Kenar çubuğunun konumunu denetler. Çalışma ekranının ya solunda ya da sağında gösterilebilir.", + "statusBarVisibility": "Çalışma ekranının altındaki durum çubuğunun görünürlüğünü denetler.", + "activityBarVisibility": "Çalışma ekranındaki etkinlik çubuğunun görünürlüğünü denetler.", + "closeOnFileDelete": "Düzenleyicinin gösterdiği bir dosyanın, başka bir işlem tarafından silinmesi veya yeniden adlandırması durumunda dosyayı otomatik olarak kapatıp kapatmamasını denetler. Bunu devre dışı bırakmak, böyle bir durumda düzenleyicinin kaydedilmemiş değişiklikler içeriyor durumunda kalmasını sağlar. Uygulama içinde silmek, düzenleyiciyi her zaman kapatır ve kaydedilmemiş değişiklikler içeren dosyalar, verilerinizin korunması için otomatik olarak kapatılmaz.", + "fontAliasing": "Çalışma ekranındaki yazı tipi yumuşatma yöntemini denetler.\n- default: Alt-piksel yazı tipi yumuşatma. Bu, çoğu retina olmayan ekranda en keskin metni verir\n- antialiased: Alt-pikselin tersine, pikselin seviyesine göre yazı tipini yumuşat. Yazı tipinin genel olarak daha açık görünmesini sağlayabilir\n- none: Yazı tipi yumuşatmayı devre dışı bırakır. Metin pürüzlü keskin kenarlarla gösterilir.", + "workbench.fontAliasing.default": "Alt-piksel yazı tipi yumuşatma. Bu, çoğu retina olmayan ekranda en keskin metni verir.", + "workbench.fontAliasing.antialiased": "Alt-pikselin tersine, pikselin seviyesine göre yazı tipini yumuşat. Yazı tipinin genel olarak daha açık görünmesini sağlayabilir.", + "workbench.fontAliasing.none": "Yazı tipi yumuşatmayı devre dışı bırakır. Metin pürüzlü keskin kenarlarla gösterilir.", + "swipeToNavigate": "Yatay olarak üç parmakla kaydırma ile açık dosyalar arasında gezinin.", + "workbenchConfigurationTitle": "Çalışma Ekranı", + "window.openFilesInNewWindow.on": "Dosyalar yeni bir pencerede açılacak", + "window.openFilesInNewWindow.off": "Dosyalar, dosyaların klasörünün olduğu pencerede veya son aktif pencerede açılacak", + "window.openFilesInNewWindow.default": "Dosyalar, Dock veya Finder ile açılmadıkça (sadece macOS için) dosyaların klasörünün olduğu pencerede veya son aktif pencerede açılacak", + "openFilesInNewWindow": "Dosyaların yeni bir pencerede açılıp açılmayacağını denetler.\n- default: dosyalar, Dock veya Finder ile açılmadıkça (sadece macOS için) dosyaların klasörünün olduğu pencerede veya son aktif pencerede açılacak\n- on: dosyalar yeni bir pencerede açılacak\n- off: dosyalar, dosyaların klasörünün olduğu pencerede veya son aktif pencerede açılacak\nBu ayarın halen yok sayılacağı durumlar olabilir (ör. -new-window veya -reuse-window komut satırı seçenekleri kullanılırken).", + "window.openFoldersInNewWindow.on": "Klasörler yeni bir pencerede açılacak", + "window.openFoldersInNewWindow.off": "Klasörler son aktif pencereyi değiştirir", + "window.openFoldersInNewWindow.default": "Klasörler, bir klasör uygulama içinden seçilmedikçe (ör. Dosya menüsünden) yeni bir pencerede açılır", + "openFoldersInNewWindow": "Klasörlerin yeni bir pencerede mi açılacağını yoksa son aktif pencereyi mi değiştireceğini denetler.\n- default: klasörler, bir klasör uygulama içinden seçilmedikçe (ör. Dosya menüsünden) yeni bir pencerede açılır\n- on: klasörler yeni bir pencerede açılır\n- off: klasörler son aktif pencereyi değiştirir\nBu ayarın halen yok sayılacağı durumlar olabilir (ör. -new-window veya -reuse-window komut satırı seçenekleri kullanılırken).", + "window.reopenFolders.all": "Tüm pencereleri yeniden aç.", + "window.reopenFolders.folders": "Tüm klasörleri yeniden aç. Boş çalışma alanları geri yüklenmeyecektir.", + "window.reopenFolders.one": "Son aktif pencereyi yeniden aç.", + "window.reopenFolders.none": "Asla bir pencereyi yeniden açma. Her zaman boş bir pencereyle başla.", + "restoreWindows": "Pencerelerin, bir yeniden başlatma sonrası nasıl yeniden açılacağını denetler. Her zaman boş bir çalışma alanı ile başlamak için 'none', üzerinde çalıştığınız son pencereyi yeniden açmak için 'one', açık klasör bulunduran tüm pencereleri yeniden açmak için 'folders' veya son oturumunuzdaki tüm pencereleri yeniden açmak için 'all' seçeneğini seçin.", + "restoreFullscreen": "Bir pencere tam ekran modundayken çıkıldıysa, bu pencerenin tam ekran moduna geri dönüp dönmeyeceğini denetler.", + "zoomLevel": "Pencerenin yakınlaştırma düzeyini ayarlayın. Orijinal boyut 0'dır ve üstündeki (ör. 1) veya altındaki (ör. -1) her artırma 20% daha fazla veya az yakınlaştırmayı temsil eder. Yakınlaştırma düzeyini daha ince ayrıntılarla ayarlamak için ondalık değerler de girebilirsiniz.", + "title": "Pencere başlığını aktif düzenleyiciye bağlı olarak denetler. Değişkenler, bağlama göre değiştirilir:\n${activeEditorShort}: ör. myFile.txt\n${activeEditorMedium}: ör. myFolder/myFile.txt\n${activeEditorLong}: ör. /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: ör. myFolder\n${folderPath}: ör. /Users/Development/myFolder\n${rootName}: ör. myFolder1, myFolder2, myFolder3\n${rootPath}: ör. /Users/Development/myWorkspace\n${appName}: ör. VS Code\n${dirty}: etkin düzenleyici kaydedilmemiş değişiklikler içeriyorsa, değişiklik göstergesi\n${separator}: şartlı ayırıcı (\" - \") yalnızca değer içeren değişkenlerle çevrili olduğunda gösterilir", + "window.newWindowDimensions.default": "Yeni pencereleri ekranın ortasında açın.", + "window.newWindowDimensions.inherit": "Yeni pencereleri son aktif pencere ile aynı ölçülerde açın.", + "window.newWindowDimensions.maximized": "Yeni pencereleri ekranı kapla modunda açın.", + "window.newWindowDimensions.fullscreen": "Yeni pencereleri tam ekran modunda açın.", + "newWindowDimensions": "En az bir pencere açıkken, açılacak yeni bir pencerenin boyutlarını denetler. Varsayılan olarak; yeni pencere ekranın ortasında küçük bir boyutta açılır. 'inherit' olarak ayarlandığında, pencere aktif olan son pencere ile aynı boyutları alacaktır. 'maximized' olarak ayarlandığında, ekranı kaplar biçimde açılır ve 'fullscreen' olarak yapılandırılmışsa, tam ekran olarak açılır. Bu ayarın açılan ilk pencere üzerinde bir etkisi olmadığını unutmayın. İlk pencere, daima boyutunu ve konumunu kapanmadan önce bıraktığınız şekilde geri yükler.", + "closeWhenEmpty": "Son düzenleyiciyi kapatmanın pencereyi de kapatıp kapatmayacağını denetler. Bu ayar sadece klasör göstermeyen pencereler için uygulanır.", + "window.menuBarVisibility.default": "Menü, sadece tam ekran modunda gizlenir.", + "window.menuBarVisibility.visible": "Menü, tam ekran modunda bile daima görünür.", + "window.menuBarVisibility.toggle": "Menü gizlidir, fakat Alt tuşuyla görüntülenebilir.", + "window.menuBarVisibility.hidden": "Menü daima gizlidir.", + "menuBarVisibility": "Menü çubuğunun gizliliğini denetleyin. 'toggle' ayarı, menü çubuğu gizlidir ve Alt tuşuna bir kez basıldığında gösterilir anlamına gelir. Varsayılan olarak; menü çubuğu, pencere tam ekran değilse görünür durumdadır.", + "enableMenuBarMnemonics": "Etkinleştirilirse, ana menüler Alt tuşu kısayollarıyla açılabilir. Anımsatıcıları devre dışı bırakmak, bu Alt tuşu kısayollarının düzenleyici komutlarının yerine kullanılmasını sağlar.", + "autoDetectHighContrast": "Etkinleştirilirse; eğer Windows bir yüksek karşıtlık teması kullanıyorsa, otomatik olarak yüksek karşıtlık temasına geçiş yapılır; ve Windows, yüksek karşıtlık temasını kullanmayı bıraktığında koyu temaya geçiş yapılır.", + "titleBarStyle": "Pencere başlık çubuğunun görünümünü ayarlayın. Değişikliklerin uygulanması için tam yeniden başlatma gerekir.", + "window.nativeTabs": "macOS Sierra pencere sekmelerini etkinleştirir. Değişikliklerin uygulanması için tam yeniden başlatma gerekeceğini ve yerel sekmelerin, eğer yapılandırılmışsa özel başlık çubuğu stilini devre dışı bıracağını unutmayın.", + "windowConfigurationTitle": "Pencere", + "zenModeConfigurationTitle": "Zen Modu", + "zenMode.fullScreen": "Zen Moduna geçmenin ayrıca çalışma ekranını tam ekran moduna geçirip geçirmeyeceğini denetler.", + "zenMode.hideTabs": "Zen Moduna geçmenin ayrıca çalışma ekranı sekmelerini gizleyip gizlemeyeceğini denetler.", + "zenMode.hideStatusBar": "Zen Moduna geçmenin ayrıca çalışma ekranının altındaki durum çubuğunu gizleyip gizlemeyeceğini denetler.", + "zenMode.hideActivityBar": "Zen Moduna geçmenin ayrıca çalışma ekranının solundaki etkinlik çubuğunu gizleyip gizlemeyeceğini denetler.", + "zenMode.restore": "Bir pencere Zen modundayken çıkıldıysa, bu pencerenin Zen moduna geri dönüp dönmeyeceğini denetler." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/electron-browser/main.i18n.json b/i18n/trk/src/vs/workbench/electron-browser/main.i18n.json new file mode 100644 index 0000000000..97b083e466 --- /dev/null +++ b/i18n/trk/src/vs/workbench/electron-browser/main.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "loaderError": "Gerekli bir dosya yüklenemedi. Artık İnternet'e bağlı değilsiniz veya bağlı olduğunuz sunucu çevrimdışı. Yeniden denemek için lütfen tarayıcıyı yenileyin.", + "loaderErrorNative": "Gerekli bir dosya yüklenemedi. Yeniden denemek için lütfen uygulamayı yeniden başlatın. Ayrıntılar: {0}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/electron-browser/shell.i18n.json b/i18n/trk/src/vs/workbench/electron-browser/shell.i18n.json new file mode 100644 index 0000000000..be2c81af0a --- /dev/null +++ b/i18n/trk/src/vs/workbench/electron-browser/shell.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "runningAsRoot": "Code'u 'root' olarak çalıştırmamanız önerilir." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/electron-browser/window.i18n.json b/i18n/trk/src/vs/workbench/electron-browser/window.i18n.json new file mode 100644 index 0000000000..e50f62148b --- /dev/null +++ b/i18n/trk/src/vs/workbench/electron-browser/window.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "undo": "Geri Al", + "redo": "Yinele", + "cut": "Kes", + "copy": "Kopyala", + "paste": "Yapıştır", + "selectAll": "Tümünü Seç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/electron-browser/workbench.i18n.json b/i18n/trk/src/vs/workbench/electron-browser/workbench.i18n.json new file mode 100644 index 0000000000..628f272131 --- /dev/null +++ b/i18n/trk/src/vs/workbench/electron-browser/workbench.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "developer": "Geliştirici", + "file": "Dosya" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/node/extensionHostMain.i18n.json b/i18n/trk/src/vs/workbench/node/extensionHostMain.i18n.json new file mode 100644 index 0000000000..af120e8494 --- /dev/null +++ b/i18n/trk/src/vs/workbench/node/extensionHostMain.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionTestError": "{0} yolu, geçerli bir eklenti test çalıştırıcısına işaret etmiyor." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/node/extensionPoints.i18n.json b/i18n/trk/src/vs/workbench/node/extensionPoints.i18n.json new file mode 100644 index 0000000000..e1fbeaa5a2 --- /dev/null +++ b/i18n/trk/src/vs/workbench/node/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "{0} ayrıştırılamadı: {1}.", + "fileReadFail": "{0} dosyası okunamadı: {1}.", + "jsonsParseFail": "{0} veya {1} ayrıştırılamadı: {2}.", + "missingNLSKey": "{0} anahtarı için mesaj bulunamadı." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json new file mode 100644 index 0000000000..af06ea9213 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/cli/electron-browser/cli.contribution.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "install": "'{0}' kabuk komutunu PATH'e yükle", + "not available": "Bu komut mevcut değil", + "successIn": "'{0}' kabuk komutu PATH'e başarıyla yüklendi.", + "warnEscalation": "Code, şimdi kabuk komutunu yüklemek üzere yönetici ayrıcalıkları için 'osascript' ile izin isteyecektir.", + "ok": "Tamam", + "cantCreateBinFolder": "'/usr/local/bin' oluşturulamadı.", + "cancel2": "İptal", + "aborted": "Durduruldu", + "uninstall": "'{0}' kabuk komutunu PATH'den kaldır", + "successFrom": "'{0}' kabuk komutu PATH'den başarıyla kaldırıldı.", + "shellCommand": "Kabuk Komutu" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json new file mode 100644 index 0000000000..699a8042b5 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emergencyConfOn": "Şu an `editor.accessibilitySupport` ayarı 'on' olarak değiştiriliyor.", + "openingDocs": "Şu an VS Code Erişilebilirlik belgeleri sayfası açılıyor.", + "introMsg": "VS Code'un erişilebilirlik seçeneklerini denediğiniz için teşekkür ederiz.", + "status": "Durum:", + "changeConfigToOnMac": "Düzenleyicinin kalıcı olarak bir Ekran Okuyucu ile kullanılmak üzere optimize edilmesini ayarlamak için Command+E tuşlarına basın.", + "changeConfigToOnWinLinux": "Düzenleyicinin kalıcı olarak bir Ekran Okuyucu ile kullanılmak üzere optimize edilmesini ayarlamak için Control+E tuşlarına basın.", + "auto_unknown": "Düzenleyici, bir Ekran Okuyucu'nun ne zaman bağlandığını algılamak için platform API'larını kullanmak üzere ayarlanmış, fakat geçerli çalışma zamanı bunu desteklemiyor.", + "auto_on": "Düzenleyici, bir Ekran Okuyucu'nun bağlandığını otomatik olarak algıladı.", + "auto_off": "Düzenleyici, bir Ekran Okuyucu'nun ne zaman bağlandığını otomatik olarak algılamak için yapılandırılmış, şu anki durum böyle değil.", + "configuredOn": "Düzenleyici kalıcı olarak bir Ekran Okuyucu ile kullanılmak üzere optimize edilmesi için yapılandırılmış - bunu, `editor.accessibilitySupport` ayarını düzenleyerek değiştirebilirsiniz.", + "configuredOff": "Düzenleyici hiçbir zaman bir Ekran Okuyucu ile kullanılmak üzere optimize edilmemesi için yapılandırılmış.", + "tabFocusModeOnMsg": "Geçerli düzenleyicide Tab tuşuna basmak odağı bir sonraki odaklanabilir ögeye kaydıracaktır. {0} tuşuna basarak bu davranışı açıp kapatın.", + "tabFocusModeOnMsgNoKb": "Geçerli düzenleyicide Tab tuşuna basmak odağı bir sonraki odaklanabilir ögeye kaydıracaktır. {0} komutu, şu an bir tuş bağı ile tetiklenemez.", + "tabFocusModeOffMsg": "Geçerli düzenleyicide Tab tuşuna basmak bir sekme karakteri ekleyecektir. {0} tuşuna basarak bu davranışı açıp kapatın.", + "tabFocusModeOffMsgNoKb": "Geçerli düzenleyicide Tab tuşuna basmak bir sekme karakteri ekleyecektir. {0} komutu, şu an bir tuş bağı ile tetiklenemez.", + "openDocMac": "Erişilebilirlik ile ilgili daha fazla VS Code bilgisi içeren bir tarayıcı penceresi açmak için Command+H tuşlarına basın.", + "openDocWinLinux": "Erişilebilirlik ile ilgili daha fazla VS Code bilgisi içeren bir tarayıcı penceresi açmak için Control+H tuşlarına basın.", + "outroMsg": "Escape veya Shift+Escape tuşlarına basarak bu ipucunu kapatabilir ve düzenleyiciye dönebilirsiniz.", + "ShowAccessibilityHelpAction": "Erişilebilirlik Yardımını Göster" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json new file mode 100644 index 0000000000..d8f0b066bc --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.inspectKeyMap": "Geliştirici: Tuş Eşlemelerini Denetle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..5e72d724b8 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Geliştirici: TM Kapsamlarını Denetle", + "inspectTMScopesWidget.loading": "Yükleniyor..." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json new file mode 100644 index 0000000000..66d322d0c6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "parseErrors": "{0} için ayrıştırma hataları: {1}", + "schema.openBracket": "Açılış ayracı karakteri veya dize sırası.", + "schema.closeBracket": "Kapanış ayracı karakteri veya dize sırası.", + "schema.comments": "Yorum sembollerini tanımlar.", + "schema.blockComments": "Blok açıklamalarının nasıl işaretlendiğini tanımlar.", + "schema.blockComment.begin": "Blok açıklamasını başlatan karakter dizisi.", + "schema.blockComment.end": "Blok açıklamasını bitiren karakter dizisi.", + "schema.lineComment": "Satır açıklamasını başlatan karakter dizisi.", + "schema.brackets": "Girintiyi artıran veya azaltan ayraç sembollerini tanımlar.", + "schema.autoClosingPairs": "Ayraç çiftlerini tanımlar. Bir açılış ayracı girildiğinde, kapanış ayracı otomatik olarak eklenir.", + "schema.autoClosingPairs.notIn": "Otomatik çiftlerin devre dışı bırakıldığı bir kapsamlar listesi tanımlar.", + "schema.surroundingPairs": "Seçili bir dizeyi çevrelemek için kullanılabilecek ayraç çiftlerini tanımlar.", + "schema.wordPattern": "Dilin kelime tanımı.", + "schema.wordPattern.pattern": "Sözcükleri eşleştirmek için kullanılacak Düzenli İfade.", + "schema.wordPattern.flags": "Sözcükleri eşleştirmek için kullanılacak Düzenli İfade işaretleri.", + "schema.wordPattern.flags.errorMessage": "`/^([gimuy]+)$/` kalıbı ile eşleşmelidir.", + "schema.indentationRules": "Dilin girintileme ayarları.", + "schema.indentationRules.increaseIndentPattern": "Bir satır bu kalıpla eşleşirse, ondan sonraki tüm satırlar (başka bir kuralla eşleşmedikçe) bir kez girintilenmelidir.", + "schema.indentationRules.increaseIndentPattern.pattern": "increaseIndentPattern için Düzenli İfade.", + "schema.indentationRules.increaseIndentPattern.flags": "increaseIndentPattern için Düzenli İfade işaretleri.", + "schema.indentationRules.increaseIndentPattern.errorMessage": "`/^([gimuy]+)$/` kalıbı ile eşleşmelidir.", + "schema.indentationRules.decreaseIndentPattern": "Bir satır bu kalıpla eşleşirse, ondan sonraki tüm satırların (başka bir kuralla eşleşmedikçe) girintisi bir kez azaltılmalıdır.", + "schema.indentationRules.decreaseIndentPattern.pattern": "decreaseIndentPattern için Düzenli İfade.", + "schema.indentationRules.decreaseIndentPattern.flags": "decreaseIndentPattern için Düzenli İfade işaretleri.", + "schema.indentationRules.decreaseIndentPattern.errorMessage": "`/^([gimuy]+)$/` kalıbı ile eşleşmelidir.", + "schema.indentationRules.indentNextLinePattern": "Bir satır bu kalıpla eşleşirse, ondan **sadece bir sonraki satır** bir kez girintilenmelidir.", + "schema.indentationRules.indentNextLinePattern.pattern": "indentNextLinePattern için Düzenli İfade.", + "schema.indentationRules.indentNextLinePattern.flags": "indentNextLinePattern için Düzenli İfade işaretleri.", + "schema.indentationRules.indentNextLinePattern.errorMessage": "`/^([gimuy]+)$/` kalıbı ile eşleşmelidir.", + "schema.indentationRules.unIndentedLinePattern": "Bir satır bu kalıpla eşleşirse, o satırın girintisi değiştirilmemelidir ve diğer kurallara karşı değerlendirilmemelidir.", + "schema.indentationRules.unIndentedLinePattern.pattern": "unIndentedLinePattern için Düzenli İfade.", + "schema.indentationRules.unIndentedLinePattern.flags": "unIndentedLinePattern için Düzenli İfade işaretleri.", + "schema.indentationRules.unIndentedLinePattern.errorMessage": "`/^([gimuy]+)$/` kalıbı ile eşleşmelidir." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json new file mode 100644 index 0000000000..5e72d724b8 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "inspectTMScopes": "Geliştirici: TM Kapsamlarını Denetle", + "inspectTMScopesWidget.loading": "Yükleniyor..." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json new file mode 100644 index 0000000000..7c62ad7e54 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleMinimap": "Görünüm: Mini Haritayı Aç/Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json new file mode 100644 index 0000000000..4c8b3ff19e --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleLocation": "Çoklu İmleç Değiştiricisini Aç/Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json new file mode 100644 index 0000000000..a99ce919c0 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderControlCharacters": "Görünüm: Kontrol Karakterlerini Aç/Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json new file mode 100644 index 0000000000..a9c25a26ec --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleRenderWhitespace": "Görünüm: Boşlukları Görüntülemeyi Aç/Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json new file mode 100644 index 0000000000..94e4cec05d --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggle.wordwrap": "Görünüm: Sözcük Kaydırmasını Aç/Kapat", + "wordWrap.notInDiffEditor": "Diff düzenleyicisinde sözcük kaydırma açılıp kapatılamıyor", + "unwrapMinified": "Bu dosya için sonraki satıra kaydırmayı devre dışı bırak", + "wrapMinified": "Bu dosya için sonraki satıra kaydırmayı etkinleştir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json new file mode 100644 index 0000000000..f0e04507b7 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wordWrapMigration.ok": "Tamam", + "wordWrapMigration.dontShowAgain": "Tekrar gösterme", + "wordWrapMigration.openSettings": "Ayarları Aç", + "wordWrapMigration.prompt": "`editor.wrappingColumn` ayarı, `editor.wordWrap` yüzünden kullanım dışıdır." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json new file mode 100644 index 0000000000..41d3ef9d70 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/breakpointWidget.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointWidgetExpressionPlaceholder": "İfade değerlendirmesi doğru olduğunda mola ver. Kabul etmek için 'Enter', iptal etmek için 'Esc' tuşuna basın.", + "breakpointWidgetAriaLabel": "Program, burada sadece bu koşul doğruysa durur. Kabul etmek için Enter veya iptal etmek için Escape tuşuna basın. ", + "breakpointWidgetHitCountPlaceholder": "İsabet sayısı koşulu sağlandığında mola ver. Kabul etmek için 'Enter', iptal etmek için 'Esc' tuşuna basın.", + "breakpointWidgetHitCountAriaLabel": "Program, burada sadece isabet sayısı koşulu sağlandığında durur. Kabul etmek için Enter veya iptal etmek için Escape tuşuna basın. ", + "expression": "İfade", + "hitCount": "İsabet Sayısı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json new file mode 100644 index 0000000000..942a5d1412 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/debugActionItems.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noConfigurations": "Yapılandırma Yok", + "addConfigTo": "Yapılandırma Ekle ({0})...", + "addConfiguration": "Yapılandırma Ekle..." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/debugActions.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/debugActions.i18n.json new file mode 100644 index 0000000000..95c3112870 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/debugActions.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openLaunchJson": "{0} Dosyasını Aç", + "launchJsonNeedsConfigurtion": "'launch.json'u Yapılandır veya Düzelt", + "noFolderDebugConfig": "Gelişmiş hata ayıklama yapılandırması yapmak için lütfen ilk olarak bir klasör açın.", + "startDebug": "Hata Ayıklamaya Başla", + "startWithoutDebugging": "Hata Ayıklama Olmadan Başlat", + "selectAndStartDebugging": "Seç ve Hata Ayıklamaya Başla", + "restartDebug": "Yeniden Başlat", + "reconnectDebug": "Yeniden Bağlan", + "stepOverDebug": "Adım At", + "stepIntoDebug": "İçine Adımla", + "stepOutDebug": "Dışına Adımla", + "stopDebug": "Durdur", + "disconnectDebug": "Bağlantıyı Kes", + "continueDebug": "Devam Et", + "pauseDebug": "Duraklat", + "restartFrame": "Çerçeveyi Yeniden Başlat", + "removeBreakpoint": "Kesme Noktasını Kaldır", + "removeAllBreakpoints": "Tüm Kesme Noktalarını Kaldır", + "enableBreakpoint": "Kesme Noktasını Etkinleştir", + "disableBreakpoint": "Kesme Noktasını Devre Dışı Bırak", + "enableAllBreakpoints": "Tüm Kesme Noktalarını Etkinleştir", + "disableAllBreakpoints": "Tüm Kesme Noktalarını Devre Dışı Bırak", + "activateBreakpoints": "Kesme Noktalarını Etkinleştir", + "deactivateBreakpoints": "Kesme Noktalarını Devre Dışı Bırak", + "reapplyAllBreakpoints": "Tüm Kesme Noktalarını Yeniden Uygula", + "addFunctionBreakpoint": "Fonksiyon Kesme Noktası Ekle", + "renameFunctionBreakpoint": "Fonksiyon Kesme Noktasını Kaldır", + "addConditionalBreakpoint": "Koşullu Kesme Noktası Ekle...", + "editConditionalBreakpoint": "Kesme Noktasını Düzenle...", + "setValue": "Değeri Ayarla", + "addWatchExpression": "İfade Ekle", + "editWatchExpression": "İfadeyi Düzenle", + "addToWatchExpressions": "İzlemeye Ekle", + "removeWatchExpression": "İfadeyi Kaldır", + "removeAllWatchExpressions": "Tüm İfadeleri Kaldır", + "clearRepl": "Konsolu Temizle", + "debugConsoleAction": "Hata Ayıklama Konsolu", + "unreadOutput": "Hata Ayıklama Konsolunda Yeni Çıktı", + "debugFocusConsole": "Hata Ayıklama Konsoluna Odakla", + "focusProcess": "İşleme Odakla", + "stepBackDebug": "Geri Adım At", + "reverseContinue": "Tersine Çevir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json new file mode 100644 index 0000000000..52824aa59f --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/debugActionsWidget.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugToolBarBackground": "Hata ayıklama araç çubuğu arka plan rengi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json new file mode 100644 index 0000000000..42fd9ef670 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/debugContentProvider.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unable": "Hata ayıklama oturumu olmadan kaynak çözümlenemiyor" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json new file mode 100644 index 0000000000..e294fd8893 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/debugEditorActions.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleBreakpointAction": "Hata Ayıklama: Kesme Noktası Ekle/Kaldır", + "columnBreakpointAction": "Hata Ayıklama: Sütun Kesme Noktası", + "columnBreakpoint": "Sütun Kesme Noktası Ekle", + "conditionalBreakpointEditorAction": "Hata Ayıklama: Koşullu Kesme Noktası Ekle...", + "runToCursor": "İmlece Kadar Çalıştır", + "debugEvaluate": "Hata Ayıklama: Değerlendir", + "debugAddToWatch": "Hata Ayıklama: İzlemeye Ekle", + "showDebugHover": "Hata Ayıklama: Bağlantı Vurgusunu Göster" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json new file mode 100644 index 0000000000..c047d85329 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/debugEditorModelManager.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "breakpointDisabledHover": "Devre Dışı Bırakılmış Kesme Noktası", + "breakpointUnverifieddHover": "Doğrulanmamış Kesme Noktası", + "breakpointDirtydHover": "Doğrulanmamış Kesme Noktası Dosya düzenlendi, lütfen hata ayıklama oturumunu yeniden başlatın.", + "breakpointUnsupported": "Koşullu kesme noktaları bu hata ayıklama türü tarafından desteklenmiyor" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json new file mode 100644 index 0000000000..3e7b6eb0d3 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, hata ayıklama", + "debugAriaLabel": "Çalıştırılacak bir başlatma yapılandırması adı girin.", + "noConfigurationsMatching": "Eşleyen hata ayıklama yapılandırması yok", + "noConfigurationsFound": "Hiçbir hata ayıklama yapılandırması bulunamadı. Lütfen bir 'launch.json' dosyası oluşturun." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json new file mode 100644 index 0000000000..41cb393a5c --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/exceptionWidget.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugExceptionWidgetBorder": "İstisna aracı kenarlık rengi.", + "debugExceptionWidgetBackground": "İstisna aracı arka plan rengi.", + "exceptionThrownWithId": "İstisna oluştu: {0}", + "exceptionThrown": "İstisna oluştu." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json new file mode 100644 index 0000000000..896d4cfee1 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/linkDetector.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileLinkMac": "Takip etmek için tıklayın (Cmd + tıklama yana açar)", + "fileLink": "Takip etmek için tıklayın (Ctrl + tıklama yana açar)" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/common/debug.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/common/debug.i18n.json new file mode 100644 index 0000000000..11a3a89e68 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/common/debug.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "internalConsoleOptions": "Dahili hata ayıklama konsolunun davranışlarını denetler." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/common/debugModel.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/common/debugModel.i18n.json new file mode 100644 index 0000000000..af49f10483 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/common/debugModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "notAvailable": "mevcut değil", + "startDebugFirst": "Lütfen değerlendirilecek bir hata ayıklama oturumu başlatın" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/common/debugSource.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/common/debugSource.i18n.json new file mode 100644 index 0000000000..b90b1b915d --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/common/debugSource.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unknownSource": "Bilinmeyen Kaynak" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json new file mode 100644 index 0000000000..4d986e3196 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debug.contribution.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleDebugViewlet": "Hata Ayıklamayı Göster", + "toggleDebugPanel": "Hata Ayıklama Konsolu", + "debug": "Hata Ayıklama", + "debugPanel": "Hata Ayıklama Konsolu", + "variables": "Değişkenler", + "watch": "İzle", + "callStack": "Çağrı Yığını", + "breakpoints": "Kesme Noktaları", + "view": "Görüntüle", + "debugCategory": "Hata Ayıklama", + "debugCommands": "Hata Ayıklama Yapılandırması", + "debugConfigurationTitle": "Hata Ayıklama", + "allowBreakpointsEverywhere": "Herhangi bir dosyada kesme noktası ayarlamaya izin verir", + "openExplorerOnEnd": "Bir hata ayıklama oturumunun sonunda otomatik olarak gezgin görünümünü açın", + "inlineValues": "Hata ayıklama sırasında değişken değerlerini düzenleyicide satır içinde göster", + "hideActionBar": "Dolaştırılabilir hata ayıklama eylem çubuğunun gizlenip gizlenmeyeceğini denetler", + "launch": "Global hata ayıklama başlatma yapılandırması. Çalışma alanlarında paylaşılan 'launch.json'a alternatif olarak kullanılmalıdır" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json new file mode 100644 index 0000000000..cc4e859f19 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noFolderDebugConfig": "Gelişmiş hata ayıklama yapılandırması yapmak için lütfen ilk olarak bir klasör açın." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json new file mode 100644 index 0000000000..3ba691cc2d --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.debuggers": "Hata ayıklama bağdaştırıcılarına ekleme yapar.", + "vscode.extension.contributes.debuggers.type": "Bu hata ayıklama bağdaştırıcısnın benzersiz tanımlayıcısı.", + "vscode.extension.contributes.debuggers.label": "Bu hata ayıklama bağdaştırıcısnın görünen adı.", + "vscode.extension.contributes.debuggers.program": "Hata ayıklama bağdaştırıcı programının yolu. Yol, ya mutlak ya da eklenti klasörüne görelidir.", + "vscode.extension.contributes.debuggers.args": "Bağdaştırıcıya iletilecek ek argümanlar.", + "vscode.extension.contributes.debuggers.runtime": "Program özniteliğinin yürütülebilir olmadığı halde bir çalışma zamanını gerektirmesi durumu için isteğe bağlı çalışma zamanı.", + "vscode.extension.contributes.debuggers.runtimeArgs": "İsteğe bağlı çalışma zamanı argümanları.", + "vscode.extension.contributes.debuggers.variables": "`launch.json` dosyasındaki interaktif değişkenlerin (ör. ${action.pickProcess}) bir komuta eşlenmesi.", + "vscode.extension.contributes.debuggers.initialConfigurations": "İlk 'launch.json' dosyasının üretimi için yapılandırmalar.", + "vscode.extension.contributes.debuggers.languages": "Hata ayıklama eklentisinin, \"varsayılan hata ayıklayıcı\" olarak değerlendirilebileceği diller listesi.", + "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Belirtilirse; VS Code, hata ayıklama bağdaştırıcısı yürütülebilir dosyasının yolunu ve ona gönderilecek argümanları belirlemek için bu komutu çağırır.", + "vscode.extension.contributes.debuggers.startSessionCommand": "Belirtilirse; VS Code, bu eklenti için hedeflenen \"hata ayıklama\" ve \"çalıştır\" eylemleri için bu komutu çağırır.", + "vscode.extension.contributes.debuggers.configurationSnippets": "'launch.json' dosyasına yeni yapılandırmalar ekleme parçacıkları.", + "vscode.extension.contributes.debuggers.configurationAttributes": "'launch.json' dosyasını doğrulayacak JSON şema yapılandırmaları.", + "vscode.extension.contributes.debuggers.windows": "Windows'a özel ayarlar.", + "vscode.extension.contributes.debuggers.windows.runtime": "Windows'da kullanılacak çalışma zamanı.", + "vscode.extension.contributes.debuggers.osx": "OS X'e özel ayarlar.", + "vscode.extension.contributes.debuggers.osx.runtime": "OS X'de kullanılacak çalışma zamanı.", + "vscode.extension.contributes.debuggers.linux": "Linux'a özel ayarlar.", + "vscode.extension.contributes.debuggers.linux.runtime": "Linux'da kullanılacak çalışma zamanı.", + "vscode.extension.contributes.breakpoints": "Kesme noktalarına ekleme yapar.", + "vscode.extension.contributes.breakpoints.language": "Bu dil için kesme noktalarını etkinleştir.", + "app.launch.json.title": "Başlat", + "app.launch.json.version": "Bu dosya biçiminin sürümü.", + "app.launch.json.configurations": "Yapılandırma listesi. IntelliSense kullanarak yeni yapılandırmalar ekleyin veya mevcut olanları düzenleyin.", + "app.launch.json.compounds": "Bileşikler listesi. Her bileşik, birlikte çalıştırılacak birden çok yapılandırmaya başvurur.", + "app.launch.json.compound.name": "Bileşiğin adı. Başlatma yapılandırması açılır kutu menüsünde görünür.", + "app.launch.json.compounds.configurations": "Bu bileşiğin parçası olarak başlatılacak yapılandırmaların adları.", + "debugNoType": "Hata ayıklama bağdaştırıcısının 'type' ögesi atlanabilir veya 'dize' türünde olmalıdır.", + "selectDebug": "Ortam Seçin", + "DebugConfig.failed": " '.vscode' klasörü içinde 'launch.json' dosyası oluşturulamıyor ({0})." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json new file mode 100644 index 0000000000..b267c2e856 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeBreakpoints": "Kesme Noktalarını Kaldır", + "removeBreakpointOnColumn": "{0}. Sütundaki Kesme Noktasını Kaldır", + "removeLineBreakpoint": "Satır Kesme Noktasını Kaldır", + "editBreakpoints": "Kesme Noktalarını Düzenle", + "editBreakpointOnColumn": "{0}. Sütundaki Kesme Noktasını Düzenle", + "editLineBrekapoint": "Satır Kesme Noktasını Düzenle", + "enableDisableBreakpoints": "Kesme Noktalarını Etkinleştir/Devre Dışı Bırak", + "disableColumnBreakpoint": "{0}. Sütundaki Kesme Noktasını Devre Dışı Bırak", + "disableBreakpointOnLine": "Satır Kesme Noktasını Devre Dışı Bırak", + "enableBreakpoints": "{0}. Sütundaki Kesme Noktasını Etkinleştir", + "enableBreakpointOnLine": "Satır Kesme Noktasını Etkinleştir", + "addBreakpoint": "Kesme Noktası Ekle", + "addConfiguration": "Yapılandırma Ekle..." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json new file mode 100644 index 0000000000..9a9f233dbf --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugHover.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeAriaLabel": "Hata Ayıklama Bağlantı Vurgusu" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json new file mode 100644 index 0000000000..f134f3bea9 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snapshotObj": "Bu nesne için sadece ilkel türler gösterilir.", + "debuggingPaused": "Hata ayıklama duraklatıldı, sebep {0}, {1} {2}", + "debuggingStarted": "Hata ayıklama başlatıldı.", + "debuggingStopped": "Hata ayıklama durduruldu.", + "breakpointAdded": "Kesme noktası eklendi, {0}. satır, {1} dosyası", + "breakpointRemoved": "Kesme noktası kaldırıldı, {0}. satır, {1} dosyası", + "compoundMustHaveConfigurations": "Bileşik, birden çok yapılandırmayı başlatmak için \"configurations\" özniteliği bulundurmalıdır.", + "configMissing": "'launch.json' dosyasında '{0}' yapılandırması eksik.", + "debugTypeNotSupported": "Yapılandırılan hata ayıklama türü '{0}', desteklenmiyor.", + "debugTypeMissing": "Seçilen başlatma yapılandırması için 'type' özelliği eksik.", + "preLaunchTaskErrors": "'{0}' ön başlatma görevi sırasında derleme hataları algılandı.", + "preLaunchTaskError": "'{0}' ön başlatma görevi sırasında derleme hatası algılandı.", + "preLaunchTaskExitCode": "'{0}' ön başlatma görevi {1} çıkış koduyla sonlandı.", + "debugAnyway": "Yine de Hata Ayıkla", + "noFolderWorkspaceDebugError": "Aktif dosyada hata ayıklama yapılamıyor. Lütfen, dosyanın diskte kayıtlı olduğundan ve bu dosya türü için hata ayıklama eklentinizin olduğundan emin olun.", + "NewLaunchConfig": "Lütfen uygulamanızın başlatma yapılandırması dosyasını ayarlayın. {0}", + "DebugTaskNotFound": "'{0}' ön başlatma görevi bulunamadı." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json new file mode 100644 index 0000000000..b4a91c8e73 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugViewer.i18n.json @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "process": "İşlem", + "paused": "Duraklatıldı", + "running": "Çalışıyor", + "thread": "İş Parçacığı", + "pausedOn": "{0} Üzerinde Duraklatıldı", + "loadMoreStackFrames": "Daha Fazla Yığın Çerçevesi Yükleyin", + "threadAriaLabel": "{0} iş parçacığı, çağrı yığını, hata ayıklama", + "stackFrameAriaLabel": "Yığın Çerçevesi {0} satır {1} {2}, çağrı yığını, hata ayıklama", + "variableValueAriaLabel": "Yeni değişken adını girin", + "variableScopeAriaLabel": "{0} kapsamı, değişkenler, hata ayıklama", + "variableAriaLabel": "{0} değeri {1}, değişkenler, hata ayıklama", + "watchExpressionPlaceholder": "İzlenecek ifade", + "watchExpressionInputAriaLabel": "İzleme ifadesi girin", + "watchExpressionAriaLabel": "{0} değeri {1}, izleme, hata ayıklama", + "watchVariableAriaLabel": "{0} değeri {1}, izleme, hata ayıklama", + "functionBreakpointPlaceholder": "Mola verilecek fonksiyon", + "functionBreakPointInputAriaLabel": "Fonksiyon kesme noktasını girin", + "functionBreakpointsNotSupported": "Fonksiyon kesme noktaları bu hata ayıklama türü tarafından desteklenmiyor", + "breakpointAriaLabel": "Kesme noktası satır {0} {1}, kesme noktaları, hata ayıklama", + "functionBreakpointAriaLabel": "Fonksiyon kesme noktası {0}, kesme noktaları, hata ayıklama", + "exceptionBreakpointAriaLabel": "İstisna kesme noktası {0}, kesme noktaları, hata ayıklama" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json new file mode 100644 index 0000000000..4aefa0ee79 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugViews.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "variablesSection": "Değişkenler Bölümü", + "variablesAriaTreeLabel": "Hata Ayıklama Değişkenleri", + "expressionsSection": "İfadeler Bölümü", + "watchAriaTreeLabel": "Hata Ayıklama İzleme İfadeleri", + "callstackSection": "Çağrı Yığını Bölümü", + "debugStopped": "{0} Üzerinde Duraklatıldı", + "callStackAriaLabel": "Hata Ayıklama Çağrı Yığını", + "breakpointsSection": "Kesme Noktaları Bölümü", + "breakpointsAriaTreeLabel": "Hata Ayıklama Kesme Noktaları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json new file mode 100644 index 0000000000..ac7df2af3d --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyValue": "Değeri Kopyala", + "copy": "Kopyala", + "copyAll": "Tümünü Kopyala", + "copyStackTrace": "Çağrı Yığınını Kopyala" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json new file mode 100644 index 0000000000..e3bcc8bfed --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreInfo": "Daha Fazla Bilgi", + "unableToLaunchDebugAdapter": "'{0}' tarafından hata ayıklama bağdaştırıcısı başlatılamadı.", + "unableToLaunchDebugAdapterNoArgs": "Hata ayıklama bağdaştırıcısı başlatılamıyor.", + "stoppingDebugAdapter": "{0}. Hata ayıklama bağdaştırıcısı durduruluyor.", + "debugAdapterCrash": "Hata ayıklama bağdaştırıcısı beklenmeyen biçimde sonlandırıldı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json new file mode 100644 index 0000000000..aea33c8bf3 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/repl.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "replAriaLabel": "Oku Değerlendir Yaz Döngüsü Paneli", + "actions.repl.historyPrevious": "Önceki Geçmiş", + "actions.repl.historyNext": "Sonraki Geçmiş", + "actions.repl.acceptInput": "REPL Girdiyi Kabul Et", + "actions.repl.copyAll": "Hata Ayıklama: Konsol Tümünü Kopyala" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json new file mode 100644 index 0000000000..b85a4d9bc0 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "stateCapture": "Nesne durumu ilk değerlendirmeden alındı", + "replVariableAriaLabel": "{0} değişkeni, {1} değerine sahip, oku değerlendir yaz döngüsü, hata ayıklama", + "replExpressionAriaLabel": "{0} ifadesi, {1} değerine sahip, oku değerlendir yaz döngüsü, hata ayıklama", + "replValueOutputAriaLabel": "{0}, oku değerlendir yaz döngüsü, hata ayıklama", + "replKeyValueOutputAriaLabel": "{0} çıktı değişkeni, {1} değerine sahip, oku değerlendir yaz döngüsü, hata ayıklama" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json new file mode 100644 index 0000000000..2bc1ff3bfc --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "statusBarDebuggingBackground": "Bir programda hata ayıklama yapılırken durum çubuğu arka plan rengi. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarDebuggingForeground": "Bir programda hata ayıklama yapılırken durum çubuğu ön plan rengi. Durum çubuğu, pencerenin alt kısmında gösterilir.", + "statusBarDebuggingBorder": "Bir programda hata ayıklama yapılırken, durum çubuğunu kenar çubuğundan ve düzenleyiciden ayıran kenarlık rengi. Durum çubuğu, pencerenin alt kısmında gösterilir." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json new file mode 100644 index 0000000000..5b33d20080 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/terminalSupport.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debug.terminal.title": "hata ayıklanan", + "debug.terminal.not.available.error": "Entegre terminal mevcut değil" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json new file mode 100644 index 0000000000..57fa28db10 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/node/debugAdapter.i18n.json @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "debugAdapterBinNotFound": "Hata ayıklama bağdaştırıcısı yürütülebilir dosyası '{0}', mevcut değil.", + "debugAdapterCannotDetermineExecutable": "Hata ayıklama bağdaştırıcısı yürütülebilir dosyası '{0}' belirlenemedi.", + "launch.config.comment1": "Olası öznitelikler hakkında bilgi edinmek için IntelliSense kullanın.", + "launch.config.comment2": "Mevcut özniteliklerin açıklamalarını görmek için fare ile üzerine gelin.", + "launch.config.comment3": "Daha fazla bilgi için ziyaret edin: {0}", + "debugType": "Yapılandırma türü.", + "debugTypeNotRecognised": "Hata ayıklama türü tanınmıyor. Karşılık gelen hata ayıklama uzantısı yüklemiş olduğunuzdan ve etkinleştirildiğinden emin olun.", + "node2NotSupported": "\"node2\" artık desteklenmiyor, bunun yerine \"node\" kullanın ve \"protocol\" özniteliğini \"inspector\" olarak ayarlayın.", + "debugName": "Yapılandırmanın adı; başlatma yapılandırması açılır kutu menüsünde görünür.", + "debugRequest": "Yapılandırmanın istek türü. \"launch\" veya \"attach\" olabilir.", + "debugServer": "Sadece eklenti geliştirme hata ayıklaması için: eğer port belirtildiyse; Vs Code, bir hata ayıklama bağdaştırıcısına sunucu modunda bağlanmayı dener", + "debugPrelaunchTask": "Hata ayıklama oturumu başlamadan önce çalıştırılacak görev.", + "debugWindowsConfiguration": "Windows'a özel başlangıç yapılandırması öznitelikleri.", + "debugOSXConfiguration": "OS X'e özel başlangıç yapılandırması öznitelikleri.", + "debugLinuxConfiguration": "Linux'a özel başlangıç yapılandırması öznitelikleri.", + "deprecatedVariables": "'env.', 'config.' ve 'command.' kullanım dışıdır, bunların yerine 'env:', 'config:' ve 'command:' kulanın." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json new file mode 100644 index 0000000000..60328cb0ff --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showEmmetCommands": "Emmet Komutlarını Göster" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json new file mode 100644 index 0000000000..d1ca8078da --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/balance.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "balanceInward": "Emmet: Dengele (içe)", + "balanceOutward": "Emmet: Dengele (dışa)" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json new file mode 100644 index 0000000000..9c844c8a06 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/editPoints.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "previousEditPoint": "Emmet: Önceki Düzenleme Noktasına Git", + "nextEditPoint": "Emmet: Sonraki Düzenleme Noktasına Git" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json new file mode 100644 index 0000000000..0fe82e8194 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/evaluateMath.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "evaluateMathExpression": "Emmet: Matematik İfadesini Değerlendir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json new file mode 100644 index 0000000000..cce7a8aa62 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "expandAbbreviationAction": "Emmet: Kısaltmayı Genişlet" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json new file mode 100644 index 0000000000..3492476a8b --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/incrementDecrement.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "incrementNumberByOneTenth": "Emmet: 0.1 Arttır", + "incrementNumberByOne": "Emmet: 1 Arttır", + "incrementNumberByTen": "Emmet: 10 Arttır", + "decrementNumberByOneTenth": "Emmet: 0.1 Azalt", + "decrementNumberByOne": "Emmet: 1 Azalt", + "decrementNumberByTen": "Emmet: 10 Azalt" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json new file mode 100644 index 0000000000..f7a03e93a4 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/matchingPair.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "matchingPair": "Emmet: Eşleşen Çifte Git" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json new file mode 100644 index 0000000000..708c6a7632 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/mergeLines.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "mergeLines": "Emmet: Satırları Birleştir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json new file mode 100644 index 0000000000..59b548bac7 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/reflectCssValue.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reflectCSSValue": "Emmet: CSS Değerini Yansıt" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json new file mode 100644 index 0000000000..0f422adf0d --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/removeTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "removeTag": "Emmet: Etiketi Sil" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json new file mode 100644 index 0000000000..28cd133fa3 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/selectItem.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectPreviousItem": "Emmet: Önceki Ögeyi Seç", + "selectNextItem": "Emmet: Sonraki Ögeyi Seç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json new file mode 100644 index 0000000000..68917b0c88 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/splitJoinTag.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "splitJoinTag": "Emmet: Etiketi Böl/Birleştir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json new file mode 100644 index 0000000000..9d7317ea2e --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/toggleComment.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleComment": "Emmet: Yorumu Aç/Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json new file mode 100644 index 0000000000..7825a46984 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/updateImageSize.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateImageSize": "Emmet: Görüntü Boyutunu Güncelle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json new file mode 100644 index 0000000000..2041fe7076 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/updateTag.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateTag": "Emmet: Etiketi Güncelle", + "enterTag": "Etiketi Gir", + "tag": "Etiket" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json new file mode 100644 index 0000000000..3285e73769 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/actions/wrapWithAbbreviation.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "wrapWithAbbreviationAction": "Emmet: Kısaltma ile Sarmala", + "enterAbbreviation": "Kısaltmayı Gir", + "abbreviation": "Kısaltma" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json new file mode 100644 index 0000000000..ab4e1678e1 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emmetConfigurationTitle": "Emmet", + "triggerExpansionOnTab": "Etkinleştirildiğinde, emmet kısaltmaları TAB tuşuna basıldığında genişletilir. emmet.useNewemmet, 'true' olarak ayarlandığında geçerli değildir.", + "emmetPreferences": "Emmet'in bazı eylemleri ve çözümleyicilerinin davranışını değiştirmek için kullanılacak tercihler. emmet.useNewemmet, 'true' olarak ayarlandığında geçerli değildir.", + "emmetSyntaxProfiles": "Belirtilen sentaks için profil tanımlayın veya kendi profilinizi belirli kurallarla kullanın.", + "emmetExclude": "Emmet kısaltmalarının genişletilmeyeceği bir diller dizisi.", + "emmetExtensionsPath": "Emmet profileri, parçacıkları ve tercihlerini içeren bir klasör yolu. emmet.useNewEmmet, 'true' olarak ayarlandığında, yalnızca profiller eklentiler yolundan kabul edilir.", + "useNewEmmet": "Tüm emmet özellikleri için yeni emmet modüllerini deneyin(sonunda eski tekil emmet kütüphanesinin yerini alacaktır)." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json new file mode 100644 index 0000000000..f0c84d12f6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/execution/electron-browser/execution.contribution.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Harici Terminal", + "explorer.openInTerminalKind": "Başlatılacak terminal türünü özelleştirir.", + "terminal.external.windowsExec": "Windows'da hangi terminalin çalışacağını ayarlar.", + "terminal.external.osxExec": "OS X'de hangi terminalin çalışacağını ayarlar.", + "terminal.external.linuxExec": "Linux'da hangi terminalin çalışacağını ayarlar.", + "globalConsoleActionWin": "Yeni Komut İstemi Aç", + "globalConsoleActionMacLinux": "Yeni Terminal Aç", + "scopedConsoleActionWin": "Komut İsteminde Aç", + "scopedConsoleActionMacLinux": "Terminalde Aç", + "openFolderInIntegratedTerminal": "Terminalde Aç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..2403e6245c --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/execution/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalConfigurationTitle": "Harici Terminal", + "terminal.external.windowsExec": "Windows'da hangi terminalin çalışacağını ayarlar.", + "terminal.external.osxExec": "OS X'de hangi terminalin çalışacağını ayarlar.", + "terminal.external.linuxExec": "Linux'da hangi terminalin çalışacağını ayarlar.", + "globalConsoleActionWin": "Yeni Komut İstemi Aç", + "globalConsoleActionMacLinux": "Yeni Terminal Aç", + "scopedConsoleActionWin": "Komut İsteminde Aç", + "scopedConsoleActionMacLinux": "Terminalde Aç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json b/i18n/trk/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..33fa0a21c2 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/execution/electron-browser/terminalService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "console.title": "VS Code Konsolu", + "mac.terminal.script.failed": "'{0}' betiği, {1} çıkış koduyla başarısız oldu", + "mac.terminal.type.not.supported": "'{0}' desteklenmiyor", + "press.any.key": "Devam etmek için bir tuşa basın ...", + "linux.term.failed": "'{0}', {1} çıkış koduyla başarısız oldu" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json new file mode 100644 index 0000000000..d3730da7c2 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorer.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.view": "Özel görünüme ekleme yapar", + "vscode.extension.contributes.view.id": "vscode.workspace.createTreeView aracılığıyla oluşturulan görünümü tanımlamak için kullanılan benzersiz kimlik", + "vscode.extension.contributes.view.label": "Görünümde gösterilecek insanlar tarafından okunabilir dize", + "vscode.extension.contributes.view.icon": "Görünüm simgesinin yolu", + "vscode.extension.contributes.views": "Özel görünümlere ekleme yapar", + "showViewlet": "{0}'i Göster", + "view": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json b/i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json new file mode 100644 index 0000000000..1434700966 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorerActions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "refresh": "Yenile" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json b/i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json new file mode 100644 index 0000000000..e3e0cff803 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/explorers/browser/treeExplorerService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorer.noMatchingProviderId": "{providerId} kimliği ile kayıtlı \"TreeExplorerNodeProvider\" yok." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json b/i18n/trk/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json new file mode 100644 index 0000000000..894cd149d2 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "treeExplorerViewlet.tree": "Ağaç Gezgini Bölümü" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json new file mode 100644 index 0000000000..5d577cf72f --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/browser/dependenciesViewer.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error": "Hata", + "Unknown Dependency": "Bilinmeyen Bağımlılık:" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json new file mode 100644 index 0000000000..580834218e --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "name": "Eklenti adı", + "extension id": "Eklenti tanımlayıcısı", + "publisher": "Yayıncı adı", + "install count": "Yüklenme sayısı", + "rating": "Derecelendirme", + "license": "Lisans", + "details": "Detaylar", + "contributions": "Eklemeler", + "changelog": "Değişim Günlüğü", + "dependencies": "Bağımlılıklar", + "noReadme": "README dosyası yok.", + "noChangelog": "Değişim günlüğü yok.", + "noContributions": "Hiçbir Ekleme Yapmıyor", + "noDependencies": "Bağımlılık Yok", + "settings": "Ayarlar ({0})", + "setting name": "Adı", + "description": "Açıklama", + "default": "Varsayılan", + "debuggers": "Hata Ayıklayıcılar ({0})", + "debugger name": "Adı", + "debugger type": "Tür", + "views": "Görünümler ({0})", + "view id": "ID", + "view name": "Adı", + "view location": "Yeri", + "themes": "Temalar ({0})", + "JSON Validation": "JSON Doğrulama ({0})", + "commands": "Komutlar ({0})", + "command name": "Adı", + "keyboard shortcuts": "Klavye Kısayolları", + "menuContexts": "Menü Bağlamları", + "languages": "Diller ({0})", + "language id": "ID", + "language name": "Adı", + "file extensions": "Dosya Uzantıları", + "grammar": "Gramer", + "snippets": "Parçacıklar" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..534ab7f054 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAction": "Yükle", + "installing": "Yükleniyor", + "uninstallAction": "Kaldır", + "Uninstalling": "Kaldırılıyor", + "updateAction": "Güncelle", + "updateTo": "{0} sürümüne güncelle", + "enableForWorkspaceAction.label": "Etkinleştir (Çalışma Alanı)", + "enableAlwaysAction.label": "Etkinleştir (Daima)", + "disableForWorkspaceAction.label": "Devre Dışı Bırak (Çalışma Alanı)", + "disableAlwaysAction.label": "Devre Dışı Bırak (Daima)", + "ManageExtensionAction.uninstallingTooltip": "Kaldırılıyor", + "enableForWorkspaceAction": "Çalışma Alanı", + "enableGloballyAction": "Daima", + "enableAction": "Etkinleştir", + "disableForWorkspaceAction": "Çalışma Alanı", + "disableGloballyAction": "Daima", + "disableAction": "Devre Dışı Bırak", + "checkForUpdates": "Güncelleştirmeleri Denetle", + "enableAutoUpdate": "Eklentileri Otomatik Olarak Güncelleştirmeyi Etkinleştir", + "disableAutoUpdate": "Eklentileri Otomatik Olarak Güncelleştirmeyi Devre Dışı Bırak", + "updateAll": "Tüm Eklentileri Güncelle", + "reloadAction": "Yeniden Yükle", + "postUpdateTooltip": "Güncellemek için yeniden yükleyin", + "postUpdateMessage": "Güncellenen '{0}' eklentisini etkinleştirmek için bu pencere yeniden yüklensin mi?", + "postEnableTooltip": "Etkinleştirmek için yeniden yükleyin", + "postEnableMessage": "'{0}' eklentisini etkinleştirmek için bu pencere yeniden yüklensin mi?", + "postDisableTooltip": "Devre dışı bırakmak için yeniden yükleyin", + "postDisableMessage": "'{0}' eklentisini devre dışı bırakmak için bu pencere yeniden yüklensin mi?", + "postUninstallTooltip": "Devre dışı bırakmak için yeniden yükleyin", + "postUninstallMessage": "Kaldırılan '{0}' eklentisini devre dışı bırakmak için bu pencere yeniden yüklensin mi?", + "reload": "Pencereyi &&Yeniden Yükle", + "toggleExtensionsViewlet": "Eklentileri Göster", + "installExtensions": "Eklenti Yükle", + "showEnabledExtensions": "Etkinleştirilmiş Eklentileri Göster", + "showInstalledExtensions": "Yüklenen Eklentileri Göster", + "showDisabledExtensions": "Devre Dışı Bırakılan Eklentileri Göster", + "clearExtensionsInput": "Eklenti Girdisini Temizle", + "showOutdatedExtensions": "Eski Eklentileri Göster", + "showPopularExtensions": "Popüler Eklentileri Göster", + "showRecommendedExtensions": "Tavsiye Edilen Eklentileri Göster", + "showWorkspaceRecommendedExtensions": "Çalışma Alanının Tavsiye Ettiği Eklentileri Göster", + "showRecommendedKeymapExtensions": "Tavsiye Edilen Tuş Haritalarını Göster", + "showRecommendedKeymapExtensionsShort": "Tuş Haritaları", + "showLanguageExtensions": "Dil Eklentilerini Göster", + "showLanguageExtensionsShort": "Dil Eklentileri", + "showAzureExtensions": "Azure Eklentilerini Göster", + "showAzureExtensionsShort": "Azure Eklentileri", + "configureWorkspaceRecommendedExtensions": "Tavsiye Edilen Eklentileri Yapılandır (Çalışma Alanı)", + "ConfigureWorkspaceRecommendations.noWorkspace": "Tavsiyeler, sadece çalışma alanı klasöründe mevcuttur.", + "OpenExtensionsFile.failed": " '.vscode' klasörü içinde 'extensions.json' dosyası oluşturulamıyor ({0}).", + "builtin": "Yerleşik", + "disableAll": "Yüklü Tüm Eklentileri Devre Dışı Bırak", + "disableAllWorkspace": "Bu Çalışma Alanı için Yüklü Tüm Eklentileri Devre Dışı Bırak", + "enableAll": "Yüklü Tüm Eklentileri Etkinleştir", + "enableAllWorkspace": "Bu Çalışma Alanı için Yüklü Tüm Eklentileri Etkinleştir", + "extensionButtonProminentBackground": "Dikkat çeken eklenti eylemleri için buton arka plan rengi (ör. yükle butonu)", + "extensionButtonProminentForeground": "Dikkat çeken eklenti eylemleri için buton ön plan rengi (ör. yükle butonu)", + "extensionButtonProminentHoverBackground": "Dikkat çeken eklenti eylemleri için buton bağlantı vurgusu arka plan rengi (ör. yükle butonu)" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json new file mode 100644 index 0000000000..afe7bc75e2 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "manage": "Eklentilerinizi yönetmek için Enter'a basın.", + "searchFor": "Markette '{0}' için arama yapmak için Enter'a basın.", + "noExtensionsToInstall": "Bir eklenti adı girin" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json new file mode 100644 index 0000000000..7dbd11060c --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "app.extensions.json.title": "Eklentiler", + "app.extensions.json.recommendations": "Eklenti tavsiyelerinin listesi. Bir eklentinin tanımlayıcısı daima '${yayinci}.${ad}' şeklindedir. Örnek: 'vscode.csharp'.", + "app.extension.identifier.errorMessage": "'${yayinci}.${ad}' biçimi bekleniyor. Örnek: 'vscode.csharp'." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json new file mode 100644 index 0000000000..c7643614bc --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/common/extensionsInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsInputName": "Eklenti: {0}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json new file mode 100644 index 0000000000..8491748bfb --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "reallyRecommended2": "'{0}' eklentisi bu dosya türü için tavsiye edilir.", + "showRecommendations": "Tavsiyeleri Göster", + "neverShowAgain": "Tekrar gösterme", + "close": "Kapat", + "workspaceRecommended": "Bu çalışma alanı bazı eklentileri tavsiye ediyor.", + "ignoreExtensionRecommendations": "Tüm eklenti tavsiyelerini yok saymak istiyor musunuz?", + "ignoreAll": "Evet, Tümünü Yok Say", + "no": "Hayır", + "cancel": "İptal" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json new file mode 100644 index 0000000000..be3289f0d9 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionsCommands": "Eklentileri Yönet", + "galleryExtensionsCommands": "Galeri Eklentileri Yükle", + "extension": "Eklenti", + "extensions": "Eklentiler", + "view": "Görüntüle", + "extensionsConfigurationTitle": "Eklentiler", + "extensionsAutoUpdate": "Eklentileri otomatik olarak güncelle", + "extensionsIgnoreRecommendations": "Eklenti tavsiyelerini yok say" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json new file mode 100644 index 0000000000..c642a14729 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openExtensionsFolder": "Eklentiler Klasörünü Aç", + "installVSIX": "VSIX'ten yükle...", + "InstallVSIXAction.success": "Eklenti başarıyla yüklendi. Etkinleştirmek için yeniden başlatın.", + "InstallVSIXAction.reloadNow": "Şimdi Yeniden Yükle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json new file mode 100644 index 0000000000..d904852d77 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "disableOtherKeymapsConfirmation": "Tuş bağlamlarında çakışmalardan kaçınmak için diğer tuş haritaları ({0}) devre dışı bırakılsın mı?", + "yes": "Evet", + "no": "Hayır", + "betterMergeDisabled": "\"Better Merge\" artık yerleşik bir eklenti, yüklenen eklendi devre dışı bırakıldı ve kaldırılabilir.", + "uninstall": "Kaldır", + "later": "Daha Sonra" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json new file mode 100644 index 0000000000..001cc14e08 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "marketPlace": "Market", + "installedExtensions": "Yüklü", + "searchInstalledExtensions": "Yüklü", + "recommendedExtensions": "Tavsiye Edilen", + "searchExtensions": "Markette Eklenti Ara", + "sort by installs": "Sırala: Yüklenme Sayısına Göre", + "sort by rating": "Sırala: Derecelendirmeye Göre", + "sort by name": "Sırala: Ada Göre", + "suggestProxyError": "Market, 'ECONNREFUSED' döndürdü. Lütfen 'http.proxy' ayarını kontrol edin.", + "extensions": "Eklentiler", + "outdatedExtensions": "{0} Eski Eklenti" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json new file mode 100644 index 0000000000..ec6253ae9b --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensions": "Eklentiler", + "no extensions found": "Eklenti yok.", + "suggestProxyError": "Market, 'ECONNREFUSED' döndürdü. Lütfen 'http.proxy' ayarını kontrol edin." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json new file mode 100644 index 0000000000..755ec1107a --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "enableDependeciesConfirmation": "'{0}' eklentisini etkinleştirdiğinizde onun bağımlılıkları da etkinleştirilir. Devam etmek istiyor musunuz?", + "enable": "Evet", + "doNotEnable": "Hayır", + "disableDependeciesConfirmation": "Yalnızca '{0}' eklentisini mi devre dışı bırakmak istersiniz yoksa bağımlılıklarını da devre dışı bırakmak ister misiniz?", + "disableOnly": "Sadece Eklenti", + "disableAll": "Tümü", + "cancel": "İptal", + "singleDependentError": "'{0}' eklentisi devre dışı bırakılamıyor. '{1}' eklentisi buna bağlı.", + "twoDependentsError": "'{0}' eklentisi devre dışı bırakılamıyor. '{1}' ve '{2}' eklentileri buna bağlı.", + "multipleDependentsError": "'{0}' eklentisi devre dışı bırakılamıyor. '{1}, '{2}' eklentileri ve diğerleri buna bağlı.", + "installConfirmation": "'{0}' eklentisini yüklemek ister misiniz?", + "install": "Yükle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json b/i18n/trk/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json new file mode 100644 index 0000000000..b774684635 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/feedback/electron-browser/feedback.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "sendFeedback": "Geri Bildirimi Tweet'le", + "label.sendASmile": "Geri bildiriminizi bize Tweet'leyin.", + "patchedVersion1": "Kurulumunuz bozuk.", + "patchedVersion2": "Eğer bir hata gönderiyorsanız bunu belirtin.", + "sentiment": "Deneyiminiz nasıldı?", + "smileCaption": "Mutlu", + "frownCaption": "Üzgün", + "other ways to contact us": "Bize ulaşmanın diğer yolları", + "submit a bug": "Bir hata gönder", + "request a missing feature": "Eksik bir özellik talebinde bulun", + "tell us why?": "Bize nedenini söyleyin:", + "commentsHeader": "Açıklamalar", + "tweet": "Tweet'le", + "character left": "karakter kaldı", + "characters left": "karakter kaldı", + "feedbackSending": "Gönderiliyor", + "feedbackSent": "Teşekkürler", + "feedbackSendingError": "Yeniden dene" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json new file mode 100644 index 0000000000..dfed8b6a21 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "binaryFileEditor": "İkili Dosya Görüntüleyici" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json new file mode 100644 index 0000000000..2aae702223 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/editors/textFileEditor.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "textFileEditor": "Metin Dosyası Düzenleyicisi", + "createFile": "Dosya Oluştur", + "fileEditorWithInputAriaLabel": "{0}. Metin dosyası düzenleyici.", + "fileEditorAriaLabel": "Metin dosyası düzenleyici." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json new file mode 100644 index 0000000000..c224baa0ec --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/explorerViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folders": "Klasörler" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json new file mode 100644 index 0000000000..fbcb224373 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "filesCategory": "Dosyalar", + "revealInSideBar": "Kenar Çubuğunda Ortaya Çıkar", + "acceptLocalChanges": "Değişikliklerinizi kullanın ve diskteki içeriklerin üzerine yazın", + "revertLocalChanges": "Değişikliklerinizi göz ardı edin ve diskteki içeriğe geri dönün" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.i18n.json new file mode 100644 index 0000000000..c360299204 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "retry": "Yeniden Dene", + "rename": "Yeniden Adlandır", + "newFile": "Yeni Dosya", + "newFolder": "Yeni Klasör", + "openFolderFirst": "İçinde dosyalar veya klasörler oluşturmak için ilk olarak bir klasör açın.", + "newUntitledFile": "Yeni İsimsiz Dosya", + "createNewFile": "Yeni Dosya", + "createNewFolder": "Yeni Klasör", + "deleteButtonLabelRecycleBin": "&&Geri Dönüşüm Kutusuna Taşı", + "deleteButtonLabelTrash": "&&Çöp Kutusuna Taşı", + "deleteButtonLabel": "&&Sil", + "dirtyMessageFolderOneDelete": "1 dosyada kaydedilmemiş değişiklik barındıran bir klasörü siliyorsunuz. Devam etmek istiyor musunuz?", + "dirtyMessageFolderDelete": "{0} dosyada kaydedilmemiş değişiklik barındıran bir klasörü siliyorsunuz. Devam etmek istiyor musunuz?", + "dirtyMessageFileDelete": "Kaydedilmemiş değişiklik barındıran bir dosyayı siliyorsunuz. Devam etmek istiyor musunuz?", + "dirtyWarning": "Değişiklikleriniz, kaydetmezseniz kaybolur.", + "confirmMoveTrashMessageFolder": "'{0}' ve içindekileri silmek istediğinizden emin misiniz?", + "confirmMoveTrashMessageFile": "'{0}' öğesini silmek istediğinize emin misiniz?", + "undoBin": "Geri dönüşüm kutusundan geri alabilirsiniz.", + "undoTrash": "Çöp kutusundan geri alabilirsiniz.", + "confirmDeleteMessageFolder": "'{0}' öğesini ve içindekileri kalıcı olarak silmek istediğinizden emin misiniz?", + "confirmDeleteMessageFile": "'{0}' öğesini kalıcı olarak silmek istediğinizden emin misiniz?", + "irreversible": "Bu eylem geri döndürülemez!", + "permDelete": "Kalıcı Olarak Sil", + "delete": "Sil", + "importFiles": "Dosya İçe Aktar", + "confirmOverwrite": "Hedef klasörde aynı ada sahip bir dosya veya klasör zaten var. Değiştirmek istiyor musunuz?", + "replaceButtonLabel": "&&Değiştir", + "copyFile": "Kopyala", + "pasteFile": "Yapıştır", + "duplicateFile": "Çoğalt", + "openToSide": "Yana Aç", + "compareSource": "Karşılaştırma İçin Seç", + "globalCompareFile": "Aktif Dosyayı Karşılaştır...", + "pickHistory": "Karşılaştırmak için daha önce açılan bir dosyayı seçin", + "unableToFileToCompare": "Seçtiğiniz dosya, '{0}' ile karşılaştırılamaz.", + "openFileToCompare": "Bir başka dosya ile karşılaştırmak için ilk olarak bir dosya açın.", + "compareWith": "'{0}' dosyasını '{1}' ile karşılaştır", + "compareFiles": "Dosyaları Karşılaştır", + "refresh": "Yenile", + "save": "Kaydet", + "saveAs": "Farklı Kaydet...", + "saveAll": "Tümünü Kaydet", + "saveAllInGroup": "Gruptaki Tümünü Kadet", + "saveFiles": "Kaydedilmemiş Değişiklikler İçeren Dosyaları Kaydet", + "revert": "Dosyayı Geri Döndür", + "focusOpenEditors": "Açık Düzenleyiciler Görünümüne Odakla", + "focusFilesExplorer": "Dosya Gezginine Odakla", + "showInExplorer": "Aktif Dosyayı Kenar Çubuğunda Ortaya Çıkar", + "openFileToShow": "Gezginde göstermek için ilk olarak bir dosya açın", + "collapseExplorerFolders": "Gezgindeki Klasörleri Daralt", + "refreshExplorer": "Gezgini Yenile", + "openFile": "Dosya Aç...", + "openFileInNewWindow": "Aktif Dosyayı Yeni Pencerede Aç", + "openFileToShowInNewWindow": "Yeni pencerede açmak için ilk olarak bir dosya açın", + "revealInWindows": "Gezginde Ortaya Çıkar", + "revealInMac": "Finder'da Ortaya Çıkar", + "openContainer": "İçeren Klasörü Aç", + "revealActiveFileInWindows": "Aktif Dosyayı Windows Gezgini'nde Ortaya Çıkar", + "revealActiveFileInMac": "Aktif Dosyayı Finder'da Ortaya Çıkar", + "openActiveFileContainer": "Aktif Dosyayı İçeren Klasörü Aç", + "copyPath": "Yolu Kopyala", + "copyPathOfActive": "Aktif Dosyanın Yolunu Kopyala", + "emptyFileNameError": "Bir dosya veya klasör adı sağlanması gerekiyor.", + "fileNameExistsError": "Bu konumda bir **{0}** dosyası veya klasörü zaten mevcut. Lütfen başka bir ad seçin.", + "invalidFileNameError": "**{0}** adı, bir dosya veya klasör adı olarak geçerli değildir. Lütfen başka bir ad seçin.", + "filePathTooLongError": "**{0}** adı çok uzun bir yol ile sonuçlanıyor. Lütfen daha kısa bir ad seçin.", + "compareWithSaved": "Aktif Dosyayı Kaydedilenle Karşılaştır", + "modifiedLabel": "{0} (diskte) ↔ {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/fileCommands.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/fileCommands.i18n.json new file mode 100644 index 0000000000..2f1b0b7dda --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/fileCommands.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFileToCopy": "Yolunu kopyalamak için ilk olarak bir dosya açın", + "openFileToReveal": "Ortaya çıkarmak için ilk olarak bir dosya açın" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/files.contribution.i18n.json new file mode 100644 index 0000000000..e6ebaf1969 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showExplorerViewlet": "Gezgini Göster", + "explore": "Gezgin", + "view": "Görüntüle", + "textFileEditor": "Metin Dosyası Düzenleyicisi", + "binaryFileEditor": "İkili Dosya Düzenleyicisi", + "filesConfigurationTitle": "Dosyalar", + "exclude": "Dosya ve klasörleri hariç tutmak için glob desenlerini yapılandırın.", + "files.exclude.boolean": "Dosya yollarının eşleştirileceği glob deseni. Deseni etkinleştirmek veya devre dışı bırakmak için true veya false olarak ayarlayın.", + "files.exclude.when": "Eşleşen bir dosyanın eşdüzey dosyalarında ek denetim. Eşleşen dosya adı için değişken olarak $(basename) kullanın.", + "associations": "Dillerle dosya ilişkilendirmelerini yapılandırın (ör. \"*.uzanti\": \"html\"). Bunların, kurulu olan dillerin varsayılan ilişkilendirmeleri karşısında önceliği vardır.", + "encoding": "Dosyalar okunurken ve yazılırken kullanılacak varsayılan karakter kümesi kodlaması.", + "autoGuessEncoding": "Etkinleştirildiğinde, dosyaları açarken karakter kümesini tahmin etmeye çalışır", + "eol": "Varsayılan satır sonu karakteri. LF için \\n ve CRLF için \\r\\n kullan.", + "trimTrailingWhitespace": "Etkinleştirildiğinde, bir dosyayı kaydettiğinizde sondaki boşluk kırpılır.", + "insertFinalNewline": "Etkinleştirildiğinde, bir dosyayı kaydederken dosya sonuna bir boş satır ekler.", + "files.autoSave.off": "Kaydedilmemiş değişiklikler içeren bir dosya hiçbir zaman otomatik olarak kaydedilmez.", + "files.autoSave.afterDelay": "Kaydedilmemiş değişiklikler içeren bir dosya, 'files.autoSaveDelay' ayarlandıktan sonra otomatik olarak kaydedilir.", + "files.autoSave.onFocusChange": "Kaydedilmemiş değişiklikler içeren bir dosya, düzenleyici odaktan çıktığı an otomatik olarak kaydedilir.", + "files.autoSave.onWindowChange": "Kaydedilmemiş değişiklikler içeren bir dosya, pencere odaktan çıktığı an otomatik olarak kaydedilir.", + "autoSave": "Kaydedilmemiş değişiklikler içeren dosyaların otomatik kaydedilmesini denetler. Kabul edilen değerler: '{0}', '{1}', '{2}' (düzenleyici odaktan çıktığında), '{3}' (pencere odaktan çıktığında). '{4}' olarak ayarlanırsa, gecikmeyi 'files.autoSaveDelay' ile ayarlayabilirsiniz.", + "autoSaveDelay": "Kaydedilmemiş değişiklikler içeren bir dosyanın kaç ms gecikmeli otomatik olarak kaydedileceğini denetler. Sadece 'files.autoSave', '{0}' olarak ayarlandığında uygulanır.", + "watcherExclude": "Dosya izlemeden hariç tutulacak dosya yollarının glob desenlerini yapılandırın. Desenler mutlak yollarla eşleşmelidir (ör. ** ile ön ek veya düzgün eşleştirmek için tam yol). Bu ayar değiştiğinde yeniden başlatma gerektirir. Code'un başlangıçta çok fazla CPU zamanı harcadığını görürseniz, başlangıç yüklemesini azaltmak için büyük klasörleri hariç tutabilirsiniz.", + "hotExit.off": "Hızlı çıkışı devre dışı bırak.", + "hotExit.onExit": "Hızlı çıkış, uygulama kapandığında tetiklenir, yani Windows/Linux'da son pencere kapandığında veya workbench.action.quit komutu tetiklendiği zaman (komut paleti, tuş bağı, menü). Bir sonraki başlatmada tüm pencereler yedekleriyle geri yüklenir.", + "hotExit.onExitAndWindowClose": "Hızlı çıkış, uygulama kapandığında tetiklenir, yani Windows/Linux'da son pencere kapandığında veya workbench.action.quit komutu tetiklendiği zaman (komut paleti, tuş bağı, menü), ve ayrıca son pencere olmasından bağımsız açık bir klasör bulunan herhangi bir pencere varsa. Bir sonraki başlatmada tüm pencereler yedekleriyle geri yüklenir. Klasör pencerelerini kapatılmadan önceki konumlarına geri yüklemek için \"window.restoreWindows\" ögesini \"all\" olarak ayarlayın.", + "hotExit": "Oturumlar arasında kaydedilmemiş dosyaların hatırlanıp hatırlanmayacağını denetler, düzenleyiciden çıkarken kaydetmek için izin istenmesi atlanacaktır.", + "useExperimentalFileWatcher": "Yeni deneysel dosya izleyicisini kullanın.", + "defaultLanguage": "Yeni dosyalara atanan varsayılan dil modu.", + "editorConfigurationTitle": "Düzenleyici", + "formatOnSave": "Dosyayı kaydederken biçimlendir. Bir biçimlendirici mevcut olmalıdır, dosya otomatik olarak kaydedilmemelidir, ve düzenleyici kapanmıyor olmalıdır.", + "explorerConfigurationTitle": "Dosya Gezgini", + "openEditorsVisible": "Açık Editörler bölmesinde gösterilen düzenleyici sayısı. Bölmeyi gizlemek için 0 olarak ayarlayın.", + "dynamicHeight": "Açık düzenleyiciler bölümü yüksekliğinin öge sayısına göre dinamik olarak uyarlanıp uyarlanmayacağını denetler.", + "autoReveal": "Gezginin dosyaları açarken, onları otomatik olarak ortaya çıkartmasını ve seçmesini denetler.", + "enableDragAndDrop": "Gezgeinin sürükle bırak ile dosyaları ve klasörleri taşımaya izin verip vermeyeceğini denetler.", + "sortOrder.default": "Dosya ve klasörler adlarına göre, alfabetik olarak sıralanırlar. Klasörler dosyalardan önce görüntülenir.", + "sortOrder.mixed": "Dosya ve klasörler adlarına göre, alfabetik olarak sıralanırlar. Dosyalar ve klasörler iç içe bulunur.", + "sortOrder.filesFirst": "Dosya ve klasörler adlarına göre, alfabetik olarak sıralanırlar. Dosyalar klasörlerden önce görüntülenir.", + "sortOrder.type": "Dosya ve klasörler uzantılarına göre, alfabetik olarak sıralanırlar. Klasörler dosyalardan önce görüntülenir.", + "sortOrder.modified": "Dosya ve klasörler son değiştirilme tarihine göre, azalan düzende sıralanırlar. Klasörler dosyalardan önce görüntülenir.", + "sortOrder": "Gezginde dosya ve klasörlerin sıralamasını denetler. Varsayılan sıralamaya ek olarak, sıralamayı; 'mixed' (dosya ve klasörler karışık olarak sıralanır), 'type' (dosya türüne göre), 'modified' (son düzenlenme tarihine göre) veya 'filesFirst' (dosyaları klasörlerden önce sırala) olarak ayarlayabilirsiniz." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json new file mode 100644 index 0000000000..4d62341853 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/saveErrorHandler.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "discard": "At", + "overwrite": "Üzerine Yaz", + "retry": "Yeniden Dene", + "readonlySaveError": "'{0}' kaydedilemedi: Dosya yazmaya karşı korunuyor. Korumayı kaldırmak için 'Üzerine Yaz'ı seçin.", + "genericSaveError": "'{0}' kaydedilemedi: ({1}).", + "staleSaveError": "'{0}' kaydedilemedi: Diskteki içerik daha yeni. Sizdeki sürüm ile disktekini karşılaştırmak için **Karşılaştır**a tıklayın.", + "compareChanges": "Karşılaştır", + "saveConflictDiffLabel": "{0} (diskte) ↔ {1} ({2} uygulamasında) - Kaydetme çakışmasını çöz", + "userGuide": "Değişikliklerinizi **geri al**mak veya diskteki içeriğin **üzerine yaz**mak için düzenleyicideki araç çubuğunu kullanabilirsiniz" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json new file mode 100644 index 0000000000..9794c3955f --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "noWorkspace": "Açık Klasör Yok", + "explorerSection": "Dosya Gezgini Bölümü", + "noWorkspaceHelp": "Henüz bir klasör açmadınız.", + "openFolder": "Klasör Aç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json new file mode 100644 index 0000000000..493a8cc3aa --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/views/explorerView.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "explorerSection": "Dosya Gezgini Bölümü", + "treeAriaLabel": "Dosya Gezgini" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json new file mode 100644 index 0000000000..f185d21353 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInputAriaLabel": "Dosya adı girin. Onaylamak için Enter'a, iptal etmek için Escape tuşuna basın.", + "filesExplorerViewerAriaLabel": "{0}, Dosya Gezgini", + "dropFolders": "Çalışma alanına klasörleri eklemek istiyor musunuz?", + "dropFolder": "Klasörü çalışma alanına eklemek istiyor musunuz?", + "addFolders": "Klasörleri &&Ekle", + "addFolder": "Klasörü &&Ekle", + "confirmOverwriteMessage": "'{0}' hedef klasörde zaten mevcut. Değiştirmek istiyor musunuz?", + "irreversible": "Bu eylem geri döndürülemez!", + "replaceButtonLabel": "&&Değiştir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json new file mode 100644 index 0000000000..de889251ea --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/views/openEditorsView.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openEditors": "Açık Düzenleyiciler", + "openEditosrSection": "Açık Düzenleyiciler Bölümü", + "treeAriaLabel": "Açık Düzenleyiciler: Aktif Dosyaların Listesi", + "dirtyCounter": "{0} kaydedilmemiş" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json new file mode 100644 index 0000000000..930b92c38f --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/browser/views/openEditorsViewer.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGroupAriaLabel": "{0}, Düzenleyici Grubu", + "openEditorAriaLabel": "{0}, Açık Düzenleyici", + "saveAll": "Tümünü Kaydet", + "closeAllUnmodified": "Değiştirilmeyenleri Kapat", + "closeAll": "Tümünü Kapat", + "compareWithSaved": "Kaydedilenle Karşılaştır", + "close": "Kapat", + "closeOthers": "Diğerlerini Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/trk/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json new file mode 100644 index 0000000000..81c2f606f6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dirtyFiles": "{0} kaydedilmemiş dosya" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json b/i18n/trk/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json new file mode 100644 index 0000000000..598fdd3a9f --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/files/common/editors/fileEditorInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "orphanedFile": "{0} (diskten silindi)" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/html/browser/html.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/html/browser/html.contribution.i18n.json new file mode 100644 index 0000000000..7e2408cad3 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/html/browser/html.contribution.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.editor.label": "Html Önizlemesi", + "devtools.webview": "Geliştirici: Web Görünümü Araçları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json b/i18n/trk/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json new file mode 100644 index 0000000000..aca7f97aeb --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/html/browser/htmlPreviewPart.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "html.voidInput": "Geçersiz düzenleyici girdisi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/trk/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 0000000000..c2d764a9a7 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devtools.webview": "Geliştirici: Web Görünümü Araçları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/markers/common/messages.i18n.json b/i18n/trk/src/vs/workbench/parts/markers/common/messages.i18n.json new file mode 100644 index 0000000000..486d0cd551 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/markers/common/messages.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewCategory": "Görüntüle", + "problems.view.show.label": "Sorunları Göster", + "problems.panel.configuration.title": "Sorunlar Görünümü", + "problems.panel.configuration.autoreveal": "Sorunlar görünümünün; dosyalar açılırken, dosyaları otomatik olarak ortaya çıkarıp çıkarmayacağını denetler.", + "markers.panel.title.problems": "Sorunlar", + "markers.panel.aria.label.problems.tree": "Dosyalara göre gruplandırılmış sorunlar", + "markers.panel.no.problems.build": "Şu ana kadar çalışma alanında herhangi bir sorun tespit edilmedi.", + "markers.panel.no.problems.filters": "Belirtilen süzgeç ölçütleriyle sonuç bulunamadı", + "markers.panel.action.filter": "Sorunları Süz", + "markers.panel.filter.placeholder": "Türe veya metne göre süz", + "markers.panel.filter.errors": "hatalar", + "markers.panel.filter.warnings": "uyarılar", + "markers.panel.filter.infos": "bilgilendirmeler", + "markers.panel.single.error.label": "1 Hata", + "markers.panel.multiple.errors.label": "{0} Hata", + "markers.panel.single.warning.label": "1 Uyarı", + "markers.panel.multiple.warnings.label": "{0} Uyarı", + "markers.panel.single.info.label": "1 Bilgilendirme", + "markers.panel.multiple.infos.label": "{0} Bilgilendirme", + "markers.panel.single.unknown.label": "1 Bilinmeyen", + "markers.panel.multiple.unknowns.label": "{0} Bilinmeyen", + "markers.panel.at.ln.col.number": "({0}, {1})", + "problems.tree.aria.label.resource": "{0} {1} sorun içeriyor", + "problems.tree.aria.label.error.marker": "{0} tarafından oluşturulan hata: {2}. satırın {3}. karakterinde {1}", + "problems.tree.aria.label.error.marker.nosource": "Hata: {1}. satırın {2}. karakterinde {0}", + "problems.tree.aria.label.warning.marker": "{0} tarafından oluşturulan uyarı: {2}. satırın {3}. karakterinde {1}", + "problems.tree.aria.label.warning.marker.nosource": "Uyarı: {1}. satırın {2}. karakterinde {0}", + "problems.tree.aria.label.info.marker": "{0} tarafından oluşturulan bilgilendirme: {2}. satırın {3}. karakterinde {1}", + "problems.tree.aria.label.info.marker.nosource": "Bilgilendirme: {1}. satırın {2}. karakterinde {0}", + "problems.tree.aria.label.marker": "{0} tarafından oluşturulan sorun: {2}. satırın {3}. karakterinde {1}", + "problems.tree.aria.label.marker.nosource": "Sorun: {1}. satırın {2}. karakterinde {0}", + "errors.warnings.show.label": "Hataları ve Uyarıları Göster" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json b/i18n/trk/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json new file mode 100644 index 0000000000..b02d4783d8 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copyMarker": "Kopyala" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..49862b2bfa --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/nps/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Hızlı bir geri bildirim anketine katılmak ister misiniz?", + "takeSurvey": "Ankete Katıl", + "remindLater": "Daha Sonra Hatırlat", + "neverAgain": "Tekrar Gösterme" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/output/browser/output.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/output/browser/output.contribution.i18n.json new file mode 100644 index 0000000000..a4d7d9d199 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/output/browser/output.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Çıktı", + "viewCategory": "Görüntüle", + "clearOutput.label": "Çıktıyı Temizle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/output/browser/outputActions.i18n.json b/i18n/trk/src/vs/workbench/parts/output/browser/outputActions.i18n.json new file mode 100644 index 0000000000..361a5736f4 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/output/browser/outputActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleOutput": "Çıktıyı Aç/Kapat", + "clearOutput": "Çıktıyı Temizle", + "toggleOutputScrollLock": "Çıktı Kaydırma Kilidini Aç/Kapat", + "switchToOutput.label": "Çıktıya Geçiş Yap" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/output/browser/outputPanel.i18n.json b/i18n/trk/src/vs/workbench/parts/output/browser/outputPanel.i18n.json new file mode 100644 index 0000000000..d943d274af --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/output/browser/outputPanel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "outputPanelWithInputAriaLabel": "{0}, Çıktı paneli", + "outputPanelAriaLabel": "Çıktı paneli" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/output/common/output.i18n.json b/i18n/trk/src/vs/workbench/parts/output/common/output.i18n.json new file mode 100644 index 0000000000..09f98ba0ef --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/output/common/output.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "output": "Çıktı", + "channel": "'{0}' için" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json new file mode 100644 index 0000000000..5fb421efac --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/performance/electron-browser/performance.contribution.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "slow": "Yavaş başlangıç tespit edildi", + "slow.detail": "Az önce yavaş başlangıç yaşadığınız için üzgünüz. Lütfen '{0}' uygulamasını profil oluşturucu etkinleştirilmiş olarak başlatın, profilleri bizle paylaşın, ve biz de başlangıcı yeniden harika yapmak için çok çalışalım.", + "prof.message": "Profiller başarıyla oluşturuldu.", + "prof.detail": "Lütfen bir sorun (bildirimi) oluşturun ve aşağıdaki dosyaları manuel olarak ekleyin:\n{0}", + "prof.restartAndFileIssue": "Sorun Oluştur ve Yeniden Başlat", + "prof.restart": "Yeniden Başlat", + "prof.thanks": "Bize yardımcı olduğunuz için teşekkürler.", + "prof.detail.restart": "'{0}' uygulamasını kullanmaya devam etmek için son bir yeniden başlatma gerekiyor. Katkılarınız için tekrar teşekkür ederiz." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json new file mode 100644 index 0000000000..273d36b65f --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingWidgets.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.initial": "İstenen tuş kombinasyonuna basın ve daha sonra ENTER'a basın. İptal etmek için ESCAPE tuşuna basın.", + "defineKeybinding.chordsTo": "ardından" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json new file mode 100644 index 0000000000..7ed67495e4 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingsEditor.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "keybindingsInputName": "Klavye Kısayolları", + "SearchKeybindings.AriaLabel": "Tuş bağlarını ara", + "SearchKeybindings.Placeholder": "Tuş bağlarını ara", + "sortByPrecedene": "Önceliğe Göre Sırala", + "header-message": "Gelişmiş özelleştirmeler için açın ve düzenleyin:", + "keybindings-file-name": "keybindings.json", + "keybindingsLabel": "Tuş bağları", + "changeLabel": "Tuş Bağını Değiştir", + "addLabel": "Tuş Bağını Ekle", + "removeLabel": "Tuş Bağını Kaldır", + "resetLabel": "Tuş Bağını Sıfırla", + "showConflictsLabel": "Çakışmaları Göster", + "copyLabel": "Kopyala", + "error": "Tuş bağını düzenlerken '{0}' hatası. Lütfen 'keybindings.json' dosyasını açın ve kontrol edin.", + "command": "Command", + "keybinding": "Tuş bağı", + "source": "Kaynak", + "when": "Koşul", + "editKeybindingLabelWithKey": "{0} Tuş Bağını Değiştir", + "editKeybindingLabel": "Tuş Bağını Değiştir", + "addKeybindingLabelWithKey": "{0} Tuş Bağını Ekle", + "addKeybindingLabel": "Tuş Bağını Ekle", + "commandAriaLabel": "Komut {0}'dır.", + "keybindingAriaLabel": "Tuş bağı {0}'dır.", + "noKeybinding": "Tuş bağı atanmamış.", + "sourceAriaLabel": "Kaynak {0}'dır.", + "whenAriaLabel": "Koşul {0} şeklindedir.", + "noWhen": "Koşul içeriği yok." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json new file mode 100644 index 0000000000..752dec08b4 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defineKeybinding.start": "Tuş Bağı Tanımla", + "defineKeybinding.kbLayoutErrorMessage": "Bu tuş kombinasyonunu geçerli klavye düzeninizde üretemeyeceksiniz.", + "defineKeybinding.kbLayoutLocalAndUSMessage": "Geçerli klavye düzeniniz için **{0}** (Birleşik Devletler standardı için **{1}**).", + "defineKeybinding.kbLayoutLocalMessage": "Geçerli klavye düzeniniz için **{0}**." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json new file mode 100644 index 0000000000..1546e847c0 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferences.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultPreferencesEditor": "Varsayılan Tercihler Düzenleyicisi", + "keybindingsEditor": "Tuş Bağları Düzenleyicisi", + "preferences": "Tercihler" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json new file mode 100644 index 0000000000..b3bc757263 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openGlobalSettings": "Kullanıcı Ayarlarını Aç", + "openGlobalKeybindings": "Klavye Kısayollarını Aç", + "openGlobalKeybindingsFile": "Klavye Kısayolları Dosyasını Aç", + "openWorkspaceSettings": "Çalışma Alanı Ayarlarını Aç", + "openFolderSettings": "Klasör Ayarlarını Aç", + "pickFolder": "Klasör Seç", + "configureLanguageBasedSettings": "Dile Özel Ayarları Yapılandır...", + "languageDescriptionConfigured": "({0})", + "pickLanguage": "Dili Seç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json new file mode 100644 index 0000000000..bc2c309366 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesEditor.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "settingsEditorName": "Varsayılan Ayarlar", + "SearchSettingsWidget.AriaLabel": "Ayarları ara", + "SearchSettingsWidget.Placeholder": "Ayarları Ara", + "totalSettingsMessage": "Toplam {0} Ayar", + "noSettingsFound": "Sonuç Yok", + "oneSettingFound": "1 ayar eşleşti", + "settingsFound": "{0} ayar eşleşti", + "fileEditorWithInputAriaLabel": "{0}. Metin dosyası düzenleyici.", + "fileEditorAriaLabel": "Metin dosyası düzenleyici.", + "preferencesAriaLabel": "Varsayılan tercihler. Salt okunabilir metin editörü." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json new file mode 100644 index 0000000000..4b0c9dc34d --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "emptyUserSettingsHeader": "Varsayılan ayarların üzerine yazmak için ayarlarınızı buraya yerleştirin.", + "errorInvalidConfiguration": "Ayarlara yazılamıyor. Lütfen dosyadaki hataları/uyarıları düzeltin ve tekrar deneyin.", + "emptyWorkspaceSettingsHeader": "Varsayılan kullanıcı ayarlarının üzerine yazmak için ayarlarınızı buraya yerleştirin.", + "emptyFolderSettingsHeader": "Çalışma alanı ayarlarındakilerin üzerine yazmak için klasör ayarlarınızı buraya yerleştirin.", + "defaultFolderSettingsTitle": "Varsayılan Klasör Ayarları", + "defaultSettingsTitle": "Varsayılan Ayarlar", + "noSettingsFound": "Hiçbir Ayar Bulunamadı.", + "editTtile": "Düzenle", + "replaceDefaultValue": "Ayarlarda Değiştir", + "copyDefaultValue": "Ayarlara Kopyala", + "unsupportedPHPExecutablePathSetting": "Bu ayar, bir Kullanıcı Ayarı olmalıdır. PHP'yi çalışma alanı için yapılandırmak için bir PHP dosyasını açın ve durum çubuğundaki 'PHP Yolu'na tıklayın.", + "unsupportedWorkspaceSetting": "Bu ayar, bir Kullanıcı Ayarı olmalıdır.", + "unsupportedWorkbenchSetting": "Bu ayar şu an uygulanamıyor. Ayar, bu klasörü açtığınızda uygulanacaktır." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json new file mode 100644 index 0000000000..f9b9c847c4 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesService.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openFolderFirst": "Çalışma alanı ayarları oluşturmak için ilk olarak bir klasör açın", + "emptyKeybindingsHeader": "Varsayılanların üzerine yazmak için tuş bağlarınızı bu dosyaya yerleştirin", + "defaultKeybindings": "Varsayılan Tuş Bağları", + "folderSettingsName": "{0} (Klasör Ayarları)", + "fail.createSettings": " '{0}' oluşturulamadı ({1})." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json new file mode 100644 index 0000000000..aad7afb00c --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesWidgets.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "folderSettingsDetails": "Klasör Ayarları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json new file mode 100644 index 0000000000..15ea5810ce --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "default": "Varsayılan", + "user": "Kullanıcı", + "meta": "meta", + "option": "seçenek" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/common/preferences.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/common/preferences.i18n.json new file mode 100644 index 0000000000..cbf1c20610 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/common/preferences.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "userSettingsTarget": "Kullanıcı Ayarları", + "workspaceSettingsTarget": "Çalışma Alanı Ayarları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json new file mode 100644 index 0000000000..1b99afed55 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/preferences/common/preferencesModels.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commonlyUsed": "Yaygın Olarak Kullanılan", + "noSettings": "Ayar Yok", + "defaultKeybindingsHeader": "Tuş bağları dosyanıza yerleştirerek tuş bağlarının üzerine yazın." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json new file mode 100644 index 0000000000..0d723a12da --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Tüm Komutları Göster", + "clearCommandHistory": "Komut Geçmişini Temizle", + "showCommands.label": "Komut Paleti...", + "entryAriaLabelWithKey": "{0}, {1}, komutlar", + "entryAriaLabel": "{0}, komutlar", + "canNotRun": "'{0}' komutu buradan çalıştırılamıyor.", + "actionNotEnabled": "'{0}' komutu geçerli bağlamda etkin değil.", + "recentlyUsed": "yakınlarda kullanıldı", + "morecCommands": "diğer komutlar", + "commandLabel": "{0}: {1}", + "cat.title": "{0}: {1}", + "noCommandsMatching": "Eşleşen komut yok" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json new file mode 100644 index 0000000000..fd8539153d --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoLine": "Satıra Git...", + "gotoLineLabelEmptyWithLimit": "Gitmek için 1 ile {0} arasında bir satır numarası yazın", + "gotoLineLabelEmpty": "Gidilecek satır numarasını yazın", + "gotoLineColumnLabel": "{0}. satırın {1}. karakterine git", + "gotoLineLabel": "{0} satırına git", + "gotoLineHandlerAriaLabel": "Gidilecek satır numarasını yazın.", + "cannotRunGotoLine": "Satıra gitmek için ilk olarak bir metin dosyası açın" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json new file mode 100644 index 0000000000..f0f5582810 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "gotoSymbol": "Dosyada Sembole Git...", + "symbols": "semboller ({0})", + "method": "yöntemler ({0})", + "function": "fonksiyonlar ({0})", + "_constructor": "oluşturucular ({0})", + "variable": "değişkenler ({0})", + "class": "sınıflar ({0})", + "interface": "arayüzler ({0})", + "namespace": "isim uzayları ({0})", + "package": "paketler ({0})", + "modules": "modüller ({0})", + "property": "özellikler ({0})", + "enum": "numaralandırmalar ({0})", + "string": "dizeler ({0})", + "rule": "kurallar ({0})", + "file": "dosyalar ({0})", + "array": "diziler ({0})", + "number": "sayılar ({0})", + "boolean": "boole değerleri ({0})", + "object": "nesneler ({0})", + "key": "anahtarlar ({0})", + "entryAriaLabel": "{0}, semboller", + "noSymbolsMatching": "Eşleşen sembol yok", + "noSymbolsFound": "Sembol bulunamadı", + "gotoSymbolHandlerAriaLabel": "Geçerli düzenleyicideki sembolleri daraltmak için yazmaya başlayın.", + "cannotRunGotoSymbolInFile": "Dosya için sembol bilgisi yok", + "cannotRunGotoSymbol": "Sembole gitmek için ilk olarak bir metin dosyası açın" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json new file mode 100644 index 0000000000..1c482f131e --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/quickopen/browser/helpHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, seçici yardımı", + "globalCommands": "genel komutlar", + "editorCommands": "düzenleyici komutları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json new file mode 100644 index 0000000000..03c31d7759 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commandsHandlerDescriptionDefault": "Komutları Göster ve Çalıştır", + "gotoLineDescriptionMac": "Satıra Git", + "gotoLineDescriptionWin": "Satıra Git", + "gotoSymbolDescription": "Dosyada Sembole Git", + "gotoSymbolDescriptionScoped": "Kategoriye Göre Dosyada Sembole Git", + "helpDescription": "Yardımı Göster", + "viewPickerDescription": "Görünümü Aç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json new file mode 100644 index 0000000000..d312cfd67c --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, görünüm seçici", + "views": "Görünümler", + "panels": "Paneller", + "terminals": "Terminal", + "terminalTitle": "{0}: {1}", + "channels": "Çıktı", + "openView": "Görünümü Aç", + "quickOpenView": "Görünümü Hızlı Aç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json new file mode 100644 index 0000000000..26a6f3b052 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "relaunchSettingMessage": "Yürürlüğe girmesi için yeniden başlatma gerektiren bir ayar değişti.", + "relaunchSettingDetail": "{0} uygulamasını yeniden başlatmak ve bu ayarı etkinleştirmek için lütfen yeniden başlat butonuna basın.", + "restart": "Yeniden Başlat", + "relaunchWorkspaceMessage": "Bu çalışma alanı değişikliği eklenti sistemimizi yeniden başlatmayı gerektirir.", + "reload": "Yeniden Yükle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json new file mode 100644 index 0000000000..93151a27a0 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorGutterModifiedBackground": "Değiştirilen satırlar için düzenleyici oluğu arka plan rengi.", + "editorGutterAddedBackground": "Eklenen satırlar için düzenleyici oluğu arka plan rengi.", + "editorGutterDeletedBackground": "Silinen satırlar için düzenleyici oluğu arka plan rengi.", + "overviewRulerModifiedForeground": "Değiştirilen içerik için genel bakış cetvelinin işaretleyici rengi.", + "overviewRulerAddedForeground": "Eklenen içerik için genel bakış cetvelinin işaretleyici rengi.", + "overviewRulerDeletedForeground": "Silinen içerik için genel bakış cetvelinin işaretleyici rengi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json new file mode 100644 index 0000000000..2e8d17f00f --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scm.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "toggleGitViewlet": "Git'i Göster", + "source control": "Kaynak Kontrolü", + "toggleSCMViewlet": "SCM'yi Göster", + "view": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json new file mode 100644 index 0000000000..0dc98ece1a --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmActivity.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "scmPendingChangesBadge": "{0} bekleyen değişiklik" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json new file mode 100644 index 0000000000..2f7e00d929 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmMenus.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "installAdditionalSCMProviders": "Ek SCM Sağlayıcıları Yükle...", + "switch provider": "SCM Sağlayıcısı Değiştir..." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json new file mode 100644 index 0000000000..9f5c48d8f0 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "commitMessage": "Mesaj (commit'lemek için {0} tuşlarına basın)", + "installAdditionalSCMProviders": "Ek SCM Sağlayıcıları Yükle...", + "no open repo": "Aktif kaynak kontrolü yok.", + "source control": "Kaynak Kontrolü", + "viewletTitle": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json new file mode 100644 index 0000000000..c2217ffd86 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/browser/openAnythingHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileAndTypeResults": "dosya ve sembol sonuçları", + "fileResults": "dosya sonuçları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json new file mode 100644 index 0000000000..1db710f6c7 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/browser/openFileHandler.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, dosya seçici", + "searchResults": "arama sonuçları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json new file mode 100644 index 0000000000..bb9bc8a6f7 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/browser/openSymbolHandler.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, sembol seçici", + "symbols": "sembol sonuçları", + "noSymbolsMatching": "Eşleşen sembol yok", + "noSymbolsWithoutInput": "Sembolleri aramak için yazmaya başlayın" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json new file mode 100644 index 0000000000..db9fc68d7a --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "defaultLabel": "giriş", + "useIgnoreFilesDescription": "Yok Sayma Dosyalarını Kullan", + "useExcludeSettingsDescription": "Hariç Tutma Ayarlarını Kullan" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/replaceService.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/replaceService.i18n.json new file mode 100644 index 0000000000..7e954e8c92 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/browser/replaceService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileReplaceChanges": "{0} ↔ {1} (Değiştirme Önizlemesi)" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/search.contribution.i18n.json new file mode 100644 index 0000000000..40b4e1e197 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "showTriggerActions": "Çalışma Alanında Sembole Git...", + "name": "Ara", + "showSearchViewlet": "Aramayı Göster", + "view": "Görüntüle", + "findInFiles": "Dosyalarda Bul", + "openAnythingHandlerDescription": "Dosyaya Git", + "openSymbolDescriptionNormal": "Çalışma Alanında Sembole Git", + "searchOutputChannelTitle": "Ara", + "searchConfigurationTitle": "Ara", + "exclude": "Aramalarda dosyaları ve klasörleri hariç tutmak için glob desenlerini yapılandırın. files.exclude ayarından, tüm glob desenlerini devralır.", + "exclude.boolean": "Dosya yollarının eşleştirileceği glob deseni. Deseni etkinleştirmek veya devre dışı bırakmak için true veya false olarak ayarlayın.", + "exclude.when": "Eşleşen bir dosyanın eşdüzey dosyalarında ek denetim. Eşleşen dosya adı için değişken olarak $(basename) kullanın.", + "useRipgrep": "Metin aramasında Ripgrep kullanılıp kullanılmayacağını denetler", + "useIgnoreFilesByDefault": "Yeni bir çalışma alanında arama yaparken .gitignore ve .ignore dosyalarının varsayılan olarak kullanılıp kullanılmayacağını denetler.", + "search.quickOpen.includeSymbols": "Dosya sonuçlarındaki bir global sembol aramasının sonuçlarının Hızlı Aç'a dahil edilip edilmeyeceğini yapılandırın." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/searchActions.i18n.json new file mode 100644 index 0000000000..253924f55e --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nextSearchIncludePattern": "Sonraki Aramaya Dahil Edilen Kalıbı Göster", + "previousSearchIncludePattern": "Önceki Aramaya Dahil Edilen Kalıbı Göster", + "nextSearchExcludePattern": "Sonraki Aramada Hariç Tutulan Kalıbı Göster", + "previousSearchExcludePattern": "Önceki Aramada Hariç Tutulan Kalıbı Göster", + "nextSearchTerm": "Sonraki Arama Terimini Göster", + "previousSearchTerm": "Önceki Arama Terimini Göster", + "focusNextInputBox": "Sonraki Girdi Kutusuna Odakla", + "focusPreviousInputBox": "Önceki Girdi Kutusuna Odakla", + "replaceInFiles": "Dosyalardakileri Değiştir", + "findInWorkspace": "Çalışma Alanında Bul...", + "findInFolder": "Klasörde Bul...", + "RefreshAction.label": "Yenile", + "ClearSearchResultsAction.label": "Arama Sonuçlarını Temizle", + "FocusNextSearchResult.label": "Sonraki Arama Sonucuna Odakla", + "FocusPreviousSearchResult.label": "Önceki Arama Sonucuna Odakla", + "RemoveAction.label": "Kaldır", + "file.replaceAll.label": "Tümünü Değiştir", + "match.replace.label": "Değiştir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json new file mode 100644 index 0000000000..f25d2be8ce --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/browser/searchResultsView.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "searchFolderMatch.other.label": "Diğer dosyalar", + "searchFileMatches": "{0} dosya bulundu", + "searchFileMatch": "{0} dosya bulundu", + "searchMatches": "{0} eşleşme bulundu", + "searchMatch": "{0} eşleşme bulundu", + "folderMatchAriaLabel": "{1} kök dizininde {0} eşleşme, Arama sonucu", + "fileMatchAriaLabel": "{2} klasöründeki {1} dosyasında {0} eşleşme, Arama sonucu", + "replacePreviewResultAria": "{3} metinli satırdaki {2}. sütunda {1} ile arama terimi {0}", + "searchResultAria": "{2} metinli satırdaki {1}. sütunda terim {0} bulundu" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json new file mode 100644 index 0000000000..03cc962b50 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/browser/searchViewlet.i18n.json @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "moreSearch": "Arama Detaylarını Aç/Kapat", + "searchScope.includes": "dahil edilecek dosyalar", + "label.includes": "Aramaya Dahil Edilen Kalıplar", + "searchScope.excludes": "hariç tutulacak klasörler", + "label.excludes": "Aramada Hariç Tutulan Kalıplar", + "replaceAll.confirmation.title": "Tümünü Değiştir", + "replaceAll.confirm.button": "Değiştir", + "replaceAll.occurrence.file.message": "{1} dosyadaki {0} tekrarlama '{2}' ile değiştirildi.", + "removeAll.occurrence.file.message": "{1} dosyadaki {0} tekrarlama değiştirildi.", + "replaceAll.occurrence.files.message": "{1} dosyadaki {0} tekrarlama '{2}' ile değiştirildi.", + "removeAll.occurrence.files.message": "{1} dosyadaki {0} tekrarlama değiştirildi.", + "replaceAll.occurrences.file.message": "{1} dosyadaki {0} tekrarlama '{2}' ile değiştirildi.", + "removeAll.occurrences.file.message": "{1} dosyadaki {0} tekrarlama değiştirildi.", + "replaceAll.occurrences.files.message": "{1} dosyadaki {0} tekrarlama '{2}' ile değiştirildi.", + "removeAll.occurrences.files.message": "{1} dosyadaki {0} tekrarlama değiştirildi.", + "removeAll.occurrence.file.confirmation.message": "{1} dosyadaki {0} tekralama '{2}' ile değiştirilsin mi?", + "replaceAll.occurrence.file.confirmation.message": "{1} dosyadaki {0} tekrarlama değiştirilsin mi?", + "removeAll.occurrence.files.confirmation.message": "{1} dosyadaki {0} tekralama '{2}' ile değiştirilsin mi?", + "replaceAll.occurrence.files.confirmation.message": "{1} dosyadaki {0} tekrarlama değiştirilsin mi?", + "removeAll.occurrences.file.confirmation.message": "{1} dosyadaki {0} tekralama '{2}' ile değiştirilsin mi?", + "replaceAll.occurrences.file.confirmation.message": "{1} dosyadaki {0} tekrarlama değiştirilsin mi?", + "removeAll.occurrences.files.confirmation.message": "{1} dosyadaki {0} tekralama '{2}' ile değiştirilsin mi?", + "replaceAll.occurrences.files.confirmation.message": "{1} dosyadaki {0} tekrarlama değiştirilsin mi?", + "treeAriaLabel": "Arama Sonuçları", + "searchPathNotFoundError": "Arama yolu bulunamadı: {0}", + "searchMaxResultsWarning": "Sonuç kümesi yalnızca tüm eşleşmelerin bir alt kümesini içerir. Lütfen sonuçları daraltmak için aramanızda daha fazla ayrıntı belirtin.", + "searchCanceled": "Arama, hiçbir sonuç bulunamadan iptal edildi - ", + "noResultsIncludesExcludes": "'{0}' içinde '{1}' hariç tutularak sonuç bulunamadı - ", + "noResultsIncludes": "'{0}' içinde sonuç bulunamadı - ", + "noResultsExcludes": "'{0}' hariç tutularak sonuç bulunamadı - ", + "noResultsFound": "Sonuç bulunamadı. Yapılandırılan hariç tutmalar ve yok sayma dosyaları için ayarlarınızı gözden geçirin - ", + "rerunSearch.message": "Yeniden ara", + "rerunSearchInAll.message": "Tüm dosyalarda yeniden ara", + "openSettings.message": "Ayarları Aç", + "openSettings.learnMore": "Daha Fazla Bilgi Edin", + "ariaSearchResultsStatus": "Arama ile {1} dosyada {0} sonuç bulundu", + "search.file.result": "{1} dosyada {0} sonuç", + "search.files.result": "{1} dosyada {0} sonuç", + "search.file.results": "{1} dosyada {0} sonuç", + "search.files.results": "{1} dosyada {0} sonuç", + "searchWithoutFolder": "Henüz bir klasör açmadınız. Şu an sadece açık dosyalar aranıyor - ", + "openFolder": "Klasör Aç" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/searchWidget.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/searchWidget.i18n.json new file mode 100644 index 0000000000..1d6d401fd7 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/browser/searchWidget.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.action.replaceAll.disabled.label": "Tümünü Değiştir (Etkinleştirmek İçin Aramayı Gönderin)", + "search.action.replaceAll.enabled.label": "Tümünü Değiştir", + "search.replace.toggle.button.title": "Değiştirmeyi Aç/Kapat", + "label.Search": "Ara: Arama Terimi girin ve aramak için Enter'a, iptal etmek için Escape tuşuna basın", + "search.placeHolder": "Ara", + "label.Replace": "Değiştir: Değiştirme terimini girin ve önizlemek için Enter'a, iptal etmek için Escape tuşuna basın", + "search.replace.placeHolder": "Değiştir", + "regexp.validationFailure": "İfade her öğe ile eşleşiyor" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/common/queryBuilder.i18n.json b/i18n/trk/src/vs/workbench/parts/search/common/queryBuilder.i18n.json new file mode 100644 index 0000000000..509f53c2ae --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/search/common/queryBuilder.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "search.noWorkspaceWithName": "Çalışma alanında belirtilen isimde klasör yok: {0}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json new file mode 100644 index 0000000000..ca9ac403e0 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.snippets": "Parçacıklara ekleme yapar.", + "vscode.extension.contributes.snippets-language": "Bu parçacığın ekleneceği dilin tanımlayıcısı.", + "vscode.extension.contributes.snippets-path": "Parçacıklar dosyasının yolu. Yol, eklenti klasörüne görecelidir ve genellikle './snippets/' ile başlar.", + "invalid.language": "`contributes.{0}.language` ögesinde bilinmeyen dil. Sağlanan değer: {1}", + "invalid.path.0": "`contributes.{0}.path` ögesinde dize bekleniyor. Sağlanan değer: {1}", + "invalid.path.1": "`contributes.{0}.path` ögesinin ({1}) eklentinin klasöründe ({2}) yer alması bekleniyor. Bu, eklentiyi taşınamaz yapabilir.", + "badVariableUse": "\"{0}\"-parçacığı yüksek olasılıkla parçacık değişkenleri ile parçacık yer tutucularını karıştırıyor. Daha fazla bilgi için https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax adresini ziyaret edin." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json new file mode 100644 index 0000000000..0fcf7d47b7 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "snippet.suggestions.label": "Parçacık Ekle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json new file mode 100644 index 0000000000..e6511f715e --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "openSnippet.pickLanguage": "Parçacık için Dil seçin", + "openSnippet.errorOnCreate": "{0} oluşturulamadı", + "openSnippet.label": "Kullanıcı Parçacıklarını Aç", + "preferences": "Tercihler", + "snippetSchema.json.default": "Boş parçacık", + "snippetSchema.json": "Kullanıcı parçacığı yapılandırması", + "snippetSchema.json.prefix": "Parçacığı IntelliSense'de seçerken kullanılacak ön ek", + "snippetSchema.json.body": "Parçacık içeriği. İmleç konumlarını tanımlamak için '$1', '${1:varsayilanMetin}' kullanın, en son imleç konumu için '$0' kullanın. Değişken değerlerini '${degiskenAdi}' ve '${degiskenAdi:varsayilanMetin}' ile ekleyin, ör. 'Bu bir dosyadır: $TM_FILENAME'.", + "snippetSchema.json.description": "Parçacık açıklaması." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json new file mode 100644 index 0000000000..8a5e1302c3 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "source.snippet": "Kullanıcı Parçacığı", + "detail.snippet": "{0} ({1})", + "snippetSuggest.longLabel": "{0}, {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json new file mode 100644 index 0000000000..ba53fcb278 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tabCompletion": "Ön ekleri eşleştiğinde parçacıkları ekleyin. 'quickSuggestions' etkinleştirilmediği zaman en iyi şekilde çalışır." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json new file mode 100644 index 0000000000..62d9f6bfb6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "helpUs": "{0} için hizmetimizi iyileştirmemize yardımcı olun", + "takeShortSurvey": "Kısa Ankete Katıl", + "remindLater": "Daha Sonra Hatırlat", + "neverAgain": "Tekrar Gösterme" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json new file mode 100644 index 0000000000..49862b2bfa --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "surveyQuestion": "Hızlı bir geri bildirim anketine katılmak ister misiniz?", + "takeSurvey": "Ankete Katıl", + "remindLater": "Daha Sonra Hatırlat", + "neverAgain": "Tekrar Gösterme" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json new file mode 100644 index 0000000000..6109c455b6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/browser/buildQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Bir derleme görevinin adını girin", + "noTasksMatching": "Eşleşen görev yok", + "noTasksFound": "Derleme görevi bulunamadı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json new file mode 100644 index 0000000000..34a573f872 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/browser/quickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "entryAriaLabel": "{0}, görevler", + "recentlyUsed": "yakınlarda kullanılan görevler", + "configured": "yapılandırılmış görevler", + "detected": "algılanan görevler", + "customizeTask": "Görevi Yapılandır" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json new file mode 100644 index 0000000000..2af3d80033 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/browser/restartQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Yeniden başlatılacak görevin adını girin", + "noTasksMatching": "Eşleşen görev yok", + "noTasksFound": "Yeniden başlatılacak bir görev bulunamadı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json new file mode 100644 index 0000000000..383b0ea201 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/browser/taskQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Çalıştırılacak görevin adını girin", + "noTasksMatching": "Eşleşen görev yok", + "noTasksFound": "Görev bulunamadı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json new file mode 100644 index 0000000000..a64bda48e0 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/browser/terminateQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Sonlandırılacak görevin adını girin", + "noTasksMatching": "Eşleşen görev yok", + "noTasksFound": "Sonlandırılacak bir görev bulunamadı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json new file mode 100644 index 0000000000..e618edd545 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/browser/testQuickOpen.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksAriaLabel": "Bir test görevinin adını girin", + "noTasksMatching": "Eşleşen görev yok", + "noTasksFound": "Test görevi bulunamadı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json new file mode 100644 index 0000000000..10aa5633a7 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/common/taskConfiguration.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Uyarı: options.cwd dize türünde olmalıdır. {0} değeri yok sayıldı.\n", + "ConfigurationParser.noargs": "Hata: komut argümanları dizelerden oluşan bir dizi olmalıdır. Belirtilen değer:\n{0}", + "ConfigurationParser.noShell": "Uyarı: kabuk yapılandırması sadece görevler terminalde çalıştırılırken desteklenir.", + "ConfigurationParser.noName": "Hata: Kapsam bildiriminde Sorun Eşleştirici'nin bir adı olmalıdır:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Uyarı: tanımlanan sorun eşleştirici bilinmiyor. Desteklenen türler: dize | ProblemMatcher | (dize | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Hata: Geçersiz problemMatcher başvusu: {0}\n", + "ConfigurationParser.noTaskName": "Hata: görevler bir taskName özelliği belirtmelidir. Görev yok sayılacaktır.\n{0}\n", + "taskConfiguration.shellArgs": "Uyarı: '{0}' görevi bir kabuk komutudur ve komut adı veya argümanlarından biri kaçış karakteri içermeyen boşluklar içeriyor. Doğru komut satırı alıntılamasını sağlamak için lütfen argümanları komutlarla birleştirin.", + "taskConfiguration.noCommandOrDependsOn": "Hata: '{0}' görevi bir komut veya dependsOn özelliği belirtmiyor. Görev yok sayılacaktır. Görevin tanımı:\n{1}", + "taskConfiguration.noCommand": "Hata: '{0}' görevi bir komut tanımlamıyor. Görev yok sayılacaktır. Görevin tanımı:\n{1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json new file mode 100644 index 0000000000..517da8acdd --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskDefinition.description": "Gerçek görev türü", + "TaskDefinition.properties": "Görev türünün ek özellikleri", + "TaskTypeConfiguration.noType": "Görev türü yapılandırmasında gerekli olan 'taskType' özelliği eksik", + "TaskDefinitionExtPoint": "Görev türlerine ekleme yapar" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json new file mode 100644 index 0000000000..828ec5e341 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/common/taskTemplates.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "dotnetCore": ".NET Core derleme komutu çalıştırır", + "msbuild": "Derleme hedefini çalıştırır", + "externalCommand": "İsteğe bağlı bir harici komut çalıştırma örneği", + "Maven": "Yaygın maven komutlarını çalıştırır" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json new file mode 100644 index 0000000000..eec828de0e --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/common/taskTypeRegistry.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json new file mode 100644 index 0000000000..5be493535c --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.i18n.json @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.options": "Ek komut seçenekleri", + "JsonSchema.options.cwd": "Çalıştırılan program veya betiğin geçerli çalışma klasörü. Atlanırsa Code'un geçerli çalışma alanının kök dizini kullanılır.", + "JsonSchema.options.env": "Çalıştırılan program veya kabuğun ortamı. Atlanırsa üst işlemin ortamı kullanılır.", + "JsonSchema.shellConfiguration": "Kullanılacak kabuğu özelleştirir.", + "JsonSchema.shell.executable": "Kullanılacak kabuk.", + "JsonSchema.shell.args": "Kabuk argümanları.", + "JsonSchema.command": "Çalıştırılacak komut. Harici bir program veya bir kabuk komutu olabilir.", + "JsonSchema.tasks.args": "Bu görev çağrıldığında, komuta iletilecek argümanlar.", + "JsonSchema.tasks.taskName": "Görevin adı", + "JsonSchema.tasks.windows": "Windows'a özel komut yapılandırması", + "JsonSchema.tasks.mac": "Mac'e özel komut yapılandırması", + "JsonSchema.tasks.linux": "Linux'a özel komut yapılandırması", + "JsonSchema.tasks.suppressTaskName": "Görev adının komuta argüman olarak eklenip eklenmeyeceğini denetler. Atlanırsa global olarak tanımlanan değer kullanılır.", + "JsonSchema.tasks.showOutput": "Çalışan görev çıktısının görünüp görünmeyeceğini denetler. Atlanırsa global olarak tanımlanan değer kullanılır.", + "JsonSchema.echoCommand": "Çalıştırılan komutun çıktıya yazdırılıp yazdırılmayacağını denetler. Varsayılan olarak kapalıdır.", + "JsonSchema.tasks.watching.deprecation": "Kullanım dışı. Bunun yerine isBackground ögesini kullanın.", + "JsonSchema.tasks.watching": "Çalıştırılan görevin etkin tutulup tutulmadığı ve dosya sistemini izleyip izlemediği.", + "JsonSchema.tasks.background": "Çalıştırılan görevin etkin tutulup tutulmadığı ve arka planda çalışıp çalışmadığı.", + "JsonSchema.tasks.promptOnClose": "VS Code'un çalışan bir görevle kapatılırken kullanıcının uyarılıp uyarılmayacağı.", + "JsonSchema.tasks.build": "Bu görevi, Code'un varsayılan derleme komutuna eşler.", + "JsonSchema.tasks.test": "Bu görevi, Code'un varsayılan test komutuna eşler.", + "JsonSchema.tasks.matchers": "Kullanılacak problem eşleştirici(leri). Bir dize veya bir problem eşleştirici tanımı veya bir dize ve problem eşleştiricileri dizisi.", + "JsonSchema.args": "Komuta iletilecek ek argümanlar.", + "JsonSchema.showOutput": "Çalışan görev çıktısının görünüp görünmeyeceğini denetler. Atlanırsa \"daima\" olarak varsayılır.", + "JsonSchema.watching.deprecation": "Kullanım dışı. Bunun yerine isBackground ögesini kullanın.", + "JsonSchema.watching": "Çalıştırılan görevin etkin tutulup tutulmadığı ve dosya sistemini izleyip izlemediği.", + "JsonSchema.background": "Çalıştırılan görevin etkin tutulup tutulmadığı ve arka planda çalışıp çalışmadığı.", + "JsonSchema.promptOnClose": "VS Code'un arka planda çalışan bir görevle kapatılırken kullanıcının uyarılıp uyarılmayacağı.", + "JsonSchema.suppressTaskName": "Görev adının komuta argüman olarak eklenip eklenmeyeceğini denetler. Varsayılan olarak kapalıdır.", + "JsonSchema.taskSelector": "Bir argümanın, görev olduğunu gösterecek ön ek.", + "JsonSchema.matchers": "Kullanılacak problem eşleştirici(leri). Bir dize veya bir problem eşleştirici tanımı veya bir dize ve problem eşleştiricileri dizisi.", + "JsonSchema.tasks": "Görev yapılandırmaları. Genellikle harici görev çalıştırıcısında tanımlı görevin zenginleştirilmesidir." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json new file mode 100644 index 0000000000..ca52884be4 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.version": "Yapılandırmanın sürüm numarası.", + "JsonSchema._runner": "\"runner\" görevini tamamladı. Resmi runner özelliğini kullanın", + "JsonSchema.runner": "Görevin bir işlem olarak çalıştırılıp çalıştırılmayacağını ve çıktının, çıktı penceresinde veya terminalin içinde gösterilip gösterilmeyeceğini denetler.", + "JsonSchema.windows": "Windows'a özel komut yapılandırması", + "JsonSchema.mac": "Mac'e özel komut yapılandırması", + "JsonSchema.linux": "Linux'a özel komut yapılandırması", + "JsonSchema.shell": "Komutun bir kabuk komutu veya harici bir program olup olmadığını belirtir. Atlanırsa hayır olarak kabul edilir." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json new file mode 100644 index 0000000000..6d9407e796 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.i18n.json @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "JsonSchema.shell": "Komutun bir kabuk komutu veya harici bir program olup olmadığını belirtir. Atlanırsa hayır olarak kabul edilir.", + "JsonSchema.tasks.isShellCommand.deprecated": "isShellCommand özelliği kullanım dışıdır. Bunun yerine görevin 'type' özelliğini ve 'options' özelliğindeki 'shell' özelliğini kullanın. Ayrıca 1.14 sürüm notlarına bakın.", + "JsonSchema.tasks.dependsOn.string": "Bu görevin bağlı olduğu başka bir görev.", + "JsonSchema.tasks.dependsOn.array": "Bu görevin bağlı olduğu diğer görevler.", + "JsonSchema.tasks.presentation": "Görevin çıktısını gösterip, girdisini okumak için kullanılacak paneli yapılandırır.", + "JsonSchema.tasks.presentation.echo": "Çalıştırılan komutun panele yazdırılıp yazdırılmayacağını denetler. Varsayılan olarak açıktır.", + "JsonSchema.tasks.presentation.focus": "Panelin, kendisine odaklanmaya izin verip vermeyeceğini denetler. Varsayılan olarak kapalıdır. Eğer \"true\" olarak ayarlanırsa panel de açığa çıkar.", + "JsonSchema.tasks.presentation.reveal.always": "Bu görev yürütülürken terminali her zaman ortaya çıkarır.", + "JsonSchema.tasks.presentation.reveal.silent": "Sadece hiçbir sorun eşleştiricisi görevle ilişkilendirilmemiş ve görev çalıştırılırken hata oluşuyorsa terminali ortaya çıkar.", + "JsonSchema.tasks.presentation.reveal.never": "Bu görev yürütülürken terminali hiçbir zaman ortaya çıkarmaz.", + "JsonSchema.tasks.presentation.reveals": "Panelin çalıştırdığı görevin ortaya çıkarılıp çıkarılmayacağını denetler. Varsayılan \"always\"tir.", + "JsonSchema.tasks.presentation.instance": "Panelin görevler arasında paylaşılacağı mı, bu göreve mi tahsis edileceği yoksa her çalıştırmada yeni bir panel mi oluşturulacağını denetler.", + "JsonSchema.tasks.terminal": "'terminal' özelliği kullanım dışıdır. Bunun yerine 'presentation' özelliğini kullanın.", + "JsonSchema.tasks.group.kind": "Görevin yürütme grubu.", + "JsonSchema.tasks.group.isDefault": "Bu görevin, gruptaki varsayılan görev olup olmadığını tanımlar.", + "JsonSchema.tasks.group.defaultBuild": "Bu görevi varsayılan derleme görevi olarak işaretler.", + "JsonSchema.tasks.group.defaultTest": "Bu görevi varsayılan test görevi olarak işaretler.", + "JsonSchema.tasks.group.build": "Görevleri 'Derleme Görevini Çalıştır' komutu ile ulaşılabilecek şekilde bir derleme görevi olarak işaretler.", + "JsonSchema.tasks.group.test": "Görevleri 'Test Görevini Çalıştır' komutu ile ulaşılabilecek şekilde bir test görevi olarak işaretler.", + "JsonSchema.tasks.group.none": "Görevi grupsuz olarak atar", + "JsonSchema.tasks.group": "Bu görevin ait olduğu çalıştırma grubunu tanımlar. Derleme grubuna eklemek için \"build\"ı ve test grubuna eklemek için \"test\"i destekler.", + "JsonSchema.tasks.type": "Görevin bir işlem olarak veya bir kabukta komut olarak çalıştırılıp çalıştırılmayacağını tanımlar. Varsayılan işlem olarak çalıştırmaktır.", + "JsonSchema.version": "Yapılandırmanın sürüm numarası.", + "JsonSchema.tasks.identifier": "launch.json veya dependsOn maddesindeki göreve atıfta başvuracak, kullanıcı tanımlı bir tanımlayıcı.", + "JsonSchema.tasks.taskLabel": "Görevin etiketi", + "JsonSchema.tasks.taskName": "Görevin adı", + "JsonSchema.tasks.taskName.deprecated": "Görevin 'name' özelliği kullanım dışıdır. Bunun yerine label özelliğini kullanın.", + "JsonSchema.tasks.background": "Çalıştırılan görevin etkin tutulup tutulmadığı ve arka planda çalışıp çalışmadığı.", + "JsonSchema.tasks.promptOnClose": "VS Code'un çalışan bir görevle kapatılırken kullanıcının uyarılıp uyarılmayacağı.", + "JsonSchema.tasks.matchers": "Kullanılacak problem eşleştirici(leri). Bir dize veya bir problem eşleştirici tanımı veya bir dize ve problem eşleştiricileri dizisi.", + "JsonSchema.customizations.customizes.type": "Özelleştirilecek görev türü", + "JsonSchema.tasks.customize.deprecated": "customize özelliği kullanım dışıdır. Yeni görev özelleştirme yaklaşımına nasıl geçiş yapılacağı için 1.14 sürüm notlarına bakın", + "JsonSchema.tasks.showOputput.deprecated": "showOutput özelliği kullanım dışıdır. Bunun yerine presentation özelliğinin içindeki reveal özelliğini kullanın. Ayrıca 1.14 sürüm notlarına bakın.", + "JsonSchema.tasks.echoCommand.deprecated": "'echoCommand' özelliği kullanım dışıdır. Bunun yerine 'presentation' özelliğinin içindeki 'echo' özelliğini kullanın. Ayrıca 1.14 sürüm notlarına bakın.", + "JsonSchema.tasks.suppressTaskName.deprecated": "'suppressTaskName' özelliği kullanım dışıdır. Bunun yerine, komutu argümanlarıyla görevin içine ekleyin. Ayrıca 1.14 sürüm notlarına bakın.", + "JsonSchema.tasks.isBuildCommand.deprecated": "'isBuildCommand' özelliği kullanım dışıdır. Bunun yerine 'group' özelliğini kullanın. Ayrıca 1.14 sürüm notlarına bakın.", + "JsonSchema.tasks.isTestCommand.deprecated": "'isTestCommand' özelliği kullanım dışıdır. Bunun yerine 'group' özelliğini kullanın. Ayrıca 1.14 sürüm notlarına bakın.", + "JsonSchema.tasks.taskSelector.deprecated": "'taskSelector' özelliği kullanım dışıdır. Bunun yerine, komutu argümanlarıyla görevin içine ekleyin. Ayrıca 1.14 sürüm notlarına bakın.", + "JsonSchema.windows": "Windows'a özel komut yapılandırması", + "JsonSchema.mac": "Mac'e özel komut yapılandırması", + "JsonSchema.linux": "Linux'a özel komut yapılandırması" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json new file mode 100644 index 0000000000..9c8f13c0e6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasksCategory": "Görevler", + "ConfigureTaskRunnerAction.noWorkspace": "Görevler, sadece çalışma alanı klasöründe mevcuttur.", + "ConfigureTaskRunnerAction.quickPick.template": "Bir Görev Çalıştırıcısı Seç", + "ConfigureTaskRunnerAction.autoDetecting": "{0} görevleri otomatik algılanıyor", + "ConfigureTaskRunnerAction.autoDetect": "Görev sisteminin otomatik algılanması başarısız oldu. Varsayılan şablon kullanılıyor. Ayrıntılar için görev çıktısına bakın.", + "ConfigureTaskRunnerAction.autoDetectError": "Görev sisteminin otomatik algılanması sırasında hatalar oluştu. Ayrıntılar için görev çıktısına bakın.", + "ConfigureTaskRunnerAction.failed": " '.vscode' klasörü içinde 'tasks.json' dosyası oluşturulamıyor. Ayrıntılar için görev çıktısına bakın.", + "ConfigureTaskRunnerAction.label": "Görev Çalıştırıcısını Yapılandır", + "ConfigureBuildTaskAction.label": "Derleme Görevini Yapılandır", + "CloseMessageAction.label": "Kapat", + "ShowTerminalAction.label": "Terminali Görüntüle", + "problems": "Sorunlar", + "manyMarkers": "99+", + "runningTasks": "Çalışan Görevleri Göster", + "tasks": "Görevler", + "TaskSystem.noHotSwap": "Görev yürütme motorunu değiştirmek pencereyi yeniden yüklemeyi gerektirir", + "TaskService.noBuildTask1": "Derleme görevi tanımlanmamış. tasks.json dosyasındaki bir görevi 'isBuildCommand' ile işaretleyin.", + "TaskService.noBuildTask2": "Derleme görevi tanımlanmamış. tasks.json dosyasındaki bir görevi, bir 'build' grubu olarak işaretleyin.", + "TaskService.noTestTask1": "Test görevi tanımlanmamış. tasks.json dosyasındaki bir testi 'isTestCommand' ile işaretleyin.", + "TaskService.noTestTask2": "Test görevi tanımlanmamış. tasks.json dosyasındaki bir görevi, bir 'test' grubu olarak işaretleyin.", + "TaskServer.noTask": " Çalıştırılmak istenen {0} görevi bulunamadı.", + "TaskService.attachProblemMatcher.continueWithout": "Görev çıktısını taramadan devam et", + "TaskService.attachProblemMatcher.never": "Hiçbir zaman görev çıktısını tarama", + "TaskService.attachProblemMatcher.learnMoreAbout": "Görev çıktısını tarama hakkında daha fazla bilgi edin", + "selectProblemMatcher": "Görev çıktısında taranacak hata ve uyarı türlerini seçin", + "customizeParseErrors": "Geçerli görev yapılandırmasında hatalar var. Lütfen, bir görevi özelleştirmeden önce ilk olarak hataları düzeltin.", + "moreThanOneBuildTask": "tasks.json dosyasında tanımlı çok fazla derleme görevi var. İlk görev çalıştırılıyor.", + "TaskSystem.activeSame.background": " '{0}' görevi zaten aktif ve arka plan modunda. Görevi sonlandırmak için Görevler menüsünden `Görevi Sonlandır...`ı kullanın.", + "TaskSystem.activeSame.noBackground": " '{0}' görevi zaten aktif. Görevi sonlandırmak için Görevler menüsünden `Görevi Sonlandır...`ı kullanın.", + "TaskSystem.active": "Çalışan bir görev zaten var. Bir başkasını çalıştırmadan önce bu görevi sonlandırın.", + "TaskSystem.restartFailed": "{0} görevini sonlandırma ve yeniden başlatma başarısız oldu", + "TaskSystem.configurationErrors": "Hata: belirtilen görev yapılandırmasında doğrulama hataları var ve kullanılamıyor. Lütfen ilk olarak hataları düzeltin.", + "TaskSystem.invalidTaskJson": "Hata: tasks.json dosyasının içeriğinde sentaks hataları var. Lütfen, bir görevi çalıştırmadan önce hataları düzeltin.\n", + "TaskSystem.runningTask": "Çalışan bir görev var. Bu görevi sonlandırmak istiyor musunuz?", + "TaskSystem.terminateTask": "&&Görevi Sonlandır", + "TaskSystem.noProcess": "Başlatılan görev artık mevcut değil. Eğer görev arka plan işlemleri oluşturduysa, VS Code'dan çıkmak işlemlerin sahipsiz kalmasına neden olabilir. Bunu önlemek için son arka plan işlemini bekle işaretçisiyle başlatın.", + "TaskSystem.exitAnyways": "Yine de &&Çık", + "TerminateAction.label": "Görevi Sonlandır", + "TaskSystem.unknownError": "Bir görev çalıştırılırken hata oluştu. Detaylar için görev günlüğüne bakın.", + "TaskService.noWorkspace": "Görevler, sadece çalışma alanı klasöründe mevcuttur.", + "recentlyUsed": "yakınlarda kullanılan görevler", + "configured": "yapılandırılmış görevler", + "detected": "algılanan görevler", + "TaskService.fetchingBuildTasks": "Derleme görevleri alınıyor...", + "TaskService.noBuildTaskTerminal": "Derleme görevi bulunamadı. Yeni bir tane tanımlamak için 'Derleme Görevini Yapılandır'a basın.", + "TaskService.pickBuildTask": "Çalıştırılacak derleme görevini seçin", + "TaskService.fetchingTestTasks": "Test görevleri alınıyor...", + "TaskService.noTestTaskTerminal": "Test görevi bulunamadı. Yeni bir tane tanımlamak için 'Görev Çalıştırıcısını Yapılandır'a basın.", + "TaskService.pickTestTask": "Çalıştırılacak test görevini seçin", + "TaskService.noTaskRunning": "Şu an çalışan bir görev yok.", + "TaskService.tastToTerminate": "Sonlandırılacak görevi seçin", + "TerminateAction.noProcess": "Başlatılan işlem artık mevcut değil. Eğer görev arka plan görevleri oluşturduysa, VS Code'dan çıkmak işlemlerin sahipsiz kalmasına neden olabilir.", + "TerminateAction.failed": "Çalışan görevi sonlandırma başarısız oldu.", + "TaskService.noTaskToRestart": "Yeniden başlatılacak bir görev yok.", + "TaskService.tastToRestart": "Yeniden başlatılacak görevi seçin", + "TaskService.defaultBuildTaskExists": "{0} zaten varsayılan derleme görevi olarak işaretlenmiş.", + "TaskService.pickDefaultBuildTask": "Varsayılan derleme görevi olarak kullanılacak görevi seçin", + "TaskService.defaultTestTaskExists": "{0} zaten varsayılan test görevi olarak işaretlenmiş.", + "TaskService.pickDefaultTestTask": "Varsayılan test görevi olarak kullanılacak görevi seçin", + "TaskService.noTaskIsRunning": "Çalışan bir görev yok.", + "TaskService.pickShowTask": "Çıktısını göstermek için görev seçin", + "ShowLogAction.label": "Görev Günlüğünü Göster", + "RunTaskAction.label": "Görevi Çalıştır", + "RestartTaskAction.label": "Çalışan Görevi Yeniden Başlat", + "ShowTasksAction.label": "Çalışan Görevleri Göster", + "BuildAction.label": "Derleme Görevini Çalıştır", + "TestAction.label": "Test Görevini Çalıştır", + "ConfigureDefaultBuildTask.label": "Varsayılan Derleme Görevini Yapılandır", + "ConfigureDefaultTestTask.label": "Varsayılan Test Görevini Yapılandır", + "quickOpen.task": "Görevi Çalıştır" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json new file mode 100644 index 0000000000..eb7b1d5ecc --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/taskPanel.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "tasks": "Görevler" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json new file mode 100644 index 0000000000..64c0a2ea24 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TerminalTaskSystem.unknownError": "Görev çalıştırılırken bir hata oluştu. Detaylar için görev çıktısı günlüğüne bakın.", + "TerminalTaskSystem.terminalName": "Görev - {0}", + "reuseTerminal": "Terminal görevler tarafından tekrar kullanılacak, kapatmak için herhangi bir tuşa basın.", + "TerminalTaskSystem": "UNC sürücüsünde kabuk komutu yürütülemez.", + "unkownProblemMatcher": "{0} sorun eşleştirici çözümlenemiyor. Eşleştirici yok sayılacaktır." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json new file mode 100644 index 0000000000..1142dbe409 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/node/processRunnerDetector.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskSystemDetector.noGulpTasks": "gulp --tasks-simple komutu çalıştırıldığında herhangi bir görev listelemedi. npm install komutunu çalıştırdınız mı?", + "TaskSystemDetector.noJakeTasks": "jake --tasks komutu çalıştırıldığında herhangi bir görev listelemedi. npm install komutunu çalıştırdınız mı?", + "TaskSystemDetector.noGulpProgram": "Gulp, sisteminizde yüklü değil. Yüklemek için npm install -g gulp komutunu çalıştırın.", + "TaskSystemDetector.noJakeProgram": "Jake, sisteminizde yüklü değil. Yüklemek için npm install -g jake komutunu çalıştırın.", + "TaskSystemDetector.noGruntProgram": "Grunt, sisteminizde yüklü değil. Yüklemek için npm install -g grunt komutunu çalıştırın.", + "TaskSystemDetector.noProgram": "{0} programı bulunamadı. Mesaj: {1}", + "TaskSystemDetector.buildTaskDetected": "'{0}' olarak adlandırılmış derleme görevi algılandı.", + "TaskSystemDetector.testTaskDetected": "'{0}' olarak adlandırılmış test görevi algılandı." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json new file mode 100644 index 0000000000..e727933c63 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/node/processTaskSystem.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "TaskRunnerSystem.unknownError": "Görev çalıştırılırken bir hata oluştu. Detaylar için görev çıktısı günlüğüne bakın.", + "TaskRunnerSystem.watchingBuildTaskFinished": "\nDerleme görevlerinin izlenmesi bitti.", + "TaskRunnerSystem.childProcessError": "Harici program {0} {1} başlatılamadı.", + "TaskRunnerSystem.cancelRequested": "\n'{0}' görevi kullanıcı isteği üzerine sonlandırıldı.", + "unkownProblemMatcher": "{0} sorun eşleştirici çözümlenemiyor. Eşleştirici yok sayılacaktır." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json new file mode 100644 index 0000000000..f0ab8579a0 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/tasks/node/taskConfiguration.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "ConfigurationParser.invalidCWD": "Uyarı: options.cwd dize türünde olmalıdır. {0} değeri yok sayıldı.\n", + "ConfigurationParser.noargs": "Hata: komut argümanları dizelerden oluşan bir dizi olmalıdır. Belirtilen değer:\n{0}", + "ConfigurationParser.noShell": "Uyarı: kabuk yapılandırması sadece görevler terminalde çalıştırılırken desteklenir.", + "ConfigurationParser.noName": "Hata: Kapsam bildiriminde Sorun Eşleştirici'nin bir adı olmalıdır:\n{0}\n", + "ConfigurationParser.unknownMatcherKind": "Uyarı: tanımlanan sorun eşleştirici bilinmiyor. Desteklenen türler: dize | ProblemMatcher | (dize | ProblemMatcher)[].\n{0}\n", + "ConfigurationParser.invalidVaraibleReference": "Hata: Geçersiz problemMatcher başvusu: {0}\n", + "ConfigurationParser.noTaskType": "Hata: 'tasks' yapılandırması bir 'type' özelliğine sahip olmalıdır. Yapılandırma yok sayılacaktır.\n{0}\n", + "ConfigurationParser.noTypeDefinition": "Hata: '{0}' olarak kayıtlı bir görev türü yok. İlgili görev sağlayıcısını içeren bir eklentiyi yüklemeyi mi unuttunuz?", + "ConfigurationParser.notCustom": "Hata: 'tasks' bir özel görev olarak tanımlanmamış. Yapılandırma yok sayılacaktır.\n{0}\n", + "ConfigurationParser.noTaskName": "Hata: 'tasks' bir 'taskName' özelliği belirtmelidir. Görev yok sayılacaktır.\n{0}\n", + "taskConfiguration.shellArgs": "Uyarı: '{0}' görevi bir kabuk komutudur ve komut adı veya argümanlarından biri kaçış karakteri içermeyen boşluklar içeriyor. Doğru komut satırı alıntılamasını sağlamak için lütfen argümanları komutlarla birleştirin.", + "taskConfiguration.noCommandOrDependsOn": "Hata: '{0}' görevi bir komut veya dependsOn özelliği belirtmiyor. Görev yok sayılacaktır. Görevin tanımı:\n{1}", + "taskConfiguration.noCommand": "Hata: '{0}' görevi bir komut tanımlamıyor. Görev yok sayılacaktır. Görevin tanımı:\n{1}", + "TaskParse.noOsSpecificGlobalTasks": "2.0.0 görev sürümü genel işletim sistemi özel görevlerini desteklemiyor. Bunları işletim sistemine özel komut içeren bir göreve çevirin. Etkilenen görevler:\n{0}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json new file mode 100644 index 0000000000..ba086faa53 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "termEntryAriaLabel": "{0}, terminal seçici", + "termCreateEntryAriaLabel": "{0}, yeni terminal oluştur", + "'workbench.action.terminal.newplus": "$(plus) Yeni Entegre Terminal Oluştur", + "noTerminalsMatching": "Eşleşen terminal yok", + "noTerminalsFound": "Açık terminal yok" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json new file mode 100644 index 0000000000..f49421b02c --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "quickOpen.terminal": "Tüm Açık Terminalleri Göster", + "terminalIntegratedConfigurationTitle": "Entegre Terminal", + "terminal.integrated.shell.linux": "Terminalin Linux'da kullandığı kabuğun yolu.", + "terminal.integrated.shellArgs.linux": "Linux terminalindeyken kullanılacak komut satırı argümanları.", + "terminal.integrated.shell.osx": "Terminalin OS X'de kullandığı kabuğun yolu.", + "terminal.integrated.shellArgs.osx": "OS X terminalindeyken kullanılacak komut satırı argümanları.", + "terminal.integrated.shell.windows": "Terminalin Windows'da kullandığı kabuğun yolu. Windows ile birlikte gelen kabukları kullanırken (cmd, PowerShell veya Bash on Ubuntu) uygulanır.", + "terminal.integrated.shellArgs.windows": "Windows terminalindeyken kullanılacak komut satırı argümanları.", + "terminal.integrated.rightClickCopyPaste": "Ayarlandığında, terminal içinde sağ tıklandığında bağlam menüsünün görünmesini engeller, onun yerine bir seçim varsa kopyalama yapar, bir seçim yoksa yapıştırma yapar.", + "terminal.integrated.fontFamily": "Terminalin yazı tipi ailesini denetler; bu, varsayılan olarak editor.fontFamily'nin değeridir.", + "terminal.integrated.fontLigatures": "Terminalde yazı tipi ligatürlerinin etkinleştirilip etkinleştirilmeyeceğini denetler.", + "terminal.integrated.fontSize": "Terminaldeki yazı tipi boyutunu piksel olarak denetler.", + "terminal.integrated.lineHeight": "Terminalin satır yüksekliğini denetler, bu sayı gerçek satır yüksekliğini piksel olarak elde etmek için terminal yazı tipi boyutu ile çarpılır.", + "terminal.integrated.enableBold": "Terminalde kalın yazının etkinleştirilip etkinleştirilmeyeceği; bu, terminal kabuğunun desteğini gerektirir.", + "terminal.integrated.cursorBlinking": "Terminaldeki imlecin yanıp sönmesini denetler.", + "terminal.integrated.cursorStyle": "Terminaldeki imlecin stilini denetler.", + "terminal.integrated.scrollback": "Terminalin tamponunda tuttuğu maksimum satır sayısını denetler.", + "terminal.integrated.setLocaleVariables": "Terminal başlangıcında yereli içeren değişkenlerin ayarlanıp ayarlanmayacağını denetler; bu, OS X'de varsayılan olarak açıktır, diğer platformlarda kapalıdır.", + "terminal.integrated.cwd": "Terminalin nerede başlatılacağına ait açık bir yol; bu, kabuk işlemleri için geçerli çalışma klasörü (cwd) olarak kullanılır. Bu, çalışma alanı ayarlarında kök dizini uygun bir cwd değilse özellikle yararlı olabilir.", + "terminal.integrated.confirmOnExit": "Aktif terminal oturumları varken çıkışta onay istenip istenmeyeceği.", + "terminal.integrated.commandsToSkipShell": "Tuş bağlarının kabuğa gönderilmeyip bunun yerine her zaman Code tarafından işleneceği bir komut ID'leri kümesi. Bu, tuş bağlarının normalde kabuk tarafından terminal odakta değilken nasılsa öyle davranmasını sağlar, örnek olarak Hızlı Aç'ı başlatmak için ctrl+p.", + "terminal.integrated.env.osx": "OS X'deki terminal tarafından kullanılacak VS Code işlemine eklenecek ortam değişkenlerini içeren nesne", + "terminal.integrated.env.linux": "Linux'daki terminal tarafından kullanılacak VS Code işlemine eklenecek ortam değişkenlerini içeren nesne", + "terminal.integrated.env.windows": "Windows'daki terminal tarafından kullanılacak VS Code işlemine eklenecek ortam değişkenlerini içeren nesne", + "terminal": "Terminal", + "terminalCategory": "Terminal", + "viewCategory": "Görüntüle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json new file mode 100644 index 0000000000..33aa86adbe --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbench.action.terminal.toggleTerminal": "Entegre Terminali Aç/Kapat", + "workbench.action.terminal.kill": "Aktif Terminal Örneğini Sonlandır", + "workbench.action.terminal.kill.short": "Terminali Sonlandır", + "workbench.action.terminal.quickKill": "Terminal Örneğini Sonlandır", + "workbench.action.terminal.copySelection": "Seçimi Kopyala", + "workbench.action.terminal.selectAll": "Tümünü Seç", + "workbench.action.terminal.deleteWordLeft": "Soldaki Sözcüğü Sil", + "workbench.action.terminal.deleteWordRight": "Sağdaki Sözcüğü Sil", + "workbench.action.terminal.new": "Yeni Entegre Terminal Oluştur", + "workbench.action.terminal.new.short": "Yeni Terminal", + "workbench.action.terminal.focus": "Terminale Odakla", + "workbench.action.terminal.focusNext": "Sonraki Terminale Odakla", + "workbench.action.terminal.focusAtIndex": "{0}. Terminale Odakla", + "workbench.action.terminal.focusPrevious": "Önceki Terminale Odakla", + "workbench.action.terminal.paste": "Aktif Terminale Yapıştır", + "workbench.action.terminal.DefaultShell": "Varsayılan Kabuğu Seç", + "workbench.action.terminal.runSelectedText": "Seçili Metni Aktif Terminalde Çalıştır", + "workbench.action.terminal.runActiveFile": "Aktif Dosyayı Aktif Terminalde Çalıştır", + "workbench.action.terminal.runActiveFile.noFile": "Sadece diskteki dosyalar terminalde çalıştırılabilir", + "workbench.action.terminal.switchTerminalInstance": "Terminal Örneğini Değiştir", + "workbench.action.terminal.scrollDown": "Aşağı Kaydır (Satır)", + "workbench.action.terminal.scrollDownPage": "Aşağı Kaydır (Sayfa)", + "workbench.action.terminal.scrollToBottom": "En Alta Kaydır", + "workbench.action.terminal.scrollUp": "Yukarı Kaydır (Satır)", + "workbench.action.terminal.scrollUpPage": "Yukarı Kaydır (Sayfa)", + "workbench.action.terminal.scrollToTop": "En Üste Kaydır", + "workbench.action.terminal.clear": "Temizle", + "workbench.action.terminal.allowWorkspaceShell": "Çalışma Alanı Kabuk Yapılandırmasına İzin Ver", + "workbench.action.terminal.disallowWorkspaceShell": "Çalışma Alanı Kabuk Yapılandırmasına İzin Verme", + "workbench.action.terminal.rename": "Yeniden Adlandır", + "workbench.action.terminal.rename.prompt": "Terminal adını girin", + "workbench.action.terminal.focusFindWidget": "Bulma Aracına Odakla", + "workbench.action.terminal.hideFindWidget": "Bulma Aracını Gizle", + "nextTerminalFindTerm": "Sonraki Arama Terimini Göster", + "previousTerminalFindTerm": "Önceki Arama Terimini Göster", + "quickOpenTerm": "Terminal: Aktif Terminali Değiştir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json new file mode 100644 index 0000000000..3f5e4eb315 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.background": "Terminalin arka plan rengi; bu, terminalin panelden farklı olarak renklendirilmesini sağlar.", + "terminal.foreground": "Terminalin ön plan rengi.", + "terminalCursor.foreground": "Terminal imlecinin ön plan rengi.", + "terminalCursor.background": "Terminal imlecinin arka plan rengi. Bir blok imlecinin kapladığı bir karakterin rengini özelleştirmeyi sağlar.", + "terminal.ansiColor": "Terminalde '{0}' ANSI rengi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json new file mode 100644 index 0000000000..3050e0f65d --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.allowWorkspaceShell": "{0} ögesinin (çalışma alanı ayarı olarak tanımlı) terminalde başlatılmasına izin veriyor musunuz?", + "allow": "İzin Ver", + "disallow": "İzin Verme" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json new file mode 100644 index 0000000000..ef27920f7e --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "label.find": "Bul", + "placeholder.find": "Bul", + "label.previousMatchButton": "Önceki eşleşme", + "label.nextMatchButton": "Sonraki eşleşme", + "label.closeButton": "Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json new file mode 100644 index 0000000000..f817eb6e26 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.copySelection.noSelection": "Terminalde kopyalanacak bir seçim bulunmuyor", + "terminal.integrated.exitedWithCode": "Terminal işlemi şu çıkış koduyla sonlandı: {0}", + "terminal.integrated.waitOnExit": "Terminali kapatmak için lütfen bir tuşa basın", + "terminal.integrated.launchFailed": "Terminal işlem komutu `{0}{1}` başlatılamadı (çıkış kodu: {2})" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json new file mode 100644 index 0000000000..1f869a29bd --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminalLinkHandler.followLinkAlt": "Bağlantıyı izlemek için Alt tuşuna basarak tıklayın", + "terminalLinkHandler.followLinkCmd": "Bağlantıyı izlemek için Cmd tuşuna basarak tıklayın", + "terminalLinkHandler.followLinkCtrl": "Bağlantıyı izlemek için Ctrl tuşuna basarak tıklayın" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json new file mode 100644 index 0000000000..5f2eb118d9 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "copy": "Kopyala", + "createNewTerminal": "Yeni Terminal", + "paste": "Yapıştır", + "selectAll": "Tümünü Seç", + "clear": "Temizle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json new file mode 100644 index 0000000000..ea021e1dac --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "terminal.integrated.chooseWindowsShellInfo": "Özelleştir butonuna tıklayıp varsayılan terminal kabuğunu seçebilirsiniz.", + "customize": "Özelleştir", + "cancel": "İptal", + "never again": "Tamam, Tekrar Gösterme", + "terminal.integrated.chooseWindowsShell": "Tercih ettiğiniz terminal kabuğunu seçin, bunu daha sonra ayarlarınızdan değiştirebilirsiniz", + "terminalService.terminalCloseConfirmationSingular": "Aktif bir terminal oturumu var, sonlandırmak istiyor musunuz?", + "terminalService.terminalCloseConfirmationPlural": "{0} aktif terminal oturumu var, bunları sonlandırmak istiyor musunuz?", + "yes": "Evet" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json new file mode 100644 index 0000000000..7f646de9b6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/themes/electron-browser/themes.contribution.i18n.json @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "selectTheme.label": "Renk Teması", + "installColorThemes": "Ek Renk Temaları Yükle...", + "themes.selectTheme": "Bir Renk Teması Seç (Yukarı/Aşağı Tuşlarıyla Önizleme Yap)", + "selectIconTheme.label": "Dosya Simgesi Teması", + "installIconThemes": "Ek Dosya Simgesi Temaları Yükle...", + "noIconThemeLabel": "Hiçbiri", + "noIconThemeDesc": "Dosya simgelerini devre dışı bırak", + "problemChangingIconTheme": "Simge temasını ayarlama sorunu: {0}", + "themes.selectIconTheme": "Dosya Simgesi Teması Seç", + "generateColorTheme.label": "Geçerli Ayarlardan Renk Teması Oluştur", + "preferences": "Tercihler", + "developer": "Geliştirici" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json new file mode 100644 index 0000000000..3ed323c9c2 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "unsupportedWorkspaceSettings": "Bu çalışma alanı sadece Kullanıcı Ayarları'nda ayarlanabilen ayarlar içeriyor. ({0})", + "openWorkspaceSettings": "Çalışma Alanı Ayarlarını Aç", + "openDocumentation": "Daha Fazla Bilgi Edin", + "ignore": "Yok Say" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json b/i18n/trk/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json new file mode 100644 index 0000000000..8b30db8bad --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "releaseNotesInputName": "Sürüm Notları: {0}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json new file mode 100644 index 0000000000..af8b38bd06 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "release notes": "Sürüm notları", + "updateConfigurationTitle": "Güncelle", + "updateChannel": "Güncelleştirme kanalından otomatik güncelleştirmeler alıp almayacağınızı ayarlayın. Değişiklikten sonra yeniden başlatma gerektirir." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/trk/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 0000000000..411f0851a2 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "updateNow": "Şimdi Güncelle", + "later": "Daha Sonra", + "unassigned": "atanmamış", + "releaseNotes": "Sürüm Notları", + "showReleaseNotes": "Sürüm Notlarını Göster", + "downloadNow": "Şimdi İndir", + "read the release notes": "{0} v{1} uygulamasına hoş geldiniz! Sürüm Notları'nı okumak ister misiniz?", + "licenseChanged": "Lisans koşullarımız değişti, lütfen inceleyin.", + "license": "Lisansı Oku", + "neveragain": "Tekrar Gösterme", + "64bitisavailable": "64-bit Windows için {0} şu an mevcut!", + "learn more": "Daha Fazla Bilgi Edin", + "updateIsReady": "Yeni {0} güncellemesi var.", + "thereIsUpdateAvailable": "Bir güncelleştirme var.", + "updateAvailable": "{0} yeniden başlatıldıktan sonra güncellenecektir.", + "noUpdatesAvailable": "Şu anda mevcut herhangi bir güncelleme yok.", + "commandPalette": "Komut Paleti...", + "settings": "Ayarlar", + "keyboardShortcuts": "Klavye Kısayolları", + "selectTheme.label": "Renk Teması", + "themes.selectIconTheme.label": "Dosya Simgesi Teması", + "not available": "Güncelleştirme Yok", + "checkingForUpdates": "Güncelleştirmeler Denetleniyor...", + "DownloadUpdate": "Mevcut Güncelleştirmeyi İndir", + "DownloadingUpdate": "Güncelleştirme İndiriliyor...", + "InstallingUpdate": "Güncelleştirme Yükleniyor...", + "restartToUpdate": "Güncelleştirmek için Yeniden Başlatın...", + "checkForUpdates": "Güncelleştirmeleri Denetle..." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/views/browser/views.i18n.json b/i18n/trk/src/vs/workbench/parts/views/browser/views.i18n.json new file mode 100644 index 0000000000..d5b88d0e7a --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/views/browser/views.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} eylem", + "hideView": "Kenar Çubuğunda Gizle" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json b/i18n/trk/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json new file mode 100644 index 0000000000..44e6367a3b --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/views/browser/viewsExtensionPoint.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "requirearray": "görünümler bir dizi olmalıdır", + "requirestring": "`{0}` özelliği zorunludur ve `string` türünde olmalıdır", + "optstring": "`{0}` özelliği atlanabilir veya `string` türünde olmalıdır", + "vscode.extension.contributes.view.id": "Görünümün tanımlayıcısı. Bunu, `vscode.window.registerTreeDataProviderForView` API ile bir veri sağlayıcısı kaydetmek için kullanın. Ayrıca `onView:${id}` olayını `activationEvents` ögesine kaydederek eklentinizi etkinleştirmeyi tetikleyin.", + "vscode.extension.contributes.view.name": "Görünümün insanlar tarafından okunabilir adı. Gösterilecektir", + "vscode.extension.contributes.view.when": "Bu görünümü göstermek için doğru olması gereken koşul", + "vscode.extension.contributes.views": "Görünümleri düzenleyiciye ekler.", + "views.explorer": "Gezgin Görünümü", + "views.debug": "Hata Ayıklama Görünümü", + "locationId.invalid": "`{0}` geçerli bir görünüm konumu değil" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json b/i18n/trk/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json new file mode 100644 index 0000000000..3318c1338d --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/watermark/electron-browser/watermark.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "watermark.showCommands": "Tüm Komutları Göster", + "watermark.quickOpen": "Dosyaya Git", + "watermark.openFile": "Dosya Aç", + "watermark.openFolder": "Klasör Aç", + "watermark.openFileFolder": "Dosya veya Klasör Aç", + "watermark.openRecent": "Son Kullanılanları Aç", + "watermark.newUntitledFile": "Yeni İsimsiz Dosya", + "watermark.toggleTerminal": "Terminali Aç/Kapat", + "watermark.findInFiles": "Dosyalarda Bul", + "watermark.startDebugging": "Hata Ayıklamaya Başla", + "watermark.unboundCommand": "serbest", + "workbenchConfigurationTitle": "Çalışma Ekranı", + "tips.enabled": "Etkinleştirildiğinde, hiçbir düzenleyici açık değilken filigran ipuçları gösterir" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json b/i18n/trk/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json new file mode 100644 index 0000000000..4c8d7e3797 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.i18n.json @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomeOverlay.explorer": "Dosya gezgini", + "welcomeOverlay.search": "Dosyalar arasında ara", + "welcomeOverlay.git": "Kaynak kodu yönetimi", + "welcomeOverlay.debug": "Başlat ve hata ayıkla", + "welcomeOverlay.extensions": "Eklentileri yönet", + "welcomeOverlay.problems": "Hataları ve uyarıları görüntüle", + "welcomeOverlay.commandPalette": "Tüm komutları bul ve çalıştır", + "welcomeOverlay": "Kullanıcı Arayüzüne Genel Bakış", + "hideWelcomeOverlay": "Arayüz Genel Bakışını Gizle", + "help": "Yardım" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json new file mode 100644 index 0000000000..15c82ac84a --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage.vscode": "Visual Studio Code", + "welcomePage.editingEvolved": "Düzenleme evrim geçirdi", + "welcomePage.start": "Başlangıç", + "welcomePage.newFile": "Yeni dosya", + "welcomePage.openFolder": "Klasör aç...", + "welcomePage.cloneGitRepository": "Git deposu kopyala...", + "welcomePage.recent": "Son Kullanılanlar", + "welcomePage.moreRecent": "Diğerleri...", + "welcomePage.noRecentFolders": "Son kullanılan klasör yok", + "welcomePage.help": "Yardım", + "welcomePage.keybindingsCheatsheet": "Yazdırılabilir klavye kopya kağıdı", + "welcomePage.introductoryVideos": "Tanıtım videoları", + "welcomePage.tipsAndTricks": "İpuçları ve Püf noktaları", + "welcomePage.productDocumentation": "Ürün belgeleri", + "welcomePage.gitHubRepository": "GitHub deposu", + "welcomePage.stackOverflow": "Stack Overflow", + "welcomePage.showOnStartup": "Başlangıçta hoş geldiniz sayfasını göster", + "welcomePage.customize": "Özelleştir", + "welcomePage.installExtensionPacks": "Araçlar ve diller", + "welcomePage.installExtensionPacksDescription": "{0} ve {1} için destek yükle", + "welcomePage.moreExtensions": "fazlası", + "welcomePage.installKeymapDescription": "Klavye kısayolları yükle", + "welcomePage.installKeymapExtension": "{0} ve {1} için klavye kısayolları yükle", + "welcomePage.others": "diğerleri", + "welcomePage.colorTheme": "Renk teması", + "welcomePage.colorThemeDescription": "Düzenleyici ve kodlarınız sevdiğiniz şekilde görünsün", + "welcomePage.learn": "Öğren", + "welcomePage.showCommands": "Tüm komutları bul ve çalıştır", + "welcomePage.showCommandsDescription": "Komut Paleti'nden hızlıca komutlara erişin ve arayın ({0})", + "welcomePage.interfaceOverview": "Arayüze genel bakış", + "welcomePage.interfaceOverviewDescription": "Kullanıcı arayüzünün ana bileşenlerini vurgulayan bir kaplamayı görüntüleyin", + "welcomePage.deployToAzure": "Uygulamaları buluta deploy edin", + "welcomePage.deployToAzureDescription": "Node uygulamalarınızı Azure Uygulama Hizmeti'ne nasıl deploy edeceğinizi öğrenin", + "welcomePage.interactivePlayground": "İnteraktif oyun alanı", + "welcomePage.interactivePlaygroundDescription": "Başlıca düzenleyici özelliklerini kısa örneklerle deneyin" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json new file mode 100644 index 0000000000..eae5dacd44 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "workbenchConfigurationTitle": "Çalışma Ekranı", + "workbench.startupEditor.none": "Bir düzenleyici olmadan başlayın.", + "workbench.startupEditor.welcomePage": "Hoş Geldiniz sayfasını açın (varsayılan).", + "workbench.startupEditor.newUntitledFile": "Yeni bir isimsiz dosya açın.", + "workbench.startupEditor": "Önceki oturumdan hiçbiri düzenleyici geri yüklenmeyecekse, başlangıçta hangi düzenleyicinin gösterileceğini denetler. Bir düzenleyici olmadan başlamak için 'none', Hoş Geldiniz sayfasını açmak için 'welcomePage' (varsayılan), (sadece boş bir çalışma alanı açarken) yeni bir isimsiz dosya açmak için 'newUntitledFile'ı seçin.", + "help": "Yardım" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json b/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json new file mode 100644 index 0000000000..dfe0a5772a --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.i18n.json @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "welcomePage": "Hoş Geldiniz", + "welcomePage.javaScript": "JavaScript", + "welcomePage.typeScript": "TypeScript", + "welcomePage.python": "Python", + "welcomePage.php": "PHP", + "welcomePage.azure": "Azure", + "welcomePage.showAzureExtensions": "Azure eklentilerini göster", + "welcomePage.docker": "Docker", + "welcomePage.vim": "Vim", + "welcomePage.sublime": "Sublime", + "welcomePage.atom": "Atom", + "welcomePage.extensionPackAlreadyInstalled": "{0} desteği zaten yüklü.", + "welcomePage.willReloadAfterInstallingExtensionPack": "{0} ek desteği yüklendikten sonra pencere yeniden yüklenecektir.", + "welcomePage.installingExtensionPack": "{0} ek desteği yükleniyor...", + "welcomePage.extensionPackNotFound": "{1} Id'li {0} desteği bulunamadı.", + "welcomePage.keymapAlreadyInstalled": "{0} klavye kısayolları zaten yüklü.", + "welcomePage.willReloadAfterInstallingKeymap": "{0} klavye kısayolları yüklendikten sonra pencere yeniden yüklenecektir.", + "welcomePage.installingKeymap": "{0} klavye kısayolları yükleniyor...", + "welcomePage.keymapNotFound": "{1} Id'li {0} klavye kısayolları bulunamadı.", + "welcome.title": "Hoş Geldiniz", + "welcomePage.openFolderWithPath": "{1} yolundaki {0} klasörünü aç", + "welcomePage.extensionListSeparator": ", ", + "welcomePage.installKeymap": "{0} tuş haritasını yükle", + "welcomePage.installExtensionPack": "{0} ek desteği yükleniyor...", + "welcomePage.installedKeymap": "{0} tuş haritası zaten yüklü", + "welcomePage.installedExtensionPack": "{0} desteği zaten yüklü", + "ok": "Tamam", + "details": "Detaylar", + "cancel": "İptal", + "welcomePage.buttonBackground": "Hoş geldiniz sayfasındaki butonların arka plan rengi.", + "welcomePage.buttonHoverBackground": "Hoş geldiniz sayfasındaki butonların bağlantı vurgusu arka plan rengi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json b/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json new file mode 100644 index 0000000000..97bccb14c8 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.title": "İnteraktif Oyun Alanı", + "editorWalkThrough": "İnteraktif Oyun Alanı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json new file mode 100644 index 0000000000..bd38fae673 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.editor.label": "İnteraktif Oyun Alanı", + "help": "Yardım", + "interactivePlayground": "İnteraktif Oyun Alanı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json b/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json new file mode 100644 index 0000000000..f4da54d27e --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "editorWalkThrough.arrowUp": "Yukarı Kaydır (Satır)", + "editorWalkThrough.arrowDown": "Aşağı Kaydır (Satır)", + "editorWalkThrough.pageUp": "Yukarı Kaydır (Sayfa)", + "editorWalkThrough.pageDown": "Aşağı Kaydır (Sayfa)" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json b/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json new file mode 100644 index 0000000000..2386cad606 --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "walkThrough.unboundCommand": "serbest", + "walkThrough.gitNotFound": "Git, sisteminizde yüklü değil gibi görünüyor.", + "walkThrough.embeddedEditorBackground": "İnteraktif oyun alanındaki gömülü düzenleyicilerin arka plan rengi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/trk/src/vs/workbench/services/configuration/node/configuration.i18n.json new file mode 100644 index 0000000000..8657f15959 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration": "Yapılandırma ayarlarına ekleme yapar.", + "vscode.extension.contributes.configuration.title": "Ayarların bir özeti. Bu etiket ayar dosyasında ayırıcı yorum olarak kullanılacaktır.", + "vscode.extension.contributes.configuration.properties": "Yapılandırma özelliklerinin açıklaması.", + "scope.window.description": "Kullanıcı veya çalışma alanında yapılandırılabilen Windows'a özel yapılandırma.", + "scope.resource.description": "Kullanıcı veya çalışma alanında yapılandırılabilen kaynağa özel yapılandırma.", + "scope.description": "Yapılandırmanın uygulanabilir olduğu kapsam. Mevcut kapsamlar 'window' ve 'resource'tır.", + "invalid.type": "eğer ayarlanırsa, 'configuration.type' ögesi 'object' olarak ayarlanmalıdır", + "invalid.title": "'configuration.title' bir dize olmalıdır", + "vscode.extension.contributes.defaultConfiguration": "Varsayılan düzenleyici yapılandırma ayarlarına dil bazında ekleme yapar.", + "invalid.properties": "'configuration.properties' bir nesne olmalıdır", + "workspaceConfig.folders.description": "Çalışma alanında yüklenecek klasörler listesi. Bir dosya yolu olmalıdır. ör. `/root/folderA` veya çalışma alanı dosyasının konumuna karşı çözümlenecek göreceli bir yol için `./folderA`.", + "workspaceConfig.folder.description": "Bir dosya yolu. ör. `/root/folderA` veya çalışma alanı dosyasının konumuna karşı çözümlenecek göreceli bir yol için `./folderA`.", + "workspaceConfig.settings.description": "Çalışma alanı ayarları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json b/i18n/trk/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json new file mode 100644 index 0000000000..9cb6f21a63 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/configuration/node/configurationEditingService.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "open": "Ayarları Aç", + "close": "Kapat", + "saveAndRetry": "Ayarları Kaydet ve Yeniden Dene", + "errorUnknownKey": "{0} dosyasına, {1} kayıtlı bir yapılandırma olmadığı için yazılamıyor.", + "errorInvalidFolderConfiguration": "{0}, klasör kaynak kapsamını desteklemediği için klasör ayarlarına yazılamıyor.", + "errorInvalidUserTarget": "{0}, global kapsamı desteklemediği için kullanıcı ayarlarına yazılamıyor.", + "errorInvalidFolderTarget": "Hiçbir kaynak belirtilmediği için klasör ayarlarına yazılamıyor.", + "errorNoWorkspaceOpened": "Hiçbir çalışma alanı açık olmadığı için {0}'a yazılamıyor. Lütfen ilk olarak bir çalışma alanı açın ve tekrar deneyin.", + "errorInvalidConfiguration": "Ayarlara yazılamıyor. Lütfen dosyadaki hata/uyarıları düzeltmek için **Kullanıcı Ayarları'nı** açın ve tekrar deneyin.", + "errorInvalidConfigurationWorkspace": "Ayarlara yazılamıyor. Lütfen dosyadaki hata/uyarıları düzeltmek için **Çalışma Alanı Ayarları'nı** açın ve tekrar deneyin.", + "errorConfigurationFileDirty": "Dosya kaydedilmemiş değişiklikler içerdiği için ayarlara yazılamıyor. Lütfen dosyadaki hata/uyarıları düzeltmek için **Kullanıcı Ayarları'nı** açın ve tekrar deneyin.", + "errorConfigurationFileDirtyWorkspace": "Dosya kaydedilmemiş değişiklikler içerdiği için ayarlara yazılamıyor. Lütfen dosyadaki hata/uyarıları düzeltmek için **Çalışma Alanı Ayarları'nı** açın ve tekrar deneyin.", + "userTarget": "Kullanıcı Ayarları", + "workspaceTarget": "Çalışma Alanı Ayarları", + "folderTarget": "Klasör Ayarları" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json b/i18n/trk/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json new file mode 100644 index 0000000000..09b1482662 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/configuration/node/jsonEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorInvalidFile": "Dosyaya yazılamıyor. Lütfen dosyadaki hata/uyarıları düzeltmek için dosyayı açın ve tekrar deneyin.", + "errorFileDirty": "Dosya kaydedilmemiş değişiklikler içerdiği için dosyaya yazılamıyor. Lütfen dosyayı kaydedin ve tekrar deneyin." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json b/i18n/trk/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json new file mode 100644 index 0000000000..1ffa7bf9bd --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/crashReporter/common/crashReporterService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "telemetryConfigurationTitle": "Telemetri", + "telemetry.enableCrashReporting": "Kilitlenme raporlarının Microsoft'a gönderilmesini etkinleştirin.\nBu seçeneğin yürürlüğe girmesi için yeniden başlatma gerekir." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/editor/browser/editorService.i18n.json b/i18n/trk/src/vs/workbench/services/editor/browser/editorService.i18n.json new file mode 100644 index 0000000000..890847d396 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/editor/browser/editorService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json b/i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json new file mode 100644 index 0000000000..7dc4380ca8 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionHost.i18n.json @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "extensionHostProcess.startupFailDebug": "Eklenti sunucusu 10 saniye içinde başlamadı, ilk satırda durdurulmuş olabilir ve devam etmesi için bir hata ayıklayıcıya ihtiyacı olabilir.", + "extensionHostProcess.startupFail": "Eklenti sunucusu 10 saniye içinde başlamadı, bu bir sorun olabilir.", + "extensionHostProcess.error": "Eklenti sunucusundan hata: {0}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json b/i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json new file mode 100644 index 0000000000..e1fbeaa5a2 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionPoints.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "jsonParseFail": "{0} ayrıştırılamadı: {1}.", + "fileReadFail": "{0} dosyası okunamadı: {1}.", + "jsonsParseFail": "{0} veya {1} ayrıştırılamadı: {2}.", + "missingNLSKey": "{0} anahtarı için mesaj bulunamadı." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json b/i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json new file mode 100644 index 0000000000..b64e7b6e5e --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/extensions/electron-browser/extensionService.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "devTools": "Geliştirici Araçları", + "restart": "Eklenti Sunucusunu Yeniden Başlat", + "extensionHostProcess.crash": "Eklenti sunucusu beklenmeyen biçimde sonlandırıldı.", + "extensionHostProcess.unresponsiveCrash": "Eklenti sunucusu yanıt vermediğinden sonlandırıldı.", + "overwritingExtension": "{0} eklentisinin üzerine {1} yazılıyor.", + "extensionUnderDevelopment": "{0} konumundaki geliştirme eklentisi yükleniyor" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/files/electron-browser/fileService.i18n.json b/i18n/trk/src/vs/workbench/services/files/electron-browser/fileService.i18n.json new file mode 100644 index 0000000000..e0c4a55299 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/files/electron-browser/fileService.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "netVersionError": "Microsoft .NET Framework 4.5 gerekli. Yüklemek için bağlantıyı izleyin.", + "installNet": ".NET Framework 4.5'i İndir", + "neverShowAgain": "Tekrar Gösterme", + "trashFailed": "'{0}' çöpe taşınamadı" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/trk/src/vs/workbench/services/files/node/fileService.i18n.json new file mode 100644 index 0000000000..f99df04857 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/files/node/fileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileInvalidPath": "Geçersiz dosya kaynağı ({0})", + "fileIsDirectoryError": "Dosya bir dizindir ({0})", + "fileNotModifiedError": "Dosya şu tarihten beri değiştirilmemiş:", + "fileTooLargeError": "Dosya, açmak için çok büyük", + "fileBinaryError": "Dosya ikili olarak görünüyor ve metin olarak açılamıyor", + "fileNotFoundError": "Dosya bulunamadı ({0})", + "fileMoveConflict": "Taşıma/kopyalama yapılamadı. Dosya, hedefte zaten mevcut.", + "unableToMoveCopyError": "Taşıma/kopyalama yapılamadı. Dosya, içinde bulunduğu klasörü değiştiriyor.", + "foldersCopyError": "Klasörler çalışma alanına kopyalanamaz. Lütfen kopyalamak için dosyaları tek tek seçin.", + "fileModifiedError": "Dosya Şu Tarihten Beri Değiştiriliyor", + "fileReadOnlyError": "Dosya Salt Okunur" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json b/i18n/trk/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json new file mode 100644 index 0000000000..f56ecc9bbc --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/keybinding/common/keybindingEditing.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "errorKeybindingsFileDirty": "Dosya kaydedilmemiş değişiklikler içerdiği için yazılamıyor. Lütfen **tuş bağları** dosyasını kaydedin ve tekrar deneyin.", + "parseErrors": "Tuş bağları yazılamadı. Lütfen dosyadaki hata/uyarıları düzeltmek için **tuş bağları dosyasını** açın ve yeniden deneyin.", + "errorInvalidConfiguration": "Tuş bağları yazılamadı. **Tuş bağları dosyasında** Dizi olmayan bir nesne var. Temizlemek için lütfen dosyayı açın ve yeniden deneyin.", + "emptyKeybindingsHeader": "Varsayılanların üzerine yazmak için tuş bağlarınızı bu dosyaya yerleştirin" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json b/i18n/trk/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json new file mode 100644 index 0000000000..d62f5089f2 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/keybinding/electron-browser/keybindingService.i18n.json @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "nonempty": "boş olmayan değer bekleniyordu.", + "requirestring": "`{0}` özelliği zorunludur ve `string` türünde olmalıdır", + "optstring": "`{0}` özelliği atlanabilir veya `string` türünde olmalıdır", + "vscode.extension.contributes.keybindings.command": "Tuş bağı tetiklendiğinde çalıştırılacak komutun tanımlayıcısı.", + "vscode.extension.contributes.keybindings.key": "Tuş veya tuş dizisi (tuşları artı işaretiyle veya boşluk dizisiyle ayırın, ör. bir akor için Ctrl+O ve Ctrl+L).", + "vscode.extension.contributes.keybindings.mac": "Mac'e özel tuş veya tuş dizisi.", + "vscode.extension.contributes.keybindings.linux": "Linux'a özel tuş veya tuş dizisi.", + "vscode.extension.contributes.keybindings.win": "Windows'a özel tuş veya tuş dizisi.", + "vscode.extension.contributes.keybindings.when": "Tuşun aktif olacağı koşul", + "vscode.extension.contributes.keybindings": "Tuş bağlarına ekleme yapar.", + "invalid.keybindings": "Geçersiz `contributes.{0}`: {1}", + "unboundCommands": "Kullanılabilen diğer komutlar şunlardır: ", + "keybindings.json.title": "Tuş bağları yapılandırması", + "keybindings.json.key": "Tuş veya tuş dizisi (boşluk ile ayrılmış olarak)", + "keybindings.json.command": "Yürütülecek komutun adı", + "keybindings.json.when": "Tuşun aktif olacağı koşul", + "keybindings.json.args": "Yürütülecek komuta iletilecek argümanlar.", + "keyboardConfigurationTitle": "Klavye", + "dispatch": "Tuş basımlarının ya `keydown.code` (önerilen) ya da ` keydown.keyCode` kullanarak gönderilmesini denetler." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/message/browser/messageList.i18n.json b/i18n/trk/src/vs/workbench/services/message/browser/messageList.i18n.json new file mode 100644 index 0000000000..3751a341b1 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/message/browser/messageList.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "alertErrorMessage": "Hata: {0}", + "alertWarningMessage": "Uyarı: {0}", + "alertInfoMessage": "Bilgi: {0}", + "error": "Hata", + "warning": "Uyar", + "info": "Bilgi", + "close": "Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/message/electron-browser/messageService.i18n.json b/i18n/trk/src/vs/workbench/services/message/electron-browser/messageService.i18n.json new file mode 100644 index 0000000000..0c20b07941 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/message/electron-browser/messageService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "yesButton": "&&Evet", + "cancelButton": "İptal" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json b/i18n/trk/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json new file mode 100644 index 0000000000..d6fbf4484f --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/mode/common/workbenchModeService.i18n.json @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.languages": "Dil bildirimlerine ekleme yapar.", + "vscode.extension.contributes.languages.id": "Dilin ID'si.", + "vscode.extension.contributes.languages.aliases": "Dilin takma adları.", + "vscode.extension.contributes.languages.extensions": "Dil ile ilişkili dosya uzantıları.", + "vscode.extension.contributes.languages.filenames": "Dil ile ilişkili dosya adları.", + "vscode.extension.contributes.languages.filenamePatterns": "Dil ile ilişkili dosya adı glob desenleri.", + "vscode.extension.contributes.languages.mimetypes": "Dil ile ilişkili MIME türleri.", + "vscode.extension.contributes.languages.firstLine": "Dilin bir dosyasının ilk satırıyla eşleşen düzenli ifade.", + "vscode.extension.contributes.languages.configuration": "Dil için yapılandırma seçenekleri içeren dosyaya, bir göreli yol.", + "invalid": "Geçersiz `contributes.{0}`. Bir dizi bekleniyordu.", + "invalid.empty": "`contributes.{0}` için boş değer", + "require.id": "`{0}` özelliği zorunludur ve `string` türünde olmalıdır", + "opt.extensions": "`{0}` özelliği atlanabilir veya `string[]` türünde olmalıdır", + "opt.filenames": "`{0}` özelliği atlanabilir veya `string[]` türünde olmalıdır", + "opt.firstLine": "`{0}` özelliği atlanabilir veya `string` türünde olmalıdır", + "opt.configuration": "`{0}` özelliği atlanabilir veya `string` türünde olmalıdır", + "opt.aliases": "`{0}` özelliği atlanabilir veya `string[]` türünde olmalıdır", + "opt.mimetypes": "`{0}` özelliği atlanabilir veya `string[]` türünde olmalıdır" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/progress/browser/progressService2.i18n.json b/i18n/trk/src/vs/workbench/services/progress/browser/progressService2.i18n.json new file mode 100644 index 0000000000..441530a01b --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/progress/browser/progressService2.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "progress.subtitle": "{0} - {1}", + "progress.title": "{0}: {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json b/i18n/trk/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json new file mode 100644 index 0000000000..33f4b46b0a --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/textMate/electron-browser/TMGrammars.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.grammars": "textmate tokenizerlere ekleme yapar.", + "vscode.extension.contributes.grammars.language": "Bu söz diziminin ekleneceği dil tanımlayıcısı.", + "vscode.extension.contributes.grammars.scopeName": "tmLanguage dosyası tarafından kullanılan Textmate kapsam adı.", + "vscode.extension.contributes.grammars.path": "tmLanguage dosyasının yolu. Yol, eklenti klasörüne görecelidir ve genellikle './syntaxes/' ile başlar.", + "vscode.extension.contributes.grammars.embeddedLanguages": "Bu dil bilgisinin gömülü dilleri içermesi durumundaki bir dil kimliğinin kapsam adı haritası.", + "vscode.extension.contributes.grammars.injectTo": "Bu dil bilgisinin yerleştirileceği dil kapsam adları listesi." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json b/i18n/trk/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json new file mode 100644 index 0000000000..e0a16b35ad --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/textMate/electron-browser/TMSyntax.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "invalid.language": "`contributes.{0}.language` ögesinde bilinmeyen dil. Sağlanan değer: {1}", + "invalid.scopeName": "`contributes.{0}.scopeName` ögesinde dize bekleniyor. Sağlanan değer: {1}", + "invalid.path.0": "`contributes.{0}.path` ögesinde dize bekleniyor. Sağlanan değer: {1}", + "invalid.injectTo": "`contributes.{0}.injectTo` ögesinde geçersiz değer. Dil kapsam adlarından oluşan bir dizi olmalıdır. Sağlanan değer: {1}", + "invalid.embeddedLanguages": "`contributes.{0}.embeddedLanguages` ögesinde geçersiz değer. Kapsam adından dile kadar olan bir nesne haritası olmalıdır. Sağlanan değer: {1}", + "invalid.path.1": "`contributes.{0}.path` ögesinin ({1}) eklentinin klasöründe ({2}) yer alması bekleniyor. Bu, eklentiyi taşınamaz yapabilir.", + "no-tm-grammar": "Bu dil için kayıtlı bir TM Grameri yok." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json b/i18n/trk/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json new file mode 100644 index 0000000000..023cdc8e93 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/textfile/common/textFileEditorModel.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveFileFirst": "Dosya kaydedilmemiş değişiklikler içeriyor. Başka bir kodlama ile yeniden açmadan önce lütfen ilk olarak kaydedin.", + "genericSaveError": "'{0}' kaydedilemedi: ({1})." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/textfile/common/textFileService.i18n.json b/i18n/trk/src/vs/workbench/services/textfile/common/textFileService.i18n.json new file mode 100644 index 0000000000..55acf550cd --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/textfile/common/textFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "files.backup.failSave": "Dosyalar yedeklenemedi (Hata: {0}), çıkmak için dosyalarınızı kaydetmeyi deneyin." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json b/i18n/trk/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json new file mode 100644 index 0000000000..4e5397d9d5 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/textfile/electron-browser/textFileService.i18n.json @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "saveChangesMessage": "{0} dosyasına yaptığınız değişiklikleri kaydetmek istiyor musunuz?", + "saveChangesMessages": "Aşağıdaki {0} dosyaya yaptığınız değişiklikleri kaydetmek istiyor musunuz?", + "moreFile": "...1 ek dosya gösterilmiyor", + "moreFiles": "...{0} ek dosya gösterilmiyor", + "saveAll": "&&Tümünü Kaydet", + "save": "&&Kaydet", + "dontSave": "Kaydet&&me", + "cancel": "İptal", + "saveChangesDetail": "Değişiklikleriniz, kaydetmezseniz kaybolur.", + "allFiles": "Tüm Dosyalar", + "noExt": "Uzantısız" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/trk/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json new file mode 100644 index 0000000000..dfa12eeeb1 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.token.settings": "Belirteç renkleri ve stilleri.", + "schema.token.foreground": "Belirteç ön plan rengi.", + "schema.token.fontStyle": "Kuralın yazı tipi stili: 'italic', 'bold' ve 'underline' kombinasyonu veya bunlardan bir tanesi", + "schema.fontStyle.error": "Yazı tipi stili; 'italic', 'bold' ve 'underline' kombinasyonu olmalıdır", + "schema.properties.name": "Kural açıklaması.", + "schema.properties.scope": "Bu kural karşısında eşleşecek kapsam seçicisi.", + "schema.tokenColors.path": "Bir tmTheme dosyasının yolu (geçerli dosyaya göreli).", + "schema.colors": "Sentaks vurgulaması renkleri" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/trk/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json new file mode 100644 index 0000000000..6394957c8d --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "schema.folderExpanded": "Genişletilmiş klasörler için klasör simgesi. Genişletilmiş klasör simgesi isteğe bağlıdır. Ayarlanmazsa, klasör için tanımlanan simge gösterilir.", + "schema.folder": "Daraltılmış klasörler için klasör simgesi; eğer folderExpanded ayarlanmamışsa, genişletilen klasörler için de kullanılır.", + "schema.file": "Varsayılan dosya simgesi, bir uzantı, dosya adı veya dil kimliği ile eşleşmeyen tüm dosyalar için gösterilir.", + "schema.folderNames": "Klasör adlarını simgelerle ilişkilendirir. Nesne anahtarı, herhangi bir yol parçası içermeyen klasör adıdır. Örüntüler veya joker karakterlere izin verilmez. Klasör adı eşleştirme büyük/küçük harfe duyarlı değildir.", + "schema.folderName": "İlişkilendirilecek simge tanımı ID'si.", + "schema.folderNamesExpanded": "Klasör adlarını genişletilmiş klasör simgeleriyle ilişkilendirir. Nesne anahtarı, herhangi bir yol parçası içermeyen klasör adıdır. Örüntüler veya joker karakterlere izin verilmez. Klasör adı eşleştirme büyük/küçük harfe duyarlı değildir.", + "schema.folderNameExpanded": "İlişkilendirilecek simge tanımı ID'si.", + "schema.fileExtensions": "Dosya uzantılarını simgelerle ilişkilendirir. Nesne anahtarı, dosya uzantısı adıdır. Uzantı adı, bir dosya adındaki son noktadan sonraki kısmıdır(nokta dahil değil). Uzantılar büyük/küçük harf ayırt etmeksizin karşılaştırılır.", + "schema.fileExtension": "İlişkilendirilecek simge tanımı ID'si.", + "schema.fileNames": "Dosya adlarını simgelerle ilişkilendirir. Nesne anahtarı, herhangi bir yol parçası içermeyen tam dosya adıdır. Dosya adı noktalar ve olası bir dosya uzantısı içerebilir. Örüntüler veya joker karakterlere izin verilmez. Dosya adı eşleştirme büyük/küçük harfe duyarlı değildir.", + "schema.fileName": "İlişkilendirilecek simge tanımı ID'si.", + "schema.languageIds": "Dilleri simgelerle ilişkilendirir. Nesne anahtarı, dil ekleme noktasında tanımlanan dil kimliğidir.", + "schema.languageId": "İlişkilendirilecek simge tanımı ID'si.", + "schema.fonts": "Simge tanımlarında kullanılacak yazı tipleri.", + "schema.id": "Yazı tipinin ID'si.", + "schema.src": "Yazı tipinin konumları.", + "schema.font-path": "Yazı tipi yolu, geçerli simge teması dosyasına göreli yol.", + "schema.font-format": "Yazı tipinin biçimi.", + "schema.font-weight": "Yazı tipinin kalınlığı.", + "schema.font-sstyle": "Yazı tipinin stili.", + "schema.font-size": "Yazı tipinin varsayılan boyutu.", + "schema.iconDefinitions": "Dosyalar simgelerle ilişkilendirirken kullanılabilecek tüm simgelerin açıklaması.", + "schema.iconDefinition": "Bir simge tanımı. Nesne anahtarı, tanımın ID'sidir.", + "schema.iconPath": "SVG veya PNG kullanırken: Görüntünün yolu. Yol, simge kümesi dosyasına görelidir.", + "schema.fontCharacter": "Glif yazı tipi kullanırken: Kullanılacak yazı tipindeki karakter.", + "schema.fontColor": "Glif yazı tipi kullanırken: Kullanılacak renk.", + "schema.fontSize": "Yazı tipi kullanırken: Metin yazı tipi yüzdesine göre yazı tipi boyutu. Ayarlanmazsa, yazı tipi tanımındaki boyut kullanılır.", + "schema.fontId": "Yazı tipi kullanırken: Yazı tipinin kimliği. Ayarlanmazsa, ilk yazı tipi tanımı varsayılan olarak kullanılır.", + "schema.light": "Açık renk temalarındaki dosya simgeleri için isteğe bağlı ilişkilendirmeler.", + "schema.highContrast": "Yüksek karşıtlık renk temalarındaki dosya simgeleri için isteğe bağlı ilişkilendirmeler." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json b/i18n/trk/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json new file mode 100644 index 0000000000..9b6d5ef68e --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/themes/electron-browser/colorThemeData.i18n.json @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "error.cannotparsejson": "JSON tema dosyasını ayrıştırma sorunları: {0}", + "error.invalidformat.colors": "Renk teması dosyasını ayrıştırma sorunu: {0}. 'colors' özelliği 'nesne' türünde değil.", + "error.invalidformat.tokenColors": "Renk teması dosyasını ayrıştırma sorunu: {0}. 'tokenColors' özelliği ya renkleri belirten bir dizi ya da bir TextMate tema dosyasının yolunu içermelidir", + "error.plist.invalidformat": "tmTheme tema dosyasını ayrıştırma sorunları: {0}. 'settings' dizi değil", + "error.cannotparse": "tmTheme tema dosyasını ayrıştırma sorunları: {0}", + "error.cannotload": "{0} tmTheme tema dosyasını yükleme sorunları: {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/trk/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json new file mode 100644 index 0000000000..185c42dd33 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.themes": "Textmate renk temalarına ekleme yapar.", + "vscode.extension.contributes.themes.id": "Kullanıcı ayarlarında kullanılan simge teması Id'si.", + "vscode.extension.contributes.themes.label": "Kullanıcı arayüzünde görünen renk temasının etiketi.", + "vscode.extension.contributes.themes.uiTheme": "Editördeki renkleri tanımlayan temel tema: 'vs' açık renk temasıdır, 'vs-dark' koyu renk temasıdır. 'hc-black' ise yüksek kontrast temasıdır.", + "vscode.extension.contributes.themes.path": "tmLanguage dosyasının yolu. Yol, eklenti klasörüne görecelidir ve genellikle './themes/themeFile.tmTheme'dir.", + "vscode.extension.contributes.iconThemes": "Dosya simgesi temalarına ekleme yapar.", + "vscode.extension.contributes.iconThemes.id": "Kullanıcı ayarlarında kullanılan simge teması Id'si.", + "vscode.extension.contributes.iconThemes.label": "Kullanıcı arayüzünde görünen simge temasının etiketi.", + "vscode.extension.contributes.iconThemes.path": "Simge teması tanımlama dosyasının yolu. Yol, eklenti klasörüne görecelidir ve genellikle './icons/awesome-icon-theme.json'dur.", + "migration.completed": "Yeni tema ayarları kullanıcı ayarlarına eklendi. Yedek, {0} konumunda mevcuttur.", + "error.cannotloadtheme": "{0} yüklenemedi: {1}", + "reqarray": "Eklenti noktası `{0}` bir dizi olmalıdır.", + "reqpath": "`contributes.{0}.path` ögesinde dize bekleniyor. Sağlanan değer: {1}", + "invalid.path.1": "`contributes.{0}.path` ögesinin ({1}) eklentinin klasöründe ({2}) yer alması bekleniyor. Bu, eklentiyi taşınamaz yapabilir.", + "reqid": "`contributes.{0}.id` ögesinde dize bekleniyordu. Belirtilen değer: {1}", + "error.cannotloadicontheme": "{0} yüklenemedi", + "error.cannotparseicontheme": "Dosya simgeleri dosyasını ayrıştırma sorunları: {0}", + "colorTheme": "Çalışma ekranında kullanılan renk temasını belirtir.", + "colorThemeError": "Tema bilinmiyor veya yüklenmemiş.", + "iconTheme": "Çalışma ekranında kullanılan simge temasını veya hiçbir dosya simgesi göstermemek için 'null' belirtir.", + "noIconThemeDesc": "Dosya simgesi yok", + "iconThemeError": "Dosya simgesi teması bilinmiyor veya yüklenmemiş.", + "workbenchColors": "Şu an seçili renk temasındaki renkleri geçersiz kılar.", + "workbenchColors.deprecated": "Ayar, artık deneysel değildir ve 'workbench.colorCustomizations' olarak yeniden adlandırılmıştır", + "workbenchColors.deprecatedDescription": "Bunun yerine 'workbench.colorCustomizations' kullanın", + "editorColors": "Şu an seçili renk temasındaki düzenleyici renklerini ve yazı tipi stilini geçersiz kılar.", + "editorColors.comments": "Yorumların rengini ve stillerini ayarlar", + "editorColors.strings": "Dizelerin rengini ve stillerini ayarlar.", + "editorColors.keywords": "Anahtar sözcüklerin rengini ve stillerini ayarlar.", + "editorColors.numbers": "Numaraların rengini ve stillerini ayarlar.", + "editorColors.types": "Tür bildirimi ve başvurularının rengini ve stillerini ayarlar.", + "editorColors.functions": "Fonksiyon bildirimi ve başvurularının rengini ve stillerini ayarlar.", + "editorColors.variables": "Değişken bildirimi ve başvurularının rengini ve stillerini ayarlar.", + "editorColors.textMateRules": "Textmate tema kurallarını kullanarak renkleri ve stilleri ayarlar (gelişmiş)." +} \ No newline at end of file diff --git a/issue_template.md b/issue_template.md new file mode 100644 index 0000000000..c36276665a --- /dev/null +++ b/issue_template.md @@ -0,0 +1,4 @@ +- SQL Operations Studio Version: + +Steps to Reproduce: + diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000000..cc73203101 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,27 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=759670 + // for the documentation about the jsconfig.json format + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "allowSyntheticDefaultImports": true + }, + "exclude": [ + "src", + "extensions", + "test", + "i18n", + "out", + "out-build", + "out-vscode", + "out-vscode-min", + "out-editor", + "out-editor-min", + "out-monaco-editor-core", + "resources", + "scripts", + ".build", + "node_modules", + "build/monaco/node_modules" + ] +} diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json new file mode 100644 index 0000000000..418ae9c56e --- /dev/null +++ b/npm-shrinkwrap.json @@ -0,0 +1,880 @@ +{ + "name": "sqlops", + "version": "0.20.0", + "dependencies": { + "zone.js": { + "version": "0.8.11", + "from": "zone.js@>=0.8.4 <0.9.0", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.11.tgz" + }, + "@angular/animations": { + "version": "4.1.3", + "from": "@angular/animations@>=4.1.3 <4.2.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.1.3.tgz" + }, + "@angular/common": { + "version": "4.1.3", + "from": "@angular/common@>=4.1.3 <4.2.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-4.1.3.tgz" + }, + "@angular/compiler": { + "version": "4.1.3", + "from": "@angular/compiler@>=4.1.3 <4.2.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.1.3.tgz" + }, + "@angular/core": { + "version": "4.1.3", + "from": "@angular/core@>=4.1.3 <4.2.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-4.1.3.tgz" + }, + "@angular/forms": { + "version": "4.1.3", + "from": "@angular/forms@>=4.1.3 <4.2.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.1.3.tgz" + }, + "@angular/http": { + "version": "4.1.3", + "from": "@angular/http@>=4.1.3 <4.2.0", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-4.1.3.tgz" + }, + "@angular/platform-browser": { + "version": "4.1.3", + "from": "@angular/platform-browser@>=4.1.3 <4.2.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.1.3.tgz" + }, + "@angular/platform-browser-dynamic": { + "version": "4.1.3", + "from": "@angular/platform-browser-dynamic@>=4.1.3 <4.2.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.1.3.tgz" + }, + "@angular/router": { + "version": "4.1.3", + "from": "@angular/router@>=4.1.3 <4.2.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-4.1.3.tgz" + }, + "@angular/upgrade": { + "version": "4.1.3", + "from": "@angular/upgrade@>=4.1.3 <4.2.0", + "resolved": "https://registry.npmjs.org/@angular/upgrade/-/upgrade-4.1.3.tgz" + }, + "agent-base": { + "version": "1.0.2", + "from": "agent-base@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-1.0.2.tgz" + }, + "angular2-slickgrid": { + "version": "1.3.4", + "from": "git://github.com/Microsoft/angular2-slickgrid.git#1.3.5", + "resolved": "git://github.com/Microsoft/angular2-slickgrid.git#d122015f2f3e4023394a7e485079da62f20b8356" + }, + "angular2-grid": { + "version": "2.0.6", + "from": "angular2-grid@2.0.6", + "resolved": "https://registry.npmjs.org/angular2-grid/-/angular2-grid-2.0.6.tgz" + }, + "anymatch": { + "version": "1.3.0", + "from": "anymatch@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz" + }, + "applicationinsights": { + "version": "0.17.1", + "from": "applicationinsights@0.17.1", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.17.1.tgz" + }, + "arr-diff": { + "version": "2.0.0", + "from": "arr-diff@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz" + }, + "arr-flatten": { + "version": "1.0.3", + "from": "arr-flatten@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz" + }, + "array-unique": { + "version": "0.2.1", + "from": "array-unique@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz" + }, + "arrify": { + "version": "1.0.1", + "from": "arrify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" + }, + "async-each": { + "version": "1.0.1", + "from": "async-each@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz" + }, + "balanced-match": { + "version": "0.4.2", + "from": "balanced-match@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + }, + "binary-extensions": { + "version": "1.8.0", + "from": "binary-extensions@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz" + }, + "bindings": { + "version": "1.2.1", + "from": "bindings@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "optional": true + }, + "brace-expansion": { + "version": "1.1.7", + "from": "brace-expansion@>=1.1.7 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz" + }, + "braces": { + "version": "1.8.5", + "from": "braces@>=1.8.2 <2.0.0", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz" + }, + "buffer-crc32": { + "version": "0.2.13", + "from": "buffer-crc32@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" + }, + "buffer-shims": { + "version": "1.0.0", + "from": "buffer-shims@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + }, + "caniuse-db": { + "version": "1.0.30000676", + "from": "caniuse-db@>=1.0.30000161 <2.0.0", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000676.tgz" + }, + "chart.js": { + "version": "2.6.0", + "from": "chart.js@>=2.6.0 <3.0.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.6.0.tgz" + }, + "chartjs-color": { + "version": "2.1.0", + "from": "chartjs-color@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.1.0.tgz" + }, + "chartjs-color-string": { + "version": "0.4.0", + "from": "chartjs-color-string@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.4.0.tgz" + }, + "chokidar": { + "version": "1.6.1", + "from": "bpasero/chokidar#vscode", + "resolved": "git://github.com/bpasero/chokidar.git#4c167ce0c29dae1727518998ecad63a049433e35" + }, + "color-convert": { + "version": "0.5.3", + "from": "color-convert@>=0.5.3 <0.6.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz" + }, + "color-name": { + "version": "1.1.2", + "from": "color-name@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.2.tgz" + }, + "comment-json": { + "version": "1.1.3", + "from": "comment-json@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-1.1.3.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + }, + "core-js": { + "version": "2.4.1", + "from": "core-js@>=2.4.1 <3.0.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "debug": { + "version": "2.6.8", + "from": "debug@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + }, + "emmet": { + "version": "1.3.2", + "from": "ramya-rao-a/emmet#vscode", + "resolved": "git://github.com/ramya-rao-a/emmet.git#b4d3edc30de7fa98032302cd1f017a940ab879fd" + }, + "error-ex": { + "version": "1.3.1", + "from": "error-ex@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz" + }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + }, + "esprima": { + "version": "2.7.3", + "from": "esprima@>=2.7.0 <3.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz" + }, + "expand-brackets": { + "version": "0.1.5", + "from": "expand-brackets@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz" + }, + "expand-range": { + "version": "1.8.2", + "from": "expand-range@>=1.8.1 <2.0.0", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz" + }, + "extend": { + "version": "3.0.1", + "from": "extend@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz" + }, + "extglob": { + "version": "0.3.2", + "from": "extglob@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz" + }, + "extract-opts": { + "version": "2.2.0", + "from": "extract-opts@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/extract-opts/-/extract-opts-2.2.0.tgz" + }, + "fast-plist": { + "version": "0.1.2", + "from": "fast-plist@0.1.2", + "resolved": "https://registry.npmjs.org/fast-plist/-/fast-plist-0.1.2.tgz" + }, + "fd-slicer": { + "version": "1.0.1", + "from": "fd-slicer@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz" + }, + "filename-regex": { + "version": "2.0.1", + "from": "filename-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz" + }, + "fill-range": { + "version": "2.2.3", + "from": "fill-range@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz" + }, + "for-in": { + "version": "1.0.2", + "from": "for-in@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" + }, + "for-own": { + "version": "0.1.5", + "from": "for-own@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz" + }, + "fs-extra": { + "version": "3.0.1", + "from": "fs-extra@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz" + }, + "fsevents": { + "version": "0.3.8", + "from": "bpasero/fsevents#vscode", + "resolved": "git+https://github.com/bpasero/fsevents.git#fe2aaccaaffbd69a23374cf46a8c6bafe8e51b01", + "optional": true + }, + "gc-signals": { + "version": "0.0.1", + "from": "gc-signals@0.0.1", + "resolved": "https://registry.npmjs.org/gc-signals/-/gc-signals-0.0.1.tgz" + }, + "getmac": { + "version": "1.0.7", + "from": "getmac@1.0.7", + "resolved": "https://registry.npmjs.org/getmac/-/getmac-1.0.7.tgz" + }, + "glob-base": { + "version": "0.3.0", + "from": "glob-base@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz" + }, + "glob-parent": { + "version": "2.0.0", + "from": "glob-parent@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz" + }, + "graceful-fs": { + "version": "4.1.11", + "from": "graceful-fs@4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + }, + "html-query-plan": { + "version": "1.0.0", + "from": "git://github.com/anthonydresser/html-query-plan.git#v2.2.5", + "resolved": "git://github.com/anthonydresser/html-query-plan.git#fbf8beac00b3870c0d3f4e95de979f7f1ec7af5d" + }, + "http-proxy-agent": { + "version": "0.2.7", + "from": "http-proxy-agent@0.2.7", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-0.2.7.tgz" + }, + "https-proxy-agent": { + "version": "0.3.6", + "from": "https-proxy-agent@0.3.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-0.3.6.tgz" + }, + "iconv-lite": { + "version": "0.4.15", + "from": "iconv-lite@0.4.15", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "is-arrayish": { + "version": "0.2.1", + "from": "is-arrayish@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + }, + "is-binary-path": { + "version": "1.0.1", + "from": "is-binary-path@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz" + }, + "is-buffer": { + "version": "1.1.5", + "from": "is-buffer@>=1.1.5 <2.0.0", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + }, + "is-dotfile": { + "version": "1.0.3", + "from": "is-dotfile@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz" + }, + "is-equal-shallow": { + "version": "0.1.3", + "from": "is-equal-shallow@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz" + }, + "is-extendable": { + "version": "0.1.1", + "from": "is-extendable@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + }, + "is-extglob": { + "version": "1.0.0", + "from": "is-extglob@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + }, + "is-glob": { + "version": "2.0.1", + "from": "is-glob@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + }, + "is-number": { + "version": "2.1.0", + "from": "is-number@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz" + }, + "is-posix-bracket": { + "version": "0.1.1", + "from": "is-posix-bracket@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz" + }, + "is-primitive": { + "version": "2.0.0", + "from": "is-primitive@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "isobject": { + "version": "2.1.0", + "from": "isobject@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" + }, + "jquery": { + "version": "2.2.4", + "from": "jquery@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz" + }, + "jquery-ui": { + "version": "1.12.1", + "from": "jquery-ui@>=1.12.1 <2.0.0", + "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz" + }, + "jquery.event.drag": { + "version": "2.2.2", + "from": "jquery.event.drag@2.2.2", + "resolved": "https://registry.npmjs.org/jquery.event.drag/-/jquery.event.drag-2.2.2.tgz" + }, + "jschardet": { + "version": "1.4.2", + "from": "jschardet@>=1.4.2 <2.0.0", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.4.2.tgz" + }, + "json-parser": { + "version": "1.1.5", + "from": "json-parser@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/json-parser/-/json-parser-1.1.5.tgz" + }, + "jsonfile": { + "version": "3.0.0", + "from": "jsonfile@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.0.tgz" + }, + "keytar": { + "version": "4.0.3", + "from": "keytar@latest", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-4.0.3.tgz", + "dependencies": { + "nan": { + "version": "2.5.1", + "from": "nan@2.5.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" + } + } + }, + "kind-of": { + "version": "3.0.4", + "from": "kind-of@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.0.4.tgz" + }, + "make-error": { + "version": "1.3.0", + "from": "make-error@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz" + }, + "micromatch": { + "version": "2.3.11", + "from": "micromatch@>=2.1.5 <3.0.0", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz" + }, + "minimatch": { + "version": "3.0.3", + "from": "minimatch@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "moment": { + "version": "2.18.1", + "from": "moment@>=2.15.1 <3.0.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz" + }, + "ms": { + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + }, + "nan": { + "version": "2.5.0", + "from": "nan@2.5.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.0.tgz" + }, + "native-keymap": { + "version": "1.2.5", + "from": "native-keymap@1.2.5", + "resolved": "https://registry.npmjs.org/native-keymap/-/native-keymap-1.2.5.tgz" + }, + "native-watchdog": { + "version": "0.3.0", + "from": "native-watchdog@0.3.0", + "resolved": "https://registry.npmjs.org/native-watchdog/-/native-watchdog-0.3.0.tgz" + }, + "ng2-charts": { + "version": "1.6.0", + "from": "ng2-charts@>=1.6.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-1.6.0.tgz" + }, + "node-pty": { + "version": "0.7.0", + "from": "node-pty@0.7.0", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.7.0.tgz", + "dependencies": { + "nan": { + "version": "2.5.0", + "from": "nan@2.5.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.0.tgz" + } + } + }, + "normalize-path": { + "version": "2.1.1", + "from": "normalize-path@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" + }, + "object.omit": { + "version": "2.0.1", + "from": "object.omit@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz" + }, + "oniguruma": { + "version": "6.1.1", + "from": "oniguruma@>=6.0.1 <7.0.0", + "resolved": "https://registry.npmjs.org/oniguruma/-/oniguruma-6.1.1.tgz" + }, + "parse-glob": { + "version": "3.0.4", + "from": "parse-glob@>=3.0.4 <4.0.0", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz" + }, + "path-is-absolute": { + "version": "1.0.1", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + }, + "pend": { + "version": "1.2.0", + "from": "pend@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" + }, + "preserve": { + "version": "0.2.0", + "from": "preserve@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz" + }, + "pretty-data": { + "version": "0.40.0", + "from": "pretty-data@>=0.40.0 <0.41.0", + "resolved": "https://registry.npmjs.org/pretty-data/-/pretty-data-0.40.0.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "process-nextick-args@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "pty.js": { + "version": "0.3.0", + "from": "https://github.com/Tyriar/pty.js/tarball/c75c2dcb6dcad83b0cb3ef2ae42d0448fb912642", + "resolved": "https://github.com/Tyriar/pty.js/tarball/c75c2dcb6dcad83b0cb3ef2ae42d0448fb912642", + "dependencies": { + "extend": { + "version": "1.2.1", + "from": "extend@>=1.2.1 <1.3.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz" + }, + "nan": { + "version": "2.2.1", + "from": "nan@2.2.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.2.1.tgz" + } + } + }, + "nsfw": { + "version": "1.0.16", + "from": "nsfw@1.0.16", + "resolved": "https://registry.npmjs.org/nsfw/-/nsfw-1.0.16.tgz", + "dependencies": { + "asap": { + "version": "2.0.5", + "from": "asap@>=2.0.3 <2.1.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz" + }, + "balanced-match": { + "version": "1.0.0", + "from": "balanced-match@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" + }, + "brace-expansion": { + "version": "1.1.8", + "from": "brace-expansion@>=1.1.7 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + }, + "fs-extra": { + "version": "0.26.7", + "from": "fs-extra@>=0.26.5 <0.27.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz" + }, + "fs.realpath": { + "version": "1.0.0", + "from": "fs.realpath@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + }, + "glob": { + "version": "7.1.2", + "from": "glob@>=7.0.5 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + }, + "graceful-fs": { + "version": "4.1.11", + "from": "graceful-fs@>=4.1.2 <5.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + }, + "inflight": { + "version": "1.0.6", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "jsonfile": { + "version": "2.4.0", + "from": "jsonfile@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz" + }, + "klaw": { + "version": "1.3.1", + "from": "klaw@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz" + }, + "lodash": { + "version": "4.17.4", + "from": "lodash@>=4.6.1 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + }, + "lodash.isinteger": { + "version": "4.0.4", + "from": "lodash.isinteger@>=4.0.4 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" + }, + "lodash.isundefined": { + "version": "3.0.1", + "from": "lodash.isundefined@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz" + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=3.0.4 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + }, + "nan": { + "version": "2.6.2", + "from": "nan@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz" + }, + "nodegit-promise": { + "version": "4.0.0", + "from": "nodegit-promise@>=4.0.0 <4.1.0", + "resolved": "https://registry.npmjs.org/nodegit-promise/-/nodegit-promise-4.0.0.tgz" + }, + "once": { + "version": "1.4.0", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + }, + "path-is-absolute": { + "version": "1.0.1", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + }, + "promisify-node": { + "version": "0.3.0", + "from": "promisify-node@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/promisify-node/-/promisify-node-0.3.0.tgz" + }, + "rimraf": { + "version": "2.6.1", + "from": "rimraf@>=2.2.8 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz" + }, + "wrappy": { + "version": "1.0.2", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "readdirp": { + "version": "2.1.0", + "from": "readdirp@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "from": "balanced-match@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" + }, + "brace-expansion": { + "version": "1.1.8", + "from": "brace-expansion@>=1.1.7 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + } + } + }, + "regex-cache": { + "version": "0.4.3", + "from": "regex-cache@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz" + }, + "repeat-element": { + "version": "1.1.2", + "from": "repeat-element@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz" + }, + "semver": { + "version": "4.3.6", + "from": "semver@4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" + }, + "set-immediate-shim": { + "version": "1.0.1", + "from": "set-immediate-shim@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" + }, + "typechecker": { + "version": "2.0.8", + "from": "typechecker@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/typechecker/-/typechecker-2.0.8.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + }, + "v8-profiler": { + "version": "5.6.5", + "from": "jrieken/v8-profiler#vscode", + "resolved": "git://github.com/jrieken/v8-profiler.git#5e4a336693e1d5b079c7aecd286a1abcfbc10421" + }, + "vscode-debugprotocol": { + "version": "1.23.0", + "from": "vscode-debugprotocol@1.23.0", + "resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.23.0.tgz" + }, + "vscode-ripgrep": { + "version": "0.0.25", + "from": "vscode-ripgrep@0.0.25", + "resolved": "https://registry.npmjs.org/vscode-ripgrep/-/vscode-ripgrep-0.0.25.tgz" + }, + "vscode-textmate": { + "version": "3.1.5", + "from": "vscode-textmate@3.1.5", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-3.1.5.tgz" + }, + "winreg": { + "version": "1.2.0", + "from": "winreg@1.2.0", + "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.0.tgz" + }, + "windows-process-tree": { + "version": "0.1.3", + "from": "windows-process-tree@0.1.3", + "resolved": "https://registry.npmjs.org/windows-process-tree/-/windows-process-tree-0.1.3.tgz" + }, + "xterm": { + "version": "2.9.1", + "from": "Tyriar/xterm.js#vscode-release/1.16", + "resolved": "git+https://github.com/Tyriar/xterm.js.git#d383d25c4e879578a2dabc3381f039b246abb34b" + }, + "yauzl": { + "version": "2.8.0", + "from": "yauzl@2.8.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz" + }, + "randomatic": { + "version": "1.1.6", + "from": "randomatic@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.6.tgz" + }, + "rangy": { + "version": "1.3.0", + "from": "rangy@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/rangy/-/rangy-1.3.0.tgz" + }, + "readable-stream": { + "version": "2.2.10", + "from": "readable-stream@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.10.tgz" + }, + "reflect-metadata": { + "version": "0.1.10", + "from": "reflect-metadata@>=0.1.8 <0.2.0", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.10.tgz" + }, + "remove-trailing-separator": { + "version": "1.0.1", + "from": "remove-trailing-separator@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz" + }, + "repeat-string": { + "version": "1.6.1", + "from": "repeat-string@>=1.5.2 <2.0.0", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.5.4.tgz" + }, + "rxjs": { + "version": "5.4.0", + "from": "rxjs@5.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.0.tgz" + }, + "safe-buffer": { + "version": "5.1.0", + "from": "safe-buffer@>=5.0.1 <6.0.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz" + }, + "slickgrid": { + "version": "2.3.6", + "from": "anthonydresser/SlickGrid#2.3.7", + "resolved": "git://github.com/anthonydresser/SlickGrid.git#fa7911c34b5449f9ce1e7148480fbc24fd20743a" + }, + "string_decoder": { + "version": "1.0.1", + "from": "string_decoder@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz" + }, + "symbol-observable": { + "version": "1.0.4", + "from": "symbol-observable@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz" + }, + "systemjs": { + "version": "0.19.40", + "from": "systemjs@0.19.40", + "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-0.19.40.tgz" + }, + "underscore": { + "version": "1.8.3", + "from": "underscore@>=1.8.3 <2.0.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz" + }, + "universalify": { + "version": "0.1.0", + "from": "universalify@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.0.tgz" + }, + "when": { + "version": "3.7.8", + "from": "when@>=3.7.5 <4.0.0", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz" + }, + "windows-foreground-love": { + "version": "0.1.0", + "from": "windows-foreground-love@0.1.0", + "resolved": "https://registry.npmjs.org/windows-foreground-love/-/windows-foreground-love-0.1.0.tgz", + "optional": true + }, + "windows-mutex": { + "version": "0.2.0", + "from": "windows-mutex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/windows-mutex/-/windows-mutex-0.2.0.tgz", + "optional": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000..00b67f281a --- /dev/null +++ b/package.json @@ -0,0 +1,172 @@ +{ + "name": "sqlops", + "version": "0.23.6", + "electronVersion": "1.7.9", + "distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee", + "author": { + "name": "Microsoft Corporation" + }, + "main": "./out/main", + "private": true, + "scripts": { + "test": "mocha", + "preinstall": "node build/npm/preinstall.js", + "postinstall": "node build/npm/postinstall.js", + "compile": "gulp compile --max_old_space_size=4096", + "watch": "gulp watch --max_old_space_size=4096", + "monaco-editor-setup": "node scripts/monaco-editor-setup.js", + "monaco-editor-test": "mocha --only-monaco-editor", + "precommit": "node build/gulpfile.hygiene.js", + "gulp": "gulp --max_old_space_size=4096", + "7z": "7z", + "update-grammars": "node build/npm/update-all-grammars.js" + }, + "dependencies": { + "@angular/animations": "~4.1.3", + "@angular/common": "~4.1.3", + "@angular/compiler": "~4.1.3", + "@angular/core": "~4.1.3", + "@angular/forms": "~4.1.3", + "@angular/http": "~4.1.3", + "@angular/platform-browser": "~4.1.3", + "@angular/platform-browser-dynamic": "~4.1.3", + "@angular/router": "~4.1.3", + "@angular/upgrade": "~4.1.3", + "angular2-grid": "2.0.6", + "angular2-slickgrid": "git://github.com/Microsoft/angular2-slickgrid.git#1.3.5", + "applicationinsights": "0.17.1", + "chart.js": "^2.6.0", + "chokidar": "bpasero/chokidar#vscode", + "comment-json": "^1.1.3", + "core-js": "^2.4.1", + "emmet": "ramya-rao-a/emmet#vscode", + "error-ex": "^1.3.0", + "escape-string-regexp": "^1.0.2", + "fast-plist": "0.1.2", + "fs-extra": "^3.0.1", + "gc-signals": "^0.0.1", + "getmac": "1.0.7", + "graceful-fs": "4.1.11", + "html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#v2.2.5", + "http-proxy-agent": "0.2.7", + "https-proxy-agent": "0.3.6", + "iconv-lite": "0.4.15", + "jquery": "^2.2.0", + "jquery-ui": "^1.12.1", + "jquery.event.drag": "2.2.2", + "jschardet": "^1.4.2", + "make-error": "^1.1.1", + "minimist": "1.2.0", + "moment": "^2.15.1", + "native-keymap": "1.2.5", + "native-watchdog": "0.3.0", + "ng2-charts": "^1.6.0", + "node-pty": "0.7.0", + "nsfw": "1.0.16", + "pretty-data": "^0.40.0", + "pty.js": "https://github.com/Tyriar/pty.js/tarball/c75c2dcb6dcad83b0cb3ef2ae42d0448fb912642", + "rangy": "^1.3.0", + "reflect-metadata": "^0.1.8", + "rxjs": "5.4.0", + "semver": "4.3.6", + "slickgrid": "github:anthonydresser/SlickGrid#2.3.7", + "svg.js": "^2.2.5", + "systemjs": "0.19.40", + "underscore": "^1.8.3", + "v8-profiler": "jrieken/v8-profiler#vscode", + "vscode-debugprotocol": "1.23.0", + "vscode-ripgrep": "0.0.25", + "vscode-textmate": "^3.1.5", + "winreg": "1.2.0", + "xterm": "Tyriar/xterm.js#vscode-release/1.16", + "yauzl": "2.8.0", + "zone.js": "^0.8.4" + }, + "devDependencies": { + "7zip": "0.0.6", + "@types/keytar": "^4.0.0", + "@types/minimist": "^1.2.0", + "@types/mocha": "^2.2.39", + "@types/semver": "^5.3.30", + "@types/sinon": "^1.16.34", + "@types/winreg": "^1.2.30", + "azure-storage": "^0.3.1", + "clean-css": "3.4.6", + "coveralls": "^2.11.11", + "cson-parser": "^1.3.3", + "debounce": "^1.0.0", + "documentdb": "^1.5.1", + "electron-mksnapshot": "~1.7.0", + "eslint": "^3.4.0", + "event-stream": "^3.1.7", + "express": "^4.13.1", + "flatpak-bundler": "^0.1.1", + "glob": "^5.0.13", + "gulp": "^3.8.9", + "gulp-atom-electron": "^1.11.0", + "gulp-azure-storage": "^0.7.0", + "gulp-bom": "^1.0.0", + "gulp-buffer": "0.0.2", + "gulp-concat": "^2.6.0", + "gulp-cssnano": "^2.1.0", + "gulp-eslint": "^3.0.1", + "gulp-filter": "^3.0.0", + "gulp-flatmap": "^1.0.0", + "gulp-image-resize": "^0.10.0", + "gulp-json-editor": "^2.2.1", + "gulp-mocha": "^2.1.3", + "gulp-remote-src": "^0.4.0", + "gulp-rename": "^1.2.0", + "gulp-replace": "^0.5.4", + "gulp-shell": "^0.5.2", + "gulp-sourcemaps": "^1.11.0", + "gulp-tsb": "^2.0.4", + "gulp-tslint": "^7.0.1", + "gulp-uglify": "^3.0.0", + "gulp-util": "^3.0.6", + "gulp-vinyl-zip": "^1.2.2", + "husky": "^0.13.1", + "innosetup-compiler": "^5.5.60", + "is": "^3.1.0", + "istanbul": "^0.3.17", + "jsdom-no-contextify": "^3.1.0", + "lazy.js": "^0.4.2", + "mime": "1.2.11", + "minimatch": "^2.0.10", + "mkdirp": "^0.5.0", + "mocha": "^2.2.5", + "mocha-junit-reporter": "^1.13.0", + "object-assign": "^4.0.1", + "optimist": "0.3.5", + "pump": "^1.0.1", + "queue": "3.0.6", + "remap-istanbul": "^0.6.4", + "rimraf": "^2.2.8", + "sinon": "^1.17.2", + "source-map": "^0.4.4", + "tslint": "^4.3.1", + "typemoq": "^0.3.2", + "typescript": "2.4.1", + "typescript-formatter": "4.0.1", + "uglify-js": "mishoo/UglifyJS2#harmony-v2.8.22", + "uglify-es": "^3.0.18", + "underscore": "^1.8.2", + "vinyl": "^0.4.5", + "vinyl-fs": "^2.4.3", + "vsce": "^1.25.1", + "vscode-nls-dev": "^2.0.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/sqlopsstudio.git" + }, + "bugs": { + "url": "https://github.com/Microsoft/sqlopsstudio/issues" + }, + "optionalDependencies": { + "windows-process-tree": "0.1.3", + "windows-foreground-love": "0.1.0", + "windows-mutex": "^0.2.0", + "fsevents": "0.3.8" + } +} diff --git a/product.json b/product.json new file mode 100644 index 0000000000..967ac6a6fd --- /dev/null +++ b/product.json @@ -0,0 +1,32 @@ +{ + "nameShort": "sqlops", + "nameLong": "SQL Operations Studio", + "applicationName": "sqlops", + "dataFolderName": ".sqlops", + "win32MutexName": "sqlopsstudio", + "licenseName": "Microsoft EULA", + "licenseUrl": "https://github.com/Microsoft/sqlopsstudio/blob/master/LICENSE.txt", + "win32DirName": "SQL Operations Studio", + "win32NameVersion": "sqlops", + "win32RegValueName": "sqlops", + "win32AppId": "{{E34003BB-9E10-4501-8C11-1234AA83F23F}", + "win32x64AppId": "{{EA457B21-9E10-494C-ACAB-1234DE069978}", + "win32AppUserModelId": "Microsoft.sqlops", + "win32ShellNameShort": "SQL Operations Studio", + "darwinBundleIdentifier": "com.sqlopsstudio.oss", + "reportIssueUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=customer%20reported%20issue", + "requestFeatureUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=feature-request", + "urlProtocol": "sqlops", + "enableTelemetry": true, + "aiConfig": { + "asimovKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412" + }, + "sendASmile": { + "reportIssueUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=customer%20reported%20issue", + "requestFeatureUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=feature-request" + }, + "releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=862039", + "documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277", + "commit": "85fd52b50a4bf6dec0f5314d22535317fd5167b2", + "date": "2017-11-02T12:00:00.000Z" +} diff --git a/resources/darwin/bin/code.sh b/resources/darwin/bin/code.sh new file mode 100644 index 0000000000..d05250d014 --- /dev/null +++ b/resources/darwin/bin/code.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the Source EULA. See License.txt in the project root for license information. + +function realpath() { /usr/bin/python -c "import os,sys; print os.path.realpath(sys.argv[1])" "$0"; } +CONTENTS="$(dirname "$(dirname "$(dirname "$(dirname "$(realpath "$0")")")")")" +ELECTRON="$CONTENTS/MacOS/Electron" +CLI="$CONTENTS/Resources/app/out/cli.js" +ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" +exit $? \ No newline at end of file diff --git a/resources/darwin/code.icns b/resources/darwin/code.icns new file mode 100644 index 0000000000000000000000000000000000000000..a7cc8d2c8855ddf31518eaffa32a6054645ce308 GIT binary patch literal 43649 zcmZsC1z23mvh^Mq+zAjIf+k2HxC{;<5Flu93&GtX*Z>Lc65KTqB)Ge4aCf%=!FB%0 zIrrTAzjt4KuzOdn+SOg%Yj^j2(_?OA;|KsnzK(p>mH+@k1^yP$`#Fn$?g0Q#JOu`W zeMkBi$UFnr;G*=Gj)a@{w+j26fC2!9C(!sj1KZ4i1A|{_QREFa5;%@h^icDu50*!CIaz{Lx1_v!JPPz0M>qZnmzj zN-jS5b$m1_kXm-7r}8x+{#^kEK$VXSjKW~<0WjFPYL@_@*g*miR6gu)!(b<3%-w0t z^zinAc$|-i4d_8274moRc;=3r8~}je_89=fT)jLD0ss_qPYooy#^wey zH7#AJI;NHy0Px%8R<|@2TOoZ0P#}B|MI{yrq%Qy%!Y9c0G6+Bt5#SNzLI?!FLcD?y zt{?!+2QTU2fPko|ARRamK;nc5Lf8<30R(h2h5Z>vm#x3s0Z zx1*t|s=gB5(xlP$p_}YN9tEXtJDg|0* zrsgJUtEy`%D=O+M03f+?aAJOD=(~%7KY*5!l#-Vd_Ql=J&L04$CMTuFK<&XUHq-zq z6u`_)icLv!advgIfrvt&04O5`n#Av5>!=D55*8L=1dOgHlP5*e8KY+Qlv9-53*w-`C z4h5j_1W(sy*LQ{oNBUZ_zhy%K;r+GsjjitPzKY!J!mPYlU}JrAZN9&2s3SWRNX>x) z8yCmh3p1mAmAUCaMotnix3#~$JUr6Zl$l+eoe=|1aARksx4Rt%Waef;0js~>Kk$Fv zIXD;p2;J$< z{v`2s9gh4a%=ka>9b5ol{rwl9BJzLWWAF*=ul|C;1`w0~+W|fS{yiVTp)lkx$N$sg zI`mKV|F)$Nk|})nBgU40$w#)oTK{hxwjHnWTI%)d*9?Ez|GE4J|K~3AALy_0AM1b0 zKeOC_zwjgZe@&r(yZskl4r=@V1Hf^3VhEtW>wqH~fcgKAX9Azo0l=&R zK3Bp82HPf-BtQAL!>=$`!0xfD=cxRj=M$e!V-@`y&jFC1&+Znc&mAj)u;4Dey|0X5 z?_^;+y?+a*_4NEOH2`4cob(mL+klRS-UTZ|-_t}y|4RUt3K-9y(Ae8^GH`rhVfg{} zo790IWpM#6OWo84JAc14RYiZ?{;t>t;o0BRK8+@{IK}$|0No{jUH@|E0c`$uTmX#E zV77k%$lZM^oS<|Ee}G%|*1vI>3dAb(#`PJg)^5G-hd+KoQ-6@Uzi9@8Wx=~H8g_F5 zYsdeC!Ufm1@1Op^8gL+jTO!vL6>A3nVL$uSqbHu-%5HkV*F@wL~P;;w4 z>kmqNIDll~@yX5I^DBVr;_2<_;q7F>9}Qp_*tvRmdwjCe)7I071h5@FK7Dfc@^ClO zfGfr#Is|VD})5XrtSi@Hnq@x}NARD=PdAJy9K{fm|5w+Ac)ItCxcc_Q6u11h1 zf{w=fF92#%b#;up_a{S*5KROvT`fNVt*E}Psiw%y+d(7jkCP~Tx~i&gZ)&I+arL!; z+fcOLD*`}uT{E-}sino-#|&;m*4E$zfW{`@I&gE1n!5x1Ni97U_*(F_$q(*T16XS6 zgy@1aRDA&i>2J-xbx8Hi4IK<`pb7{YdK&&JAax}_0Ez!ya#M2yv<{`UzP@#6c|tf0 zz=QA$eT*roYie%k=xHkrkU$580$31;ARoV!x~YSyf+!PiB;X4{;uRF&+pb%sM zZUG2Hm?{=PgBSb&pYXpG0YE_XUy2Yv9Um{B&|hxCqJk`ZjDo_V&-~z<0(eKl7hfJx z0WM)7vN!;R91s-c1qArx;C4Yi0U-fKK6xB?aA6@K2t-r>cuf`yfcS->5HP$lF`8Ha zfkP0kAquiXp2Y#EJP;9Kh!8)7>Q8Jx01-}PPym3+&I=I`77?WaB>-S9K|aW{SO7lm zMcrLpo$c^(Z|Ujp=^E&0EQ$s&Gs;@J`a3(DYie35pb-FCQ)gFaM^9&GeI>LC)L0I0 zZ(HwBZ%;={OMRtp6{w*y3_z{!>g#N;ud1x{uR^S@tSN#Ie8*5{drf6v6}Y;w_6HQc zgjUXv#dP#{)>Vd7A=ETg`2naslT))3tsVWX;Z+Fm=|D~g;3pG2_O32X}?ME!h~8=T}UHh zQ%wat975*sG~97&etIP02ecHSs;0)T9HFwh6h8RLxjoac zDd1~Naw;72wMTcfb#{OQ5Mg0Ke*irrFDWV|I4LtD%+c2w+1(z#y%QCN3L%O>00MZ| zC8s7N<|ig*rvzI2+G9C53Wx{`^8o;Xh$sZUOG1a*LzD9op~)$LiJv?e;1Utz_XQB? zgoFj*n~r1@c&lT-=6g8#*@GS2_=Ktb0FaQdFMyVnmY$N5l>}r5`Z^&y+uOPe2(kVP z10`iZQ;;%}fbXsX=!3_D;^9Y@q-$L_!Gu$NU+e|tl|O(a$PWO#0{(x9+`7KLw!R7{^3wM1*81MYQV*QSt)mNDyK8Gp z^9x%ua3aI^aGPsuTWf2JzH^}U>2Lsbaq|#vT3%lCn**)RgaJs?8#`;Oive?p^RtU% zApm&gaBX!yXbwETwlEEY@1aRwG6So-YYU-s2n!oCa0ZjV;&{altnaLZ&mpYL!Jq&d z`zuZ^4mM!(U@3eKWpR2GzE9xbgmQp6$zeOo;d96u9CgJSe>(^yrVTW>{ak4X# z!k0_v&!!Lnd2@H8*j)S-lm(TQom14{Yxh(SoYR||Tl_xJ0kL@v1B1OIaI5bo z+S<t> z_3J_Jo$dj$;j2@2b_|@tn>%X@YYQ9OaLl&{y{~hi8^|uq$@-QJr||yS`oz}k=Hc$* zfbSsIL@xl%$j{Ht$t%o)1;hJpV|R7qVtwOiW7@Zqymz28qaeQ^0RV#Y3-fbe05H@u zwEla&a&twk(k}@Nq~?E1^#$-^a`Lm_;QIR3=Jv+y`u50xZ#Pn3e{Wh26i(EfY+nHV zXm@vW^Vb?|b!q_Gk387h+mVs;6%JF(+29h~ffvpBab zC%ZH^70%+(?d{EjQS3<#g$>FdC)i-e4WNgH|&qZEw1zkuOyfK3G9R`V3%34WgE% z(F~9wN@}#8Sf-0n+-19HJ*y49BMRai-COL{p;oT4%Snl+=l24JEw8=p2WWvvxBhrF;QB!{+PHnv z*|cir>C0@ESUTq18>Xv+hMS-1tR6e8_0}KUy$0o7A61Az`A><8mKGwfljwq4V{}U) zDieU4V9CQ^+F%fM2*(Y#e)Njvz30e{kg$R!{RPWmoFudnUA@+!TI5J(YAe}>RBn6t zlZz3xhns0~8~qbhV=c%t-2S4JCSh2uaXg`juG=YeXF}PK6dldhku=2eR%-F_a@xk7 za`Z#ktx~`?Bj(*FZh=`}OgpThY^sEr@;+dde?T4sY5=7 z#+2MOw|?3i2l=4;%n)Cl&d(Ab^|_@bu!*~K721qSE~)#ha<0DR_8$Owwk@Ic zyD-p#z_Qw$=P7#ixiAsV$5U=q>gkhTg69K7x7CN2nfn8!PftkNLwKY`<;1??~FW!mo48&yK zI9KO>9G#J#zYsWb(0uK=rN#^r}h}d)$TJl!gwEHrot1D)7C&-Yn4#% zk?KOud1n5-zZmD_E&F^V=vwaA1jzFjzsk%T=SCEF>w^Th)M0xpig9Q!*6ilqMBZGj zuv7YXEnf7me0uiZtTkO+Uq-1(N8pq%V#}MTu^t?<2Dy(}EH+f#^1XRP6X}xrDC-*d zO2k0peSHbp##cFt6La19_=qY-ETrp|%G2=d7Y_v;T7ooZ5Pp?DmNKOZ)q+pLQ*58Gp& zpbsM;?2?dNj13zV3ror0;pgESS4hLUqH~f(5Va6!Vlu-tdTZ;PFf!O|NNC}_8he*vp`~&D0mj#rP2AP= zEp6oBqAHCTkmu0VH)Lc=#3dZjyUdc}&zPgM3!15Dc7dm%uV0TWM78Y%u72tdl>Rxrobd5Xdho#z9C2jTC87#qnS|1(@L$%5A57Fp!F;N5d*2u~LSJ z+BAN@qsB{9qguTyC%uyYq7cZ$#qzVQC{V_o_#}m9Rl?fu4Qfg@<s%P_3x03S9YwsWjKB194HpfPwvbUCXYlMOPu`fiItY2+4jTR{0nUrqaHea9yCgzCUHh| zE*Ad;$1Bixl>Or#v?Nx3R~%qbbEwaM6H@h|$5Q{^=3L^7rc4LFVa01IE)n;ujr*^% z2k){@_6Deu)~pG(hV??JPzdDH7{#QagOlCSe%9{fPQxo zbsB5#sXpzLGx?3J{^8Xw0azF@xj;tcEIKt_sM=rfAnzN-Otr8%p6L6{wTcCL#>TeJ zoHX^_`xT@BHnl>vEzWlKMhO2-q4jSnGDXG0d}W;+72xn2sfplMfok3i-_iH{Ec@JQ z#tKLJ(aQuZ-2E{J11%CL%W<;(Bq~TE_pjfnM`DYM^l=q1hd@NH7D||zB4LI-iC(=O zqys(L%?Q&1hCk43lFofno^^(S)zZOZR=9{M2r{?BTWr>zJ6p^F)@5&VCIWyjUvf=w zh#^BfU2EAass8WNK+$=K(2R`{{6f{4R^|KO$2p|E41%mN&tQ;sW`nO&a<@63a3gsV zP3Mkm0+&!-*H0s634)FGzW`sD7q&mv-YlER#I@p3DD0*0{CxQ5&}`@$x>PwZ-9r z#3SaREe>~eYS;_qo8O&mpS}vc@9j4mv=pYS9&nr2GBuZfYj8^TY)ptXzM|7q>U7c_ zNpvzv>*ut744I;R^LPBybacrX?1qz}0?*nG$dJQb>$g%2>)5-~=(qSqQ4H>}e_DcQ z7WQ6$8K@QE-*34^u;n1vr6+PpMCao4QA#5|4LUNV(}fo1;!(eNv3e0MA>E`2*(FS1 zYAr5WSEIgh$8Ry35t8_IgfT=djpBcUtgSe5}Mhn_6aLAm;3 zhUgpYvtRtL&%xPxlW#!i>Mqt9O9EFmZcT|>2wr2{WT8(mPw6r#-{YNuC6%@5XZm{N z>!t^Y=qwqzwEa6`q!&4cxOea7k_$*4@pPY@tx^M;)>`N0W0Y*Jy>vlOqN5O+8nRY4 z?_F4zFWe>@=`>Hw9*e>w?a{xnhIA8X5;-op*aYrY)fhevRkUWngfoN&*;=LOrwefdK7HO?*PWTnWCyLO=due(Zn-nu4=+!)L;O3{c8l+Z+H!@^ul8VrmM#!*?g`CI5j-U{gu7VZ99V!{i1|W!=JpgI#`hhi#|+GK~d} zxS1z8!d~nMFsoQPJAG58=S6fMP>#38wtODvYFw?CYJ;0CxC19W+UCqG7Bajz4%P=g zzO~1tW_;T`dK5FC9Cg4+q9GLKZZIF>>_t0Ww$kJnvcgj$;`bJ+r2oB~(Wvq@=MxRV zk8bivh=&*RD(?$$6TBdWY?&PLGam$71T;VWB5q%N`6F}`i`zlkN}W*K1mo^irR@;0`*RyVaP;cnF6@>6w4(2U@{+~M zVd`}4>zSL#&|90clx;S4oLxu;KZrQ<#A_Q()yYvI*_4t!x%RhZ``gH=>M=X(Vy6+h zji+T|$ZjArnD^~|0O%oS1!gTC=|kcTvz4l)sD zS7;3l33Xb!IuA{_@ZcFa(8`fq3em_c=9Xx{FK+?89o)IvUrN@^kwj{L-1uzMdVcfZ znpmUw%%~tt+QtNlRsfAw-|tRy_}I%GcKJl4n>=Y8^70o82{Mwr!{s#|)|*bmIFU`| zZjS_8%G*x2?g!@&SJGZBA%ph~?^82ku{NZ4V^jPk2$S|YTcBS8uPB?mhevzqN^Zk{ z3EOvOg|Y{7DfEi(_d+7yDRuQGSd%+xI&ECLk8Cb8U#6ePpX@!Nd=%pw(HNcR_(ts3 zlAxopUu#Hme71bnk-f55SCsy8_Y2y)Fxg z6KWo=X#bq62ym+o%$uXbXDjUpvs=>Py88VZR_+DW0s_NzfHO$=P+f za3>n^wh%eM*&Elx89TYOjke*zR_UW!3woR-!YT+plU0a=9fvu(eAdS4ei#W&*G@@@ zO+k`)QXgrIkw#xwQ)sV|TzB;LMa+c(GWN5WqX+Md^6o~{m2_7VePE*g9L4ZZ<<4#- zTC#q?{?-t-qFk`ZdZIJu_6BMmL7+x0&&j)2^LP-7Ld5nKX3CpDde19L-!OMe18E@e}d}Hwo6o>$hC9V-K zS-dmXDr;4$y_e4f%jTSd7xSsPPwrT;n6hT%25U1tE%%B@I4|>9F2SwUH@rU)@le~8 zPUIytWnZMNTx4)nEeZa_OEefMdTL6Vv*8zHu*;71>Ptz!HEX4{QJ(aS$m6@Y1V(g* z^z%w@Bd1~&Q%nTA?`Rtidi12@mqN)u-SMgi1Xig**eX_40S?_^sGe_fSQZi)pvXiN zh_D8vms~ZgA4|(vI|Xb)I(}?wn=60aq*deE^TjF4GtNlIIk@1VM)&4*G18)Ttc06)0Dhxf|H zaDAmm9rQbJrsaPXbwjCV#HYHPqz79k|KzhHi5jyT;WkH)lZqGL8lb?>jY|z3H|T1^ z{Ay-xb~CcD7b~H%`#qvesdZW^t|U%W_zTtJ>r_%=61P^+0koX0LSQlz-*k3>hKwS~ zsqQ;>$*eGbTZU-h+l3FCofcCH)FKtVFXe=%;yqn|tt&%MMmM7U?4<*l8pkXJe`Ryu z6^v6qk3g=uVXGbzK;6LRV@IG7MV4-G>tt-ILavL{!Z)1y8YJIe(WfV+I`3Weq`B1y z+zgv~BcL^nEN|jyY@n#xf^23UTbTVGj9Q~yq{S|9cqGnzx%kEsTSsh3+bVOJbw)+~ z4W@gktuLk)q4DaOBR>~JoUcJnmDy15>8tn~Ctix@=2B!h=Xq0&fj_q;n%hJSkdmb# zh>rv~GPI)Q8RQ{Qr43G@^SS8Y^P92LI7E8onfqa0!ZR~k?>Wr{HeD*-c=P)NDEH1#9UULX-EsK z4I|nDh$`D?#JhpyNY#>}?PKz80DT{mHWB}AiX0+`F4q~{cZfAmPb4hR9K5~}dI3T3w zu@kyLt5lmUA7v&H&84BUAw@)qAolOehz4UXh>fXRIosM8*%7YGX}8GYj>8zKoK(%&3{3AuSz8(z0s;=G)_NUl&y`34Jw0M+|(l-`2km!bXQ=Ic5xVn$^a zC-L0k3k0e2FH6X|Du?uqqi-D(iq9?>W83>ZDO zd-FK(%P+V^GCmxeoUYvs4GYW9EdYp)SN*CmQ!ZyA z0*239JldWu;4kBgvMMA+oi(pSt!s%@90<7mFni0;6Zjk_4^Ow3ZV4q!GUP;J;heUt zFzUNp096`N+>v+StuX1D66+pE?3+Xgg6IWCTdAJdG zyy{R+pA)q@%2p%S%K#xb5>dYOHN30Ih#Lov+3hdC1qv5jrVMn#diFH*S3mFUS2gtl zjlayhjM9o}E$g1-9-yIcepa?i5MO3^n;2Oac8XxgimgWB*a_w0g7L0UUbQ}XaM+CX zDCK?-RQLL=GEe5IjyZL*Td&{BP$(-RJbsvqhUqo|37DIfFJ-}zr1UOrzDY4v|2Zoa z3OcUq6}olMnK8 z`>>!}vZq}>^LdN)QdqNT+b3Kdr3c(lRk5UiJc{$DXwWmBXH$D~MBGH-V#~DWo)@rY8>hXo`p=xs0UO`7qpg^(I^< zMhV_(8o=?)uIt=*F8YD$buFkMP(ER`VW6tZF+k+r`6}q@46Ms_m{6X^OViLVe1ec* z@G}1<;p>-qKG;1Xm+27Xb~E(H<^_z2fa=&}(l7KGa{6A_zT5Eues2oho4&loRL#k1 zz^0w!X^bdPvsJyq@{;(8_cq(b!^YQX(aRTKv!eGMc8Xu(4P^AKJ`LmL(w8#tJGR|C z$Up0HY}L~g3n1|6DoNhUagS|5P$cG1GHj4EdU}smz|#U|C}j(9oxEG6b>+qhIKi+# z4MJC^MddRYy%5|V-*6_qm7FnnvG-U)0@AoBHV-i9F7no{xlCnd+TR=6Y#$_uOgDGx zCBti37HO$oNXVuZ=U(h`Mu6P1YAHlUl&W=w1ELGP4rYtp7;jD&F=)zq4quwAqF=kT zuvC)gI#FGRrun{Ikk9uXT0;XTwn;B+v*Vw9r)?a`xliTY{eJVxJDzqvW?dp2wXI11 z`uOg=ElXpH&FZG?WA7JotHV*`OzHHwRv%Sbq=g`V$cq|o&be6(Zq5%i*DTb8rk@#$ zT>Rv`94Q&-52WoZiITWc-3CiPUst8xKPCa&8n;$3QKnl{svUuvVjQ=C=-Dbaw4~+U zb{|iPQ(WI@QPsP4$LT>XC$Tcg5*h9mlNUhltMq->O7O!g{73u4S7T?Aj zkA5jySYlY5;JbU*CVU@y2Px;~ZCYP%Y^3m;y{iGPyyQ99W0}xH&HBTB2VI7H-D29E z^LFwc*!IVs;iWwsR&s48onaX?Sy;Gl zc*NTHZ8uq8d`g%EwSFXtr(@y=IIC_XYlPJ&)GRuH}wDv;&;SwRR74+1nw8& z*h)~u@7bbisX`OgEKxZNcRJk-`7N5`u_Q!@+2X-eki^zk95iaJIaJH9R0)JkwcdXZ zh-?;?(AhR$T{__yl)WUV9uaw97E|+>W9ZX#Gk870)$C_R71;O`^nqT)(ewkC)X(V? zoxS>mwPetQ2 z9K5zU-P{{*O}$Saex&!(M(k=Jy%%Yg$?zmv>e|e}IusVFz&P7WJjs%qMA%gA|F!(8=71n32bEUNz?d&b`4H*)c?%Ha$to?+3nU;FBDt)0pTsLjixZV(&3* zv0;4c=Jnp7HNpkS-)54p-3Pxv+P@H$EGh~FAu&7tG*0-E*jgw_URG$ab4avK*L16X zZCU7_pw)1!@CI8E3*|QD0x>^}A|U1DX^1Hf+A}$~me=*Q7Z)FIcZ}k6#VZ=IGE8<9 z{ZDR$XW5tWj4f=H#>me6mCWxRc5gqBHlEF zoX{mtL6H=N7N*cQn$EK{8beoYYmnRf*0-jdX_Bimb-|Bkon+5i!n@4nT#W?g`_>gF8Igp?y1Krn_(%i>j-u_|g&=B< zwzUAMLbu!u)FT`?NL_ENX}s(%+zlXEL$WQb&f-8f{V*;exsn>UpV7QOYA6Yz5-C*< z=}YJpN~qpP6En~|wtioMoiw!mY`@5P#;ky{p-toqzGtne`8uuB z%3&nG#GeB`?C}Fu@ZMuECtz++8GMlb=~LDiKD}5j5bq5};EN@G_mNvi5|NVG28UfY zG3Y)p8CgL=X_BZy`&#UI>Hh1PW?Nz{_asMRg6E)t`876vC4w~UQ8}R)D{c9s(j(of zTULd)d?D(avel!|<>(3)6+sED00NoeS~S@*q4|ohzUX3dSd2w=PctD}t`jZu7;jR= zDIYdl2!r)emS;PJ-{O~it)X@*B?R$dup=?$4rY;h$51BWYmMA*MhGwT3@xaGr%nhS ziTMIFDXsN{FHeP7TOls%-#Vq4+ky)s3qoMw&-MM_Umc6Guz7t+PfvAY`u$v@&8tzo zE5!co)!xt5l1P0QQX}RFZ+UkL7twoitr8m(6GdW;6|IlmN5Z^G;;D}2KTcxhR*iJn zq0$k*rdnaiZ-%K=JLf~o9~ zFa79`o1J#{^P*Za3TOj)#aLx$PcRcPnvlAn(~;YLqCev7Ldsi8t-|Ud{|fTc!cb>TO4bgxLGYLNJ>~j zp7TrKeD_>$Z<8KD<;l;W>paCAg=Lv^fz$?CZu^~9Xiy0evbFRbT<0k;k|)J`>ZK3y^PxPSODBApj`YSFJN8leAQG zg7>gr*PpJPyCZz9snQ1w-_b(#?0;kCCVoZjOI&J*{@ z%Z>QRQ)qiZx7PH>b_+aSH@*8U#wE`@j!V-Y*3V#+qr?}R&bNNrtovkZZ?Konln?$^X`LA*JWa}nm!nG*j`uEZnbL$c@l|*9I?$2I-dJjI~&*8-C)0{=P zJgeKvrLB;-GM`Oguh0S!s1-iP@zZU4Fx&YguyeCxcFq|KWs!-r82ftYV>;bL4+yEm_jj@|kX2zs@L34Z_1{_lrV7#s_gWX*`Qb35b3ydjVOWV<;2NRX925Z zi_FfGck1P&zetR^htx3L3hM~%Hu1G1Rh#lEZ|jUtzjA9*ylEaGesZ>~{uon|KoRn+ zQ9=9!XYDO2wgjq@_Z+QE7?E_%7Yn|I2#D(l9Wh60Ph%u07dui^d4UW; zS?IT`oCWIN?aI%0AL0a4)K}bOrrB$yaFjD0jw;Hy<7_U zB(ppy=5(Oo@cyVC)M>u?Y1M|jLJzrbHTAg3V&Byb1Iqg_{6k%3cuTW>=j24~U?)0B z&!h0kC4FKrHf`M_4(}mI{MeOP@U8&6MDO!6bh&=9ybi=;B|5(FSuZCp^YMDo&jmlf zN`$5QeRe^g4X(a$NmQYyD%x#f4Rg0xiLKbbk%6d`ZHB&0#qVuK6np5FZk-qWwBTJs=!)V#E93@bS59Reo2Q73SHL+gv$GP*XS3X#;k-RZJ3KZOZ zsL0J>uX78SzUPzU|D4swuwgN6{lmmaF1=l2VOodjOh}x}r-`v*5&X{UG3o75TyYf1 z_O@o41W!==rkt-Fi;TzHQO115c6JLf_3vg1DAl*Zxa-(joRtaa@F{i3AbKl5UvU8_8 zpXJmk>h}xkV+~MuOy!e~ZBK8Pfr`u6vfp=Al+&`Oh@BN9AGtmQI@?eb*XmK*EFy6# zk(W>2P>au$-Y}=@2_BrM{CsaXx%j}G^_2{4}m@2Fh@ZpMI0y6gV}q9A;?%r z%zOF6(9}~Jk=WJ3&xv!hW?h;~lUEzt)NwSk*a1(zgQq1lRwR(GF;Qhj?d@2hZ|)3T z?GQ^^9@7fZ_aBIT0iAg$O6bB)^^gztV0~+{+v31EFlbfj>Mj42`-r2fVas9^ta zLg>%;rU#R1^d`Uu@ur&0g7_wjRJneQNDA!o+v#xj*SCh+-&CtkSYiqpPHksR*>wWd zgVvlfbjBfcjWkfIv8Vt~yQah-mA&nF=i2fcf`zJ((nh@P;aWfQg#?YY{
}od=|m zZuB?QZ)sLzR}r5GB*+f14G^5vhZ^BRY3Cw*hKJL9O!iwy8Vs%Q)SkC@gz@vs7lEb^ zqEI(e?9s2M#yr5vtd)8&^9yQI#y+H*Q6hym%>NSnnc)oUro?Q5-tbD-}jBGMK z+v~^J7a8aga^&r*MWG?!wX)2-fuRdDV(W_#(~RsTo~_wS_=W}I;w}=$>zGF z%YNIx2msF1C1I?|l3bBHp<$ol-@R+x74kPWDD&JQN0ow?v5iL3cU(;A*|0E;=JBXe zofwMqsie~oiSr*>+%MEEs}Qig?KoN~c0LsbnzC-!h>gtq#s(f#MyS-=YA82A?MGSt ziLd=}5%{%4%o13<@V)pdpC_5*#^jGYnbu0h`y4`<>1P}nX2I(g_R&(^03i*Pc61pc z1r$?!L}hYOHdk%_U9QpFL&@TO+Po6#b-?3!J!kvjZ2RDR_l`ImY0RlXPfc0gi0V{;H%epdTQEI3I*bIU)8nqvYb)h{+raFPThj`h5*&Vh9d9a6T6 zl-5M#?(b9v(^2ALSXbQDn-!fDUL_6arl3aZqL5x0{qU~A577*n2N-_JY@ktKknC|n z<#btvKg#^gX=Ug9u$pkMh$o-iP1;_PN6+7I##Rn(uBw0fX;-d1fqmDf(D{9B(0L0s zp&{6z1tWj;kf|nuX<2egve&UJPw2dxYpjF*vdY-K&?=?#Cq*-hwi#Qni#8e=-bont z`pA03hs{-DMxqsR=_kb4t3QP*c3T|6oDz*ORx+cke~#Khi7(NrYy{1h`BCbVk91gA-V zeEty(`Y)A@JKcB&MbwlE3ysGR*xGb$_}@TPyW3$-PRNEO9MvSauaI zWZ>9*beq?FCH47~Q=05src)ToV^8n4*$;_G_(4@bMoK}lRNTN1e!3NYB%SHif1hrJ z|4ab@0e_ut)o~~^`Cq47Uu^tyx>Y}nyx)B#N;;!ZWDJY7s{)UH#v>*xu&+0Z>ca-L zrKvOW2k;NR&l%v-+%0rZwc#@(~n{`zn!~i5KHt{wb_f-86pRnb&m z&E0NLVt1w5{Leihu3h}Zv=$5-1SIwkI1vVuU?ZJcF>>gE>rcOj)6{f|R$H;=7x7`} zr5$+Se1g(!N_(K#ZRTm!Ty%s_*Dd3_jDW~=Nb@z-on=7RxG%|b zWBSQ&f3LB#wo%a8T7PArk`JD%)g~9^d0Fz->-Kz)v-PY%#Sdq6It=?|AdW;?M*gX+h!_)C& zc-{zN6g8zLpryvn+=}$GOv_liYmB6H{nbm6PTM4g&&ZCL&j%$Dop;((-r3oG#1?pL zj7;DBa%OBB&F4$t_nU|s={3eXGOEiJkL$WK-YZI%$IHh~>`|=9Yt7oK(5@8y`;Fs^ z3J!Hvc^c@~W&5zlMziFV$?YGND_$qwJ6=8a7e9%o9OR*R?E~Y%@z(C4WDz5+;oCRz zRx}d#RLxg~BM}*G9jH*e@gzD!!bp5j)nTcv&jt$HM)jX&@j8Bx-DlE3yF$gp`0Ct{$bul?b+;YX z8WGpy9Fuaq7H5_@weHYTla@j4!A z@h%15F!K`PqA0GZkhw35dYQ!QVGR&u`Y{qxv_@LY z?s?fU2d8DExL-?91orzMX|GzJ$VN#5M7EBLr)*gvE!`Ft>Ps;r8LNEl{qseVAbb#A z?b52fnt<>#OU)hA*oIZ+Re8OtG&%%!x4;el$Cs)O=_{Ws40qnGRH_u?B9XtV)ffq- zg^rBYZwR~53y4Hpw5MhaYe+U_lrSbC#5Hvn^XxO)*;lWeBDLne2#t6e^`rTDCF>qO zD6TGc_g;u#rTeRum?Y*C8v9ceXhOOVm{FYB53N|z4zp^@e?{3z!h0+ z*FP&kO|$k8G{!4$qlgd`d3yIgq;5CRr(td2QcvY7pbRXbGLW1vO#=)IRurXWus5In z>Qp5f?9=iKH-PU78+ISCzj4&YD)k;l3Boq>H{yK zq~#Msl%02W$E~duwAJ#5H%D#J!|40ttazf>zjyB`B};dLrecPp(_5Q7FsNp&ZUk?% zPUO|*dvBgQFD0wk12?rXX{{-2@6*829^Oi|ezv>nGleEIUCz+=F?>+^TEZL{Pld-@ zNv2_0_>a=Qv8uj62zN)8tJRtYh^RwGMpH&~y{n%{ctu=CcpDeE++&jb9C!fmK`h<^ zE>BI>5;|vW5AP#9waWwyipu&W;temR=1Y`lpqQSoZf&34$i9AWEQ5JPc1!Uvz;31? zMLX+=2#GjKUL6*7pk%Cf#YJIv9Q}FI_+}pEOasF{Vid=6pim#E?0y!AE*jj%lA#{v{wAY6xDvBu=?U%uKL}I+e{KHSsRUkStmHMhLd!6`U^ZK)SHVUn>KV$g+`|5 z3(D_su)YnW1tA7e=E%|jzYbilMRscPI=O#Ps4yx98ZsXP?651K=>j(_)SLO7|41O+ zc;?{Hkf~hTnfCoM2srU3%;Y?OiVt?cJy`?alau7ktpiMMS0e4B?#2ljV>>m&1EpEig$od7sc?r(ljQBkL0xIhIL{UQh1k`VCJ1ubzNY1zjegZ- zD2?y+$$chrH03Av83(Tp%-EJe@D4k6D1&<$lv(LIb|{>fB@oF(af!WY!5Hx9h89O$QH$YC-yV3Oge=0qiX0pd7E5;=}wvdcRte6`@zN5h=sWU0TLu| zjn?~m#GpCg> zmPYUUWWj9_IF|hLcdPLah(F!L)dQfbi3^nzpBH@*1-T=1}f{?-BRnQjyLvF_N zXaT23(Bv{Q=jQQ<`q*_`@Cyv%jmQ^tQtyxv9>S`|Wxmit-O_!Kh<<{&!f#2Dm;C;U zB;tPm(J=iZDuTRZrXo0s^de>Urhu<^0-tc`|Fw5lL2*RggTQBo0R{*b+}#Nd!5JKa zyK8WF3GPk^?ry;ePLKf-AV6?}26uOt<@@i;SG8OFzEydchpy`Bp1wWZeeSvE{#qP| z0*9ari18N8OmW(Q+Np~PUHZzq|G*Q=Ue$o;M+Kkr5uShs^KLidtvsdr8(^(={Z>gb zRUq$XH3mSIxrCqy0c?76edh+P%uRp9?xaQ zDY=#BYt~B{g(^)x3(UWGu!56*cIaE=u+5EcXlK{czR{%w82pgvLV_^61DtnO!U}5K z{Y{7M2T7H%t;)$8)Hh{!_9lIQVZX+RuojJe>w?#P5ajcC_@_nL57Jb@$&;%uAZ=<= zgMy)b7=vzr*XPIF-lrY9=>tNqv`hrS1g$_q)d`BB%e zH=E&9pnw9|npF@`2mIOrpw1E_=PIGEwj2eLU+}l0=7?DDkn^eV8fqk9Vv6RwfKNpe zzQld_%nOK3)hjP{EGi^)&z&jvkrvmKfDfv`z+bG`g#mvQoYUyx%#^210hCIfz1Z25 zzvQIwIh^IijQpXQWr~0eYQ~@w0~lLD#|stZ7M)#EPk0mp6A5|I{;L@u+a)CoF_a~Z zl{A#K9QFb0n3J>b4ig^XHS{*V{eqY*iBC&y1D|_@4>B;dZF_pyw;o>Jb%Ss*Uu}NSR zDtM^dxrpY$SP=4TA8}!N%s`G(%2w;a+E+l4^ zoMK~hGfE?j6Yh;HV>ApSCqTQg&Y?}i-_(;E4=msW(np8j2Klbv)}!gjV9-ZGBVBy^ z8H+SL00#NK26)!|K?6#82r)x`yJfjL$uxVxFb**7prDqI^%NE zm%lrLpZ4UHo=h?j%6sWuA|Q1aj`c#E^yw22MJj2!=Iy3%XF4n(*M~zpcE?D22cH{}pgO zS=$)C-GQmyM^?c}KnGXMA)$j+G>1w=?vF2zw`o(emA<2p)zagvAaLD^-(A?z%T1EB z;&#P@qZ5E&OTQV zh2XvggP?Wcquo=~^znYz=r5o6U7ymrZ%am9$-Ek5n`i8Y@e%AE7c9FZYYn2iZU1E&IB4swiMre~NlG!#GXlwsv_(knQDNt17l`Qgx+ zrxn4sOVlJHb}m@2*IV4XtVEGJM$K|j3*0vx?Q;th;cd(Z$Z zmM#ohfmu0T>I5m;y8QsUvBTd^#^CYODJ`{V7W74Xu7X_xFtr=~oFE3tB~;`Wk8Zv{ zM9YI9u_jY`#-szgmlW}%6PSq519Qf@OVV?Tybvl#+6UQE@>K|Ess&)J*ZtI&CQus` z#(}v@am90uY-hgxp^$tPALNP_cpY%X$~w3l`1Ad@m?|bGkQ?~vr)eS;go`Xl9ueB` za&hDT{i5HmYY1025Ksg>Cf>v~bqtE7Mghf185h8Lojjp?K2PNZ=ww5mk1i;_q0_<2 zVZ(TMNTT_Ckdb$w(aiq$pzh~il{rc;m+vhbHu;Hy0Kd}A3)qN=W$kf6ZD{1|Hz;$| zmy|pxNDNeL!J8UW4~WUOc%q_EAkZ8UYH%K{44m-(f|MA(O0@oFyXQ;tC(Lmd0N2$9 z+470+T9Uxj$Iem#9f6!4h;N8=1}y--Ko@h|1JIay0Lpv#LFWGaYs{}nb3;#=kE9ph z7loLXIUXnj(Flf8ASP&EKT3Bm-u0GwY-)ufk#B!?LvwqZGNE_kSA0?Xf^r=s?_VRI z<1>`7b#PUJ!34po$Ztzr23ThdXn{}HQy+Xdpw z#e4o6QvW(T_*kn@VrYIcoXo<_%FBLC&g`Y}Opmd5Q76)VPP2LD}?dU&8aVuAxwH#c-UEn*jP`vAJb zuqLG7+_|ZL9RwCA6EJ!hHcGtfB#_aoEKx;F$R8|URqAC`$d~|7!8s!!@&AmMEqiGo z>xdkEF=eMwwb{Zb{|PKs*?y1sy!otR2}4gB;-ZLCz2@tC5D~ZBDa>`dw~M8A=L0nf z1U7m{aT*i05Cz;%zZV!2x%s=77HwWJzUP#@!}PtpFYma+`J`;>^j8~sejK!j7`pJu zKY?0@)0lr2HE}R-qKDD9>(SkAD$1nR$`oJ<=CfEC|;h;0RCr0RonP z)n!BkpBq@@pl_9S!ba@~m&WdDT+&K5IX(+bM5 z3BzZCKtN#_uU&Hp4+Ax%5f11KIBNYoi7c9r@tO@n?x76>2YHac=153xRCFmI*kVv( zD8*cH0{J)^ICCA)>j7h^#sDvge(36v}0A z>Bg5LyO+G^6WbvN(F?3I5D-%wGzX1*5GqW9X=WyuNKOt(m&jK{OyJ(J1hcWvLnmln zk0=JSd>9ydHuQoyybdp#fj}%O$pQxYK&H0g4}rntA2LtRBD)63=cNF?$(M@IRb8w z-1zw;z69X-@uNqzrWK zC^RY+LPh-j_B-MmlQH*Gz)p2O^zum7xi(`UWId0Bsfz6us2zSBp@(W^aOlC7Ww z;ipO>VE(}89~85f3^eh(w#u6^RYZEMdh^kGf~%-oA}^vtZk3vG_uUtJyHtC`{D!Od z=+vL|3OuM*ZMj%&-Tu($G7XJZe}IET!wo`*&B{nDcGHTR$uYtvsN%Mr{9scFT6l4# z6o=Cl*rtRS<;d@4M=iklO|P3t_M%Y9hLS2f>>SkK?Zr;=57u3HawDe`?{ZbVW3`zA z&h1vco{C2y$C~ihpPPZi{btiNTTxm=qxvD0LRn*pi!TceI-6B#+=pmZNJ-BzN}11@ z`RxKdwwY_;c4}&zFS!M9Va4G1^mIt}+=Qsv9+i$DuRP<_#V>1(2@0~%bCW(0ylTI< zd*kNKLpa>hgbp2ud9eCzU1u|}%@t@KS|J`7-=Zh$uKz~A8#kZDsJ@GI)2E*KGpUzQ z*YUtWRUf?~@rD=T2-Q4d)2)5)__*qG2Z44*?~O zRRJ8sd)OjFRI>9`d785(Yv;bE0ii9OB>DzL8)|)=UJl2#!9@&BqzwkFQu|! z4dB+X9;O~cfK;A%3@ND4+T6zE{Ul=K#}G<27he>t5czpK249v>w-z%+D#Zz`l?rLp0ERhaEgG+?b4}W^7S#A&>i&1?F8VNr;tAg@s|3! zhxXxXeJM%c`JWZ9t!{yj=KPfDzcRlk(;K6`-9WOzmsaVawJD=oWe2B~kYcC1(VHJ- zygT_#+v;V1|1NL=4vQiM=Nvu3T?KQHVzbqY$_x?6@(izAVdNJ@qFFm)2S3rDD<`v@ z`P*iWI{7mjQw|_Ut_ETyQyEunU)EOiKz#p7{C=VRfUQh`U5(E#*<7ewkAmV z1?(0DCP$a|Q!+tO+r=Nw`#I6~*Q9vh$z5es)op=7!wpMl7v?Q4FZeSa%edr)2RcVt z4;rugrvs0+z+gi10LV{q%oG-(47^>!-G=l%FZWeqiVC zzTVdYp9F){{S)}prd%N9CIo(cW31{Lml}V+nFK_(-yt7k-?wsKM4TpBYSdt%aBNMt?`_`;)iWM_MUGxl1-s$C_*$*^rY*m}C_?-$ zzq1oLhSrUM=H!rDelw0)*HtLtd;cM$c9)zsekXYW+{cu@Ge5veWfV1u+1gyNP$;ByXhN$e=2`a zUK|J^T9>ix{gS+c=^U!}j6k0_{5-7Eof)53BXZv}#@F~unj^2a%A-tfBv&n0@nlI< zY7+yYRnhY{`0c+%7LL zep)EQaN{#={Su$s^A`)ghV@v4KecfMm$a>wU$cwu0On$Bh}^lAyT*C*2zo^vj=G1r*k~tpJJ{8EG*VpgUxh z5G_jARs)?9xGc7)QAaB=mDG3+TL8Zl>NiF$`64`Znj95*jUC3pJ?3X;aAh!Ud9hAT zUUhu-1j4A55l->NqwS(vKhWv*9&b)cNV3DO&MmVUPoBTQ`$R=^WZ<+(w5Vnz=GvqP z+czbj1?>KAL^5fc{4J_|Eb6WFJ-81vRozq0$}2x495MpFQW+gf)y^EcF0Xmdtg)%T zUCA1kY-iYUTc)lr9*N>_!(&a)&G>F(b6(SPN;ytn?>a{N)`Ef z&8!%_Nx9<%cjq53uX$|YVS9lbdnyNlu<9aW% zKgZ6KbkQnM0}&9S5FX3le$Cah-Y6;q59~^8ze3J|5#DmlP47wz2^indFRHOu#~p6 z`1ecpx&100m~+G;R=}U-i3%&mqTwR74&C`HD>_TN2}hJNW)Qk;igJ=-q}Y}Ej+?7` zz=ZatR!VBit*tnS04BeUR8mH+HvDoz8sa}JmUocyX9VF1Gv`_#RY{tGUDSTaI5kJ%tT+JY7`5I%`NxPL_2+>ih6ATZro8K< zGuyLRMz718U+&t8`l?~?4|v2H{MGbA1|TouDeY_2KDXTYw!B}tj+D1sy4ZVvYKW78 zk4PCug}$dSt);?RUd!Cvu?ZQkBiB9M3tegM>ux{ov~<;@ zFC!edt4OPSo%Nu=GOU;)u3v2isZp3xH`b&o!_|k6-pdV*Ew= zE~8^($8xlP_i=OUJ|Hry7HG&uRoQWcxAp=scPHJ=sL1ZRGmK3U? z0@FwW6}5$lj>imH#pNv9>*YTWdy~e__ULt1*Bm`*Ic8ubvIqo{N?9twn=@a>QQ-A| zTe=reXFz-+zVpM;`GN`7mw>%?y*pT~0&_dg)%G9jmxIFnF>%1z;xKo_{3+3-_&i=v zN|rg@p~hEl4KwDaQ9q4UrbWeBlEC9XtK_+8o9nnrtoLofl*+h44m$9vUA&+g0YJ09 zce2#Z%5>ldy7qD9gX~=}?n$6~-A4kAb9MNfgsDXYp^EPcOGvx?^+o%F*%e1KI!4Mf zdx=Ys{$mqMV`Z4tarlOx3Wbv7AO%6$!c;iKxSe}$6E66)8_`ekuZ;N7&R{3pwg}QZ z{oI}Phe46(#v1ya=yXLNhnc_SJQ9E(2`Bq-x%iY@)?o$r`)STVp65xxFlS_=h|M+;qN42X__V_|yRa2?> zYd0>u%mhT@SUru42*vQjlA(MggDTC2)*u`}jEVXl73%IH*zR9~3Zxzuzb#I-4c055 z!LV=bz46noM8@W<@@m4U?(UloUWc@N_Nua7$+i|&-JY^NMlDvPzW-|9vbe(TV!2oe zLea9R&p+1+X|RiId0zfKWV;rCXaUBsXwqPMM|k9yTmW|sleC`cMivA*0b3_u;>JrM zf#}Qd&kUj=Z_N#ubkUf)P7J%7W29UgyTR8m^WESL6#zTEcx+%Z>;blBp`ij?0gYeO zo%4(X;##!}DR$(N-K)#dL$2zgd)-otE>2;zQDdvPUW>((AKa>qivH&IwBL_QBFdG0 zL)_l!6(^3vq6(~cuD)VWUnUf^T}O6!I>Nn;6DgC{f-N)qhu5&K6||HrCPZ=CP?l+X zT;{2Ka@t#qZ70AvUE-YpE^*W}8HPzppNV!_%&LB8(dSxkB`m8Kg~QFCTJAFJ&YuAy%cKSs1zYavX)$>5h36XNJkiN%~3D0=e^P(tf$lq zxX2&RKnPXl8BxREQM=~wLhS(N2D(S)#3~x+s|{w_VPE6zJ#txN`67>K)lz7Owcfs0 z#bjnok^nG|5taCaME}u~97%|7fevU903A*8x7)o+kjcl`pIH^wZOV@Og*&Gk6E+b*HI$?6N_dX38bT z&YxPReFiEiygB(!KhLV1DcQe@Iw$YDZGEO`Gc*-sk(M>9PESJubYsK87ei@#myEZ7 zwlJv-cZ5%lylb;NA7ehiJL!CC(pf&vHzw-wHf=`eQYA3Uw7G}BdH)aAFKLwld;QB8 zhVX@Z9+WG78Sck_?0bDtMLHihcxAcZW80jk%UjG>J$2W4U0MRcQa>gS04aA zpvK|83H_{mJ4}+{p~#gOPW1zeErHZ#%oBHBQ=Wxg3X>O^^~4yQbWq*ZM|rqW=Aa&Z z&CA??*@RxeCx`q7RFs1lT*9%!M}XaWo8=BV@}inbk31r*U2hY7%W(QzM{uI+U9mxi zf{|M%C0gL`o!OxN7$>$&hd)TvrEyL!D8!^rpMH}QuK5gpq}y>851HYCoI)f^j786`cFB;CIae$o#9BiEq=L91bluN$0#{_NJ#q^rdNy&wUa~yhByptmV5G}o>gS8oC zj+U`*K0-im!k(xYnpr97U`>cxh=dD%qhWk6ht(tQ0A>tXH?p#HGzF}9eARH;tw9Z#X^VfVWJe?EfPKPYZQvMG+ z(xkco5B`MIFBjtAw;gC|75p3h_+4?zE1X6t=6j#!&gSv5Y3&hVe|nK(os3&?0}TpZ zqr{!>rcQAl@ya(dga=9$*NI68qK=GSDnq3u=K7Hsn zdyed&&r~F$WX$UCKok`l6E1OXhA_C9{WB7aW25lBey{;CoiDN*)2yyO5#l~BEvkM*# z_C5>_8=_(hjV4+(`G|Wpy|^i^zQe~RbWfM6>L}8t%xf4tQW~tkAra60voM)+w~w}# z!Gp$wix-p*>&Kj%QOtvm+`S01bo7NTHK?TrG(2s~o^Vs!uZk6u=#8`-LqBd|!bE>_ z(nY=rm!YIvq1fF>o&bIplg7l~Z~=F%$$JG%Ki^eaosDIUX{YzD{9QbB(&tpyKj)(*;k;hriHg2Oqus;_4T9!~2h6Z5AgZq zW)2*qK3Cy_O0Tji>-~PXxw4&Ac`<(FONxz}_Cs;i?KtLT5fk76*})NRE@Y3yt%%pA z{RPAX9sn$DPK1b60mPlFlkNxx)WV-eyPc8oEg%0*?ur+aSMgiontC9at!Kmw} zPnWL?|IML7(Ed{}s*O~wh2o?>L-0JdA$)Dl!~V-E5J^fGp9@|WnMQ{ct@(A17IKrg z{T^7opPqkOYq;F+#~%jq#h~C1w#6E7j6a$+gZAb$^MOe442|L#@LcH$p$R**-+gX` zNB06)4in4zn=Q8wg>IH!9$#*{$KKwO4OA?2t}QvwvL?SJ#Un4nlKsO@Vd(Gj{KIpg zH#0WOiYxQvgzvlZdpIKcqLSVVC^CAw19b8m)~`kbzp97cjVrlf)mP^3A&(Fiy>pW} zVKvR=(%aBR`Y1G|J=ZWr{zYfWgCh4DCf+>qCipK|fcb-nr#e(4MSw7p9nS04v8*gp z>J%uuDVSi0uRyw$9TB<{3_U)QMSR=5Q<-cs6bG1#dfpPiRjMXH1&|%}!JfDuMtxI= zwlx(vYp#i>H96`G&{Z`T=;U~N@f0}{S;U@r5&soxi9K}{j z_LEN^nO;yblL%7~2?HXr(!H4U7d~GiQ7>ybAd+ptAbPjZh5E%XV1}TVIddUoW1!R9 zv|lMku7=MBqN!Bge3q#?u8fGK+_8l$vqrqX^QK7Y(}y4pG5D(D8|Lyvou7Vy@9_%V z+;nC6%Wo#hoTSPiL8irAbX!oU>ZeS0wA2V-%Gn*AEYa5Y3LH>}ZP8SG1ShrIO}Cs} zo2b!RwxSFTO=~$#Fgz!WO6dO9+u3IcB~8`7m~FU@U@?geL4EwX^tvx<1gXOH?yV*D zI&a8~8iCobM{bgCTExR-FBW`cxB{Ke=A)nQWFheV+)m}?!n!(pW@wXBo%O1*i6cJO zUjZ4k59=f%?aqG}ETfN==&JHe6mQu2z#7KFGhA?ih6;38e%+6K6AP9NVostC-fN*9 z4VRdM=v*J;58%4*fCJnk=Ccc?x~QM4m*}n%4}7}vefE4|!Ie_Vp3pL+x**PhYXnYZ z@S8xj)aI$tFSA|#8{N}?V4~aVMhRg(Qmsv!*7Q-N7bwvYqR|tXC7kjgkA=Qi`Oeaq zt|!c*1|}t?VKC5T<(jcvpaDO>r3lkw>Cyjs93PMRTE9UZ#OH48a!hfn-$ROz=Z!9= z+tOVorc^?@18yz-z!p+t23BlR3T+u33BGroB9|;ASLB$pOQOkl>;0i{3uI3x4tt}ovG6*~svMw>+)r~+m2B8%8KZq`3 zR|v&62)>w6ZDokgwC0MN0_7BhFG<-utCrA%Zbjml0YmN@yVqnoI*lkO$0k?LE1xT> z%)dk=7KmB@_yV85&wOSqFll;)BS5cfCLcoKh`I>BFj)3^Mi9xhc9@5VzP62Md!_>| ziHCsd>;fHg2qsIX$Lg`@=x2S=mer~Lsf^~AZrr!*&-Q={4t~Y=3C}AO$1z<1Bob)c z0D1FOL&Y%&KdFBFD1x){NrnVDm{EToae({Jc7=Yd{BLK2NkW~tD69_!GptxRtD`I~>D~S+HzBQvz zaTUC!T}f|}hi;hC@-yC(r8$VV$JnVwo^DZich^>MWio^PeCxtWFxWi4_$1<<(|kAF zr#__Ow{V@0Lx}hd&wc2JRa7jVk5oF~8s0FRMkF*}%daj?_oFKgZFNq*wePZA-Bb6TUC8SKt7vwZ8;VzNmAAdS}-EWCMb}d zbbll)a$w%4tvo0goDuWgFy0C^@+;ZacjmS8MFED^9&BR=`QQk&zX*=qFD$LgMdaD0#zA@FhcsA+X74Tw`@I)HkKN6r z-AAGq4t?@4^>)<{PsrT17l^LzeUGS9BVV6?VgfOSCVH>4ViUiW#&-?2@f^Zu5PfA7 z4uzWlkz)yQ*ReN0azK&m?TI2Wb>AKN6T!FtfFPFSRE4nEB5_qo(1t`fM)y^IxV{-+ zP1Z}j0xZ8gQxwZHwCo0*zRPAG(R0Vq!aBGy6I<|8 zz$ah%AwFE3^vH=|gh6<`PXJ9buqfKU4dKM@k;1;79{Ek?9}ej3an0uFKu|YkXM_&B zk;mIZo8Y{jtOLttKVEn6FTL;eh70uo?VsExv6^2y`UF}31Srv)N4VF&0J!_j!p^(J zmR`ez|C)@#&H(^mRsLUM3+U@=(Eo`oDy*HCUl;y6w!kv@-;+`P>b(}O&NLZXc8K@l z2fx82_zfpP>I%XHmT4+8A=5V-6%i4n!e^-QVvKt1P{uoXf`jAEWQ}6ZA=;=m!?7`8z zeJ>;Nz9Kr)L02=<%o`r*uF1*#tW-zqM#QDP=co1#&(rmj!4cf=V9z13E}g^_hs45! zkA&2rc8QYwYNC0Zqkx6jo(B{m`f`$xvk=n(D;R%74WXi> zDWt;v@UE#WNIA{Fhr>qE8r65PAM<(3Zza}GKm#kjBYPMdJDl&skkT?)sHTw(4W)uN z)5s_qa)CJ?f|YxYuz5)M3E6+G5)6E&OJK$wM_;qL-5IVucp3|hPaHWt6!_JArTN|S z>NsBbuZUz~2~kf^MrMWeTeEb_tdy)e`z0X@(s6au{+PbiH15_V_(BiGzqko5M-h6Nd8f4j7IF6l^aN{P}&EVO_kU~_$R2(sT{1UT$(J3E2*%KQYm6ByUT0z92I?`5hVcvJg3ujW2v%AQISWsjFSwiPJ@ErUlg ze?o!$xQ;g;#Kf)mXsAGTBa|UB@NMu*iJ`&b(QbQ+l_!V2%^ww;uI$5$5VFJJ3E%tS z-ujtL&e11!F~P+Wm6#7~eH8S(Ntn{4RAW&mXO{1aq5>*PD|>NQJic$w-hJOnq(53n zar0E5@uIYn3q6M}cA!7@tjaLqR%?vr3`hAFkKW0 zjR5Y$C`VDZ!FfZBCXYm@nr3iCaqJ(zF5SV4KwPg5-Zz^%HA)#Hs0GRf{ig7o!v_#1 zLIjGm-jf|9c;?olOSvkNDPkl?YoaH-GhfNg=$|#%wB#l<>DKqdV83SA{;j% z)cTuXF(hqXSnnS?%XJN2R`hwt=?3BC<*oFTHH`)h3rCw-CLF7^6PO&iH$HKzp*m>86E_;aM;|d?#8%*_$G_SKbiYu4H`?Hz8SrF>|()u3#oIF zJ0TX@4FK**N{>QtMj3G}}+Xt~}WZf1N>2 z2uwnToo!*<3pvBm&B#uo{9Zk)Z;ktX&pn6s8`$s@$bz&V@SRuJGVEf0#|`SYH~SFP zkk9|w&y>?88l0V)Zs=}E{52@E*+%Y+#Adeo^D{!$-g{{(!41@qAH)Us$;}M!t_t)U z1hJo6Fj4Ua8HoaQS=eWo>D){d^#~#dF z{nwuTTw|4YKccD-N{2CK}Z}w$ULnq>spUXDm z`MY#M4x3QQq~BJTl~6W_TGXnCZ>X!6mLHIVsz3o=a^TsJ4F7yWS)k6ID)?k#0f2X_ zwkg+cd3i^LC&JmT)rG@d8;<@Q@LN?4dX=P`>k~VD$!v&K9z?DqwJ8@mMOX8kyR5Lw zuy`@GlcL{JI6Pk>SWej}Yvb;4mp&RpYQ%qaze`{#a-@|&?(z>GTxl}F=-=q7OEUtu z>QGzF(P9t4Sp9R*fCb?Me&gBtxzpaI#s(rDDdOCQX#3_*kh5H^bh&ipJBaEosRrSw zY1p8Dpf#aVz{OSlOPk|k52OX3{b6JJGWLQctOg&=lUY_#koJAkmeuw2rA@c1w=*0p-JwvLllBBk#P>-7wbv3#J#XzVlzal{2bMm3%u3# zl6hD&!4g|_!!W}DgUgU+y=m6-j1_x1sT)dH@ zprhpyLwn(()Go@|`g)aZ^5xBTA6c;tetFnHXG3qYy*?U2s+3Q>N1?{b=D73&f{G3` z&R@;1h)93LLx=mPR#ScW>CQZ6A_=nXW)xv*WD$Co(5(OpNYhS*Fc8N3~Cve`61>6jZY6K)_%`FFBP2e zzR8P}_f9%frtUhV%cvefh8YUxoHlg9ky;wmF}KJB@2q{!a4sd!%##aKRG40Frg_qp ztd&!%AH$L>o}le-fhwlth@d=egaJELpfX8T8iV><8`+5ADTeMwA)MsqqQdA##^U)C z<-nr#HsPocXU*eWaO7v=9S8(b@(7z>Jwp~Ar*p}48_` zgjh&)wn_v>!9v6~Y5cMwlgA!{A0gc469Rsn(fS_U3fh8{#+1%!L;Hu|ZIul6@Maw_ z)#8Uf-)g=LiQ@AYasw=X+|5r|z9_e>e=fAwrPINNQAis$KwOWmbdcrf${A=a`Fx)S zK&J79SOtTH>48(i1W#Jy8B6U6ti1MS{_;5f;Q^PbwLN>&wWXCWDYP~Ma2S)0W_lp! z<<#e#sw35E$j+Mt$ldD_LV_*ICEEKMQ1aJW0))E{f*tGb(wRb-o~IJaj4ytnFcCh@ zE?cWsA)Cm~srDRXQX6>uK^ph7aJ5-k|B!fPL@xhCY@gmi3k)|w35bSn9XWAqPql23 zO?>$-SR{gUl<;K`IDW0kjLm7clEqDwoD#07Gch>f1?jMuxAwLOm}knFwSyEA$;qPV z%e4#ttVA9eQ_s%4C$Qc}-cOyJ{T}b>074v$R8;+Gph1T0fAm{bm`c>kid!HF?LMTPhZ$x zBdzDQBs-ET{^l`)iNrJNO{MN7+hqKMIE3My#}kIEy=>+++FsE6!P>EbDppB-7Xr-$ zhCsZO9LYw{KT*xH0NpGAY>A7qq?Ew0#f%J~(f>GLQ{5!JQ~WNAAOqOIF!=+J@qVDt!3fD9T@YkXZfw;0w{aT)A<=j?!$3Z+yu7S=IGrx9_@YE6~-R>~7@8ncgxc|zm|HX_K zm9nd~CkR%uGr3CkUDrvm;(7P8;_J$YoANo2YGbpqNZQ9{@q`m>O&K;h)(K0;4Z&Vj zNBBnXNv&z`e(j;%W(cv)!|G&)R5UH+7t2`#o5d*{_*!IIjn4KF7|F$-NbN6^}^>B@IlxJE7iZokoZ zY=oZUYTRHh%^An>K;^J~RvMm~gAS%z9xpy1VOEg=9s*r!QX(J~A}Dk^5V7(7_28Y-lqqGA@)S z90s*YDRcopqNVZ48K=b@aM|3>1SyHWbqu>B7tJB8i7hzNZms8c;A49A4@VA-2MP|N zGzIDhv*P7zI!Ui4*HGd^d((8kkQrBWTqW<2AA)->LPts|^3$C=&_LrXCUqx@=Jlm~ zBolSPSkoE}U@3uJScqG{(<4`~|>JDqhuhqPWusz~cs!mH;0$Yj%jq$Ry0 z5k1gI-wIRL?udVINy3PzAl+VL)k z76UF&=+miDm*V23(^!#1r}L|X1eH%Xl~hYge1qAr}F1 zq+=yKEU1-q>lYTZ)OnSjm)eSTSW9iR5DvSi*WrM7H(f%KUT-{^`!#2mU@Rs|(5$6b zqK{d7vqD9BKxJl)@fhf(>EfwbCq^LNQv0SIHT>Ofm^h-I%$X3{>nmPofcny3PD*=N z*M*|EE=yvR1ELRj8jv)SNsAxy3XJXGP%}^FBZ%wc_Imqe4xOk|jNi^nmn(|S1W<7? zIMVhvBz_k!GxmNk!j}I&X`c{qC?Mni<@;z|?Axlt=I;^-O&-{gt`8v=Ew*(ZX%p99 z5G}e(^u~BZB4P}XfBr0V3gjVk`q|__c7FZ6-JZwZl_jBEZ~N|e4uN-=bpB+|0B`(A z4353OSLnKG(TWz!AX4w|C&ZJO)h#3w$!7ozQPupHj918FMP&ZAa{i1om)itt@|m4I zbjq@9@>H@VVXT`ldurAIi}$)rU6Yr%-5f)nAt2*gtB@d<8Tkus`|Vd&)yyWlr;2?g zk^|i^DGN1(dQ&NyYu_A_+fPhd9(G?T-xeKthbg#*8A#GonuQ@wRt;lqb)0mdeD!wX z^F1>pE^tq(>yiQkmPCJI?0KAC-;3{EIStP>H5aDe&1nziBpS`+uLXp~#0Y!!FypiB z=Un&j{3hoO!S>ylPB7gRf<`JtGynRl{(Cmhz`XC<*E;Mk<)tzpvU21`=;LqZ0@RCH z>OkGbb9(mrSFGZe-m_3nLK6^%%cb-GTKXzH=xc5e15a>_Hcp$I~*}=oIAAx`L5||HM)76 z1d%~~`g8MhO|bMW-Anfgrdw^iCP*#pQPfjmkZ|aNvDKiyJdvZ^^mZR! zSJb&cSeU(afUf6dCKu2PElFu?8zCG#HijX#SMlX9+y}@C#^95=PJ2G3>G-uTz35cG zVOt}LN-`>x7jKz)Br8dJ4E=$$N;yJR7ENe%MsYLkSi13&d#Dq~RgDhS&m8+Pf?b{1 zFVlmL^<`=J1&-qG2>&t?`k$=Zf2ZnR-}lQX^?#=7{@-p40Le*4=XF34m*zhopsXtO zDFE;fllh0q{KI7aVKV1-?iQQ`3D5+B1a}AoCqQry-Z%*!f+V;E3naJ`Ahjx>?0^4wLi@|{j}2G+5BrZX z{zX^+5B>j_t@`))aIIgj$CLGUd-Tix_h#wD%L4!i8VCT;30py!ovRB5P?z90H&-VM zT|oe_4TB@yT_10+1m0i!!x68pj|Kz&xI~)%u{hW24*(l5IO=?ooqF2cc8?!`u>n6{ zPf*j+(#U{s{9$%CmA-y$td|Dgy*GF6e4_JWtrzxg|1{wirsM$5{1*IA^X5~61c!Ba zNaJtS&c^4VYxZK~ALa(U+D@9IJS*tuFB8~=!GXH~IPmLw9JT}O1?&KmQNVp9Bflb9 z`ZJQ-+w0SRBDp!)`74ski@zh;n_vAqlEdNer6V`n!+%Wu>R_<6tgLkOcO>&ary3fn zD@T6MrmnfK<=er<-EXtBB&d#9}#TA?;|){{Ch8MPXCVJ;-3i4w%7lT;9_%ba^q&_ zJ_4i-_Kb`(lhZkV{Bi>*A4;@~H z^RFHLrw*?p_?HgF@@$zdJw{|1G}X#$Ut7!( zsQP{Y2wof5icnXR5d$E&uJ8G8BX6uOFO1dRr)&FzJHJcRi}SOSgWcJt$a}{L-2WPU zb$Ncew>Ce;8-DLOgZtm1-QHYZ9Pcci&l`pTAp7$mIQZ`7{BZ3YKI{DMmm~PzadonF zpU##G-u`lgz`?f{hliVsYyBTXe>pxhioq*3$OV zLD4cFzP&qq6$GH)&li3@-8h-|T02k&StSq-KTZA9eK$~E(bH?H5vYx<914e@Cj|jm z_r1^RYO1T|N=)?)j9@QObnU19l(6rm9rxnz_4U(>v%M8_uod781_%AR>zA}IE$bNl zDZ*Em8h0`{Mlk4Gs6nU%X845)Oa*?N8~~SU280JgzP<07JCN%H*qq!Efo;4B=;)FQtT0 zusDM5uEafs*M3jqepCG5nAa<#y>nM^_{|o4=kgyV=Fhl6_80e87imENNMk-`88r8% z8Tf;`pjOb|(Cr{^ZLI$V9o+v4b1b7Etn zzAUgDxuo;%ALN#XhUYhGO7GppU4QPwFXXPq$3{oCXKE^|YhaZq72o&&lzwq_T0Qu4 ze0*bnZ*}qjb_`tqUhQAfc}7P0kM@R&Oh3=V-)#Ni&Pw^5nBZz_Yv;cIFC?C)m(L_7 zy22cgU3PZhdw*@)uf0A`8Q9N>ce1r}$lbSzeRY4#`~&M>TwT;mwYMZkhlhRc4UK3| zgnj>qtLvZDSyPkk&CP9nuW#URr?!8j^VGz|*%>=)%lK}K`~7hE@sD&{9~WO!?PzZm z*B=qu92@jo`UAP^fx`5Uu5NGZ;MaB^U+??_);)5!hnveg_ru)o!w;|iQT|ut0C@0U z#BJ>R?;vk){zIJKHR{#n#p%)h+#hM~KB?U!cYS$wkKD|k$OXXB?uolP-CMf+O z_4e}UzW}%LcaFRJPvEXj_E-K5xTDpj<=w-71MX^ndvShxV(R4b^yuhs;I5YEXQrnn zCx5P8-2=Dr8#tu%{k{3=fJu~znUfp%(S3&dm1f|_@iHODvq6){KbP;|XTKBNwF4uI z-pTAl;Jth0&n@^x+p+jV9*&&zrTN)uzbWLo`!nWWNerGX#KQUz*3><*e*wD$ZvO?g z+nv7m)-M$G{Y!hU{sp$%wT=DFmG17|&aTUUf$es6`*eA!3)YU@aemL&&F>fbwad4w zN7w6%oo#LHGZ(eJRe$BsU$EWoobK;0j|>e?uXl8<^tarfCV$QBmvncsvvzv2v9`9c zQGEx8H~bFgkGocvm(NbxTU*A~t8d}(`uj8LUw6TGmzH<7JKI}EH@Z5OdfR?Wf53Kk zw6VI>)j7BfzpWdn{%ab4=C`}+%ail_VeZc17k@3nz4E`nW@*Cp7yuaB-zqCgW1*A& zI?-Xt%1El-Kf`|gfl=-sTSSd|?w>$Ts?tw^ul?kk_jkOJl@wQh1KRb*c%v~^vDY~_ zIoZ`8Mjxm}U5^Kyqb0y?L)H8e^tveZwQfmfMe3G_PeOss?e{aE5R<5i-J&Y}p%N@2bZt^v52Z}(yV`}#!wQI(zNO1j=)mOng z!S+NG(Ri6346mu5^G6eW_QIPyF~O1yv-mVn>tgqW%XH#ECj-Vg8nq9TuBZ_EdfS{4 z9oPbP4P+pHjZsliad{(ZAZKM|mGX1D2onim6$=aNTJxK}x5aF&%W4Xb<$%pXnj{9G zsIDWELolwAE4|>+ zB!IW)Ou-f^IvMj}#h!sFtcN7Rx{fXW8DAKEOi`K}SLMTMvD>Z zQlGnSVkRwnvmtbtcX;uMmYLZr{hC*OpNM{beR@xC=bfS`uILp)r;%dme>AG8p=A--y;jBsE{8D%mv z;qcRUwZ|Zp*XQO!c)t;T_cLYyhUC}swbO#)0a~7^rn7;2$OsDsC6ddFIq@ZgKy0RJ zNR(_le&Q#Tb8f8VrqAy;9~5)Qx_%DrS$$9NM(a6xX9|5l2quY$)p()U%~3+*4bdo$ z3eoA+JI#jkM`@z&G)1Oi#6zM=2r3F;{L-kQv)FtL9L?g?0&z`90kP?T1+93K7Xdpu zQmceB3mEF{V7)#+$2@bl2M*$7Uzape9J)jvBs=4@lxMPpuE~N{U0NC-ily zL4hU39U!g@!4$xwrdXiY4&QJTY6gA4htXwMe^ATL`TSWSk!!I_%fS0| zI{k@YGYllI4vG%flq9mrq)JNj2l$HN=TG;%iHY>9L_Ho>S1fQ>wR8<=&V_rl}_R35KBz@r73i9=u%;R3JdOt*9m|cR|SRtPp-XEXR4< zj0!Tp&(1! z*YO_+s+{_h1;wO@4(G+aNSpdCem(}Eafz-|kEQvhLdWODFJzvVSG{2n9Cb_5)n*uZ zQ|W_8>41(-X|u$3o_^Gs`jiKZ^BLmn2ct4PpjSwGbQ=ORx4g+=JE}3Ca(4z9k|%6%c1~3*Q-3~H*&3yy^KNRN^wMFeG6{k_kxiW<#NNoWrnxZIZXh&u7sc*Wu&S_qT7T)T>60z?2y1zVi;Y5dev>kI z+kl?A@H&)0!46#LP9sux`lJetq6>{Ld8TPgl@wA9z^I)OC$Io8>P`4Z-p1p#Bs)=2 z<5sW|0x&xUn^jjLoPR0AknU2J!`-@1T~QMQ3w5sNqoG(tCuKBFMmcV^XEkg_qWwXL z{E122*ujrWzGLP7z1=q_E35cX)VsZ?@p`r(cYPo&jpXu%ckxLrd6S1pk@UEkfyd{h zu^%>j>CzJNx+fN2q+Yy0R*u7Tg+m@leWarxL@3K5pQHSD$owjOHN zoB1km#5g1C|C)Ftc0#j<$sx|9JVYm60-|?{Iy2kPzz!MIhh;E+^y**X^xD>SyII_; z+o$w1b+F?S$^t=oTvtDQkHG~O>sH~BI?&fU5FW}}2Y>WMCPV<^BVq@axfy<5R;dAg zyay0=Syjmw?0&SOC3r29&0j31xXlTo41`)iFlB(~;z#c8bD!zNZRS&(5J3A<*$UQD z$I0(##G$!{P@!NFho-NYBntFQ%&Z)#RnD8k{O2=H1CsPnx)4llfH_8nIkkzUqUPr`BwXP2*HQ%m;ChKM%h=sY3Kgjh8sTH)Jg(LZttsIjn^H2 za?pf067z&ZoGTp!d#)vIJj60|(m|W*-_$zcKwp5D1XZznQ3FD5##b?`&bNXLdL?H< zNi?(tROII2EAS80G)DvxO=V8b6vf+oE(Ti0?fhVe zD@gJ^*M(HU`JvR()aMJuyt_!nggm=>DnV^wgbx|P96ug9@aHZRB0_~1Pg-fShf}Rn zm^u)iA|}3gE9XwN3BjNp(WPF9p-wjrbwEM}eU*+N)v11SxHJ~^co zoxvlb7O(;t#*hS-_1g=?BP#C&EPaw{_P0`Gvuc!Pdz(}n)RM{OC83=xAgAXCuwX?~u zIV>$H9ZcTol=5mwMY78%DW;x}It_>|$ z1y)v857W~vqT%mXV`M(BAA}fZ80aAYn*3ivA3uTVHKq{79@KuoOwNeK?fgD~fQ_?4 zs>m*^wuBETeii-U?1b#kja{QQLeI;KuSdfyYWIcj5ud$6CLJ%^a>jb9XM34x*W(1b z?Xze7-Vq2#UjoDOK&(!BPhW8g(y3IOs(nEv1|v+L4G4k?R!vfY2j5K#nTkao`jvyK zH)+mi2NCm-atf62*`IzW+!5b+fEh<~dHF6)JiyWS0SwIHWWn4kEOHwG|vC zCP6$S%It>kNO-8VXY%tQIQjtn>D&W0%{P0s+h6yWGDI&#AE2TEk^n51{_0Muap&vl zbDda&EpH=O6Z5()K+VLjBW3+tpu}(?P>VBJ zi!$M)Q@Uf<$BrFt`84ij;l{huZxU`Z4jnv%Xx|5*h9^|t-S;Vu7e*}x_Gar00-rir z?w7rrP0_QjuPD%Y^nu_1U~o>{X$#@RV*UjeUd-l(T8V!im|9yonkI-cq}<~cFYreP zp;B&UD?%g|AmwV0x`vFMkqnUgLBS<#Zy$846E^5i*1Bw;bLYa;1{8iib&A}JaxWX{ ziFHpN0`g8#RyjSbOs|JD8Tic+U9`9{bMvUdH_Cg=JZo43A9|!eOH5)s$SU zg3qN4)GYmbowl#j=no{M%Kba%sGmuYmESzl(V0d+FztLfa?CIm)JA80Ngap31Plp0 z_QWS9PuKE`r3YZ$fr|)(RK*Fn51yY9JJS}7a9-w1JaJm@zny%brB{L+rd$xMHYyJ3 z(+4nqdSgjFs2b`jM0p6W5-!LtIHhmx&~+D2eD|vH1OHWa0RywbUGW8gvW3Y3tfxtn zu9ifx8IeKlfVmS$_)_QCGd-WLa-)Lky)0|gm3YaUlL5s~%t@GZb4(mR4P|x6!?gtJ zxexJAuOb+MOo=$r?x3QIexlKM#UGC^B#v@t`Jc-ZA$t==W)z-oji-~NGq!oC>C$Z#NHUWJ*@R1zsS&to(rcgz3yzLMnrIB5Qe225!!c!x1V%IxmJuwpM4}O5q0L`mvM){mHnDzS@w>o5)m{H_O-)U!bw~u=5MR0;#*53Xl(C~HUdS&vCxvxuIPS_fln|`K z+LK~23z3Aw7t@2tE!)9V0>HB8Qm2k+k!_5Ki)gW9baX2{E(?M>`N9j`$6Z!S&W~c= zf#0EF(v7GorHcX%(BzfXbp@}3W>ik7P7|+(UZJgl5!3;|gS(|^1ngGHuqJsuj+A`$ z1#^K3Sml7FCdKS2Z$Ku|G(wW&69xFqfQsV2c2fzayf^r}3}J zetuvRA&O6}pYe^#1q!sA+=H3u-1St3R%H&+pBQkkE+ZqN0OZY{Xx^78(dVaTa3?!!b|XRUpqzB^lsg zh>#-LOf8X1;CLwcgjZadi27E^j1rqMk-3ygSLt8A z*1rBRq9k@(GDjSg1_Hq-xE4#>T*lIvO1A511mArZduf0_pMxge@lk4&;rUiWDaQB@ z7u2oQ?O2qaXMq1lSyS{@9$XA`pyzd`Q>8-?*vCTeZc zmv)K@nlqTo3K5S7^-c1seVnrKdZ32G%Ib(=%UM)$Jw2Y=qtw}aQuRi+?1U}>*yBn< zyg2P6#gZWpF<20s-CVH*`qFp_fGLiXvHZ?Bi5%V2k$fSjr}iO2&I6ZsZ~KrRJav`! zt;o(wLS@i0x~*5j2GhMjOyvns^lIcp+Wdzl*V^?*6diNwxwH{;j7Q2 zCVd0;UX>r)(51FhQAo+_9@ju!8sQpwARxrB*MSj_T$}d4bOlq}zVGa=O`?YEMMhvzi3 zz^8?e#Q|munYkQ$_Xi5k&zXz+j>T&@7R_nU%fpOkhW5yFn=%q+4*2WX*fM=-j+i!i zvn6*g(!GZEeC1zO=DrQV|MUe7h}*vj23fxK{T{WMPN}4;4Hgj;6lAHW7%BmGJOCme z-B#@sXwy^_QGurTQjE)9L2k8)(5noID<61I$jiD3j*XIGrkB!^Vi)-z?&M+Eb0GBf z@oA8yFCjCd9MG<56z);07zsM#q_A9GcJV|55L=ASxo##iyHmA|T+|F^m&SeCB2xn7 zqPvz26C4o0RYmE&E!C)G;g@CdSeMC668KTjSS_f`=MV`pfAG0!Fl!l+Ipq@yCdvo0 zVy!0p=~qBDJ(g#9fcv-lYWfI{rlb5Tok3_jay_ zY?hpgVu50B@fSZrQGe>l&2P_KGIo-Eq|6gE_vUxhB z32U3l)c>SbbeJcWNb<--EcX4D`5=1U=#;ddmu{_h zPNya+J6dJmiqJI%%JzlnAOu581Ib)q3Y9BEUX**pabTn4F!-vcfluT$so*(^uE%=Fc}@Gi&g zlj6HKwo0kEyg+euWOjPtEc_wK`^cD*`qRYHXBdWd0w6P=0zP**4cYlDW0qq=XJY*} zeP7(?8Z0?CH18i228i4vAZ-HFSG_1Lw(g5%s4D?0-A1k;5}5Z{#1kf*;wC)prC;+!vRpD z=S$@!5B(>Rt(fOMypB5uB&y&7`@KAC_x>SVERytUvH zEHj>`v|xu4(|$;ez4gxZwl{KL>Q8&B53O%gfe^7Ob$uGo*8{OSEq5r$d;DEWY-71W z#e!Gsp-Pax0%Jmo`n=&mcsMIJ8H$fo4!SZ&X#En zN#JII;uS9P@nZT%qeIz2PI6^-HLh0+FW1c|jJ5 zV=@_!$Eu-*2qs6@M9l~mT@0$+kCCye1!Bw>A|M7KOlA5joxLSO;L%G9ZuRk$QSm|u zcm~qlLO*)?!4-yv{5+YjS3s|r}icJ+A-zLTNxmzhnc859j^!)-Oav@8Y?aC z&GhWYbnU zgZ=JpGCFH)Tp5cdDS{rK8riSa<|%r2cH5@rmL`(cUMcMqfOZ#AliikjJjGrA_psCE76>AM3Ny|1%xw9h^~%G)#=SY+&q zT&@8-n1PK`%xfqd54CxPLxr7e1qNBQ2y;#LV4Xh>EsySKOek%?T{5TIt98_4CU_kt zlj~gTq>7J`ja5qtx~glHp+*Y-$g$^|J*1!8>4bM?+P3F%vzgjA`eqNbfQ9#algPFw0Os|)%4$OHBmPXXL`IUlnd1!NH6yDt z6m1m2&+JoQv9^1^eSFW>SmRJnUU=Gdob<)a`RnG}i2>I38yos zkR<_!tq~NBqMpTg1za5u&pFwnzYHvXwQe6XvR$-+U#8YqVUfAjmDHiG#^F(+621tb zReL>Sx(o|y-M=bU43c~5<0^k-?P-X zBvoJ>L~#|dl8^5V$jv(2GnT8=6M2+TyD7cBdU~3e{T^k>ByBO{aFdT(9Xl=`D6iPFuH*UjcAt0T8AjJZs*N7%u4FO_JNu6i4cm`kUVXVT_ktIEYNG*tOL3`SJVy>d zeakG=C6b~3<-NI9I~jw;YC2H7ZB{uSx2}LdgMpiyq)_7Pk5Y(C*Ow)ZvC0mI*?=d~vu$~N3>ayrTHNQAaS?@x8 zth9N?O&J<8O%##vQTx_u7H96g>Iv!d65oT&>$wlX?J#vGE#AMF^D~TXGnTiC>Urz%zG?hlsfK;>KyNhn=PA4PhQB3;@CA8 zE>b?(2s=hMJ!A+vLO7yLate@m2;;Et8GGi~mb@s^nQ!;L{R1N3L9De!vo%4bgh)^A z?86D3Hf#_8F^Kv)FMR!CW$mL*;u_Y-K_cH}-VEnRNKPk`Huh$smWV6zZUmiq*JGi6ZK1;K?ruMFg6MctXFWlg$hQ{&mf<0WC7-4Adn_77E>P-Gj=C^g*R}PUYPba@L zV=q16BIR!iy#9AvXewwV~5TnsnH>5$?kVd{m`twp- zo06mCTZH0a$WOMKtXJ~GT-~Bm^)l0;I?}oe(2YM-CCROdhSrk0M z6m6ZIiycG^Vw?Lk&Db?uBQgC}Nw*2!k_g}b8Q&xo?v;UmgO%kKcR{GEG>c(pR7Ce( z3%chFPLiBIzj!rSnu{%*d{T_yakZc(L!}0o8g{!Wi387SUt&(|pVJfuhYUU2F(oOd zQ*xEu7S-O3t8#Pm3R;cmlQ!LZY!El?TQ9Cq+YO3#pInsEIix-37jmh3kh2)~4N+3v z8AkU4+4pXT$#H1cH||=~>&+I)sY-Vuf}tpZz=H;nH-ffz<+u_*__?sA_aYh|ioCi$ zYj`pYJK1S}E^<^05e^%Ds-h-`-wx&}xlSY`ki5C<-aT4B4N2U|j+|K%CdMpiL8aRm z&QmHLLIf*ZIQK0OC;92*+z&v5;>u{W7G+R%{mMacFYiW!FJwg|F?h3^ET9yP9G0^% zS4&^M*SKF0yDNQ>!4$LPFJD9W=oT$V=O`A=tvfE}@LA851I5u0ZDssqUhfV^=P>u> z4^Yb93~CxhoE10tgi@4A4(lwXp;3^gJWcqn=@B_D3 zMvSFuj$Gn_ds^pxacwj29zL&<19>+H0s~^ z!xHWb4izPMH7_Im^&mG*pSFs9o*F*m zuD*_FJ)ufNCRzX?s`Tn>4IV3(eB@uTy`$0XWgl-{d>ayboE_>g(E{ zM+41y3szC=A8OPLagjO@dQ{yho7U8l3CBYt@|?#Df|EGoMQ^h(gmD||cv@MXtdTeQ zI4EA-v=JSReyX~PWz@dy)fXqgcqxnW`sSO7G5A>{-eCU*K~#On{L=FJ{Wm2G4zo29 zEBd{_C(%q3ArCE`%cg!Dhipi>`Q+LFIgQ2!PbJiHC4S#@+jCHtS!9^qr`M9bgUkL& zT{Q@}|L*+ivn)v>;aan)GH25uZJPQ8SCa`!c;}U6YzfGSO6jwI+`^92`#RWx4ODq( zLH%-gRF5C%V;A6$zv(rQ%MNd;t@a%c)YG{R3z|whZ$@w9^ffhtTDGORb?FLCjNN&CT#>&HrC>w3PE7A#E{PwQ)|QK6=74xjB+ zC`BeM%^x<<%^pOKhziIBx%vt+h7YE7-VS_I6jE`XX>~a$s%BBmq`uWbL<3IDZ8{dq z@PJT>%oFn%JYZvBDze3#0f2-^ZVu2g0E1~26d?o80cz~G!)ec+1L zvsl;%0O~vLN-oL8%*<9n z$!CuBMVw!3p)0fF!^5h+@@vA3ZEG&m!~lYXiH_UuKOn?tD?NmvnBiA zlFE!iGg0vPqx1YEu`nk{FW|A-Q^?Z7yYcr0S%B;_Mai#E4gK${xC8*ei1mL~as8^X z1_6Imap^hcz50(TE-3I<6&EUuye;l*I;BtLMNxS-t{>znwJAMx#o8ZalH9!5pX_}B zW}H|@M74&Wkp%Qn5X+Y~uUQv!H`^~D;$za15y(7c&LG$P(&yj`-}fBjABlZr>1zHe z_jOfn9ozY~k3oaSX#s0)ZmzWIW;~|>M_*rGexX0xv!zLu}4*};_6x2~JZW3@A7cNVruw(6`bi8mL?W?t7B zQ=SF{Z`apx2I8&c=&tJzH=c_5J|XMa?>HGcf28C_`4m077NjT$tgKAztK|2R!DF^( zr?nuslODBYsxZu%h?Z3M*`(ui;FaT`{H z+G}TJg>f`TWY?+}EZFPzIbm(Cz}$n~z=P7G^e1Wj$jB@OrtA5MvkPjSu>Z>xo9KcU zWJ#a4$Vf@Iwzsxi?Pg2OQ~oi zjh>AGXs=bR*SjBMKe-r`AflL^o>p?QIG zpH~s=#5PZsI|QygcN>Ysvlf}iXXUI~NDM~aaPN4#RUPhcq7e`fgr|ynUu}HkHQ)8^ zE$Q&VTngZO^r*5>x60=MLFxSu5Z;UV2?jowpQom#DD>mNNIsBsw`z2KdbjQ;=-K!{ zw1=sWkM*UXMVb8u6(Y(i`!R1xL*=@-*@Rr?hF8{}r=!QoTVqG%M^8-yJE|#F{jh`* ze|o~mk0@@5bv{mq1%#SfG0b8_FkY!lh!3q#)0cPW>4L&Ok?$@5(@7JRuR4$NYDYTiFm<)F@#7T=QkB(=MQO3pPw7hIU==w`;vd31C-S@-x z5uE#4lZU?w9kKvy?8$>VqxK9uhh04sRqB^O)B8xhbX6$Fr_yA-V&q8N#ZJ~K((6IDq6m#+mrqjK?o2J_h1Dt=8Rd8Xk4 zW^2`I)u<_erh!Glsg?7S^u>Wbv;~Rvn8`?XaBeTzCB29~hGLG~MpJe7(AAe6>&&b7 z%mUQ<#F$_Jh4|?#>WrHeMbilq{`fnfhnIxPxxWMkku-$?#~WMdPkA;}k!cIZBOCT= z(H1r zfN{%|H7U(+ZWEVvbHk149H{Hr=5Nc$^-%0<05W25iHkWY9SyU?P$t=06&`3{ z$61Hs8MY_Vr-^JWG;t8EbgfVwF`>YAZE>Q{J}BVcWk*zb4bcOZmf}8<1UKn9?3(t2lm!RS=jzb2o%j6{omD{a6Bsu15xYX^R2to%Iko-dU|4SZk{9`Gc=|HKaq#$ zhFC`Kixsb9so*O^O5R%|1})cHN20EXOO(N;2#BVh6eF?-q3%SKc&!)a@gbHS%}yoQ znbq%5OK4J~uuxkHaJYt#ON(kfrh7e`d?Cv=>SH;H!t@e7-^5303@Sk6c1rO2zLzQ^-76a>$ZIdB-W%F=E3 zX6&X^sEa_wM+kaW4hv3#ig-Q~JL1c}Y6 zSl!(fn_+b4YXJzjF~0o!a}$6o?PcB=37F&(E%^IXUf>;_Rg?g#^%XqTR_wI7uypUH>#Z zpWzdDt;Pn$RK8`T`}rrFAL`-fOR?@ETtk`^v?6wK4Y)ZjP~+Q?UhM}t3`zJF(Jb&& zBrysWXu~BfJGj_~o$LtJ1-Y<_J>L>;q8(Y}d>Z9&&6_w?Kfw9z3^JnM^ zHuW=hLdq~Zu?C99VoZ_FRvKWLH+t&MoMzR5>Rroqb^lsRXDGz@dDe-V81|<7stO75 zAdP6{$w5amAB`D3-hmPvH>Vq#opF*Laz)~+!J*m=e(Q%WLpcs*Uw? z=w~4Hr`-f>uZhKc&Y^?CH;+_JBjRe{~3h5gCQGGfSHZ{_IsM-9b#9K zLu?w3cKOfetyn?1$Pi!St2QSiUwqr{l?o6x0{u|JC8J4f0iXs%J({-;B=fG zJtd7J|1de^a*(L6>-!C(=__cz!V?;hapToYN}s4b$|4s}?mqg`ZT8f#@8Z*D9t;r# zp-GtuMF4KwuVrU9s)x>Rdl}yjJ>A(`?ebjCl-Q69j&Q)$Uz1vPCrRWRPkRp>)393v zFFx7KKn4ByU|feIrP^d};s-P5YK%bwBY;>~**|E?fRY6!&~*LULMSRX?rTcjEv`;B z%VLrcK@!8|7Ye!n*!xW?xWIc-^lQa~ZMw)lCL!CChKPjlGNbzM^N*K<7y>)39J5#D zcxaU5+QVl#hu~_D0nxTNAuYAD+c(g2=w5Zl{+h}p(&9jm1oPl#qT594! z)5I)P?MO@yzo(n9u?2tR5OU}9SvRrpQxt5drVpezs*N_8>=kB$m!FG97JjAZ^)D^`l-*Zm+1$8)?`f?^~?&g%_YUtJ_o3rY57OT)sZy z-}xT%TQVFCJxm{4@Nrt(@jbWmIleD!p(+PJgh=u^FH}uaW@iW0sCPk4a2L}Zepbb` zExAv{;RzcRWKt)a5D0fLBRfWKo^+^#LZl{M@vWCrr4T6F$Op1%^CCwz-~ZD63Pi`v z#n{uH#tKAWH(_I_k{b35Oa^FAJTzs4wBGpVkK_#e_=JGN`J#M-%XAS6W%*c}R*niA zjbW@|AdBA=QSuKoI+|OaROT@&-&PX*#Hc8VO#K204;@VTz4xJjB?UG2SCz^?J&jrn`j zD2NB;bIcN#Q2NXV9dt_>AC%>Gd!ih|c`r-3rl+h#<#SICwtyTn(l5Hj4n_1?mzx1x zV$bnH!}!W^)A^e6TebZJOnD8(VQcLs9g$?d(?yLQ3yCI?f)8A1!3Vm|)Q0+l#kV;R zfk2jY17h12s&6%C*By1Qu!rtSe1cg}KDjx6_s(V7FJV`s+{WFebv!NQ+e{p%?4=X3 zk?dBkmTd`KyjVPHxzrYGX9Bd_44LUTYw{ZxRTKh{&Z5i$6AeJXL(y^EjaFsqbt>XA zqYVrK;$8y3Aq@?Usu{-~Z>HooOM=Wl{Gb>7SkoFPKvVIMaVW--!=iq|sFr^4!|Rur zrM%RzE-{(b(XFR5eu~>k?SXsI!!iSU~Ykx`aR2yIbWle%z%NX&Fu z12!M|2zL7VOm384dbyZ~Dab-|G0Fge_K`_nFDjXDv|>vx%c50e+h`jvjB_xW-puac zLB*L67{EUzX^Pc++Zp}&Cn57PmWnl5Z4ClLZ?hmW< zOm_3qq_G)TVWPa5r)6(5_p_E;F`^<(j<@>6XfZzq6Zf}-L!_sAe;|8$<;t5#+?tbp zea5wo^aVqwJwo0pQE(>&^I~AB%dt|wSIdqV>MLB7=&-pyyR6!yV$Y@OhmC67hp>fH zO^4f}!2@N5OPfQ}!&-ua#IA;-Y}q(iIleFV3)@QZp(0TKP!|4)O#B+1&bLP?_+vdn zk@=4!+`1?MO5NAkD3BY`N6WfIJx?au!X`GOslxzc=pCo*&o~b~BYOFkoJKfr`SDiV!ezDEF!KWH za<o%w|IPi9u*>A-XW`W3#&iz$5lCa~k5jKye0|_Y+Z3 zHF+)1hzd~}FZ=B72(~c@;5X}Hjf8iKcn6)l1Wec1!L<7g-6K8(XZ2biu}qdEPc?iw zl`n&O2SY|gA4_g<&3EL0rdebrZqRt9$Oh|g5Bn;bfzUlu?RLK^5Xli&+?$QYSUFso zoDSL_)mc=_v{*OQw$vjeEHGnHwS(|#B-BHUDnjjq!11{r zkreiKhs(#`ah@3+y8L{3PIbVjZ@@X^)~vrcaL{l8<07&5M#5Qo>x*vg;K8?w5f z(J^PeV2tW#P!Gl>!~4*}<8?_!a0mc1`Ax7F!v{bH}g!{{N`pYE!Bg}_KSlm?8 zMmz;B-{oQxcgqj?4tG)%AnV~9-Pcb-)RL@}{DZg{qz{%{|*(o5t$=5jy$*}D93P9R_?0;%N|EHALUk%o#8PJ{x| z+ZGCNe*HL6a_ej$(zK<(3FyN$BfpGQ4 zrJKGo71|>8tC}Gi+_(7AQ`EG{p5<5t(Vte4w1xb*6aGjz_bHc$eTka^N1tS9ZUe8W zZvz$To6d$i;t!?syHt2G$3r1Vr~&nez&(?s{!*anABs3ab7LGtb3tTiC6^)>XKuq1N`e4@Nqkfpy!#Yk{lF5DIQL<2r zFNJz(0{YCJ5$hEzXQX}^Ja2a2o_<2vvD}t`6d#j6RrO=SQpQUkOLs+8#{(OpX41aY zUO5?25fDFx+x{TI~8l8D&)- zdeE7TPPlkrpWg7UFXUs=u#E^9A%|M+D>6t*t?4O(7`WK6QaC;AJszZZ(1M2EPm!1) z#r_5@7k(74yZ5btlowm)!W^%ZSeIX4_$Lm!0I-1?GtBZl&7j&bzOg-qlCeQ7K_-o> zg%>CAGHZ&AE1#$F8Ni8cr@=UA-|&9`^FR#0(u(-=DG8`F08dS)qXO&_ zV1$6P2yqw?2+CoHAGYMSU)}c5q)C%zB3lOn=G6@N*8#wQNtW6K%5;5I!DA|o#smro zdJ#&g3>ZQaO%Mu;yfCe^-!GJt$LR_<%mR^^ul{*`dZtsq5Gv5a?`J@OIspwpAVI*! zF`JRtH3lFFKq_E&fW}1&KeBKkHXwO;)~s1`kk}6bHZyoT*Bb>K0Q5$kG!EkzP8a>9 z+k53VkLk#OrX{AOpU0p~>oIABA2QH;Wk@Z89v%}jy~)G$+nEUym^6Bg8{=it@P=hu z$NH(W~$5>O!gW)ehj-swvLe&yELFW%nstc+;5QCWeE%?ooj+(AB9Z;fwqG)a8%;9*A? zJXSRf>*b}Oz-y0z6+kp!#>OAho2*$a_;}97(LgXgyzS_KsV9C}L8MT|2M`bx%rztd z=ma=iIb+6**}wYLZ4WP8c*tTzb|B!9w?8Kb0R5p-hQp!a<5@%!_=0s?b+s|aV+zz1mMe?<=cWI@2k;-+ar1haI-~Xk_6)z|yymvjc!W2$bo;Ax5(gLXRj<|E(Vw3_IF{-Z>OG9BR_= z9)CcB(7hLz{?TPJH(yr1G#Wbu0wzDi1&HvPV8E%wMiSt5WO=$NDM4f0*s&A-`@e5~ z;K(D7JO$ZX5a2h?qC&;t+BuH`4gj*?Q;7cZl|dt$Y-Kn@3Xh3-<0Uih%G}PK$0*l6rwj_##aAOpdp~< zg&(UpOhi^Dv*~*S0p_P5fQyh$APNL%G=y!*D}Qn0jrV@^w9`I?tQ`m>B0T&Z0Q5qf zkrWigo2Io@4h=u0|JH(oZMiLDLKY7tQ1-VOdx?!7nch1PTbVYLD)B`k5U^!yo?d>C z0~A=aJ3tTs;c;of~1RMex-A1x9;$XN{t2_m_uBNrUu zWJca=OKojEcHVx8yRNqR3bnM(<1v)G8m3ae`!qp@zw}nG!2KsjxzipoXyjei|oAo z2?8(z4BrC;xQ+oZAi}|L824O5M5Mi~T`sxg(rfec^NWA+i(gy~FUHRLCB~viMp8MK z4HWRRWIPdFnGJ+65(0VIpThXli~fuoK78@i--zggrK#rT<)fvcAo@j10{RRHqh7%3 z1az3tlLQK}m=G68x#(Xn`uD4@y6Oh6KI{f$wItvqfWdy5BOo0aDcqhLMm8S09*`I6 z#o`H$>}T`?ArEAtzRH-oFsNN%)Fknk)w>zQNlU_yN+P;+5%?Bmge3vSz&-w0bGGgN z4?D+q*N)9fzw(s}FTrj^#XtPv4=?WQ#2)4b1PBH^!X&}XNOyiZjr3=A&({kD409m@ zHG24Gi%xboiau}@*X)9)GML9AmN89a49n?aU!lZaT7W~eY@VPVEUO!`y=ISVt7O!g za1s(V|C9iHLy>I18K0QbjeQ82URNPUmD#j=4+01+Lym$UPwI#WxG|zOFB$In zv5!xDU_{2JKTm9}&81G?<%&MsC5VkKT3&1df-~;98Aulv|Kt>3F8IN%#DcgdL-&xO z2?|;fGC#$98Uk_A(0o<$ae6T41e6q)z#_0v^79Kc9@8+dE|>vyd3Dl?!Jj<;@pC?Y zeo0Yr$+!OfTVHN&ZqCYe1|0zC7m?jRmVc&mdwRWgWX*kk04>d z!hi;^sRU&GpyX39D6goHQKLrbQC?OiMFm(xAg^0Gu-|}Yh@aNh7CH5!r=D9-SWtB3 zRaainP~T98Ot=#f9VLMfMbu{m{x|>_fiHD1j}-dSy$g!jRGP8j1Rtuieq;;NWiS$f z5!Dfet+U}S0|EpEwAQ-PF#Pk;YLl_MFjPA(2Y^tlKu}XtBV)#nm6DPYDS(tfC1DeQ z>Gl*OMMeOxSe>91qU!)L zaN~_P{`s=YF8lPZUAs1-vRMHF4gmNSI~PF#+qx4H$hVY*$z*gC&-Kcf|CxLBAZKe7 z^#d>C9t=%SFuZE$mB9%E01ITyEyK8>s4_Yv1KrHj=Aj^g(kKBS9m}^zF1i|FPzV?) zLBkCQn63q`2?hX>V(12f=r&LX(GD=6gz(nNmGa&@?@Bc!f{BwRN?B>CELpPTh?{P@ z`N7M+aoH!gZrQRHPcY&^zyW~yX5Ftx0rH<|xveBXmIu?KA&=NpL>b%w0|A5FPX@IB zK`+eOhd&t6B*ryjf$^yv)VRn)MSMjW#3Nh~EYL+@cYw(V3M|Spzn%#Slpqk$L%o9n z3-5W&Gs4+bK%bvrke_ePlnSUUB(p0Rw6)3Z-LqH-%L)SX#2Vv|w(BF%zrohlMaD>2Y zFP5((0vr|KUV|JhDCTGZbN~baY%n6@#*Nb>*wroXuYO-%fBkg;!T5PtcKqSFS+LHa z0|4_qyWcnz2r)FgL?^>gChe90v-@pFralH!JxdnT*g#aOU<|% zlq>S~+i!9BaP|$?UypSL7mnz41|0y{ui2eL6rc!A`C7&3kV4JeQvkN&IN$(j3^4Fg z0VCuW1_5o^$>{jnbh|Jxyn=&*f&m5#0=7kGtFGYSwfqJIIEp~2z+-v^0V6381jz6q zDS%Q!2n4Gw1-oe6#M?qkv)7FgeJyWJ=={ z9mvgwp~1X)IP$@m1&C7wrYp+FBAsY?3)%X@U;FVMb873X%x#!HBpIl5K(U|shJk?P zlKJgsp^R_32L>2a8erbq+C$t&*>-DjN&{RJM7sgd3Gh1K7f(R~@iiQh0kg<*_99L8c`LK)z6` zLX|8uj6Re)5CuP`i-K!!D3W2`rN+aLcg6%Ejc+Pu)2j61<Jz5e>^zwp8fFFb%8?dX6$ws~@vlUdFIKxVzKl+~84H;sa}G*daSxJEfr`DDBXocXDXXs~vb40FVm+$d!u1fQ&E6mnr21GNZCcs*0$Q7ufoD zN;~ENbgIoi84y6BY-@)SaYjCtX*~!r%+GkJ3!Qfg#q)ekxZZg6@)(|#q)))RSBA&X zp;{V700FxX-JVV%pdbJpLg`gkee-VE612be+G~%Zc!Gc_l9h?PHa=i{6lUr;(! zZxgE4xw2_SnSeU8VVlpZXzGk;}gGBG20aZR}w%t+r7JQu46OX+{V_;f#a0=I|7Ys+8o4L8fIl89tC>&tzb;wje=44S~qujK}(g znBBYfMitaw!vPxh>{q|~)$f8fTymVe zlyM+nMVDm1UzcBoQGiX@6YKWKvm5KR=48%fJMcW3M^Dv<0=po7Q(r(I0?*n#ZSwJb z$H?OGrRZ-%FMw_b1$F+}hSjX{10Q&42+x`?rFwIr`U9`>dX2t;U7RG;T|hVuomeN+ zJpkH4GGX_?+*7)MegGpCoL|7%ia|sssOV56t~r|Vg$pjY^vyTk`1^CuJ@*K*EM&G` z(LDLDectJF=>Q;o-bKo4!oK+*->;Q_Y;VFCKDF`5bC&6YV665o9GI2YhBHSEf;g1n zyyAA?{%5akme21$R?ga|3XImFs|Tqfkh~OPCo(4!g@J&GKqDoMYoqdvhy(#X6d9Wd z^`2=VO%O!P>TiD-R*3%kAKu%kx&bQQI232aZapgynErkU z5XjtCLC7;1o6N2U7+D#gjBZ-*$aF~S=@M+~PC!sVA-L$Z)eqnvNtxxEW>v=R?URw2$-K?53f$7% zq4ol;xTdoVuQ&bpu^{lfcXrCsEsZqAHH-@;=3m%R8Za$5E)-ErOHU@Je;5cDFTjI9 zp-(esqWtJj^uOwcy!a zTL9rb!GO%qc?OUII$)g=ZCAY8KA>TP{ zvX0+((F&2=3depT9@i-MA`11w5zjn5lzA}?G2-ax0F{0^0HAatt$2Xzk%BWGwE)~Q zo{7tJ&;{x!0)>6u3#F3`5u&23+5*iC6oDWy{Ma5 zv9np8SieWkm^m7Z$FdAhsfM2s?PDmfT7EM*DB}|dkcRaoAXos=7%=(OwFo`SLqY}P zm|T#+V|;x;H7N%Dm>2$FD@g7~`9|VthZJz)i6?&a;DZldxa`e0Uqk)`0QQ3-E2Rae z$BzSm^mq@0ss+tzUxO&Wx~@%4b;$`dy)}w2wraSEa$TN8u-D9rtZM;0yIZ@IvEmdJ zRr26(3sBhyVJvj`#B!-B#OyrX&5KV9z9Rqe3YSzJE5mz5S)kypWqMM?;D;Ai+i0X$ zX+T3we#*BdBLa=`KKNO|0dX}x{Bsb{0}vq*XgXY0QC{@fPk-if0DxDKI_;$ZR?xW& zqX1j`U!iY&C#>g|?PyWao0@Z)*ivjJbMsS<=g6pg%lu4F#^=#nS@Fsrj31wA7xW8M zT9T33H|N>(`FS>n`XjC~J89tg+p{U##^8_62LS2zMtZ3a&1Pw^bH~c<@&Q_X+UAq# zaT+#9>4II3-8hD`TpB3RN|EK}Q+W|`e0a;$q(%7J4)xinkw8HHR<;i_J|4(|ijkrW z(5Oi=K?mWg5Tt&< z&c+rv>vu4diwp0S9EuW=r#X|2R%P(Ho+#vTu?59SbB7d?R8QqAv${QHRhY1^o zyf~D{p+lZKagf>QI2yJS{czgvC$AO*dzyTR0UtKB%li6uFeX3Ua7_{dfyd0+auW=Y z#4v{m8*<{OGw7P%`Q< zSR%9jt*8T6C)#IJk+%GvN#X-dK7mVA~I|l%%e+O}Ez%hLe zeVKk|+$jC_dFTfOxN8&Psg~6|+xHxbA zf%6Z-4KuR(_-J&IO=-J|ur_@%xc9qoS}|^fVaXJ|Bk%D_|l#y`oSK#Y)4AvC{A;-1lJf5q6{e zJa41{)Bm?RfLNY1N>?id*zgc|5Hoo2q|ycRGxsb^<6$Hq2qnwB0}j}~ps1*{qYZnJ z+l;)Z>2PuYkPdIbtHWy8_OHbd*INyvy~&=3RLgnAC=W4YQevteZI48X(kHDlM^q}D-YlZlDQO&l%Z3dSV>hI-K8ek>{W@6UfI&tqQ$+Ue&(Y+yXUi|0d*w#;nK z76`&AnU5a$p-#do0~y1n)nJ!^yAW~Y=b{=J6T9Kz40K3-ZDVAD_$Mqr zG;z$YayEV}^D}`R6{s0Ec8V9*zFvFwXOWU?2LLG{du*Yc?8A>|Adbw>{r9NwbIc!~ za-?W!en96C{MVaXbwnf&_WC4sh*=S5ptLujK=z4zXS`$H{`ax}+__WX?H>uiZF)Ed z0BP_ZXsELdn{N8_T7A}v9~y`EDCV-S>0Iv=V3%Mi=2iU9yStQUEyJ@#SC51=WI)0| z4@4*t7*;Z8lP7=aCQX`Dg9uzb)GN&Rr^BxUfHZgu?7KH;|J{BveOHPfFtz>MWP>e^ zbA3~w1d;&vJbV)?A#=RmE-b>7%H|%g8&EL$S*?D&+>Hbg4he$*a?qDzUQA_W<(NQ0 zK@pJJ0)Rb-6Q}6P4Fe7#;XAr&rKMw&G`6pn#@6?>m6$W(o7#``7u$foe>+y0t_+Qd+tf8==g__T*elg^PQdp}z3r``deEWO&doKCQ{@OiPQy za2~ueARscnrlzW@y1HcNmMxsb(7%@)nbnZpIRHo^Y1=z@pjCcb_B1b(`qq`w)V@JF zx^`h`uN`B;Ei$&^s2JvF@f^eEhp4^8e$8Wq|`=)w1 zY1$|wm19jmlAo=;j`%CLTT*#80LtZ-*=?BrZ1_4_VxX~7S$TO`Nl8he4@;=`n zXm7pa6~cJ7)n_;*0s=xoenBD1Eb-Ei>C?tJ02sup=;+!l+Z&&eZS{YZx|ZcwIAaeO zo_$JeW0hN!Fh_5$T1+@Ul>R!l{!LgF7Z~6Gpua?mipPaT*1_gt6^339Kv#eq$q^OZ zToiX^YOrN?^KwXcI?KJ$uZ;fmv*C=(8-hY!eqIqIfHK?w={03UG>x009|r&fAbexn zD%n`~XE6Sgm{Pk1@0XK53ek#`^(z-_hZ#2>zQ@Q9CvGg`$(4aP71p*&y+n0E2`E7B z<7!9T7L5Qv$Cle{C5~$%fU+q0)v~@u^M2@OAi&S`n#324_dE&;^7BWJ!U`s7#{8_R z0{~K?57*H8uB@;9t!!&}QaZaDw3W}xrC2`@hwY@ho8u-Z!4Oq{7@BdY^=B6#`eTcm zvGYtw0d5EOK4xC}9Y+SZ{kLIlGB5rxPDZ{}W)2Zu?~oJ$8ri1s=N7(3E_td02z&90 zne?78GX9XXUid?u3#hHIIaDxHJXXV z?Yd~o&aWZ#jYVgmVOW-{3kpL-bp8K01R8)Br7qe~P@1BRfOV7v3|EGElfX+bh;{IV zw`thJ@%{0r17My71%>&ZKQoG#nLpXET{{3sKQ zdoW6{6Qco<08kD97+sVn%~-jIA7&!QmZsATTqla+{QF4(l9?+XvpW^-^cCR8w)#3o zV|kme2iJz_t*5zX!)}KGb-zK)*t}0{D;^dzq6M72Az3aLn)_DR2CRw6kmCI0@H6>_YMH?5*pgxm4AGAF~sv7s^}Z4_mZ2NFMC?vMB8B&wDgxVtFlO{iu2IQ z`+Xy?DFO5n92wc&^-2MPDMw#;#B<8S2;e!wMSIwLn*fomWT1c$kvKNH$l&3>i~sb) zUyqcPNPGQdA6_Z#FxjP+e<&Sw3XN^6rKv5p8^A5U=2aI%Nf^H$!0*d7{>ghgl%zQN zLIrGJ&hobZ9G!+?)`jBvJ#Ao`owFVQ-r4#SFy;FY1&i@&hPr*lP+)hHS`o(k4i--+ zM~f6tQ9oX|^GhBDxLD{EEF#L)g9hV5Ope5Z%V993CO=J91Hg`kXJpHs#|AyNKbY4+ zDPY^4C&1(#G0Ddj=gWd|C2=*=gr+#%)tEe-NB<~5dLCGvT#3(uLjP)z9&S918Y67r+TBT;iJj{bW_4Jve`ihzr;aLj+oY}{@`YCf@=G8lX2~j_CLJI3c zG5_4zRWh-x04x85*_}Foe4U+=vRY?+!L6{iI(hnNnF_-z=O9sLD-pO-PbwcjkW+*O zMBb7$JMN163d%4V@cFr8VImw*lT}8d2A~LY2EvdKH>vYXK!G-h{aNf0CZ zyt5VnsAOY{no37=<5k=5kae|h#H~h6NxppXfN?Mo3g{Yu6#ls-)*wf&;+Byuo?LmE zpnB246A~K#WD70)&zZFV(Arsn_B7RPX#iV*x63h}oqa?kGSqT7`bt^ksr=lf|HOWmn zh5N`b2VH@jp4IZ&`m3?ZP-ooO)i4qM%7HaPbDJ$0l;CTA_JqO98;u$ivVgr~dnbUsb(z#F1 z{6Hz}5`HhQtk0K2r~L=k42YkE)@%EYE|zCD*2@#?>g2=5c9jOWrFguVZSiv%xxO^< zvbg^2xFi07!!7&QbEp){n`IQ8%Oo0C1h3LV!SB(>?Oy z+Gbfe^*d5t7{9;Dwe3%xJ_^fPSI9rMH_Os3jq+Y?i|oNhAwe(zm4q}HvdY3#D?1}U zx#Zok8$=n16MHyX%H_Vtm*#6%iEI9kgA&!b-YJl^08kKU1M{3FizYlHGb)!u%g)I_ zsdWSaN&?Mof0CtZwgCjLm9b^qT0XujgjLWH&Cj^2=z=8O-7m_k>%Sng$6Y0}$DY#XkR_R5(fBew?5n2&j2iHfswrTXhq@7V z>6{xJ1+o?ZNUnlhww80{!PS?`r{??sX13gLBRxoxi`|5BWrwWVcDd|mcvj|2_*bbe zoid=eOy-E+i5O5eF7gPbK-SAjg@I;SzVifmX!R9Prm^*nKNOdmZ=e7QtO6`k_Gfuz z?PsxS(ao?Ipar9IjsnS{KzcNH$$6GR=Ta1Ck=M4JfhD&~sdbcc|_z<-6t!X3;u(ALGl?b||t@8!huEm4&(q0R2;wGgd)fC%P3uS+-?A`OQDhk@t7b#x%s(g-b}b9?Y)W zcYKMwaoK#N>t7jBYKrwc7x7sk{V%7+c}WZu|kWyz#JVe^jnG&X^N z3d8hq+;*Y>Y+m320&8Xl|8nsheYsrhT3v`1pHH-E*EIRlij(B2)r%$A3BU`)u3Z?) z55tV@oLJ#p#!|omKrgSZ0OCKz=Qp;W3Tfad*}rP396I4K%*Ci*wKj~+-T7n!1snkM z@s@I6M^}hZf^LY?Z|*o*-r996mXm&44jTJ3Z1P``(ZyQ<5kcwd$w!>Lev8|E6gwor z+^>#IpAV^^8xqX6hH80b<9zws>P50*8$^Bd`6UmGU(h-v<-1JNj{*(=`uR?B&<`dM zus^-N{vcVqX8|^q*ab7<6|!&D%Q78O!RVr`V6JvBS3nJlcA%k{nFG63qVMhJ$3;)_tX+2^*Cno{IYUj`0UX;1Lkn0l)xXBA4RGhXo;B zYgf6f*m;~DWd(JZh_XheRj$N(b8llJ$_6PfsD-s5`_SF^0~i1hx}i*@Lecb`dl9d- zguZ_?2QV;m%mtH^;R9&psiSBI!|!StIF?IA001D+NklRnT% zxupUbBV_<0etztlgngIlVvH0DH~`p-H^~J>s3)Ln>nc^H;p)0YV89M6LQyBx#haz3 zWSxvFU61RHQdzhI%Td)!5r!M{plD=zw$etSNN6#DU=@sJkGXI!oN+alo;eFH2eaU~ z+fYE7JBp>gtz34XMc-IoBkOigk~OuHWOGA})VEYZyoZed074)QX#ajMp4%lIITUaJ zkfe7>W)80~l!KXgm9nm53_#%!W!eBtjEe$I3IJsVd$jvdR-oG0Av4!(%4agZN@QmxV21ngSpwt*R@v2p4M_{Lzp10 zaFha)KY}m0!Hf-GuCZ-_|7@AD22kv>MgEXMy^hj@7crv|Xc0(NZ+ZP2K0Q5zn zB!wI126F-!;xG1N7}}1i=V1MRf`Vb{=t|gF(f;nMZXPnRNT!Vrz*uJx znYkp60x6_`1Asvgza1U^B~yz+yUcRS?xUs^c{}(HB14zNQ6NPWZ~!ns!c+6lWw1`1 z1(RE!<9#uJzzqueh~h5Oo&pX42Kb6N?mv4>z8qK+xu4+vV+&=!>U`;N%D@3qG;5-J zw%h6Tybb^c_&z!T0LRTJ!i>Mj_sQGiXBO*DJOj+qMRycP83h~w41o05WeaosbL6<0 zk>bD2;@BC*unow?G+pcC&W-}_27{D9AD%eo|^}b^e!?|6xbBQUiw?>mr!b(irfrdB$=qJT!`a8BHSB{+4I{+Lp zwODG(uqL3xfEdcI-PSBW{N3B~k$-+%R<3V|3Ga_bT~ntlx%f%Bg(o?|6L_Xa^BIC`%3P@<50lq z0OAnE@5S%FKuv$+EB}^mXptkPmSFupe=3m^FdFcOx0=*6I4USIVkX>K=5NPrrvN~3 zfE$5+<@9~z`K22ppR_O_FTAl$-dM9o4wzOcYqvE?V^fE0s%^tc$k?(BU^}U*NGc)f zPpv7%YR5r&;F%5K*5BsV(Jh}nX@-JVMysITBH!Q7} zSz`)hd|4zZ=Z-U`!SC$s)VTwF6qCNec>W2a3+2A^_m#)qt&@BIu~RnffFh5G*H2_S zd&wl3G-H%(-U!V<`|orR-@AiB`NVgg*PVqw*wQ3HN(mfmwSWWQ%_+>2X#ju{FnHY_ zY+{nCPm%p5mHB<$ z^#CZ~0H7CQ(cq*E3Z~;`#Mk~mxdt0~;3Iu%t=SzYIxCt6S7fqjFrGh96p$}39v6DF z(O~8a4jm`YTy%guciDmR)x&GBHEI-Nhhm2>4xKZ`Fm<$%6!j`(b}~ODis-`+3gPU` z72+Pce|5C4yB`1r902q}EVlmhYw~4g6*SFJZc}}iEZf$h488*A^>y4zJJL<1Y4G?A zUM>v77>CcQkdI8pcHwc{(K9RLJ10-ZH0a(xqw6sgc>edE2a_X{Xk|&9eEaQ%Zo&6& zeM7FgZ8=N}`UatX=bJ?ldw+9TM2L8gN+tdYnBf=CJ+rO_d+~ASUBOme-SYI>mUz+B zbU0&8gV9=lc~Q9akNbeVcs4;q|Ll)mk}H1llGHb1ha|S<@f>-VeSdZ*s0W~Z0MESV zOGw1q$gTxlgzJ9whMacQ)3T*5wksB|IOi9pK&0j>?4R{lY*4C8a$x!!EB>=<@btP? z?Bi!X=zJ)vo?X`_UszBPGfj92h6BfyHb_GUx9ZLyvVs8PtBcY9A3dl_?}o|lx!3$% zp8flWYP1tqVMwM2NDxKK#}vzq31w0Nt$z`^1Ran*8k;+0!}ey`y%XSyQ2=ODBb9)x z8Q%d6i~jc9hjJ1|5FY#KaWZ{e>^mFOLu5fB2LK6PGEIMvA@jnnKia*wvt3p}yI8w$bsSSi@=Xd1i-rooS{$}rPlNHw24cnp?2ANVKI0%koABEK?!Jo8+l zD_Z!Rv}IoVtFH!48>wx$(Qs@=@n=&d+am`cHT&X@)# zl4IkgNU+&?m?Bg*%#QOp${x13A ze0lS()8+1O9VTZVF)1$dGvA4$i{zArmQM_axiyGtj-0NTJp|Y zJLI!JTpCv=(15ak`{a5#|36{*TSmoa`%)qf?-}}NmdFj?u<*36a zi*Rm1G_FDtcn08e&97I4vUqP(qkQ|$<#N!MACn9I<2m`=@86Lp-+)Q6b24!GE(?f8 zCg-ZZr5FN?c5iHHlMQu2Daw!R&~r3vBSr>Z+uSZkPmfd_79;MFFeSccS(D_)$uqi) zl;1=7_fLyp!ym`}=C5l_za6dn4)o1$z4tA-?IFy{TQpJ5Tryd%CF5}xWF&b+pc6Rr zl$r9IZyzpIXsP$c&6`#sPhNk5T==tBn=y%)2F7lvyVEt4Ro*UiFh0`Sc3g9=crdU39K&1Q76p3Uo4E7#+Ccr!ScoN}T~`AzFvw!@s?{MOHyF*l%*V_07t2)&jt14=j@t zX9o2f_P=>!{F4)-%wwEAtwDKtQ#(H5_$*AR;8jQfwJ;GLQxeGpG!5Pts|mlmE7mmF zviJE1mB}dVGe`#`SgbHyBpn@9xQSn$MojWg5CE|Mco;Cj04Uns4<_HX9*c1JnT?UC ziBF^~gtBkW+_Cc8Zy%8~=J(g@j>{Lx+RaV!;>(+iBwzzcvqW`5eLMEF%kAi{{OrPk zuSsBw7zyXB1%R{mAGrDGi!k}!-V}`6HQ>jZNaDul zXv8uHONw8%B(~M{MN9eRv&(jvad|4>IQhmK22EHoplJZM{I`GeFsbslzzGu!Jb=2F zJFi+Si~hM*hYfYIQg7MN5|KlX7=nE1?OoCZ+UG^RkKU4xv@p}$jI^g3qUWKOKevz8 zI2TOu6h;Z+x#JS78cYB<9%1S3ZoYV--2Y!E$?3;WhbA8q0{Zp6YBr&Dzu?T-a^zf? z0S}>@12O)x&mLeHJ5f2*?5ffb`WWad?)>p_^5C^cNg#`vpQOrK2_VsTlE|N?zi-0i zH<9>n)3J45fmVLQo=z+Z;_G)J0`CukY4D31+jSQqreT+ZONmoDNOw01qW^hEP0?co z7WB9q3c>rH-XNPXl{ZEjz=IW57Ri;L+J9Ic>)+1VSAK~l!gkSUh`I^CLtVyHL9c#x|f8a|*^(xCKMwy^E~v0$lsdZdtp# z1JijU6>`x;VkrCQYcTfTm-uhfs%bE^{qLYFaA1^aFh>Vy8vGbcgE?w24qy;(sc~Y* zm5*(f_qH@hZh}zI$5Tq(IgJz$)18uiSk;Rg?GQPM|N$kHOp|(bGRHp;193w zlvx-K{M^E^@_8Jy#}>sceH(1`w{sSrzi$Kp7*ky&HRyK_r>lYe|Mc;tQnv#-39ARd z%j04roLJ;sECsR_0Lai>yt6+55KFeceK$(62 z@y%fg9yvrFj>qSwrl+BNSZiG+}oo=Y^xtM4S z0HA)Hq3B#J1snjx(j;j(nNDK=>p8f-@3Z==t| zMU=~-=ub(-AB0;<20bb18;|Pz3)re;*s&tF17#-BI76 zD+&+SL$JzRT0F*d8tXPrz_eoLVkr>&Mq}eRpFvW90}MN08vNpit#am^NYi5)3>9L| zeY7K3a6Um%s+xcFmzD7`bM6RC3H}0>1Um;K0%J?#e=20G@mMx|J(v9A1JglgQ3u7~ zR%qk7r1)^TEtv7Qjz&p-#V$mbeC!@nK18O-!}V-KRZjf3I#i`)VZJmCeiEj^(EyN$ z6r4xE`2>}TCbA?-m<)ev@4@J29#|z`#LB~sJ_`~|S3Uk(%E4`CYI zAIEtF1dU;LQ1Bf)yHeiz)&lwUr}ya>^RtfJ^1GXB`$bn7dVz;cI3lZ|5TKfwE$q0T0>8A@^S-BgRzKcUykz!IFBHDwqRfBk`fM}T3?4{ z&N=MZhwFr-@c3)nt` z1MjcfBMblOF*)N~&&nVFzEPUE+OTs{V(`cR^G<@?lIzXA z2kNncaNMC2JX;4A2zd;PnS2t zblhO{miyn0l|uZ55>>f$7)C5USX(FG|Jlp(`TzG~N`@-^)k^hs)&jtxlc=5Mz|qh< zvNYEjq;+2+_s;fqU0OV)001T4(lrh8k4xygArZ+ntS)=qY2_DuH5%3j~KejmNJu7syqcUXMlb;imnFa8?A zIV58Km+**K46cQHWQnH<%MM9DC44dj9RxriR4L*d4z%K`q za1Pw$w!D20YhHb^3>+8{LqH|8fROf#=|4lhQ!?@LV+C|z* z2nu?Pd6~ZdKXbxNt zU87>V@ggwf7+$>=6kkozp0DlE1kujqsunOq4_|x_3st{PTGOFXil<=VlQ`I^PfH8!CuOey1=ds$$z@ z<$g2@2KjRz-VVsHddrLWE->B0=Z3&lOJ6@$uJ#|azXt!)2rO!yYBI-^Y0PQ@S=L&r zZ{zIJn(gS#n0RpR6FJQXgZUaL1{@h(j6P-FHIoKoeHxU~gajz(0bx=X@BKP3(n|x8 z+-)OB8Io6_oYfaC-=bQza|%rvaX-V+OWJY84u3^R!J++yJ**^>D;?GFrfQrM z9k)6P`D0fqQSit18Zj-zxAJm+#mG}!bI_n&B>OgSi+-ru4Q&U*a)nF-iW*o5v{!8K zw`4Wk8zh2IdD|%?%Enx4xHxK6v+|1|)t7kDO*k@G%3YLH5Ox-v5si|21SR{|#HVxT zS4Bo-{^UPf++>^T$^A1gt#|^HqKnuZgu_$EYTO(qs-$9|+ z)c_PWCgMQJQW?rl&te_V;Mb$GM_S3NPj<)gKZuC(h{%mMs4Mu5R?(cFuJ|7Zb6(x8 zLGA9kkIcQaRJG%9n5b$d4~L#74T%=P zuE$>zJuA58j3AqoQ(tVdmM(KlKOIPx^v@Di#IpSjs0ug2SeX)y35;ih$LhX*^=Wu| zIiND?JAz%1%$46dg#=$5AIlCStNRC&A8{1~Lv|OwLF$j>cM{)t-u4$L4<8KgvcD+)e%y?%8?lig}c5EOcOP+a--w3aR+ z=1{3LR!77APwubQS_6lRy^(O{7kR~pMpt)iO<4JAEpQGY+e|j$_C?R^?x3rZ(&*%h zuTB%vAu2d6%68mRq2t^=VJJ)D=K8qP^ZOm@?wfw$o5>x%w$b8t9D9xrtnmUa(qT;L zAE_}~!{s>axCbAE35pReX(i0)($5x^!e_{wD^VL>r{<4(vsvV*bX^ACA=kvHLxuwX&-3XY)2Y208A7{_~sEJinY!3SwfRQXOic8&_v zgb;+WqgQ(q)^ef4fPeAVpLua#bCJH5TgIRLTD(J4r*(Thq#SHoxbUQ?f7l7z&i@LC z!0H(YZ5nGoZFW(8*@{EZqVtyAxE;Tm73;*$>XbP&2>R*qVn2eFtonhnc6`IG+-f!k zN~6t>$DJUusE@!04P9w{dDk&&z-7lYj}}o`ECOfgW$tYwVH5l=ywbmk+gu%f#c!fW zL!FM9^j_|!sR5mb+D%rUQ3_7q7J(GT^4hDj5x(et(4a0DCZxe97SN1bEa1p?cs~

U5 zU@Q?$Pqs@Y2l?ultG&xeeZQQ1{KLxDA_W6z0-^Rcjg^IouwKz-XF)bwiazE7hLwVv z7#W!I=Pw+>k5d+^bdfC4IiEELk`dGg!yYyMG+2CC5Mf}eK#XdulF}^e!7+_o*X#Fy zVrCUGB|Drdzf2Sr_+Z;tv&vEiHhWZZ%hxC487R-tX4FdOEik-qkTpiWTalle(uYLv z+@IXP@=!yQ3Z^!zSJqow33m7?_&FQXWRfmxMsyp;qeDmIl*C7?8<9&?uA;W7NYhnM zW#yHyj3vVKB@Vn%IUo@%R>RAV5)Iir>E05VgO;L8YX4acH~-b~9g8(IWN&gr8e(ya zf5ZJ0<2Hdd`pfY=nz=4VGwT2&=oyqq>u9@%L(s-*yyiSqi1U&A_AXf{Lbf?C3q`(L z$(>Bk%e$ia1JAk-KGVy=LX*aQzbf1XgX?P+ss95=lpc2C&5WG<(|`n&EO=!`0y~xe z43tEv*j(!WbIGjc%&{MLs0E3p%1Di&?&@8_ImlnVkx`^FBQ1;{=Jv_?*AoVgf0G+M zs%xA5!Elm0>~qm(@aN%#cGavr0#YrbDO67cuim`A18B|1zcy>O;eTFkJVp zO~evGBa-8nL!&1NOl<^}H6=l7f_g3&dEuvlGg!C_SJH%;vOkOoHods98TcwcgbFqj zI=gJSbcM=R^nRFP;h4p}Lx~@>>e%zP*|9w2NZW_z2^qHCCpTUs;`3T%JWut% zCsD^$(&I81?KI2S+>QFFOe*@mZ~gukkl!e@pgm;r4f0Dq( zNV&hJdU<@H11L+}@;*_Wu`+`m>H`%&U^?{A)AGH+d1maYRF+G>)^p z#&?3q)ZvhJtdv}0R1O&aU-Ay$#-;U1#^3B;Ik77G{R;=20Wjo(2RagWejoksTs*i5 z8pPZ002$P_RLJSRtvW4u&+fy6LS*OYF+6vIU3!T44gEc_Wc;tP!2(czV*TW|RS75A z(6&^EX*nGOnP7wTldk^f)G2vn*>9~-a+q~DLT<}c_xH<*20r)o?-&`213Qg;TwPra zl$Dji^^?c@4zW<7TOU)@IuOg2?_`@Pdf*)$9R)8hF9ZzSuBW%}%20Bvt6{j6i7@{a zm9~rBoBQE?(%iYHul%z5+8X3(`}`;Hddt`!IuVkw<}$+#*E3B9zl~jA%&oxG6P;SL z+M0xgorD9G;Ed;h##dHo^77#LmzNveso$pf+WTPkID$Hq2O6CUK<%G-%!I|L;9;o= zjyn8D)~*Fzu)t}&7NOFAsi*4eP3(}L*cQAy-E zyJY8ELr>wLzrt#Npw==2(POi-vn`PC!g{O_peJy?-X?>gUR{an+w$`A9G<@oaUxJz zcWWdt_N#WNl8?|wE9G}sO25@2k@1gFhI9_9SCVErCmh*-od!v66Pg%KS_IMwBA#52 z(#KWFDgJc`>K`cfI0X0qlH`lkN>6ba{~(;Z*LN0QQ&2~-O+b*>|CJKh*+C$9iT^Go zfXE@Ik^h&J;OOCT1v!9nRFe@0m5mbbgFtZC|5it6BaLWTS*uIoyquWco_G$`)iUQ* z%mvhhd{<|}>M>Dd|4y5fIT0joBCg^~p3CzfK}p+V7-Au5oh|)?J>UneBo}PpdwMK6 zH(9sZ86LdEy2UaDvgYMh<*_B3p&OUx<_@xgeeonnt9S`q$1L)3F`eU|MIvq zb8&DH&PjWxymjIfQdrR;Mzoti7?36>z< zfOlqD*=WMx&PQ)Tq%-j^pJCzS4el9|zYRQu`Ms~{w5`TgCB;Qybg8MW^=`6R1lYM%VL=5kc3sm*e&BZ_hLCn)i`NY!btQ(2(_GVwBM5Zp7>M1m3gd zoN7eHzke_2>gk1-WbR++zcC7#6VpILcaZQaIT%-m3zLNImXNU4OD`-bc`{yY^ZvA- zxqqx)w$Pfbq!)~eFS9Hjj zQ(hXQ!kSNFh!XWZ{axcNZCj&21es?xLB!A=k=%wCUr1mg)m|bTj+?N(r;xpXzP6_R zr5i+oxWwbkcUvSSh+w!onj{aQQ;!&W%O$UfOhX$SoByMZN-3RhhWC4Z3T4Bw%K)@{ zNE=Pwr&OeK1;(%l(u#NNI#bxNU$NUUBB;dIsN`^SB+1M6RKh^v$ePsOzekSK>nh!> z%X7(ni;KlYYtk>vTdcQp&8x9}-l5Js*xW-W>lqZ~&2qiNRxIS9=30i$hf{`GADC! zn5BkLRGrI_e8)4Gs?kA7pN_F*%u?g~VVLQ}sM;b}pZe^Sn7#<#u+UV1Zo{(Jv}9doJgN&$j<~u(L};f0;daEgo1hK&t+cuBWOLw zNs79PR&WX8`t2*Xp@wglW~7o*K~tFS$)Gm%NuLEnSaJ!y%V5JIGU5bLP&1_WJ z7<)RJf+%gT*#tzlycH!^k~eE}o`GFTnf>3v9PV;?I3;RkS2Aj0wrnZjSaGh5SOh2| zjmDry*ygOiAQn8r65|J43;!R0tsI0#hN`QOTHq?^W;*8pX`CMtK564q( z2w59GBQc z8rec>AvMZtLK_H1)o%Ic{9@6W7DdtpL= zD>eFYZpTd%8g;j{f5%uZ3$6e4`=+=rR%I@{Ae4OkCjQiNyKxcOAt_U5%64!5;+R4B z8mj)(YHKt4r{G38Gnx*10VL}oWLATcYn8q!d(JE>3It4hWNj64e;X9WqfbtIziR1O z>{Vc?*PV9~C=Zu1z+gmc4?O3kXX4`k)v5t?96m`)?Ot>JcF?QcpFAQjzv;R4GEq+3H z(ZVqU*FF|OMuYzxLO4Jm^v?gs5CVpr2L7)h9i7Nq8$4x)}hY+_=|^R?jZ7>XR?y01INcoDDTYJ1CL#%tPXy5kA| zZX4)zcO+BhW!BKwduwmv{WU(! zrKP1xDjd&dq0w=7%4(*BzckT@vTq-n921d1LM-`#6Z+TbHgdrVXstCGno<%Ao-ZyI z)umZjl!j*e#r<-JnHW;D{+Lm-gwI~&A?#tLFflr&+KwaHbdT7Mr`vj+Q%HBN6i?wZ z@8uHZtY-0Bs;la%Y7`WdaD!!tlEc*M)hnnZ3gYt3nJ8>dRh+1#CD=}beup_in*Yzd;3RHpSqzHd#M&tdn2vz2d32G37 z5iBek4yTxkuGlK();o4Kn@rQCJ0#Yll~&LDd)WQc+XtvUw?J6*re;4{BZf|H@@(C# zj{Wffr4dR@RYy#F*oMD<)vYEP9L7%Ot9Q#@kmqL|o1pf>Wer!I189se5z#OMI={tB zJ6VL9(`MsB2k4M6b0I+mDCD|Zi>+fn-W)gAm%Co?Hlv_}wequ#EPgAmOWr9RXR_|` z;WLj9Lkg#MnQ?_FvqcV$89u)}o|YfTbFN&5BZIZf8Hy+2-%oRoU}kZnkrN0d(#nj; zr9{WaqYCsPSfEFp7YCiCV^V&f^6@I1Pt^IW+q+$Z!MGMkuRv<%!BhIF4D-BxZeI)ijJc#ZdpEy;%deCkp=(l0nvDmsh(cy1qxhn zuqz%uz9{eKQDPn5$K?&_idDN4Hx)%OFR>naclaor6)g5{2{AZOMwC8@_t_)NJAdTh zu6JHL$d%UHe||lZ;Y!JycUUbb!ATae+jhihyyr?RrKpg9d~p zaLBSK$<2&6j>V(-ow_`p+sI0X>R=J(3Jzz7A5xZ}z}_($`}Pu1E|Xd$b4o=a zA31AZ2r0VPg!FUuJdb^{wHEByrNe|G_^;=q1AbfP*-Lc(&IDo9g7_m>gR$G_hSb7^_ixr=f0?Zt(M34bOZU1R$WR|kyvk-S+DLSFh3MtTX3H2)Gi$+U6JC{ zC`<(SlGyYJ;*Qv>DwsJ(cQAZ`{u)0?3c9gn?uWBJ@GWfYY6jB= zBxro$htop%EG8zl)41oly%6jh$4m^5b@ z1ur9V3c2j>l7ibRV9T~nEl>9G-;$MHn#z)&|)c~dm7^d zi%z1Tc`7Pw_wdGo0y_sc2D$(fS%JZbPy7&H6lJ+zrdMd@0{bHgg0|CS)DVIj3^#qT zw=R;M5!O^Gdt;H4jf4Pua$0i96F7d*cJsmg&DP&21y`3?oX(uCesK;KkWd@8lQqkz zIL7**|Ad9qFHhDlDXzvRq5kw656o7+6~$$JU6%dY$?mM1-&&w}hDWDrC$H}ls`*bH z-ELWlTISD!W7lf#ECO3ib=0mA^HIk-CeN{p(C}LHPsFKO-JGUs1bZ(H!)7y5ekytp z5?DlqdtX;I(EqL9HW?f2P2xQ$N30aUHK)k2!X7~k*5}jq!N1@<=?a**WKUgC*$gYx ziHPJT>~>12(m{rnH=O9rlIkB&RTr8z@B1`3Q&ztza60M`kvrT6y3tq}z~Mov{%Y6* z(i4WOJJbEjG(rg%WS>hY^MzD8*a|y@dQ39KlnGutZjUSQV?QA-&*zQskoAMGVrevR zY^kfw_ZVUQ?oDWLT?H!6M)v_6z2p1t_~Ckj;?sU3PnsMqR&SU%Jnqzp$-8`&JvQ&I z&xN)enapd{kU7TPV#bQbtjZ)GQd^E~rgMkXaGuM|Oe|t*{UAci8aNY;+WYj65u< zVL|T6a~1PFRk+?_?+-21>ms+0I0Z6!JaxJSooYhfk@IIH!A}SL##j(h^l^WRtqme1 z>h?0DO+Bloq=SwDZMtM|z=X$yMNf=b{0R7cG8MUTweb-P+h4Z}6dkntJ&T7aB}u&Y zlr)7lU_#rn;%f;bi#!1+!o}el5y56PXsEj6_a7q0DgAz}zaD2A&Q`n3NZBIHn|+9; zp^|k$AaY|%+Z4jMs@kW`Z#ij|R=k!$gzIu~*?SuZwATVux+8Y9uI^Nurm=?L}ntByN~6u-?lo1dEms139|{xz=7qs$mZEvzjk3h z-S=YQtX#ZR*tn<`5FiXkn)*J8jST+ymRzBQ&qYu{fA)vVM%)*&8T@8KOhR_y{(KrR zvBW)JB-zH5hYQw#QuRi?7Dn%HG7Jf^5Sa3l(jFAOcF6}YLOy=}X2I%MXVQ>bgA`s* z`HWA{jwYUl>>s*;{`n1E_&o8rFT5t=A-GfsIu(&!zN%zk%T_YSKEBFl1)FBuiYZy_ z%Bom}GS3>s;07BHTCE=yMSK9%67^`g7}-^m2ROj5!WQEd&I1PQ`TWGJVr zFj4(cIv=Xf2foV*)AzX$;)6^s7i~uaevwvG3g>cxCB3Rk(Av||Lzq~?so-_b)=I=G zl0$C(BoF5OPb}H?H4k;ZrvsFVLKg4On%^dQ z>gJMembfEV3mPIzOzcq4ovY|gXMY8!m!MCBc~zn6@O%lPSO;=e5lzW&VaKLpso@g5 z;czw5t>RxETPt%PE#J(_n;#viu5TO1>@%nMONRV4Z*g4;4~?T!zb;$uIIMxvY`IMh zb91sqUwN#-vrU)?Z`7JoD%~&jcFKiPe0RnkMW_8Xs=gNvZ2mp$T4XeYC+t_tz{yzu zUqADdp7jM#WHYo;QO)J!UYO9%PFV~q!8)thQxZ%Qr$TFQUIzuWzYYum=>Xjn41PgnY+K>zW0a!1qB9b(kW2`x?dKhQ&Wi zPL+>}`s%jf<{tJiRQS9=FL!E4qz^*{e7e)$X=ZP&# zxM6Jr=v)gEoK_68G=AZ+&cG6=Rs`P<~)yUlc$vlJa19DLl~-hPxj`$+L4-BrJa{fSV&p{`DIR@5k+ zB=dGO-}*_x$AeaP-+O6xyG&ByqP*ckuah)c-WBf}ntbyr^7ti0!IdW9mf;jWLDBsTX-*o1GKEQHs>uf>9p>}7QvK_EPue{U!`HR?0S<1RqR z0)#9;$O42cK*$1wEI`Ntge*YF0)#9;$O42cK*$1wEI`Ntge*YF0)#9;$O42cK*$1w zEI`Ntge*YF0)#9;$O42cK*$1wEI`Ntge*YF0)#9;$O42cK*$1wEI`Ntge*YF0)#9; z$O42cK*$1wEI`Ntge*YF0)#9;$O42cK*$1wEI`Ntge*YF0)#9;$O42cK*$1wEI`Nt zge*YF0)#9;$O42cK*$1wEI`Ntge*YF0)#9;$O42cK*$1wEI`Ntge*YF0)#9;$O42c mK*$1wEI`Ntge*YF0)#9;$O42cK*$1wEI`NtgzW#Ako`Y**-Wnh literal 0 HcmV?d00001 diff --git a/resources/letterpress-dark.svg b/resources/letterpress-dark.svg new file mode 100644 index 0000000000..5af9b89688 --- /dev/null +++ b/resources/letterpress-dark.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/resources/letterpress-hc.svg b/resources/letterpress-hc.svg new file mode 100644 index 0000000000..e566c8624a --- /dev/null +++ b/resources/letterpress-hc.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + diff --git a/resources/letterpress.svg b/resources/letterpress.svg new file mode 100644 index 0000000000..ae692aa1a4 --- /dev/null +++ b/resources/letterpress.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh new file mode 100644 index 0000000000..4a68939a91 --- /dev/null +++ b/resources/linux/bin/code.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the Source EULA. See License.txt in the project root for license information. + +# If root, ensure that --user-data-dir is specified +if [ "$(id -u)" = "0" ]; then + for i in $@ + do + if [[ $i == --user-data-dir=* ]]; then + DATA_DIR_SET=1 + fi + done + if [ -z $DATA_DIR_SET ]; then + echo "It is recommended to start vscode as a normal user. To run as root, you must specify an alternate user data directory with the --user-data-dir argument." 1>&2 + exit 1 + fi +fi + +if [ ! -L $0 ]; then + # if path is not a symlink, find relatively + VSCODE_PATH="$(dirname $0)/.." +else + if which readlink >/dev/null; then + # if readlink exists, follow the symlink and find relatively + VSCODE_PATH="$(dirname $(readlink -f $0))/.." + else + # else use the standard install location + VSCODE_PATH="/usr/share/@@NAME@@" + fi +fi + +ELECTRON="$VSCODE_PATH/@@NAME@@" +CLI="$VSCODE_PATH/resources/app/out/cli.js" +ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" +exit $? diff --git a/resources/linux/code.appdata.xml b/resources/linux/code.appdata.xml new file mode 100644 index 0000000000..ab9df8c25d --- /dev/null +++ b/resources/linux/code.appdata.xml @@ -0,0 +1,18 @@ + + + @@NAME@@.desktop + @@LICENSE@@ + @@LICENSE@@ + @@NAME_LONG@@ + https://code.visualstudio.com +

Visual Studio Code. Code editing. Redefined. + +

Visual Studio Code is a new choice of tool that combines the simplicity of a code editor with what developers need for the core edit-build-debug cycle. See https://code.visualstudio.com/docs/setup/linux for installation instructions and FAQ.

+
+ + + https://code.visualstudio.com/home/home-screenshot-linux-lg.png + Editing TypeScript and searching for extensions + + + diff --git a/resources/linux/code.desktop b/resources/linux/code.desktop new file mode 100644 index 0000000000..99a1189a0f --- /dev/null +++ b/resources/linux/code.desktop @@ -0,0 +1,27 @@ +[Desktop Entry] +Name=@@NAME_LONG@@ +Comment=Code Editing. Redefined. +GenericName=Text Editor +Exec=/usr/share/@@NAME@@/@@NAME@@ --unity-launch %F +Icon=@@NAME@@ +Type=Application +StartupNotify=true +StartupWMClass=@@NAME_SHORT@@ +Categories=Utility;TextEditor;Development;IDE; +MimeType=text/plain;inode/directory; +Actions=new-window; +Keywords=vscode; + +[Desktop Action new-window] +Name=New Window +Name[de]=Neues Fenster +Name[es]=Nueva ventana +Name[fr]=Nouvelle fenêtre +Name[it]=Nuova finestra +Name[ja]=新規ウインドウ +Name[ko]=새 창 +Name[ru]=Новое окно +Name[zh_CN]=新建窗口 +Name[zh_TW]=開新視窗 +Exec=/usr/share/@@NAME@@/@@NAME@@ --new-window %F +Icon=@@NAME@@ diff --git a/resources/linux/code.png b/resources/linux/code.png new file mode 100644 index 0000000000000000000000000000000000000000..e06d2e6d8da31f3dc90337dbd06979b5f3f79ee1 GIT binary patch literal 44657 zcmeFZc|6qJ`#*kWNR)2TLaAu9kc6_7ZKk3{i9)huNs=rhWR02jQ7PR?MWWnD)(9;a zNtBW#QP%9T@7v70e%EW%eS5z@@As$g_xJezna3ku^E%hL&UK#Ga;|foGX)y#**<61 z;#mlxIXib~??Z?e9(fTzAN-*$l|2K01YCERx+64u9{!Jj!tRJ6v{Bl@=zzxoz1?cI zROfXzhpC6`)_FU-0yRP!WN%j+TSq$&$wPMb4lbIc-sen`q{CrNl8LgOf}ZPEyQ2;} zeBJB}efJpI`a0TfI!q#K&C>8z0|w4^9yXHR&Q31wYTlY8E?+fx#$U^mB)KFWj+&&+ z_<)iJ^b91oQr+w%mDj1r*(z?>D5<(>oucxljq8=xO0HK>RFPNMB(JC>r=X~&ut`lp zQS!$RNoy87X}BFeqP9!@z_@`{@_ZIV}5FTZ}h z98k!)`?z@6c+0uCOHDEP$w%AH-PX;))x&}6B8l_0IYd3-p-Cd)1O5H;V_wd#e-Gs1 z{sTKeQQq6eRbFwOg8YB#blCRqKCUO+oVcPLww1SYvU9d`@ortu))%_^- zzYO_z^M4%}bfTyC_t^hYUT5e3IJCQmjwe9y1JeK4)7{9&)lPn&ojdh}o2{LWCrm}^ zKh565;mAMA`4>)j%YT*K&fDR?bi-Swx^YcmN<$hjSGBEfb~YYVHzO+5No&f=41Pi= zxpgaURjQKGCJru#sb20f8uI_%ZBkqhZoOV%UGDE7_IHfJ`*1y1&BhjwW?HtmL)abGkpGvr|A_T> z&mY|!{{Jd)ivP6k|9uRvqjoO;n?p^p;!^#0ChpWD9$q$XcAM?NCjL)+eTwV9cKdhs z8uEDbcXIewMNGZ@SN;67*#D_d4Lqo+fh~YsHe4NmdH;`h|BO6Ee*o91(+MuU>UstE zkypfj|L8is`QN*m{_kC=S_zl-|G=BN~8?IkMG@al#T+`9` z<@0a2ehJZZg5PjWN8^{zzv22NMAHd=!!;d^Uq1ha>z5EsC-@E5bToeX{2Q)cLNuM= zH(b-v_~r9&xPA%Ibb{Y-O-JLG&%fdNB}CH+e#12#jbA?hhU=FQO(*yb*K{;~`TQHM zUqUpU;5S^;(fH-_Z@7L5(R6~}a7{h?2#Hevw0gA--o4zZ zz1hh7YZu!w*rLs6wYS!cE$3H0eG{_w&=yMp-HZ9j`yc+X^2M#d<5jcfSND86+TgJ2 zWwpfl?@JfnuN;0OE~(c$lKgVcX^)iEp3lox++Ui$p{0j%{A=OOx;ZPI|LEs( zEJehEdg#y=VMNbY3+T!JWK1(J;XM1gRCsUo3;EhBjLPJ3`v}I-gTtdqtN)N_-#9z( z!LYApPBIC2(esr?ZX_PBAM5|VEubl~?ah`ETlI+-FSZ@{;M3T5Ds;mvewYzVA*w4V zKyIS`YbB}C(bSe~oOkocLO;d6!T3K&=StaDehZ}Hdf7^9Nk@z;^Eh?YP3=yiQ%nA z3d@IfoD)J>P7^PWG8KM)rrdK`LG|m>x5Da=O`L-XC0+d#yO-FJ@?b~2QZ(~a z!<~IUn=XDCGLbr@=EzD|v(GnzaqAMpFqZgJf>%db;{D&U*r~VAF}%MT{S81)EHllk zw4mOqb0lEH$?DS2CrFK-Vr~pSC685mW!&KT31WRAbFuK%?%NEj(RM9Qho0Juo_N}X zY(?nb!w-~?>3840<^8z2lO1+U!FslE{}>mk89%_b-W|~Us*>iPQ>p&B`&Fg!85^_i zk*{xS>zc43lZWmP2|x5IcCD#x-R|uLwl@Cc(_AIzuF4@ZJ$!~EwF@2}3x4P#-^g~!8aFZ7 zFR{tJ+1gy^C%O`KA8JRcB6B%HQjG_+jf~Ec4s;GTc31Q?-Q55At{Tv8|4B=UXGc?+ z99h}U9NIFKvFNCEDPHQ-lw|Vy$)M+LMn3@$dEf6Y`p|FHDvoJZ<)m^&RQt0WhS%rM zqIDLQe$DT$osnBW;2H%&F?|J>QpjJshnIDZG*Tb%*`?KVzp_e6)Ew?H8$JH^rwLGO z*c|5Mx18RT6PSIDHnn!7AdBXGD5rYzU0;j%Kr8_^m)!$$AH=3fN!PJPwU%J1d}`_d|WMY_=59z=Z)d_`X-;R+}-4s8vj!T zlD#F^OTSf(0_v=n?=4Jw3xOiimsWfI?`dg?W5ss^18HBnhXeGKt-S!JOR|nhAk0;q zQw&KGRizf$A#}OC7ke&I_uQVuA&1e#KFr~07jXRj93#JEjke5;)WlLRBJthGY5#oq z>bZi5e)iZzv}0`@d9Q%jl@=HI&^jyUbzXid{C^D%<#Hp-W-a4 z2Sn&7s%9-%;saP5c=5{|M45cb;ek>cx{49m?3U3 zY2Qb*oSJVw&jI5;r0h7BRsBwA^J>yE@P&pu8_xZRP@c_WPiJQ^yqA;KJlwp)+Mf zmYR80?fK8>vFl3YbA_eCo{QoXKKruynARnnptafsJSsD_e?(c;VcPhJ5O(!#XDybX z8=Mxjiq@3kGVXHIT=<&+;U^#WqSAvKmkO7!*?jD?Z;rQD(x*9C&=z|1-MK#lsS>s# zp}I5W8%^GE%;W@1^gdmF($osMxcUF4JucJW?b*2Y}XC-X@TeB2V zYKmu5M8*j{=G>o}ILeXv6U;1Uc(^?=QM^}__*2pnvW~l5j(Bh4iKgy~6R+k%gLU}G z6G23mt5K@Wr1sv$HV>9=^zajos2KL{9?)gA7e8#JvDOIp=btIPJL-c16$z=Pdv_z0 zw|xoO>zjx6?gZZG#}Giw6FL`jSl`d?L?ZV`7vN!+GN}JPJN=0BKXP6E)I^(@Ae+Ow zXSh-aJ=aGKM>#XVICO!XyvmUTC#{>d7%_bEy3|#ufCb=#C5a< z=C~=d65P|G6YQoujCQ_u%*yOhE|YSsjHdfqQ4A57}B&K>$|I?yCb<^?L=B zI9+*M6IsLK^VgJw4QoKR4|Fi|?2E^u`t2{^k+(sr_y<~amI`)5b6$U3ttCsZg3?sF zZmA}2x$7m->zkVEcu-VjXKf}#hm6+9%^f48bxDau=ibVOVQy;dhV(YaVl;l}rzD5L z7+uv!vBsx2KY5(yVnQR4)XHcY*5)#=d8YJB>F}wMI5SXZrn)XU@zulkkwO{yO$1@o ze10WD28UK-?5ms&CFJKBfc4en#6t1jlw+Ky`{hV`WO?v4PI2N4Hda{5%*f_s>!upk zS{A%&$?#?m=t2-&!>{_$yYI{3>JUhBP@dS3Z%%b6EN1%Oj?=n6KqM;5&0expob^cg zs^(bNxQrcclnRF+qP>i}PY#=1ou=4>T_1HGD9ViR2R_DA1SCN37GzSO%&IFvMH7og z?M2rJAK$+SH~Y)6<0(1?*Vnx8#G`Ph$^OQV#bJk1%`+f!_HN+EyhT zE(Ymw0z6*za0EZS6w>m`y(g&PJMZqfCp-79S~Z_Q&-Yv-W_QW%<~K zwGoUbov^Ykjj=Ww%3THeo)0L0_`phEkUMOzy~c(NYY#$ahG$~^L6KKE?c|zIZdsx! zR;K+&$=mF{zpyVQCkjde3E$z4f$x}9;$+)G70VphzKyRcC4v|- zHyXA<1Q;v3VXV&DuxC{W1-f<>;!9GkWv?Q%)|qz}f(0D6%SjNsY}gG0L>lnnGN^mu zYu-IUr`E;+v+RD(v0G!SS1(JSkIqw5L{Q!^$ZZhBI5v`HXu?jAw-_!}{dCCBv8(!v zm5HNHfjNZ3dBPEp9UtxP8`D2rG(+Oi=gwE+Ci`to+w?hit}y&U2-L=*|Nw)J~{ls;ztC5@B`p&Lb z5cIXeV@lLmP{2yZfw;z()ZVyRBkM))llS(SX|L_N%~<^`XY5&eX@y!s=keoTw$WC6 zySrigADJOP(u%zO;p`MUa>_$NyU*P^78a(LU`1VU#%L}0cAu8rkIcu_`6YP)yPqAO z+PYLL-0WOi}B{Hp_R$r1hX!@uw(g?E5WEjmeghri?EYBnYhhN#G87w)*yQHAd65`zPKp3R1C>Ln`Gx+eQoG)&Oh&ujy^LeZ_mUUZ zR7C<(co+-mY|yaflIPA_%7xS`x^-|Z;;zM~b9gI-Z%eN%Zfuw1`XjF}CZjQDa_ZYw zY`e++t|9$vC-TqJ`4L~f2(0rJkaI)Iw|hIyeYN#XNOBJN*4uKVvvsvK77AaLp3!wh zfL?O(5D{4i2hpS)S#d@@eAO(!cM>bzD*>tM;=DZ_{;*3`xC4p!*KR$UQ)$7Y)O!*~ zIW`I)^vn=~=ldT*r%Ex$sg_9r6ZS6QS2(Ji1q%ozWWifS#57x9D@a)cDVe$0BNzEt zV;&_;LHnxCS%f@a|LmMpNZ!&U1bNRQL)68ns|1H#O#8svYBmK;X6aADc&8v{p#*Sn z*ji5$SRpek@cij-0ssC*;eXva*j7CognA>xH;6 zyEreY9mg^xr$6Oz5N)e6Wl$*4cP)t*rOvoZL|4@+X(Hbp|W>AU&yAim9gNhjyaaB!#>8#tv#IhaHfspWLEXar_%1)!7Y#~@(dKk z(iBvLWC#}2`;~*MKc3LJeanDXa+I6Hu^-VS<3d9#L3902@rdtiXMF~@G0MXd?0OiC z?)BMeVy5k910wj~Uv(_-!3)O30Y``*syCgXrPTqs&e?@jWG}8n6nq2Vn=+d3Ja3+I z1)cSMgcRUmw=z_YbwT4v8@^Mrlgt;P^Mi-HQNo1pjbGq}XjabJ8c-!T7rdb3mJdhX z5t6mYRtdMBoQ&iXSg`z@m2@FfB4kF8%)2#$qQy&SI?9suu(=p%vAfDT!+Io0XQc8< z=JO(YtArRswa%)7wN=ZM50W`BbOkX;xSpid8S}r6I}f`8NS=hczJj7}?$;O)a!Z_; zN+`Irsj|DaVasR0kc=E%1ioHb+B0E$Dxb!Y7A)iYvdooJ9 z+&^eEi z6%BwqxUk1$nS#sT!8}=+5NU zwQTzV8Q2@w&Cb%>4SNm^)1HyWVqew$X%Z$$$1zG)W#q{xDq`qQg&5wDh&Tw*Z(wz) z2df!q05Uvh!?P)@H>ZW0ELm9M>y|P`xn`vCM{wE(i2Z!b^uDhhj9dE!3TF|6Mo2^z_D@lXW1 z_Y&#fo;2C|y+5yqRmH+K1VUZ5r_aiO0Zv$}g!M)sl7aXjcDmlwyZa4<&0}j`>`xl@ z1vaXX=ohjG=Sw5YLjKgm-BlCO4{n62p^Zei{4^#nm!dP7v!H~W=lv-J&tsn-_bokp0z1kgh~PF5qzU<6a~9@2`HgY( z0k7&YR?q}bb8#h?wt6HSYpwXc+NFB%mac1(i~RlcXUibu&F7J{dZ-I-Nq2q@Q`>W4 zJLh;1xRU4a%-1PlA_giD->YKkIa%EosNuJ6?GsP|-2_#xODTu}Nho(AtMQ=)7_NaN z)$>I=*~A=xg-no}KTsA=ah4k&Dy~5|{sUxq z`Ir6u$_?q>2e}FLlY5Z-d3Lvr^!S*rEN%l&fcSc-BAZOPR^txF7P@|ZBpzY>AkC zn%l5lg{x@j-2T19;`|@$eofv#mU3SMuh#TWCgcv!lumxi@Sb?cq(XX?8ol;aGPD&s(v=ud6=6Cc^q#PI~4t>bjJ3brN*KOrEwh zgFF$QDVpXly=${an(G1sFBso{GF9&B&kW)qvE2O&`R-g|%s7Sf0;su>8zPluZBz8U z)T!j`H5u8?;e~)2yIo`!co@XFY(gF$4gwFY9}uFve;iF-+V>7}v+&~O++r8KOc|?a zPhrQ=LqmgDJi(4o-QHFpbNxrXQY-*9RAa=cW#<*JmvPu$P%f<4_J%mMuNONxXf>yv zBl;e~KlxeOp=Jqsh_Y0p!!9TV;e{53Cnzrqn=$zQt1qjWt$YqeNq;SioaJ47{Rctn zU@LQRE?Yk2(p$%}M*v7;*d<1hoX}iU^KRKqLSF1L!_$Y_M@V(vG~+=bN}1mFf&|!e zak^m76S?mX72By@D}CQ!H|>_^J_y}?^mGbl zBRDBcC0!JUrDU&rB5W!q`|z6QAT^GL#u!JmHTJb?61iKoWaXrB{c1nQgC%5S zks{32B<>-DkBq8;Ns}6HiTu@x8+sGl7}{&9JQniaF}OJJh!^(t2Z$)f12W5P+INft zJ;JfbY-a3SlzO6@vC%9vh&YUI1e05$|2BiU1Z$UmILXtjsdBM{!$I4Iu z#7T|U9g3Xqx{`h!!az_8fHm*l*X~#KMu99??Smq-dymslus6R# z(tOD9vX4q{<|N`CJee?yfI2RqxN~5>^z0{A0s}EqDd2|;R(%2(C3=-@2zS>c+BIA`9gCKrdUvq(esEKVwG7T9l zywOf3eaL~5k@c8XrhTKO@#_okzxzTAH|9suR{X9bkV>0`0M-gdP%M>C$RCsQ@;mPE z>KP45#~&M7O*o6oaW8D&>3(xxn(}TA4<){W)VLEDL+}+|+_Pb0RRa*V%PI;-tqf21 zKYg6mdX0wij;t}|H`oj4=0$x9vDUcE;X&q4^wVYVd5)&dK?!RVeYar*x?C7;73DbF zs-!TH%NNRM$XfjFV+lGCX2phB4~0J0j0RirG$J)VUJrSgXVYw51>ar0DQk!*^&noe zp>0_NCZTR@e7fv=VscR$&cYr_7$8)tp`osVTCF9mn~d_-9hij=a~IBJjErp_Low@| zJcw{GjZ5iFqzmJP+bn0+OeAEs*AV^$0y<~NDQ(kci@zX6G{EXnGyXNGwZ<*VP_cy;SV}E?I~S)_*OVJ!v3- zqO?l_S{|;#T&up_RqO}k75EHeVSO@aKF&|s_Q4t=+-A%v4S$PcS(rZ` zG(M}mPyb+fpNY}AY&=Dc(xXY8e5)Vm;rFeYh9s%6 z5&()6K;0z`Q1gB1Gr7+U#W-Et2T~sLo+&Q_t&I=4YTv;j?#k>)Ia0#eD!kWFm_T{T zLs!;;!cO~g0!&(otsvQ(n6y1B?hiy+XAE3VHo;y`FJ>;H$I>=_)iqeQG#n$K@eu=U zybc}JA&OGPO+G(=)YS=c)GuBpAdTpb;T&D<-HsDBopq+2;2Oc&{Bwo%Wx$a^?5;YY z>Q-oN>E1Jdrw^qHv1 ziIrkmpE9mLxMaWay#w5)y?8mf$9M*6{;Qw1oMg%1d|e{Ku`U_!k8r=3Su{pQ29k!l zeS1TZLUAEYZK8A1@gZUSFiQeS&z%I2#SELM+i5%K{WKIsIR>!<%JdD*?3&C760EqA zD5`WB5{&7m!Aui`kDCq3Lx#3uK1!U80Bau@+wfuE@_9%wtv~W2l*sB12S>>gp$zIB zOFFc9(Zp_~5L}2oJP0+67Pswpg?gZdda{aI2ANqx?OkvioxvF^$ASfrXZaY^_1^SJ zGuJ1`Y*ritn|5WcLG+TvnC|B>{RBRNqZe|dA%KD@?m|_bgKz*~S;4x#^WGyDbv=B< zZ1Dc0!uX0Bn*}%#@ z)MWu`2g}+LzVEcwJXDhy@3)q;Z2ugWdke<{9_zl9d1j2Pakw6ZWdL0{ataKfXQ`9c95WL64Q;9M5)F5`j_?|E%s}3fL{!Y;J$bSv`)} zWnE&C_qr9#a5EJ!p^!Yhx_&<1U>DN;%F@>)ir9Zf6QZchrzO-nx;2Td8qkW^HKNLuC5Z|a+1*MirG~LM0sB{Yh z1H_{J19k`0D9lLmf`@u+}4(G=5-X#Y6Hd(z{40z{V_#O&Jm$rM13m!tPI?7^= zt~F7zrIoo*SpF#I%{8++p+2z8g?D0eWtTxb0Qmx2nb(vt+MPH9st#&QoMNrngvW-;q1qWv?UP-GxSpQUr zaChU$sPwu{0kG7INihJ~an$$7D_+>m_iTL0BTqnDru@FX$K^ASfd(v=76vm2dD7vS8D4wReRwT@v)1vkJc}I{23PNA7x|)P>aQs!?|FHxzs&7wGT_qc3|h?&=e#(7I23sFeig(GLK0cOEx~K@oKv~*p4G%OKFEI+ zHmB03N4RGgFh=I;m(OTV*75FWKcK(qAY>kn_;eqDt$kuJ9B$A%m+WexM1mp~EnCEc zf}gqLf*l#O3gjeXdBev_<(55M<&k|=bYLqj{~YL{@HB{czQaS?2A#LLs#d$t1@$U?az$=lJJnP+N~1O zpkr}vsdyHuJWFKYpqc5_U9yhJ6cqLPE2t#9^DFHEoL4CD>#rd=ReKyNa)e_n$)$Q= z0D^adFzcc}&G`BtT)4yS723Kj-^i_o5VmvUz#SC?ugVa*cI*o$Y}4+l-O#^7cox%3 zeAgX>M4J0)MEZ-jVK-%N(3h%*etf~{k*9I=^P3Js>XRG~s=z6Fy$- zz=K{d9X(2W+*4_`FEo>ln2eA2?Na1F?g%iY64L0KBiuOyE3J zp^7cBP5z4voi;XHijkoJIToEQ(N>4Yui&*fdgqVm3RtgQ_BOO(g?$d(m)WkZURuk@$F9X;+ zitzgzq9L1+!dm5(^qO0E0&A4NoFeP?#L_tur`Wn0pAZD0_&a;X8z6pI_hf3$!=&|# z4#VOjDIE9H+7$dZ3rLzg1P@TVHZa?}3sipLTDl$qck)e@T5!)pzz!VKTu(sK>&nX9 z<<{B-WsIXOtB?Z3^kxje&HG>+g51V)Pp)qf#r+m;Bgng@$utyE;m`o8DnS@8k}@eEKY2d;~l%L$$=p27C&!OjkYQ5<$dGQx1WezLVO(1ni+_dJh^IHe>X674rpyF zUh$~f%LjvK0$8!Lf*nCIYllVqpEFtGPqmZ7js=)kBvY>=-MT^!;s-FpG~tzLB~G6)~vuizOj!k#<$f~dov zx*-M`(--S$PFuqW0YJG=uZcM@v@*f(!se&-&)FNe! zNVo8tWS2@4=Wks}dang0vG1{{CkXZaja7WzAl%8UksO`DC69UvCnI-ZJ50&tq1zGA z{2g+nyKBE+^80Smt2R-7BV+q#-!r=iYeCU`pI8#cK-eRf<3$mUI)DdwrGuYDM`iKG|BTFGY7#Bq$FTz)$s0WtPjj?ob+)&5uX_Lu*0W8jU!?(|C#Pa5Yz3^g8$iAo3BoZ)$fdZ4XqT9QY8`-&8p27F-2= z)-FFVq{{qcVP^Tf4XT^w>WlcQ)D?v#RKZV=a%LbqYblEK<3h-=W=Rn7Jb-kLs z%9KP6PMw)oHHLLd3>NVsS3_{%?O+i*?yf`z-sb|==T+S0wBFnU7d^gfl6`1NIT1xO zI6*mC=W-B%E`C&}z#p#wL5Wt<=dEL8>LSVc^&M$v5&jYmBLYf=@8pO$zyiaMUY1n)(OowsaoHIhzK ztyiB*m8Wf69;LCI5~IDGf*);PwJ0SIE z@Hkuc{`-V~%0WwqgJ$D>4g?4hjpbx1I41w|s1B_elZly&LaxLS=yRhEZ_dkSvgDZ_ zr7<@$qM=JicKT-_;u^RM3su|v3IcjHG6rx30m{XlG0chcB+90(^wn=I=-?CYlRW*r z*v;&WBJ6wdG5e?08OtRhd6AuPWa~q1De6<5C1*FQ&pDj1ML-XhsCJJxTj}qCoxX)- zZ{2XTY=V14LMT|(xeo&M6SL9SgI+my8VTg;+MHL`;?kSUxaDpNYyR4^RQ4|0`&n
-a)w@PyqRLG0|)sFNsUKM#c2dOvNj>mWstQL?-<4zBCGp$_0j70 zT@*i0D2EGGLpSviil(w8$e_p5`|u)_x*#HfnMky2(?lrzu7i?M3)zA6Z3o~6%VM0L zWDEkgm*lP5JJ;aN7P>87t3zTBo-#I`XnnnTbOjWU@+5ia{Dvs+#FYT|ftXEpRc>Mi zmpB9T4uu6!fUbD@u%9Y>0YO&$7tSg*aC`2i@sEbZsoe@u!os5$Rzk(v7*2y02;&vn zE@>`lGm<1V%1&Bcrk?B&S}^Cy*BXFd?V+IhEF#pwAf6V6tmm6Iq-2Zr(jY}a$ zgn-OcAFnTLbHQ@+2_}x`ig^DZ&8_0+S)31>X{Y6+yH^;o)X5o$NLQSRyaEQcuyRt^ z$@zrJW83k2#!C)>;6iac$JP)jFEWykO;mGF2%A>`__v;QUTwNts+HoMm!Vcxza%w& zKF=JUHDy*L*Nu~VB@snfNviQS&O((>V5uzo_Ay!hL^oVY}!vz zx*rODCgLL{nwyv$LeY?s+4GV4_X9?64TP)=D2q8U9wLW&8wsr-QK5_ieG6STg#DG_R{{2E%*&>8q_mvf!me zx;DGhWZ9LL41HMf|oOFRo7xlASICda%M7@^CG@`z{+lO-eWgNAkkbRdbK}@ zQU0Zz7#jjP0mNA(of+TWE7b7l6keM;izqu!(@mV=b~daou+x6|7}7He0y_LQ6zuga zmgg~PY{EH&f2D>WUF<2fq)FnCNb@Q@l#Ggn^pJ=Oo8?1u*YTol3wX1;`hhdNT0&%% zMPkthEGN$%LN8Sou#0BqPC$C)&;no9x)Nxa5;v=ItWY9=3YfgEK3+TiW_Ev&d`V$M zWOl;}LiEQ#23#8&EqQbEYcX7J(1qJ6k}}j>FG)_tXu#^alfCpqC>l<5itaP@^Gx5u z^hfpt0AA0Ubh35Dz@k=*N#1T`(jpg)#R1EHClk&?^eMZ@h?BZ60|_1X5I=A3?%Ls^V8wDah8S}ENg4|bH;xLt7H&*VD4xYfkg zT||1w<8(^H9m-VEj2Bg>QhQs*3U6{0aH0qc$regOX*wk;W(8LaN%SPpPiyidtFf&# zj1OJ3UOq98n8Y}GTjZzg$}aU@in+)8gqqy5X!U`77yn8Z=F={4G?h+`T01qBugLn0 z1ByEPBG0@ma!LDvZCt@n40!x*9+ZD-tr(+Sel?@LSM|gDfVv3?WlQb2k^>B4R$s6~ zyN$6bC8$>)Tq>wN^zsbZBy=cyph>=vHd(X#i{(iE$2SwF(}noaP_ExWKHS=yAgdBL ztRP=W>7wo3jFL^@ONM4!O+OBFOVo(dSQ4CswA_wgU9rQQHtMbL6yQg#mO z_E=@gl1rtv{TJ;BXvMeu7%K*uBg8yXBLN!kDkw~&|I~olLPY7YupIZk<3Da<%aIS* z$z|e_!zxs5l&@p?E}7^W?2n{z5>KPx*(IM-3J2>aTb^VOq+cX}{Csw>6Orf=>bn$4 z?gWRvT#`j|rQ*Dj24^AydO0*QV6Wxv)F2`7rR4p}VfG;o(k>elxW^Q$b?8iREO>CZ%*|k+! zqiOk20&2N&wP=@Ut_aMj;>lP~+9IGpDWyuMWND|R=A*vT}qwAMWp#qZ;rkGVN+#};fD_$1{-~@8jRSJFY^)1J& zp@4g`dMp#mk0}K^f^$tjh({EBy;B%+a^~V5yGp z@X4h5!G?lyvwmz_Hm>F#eVS_SGpzdfXdE*^0oihm3e>#v`8W?9?)Bjt3UQiGM;+Ml ztFiAKQRl_{3MBFR5vIpCSx>H>FTaP$j$4=Kfz9cj9DXm_y#Oij)^lFu^f!+C`B&pA z5o1c^4!3Yo#AU=s{lkA`{K?>T(fNGV7uH!!ob%@RG3U!49DA6-#^=j2wVW)NsXrGV zcjM6P(xTc(PBP?Q<~V@~w-p(T6&+U$uWp-}!Bc1Ld^o~h21boXg# z>IWYY>&LBe{>wsGR~PW7YGYOh9(A{GC48_Z98JCi2>I`7AHpy zXAyBrm=Br9yP1=G+S*(k+IqcL;&E7R0pe@KO@TWltWY#)ga?%J`chB~adt{Tr&Kv1 z@;Q4_MT9rAd7!u`J%g3oG81ei_^e|bPYlCDaWIQU#uG0hLE7OZ##WsmZ*>r*Gzo~I}E8t(Gr{h&(B$MVpj8-nQE6k~-OE^EJttI7UKY z?4U^eOpLy?x@xPnt>FuQKlvh9yRCK6w)U^oQF$Q31Jx`p(Kp_Ti*G8mebk#dITjMbwoa!uq_}jc z=Em1|)#FEPNG##%I@Pg(Obm0}dU{SI^Hs=;APzI!)T+1eMoYy()$Ym(j(E80_r>10 zlF)6Tn=4O#3K~(Y3i6UqDG;(`K4^yvS_R8iBKQFUgfoR7kOblG2S0!!{1EY96#N&= z9)2kJuc^mh_xsN*{!71q8Jcp$QsuQI&1ZGw*w#>&g#8(WK1cz`AJ+EEe|`L1-gXa0I?$qYtrCqLfHu50WwqmoYPk{YnBm1M1+VM0(Z>VO1mm0+R~aANa2>KS8DRvF z(I01|ufmSNh8u6*Q4=r6Qq_Y*3Wg2NG*=k~TIc1^29zFhhfCP|HKHvLF8;^I5gF_f zM!WPPZsU+r)`Z0SX)?aK{Wff(8!ss0G9JpTeTT~^bw9~ts{4mGVdD`;*GO=CE4}M( z#Qe>`9}(o?Hbr_Y*1N;9Br{#m7=-`&ucq^u;!tbCo11Im39Q9>c3fzJsn1>N+U!?V zZ-F)njw{y5_QNF;3Q9Mi3Ji15dN-%PGQiAdir32zVMF7$-@rk32$baK zqC9T^(*M>EAjQ%RrN%VFiedT?u(QwC-GSL>o`Z4dPzASN?)ZNDw?!?m)#F79%c3p9 z>n}5|{0TzA8k*DkARrnz?>PX?c<<)cwAhm&KSVYdI&w1LNZKM{=NL%Uq4(m5#6=Pp zVh5%=2R&8f)alETx#FKz{WhiQO*6RTo==ES{?>FD4h?a6gCmMxC@Mw3AQU)-qUKIR z@nw3ja=lOA<#L!R2(WSB@ks6>;iYi^IdpUl>#?{MdAsW@Zqsxq0=ukpd^T9{!!~uO znZPMrxXkkyG{dsCZM9?O;De4K_yn`N{B(&#b@?8s#Y#dnRsHfqKpie8&$tNXFFr?p z2)Hgnw?Ql%zeT_$t(<0x5$3PMI_GONd4PbBV&4w={8?4%epATQM^nRyv(o{r1_0Go z{?M|t?*>iqyUPsx+zy|eqnhc4i2JeSKR&gJXpO{0K+&FpqjDeSVlG_f2G`ACE;SbF zj`t0J$|rYcO`hXXHT*CFLVlgbG!45rEqAG^}wwK9P?O6Mp7L*YE7Sa~kaZFORE*;gPqUwrN+#&qR78Tg{^P z8^9Ru?fEQtb8mUaOf+Zt)=f9nH;qcdJ-EIP8r?%Y6qm)=Ughv+%*=I~cckNRK zFBr+Gr)i?r;5u?(#U+b*)Z3!7GcDk&@Ya{iOb2%rY|zCoFLJ^*SBmqa2EIKqFJ^`b zm+j?4RVU`f3d@cvH!Ox7$0irwKnCD=#TVrzhpp3-PTn;a1y{C`9eu}x!5>m{T6NF7 zs;E3G37|KJ)M6=$Q^ux?=tnN*TRu;=0%h!3?dbXR?lVN02x6|fN;+7|k0{sGSFDmk zD6`%m?nu7FRzxwaVq<)*T=Ux}fOh1X4$P64L{URuO|QQVx=n-wuEm>Y!xfgARY~!1 z+i+7%>Sas{MJct0G_*oDJR*Kjf(s@)Ol2Kio@Us?YSa+x#F`J)Mg9n_rX7_}UzHjuuq zA)qj6@QHYck!!2tTo9758zjRWm_WG|*XJRF%SVcVJ)+1|v1u3no(7%K*G=X}uQNP9 z7o@qzg4GVrl7wGoM7@=W-Z{2E<0P-NB$8}v4YBt6F$?+%frntSi<1(4>_+eIg+)x; zy~W%KcrX9LqPk9LDe5oo)I^S&EuwSvL!{V*t%dKU!7V*y%(T3?0S~7k*YbElFBT%t zu}XVU+2Jp*I_6(FF%?g5;``)9*mgv5@JKtNJR^v)G1S00d>x$dX{WcCJLBX1_35u= zml$ULo{vIb_iJ?z5Rq;{D-D-m8oMo=MkZ~he@;JH9p?xq-;f4}4Vobk$K zEmk0k$T54pW3ysYCVZssEe;1A4tx$4YVzit4H&qqDVV^Wy`<80u_8pJgGZ2*a$EY% z<*M)19TE*sM%dk2qQJQdimF}FVwu^>^erLhlF!-VF7aG=hFJXe&^@0e7>wtn#&}qi z16v)>>baGf>LMZ%sVELBpYN18NRzr}Ms9NB5es}8bA5P^cEQItzJ48t+1KC`T%jvt z957hMSHpYVQw;7TfSDG@pS^M-{Rnek7Ps|-hZt5=oQ*?kukYt z$7}$Qxa-g;miUxf(?|~A^DP6SsLIRNGI7WTkQ%vy`!^Yv-;~*MAT-+S)4~5{>ewe`Fum7S_Q$P*6&-qx&HlE z&M|+_<7=)}R(LET3hrLLEt`4q|10dvqoMl$#_x=!u^S{yWwM1fVr&&dA`_a>B5T=q zQhiclsH8$j$=I_mSrbAgBrTRAON1g4g^+#bd0l;(@0LzISpG_eq$@jFJX6VD| zVwxucnjATb?UN2m_q1-6xm2Idis45T6$}eB+Gq<+yMyA#g={8J!4V+NT5M+Dx9S~t zhHAi3|26At-N4}!ZG#KZQNDzonpe>+MlX1!U#M>WbNRNTc>ucNB{X3XLPY4=a=EX&^_ zGpA{&F8^XFLDgkcJ-w$$&;6L8G&k{<7>ZDJ-JOk{II}h^x)b}Gu|lTX_n$s$&t6?O zfHHli%2}=E@|BOvlV8mxi~1sV(p9yT&H7jQT12X9epy`s$R$<@@s`+T zU_Wp!tvAZd>ZaPS~d6 zzRoObp*^I#Yn^moUG`wDGk6S7Zk(}IwGunso8yY@2G!nED%~VqmQ|zrXz<`-iP*yX~obxI-PTw^j6U7>Fr`wPgB4St#3Vnek3&bfSWB_%rO3k337y>QQCZ z!Yimjl+wGlt9e`i;zGv!r+9rWZfbi5*8pjrQxB%5RKV(G+ai5NCpD=c8?~Kjq z{b&Ec9>|dIEk@De457Ogazj_If!FWqY)GEpm~f~li7~9|cGm;}tLn>NHH*p4sy+>}o}A%j9H&7MP-S-1xJ1_4 zOiA%0K7yzu>hd`0ck$G}Vkk0|t^W4^dT1jlY!i_1NRP4b``o;A#w45a7A11M^H$4LNC%F%F^IWtlWPwe+VuV^ z33jt6UB5Mo?pR>!H$EXFP>WZ$eG&g7*}wQ;t;yOfZ*%}=rY#d0SVTWHzP%)?-Ku+C z3jJ`sw=>nnWaG$%N>P&v{2_iwOf#IV@wN#-XGf}|i66`L+%E6DaSfFShxGa^5xJ-; z>_7%YkYnd%KdZGb?UT@@uBxrkE@Ke;_Es9nc;1dEpxI?Mz)m_wW1@=sh40qUnqCR0HGs) z`NZ0|;OYP1&6|zjo9oROYdU@K^>kYTzula1UE6jrZ$XD>x6Jvd2xy*2z+iKCteng8 z9q@=7XSW?rVJz+F(AeBPrc-2BWQb4`WLoAPn6D#qvwKG$=5T*v!c6pMYTS%de+A>^ zz7%eTvVq@jgw#=S#X`{9@JZ7`woeqwPcbw6bu?P4J=QH9voO2wSj+-5Rr^RlGU&B3l}i@0 z{fBG1I-t6F-7?i25F?#EE%SNRyI+U&-kTAz8n*{$r;@S-z;?}~Ytl8@Kq`}md#vUF zwK35&#QO8Qg^PFlMwPt*YTd2>(HZZVVlNCT07#MqH2l=tLzYU#JJw^TVeW6l(%`ye~!{Ghv(C zw%cU;xdLPFO@y`-DDXb0d~+f+y!bdmdSh(eC4G|fGViaWUkIQ_QN4ptjrCyMl;!P) zBwD2f7z7t+BQtL6LUHqi@_;IdcUKQ6N%2BdeE1m_jJMK;^jxYM!Lzck&o9;t>1wA}q3Z-8Oq!9+PL4bz{7kzXACMQ1ppe>b7ED zm6)fQ<>yNWPOz&uSGIyeiO5u`|19_3Kex+yQ?`IcS5^f+ilzRrieu$K+P@`H4-S#wQFl5{vcuCDni@ivqcj1opV%x zBk$`)&c}y*WVm`CK#Vt4ZsmGy{s5uwe?uG8fBjZ5sa^*Hbr6a(i=tILUfa;`0+uGy zGL@701oPEO$8F$zFGeWT+tSy%RP;*${Nla)VqxH=sP8Gj94 zdrw)L35_^$#i4{NYKv5Ux^o%0pVICZ^A%?BhLbm=BHH#ypB^5C6<=ktbI&++@MMQ_ zwl8v_v4?*efrs4l?C07l-yv$Lv>>0)!oQVIMzA3+7w?(=OUo^+6MbNPB>Kf;yvP{N zc~M}&{cmagx5*wq7(%>z#nX>Ij`w<2gQ(@*$dRfHy!s$76X58z?O!+&%Y83bGg-WB zFdrC`V55;sN1vt%s1g4gi1!W~_rarc?cPP_@%%lH?Jj9EWJO%OxMVVU1jYn3TC?7A z`ds6R>U4h8b_jgtKGU3!&YSXqr4{Vwv#m~069z`ySqjI_1+YsZskqXtr&$7)=l)w^ z>>In;OGACU3`DISsah+T_1}(=%q-o2kBL)*g?+1hK7NUTJoF3J@7h#hW4Bvj|AFo3 z>f)%C302&Q+8+?Qv>BZ8^A^gM$8UKWo~^s?Q4_c=`(`(&rOC=zMD4v{-3cG4Hc;k* zGg^2(if%6%z3`498iyZh5~GjZ16-E*pZpI{H*YmtCrwAz*h&4q7S)V}fq4t?cfK~$ zdTx^^(N+@&Ri~5+r|R+F81hzjmvAIyAH*seWi!@I(sKp=;uRGu+C!oJJQ^9lZH$+c zo5mn!vs^k_o+ePe_kW;!ufY}KYP&1XBQZQ!6``R6w}BwtqQWbnFyE!@ZkaRG4Z}g*#C$Fd#&)l^vN=fKM=tWG)f+PB8xt5 zvcl37ge9?v%HzpX>sDuTiRux?3JWr+NAB2jAo~b}IGSl6^T^!h0xR+k!wX!)P=X7x zaQ8-p2b~eDk}vNreZJsm+~fjVv+`gkI7*~4+QYwQroCt6mxW2xVYQZps(GUqU}EpG zxOe*4t+Yz*8`s`7O^@lX_PH@IDq7&Wv}XwGr-NB-IC3gQbY{wVAqXm-OKJ{7jL)&f z^`K0snoK7P_iU}P=p>dDEeHvKXe)FgwuNa za|NnR%|gC(SHj9ITPx!Jfo81+sl}_Krpw{}3oxs3X#P&2Lr?NWy~NX+7T)qNpNhO^ zBY`b`KkT6?yMPb$1-@QEg~wBn2S3AdcxNhHWduS!64g?aiv2oPB$A+sDH~k=vDkD< zz%YlR-UDbc04|-#Z46o~O;&5phsj*E%=b%m1I+KM_uz&i1r9W<*_`*UB@@3`q|Fci z>tEV0%WW#Nd?eiFGhEuccDGe67EQWS%)yL#7%{eoJAVm+ACu~~l*+5)9;OF4qmyAT>p&myVAB3I(J#UJs0E9zH`2W~3 zez-a(pzPB4=85jrhVL-ff(xz+@`pt`vash`*r|YLJ1$fUmMN`OVVI>Y*IPT3;uOM- zVUD6fP!Y95m4}rCZ9aL?5pmf=@fhZ1&`P%8hdJ@!6m9N*Kgw3~3^w2NM_W_dI;7jf zb2qFg_fXpFY?<#`D>bm{4+y)rOPGr;3R(fNLmXxpfDhF?<4N^uKQ%BZJ4wP0QFuS7 z(w@#5yuVbN9SfcP1`wNNPSqfm#4T$E95hY{-XSCU|6znztIWH4z+(8d z`IjV&mVLVR#^M>d7ql2)Kcq?iR?BGV%-W44RVEPQmN6EQd*r!ntPP6gx5---`BgQp z&H0byK-%w`K3GKr za34nlO8an_Q2|Xpv}<$yJ1KGPqc1uRGNxWV0&ZSmpu(qhu=i`w0O(f_ z8B?Zf93fWUk^?Y}499+0^!K~N{G5B;=H6#!nlyyX*w$|>$#b;-F5ly3KWOyjC0H@; zXHNQ{p>sOU80^;GQ$Z`j%-(u9*uLmzAa&7vfTF2$GyCXsy#a8$1rFP?w78y@e@B^;i> zXDW8o+etf_{Qcoi1|O=xn{S*}%}Ne2>t@ANod6;}r1w#Vef@_!QEFE2tY(^4a}a8uSsB=n5{B9}r|ecJjfS4zli$Fj~ix&gEaAVN1d5V`D|lFYBY{Uvt69dTE8@osWY|eS>Mzd2B!W=SvL%TT+kH?T%&1uN{DuffKRF0L zb{@o1x46n-M!Iz8{`u{#z=i~LZk3^$<~t2fdK^Q!6Tk63)oNU?(G2g~4KTk-V~#9! z34S;`7!RW@0nQFDqUAN;0<{%MOF1PnqjVV9Awr~3+$(PvbX9Nh9_X2m3;R9Out|?pa?bYl$~-IJ)-!{J*cxT zwLIKR|9;<*lWx3|+D5@3L!tXOKwf6pY@`Lb+NZsEIb`DGmaB!F2>WQF0Ern|vvbdr zzig~%awvfBnZ`#wjFvka!r`(5Gw0o0R%Nf{6Juq>|bZO}vQ2;`p2IgO8aR1*bHClk&*IMAEU(z2;g`=Tyk z2hyXo4GGAJiD=mb+m}+*jmV_U9~J6TGJS(vugKMNe@RaBMpWz%V5aO?jImhZ z4(M5b1Al~>qu&IBcfaGzs)VUCJfv@>7c>Ew!fsl?p+C%^fB!6WVbL_KAhs$J0qDB{ zIWdmIznuEND+*m}L0;KQ-WGM5Q@I%XWNFWbE2rBP5E0EQ82U}I-b+`#P`?g;%r7rh zeH%5QqlCzDsP?@u)+c@9QBF$`{u-d6A=()8E|)bx1nVM9#qWKuOv|eCFb?LY?zQF1 zeLi&q=BxuhBjf$yeQOf8Upi46Jdrm64ZB!VzjwcAM6|i!F%ACcQM1jll9G=eMD&iC z0NRA{$Fp&q+soytw-P1#^nz{fj`EEK6{qmj1Ea*hl6sWR&5vK&Ql_TB{j#%%`90vc zXn&78)_0&{{`uffXZ>R~kXQT(d^4%nD5ZeGM-M8kgqpG3AX-04n9@;Wcs;M^;^xG@ z=~#4UR4j--S`tQLRaqZ$-DSHT7Urhvm4XIiL7gXx$?aUZ63z^*y62r2wp?1qUf(-7^~x^ zGuRjwc=uLFZB~uSoe*TqbT(+BZR;5-4C>xSzZK?^%PMp7bIy?Q;zwrn5sHh!edOG%C#vlCXB`PJ`_=b!h8PWNg+C6`UrH1}gzjb{{CGS_ z+(3`N5?5m*Ma;2pN~%b$)3N47H%0TPKO?bu!a1%@6nflrse)f}k#>>UDea#^q+D}w zI&4E1u5u1Ox=&=~XH{1lxcPEmyVDRV9hzuu=Gw4Iw;O< z!9rV){QcC%tXFd@pH}(a>?mJg>S4r#RMLkFDrBFj>rmTtFr=7mbSW5L{>I&ExRNLH z<_FrvhI~;xYfjN<9i_;%e(V-PAoA(yS&GV~xv55K@BNFx*sbLJm?09CDtK$J^6c6S)Ioz@r{wdPN~IA9B$;*M+QOu(9&L>ybwhlR)@ z%ni3IL{num7h4KKPYh38y@8}sw~>@3w;tmlXT?N(y9;yfdhC=bEw|Y#)QQJ7!AKo( z?0)FCQqXxF?zXv`N4xmUWz>;#By~*8Z+b}L!H^`k(T{CIWICFgW^ZPJ6pjd^nX<%_ zA98+p;u2bGf+i_O8l~{>P1)ZFVY!#OQ%;;663Yrv*>lJ<=8Dao1&C4##2gSE(%Oc2 zHEw7s9Hf_{YPCrInrxPIFlkSk{mT`0w0Y%rpj2|iIG@#D&XQqQJ;O;-Hv2xLzF<68oK+^Z@LtlUqHEZkkOSw`~}T)R0Brwhe?<@tM-ryH?yI_Hd| z^l>dvY4Ju73l^9bSgLtCFI2!s+k!^4r5x{UL1x#9|2?cZX(-Z#1@%Cs9uw8{l%oV@fr(M7V03B9)7#s%PZLP zFcK)o(SEh>ucL6BBX|o;k#gd&g7TKbvaF|YM{%hnwXgeDnFYgaO*6d*18K4mf;7lJ z#mcg}+}O@a;oi69rKjAMVlG`h9-;8rXBU1-B6fY-=uLvyWK$l26D>_k6$Ple47>dS zQ@u1LNY|BoV?#{#0eu zp^=OXmBC~<=BHPx{=%}qb|(8K?2gz}sDnNM?jhF^?BOF);!|~Lf2|HO8+TL-a%b>* z@B}X_l8Hlin2vReBIA7S1Zh9kqx1K07caIv4CU??!y#lm_IznJYyYMPtWiM~VlOY; zq8*_QTgRK(OtT_|Uzii0{#yu*+Ue!!;4n|&YY zNy~mz_j(XKCaFhd(j2nrXkiZO1aNe|#X;3fF+~ws#BN9!uCcu`Qdh=-M2J4bHt!Jx zFJuE^xpzG!vr?MP2F+l^;~2vBPiOu?TVNEY33l5gf4%;*;nL3is{GWM@kY7sC6@lg z7UcDTD@bt%!EU!!fCs$H2@&xfW?niq*48Dq7uoyoNcf%ys0k>!HIktRjg5bxP~Klb za;vST#ni};oJnxPsQI5bWST{7o40Opkwm;;y-LU7lvXvy17zgb zZM7;vwo5$M3U!XP$_zU|IERD~a6m6Et8563$tvC?`MKt(dJ5XKiA8jP7)iq^XzM3I zg2%onwde?V)NluKct1}40pM)CM~FGny}$+Us2L~Tt`*o)w0;30LHMYWaUhWvfLig&E&I%R%z8DnCj;c*Y-LgG3J7$6I`_R{JvjKLT_z-;+zy zUiv%r?1VHo4vFDr5rfO1;5h$=-0Z_Tgk+zibUr!B9YezI#1KTgck)e(=ul@S^w5q# zh>BFivKViZa?42a?im+9%vov(Z_kmIx^fwIQ=qcOBZM=Lzu#R_SKcD+% z5E6u5vcmW{uzD=`+xgiQO=OYgV3Pp#uY=~1)`~#E{D?ZUFnDz@W*Q@i#G--09m~Uy z?^>1HlBD*V^0&`j8@<~p=Gu+%7&De)>&=jpMaYIC^qhD|GuaYk;mDTWr+l|m^6Iy+ zqBKSqZlLBC8v%LH0$C9;?TC~KdN~1+gQ0qMf@qX&vZ*&2Ln43bb?(+M)~0Fk*cw9V zAXKZmh_v74Tj0)d(xD&x@;-#5^+fV({CDlrqRh>R;rAPqFs0(Z1}lAJtj}8~&GKKZdl7x6 zesqY3DZEUK`_$9up^w4HLuBZ*I1h8Z*YTWm6DwU#JWEL2%u1s0uJ2!nDq>H1N-vrV zrD2Ek!`YRmxF; znqPFwCb9^c8<7fb*3%Y#C3u|q*M?U-TU!GQa)iR))f0JwEDcHgsb3l{0Q-sP6WVVv zlwTykOBO#a`jAtF{|HcZ)K-F$i7UHgf~9G@%rGviK#Vtd*goNv{+ZW)`+yf_5IfK_ z5SKq`;XDrssflCH)tcNv4bxBQ2A{AhKTqId%d_BzsQ^#tO@jyHpOn?LI!;ADb&}we zU=82P-Hv;(AjSfbn0$&|`i!fMAh(g@84_{111qk5GxkC$J9KFk-QZa!rK^4-P-l`S z25^Nih6^!dXGu^!)XmBw%if%SF<>!*(bRU}GG7mQwoCkLk#R}U7Pu&rgI!fOI5(_P zN~_-%x_XKeR!`{FY+RdP-jcpTr;2I9^DIMmMn6HM$E8WwOMutoI@Y#YVRKH-R<#n} z14-ztSmF{%L5HfZ6g1Yk1jS{Fg+2r>97w8&R6F|+Lj5q;or3$s`Jdm5UUd(#`14{f zinCL~-GLpDYHplY@1*rc0rNu64bS-{yo@SmrbO#*OujOilSujJLRnGOhTH8pPV5J* zo3);a?r`F84QUrIrJe8^gv!gMg!+3tUYUXtS3Ml|Ef03uE=)~k@Wra&DlJ4(gN>N| zAqYY!DYknX;R+gCpxgWOrSzyFF5ijiRN;mca^=T~>SwNXe^M%n)|^3ed!FQ{M~dm2 zVO*2SlCvZvWiKB{u|{C(-T5m}v5W@J;g5=bfG9x!u;>Q~{Q!Z4{$bD$0{Zd4|NZ~o pM_Rw*|19zUzVH8REj`54MotGekGp(&-iWSbc*sPrP}eTxzX0jnUvU5c literal 0 HcmV?d00001 diff --git a/resources/linux/debian/control.template b/resources/linux/debian/control.template new file mode 100644 index 0000000000..57f7c2075f --- /dev/null +++ b/resources/linux/debian/control.template @@ -0,0 +1,14 @@ +Package: @@NAME@@ +Version: @@VERSION@@ +Section: devel +Depends: libnotify4, libnss3, gnupg, apt, libxkbfile1, libgconf-2-4, libsecret-1-0 +Priority: optional +Architecture: @@ARCHITECTURE@@ +Maintainer: Microsoft Corporation +Homepage: https://code.visualstudio.com/ +Installed-Size: @@INSTALLEDSIZE@@ +Provides: visual-studio-@@NAME@@ +Conflicts: visual-studio-@@NAME@@ +Replaces: visual-studio-@@NAME@@ +Description: Code editing. Redefined. + Visual Studio Code is a new choice of tool that combines the simplicity of a code editor with what developers need for the core edit-build-debug cycle. See https://code.visualstudio.com/docs/setup/linux for installation instructions and FAQ. diff --git a/resources/linux/debian/postinst.template b/resources/linux/debian/postinst.template new file mode 100644 index 0000000000..8317e9c006 --- /dev/null +++ b/resources/linux/debian/postinst.template @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the Source EULA. See License.txt in the project root for license information. + +# Symlink bin command to /usr/bin +rm -f /usr/bin/@@NAME@@ +ln -s /usr/share/@@NAME@@/bin/@@NAME@@ /usr/bin/@@NAME@@ + +# Register code in the alternatives system +# Priority of 0 should never make code the default editor in auto mode as most +# developers would prefer a terminal editor as the default. +update-alternatives --install /usr/bin/editor editor /usr/bin/@@NAME@@ 0 + +# Install the desktop entry +if hash desktop-file-install 2>/dev/null; then + desktop-file-install /usr/share/applications/@@NAME@@.desktop +fi + +if [ "@@NAME@@" != "code-oss" ]; then + # Remove the legacy bin command if this is the stable build + if [ "@@NAME@@" = "code" ]; then + rm -f /usr/local/bin/code + fi + + # Register apt repository + get_apt_config_value() { + echo $(apt-config dump | grep "$1 " | sed -e "s/$1 \"//" -e "s/\";$//") + } + + APT_DIR=$(get_apt_config_value Dir) + APT_ETC=$APT_DIR$(get_apt_config_value Dir::Etc) + APT_SOURCE_PARTS=$APT_ETC/$(get_apt_config_value Dir::Etc::sourceparts) + CODE_SOURCE_PART=$APT_SOURCE_PARTS/vscode.list + + APT_TRUSTED_PARTS=$APT_ETC/$(get_apt_config_value Dir::Etc::trustedparts) + CODE_TRUSTED_PART=$APT_TRUSTED_PARTS/microsoft.gpg + + # Sourced from https://packages.microsoft.com/keys/microsoft.asc + if [ ! -f $CODE_TRUSTED_PART ]; then + echo "-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.7 (GNU/Linux) + +mQENBFYxWIwBCADAKoZhZlJxGNGWzqV+1OG1xiQeoowKhssGAKvd+buXCGISZJwT +LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV +7aVWsCzUAF+eb7DC9fPuFLEdxmOEYoPjzrQ7cCnSV4JQxAqhU4T6OjbvRazGl3ag +OeizPXmRljMtUUttHQZnRhtlzkmwIrUivbfFPD+fEoHJ1+uIdfOzZX8/oKHKLe2j +H632kvsNzJFlROVvGLYAk2WRcLu+RjjggixhwiB+Mu/A8Tf4V6b+YppS44q8EvVr +M+QvY7LNSOffSO6Slsy9oisGTdfE39nC7pVRABEBAAG0N01pY3Jvc29mdCAoUmVs +ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATUEEwEC +AB8FAlYxWIwCGwMGCwkIBwMCBBUCCAMDFgIBAh4BAheAAAoJEOs+lK2+EinPGpsH +/32vKy29Hg51H9dfFJMx0/a/F+5vKeCeVqimvyTM04C+XENNuSbYZ3eRPHGHFLqe +MNGxsfb7C7ZxEeW7J/vSzRgHxm7ZvESisUYRFq2sgkJ+HFERNrqfci45bdhmrUsy +7SWw9ybxdFOkuQoyKD3tBmiGfONQMlBaOMWdAsic965rvJsd5zYaZZFI1UwTkFXV +KJt3bp3Ngn1vEYXwijGTa+FXz6GLHueJwF0I7ug34DgUkAFvAs8Hacr2DRYxL5RJ +XdNgj4Jd2/g6T9InmWT0hASljur+dJnzNiNCkbn9KbX7J/qK1IbR8y560yRmFsU+ +NdCFTW7wY0Fb1fWJ+/KTsC4= +=J6gs +-----END PGP PUBLIC KEY BLOCK----- +" | gpg --dearmor > microsoft.gpg + mv microsoft.gpg $CODE_TRUSTED_PART + fi + + # Install repository source list + WRITE_SOURCE=0 + if [ ! -f $CODE_SOURCE_PART ]; then + # Write source list if it does not exist + WRITE_SOURCE=1 + elif grep -q "# disabled on upgrade to" /etc/apt/sources.list.d/vscode.list; then + # Write source list if it was disabled by OS upgrade + WRITE_SOURCE=1 + fi + if [ "$WRITE_SOURCE" -eq "1" ]; then + echo "### THIS FILE IS AUTOMATICALLY CONFIGURED ### +# You may comment out this entry, but any other modifications may be lost. +deb [arch=amd64] http://packages.microsoft.com/repos/vscode stable main" > $CODE_SOURCE_PART + fi +fi diff --git a/resources/linux/debian/postrm.template b/resources/linux/debian/postrm.template new file mode 100644 index 0000000000..026784dd70 --- /dev/null +++ b/resources/linux/debian/postrm.template @@ -0,0 +1,6 @@ +#!/bin/bash +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the Source EULA. See License.txt in the project root for license information. + +rm -f /usr/bin/@@NAME@@ \ No newline at end of file diff --git a/resources/linux/debian/prerm.template b/resources/linux/debian/prerm.template new file mode 100644 index 0000000000..7677d782e8 --- /dev/null +++ b/resources/linux/debian/prerm.template @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the Source EULA. See License.txt in the project root for license information. + +# Deregister code from the alternatives system +update-alternatives --remove editor /usr/bin/@@NAME@@ \ No newline at end of file diff --git a/resources/linux/rpm/code.spec.template b/resources/linux/rpm/code.spec.template new file mode 100644 index 0000000000..cf594670da --- /dev/null +++ b/resources/linux/rpm/code.spec.template @@ -0,0 +1,54 @@ +Name: @@NAME@@ +Version: @@VERSION@@ +Release: @@RELEASE@@.el7 +Summary: Code editing. Redefined. +Group: Development/Tools +Vendor: Microsoft Corporation +Packager: Visual Studio Code Team +License: @@LICENSE@@ +URL: https://code.visualstudio.com/ +Icon: @@NAME@@.xpm +Requires: @@DEPENDENCIES@@ +AutoReq: 0 + +%description +Visual Studio Code is a new choice of tool that combines the simplicity of a code editor with what developers need for the core edit-build-debug cycle. See https://code.visualstudio.com/docs/setup/linux for installation instructions and FAQ. + +%install +mkdir -p %{buildroot}/usr/share/@@NAME@@ +mkdir -p %{buildroot}/usr/share/applications +mkdir -p %{buildroot}/usr/share/pixmaps +cp -r usr/share/@@NAME@@/* %{buildroot}/usr/share/@@NAME@@ +cp -r usr/share/applications/@@NAME@@.desktop %{buildroot}/usr/share/applications +cp -r usr/share/pixmaps/@@NAME@@.png %{buildroot}/usr/share/pixmaps + +%post +# Remove the legacy bin command if this is the stable build +if [ "@@NAME@@" = "code" ]; then + rm -f /usr/local/bin/code +fi + +# Symlink bin command to /usr/bin +ln -sf /usr/share/@@NAME@@/bin/@@NAME@@ %{_bindir}/@@NAME@@ + +# Register yum repository +# TODO: #229: Enable once the yum repository is signed +#if [ "@@NAME@@" != "code-oss" ]; then +# if [ -d "/etc/yum.repos.d" ]; then +# REPO_FILE=/etc/yum.repos.d/@@NAME@@.repo +# rm -f $REPO_FILE +# echo -e "[@@NAME@@]\nname=@@NAME_LONG@@\nbaseurl=@@UPDATEURL@@/api/rpm/@@QUALITY@@/@@ARCHITECTURE@@/rpm" > $REPO_FILE +# fi +#fi + +%postun +if [ $1 = 0 ]; then + rm -f /usr/bin/@@NAME@@ +fi + +%files +%defattr(-,root,root) + +/usr/share/@@NAME@@/ +/usr/share/applications/@@NAME@@.desktop +/usr/share/pixmaps/@@NAME@@.png diff --git a/resources/linux/rpm/code.xpm b/resources/linux/rpm/code.xpm new file mode 100644 index 0000000000..f4202c17ff --- /dev/null +++ b/resources/linux/rpm/code.xpm @@ -0,0 +1,1040 @@ +/* XPM */ +static char * code_xpm[] = { +"1024 1024 13 1", +" c None", +". c #FFFFFF", +"+ c #509BCF", +"@ c #A8CDE7", +"# c #7CB4DB", +"$ c #167ABF", +"% c #8BBDDF", +"& c #6DACD7", +"* c #C5DEEF", +"= c #4293CB", +"- c #2582C3", +"; c #338BC7", +"> c #99C5E3", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@......................................................................#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$&%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************=$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************=$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$..................................................................................................................................................................................................................................................................................................................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$>******************************************************************************************************************************************************************************************************************************************************************************************&$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*..........................................................................................................................................................................................................................................................................................%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%......................................................................+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$................", +"................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@......................................................................#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +"................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/resources/linux/rpm/dependencies.json b/resources/linux/rpm/dependencies.json new file mode 100644 index 0000000000..e78bf4f85c --- /dev/null +++ b/resources/linux/rpm/dependencies.json @@ -0,0 +1,147 @@ +{ + "x86_64": [ + "libpthread.so.0()(64bit)", + "libpthread.so.0(GLIBC_2.2.5)(64bit)", + "libpthread.so.0(GLIBC_2.3.2)(64bit)", + "libpthread.so.0(GLIBC_2.3.3)(64bit)", + "libgtk-x11-2.0.so.0()(64bit)", + "libgdk-x11-2.0.so.0()(64bit)", + "libatk-1.0.so.0()(64bit)", + "libgio-2.0.so.0()(64bit)", + "libpangocairo-1.0.so.0()(64bit)", + "libgdk_pixbuf-2.0.so.0()(64bit)", + "libcairo.so.2()(64bit)", + "libpango-1.0.so.0()(64bit)", + "libfreetype.so.6()(64bit)", + "libfontconfig.so.1()(64bit)", + "libgobject-2.0.so.0()(64bit)", + "libdbus-1.so.3()(64bit)", + "libXi.so.6()(64bit)", + "libXcursor.so.1()(64bit)", + "libXdamage.so.1()(64bit)", + "libXrandr.so.2()(64bit)", + "libXcomposite.so.1()(64bit)", + "libXext.so.6()(64bit)", + "libXfixes.so.3()(64bit)", + "libXrender.so.1()(64bit)", + "libX11.so.6()(64bit)", + "libXss.so.1()(64bit)", + "libXtst.so.6()(64bit)", + "libgconf-2.so.4()(64bit)", + "libgmodule-2.0.so.0()(64bit)", + "librt.so.1()(64bit)", + "libglib-2.0.so.0()(64bit)", + "libnss3.so()(64bit)", + "libnssutil3.so()(64bit)", + "libsmime3.so()(64bit)", + "libnspr4.so()(64bit)", + "libasound.so.2()(64bit)", + "libcups.so.2()(64bit)", + "libdl.so.2()(64bit)", + "libexpat.so.1()(64bit)", + "libstdc++.so.6()(64bit)", + "libstdc++.so.6(GLIBCXX_3.4)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.10)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.11)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.14)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.15)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.9)(64bit)", + "libm.so.6()(64bit)", + "libm.so.6(GLIBC_2.2.5)(64bit)", + "libgcc_s.so.1()(64bit)", + "libgcc_s.so.1(GCC_3.0)(64bit)", + "libgcc_s.so.1(GCC_4.0.0)(64bit)", + "libc.so.6()(64bit)", + "libc.so.6(GLIBC_2.11)(64bit)", + "libc.so.6(GLIBC_2.2.5)(64bit)", + "libc.so.6(GLIBC_2.3)(64bit)", + "libc.so.6(GLIBC_2.3.2)(64bit)", + "libc.so.6(GLIBC_2.3.4)(64bit)", + "libc.so.6(GLIBC_2.4)(64bit)", + "libc.so.6(GLIBC_2.6)(64bit)", + "libc.so.6(GLIBC_2.7)(64bit)", + "libc.so.6(GLIBC_2.9)(64bit)", + "libxcb.so.1()(64bit)", + "libxkbfile.so.1()(64bit)", + "libsecret-1.so.0()(64bit)" + ], + "i386": [ + "ld-linux.so.2", + "ld-linux.so.2(GLIBC_2.1)", + "libX11-xcb.so.1", + "libX11.so.6", + "libXcomposite.so.1", + "libXcursor.so.1", + "libXdamage.so.1", + "libXext.so.6", + "libXfixes.so.3", + "libXi.so.6", + "libXrandr.so.2", + "libXrender.so.1", + "libXss.so.1", + "libXtst.so.6", + "libasound.so.2", + "libatk-1.0.so.0", + "libc.so.6", + "libc.so.6(GLIBC_2.0)", + "libc.so.6(GLIBC_2.1)", + "libc.so.6(GLIBC_2.1.3)", + "libc.so.6(GLIBC_2.11)", + "libc.so.6(GLIBC_2.2)", + "libc.so.6(GLIBC_2.2.3)", + "libc.so.6(GLIBC_2.3)", + "libc.so.6(GLIBC_2.3.2)", + "libc.so.6(GLIBC_2.3.4)", + "libc.so.6(GLIBC_2.4)", + "libc.so.6(GLIBC_2.6)", + "libc.so.6(GLIBC_2.7)", + "libcairo.so.2", + "libcups.so.2", + "libdbus-1.so.3", + "libdl.so.2", + "libdl.so.2(GLIBC_2.0)", + "libdl.so.2(GLIBC_2.1)", + "libexpat.so.1", + "libfontconfig.so.1", + "libfreetype.so.6", + "libgcc_s.so.1", + "libgcc_s.so.1(GCC_4.0.0)", + "libgcc_s.so.1(GLIBC_2.0)", + "libgconf-2.so.4", + "libgdk-x11-2.0.so.0", + "libgdk_pixbuf-2.0.so.0", + "libgio-2.0.so.0", + "libglib-2.0.so.0", + "libgmodule-2.0.so.0", + "libgobject-2.0.so.0", + "libgtk-x11-2.0.so.0", + "libm.so.6", + "libm.so.6(GLIBC_2.0)", + "libm.so.6(GLIBC_2.1)", + "libnspr4.so", + "libnss3.so", + "libnssutil3.so", + "libpango-1.0.so.0", + "libpangocairo-1.0.so.0", + "libpthread.so.0", + "libpthread.so.0(GLIBC_2.0)", + "libpthread.so.0(GLIBC_2.1)", + "libpthread.so.0(GLIBC_2.2)", + "libpthread.so.0(GLIBC_2.2.3)", + "libpthread.so.0(GLIBC_2.3.2)", + "libpthread.so.0(GLIBC_2.3.3)", + "librt.so.1", + "librt.so.1(GLIBC_2.2)", + "libsmime3.so", + "libstdc++.so.6", + "libstdc++.so.6(GLIBCXX_3.4)", + "libstdc++.so.6(GLIBCXX_3.4.10)", + "libstdc++.so.6(GLIBCXX_3.4.11)", + "libstdc++.so.6(GLIBCXX_3.4.14)", + "libstdc++.so.6(GLIBCXX_3.4.15)", + "libstdc++.so.6(GLIBCXX_3.4.9)", + "libxcb.so.1", + "libxkbfile.so.1", + "libsecret-1.so.0" + ] +} \ No newline at end of file diff --git a/resources/win32/bin/code.cmd b/resources/win32/bin/code.cmd new file mode 100644 index 0000000000..4f31b47469 --- /dev/null +++ b/resources/win32/bin/code.cmd @@ -0,0 +1,6 @@ +@echo off +setlocal +set VSCODE_DEV= +set ELECTRON_RUN_AS_NODE=1 +call "%~dp0..\@@NAME@@.exe" "%~dp0..\resources\app\out\cli.js" %* +endlocal \ No newline at end of file diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh new file mode 100644 index 0000000000..f6338a227d --- /dev/null +++ b/resources/win32/bin/code.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the Source EULA. See License.txt in the project root for license information. + +NAME="@@NAME@@" +VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")" +ELECTRON="$VSCODE_PATH/$NAME.exe" +if grep -q Microsoft /proc/version; then + # If running under WSL don't pass cli.js to Electron as environment vars + # cannot be transferred from WSL to Windows + # See: https://github.com/Microsoft/BashOnWindows/issues/1363 + # https://github.com/Microsoft/BashOnWindows/issues/1494 + "$ELECTRON" "$@" + exit $? +fi +if [ "$(expr substr $(uname -s) 1 9)" == "CYGWIN_NT" ]; then + CLI=$(cygpath -m "$VSCODE_PATH/resources/app/out/cli.js") +else + CLI="$VSCODE_PATH/resources/app/out/cli.js" +fi +ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" +exit $? diff --git a/resources/win32/code.ico b/resources/win32/code.ico new file mode 100644 index 0000000000000000000000000000000000000000..a188831b19fbc9b3a4cb7dd8d8684dcc6fc7645b GIT binary patch literal 83328 zcmeHQ2|!HW_kUAqp|qfsXjBx^faqPgi}?_4Vi|4&=Ck>X!Oo8#xXGP4~D(HM(y#;yX@@S3G{mzoFx9-u3Yq zK4g(xnoH%k4JB@mv}eVs)#&m~e*C4_-)E@?yZ>S)oNU7TXsrlNHZhX6){c&KUj7|7 zwS1HsviyVVw@mb&G#P^p(k;(vkGp?>3%@$bQEK^db3tCcvR%&N-!ji8uHPPAkuydd zQCn;FwWIvX-3#X?itBY{O&puA)NX8<65%N2oPNm>Ij~<3^fB{{UW>Lg=YB+%BO}*h zL}0mmSJ!Ku=uJ7J0x&#d;y;_z$F6j5m>69_JS_Rx&su7ddfC6n7V4`KH*Ca-Lm9kmIe7OjHw*4ZjwtyJl!O z&Pr87%MW=se*$+?>XoE)5s{Y4jyIYgnm(=`X;N>sVS}E6NaPf8WKUMxv9d#r<~3Yi z{yNOAQeTm6`PFX<59T3X0?rA4YIjz9U>aS4T$s^T*`c@W>&dw7n{C!7lRJod(xiO6xm>m!vCHo0yz;ajcJRO#=NkHPWC zj*ku5B+oHZpL_qZK)JCzZ#dL%aEgs9#$@Fyvdf?6B;dBqE7*HNszTuezI7@lKSIwq zc14Z4>Kgx@c>-@!PQ~*3)<#7aMLF_VEo2I_H&d5S+r-x(A6;Ig#I7#>&G+E9(_bVm z*Z~=1fsB}y7zd3kUy|4f{K1XmzwLBTDc5?i3y~Lk7&bBhQ^l^FS5VsU?n7J5ejYJK)7T?+4m6G{Lx>J3xEi!;|eR70C8 z5$_Fm?^xX!?LIdEvqeq%WYKapNzwODh`tGe^8-h`5W=%Y)mQB!m}T&b@fVn@2bjB5 za|!KCml-!dQ4FVzmr4LO=FNYi!9f~mb7NvGfas(6kdrxk6DNjx`TLA=3(^0! zLhWcCrolY|nQj)4e$GFoSmafw~blFX|Q7u*-RBh-Oeb7Wk8)|t79r(a{)rnBV;;YWgzbp{%3 z&js@@)Xz(6iF=g@SNent>Dh;czyL57NJ#75^3ag2a&PpaE4)T`upjgg<#KX-ba>HZ zaU^6~l0j&G(YuP#N9Hu-ZVm;K;v*2@!{J@0>lz0|2`?akRM-sCx0X&h@#-bI0s`(z7_j1lP#D zxIf2@A2Z*Cuc17lF{)*z;+z()Wz+EIn5qoxs$Y7Z44Q@KJj~zB6Z-r?g1FuT!WGl% zk7KTWWQo&xT6{>7`4Nvd2e)~}{5dW5{(F!zgSzUXsP#wy#+<-iwAZpYD}t2uAVIP& zdr#Bk(_H3`Hh9pTEXSgK3ZaWVRAqEWhV4tdc1`LG_Y}+{-gAYl0 zBOEx^D>m)V(&LNNtm5HPv6Z~O%eS907p&acP&wl(k0Wp7Xb#-zAQq`L38eESF;Ocn zS?*NW#ioJ0tW-=?9PPz7v34>-yliE>bP1E6$`mU{r67@$jvNX%uNCeoYez;48?5%& z;&^J7+EXR@{a=ez!yPO;t5%qQSo@izD6u6lcVEGE z4ZE_}xe@_6w~BJZpI|P8@Z$^czOFMZJ9MA8zT;xsyJPu+E8+&{D0h8&!h^6+BZ!3SBP_f|w=L03w%Xye3F!7YK9d)w}$yvQfA6;$fhZ*B`)eSlTj%=pSkO zOO0#AMH4mWh41IFn~6H_al90X+*?(bn5pX_g781go8kWj?%?LL;DWQrdfgbj+^GFa zCUoqy(iJ+)7rLw&Glv=b(iE)R3u04WXCXH;=h=51)_w3&QuOHpOyRX%vMjtF*^>jt zN3!E;OHy;PYhz7=S(Vmk?^)=-&>Ew5Q$z2%K1az2tiqO?Uq5XS3yMe9_=~Jxz~km<{kDYB?8s&d9vYfsy5Si@bXmKCxnp zb}2y?5vcQSJtGR*{ZwaZn`E%d=b-+P63e!8yx~3_pf4C2*GqHCD&$ z?aH<{w&>J!S&0jHSALYFcl~HQaFos>R&R8@z$?a*g0HSGjqoVX5kTG`I#NFvIIY@+-Lt%ELO!q=VRsR9cQnn zPdHY>b424DCTA<|{7K7AdseZnK+!9;cT~*~ zo4oUy$@*;LpPo%H5C|7zT3~&xL`x?4_Re{d z&;7A!%R}+W7r6;D3w7(XSn|tnCAHUoC_H;!V*?99+>n%Ay*<_9!CdAG4x=ty#U3x_ zWOia9xFj~WYNy+fw!X2yY=N|Nxwo&;4|CEFnJwJWa%NxJt*-*(g1=n{OP5(y0HU%ZaR?))Pw zl~;$Vwx-tUXMH`9Zipbn9cf?0=B+sPxw^grP= zwOF~!wYW3~tUkfM{&~^#n3FDhE*wDu#)aJ9UD+u%GE2p%bb`OhB_6grUCyOxWnV8% z-+X(MU`9uQi1lkH)^wi7qZ@wE_Wi40^@SCqh5R)8%-DWxR3-&0_y6n{aYJTbELzk5<<{iT<)Kt?M zfp`gdZjW`5u1{hgvon3e)Ll*4yUvQt-0L3Cw8$o28bM?etHyo`Z5nvdYfc zDU$T9As}XU+2aM0v$3ynV=!L!d+*!dGIgF1%VQV%DpR|2bMgt*ub#D+**d4}74yGM za+Oy~T~Tw3z2d@EaIj5y$6R6rtly1uzOquT2#-zrHs$(e2~u0u3fELe>@!~xb#3`Q zq7L{E>m}QijPKjmt9q&%mmHpOx&C6J+nn1G-+Y+yi|Z;w8dp82IMl^o-<&PYygIz; zE4yRPM&zw-LfD;JnOa#@lpm+r4zH zPxmj#sAw$;V_vd&bZzU`nFZ~Y21Kr_p(Z?fOExg^Je(L}cbI40N|i<09#Zys3j|5c zDGNSZmMaOh%=eKVfnBwcy*6;o#@)F!w|2I5CC4ZPaH)(BI$FNpK`gv&k9!Pgfs@K8 z?%Z8b*L8P|d@K70!$!r_Iju9JGoJ_omK~oiPPkr~oka9$9vPiEIo@kBi;0w>$K3PQ z4O0tdUc540)qO%l{pFeW=8Wf>->{_I(dlc&9;b`-ilFhF3|l512`vBluO)mAp$6{p z?g_SmF8IjDOC@J(FmXAXDIFY#zkbU@n|SQ7Tj{Kkn;)~uEp(bupU(+JeBM>)lu)ne zz-@HVao&OFk2o;nSKy@X%v;%FQ@>lrl1GnyU3E#%oTFL->B5a?OyHQwWyjSY|1?`(R&3SigUcuHKp~QtoBME9D zf@d+vDUQX$n;%!c?>rlRu6z%VfVY~H%b0W7pAQOGr8cxqSfFwXL(F<)UUW<>o{0ky z+vGXlSlyikH$s7CiRLcs|7a(I#;C7xe|_YP2wO6i&m(t7~;Z ztfMfCsT!vYV&qy?;HCO4GH)l(0baRe-bW(*A}1|cwUMVcj(K8z4xS%D5ECL+c!`HQF5q4G zZpOiJh_B4N7k77lx?gPHuJEjG!skP)whE3}IG!sL40)O>R{T?*D8IYx^n+vZUgD1@ zuW2i6NmISu_4LdlGqu;EfR8As;dQNAR~~VD-?7ixvH$^!67P7dC80aoL9ciPd*5{cGGx_hD5l8 zwd_LihdfMvkJ`?R1YFeYIM?uq2RF_g2etWb*|FB-O`;C>j_fNYj*7<3TkDfA6cwpj ze=|@v%2H;eP4pH+=d|i;rDJ?K4HDXkAz;`tEj@XXmfz#F4?4sX}YJTrM?*X;pY9O%G)Xi>JBrIs66A!z=~BM zJzrPX1}qJX=}xQK(k<8}O%YURdb1z2i zvhYrq^tvp$Rq?woge%N^b(+6wEfS#XI#Hps{iq+>X%_Z@3rm^3R;NAnVK3`#Ge}XD8weI^*%^&wV%_#`pbKu^*wioM4eIlwY zkm(pDCj8bL(>{3KF_e@tDb6{p!KBm`%PwgZZxmRGl{9iX*+%iP&z#>YQ$={EjqSyE=nle3W+<- zEZCSn;aZ_L>xL(#k}2mGU`$r#NGxxyQ+PT(Fxy`~Xhw0cfx`N`oAeg8Ni^wvDtAu^ zoy3xH?Df$EFB#o~fXLMnvjZDj*uAy|Yo4e+Ha(O6mIG@oI~S@e^rVFRtq_3mVf)ad zS>ASqQ$;!EE$FzkW;7EUdi7>|`Sm>J#~C(Cl)Tc3eHx%TF4aWv z;1aE>l+|9_S#XCNcl&1sz6e?a@Qi+&Q!$N{^H?ZE?y3N3*89+#&LJX&kCopi>8p+9 zza`Udro8Rma%?SOe)htWR}bE;$zdv;hdHV1sy)BetP3AtwkVJ2QxIcSuf*nheIKIm zlvg)v;wfF1G1nubdCkn_nBE9|n2j9v(qMY-TdS5*qpf`Uz^!RD8I`Lpr^rk=F5a53 zaK{wy_WC=Q@3gNNIf`$C#X<+U&!zS2`Qw$^t<&0c*S->RX}R~%$}xJ%*)5Seu`jGV zo*qsJ4fSD7%N4!w`Z}}D*X&teKFqxp!hYo0c#hW*b&@O>mL7QO{h_KZ1TQ@c2?r}^ z-P=5qz=@u3a!e%FbLnsvzbyw#nTNQRdubErt1|*EF6lC*Fr1mJIX?$8L3ztfvvLK_ z_WVmzSlr^6GrKM#Pl8nwz<}IdFw346R<>JfW6k+@mmv7}5j!J11aS9nkKiu#8q?4Jg$awZJjA}XWv(eIipHn>G*=? zRlA$bUfiwqms)&u z$%7x5{0@Vq7?|oRiPMqV?JasUs=&1Hwz%;sxcSt)J2^Ma0xx7YTnfL*9UC=$>EXqN zyX@3o`Gw*m?O(!gu6ncAgv)nm4obQ4oojB5Vq+`ybu>K4DKFEl ztyiK4Ho!Q2SMBpUxh6#_nuJexEN?^yd49AQ2h5Xb_ar8{>X5ML)6rlDl9Xvn@@c=W zucj$o<_#KbF_7~vTosKN#$sGPevVPrUhd6j{5A}q&P&Rv;C`N}vi|OqXo>1H**$BY z%nzB%V<*zt#ktFPcWQziiN9sB^$DZcv+UQ@?*U22U08dnSU+RyOm_ZaHe#GjJ&&_+}v_`^2{@Uiq$E-QU z2L)y)M(>_^X&U~CxxRdm;~BqZp@X9(N5rw>xF@+>oXDMeU15gSzVpuumdr>XnD1so zG`<*L9sl^iyR!(mLHAk3Q&uxMJn1M~xot)ITm_J(kKLyz6>n4bCFbR|5zF&r%=bhd zIj*Tu#)0^y%Iku8dKTPsX)HmD_u0s%x9M}OZZ&O}-+R01^0=tWEcYMfuRHc`)|a(l z6=F@$cYURty3IC_sQ^4DInZ7D_10}K>^C)|kw{pwRUCl4yP#|&eyz3awZ>BqKU=sz z;@>j^Ae+9}h%{QKM#i_|>3m--r(B|T!cCllseG$$){aQw#}@CR5v10>(RY8#`}RF$ z8v{HXkQRm0k=8-_23gw#tgo!spK(U=w&+Hl=U3pEp&5JVt+BITGg_9@gbiPlv(C^X zqPi|p$>PF_IX-G?mNI5$d|g+}#K)-4D&~GYW_cuQ0^w^*{IQQL4K~QQEs;h~9kd8e z0b_6Sh;38QPsy-yW(rfkJ-gzvu3(1s^H7hx^3S!%i;z70Irh7`*$!Aq@v?jG_vpXj zE+xEPRpN$qL~S42)l_BqtwYuChDZw5DEvyK;0v64&n9Ng2S42Aj;0nEOIav2Xa7DtvAckV`tM!311^ z@wOrvVBL9rhsy~(hFERM73w8?vrF+aal%Id3t(IKz^<1WoxK9Av&Kd0GV78j8OT2{ z_KjfyUVo+1gq;Q}#y@SkaV>b9EjYR?I_*5QbiT_ZUDy2SUH6`J8rbH0cLEOX#dB5b z++_Ka>e&RFkIxj^xQg@Weu;|xz=vLZ(OZtFa^S8}?S{oHdq(2V`)-d|a44I(rhct@ zW#&#z6lI9!i->vyV6B5As8IwezcmpLc5Ys^-g-K(VA&Ulw6NvDW_ef!~!_%myC zoaUTow=?SSzWAi8(I;9$C^l5-<1+cS=d9qAPx{n6ebksvm%L3P+kxms_#ZrNTYi<2DPnx3)U~HaUSsdI z*yJnBdr#$y*QpA(awNO$m6;^I_Or9Y`Y6I>g{-YA^`Eb=-$Gs&H%VXr z7Wb56+cjs}5N2F^qCI*)A~{_*!{z)3T~UAh!unYyd3+DR33;q*s3$n@y-991FY-t$ zQ1NpBo2}**aCUG?Oy$vDo`3#cPV|Sysz}9x532jDt`uy4_3mY+rl9a=5#=soZOubc ziv81_mANOp_v)6q&JV&MIEx3(%u_D{o72~`XJM~w)4;Pc@*IMuQrbRE5; zp+?Z=b7A!U0D)&Y#Va31hMEABKb%>a!66nCC~>-?(yj57=Mu@sECDaq79ZMMq*KxH z==lwGtdzcNl_9H%;HQGRC2rwkbetTSF*6_PQi}z6J&|cJ9g(=@ixwD z!JM3pVA~!f$KNy6;2l5yi;7~`RANnN^`{MQyPoE7nv7qWTfKQ#^Nrli;!KZ&w!Aky zy3ZwH*@KfObZQ#?wR3%$n{7@P?=Gu2SHbOhGP`DuWyeNUFvG4&K6_F}ka=B~0a{*r zEA?%}Q|b1%M@y?82KX;e8NDC7q7ekziChQfT-_TeT@jtW$Gp?R)J7-t3-BK`r(M;w zHeC(e{1WGt?GODE-Ld=5d}x~3+2!-)a7;xTZ5rv<{K4@hNV%zNmY!B9EwaqbuGo<#Z>o-hyW1*oW(<_(nPx zNREl(=LIl^msG#vJsoYiLCM+W&A1c7Y4RCiiu=;-?ax1J`vyZM%T>=@^v3UuPZ&Qh z6igxD??;NHu32R2HX;02>k_9LU-g93co&yg~IqP@i z%^A6aXteU&Id4C zwr4;C>NhvBt51KaFC!^sn`!y!J~*VWrcv@NL)x^T8FK{GSl>wCAyND z2hvBFuw6)EPPCr*NIf8gU)c3a4A`gqJ2v>ezse(Kd^alPe#canH=LRoA~J&aRv0^T zZx<s$1EONyU*|45{ zzT%9ET6t5kqgyx%RE;gte>1A=;cErwQ>N8LY>HS3)?=afafh^#OSiSJInA6gnWwT!L92X+FQ`kI)bu7Cwf@i@NhZM@W zD*HsFh<9Q`UD3ge!;1`Nh=2U5xaaZe^Det?7}sPXYh_rAqW6#0-F=OZd9%Yw>1#*9 zQggXbTq;Q~>*+Rr`;(=Xx7XUlF656u&LYPir24cU+-$MlK#b`;8{hU3kttle!Dc;1 zhe_d#`PgwG?wL)Sl+Teww2U}#qL$l~bG9gOFC z?Tog4G2Bh&bI|j&(b{V}thRbYGTV0Ov&0s82rXpta~Cwwn!|*@zfFG|-vK+s%Udn^ zXr|?~l{Z>~)DT6CPpN+O(S1id%g*L|gq%Fto|e;d0yme&7|xVu#S135=4Yt0Wsz-O8IDz%tJChn5p8V?IF-`~iCmk4-k?{|Fi z4^Q4ua76r2(NRf4@v-UIWhZ3oVR{=pzw6x@B@%VYJv52*zz(66UVw5!cfy0*79<=< z*xxZOYMwEx5P_%_$o_~$$fqkykf+r3z0u%)=}W2z>FQD>HF_nY+gtlTu|CfWt5o1U zC8Weps-*nn>?Ue#8KHZ?pO#IaWepT|u^L>1cT#`N2jT`X3&$bN5I;1*^1etAse#0z z_re_75ws_0SAFT{SGR?88{+-ultMcr`1aPOpZKG8K$*k!0%n~;t{1d?LV7j>_ZCLw>QB30fjz_@`W2{r zFcwT}4fqZC6H%Ec^Pcu!xaRkUJ*uOTKwx!6*EJ!rr5av1aPtzYVR0K5nFJ0VYc zefLy8RDM1srtliBG1PLyhqLc&D)Z^%lhH+eKoQ8^qaU^Papg%y(?-%1AMlBPh`?5Eq7{IyP-!+(823q!q z^64q>>v^H4a~L%)1EC=w>M=qZ?mg7~{$l)b)N%m~$sNA*md_jTUU;!CTnKkE>*Us`?9 zDx=i*rvCB22zju_mn)*Yx4PIIpPa# zwm&>5aT=;?c+TrhHcCI_7vT@z7lQAGV0)7d)I7D}Gz^n#Y(e^IU!Zp$8|t+m)e${E5UuZTj?UAe$|>W$sEp7bpjQs%_>K6Z zc7R7^MRR&*f?tI{J^rYC{mq98ci1m=ycd-b&U0Gp0vZqgoA5`+G}QUuh(E1(FS^gu z^9x`+_z%F}gc?tJ`tPsbP2GQQG(G;k-Pc?FP|kl^@m@+lpVqkmt_!~j{~pf+^z{F$ z)_;0uz~1oftsbt?j3m@IW7J+~#e1pG`LyB!&@Xs(U_t@#!Qi`@zal~3-=sG^(ewYI zU)bBdgHcZ_76s1)gV8r5eV}_YqxjQ`_fp6CdW#D(BG<2K1J-$(&jjGS9BLWp#U2LZ z{MQ%R8C?VD;YuCnhqgq^PWmF#uV@SPPjCJ2e(uw+*c+p=Ujy+y>a#zj;hHd1&mCyV zM(bHEt@VK3x=yPtt$l(P@83}d*L-^WE+rn&Hil9@>gW8Fyx}!D2F$>FMgx_R*0Wk# zn9}p>Xtjl9wEV<@$~utyA@05DoDzefTm$IMeMk*9ATq?iVF2^*HIp`Y82GecjjR?$kVK(Evw=-!Dbw>8npj4-;5U zYj39Z;i&KLDXv4^mLC0!&woT(x`%p1b;@WC=PIqf`cnpd4q85cZ?g3CjwmfT=wSfM zY3J^KXrGQ9S~F;nWD z)-|oYnKFh$c~4LAUdnd8UH=CB!>I9xdZe6da9>4ZJ)o|~cQU&7;C`J6AP;)jLV3_L zAG8tbcT~_i>ap}h_l8AJFa(9zORGc*O#>dy@yu*oA9UkE*Q10De2zu zg7`tqU^|En!0tcqg+tt+9l?1^jWsphjs1V_rx@9z7Ov%ei5XF1Oz#?=K>+qY z`5u&f2YO8p1M2dgVtRdv_fq#ui~f!H|Hlsmm}5cm0|`DGCiJFjM!v&I4{KWG{qg5| zlaacfmi}p#^@MRxwn3|}-pYP6{qm#BmU4Yr-k7_%7!AQ*kAnVt?{({ zKFEXC_d0vS9j*V(_@j7J=RM+2@A)9v_Xm3eJPX1$)CoNoKp&5mtw6h?#%wTYROgK3 zw0t~Tvh?*`0czfVBmO<$$IzLL-Z`SL@myMX^i)Qz`<`febDg?>xL5SlPj7X98~*g3 z7xg6`2Yq~6^B>w3wSEVa?hOm-dRj3aYI)Fe0rkC%(tjKN^qv*;CH@C>NG}cqZG&3J zgGDon3EGZUjF(atxJRIE`Ee_#OA6I`2zPD;^AG@9o_XMtKb5|LZJ)QA{Y?(TfMudcT+QK1Q#H@&B{&hd5Hl z{1|;Vlo1;k#{bX9pE@4Q=)2*=`2RX=8R5hT9<%K2E z8V??5yqB8iF#dysEhD{9>%K2E8V??5yqB8iF#e1nH#lvmb>9~np7r6Gf1vSRYM#UR z4-U4B^g^xszS4tr=A-00j6WmD4Ne=Yft`A#d?FVWL!}tUI;T{gx z^`7+oC)dGs0qzt1*dzXF{2}hpU;C4>?W=5XP3VIi{FC@YU+#YmcWOI<>j4Ajh<^%y zxWBvJb^qh%`=Te|?ON#p^~N{x*_#sf68f7QFPp~!KM~(UGC}?aKeYULIOqRVzfLhb zrpw^AXNh&p&X7If#y|Z%!T&*shIB~O_``kvPdUR;uG4UiL(GR%8UJACl=z>Iv;O1O zdx-na$gO?;ZJvJ+%6<(Q#UHNul=JO(U&D2Nc)s@w(mve;@rS8v9ubFtxtgVu`$H|Pm1T@;~if1HUyv(9*}H7iUW!C52OY##xRg@z3HZ} z^)Pn-yVrV@zh~_B^q%AgQ;(S8^cAFk5#!#*4A+7I*}$*Bwc9))5L^P(h^e-&Rw z#@+9AV5l+fq5lR@hoyx-HTJ`6KSO66KU%u%t*i%phWcE9rzIP}`Y7eRhu6UOV`L5t zx9P|E5a9pfH`o9X@ce1dBBt+JPK`aJOOvgJv1KgIL7+9KdMX1r*!~9mO{irekx0zM zOy~Es@GrSzF^oTB_(MJ6sCC;D4d)?9a4#O}^&aiNftEb9)_v3#if@??{|oW|$xngt9WeL5qIgs1!}v4g|Dkh)=KSgje^>|b|5bi~EVV3P-*=(LoRS{K zzfbr>JHS%wxHlT$5BCA+m!a$QP~=2fa?soNQ5zV>zt8yNXz97PGJw4-NJHrh{IvEH zGj`8Iai*6K7>FzF4qp{NHofCb4Gq&ScoK%zcBzM#dI)?BAoHhd1~bq}EFW6{$4 zP?rJxp*_HT0uK@aBqA-I^suH^KD-9>Xalyibo?imF>?N+w1we!LFnR7bAA1jv4^sO z_}^Vhd>OeOeh>U78-VYC%KqtC(~_^ZXMeQp0ocs2FZf?Sa5XL6{tab}JpZ9(1LT;eC{4d4OxJk)ypTj}23|3a~*m&0}7 zANYOwzq z-#y_@T?gX>{{u0ie*$l68|d#@fZ7Hi4P(WB>z+WZmw$;Kh%ZRX4xn!U_l3V`O(6VB zc+(my*a!ae-CtU`_NNTk770E_hVK(Xn}F|?q6xlV0L$UJ@tfZ_#naNszg9L>Ho%D8 zKs))3?;HJ-dp|Av1~%~1cYztfZ!p^YruR*VwDj;Vmw~ys9K;9dem86|u%_3?Z+P$A z_Fu-D-q^|Up$w<9^l%!=@?ZGgA&lko(9^+iIru#wg2NQ(7yRn=;1}5dei&oMG(cVt z(sykGws7}X*}@S2{uGSu4$pnY;Qy%=d0ha00*L)tL7$PFz;}wtz610h`tgnltv!YO z?$OXZbH@ENsNsS^(?Iw0r2)s5p-li=05ZUeCeu%_IFXN})BkYUCH)uzy=?+LDY_Oi-3aW`@8r(o|CDV8^jkuBs2m){a zP9h?(At1lsjv(CKbW3+VH{|0==FS3(MBt68{yafM6OM1{@cDc8o~d!{ZI7w>L2{O z$^R|-yKc$)h2sNuf_})uRYfVFfB5--l1yXZEQcT0@AW9eWP1a}+(cLjKFr_iMet+_ z{PUewMM=;ByxjkTRwdKun$&HzU@bsDBCsCQZ9(9Yyq;i)X!UoRnec-quP@&x7`coM zO8$L3Z(99%2f1hCFdS((4(B#W*8u0!P4P^Td-35fEY!3wfFHNAN+28{EeS(s2 z0(2J@O(Ia-4}t2SqQC3(J5LcZQvc7NnpUN%cO}#KA2*Om6y5LJ4k-Fj55$8X0@_}c zOhO%zKLBp>2c^54lXo!K0HskpQ5wY?rGXB~pC4vPP2;KRiQj3FZvJ4QB!7O;-6K#% z>$`{K`u{Wm-thnA_aRZx(B$AIjD8>obhxk#4&jHmumI2^Fh@Ul@@zpVunqjDJJ`YZ z(My6Gu^{0_G=Gg=P|HwkwSBw^teMuD|T~R~YVS?9CCdxL{*Ko{l zgXecq@DM1I>5U~j9f;_J+L+at!Aq^9(gKc12YW^q> zlqZ_QawuCb{B7gbAhD1qOeimu2jmI$kIF?$7xeUAa76qN?B~rXg?1|ZDaUV{WPoU( zdcUrXgrL09JYhXzi|Pc*gO;N?E&dRXyh9=)&=!l1N`C4aW}U0Q_kRE1=1?ZcAKC*g z{L%Ra`Ji?PWhy^8y9vqz^#gcA9nea)C~wFE+B_u>cnxh4*1^;h{&2i-+~hHX`H1SG z7yBbZOyRgu`-ip$?Exlq4nn(tc0das6n{8gIBtkH)Cq(9p)L5q1a(b07kU~qY>R$S z{*WJBhae762YuiVWgx)*AjWXaXx~uxlr&mSnN#wI9E^Z{j zAbfzEi4Gs{W40U{2@<9|8Dex=h39CvG%0M17fHw1LDuf->hABKcva{CO#Yf z4f&msTW!$z>;)ZgD7pba#D@$=B))l(UYKaAf4ef0MoEjVYfJ@KJe2YCMU zjuHG$A^Ls8UUWjF*Kc3Sp&d}_f$~`fSQ~o!UbJ6d`1I7aJAQ5e*CLSq^LT@7<)H0y%K)L>gX9h5m(1PqaM?~hq zNGynXVLS*p=1c;@TnQ4$0ZlGv2lX812nbgi0fZ=iP6#(>2V-Q+1ROcq%G?01K>z~= z!`MJl1;Gkc5X4ahQ4LoRI)SW7Fiyb>k|;=MoZ=DrNdYJrq7OV%qURduXHocd@N9#F zY~lF$L_EYRT{Y6=T+-J~77@F>wAYM>@cpicGz#P(O zpRf$d0sU?0|DitMf8^MX+_`OxxWV%SdX9qUEJ``hv4FGN6?9CLeRN;r;aLpwM00qJ ze$YNB{cc))pzUepu#e{#G|4`*(Su+TOdFED$qD+*DNdXaYdo!c(X#l^)w0G zxlyv$$IEeMH*Q}yy7eGRJ1Rd~3;J@<2ZYHMBrJMA%*|7d+@ieyNV40D^0b{_K)c?2 zmmmFr`>`PX@I~Ny0@pMK`oKdO&=2SX`V-Kv2?Kq==b4PW(<+PJPo2X(2wh|0?{(7f zhoDcqn)*I!8pr|nkH_eKM&;MwsN45McfSKpezp(#{0jDe$1ch6gQt^00Cf8AYGU0vN@f7MmpvjhMSPyudkT9^|8V`2ch z001m3-|N%(0Em+SKui0*&V~oTX>tJh0puqI@2LS$qQLS0sFMPKJc8r@UN}T0JJXR12?zt{A{Fn;G94HkN#M|`%?g@gb@7b z57uEnm;!`AOs3OpV2{$erxP8y|%D+F{AQ58R+`eb<35zhff%5Wl zNWkv>0W20*f(ib^p>U7?9FRhJzk%C#+5duH{{H~)>+2AN#rnd3SnS{O(cXp-9I#&Y zSAWBM8yZ83G1kuB=r_Efp%uhi*;!$cknoorw4q@T67nO1uxP7a@K|5NAV@R%7d+M* z4Yk2~xnfaQe&I)3`9d*<#=b^aZ>Z?s@{O_HQ&0}p%kVcn(UyC7s1(-tS3S{&L57gu z5bA=1zvUSELhPULmWxw=!dn{I+uPgys~jWeqM{;SAJ;wczsQH^{O>s2^Y4BkcaZOQ zfaCrPkA-;j54^*#_~%9WKk>iL0~TFW_JiFQ;(u8`=%3PZCv0$;^8{i7yJN&@!b%A65F)Z*G9*TnKpdaFY!G9O_L(Fe@sO(Sn z-|&C;3jTrri~V=}PbFa|f9nIa`J4SW{7>B>`!9I(&ndv6f5p4)A$~mmmj6qqzw!UZ z{+AqI-(RKtn=(`x!k#VqE#JlQy8`fkbPmV8H@+V^-am4JA9a!+In9rp`A07CBX|9t z0&^G({{6#n3g*FofO~ns=aC?R!PRAd0%f;X82xv@yr8`4oiL($t`JqYO zgTLxOjNs2YI{$b2PviEJ{=-K7B@ar54SpKL@AKu0n-chn0Qfn7=pPp6@A@A3FZy@s zfAhmdu?Ii-f0~XT^}lSukGk6r_8;>8{^m(>gZYb!n+6=f=FI=$Bmw2Q!USdG!9)NP z2~2Q30A`pZuxR*Y;QT3Cm{?%?xz62S`On}67d527#l4roCoKNYt@EHCqx~!QHT3m` zS_b+0_@ei6Lx;WkG~9l1b^GscE(o_saCNkgg99$dqJ4tkJAiV~IO~p+2hn>uPB|PN zX5hzUXh{bhQPc7q9IsvZNSRsdw&IR(osAV=>k$h~_W zlsjvlO)y_bzz#E)w%K_)U z<$>c}#ek!`1hDm#feYXAfm+2YU{w(a^lKu3eA9byx~BrL_m%_szDjU^xE?5twE^?d zCSW<)1@6xb0;lmdker+hK7RZNva_>6QBfiImf#7#y?qLEU(lI^O97xeB>?nghJgO; zSg=^;0Y-}7fzh%LAh=%PTNb8v&MUUx1amS74?7HJEOD z4QAS-z*zZ5FjkogX1m^k`JN=O)}9Pjx)Z@NEU$k{0UMpEV52V;AQ}q5bW;gf82AL1 zhV#KjcP2pg=L6(G0azU@1IWQ*usKi&mPZPK$J93vH#Y?07AHYKU+4P3 z>O>_#kJW(ni6(%aYyj)ijR1-01e=Ifurd1$Y)-cW^jsHM9qR#WlLL@G3{Xo0U~8ct zV3&FTdU+63uFr!;^akiauY=*ubuh4v2BV`RU}9nnOifOJX~Yzmo}L18b2DILW)!R~ zO#$Tm6xduEgJ}Ywm!`qi@+iQrj)1MzNw5Rc*2WarT}OcBz<->-^B3W;`2SXZ zZNxK!>sQo*s&Qi}euBk%=6@g6ALYMO7U2~XVWgv@qn9bdVhagaSPA03^Z#kQ!H`)n zSeK0+!v284>Eq-#Z&5Li4B}e+L4K{E2!x3kMml*=Njxmg+0hj$jex#LBzNi?o)EgjS^cB@cf8(B z<*|ZS_vVN0N)0w1pNItC2gm(q`MiqLSY_d>9B_VcP+LG-QcWBBo&QhsBPjT8LRdr* z%HL~IfgQnO=TP7I|ERz4-C+0*IQFE|(N}!u{4eB-h)4)slwdmq0k-+?`7eq{N?Tf4 z7@jOV5C&wBa z#~K=^|E>O|#*o|G$PpW>E-x>y022<>1NR#IL4Ky5^^ahzy=BsGNlP9OZ-l{a-qezotKdg&k!6Q9h1;;XZ7D8}FX>`ei8WD*Wg2Z~3t`&o%WS z>~(VO0~U)!t}bGKXaC(^O@9av(Z3HHz`2>jLPNZ`nEPMxgoF2>HOdG!%t;x1(Empb8cp)(?8k&ARmieTj~0oo0XoP z`!CY}XrEVC5LgTxEcWrgNdFW6DjetDK>o!32YpsodwU@N>L1zvm49_^wWsUfvj3AG zg@WpIbzxWkp!y!_xBUo(MPi`#pS%7oJII2n!c_?W=CCLvRAv#;`8n_3Ne7T01y><* zZz-;#)>fhNomu~$9b`gg+zvm7n{lWMqNgYQzhDRHtB76%?yn2c)71svYuX=P>VGBR z-_iD7A^aNtng1VsUIpXBU(c&>mkGG~|Ne8UA8{(-FK_FYI5qY9VHkU!1lc-lpukiJ zRGGpU(+q$b7?ajp0?=#@%ea`+79Z4F6N0*j0DOhz277YQ;Cu))xE=!eCX%4!p$ts8 z*i#u)K2`=*u6KY-@_mr$bqX}O9Rclue4sPrBIpVe2jd~n7;^bQ{Kws-O?X zkNuyu!9boiSdG5~#$dV5?;dCfHU&)~R-hTij~#KYFlN*T1BLhY;>OvGyD(-n0wei) zV4?t)bIibet|g2cEx|(G129+U1V+lO!Dyu&82|DZs1^Bw6YbgHRC^9M1LMUrFm^oO zSpd$#IPzRiDd6eM2Qn~*)Q9n#a(z5F-CF^!!PwDi@*A*+u_7);EG#U5v7z7hxbQBfAp)RhAk2eQCM&nJNF%?0ay1z-gi?~Rs#=L_Q? zdSMjguOL80MLB3_XaH?(t)Qp78}#&agTB6AFfh;$hKGj0Olvt<=&1*b{mo#xuL|_d zbO6*y1z1DW0`zD#jOSXw?sOfD?Y@C67~8GQb^_E)H`tnP1M3J}OxFuGXM4c*LLb;% z7>04(5U5$30*$NVpmS{ojE#* zIBs)o0$|nF82G~HN!PeI1UojglUi<$mK->JE8G7O$;cwaBH-UdX|0n6;_V@ISUO*u} zSifYPtl!!PNt|G3CMKq2!(s^z;ywH){UbXwlhqYsO5*bm_ltzt>=*t=`yl2M>>ur^ zDJhRWI{-<-djFtj`d_|mWp80(^2in+c790cAM}@!_Ewjf8TQdK`sp0d6+CPFPdbx- zI6E^nRDdz#u+D+KQ}18(4`sS*#mr0%6`%}(k?CG=`WyWO2mi?v)TE@O)ZtL{9{fsw zdHmcNl{>fP>5tI-ksfsBhU)d3kE3IrJAM54G38(6$12^pb>oS(w1Bi24+OCQ*u$^# zv(*$7)Pr%P?;s8({7T1Wso!$BFaDFvBlb@^7Mq=IA}fkx5P-?^XB zUV~#Oi2qIgcLo!KYu9*qIGwS1d04G~r9&qBYuB!s0=*I5FSpd5JJj{Ehr8JwNYl0XF`3@~`x~w0LaXKgfU6^Yii& z;(yirJ16WB&SyRxby{Mg>pxpV{UNmgTL4=pdTIVE85p|4iyuQ*`1}{*k3r+k$eI7c zNdn4qg$c_3AH8?@;ivpKKi^aK7eA#C`Xp8G9J~(tB8@J`K>gDrpg-UY=nh~9;kgf? zUm*{Avy{LzlV6{LxdtDwRvQGS%7elB*Oy?eAslqWJ$a_{9YB1|0~yN` zd*`iRzkUVH%}t=KrDgBDwWXyMw6wN?R@|Np_v6ipTCg$w6`q?mftiUexR>^W?WIA` zf?5G%V`E@^ViHVDA;9?LG?+ll!ZZ)iGb3Pod3^8e6N`rXZP0TXArPR07!{p_R1Xu!i?;}RdpKxsH<=Zuh!~4ynAE;*Dgujc8H!Y zan3iCb>u>S)R>z~GMV-+WwB>iVR`#@-&+>byOcU~LDyTW+vtTa8;lu?UM9Ns*4J;v z!%Zrndu6{uY;Cm7%QFV24I3vTt{K|aoZ&q&W$3F1R(xBv3Y}3y+eN975-CF|d8R69 zxuq^MLxY&zkvS{v+0{E~xu_>gTr9Mtg!t}~zhxuSo)-rQNxP_4&51k`NTwdp;mEL5 zd6G#x7;%=Cv51yc3WxJ#p&Wk^wDH(`^$95tei)N%$36 zp1F4yWv}977?4(SGO4qPWw5r~h|QJE%*yIgK+DzA>EkpAd`NoBVM0%5s%);e-sUhV zHBFaCfld=W#|+1wij4PZFFzPRf%0!RGc8|{o7zW8xMm7{3a{@D-j_3b5 zHglu+mXh$~`qQURtE65@2D|b0MHIyfU&&G|y6!4sKt5|`z$hHnn`dy=K4S&nE%{;$ z2d}XolL4jwoE|ORJ%2p^6_)@Jfq<1-#g2FrL++aJ8S~FVM}iI;X7^UwUKGnLF5X#g zZ*P}*E-z1Wo=G&{USEIo4UCSiz^enM-Xi;@6T>Bl*b?3C{xX-5^rA&4Q2sVbAXgk{ zk1z!!9k@tnrCg%r45Dz~hk{ zPgYoTdcg^{#Duj_|3R@@(Q&-f`X&J;fURbyKT3y`=aS>}yZFjYMtl&EaJ78dhNpwj zW$n?c+=}e4$ikcC%5HZUVGM9+2)DxKYaStSNE^?K3QF4k|^ej5arX+ z*&T(X#X+b)YO#2&xu|TbGh@i5-y%g)rP$ta@=8Lyuop+HFuk1y)uI2#3r1?5V*EED&tx(9iecOxU#?8WOmUkqb zQ|a%>y)lWD!pNfxTuqxKy!8a9p$V@r_`?w?$+Lum>2f@$kVTuo;jM}lE#0Xx0>JY`3VfZl%=_4L0y2n zG@UDx#@sZo8AGEXFJJbN(;a8SUWYLWJ1mb3Zc{Vn+bm&F*nR>UQ3pgBb~^^U>(}D4 zI2?vrD54RdFA%eq=-~2jg9@4J`GQPq?&=L6*BmSR0c(+^Ld@`P-Z2roXbyb05oLpO zX>|8g)IRd0>6T2gdROIQhVqrwD7BkLj5Z=UN z(SD6*FXN$kG(PvTcDJ(JDE!-Qy1I>#L;<4-5+ zPP?Nm+sM`WHRrXlYsZ?Cn0d zHiXy;uLTUb9m z{0O3=M2>?m+pePHY_!VI?7HGi9(HZP%5^&`Z{T5r1wg*l&Wo1^b!3DEPh`?5a3(_uw?=|(w(&gd z0(dd!@J7;?9wRdB76x*YAtMh1yCc!@(+1@mrOt_;3(PrAX|#WyaVngiEgY5|F1FZ0 zp2O&TE!JKKRdJK8n-8}aJ2W58NjE2!7?eco6t;LiaVhfn;=zo#KzgL6F$t4R>2yIS z(~EG9_+_G91MTTTt4Lxwv+j(U18>JL4@JJvZ|Dtvy5q*Jbc=F-Qm*x3*2P1F2CdwM z{ycYtxbGNWeVUY#l4-(MdGBJyP>Hi^rqyt%)vzlTql<+vp`mQEN@AzVm1vn;n%la# z7#$Ue$R+n>!7aCsUZmhCNkP1B`~tE{0=bo>ShQF=-kh;GP>_8K(2`Ec)|cCpJ+Pr> z44JYY40A!#b>*ni-eBH+-{9FaZp3H6W zEtcKMVZNG{Ng}{<@02trWovjs8)?Fg0GDm#=t#vE66BKWgy!%rvh2yEneKL9Hx~Y! z9W4TwE};h}??}`!w4OS!u6-Dqa48zxGO2@Im+S6+&?9n-u^+ZZVA*k>D@*4{a=0ps z?_HAOxh8gpqG;L5?FIxx$^?V@f%jZrh#p*ye00un860`zLB@7k$0$;fE#;IR!*Ic= z@=R-mj;W|y=bk8`S>)YTJk;6bYL;$DEGpgeckk-buA}!{aq!1%qlVW!mt3(KR@i3y z-L0*;46Fp+j@##xy}57jr51~A6>&2uMvJFs5I?c|iel$fjqbP?9Xq2?JhavbXVeo} zLQqjyWm8qRHBHmmg&@D5_AK?h{h8!bs&caF=Q;O>^X%|k8O{wW4fkMOlqObu`_-yN z9=|ibQ7TE1_j+0+F=etf-iy|;MNNB_{S3j?WSR3a>1j8+6pi^9aF#}^2W}}?=N2*4 zc1}z-U2G;ZXYuupbrHoz)M|1+J1;YsEo7R2_81Vt91NAY z9NSuZERWp1O+o$b^YyhyFJ)N}`zM0bCC0;D?{1l-uqC6f+4F}a+*^Ia(?MX9jk$3GJ@d37Z#HloKZ37RWPAgq3IX>__np@H(=6S0z zpT|vdXVhZJHgtBj)+8_;5*W`i^z$v~qf zollPrOgGkWs=q%_^ya|hLA?EPdJK{rYy|Xl$t3*B$QT~C3zprX(%LG>HN3-Ser&G-4KlNztOi;%b_%h5dPVl}E5S3(wL=3>Z)yZ|G+t2&K2hd6B%YdR zjK^K9dR*aT7$G@w!@^OY$6$vSwdU9zr$-fUX7Rk7GF#m(yLX=6qbTGFJ!~D0}m<-mj7unS`MXISz z=3@%t+61PiR~99J{Hj4`Py&qd#rWKPAGgbMk8G{WeE!R2AJvmzXFRcmxR=57&LR(2jcLnzQ!k z8}W4hP`a9VnOH%@Q^MD*{cNu{NcF@0@4G2Q@K;dKjcTNH3YwcoePFvyJ9P+uD8s}s zenju06~j?R(U_)qpkVa53Hm!-jSsK{C@f|evoP#3k-&_Zj;kOh>o`xZhR0%rH~opw z7w6g#!WVG4h8Fu|xW;te)`jy8*ZVrd;{L967CIt&6xxQpqAI;y> zHum9WTSCgb^u0-5qtbLrBe&hAZ9+RTziYVKnnpX`v`}v5-POZ3s&Z2a3U4l-W$VaU z++L3+U6dU<NSRd=jGNM7K4=sDESzY-wL+&&GJMhS$F#KABb$# zDVVL0d-HHjH63&BB60kK8WSU})g46Hh<%GNBAVF|yFHBcD}kp8RaZySr9SYx37pF% z^u3Q85Ur}g0Y;|vFQApV@IkT3Eh2-|`+65uR8)>(!}50e8uBa$3tcWmFv|52UE#sc zmRn@0=k??`$VZj`ks_c}AgRtadL;htCPv_El4hY!0?YlxhU^FSiI#%w2VH7W_{nCq zey1~CLRL}FZ9h4G^AX5AN!aS1N6bBXM(|bFLAJ;qc235ow!6lhdKY3w(C0J*`=OqR z*IjESgCrKwO;`*wc9gk{<@3LzaQW`XCXlMAb-ubJAkIO2NBPCWq$l9C}%TL)U)ItO4{6koEu$5}iz6wI+1N#`zD7ki9)#I#I}BQFzhGsxXAy&g4t%VYtw^Y!5k z;h2{^RC*!Mi>&+jVyjb5Uw}8fXDyQe*_6v5g#pC|TVhf*!3pb~tGDI-zVn$U*LTT^5%g^P*un1p%$O%7yTFQyn z!=qOggKo^P(z%*OKY+(D0!c|MBTIwMOL5VkoKd|LA;cl!k<=*9{e`!rn==k-UyBP} z*KQKkr$vc92$!#@YA(Ub{h@@07=-)E^qNFKdWA@IbkEWjK8()`PE1xg^vjp~vns!+enGf;w zE^LpC4Jc<#y;f}foy$;|GBg)!WCB;nfQxSh9T zZPe}lq|%IMNCT-&SL3AQTpusYt1(&Se%}7o1D;J{1ALXJO~y~E?!BnR!IgZQ1Wd5# zt+_#0@W9pfbpAVbeb8E8c+zBYmdHWZsl6+qj_LD&UHBJC6C}syZ*iI=A+wl*CW$MP zCRAp}sU(`lckhP*UHSl%1;20Mns?I8`j5XkTgoCI=W+Akfx6*gfs+BQDk|?&`qYW< zHu!egmVH7EqHG0ZwN-ku5z&aUDgKO2^wFVZ@53zB+182IQZ}xi!%ItR(t;kON5cN& zfbl*XPxxGuLwYxQtx%#-WUB9NQ+V@2X`AUQW*ZEV--!52!%X!V-Xr_xFA9bZsC?Bm zi|&fOfM%OWl8?|TUl8C)X%ofd1Vk*diyl+*UELn*tGrKkd{p)`dPpYykkSt4q6a$FaTQh3gbM4pQ?#P66Cr~|&Eom_ zwpaZQGp6Y&olvl`)IcHV+q~q8ZhRT!w=2F%^g1wXF^Z7Ue%?b%RpyW*?dH;H!bA3L z^V&~|TVB~8dB(nppH@)fN=ZTf*-|{~<4w`a+@rzILrfUBGL_vPcJPTKE4vT6rJ>Ds z_#YB@U@ap*WRMRu&eIPZ^DV_g%T+2GD61T#X42|$-CoEmTS~KXnKQzhzoJG|K#*uD z>2h9bjQS*0+|-u{8sK|+lPz^+F$Et|jW10(z08ly5PsO`nrC)Ygg9D2Bkg^cl%1CN zlZ%EaPvd0p>(uXaUaeXx?Rdg-tzsdG{E%Cv*O!8V#P{tN{YS*!=Hzj*DIUW6bUuH( z5I;Vi-z5hdd`j{x(SnxetXzVLq0lZm?Tn7Y=+mXGo?3C_NAQ_ITqyq?=1Q&gcU#1c zi;r1c9}pfT7tIgR&OOV7VDt$tj7}vwe(up&N(ix7Eva;1$-$0<&{)Bf)lGEhd(WKN z3MGytAGwbI$>mTCUN*u&n9`tb@anaqaHYsN`nAueDtVUV4LVIMp4!qa2ntk) zl5}Ud1C?SA@@&_T5e*#l)pEPp&61Wz%gqoFD1zNt8%9R7Jf2zrc9o{sTNLbATNuRK zK+i$Ql;shCAG?6{mGy374psyQb3Kzdndi7{OmHWUJ zW6VW=D=qiFhtWjXyMrM&_L0>qhw3=wF+3xSUqXKBmCevk_UmuRMOEtfph*pCZ+?Eu zT`=FDQYr8}JdVTZHaT&Z_7yzk!cRv$;^RZC#|6Xm?dOtH?+HYO5Gxp*1C|;5(w&oX zs-#bYL*ALwyBj;E+*{lwdrgJ#Dz8pAX z7xU?e_tjS8xrlLdF^!C-jQfcehmOl%iKbv1be=UkD>hbS$@;3_3^E*OJD2$i1G7Sg~UoI?&syrRXnQrE~MJd@yWN)wqa| zbuESo(F{Yl5nmrt05k6L`|oeKpNXnH^?u^cB}7V-lcr^r?D5tAG~Y&{iWgh>v1vW^um4^Sq$WjEI$ z+!|Kr9)7G`eAX)Ol-Yi_zEMf2xZZDv}b)>DvO!P*)h50@Z9w%xrw^Nv7dQ9{6 z5u}u}#7#v3vXjcybr&kYNlGuhZaE)=V`*o5>6+dVq`7!@iB#uk9dG$D0_{zg*5qNUsaRP2wq=Z+5 zr}SIJ*5=o8vi;9L%lYCXc=}WHP{##t(Ye4xu|xDPE5L=vJzD-zH4w z-{)+^znEBl_ijBxHk0eXQFN}?qY$r4UxtV-u@cnK(|`GNPI$B`gXfYViBtvIk(pBE z)l-v%$8>~y7d=-LN^kac<6XVwIm>>Y(Gd7wKEcTqwBpG;;Ijr@JlXCNRN>Ls%|;^~ zH%oh#t#_eb9We)R>N0#+w~&+G1#^=^G7wK^2F)QmH2dyI6*I>}#rlf<7d}W_TVp3$u-p$mFxgm!FIeK+kVN!Z9J))vI<>FUM$(JS7KvTgx1 z%j5J13D2c@e0(MSZY2+M&ft0&@#4_C2yfj@3o2U^)s5G80UI@oe}z_R-NmY!fnrBG zYDTqknkUNyqR6bnS}TilLoVaIu`B*(SnlDmtK$bVBD}ypLKrshqu3;}L%-N2;I%(B zvVT`1XFZAdJ}mas=dM9 z$H6pR5Q))k(n)GLFLEfkz)#zPK~BAZIEP8DL3;i|bsSN5IU*Oc>mpgk?`3qfUQ?Me zGH2_2B~w!*t>^&XG3Ub%4g0EN{gpslme~Oc;in~UW=o$OBVBS^u?R3qc08s3F)cZ3 z{$gmB#Pa}?I{OBdo%<>tN1fwl75Nl18G?!2QtH@@#{p~M}5VaS+y40LRHI=iurMtKi~nckYSx4z2ZBZt)9)?sS-q^6N(o&k=e zw#pJn8@)4r)7es;IaA0M`K#It#(_68FTDQv)RRJJH~*Z_!g@!`<9VXPq9!I6Hj`bq zd|c5>`ujY!o;W@6fJb2?b#D29jPMFa{_e}{v-g8u(x;Y~G!{-@)NGv8KS1j-RaDlO zWfc3U@#Ga0Z+Az^m)(A~Zq~R9-{{W-zQa(g;C-dKyRM1R+{79r6)qeitUeNPJG|EC zO3z_-)T#95Bw60{(P8yzTvY7c`LUoHYa4;T3o|B z$}_Fad9&8~0OQAg%J)kTNxj@Z-(iZ~oJZgt+!h>8z3)EOj|kUmx4dO~{VnT-zIBVI z2{DTEUZm~mXAXJ1x6+GE<9^xRbLgUwj8cS4>Svv7v3qMbF5bA`ztt}%E7Dg#H8K2r zBxktK|B^9Ywxib^@nyW{&*giH9eG#QWv71r7C^vi$m^JhlK93B1(W^Cy$jpj(D{q9 z{~~hp(hfd6tF74gYztn-s~xrdkVNS5W?8|gCKsQ@@OG{)UkjdL;48cOla$$JafMc? zmw<7RYbqi6<_Gytg)_T&w%dBzPPWTs_I-~)+9ul}_Y{OcP4kNe#!D7{mK{BKp$&yq zckXM4DMVe>MM-`fseGmF+mup$?xfsV`ZR6gQ>WMI31k_b5^>6>Rz#D8ZIHGcDg}enH&clQju?yt0vz9j494Jcp&OnW>8pU3z+*g+1wc z;7soUj~q`k<()I09@Zuo7iz7uau$Wx8pTKOneswd?vXv4B@%kBpdrW_kfuE3ad(br z`kjDeBLC%teTNReUT&?TNk}o}jSx#Ltj6U>y|##I z9M{6j-BXI6h%@;IsMBVeXZPqzP&r+np%Hx??fohvz+CoOE(ON#g(|5xi*xfWC#R{{ zK>plo9?7pto@+gO`K(>z?6=UGB*hb=>m#0O8amP{-S9HDdL}u>Zjr-v_aO7*T9KiZ zyJ`k_WZ(m>Tcf=bF8V&o1VgnGY&gY= zc1$&RE7hljrq9Aa;r)5h5d`N%Jq^aCje~>g64RZ5>$-TQ0mlv~Gw4vnQhdJi)irUp zc_2<)K|j1pr+~b)`B-hheXd~c$}x%QgnkJwuoH=SloDvp)w4xlBE_Bd(q`}zA>1w% zl5Y*b8?4qx0q|`wHlkQM(*)l89Ltj))$o&3ePHE`z_hbgs zyCo3HX*Te8zs>DC8JWK>R2xmGt2=4q(J`ExV`a4KEQjrW%k?NO(X8sJ8>{1TgS1-= z7nei0amnLUWi3+GcIi2~)@*v})DXt3w($Lbc?*az(gpTF{1T(p=w)oQHAOMceLyQW93$e()FEG{|VMr*5a`Qz0n2d=vfUW!>J6juCCum@5{ z+Vb;B3@DY)YG$}iKmD`;u3qIikT7C;LTD7fW(w_NFnjhi%M;}7`#scmW{9&--`;0; z>8<<5N-s1uNU>E?#HE&)pF_)ub6%gzPiJLiZu87r_H` zn3PIiFz$KqdNT0XqL$?uLx;KMP1`TE1*Ls4Dp1Ky>ZTv|VPgTNo;t z+vIYnaJDSotWhQCVLT_a%K{@5-08kIyzj(!k0;8QL@lz^Y$qkh@yFPotMLtQr&SPn zr~8^nZ#vhu@iv))vqrb)#k8Np+J2lJ!bL)QgMM=2HzIWf#luoCgJY7Y5F#ObK zN8jyMWm=o7>ONDs&nD<658lML9GqcyN(!$T(lsj}e`=E^6K_WTiS)rxUsd9_34ifq zzcD<%fWbB8_qrW?@v}uk2kSH?XZkTB^F5?iYs+W30?$*L>i1N^&!aiMDXmq>B8 zh8X9zdQke?47TnLXyADh(W6+qc@_B!4|#y=X=IcM5vtSL92`Dta(*?>!WZv7^YyMK zkx;wm@hB-Xc!b(B-WY*W9BITj78=SN2_WZdXUH##v){f^uL+77Wh@d_LSEnWD5Aww z(c3RF$@aON4rO25O2`cgXpi)Ypk+zTshnzx%5)tT=VTOeOL;gWJ;(4!+n~7Ef8k6_ z&g5zXSJ_l{9A-WW&s;`xJdPYsI}ADqEIr(`c({X=R>LxaSG5B&S3MijlV=VaP+4wA zb~kd#aghiyU}&R&ZubulJ!w7|lVW$!pzpgURn>u4^;2QM1*XPFy%Klk%|3 z!$B9#dkrFF$UK5{fuo^Q5*p1< z)^?i7a9Om8a~5%XHTY6~QBphO?8`aad3O&BK0F+tcl&&omzo?wY0-D)u}8e}mfG~~ z_iW01XdPwlD}pCbhfY({Go?19quzw5$jiQ33-Z%8q`$SO$vf;KuA>(+etToL;Jun} ziq@pIs+SNUDaU2OrmCrSrQqfC+ng*yXJ~WGguTrA$XL!lJ)p4ycBQI4G$Q+65hsW1b`3p+nSq!=>=|OyrMLq*Oz*3H`Yv{H=FZYOS1RZ^a>} zxSp&$UvfK$Pq}ptukLZegY$4b`5f3=PgI08l#$^TT4&oI2Yk8QI4>zcATzKk88UsY zXkTI@SFV*P!F8hiPfv?4B5SvnJ2Jyo(ry!_9bo@N$ig+y`Y3aI#`Q7pH6i1ULliPn zt$qTvg-5I_j=$^~2{d5!)x?|UkHvd$VUiW`u@3((2Z33d%jI|y)ZzwS&(K3<)%Ph3 zuU6 z=F8nMqKsw^f6BD8*9mI8Qy} z+w!~a%4~A)aaI>;ZT#ApuJ1W!zH3F=v}(mghYOAt2z)TRWt+|~VAbb^*{0Fc*RL=l zPYD-M*6s{fI^C1wNEf^Q?hIxuEpE5cS-_&tmcfHIbH6={9zWM-xWH5Ke@tfsO)l@G zEnyKgBY*6hBK^QNru~$Sp7u85_mbE9ELhU$%FC!)>ucl4iB7Se+OIdDrk~bJ{iL=e zY|wg+Jl*ZJU-v*HN!I={SsD-8@iX zoVwg_VYtK2cP!_lVuar`b6!wjdv0#H&8Y>NPR65Tr%nqS8v3xw^;>MU2N?SLF(Q0S zF`6cPsw>Lvjk32daf-ZS*E}=kFH@bVStFv6G~1(nhhHt?Ud)Rkr3-H4B-+wsZnR*5 z=^1JT_3aZ6T}=%0EdiE=XW#ZM99TE+Hyysiag2z_iov3?ibl+9#bKU2Nc|$as)VG< zL3rV9F5r2G{QBcDI#*0tN&>pC;8O(jsT+&&4VplHG-1PVYI<7txmwG`tuFnO?4WGA7k*r8^cdznZwf*beV zD=TE@hh2e){ofF3HRVoTU8?QA zJNh*JY;uV_BK~u6Q0MAO>89{zv35?|qXnXm$k>@n(_^+@#&eePP(oT~l1}K{+U=d{ zlEM^jf4sBZE+jPyk9@B7)b}GL%3OaRHq*TD{QC3RcC*WTTbXXOMv@i;itE{L#V~2Yv_pzZ z{c$@w%bwPexA&8bbQ7o&NN!8(w=ErrOA7mP4792|y`eom>ceE~p<9>vZe+k+YpXSd z39lxehd9V}hfc?#ii&xw{M2jFO>0trm$TII=x1cmmw5wy84s!;swx{M8DC7{TIG#) zgFzDwu|8>Q*Pu<>g(x!0R1*c;(wmJPujzG|%4iAkwoRsEv+h)PEuU5^YGp7uwVo`^ zVKBm*L!jh&z0+f#W>Td_AI5kgQjvj(#%y8Q+7TNnEtZm%`GB^eLB3TbT8h-If$^RJ zeoBhB34=lsT{O05EyH2SS;Ar5CT@G+%)YbJG^FP(JIWVuhgk<@2;nZ0;QaeUOIaizSAO_}12>{(ilwGcSJjQ3`{UYi?!<^)c5Smph5qZ~W_Zd?PrW}P zz7EF)eIa~y;9MTt)4Kh%gOZ%#YN}^T5%)~V8lDrwB}K2Jv&`8+97^r^k8DI}8xD6i-Edw8GhQC!V9x6nI+n zBehopZ4ht+%}m;}=MJ{_LrkKV9(B-vY%@g74Aop2`PdiXJ$9#H+%fE`f&@?d<+Kgr zh_Kc#9gjY1M0szJ%@<0Mu3NM%1$B5%h^rOL@HvU<&|c~(nDPC?vI;irwzpMx7=+(UE1(;kT7~ zIC1XzTjCNo$6ovKhua)%R#YPh4vNUYN0VQKJmy=&P<+JVO{WTEE7}rz*JyMkSc%`o zyrnx=BXN*e&en+o)OiF`rrEyd8QBWXly3FoXDOQIbW>YrXIW4UtCpl?Gb`VHlM_@XB*&^RY^Dm!W$EcB^Vls)f?_LsM!kVF6{VJ2 z_5q)d$~#(3NzKYM=eB*IDHuL0o0?XVENsD6oblvVkJ=2=i_03Ucl$K)GIyP#_njSl z{XXt!hvBEY3=a>f(PVDWO|p~Xhq~fbMqf;9a$OO-u`5D8tKxQHBCskh_xuhmp&bS~a3;Z^8`yQ8iv)@BsZ{yt5FU9R|2lQpPh3@g=);pYQJo zKao^@DQV$3rE<&T#O;Et!YO8_A%2wTI#Mh%$CWvtfahVH*06ZwLkr4!zDo#X^Fc@5qhI>q0f9NXCB-_9hp30{4noL0bjs1=PF~91&)O0 z9i!XZPxN&`NiwNJ%%+AtkBO4N}tGsdNj{4bt5W@5bl%zpnQi%$`}Z z_FC(X85|D#c``+_2>Z{rV0xLXG<`G*I#~#AMlB?ZlVDg{d3<4MdMFkDUOAg!nuhJnEE#!ZP7Nl<<+6tB% zKC{ZqS=viHfEn0@ zke^SN?moY}-TIe~<96I_#_(EAr$h)D=HdI-{l)BfJ$QhM4e)?Nfo`*lD~4y&9D~{V z7aJ%H<#slp34h!MD*Ie9!d~*3GR&9*d>&exagGXCb{c*QkJ&i_zb!*3Jn4XiN-sF? zqR<>oFmU`vM_K4Q_T|y0$C7+@x_rVr-yj=3ELg@HA+W@+rIOaEPK)awjwhWke`qgg zgXCq+A#2H_Db@2Z32zg)XQKe7ar zGasyUQ1aE~FAIXT!?DVS3F}c;E-LVCVV(F&NPp-2}IJDKOw+ zf1*wV2n0@|mwFpEmQ4FgbGc(u*5#H&(6rza!kdLw_pYvUs_9plhh4E78pXvuwC57K zZx%3;xl!y3@bY4;H=Pj0i{32Skwr(lz~yr3UJn!b9JxN)%|O~S>)kVYol6{X3$;QM zv9cvTj2-wjv8|oU*h`jwnI;+9pks|qQnikrZ@TL~l5Sqb$g}%AUM#~)BF+*G@ZS$+ ztIYb&Gd+4P`Imjpfk}e}-sWPUT-JNKmlcaifq(+b*>u()|JgpRdqeEXT2s}28Y8uA zT+GkzT@u>JUGJe}i_QT4os|q~F4v^f$CW=}0!t|u4)3iE4|%T|25z~+?yJ0^N4j}7 zYh!fM0Ijx0{+c`N%4J||=(1~&@2Zcvp<%$uOLWZ5pY0t2Zm&vLAp^QXvRru=Wf<3l ztnNvial){Ki%|+Jt2VlTDtBR z13$MU3clcpMnHy+&lzd$jq;0NTJt?0wqd319`+ix?L~998cWIJlu7w9!d+=skYyZu z+=r%X6o+&_Ze-?tC}_$wp1iCaQsfCxjDYO#=T+`S_U(z`>pA`9G_0R{0zDPRBd7=W`<7BuAr-* z$Nrv}c3z6tKEfiq$H{-ML)D-KnV_r=XA-}%Izp^m5@dz&-+I4@Oq`q|;H_9R+6lyU z9Q2VK31E*gG|XzPaf^$8dbUQ{E!2~H?B*vu-4%*n{xF8GWE0LV#Rm;It*xN|HL4?U zT?JToEVZT!#^ShU%of`PG5PO&2G}ZAFs?M@gc{NcHbo~ae6-)1#jt}5I!X*F

D! z<4e4Reb5c}*{r*6y|-P)Ye;~8Egv-{O33%z<&#F*X%i(scF{-D{CEx|*>S^j0>ARl zGqdJpW~Pbg#8yh5_?~1K%O0A{7r&9f9z7z%kJQ%bRW^b-SQ$;Mt+At#T~53c2WEcr zKYu!a;@6g|Ox5AT(&MIku1BPCv>a47_ybq_o)^S$n_tTfMpq>Dy78|94xi@Vn5z#G zY8AOK?@s>EuTOwrQuZsr1@FxB)8gYtfo$7V>p`9Rgn66Ki!P83Z+V*Y;I|DkRROA@ z*-Dw|+=cF)u4U;c_oafGOBaRmDcg3CobaHyUtA87Hsi}94Ecfd1t?uwRs&WyOCpSN z>L?u?%g#l0le5a4#2m8rYJayY(m(#x*G%6GC}x{%O>i&QNGQWq(Z(;RA+EQbtKMCY z=DwMz=ilYU z#0V#KO^zf_*i4pGx9FWf&5}O^)k)!x&%E*ShZfw9>R`ri>K}xt2Y#EUI96Zf{rvLb zdYiip@_K=L>x<@(H%2XD+h5wor3m2Ii{fl>Ye0JEu%OMFM-Ws~6Zn}DDff;i)Su!k zCqy*m|0eRYJ#ydu6foEetHTm7g29uaRAoLy;CtGW)Ow2fw2JBHa=tYZ`;O%R2X=ld zb#w_(e+K=_sXG&xd^ErfF_jmc&yHk` zN%`a8zJ1%}w8!Cm-M;HzHW)e&2rbZ10Cbpac%VWD#4GQXgAhOH?FUG+Q)Kf_q(`~V zzs6^n{xu3MhtN-zSL9%g7yGryOofIux^LD#3tm%^hRZ)#%zhM-hU;TaitNL;dK-e5 z(YW)z4422-v^o*G3QpwFf~3yCgC`B;KqP%MR!}D3w&Ra|Z6Y`~70dETfa0%5w#ZcWyFjd=)Bffl!%x+=snew~-}JJoA`hU+YP-KW^`?{l{X7Y%I3QFTnZlvGQwdkq$aoYv3%{--fQ%S^c z76XB3UIbVyR+4=c_tTW^6y$10v9y#74oVKOx*ry#C3$2cMuXfdx_sr@>RyFO5Gi;Dt=553@2Hz;T`?5a2a&wDZ11hw)hE&&zvd;sq@!9^d-`M?2B09%Lh4D=Zj3c8FBmfEu5P;|>hI?2))uS%ohz*37PcuUa{hnRq{rP%N&Zzj+@-u-$#9|o!w64#T%qZnA*}Ls zew+&*aec$HS!dL3VpjV8`$m%$+EMv4ab%+ZqfS%%*+ zynh8NZdiUf$S1|m_St*8#$e7;HD52KuJkcyTjKK(|-g7Hcf=rNY`M8W88Y#9iRn(>K; z4{<5xC_(#MbK_z8DZx1r+{%X|Ok93fvz!P7e#?;aU17iz?mu4us+9M&5Bsy11WsF| zl~I1J%V*v@DJxx!>arA9y{JYxWvaK2ry~y(A$*47Bi%PLcKAZ}^N+O?qv-v=cl(@I z%HxvWT2)1-SY%a{Bs7TOt)0z$4|uQtXE>QY@7c9h?X$9wq3Gz(#yiwrHe>O*M|Z`& z;@*if92c0g?~T0GRff-U*2CkSkO#Enf~x&L?yolU@XF9sG~HW?RfvNNtT{7A9G5@F z-VT&hw0jLrm+4pBo+WiWD1IUgz`wu;OiY|v39{@mn%6V}UsO}hVsTC$ka)){P7!7DesG z-|sXx4kSaHCv1}PxTE1Chljp=G>v~QY-^a@1vA($gtF<^)BZ&ir?S-TpJEQbjy6cD zT|wP6D2c)ayC2W{UYT)t9^k?}4B0g-xpORpmuETp2_M@4NO}xRIBFaeVwGdgrYbm6 zdd9BT{=;4$5#;(IQ(MUla-@Q6DH#&b&ugnG0d1jX$MiWO6I83mOp2464ou%_)K>2GIM(eG7SM#^r zw*pU16d|xAJfid=Tc>(w-H&XIWbiO_`cwjSMs&V6f_Pk8Ipp|Y7Mrt(_M1#Kl)7nA zp|}YWlcUsli-cuefFN7k7Gr}?gg-s z{Nd$!4g=_KnC7pc-+>gq4M!2ig%E8Wgf))Rs#SIsj~f}_4 z$`}I&y-M^Czc#_54$W+;zcDyfseE2IPE>Xmqimtpc}>!H!{2tO%}Y2a-`^+dxFS zz>E z_sTiM81?!7>j@M#LazMM)JU)2zg@G1#tA03Hf^4J>raf$-iFiUsl*3W*HLL6-q2)M z{NyJC&gRQ)CKPVJgZJzZJccwSi?v%1J=maCs07HjQ3&gIKGx@u{6rYTsiJftm3&@T zO;h1alkHLU(T9|*rn69w_VWx5xXOX(L>I)pVrsH4X#eyY|4tvtE)+%lm$k`0c-%vh zM5B&f?-|W)xbJTKS#QW$9R;bbLTgNab2?#E$ThLc3B+dSZBiZBjq)e>9iWE;i~wXC z+L^Qv^2Q;uFlPtpb69L1&S1%v^TY_YwS1zf*J%3jGgEi%aqey+j*R*1A*ekitg2K=0W9i&|L-6 z4~0KTf`!JBrmWtVpxXelL?LCr<@3zvOj7&CHD*AZVLbqg#aHqy+u{l!zA+LneKn+S zK7KX>Cs?4?*qY2)`ptQKl9l{3teda%Ve_=2E1I=FiAUz+&i(%DF%pF>y@&6aaF+tj@@(r@Da~1bHceGNcBi@)~^NApwZCBbK^H|Co z*PGMqn|G5y=>hJk<7x%jC!lY#E7$ff{U<3<0B$lqYK?IPOWx7^F*)=}-99$?1BUloNm~|69PdAyZ zzeOD;_RBL<6D!QP>gs!4Fz-IX{pSJqXnP+S>$|*yLv8O*qtksR+~G0JY8gMV>1G!M zZjr)|K?&4`p_Z<=GQ2f9e4=G3x3iNqOqkHNh2!k?UoBj3X|i4&xRU+;04w6BY;`=B zS$h?a%oV-@H=2i-GpNT6GI^aMbFT!O(=n0I6b|kW$Lrgv;d^LO$pDd*6x&8YHD`)s zX>9O|n4|iJW}DsiBgVX@YnCs+#QOY22h}%Ww|s!Vcub`@+gAfdfjR$CI8^WU#b{HM zl^S)h`K!Jv8HB4CK8(zFa#yDWr~&$ai&5*b1-q@AgrnZw;)(Pc9I2_SZ%H}o$GxcoipQ-RJ!<9P%|`(8r|dKMj@4wd=!V^1_)#It3YJQ zd1N#L&g#RGK?3ASO>?+UETp8I6tPuy3s)ma#!pc;qNfqc50;>Xxq`f}?5|c1d&04R zwbuPx>u?BFu_Wx6nhWE*Kqk5$&-5O7ZYE}p3#y5#M~+n~Nn{g94ASnmw%HoVc|RP? z8+VEL-J;j8H*^);ZT|R*NNXwwDQZL)?X?vl9iM7xf|rZXm!wn z;(@{5xz|Yh=j$wkAgj23&3<#l&r5;nSlPUL#Xqey%w7=#T+OmH_LpIY&lPvpN78@{ z`}sfcj7e0CwjR~p)4A{f0~PaM`-&<#*T{2&7oOsquxM>1&CK zrP#0o@eaEqzJZBt`5l$+$Msb^|1!AWeg$q6;}!+_Gq10h@@^X}V)zJC(-_{7EwT)mkIftI_|(adnHKOV?>5pL=8f|yZV+8=U&Tr+Z_*76`_XCJLCN{J zu62WDuOr#s+hlqjgLefP<)scub$`ncvx3PVMz#eZss0Cot!tv)dr zx&GGVU-3D2$ne_xVF_n7evHw53kwXBg1-Mp&*AMNVVTjEk=(nIv?u!UzB=?ATkY9r zV%qY)EGWvJpM12to(-mFT)X$;?7Y#kl9LE7djL_y!*smannoVqTeJDNyQuH>wyv7; zIQbir^v9&!o5fdu*E}uIEELrb2(?nkHq&i?LO?Et!4hvohSHpRS7wkaC8v#$ihSI& zo6AiVf}4w-)WhbX!!mq$-H#7jmJ_=65(%dNMZ$69 z^6xB`1^JEbo0Gb~Lri8021LW}^4XbuA%xkhF<-MPx>eC(n0l?tpYm%TC(h+|uAVLq zan0Lz(aDY?Ci??x?-0Z*$3yT6FVgKO=ShcJdI?h6p~gLYeezOPjdZ!z5)j7;eg^Q# z0hBq1N>-j392cq5^9juv_w!<)#sP-Z5Agx=aKCkm`&m>Y$HTgr<(u?v;cW@m7C3Z# z*cZ4cM&;iyQR`A$xbMmB7PHjjwaPKhI??mjj)%KWYiC`$H9#ye7i+2@R<{{->a$O7 zZ-~^icNqOtHIPsLY_Rhy*`%z6_xg`g2xLhE33UslzP@{JO$#@6NX!7jG#=M9kJH)A zihgC#@JcrXW#U8&L}`<~o1wl;{-)`3SG$gmA)HF`NP*|D+0!qV<-6&Im77vviPUJQ zXfkVhrqV$D`35i(0qRdpNK6jPsy5yBd5oC$eUMm##qHwvVP2b_cG`olm?C_KXxzR_ zVG7)zsxZZYa!#sVTRcmD(9?`iAc{B!&LtQt4$cn0M@1@d`lzE-Gpv+a*hIj{s1}Or zOLq}!Rwh|zNwg6>XwB39Rn5dPZQsH8^EPIR|AZ$7M~BtWjyF~I5cwM$jRZD!zO;Wm zwBMG4f$vC|@xwZyScvMMO^i>&4M1lULcfq13^sVM8#ITwh>@4bOQ*CXeW!Eo z7(awn{GlHzrj6v=Z@J8s<45*7)<8Ij4#Mf?L|Uc;Sg0vBJyOlzco42PZ8XF4uw%N9 zn3q_`60dA~=Tw{Ro+x^EZWTB~|Do)i!InFs+_H2h*NrVDi)M(W6aj3uCL}6Rt*UdipZpEXw*eyWtMvOn z#=eskPe*_#=4|N32IPc@gA{}OFF2;rgOksGJ}%bf>~gzAxrR&gJ~aoPl4vN|M*g-O zro`gz;L$OmHIs0bkRDXeH=;AV@0od@vXxxUo=0+;9w60K-FA?!7BI0ajR?9Q_!Hm_s=!!Y50Ulc?|sYCu6kL9PVa;}64qkzE)eYwLF(r7RY;dqAc)ep#4A8%&F_PLc91vOP? z3*iqe?w^k*hkGV0s4YC%2K`7v<4l&EYZYvq@mB(MV5@x9CcK zW+`y5IkcRaHM&pW{=g;kM84xfp5MrAN>ePb6E_Yro~ZT9_dPIu?!tcQ7GS$n!+dat z&ThT3%Px;Ch9yZyJuZjWM5C`y8+OdfU25Zr4n$v_RUugftbQ{#ON*d4mFgjJiifA5 z04vyQ8F&Lz#@$c5vcj^iV_e79ZoBDNKaUF&1Nw1(PS5YACSt?dDuNpE^1Iq1z{DfH zzh49BkR}Q6G|Rsx)_Y3xdDmm*VT->51bU0<3;#<_qBG~_6z4zZsTZ2FWK!>giWxtz^Z+96YpaQ6zo&VA=nYsYDu5*6JF>-*_zXF0;`?y%5Rc@BFxIr70VoP{S+&noUsZF4CQt9jhI1K6QBE!*!Fu0)+!ft*4Jo~PU@!Fq1nDY0u z%aTo>_a0MRVn$5eG%A#9%e(Y<{Tk(H(quIcjRohbm#yk~5pJ zKYINLXu=9t{2wo8I$%33{u&0psA~m&{!3v37AE8MpuB{*Y52jWBs8r6bqI^O8?hC- z$w>I_=CDg*r8r!lqKkyMeWq63_M31kn~@sz5}nhr8jO&Sa+^Ci{1iRcR;#~GhI!V} zuP%lQL0LoOMHdG+FkPrHYwqEsjzK6KDusp+PE9neK?Ka1OC%uJX6sz8%U|lN~K{+8|I&CJ~G;c3LR>$U(IDqkY1GX5&&3pqk5ooOH0qE9Jctf}2;}BazD{`k+3+cagq5r!F zNJP`Y@)1NLqT;d6SuM|>I;W`qY#O+RRuoTteMiIytpjEHeJh+kuwNNry+7}atufg? zo6XaVWn&7dj}VF;nEil&tR5kx@}yVi&uJl4?+cnnuZ9L? z3na&p$eV*Q4%4+FmmAWlstA6#cT}`>%CHV{qH6LOf}F1xgQyZF&$VBI{3f!gbCm!;D^6k*~iC>bAxAwD0a>1 zixAqOpObxlwo0F_`*ftHmHlTsi%5OcWUL0UUfD-t&NK8f-#xv&lH-dI!IFnNDy39O z5Of1IDuG^MF>o*?5|&LeG6cr`ARW?qJzqarl$&dDMTvq@;MAz1Ig`Lb#0QuGHvjA6 zGE9aNH)IKoeFjLyo^QvVUk4cOxFa&_BHoc6b!(!U!xqkn#E3YsJmQ_Uhp z75ddGJgFit8I!_3A~6@GwsMQpzaldA^SXyW-oRXq1?t@%pMqg8@cZ?*vIti07~PoB z?e4hvidOiFMR|{!Bj^qmb9#lp0Hz!4fnb|BD_?eTt5DoDm9$k6u8J8?F;|9?6X2$% zsq8iJ&Hu$s&G))~Bu%-8(*yFp9r!t!{wvR+j*MSUPMofY{qY4!rLs^}++P#mr4xK# zw-ZoEE?pu>jAYf^R_x>Hm6~$j7AHX>BRVrpea2BdbZ0$DdK+w{Z*j)`0Q zlL(#)!lp*>TIv#=@n+`QV=I=C^R*LV@&2G5mVp9va~-arhKVnlZ!H&SZKeuEpKn)Q zKLS$MPEG(vpo>%iN^pdKKYB2lL4H%gMru9BYmczCnR`Z}L4`stP#e1x9$B%*y2m7e zQ1g;U5D2#kt;#&VT!PIDTg`)Hc@%MvUVQZLP}b#c;SKU;jAj~!Y}!oB_cjmRHDq#S zxjGRf9(HD?+evyP}5RrUnQB_kec2>@4O{YgfU zgLxq+mp`0@i9NK7cwY%4to0S)A;!IW{&|544#WO0`BDh_hqzUN^B%+|qb!Ms!UK3* zn|?{erEk{``ig}0g3%DHXK_P$(Z@%Aca?!a5+}tdB+uceGfu-WP0QWv9H;8nXq8&0 z#=ZS|-y`p58^q2n5kJF!dk}5%CZ7v*&G+Rm<6$=ZI8$6{aVW|c+Nlp4FQeI_2jd1` zmVl48hZ1c)&Vx?j(Y4`sx0UA5zP1Zo=8bVSa4q3t0~PRlUHS)0UznCc$wWoM+Zo_)%p0b{tWccm#lpp+R9E&c*QI#oESO1TfE~&X&>jo#n~v1D zALiYdM}G4qq!*-U1|sj3M~(lOI=gKo`q9jvE2L^`mm{+;8dLEv6-Xkgd-*FErr297;- zGe-A0+alY7gj^GiSmT5@6q+J&r9Zm-t{L{R-Q>(4GtG7| zkSb<5GQ4*X*ct^=+)O1_&yXI#1)B0sV9qDbLCznV+QwwbdvctLYgEm};J0c558B_Z zp`g;i^#l^gK3Jy&`a%R{H2L^3%4N*=ViKTc^DkXcRe+3Lv@*2U56Sv)@j(_*4X+08>kEl}Z3`$opWT1e?hBit|CVzh(kYnH zgFWl&HZAS^)Ig0z_Dztd$8ww*v7lOmGys)y`A2$F`AxRPSY9gV*ZDM;hm(cUPu4H)HbSQbkUofIy!H3@+!9;pA8VcFx8y z_n6n>ORG$}d{MyP2HH#o{oqttEXdRrCh`0~F2FsIkd*#}rM;lK0;%IYqv&hzw7!01 zY=o;znMvg9NJ`X*V&eLhV?IzvFK@X;VmatiTj+8(i|Da9oNCez^`n%Vf%}zJug>{l z%M2z;B65!6vy_a@l7^f#FGNF1-P8VWyEILrL6N67Bk8P7YAu!l7Ht$){aFQ!Md?n; z1W7d4_&DeM?8CE6S8{KkMDif0D3?x=t7l>YM@i_g_Po6HY>+fo?CEAh^du1Tfr%~9 z4soan+!HhD-nIZZ;BN&3?wG(wzy)&e_?B99k8>G*SP&ByQ)4kKTG!S+4)o&?qb%!)zzU=!3Tq>@AjSz5T#%WY}Db65z% zyE2f~O?qhgYY^&sM#iHHYf#0RNnTXUlbD$3wD(;}o+3ck;-oz3So+81vQ0H z&-(|OkpGwAZ8>Y*PD0#&ct=j2k#wUuCLydeoZ8*%C8l^$`V5YqyD*6NrgF`vaDDkf zK2Gvm^IAG5Ly5s>YRW;o6{y=2dHcPdsb`xb&&$9ooJXeg%5vdq12U2 z9F2aWm7|j5g3$v;K37~Yy!hxYb3p)Uyvk`mQLzTR7;dt2Y8S}jwKdY9BkbHpyTwLm2!vQmc3izjGe zdQ6mfWcWzxmF%oPv@#Zbb@ZlsZ|7Bp3aZ#;deKTf#sfbt1+qqKB(u3AqFi9QAX1wa zq=aN!Q=r+RnUJ)|>S=QpAQR`;Ivhg7 zwQw19Lg5n0rCBnPCsQtN<7IW%IT`RNJx5!GtUg*$|%kB17?|t4}fQBeNwS^xy zel+?e_tR6(7Ef3+T!{Ch*5{%&t+`yw_MB=7@%$16F>+)*0Nt*E{gY!t)C5+vRJRrM z+uwR&2|GN$Fst1`LRdgK{GZ2NDb+vnVbFK9#Sof2FS7Ai-(rQSg;~n>U%4tOc1f^~ zFhf>_2W|(`-8;~Cl5L{1sdsFPf%6j3#f}zVB_yO_ zk$5-s5*_z!yee;e7sLr#kQF6nb_oh#U##C?U&h{kv(E3o#hvAa7@$sMEhrAM5)*lW zH}L<9Bmvm*ZKMoIJrkQ{jjKc$R_kG0?JO8Gx3TY^gNe+@f(2JF#+xS}u0J*hX-YOt zK4-P@N7o+|jeGTn7E3056iK9vmV22tPEHuSG@bN^{_HWyJ~;z}=5QX?!vtc5OTZ$>le|(Fv~1s9_EwU54xhyq zTJHvY6*S}==Ch_pyjO^+*SmdxJhacQEMv34xh*8K{Vi%&HM?@iKQ z-ZFs?e_@0I4`j%y9gim@M1k#dy;=R;E=A{x=zpvoE3vP{=qGqYG-osxDYn=$Dxx@C zu1WFBsA4M|?{RySONZ=tT7?_J=mE!&tx{hg^uKuKr9}DyC%dCp7 zY*XIYqSi!vdu6X*gq6{^J_LMl@wCV=fbK0r?h&AK_Mc-r%CJZj#j z=nLf=MWJa=gt(D5HHNtd>$3~rhPjASUB_I*d zc&c5cQMjHp^qG!0DeKP!y&j5`?FI#mT$1)0CAvWrIb*~7CdS^s?%Cak1h@cb>Pp*A~4Vn^878CZ_g(2XC;ZBGRa9gl~{UVsDD0xniV6uTx8j!+!* z1PSurl91)h#glMh*QjO4wBMOgovkEDW^2?UyA6E05;^RduVglaH1?b_qjM|uvtCCr z%YeT+=NxdO)1ZKj@crFX38;9qwy|2!bufcngrofMMVs2=~8TNl2ibE>9h)YRb4cPbQh zUBF??K(4H<9{cbtn}#<8SILvk=-PW2|6sND?f}}<)<^DfOZLrR1s~Z;TD}$~EuU^( zp`iq}?f^;)TPvg7w+Eeub(>s`02JXaFAo?z4Jrcd09rV(BaM0gwvuP=PzqxmV;`+w2 zZBMh}E&(8DZjKeKnYEHz9cV-gGGN&Po}K@$a}bWVF4J!EyvV3Eqjd^1s$t&NrI#(EGykLYq`}HS?de(?pEqgyPt3FA^)(8Ln@vB#-tn z6Cv}&$0zlEf^+ZKhEq1HLwvRs(5q}v9{wkYU?z&EG9DR&MI8<8Da$04>ik_r> zpjxR?HudbJXp?W+1^>zU7W=Icw3ei7tJohoX%OS@sGD!>QbgA#*v^Rjjr6^;Gi3t| z{sFXEV(K^yC#TH_Q?nvS4R8h782qIzatN@j5-2?n0llGw0g2G~{M|eFpguri@De;3 zW?!qbH%aOBr`fUZl-{j0&yWvXTT*I7emfmmH3%5cChgQE)h4I8(ZZIQnK@3W19;fS}0RA0IdHCtMFOxxYOo% zOn5Q-6#K*o>YWSd#-YV>&2E^71GWUVize83$aoGNEH4%n3tKzpZb^H>dy9piJd1)i zAh_zzMbW??;4FWgBU=Xg)m(jWSFI@1V{bR_K2MolTvubkcO+Cu7IBg6E{Gd)Z;hh1 zmM5;6DR(GIrIGmgP(7ciu~}ri$i&1aViqf(6UpXg^lN@jUF23LsEAoZ6mEeTR7bjK z!r5(}MDDXV1d#l$i!xL1#t}s?3K?yTKVzM^+*VC!Ac4~mY_^{*0QnfC(aMoTY`5Bi zw)-kqtFZ}DOr%$9{I}v$-(0XKZGby>eI;A5%=T?2Xc3{CjiKiWzwE4K%y6Lm&^fsz zR^Aj*&h~YgpxhzU^y8AOMPp%B!xwGDb>N-m1X_f16!Est3<%e%yC@=tIog`zn($?Y zw5y$gaN18hu@5`m{%eGOYxE%B6~_S#9(%&IhypdpY|($Wj}%P!>s@$J8IN{@)Kg2Z ztJ5?wK2r0PAvR=I1M7u7jAhGN8b0)mpJrBMFeZHBw|#EL{Aav-vrWxFA3)LD?iM0p zrug3TLy8p$s5 zOpcsbbc4_OaFFP~2CbMisNv1ty3u`L^ zF6Dx4Usvw>Y6NFyE_tm>X%nSNSm2sZh7HCO+y(jXK{(sO*MQVr2T# zPjtQ!blxX8?1 zgdI9xtjJB(gj9rQ+*%hfG-@rYV&}IZDiJCu0;G0^N0DN%!Vz-%Bm4RBn0$sf2T)H2`k^j!^~Vw~v2Bd@<@ zWo4|F^xFL`nU2b%Q9e>68A)_aqRblXFBTfpsx{4kw~DSn-yY@@)PBg+s@K)AWx)5m z5VuVD`jz%wSvJc&s{)M`4rP8087hVK?`Da77OQ#AD0lRu%b%lNzt)gi+z->Aj*g!I zLywbSN+0q7!|>kRa-V<)Cuv}p@c(zhl1G-w-IG}I0x~Rpjh|Elw0am=PKg_hwl*k@ zeT&#QJ?BRMIDa+zu1iS}U|2BtWklXcjbFjUs%!?GjTPe?SjSIb0Y<=dFk>pv8D`2C zvqDrVEyz0Km?+&Ir?&)7wGL8N;ySZxgzt_)5&gk-sow2QcjdB_|KYCUagP*qAYBV$ z(+2Dq=BEc5*sw|gVD4qF;DQy#hPa5Ue+GQK)XuC-tDCE8PLB-Oyj$CL`D%1kBI_iP zHGrvshYPKRsq0m~f-D_I>VDse*M!GbX?~4!7vuhF4SMZ5*M=jM;7;meZA{N2sPxxZ z_BY@PuZdq9L*02uVvtkJ{O3CfcMZ4tir>bh@$=fsO>_fDAYhXQh8h-}23W+tEG0On z+axjn3>km@D)m$qWa4Zzj4M`F9in;lvoe0L^@h19RrssQ!8caL&e?t$n{3;krI$lJ z`wAdX>@Y_6Z*#=G1G-4Qc)z5tK9RPza{F<(**-w|MC?P9es^057dcN#mfL+k4SmdjelTBYokxk)!7?E z|B+*p8l2ddSXHOBHrLxljn6R1pI7qXz}E(!r4H^?h`tUkVY}Uty#{V~P#VO1$d6{W z!WIi|T*USuKg7*0$&3VRyoYyohnSHlD2}XJE8kmIc&*$oU&ly>VUUNGaJ#k;$*9v*Hg9N*OhlNg)j|3G>g1?g(!no824NFImd14aG zrt!$;@spLR+F}8*g~`4{{2=Sl>=jP-`r$8&STsI?C<6!@1#TP-A`joT7Qj}y{q!!{ zsA5biz`LP0yO>#cvw7d>>V;{&1Xzf3XNB|Na4Lc0OU3iE;Dg+du>t-Nw6aT;`q5Ae zlIb(|+UU(q)OLn)t4|h&R`xWGjn<>NplkV6NqjPM{wo&cpZDLUo;IDa3_4Igz&UoT zU$?^w#R-YBPfVy%F_s)j9=hBomQ??>&}D&NGDoDw&gQ*q8`MVpoG)J20NyP|xg}Nh zTK{HrcJAOc>D_+c(sBPfssT=s8Fu=aBiKa!|4!v^dua8SzAQ^Hd)=IZ%mf4FTjBTE z9-D>{8H}au+~r&DP4L+oMVxRa(dJ8NJdv3bW>-d%DLynXdF z)h;R=8Lb-VWTz$9Rm`PTAdf!8T&?FGN1lDU;5eP@Chu`BeI*|;k*|%sG!-}@eRhJY zKZUvN`Hpi@#^kf+b54Ksv?{pqKAtj|<~~Qts$!cN;iFJoxtD!rDFBLy9yI}2g1*?v z*QD0JLbE(kHE=m6z#~K67h2vg#|ZH}=u8>b`TB-%{sNJD=c4@4`>W-+`1*#N^T(_} zlLcaKP8`|?5@z%d!74D>Ja`6Afm$fh@Ob)Nn9~&eac|x*Sq8XegEZPu2&M*!O=dp@ zesLrDUbj}`?F?@hfPg>{V9g2&?j`!82scSYGzsec+4++M)r^Gb8!GpV>SDqS?DX-^ zA-<{4ZkN1Xnjx9jf3`cwVgedwP?u;LQPX?(b5kdPw1b&NX+r^h071v}{?rF?0jBJ$rvM1&(f;yo!zuvk`#s@i z-E@oH=A5v`vZG6{SY&F!#a9p_O|$;@BcMmLr zIxivDk^w_Fkt-kHJ5mk?z2Y#ovD>EYgB7-p7?v4ZHV>L@^)F6roBzkuSH?xvZf_4m zcZYO$Dk)upG$`F5-60{}-KBJQcSyI=DBX>8NDj=qd7l3{=lv)@Kg^DM?X|9TMFlHf zX59)rG8!?X%p3`-DVcT+aNXRNe~1f;#Z9e&S_=dlVA^mu2UuAX*%%S0v%s37Rq}8m z#GdP-YQfsT%o1_eikW79x;|YMx=69EM_wL;nbr&vK(E!UDfe)6i$UrTcOnl5uNOwSM_>|7LA1 z&kgBhp63gs9@2jQ0{7|!BK-q!7BV`WYt+|j)E1wNpHc7sytUkxgMJlfl7EKOgODo* z@3q)KHexKJC?`nL-ULfWVqvmkA_S;C)|#rNuN_IgiudXS4H3)qWDtK|?IDtx1BSHH z>S>Sq^!bD$|y(k)JDmJ{D+8oYU)W$C9eDKN+(^r*Ecc{4`3Z z|LT#6G<5)D3E`1r;#NG-bz2uV4QMCdjO^DGIeAGsd2F0>cz#KRXgMdIXqv-;xkjBY zN&_k;pmu&aYD7n-vJoPLu#4sP`@$#ND}32{W~O*O>Ff#9;~gs|UAL8wnXvYnuivjT zM-YqIXqiG@;j>En>slG=Y4wf#D$O9C)8YP%K1&pxPOSK${|?@I8F-kC1xB{8B<1U^ z7S3?L!8&=bLk}@@xWb}jdlohBRxE9dDj13HJWbKKS&gD~7y0NHT%)-TU{vwGS}xY* ziSmhT@5fY1yJpqVuUJI)Zq4BA?d|D(EC4pcfX(8;-^Z_iZn=1PLdwdR#wP500f}Rs z&m*rbU~T{qo_emYxj{MCx^3e8B-p}nxY}||rRB~E=r)1M!*`a6^nzH?gfQ$3l;K_3 zEiT%q%LOG(mc&Nfq+3&YlFGioa_S-g;M#No(R|P6Q1lX&sc2}3lB6Qucj+%e7vY8vA7&{aKz{58 z5KUqC-|4G4UzwY>>jtsfy??(iaG<5iVpOXT%EtHKMF|Nu(`;%gCNWUsZTu9Le_%`W zCwfJEbP?|hgrj83-H6~Y!SmPTb>`NQ)SUQX8WVW6b717#Qp+Bpu4~Evx|)>&wxw4p z5#tEQoh(m-J#qu_-FyXQUwnc);rM}7&Z8L1!`dI*HGH;DZ;r6@g%QQV1A}cKo@TCJ zDdgw1QD15fkC=LqDnd_!2M}JYhjVJl-)H3H|Di-_(?F|7OF>cDbblgiq0NTS?Q|sq zc<`o=&33>)8H$UG^}sdu-n!L>=sk=K$jNgPTX@tkb9Q*ec;6pU%AH)QU>J8Xb&U8) zx9WegcU*=UKFqlPEiQRH^ICrtL1qB^e~JSVL$ zXUW7fz_m=oVk7ac5kKS|NSo+W6UN^Twp@W$P+ZbW|C~XiiCk=)T5pe~eyZPOcV0^%!=!$_82pOT*Z-WULNOsagQBOCF|{6InjC$+-DV16kJ z`=ob&p=)S1vMZvcf)<>Z9rHOqznqzX&e0Qhb9G>T^aF#_@f{a;0m9RvNVDB)S0U%!1D9J}WJQ|uHmrz5Fr7%&=FRQ^TfhcrbdU3k|#(P;Um zR8JRhBRV&L(D=B94-IdCL3r60vqx={g-|fwXmvZ`6)SuinAr<)M)Cb+)eor>7F-oaY^&EvWvYAOmd4c)6YD`g|@9!?g128mS#%6Ub&Y!$7j-+ju^r zWNk=|5=uG!JoXoJL>aQFg~;!WBD&zkv5-76jvuDs@@?$iQV2c7Q)r5{W-sMlW{q0= z&ADfi@N?yw{4Vykz2{dj`J30KKm)5pS?a-t%eeCxXL_L?y6{;@d$HE3E!ad90siYy z3Lj4~@^B1KZH(?FkaPR_Ljm&dRANB=28)Pk5DHwY@ol#7LX#KY!&6y&kdfr*FUC)d z+TY6gV$u=jqQZ);j1+==yW`$iOk_fjEkqxy`8p33Dd207&M+jSK?}$5HzKD%@t~&! z8~y#Kv{o|h$fw_rI*oQ`#>+T&eg~bqBhcH=;(oKE>zG`E#9kXH-*_G>o5uXaWsCs5 zbMC);0}~%T*dnuyF)0s63hAn}YxH@R(mMRoHLV$rrGmS*u8f^biIsT&12K<; zQUMG6%U6)tD7O_q;%}N1Oz958cpM=@sJbwFlv|cDWw^@C`8j5&^>&G$Yw8!f zaxXV}xj_gj;l!lh^Hodd8M=cYST_5T+(nJyw~^-_fLj%6kF3M+*JcAg(;ZA zy|A?0sstSl1aobCB9wd5z<+(zs>Q;o?;^!$;fOiN|FpZQr-w>1GG3QF031|746=68 zFbs;JSTaG*H^SRBWO?>N)3+oI-->%n$>9Ol8B_I|u&e#a7$7}1_7-|P`FxBBlZk0) z%nb5?6O-~c(ii*hRVHopw zsUu|JOdKRW=ejD`IbnejcvD^(XY$4dWQ}NSdWw!2;5}HP2JnA@UyvOs6Rszb4l%`6D0Q=jM@P;26dufEA=hME6 zR8TlT3sAE7`||oLaMZg@JtRw1-*;5s;1%E`|XE@)0Y|Hf1@{O zfjU{J#Z&mvixQ(b{$7>CzBE!r>;Co;<14)e4)ulD ztzh3NlF(x4pW@Im60~=c+@FI0Zv~_EH6MPDdozCTJ5F&J8u98jVZO^E9Ppvm@>P32!iIlbkgxrk=X4L1TJV$ju-g@H~n0g zNlx3ysCC$o)143c0^^l{C@a@mcvJ4^l^-CziMS)({)( ze!~iYUf3vF3LUO+Qpe#=W ziP94O_9-(87=QV_EMJ!C`_}K{rOMvxZ^>+0*&BV?blaQgci_WufR@faWy(hvSzAT!aeY7cyY#O84nWC8P*lH(FFzi+? zc}DwmU}7`kB<&%N((iEFy8MZ`8Oc)pG7CgE5n&*3G31u@LskGo#hmZ=RSuoIcTcx| zs24TTWkRJucVdHs1Eb|53-_|T*4`=XZtWpIrl+gLce1WIu{vybxetgrc_YBxB|oT? z)`zsneAn8v;u@04%5#ywqhWp#kpOm6H}7Ubk4v(w`uy4<@^8Yqi;lr%OX!HdVU_Y9 z{)+=~BGf1ayrHi-LCn&`rOIS$Fd^kLRB(9>i~-fmw{Nf}R0pUc5E#iOKYYwB?~}JW zp@vfrDOOhI`^~R zg22~F_$vastJD3-P@8XzQ`)B>o9<*~ED@XC6VX%c5ppT&{NVxrx$5rcUwA-01*8@q z7?ij23NVdpxd(n-<28@j2dKTJrGoA0@_Ed5-HzcC*&IorG5m6n7p9b-u@IM1QDjAc zL~i%LV?{n-*971nI35VvoI7Cx3 zWjfX&x70B}UCq5NZ;T?Ja!DY{Efw2cXR^qfd5aGxmp6Zw_vdz+C@pPX()xB)FIHyi!j=nNvj7hWO@h{#c zCPg)PD7HW3NWodAtm_7q`dJ`u@#Zy_t4wNQ&Zy&4BXpMc{rX4&Y+J7n8~B~J-~jLR zR5o2sVPSNefa0>3Q22@$m4~6<-q<(6v(DSK0&cqJ-&vBPz7Oz$GWgUW4T_iczBe6_ z>zfm_|4;kuQtWS!c9W3n5~-=#o1r*+XYt%b#J#6_EjV2&&Bdpzsz;lJ&)4r1M3JIA z;w}lNm_0mri7|2E^qWgR;b-fYbpDMvojPtmC*PyQbxC+)m<~?_jncs0FSHUl(yn6P z5|QOTdw!zS3HHBRRL3aKpUS=Qr=`yIW>U@O$bF9${6}2)*XJOh>)g0L{7_5qH7g4! z-@~QVbq;51EIEMv{;H#~@urPo_fa+CHs2T39Rs+44b?Ct|KC6+A^;?_*OIaz!|@nW z?Xj#iTCY)m#l4;+jO(vUaV!FhD-#snLu0r9h%9uVe9FLex>=(FF9q|vO$_%%2!XPB zPITksf-g>GpRJnKx?S@1h*4WE&g*DRIH*p_(3!r6btQNgdyRy0Pjn?sW?*0=Eir&b z_xb&_52s>pm~gO@B71`o~-;09W=hU`*SDzypw`u6aw3RuDknC;id~ zsD1h02tWN@cIbXWdNhmd{)m0w{Zw>C4F?xTECs3*c^NzdKVycB9p!%f2{Ecd53cpO zE$mcqYh;u7C_%hkH}m-rnksNu+;8Bz(||;>Ug`vP!XL32Pr=65+03urd8-nz z$eG0@BggG{&Nw;tO}_V8$gN^@%QxBtQxj6_n^2y~zrm?_IwL-j#Hyz0n_N1cU@mf3B@q+1X6Y%@Ndp_pb?iVUPm=!{M^I z`Dy1#lp%zkUh+K=5?mbf|E?j-L|et6yX*67nlwm#v`4Ju48mveinX8Xom(FYH4{_Z z&rN^5F>TWhDzbz(>L7R?@D6JZ{ zU@tf$CR!pv>BoT_G$T;uJyR1K(G9sR!^a+3Dy+{}SC5(-Vl3k1 z6Af#ynCR0nvbIJ6x`9>qgY!)b`}L-y5j((BU08n|5{xBI>G5$&_XGym)dmSv2hBm6d~;8$!Sb9gT8P#Hp3@ce#1}!0e?rZS-Yega0YZ7c>7L zlI4>9uAnkR9p%Tmd|VKe|Wa z!*sKjF8ZZqZd}s%6u$%G{0d&|{_MjA??(mlVy={>U#@yE_^g#qSLA#I4JtqACyirZ zO1nf^G_9ipco1~7iQtK@zHHonEVdyJcA}(XCP$+O;?*o`b^YFZdwFvVE?-VLDGnJ8hE?@iV$R^ zwq=v@w-A#t<-I2Ja;x<#IDwwvP#Xq9_%aXKWm&$SE$W`ExQ^Y+icVPgAJmC{yA#YA zcIAViUt<+L6aOH8HiFbeGou$V!UgW3pwJ(3=n)W`@`-!a+eJzV62e};Sx-s3El75z z%?WQZ+oD=t6_8Sa?5>hbw9RC2BeVz*`XTV5kcFUBhRmF%55;LR^@Ki=vqmqc6C(h= zm$g6AWdC-s0siyRQD`4^3Z~ila?8*ZW^=1;4$AvJ`k`E*UbWt?c{y$N!RvYMriA$& z42oXon3tHByckM~f~P*k&HW|xR}PBz?JWS*yf5gg7B*|u(%$e>otzaBk@5kN)gB2r z*oFDoO8fCXvIEhjyA}U3xc1R)IMQFNd$H7WTPxHV+xV|tZCh=mRDfW0?h3U?%R4jf z8aD8Pm5vpIFqXz}nIA8?U*}DR`m`kN)yRm~SA=zqof$6oo~7gsmrKmTU19nBIA-LF zGn%hWmP!)IBM4EkX59>%8EXFZOSjU21EG`plzH%=6dO`{NH*0?1E&NJ}V*MFl)IT%bI{znA`%NS_6YT%_TzMm+cRS8BnXHGji+?PIt~3Ck~mp?DGM z1gH1k9g!QhKRp4=Iw!a+j`6f(v0QbBkbL1${luD9+_v$@yvf5+Ec_8!w~a`l*YTyO zw_!8PuU(4@wCcp_SzgOcC%d++uP_gY?l*I!@|l0?-+N@&sY7dS6G z_$hJ{;xaA~Dxr(q=+P~RJVQO)2t(YK(SYgpsroz-S{PAGD8I)B2p9+VyRVBlR=-@0 z^ZDWR2pv>JfP3l*>nHDBBS2U})Cap9fWs@e*L;}&5;NJVP3?!e3KJjL*R-~ZHbt*K zg%S>^-f(XCn;)5a<;Q!?^`R%ol{)3%Enc`g&8oX-v3H5 z&+xym&w7^r`+yIL?fO-tmRD#|R*qeBUf{ZG5IGMmnaLwg4zBFn&lxgM$1gXSOo6Rp zzOF!d^2JpD?ef0&Nu(I~8a?`qO-z`We?h<$^#%h7-}1w+5|NX%mo0NQK~M9JynT1P z4?83j1&$E&9$x4FMg&jV72%eWPa`MfjdD7AAr_;|1|5yI>ZULv0ZJaEj2ktEEp@?^KYJr zdz*C%uaUlghKb|9t8vi#U3x8$aI<<^j&%|4GRdM^v>mao`us{nu8#)3EM;WK(%)}- zQc=G{*57Fu81Vm2gG|wYkVJ$a7+{H1_xY%~cmC{t2yyNFVIFD$+E1v4Tv>t^xbW77 z730J>3I4ynt)Iy8q~B6FJ|-nx&rg8o(`PIU>$*nYOno6)|g1m(ZtEx?Z2i5vr%32Iu}mnRMCyhsEXzx&qsr;)anlte{r$qM_)0CRTs&G^%;=v^L=8#>=b(Ze**jf4R3FoY2PzfKxLZ4dDb{O#v^GQyol zORK-E^LJcu>k~;YUXtDd6X1NA+QKwzl`bzsI1lw-C0v z?DtUP?WZLVqu2Ci9dKf_(9mO%4ca#jG<1th*q987yjv$sgbLXV5enzUwnoR^>Lf$K z-XQA7?@(_A-0_e59;lP}{c=?nhwV8~Th=H&ye=7=kuNDS7R~`GSnolP25d&?TaAlY zbh+x#dbUzyFhG~%54J)pE`IPh$Sa`mc}c+d8PvN$q=wzIewg44Ln$HVsQSN$K&JCa zsVw4IoGK7+DX&?JU5tPK=+E$gCO(c~mGnh+iZE=9KUoH3Xh0!bzt#kr=hRtD!q)Q3KYgp-)w9#;SqdxJ_G`W@bVim7;a^ zn4$)yl*!TveO&9BTBDQIar13%S4bFHX9QsSF~siJ0nk${pCYW52GfXE4(`Sk_z!V$ znX0-lsetp2`wQ_8>hTP@FTfIv-{AV+-9PPl|Ja%W#R){JB?wg?q)I~WVQvNaJ7>t5 z9nG+iI!3%wbT0fgcZYAXk7$weCcxi_V$(EAg0gPJHM(?Xmtzl4>Snpk+F@2ys7^75 zua4&8EE;=5&1%YNvS*nij^bpStj|Kvl`=ZXZ_meEA||T7@JQ-zai^nxPO8^}ExOE@ z4TsC9N*YK;u)r)o!Y1ZuJr#A{xdpuVZXsca6yE1ltQ_E0>O@9=qrv~)civZ|*!8)q zChA-F^NkOrKP-xgcoAgwW%xxz`@V_Cw5bN&8wa%|^g>F-P1z7)F6N7GnL$q|2TUPl za;C*IUk|O0zgE}!l=qz;lDmyxJKoQUxGA|Gr!3Yvk0YDlfp*ARp0mnFcBVnPv@KbM zt=kV!(jhh-2YZ9dU(1UH|Pgf`>BmJ7kPeXUw_jp$;?;3S~-vDzop^KrVO2a zLqTl0cJg@Su3qDck<=}T^g@Y_L4cF6#1W523h0SxC^YJ#))!MMmmiknrFtk_xDQ4< zvV3Gx_h}Ma@Zl$`-Y%SDofJ0h#y$T?;AAbFl+*79*Vo*Y23b27KHt7>Abb-VVxw@W zXAd5^qFCC?=lF@rSu8$F^D&!miFPO1;e4o%8_L#{Ec?Raq9v>CCA?e^XxuU`N>|T7*nT-gRovT z#xlB4#ZS33Uy_e_B7!~D7bmYhS@Fj-r|iC|SXhhoKB|Og%s#Q^@o6WN>|41n6E}2w zjm4u%4FjFw1^Cq*S=82YoMdVND-vr?(9;-hJQ+p|8<91;9?<{ z0LEbu`eVG=!1MOx{e@&ec8MtmcfwM_?0E$gg9e@B2Ht<{Z*$5 z>ua0=y%kPY`mPden0h!mQ7r?cnPZYY+&r|2hkP%ByZM`tE#l?QD~ z;JhIGv!k|sOKM28c{RM}xL*dJNy7bey%vL0-%7}A5-r=;k)(Q+2RC;1j}K*BXr;IT zNTp0xBVovV04EQ9o!W3GoAPx$E|j*c#dYm6WcO>X@hsi*#IxLpzIgJ+c!fmM5{p&@ zxl>`!WzN;Wabp!g`nY^ufQQdX8T3&W3B!ab2B|gpmYdLjf(N|;dKWIX`b$O5*#5L{ zBg*h@i|y>oe1{0wDA;w-Pe3p}9Mr4zU+gOb+&{EI5jU|Zk2$%$&}Z2cc2#OJ?;7GD zT8lp^(nz-afDh6McqfNZg}&xsS`Ps^3ZjIyG4ENWI9*WQo@NPo1lbEd7)s1(z z^RBk@@h(!E??xVZ?Of!0PlI=~wGINMO(Ru7Nxa!Ihe`f3KI!PhD4Fco`+AVZoNoufEgetDT+trYQuBI$%N|T z{^9|@pyj#yZO-tzs3W=Z;+v>5;qS1T_Bj}ZDH;(eP4?ZeUs9yRl%Q2VSOr!qJgp=+ z)zu?e*FA5w1Jt{kIOy)x@Thj+2A(^ZdxmCe1sD{`;XGS5*A%W9i2cC;l6fWS5ZmeICfR4eBQ10PggJ<(~^n54HD@Noc!IxHdP z)-wpm&ufRY%gN1Z_I2D4*gackAghGH>31xX(Al|?kmsZ=|0J%RJhi{Zwp3aO#|7Rd!A&epWYWqFr$ZZ=blcFZI{2i`3^p|{boZBFKV()mFmSy`a6UFX zY>Z~jnQVM&W<)A5zt++oCm$?&t80_j1gtuJxVag0I)pH0gD+*Nyn4Ypf-t6_15Ocu zJqsJ%(5qbpD#}f#AGu#?cKvd1l|9Hv4089>dD%(cLke9RSYSLM!WUn3iDj1bD!QvbZ9%kWxRzQ! zIEM){?!irws{PHqakrDCZVj*^{#ERB3pkfoOJW3;bDwq{Rd@fBk=ODw znR_1;iG&3l1=xRZQ(m~(3Cs)4+pNLW627xYsGOt6Bc6flIJ=PVzJkPC#YEc72#`5P z#DJeIab-8Xw>7GNU&!`|J2lSN7zT(fHY<<*ju%90czGc8Crj;IXpwz3%QT&=N!jn_ z*ZC{{9yN*&`=7`e)iGa3V1=s(nz)8ZiOq6@SfeLlK>qDkQ@_yPQ?K|ApHQ^jqw{r> zt?r6@O6MjLjfkT1GL`=(_s?YnqzP$5zmh|nl(2p^y*R1^U$#Apob>dido2vWKM)CI zRK|NJdL|qs+P2TFRNe48yCUMMAHmSu1LM|^Xe*uU&DX!9eZ44!TNeJ`Pnh*^W10Au z!~YokzM4oh+KhmfmlleybzP!2R|3hS)xs(3!$YM_N(jYTIazkGZRRVUDLG*%lSJm6 z8H^#ZUCDtnR$U=#_CB$DQMRg|Ft>Q=>k&dcH#||sh=GQ6UAd?U7yCM78loLk{>OOu zhKx@S;67RaFH1L+dT9;nG$IgclqC7zg+f{_NW|+J{2W)4HPAAF`APdCKiBa~*^nq% zr+?AV#zlvD?yGy5ceP1^Z)W5MvY|oK(`NLqA$5-3kv=x)a;f^UJgGxvDRRCXj5+>M zs7gIigqMT}%PVfJq9@)>#&(_N5|eL~mcwK&Dq2 zE~X$u?ECjxd38M&~Ir*9PR18n~;ooX15-0@M|Cqti5yC ze#(3G?vo%6`iheS`j)zf-m*wp8usB7_YV@UCO4{gOP}Az*|*a;I*65^nk&$xBGNq_)Ln5<*oL zox`F>qA~WQb?~N2OY$+rwyLjH4?f#cbM%Dot?nU1;ct&0HB-uYC)F7#Gb(bD6|xSO zk9uv2VqW<*pTA>P9l6$?7B-jiLzmVOt%G7>x+>&sC>Hb5Zdf2)m<~MoM`dhPJYPD= zUDfk0Yo;B;SuiQOF8n-qS=Nz^I5PZqjwa~eQ_4~j@5mXLm4J~*Lrd%RJ|q;lQrAvD5Mp1_xCvNoZ_lm3fjR8?eD9O+DU7Y zhpg3;tJ_jxQ;)eMHIfWbu<^c)LP%x^YCpn)HEp#^%VK;Iz!!oL6E>Y^L;E@lzp}=M z2lJ<;H}wxGQwJ{QHO_Gvf0JzhX6^^SM?OE742tPNNEMz_?R%>K z(o{UXcXzj^Jl~dzxx_bHEelOBeGg&yAhFrjsvmWW zpXlW&n)2>pC(*`604$>40U#6IR@c=3tbRl_OSs^5 z!9wDqR15Qe#7pV1IO3OAdlP7ds%}(xVOGplW132121kg{NN&r~&#PQuzn?XSIlB5L zM}m|QnkGh1<$6OvKRHz1YJX!eQ4n-U*h8u+<-JF&UwT@Ef1g6Bl>Ci<<2D|PGRKb^ z1rN*>CxF5@5rxvnw6quW6vlvC!HB}FA7W!l3p*Gt{Z%qYFt37H2=|0Fb02}fPStZ7 z3kb$x`PeCv+xQcHidg-(E9^OH5o{}T$zFkA-QSEY14*k9iGCwL4t#DUbL$8ShOi$K>MYSzbfbo}h>j*W5R3d5<+eX7iw2jR zWT6ZG976B6UE#Kc?>KkM`@WeP^Vo>ce>F9_mWAH%M|WR+FtvU;8R9P>!WL3az6}Xl=W}*`qaT z)Xv$V6+?u0iUXPm03L(|VB7N44ShcA-fIzoYxh^5TH`eNiiT2q_1Q7D9C7+$%2w}7_AY$9I@3~^vo4l* za7j7y$4>HxC#||p6!k!FV9U-){ClC>51O6!{Xkl@1-Qum@Le&6I#@cG%()#TURGLU zkH6D@(`t|C-uD3Bm#Y0_3B9Y7J$pXPtz); zd61Ql3iYZ7ONrG^QBo-HiEmL;w(On5B%gc`$(v8LQ;ZzvJ8>6--ML5dUhziDdo5~{ zv&R-2mn}1``&uKCn8>u&bU3ym`#hK-6M7{Iy_%$n^mk%`P=k57{xfj~@&x$3{Q?91 zdsI?1u>YEK&ZLW%pU>9Ox^4&n2c)~xp;}6-^GX~hdAzNgJsQfj{S>Ex7|fdG`Loie z9r1<^oooo<1Y||761Gj3ENkJ^RJYwpV!0O;=W51gnfc&e$$=1Q5B6r~NuT@sNFQEI z2!C(Yn2ggb&RY;Oe$tmwV(Cc=TX|pQfOy%lhv~}hPxen&(?m>{`5(VzaiPE9l|GL* zC9Lf1zNZs{XF_Us=tK;Uz&HGLI1nldeBth(V;G%C)c{$ZO`2r7O(N9VIS&DmF>26#ewq3Atwp zl{-yqyc{b;_8nyHZXrl^h8IrH)H+4WUFjBc-eCuNi;Il+YK zPemg8UILHGnC^R6wLiD5{p3>HKzaCA3m%c|0cu=SJu+YUZM-L7W9D^G2kWCFSZB4` zuqd(PhwUW1DkD`q?!izYO-dvRXyg_7a5=~jzEl``9n79nU}87C0wT+&JD2FuG%Igh zS6tXdm_~JIcVF;Q+>EY@;gmIh}h{AnsiwdXhY=zvNV)VX0s1 zgJtC)fM3w;hrK{dE2X{69M>FC(ETo#yVV*Y(n_&UEt~I)e|8RC4g^xF_~O6SOOz%F z?4;AOtY3nT{zV>iKt*x$u}*^_5tMlLpCkUENtFLX0{I@(&dOY#xxWj(sbeCAY5K(>pyja933*WX)`hfirtmt~3 z3>z2xd9nZR#t&vO5T2KLDc7{&pZxBt<>Ig7?0ecrv4wf|c2pyF zHl~!?fsF&)f-KVac@PO)Wj`w?pyoVQ6A&rlP0~CXQ*$knfyCZ*StG`(5whxfsON#8 z(=^{Gk*$IKWB9$9y#8x@TIk0<+#hFbVj5Jun#Ou;T)3jzhSVu7HQMz6R%HdRerRd) z!@yn?oVC;mF~HKXK?{3f*YBc>-Y1FPn}UjUlJsNY!jTp}{i`v|z*-mL>!Dclk;C!b zEWabopxEkd%hNqEJb@7gL*^Yahk&*(Ez+N$EC7RwLS8 zooE|E_fkKsCP)`=+rUfGaM4`0gcdl6&m!yOA6emb)YUz5Ia^Ycy5jDhw1BO?(BCp; zpFzM)!L=YKcIJ<0{bZmbV7U3B$QymBFvDGL-~z>oXT+G^>3b^jbP|&~UI27aI$t&v zh~*Fw%2{xw#?~~Z>-_1}O)7z7jTqrh5wD^ky<>{G#3xFuF`{iXf{j;p8L zeMjih#}7|z46*jSnRJd}(G{ly8!vgcgSy3?)GZu4I^G>rPuG@URb}=wFFTpS&>4o8v*g%dE+j&m6BOWlW!TppyZ=d}mc$I1xjYXNR zKS4Hv7<_Z#*w(Uq?wkg%t1vuHrIV}mNDId-{ruQ>AjutkvL%1D<-WV?mPbG(15r>9 z$-mq5#7J*~B}AUmle*jWgsy6d<`BQV&(8G@Y={LgGSzhHkFu7n3~xLjJoAuqW$3lC zC`ldd>{kxC+He1AFL;qz{o~d@0}xZZw8y~n#Prs9KT`p{6I`D> z%Q>>X+sOOt9ma$kjC#X%U8=idn)#TDL+;`W6Mq?`@0>=&JdLLe$mE?iX=oZ32?Q}`{+hm9XCrRijN(%XC2 zI=$S#-wks34gwk{_Tv?8H5>g3C^Z%@|NouLUPIp_)-GRjtER9z=C33?9vgjF`v^zN z+ub-X0G*_v5CUk8hQf~J-da$#C#nhFsWBc=?QLA91)MOuvj>3b(a+m~&h*PRF zeHfQ>{3j7b$z3?Olh;4^@2kZll{UUB!Y}`k0>26XR;hrIz5sL<;A3NKth6QKhvPn7 zQZL`woLf#MKA^%EZE1LtAJC||K8CbO-=)Ho841PlcO}e|_ z7~;i~ty5ZsqUpzkRo?JO5%<;Y$QlOIbIfm?HH>NJ;Lx)8CNPSY?kLJLu>T zZ$z?Sh_3I0SzG?BKeWH?mhEi^++_h+ZDHeXyF=O7xY6^#&vdmMS~mD)++pJ{pwig@ z-h@Awibmg0Bn$l(&NWAyAL-z~hk!k-5#kcQjhYbz!;5RewN8~? z(22sR8L-qiWb_z&GW%j@UBVxMQJI6fl*{6A{U#wLPBDzD7)+PS zQG}wzRpT$RT&ZYDnJTv{h}MCCY2bD)0H|p>gb^E4BwC(i!k_8I9GVdS0R_ltOq~E! zrcFHN{$=~qYhuS~Ys>PI>Ak1;hi*pDP1ECB*UnR#k zVGDUf<6BpXM+-0h*3T%00&V`|Jz*p^PTs!3;xrY{KQ{{uMPhG-Zi#IVtsG#cIDhcJ z{yE}w#P^zLds$Bfrb@O)OMKdzJBoSghp%N?B|klmn+O8Q_pd!yC0W%Pg4`$C{0AGNMd*D&-o4kN5 z45x1aulc{AQWOmEd$eVfCW5(HwQr5o_JU*dPKJ%_wbER&zT3a;VwYy z1Nyi1NyA!yE83H$Jhq+PAkW{X^|L!Mj+)>dI=KPq`}{MMfAHU{>Xir^HwD3?aD-%4 zU#D)NZ)5Oy5fv6qgHlFrj5R%P1lp3{=2*FF?_3szi3EV8@p0`%|X*!g{RuyrH30n;$D_~ASVq&H#UTSnYE4gFb^jRWc{307E?aV0T-~v+yq*pBo;`Ru zH+P{k!mvAtoqorM&d1z37)qM-W%;SlBp@c?{@&A9tm4?pl5Z(!McK(rlzvwyeWGWx z>N5d3v#1=)^V2+20epI*qaFpx-T!$H2$nnxATe{r(Z*`rnV_d|!QEhNlPK@TO#({yT=zkat z+2^I6H}`oOC(m*~%&|052oV0JYVXnMJBUBaD-?Xb_XS+FVZn6>9d2j7ecj!=ggJ3H zd`bJT$s(w{AX@NC?Zw>yYG=2IGr>!Vc{kUu0nb42?kh>e;2*Wkoh>B4i>ih@!nk$x zA2FYiYrMG@#F=#P&&;nk(M;QxmOoc^`z00{_sLPivI#0`UBvAy`0Png9>uYWM<(n6 zP3fLZE4;T(3SViiaogYF_0kl;kEr&EkvNS6WV94@iV8EgRpDeCj@hfuv^TcjAeo<(uOr=?s_pkY z3JrWOG4Amj(&;_i6VU|Q*`TQ(9W#wr_V#Qus%Q%;I}FijylC>b1i9F?zVcV;1DG=x zgS1B70a{0GW%~0UvhRe-ZH`4fHUtfTj29n7e_}zm9pj{GL%{gx-?oO)Xh92NxGofQ zyY7Z(=;2`y4rSi@l^+k4L3Qaz@bg^P204Z3C`olUxrelKdWQ_Rj7P{)m98k{?4JE__Deou=r)BIgu27if( z7P-UbL!JcSpSBP^l$#>2HK!BiId3q6mWse%>j&+6X~Nb~jfNah=IFq3*f70s|6!3+ zj-0ody44&2@EAmSF7<8Jkb2!!cs(RdW5;TZanc{DO@nx;yJZ*;pbThVT7oR; zg};S4XM|&uw#|=r>HyZvFq>y}1g$qqHiqXfey7$_M{$~xxKSW%*drhe3_yc>wtsCQ zb$bqTL0W(bG9>Sqxnqf+uhhw&atAs+v{KuMknJ#vdqI5cI zgpIhn=fO#8GTG&$>~$z__yraebF$1p^}L$Iq^0@qMo*a4zq<`eDsnw&3NSiam-?}L z64c7BpbMKEU7iF~3)0jZI8FMoNQJ7QCYdqSIYyL(92%{bD#(Wn5)?wuD_0t_3FZpi<8eV zAYDM=M5{qT_4uz!xwm)-c=y*KGhDzbbe|KaV=}!*&CbzT^+$zBDt{e+pWX02$Rn^# z!fX7K5zEwAMRvBCs7qoM1I168SAvtcxr#%2@U$73CF(xq6Q+vo7Gj4kE1Ld$z3YbM zr8@$Gba*Zoy*X_6)vnA^jdQ(M6t-F-Gvd=2RXl*aa%*wYbHb$>4nzSz#i$!FaMKX* zFK0n##n*(K;$cg5#ZO%0X&%Me^y0iv)*7`M{0zyk*uo63e3$c36h_ng|0w(FsHnQI z-=RYqNh#@)?i^r9=`IOrknTj*H-4>+F}u9Oh-?V;>Y9r&jt*~l&~+qbAmrG zN>MnmV=bbUJ%^jX#;1pvqJdHiyXS0|SYAuC+7G<3Z^=(6Uw{pc)#LxSdV=tc&q3z& zU2m(RaS48Dv!+qS@AI$kQ@{Ns(mWeHZBr92A17w(FS$M~$5H}r34e{vl? z&iJhs>tYEfSy>;gwwHNbT+jPs22@mQKE`KP1n~=r8&MjRMG(lDKhQTnE6U!jVB7OPBiQy8y|I?=Cr2=l9O~_ z%f2g#I

Dnv^=z!iyf(Hn_pe?)x})uC*)7{`S5GVwqjPk@0kgicF1cE?2%jhc{vU zInfz@dAT5bJHnvs@W>v*Uaf>0j`yt%{Ow)hZ;>bDge;h9xsP_0Nk0*Rz5Az$tUl}C zI#)z3lU{J*JO3|+P?w#7ApL!T2_KUg%{&QU67EZu+|}U&0qivQlQjmj)9 zGMbL1qt5j26sQRdqM9AqLaYA@T8j_4kIENqESDQ%Q?EyNT`cEftZ4rr^SBpqyHlLZ zm%fF3N1)wmM;_F~o)VNsUiy}c5Y6GNHhALc<@EJM0>oT~_(QN(E(jd{D)3*SM|FNhd>MeLk=NOPER<|df|U-0Y;dv zsq@n^DE|q4+Dv7U1Wh>(+Y0spnE(s5y7e!Fqt6|#Z#rbFQ}92B{9;kkr?!D{{o z`v}(>K9lFXNtW?mdc``)nDde*VD8?5?iYz}Zf&TCTUdzEx%x ztz}tWUTyoH9TS(n9hg#-jv<=~Uq~tY=C^x;}O#pQ_+bZG+WVho}rg|TZ5((I_(hqPDRRYu9iQDpGXC$2kF7(d8h!S`)C-j(`joO(`pE6Q`n@x7IQ zL`uGU-7;m@hoQ)=U61KIZ>s8`Q^x_4HWd-5`i8<-w#SlB>`9VS{yXYk+C}U zZ8dGD6pUgF0z=H!{|GE-nY9xk7+epEZYF8E3s98bm58@^c$n!ar>`Pa!42 z2}f@6&bLA77;k@!+w4_vQEN*5a0aP!XK?*Hbf6AeY*{3-$Z(z$HywXcOEE^p!cJ^+ zZ+X_9CuM7WSBNaNNr*=0bQcjdLlBw6`c@*3&bc_dLN(9R>F7@`2SpP1K79mT9nyWN zY5bM%^>px+swoH3ZUmwkf8A&01Krt2HH&}9GFwX{@#%I-iJ-j79nQEP6dznOJ8#aT zN~MQyCGgT6~MN$Ss8ld&Bmns8a6p9lFj8Ul3ahu0r2MdbDkW)Ea8{62iJ9RZG7SqO*JCn`N) zCK4ioAnLW$L>>;(tc-NWqU>j)TsUI(;dFbd>)WWHAL$P+mCYxTp1U!o7&m672*|Y$ zF_5+bkn-u<5hzh!xE+8YQfqCZU^M+<)$`70E4 z)CF1Us4WA_7a}e|U}|LGKPZfSR1&_P*_n}CNKswLD2VR?1%%7ZIU^zcDPh#m9)J47 zhe3z*!jAD&B7=eP)mfwn1J>p8^x80AK}bosTxsq;q4|K#@xT{lSa@cl@D;y4CPLin zm+%5i3h1NMjhv+7F)JP3ppGoY-d#(4~!Kaz$H4q<*% zTsij!zW!fkxt-F%n5qgJh$1(BWMvX`QRP9V^UgiN`;i#0?>i$r zK|$2E@P%hIQ}|IE!~!xlm6jhS2TrO~fy&7E%f&cjmmiJ?F{1G@x9e+J<=LFE{+=s7 z6UdWGqiO96n)Y7&I$|;@8A=7`dqr(qxt04>Uxdb2^fbb7SY_Igwyg?Gi`@*tQi>fH z>9oF9)@3jziDro~DkbNv)JY2U_OiQz%y2wu;h{S_k_`O7EHNee$R;t)5QtiXKfTa+Nn1+4AQOdWoHQ}wDk2O+X@DwRjyR728nKSdTDu?O^l?Z)Y~$4LwHowr->c~0%LSkh9a(=eXjQw z>rw0yRm2a4K_Od@Luh{S-Jp=Zp^lR1F()r>F$-kjIAM%tEvzXfdJe8z&2|>Dn7rZL zx`|Y7o4}plUj8$B+%tMDW^5GI%usp6ktxWht;xB?E-M9SJQ<=&OJy@uDVi^PC`J-W zApcdAI}#p}dDX9KMH(iObp#mf&Y%jiLRv=H0;Gcd-MVQT)rnn_COS$u6)XzbeF}=_ z<-XNHkLtHios!04qlj%6gXQNZkbjU?VmrQ`D=F7m~;^G>GQT__10A(pP3modjuE6S$!ht9mQC7O?f88Reu9tkULYN=Vik&S^fg_O#HDthNaoFe7Co>zdq9tA#h*rQ3*))lcq5n}nq9?XJO^ zo_y{nk1*?RS`cdG)=a6fMn!G0#%<;T$>^}Ms}%ksf3MqE8neTl62pKLHMB1=&P6WM zad5D|o8|vfPGI0++-i*(u!;&wB(#I2&5b`B4TKpq9Zs6Ew z%^{~ISTN-0JVE4VEq~;KS8s@vJ$)$l&ZFAbB~HFb!GFh)vPJ@O{TYBl>P3j{@#!qr zDcsHFDcbWN`LtjUN6)K3VuLmDM&YBsaMMx~>J9`rpYQ8KO}`Ddd260gQkx$oIwSO# zo^am4m>wiXx(NQUd`ni<=m+fA`T(K^@tFF<4UB##-sI;X(gKAKUKof7>0i{n0+46I z0<^45fgs`USKFf=Edzmo#+Iju6g#m11*`-boTU%=_t8;x6H+p#VOSJkEZlbyfhART`Ai5j zGVvj)2i7?y1!jmGbWsC(%h@a&@I4P~_Xa*qAM?})`GRjHQ@Yi6b`5B9wlHddPb})U1-u2*)){ z&=^)3pvbg0Zr#c~8ql#g3mF2s*L!<7(UqTNbhoC4j4lCCxRf849Jq|!2xvdBUy_R@ zn~6kR&<=H}JtCboXkmPw2h($t;32^9us0KFa%TeX;+i0QEK)?#oPAQt+GE;7L^T;9 z%Pn#~)u6+9v%kv;hX^y=thX5*dR;i(5EC(U^P>(?6)7QNBbk@b9uNG1WvIBa#Ol+p zFFG0rbNg`J{RcccBmhU(mitQN?V3Cp@z3v zx{J1p7TAR)HoPXtEu*Bdqw=hFU0uGSkp4wFhQqsxbkv4-wY2J^#boi4JUo(IV&2RP z!gV`G<#_uhPXk%bfo8w4Nhu!e^DUR302Zi^2Bm}1P)|)wE&AbCX{Ylsk`bGcBN_nO z+SPr=F*Sd{?007yt|!lBh&NlS@_uk!TI?kp{|n9{>{_%P5{6woFN|l%|4@{okByb6 z<9!Vh9*EUYL?Azi?n>dlo1LctP74-^)PV_Mwy{`GNQ>dV)%Yjr$)cqktBb8H%3@lH zw)f{JXciH%mb&}+ya|6?kB>A!SYxrcFcj_yX8;OVz%9D(Dk!FV+e^>;ghOO*V~F+o zuqZ}Cf?**{x7asYpG}fx2Lld`y`vgQ?RMEPBti&Ng@If|Mrj}%#|rs=+M&?3(vm@d zjhKZ4Dp(#2(E2W!0QYsIOAf9x-Zm&XpD%X}?sTHm3J23?sIU$I%f-z-2EP_ii$vkeSbg(T9)S@Xx*GAnu6`QqPsGWP1g}pm*MZehrA%=A&J>Xh z1Ub^L(i0`c}hMgbGR~2{^h=Hc!Z@DACw`VIuT6P z{wR>=$nNMC@T+3x@sDR5ePnT2nT_NI1slcX(g2KK#Am?b`C0F8IdjA!FT=+I_%Ph9 z?;vKZEF*WnLs^9U9oP<=`#L}>t!4zUAzPlXrOzP#MWxdfg?dUVLB2t5e63SB<%Yuj zJilu88>{vN!9Av1sn7g192A9WA*x6}QHExJm}XM}n+SY;nP$QQ%_>1#J!@S(gdnWA zZ5KpJ!tXy6G1{p|0Eg~k)H|2*VeDs~$9?RJTmU^|@4M%$;Vb4=>&FV~0&shV5hd>F zLZUzIh3@k^E8hL@uC{YF8n&iKa>_TvGNp1-CH4|G&4_kMt;VyZ8V$;-0aI0zFMn2b zs{~D9ppa1AeJ>>Ol}BZ7-A8-k@x!)W_nU2um9aDl*c?!B+5~xTcUm6}tx- z4mI`o^I&&@$-IT&1f6?Y&M_K=lJzR$avSpzwK4=)HiM(=_OAlR{gx58MTT-*^NfMN z3YDdFV^5fH7{2Gyw=6C$600R`E5haCbVGTsfR0d-O&4{aYxI$>7S*5YJ9)=+oa=?# zgHON~7O%#U6cN`q8PeVY5r}reX&%eFQr_XQ2D$7He7eYT6cj}myqc@_2a#K@i}U-t zmzMvssAU~mS3a9KJL7`P;Hp7cF>o(9{m_oK1IEQfHM!V`rGI$7LVh|r@J0Qz6HYx( zc2l#{)Rp&2Du3zM)frD+ox`XwYgchGr^5B}sD;-ci*fp^{U8+~`*>9v4jLRrv}{j= z)!;QFQfwtf2R>en+!w;i=l{w0eN@QkNlz1{BZ@@%t&%A}D=G2R!rOAPE!s}}Sothj zwXY~cxWS2Io%_L{tDr#r#GoIS$b}p)^N2L}SqK!S`S4bluph~dem?8Bd^Hs<=p!p) ztZIY_K+jS3!lUrDsuXQbrAhctT<{au`+ay`RgNkVh2UQwj!AQ3Y<WIb#qsqxYCgZOEd_IZ_ioHr3!L))oWS{dpaB|d;^J(VcD<^KwWD9r zKY{yQkLO>D`8@pkY)XbznDIMnuSs|O6RAw&Tjhsz_^`1Zy$&DG54Cw%b|4lpC`WKm z6CrCi{-l`Q;vAIj>NdGyv%ea8kZL{o-%+ZNn1)SMsW3zf1r1l2KW3>=ylpT z{2{tufl!LxkGqVSepqkBuG4C$c*BrINKr=|?ecPRw zP3MR2)5q}e8PgC-(zCEzDlC4GzXv(&Ivk(h-0gb(;W75ezb?zSnyXrZgx{$|78-mg z8A~4iMD8+t`pUq@FIo<#HBv?Z=d!dl+eu>nrfoUq0b>8 zH#fplKgM-bJ>*GYP1#zA6#9Ii<9$&SS&^A_l4>3HJ;tz@jasPlRrcpBD?^BiDmE;B zQrRUsmzWL$B$A4vBST2spHGBjD_?;>XE7)JG9H*4sy8`;ZfQK+E*lTbLak&8rf8Do zOQMv+E?k7a0q@Ll|LLK8_Xh=7Iq60IMNjy(D)Up)?7X;)6p~C9ZMrp)4y!8SE>3(I zb}5YSPQWMSf+y1}9$$ZcQiEco=ix$w>jsk%3s}(=@}k~#TPv%QPyx_j=llPxC3sNm zHlLJ~yD?imQE$#MA*!t;MH_j9p^iyi1X*~H^RRFAMUJk-dNK^B4Mbhue`zJ`(tNh( z5Q|gQ`|SbItq;A)%T*@m8g3yvKR(cu-(&{hfo}pu-x!!cat&k(D>+deOiVCNPMb9s z#O<>n8YAVG@vvIh&vg+e2M5+wrJRd!-L(d&&kO#QsM;VnP%bK7QV=1sT^HB^4l(#0 z``g8!CN6=JU+KS&h<*(rLn=~U$rDVYARFfH!Qr&@fGfTA0^?)qXfo$K9MvA1dQWaI zpIUj4K##&`8QMM^XHrt)HCs0I0l8GH&1*8#S`te=Fak|hoblAqK0SkMlBxlRf-Z>* z!p?~$li^XWlR_9n4Wn2zqM>JaD%&QhGiq}Qqk|6}7j}{qJC+Z@bD--UW?S(3AKO?n z6U1cfe+YR<(APpV z+tGe<9fSDVbMR$$j;S+auMa$}FYTt1N-?Q4wLq$P*s7U|&mSxe)975&S_B=qnGlB; zU=QTPn};J7kKM>YbO90Re+L7)p!}Pd-Sk#!VsK=S*bhUPstS*}q3AC&&r6snapP4p zJmg0w!6MQC8qlZ{+gulaTvb*i>eWGnHO^}T_mf5hn5fpBXiIwcr_QO^sc-=-f(AG{ z|E*c(*I2)Tk?kv32XMxPni`dnhX<@MEfWyrP^UDNAw+Kb_Pv{KQuvATvuf87O965a zdst-H=G-Fu=&&ec+eA$X>qUk{r0BygcZo#j)@UmDAmUa1dnY~kr@9%~EaQD6$P}&p z>lIkAj!gL`+fZ@&^Kyx^*rltNpwmudk4KdT5*&DEIQU|Y1fyvF@LC|a+ocA2WqIIi6q zph0Exo?l!k|IT8fm(F}CsJ zHZ8R}aR&8|Kq#Rqua-jRr-RFu83~yOeESQpGDB9DrdLw>GaaL-y?b;N5ePH{Q>BRh zIs+#l@d;bG;7#c*Rxyhl%_}_v?|yvFflBSoOS7tHeV$D_SGHqI#&v@mtN4R2b*3YK zM&{0r`Qv(Tm`3!-Vj|9Q0cms@UV3QAEhg%1|egJ0P9?jkuCi7NI4Vb5n(mY_e$rp=KM z2$!phZ2zwgitqMBoy4(jcwpfGrLhf(68KafYR7!GsVaH2oPm$`fA+;L?D=PhwWkjj zcLDRF#z1rqHSoceCv9xst@T(SX?yxZEf%2RX^GKn`vX@W0r=p^-8*xbEAOjRQQhve zeZG)Rd67`B{I!b>0u3(T3(lFPN_0`vb3TfDNrH#+=1Bqf6BR=B6m~D@YCq%bi*Z2SXVCs|Bm~!fDxg$uskIbn zmd5J4f-#jV8Q=0Z)gg196PfRUQtG`-Y8R@l2u0s97u?g8<%HkJ? zE_-9ZVnYT0G_gE=#l0bEA5o>d!B?eUWZwp5uiA#u7av*?Br%U5bAl)sy)y!VmsY5$ z22z{dd>TiCbx8=Whr3ok=!|2y3}V!)*P&H)ULs9dxf zV@beXf}-u;QRQ0=`l&#HiAaWq_o(28>Cl{NehrVF^BUd6G-Z$rOz5yLr2Hh1w#}yysln=vD#ZVpfwxDe~u+}i|_93cE z@+}EZksAE8w;UPZ6Ty*?dJ^dgc7(~41LIAmHB|;ps&036!>#C{)^?{0W2XT zIT8l+=WSm;$jxOnkSaJPZ3S-{p$7@gs%Y#G;F_WEV zI>a{t%w`jXhv?%CF-&zQ2Qa=JaF2ceWZKD;$KT0hakFU(E(ZGd7wr49Z5Q`9+dB?{Lf0vl|$D881-`D9| znt%7>IR+VK#OIABDiuz}P=uER_tyvCk9JX>3sIP_zT58|0Di(KE$btGU+5gr8&`3f>&-q;7gu_`CP6Q?;$zht4;FtkY+xHD67RimLXNWpO58_K( zk<*shY(!UWtPq~|wN7CLYRK{ggQ?EAuH4u z8b0kGf9UvYDEjxmz!dYmM2a)g4>nlwEhW)#dsaE} zh`Rfse1n!xJpE%`i|3`#=~41M=Wpq8pXi6Pny6B-`1BON{6*Ysw6nwji44?yeJc8W zQKCnbsYZ@9JuzA!-1MXOW%b`@y)Hq+G&{EZ3?TU>Hl~lww)-_df7mE=iD%y3O%F!_NYEf>UW%RJ5qiUDZG9DPy(oU(4yG0we6 z%83PbGb3$R=o|(MZOVhj%;ArT+7EQRiR2dJ#*Iyf4kYB@7V&~c@xGDR2gzXo^GK{* z->qribOUn3Q&*(FMWlEWoBaR=KobgUABa@BwJipoj99T&~*_ad>~)_jae6@k$X%B8?+%yjzfwz&oyh6%2|UFc+v3^a1pr7LwYKO!%~5<- zsY2X)u3$mV|HMC-yGWPqu5|1d;<7Mt)KQW)>Y>lQnKekV!7SShC{#S9_&ja@J*M!! z2cWqHz%LI3Z)Lx?&ETjYRvhQUjtex`B6z7kuVF+~Fr5@N2WPXOlsfxX26h;{a%4_d z66R}CIJt(%p~HbnJz87+L)RyH0Ajkegl2N)lg51ND>cu0Z@b+i41y%)4Q&QYz}BHI zCc?Bw_sM0)9^%T$6rNjZ;r*DmM0(<=4TfvCsrOYrOQ`i>17%0f?fT;)J&*xqAZ8^t zdpf;~0ygI8eX!#~z!^5ukZlV`Uq?)$QLRcMPnd=7pw-d<9_>ktodzb106&Ku-Zk>7 zcsa`{CNTP`>jOa&N%B%&K zcAG0i;8?D0eQxDv;Z?uja~?XieE@}cHn)GWOt=ywHS5vlEl|+a9?dZRoJ2a;&(V`N~P1*W!@PXhT|n~A{i%cU5 za6@+|r_J5-J|7>p4^;mGDyVgrE%!o=-tod>C03{vY30qCz3g{FG@;aox~$={Fu-7H z0AL1-w#`@H=(b~|b5A#+`(gmf3^?!c{@UCn=C zQ;K3l4F5^|_HOJ1Hg5ZjmrNrB>>-+a{=TkN4CLnaJUMwoyzHj`%jjU_QSlw1Z-&Pr zDnyveC#SL?CQ$w~rf>Rv#GA}}jKFV}96oA{3-9A& zm9VfvQA-WL9XnLIF>M7wbz$L|O2(;gl7}_pAKUgbNFRZMk(^+xthb)gWCQ^>@eTCZ zptxRO+UnwQRD6GLktCMhBCMsFjTNVfmxxl`;9$kw6AQcfcwjrfMmVk83zj11ScWZp zqKIx6?i8nowp~WTquEDZ}uv zUpmIBA3_I`6f?pCtM>p4r|5drR=l&wg54LA(z28#V z{#>>T-3*<+r=Yy#gTU}+uw|6^Ks?u!P_R?v`j9hMUTkG*zj!P$rL{L%%T9-A!Y1>| ztEY;zx8Yq{dv36*h}@-pXB6i<;ck7y73%5w?rc-*rHaC~%JO34z$A5j)cNy2TLRiP zYcOP|#lt{&JW~UZFyV55dMrhbYZ^FWndJL!)r6^Z{8u^lR}znh!*T7LP#GM`nLsik zM4eI5`f@$%Cr{c6dM>nFiZMu3*WQi5l7xpnQtY+y54(_d?9J|M%h|n#t!D4P+04lmy{01q-^VNws%XGia21ICy^1O8G0EQ$>C>{{!J5{4y}paF_ioL;)tS}*s;86=l+qvt zjLw1@KZOK)0!cEb@p4_C<1Hfj$M>m}&|&sCvTDJ|MMW^^78+6Q!F0(h2&oiV&RBrI)m@o+ z60|A}a(Fq?UG~~QlfUH0QaV9DfW-oj$xC63TSneMKi zfm7x5SK10`ZY`kb8HfAx2vUI=(`MMK6mB4y0(z~RD!p%dv32@zjv73ANFtLAvie!YGUHPF`ColnqWSw^03H#2R zwRgb{*6BP?(^KY8M^E>6cT?tEY##y#v0PaMEA>``1jjSK;p+LCyN4B1p79cDq&Et% z_8wTp^qRd%UBOGux0>|^e4%%2Tn{GYncP*yw>OKD`U0I~1QTVw4<{glFXTP_nINYO z26@Oss*qcozaQLqn#LQt!v|bEJbryb$VFh7%P;XlrS8cT(2;l`gL!TSUXAU4i3gna6nxq z1T`&VVY9Xo(a|chQjZRVVbYX*TsZy4{)E!O>{nlN!{yM>in$gfVWvydlRq8zl@k-- z&w8?Tj-wM?K147>?Sy&@OdtG9Yu@a|1$#HGLC~ASf893TqJcW-|9Vp{zw@>V>X76H zAh_Bw7du-B^AdhHHsbmY=ECp?ejwuN@f0IqdfJppypWG%dl?n_gp>7}nhJl3}?;zNXJ(TC4y~D&|R$t%?6#%>NaH#&qg7K)^g!{Yd z9tx<4;IWdW(CcIJ-@&J=m?>%Y5#iw=z_XOYIs2RG-yau8W9}Y;w&NP2eEPtC;<<_Y zcd=pQ=mZNJJLNkN8ckU zi~urUs-Fc3Be;N-VFZPJ2uZ-LgF2!XRWpY>kf`R1gUyMIOfqPT(&FK*aZHRprMdh9v&-#qZ%0vQ8#H0e}_ z)1R-Bk7qT&laWS>E3_fKyqiGh5z;&&QI_bf~2jVp~Czi({bOR%*Hs#Bw9ii{R_ zkf&jO3VroHzVt93xD1zdtZS77qPsM1b%J1Z0r)n*EjgD5g7x5UrvdXKU);iqWxnKw zcwXDXoj0C0qR;NV$%rt~W$jOSqZq8PCWMNOorn+685gLd!1HTAAGn*`c>O(kKng5+ zWcea@koEjOcH$R7G6St|foFiga;F{Fb-=J*)-ZRE2@ki_2dG7B%r_PC_-pk$r-e{R zo%suP5Jk!g4To5U1+r_V1&q))E%&8K_BMF8CF@Ze0T{Y55p0nnK#K!2H3JMWF4co5=Yf)nNmmw62LYyS2T(Bq2hOIyMVD+okb-bQYi!VHhj_}5+hFy)Cb}2u$huG}CSYU7 zPViO4+7~)tUmI`aT%w_@LP<%C1mF`E1!8kP)4^)|>GpS%!?)k-dEXf@%b|N1D#zJM-KsEL%H>&aE zbF7^?JC0U0h|bN`SWoaf)}GYhGKvxT>-Ut5NB}wHe*w6?!K@Isne{8T^Cn?RH3(IR zN?rL-Y821>c`Q)F!)1y8?^Jsilx5?(fl2B>+>OXG|9ayZe0il-wDd%zs~3(Ywy0WI z$wlSBx!E`soaowInh;mYUWI-~B@hs5waG^H_*JCMs5SzXeLXF%d>z|5%%%SCg1UOY z4|;IGe>u_*v2f1_oyQKVsY0qB3YVvdjNKIhZ zwMv;~(<#U7x@ARLxMvObwuXl@qCxYIYcX>sV{uG1JA;YInhk*2W7uC4UDdTq!q#e| zF8*Z9Y9Q<&WU%TF%j3qfQ)ob$i~1YLQ1rPV;^%T!V@;4hz1^An(m&PDtvz){TS^ja z?jFrdPw2Wic7t~}usW;#uLhod=s$@x2y9Ocm;1r|tpruv|KBabXz4;I4Z8hCa_GFu z;Q@QAw`p#N5$W77)`54&Ow~EgD7lV)Gq~6)#QscJi7-U4i(xTPndfoRbq!bxeYp?r z)!BBW<{I{4^nGI|lz4wvJ5oIId}-#$biWI?*HHxT3qv6JF@WeNIzSm0Q*yvC5WO&N z`XV-3wi15tddS}BhD*E*h@BWThZ)0|YtjKD-vONRQ$9qAJiyU`Kp-?hz?W0r47C6K z<4^2%m4yf@cD(tlc#IFU5U{|lr?Pv%(;W6AW))2Oi!38%?9-c?mqAq$B0HYej{sW( z-Y}L%^12Kz&ux0}nD8zL!h!ZgyIC9Bssme89us8=U7QdXvjXU_Gl zMsM}9FtPt9f3*McNthV0V7MtM8Px!K(@W?(W%qU>lfFon3_#w0H$N4t_gMOK#2;>c z`ku=hGk4teZNL`;i;wb3*<{DTwtRei=txL7{x{bXYx@8V)YXATEcN5RNC1r50(Z$t z<{qABc|{`D-ufo`gsS+~CAWJe0?}RicjiYnKHcc?by}D2Tsbz>=eIfCn)R5!-V?f} ztq4<60!{#g%Tr}2y0Qz_1p(hw*n)tn*p*bWqK<&Ol5D2QOJ(y&tf7uvWPqYH9qw!~ z>|-fp-=PWhxm))N!RF7qVhR6h{Pu1E=s(By;CUHbah0Tn6>320-~z~J3{T#I7+TUk zh;t6KjUN&Q&5J}a4q)lufP{ENf#1ueRivK@dDu~tJ|L%I1xujyTD=i_o3Ad2v1DnT zC9WU;egsTyC#{}nfSV*4lFQOZ{jP_d02Z6K#}SE=-D2Y~8TTcepm&`SEk@IrPycJ& z3^}XP9~#QsT4?RCJYLisC1Hi6z2#41+m5;-xI7g*U0O=YDXP>J7+~(eVgSJ_08tPK zUI+{b8~CUo01^#)L0t@h3G|8vG{*`3{Qo|p-eam0N-YbmApy2PAVax&2WQjL1qkK; zzbiK#k_P(iwf#qSKxZl_P;r{O;fP|$SAeZmRVu+u1{i|S(NF@TNh~&xLCg9WLJxX# zaGxg+T)tl~4lIV0sQHUDwygGLLYrWKHW9RO&L`4R%5r^rJ6aY*{agMBI+H|HU?wz{ zV!o8u(ze$wMf@4>DJlQ+jEmuYPqvPJWTh$Dhm1yM11)nwLpC&0KR5WC?KI{#FNxC~ zP~_nNisjY-adbxZvLZnz5%(8ZC2VhT;PHZRfF)7l%!qh1{v==CrzzS8QOipHPp?qx zji!&LkG)Fd*4!}bBP2NO9SlGcdS2*_t@bzVn+BJ0+q05;D9}MNLa$P?$lgs^HBbA$ z&Eo4Ib7U|GAp`FHkN%)cUFPtqJoh)1gh|$8it;7}?q~qnHwewfK_a)O;bZgc!Y({? z?O>piO`Mr*V7BbwqT}W0UStqw0v@mwbH70EbOi}uZ@|}iDJK7H)rCHCUbh2j42S<7>?bH~^ZR!U zbODQ?QjlPlV)C5>fZ$+{DSzBBrw(uC9I|XY+W!zl0O$pO2j_G0Jlj290C6MwwVmC6 zqvC?zaC-?VDLV79oS-ac;U9Dwf~qdqDTtiEz*_%&z@h zcUE|oo?$^v*6S741D4|lwCh}v+P1~&Um~qu8@tF*Xm|qJ*Hp}r;ky!v{c`s6GQ$6o z11bc5ay4oOSUSv&JKAEHyaV_*H@**OBuGRn;77*)!v!87xZ{2gs-js;@OHTD=F$=z z!A=JNtH}u6>QeM5t$oH=kcg$>3c6_cT;3zHy9X zXSQTsMg?aqg$g98SKk1x!{7r{HOsK8x>O3y-8~}!vr!j-UJ9X>Z91>-DR`x?D9{8Yy%PC_#VPNP*iUjYwWWr(}fkEWXNVyvpV;yeAPim9AyoU`!$k zN)WhHG;-p}3C>#NO3UZYhIQ>*=dzJo677+ZJ+k3L*8HD~?oOlGG1iT^#ulgvPBd!g zh7?Y}-YY3Tc0Ve6eHW7y8tEpQ>vVh9V)wKJr&r_X%)6!Sl{Kb!>eeGl99D(g-LCR- zpE(vlE%DRz%i`Y%O#35Uvi(W_hPQ~pPSrTax1m90ocQ3++?1u?T#4}8kj6f_8LJMpqFvnGMv=eGa ziSP3TLA>mmjCden>`bOk@;&o@7As?2amgV`^JE<5?YEg6z16?M2R#4@BX3IZS$k&d z|FHL!Uu^|lzrmpflu}%ZwonQbcPJDo1&X@{cXy`+N|EC3Zoypwr9g{&@F2w{xQD!l z=ehU8{R8foyY5*zgq4#sXJ*gV-`<&de{lbln!-BsdV`ZloxEx=luI$oUdt!I-ydph zM|RMLkN99al`>!ytk8uo*0A`qF}#EW;P}+-;A`r-wm^L1<2$rGmD3QQcH{}m7%?gC zT99T8>10axp|@>}u^pT4OoWx&rh+ZO_|?O0z|XLJ=g<>9Awo5Pm@ER8MSEfw%Pobs ze5CrK^AcyT5Ur!OgGPj^kLt-)rcE?Y1sfg9gGa+u>hiawPW-^O?X{8OQ?W*$}}b#QCH`5;{!rv0 zC4RheBp7A&InHUYBR$G$5Cm|^vUk&D^##NW?%Oj@m-h=WQ>MXM!qGO0ukTk4TlAVs z$m2|hu*LJ7=^vrL^B=YCnfJLXbnlr@E2|Q#wi{qnB+l!JUJ$DpdZ17B!$6JfD?#ke z#^nFI7NE^?Y~(`-CCqx+3sXwtvgd(bH>p%qWWA%=0!FgBs^?u?w?I~- z_=TTeXoJ`CPMuMc(?noN0^lUD@%7;PKWTq6uQ*=dDaTqsee=&3O(b?nhPa=tQluj=DyaPD7?+`k>Ds_sfweGM2cJD zfu>S%ri-qjoI7GrLorYrn}}@fcRK%;nG~{)JxxAZ>KI_OvGY%h8$OVN^E|c7am21y zgkA4$<*ue>HrJcUW}ZkpSTv+32%J(>+ZMS!<6+3Vd+2SPVDeYN5OZEsyBWh?O|b`s z>azzQLcJ#j`+d?R)5;`s(?2)6MAhFGN_M?Bx|(ZI=}GJJ@oB5?Umx`0BJ}4XLmRE@ z+KllGJ9bHd6qHoFQKphw-uugLv%aCY>z?N~GIhnuJ#j$fLLa)gKFK^29Y8pa>Fa4m zpH{|X;ITF(Fx`v*KX&>DSNu(tJlq{D?Sbaa=L9pX9ov7GGNOObm;5fx2FUnWJ_qET zSOW0m+?dH-u3$Ip?fyVQKrhOuRJ$B%P$VtF=Txt**Wp z;%2ch->nWjO-Q%N1d?k&J_oQpA03f$eFandh#1l1U2jZrkri#{CYiwM;T|8kTKl8 z&s3eByuC2n&U?Yo1KeAjC3U$9fU+h6D%{aRhuSe4e4T;aMmrh1pVj{Ur>Ur7GSyqu zG!iX*?F!>f`f#Y213CAIqCf8ipcrFtIbRYrN}ajg$6NP3iK4Vm(^7h5Pn7o%a}EF=VRI^4iF2J zhu)y)Nr0ctl&j&3bUyA!p8oi0T15C<8z|?| ziniU_cX_oqh-fFCTh_$F-R)AC$7+#~K^P@?H=U5ze*1Uas8|>-G+Qc3#XEZhh)yBT(%tLM^K$bXjWE}%E+ZqUv<_O}JkCY| zyz6PAPhzk*8*<)uGd9U%V*SO@69r20U)A34xTWJi_$*5Mp5lRuDeCmU-jT=ip8i@U zV7kq3y>OiUDGKU}cq!C)u5D?a@V0!)92IbMCA#Q-*|xc;M*tMiyiCp(AmTHu1r3^$ z6Y;a9@ATBBK0a<>eTgocz}0bk?Y?+fzQ9$Vd_? zFO!B65qA|URqzyKB9Vk&H{Sf-$)mEtC#xe_87wBN%ahm`-l|>A{$zQI@MEXx)AT-lMymr4Eco*^(HqTvFU1- z`9Wa|yP(dvOHBUZt@IzN|1pYt(ev>~RH>i;)}Ej0J@W1c-|f?#!3bVHsZ^X-mEg|RG+?xVD#$PONk{q22WZiVvhhh{&-_>tl-d()Zoct^N9d|P5#Ns`}OAL}4l zU-Dt^-JRw_3(!GZ=k%JYwFv3h{ing1m!CMiRM$WJ_ue&@6nX5$*&s|ChjxOG+^CO@k8MON)8B*av(DyKOvY9#ut!3tF49 zP&weGpq>{t6^{wj9kC5o`#q<^!d7l^?YQHgtug?*=)MEbbD5fcc3l2q8K-J2* zzUuAV)ojen>X<)RW!?EXW`@rULgB11E!Y+K+`wmdPkM^@llL@GQ zP{up%Ks$vCyWWvTud_lILPN``dl;L9!=CO5ag|Xa_p90>w^@|u9(?Q^mrXLRt7KE7 z(LkqCA(;^L56a;A+pdWT7KCiC@ zIp ziNGDh=5WE5&fk8_d}Xx4URUHl&M~Spj+Xw60;MwHir6vQiGC!SB{+ zUj20gxQK`Vabf4_LQNDh_UVf5? zeU+}_qzI&0G`)+Q<^q&7&Mu&&yMdL*I9QNdhW>4CA280eUPo9MEQW!ryq=sw0?47B zo8luj?AM1BxVKS46!amlS59dRgQ7gL!pN1psA^83u(v%#a50*ZvR}k#SIbp=D@G2o zmn|Jj5_(PxI?e1}q=u+t9oI&DbfWOp`{PYxdu*nO|MUm{EsyzWzS!GbkEyFql)25} z^pdJoKrugz+F?ilM~zxBT{nt&vOTo^d{!QZ>GFeiUr>kN)r6hRO3NMl1Fz+}aEQzX z{HuJ|e!pofc~inDXnQpc&H8S*TwP0hpxb>LC{3V3~aEHhD=hKn>hkiQ* zFPD#=y!799@Z3)o3CE_7w-$OCvDOGZb?^s-PgRBe-tin=!>=CCUgAB8pO;ENXE-GKpHzL1u9 zc3a!B?|C6ZvQqaFas*EBVl$xZM5)w|ydA>q%E}bE>O(ZGw`N0Rrio)#CQ-+afRKQP z!EBEt<~@j3g|*7<6J7jJ#mkjyGAD8`)c_!cf5H!)`v50%L=2ap-Udzm=2Ig}b-A{YmKn8HzS6 z|8y^@*Y{E3TD&Mlu?Wmk{DNe5V4s!n+rASrxH-lm=DE_mTI998-SBzb`1}jO`=dm- zEiVuSBmw@AWyIPs=`yrn{%50qZ4rL(m)UrJltU%w*5itd&7jvLTQAgWArw#M?`{)) znqJfk#>8T_g)`r831@URJBJu$FNErQK=FAL`u6=;LN%Ay%#G-<0NeRSXU=-jSir%9 zv`)_BtQhXo9AV_NC+Who5;}nCW6>is}-)zA8oOaIWE>?uXuow9+y~o!3pIkWOE` zPSjYzJVhWv?LY<$AM6N^*2D3fz%NrNxB9gO zj>W;5oIl=3Z`eC{5cph@s%AC*+^5DdXj?}$dB9hl)M3m00b4^Q9)FWewGXj09lNn9 zK09>R8{rasP{2h~p%riD!((h=c=ambOPqK~XrQ5Yb!pHSlP=zOE75Ow4?i}VxmlTy zc%iDlF>1%DUu+L;`-+Wn#q_cG@XB)4&CJ*hxECY_q(aB`PwWwAyr=U{-np zo$!2sqp6#`#%g53&Opf+8u|=TT3-C@$eWzAik4Nb>CQ0&HaS}*t1mS=y7cdy8>AEq z0#TQjxT&d3@-(2Hs((bES2H(vIl}@EUt)-vX`OxgK_JBYSxno*KuoR|!@1yfQ`Z45 ztEJ7%aF~}!RP*tkmE=@gfNi6Uf~>++Df6=#60VUkudwqAZ^6J~Sr8~@2{rDHwGRnk znNKQg6Xj6#@$38ZS%A>x;3Iu>{vh)vIR$>rVB=eTF1MT+5${@2zq=PyE>frz7J32X zl?1s8saVG$xH}RhpjHcJNUbaQB|3M59AzroOsqRQK~d*PU@2?WVy=DOZy!JxJ>Miz zKiMt48gJDBgP-mZjLb*a_Mx`DK^-{ z?CzJssHTFSPD_X0{AF;_+|O55`Fa&gT7Q0vcsAKstTf#gAReoYcXUmnUxc5OXe*&$yZiO7w3PXH41EN#y#laCO zrY)9PIQfxJ@-@jeJdF=EIjo|Uaybl?uz8Sm_oqdXl;qAZkjvkD3b%szjxri(& zBt6gd5p*%hO_J0nhrH~X^C|L0nCtZ=xVojAe^?Ui1 zX5wlfZ({1sC)az4gQ{#%Gp*Lx{!p<>Z3m`OQTq?Eu};;njs8onnVOw%E$LB_82`Qf z5M|kh?!GT_ZaX+3LSxVOex_boJL2m?CmH`5f2%LuQoxv#x*3mj-}SPxUu0xR$3#5F zz~Csy`x&4d$sivD1!odsKSXWwF~rhBE`-fle^HXl69F&4i0*5p3Okv zjs{@CJMQwC8hj8(5GJVe3w3m6pb$aNs37doZhX|Vw2qU3n9w#@`%F~OUz zEbqOg=}c-120CikWvY#N>Ck-&&EGQ7onV^Z)iHr_G<$Qj9HpPJMK7Ug?A--S>u-tLYQo6&W5V zP+B9&^^Ufxh=~{ha%wDNEl@9;|}FR`q(;UJK`9_s$Lmm z>3_0j%?4MoyW@1;(ayTxd?Ea~k$A6if+8uzQ?v-y`TIv#t;J~{d0lNt%FQ*7mNwr~ z-J`c?>K!~1Rl|xY$l3(|S=tZ>=?Z_sO}Xy)_Ot?|6}lO`&6-R6dd%wKCNtfn`;(wl zjHD6r%tBdHhfMe1POjI#_2|JUzr$y9yzFMjui3_j5W^R1XPll)wD2X{Z1-_vXrq`zTAzl+2KiT|KvE$K}k zs<&&;lYpXr^rZ?oTB&NwsuS{}ZuG~-h*u-X1?4`fer!mUY=0h7aRti!hz`n8za-nO z63gXRhY%$Zeczs1HNUBZpLe_S+V|B=SaYFy&>C%4?w40qFnv_jE=f^i9h>9q9oKueY??#WIUN8>?@A0SJL=O9pJ zEe6`Kp0c@VMLGW9q!Lr=+?`N>U!rNcJkwZ1TyW#fR+<3ryZ$k}I{8lCQ;+D)M2g*I zuJ)$XXC{H!$A@@EP-?^GLD@M)L{Nw*iXUsh-eBB|_>0wQ`JvOhAl2(J z%aHuI3C8+c{Xwti<0>TXFWy(vy2K&Xg{B9d5` zu%dTy?fv(GN{B(esLAeZC)LC>Be4P`H4G(5mbKr)Kl=2U(*+IfNC+6qEpfG-2){!Z zBrRd~S36;>QyMGW@EQz=KNTtaox>zC~a|kv!x$3hpOavNK3*fQ{2l@Bwz7FKA zv6J^0es}Cme$(GhRr~!O+?NoPdb4#cS$|)2+-heHe}{j+XJ(#0hr_Xsbd&DuHpq5> zsBMg}(1WRX%EAD12L)q+#M6fj_Wa;T?Q)i0`Q)be@ffW~D_N~3&biVrQt;Y^_5#u7 z(%LHXCfA2_t&23Me3niJG|jPV*)m0Q$A+{5M~~k<${7Z@nlasmyLBwRyghy3L)jCY zDJ-)M%f99{Zhe84FI&+Ex%Izib-oGg=J*b0Hg!&}@6T)GZtSsg=cO2&7PWJ?okB_E z1_tC+oj=$jm(Y$QES?9tw;=MVvP3SHXl#@1TEiGF_S$PixDmVKI*!9ZH}|iuIZ5v1 zCtAYJd3k40BDbkEHFx%d5S>cY{7@8v?6?}X0GYWaKgoNdHSUf-kQ>i^X}M_%PJmX+-MGj{+1Dq^0Q z0M~M603ya=)g5Zx@>2bWf#*s-8{qKMm`xi$e0M-_&roo^wW;B1QlYO8vZ!5%x?Gh8 zuV2sAIaKlTYPe}f9R@hp)GGQZ#@l-uBC3w;d7g)RJhXjq{H?9^juVmHlis2E5>ho4 ze4c2aBpG+Md~zUoMuK>=RlntiivOXmF>xIQ9d6nAZl+8IQ=-1Qgq5*W@`^%;9@&g6 zx(S{{z!FeP+O1A~pQgW2t;b-vS}=f)u=-bry9=IfKt2$14c^!_I8zMX8{8~}rHlCN zw^jpNlr^Y`jHZ|7LwxmJwknf}BJAJHmnAOLyHYV@c=S1BX_24z&o(+G+aqt$3o}nD zqT8+|*$fuW%j3m`>Cgh%o9$uP44l6I{14Wn zWMi2>L0u#hMD1A;m#Qyo)m6xlT@}3h)kFFQC!+mQIjML-x83S|SL%P0Xz=ap^xyN(UN!c6pcA8~;|W>Zc|LhU~I zU8_gzrvdOD;>oskd-*S5`!)tIk#F`p9XveB0g>9Q`I8%1RNz*|?J8>h3U5hbR}A2m^VOL4F`M&YppFu{`}!i1PN>ucQHP{$WmF-{x}Ey$9mn4uqTu#lPpAaw zM)#~hSr4ppHmI1e?&%S1=i!c!8Q;TxAdH!=##EooZR+w9-*s(CV%Z2J#&VXIS)u`o z;U+z2U0vbNcq!B$K3fVMcl|pxt0Wj?3G~b1g@q%d0QpM?ypMMKb2i|ngs##1i`TaX-B6X?OD=8Er87V z9gXW$6^}E;w_EzBTZDj-{67=4;#L3lB5AzdCPy zHscRic(k6mXj9r^LoSDgM$h(&HN8cij{4o1m3F$U`#ASR!&I%}vp#C433K-YM$IkMD_3dMPP~qI>_+Nz~?UrSHOk1f<=4+gGFjAlWS@SM3_b~ z!#B-W>NC_%Lj)lZs1JlaJ}{AjmHXm^Pfb_Z#&`c;r0HK?`1K$V=x-t@jePZJ4PrrE z9`*u_#Ti;6uCJpLXXfS&rmxF`8jLm5O`xf`3~UQ}g(!bBdsNu?jqSq3Q7o4CW+Pbb zc&&-YRw}5a=F*Wq8q#JH?T`Ak)z=@bP7|PA#wq5j&$F>9B9V!eGD{;eKisuZ2B}J1{vzaXarO{_Z(zWZ_&^ozI1ICzA+Hyw0)7hjqKmqm-LZ~Gw3j@-T(SJfm8tE?}q4a-Cn+$!(C7w9hobT z)0EyCfHENewbAncRvqbGu-!|+ROUUS-HM!-RG;G0-TjvbN)HI?^GyE6zU1~Pm(CUQ z{UH$eKw0=*K7I>I%_$eY-EmJ;^KK`btyL4I)%4I8(^lsy-kIBDF7dE;&UYaDnO#;s zLSbx~q|gj`5K}PZKBmmNZEJ79Z8CW;!?1&>Aq^G0_IAlnZk=`sfP6f5Y3))()lX?e zKgv)pYr31<#y11ksowK1OsV;ex=|`x>|+DkR^F>rB9oin3k45%cdE!qa@g^FUfn=f z5rLZ?ur6meJHAenu8U?M`&L0jv3>_=z%@i&?EM8V#tmooJ?BI`KjI~}^kCBB>5 ztrrtD1Ba>0o5$;=Juk-8fiCItQKeT&vHA;Y{r=%2w(ChYW+?jFiYHLg*4uI9W$L^} zJZZh#zgNAL{z~@7q4v$T87$_Rm8iQ5$EY3nh2*_C+&>}s@Aw|CnubbgPnEB}n75JB z6m@=i``NKMan>n2n^yI!vY69!nyASjzl95-&kB#WM@gc`Ml6(I6+$sE39$FyM~##N zm~p|a#}&CnMUPx50Q=g2GrG;#|2>9!=Gf$TCYo8j4klgPNIKqNts8ZJ`mJR|A)wRs zOq2%>zz(xcAHCPJi0b|NtXdE5R;lKv=Bi{O?wyBUg__R`^X+d36(^T$d2yZJM_dVw zLF!ow=mU_3ezznjWw+Gp;m)T~G;m#L7H@&1n2?)Zr)bM9shz)Z+B<%iW|jAs`qLu5 z9~>AP<7h$ty>;lFysz1n;bf_6=iIHt3(t1p*~v|}TQ42TlEA%6G@jh%wT#=d_o*yz zm-Xv1(-QOKN2p6H*%c>$3B}Cd;au#WU1?^`8mh*`_$yt-DVPbK48VAHLN6PPWn3|= zh$vyxqcpljS(64+w{@_A*;>WNwcbU0OK^pqGydz;?`ijwPG#O+Y1Hjb5!lZTUTG(p z%6aGs&UbwdIt=sBy;>f9wwD_NtAfEc60%ZGj_&jY{bi-w{5LxVmWu7?V+?d!CDR8b zS%zde-Y+j{Myi|HNhMP+_5bly7`~a#C2uxQ)(Ega>X!E zsY+<5xYE>N*!$hPAXmRL0#4FlT>oPxX3G)7Xm#(}IT5jBss+u5vz%9bm&U=b@)*PF z8k;+e7$iOcwz$;zvX8^DvG-nP)jg}Zy@IGv^2p%z+HS1ZCND-dSIcYb?tqSO0Rl{d zVPAQAo(g!ciy4-}>Pff(>hN6tn0V*4IFq0r=GGhXBhh*OwDow|OaJBFVB^^tn<{rj z$pMAef{kSr_maOvLaQj+5(p1O`^Hk&dspaVJvI*mDeiU+mK`)t%;jV1K4>XmZAsFl zu_V(gySP@Bh#Pgkp7_)Hu&Wg5)D!(MZpnb7h(}O9d6;nHXCamk`WIJmXa55DwpVQ6 z>K9g{y9gyVWrc*jJBpgONMO+!*O{zSGLUBoumDy*fGAM&ix?7yF?4+;NowfGMy)>{ zwxj*#p@Arw-eX-aPq5KaZoT&zJ+`wKv7SgeZ*#o_XTL_&TTI{n(sUi~_>l*!jclWx zd4DxQLLP7+^hx+yTi;&Bv4=<4Ma|=pHd-4nOttz z*`Uf>JnlXK5!4kbqaRSPLJs)9Ub6-lzE}zHXgXTSs1)?_jD}5GCIg0YZ+anSyb0o` ztN176F(|q`%0`~Y&#v@Af<$;umHM~qQQw!Xn(Q{F zzBO3cmGV}yP5abA?az2vyESMDewyjl+R4XO)5F||@jkv7Jl9Ol5>df7FCp{5in4bI?#Pwv>w<7?7r{Bt))3Nw7 z-%&v}w@h$aV~RzOXg;(0xAl>OcI)OECnicf@)a6L$8l}!$&$YcwGX=BtGpZydz)*= zsm>H+_GO)S@@~`%HHyAVHkrm>|nuW8}oy zA2KXwy84p+5+AX}$Ve=7gL!A;u{Ucs9o}}h5^vMun&ZWMk|quToSNJ7eKm=< zDTDQS5A*IUJ&vbHVsrd7m;NAiYS|?IyEAVVkd8Pys9wUr`iq0!qGs=oKAe|*c`hht z`Q8LBhyr5He_Cg+wjl`rp=rT}e~slcxEXVbl{c>Dhwnl0JoA zBE+Cd0K=Yt{mN)($K`grKKHxxT7BmnEB-LNiSl1(%+RHf%)X=kPHxMlBKF0^Q;R1v zwf#jbQ2xQa(%)|ZFjUOIC0z7oo$!+)Z9T|@YE&gB6C##ZY%e4l_DtfS+B~^!v}lY` z_f6^U;hGnhqiRpYM8nSa($n?whCR>DPU_Z&>i6hhYr^zjc zQ}0K8RI_}l5BA#mgsVL(qByJVTz2Us6Qex~JMbAmBf%XUBIonPmX3r(@#kY{6FRzu zJcQrqAiN14F|`EshYR^GZm86yx*sF`hDp|2jm}lYtEFP1O#^#xK-;wQcyo<1|P?N*NW=y-CTsYec`E3rVLk%%+V|}ywM|7Lz z0ROsTbJ0x~1ddSQnbarg>w*G!y)G!{9E zba7C`JcNE64*9UjqYR=)d!fupJ|a&+Q~>*sCZ$r2ty_@nb(VU4!W-Ku%B`kQ`xsu1 zS{iqftgGMN!r85w#Ae}Y@j*ogTu(U*yvkYtbm*R;w+PmM8j$)soA;u_OHK^ws8^PO z+a1iHJp;tJt~8Q9OnV#hVLR~Tn6YhxQUj%8`Og6dtX0JXTrBQtJ3IKioH16j=cr-Hw$COb(57?zC_?F`68#+<)fn1 z0XGo}HLW-IZaxVaX z=S~aMiirnW2`b1bLK*NXOy2PoSt5J>+`z_f%qSh!iw@hZsmK}<#Yuf6!0x`W}A zMltvV_;*O_XP$2@bzASlvu;*SmeRk)AB4kof6z-)^1N29^XK?H7{Dnl#?Ju1?1rwl zdS7Q&MMZg>By`rYIfQHou~RkpSahNR>UrrRF4YuW{OtOcm@51gN#x)-l>MOGM`=VI z!L1n^zJ8vt!nGrZrtfYU^z!~ryu)QfR)H!D%pw};6X506V9jbh{KQqij~q@uAN#7Z{8d{_Z)5wl9+H(|F`*#QA1#hud8s=G;3rDMEc5;=-MvIvnfu z;q~*CISiggk2IKSgn)-=)5NTnWaz~gDD|7ZzC9qlYtdX&(^vs7oMZJMrN9?I1C$3R zi^$&@h5pUGj=`H9rGATYrPh27J1JvX#ZO-VP8(ayj+$YHQJ2tA>*Pc-@D}h{0N_rg zp{liQJ4ytmH@2kx;8ndz{R?o&y|Rmb97qbk?O6~%N2y^N)49Crtke1LUzy&yf`)up>xFq}u9p-J>%2s6r86C5A`|C&j*%2i zkycrUKQNh^?~<50q<()+ub6!j zBbmn3$Q+dS4Jaj0-H~g2(DM+n4`mfDXS2bX-gfGR?lU?J7^2C5a`_A7v-(Q(GKGcH zC$bc;&L>$nrc&p^z*x4(>osNRzW1d-B|yIPrp@2AVd*}f-KZ+S=O6{(2EdX2-Uy)6 z#pO&JZ}ywLfn>?Bj&|;JA>jzg(f3y(ac*nXwg6k?=i^wg{N1t>Qt&z>cBy2OiDCKT zZ=P3k{P_m@SY|hJZ-@azEL3(jwg2?yaJ3^U5~oX;4kITWZ$4>61nk=E#4h|2t1;%?ucZS4`Lu_x0TlUrLiw z4Auj-SWRQp_2B&(+-ER;@S*cS7Rl8`(wpHyQ}~`DJUT1JDiSwz+8GPVI5s{U(=jua z^kAEAhXKl*vKwwM7@ku;L@u8=C zA+N9g{N_xKMsVeVN%LK?fw!|bQUCcwM$6v(9T%R}_cU)Qu_dp+ub=)=idx$TrTi9p z5-9y>BBa&QH~JlU_UZ6eqvp5s&9X;qvs(F8?xFP^1Im9I1j2GMYvVA;wNuK*J5+e` zXg}ur6k4kpmw6X4dy4SX2ia-2n(vA~)OftCd^5>|&o;9k!}nTc;fDAW4;-;p#D0H} z966DIWCSHml`AzuvfKk9LUuoGe-yr_khD)-st%cBSJMjBYx*;k*duydY0rqL=n?x?gKzjf|6IXjOq%hUBD3v)s zaH%#Wcw^#I*}14(tgvBxP%@v%kTAm!=m<XEmZE%6|1fiF&X&dQFIpT#~63TpfF% zk+_=8jEDbtrj{HfME@oSpj;_lW35W?rv(==HqM;=gus)D=LyN=Y6d!A00ib-R{l#& zea-})|5!vkpp^gB^jiYq7R1N4G|R`P;hm6MIBe3oiDHzzObqE#8v5+dcqK{nRO$NT z3zQ^i?GYXbG+MwsN3NQX$NBM1s7%pgy!QC))RG-Bs$$>-a$FG}?$Y z8zdD71QIU>3;}S6en5YCyTS#U0fB(M#va}RWB&ie|F?-CzVFzP1 z?~lF$9Ax04+Z@9$j!r<6CR(FseF!w53H-K?<~N;F1ZECJwJbL93GCHjxQj#;bD%lT zFocZ>gJZ;MNTFFHYjUp8Mf)#Y!ul}+3kokq?JsK!b^P=z7Z+fRt;=sf1 znzuhpfu2aI({hfFAO85+CRT&U0zq#2(Vl}4)5%x+2p@9z+rjJ2tq-~31+p);fOJWY zIK!GHu*p5_+DIHkiIO-PW7np7f=EFTY5Kvi0NraSC=` z3beMSi$nLZ@5Y8x*q+oW^xI$Yjm^U`0$4$y-3_|ZVe1uQWUGgN1X)ytEcaXIfx*D+C2-a%>fuH|c zCFC0SS{nzst}vcm+i|K{n9IgiokC2uFLw!ugdoc|*_ z0Y#cUrhV)4F>VfrS!~zKc#lzaq=-ZSvj1jY)MC_H92pUq-|=01@+3c07MRv>L$>ox z2T$m@ol*{BzXheBtrYcdf9`BS&q~Qg?>ML8!dX zjd5@yD>3kfIPk^@ZujM$zQF;QQ&70WFcxa!&nrd?yQY_CL{|Wp2F^|&?FRHjby+(GTs{i4laJ0XGAvKr|F@JL%vOHfmmPW_ z2F1I9&QStAYAu(R3!J{@&#cx?9f@N^8Ol0H>`Q1ZC zdG>cFtBV3Ik!P;DkhWVU?WdwZk^#}Gp>pbmx?=-@#*ha@C9uOK&E{K|D89Qx1<(cC ztrcX$q9>}?6(d3!)2}AK(5Sto6Ztegs1J$cdcPYVScKQLT$|l@3(`r$0D-ps)38_C zP)m;Z;z(r2K_3M+>L~^?$@{)(<}w?T7MtLxH)%62FtpoS9>*}jOc`XjZ&fK=Y$foe=XRJGBQ7Cp z`D`~}@_726%>z}f+VPsR7XH~lSXjuh5o}mUg;9c^7fI+}U&QokR zxkGn8b=#AXW!ygo1;TkghtqJQsN_Tbj$e0ta_o59Cz3=b`9AKU8x54-J;k4t1SAH0vz$Aiwpez5Ej};*l&8JsAubS>Y zsXjbP?xUg<HY1n)`<((X9Up{CS+3&roj{|{L4$eI%$u3Zbi>D{|K~w@lauU&_T$BMi`mHZ=#+$I1x)?gC8{+?>6;6;{uf&8) zMiz!wJ4PO*{3}~$K5{oZmh;(T;x0SnrjNe^EH*Z_M)oI`8dn--htSMZ*y8n_FxLf7 zpH+r5-fof2PK%!cCD8OsyxJ~n=>S)>oqMf3NB6Sy7t zkWJ#xpFdg*e?F8&tK|?0Y0%uQg!v(qfIis`Tt0Gs^Q?WB1&Fo)+fl+~+9R=O3|Q?pctwU&AJlZeoXYFXq6rcg}Cy zfDHHkVvXHG<5HUkA~hX2GBXcEkY|%zN@B<}NLiUX#>DT(9D1d7t>eZvGNVg{KE{2elV;JrNE`RL z+F21cv+QfrcANEe_M0fV4$)V~bbq02;;0#1BMKt%8es+k?n$QD1Bu9ROG_@uH0G5H zD8ZQ=fM?&M|2oWt?LAla*qJCS*Qn5P+TF3pKr4K4(?M+x9PWpU*C9ZMcYH>LkwcgF z{f5Y1s6XmEGmXFZU=ocma!OT~(3a+TBradhcY>S^J!$DaiNMdGv~G`kJrR~y-)Pf5 z%Yuw$ntt-$`KnS>y4g|r_6z*I^X=EB2#|uy9oppj%^%tL=ljXZjkPwL0V9{Q; zVrk8S=FOf_`?q{jO*DQs8BC&Ko6l6hrUHT&uA=YuAN{@q2^>vUKsq9f|_(>{<1 zfpFbxIo*upuvPyyf5I*5vhlS0+ec#~jpv489)+YZh#ZcFS#RiJ+C#O+NjUyk%C>l) zaT+Eu2u*k>NI~YuyufQ`DB0mM0VTvn_XXU-erKCIfCJ@Ajl_SEO!lnBpW3 z=b!kTZUQd235MRPuIzR2)h{Ws25VbprNF0~){!0uvAxbB#wov~G7!b6nyx?t4#o(h}*N&mmI)by&}GjZgQFHx^;iVZnt`| zKRmBVfel%J^E6m8R@lREf^@%{u3?F@GF_K>wWUJmsyr+~8akv{!QfKmKqW5mze(C^ z0Xw#XH`BP>P5buiSw6dQ4)YDQb*AeB>sQSSctJ(ci#TkcR8dRdD|@Iw!N-Lp#2^mL zk6eUP&H58PA+(a2fC5u!Yz9G~dObwQIugNk z!+A8APDAZ*=>mv-l7>;vRAk3VJ?-Ldk6RL3P|A{e?V^Lr911e>Gl9qOZop?K700z+ zap#USk)G{Eo4ypb0yN`$=O_lBs{%VF3upv1{k>L@7$#j3Mv7$vV$^e9_`cq9Gj5sf zchIH-Wq^`b7U9|!;_?;Y-(h1fqLRK}35 z&SyDbB9#~Kw~|jy)^%ect!m;HOROX}txVi&v(d|Y!63$Ec|mDY5egv3yvqkRLc!6X z_+F-ErrX5j{j2b)kMzGuVCEYu-C(3S3IHcj>q;dXXwUzwaWg@Y4xIKk3X~vM2Si-t zGGfoOTl}%;dWOry+11U`*as=>U5u6Q3$Fce6U$1%4tV`q91|`xgd^n`1H>$M9T!%X zfUADXUFs$QfkuXTQLUYH&@G**S-oNlB~87Gp0pJS+)B*wpjQNG&`+W|nS1%DLYJLj zg|)hk?mL@~-}fImO07+qew4acO?4F0W8{mPTK#747c9c2{jU&y3vx!Xw(Wq#XkoSORL=l# z@`v{xOyVz^SX0=srEeoICQhY)Fe_D>Dsq9Ut;0f*r-boZGRklUn3gh$e4tEPvwZ7f zc>JFd4};6t!3F>BodC~vO!5}eqP!v*g8<_o@xh*+spK9>kefhrL_L&`ZH<$c@01@V zp#GJ&eR_tMdsub}%a-qbG|1u48A0TL;t*ksGe#WxYrnAjh}bVdl$=H%cgO5`Ch0;< zs@gLs-@qMmYrlK%6+A^wc7n3~0uupOl14rU6|f;oOL-^=c*XgHiDRXf@w0EXE$;?j zmPxG)EZ*HL-W`4`oBamfU_6CKbdvljv@kQKvuC)JRIJeZj|GHDSys`h%+b{0>_RN3yse>6gi`N3sp}r&Z|Q^Z z-@;=vQflLr^;N@O;4p)5~b2dMw!+{ z*<$VTmhI}_LghwxrALyjo2QGHMt55QPjFU{N-#I0==1 zzm8j#-3*BJXOo)~LGv&8T`HXjU)1UAhQ7dq5+`( zDe>|dBFea5iGzP7khfV-3z+zJJvXk#N;~-ArQjZg{NmO2oIB*;fzt&B2^fNKr#YHa zRm8MsX;3H>C~zZSX2iqtQ+h??H*KD5Z&>x_=dj1T+hpdKa+RxM?|6DQyXWjt! zpGUF@y2lq+Q6#5q+E^lk9;F||kT?7@w!`mG5{<_B2&>b(of6otk1i9*V-7jMsGMX* zE8E&WWq`5dJZ;x`ETo5F{ZcEi1XcL3ErFb|WElZ^BNI%^QgM|u$LwVh$qWHoMcxiZ| zuU(K-4L7$YtnyoEk1!~ZJtZ|0-gfa6h6qiAc5P|sKf;lQ|E&dq`gke-$OIvcdp~Lo z!K_0*_(h^=x zQVj^#X0PfA`2EhXKPvXKt>d@&qIt$!>Nh*1*uOa8G4`a^cL)m>`rJk&+|4Yg_TlcI9qX1c)!y|=vU$;yUg0&&N||6W{IS!!2SC& z3>l-@JlYX!KFrbxK7T_k9PS~O=>q95aW#=4W+u=8B`&k~R8JX!GLv>a)8jULw|*s7 z{AGnb{M*&*cQBGI!xIlpmnH}{U_!5o02?2V58R_$ zvn1oMHBws#gbQr@7~de&A^nM1;tY`JZtY;B(iQgs49`AsO)6vGu>jHM;J2!j|be zqDMg#Zv&Z8LfvB?Vx%~DfcRFP?cZL?*sIU>mU`m!ib*@d(KtSzUmp?J9<@Gjz|JmM z{ITpgCrbNVDw{ch_N-26vJw&E{K^xlJvsYx!t~NjSATBk7+*A4#j<(^8z27@_zJCR zV`!wDl9ymWX+*wIJS}oNEf*W@KtnizLJx&)q-e-2GhwPgLu2lk@U>XkFsw19Svlgh ze#t$sBF5RQhT|~YTwbeU42>f|Nb^v(Y){BN6d#;-8GNFh4Yb%@Zq(8HpFd3rkw(#w zDQ(|Rql_7To8%7>Qtzz`j2a_K7O3rH$}f*URVhwc>a7Zl!^h$~zh(prf6;z_G3gjW zX2P%}ZjH{%448}eQ;1G1k3(ESV<6&`s+z)Jxq+x3le0O_$afzpU#~%bmVJ;Z$MEPE zy-s+BT=nP;In>87({ z=RFMQy|dYIof9=varFnY5+jW<>yrEpbiuoRlo|2l9qIf{571@I8PORL*@ zt5dPqVUvG)!j4-qy%<;t;0KKSVZT*#ZVUpi>uWK~&@5a* zi42NZIO>pwOrp+Z(CjyR`HIDE>*6f$-L=L&|NVYP|52}!bPOAsstapNGc!;ssE;~v z4}p30L%hyYQR&fo*N(*Kyjh~K$7tHjfZ%J>hc;g?KVA_tO^nc4(hok;o2h^8d~@4A zgYldg{fXJ?rhc9ecNSx6&&)E*hM0AjM&)qtmlU>mtCIUpze}}cZ%A2km_s)~W9Rw| zR^63_-%YFYyGgR!TL(qQKezK@p-EvOFxxytF6|`9Nfljp$2O@-zj=h?+W6XT{u_R9 zJIKZ5$rt^)-7n%m>IpAaTf6#<)K0(fReoQc6%E_bHs&T{ zX3sd^1$V77u;a_UmEA#WOgs79)hhXpv?rFFzij1DQ?@4WRw)zwy6|1<)iyKy4TRnQ zsY~yFhl1YNQ)(iU+1e6F*A)Q-CIf(Su#S%BK`6O~tJucbrO0l5ZSkVlWaa}y-`=0( z?I{?n4ENM2UMk3pOAmp`?R8`?Nt}%7K;=T`QY?PgCF=URneOkd<7-Z)s2G#zM=sik zB4_&k8d~?AFBSZOBKBw`p*-G?Eyd%VOT{#V0;2@;x{hFu!Z@p0gIa3t%t*V`;JxUc<8A=B8* z7%K?LWezwwv=8I#NactS1z@6~j_0V1w1029V|P;0 z#j&)twKeqe7q?6SL}lvu{qIBtXybaA{$@A;b4Yq>uKr)3gz9rOx}=1_FW?>#W?uiZ z#fd@K_Jwui>>-IX6A^a|DfD7HGOaa}OQg1$dRS#_oBWM`;tA(|qtjG}6l2z8DyDBa z*wV)7x@}SRraY2;pW&H>Z*qFXI6z(Cxyw#f~RE9 zUBxKy!gcwY%}(qAq_>&8c(!oXX$7%M>o5>uXUfM1!pe*XY82GlUiUu95UJ8Mxmpx7 zyaw5QX*|Af#rnWL*7qZy?yQuf41OeqEY7knbsJQ&!^`N)(De1P0Mm~NyO35+WI?7i z`T|tHfwjvvw3!4%lQ`B5!@Fvfrc#nT_PB9GHP;KfdWX)nv*x>(QqUY36$os{A`oEz z37*|obQ(J#eC~F_n~SiBwArHd+1T0gxb%RrDb=%%$&B(RPlO#>z6tiP&(t5RMLnOI z@EF+)>BqGa5U_nop4-w{ak3s8YP!qo&RjDX5?RNd!K*cI>qzLd$e22?8190qv4~k? z5A;r9VhK2ofTj=|QiG7p*wwvg7?<3z1%Yc}hk?vbfGWG^q_CigP zzl6dD0iCqj*Q;z&tc9)*te-b5MMrm%O=BCkRB}H`XeuC=fppbohCoF`kwgzI7w3=Y zeCE7a8Gz=bW4(r5XP1XBuygu9*^{Jo9Hw<1%>>wP;cYxz*PMEIb=u%O_fgRLXz!VL zaSOAm8aEE$X?IT&cSQhKMJw@s8bv1XcHnF4j&%#ZtIj3B%1y3060w9QY zua+=TGv{2Dmg@o!lwmOHTP2x-_*7XPtoy(a$ILSC+MNYBnfW?%U@u`#3}p++SqbvX^H z_6739MlA>W)oCeh4*d9O(Thspq(c{>j`A)2* zY`bf05^N4H6aX}kgTM#A`(G!485#ebOs@%|c;m{_&dM*X(8UmSk3;-xN)BHC&|~LN z->$En^;e{Mud8S~hL&>7+-0;xH6w?|(sCk}T+Z~lCAf#m9NGqd6JLf13Z+8c5Xtsl z6gm46fcDrB0%CIQb89Uc=GEDcKIk=9_7qL8a(wg2h2N-dAV|cJ|)^x@%>`?E# zQmXU0$Rq>fE0A$9fJkfaag4!;_%>hjWM^d$%w8-x0VPU+&((~d@Mi3!U(towdElB8 z?uff{i@E)4q$e|kd@{P~kuS#NiAJTb ze{zd}RSpEk3hcwcC|x5cq^4RfECJ&$4KADtr=vnq{G|CnXNYUQdO{(bo4IC;{TBgH zoO$2S6%~C<5e*3&0VN&4QN_XSIdO)q;U3Lb>vyB?Hw))&%fGMv16VnKHkESF9Yum4 zgpUBY2(V2e0rRds^mu1GxJB_?9?>3NdhqR&>FaABquamTpV>W$UTG%2q4%@aBz}XI4Ov+G-Iu_}hG{_~55<{1B zvXr!c9Lyj}2V~EDB1$6S%Y%yzw)7}PJc?yQ5*9#I%6S+g(^Z&A3rCRLgNbABCrz8G zj^2CsTl{aW?Iay`PFxCT{}VqSfi;k69YZ{1OJ)9YmrmI>mOI!-J&9Wx8a*RqF=eos z6)?Kab1?D|dog<9+IQ@~b@s8GGn1NWrmljM$lIBzG1Sylk*T}ezSXq;b!B7SNuJAjFXf7*o$m(m?9mC-10-@45xONavBTl}11Nj_dFE;sZgoxgWT9 zcYM*an$Cs1$gzoxt!DmwbCGyB#n-^(E0k)PyZ`Ybj`u4it1@13VOx(o#&-`(5UYZ zxU4fxWs12hft*uw?GSVxuR}Onz0uE3Fnw#%yF%wj)iX5kW=^8=|9JuERCt8sRWD-a z(uq=F(b>iHO(&8DSMh%FMxTi}C9}A(NZ`&mX&^O%khZzGi~y36H4NmgBC=5U0m;ZJ zJifZkpdy@qP~s@aXRrAza8%9b=VpoualFKGbo9^r)JXQE=EXL}(%wI+ry`(|-v;YS zb#+H zgoUy3F*#U3vQq^WNW9f#dZ!P!%5(**gkBc7tPC)&b_KPFVw1IlLCANhwbbrnz>mA~hy_v`%F-S|cS z@HAh34=pQ^#3C=MrF$eJ#tbjr8~MH%c##<^>TSml*5X#JZ}#7(K-xA}>EV@8Mt-#? zA`UdZWvq?|Fr?JR#e9qYH>(qXz=YyjE1@)v9UKVQvfmy~pUt@p29v)&|ClZJL=0&Q zWK~f!NF_>6ob>e1LF}tu!G(odmlch}KYZjGv+%;dujeIdzY|g0tcc0|<{gjAKhgBv z+k7Hm_-t2@O20-bzeqIi%FTW2Sa>P3quzx<@QWasYk#RlfFyhAMRtjMtP!!Dp`nxI z>SFPY5pA2qw*1IP^oG}AtNoGRPtMre*I+~YtOn>0GQTxP>2|}tyKju5iVlyN231?` zT>WM$=H;pCuTC_f3&R>9s*S;uYp-HBk6$z^GL`^l`(7)LJw`NVam!?9W<|aAVnTnf z_54p1HTxchELlUX5L0$ zHW#kv`B+(#@32#Mw4_bgxCcHpIyJBvoU0vmC+}_glgU-(=4m37qWZ}tQJ&e9xJv7L z`RfJ@N71z*5OEOxB%2mq!QEPl>$geQ%P$7WB^D52aJ^vadU5TIJBQD*3K-I1(T5+I zFy~^<=~6^6Df}>&*|>T)tu^sm&_cnD&NuRQ^R<{uR+SJ0^um}&X_u`)oa`I3b0HpY zh!#Qy#R0tc&91^Kt%|Bx?BwmHxp|lF*l_V#k*4Z6KVTm_;2eSGGpxtHi_!!n=an4A z2~gjItB;%OezN={>wN7lZ-Zo9#-;cd_R3~5*+nQGo0UBM7G($Ti~n zj*rtfSAJ~TA96vqZBW({6k)`9;scNVQKzCLyFIx)hAF+|Vvk0I__xw0P!+>rJD3HI z+*Ev6I7#M_YSYrKlbyeGIMuJ`1Sf9?Dhy6J_t4IGP=%L-tHMB_v)>0g14jS1T&VK) zUVbhAN;_u0TF4(?1xlCsG@3O<-I8UivGXIv8FgFyL$#Rv zc&zpM(1=Xtx%-gN^5Curlc_V{Es2)JDS-P*ktL4_T0Jxsmx58pw(dKRJTm>qB_bvI zT3AuQEv}E}5tLP|8_TAzjHj2eRr7gMn-wy3V`#e{ zGmrPBELOq8?MzkswwM|mVEDi*=>F!vXvy=|ww6>yP8UN+AO&UWpoB9x4^&P+8?=(m zvvH>BF2*T&5UD<=L-KR%$=Te|cxfppk+%}ZWBfejs}ZV8I_AHUQd(@P>CC@-kWlO4 zUO(%gJ)u`@pcNUf6tS$fTf1v)Z};2ABeGWTyIH|l1|n)n+d+>_HI&O82L1edNz#5> zy!vk)u^7)ge<|t-i2hF702vC#Ux-wabSALoj0V0Sjrc)Gf3u+|?01-$eVyW0fU_b9 z#uo-||Jw(aC7EzjKd--}zG6LXu+~rT%y*q)l1(R9aYrzxS8Oy;f-y`lxmuD1>-x+U^m0#`u#Ym6d9s>m=JgE zSZCmOGX4_i)H6{+>8Xj_6sOVwCIo>VRy5`tqZ{&hp@Ccl2$@*7uM%2(!t5cWVMB@y zDu|fQ(`Q@4nJ;>3(33k#e5K|LNgw7RhSi}UMYyqghTL3(26I)xk!2YSPRVK?p#gp)Sp0-WLDP<;1V285N=e~JR}&JZA=$~_ z{+yvEFi209!#pb!F{GRi$)$Q>!M3->V$~MPU(|x{{y<#@WrfL3oR|~3S-eA|1>49& zg-46#l!pEMn{4FYob=WkkVfi26dmy(q!ngH81k?CG)=s-27@h7v~Wb@zFk?wb+8%? zN7P0t*i%L@F7I_`u1DBQdd)4D+h6MGEkPGT_!N8vt-0s&qB*({X^2^RZ|H;DZ-T`P zXAG3@nS=p8P_-mM@^lW)Wtsz|#1I)GxeiM_u3rS8oYK<6N&lV0&yGz8U0aQr10;Cx z6lMXQ`@H?|o+GjvW{yjf>t@&FtGK9-55v{$R9Y6z=2gs0k4Dsd;vcE~N=H(?Q_D@n zM6dVn(;`%wqysB+|6~;usUB+aH1SWBHelH_{G6L70U9_q&VDVc@S}$f1zVFc4r#4m zRf1@R%z&9;H`4UUH;3jg7b;CURWsErsluQn5jWtu;C^Q0N{-w;S|GFk<3I#%&AIW; zQ#e(#dpu(d4(@84*KFKBIFrXWGutCV{!Bfa`6%2=Gl4LPb1QkXfmzM(#iJsUJq!)m z4ylR^YW`!h@>5RcJsRc2=t>azxXe{ksi!))nq3-sjE&(v=(BJ&d`~u>{{5X?Ay+~l zF9@cLf&jD%jh2=7iqKE1om$?Hp=n?rPOHGFBBSI7E6ia>Ar-jYh}xl|Pgc)TR?9Eu zMH|;y!#gJgZMIgE&k1VEx>He}RgC$9R#P{Lzp;@yIzN@<5AoUn#a0MeN{p1b!D2x6 zSVQ{0=kW}KGu5zV5Nm4|2d8zdH^FuD-!=zyKEEgn>LvaEzl9%n-tR}GYr2I5+-d>>%7^2eeT>eQ`)(Yd?JbH z=wPh>=3=Sww&yCFws0qfn<-kapOV z8Q!)G8Ij9Ze$N%^#q-@+hpX#kHOZ5)6-_pRAEwC%Z`_M7P!h=oiHCCw+U<2F z5edZx=EbLMo$RlyH}CFv0KSWxEu-370cbWlQMq0=QoBt3tCvI| zN}~9z?`)8@Z>#Af5{ex8;=fk+J5%GTu~-s=$rl2u{5~hqN3*o64MVisKyXOpvE?<5 zfjSHDBb~~p3)wg9_3;n-vb<4kt5*(6B{8GX0Csc&FXYIJN&7PY+s~|S-P$QPkKCtG zb4_FnT{@(ArUj6OX}mTwoz8)o!9q8Cw*eCLj|WK7fi4mx%ANg)B1oY#fpce~r|*Ft zCz%$-3l-nnFdxTDxWaptE)-!GA(2@>7>XjpR0b-eC`hyag9^RYTh+Qn|Eu78A#1uM z5iL}nn&6B|fTI+;B+-))m85hKy_lSwoFz~it?uTYFxZW$sa4(s&7 z3nK9@PR5q?3P^)@jEEr`={C%oxR3k&9okDzLYIzMD%zg&wyEHch!VbE;pw=-LV!4M zNiqSTk;8?1eDx&5btJOl_S(Mg$-Nin8XC=NOhgn93>bi33SIsk3wD2lEN>`u?qK%l zu->+0oTD^yztN_?7eP})=b5K5imv4Hc%yDNZfB!r=>e`|fw*XH{j9o?)j z0cZs@+g3V|Vxc{F*kR-NTBK_y%0GOL(cAOk>adIWauiZGy({V+ac#8hvgi9sCCPwv z)|^lF1uQvY2jFPdwiMQ8~f+$wJ; zd->82uzQ)B;orx_%4nw2?<#f+C+}*ZyTQRYiHyePTu1@&=g)^|^rL z(?IOzDq!XOx3&yH-=NaAIq_8zdk-#=9I^HPNg*!B&iw#wO^_C;p-WA0V+DoiK#vau{ zShl&?bSUh2HKz0OwWy%r-{*Jz54f33>6w69DEbC=dpd!?p7vqn;AEq7{7h{s=yPW9 zzf`$>PFpQ&7Z;S=W8H^Fb^PwmS3&huw zlS-rVxXJ-f`$6s}PoFBF7U)oqOpSM@<%hw!VS&c1AaWLj4K|FVLk!DZjQux3l9qkV3kfbW#*UR0t12~fIqH#c86i1t5LJBUy5qYmrW}r&J$367Kw1`kpxp-k-Wl)PdMOXn(mv7)gO$h8n?yiSQMbo)D?Nx z_0?Wm84kW=lY3P{; z?v5k7*ZtO(WQYaf9CjR%OoDgSa>d#*Fu==5o@{XVm3|Qr9MYo2~ zA^s}*C*e;v$AwiNOAhgP1A){VN1w+@7niKhuztK0RVGI4avDyqrlCcMWME*93|6!? z*lrAs=m^86QblRP`cAw>?ye_Ww+6)bt`8&GvOFbJ{w+PnxX0zV_jD#@6va`%=vG%| zOLAQ=x=iPS-F~#CbP5jvt&NxwNM-4n9qKe#o|;rTTrFD0B0~$Qf{biMV*4>a!B~sO zk>^$*VWsD4Q`QiJuD0sn{}86r?)M6xt4xmtue%1fEj4L70t%LWes~YXJ!#8FfD!V( zWrqq~blmg_gPvt_XoyX$8|vO~&GQ;f`q98PH}zesv+p-{={E~njtpr)aS0izt+|9w zDF+|6Dyiqm?@v| zzdS%-oR@2#*f%~$;0#}G@Rquq-guOaGg28vzx8$~Ui6biM^1cy+upY`!nGA*jZr8_ zlhRVnrl6lY}*)YiPhQltO-gY>UY8 z?5gg`tTi#c_L1c`WVD+%$xpJT=Jx6Ye6+isNmUJoI4MS^TSr?Vs+>(cTyn%qJz8Jj zN`jaYqMAcc0IDYo)tAGK*U)*v|H&vB{+E;$jR5?l^Bj-Myr2T{J5XA&V!R@b=Oy}Y zSd7O0E+@8P1v7$H9%)AwTOaY=5-E9+rGwScQHV+}h!P@%BtZy;V@-rbN}F_VJmQ&5?`&96_XJSj zyz3p%VCDplR!0Fgi$3cAjWlI-r~I|@0&fQ&>~pLx+_a|JR$q+vyzY3%E(Uopb- z*W=H)n~&d6sG^y`_w!2lzlDsSAh05SBiT#Pq2@|bk3V}tD3IFR+;4zlZfYY#Y6r&u z-v0)}cteE_DTwgnnmG++MK%!dSW2A*zP!RMCq>w&p^3~jb4EFaw$3VT#!C~Wj#38~ z&Tw1zag!k_^7rGy_)eh#D7=u{7xc-<9;4&1Gf_OvfxYt0~v4C|#nV zkP;rmxGe(aFLgzssvcIsVDjzR!F0=3B z@c6(B`f`zQ8U11MZ|LQc+?kKWpVrR=$@1V!KT0%-+XiYAK}Y(OAHXnZB;%pS5D4oA z{OhgF-)_m%;r{CQXS-LKSGyIZI1z$isz-GrPLEf3Q1|&SoTsaKx);ZhoO!Bfz~l5E z>-Z`2NaxgbzZw}d-hw4K6?x(vNEW!+56W_aihJA62l<6u*g()zG{zG)J&5n%NYKwC zx)fFKL{=0hsN<&Acwym-?nsVQNc_)g&f68z?7gJ zD}ketfU7zr2uoU~15>JAqgOfr#PDv@=PGPq zXxx##okGM<$VGr{Xj~oF;Uyn%NkOPPC!wq-Kh9X-M`kCy0GigQpQ76gSgr|%OIE+E4 zmH?5uJtD9w#QpQ~T#5vk@j$bLMbk!!Q{}Nb2u{C7p$tpkX(b_&e}($Us)(BAFkuwb zXt}#-b?>juzM8`acsHCjQ2*cH3%=2nXjzJT1e0Jwa~u)fAUX}|QnNqF^jXDjqXFKE zh1yp5s%euDCrqd5F`{HVkK`Uf<5O~2G=C#KkqG-_@(p5R{EKhW|A>wCe+{YGQk#Py z8qyq?9L%oECm{3$Fk}imtn zz`A(QTT*P#%7IpXMGw+`r5U{Rmh;>V0kY3pH|F_aP!ad+vDX#0_`Ji}l_$X6^d#LixZXTh4TL z&=ZVyKjw~bgK<s=I#n(=L|!z1zpbxlJaYam~5zt%eEC6mJaPBD0YtEK;b%O8d!U;otZHl0=W1 z#vC__3Eva2e1)LZQyaN+1vNh5=m`FBvZDESc>}(@X}JT`n>$K^@REmrM=2Zi`5E4= z%90i)4HvVU9ZGOWGLAh31>G!SVW0~LZ##&%t)s9>nN@ofqrbXD)T~?z14YvDN0gT4JP%d2wkp*4lxzP| z+IG}YS7%5$RpL%LkevOniSf!hQAcQ3%X#XZwAULm=j7eg() z;IUIX>A$)hPVY~GN>vto`QM!9@7NDu@}W!i2hIs?r+rW}+sa$a=o_+6 zD+bDsGZOU?-bfTKNKPXdM^;z*3C$ax4O3kmjZU24Wkjb*sEtTlWhqKIe}qa;2WU43 zuC~(9yKtORf){Ru@L$Y)?K7=nz9_HJR9;7Cc+ z3)(Ck)opO)h}CT%F)t2QD-L!j#=xg|ua)W}0R08?%N2nAV#cec#HiIMNm4NR4D)UU zUkUG=d5f$>zihT%>56&aFjcwyF5m)a`6R=GJ3Wx(sc~-|j+T$UKjHYOB2irN!5ql9 zgmq&7M0|2s@O=edzAqB8#drnsx_BHB&EwwqTA1zk10;%qAZ)6|g$--G4X5W9u4(q# z)It;qQ5s1BRuU3cmWu;=QJF}-KO?rrc{iJ(t&rX}@F{E&ly{Ux!TwC(VQ8)vMPHxs zWO*h9LAXM`jK$gG!XIPrIaJiC`6}%$-hKfZGoB?smaWsz8|AH@v4)2QgL6nwR5%a~ zGdf}c8LGg2k8O;U^z_cS>z=sl^R5_b`ugR|t-BSV>3VAs#1m>v=Wq|6oM89rXfz&Z zWnndG``0LTEg52+c*$QZgw<)GrBp#R4EQr$eb0WnZ*_c` z{j?XeFF;JG9Y#DtW!6s|hC+mpXW{5QgO!8Pr!(NfKC#TDm&#s{KjW7=GC141%C;q? zGCGuHa)LB(9NY;SJ$AbIJ98&{024M%l^x|rZv9xD&o52-(*#BSL@@CP3SwHtl$J)n z!oZPv#{L{B&yI82;IT#mxoifjiT+J`={Q$^5Qt9=wCgV3%>xh#U_Sx7w(jcv8O?;z zW~SdzPn0eG(gS4IN8T3Vrf+G>5urlxSU3<1#j0v-_daLJWXKE0ijm#wYv*KTVOZ^j z`)&A(M4&zCd2Vi~kh`lrc}E5VEtt2_8Vs{vIl$-T!3lpydRXBIPA=vOfEWg22%^qoo@vk2?re2@pwV1S(gz1!97aC zyq(v_-gSFEPd3STtj9|Z;+{&AP#T9yNK>1XXKFG`AUw6+Tv6wvHE(eFItZ-uX9El40HM0kGK{VtV!yNX}l z6Ox1P%#OPfl~bG)qsF5b)NYKPo_-~9u}bWDc+ibB3;e+O@=r;AY)4f$Z8)%GroCfC zB^@FHVp5>a%%J=`K#;Feq<5JK!>8VDy;w_^yd09fJCjZ6qlvFlx-Zog6quqj1RzFV z`?H>{{4Tq;PsPBbY~PYShX%Uxu!Q`OwUCfvv)>4LOGuK&G=vVN z@j0It2K{@H@-_h>WYkWqE)ffG1$d*Q&z$FGRw^Cl;ksWf@IOAzRH&Fd8cgh26#z)= zi{VK_pPiRtnFGh!x0~6wZEp+y#bN5{O zBOc2wzL_A+Kz17o9OpZMKoXfhm!hG8id44ZZ;AwT=cKTskTAjHRTMK{*}A z^Qn%BE+rpOgB_f&#H~bjXSedBRvrJ$LFObh))uVLcteOdCkqUDl~ zLGGeE;JUjNTU);JDe)2jccSjw9?Z8mqV_fgk_9ofbPGj3?Y_zd$HEL`djgvOE z8{4*;#*OVX-q`zWfB*O04{-LJdw9=Wa}Dg3Qpo0a6&N|$yhm$x;=(^9Zi0pinvxoW zNN)jTka_NbUjI}5#|zKi7|0YlAzii;rB<~bA_Fp{taUtjH*@Zx{SS9wYycSny}f+D zxD3FhFgKYeV}Z!!!~f($!F?eRCs6BhBt{*=0+~_blhZd*O83sj&TAbcTA53hO{|@= z!CjAEK2M!oAfGv9&x9-a?UN!5U+_Df)h40Wv2==eDtxNuvf|1{^V9f`Y~`E}@^K|l7ySOGs55aK;PMkrRg1!R59K`;fYd`a55Y&`8dgV^vUDekfyCPHi|t&14~28 z%NbC2|#9jgF7)l~5o9;^|LnRrtKx!tI-j|ia0 z2p~WUqwz@+708=Q0$@!d<;Cs07)o;YMgKGi7uz5SlWIVJ-UPq%Ua-rZ>4=(myNXuM z{V|3SP|VvdQ?4r|K7%6TJN1Ny^NX5n?vsrRS{c%Y1HODJbH73FHwdN9?l5F<@EVuN zX%~@hdu&0P&={urCwSiRb;O=2tyaeef??cTIlaq9>=QoddNB_BFSZonD&`v9833v* zyK%KH>Uc;Yt>pfgV?IS-SR_3_T<2vBKs#o-@GMdu#Le}%sOPH)^Sy=i3YbJZY7L!@ zB%IAa1z0C?#tvsNW=BX6i~{J}n8RtqRKCUy&>}NNV+}S)>7sHh zoEL6gnoj>lA@$y0(`k3XU92&T!DZ4;y@XKkh_rt3@fkPt+-!F~n#vId%(2eez&6ww zQ~$sBFSmXO>s#rjDj@uY0HgHSIvz4pQ+}n~h1@c*&X3~LPjCw!fHZY?d9W~ipNN8O zgwgYvH)e4d$vVoQm90YfbU&CC5Zl|Y_9B~IQNHAyQg2w}t z;ns<381rW_+IW6FGFqcj;(o%TF9A@9tv<6s*g5QOgH#fu?RLncOf~Zv4yP^lz9y8wN6{pRG$2#J5NVh* zySe;^<H7f)d6I4Fg4Ai2bLwXqmr+8ZSb7z)F-GQMoe~}K zVHu_5O=ywRh=K`9oGt(oVf^b{jjPDo4NIch)u%nM1{8%*42Fl|4vUFB$`lqetn6mn zO|Y4EnAF-Dh%IyZYKbWvsE{I_IRB87O5xgH$dm;dThAlqzli95x)_!Bl#%!3W%c#z zUsPTLa7Hzo{YEgkas#c&Kc*(g6fnpdV~SQ4bsoCSkTLKQ{>g|aB+u3P-90d<$q>V< zfW%-wZ^fg-UhwduE*&h6j?l7AXLZ4?dsOe)%J_k)0))=iwSx5JD5k~Eq}EyE>0L? zv5r3==(9E|e|>C)R{_t*7}t%^lt0yJ6V>+Rn15b(cprph*m!D_&K%Z_e{QSD+y~Ak zjthM;f7yuGe7oQAmm(L(pdPTU;gEQlNf4tZ534f7mXOM0uLWU8*2vq4P@lc-yFR~g zKeg15J#9&SPDj>-bWmg%DC`9pCLh%ha6SbWGDE<5^qnNhBuNU^6@uaA&3JFS){jz4#4`6B@tE>9ls6bT_`;t7bmPi{Y4!k9vZk)i_8=odIbl- zit3BNUJgOj4H`pGH5*v*BN50GK?n7h zcU!$)~QZw=j21kq*Vn0i;j{8qMR<0fNS(2 z18oy@d4AU*fDZz7A12g8P5`IT=l?uE2ULj4p`$%z2({_dg5BaK>-PSx7oR4$xbpf7 zMOjadovi1T_*my!xh>2i>X6|5m$%t;rA~|{6w_RN5X>r-m4%SHUA|Ku@TsbHfJ5UE=rG52w$NEf%SO?!?_qp z4^e-%#Wz^S5GQp~NIh1>-xMA2FS()WLYB@CyW1l2^EuJ=pjuz>?#*G#Y)?I3DbMa1 zZgB^`5@sJ#8a7fvoh4nIt^J0b3jlt{TwSN`yO6{QCz~#R4Yka#!DatKg@I;vl;fR0 z{})nj4I`~)F0sm1~_NNKr|DxC@9`|4Jxhg*Cc>L$P zl1@J%MiT7#`95e$NW`+j;pO|lnU_VyvqNL4Jned6z0^KgVomT?)dY)5r*Y5Ws{c^P zdV`NC!0DA!P>LQ2JU=kX&*f$$QW-79bv*5YIXU|}an!E<*H`REUhD>zc?(V>} zl9_#a$hrXcJ}1|g!cPM60y8|j4DFp80AL*MfgxTUMTH>^ZoR^WN;S8sP9Y;m{7P|Q(zmU=(GgM+!yaQ@57 z(ov+SS{`L!4I5+fX;nEX&xr%4RA*`bdTsES6O{p-bQ17Fs*pl6yr4Jlr28v6o2 z{C7bc(NXA7=VkozL2#uXp&)q9%R?Dmba`P=zESe*SaW%n%*-+$1*!}u;@4rdRhC=G zY%IQ1E`XTgrY(R zaLNQ`P0lbtdON+LVrAa>&GKATV(jDL$&?z4ujaoIu4ps*O(MmxC1S>=-RDpjK<<#OIqA_&y^1& zVzBdo3&m;qY}i%AdW}u3YD4B==&fZaaY9s(Ob#@n)YFuA{-hWZw>AxB($Gp)H-2ZT zwukFNs-UEYFeXM&4!7K~mOJ@NUuZrnu}mw#x4_z-4s~FURLLab?+?u#@^1wV?GJ>p z|4ql}t4_uINHuf*-J?9@++CSfg3%OFn)PEc)92!54qP3|{ASrWI|TF3bNUB*oXw=I<=9S_fR?~Z;lr>G!`dH%#9@V6OB1Xhhw-3nOWa;O)IlY{ z=YNM)Wy}>v6VCn2nH@g!b${B! zC&NC$C0WOLy0BfbMWf5}m-R1wbAXo&?yhoL8aPXqR0V6yJNIT-na)+HRVyTFzNA<| zD>)Jj*-ln$xC*%t73h?**d)qWhA%1%**+)bN8o>iK5MO;E~oIVb}QG!pO?_KGlLb- z?y3Iwg|>E=nqF?FC_PW>q(Y6pK}HsvN8#iOxG$p1V<2e65NBj^yDXyJ;T5R&w-Vjg zgiFWK3~9x^DsFk_xocMHj4B^F2$p1SbI!^7HxQV`gI|C}uHt#(>dIvWxI`exakfY3 zSDwZUF5K8i8!pl%7HRPZ|DJm)kp6{&QzbX6eVrMz@JRK&SL&!7Xf?tX= zgQ{@(#Qv8W_IufQLCi=Ql%Hz1{H)z6M7Yc$$3||&!PYQmE{4X{g z#^GDRKbEp|f+mkV&8?KhF%GL2MOBFGj!Q(xV#>Jl?0Au z0PQrpDk>-iec5l+{XHiSp>2=WU%E~<$4!VZcO-t>AK8^ z!9mSc3p3B~6Cgz^0y1~$VXBio4XEdg)WvuzLc-W38=xhB0A(;`!huSIrWX8A1p@#* zo38U8t^U{W%5U57;Pc-1)>?8YIH%xgGKNIiz5vKL9fU;zyU5t)^=)1dl%Vfndb*BL zf&pK>?HEGENZ4n-c67BL&!64ngUBjm3aezW8IA!=3{t}t18Qm_c*8N|q1vxsiq zc3wmV}((oa5QBGQ2?W^uZ=tX2GR3;p6df2zk#39*s1&apP=_Qz)=6S zM?YZiY#C?w8^4>t2eef>!v5V=q?Tjnhi!s;KOffhb9^D567=}6={MlQd?`a8qFO)b z)j*WssZ&rrc}G546kKG7xXQiH^V$L6Im)o#ym;*nEubNDrj2OxgUsV0yaTU(qCivO z#cNwzeMK&3s1bt4P>>O`3~8@#zcR>C`out3Cw3qQV)vEXWRn_G#v(gif4k9@bDruNvCf(>92k-!RY_b_;ufskqL5AcwlE$;hvC)@5r&l_~^ z%7(ObW?iQw)E91OIheqg!JNk&N8xXQzRqwG2+84L%t;@IL}Ci8Wq64Lv;X#&VBT}1 z>Q#V29WX}e9lydovRhW6x;(`B(8hgs1z?5zIm3O+H3i(bVSk*CXG^3XBN+{f;T6z^ zG9!%@Hfh4efb+U}6OzsZPCI*#*7Y^Jm6WFyrYm&0^{kro@fv8>I7fcicR`Ir09qu; zm#HyQAwv-Ww#x~kRN!^6a-?$*Mju}lPm6OM%XliwN`sv(kuISuSEj#@kI&ZE&WGHa zcW1~xmXn&3zxF@#YHd17PgqO!HSb=h8g~y3gtn}w+_pgh&B#&X$F>)f2vhxlC zl4`Xz!d8%Cs)Ub{z_N^0O;QaD9uy}6a8&p`MvG5L%fNY?uM?5)7Z`y0TBuzWF`?_S@F7rD@h}+yK3VtPA1lQV~^xJ z@Aj^+jmb@@x`(ADqo@Hixri0IgP@5x;9*hnn!59=Lsxh4Uig(f?W@yIR@s zF@KZ6s8ze>5;rlG!_xJ9*HYY>L$V1_UB^>3GcTvgB`t0t0i{LfjRn#eEA};M!Xwfl zh9{QB1m-(~itq+5eJ_0j-~ZC(y^j0?a?zWLh_who7>Dz9f8^K6NC6N+R0gZ?uh2(F zyiUw8c~5!++p#a*_6WR9Sb`xJIJ^1#L1(Uft8X#c*^Dy-t8j7f znA~lyifp+892lYmbVyUGuhC{xJbT=8AIJ!|JMSHaCWG%-ppX<)IvZ;ymL0nq{zU)OU-@Xbm8pW5 z4*Lw39^`~yg9GZLA*wZ}Ko3R|hC#)~)coqZHzMKup+FO|e%3`}&X(LjMTe}Qd0H*jA%V1G35sP{DuER~LtzVT$m1QSLh1%!Yi#Fhsh z0?9~rUhPuiCuR=F4kmX#$SzgjOs(`n&I(I~Zh~SVE&Gt*p zhQgF)J~YCvO{nU8mK}p3wz{UM2E%wy0we4$oP>a@9iPxxqTM^x`Hd;8BoWSVXmpZ^bQ1#Z0g=uCKQv>7S7g!$`!C0PJc9%&88jU{cE$eiVudj?20uFg^Z*~|z1s&8H6Bl3+1RO^MTcgy&;hHGY1If+(PA|=E z=+PSX!W-#+TKLau=`8q|&s960s7As<4^_uBM=Iz3o_qxcM+V7nUwfA#htx3-jRY3&C-o}qzJWMna!rfRTs?mNQkgZc_*(XC zgW_8rKqSpTT=F*+j`=8(T?I7#nD<5|Pfr z9a{n2v?pauxa58%lpuK7byF>RZ!UUIT9Qn*Vc6&f{tzx81!#(D)-sA6z#UYX-3#nx z|E;rPd_(fqQ2$mg*SBq#(!aupZ9z62CTrcXIK{%CC$3~G;%pb&>u&=r8Q>UH(s&_F zMO;j|v_LU6>q^?wP#ib%C^TUvXqw|*ii)#+#*YHCW{AK@S$vy)bJH->!KREP!Idp9 zRO&h17n$W3a5d1NOc&;5bsze1k1$5y`_Y=0kwnAh5?XsHI+Ty~rsv_UuUEinyN|Sw zs!0`;R`CLj37-~*04WTYDriE#Kg?pXroM9L{jJ3XE^_K3^6{$gn2i@5XY9A9=yhwE z%4|a{`M{NqPDO6V@?_&k+9KBL>);&ciw4G!5#VF9`&$&9c+!S_(#p8)r@7cM)eO6i zV?F8+D#anHd%m(ZRaN`EV5c0_uDm))6K0%`89$|FL3*R8v4ws?)*G^pLC22U0!nl8 zea<98wF4I$o#_5odEguLpPso?fG`>fh*efe3YkA;rAhO6w;uj~EC3&uq2atBc=@N2 zCiK*_)(2J}jZnANUcxXqy!W#;{HH^eW%6-ZkA~)r`@^Em#M0E3_jH9LQB4RVdMA-` zH>$v1RHP)V^R*Sii+&|2K#Hq?YAMK{3ItaEsZ}uRBlZ-%HXw<5b_h3^z6I{1PFMFkI&hw z&l&MGs2T=lO%!ss1Y1j6mBy%z)58oMvoTNtfTlU1IAkur^Vg63-8@_G^__uj`&>Fz zYS)Up)opps2u&bHSQ;?A@1iZ(UomI}M3z|ps zwoMKx;^$P`cQhh~>T@4p7n2yV=0!&ReMy60Z@{;$yg;ySXVv3{CYBA_Qg9oB5>l|@3q3S+LW;Kh45 zeOqRr0RI#=K17S@lX@)ALGj(e7}g~<^zQRkJUzO5q|F3M3SR3jZ6lLpL|Yw*A+x$s zry}Km$Vx`D&^#VIIU#7CZVv$#m2ePP;gKB8b?E44gs7sBLMd7VCDoPxMf*{%XZIm@ zZFl%bd~zn>3D_lVaKb{7A}$DM8mE4ZnzRtK@>Oh{S3$$?XC>69Efv&()RtgwJ^_XM z6E_?~{%>)xX`3OEt(AYr1^sNTBh0aBu5GJk6SNBWKCnI~L%Xiv2H5696w=o;J(Ngr z`OnU-2`KLCaxk0Hko-3wOHG#IF>uC25vLJe<4ZiSJ!S{KA?$mkDtoHZ;fKS%I?% zM}=Z2&Jm^W1Y2GKVnwe(?cL8@7dQKu$Z~Y7Z!;!Vs7@|=mJ)f29+!8L!6&OYpHq3x zR!euA>3Wqih0^IWk3H*oP|%0$g9cy1SmG7^0 z`%g*Wr{9pKqq21)N;w^QE_Pyf1wshkzeLD3C@Ok7<1Zs5MegEMwd^3={v>HD(duG- zE4c1~;`NBA&uuD^lQe#BWWQ^npaK1P!bnm1Wg;b{$ou#Y(DU+mXXcQVxEis+-jfPT z@@vs!AT!X6v-9pVjPKW495TU&daoX_GYT0L*VpdD*JXzhet24QS$iDDy7Vs|;%%1d zm&0R`u~Ly3$T{>HK~bHEQh{MQ^_Hn*-CCi2b@h&Wxeq;;{+F>pu~+#;qCiv3f1=PF zfG#a%6ypCl2pUI{{DjV|>br8)*$JGw;v2WE<}kYd!1iLAl)hl}8!I0-*?(}W`{OU} z+6(GS`YN>ZWLX7GA-`{W`Xm$}ZDZGAi*>qf+V!*L2#TES{O2tFtprGRuFGD zNO0?E?JW!J=I7^@&W=w`K4}^wjb71lIRIV75|~e31=H1cy)N@zRDg2`?Bx+zo13>| zHcM5D>n&}_Gwv8e@!tJ;LZk>+l+#5U4OaT*?20)7T?wZCs$$EA7aWKCTmO1lj=>Z! zYiMWwWuzTmR(@v)sVvj+ZYTINO(-=(*lgyJp^z-yz?F|PV!m7wqI)KFSrPD)C<0$y zAb5xDHTa=BxJkH<8o&zm&x?fD^)(JQzJgrwpBj(8d3$%oLS4t}-Bf#}p}-c=FzC`RIe)IOVyKs#_0N_!30{>oOZC7i^ZILC={A|{j^LsflSc$d%o+t%Hb zViu2kQL?FnG5`Z}hDUqXR|R8{n2vVhb+%FR^-THQEf==t~nw05DLF0|- z`N5-kcV8o1dP)iEyQ=LoQRRL@k`KI@YldBFk^ngT-RZ^`;qYSLRtTsXF18zyu!x78 z-a!;@+hnSLNT0@$H1Qo*xVhllS+DJCypN!OUH|@14DS7fYlfevhgTzh}{ewDEd+p)S`ig>%z8C+db{f+FPS6Rpd( z2W$N`n}}$H4@PW3SbM&_wx34-4&|c|ElBALO53lFCw0e9bH8|>S7r(6I3B-i)~roh zbwkzHWiSCbM^9%?O{yr;rX78b1UH`!_wz@fgJ=^FQQPLWVnXV^3JI$1Q%$3QQe4%6 z5qCC^h;jWIG$vL1yr}5LHaNt?6MLWHQSgKB%^yJgx}ikI1+-OUm&a+_mjZMs;(k<= z@Z#92n4RFFFCx<6#^ZY{>wuJZQf>(`i=HG*3OO3ts>OlRt*eO3)tR+U8aWl~FOT6o z7!Y?MufWgl_ng8Z@;-n-MU#LgZQOEqo))d8QbqV#ezW7*D}G}eJuf5jz4`~AXO?y$ zk7K@;6fH#vt>D{jn}-82E_nD2xzJ^p4+R6AI=WV{^_qMNcPtw22SIPN;oAyv@MC99 z7H%RYvG{hQ@8xDz&r7vqC$4RGEWX$XEE^UkAfzq~1z-66wmru0uL1$seW~(*?zOKL zE_LUJw)$?jkyG>T(<~)jDWljQv$Q`1BgW?#7wK7kc5I1E?8Y)CQ{kNah>Q%gFm9Rb zk!rHwkk3n+!WFOE+fRTTB`}N zqJda&q19qHhL3|+>Ib$lp|T%G4d~66xaqd91vM~ABg8yhk2k`k-&RipBRZTSGJPE; z1*kyV^qPB%+877O7>DpuQQ;s*{4@p$`g}i8lgr%hCE^~?mAy&m@$%D%bk&~r*1$gx zEWb~LZ~3@}FI^72k1ji!KD7J0FK^Ma%iv7CvZ*`p{tl(SU}8uV7?i!3omp}H>k9$g z*hL~yb48_Mlm6qgjf0pqOd~(^iYh?%hvA4nS>D#M@BQ=N>6_YCqc4BH5t}6hEB%0L z+(a_mt5>;80hASY=Fe4}zQbSiB9A&}YQX$CEp&fpw2Np1%1`=aRW3h2SCI9TOhH+#@H;G%+5s@7BJago*&^V)azTG z4SLpVZ>(r@zm`a(M@CPc4+73?n;FMDq8`K4qy|5)x`n5sh_n6DS#HLb9!q7n9Nn+)f3Jee|8N^pcga{q`4_ zk%rfdYC+>V#@ORMd?LmV0`aAHCB2g8+(h8qyL;^-jgyXP9%i+lEnye~se_9nMBZ6a zoCC7`v&OVOs)99xJ6R4snvTZCOSflL)32II)#I!VCb{o4mlG`f{XZWB8d9#$jn-~godtYg@2aKC(ZbHn=}>$yIeiL&5%CeQVx z&gO10qT&xD1H6E8)Z-Te3gdB&!z?`kY5OkK|lwLKPF zo5K26Z$VCe^{=tAoV!A3J zx&Cn7gs&={mAPxkz4uHtUF56Z^@$Fx!$g-#Eq|YLv3rtK+zU2izVb7?Xy6;h}-=W|MwIykAg3?BQgzl-A4(jqK(*so1hdKkK+nw)e%^XRm9-4ani zi(%;P|BY!B65*3ns;zESB;Lxafy>SVwdq{b`@kjYyz2F9M$i5jm079u7AkkIH5Y*> z{@b#+5+$77L3LRVI#>VMhR_qzuf+Z z_MW@9{uhCFdYnCCO&^lG*1xS_ix_UJm6E1ea`*DkYn61Ns&c5Jjr z(hOf6w<$QX@}P)SLX z9zH_OWW7=L9a-Y1$VbTAk6C;F+tR(|R^lYN~Kdk#+HXD22&eGNM@IJ83T&3YtLaN&XLPA_Z zgHhw0?V1jErJ4k%WDMRK#S@zr@+SQcgXs@$+}i`pU3_rjfBfrtFYAf~abS2I|L(Tf z3)?aCnda&fBlGewEX82&HBjb%FH!$x=`qE_ug1->>~3WhALENFWg5!okY~hM^^{sHU@fP8H_k&Qr_BGCw-+F$^j$67 z%%^L_886wR17X#-!WrQtLA2LrZBNuQOyFmj4VBJ=}vry=rgr&| zwjB4N#x@Oy#Q&8Ht%G1m$+8i%>Z+jS!n{!Q!TSZts-CKX3 z&dc83Sh733uimi6rNvtOa!q$sA@$L6;!T6rEm3d4$2;RLvoiB^k(T18Kf>yF+@00J z8L=i~$L4kn5TQmoLK|dBB7}aw}N9DNx5>rd-bY>Qleq%kctS8vx$0SvgPCe`xXf4 zzNQf++2a$Zy$){%7e|{Iaf*a=poP`bQ7~bBB`Y`NMI~Lb(V}p+F%@*|7_*)6o?vM` zgYF|}zPh9y`WJBpMkYicpIh~Lx3`(y;5t2y@C8AFs!7eQBeT9?noP(&7z{d7`Gdl^ zE?e6CXw$jf61kk6ZBaz=!6J8 z!zqIO<^EzTe9^SvVm$yblUk)C`rp4QDTUA9Xsy20;>22-y@%9c@ROh+Kc7W82iy=H zwdtT(q0*Pk#D^;VIQ(j?iihi8GX67>@a0e4=uri~=+gapRJ?Y@&jE7VCi}z6*;C(m zg!Im{)g#Wcjb6uuA`RXiN9PTn4GPCn+W-D7myG47`m7d0wMii{yYAhAB12QS*393p zx!pSZygdP_N$R-dO+Kr$vNneH3q~g&WJoK$``5nkl6j*1E+)X>#i}S_#YsdQ9yRwj zuNC9^+HxMvF9_ami{)z-10TMh>y<+<@|?hve-sz71yV?^@Io?XYDH}==xUQiWCAAd zYa1HoOf+q0vx)&nVdwZg?1O#hQRhOA{#n+%kE(U1RU)hXym=fdc>?)g<0nY<^+%ST zD?3NzG1w}mikYela{)8G^?~}Ho}Nb`)o|g25Wgl70eNZ5u9TQwTUbr=4-o#{S9kF? zpn&1zOx5URz^RbcPp0v`_(fN<6X%z$GZ`~^MGkRAEYsAh_S+~+{qxTY(0^lt4~cI1 zgJ0Uz#1@`0c3lQGb2ob+9SB#z^h7ZZPozEq_rK#qp)396WqHs5jtl=@_^lNQ7k(fG zvMUKvM7{P^M&r!Q5J~SiEDGfP5$xkJg}#K39ZtYt7YeEA0T$_x~PYg zLn6Fn;Ct@TCq)tTxwOTj(fyybk2%#JHw#a>)my_R;pVO2-tr-whAL^h+7|?h#GjcndnoP*#7K$)IN~kQz~})N14Cs4n;~fD((jw*8}eeq{Qzq(bs25 zPK<0INT_2S0(pF7dIel;atQ9<_fm~?M|M@jp-1EiiEowOW=@&CzTO|b?P@p_-`=<{ zp4*JY-ae%%5>*PIfBI83X8HWQJaWJO+4lVMato-_G-XMSbVCktm6SgjMdmdFKVbn_ z*tPCK+U)D(Rctuh0@2DOWP4*W9DdA7wRCaiqeGJcEJ%{eahx9n(%Y^5e;E6R_jlQ< z-9Z337z+h**x)4FJx2vNSRyC+1(g*{3zgKnPlf(0-f3<-65G@2b+_W~ z*vroIblB>l$(zQ%^4Fh_6|;^;0PIBfFEio>hSje)y11ON@e6{=Tc+~vp<@jSz5xri8f%ufKtM4O-iizs{rwTj%* zeN;|$krB580|~9$m4#0KBo=ZB54jhG(u|;95t!Qy%NaX6J#B2>^0fsts?j6Xsf>Gi0v%?XAZxq zru_>HVXx_KG58(w%iY`*I+~u|<{O`%1T#XEW2d2ymrEffp3}%L;_o#wPNfxtCOpFn zK2+$yCno$tX0vJoq^ZM}3l2lWO`sU8*9fN}X!sGCNlu9=B1W^v>FWAPl#3+nSVLa9sc1@r!L*)<%A`8AFy`I*3Q{7FMl>@$e(LLFf zygC8vlJu%aAvd2NWNiWR_J;nUz}_#?q(nZ|oG%txG7&o1)NAK_dHMaw{yky6&!s1> zKu*v( zE96iBH$}t~Er3Ul?Bo5!s3|;2cksY^@U6_K`?a1R(T{l&c)wnd1yBqk2qdq5YWz|% zQ|pr$h17VJs%&vv{E}<>k2M!YKVFy(jFQdjfwM!62You}pj6Ru6^uiwBWoo^Z9W05 z-339PuetJF0G{(x{ED?$o%RS1jEvAB6=)+ZDZKS_&sQL&ei^bVYJa>Lo`%6wY_sw8&G=rL`g zL|Ji6y$a6c=gCanRm19E9nmI#V*)@oJewe1FV1QL8RKyts_g2?FgVWQ1+eQWOn&>$ zv=PIpYPxGSbjg?_$?_Zt3$-9#+sgTpFhqwehS4nvvR_3}lmc==yXudYgwpPX$)T00 zmZe(+G0jH$R!w^x)(;(xJ^ebe@7P*-*^5HFoboHYZf(ugCK_K}&6z$BxmDVoY}U@h zb8oz5qF|`Gi}G?Ex?W>&CM+#s4^3OWpWi<`cuFDXT{cKSe7~t~dUfvZz|aw&AP6yf zp;70w$Q??XZQ7w1Gve#SC?l?=EE{(;`%`VxZ(O*L8AihNG9Ymly{nh}Qua}bTA2)x z+C0?O>>UZW9lEecudc5Cw5J}QnF;P>5dMy`;Qa~3!Ae`Z0HjU{O$bSHb=0C4xUxxK znBeF{rR!Y?r9Hz|9+53C*l3V!VO~vI;<)lB7UVC;G>{mB3%(Yg*Vgfjq6W^1M{ z?&Sg(`5`itA3(R{GvJ0pjkUd~vjJ4FZ(12i2yuG9eSHBh8Fe0l@KQYYQhr|dL*P|# zOO+2m8BUXb!c)~j!KW1+1;vnARQP>VIMFY>?k*840EO7ZR=3RX(SI@T*L9J!uPPyA zHV&l_*FlL_Q_%3IWh7ne$m|u}t$@OPoBj;0q$FN1s?FwK^9a<)Q&Ar+#t4S?flp$BaM;D5XTEN!=kB%kHqf1tpsiP6-# z?xu#+t%(FV@NNs{2jbT{-AJU zhBXIinL5aETNep&Ls}MR)p2^jrOUB-^%IX1J<}uisFKgZr>J9S7@0(yt>eB5whMOB zKPK^T@(lh-Nj##o_xDX`Z@HaaG1ZI_L1oO6s&zvYCGC#KfMwvnK*dQ$s&i z*aCT7YQCX50g=^QR}lZFmz(rueo6|FR!eiTG~}P8-|Cupt2t$~ROxD5pZu5stl!Yf zH6i^Jb~d`t@3CnS0c7SAOshjy3@X1&Tdw8wkIqgQwog{%J=d5JVo|Zn_KRfNevHw+ zSQxK;Usim%TPt%l`O>BNGeL}af-deaT?$;iG?YqIr8o^sbQ0FUZ%botEUbYLNH`+I zO6Qbjd$m%Ed!xzJ`g~O*ocxPJ*9R}ghF^Qa$QXd!^xPCt(ntyUqI^|L z(0(&)=9q31P(4Qq$lwtJahSU19|vk#Tt64)xj(wv9W^gTv5)1S^dSH9?|-&olZ1Tl z@%ec{=xs`#*R0e`=%^(>)P9{^*N# zLijr5pB}M_RB;WE0h5fJ z4er@`ynILie3(_Sz1m9;^zqZO9(sax#bdyR!iN+f0YUX5(|KTbh#&oST9p$YkQQ&& z@M;gO?(h^#vcZ3UhufA66@bUeA4`rf5nex&v~;_#&kGgzdg5xweXhUy`5?5JSH=Kt zw$Kgo`iLa@^E3|-bs=+`bWVvrL%M@XXG<-;lhZ8H%(eG+GMB1kOv>p ziB5oGz%bPUZF8IA5IzIZRS$yg)L@4ew~o0H?KVmdJRZ-(V<*;0;K!FH>&1m$Q_g}M z@Y9pXcdX#qGGw*@nYoz!?%{5<8tlKqa z4Ks71dUQ9DKDL|EZZ1weS_*RLIaHv-M zg3(k%LGqx+lePW=0MP1iZYFPqro0Xq!2LX>YS3%*1QcUxTI(!bIW$57<0l{lx&NN| zBWaNCgJ6Xk2Qoy%A>8`y5^&9BpR;7_)NXG3@G2{Jv#E`-NIIg&gJ*zdXYc{a{!{P02O=>eY5}?xx6#u@54<=wwbFU7Zp#Qe>SDL|B(mv-t)3Dp_?^yE& zm$0lB)_=GOhm}?LaS&K*obcAi)8AhwUzK%Bf>LbLB1Rh6CvdR7ZWPC!q0WTM4)bS+O^i|?&@SNoz)J&BWdQw4QrNI(7AU!-tJJe zwasqrlGlQV2qzJevMAu>4bFxy82ySa)Qm5`mPb+YFQ~qONax+32m%|c{0m5RI_y~7 zg~3>abH?w{w!bWCje`>#NQvn}TBI19Prz8_tX3N9bWzbEa`L&|1OEK8nDO$0wBBh8 zV?=})+%as*6jFypfSe`Z>UXr+vtSfm_gTjg&t${gE@da9cBkyF`6a>G4Suf#vbpx| zsb#v`QJGBp0k^E_2c~s@95BcYh_?v6(03KF;cs`*CRFN+B>zvIBX$SW2PfnP1&V=q zp9^NP3EtJaOggH$kmGlA;CUPCx0!WtO%Ri+SJRs*5hfz6m<2m-*4a7(DMI~(s=0p3 zNpmdfFUj)i9b_<)9sD^ST@&$?_Kd$2nNb!vcMLG7{!SlMw@P5^Z36xb*HN<`VJz_i z5xx~p_qW%eY$Gwn};M5sg@_S<)MaJNrh)F}Cy4t|z zG|DOti1uodNC_5*kUs>ogr-38nmD`B;r|pFEw5K@_HTjS47jtrSjCJE8u0{3GhFM* zGvwFp>h!gyUJABl5b?U#+`EKvNB%o0vzJxljIfR89`EM|K7NnF^F|Wb%Pt=-Plw9_ ztdD5$*04D>sd50*VTP9oU(Y*GOXct#+sDrk6&>V-JrnQ*Fa0Q86P{jDdZncr{`z7i z7=798fn2+>5{jSBA7KPV!jA}pzy!%^*W21-&`C-A7Or=(38|n>a#U}9IkesztyC+L zHrLaQCZF5@DW$K@N-{XmeGQEVulUf| zI$kHleMHJzaO6-0E2W0w@-QRwjow&r#VBi=K$_uVYiatys51jin$8u5IdMKZK6@Vu zVM3Xv4iZDJ938gC;~f~Dun+&)nt5B(x4vIB`J}caJZ@gugiolsBH-P^+MAPeVZZGe zEf2RN`cVofWZ{}nFSGE2;xnRq2l^|=%k|-&p1YO5>elEGdx=Hn(pJCrRgt?dx+BQ4 zay{~nM}PA*`p{W-e0p}tbhXFpxOb7$O~9;a{?~~cR{E87V9q?a(6Pjq+741Jbc|7|N(@X{t&VSOrfXx+t`9HS4DyXeCS`_yH#jQw-yStVG#VHi` zQl!N-xJ!#$p|}y~+1(_W1E` zY-w-2Vh%|3{{f+$lR zdh-0kOr*XfDyP|kNy>FV&Exu0o2j07B5udHMqq(>hu3<=a?3R~QD)*5s?7&xq?Z5g zb2cX}EKlYyU#fjJQxK-RF&a%(4UZ^oG z`X)GL-m@?&b@r=@;c^Z&0B^Py7Ls;@p8VRl4K{vZ^R)@gT=9qB$<9^0UuJ!dh%UW)N@qpa2`}s75 zmT|C!TQaDh_~GG$?x;?{uhyHDx^HMA#R5WjZmyToLh!+yX)WSd)npM-la1bwVdPw0 zLL?Bq;Qz96HFJN;1EPeRj;acA@>*#(Pn%z8d3%jh-mRFY4($1-MWI;!hswQJsMI*qn#V^cfNr=twnQ`0Z&%hG# zxZPQ3&Fjhrymf1Nw;#Iwst%01$s)Xu-ne$Yk*D+Ln5+a?dib_t^Yi-VrFTod`-P-U z?zf%hx~n&q&8bMDMq|$%NDqgitzP|_m^db8Jc4edQA15C8dwT4E0bvqvrlXTRlBQF2E;~%BbvFOw6x>q396p0wo<((nVMEW87sIt z9Nz1o-^%3WiiM7uwx21h=z`)2)uj#f4yV#iX^V5#mggbo3yuMQ9PuQF-Q+|$;orG= z4WKogBHP6MnP{Z$*>FgKNxwC+<53*Ql3F}iP@R_Nqiygm!-ZIe3``ojbe_0J7HSjvZx*3b`09ghM zFdlunwY7IFd{6DL2=i{i&d{k3)qoFaz=2=JPZLKzqdl+BL!A6?Xjhwe112+Q3Mkyl zZ>Bh9iBhO4$0t{LlxiO1$f&p2xNfy@9X7*{s|wiyIG|xqcBjY2$9-@L;KC^|8dWCF z9$A(3Ks~N*I`H%$IXv^EPM{Yd-G&$io~NozdY{S5EfewJr55w}o^OWZis+`c#7C-b zCgjT9c%++SZc1zZX9K6wcPc!1O8qi3mVt^dPkt8Udmhd1y0-+C8u+0)aJcmbC*_uzLZoTwIWbCWWWMcB={QufAjklc~V^EOa3X&N&r`8@j> zxN-YM+1`PP;(=v)Ww87Gp2uyz)3wo$EZ#28iuoYBrqc;|>ib`^pOk%N^z`9uu+RHV zS2diO(VQLEdB(O}z0G3+oEc~?-L)YjsU{%Nqxf zj~I?+l)U+n7=zL?8nxIhbvC6Ja--t|d!vJ2ygif&vpj^Satp)AikUrDaEmHVi%`sS zZ{FB}W^L`_v+4-_yxp|X@d`~>A zK*Cq6OK)GSA!Tw{!%4z-)5*bwEl0$gD~_r&Deu?YwJ&r`6?v%ZO7QK7>Tfp0aJ5s> zLGwIXzCFJ>%V=_#o@;hr!#;wZQN2CE$!7Vef4D)cht14x5T^eGzr;gmd}UCUYqc*` zeRV${iS2tI8$NUUZX578os*iItiZlt*4Zl6#)^*q&ToY&$Zb5I0T?Svoz>aBCW2ob)TkS?}BZFBORqZ?FnC_MAmBPL48#EprQ z<1tzsTRWN&_jrDopYjT?FlJhq1BUuh##WbHQi&MTRh8+{ayJtn`ZuP04&{8x+$^a6 z5<*2RH2^C)7I$4Yh&2)({54r7EYX4d=xcNK8x#5nH=sUnu`G)PGZu!D^f@_mky=&l zz$Sf$_OYJAu#fg11Y3P_thIJU1c^B(ben$8O7=IA9nza1>HX^PWQ%%tbXSbg!%`{O zv^bzQ)|cl0`ZgXeY+SKNn+x5x!a~ge2t+m>8_=;SW8!fiO#AQkg|wL2D=RlN`|>gD zVxtP%aY(9m;LIoz6OsuS@VXb{tZq1@UuspWu6Q68B$9Vdjwp~Y+C~;6LesHu=FFf* zF(9Y1oLJoW4*>zvRK$$`X@}sBLySfi_9>l`< z`@^bJSsA(0S%}zZSt^h^!K1pI`pJfdY~&1mMW4g*%!&gVyqsy)(TIuR+=5dCz8STC zB|nwlK4e_iyy8ocv?2SD|WT|b4gJyY^&2VzDgJr#dYdROTLr8#l7a~k_Cx6YA8c~j7& zCe8c47#wxSuldU(-K)O#x%~1d&cLN!v)Jd>5LKc?%JeiF+FMu z@?X@{q-M3li%akd=X&Hv^B*73dCnc6A44J$SY=2Ff7>g*&-c8|hC+*zZl&`u%D0D7 zAu=-=lyrlh*oisXH@l;k_BxTtQYN;*7 zMUl~?F`_P+45q~hre%NKa+1MEF@Fn}O?>#LS-StvKSH(p47fekH{s7i3+3SIUTT9R zN(C<%!M~vggvV7nF^Se*a{vk7mvs{>i8qMwv)O*Z3$@zSfeqoOqVr>Z=q-c$+*Ge1 z%W>c}YVEuuLx)8jvw+T#AXO2ErG(`tJ0Un+qSUctTAh5L9&ZXEs@wTtA4gDM8r?>TTg{-I#O zL1Mr`5*O%! zuhN2DUJV6Lxj>tL0Mn14&67o_jGEzh_>WiyS4Y1d8k*?X?B$c4(I^o2GC;Lxuy*s- z>l_Kbp-r7A{*UDKUAy&0@`9)&!oo~lvP8yImt2_7;Tznv?3D0_mIFu; ze}K?=WnDECxZHtVUR}Ni5+Udre*2okWscR+a{T6mH17BRc^h#!;FPR|A6=~n1}~hU zNF5LN)Ji7RzSx9t5)QmR{9S!rwUcsOar$iGu%csjDzE#$6byt@P#;b~L(p!s_vP|z zCw7gGMQjFW=fAuBSK&*cfD@zEJSe!J+4>aR@G|B(J!5QcyH;rQ=T%N zeLiDZxrs8}aP06l=`F9ea?`W<|^nq)GVNY zy}vruf_v0Ww&I>4&eT3G_>L_|3^@VbPzT)O4iCAxi{2XN=d0;~Gldb~n=%d(kfjavjMAu+<96yz)aND6HKtJtTt*`b1M# zFt@#^$_X*Knv7*epxdGxVDx+mxToOq^&zW{2jUIUZ-#{PJ#ca6UZOVmhju)%af{OVc;zV+XHWXtvt9w+6AREl`nUy+Q}178a4 zm$zD&sB;7BTff z{`HYdUf#jBjmgepQU_oMjp1O8k8F40@Cm<-h|{IIHf+LB47l(oYF+>uPnn&bq#xiM z`1(>L-sr%kaW02|!E);NL@jzR2A>nBwN^LC04n!oTm8vTsE?c z`iK4A_XwLQx1f)ej4L-l0nf43cCNNU5&r;gY1zq1=Hnw8jT+q+Uas4orG+TPz=>Qk zff{bl6B&4+l{Y$}c}T;O0XHz+6pqZRNl2F2R~xJK%uq7OSyPV>8wf`7c_W@9ly`ax-yWIJY`<-Ru!^TH$t#Y)EpV+3QKBlBgZ_6;?BPd!y$;=(y}K0~B+~zc?CtyyZM_-7l+dj?$y$;qmGv?NNdznJw4nggKQ{PU|-40ZW;e_M21Vf~US z(c?<$r<843OEodKfxfeDkLm!;-c|e#X!GFCbABB3!R6K#eC)Oe5tq^nEyV;}U|l(Q z4nSM)vf|wN1oV*_!{#)LdlV$E2{!AfWl60)Pqv7dxW+eBwew4nC|l07v*Iz)jYCaE z(7i8?=9h=oCEAY`Yv|aOuY#+YWZ1p{q+qeO2C*a}0QZFzN{sFG`IW(0n zZu12{*deCnF1Oj^DdUI3-yK%cH%q!%*+6|nI|F>neo%S;yI4d7|06NbMVe#W<*qvJ z<(YDlimFaek&rj64XN??bW`kfv5!geNz)cd+O}UI(sv7z17O4Wnzz9^xNXo{g32lx zV$Db{&O;rEs@rJ(i0pB+I%s&hIJtNZ-=X@18|XBo2cGRbb*Q^Ool{Fa!?CG=T?~CS z-0gVK;4&6zFrc%hi$bm6cyLth5%y9zmk@H?Af#!Ejga#F`Em#KnXfB+zbv2|Y&k-F z*JX$+ty_70c=%tC29VQk?(pEoTb1p@WIP@vY+^tBL$S7R4-ncAg>2YrAf5i0 z{RXt5kBf#kPFuT<>jBSoww}}LV_UAc$?yO+S#!-EXtxQsat7*XMylm6VSr|_B8>{{=*G?k!(Kk3%9qwXP&6NTF z4^XJ1nQdwh$I_DR^JUnkoKlC}8|we&pZ@PGz;8?R6s{chM)#Nu5b^xe-pFgf9tEC-Fm4jh?kt*BxUnyjojXPBu z|8+8@iGH|aJ>_jPi18&W>&o*aUzhKtt!GAD?92ANO~dx#(J{n%8nj&7=tjKL&A05D zo4QnWVDfL@M~KAp5J2giN{jkT(<#skd>##Xe17o;J?TCF&~Wm6%m`^aHx0;My>5r)w@3Rnwil}jd00&SPw7T1x;<+uT?u(ghPF+P0oL;rO%-*a4BbeT zJ2_BEX$N8jeo~1ZuIK$XE6s}`VIDmdNQiMb3MH9DOm51j>V7yci&3F9{E9lx#PoIU=2^_55V56&}^-(RuB+c z|9vLK$-EZa>c7qQxDyaSa0p9{JBK6!b9=U@bHv7h&quPp`^^b6CJEJQuMROi($TA@ z1GnX}qB9z;SR&Twib6+81j~e6D3i>wUBIb(W;sBW)KU>e&#M$s%56Y;1dy4M&ZMq# zVJ!84Z+iF0W(}jqySH26Ti-|pIscA#JFB@~z+qaT_zWbGI z7=R|6CrhwF?uH?*{g`Wy^x;qd1LSNpUV&SkO}K&xVtVzbgu>h4iha4dp^HNdOI%`a zlBFU@7Y*VBiOFc%EZxp{oR1FhK6oc}Rc2~_1;%j$&DT_sr*NliqpqK2XCpn_Tsik> zw_jbmPb?)_%c^I`W}=~HJEpLc#r0~s=c8e)3Y=dI z901EnbIbH_6l0lTr|1q#wHr@|G-9ITcu$K3(I%PA^`4y#=I`HC>>ZqLX-A;n)wE_+-FE%NR(7CKmtfI65h-`p z#6_YsfnbIzZD_peQhcpBsUZD=D%c8{e!=bo#YiF3(1P8tYPR`PZ_;*|f`N zoLS2Vbe2D7*4zpg8rI*SHA83s>2+?CXNzgUFX9w3@$l63*&L@1M~0(jA|6 zm%drv-E9yPOZd>xseM2~SWlb9hc8&AA64Am>S!m2R_u9M2+7??~ z-tBlKf*v^L$8{s6H4p@1LZKG<7!bN(97AFU>`Y)G0N)Ju^!Nc%k++ zcT+da$m#o!XKehFZhCG@s2^Z|KASRme*C~NvihG(5!MGQND;FNDrSb*%ciOe*xx~4 zc=5pp!vg@(7_! zDS$BY$mwt|AN;E2H_l2gEtm*IACuB&eA<4aWg8t?e~e`*40_O-+IftmDoSlOeX8*Q z5^}Wz5qO{KsUpuVSF0?}Q+2BE4!O?itUabk#9;oocoMBiOgT#@N1$D8aCqq0Kvi1r zaE?iDmTnx)S-zZZj&^@34aZg`1R@VpwcZZYHJ37M{CA0W+u1YZaN1O;-vLUAgWjS= zb<_t}y{x%=*gFR1DsDcLnCNLuZ+1p}k|039P=BYGp>HWugzIYh&{@kfzL+kInDc<{ zIB0$5@1ALYeZq#ULxhs1O_Ds-^(2SvAEG|a(tkA(<#*7)YGAvVxo)Q=0`LNV-H!Ub zeBM5jy!yxweDankYorq0nFo6CUsOb_xOA*JWiHd=`T0->v>j6Qr{b7Egt(42 zeR9(K`CdBWFV0cNlLK|hC7okCj;8$#K+fGsCXmyAl`{)F z^Q)gp7Oc;@w3hzF=?$dycp z0=+zRFE6v}o;uqBp-Da^HR^lrx;V5ySPGU`IScAwJ*@Bj$nXz|F~qw7dzbJtQ^D2K zXquun#HsHa@bY4uC%x0|2c2hPVE_}sO$Ix30=%8XYrIRzUW##m6AqQbX-j3GuXz-c32)YEHd%g7khUiXGc76e?}wx{abw6ZIog zJ{8Fs>Ot>NlK~D#gn8Ydle!#3t77S{~$M}ej?CR%_ z^Z1>*`1N=tlWa7@5$=-%?RzbA6nVU z3M7_d!R^y0gm{m7kJ{Sy4t4d7FQ%440Cm1*p z$>EVZ!GhrwEmy}4oMuCoUFE$Ym>7S?M{LdBa+j3YZNM6De8klL(x)MP;**(X9B~AS z8FEg|$guFx%T-0w( zB^9b648wPo534DU-@T>z!Jo_j__S=E9qV&&&ANyS0!r~iNeKk=j%Yg{?-x`=!hD_; zMsU-_@3UO>eRr`uPVCO8udY6Jtpyb~x-JoK7TV~fCn3;|Jx;^IeBJ#atW7Z0Fh~!f zg_d#VZQ15GPt~=9cUxNJ6~AhlIs5esmairGkkO{2{3KWb=M5F0=m;-oE>d zq!lAh0Qf5!R{TBl)ACMd4s^Z!c9oE#+Ww^3o+LGMo#T@TaWZKHY+-JEBeJTzm-?q> zN>O(T9&YVdU==?Z(kr*_>1oM;?!kXmY0_z!K%dG^KA>BbHt~`(qs}Q& z5bEgr18vo3&G@XkQ$CIi3re+X9p`9%9{rUZ^^?U#DB)9n-Ib#$e?DDKcjVjSj}qml zG+Bq%AA){u*8nS>1x2uYKDiMMDAC)8$BjzXW;p~@j6@r5rCOe!=18;$tOOwV9fuE% z{?T5nqd;`9$;|u>LV13~e~;bs@$U~EgQVijJyH{;7Y_v%o7kSxx756;#6}Vf88>%B zWuxwvwu4t}l@c7q=+-PP_Zz>7%e|d2@t_hpCB*<)mhEq?Ov^7mZpz3hJF|z3Jod>C zg(bzMZPjr;FaAY*_EV{dNrhRqIrS{;V;hH!<6mcxl|9d*>LfV!iAofT5I%P5brfWM z(o7=W^Fzsnd(F-!Hn3&VGLxq&7l500;4j@lWo&ezxl7*ApJl{P2Fl1uN_=T<`l#?V z)mZJrgS#mr7d@fNCxSIY)r-%iBtKYOgrYF|Df zW6FjJLo9Q-e%Ikc>^|;XAj_cj^OpJXL{%KQ@oH`wL%s29(pak+ z`iEbepDJ>woR#3q{ua8jiAJd-H_K<_8Z&k^mHmDQ2-UgbA*pN@P)<3OML21`d4I?A z$RMm7qe|)-U%rG_#?Qqgy$F<<~mQAc5#ETK#xNt?BWxw59) zb$6R34K*tyv{u!4mZtIkvY zVBFRkRh0zt_Jlppt!MUVVQ#sTi9s_KPAv6`3dM;C?Dp8adhnGYWCiGjIBfc8CjX-b5nW-&L+vn3FG)lSdFOmx}o6ffKm{GXD z{rgcVfhv%{&P^8EwzR-cO69-YJgAkHeWnxM!D%b;1KDk15S|V@q_^qThG= zTCR5A&6q?qTvzlQdtOEw8!uK?tb&ohXggQygRG{iaY)ohk1wT%rZfTuWn&`#?nLq_ zKU`6%$yxT9Kf=rjj(My{wvx_SZe|qb4d>Kg`12TwnH2O0pZFYg`d}kF9=anNZM?~R zyDK@W`C8tqX6}}u&F}e))CG+A1frNg^%ifne?VEDD8DJyJZyD>n#NA&>dzi@Zv#R1 z)*YK){+cbGsXRNJaC2rP4s4nGO-~$^#8uk|P*xHs*a6ZIm8NnNGWdBTFbirI>glHZ z5X_hp$9O0*ERbDY-Z8*Eq*bt_oK+Xu6mi!F$-dg-<7I!3>_Y=&i;KrETWNNcjXW*v z8WT2F$#+9H)!)bN)5#8PH&B8Z&znZduZe2RO({(m+CyHLv1-04Nj7Hc_dZ@wjhZC; zicgRgf>?Gc?f%*)8B!H7&plNp%g$8WNfD0lF4Xt;@ds++tH)n)3o!Oh*rR@2)ReZM z((z^nlqYX2mMna1NzQK&_=ZX}r{dmOu7O;oBahJYy=v-sTMM7J#3B0ptZate+li1= zygI?OUB73gKl@tJaEajKqOnyhqriJ?J~I7eYRnA%1sQeq{q%mDm#pYdBD_en;Z%jR zDHCl5r9?Behb2E-%ix~&5tX`QC+?Z0LW zDalPqtcH?(B_)7l#c_jf?RX#g9c0+|QAnXR0)<{Ok+stWMy^&?U48=6vNdJ1vYcU` zd|u)GjfbqYACN8KwVb-EF=O%Cdal7QE4>NrYtX98iuqw=0BVmZYD)RhokgU^jnBVe zLbx8Z{m^tTWVoQSD@h3jH9TCOfEb02lrzwE)rzY4&I^5%Cg*3>#V{k2lR2dn-u2z# z{O3?JZ;*QCw%lAG8|oVwT0x#*&s#EYsD9BP@%bxfYFY8yPDDiHrP=IYRwjgM=SDOQ zbGKCd)DOY_(@$eXxg4yBflge0{)r!lht4v@J4#TG%@6-Y4Ok~q27dbBDz6`Z9(Iy6 z1ezJ}bPUKei>#75t;khhzpWP6tlasvK<#?(ofOS76^DZGb&wY@zhYL?IP}CU-DO?< zA6SuA%u!`0-6?nGAeDDuUxgfN&OMM=kBQ|l^G81Bh#wCvAkD7{G4+$*1v8;+ zN?xu&rPo)jvO^0Zr|~^}FT{*6;asDe3x9 z5FXjoi@#0M5xvcuOHVvPFvshmCzqiK^&R(d8{q!W;iq#hiv-q6eM4VXA&G-tI;Q1* zjkx9&z$^0?-53ji%0esa;Vlw2m{FP0YV(rY^uD)9XK`ckdJ0r;Zu`^V&JQiBi1;ij zPJL~gE3<#k_qLgMA<>&k3C}+;3-v?S>fC7u@Oxvr;mB1+BVgeD#SJ8zN=7m) z>w%|*vdW`hWmUmxWh80^In~`?h_=yScjRG4QguIJ+Y1r~Z+Y5iKkEC(+q+kcU&O+V z%pzqz|KDzmu9c9_g1QZE0jgavAxszs%2opBa#E`tju%~L_bRBt^+DeY<=$MOQ?-F- zcD5Iz@ZmAQx`&28O^PLsQjKe?pD&UuWu`S>{QaeDG);CNNcDOG+7+aUAETuYoh^e< zVR;-mTHbb$IBB^J&wkso)cBKn&yXm$nt&jULB=&BX(L7=>Uro1UHs>D}M^O9JKOlwLB9N`J@;4Romn z{;p@?!qhU^kgUMt~#Je3fAJ1NrVdolWzlPYKFv{k{xO&;?$ z+?Sq^6QnDMla2?v+{&0)_U-=9U!tm8bB=0v?7CsiYEcncGNg8@gXW2#gd>aE_FWhZ zFu#=i?Kr4I71zfZ7;3`{uzj%|6X3&c^8Ml8Dfm32%JX~&rtY{dYtX|?HDqI(sG*Om zD0ORaGGo5qz7+c|M&#@#aYoMWmzPMx^%UstV{rfU`0+>l8-xU2uzylqXA zu_%$XpQ`v%_zN+9BW%;>dfC(ab53VF8c3(%qHB6d|6HEf5JcrB$+t&0#A|l3AvRME zg^uo9xZXcfp=-JR+xV4wBfO-IW@JLBRTE%JXbO9_C5jm}Ur)32y|2Y0QQswGCvRhe)5f~T2G&`-a`zNMG?l@?ds~V;}6!a&okD9CP7x4;RX(du7PhXE}Qm_-uT{JV2x+S zpO#>xd@?3L_?uSE)zDZw^R_T(i4aumQm;^Er&RqEAC@*uS|)~=B-Lg(U>qeMe{AVV zE&MNK?RpFy2CapPUk_8~$Gj=7Hy-S{0YP~H2DVG^mWJcj+>3CrJaG+LqsZ4nvp@^ zeT)=9->~~{-;Jz!;+IOl~jPNMm@jUg41Wbwf2 z9HMsDT8(w%<#17m-$O@8ZmlG^z0-J(=fm9G+)!wB++WMfSwn5#`!Y5dSbbGBL-lv> zxgtE2Ac=v{_f#33a*O_B?wX36yZz|%NNw+&5z{c_#_{ADij*iA;8~UZcM0%abTr3c z>*_|fh)-zRGOo9P3p%#L#^W zSg^p1&4{;#(Rgxnof|NF``(Id0Sw9?U&}@CnYKU}%B+`^ZM6ky0@^D|rzZYZELx1j zB84FjhJyo(x23S%RfoM7$N|$!oeLfw7j*HV4MWH-hD&HLwJIa|gwA2{H7~vc^!-la z`-RgR;uTLOZGmGlKY0Svt1x%Br1w*Pp&rwyZ=}DQ_I8v#LOX+%_2a1^AmDSu&QE90 z%eH?bUwZlNUE@*QEj~F0dFZ3(HExiQW(g$zs%51;hLb6Loa)WHBVgmxy~f{UEsDEs zrPFBy=Mk%g8;=i?-=Fa=Da9{LBr14Drp=?Qert-eq7$sdij9sueP^9W4`_Sg(5b9C zUDyQ#54U(oUOQGzYRv8Uj|Di|0EXA5QjhN zMEl2OR}v9*Pi%^LRsytUWfSw}f!zH$b6(15nY{0!`m6!yT(@ax?10I?guJXCq=j( z&=sz=l<1?VWYv}-KB67jgXT3~qm#1VpT8<*O(`=-9DYK3T)jQ45yz#3x+tQu9v%8O zDcb&|r*lY1qucFcqJ}bLP5Bpzhh_>F92Ln8jyAntF!JzP zx9>-Mb*`8X{6j=z==^{_kgQ^9I#htx@Q~n3gWjiy0IKl(3XY+iSuYi|FHTFpH?!yt z)hMigN~P12T{v$S5{sV~gq61iyt!TRr6~03?pgI-U4{I$apUaeaJ=NDqfw1`=X-Bz zvYycil^)&X?SHQ60lIH##-h=zL6#$bq(lCD9_+7*MJk+Iv7H%1EvqI& zT0qF|duu$VU11;2XpZr8@VazXw7Spq&o%Rm;g#-SZuEz)I|?>=JIh^R!=; z>!C!|)I7fX!F$gyW*@LRoj;ZDr8addby^G#54CYFY9VUW;2Mw`>3PopbEC`**=DzIO|JcYs&_!j%# zj+h?A4Ce4$H5Q_g>2vex`R)m|{1Fm$x4)|fY#=&*zl*c$N|S;1dE`Noq$n7NrfhY7 z0L;a?8yhj}lw0E17%iD*Gqr;rus94BOT8Qv2uyFVg}x?N}O`?{%B6LqJPP$QxDezLZYO zJ0jcCO}o-49_!v;LecO5nytcFRWI@-^A8exhE#{{0FxD|Pm6Ac|K^ttr|Xa7nSbKu zpx=ql3Lv9qu9k#sI!B>Scpl!PXOk-wo7SEBBQ&YUTjPg}&d0EVG)d8=2cPEZPM-d0 z$1mqJFO6Zxh6h=lAJxYxY16-L)SHXD%!z#SArrwMi&VBwb=g1C1g-LBm4;)$%)Pdv z-fubJN3&s^RbgYprd+S-?c};XWdV?XyLjjswllea5po5rYC0I{3ImqUDBEi4!hkm* z-l}E4O_73BK*j18aqqbWb`hd=D6p&@l7oDQb4JepuhI23pkoXiZr0My4xzBxtt-4V+JOEw)R*^Vl|%MT z4850|YI`w4E*Fs%`rWj|uJh2G$y&(RwFFm&E$iTwmTrnA$t(f_0j=UknJ?4ef;ge` zy-P7>R@^$z8>h-YhDy#dXg%;em5<#QY>dwVX#KHNQX1#sx1fLq-r$Pg>bUre4Q_S+Q>4m=9c8;ky}7!CKsEnq zukWf)K|0h=BP7&MmsW$g8b(A`D$al2pwb~B5?WF)qk=3g^aZa~c|7Li=XK{|p+xS9 zS4eLuX|ClYUThLG!7Bbw$6iJ*Eu%|O&vb}^ck%GnoJ`)``homLiVQ1#9~*7rzw$+L z5k>@Iqx-p>53m z-xnSagOPlPJxVtKH<;H!@G6j>YH)iDO(o9V#<)lNSL8Z$}F4MSi**oU!TVKpmaE1?c+GJ?3}Jn7p5pu5h4H8OnZSqeSey0!_UrVr#$}W z{8dE++StQ<%(Tb5sSjAZxe}-;825nx?38LNcY2h&bdIC(RTF5frBQKKmKR1TeGJf# ziH0vi#9dcWmpw*Qn1Z8jou>1&f-Gf6zn*V6vp-C}8&8iHnzyE(3WPH`bSD<;^0F-4 zS-vE0wn<_Oh&Kw)(Ax{IolImcWrN`_p_BXh1qcsnNLRI;8g$~Dh{>Z3oW|AMu;Dy@8jd=a(^exIU@l?jTMhKR4UF< zX~n4*M*-{`9qOv@WgX;s0=FCpp;$0Lhj?e;DaUv*z1AcFWqJW(X#wj&QU+W|)g2S2 zI@`a>_T-VN+f%g>v>L0jj%=Nuj&Ayx{yDaj@hwG1tf8lA{u`_gk|*u|{h={3>e{0Uxd~MTQ-~_Mk^A@Uw4l6P zHB#7BaEA~7%3&oCq_^8q9bIoWEWRix>(_L{5-?LupvMzGmw_A{V^Oxl$Ry96^y#|Yls`o)RQO@r^!P4XYuR)Xb|k= zQ4Z7{TuuuI7>%3d%SHhf;%i)75^^fgrA6 z-{d4pZ=zcK#6p>_uuRZuV0>I=If8{UFS#y3k&PbXZl=ztSjK_Z-t8guH_8n;ybE7* zwcSpD@Dq^k9wDwbCjq}K-l?;POaPaA6y@P6Z>8*zj^&b_?RR}^QF^2u=*1)slI8_D<<}J>j z$#DC(=Q^z{tHK31FX1D2Tx}sSStYe_H4ctn?S*gA4lR5i@bG@P1MKK*Ic=7Il@uxd z1E#A7KCXnkN$Jqr@3@6`7_}1y5=AU&?pNW>sp!uO+@-r>Z^y}V27;R{#T)IILXG+N zit;^7`S7aBw?N7xK99{GJO$kR!F};s>Bqa{UZ~5<*;~+3vVfHda7u{cA_0Z0JSv*5 zb%7&uZSA9xfLKn36{)MgoS=SCo-XBjp0x*o?N8~6?e}9-M!vHc{Y1AbV zTsop6NKm;pC7E|aIXcHXe9HYBsa||M()0-UPnFz9n;V#tB-)`1(D*e7rQy6@2E5xt zRfzLJ(%tii_SO5b^>C^9{mt_8X7;q?kpXLBEfc37kkcF$S)asWlZB|mFWwbAwaC6V zk>^hyYC_pUiH@=31wYgaw5poK=#o-bOLnfuB7|S+!Tw@gbS;VSDihhX#up;AyW&gi z@4pP-iQi^}#2dJ&ok$4;HT844C%?HPxtiY0{>)~_XT?NMvDN)}Mdb)l@T}J$FWkA43mPbP1 zVXl>B+>HUauFP`-vn?)9o|)M|jam!eYH9@K|7m`^1E_>W<2KEtl{nZIuQv+7U@}(8 zVQHobV*=AyX(l9$YnCyO_-F9y3*xbZp$TCAEUfII>UMSi!0SD1{XOM(5u)yya6*|R zg_P5+Y<0B)ddI!k!Jm1uaL{`n{6oAfz@wCeMH~)wC(_9WBvIClpxUybcUCNmn}UM) zSLAp1PS`heklFDTpkc^r*RB>07e=j12)5bco*hY!em|kQqbn)G`~;{m3_W|HyK{xVVHKkOT6$w);4_2QV!07? zRfpc0e_vT4h3CS_vY#{T(Fu?2l@k9&=9ZkLy@etFyQo;AR>ax&zc<~+wNm9-~JSVM(FHM3s ziM)r8g$swbhWX<&zCrC=DytIsaU-g30an9O?zxMAC)u8LopDgJwE4DMI+Yk;usMQx z24i~&xa*v@tW*A@@wp3B;Z7q-I^t1^ho+(=zjf9;!q#+^`6g3>E}&~%_2-rEkDqk5 zD*k~yFg2FN5k@NlJ8M%Gx{% zMW3gOyTQ&8h1U_g2LfuR)pg}|6Mt!!)IS&HB(h8OxIU>+q;&sF#3}k6N^VF-e;CC_1PomY9iA-4N2 z_P!K{`!|?BlPbIohvvNta`H9`SvIAaCn#qUCZpu~h%<7fmAPF^zm)TT*n7*UHlN^8 zID`Pf-QA@?aVSo3r$BLcibHXCC{A&A+T!kR#ob%n-Cb^e|M$G-e!b`1uQ%r;Pm(9Q zo1K{*+u0c`?I+k_2PV9628-!vlLd92x|h?hWIkVsp&P)Eu|0D5{HcinmytiL;|0a7 z$yu1oe|?-jE#``RhntN;c+m?vkOKk|60bOYcF3XGihnbvYc&qmX2X+Y;X1bwW1rZ_ zIsO!qRU0c|DT&m{D5|Kj3ylfuONW;pV%$QIfFzK}s`&Q{Z@kZy}_E zBNRsB@Wl!7#R>BT`NI3D$-=J3DUtu^J2Z7}-abNnv3A8REG2D5HO{%1Q0+Of|B!{=7E~{{e0Kr5tTFMZiLY9V0ZaF~crzL#|MuC8|$WYH=+wjls&oltXzD8~(=SWf%_~2k)ekcNC zanZtk17cKlN?=>}iBGTzY~@o~=d(P_-eqtO|0;Cie$WtHTUqO5A&O!gtyQRKsI^<2 zYP7SeNQN(N&^YM3&m1FylEJxEQZE|{u_&WCv7EIzLF4r02srcvcQmD9kPDV{@)(=@ z1qL4HnGSB~1$4#<|C*^=%*SR+6gL_WS8v+7OH_ zE&8un0>mzD+hy~+$tNBIFu~C%u(JwN3n>!E6C!nO?XB?e)}$y!uV7L9Y-$*NX;2pN zU@cCHxoL*%i(l^H^`kM_Yy2 znG|!YYSiLH9UJUCY7CWt=}Rekr2m#m*5BzE*L^Nq%R>9IiGJJSL>?{KVq26xDVr#X z0vm^Sv-8Xa8o)z!WuWN3;}1?IjJVaMMn$Qx+J;8k)j@<61fAfDtQ`OSIhgG8!iJPE zLolV+&%##nZ->JlpzKz7?7=P=uEX}S=QO%0ItKW&|xhQBbAk*rEG$H5Df+#qHn*A9yN5PEq1Me*000pX>-KG-Gc~o&WaCd( zm!2+k&bqCmKff;SbZ)ae3anv?A_7|DrH-_Qh~W-iylOQU&v7B(>3b!v^7F<`ku~ok zmSGsan##~Xr5j7flb?Z5*7xRGK<4W%X79`riea+RynG7R>DiRSA^ea?Z83|PY00#ON1^|YKUi6%QT0k#|jxyRV z003Iwe@~bpho5Hu|H%`Ntdm#w0CQL0_c)QYa~J!}&bp2oW@H(M4{hHLi7;zGrpyG2 zTmWg@yHQv|XNorqQjjnamN=hkoIU+k_Ku zPfJN-<{00bb3}ac`P2345UKl%Y-vvG(Swhl)z@=dtbA7%PY!IyZ_I9fCmGHrQxaJ! zvef(&B-2e`NBvZjnuFcz$IjT;SpC%|2g5GozOYn7uftu-T;IFphD;v2zgi49TO!T7 zaU5AG@kSYi1{MoOdx(v5uU+r2u%(rHEzS(Nxeq^k3W@#6*Ll-_k~9tO>p;jz%6VO%iZ3#?{G*WC+T7x{Mn>w{)atujbOda7?&zBt41I8B2+ zC;~f>HSv6uy@0L7H5oT>TZ6pT7zxV?xBQk8js9<%fZgrJ;>Wz-Zu#v1ul^jz!dEt+rkhKA?(zdfGo z*LzI&;^+8p4*LkNE5Gt3Vk-b{Z{jRZJ%ONEgq448C=cUc2nW+@|S%m z`+D5L_xe1?V=MBReKLV!L`Vjj-Z#iAvC?BN7h@(jrsS1wobER-Bdsb*mo|h7Q>&B5 zST@pp*lNttHZTcK6?9&lzC4a@-Tn&6U@sTVGxx~p?!uRUnua;_PYtzOEmGxa%%)0K z`XN`$m_mXk^lh2_{c7I-@izB$xlIJA`K?QTgQgsRmZOK^Cp~H&k}$Tyt=$Rri+Z*W zRet#B3@XL|OQ?uOb=jCF0ZJw9!-R;TXG9Fm)CfsDBtP_AeGX5DVO=CoEQl7=dyOH7 z+5C1oHOJPp%ZHY((7K78`|xnTH;`M`-{ct{ZS=M`l>~M=}WxuU6PHuTBK| zS8G0^5U|iQB!UTZ2FT1Z+VR$j$Qs%`Yk^W9!4>rI3XWF|+Z06vGb->wOW9HsRZSvr zc18Gct+Kw?d=CZsKTU|ddg$uvp5=DlD3gCk`OX>!3d#rx0UhDE$^-HnKVbq14ij~X zC9!L?hExj)zK&7JtyehdfBM`X`H`SI-5W% zN+82X5+A}b&aH!yrUs%yp`!ZIxH)Sr!DFrep6Znd{ICYUW-X?A{o*mFPIe*ks8ZBtrN zQ89*K-za_?^92d<2<)m1aHSY}?i~1&XZxejr&1OrdGTTGMdi!=BvJul|1~BGrOfDe zXQZFagK-L4=9uu2@pviq)hxuTwbi!B*4lnW-Mx)!W_8+)4(o%G`1&4?DohmNU!IoV$XXNKR>D!=H1`wqw4Ta?6K6^q?bbZhgfhs%*WBg+8 z*#vlIXez-u!&)vX0G5%`ky!=%V$jvwB9GSA*hPKgkFnFWU8@b{?YxXhU2`5~d0!T8 z^F>pkq6}=6_=nZ~hv4iny?p=c)evT3wDz@CF(Kv&*`Qg(UDT3$h9amJ9TTyC5X~7J z#GKco6@4hm|(G^GgS zO@KgSG*lNzW#-u-KjvUxB^rpZBV9dW7pf{I+8YkrV}SI3vl8_BJxtrlUOeh>?_rZ1a~3 zpVohA`ggZ&Nal5rFC8B*s-JIl-#dHv{ zb~Dj=oJ{&2W?L>B=_f9Pu{hS^8_U;?w2v44KDZoHctQ2Xm|tOMYGsONWvr(c7p3{L z^LO~Ne6ASj*W6a^+(}*MJ`brX0UI7zdSD<>1oVKYb)T=Ihk65Q*ux(SGSrmK>W*FfS-~lqrjCzZkuF|BgH+CXV)B5z;-?oM&XefT4 zGxY>Q{$Y1F_uAdIK60LPent8+9l6>0;jB6;hVUZ^3j7S1g2Z2;ZR@mRl1qk=yM%2P<7RF8l>cx5J?^T-~n zU8wSP3`>19lmY}6(f^z@#ImIW$(s&o0f})@;Ne#Lc8TVzP?)d!ZK)s?<09j;=y=US ziQIEA-7-hs1WkeQCEl*&L%P3dM)JeQ?J%uC6N~5B1F1iam5NnEP zZz8*CqlCE2e~5VPB8=BqhT;!_g3Jb>0E$~kazwP#|BUCn>Sc)Ynot#oe5ds`^M|bJ z?^kQ^{5r1whmrnYS)h@7M6t9_3|qXIZ3|YU?7++Sa0;w9QDm8AK3asByV)?7Tmps2 z?()04J7W9h%~E_lkIhwC86!D1RPcsTkD2gfVGJz<^=o+_oCLwSFve+|R4AWue?zp^ z4FrB%+E%cuE031N*7~sl#M5XOj8R|T*r@0KdMna$Qak#V6#_!zJ8C3i^MN-%AtaDb zcc{QxA36RSgZ**0qFkHUK1*j2&7FB+Z4|6h@o^e=ah^2?yEo|_UNTTj+tKrLzA{iJ z6hcIX<`rgD;LZ4)&W*L%#2gi(Z)5i+l!WOKDJ2`Ijzx>}Ko_t;L>rA4fX#(IoY%nW zyldT@e>KRFir8r4)iKX3=RU&B^~zia66`fBxHb$D9==4L zeH5U3QT4AYZ1fqj(0xC36oT7YX`u|%DZ)ZL`|}*6opp8Oi+RV1WDk#t7569{DEgcc zlmbjTr>~|_%>)M@wTUpaBYUmXkSO z{%FQ5C>KT7N$lnv)kzFwUSSx&d{3ap%y*z?`Qo9%@7VZiGS|M;lAovo*LTQN@bdx3``UpP!8oM3Df=kCzFg45wgb z;7&2$^t1@H42+QYBn7k|Sl>+{d3*ca^-jOq=wkT(wD^wc@@2--Q6vva?qYup^Q7%? z_!AZ8u3*)DrDJeIN4p9Okev?3xEc`i`=%_ZDB&Q)ETWH!QsO@f=@1{bpA2{TXocTu z;(X~c$9a4nM-4kix}dT`R%#124HzxoMtE&%(6 zl7PiP@AXtt5oL-9s;}lRXhfu8aG@CBir%n(q|>rHekvG7Ou5V()$&#L(WbUPX0La=wY^tWG`Ql))=kTbK>)L0Q>wu4bxd%ds^@LUsvd$y zfCVG7zlS2h3^yQ9R9l!IkTBMmigK3*gBO9hE4ap7*NlUZWN|m>`eXBsH}Btzy1TpO zYnfK$Q%)>kngv0Wc;!pc6)Dx-x{f!K-TP6^#i>6Pe1uy#k(klGW-Q@HhQ!SpnIB*o z(I+AJiYfLqsvqN9UabF_jBvbb9^0fY`xE*Q2=F-sib%pilLHMK7oFK(<#cgo&MeUft9GR+)`AO&wQerp`X5(xvgtMF$=VHj~y?%R6BNc4F9rM82D*} zae#e@#QQH)j$M_R#tGd?B;uKFOF@nA^nhiYYG3Ufq%vpyCaGf@FIy+Hk- z6aw~si`Q;Fa3rmTaMC~bVmeVAQgCGdA`0_52)sa3&626W6D3vW^LRzw`Tm-Drqgnj zG*3gr#!3Tn#3(!(#!o`LjnIs^x)$GxK(uQi&-cmV*i4;|^lp&p)>Bgi&U6{b<${E|a4=DLEIMwlx4eHnSFFn>DJcWlsVM`3Ng8DGoGsJoFx>$QiNv3M>qmZ&W3OF| zC4@80Ds9nVU@4aWs3EP?upNo0TV_^8+8fVqyI6laeG-`9KQ8A@d;kqXk!%6@tr+~Z zG#AD0O$Ho{EIk6pHZ<0Acjia*ktT1qWR3^)~PWRX(~>*&H*_hs?0N|%z`~%RQ3-{1 zgz;O3V($?BejwYAdq<+bpB`uGp@*YMSJGrk>%1liR74RCAF_T(j|>+Tuf-t+TxMom zrOO^StL(U2$*gSN`rb8AParRo2bAUBP>`aq6O*PW_$^NAKnkrjg(O3Dz)bUkR?2SL zO;W|@FW6+Bf%Zrm0g~z3jF^GRPvc$ToDRGow#^l+4fB-KSKPM35;(0vCdO9td}#wPzyGB3jiQ|#%6Ur= zwL0nIhs~#ToOag@9?=)|T*i4{H-qs^IW@3lxS)hFvTP6{z?Hzd(e?eJ{7Pf@jf*Ad zgT#&+n1cQ%O#eA21xy{_`|l~xm{3N;cdkEI2)g~kxYwS-FPm{b?KL&*_ml50ljJTW z^j6FT42F2-QWTT{AW;gvfGJ~aWglbS7@?R6(sLCOaLFfaDwT+Xtv|ASgKT*VM8!#y zV*HKK8J7ZJLLAD0!McJ5jHD|Sxy?s?SQha4z#X|O%uaZi$GP|VbAl~v>hlbK*Tb}r zaPE*uHsKzL0@x^wI_t(}X{Ddu#oq#5{rv$#z3CN*5rR2J%YQ}Xl@q?*+FTjD-OLvo zeB8`e8GOvwTp7Oa*_;z~-0D#ou1coEC|C@s=u5+~*qr{xFUkLd^2c!exP|o}?)YH~ znc^8^nm$*3J-KG5SqYn~N5ONCXMwf#_;GHZJ#yje{GOg3!evV~=xohtR4(aoXx7Iy zN$F4!Fu|nESX-H32t}xw6@&77Hz0N!s^pJctK>i!%`NOI`X+naWf{e61dSDti@9=~ zKj-w`>g6({SbOAzYm}H!04#QgfqfVbS?&AZy@AdMsjg$>lu zG3f~kh3fJgTx{Limf5a7|LF?vPdoG?k7pAT6Lw=5SZN+U7KznHJwT|B*Ek1_ttCsR z^zEsS+;6g1G73-O_H|sMAIZ1R(I|<4;QB2!n>Y>!Lkc@6+ZCu{KrKrxFMPB4LEr?NOdxi^~_17Y@z-u_gBYOYD6qJI>8GT zn5u=iGDYf}pGJ6e#F%ic5teOJN(QA!7K^nQs@)y%+uS;&FBnp6*5yHcjO^_>=EuJZwwdP1ifmZ&7 z5=sUUv9*nz`C7Z@SSBy}P}OFUof8^_2>~F6Nis~;=)KfR zbfq~ydl#$(sT>qTquLhmMI)TCFvN%*H{IV7z}AmZK}&vWDqUNXwY_lSw*J%c8}qeA zRd2-D6icd%;vnl>Fp%TV4hkU`FE6L}1j*iHW?cM5FY@Ge>lx;H`&nz7Mx~yo$~~b1 zm_iJSjNB-aY>#aq>{L0ul`f{NmYXQ}?!qiy>Bq1IB*tjs zWVuMyT5nPR;a6GJ+rWQ5ZI!$KnI<9AybmKtBW;9u5ZD(jsEeeULc>C`C(lz&(nDXobxaPY$6+Y^oR=I) zBiD7KC10ow+5@lLKRxe-!`tyEJnUn4^1kyf${LEp=;O(%^pq8k)M5nt07t86>TNSs ze@XCX+js0`*w}ZSwH^y!T=b!@i+Sb*yr(LQK;dtsP|b{7%q@UG%5#re)6vzR(=Ki} zTzJ4lQY?-b4^&l6tfOTijDkaDOyMcjUdt#J%B0JNpLAv5M~gjvgwV4WkcVIegJ?+a z7y;`dVjIB`SJ;qEo?J*GcqTOx8-eJ9#UR74-!loxwbjhHUs;P)5e1UJw(n&quWK;l zMJWqj_E)jfVBo)4O9K&MXb5n#J;N>UQ7$2i7^J~yaLVa_0h{;7QM4$5>2t`HROepb z_DC1p_c6glYKg_;gi2Y9-8FT@R)3Z~D&_OFdT$jn9N1=~w78Ah&nU7s#i=4lr}LYi zb%Lw(9#$k<+>Gwz8L9JA#WoWljDf0R3^Na?-v_9EEu;)i`o3L@y#3u5c0Y8nSriVU zgo&$NIXn!8GH#S8QdnBD<>|@mY;hERvJ{7F%Zh1zX|Yc=v=qCO>$J*s_6|9z_j~Et zr{=psbV`Ox5-!k<9Fxd`?S7IdDjW+^8IE~~ZR~LxA;uyKP_YLoI~l;l^#P?tYlg!t zk1NG6b%Sx8E8fpJ@!W+W`TJNlr{%)F@O=xp|BETP_jaXz8;`1iBws~*5s>~T#D93Z zDbu^zrq6)q9`_YO7KmGPs)PZ-NR(MqYp>; zi-h29MAX=g(zr0egH@~^PYhKRY+yQM_M$`B#Y&_JzZ}hxQ)aa3N%jFv;6!D#an@#` z{E_AJzwUa736KUbHg~(KC?r^2fgg}1^oVJKIY;nt*IL$KeI2vcGC|36hvPAnP^aCI zmtay8a_rHm%l3PKUEhz&+weHg4Q7^A;o&WKL!dzlnUsefaU6~LCb0BT+LBK*7 zI7vn3SG?RN9AT93a&zSn0#t{_Ru;LPJ16(K=rM#LEJ;`UPmM_u9G^5i+=?TGHM46` z{^~~y%)hTMIr#cLN4{sCPi~u0gk(RrP1m~fvL9{D{YH;fpq-RbjxSbzDwz+fRP;mS zr?-09FGxC>aAyFP5JcMVKcf=BBoW4c*blgWitiU9MTmowcv_%a<7&T8Z15^S)Ub@U zJ{^^H-HmZh3Qn)Cu7*yV36(_3#?t`D9dU%ll?_(<$OYj4C>C5GNQ%N~PN#pYDeU?) z5lH17ruQUICaJtR7((6+CF6@ie7EaaSAD45QDqcRy&hG6`&mLELswgLGY-cYOFNE- zOEPeT7R0qZ#fx#mY4A?*$iN_orAqZ$-bXRakX`#($LZ#?bk|uS`Ri&K!z50Kbv%uF zE-0^IYIb8N^X#I2uk@JTv(v?DM*S@h#?gP+d|xy&8dpF;`UX-C%iv@f4a!Mwf~r%5igI4~0nTWY8hNN_`XQHNGc zn}8cCMO)hcymsFiN9O+5sf45wCIEy0a(6h{OaB-AgX)FuBLJRsV4?>L z$|pRB_^yG0EHOBmy%;}KAnJ%&`jz&;&=Ov;dCJ7Pi((cx!|M& z^WmZSbO8TfMaoi&(98jgkW z>*~lop#gW{3=WFNUe*S&di zxD<7@4{8t6M2t1oWu^G0H|8xZmR;wU)1Di9G(L@*t@qrLvsQmqNX<536B?_V$U&@hOlt=qtjy3%%mk+AAB}qPfo; z2~!N_1z@egX)sxf?ZVq1XhzfB3~n1Tsbf*(pT|lNL>Lm#0c&&lIb(HxX?JB07W97w z;p4QYxdeUv7tN9Y{9b9e#3=^$AIm9_)?5>_q5JXxyJI`NA{6}M=7R3<$rexU(`QoA z_%s|mB=sMG7sm;~69Yv0l|l@G7<4HFAaHOuH8@@jRZOLtCc&>Z5%S0`Q+SCZPs`eV z(JS)lu;_<|@b%5CGDZbNFSQpP5k|qLd^K}FG8SgR9iQNv!p<<(=U6`$7=S@136tELJ#qXoqec#KM<@R;tjVCvTiMB8U>Dtb2i*lhafq zxZtRSf^q|v5W{k-6tY<=Buu1=sgW1Jh#FS|m*x8PnTY>6r?SW^U&6;c`-Xsi1UA%~ z&eHDtMYoIKefR6JGfKG}I#XGW6xlk`;X}IqPUJ3}YF>vB|8}&_$nKP&3Ci#`UN>p$ zj^t(kns4h@DQ3yS!CG=oa}_ir!01OLa97z%Dn=YyNX3Y4lu3TSE^Eh4?ITn}9RU02 zKLepYB&maso-Z%StoELj!J2KzMMV^*k*@__=m?CUe{iWCaQ6Xz4htzzjtPCAs-C_* z!?!|>^-}>h*1mSelYt3NWT8oAFk%o(L%UOyfEKJ8+_zU;^3!T~ra0qP-FOzM@D z=s(Q=Tz#ObJDldtWi+zYMd|yeb&VU-TbgKfD-aee=F%?OI>krw4p-|Hu)6 z8SuylEv>N#M;Jy`LN^dO@Eu!WsfkSqL&o-RMr90JLx4$`4tF)X51`)NIoHd$wQ@S@cNqt{Vl39}$ zis+nHaM>;M0fMNpGf9Gq)0|bYrB1%m88g+Ee|prsMoMavXqQ$r&0HL&4V~M4G^%7l zPO}s|eVR=ltLTClcq!x{NCu|?ppXmf5ULna>wzrK>y-)q?J>@uE}RsRz&ZAuCGKv0 z;?|mhoux;MFkV~-T4gmyMI}E*<}t+Iy9L+d3YHdgL8WNTM~0s7p30XeIh1G)qg2Ha z+(?&`n3#wXilRZsW`vz1dMc!Xr{C!sA z{7%qmei0cTC18kVn}{n13K;%7Ud?{y(a5{ym*dp>M)(aEDg{#ir4Gz&8y2(EwpCgR z&-cyIhHmV3h>HrOBzfc@fc(W<07I6ywed#GyZ|m$a6LKs9Cu`Q7n2eQ!v=_l0Rzte z3{i6?Q4%<X7b!yK{I;+qUB^Dczl}w?mzwsYYU%JKIn*QX_=^Ca7>Y`3^?zyy>PcVrD}-I=W#=vv zO(N0rV)Fvl?M}s%s|g_(gU7em*YkcOleu5DfM=^K#BaWhIQ3?>Xm-PnM?rKNA8iX* zObe64kDFeRFdVqa9ss}n?KW*xxU&LP_C}%xk(F*k4L=AfJN>O0BFmnzAeIk>F&q%L32v@~^ag2)O7Qde=F&!{;DJKl*!c@WkOa#47GZR-0$3j1Q>``snA-f`Ht z&T3T4BZtZ5ESb8}dO-I(0;PyP9$_E_ljyP>AURf?lBROM_GHR@bnl~qpyg^9>MFxM z?3^SQbQA0dY-2zys(4d_L8r8??>LI(VF3h{Trk?m64}Z+^z2$5w?2f2H(pJ}`uX2( z=)m6(>Dn&29||dv+U(!EB5q6D~{d8)t7QS`|*(n zyyuR9gBCK9>{i3c8%+okI#o2^q0(v&5o#L{GJ9_O@keX^Eqn&x?<*ku$1mmIy!#9) zy`Aad7RzrfDNG<_?HW>u-v(L&f2e3+isfPUXNQZa8@;u+$eWf97?zU!*O$R5BZ_Gu z8agA$bCK@_YqJU!hO2~`?BDWam+&i5&cf0R6TU{+HF~t5vzDsfrSBkD#2u;c7<;%1 z8RAc3D77ItCRr@z^?9sZr#q&}Ui>v=qh!@A4o15y^%=#$(#2` z9s`G7(yvo=1|Nz8a!T7DZCU@R-8XnbPtd9oPhX=kAX92=QMM#wYj496;v-v?sNSAl z_eS+}qRf~K5lDmje)F&J)an^1P`f!J^wZMH3jeBHY2DiqwQDkZd-qDH7A&ldvEMAcpajEer9(4rKw|+aB(=7gsFxP*IJ9 z90rHw*F0^h90Wgo-&E25hEvb1TslLj8xgiqFZ&*R2LqiDfY$vZ>Fa%6*}eZEnHS#B zu^2@HRSa!ip8eThNHxVs%^}eBpJ#0LzkqHDb@eICrQFof zE6~=LV0COUL3XxIHE^|>TVsBHJ|g7^G0q$e7LWkp3Hf+w;nncus3W|+ZWi1zW;9yD zu3zP7bJjGN&{A4&G4(m}vC##MKow<9Eqy1C*~;f)b%kRkU`dkK8BJh>fBuj{kxiN6tli zv8K}93BKXP{QAR%ZQcP2@@+VRr*K3%!jz%-iKkG?`Rk&9e+H`T5Wuf`pP9W@o^zw>&zED(-4cS*)Y7SpLLe%`RFY8Ti)kWt_!B*fu7BQjRQgrxuZy?*yTzax9o+{LiTY01zA|q#yE@`QZ3&T~) zVqtCv|13{U2WyQQi>ODxB%NmUoxSucsdxzLK;aZc$aim*6Ks7`y1Snw&F!fH^>_Jw zqkauRZ2Z+mHyf6`p~&lp9nApb=BaiK{!n~WR{qM-;{v#D`2!8m7CH6v&QzdmYK&3G{(NO z8F<>l7j0=|yP8*vB~%8`Hbq%B8YoVm{;ldor;z;q*6Rh2G;bj~kwU^tAFkF%7s?PP z)UzXMtth`zLM!C;=4uY7eCi<@bA7Dzw~T!oabsZdE%dGTgBI1$8#GWmc-%dvzVT^cI|o`vJg0Jz=%o1DmLqbFI-1sBQGlO*D z`Ix%3ChX-xY zI%>1{+%CO;rI2d+0WpZ$+O=p{e@fYt!lZPahmGyjd_TAb63PQ!LtVk_gJ>kru#jrM z`}>P;`A--Z6Vxl(b4eu!19jsRv{>*<_&fRo1K|@}@p~gHS?Q6`X0%N|e!X0?Boy<$ z3lh7n>s))NjcoZ2jhSmK6zoWHA8PB;T#1O`2o8L2L`fz81k_3nQkrSP)jO(laKk*_wI6*mX|-v?dNGNBnv46 zpTDana_}(=cDb$`uekUeMi*x#_P^MeNWnu`U^2ax%$)2$h1c8meEy+*C%|4*F69;W z7A#_`llxKmDY?{K`^l-atxVkluFqg!BMzN!#h#zm_sJ{$fTpPJ+bXb6cu>uj6C7cD4UmpyLX^>sPWpFl}&PUUF%;wu= zHPwfTxS?GAnea0>BPu98*kr8J2*9EGhDTQ3p7$zDDrX~Tx8nQJO2G*V3Razk!S=AZ zpQdBqphPUA3-`8kX`#W=p1WHyse0qA<|}UnVk}GO>HX+o*2k(r=FH-fnS$c&H<*}J z^D~_jkGrmEH&0i?&;!l?H;o#wRLp&fh` z!!E;<2vvmB-255b6dphe($-D1dcYovrUeAlGgo)K6P)S!#$+yj5iF9SK(4Qw_H zty;tbQ_ab)nQ3Vcnkm_13Hk4_e5zaTW%AnC+TR0VcrIJ zo@)n#Y~y-!QIyatZ(ZAaDOZWIh0=A)n2mv?jXj}u8<`IQVhJ`DFoMz)wYKA(!CwDa zk(FQP=g#U<@wQc#Tus$k9r?jA(rb;Fv%4GO7XJ>HKNlLkYh8Xf2ddPjXsF zD>#}H_CL8_pDa!7`FmTjeaV$ax=Ez>7xKA<_Na^52_;GLugq#=?l9eY{vZ}14!6jl z%)LX>p^Np8*?r=f8mdfOkl$cN%l_j8Dn`Yey`PJvP4*NjX62MgwH{vg1A^S-AH>1b(MC~7h{VY@<>B?e)}1Y+n zyV_UBR)qFz+$KG_irciOngh)%CY@xik;iP7ATF&@^~^9xp`ilAjQtRS3f|0 zf>n{a7Q|QO3FW-}0#QF6tEvY$-#5Gf z+z{H`W3`YLrF{*_tPA<1W*t~cTUkrMynDdHRp*_TZP#(VT<^|K+=^FM=pfu7Ua?Uk z$`oH&bB;{|wYp`4aSY1);*I`To2yy#5PK<-JgP{%Ai-(ukyJto-aqSM^Vj@<0h4efPrTw+ri{q_O$oKIxqE#&0 z<$rxCi+4du0c?;8I4f&|o~3h0x+0U|2DY&>8xJ%eT}qk@f;~&9<|WsQ|p@13Ic6I%@l0W=`UHHGCX?b zJT}M^1}V3i7mD#lPf(aUQCS7MZhjA|eZ{Y|qOhbdyQ51pfQF!DF?eQBTa@zo`8_(D zNKcMmw0>fgi{xYzvzseUWq`Kpz2w?wnKb!Q-gghZo zp1*hLlDcm-E`x<8!=?`fH=oK4*U;o-wU5T>+q1l`T2|5yh$c-6IvU9Zj$X6L4mXfh zFG2h;1R&MhIVgvPYI6l`MX+g|wo38IX88}uagg%U9?rR=w-Sy|m3zyqvhgZQlnPqp$fEi=#fe++FCuwogw}Dq+`LLV-^>5|`!*zGyPjQY z+XHp&G;-qCd3X{FG~N|iVy_)<0bZpcgs{YK@w!I!QIw4Ye^IT#NYHxEjpU9N8lcRjw}!qvYn zEM^wRvPNc(bQ9!pINTga75H^kjwi?}WXV{i`|x zHDzZlreSeTz}+)8YhGiDc zm}**9fAR0fFW}I#qz(S+iQT%vWoh@5H68EUCt2vlXElIKkUg@up5Y&Ryr+_JDC-v2 znHk16JeCsYZe?WVD7JDcV?bpyK(hxfeBSNtUzc^nT2ODwl%eY1Ca0f!p0oViS~kM< zU)K)yQG)}6hP*qk2y&lNhYn6ZYLs1U-W%Dz7g!d+c`ml&30=pr{hg#Y`g8g4bf3F5 zzW8@&;>;|6_L9m8hz6_Nmj&OWJ^Fjzzj(7#(NpyeHoF#tZp(BL_(S*iNXVpKim8Dq zxi76y#}m9)2AqpC6kFQZtv=@geF7ry-eeE28dh?PZUkOPNMuWid&{3vpdQd-MnxXG z&(1in!L*IvP-@*LF_HxTXzePAvK7r_?YX1d?-o=7-ThAm(Xx3PLn=UsFep(4WuASt zB-a_g?Iu(2akQI;C{M%tW>PkiS;O(&Qu&OENj00ulBA3E{dazgI%Ly)Yz5S zHHy89Vq)xtsIeC`_TFN_h9&l1v4XuL3L;f$(tGb5y_cgO9PqyPW^a!JD~g|lu)p8j z?%v(*&d&d}dGF24xI=2X*Ooo9>}X>;`Krs)V3Fe4Tj%Ve)ppp|($LAAr#jN|-j)UJ zHRtPHFx1w+aQ^gV8$Dy6{igb+7Mok{G|C814$cjmv2}golS$Pktz58ck%H#D%_`>N z?1SA`JUJEE;o<1Ns?`0##pbZz4A3xas~YO_VyW50N5Ww(S6!3u)PH-6EAlOpuG`d9 zy)icfb6SI@?TT%h=pQn7=an?`-0btg+u%~5mG-uA4>~Bq@kZP8d?y$s_kED|s&5m& z#(VaT($_QF7+qyuz9>;W<*=sE#(d|nNiNNsJ~J zrU16N7%OS$6Y;lKS=u`*xko7x?vb)qCoqHJk|T zsrB4JD>oWsq+U7sI#}q{RP=VHVzkIy)L*e6`+WYQkWis$66o*KfLy21$+~ZETGzI7 ziQbp``s{i&^*Tn*?PX4ooMF#HuBi|!r0@^*DgjX^h^o&Q&`*H+)!?1?Ro=l8b_zhRJXfw zJSTp5{TkSVYKDY5YLr7ajj8=_8nhXZm7_KD@FAV7nR7O{JsCN@n%w=>LN_hNR=xXg ze}32Mx_r?7!rQ6MHEX)>+R#5ECU$$`3m;eW(6*Y|p@sqeVGENs%)NafMysabCbt7~ zU0(coP_V>3+@)#H*jV^`#>JR^Qsum-7?{7=jc86yDjT3Zx$=+)I-_%NqWaCQHoQdovX$V5gk ztNex?Xrr*+Vdly8wFjj>Z{@c2!sA*uqgI}}dOqU(^2yz!w%!z4^ff(sV2OS2QG@UF zANEI!Zi_c(JW##!dZoa-F#ODmjw(jW`adare7w-|T-O;bYR_re$ab1?_HUh^J$Qi& zZEoGvyIx(-Jj-D%51qN=Y&*41O2{8W`-{vLnbgo!cFA=bv#$RP1*5{nsz23gFDLio zYMO-9ej-C7Ul z!5DvjrOT!)?bf?Tq$Gn`!Pqz3VuuXLsXINkRu`9~>{jcC?d+(AxciR?T*Y`N@yRlYvR;caM!S(*$X5%b3S5MvV=ayV6WeX&FI~Dgr!%o>7s|n?yZV`o&RL^6pkIFM`JJ;~{A{;#rfc->Zn2x*1_#!gvTaM#xVRn{ z#BvvwWnb@}5v1^RSzmJ{>svpxhz}js;K#iUwvY1ry;j3NksHvoTe7xy%S(^O(u|0z_$@? zzREUHHojvrdYI<9>#zDb<#%#OY&z5JDXwNKykxbf{e#}tm(Cb8>%KidKcGjA@jh); zbb4ivx6!%3c5uhl?Uzi|yDIl`*~5N267{a&0Eq1NFPm7;sHLUf-N~lgrs;w57LA71 zS(Ciw$?e(GYP9}4{qfdSb-JE7*;u8+iDhAve(m}4w|fI31gf6>jRu@kTdLCTd_(0o zueYuec0I1&s=wi@W4qj*sQ;ODcgMn8y~hn7-&bSnr5v+8J(R8_*mcx2uwS;O&c>EO z4b6O~E!npI=t2GZ^X9*)kymrP&m*r(OTwSNP)g|IMSaSDIxft)X?JIC#=ISWs;}B$ z+i=My6t^hu?6x&_nCz38y0X1!r9<<>hQfW*Gq#Is>t6|)C7v>SP&cRjvzrgdX?o|P z#@)N7rWUK`kF0q?ZflCt^Ob%ha!snwIq!IHMX2f{d#~2D<&&0=?P|1qWPka(hYuS@ z1@w*c7_x7&vbgTODA=Cd_t(v^#%J^W@ zwx_SxNu51zW6F;@%5dd;Zn&v};(6hL+39oDA0G-)5=C1Fty-XOE<9e-@alCCy{;ux zcFAZo?^OOmbkpsv>ho}Bow#nlAe)+w=yFPFSpC(j|NNm%gIW7sLhfB%YH@MHg_wb= zNf9A_!>z_?q^;g#xNJ<;x%thHAJ*zOL8Eo8q&UaT%iT}^eCnl5w`)6QYv9>WjCqweR2Kox$fZ)!whL4|iMx8%s%f>^sb=8MWt~F8H za_3=(+H%`p+_CRA-_^U}i1F$6hlUp#?Z%#}liTcsAI?r6I(l~Q<~sYb`{9t<{${VV znlwedYU%q;P^ zg4fV~#yUpx@8w#|RE!K*KICzbM%UIMGow}XaJ{)83vtgm!yo1JZH?`AvtxU8aqpA; zgSMwC-0iR?K1~$qnsB98>WVgjQ~i4m%dkCpAu84_W$F5Qi`F{NGRv_Vc`K@?-}KCd z?u%wnH!X2RO1^FN?hE{vWY=^bvG~|w#UR(E+PlxR-r0Ffqqydq)crKu#At&4Et4^~ zo~&3iezTpQYiuJ$<+HgNEhY}Uxqo%agRt~VeKqy|cFk0>*6QwU*=XPvk$&XW!K)@8 zvyz`_SRT&Z*9os2+J|A(hhY4$j?44j%l<#>0S?4vH!5(+-vosL-trU z-Eev}q<0A281vVgERXZ*DPz`$+xFCQS=qT~ErSc@J2DoD|5k_`edC$J;5&`>weULU zc0I)QqIt^O=?1Os&mVoN-NI~Osz)2ADrU>A8;timtMp4XmF>F*D$eV#>)2uNs&ReQ z3ggC~THxtCce2`__YBO$^$rW|#iwIU4TsNt-RF?95~+i$Mn=W*Ta z_F8AdEXPywg;}RGjdL{GNt_z0lCrQxBwGd5p%=nREGmfjzS8 zo>Dq!+|c-CRIHlv@oo@Ntx^An(@!gQ@8Y@ezytrRKsmQJ&ksNFONqG>oAZ3_TouQ( z{LP0P1`C6Bn~RfmBmE#Jxa-Q_`rN<_t-6}6L*8x+RX#k*rAgI}uhsOb?SE!@dX19% z#hmS1{TkmZJXK?Yd%)}{JJpmcq6SliA)tRwQ6qX@{n_5m&sRU*m%C`H=jB?Km(I2C zIsA4%{UE)0ISLOl6t#yQxO6v0BN%<0RUY(k-Q*M$voSt#Q{&c?HmdwyYwD%2TT0ba zc5K|c&yyZ;=v9iZFJBbRHyY)J|&mn*C@iuCC_6|r|Y6FiRaP3b0#mz0c zKYP7!fSSg%q1^)4))-%Bc|(`|7}t7s=WViE{WDLF`>WMGHT%rpKIzN$h_~6L9X#Io zfkBRjieBsMEkbr2(>m%Vb{XUTLTPMFSEFT5_L(XM_3oN8aN60piS5IJqd{%www(6U zF&2RjRmgbWSAUxS!F|=2&TiXn_}+TIDUELYTw%cFg@Fz09aU`_d}X`G{h5=zju*ZQ&1x^!;X)k48!8$*o=Iogv9 zFKjQgY@-$WMsKZQ$I~Z#)uV+LNwJyoR+p3K&WtoYwC~pO6DNF+?q6YIchgc-KjenG zZ_b6TKMe~V+(%iwH+ooo^MP;0oyQnINDb`S*|BbQi|LJbAMQM0rhkxHM3#cBde+-& zFt}-h*Ywo8Vsp?@f#5^}+ zjF~Xl_tmMv-5SgJJv_I~Ut{J$y8#!~Q@y$_fQ8pJ>iOjEYG>Pf-1O(}$>P|1vvaO+0Y>rl2}o*FFqAv#P_1?&e~rG5SG&%TGMGE9cghra=`|7b-u}$8ch?n3&4ZSB+BH!=thIFT*6c0maW|aQjm#Y#TLyPaRfSeD z{xAAE^w0O*Xk5Q$&N`!KIXkk?4qq_n@BO`2wCY?w6ofsq*(v-6z822Cs%VAxXtz8=-)LonkuNp zwpPEk^NF(2+WzYcvLd72?EU%B=zr#B4$2LPR$O!mCds>Z>NdWOsaSbyPREYP!h>fl zJ@$fDJ(2ME-f2yLb3HZYy7sZ!i)*#J_Oft*gL<{T59SA^^d8g0nxU=2qOGfr z%WzNI`{0dWpzZ@jg_IEcxrb{mRKEG@m6gJ_taRu0yMs+PhF?7$IG)&$r7 z7t(t~U$@r7Om2rercp2MUtK<_zun_wsyY#BTY9_MxSQX%!j+7j-L#Aj_!ahkd&{t< z`Yu16j2*g)Yt(EOi}M0rt5+9q(_OrDOL*WZ!HLFlKRkS5U9e-{nYEg;8zxO1=+|iV zjZvqo?)2Tb(9J~Me2j5e+F`X|f$fx~K~o2c31_E$463X7AX!&AsM%D^2%oBIodHpBjsuZ})iG z?OgShb@p~>eaF7a#l zeCoFS=}3QlUz28wpAS%4+{F0LsYbRn{#raSYS84coeLsU$DGRF8Rz}TtJ<$sYwbPK zxY}=XI-MWcr-{PiO^xfQcj_#k?saY8>*k()m!0i)^nqc66>`&}GA{M1d-V2@zVgrG zVyk=V4SC-2ihq;UixaZWOzpZn!apsd*;9i~gBRA3n_Z`SgT*IKp6g@L@mP!BrzE~r zJ@|6s{g}7w3c7}!cM~^@tnzGS;mJuZFB%#Dncm2vif^asBYto2Pqkkyk~}p$oHWL& z%2`a5>o>6JhTgU~rPsWHlNSGE++sqrIT3?u@4IqTFek5@+?>mUuk~H7w&c;u$?4l& zSO3yU4c75@OUqMf%7=Zfyx#rvlbxZq6(I(mF1hZ0kv^?GvugF)v?sBQbn$A)v9v!D@)n8 z0kti)v81i3&CAPHRbF212doTc3(Gab(h5uK%2MWS&>Y+JM=V4K)oa$Q`Q7Ef&p-c6 zb*NsWhJ2%*J-d(S-Mi0}UcGwFz%modtjba*Z9r`~qj&G#Q~UMnGgd*Nc1vt?oJFhr z;Qzx9HCu;-gbE4^3kxdCw`>Ct_wV0p7~)?Q@&E2Rfam|Zb!#dL1cE52+`f4WApR=l z5&w{okce;o@|6!LsKoz2O$3$r|EJ-{k>8c_|G!KDmH7Xc(T7U@|8K)ziGL;jmHof} zFb!1d|Nk)f`0ADTSK?pk|NHOKKqdc|z`n3R47h+yQ7OnT@sA31`rpLAun@#)QIH*G z4e9Q8AjRqkB)-}R@z0k)?8BK5_h=@BYmWt&gYCiXXjkw&-4A>&{|-U-=0k+x9!PY# z51CP}kS|LADq;|&B|@mtO)y>mI|v0{AH0|6%{}mTzZxX@+Cf2{=!5_Jf*<}f_!s77 zLr%C2q*xz^xTo_W>c+1Sso4)AF7|@Ri@hQ8QeSo+d3gXtT^$0!r@DakS~+;T4h!zB z*I-$NWhIu?a$vhr4xIP32LBtX5c~EL#vG=DI;eEM3!#L`iGDyqSq0-~%8!8ZT zwg=cC#MtN=3O!XQ5<6G9BGfp$MRxP{oinB5k{IT@dI@6Y^Gg~dNVD+yBHol^gJ!x2j%)t z4A0v`Io`J1IR&Ea^dToXhS~U}=jnfbk50hzne7-I+(8<+(TiwcvGiVakP_$&1)tJ2 z#jcQ^R2cllso|*WRUqohpij$tp7*Z}MY~`Ch+gXCu>K(zKqh`z4;Y5T8){^#XC z*#Y)jYlA2`RNBbad;dfkUKi|+E`m5$GcaB?5QyJNrav6h03s}(NdNo3gWQZ1usyVZ zT|+cLHlx4J84#G<0>kBl@ILa8;9>c`OF#8pg~6X}y@(4vN`bvHV&5O)9?wB}DweiW zlF4?6y4>%J@u$9_zxE%}`=v((Aof+5Y-gAwzmrbCGfWQd{aOqAb8n>o{nUZ&q4`V> zkS<_0A=(LKE8OTQ2UdHg;N^v%x<$#~D+d0B`4IPH4&&ty+xGA3e(D#bIbAE(CfOmc zCHO0Sy#Mzh{$D2#LVsiFJyQal(8lk}c%85!TaF6#>Adgkm;Uc_4oD|FpVf}_1?e6{ zL)v}iu&+=b98WL*61$*6;7|RmxCeiJW*<*N2duw86!Mab^fF;kkeiA2;2#ijx@)QN zCx3wRzLp@)PALIrA;i0xgT?kqj1EZW>r811d0D~||NHc5;EM~KEr8fR98({B?i~iQ zFTQZAIQT!DUMl>__m`lz;=Ro#e0v;%j<+i{{+ta^6>a}c5b#iv9MvCt2~~@Njf(rcZRHH2JiT;o~>{&*D#hU$Xc4EjPgz)hE}l%8heJ|8M6n z#GmsSQy;))eGQ1Sxx_yLTrRF>@|t9}?}H=K?@Mz)vhKy)_Dt{J!~S7fxL;{5FZth! zga7?MSl^HA`J(axpC9^r$j&EwpEip3#XtKCV?vQ!`yd^JVf=?MB;Ws+6m~7zg0WU- zxO?KK)0tIL`~#mC(S`I6pL2jd!GMl-kdHRtvuVwl{C_y6He@Ay)i9|Ffj_k$7q`#Dy-X43AYU&+ zpB~10q;a17V63>*g}-r$eUQE;^Z8Iez;vB*iC;hO zG|AT%`+oAwj?|gjoW+bj@5=Z7tHR*_-nYXq)7|f}_>UBS^bf_{oy6>Xx`yn1Iwze$ zzW=;b0exL&8^!Qq91Ou)V+K4Ls{rJ`p>lV4HK4v>X*av0YMc*%`jNw{U`((hcxr1v zdW1hmr=`1L#lRnVG&jx*a-y6dC)x=_u^!U45T`|BJZA)AJA}yq&fi;PdyyO8pX@0BjFDSZij3t2Jve91=s9rD+a%}@P47SqAL8~O50*AK^fqKx^>E#B=l z_W7%^Sm7=79a2n~3gr+OF7Lx+;Ah^7|67H?pKM}^;jml|%IUz88|TfxBg^ff{EjH* z@9od<<$ol9ACEty0g4k}9mMiK-eP~A`t+qKWGmWls{^7Gu1}tyn+-4Kw1aDi5&7+m zR}R4VZVk?NgLL6=eCcP=KuVAcJe}N(<$w_8dhv>mbnUrGB0agS$9`GoiPmy8i}@gMa6p)a5Ed`erau-_16`j7Pa z0qCpe^S9_rubUd6)tl1;Bp1v!{tn3)!+GCE^b`0(6CRFjz+?wu|7ctT$cPTY_#)!Mwr4N%mKz(I*M2({Xi7vSJYc<4i40zu=h?w4h;Fnjx`TRQM^Ul&ZA=!=h zf2#wr&PK9#`&KXp-rTN`;PpjuzIPwbeN%DqztkI& zU+8MH1$?wl_m)M1}H1M_jPx!MqpcWSg%t;D?FgHA9R2#-C7b5BYnjzehSEJ>skU{rqkDeTBxK`h1Bm*YWQv z-HT$}IDZfJ2W4^RkIM{ynH&Mi5if~9<#XyxRb(^}YNRc#U&#Zb6@8fx!FcuXul4tr zymf`fpZR>Q4+pWZxQ{%=`WTM+==EOgi3a{(_!Ai7>@>{(dJ1_NsU@+Ip3}IW1kWN{ zR{F!&A5?7onGWa)>29~BEmg?;y^}Fd@B^{I|1kwHmHdx0!hVNMcK+Ye%>Vi+{7Kix$6k_sPos|F`{D#EReL=szI; zCHcPBhe5Q~NVNIi-{+I1flB`W4$O~_{AT=*Ha)|=gs(RmzmLK1;?Y-_h%zD>b3jti zUQEUKZ|uoV;EKK*SB&RUalx356UKraFeYe6F+rKW{pTFNumAsDIKKEp|2zCo{W>gh zh&ADyier2d&@K$c+z_|h6Ttkz40!QG4eskMhnsrq;PTUDaO}!>IITGfG|vu&TgQ9A zv;B%-xQK(t_Ow|9|{Rt`nd8YmWkLy|u6ybMc3I=)o_Dac9C8 z_e#E|h`kB)_BMt--bNoR^m}joUr%2%Q1UlN+OUGINFTlM`q6gUu<^-ixOb!*(+?yY zO6rJ8{QrOW$DoZzxO?6j%lKTyU6hw!M|Uqn=;CProjhMcCy$rV z5s%v;icm(hv)>95AVH_H1?$nzUe#wWaI{+_(7ILL@~fRvDzkQDgz|5F0! zIXaLUW&$}W0XQ6q8>jlEpvzoWG4RjJfTyN=pz~MQb|eo7dy@CZBU~|FDBH$`kRELd zk#_qb@YQ_qdocz4bXCFc`TxK8>rDrN#bywOo5{fV#fKFH|14PPt^-}L5C18-{;}UX zqK$Vbym%gdZe|R`xSRq1=ZHC$0R5Q|@N)KdEVID>#WbWp%wcdif}-!kbBuikRa6N4 z#mK9Z1I*D*fAz)qQ=e{ekQL-(ZXmy9C3!=HjRpi6{Z$Tdf0yQbbtggi+g(r~&gQFJ z+I@w}i&B}c53}5g_-{v@Kd;>4PjpE( zK~8E=X|bpOR~Y=?W+XDd9NBOm!ufsQ6Ymd2KS8{>XkPV1zXu>N--xnxClCz;yqr@m zd7qa7WQWFi6pd>u1tC-z{OO%{M0!AbwBO%{^M}6cjJ&@ceR=$rn-R@;KHM5*Kf`}Z zxyGJoz+dlAMz6B-81nD)iG78_KdT@gmI@ph4ScQ~ApBnmW2DWL60DDHxe3Cpcl>Mk zpZX+0MvHK6_!Q}Ta0fK^%!dm7R9K<#r+3aq8rT)(0fcL}PxJ>Up3@Kei~`>7!yIAi z>oL7g_*?Jf^uIxQ*8gZ5BL3u;$jj#Dv!Zs8;x3uL2BF3jXs#6Cp8bQve>g_ws@AMo zv&yF(LUmE{sFH_>5=|4mVO6cDog>kzKNuMuYBxLhgqc0#$f@=W~#ysA#`~3WTpi+o*f&2WveS0WZMEt2C2-jPQ0+>v( zdP&Sy31tGW8^~WU75k0Uf21upBMNaxz0V5q{|<=2{&k4i%5vHN=j}i82ZdojmCaLy z?@@sGQpwL3bH_*v1t=GJ>}gM%2sb6AE(0a_%YR$${`B3;lK&zLoy}DBvZ2OpCGt=KGV8|GX|>wxH?v zv;{d|FX{V0gE=fGfMgfx{9=60@m%kH`Uz$%Fz`2Xnw^6`=G2{Bv6e7!koVXW7ZkM<0YeID|+w2+)<{GNmO zW0Bxa*ym(tF{G2E!cSJSJ{ky&;LN5G&h&2v>#7=PdZ*=%X2#Z z{kRja6XsOX)6zgFOlA7Oz(C&;ZNHj`J?Z~)v6UD3&r<=>04E1FGXI*a9q`UE#tO+c zC|-6m9m3=T$%HS+8zcNF_mH@s!@LN0X4lEcd7j^SIiH!C0U7B`&4|Q(9LC*~A=dRg%7)D!>@!fFCdr9l6SN1&hxiG7!18+?k3s>;AHrUi z&v~1UFz0Q$jP!J--_z1kIowl{n5<7go{x)-f#|46K)#2_$VeD8XwYbCZ`{fa?^9*U zrTmu#{v*fae~+}=$Lzp&Yy+uI;A{laBV0_G?=w&zp{SogIzJTUelE^ko12@*Y~Fa!n$f~$*5c83lf`cPZr zR<3xTDkRJQ|3>_YC@BXg*8LJ?Kxk6^fC8#3Izy}iNT&kyF$pI7c|e!`vKDgypQ z1LQwSk9rGHjz^eHKzz<IiMPp) zi$(s9iH>5jJt{JS*>r?C>35Rz0RjHt>*E8So}S?B>NJ$Jb0z*9 zJpY%^0`k{l5sPz~To+{~f+!;aL|LgU4xfjqSS0JIzeX}W3H>w0bU0!ih4P$iII`af z>##5Z!yD&FgrJ}xP``P3$DZm}QRF{z zz9hd=;0N|^f>Z4@i^&m-gfu*N4$LzZ{%^3?WEV;5OZgg@Ah`~V1>MH z_{tETK79(NrlxS>#BsxV_3JnIw_snf@JE4pY+PAfJ1AahPKtj_GteJ3Fwnv;-p~Bhb;&fk%%X!ONE~!2@Lh z%KT@wYuBc{9@@&SzUQh}^jTr#e_>%U>?uCSV^97%>eG>LmTbH@#6609bG!|QxD&6F zTqhnUyd51J!PdqGEG;ZhuA4HPb#PDR#K)uW5dE?tA;GMlPV(Iaad&WV zKs(L`-oAayik+P;v*~?&yqO)Cg!Gdx(IIiMv7n)`_qx2iJfF+=ufo1!;7@iR%6-J1 zkI$Es`%LGf+^4=g(ShtnV{*7$J~6Wu$zEhRJ{bF>dlq7@7k7U#=Jw?D)~;Q9L)cpUcA{*G3=PSy?VsRVv?UKDvw0#15wD&`TgT<<|ZVKdEEK^zY>4M zp2pYndY{JD@z`^EpK>rUcZ~JZFc%B``xMWi7!Jj6NM~@HEcSE{iN2TcJ^2a(0|Ii! zjT<+sB4SVPRKD?-@H*P~WapEuSA=^}e~;N|IPQYW*Wqk_^6``1M;P+HK8opthlew| zA^dsUk;RE5x+4Rzr`Rvqi`eJSP*G7)t*F?SZ~Tj6Px79y=lXg@I{#hXCtsh0_j&x0 z_bE4r`S=9_#9xfJD`a%QW1mBIK94=>eDnvU4jeeJT;}Xa=&?+8<&*zQU{Ab{*vs-h zjl1P+J{o&PF&-Lcg?1n1U^D#5?w91@QjQMFe)9LC9f#NDAsunPLjGFP7sU6J|KaND z65qE^ADW|wwsM!V$5pFrKEFTyC9sz8IgdBT^Th8Q&vWDLCF3z@tR?pML@d5nggyCt zq_SVa`=s{;h&|eJ%+E)@!eZE?yr=8z?d_wwckezx(z+GO)_Whtd|&)aU@xQNdHjpy zeG&de_C9B?F`G{s-=*2rxa_kOI4^E_crx>{D|^M2m!Y%aFX`g@4I zl=snJM|z*^K8o!J1O#BrF6dqCv7cc6=8d1CqGFf-687H{f8Lhov1f9g$#@=j8Gc7u z&G2XaJWk&i>+ebY{M6qQ3b}lI^4U;c57Ya|`=s|NcPA9{w@7Dje2=j_%dnjkq zAM-mXz8{VK0LlZQe*WXfk1Z7x6q@{3vHuYM?_l$eT$aJD)V~qV#j)n&_B`gamtsyf zK5ys04}0WcHr|$EJe12za-VE|%;$_Gn_m#h^uCv;C+2E8GCv@V@40*Tu2H>u^?v*> zWB(5RL4l0_c^>>$ds&?M<03xiY;zuS($%?=IaOq3KK1jNkC*T3Nw8;m`)uqb`ub4T z2L%O^9}gT6dv|wtmIr$6+STX(##}uKt(NId#{O>;6d1tp=P~(K`(juVuk$?4_QmH- z;&nacaI!p3hP%YagV>YIr~Vu9J?G~on~%%wqdZ>9-*t0y18Zv>M~?R3#fujnR;yO6 z_J5u4c_Na)&zxd&cuLZU=2WlKI@&TW$TPj=s4 z4UJ2fr^)+rE4*K?2;q+($?|`Izh5{y*`>Jf_dl~zU$DDYZ#%Azyq%l56 z`;7DB<5*m@?YVxvWLzfddFtP>m@ekxkxZvKHh3Qo$$T28L*sL3EFP8h>(-r=X@yGc zWg0^kf0UQu)L-K;;pA@FA9M3(a5!^wr%--2D;R?z`OYwx*mcxbque_-_8N6O>3KF* z4{_(m+mmlsAmIA=Oy(o@r1NRKhMJn%ZkgAu#9pQ`=uj4aA8+q)8Z$?B^|vZqY>wk; zj<0D>1-@{yowMmUK4-ix!JHqHOMN>wW()gw+}LYw%sq|Sp|M&t_FhNlnPAwkVN*)H zZslo-7LedytxlbqEj&Hk!$>X@&waZ>{#njP%jQiXc`jQ}wv$emj<-kM&gbp%a-R5| zjlV{pF2!_6=96ESe0*l6rm<^QuQ~MNk3Z5J+q6~kJl78I{gmJ@U$bV7#y&n?(bN|s zzv#Ct>FGi?hK9z`P$B;fUnu^bLVY@n#WT7<8;_Iuam>!6J{}bszY~k&Z^*x6XKNdG z?dsK+GiT0R*0^!wW->2YiM>o?eC)7#_3Cx?_4O>hJUzl(oSg*UvN+@YaSW}bI0~Gc zusAvh9I-e!*a;l$?F9DrwgP)QTY;Uejljm{ZHSeXr9bkv>$7J%7T2y_d$MoezEcYp zELb^Y$PksrjT-Sj+>gCtKu0pb(t8`Z&5uN;;iuukN&DL3<`VqGe?$`<(UEkJp! zU$Fao)73%KA&20$Oz(}&WETggCO!^ zPl(j)57Ac#fyeIpV6{>XtXFZxZgVy8zAzHfLyGE`8sr9dhRT8Ywh3UdV=~+xA_uWf z#wDvQJumC}3+>KI>f8^at__9AOMO6ScMg&b)_~{UhG4Tn4s16`%0@Y`Tp|bYwih8g zDF$-W62WTEG|-u%2#%*#g7dkxpgW@@m~H$WM5%F*6(26GH-Au;_0Ns-z_#v-^&Wyn znbkk)>R`yn`PH+0o?`pgEmnV<^>X0)k0Pte+kLa(;pk@YeCE%PlN`fW57!A!1%pLB z;NhsI_#VGvngX|;HvfmRtUu9B#JOJ}>hb`t{+Ihh!t*8UQH8i(vFE;@82!9mD+g4F zjvRM2MqaoK*@+RLGqnu_KEJ?MO}dY;dJ2!m{`k$?Uli>G@j442{>ePn_C)(xe$PP^ zVG9N6xHiGdE#Pr#2zZ_6iZ|{(PY#5jdvjS`4Hox+2#Y7udh-Vf9#)7;YyPy@zAW3n zP@E0o^k|&GHwWS#PKRiup@M9jze#Teq}iW`ymX}T)QI9m7|H76bN>iv_mcygf94dc zADy^d+z2;%$${IIt>2V>azfuAzxIaQSa(Qs)P%&BYau@~9-^-ghnVZbz~@L?u-{q- z9JWi!wmM+<_YWw4%ppi$3w-XMgvS#Uz*TcQWJCu;W}E=LZy$n(W9p$yJ%Z%`z737X zD%#4j{j)>NAmU6%NHO0JdC5VHZZf?eqde*bF@|JMDsB>M?pbO3VTw#jvDfFf2{uq@H)`E znEc`8sNE)cutS+DO7w+99|w53uqy}!K9C;a4@S%TLAd#&616TmE%W-Lyr=fg4mJj{ zFbs9rK*q}-tG_Mk0m36I+6~vN#PO}D2ly>FJq6cU<<_9(zmwg6i~2{Q4oEiMf_e*e z8S(@1aV)mK_km^{-SGO0-&6guWJbBlR_(KnaeP;q*`GxJq;E;@#@-kX$;h**^e(6m zl5Qx2AIg76l>4q&+}6l}Gx7q_4Qu~$QUBKvjqRO^^d63NfAwe%oG{t}>n%^gBHJr4 z%kn(TGCl;0bT@!N+(jO$Zyy|)O0w5Fn7h?1?^XC0Si z{qvA#)s1&S#{er<_dY(x(8t?|mCl|oVY$E&yhNEG&J_Zrhq02Eg=^1b$KzTivAE`} z07Sx&VkIXv7{s{F$%nSGtiMBIAawRLfZjeP(t1<<>AM|~9sq@6$P9b}p~j23HMR65 zYp=ef^%f=TZ1LYo_cL(4ow>NSqbBO!+;?81EbDKTnFJlMu6&!*zPq;}ObE1s9Mqda z|NB@^sx!B~Gq(;DzaG>__V_No&&ET7&&_XIe-ZY%mLQF^_0Wf&SZ}I-C!`zg6tsy_ z{c(MhO}Ks$t$$mze${8MbE!Wa*GXN5>ty5tVo6I<8hDEf~35az&56QuLkdYb#!nDky@e*uoWF*U*6kyK$;lqdZs#K}M zkBKT#yHB5%W&P#gA17$bz7m}RR zz;R$$;WEn$E{pc3xll-tQrpwK1hif_jdh@nexvy?NakY5eOe*=2n^65R*PPY=L*t%l96Yr%Z!@wv=cINFH^(&bEYW-f&M%7NO~vEKkDvFE z$ClYoRDWBWw|*omXb#Kw6>4Mpo*T=@jlZMu9n|JD&Vp$ zaO}yb>C>lwM)`BppMO%BHfq|mX(K01nmA6hw9{U^72jwR5`+WLV zMNY0l?=>XvS8|W-z}{Q3mzV6TO7=A+dj-i}MY7kB?5Q2`ddwzd`>K*X=Et#f%++Fh zMcNn2$!XA@^0a=&G6IV$Y#-PN`$Z_GMZ+bZDp4u3g*upL*{< z{!slrH8nNoTa=kIXZ-Q0=g*iieJsZ_W&gQg!Tdiz^?Y1&U`*LRnY$ydO+D>PpP!fL z4_W?tkmhm&V$6?2qVqk>1EJU-zZF8fy9GN*33A1__`5#&FL{1$tQ#cg{)O?d0T|2s z6~Zs}2AkEAJdTak!0+040P-)RjikSz3+8Y90Z&z%LbRPeUoq*vw9hZdK)Y6ZB#UKH zd@%$sSK2$^8)|xjlkZ-v8|mG=307*9{sTMpR)29Sw)B*}&wAmUPAaM{}&oHUw%)6OQ~b72Ui z1URx7`s+<2!SB%-us^y4Y!A(cNNe4%etzVI9v}*T3*zKJh{4!kIK~O>DfY+5!jTqy zFAc}Ia{^dv%)nUvQN-eRNcMN+n?kxT_4`v^&4unDe0vNs+;6iOU!-O~aQvI_=V*|R zLFUB!VZ11VJ*yCH@&cUCrR4UR&mYZSmxs@f#+YKV_HU4W^LI!@I*h=aDhGVN6Xoz= zEYy)=p?EGz_;gBTX`e4f{F5~MfW`f3aKL0YEV8=-e>mQRkrdA~Jp#KQEQRO)bOa~N zr^rnf@YVj{zVy#`gMToNs)X^!P63wC8RL>l{%_!TawOPdZj*C*Bm|D;4;?Y4vp(7z3gd7cOvCvsmKKQq z-VYVt4_sIvlGXwB-{~o9x@q-N2SRJ_cm#GKECNSm}pf?Tu zvCmo74*9Ccw=iMC_z|BWf8YCjz8*QLfe>!J4Sm<^f&A`4h6@<(R1azkdM?FEL}{U1 z+yncv)ECEoc6QgUU0Q$b^Qj&=sR5WTa|`0!F0f~@_#~r=>EN$B5rpBd*?Uv0h2~zQ zcfWP(rpc$CO~22W@&5Mb-@o|25K=?*F+Q{l!ZAh~W{&x(KKIbSkj>_m;pd9OIRW#3 z`Q?|PW&eDxHo2K`kdfd6X~_|gn3RfhRfIF&wZESqts`9U`|rOm{G4age@gfM-1BLD zo2+aR`h(KgI=8rn4bG84^WVDX{rc;#vp@IQU-EpSZSpseUqOuXeDWK{NwV?1`L$F& zy9%XxfAo)$uZGtCB0qsBhtqjcQITob$L81a`0Qtw=KYzUR^r1YzY5JeOmrR|CP#KNRaOD)da=uT68Kvw5w_kAm~3#Ky#ckGEH>l9JMZQa@DYg6Y$zjppSQ z=flG}dh@wLK5O!s(L8aq#ya_w*t{J{NnmPX65g?6#~w1b`l7>W)25E4c4W`S=gH1V z%KUQZ7cW}7C7JmRuU@@s-mF=(G8qLma^%SNCypOK^1gEX_^~4x3psN1=#e9b4<9qy`j-<5TU+sx(t~P z=OE~0XK>!#8iMbk{ib^kJg)76SO-J?`R}>j_%`I?^^c}O3fAB8U~@3R{;)mfA&N04 zx-gH+&!8LNJooQh|H{z#cA+>KE*oru$qqMQk+(kFb<_gy7i&SB8iM@>%Ae)>k?*_S z$iU!s7QS0=%*X2!jIqt&7w8XIg*h_o(BAqBZNqsGZBO~i0LCUJ@3h~`m-^WL0B$)r z7rQVD5}gi1z>7c7j+~Bm4CX=TO~E`5YCiz|moM*?Y(M^b_6PjPO29e39S%aM=~BGU zWV8i#LVg}+^X=Zf>+erp&)ugGvf>=jcDM`iQNAn={rJ%%_g1Z1y)(Y7^HD++H02=4@|o#|qA+EEcI_HhvyOOT1e)`Ro zbel%rd-hl7598@->GNyplOTQ42Z+ua_6zCTuwl!j<(FPOO#a9L(jUSE0w4eaAOHd& z00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY z0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd& z00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY z0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd& z00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY z0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd& z00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JN&B+%1dMa`xAsrJkYsyOy1D$Sftu0wB< z>);Ie<398jxij9QitMkb=G0PZa;H*Pvx8`0Ko}naLBK45-p+bzC`qKU!ygZNr}*dV z+%0)qSFoPCTT9LE9_xdEOrX29oT~Gdkn`XhetRhLwK8Webu<)!G6?uYpue|+>hjlG z?xEJr>XQqpr=!-VG-0LKB+${2Po-HOSehSdJsk2o?Hy_xc=jBiJ$#@#*@Aa5IEQ}Qb1Jd<*T~P8w_K)2wN`8t8 zc05M;i4PloN;BT4fxfO-fsW%eAi(#E+I%d~-<$^)?V3pW+aEFXPvj&he%k)OuNw{iQ@7<&`m_;NW4l*PfJIw5BY_xqm7Z?wSyghiWgmtP)f7 z5#VQpzMeL@T&1t4ReEOVkq@%3yIHPB3G}+m+n1!iEM1q!TIQncr4(N$$|xgRNOq0$ zy!ygTRC)aCb9H&&)#ItX<2t_5%)rZZoKoCH$?WzRshC{p?*=Vj7x`I)^S*)r>+^!)n=f(pqOD}eg{(cZ$`Ti44|hBo zvim<@eDk(WYvqxUpO08hKxbo-OvAyn8KrR*~KONE6W>daO z&tnzG=1^CQJH{-r?{KS5#QEi8DC$p4xnrV?$8@|>n;`BFi8kieddk-yfA1URHrc^E z=MfM$-!pf&RakpcVmP*D_g=s9#^T-2#m+U_%l`%6uW%g#roahsUpr6rcA{lmk7D}z z=H;A5dVK68^2xw`D5ugpqef z;W70rEpX0TK3;P=!Sl(8v87%8eU#JQNXu#+G~IoI9xOOSSLW@aHT5nXiLMrxDId8W z+S~bv_2qo+=xM9ev5uvH{LU6ySW`^TlpLmO@{;NDlS#VIw#ITDl8(C5rhF{g^%Q*c zAu+kp9SE@C}@|nI% zH+Vel(mf$xk4K)_W78SC^N9Tb*!bOw!D%>wJnx?#tCVHqaEnp87A3rQVt;Zwz&SP*Z z*JPu4DEqQRY97pOf}&ySmmJ}1GndgQE?F_>XK&HuOaH;5-OnO_G?_p0|52n*9nyW` zB-b%ZJycxgdr#3QUUdZt1P>b~Jq@8$t(bB9Bg-+Tr_F^#eD!b!$NoBf`IK5MvYrBu^WV}?q zDVq5GM54Ylp%`b(ZilSgU!;yD;A`6)<=)S#`E{pKsHRu^?v+NzPF)_!?-H`|d_{9y zuC%wMe|1qwuP6ryudu5P+A3P*ou_!FxpNF)49M^IRo_cG_DzP5iQb>Fxm+xg`(=Cb z#g1hgGUCKeO68$7rFUm*#O4_#I@?G?gwGK{JzQg@uotPd=Ejm@2AH zc}2`){&0PcJMG<=8XJJe6s$eFilTMwT=ubG(am({(bZw&X0>%%VJyyo~8dVKp#`f&a|w9{l> zs5q#eBY#xP3C`Rf2aUFIOV8zpRy;uSw!TWQ?E9Q<%U)}{pTnhR?y*V#OEZ>_r$wJ$ zPWz`^=vO|dc2NBtJ_g4_B~~N1tO`G~_!io+W-NV^_&U9nGMk<{_%+>kI3b|ANqz>o zIWvJqr+iM&Z+xBJT|9=q{c1REn)y>YH0eA`{ZqTh-{GTTPH^T{dFM|&Me#S$@uhdk zb?5BtmUWNQx(yR(?S_dockXR8S8{B@=U38_PcEb7AN@x5EoIupLG?}RpY}I=4339N ztp0dp$2Lc1gw{K)hstmGsF)L+xgBo#vk&+fobw&-vHIhR9oxXia5pD7=?l~?=e*yM zJMLP!4P~HlskeuZ!8zaI9;?~`zrIWD)0~U%f{)>Tq9Y`IemN+WGb-D)yR)eNNc`Y$ zB|OkM^2Z1f$l_!M;*BG2gq)fIsU4zo-Zhpw2_G#!(Ge0|AU;-XpE7uqu9aip;~))u z@t1(VKDy@*r=F(o-?!CLm;xXDJqg%ukbu8FIv0){j6?b2m}lK{Hm=tS)!3T*>toIS zPj#TmHost59tj_Hcw-{KA0J&(nPY!XJhF2 zw@-P(Ww`9f=MU5IZzfWAOReEL(YU~@kEK%oS8tPZN#ON|k?LdVf#sC^+^;F&=JROz z4Z~>l9T(7m;N2#k81}R^(u$Giw&!`u%a>8r(XE~~iJE0*eQbAUdOh~KtoZ?7vm?>R zlvjQ$JIEfYKWEp>^vX=V0_Umf?UnC`-nmu1T2y{F+b!bPuoZOO1}EY^@H7wZr$NF9A820Wk+@4Ed|LD?PJE&zYzA*@z!4n8}ySbW@U-)<7_Ss)~zi-!_ga{1q z)kjhNARn%4>Hkpa`X_})9!ULRDD%g|@BD=Nq;XK$y%S$vJ;Y0IKDLyeqJ$fV-apFs zf1)tWeWr4ssGajO4Z9i(ldnFC>IeI~TFE7~4R_8JmjRV+e%|jqGL$~Lw!b;#)VGyp zQ_<>)lrilhpZdG*c=G4MJWlJGufO>@DOM(5d^D;btll$w(6^?O>vhOFtIrHB_hmoo zbwcLj>*q<=Rj2M)IG|T5TkVp@^Yb)u{N2$hM&FI< z2OSG;9`wz%e3TG@W5ErB>lno^3J;Z!r{~`#JPuz1&#rlwR^IYwmb$3qp{s5?pPb3_ zd`U(`uOo6n8z!nB?5;1NQt25-S(mTd1A_OpR;A1wTt`{gmwBjstk2!#*ZIu+=z*Yk zsQAcpzAw#P__AN=ibz0?o%6|(9e>hndG2Sk>jZJ#cce~6 zJ}BXB&O^FGYb`k-^vgZ}TIy}7)UEGN{h{4HrcXYfx&-B`N}UTku=D9(k}EZS@c6il zd&PJ4)M{Up?}?B9l-ert2iG_Ar6E6!Ha++wLnrxuQvE+tocJYeAOBB-`77tSkvJ*d ziRRAl`VVT4%e{J>&_|A=GHHf(b?F*tzW<@!KAwrcUzctU;JQA=FJ;bkCE0Txr_G~( ztXrn6lm4Fn&qYc?IB(wl(tjB4BXRuCksYrvElS~UAj5{Q~WYB&-_qa)}Y&gj{)4i$ISkDUB2G) z{b{64T+VIu=@qDoF8A?4GAL+Vj$ly zn(>j_QUt60Y?I@d$9q*#I`>Ta?;z3%ebh?*;1Kr&ZaSrYucE1J({sW+Qa!GKXuD8` z)t*eDy{}(6*gjTt-j%Rmm%|vf)ll}&^HoxLJm>3;h2Gn#hLWol`&QQvdULWVPhYOw zDZSTPyPbAi0V~oOj5xT>Hn+<$YMZ!P${YE*8qBGyscNv@x!OVgzUjeB=;Wf;q;??f z!AwF}#jUO%)Ow};?OOhTFzwFyL+cNn@wW)q3)jGkjCI0m`2OkV#xe?ax|5#%m2m$} z#rigX!A?ss;;%olNE)Ba`=>VJn<4dsTK}}aU2(q?)*n{xerHG?S#v-?t^nT`G@ac= zO@(`H`}DN=)^EzFF9Ek}v}4L!GFj-O**+0=q}88&Xg#v>x2XQGx4llPKRj1nB)1#t z>Ch#+D)oJ`ySyhudOuRZipQwM>A4;E-YM4zeF)Zbj22G)>HWg}TV2Ma-LGB0t@KYD zmtpY$JGQRc0{Onc?)*{hBY)4F{#UxRaRArJX?@^l89OIY z9c9X^|7B+qg0?R!{S(DyRDbB0FaCcn+w!99YRG+@@gpH~(enh?CEGb^{~!4GzNz9i z`gW$vihYS3vNFsOC-#oZengYfZYY#Krce2E`Cd|#zSP$)*X57g)*{$tJxW@*eTK+K zE9%RYeS~surF%wkbyVc|bcn4*se{knq3|(#_AjYy&nM*RS(~35L_Sm>)7O9dbkHVO zD%$#)aR1TLPS?mGE5pk+KJ62A*UE=&_mSI~omn)B8m00jQ6086;oPoEkh zoTB~P_5Tp;W3%n9Z&r8U|UyF z{={KrhgOO*!gQgFN_x1B8Mk{Z-M`GTecBz7)$i$0JEi{aO8C9ULvt>X#uZSt z&9KYIEa@HX{oUd^-=klSBT-vd<@{^sTgLp2$72j+VSi7jskE%W?;^QAJCJ>bVq0hY z$RQr6ov`w|Tz}ZtBRoVNGm`r!s`qD?k6hMhH0H$4iT~m;KhpZhzjNZA6CPurfd}6n zX*w-?Ctn@tZf)P*-~Om-cYOU`oBn^oxwj=&_;c;^PU?_d$JhHtaSC}^8^;#-#qtshqWmg^5SeIS(jL2dkY z=8x=S@rDWLeR4$JHEcoZTZIgIxI?7we^Qe{h)R~cKgVAWL4%u(`Tr+v(>9R%cZ=G$Asdxi|Rhp>YZ^vejW$X zmwsE^uZPPbqkZCfJx_gEwa)naTd6;6E#7D7e<*y+`TPpMPBFKA^p>Bguj_o@$^9O< zkHLofFP8hfsF!>CzGK2KY^y8x3x>rT@%@}5?m9zH{B=;hol<}3UiFCKenR0R=aH?Y z{?P!+EqCL43=rXP=jD>trtF-*gExm(F?U0q<y1-YKd6mAR6Z81@vW|1 z%S`#r*JP{5+~fSTXxkUcE@F0Gm+@`T%7ToZV-zn#;Ul-LaPOKG@cG_5`6`dkIA-%n zuA9bmjsTBQ;a)YCxU5ET#frJN`0VyEZ|<#zWsqiEkl$r=%ei+Mnf1%&7k%m;3N|0o)&qzw>i6w+Z4o_krMh zLoPEk8q=(B(?@2K5i{$`8pjK&M_b^JRn zLl@|YjEBd~#bllUmtj`#{eWC6MoVRx*Aw^qH{&9&Yt~2Z zgLV9q|CHYoQo*6_f0kXE+ZbxjO%oiIJn&e*JXFs zmzh5q*g{C4vno#>mxb%E-D@6|uJd=2W5JD58RQ7LPKceA+dzKz&Ib08%U?33ey3S) zUP?#b`#Bx|^fKb-kAehg`>j){eBT$uxq^ zMzgWQlSfWTA5&i|XyxxahVlnl?K(`hK zdm6z!V1UM=Lr%Gr>2E+y(mDx`EuW6O8-U^Q9z7}b$k3roaCzS0_ogOMapP}JnMB4e zUFM|7KK7u)(5X$Q-^IB0!Nz{L!-b6XH{T?xn=0K6gT6Y->3n0n>g>Is%G^aG=9Ot% zUDlVVyT10Sv0w89)^sSYIORh7fVQso+9r=UL?)KUHDt1b(V!q)`y8_2cce_)GOm4K z^174aJm_t`4QP8OD>Rp$aLh&KTV3+z3Dc7^Zk*e_EyWXxXzp)!%thX5UGl3_!X+}d zR)kHMjp=U4b;zbjd2PzM7-QSrb8hL+IAnpGZaoCGDqy?11oY+s+P}T_k{z2WYZu{i zzWy>fCMR7-{RL-$rr{w->1+mn^(~lNnF6yal6f>(25Hx}>T`B%iWJw^;K^ZI3oYfr z4$&-RO}x;dfj?Ada2AmZ^SZ%4hk8T1Sd+AV+U6sBSLk2chB{+6tg6i&lZi1F_FDaM z+wV=488Gc!l#je$F`&Hjt5V11LcUY_dfL_2PNFZw-raPjwbwRsv}ThL7T!s7i3{0$ zmGaTPWkLL#t55&OeZi*6*vaL4M_2AL7n_&0Wd33h{gV4dZ~8kCobA~v*LB9&wy8(` z0On7k4?LZ75uc4dtGGXB^17SG%0oup57`IYiKKILy6t4A_9oXEW|kC1yFDlIJ$RK~ zfs*PZ&ey3)hx#fKBi@$lb&JWInGq@ zq2Jn6a+IE1a3O?Deb_=+EkErs-<W{dQkZOGI|{hxs@HB3e{_3O#@+ai$I zXLTX-q4LaPSW|GyV?Na368Tv;TYMgRjQ7V`6p|tvMrD{c9Jui>c)I-hJu%KHy$b7c zLm)8c1RTl_h2vj@+r?=4DbQto3QZ%iy>xQ+(Tt6y^2nDU;O0j#yZqWcF*>rl3yLm6 zVE6_|+dC8TcF1_F_}trmLQTLO#$=mTon8#V32%a5_GiQd*y{5xok+4x0kwJTM9=A2NOsxD>Wou3e zd?49WLG!4kQlEInDc@I7Zw>KVDVy%n<_C<;3kLLeDO+E|tp|!t72-{~*hLlm?3jF$ zF((JUY=Yv$8=+HG!bFgyMIWz)_NwuJt77&}Ok2|4lZ@3w_6lOXn4f62PUS6#`uz-u zSSW++eclo|u+SurrFB?e{F$#^y?8^E6Z2^IE&bNBEMrS0ZEEUj0&TG4_28b^c3RhU z+x0DW`AjPRxy4@xL!BH{$;UvM_$la1Z$RqSUvsvO>KqZpE^o`GKJ`3pGdyGloQo`| zLVXLhM;zYQ(SL|v+Gr2aZlJuC zr+2``7rx2m&fNVBIkW1a&$Yc&N52hG&yBnTZ>#=NUVj2mmzCkWcymp}N?@^9W&JkM zPKpfr?1Qwuq5PucKC)_}RzrQV}^{ zZR~$|7gJ{gh_P3bPh|I!uxW$60M`Kwx)@(t*LZb1Ef165tY7}WU$E;U>c?3g*KZ7cS~*?o1`TRj*2TTy_WrFf@Q2j)o4T9n7-%P$ zjH>IO(|fL7nC(gR|BYQ2XFOt^N|=KUm;LxS%42ifW@GzPwhr9A@GfW5Wu4_?yOP26 z9p6ie->>YNi}H_`@6#1p`;*^QF?3eOd1NQcK7JZnYWwJ!?y8$@v9!Cewo-_ zbJBi2mhcdp$jZ1+WdUsn_H~!7^x$vV5KwqCde)+q6ex3j0L(pC+{Y^$>H1!%F zapO;U-`Ex3=6%Jz!yx*r4t%Mx_%snK=wno2i#=wmi}vNBqU~-jjHBhe_x{A?MZZJ- z-<8ncuH|iq%!7b-CuZ3ryj|QY#D61x7W!@l2VMk&V#Fql^y;d)?lPZ-?19k!vptU| zdMnuT2we}k&_X67U=EiX=NbAQ=!+o__p``-tczXo9q4Pd{{AiuVV5;FmxDTZp-b}< z{S>BGPuTJU$iA#5#ZCrxqu_&O57B;Ut^i= zV@msX+oX^+XW}^=w>sLo{IH<#y62H;89d(86 z_D4^G=K6jxwksqIjtYG(3hK|Jj*@(TX4eGwY)pS;A=dqbT_ZPc^wehq`dpgGSIAy` z5z*^hYX7yu8i4bxaQ}}1IjTcn0(HV}XuEwL1`MONz?Da$Z7({shVD-lSFHOFv7foW Ti~7^3&+*O_#C{K2UMBlL1$TLB literal 0 HcmV?d00001 diff --git a/scripts/build.bat b/scripts/build.bat new file mode 100644 index 0000000000..c002b950b5 --- /dev/null +++ b/scripts/build.bat @@ -0,0 +1,9 @@ +@echo off + +CALL gulp --max_old_space_size=2000 electron-ia32 +CALL "%~dp0\test.bat" +CALL gulp --max_old_space_size=2000 optimize-vscode + +:error +echo Exit code %errorlevel% +exit /b %errorlevel% \ No newline at end of file diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000000..3e12ccc74a --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e +gulp --max_old_space_size=8196 electron || { echo 'gulp electron failed' ; exit 1; } +./scripts/test.sh || { echo 'Tests failed' ; exit 1; } +gulp --max_old_space_size=8196 optimize-vscode || { echo 'gulp optimize vscode failed' ; exit 1; } \ No newline at end of file diff --git a/scripts/code-cli.bat b/scripts/code-cli.bat new file mode 100644 index 0000000000..20b308a82d --- /dev/null +++ b/scripts/code-cli.bat @@ -0,0 +1,40 @@ +@echo off +setlocal + +title VSCode Dev + +pushd %~dp0\.. + +:: Node modules +if not exist node_modules call .\scripts\npm.bat install + +for /f "tokens=2 delims=:," %%a in ('findstr /R /C:"\"nameShort\":.*" product.json') do set NAMESHORT=%%~a +set NAMESHORT=%NAMESHORT: "=% +set NAMESHORT=%NAMESHORT:"=%.exe +set CODE=".build\electron\%NAMESHORT%" + +for /f "tokens=2 delims=:," %%a in ('findstr /R /C:"\"electronVersion\":.*" package.json') do set DESIREDVERSION=%%~a +set DESIREDVERSION=%DESIREDVERSION: "=% +set DESIREDVERSION=v%DESIREDVERSION:"=% +if exist .\.build\electron\version (set /p INSTALLEDVERSION=<.\.build\electron\version) else (set INSTALLEDVERSION="") + +:: Get electron +if not exist %CODE% node .\node_modules\gulp\bin\gulp.js electron +if not "%INSTALLEDVERSION%" == "%DESIREDVERSION%" node .\node_modules\gulp\bin\gulp.js electron + +:: Build +if not exist out node .\node_modules\gulp\bin\gulp.js compile + +:: Configuration +set ELECTRON_RUN_AS_NODE=1 +set NODE_ENV=development +set VSCODE_DEV=1 +set ELECTRON_DEFAULT_ERROR_MODE=1 +set ELECTRON_ENABLE_LOGGING=1 +set ELECTRON_ENABLE_STACK_DUMPING=1 + +:: Launch Code +%CODE% --debug=5874 out\cli.js . %* +popd + +endlocal diff --git a/scripts/code-cli.sh b/scripts/code-cli.sh new file mode 100644 index 0000000000..d04f7cced9 --- /dev/null +++ b/scripts/code-cli.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname $(dirname $(realpath "$0"))) +else + ROOT=$(dirname $(dirname $(readlink -f $0))) +fi + +function code() { + cd $ROOT + + if [[ "$OSTYPE" == "darwin"* ]]; then + NAME=`node -p "require('./product.json').nameLong"` + CODE="./.build/electron/$NAME.app/Contents/MacOS/Electron" + else + NAME=`node -p "require('./product.json').applicationName"` + CODE=".build/electron/$NAME" + fi + + INTENDED_VERSION="v`node -p "require('./package.json').electronVersion"`" + INSTALLED_VERSION=`cat .build/electron/version 2> /dev/null` + + # Node modules + test -d node_modules || ./scripts/npm.sh install + + # Get electron + (test -f "$CODE" && [ $INTENDED_VERSION == $INSTALLED_VERSION ]) || ./node_modules/.bin/gulp electron + + # Build + test -d out || ./node_modules/.bin/gulp compile + + ELECTRON_RUN_AS_NODE=1 \ + NODE_ENV=development \ + VSCODE_DEV=1 \ + ELECTRON_ENABLE_LOGGING=1 \ + ELECTRON_ENABLE_STACK_DUMPING=1 \ + "$CODE" --debug=5874 "$ROOT/out/cli.js" . "$@" +} + +code "$@" diff --git a/scripts/code.bat b/scripts/code.bat new file mode 100644 index 0000000000..53bff89cb0 --- /dev/null +++ b/scripts/code.bat @@ -0,0 +1,44 @@ +@echo off +setlocal + +title VSCode Dev + +pushd %~dp0\.. + +:: Node modules +if not exist node_modules call .\scripts\npm.bat install + +for /f "tokens=2 delims=:," %%a in ('findstr /R /C:"\"nameShort\":.*" product.json') do set NAMESHORT=%%~a +set NAMESHORT=%NAMESHORT: "=% +set NAMESHORT=%NAMESHORT:"=%.exe +set CODE=".build\electron\%NAMESHORT%" + +for /f "tokens=2 delims=:," %%a in ('findstr /R /C:"\"electronVersion\":.*" package.json') do set DESIREDVERSION=%%~a +set DESIREDVERSION=%DESIREDVERSION: "=% +set DESIREDVERSION=v%DESIREDVERSION:"=% +if exist .\.build\electron\version (set /p INSTALLEDVERSION=<.\.build\electron\version) else (set INSTALLEDVERSION="") + +:: Get electron +if not exist %CODE% node .\node_modules\gulp\bin\gulp.js electron +if not "%INSTALLEDVERSION%" == "%DESIREDVERSION%" node .\node_modules\gulp\bin\gulp.js electron + +:: Build +if not exist out node .\node_modules\gulp\bin\gulp.js compile + +:: Configuration +set NODE_ENV=development +set VSCODE_DEV=1 +set VSCODE_CLI=1 +set ELECTRON_DEFAULT_ERROR_MODE=1 +set ELECTRON_ENABLE_LOGGING=1 +set ELECTRON_ENABLE_STACK_DUMPING=1 + +:: Launch Code + +:: Use the following to get v8 tracing: +:: %CODE% --js-flags="--trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces" . %* + +%CODE% . %* +popd + +endlocal diff --git a/scripts/code.sh b/scripts/code.sh new file mode 100644 index 0000000000..5faa780334 --- /dev/null +++ b/scripts/code.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname "$(dirname "$(realpath "$0")")") +else + ROOT=$(dirname "$(dirname "$(readlink -f $0)")") +fi + +function code() { + cd "$ROOT" + + if [[ "$OSTYPE" == "darwin"* ]]; then + NAME=`node -p "require('./product.json').nameLong"` + CODE="./.build/electron/$NAME.app/Contents/MacOS/Electron" + else + NAME=`node -p "require('./product.json').applicationName"` + CODE=".build/electron/$NAME" + fi + + INTENDED_VERSION="v`node -p "require('./package.json').electronVersion"`" + INSTALLED_VERSION=`cat .build/electron/version 2> /dev/null` + + # Node modules + test -d node_modules || ./scripts/npm.sh install + + # Get electron + (test -f "$CODE" && [ $INTENDED_VERSION == $INSTALLED_VERSION ]) || ./node_modules/.bin/gulp electron + + # Build + test -d out || ./node_modules/.bin/gulp compile + + # Configuration + export NODE_ENV=development + export VSCODE_DEV=1 + export VSCODE_CLI=1 + export ELECTRON_ENABLE_LOGGING=1 + export ELECTRON_ENABLE_STACK_DUMPING=1 + + # Launch Code + exec "$CODE" . "$@" +} + +# Use the following to get v8 tracing: +# code --js-flags="--trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces" "$@" + +code "$@" diff --git a/scripts/env.ps1 b/scripts/env.ps1 new file mode 100644 index 0000000000..afd26f17ba --- /dev/null +++ b/scripts/env.ps1 @@ -0,0 +1,3 @@ +$env:npm_config_disturl="https://atom.io/download/electron" +$env:npm_config_target=(node -p "require('./package.json').electronVersion") +$env:npm_config_runtime="electron" \ No newline at end of file diff --git a/scripts/env.sh b/scripts/env.sh new file mode 100644 index 0000000000..35d09f66bb --- /dev/null +++ b/scripts/env.sh @@ -0,0 +1,6 @@ +#!/bin/bash +export npm_config_disturl=https://atom.io/download/electron +export npm_config_target=$(node -p "require('./package.json').electronVersion") +export npm_config_runtime=electron +export npm_config_cache="$HOME/.npm-electron" +mkdir -p "$npm_config_cache" \ No newline at end of file diff --git a/scripts/monaco-editor-setup.js b/scripts/monaco-editor-setup.js new file mode 100644 index 0000000000..c656306d5c --- /dev/null +++ b/scripts/monaco-editor-setup.js @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +var fs = require('fs'); +var cp = require('child_process'); +var path = require('path'); + +var ROOT = path.join(__dirname, '..'); +var ROOT_NODE_MODULES_PATH = path.join(ROOT, 'node_modules'); +var EDITOR_ROOT = path.join(ROOT, 'build/monaco') +var EDITOR_NODE_MODULES_PATH = path.join(EDITOR_ROOT, 'node_modules') + +var cmd = `npm install`; +cp.execSync(cmd, { + cwd: EDITOR_ROOT, + stdio:[0,1,2] +}); + +if (!fs.existsSync(ROOT_NODE_MODULES_PATH)) { + fs.mkdirSync(ROOT_NODE_MODULES_PATH); +} + +// Move deps over +var modules = fs.readdirSync(EDITOR_NODE_MODULES_PATH); +modules.forEach(function(module) { + var src = path.join(EDITOR_NODE_MODULES_PATH, module); + var dst = path.join(ROOT_NODE_MODULES_PATH, module); + if (!fs.existsSync(dst)) { + console.log('Moving ' + module + '...'); + fs.renameSync(src, dst); + } else { + console.log('Skipping moving ' + module + '.'); + } +}); diff --git a/scripts/npm.bat b/scripts/npm.bat new file mode 100644 index 0000000000..2759655c8b --- /dev/null +++ b/scripts/npm.bat @@ -0,0 +1,8 @@ +@echo off +setlocal +set npm_config_disturl="https://atom.io/download/electron" +for /f "tokens=2 delims=:, " %%a in ('findstr /R /C:"\"electronVersion\":.*" "%~dp0..\package.json"') do set npm_config_target=%%~a +set npm_config_runtime="electron" +set npm_config_cache=~\.npm-electron +npm %* +endlocal diff --git a/scripts/npm.sh b/scripts/npm.sh new file mode 100644 index 0000000000..69c6d0c48a --- /dev/null +++ b/scripts/npm.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname "$(dirname "$(realpath "$0")")") + npm_config_arch=x64 +else + ROOT=$(dirname "$(dirname "$(readlink -f $0)")") + + # if [ -z $npm_config_arch ]; then + # npm_config_arch=$(node -p process.arch) + # echo "Warning: remember to set \$npm_config_arch to either x64 or ia32 to build the binaries for the right architecture. Picking '$npm_config_arch'." + # fi +fi + +ELECTRON_VERSION=$( + cat "$ROOT"/package.json | + grep electronVersion | + sed -e 's/[[:space:]]*"electronVersion":[[:space:]]*"\([0-9.]*\)"\(,\)*/\1/' +) + +ELECTRON_GYP_HOME=~/.electron-gyp +mkdir -p $ELECTRON_GYP_HOME + +npm_config_disturl=https://atom.io/download/electron \ +npm_config_target=$ELECTRON_VERSION \ +npm_config_runtime=electron \ +HOME=$ELECTRON_GYP_HOME \ +npm $* \ No newline at end of file diff --git a/scripts/sql-cli.sh b/scripts/sql-cli.sh new file mode 100644 index 0000000000..d04f7cced9 --- /dev/null +++ b/scripts/sql-cli.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname $(dirname $(realpath "$0"))) +else + ROOT=$(dirname $(dirname $(readlink -f $0))) +fi + +function code() { + cd $ROOT + + if [[ "$OSTYPE" == "darwin"* ]]; then + NAME=`node -p "require('./product.json').nameLong"` + CODE="./.build/electron/$NAME.app/Contents/MacOS/Electron" + else + NAME=`node -p "require('./product.json').applicationName"` + CODE=".build/electron/$NAME" + fi + + INTENDED_VERSION="v`node -p "require('./package.json').electronVersion"`" + INSTALLED_VERSION=`cat .build/electron/version 2> /dev/null` + + # Node modules + test -d node_modules || ./scripts/npm.sh install + + # Get electron + (test -f "$CODE" && [ $INTENDED_VERSION == $INSTALLED_VERSION ]) || ./node_modules/.bin/gulp electron + + # Build + test -d out || ./node_modules/.bin/gulp compile + + ELECTRON_RUN_AS_NODE=1 \ + NODE_ENV=development \ + VSCODE_DEV=1 \ + ELECTRON_ENABLE_LOGGING=1 \ + ELECTRON_ENABLE_STACK_DUMPING=1 \ + "$CODE" --debug=5874 "$ROOT/out/cli.js" . "$@" +} + +code "$@" diff --git a/scripts/sql.bat b/scripts/sql.bat new file mode 100644 index 0000000000..4faa719c1b --- /dev/null +++ b/scripts/sql.bat @@ -0,0 +1,40 @@ +@echo off +setlocal + +title VSCode Dev + +pushd %~dp0\.. + +:: Node modules +if not exist node_modules call .\scripts\npm.bat install + +for /f "tokens=2 delims=:," %%a in ('findstr /R /C:"\"nameShort\":.*" product.json') do set NAMESHORT=%%~a +set NAMESHORT=%NAMESHORT: "=% +set NAMESHORT=%NAMESHORT:"=%.exe +set CODE=".build\electron\%NAMESHORT%" + +for /f "tokens=2 delims=:," %%a in ('findstr /R /C:"\"electronVersion\":.*" package.json') do set DESIREDVERSION=%%~a +set DESIREDVERSION=%DESIREDVERSION: "=% +set DESIREDVERSION=v%DESIREDVERSION:"=% +if exist .\.build\electron\version (set /p INSTALLEDVERSION=<.\.build\electron\version) else (set INSTALLEDVERSION="") + +:: Get electron +if not exist %CODE% node .\node_modules\gulp\bin\gulp.js electron +if not "%INSTALLEDVERSION%" == "%DESIREDVERSION%" node .\node_modules\gulp\bin\gulp.js electron + +:: Build +if not exist out node .\node_modules\gulp\bin\gulp.js compile + +:: Configuration +set NODE_ENV=development +set VSCODE_DEV=1 +set VSCODE_CLI=1 +set ELECTRON_DEFAULT_ERROR_MODE=1 +set ELECTRON_ENABLE_LOGGING=1 +set ELECTRON_ENABLE_STACK_DUMPING=1 + +:: Launch Code +%CODE% . %* +popd + +endlocal diff --git a/scripts/sql.sh b/scripts/sql.sh new file mode 100644 index 0000000000..fe3d014c63 --- /dev/null +++ b/scripts/sql.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname $(dirname $(realpath "$0"))) +else + ROOT=$(dirname $(dirname $(readlink -f $0))) +fi + +function code() { + cd $ROOT + + if [[ "$OSTYPE" == "darwin"* ]]; then + NAME=`node -p "require('./product.json').nameLong"` + CODE="./.build/electron/$NAME.app/Contents/MacOS/Electron" + else + NAME=`node -p "require('./product.json').applicationName"` + CODE=".build/electron/$NAME" + fi + + INTENDED_VERSION="v`node -p "require('./package.json').electronVersion"`" + INSTALLED_VERSION=`cat .build/electron/version 2> /dev/null` + + # Node modules + test -d node_modules || ./scripts/npm.sh install + + # Get electron + (test -f "$CODE" && [ $INTENDED_VERSION == $INSTALLED_VERSION ]) || ./node_modules/.bin/gulp electron + + # Build + test -d out || ./node_modules/.bin/gulp compile + + # Configuration + export NODE_ENV=development + export VSCODE_DEV=1 + export VSCODE_CLI=1 + export ELECTRON_ENABLE_LOGGING=1 + export ELECTRON_ENABLE_STACK_DUMPING=1 + + # Launch Code + exec "$CODE" . "$@" +} + +code "$@" diff --git a/scripts/test-electron.bat b/scripts/test-electron.bat new file mode 100644 index 0000000000..0f7ce8e9a8 --- /dev/null +++ b/scripts/test-electron.bat @@ -0,0 +1,19 @@ +@echo off +setlocal + +set ELECTRON_RUN_AS_NODE= + +pushd %~dp0\.. + +for /f "tokens=2 delims=:," %%a in ('findstr /R /C:"\"nameShort\":.*" product.json') do set NAMESHORT=%%~a +set NAMESHORT=%NAMESHORT: "=% +set NAMESHORT=%NAMESHORT:"=%.exe +set CODE=".build\electron\%NAMESHORT%" + +rem Run tests in electron +%CODE% .\test\electron\index.js %* + +popd + +endlocal +exit /b %errorlevel% diff --git a/scripts/test-electron.sh b/scripts/test-electron.sh new file mode 100644 index 0000000000..c8318a434d --- /dev/null +++ b/scripts/test-electron.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname $(dirname $(realpath "$0"))) +else + ROOT=$(dirname $(dirname $(readlink -f $0))) +fi + +cd $ROOT + +if [[ "$OSTYPE" == "darwin"* ]]; then + NAME=`node -p "require('./product.json').nameLong"` + CODE="./.build/electron/$NAME.app/Contents/MacOS/Electron" +else + NAME=`node -p "require('./product.json').applicationName"` + CODE=".build/electron/$NAME" +fi + +INTENDED_VERSION="v`node -p "require('./package.json').electronVersion"`" +INSTALLED_VERSION=$(cat .build/electron/version 2> /dev/null) + +# Node modules +test -d node_modules || ./scripts/npm.sh install + +# Get electron +(test -f "$CODE" && [ $INTENDED_VERSION == $INSTALLED_VERSION ]) || ./node_modules/.bin/gulp electron + +# Build +test -d out || ./node_modules/.bin/gulp compile +echo "code $CODE" +# Unit Tests +export VSCODE_DEV=1 +if [[ "$OSTYPE" == "darwin"* ]]; then + cd $ROOT ; ulimit -n 4096 ; \ + "$CODE" \ + test/electron/index.js "$@" +else + cd $ROOT ; \ + "$CODE" \ + test/electron/index.js "$@" +fi diff --git a/scripts/test-int-mocha.bat b/scripts/test-int-mocha.bat new file mode 100644 index 0000000000..482aadc5bd --- /dev/null +++ b/scripts/test-int-mocha.bat @@ -0,0 +1 @@ +.\scripts\test.bat --runGlob **\*.integrationTest.js -g integration %* diff --git a/scripts/test-int-mocha.sh b/scripts/test-int-mocha.sh new file mode 100644 index 0000000000..c0a8913886 --- /dev/null +++ b/scripts/test-int-mocha.sh @@ -0,0 +1 @@ +./scripts/test.sh --runGlob **/*.integrationTest.js -g integration "$@" \ No newline at end of file diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat new file mode 100644 index 0000000000..3d83e302c7 --- /dev/null +++ b/scripts/test-integration.bat @@ -0,0 +1,20 @@ +@echo off +setlocal + +pushd %~dp0\.. + +if not "%APPVEYOR%" == "" ( + set ELECTRON_RUN_AS_NODE= +) +set VSCODEUSERDATADIR=%TMP%\vscodeuserfolder-%RANDOM%-%TIME:~6,5% + +:: Integration Tests +.\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% +.\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --user-data-dir=%VSCODEUSERDATADIR% +.\scripts\test-int-mocha.bat + +rmdir /s /q %VSCODEUSERDATADIR% + +popd + +endlocal diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh new file mode 100644 index 0000000000..2b4b263378 --- /dev/null +++ b/scripts/test-integration.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname $(dirname $(realpath "$0"))) + VSCODEUSERDATADIR=`mktemp -d -t 'myuserdatadir'` +else + ROOT=$(dirname $(dirname $(readlink -f $0))) + VSCODEUSERDATADIR=`mktemp -d 2>/dev/null` +fi + +cd $ROOT + +# Integration Tests +./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out --disableExtensions --user-data-dir=$VSCODEUSERDATADIR +./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --user-data-dir=$VSCODEUSERDATADIR +./scripts/test-int-mocha.sh + +rm -r $VSCODEUSERDATADIR diff --git a/scripts/test-mocha.bat b/scripts/test-mocha.bat new file mode 100644 index 0000000000..e1aa4bef47 --- /dev/null +++ b/scripts/test-mocha.bat @@ -0,0 +1,25 @@ +@echo off +setlocal + +set ELECTRON_RUN_AS_NODE=1 + +pushd %~dp0\.. + +for /f "tokens=2 delims=:," %%a in ('findstr /R /C:"\"nameShort\":.*" product.json') do set NAMESHORT=%%~a +set NAMESHORT=%NAMESHORT: "=% +set NAMESHORT=%NAMESHORT:"=%.exe +set CODE=".build\electron\%NAMESHORT%" + +rem TFS Builds +if not "%BUILD_BUILDID%" == "" ( + %CODE% .\node_modules\mocha\bin\_mocha %* +) + +rem Otherwise +if "%BUILD_BUILDID%" == "" ( + %CODE% .\node_modules\mocha\bin\_mocha --reporter dot %* +) +popd + +endlocal +exit /b %errorlevel% \ No newline at end of file diff --git a/scripts/test-mocha.sh b/scripts/test-mocha.sh new file mode 100644 index 0000000000..9aa16fa324 --- /dev/null +++ b/scripts/test-mocha.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname $(dirname $(realpath "$0"))) +else + ROOT=$(dirname $(dirname $(readlink -f $0))) +fi + +cd $ROOT + +if [[ "$OSTYPE" == "darwin"* ]]; then + NAME=`node -p "require('./product.json').nameLong"` + CODE="./.build/electron/$NAME.app/Contents/MacOS/Electron" +else + NAME=`node -p "require('./product.json').applicationName"` + CODE=".build/electron/$NAME" +fi + +INTENDED_VERSION="v`node -p "require('./package.json').electronVersion"`" +INSTALLED_VERSION=$(cat .build/electron/version 2> /dev/null) + +# Node modules +test -d node_modules || ./scripts/npm.sh install + +# Get electron +(test -f "$CODE" && [ $INTENDED_VERSION == $INSTALLED_VERSION ]) || ./node_modules/.bin/gulp electron + +# Build +test -d out || ./node_modules/.bin/gulp compile + +# Unit Tests +export VSCODE_DEV=1 +if [[ "$OSTYPE" == "darwin"* ]]; then + cd $ROOT ; ulimit -n 4096 ; ELECTRON_RUN_AS_NODE=1 \ + "$CODE" \ + node_modules/mocha/bin/_mocha "$@" +else + cd $ROOT ; ELECTRON_RUN_AS_NODE=1 \ + "$CODE" \ + node_modules/mocha/bin/_mocha "$@" +fi diff --git a/scripts/test.bat b/scripts/test.bat new file mode 100644 index 0000000000..e1aa4bef47 --- /dev/null +++ b/scripts/test.bat @@ -0,0 +1,25 @@ +@echo off +setlocal + +set ELECTRON_RUN_AS_NODE=1 + +pushd %~dp0\.. + +for /f "tokens=2 delims=:," %%a in ('findstr /R /C:"\"nameShort\":.*" product.json') do set NAMESHORT=%%~a +set NAMESHORT=%NAMESHORT: "=% +set NAMESHORT=%NAMESHORT:"=%.exe +set CODE=".build\electron\%NAMESHORT%" + +rem TFS Builds +if not "%BUILD_BUILDID%" == "" ( + %CODE% .\node_modules\mocha\bin\_mocha %* +) + +rem Otherwise +if "%BUILD_BUILDID%" == "" ( + %CODE% .\node_modules\mocha\bin\_mocha --reporter dot %* +) +popd + +endlocal +exit /b %errorlevel% \ No newline at end of file diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100644 index 0000000000..446de63886 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname $(dirname $(realpath "$0"))) +else + ROOT=$(dirname $(dirname $(readlink -f $0))) +fi + +cd $ROOT + +if [[ "$OSTYPE" == "darwin"* ]]; then + NAME=`node -p "require('./product.json').nameLong"` + CODE="./.build/electron/$NAME.app/Contents/MacOS/Electron" +else + NAME=`node -p "require('./product.json').applicationName"` + CODE=".build/electron/$NAME" +fi + +INTENDED_VERSION="v`node -p "require('./package.json').electronVersion"`" +INSTALLED_VERSION=$(cat .build/electron/version 2> /dev/null) + +# Node modules +test -d node_modules || ./scripts/npm.sh install + +# Get electron +(test -f "$CODE" && [ $INTENDED_VERSION == $INSTALLED_VERSION ]) || ./node_modules/.bin/gulp electron + +# Build +test -d out || ./node_modules/.bin/gulp compile + +# Unit Tests +export VSCODE_DEV=1 +if [[ "$OSTYPE" == "darwin"* ]]; then + cd $ROOT ; ulimit -n 4096 ; ELECTRON_RUN_AS_NODE=1 \ + "$CODE" \ + node_modules/mocha/bin/_mocha "$@" +else + cd $ROOT ; ELECTRON_RUN_AS_NODE=1 \ + "$CODE" \ + node_modules/mocha/bin/_mocha "$@" +fi \ No newline at end of file diff --git a/scripts/watch.bat b/scripts/watch.bat new file mode 100644 index 0000000000..061b640cdf --- /dev/null +++ b/scripts/watch.bat @@ -0,0 +1,4 @@ +CALL gulp --max_old_space_size=2000 watch || goto :error +:error +echo Failed with error #%errorlevel% +exit /b %errorlevel% diff --git a/scripts/watch.sh b/scripts/watch.sh new file mode 100644 index 0000000000..139f3ab469 --- /dev/null +++ b/scripts/watch.sh @@ -0,0 +1,2 @@ +#!/bin/bash +gulp --max_old_space_size=2000 watch || { echo 'gulp electron failed' ; exit 1; } diff --git a/src/.eslintrc b/src/.eslintrc new file mode 100644 index 0000000000..c25b0d558c --- /dev/null +++ b/src/.eslintrc @@ -0,0 +1,19 @@ +{ + "parserOptions": { + "ecmaVersion": 6 + }, + "env": { + "node": true, + "es6": true, + "browser": true, + "amd": true + }, + "rules": { + "no-console": 0, + "no-cond-assign": 0, + "no-unused-vars": "error", + "no-extra-semi": "error", + "semi": "error", + "no-inner-declarations": 0 + } +} \ No newline at end of file diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js new file mode 100644 index 0000000000..e6023153e7 --- /dev/null +++ b/src/bootstrap-amd.js @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +var path = require('path'); +var loader = require('./vs/loader'); + +function uriFromPath(_path) { + var pathName = path.resolve(_path).replace(/\\/g, '/'); + + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = '/' + pathName; + } + + return encodeURI('file://' + pathName); +} + +var rawNlsConfig = process.env['VSCODE_NLS_CONFIG']; +var nlsConfig = rawNlsConfig ? JSON.parse(rawNlsConfig) : { availableLanguages: {} }; + +loader.config({ + baseUrl: uriFromPath(__dirname), + catchError: true, + nodeRequire: require, + nodeMain: __filename, + 'vs/nls': nlsConfig, + nodeCachedDataDir: process.env['VSCODE_NODE_CACHED_DATA_DIR_' + process.pid] +}); + +if (nlsConfig.pseudo) { + loader(['vs/nls'], function (nlsPlugin) { + nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); + }); +} + +exports.bootstrap = function (entrypoint) { + if (!entrypoint) { + return; + } + + loader([entrypoint], function () { }, function (err) { console.error(err); }); +}; diff --git a/src/bootstrap.js b/src/bootstrap.js new file mode 100644 index 0000000000..96c34331e9 --- /dev/null +++ b/src/bootstrap.js @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// disable electron's asar support early on because bootstrap.js is used in forked processes +// where the environment is purely node based. this will instruct electron to not treat files +// with *.asar ending any special from normal files. +process.noAsar = true; + +// Will be defined if we got forked from another node process +// In that case we override console.log/warn/error to be able +// to send loading issues to the main side for logging. +if (!!process.send && process.env.PIPE_LOGGING === 'true') { + var MAX_LENGTH = 100000; + + // Prevent circular stringify + function safeStringify(args) { + var seen = []; + var res; + + // Massage some arguments with special treatment + if (args.length) { + for (var i = 0; i < args.length; i++) { + + // Any argument of type 'undefined' needs to be specially treated because + // JSON.stringify will simply ignore those. We replace them with the string + // 'undefined' which is not 100% right, but good enough to be logged to console + if (typeof args[i] === 'undefined') { + args[i] = 'undefined'; + } + + // Any argument that is an Error will be changed to be just the error stack/message + // itself because currently cannot serialize the error over entirely. + else if (args[i] instanceof Error) { + var errorObj = args[i]; + if (errorObj.stack) { + args[i] = errorObj.stack; + } else { + args[i] = errorObj.toString(); + } + } + } + } + + try { + res = JSON.stringify(args, function (key, value) { + + // Objects get special treatment to prevent circles + if (value && Object.prototype.toString.call(value) === '[object Object]') { + if (seen.indexOf(value) !== -1) { + return Object.create(null); // prevent circular references! + } + + seen.push(value); + } + + return value; + }); + } catch (error) { + return 'Output omitted for an object that cannot be inspected (' + error.toString() + ')'; + } + + if (res && res.length > MAX_LENGTH) { + return 'Output omitted for a large object that exceeds the limits'; + } + + return res; + } + + function safeSend(arg) { + try { + process.send(arg); + } catch (error) { + // Can happen if the parent channel is closed meanwhile + } + } + + // Pass console logging to the outside so that we have it in the main side if told so + if (process.env.VERBOSE_LOGGING === 'true') { + console.log = function () { safeSend({ type: '__$console', severity: 'log', arguments: safeStringify(arguments) }); }; + console.info = function () { safeSend({ type: '__$console', severity: 'log', arguments: safeStringify(arguments) }); }; + console.warn = function () { safeSend({ type: '__$console', severity: 'warn', arguments: safeStringify(arguments) }); }; + } else { + console.log = function () { /* ignore */ }; + console.warn = function () { /* ignore */ }; + console.info = function () { /* ignore */ }; + } + + console.error = function () { safeSend({ type: '__$console', severity: 'error', arguments: safeStringify(arguments) }); }; +} + +if (!process.env['VSCODE_ALLOW_IO']) { + // Let stdout, stderr and stdin be no-op streams. This prevents an issue where we would get an EBADF + // error when we are inside a forked process and this process tries to access those channels. + var stream = require('stream'); + var writable = new stream.Writable({ + write: function () { /* No OP */ } + }); + + process.__defineGetter__('stdout', function () { return writable; }); + process.__defineGetter__('stderr', function () { return writable; }); + process.__defineGetter__('stdin', function () { return writable; }); +} + +if (!process.env['VSCODE_HANDLES_UNCAUGHT_ERRORS']) { + // Handle uncaught exceptions + process.on('uncaughtException', function (err) { + console.error('Uncaught Exception: ', err.toString()); + if (err.stack) { + console.error(err.stack); + } + }); +} + +// Kill oneself if one's parent dies. Much drama. +if (process.env['VSCODE_PARENT_PID']) { + const parentPid = Number(process.env['VSCODE_PARENT_PID']); + + if (typeof parentPid === 'number' && !isNaN(parentPid)) { + setInterval(function () { + try { + process.kill(parentPid, 0); // throws an exception if the main process doesn't exist anymore. + } catch (e) { + process.exit(); + } + }, 5000); + } +} + +const crashReporterOptionsRaw = process.env['CRASH_REPORTER_START_OPTIONS']; +if (typeof crashReporterOptionsRaw === 'string') { + try { + const crashReporterOptions = JSON.parse(crashReporterOptionsRaw); + if (crashReporterOptions) { + process.crashReporter.start(crashReporterOptions); + } + } catch (error) { + console.error(error); + } +} + +require('./bootstrap-amd').bootstrap(process.env['AMD_ENTRYPOINT']); diff --git a/src/buildfile.js b/src/buildfile.js new file mode 100644 index 0000000000..c1873b7395 --- /dev/null +++ b/src/buildfile.js @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +exports.base = [{ + name: 'vs/base/common/worker/simpleWorker', + include: [ 'vs/editor/common/services/editorSimpleWorker' ], + prepend: [ 'vs/loader.js' ], + append: [ 'vs/base/worker/workerMain' ], + dest: 'vs/base/worker/workerMain.js' +}]; +exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.main']); +exports.code = require('./vs/code/buildfile').collectModules(); + +exports.entrypoint = function (name) { + return [{ name: name, include: [], exclude: ['vs/css', 'vs/nls'] }]; +}; diff --git a/src/cli.js b/src/cli.js new file mode 100644 index 0000000000..e63e5f137f --- /dev/null +++ b/src/cli.js @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +require('./bootstrap-amd').bootstrap('vs/code/node/cli'); \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000000..d66ee48cb3 --- /dev/null +++ b/src/main.js @@ -0,0 +1,228 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +if (process.argv.indexOf('--prof-startup') >= 0) { + var profiler = require('v8-profiler'); + var prefix = require('crypto').randomBytes(2).toString('hex'); + process.env.VSCODE_PROFILES_PREFIX = prefix; + profiler.startProfiling('main', true); +} + +// Perf measurements +global.perfStartTime = Date.now(); + +var app = require('electron').app; +var fs = require('fs'); +var path = require('path'); +var minimist = require('minimist'); +var paths = require('./paths'); + +var args = minimist(process.argv, { + string: ['user-data-dir', 'locale'] +}); + +function stripComments(content) { + var regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; + var result = content.replace(regexp, function (match, m1, m2, m3, m4) { + // Only one of m1, m2, m3, m4 matches + if (m3) { + // A block comment. Replace with nothing + return ''; + } + else if (m4) { + // A line comment. If it ends in \r?\n then keep it. + var length_1 = m4.length; + if (length_1 > 2 && m4[length_1 - 1] === '\n') { + return m4[length_1 - 2] === '\r' ? '\r\n' : '\n'; + } + else { + return ''; + } + } + else { + // We match a string + return match; + } + }); + return result; +} + +function getNLSConfiguration() { + var locale = args['locale']; + + if (!locale) { + var userData = app.getPath('userData'); + var localeConfig = path.join(userData, 'User', 'locale.json'); + if (fs.existsSync(localeConfig)) { + try { + var content = stripComments(fs.readFileSync(localeConfig, 'utf8')); + var value = JSON.parse(content).locale; + if (value && typeof value === 'string') { + locale = value; + } + } catch (e) { + // noop + } + } + } + + var appLocale = app.getLocale(); + locale = locale || appLocale; + // Language tags are case insensitve however an amd loader is case sensitive + // To make this work on case preserving & insensitive FS we do the following: + // the language bundles have lower case language tags and we always lower case + // the locale we receive from the user or OS. + locale = locale ? locale.toLowerCase() : locale; + if (locale === 'pseudo') { + return { locale: locale, availableLanguages: {}, pseudo: true }; + } + var initialLocale = locale; + if (process.env['VSCODE_DEV']) { + return { locale: locale, availableLanguages: {} }; + } + + // We have a built version so we have extracted nls file. Try to find + // the right file to use. + + // Check if we have an English locale. If so fall to default since that is our + // English translation (we don't ship *.nls.en.json files) + if (locale && (locale == 'en' || locale.startsWith('en-'))) { + return { locale: locale, availableLanguages: {} }; + } + + function resolveLocale(locale) { + while (locale) { + var candidate = path.join(__dirname, 'vs', 'code', 'electron-main', 'main.nls.') + locale + '.js'; + if (fs.existsSync(candidate)) { + return { locale: initialLocale, availableLanguages: { '*': locale } }; + } else { + var index = locale.lastIndexOf('-'); + if (index > 0) { + locale = locale.substring(0, index); + } else { + locale = null; + } + } + } + return null; + } + + var resolvedLocale = resolveLocale(locale); + if (!resolvedLocale && appLocale && appLocale !== locale) { + resolvedLocale = resolveLocale(appLocale); + } + return resolvedLocale ? resolvedLocale : { locale: initialLocale, availableLanguages: {} }; +} + +function getNodeCachedDataDir() { + + // IEnvironmentService.isBuilt + if (process.env['VSCODE_DEV']) { + return Promise.resolve(undefined); + } + + // find commit id + var productJson = require(path.join(__dirname, '../product.json')); + if (!productJson.commit) { + return Promise.resolve(undefined); + } + + var dir = path.join(app.getPath('userData'), 'CachedData', productJson.commit); + + return mkdirp(dir).then(undefined, function () { /*ignore*/ }); +} + +function mkdirp(dir) { + return mkdir(dir) + .then(null, function (err) { + if (err && err.code === 'ENOENT') { + var parent = path.dirname(dir); + if (parent !== dir) { // if not arrived at root + return mkdirp(parent) + .then(function () { + return mkdir(dir); + }); + } + } + throw err; + }); +} + +function mkdir(dir) { + return new Promise(function (resolve, reject) { + fs.mkdir(dir, function (err) { + if (err && err.code !== 'EEXIST') { + reject(err); + } else { + resolve(dir); + } + }); + }); +} + +// Set userData path before app 'ready' event and call to process.chdir +var userData = path.resolve(args['user-data-dir'] || paths.getDefaultUserDataPath(process.platform)); +app.setPath('userData', userData); + +// Update cwd based on environment and platform +try { + if (process.platform === 'win32') { + process.env['VSCODE_CWD'] = process.cwd(); // remember as environment variable + process.chdir(path.dirname(app.getPath('exe'))); // always set application folder as cwd + } else if (process.env['VSCODE_CWD']) { + process.chdir(process.env['VSCODE_CWD']); + } +} catch (err) { + console.error(err); +} + +// Mac: when someone drops a file to the not-yet running VSCode, the open-file event fires even before +// the app-ready event. We listen very early for open-file and remember this upon startup as path to open. +global.macOpenFiles = []; +app.on('open-file', function (event, path) { + global.macOpenFiles.push(path); +}); + +var openUrls = []; +var onOpenUrl = function (event, url) { + event.preventDefault(); + openUrls.push(url); +}; + +app.on('will-finish-launching', function () { + app.on('open-url', onOpenUrl); +}); + +global.getOpenUrls = function () { + app.removeListener('open-url', onOpenUrl); + return openUrls; +}; + + +// use '/CachedData'-directory to store +// node/v8 cached data. +var nodeCachedDataDir = getNodeCachedDataDir().then(function (value) { + if (value) { + // store the data directory + process.env['VSCODE_NODE_CACHED_DATA_DIR_' + process.pid] = value; + + // tell v8 to not be lazy when parsing JavaScript. Generally this makes startup slower + // but because we generate cached data it makes subsequent startups much faster + app.commandLine.appendSwitch('--js-flags', '--nolazy'); + } +}); + +// Load our code once ready +app.once('ready', function () { + global.perfAppReady = Date.now(); + var nlsConfig = getNLSConfiguration(); + process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig); + + nodeCachedDataDir.then(function () { + require('./bootstrap-amd').bootstrap('vs/code/electron-main/main'); + }, console.error); +}); diff --git a/src/paths.js b/src/paths.js new file mode 100644 index 0000000000..bcdaed30f6 --- /dev/null +++ b/src/paths.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +var path = require('path'); +var os = require('os'); +var pkg = require('../package.json'); + +function getAppDataPath(platform) { + switch (platform) { + case 'win32': return process.env['VSCODE_APPDATA'] || process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming'); + case 'darwin': return process.env['VSCODE_APPDATA'] || path.join(os.homedir(), 'Library', 'Application Support'); + case 'linux': return process.env['VSCODE_APPDATA'] || process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); + default: throw new Error('Platform not supported'); + } +} + +function getDefaultUserDataPath(platform) { + return path.join(getAppDataPath(platform), pkg.name); +} + +exports.getAppDataPath = getAppDataPath; +exports.getDefaultUserDataPath = getDefaultUserDataPath; \ No newline at end of file diff --git a/src/sql/base/browser/ui/breadcrumb/breadcrumb.component.ts b/src/sql/base/browser/ui/breadcrumb/breadcrumb.component.ts new file mode 100644 index 0000000000..88bb8ff0e2 --- /dev/null +++ b/src/sql/base/browser/ui/breadcrumb/breadcrumb.component.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/media/icons/common-icons'; +import 'vs/css!./media/breadcrumb'; + +import { Component, Inject, forwardRef, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core'; +import { Router } from '@angular/router'; + +import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils'; +import { IBreadcrumbService, MenuItem } from './interfaces'; + +import { IDisposable } from 'vs/base/common/lifecycle'; + +@Component({ + selector: 'breadcrumb', + template: ` + + + + + {{item.label}} + {{item.label}} + {{item.label}} + + + + + ` +}) +export class BreadcrumbComponent implements OnInit, OnDestroy { + private menuItems: MenuItem[] = []; + private disposables: Array = new Array(); + + constructor( + @Inject(forwardRef(() => IBreadcrumbService)) private _breadcrumbService: IBreadcrumbService, + @Inject(forwardRef(() => Router)) private _router: Router, + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef + ) { } + + ngOnInit() { + this.disposables.push(toDisposableSubscription(this._breadcrumbService.breadcrumbItem.subscribe((item) => this.updateCrumb(item)))); + } + + ngOnDestroy() { + this.disposables.forEach(item => item.dispose()); + } + + private updateCrumb(items: MenuItem[]) { + this.menuItems = items; + this._changeRef.detectChanges(); + } + + public route(link: any[]): void { + this._router.navigate(link); + } +} diff --git a/src/sql/base/browser/ui/breadcrumb/interfaces.ts b/src/sql/base/browser/ui/breadcrumb/interfaces.ts new file mode 100644 index 0000000000..a75b68c691 --- /dev/null +++ b/src/sql/base/browser/ui/breadcrumb/interfaces.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { InjectionToken, EventEmitter } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; + +export const IBreadcrumbService = new InjectionToken('breadcrumbService'); + +export interface IBreadcrumbService { + breadcrumbItem: Subject; + setBreadcrumbs(any): void; +} + +export interface MenuItem { + label?: string; + icon?: string; + command?: (event?: any) => void; + url?: string; + routerLink?: any[]; + eventEmitter?: EventEmitter; + items?: MenuItem[]; + expanded?: boolean; + disabled?: boolean; + visible?: boolean; + target?: string; + routerLinkActiveOptions?: any; +} diff --git a/src/sql/base/browser/ui/breadcrumb/media/breadcrumb.css b/src/sql/base/browser/ui/breadcrumb/media/breadcrumb.css new file mode 100644 index 0000000000..e263f918b8 --- /dev/null +++ b/src/sql/base/browser/ui/breadcrumb/media/breadcrumb.css @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +.vs .chevron-right.icon { + background-image: url("chevron_right.svg"); +} + +.vs-dark .chevron-right.icon, +.hc-black .chevron-right.icon { + background-image: url("chevron_right_inverse.svg"); +} + +breadcrumb .icon { + background-size: 16px; + background-position: 50% 50%; + background-repeat: no-repeat; + padding: 8px; +} + +breadcrumb .chevron-right.icon { + background-size: 16px; + background-position: 50% 50%; + background-repeat: no-repeat; + padding: 8px; +} + +breadcrumb .router-link { + cursor: pointer; +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/breadcrumb/media/chevron_right.svg b/src/sql/base/browser/ui/breadcrumb/media/chevron_right.svg new file mode 100644 index 0000000000..8e88dd9f4d --- /dev/null +++ b/src/sql/base/browser/ui/breadcrumb/media/chevron_right.svg @@ -0,0 +1 @@ +chevron_right \ No newline at end of file diff --git a/src/sql/base/browser/ui/breadcrumb/media/chevron_right_inverse.svg b/src/sql/base/browser/ui/breadcrumb/media/chevron_right_inverse.svg new file mode 100644 index 0000000000..f16b070639 --- /dev/null +++ b/src/sql/base/browser/ui/breadcrumb/media/chevron_right_inverse.svg @@ -0,0 +1 @@ +chevron_right_inverse \ No newline at end of file diff --git a/src/sql/base/browser/ui/checkbox/checkbox.ts b/src/sql/base/browser/ui/checkbox/checkbox.ts new file mode 100644 index 0000000000..994c5c0e78 --- /dev/null +++ b/src/sql/base/browser/ui/checkbox/checkbox.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/base/browser/ui/checkbox/media/checkbox'; +import { Checkbox as vsCheckbox, ICheckboxOpts, ICheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox'; +import { Color } from 'vs/base/common/color'; + +const defaultOpts = { + inputActiveOptionBorder: Color.fromHex('#007ACC'), + actionClassName: ' sql-checkbox' +}; + +/** + * Extends Checkbox to include Carbon checkbox icon and styling. + */ +export class Checkbox extends vsCheckbox { + private _inputActiveOptionBorder: Color; + + constructor(opts: ICheckboxOpts) { + super({ + actionClassName: opts.actionClassName + defaultOpts.actionClassName, + title: opts.title, + isChecked: opts.isChecked, + onChange: opts.onChange, + onKeyDown: opts.onKeyDown, + inputActiveOptionBorder: opts.inputActiveOptionBorder + }); + this._inputActiveOptionBorder = opts.inputActiveOptionBorder ? opts.inputActiveOptionBorder : defaultOpts.inputActiveOptionBorder; + } + + public enable(): void { + super.enable(); + this.domNode.classList.remove('disabled'); + } + + public disable(): void { + super.disable(); + this.domNode.classList.add('disabled'); + } + + public style(styles: ICheckboxStyles): void { + if (styles.inputActiveOptionBorder) { + this._inputActiveOptionBorder = styles.inputActiveOptionBorder; + } + this.applyStyles(); + } + + protected applyStyles(): void { + if (this.domNode) { + this.domNode.style.borderColor = this._inputActiveOptionBorder ? this._inputActiveOptionBorder.toString(): defaultOpts.inputActiveOptionBorder.toString(); + } + } +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/checkbox/defaultCheckbox.ts b/src/sql/base/browser/ui/checkbox/defaultCheckbox.ts new file mode 100644 index 0000000000..b7acac8249 --- /dev/null +++ b/src/sql/base/browser/ui/checkbox/defaultCheckbox.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Event, { Emitter } from 'vs/base/common/event'; +import * as DOM from 'vs/base/browser/dom'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; + +export interface ICheckboxOptions { + label: string; + enabled?: boolean; + checked?: boolean; +} + +export class Checkbox extends Disposable { + private _el: HTMLInputElement; + private _label: HTMLSpanElement; + + private _onChange = new Emitter(); + public readonly onChange: Event = this._onChange.event; + + constructor(container: HTMLElement, opts: ICheckboxOptions) { + super(); + + this._el = document.createElement('input'); + this._el.type = 'checkbox'; + + this._register(DOM.addDisposableListener(this._el, DOM.EventType.CHANGE, e => { + this._onChange.fire(e); + })); + + this._register(DOM.addStandardDisposableListener(this._el, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => { + if (e.equals(KeyCode.Enter)) { + this.checked = !this.checked; + e.stopPropagation(); + } + })); + + this._label = document.createElement('span'); + + this.label = opts.label; + this.enabled = opts.enabled; + this.checked = opts.checked; + + container.appendChild(this._el); + container.appendChild(this._label); + } + + public set label(val: string) { + this._label.innerText = val; + } + + public set enabled(val: boolean) { + this._el.disabled = !val; + } + + public get enabled(): boolean { + return !this._el.disabled; + } + + public set checked(val: boolean) { + this._el.checked = val; + } + + public get checked(): boolean { + return this._el.checked; + } +} diff --git a/src/sql/base/browser/ui/checkbox/media/check.svg b/src/sql/base/browser/ui/checkbox/media/check.svg new file mode 100644 index 0000000000..544245b73e --- /dev/null +++ b/src/sql/base/browser/ui/checkbox/media/check.svg @@ -0,0 +1 @@ +check_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/checkbox/media/check_inverse.svg b/src/sql/base/browser/ui/checkbox/media/check_inverse.svg new file mode 100644 index 0000000000..d115f31e8e --- /dev/null +++ b/src/sql/base/browser/ui/checkbox/media/check_inverse.svg @@ -0,0 +1 @@ +check_inverse_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/checkbox/media/checkbox.css b/src/sql/base/browser/ui/checkbox/media/checkbox.css new file mode 100644 index 0000000000..cf594a8188 --- /dev/null +++ b/src/sql/base/browser/ui/checkbox/media/checkbox.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. + *--------------------------------------------------------------------------------------------*/ + + .custom-checkbox.sql-checkbox { + margin-right: 5px; + margin-top: 2px; + width: 12px; + height: 12px; +} + +.custom-checkbox.sql-checkbox.checked { + background: url('check.svg') center center no-repeat; +} + +.vs-dark.monaco-shell .custom-checkbox.sql-checkbox.checked { + background: url('check_inverse.svg') center center no-repeat; +} + +.custom-checkbox.sql-checkbox.disabled { + pointer-events: none; + opacity: 0.3; +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/dropdownList/dropdownList.ts b/src/sql/base/browser/ui/dropdownList/dropdownList.ts new file mode 100644 index 0000000000..44481d9183 --- /dev/null +++ b/src/sql/base/browser/ui/dropdownList/dropdownList.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import 'vs/css!sql/base/browser/ui/dropdownList/media/dropdownList'; +import * as DOM from 'vs/base/browser/dom'; +import { Dropdown, IDropdownOptions } from 'vs/base/browser/ui/dropdown/dropdown'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Color } from 'vs/base/common/color'; +import { IAction } from 'vs/base/common/actions'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { EventType as GestureEventType } from 'vs/base/browser/touch'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; + +export interface IDropdownStyles { + backgroundColor?: Color; + foregroundColor?: Color; + borderColor?: Color; +} + +export class DropdownList extends Dropdown { + + protected backgroundColor: Color; + protected foregroundColor: Color; + protected borderColor: Color; + + constructor( + container: HTMLElement, + private _options: IDropdownOptions, + private _contentContainer: HTMLElement, + private _list: List, + private _themeService: IThemeService, + private _action?: IAction, + ) { + super(container, _options); + if (_action) { + let button = new Button(_contentContainer); + button.label = _action.label; + this.toDispose.push(DOM.addDisposableListener(button.getElement(), DOM.EventType.CLICK, () => { + this._action.run(); + this.hide(); + })); + attachButtonStyler(button, this._themeService); + } + + DOM.append(this.element.getHTMLElement(), DOM.$('div.dropdown-icon')); + + this.toDispose.push(this.element.on([DOM.EventType.CLICK, DOM.EventType.MOUSE_DOWN, GestureEventType.Tap], (e: Event) => { + DOM.EventHelper.stop(e, true); // prevent default click behaviour to trigger + }).on([DOM.EventType.MOUSE_DOWN, GestureEventType.Tap], (e: Event) => { + // We want to show the context menu on dropdown so that as a user you can press and hold the + // mouse button, make a choice of action in the menu and release the mouse to trigger that + // action. + // Due to some weird bugs though, we delay showing the menu to unwind event stack + // (see https://github.com/Microsoft/vscode/issues/27648) + setTimeout(() => this.show(), 100); + }).on([DOM.EventType.KEY_DOWN], (e: KeyboardEvent) => { + let event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Enter)) { + e.stopPropagation(); + setTimeout(() => { + this.show(); + this._list.getHTMLElement().focus(); + }, 100); + } + })); + + this.toDispose.push(this._list.onSelectionChange(() => { + // focus on the dropdown label then hide the dropdown list + this.element.getHTMLElement().focus(); + this.hide(); + })); + + this.element.getHTMLElement().setAttribute('tabindex', '0'); + } + + /** + * Render the dropdown contents + */ + protected renderContents(container: HTMLElement): IDisposable { + let div = DOM.append(container, this._contentContainer); + div.style.width = DOM.getTotalWidth(this.element.getHTMLElement()) + 'px'; + return null; + } + + /** + * Render the selected label of the dropdown + */ + public renderLabel(): void { + if (this._options.labelRenderer) { + this._options.labelRenderer(this.label.getHTMLElement()); + } + } + + protected onEvent(e: Event, activeElement: HTMLElement): void { + // If there is an event outside dropdown label and dropdown list, hide the dropdown list + if (!DOM.isAncestor(e.target, this.element.getHTMLElement()) && !DOM.isAncestor(e.target, this._contentContainer)) { + // focus on the dropdown label then hide the dropdown list + this.element.getHTMLElement().focus(); + this.hide(); + // If there is an keyboard event inside the list and key code is escape, hide the dropdown list + } else if (DOM.isAncestor(e.target, this._list.getHTMLElement()) && e instanceof KeyboardEvent) { + let event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Escape)) { + // focus on the dropdown label then hide the dropdown list + this.element.getHTMLElement().focus(); + this.hide(); + e.stopPropagation(); + } + } + } + + public style(styles: IDropdownStyles): void { + this.backgroundColor = styles.backgroundColor; + this.foregroundColor = styles.foregroundColor; + this.borderColor = styles.borderColor; + this.applyStyles(); + } + + protected applyStyles(): void { + const background = this.backgroundColor ? this.backgroundColor.toString() : null; + const foreground = this.foregroundColor ? this.foregroundColor.toString() : null; + const border = this.borderColor ? this.borderColor.toString() : null; + this.applyStylesOnElement(this._contentContainer, background, foreground, border); + if (this.label) { + this.applyStylesOnElement(this.element.getHTMLElement(), background, foreground, border); + } + } + + private applyStylesOnElement(element: HTMLElement, background: string, foreground: string, border: string): void { + if (element) { + element.style.backgroundColor = background; + element.style.color = foreground; + + element.style.borderWidth = border ? '1px' : null; + element.style.borderStyle = border ? 'solid' : null; + element.style.borderColor = border; + } + } +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/dropdownList/media/dropdownList.css b/src/sql/base/browser/ui/dropdownList/media/dropdownList.css new file mode 100644 index 0000000000..e1b4c44eb8 --- /dev/null +++ b/src/sql/base/browser/ui/dropdownList/media/dropdownList.css @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.dropdown { + width: 100%; + /* TODO: Determine a more permanent fix; vs/dropdown is overwriting this selector in packaged builds */ + display: flex !important; + align-items: flex-start; + cursor: pointer; +} + +.dropdown > .dropdown-label { + width: 100%; +} + +.dropdown > .dropdown-label:not(:empty) { + padding: 0px; +} + +.dropdown > .dropdown-icon { + flex: 0 0 15px; + height: 10px; + width: 10px; + content: url("dropdownarrow.svg"); + align-self: center; + padding-right: 10px; +} + +.vs-dark .dropdown > .dropdown-icon, +.hc-black .dropdown > .dropdown-icon { + content: url("dropdownarrow_inverse.svg"); +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/dropdownList/media/dropdownarrow.svg b/src/sql/base/browser/ui/dropdownList/media/dropdownarrow.svg new file mode 100644 index 0000000000..17e7e8abe3 --- /dev/null +++ b/src/sql/base/browser/ui/dropdownList/media/dropdownarrow.svg @@ -0,0 +1 @@ +dropdownarrow \ No newline at end of file diff --git a/src/sql/base/browser/ui/dropdownList/media/dropdownarrow_inverse.svg b/src/sql/base/browser/ui/dropdownList/media/dropdownarrow_inverse.svg new file mode 100644 index 0000000000..be66e61fe7 --- /dev/null +++ b/src/sql/base/browser/ui/dropdownList/media/dropdownarrow_inverse.svg @@ -0,0 +1 @@ +dropdownarrow_inverse \ No newline at end of file diff --git a/src/sql/base/browser/ui/editableDropdown/actions.ts b/src/sql/base/browser/ui/editableDropdown/actions.ts new file mode 100644 index 0000000000..469ce50f94 --- /dev/null +++ b/src/sql/base/browser/ui/editableDropdown/actions.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Action } from 'vs/base/common/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as nls from 'vs/nls'; + +export class ToggleDropdownAction extends Action { + private static readonly ID = 'dropdownAction.toggle'; + private static readonly LABEL = nls.localize('dropdownAction.toggle', "Toggle dropdown"); + private static readonly ICON = 'dropdown-arrow'; + + constructor(private _fn: () => any) { + super(ToggleDropdownAction.ID, ToggleDropdownAction.LABEL, ToggleDropdownAction.ICON); + } + + public run(): TPromise { + this._fn(); + return TPromise.as(true); + } +} diff --git a/src/sql/base/browser/ui/editableDropdown/dropdown.ts b/src/sql/base/browser/ui/editableDropdown/dropdown.ts new file mode 100644 index 0000000000..42aae95343 --- /dev/null +++ b/src/sql/base/browser/ui/editableDropdown/dropdown.ts @@ -0,0 +1,307 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/dropdownList'; + +import { ToggleDropdownAction } from './actions'; + +import { IContextViewProvider, ContextView } from 'vs/base/browser/ui/contextview/contextview'; +import { mixin } from 'vs/base/common/objects'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { InputBox, IInputBoxStyles } from 'sql/base/browser/ui/inputBox/inputBox'; +import { IMessage, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; +import { List, IListStyles } from 'vs/base/browser/ui/list/listWidget'; +import * as DOM from 'vs/base/browser/dom'; +import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Color } from 'vs/base/common/color'; +import * as nls from 'vs/nls'; +import Event, { Emitter } from 'vs/base/common/event'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; + +export interface IDropdownOptions extends IDropdownStyles { + /** + * Whether or not a options in the list must be selected or a "new" option can be set + */ + strictSelection?: boolean; + /** + * Maximum height of the dropdown, defaults to 500 + */ + maxHeight?: number; + /** + * Initial values for the dropdown, can be set afterwards + */ + values?: string[]; + /** + * Placeholder to use in the input + */ + placeholder?: string; + /** + * Warning message to show when the input is not part of the supplied list, only used if strictSelection = false + */ + warningMessage?: string; + /** + * Error Message to show if input is not part of the supplied list, only used if strictSelection = false + */ + errorMessage?: string; +} + +export interface IDropdownStyles { + contextBackground?: Color; + contextBorder?: Color; +} + +const errorMessage = nls.localize('editableDropdown.errorValidate', "Must be an option from the list"); + +const defaults: IDropdownOptions = { + strictSelection: true, + maxHeight: 300, + errorMessage: errorMessage, + contextBorder: Color.fromHex('#696969') +}; + +interface ListResource { + label: string; +} + +interface TableTemplate { + label: HTMLElement; +} + +class Delegate implements IDelegate { + getHeight = (): number => 22; + + getTemplateId(element: ListResource): string { + return 'string'; + } +} + +class Renderer implements IRenderer { + static TEMPLATE_ID = 'string'; + get templateId(): string { return Renderer.TEMPLATE_ID; } + + renderTemplate(container: HTMLElement): TableTemplate { + const row = $('div.list-row').style('height', '22px').style('padding-left', '5px').getHTMLElement(); + DOM.append(container, row); + const label = $('span.label').style('margin', 'auto').getHTMLElement(); + DOM.append(row, label); + + return { label }; + } + + renderElement(resource: ListResource, index: number, template: TableTemplate): void { + template.label.innerText = resource.label; + } + + disposeTemplate(template: TableTemplate): void { + // noop + } +} + +export class Dropdown extends Disposable { + private $el: Builder; + private $input: Builder; + private $list: Builder; + private _input: InputBox; + private _list: List; + private _values: string[]; + private _options: IDropdownOptions; + private _toggleAction: ToggleDropdownAction; + // we have to create our own contextview since otherwise inputbox will override ours + private _contextView: ContextView; + + private _onBlur = this._register(new Emitter()); + public onBlur: Event = this._onBlur.event; + + private _onValueChange = this._register(new Emitter()); + public onValueChange: Event = this._onValueChange.event; + + private _onFocus = this._register(new Emitter()); + public onFocus: Event = this._onFocus.event; + + constructor( + container: HTMLElement, + contextViewService: IContextViewProvider, + private _themeService: IThemeService, + opt?: IDropdownOptions + ) { + super(); + this._contextView = new ContextView(document.body); + this._options = mixin(opt, defaults, false) as IDropdownOptions; + this._values = this._options.values; + this.$el = $('.dropdown').style('width', '100%').appendTo(container); + + this.$input = $('.dropdown-input').style('width', '100%').appendTo(this.$el); + this.$list = $('.dropdown-list'); + + this._toggleAction = new ToggleDropdownAction(() => this._showList()); + + this._input = new InputBox(this.$input.getHTMLElement(), contextViewService, { + validationOptions: { + showMessage: false, + validation: v => this._inputValidator(v) + }, + placeholder: this._options.placeholder, + actions: [this._toggleAction] + }); + + this._register(DOM.addDisposableListener(this._input.inputElement, DOM.EventType.FOCUS, () => { + this._showList(); + })); + + this._register(DOM.addDisposableListener(this._input.inputElement, DOM.EventType.BLUR, () => { + if (!this._list.isDOMFocused) { + this._onBlur.fire(); + } + })); + + this._register(DOM.addStandardDisposableListener(this._input.inputElement, DOM.EventType.KEY_UP, (e: StandardKeyboardEvent) => { + switch (e.keyCode) { + case KeyCode.Enter: + if (this._input.validate()) { + this._onValueChange.fire(this._input.value); + } + e.stopPropagation(); + break; + case KeyCode.Escape: + if (this.$list.getHTMLElement().parentElement) { + this._input.validate(); + this._onBlur.fire(); + this._contextView.hide(); + e.stopPropagation(); + } + break; + case KeyCode.Tab: + this._input.validate(); + this._onBlur.fire(); + this._contextView.hide(); + e.stopPropagation(); + break; + case KeyCode.DownArrow: + if (!this.$list.getHTMLElement().parentElement) { + this._showList(); + } + this._list.getHTMLElement().focus(); + e.stopPropagation(); + break; + } + })); + + this._list = new List(this.$list.getHTMLElement(), new Delegate(), [new Renderer()]); + if (this._values) { + this._list.splice(0, this._list.length, this._values.map(i => { return { label: i }; })); + let height = this._list.length * 22 > this._options.maxHeight ? this._options.maxHeight : this._list.length * 22; + this.$list.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) + 'px'); + } + + this._list.onSelectionChange(e => { + if (e.elements.length === 1) { + this.value = e.elements[0].label; + this._onValueChange.fire(e.elements[0].label); + this._contextView.hide(); + } + }); + + this._input.onDidChange(e => { + if (this._values) { + this._list.splice(0, this._list.length, this._values.filter(i => i.includes(e)).map(i => { return { label: i }; })); + let height = this._list.length * 22 > this._options.maxHeight ? this._options.maxHeight : this._list.length * 22; + this.$list.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) + 'px'); + this._list.layout(parseInt(this.$list.style('height'))); + } + }); + + this._register(this._contextView); + this._register(this.$el); + this._register(this.$input); + this._register(this.$list); + this._register(this._list); + this._register(this._input); + this._register(this._contextView); + } + + private _showList(): void { + if (this._input.isEnabled) { + this._onFocus.fire(); + this._contextView.show({ + getAnchor: () => this.$input.getHTMLElement(), + render: container => { + this.$list.appendTo(container); + this._list.layout(parseInt(this.$list.style('height'))); + return { dispose: () => { } }; + }, + onDOMEvent: (e, activeElement) => { + if (!DOM.isAncestor(activeElement, this.$el.getHTMLElement())) { + this._input.validate(); + this._onBlur.fire(); + this._contextView.hide(); + } + } + }); + } + } + + public set values(vals: string[]) { + this._values = vals; + this._list.splice(0, this._list.length, this._values.map(i => { return { label: i }; })); + let height = this._list.length * 22 > this._options.maxHeight ? this._options.maxHeight : this._list.length * 22; + this.$list.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) + 'px'); + this._list.layout(parseInt(this.$list.style('height'))); + this._input.validate(); + } + + public get value(): string { + return this._input.value; + } + + public set value(val: string) { + this._input.value = val; + } + + public focus() { + this._input.focus(); + } + + public blur() { + this._input.blur(); + this._contextView.hide(); + } + + style(style: IListStyles & IInputBoxStyles & IDropdownStyles) { + this._list.style(style); + this._input.style(style); + this.$list.style('background-color', style.contextBackground.toString()); + this.$list.style('outline', `1px solid ${style.contextBorder || this._options.contextBorder}`); + } + + private _inputValidator(value: string): IMessage { + if (this._values && !this._values.includes(value)) { + if (this._options.strictSelection) { + return { + content: this._options.errorMessage, + type: MessageType.ERROR + }; + } else if (this._options.warningMessage) { + return { + content: this._options.warningMessage, + type: MessageType.WARNING + }; + } + } + + return undefined; + } + + public set enabled(val: boolean) { + this._input.setEnabled(val); + this._toggleAction.enabled = val; + } + + public get enabled(): boolean { + return this._input.isEnabled(); + } +} diff --git a/src/sql/base/browser/ui/editableDropdown/media/dropdownList.css b/src/sql/base/browser/ui/editableDropdown/media/dropdownList.css new file mode 100644 index 0000000000..ab4fe294dc --- /dev/null +++ b/src/sql/base/browser/ui/editableDropdown/media/dropdownList.css @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.vs .dropdown-arrow.icon { + background-image: url("dropdownarrow.svg"); +} + +.vs-dark .dropdown-arrow.icon, +.hc-black .dropdown-arrow.icon { + background-image: url("dropdownarrow_inverse.svg"); +} + +.dropdown .monaco-action-bar .action-label.icon.dropdown-arrow { + padding: 0; + background-size: 10px; + background-position: 50%; +} + +.dropdown .monaco-action-bar .action-item { + margin: 0; +} diff --git a/src/sql/base/browser/ui/editableDropdown/media/dropdownarrow.svg b/src/sql/base/browser/ui/editableDropdown/media/dropdownarrow.svg new file mode 100644 index 0000000000..17e7e8abe3 --- /dev/null +++ b/src/sql/base/browser/ui/editableDropdown/media/dropdownarrow.svg @@ -0,0 +1 @@ +dropdownarrow \ No newline at end of file diff --git a/src/sql/base/browser/ui/editableDropdown/media/dropdownarrow_inverse.svg b/src/sql/base/browser/ui/editableDropdown/media/dropdownarrow_inverse.svg new file mode 100644 index 0000000000..be66e61fe7 --- /dev/null +++ b/src/sql/base/browser/ui/editableDropdown/media/dropdownarrow_inverse.svg @@ -0,0 +1 @@ +dropdownarrow_inverse \ No newline at end of file diff --git a/src/sql/base/browser/ui/inputBox/inputBox.ts b/src/sql/base/browser/ui/inputBox/inputBox.ts new file mode 100644 index 0000000000..74c1cbac1c --- /dev/null +++ b/src/sql/base/browser/ui/inputBox/inputBox.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { InputBox as vsInputBox, IInputOptions, IInputBoxStyles as vsIInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { Color } from 'vs/base/common/color'; +import Event, { Emitter } from 'vs/base/common/event'; + +export interface OnLoseFocusParams { + value: string; + hasChanged: boolean; +} + +export interface IInputBoxStyles extends vsIInputBoxStyles { + disabledInputBackground?: Color; + disabledInputForeground?: Color; +} + +export class InputBox extends vsInputBox { + private enabledInputBackground: Color; + private enabledInputForeground: Color; + private enabledInputBorder: Color; + private disabledInputBackground: Color; + private disabledInputForeground: Color; + private disabledInputBorder: Color; + + private _lastLoseFocusValue: string; + + private _onLoseFocus = this._register(new Emitter()); + public onLoseFocus: Event = this._onLoseFocus.event; + + + constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) { + super(container, contextViewProvider, options); + this.enabledInputBackground = this.inputBackground; + this.enabledInputForeground = this.inputForeground; + this.enabledInputBorder = this.inputBorder; + this.disabledInputBackground = Color.transparent; + this.disabledInputForeground = null; + this.disabledInputBorder = null; + + this._lastLoseFocusValue = this.value; + let self = this; + this.onblur(this.inputElement, () => { + self._onLoseFocus.fire({ value: self.value, hasChanged: self._lastLoseFocusValue !== self.value }); + self._lastLoseFocusValue = self.value; + }); + } + + public style(styles: IInputBoxStyles): void { + super.style(styles); + this.enabledInputBackground = this.inputBackground; + this.enabledInputForeground = this.inputForeground; + this.enabledInputBorder = this.inputBorder; + this.disabledInputBackground = styles.disabledInputBackground; + this.disabledInputForeground = styles.disabledInputForeground; + } + + public enable(): void { + super.enable(); + this.inputBackground = this.enabledInputBackground; + this.inputForeground = this.enabledInputForeground; + this.inputBorder = this.enabledInputBorder; + this.applyStyles(); + } + + public disable(): void { + super.disable(); + this.inputBackground = this.disabledInputBackground; + this.inputForeground = this.disabledInputForeground; + this.inputBorder = this.disabledInputBorder; + this.applyStyles(); + } + + public isEnabled(): boolean { + return !this.inputElement.hasAttribute('disabled'); + } +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/listBox/listBox.ts b/src/sql/base/browser/ui/listBox/listBox.ts new file mode 100644 index 0000000000..f9e0457d4b --- /dev/null +++ b/src/sql/base/browser/ui/listBox/listBox.ts @@ -0,0 +1,261 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; +import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import { Color } from 'vs/base/common/color'; +import { ISelectBoxStyles } from 'vs/base/browser/ui/selectBox/selectBox'; +import { IMessage, MessageType, defaultOpts } from 'vs/base/browser/ui/inputbox/inputBox'; +import * as dom from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IContextViewProvider, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { RenderOptions, renderFormattedText, renderText } from 'vs/base/browser/htmlContentRenderer'; + +const $ = dom.$; + +export interface IListBoxStyles { + selectBackground?: Color; + selectForeground?: Color; + selectBorder?: Color; + inputValidationInfoBorder?: Color; + inputValidationInfoBackground?: Color; + inputValidationWarningBorder?: Color; + inputValidationWarningBackground?: Color; + inputValidationErrorBorder?: Color; + inputValidationErrorBackground?: Color; +} + +/* +* Extends SelectBox to allow multiple selection and adding/remove items dynamically +*/ +export class ListBox extends SelectBox { + private _toDispose2: lifecycle.IDisposable[]; + private enabledSelectBackground: Color; + private enabledSelectForeground: Color; + private enabledSelectBorder: Color; + private disabledSelectBackground: Color; + private disabledSelectForeground: Color; + private disabledSelectBorder: Color; + private keyC = 33; + + private inputValidationInfoBorder: Color; + private inputValidationInfoBackground: Color; + private inputValidationWarningBorder: Color; + private inputValidationWarningBackground: Color; + private inputValidationErrorBorder: Color; + private inputValidationErrorBackground: Color; + + private message: IMessage; + private contextViewProvider: IContextViewProvider; + private isValid: boolean; + + constructor(options: string[], selectedOption: string, contextViewProvider: IContextViewProvider) { + super(options, 0); + this.contextViewProvider = contextViewProvider; + this.isValid = true; + this.selectElement.multiple = true; + this.selectElement.style['height'] = '80px'; + + // Set width style for horizontal scrollbar + this.selectElement.style['width'] = 'inherit'; + this.selectElement.style['min-width'] = '100%'; + + this._toDispose2 = []; + this._toDispose2.push(dom.addStandardDisposableListener(this.selectElement, 'keydown', (e) => { + this.onKeyDown(e) + })); + + this.enabledSelectBackground = this.selectBackground; + this.enabledSelectForeground = this.selectForeground; + this.enabledSelectBorder = this.selectBorder; + this.disabledSelectBackground = Color.transparent; + this.disabledSelectForeground = null; + this.disabledSelectBorder = null; + + this.inputValidationInfoBorder = defaultOpts.inputValidationInfoBorder; + this.inputValidationInfoBackground = defaultOpts.inputValidationInfoBackground; + this.inputValidationWarningBorder = defaultOpts.inputValidationWarningBorder; + this.inputValidationWarningBackground = defaultOpts.inputValidationWarningBackground; + this.inputValidationErrorBorder = defaultOpts.inputValidationErrorBorder; + this.inputValidationErrorBackground = defaultOpts.inputValidationErrorBackground; + + this.onblur(this.selectElement, () => this.onBlur()); + this.onfocus(this.selectElement, () => this.onFocus()); + } + + public style(styles: IListBoxStyles): void { + var superStyle: ISelectBoxStyles = { + selectBackground: styles.selectBackground, + selectForeground: styles.selectForeground, + selectBorder: styles.selectBorder + } + super.style(superStyle); + this.enabledSelectBackground = this.selectBackground; + this.enabledSelectForeground = this.selectForeground; + this.enabledSelectBorder = this.selectBorder; + + this.inputValidationInfoBackground = styles.inputValidationInfoBackground; + this.inputValidationInfoBorder = styles.inputValidationInfoBorder; + this.inputValidationWarningBackground = styles.inputValidationWarningBackground; + this.inputValidationWarningBorder = styles.inputValidationWarningBorder; + this.inputValidationErrorBackground = styles.inputValidationErrorBackground; + this.inputValidationErrorBorder = styles.inputValidationErrorBorder; + } + + public setValidation(isValid: boolean, message?: IMessage): void { + this.isValid = isValid; + this.message = message; + + if (this.isValid) { + this.selectElement.style.border = `1px solid ${this.selectBorder}`; + } else { + const styles = this.stylesForType(this.message.type); + this.selectElement.style.border = styles.border ? `1px solid ${styles.border}` : null; + } + } + + public get isContentValid(): boolean { + return this.isValid; + } + + public get selectedOptions(): string[] { + var selected = []; + for (var i = 0; i < this.selectElement.selectedOptions.length; i++ ) { + selected.push(this.selectElement.selectedOptions[i].innerHTML); + } + return selected; + } + + public get count(): number { + return this.selectElement.options.length; + } + + // Remove selected options + public remove(): void { + var indexes = []; + for (var i = 0; i < this.selectElement.selectedOptions.length; i++ ) { + indexes.push(this.selectElement.selectedOptions[i].index); + } + indexes.sort((a, b) => b-a); + + for (var i = 0; i < indexes.length; i++) { + this.selectElement.remove(indexes[i]); + this.options.splice(indexes[i], 1); + } + } + + public add(option: string): void { + this.selectElement.add(this.createOption(option)); + this.options.push(option); + } + + // Allow copy to clipboard + public onKeyDown(event: IKeyboardEvent): void { + if (this.selectedOptions.length > 0) + { + var key = event.keyCode; + var ctrlOrCmd = event.ctrlKey || event.metaKey; + + if (ctrlOrCmd && key === this.keyC) { + var textToCopy = this.selectedOptions[0]; + for (var i = 1; i < this.selectedOptions.length; i++) { + textToCopy = textToCopy + ', ' + this.selectedOptions[i]; + } + + // Copy to clipboard + WorkbenchUtils.executeCopy(textToCopy); + event.stopPropagation(); + } + } + } + + public dispose(): void { + this._toDispose2 = lifecycle.dispose(this._toDispose2); + super.dispose(); + } + + public enable(): void { + this.selectElement.disabled = false; + this.selectBackground = this.enabledSelectBackground; + this.selectForeground = this.enabledSelectForeground; + this.selectBorder = this.enabledSelectBorder; + this.applyStyles(); + } + + public disable(): void { + this.selectElement.disabled = true; + this.selectBackground = this.disabledSelectBackground; + this.selectForeground = this.disabledSelectForeground; + this.selectBorder = this.disabledSelectBorder; + this.applyStyles(); + } + + public onBlur(): void { + if (!this.isValid) { + this.contextViewProvider.hideContextView(); + } + } + + public onFocus(): void { + if (!this.isValid) { + this.showMessage(); + } + } + + public focus(): void { + this.selectElement.focus(); + } + + public showMessage(): void { + let div: HTMLElement; + let layout = () => div.style.width = dom.getTotalWidth(this.selectElement) + 'px'; + + this.contextViewProvider.showContextView({ + getAnchor: () => this.selectElement, + anchorAlignment: AnchorAlignment.RIGHT, + render: (container: HTMLElement) => { + div = dom.append(container, $('.monaco-inputbox-container')); + layout(); + + const renderOptions: RenderOptions = { + inline: true, + className: 'monaco-inputbox-message' + }; + + let spanElement: HTMLElement = (this.message.formatContent + ? renderFormattedText(this.message.content, renderOptions) + : renderText(this.message.content, renderOptions)) as any; + dom.addClass(spanElement, this.classForType(this.message.type)); + + const styles = this.stylesForType(this.message.type); + spanElement.style.backgroundColor = styles.background ? styles.background.toString() : null; + spanElement.style.border = styles.border ? `1px solid ${styles.border}` : null; + + dom.append(div, spanElement); + + return null; + }, + layout: layout + }); + } + + private classForType(type: MessageType): string { + switch (type) { + case MessageType.INFO: return 'info'; + case MessageType.WARNING: return 'warning'; + default: return 'error'; + } + } + + private stylesForType(type: MessageType): { border: Color; background: Color } { + switch (type) { + case MessageType.INFO: return { border: this.inputValidationInfoBorder, background: this.inputValidationInfoBackground }; + case MessageType.WARNING: return { border: this.inputValidationWarningBorder, background: this.inputValidationWarningBackground }; + default: return { border: this.inputValidationErrorBorder, background: this.inputValidationErrorBackground }; + } + } +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/modal/dialogHelper.ts b/src/sql/base/browser/ui/modal/dialogHelper.ts new file mode 100644 index 0000000000..6da832bf24 --- /dev/null +++ b/src/sql/base/browser/ui/modal/dialogHelper.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Builder } from 'vs/base/browser/builder'; +import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox'; +import * as data from 'data'; +import * as types from 'vs/base/common/types'; + +export function appendRow(container: Builder, label: string, labelClass: string, cellContainerClass: string): Builder { + let cellContainer: Builder; + container.element('tr', {}, (rowContainer) => { + rowContainer.element('td', { class: labelClass }, (labelCellContainer) => { + labelCellContainer.div({}, (labelContainer) => { + labelContainer.innerHtml(label); + }); + }); + rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => { + cellContainer = inputCellContainer; + }); + }); + + return cellContainer; +} + +export function appendRowLink(container: Builder, label: string, labelClass: string, cellContainerClass: string): Builder { + let rowButton: Button; + container.element('tr', {}, (rowContainer) => { + rowContainer.element('td', { class: labelClass }, (labelCellContainer) => { + labelCellContainer.div({}, (labelContainer) => { + labelContainer.innerHtml(label); + }); + }); + rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => { + inputCellContainer.element('div', {}, (rowContainer) => { + rowButton = new Button(rowContainer); + + }); + }); + }); + + return new Builder(rowButton.getElement()); +} + +export function createCheckBox(container: Builder, label: string, checkboxClass: string, isChecked: boolean, onCheck?: (viaKeyboard: boolean) => void): Checkbox { + let checkbox = new Checkbox({ + actionClassName: checkboxClass, + title: label, + isChecked: isChecked, + onChange: (viaKeyboard) => { + if (onCheck) { + onCheck(viaKeyboard); + } + } + }); + container.getHTMLElement().appendChild(checkbox.domNode); + container.div({}, (labelContainer) => { + labelContainer.innerHtml(label); + }); + + return checkbox; +} + +export function appendInputSelectBox(container: Builder, selectBox: SelectBox): SelectBox { + selectBox.render(container.getHTMLElement()); + return selectBox; +} + +export function isNullOrWhiteSpace(value: string): boolean { + // returns true if the string is null or contains white space/tab chars only + return !value || value.trim().length === 0; +} + +export function getBooleanValueFromStringOrBoolean(value: any): boolean { + if (types.isBoolean(value)) { + return value; + } else if (types.isString(value)) { + return value.toLowerCase() === 'true'; + } + return false; +} + +export function getCategoryDisplayName(categories: data.CategoryValue[], categoryName: string) { + var displayName: string; + categories.forEach(c => { + if (c.name === categoryName) { + displayName = c.displayName; + } + }); + return displayName; +} + +export function getCategoryName(categories: data.CategoryValue[], categoryDisplayName: string) { + var categoryName: string; + categories.forEach(c => { + if (c.displayName === categoryDisplayName) { + categoryName = c.name; + } + }); + return categoryName; +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/modal/media/back.svg b/src/sql/base/browser/ui/modal/media/back.svg new file mode 100644 index 0000000000..e7b06e9a10 --- /dev/null +++ b/src/sql/base/browser/ui/modal/media/back.svg @@ -0,0 +1 @@ +back_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/modal/media/back_inverse.svg b/src/sql/base/browser/ui/modal/media/back_inverse.svg new file mode 100644 index 0000000000..247a241958 --- /dev/null +++ b/src/sql/base/browser/ui/modal/media/back_inverse.svg @@ -0,0 +1 @@ +back_inverse_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/modal/media/modal.css b/src/sql/base/browser/ui/modal/media/modal.css new file mode 100644 index 0000000000..b34f205f0e --- /dev/null +++ b/src/sql/base/browser/ui/modal/media/modal.css @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-shell .modal { + background-color: rgba(204, 204, 204, 0.6); + right: 0; + left: 0; + top: 0; + bottom: 0; + position: absolute; + z-index: 500; +} + +.monaco-shell .modal:not(.flyout-dialog) .modal-dialog { + margin: auto; + width: 640px; + height: 480px; +} + +.modal .modal-header { + padding: 15px; +} + +.modal .modal-footer { + padding: 15px; +} + +.modal .icon.in-progress { + width: 25px; + height: 25px; +} + +.monaco-shell .modal.flyout-dialog .modal-dialog { + margin: auto auto auto auto; + height: 100%; + width: 500px; + right: 0; + position: absolute; + overflow-y: hidden; +} + +.monaco-shell .modal.flyout-dialog.wide .modal-dialog { + width: 1200px; + max-width: 95%; + min-width: 400px; +} + +.monaco-shell .modal.flyout-dialog .modal-content { + height: 100%; + font-size: 11px; +} + +.modal .modal-title { + font-size: 15px; +} + +.modal .modal-title-icon { + width: 20px; + height: 20px; + float: left; + margin-right: 10px; +} + +.monaco-shell .modal.flyout-dialog .modal-body { + height: calc(100% - 105px); +} + +/* modal body for angular component dialog */ +.monaco-shell .modal.flyout-dialog .angular-modal-body { + height: calc(100% - 90px); +} + +/* Style for body and footer in modal dialog. This should be applied to dialog created with angular component. */ +.monaco-shell .modal.flyout-dialog .modal-body-and-footer { + height: 100%; +} + +/* modl body content style(excluding dialogErrorMessage section) for angulr component dialog */ +.angular-modal-body-content { + overflow-x: hidden; + overflow-y: auto; + padding: 15px; +} + +.modal.flyout-dialog .angular-form { + height: 100%; +} + +.modal.flyout-dialog .dialog-label { + width: 100%; + padding-bottom: 5px; +} + +/* Add space in the bottom to separate each input elements */ +.modal.flyout-dialog .input-divider { + width: 100%; + padding-bottom: 20px; +} + +.modal.flyout-dialog .input { + width: 100%; + border: none; + height: 25px; + padding-left: 4px; +} + +.modal.flyout-dialog .modal-body { + overflow-y: auto; +} + +.vs-dark.monaco-shell .modal.flyout-dialog .input { + background-color: #3C3C3C; +} + +.vs-dark.monaco-shell .modal.flyout-dialog input:disabled { + background-color: #E1E1E1; + color: #3C3C3C; +} + +.modal .select-box { + width: 100%; + height: 25px; + color: #6C6C6C; + font-size: 11px; + border: 1px solid transparent; +} + +.modal.flyout-dialog .dialogErrorMessage { + overflow: hidden; + padding-left: 10px; + overflow-y: auto; + display: flex; +} + +.modal .modal-footer { + display: flex; +} + +.modal .modal-footer .left-footer { + display: flex; + flex: 1 1 auto; + justify-content: flex-start; +} + +.modal .modal-footer .right-footer { + display: flex; + flex: 1 1 auto; + justify-content: flex-end; +} + +.modal .footer-button a.monaco-button.monaco-text-button { + width: 100px; +} + +.vs-dark.monaco-shell .modal.flyout-dialog .footer-button a.monaco-button.monaco-text-button { + outline-color: #8e8c8c; +} + +.modal.flyout-dialog .footer-button a.monaco-button.monaco-text-button:focus { + outline-width: 1px; +} + +.modal .footer-button { + margin-right: 5px; +} + +.modal .right-footer .footer-button:last-of-type { + margin-right: none; +} + +.modal.flyout-dialog .icon.error { + float: left; + margin-right: 10px; + width: 20px; + height: 20px; +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/modal/media/optionsDialog.css b/src/sql/base/browser/ui/modal/media/optionsDialog.css new file mode 100644 index 0000000000..1c13032185 --- /dev/null +++ b/src/sql/base/browser/ui/modal/media/optionsDialog.css @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.optionsDialog-label { + width: 120px; + padding-bottom: 5px; +} + +.optionsDialog-input { + width: 200px; + padding-bottom: 5px; + padding-right: 7px; +} + +.options-dialog .select-box { + width: 321px; + margin-left: 1px; +} + +.optionsDialog-options-groups { + padding: 15px; + height: calc(100% - 150px); + overflow-y: auto; +} + +.backButtonIcon { + content: url('back.svg'); + width: 20px; + margin-right: 10px; + float: left; + cursor: pointer; +} + +.vs-dark.monaco-shell .backButtonIcon { + content: url('back_inverse.svg'); +} + +.optionsDialog-description { + padding: 15px; + overflow-y: auto; +} + +.optionsDialog-options { + height: calc(100% - 30px); +} + +.optionsDialog-description-content { + padding: 10px; +} + +.optionsDialog-table{ + width:100%; + padding: 10px; +} + +td.optionsDialog-input.select-container { + padding-right: 9px; +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/modal/modal.ts b/src/sql/base/browser/ui/modal/modal.ts new file mode 100644 index 0000000000..455e7ca72b --- /dev/null +++ b/src/sql/base/browser/ui/modal/modal.ts @@ -0,0 +1,432 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/media/icons/common-icons'; +import 'vs/css!./media/modal'; +import { IThemable } from 'vs/platform/theme/common/styler'; +import { Color } from 'vs/base/common/color'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { mixin } from 'vs/base/common/objects'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Builder, $, withElementById } from 'vs/base/browser/builder'; +import { Button } from 'vs/base/browser/ui/button/button'; +import * as DOM from 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; + +import * as TelemetryUtils from 'sql/common/telemetryUtilities'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; + +export const MODAL_SHOWING_KEY = 'modalShowing'; +export const MODAL_SHOWING_CONTEXT = new RawContextKey>(MODAL_SHOWING_KEY, []); + +export interface IModalDialogStyles { + dialogForeground?: Color; + dialogBorder?: Color; + dialogHeaderAndFooterBackground?: Color; + dialogBodyBackground?: Color; +} + +export interface IModalOptions { + isFlyout?: boolean; + isWide?: boolean; + isAngular?: boolean; + hasBackButton?: boolean; + hasTitleIcon?: boolean; + hasErrors?: boolean; + hasSpinner?: boolean; +} + +// Needed for angular component dialogs to style modal footer +export class ModalFooterStyle { + public static backgroundColor; + public static borderTopWidth; + public static borderTopStyle; + public static borderTopColor; +} + +const defaultOptions: IModalOptions = { + isFlyout: true, + isWide: false, + isAngular: false, + hasBackButton: false, + hasTitleIcon: false, + hasErrors: false, + hasSpinner: false +}; + +export abstract class Modal extends Disposable implements IThemable { + + private _errorMessage: Builder; + private _spinnerElement: HTMLElement; + private _errorIconElement: HTMLElement; + private _dialogForeground: Color; + private _dialogBorder: Color; + private _dialogHeaderAndFooterBackground: Color; + private _dialogBodyBackground: Color; + + private _modalDialog: Builder; + private _modalHeaderSection: Builder; + private _modalBodySection: HTMLElement; + private _modalFooterSection: Builder; + private _closeButtonInHeader: Builder; + private _builder: Builder; + private _footerBuilder: Builder; + private _modalTitle: Builder; + private _modalTitleIcon: HTMLElement; + private _leftFooter: Builder; + private _rightFooter: Builder; + private _footerButtons: Button[]; + + private _keydownListener: IDisposable; + private _resizeListener: IDisposable; + + private _modalOptions: IModalOptions; + private _backButton: Button; + + private _modalShowingContext: IContextKey>; + private readonly _staticKey: string; + + /** + * Get the back button, only available after render and if the hasBackButton option is true + */ + protected get backButton(): Button { + return this._backButton; + } + + /** + * Set the dialog to have wide layout dynamically. + * Temporary solution to render file browser as wide or narrow layout. + * This will be removed once backup dialog is changed to wide layout. + */ + public setWide(isWide: boolean): void { + if (this._builder.hasClass('wide') && isWide === false) { + this._builder.removeClass('wide'); + } else if (!this._builder.hasClass('wide') && isWide === true) { + this._builder.addClass('wide'); + } + } + + /** + * Constructor for modal + * @param _title Title of the modal, if undefined, the title section is not rendered + * @param _name Name of the modal, used for telemetry + * @param _partService + * @param options Modal options + */ + constructor( + private _title: string, + private _name: string, + private _partService: IPartService, + private _telemetryService: ITelemetryService, + private _contextKeyService: IContextKeyService, + options?: IModalOptions + ) { + super(); + this._modalOptions = options || Object.create(null); + mixin(this._modalOptions, defaultOptions, false); + this._staticKey = generateUuid(); + this._modalShowingContext = MODAL_SHOWING_CONTEXT.bindTo(_contextKeyService); + this._footerButtons = []; + } + + /** + * Build and render the modal, will call {@link Modal#renderBody} + */ + public render() { + let modalBodyClass = (this._modalOptions.isAngular === false ? 'modal-body' : 'modal-body-and-footer'); + let parts: Array = []; + // This modal header section refers to the header of of the dialog + // will not be rendered if the title is passed in as undefined + if (this._title !== undefined) { + this._modalHeaderSection = $().div({ class: 'modal-header' }, (modalHeader) => { + if (this._modalOptions.hasBackButton) { + modalHeader.div({ class: 'modal-go-back' }, (cellContainer) => { + this._backButton = new Button(cellContainer); + this._backButton.icon = 'backButtonIcon'; + }); + } + if (this._modalOptions.hasTitleIcon) { + modalHeader.div({ class: 'modal-title-icon' }, (modalIcon) => { + this._modalTitleIcon = modalIcon.getHTMLElement(); + }); + } + modalHeader.div({ class: 'modal-title' }, (modalTitle) => { + this._modalTitle = modalTitle; + modalTitle.innerHtml(this._title); + }); + }); + parts.push(this._modalHeaderSection.getHTMLElement()); + } + + // This modal body section refers to the body of of the dialog + let body: Builder; + $().div({ class: modalBodyClass }, (builder) => { + body = builder; + }); + this._modalBodySection = body.getHTMLElement(); + parts.push(body.getHTMLElement()); + + this.renderBody(body.getHTMLElement()); + + if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) { + body.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => { + errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => { + this._errorIconElement = iconContainer.getHTMLElement(); + this._errorIconElement.style.visibility = 'hidden'; + }); + errorMessageContainer.div({ class: 'errorMessage' }, (messageContainer) => { + this._errorMessage = messageContainer; + }); + }); + } + // This modal footer section refers to the footer of of the dialog + if (this._modalOptions.isAngular === false) { + this._modalFooterSection = $().div({ class: 'modal-footer' }, (modelFooter) => { + if (this._modalOptions.hasSpinner) { + modelFooter.div({ 'class': 'icon in-progress' }, (spinnerContainer) => { + this._spinnerElement = spinnerContainer.getHTMLElement(); + this._spinnerElement.style.visibility = 'hidden'; + }); + } + modelFooter.div({ 'class': 'left-footer' }, (leftFooter) => { + this._leftFooter = leftFooter; + }); + modelFooter.div({ 'class': 'right-footer' }, (rightFooter) => { + this._rightFooter = rightFooter; + }); + this._footerBuilder = modelFooter; + }); + parts.push(this._modalFooterSection.getHTMLElement()); + } + + let builderClass = 'modal fade'; + if (this._modalOptions.isFlyout) { + builderClass += ' flyout-dialog'; + } + if (this._modalOptions.isWide) { + builderClass += ' wide'; + } + + // The builder builds the dialog. It append header, body and footer sections. + this._builder = $().div({ class: builderClass, 'role': 'dialog' }, (dialogContainer) => { + this._modalDialog = dialogContainer.div({ class: 'modal-dialog ', role: 'document' }, (modalDialog) => { + modalDialog.div({ class: 'modal-content' }, (modelContent) => { + parts.forEach((part) => { + modelContent.append(part); + }); + }); + }); + }); + } + + /** + * Called for extended classes to render the body + * @param container The parent container to attach the rendered body to + */ + protected abstract renderBody(container: HTMLElement): void; + + /** + * Overridable to change behavior of escape key + */ + protected onClose(e: StandardKeyboardEvent) { + this.hide(); + } + + /** + * Overridable to change behavior of enter key + */ + protected onAccept(e: StandardKeyboardEvent) { + this.hide(); + } + + /** + * Shows the modal and attaches key listeners + */ + protected show() { + this._modalShowingContext.get().push(this._staticKey); + this._builder.appendTo(withElementById(this._partService.getWorkbenchElementId()).getHTMLElement().parentElement); + this._keydownListener = DOM.addDisposableListener(document, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + let context = this._modalShowingContext.get(); + if (context[context.length - 1] === this._staticKey) { + let event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Enter)) { + this.onAccept(event); + } else if (event.equals(KeyCode.Escape)) { + this.onClose(event); + } + } + }); + this._resizeListener = DOM.addDisposableListener(window, DOM.EventType.RESIZE, (e: Event) => { + this.layout(DOM.getTotalHeight(this._builder.getHTMLElement())); + }); + + this.layout(DOM.getTotalHeight(this._builder.getHTMLElement())); + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.ModalDialogOpened, { name: this._name }); + } + + /** + * Required to be implemented so that scrolling and other functions operate correctly. Should re-layout controls in the modal + */ + protected abstract layout(height?: number): void; + + /** + * Hides the modal and removes key listeners + */ + protected hide() { + this._footerButtons.forEach(button => button.applyStyles()); + this._modalShowingContext.get().pop(); + this._builder.offDOM(); + this._keydownListener.dispose(); + this._resizeListener.dispose(); + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.ModalDialogClosed, { name: this._name }); + } + + /** + * Adds a button to the footer of the modal + * @param label Label to show on the button + * @param onSelect The callback to call when the button is selected + */ + protected addFooterButton(label: string, onSelect: () => void, orientation: 'left' | 'right' = 'right'): Button { + let footerButton = $('div.footer-button'); + let button = new Button(footerButton); + button.label = label; + button.addListener('click', () => onSelect()); + if (orientation === 'left') { + footerButton.appendTo(this._leftFooter); + } else { + footerButton.appendTo(this._rightFooter); + } + this._footerButtons.push(button); + return button; + } + + /** + * Show an error in the error message element + * @param err Text to show in the error message + */ + protected setError(err: string) { + if (this._modalOptions.hasErrors) { + if (err === '') { + this._errorIconElement.style.visibility = 'hidden'; + } else { + this._errorIconElement.style.visibility = 'visible'; + } + this._errorMessage.innerHtml(err); + } + } + + /** + * Show the spinner element that shows something is happening, hidden by default + */ + protected showSpinner(): void { + if (this._modalOptions.hasSpinner) { + this._spinnerElement.style.visibility = 'visible'; + } + } + + /** + * Hide the spinner element to show that something was happening, hidden by default + */ + protected hideSpinner(): void { + if (this._modalOptions.hasSpinner) { + this._spinnerElement.style.visibility = 'hidden'; + } + } + + /** + * Set spinner element to show or hide + */ + public set spinner(show: boolean) { + if (show) { + this.showSpinner(); + } else { + this.hideSpinner(); + } + } + + /** + * Return background color of header and footer + */ + protected get headerAndFooterBackground(): string { + return this._dialogHeaderAndFooterBackground ? this._dialogHeaderAndFooterBackground.toString() : null; + } + + /** + * Set the title of the modal + * @param title + */ + protected set title(title: string) { + if (this._title !== undefined) { + this._modalTitle.innerHtml(title); + } + } + + /** + * Set the icon title class name + * @param iconClassName + */ + protected set titleIconClassName(iconClassName: string) { + if (this._modalTitleIcon) { + this._modalTitleIcon.className = 'modal-title-icon ' + iconClassName; + } + } + + /** + * Called by the theme registry on theme change to style the component + */ + public style(styles: IModalDialogStyles): void { + this._dialogForeground = styles.dialogForeground; + this._dialogBorder = styles.dialogBorder; + this._dialogHeaderAndFooterBackground = styles.dialogHeaderAndFooterBackground; + this._dialogBodyBackground = styles.dialogBodyBackground; + this.applyStyles(); + } + + private applyStyles(): void { + const foreground = this._dialogForeground ? this._dialogForeground.toString() : null; + const border = this._dialogBorder ? this._dialogBorder.toString() : null; + const headerAndFooterBackground = this._dialogHeaderAndFooterBackground ? this._dialogHeaderAndFooterBackground.toString() : null; + const bodyBackground = this._dialogBodyBackground ? this._dialogBodyBackground.toString() : null; + ModalFooterStyle.backgroundColor = headerAndFooterBackground; + ModalFooterStyle.borderTopWidth = border ? '1px' : null; + ModalFooterStyle.borderTopStyle = border ? 'solid' : null; + ModalFooterStyle.borderTopColor = border; + + if (this._closeButtonInHeader) { + this._closeButtonInHeader.style('color', foreground); + } + if (this._modalDialog) { + this._modalDialog.style('color', foreground); + this._modalDialog.style('border-width', border ? '1px' : null); + this._modalDialog.style('border-style', border ? 'solid' : null); + this._modalDialog.style('border-color', border); + } + if (this._modalHeaderSection) { + this._modalHeaderSection.style('background-color', headerAndFooterBackground); + this._modalHeaderSection.style('border-bottom-width', border ? '1px' : null); + this._modalHeaderSection.style('border-bottom-style', border ? 'solid' : null); + this._modalHeaderSection.style('border-bottom-color', border); + } + + if (this._modalBodySection) { + this._modalBodySection.style.backgroundColor = bodyBackground; + } + + if (this._modalFooterSection) { + this._modalFooterSection.style('background-color', ModalFooterStyle.backgroundColor); + this._modalFooterSection.style('border-top-width', ModalFooterStyle.borderTopWidth); + this._modalFooterSection.style('border-top-style', ModalFooterStyle.borderTopStyle); + this._modalFooterSection.style('border-top-color', ModalFooterStyle.borderTopColor); + } + } + + public dispose() { + super.dispose(); + this._keydownListener.dispose(); + } +} diff --git a/src/sql/base/browser/ui/modal/optionsDialog.ts b/src/sql/base/browser/ui/modal/optionsDialog.ts new file mode 100644 index 0000000000..a03bb7f0a3 --- /dev/null +++ b/src/sql/base/browser/ui/modal/optionsDialog.ts @@ -0,0 +1,268 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/optionsDialog'; + +import { FixedCollapsibleView } from 'sql/platform/views/fixedCollapsibleView'; +import * as DialogHelper from './dialogHelper'; +import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; +import { IModalOptions, Modal } from './modal'; +import * as OptionsDialogHelper from './optionsDialogHelper'; +import { attachModalDialogStyler } from 'sql/common/theme/styler'; + +import * as data from 'data'; + +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import Event, { Emitter } from 'vs/base/common/event'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { localize } from 'vs/nls'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import * as styler from 'vs/platform/theme/common/styler'; +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { SplitView, CollapsibleState } from 'vs/base/browser/ui/splitview/splitview'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { Widget } from 'vs/base/browser/ui/widget'; + +class CategoryView extends FixedCollapsibleView { + private _treecontainer: HTMLElement; + constructor(private viewTitle: string, private _bodyContainer: HTMLElement, collapsed: boolean, initialBodySize: number, headerSize: number) { + super( + initialBodySize, + { + expandedBodySize: initialBodySize, + sizing: headerSize, + initialState: collapsed ? CollapsibleState.COLLAPSED : CollapsibleState.EXPANDED, + ariaHeaderLabel: viewTitle + }); + } + + public renderHeader(container: HTMLElement): void { + const titleDiv = $('div.title').appendTo(container); + $('span').text(this.viewTitle).appendTo(titleDiv); + } + + public renderBody(container: HTMLElement): void { + this._treecontainer = document.createElement('div'); + container.appendChild(this._treecontainer); + this._treecontainer.appendChild(this._bodyContainer); + } + + public layoutBody(size: number): void { + this._treecontainer.style.height = size + 'px'; + } +} + +export class OptionsDialog extends Modal { + private _body: HTMLElement; + private _optionGroups: HTMLElement; + private _dividerBuilder: Builder; + private _okButton: Button; + private _closeButton: Button; + private _optionTitle: Builder; + private _optionDescription: Builder; + private _optionElements: { [optionName: string]: OptionsDialogHelper.IOptionElement } = {}; + private _optionValues: { [optionName: string]: string }; + private _optionRowSize = 31; + private _optionCategoryPadding = 30; + private _categoryHeaderSize = 22; + + private _onOk = new Emitter(); + public onOk: Event = this._onOk.event; + + private _onCloseEvent = new Emitter(); + public onCloseEvent: Event = this._onCloseEvent.event; + + public okLabel: string = localize('ok', 'OK'); + public cancelLabel: string = localize('cancel', 'Cancel'); + + constructor( + title: string, + name: string, + options: IModalOptions, + @IPartService partService: IPartService, + @IWorkbenchThemeService private _themeService: IWorkbenchThemeService, + @IContextViewService private _contextViewService: IContextViewService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(title, name, partService, telemetryService, contextKeyService, options); + } + + public render() { + super.render(); + attachModalDialogStyler(this, this._themeService); + if (this.backButton) { + this.backButton.addListener('click', () => this.cancel()); + styler.attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND }); + } + this._okButton = this.addFooterButton(this.okLabel, () => this.ok()); + this._closeButton = this.addFooterButton(this.cancelLabel, () => this.cancel()); + // Theme styler + styler.attachButtonStyler(this._okButton, this._themeService); + styler.attachButtonStyler(this._closeButton, this._themeService); + let self = this; + this._register(self._themeService.onDidColorThemeChange(e => self.updateTheme(e))); + self.updateTheme(self._themeService.getColorTheme()); + } + + protected renderBody(container: HTMLElement) { + new Builder(container).div({ class: 'optionsDialog-options' }, (bodyBuilder) => { + this._body = bodyBuilder.getHTMLElement(); + }); + + let builder = new Builder(this._body); + builder.div({ class: 'Connection-divider' }, (dividerContainer) => { + this._dividerBuilder = dividerContainer; + }); + + builder.div({ class: 'optionsDialog-description' }, (descriptionContainer) => { + descriptionContainer.div({ class: 'modal-title' }, (optionTitle) => { + this._optionTitle = optionTitle; + }); + descriptionContainer.div({ class: 'optionsDialog-description-content' }, (optionDescription) => { + this._optionDescription = optionDescription; + }); + }); + } + + // Update theming that is specific to options dialog flyout body + private updateTheme(theme: IColorTheme): void { + let borderColor = theme.getColor(contrastBorder); + let border = borderColor ? borderColor.toString() : null; + if (this._dividerBuilder) { + this._dividerBuilder.style('border-top-width', border ? '1px' : null); + this._dividerBuilder.style('border-top-style', border ? 'solid' : null); + this._dividerBuilder.style('border-top-color', border); + } + } + + private onOptionLinkClicked(optionName: string): void { + var option = this._optionElements[optionName].option; + this._optionTitle.innerHtml(option.displayName); + this._optionDescription.innerHtml(option.description); + } + + private fillInOptions(container: Builder, options: data.ServiceOption[]): void { + for (var i = 0; i < options.length; i++) { + var option: data.ServiceOption = options[i]; + var rowContainer = DialogHelper.appendRow(container, option.displayName, 'optionsDialog-label', 'optionsDialog-input'); + OptionsDialogHelper.createOptionElement(option, rowContainer, this._optionValues, this._optionElements, this._contextViewService, (name) => this.onOptionLinkClicked(name)); + } + } + + private registerStyling(): void { + // Theme styler + for (var optionName in this._optionElements) { + var widget: Widget = this._optionElements[optionName].optionWidget; + var option = this._optionElements[optionName].option; + switch (option.valueType) { + case OptionsDialogHelper.ServiceOptionType.category: + case OptionsDialogHelper.ServiceOptionType.boolean: + this._register(styler.attachSelectBoxStyler(widget, this._themeService)); + break; + case OptionsDialogHelper.ServiceOptionType.string: + case OptionsDialogHelper.ServiceOptionType.password: + case OptionsDialogHelper.ServiceOptionType.number: + this._register(styler.attachInputBoxStyler(widget, this._themeService)); + } + } + } + + public get optionValues(): { [name: string]: any } { + return this._optionValues; + } + + public hideError() { + this.setError(''); + } + + public showError(err: string) { + this.setError(err); + } + + /* Overwrite escape key behavior */ + protected onClose() { + this.close(); + } + + /* Overwrite enter key behavior */ + protected onAccept() { + this.ok(); + } + + public ok(): void { + if (OptionsDialogHelper.validateInputs(this._optionElements)) { + OptionsDialogHelper.updateOptions(this._optionValues, this._optionElements); + this._onOk.fire(); + this.close(); + } + } + + public cancel() { + this.close(); + } + + public close() { + this._optionGroups.remove(); + this.dispose(); + this.hide(); + this._onCloseEvent.fire(); + } + + public open(options: data.ServiceOption[], optionValues: { [name: string]: any }) { + this._optionValues = optionValues; + var firstOption: string; + var containerGroup: Builder; + var layoutSize = 0; + var optionsContentBuilder: Builder = $().div({ class: 'optionsDialog-options-groups' }, (container) => { + containerGroup = container; + this._optionGroups = container.getHTMLElement(); + }); + var splitview = new SplitView(containerGroup.getHTMLElement()); + let categoryMap = OptionsDialogHelper.groupOptionsByCategory(options); + for (var category in categoryMap) { + var serviceOptions: data.ServiceOption[] = categoryMap[category]; + var bodyContainer = $().element('table', { class: 'optionsDialog-table' }, (tableContainer: Builder) => { + this.fillInOptions(tableContainer, serviceOptions); + }); + + var viewSize = this._optionCategoryPadding + serviceOptions.length * this._optionRowSize; + layoutSize += (viewSize + this._categoryHeaderSize); + var categoryView = new CategoryView(category, bodyContainer.getHTMLElement(), false, viewSize, this._categoryHeaderSize); + splitview.addView(categoryView); + + if (!firstOption) { + firstOption = serviceOptions[0].name; + } + } + splitview.layout(layoutSize); + let body = new Builder(this._body); + body.append(optionsContentBuilder.getHTMLElement(), 0); + this.show(); + var firstOptionWidget = this._optionElements[firstOption].optionWidget; + this.registerStyling(); + firstOptionWidget.focus(); + } + + protected layout(height?: number): void { + // Nothing currently laid out in this class + } + + public dispose(): void { + super.dispose(); + for (var optionName in this._optionElements) { + var widget: Widget = this._optionElements[optionName].optionWidget; + widget.dispose(); + delete this._optionElements[optionName]; + } + } +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/modal/optionsDialogHelper.ts b/src/sql/base/browser/ui/modal/optionsDialogHelper.ts new file mode 100644 index 0000000000..8b057709dd --- /dev/null +++ b/src/sql/base/browser/ui/modal/optionsDialogHelper.ts @@ -0,0 +1,189 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as DialogHelper from './dialogHelper'; +import { Builder } from 'vs/base/browser/builder'; +import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; +import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox'; +import * as types from 'vs/base/common/types'; +import data = require('data'); +import { localize } from 'vs/nls'; + +export interface IOptionElement { + optionWidget: any; + option: data.ServiceOption; + optionValue: any; +} + +export enum ServiceOptionType { + string = 0, + multistring = 1, + password = 2, + number = 3, + category = 4, + boolean = 5 +} + +export function createOptionElement(option: data.ServiceOption, rowContainer: Builder, options: { [name: string]: any }, + optionsMap: { [optionName: string]: IOptionElement }, contextViewService: IContextViewService, onFocus: (name) => void): void { + let possibleInputs: string[] = []; + let optionValue = this.getOptionValueAndCategoryValues(option, options, possibleInputs); + let optionWidget: any; + let inputElement: HTMLElement; + let missingErrorMessage = localize('missingRequireField', ' is required.'); + let invalidInputMessage = localize('invalidInput', 'Invalid input. Numeric value expected.'); + switch (option.valueType) { + case ServiceOptionType.number: + optionWidget = new InputBox(rowContainer.getHTMLElement(), contextViewService, { + validationOptions: { + validation: (value: string) => { + if (!value && option.isRequired) { + return { type: MessageType.ERROR, content: option.displayName + missingErrorMessage }; + } else if (!types.isNumber(Number(value))) { + return { type: MessageType.ERROR, content: invalidInputMessage }; + } else { + return null; + } + } + } + }); + optionWidget.value = optionValue; + inputElement = this.findElement(rowContainer, 'input'); + break; + case ServiceOptionType.category: + case ServiceOptionType.boolean: + optionWidget = new SelectBox(possibleInputs, optionValue.toString()); + DialogHelper.appendInputSelectBox(rowContainer, optionWidget); + inputElement = this.findElement(rowContainer, 'select-box'); + break; + case ServiceOptionType.string: + case ServiceOptionType.password: + optionWidget = new InputBox(rowContainer.getHTMLElement(), contextViewService, { + validationOptions: { + validation: (value: string) => (!value && option.isRequired) ? ({ type: MessageType.ERROR, content: option.displayName + missingErrorMessage }) : null + } + }); + optionWidget.value = optionValue; + if (option.valueType === ServiceOptionType.password) { + optionWidget.inputElement.type = 'password'; + } + inputElement = this.findElement(rowContainer, 'input'); + } + optionsMap[option.name] = { optionWidget: optionWidget, option: option, optionValue: optionValue }; + inputElement.onfocus = () => onFocus(option.name); +} + +export function getOptionValueAndCategoryValues(option: data.ServiceOption, options: { [optionName: string]: any }, possibleInputs: string[]): any { + var optionValue = option.defaultValue; + if (options[option.name]) { + // if the value type is boolean, the option value can be either boolean or string + if (option.valueType === ServiceOptionType.boolean) { + if (options[option.name] === true || options[option.name] === this.trueInputValue) { + optionValue = this.trueInputValue; + } else { + optionValue = this.falseInputValue; + } + } else { + optionValue = options[option.name]; + } + } + + if (option.valueType === ServiceOptionType.boolean || option.valueType === ServiceOptionType.category) { + // If the option is not required, the empty string should be add at the top of possible choices + if (!option.isRequired) { + possibleInputs.push(''); + } + + if (option.valueType === ServiceOptionType.boolean) { + possibleInputs.push(this.trueInputValue, this.falseInputValue); + } else { + option.categoryValues.map(c => possibleInputs.push(c.name)); + } + + // If the option value is not set and default value is null, the option value should be set to the first possible input. + if (optionValue === null || optionValue === undefined) { + optionValue = possibleInputs[0]; + } + } + return optionValue; +} + +export function validateInputs(optionsMap: { [optionName: string]: IOptionElement }): boolean { + let isValid = true; + let isFocused = false; + for (var optionName in optionsMap) { + var optionElement: IOptionElement = optionsMap[optionName]; + var widget = optionElement.optionWidget; + var isInputBox = (optionElement.option.valueType === ServiceOptionType.string || + optionElement.option.valueType === ServiceOptionType.password || + optionElement.option.valueType === ServiceOptionType.number); + + if (isInputBox) { + if (!widget.validate()) { + isValid = false; + if (!isFocused) { + isFocused = true; + widget.focus(); + } + } + } + } + return isValid; +} + +export function updateOptions(options: { [optionName: string]: any }, optionsMap: { [optionName: string]: IOptionElement }): void { + for (var optionName in optionsMap) { + var optionElement: IOptionElement = optionsMap[optionName]; + if (optionElement.optionWidget.value !== options[optionName]) { + if (!optionElement.optionWidget.value && options[optionName]) { + delete options[optionName]; + } + if (optionElement.optionWidget.value) { + if (optionElement.option.valueType === ServiceOptionType.boolean) { + options[optionName] = (optionElement.optionWidget.value === this.trueInputValue) ? true : false; + } else { + options[optionName] = optionElement.optionWidget.value; + } + } + optionElement.optionValue = options[optionName]; + } + } +} + +export let trueInputValue: string = 'True'; +export let falseInputValue: string = 'False'; + +export function findElement(container: Builder, className: string): HTMLElement { + var elementBuilder: Builder = container; + while (elementBuilder.getHTMLElement()) { + var htmlElement = elementBuilder.getHTMLElement(); + if (htmlElement.className === className) { + break; + } + elementBuilder = elementBuilder.child(0); + } + return elementBuilder.getHTMLElement(); +} + +export function groupOptionsByCategory(options: data.ServiceOption[]): { [category: string]: data.ServiceOption[] } { + var connectionOptionsMap: { [category: string]: data.ServiceOption[] } = {}; + options.forEach(option => { + var groupName = option.groupName; + if (groupName === null || groupName === undefined) { + groupName = 'General'; + } + + if (!!connectionOptionsMap[groupName]) { + connectionOptionsMap[groupName].push(option); + } else { + connectionOptionsMap[groupName] = [option]; + } + }); + return connectionOptionsMap; +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/panel/media/panel.css b/src/sql/base/browser/ui/panel/media/panel.css new file mode 100644 index 0000000000..d465d6c2c4 --- /dev/null +++ b/src/sql/base/browser/ui/panel/media/panel.css @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.tabbedPanel { + border-top-color: rgba(128, 128, 128, 0.35); + border-top-width: 1; + border-top-style: solid; + box-sizing: border-box; +} + +.tabbedPanel .composite.title { + display: flex; +} + +.tabbedPanel .tabList { + display: flex; + margin: 0 auto; + padding: 0; + justify-content: flex-start; + flex-flow: row; + line-height: 35px; +} + +.tabbedPanel .tabList > .tab { + cursor: pointer; +} + +.tabbedPanel .tabList > .tab > .tabLabel { + text-transform: uppercase; + margin-left: 16px; + margin-right: 16px; + font-size: 11px; + padding-bottom: 4px; +} + +.tabbedPanel .composite.title .title-actions .action-label { + display: block; + height: 35px; + line-height: 35px; + min-width: 28px; + background-size: 16px; + background-position: center center; + background-repeat: no-repeat; +} + +.composite.title .title-actions { + flex: 1; +} + +.tab > .tabLabel.active { + border-bottom: 1px solid; +} + +.composite.title ~ tab.fullsize > :first-child { + height: calc(100% - 38px); +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/panel/panel.component.ts b/src/sql/base/browser/ui/panel/panel.component.ts new file mode 100644 index 0000000000..072a1392f5 --- /dev/null +++ b/src/sql/base/browser/ui/panel/panel.component.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Component, ContentChildren, QueryList, AfterContentInit, Inject, forwardRef, NgZone, OnInit, Input } from '@angular/core'; + +import { TabComponent } from './tab.component'; +import './panelStyles'; + +import * as types from 'vs/base/common/types'; +import { mixin } from 'vs/base/common/objects'; + +export interface IPanelOptions { + /** + * Whether or not to show the tabs if there is only one tab present + */ + showTabsWhenOne?: boolean; +} + +const defaultOptions: IPanelOptions = { + showTabsWhenOne: true +}; + +@Component({ + selector: 'panel', + template: ` +

+
+ +
+
+
+ +
+ ` +}) +export class PanelComponent implements AfterContentInit, OnInit { + @Input() public options: IPanelOptions; + @ContentChildren(TabComponent) private _tabs: QueryList; + private _activeTab: TabComponent; + + constructor( @Inject(forwardRef(() => NgZone)) private _zone: NgZone) { } + + ngOnInit(): void { + this.options = mixin(this.options || {}, defaultOptions, false); + } + + ngAfterContentInit(): void { + if (this._tabs && this._tabs.length > 0) { + this._activeTab = this._tabs.first; + this._activeTab.active = true; + } + } + + /** + * Select a tab based on index (unrecommended) + * @param index index of tab in the html + */ + selectTab(index: number) + /** + * Select a tab based on the identifier that was passed into the tab + * @param identifier specified identifer of the tab + */ + selectTab(identifier: string); + /** + * Select a tab directly if you have access to the object + * @param tab tab to navigate to + */ + selectTab(tab: TabComponent); + selectTab(input: TabComponent | number | string) { + if (this._tabs && this._tabs.length > 0) { + let tab: TabComponent; + if (input instanceof TabComponent) { + tab = input; + } else if (types.isNumber(input)) { + tab = this._tabs[input]; + } else if (types.isString(input)) { + tab = this._tabs.find(i => i.identifier === input); + } + + this._zone.run(() => { + if (this._activeTab) { + this._activeTab.active = false; + } + + this._activeTab = tab; + this._activeTab.active = true; + }); + } + } +} diff --git a/src/sql/base/browser/ui/panel/panel.module.ts b/src/sql/base/browser/ui/panel/panel.module.ts new file mode 100644 index 0000000000..92d15ff75b --- /dev/null +++ b/src/sql/base/browser/ui/panel/panel.module.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { TabComponent } from './tab.component'; +import { PanelComponent } from './panel.component'; + +@NgModule({ + imports: [CommonModule], + exports: [TabComponent, PanelComponent], + declarations: [TabComponent, PanelComponent] +}) +export class PanelModule { } \ No newline at end of file diff --git a/src/sql/base/browser/ui/panel/panel.ts b/src/sql/base/browser/ui/panel/panel.ts new file mode 100644 index 0000000000..c1f56b4895 --- /dev/null +++ b/src/sql/base/browser/ui/panel/panel.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IThemable } from 'vs/platform/theme/common/styler'; +import * as objects from 'vs/base/common/objects'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import { addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner } from 'vs/base/common/actions'; +import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import './panelStyles'; + +export interface IPanelStyles { + +} + +export interface IPanelView { + render(container: HTMLElement): void; + layout(dimension: Dimension): void; +} + +export interface IPanelTab { + title: string; + identifier: string; + view: IPanelView; +} + +interface IInternalPanelTab extends IPanelTab { + header: HTMLElement; + label: HTMLElement; +} + +export type PanelTabIdentifier = string; + +export class TabbedPanel implements IThemable { + private _tabMap = new Map(); + private _shownTab: PanelTabIdentifier; + public readonly headersize = 35; + private _header: HTMLElement; + private _tabList: HTMLElement; + private _actionbar: ActionBar; + private _body: HTMLElement; + private _currentDimensions: Dimension; + private _collapsed = false; + private _parent: HTMLElement; + + private _onTabChange = new Emitter(); + public onTabChange: Event = this._onTabChange.event; + + constructor(private container: HTMLElement) { + this._parent = document.createElement('div'); + this._parent.className = 'tabbedPanel'; + container.appendChild(this._parent); + this._header = document.createElement('div'); + this._header.className = 'composite title'; + this._tabList = document.createElement('div'); + this._tabList.className = 'tabList'; + this._tabList.style.height = this.headersize + 'px'; + this._header.appendChild(this._tabList); + let actionbarcontainer = document.createElement('div'); + actionbarcontainer.className = 'title-actions'; + this._actionbar = new ActionBar(actionbarcontainer); + this._header.appendChild(actionbarcontainer); + this._parent.appendChild(this._header); + this._body = document.createElement('div'); + this._body.className = 'tabBody'; + this._parent.appendChild(this._body); + } + + public pushTab(tab: IPanelTab): PanelTabIdentifier { + let internalTab = objects.clone(tab) as IInternalPanelTab; + this._tabMap.set(tab.identifier, internalTab); + this._createTab(internalTab); + if (!this._shownTab) { + this.showTab(tab.identifier); + } + return tab.identifier as PanelTabIdentifier; + } + + public pushAction(arg: IAction | IAction[], options: IActionOptions = {}): void { + this._actionbar.push(arg, options); + } + + public set actionBarContext(context: any) { + this._actionbar.context = context; + } + + private _createTab(tab: IInternalPanelTab): void { + let tabElement = document.createElement('div'); + tabElement.className = 'tab'; + let tabLabel = document.createElement('a'); + tabLabel.className = 'tabLabel'; + tabLabel.innerText = tab.title; + tabElement.appendChild(tabLabel); + addDisposableListener(tabElement, EventType.CLICK, (e) => this.showTab(tab.identifier)); + this._tabList.appendChild(tabElement); + tab.header = tabElement; + tab.label = tabLabel; + } + + public showTab(id: PanelTabIdentifier): void { + if (this._shownTab && this._shownTab === id) { + return; + } + + if (this._shownTab) { + this._tabMap.get(this._shownTab).label.classList.remove('active'); + } + + this._shownTab = id; + new Builder(this._body).empty(); + let tab = this._tabMap.get(this._shownTab); + tab.label.classList.add('active'); + tab.view.render(this._body); + this._onTabChange.fire(id); + if (this._currentDimensions) { + this._layoutCurrentTab(new Dimension(this._currentDimensions.width, this._currentDimensions.height - this.headersize)); + } + } + + public removeTab(tab: PanelTabIdentifier) { + this._tabMap.get(tab).header.remove(); + this._tabMap.delete(tab); + } + + public style(styles: IPanelStyles): void { + + } + + public layout(dimension: Dimension): void { + this._currentDimensions = dimension; + this._header.style.width = dimension.width + 'px'; + this._body.style.width = dimension.width + 'px'; + this._body.style.height = (dimension.height - this.headersize) + 'px'; + this._layoutCurrentTab(new Dimension(dimension.width, dimension.height - this.headersize)); + } + + private _layoutCurrentTab(dimension: Dimension): void { + if (this._shownTab) { + this._tabMap.get(this._shownTab).view.layout(dimension); + } + } + + public focus(): void { + + } + + public set collapsed(val: boolean) { + if (val === this._collapsed) { + return; + } + + this._collapsed = val === false ? false : true; + if (this.collapsed) { + this._body.remove(); + } else { + this._parent.appendChild(this._body); + } + } + + public get collapsed(): boolean { + return this._collapsed; + } +} diff --git a/src/sql/base/browser/ui/panel/panelStyles.ts b/src/sql/base/browser/ui/panel/panelStyles.ts new file mode 100644 index 0000000000..ffdc18b0c6 --- /dev/null +++ b/src/sql/base/browser/ui/panel/panelStyles.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/panel'; + +import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER } from 'vs/workbench/common/theme'; +import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + + // Title Active + const titleActive = theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND); + const titleActiveBorder = theme.getColor(PANEL_ACTIVE_TITLE_BORDER); + if (titleActive || titleActiveBorder) { + collector.addRule(` + .tabbedPanel > .title > .tabList > .tab:hover .tabLabel, + .tabbedPanel > .title > .tabList > .tab .tabLabel.active { + color: ${titleActive}; + border-bottom-color: ${titleActiveBorder}; + } + `); + } + + // Title Inactive + const titleInactive = theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND); + if (titleInactive) { + collector.addRule(` + .tabbedPanel > .title > .tabList > .tab .tabLabel { + color: ${titleInactive}; + } + `); + } + + // Title focus + const focusBorderColor = theme.getColor(focusBorder); + if (focusBorderColor) { + collector.addRule(` + .tabbedPanel > .title > .tabList > .tab .tabLabel:focus { + color: ${titleActive}; + border-bottom-color: ${focusBorderColor} !important; + border-bottom: 1px solid; + outline: none; + } + `); + } + + // Styling with Outline color (e.g. high contrast theme) + const outline = theme.getColor(activeContrastBorder); + if (outline) { + const outline = theme.getColor(activeContrastBorder); + + collector.addRule(` + .tabbedPanel > .title > .tabList > .tab .tabLabel.active, + .tabbedPanel > .title > .tabList > .tab .tabLabel:hover { + outline-color: ${outline}; + outline-width: 1px; + outline-style: solid; + border-bottom: none; + padding-bottom: 0; + outline-offset: 3px; + } + + .tabbedPanel > .title > .tabList > .tab .tabLabel:hover:not(.active) { + outline-style: dashed; + } + `); + } +}); \ No newline at end of file diff --git a/src/sql/base/browser/ui/panel/tab.component.ts b/src/sql/base/browser/ui/panel/tab.component.ts new file mode 100644 index 0000000000..8b2ecf50c4 --- /dev/null +++ b/src/sql/base/browser/ui/panel/tab.component.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Component, Input, ContentChild } from '@angular/core'; + +export abstract class TabChild { + public abstract layout(): void; +} + +@Component({ + selector: 'tab', + template: ` +
+ +
+ ` +}) +export class TabComponent { + @ContentChild(TabChild) private _child: TabChild; + @Input() public title: string; + public _active = false; + @Input() public identifier: string; + + public set active(val: boolean) { + this._active = val; + if (this.active && this._child) { + this._child.layout(); + } + } + + public get active(): boolean { + return this._active; + } +} diff --git a/src/sql/base/browser/ui/selectBox/selectBox.ts b/src/sql/base/browser/ui/selectBox/selectBox.ts new file mode 100644 index 0000000000..620e052f0e --- /dev/null +++ b/src/sql/base/browser/ui/selectBox/selectBox.ts @@ -0,0 +1,225 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { SelectBox as vsSelectBox, ISelectBoxStyles as vsISelectBoxStyles } from 'vs/base/browser/ui/selectBox/selectBox'; +import { Color } from 'vs/base/common/color'; +import { IContextViewProvider, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import * as dom from 'vs/base/browser/dom'; +import { RenderOptions, renderFormattedText, renderText } from 'vs/base/browser/htmlContentRenderer'; +import { IMessage, MessageType, defaultOpts } from 'vs/base/browser/ui/inputbox/inputBox'; +import aria = require('vs/base/browser/ui/aria/aria'); +import nls = require('vs/nls'); + +const $ = dom.$; + +export interface ISelectBoxStyles extends vsISelectBoxStyles { + disabledSelectBackground?: Color, + disabledSelectForeground?: Color, + inputValidationInfoBorder?: Color; + inputValidationInfoBackground?: Color; + inputValidationWarningBorder?: Color; + inputValidationWarningBackground?: Color; + inputValidationErrorBorder?: Color; + inputValidationErrorBackground?: Color; +} + +export class SelectBox extends vsSelectBox { + private _optionsDictionary; + private _dialogOptions: string[]; + private _selectedOption: string; + private enabledSelectBackground: Color; + private enabledSelectForeground: Color; + private enabledSelectBorder: Color; + private disabledSelectBackground: Color; + private disabledSelectForeground: Color; + private disabledSelectBorder: Color; + private contextViewProvider: IContextViewProvider; + private message: IMessage; + private inputValidationInfoBorder: Color; + private inputValidationInfoBackground: Color; + private inputValidationWarningBorder: Color; + private inputValidationWarningBackground: Color; + private inputValidationErrorBorder: Color; + private inputValidationErrorBackground: Color; + private element: HTMLElement; + + constructor(options: string[], selectedOption: string, container?: HTMLElement, contextViewProvider?: IContextViewProvider) { + super(options, 0); + this._optionsDictionary = new Array(); + for (var i = 0; i < options.length; i++) { + this._optionsDictionary[options[i]] = i; + } + super.select(this._optionsDictionary[selectedOption]); + this._selectedOption = selectedOption; + this._dialogOptions = options; + this._register(this.onDidSelect(newInput => { + this._selectedOption = newInput.selected; + })); + + this.enabledSelectBackground = this.selectBackground; + this.enabledSelectForeground = this.selectForeground; + this.enabledSelectBorder = this.selectBorder; + this.disabledSelectBackground = Color.transparent; + this.disabledSelectForeground = null; + this.disabledSelectBorder = null; + this.contextViewProvider = contextViewProvider; + if (container) { + this.element = dom.append(container, $('.monaco-selectbox.idle')); + } + } + + public style(styles: ISelectBoxStyles): void { + super.style(styles); + this.enabledSelectBackground = this.selectBackground; + this.enabledSelectForeground = this.selectForeground; + this.enabledSelectBorder = this.selectBorder; + this.disabledSelectBackground = styles.disabledSelectBackground; + this.disabledSelectForeground = styles.disabledSelectForeground; + this.inputValidationInfoBorder = styles.inputValidationInfoBorder; + this.inputValidationInfoBackground = styles.inputValidationInfoBackground; + this.inputValidationWarningBorder = styles.inputValidationWarningBorder; + this.inputValidationWarningBackground = styles.inputValidationWarningBackground; + this.inputValidationErrorBorder = styles.inputValidationErrorBorder; + this.inputValidationErrorBackground = styles.inputValidationErrorBackground; + } + + public selectWithOptionName(optionName: string): void { + if (this._optionsDictionary[optionName]) { + this.select(this._optionsDictionary[optionName]); + } else { + this.select(0); + } + } + + public select(index: number): void { + super.select(index); + if (this._dialogOptions !== undefined) { + this._selectedOption = this._dialogOptions[index]; + } + } + + public setOptions(options: string[], selected?: number, disabled?: number): void { + this._optionsDictionary = []; + for (var i = 0; i < options.length; i++) { + this._optionsDictionary[options[i]] = i; + } + this._dialogOptions = options; + super.setOptions(options, selected, disabled); + } + + public get value(): string { + return this._selectedOption; + } + + public enable(): void { + this.selectElement.disabled = false; + this.selectBackground = this.enabledSelectBackground; + this.selectForeground = this.enabledSelectForeground; + this.selectBorder = this.enabledSelectBorder; + this.applyStyles(); + } + + public disable(): void { + this.selectElement.disabled = true; + this.selectBackground = this.disabledSelectBackground; + this.selectForeground = this.disabledSelectForeground; + this.selectBorder = this.disabledSelectBorder; + this.applyStyles(); + } + + public showMessage(message: IMessage): void { + this.message = message; + + dom.removeClass(this.element, 'idle'); + dom.removeClass(this.element, 'info'); + dom.removeClass(this.element, 'warning'); + dom.removeClass(this.element, 'error'); + dom.addClass(this.element, this.classForType(message.type)); + + // ARIA Support + let alertText: string; + if (message.type === MessageType.ERROR) { + alertText = nls.localize('alertErrorMessage', "Error: {0}", message.content); + } else if (message.type === MessageType.WARNING) { + alertText = nls.localize('alertWarningMessage', "Warning: {0}", message.content); + } else { + alertText = nls.localize('alertInfoMessage', "Info: {0}", message.content); + } + + aria.alert(alertText); + + this._showMessage(); + } + + public _showMessage(): void { + if (this.message && this.contextViewProvider && this.element) { + let div: HTMLElement; + let layout = () => div.style.width = dom.getTotalWidth(this.selectElement) + 'px'; + + this.contextViewProvider.showContextView({ + getAnchor: () => this.selectElement, + anchorAlignment: AnchorAlignment.RIGHT, + render: (container: HTMLElement) => { + div = dom.append(container, $('.monaco-inputbox-container')); + layout(); + + const renderOptions: RenderOptions = { + inline: true, + className: 'monaco-inputbox-message' + }; + + let spanElement: HTMLElement = (this.message.formatContent + ? renderFormattedText(this.message.content, renderOptions) + : renderText(this.message.content, renderOptions)) as any; + dom.addClass(spanElement, this.classForType(this.message.type)); + + const styles = this.stylesForType(this.message.type); + spanElement.style.backgroundColor = styles.background ? styles.background.toString() : null; + spanElement.style.border = styles.border ? `1px solid ${styles.border}` : null; + + dom.append(div, spanElement); + + return null; + }, + layout: layout + }); + } + } + + public hideMessage(): void { + this.message = null; + + dom.removeClass(this.element, 'info'); + dom.removeClass(this.element, 'warning'); + dom.removeClass(this.element, 'error'); + dom.addClass(this.element, 'idle'); + + this._hideMessage(); + this.applyStyles(); + } + + private _hideMessage(): void { + if (this.contextViewProvider) { + this.contextViewProvider.hideContextView(); + } + } + + private classForType(type: MessageType): string { + switch (type) { + case MessageType.INFO: return 'info'; + case MessageType.WARNING: return 'warning'; + default: return 'error'; + } + } + + private stylesForType(type: MessageType): { border: Color; background: Color } { + switch (type) { + case MessageType.INFO: return { border: this.inputValidationInfoBorder, background: this.inputValidationInfoBackground }; + case MessageType.WARNING: return { border: this.inputValidationWarningBorder, background: this.inputValidationWarningBackground }; + default: return { border: this.inputValidationErrorBorder, background: this.inputValidationErrorBackground }; + } + } +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/table/media/sort-asc.gif b/src/sql/base/browser/ui/table/media/sort-asc.gif new file mode 100644 index 0000000000000000000000000000000000000000..67a2a4c669fc5821a07fc486228d626e16d6ad9e GIT binary patch literal 830 zcmZ?wbhEHb div { + width: 100%; + height: 100%; +} + +.slick-sort-indicator-asc { + background: url('sort-asc.gif'); +} + +.slick-sort-indicator-desc { + background: url('sort-desc.gif'); +} + +.slick-sort-indicator { + display: inline-block; + width: 8px; + height: 5px; + margin-left: 4px; + margin-top: 6px; +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts b/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts new file mode 100644 index 0000000000..3b227e86da --- /dev/null +++ b/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts @@ -0,0 +1,137 @@ +// Adapted from https://github.com/naresh-n/slickgrid-column-data-autosize/blob/master/src/slick.autocolumnsize.js + +import { mixin, clone } from 'vs/base/common/objects'; + +export interface IAutoColumnSizeOptions extends Slick.PluginOptions { + maxWidth?: number; +} + +const defaultOptions: IAutoColumnSizeOptions = { + maxWidth: 200 +}; + +export class AutoColumnSize implements Slick.Plugin { + private _grid: Slick.Grid; + private _$container: JQuery; + private _context: CanvasRenderingContext2D; + private _options: IAutoColumnSizeOptions; + + constructor(options: IAutoColumnSizeOptions) { + this._options = mixin(options, defaultOptions, false); + } + + public init(grid: Slick.Grid) { + this._grid = grid; + + this._$container = $(this._grid.getContainerNode()); + this._$container.on('dblclick.autosize', '.slick-resizable-handle', e => this.reSizeColumn(e)); + this._context = document.createElement('canvas').getContext('2d'); + } + + public destroy() { + this._$container.off(); + } + + private reSizeColumn(e: Event) { + let headerEl = $(e.currentTarget).closest('.slick-header-column'); + let columnDef = headerEl.data('column'); + + if (!columnDef || !columnDef.resizable) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + let headerWidth = this.getElementWidth(headerEl[0]); + let colIndex = this._grid.getColumnIndex(columnDef.id); + let origCols = this._grid.getColumns(); + let allColumns = clone(origCols); + allColumns.forEach((col, index) => { + col.formatter = origCols[index].formatter; + col.asyncPostRender = origCols[index].asyncPostRender; + }); + let column = allColumns[colIndex]; + + let autoSizeWidth = Math.max(headerWidth, this.getMaxColumnTextWidth(columnDef, colIndex)) + 1; + + if (autoSizeWidth !== column.width) { + allColumns[colIndex].width = autoSizeWidth; + this._grid.setColumns(allColumns); + this._grid.onColumnsResized.notify(); + } + } + + private getMaxColumnTextWidth(columnDef, colIndex: number): number { + let texts = []; + let rowEl = this.createRow(columnDef); + let data = this._grid.getData(); + let viewPort = this._grid.getViewport(); + let start = Math.max(0, viewPort.top + 1); + let end = Math.min(data.getLength(), viewPort.bottom); + for (let i = start; i < end; i++) { + texts.push(data.getItem(i)[columnDef.field]); + } + let template = this.getMaxTextTemplate(texts, columnDef, colIndex, data, rowEl); + let width = this.getTemplateWidth(rowEl, template); + this.deleteRow(rowEl); + return width; + } + + private getTemplateWidth(rowEl: JQuery, template: JQuery | HTMLElement): number { + let cell = $(rowEl.find('.slick-cell')); + cell.append(template); + $(cell).find('*').css('position', 'relative'); + return cell.outerWidth() + 1; + } + + private getMaxTextTemplate(texts: string[], columnDef, colIndex: number, data, rowEl: JQuery): JQuery | HTMLElement { + let max = 0, + maxTemplate = null; + let formatFun = columnDef.formatter; + texts.forEach((text, index) => { + let template; + if (formatFun) { + template = $('' + formatFun(index, colIndex, text, columnDef, data[index]) + ''); + text = template.text() || text; + } + let length = text ? this.getElementWidthUsingCanvas(rowEl, text) : 0; + if (length > max) { + max = length; + maxTemplate = template || text; + } + }); + return maxTemplate; + } + + private createRow(columnDef): JQuery { + let rowEl = $('
'); + rowEl.find('.slick-cell').css({ + 'visibility': 'hidden', + 'text-overflow': 'initial', + 'white-space': 'nowrap' + }); + let gridCanvas = this._$container.find('.grid-canvas'); + $(gridCanvas).append(rowEl); + return rowEl; + } + + private deleteRow(rowEl: JQuery) { + $(rowEl).remove(); + } + + private getElementWidth(element: HTMLElement): number { + let width, clone = element.cloneNode(true) as HTMLElement; + clone.style.cssText = 'position: absolute; visibility: hidden;right: auto;text-overflow: initial;white-space: nowrap;'; + element.parentNode.insertBefore(clone, element); + width = clone.offsetWidth; + clone.parentNode.removeChild(clone); + return width; + } + + private getElementWidthUsingCanvas(element: JQuery, text: string): number { + this._context.font = element.css('font-size') + ' ' + element.css('font-family'); + let metrics = this._context.measureText(text); + return metrics.width; + } +} diff --git a/src/sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin.ts b/src/sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin.ts new file mode 100644 index 0000000000..428f8df095 --- /dev/null +++ b/src/sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin.ts @@ -0,0 +1,189 @@ +// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.checkboxselectcolumn.js + +import 'vs/css!vs/base/browser/ui/checkbox/checkbox'; + + +import { mixin } from 'vs/base/common/objects'; +import * as nls from 'vs/nls'; +import { ICheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox'; +import { Color } from 'vs/base/common/color'; +import * as strings from 'vs/base/common/strings'; + +export interface ICheckboxSelectColumnOptions extends Slick.PluginOptions, ICheckboxStyles { + columnId?: string; + cssClass?: string; + toolTip?: string; + width?: number; + title?: string; +} + +const defaultOptions: ICheckboxSelectColumnOptions = { + columnId: '_checkbox_selector', + cssClass: null, + toolTip: nls.localize('selectDeselectAll', 'Select/Deselect All'), + width: 30, + inputActiveOptionBorder: Color.fromHex('#007ACC') +}; + +const checkBoxTemplate = `
+ +
`; + +export class CheckboxSelectColumn implements Slick.Plugin { + private _options: ICheckboxSelectColumnOptions; + private _grid: Slick.Grid; + private _handler = new Slick.EventHandler(); + private _selectedRowsLookup = {}; + private _checkboxTemplate: string; + + constructor(options?: Slick.PluginOptions) { + this._options = mixin(options, defaultOptions, false); + this.applyStyles(); + } + + public init(grid: Slick.Grid): void { + this._grid = grid; + this._handler + .subscribe(this._grid.onSelectedRowsChanged, (e, args) => this.handleSelectedRowsChanged(e, args)) + .subscribe(this._grid.onClick, (e, args) => this.handleClick(e, args)) + .subscribe(this._grid.onHeaderClick, (e, args) => this.handleHeaderClick(e, args)) + .subscribe(this._grid.onKeyDown, (e, args) => this.handleKeyDown(e, args)); + } + + public destroy(): void { + this._handler.unsubscribeAll(); + } + + private handleSelectedRowsChanged(e: Event, args: Slick.OnSelectedRowsChangedEventArgs): void { + let selectedRows = this._grid.getSelectedRows(); + let lookup = {}, row, i; + for (i = 0; i < selectedRows.length; i++) { + row = selectedRows[i]; + lookup[row] = true; + if (lookup[row] !== this._selectedRowsLookup[row]) { + this._grid.invalidateRow(row); + delete this._selectedRowsLookup[row]; + } + } + for (i in this._selectedRowsLookup) { + this._grid.invalidateRow(i); + } + this._selectedRowsLookup = lookup; + this._grid.render(); + + if (!this._options.title) { + if (selectedRows.length && selectedRows.length === this._grid.getDataLength()) { + this._grid.updateColumnHeader(this._options.columnId, + strings.format(this._checkboxTemplate, 'true', 'checked'), + this._options.toolTip); + } else { + this._grid.updateColumnHeader(this._options.columnId, + strings.format(this._checkboxTemplate, 'true', 'unchecked'), + this._options.toolTip); + } + } + } + + private handleKeyDown(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs): void { + if (e.which === 32) { + if (this._grid.getColumns()[args.cell].id === this._options.columnId) { + // if editing, try to commit + if (!this._grid.getEditorLock().isActive() || this._grid.getEditorLock().commitCurrentEdit()) { + this.toggleRowSelection(args.row); + } + e.preventDefault(); + e.stopImmediatePropagation(); + } + } + } + + private handleClick(e: Event, args: Slick.OnClickEventArgs): void { + // clicking on a row select checkbox + if (this._grid.getColumns()[args.cell].id === this._options.columnId && $(e.target).is('.custom-checkbox')) { + // if editing, try to commit + if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) { + e.preventDefault(); + e.stopImmediatePropagation(); + return; + } + + this.toggleRowSelection(args.row); + e.stopPropagation(); + e.stopImmediatePropagation(); + } + } + + private toggleRowSelection(row: number): void { + if (this._selectedRowsLookup[row]) { + this._grid.setSelectedRows(this._grid.getSelectedRows().filter(n => n !== row)); + } else { + this._grid.setSelectedRows(this._grid.getSelectedRows().concat(row)); + } + } + + private handleHeaderClick(e: Event, args: Slick.OnHeaderClickEventArgs): void { + if (!this._options.title && args.column.id === this._options.columnId && $(e.target).is('.custom-checkbox')) { + // if editing, try to commit + if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) { + e.preventDefault(); + e.stopImmediatePropagation(); + return; + } + + if ($(e.target).is('.unchecked')) { + let rows = []; + for (let i = 0; i < this._grid.getDataLength(); i++) { + rows.push(i); + } + this._grid.setSelectedRows(rows); + this._grid.updateColumnHeader(this._options.columnId, + strings.format(this._checkboxTemplate, 'true', 'checked'), + this._options.toolTip); + } else { + this._grid.setSelectedRows([]); + this._grid.updateColumnHeader(this._options.columnId, + strings.format(this._checkboxTemplate, 'true', 'unchecked'), this._options.toolTip); + e.stopPropagation(); + e.stopImmediatePropagation(); + } + } + } + + public getColumnDefinition(): Slick.Column { + return { + id: this._options.columnId, + name: this._options.title || strings.format(this._checkboxTemplate, 'true', 'unchecked'), + toolTip: this._options.toolTip, + field: 'sel', + width: this._options.width, + resizable: false, + sortable: false, + cssClass: this._options.cssClass, + formatter: (r, c, v, cd, dc) => this.checkboxSelectionFormatter(r, c, v, cd, dc) + }; + } + + private checkboxSelectionFormatter(row, cell, value, columnDef: Slick.Column, dataContext): string { + if (dataContext) { + return this._selectedRowsLookup[row] + ? strings.format(this._checkboxTemplate, 'true', 'checked') + : strings.format(this._checkboxTemplate, 'true', 'unchecked'); + } + return null; + } + + public style(styles: ICheckboxStyles): void { + if (styles.inputActiveOptionBorder) { + this._options.inputActiveOptionBorder = styles.inputActiveOptionBorder; + } + this.applyStyles(); + } + + protected applyStyles(): void { + this._checkboxTemplate = strings.format(checkBoxTemplate, this._options.inputActiveOptionBorder.toString(), '{0}', '{1}'); + if (this._grid) { + this._grid.invalidateAllRows(); + this._grid.render(); + } + } +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin.ts b/src/sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin.ts new file mode 100644 index 0000000000..93ad1468d5 --- /dev/null +++ b/src/sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin.ts @@ -0,0 +1,364 @@ +// Drag select selection model gist taken from https://gist.github.com/skoon/5312536 +// heavily modified + +import { clone } from 'vs/base/common/objects'; + +export class DragCellSelectionModel implements Slick.SelectionModel> { + private readonly keyColResizeIncr = 5; + + private _grid: Slick.Grid; + private _ranges: Array = []; + private _dragging = false; + private _handler = new Slick.EventHandler(); + + public onSelectedRangesChanged = new Slick.Event(); + + public init(grid: Slick.Grid): void { + this._grid = grid; + this._handler.subscribe(this._grid.onActiveCellChanged, (e: Event, data: Slick.OnActiveCellChangedEventArgs) => this.handleActiveCellChange(e, data)); + this._handler.subscribe(this._grid.onKeyDown, (e: JQueryInputEventObject) => this.handleKeyDown(e)); + this._handler.subscribe(this._grid.onClick, (e: MouseEvent) => this.handleClick(e)); + this._handler.subscribe(this._grid.onDrag, (e: MouseEvent) => this.handleDrag(e)); + this._handler.subscribe(this._grid.onDragInit, (e: MouseEvent) => this.handleDragInit(e)); + this._handler.subscribe(this._grid.onDragStart, (e: MouseEvent) => this.handleDragStart(e)); + this._handler.subscribe(this._grid.onDragEnd, (e: MouseEvent) => this.handleDragEnd(e)); + this._handler.subscribe(this._grid.onHeaderClick, (e: MouseEvent, args: Slick.OnHeaderClickEventArgs) => this.handleHeaderClick(e, args)); + } + + public destroy(): void { + this._handler.unsubscribeAll(); + } + + private rangesToRows(ranges: Array): Array { + let rows = []; + for (let i = 0; i < ranges.length; i++) { + for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { + rows.push(j); + } + } + return rows; + } + + private rowsToRanges(rows: Array): Array { + let ranges = []; + let lastCell = this._grid.getColumns().length - 1; + for (let i = 0; i < rows.length; i++) { + ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell)); + } + return ranges; + } + + public getSelectedRows(): Array { + return this.rangesToRows(this._ranges); + } + + public setSelectedRows(rows: Array) { + this.setSelectedRanges(this.rowsToRanges(rows)); + } + + public setSelectedRanges(ranges: Array) { + this._ranges = ranges; + this.onSelectedRangesChanged.notify(this._ranges); + } + + public getSelectedRanges(): Array { + return this._ranges; + } + + private handleActiveCellChange(e: Event, data: Slick.OnActiveCellChangedEventArgs) { } + + private isNavigationKey(e: BaseJQueryEventObject) { + // Nave keys (home, end, arrows) are all in sequential order so use a + switch (e.which) { + case $.ui.keyCode.HOME: + case $.ui.keyCode.END: + case $.ui.keyCode.LEFT: + case $.ui.keyCode.UP: + case $.ui.keyCode.RIGHT: + case $.ui.keyCode.DOWN: + return true; + default: + return false; + } + } + + private navigateLeft(e: JQueryInputEventObject, activeCell: Slick.Cell) { + if (activeCell.cell > 1) { + let isHome = e.which === $.ui.keyCode.HOME; + let newActiveCellColumn = isHome ? 1 : activeCell.cell - 1; + // Unsure why but for range, must record 1 index less than expected + let newRangeColumn = newActiveCellColumn - 1; + + if (e.shiftKey) { + let last = this._ranges.pop(); + + // If we are on the rightmost edge of the range and we navigate left, + // we want to deselect the rightmost cell + if (last.fromCell <= newRangeColumn) { last.toCell -= 1; } + + let fromRow = Math.min(activeCell.row, last.fromRow); + let fromCell = Math.min(newRangeColumn, last.fromCell); + let toRow = Math.max(activeCell.row, last.toRow); + let toCell = Math.max(newRangeColumn, last.toCell); + this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)]; + } else { + this._ranges = [new Slick.Range(activeCell.row, newRangeColumn, activeCell.row, newRangeColumn)]; + } + + this._grid.setActiveCell(activeCell.row, newActiveCellColumn); + this.setSelectedRanges(this._ranges); + } + } + + private navigateRight(e: JQueryInputEventObject, activeCell: Slick.Cell) { + let columnLength = this._grid.getColumns().length; + if (activeCell.cell < columnLength) { + let isEnd = e.which === $.ui.keyCode.END; + let newActiveCellColumn = isEnd ? columnLength : activeCell.cell + 1; + // Unsure why but for range, must record 1 index less than expected + let newRangeColumn = newActiveCellColumn - 1; + if (e.shiftKey) { + let last = this._ranges.pop(); + + // If we are on the leftmost edge of the range and we navigate right, + // we want to deselect the leftmost cell + if (newRangeColumn <= last.toCell) { last.fromCell += 1; } + + let fromRow = Math.min(activeCell.row, last.fromRow); + let fromCell = Math.min(newRangeColumn, last.fromCell); + let toRow = Math.max(activeCell.row, last.toRow); + let toCell = Math.max(newRangeColumn, last.toCell); + + this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)]; + } else { + this._ranges = [new Slick.Range(activeCell.row, newRangeColumn, activeCell.row, newRangeColumn)]; + } + this._grid.setActiveCell(activeCell.row, newActiveCellColumn); + this.setSelectedRanges(this._ranges); + } + } + + private handleKeyDown(e: JQueryInputEventObject) { + let activeCell = this._grid.getActiveCell(); + + if (activeCell) { + // navigation keys + if (this.isNavigationKey(e)) { + e.stopImmediatePropagation(); + if (e.ctrlKey || e.metaKey) { + let event = new CustomEvent('gridnav', { + detail: { + which: e.which, + ctrlKey: e.ctrlKey, + metaKey: e.metaKey, + shiftKey: e.shiftKey, + altKey: e.altKey + } + }); + window.dispatchEvent(event); + return; + } + // end key + if (e.which === $.ui.keyCode.END) { + this.navigateRight(e, activeCell); + } + // home key + if (e.which === $.ui.keyCode.HOME) { + this.navigateLeft(e, activeCell); + } + // left arrow + if (e.which === $.ui.keyCode.LEFT) { + // column resize + if ((e.ctrlKey || e.metaKey) && e.shiftKey) { + let allColumns = clone(this._grid.getColumns()); + allColumns[activeCell.cell - 1].width = allColumns[activeCell.cell - 1].width - this.keyColResizeIncr; + this._grid.setColumnWidths(allColumns); + } else { + this.navigateLeft(e, activeCell); + } + // up arrow + } else if (e.which === $.ui.keyCode.UP && activeCell.row > 0) { + if (e.shiftKey) { + let last = this._ranges.pop(); + + // If we are on the bottommost edge of the range and we navigate up, + // we want to deselect the bottommost row + let newRangeRow = activeCell.row - 1; + if (last.fromRow <= newRangeRow) { last.toRow -= 1; } + + let fromRow = Math.min(activeCell.row - 1, last.fromRow); + let fromCell = Math.min(activeCell.cell - 1, last.fromCell); + let toRow = Math.max(newRangeRow, last.toRow); + let toCell = Math.max(activeCell.cell - 1, last.toCell); + this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)]; + } else { + this._ranges = [new Slick.Range(activeCell.row - 1, activeCell.cell - 1, activeCell.row - 1, activeCell.cell - 1)]; + } + this._grid.setActiveCell(activeCell.row - 1, activeCell.cell); + this.setSelectedRanges(this._ranges); + // right arrow + } else if (e.which === $.ui.keyCode.RIGHT) { + // column resize + if ((e.ctrlKey || e.metaKey) && e.shiftKey) { + let allColumns = clone(this._grid.getColumns()); + allColumns[activeCell.cell - 1].width = allColumns[activeCell.cell - 1].width + this.keyColResizeIncr; + this._grid.setColumnWidths(allColumns); + } else { + this.navigateRight(e, activeCell); + } + // down arrow + } else if (e.which === $.ui.keyCode.DOWN && activeCell.row < this._grid.getDataLength() - 1) { + if (e.shiftKey) { + let last = this._ranges.pop(); + + // If we are on the topmost edge of the range and we navigate down, + // we want to deselect the topmost row + let newRangeRow = activeCell.row + 1; + if (newRangeRow <= last.toRow) { last.fromRow += 1; } + + let fromRow = Math.min(activeCell.row + 1, last.fromRow); + let fromCell = Math.min(activeCell.cell - 1, last.fromCell); + let toRow = Math.max(activeCell.row + 1, last.toRow); + let toCell = Math.max(activeCell.cell - 1, last.toCell); + this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)]; + } else { + this._ranges = [new Slick.Range(activeCell.row + 1, activeCell.cell - 1, activeCell.row + 1, activeCell.cell - 1)]; + } + this._grid.setActiveCell(activeCell.row + 1, activeCell.cell); + this.setSelectedRanges(this._ranges); + } + } + } + } + + private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs) { + let columnIndex = this._grid.getColumnIndex(args.column.id); + if (e.ctrlKey || e.metaKey) { + this._ranges.push(new Slick.Range(0, columnIndex, this._grid.getDataLength() - 1, columnIndex)); + this._grid.setActiveCell(0, columnIndex + 1); + } else if (e.shiftKey && this._ranges.length) { + let last = this._ranges.pop().fromCell; + let from = Math.min(columnIndex, last); + let to = Math.max(columnIndex, last); + this._ranges = []; + for (let i = from; i <= to; i++) { + if (i !== last) { + this._ranges.push(new Slick.Range(0, i, this._grid.getDataLength() - 1, i)); + } + } + this._ranges.push(new Slick.Range(0, last, this._grid.getDataLength() - 1, last)); + } else { + this._ranges = [new Slick.Range(0, columnIndex, this._grid.getDataLength() - 1, columnIndex)]; + this._grid.resetActiveCell(); + } + this.setSelectedRanges(this._ranges); + e.stopImmediatePropagation(); + return true; + } + + private handleClick(e: MouseEvent) { + let cell = this._grid.getCellFromEvent(e); + if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) { + return false; + } + + if (!e.ctrlKey && !e.shiftKey && !e.metaKey) { + if (cell.cell !== 0) { + this._ranges = [new Slick.Range(cell.row, cell.cell - 1, cell.row, cell.cell - 1)]; + this.setSelectedRanges(this._ranges); + this._grid.setActiveCell(cell.row, cell.cell); + return true; + } else { + this._ranges = [new Slick.Range(cell.row, 0, cell.row, this._grid.getColumns().length - 1)]; + this.setSelectedRanges(this._ranges); + this._grid.setActiveCell(cell.row, 1); + return true; + } + } + else if (this._grid.getOptions().multiSelect) { + if (e.ctrlKey || e.metaKey) { + if (cell.cell === 0) { + this._ranges.push(new Slick.Range(cell.row, 0, cell.row, this._grid.getColumns().length - 1)); + this._grid.setActiveCell(cell.row, 1); + } else { + this._ranges.push(new Slick.Range(cell.row, cell.cell - 1, cell.row, cell.cell - 1)); + this._grid.setActiveCell(cell.row, cell.cell); + } + } else if (this._ranges.length && e.shiftKey) { + let last = this._ranges.pop(); + if (cell.cell === 0) { + let fromRow = Math.min(cell.row, last.fromRow); + let toRow = Math.max(cell.row, last.fromRow); + this._ranges = [new Slick.Range(fromRow, 0, toRow, this._grid.getColumns().length - 1)]; + } else { + let fromRow = Math.min(cell.row, last.fromRow); + let fromCell = Math.min(cell.cell - 1, last.fromCell); + let toRow = Math.max(cell.row, last.toRow); + let toCell = Math.max(cell.cell - 1, last.toCell); + this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)]; + } + } + } + + this.setSelectedRanges(this._ranges); + + return true; + } + + private handleDragInit(e: MouseEvent) { + e.stopImmediatePropagation(); + } + + private handleDragStart(e: MouseEvent) { + let cell = this._grid.getCellFromEvent(e); + e.stopImmediatePropagation(); + this._dragging = true; + if (e.ctrlKey || e.metaKey) { + this._ranges.push(new Slick.Range(cell.row, cell.cell)); + this._grid.setActiveCell(cell.row, cell.cell); + } else if (this._ranges.length && e.shiftKey) { + let last = this._ranges.pop(); + let fromRow = Math.min(cell.row, last.fromRow); + let fromCell = Math.min(cell.cell - 1, last.fromCell); + let toRow = Math.max(cell.row, last.toRow); + let toCell = Math.max(cell.cell - 1, last.toCell); + this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)]; + } else { + this._ranges = [new Slick.Range(cell.row, cell.cell)]; + this._grid.setActiveCell(cell.row, cell.cell); + } + this.setSelectedRanges(this._ranges); + } + + private handleDrag(e: MouseEvent) { + if (this._dragging) { + let cell = this._grid.getCellFromEvent(e); + let activeCell = this._grid.getActiveCell(); + if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) { + return false; + } + + this._ranges.pop(); + + if (activeCell.cell === 0) { + let lastCell = this._grid.getColumns().length - 1; + let firstRow = Math.min(cell.row, activeCell.row); + let lastRow = Math.max(cell.row, activeCell.row); + this._ranges.push(new Slick.Range(firstRow, 0, lastRow, lastCell)); + } else { + let firstRow = Math.min(cell.row, activeCell.row); + let lastRow = Math.max(cell.row, activeCell.row); + let firstColumn = Math.min(cell.cell - 1, activeCell.cell - 1); + let lastColumn = Math.max(cell.cell - 1, activeCell.cell - 1); + this._ranges.push(new Slick.Range(firstRow, firstColumn, lastRow, lastColumn)); + } + this.setSelectedRanges(this._ranges); + return true; + } + return false; + } + + private handleDragEnd(e: MouseEvent) { + this._dragging = false; + } +} \ No newline at end of file diff --git a/src/sql/base/browser/ui/table/plugins/rowSelectionModel.plugin.ts b/src/sql/base/browser/ui/table/plugins/rowSelectionModel.plugin.ts new file mode 100644 index 0000000000..884936feb5 --- /dev/null +++ b/src/sql/base/browser/ui/table/plugins/rowSelectionModel.plugin.ts @@ -0,0 +1,164 @@ +// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowselectionmodel.js +// heavily modified +import { mixin } from 'vs/base/common/objects'; + +const defaultOptions: IRowSelectionModelOptions = { + selectActiveRow: true +}; + +export interface IRowSelectionModelOptions extends Slick.PluginOptions { + selectActiveRow?: boolean; +} + +export class RowSelectionModel implements Slick.SelectionModel> { + private _options: IRowSelectionModelOptions; + private _grid: Slick.Grid; + private _handler = new Slick.EventHandler(); + private _ranges: Array = []; + + public onSelectedRangesChanged = new Slick.Event>(); + + constructor(options?: Slick.PluginOptions) { + this._options = mixin(options, defaultOptions, false); + } + + public init(grid: Slick.Grid) { + this._grid = grid; + this._handler + .subscribe(this._grid.onActiveCellChanged, (e, data) => this.handleActiveCellChange(e, data)) + .subscribe(this._grid.onKeyDown, e => this.handleKeyDown(e)) + .subscribe(this._grid.onClick, e => this.handleClick(e)); + } + + private rangesToRows(ranges: Slick.Range[]): number[] { + let rows = []; + for (let i = 0; i < ranges.length; i++) { + for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { + rows.push(j); + } + } + return rows; + } + + private rowsToRanges(rows: number[]): Slick.Range[] { + let ranges = []; + let lastCell = this._grid.getColumns().length - 1; + for (let i = 0; i < rows.length; i++) { + ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell)); + } + return ranges; + } + + public getSelectedRows(): number[] { + return this.rangesToRows(this._ranges); + } + + public setSelectedRows(rows: number[]): void { + this.setSelectedRanges(this.rowsToRanges(rows)); + } + + public setSelectedRanges(ranges: Slick.Range[]): void { + // simle check for: empty selection didn't change, prevent firing onSelectedRangesChanged + if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) { return; } + this._ranges = ranges; + this.onSelectedRangesChanged.notify(this._ranges); + } + + public getSelectedRanges(): Slick.Range[] { + return this._ranges; + } + + private getRowsRange(from: number, to: number): number[] { + let i, rows = []; + for (i = from; i <= to; i++) { + rows.push(i); + } + for (i = to; i < from; i++) { + rows.push(i); + } + return rows; + } + + private handleActiveCellChange(e: Event, data: Slick.OnActiveCellChangedEventArgs): void { + if (this._options.selectActiveRow && data.row !== null) { + this.setSelectedRanges([new Slick.Range(data.row, 0, data.row, this._grid.getColumns().length - 1)]); + } + } + + private handleKeyDown(e: KeyboardEvent): void { + let activeRow = this._grid.getActiveCell(); + if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which === 38 || e.which === 40)) { + let selectedRows = this.getSelectedRows(); + selectedRows.sort((x, y) => x - y); + + if (!selectedRows.length) { + selectedRows = [activeRow.row]; + } + + let top = selectedRows[0]; + let bottom = selectedRows[selectedRows.length - 1]; + let active; + + if (e.which === 40) { + active = activeRow.row < bottom || top === bottom ? ++bottom : ++top; + } else { + active = activeRow.row < bottom ? --bottom : --top; + } + + if (active >= 0 && active < this._grid.getDataLength()) { + this._grid.scrollRowIntoView(active, undefined); + let tempRanges = this.rowsToRanges(this.getRowsRange(top, bottom)); + this.setSelectedRanges(tempRanges); + } + + e.preventDefault(); + e.stopPropagation(); + } + } + + private handleClick(e: KeyboardEvent): boolean { + let cell = this._grid.getCellFromEvent(e); + if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) { + return false; + } + + if (!this._grid.getOptions().multiSelect || ( + !e.ctrlKey && !e.shiftKey && !e.metaKey)) { + return false; + } + + let selection = this.rangesToRows(this._ranges); + let idx = $.inArray(cell.row, selection); + + if (idx === -1 && (e.ctrlKey || e.metaKey)) { + selection.push(cell.row); + this._grid.setActiveCell(cell.row, cell.cell); + } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { + selection = selection.filter(o => o !== cell.row); + this._grid.setActiveCell(cell.row, cell.cell); + } else if (selection.length && e.shiftKey) { + let last = selection.pop(); + let from = Math.min(cell.row, last); + let to = Math.max(cell.row, last); + selection = []; + for (let i = from; i <= to; i++) { + if (i !== last) { + selection.push(i); + } + } + selection.push(last); + this._grid.setActiveCell(cell.row, cell.cell); + } + + let tempRanges = this.rowsToRanges(selection); + this.setSelectedRanges(tempRanges); + e.stopImmediatePropagation(); + + return true; + } + + public destroy() { + this._handler.unsubscribeAll(); + } + +} diff --git a/src/sql/base/browser/ui/table/table.ts b/src/sql/base/browser/ui/table/table.ts new file mode 100644 index 0000000000..fe296ec798 --- /dev/null +++ b/src/sql/base/browser/ui/table/table.ts @@ -0,0 +1,273 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/table'; +import { TableDataView } from './tableDataView'; + +import { IThemable } from 'vs/platform/theme/common/styler'; +import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; +import * as DOM from 'vs/base/browser/dom'; +import { Color } from 'vs/base/common/color'; +import { mixin } from 'vs/base/common/objects'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Dimension } from 'vs/base/browser/builder'; +import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; + +export interface ITableStyles extends IListStyles { + tableHeaderBackground?: Color; + tableHeaderForeground?: Color; +} + +function getDefaultOptions(): Slick.GridOptions { + return >{ + syncColumnCellResize: true, + enableColumnReorder: false + }; +} + +export class Table implements IThemable { + private _grid: Slick.Grid; + private _columns: Slick.Column[]; + private _data: TableDataView; + private _styleElement: HTMLStyleElement; + private _idPrefix: string; + private _autoscroll: boolean; + private _onRowCountChangeListener: IDisposable; + private _container: HTMLElement; + private _tableContainer: HTMLElement; + + constructor(parent: HTMLElement, data?: Array | TableDataView, columns?: Slick.Column[], options?: Slick.GridOptions) { + if (data instanceof TableDataView) { + this._data = data; + } else { + this._data = new TableDataView(data); + } + + if (columns) { + this._columns = columns; + } else { + this._columns = new Array>(); + } + + let newOptions = mixin(options || {}, getDefaultOptions(), false); + + this._container = document.createElement('div'); + this._container.className = 'monaco-table'; + parent.appendChild(this._container); + this._styleElement = DOM.createStyleSheet(this._container); + this._tableContainer = document.createElement('div'); + this._container.appendChild(this._tableContainer); + this._styleElement = DOM.createStyleSheet(this._container); + this._grid = new Slick.Grid(this._tableContainer, this._data, this._columns, newOptions); + this._idPrefix = this._tableContainer.classList[0]; + this._onRowCountChangeListener = this._data.onRowCountChange(() => this._handleRowCountChange()); + this._grid.onSort.subscribe((e, args) => { + this._data.sort(args); + this._grid.invalidate(); + this._grid.render(); + }); + } + + private _handleRowCountChange() { + this._grid.updateRowCount(); + this._grid.render(); + if (this._autoscroll) { + this._grid.scrollRowIntoView(this._data.getLength() - 1, false); + } + } + + set columns(columns: Slick.Column[]) { + this._grid.setColumns(columns); + } + + setData(data: Array); + setData(data: TableDataView); + setData(data: Array | TableDataView) { + if (data instanceof TableDataView) { + this._data = data; + } else { + this._data = new TableDataView(data); + } + this._onRowCountChangeListener.dispose(); + this._grid.setData(this._data, true); + this._onRowCountChangeListener = this._data.onRowCountChange(() => this._handleRowCountChange()); + } + + get columns(): Slick.Column[] { + return this._grid.getColumns(); + } + + setSelectedRows(rows: number[]) { + this._grid.setSelectedRows(rows); + } + + getSelectedRows(): number[] { + return this._grid.getSelectedRows(); + } + + onSelectedRowsChanged(fn: (e: Slick.EventData, data: Slick.OnSelectedRowsChangedEventArgs) => any): IDisposable + onSelectedRowsChanged(fn: (e: DOMEvent, data: Slick.OnSelectedRowsChangedEventArgs) => any): IDisposable + onSelectedRowsChanged(fn: any): IDisposable { + this._grid.onSelectedRowsChanged.subscribe(fn); + return { + dispose() { + this._grid.onSelectedRowsChanged.unsubscribe(fn); + } + }; + } + + onContextMenu(fn: (e: Slick.EventData, data: Slick.OnContextMenuEventArgs) => any): IDisposable; + onContextMenu(fn: (e: DOMEvent, data: Slick.OnContextMenuEventArgs) => any): IDisposable; + onContextMenu(fn: any): IDisposable { + this._grid.onContextMenu.subscribe(fn); + return { + dispose() { + this._grid.onContextMenu.unsubscribe(fn); + } + }; + } + + getCellFromEvent(e: DOMEvent): Slick.Cell { + return this._grid.getCellFromEvent(e); + } + + setSelectionModel(model: Slick.SelectionModel>) { + this._grid.setSelectionModel(model); + } + + focus(): void { + this._grid.focus(); + } + + setActiveCell(row: number, cell: number): void { + this._grid.setActiveCell(row, cell); + } + + get activeCell(): Slick.Cell { + return this._grid.getActiveCell(); + } + + registerPlugin(plugin: Slick.Plugin): void { + this._grid.registerPlugin(plugin); + } + + /** + * This function needs to be called if the table is drawn off dom. + */ + resizeCanvas() { + this._grid.resizeCanvas(); + } + + layout(dimension: Dimension): void + layout(size: number, orientation: Orientation): void + layout(sizing: number | Dimension, orientation?: Orientation): void { + if (sizing instanceof Dimension) { + this._container.style.width = sizing.width + 'px'; + this._container.style.height = sizing.height + 'px'; + this._tableContainer.style.width = sizing.width + 'px'; + this._tableContainer.style.height = sizing.height + 'px'; + } else { + if (orientation === Orientation.HORIZONTAL) { + this._container.style.width = '100%'; + this._container.style.height = sizing + 'px'; + this._tableContainer.style.width = '100%'; + this._tableContainer.style.height = sizing + 'px'; + } else { + this._container.style.width = sizing + 'px'; + this._container.style.height = '100%'; + this._tableContainer.style.width = sizing + 'px'; + this._tableContainer.style.height = '100%'; + } + } + this.resizeCanvas(); + } + + autosizeColumns() { + this._grid.autosizeColumns(); + } + + set autoScroll(active: boolean) { + this._autoscroll = active; + } + + style(styles: ITableStyles): void { + const content: string[] = []; + + if (styles.tableHeaderBackground) { + content.push(`.monaco-table .${this._idPrefix} .slick-header .slick-header-column { background-color: ${styles.tableHeaderBackground}; }`); + } + + if (styles.tableHeaderForeground) { + content.push(`.monaco-table .${this._idPrefix} .slick-header .slick-header-column { color: ${styles.tableHeaderForeground}; }`); + } + + if (styles.listFocusBackground) { + content.push(`.monaco-table .${this._idPrefix} .slick-row .focused { background-color: ${styles.listFocusBackground}; }`); + } + + if (styles.listFocusForeground) { + content.push(`.monaco-table .${this._idPrefix} .slick-row .focused { color: ${styles.listFocusForeground}; }`); + } + + if (styles.listActiveSelectionBackground) { + content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { background-color: ${styles.listActiveSelectionBackground}; }`); + content.push(`.monaco-table .${this._idPrefix} .slick-row .selected:hover { background-color: ${styles.listActiveSelectionBackground}; }`); // overwrite :hover style in this case! + } + + if (styles.listActiveSelectionForeground) { + content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { color: ${styles.listActiveSelectionForeground}; }`); + } + + if (styles.listFocusAndSelectionBackground) { + content.push(`.monaco-table .${this._idPrefix} .slick-row .selected.active { background-color: ${styles.listFocusAndSelectionBackground}; }`); + } + + if (styles.listFocusAndSelectionForeground) { + content.push(`.monaco-table .${this._idPrefix} .slick-row .selected.active { color: ${styles.listFocusAndSelectionForeground}; }`); + } + + /* Commented out andresse 8/17/2017; keeping for reference as we iterate on the table styling */ + // if (styles.listInactiveFocusBackground) { + // content.push(`.monaco-table .${this._idPrefix} .slick-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`); + // content.push(`.monaco-table .${this._idPrefix} .slick-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case! + // } + + // if (styles.listInactiveSelectionBackground) { + // content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { background-color: ${styles.listInactiveSelectionBackground}; }`); + // content.push(`.monaco-table .${this._idPrefix} .slick-row .selected:hover { background-color: ${styles.listInactiveSelectionBackground}; }`); // overwrite :hover style in this case! + // } + + // if (styles.listInactiveSelectionForeground) { + // content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { color: ${styles.listInactiveSelectionForeground}; }`); + // } + + if (styles.listHoverBackground) { + content.push(`.monaco-table .${this._idPrefix} .slick-row:hover { background-color: ${styles.listHoverBackground}; }`); + } + + if (styles.listHoverForeground) { + content.push(`.monaco-table .${this._idPrefix} .slick-row:hover { color: ${styles.listHoverForeground}; }`); + } + + if (styles.listSelectionOutline) { + content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { outline: 1px dotted ${styles.listSelectionOutline}; outline-offset: -1px; }`); + } + + /* Commented out andresse 8/17/2017; keeping for reference as we iterate on the table styling */ + // if (styles.listFocusOutline) { + // content.push(`.monaco-table .${this._idPrefix}:focus .slick-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`); + // } + + // if (styles.listInactiveFocusOutline) { + // content.push(`.monaco-table .${this._idPrefix} .slick-row.focused { outline: 1px dotted ${styles.listInactiveFocusOutline}; outline-offset: -1px; }`); + // } + + if (styles.listHoverOutline) { + content.push(`.monaco-table .${this._idPrefix} .slick-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`); + } + + this._styleElement.innerHTML = content.join('\n'); + } +} diff --git a/src/sql/base/browser/ui/table/tableDataView.ts b/src/sql/base/browser/ui/table/tableDataView.ts new file mode 100644 index 0000000000..c1b8398fe8 --- /dev/null +++ b/src/sql/base/browser/ui/table/tableDataView.ts @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Observable } from 'rxjs/Observable'; +import { Observer } from 'rxjs/Observer'; + +import Event, { Emitter } from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as types from 'vs/base/common/types'; + +export interface IFindPosition { + col: number; + row: number; +} + +function defaultSort(args: Slick.OnSortEventArgs, data: Array): Array { + let field = args.sortCol.field; + let sign = args.sortAsc ? 1 : -1; + return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign); +} + +export class TableDataView implements Slick.DataProvider { + private _data: Array; + private _findArray: Array; + private _findObs: Observable; + private _findIndex: number; + + private _onRowCountChange = new Emitter(); + get onRowCountChange(): Event { return this._onRowCountChange.event; } + + private _onFindCountChange = new Emitter(); + get onFindCountChange(): Event { return this._onFindCountChange.event; } + + constructor( + data?: Array, + private _findFn?: (val: T, exp: string) => Array, + private _sortFn?: (args: Slick.OnSortEventArgs, data: Array) => Array + ) { + if (data) { + this._data = data; + } else { + this._data = new Array(); + } + + if (!_sortFn) { + this._sortFn = defaultSort; + } + } + + sort(args: Slick.OnSortEventArgs) { + this._data = this._sortFn(args, this._data); + } + + getLength(): number { + return this._data.length; + } + + getItem(index: number): T { + return this._data[index]; + } + + push(items: Array); + push(item: T); + push(input: T | Array) { + if (Array.isArray(input)) { + this._data.push(...input); + } else { + this._data.push(input); + } + this._onRowCountChange.fire(); + } + + clear() { + this._data = new Array(); + this._onRowCountChange.fire(); + } + + find(exp: string): Thenable { + if (!this._findFn) { + return TPromise.wrapError(new Error('no find function provided')); + } + this._findArray = new Array(); + this._findIndex = 0; + this._onFindCountChange.fire(this._findArray.length); + if (exp) { + this._findObs = Observable.create((observer: Observer) => { + this._data.forEach((item, i) => { + let result = this._findFn(item, exp); + if (result) { + result.forEach(pos => { + let index = { col: pos, row: i }; + this._findArray.push(index); + observer.next(index); + this._onFindCountChange.fire(this._findArray.length); + }); + } + }); + }); + return this._findObs.take(1).toPromise().then(() => { + return this._findArray[this._findIndex]; + }); + } else { + return TPromise.wrapError(new Error('no expression')); + } + } + + clearFind() { + this._findArray = new Array(); + this._findIndex = 0; + this._findObs = undefined; + this._onFindCountChange.fire(this._findArray.length); + } + + findNext(): Thenable { + if (this._findArray && this._findArray.length !== 0) { + if (this._findIndex === this._findArray.length - 1) { + this._findIndex = 0; + } else { + ++this._findIndex; + } + return TPromise.as(this._findArray[this._findIndex]); + } else { + return TPromise.wrapError(new Error('no search running')); + } + } + + findPrevious(): Thenable { + if (this._findArray && this._findArray.length !== 0) { + if (this._findIndex === 0) { + this._findIndex = this._findArray.length - 1; + } else { + --this._findIndex; + } + return TPromise.as(this._findArray[this._findIndex]); + } else { + return TPromise.wrapError(new Error('no search running')); + } + } + + get currentFindPosition(): Thenable { + if (this._findArray && this._findArray.length !== 0) { + return TPromise.as(this._findArray[this._findIndex]); + } else { + return TPromise.wrapError(new Error('no search running')); + } + } + + /* 1 indexed */ + get findPosition(): number { + return types.isUndefinedOrNull(this._findIndex) ? 0 : this._findIndex + 1; + } + + get findCount(): number { + return types.isUndefinedOrNull(this._findArray) ? 0 : this._findArray.length; + } +} diff --git a/src/sql/base/browser/ui/table/tableView.ts b/src/sql/base/browser/ui/table/tableView.ts new file mode 100644 index 0000000000..e71b2f5749 --- /dev/null +++ b/src/sql/base/browser/ui/table/tableView.ts @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Table } from './table'; +import { TableDataView } from './tableDataView'; + +import { View, Orientation, AbstractCollapsibleView, HeaderView, IViewOptions, ICollapsibleViewOptions } from 'vs/base/browser/ui/splitview/splitview'; +import { $ } from 'vs/base/browser/builder'; + +export class TableBasicView extends View { + private _table: Table; + private _container: HTMLElement; + + constructor( + viewOpts: IViewOptions, + data?: Array | TableDataView, + columns?: Slick.Column[], + tableOpts?: Slick.GridOptions + ) { + super(undefined, viewOpts); + this._container = document.createElement('div'); + this._container.className = 'table-view'; + this._table = new Table(this._container, data, columns, tableOpts); + } + + public get table(): Table { + return this._table; + } + + render(container: HTMLElement, orientation: Orientation): void { + container.appendChild(this._container); + } + + focus(): void { + this._table.focus(); + } + + layout(size: number, orientation: Orientation): void { + this._table.layout(size, orientation); + } +} + +export class TableHeaderView extends HeaderView { + private _table: Table; + private _container: HTMLElement; + + constructor( + private _viewTitle: string, + viewOpts: IViewOptions, + data?: Array | TableDataView, + columns?: Slick.Column[], + tableOpts?: Slick.GridOptions + ) { + super(undefined, viewOpts); + this._container = document.createElement('div'); + this._container.className = 'table-view'; + this._table = new Table(this._container, data, columns, tableOpts); + } + + public get table(): Table { + return this._table; + } + + protected renderHeader(container: HTMLElement): void { + const titleDiv = $('div.title').appendTo(container); + $('span').text(this._viewTitle).appendTo(titleDiv); + } + + protected renderBody(container: HTMLElement): void { + container.appendChild(this._container); + } + + protected layoutBody(size: number): void { + this._table.layout(size, Orientation.HORIZONTAL); + } + + focus(): void { + this._table.focus(); + } +} + +export class TableCollapsibleView extends AbstractCollapsibleView { + private _table: Table; + private _container: HTMLElement; + + constructor( + private _viewTitle: string, + viewOpts: ICollapsibleViewOptions, + data?: Array | TableDataView, + columns?: Slick.Column[], + tableOpts?: Slick.GridOptions + ) { + super(undefined, viewOpts); + this._container = document.createElement('div'); + this._container.className = 'table-view'; + this._table = new Table(this._container, data, columns, tableOpts); + } + + public addContainerClass(className: string) { + this._container.classList.add(className); + } + + public get table(): Table { + return this._table; + } + + protected renderHeader(container: HTMLElement): void { + const titleDiv = $('div.title').appendTo(container); + $('span').text(this._viewTitle).appendTo(titleDiv); + } + + protected renderBody(container: HTMLElement): void { + container.appendChild(this._container); + } + + protected layoutBody(size: number): void { + this._table.layout(size, Orientation.HORIZONTAL); + } +} diff --git a/src/sql/base/browser/ui/taskbar/actionbar.ts b/src/sql/base/browser/ui/taskbar/actionbar.ts new file mode 100644 index 0000000000..396d6ead2c --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/actionbar.ts @@ -0,0 +1,381 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!vs/base/browser/ui/actionbar/actionbar'; + +import { Promise } from 'vs/base/common/winjs.base'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions'; +import { EventType as CommonEventType } from 'vs/base/common/events'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { + IActionBarOptions, ActionsOrientation, IActionItem, + IActionOptions, ActionItem, BaseActionItem +} from 'vs/base/browser/ui/actionbar/actionbar'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import * as DOM from 'vs/base/browser/dom'; +import * as types from 'vs/base/common/types'; + +let defaultOptions: IActionBarOptions = { + orientation: ActionsOrientation.HORIZONTAL, + context: null +}; + +/** + * Contains logic for displaying and handling callbacks for clickable icons. Based on + * ActionBar vs/base/browser/ui/actionbar/actionbar. This class was needed because we + * want the ability to display content other than Action icons in the QueryTaskbar. + */ +export class ActionBar extends EventEmitter implements IActionRunner { + + private _options: IActionBarOptions; + private _actionRunner: IActionRunner; + private _context: any; + + // Items + private _items: IActionItem[]; + private _focusedItem: number; + private _focusTracker: DOM.IFocusTracker; + private _toDispose: lifecycle.IDisposable[]; + + // Elements + private _domNode: HTMLElement; + private _actionsList: HTMLElement; + + constructor(container: HTMLElement | Builder, options: IActionBarOptions = defaultOptions) { + super(); + this._options = options; + this._context = options.context; + this._toDispose = []; + this._actionRunner = this._options.actionRunner; + + if (!this._actionRunner) { + this._actionRunner = new ActionRunner(); + this._toDispose.push(this._actionRunner); + } + + this._toDispose.push(this.addEmitter(this._actionRunner)); + + this._items = []; + this._focusedItem = undefined; + + this._domNode = document.createElement('div'); + this._domNode.className = 'monaco-action-bar'; + + if (options.animated !== false) { + DOM.addClass(this._domNode, 'animated'); + } + + let isVertical = this._options.orientation === ActionsOrientation.VERTICAL; + if (isVertical) { + this._domNode.className += ' vertical'; + } + + $(this._domNode).on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + let event = new StandardKeyboardEvent(e); + let eventHandled = true; + + if (event.equals(isVertical ? KeyCode.UpArrow : KeyCode.LeftArrow)) { + this.focusPrevious(); + } else if (event.equals(isVertical ? KeyCode.DownArrow : KeyCode.RightArrow)) { + this.focusNext(); + } else if (event.equals(KeyCode.Escape)) { + this.cancel(); + } else if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { + // Nothing, just staying out of the else branch + } else { + eventHandled = false; + } + + if (eventHandled) { + event.preventDefault(); + event.stopPropagation(); + } + }); + + // Prevent native context menu on actions + $(this._domNode).on(DOM.EventType.CONTEXT_MENU, (e: Event) => { + e.preventDefault(); + e.stopPropagation(); + }); + + $(this._domNode).on(DOM.EventType.KEY_UP, (e: KeyboardEvent) => { + let event = new StandardKeyboardEvent(e); + + // Run action on Enter/Space + if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { + this.doTrigger(event); + event.preventDefault(); + event.stopPropagation(); + } + + // Recompute focused item + else if (event.equals(KeyCode.Tab) || event.equals(KeyMod.Shift | KeyCode.Tab)) { + this.updateFocusedItem(); + } + }); + + this._focusTracker = DOM.trackFocus(this._domNode); + this._focusTracker.addBlurListener(() => { + if (document.activeElement === this._domNode || !DOM.isAncestor(document.activeElement, this._domNode)) { + this.emit(DOM.EventType.BLUR, {}); + this._focusedItem = undefined; + } + }); + + this._focusTracker.addFocusListener(() => this.updateFocusedItem()); + + this._actionsList = document.createElement('ul'); + this._actionsList.className = 'actions-container'; + this._actionsList.setAttribute('role', 'toolbar'); + if (this._options.ariaLabel) { + this._actionsList.setAttribute('aria-label', this._options.ariaLabel); + } + + this._domNode.appendChild(this._actionsList); + + ((container instanceof Builder) ? container.getHTMLElement() : container).appendChild(this._domNode); + } + + public setAriaLabel(label: string): void { + if (label) { + this._actionsList.setAttribute('aria-label', label); + } else { + this._actionsList.removeAttribute('aria-label'); + } + } + + private updateFocusedItem(): void { + for (let i = 0; i < this._actionsList.children.length; i++) { + let elem = this._actionsList.children[i]; + if (DOM.isAncestor(document.activeElement, elem)) { + this._focusedItem = i; + break; + } + } + } + + public get context(): any { + return this._context; + } + + public set context(context: any) { + this._context = context; + this._items.forEach(i => i.setActionContext(context)); + } + + public get actionRunner(): IActionRunner { + return this._actionRunner; + } + + public set actionRunner(actionRunner: IActionRunner) { + if (actionRunner) { + this._actionRunner = actionRunner; + this._items.forEach(item => item.actionRunner = actionRunner); + } + } + + public getContainer(): Builder { + return $(this._domNode); + } + + /** + * Push an HTML Element onto the action bar UI in the position specified by options. + * Pushes to the last position if no options are provided. + */ + public pushElement(element: HTMLElement, options: IActionOptions = {}): void { + let index = types.isNumber(options.index) ? options.index : null; + + if (index === null || index < 0 || index >= this._actionsList.children.length) { + this._actionsList.appendChild(element); + } else { + this._actionsList.insertBefore(element, this._actionsList.children[index++]); + } + } + + /** + * Push an action onto the action bar UI in the position specified by options. + * Pushes to the last position if no options are provided. + */ + public pushAction(arg: IAction | IAction[], options: IActionOptions = {}): void { + + const actions: IAction[] = !Array.isArray(arg) ? [arg] : arg; + + let index = types.isNumber(options.index) ? options.index : null; + + actions.forEach((action: IAction) => { + const actionItemElement = document.createElement('li'); + actionItemElement.className = 'action-item'; + actionItemElement.setAttribute('role', 'presentation'); + + let item: IActionItem = null; + + if (this._options.actionItemProvider) { + item = this._options.actionItemProvider(action); + } + + if (!item) { + item = new ActionItem(this.context, action, options); + } + + item.actionRunner = this._actionRunner; + item.setActionContext(this.context); + this.addEmitter(item); + item.render(actionItemElement); + + if (index === null || index < 0 || index >= this._actionsList.children.length) { + this._actionsList.appendChild(actionItemElement); + } else { + this._actionsList.insertBefore(actionItemElement, this._actionsList.children[index++]); + } + + this._items.push(item); + }); + } + + public pull(index: number): void { + if (index >= 0 && index < this._items.length) { + this._items.splice(index, 1); + this._actionsList.removeChild(this._actionsList.childNodes[index]); + } + } + + public clear(): void { + // Do not dispose action items if they were provided from outside + this._items = this._options.actionItemProvider ? [] : lifecycle.dispose(this._items); + $(this._actionsList).empty(); + } + + public length(): number { + return this._items.length; + } + + public isEmpty(): boolean { + return this._items.length === 0; + } + + public focus(selectFirst?: boolean): void { + if (selectFirst && typeof this._focusedItem === 'undefined') { + this._focusedItem = 0; + } + + this.updateFocus(); + } + + private focusNext(): void { + if (typeof this._focusedItem === 'undefined') { + this._focusedItem = this._items.length - 1; + } + + let startIndex = this._focusedItem; + let item: IActionItem; + + do { + this._focusedItem = (this._focusedItem + 1) % this._items.length; + item = this._items[this._focusedItem]; + } while (this._focusedItem !== startIndex && !item.isEnabled()); + + if (this._focusedItem === startIndex && !item.isEnabled()) { + this._focusedItem = undefined; + } + + this.updateFocus(); + } + + private focusPrevious(): void { + if (typeof this._focusedItem === 'undefined') { + this._focusedItem = 0; + } + + let startIndex = this._focusedItem; + let item: IActionItem; + + do { + this._focusedItem = this._focusedItem - 1; + + if (this._focusedItem < 0) { + this._focusedItem = this._items.length - 1; + } + + item = this._items[this._focusedItem]; + } while (this._focusedItem !== startIndex && !item.isEnabled()); + + if (this._focusedItem === startIndex && !item.isEnabled()) { + this._focusedItem = undefined; + } + + this.updateFocus(); + } + + private updateFocus(): void { + if (typeof this._focusedItem === 'undefined') { + this._domNode.focus(); + return; + } + + for (let i = 0; i < this._items.length; i++) { + let item = this._items[i]; + + let actionItem = item; + + if (i === this._focusedItem) { + if (types.isFunction(actionItem.focus)) { + actionItem.focus(); + } + } else { + if (types.isFunction(actionItem.blur)) { + actionItem.blur(); + } + } + } + } + + private doTrigger(event: StandardKeyboardEvent): void { + if (typeof this._focusedItem === 'undefined') { + return; //nothing to focus + } + + // trigger action + let actionItem = this._items[this._focusedItem]; + if (actionItem instanceof BaseActionItem) { + const context = (actionItem._context === null || actionItem._context === undefined) ? event : actionItem._context; + this.run(actionItem._action, context).done(); + } + } + + private cancel(): void { + if (document.activeElement instanceof HTMLElement) { + (document.activeElement).blur(); // remove focus from focussed action + } + + this.emit(CommonEventType.CANCEL); + } + + public run(action: IAction, context?: any): Promise { + return this._actionRunner.run(action, context); + } + + public dispose(): void { + if (this._items !== null) { + lifecycle.dispose(this._items); + } + this._items = null; + + if (this._focusTracker) { + this._focusTracker.dispose(); + this._focusTracker = null; + } + + this._toDispose = lifecycle.dispose(this._toDispose); + + this.getContainer().destroy(); + + super.dispose(); + } +} diff --git a/src/sql/base/browser/ui/taskbar/media/change_connection.svg b/src/sql/base/browser/ui/taskbar/media/change_connection.svg new file mode 100644 index 0000000000..59d5195b3a --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/change_connection.svg @@ -0,0 +1 @@ +change_connection \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/change_connection_inverse.svg b/src/sql/base/browser/ui/taskbar/media/change_connection_inverse.svg new file mode 100644 index 0000000000..1d59e281f2 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/change_connection_inverse.svg @@ -0,0 +1 @@ +change_connection_inverse \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/connect.svg b/src/sql/base/browser/ui/taskbar/media/connect.svg new file mode 100644 index 0000000000..948d4f0fd8 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/connect.svg @@ -0,0 +1 @@ +connect \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/connect_inverse.svg b/src/sql/base/browser/ui/taskbar/media/connect_inverse.svg new file mode 100644 index 0000000000..29a6ecc23e --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/connect_inverse.svg @@ -0,0 +1 @@ +connect_inverse \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/copy_image.svg b/src/sql/base/browser/ui/taskbar/media/copy_image.svg new file mode 100644 index 0000000000..2488c1e9d4 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/copy_image.svg @@ -0,0 +1 @@ +copy_image_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/copy_image_inverse.svg b/src/sql/base/browser/ui/taskbar/media/copy_image_inverse.svg new file mode 100644 index 0000000000..944903b8bf --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/copy_image_inverse.svg @@ -0,0 +1 @@ +copy_image_inverse_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/create_insight.svg b/src/sql/base/browser/ui/taskbar/media/create_insight.svg new file mode 100644 index 0000000000..c21117f2e9 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/create_insight.svg @@ -0,0 +1 @@ +create_dashboard_insight_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/create_insight_inverse.svg b/src/sql/base/browser/ui/taskbar/media/create_insight_inverse.svg new file mode 100644 index 0000000000..b837fb735f --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/create_insight_inverse.svg @@ -0,0 +1 @@ +create_dashboard_insight_inverse_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/disconnect.svg b/src/sql/base/browser/ui/taskbar/media/disconnect.svg new file mode 100644 index 0000000000..eb421db85b --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/disconnect.svg @@ -0,0 +1 @@ +disconnect \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/disconnect_inverse.svg b/src/sql/base/browser/ui/taskbar/media/disconnect_inverse.svg new file mode 100644 index 0000000000..ca6bb34ad9 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/disconnect_inverse.svg @@ -0,0 +1 @@ +disconnect_inverse \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/ellipsis-inverse.svg b/src/sql/base/browser/ui/taskbar/media/ellipsis-inverse.svg new file mode 100644 index 0000000000..1140c11ecc --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/ellipsis-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/ellipsis.svg b/src/sql/base/browser/ui/taskbar/media/ellipsis.svg new file mode 100644 index 0000000000..b61e2d0c75 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/ellipsis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/icons.css b/src/sql/base/browser/ui/taskbar/media/icons.css new file mode 100644 index 0000000000..fe161a4056 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/icons.css @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.vs .monaco-toolbar .action-label.toolbar-toggle-more { + background-image: url('ellipsis.svg'); +} + +.hc-black .monaco-toolbar .action-label.toolbar-toggle-more, +.vs-dark .monaco-toolbar .action-label.toolbar-toggle-more { + background-image: url('ellipsis-inverse.svg'); +} + +.vs .icon.start, +.vs-dark .icon.start, +.hc-black .icon.start { + background-image: url('start.svg'); +} + +.vs .icon.stop, +.vs-dark .icon.stop, +.hc-black .icon.stop { + background-image: url('stop.svg'); +} + +.vs .icon.disconnect { + background-image: url('disconnect.svg'); +} + +.vs-dark .icon.disconnect, +.hc-black .icon.disconnect { + background-image: url('disconnect_inverse.svg'); + background-repeat: no-repeat; +} + +.vs .icon.connect { + background-image: url('connect.svg'); +} + +.vs-dark .icon.connect, +.hc-black .icon.connect { + background-image: url('connect_inverse.svg'); + background-repeat: no-repeat; +} + +.vs .icon.changeConnection { + background-image: url('change_connection.svg'); +} + +.vs-dark .icon.changeConnection, +.hc-black .icon.changeConnection { + background-image: url('change_connection_inverse.svg'); + background-repeat: no-repeat; +} + +.vs .icon.estimatedQueryPlan { + background-image: url('query-plan.svg'); +} + +.vs-dark .icon.estimatedQueryPlan, +.hc-black .icon.estimatedQueryPlan { + background-image: url('query-plan-inverse.svg'); +} + +.vs .icon.createInsight { + background-image: url('create_insight.svg'); +} + +.vs-dark .icon.createInsight, +.hc-black .icon.createInsight { + background-image: url('create_insight_inverse.svg'); +} + +.vs .icon.copyImage { + background-image: url('copy_image.svg'); +} + +.vs-dark .icon.copyImage, +.hc-black .icon.copyImage { + background-image: url('copy_image_inverse.svg'); +} + +.vs .icon.saveAsImage { + background-image: url('save_as_image.svg'); +} + +.vs-dark .icon.saveAsImage, +.hc-black .icon.saveAsImage { + background-image: url('save_as_image_inverse.svg'); +} diff --git a/src/sql/base/browser/ui/taskbar/media/query-plan-inverse.svg b/src/sql/base/browser/ui/taskbar/media/query-plan-inverse.svg new file mode 100644 index 0000000000..1c7c0724d8 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/query-plan-inverse.svg @@ -0,0 +1 @@ +query_plan_inverse_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/query-plan.svg b/src/sql/base/browser/ui/taskbar/media/query-plan.svg new file mode 100644 index 0000000000..d1c246bf81 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/query-plan.svg @@ -0,0 +1 @@ +query_plan_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/save_as_image.svg b/src/sql/base/browser/ui/taskbar/media/save_as_image.svg new file mode 100644 index 0000000000..c11296ab00 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/save_as_image.svg @@ -0,0 +1 @@ +save_as_image_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/save_as_image_inverse.svg b/src/sql/base/browser/ui/taskbar/media/save_as_image_inverse.svg new file mode 100644 index 0000000000..e1777e3c4c --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/save_as_image_inverse.svg @@ -0,0 +1 @@ +save_as_image_inverse_16x16 \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/start.svg b/src/sql/base/browser/ui/taskbar/media/start.svg new file mode 100644 index 0000000000..2ceb9e2292 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/start.svg @@ -0,0 +1 @@ +run \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/stop.svg b/src/sql/base/browser/ui/taskbar/media/stop.svg new file mode 100644 index 0000000000..b3ac86d210 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/stop.svg @@ -0,0 +1 @@ +stop \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/taskbar.css b/src/sql/base/browser/ui/taskbar/media/taskbar.css new file mode 100644 index 0000000000..c257a2b065 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/taskbar.css @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* CSS Properties ported from taskbar.css */ + +.monaco-toolbar .dropdown > .dropdown-label:not(:empty):not(.tick) { + padding: 0; +} + +.monaco-toolbar .toolbar-toggle-more { + display: inline-block; + padding: 0; +} + +/* Taskbar */ + +.carbon-taskbar { + width: 100%; + position: relative; +} + +.carbon-taskbar.monaco-toolbar .monaco-action-bar.animated .actions-container { + justify-content: flex-start; + padding-left: 15px; + flex-wrap: wrap; +} + +.taskbar-progress { + padding-left: 5px; + padding-top: 5px; + width: 15px; + height: 15px; +} + +.carbon-taskbar .monaco-action-bar li { + display: flex; + align-items: center; +} + +.carbon-taskbar { + margin-top: 2px; + margin-bottom: 2px; +} + +.carbon-taskbar .action-item { + margin-right: 5px; +} + +.carbon-taskbar .action-label { + background-repeat: no-repeat; + background-position: 0% 50%; + padding-left: 15px; + } + +.listDatabasesSelectBox { + padding-left: 2px; + min-width: 100px; +} + +.carbon-taskbar.monaco-toolbar .monaco-action-bar.animated .actions-container .action-item +.configuration.select-container .select-box { + margin-top: 4px; + margin-top: 3px; + } + +.taskbarSeparator { + width: 1px; + background-color:#555; + margin-right: 6px; + margin-top: 3px; +} + +.taskbarTextSeparator { + margin-left: 3px; + margin-right: 3px; + margin-top: 4px; +} + +/* Taskbar Icons */ + +.carbon-taskbar .icon { + background-size: 11px; +} diff --git a/src/sql/base/browser/ui/taskbar/taskbar.ts b/src/sql/base/browser/ui/taskbar/taskbar.ts new file mode 100644 index 0000000000..6e80ef38bd --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/taskbar.ts @@ -0,0 +1,141 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/taskbar'; +import 'vs/css!./media/icons'; +import 'vs/css!sql/media/icons/common-icons'; + +import { ActionBar } from './actionbar'; + +import { Builder, $ } from 'vs/base/browser/builder'; +import { Action, IActionRunner, IAction } from 'vs/base/common/actions'; +import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IContextMenuProvider } from 'vs/base/browser/ui/dropdown/dropdown'; +import { IToolBarOptions } from 'vs/base/browser/ui/toolbar/toolbar'; + +/** + * A wrapper for the different types of content a QueryTaskbar can display + */ +export interface ITaskbarContent { + + // Display the element created by this IAction + action?: IAction; + + // Display a pre-created element + element?: HTMLElement; +} + +/** + * A widget that combines an action bar for actions. This class was needed because we + * want the ability to use the custom QueryActionBar in order to display other HTML + * in our taskbar. Based off import ToolBar from vs/base/browser/ui/toolbar/toolbar. + */ +export class Taskbar { + private options: IToolBarOptions; + private actionBar: ActionBar; + private lookupKeybindings: boolean; + + constructor(container: HTMLElement, contextMenuProvider: IContextMenuProvider, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) { + this.options = options; + this.lookupKeybindings = typeof this.options.getKeyBinding === 'function' && typeof this.options.getKeyBinding === 'function'; + + let element = document.createElement('div'); + element.className = 'monaco-toolbar carbon-taskbar'; + container.appendChild(element); + + this.actionBar = new ActionBar($(element), { + orientation: options.orientation, + ariaLabel: options.ariaLabel, + actionItemProvider: (action: Action) => { + return options.actionItemProvider ? options.actionItemProvider(action) : null; + } + }); + } + + /** + * Creates an HTML vertical separator. + */ + public static createTaskbarSeparator(): HTMLElement { + let element = document.createElement('div'); + element.className = 'taskbarSeparator'; + element.innerHTML = ' '; + return element; + } + + /** + * Creates an HTML spinner. + */ + public static createTaskbarSpinner(): HTMLElement { + let spinnerContainer = document.createElement('div'); + spinnerContainer.className = 'taskbar-progress icon in-progress '; + spinnerContainer.style.visibility = 'hidden'; + return spinnerContainer; + } + + /** + * Creates an HTML text separator. + */ + public static createTaskbarText(inputText: string): HTMLElement { + let element = document.createElement('div'); + element.className = 'taskbarTextSeparator'; + element.innerHTML = inputText; + return element; + } + + public set actionRunner(actionRunner: IActionRunner) { + this.actionBar.actionRunner = actionRunner; + } + + public get actionRunner(): IActionRunner { + return this.actionBar.actionRunner; + } + + public set context(context: any) { + this.actionBar.context = context; + } + + public getContainer(): Builder { + return this.actionBar.getContainer(); + } + + public setAriaLabel(label: string): void { + this.actionBar.setAriaLabel(label); + } + + /** + * Push HTMLElements and icons for IActions into the ActionBar UI. Push IActions into ActionBar's private collection. + */ + public setContent(content: ITaskbarContent[]): void { + let contentToSet: ITaskbarContent[] = content ? content.slice(0) : []; + this.actionBar.clear(); + + for (let item of contentToSet) { + if (item.action) { + this.actionBar.pushAction(item.action, { icon: true, label: true, keybinding: this.getKeybindingLabel(item.action) }); + } else if (item.element) { + this.actionBar.pushElement(item.element); + } + } + } + + private getKeybindingLabel(action: IAction): string { + const key = this.lookupKeybindings ? this.options.getKeyBinding(action) : void 0; + return key ? key.getLabel() : ''; + } + + public addAction(primaryAction: IAction): void { + this.actionBar.pushAction(primaryAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(primaryAction) }); + } + + public addElement(element: HTMLElement): void { + this.actionBar.pushElement(element); + } + + public dispose(): void { + this.actionBar.dispose(); + } +} diff --git a/src/sql/base/common/async.ts b/src/sql/base/common/async.ts new file mode 100644 index 0000000000..cd6df4508c --- /dev/null +++ b/src/sql/base/common/async.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ITask } from 'vs/base/common/async'; +import { Promise, TPromise, ValueCallback } from 'vs/base/common/winjs.base'; + +/** + * Bases on vscode Delayer, however, it works by only running the task if it gets + * a specified number of requests in a specified time + * + * Ex. Useful encapsulation of handling double click from click listeners + */ +export class MultipleRequestDelayer { + + private timeout: number; + private completionPromise: Promise; + private onSuccess: ValueCallback; + private requests: number = 0; + private task: ITask; + + constructor(public delay: number, private maxRequests: number = 2) { + this.timeout = null; + this.completionPromise = null; + this.onSuccess = null; + } + + trigger(task: ITask): TPromise { + this.cancelTimeout(); + this.task = task; + + if (++this.requests > this.maxRequests - 1) { + this.requests = 0; + this.onSuccess(null); + return this.completionPromise; + } + + if (!this.completionPromise) { + this.completionPromise = new TPromise((c) => { + this.onSuccess = c; + }, () => { + // no-op + }).then(() => { + this.completionPromise = null; + this.onSuccess = null; + const task = this.task; + this.task = null; + + return task(); + }); + } + + this.timeout = setTimeout(() => { + this.timeout = null; + this.requests = 0; + }, this.delay); + + return this.completionPromise; + } + + isTriggered(): boolean { + return this.timeout !== null; + } + + cancel(): void { + this.cancelTimeout(); + + if (this.completionPromise) { + this.completionPromise.cancel(); + this.completionPromise = null; + } + } + + private cancelTimeout(): void { + if (this.timeout !== null) { + clearTimeout(this.timeout); + this.timeout = null; + } + } +} \ No newline at end of file diff --git a/src/sql/base/common/decorators.ts b/src/sql/base/common/decorators.ts new file mode 100644 index 0000000000..c47e68bf84 --- /dev/null +++ b/src/sql/base/common/decorators.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +/** + * Alterable version of the vs memorize function; to unmemoize use unmemoize +*/ +export function memoize(target: any, key: string, descriptor: any) { + let fnKey: string = null; + let fn: Function = null; + + if (typeof descriptor.value === 'function') { + fnKey = 'value'; + fn = descriptor.value; + } else if (typeof descriptor.get === 'function') { + fnKey = 'get'; + fn = descriptor.get; + } + + if (!fn) { + throw new Error('not supported'); + } + + const memoizeKey = `$memoize$${key}`; + + descriptor[fnKey] = function (...args: any[]) { + if (!this.hasOwnProperty(memoizeKey)) { + Object.defineProperty(this, memoizeKey, { + configurable: true, + enumerable: false, + writable: false, + value: fn.apply(this, args) + }); + } + + return this[memoizeKey]; + }; +} + +export function unmemoize(target: Object, key: string) { + const memoizeKey = `$memoize$${key}`; + if (target.hasOwnProperty(memoizeKey)) { + delete target[memoizeKey]; + } +} \ No newline at end of file diff --git a/src/sql/base/common/log.ts b/src/sql/base/common/log.ts new file mode 100644 index 0000000000..b5537cafba --- /dev/null +++ b/src/sql/base/common/log.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export function log(...args: any[]): void { + console.log(`\x1b[90m[main ${new Date().toLocaleTimeString()}]\x1b[0m`, ...args); +} + +export function warn(...args: any[]): void { + console.warn(`\x1b[93m[main ${new Date().toLocaleTimeString()}]\x1b[0m`, ...args); +} + +export function error(...args: any[]) { + console.error(`\x1b[91m[main ${new Date().toLocaleTimeString()}]\x1b[0m`, ...args); +} diff --git a/src/sql/base/common/objects.ts b/src/sql/base/common/objects.ts new file mode 100644 index 0000000000..60f873ef8f --- /dev/null +++ b/src/sql/base/common/objects.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +import * as Types from 'vs/base/common/types'; + +/** + * A copy of the vs mixin that accepts a custom behavior function + */ +export function mixin(destination: any, source: any, overwrite: boolean = true, fn?: (destination: any, source: any, overwrite?: boolean) => any): any { + if (!Types.isObject(destination)) { + return source; + } + + if (Types.isObject(source)) { + Object.keys(source).forEach((key) => { + if (key in destination) { + if (overwrite) { + if (Types.isObject(destination[key]) && Types.isObject(source[key])) { + mixin(destination[key], source[key], overwrite, fn); + } else if(fn) { + fn(destination[key], source[key], overwrite); + } else { + destination[key] = source[key]; + } + } + } else { + destination[key] = source[key]; + } + }); + } + return destination; +} \ No newline at end of file diff --git a/src/sql/base/common/promise.ts b/src/sql/base/common/promise.ts new file mode 100644 index 0000000000..dbad3a307d --- /dev/null +++ b/src/sql/base/common/promise.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +/** + * Deferred promise + */ +export class Deferred { + promise: Promise; + resolve: (value?: T | PromiseLike) => void; + reject: (reason?: any) => void; + constructor() { + this.promise = new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + }); + } +} \ No newline at end of file diff --git a/src/sql/base/node/rangy.ts b/src/sql/base/node/rangy.ts new file mode 100644 index 0000000000..9f7a144602 --- /dev/null +++ b/src/sql/base/node/rangy.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export * from 'rangy'; + +// add the text range to rangy +import 'rangy/lib/rangy-textrange'; \ No newline at end of file diff --git a/src/sql/code/electron-main/oauth.ts b/src/sql/code/electron-main/oauth.ts new file mode 100644 index 0000000000..0eeb5c6dad --- /dev/null +++ b/src/sql/code/electron-main/oauth.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as electron from 'electron'; +import * as urlLib from 'url'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { warn } from 'sql/base/common/log'; + +export class ProxyOAuthHandler { + _serviceBrand: any; + + private disposables: IDisposable[] = []; + + constructor( @IWindowsMainService private windowsService: IWindowsMainService) { + let self = this; + electron.ipcMain.on('oauth', (event, args) => { + self.onOAuthRequest(event, args); + }); + } + + private static sendOAuthResponse(event, eventId: string, error: any, code: string) { + let args = { + eventId: eventId, + error: error, + code: code + }; + event.sender.send('oauth-reply', args); + } + + private onOAuthRequest(event, args) { + // Verify the arguments are correct + if (!args || args['eventId'] === undefined) { + warn('Received OAuth request with invalid arguments'); + return; + } + let eventId: string = args['eventId']; + let url: string = args['url']; + let silent: boolean = args['silent']; + + let windowConfig = { + show: !silent, + width: 568, + height: 720, + resizable: false, + center: false, + alwaysOnTop: true, + autoHideMenuBar: true, + webPreferences: { + nodeIntegration: false + } + }; + + let authWindow = new electron.BrowserWindow(windowConfig); + + // TODO: Determine if we need to do the fancy logic that devdiv did to get around buggy AAD calls + + // Define a function that will parse the redirect URI + // NOTE: This is defined like this b/c we need access to a lot of scope + function onCallback(webEvent, url: string) { + let query: any = urlLib.parse(url, true).query; + let code: string = query.code; + let error: string = query.error; + + if (error !== undefined) { + // We received an error + ProxyOAuthHandler.sendOAuthResponse(event, eventId, error, null); + } else if (code) { + // We received a successful authorization code + ProxyOAuthHandler.sendOAuthResponse(event, eventId, null, code); + } else { + // We didn't get a code or error, so let this redirect continue on + return; + } + + // We got an error or a code, so we should close the window without redirecting + authWindow.removeAllListeners('closed'); + authWindow.close(); + webEvent.preventDefault(); + } + + // Remove all 'will-navigate' events since VS Code prevents navigation by default + authWindow.webContents.removeAllListeners('will-navigate'); + + // Setup event handlers + // closed -> user closed the window + // will-navigate, did-get-redirect-request -> OAuth redirected back to the redirect URL + authWindow.on('closed', () => { ProxyOAuthHandler.sendOAuthResponse(event, eventId, 'User cancelled authentication', null); }); + authWindow.webContents.on('will-navigate', (event, url) => { onCallback(event, url); }); + authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => { onCallback(event, newUrl); }); + + // Load the URL + authWindow.loadURL(url); + } + + public dispose(): void { + this.disposables = dispose(this.disposables); + } +} diff --git a/src/sql/common/browser/sqlOAuthServiceImpl.ts b/src/sql/common/browser/sqlOAuthServiceImpl.ts new file mode 100644 index 0000000000..827987e919 --- /dev/null +++ b/src/sql/common/browser/sqlOAuthServiceImpl.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as electron from 'electron'; +import { ISqlOAuthService } from 'sql/common/sqlOAuthService'; + +/** + * Implements simple electron based utilities for registering and sending IPC to the main thread to + * handle OAuth requests. This is to avoid breaking layering issues caused when certain + * electron-based services are referenced in unit testable code. + */ +export class SqlOAuthService implements ISqlOAuthService { + public _serviceBrand: any; + + /** + * Sends request to main thread to handle OAuth request + * @param {string} eventId Unique ID of the request to return upon completion + * @param {string} url URL to load to do OAuth + * @param {boolean} silent Whether or not to show the OAuth window + * @return {Thenable} Promise to return an authorization code + */ + performOAuthAuthorization(eventId: string, url: string, silent: boolean): void { + // Setup the args and send the IPC call + electron.ipcRenderer.send( + 'oauth', + { + url: url, + silent: silent, + eventId: eventId + } + ); + } + + /** + * Registers a handler for the oauth-reply event on the IPC channel + * @param {(event, args) => void} handler Handler to call when the event is triggered + */ + registerOAuthCallback(handler: (event, args) => void): void { + electron.ipcRenderer.on('oauth-reply', handler); + } +} diff --git a/src/sql/common/constants.ts b/src/sql/common/constants.ts new file mode 100644 index 0000000000..c0d40ee7e5 --- /dev/null +++ b/src/sql/common/constants.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// localizable strings + +export const InvalidProvider = 'Provider is invalid'; +export const SerializationDisabled = 'Saving results into different format disabled for this data provider.'; + +/** + * Feature names + */ +export const RestoreFeatureName = 'restore'; +export const BackupFeatureName = 'backup'; \ No newline at end of file diff --git a/src/sql/common/pathUtilities.ts b/src/sql/common/pathUtilities.ts new file mode 100644 index 0000000000..1713b73ad3 --- /dev/null +++ b/src/sql/common/pathUtilities.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import * as os from 'os'; + +import URI from 'vs/base/common/uri'; +import { UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +export const FILE_SCHEMA: string = 'file'; + +export function resolveCurrentDirectory(uri: string, rootPath: string): string { + let sqlUri = URI.parse(uri); + let currentDirectory: string; + + // use current directory of the sql file if sql file is saved + if (sqlUri.scheme === FILE_SCHEMA) { + currentDirectory = path.dirname(sqlUri.fsPath); + } else if (sqlUri.scheme === UNTITLED_SCHEMA) { + // if sql file is unsaved/untitled but a workspace is open use workspace root + let root = rootPath; + if (root) { + currentDirectory = root; + } else { + // use temp directory + currentDirectory = os.tmpdir(); + } + } else { + currentDirectory = path.dirname(sqlUri.path); + } + return currentDirectory; +} + +export function resolveFilePath(uri: string, filePath: string, rootPath: string): string { + let currentDirectory = resolveCurrentDirectory(uri, rootPath); + return path.normalize(path.join(currentDirectory, filePath)); +} + +export function getRootPath(contextService: IWorkspaceContextService): string { + return contextService.hasWorkspace() && contextService.getWorkspace().roots[0] + ? contextService.getWorkspace().roots[0].fsPath : undefined; +} diff --git a/src/sql/common/sqlOAuthService.ts b/src/sql/common/sqlOAuthService.ts new file mode 100644 index 0000000000..e39290429f --- /dev/null +++ b/src/sql/common/sqlOAuthService.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ISqlOAuthService = createDecorator('sqlOAuthService'); + +/** + * Defines simple electron based utilities for registering and sending IPC to the main thread to + * handle OAuth requests. This is to avoid breaking layering issues caused when certain + * electron-based services are referenced in unit testable code. + */ +export interface ISqlOAuthService { + _serviceBrand: any; + + /** + * Sends request to main thread to handle OAuth request + * @param {string} eventId Unique ID of the request to return upon completion + * @param {string} url URL to load to do OAuth + * @param {boolean} silent Whether or not to show the OAuth window + * @return {Thenable} Promise to return an authorization code + */ + performOAuthAuthorization(eventId: string, url: string, silent: boolean): void; + + /** + * Registers a handler for the oauth-reply event on the IPC channel + * @param {(event, args) => void} handler Handler to call when the event is triggered + */ + registerOAuthCallback(handler: (event, args) => void): void; +} diff --git a/src/sql/common/telemetryKeys.ts b/src/sql/common/telemetryKeys.ts new file mode 100644 index 0000000000..6dc8aaddcd --- /dev/null +++ b/src/sql/common/telemetryKeys.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. + *--------------------------------------------------------------------------------------------*/ + +// Telemetry Event Names + +export const DatabaseConnected = 'DatabaseConnected'; +export const DatabaseDisconnected = 'DatabaseDisconnected'; +export const DeleteConnection = 'DeleteConnection'; +export const AddServerGroup = 'AddServerGroup'; +export const MoveServerGroup = 'MoveServerGroup'; +export const MoveServerConnection = 'MoveServerConnection'; +export const DeleteServerGroup = 'DeleteServerGroup'; +export const ModalDialogClosed = 'ModalDialogClosed'; +export const ModalDialogOpened = 'ModalDialogOpened'; +export const BackupCreated = 'BackupCreated'; +export const RestoreRequested = 'RestoreRequested'; +export const ChartCreated = 'ChartCreated'; +export const ObjectExplorerExpand = 'ObjectExplorerExpand'; +export const RunQuery = 'RunQuery'; +export const RunQueryStatement = 'RunQueryStatement'; +export const CancelQuery = 'CancelQuery'; +export const NewQuery = 'NewQuery'; +export const FirewallRuleRequested = 'FirewallRuleCreated'; + + +// Telemetry Properties + +// Modal Dialogs: +export const ErrorMessage = 'ErrorMessage'; +export const ConnectionAdvancedProperties = 'ConnectionAdvancedProperties'; +export const Connection = 'Connection'; +export const Backup = 'Backup'; +export const Restore = 'Restore'; +export const Insights = 'Insights'; +export const Profiler = 'Profiler'; +export const ServerGroups = 'ServerGroups'; +export const Accounts = 'Accounts'; +export const FireWallRule = 'FirewallRule'; diff --git a/src/sql/common/telemetryUtilities.ts b/src/sql/common/telemetryUtilities.ts new file mode 100644 index 0000000000..44b5409489 --- /dev/null +++ b/src/sql/common/telemetryUtilities.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as crypto from 'crypto'; +import * as os from 'os'; +import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { warn } from 'sql/base/common/log'; + +import { generateUuid } from 'vs/base/common/uuid'; + +// Generate a unique, deterministic ID for the current user of the extension +export function generateUserId(): Promise { + return new Promise(resolve => { + try { + getmac.getMac((error, macAddress) => { + if (!error) { + resolve(crypto.createHash('sha256').update(macAddress + os.homedir(), 'utf8').digest('hex')); + } else { + resolve(generateUuid()); // fallback + } + }); + } catch (err) { + resolve(generateUuid()); // fallback + } + }); +} + +export interface IConnectionTelemetryData extends ITelemetryData { + provider?: string; +} + +/** + * Call the given telemetry service to log the telemetry event. + * If the provider is not in the data, tries to get it from connection inside the data. + * The connection in the data won't be included in the telemetry data + * Note: userId is added to all telemetry events so no need to add it here + * @param telemetryService Telemetry Service + * @param telemetryEventName Telemetry event name + * @param data Telemetry data + */ +export function addTelemetry( + telemetryService: ITelemetryService, + telemetryEventName: string, + data?: IConnectionTelemetryData, + connection?: IConnectionProfile): Promise { + return new Promise(resolve => { + try { + let telData: ITelemetryData = data === undefined ? {} : data; + + if (telData && telData.provider === undefined) { + + let provider: string = ''; + if (connection) { + provider = connection.providerName; + } + telData.provider = provider; + } + delete telData['connection']; + if (telemetryService) { + telemetryService.publicLog(telemetryEventName, telData).then(() => { + resolve(); + }, telemetryServiceError => { + warn(`Failed to add telemetry. error: ${telemetryServiceError}`); + resolve(); + }); + } else { + resolve(); + } + } catch (error) { + warn(`Failed to add telemetry. error: ${error}`); + resolve(); + } + }); +} \ No newline at end of file diff --git a/src/sql/common/theme/colors.ts b/src/sql/common/theme/colors.ts new file mode 100644 index 0000000000..bd1e905766 --- /dev/null +++ b/src/sql/common/theme/colors.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerColor, foreground } from 'vs/platform/theme/common/colorRegistry'; +import { Color, RGBA } from 'vs/base/common/color'; +import * as nls from 'vs/nls'; + +export const tableHeaderBackground = registerColor('table.headerBackground', { dark: new Color(new RGBA(51, 51, 52)), light: new Color(new RGBA(245, 245, 245)), hc: null }, nls.localize('tableHeaderBackground', 'Table header background color')); +export const tableHeaderForeground = registerColor('table.headerForeground', { dark: new Color(new RGBA(229, 229, 229)), light: new Color(new RGBA(16, 16, 16)), hc: null }, nls.localize('tableHeaderForeground', 'Table header foreground color')); +export const disabledInputBackground = registerColor('input.disabled.background', { dark: '#444444', light: '#dcdcdc', hc: Color.black }, nls.localize('disabledInputBoxBackground', "Disabled Input box background.")); +export const disabledInputForeground = registerColor('input.disabled.foreground', { dark: '#888888', light: '#888888', hc: foreground }, nls.localize('disabledInputBoxForeground', "Disabled Input box foreground.")); \ No newline at end of file diff --git a/src/sql/common/theme/styler.ts b/src/sql/common/theme/styler.ts new file mode 100644 index 0000000000..a7ad53174d --- /dev/null +++ b/src/sql/common/theme/styler.ts @@ -0,0 +1,227 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as sqlcolors from './colors'; + +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import * as cr from 'vs/platform/theme/common/colorRegistry'; +import { IThemable, attachStyler } from 'vs/platform/theme/common/styler'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; + +export function attachModalDialogStyler(widget: IThemable, themeService: IThemeService, style?: + { + dialogForeground?: cr.ColorIdentifier, + dialogHeaderAndFooterBackground?: cr.ColorIdentifier, + dialogBodyBackground?: cr.ColorIdentifier, + }): IDisposable { + return attachStyler(themeService, { + dialogForeground: (style && style.dialogForeground) || cr.foreground, + dialogBorder: cr.contrastBorder, + dialogHeaderAndFooterBackground: (style && style.dialogHeaderAndFooterBackground) || SIDE_BAR_BACKGROUND, + dialogBodyBackground: (style && style.dialogBodyBackground) || cr.editorBackground + }, widget); +} + +export function attachDropdownStyler(widget: IThemable, themeService: IThemeService, style?: + { + backgroundColor?: cr.ColorIdentifier, + foregroundColor?: cr.ColorIdentifier, + borderColor?: cr.ColorIdentifier, + }): IDisposable { + return attachStyler(themeService, { + foregroundColor: (style && style.foregroundColor) || cr.inputForeground, + borderColor: (style && style.borderColor) || cr.inputBorder, + backgroundColor: (style && style.backgroundColor) || cr.editorBackground + }, widget); +} + +export function attachInputBoxStyler(widget: IThemable, themeService: IThemeService, style?: + { + inputBackground?: cr.ColorIdentifier, + inputForeground?: cr.ColorIdentifier, + disabledInputBackground?: cr.ColorIdentifier, + disabledInputForeground?: cr.ColorIdentifier, + inputBorder?: cr.ColorIdentifier, + inputValidationInfoBorder?: cr.ColorIdentifier, + inputValidationInfoBackground?: cr.ColorIdentifier, + inputValidationWarningBorder?: cr.ColorIdentifier, + inputValidationWarningBackground?: cr.ColorIdentifier, + inputValidationErrorBorder?: cr.ColorIdentifier, + inputValidationErrorBackground?: cr.ColorIdentifier + }): IDisposable { + return attachStyler(themeService, { + inputBackground: (style && style.inputBackground) || cr.inputBackground, + inputForeground: (style && style.inputForeground) || cr.inputForeground, + disabledInputBackground: (style && style.disabledInputBackground) || sqlcolors.disabledInputBackground, + disabledInputForeground: (style && style.disabledInputForeground) || sqlcolors.disabledInputForeground, + inputBorder: (style && style.inputBorder) || cr.inputBorder, + inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder, + inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground, + inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder, + inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground, + inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder, + inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground + }, widget); +} + +export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeService, style?: + { + selectBackground?: cr.ColorIdentifier, + selectForeground?: cr.ColorIdentifier, + selectBorder?: cr.ColorIdentifier, + disabledSelectBackground?: cr.ColorIdentifier, + disabledSelectForeground?: cr.ColorIdentifier, + inputValidationInfoBorder?: cr.ColorIdentifier, + inputValidationInfoBackground?: cr.ColorIdentifier, + inputValidationWarningBorder?: cr.ColorIdentifier, + inputValidationWarningBackground?: cr.ColorIdentifier, + inputValidationErrorBorder?: cr.ColorIdentifier, + inputValidationErrorBackground?: cr.ColorIdentifier + }): IDisposable { + return attachStyler(themeService, { + selectBackground: (style && style.selectBackground) || cr.selectBackground, + selectForeground: (style && style.selectForeground) || cr.selectForeground, + selectBorder: (style && style.selectBorder) || cr.selectBorder, + disabledSelectBackground: (style && style.disabledSelectBackground) || sqlcolors.disabledInputBackground, + disabledSelectForeground: (style && style.disabledSelectForeground) || sqlcolors.disabledInputForeground, + inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder, + inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground, + inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder, + inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground, + inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder, + inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground + }, widget); +} + +export function attachListBoxStyler(widget: IThemable, themeService: IThemeService, style?: + { + selectBackground?: cr.ColorIdentifier, + selectForeground?: cr.ColorIdentifier, + selectBorder?: cr.ColorIdentifier, + inputValidationInfoBorder?: cr.ColorIdentifier, + inputValidationInfoBackground?: cr.ColorIdentifier, + inputValidationWarningBorder?: cr.ColorIdentifier, + inputValidationWarningBackground?: cr.ColorIdentifier, + inputValidationErrorBorder?: cr.ColorIdentifier, + inputValidationErrorBackground?: cr.ColorIdentifier + }): IDisposable { + return attachStyler(themeService, { + selectBackground: (style && style.selectBackground) || cr.selectBackground, + selectForeground: (style && style.selectForeground) || cr.selectForeground, + selectBorder: (style && style.selectBorder) || cr.selectBorder, + inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder, + inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground, + inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder, + inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground, + inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder, + inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground + }, widget); +} + +export function attachTableStyler(widget: IThemable, themeService: IThemeService, style?: { + listFocusBackground?: cr.ColorIdentifier, + listFocusForeground?: cr.ColorIdentifier, + listActiveSelectionBackground?: cr.ColorIdentifier, + listActiveSelectionForeground?: cr.ColorIdentifier, + listFocusAndSelectionBackground?: cr.ColorIdentifier, + listFocusAndSelectionForeground?: cr.ColorIdentifier, + listInactiveFocusBackground?: cr.ColorIdentifier, + listInactiveSelectionBackground?: cr.ColorIdentifier, + listInactiveSelectionForeground?: cr.ColorIdentifier, + listHoverBackground?: cr.ColorIdentifier, + listHoverForeground?: cr.ColorIdentifier, + listDropBackground?: cr.ColorIdentifier, + listFocusOutline?: cr.ColorIdentifier, + listInactiveFocusOutline?: cr.ColorIdentifier, + listSelectionOutline?: cr.ColorIdentifier, + listHoverOutline?: cr.ColorIdentifier, + tableHeaderBackground?: cr.ColorIdentifier, + tableHeaderForeground?: cr.ColorIdentifier +}): IDisposable { + return attachStyler(themeService, { + listFocusBackground: (style && style.listFocusBackground) || cr.listFocusBackground, + listFocusForeground: (style && style.listFocusForeground) || cr.listFocusForeground, + listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || cr.lighten(cr.listActiveSelectionBackground, 0.1), + listActiveSelectionForeground: (style && style.listActiveSelectionForeground) || cr.listActiveSelectionForeground, + listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || cr.listActiveSelectionBackground, + listFocusAndSelectionForeground: (style && style.listFocusAndSelectionForeground) || cr.listActiveSelectionForeground, + listInactiveFocusBackground: (style && style.listInactiveFocusBackground), + listInactiveSelectionBackground: (style && style.listInactiveSelectionBackground) || cr.listInactiveSelectionBackground, + listInactiveSelectionForeground: (style && style.listInactiveSelectionForeground) || cr.listInactiveSelectionForeground, + listHoverBackground: (style && style.listHoverBackground) || cr.listHoverBackground, + listHoverForeground: (style && style.listHoverForeground) || cr.listHoverForeground, + listDropBackground: (style && style.listDropBackground) || cr.listDropBackground, + listFocusOutline: (style && style.listFocusOutline) || cr.activeContrastBorder, + listSelectionOutline: (style && style.listSelectionOutline) || cr.activeContrastBorder, + listHoverOutline: (style && style.listHoverOutline) || cr.activeContrastBorder, + listInactiveFocusOutline: style && style.listInactiveFocusOutline, + tableHeaderBackground: (style && style.tableHeaderBackground) || sqlcolors.tableHeaderBackground, + tableHeaderForeground: (style && style.tableHeaderForeground) || sqlcolors.tableHeaderForeground + }, widget); +} + +export function attachEditableDropdownStyler(widget: IThemable, themeService: IThemeService, style?: { + listFocusBackground?: cr.ColorIdentifier, + listFocusForeground?: cr.ColorIdentifier, + listActiveSelectionBackground?: cr.ColorIdentifier, + listActiveSelectionForeground?: cr.ColorIdentifier, + listFocusAndSelectionBackground?: cr.ColorIdentifier, + listFocusAndSelectionForeground?: cr.ColorIdentifier, + listInactiveFocusBackground?: cr.ColorIdentifier, + listInactiveSelectionBackground?: cr.ColorIdentifier, + listInactiveSelectionForeground?: cr.ColorIdentifier, + listHoverBackground?: cr.ColorIdentifier, + listHoverForeground?: cr.ColorIdentifier, + listDropBackground?: cr.ColorIdentifier, + listFocusOutline?: cr.ColorIdentifier, + listInactiveFocusOutline?: cr.ColorIdentifier, + listSelectionOutline?: cr.ColorIdentifier, + listHoverOutline?: cr.ColorIdentifier, + + inputBackground?: cr.ColorIdentifier, + inputForeground?: cr.ColorIdentifier, + inputBorder?: cr.ColorIdentifier, + inputValidationInfoBorder?: cr.ColorIdentifier, + inputValidationInfoBackground?: cr.ColorIdentifier, + inputValidationWarningBorder?: cr.ColorIdentifier, + inputValidationWarningBackground?: cr.ColorIdentifier, + inputValidationErrorBorder?: cr.ColorIdentifier, + inputValidationErrorBackground?: cr.ColorIdentifier, + contextBackground?: cr.ColorIdentifier, + contextBorder?: cr.ColorIdentifier +}): IDisposable { + return attachStyler(themeService, { + listFocusBackground: (style && style.listFocusBackground) || cr.listFocusBackground, + listFocusForeground: (style && style.listFocusForeground) || cr.listFocusForeground, + listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || cr.lighten(cr.listActiveSelectionBackground, 0.1), + listActiveSelectionForeground: (style && style.listActiveSelectionForeground) || cr.listActiveSelectionForeground, + listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || cr.listActiveSelectionBackground, + listFocusAndSelectionForeground: (style && style.listFocusAndSelectionForeground) || cr.listActiveSelectionForeground, + listInactiveFocusBackground: (style && style.listInactiveFocusBackground), + listInactiveSelectionBackground: (style && style.listInactiveSelectionBackground) || cr.listInactiveSelectionBackground, + listInactiveSelectionForeground: (style && style.listInactiveSelectionForeground) || cr.listInactiveSelectionForeground, + listHoverBackground: (style && style.listHoverBackground) || cr.listHoverBackground, + listHoverForeground: (style && style.listHoverForeground) || cr.listHoverForeground, + listDropBackground: (style && style.listDropBackground) || cr.listDropBackground, + listFocusOutline: (style && style.listFocusOutline) || cr.activeContrastBorder, + listSelectionOutline: (style && style.listSelectionOutline) || cr.activeContrastBorder, + listHoverOutline: (style && style.listHoverOutline) || cr.activeContrastBorder, + listInactiveFocusOutline: style && style.listInactiveFocusOutline, + inputBackground: (style && style.inputBackground) || cr.inputBackground, + inputForeground: (style && style.inputForeground) || cr.inputForeground, + inputBorder: (style && style.inputBorder) || cr.inputBorder, + inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder, + inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground, + inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder, + inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground, + inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder, + inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground, + contextBackground: (style && style.contextBackground) || cr.editorBackground, + contextBorder: (style && style.contextBorder) || cr.inputBorder + }, widget); +} diff --git a/src/sql/common/urlSerializer.ts b/src/sql/common/urlSerializer.ts new file mode 100644 index 0000000000..25fd338787 --- /dev/null +++ b/src/sql/common/urlSerializer.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DefaultUrlSerializer, UrlSerializer, UrlTree } from '@angular/router'; + +/** + * Angular router uses parentheses for custom behavior, however for file system, + * they are valid in paths. Therefore before and after angular handles url's, we + * encode and decode the parentheses. Github issue angular/angular#10280, microsoft/carbon#1116 + */ +export default class CustomUrlSerializer implements UrlSerializer { + private _defaultUrlSerializer: DefaultUrlSerializer = new DefaultUrlSerializer(); + + parse(url: string): UrlTree { + // Encode parentheses + url = url.replace(/\(/g, '%28').replace(/\)/g, '%29'); + // Use the default serializer from here on + return this._defaultUrlSerializer.parse(url); + } + + serialize(tree: UrlTree): string { + // serialize parentheses after angular router + return this._defaultUrlSerializer.serialize(tree).replace(/%28/g, '(').replace(/%29/g, ')'); + } +} diff --git a/src/sql/data.d.ts b/src/sql/data.d.ts new file mode 100644 index 0000000000..267c54054f --- /dev/null +++ b/src/sql/data.d.ts @@ -0,0 +1,1313 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'data' { + import * as vscode from 'vscode'; + + // EXPORTED NAMESPACES ///////////////////////////////////////////////// + /** + * Namespace for Data Management Protocol global methods + */ + export namespace dataprotocol { + export function registerProvider(provider: DataProtocolProvider): vscode.Disposable; + + /** + * An [event](#Event) which fires when the specific flavor of a language used in DMP + * connections has changed. And example is for a SQL connection, the flavor changes. + */ + export const onDidChangeLanguageFlavor: vscode.Event; + } + + /** + * Namespace for credentials management global methods, available to all extensions + */ + export namespace credentials { + /** + * Register a credential provider to handle credential requests. + * @param {CredentialProvider} provider The provider to register + * @return {Disposable} Handle to the provider for disposal + */ + export function registerProvider(provider: CredentialProvider): vscode.Disposable; + + /** + * Retrieves a provider from the extension host if one has been registered. Any credentials + * accessed with the returned provider will have the namespaceId appended to credential ID + * to prevent extensions from trampling over each others' credentials. + * @param {string} namespaceId ID that will be appended to credential IDs. + * @return {Thenable} Promise that returns the namespaced provider + */ + export function getProvider(namespaceId: string): Thenable; + } + + /** + * Namespace for serialization management global methods + */ + export namespace serialization { + export function registerProvider(provider: SerializationProvider): vscode.Disposable; + } + + // EXPORTED INTERFACES ///////////////////////////////////////////////// + export interface ConnectionInfo { + + options: { [name: string]: any }; + } + + export interface ConnectionInfoSummary { + + /** + * URI identifying the owner of the connection + */ + ownerUri: string; + + /** + * connection id returned from service host. + */ + connectionId: string; + + /** + * any diagnostic messages return from the service host. + */ + messages: string; + + /** + * Error message returned from the engine, if any. + */ + errorMessage: string; + + /** + * Error number returned from the engine, if any. + */ + errorNumber: number; + /** + * Information about the connected server. + */ + serverInfo: ServerInfo; + /** + * information about the actual connection established + */ + connectionSummary: ConnectionSummary; + } + + /** + * Summary that identifies a unique database connection. + */ + export interface ConnectionSummary { + /** + * server name + */ + serverName: string; + /** + * database name + */ + databaseName: string; + /** + * user name + */ + userName: string; + } + + /** + * Information about a Server instance. + */ + export interface ServerInfo { + /** + * The major version of the instance. + */ + serverMajorVersion: number; + /** + * The minor version of the instance. + */ + serverMinorVersion: number; + /** + * The build of the instance. + */ + serverReleaseVersion: number; + /** + * The ID of the engine edition of the instance. + */ + engineEditionId: number; + /** + * String containing the full server version text. + */ + serverVersion: string; + /** + * String describing the product level of the server. + */ + serverLevel: string; + /** + * The edition of the instance. + */ + serverEdition: string; + /** + * Whether the instance is running in the cloud (Azure) or not. + */ + isCloud: boolean; + /** + * The version of Azure that the instance is running on, if applicable. + */ + azureVersion: number; + /** + * The Operating System version string of the machine running the instance. + */ + osVersion: string; + } + + export interface ConnectionProvider { + handle: number; + + connect(connectionUri: string, connectionInfo: ConnectionInfo): Thenable; + + disconnect(connectionUri: string): Thenable; + + cancelConnect(connectionUri: string): Thenable; + + listDatabases(connectionUri: string): Thenable; + + changeDatabase(connectionUri: string, newDatabase: string): Thenable; + + rebuildIntelliSenseCache(connectionUri: string): Thenable; + + registerOnConnectionComplete(handler: (connSummary: ConnectionInfoSummary) => any); + + registerOnIntelliSenseCacheComplete(handler: (connectionUri: string) => any); + + registerOnConnectionChanged(handler: (changedConnInfo: ChangedConnectionInfo) => any); + } + + export enum ServiceOptionType { + string = 0, + multistring = 1, + password = 2, + number = 3, + category = 4, + boolean = 5, + object = 6 + } + + export enum ConnectionOptionSpecialType { + serverName = 0, + databaseName = 1, + authType = 2, + userName = 3, + password = 4, + appName = 5 + } + + export interface CategoryValue { + displayName: string; + name: string; + } + + export interface ConnectionOption { + name: string; + + displayName: string; + + description: string; + + groupName: string; + + valueType: ServiceOptionType; + + specialValueType: ConnectionOptionSpecialType; + + defaultValue: string; + + categoryValues: CategoryValue[]; + + isIdentity: boolean; + + isRequired: boolean; + } + + export interface ConnectionProviderOptions { + options: ConnectionOption[]; + } + + export interface ServiceOption { + name: string; + + displayName: string; + + description: string; + + groupName: string; + + valueType: ServiceOptionType; + + defaultValue: string; + + objectType: string; + + categoryValues: CategoryValue[]; + + isRequired: boolean; + + isArray: boolean; + } + + export interface AdminServicesOptions { + databaseInfoOptions: ServiceOption[]; + + databaseFileInfoOptions: ServiceOption[]; + + fileGroupInfoOptions: ServiceOption[]; + } + + + // List Databases Request ---------------------------------------------------------------------- + export interface ListDatabasesResult { + databaseNames: Array; + } + + /** + * Information about a connection changed event for a resource represented by a URI + */ + export interface ChangedConnectionInfo { + /** + * Owner URI of the connection that changed. + */ + connectionUri: string; + + /** + * Summary of details containing any connection changes. + */ + connection: ConnectionSummary; + } + + export interface FeatureMetadataProvider { + enabled: boolean; + + featureName: string; + + optionsMetadata: ServiceOption[]; + } + + export interface DataProtocolServerCapabilities { + protocolVersion: string; + + providerName: string; + + providerDisplayName: string; + + connectionProvider: ConnectionProviderOptions; + + adminServicesProvider: AdminServicesOptions; + + features: FeatureMetadataProvider[]; + } + + export interface DataProtocolClientCapabilities { + hostName: string; + + hostVersion: string; + } + + export interface CapabilitiesProvider { + getServerCapabilities(client: DataProtocolClientCapabilities): Thenable; + } + + export enum MetadataType { + Table = 0, + View = 1, + SProc = 2, + Function = 3 + } + + export interface ObjectMetadata { + metadataType: MetadataType; + + metadataTypeName: string; + + urn: string; + + name: string; + + schema: string; + } + + export interface ColumnMetadata { + + hasExtendedProperties: boolean; + + defaultValue: string; + + /// + /// Escaped identifier for the name of the column + /// + escapedName: string; + + /// + /// Whether or not the column is computed + /// + isComputed: boolean; + + /// + /// Whether or not the column is deterministically computed + /// + isDeterministic: boolean; + + /// + /// Whether or not the column is an identity column + /// + isIdentity: boolean; + + /// + /// The ordinal ID of the column + /// + ordinal: number; + + /// + /// Whether or not the column is calculated on the server side. This could be a computed + /// column or a identity column. + /// + isCalculated: boolean; + + /// + /// Whether or not the column is used in a key to uniquely identify a row + /// + isKey: boolean; + + /// + /// Whether or not the column can be trusted for uniqueness + /// + isTrustworthyForUniqueness: boolean; + } + + export interface TableMetadata { + + columns: ColumnMetadata; + + } + + export interface ProviderMetadata { + objectMetadata: ObjectMetadata[]; + } + + export interface MetadataProvider { + getMetadata(connectionUri: string): Thenable; + + getDatabases(connectionUri: string): Thenable; + + getTableInfo(connectionUri: string, metadata: ObjectMetadata): Thenable; + + getViewInfo(connectionUri: string, metadata: ObjectMetadata): Thenable; + } + + export interface ScriptingResult { + operationId: string; + script: string; + } + + export interface ScriptingParamDetails { + filePath: string; + scriptCompatibilityOption: string; + targetDatabaseEngineEdition: string; + targetDatabaseEngineType: string; + } + + export interface ScriptingProvider { + scriptAsSelect(connectionUri: string, metadata: ObjectMetadata, paramDetails: ScriptingParamDetails): Thenable; + + scriptAsCreate(connectionUri: string, metadata: ObjectMetadata, paramDetails: ScriptingParamDetails): Thenable; + + scriptAsInsert(connectionUri: string, metadata: ObjectMetadata, paramDetails: ScriptingParamDetails): Thenable; + + scriptAsUpdate(connectionUri: string, metadata: ObjectMetadata, paramDetails: ScriptingParamDetails): Thenable; + + scriptAsDelete(connectionUri: string, metadata: ObjectMetadata, paramDetails: ScriptingParamDetails): Thenable; + + registerOnScriptingComplete(handler: (scriptingCompleteResult: ScriptingCompleteResult) => any); + } + + export interface ScriptingCompleteResult { + errorDetails: string; + + errorMessage: string; + + hasError: boolean; + + canceled: boolean; + + success: boolean; + + operationId: string; + } + /** + * Data Management Protocol main provider class that DMP extensions should implement. + * This provider interface contains references to providers for the various capabilitiesProvider + * that an extension can implement. + */ + export interface DataProtocolProvider { + handle: number; + + providerId: string; + + capabilitiesProvider: CapabilitiesProvider; + + connectionProvider: ConnectionProvider; + + queryProvider: QueryProvider; + + metadataProvider: MetadataProvider; + + scriptingProvider: ScriptingProvider; + + objectExplorerProvider: ObjectExplorerProvider; + + adminServicesProvider: AdminServicesProvider; + + disasterRecoveryProvider: DisasterRecoveryProvider; + + taskServicesProvider: TaskServicesProvider; + + fileBrowserProvider: FileBrowserProvider; + } + + /** + * Parameters to initialize a connection to a database + */ + export interface Credential { + /** + * Unique ID identifying the credential + */ + credentialId: string; + + /** + * password + */ + password: string; + } + + export interface CredentialProvider { + handle: number; + + saveCredential(credentialId: string, password: string): Thenable; + + readCredential(credentialId: string): Thenable; + + deleteCredential(credentialId: string): Thenable; + } + + export interface SerializationProvider { + handle: number; + saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable; + } + + + export interface DidChangeLanguageFlavorParams { + uri: string; + language: string; + flavor: string; + } + + export interface QueryProvider { + handle: number; + // TODO replace this temporary queryType field to detect "MSSQL" vs "Other" with a standard definition for supported platform + queryType: string; + cancelQuery(ownerUri: string): Thenable; + runQuery(ownerUri: string, selection: ISelectionData, runOptions?: ExecutionPlanOptions): Thenable; + runQueryStatement(ownerUri: string, line: number, column: number): Thenable; + runQueryString(ownerUri: string, queryString: string): Thenable; + runQueryAndReturn(ownerUri: string, queryString: string): Thenable; + getQueryRows(rowData: QueryExecuteSubsetParams): Thenable; + disposeQuery(ownerUri: string): Thenable; + saveResults(requestParams: SaveResultsRequestParams): Thenable; + + // Notifications + registerOnQueryComplete(handler: (result: QueryExecuteCompleteNotificationResult) => any): void; + registerOnBatchStart(handler: (batchInfo: QueryExecuteBatchNotificationParams) => any): void; + registerOnBatchComplete(handler: (batchInfo: QueryExecuteBatchNotificationParams) => any): void; + registerOnResultSetComplete(handler: (resultSetInfo: QueryExecuteResultSetCompleteNotificationParams) => any): void; + registerOnMessage(handler: (message: QueryExecuteMessageParams) => any): void; + + // Edit Data Requests + commitEdit(ownerUri: string): Thenable; + createRow(ownerUri: string): Thenable; + deleteRow(ownerUri: string, rowId: number): Thenable; + disposeEdit(ownerUri: string): Thenable; + initializeEdit(ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): Thenable; + revertCell(ownerUri: string, rowId: number, columnId: number): Thenable; + revertRow(ownerUri: string, rowId: number): Thenable; + updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable; + getEditRows(rowData: EditSubsetParams): Thenable; + + // Edit Data Notifications + registerOnEditSessionReady(handler: (ownerUri: string, success: boolean, message: string) => any): void; + } + + export interface IDbColumn { + allowDBNull?: boolean; + baseCatalogName: string; + baseColumnName: string; + baseSchemaName: string; + baseServerName: string; + baseTableName: string; + columnName: string; + columnOrdinal?: number; + columnSize?: number; + isAliased?: boolean; + isAutoIncrement?: boolean; + isExpression?: boolean; + isHidden?: boolean; + isIdentity?: boolean; + isKey?: boolean; + isBytes?: boolean; + isChars?: boolean; + isSqlVariant?: boolean; + isUdt?: boolean; + dataType: string; + isXml?: boolean; + isJson?: boolean; + isLong?: boolean; + isReadOnly?: boolean; + isUnique?: boolean; + numericPrecision?: number; + numericScale?: number; + udtAssemblyQualifiedName: string; + dataTypeName: string; + } + + export interface IGridResultSet { + columns: IDbColumn[]; + rowsUri: string; + numberOfRows: number; + } + + export interface IResultMessage { + batchId?: number; + isError: boolean; + time: string; + message: string; + } + + export interface ISelectionData { + startLine: number; + startColumn: number; + endLine: number; + endColumn: number; + } + + export interface ResultSetSummary { + id: number; + batchId: number; + rowCount: number; + columnInfo: IDbColumn[]; + } + + export interface BatchSummary { + hasError: boolean; + id: number; + selection: ISelectionData; + resultSetSummaries: ResultSetSummary[]; + executionElapsed: string; + executionEnd: string; + executionStart: string; + } + + export enum EditRowState { + clean = 0, + dirtyInsert = 1, + dirtyDelete = 2, + dirtyUpdate = 3 + } + + export interface EditRow { + cells: DbCellValue[]; + id: number; + isDirty: boolean; + state: EditRowState; + } + + export interface EditCell extends DbCellValue { + isDirty: boolean; + } + + export interface QueryExecuteCompleteNotificationResult { + ownerUri: string; + batchSummaries: BatchSummary[]; + } + + export interface ExecutionPlanOptions { + displayEstimatedQueryPlan?: boolean; + displayActualQueryPlan?: boolean; + } + + export interface SimpleExecuteParams { + queryString: string; + ownerUri: string; + } + + export interface SimpleExecuteResult { + rowCount: number; + columnInfo: IDbColumn[]; + rows: DbCellValue[][]; + } + + // Query Batch Notification ----------------------------------------------------------------------- + export interface QueryExecuteBatchNotificationParams { + batchSummary: BatchSummary; + ownerUri: string; + } + + + export interface QueryExecuteResultSetCompleteNotificationParams { + resultSetSummary: ResultSetSummary; + ownerUri: string; + } + + + export interface QueryExecuteMessageParams { + message: IResultMessage; + ownerUri: string; + } + + export interface QueryExecuteParams { + ownerUri: string; + querySelection: ISelectionData; + } + + export interface QueryExecuteSubsetParams { + ownerUri: string; + batchIndex: number; + resultSetIndex: number; + rowsStartIndex: number; + rowsCount: number; + } + + export interface DbCellValue { + displayValue: string; + isNull: boolean; + } + + export interface ResultSetSubset { + rowCount: number; + rows: DbCellValue[][]; + } + + export interface QueryExecuteSubsetResult { + resultSubset: ResultSetSubset; + } + + export interface QueryCancelResult { + messages: string; + } + + // Save Results =============================================================================== + export interface SaveResultsRequestParams { + /** + * 'csv', 'json', 'excel' + */ + resultFormat: string; + ownerUri: string; + filePath: string; + batchIndex: number; + resultSetIndex: number; + rowStartIndex: number; + rowEndIndex: number; + columnStartIndex: number; + columnEndIndex: number; + includeHeaders?: boolean; + } + + export interface SaveResultRequestResult { + messages: string; + } + + // Edit Data ================================================================================== + // Shared Interfaces -------------------------------------------------------------------------- + export interface IEditSessionOperationParams { + ownerUri: string; + } + + export interface IEditRowOperationParams extends IEditSessionOperationParams { + rowId: number; + } + + export interface EditCellResult { + cell: EditCell; + isRowDirty: boolean; + } + + // edit/commit -------------------------------------------------------------------------------- + export interface EditCommitParams extends IEditSessionOperationParams { } + export interface EditCommitResult { } + + // edit/createRow ----------------------------------------------------------------------------- + export interface EditCreateRowParams extends IEditSessionOperationParams { } + export interface EditCreateRowResult { + defaultValues: string[]; + newRowId: number; + } + + // edit/deleteRow ----------------------------------------------------------------------------- + export interface EditDeleteRowParams extends IEditRowOperationParams { } + export interface EditDeleteRowResult { } + + // edit/dispose ------------------------------------------------------------------------------- + export interface EditDisposeParams extends IEditSessionOperationParams { } + export interface EditDisposeResult { } + + // edit/initialize ---------------------------------------------------------------------------- + export interface EditInitializeFiltering { + LimitResults?: number; + } + export interface EditInitializeParams extends IEditSessionOperationParams { + filters: EditInitializeFiltering; + objectName: string; + objectType: string; + } + + export interface EditInitializeResult { } + + // edit/revertCell ---------------------------------------------------------------------------- + export interface EditRevertCellParams extends IEditRowOperationParams { + columnId: number; + } + export interface EditRevertCellResult extends EditCellResult { + } + + // edit/revertRow ----------------------------------------------------------------------------- + export interface EditRevertRowParams extends IEditRowOperationParams { } + export interface EditRevertRowResult { } + + // edit/sessionReady Event -------------------------------------------------------------------- + export interface EditSessionReadyParams { + ownerUri: string; + success: boolean; + message: string; + } + + // edit/updateCell ---------------------------------------------------------------------------- + export interface EditUpdateCellParams extends IEditRowOperationParams { + columnId: number; + newValue: string; + } + + export interface EditUpdateCellResult extends EditCellResult { + } + + // edit/subset -------------------------------------------------------------------------------- + export interface EditSubsetParams extends IEditSessionOperationParams { + rowStartIndex: number; + rowCount: number; + } + export interface EditSubsetResult { + rowCount: number; + subset: EditRow[]; + } + + export interface NodeInfo { + nodePath: string; + nodeType: string; + nodeSubType: string; + nodeStatus: string; + label: string; + isLeaf: boolean; + metadata: ObjectMetadata; + errorMessage: string; + } + + // Object Explorer interfaces ----------------------------------------------------------------------- + export interface ObjectExplorerSession { + success: boolean; + sessionId: string; + rootNode: NodeInfo; + errorMessage: string; + } + + export interface ObjectExplorerSessionResponse { + sessionId: string; + } + + export interface ObjectExplorerExpandInfo { + sessionId: string; + nodePath: string; + nodes: NodeInfo[]; + errorMessage: string; + } + + export interface ExpandNodeInfo { + sessionId: string; + nodePath: string; + } + + export interface ObjectExplorerCloseSessionInfo { + sessionId: string; + } + + export interface ObjectExplorerCloseSessionResponse { + sessionId: string; + success: boolean; + } + + export interface ObjectExplorerProvider { + createNewSession(connInfo: ConnectionInfo): Thenable; + + expandNode(nodeInfo: ExpandNodeInfo): Thenable; + + refreshNode(nodeInfo: ExpandNodeInfo): Thenable; + + closeSession(closeSessionInfo: ObjectExplorerCloseSessionInfo): Thenable; + + registerOnSessionCreated(handler: (response: ObjectExplorerSession) => any); + + registerOnExpandCompleted(handler: (response: ObjectExplorerExpandInfo) => any); + + } + + // Admin Services interfaces ----------------------------------------------------------------------- + export interface DatabaseInfo { + options: {}; + } + + export interface LoginInfo { + name: string; + } + + export interface CreateDatabaseResponse { + result: boolean; + taskId: number; + } + + export interface CreateLoginResponse { + result: boolean; + taskId: number; + } + + export interface AdminServicesProvider { + createDatabase(connectionUri: string, database: DatabaseInfo): Thenable; + + createLogin(connectionUri: string, login: LoginInfo): Thenable; + + getDefaultDatabaseInfo(connectionUri: string): Thenable; + + getDatabaseInfo(connectionUri: string): Thenable; + } + + // Task service interfaces ---------------------------------------------------------------------------- + export enum TaskStatus { + notStarted = 0, + inProgress = 1, + succeeded = 2, + succeededWithWarning = 3, + failed = 4, + canceled = 5 + } + + export enum TaskExecutionMode { + execute = 0, + script = 1, + executeAndScript = 2, + } + + export interface ListTasksParams { + listActiveTasksOnly: boolean; + } + + export interface TaskInfo { + taskId: string; + status: TaskStatus; + taskExecutionMode: TaskExecutionMode; + serverName: string; + databaseName: string; + name: string; + description: string; + providerName: string; + isCancelable: boolean; + } + + export interface ListTasksResponse { + tasks: TaskInfo[]; + } + + export interface CancelTaskParams { + taskId: string; + } + + export interface TaskProgressInfo { + taskId: string; + status: TaskStatus; + message: string; + script: string; + duration: number; + } + + export interface TaskServicesProvider { + getAllTasks(listTasksParams: ListTasksParams): Thenable; + + cancelTask(cancelTaskParams: CancelTaskParams): Thenable; + + registerOnTaskCreated(handler: (response: TaskInfo) => any); + + registerOnTaskStatusChanged(handler: (response: TaskProgressInfo) => any); + } + + // Disaster Recovery interfaces ----------------------------------------------------------------------- + + export interface BackupConfigInfo { + recoveryModel: string; + defaultBackupFolder: string; + backupEncryptors: {}; + } + + export interface BackupResponse { + result: boolean; + taskId: number; + } + + export interface DisasterRecoveryProvider { + backup(connectionUri: string, backupInfo: { [key: string]: any }, taskExecutionMode: TaskExecutionMode): Thenable; + getBackupConfigInfo(connectionUri: string): Thenable; + getRestorePlan(connectionUri: string, restoreInfo: RestoreInfo): Thenable; + cancelRestorePlan(connectionUri: string, restoreInfo: RestoreInfo): Thenable; + restore(connectionUri: string, restoreInfo: RestoreInfo): Thenable; + getRestoreConfigInfo(connectionUri: string): Thenable; + } + + export interface RestoreInfo { + options: { [key: string]: any }; + taskExecutionMode: TaskExecutionMode; + } + + export interface RestoreDatabaseFileInfo { + fileType: string; + + logicalFileName: string; + + originalFileName: string; + + restoreAsFileName: string; + } + + export interface DatabaseFileInfo { + properties: LocalizedPropertyInfo[]; + id: string; + isSelected: boolean; + } + + export interface LocalizedPropertyInfo { + propertyName: string; + propertyValue: string; + propertyDisplayName: string; + propertyValueDisplayName: string; + } + + export interface RestorePlanDetailInfo { + name: string; + currentValue: any; + isReadOnly: boolean; + isVisible: boolean; + defaultValue: any; + } + + export interface RestorePlanResponse { + sessionId: string; + backupSetsToRestore: DatabaseFileInfo[]; + canRestore: boolean; + errorMessage: string; + dbFiles: RestoreDatabaseFileInfo[]; + databaseNamesFromBackupSets: string[]; + planDetails: { [key: string]: RestorePlanDetailInfo }; + } + + export interface RestoreConfigInfo { + configInfo: { [key: string]: any }; + } + + export interface RestoreResponse { + result: boolean; + taskId: string; + errorMessage: string; + } + + export interface IProfilerProvider { + startSession(sessionId: string): Thenable; + stopSession(sessionId: string): Thenable; + pauseSession(sessionId: string): Thenable; + connectSession(sessionId: string): Thenable; + disconnectSession(sessionId: string): Thenable; + } + + export interface IProfilerTableRow { + /** + * Name of the event; known issue this is not camel case, need to figure + * out a better way to determine column id's from rendered column names + */ + EventClass: string; + } + + export interface IProfilerMoreRowsNotificationParams { + uri: string; + rowCount: number; + data: IProfilerTableRow; + } + + // File browser interfaces ----------------------------------------------------------------------- + + export interface FileBrowserProvider { + openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable; + registerOnFileBrowserOpened(handler: (response: FileBrowserOpenedParams) => any); + expandFolderNode(ownerUri: string, expandPath: string): Thenable; + registerOnFolderNodeExpanded(handler: (response: FileBrowserExpandedParams) => any); + validateFilePaths(ownerUri: string, serviceType: string, selectedFiles: string[]): Thenable; + registerOnFilePathsValidated(handler: (response: FileBrowserValidatedParams) => any); + closeFileBrowser(ownerUri: string): Thenable; + } + + export interface FileTreeNode { + children: FileTreeNode[]; + isExpanded: boolean; + isFile: boolean; + name: string; + fullPath: string; + } + + export interface FileTree { + rootNode: FileTreeNode; + selectedNode: FileTreeNode; + } + + export interface FileBrowserOpenedParams { + ownerUri: string; + fileTree: FileTree; + succeeded: boolean; + message: string; + } + + export interface FileBrowserExpandedParams { + ownerUri: string; + expandPath: string; + children: FileTreeNode[]; + succeeded: boolean; + message: string; + } + + export interface FileBrowserValidatedParams { + succeeded: boolean; + message: string; + } + + export interface FileBrowserCloseResponse { + succeeded: boolean; + message: string; + } + + // ACCOUNT MANAGEMENT ////////////////////////////////////////////////// + export namespace accounts { + export function registerAccountProvider(providerMetadata: AccountProviderMetadata, provider: AccountProvider): vscode.Disposable; + + /** + * Performs OAuth via the account management service and returns the resulting authorization code + * @param {string} url URL to load to begin OAuth + * @param {boolean} silent Whether or not to show the browser, use false when doing initial + * login, true when doing subsequent auth requests + * @return {Thenable} Promise to return the authorization code, rejects on failure + */ + export function performOAuthAuthorization(url: string, silent: boolean): Thenable; + } + + // - ACCOUNT DATATYPES ///////////////////////////////////////////////// + /** + * Image to display for an account + */ + export interface AccountContextualLogo { + /** + * Image to display on light theme + */ + light: string; + + /** + * Image to display on dark theme + */ + dark: string; + } + + /** + * Represents display information for an account. + */ + export interface AccountDisplayInfo { + /** + * A display name that offers context for the account, such as "Contoso". + */ + contextualDisplayName: string; + + /** + * Contents of the logo to display alongside the account. Indicates the context of the + * account provider (eg, Work/School vs Microsoft Account) + */ + contextualLogo: AccountContextualLogo; + + /** + * A display name that identifies the account, such as "user@contoso.com". + */ + displayName: string; + } + + /** + * Represents a key that identifies an account. + */ + export interface AccountKey { + /** + * Identifier of the provider + */ + providerId: string; + + /** + * Any arguments that identify an instantiation of the provider + */ + providerArgs?: any; + + /** + * Identifier for the account, unique to the provider + */ + accountId: string; + } + + /** + * Represents an account. + */ + export interface Account { + /** + * The key that identifies the account + */ + key: AccountKey; + + /** + * Display information for the account + */ + displayInfo: AccountDisplayInfo; + + /** + * Custom properties stored with the account + */ + properties: any; + + /** + * Indicates if the account needs refreshing + */ + isStale: boolean; + } + + // - ACCOUNT PROVIDER ////////////////////////////////////////////////// + /** + * Represents a provider of accounts. + */ + export interface AccountProviderMetadata { + /** + * The identifier of the provider + */ + id: string; + + /** + * Display name of the provider + */ + displayName: string; + + /** + * Any arguments that identify an instantiation of the provider + */ + args?: any; + + /** + * Optional settings that identify an instantiation of a provider + */ + settings?: {}; + } + + /** + * Represents a provider of accounts for use with the account management service + */ + export interface AccountProvider { + /** + * Initializes the account provider with the accounts restored from the memento, + * @param {Account[]} storedAccounts Accounts restored from the memento + * @return {Thenable} Account objects after being rehydrated (if necessary) + */ + initialize(storedAccounts: Account[]): Thenable; + + /** + * Generates a security token for the provided account + * @param {Account} account The account to generate a security token for + * @return {Thenable<{}>} Promise to return a security token object + */ + getSecurityToken(account: Account): Thenable<{}>; + + /** + * Prompts the user to enter account information. + * Returns an error if the user canceled the operation. + */ + prompt(): Thenable; + + /** + * Refreshes a stale account. + * Returns an error if the user canceled the operation. + * Otherwise, returns a new updated account instance. + * @param account - An account. + */ + refresh(account: Account): Thenable; + + /** + * Clears sensitive information for an account. To be called when account is removed + * @param accountKey - Key that uniquely identifies the account to clear + */ + clear(accountKey: AccountKey): Thenable; + } + + // Resource provider interfaces ----------------------------------------------------------------------- + + // - ACCOUNT PROVIDER ////////////////////////////////////////////////// + /** + * Represents a provider of accounts. + */ + export interface ResourceProviderMetadata { + /** + * The identifier of the provider + */ + id: string; + + /** + * Display name of the provider + */ + displayName: string; + + /** + * Optional settings that identify an instantiation of a provider + */ + settings?: {}; + } + + export namespace resources { + /** + * Registers a resource provider that can suport + */ + export function registerResourceProvider(providerMetadata: ResourceProviderMetadata, provider: ResourceProvider): vscode.Disposable; + } + + /** + * Represents a provider of resource + */ + export interface ResourceProvider { + createFirewallRule(account: Account, firewallruleInfo: FirewallRuleInfo): Thenable; + handleFirewallRule(errorCode: number, errorMessage: string, connectionTypeId: string): Thenable; + } + + export interface FirewallRuleInfo { + startIpAddress: string; + endIpAddress: string; + serverName: string; + securityTokenMappings: {}; + } + + export interface CreateFirewallRuleResponse { + result: boolean; + errorMessage: string; + } + + export interface HandleFirewallRuleResponse { + result: boolean; + ipAddress: string; + } +} diff --git a/src/sql/media/actionBarLabel.css b/src/sql/media/actionBarLabel.css new file mode 100644 index 0000000000..3b38ff35f7 --- /dev/null +++ b/src/sql/media/actionBarLabel.css @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Activity Bar - connection */ +.monaco-workbench > .activitybar > .content > .monaco-action-bar .action-label.connectionViewlet { + -webkit-mask: url('icons/server_page_inverse.svg') no-repeat 50% 50%; + -webkit-mask-size: 25px 25px; +} + +/* Activity Bar - task history */ +.monaco-workbench > .activitybar > .content > .monaco-action-bar .action-label.taskHistoryViewlet { + -webkit-mask: url('icons/run_history_inverse.svg') no-repeat 50% 50%; + -webkit-mask-size: 25px 25px; +} + +/* Activity Bar - explore */ +.monaco-workbench > .activitybar > .content > .monaco-action-bar .action-label.explore { + -webkit-mask: url('icons/file_inverse.svg') no-repeat 50% 50%; + -webkit-mask-size: 25px 25px; +} + +/* Activity Bar - source control */ +.monaco-workbench > .activitybar > .content > .monaco-action-bar .action-label.scm { + -webkit-mask: url('icons/sourcecontrol_inverse.svg') no-repeat 50% 50%; + -webkit-mask-size: 25px 25px; +} + +/* Activity Bar - search */ +.monaco-workbench > .activitybar > .content > .monaco-action-bar .action-label.search { + -webkit-mask: url('icons/search_inverse.svg') no-repeat 50% 50%; + -webkit-mask-size: 25px 25px; +} diff --git a/src/sql/media/icons/backup.svg b/src/sql/media/icons/backup.svg new file mode 100644 index 0000000000..a83591e2e1 --- /dev/null +++ b/src/sql/media/icons/backup.svg @@ -0,0 +1 @@ +backup_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/backup_inverse.svg b/src/sql/media/icons/backup_inverse.svg new file mode 100644 index 0000000000..839779ffcf --- /dev/null +++ b/src/sql/media/icons/backup_inverse.svg @@ -0,0 +1 @@ +backup_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/close-dark.svg b/src/sql/media/icons/close-dark.svg new file mode 100644 index 0000000000..ce0e589640 --- /dev/null +++ b/src/sql/media/icons/close-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/icons/close.svg b/src/sql/media/icons/close.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/sql/media/icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/icons/common-icons.css b/src/sql/media/icons/common-icons.css new file mode 100644 index 0000000000..829fb24068 --- /dev/null +++ b/src/sql/media/icons/common-icons.css @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.vs .icon.backup { + background: url("backup.svg") center center no-repeat; +} + +.vs-dark .icon.backup, +.hc-black .icon.backup { + background: url("backup_inverse.svg") center center no-repeat; +} + +.vs .icon.restore { + background: url("restore.svg") center center no-repeat; +} + +.vs-dark .icon.restore, +.hc-black .icon.restore { + background: url("restore_inverse.svg") center center no-repeat; +} + +.vs .icon.database { + background: url("database.svg") center center no-repeat; +} + +.vs-dark .icon.database, +.hc-black .icon.database { + background: url("database_inverse.svg") center center no-repeat; +} + +.vs .icon.file { + background: url("file.svg") center center no-repeat; +} + +.vs-dark .icon.file, +.hc-black .icon.file { + background: url("file_inverse.svg") center center no-repeat; +} + +.vs .icon.new-database { + background: url("new_database.svg") center center no-repeat; +} + +.vs-dark .icon.new-database, +.hc-black .icon.new-database { + background: url("new_database_inverse.svg") center center no-repeat; +} + +.vs .icon.server-page { + background: url("server_page.svg") center center no-repeat; +} + +.vs-dark .icon.server-page, +.hc-black .icon.server-page { + background: url("server_page_inverse.svg") center center no-repeat; +} + +.vs .icon.error, +.vs-dark .icon.error, +.hc-black .icon.error { + content: url("status_error.svg"); +} + +.vs .icon.warning, +.vs-dark .icon.warning, +.hc-black .icon.warning { + content: url("status_warning.svg"); +} + +.vs .icon.info, +.vs-dark .icon.info, +.hc-black .icon.info { + content: url("status_info.svg"); +} + +.vs .icon.success, +.vs-dark .icon.success, +.hc-black .icon.success { + content: url("status_success.svg"); +} + +.vs .icon.cancelled, +.vs-dark .icon.cancelled, +.hc-black .icon.cancelled { + content: url("status_cancelled.svg"); +} + +.vs .icon.in-progress { + content: url("loading.svg"); +} + +.vs-dark .icon.in-progress, +.hc-black .icon.in-progress { + content: url("loading_inverse.svg"); +} + +.vs .icon.scriptToClipboard, +.vs-dark .icon.scriptToClipboard, +.hc-black .icon.scriptToClipboard { + content: url('script_to_clipboard.svg'); + width: auto !important; + height: 25px; +} + +.vs .icon.close { + background-image: url('close.svg'); +} + +.vs-dark .icon.close, +.hc-black .icon.close { + background-image: url('close-dark.svg'); +} + +.vs .icon.filter { + background: url("filter.svg") center center no-repeat !important; +} + +.vs-dark .icon.filter, +.hc-black .icon.filter { + background: url("filter_inverse.svg") center center no-repeat !important; +} + +.vs .icon.remove { + background: url("remove.svg") center center no-repeat !important; +} + +.vs-dark .icon.remove, +.hc-black .icon.remove { + background: url("remove_inverse.svg") center center no-repeat !important; +} + +.vs .icon.warning-badge, +.vs-dark .icon.warning-badge, +.hc-black .icon.warning-badge { + background: url("status_warning.svg") center center no-repeat; +} + +.vs .icon.refresh { + background-image: url('refresh.svg'); +} + +.vs-dark .icon.refresh, +.hc-black .icon.refresh { + background-image: url('refresh_inverse.svg'); +} + +.hc-black .icon.toggle-more, +.vs-dark .icon.toggle-more { + background: url('ellipsis-inverse.svg') center center no-repeat; +} + +.vs .icon.toggle-more { + background: url('ellipsis.svg') center center no-repeat; +} + +.hc-black .icon.new-query, +.vs-dark .icon.new-query { + background: url('newquery_inverse.svg') center center no-repeat; +} + +.vs .icon.new-query { + background: url('newquery.svg') center center no-repeat; +} + +.hc-black .icon.configure-dashboard, +.vs-dark .icon.configure-dashboard { + background: url('configdashboard_inverse.svg') center center no-repeat; +} + +.vs .icon.configure-dashboard { + background: url('configdashboard.svg') center center no-repeat; +} \ No newline at end of file diff --git a/src/sql/media/icons/configdashboard.svg b/src/sql/media/icons/configdashboard.svg new file mode 100644 index 0000000000..9123b326d2 --- /dev/null +++ b/src/sql/media/icons/configdashboard.svg @@ -0,0 +1 @@ +configure_dashboard \ No newline at end of file diff --git a/src/sql/media/icons/configdashboard_inverse.svg b/src/sql/media/icons/configdashboard_inverse.svg new file mode 100644 index 0000000000..e5541da171 --- /dev/null +++ b/src/sql/media/icons/configdashboard_inverse.svg @@ -0,0 +1 @@ +manage_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/database.svg b/src/sql/media/icons/database.svg new file mode 100644 index 0000000000..c11f929ce4 --- /dev/null +++ b/src/sql/media/icons/database.svg @@ -0,0 +1 @@ +Database@2x \ No newline at end of file diff --git a/src/sql/media/icons/database_inverse.svg b/src/sql/media/icons/database_inverse.svg new file mode 100644 index 0000000000..fbb38a2836 --- /dev/null +++ b/src/sql/media/icons/database_inverse.svg @@ -0,0 +1 @@ +Database_Inverse@2x \ No newline at end of file diff --git a/src/sql/media/icons/ellipsis-inverse.svg b/src/sql/media/icons/ellipsis-inverse.svg new file mode 100644 index 0000000000..1140c11ecc --- /dev/null +++ b/src/sql/media/icons/ellipsis-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/icons/ellipsis.svg b/src/sql/media/icons/ellipsis.svg new file mode 100644 index 0000000000..b61e2d0c75 --- /dev/null +++ b/src/sql/media/icons/ellipsis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/icons/file.svg b/src/sql/media/icons/file.svg new file mode 100644 index 0000000000..69412f5c61 --- /dev/null +++ b/src/sql/media/icons/file.svg @@ -0,0 +1 @@ +file_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/file_inverse.svg b/src/sql/media/icons/file_inverse.svg new file mode 100644 index 0000000000..8276c545aa --- /dev/null +++ b/src/sql/media/icons/file_inverse.svg @@ -0,0 +1 @@ +file_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/filter.svg b/src/sql/media/icons/filter.svg new file mode 100644 index 0000000000..32f914f54a --- /dev/null +++ b/src/sql/media/icons/filter.svg @@ -0,0 +1 @@ +filter_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/filter_inverse.svg b/src/sql/media/icons/filter_inverse.svg new file mode 100644 index 0000000000..60b90abd6d --- /dev/null +++ b/src/sql/media/icons/filter_inverse.svg @@ -0,0 +1 @@ +filter_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/loading.svg b/src/sql/media/icons/loading.svg new file mode 100644 index 0000000000..e762f06d5e --- /dev/null +++ b/src/sql/media/icons/loading.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/src/sql/media/icons/loading_inverse.svg b/src/sql/media/icons/loading_inverse.svg new file mode 100644 index 0000000000..c3633c0dda --- /dev/null +++ b/src/sql/media/icons/loading_inverse.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/src/sql/media/icons/new_database.svg b/src/sql/media/icons/new_database.svg new file mode 100644 index 0000000000..dcbd9aabf7 --- /dev/null +++ b/src/sql/media/icons/new_database.svg @@ -0,0 +1 @@ +new_database_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/new_database_inverse.svg b/src/sql/media/icons/new_database_inverse.svg new file mode 100644 index 0000000000..4ca6a107f5 --- /dev/null +++ b/src/sql/media/icons/new_database_inverse.svg @@ -0,0 +1 @@ +new_database_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/newquery.svg b/src/sql/media/icons/newquery.svg new file mode 100644 index 0000000000..e783cf3958 --- /dev/null +++ b/src/sql/media/icons/newquery.svg @@ -0,0 +1 @@ +newquery_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/newquery_inverse.svg b/src/sql/media/icons/newquery_inverse.svg new file mode 100644 index 0000000000..5e52f63628 --- /dev/null +++ b/src/sql/media/icons/newquery_inverse.svg @@ -0,0 +1 @@ +newquery_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/refresh.svg b/src/sql/media/icons/refresh.svg new file mode 100644 index 0000000000..ce3d329303 --- /dev/null +++ b/src/sql/media/icons/refresh.svg @@ -0,0 +1 @@ +refresh \ No newline at end of file diff --git a/src/sql/media/icons/refresh_inverse.svg b/src/sql/media/icons/refresh_inverse.svg new file mode 100644 index 0000000000..f49e6f49b6 --- /dev/null +++ b/src/sql/media/icons/refresh_inverse.svg @@ -0,0 +1 @@ +refresh_inverse \ No newline at end of file diff --git a/src/sql/media/icons/remove.svg b/src/sql/media/icons/remove.svg new file mode 100644 index 0000000000..b8d1d94e26 --- /dev/null +++ b/src/sql/media/icons/remove.svg @@ -0,0 +1 @@ +close_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/remove_inverse.svg b/src/sql/media/icons/remove_inverse.svg new file mode 100644 index 0000000000..13449c366c --- /dev/null +++ b/src/sql/media/icons/remove_inverse.svg @@ -0,0 +1 @@ +close_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/restore.svg b/src/sql/media/icons/restore.svg new file mode 100644 index 0000000000..368a8cf7bf --- /dev/null +++ b/src/sql/media/icons/restore.svg @@ -0,0 +1 @@ +restore_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/restore_inverse.svg b/src/sql/media/icons/restore_inverse.svg new file mode 100644 index 0000000000..a7bebbce6e --- /dev/null +++ b/src/sql/media/icons/restore_inverse.svg @@ -0,0 +1 @@ +restore_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/run_history_inverse.svg b/src/sql/media/icons/run_history_inverse.svg new file mode 100644 index 0000000000..7475a62cb3 --- /dev/null +++ b/src/sql/media/icons/run_history_inverse.svg @@ -0,0 +1 @@ +run_history_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/script_to_clipboard.svg b/src/sql/media/icons/script_to_clipboard.svg new file mode 100644 index 0000000000..a33c47724a --- /dev/null +++ b/src/sql/media/icons/script_to_clipboard.svg @@ -0,0 +1 @@ +save_option_text_16x \ No newline at end of file diff --git a/src/sql/media/icons/search_inverse.svg b/src/sql/media/icons/search_inverse.svg new file mode 100644 index 0000000000..14cbfcf597 --- /dev/null +++ b/src/sql/media/icons/search_inverse.svg @@ -0,0 +1 @@ +search_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/server_page.svg b/src/sql/media/icons/server_page.svg new file mode 100644 index 0000000000..452693ba2b --- /dev/null +++ b/src/sql/media/icons/server_page.svg @@ -0,0 +1 @@ +server_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/server_page_inverse.svg b/src/sql/media/icons/server_page_inverse.svg new file mode 100644 index 0000000000..c11b61e1cd --- /dev/null +++ b/src/sql/media/icons/server_page_inverse.svg @@ -0,0 +1 @@ +server_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/sourcecontrol_inverse.svg b/src/sql/media/icons/sourcecontrol_inverse.svg new file mode 100644 index 0000000000..3695588695 --- /dev/null +++ b/src/sql/media/icons/sourcecontrol_inverse.svg @@ -0,0 +1 @@ +sourcecontrol_inverse_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/status_cancelled.svg b/src/sql/media/icons/status_cancelled.svg new file mode 100644 index 0000000000..ffe0a467c7 --- /dev/null +++ b/src/sql/media/icons/status_cancelled.svg @@ -0,0 +1 @@ +cancelledstate_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/status_error.svg b/src/sql/media/icons/status_error.svg new file mode 100644 index 0000000000..abe3c65921 --- /dev/null +++ b/src/sql/media/icons/status_error.svg @@ -0,0 +1 @@ +error_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/status_info.svg b/src/sql/media/icons/status_info.svg new file mode 100644 index 0000000000..aabb0d4051 --- /dev/null +++ b/src/sql/media/icons/status_info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/icons/status_success.svg b/src/sql/media/icons/status_success.svg new file mode 100644 index 0000000000..776e1fd909 --- /dev/null +++ b/src/sql/media/icons/status_success.svg @@ -0,0 +1 @@ +success_16x16 \ No newline at end of file diff --git a/src/sql/media/icons/status_warning.svg b/src/sql/media/icons/status_warning.svg new file mode 100644 index 0000000000..dff0c991e1 --- /dev/null +++ b/src/sql/media/icons/status_warning.svg @@ -0,0 +1 @@ +warning_16x16 \ No newline at end of file diff --git a/src/sql/media/objectTypes/AggregateFunction.svg b/src/sql/media/objectTypes/AggregateFunction.svg new file mode 100644 index 0000000000..391a5c7d51 --- /dev/null +++ b/src/sql/media/objectTypes/AggregateFunction.svg @@ -0,0 +1 @@ +aggregate_valued_function_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/AggregateFunctionParameter_Input.svg b/src/sql/media/objectTypes/AggregateFunctionParameter_Input.svg new file mode 100644 index 0000000000..ca513eb390 --- /dev/null +++ b/src/sql/media/objectTypes/AggregateFunctionParameter_Input.svg @@ -0,0 +1 @@ +input_parameter_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/AggregateFunctionParameter_Output.svg b/src/sql/media/objectTypes/AggregateFunctionParameter_Output.svg new file mode 100644 index 0000000000..c1283bafa7 --- /dev/null +++ b/src/sql/media/objectTypes/AggregateFunctionParameter_Output.svg @@ -0,0 +1 @@ +output_parameter_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/AggregateFunctionParameter_Return.svg b/src/sql/media/objectTypes/AggregateFunctionParameter_Return.svg new file mode 100644 index 0000000000..605bcd4f38 --- /dev/null +++ b/src/sql/media/objectTypes/AggregateFunctionParameter_Return.svg @@ -0,0 +1 @@ +parameter_return_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ApplicationRole.svg b/src/sql/media/objectTypes/ApplicationRole.svg new file mode 100644 index 0000000000..0ff7f2d4aa --- /dev/null +++ b/src/sql/media/objectTypes/ApplicationRole.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/Assembly.svg b/src/sql/media/objectTypes/Assembly.svg new file mode 100644 index 0000000000..45d9cbe893 --- /dev/null +++ b/src/sql/media/objectTypes/Assembly.svg @@ -0,0 +1 @@ +assemblyfile_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/AsymmetricKey.svg b/src/sql/media/objectTypes/AsymmetricKey.svg new file mode 100644 index 0000000000..2dc5e0a328 --- /dev/null +++ b/src/sql/media/objectTypes/AsymmetricKey.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/BrokerPriority.svg b/src/sql/media/objectTypes/BrokerPriority.svg new file mode 100644 index 0000000000..e2679e8dce --- /dev/null +++ b/src/sql/media/objectTypes/BrokerPriority.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/Certificate.svg b/src/sql/media/objectTypes/Certificate.svg new file mode 100644 index 0000000000..a1a0d6a5af --- /dev/null +++ b/src/sql/media/objectTypes/Certificate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/Column.svg b/src/sql/media/objectTypes/Column.svg new file mode 100644 index 0000000000..96bfe6960c --- /dev/null +++ b/src/sql/media/objectTypes/Column.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/ColumnEncryptionKey.svg b/src/sql/media/objectTypes/ColumnEncryptionKey.svg new file mode 100644 index 0000000000..4db7cba3dd --- /dev/null +++ b/src/sql/media/objectTypes/ColumnEncryptionKey.svg @@ -0,0 +1 @@ +column_encryption_key_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ColumnMasterKey.svg b/src/sql/media/objectTypes/ColumnMasterKey.svg new file mode 100644 index 0000000000..d41baae320 --- /dev/null +++ b/src/sql/media/objectTypes/ColumnMasterKey.svg @@ -0,0 +1 @@ +column_master_key_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Constraint.svg b/src/sql/media/objectTypes/Constraint.svg new file mode 100644 index 0000000000..3b712c48c1 --- /dev/null +++ b/src/sql/media/objectTypes/Constraint.svg @@ -0,0 +1 @@ +default_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Contract.svg b/src/sql/media/objectTypes/Contract.svg new file mode 100644 index 0000000000..1db9650be2 --- /dev/null +++ b/src/sql/media/objectTypes/Contract.svg @@ -0,0 +1 @@ +contract_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Database.svg b/src/sql/media/objectTypes/Database.svg new file mode 100644 index 0000000000..60fcdb4136 --- /dev/null +++ b/src/sql/media/objectTypes/Database.svg @@ -0,0 +1 @@ +database_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/DatabaseAndQueueEventNotification.svg b/src/sql/media/objectTypes/DatabaseAndQueueEventNotification.svg new file mode 100644 index 0000000000..d44f335cbb --- /dev/null +++ b/src/sql/media/objectTypes/DatabaseAndQueueEventNotification.svg @@ -0,0 +1 @@ +queue_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/DatabaseAuditSpecification.svg b/src/sql/media/objectTypes/DatabaseAuditSpecification.svg new file mode 100644 index 0000000000..3651e7f9f6 --- /dev/null +++ b/src/sql/media/objectTypes/DatabaseAuditSpecification.svg @@ -0,0 +1 @@ +DatabaseAuditSpecification_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/DatabaseEncryptionKey.svg b/src/sql/media/objectTypes/DatabaseEncryptionKey.svg new file mode 100644 index 0000000000..4db7cba3dd --- /dev/null +++ b/src/sql/media/objectTypes/DatabaseEncryptionKey.svg @@ -0,0 +1 @@ +column_encryption_key_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/DatabaseRole.svg b/src/sql/media/objectTypes/DatabaseRole.svg new file mode 100644 index 0000000000..37ce0e01fe --- /dev/null +++ b/src/sql/media/objectTypes/DatabaseRole.svg @@ -0,0 +1 @@ +database_roles_16x_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/DatabaseScopedCredential.svg b/src/sql/media/objectTypes/DatabaseScopedCredential.svg new file mode 100644 index 0000000000..ad25290cae --- /dev/null +++ b/src/sql/media/objectTypes/DatabaseScopedCredential.svg @@ -0,0 +1 @@ +credential_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/DatabaseTrigger.svg b/src/sql/media/objectTypes/DatabaseTrigger.svg new file mode 100644 index 0000000000..7ee9b1bf93 --- /dev/null +++ b/src/sql/media/objectTypes/DatabaseTrigger.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/src/sql/media/objectTypes/Database_Unavailable.svg b/src/sql/media/objectTypes/Database_Unavailable.svg new file mode 100644 index 0000000000..904a92d137 --- /dev/null +++ b/src/sql/media/objectTypes/Database_Unavailable.svg @@ -0,0 +1 @@ +Availability_Database_Offline_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/DefaultIcon.svg b/src/sql/media/objectTypes/DefaultIcon.svg new file mode 100644 index 0000000000..404d4cca71 --- /dev/null +++ b/src/sql/media/objectTypes/DefaultIcon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/ExternalDataSource.svg b/src/sql/media/objectTypes/ExternalDataSource.svg new file mode 100644 index 0000000000..0b7ed977dc --- /dev/null +++ b/src/sql/media/objectTypes/ExternalDataSource.svg @@ -0,0 +1 @@ +ExternalDataSource_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ExternalFileFormat.svg b/src/sql/media/objectTypes/ExternalFileFormat.svg new file mode 100644 index 0000000000..4814fa0254 --- /dev/null +++ b/src/sql/media/objectTypes/ExternalFileFormat.svg @@ -0,0 +1 @@ +ExternalFileFormat_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/FileGroupFile.svg b/src/sql/media/objectTypes/FileGroupFile.svg new file mode 100644 index 0000000000..5927f529ce --- /dev/null +++ b/src/sql/media/objectTypes/FileGroupFile.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/Folder.svg b/src/sql/media/objectTypes/Folder.svg new file mode 100644 index 0000000000..8442363a76 --- /dev/null +++ b/src/sql/media/objectTypes/Folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/FullTextCatalog.svg b/src/sql/media/objectTypes/FullTextCatalog.svg new file mode 100644 index 0000000000..a2502482c4 --- /dev/null +++ b/src/sql/media/objectTypes/FullTextCatalog.svg @@ -0,0 +1 @@ +fulltext_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/FullTextStopList.svg b/src/sql/media/objectTypes/FullTextStopList.svg new file mode 100644 index 0000000000..da9af29b64 --- /dev/null +++ b/src/sql/media/objectTypes/FullTextStopList.svg @@ -0,0 +1 @@ +full_text_stoplist_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Index.svg b/src/sql/media/objectTypes/Index.svg new file mode 100644 index 0000000000..0ec26272d1 --- /dev/null +++ b/src/sql/media/objectTypes/Index.svg @@ -0,0 +1 @@ +Index_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Key_ForeignKey.svg b/src/sql/media/objectTypes/Key_ForeignKey.svg new file mode 100644 index 0000000000..f021221d03 --- /dev/null +++ b/src/sql/media/objectTypes/Key_ForeignKey.svg @@ -0,0 +1 @@ +foreign_key_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Key_PrimaryKey.svg b/src/sql/media/objectTypes/Key_PrimaryKey.svg new file mode 100644 index 0000000000..d41baae320 --- /dev/null +++ b/src/sql/media/objectTypes/Key_PrimaryKey.svg @@ -0,0 +1 @@ +column_master_key_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Key_UniqueKey.svg b/src/sql/media/objectTypes/Key_UniqueKey.svg new file mode 100644 index 0000000000..ee35fc356b --- /dev/null +++ b/src/sql/media/objectTypes/Key_UniqueKey.svg @@ -0,0 +1 @@ +unique_key_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/MasterKey.svg b/src/sql/media/objectTypes/MasterKey.svg new file mode 100644 index 0000000000..d41baae320 --- /dev/null +++ b/src/sql/media/objectTypes/MasterKey.svg @@ -0,0 +1 @@ +column_master_key_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/MessageType.svg b/src/sql/media/objectTypes/MessageType.svg new file mode 100644 index 0000000000..43bcf97c70 --- /dev/null +++ b/src/sql/media/objectTypes/MessageType.svg @@ -0,0 +1 @@ +message_type_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/PartitionFunction.svg b/src/sql/media/objectTypes/PartitionFunction.svg new file mode 100644 index 0000000000..e487be2056 --- /dev/null +++ b/src/sql/media/objectTypes/PartitionFunction.svg @@ -0,0 +1 @@ +partition_function_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/PartitionScheme.svg b/src/sql/media/objectTypes/PartitionScheme.svg new file mode 100644 index 0000000000..18e5287ec4 --- /dev/null +++ b/src/sql/media/objectTypes/PartitionScheme.svg @@ -0,0 +1 @@ +partition_scheme_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Queue.svg b/src/sql/media/objectTypes/Queue.svg new file mode 100644 index 0000000000..d44f335cbb --- /dev/null +++ b/src/sql/media/objectTypes/Queue.svg @@ -0,0 +1 @@ +queue_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/RemoteServiceBinding.svg b/src/sql/media/objectTypes/RemoteServiceBinding.svg new file mode 100644 index 0000000000..5f79856ba7 --- /dev/null +++ b/src/sql/media/objectTypes/RemoteServiceBinding.svg @@ -0,0 +1 @@ +remote_service_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Route.svg b/src/sql/media/objectTypes/Route.svg new file mode 100644 index 0000000000..29f354d2f9 --- /dev/null +++ b/src/sql/media/objectTypes/Route.svg @@ -0,0 +1 @@ +route_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ScalarValuedFunction.svg b/src/sql/media/objectTypes/ScalarValuedFunction.svg new file mode 100644 index 0000000000..391a5c7d51 --- /dev/null +++ b/src/sql/media/objectTypes/ScalarValuedFunction.svg @@ -0,0 +1 @@ +aggregate_valued_function_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ScalarValuedFunctionParameter_Input.svg b/src/sql/media/objectTypes/ScalarValuedFunctionParameter_Input.svg new file mode 100644 index 0000000000..ca513eb390 --- /dev/null +++ b/src/sql/media/objectTypes/ScalarValuedFunctionParameter_Input.svg @@ -0,0 +1 @@ +input_parameter_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ScalarValuedFunctionParameter_Output.svg b/src/sql/media/objectTypes/ScalarValuedFunctionParameter_Output.svg new file mode 100644 index 0000000000..c1283bafa7 --- /dev/null +++ b/src/sql/media/objectTypes/ScalarValuedFunctionParameter_Output.svg @@ -0,0 +1 @@ +output_parameter_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ScalarValuedFunctionParameter_Return.svg b/src/sql/media/objectTypes/ScalarValuedFunctionParameter_Return.svg new file mode 100644 index 0000000000..605bcd4f38 --- /dev/null +++ b/src/sql/media/objectTypes/ScalarValuedFunctionParameter_Return.svg @@ -0,0 +1 @@ +parameter_return_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Schema.svg b/src/sql/media/objectTypes/Schema.svg new file mode 100644 index 0000000000..c3fed3a829 --- /dev/null +++ b/src/sql/media/objectTypes/Schema.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff --git a/src/sql/media/objectTypes/SearchPropertyList.svg b/src/sql/media/objectTypes/SearchPropertyList.svg new file mode 100644 index 0000000000..ff0c53ce1f --- /dev/null +++ b/src/sql/media/objectTypes/SearchPropertyList.svg @@ -0,0 +1 @@ +search_property_list_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SecurityPolicy.svg b/src/sql/media/objectTypes/SecurityPolicy.svg new file mode 100644 index 0000000000..b5bce63682 --- /dev/null +++ b/src/sql/media/objectTypes/SecurityPolicy.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/sql/media/objectTypes/Sequence.svg b/src/sql/media/objectTypes/Sequence.svg new file mode 100644 index 0000000000..52306a7078 --- /dev/null +++ b/src/sql/media/objectTypes/Sequence.svg @@ -0,0 +1 @@ +sequence_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Server.svg b/src/sql/media/objectTypes/Server.svg new file mode 100644 index 0000000000..7b9f7eb690 --- /dev/null +++ b/src/sql/media/objectTypes/Server.svg @@ -0,0 +1 @@ +add_certificate_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelCredential.svg b/src/sql/media/objectTypes/ServerLevelCredential.svg new file mode 100644 index 0000000000..ad25290cae --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelCredential.svg @@ -0,0 +1 @@ +credential_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelCryptographicProvider.svg b/src/sql/media/objectTypes/ServerLevelCryptographicProvider.svg new file mode 100644 index 0000000000..939821d5cd --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelCryptographicProvider.svg @@ -0,0 +1 @@ +cryptographicprovider_enabled_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelEndpoint.svg b/src/sql/media/objectTypes/ServerLevelEndpoint.svg new file mode 100644 index 0000000000..6f70ba42ba --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelEndpoint.svg @@ -0,0 +1 @@ +endpoint_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelLinkedServer.svg b/src/sql/media/objectTypes/ServerLevelLinkedServer.svg new file mode 100644 index 0000000000..6b8bd933b0 --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelLinkedServer.svg @@ -0,0 +1 @@ +linked_server_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelLinkedServerLogin.svg b/src/sql/media/objectTypes/ServerLevelLinkedServerLogin.svg new file mode 100644 index 0000000000..4e893751be --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelLinkedServerLogin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelLinkedServerLogin_Disabled.svg b/src/sql/media/objectTypes/ServerLevelLinkedServerLogin_Disabled.svg new file mode 100644 index 0000000000..96d3847ffe --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelLinkedServerLogin_Disabled.svg @@ -0,0 +1 @@ +login_disabled_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelLogin.svg b/src/sql/media/objectTypes/ServerLevelLogin.svg new file mode 100644 index 0000000000..4e893751be --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelLogin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelLogin_Disabled.svg b/src/sql/media/objectTypes/ServerLevelLogin_Disabled.svg new file mode 100644 index 0000000000..96d3847ffe --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelLogin_Disabled.svg @@ -0,0 +1 @@ +login_disabled_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelServerAudit.svg b/src/sql/media/objectTypes/ServerLevelServerAudit.svg new file mode 100644 index 0000000000..912d318ec3 --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelServerAudit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelServerAuditSpecification.svg b/src/sql/media/objectTypes/ServerLevelServerAuditSpecification.svg new file mode 100644 index 0000000000..ecdad25138 --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelServerAuditSpecification.svg @@ -0,0 +1 @@ +ServerAuditSpecification_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelServerRole.svg b/src/sql/media/objectTypes/ServerLevelServerRole.svg new file mode 100644 index 0000000000..ad25290cae --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelServerRole.svg @@ -0,0 +1 @@ +credential_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/ServerLevelServerTrigger.svg b/src/sql/media/objectTypes/ServerLevelServerTrigger.svg new file mode 100644 index 0000000000..1285979557 --- /dev/null +++ b/src/sql/media/objectTypes/ServerLevelServerTrigger.svg @@ -0,0 +1 @@ +Trigger_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Service.svg b/src/sql/media/objectTypes/Service.svg new file mode 100644 index 0000000000..73d6515447 --- /dev/null +++ b/src/sql/media/objectTypes/Service.svg @@ -0,0 +1 @@ +broker_service_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SqlLogFile.svg b/src/sql/media/objectTypes/SqlLogFile.svg new file mode 100644 index 0000000000..5927f529ce --- /dev/null +++ b/src/sql/media/objectTypes/SqlLogFile.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/Statistic.svg b/src/sql/media/objectTypes/Statistic.svg new file mode 100644 index 0000000000..a7d8413272 --- /dev/null +++ b/src/sql/media/objectTypes/Statistic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/StoredProcedure.svg b/src/sql/media/objectTypes/StoredProcedure.svg new file mode 100644 index 0000000000..6a52787f15 --- /dev/null +++ b/src/sql/media/objectTypes/StoredProcedure.svg @@ -0,0 +1 @@ +stored_procedure_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/StoredProcedureParameter_Input.svg b/src/sql/media/objectTypes/StoredProcedureParameter_Input.svg new file mode 100644 index 0000000000..ca513eb390 --- /dev/null +++ b/src/sql/media/objectTypes/StoredProcedureParameter_Input.svg @@ -0,0 +1 @@ +input_parameter_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/StoredProcedureParameter_Output.svg b/src/sql/media/objectTypes/StoredProcedureParameter_Output.svg new file mode 100644 index 0000000000..c1283bafa7 --- /dev/null +++ b/src/sql/media/objectTypes/StoredProcedureParameter_Output.svg @@ -0,0 +1 @@ +output_parameter_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/StoredProcedureParameter_Return.svg b/src/sql/media/objectTypes/StoredProcedureParameter_Return.svg new file mode 100644 index 0000000000..605bcd4f38 --- /dev/null +++ b/src/sql/media/objectTypes/StoredProcedureParameter_Return.svg @@ -0,0 +1 @@ +parameter_return_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SymmetricKey.svg b/src/sql/media/objectTypes/SymmetricKey.svg new file mode 100644 index 0000000000..d4e745a7e0 --- /dev/null +++ b/src/sql/media/objectTypes/SymmetricKey.svg @@ -0,0 +1 @@ +symmetric_key_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Synonym.svg b/src/sql/media/objectTypes/Synonym.svg new file mode 100644 index 0000000000..5e79c56fe5 --- /dev/null +++ b/src/sql/media/objectTypes/Synonym.svg @@ -0,0 +1 @@ +synonym_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemApproximateNumeric.svg b/src/sql/media/objectTypes/SystemApproximateNumeric.svg new file mode 100644 index 0000000000..07398b2acb --- /dev/null +++ b/src/sql/media/objectTypes/SystemApproximateNumeric.svg @@ -0,0 +1 @@ +udt_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemBinaryString.svg b/src/sql/media/objectTypes/SystemBinaryString.svg new file mode 100644 index 0000000000..07398b2acb --- /dev/null +++ b/src/sql/media/objectTypes/SystemBinaryString.svg @@ -0,0 +1 @@ +udt_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemCharacterString.svg b/src/sql/media/objectTypes/SystemCharacterString.svg new file mode 100644 index 0000000000..07398b2acb --- /dev/null +++ b/src/sql/media/objectTypes/SystemCharacterString.svg @@ -0,0 +1 @@ +udt_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemClrDataType.svg b/src/sql/media/objectTypes/SystemClrDataType.svg new file mode 100644 index 0000000000..07398b2acb --- /dev/null +++ b/src/sql/media/objectTypes/SystemClrDataType.svg @@ -0,0 +1 @@ +udt_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemContract.svg b/src/sql/media/objectTypes/SystemContract.svg new file mode 100644 index 0000000000..1db9650be2 --- /dev/null +++ b/src/sql/media/objectTypes/SystemContract.svg @@ -0,0 +1 @@ +contract_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemDateAndTime.svg b/src/sql/media/objectTypes/SystemDateAndTime.svg new file mode 100644 index 0000000000..07398b2acb --- /dev/null +++ b/src/sql/media/objectTypes/SystemDateAndTime.svg @@ -0,0 +1 @@ +udt_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemExactNumeric.svg b/src/sql/media/objectTypes/SystemExactNumeric.svg new file mode 100644 index 0000000000..07398b2acb --- /dev/null +++ b/src/sql/media/objectTypes/SystemExactNumeric.svg @@ -0,0 +1 @@ +udt_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemMessageType.svg b/src/sql/media/objectTypes/SystemMessageType.svg new file mode 100644 index 0000000000..43bcf97c70 --- /dev/null +++ b/src/sql/media/objectTypes/SystemMessageType.svg @@ -0,0 +1 @@ +message_type_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemOtherDataType.svg b/src/sql/media/objectTypes/SystemOtherDataType.svg new file mode 100644 index 0000000000..07398b2acb --- /dev/null +++ b/src/sql/media/objectTypes/SystemOtherDataType.svg @@ -0,0 +1 @@ +udt_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemQueue.svg b/src/sql/media/objectTypes/SystemQueue.svg new file mode 100644 index 0000000000..d44f335cbb --- /dev/null +++ b/src/sql/media/objectTypes/SystemQueue.svg @@ -0,0 +1 @@ +queue_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemService.svg b/src/sql/media/objectTypes/SystemService.svg new file mode 100644 index 0000000000..73d6515447 --- /dev/null +++ b/src/sql/media/objectTypes/SystemService.svg @@ -0,0 +1 @@ +broker_service_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemSpatialDataType.svg b/src/sql/media/objectTypes/SystemSpatialDataType.svg new file mode 100644 index 0000000000..07398b2acb --- /dev/null +++ b/src/sql/media/objectTypes/SystemSpatialDataType.svg @@ -0,0 +1 @@ +udt_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/SystemUnicodeCharacterString.svg b/src/sql/media/objectTypes/SystemUnicodeCharacterString.svg new file mode 100644 index 0000000000..07398b2acb --- /dev/null +++ b/src/sql/media/objectTypes/SystemUnicodeCharacterString.svg @@ -0,0 +1 @@ +udt_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Table.svg b/src/sql/media/objectTypes/Table.svg new file mode 100644 index 0000000000..025d9bd136 --- /dev/null +++ b/src/sql/media/objectTypes/Table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/TableValuedFunction.svg b/src/sql/media/objectTypes/TableValuedFunction.svg new file mode 100644 index 0000000000..fed14f4c85 --- /dev/null +++ b/src/sql/media/objectTypes/TableValuedFunction.svg @@ -0,0 +1 @@ +table_valued_function \ No newline at end of file diff --git a/src/sql/media/objectTypes/TableValuedFunctionParameter_Input.svg b/src/sql/media/objectTypes/TableValuedFunctionParameter_Input.svg new file mode 100644 index 0000000000..ca513eb390 --- /dev/null +++ b/src/sql/media/objectTypes/TableValuedFunctionParameter_Input.svg @@ -0,0 +1 @@ +input_parameter_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/TableValuedFunctionParameter_Output.svg b/src/sql/media/objectTypes/TableValuedFunctionParameter_Output.svg new file mode 100644 index 0000000000..c1283bafa7 --- /dev/null +++ b/src/sql/media/objectTypes/TableValuedFunctionParameter_Output.svg @@ -0,0 +1 @@ +output_parameter_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/TableValuedFunctionParameter_Return.svg b/src/sql/media/objectTypes/TableValuedFunctionParameter_Return.svg new file mode 100644 index 0000000000..605bcd4f38 --- /dev/null +++ b/src/sql/media/objectTypes/TableValuedFunctionParameter_Return.svg @@ -0,0 +1 @@ +parameter_return_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Table_Temporal.svg b/src/sql/media/objectTypes/Table_Temporal.svg new file mode 100644 index 0000000000..c6550c0885 --- /dev/null +++ b/src/sql/media/objectTypes/Table_Temporal.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/sql/media/objectTypes/Trigger.svg b/src/sql/media/objectTypes/Trigger.svg new file mode 100644 index 0000000000..1285979557 --- /dev/null +++ b/src/sql/media/objectTypes/Trigger.svg @@ -0,0 +1 @@ +Trigger_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/Trigger_Disabled.svg b/src/sql/media/objectTypes/Trigger_Disabled.svg new file mode 100644 index 0000000000..43bf361a2c --- /dev/null +++ b/src/sql/media/objectTypes/Trigger_Disabled.svg @@ -0,0 +1 @@ +Trigger_disabled_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/User.svg b/src/sql/media/objectTypes/User.svg new file mode 100644 index 0000000000..37ce0e01fe --- /dev/null +++ b/src/sql/media/objectTypes/User.svg @@ -0,0 +1 @@ +database_roles_16x_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/UserDefinedDataType.svg b/src/sql/media/objectTypes/UserDefinedDataType.svg new file mode 100644 index 0000000000..0b8e52cbfd --- /dev/null +++ b/src/sql/media/objectTypes/UserDefinedDataType.svg @@ -0,0 +1 @@ +UserDefinedType_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/UserDefinedTableType.svg b/src/sql/media/objectTypes/UserDefinedTableType.svg new file mode 100644 index 0000000000..025d9bd136 --- /dev/null +++ b/src/sql/media/objectTypes/UserDefinedTableType.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/UserDefinedTableTypeColumn.svg b/src/sql/media/objectTypes/UserDefinedTableTypeColumn.svg new file mode 100644 index 0000000000..96bfe6960c --- /dev/null +++ b/src/sql/media/objectTypes/UserDefinedTableTypeColumn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/UserDefinedTableTypeConstraint.svg b/src/sql/media/objectTypes/UserDefinedTableTypeConstraint.svg new file mode 100644 index 0000000000..3b712c48c1 --- /dev/null +++ b/src/sql/media/objectTypes/UserDefinedTableTypeConstraint.svg @@ -0,0 +1 @@ +default_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/UserDefinedType.svg b/src/sql/media/objectTypes/UserDefinedType.svg new file mode 100644 index 0000000000..0b8e52cbfd --- /dev/null +++ b/src/sql/media/objectTypes/UserDefinedType.svg @@ -0,0 +1 @@ +UserDefinedType_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/User_Disabled.svg b/src/sql/media/objectTypes/User_Disabled.svg new file mode 100644 index 0000000000..5a45b068ad --- /dev/null +++ b/src/sql/media/objectTypes/User_Disabled.svg @@ -0,0 +1 @@ +database_user_disabled_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/View.svg b/src/sql/media/objectTypes/View.svg new file mode 100644 index 0000000000..404d4cca71 --- /dev/null +++ b/src/sql/media/objectTypes/View.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/media/objectTypes/XmlSchemaCollection.svg b/src/sql/media/objectTypes/XmlSchemaCollection.svg new file mode 100644 index 0000000000..ee8c4fa7af --- /dev/null +++ b/src/sql/media/objectTypes/XmlSchemaCollection.svg @@ -0,0 +1 @@ +Script_16x \ No newline at end of file diff --git a/src/sql/media/objectTypes/objecttypes.css b/src/sql/media/objectTypes/objecttypes.css new file mode 100644 index 0000000000..89fd9d93ed --- /dev/null +++ b/src/sql/media/objectTypes/objecttypes.css @@ -0,0 +1,640 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.vs .icon.table, +.vs-dark .icon.table, +.hc-black .icon.table { + background: url("Table.svg") center center no-repeat; +} + +.vs .icon.stored-procedure, +.vs-dark .icon.stored-procedure, +.hc-black .icon.stored-procedure { + background: url("StoredProcedure.svg") center center no-repeat; +} + +.vs .icon.view, +.vs-dark .icon.view, +.hc-black .icon.view { + background: url("View.svg") center center no-repeat; +} + +.vs .icon.aggregatefunction, +.vs-dark .icon.aggregatefunction, +.hc-black .icon.aggregatefunction { + background: url("AggregateFunction.svg") center center no-repeat; +} + +.vs .icon.aggregatefunctionparameter_input, +.vs-dark .icon.aggregatefunctionparameter_input, +.hc-black .icon.aggregatefunctionparameter_input { + background: url("AggregateFunctionParameter_Input.svg") center center no-repeat; +} + +.vs .icon.aggregatefunctionparameter_output, +.vs-dark .icon.aggregatefunctionparameter_output, +.hc-black .icon.aggregatefunctionparameter_output { + background: url("AggregateFunctionParameter_Output.svg") center center no-repeat; +} + +.vs .icon.aggregatefunctionparameter_return, +.vs-dark .icon.aggregatefunctionparameter_return, +.hc-black .icon.aggregatefunctionparameter_return { + background: url("AggregateFunctionParameter_Return.svg") center center no-repeat; +} + +.vs .icon.applicationrole, +.vs-dark .icon.applicationrole, +.hc-black .icon.applicationrole { + background: url("ApplicationRole.svg") center center no-repeat; +} + +.vs .icon.assembly, +.vs-dark .icon.assembly, +.hc-black .icon.assembly { + background: url("Assembly.svg") center center no-repeat; +} + +.vs .icon.asymmetrickey, +.vs-dark .icon.asymmetrickey, +.hc-black .icon.asymmetrickey { + background: url("AsymmetricKey.svg") center center no-repeat; +} + +.vs .icon.brokerpriority, +.vs-dark .icon.brokerpriority, +.hc-black .icon.brokerpriority { + background: url("BrokerPriority.svg") center center no-repeat; +} + +.vs .icon.certificate, +.vs-dark .icon.certificate, +.hc-black .icon.certificate { + background: url("Certificate.svg") center center no-repeat; +} + +.vs .icon.column, +.vs-dark .icon.column, +.hc-black .icon.column { + background: url("Column.svg") center center no-repeat; +} + +.vs .icon.columnencryptionkey, +.vs-dark .icon.columnencryptionkey, +.hc-black .icon.columnencryptionkey { + background: url("ColumnEncryptionKey.svg") center center no-repeat; +} + +.vs .icon.columnmasterkey, +.vs-dark .icon.columnmasterkey, +.hc-black .icon.columnmasterkey { + background: url("ColumnMasterKey.svg") center center no-repeat; +} + +.vs .icon.constraint, +.vs-dark .icon.constraint, +.hc-black .icon.constraint { + background: url("Constraint.svg") center center no-repeat; +} + +.vs .icon.contract, +.vs-dark .icon.contract, +.hc-black .icon.contract { + background: url("Contract.svg") center center no-repeat; +} + +.vs .icon.database, +.vs-dark .icon.database, +.hc-black .icon.database { + background: url("Database.svg") center center no-repeat; +} + +.vs .icon.database_unavailable, +.vs-dark .icon.database_unavailable, +.hc-black .icon.database_unavailable { + background: url("Database_Unavailable.svg") center center no-repeat; +} + +.vs .icon.databaseandqueueeventnotification, +.vs-dark .icon.databaseandqueueeventnotification, +.hc-black .icon.databaseandqueueeventnotification { + background: url("DatabaseAndQueueEventNotification.svg") center center no-repeat; +} + +.vs .icon.databaseauditspecification, +.vs-dark .icon.databaseauditspecification, +.hc-black .icon.databaseauditspecification { + background: url("DatabaseAuditSpecification.svg") center center no-repeat; +} + +.vs .icon.databaseencryptionkey, +.vs-dark .icon.databaseencryptionkey, +.hc-black .icon.databaseencryptionkey { + background: url("DatabaseEncryptionKey.svg") center center no-repeat; +} + +.vs .icon.databaserole, +.vs-dark .icon.databaserole, +.hc-black .icon.databaserole { + background: url("DatabaseRole.svg") center center no-repeat; +} + +.vs .icon.databasescopedcredential, +.vs-dark .icon.databasescopedcredential, +.hc-black .icon.databasescopedcredential { + background: url("DatabaseScopedCredential.svg") center center no-repeat; +} + +.vs .icon.databasetrigger, +.vs-dark .icon.databasetrigger, +.hc-black .icon.databasetrigger { + background: url("DatabaseTrigger.svg") center center no-repeat; +} + +.vs .icon.defaulticon, +.vs-dark .icon.defaulticon, +.hc-black .icon.defaulticon { + background: url("DefaultIcon.svg") center center no-repeat; +} + +.vs .icon.externaldatasource, +.vs-dark .icon.externaldatasource, +.hc-black .icon.externaldatasource { + background: url("ExternalDataSource.svg") center center no-repeat; +} + +.vs .icon.externalfileformat, +.vs-dark .icon.externalfileformat, +.hc-black .icon.externalfileformat { + background: url("ExternalFileFormat.svg") center center no-repeat; +} + +.vs .icon.filegroupfile, +.vs-dark .icon.filegroupfile, +.hc-black .icon.filegroupfile { + background: url("FileGroupFile.svg") center center no-repeat; +} + +.vs .icon.folder, +.vs-dark .icon.folder, +.hc-black .icon.folder { + background: url("Folder.svg") center center no-repeat; +} + +.vs .icon.fulltextcatalog, +.vs-dark .icon.fulltextcatalog, +.hc-black .icon.fulltextcatalog { + background: url("FullTextCatalog.svg") center center no-repeat; +} + +.vs .icon.fulltextstoplist, +.vs-dark .icon.fulltextstoplist, +.hc-black .icon.fulltextstoplist { + background: url("FullTextStopList.svg") center center no-repeat; +} + +.vs .icon.index, +.vs-dark .icon.index, +.hc-black .icon.index { + background: url("Index.svg") center center no-repeat; +} + +.vs .icon.key_foreignkey, +.vs-dark .icon.key_foreignkey, +.hc-black .icon.key_foreignkey { + background: url("Key_ForeignKey.svg") center center no-repeat; +} + +.vs .icon.key_primarykey, +.vs-dark .icon.key_primarykey, +.hc-black .icon.key_primarykey { + background: url("Key_PrimaryKey.svg") center center no-repeat; +} + +.vs .icon.key_uniquekey, +.vs-dark .icon.key_uniquekey, +.hc-black .icon.key_uniquekey { + background: url("Key_UniqueKey.svg") center center no-repeat; +} + +.vs .icon.masterkey, +.vs-dark .icon.masterkey, +.hc-black .icon.masterkey { + background: url("MasterKey.svg") center center no-repeat; +} + +.vs .icon.messagetype, +.vs-dark .icon.messagetype, +.hc-black .icon.messagetype { + background: url("MessageType.svg") center center no-repeat; +} + +.vs .icon.objecttypes.css, +.vs-dark .icon.objecttypes.css, +.hc-black .icon.objecttypes.css { + background: url("objecttypes.css") center center no-repeat; +} + +.vs .icon.partitionfunction, +.vs-dark .icon.partitionfunction, +.hc-black .icon.partitionfunction { + background: url("PartitionFunction.svg") center center no-repeat; +} + +.vs .icon.partitionscheme, +.vs-dark .icon.partitionscheme, +.hc-black .icon.partitionscheme { + background: url("PartitionScheme.svg") center center no-repeat; +} + +.vs .icon.queue, +.vs-dark .icon.queue, +.hc-black .icon.queue { + background: url("Queue.svg") center center no-repeat; +} + +.vs .icon.remoteservicebinding, +.vs-dark .icon.remoteservicebinding, +.hc-black .icon.remoteservicebinding { + background: url("RemoteServiceBinding.svg") center center no-repeat; +} + +.vs .icon.route, +.vs-dark .icon.route, +.hc-black .icon.route { + background: url("Route.svg") center center no-repeat; +} + +.vs .icon.scalarvaluedfunction, +.vs-dark .icon.scalarvaluedfunction, +.hc-black .icon.scalarvaluedfunction { + background: url("ScalarValuedFunction.svg") center center no-repeat; +} + +.vs .icon.scalarvaluedfunctionparameter_input, +.vs-dark .icon.scalarvaluedfunctionparameter_input, +.hc-black .icon.scalarvaluedfunctionparameter_input { + background: url("ScalarValuedFunctionParameter_Input.svg") center center no-repeat; +} + +.vs .icon.scalarvaluedfunctionparameter_output, +.vs-dark .icon.scalarvaluedfunctionparameter_output, +.hc-black .icon.scalarvaluedfunctionparameter_output { + background: url("ScalarValuedFunctionParameter_Output.svg") center center no-repeat; +} + +.vs .icon.scalarvaluedfunctionparameter_return, +.vs-dark .icon.scalarvaluedfunctionparameter_return, +.hc-black .icon.scalarvaluedfunctionparameter_return { + background: url("ScalarValuedFunctionParameter_Return.svg") center center no-repeat; +} + +.vs .icon.schema, +.vs-dark .icon.schema, +.hc-black .icon.schema { + background: url("Schema.svg") center center no-repeat; +} + +.vs .icon.searchpropertylist, +.vs-dark .icon.searchpropertylist, +.hc-black .icon.searchpropertylist { + background: url("SearchPropertyList.svg") center center no-repeat; +} + +.vs .icon.securitypolicy, +.vs-dark .icon.securitypolicy, +.hc-black .icon.securitypolicy { + background: url("SecurityPolicy.svg") center center no-repeat; +} + +.vs .icon.sequence, +.vs-dark .icon.sequence, +.hc-black .icon.sequence { + background: url("Sequence.svg") center center no-repeat; +} + +.vs .icon.server, +.vs-dark .icon.server, +.hc-black .icon.server { + background: url("Server.svg") center center no-repeat; +} + +.vs .icon.serverlevelcredential, +.vs-dark .icon.serverlevelcredential, +.hc-black .icon.serverlevelcredential { + background: url("ServerLevelCredential.svg") center center no-repeat; +} + +.vs .icon.serverlevelcryptographicprovider, +.vs-dark .icon.serverlevelcryptographicprovider, +.hc-black .icon.serverlevelcryptographicprovider { + background: url("ServerLevelCryptographicProvider.svg") center center no-repeat; +} + +.vs .icon.serverlevelendpoint, +.vs-dark .icon.serverlevelendpoint, +.hc-black .icon.serverlevelendpoint { + background: url("ServerLevelEndpoint.svg") center center no-repeat; +} + +.vs .icon.serverlevellinkedserver, +.vs-dark .icon.serverlevellinkedserver, +.hc-black .icon.serverlevellinkedserver { + background: url("ServerLevelLinkedServer.svg") center center no-repeat; +} + +.vs .icon.serverlevellinkedserverlogin, +.vs-dark .icon.serverlevellinkedserverlogin, +.hc-black .icon.serverlevellinkedserverlogin { + background: url("ServerLevelLinkedServerLogin.svg") center center no-repeat; +} + +.vs .icon.serverlevellinkedserverlogin_disabled, +.vs-dark .icon.serverlevellinkedserverlogin_disabled, +.hc-black .icon.serverlevellinkedserverlogin_disabled { + background: url("ServerLevelLinkedServerLogin_Disabled.svg") center center no-repeat; +} + +.vs .icon.serverlevellogin, +.vs-dark .icon.serverlevellogin, +.hc-black .icon.serverlevellogin { + background: url("ServerLevelLogin.svg") center center no-repeat; +} + +.vs .icon.serverlevellogin_disabled, +.vs-dark .icon.serverlevellogin_disabled, +.hc-black .icon.serverlevellogin_disabled { + background: url("ServerLevelLogin_Disabled.svg") center center no-repeat; +} + +.vs .icon.serverlevelserveraudit, +.vs-dark .icon.serverlevelserveraudit, +.hc-black .icon.serverlevelserveraudit { + background: url("ServerLevelServerAudit.svg") center center no-repeat; +} + +.vs .icon.serverlevelserverauditspecification, +.vs-dark .icon.serverlevelserverauditspecification, +.hc-black .icon.serverlevelserverauditspecification { + background: url("ServerLevelServerAuditSpecification.svg") center center no-repeat; +} + +.vs .icon.serverlevelserverrole, +.vs-dark .icon.serverlevelserverrole, +.hc-black .icon.serverlevelserverrole { + background: url("ServerLevelServerRole.svg") center center no-repeat; +} + +.vs .icon.serverlevelservertrigger, +.vs-dark .icon.serverlevelservertrigger, +.hc-black .icon.serverlevelservertrigger { + background: url("ServerLevelServerTrigger.svg") center center no-repeat; +} + +.vs .icon.service, +.vs-dark .icon.service, +.hc-black .icon.service { + background: url("Service.svg") center center no-repeat; +} + +.vs .icon.sqllogfile, +.vs-dark .icon.sqllogfile, +.hc-black .icon.sqllogfile { + background: url("SqlLogFile.svg") center center no-repeat; +} + +.vs .icon.statistic, +.vs-dark .icon.statistic, +.hc-black .icon.statistic { + background: url("Statistic.svg") center center no-repeat; +} + +.vs .icon.storedprocedure, +.vs-dark .icon.storedprocedure, +.hc-black .icon.storedprocedure { + background: url("StoredProcedure.svg") center center no-repeat; +} + +.vs .icon.storedprocedureparameter_input, +.vs-dark .icon.storedprocedureparameter_input, +.hc-black .icon.storedprocedureparameter_input { + background: url("StoredProcedureParameter_Input.svg") center center no-repeat; +} + +.vs .icon.storedprocedureparameter_output, +.vs-dark .icon.storedprocedureparameter_output, +.hc-black .icon.storedprocedureparameter_output { + background: url("StoredProcedureParameter_Output.svg") center center no-repeat; +} + +.vs .icon.storedprocedureparameter_return, +.vs-dark .icon.storedprocedureparameter_return, +.hc-black .icon.storedprocedureparameter_return { + background: url("StoredProcedureParameter_Return.svg") center center no-repeat; +} + +.vs .icon.symmetrickey, +.vs-dark .icon.symmetrickey, +.hc-black .icon.symmetrickey { + background: url("SymmetricKey.svg") center center no-repeat; +} + +.vs .icon.synonym, +.vs-dark .icon.synonym, +.hc-black .icon.synonym { + background: url("Synonym.svg") center center no-repeat; +} + +.vs .icon.systemapproximatenumeric, +.vs-dark .icon.systemapproximatenumeric, +.hc-black .icon.systemapproximatenumeric { + background: url("SystemApproximateNumeric.svg") center center no-repeat; +} + +.vs .icon.systembinarystring, +.vs-dark .icon.systembinarystring, +.hc-black .icon.systembinarystring { + background: url("SystemBinaryString.svg") center center no-repeat; +} + +.vs .icon.systemcharacterstring, +.vs-dark .icon.systemcharacterstring, +.hc-black .icon.systemcharacterstring { + background: url("SystemCharacterString.svg") center center no-repeat; +} + +.vs .icon.systemclrdatatype, +.vs-dark .icon.systemclrdatatype, +.hc-black .icon.systemclrdatatype { + background: url("SystemClrDataType.svg") center center no-repeat; +} + +.vs .icon.systemcontract, +.vs-dark .icon.systemcontract, +.hc-black .icon.systemcontract { + background: url("SystemContract.svg") center center no-repeat; +} + +.vs .icon.systemdateandtime, +.vs-dark .icon.systemdateandtime, +.hc-black .icon.systemdateandtime { + background: url("SystemDateAndTime.svg") center center no-repeat; +} + +.vs .icon.systemexactnumeric, +.vs-dark .icon.systemexactnumeric, +.hc-black .icon.systemexactnumeric { + background: url("SystemExactNumeric.svg") center center no-repeat; +} + +.vs .icon.systemmessagetype, +.vs-dark .icon.systemmessagetype, +.hc-black .icon.systemmessagetype { + background: url("SystemMessageType.svg") center center no-repeat; +} + +.vs .icon.systemotherdatatype, +.vs-dark .icon.systemotherdatatype, +.hc-black .icon.systemotherdatatype { + background: url("SystemOtherDataType.svg") center center no-repeat; +} + +.vs .icon.systemqueue, +.vs-dark .icon.systemqueue, +.hc-black .icon.systemqueue { + background: url("SystemQueue.svg") center center no-repeat; +} + +.vs .icon.systemservice, +.vs-dark .icon.systemservice, +.hc-black .icon.systemservice { + background: url("SystemService.svg") center center no-repeat; +} + +.vs .icon.systemspatialdatatype, +.vs-dark .icon.systemspatialdatatype, +.hc-black .icon.systemspatialdatatype { + background: url("SystemSpatialDataType.svg") center center no-repeat; +} + +.vs .icon.systemunicodecharacterstring, +.vs-dark .icon.systemunicodecharacterstring, +.hc-black .icon.systemunicodecharacterstring { + background: url("SystemUnicodeCharacterString.svg") center center no-repeat; +} + +.vs .icon.tablevaluedfunction, +.vs-dark .icon.tablevaluedfunction, +.hc-black .icon.tablevaluedfunction { + background: url("TableValuedFunction.svg") center center no-repeat; +} + +.vs .icon.tablevaluedfunctionparameter_input, +.vs-dark .icon.tablevaluedfunctionparameter_input, +.hc-black .icon.tablevaluedfunctionparameter_input { + background: url("TableValuedFunctionParameter_Input.svg") center center no-repeat; +} + +.vs .icon.tablevaluedfunctionparameter_output, +.vs-dark .icon.tablevaluedfunctionparameter_output, +.hc-black .icon.tablevaluedfunctionparameter_output { + background: url("TableValuedFunctionParameter_Output.svg") center center no-repeat; +} + +.vs .icon.tablevaluedfunctionparameter_return, +.vs-dark .icon.tablevaluedfunctionparameter_return, +.hc-black .icon.tablevaluedfunctionparameter_return { + background: url("TableValuedFunctionParameter_Return.svg") center center no-repeat; +} + +.vs .icon.table_temporal, +.vs-dark .icon.table_temporal, +.hc-black .icon.table_temporal { + background: url("Table_Temporal.svg") center center no-repeat; +} + +.vs .icon.trigger, +.vs-dark .icon.trigger, +.hc-black .icon.trigger { + background: url("Trigger.svg") center center no-repeat; +} + +.vs .icon.trigger_disabled, +.vs-dark .icon.trigger_disabled, +.hc-black .icon.trigger_disabled { + background: url("Trigger_Disabled.svg") center center no-repeat; +} + +.vs .icon.user, +.vs-dark .icon.user, +.hc-black .icon.user { + background: url("User.svg") center center no-repeat; +} + +.vs .icon.user_disabled, +.vs-dark .icon.user_disabled, +.hc-black .icon.user_disabled { + background: url("User_Disabled.svg") center center no-repeat; +} + +.vs .icon.userdefineddatatype, +.vs-dark .icon.userdefineddatatype, +.hc-black .icon.userdefineddatatype { + background: url("UserDefinedDataType.svg") center center no-repeat; +} + +.vs .icon.userdefinedtabletype, +.vs-dark .icon.userdefinedtabletype, +.hc-black .icon.userdefinedtabletype { + background: url("UserDefinedTableType.svg") center center no-repeat; +} + +.vs .icon.userdefinedtabletypecolumn, +.vs-dark .icon.userdefinedtabletypecolumn, +.hc-black .icon.userdefinedtabletypecolumn { + background: url("UserDefinedTableTypeColumn.svg") center center no-repeat; +} + +.vs .icon.userdefinedtabletypeconstraint, +.vs-dark .icon.userdefinedtabletypeconstraint, +.hc-black .icon.userdefinedtabletypeconstraint { + background: url("UserDefinedTableTypeConstraint.svg") center center no-repeat; +} + +.vs .icon.userdefinedtype, +.vs-dark .icon.userdefinedtype, +.hc-black .icon.userdefinedtype { + background: url("UserDefinedType.svg") center center no-repeat; +} + +.vs .icon.view, +.vs-dark .icon.view, +.hc-black .icon.view { + background: url("View.svg") center center no-repeat; +} + +.vs .icon.xmlschemacollection, +.vs-dark .icon.xmlschemacollection, +.hc-black .icon.xmlschemacollection { + background: url("XmlSchemaCollection.svg") center center no-repeat; +} + +.vs .icon.filegroup, +.vs-dark .icon.filegroup, +.hc-black .icon.filegroup { + background: url("DefaultIcon.svg") center center no-repeat; +} + +.vs .icon.securitypolicie, +.vs-dark .icon.securitypolicie, +.hc-black .icon.securitypolicie { + background: url("DefaultIcon.svg") center center no-repeat; +} + +.vs .icon.historytable, +.vs-dark .icon.historytable, +.hc-black .icon.historytable { + background: url("DefaultIcon.svg") center center no-repeat; +} \ No newline at end of file diff --git a/src/sql/parts/accountManagement/accountDialog/accountDialog.ts b/src/sql/parts/accountManagement/accountDialog/accountDialog.ts new file mode 100644 index 0000000000..8a390e37bf --- /dev/null +++ b/src/sql/parts/accountManagement/accountDialog/accountDialog.ts @@ -0,0 +1,230 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/accountDialog'; +import 'vs/css!sql/parts/accountManagement/common/media/accountActions'; +import * as DOM from 'vs/base/browser/dom'; +import { SplitView } from 'vs/base/browser/ui/splitview/splitview'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import Event, { Emitter } from 'vs/base/common/event'; +import { localize } from 'vs/nls'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachListStyler, attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { ActionRunner } from 'vs/base/common/actions'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; + +import * as data from 'data'; +import { Modal } from 'sql/base/browser/ui/modal/modal'; +import { attachModalDialogStyler } from 'sql/common/theme/styler'; +import { AccountViewModel } from 'sql/parts/accountManagement/accountDialog/accountViewModel'; +import { AddAccountAction } from 'sql/parts/accountManagement/common/accountActions'; +import { AccountListRenderer, AccountListDelegate } from 'sql/parts/accountManagement/common/accountListRenderer'; +import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes'; +import { FixedListView } from 'sql/platform/views/fixedListView'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; + +export class AccountDialog extends Modal { + public static ACCOUNTLIST_HEIGHT = 77; + + public viewModel: AccountViewModel; + + // MEMBER VARIABLES //////////////////////////////////////////////////// + private _providerViews: { [providerId: string]: FixedListView } = {}; + + private _closeButton: Button; + private _delegate: AccountListDelegate; + private _accountRenderer: AccountListRenderer; + private _actionRunner: ActionRunner; + private _splitView: SplitView; + private _container: HTMLElement; + + // EVENTING //////////////////////////////////////////////////////////// + private _onAddAccountErrorEmitter: Emitter; + public get onAddAccountErrorEvent(): Event { return this._onAddAccountErrorEmitter.event; } + + private _onCloseEmitter: Emitter; + public get onCloseEvent(): Event { return this._onCloseEmitter.event; } + + constructor( + @IPartService partService: IPartService, + @IThemeService private _themeService: IThemeService, + @IListService private _listService: IListService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IContextMenuService private _contextMenuService: IContextMenuService, + @IKeybindingService private _keybindingService: IKeybindingService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super( + localize('linkedAccounts', 'Linked Accounts'), + TelemetryKeys.Accounts, + partService, + telemetryService, + contextKeyService, + { hasSpinner: true } + ); + let self = this; + + this._delegate = new AccountListDelegate(AccountDialog.ACCOUNTLIST_HEIGHT); + this._accountRenderer = this._instantiationService.createInstance(AccountListRenderer); + this._actionRunner = new ActionRunner(); + + // Setup the event emitters + this._onAddAccountErrorEmitter = new Emitter(); + this._onCloseEmitter = new Emitter(); + + // Create the view model and wire up the events + this.viewModel = this._instantiationService.createInstance(AccountViewModel); + this.viewModel.addProviderEvent(arg => { self.addProvider(arg); }); + this.viewModel.removeProviderEvent(arg => { self.removeProvider(arg); }); + this.viewModel.updateAccountListEvent(arg => { self.updateProviderAccounts(arg); }); + } + + // MODAL OVERRIDE METHODS ////////////////////////////////////////////// + protected layout(height?: number): void { + // Ignore height as it's a subcomponent being laid out + this._splitView.layout(DOM.getContentHeight(this._container)); + } + + public render() { + let self = this; + + super.render(); + attachModalDialogStyler(this, this._themeService); + this._closeButton = this.addFooterButton(localize('close', 'Close'), () => this.close()); + this.registerListeners(); + + // Load the initial contents of the view model + this.viewModel.initialize() + .then(addedProviders => { + for (let addedProvider of addedProviders) { + self.addProvider(addedProvider); + } + }); + } + + protected renderBody(container: HTMLElement) { + this._container = container; + let viewBody = DOM.$('div.account-view'); + DOM.append(container, viewBody); + this._splitView = new SplitView(viewBody); + } + + private registerListeners(): void { + // Theme styler + this._register(attachButtonStyler(this._closeButton, this._themeService)); + } + + /* Overwrite escape key behavior */ + protected onClose() { + this.close(); + } + + /* Overwrite enter key behavior */ + protected onAccept() { + this.close(); + } + + public close() { + this._onCloseEmitter.fire(); + this.hide(); + } + + public open() { + this.show(); + } + + public dispose(): void { + super.dispose(); + for (let key in this._providerViews) { + this._providerViews[key].dispose(); + delete this._providerViews[key]; + } + } + + // PRIVATE HELPERS ///////////////////////////////////////////////////// + private addProvider(newProvider: AccountProviderAddedEventParams) { + let self = this; + + // Skip adding the provider if it already exists + if (this._providerViews[newProvider.addedProvider.id]) { + return; + } + + // Account provider doesn't exist, so add it + // Create a scoped add account action + let addAccountAction = this._instantiationService.createInstance( + AddAccountAction, + newProvider.addedProvider.id + ); + addAccountAction.addAccountCompleteEvent(() => { self.hideSpinner(); }); + addAccountAction.addAccountErrorEvent(msg => { self._onAddAccountErrorEmitter.fire(msg); }); + addAccountAction.addAccountStartEvent(() => { self.showSpinner(); }); + + // Create a fixed list view for the account provider + let providerViewContainer = DOM.$('.provider-view'); + let accountList = new List(providerViewContainer, this._delegate, [this._accountRenderer]); + let providerView = new FixedListView( + undefined, + false, + newProvider.addedProvider.displayName, + accountList, + providerViewContainer, + 22, + [addAccountAction], + this._actionRunner, + this._contextMenuService, + this._keybindingService, + this._themeService + ); + + // Append the list view to the split view + this._splitView.addView(providerView); + this._register(attachListStyler(accountList, this._themeService)); + this._register(this._listService.register(accountList)); + this._splitView.layout(DOM.getContentHeight(this._container)); + + // Set the initial items of the list + providerView.updateList(newProvider.initialAccounts); + this.layout(); + + // Store the view for the provider + this._providerViews[newProvider.addedProvider.id] = providerView; + } + + private removeProvider(removedProvider: data.AccountProviderMetadata) { + // Skip removing the provider if it doesn't exist + let providerView = this._providerViews[removedProvider.id]; + if (!providerView) { + return; + } + + // Remove the list view from the split view + this._splitView.removeView(providerView); + this._splitView.layout(DOM.getContentHeight(this._container)); + + // Remove the list view from our internal map + delete this._providerViews[removedProvider.id]; + this.layout(); + } + + private updateProviderAccounts(args: UpdateAccountListEventParams) { + let providerMapping = this._providerViews[args.providerId]; + if (!providerMapping) { + return; + } + providerMapping.updateList(args.accountList); + this.layout(); + } +} diff --git a/src/sql/parts/accountManagement/accountDialog/accountDialogController.ts b/src/sql/parts/accountManagement/accountDialog/accountDialogController.ts new file mode 100644 index 0000000000..812a47c1f1 --- /dev/null +++ b/src/sql/parts/accountManagement/accountDialog/accountDialogController.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import Severity from 'vs/base/common/severity'; +import { AccountDialog } from 'sql/parts/accountManagement/accountDialog/accountDialog'; +import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import { localize } from 'vs/nls'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +export class AccountDialogController { + + // MEMBER VARIABLES //////////////////////////////////////////////////// + private _addAccountErrorTitle = localize('addAccountErrorTitle', 'Error adding account'); + + private _accountDialog: AccountDialog; + public get accountDialog(): AccountDialog { return this._accountDialog; } + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService, + @IErrorMessageService private _errorMessageService: IErrorMessageService + ) { } + + /** + * Open account dialog + */ + public openAccountDialog(): void { + let self = this; + + // Create a new dialog if one doesn't exist + if (!this._accountDialog) { + this._accountDialog = this._instantiationService.createInstance(AccountDialog); + this._accountDialog.onAddAccountErrorEvent(msg => { self.handleOnAddAccountError(msg); }); + this._accountDialog.onCloseEvent(() => { self.handleOnClose(); }); + this._accountDialog.render(); + } + + // Open the dialog + this._accountDialog.open(); + } + + // PRIVATE HELPERS ///////////////////////////////////////////////////// + private handleOnClose(): void { } + + private handleOnAddAccountError(msg: string): void { + this._errorMessageService.showDialog(Severity.Error, this._addAccountErrorTitle, msg); + } +} diff --git a/src/sql/parts/accountManagement/accountDialog/accountViewModel.ts b/src/sql/parts/accountManagement/accountDialog/accountViewModel.ts new file mode 100644 index 0000000000..97f8ceb0d3 --- /dev/null +++ b/src/sql/parts/accountManagement/accountDialog/accountViewModel.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; +import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes'; + +/** + * View model for account dialog + */ +export class AccountViewModel { + // EVENTING /////////////////////////////////////////////////////// + private _addProviderEmitter: Emitter; + public get addProviderEvent(): Event { return this._addProviderEmitter.event; } + + private _removeProviderEmitter: Emitter; + public get removeProviderEvent(): Event { return this._removeProviderEmitter.event; } + + private _updateAccountListEmitter: Emitter; + public get updateAccountListEvent(): Event { return this._updateAccountListEmitter.event; } + + constructor(@IAccountManagementService private _accountManagementService: IAccountManagementService) { + let self = this; + + // Create our event emitters + this._addProviderEmitter = new Emitter(); + this._removeProviderEmitter = new Emitter(); + this._updateAccountListEmitter = new Emitter(); + + // Register handlers for any changes to the providers or accounts + this._accountManagementService.addAccountProviderEvent(arg => self._addProviderEmitter.fire(arg)); + this._accountManagementService.removeAccountProviderEvent(arg => self._removeProviderEmitter.fire(arg)); + this._accountManagementService.updateAccountListEvent(arg => self._updateAccountListEmitter.fire(arg)); + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + /** + * Loads an initial list of account providers and accounts from the account management service + * and fires an event after each provider/accounts has been loaded. + * + */ + public initialize(): Thenable { + let self = this; + + // Load a baseline of the account provider metadata and accounts + // 1) Get all the providers from the account management service + // 2) For each provider, get the accounts + // 3) Build parameters to add a provider and return it + return this._accountManagementService.getAccountProviderMetadata() + .then( + (providers: data.AccountProviderMetadata[]) => { + let promises = providers.map(provider => { + return self._accountManagementService.getAccountsForProvider(provider.id) + .then( + accounts => { + addedProvider: provider, + initialAccounts: accounts + }, + () => { /* Swallow failures at getting accounts, we'll just hide that provider */ }); + }); + return Promise.all(promises); + }, + () => { + /* Swallow failures and just pretend we don't have any providers */ + return []; + } + ); + } +} \ No newline at end of file diff --git a/src/sql/parts/accountManagement/accountDialog/media/accountDialog.css b/src/sql/parts/accountManagement/accountDialog/media/accountDialog.css new file mode 100644 index 0000000000..fe9746b1c3 --- /dev/null +++ b/src/sql/parts/accountManagement/accountDialog/media/accountDialog.css @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.account-view .monaco-split-view .split-view-view .header { + padding-right: 12px; +} + +.account-view .provider-view .list-row { + padding: 12px; +} + +.account-view .provider-view .list-row .icon { + flex: 0 0 50px; + height: 50px; + width: 50px; + background-size: 50px; +} + +.account-view .provider-view .list-row .icon .badge { + position: absolute; + top: 43px; + left: 43px; + overflow: hidden; + width: 22px; + height: 22px; +} + +.account-view .provider-view .list-row .icon .badge .badge-content { + width: 22px; + height: 22px; + background-size: 22px; +} + +.account-view .provider-view .list-row .actions-container { + flex: 0 0 50px; +} + +.account-view .provider-view .list-row .actions-container .action-item .action-label { + width: 16px; + margin-left: 20px; + margin-right: 10px; + background-size: 16px; + background-position: center; + background-repeat: no-repeat; +} + +.account-view .provider-view .list-row .actions-container { + display: none; +} + +.account-view .provider-view .monaco-list .monaco-list-row:hover .list-row .actions-container, +.account-view .provider-view .monaco-list .monaco-list-row.selected .list-row .actions-container, +.account-view .provider-view .monaco-list .monaco-list-row.focused .list-row .actions-container{ + display: block; +} + +.account-view .split-view-view .header .title { + display: flex; + justify-content: flex-start; + flex: 1 1 auto; +} + +.account-view .split-view-view .header .actions { + display: flex; + justify-content: flex-end; +} + +.account-view .split-view-view .header .actions .action-item .action-label { + width: 30px; + background-size: 16px; + background-position: center center; + background-repeat: no-repeat; + margin-right: 0; + height: 22px; +} \ No newline at end of file diff --git a/src/sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem.ts b/src/sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem.ts new file mode 100644 index 0000000000..faf96d3b21 --- /dev/null +++ b/src/sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/accountListStatusbarItem'; +import { Action, IAction } from 'vs/base/common/actions'; +import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle'; +import { $, append } from 'vs/base/browser/dom'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { localize } from 'vs/nls'; +import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; + +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; + +export class AccountListStatusbarItem implements IStatusbarItem { + private _rootElement: HTMLElement; + private _iconElement: HTMLElement; + private _toDispose: IDisposable[]; + private _manageLinkedAccountAction: IAction; + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService, + @IAccountManagementService private _accountManagementService: IAccountManagementService + ) { + this._toDispose = []; + } + + public render(container: HTMLElement): IDisposable { + // Create root element for account list + this._rootElement = append(container, $('.linked-account-staus')); + this._rootElement.title = ManageLinkedAccountAction.LABEL; + this._rootElement.onclick = () => this._onClick(); + + this._iconElement = append(this._rootElement, $('a.linked-account-status-selection')); + + return combinedDisposable(this._toDispose); + } + + private _onClick() { + if (!this._manageLinkedAccountAction) { + this._manageLinkedAccountAction = this._instantiationService.createInstance(ManageLinkedAccountAction, ManageLinkedAccountAction.ID, ManageLinkedAccountAction.LABEL); + } + this._manageLinkedAccountAction.run().done(null, onUnexpectedError); + } +} + +export class ManageLinkedAccountAction extends Action { + public static ID = 'sql.action.accounts.manageLinkedAccount'; + public static LABEL = localize('manageLinedAccounts', 'Manage Linked Accounts'); + + constructor(id: string, label: string, + @IAccountManagementService protected _accountManagementService: IAccountManagementService) { + super(id, label); + } + + public run(): TPromise { + return new TPromise(() => this._accountManagementService.openAccountListDialog()); + } +} diff --git a/src/sql/parts/accountManagement/accountListStatusbar/media/accountListStatusbarItem.css b/src/sql/parts/accountManagement/accountListStatusbar/media/accountListStatusbarItem.css new file mode 100644 index 0000000000..e9838a69ef --- /dev/null +++ b/src/sql/parts/accountManagement/accountListStatusbar/media/accountListStatusbarItem.css @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.statusbar-item .linked-account-staus a.linked-account-status-selection { + background: url('accounts_statusbar_inverse.svg') center center no-repeat; + background-size: 12px; + height: 12px !important; + width: 12px; +} diff --git a/src/sql/parts/accountManagement/accountListStatusbar/media/accounts_statusbar_inverse.svg b/src/sql/parts/accountManagement/accountListStatusbar/media/accounts_statusbar_inverse.svg new file mode 100644 index 0000000000..1d2fb6fefd --- /dev/null +++ b/src/sql/parts/accountManagement/accountListStatusbar/media/accounts_statusbar_inverse.svg @@ -0,0 +1 @@ +accounts_statusbar_inverse_11x11 \ No newline at end of file diff --git a/src/sql/parts/accountManagement/accountPicker/accountPicker.ts b/src/sql/parts/accountManagement/accountPicker/accountPicker.ts new file mode 100644 index 0000000000..64f3218a0b --- /dev/null +++ b/src/sql/parts/accountManagement/accountPicker/accountPicker.ts @@ -0,0 +1,232 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import 'vs/css!./media/accountPicker'; +import { Builder } from 'vs/base/browser/builder'; +import * as DOM from 'vs/base/browser/dom'; +import Event, { Emitter } from 'vs/base/common/event'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { IDropdownOptions } from 'vs/base/browser/ui/dropdown/dropdown'; +import { IListEvent } from 'vs/base/browser/ui/list/list'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { buttonBackground } from 'vs/platform/theme/common/colorRegistry'; +import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; + +import * as data from 'data'; +import { DropdownList } from 'sql/base/browser/ui/dropdownList/dropdownList'; +import { attachDropdownStyler } from 'sql/common/theme/styler'; +import { AddAccountAction, RefreshAccountAction } from 'sql/parts/accountManagement/common/accountActions'; +import { AccountPickerListRenderer, AccountListDelegate } from 'sql/parts/accountManagement/common/accountListRenderer'; +import { AccountPickerViewModel } from 'sql/parts/accountManagement/accountPicker/accountPickerViewModel'; + +export class AccountPicker extends Disposable { + public static ACCOUNTPICKERLIST_HEIGHT = 47; + public viewModel: AccountPickerViewModel; + private _accountList: List; + private _rootElement: HTMLElement; + private _refreshContainer: HTMLElement; + private _listContainer: HTMLElement; + private _dropdown: DropdownList; + + // EVENTING //////////////////////////////////////////////////////////// + private _addAccountCompleteEmitter: Emitter; + public get addAccountCompleteEvent(): Event { return this._addAccountCompleteEmitter.event; } + + private _addAccountErrorEmitter: Emitter; + public get addAccountErrorEvent(): Event { return this._addAccountErrorEmitter.event; } + + private _addAccountStartEmitter: Emitter; + public get addAccountStartEvent(): Event { return this._addAccountStartEmitter.event; } + + private _onAccountSelectionChangeEvent: Emitter; + public get onAccountSelectionChangeEvent(): Event { return this._onAccountSelectionChangeEvent.event; } + + constructor( + private _providerId: string, + @IWorkbenchThemeService private _themeService: IWorkbenchThemeService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IContextViewService private _contextViewService: IContextViewService + ) { + super(); + + // Create event emitters + this._addAccountCompleteEmitter = new Emitter(); + this._addAccountErrorEmitter = new Emitter(); + this._addAccountStartEmitter = new Emitter(); + this._onAccountSelectionChangeEvent = new Emitter(); + + // Create an account list + let delegate = new AccountListDelegate(AccountPicker.ACCOUNTPICKERLIST_HEIGHT); + let accountRenderer = new AccountPickerListRenderer(); + this._listContainer = DOM.$('div.account-list-container'); + this._accountList = new List(this._listContainer, delegate, [accountRenderer]); + this._register(attachListStyler(this._accountList, this._themeService)); + + // Create the view model, wire up the events, and initialize with baseline data + this.viewModel = this._instantiationService.createInstance(AccountPickerViewModel, this._providerId); + this.viewModel.updateAccountListEvent(arg => { + if (arg.providerId === this._providerId) { + this.updateAccountList(arg.accountList); + } + }); + + this.createAccountPickerComponent(); + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + /** + * Render account picker + */ + public render(container: HTMLElement): void { + DOM.append(container, this._rootElement); + } + + private createAccountPickerComponent() { + this._rootElement = DOM.$('div.account-picker-container'); + + // Create a dropdown for account picker + let option: IDropdownOptions = { + contextViewProvider: this._contextViewService, + labelRenderer: (container) => this.renderLabel(container) + }; + + // Create the add account action + let addAccountAction = this._instantiationService.createInstance(AddAccountAction, this._providerId); + addAccountAction.addAccountCompleteEvent(() => this._addAccountCompleteEmitter.fire()); + addAccountAction.addAccountErrorEvent((msg) => this._addAccountErrorEmitter.fire(msg)); + addAccountAction.addAccountStartEvent(() => this._addAccountStartEmitter.fire()); + + this._dropdown = this._register(new DropdownList(this._rootElement, option, this._listContainer, this._accountList, this._themeService, addAccountAction)); + this._register(attachDropdownStyler(this._dropdown, this._themeService)); + this._register(this._accountList.onSelectionChange((e: IListEvent) => { + if (e.elements.length === 1) { + this._dropdown.renderLabel(); + this.onAccountSelectionChange(e.elements[0]); + } + })); + + // Create refresh account action + this._refreshContainer = DOM.append(this._rootElement, DOM.$('div.refresh-container')); + DOM.append(this._refreshContainer, DOM.$('div.icon warning')); + let actionBar = new ActionBar(this._refreshContainer, { animated: false }); + actionBar.push(new RefreshAccountAction(RefreshAccountAction.ID, RefreshAccountAction.LABEL), { icon: false, label: true }); + + if (this._accountList.length > 0) { + this._accountList.setSelection([0]); + this.onAccountSelectionChange(this._accountList.getSelectedElements()[0]); + } else { + new Builder(this._refreshContainer).hide(); + } + + this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme(e))); + this.updateTheme(this._themeService.getColorTheme()); + + // Load the initial contents of the view model + this.viewModel.initialize() + .then((accounts: data.Account[]) => { + this.updateAccountList(accounts); + }); + } + + public dispose() { + super.dispose(); + if (this._accountList) { + this._accountList.dispose(); + } + } + + // PRIVATE HELPERS ///////////////////////////////////////////////////// + private onAccountSelectionChange(account: data.Account) { + this.viewModel.selectedAccount = account; + if (account && account.isStale) { + new Builder(this._refreshContainer).show(); + } else { + new Builder(this._refreshContainer).hide(); + } + this._onAccountSelectionChangeEvent.fire(account); + } + + private renderLabel(container: HTMLElement): IDisposable { + if (container.hasChildNodes()) { + for (let i = 0; i < container.childNodes.length; i++) { + container.removeChild(container.childNodes.item(i)); + } + } + + let selectedAccounts = this._accountList.getSelectedElements(); + let account = selectedAccounts ? selectedAccounts[0] : null; + if (account) { + const badge = DOM.$('div.badge'); + const row = DOM.append(container, DOM.$('div.selected-account-container')); + const icon = DOM.append(row, DOM.$('div.icon')); + DOM.append(icon, badge); + const badgeContent = DOM.append(badge, DOM.$('div.badge-content')); + const label = DOM.append(row, DOM.$('div.label')); + + icon.className = 'icon'; + // Set the account icon + icon.style.background = `url('data:${account.displayInfo.contextualLogo.light}')`; + // TODO: Pick between the light and dark logo + label.innerText = account.displayInfo.displayName + ' (' + account.displayInfo.contextualDisplayName + ')'; + + if (account.isStale) { + badgeContent.className = 'badge-content icon warning-badge'; + } else { + badgeContent.className = 'badge-content'; + } + } else { + const row = DOM.append(container, DOM.$('div.no-account-container')); + row.innerText = AddAccountAction.LABEL + '...'; + } + return null; + } + + private updateAccountList(accounts: data.Account[]): void { + // keep the selection to the current one + let selectedElements = this._accountList.getSelectedElements(); + + // find selected index + let selectedIndex: number; + if (selectedElements.length > 0 && accounts.length > 0) { + selectedIndex = accounts.findIndex((account) => { + return (account.key.accountId === selectedElements[0].key.accountId); + }); + } + + // Replace the existing list with the new one + this._accountList.splice(0, this._accountList.length, accounts); + + if (this._accountList.length > 0) { + if (selectedIndex && selectedIndex !== -1) { + this._accountList.setSelection([selectedIndex]); + } else { + this._accountList.setSelection([0]); + } + } else { + // if the account is empty, re-render dropdown label + this.onAccountSelectionChange(undefined); + this._dropdown.renderLabel(); + } + + this._accountList.layout(this._accountList.contentHeight); + } + + /** + * Update theming that is specific to account picker + */ + private updateTheme(theme: IColorTheme): void { + let linkColor = theme.getColor(buttonBackground); + let link = linkColor ? linkColor.toString() : null; + this._refreshContainer.style.color = link; + if (this._refreshContainer) { + this._refreshContainer.style.color = link; + } + } +} \ No newline at end of file diff --git a/src/sql/parts/accountManagement/accountPicker/accountPickerService.ts b/src/sql/parts/accountManagement/accountPicker/accountPickerService.ts new file mode 100644 index 0000000000..ecefefc8e5 --- /dev/null +++ b/src/sql/parts/accountManagement/accountPicker/accountPickerService.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import Event, { Emitter } from 'vs/base/common/event'; +import * as data from 'data'; + +import { IAccountPickerService } from 'sql/parts/accountManagement/common/interfaces'; +import { AccountPicker } from 'sql/parts/accountManagement/accountPicker/accountPicker'; + +export class AccountPickerService implements IAccountPickerService { + _serviceBrand: any; + + private _accountPicker: AccountPicker; + + // EVENTING //////////////////////////////////////////////////////////// + private _addAccountCompleteEmitter: Emitter; + public get addAccountCompleteEvent(): Event { return this._addAccountCompleteEmitter.event; } + + private _addAccountErrorEmitter: Emitter; + public get addAccountErrorEvent(): Event { return this._addAccountErrorEmitter.event; } + + private _addAccountStartEmitter: Emitter; + public get addAccountStartEvent(): Event { return this._addAccountStartEmitter.event; } + + private _onAccountSelectionChangeEvent: Emitter; + public get onAccountSelectionChangeEvent(): Event { return this._onAccountSelectionChangeEvent.event; } + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService + ) { + // Create event emitters + this._addAccountCompleteEmitter = new Emitter(); + this._addAccountErrorEmitter = new Emitter(); + this._addAccountStartEmitter = new Emitter(); + this._onAccountSelectionChangeEvent = new Emitter(); + } + + /** + * Get selected account + */ + public get selectedAccount(): data.Account { + return this._accountPicker.viewModel.selectedAccount; + } + + /** + * Render account picker + */ + public renderAccountPicker(container: HTMLElement): void { + if (!this._accountPicker) { + // TODO: expand support to multiple providers + const providerId: string = 'azurePublicCloud'; + this._accountPicker = this._instantiationService.createInstance(AccountPicker, providerId); + } + + this._accountPicker.addAccountCompleteEvent(() => this._addAccountCompleteEmitter.fire()); + this._accountPicker.addAccountErrorEvent((msg) => this._addAccountErrorEmitter.fire(msg)); + this._accountPicker.addAccountStartEvent(() => this._addAccountStartEmitter.fire()); + this._accountPicker.onAccountSelectionChangeEvent((account) => this._onAccountSelectionChangeEvent.fire(account)); + this._accountPicker.render(container); + } +} diff --git a/src/sql/parts/accountManagement/accountPicker/accountPickerViewModel.ts b/src/sql/parts/accountManagement/accountPicker/accountPickerViewModel.ts new file mode 100644 index 0000000000..a321ef03d6 --- /dev/null +++ b/src/sql/parts/accountManagement/accountPicker/accountPickerViewModel.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as data from 'data'; +import Event, { Emitter } from 'vs/base/common/event'; + +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; +import { UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes'; + +/** + * View model for account picker + */ +export class AccountPickerViewModel { + // EVENTING //////////////////////////////////////////////////////////// + private _updateAccountListEmitter: Emitter; + public get updateAccountListEvent(): Event { return this._updateAccountListEmitter.event; } + + public selectedAccount: data.Account; + + constructor( + private _providerId: string, + @IAccountManagementService private _accountManagementService: IAccountManagementService + ) { + let self = this; + + // Create our event emitters + this._updateAccountListEmitter = new Emitter(); + + // Register handlers for any changes to the accounts + this._accountManagementService.updateAccountListEvent(arg => self._updateAccountListEmitter.fire(arg)); + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + /** + * Loads an initial list of accounts from the account management service + * @return {Thenable} Promise to return the list of accounts + */ + public initialize(): Thenable { + // Load a baseline of the accounts for the provider + return this._accountManagementService.getAccountsForProvider(this._providerId) + .then(null, () => { + // In the event we failed to lookup accounts for the provider, just send + // back an empty collection + return []; + }); + } +} \ No newline at end of file diff --git a/src/sql/parts/accountManagement/accountPicker/media/accountPicker.css b/src/sql/parts/accountManagement/accountPicker/media/accountPicker.css new file mode 100644 index 0000000000..8ee46132a4 --- /dev/null +++ b/src/sql/parts/accountManagement/accountPicker/media/accountPicker.css @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Selected account */ +.selected-account-container { + padding: 6px; + display: flex; + align-items: flex-start; +} + +.selected-account-container .icon { + flex: 0 0 25px; + height: 25px; + width: 25px; +} + +.selected-account-container .label { + flex: 1 1 auto; + padding-left: 10px; + align-self: center; +} + +.selected-account-container .icon { + background-size: 25px; +} + +.selected-account-container .icon .badge { + position: relative; + top: 15px; + left: 15px; + overflow: hidden; + width: 12px; + height: 12px; +} + +.selected-account-container .icon .badge .badge-content { + width: 12px; + height: 12px; + background-size: 12px; +} + +/* A container when the account list is empty */ +.no-account-container { + padding: 6px; + opacity: 0.7; + font-style: italic; +} + +/* Account list */ +.account-list-container .list-row { + padding: 6px; +} + +.account-list-container .list-row .icon { + flex: 0 0 35px; + height: 35px; + width: 35px; + background-size: 35px; +} + +.account-list-container .list-row .icon .badge { + position: relative; + top: 22px; + left: 22px; + overflow: hidden; + width: 15px; + height: 15px; +} + +.account-list-container .list-row .icon .badge .badge-content { + width: 15px; + height: 15px; + background-size: 15px; +} + +/* Refresh link */ +.refresh-container { + padding-left: 15px; + padding-top: 6px; + display: flex; + align-items: flex-start; +} + +.refresh-container .monaco-action-bar .actions-container { + justify-content: flex-start; +} + +.refresh-container .icon { + flex: 0 0 16px; + height: 16px; + width: 16px; +} + +.refresh-container .monaco-action-bar { + flex: 1 1 auto; + margin-left: 10px; +} diff --git a/src/sql/parts/accountManagement/common/accountActions.ts b/src/sql/parts/accountManagement/common/accountActions.ts new file mode 100644 index 0000000000..9490027908 --- /dev/null +++ b/src/sql/parts/accountManagement/common/accountActions.ts @@ -0,0 +1,158 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import Event, { Emitter } from 'vs/base/common/event'; +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { IMessageService, IConfirmation, Severity } from 'vs/platform/message/common/message'; + +import { error } from 'sql/base/common/log'; +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; +import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; + +/** + * Actions to add a new account + */ +export class AddAccountAction extends Action { + // CONSTANTS /////////////////////////////////////////////////////////// + public static ID = 'account.addLinkedAccount'; + public static LABEL = localize('addAccount', 'Add an account'); + + // EVENTING //////////////////////////////////////////////////////////// + private _addAccountCompleteEmitter: Emitter; + public get addAccountCompleteEvent(): Event { return this._addAccountCompleteEmitter.event; } + + private _addAccountErrorEmitter: Emitter; + public get addAccountErrorEvent(): Event { return this._addAccountErrorEmitter.event; } + + private _addAccountStartEmitter: Emitter; + public get addAccountStartEvent(): Event { return this._addAccountStartEmitter.event; } + + constructor( + private _providerId: string, + @IMessageService private _messageService: IMessageService, + @IErrorMessageService private _errorMessageService: IErrorMessageService, + @IAccountManagementService private _accountManagementService: IAccountManagementService + ) { + super(AddAccountAction.ID, AddAccountAction.LABEL); + this.class = 'add-linked-account-action'; + + this._addAccountCompleteEmitter = new Emitter(); + this._addAccountErrorEmitter = new Emitter(); + this._addAccountStartEmitter = new Emitter(); + } + + public run(): TPromise { + let self = this; + + // Fire the event that we've started adding accounts + this._addAccountStartEmitter.fire(); + + return new TPromise((resolve, reject) => { + self._accountManagementService.addAccount(self._providerId) + .then( + () => { + self._addAccountCompleteEmitter.fire(); + resolve(true); + }, + err => { + error(`Error while adding account: ${err}`); + self._addAccountErrorEmitter.fire(err); + self._addAccountCompleteEmitter.fire(); + reject(err); + } + ); + }); + } +} + +/** + * Actions to remove the account + */ +export class RemoveAccountAction extends Action { + public static ID = 'account.removeAccount'; + public static LABEL = localize('removeAccount', 'Remove account'); + + constructor( + private _account: data.Account, + @IMessageService private _messageService: IMessageService, + @IErrorMessageService private _errorMessageService: IErrorMessageService, + @IAccountManagementService private _accountManagementService: IAccountManagementService + ) { + super(RemoveAccountAction.ID, RemoveAccountAction.LABEL, 'remove-account-action icon remove'); + } + + public run(): TPromise { + let self = this; + + // Ask for Confirm + let confirm: IConfirmation = { + message: localize('confirmRemoveUserAccountMessage', "Are you sure you want to remove '{0}'?", this._account.displayInfo.displayName), + primaryButton: localize('yes', 'Yes'), + secondaryButton: localize('no', 'No'), + type: 'question' + }; + + if (!this._messageService.confirm(confirm)) { + return TPromise.as(false); + } + + return new TPromise((resolve, reject) => { + self._accountManagementService.removeAccount(self._account.key) + .then( + (result) => { resolve(result); }, + (err) => { + // Must handle here as this is an independent action + self._errorMessageService.showDialog(Severity.Error, + localize('removeAccountFailed', 'Failed to remove account'), err); + resolve(false); + } + ); + }); + } +} + +/** + * Actions to apply filter to the account + */ +export class ApplyFilterAction extends Action { + public static ID = 'account.applyFilters'; + public static LABEL = localize('applyFilters', 'Apply Filters'); + + constructor( + id: string, + label: string + ) { + super(id, label, 'apply-filters-action icon filter'); + } + + public run(): TPromise { + // Todo: apply filter to the account + return TPromise.as(true); + } +} + +/** + * Actions to refresh the account + */ +export class RefreshAccountAction extends Action { + public static ID = 'account.refresh'; + public static LABEL = localize('refreshAccount', 'Reenter your credentials'); + + constructor( + id: string, + label: string + ) { + super(id, label, 'refresh-account-action icon refresh'); + } + public run(): TPromise { + // Todo: refresh the account + return TPromise.as(true); + } +} diff --git a/src/sql/parts/accountManagement/common/accountListRenderer.ts b/src/sql/parts/accountManagement/common/accountListRenderer.ts new file mode 100644 index 0000000000..25b3765ca5 --- /dev/null +++ b/src/sql/parts/accountManagement/common/accountListRenderer.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import 'vs/css!sql/parts/accountManagement/common/media/accountListRenderer'; +import 'vs/css!sql/parts/accountManagement/common/media/accountActions'; +import 'vs/css!sql/media/icons/common-icons'; +import * as DOM from 'vs/base/browser/dom'; +import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; +import { ActionBar, IActionOptions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { localize } from 'vs/nls'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +import { RemoveAccountAction, ApplyFilterAction, RefreshAccountAction } from 'sql/parts/accountManagement/common/accountActions'; + +import * as data from 'data'; + +export class AccountListDelegate implements IDelegate { + + constructor( + private _height: number + ) { + } + + public getHeight(element: data.Account): number { + return this._height; + } + + public getTemplateId(element: data.Account): string { + return 'accountListRenderer'; + } +} + +export interface AccountListTemplate { + root: HTMLElement; + icon: HTMLElement; + badgeContent: HTMLElement; + contextualDisplayName: HTMLElement; + label: HTMLElement; + displayName: HTMLElement; + content?: HTMLElement; + actions?: ActionBar; +} + +export class AccountPickerListRenderer implements IRenderer { + public static TEMPLATE_ID = 'accountListRenderer'; + + public get templateId(): string { + return AccountPickerListRenderer.TEMPLATE_ID; + } + + public renderTemplate(container: HTMLElement): AccountListTemplate { + const tableTemplate: AccountListTemplate = Object.create(null); + const badge = DOM.$('div.badge'); + tableTemplate.root = DOM.append(container, DOM.$('div.list-row.account-picker-list')); + tableTemplate.icon = DOM.append(tableTemplate.root, DOM.$('div.icon')); + DOM.append(tableTemplate.icon, badge); + tableTemplate.badgeContent = DOM.append(badge, DOM.$('div.badge-content')); + tableTemplate.label = DOM.append(tableTemplate.root, DOM.$('div.label')); + tableTemplate.contextualDisplayName = DOM.append(tableTemplate.label, DOM.$('div.contextual-display-name')); + tableTemplate.displayName = DOM.append(tableTemplate.label, DOM.$('div.display-name')); + return tableTemplate; + } + + public renderElement(account: data.Account, index: number, templateData: AccountListTemplate): void { + // Set the account icon + templateData.icon.classList.add('account-logo'); + templateData.icon.style.background = `url('data:${account.displayInfo.contextualLogo.light}')`; + // TODO: Pick between the light and dark logo + + templateData.contextualDisplayName.innerText = account.displayInfo.contextualDisplayName; + templateData.displayName.innerText = account.displayInfo.displayName; + if (account.isStale) { + templateData.badgeContent.className = 'badge-content icon warning-badge'; + } else { + templateData.badgeContent.className = 'badge-content'; + } + } + + public disposeTemplate(template: AccountListTemplate): void { + // noop + } +} + +export class AccountListRenderer extends AccountPickerListRenderer { + constructor( + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(); + } + + public get templateId(): string { + return AccountListRenderer.TEMPLATE_ID; + } + + public renderTemplate(container: HTMLElement): AccountListTemplate { + const tableTemplate = super.renderTemplate(container); + tableTemplate.content = DOM.append(tableTemplate.label, DOM.$('div.content')); + tableTemplate.actions = new ActionBar(tableTemplate.root, { animated: false }); + + return tableTemplate; + } + + public renderElement(account: data.Account, index: number, templateData: AccountListTemplate): void { + super.renderElement(account, index, templateData); + if (account.isStale) { + templateData.content.innerText = localize('refreshCredentials', 'You need to refresh the credentials for this account.'); + } else { + templateData.content.innerText = ''; + } + templateData.actions.clear(); + + let actionOptions: IActionOptions = { icon: true, label: false }; + if (account.isStale) { + templateData.actions.push(new RefreshAccountAction(RefreshAccountAction.ID, RefreshAccountAction.LABEL), actionOptions); + } else { + templateData.actions.push(new ApplyFilterAction(ApplyFilterAction.ID, ApplyFilterAction.LABEL), actionOptions); + } + + let removeAction = this._instantiationService.createInstance(RemoveAccountAction, account); + templateData.actions.push(removeAction, actionOptions); + } +} \ No newline at end of file diff --git a/src/sql/parts/accountManagement/common/interfaces.ts b/src/sql/parts/accountManagement/common/interfaces.ts new file mode 100644 index 0000000000..d5da8ea098 --- /dev/null +++ b/src/sql/parts/accountManagement/common/interfaces.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import Event from 'vs/base/common/event'; +import * as data from 'data'; + +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; + +export const SERVICE_ID = 'resourceProviderService'; +export const IResourceProviderService = createDecorator(SERVICE_ID); + +export interface IHandleFirewallRuleResult { + canHandleFirewallRule: boolean; + ipAddress: string; + resourceProviderId: string; +} + +export interface IResourceProviderService { + _serviceBrand: any; + + /** + * Register a resource provider + */ + registerProvider(providerId: string, provider: data.ResourceProvider): void; + + /** + * Unregister a resource provider + */ + unregisterProvider(ProviderId: string): void; + + /** + * Create a firewall rule + */ + createFirewallRule(selectedAccount: data.Account, firewallruleInfo: data.FirewallRuleInfo, resourceProviderId: string): Promise; + + /** + * handle a firewall rule + */ + handleFirewallRule(errorCode: number, errorMessage: string, connectionTypeId: string): Promise; + + /** + * Show firewall rule dialog + */ + showFirewallRuleDialog(connection: IConnectionProfile, ipAddress: string, resourceProviderId: string): Promise; +} + +export const IAccountPickerService = createDecorator('AccountPickerService'); +export interface IAccountPickerService { + _serviceBrand: any; + renderAccountPicker(container: HTMLElement): void; + addAccountCompleteEvent: Event; + addAccountErrorEvent: Event; + addAccountStartEvent: Event; + onAccountSelectionChangeEvent: Event; + selectedAccount: data.Account; +} diff --git a/src/sql/parts/accountManagement/common/media/accountActions.css b/src/sql/parts/accountManagement/common/media/accountActions.css new file mode 100644 index 0000000000..251aa414f9 --- /dev/null +++ b/src/sql/parts/accountManagement/common/media/accountActions.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +/* Icons for various account actions */ +.vs .action-item .icon.add-linked-account-action { + background-image: url('new_account.svg'); +} + +.vs-dark .action-item .icon.add-linked-account-action, +.hc-black .action-item .icon.add-linked-account-action { + background-image: url('new_account_inverse.svg'); +} \ No newline at end of file diff --git a/src/sql/parts/accountManagement/common/media/accountListRenderer.css b/src/sql/parts/accountManagement/common/media/accountListRenderer.css new file mode 100644 index 0000000000..2fa2574d1c --- /dev/null +++ b/src/sql/parts/accountManagement/common/media/accountListRenderer.css @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.list-row.account-picker-list { + display: flex; + align-items: flex-start; +} + +.list-row.account-picker-list .label { + flex: 1 1 auto; + margin-left: 15px; +} + +.list-row.account-picker-list .label .contextual-display-name { + font-size: 15px; +} + +.list-row.account-picker-list .label .content { + opacity: 0.7; +} + +.account-logo { + background: no-repeat center center; +} diff --git a/src/sql/parts/accountManagement/common/media/new_account.svg b/src/sql/parts/accountManagement/common/media/new_account.svg new file mode 100644 index 0000000000..25ffd03ad8 --- /dev/null +++ b/src/sql/parts/accountManagement/common/media/new_account.svg @@ -0,0 +1 @@ +new_account_16x16 \ No newline at end of file diff --git a/src/sql/parts/accountManagement/common/media/new_account_inverse.svg b/src/sql/parts/accountManagement/common/media/new_account_inverse.svg new file mode 100644 index 0000000000..a02a86a5b0 --- /dev/null +++ b/src/sql/parts/accountManagement/common/media/new_account_inverse.svg @@ -0,0 +1 @@ +new_account_inverse_16x16 \ No newline at end of file diff --git a/src/sql/parts/accountManagement/common/resourceProviderService.ts b/src/sql/parts/accountManagement/common/resourceProviderService.ts new file mode 100644 index 0000000000..2002173ec2 --- /dev/null +++ b/src/sql/parts/accountManagement/common/resourceProviderService.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { IResourceProviderService, IHandleFirewallRuleResult } from 'sql/parts/accountManagement/common/interfaces'; +import * as Constants from 'sql/common/constants'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import * as TelemetryUtils from 'sql/common/telemetryUtilities'; +import { FirewallRuleDialogController } from 'sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialogController'; + +import * as data from 'data'; + +export class ResourceProviderService implements IResourceProviderService { + + public _serviceBrand: any; + private _providers: { [handle: string]: data.ResourceProvider; } = Object.create(null); + private _firewallRuleDialogController: FirewallRuleDialogController; + + constructor( + @ITelemetryService private _telemetryService: ITelemetryService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IEnvironmentService private _environmentService: IEnvironmentService + ) { + } + + /** + * Opens the firewall rule dialog + */ + public showFirewallRuleDialog(connection: IConnectionProfile, ipAddress: string, resourceProviderId: string): Promise { + let self = this; + // If the firewall rule dialog hasn't been defined, create a new one + if (!self._firewallRuleDialogController) { + self._firewallRuleDialogController = self._instantiationService.createInstance(FirewallRuleDialogController); + } + + return self._firewallRuleDialogController.openFirewallRuleDialog(connection, ipAddress, resourceProviderId); + } + + /** + * Create a firewall rule + */ + public createFirewallRule(selectedAccount: data.Account, firewallruleInfo: data.FirewallRuleInfo, resourceProviderId: string): Promise { + return new Promise((resolve, reject) => { + let provider = this._providers[resourceProviderId]; + if (provider) { + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.FirewallRuleRequested, { provider: resourceProviderId }); + provider.createFirewallRule(selectedAccount, firewallruleInfo).then(result => { + resolve(result); + }, error => { + reject(error); + }); + } else { + reject(Constants.InvalidProvider); + } + }); + } + + /** + * Handle a firewall rule + */ + public handleFirewallRule(errorCode: number, errorMessage: string, connectionTypeId: string): Promise { + if (!this._environmentService.isBuilt) { + let self = this; + return new Promise((resolve, reject) => { + let handleFirewallRuleResult: IHandleFirewallRuleResult; + let promises = []; + if (self._providers) { + for (let key in self._providers) { + let provider = self._providers[key]; + promises.push(provider.handleFirewallRule(errorCode, errorMessage, connectionTypeId) + .then(response => { + if (response.result) { + handleFirewallRuleResult = { canHandleFirewallRule: response.result, ipAddress: response.ipAddress, resourceProviderId: key }; + } + }, + () => { /* Swallow failures at getting accounts, we'll just hide that provider */ + })); + } + } + + Promise.all(promises).then(() => { + if (handleFirewallRuleResult) { + resolve(handleFirewallRuleResult); + } else { + handleFirewallRuleResult = { canHandleFirewallRule: false, ipAddress: undefined, resourceProviderId: undefined }; + resolve(handleFirewallRuleResult); + } + }); + }); + } else { + return new Promise((resolve, reject) => { + resolve({ canHandleFirewallRule: false, ipAddress: undefined, resourceProviderId: undefined }); + }); + } + } + + /** + * Register a resource provider + */ + public registerProvider(providerId: string, provider: data.ResourceProvider): void { + this._providers[providerId] = provider; + } + + public unregisterProvider(providerId: string): void { + delete this._providers[providerId]; + } +} diff --git a/src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialog.ts b/src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialog.ts new file mode 100644 index 0000000000..bb17ec2291 --- /dev/null +++ b/src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialog.ts @@ -0,0 +1,309 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/firewallRuleDialog'; +import { Builder, $ } from 'vs/base/browser/builder'; +import * as DOM from 'vs/base/browser/dom'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { localize } from 'vs/nls'; +import { buttonBackground } from 'vs/platform/theme/common/colorRegistry'; +import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { attachButtonStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; + +import * as data from 'data'; + +import { Modal } from 'sql/base/browser/ui/modal/modal'; +import { FirewallRuleViewModel } from 'sql/parts/accountManagement/firewallRuleDialog/firewallRuleViewModel'; +import { attachModalDialogStyler } from 'sql/common/theme/styler'; +import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox'; +import { IAccountPickerService } from 'sql/parts/accountManagement/common/interfaces'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; + +export class FirewallRuleDialog extends Modal { + public viewModel: FirewallRuleViewModel; + private _createButton: Button; + private _closeButton: Button; + private _fromRangeinputBox: InputBox; + private _toRangeinputBox: InputBox; + + private _helpLink: HTMLElement; + private _IPAddressInput: HTMLElement; + private _subnetIPRangeInput: HTMLElement; + private _IPAddressElement: HTMLElement; + + // EVENTING //////////////////////////////////////////////////////////// + private _onAddAccountErrorEmitter: Emitter; + public get onAddAccountErrorEvent(): Event { return this._onAddAccountErrorEmitter.event; } + + private _onCancel: Emitter; + public get onCancel(): Event { return this._onCancel.event; } + + private _onCreateFirewallRule: Emitter; + public get onCreateFirewallRule(): Event { return this._onCreateFirewallRule.event; } + + constructor( + @IAccountPickerService private _accountPickerService: IAccountPickerService, + @IPartService partService: IPartService, + @IWorkbenchThemeService private _themeService: IWorkbenchThemeService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IContextViewService private _contextViewService: IContextViewService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super( + localize('createNewFirewallRule', 'Create new firewall rule'), + TelemetryKeys.FireWallRule, + partService, + telemetryService, + contextKeyService, + { + isFlyout: true, + hasBackButton: true, + hasSpinner: true + } + ); + + // Setup event emitters + this._onAddAccountErrorEmitter = new Emitter(); + this._onCancel = new Emitter(); + this._onCreateFirewallRule = new Emitter(); + + this.viewModel = this._instantiationService.createInstance(FirewallRuleViewModel); + } + + public render() { + super.render(); + attachModalDialogStyler(this, this._themeService); + this.backButton.addListener('click', () => this.cancel()); + this._register(attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND })); + this._createButton = this.addFooterButton(localize('ok', 'OK'), () => this.createFirewallRule()); + this._closeButton = this.addFooterButton(localize('cancel', 'Cancel'), () => this.cancel()); + this.registerListeners(); + } + + protected renderBody(container: HTMLElement) { + let descriptionSection; + $().div({ class: 'firewall-rule-description-section new-section' }, (descriptionContainer) => { + descriptionSection = descriptionContainer.getHTMLElement(); + DOM.append(descriptionContainer.getHTMLElement(), DOM.$('div.firewall-rule-icon')); + + const textDescriptionContainer = DOM.append(descriptionContainer.getHTMLElement(), DOM.$('div.firewall-rule-description')); + let dialogDescription = localize('firewallRuleDialogDescription', + 'Your client IP address does not have access to the server. Sign in to an Azure account and create a new firewall rule to enable access.'); + this.createLabelElement(new Builder(textDescriptionContainer), dialogDescription, false); + this._helpLink = DOM.append(textDescriptionContainer, DOM.$('a.help-link')); + this._helpLink.setAttribute('href', 'https://docs.microsoft.com/en-us/azure/sql-database/sql-database-firewall-configure'); + this._helpLink.innerHTML += localize('firewallRuleHelpDescription', 'Learn more about firewall settings'); + }); + + // Create account picker with event handling + this._accountPickerService.addAccountCompleteEvent(() => this.hideSpinner()); + this._accountPickerService.addAccountErrorEvent((msg) => { + this.hideSpinner(); + this._onAddAccountErrorEmitter.fire(msg); + }); + this._accountPickerService.addAccountStartEvent(() => this.showSpinner()); + this._accountPickerService.onAccountSelectionChangeEvent((account) => this.onAccountSelectionChange(account)); + + let azureAccountSection; + $().div({ class: 'azure-account-section new-section' }, (azureAccountContainer) => { + azureAccountSection = azureAccountContainer.getHTMLElement(); + let azureAccountLabel = localize('azureAccount', 'Azure account'); + this.createLabelElement(azureAccountContainer, azureAccountLabel, true); + azureAccountContainer.div({ class: 'dialog-input' }, (inputCellContainer) => { + this._accountPickerService.renderAccountPicker(inputCellContainer.getHTMLElement()); + }); + }); + + let subnetIPRangeSection; + $().div({ class: 'subnet-ip-range-input' }, (subnetIPRangeContainer) => { + subnetIPRangeSection = subnetIPRangeContainer.getHTMLElement(); + subnetIPRangeContainer.div({ class: 'dialog-input-section' }, (inputContainer) => { + inputContainer.div({ class: 'dialog-label' }, (labelContainer) => { + labelContainer.innerHtml(localize('from', 'From')); + }); + + inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => { + this._fromRangeinputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService); + }); + + inputContainer.div({ class: 'dialog-label' }, (labelContainer) => { + labelContainer.innerHtml(localize('to', 'To')); + }); + + inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => { + this._toRangeinputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService); + }); + }); + }); + + let firewallRuleSection; + $().div({ class: 'firewall-rule-section new-section' }, (firewallRuleContainer) => { + firewallRuleSection = firewallRuleContainer.getHTMLElement(); + let firewallRuleLabel = localize('filewallRule', 'Firewall rule'); + this.createLabelElement(firewallRuleContainer, firewallRuleLabel, true); + firewallRuleContainer.div({ class: 'radio-section' }, (radioContainer) => { + const form = DOM.append(radioContainer.getHTMLElement(), DOM.$('form.firewall-rule')); + const IPAddressDiv = DOM.append(form, DOM.$('div.firewall-ip-address dialog-input')); + const subnetIPRangeDiv = DOM.append(form, DOM.$('div.firewall-subnet-ip-range dialog-input')); + + const IPAddressContainer = DOM.append(IPAddressDiv, DOM.$('div.option-container')); + this._IPAddressInput = DOM.append(IPAddressContainer, DOM.$('input.option-input')); + this._IPAddressInput.setAttribute('type', 'radio'); + this._IPAddressInput.setAttribute('name', 'firewallRuleChoice'); + this._IPAddressInput.setAttribute('value', 'ipAddress'); + const IPAddressDescription = DOM.append(IPAddressContainer, DOM.$('div.option-description')); + IPAddressDescription.innerText = localize('addIPAddressLabel', 'Add my client IP '); + this._IPAddressElement = DOM.append(IPAddressContainer, DOM.$('div.option-ip-address')); + + const subnetIpRangeContainer = DOM.append(subnetIPRangeDiv, DOM.$('div.option-container')); + this._subnetIPRangeInput = DOM.append(subnetIpRangeContainer, DOM.$('input.option-input')); + this._subnetIPRangeInput.setAttribute('type', 'radio'); + this._subnetIPRangeInput.setAttribute('name', 'firewallRuleChoice'); + this._subnetIPRangeInput.setAttribute('value', 'ipRange'); + const subnetIPRangeDescription = DOM.append(subnetIpRangeContainer, DOM.$('div.option-description')); + subnetIPRangeDescription.innerText = localize('addIpRangeLabel', 'Add my subnet IP range'); + DOM.append(subnetIPRangeDiv, subnetIPRangeSection); + }); + }); + + new Builder(container).div({ class: 'firewall-rule-dialog' }, (builder) => { + builder.append(descriptionSection); + builder.append(azureAccountSection); + builder.append(firewallRuleSection); + }); + + this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme(e))); + this.updateTheme(this._themeService.getColorTheme()); + + jQuery(this._IPAddressInput).on('click', () => { + this.onFirewallRuleOptionSelected(true); + }); + + jQuery(this._subnetIPRangeInput).on('click', () => { + this.onFirewallRuleOptionSelected(false); + }); + } + + private onFirewallRuleOptionSelected(isIPAddress: boolean) { + this.viewModel.isIPAddressSelected = isIPAddress; + if (this._fromRangeinputBox) { + isIPAddress ? this._fromRangeinputBox.disable() : this._fromRangeinputBox.enable(); + } + if (this._toRangeinputBox) { + isIPAddress ? this._toRangeinputBox.disable() : this._toRangeinputBox.enable(); + } + } + + protected layout(height?: number): void { + // Nothing currently laid out statically in this class + } + + private createLabelElement(container: Builder, content: string, isHeader?: boolean) { + let className = 'dialog-label'; + if (isHeader) { + className += ' header'; + } + container.div({ class: className }, (labelContainer) => { + labelContainer.innerHtml(content); + }); + } + + // Update theming that is specific to firewall rule flyout body + private updateTheme(theme: IColorTheme): void { + let linkColor = theme.getColor(buttonBackground); + let link = linkColor ? linkColor.toString() : null; + this._helpLink.style.color = link; + if (this._helpLink) { + this._helpLink.style.color = link; + } + } + + private registerListeners(): void { + // Theme styler + this._register(attachButtonStyler(this._createButton, this._themeService)); + this._register(attachButtonStyler(this._closeButton, this._themeService)); + this._register(attachInputBoxStyler(this._fromRangeinputBox, this._themeService)); + this._register(attachInputBoxStyler(this._toRangeinputBox, this._themeService)); + + // handler for from subnet ip range change events + this._register(this._fromRangeinputBox.onDidChange(IPAddress => { + this.fromRangeInputChanged(IPAddress); + })); + + // handler for to subnet ip range change events + this._register(this._toRangeinputBox.onDidChange(IPAddress => { + this.toRangeInputChanged(IPAddress); + })); + } + + private fromRangeInputChanged(IPAddress: string) { + this.viewModel.fromSubnetIPRange = IPAddress; + } + + private toRangeInputChanged(IPAddress: string) { + this.viewModel.toSubnetIPRange = IPAddress; + } + + /* Overwrite esapce key behavior */ + protected onClose() { + this.cancel(); + } + + /* Overwrite enter key behavior */ + protected onAccept() { + this.createFirewallRule(); + } + + public cancel() { + this._onCancel.fire(); + this.close(); + } + + public close() { + this.hide(); + } + + public createFirewallRule() { + if (this._createButton.enabled) { + this._createButton.enabled = false; + this.showSpinner(); + this._onCreateFirewallRule.fire(); + } + } + + public onAccountSelectionChange(account: data.Account): void { + this.viewModel.selectedAccount = account; + if (account && !account.isStale) { + this._createButton.enabled = true; + } else { + this._createButton.enabled = false; + } + } + + public onServiceComplete() { + this._createButton.enabled = true; + this.hideSpinner(); + } + + public open() { + this._IPAddressInput.click(); + this.onAccountSelectionChange(this._accountPickerService.selectedAccount); + this._fromRangeinputBox.setPlaceHolder(this.viewModel.defaultFromSubnetIPRange); + this._toRangeinputBox.setPlaceHolder(this.viewModel.defaultToSubnetIPRange); + this._IPAddressElement.innerText = '(' + this.viewModel.defaultIPAddress + ')'; + + this.show(); + } +} \ No newline at end of file diff --git a/src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialogController.ts b/src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialogController.ts new file mode 100644 index 0000000000..f0c4bff51e --- /dev/null +++ b/src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialogController.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import Severity from 'vs/base/common/severity'; +import { localize } from 'vs/nls'; +import * as data from 'data'; + +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import { FirewallRuleDialog } from 'sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialog'; +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; +import { IResourceProviderService } from 'sql/parts/accountManagement/common/interfaces'; +import { Deferred } from 'sql/base/common/promise'; + +export class FirewallRuleDialogController { + + private _firewallRuleDialog: FirewallRuleDialog; + private _connection: IConnectionProfile; + private _resourceProviderId: string; + + private _addAccountErrorTitle = localize('addAccountErrorTitle', 'Error adding account'); + private _firewallRuleErrorTitle = localize('firewallRuleError', 'Firewall rule error'); + private _deferredPromise: Deferred; + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService, + @IResourceProviderService private _resourceProviderService: IResourceProviderService, + @IConnectionManagementService private _connectionService: IConnectionManagementService, + @IAccountManagementService private _accountManagementService: IAccountManagementService, + @IErrorMessageService private _errorMessageService: IErrorMessageService + ) { + } + + /** + * Open firewall rule dialog + */ + public openFirewallRuleDialog(connection: IConnectionProfile, ipAddress: string, resourceProviderId: string): Promise { + if (!this._firewallRuleDialog) { + this._firewallRuleDialog = this._instantiationService.createInstance(FirewallRuleDialog); + this._firewallRuleDialog.onCancel(this.handleOnCancel, this); + this._firewallRuleDialog.onCreateFirewallRule(this.handleOnCreateFirewallRule, this); + this._firewallRuleDialog.onAddAccountErrorEvent(this.handleOnAddAccountError, this); + this._firewallRuleDialog.render(); + } + this._connection = connection; + this._resourceProviderId = resourceProviderId; + this._firewallRuleDialog.viewModel.updateDefaultValues(ipAddress); + this._firewallRuleDialog.open(); + this._deferredPromise = new Deferred(); + return this._deferredPromise.promise; + } + + // PRIVATE HELPERS ///////////////////////////////////////////////////// + private handleOnAddAccountError(message: string): void { + this._errorMessageService.showDialog(Severity.Error, this._addAccountErrorTitle, message); + } + + private handleOnCreateFirewallRule(): void { + let resourceProviderId = this._resourceProviderId; + + this._accountManagementService.getSecurityToken(this._firewallRuleDialog.viewModel.selectedAccount).then(tokenMappings => { + let firewallRuleInfo: data.FirewallRuleInfo = { + startIpAddress: this._firewallRuleDialog.viewModel.isIPAddressSelected ? this._firewallRuleDialog.viewModel.defaultIPAddress : this._firewallRuleDialog.viewModel.fromSubnetIPRange, + endIpAddress: this._firewallRuleDialog.viewModel.isIPAddressSelected ? this._firewallRuleDialog.viewModel.defaultIPAddress : this._firewallRuleDialog.viewModel.toSubnetIPRange, + serverName: this._connection.serverName, + securityTokenMappings: tokenMappings + }; + + this._resourceProviderService.createFirewallRule(this._firewallRuleDialog.viewModel.selectedAccount, firewallRuleInfo, resourceProviderId).then(createFirewallRuleResponse => { + if (createFirewallRuleResponse.result) { + this._firewallRuleDialog.close(); + this._deferredPromise.resolve(true); + } else { + this._errorMessageService.showDialog(Severity.Error, this._firewallRuleErrorTitle, createFirewallRuleResponse.errorMessage); + } + this._firewallRuleDialog.onServiceComplete(); + }, error => { + this.showError(error); + }); + }, error => { + this.showError(error); + }); + } + + private showError(error: any): void { + this._errorMessageService.showDialog(Severity.Error, this._firewallRuleErrorTitle, error); + this._firewallRuleDialog.onServiceComplete(); + // Note: intentionally not rejecting the promise as we want users to be able to choose a different account + } + + private handleOnCancel(): void { + this._deferredPromise.resolve(false); + } + +} diff --git a/src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleViewModel.ts b/src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleViewModel.ts new file mode 100644 index 0000000000..63adfa1d68 --- /dev/null +++ b/src/sql/parts/accountManagement/firewallRuleDialog/firewallRuleViewModel.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as data from 'data'; + +/** + * View model for firewall rule dialog + */ +export class FirewallRuleViewModel { + public isIPAddressSelected: boolean; + public selectedAccount: data.Account; + + private _defaultIPAddress: string; + private _defaultFromSubnetIPRange: string; + private _defaultToSubnetIPRange: string; + private _fromSubnetIPRange: string; + private _toSubnetIPRange: string; + + constructor() { + this.isIPAddressSelected = true; + } + + public get defaultIPAddress(): string { + return this._defaultIPAddress; + } + + public get defaultFromSubnetIPRange(): string { + return this._defaultFromSubnetIPRange; + } + + public get defaultToSubnetIPRange(): string { + return this._defaultToSubnetIPRange; + } + + public set fromSubnetIPRange(IPAddress: string) { + this._fromSubnetIPRange = IPAddress; + } + + public get fromSubnetIPRange(): string { + if (this._fromSubnetIPRange) { + return this._fromSubnetIPRange; + } else { + return this._defaultFromSubnetIPRange; + } + } + + public set toSubnetIPRange(IPAddress: string) { + this._toSubnetIPRange = IPAddress; + } + + public get toSubnetIPRange(): string { + if (this._toSubnetIPRange) { + return this._toSubnetIPRange; + } else { + return this._defaultToSubnetIPRange; + } + } + + public updateDefaultValues(ipAddress: string): void { + this._defaultIPAddress = ipAddress; + this._defaultFromSubnetIPRange = ipAddress.replace(/\.[0-9]+$/g, '.0'); + this._defaultToSubnetIPRange = ipAddress.replace(/\.[0-9]+$/g, '.255'); + } +} \ No newline at end of file diff --git a/src/sql/parts/accountManagement/firewallRuleDialog/media/firewallRuleDialog.css b/src/sql/parts/accountManagement/firewallRuleDialog/media/firewallRuleDialog.css new file mode 100644 index 0000000000..d93cf01df2 --- /dev/null +++ b/src/sql/parts/accountManagement/firewallRuleDialog/media/firewallRuleDialog.css @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.firewall-rule-dialog { + padding: 15px +} + +.modal .firewall-rule-dialog .dialog-label.header { + font-size: 15px; +} + +.modal .firewall-rule-dialog .new-section { + padding-bottom: 30px; +} + +.modal .firewall-rule-dialog .dialog-input { + padding-left: 15px; + padding-right: 15px; + padding-top: 10px; +} + +.modal .firewall-rule-dialog .dialog-input-section { + display: flex; + padding-left: 30px; + padding-right: 20px; + padding-top: 10px; + padding-bottom: 10px; +} + +.modal .firewall-rule-dialog .dialog-input-section .dialog-label { + flex: 0 0 15px; + align-self: center; +} + +.modal .firewall-rule-dialog .dialog-input-section .dialog-input { + flex: 0 0 100px; + padding-left: 10px; + padding-right: 10px; + padding-top: 0px; + padding-bottom: 0px; +} + +.modal .firewall-rule-dialog .azure-account-section { + height: 92px; +} + +/* Firewall rule description section */ +.modal .firewall-rule-dialog a:link { + text-decoration: underline; +} + +.modal .firewall-rule-dialog .firewall-rule-description-section { + display: flex; +} + +.modal .firewall-rule-dialog .firewall-rule-description-section .firewall-rule-icon { + background: url('secure.svg') center center no-repeat; + width: 50px; + height: 50px; + flex: 0 0 50px; +} + +.modal .firewall-rule-dialog .firewall-rule-description-section .firewall-rule-description { + padding-left: 15px; +} + +/* Radio button section*/ +.modal .firewall-rule-dialog .firewall-rule-section .radio-section .option-container { + display: flex; + align-items: flex-start; +} + +.modal .firewall-rule-dialog .firewall-rule-section .radio-section .option-container .option-input { + margin-top: 0px; + align-self: center; +} + +.modal .firewall-rule-dialog .firewall-rule-section .radio-section .option-container .option-description { + padding-left: 10px; +} + +.modal .firewall-rule-dialog .firewall-rule-section .radio-section .option-container .option-ip-address { + padding-left: 3px; +} diff --git a/src/sql/parts/accountManagement/firewallRuleDialog/media/secure.svg b/src/sql/parts/accountManagement/firewallRuleDialog/media/secure.svg new file mode 100644 index 0000000000..5eb4405e16 --- /dev/null +++ b/src/sql/parts/accountManagement/firewallRuleDialog/media/secure.svg @@ -0,0 +1 @@ +secure \ No newline at end of file diff --git a/src/sql/parts/admin/common/adminService.ts b/src/sql/parts/admin/common/adminService.ts new file mode 100644 index 0000000000..b072db662d --- /dev/null +++ b/src/sql/parts/admin/common/adminService.ts @@ -0,0 +1,138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { localize } from 'vs/nls'; + +export const SERVICE_ID = 'adminService'; + +import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { CreateLoginInput } from 'sql/parts/admin/security/createLoginInput'; +import { TaskDialogInput } from 'sql/parts/tasks/dialog/taskDialogInput'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; + +import data = require('data'); + +export const IAdminService = createDecorator(SERVICE_ID); + +export interface IAdminService { + _serviceBrand: any; + + registerProvider(providerId: string, provider: data.AdminServicesProvider): void; + + showCreateDatabaseWizard(uri: string, connection: IConnectionProfile): Promise; + + showCreateLoginWizard(uri: string, connection: IConnectionProfile): Promise; + + createDatabase(connectionUri: string, database: data.DatabaseInfo): Thenable; + + getDefaultDatabaseInfo(connectionUri: string): Thenable; + + getDatabaseInfo(connectionUri: string): Thenable; +} + +export class AdminService implements IAdminService { + _serviceBrand: any; + + private _providers: { [handle: string]: data.AdminServicesProvider; } = Object.create(null); + + private _providerOptions: { [handle: string]: data.AdminServicesOptions; } = Object.create(null); + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IConnectionManagementService private _connectionService: IConnectionManagementService, + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService + ) { + if (_capabilitiesService && _capabilitiesService.onProviderRegisteredEvent) { + _capabilitiesService.onProviderRegisteredEvent((capabilities => { + this._providerOptions[capabilities.providerName] = capabilities.adminServicesProvider; + })); + } + } + + private _runAction(uri: string, action: (handler: data.AdminServicesProvider) => Thenable): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(uri); + + if (!providerId) { + return TPromise.wrapError(new Error(localize('providerIdNotValidError', 'Connection is required in order to interact with adminservice'))); + } + let handler = this._providers[providerId]; + if (handler) { + return action(handler); + } else { + return TPromise.wrapError(new Error(localize('noHandlerRegistered', 'No Handler Registered'))); + } + } + + public showCreateDatabaseWizard(uri: string, connection: IConnectionProfile): Promise { + const self = this; + return new Promise((resolve, reject) => { + let input: TaskDialogInput = self._instantiationService ? self._instantiationService.createInstance(TaskDialogInput, uri, connection) : undefined; + self._editorService.openEditor(input, { pinned: true }, false); + resolve(true); + }); + } + + public createDatabase(connectionUri: string, database: data.DatabaseInfo): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + if (providerId) { + let provider = this._providers[providerId]; + if (provider) { + return provider.createDatabase(connectionUri, database); + } + } + + return Promise.resolve(undefined); + } + + public showCreateLoginWizard(uri: string, connection: IConnectionProfile): Promise { + const self = this; + self.createLogin(uri, { name: 'TEST: login name' }); + + return new Promise((resolve, reject) => { + let loginInput: CreateLoginInput = self._instantiationService ? self._instantiationService.createInstance(CreateLoginInput, uri, connection) : undefined; + self._editorService.openEditor(loginInput, { pinned: true }, false); + resolve(true); + }); + } + + public createLogin(connectionUri: string, login: data.LoginInfo): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + if (providerId) { + let provider = this._providers[providerId]; + if (provider) { + return provider.createLogin(connectionUri, login); + } + } + return Promise.resolve(undefined); + } + + public getDefaultDatabaseInfo(connectionUri: string): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + if (providerId) { + let provider = this._providers[providerId]; + if (provider) { + return provider.getDefaultDatabaseInfo(connectionUri); + } + } + return Promise.resolve(undefined); + } + + public getDatabaseInfo(connectionUri: string): Thenable { + return this._runAction(connectionUri, (runner) => { + return runner.getDatabaseInfo(connectionUri); + }); + } + + public registerProvider(providerId: string, provider: data.AdminServicesProvider): void { + this._providers[providerId] = provider; + } +} diff --git a/src/sql/parts/admin/database/create/createDatabase.component.html b/src/sql/parts/admin/database/create/createDatabase.component.html new file mode 100644 index 0000000000..a3bad1441b --- /dev/null +++ b/src/sql/parts/admin/database/create/createDatabase.component.html @@ -0,0 +1,132 @@ + + +
+ +
+ +General + + + + + + + + + +
+ Database name: + + +
+ +{{databaseFilesLabel}} + + + + + +
+ + + + + + + + +
+ + + +
+ +
+ +
+ +
+ diff --git a/src/sql/parts/admin/database/create/createDatabase.component.ts b/src/sql/parts/admin/database/create/createDatabase.component.ts new file mode 100644 index 0000000000..b86ab483ac --- /dev/null +++ b/src/sql/parts/admin/database/create/createDatabase.component.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { ChangeDetectorRef, ElementRef, Component, forwardRef, Inject } from '@angular/core'; +import { NgForm } from '@angular/forms'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { TaskDialogComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import { IAdminService } from 'sql/parts/admin/common/adminService'; +import { ITaskDialogComponent } from 'sql/parts/tasks/common/tasks'; + +import data = require('data'); +import * as nls from 'vs/nls'; + +export const CREATEDATABASE_SELECTOR: string = 'createdatabase-component'; + +export interface DatabaseFile { + logicalName: string; + fileType: string; + filegroup: string; + initialSize: string; + autogrow: string; + path: string; +} + +@Component({ + selector: CREATEDATABASE_SELECTOR, + templateUrl: decodeURI(require.toUrl('sql/parts/admin/database/create/createDatabase.component.html')) +}) +export class CreateDatabaseComponent implements ITaskDialogComponent { + + private _adminService: IAdminService; + + public formSubmitted: boolean = false; + + public ownerUri: string; + + public connection: ConnectionManagementInfo; + + public databaseFiles: DatabaseFile[] = []; + + // tslint:disable:no-unused-variable + private readonly databaseFilesLabel: string = nls.localize('createDatabase.databaseFiles', 'Database files:'); + private readonly noRecordsFoundLabel: string = nls.localize('createDatabase.noRecordsFound', 'No records found'); + // tslint:enable:no-unused-variable + + constructor( + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeDetectorRef: ChangeDetectorRef, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { + this._adminService = this._bootstrapService.adminService; + } + + private getDatabaseInfo(form: NgForm): data.DatabaseInfo { + return { + options: { + name: form.value.databaseName, + owner: form.value.databaseOwner + } + }; + } + + public onSubmit(form: NgForm): void { + this._adminService.createDatabase(this.ownerUri, this.getDatabaseInfo(form)); + this.formSubmitted = true; + this._changeDetectorRef.detectChanges(); + } + + public onOk(): void { } + + public onGenerateScript(): void { } + + public onCancel(): void { } + + public onSelectOwner(): void { } + + public injectBootstapper(parameters: TaskDialogComponentParams ): void { + let self = this; + this.ownerUri = parameters.ownerUri; + this._adminService.getDefaultDatabaseInfo(this.ownerUri).then(dbInfo => { + let databaseFilesCount = dbInfo.options['databaseFilesCount']; + for (let i = 0; i < databaseFilesCount; ++i) { + self.databaseFiles[i] = { + logicalName: dbInfo.options['databaseFiles.' + i + '.name'], + fileType: dbInfo.options['databaseFiles.' + i + '.databaseFileType'], + filegroup: dbInfo.options['databaseFiles.' + i + '.fileGroup'], + initialSize: dbInfo.options['databaseFiles.' + i + '.initialSize'], + autogrow: dbInfo.options['databaseFiles.' + i + '.autogrowth'], + path: dbInfo.options['databaseFiles.' + i + '.folder'] + }; + } + self._changeDetectorRef.detectChanges(); + }); + } +} diff --git a/src/sql/parts/admin/security/createLogin.component.html b/src/sql/parts/admin/security/createLogin.component.html new file mode 100644 index 0000000000..42096908d2 --- /dev/null +++ b/src/sql/parts/admin/security/createLogin.component.html @@ -0,0 +1,34 @@ + + +

New Login

+ +

General

+ + + + + + + + + + +
+ Login name: + + +
+ Password: + + +
+ +
+
+ + diff --git a/src/sql/parts/admin/security/createLogin.component.ts b/src/sql/parts/admin/security/createLogin.component.ts new file mode 100644 index 0000000000..1388129841 --- /dev/null +++ b/src/sql/parts/admin/security/createLogin.component.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { ElementRef, Component, Inject, forwardRef } from '@angular/core'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { DashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; + +export const CREATELOGIN_SELECTOR: string = 'createlogin-component'; + +@Component({ + selector: CREATELOGIN_SELECTOR, + templateUrl: decodeURI(require.toUrl('sql/parts/admin/security/createLogin.component.html')) +}) +export class CreateLoginComponent { + + public ownerUri: string; + + public connection: ConnectionManagementInfo; + + constructor( + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { + let parameters: DashboardComponentParams = this._bootstrapService.getBootstrapParams(this._el.nativeElement.tagName); + + } +} diff --git a/src/sql/parts/admin/security/createLogin.module.ts b/src/sql/parts/admin/security/createLogin.module.ts new file mode 100644 index 0000000000..debdc56b93 --- /dev/null +++ b/src/sql/parts/admin/security/createLogin.module.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 { NgModule, Inject, forwardRef, ApplicationRef, ComponentFactoryResolver } from '@angular/core'; +import { APP_BASE_HREF, CommonModule } from '@angular/common'; +import { BrowserModule } from '@angular/platform-browser'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; + +import { CreateLoginComponent, CREATELOGIN_SELECTOR } from 'sql/parts/admin/security/createLogin.component'; + +// Connection Dashboard main angular module +@NgModule({ + declarations: [ + CreateLoginComponent + ], + entryComponents: [CreateLoginComponent], + imports: [ + CommonModule, + BrowserModule + ], + providers: [{ provide: APP_BASE_HREF, useValue: '/' }] +}) +export class CreateLoginModule { + + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { + } + + ngDoBootstrap(appRef: ApplicationRef) { + const factory = this._resolver.resolveComponentFactory(CreateLoginComponent); + const uniqueSelector: string = this._bootstrapService.getUniqueSelector(CREATELOGIN_SELECTOR); + (factory).factory.selector = uniqueSelector; + appRef.bootstrap(factory); + } +} diff --git a/src/sql/parts/admin/security/createLoginEditor.ts b/src/sql/parts/admin/security/createLoginEditor.ts new file mode 100644 index 0000000000..83329e11e3 --- /dev/null +++ b/src/sql/parts/admin/security/createLoginEditor.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/parts/query/editor/media/queryEditor'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CreateLoginInput } from './createLoginInput'; +import { CreateLoginModule } from './createLogin.module'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { IMetadataService } from 'sql/services/metadata/metadataService'; +import { IScriptingService } from 'sql/services/scripting/scriptingService'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import { DashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { CREATELOGIN_SELECTOR } from 'sql/parts/admin/security/createLogin.component'; + +export class CreateLoginEditor extends BaseEditor { + + public static ID: string = 'workbench.editor.createlogin'; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IInstantiationService private instantiationService: IInstantiationService, + @IConnectionManagementService private _connectionService: IConnectionManagementService, + @IMetadataService private _metadataService: IMetadataService, + @IScriptingService private _scriptingService: IScriptingService, + @IQueryEditorService private _queryEditorService: IQueryEditorService, + @IBootstrapService private _bootstrapService: IBootstrapService + ) { + super(CreateLoginEditor.ID, telemetryService, themeService); + } + + /** + * Called to create the editor in the parent builder. + */ + public createEditor(parent: Builder): void { + } + + /** + * Sets focus on this editor. Specifically, it sets the focus on the hosted text editor. + */ + public focus(): void { + } + + /** + * Updates the internal variable keeping track of the editor's size, and re-calculates the sash position. + * To be called when the container of this editor changes size. + */ + public layout(dimension: Dimension): void { + } + + public setInput(input: CreateLoginInput, options: EditorOptions): TPromise { + if (this.input instanceof CreateLoginInput && this.input.matches(input)) { + return TPromise.as(undefined); + } + + if (!input.hasInitialized) { + this.bootstrapAngular(input); + } + this.revealElementWithTagName(input.uniqueSelector, this.getContainer().getHTMLElement()); + + return super.setInput(input, options); + } + + /** + * Reveal the child element with the given tagName and hide all other elements. + */ + private revealElementWithTagName(tagName: string, parent: HTMLElement): void { + let elementToReveal: HTMLElement; + + for (let i = 0; i < parent.children.length; i++) { + let child: HTMLElement = parent.children[i]; + if (child.tagName && child.tagName.toLowerCase() === tagName && !elementToReveal) { + elementToReveal = child; + } else { + child.style.display = 'none'; + } + } + + if (elementToReveal) { + elementToReveal.style.display = ''; + } + } + + /** + * Load the angular components and record for this input that we have done so + */ + private bootstrapAngular(input: CreateLoginInput): void { + + // Get the bootstrap params and perform the bootstrap + let params: DashboardComponentParams = { + connection: input.getConnectionProfile(), + ownerUri: input.getUri() + }; + let uniqueSelector = this._bootstrapService.bootstrap( + CreateLoginModule, + this.getContainer().getHTMLElement(), + CREATELOGIN_SELECTOR, + params); + input.setUniqueSelector(uniqueSelector); + } + + public dispose(): void { + super.dispose(); + } +} diff --git a/src/sql/parts/admin/security/createLoginInput.ts b/src/sql/parts/admin/security/createLoginInput.ts new file mode 100644 index 0000000000..6ca44e7287 --- /dev/null +++ b/src/sql/parts/admin/security/createLoginInput.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; + +export class CreateLoginInput extends EditorInput { + + public static ID: string = 'workbench.editorinputs.createlogininput'; + public static SCHEMA: string = 'adminlogincreate'; + + private _uniqueSelector: string; + + constructor(private _uri: string, private _connection: IConnectionProfile) { + super(); + } + + public setUniqueSelector(uniqueSelector: string): void { + this._uniqueSelector = uniqueSelector; + } + + public getTypeId(): string { + return UntitledEditorInput.ID; + } + + public getName(): string { + return this._connection.serverName + ':' + this._connection.databaseName; + } + + public getUri(): string { + return this._uri; + } + + public supportsSplitEditor(): boolean { + return false; + } + + public getConnectionProfile(): IConnectionProfile { + return this._connection; + } + + public resolve(refresh?: boolean): TPromise { + return undefined; + } + + public get hasInitialized(): boolean { + return !!this._uniqueSelector; + } + + public get uniqueSelector(): string { + return this._uniqueSelector; + } +} diff --git a/src/sql/parts/common/customInputConverter.ts b/src/sql/parts/common/customInputConverter.ts new file mode 100644 index 0000000000..51b73f95a2 --- /dev/null +++ b/src/sql/parts/common/customInputConverter.ts @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { EditorInput } from 'vs/workbench/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; +import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; +import { QueryInput } from 'sql/parts/query/common/queryInput'; +import URI from 'vs/base/common/uri'; +import { IEditorInput } from 'vs/platform/editor/common/editor'; +import { IQueryEditorOptions } from 'sql/parts/query/common/queryEditorService'; +import { QueryPlanInput } from 'sql/parts/queryPlan/queryPlanInput'; + +const fs = require('fs'); + +////// Exported public functions/vars + +// prefix for untitled sql editors +export const untitledFilePrefix = 'SQLQuery'; + +// mode identifier for SQL mode +export const sqlModeId = 'sql'; + +/** + * Checks if the specified input is supported by one our custom input types, and if so convert it + * to that type. + * @param input The input to check for conversion + * @param options Editor options for controlling the conversion + * @param instantiationService The instatianation service to use to create the new input types + */ +export function convertEditorInput(input: EditorInput, options: IQueryEditorOptions, instantiationService: IInstantiationService): EditorInput { + let denyQueryEditor = options && options.denyQueryEditor; + if (input && !denyQueryEditor) { + //QueryInput + let uri: URI = getQueryEditorFileUri(input); + if (uri) { + const queryResultsInput: QueryResultsInput = instantiationService.createInstance(QueryResultsInput, uri.toString()); + let queryInput: QueryInput = instantiationService.createInstance(QueryInput, input.getName(), '', input, queryResultsInput, undefined); + return queryInput; + } + + //QueryPlanInput + uri = getQueryPlanEditorUri(input); + if(uri) { + let queryPlanXml: string = fs.readFileSync(uri.fsPath); + let queryPlanInput: QueryPlanInput = instantiationService.createInstance(QueryPlanInput, queryPlanXml, 'aaa', undefined); + return queryPlanInput; + } + } + + return input; +} + +/** + * Gets the resource of the input if it's one of the ones we support. + * @param input The IEditorInput to get the resource of + */ +export function getSupportedInputResource(input: IEditorInput): URI { + if (input instanceof UntitledEditorInput) { + let untitledCast: UntitledEditorInput = input; + if (untitledCast) { + return untitledCast.getResource(); + } + } + + if (input instanceof FileEditorInput) { + let fileCast: FileEditorInput = input; + if (fileCast) { + return fileCast.getResource(); + } + } + + return undefined; +} + +////// Non-Exported Private functions/vars + +// file extensions for the inputs we support (should be all upper case for comparison) +const sqlFileTypes = ['SQL']; +const sqlPlanFileTypes = ['SQLPLAN']; + +/** + * If input is a supported query editor file, return it's URI. Otherwise return undefined. + * @param input The EditorInput to retrieve the URI of + */ +function getQueryEditorFileUri(input: EditorInput): URI { + if (!input || !input.getName()) { + return undefined; + } + + // If this editor is not already of type queryinput + if (!(input instanceof QueryInput)) { + + // If this editor has a URI + let uri: URI = getSupportedInputResource(input); + if (uri) { + let isValidUri: boolean = !!uri && !!uri.toString; + + if (isValidUri && (hasFileExtension(sqlFileTypes, input, true) || hasSqlFileMode(input)) ) { + return uri; + } + } + } + + return undefined; +} + +/** + * If input is a supported query plan editor file (.sqlplan), return it's URI. Otherwise return undefined. + * @param input The EditorInput to get the URI of + */ +function getQueryPlanEditorUri(input: EditorInput): URI { + if (!input || !input.getName()) { + return undefined; + } + + // If this editor is not already of type queryinput + if (!(input instanceof QueryPlanInput)) { + let uri: URI = getSupportedInputResource(input); + if(uri) { + if (hasFileExtension(sqlPlanFileTypes, input, false)) { + return uri; + } + } + } + + return undefined; +} + +/** + * Checks whether the given EditorInput is set to either undefined or sql mode + * @param input The EditorInput to check the mode of + */ +function hasSqlFileMode(input: EditorInput): boolean { + if (input instanceof UntitledEditorInput) { + let untitledCast: UntitledEditorInput = input; + return untitledCast && (untitledCast.getModeId() === undefined || untitledCast.getModeId() === sqlModeId); + } + + return false; +} + +/** + * Checks whether the name of the specified input has an extension that is + * @param extensions The extensions to check for + * @param input The input to check for the specified extensions + */ +function hasFileExtension(extensions: string[], input: EditorInput, checkUntitledFileType: boolean): boolean { + // Check the extension type + let lastPeriodIndex = input.getName().lastIndexOf('.'); + if (lastPeriodIndex > -1) { + let extension: string = input.getName().substr(lastPeriodIndex + 1).toUpperCase(); + return !!extensions.find(x => x === extension); + } + + // Check for untitled file type + if (checkUntitledFileType && input.getName().includes(untitledFilePrefix)) { + return true; + } + + // Return false if not a queryEditor file + return false; +} + diff --git a/src/sql/parts/common/rxjsUtils.ts b/src/sql/parts/common/rxjsUtils.ts new file mode 100644 index 0000000000..3a869c6335 --- /dev/null +++ b/src/sql/parts/common/rxjsUtils.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Subscription } from 'rxjs/Subscription'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export function toDisposableSubscription(sub: Subscription): IDisposable { + return { + dispose: () => { + sub.unsubscribe(); + } + }; +} \ No newline at end of file diff --git a/src/sql/parts/connection/common/connection.contribution.ts b/src/sql/parts/connection/common/connection.contribution.ts new file mode 100644 index 0000000000..b0cecf360e --- /dev/null +++ b/src/sql/parts/connection/common/connection.contribution.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IExtensionGalleryService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; +import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { DashboardEditor } from 'sql/parts/dashboard/dashboardEditor'; +import { DashboardInput } from 'sql/parts/dashboard/dashboardInput'; +import { ClearRecentConnectionsAction } from 'sql/parts/connection/common/connectionActions'; + +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; +import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { localize } from 'vs/nls'; +import { AddServerGroupAction, AddServerAction } from 'sql/parts/registeredServer/viewlet/connectionTreeAction'; + +// Singletons +registerSingleton(IExtensionGalleryService, ExtensionGalleryService); +registerSingleton(IExtensionTipsService, ExtensionTipsService); +registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); + +// Connection Dashboard registration +const dashboardEditorDescriptor = new EditorDescriptor( + DashboardEditor.ID, + 'Dashboard', + 'sql/parts/dashboard/dashboardEditor', + 'DashboardEditor' +); + +Registry.as(EditorExtensions.Editors) + .registerEditor(dashboardEditorDescriptor, [new SyncDescriptor(DashboardInput)]); + +let actionRegistry = Registry.as(Extensions.WorkbenchActions); + +// Connection Actions +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor( + ClearRecentConnectionsAction, + ClearRecentConnectionsAction.ID, + ClearRecentConnectionsAction.LABEL + ), + ClearRecentConnectionsAction.LABEL +); + +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor( + AddServerGroupAction, + AddServerGroupAction.ID, + AddServerGroupAction.LABEL + ), + AddServerGroupAction.LABEL +); + +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor( + AddServerAction, + AddServerAction.ID, + AddServerAction.LABEL + ), + AddServerAction.LABEL +); + +let configurationRegistry = Registry.as(ConfigExtensions.Configuration); +configurationRegistry.registerConfiguration({ + 'id': 'connection', + 'title': 'Connection', + 'type': 'object', + 'properties': { + 'sql.maxRecentConnections': { + 'type': 'number', + 'default': 25, + 'description': localize('sql.maxRecentConnectionsDescription', 'The maximum number of recently used connections to store in the connection list.') + }, + 'sql.defaultEngine': { + 'type': 'string', + 'description': localize('sql.defaultEngineDescription', 'Default SQL Engine to use. This drives default language provider in .sql files and the default to use when creating a new connection.'), + 'default': 'MSSQL' + }, + } +}); diff --git a/src/sql/parts/connection/common/connection.ts b/src/sql/parts/connection/common/connection.ts new file mode 100644 index 0000000000..489c7cf53b --- /dev/null +++ b/src/sql/parts/connection/common/connection.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// ------------------------------- < Cancel Connect Request > --------------------------------------- + +/** + * Cancel connect request message format + */ +export class CancelConnectParams { + /** + * URI identifying the owner of the connection + */ + public ownerUri: string; +} + +// ------------------------------- -------------------------------------- + +// ------------------------------- < Disconnect Request > ------------------------------------------- + +// Disconnect request message format +export class DisconnectParams { + // URI identifying the owner of the connection + public ownerUri: string; +} + +// ------------------------------- ------------------------------------------ diff --git a/src/sql/parts/connection/common/connectionActions.ts b/src/sql/parts/connection/common/connectionActions.ts new file mode 100644 index 0000000000..54ea6f76fe --- /dev/null +++ b/src/sql/parts/connection/common/connectionActions.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; + +import nls = require('vs/nls'); +import { Action } from 'vs/base/common/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; + +/** + * Locates the active editor and calls runQuery() on the editor if it is a QueryEditor. + */ +export class ClearRecentConnectionsAction extends Action { + + public static ID = 'clearRecentConnectionsAction'; + public static LABEL = nls.localize('ClearRecentlyUsedLabel', 'Clear Recent Connections List'); + + constructor( + id: string, + label: string, + @IInstantiationService private _instantiationService: IInstantiationService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IMessageService private _messageService: IMessageService, + @IQuickOpenService private _quickOpenService: IQuickOpenService + ) { + super(id, label); + this.enabled = true; + } + + public run(): TPromise { + let self = this; + return self.promptToClearRecentConnectionsList().then(result => { + if (result) { + self._connectionManagementService.clearRecentConnectionsList(); + self._messageService.show(Severity.Info, nls.localize('ClearedRecentConnections', 'Recent connections list cleared')); + } + }); + } + + private promptToClearRecentConnectionsList(): TPromise { + const self = this; + return new TPromise((resolve, reject) => { + let choices: { key, value }[] = [ + { key: nls.localize('yes', 'Yes'), value: true }, + { key: nls.localize('no', 'No'), value: false } + ]; + + self._quickOpenService.pick(choices.map(x => x.key), { placeHolder: nls.localize('ClearRecentlyUsedLabel', 'Clear Recent Connections List'), ignoreFocusLost: true }).then((choice) => { + let confirm = choices.find(x => x.key === choice); + resolve(confirm && confirm.value); + }); + }); + } +} diff --git a/src/sql/parts/connection/common/connectionConfig.ts b/src/sql/parts/connection/common/connectionConfig.ts new file mode 100644 index 0000000000..a89c195c13 --- /dev/null +++ b/src/sql/parts/connection/common/connectionConfig.ts @@ -0,0 +1,543 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as Constants from './constants'; +import * as Utils from './utils'; +import { IConnectionProfile, IConnectionProfileStore } from './interfaces'; +import { IConnectionConfig } from './iconnectionConfig'; +import { ConnectionProfileGroup, IConnectionProfileGroup } from './connectionProfileGroup'; +import { IConfigurationEditingService, ConfigurationTarget, IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IConfigurationValue as TConfigurationValue } from 'vs/platform/configuration/common/configuration'; +import { ConnectionProfile } from './connectionProfile'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import * as data from 'data'; +import * as nls from 'vs/nls'; + +import { generateUuid } from 'vs/base/common/uuid'; + +export interface ISaveGroupResult { + groups: IConnectionProfileGroup[]; + newGroupId: string; +} + +/** + * Implements connection profile file storage. + */ +export class ConnectionConfig implements IConnectionConfig { + + private _providerCapabilitiesMap: { [providerName: string]: data.DataProtocolServerCapabilities }; + private _providerCachedCapabilitiesMap: { [providerName: string]: data.DataProtocolServerCapabilities }; + /** + * Constructor. + */ + public constructor( + private _configurationEditService: IConfigurationEditingService, + private _workspaceConfigurationService: IWorkspaceConfigurationService, + private _capabilitiesService: ICapabilitiesService, + private _cachedMetadata?: data.DataProtocolServerCapabilities[] + ) { + this._providerCapabilitiesMap = {}; + this._providerCachedCapabilitiesMap = {}; + } + + public setCachedMetadata(cachedMetaData: data.DataProtocolServerCapabilities[]): void { + this._cachedMetadata = cachedMetaData; + } + + /** + * Returns connection groups from user and workspace settings. + */ + public getAllGroups(): IConnectionProfileGroup[] { + + let allGroups: IConnectionProfileGroup[] = []; + let userGroups = this.getConfiguration(Constants.connectionGroupsArrayName).user as IConnectionProfileGroup[]; + let workspaceGroups = this.getConfiguration(Constants.connectionGroupsArrayName).workspace as IConnectionProfileGroup[]; + + if (userGroups) { + + if (workspaceGroups) { + userGroups = userGroups.filter(x => workspaceGroups.find(f => this.isSameGroupName(f, x)) === undefined); + allGroups = allGroups.concat(workspaceGroups); + } + allGroups = allGroups.concat(userGroups); + } + allGroups = allGroups.map(g => { + if (g.parentId === '' || !g.parentId) { + g.parentId = undefined; + } + return g; + }); + return allGroups; + } + + /** + * Returns the capabilities for given provider name. First tries to get it from capabilitiesService and if it's not registered yet, + * Gets the data from the metadata stored in the config + * @param providerName Provider Name + */ + public getCapabilities(providerName: string): data.DataProtocolServerCapabilities { + let result: data.DataProtocolServerCapabilities; + + if (providerName in this._providerCapabilitiesMap) { + result = this._providerCapabilitiesMap[providerName]; + } else { + let capabilities = this._capabilitiesService.getCapabilities(); + if (capabilities) { + let providerCapabilities = capabilities.find(c => c.providerName === providerName); + if (providerCapabilities) { + this._providerCapabilitiesMap[providerName] = providerCapabilities; + result = providerCapabilities; + } + } + } + + if (!result && this._cachedMetadata) { + if (providerName in this._providerCachedCapabilitiesMap) { + result = this._providerCachedCapabilitiesMap[providerName]; + } else { + let metaDataFromConfig = this._cachedMetadata; + if (metaDataFromConfig) { + let providerCapabilities = metaDataFromConfig.find(m => m.providerName === providerName); + this._providerCachedCapabilitiesMap[providerName] = providerCapabilities; + result = providerCapabilities; + } + } + } + + return result; + + } + /** + * Add a new connection to the connection config. + */ + public addConnection(profile: IConnectionProfile): Promise { + return new Promise((resolve, reject) => { + if (profile.saveProfile) { + this.addGroupFromProfile(profile).then(groupId => { + let profiles = this._workspaceConfigurationService.lookup(Constants.connectionsArrayName).user; + if (!profiles) { + profiles = []; + } + + let providerCapabilities = this.getCapabilities(profile.providerName); + let connectionProfile = this.getConnectionProfileInstance(profile, groupId); + let newProfile = ConnectionProfile.convertToProfileStore(providerCapabilities, connectionProfile); + + // Remove the profile if already set + var sameProfileInList = profiles.find(value => { + let providerCapabilities = this.getCapabilities(value.providerName); + let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, providerCapabilities); + return providerConnectionProfile.matches(connectionProfile); + }); + if (sameProfileInList) { + profiles = profiles.filter(value => value !== sameProfileInList); + newProfile.id = sameProfileInList.id; + connectionProfile.id = sameProfileInList.id; + } + + profiles.push(newProfile); + + this.writeConfiguration(Constants.connectionsArrayName, profiles).then(() => { + resolve(connectionProfile); + }).catch(err => { + reject(err); + }); + }); + } + }); + } + + private getConnectionProfileInstance(profile: IConnectionProfile, groupId: string): ConnectionProfile { + let connectionProfile = profile as ConnectionProfile; + let providerCapabilities = this.getCapabilities(profile.providerName); + if (connectionProfile === undefined) { + connectionProfile = new ConnectionProfile(providerCapabilities, profile); + } + connectionProfile.groupId = groupId; + return connectionProfile; + } + + /** + *Returns group id + * @param groupName + */ + public addGroupFromProfile(profile: IConnectionProfile): Promise { + return new Promise((resolve, reject) => { + if (profile.groupId && profile.groupId !== Utils.defaultGroupId) { + resolve(profile.groupId); + } else { + let groups = this._workspaceConfigurationService.lookup(Constants.connectionGroupsArrayName).user; + let result = this.saveGroup(groups, profile.groupFullName, undefined, undefined); + groups = result.groups; + + this.writeConfiguration(Constants.connectionGroupsArrayName, groups).then(() => { + resolve(result.newGroupId); + }).catch(err => { + reject(err); + }); + } + }); + } + + /** + *Returns group id + * @param groupName + */ + public addGroup(profileGroup: IConnectionProfileGroup): Promise { + return new Promise((resolve, reject) => { + if (profileGroup.id) { + resolve(profileGroup.id); + } else { + let groups = this._workspaceConfigurationService.lookup(Constants.connectionGroupsArrayName).user; + let sameNameGroup = groups ? groups.find(group => group.name === profileGroup.name) : undefined; + if (sameNameGroup) { + let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists."); + reject(errMessage); + } else { + let result = this.saveGroup(groups, profileGroup.name, profileGroup.color, profileGroup.description); + groups = result.groups; + + this.writeConfiguration(Constants.connectionGroupsArrayName, groups).then(() => { + resolve(result.newGroupId); + }).catch(err => { + reject(err); + }); + } + } + }); + } + + private getConnectionProfilesForTarget(configTarget: ConfigurationTarget): IConnectionProfileStore[] { + let configs = this.getConfiguration(Constants.connectionsArrayName); + let profiles: IConnectionProfileStore[]; + if (configs) { + if (configTarget === ConfigurationTarget.USER) { + profiles = configs.user; + } else if (configTarget === ConfigurationTarget.WORKSPACE) { + profiles = configs.workspace; + } + if (profiles) { + if(this.fixConnectionIds(profiles)) { + this.writeConfiguration(Constants.connectionsArrayName, profiles, configTarget); + } + } else { + profiles = []; + } + } + + return profiles; + } + + /** + * Replace duplicate ids with new ones. Sets id for the profiles without id + * @param profiles + */ + public fixConnectionIds(profiles: IConnectionProfileStore[]): boolean { + let idsCache: { [label: string]: boolean } = {}; + let changed: boolean = false; + for (var index = 0; index < profiles.length; index++) { + var profile = profiles[index]; + if (!profile.id) { + profile.id = generateUuid(); + changed = true; + } + if (profile.id in idsCache) { + profile.id = generateUuid(); + changed = true; + } + idsCache[profile.id] = true; + } + return changed; + } + + /** + * Get a list of all connections in the connection config. Connections returned + * are sorted first by whether they were found in the user/workspace settings, + * and next alphabetically by profile/server name. + */ + public getConnections(getWorkspaceConnections: boolean): ConnectionProfile[] { + let profiles: IConnectionProfileStore[] = []; + //TODO: have to figure out how to sort connections for all provider + // Read from user settings + + let userProfiles: IConnectionProfileStore[] = this.getConnectionProfilesForTarget(ConfigurationTarget.USER); + if (userProfiles !== undefined) { + profiles = profiles.concat(userProfiles); + } + + if (getWorkspaceConnections) { + // Read from workspace settings + + let workspaceProfiles: IConnectionProfileStore[] = this.getConnectionProfilesForTarget(ConfigurationTarget.WORKSPACE); + if (workspaceProfiles !== undefined) { + profiles = profiles.concat(workspaceProfiles); + } + } + + let connectionProfiles = profiles.map(p => { + let capabilitiesForProvider = this.getCapabilities(p.providerName); + + let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(p, capabilitiesForProvider); + providerConnectionProfile.setServerCapabilities(capabilitiesForProvider); + this._capabilitiesService.onProviderRegisteredEvent((serverCapabilities) => { + providerConnectionProfile.onProviderRegistered(serverCapabilities); + }); + + return providerConnectionProfile; + }); + + return connectionProfiles; + } + + /** + * Delete a connection profile from settings. + */ + public deleteConnection(profile: ConnectionProfile): Promise { + // Get all connections in the settings + let profiles = this._workspaceConfigurationService.lookup(Constants.connectionsArrayName).user; + // Remove the profile from the connections + profiles = profiles.filter(value => { + let providerCapabilities = this.getCapabilities(value.providerName); + let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, providerCapabilities); + return providerConnectionProfile.getOptionsKey() !== profile.getOptionsKey(); + }); + + // Write connections back to settings + return this.writeConfiguration(Constants.connectionsArrayName, profiles); + } + + /** + * Delete a group and all its child connections and groups from settings. + * Fails if writing to settings fails. + */ + public deleteGroup(group: ConnectionProfileGroup): Promise { + let connections = ConnectionProfileGroup.getConnectionsInGroup(group); + let subgroups = ConnectionProfileGroup.getSubgroups(group); + // Add selected group to subgroups list + subgroups.push(group); + // Get all connections in the settings + let profiles = this._workspaceConfigurationService.lookup(Constants.connectionsArrayName).user; + // Remove the profiles from the connections + profiles = profiles.filter(value => { + let providerCapabilities = this.getCapabilities(value.providerName); + let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, providerCapabilities); + return !connections.some((val) => val.getOptionsKey() === providerConnectionProfile.getOptionsKey()); + }); + + // Get all groups in the settings + let groups = this._workspaceConfigurationService.lookup(Constants.connectionGroupsArrayName).user; + // Remove subgroups in the settings + groups = groups.filter((grp) => { + return !subgroups.some((item) => item.id === grp.id); + }); + return new Promise((resolve, reject) => { + this.writeConfiguration(Constants.connectionsArrayName, profiles).then(() => { + this.writeConfiguration(Constants.connectionGroupsArrayName, groups).then(() => { + resolve(); + }).catch(() => reject()); + }).catch(() => reject()); + }); + } + + /** + * Moves the source group under the target group. + */ + public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise { + let groups = this._workspaceConfigurationService.lookup(Constants.connectionGroupsArrayName).user; + groups = groups.map(g => { + if (g.id === source.id) { + g.parentId = target.id; + } + return g; + }); + return this.writeConfiguration(Constants.connectionGroupsArrayName, groups); + } + + /** + * Returns true if connection can be moved to another group + */ + public canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean { + let profiles = this.getConnections(true); + let existingProfile = profiles.find(p => p.getConnectionInfoId() === profile.getConnectionInfoId() + && p.groupId === newGroupID); + return existingProfile === undefined; + } + + /** + * Moves the connection under the target group with the new ID. + */ + private changeGroupIdForConnectionInSettings(profile: ConnectionProfile, newGroupID: string, target: ConfigurationTarget = ConfigurationTarget.USER): Promise { + return new Promise((resolve, reject) => { + let profiles = target === ConfigurationTarget.USER ? this._workspaceConfigurationService.lookup(Constants.connectionsArrayName).user : + this._workspaceConfigurationService.lookup(Constants.connectionsArrayName).workspace; + if (profiles) { + let providerCapabilities = this.getCapabilities(profile.providerName); + if (profile.parent && profile.parent.id === Constants.unsavedGroupId) { + profile.groupId = newGroupID; + profiles.push(ConnectionProfile.convertToProfileStore(providerCapabilities, profile)); + } else { + profiles.forEach((value) => { + let configProf = ConnectionProfile.createFromStoredProfile(value, providerCapabilities); + if (configProf.getOptionsKey() === profile.getOptionsKey()) { + value.groupId = newGroupID; + } + }); + } + + this.writeConfiguration(Constants.connectionsArrayName, profiles, target).then(result => { + resolve(); + }).catch(error => { + reject(error); + }); + } else { + resolve(); + } + }); + } + + /** + * Moves the connection under the target group with the new ID. + */ + public changeGroupIdForConnection(profile: ConnectionProfile, newGroupID: string): Promise { + return new Promise((resolve, reject) => { + if (!this.canChangeConnectionConfig(profile, newGroupID)) { + // Same connection already exists in this group + reject('Same connection already exists in the group'); + } else { + this.changeGroupIdForConnectionInSettings(profile, newGroupID, ConfigurationTarget.USER).then(result1 => { + this.changeGroupIdForConnectionInSettings(profile, newGroupID, ConfigurationTarget.WORKSPACE).then(result2 => { + resolve(); + }).catch(error2 => { + reject(error2); + }); + }).catch(error1 => { + reject(error1); + }); + } + }); + } + + public saveGroup(groups: IConnectionProfileGroup[], groupFullName: string, color: string, description: string): ISaveGroupResult { + let result: ISaveGroupResult; + let groupNames = ConnectionProfileGroup.getGroupFullNameParts(groupFullName); + result = this.saveGroupInTree(groups, undefined, groupNames, color, description, 0); + return result; + } + + public editGroup(source: ConnectionProfileGroup): Promise { + let groups = this._workspaceConfigurationService.lookup(Constants.connectionGroupsArrayName).user; + let sameNameGroup = groups ? groups.find(group => group.name === source.name && group.id !== source.id) : undefined; + if (sameNameGroup) { + let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists."); + return Promise.reject(errMessage); + } + groups = groups.map(g => { + if (g.id === source.id) { + g.name = source.name; + g.description = source.description; + g.color = source.color; + source.isRenamed = false; + } + return g; + }); + return this.writeConfiguration(Constants.connectionGroupsArrayName, groups); + } + + private isSameGroupName(group1: IConnectionProfileGroup, group2: IConnectionProfileGroup): boolean { + let sameGroupName: boolean = false; + if (group1 && group2) { + sameGroupName = ((!group1.name && !group2.name) || group1.name.toUpperCase() === group2.name.toUpperCase()) && + (group1.parentId === group2.parentId || (!group1.parentId && !group2.parentId)); + } + return sameGroupName; + } + + private saveGroupInTree(groupTree: IConnectionProfileGroup[], parentId: string, groupNames: string[], color: string, description: string, index: number): ISaveGroupResult { + if (!groupTree) { + groupTree = []; + } + let newGroupId: string; + + if (index < groupNames.length) { + let groupName: string = groupNames[index]; + let newGroup: IConnectionProfileGroup = { + name: groupName, + id: undefined, + parentId: parentId, + color: color, + description: description + }; + let found = groupTree.find(group => this.isSameGroupName(group, newGroup)); + if (found) { + if (index === groupNames.length - 1) { + newGroupId = found.id; + //Found the group full name + } else { + let result = this.saveGroupInTree(groupTree, found.id, groupNames, color, description, index + 1); + groupTree = result.groups; + newGroupId = result.newGroupId; + } + + } else { + if (ConnectionProfileGroup.isRoot(newGroup.name)) { + newGroup.id = Utils.defaultGroupId; + } else { + newGroup.id = generateUuid(); + } + let result = this.saveGroupInTree(groupTree, newGroup.id, groupNames, color, description, index + 1); + newGroupId = result.newGroupId; + groupTree = result.groups; + groupTree.push(newGroup); + if (index === groupNames.length - 1) { + newGroupId = newGroup.id; + } + } + } + let groupResult: ISaveGroupResult = { + groups: groupTree, + newGroupId: newGroupId + }; + return groupResult; + } + + /** + * Get all profiles from the parsed settings file. + * This is public for testing only. + * @param parsedSettingsFile an object representing the parsed contents of the settings file. + * @returns the set of connection profiles found in the parsed settings file. + */ + private getConfiguration(key: string): TConfigurationValue { + let configs: TConfigurationValue; + + configs = this._workspaceConfigurationService.lookup(key); + return configs; + } + + /** + * Replace existing profiles in the settings file with a new set of profiles. + * @param parsedSettingsFile an object representing the parsed contents of the settings file. + * @param profiles the set of profiles to insert into the settings file. + */ + private writeConfiguration( + key: string, + profiles: IConnectionProfileStore[] | IConnectionProfileGroup[] | data.DataProtocolServerCapabilities[], + target: ConfigurationTarget = ConfigurationTarget.USER): Promise { + return new Promise((resolve, reject) => { + let configValue: IConfigurationValue = { + key: key, + value: profiles + }; + this._configurationEditService.writeConfiguration(target, configValue).then(result => { + this._workspaceConfigurationService.reloadConfiguration().then(() => { + resolve(); + }); + }, (error => { + reject(error); + })); + }); + } +} diff --git a/src/sql/parts/connection/common/connectionGlobalStatus.ts b/src/sql/parts/connection/common/connectionGlobalStatus.ts new file mode 100644 index 0000000000..b5c66fb87d --- /dev/null +++ b/src/sql/parts/connection/common/connectionGlobalStatus.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 { ConnectionSummary } from 'data'; +import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import * as LocalizedConstants from 'sql/parts/connection/common/localizedConstants'; + +// Status when making connections from the viewlet +export class ConnectionGlobalStatus { + + private _displayTime: number = 5000; // (in ms) + + constructor( + @IStatusbarService private _statusBarService: IStatusbarService + ) { + } + + public setStatusToConnected(connectionSummary: ConnectionSummary): void { + if (this._statusBarService) { + let text: string; + let connInfo: string = connectionSummary.serverName; + if (connInfo) { + if (connectionSummary.databaseName && connectionSummary.databaseName !== '') { + connInfo = connInfo + ' : ' + connectionSummary.databaseName; + } else { + connInfo = connInfo + ' : ' + ''; + } + text = LocalizedConstants.onDidConnectMessage + ' ' + connInfo; + } + this._statusBarService.setStatusMessage(text, this._displayTime); + } + } + + public setStatusToDisconnected(fileUri: string): void { + if (this._statusBarService) { + this._statusBarService.setStatusMessage(LocalizedConstants.onDidDisconnectMessage, this._displayTime); + } + } +} diff --git a/src/sql/parts/connection/common/connectionInfo.ts b/src/sql/parts/connection/common/connectionInfo.ts new file mode 100644 index 0000000000..c826be69f5 --- /dev/null +++ b/src/sql/parts/connection/common/connectionInfo.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import Interfaces = require('./interfaces'); + +/** + * Sets sensible defaults for key connection properties, especially + * if connection to Azure + * + * @export connectionInfo/fixupConnectionCredentials + * @param {Interfaces.IConnectionCredentials} connCreds connection to be fixed up + * @returns {Interfaces.IConnectionCredentials} the updated connection + */ +export function fixupConnectionCredentials(connCreds: Interfaces.IConnectionProfile): Interfaces.IConnectionProfile { + if (!connCreds.serverName) { + connCreds.serverName = ''; + } + + if (!connCreds.databaseName) { + connCreds.databaseName = ''; + } + + if (!connCreds.userName) { + connCreds.userName = ''; + } + + if (!connCreds.password) { + connCreds.password = ''; + } + return connCreds; +} + diff --git a/src/sql/parts/connection/common/connectionManagement.ts b/src/sql/parts/connection/common/connectionManagement.ts new file mode 100644 index 0000000000..814ce3c0cb --- /dev/null +++ b/src/sql/parts/connection/common/connectionManagement.ts @@ -0,0 +1,341 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IViewlet } from 'vs/workbench/common/viewlet'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { TPromise } from 'vs/base/common/winjs.base'; +import Event from 'vs/base/common/event'; +import data = require('data'); +import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import Severity from 'vs/base/common/severity'; +import { ISelectionData } from 'data'; +import { ConnectionManagementInfo } from './connectionManagementInfo'; + +export const VIEWLET_ID = 'workbench.view.connections'; + +export interface IConnectionsViewlet extends IViewlet { + search(text: string): void; +} + +/** + * Options for the actions that could happen after connecting is complete + */ +export interface IConnectionCompletionOptions { + /** + * save the connection to MRU and settings (only save to setting if profile.saveProfile is set to true) + */ + saveTheConnection: boolean; + + /** + * open the dashboard after connection is complete + */ + showDashboard: boolean; + + /** + * Parameters to be used if connecting from an editor + */ + params: INewConnectionParams; + + /** + * Open the connection dialog if connection fails + */ + showConnectionDialogOnError: boolean; + + /** + * Open the connection firewall rule dialog if connection fails + */ + showFirewallRuleOnError: boolean; +} + +export interface IConnectionResult { + connected: boolean; + errorMessage: string; + errorCode: number; + errorHandled?: boolean; +} + +export interface IConnectionCallbacks { + onConnectStart(): void; + onConnectReject(error?: string): void; + onConnectSuccess(params?: INewConnectionParams): void; + onDisconnect(): void; +} + +export const SERVICE_ID = 'connectionManagementService'; + +export const IConnectionManagementService = createDecorator(SERVICE_ID); + +export interface IConnectionManagementService { + _serviceBrand: any; + + // Event Emitters + onAddConnectionProfile: Event; + onDeleteConnectionProfile: Event; + onConnect: Event; + onDisconnect: Event; + onConnectionChanged: Event; + onLanguageFlavorChanged: Event; + + /** + * Opens the connection dialog to create new connection + */ + showConnectionDialog(params?: INewConnectionParams, model?: IConnectionProfile, error?: string): Promise; + + /** + * Opens the add server group dialog + */ + showCreateServerGroupDialog(callbacks?: IServerGroupDialogCallbacks): Promise; + + /** + * Opens the edit server group dialog + */ + showEditServerGroupDialog(group: ConnectionProfileGroup): Promise; + + /** + * Load the password and opens a new connection + */ + connect(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise; + + /** + * Opens a new connection and save the profile in settings + */ + connectAndSaveProfile(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise; + + /** + * Finds existing connection for given profile and purpose is any exists. + * The purpose is connection by default + */ + findExistingConnection(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): ConnectionProfile; + + /** + * If there's already a connection for given profile and purpose, returns the ownerUri for the connection + * otherwise tries to make a connection and returns the owner uri when connection is complete + * The purpose is connection by default + */ + connectIfNotConnected(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): Promise; + + /** + * Adds the successful connection to MRU and send the connection error back to the connection handler for failed connections + */ + onConnectionComplete(handle: number, connectionInfoSummary: data.ConnectionInfoSummary): void; + + onIntelliSenseCacheComplete(handle: number, connectionUri: string): void; + + onConnectionChangedNotification(handle: number, changedConnInfo: data.ChangedConnectionInfo); + + getConnectionGroups(): ConnectionProfileGroup[]; + + getRecentConnections(): ConnectionProfile[]; + + clearRecentConnectionsList(): void; + + getActiveConnections(): ConnectionProfile[]; + + saveProfileGroup(profile: IConnectionProfileGroup): Promise; + + changeGroupIdForConnectionGroup(source: IConnectionProfileGroup, target: IConnectionProfileGroup): Promise; + + changeGroupIdForConnection(source: ConnectionProfile, targetGroupName: string): Promise; + + deleteConnection(connection: ConnectionProfile): Promise; + + deleteConnectionGroup(group: ConnectionProfileGroup): Promise; + + getAdvancedProperties(): data.ConnectionOption[]; + + getConnectionId(connectionProfile: IConnectionProfile): string; + + getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string; + + isConnected(fileUri: string): boolean; + + /** + * Returns true if the connection profile is connected + */ + isProfileConnected(connectionProfile: IConnectionProfile): boolean; + + /** + * Returns true if the connection profile is connecting + */ + isProfileConnecting(connectionProfile: IConnectionProfile): boolean; + + isRecent(connectionProfile: ConnectionProfile): boolean; + + isConnected(fileUri: string, connectionProfile?: ConnectionProfile): boolean; + + disconnectEditor(owner: IConnectableInput, force?: boolean): Promise; + + disconnect(connection: ConnectionProfile): Promise; + + disconnect(ownerUri: string): Promise; + + addSavedPassword(connectionProfile: IConnectionProfile): Promise; + + listDatabases(connectionUri: string): Thenable; + + /** + * Register a connection provider + */ + registerProvider(providerId: string, provider: data.ConnectionProvider): void; + + editGroup(group: ConnectionProfileGroup): Promise; + + getConnectionProfile(fileUri: string): IConnectionProfile; + + getConnectionInfo(fileUri: string): ConnectionManagementInfo; + + /** + * Cancels the connection + */ + cancelConnection(connection: IConnectionProfile): Thenable; + + /** + * Changes the database for an active connection + */ + changeDatabase(connectionUri: string, databaseName: string): Thenable; + + /** + * Cancels the connection for the editor + */ + cancelEditorConnection(owner: IConnectableInput): Thenable; + + showDashboard(connection: ConnectionProfile): Thenable; + + closeDashboard(uri: string): void; + + getProviderIdFromUri(ownerUri: string): string; + + hasRegisteredServers(): boolean; + + getCapabilities(providerName: string): data.DataProtocolServerCapabilities; + + canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean; + + /** + * Sends a notification that the language flavor for a given URI has changed. + * For SQL, this would be the specific SQL implementation being used. + * + * @param {string} uri the URI of the resource whose language has changed + * @param {string} language the base language + * @param {string} flavor the specific language flavor that's been set + * + * @memberof IConnectionManagementService + */ + doChangeLanguageFlavor(uri: string, language: string, flavor: string): void; + + /** + * Ensures that a default language flavor is set for a URI, if none has already been defined. + * @param {string} uri document identifier + * @memberof ConnectionManagementService + */ + ensureDefaultLanguageFlavor(uri: string): void; + + /** + * Gets an array of all known providers. + * + * @returns {string[]} An array of provider names + * @memberof IConnectionManagementService + */ + getProviderNames(): string[]; + + /** + * Refresh the IntelliSense cache for the connection with the given URI + */ + rebuildIntelliSenseCache(uri: string): Thenable; +} + +export const IConnectionDialogService = createDecorator('connectionDialogService'); +export interface IConnectionDialogService { + _serviceBrand: any; + showDialog(connectionManagementService: IConnectionManagementService, params: INewConnectionParams, model: IConnectionProfile, error?: string): Thenable; +} + +export interface IServerGroupDialogCallbacks { + onAddGroup(groupName: string): void; + onClose(): void; +} +export const IServerGroupController = createDecorator('serverGroupController'); +export interface IServerGroupController { + _serviceBrand: any; + showCreateGroupDialog(connectionManagementService: IConnectionManagementService, callbacks?: IServerGroupDialogCallbacks): TPromise; + showEditGroupDialog(connectionManagementService: IConnectionManagementService, group: ConnectionProfileGroup): TPromise; +} + +export const IErrorMessageService = createDecorator('errorMessageService'); +export interface IErrorMessageService { + _serviceBrand: any; + showDialog(severity: Severity, headerTitle: string, message: string): void; +} + +export enum ServiceOptionType { + string = 0, + multistring = 1, + password = 2, + number = 3, + category = 4, + boolean = 5 +} + +export enum ConnectionOptionSpecialType { + serverName = 0, + databaseName = 1, + authType = 2, + userName = 3, + password = 4, + appName = 5 +} + +export enum RunQueryOnConnectionMode { + none = 0, + executeQuery = 1, + executeCurrentQuery = 2, + estimatedQueryPlan = 3 +} + +export interface INewConnectionParams { + connectionType: ConnectionType; + input?: IConnectableInput; + runQueryOnCompletion?: RunQueryOnConnectionMode; + querySelection?: ISelectionData; + showDashboard?: boolean; +} + +export interface IConnectableInput { + uri: string; + onConnectStart(): void; + onConnectReject(error?: string): void; + onConnectSuccess(params?: INewConnectionParams): void; + onDisconnect(): void; +} + +export enum ConnectionType { + default = 0, + editor = 1 +} + +export enum MetadataType { + Table = 0, + View = 1, + SProc = 2, + Function = 3 +} + +export enum TaskStatus { + notStarted = 0, + inProgress = 1, + succeeded = 2, + succeededWithWarning = 3, + failed = 4, + canceled = 5 +} + +export interface IConnectionParams { + connectionUri: string; + connectionProfile: IConnectionProfile; +} \ No newline at end of file diff --git a/src/sql/parts/connection/common/connectionManagementInfo.ts b/src/sql/parts/connection/common/connectionManagementInfo.ts new file mode 100644 index 0000000000..2f0c44f63c --- /dev/null +++ b/src/sql/parts/connection/common/connectionManagementInfo.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import * as data from 'data'; +import { StopWatch } from 'vs/base/common/stopwatch'; + +/** + * Information for a document's connection. Exported for testing purposes. + */ +export class ConnectionManagementInfo { + /** + * Connection GUID returned from the service host + */ + public connectionId: string; + + + public providerId: string; + + /** + * Credentials used to connect + */ + public connectionProfile: ConnectionProfile; + + /** + * Callback for when a connection notification is received. + */ + public connectHandler: (result: boolean, errorMessage?: string, errorCode?: number) => void; + + /** + * Information about the SQL Server instance. + */ + //public serverInfo: ConnectionContracts.ServerInfo; + + /** + * Timer for tracking extension connection time. + */ + public extensionTimer: StopWatch; + + /** + * Timer for tracking service connection time. + */ + public serviceTimer: StopWatch; + + /** + * Timer for tracking intelliSense activation time. + */ + public intelliSenseTimer: StopWatch; + + /** + * Whether the connection is in the process of connecting. + */ + public connecting: boolean; + + /** + * Whether the connection should be deleted after connection is complete. + */ + public deleted: boolean; + + /** + * Information about the connected server. + */ + serverInfo: data.ServerInfo; + + /** + * Owner uri assigned to the connection + */ + public ownerUri: string; +} \ No newline at end of file diff --git a/src/sql/parts/connection/common/connectionManagementService.ts b/src/sql/parts/connection/common/connectionManagementService.ts new file mode 100644 index 0000000000..fd03723e98 --- /dev/null +++ b/src/sql/parts/connection/common/connectionManagementService.ts @@ -0,0 +1,1311 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; +import { + IConnectionManagementService, IConnectionDialogService, INewConnectionParams, + ConnectionType, IConnectableInput, IConnectionCompletionOptions, IConnectionCallbacks, + IConnectionParams, IConnectionResult, IServerGroupController, IServerGroupDialogCallbacks, + RunQueryOnConnectionMode +} from 'sql/parts/connection/common/connectionManagement'; +import { ConnectionStore } from 'sql/parts/connection/common/connectionStore'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import * as Utils from 'sql/parts/connection/common/utils'; +import * as Constants from 'sql/parts/connection/common/constants'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { ICredentialsService } from 'sql/services/credentials/credentialsService'; +import * as ConnectionContracts from 'sql/parts/connection/common/connection'; +import { ConnectionStatusManager } from 'sql/parts/connection/common/connectionStatusManager'; +import { DashboardInput } from 'sql/parts/dashboard/dashboardInput'; +import { ConnectionGlobalStatus } from 'sql/parts/connection/common/connectionGlobalStatus'; +import { ConnectionStatusbarItem } from 'sql/parts/connection/common/connectionStatus'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import * as TelemetryUtils from 'sql/common/telemetryUtilities'; +import { warn } from 'sql/base/common/log'; +import { IResourceProviderService } from 'sql/parts/accountManagement/common/interfaces'; +import { IAngularEventingService, AngularEventType } from 'sql/services/angularEventing/angularEventingService'; + +import * as data from 'data'; + +import * as nls from 'vs/nls'; +import * as errors from 'vs/base/common/errors'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import * as platform from 'vs/platform/registry/common/platform'; +import { Memento } from 'vs/workbench/common/memento'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ConnectionProfileGroup, IConnectionProfileGroup } from './connectionProfileGroup'; +import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { EditorGroup } from 'vs/workbench/common/editor/editorStacksModel'; +import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; +import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { Deferred } from 'sql/base/common/promise'; + +export class ConnectionManagementService implements IConnectionManagementService { + + _serviceBrand: any; + + private disposables: IDisposable[] = []; + + private _providers: { [handle: string]: data.ConnectionProvider; } = Object.create(null); + + private _uriToProvider: { [uri: string]: string; } = Object.create(null); + + private _connectionStatusManager: ConnectionStatusManager; + + private _onAddConnectionProfile: Emitter; + private _onDeleteConnectionProfile: Emitter; + private _onConnect: Emitter; + private _onDisconnect: Emitter; + private _onConnectRequestSent: Emitter; + private _onConnectionChanged: Emitter; + private _onLanguageFlavorChanged: Emitter; + + private _connectionGlobalStatus: ConnectionGlobalStatus; + + constructor( + private _connectionMemento: Memento, + private _connectionStore: ConnectionStore, + @IConnectionDialogService private _connectionDialogService: IConnectionDialogService, + @IServerGroupController private _serverGroupController: IServerGroupController, + @ICommandService private _commandService: ICommandService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IWorkspaceContextService private _contextService: IWorkspaceContextService, + @IStorageService private _storageService: IStorageService, + @ITelemetryService private _telemetryService: ITelemetryService, + @IConfigurationEditingService private _configurationEditService: IConfigurationEditingService, + @IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService, + @ICredentialsService private _credentialsService: ICredentialsService, + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService, + @IQuickOpenService private _quickOpenService: IQuickOpenService, + @IEditorGroupService private _editorGroupService: IEditorGroupService, + @IStatusbarService private _statusBarService: IStatusbarService, + @IResourceProviderService private _resourceProviderService: IResourceProviderService, + @IViewletService private _viewletService: IViewletService, + @IAngularEventingService private _angularEventing: IAngularEventingService + ) { + // _connectionMemento and _connectionStore are in constructor to enable this class to be more testable + if (!this._connectionMemento) { + this._connectionMemento = new Memento('ConnectionManagement'); + } + if (!this._connectionStore) { + this._connectionStore = new ConnectionStore(_storageService, this._connectionMemento, + _configurationEditService, this._workspaceConfigurationService, this._credentialsService, this._capabilitiesService); + } + + this._connectionStatusManager = new ConnectionStatusManager(this._capabilitiesService); + this._connectionGlobalStatus = new ConnectionGlobalStatus(this._statusBarService); + + // Setting up our event emitters + this._onAddConnectionProfile = new Emitter(); + this._onDeleteConnectionProfile = new Emitter(); + this._onConnect = new Emitter(); + this._onDisconnect = new Emitter(); + this._onConnectionChanged = new Emitter(); + this._onConnectRequestSent = new Emitter(); + this._onLanguageFlavorChanged = new Emitter(); + + this._onProvidersReady = new Deferred(); + + // Register Statusbar item + (platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( + ConnectionStatusbarItem, + statusbar.StatusbarAlignment.RIGHT, + 100 /* High Priority */ + )); + + if (_capabilitiesService && _capabilitiesService.onProviderRegisteredEvent) { + _capabilitiesService.onProviderRegisteredEvent((capabilities => { + if (capabilities.providerName === 'MSSQL') { + if (!this.hasRegisteredServers()) { + // prompt the user for a new connection on startup if no profiles are registered + this.showConnectionDialog(); + } + } + })); + } + + this.disposables.push(this._onAddConnectionProfile); + this.disposables.push(this._onDeleteConnectionProfile); + } + + // Event Emitters + public get onAddConnectionProfile(): Event { + return this._onAddConnectionProfile.event; + } + + public get onDeleteConnectionProfile(): Event { + return this._onDeleteConnectionProfile.event; + } + + public get onConnect(): Event { + return this._onConnect.event; + } + + public get onDisconnect(): Event { + return this._onDisconnect.event; + } + + public get onConnectionChanged(): Event { + return this._onConnectionChanged.event; + } + + public get onConnectionRequestSent(): Event { + return this._onConnectRequestSent.event; + } + + public get onLanguageFlavorChanged(): Event { + return this._onLanguageFlavorChanged.event; + } + + private _onProvidersReady: Deferred; + + private onProvidersReady(): Promise { + return this._onProvidersReady.promise; + } + + private _providerCount: number = 0; + + // Connection Provider Registration + public registerProvider(providerId: string, provider: data.ConnectionProvider): void { + this._providers[providerId] = provider; + + // temporarily close splash screen when a connection provider has been registered + // @todo remove this code once a proper initialization event is available (karlb 4/1/2017) + ++this._providerCount; + + this._onProvidersReady.resolve(); + + if (this._providerCount === 1) { + // show the Registered Server viewlet + let startupConfig = this._workspaceConfigurationService.getConfiguration('startup'); + if (startupConfig) { + let showServerViewlet = startupConfig['alwaysShowServersView']; + if (showServerViewlet) { + // only show the Servers viewlet if there isn't another active viewlet + if (!this._viewletService.getActiveViewlet()) { + this._commandService.executeCommand('workbench.view.connections', {}); + } + } + } + } + } + + /** + * Opens the connection dialog + * @param params Include the uri, type of connection + * @param model the existing connection profile to create a new one from + */ + public showConnectionDialog(params?: INewConnectionParams, model?: IConnectionProfile, error?: string): Promise { + let self = this; + return new Promise((resolve, reject) => { + if (!params) { + params = { connectionType: ConnectionType.default }; + } + if (!model && params.input && params.input.uri) { + model = this._connectionStatusManager.getConnectionProfile(params.input.uri); + } + self._connectionDialogService.showDialog(self, params, model, error).then(() => { + resolve(); + }, dialogError => { + warn('failed to open the connection dialog. error: ' + dialogError); + reject(dialogError); + }); + }); + } + + /** + * Opens the add server group dialog + */ + public showCreateServerGroupDialog(callbacks?: IServerGroupDialogCallbacks): Promise { + let self = this; + return new Promise((resolve, reject) => { + self._serverGroupController.showCreateGroupDialog(self, callbacks).then(() => { + resolve(); + }, error => { + reject(); + }); + }); + } + + /** + * Opens the edit server group dialog + */ + public showEditServerGroupDialog(group: ConnectionProfileGroup): Promise { + let self = this; + return new Promise((resolve, reject) => { + self._serverGroupController.showEditGroupDialog(self, group).then(() => { + resolve(); + }, error => { + reject(); + }); + }); + } + + /** + * Load the password for the profile + * @param connectionProfile Connection Profile + */ + public addSavedPassword(connectionProfile: IConnectionProfile): Promise { + return this._connectionStore.addSavedPassword(connectionProfile).then(result => result.profile); + } + + /** + * Get the connections provider ID from an connection URI + */ + public getProviderIdFromUri(ownerUri: string): string { + let providerId = this._uriToProvider[ownerUri]; + if (!providerId) { + providerId = this._connectionStatusManager.getProviderIdFromUri(ownerUri); + } + + return providerId; + } + + /** + * Loads the password and try to connect. If fails, shows the dialog so user can change the connection + * @param Connection Profile + * @param owner of the connection. Can be the editors + * @param options to use after the connection is complete + */ + private tryConnect(connection: IConnectionProfile, owner: IConnectableInput, options?: IConnectionCompletionOptions): Promise { + return new Promise((resolve, reject) => { + // Load the password if it's not already loaded + this._connectionStore.addSavedPassword(connection).then(result => { + let newConnection = result.profile; + let foundPassword = result.savedCred; + + // If there is no password, try to load it from an existing connection + if (!foundPassword && this._connectionStore.isPasswordRequired(newConnection)) { + let existingConnection = this._connectionStatusManager.findConnectionProfile(connection); + if (existingConnection && existingConnection.connectionProfile) { + newConnection.password = existingConnection.connectionProfile.password; + foundPassword = true; + } + } + // If the password is required and still not loaded show the dialog + if (!foundPassword && this._connectionStore.isPasswordRequired(newConnection) && !newConnection.password) { + resolve(this.showConnectionDialogOnError(connection, owner, { connected: false, errorMessage: undefined, errorCode: undefined }, options)); + } else { + // Try to connect + this.connectWithOptions(newConnection, owner.uri, options, owner).then(connectionResult => { + if (!connectionResult.connected && !connectionResult.errorHandled) { + // If connection fails show the dialog + resolve(this.showConnectionDialogOnError(connection, owner, connectionResult, options)); + } else { + //Resolve with the connection result + resolve(connectionResult); + } + }).catch(connectionError => { + reject(connectionError); + }); + } + }).catch(err => { + reject(err); + }); + }); + } + + /** + * If showing the dialog on error is set to true in the options, shows the dialog with the error + * otherwise does nothing + */ + private showConnectionDialogOnError( + connection: IConnectionProfile, + owner: IConnectableInput, + connectionResult: IConnectionResult, + options?: IConnectionCompletionOptions): Promise { + + return new Promise((resolve, reject) => { + if (options && options.showConnectionDialogOnError) { + let params: INewConnectionParams = options && options.params ? options.params : { + connectionType: this._connectionStatusManager.isDefaultTypeUri(owner.uri) ? ConnectionType.default : ConnectionType.editor, + input: owner, + runQueryOnCompletion: RunQueryOnConnectionMode.none, + showDashboard: options.showDashboard + }; + this.showConnectionDialog(params, connection, connectionResult.errorMessage).then(() => { + resolve(connectionResult); + }).catch(err => { + reject(err); + }); + } else { + resolve(connectionResult); + } + }); + } + + /** + * Load the password and opens a new connection + * @param Connection Profile + * @param uri assigned to the profile (used only when connecting from an editor) + * @param options to be used after the connection is completed + * @param callbacks to call after the connection is completed + */ + public connect(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise { + if (!uri) { + uri = Utils.generateUri(connection); + } + let input: IConnectableInput = options && options.params ? options.params.input : undefined; + if (!input) { + input = { + onConnectReject: callbacks ? callbacks.onConnectReject : undefined, + onConnectStart: callbacks ? callbacks.onConnectStart : undefined, + onConnectSuccess: callbacks ? callbacks.onConnectSuccess : undefined, + onDisconnect: callbacks ? callbacks.onDisconnect : undefined, + uri: uri + }; + } + + + if (uri !== input.uri) { + //TODO: this should never happen. If the input is already passed, it should have the uri + warn(`the given uri is different that the input uri. ${uri}|${input.uri}`); + } + return this.tryConnect(connection, input, options); + } + + /** + * If there's already a connection for given profile and purpose, returns the ownerUri for the connection + * otherwise tries to make a connection and returns the owner uri when connection is complete + * The purpose is connection by default + */ + public connectIfNotConnected(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): Promise { + return new Promise((resolve, reject) => { + let ownerUri: string = Utils.generateUri(connection, purpose); + if (this._connectionStatusManager.isConnected(ownerUri)) { + resolve(this._connectionStatusManager.getOriginalOwnerUri(ownerUri)); + } else { + this.connect(connection, ownerUri).then(connectionResult => { + if (connectionResult && connectionResult.connected) { + resolve(this._connectionStatusManager.getOriginalOwnerUri(ownerUri)); + } else { + reject(connectionResult.errorMessage); + } + }, error => { + reject(error); + }); + } + }); + } + + /** + * Opens a new connection and saves the profile in the settings. + * This method doesn't load the password because it only gets called from the + * connection dialog and password should be already in the profile + */ + public connectAndSaveProfile(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): + Promise { + if (!options) { + options = { + saveTheConnection: true, + showDashboard: false, + params: undefined, + showConnectionDialogOnError: false, + showFirewallRuleOnError: true + }; + } + + // Do not override options.saveTheConnection as this is for saving to the server groups, not the MRU. + // MRU save always happens through a different path using tryAddActiveConnection + return this.connectWithOptions(connection, uri, options, callbacks); + } + + private connectWithOptions(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): + Promise { + connection.options['groupId'] = connection.groupId; + connection.options['databaseDisplayName'] = connection.databaseName; + + if (!uri) { + uri = Utils.generateUri(connection); + } + uri = this._connectionStatusManager.getOriginalOwnerUri(uri); + if (!callbacks) { + callbacks = { + onConnectReject: () => { }, + onConnectStart: () => { }, + onConnectSuccess: () => { }, + onDisconnect: () => { } + }; + } + if (!options) { + options = { + saveTheConnection: false, + showDashboard: false, + params: undefined, + showConnectionDialogOnError: false, + showFirewallRuleOnError: true + }; + } + return new Promise((resolve, reject) => { + if (callbacks.onConnectStart) { + callbacks.onConnectStart(); + } + this.createNewConnection(uri, connection).then(connectionResult => { + if (connectionResult && connectionResult.connected) { + if (callbacks.onConnectSuccess) { + callbacks.onConnectSuccess(options.params); + } + if (options.saveTheConnection) { + this.saveToSettings(uri, connection).then(value => { + this._onAddConnectionProfile.fire(connection); + this.doActionsAfterConnectionComplete(value, options); + }); + } else { + connection.saveProfile = false; + this.doActionsAfterConnectionComplete(uri, options); + } + resolve(connectionResult); + } else if (connectionResult && connectionResult.errorMessage) { + this.handleConnectionError(connection, uri, options, callbacks, connectionResult).then(result => { + resolve(result); + }).catch(handleConnectionError => { + if (callbacks.onConnectReject) { + callbacks.onConnectReject(handleConnectionError); + } + reject(handleConnectionError); + }); + } else { + if (callbacks.onConnectReject) { + callbacks.onConnectReject(nls.localize('connectionNotAcceptedError', 'Connection Not Accepted')); + } + resolve(connectionResult); + } + }).catch(err => { + if (callbacks.onConnectReject) { + callbacks.onConnectReject(err); + } + reject(err); + }); + }); + } + + private handleConnectionError(connection: IConnectionProfile, uri: string, options: IConnectionCompletionOptions, callbacks: IConnectionCallbacks, connectionResult: IConnectionResult) { + return new Promise((resolve, reject) => { + let connectionNotAcceptedError = nls.localize('connectionNotAcceptedError', 'Connection Not Accepted'); + if (options.showFirewallRuleOnError && connectionResult.errorCode) { + this.handleFirewallRuleError(connection, connectionResult).then(success => { + if (success) { + options.showFirewallRuleOnError = false; + this.connectWithOptions(connection, uri, options, callbacks).then((result) => { + resolve(result); + }).catch(connectionError => { + reject(connectionError); + }); + } else { + if (callbacks.onConnectReject) { + callbacks.onConnectReject(connectionNotAcceptedError); + } + resolve(connectionResult); + } + }).catch(handleFirewallRuleError => { + reject(handleFirewallRuleError); + }); + } else { + if (callbacks.onConnectReject) { + callbacks.onConnectReject(connectionNotAcceptedError); + } + resolve(connectionResult); + } + }); + } + + private handleFirewallRuleError(connection: IConnectionProfile, connectionResult: IConnectionResult): Promise { + return new Promise((resolve, reject) => { + this._resourceProviderService.handleFirewallRule(connectionResult.errorCode, connectionResult.errorMessage, connection.providerName).then(response => { + if (response.canHandleFirewallRule) { + connectionResult.errorHandled = true; + this._resourceProviderService.showFirewallRuleDialog(connection, response.ipAddress, response.resourceProviderId).then(success => { + resolve(success); + }).catch(showFirewallRuleError => { + reject(showFirewallRuleError); + }); + } else { + resolve(false); + } + }).catch(handleFirewallRuleError => { + reject(handleFirewallRuleError); + }); + }); + } + + private doActionsAfterConnectionComplete(uri: string, options: IConnectionCompletionOptions, ) { + let connectionManagementInfo = this._connectionStatusManager.findConnection(uri); + if (options.showDashboard) { + this.showDashboardForConnectionManagementInfo(connectionManagementInfo.connectionProfile); + } + this._onConnect.fire({ + connectionUri: uri, + connectionProfile: connectionManagementInfo.connectionProfile + }); + } + + public showDashboard(connection: ConnectionProfile): Thenable { + return this.showDashboardForConnectionManagementInfo(connection); + } + + private showDashboardForConnectionManagementInfo(connectionProfile: IConnectionProfile): Thenable { + // if dashboard profile is already open, focus on that tab + if (!this.focusDashboard(connectionProfile)) { + let dashboardInput: DashboardInput = this._instantiationService ? this._instantiationService.createInstance(DashboardInput, connectionProfile) : undefined; + return dashboardInput.initializedPromise.then(() => { + this._editorService.openEditor(dashboardInput, { pinned: true }, false); + }).then(() => true); + } else { + return Promise.resolve(true); + } + } + + private focusDashboard(profile: IConnectionProfile): boolean { + let found: boolean = false; + let options = { + preserveFocus: false, + revealIfVisible: true, + revealInCenterIfOutsideViewport: true, + pinned: true + }; + let model = this._editorGroupService.getStacksModel(); + // check if editor is already present + if (model) { + model.groups.map(group => { + if (group instanceof EditorGroup) { + group.getEditors().map(editor => { + if (editor instanceof DashboardInput) { + if (DashboardInput.profileMatches(profile, editor.connectionProfile)) { + editor.connectionProfile.databaseName = profile.databaseName; + // change focus to the matched editor + let position = model.positionOfGroup(group); + this._editorGroupService.activateGroup(model.groupAt(position)); + this._editorService.openEditor(editor, options, position) + .done(() => { + this._editorGroupService.activateGroup(model.groupAt(position)); + if (!profile.databaseName || Utils.isMaster(profile)) { + this._angularEventing.sendAngularEvent(editor.uri, AngularEventType.NAV_SERVER); + } else { + this._angularEventing.sendAngularEvent(editor.uri, AngularEventType.NAV_DATABASE); + } + found = true; + }, errors.onUnexpectedError); + } + } + }); + } + }); + } + return found; + } + + public closeDashboard(uri: string): void { + let model = this._editorGroupService.getStacksModel(); + if (model) { + model.groups.map(group => { + if (group instanceof EditorGroup) { + group.getEditors().map(editor => { + if (editor instanceof DashboardInput) { + if (editor.uri === uri && this._editorGroupService instanceof EditorPart) { + // close matched editor + let position = model.positionOfGroup(group); + this._editorGroupService.closeEditor(position, editor); + } + } + }); + } + }); + } + } + + public getConnectionGroups(): ConnectionProfileGroup[] { + return this._connectionStore.getConnectionProfileGroups(); + } + + public getRecentConnections(): ConnectionProfile[] { + return this._connectionStore.getRecentlyUsedConnections(); + } + + + public clearRecentConnectionsList(): void { + return this._connectionStore.clearRecentlyUsed(); + } + + public getActiveConnections(): ConnectionProfile[] { + return this._connectionStore.getActiveConnections(); + } + + public saveProfileGroup(profile: IConnectionProfileGroup): Promise { + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.AddServerGroup); + return new Promise((resolve, reject) => { + this._connectionStore.saveProfileGroup(profile).then(groupId => { + this._onAddConnectionProfile.fire(); + resolve(groupId); + }).catch(err => { + reject(err); + }); + }); + } + + public getProviderNames(): string[] { + return Object.keys(this._providers); + } + + public getCapabilities(providerName: string): data.DataProtocolServerCapabilities { + let capabilities = this._capabilitiesService.getCapabilities(); + if (capabilities !== undefined && capabilities.length > 0) { + return capabilities.find(c => c.providerName === providerName); + } + return undefined; + } + + public getAdvancedProperties(): data.ConnectionOption[] { + + let capabilities = this._capabilitiesService.getCapabilities(); + if (capabilities !== undefined && capabilities.length > 0) { + // just grab the first registered provider for now, this needs to change + // to lookup based on currently select provider + let providerCapabilities = capabilities[0]; + if (!!providerCapabilities.connectionProvider) { + return providerCapabilities.connectionProvider.options; + } + } + + return undefined; + } + + public hasRegisteredServers(): boolean { + return this.doHasRegisteredServers(this.getConnectionGroups()); + } + + private doHasRegisteredServers(root: ConnectionProfileGroup[]): boolean { + + if (!root || root.length === 0) { + return false; + } + + for (let i = 0; root.length; ++i) { + let item = root[i]; + + if (!item) { + return false; + } + + if (item.connections && item.connections.length > 0) { + return true; + } + + if (this.doHasRegisteredServers(item.children)) { + return true; + } + } + + return false; + } + + public getConnectionId(connectionProfile: IConnectionProfile): string { + return this._connectionStatusManager.getOriginalOwnerUri(Utils.generateUri(connectionProfile)); + } + + /** + * Returns a formatted URI in case the database field is empty for the original + * URI, which happens when the connected database is master or the default database + * @param uri + * @param connectionProfile + */ + public getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string { + if (this._connectionStatusManager.isDefaultTypeUri(uri)) { + return this.getConnectionId(connectionProfile); + } else { + return uri; + } + } + + /** + * Sends a notification that the language flavor for a given URI has changed. + * For SQL, this would be the specific SQL implementation being used. + * + * @param {string} uri the URI of the resource whose language has changed + * @param {string} language the base language + * @param {string} flavor the specific language flavor that's been set + * @throws {Error} if the provider is not in the list of registered providers + * @memberof ConnectionManagementService + */ + public doChangeLanguageFlavor(uri: string, language: string, provider: string): void { + if (provider in this._providers) { + this._onLanguageFlavorChanged.fire({ + uri: uri, + language: language, + flavor: provider + }); + } else { + throw new Error(`provider "${provider}" is not registered`); + } + } + + /** + * Ensures that a default language flavor is set for a URI, if none has already been defined. + * @param {string} uri document identifier + * @memberof ConnectionManagementService + */ + public ensureDefaultLanguageFlavor(uri: string): void { + if (!this.getProviderIdFromUri(uri)) { + // Lookup the default settings and use this + let defaultProvider = WorkbenchUtils.getSqlConfigValue(this._workspaceConfigurationService, Constants.defaultEngine); + if (defaultProvider && defaultProvider in this._providers) { + // Only set a default if it's in the list of registered providers + this.doChangeLanguageFlavor(uri, 'sql', defaultProvider); + } + } + } + + // Request Senders + private sendConnectRequest(connection: IConnectionProfile, uri: string): Thenable { + let connectionInfo = Object.assign({}, { + options: connection.options + }); + + // setup URI to provider ID map for connection + this._uriToProvider[uri] = connection.providerName; + + return new Promise((resolve, reject) => { + this.onProvidersReady().then(() => { + this._providers[connection.providerName].connect(uri, connectionInfo); + this._onConnectRequestSent.fire(); + + // TODO make this generic enough to handle non-SQL languages too + this.doChangeLanguageFlavor(uri, 'sql', connection.providerName); + resolve(true); + }); + }); + } + + private sendDisconnectRequest(uri: string): Thenable { + let providerId: string = this.getProviderIdFromUri(uri); + if (!providerId) { + return Promise.resolve(false); + } + + return new Promise((resolve, reject) => { + this._providers[providerId].disconnect(uri); + resolve(true); + }); + } + + private sendCancelRequest(uri: string): Thenable { + let providerId: string = this.getProviderIdFromUri(uri); + if (!providerId) { + return Promise.resolve(false); + } + + return new Promise((resolve, reject) => { + this._providers[providerId].cancelConnect(uri); + resolve(true); + }); + } + + private sendListDatabasesRequest(uri: string): Thenable { + let providerId: string = this.getProviderIdFromUri(uri); + if (!providerId) { + return Promise.resolve(undefined); + } + + return new Promise((resolve, reject) => { + let provider = this._providers[providerId]; + provider.listDatabases(uri).then(result => { + resolve(result); + }, error => { + reject(error); + }); + }); + } + + private saveToSettings(id: string, connection: IConnectionProfile): Promise { + + return new Promise((resolve, reject) => { + this._connectionStore.saveProfile(connection).then(savedProfile => { + let newId = this._connectionStatusManager.updateConnectionProfile(savedProfile, id); + return resolve(newId); + }); + }); + } + + /** + * Add a connection to the active connections list. + */ + private tryAddActiveConnection(connectionManagementInfo: ConnectionManagementInfo, newConnection: IConnectionProfile): void { + if (newConnection) { + this._connectionStore.addActiveConnection(newConnection) + .then(() => { + connectionManagementInfo.connectHandler(true); + }, err => { + connectionManagementInfo.connectHandler(false, err); + }); + } else { + connectionManagementInfo.connectHandler(false); + } + } + + private addTelemetryForConnection(connection: ConnectionManagementInfo): void { + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.DatabaseConnected, { + connectionType: connection.serverInfo ? (connection.serverInfo.isCloud ? 'Azure' : 'Standalone') : '', + provider: connection.connectionProfile.providerName, + serverVersion: connection.serverInfo ? connection.serverInfo.serverVersion : '', + serverEdition: connection.serverInfo ? connection.serverInfo.serverEdition : '', + + extensionConnectionTime: connection.extensionTimer.elapsed() - connection.serviceTimer.elapsed(), + serviceConnectionTime: connection.serviceTimer.elapsed() + }); + } + + private addTelemetryForConnectionDisconnected(connection: IConnectionProfile): void { + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.DatabaseDisconnected, { + provider: connection.providerName + }); + } + + public onConnectionComplete(handle: number, info: data.ConnectionInfoSummary): void { + const self = this; + let connection = this._connectionStatusManager.onConnectionComplete(info); + + if (info.connectionId) { + if (info.connectionSummary && info.connectionSummary.databaseName) { + this._connectionStatusManager.updateDatabaseName(info); + } + connection.serverInfo = info.serverInfo; + connection.extensionTimer.stop(); + + connection.connectHandler(true); + let activeConnection = connection.connectionProfile; + self.tryAddActiveConnection(connection, activeConnection); + self.addTelemetryForConnection(connection); + } else { + connection.connectHandler(false, info.messages, info.errorNumber); + } + + if (this._connectionStatusManager.isDefaultTypeUri(info.ownerUri)) { + this._connectionGlobalStatus.setStatusToConnected(info.connectionSummary); + } + } + + public onConnectionChangedNotification(handle: number, changedConnInfo: data.ChangedConnectionInfo): void { + let profile: IConnectionProfile = this._connectionStatusManager.onConnectionChanged(changedConnInfo); + this._notifyConnectionChanged(profile, changedConnInfo.connectionUri); + } + + private _notifyConnectionChanged(profile: IConnectionProfile, connectionUri: string): void { + if (profile) { + this._onConnectionChanged.fire({ + connectionProfile: profile, + connectionUri: connectionUri + }); + } + } + + public onIntelliSenseCacheComplete(handle: number, connectionUri: string): void { + } + + public dispose(): void { + this.disposables = dispose(this.disposables); + } + + public shutdown(): void { + this._connectionStore.clearActiveConnections(); + this._connectionMemento.saveMemento(); + } + + public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise { + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.MoveServerConnection); + return this._connectionStore.changeGroupIdForConnectionGroup(source, target); + } + + public changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise { + let id = Utils.generateUri(source); + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.MoveServerGroup); + return this._connectionStore.changeGroupIdForConnection(source, targetGroupId).then(result => { + if (id && targetGroupId) { + source.groupId = targetGroupId; + this._connectionStatusManager.updateConnectionProfile(source, id); + } + }); + } + + /** + * Returns true if the connection can be moved to another group + */ + public canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean { + return this._connectionStore.canChangeConnectionConfig(profile, newGroupID); + } + + public isRecent(connectionProfile: ConnectionProfile): boolean { + let recentConnections = this._connectionStore.getRecentlyUsedConnections(); + recentConnections = recentConnections.filter(con => { + return connectionProfile.id === con.id; + }); + return (recentConnections.length >= 1); + } + // Disconnect a URI from its current connection + // The default editor implementation does not perform UI updates + // The default force implementation is set to false + public disconnectEditor(owner: IConnectableInput, force: boolean = false): Promise { + const self = this; + + return new Promise((resolve, reject) => { + // If the URI is connected, disconnect it and the editor + if (self.isConnected(owner.uri)) { + var connection = self.getConnectionProfile(owner.uri); + owner.onDisconnect(); + resolve(self.doDisconnect(owner.uri, connection)); + + // If the URI is connecting, prompt the user to cancel connecting + } else if (self.isConnecting(owner.uri)) { + if (!force) { + self.shouldCancelConnect(owner.uri).then((result) => { + // If the user wants to cancel, then disconnect + if (result) { + owner.onDisconnect(); + resolve(self.cancelEditorConnection(owner)); + } + // If the user does not want to cancel, then ignore + resolve(false); + }); + } else { + owner.onDisconnect(); + resolve(self.cancelEditorConnection(owner)); + } + } + // If the URI is disconnected, ensure the UI state is consistent and resolve true + owner.onDisconnect(); + resolve(true); + }); + } + + /** + * Functions to handle the connecting life cycle + */ + + // Connect an open URI to a connection profile + private createNewConnection(uri: string, connection: IConnectionProfile): Promise { + const self = this; + + return new Promise((resolve, reject) => { + this._capabilitiesService.onCapabilitiesReady().then(() => { + let connectionInfo = this._connectionStatusManager.addConnection(connection, uri); + // Setup the handler for the connection complete notification to call + connectionInfo.connectHandler = ((connectResult, errorMessage, errorCode) => { + let connectionMngInfo = this._connectionStatusManager.findConnection(uri); + if (connectionMngInfo && connectionMngInfo.deleted) { + this._connectionStatusManager.deleteConnection(uri); + resolve({ connected: connectResult, errorMessage: undefined, errorCode: undefined, errorHandled: true }); + } else { + if (errorMessage) { + // Connection to the server failed + this._connectionStatusManager.deleteConnection(uri); + resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode }); + } else { + resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode }); + } + } + }); + + // send connection request + self.sendConnectRequest(connection, uri); + }); + }); + } + + // Ask user if they are sure they want to cancel connection request + private shouldCancelConnect(fileUri: string): Thenable { + const self = this; + + // Double check if the user actually wants to cancel their connection request + return new Promise((resolve, reject) => { + // Setup our cancellation choices + let choices: { key, value }[] = [ + { key: nls.localize('yes', 'Yes'), value: true }, + { key: nls.localize('no', 'No'), value: false } + ]; + + self._quickOpenService.pick(choices.map(x => x.key), { placeHolder: nls.localize('cancelConnectionConfirmation', 'Are you sure you want to cancel this connection?'), ignoreFocusLost: true }).then((choice) => { + let confirm = choices.find(x => x.key === choice); + resolve(confirm && confirm.value); + }); + }); + } + + private doDisconnect(fileUri: string, connection?: IConnectionProfile): Promise { + const self = this; + + return new Promise((resolve, reject) => { + let disconnectParams = new ConnectionContracts.DisconnectParams(); + disconnectParams.ownerUri = fileUri; + + // Send a disconnection request for the input URI + self.sendDisconnectRequest(fileUri).then((result) => { + // If the request was sent + if (result) { + this._connectionStatusManager.deleteConnection(fileUri); + if (connection) { + this._notifyDisconnected(connection, fileUri); + } + + if (this._connectionStatusManager.isDefaultTypeUri(fileUri)) { + this._connectionGlobalStatus.setStatusToDisconnected(fileUri); + } + + // TODO: send telemetry events + // Telemetry.sendTelemetryEvent('DatabaseDisconnected'); + } + + resolve(result); + }); + }); + } + + public disconnect(connection: IConnectionProfile): Promise; + public disconnect(ownerUri: string): Promise; + public disconnect(input: any): Promise { + return new Promise((resolve, reject) => { + let uri: string; + let profile: IConnectionProfile; + if (typeof input === 'object') { + uri = Utils.generateUri(input); + profile = input; + } else if (typeof input === 'string') { + profile = this.getConnectionProfile(input); + uri = input; + } + this.doDisconnect(uri, profile).then(result => { + if (result) { + this.addTelemetryForConnectionDisconnected(input); + this._connectionStore.removeActiveConnection(input); + this._connectionStatusManager.removeConnection(uri); + resolve(); + } else { + reject(result); + } + }); + }); + } + + public cancelConnection(connection: IConnectionProfile): Thenable { + let fileUri = Utils.generateUri(connection); + return this.cancelConnectionForUri(fileUri); + } + + public cancelConnectionForUri(fileUri: string): Thenable { + const self = this; + return new Promise((resolve, reject) => { + // Create a new set of cancel connection params with our file URI + let cancelParams: ConnectionContracts.CancelConnectParams = new ConnectionContracts.CancelConnectParams(); + cancelParams.ownerUri = fileUri; + + this._connectionStatusManager.deleteConnection(fileUri); + // Send connection cancellation request + resolve(self.sendCancelRequest(fileUri)); + }); + } + + public cancelEditorConnection(owner: IConnectableInput): Thenable { + const self = this; + let fileUri: string = owner.uri; + return new Promise((resolve, reject) => { + if (self.isConnecting(fileUri)) { + this.cancelConnectionForUri(fileUri).then(result => { + resolve(result); + }); + } else { + resolve(self.disconnectEditor(owner)); + } + }); + } + // Is a certain file URI connected? + public isConnected(fileUri: string, connectionProfile?: ConnectionProfile): boolean { + if (connectionProfile) { + fileUri = Utils.generateUri(connectionProfile); + } + return this._connectionStatusManager.isConnected(fileUri); + } + + /** + * Finds existing connection for given profile and purpose is any exists. + * The purpose is connection by default + */ + public findExistingConnection(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): ConnectionProfile { + let connectionUri = Utils.generateUri(connection, purpose); + let existingConnection = this._connectionStatusManager.findConnection(connectionUri); + if (existingConnection && this._connectionStatusManager.isConnected(connectionUri)) { + return existingConnection.connectionProfile; + } else { + return undefined; + } + } + + public isProfileConnected(connectionProfile: IConnectionProfile): boolean { + let connectionManagement = this._connectionStatusManager.findConnectionProfile(connectionProfile); + return connectionManagement && !connectionManagement.connecting; + } + + public isProfileConnecting(connectionProfile: IConnectionProfile): boolean { + let connectionManagement = this._connectionStatusManager.findConnectionProfile(connectionProfile); + return connectionManagement && connectionManagement.connecting; + } + + private isConnecting(fileUri: string): boolean { + return this._connectionStatusManager.isConnecting(fileUri); + } + + public getConnectionProfile(fileUri: string): IConnectionProfile { + return this._connectionStatusManager.isConnected(fileUri) ? this._connectionStatusManager.getConnectionProfile(fileUri) : undefined; + } + + public getConnectionInfo(fileUri: string): ConnectionManagementInfo { + return this._connectionStatusManager.isConnected(fileUri) ? this._connectionStatusManager.findConnection(fileUri) : undefined; + } + + public listDatabases(connectionUri: string): Thenable { + const self = this; + if (self.isConnected(connectionUri)) { + return self.sendListDatabasesRequest(connectionUri); + } + return Promise.resolve(undefined); + } + + public changeDatabase(connectionUri: string, databaseName: string): Thenable { + if (this.isConnected(connectionUri)) { + let providerId: string = this.getProviderIdFromUri(connectionUri); + if (!providerId) { + return Promise.resolve(false); + } + + let provider = this._providers[providerId]; + return provider.changeDatabase(connectionUri, databaseName).then(result => { + if (result) { + this.getConnectionProfile(connectionUri).databaseName = databaseName; + } + return result; + }); + } + return Promise.resolve(false); + } + + public editGroup(group: ConnectionProfileGroup): Promise { + return new Promise((resolve, reject) => { + this._connectionStore.editGroup(group).then(groupId => { + this._onAddConnectionProfile.fire(); + resolve(null); + }).catch(err => { + reject(err); + }); + }); + } + + /** + * Deletes a connection from registered servers. + * Disconnects a connection before removing from settings. + */ + public deleteConnection(connection: ConnectionProfile): Promise { + + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.DeleteConnection, {}, connection); + // Disconnect if connected + let uri = Utils.generateUri(connection); + if (this.isConnected(uri) || this.isConnecting(uri)) { + this.doDisconnect(uri, connection).then((result) => { + if (result) { + // Remove profile from configuration + this._connectionStore.deleteConnectionFromConfiguration(connection).then(() => { + this._onDeleteConnectionProfile.fire(); + Promise.resolve(true); + }).catch(err => { + // Reject promise if error occurred writing to settings + Promise.reject(err); + }); + + } else { + // If connection fails to disconnect, resolve promise with false + Promise.resolve(false); + } + }); + } else { + // Remove disconnected profile from settings + this._connectionStore.deleteConnectionFromConfiguration(connection).then(() => { + this._onDeleteConnectionProfile.fire(); + Promise.resolve(true); + }).catch(err => { + // Reject promise if error ocurred writing to settings + Promise.reject(err); + }); + } + return Promise.resolve(undefined); + } + + /** + * Deletes a group with all its children groups and connections from registered servers. + * Disconnects a connection before removing from config. If disconnect fails, settings is not modified. + */ + public deleteConnectionGroup(group: ConnectionProfileGroup): Promise { + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.DeleteServerGroup); + // Get all connections for this group + let connections = ConnectionProfileGroup.getConnectionsInGroup(group); + + // Disconnect all these connections + let disconnected = []; + connections.forEach((con) => { + let uri = Utils.generateUri(con); + if (this.isConnected(uri)) { + disconnected.push(this.doDisconnect(uri, con)); + } + }); + + // When all the disconnect promises resolve, remove profiles from config + Promise.all(disconnected).then(() => { + // Remove profiles and groups from config + this._connectionStore.deleteGroupFromConfiguration(group).then(() => { + this._onDeleteConnectionProfile.fire(); + Promise.resolve(true); + }).catch(err => { + // If saving to config fails, reject promise with false + return Promise.reject(false); + }); + }).catch(err => { + // If disconnecting all connected profiles fails, resolve promise with false + return Promise.resolve(false); + }); + return Promise.resolve(undefined); + } + + private _notifyDisconnected(connectionProfile: IConnectionProfile, connectionUri: string): void { + this._onDisconnect.fire({ + connectionUri: connectionUri, + connectionProfile: connectionProfile + }); + } + + /** + * Rebuild the IntelliSense cache for the connection with the given URI + */ + public rebuildIntelliSenseCache(connectionUri: string): Thenable { + if (this.isConnected(connectionUri)) { + let providerId: string = this.getProviderIdFromUri(connectionUri); + if (!providerId) { + return Promise.reject('No provider corresponding to the given URI'); + } + + let provider = this._providers[providerId]; + return provider.rebuildIntelliSenseCache(connectionUri); + } + return Promise.reject('The given URI is not currently connected'); + } +} diff --git a/src/sql/parts/connection/common/connectionProfile.ts b/src/sql/parts/connection/common/connectionProfile.ts new file mode 100644 index 0000000000..b74edf3d0b --- /dev/null +++ b/src/sql/parts/connection/common/connectionProfile.ts @@ -0,0 +1,227 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ConnectionProfileGroup } from './connectionProfileGroup'; +import * as data from 'data'; +import { ProviderConnectionInfo } from 'sql/parts/connection/common/providerConnectionInfo'; +import * as interfaces from 'sql/parts/connection/common/interfaces'; +import { equalsIgnoreCase } from 'vs/base/common/strings'; +import { generateUuid } from 'vs/base/common/uuid'; + +// Concrete implementation of the IConnectionProfile interface + +/** + * A concrete implementation of an IConnectionProfile with support for profile creation and validation + */ +export class ConnectionProfile extends ProviderConnectionInfo implements interfaces.IConnectionProfile { + + public parent: ConnectionProfileGroup = null; + private _id: string; + public savePassword: boolean; + private _groupName: string; + public groupId: string; + public saveProfile: boolean; + + public isDisconnecting: boolean = false; + public constructor(serverCapabilities?: data.DataProtocolServerCapabilities, model?: interfaces.IConnectionProfile) { + super(serverCapabilities, model); + if (model) { + this.groupId = model.groupId; + this.groupFullName = model.groupFullName; + this.savePassword = model.savePassword; + this.saveProfile = model.saveProfile; + this._id = model.id; + } else { + //Default for a new connection + this.savePassword = false; + this.saveProfile = true; + this._groupName = ConnectionProfile.RootGroupName; + this._id = generateUuid(); + } + + this.options['groupId'] = this.groupId; + this.options['databaseDisplayName'] = this.databaseName; + } + + public matches(other: interfaces.IConnectionProfile): boolean { + return other + && this.providerName === other.providerName + && equalsIgnoreCase(this.serverName, other.serverName) + && equalsIgnoreCase(this.databaseName, other.databaseName) + && equalsIgnoreCase(this.userName, other.userName) + && equalsIgnoreCase(this.options['databaseDisplayName'], other.options['databaseDisplayName']) + && this.authenticationType === other.authenticationType + && this.groupId === other.groupId; + } + + public generateNewId() { + this._id = generateUuid(); + } + + public getParent(): ConnectionProfileGroup { + return this.parent; + } + + public get id(): string { + if (!this._id) { + this._id = generateUuid(); + } + return this._id; + } + + public set id(value: string) { + this._id = value; + } + + public get groupFullName(): string { + return this._groupName; + } + + public set groupFullName(value: string) { + this._groupName = value; + } + + public get isAddedToRootGroup(): boolean { + return (this._groupName === ConnectionProfile.RootGroupName); + } + + public clone(): ConnectionProfile { + let instance = new ConnectionProfile(this._serverCapabilities, this); + return instance; + } + + public cloneWithNewId(): ConnectionProfile { + let instance = this.clone(); + instance.generateNewId(); + return instance; + } + + public cloneWithDatabase(databaseName: string): ConnectionProfile { + let instance = this.cloneWithNewId(); + instance.databaseName = databaseName; + return instance; + } + + public static readonly RootGroupName: string = '/'; + + public withoutPassword(): ConnectionProfile { + let clone = this.clone(); + clone.password = ''; + return clone; + } + + /** + * Returns a key derived the connections options (providerName, authenticationType, serverName, databaseName, userName, groupid) + * This key uniquely identifies a connection in a group + * Example: "providerName:MSSQL|authenticationType:|databaseName:database|serverName:server3|userName:user|group:testid" + */ + public getOptionsKey(): string { + let id = super.getOptionsKey(); + let databaseDisplayName: string = this.options['databaseDisplayName']; + if (databaseDisplayName) { + id += ProviderConnectionInfo.idSeparator + 'databaseDisplayName' + ProviderConnectionInfo.nameValueSeparator + databaseDisplayName; + } + + return id + ProviderConnectionInfo.idSeparator + 'group' + ProviderConnectionInfo.nameValueSeparator + this.groupId; + } + + /** + * Returns the unique id for the connection that doesn't include the group name + */ + public getConnectionInfoId(): string { + return super.getOptionsKey(); + } + + public onProviderRegistered(serverCapabilities: data.DataProtocolServerCapabilities): void { + if (serverCapabilities.providerName === this.providerName) { + this.setServerCapabilities(serverCapabilities); + } + } + + public toIConnectionProfile(): interfaces.IConnectionProfile { + let result: interfaces.IConnectionProfile = { + serverName: this.serverName, + databaseName: this.databaseName, + authenticationType: this.authenticationType, + getOptionsKey: undefined, + matches: undefined, + groupId: this.groupId, + groupFullName: this.groupFullName, + password: this.password, + providerName: this.providerName, + savePassword: this.savePassword, + userName: this.userName, + options: this.options, + saveProfile: this.saveProfile, + id: this.id + }; + + return result; + } + + public toConnectionInfo(): data.ConnectionInfo { + return { + options: this.options + }; + } + + public static createFromStoredProfile(profile: interfaces.IConnectionProfileStore, serverCapabilities: data.DataProtocolServerCapabilities): ConnectionProfile { + let connectionInfo = new ConnectionProfile(serverCapabilities, undefined); + connectionInfo.options = profile.options; + + // append group ID and original display name to build unique OE session ID + connectionInfo.options = profile.options; + connectionInfo.options['groupId'] = connectionInfo.groupId; + connectionInfo.options['databaseDisplayName'] = connectionInfo.databaseName; + + connectionInfo.groupId = profile.groupId; + connectionInfo.providerName = profile.providerName; + connectionInfo.saveProfile = true; + connectionInfo.savePassword = profile.savePassword; + connectionInfo.id = profile.id || generateUuid(); + return connectionInfo; + } + + public static convertToConnectionProfile(serverCapabilities: data.DataProtocolServerCapabilities, conn: interfaces.IConnectionProfile): ConnectionProfile { + if (conn) { + let connectionProfile: ConnectionProfile = undefined; + let connectionProfileInstance = conn as ConnectionProfile; + if (connectionProfileInstance && conn instanceof ConnectionProfile) { + connectionProfile = connectionProfileInstance; + connectionProfile.setServerCapabilities(serverCapabilities); + } else { + connectionProfile = new ConnectionProfile(serverCapabilities, conn); + } + + return connectionProfile; + } else { + return undefined; + } + } + + public static convertToProfileStore( + serverCapabilities: data.DataProtocolServerCapabilities, + connectionProfile: interfaces.IConnectionProfile): interfaces.IConnectionProfileStore { + if (connectionProfile) { + let connectionInfo = ConnectionProfile.convertToConnectionProfile(serverCapabilities, connectionProfile); + let profile: interfaces.IConnectionProfileStore = { + options: {}, + groupId: connectionProfile.groupId, + providerName: connectionInfo.providerName, + savePassword: connectionInfo.savePassword, + id: connectionInfo.id + }; + + profile.options = connectionInfo.options; + + return profile; + } else { + return undefined; + } + } + +} diff --git a/src/sql/parts/connection/common/connectionProfileGroup.ts b/src/sql/parts/connection/common/connectionProfileGroup.ts new file mode 100644 index 0000000000..f4041e0f8a --- /dev/null +++ b/src/sql/parts/connection/common/connectionProfileGroup.ts @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ConnectionProfile } from './connectionProfile'; + +export interface IConnectionProfileGroup { + id: string; + parentId: string; + name: string; + color: string; + description: string; +} + +export class ConnectionProfileGroup implements IConnectionProfileGroup { + + public children: ConnectionProfileGroup[]; + public connections: ConnectionProfile[]; + public parentId: string; + private _isRenamed: boolean; + public constructor( + public name: string, + public parent: ConnectionProfileGroup, + public id: string, + public color: string, + public description: string + ) { + this.parentId = parent ? parent.id : undefined; + if (this.name === ConnectionProfileGroup.RootGroupName) { + this.name = ''; + } + } + + public static GroupNameSeparator: string = '/'; + public static RootGroupName: string = 'ROOT'; + + public toObject(): IConnectionProfileGroup { + let subgroups = undefined; + if (this.children) { + subgroups = []; + this.children.forEach((group) => { + subgroups.push(group.toObject()); + }); + } + + return Object.assign({}, { name: this.name, id: this.id, parentId: this.parentId, children: subgroups, color: this.color, description: this.description }); + } + + public get groupName(): string { + return this.name; + } + + public get fullName(): string { + let fullName: string = (this.id === 'root') ? undefined : this.name; + if (this.parent) { + let parentFullName = this.parent.fullName; + if (parentFullName) { + fullName = parentFullName + ConnectionProfileGroup.GroupNameSeparator + this.name; + } + } + return fullName; + } + + public get isRenamed(): boolean { + return this._isRenamed; + } + + public set isRenamed(val: boolean) { + this._isRenamed = val; + } + + public hasChildren(): boolean { + if ((this.children && this.children.length > 0) || (this.connections && this.connections.length > 0)) { + return true; + } + return false; + } + + public getChildren(): any { + let allChildren = []; + + if (this.connections) { + this.connections.forEach((conn) => { + allChildren.push(conn); + }); + } + + if (this.children) { + this.children.forEach((group) => { + allChildren.push(group); + }); + } + return allChildren; + } + + public equals(other: any): boolean { + if (!(other instanceof ConnectionProfileGroup)) { + return false; + } + return other.id === this.id; + } + + public addConnections(connections: ConnectionProfile[]): void { + if (!this.connections) { + this.connections = []; + } + connections.forEach((conn) => { + this.connections = this.connections.filter((curConn) => { return curConn.id !== conn.id; }); + conn.parent = this; + this.connections.push(conn); + }); + + } + + public addGroups(groups: ConnectionProfileGroup[]): void { + if (!this.children) { + this.children = []; + } + groups.forEach((group) => { + this.children = this.children.filter((grp) => { return group.id !== grp.id; }); + group.parent = this; + this.children.push(group); + }); + } + + public getParent(): ConnectionProfileGroup { + return this.parent; + } + + public static getGroupFullNameParts(groupFullName: string): string[] { + groupFullName = groupFullName ? groupFullName : ''; + let groupNames: string[] = groupFullName.split(ConnectionProfileGroup.GroupNameSeparator); + groupNames = groupNames.filter(g => !!g); + if (groupNames.length === 0) { + groupNames.push('ROOT'); + } else if (groupNames[0].toUpperCase() !== 'ROOT') { + groupNames.unshift('ROOT'); + } + groupNames[0] = 'ROOT'; + return groupNames; + } + + public static isRoot(name: string): boolean { + return (!name || name.toUpperCase() === ConnectionProfileGroup.RootGroupName || + name === ConnectionProfileGroup.GroupNameSeparator); + } + + public static sameGroupName(name1: string, name2: string): boolean { + let sameGroupName: boolean = + (!name1 && !name2) || + name1.toUpperCase() === name2.toUpperCase() || + (ConnectionProfileGroup.isRoot(name1) && ConnectionProfileGroup.isRoot(name2)); + + return sameGroupName; + } + + public static getConnectionsInGroup(group: ConnectionProfileGroup): ConnectionProfile[] { + let connections = []; + if (group.connections) { + group.connections.forEach((con) => connections.push(con)); + } + if (group.children) { + group.children.forEach((subgroup) => { + connections = connections.concat(this.getConnectionsInGroup(subgroup)); + }); + } + return connections; + } + + public static getSubgroups(group: ConnectionProfileGroup): ConnectionProfileGroup[] { + let subgroups = []; + if (group && group.children) { + group.children.forEach((grp) => subgroups.push(grp)); + group.children.forEach((subgroup) => { + subgroups = subgroups.concat(this.getSubgroups(subgroup)); + }); + } + return subgroups; + } +} \ No newline at end of file diff --git a/src/sql/parts/connection/common/connectionStatus.ts b/src/sql/parts/connection/common/connectionStatus.ts new file mode 100644 index 0000000000..b12c417ab8 --- /dev/null +++ b/src/sql/parts/connection/common/connectionStatus.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { $, append, show, hide } from 'vs/base/browser/dom'; +import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import URI from 'vs/base/common/uri'; +import { IEditorInput } from 'vs/platform/editor/common/editor'; +import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { IEditorCloseEvent } from 'vs/workbench/common/editor'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IConnectionManagementService, IConnectionParams } from 'sql/parts/connection/common/connectionManagement'; +import { ConnectionStatusManager } from 'sql/parts/connection/common/connectionStatusManager'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { QueryInput } from 'sql/parts/query/common/queryInput'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; + +enum ConnectionActivityStatus { + Connected, + Disconnected +} + +// Contains connection status for each editor +class ConnectionStatusEditor { + public connectionActivityStatus: ConnectionActivityStatus; + public connectionProfile: IConnectionProfile; + + constructor() { + this.connectionActivityStatus = ConnectionActivityStatus.Disconnected; + } +} + +// Connection status bar for editor +export class ConnectionStatusbarItem implements IStatusbarItem { + + private _element: HTMLElement; + private _connectionElement: HTMLElement; + private _connectionStatusEditors: { [connectionUri: string]: ConnectionStatusEditor }; + private _toDispose: IDisposable[]; + private _connectionStatusManager: ConnectionStatusManager; + + constructor( + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IEditorGroupService private _editorGroupService: IEditorGroupService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService, + ) { + this._connectionStatusEditors = {}; + this._connectionStatusManager = new ConnectionStatusManager(this._capabilitiesService); + } + + public render(container: HTMLElement): IDisposable { + this._element = append(container, $('.connection-statusbar-item')); + this._connectionElement = append(this._element, $('div.connection-statusbar-conninfo')); + hide(this._connectionElement); + + this._toDispose = []; + this._toDispose.push( + this._connectionManagementService.onConnect((connectionUri: IConnectionParams) => this._onConnect(connectionUri)), + this._connectionManagementService.onConnectionChanged((connectionUri: IConnectionParams) => this._onConnect(connectionUri)), + this._connectionManagementService.onDisconnect((connectionUri: IConnectionParams) => this._onDisconnect(connectionUri)), + this._editorGroupService.onEditorsChanged(() => this._onEditorsChanged()), + this._editorGroupService.getStacksModel().onEditorClosed(event => this._onEditorClosed(event)) + ); + + return combinedDisposable(this._toDispose); + } + + private _onEditorClosed(event: IEditorCloseEvent): void { + let uri = WorkbenchUtils.getEditorUri(event.editor); + if (uri && uri in this._connectionStatusEditors) { + this._updateStatus(uri, ConnectionActivityStatus.Disconnected, undefined); + delete this._connectionStatusEditors[uri]; + } + } + + private _onEditorsChanged(): void { + let activeEditor = this._editorService.getActiveEditor(); + if (activeEditor) { + let uri = WorkbenchUtils.getEditorUri(activeEditor.input); + + // Show active editor's query status + if (uri && uri in this._connectionStatusEditors) { + this._showStatus(uri); + } else { + hide(this._connectionElement); + } + } else { + hide(this._connectionElement); + } + } + + private _onConnect(connectionParams: IConnectionParams): void { + if (!this._connectionStatusManager.isDefaultTypeUri(connectionParams.connectionUri)) { + this._updateStatus(connectionParams.connectionUri, ConnectionActivityStatus.Connected, connectionParams.connectionProfile); + } + } + + private _onDisconnect(connectionUri: IConnectionParams): void { + if (!this._connectionStatusManager.isDefaultTypeUri(connectionUri.connectionUri)) { + this._updateStatus(connectionUri.connectionUri, ConnectionActivityStatus.Disconnected, undefined); + } + } + + // Update connection status for the editor + private _updateStatus(uri: string, newStatus: ConnectionActivityStatus, connectionProfile: IConnectionProfile) { + if (uri) { + if (!(uri in this._connectionStatusEditors)) { + this._connectionStatusEditors[uri] = new ConnectionStatusEditor(); + } + this._connectionStatusEditors[uri].connectionActivityStatus = newStatus; + this._connectionStatusEditors[uri].connectionProfile = connectionProfile; + this._showStatus(uri); + } + } + + // Show/hide query status for active editor + private _showStatus(uri: string): void { + let activeEditor = this._editorService.getActiveEditor(); + if (activeEditor) { + let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); + if (uri === currentUri) { + switch (this._connectionStatusEditors[uri].connectionActivityStatus) { + case ConnectionActivityStatus.Connected: + this._setConnectionText(this._connectionStatusEditors[uri].connectionProfile); + show(this._connectionElement); + break; + case ConnectionActivityStatus.Disconnected: + hide(this._connectionElement); + break; + } + } + } + } + + // Set connection info to connection status bar + private _setConnectionText(connectionProfile: IConnectionProfile): void { + let text: string = connectionProfile.serverName; + if (text) { + if (connectionProfile.databaseName && connectionProfile.databaseName !== '') { + text = text + ' : ' + connectionProfile.databaseName; + } else { + text = text + ' : ' + ''; + } + } + + let tooltip: string = + 'Server name: ' + connectionProfile.serverName + '\r\n' + + 'Database name: ' + (connectionProfile.databaseName ? connectionProfile.databaseName : '') + '\r\n'; + + if (connectionProfile.userName && connectionProfile.userName !== '') { + tooltip = tooltip + 'Login name: ' + connectionProfile.userName + '\r\n'; + } + + this._connectionElement.textContent = text; + this._connectionElement.title = tooltip; + } +} diff --git a/src/sql/parts/connection/common/connectionStatusManager.ts b/src/sql/parts/connection/common/connectionStatusManager.ts new file mode 100644 index 0000000000..a5eb57d906 --- /dev/null +++ b/src/sql/parts/connection/common/connectionStatusManager.ts @@ -0,0 +1,212 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ConnectionManagementInfo } from './connectionManagementInfo'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IConnectionProfile } from './interfaces'; +import * as Utils from './utils'; +import * as data from 'data'; +import { StopWatch } from 'vs/base/common/stopwatch'; + +export class ConnectionStatusManager { + + private _connections: { [id: string]: ConnectionManagementInfo }; + private _providerCapabilitiesMap: { [providerName: string]: data.DataProtocolServerCapabilities }; + + constructor( @ICapabilitiesService private _capabilitiesService: ICapabilitiesService) { + this._connections = {}; + this._providerCapabilitiesMap = {}; + } + + public getCapabilities(providerName: string): data.DataProtocolServerCapabilities { + let result: data.DataProtocolServerCapabilities; + + if (providerName in this._providerCapabilitiesMap) { + result = this._providerCapabilitiesMap[providerName]; + } else { + let capabilities = this._capabilitiesService.getCapabilities(); + if (capabilities) { + let providerCapabilities = capabilities.find(c => c.providerName === providerName); + if (providerCapabilities) { + this._providerCapabilitiesMap[providerName] = providerCapabilities; + result = providerCapabilities; + } + } + } + + return result; + } + + public findConnection(id: string): ConnectionManagementInfo { + if (id in this._connections) { + return this._connections[id]; + } else { + return undefined; + } + } + + public findConnectionProfile(connectionProfile: IConnectionProfile): ConnectionManagementInfo { + let id = Utils.generateUri(connectionProfile); + return this.findConnection(id); + } + + public hasConnection(id: string): Boolean { + return !!this.findConnection(id); + } + + public deleteConnection(id: string): void { + let info = this.findConnection(id); + if (info) { + for (let key in this._connections) { + if (this._connections[key].connectionId === info.connectionId) { + if (this._connections[key].connecting) { + this._connections[key].deleted = true; + } else { + delete this._connections[key]; + } + } + } + } + } + + public getConnectionProfile(id: string): ConnectionProfile { + let connectionInfoForId = this.findConnection(id); + return connectionInfoForId ? connectionInfoForId.connectionProfile : undefined; + } + + public addConnection(connection: IConnectionProfile, id: string): ConnectionManagementInfo { + // Always create a copy and save that in the list + let connectionProfile = new ConnectionProfile(this.getCapabilities(connection.providerName), connection); + const self = this; + let connectionInfo: ConnectionManagementInfo = new ConnectionManagementInfo(); + connectionInfo.providerId = connection.providerName; + connectionInfo.extensionTimer = StopWatch.create(); + connectionInfo.intelliSenseTimer = StopWatch.create(); + connectionInfo.connectionProfile = connectionProfile; + connectionInfo.connecting = true; + self._connections[id] = connectionInfo; + connectionInfo.serviceTimer = StopWatch.create(); + connectionInfo.ownerUri = id; + + return connectionInfo; + } + + /** + * + * @param uri Remove connection from list of active connections + */ + public removeConnection(uri: string) { + delete this._connections[uri]; + } + + /** + * Call after a connection is saved to settings. It's only for default url connections + * which their id is generated from connection options. The group id is used in the generated id. + * when the connection is stored, the group id get assigned to the profile and it can change the id + * So for those kind of connections, we need to add the new id and the connection + */ + public updateConnectionProfile(connection: IConnectionProfile, id: string): string { + let newId: string = id; + let connectionInfo: ConnectionManagementInfo = this._connections[id]; + if (connectionInfo && connection) { + if (this.isDefaultTypeUri(id)) { + connectionInfo.connectionProfile.groupId = connection.groupId; + newId = Utils.generateUri(connection); + if (newId !== id) { + this.deleteConnection(id); + this._connections[newId] = connectionInfo; + } + } + connectionInfo.connectionProfile.id = connection.id; + } + return newId; + } + + public onConnectionComplete(summary: data.ConnectionInfoSummary): ConnectionManagementInfo { + let connection = this._connections[summary.ownerUri]; + connection.serviceTimer.stop(); + connection.connecting = false; + connection.connectionId = summary.connectionId; + connection.serverInfo = summary.serverInfo; + return connection; + } + + /** + * Updates database name after connection is complete + * @param summary connection summary + */ + public updateDatabaseName(summary: data.ConnectionInfoSummary): void { + let connection = this._connections[summary.ownerUri]; + + //Check if the existing connection database name is different the one in the summary + if (connection.connectionProfile.databaseName !== summary.connectionSummary.databaseName) { + //Add the ownerUri with database name to the map if not already exists + connection.connectionProfile.databaseName = summary.connectionSummary.databaseName; + let prefix = Utils.getUriPrefix(summary.ownerUri); + let ownerUriWithDbName = Utils.generateUriWithPrefix(connection.connectionProfile, prefix); + if (!(ownerUriWithDbName in this._connections)) { + this._connections[ownerUriWithDbName] = connection; + } + } + } + + /** + * Tries to find an existing connection that's mapped with given the ownerUri + * The purpose for this method is to find the connection given the ownerUri and find the original uri assigned to it. most of the times should be the same. + * Only if the db name in the original uri is different than when connection is complete, we need to use the original uri + * Returns the generated ownerUri for the connection profile if not existing connection found + * @param ownerUri connection owner uri to find an existing connection + * @param purpose purpose for the connection + */ + public getOriginalOwnerUri(ownerUri: string): string { + let ownerUriToReturn: string = ownerUri; + + let connectionStatusInfo = this.findConnection(ownerUriToReturn); + if (connectionStatusInfo && connectionStatusInfo.ownerUri) { + //The ownerUri in the connection status is the one service knows about so use that + //To call the service for any operation + ownerUriToReturn = connectionStatusInfo.ownerUri; + } + return ownerUriToReturn; + } + + public onConnectionChanged(changedConnInfo: data.ChangedConnectionInfo): IConnectionProfile { + let connection = this._connections[changedConnInfo.connectionUri]; + if (connection && connection.connectionProfile) { + connection.connectionProfile.serverName = changedConnInfo.connection.serverName; + connection.connectionProfile.databaseName = changedConnInfo.connection.databaseName; + connection.connectionProfile.userName = changedConnInfo.connection.userName; + return connection.connectionProfile; + } + return undefined; + } + + public isConnected(id: string): boolean { + return (id in this._connections && this._connections[id].connectionId && !!this._connections[id].connectionId); + } + + public isConnecting(id: string): boolean { + return (id in this._connections && this._connections[id].connecting); + } + + public isDefaultTypeUri(uri: string): boolean { + return uri && uri.startsWith(Utils.uriPrefixes.default); + } + + public getProviderIdFromUri(ownerUri: string): string { + let providerId: string = ''; + let connection = this.findConnection(ownerUri); + if (connection) { + providerId = connection.connectionProfile.providerName; + } + if (!providerId && this.isDefaultTypeUri(ownerUri)) { + let optionsKey = ownerUri.replace(Utils.uriPrefixes.default, ''); + providerId = ConnectionProfile.getProviderFromOptionsKey(optionsKey); + } + return providerId; + } +} \ No newline at end of file diff --git a/src/sql/parts/connection/common/connectionStore.ts b/src/sql/parts/connection/common/connectionStore.ts new file mode 100644 index 0000000000..59cbed7e9d --- /dev/null +++ b/src/sql/parts/connection/common/connectionStore.ts @@ -0,0 +1,616 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as Constants from './constants'; +import * as ConnInfo from './connectionInfo'; +import { ConnectionProfile } from '../common/connectionProfile'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ICredentialsService } from 'sql/services/credentials/credentialsService'; +import { IConnectionConfig } from './iconnectionConfig'; +import { ConnectionConfig } from './connectionConfig'; +import { Memento, Scope as MementoScope } from 'vs/workbench/common/memento'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ConnectionProfileGroup, IConnectionProfileGroup } from './connectionProfileGroup'; +import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { equalsIgnoreCase } from 'vs/base/common/strings'; +import * as data from 'data'; + +const MAX_CONNECTIONS_DEFAULT = 25; + +/** + * Manages the connections list including saved profiles and the most recently used connections + * + * @export + * @class ConnectionStore + */ +export class ConnectionStore { + private _memento: any; + private _groupIdToFullNameMap: { [groupId: string]: string }; + private _groupFullNameToIdMap: { [groupId: string]: string }; + + constructor( + private _storageService: IStorageService, + private _context: Memento, + private _configurationEditService: IConfigurationEditingService, + private _workspaceConfigurationService: IWorkspaceConfigurationService, + private _credentialService: ICredentialsService, + private _capabilitiesService: ICapabilitiesService, + private _connectionConfig?: IConnectionConfig + ) { + + if (_context) { + this._memento = this._context.getMemento(this._storageService, MementoScope.GLOBAL); + } + this._groupIdToFullNameMap = {}; + this._groupFullNameToIdMap = {}; + + if (!this._connectionConfig) { + let cachedServerCapabilities = this.getCachedServerCapabilities(); + this._connectionConfig = new ConnectionConfig(this._configurationEditService, + this._workspaceConfigurationService, this._capabilitiesService, cachedServerCapabilities); + this._connectionConfig.setCachedMetadata(cachedServerCapabilities); + } + } + + public static get CRED_PREFIX(): string { return 'Microsoft.SqlTools'; } + public static get CRED_SEPARATOR(): string { return '|'; } + public static get CRED_ID_PREFIX(): string { return 'id:'; } + public static get CRED_ITEMTYPE_PREFIX(): string { return 'itemtype:'; } + public static get CRED_PROFILE_USER(): string { return 'Profile'; } + + public formatCredentialIdForCred(connectionProfile: IConnectionProfile): string { + if (!connectionProfile) { + throw new Error('Missing Connection which is required'); + } + let itemTypeString: string = ConnectionStore.CRED_PROFILE_USER; + return this.formatCredentialId(connectionProfile, itemTypeString); + } + + /** + * Creates a formatted credential usable for uniquely identifying a SQL Connection. + * This string can be decoded but is not optimized for this. + * @static + * @param {IConnectionProfile} connectionProfile connection profile - require + * @param {string} itemType type of the item (MRU or Profile) - optional + * @returns {string} formatted string with server, DB and username + */ + public formatCredentialId(connectionProfile: IConnectionProfile, itemType?: string): string { + let connectionProfileInstance: ConnectionProfile = ConnectionProfile.convertToConnectionProfile( + this._connectionConfig.getCapabilities(connectionProfile.providerName), connectionProfile); + if (!connectionProfileInstance.getConnectionInfoId()) { + throw new Error('Missing Id, which is required'); + } + let cred: string[] = [ConnectionStore.CRED_PREFIX]; + if (!itemType) { + itemType = ConnectionStore.CRED_PROFILE_USER; + } + + ConnectionStore.pushIfNonEmpty(itemType, ConnectionStore.CRED_ITEMTYPE_PREFIX, cred); + ConnectionStore.pushIfNonEmpty(connectionProfileInstance.getConnectionInfoId(), ConnectionStore.CRED_ID_PREFIX, cred); + return cred.join(ConnectionStore.CRED_SEPARATOR); + } + + private static pushIfNonEmpty(value: string, prefix: string, arr: string[]): void { + if (value) { + arr.push(prefix.concat(value)); + } + } + + /** + * Returns true if the password is required + * @param connection profile + */ + public isPasswordRequired(connection: IConnectionProfile): boolean { + if (connection) { + let connectionProfile = ConnectionProfile.convertToConnectionProfile(this._connectionConfig.getCapabilities(connection.providerName), connection); + return connectionProfile.isPasswordRequired(); + } else { + return false; + } + } + + /** + * Gets all connection profiles stored in the user settings + * Profiles from workspace will be included if getWorkspaceProfiles is passed as true + * Note: connections will not include password value + * + * @returns {IConnectionProfile[]} + */ + public getProfiles(getWorkspaceProfiles: boolean): IConnectionProfile[] { + return this.loadProfiles(getWorkspaceProfiles); + } + + public addSavedPassword(credentialsItem: IConnectionProfile): Promise<{ profile: IConnectionProfile, savedCred: boolean }> { + let self = this; + return new Promise<{ profile: IConnectionProfile, savedCred: boolean }>((resolve, reject) => { + if (credentialsItem.savePassword && this.isPasswordRequired(credentialsItem) + && !credentialsItem.password) { + + let credentialId = this.formatCredentialIdForCred(credentialsItem); + self._credentialService.readCredential(credentialId) + .then(savedCred => { + if (savedCred) { + credentialsItem.password = savedCred.password; + } + resolve({ profile: credentialsItem, savedCred: !!savedCred }); + }, + reason => { + reject(reason); + }); + } else { + // No need to look up the password + resolve({ profile: credentialsItem, savedCred: credentialsItem.savePassword }); + } + }); + } + + /** + * Saves a connection profile to the user settings. + * Password values are stored to a separate credential store if the "savePassword" option is true + * + * @param {IConnectionProfile} profile the profile to save + * @param {forceWritePlaintextPassword} whether the plaintext password should be written to the settings file + * @returns {Promise} a Promise that returns the original profile, for help in chaining calls + */ + public saveProfile(profile: IConnectionProfile, forceWritePlaintextPassword?: boolean): Promise { + const self = this; + return new Promise((resolve, reject) => { + // Add the profile to the saved list, taking care to clear out the password field if necessary + let savedProfile: IConnectionProfile; + if (forceWritePlaintextPassword) { + savedProfile = profile; + } else { + + savedProfile = this.getProfileWithoutPassword(profile); + } + self.saveProfileToConfig(savedProfile) + .then(savedConnectionProfile => { + profile.groupId = savedConnectionProfile.groupId; + profile.id = savedConnectionProfile.id; + // Only save if we successfully added the profile + return self.saveProfilePasswordIfNeeded(profile); + // And resolve / reject at the end of the process + }, err => { + reject(err); + }).then(resolved => { + // Add necessary default properties before returning + // this is needed to support immediate connections + ConnInfo.fixupConnectionCredentials(profile); + this.saveCachedServerCapabilities(); + resolve(profile); + }, err => { + reject(err); + }); + }); + } + + /** + * Saves a connection profile group to the user settings. + * + * @param {IConnectionProfileGroup} profile the profile group to save + * @returns {Promise} a Promise that returns the id of connection group + */ + public saveProfileGroup(profile: IConnectionProfileGroup): Promise { + const self = this; + return new Promise((resolve, reject) => { + self._connectionConfig.addGroup(profile).then(groupId => { + resolve(groupId); + }).catch(error => { + reject(error); + }); + }); + } + + private saveProfileToConfig(profile: IConnectionProfile): Promise { + const self = this; + return new Promise((resolve, reject) => { + if (profile.saveProfile) { + self._connectionConfig.addConnection(profile).then(savedProfile => { + resolve(savedProfile); + }).catch(error => { + reject(error); + }); + } else { + resolve(profile); + } + }); + } + + private getCachedServerCapabilities(): data.DataProtocolServerCapabilities[] { + if (this._memento) { + let metadata: data.DataProtocolServerCapabilities[] = this._memento[Constants.capabilitiesOptions]; + return metadata; + } else { + return undefined; + } + + } + + private saveCachedServerCapabilities(): void { + if (this._memento) { + let capabilities = this._capabilitiesService.getCapabilities(); + this._memento[Constants.capabilitiesOptions] = capabilities; + } + } + + /** + * Gets the list of recently used connections. These will not include the password - a separate call to + * {addSavedPassword} is needed to fill that before connecting + * + * @returns {data.ConnectionInfo} the array of connections, empty if none are found + */ + public getRecentlyUsedConnections(): ConnectionProfile[] { + let configValues: IConnectionProfile[] = this._memento[Constants.recentConnections]; + if (!configValues) { + configValues = []; + } + + configValues = configValues.filter(c => !!(c)); + return this.convertConfigValuesToConnectionProfiles(configValues); + } + + private convertConfigValuesToConnectionProfiles(configValues: IConnectionProfile[]): ConnectionProfile[] { + return configValues.map(c => { + if (c) { + let capabilities = this._connectionConfig.getCapabilities(c.providerName); + let connectionProfile = new ConnectionProfile(capabilities, c); + this._capabilitiesService.onProviderRegisteredEvent((serverCapabilities) => { + connectionProfile.onProviderRegistered(serverCapabilities); + }); + if (connectionProfile.saveProfile) { + if (!connectionProfile.groupFullName && connectionProfile.groupId) { + connectionProfile.groupFullName = this.getGroupFullName(connectionProfile.groupId); + } + if (!connectionProfile.groupId && connectionProfile.groupFullName) { + connectionProfile.groupId = this.getGroupId(connectionProfile.groupFullName); + } else if (!connectionProfile.groupId && !connectionProfile.groupFullName) { + connectionProfile.groupId = this.getGroupId(''); + } + } + return connectionProfile; + } else { + return undefined; + }; + }); + } + + /** + * Gets the list of active connections. These will not include the password - a separate call to + * {addSavedPassword} is needed to fill that before connecting + * + * @returns {data.ConnectionInfo} the array of connections, empty if none are found + */ + public getActiveConnections(): ConnectionProfile[] { + let configValues: IConnectionProfile[] = this._memento[Constants.activeConnections]; + if (!configValues) { + configValues = []; + } + + return this.convertConfigValuesToConnectionProfiles(configValues); + } + + public getProfileWithoutPassword(conn: IConnectionProfile): ConnectionProfile { + if (conn) { + let savedConn: ConnectionProfile = ConnectionProfile.convertToConnectionProfile(this._connectionConfig.getCapabilities(conn.providerName), conn); + savedConn = savedConn.withoutPassword(); + + return savedConn; + } else { + return undefined; + } + } + + /** + * Adds a connection to the active connections list. + * Connection is only added if there are no other connections with the same connection ID in the list. + * Password values are stored to a separate credential store if the "savePassword" option is true + * + * @param {IConnectionCredentials} conn the connection to add + * @returns {Promise} a Promise that returns when the connection was saved + */ + public addActiveConnection(conn: IConnectionProfile): Promise { + if(this.getActiveConnections().some(existingConn => existingConn.id === conn.id)) { + return Promise.resolve(undefined); + } else { + return this.addConnectionToMemento(conn, Constants.activeConnections, undefined, conn.savePassword).then(() => { + let maxConnections = this.getMaxRecentConnectionsCount(); + return this.addConnectionToMemento(conn, Constants.recentConnections, maxConnections); + }); + } + } + + public addConnectionToMemento(conn: IConnectionProfile, mementoKey: string, maxConnections?: number, savePassword?: boolean): Promise { + const self = this; + return new Promise((resolve, reject) => { + // Get all profiles + let configValues = self.getConnectionsFromMemento(mementoKey); + let configToSave: IConnectionProfile[] = this.addToConnectionList(conn, configValues, mementoKey === Constants.recentConnections); + + if (maxConnections) { + // Remove last element if needed + if (configToSave.length > maxConnections) { + configToSave = configToSave.slice(0, maxConnections); + } + } + self._memento[mementoKey] = configToSave; + if (savePassword) { + self.doSavePassword(conn).then(result => { + resolve(undefined); + }); + } else { + resolve(undefined); + } + }); + } + + private isSameConnectionProfileNoGroup(profile1: IConnectionProfile, profile2: IConnectionProfile): boolean { + // both are undefined + if (!profile1 && !profile2) { + return true; + } + // only one is undefined + if (!profile1 || !profile2) { + return false; + } + + // compare all the connection's "identity" properties + return equalsIgnoreCase(profile1.serverName, profile2.serverName) && + equalsIgnoreCase(profile1.databaseName, profile2.databaseName) && + equalsIgnoreCase(profile1.userName, profile2.userName) && + profile1.authenticationType === profile2.authenticationType && + profile1.providerName === profile2.providerName; + } + + public removeConnectionToMemento(conn: IConnectionProfile, mementoKey: string): Promise { + const self = this; + return new Promise((resolve, reject) => { + // Get all profiles + let configValues = self.getConnectionsFromMemento(mementoKey); + let configToSave = this.removeFromConnectionList(conn, configValues); + + self._memento[mementoKey] = configToSave; + resolve(undefined); + }); + } + + public getConnectionsFromMemento(mementoKey: string): ConnectionProfile[] { + let configValues: IConnectionProfile[] = this._memento[mementoKey]; + if (!configValues) { + configValues = []; + } + + return this.convertConfigValuesToConnectionProfiles(configValues); + } + + private addToConnectionList(conn: IConnectionProfile, list: ConnectionProfile[], isRecentConnections: boolean): IConnectionProfile[] { + let savedProfile: ConnectionProfile = this.getProfileWithoutPassword(conn); + + // Remove the connection from the list if it already exists + if (isRecentConnections) { + // recent connections should use a different comparison the server viewlet for managing connection list + list = list.filter(value => { + return !(this.isSameConnectionProfileNoGroup(value, savedProfile)); + }); + } else { + list = list.filter(value => { + let equal = value && value.getConnectionInfoId() === savedProfile.getConnectionInfoId(); + if (equal && savedProfile.saveProfile) { + equal = value.groupId === savedProfile.groupId || + ConnectionProfileGroup.sameGroupName(value.groupFullName, savedProfile.groupFullName); + } + return !equal; + }); + } + + list.unshift(savedProfile); + + let newList = list.map(c => { + let connectionProfile = c ? c.toIConnectionProfile() : undefined;; + return connectionProfile; + }); + return newList.filter(n => n !== undefined); + } + + private removeFromConnectionList(conn: IConnectionProfile, list: ConnectionProfile[]): IConnectionProfile[] { + let savedProfile: ConnectionProfile = this.getProfileWithoutPassword(conn); + + // Remove the connection from the list if it already exists + list = list.filter(value => { + let equal = value && value.getConnectionInfoId() === savedProfile.getConnectionInfoId(); + if (equal && savedProfile.saveProfile) { + equal = value.groupId === savedProfile.groupId || + ConnectionProfileGroup.sameGroupName(value.groupFullName, savedProfile.groupFullName); + } + return !equal; + }); + + let newList = list.map(c => { + let connectionProfile = c ? c.toIConnectionProfile() : undefined;; + return connectionProfile; + }); + return newList.filter(n => n !== undefined); + } + + /** + * Clear all recently used connections from the MRU list. + */ + public clearRecentlyUsed(): void { + this._memento[Constants.recentConnections] = []; + } + + public clearFromMemento(name: string): void { + this._memento[name] = []; + } + + + /** + * Clear all active connections from the MRU list. + */ + public clearActiveConnections(): void { + this._memento[Constants.activeConnections] = []; + } + + /** + * Remove a connection profile from the active connections list. + */ + public removeActiveConnection(conn: IConnectionProfile): Promise { + return this.removeConnectionToMemento(conn, Constants.activeConnections); + } + + private saveProfilePasswordIfNeeded(profile: IConnectionProfile): Promise { + if (!profile.savePassword) { + return Promise.resolve(true); + } + return this.doSavePassword(profile); + } + + private doSavePassword(conn: IConnectionProfile): Promise { + let self = this; + return new Promise((resolve, reject) => { + if (conn.password) { + let credentialId = this.formatCredentialId(conn); + self._credentialService.saveCredential(credentialId, conn.password) + .then((result) => { + resolve(result); + }, reason => { + // Bubble up error if there was a problem executing the set command + reject(reason); + }); + } else { + resolve(true); + } + }); + } + + public getConnectionProfileGroups(withoutConnections?: boolean): ConnectionProfileGroup[] { + let profilesInConfiguration: ConnectionProfile[]; + if (!withoutConnections) { + profilesInConfiguration = this._connectionConfig.getConnections(true); + } + let groups = this._connectionConfig.getAllGroups(); + + let connectionProfileGroups = this.convertToConnectionGroup(groups, profilesInConfiguration, undefined); + return connectionProfileGroups; + } + + private convertToConnectionGroup(groups: IConnectionProfileGroup[], connections: ConnectionProfile[], parent: ConnectionProfileGroup = undefined): ConnectionProfileGroup[] { + let result: ConnectionProfileGroup[] = []; + let children = groups.filter(g => g.parentId === (parent ? parent.id : undefined)); + if (children) { + children.map(group => { + let connectionGroup = new ConnectionProfileGroup(group.name, parent, group.id, group.color, group.description); + this.addGroupFullNameToMap(group.id, connectionGroup.fullName); + if (connections) { + let connectionsForGroup = connections.filter(conn => conn.groupId === connectionGroup.id); + var conns = []; + connectionsForGroup.forEach((conn) => { + conn.groupFullName = connectionGroup.fullName; + conns.push(conn); + }); + connectionGroup.addConnections(conns); + } + + let childrenGroups = this.convertToConnectionGroup(groups, connections, connectionGroup); + connectionGroup.addGroups(childrenGroups); + result.push(connectionGroup); + }); + if (parent) { + parent.addGroups(result); + } + } + return result; + } + + private loadProfiles(loadWorkspaceProfiles: boolean): IConnectionProfile[] { + let connections: IConnectionProfile[] = this._connectionConfig.getConnections(loadWorkspaceProfiles); + return connections; + } + + private getMaxRecentConnectionsCount(): number { + let config = this._workspaceConfigurationService.getConfiguration(Constants.sqlConfigSectionName); + + let maxConnections: number = config[Constants.configMaxRecentConnections]; + if (typeof (maxConnections) !== 'number' || maxConnections <= 0) { + maxConnections = MAX_CONNECTIONS_DEFAULT; + } + return maxConnections; + } + + public editGroup(group: ConnectionProfileGroup): Promise { + const self = this; + return new Promise((resolve, reject) => { + self._connectionConfig.editGroup(group).then(() => { + resolve(null); + }).catch(error => { + reject(error); + }); + }); + } + + public deleteConnectionFromConfiguration(connection: ConnectionProfile): Promise { + return this._connectionConfig.deleteConnection(connection); + } + + public deleteGroupFromConfiguration(group: ConnectionProfileGroup): Promise { + return this._connectionConfig.deleteGroup(group); + } + + public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise { + return this._connectionConfig.changeGroupIdForConnectionGroup(source, target); + } + + public canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean { + return this._connectionConfig.canChangeConnectionConfig(profile, newGroupID); + } + + public changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise { + return new Promise((resolve, reject) => { + this._connectionConfig.changeGroupIdForConnection(source, targetGroupId).then(() => { + resolve(); + }, (error => { + reject(error); + })); + }); + } + + private addGroupFullNameToMap(groupId: string, groupFullName: string): void { + if (groupId) { + this._groupIdToFullNameMap[groupId] = groupFullName; + } + if (groupFullName !== undefined) { + this._groupFullNameToIdMap[groupFullName.toUpperCase()] = groupId; + } + } + + private getGroupFullName(groupId: string): string { + if (groupId in this._groupIdToFullNameMap) { + return this._groupIdToFullNameMap[groupId]; + } else { + // Load the cache + this.getConnectionProfileGroups(true); + } + return this._groupIdToFullNameMap[groupId]; + } + + private getGroupId(groupFullName: string): string { + if (groupFullName === ConnectionProfileGroup.GroupNameSeparator) { + groupFullName = ''; + } + let key = groupFullName.toUpperCase(); + let result: string = ''; + if (key in this._groupFullNameToIdMap) { + result = this._groupFullNameToIdMap[key]; + } else { + // Load the cache + this.getConnectionProfileGroups(true); + result = this._groupFullNameToIdMap[key]; + } + return result; + } +} \ No newline at end of file diff --git a/src/sql/parts/connection/common/constants.ts b/src/sql/parts/connection/common/constants.ts new file mode 100644 index 0000000000..60d4b5a85a --- /dev/null +++ b/src/sql/parts/connection/common/constants.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// constants +export const sqlConfigSectionName = 'sql'; +export const outputChannelName = 'MSSQL'; + +export const connectionsArrayName = 'datasource.connections'; +export const connectionGroupsArrayName = 'datasource.connectionGroups'; + +/**Unsaved connections Id */ +export const unsavedGroupId = 'unsaved'; + +/* Memento constants */ +export const activeConnections = 'ACTIVE_CONNECTIONS'; +export const recentConnections = 'RECENT_CONNECTIONS'; +export const capabilitiesOptions = 'OPTIONS_METADATA'; + +export const configMaxRecentConnections = 'maxRecentConnections'; + +export const mssqlProviderName = 'MSSQL'; + +export const applicationName = 'sqlops'; + +export const defaultEngine = 'defaultEngine'; diff --git a/src/sql/parts/connection/common/iconnectionConfig.ts b/src/sql/parts/connection/common/iconnectionConfig.ts new file mode 100644 index 0000000000..1152bb3394 --- /dev/null +++ b/src/sql/parts/connection/common/iconnectionConfig.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IConnectionProfile } from './interfaces'; +import { IConnectionProfileGroup, ConnectionProfileGroup } from './connectionProfileGroup'; +import { ConnectionProfile } from './connectionProfile'; +import data = require('data'); + +/** + * Interface for a configuration file that stores connection profiles. + * + * @export + * @interface IConnectionConfig + */ +export interface IConnectionConfig { + addConnection(profile: IConnectionProfile): Promise; + addGroup(profileGroup: IConnectionProfileGroup): Promise; + getConnections(getWorkspaceConnections: boolean): ConnectionProfile[]; + getAllGroups(): IConnectionProfileGroup[]; + changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise; + changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise; + setCachedMetadata(cachedMetaData: data.DataProtocolServerCapabilities[]): void; + getCapabilities(providerName: string): data.DataProtocolServerCapabilities; + editGroup(group: ConnectionProfileGroup): Promise; + deleteConnection(profile: ConnectionProfile): Promise; + deleteGroup(group: ConnectionProfileGroup): Promise; + canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean; +} diff --git a/src/sql/parts/connection/common/interfaces.ts b/src/sql/parts/connection/common/interfaces.ts new file mode 100644 index 0000000000..ec1e80f791 --- /dev/null +++ b/src/sql/parts/connection/common/interfaces.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as data from 'data'; + +// A Connection Profile contains all the properties of connection credentials, with additional +// optional name and details on whether password should be saved +export interface IConnectionProfile extends data.ConnectionInfo { + serverName: string; + databaseName: string; + userName: string; + password: string; + authenticationType: string; + savePassword: boolean; + groupFullName: string; + groupId: string; + getOptionsKey(): string; + matches(profile: IConnectionProfile): boolean; + providerName: string; + saveProfile: boolean; + id: string; +}; + +export interface IConnectionProfileStore { + options: {}; + groupId: string; + providerName: string; + savePassword: boolean; + id: string; +}; + diff --git a/src/sql/parts/connection/common/localizedConstants.ts b/src/sql/parts/connection/common/localizedConstants.ts new file mode 100644 index 0000000000..8afc77f8a0 --- /dev/null +++ b/src/sql/parts/connection/common/localizedConstants.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; + +export const onDidConnectMessage = localize('onDidConnectMessage', 'Connected to'); +export const onDidDisconnectMessage = localize('onDidDisconnectMessage', 'Disconnected'); +export const unsavedGroupLabel = localize('unsavedGroupLabel', 'Unsaved Connections'); \ No newline at end of file diff --git a/src/sql/parts/connection/common/providerConnectionInfo.ts b/src/sql/parts/connection/common/providerConnectionInfo.ts new file mode 100644 index 0000000000..2b91a84ada --- /dev/null +++ b/src/sql/parts/connection/common/providerConnectionInfo.ts @@ -0,0 +1,241 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import data = require('data'); +import * as interfaces from 'sql/parts/connection/common/interfaces'; +import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/parts/connection/common/connectionManagement'; +import * as Constants from 'sql/parts/connection/common/constants'; + +export class ProviderConnectionInfo implements data.ConnectionInfo { + + options: { [name: string]: any }; + + public providerName: string; + protected _serverCapabilities: data.DataProtocolServerCapabilities; + private static readonly SqlAuthentication = 'SqlLogin'; + public static readonly ProviderPropertyName = 'providerName'; + + public constructor(serverCapabilities?: data.DataProtocolServerCapabilities, model?: interfaces.IConnectionProfile) { + this.options = {}; + if (serverCapabilities) { + this._serverCapabilities = serverCapabilities; + this.providerName = serverCapabilities.providerName; + } + if (model) { + if (model.options && this._serverCapabilities) { + this._serverCapabilities.connectionProvider.options.forEach(option => { + let value = model.options[option.name]; + this.options[option.name] = value; + }); + } + this.serverName = model.serverName; + this.authenticationType = model.authenticationType; + this.databaseName = model.databaseName; + this.password = model.password; + this.userName = model.userName; + } + } + + public clone(): ProviderConnectionInfo { + let instance = new ProviderConnectionInfo(this._serverCapabilities); + instance.options = Object.assign({}, this.options); + instance.providerName = this.providerName; + return instance; + } + + public get serverCapabilities(): data.DataProtocolServerCapabilities { + return this._serverCapabilities; + } + + public setServerCapabilities(value: data.DataProtocolServerCapabilities) { + this._serverCapabilities = value; + } + + public get serverName(): string { + return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.serverName); + } + + public get databaseName(): string { + return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.databaseName); + } + + public get userName(): string { + return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.userName); + } + + public get password(): string { + return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.password); + } + + public get authenticationType(): string { + return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.authType); + } + + public set serverName(value: string) { + this.setSpecialTypeOptionName(ConnectionOptionSpecialType.serverName, value); + } + + public set databaseName(value: string) { + this.setSpecialTypeOptionName(ConnectionOptionSpecialType.databaseName, value); + } + + public set userName(value: string) { + this.setSpecialTypeOptionName(ConnectionOptionSpecialType.userName, value); + } + + public set password(value: string) { + this.setSpecialTypeOptionName(ConnectionOptionSpecialType.password, value); + } + + public set authenticationType(value: string) { + this.setSpecialTypeOptionName(ConnectionOptionSpecialType.authType, value); + } + + public getOptionValue(name: string): any { + return this.options[name]; + } + + public setOptionValue(name: string, value: any): void { + //TODO: validate + this.options[name] = value; + } + + public isPasswordRequired(): boolean { + let optionMetadata = this._serverCapabilities.connectionProvider.options.find( + option => option.specialValueType === ConnectionOptionSpecialType.password); + let isPasswordRequired: boolean = optionMetadata.isRequired; + if (this.providerName === Constants.mssqlProviderName) { + isPasswordRequired = this.authenticationType === ProviderConnectionInfo.SqlAuthentication && optionMetadata.isRequired; + } + return isPasswordRequired; + } + + private getSpecialTypeOptionValue(type: number): string { + let name = this.getSpecialTypeOptionName(type); + if (name) { + return this.options[name]; + } + return undefined; + } + + /** + * Returns a key derived the connections options (providerName, authenticationType, serverName, databaseName, userName, groupid) + * This key uniquely identifies a connection in a group + * Example: "providerName:MSSQL|authenticationType:|databaseName:database|serverName:server3|userName:user|group:testid" + */ + public getOptionsKey(): string { + let idNames = []; + if (this._serverCapabilities) { + idNames = this._serverCapabilities.connectionProvider.options.map(o => { + if ((o.specialValueType || o.isIdentity) && o.specialValueType !== ConnectionOptionSpecialType.password) { + return o.name; + } else { + return undefined; + } + }); + } else { + // This should never happen but just incase the serverCapabilities was not ready at this time + idNames = ['authenticationType', 'database', 'server', 'user']; + } + + idNames = idNames.filter(x => x !== undefined); + + //Sort to make sure using names in the same order every time otherwise the ids would be different + idNames.sort(); + + let idValues: string[] = []; + for (var index = 0; index < idNames.length; index++) { + let value = this.options[idNames[index]]; + value = value ? value : ''; + idValues.push(`${idNames[index]}${ProviderConnectionInfo.nameValueSeparator}${value}`); + } + + return ProviderConnectionInfo.ProviderPropertyName + ProviderConnectionInfo.nameValueSeparator + + this.providerName + ProviderConnectionInfo.idSeparator + idValues.join(ProviderConnectionInfo.idSeparator); + } + + public static getProviderFromOptionsKey(optionsKey: string) { + let providerId: string = ''; + if (optionsKey) { + let ids: string[] = optionsKey.split(ProviderConnectionInfo.idSeparator); + ids.forEach(id => { + let idParts = id.split(ProviderConnectionInfo.nameValueSeparator); + if (idParts.length >= 2 && idParts[0] === ProviderConnectionInfo.ProviderPropertyName) { + providerId = idParts[1]; + } + }); + } + return providerId; + } + + public getSpecialTypeOptionName(type: number): string { + if (this._serverCapabilities) { + let optionMetadata = this._serverCapabilities.connectionProvider.options.find(o => o.specialValueType === type); + return !!optionMetadata ? optionMetadata.name : undefined; + } else { + return type.toString(); + } + } + + public setSpecialTypeOptionName(type: number, value: string): void { + let name = this.getSpecialTypeOptionName(type); + if (!!name) { + this.options[name] = value; + } + } + + public get authenticationTypeDisplayName(): string { + let optionMetadata = this._serverCapabilities.connectionProvider.options.find(o => o.specialValueType === ConnectionOptionSpecialType.authType); + let authType = this.authenticationType; + let displayName: string = authType; + + if (optionMetadata && optionMetadata.categoryValues) { + optionMetadata.categoryValues.forEach(element => { + if (element.name === authType) { + displayName = element.displayName; + } + }); + } + return displayName; + } + + public getProviderOptions(): data.ConnectionOption[] { + return this._serverCapabilities.connectionProvider.options; + } + + public static get idSeparator(): string { + return '|'; + } + + public static get nameValueSeparator(): string { + return ':'; + } + + public get titleParts(): string[] { + let parts: string[] = []; + // Always put these three on top. TODO: maybe only for MSSQL? + parts.push(this.serverName); + parts.push(this.databaseName); + parts.push(this.authenticationTypeDisplayName); + + this._serverCapabilities.connectionProvider.options.forEach(element => { + if (element.specialValueType !== ConnectionOptionSpecialType.serverName && + element.specialValueType !== ConnectionOptionSpecialType.databaseName && + element.specialValueType !== ConnectionOptionSpecialType.authType && + element.specialValueType !== ConnectionOptionSpecialType.password && + element.isIdentity && element.valueType === ServiceOptionType.string) { + let value = this.getOptionValue(element.name); + if (value) { + parts.push(value); + } + } + }); + + return parts; + } +} + diff --git a/src/sql/parts/connection/common/utils.ts b/src/sql/parts/connection/common/utils.ts new file mode 100644 index 0000000000..445eac15e7 --- /dev/null +++ b/src/sql/parts/connection/common/utils.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IConnectionProfile } from './interfaces'; +import { ConnectionProfile } from './connectionProfile'; +import { ConnectionProfileGroup } from './connectionProfileGroup'; + +// CONSTANTS ////////////////////////////////////////////////////////////////////////////////////// +const msInH = 3.6e6; +const msInM = 60000; +const msInS = 1000; +export const uriPrefixes = { + default: 'connection://', + connection: 'connection://', + dashboard: 'dashboard://', + insights: 'insights://' +}; + + +// FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////// + +export const defaultGroupId = 'C777F06B-202E-4480-B475-FA416154D458'; +export const ConnectionUriBackupIdAttributeName = 'backupId'; +export const ConnectionUriRestoreIdAttributeName = 'restoreId'; + +/** + * Takes a string in the format of HH:MM:SS.MS and returns a number representing the time in + * miliseconds + * @param value The string to convert to milliseconds + * @return False is returned if the string is an invalid format, + * the number of milliseconds in the time string is returned otherwise. + */ +export function parseTimeString(value: string): number | boolean { + if (!value) { + return false; + } + let tempVal = value.split('.'); + + if (tempVal.length === 1) { + // Ideally would handle more cleanly than this but for now handle case where ms not set + tempVal = [tempVal[0], '0']; + } else if (tempVal.length !== 2) { + return false; + } + + let msString = tempVal[1]; + let msStringEnd = msString.length < 3 ? msString.length : 3; + let ms = parseInt(tempVal[1].substring(0, msStringEnd), 10); + + tempVal = tempVal[0].split(':'); + + if (tempVal.length !== 3) { + return false; + } + + let h = parseInt(tempVal[0], 10); + let m = parseInt(tempVal[1], 10); + let s = parseInt(tempVal[2], 10); + + return ms + (h * msInH) + (m * msInM) + (s * msInS); +} + +/** + * Takes a number of milliseconds and converts it to a string like HH:MM:SS.fff + * @param value The number of milliseconds to convert to a timespan string + * @returns A properly formatted timespan string. + */ +export function parseNumAsTimeString(value: number): string { + let tempVal = value; + let h = Math.floor(tempVal / msInH); + tempVal %= msInH; + let m = Math.floor(tempVal / msInM); + tempVal %= msInM; + let s = Math.floor(tempVal / msInS); + tempVal %= msInS; + + let hs = h < 10 ? '0' + h : '' + h; + let ms = m < 10 ? '0' + m : '' + m; + let ss = s < 10 ? '0' + s : '' + s; + let mss = tempVal < 10 ? '00' + tempVal : tempVal < 100 ? '0' + tempVal : '' + tempVal; + + let rs = hs + ':' + ms + ':' + ss; + + return tempVal > 0 ? rs + '.' + mss : rs; +} + +/** + * Converts <, >, &, ", ', and any characters that are outside \u00A0 to numeric HTML entity values + * like { + * (Adapted from http://stackoverflow.com/a/18750001) + * @param str String to convert + * @return String with characters replaced. + */ +export function htmlEntities(str: string): string { + return typeof (str) === 'string' + ? str.replace(/[\u00A0-\u9999<>\&"']/gim, (i) => { return `&#${i.charCodeAt(0)};`; }) + : undefined; +} + +export function generateUri(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): string { + let prefix = purpose ? uriPrefixes[purpose] : uriPrefixes.default; + let uri = generateUriWithPrefix(connection, prefix); + + return uri; +} + +export function getUriPrefix(ownerUri: string): string { + let prefix: string = ''; + if (ownerUri) { + let index = ownerUri.indexOf('://'); + if (index > 0) { + prefix = ownerUri.substring(0, index + 3); + } else { + return uriPrefixes.default; + } + } + return prefix; +} + +export function generateUriWithPrefix(connection: IConnectionProfile, prefix: string): string { + let id = connection.getOptionsKey(); + let uri = prefix + (id ? id : connection.serverName + ':' + connection.databaseName); + + return uri; +} + +export function findProfileInGroup(og: IConnectionProfile, groups: ConnectionProfileGroup[]): ConnectionProfile { + for (let group of groups) { + for (let conn of group.connections) { + if (conn.id === og.id) { + return conn; + } + } + + if (group.hasChildren()) { + let potentialReturn = findProfileInGroup(og, group.children); + if (potentialReturn) { + return potentialReturn; + } + } + } + + return undefined; +} + +export function isMaster(profile: IConnectionProfile): boolean { + return profile.providerName.toLowerCase() === 'mssql' && profile.databaseName.toLowerCase() === 'master'; +} diff --git a/src/sql/parts/connection/connectionDialog/advancedPropertiesController.ts b/src/sql/parts/connection/connectionDialog/advancedPropertiesController.ts new file mode 100644 index 0000000000..fd99edfb3c --- /dev/null +++ b/src/sql/parts/connection/connectionDialog/advancedPropertiesController.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +// // import { AdvancedPropertiesDialog } from 'sql/parts/connection/connectionDialog/advancedPropertiesDialog'; +import { OptionsDialog } from 'sql/base/browser/ui/modal/optionsDialog'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import data = require('data'); +import { localize } from 'vs/nls'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; + +export class AdvancedPropertiesController { + private _container: HTMLElement; + + private _advancedDialog: OptionsDialog; + private _options: { [name: string]: any }; + + constructor(private _onCloseAdvancedProperties: () => void, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + } + + + private handleOnOk(): void { + this._options = this._advancedDialog.optionValues; + } + + public showDialog(providerOptions: data.ConnectionOption[], container: HTMLElement, options: { [name: string]: any }): void { + this._options = options; + this._container = container; + var serviceOptions = providerOptions.map(option => AdvancedPropertiesController.connectionOptionToServiceOption(option)); + this.advancedDialog.open(serviceOptions, this._options); + } + + public get advancedDialog() { + if (!this._advancedDialog) { + this._advancedDialog = this._instantiationService.createInstance( + OptionsDialog, localize('connectionAdvancedProperties', 'Advanced properties'), TelemetryKeys.ConnectionAdvancedProperties, { hasBackButton: true }); + this._advancedDialog.cancelLabel = localize('discard', 'Discard'); + this._advancedDialog.onCloseEvent(() => this._onCloseAdvancedProperties()); + this._advancedDialog.onOk(() => this.handleOnOk()); + this._advancedDialog.render(); + } + return this._advancedDialog; + } + + public set advancedDialog(dialog: OptionsDialog) { + this._advancedDialog = dialog; + } + + public static connectionOptionToServiceOption(connectionOption: data.ConnectionOption): data.ServiceOption { + return { + name: connectionOption.name, + displayName: connectionOption.displayName, + description: connectionOption.description, + groupName: connectionOption.groupName, + valueType: connectionOption.valueType, + defaultValue: connectionOption.defaultValue, + objectType: undefined, + categoryValues: connectionOption.categoryValues, + isRequired: connectionOption.isRequired, + isArray: undefined, + }; + } +} \ No newline at end of file diff --git a/src/sql/parts/connection/connectionDialog/connectionController.ts b/src/sql/parts/connection/connectionDialog/connectionController.ts new file mode 100644 index 0000000000..3a5f8f7d0d --- /dev/null +++ b/src/sql/parts/connection/connectionDialog/connectionController.ts @@ -0,0 +1,138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IConnectionManagementService, ConnectionOptionSpecialType } from 'sql/parts/connection/common/connectionManagement'; +import { IConnectionComponentCallbacks, IConnectionComponentController, IConnectionResult } from 'sql/parts/connection/connectionDialog/connectionDialogService'; +import { ConnectionWidget } from 'sql/parts/connection/connectionDialog/connectionWidget'; +import { AdvancedPropertiesController } from 'sql/parts/connection/connectionDialog/advancedPropertiesController'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import * as Constants from 'sql/parts/connection/common/constants'; +import data = require('data'); +import * as Utils from 'sql/parts/connection/common/utils'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +export class ConnectionController implements IConnectionComponentController { + private _container: HTMLElement; + private _connectionManagementService: IConnectionManagementService; + private _callback: IConnectionComponentCallbacks; + private _connectionWidget: ConnectionWidget; + private _advancedController: AdvancedPropertiesController; + private _model: IConnectionProfile; + private _providerOptions: data.ConnectionOption[]; + private _providerName: string; + + constructor(container: HTMLElement, + connectionManagementService: IConnectionManagementService, + sqlCapabilities: data.DataProtocolServerCapabilities, + callback: IConnectionComponentCallbacks, + providerName: string, + @IInstantiationService private _instantiationService: IInstantiationService, ) { + this._container = container; + this._connectionManagementService = connectionManagementService; + this._callback = callback; + this._providerOptions = sqlCapabilities.connectionProvider.options; + var specialOptions = this._providerOptions.filter( + (property) => (property.specialValueType !== null && property.specialValueType !== undefined)); + this._connectionWidget = this._instantiationService.createInstance(ConnectionWidget, specialOptions, { + onSetConnectButton: (enable: boolean) => this._callback.onSetConnectButton(enable), + onCreateNewServerGroup: () => this.onCreateNewServerGroup(), + onAdvancedProperties: () => this.handleOnAdvancedProperties(), + onSetAzureTimeOut: () => this.handleonSetAzureTimeOut() + }, providerName); + this._providerName = providerName; + } + + private onCreateNewServerGroup(): void { + this._connectionManagementService.showCreateServerGroupDialog({ + onAddGroup: (groupName) => this._connectionWidget.updateServerGroup(this.getAllServerGroups(), groupName), + onClose: () => this._connectionWidget.focusOnServerGroup() + }); + } + + private handleonSetAzureTimeOut(): void { + var timeoutPropertyName = 'connectTimeout'; + var timeoutOption = this._model.options[timeoutPropertyName]; + if (timeoutOption === undefined || timeoutOption === null) { + this._model.options[timeoutPropertyName] = 30; + } + } + + private handleOnAdvancedProperties(): void { + if (!this._advancedController) { + this._advancedController = this._instantiationService.createInstance(AdvancedPropertiesController, () => this._connectionWidget.focusOnAdvancedButton()); + } + var advancedOption = this._providerOptions.filter( + (property) => (property.specialValueType === undefined || property.specialValueType === null)); + this._advancedController.showDialog(advancedOption, this._container, this._model.options); + } + + public showUiComponent(container: HTMLElement): void { + this._connectionWidget.createConnectionWidget(container); + } + + private getServerGroupHelper(group: ConnectionProfileGroup, groupNames: IConnectionProfileGroup[]): void { + if (group) { + if (group.fullName !== '') { + groupNames.push(group); + } + if (group.hasChildren()) { + group.children.forEach((child) => this.getServerGroupHelper(child, groupNames)); + } + } + } + + private getAllServerGroups(): IConnectionProfileGroup[] { + var connectionGroupRoot = this._connectionManagementService.getConnectionGroups(); + var connectionGroupNames: IConnectionProfileGroup[] = []; + if (connectionGroupRoot && connectionGroupRoot.length > 0) { + this.getServerGroupHelper(connectionGroupRoot[0], connectionGroupNames); + } + let defaultGroupId: string; + if (connectionGroupRoot && connectionGroupRoot.length > 0 && ConnectionProfileGroup.isRoot(connectionGroupRoot[0].name)) { + defaultGroupId = connectionGroupRoot[0].id; + } else { + defaultGroupId = Utils.defaultGroupId; + } + connectionGroupNames.push(Object.assign({}, this._connectionWidget.DefaultServerGroup, { id: defaultGroupId })); + connectionGroupNames.push(this._connectionWidget.NoneServerGroup); + return connectionGroupNames; + } + + public initDialog(connectionInfo: IConnectionProfile): void { + this._connectionWidget.updateServerGroup(this.getAllServerGroups()); + this._model = connectionInfo; + this._model.providerName = this._providerName; + let appNameOption = this._providerOptions.find(option => option.specialValueType === ConnectionOptionSpecialType.appName); + if (appNameOption) { + let appNameKey = appNameOption.name; + this._model.options[appNameKey] = Constants.applicationName; + } + this._connectionWidget.initDialog(this._model); + } + + public focusOnOpen(): void { + this._connectionWidget.focusOnOpen(); + } + + public validateConnection(): IConnectionResult { + return { isValid: this._connectionWidget.connect(this._model), connection: this._model }; + } + + public fillInConnectionInputs(connectionInfo: IConnectionProfile): void { + this._model = connectionInfo; + this._connectionWidget.fillInConnectionInputs(connectionInfo); + } + + public handleOnConnecting(): void { + this._connectionWidget.handleOnConnecting(); + } + + public handleResetConnection(): void { + this._connectionWidget.handleResetConnection(); + } +} \ No newline at end of file diff --git a/src/sql/parts/connection/connectionDialog/connectionDialogService.ts b/src/sql/parts/connection/connectionDialog/connectionDialogService.ts new file mode 100644 index 0000000000..5915444d3f --- /dev/null +++ b/src/sql/parts/connection/connectionDialog/connectionDialogService.ts @@ -0,0 +1,338 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { + IConnectionDialogService, IConnectionManagementService, IErrorMessageService, + ConnectionType, INewConnectionParams, IConnectionCompletionOptions +} from 'sql/parts/connection/common/connectionManagement'; +import { ConnectionDialogWidget, OnShowUIResponse } from 'sql/parts/connection/connectionDialog/connectionDialogWidget'; +import { ConnectionController } from 'sql/parts/connection/connectionDialog/connectionController'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; +import * as Constants from 'sql/parts/connection/common/constants'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { localize } from 'vs/nls'; + +import * as data from 'data'; + +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { withElementById } from 'vs/base/browser/builder'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import Severity from 'vs/base/common/severity'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; + +export interface IConnectionResult { + isValid: boolean; + connection: IConnectionProfile; +} + +export interface IConnectionComponentCallbacks { + onSetConnectButton: (enable: boolean) => void; + onCreateNewServerGroup?: () => void; + onAdvancedProperties?: () => void; + onSetAzureTimeOut?: () => void; +} + +export interface IConnectionComponentController { + showUiComponent(container: HTMLElement): void; + initDialog(model: IConnectionProfile): void; + validateConnection(): IConnectionResult; + fillInConnectionInputs(connectionInfo: IConnectionProfile): void; + handleOnConnecting(): void; + handleResetConnection(): void; + focusOnOpen(): void; +} + +export class ConnectionDialogService implements IConnectionDialogService { + + _serviceBrand: any; + + private _connectionManagementService: IConnectionManagementService; + private _container: HTMLElement; + private _connectionDialog: ConnectionDialogWidget; + private _connectionControllerMap: { [providerDisplayName: string]: IConnectionComponentController }; + private _model: ConnectionProfile; + private _params: INewConnectionParams; + private _inputModel: IConnectionProfile; + private _capabilitiesMaps: { [providerDisplayName: string]: data.DataProtocolServerCapabilities }; + private _providerNameToDisplayNameMap: { [providerDisplayName: string]: string }; + private _providerTypes: string[]; + private _currentProviderType: string = 'Microsoft SQL Server'; + private _connecting: boolean = false; + private _connectionErrorTitle = localize('connectionError', 'Connection Error'); + + constructor( + @IPartService private _partService: IPartService, + @IInstantiationService private _instantiationService: IInstantiationService, + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService, + @IErrorMessageService private _errorMessageService: IErrorMessageService, + @IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService + ) { + this._capabilitiesMaps = {}; + this._providerNameToDisplayNameMap = {}; + this._connectionControllerMap = {}; + this._providerTypes = []; + if (_capabilitiesService) { + _capabilitiesService.onProviderRegisteredEvent((capabilities => { + let defaultProvider = this.getDefaultProviderName(); + if (capabilities.providerName === defaultProvider) { + this.showDialogWithModel(); + } + })); + } + } + + private getDefaultProviderName() { + if (this._workspaceConfigurationService) { + let defaultProvider = WorkbenchUtils.getSqlConfigValue(this._workspaceConfigurationService, Constants.defaultEngine); + if (defaultProvider + && this._capabilitiesMaps + && defaultProvider in this._capabilitiesMaps) { + return defaultProvider; + } + } + // as a fallback, default to MSSQL if the value from settings is not available + return Constants.mssqlProviderName; + } + + private handleOnConnect(params: INewConnectionParams, profile?: IConnectionProfile): void { + if (!this._connecting) { + this._connecting = true; + this.handleProviderOnConnecting(); + if (!profile) { + let result = this.uiController.validateConnection(); + if (!result.isValid) { + this._connecting = false; + this._connectionDialog.resetConnection(); + return; + } + profile = result.connection; + + // append the port to the server name for SQL Server connections + if (this.getCurrentProviderName() === Constants.mssqlProviderName) { + let portPropertyName: string = 'port'; + let portOption: string = profile.options[portPropertyName]; + if (portOption && portOption.indexOf(',') === -1) { + profile.serverName = profile.serverName + ',' + portOption; + } + profile.options[portPropertyName] = undefined; + } + + // Disable password prompt during reconnect if connected with an empty password + if (profile.password === '' && profile.savePassword === false) { + profile.savePassword = true; + } + + this.handleDefaultOnConnect(params, profile); + } else { + this._connectionManagementService.addSavedPassword(profile).then(connectionWithPassword => { + this.handleDefaultOnConnect(params, connectionWithPassword); + }); + } + } + } + + private handleOnCancel(params: INewConnectionParams): void { + if (params && params.input && params.connectionType === ConnectionType.editor) { + this._connectionManagementService.cancelEditorConnection(params.input); + } else { + this._connectionManagementService.cancelConnection(this._model); + } + if (params && params.input && params.input.onConnectReject) { + params.input.onConnectReject(); + } + this._connectionDialog.resetConnection(); + this._connecting = false; + } + + private handleDefaultOnConnect(params: INewConnectionParams, connection: IConnectionProfile): Thenable { + let fromEditor = params && params.connectionType === ConnectionType.editor; + let uri: string = undefined; + if (fromEditor && params.input) { + uri = params.input.uri; + } + let options: IConnectionCompletionOptions = { + params: params, + saveTheConnection: !fromEditor, + showDashboard: params.showDashboard !== undefined ? params.showDashboard : !fromEditor, + showConnectionDialogOnError: false, + showFirewallRuleOnError: true + }; + + return this._connectionManagementService.connectAndSaveProfile(connection, uri, options, params.input).then(connectionResult => { + this._connecting = false; + if (connectionResult && connectionResult.connected) { + this._connectionDialog.close(); + } else if (connectionResult && connectionResult.errorHandled) { + this._connectionDialog.resetConnection(); + } else { + this._errorMessageService.showDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage); + this._connectionDialog.resetConnection(); + } + }).catch(err => { + this._connecting = false; + this._errorMessageService.showDialog(Severity.Error, this._connectionErrorTitle, err); + this._connectionDialog.resetConnection(); + }); + } + + private get uiController(): IConnectionComponentController { + // Find the provider name from the selected provider type, or throw an error if it does not correspond to a known provider + let providerName = this.getCurrentProviderName(); + if (!providerName) { + throw Error('Invalid provider type'); + } + + // Set the model name, initialize the controller if needed, and return the controller + this._model.providerName = providerName; + if (!this._connectionControllerMap[providerName]) { + this._connectionControllerMap[providerName] = this._instantiationService.createInstance(ConnectionController, this._container, this._connectionManagementService, this._capabilitiesMaps[providerName], { + onSetConnectButton: (enable: boolean) => this.handleSetConnectButtonEnable(enable) + }, providerName); + } + return this._connectionControllerMap[providerName]; + } + + private handleSetConnectButtonEnable(enable: boolean): void { + this._connectionDialog.connectButtonState = enable; + } + + private handleShowUiComponent(input: OnShowUIResponse) { + this._currentProviderType = input.selectedProviderType; + this._model = new ConnectionProfile(this._capabilitiesMaps[this.getCurrentProviderName()], this._model); + this.uiController.showUiComponent(input.container); + } + + private handleInitDialog() { + this.uiController.initDialog(this._model); + } + + private handleFillInConnectionInputs(connectionInfo: IConnectionProfile): void { + this._connectionManagementService.addSavedPassword(connectionInfo).then(connectionWithPassword => { + var model = this.createModel(connectionWithPassword); + this._model = model; + this.uiController.fillInConnectionInputs(model); + }); + this._connectionDialog.updateProvider(this._providerNameToDisplayNameMap[connectionInfo.providerName]); + } + + private handleProviderOnResetConnection(): void { + this.uiController.handleResetConnection(); + } + + private handleProviderOnConnecting(): void { + this.uiController.handleOnConnecting(); + } + + private updateModelServerCapabilities(model: IConnectionProfile) { + this._model = this.createModel(model); + this._currentProviderType = this._providerNameToDisplayNameMap[this._model.providerName]; + if (this._connectionDialog) { + this._connectionDialog.updateProvider(this._currentProviderType); + } + } + + private createModel(model: IConnectionProfile): ConnectionProfile { + let defaultProvider = this.getDefaultProviderName(); + let providerName = model ? model.providerName : defaultProvider; + providerName = providerName ? providerName : defaultProvider; + let serverCapabilities = this._capabilitiesMaps[providerName]; + let newProfile = new ConnectionProfile(serverCapabilities, model); + newProfile.saveProfile = true; + newProfile.generateNewId(); + // If connecting from a query editor set "save connection" to false + if (this._params && this._params.input && this._params.connectionType === ConnectionType.editor) { + newProfile.saveProfile = false; + } + return newProfile; + } + + private cacheCapabilities(capabilities: data.DataProtocolServerCapabilities) { + if (capabilities) { + this._providerTypes.push(capabilities.providerDisplayName); + this._capabilitiesMaps[capabilities.providerName] = capabilities; + this._providerNameToDisplayNameMap[capabilities.providerName] = capabilities.providerDisplayName; + } + } + + private showDialogWithModel(): TPromise { + return new TPromise((resolve, reject) => { + if (this.getDefaultProviderName() in this._capabilitiesMaps) { + this.updateModelServerCapabilities(this._inputModel); + + this.doShowDialog(this._params); + } + let none: void; + resolve(none); + }); + } + + public showDialog( + connectionManagementService: IConnectionManagementService, + params: INewConnectionParams, + model?: IConnectionProfile, + error?: string): Thenable { + + this._connectionManagementService = connectionManagementService; + this._params = params; + this._inputModel = model; + + return new Promise((resolve, reject) => { + // only create the provider maps first time the dialog gets called + let capabilitiesPromise: Promise = Promise.resolve(); + if (this._providerTypes.length === 0) { + capabilitiesPromise = this._capabilitiesService.onCapabilitiesReady().then(() => { + let capabilities = this._capabilitiesService.getCapabilities(); + capabilities.forEach(c => { + this.cacheCapabilities(c); + }); + }); + } + + capabilitiesPromise.then(success => { + this.updateModelServerCapabilities(model); + // If connecting from a query editor set "save connection" to false + if (params && params.input && params.connectionType === ConnectionType.editor) { + this._model.saveProfile = false; + } + + resolve(this.showDialogWithModel().then(() => { + if (error && error !== '') { + this._errorMessageService.showDialog(Severity.Error, this._connectionErrorTitle, error); + } + })); + }, err => reject(err)); + }); + } + + private doShowDialog(params: INewConnectionParams): TPromise { + if (!this._connectionDialog) { + let container = withElementById(this._partService.getWorkbenchElementId()).getHTMLElement().parentElement; + this._container = container; + this._connectionDialog = this._instantiationService.createInstance(ConnectionDialogWidget, this._providerTypes, this._providerNameToDisplayNameMap[this._model.providerName]); + this._connectionDialog.onCancel(() => this.handleOnCancel(this._connectionDialog.newConnectionParams)); + this._connectionDialog.onConnect((profile) => this.handleOnConnect(this._connectionDialog.newConnectionParams, profile)); + this._connectionDialog.onShowUiComponent((input) => this.handleShowUiComponent(input)); + this._connectionDialog.onInitDialog(() => this.handleInitDialog()); + this._connectionDialog.onFillinConnectionInputs((input) => this.handleFillInConnectionInputs(input)); + this._connectionDialog.onResetConnection(() => this.handleProviderOnResetConnection()); + this._connectionDialog.render(); + } + this._connectionDialog.newConnectionParams = params; + + return new TPromise(() => { + this._connectionDialog.open(this._connectionManagementService.getRecentConnections().length > 0); + this.uiController.focusOnOpen(); + }); + } + + private getCurrentProviderName(): string { + return 'MSSQL'; + } +} diff --git a/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts b/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts new file mode 100644 index 0000000000..6cd640543b --- /dev/null +++ b/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts @@ -0,0 +1,301 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/connectionDialog'; + +import { attachModalDialogStyler } from 'sql/common/theme/styler'; +import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { Modal } from 'sql/base/browser/ui/modal/modal'; +import { IConnectionManagementService, INewConnectionParams } from 'sql/parts/connection/common/connectionManagement'; +import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper'; +import { TreeCreationUtils } from 'sql/parts/registeredServer/viewlet/treeCreationUtils'; +import { TreeUpdateUtils } from 'sql/parts/registeredServer/viewlet/treeUpdateUtils'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; + +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import * as styler from 'vs/platform/theme/common/styler'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { DefaultController, ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; +import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import { localize } from 'vs/nls'; +import * as DOM from 'vs/base/browser/dom'; + +export interface OnShowUIResponse { + selectedProviderType: string; + container: HTMLElement; +} + +class TreeController extends DefaultController { + constructor(private clickcb: (element: any, eventish: ICancelableEvent, origin: string) => void) { + super(); + } + + protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean { + this.clickcb(element, eventish, origin); + return super.onLeftClick(tree, element, eventish, origin); + } + + protected onEnter(tree: ITree, event: IKeyboardEvent): boolean { + super.onEnter(tree, event); + this.clickcb(tree.getSelection()[0], event, 'keyboard'); + return true; + } +} + +export class ConnectionDialogWidget extends Modal { + private _bodyBuilder: Builder; + private _recentConnectionBuilder: Builder; + private _dividerBuilder: Builder; + private _connectButton: Button; + private _closeButton: Button; + private _newConnectionParams: INewConnectionParams; + private _recentConnectionTree: ITree; + private $connectionUIContainer: Builder; + + private _onInitDialog = new Emitter(); + public onInitDialog: Event = this._onInitDialog.event; + + private _onCancel = new Emitter(); + public onCancel: Event = this._onCancel.event; + + private _onConnect = new Emitter(); + public onConnect: Event = this._onConnect.event; + + private _onShowUiComponent = new Emitter(); + public onShowUiComponent: Event = this._onShowUiComponent.event; + + private _onFillinConnectionInputs = new Emitter(); + public onFillinConnectionInputs: Event = this._onFillinConnectionInputs.event; + + private _onResetConnection = new Emitter(); + public onResetConnection: Event = this._onResetConnection.event; + + constructor( + private providerTypeOptions: string[], + private selectedProviderType: string, + @IInstantiationService private _instantiationService: IInstantiationService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IWorkbenchThemeService private _themeService: IWorkbenchThemeService, + @IPartService _partService: IPartService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(localize('connection', 'Connection'), TelemetryKeys.Connection, _partService, telemetryService, contextKeyService, { hasSpinner: true, hasErrors: true }); + } + + protected renderBody(container: HTMLElement): void { + this._bodyBuilder = new Builder(container); + + this._bodyBuilder.div({ class: 'connection-recent', id: 'recentConnection' }, (builder) => { + this._recentConnectionBuilder = new Builder(builder.getHTMLElement()); + this.createRecentConnections(); + this._recentConnectionBuilder.hide(); + }); + + this._bodyBuilder.div({ class: 'Connection-divider' }, (dividerContainer) => { + this._dividerBuilder = dividerContainer; + }); + + this._bodyBuilder.div({ class: 'connection-type' }, (modelTableContent) => { + // add SQL Server label to Connection Dialog until we support multiple connection providers + let sqlServerName = localize('microsoftSqlServer', "Microsoft SQL Server"); + modelTableContent.div({ class: 'server-name-label' }, (nameLabel) => { + nameLabel.innerHtml(sqlServerName); + }); + + //let connectTypeLabel = localize('connectType', 'Connection type'); + modelTableContent.element('table', { class: 'connection-table-content' }, (tableContainer) => { + // DialogHelper.appendInputSelectBox( + // DialogHelper.appendRow(tableContainer, connectTypeLabel, 'connection-label', 'connection-input'), this._providerTypeSelectBox); + }); + }); + + this.$connectionUIContainer = $('.connection-provider-info#connectionProviderInfo'); + this.$connectionUIContainer.appendTo(this._bodyBuilder); + + let self = this; + this._register(self._themeService.onDidColorThemeChange(e => self.updateTheme(e))); + self.updateTheme(self._themeService.getColorTheme()); + } + + /** + * Render the connection flyout + */ + public render() { + super.render(); + attachModalDialogStyler(this, this._themeService); + let connectLabel = localize('connect', 'Connect'); + let cancelLabel = localize('cancel', 'Cancel'); + this._connectButton = this.addFooterButton(connectLabel, () => this.connect()); + this._connectButton.enabled = false; + this._closeButton = this.addFooterButton(cancelLabel, () => this.cancel()); + this.registerListeners(); + this.onProviderTypeSelected('MSSQL'); + } + + // Update theming that is specific to connection flyout body + private updateTheme(theme: IColorTheme): void { + let borderColor = theme.getColor(contrastBorder); + let border = borderColor ? borderColor.toString() : null; + if (this._dividerBuilder) { + this._dividerBuilder.style('border-top-width', border ? '1px' : null); + this._dividerBuilder.style('border-top-style', border ? 'solid' : null); + this._dividerBuilder.style('border-top-color', border); + } + } + + private registerListeners(): void { + this._register(styler.attachButtonStyler(this._connectButton, this._themeService)); + this._register(styler.attachButtonStyler(this._closeButton, this._themeService)); + + } + + private onProviderTypeSelected(selectedProviderType: string) { + // Show connection form based on server type + this.$connectionUIContainer.empty(); + this._onShowUiComponent.fire({ selectedProviderType: selectedProviderType, container: this.$connectionUIContainer.getHTMLElement() }); + this.initDialog(); + } + + private connect(element?: IConnectionProfile): void { + if (this._connectButton.enabled) { + this._connectButton.enabled = false; + this.showSpinner(); + this._onConnect.fire(element); + } + } + + /* Overwrite espace key behavior */ + protected onClose(e: StandardKeyboardEvent) { + this.cancel(); + } + + /* Overwrite enter key behavior */ + protected onAccept(e: StandardKeyboardEvent) { + if (!e.target.classList.contains('monaco-tree')) { + this.connect(); + } + } + + private cancel() { + this._onCancel.fire(); + this.close(); + } + + public close() { + this.resetConnection(); + this.hide(); + } + + private createRecentConnections() { + this._recentConnectionBuilder.div({ class: 'connection-recent-content' }, (recentConnectionContainer) => { + let recentHistoryLabel = localize('recentHistory', 'Recent history'); + recentConnectionContainer.div({ class: 'connection-history-label' }, (recentTitle) => { + recentTitle.innerHtml(recentHistoryLabel); + }); + + recentConnectionContainer.div({ class: 'server-explorer-viewlet' }, (divContainer: Builder) => { + divContainer.div({ class: 'explorer-servers' }, (treeContainer: Builder) => { + let leftClick = (element: any, eventish: ICancelableEvent, origin: string) => { + // element will be a server group if the tree is clicked rather than a item + if (element instanceof ConnectionProfile) { + this.onRecentConnectionClick({ payload: { origin: origin, originalEvent: eventish } }, element); + } + + }; + let controller = new TreeController(leftClick); + this._recentConnectionTree = TreeCreationUtils.createConnectionTree(treeContainer.getHTMLElement(), this._instantiationService, controller); + + // Theme styler + this._register(styler.attachListStyler(this._recentConnectionTree, this._themeService)); + divContainer.append(this._recentConnectionTree.getHTMLElement()); + }); + }); + }); + } + + private onRecentConnectionClick(event: any, element: IConnectionProfile) { + let isMouseOrigin = event.payload && (event.payload.origin === 'mouse'); + let isDoubleClick = isMouseOrigin && event.payload.originalEvent && event.payload.originalEvent.detail === 2; + if (isDoubleClick) { + this.connect(element); + } else { + if (element) { + this._onFillinConnectionInputs.fire(element); + } + } + } + + /** + * Open the flyout dialog + * @param recentConnections Are there recent connections that should be shown + */ + public open(recentConnections: boolean) { + this.show(); + if (recentConnections) { + this._recentConnectionBuilder.show(); + TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService); + // call layout with view height + this.layout(); + } else { + this._recentConnectionBuilder.hide(); + } + + this.initDialog(); + } + + protected layout(height?: number): void { + // Height is the overall height. Since we're laying out a specific component, always get its actual height + this._recentConnectionTree.layout(DOM.getTotalHeight(this._recentConnectionTree.getHTMLElement())); + } + + /** + * Set the state of the connect button + * @param enabled The state to set the the button + */ + public set connectButtonState(enabled: boolean) { + this._connectButton.enabled = enabled; + } + + /** + * Get the connect button state + */ + public get connectButtonState(): boolean { + return this._connectButton.enabled; + } + + private initDialog(): void { + super.setError(''); + this.hideSpinner(); + this._onInitDialog.fire(); + } + + public resetConnection(): void { + this.hideSpinner(); + this._connectButton.enabled = true; + this._onResetConnection.fire(); + } + + public get newConnectionParams(): INewConnectionParams { + return this._newConnectionParams; + } + + public set newConnectionParams(params: INewConnectionParams) { + this._newConnectionParams = params; + } + + public updateProvider(displayName: string) { + this.onProviderTypeSelected('MSSQL'); + } +} diff --git a/src/sql/parts/connection/connectionDialog/connectionWidget.ts b/src/sql/parts/connection/connectionDialog/connectionWidget.ts new file mode 100644 index 0000000000..b05e5526d9 --- /dev/null +++ b/src/sql/parts/connection/connectionDialog/connectionWidget.ts @@ -0,0 +1,482 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/sqlConnection'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; +import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; +import { Checkbox } from 'sql/base/browser/ui/checkbox/defaultCheckbox'; +import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox'; +import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper'; +import { IConnectionComponentCallbacks } from 'sql/parts/connection/connectionDialog/connectionDialogService'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ConnectionOptionSpecialType } from 'sql/parts/connection/common/connectionManagement'; +import * as Constants from 'sql/parts/connection/common/constants'; +import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import * as styler from 'vs/platform/theme/common/styler'; +import { attachInputBoxStyler } from 'sql/common/theme/styler'; +import * as DOM from 'vs/base/browser/dom'; +import data = require('data'); +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { localize } from 'vs/nls'; + +export class ConnectionWidget { + private _builder: Builder; + private _serverGroupSelectBox: SelectBox; + private _previousGroupOption: string; + private _serverGroupOptions: IConnectionProfileGroup[]; + private _serverNameInputBox: InputBox; + private _databaseNameInputBox: InputBox; + private _userNameInputBox: InputBox; + private _passwordInputBox: InputBox; + private _rememberPasswordCheckBox: Checkbox; + private _advancedButton: Button; + private _callbacks: IConnectionComponentCallbacks; + private _authTypeSelectBox: SelectBox; + private _toDispose: lifecycle.IDisposable[]; + private _optionsMaps: { [optionType: number]: data.ConnectionOption }; + private _tableContainer: Builder; + private _providerName: string; + private _authTypeMap: { [providerName: string]: AuthenticationType[] } = { + [Constants.mssqlProviderName]: [new AuthenticationType('Integrated', false), new AuthenticationType('SqlLogin', true)] + }; + private _saveProfile: boolean; + public DefaultServerGroup: IConnectionProfileGroup = { + id: '', + name: localize('defaultServerGroup', ''), + parentId: undefined, + color: undefined, + description: undefined, + }; + + private _addNewServerGroup = { + id: '', + name: localize('addNewServerGroup', 'Add new group...'), + parentId: undefined, + color: undefined, + description: undefined, + }; + public NoneServerGroup: IConnectionProfileGroup = { + id: '', + name: localize('noneServerGroup', ''), + parentId: undefined, + color: undefined, + description: undefined, + }; + constructor(options: data.ConnectionOption[], + callbacks: IConnectionComponentCallbacks, + providerName: string, + @IThemeService private _themeService: IThemeService, + @IContextViewService private _contextViewService: IContextViewService) { + this._callbacks = callbacks; + this._toDispose = []; + this._optionsMaps = {}; + for (var i = 0; i < options.length; i++) { + var option = options[i]; + this._optionsMaps[option.specialValueType] = option; + } + + var authTypeOption = this._optionsMaps[ConnectionOptionSpecialType.authType]; + this._authTypeSelectBox = authTypeOption ? new SelectBox(authTypeOption.categoryValues.map(c => c.displayName), authTypeOption.defaultValue) : undefined; + this._providerName = providerName; + } + + public createConnectionWidget(container: HTMLElement): void { + this._serverGroupOptions = [this.DefaultServerGroup]; + this._serverGroupSelectBox = new SelectBox(this._serverGroupOptions.map(g => g.name), this.DefaultServerGroup.name); + this._previousGroupOption = this._serverGroupSelectBox.value; + this._builder = $().div({ class: 'connection-table' }, (modelTableContent) => { + modelTableContent.element('table', { class: 'connection-table-content' }, (tableContainer) => { + this._tableContainer = tableContainer; + }); + }); + this.fillInConnectionForm(); + this.registerListeners(); + if (this._authTypeSelectBox) { + this.onAuthTypeSelected(this._authTypeSelectBox.value); + } + DOM.append(container, this._builder.getHTMLElement()); + } + + private fillInConnectionForm(): void { + let errorMessage = localize('missingRequireField', ' is required.'); + + let serverNameOption = this._optionsMaps[ConnectionOptionSpecialType.serverName]; + let serverNameBuilder = DialogHelper.appendRow(this._tableContainer, serverNameOption.displayName, 'connection-label', 'connection-input'); + this._serverNameInputBox = new InputBox(serverNameBuilder.getHTMLElement(), this._contextViewService, { + validationOptions: { + validation: (value: string) => !value ? ({ type: MessageType.ERROR, content: serverNameOption.displayName + errorMessage }) : null + }, + }); + + if (this._optionsMaps[ConnectionOptionSpecialType.authType]) { + let authTypeBuilder = DialogHelper.appendRow(this._tableContainer, this._optionsMaps[ConnectionOptionSpecialType.authType].displayName, 'connection-label', 'connection-input'); + DialogHelper.appendInputSelectBox(authTypeBuilder, this._authTypeSelectBox); + } + + let self = this; + let userNameOption = this._optionsMaps[ConnectionOptionSpecialType.userName]; + let userNameBuilder = DialogHelper.appendRow(this._tableContainer, userNameOption.displayName, 'connection-label', 'connection-input'); + this._userNameInputBox = new InputBox(userNameBuilder.getHTMLElement(), this._contextViewService, { + validationOptions: { + validation: (value: string) => self.validateUsername(value, userNameOption.isRequired) ? ({ type: MessageType.ERROR, content: userNameOption.displayName + errorMessage }) : null + } + }); + + let passwordOption = this._optionsMaps[ConnectionOptionSpecialType.password]; + let passwordBuilder = DialogHelper.appendRow(this._tableContainer, passwordOption.displayName, 'connection-label', 'connection-input'); + this._passwordInputBox = new InputBox(passwordBuilder.getHTMLElement(), this._contextViewService); + this._passwordInputBox.inputElement.type = 'password'; + + let rememberPasswordLabel = localize('rememberPassword', 'Remember password'); + this._rememberPasswordCheckBox = this.appendCheckbox(this._tableContainer, rememberPasswordLabel, 'connection-checkbox', 'connection-input', false); + + let databaseOption = this._optionsMaps[ConnectionOptionSpecialType.databaseName]; + let databaseNameBuilder = DialogHelper.appendRow(this._tableContainer, databaseOption.displayName, 'connection-label', 'connection-input'); + this._databaseNameInputBox = new InputBox(databaseNameBuilder.getHTMLElement(), this._contextViewService, { + validationOptions: { + validation: (value: string) => (!value && databaseOption.isRequired) ? ({ type: MessageType.ERROR, content: databaseOption.displayName + errorMessage }) : null + }, + placeholder: (databaseOption.defaultValue || '') + }); + + let serverGroupLabel = localize('serverGroup', 'Server group'); + let serverGroupBuilder = DialogHelper.appendRow(this._tableContainer, serverGroupLabel, 'connection-label', 'connection-input'); + DialogHelper.appendInputSelectBox(serverGroupBuilder, this._serverGroupSelectBox); + + let AdvancedLabel = localize('advanced', 'Advanced...'); + this._advancedButton = this.createAdvancedButton(this._tableContainer, AdvancedLabel); + } + + private validateUsername(value: string, isOptionRequired: boolean): boolean { + let currentAuthType = this._authTypeSelectBox ? this.getMatchingAuthType(this._authTypeSelectBox.value) : undefined; + if (!currentAuthType || currentAuthType.showUsernameAndPassword) { + if (!value && isOptionRequired) { + return true; + } + } + return false; + } + + private createAdvancedButton(container: Builder, title: string): Button { + let button; + container.element('tr', {}, (rowContainer) => { + rowContainer.element('td'); + rowContainer.element('td', { align: 'right' }, (cellContainer) => { + cellContainer.div({ class: 'advanced-button' }, (divContainer) => { + button = new Button(divContainer); + button.label = title; + button.addListener('click', () => { + //open advanced page + this._callbacks.onAdvancedProperties(); + }); + }); + }); + }); + return button; + } + + private appendCheckbox(container: Builder, label: string, checkboxClass: string, cellContainerClass: string, isChecked: boolean): Checkbox { + let checkbox: Checkbox; + container.element('tr', {}, (rowContainer) => { + rowContainer.element('td'); + rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => { + checkbox = new Checkbox(inputCellContainer.getHTMLElement(), { label, checked: isChecked }); + }); + }); + return checkbox; + } + + private registerListeners(): void { + // Theme styler + this._toDispose.push(attachInputBoxStyler(this._serverNameInputBox, this._themeService)); + this._toDispose.push(attachInputBoxStyler(this._databaseNameInputBox, this._themeService)); + this._toDispose.push(attachInputBoxStyler(this._userNameInputBox, this._themeService)); + this._toDispose.push(attachInputBoxStyler(this._passwordInputBox, this._themeService)); + this._toDispose.push(styler.attachSelectBoxStyler(this._serverGroupSelectBox, this._themeService)); + this._toDispose.push(styler.attachButtonStyler(this._advancedButton, this._themeService)); + + if (this._authTypeSelectBox) { + // Theme styler + this._toDispose.push(styler.attachSelectBoxStyler(this._authTypeSelectBox, this._themeService)); + this._toDispose.push(this._authTypeSelectBox.onDidSelect(selectedAuthType => { + this.onAuthTypeSelected(selectedAuthType.selected); + })); + } + + this._toDispose.push(this._serverGroupSelectBox.onDidSelect(selectedGroup => { + this.onGroupSelected(selectedGroup.selected); + })); + + this._toDispose.push(this._serverNameInputBox.onDidChange(serverName => { + this.serverNameChanged(serverName); + })); + } + + private onGroupSelected(selectedGroup: string) { + if (selectedGroup === this._addNewServerGroup.name) { + // Select previous non-AddGroup option in case AddServerGroup dialog is cancelled + this._serverGroupSelectBox.selectWithOptionName(this._previousGroupOption); + this._callbacks.onCreateNewServerGroup(); + } else { + this._previousGroupOption = selectedGroup; + } + } + + private onAuthTypeSelected(selectedAuthType: string) { + let currentAuthType = this.getMatchingAuthType(selectedAuthType); + if (!currentAuthType.showUsernameAndPassword) { + this._userNameInputBox.disable(); + this._passwordInputBox.disable(); + this._userNameInputBox.hideMessage(); + this._passwordInputBox.hideMessage(); + this._userNameInputBox.value = ''; + this._passwordInputBox.value = ''; + + this._rememberPasswordCheckBox.checked = false; + this._rememberPasswordCheckBox.enabled = false; + } else { + this._userNameInputBox.enable(); + this._passwordInputBox.enable(); + this._rememberPasswordCheckBox.enabled = true; + } + } + + private serverNameChanged(serverName: string) { + this._callbacks.onSetConnectButton(!!serverName); + if (serverName.toLocaleLowerCase().includes('database.windows.net')) { + this._callbacks.onSetAzureTimeOut(); + } + } + + public focusOnAdvancedButton() { + this._advancedButton.focus(); + } + + public focusOnServerGroup() { + this._serverGroupSelectBox.focus(); + } + + public updateServerGroup(connectionGroups: IConnectionProfileGroup[], groupName?: string) { + this._serverGroupOptions = connectionGroups; + this._serverGroupOptions.push(this._addNewServerGroup); + this._serverGroupSelectBox.setOptions(this._serverGroupOptions.map(g => g.name)); + if (groupName) { + this._serverGroupSelectBox.selectWithOptionName(groupName); + this._previousGroupOption = this._serverGroupSelectBox.value; + } + } + + public initDialog(connectionInfo: IConnectionProfile): void { + this.fillInConnectionInputs(connectionInfo); + } + + public focusOnOpen(): void { + this._serverNameInputBox.focus(); + } + + private getModelValue(value: string): string { + return value ? value : ''; + } + + public fillInConnectionInputs(connectionInfo: IConnectionProfile) { + if (connectionInfo) { + this._serverNameInputBox.value = this.getModelValue(connectionInfo.serverName); + this._callbacks.onSetConnectButton(!!connectionInfo.serverName); + this._databaseNameInputBox.value = this.getModelValue(connectionInfo.databaseName); + this._userNameInputBox.value = this.getModelValue(connectionInfo.userName); + this._passwordInputBox.value = this.getModelValue(connectionInfo.password); + this._saveProfile = connectionInfo.saveProfile; + let groupName: string; + if (this._saveProfile) { + if (!connectionInfo.groupFullName) { + groupName = this.DefaultServerGroup.name; + } else { + groupName = connectionInfo.groupFullName.replace('root/', ''); + } + } else { + groupName = this.NoneServerGroup.name; + } + this._serverGroupSelectBox.selectWithOptionName(groupName); + this._previousGroupOption = this._serverGroupSelectBox.value; + + // To handle the empty password case + if (this.getModelValue(connectionInfo.password) === '') { + this._rememberPasswordCheckBox.checked = false; + } else { + this._rememberPasswordCheckBox.checked = connectionInfo.savePassword; + } + + if (connectionInfo.authenticationType !== null && connectionInfo.authenticationType !== undefined) { + var authTypeDisplayName = this.getAuthTypeDisplayName(connectionInfo.authenticationType); + this._authTypeSelectBox.selectWithOptionName(authTypeDisplayName); + } + + if (this._authTypeSelectBox) { + this.onAuthTypeSelected(this._authTypeSelectBox.value); + } + } + } + + private getAuthTypeDisplayName(authTypeName: string) { + var displayName: string; + var authTypeOption = this._optionsMaps[ConnectionOptionSpecialType.authType]; + authTypeOption.categoryValues.forEach(c => { + if (c.name === authTypeName) { + displayName = c.displayName; + } + }); + return displayName; + } + + private getAuthTypeName(authTypeDisplayName: string) { + var authTypeName: string; + var authTypeOption = this._optionsMaps[ConnectionOptionSpecialType.authType]; + authTypeOption.categoryValues.forEach(c => { + if (c.displayName === authTypeDisplayName) { + authTypeName = c.name; + } + }); + return authTypeName; + } + + public handleOnConnecting(): void { + this._advancedButton.enabled = false; + + this._serverGroupSelectBox.disable(); + this._serverNameInputBox.disable(); + this._databaseNameInputBox.disable(); + this._userNameInputBox.disable(); + this._passwordInputBox.disable(); + this._rememberPasswordCheckBox.enabled = false; + if (this._authTypeSelectBox) { + this._authTypeSelectBox.disable(); + } + } + + public handleResetConnection(): void { + this._advancedButton.enabled = true; + + this._serverGroupSelectBox.enable(); + this._serverNameInputBox.enable(); + this._databaseNameInputBox.enable(); + let currentAuthType: AuthenticationType = undefined; + if (this._authTypeSelectBox) { + this._authTypeSelectBox.enable(); + currentAuthType = this.getMatchingAuthType(this._authTypeSelectBox.value); + } + + if (!currentAuthType || currentAuthType.showUsernameAndPassword) { + this._userNameInputBox.enable(); + this._passwordInputBox.enable(); + this._rememberPasswordCheckBox.enabled = true; + } + } + + public get serverName(): string { + return this._serverNameInputBox.value; + } + + public get databaseName(): string { + return this._databaseNameInputBox.value; + } + + public get userName(): string { + return this._userNameInputBox.value; + } + + public get password(): string { + return this._passwordInputBox.value; + } + + public get authenticationType(): string { + return this._authTypeSelectBox ? this.getAuthTypeName(this._authTypeSelectBox.value) : undefined; + } + + private validateInputs(): boolean { + let isFocused = false; + let validateServerName = this._serverNameInputBox.validate(); + if (!validateServerName) { + this._serverNameInputBox.focus(); + isFocused = true; + } + let validateUserName = this._userNameInputBox.validate(); + if (!validateUserName && !isFocused) { + this._userNameInputBox.focus(); + isFocused = true; + } + let validatePassword = this._passwordInputBox.validate(); + if (!validatePassword && !isFocused) { + this._passwordInputBox.focus(); + isFocused = true; + } + let validateDatabaseName = this._databaseNameInputBox.validate(); + if (!validateDatabaseName && !isFocused) { + this._databaseNameInputBox.focus(); + } + return validateServerName && validateUserName && validatePassword && validateDatabaseName; + } + + public connect(model: IConnectionProfile): boolean { + let validInputs = this.validateInputs(); + if (validInputs) { + model.serverName = this.serverName; + model.databaseName = this.databaseName; + model.userName = this.userName; + model.password = this.password; + model.authenticationType = this.authenticationType; + model.savePassword = this._rememberPasswordCheckBox.checked; + if (this._serverGroupSelectBox.value === this.DefaultServerGroup.name) { + model.groupFullName = ''; + model.saveProfile = true; + model.groupId = this.findGroupId(model.groupFullName); + } else if (this._serverGroupSelectBox.value === this.NoneServerGroup.name) { + model.groupFullName = ''; + model.saveProfile = false; + } else if (this._serverGroupSelectBox.value !== this._addNewServerGroup.name) { + model.groupFullName = this._serverGroupSelectBox.value; + model.saveProfile = true; + model.groupId = this.findGroupId(model.groupFullName); + } + } + return validInputs; + } + + private findGroupId(groupFullName: string): string { + let group: IConnectionProfileGroup; + if (ConnectionProfileGroup.isRoot(groupFullName)) { + group = this._serverGroupOptions.find(g => ConnectionProfileGroup.isRoot(g.name)); + if (group === undefined) { + group = this._serverGroupOptions.find(g => g.name === this.DefaultServerGroup.name); + } + } else { + group = this._serverGroupOptions.find(g => g.name === groupFullName); + } + return group ? group.id : undefined; + } + + public dispose(): void { + this._toDispose = lifecycle.dispose(this._toDispose); + } + + private getMatchingAuthType(displayName: string): AuthenticationType { + return this._authTypeMap[this._providerName].find(authType => this.getAuthTypeDisplayName(authType.name) === displayName); + } +} + +class AuthenticationType { + public name: string; + public showUsernameAndPassword: boolean; + + constructor(name: string, showUsernameAndPassword: boolean) { + this.name = name; + this.showUsernameAndPassword = showUsernameAndPassword; + } +} \ No newline at end of file diff --git a/src/sql/parts/connection/connectionDialog/media/connectionDialog.css b/src/sql/parts/connection/connectionDialog/media/connectionDialog.css new file mode 100644 index 0000000000..285c1994f7 --- /dev/null +++ b/src/sql/parts/connection/connectionDialog/media/connectionDialog.css @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.connection-label { + width: 80px; + padding-bottom: 5px; +} + +.connection-input { + padding-right:8px; + width: 200px; + padding-bottom: 5px; +} + +.connection-recent { + margin: 15px; + height: calc(100% - 400px); + overflow-y: auto; +} + +.connection-history-label { + font-size: 15px; +} + +.server-name-label { + font-size: 15px; + margin-bottom: 15px; +} + +.connection-provider-info { + overflow-y: hidden; + margin: 15px; +} + +.connection-recent-content { + height: calc(100% - 20px); +} + +.connection-table-content { + width:100%; + table-layout: fixed; +} + +.connection-type { + margin: 15px; + overflow-y: hidden; +} \ No newline at end of file diff --git a/src/sql/parts/connection/connectionDialog/media/sqlConnection.css b/src/sql/parts/connection/connectionDialog/media/sqlConnection.css new file mode 100644 index 0000000000..e25c3b72d5 --- /dev/null +++ b/src/sql/parts/connection/connectionDialog/media/sqlConnection.css @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.advanced-button { + width: 100px; + padding-right:8px; + padding-bottom: 5px; +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/common/actions.ts b/src/sql/parts/dashboard/common/actions.ts new file mode 100644 index 0000000000..99e47fdde6 --- /dev/null +++ b/src/sql/parts/dashboard/common/actions.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; + +export class RefreshWidgetAction extends Action { + + public static ID = 'refreshWidget'; + public static LABEL = nls.localize('refreshWidget', 'Refresh'); + + constructor( + id: string, label: string, + private refreshFn: () => void + ) { + super(id, label); + } + + run(): TPromise { + try { + this.refreshFn(); + return TPromise.as(true); + } catch (e) { + return TPromise.as(false); + } + } +} diff --git a/src/sql/parts/dashboard/common/componentHost.directive.ts b/src/sql/parts/dashboard/common/componentHost.directive.ts new file mode 100644 index 0000000000..a736495e12 --- /dev/null +++ b/src/sql/parts/dashboard/common/componentHost.directive.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { Directive, ViewContainerRef, Inject, forwardRef } from '@angular/core'; + +@Directive({ + selector: '[component-host]', +}) +export class ComponentHostDirective { + constructor( @Inject(forwardRef(() => ViewContainerRef)) public viewContainerRef: ViewContainerRef) { } +} diff --git a/src/sql/parts/dashboard/common/dashboardPage.component.html b/src/sql/parts/dashboard/common/dashboardPage.component.html new file mode 100644 index 0000000000..f72ac5e066 --- /dev/null +++ b/src/sql/parts/dashboard/common/dashboardPage.component.html @@ -0,0 +1,16 @@ + +
+ + +
+
+
+ + +
+
\ No newline at end of file diff --git a/src/sql/parts/dashboard/common/dashboardPage.component.ts b/src/sql/parts/dashboard/common/dashboardPage.component.ts new file mode 100644 index 0000000000..b3069bc7ea --- /dev/null +++ b/src/sql/parts/dashboard/common/dashboardPage.component.ts @@ -0,0 +1,300 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList } from '@angular/core'; +import { NgGridConfig } from 'angular2-grid'; + +import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; +import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry'; +import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component'; + +import { Registry } from 'vs/platform/registry/common/platform'; +import * as types from 'vs/base/common/types'; +import { Severity } from 'vs/platform/message/common/message'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import * as colors from 'vs/platform/theme/common/colorRegistry'; +import * as nls from 'vs/nls'; + +/** + * @returns whether the provided parameter is a JavaScript Array and each element in the array is a number. + */ +function isNumberArray(value: any): value is number[] { + return types.isArray(value) && (value).every(elem => types.isNumber(elem)); +} + +@Component({ + selector: 'dashboard-page', + templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/common/dashboardPage.component.html')), + host: { + class: 'dashboard-page' + } +}) +export abstract class DashboardPage { + + protected SKELETON_WIDTH = 5; + protected widgets: Array = []; + protected gridConfig: NgGridConfig = { + 'margins': [10], // The size of the margins of each item. Supports up to four values in the same way as CSS margins. Can be updated using setMargins() + 'draggable': false, // Whether the items can be dragged. Can be updated using enableDrag()/disableDrag() + 'resizable': false, // Whether the items can be resized. Can be updated using enableResize()/disableResize() + 'max_cols': this.SKELETON_WIDTH, // The maximum number of columns allowed. Set to 0 for infinite. Cannot be used with max_rows + 'max_rows': 0, // The maximum number of rows allowed. Set to 0 for infinite. Cannot be used with max_cols + 'visible_cols': 0, // The number of columns shown on screen when auto_resize is set to true. Set to 0 to not auto_resize. Will be overriden by max_cols + 'visible_rows': 0, // The number of rows shown on screen when auto_resize is set to true. Set to 0 to not auto_resize. Will be overriden by max_rows + 'min_cols': 0, // The minimum number of columns allowed. Can be any number greater than or equal to 1. + 'min_rows': 0, // The minimum number of rows allowed. Can be any number greater than or equal to 1. + 'col_width': 250, // The width of each column + 'row_height': 250, // The height of each row + 'cascade': 'left', // The direction to cascade grid items ('up', 'right', 'down', 'left') + 'min_width': 100, // The minimum width of an item. If greater than col_width, this will update the value of min_cols + 'min_height': 100, // The minimum height of an item. If greater than row_height, this will update the value of min_rows + 'fix_to_grid': false, // Fix all item movements to the grid + 'auto_style': true, // Automatically add required element styles at run-time + 'auto_resize': false, // Automatically set col_width/row_height so that max_cols/max_rows fills the screen. Only has effect is max_cols or max_rows is set + 'maintain_ratio': false, // Attempts to maintain aspect ratio based on the colWidth/rowHeight values set in the config + 'prefer_new': false, // When adding new items, will use that items position ahead of existing items + 'limit_to_screen': true, // When resizing the screen, with this true and auto_resize false, the grid will re-arrange to fit the screen size. Please note, at present this only works with cascade direction up. + }; + private _themeDispose: IDisposable; + + @ViewChild('propertyContainer', { read: ElementRef }) private propertyContainer: ElementRef; + @ViewChild('properties') private _properties: DashboardWidgetWrapper; + @ViewChildren(DashboardWidgetWrapper) private _widgets: QueryList; + + // a set of config modifiers + private readonly _configModifiers: Array<(item: Array) => Array> = [ + this.removeEmpty, + this.initExtensionConfigs, + this.validateGridConfig, + this.addProvider, + this.addEdition, + this.addContext, + this.filterWidgets + ]; + + constructor( + @Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface + ) { } + + protected init() { + if (!this.dashboardService.connectionManagementService.connectionInfo) { + this.dashboardService.messageService.show(Severity.Warning, nls.localize('missingConnectionInfo', 'No connection information could be found for this dashboard')); + } else { + let tempWidgets = this.dashboardService.getSettings(this.context).widgets; + let properties = this.getProperties(); + this._configModifiers.forEach((cb) => { + tempWidgets = cb.apply(this, [tempWidgets]); + properties = properties ? cb.apply(this, [properties]) : undefined; + }); + this.widgets = tempWidgets; + this.propertiesWidget = properties ? properties[0] : undefined; + } + } + + protected baseInit(): void { + let self = this; + self._themeDispose = self.dashboardService.themeService.onDidColorThemeChange((event: IColorTheme) => { + self.updateTheme(event); + }); + self.updateTheme(self.dashboardService.themeService.getColorTheme()); + + } + + protected baseDestroy(): void { + if (this._themeDispose) { + this._themeDispose.dispose(); + } + } + + protected abstract propertiesWidget: WidgetConfig; + protected abstract get context(): string; + + /** + * Returns a filtered version of the widgets passed based on edition and provider + * @param config widgets to filter + */ + private filterWidgets(config: WidgetConfig[]): Array { + let connectionInfo: ConnectionManagementInfo = this.dashboardService.connectionManagementService.connectionInfo; + let edition = connectionInfo.serverInfo.engineEditionId; + let provider = connectionInfo.providerId; + + // filter by provider + return config.filter((item) => { + return this.stringCompare(item.provider, provider); + }).filter((item) => { + if (item.edition) { + if (edition) { + return this.stringCompare(isNumberArray(item.edition) ? item.edition.map(item => item.toString()) : item.edition.toString(), edition.toString()); + } else { + this.dashboardService.messageService.show(Severity.Warning, nls.localize('providerMissingEdition', 'Widget filters based on edition, but the provider does not have an edition')); + return true; + } + } else { + return true; + } + }); + } + + /** + * Does a compare against the val passed in and the compare string + * @param val string or array of strings to compare the compare value to; if array, it will compare each val in the array + * @param compare value to compare to + */ + private stringCompare(val: string | Array, compare: string): boolean { + if (types.isUndefinedOrNull(val)) { + return true; + } else if (types.isString(val)) { + return val === compare; + } else if (types.isStringArray(val)) { + return val.some(item => item === compare); + } else { + return false; + } + } + + /** + * Add provider to the passed widgets and returns the new widgets + * @param widgets Array of widgets to add provider onto + */ + protected addProvider(config: WidgetConfig[]): Array { + let provider = this.dashboardService.connectionManagementService.connectionInfo.providerId; + return config.map((item) => { + if (item.provider === undefined) { + item.provider = provider; + } + return item; + }); + } + + /** + * Adds the edition to the passed widgets and returns the new widgets + * @param widgets Array of widgets to add edition onto + */ + protected addEdition(config: WidgetConfig[]): Array { + let connectionInfo: ConnectionManagementInfo = this.dashboardService.connectionManagementService.connectionInfo; + let edition = connectionInfo.serverInfo.engineEditionId; + return config.map((item) => { + if (item.edition === undefined) { + item.edition = edition; + } + return item; + }); + } + + /** + * Adds the context to the passed widgets and returns the new widgets + * @param widgets Array of widgets to add context to + */ + protected addContext(config: WidgetConfig[]): Array { + let context = this.context; + return config.map((item) => { + if (item.context === undefined) { + item.context = context; + } + return item; + }); + } + + /** + * Validates configs to make sure nothing will error out and returns the modified widgets + * @param config Array of widgets to validate + */ + protected removeEmpty(config: WidgetConfig[]): Array { + return config.filter(widget => { + return !types.isUndefinedOrNull(widget); + }); + } + + /** + * Validates configs to make sure nothing will error out and returns the modified widgets + * @param config Array of widgets to validate + */ + protected validateGridConfig(config: WidgetConfig[]): Array { + return config.map((widget) => { + if (widget.gridItemConfig === undefined) { + widget.gridItemConfig = {}; + } + return widget; + }); + } + + protected initExtensionConfigs(configurations: WidgetConfig[]): Array { + let widgetRegistry = Registry.as(Extensions.InsightContribution); + return configurations.map((config) => { + if (config.widget && Object.keys(config.widget).length === 1) { + let key = Object.keys(config.widget)[0]; + let insightConfig = widgetRegistry.getRegisteredExtensionInsights(key); + if (insightConfig !== undefined) { + // Setup the default properties for this extension if needed + if (!config.provider && insightConfig.provider) { + config.provider = insightConfig.provider; + } + if (!config.name && insightConfig.name) { + config.name = insightConfig.name; + } + if (!config.edition && insightConfig.edition) { + config.edition = insightConfig.edition; + } + if (!config.gridItemConfig && insightConfig.gridItemConfig) { + config.gridItemConfig = { + sizex: insightConfig.gridItemConfig.x, + sizey: insightConfig.gridItemConfig.y + }; + } + } + } + return config; + }); + } + + private getProperties(): Array { + let properties = this.dashboardService.getSettings(this.context).properties; + if (types.isUndefinedOrNull(properties)) { + return [this.propertiesWidget]; + } else if (types.isBoolean(properties)) { + return properties ? [this.propertiesWidget] : []; + } else if (types.isArray(properties)) { + return properties.map((item) => { + let retVal = Object.assign({}, this.propertiesWidget); + retVal.edition = item.edition; + retVal.provider = item.provider; + retVal.widget = { 'properties-widget': { properties: item.properties } }; + return retVal; + }); + } else { + return undefined; + } + } + + private updateTheme(theme: IColorTheme): void { + let propsEl: HTMLElement = this.propertyContainer.nativeElement; + let widgetShadowColor = theme.getColor(colors.widgetShadow); + if (widgetShadowColor) { + // Box shadow on bottom only. + // The below settings fill the shadow across the whole page + propsEl.style.boxShadow = `-5px 5px 10px -5px ${widgetShadowColor}`; + propsEl.style.marginRight = '-10px'; + propsEl.style.marginBottom = '5px'; + } + } + + public refresh(refreshConfig: boolean = false): void { + if (refreshConfig) { + this.init(); + if (this._properties) { + this._properties.refresh(); + } + } else { + if (this._widgets) { + this._widgets.forEach(item => { + item.refresh(); + }); + } + } + } +} diff --git a/src/sql/parts/dashboard/common/dashboardWidget.ts b/src/sql/parts/dashboard/common/dashboardWidget.ts new file mode 100644 index 0000000000..2bd04ef3f0 --- /dev/null +++ b/src/sql/parts/dashboard/common/dashboardWidget.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { InjectionToken } from '@angular/core'; +import { NgGridItemConfig } from 'angular2-grid'; +import { Action } from 'vs/base/common/actions'; + +export interface IDashboardWidget { + actions: Array; + actionsContext?: any; + refresh?: () => void; +} + +export const WIDGET_CONFIG = new InjectionToken('widget_config'); + +export interface WidgetConfig { + name?: string; + icon?: string; + context: string; + provider: string | Array; + edition: number | Array; + gridItemConfig?: NgGridItemConfig; + widget: Object; + background_color?: string; + border?: string; + fontSize?: string; + fontWeight?: string; + padding?:string; +} + +export abstract class DashboardWidget { + protected _config: WidgetConfig; + + get actions(): Array { + return []; + } +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/common/dashboardWidgetWrapper.component.html b/src/sql/parts/dashboard/common/dashboardWidgetWrapper.component.html new file mode 100644 index 0000000000..e170cb5506 --- /dev/null +++ b/src/sql/parts/dashboard/common/dashboardWidgetWrapper.component.html @@ -0,0 +1,18 @@ + +
+ +
+
+ + {{_config.name}} +
+
+
+ + +
\ No newline at end of file diff --git a/src/sql/parts/dashboard/common/dashboardWidgetWrapper.component.ts b/src/sql/parts/dashboard/common/dashboardWidgetWrapper.component.ts new file mode 100644 index 0000000000..2ce7d90c44 --- /dev/null +++ b/src/sql/parts/dashboard/common/dashboardWidgetWrapper.component.ts @@ -0,0 +1,215 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/media/icons/common-icons'; + +import { + Component, Input, Inject, forwardRef, ComponentFactoryResolver, AfterContentInit, ViewChild, + ElementRef, OnInit, ChangeDetectorRef, OnDestroy, ReflectiveInjector, Injector, Type, ComponentRef +} from '@angular/core'; + +import { ComponentHostDirective } from './componentHost.directive'; +import { WidgetConfig, WIDGET_CONFIG, IDashboardWidget } from './dashboardWidget'; +import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry'; +import { error } from 'sql/base/common/log'; +import * as ACTIONS from './actions'; + +/* Widgets */ +import { PropertiesWidgetComponent } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component'; +import { ExplorerWidget } from 'sql/parts/dashboard/widgets/explorer/explorerWidget.component'; +import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.component'; +import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component'; + +import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import * as colors from 'vs/platform/theme/common/colorRegistry'; +import * as themeColors from 'vs/workbench/common/theme'; +import { Action } from 'vs/base/common/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Registry } from 'vs/platform/registry/common/platform'; + +const componentMap: { [x: string]: Type } = { + 'properties-widget': PropertiesWidgetComponent, + 'explorer-widget': ExplorerWidget, + 'tasks-widget': TasksWidget, + 'insights-widget': InsightsWidget +}; + +@Component({ + selector: 'dashboard-widget-wrapper', + templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/common/dashboardWidgetWrapper.component.html')) +}) +export class DashboardWidgetWrapper implements AfterContentInit, OnInit, OnDestroy { + @Input() private _config: WidgetConfig; + private _themeDispose: IDisposable; + private _actions: Array; + private _component: IDashboardWidget; + + @ViewChild('header', { read: ElementRef }) private header: ElementRef; + @ViewChild(ComponentHostDirective) componentHost: ComponentHostDirective; + + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver, + @Inject(forwardRef(() => ElementRef)) private _ref: ElementRef, + @Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface, + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeref: ChangeDetectorRef, + @Inject(forwardRef(() => Injector)) private _injector: Injector + ) { } + + ngOnInit() { + let self = this; + self._themeDispose = self._bootstrap.themeService.onDidColorThemeChange((event: IColorTheme) => { + self.updateTheme(event); + }); + } + + ngAfterContentInit() { + this.updateTheme(this._bootstrap.themeService.getColorTheme()); + this.loadWidget(); + } + + ngOnDestroy() { + this._themeDispose.dispose(); + } + + public refresh(): void { + if (this._component && this._component.refresh) { + this._component.refresh(); + } + } + + private loadWidget(): void { + if (Object.keys(this._config.widget).length !== 1) { + error('Exactly 1 widget must be defined per space'); + return; + } + let key = Object.keys(this._config.widget)[0]; + let selector = this.getOrCreateSelector(key); + if (selector === undefined) { + error('Could not find selector', key); + return; + } + + let componentFactory = this._componentFactoryResolver.resolveComponentFactory(selector); + + let viewContainerRef = this.componentHost.viewContainerRef; + viewContainerRef.clear(); + + let injector = ReflectiveInjector.resolveAndCreate([{ provide: WIDGET_CONFIG, useValue: this._config }], this._injector); + let componentRef: ComponentRef; + try { + componentRef = viewContainerRef.createComponent(componentFactory, 0, injector); + this._component = componentRef.instance; + let actions = componentRef.instance.actions; + if (componentRef.instance.refresh) { + actions.push(this._bootstrap.instantiationService.createInstance(ACTIONS.RefreshWidgetAction, ACTIONS.RefreshWidgetAction.ID, ACTIONS.RefreshWidgetAction.LABEL, componentRef.instance.refresh)); + } + if (actions !== undefined && actions.length > 0) { + this._actions = actions; + this._changeref.detectChanges(); + } + } catch (e) { + error('Error rendering widget', key, e); + return; + } + let el = componentRef.location.nativeElement; + + // set widget styles to conform to its box + el.style.overflow = 'hidden'; + el.style.flex = '1 1 auto'; + el.style.position = 'relative'; + } + + /** + * Attempts to get the selector for a given key, and if none is defined tries + * to load it from the widget registry and configure as needed + * + * @private + * @param {string} key + * @returns {Type} + * @memberof DashboardWidgetWrapper + */ + private getOrCreateSelector(key: string): Type { + let selector = componentMap[key]; + if (selector === undefined) { + // Load the widget from the registry + let widgetRegistry = Registry.as(Extensions.InsightContribution); + let insightConfig = widgetRegistry.getRegisteredExtensionInsights(key); + if (insightConfig === undefined) { + return undefined; + } + // Save the widget for future use + selector = componentMap['insights-widget']; + this._config.widget['insights-widget'] = insightConfig; + } + return selector; + } + + //tslint:disable-next-line + private onActionsClick(e: any) { + let anchor = { x: e.pageX + 1, y: e.pageY }; + this._bootstrap.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.as(this._actions), + getActionsContext: () => this._component.actionsContext + }); + } + + private updateTheme(theme: IColorTheme): void { + let el = this._ref.nativeElement; + let headerEl: HTMLElement = this.header.nativeElement; + let borderColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true); + let backgroundColor = theme.getColor(colors.editorBackground, true); + let foregroundColor = theme.getColor(themeColors.SIDE_BAR_FOREGROUND, true); + // TODO: highContrastBorder does not exist, how to handle? + let border = theme.getColor(colors.contrastBorder, true); + + if (this._config.background_color) { + backgroundColor = theme.getColor(this._config.background_color); + } + + if (this._config.border === 'none') { + borderColor = undefined; + } + + if (backgroundColor) { + el.style.backgroundColor = backgroundColor.toString(); + } + + if (foregroundColor) { + el.style.color = foregroundColor.toString(); + } + + let borderString = undefined; + if (border) { + borderString = border.toString(); + el.style.borderColor = borderString; + el.style.borderWidth = '1px'; + el.style.borderStyle = 'solid'; + } else if (borderColor) { + borderString = borderColor.toString(); + el.style.border = '3px solid ' + borderColor.toString(); + } else { + el.style.border = 'none'; + } + + if (borderString) { + headerEl.style.backgroundColor = borderString; + } else { + headerEl.style.backgroundColor = ''; + } + + if (this._config.fontSize) { + headerEl.style.fontSize = this._config.fontSize; + } + if (this._config.fontWeight) { + headerEl.style.fontWeight = this._config.fontWeight; + } + if (this._config.padding) { + headerEl.style.padding = this._config.padding; + } + } +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/common/interfaces.ts b/src/sql/parts/dashboard/common/interfaces.ts new file mode 100644 index 0000000000..fa6acf998e --- /dev/null +++ b/src/sql/parts/dashboard/common/interfaces.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. + *--------------------------------------------------------------------------------------------*/ + +export enum Conditional { + 'equals', + 'notEquals', + 'greaterThanOrEquals', + 'greaterThan', + 'lessThanOrEquals', + 'lessThan', + 'always' +}; \ No newline at end of file diff --git a/src/sql/parts/dashboard/dashboard.component.html b/src/sql/parts/dashboard/dashboard.component.html new file mode 100644 index 0000000000..9e637d39af --- /dev/null +++ b/src/sql/parts/dashboard/dashboard.component.html @@ -0,0 +1,12 @@ + +
+
+ +
+ +
\ No newline at end of file diff --git a/src/sql/parts/dashboard/dashboard.component.ts b/src/sql/parts/dashboard/dashboard.component.ts new file mode 100644 index 0000000000..e1bd748059 --- /dev/null +++ b/src/sql/parts/dashboard/dashboard.component.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; + +import { DashboardServiceInterface } from './services/dashboardServiceInterface.service'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import * as Utils from 'sql/parts/connection/common/utils'; + +import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import * as themeColors from 'vs/workbench/common/theme'; + +export const DASHBOARD_SELECTOR: string = 'dashboard-component'; + +@Component({ + selector: DASHBOARD_SELECTOR, + templateUrl: decodeURI(require.toUrl('./dashboard.component.html')) +}) +export class DashboardComponent implements OnInit, OnDestroy { + private _subs: Array = new Array(); + @ViewChild('header', { read: ElementRef }) private header: ElementRef; + + constructor( + @Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrapService: DashboardServiceInterface, + @Inject(forwardRef(() => Router)) private _router: Router, + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef + ) { } + + ngOnInit() { + let self = this; + self._subs.push(self._bootstrapService.themeService.onDidColorThemeChange(e => self.updateTheme(e))); + self.updateTheme(self._bootstrapService.themeService.getColorTheme()); + let profile: IConnectionProfile = self._bootstrapService.getOriginalConnectionProfile(); + if (profile && (!profile.databaseName || Utils.isMaster(profile))) { + // Route to the server page as this is the default database + self._router.navigate(['server-dashboard']); + } + } + + ngOnDestroy() { + this._subs.forEach((value) => { + value.dispose(); + }); + } + + private updateTheme(theme: IColorTheme): void { + let headerEl = this.header.nativeElement; + headerEl.style.borderBottomColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString(); + headerEl.style.borderBottomWidth = '1px'; + headerEl.style.borderBottomStyle = 'solid'; + + } + +} diff --git a/src/sql/parts/dashboard/dashboard.module.ts b/src/sql/parts/dashboard/dashboard.module.ts new file mode 100644 index 0000000000..e38be564fc --- /dev/null +++ b/src/sql/parts/dashboard/dashboard.module.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Inject, NgModule, forwardRef, ApplicationRef, ComponentFactoryResolver } from '@angular/core'; +import { CommonModule, APP_BASE_HREF } from '@angular/common'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule, Routes, UrlSerializer } from '@angular/router'; +import { FormsModule } from '@angular/forms'; +import { NgGridModule } from 'angular2-grid'; +import { ChartsModule } from 'ng2-charts/ng2-charts'; + +import CustomUrlSerializer from 'sql/common/urlSerializer'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry'; + +import { Registry } from 'vs/platform/registry/common/platform'; + +/* Services */ +import { BreadcrumbService } from 'sql/parts/dashboard/services/breadcrumb.service'; +import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; + +/* Directives */ +import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive'; + +/* Base Components */ +import { DashboardComponent, DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component'; +import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component'; +import { BreadcrumbComponent } from 'sql/base/browser/ui/breadcrumb/breadcrumb.component'; +import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces'; +let baseComponents = [DashboardComponent, DashboardWidgetWrapper, ComponentHostDirective, BreadcrumbComponent]; + +/* Pages */ +import { ServerDashboardPage } from 'sql/parts/dashboard/pages/serverDashboardPage.component'; +import { DatabaseDashboardPage } from 'sql/parts/dashboard/pages/databaseDashboardPage.component'; +let pageComponents = [ServerDashboardPage, DatabaseDashboardPage]; + +/* Widget Components */ +import { PropertiesWidgetComponent } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component'; +import { ExplorerWidget } from 'sql/parts/dashboard/widgets/explorer/explorerWidget.component'; +import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.component'; +import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component'; +let widgetComponents = [PropertiesWidgetComponent, ExplorerWidget, TasksWidget, InsightsWidget]; + +/* Insights */ +let insightComponents = Registry.as(Extensions.InsightContribution).getAllCtors(); + +// Setup routes for various child components +const appRoutes: Routes = [ + { path: 'database-dashboard', component: DatabaseDashboardPage }, + { path: 'server-dashboard', component: ServerDashboardPage }, + { + path: '', + redirectTo: 'database-dashboard', + pathMatch: 'full' + }, + { path: '**', component: DatabaseDashboardPage } +]; + +// Connection Dashboard main angular module +@NgModule({ + declarations: [ + ...baseComponents, + ...pageComponents, + ...widgetComponents, + ...insightComponents + ], + // also for widgets + entryComponents: [ + DashboardComponent, + ...widgetComponents, + ...insightComponents + ], + imports: [ + CommonModule, + BrowserModule, + FormsModule, + NgGridModule, + ChartsModule, + RouterModule.forRoot(appRoutes) + ], + providers: [ + { provide: APP_BASE_HREF, useValue: '/' }, + { provide: IBreadcrumbService, useClass: BreadcrumbService }, + DashboardServiceInterface, + { provide: UrlSerializer, useClass: CustomUrlSerializer } + ] +}) +export class DashboardModule { + + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService, + @Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface + ) { + } + + ngDoBootstrap(appRef: ApplicationRef) { + const factory = this._resolver.resolveComponentFactory(DashboardComponent); + const uniqueSelector: string = this._bootstrapService.getUniqueSelector(DASHBOARD_SELECTOR); + this._bootstrap.selector = uniqueSelector; + (factory).factory.selector = uniqueSelector; + appRef.bootstrap(factory); + } +} diff --git a/src/sql/parts/dashboard/dashboardConfig.contribution.ts b/src/sql/parts/dashboard/dashboardConfig.contribution.ts new file mode 100644 index 0000000000..3cd639f173 --- /dev/null +++ b/src/sql/parts/dashboard/dashboardConfig.contribution.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, Extensions, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { DATABASE_DASHBOARD_SETTING, DATABASE_DASHBOARD_PROPERTIES, databaseDashboardSettingSchema, databaseDashboardPropertiesSchema } from 'sql/parts/dashboard/pages/databaseDashboardPage.contribution'; +import { SERVER_DASHBOARD_SETTING, SERVER_DASHBOARD_PROPERTIES, serverDashboardSettingSchema, serverDashboardPropertiesSchema } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution'; + +const configurationRegistry = Registry.as(Extensions.Configuration); +const dashboardConfig: IConfigurationNode = { + id: 'Dashboard', + type: 'object', + properties: { + [DATABASE_DASHBOARD_PROPERTIES]: databaseDashboardPropertiesSchema, + [SERVER_DASHBOARD_PROPERTIES]: serverDashboardPropertiesSchema, + [DATABASE_DASHBOARD_SETTING]: databaseDashboardSettingSchema, + [SERVER_DASHBOARD_SETTING]: serverDashboardSettingSchema + } +}; + +configurationRegistry.registerConfiguration(dashboardConfig); diff --git a/src/sql/parts/dashboard/dashboardEditor.ts b/src/sql/parts/dashboard/dashboardEditor.ts new file mode 100644 index 0000000000..a13f26d452 --- /dev/null +++ b/src/sql/parts/dashboard/dashboardEditor.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Dimension, Builder, $ } from 'vs/base/browser/builder'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; + +import { DashboardInput } from './dashboardInput'; +import { DashboardModule } from './dashboard.module'; +import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import { DashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component'; + +export class DashboardEditor extends BaseEditor { + + public static ID: string = 'workbench.editor.connectiondashboard'; + private _dashboardContainer: HTMLElement; + protected _input: DashboardInput; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IWorkbenchThemeService themeService: IWorkbenchThemeService, + @IInstantiationService private instantiationService: IInstantiationService, + @IBootstrapService private _bootstrapService: IBootstrapService + ) { + super(DashboardEditor.ID, telemetryService, themeService); + } + + public get input(): DashboardInput { + return this._input; + } + + /** + * Called to create the editor in the parent builder. + */ + public createEditor(parent: Builder): void { + } + + /** + * Sets focus on this editor. Specifically, it sets the focus on the hosted text editor. + */ + public focus(): void { + } + + /** + * Updates the internal variable keeping track of the editor's size, and re-calculates the sash position. + * To be called when the container of this editor changes size. + */ + public layout(dimension: Dimension): void { + } + + public setInput(input: DashboardInput, options: EditorOptions): TPromise { + if (this.input && this.input.matches(input)) { + return TPromise.as(undefined); + } + + const parentElement = this.getContainer().getHTMLElement(); + + super.setInput(input, options); + + $(parentElement).empty(); + + if (!input.hasBootstrapped) { + let container = DOM.$('.dashboardEditor'); + container.style.height = '100%'; + this._dashboardContainer = DOM.append(parentElement, container); + this.input.container = this._dashboardContainer; + return TPromise.wrap(input.initializedPromise.then(() => this.bootstrapAngular(input))); + } else { + this._dashboardContainer = DOM.append(parentElement, this.input.container); + return TPromise.as(null); + } + } + + /** + * Load the angular components and record for this input that we have done so + */ + private bootstrapAngular(input: DashboardInput): void { + // Get the bootstrap params and perform the bootstrap + let params: DashboardComponentParams = { + connection: input.connectionProfile, + ownerUri: input.uri + }; + + input.hasBootstrapped = true; + + let uniqueSelector = this._bootstrapService.bootstrap( + DashboardModule, + this._dashboardContainer, + DASHBOARD_SELECTOR, + params, + input); + input.setUniqueSelector(uniqueSelector); + } + + public dispose(): void { + super.dispose(); + } +} + +ModesRegistry.registerLanguage({ + extensions: ['.dashboard'], + id: 'dashboard', +}); \ No newline at end of file diff --git a/src/sql/parts/dashboard/dashboardInput.ts b/src/sql/parts/dashboard/dashboardInput.ts new file mode 100644 index 0000000000..a2316ca1b4 --- /dev/null +++ b/src/sql/parts/dashboard/dashboardInput.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import URI from 'vs/base/common/uri'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; + +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; + +export class DashboardInput extends EditorInput { + + private _uri: string; + public static ID: string = 'workbench.editorinputs.connectiondashboardinputs'; + public static SCHEMA: string = 'sqldashboard'; + + private _initializedPromise: Thenable; + private _onConnectionChanged: IDisposable; + + public get initializedPromise(): Thenable { + return this._initializedPromise; + } + + private _uniqueSelector: string; + + public hasBootstrapped = false; + // Holds the HTML content for the editor when the editor discards this input and loads another + private _parentContainer: HTMLElement; + + constructor( + _connectionProfile: IConnectionProfile, + @IConnectionManagementService private _connectionService: IConnectionManagementService, + @IModeService modeService: IModeService, + @IModelService model: IModelService + ) { + super(); + // TODO; possible refactor + // basically this is mimicing creating a "model" (the backing model for text for editors) + // for dashboard, even though there is no backing text. We need this so that we can + // tell the icon theme services that we are a dashboard resource, therefore loading the correct icon + + // vscode has a comment that Mode's will eventually be removed (not sure the state of this comment) + // so this might be able to be undone when that happens + if (!model.getModel(this.getResource())) { + model.createModel('', modeService.getMode('dashboard'), this.getResource()); + } + this._initializedPromise = _connectionService.connectIfNotConnected(_connectionProfile, 'dashboard').then( + u => { + this._uri = u; + let info = this._connectionService.getConnectionInfo(u); + if (info) { + this._onConnectionChanged = this._connectionService.onConnectionChanged(e => { + if (e.connectionUri === u) { + this._onDidChangeLabel.fire(); + } + }); + } + } + ); + } + + public setUniqueSelector(uniqueSelector: string): void { + this._uniqueSelector = uniqueSelector; + } + + public getTypeId(): string { + return UntitledEditorInput.ID; + } + + public getResource(): URI { + return URI.from({ + scheme: 'dashboard', + path: '.dashboard' + }); + } + + public getName(): string { + if (!this.connectionProfile) { + return ''; + } + + let name = this.connectionProfile.serverName; + if (this.connectionProfile.databaseName + && !this.isMasterMssql()) { + // Only add DB name if this is a non-default, non-master connection + name = name + ':' + this.connectionProfile.databaseName; + } + return name; + } + + private isMasterMssql(): boolean { + return this.connectionProfile.providerName.toLowerCase() === 'mssql' + && this.connectionProfile.databaseName.toLowerCase() === 'master'; + } + + public get uri(): string { + return this._uri; + } + + public dispose(): void { + this._disposeContainer(); + if (this._onConnectionChanged) { + this._onConnectionChanged.dispose(); + } + this._connectionService.disconnect(this._uri); + super.dispose(); + } + + private _disposeContainer() { + if (!this._parentContainer) { + return; + } + + let parentNode = this._parentContainer.parentNode; + if (parentNode) { + parentNode.removeChild(this._parentContainer); + this._parentContainer = null; + } + } + + set container(container: HTMLElement) { + this._disposeContainer(); + this._parentContainer = container; + } + + get container(): HTMLElement { + return this._parentContainer; + } + + public supportsSplitEditor(): boolean { + return false; + } + + public get connectionProfile(): IConnectionProfile { + return this._connectionService.getConnectionProfile(this._uri); + } + + public resolve(refresh?: boolean): TPromise { + return undefined; + } + + public get hasInitialized(): boolean { + return !!this._uniqueSelector; + } + + public get uniqueSelector(): string { + return this._uniqueSelector; + } + + public matches(otherinput: any): boolean { + return otherinput instanceof DashboardInput + && DashboardInput.profileMatches(this.connectionProfile, otherinput.connectionProfile); + } + + // similar to the default profile match but without databasename + public static profileMatches(profile1: IConnectionProfile, profile2: IConnectionProfile): boolean { + return profile1 && profile2 + && profile1.providerName === profile2.providerName + && profile1.serverName === profile2.serverName + && profile1.userName === profile2.userName + && profile1.authenticationType === profile2.authenticationType + && profile1.groupFullName === profile2.groupFullName; + } +} diff --git a/src/sql/parts/dashboard/pages/databaseDashboardPage.component.ts b/src/sql/parts/dashboard/pages/databaseDashboardPage.component.ts new file mode 100644 index 0000000000..a65cfd7260 --- /dev/null +++ b/src/sql/parts/dashboard/pages/databaseDashboardPage.component.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { OnInit, Inject, forwardRef, ChangeDetectorRef, OnDestroy } from '@angular/core'; + +import { DashboardPage } from 'sql/parts/dashboard/common/dashboardPage.component'; +import { BreadcrumbClass } from 'sql/parts/dashboard/services/breadcrumb.service'; +import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces'; +import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; +import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget'; + +import * as colors from 'vs/platform/theme/common/colorRegistry'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as nls from 'vs/nls'; + +export class DatabaseDashboardPage extends DashboardPage implements OnInit, OnDestroy { + protected propertiesWidget: WidgetConfig = { + name: nls.localize('databasePageName', 'DATABASE DASHBOARD'), + widget: { + 'properties-widget': undefined + }, + context: 'database', + background_color: colors.editorBackground, + border: 'none', + fontSize: '14px', + fontWeight: '200', + padding: '5px 0 0 0', + provider: undefined, + edition: undefined + }; + + protected readonly context = 'database'; + private _dispose: IDisposable[] = []; + + constructor( + @Inject(forwardRef(() => IBreadcrumbService)) private _breadcrumbService: IBreadcrumbService, + @Inject(forwardRef(() => DashboardServiceInterface)) dashboardService: DashboardServiceInterface, + @Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef + ) { + super(dashboardService); + this._dispose.push(dashboardService.onUpdatePage(() => { + this.refresh(true); + this._cd.detectChanges(); + })); + this.init(); + } + + ngOnInit() { + this._breadcrumbService.setBreadcrumbs(BreadcrumbClass.DatabasePage); + this.baseInit(); + } + + ngOnDestroy() { + this._dispose = dispose(this._dispose); + this.baseDestroy(); + } +} diff --git a/src/sql/parts/dashboard/pages/databaseDashboardPage.contribution.ts b/src/sql/parts/dashboard/pages/databaseDashboardPage.contribution.ts new file mode 100644 index 0000000000..a529a80042 --- /dev/null +++ b/src/sql/parts/dashboard/pages/databaseDashboardPage.contribution.ts @@ -0,0 +1,158 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Registry } from 'vs/platform/registry/common/platform'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { Extensions, IDashboardWidgetRegistry } from 'sql/platform/dashboard/common/widgetRegistry'; +import * as nls from 'vs/nls'; + +let widgetRegistry = Registry.as(Extensions.DashboardWidgetContribution); + +export const databaseDashboardPropertiesSchema: IJSONSchema = { + description: nls.localize('dashboardDatabaseProperties', 'Enable or disable the properties widget'), + default: true, + oneOf: [ + { type: 'boolean' }, + { + type: 'array', + items: { + type: 'object', + properties: { + provider: { + type: 'string' + }, + edition: { + type: 'number' + }, + properties: { + description: nls.localize('dashboard.databaseproperties', 'Property values to show'), + type: 'array', + items: { + type: 'object', + properties: { + displayName: { + type: 'string', + description: nls.localize('dashboard.databaseproperties.displayName', 'Display name of the property') + }, + value: { + type: 'string', + description: nls.localize('dashboard.databaseproperties.value', 'Value in the Database Info Object') + }, + ignore: { + type: 'array', + description: nls.localize('dashboard.databaseproperties.ignore', 'Specify specific values to ignore'), + items: 'string' + } + } + }, + default: [ + { + displayName: nls.localize('recoveryModel', 'Recovery Model'), + value: 'recoveryModel' + }, + { + displayName: nls.localize('lastDatabaseBackup', 'Last Database Backup'), + value: 'lastBackupDate', + ignore: [ + '1/1/0001 12:00:00 AM' + ] + }, + { + displayName: nls.localize('lastLogBackup', 'Last Log Backup'), + value: 'lastLogBackupDate', + ignore: [ + '1/1/0001 12:00:00 AM' + ] + }, + { + displayName: nls.localize('compatibilityLevel', 'Compatibility Level'), + value: 'compatibilityLevel' + }, + { + displayName: nls.localize('owner', 'Owner'), + value: 'owner' + } + ] + } + } + } + } + ] +}; + +export const databaseDashboardSettingSchema: IJSONSchema = { + type: ['array'], + description: nls.localize('dashboardDatabase', 'Customizes the database dashboard page'), + items: { + type: 'object', + properties: { + name: { + type: 'string' + }, + icon: { + type: 'string' + }, + provider: { + anyOf: [ + 'string', + { + type: 'array', + items: 'string' + } + ] + }, + edition: { + anyOf: [ + 'number', + { + type: 'array', + items: 'number' + } + ] + }, + gridItemConfig: { + type: 'object', + properties: { + sizex: { + type: 'number' + }, + sizey: { + type: 'number' + } + } + }, + widget: { + type: 'object', + properties: widgetRegistry.databaseWidgetSchema.properties, + minItems: 1, + maxItems: 1 + } + } + }, + default: [ + { + name: 'Tasks', + gridItemConfig: { + sizex: 1, + sizey: 1 + }, + widget: { + 'tasks-widget': {} + } + }, + { + gridItemConfig: { + sizex: 1, + sizey: 2 + }, + widget: { + 'explorer-widget': {} + } + } + ] +}; + +export const DATABASE_DASHBOARD_SETTING = 'dashboard.database.widgets'; +export const DATABASE_DASHBOARD_PROPERTIES = 'dashboard.database.properties'; \ No newline at end of file diff --git a/src/sql/parts/dashboard/pages/serverDashboardPage.component.ts b/src/sql/parts/dashboard/pages/serverDashboardPage.component.ts new file mode 100644 index 0000000000..4ffb69ea4b --- /dev/null +++ b/src/sql/parts/dashboard/pages/serverDashboardPage.component.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { OnInit, Inject, forwardRef, OnDestroy, ChangeDetectorRef } from '@angular/core'; + +import { DashboardPage } from 'sql/parts/dashboard/common/dashboardPage.component'; +import { BreadcrumbClass } from 'sql/parts/dashboard/services/breadcrumb.service'; +import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces'; +import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget'; +import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; + +import * as colors from 'vs/platform/theme/common/colorRegistry'; +import * as nls from 'vs/nls'; + +export class ServerDashboardPage extends DashboardPage implements OnInit, OnDestroy { + protected propertiesWidget: WidgetConfig = { + name: nls.localize('serverPageName', 'SERVER DASHBOARD'), + widget: { + 'properties-widget': undefined + }, + context: 'server', + background_color: colors.editorBackground, + border: 'none', + fontSize: '14px', + fontWeight: '200', + padding: '5px 0 0 0', + provider: undefined, + edition: undefined + }; + + protected readonly context = 'server'; + + constructor( + @Inject(forwardRef(() => IBreadcrumbService)) private breadcrumbService: IBreadcrumbService, + @Inject(forwardRef(() => DashboardServiceInterface)) dashboardService: DashboardServiceInterface, + @Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef + ) { + super(dashboardService); + // revert back to default database + this.dashboardService.connectionManagementService.changeDatabase('master').then(() => { + this.dashboardService.connectionManagementService.connectionInfo.connectionProfile.databaseName = undefined; + this.init(); + cd.detectChanges(); + }); + } + + ngOnInit() { + this.breadcrumbService.setBreadcrumbs(BreadcrumbClass.ServerPage); + this.dashboardService.connectionManagementService.connectionInfo.connectionProfile.databaseName = null; + this.baseInit(); + } + + ngOnDestroy() { + this.baseDestroy(); + } +} diff --git a/src/sql/parts/dashboard/pages/serverDashboardPage.contribution.ts b/src/sql/parts/dashboard/pages/serverDashboardPage.contribution.ts new file mode 100644 index 0000000000..01d72c1795 --- /dev/null +++ b/src/sql/parts/dashboard/pages/serverDashboardPage.contribution.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Registry } from 'vs/platform/registry/common/platform'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { Extensions, IDashboardWidgetRegistry } from 'sql/platform/dashboard/common/widgetRegistry'; +import * as nls from 'vs/nls'; + +let widgetRegistry = Registry.as(Extensions.DashboardWidgetContribution); + +export interface IPropertiesConfig { + edition: number | Array; + provider: string | Array; + properties: { + displayName: string; + value: string + }[]; +} + +export const serverDashboardPropertiesSchema: IJSONSchema = { + description: nls.localize('dashboardServerProperties', 'Enable or disable the properties widget'), + default: true, + oneOf: [ + { type: 'boolean' }, + { + type: 'object', + properties: { + provider: { + type: 'string' + }, + edition: { + type: 'number' + }, + properties: { + description: nls.localize('dashboard.serverproperties', 'Property values to show'), + type: 'array', + items: { + type: 'object', + properties: { + displayName: { + type: 'string', + description: nls.localize('dashboard.serverproperties.displayName', 'Display name of the property') + }, + value: { + type: 'string', + description: nls.localize('dashboard.serverproperties.value', 'Value in the Server Info Object') + } + } + }, + default: [ + { + displayName: nls.localize('version', 'Version'), + value: 'serverVersion' + }, + { + displayName: nls.localize('edition', 'Edition'), + value: 'serverEdition' + }, + { + displayName: nls.localize('computerName', 'Computer Name'), + value: 'machineName' + }, + { + displayName: nls.localize('osVersion', 'OS Version'), + value: 'osVersion' + } + ] + } + } + } + ] +}; + +let defaultVal = [ + { + name: 'Tasks', + widget: { + 'tasks-widget': {} + }, + gridItemConfig: { + sizex: 1, + sizey: 1 + } + }, + { + gridItemConfig: { + sizex: 1, + sizey: 2 + }, + widget: { + 'explorer-widget': {} + } + }, + { + widget: { + 'backup-history-server-insight': { + cacheId: '0c7cba8b-c87a-4bcc-ae54-2f40a5503a90' + } + } + }, + { + widget: { + 'all-database-size-server-insight': { + cacheId: '1d7cba8b-c87a-4bcc-ae54-2f40a5503a90' + } + } + } +]; + +export const serverDashboardSettingSchema: IJSONSchema = { + type: ['array'], + description: nls.localize('dashboardServer', 'Customizes the server dashboard page'), + items: { + type: 'object', + properties: { + name: { + type: 'string' + }, + icon: { + type: 'string' + }, + provider: { + anyOf: [ + 'string', + { + type: 'array', + items: 'string' + } + ] + }, + edition: { + anyOf: [ + 'number', + { + type: 'array', + items: 'number' + } + ] + }, + gridItemConfig: { + type: 'object', + properties: { + sizex: { + type: 'number' + }, + sizey: { + type: 'number' + } + } + }, + widget: { + type: 'object', + properties: widgetRegistry.serverWidgetSchema.properties, + maxItems: 1 + } + } + }, + default: defaultVal +}; + +export const SERVER_DASHBOARD_SETTING = 'dashboard.server.widgets'; +export const SERVER_DASHBOARD_PROPERTIES = 'dashboard.server.properties'; \ No newline at end of file diff --git a/src/sql/parts/dashboard/services/breadcrumb.service.ts b/src/sql/parts/dashboard/services/breadcrumb.service.ts new file mode 100644 index 0000000000..dcb8de6e0a --- /dev/null +++ b/src/sql/parts/dashboard/services/breadcrumb.service.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Injectable, forwardRef, Inject, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; + +import { DashboardServiceInterface } from './dashboardServiceInterface.service'; +import { MenuItem, IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as nls from 'vs/nls'; + +export enum BreadcrumbClass { + DatabasePage, + ServerPage +}; + +@Injectable() +export class BreadcrumbService implements IBreadcrumbService, OnDestroy { + public breadcrumbItem: Subject; + private itemBreadcrums: MenuItem[]; + private _disposables: IDisposable[] = []; + private _currentPage: BreadcrumbClass; + + constructor( @Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface) { + _bootstrap.onUpdatePage(() => { + this.setBreadcrumbs(this._currentPage); + }); + this.breadcrumbItem = new Subject(); + } + + public setBreadcrumbs(page: BreadcrumbClass) { + this._currentPage = page; + this.itemBreadcrums = []; + let refList: MenuItem[] = this.getBreadcrumbsLink(page); + this.breadcrumbItem.next(refList); + } + + private getBreadcrumbsLink(page: BreadcrumbClass): MenuItem[] { + this.itemBreadcrums = []; + let profile = this._bootstrap.connectionManagementService.connectionInfo.connectionProfile; + this.itemBreadcrums.push({ label: nls.localize('homeCrumb', 'Home')}); + switch (page) { + case BreadcrumbClass.DatabasePage: + this.itemBreadcrums.push(this.getServerBreadcrumb(profile)); + this.itemBreadcrums.push(this.getDbBreadcrumb(profile)); + break; + case BreadcrumbClass.ServerPage: + this.itemBreadcrums.push(this.getServerBreadcrumb(profile)); + break; + default: + this.itemBreadcrums = []; + } + return this.itemBreadcrums; + } + + private getServerBreadcrumb(profile: ConnectionProfile): MenuItem { + return { label: profile.serverName, routerLink: ['server-dashboard'] }; + } + + private getDbBreadcrumb(profile: ConnectionProfile): MenuItem { + return { + label: profile.databaseName ? profile.databaseName : 'database-name', + routerLink: ['database-dashboard'] + }; + } + + ngOnDestroy() { + this._disposables = dispose(this._disposables); + } +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/services/dashboardServiceInterface.service.ts b/src/sql/parts/dashboard/services/dashboardServiceInterface.service.ts new file mode 100644 index 0000000000..ed3e20b69d --- /dev/null +++ b/src/sql/parts/dashboard/services/dashboardServiceInterface.service.ts @@ -0,0 +1,274 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Node Modules */ +import { Injectable, Inject, forwardRef, OnDestroy } from '@angular/core'; +import { Router } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; + +/* SQL imports */ +import { DashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { IMetadataService } from 'sql/services/metadata/metadataService'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import { IAdminService } from 'sql/parts/admin/common/adminService'; +import { IQueryManagementService } from 'sql/parts/query/common/queryManagement'; +import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils'; +import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget'; +import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces'; +import { IPropertiesConfig } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { AngularEventType } from 'sql/services/angularEventing/angularEventingService'; + +import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'data'; + +/* VS imports */ +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import Event, { Emitter } from 'vs/base/common/event'; +import Severity from 'vs/base/common/severity'; +import * as nls from 'vs/nls'; + +const DASHBOARD_SETTINGS = 'dashboard'; + +/* Wrapper for a metadata service that contains the uri string to use on each request */ +export class SingleConnectionMetadataService { + + constructor( + private _metadataService: IMetadataService, + private _uri: string + ) { } + + get metadata(): Observable { + return Observable.fromPromise(this._metadataService.getMetadata(this._uri)); + } + + get databaseNames(): Observable { + return Observable.fromPromise(this._metadataService.getDatabaseNames(this._uri)); + } +} + +/* Wrapper for a connection service that contains the uri string to use on each request */ +export class SingleConnectionManagementService { + + constructor( + private _connectionService: IConnectionManagementService, + private _uri: string + ) { } + + public changeDatabase(name: string): Thenable { + return this._connectionService.changeDatabase(this._uri, name); + } + + public get connectionInfo(): ConnectionManagementInfo { + return this._connectionService.getConnectionInfo(this._uri); + } +} + +export class SingleAdminService { + + constructor( + private _adminService: IAdminService, + private _uri: string + ) { } + + public get databaseInfo(): Observable { + return Observable.fromPromise(this._adminService.getDatabaseInfo(this._uri)); + } +} + +export class SingleQueryManagementService { + constructor( + private _queryManagementService: IQueryManagementService, + private _uri: string + ) { } + + public runQueryAndReturn(queryString: string): Thenable { + return this._queryManagementService.runQueryAndReturn(this._uri, queryString); + } +} + +/* + Providers a interface between a dashboard interface and the rest of carbon. + Stores the uri and unique selector of a dashboard instance and uses that + whenever a call to a carbon service needs this information, so that the widgets + don't need to be aware of the uri or selector. Simplifies the initialization and + usage of a widget. +*/ +@Injectable() +export class DashboardServiceInterface implements OnDestroy { + private _uniqueSelector: string; + private _uri: string; + private _bootstrapParams: DashboardComponentParams; + private _disposables: IDisposable[] = []; + + /* Services */ + private _metadataService: SingleConnectionMetadataService; + private _connectionManagementService: SingleConnectionManagementService; + private _themeService: IWorkbenchThemeService; + private _contextMenuService: IContextMenuService; + private _instantiationService: IInstantiationService; + private _adminService: SingleAdminService; + private _queryManagementService: SingleQueryManagementService; + private _configService: IConfigurationService; + private _insightsDialogService: IInsightsDialogService; + private _contextViewService: IContextViewService; + private _messageService: IMessageService; + private _workspaceContextService: IWorkspaceContextService; + private _storageService: IStorageService; + private _capabilitiesService: ICapabilitiesService; + + private _updatePage = new Emitter(); + public readonly onUpdatePage: Event = this._updatePage.event; + + constructor( + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService, + @Inject(forwardRef(() => Router)) private _router: Router, + ) { + this._themeService = this._bootstrapService.themeService; + this._contextMenuService = this._bootstrapService.contextMenuService; + this._instantiationService = this._bootstrapService.instantiationService; + this._configService = this._bootstrapService.configurationService; + this._insightsDialogService = this._bootstrapService.insightsDialogService; + this._contextViewService = this._bootstrapService.contextViewService; + this._messageService = this._bootstrapService.messageService; + this._workspaceContextService = this._bootstrapService.workspaceContextService; + this._storageService = this._bootstrapService.storageService; + this._capabilitiesService = this._bootstrapService.capabilitiesService; + } + + ngOnDestroy() { + this._disposables.forEach((item) => item.dispose()); + } + + public get messageService(): IMessageService { + return this._messageService; + } + + public get metadataService(): SingleConnectionMetadataService { + return this._metadataService; + } + + public get connectionManagementService(): SingleConnectionManagementService { + return this._connectionManagementService; + } + + public get themeService(): IWorkbenchThemeService { + return this._themeService; + } + + public get contextMenuService(): IContextMenuService { + return this._contextMenuService; + } + + public get instantiationService(): IInstantiationService { + return this._instantiationService; + } + + public get adminService(): SingleAdminService { + return this._adminService; + } + + public get queryManagementService(): SingleQueryManagementService { + return this._queryManagementService; + } + + public get contextViewService(): IContextViewService { + return this._contextViewService; + } + + public get workspaceContextService(): IWorkspaceContextService { + return this._workspaceContextService; + } + + public get storageService(): IStorageService { + return this._storageService; + } + + public get CapabilitiesService(): ICapabilitiesService { + return this._capabilitiesService; + } + + /** + * Set the selector for this dashboard instance, should only be set once + */ + public set selector(selector: string) { + this._uniqueSelector = selector; + this._getbootstrapParams(); + } + + private _getbootstrapParams(): void { + this._bootstrapParams = this._bootstrapService.getBootstrapParams(this._uniqueSelector); + this.uri = this._bootstrapParams.ownerUri; + } + + /** + * Set the uri for this dashboard instance, should only be set once + * Inits all the services that depend on knowing a uri + */ + private set uri(uri: string) { + this._uri = uri; + this._metadataService = new SingleConnectionMetadataService(this._bootstrapService.metadataService, this._uri); + this._connectionManagementService = new SingleConnectionManagementService(this._bootstrapService.connectionManagementService, this._uri); + this._adminService = new SingleAdminService(this._bootstrapService.adminService, this._uri); + this._queryManagementService = new SingleQueryManagementService(this._bootstrapService.queryManagementService, this._uri); + this._disposables.push(toDisposableSubscription(this._bootstrapService.angularEventingService.onAngularEvent(this._uri, (event) => this.handleDashboardEvent(event)))); + } + + /** + * Gets the underlying Uri for dashboard + * In general don't use this, use specific services instances exposed publically + */ + public getUnderlyingUri(): string { + return this._uri; + } + + public getOriginalConnectionProfile(): IConnectionProfile { + return this._bootstrapParams.connection; + } + + /** + * Get settings for given string + * @param type string of setting to get from dashboard settings; i.e dashboard.{type} + */ + public getSettings(type: string): { widgets: Array, properties: boolean | IPropertiesConfig[] } { + let config = this._configService.getConfiguration(DASHBOARD_SETTINGS); + return config[type]; + } + + private handleDashboardEvent(event: AngularEventType): void { + switch (event) { + case AngularEventType.NAV_DATABASE: + this.connectionManagementService.changeDatabase(this.connectionManagementService.connectionInfo.connectionProfile.databaseName).then( + result => { + if (result) { + if (this._router.url === '/database-dashboard') { + this._updatePage.fire(); + } else { + this._router.navigate(['database-dashboard']); + } + } else { + this.messageService.show(Severity.Error, nls.localize('dashboard.changeDatabaseFailure', "Failed to change database")); + } + }, + () => { + this.messageService.show(Severity.Error, nls.localize('dashboard.changeDatabaseFailure', "Failed to change database")); + } + ); + break; + case AngularEventType.NAV_SERVER: + this._router.navigate(['server-dashboard']); + break; + } + } +} diff --git a/src/sql/parts/dashboard/widgets/explorer/explorerActions.ts b/src/sql/parts/dashboard/widgets/explorer/explorerActions.ts new file mode 100644 index 0000000000..6eca98d02f --- /dev/null +++ b/src/sql/parts/dashboard/widgets/explorer/explorerActions.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; +import { IConnectionManagementService, MetadataType } from 'sql/parts/connection/common/connectionManagement'; +import { + NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, + BackupAction, BaseActionContext, ManageAction +} from 'sql/workbench/common/actions'; +import { IDisasterRecoveryUiService } from 'sql/parts/disasterRecovery/common/interfaces'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction } from 'vs/base/common/actions'; + +export function GetExplorerActions(type: MetadataType, isCloud: boolean, dashboardService: DashboardServiceInterface): TPromise { + let actions: IAction[] = []; + + // When context menu on database + if (type === undefined) { + actions.push(dashboardService.instantiationService.createInstance(DashboardNewQueryAction, DashboardNewQueryAction.ID, NewQueryAction.LABEL, NewQueryAction.ICON)); + if (!isCloud) { + actions.push(dashboardService.instantiationService.createInstance(DashboardBackupAction, DashboardBackupAction.ID, DashboardBackupAction.LABEL)); + } + actions.push(dashboardService.instantiationService.createInstance(ManageAction, ManageAction.ID, ManageAction.LABEL)); + return TPromise.as(actions); + } + + if (type === MetadataType.View || type === MetadataType.Table) { + actions.push(dashboardService.instantiationService.createInstance(ScriptSelectAction, ScriptSelectAction.ID, ScriptSelectAction.LABEL)); + } + + if (type === MetadataType.Table) { + actions.push(dashboardService.instantiationService.createInstance(EditDataAction, EditDataAction.ID, EditDataAction.LABEL)); + } + + actions.push(dashboardService.instantiationService.createInstance(ScriptCreateAction, ScriptCreateAction.ID, ScriptCreateAction.LABEL)); + + return TPromise.as(actions); +} + +export class DashboardBackupAction extends BackupAction { + public static ID = 'dashboard.' + BackupAction.ID; + + constructor( + id: string, label: string, + @IDisasterRecoveryUiService disasterRecoveryService: IDisasterRecoveryUiService, + @IConnectionManagementService private connectionManagementService: IConnectionManagementService + ) { + super(id, label, BackupAction.ICON, disasterRecoveryService, ); + } + + run(actionContext: BaseActionContext): TPromise { + let self = this; + // change database before performing action + return new TPromise((resolve, reject) => { + self.connectionManagementService.changeDatabase(actionContext.uri, actionContext.profile.databaseName).then(() => { + actionContext.connInfo = self.connectionManagementService.getConnectionInfo(actionContext.uri); + super.run(actionContext).then((result) => { + resolve(result); + }); + }, + () => { + resolve(false); + }); + }); + } +} + +export class DashboardNewQueryAction extends NewQueryAction { + public static ID = 'dashboard.' + NewQueryAction.ID; + + run(actionContext: BaseActionContext): TPromise { + let self = this; + // change database before performing action + return new TPromise((resolve, reject) => { + self._connectionManagementService.changeDatabase(actionContext.uri, actionContext.profile.databaseName).then(() => { + actionContext.profile = self._connectionManagementService.getConnectionProfile(actionContext.uri); + super.run(actionContext).then((result) => { + resolve(result); + }); + }, + () => { + resolve(false); + }); + }); + } +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.html b/src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.html new file mode 100644 index 0000000000..b833ca362e --- /dev/null +++ b/src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.html @@ -0,0 +1,12 @@ + +
+
+
+
+
+
\ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.ts b/src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.ts new file mode 100644 index 0000000000..4cea9dd43d --- /dev/null +++ b/src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.ts @@ -0,0 +1,394 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/media/objectTypes/objecttypes'; +import 'vs/css!sql/media/icons/common-icons'; +import 'vs/css!./media/explorerWidget'; + +import { Component, Inject, forwardRef, ChangeDetectorRef, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; +import { Router } from '@angular/router'; + +import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget'; +import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; +import { MetadataType } from 'sql/parts/connection/common/connectionManagement'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { BaseActionContext } from 'sql/workbench/common/actions'; +import { GetExplorerActions } from './explorerActions'; +import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils'; +import { warn } from 'sql/base/common/log'; +import { MultipleRequestDelayer } from 'sql/base/common/async'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { InputBox, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox'; +import { attachInputBoxStyler, attachListStyler } from 'vs/platform/theme/common/styler'; +import * as nls from 'vs/nls'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; +import * as types from 'vs/base/common/types'; +import { $, getContentHeight } from 'vs/base/browser/dom'; +import { Delayer } from 'vs/base/common/async'; + +import { ObjectMetadata } from 'data'; + +export class ObjectMetadataWrapper implements ObjectMetadata { + public metadataType: MetadataType; + public metadataTypeName: string; + public urn: string; + public name: string; + public schema: string; + + constructor(from?: ObjectMetadata) { + if (from) { + this.metadataType = from.metadataType; + this.metadataTypeName = from.metadataTypeName; + this.urn = from.urn; + this.name = from.name; + this.schema = from.schema; + } + } + + public matches(other: ObjectMetadataWrapper): boolean { + if (!other) { + return false; + } + + return this.metadataType === other.metadataType + && this.schema === other.schema + && this.name === other.name; + } + + public static createFromObjectMetadata(objectMetadata: ObjectMetadata[]): ObjectMetadataWrapper[] { + if (!objectMetadata) { + return undefined; + } + + return objectMetadata.map(m => new ObjectMetadataWrapper(m)); + } + + + // custom sort : Table > View > Stored Procedures > Function + public static sort(metadata1: ObjectMetadataWrapper, metadata2: ObjectMetadataWrapper): number { + // compare the object type + if (metadata1.metadataType < metadata2.metadataType) { + return -1; + } else if (metadata1.metadataType > metadata2.metadataType) { + return 1; + + // otherwise compare the schema + } else { + let schemaCompare: number = metadata1.schema && metadata2.schema + ? metadata1.schema.localeCompare(metadata2.schema) + // schemas are not expected to be undefined, but if they are then compare using object names + : 0; + + if (schemaCompare !== 0) { + return schemaCompare; + + // otherwise compare the object name + } else { + return metadata1.name.localeCompare(metadata2.name); + } + } + } +} + +declare type ListResource = string | ObjectMetadataWrapper; + +enum TemplateIds { + STRING = 'string', + METADATA = 'metadata' +} + +interface IListTemplate { + icon?: HTMLElement; + label: HTMLElement; +} + +class Delegate implements IDelegate { + getHeight(element: ListResource): number { + return 22; + } + + getTemplateId(element: ListResource): string { + if (element instanceof ObjectMetadataWrapper) { + return TemplateIds.METADATA.toString(); + } else if (types.isString(element)) { + return TemplateIds.STRING.toString(); + } else { + return ''; + } + } +} + +class StringRenderer implements IRenderer { + public readonly templateId = TemplateIds.STRING.toString(); + + renderTemplate(container: HTMLElement): IListTemplate { + let row = $('.list-row'); + let icon = $('.icon.database'); + let label = $('.label'); + row.appendChild(icon); + row.appendChild(label); + container.appendChild(row); + return { icon, label }; + } + + renderElement(element: string, index: number, templateData: IListTemplate): void { + templateData.label.innerText = element; + } + + disposeTemplate(templateData: IListTemplate): void { + // no op + } +} + +class MetadataRenderer implements IRenderer { + public readonly templateId = TemplateIds.METADATA.toString(); + + renderTemplate(container: HTMLElement): IListTemplate { + let row = $('.list-row'); + let icon = $('div'); + let label = $('.label'); + row.appendChild(icon); + row.appendChild(label); + container.appendChild(row); + return { icon, label }; + } + + renderElement(element: ObjectMetadataWrapper, index: number, templateData: IListTemplate): void { + if (element && element) { + switch (element.metadataType) { + case MetadataType.Function: + templateData.icon.className = 'icon scalarvaluedfunction'; + break; + case MetadataType.SProc: + templateData.icon.className = 'icon stored-procedure'; + break; + case MetadataType.Table: + templateData.icon.className = 'icon table'; + break; + case MetadataType.View: + templateData.icon.className = 'icon view'; + break; + } + + templateData.label.innerText = element.schema + '.' + element.name; + } + } + + disposeTemplate(templateData: IListTemplate): void { + // no op + } +} + +@Component({ + selector: 'explorer-widget', + templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/widgets/explorer/explorerWidget.component.html')) +}) +export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, OnInit, OnDestroy { + + private _isCloud: boolean; + private _tableData: ListResource[]; + private _disposables: Array = []; + private _input: InputBox; + private _table: List; + private _lastClickedItem: ListResource; + private _filterDelayer = new Delayer(200); + private _dblClickDelayer = new MultipleRequestDelayer(500); + + @ViewChild('input') private _inputContainer: ElementRef; + @ViewChild('table') private _tableContainer: ElementRef; + + constructor( + @Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface, + @Inject(forwardRef(() => Router)) private _router: Router, + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef, + @Inject(WIDGET_CONFIG) protected _config: WidgetConfig, + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef + ) { + super(); + this._isCloud = _bootstrap.connectionManagementService.connectionInfo.serverInfo.isCloud; + this.init(); + } + + ngOnInit() { + let inputOptions: IInputOptions = { + placeholder: this._config.context === 'database' ? nls.localize('seachObjects', 'Search by name of type (a:, t:, v:, f:, or sp:)') : nls.localize('searchDatabases', 'Search databases') + }; + this._input = new InputBox(this._inputContainer.nativeElement, this._bootstrap.contextViewService, inputOptions); + this._disposables.push(this._input.onDidChange(e => { + this._filterDelayer.trigger(() => { + this._table.splice(0, this._table.length, this._filterTable(e)); + }); + })); + this._table = new List(this._tableContainer.nativeElement, new Delegate(), [new MetadataRenderer(), new StringRenderer()]); + this._disposables.push(this._table.onContextMenu(e => { + this.handleContextMenu(e.element, e.index, e.anchor); + })); + this._disposables.push(this._table.onSelectionChange(e => { + if (e.elements.length > 0 && this._lastClickedItem === e.elements[0]) { + this._dblClickDelayer.trigger(() => this.handleItemDoubleClick(e.elements[0])); + } else { + this._lastClickedItem = e.elements.length > 0 ? e.elements[0] : undefined; + } + })); + this._table.layout(getContentHeight(this._tableContainer.nativeElement)); + this._disposables.push(this._input); + this._disposables.push(attachInputBoxStyler(this._input, this._bootstrap.themeService)); + this._disposables.push(this._table); + this._disposables.push(attachListStyler(this._table, this._bootstrap.themeService)); + } + + ngOnDestroy() { + this._disposables.forEach(i => i.dispose()); + } + + private init(): void { + if (this._config.context === 'database') { + this._disposables.push(toDisposableSubscription(this._bootstrap.metadataService.metadata.subscribe( + data => { + if (data) { + this._tableData = ObjectMetadataWrapper.createFromObjectMetadata(data.objectMetadata); + this._tableData.sort(ObjectMetadataWrapper.sort); + this._table.splice(0, this._table.length, this._tableData); + } + }, + error => { + (this._el.nativeElement).innerText = nls.localize('dashboard.explorer.objectError', "Unable to load objects"); + } + ))); + } else { + this._disposables.push(toDisposableSubscription(this._bootstrap.metadataService.databaseNames.subscribe( + data => { + this._tableData = data; + this._table.splice(0, this._table.length, this._tableData); + }, + error => { + (this._el.nativeElement).innerText = nls.localize('dashboard.explorer.databaseError', "Unable to load databases"); + } + ))); + } + } + + /** + * Handles action when an item is double clicked in the explorer widget + * @param val If on server page, explorer objects will be strings representing databases; + * If on databasepage, explorer objects will be ObjectMetadataWrapper representing object types; + * + */ + private handleItemDoubleClick(val: ListResource): void { + if (types.isString(val)) { + this._bootstrap.connectionManagementService.changeDatabase(val as string).then(result => { + this._router.navigate(['database-dashboard']); + }); + } + } + + /** + * Handles action when a item is clicked in the explorer widget + * @param val If on server page, explorer objects will be strings representing databases; + * If on databasepage, explorer objects will be ObjectMetadataWrapper representing object types; + * @param index Index of the value in the array the ngFor template is built from + * @param event Click event + */ + private handleContextMenu(val: ListResource, index: number, anchor: HTMLElement | { x: number, y: number }): void { + // event will exist if the context menu span was clicked + if (event) { + if (this._config.context === 'server') { + let newProfile = Object.create(this._bootstrap.connectionManagementService.connectionInfo.connectionProfile); + newProfile.databaseName = val as string; + this._bootstrap.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => GetExplorerActions(undefined, this._isCloud, this._bootstrap), + getActionsContext: () => { + return { + uri: this._bootstrap.getUnderlyingUri(), + profile: newProfile, + connInfo: this._bootstrap.connectionManagementService.connectionInfo, + databasename: val as string + }; + } + }); + } else if (this._config.context === 'database') { + let object = val as ObjectMetadataWrapper; + this._bootstrap.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => GetExplorerActions(object.metadataType, this._isCloud, this._bootstrap), + getActionsContext: () => { + return { + object: object, + uri: this._bootstrap.getUnderlyingUri(), + profile: this._bootstrap.connectionManagementService.connectionInfo.connectionProfile + }; + } + }); + } else { + warn('Unknown dashboard context: ', this._config.context); + } + } + this._changeRef.detectChanges(); + } + + private _filterTable(val: string): ListResource[] { + let items = this._tableData; + if (!items) { + return items; + } + + // format filter string for clean filter, no white space and lower case + let filterString = val.trim().toLowerCase(); + + // handle case when passed a string array + if (types.isString(items[0])) { + let _items = items; + return _items.filter(item => { + return item.toLowerCase().includes(filterString); + }); + } + + // make typescript compiler happy + let objectItems = items as ObjectMetadataWrapper[]; + + // determine is a filter is applied + let metadataType: MetadataType; + + if (val.includes(':')) { + let filterArray = filterString.split(':'); + + if (filterArray.length > 2) { + filterString = filterArray.slice(1, filterArray.length - 1).join(':'); + } else { + filterString = filterArray[1]; + } + + switch (filterArray[0].toLowerCase()) { + case 'v': + metadataType = MetadataType.View; + break; + case 't': + metadataType = MetadataType.Table; + break; + case 'sp': + metadataType = MetadataType.SProc; + break; + case 'f': + metadataType = MetadataType.Function; + break; + case 'a': + return objectItems; + default: + break; + } + } + + return objectItems.filter(item => { + if (metadataType !== undefined) { + return item.metadataType === metadataType && (item.schema + '.' + item.name).toLowerCase().includes(filterString); + } else { + return (item.schema + '.' + item.name).toLowerCase().includes(filterString); + } + }); + } +} diff --git a/src/sql/parts/dashboard/widgets/explorer/explorerWidget.contribution.ts b/src/sql/parts/dashboard/widgets/explorer/explorerWidget.contribution.ts new file mode 100644 index 0000000000..3240f9c3bd --- /dev/null +++ b/src/sql/parts/dashboard/widgets/explorer/explorerWidget.contribution.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { registerDashboardWidget } from 'sql/platform/dashboard/common/widgetRegistry'; + +let explorerSchema: IJSONSchema = { + type: 'object', +}; + +registerDashboardWidget('explorer-widget', '', explorerSchema); \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/explorer/media/explorerWidget.css b/src/sql/parts/dashboard/widgets/explorer/media/explorerWidget.css new file mode 100644 index 0000000000..f641a10da7 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/explorer/media/explorerWidget.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +explorer-widget .list-row .icon { + padding: 10px; +} + +explorer-widget .list-row { + display: flex; + flex-direction: row; + align-items: center; +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/insights/actions.ts b/src/sql/parts/dashboard/widgets/insights/actions.ts new file mode 100644 index 0000000000..6c38407e01 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/actions.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; + +import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; +import { RunQueryOnConnectionMode, IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { InsightActionContext } from 'sql/workbench/common/actions'; + +export class RunInsightQueryAction extends Action { + public static ID = 'runQuery'; + public static LABEL = nls.localize('insights.runQuery', "Run Query"); + + constructor( + id: string, label: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService + ) { + super(id, label); + } + + public run(context: InsightActionContext): TPromise { + return new TPromise((resolve, reject) => { + TaskUtilities.newQuery( + context.profile, + this._connectionManagementService, + this._queryEditorService, + context.insight.query as string, + RunQueryOnConnectionMode.executeQuery + ).then(() => resolve(true), () => resolve(false)); + }); + } +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/insights/insightsWidget.component.ts b/src/sql/parts/dashboard/widgets/insights/insightsWidget.component.ts new file mode 100644 index 0000000000..9c849b08d8 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/insightsWidget.component.ts @@ -0,0 +1,259 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { + Component, Inject, ViewContainerRef, forwardRef, AfterContentInit, + ComponentFactoryResolver, ViewChild, OnDestroy, ChangeDetectorRef +} from '@angular/core'; +import { Observable } from 'rxjs/Observable'; + +import { DashboardWidget, IDashboardWidget, WIDGET_CONFIG, WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget'; +import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; +import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive'; +import { InsightAction, InsightActionContext } from 'sql/workbench/common/actions'; +import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils'; +import { IInsightsConfig, IInsightsView } from './interfaces'; +import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry'; +import { insertValueRegex } from 'sql/parts/insights/browser/insightsDialogView'; +import { RunInsightQueryAction } from './actions'; + +import { SimpleExecuteResult } from 'data'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Action } from 'vs/base/common/actions'; +import * as types from 'vs/base/common/types'; +import * as pfs from 'vs/base/node/pfs'; +import * as nls from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; + +const insightRegistry = Registry.as(Extensions.InsightContribution); + +@Component({ + selector: 'insights-widget', + template: ` +
{{error}}
+
+ +
`, + styles: [':host { width: 100%; height: 100%}'] +}) +export class InsightsWidget extends DashboardWidget implements IDashboardWidget, AfterContentInit, OnDestroy { + private insightConfig: IInsightsConfig; + private queryObv: Observable; + private _disposables: Array = []; + @ViewChild(ComponentHostDirective) private componentHost: ComponentHostDirective; + + private _typeKey: string; + private _init: boolean = false; + + public error: string; + + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver, + @Inject(forwardRef(() => DashboardServiceInterface)) private dashboardService: DashboardServiceInterface, + @Inject(WIDGET_CONFIG) protected _config: WidgetConfig, + @Inject(forwardRef(() => ViewContainerRef)) private viewContainerRef: ViewContainerRef, + @Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef + ) { + super(); + this.insightConfig = this._config.widget['insights-widget']; + + this._verifyConfig(); + + this._parseConfig().then(() => { + if (!this._checkStorage()) { + let promise = this._runQuery(); + this.queryObv = Observable.fromPromise(promise); + promise.then( + result => { + if (this._init) { + this._updateChild(result); + } else { + this.queryObv = Observable.fromPromise(Promise.resolve(result)); + } + }, + error => { + if (this._init) { + this.showError(error); + } else { + this.queryObv = Observable.fromPromise(Promise.reject(error)); + } + } + ); + } + }, error => { + this.showError(error); + }); + } + + ngAfterContentInit() { + this._init = true; + if (this.queryObv) { + this._disposables.push(toDisposableSubscription(this.queryObv.subscribe( + result => { + this._updateChild(result); + }, + error => { + this.showError(error); + } + ))); + } + } + + ngOnDestroy() { + this._disposables.forEach(i => i.dispose()); + } + + private showError(error: string): void { + this.error = error; + this._cd.detectChanges(); + } + + get actions(): Array { + let actions: Array = []; + if (this.insightConfig.details && (this.insightConfig.details.query || this.insightConfig.details.queryFile)) { + actions.push(this.dashboardService.instantiationService.createInstance(InsightAction, InsightAction.ID, InsightAction.LABEL)); + } + actions.push(this.dashboardService.instantiationService.createInstance(RunInsightQueryAction, RunInsightQueryAction.ID, RunInsightQueryAction.LABEL)); + return actions; + } + + get actionsContext(): InsightActionContext { + return { + profile: this.dashboardService.connectionManagementService.connectionInfo.connectionProfile, + insight: this.insightConfig + }; + } + + private _storeResult(result: SimpleExecuteResult): SimpleExecuteResult { + if (this.insightConfig.cacheId) { + this.dashboardService.storageService.store(this._getStorageKey(), JSON.stringify(result)); + } + return result; + } + + private _checkStorage(): boolean { + if (this.insightConfig.cacheId) { + let storage = this.dashboardService.storageService.get(this._getStorageKey()); + if (storage) { + if (this._init) { + this._updateChild(JSON.parse(storage)); + } else { + this.queryObv = Observable.fromPromise(Promise.resolve(JSON.parse(storage))); + } + return true; + } else { + return false; + } + } + + return false; + } + + public get refresh(): () => void { + return this._refresh(); + } + + public _refresh(): () => void { + return () => { + this._runQuery().then( + result => this._updateChild(result), + error => this.showError(error) + ); + }; + } + + private _getStorageKey(): string { + return `insights.${this.insightConfig.cacheId}.${this.dashboardService.connectionManagementService.connectionInfo.connectionProfile.getOptionsKey()}`; + } + + private _runQuery(): Thenable { + return this.dashboardService.queryManagementService.runQueryAndReturn(this.insightConfig.query as string).then( + result => { + return this._storeResult(result); + }, + error => { + throw error; + } + ); + } + + private _updateChild(result: SimpleExecuteResult): void { + if (result.rowCount === 0) { + this.showError(nls.localize('noResults', 'No results to show')); + return; + } + + let componentFactory = this._componentFactoryResolver.resolveComponentFactory(insightRegistry.getCtorFromId(this._typeKey)); + this.componentHost.viewContainerRef.clear(); + + let componentRef = this.componentHost.viewContainerRef.createComponent(componentFactory); + let componentInstance = componentRef.instance; + componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => item.displayValue)) }; + // check if the setter is defined + componentInstance.config = this.insightConfig.type[this._typeKey]; + if (componentInstance.init) { + componentInstance.init(); + } + } + + private _verifyConfig() { + if (types.isUndefinedOrNull(this.insightConfig)) { + throw new Error('Insight config must be defined'); + } + + if (types.isUndefinedOrNull(this.insightConfig.type)) { + throw new Error('An Insight type must be specified'); + } + + if (Object.keys(this.insightConfig.type).length !== 1) { + throw new Error('Exactly 1 insight type must be specified'); + } + + if (!insightRegistry.getAllIds().includes(Object.keys(this.insightConfig.type)[0])) { + throw new Error('The insight type must be a valid registered insight'); + } + + if (!this.insightConfig.query && !this.insightConfig.queryFile) { + throw new Error('No query was specified for this insight'); + } + + if (!types.isStringArray(this.insightConfig.query) + && !types.isString(this.insightConfig.query) + && !types.isString(this.insightConfig.queryFile)) { + throw new Error('Invalid query or queryfile specified'); + } + } + + private _parseConfig(): Thenable { + let promises: Array> = []; + + this._typeKey = Object.keys(this.insightConfig.type)[0]; + + if (types.isStringArray(this.insightConfig.query)) { + this.insightConfig.query = this.insightConfig.query.join(' '); + } else if (this.insightConfig.queryFile) { + let filePath = this.insightConfig.queryFile; + // check for workspace relative path + let match = filePath.match(insertValueRegex); + if (match && match.length > 0 && match[1] === 'workspaceRoot') { + filePath = filePath.replace(match[0], ''); + filePath = this.dashboardService.workspaceContextService.toResource(filePath).fsPath; + } + promises.push(new Promise((resolve, reject) => { + pfs.readFile(filePath).then( + buffer => { + this.insightConfig.query = buffer.toString(); + resolve(); + }, + error => { + reject(error); + } + ); + })); + } + + return Promise.all(promises); + } +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/insights/insightsWidget.contribution.ts b/src/sql/parts/dashboard/widgets/insights/insightsWidget.contribution.ts new file mode 100644 index 0000000000..e1ff3f22d0 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/insightsWidget.contribution.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { join } from 'path'; + +import { registerDashboardWidget, registerNonCustomDashboardWidget } from 'sql/platform/dashboard/common/widgetRegistry'; +import { Extensions as InsightExtensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry'; +import { IInsightsConfig } from './interfaces'; +import { insightsContribution, insightsSchema } from 'sql/parts/dashboard/widgets/insights/insightsWidgetSchemas'; + +import { IExtensionPointUser, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; + +const insightRegistry = Registry.as(InsightExtensions.InsightContribution); + +interface IInsightTypeContrib { + id: string; + contrib: IInsightsConfig; +} + +registerDashboardWidget('insights-widget', '', insightsSchema); + +ExtensionsRegistry.registerExtensionPoint('insights', [], insightsContribution).setHandler(extensions => { + + function handleCommand(insight: IInsightTypeContrib, extension: IExtensionPointUser) { + + if (insight.contrib.queryFile) { + insight.contrib.queryFile = join(extension.description.extensionFolderPath, insight.contrib.queryFile); + } + + if (insight.contrib.details && insight.contrib.details.queryFile) { + insight.contrib.details.queryFile = join(extension.description.extensionFolderPath, insight.contrib.details.queryFile); + } + + registerNonCustomDashboardWidget(insight.id, '', insight.contrib); + insightRegistry.registerExtensionInsight(insight.id, insight.contrib); + } + + for (let extension of extensions) { + const { value } = extension; + if (Array.isArray(value)) { + for (let command of value) { + handleCommand(command, extension); + } + } else { + handleCommand(value, extension); + } + } +}); \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/insights/insightsWidgetSchemas.ts b/src/sql/parts/dashboard/widgets/insights/insightsWidgetSchemas.ts new file mode 100644 index 0000000000..6d67f691fd --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/insightsWidgetSchemas.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IInsightRegistry, Extensions as InsightExtensions } from 'sql/platform/dashboard/common/insightRegistry'; +import { ITaskRegistry, Extensions as TaskExtensions } from 'sql/platform/tasks/taskRegistry'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { Registry } from 'vs/platform/registry/common/platform'; +import * as nls from 'vs/nls'; + +const insightRegistry = Registry.as(InsightExtensions.InsightContribution); +const taskRegistry = Registry.as(TaskExtensions.TaskContribution); + +export const insightsSchema: IJSONSchema = { + type: 'object', + description: nls.localize('insightWidgetDescription', 'Adds a widget that can query a server or database and display the results in multiple ways - as a chart, summarized count, and more'), + properties: { + id: { + type: 'string', + description: nls.localize('insightIdDescription', 'Unique Identifier used for cacheing the results of the insight.') + }, + type: { + type: 'object', + properties: insightRegistry.insightSchema.properties, + minItems: 1, + maxItems: 1 + }, + query: { + type: ['string', 'array'], + description: nls.localize('insightQueryDescription', 'SQL query to run. This should return exactly 1 resultset.') + }, + queryFile: { + type: 'string', + description: nls.localize('insightQueryFileDescription', '[Optional] path to a file that contains a query. Use if "query" is not set') + }, + details: { + type: 'object', + properties: { + query: { + type: ['string', 'array'] + }, + queryFile: { + type: 'string' + }, + value: { + type: 'string' + }, + label: { + type: ['string', 'object'], + properties: { + column: { + type: 'string' + }, + icon: { + type: 'string' + }, + state: { + type: 'array', + items: { + type: 'object', + properties: { + condition: { + type: 'object', + properties: { + if: { + type: 'string', + enum: ['equals', 'notEquals', 'greaterThanOrEquals', 'greaterThan', 'lessThanOrEquals', 'lessThan', 'always'] + }, + equals: { + type: 'string' + } + } + }, + color: { + type: 'string' + }, + icon: { + type: 'string' + } + } + } + } + } + }, + actions: { + type: 'object', + properties: { + types: { + type: 'object', + properties: taskRegistry.taskSchemas + }, + database: { + type: 'string', + description: nls.localize('actionDatabaseDescription', 'Target database for the action; can use the format "${columnName} to use a data driven column name.') + }, + server: { + type: 'string', + description: nls.localize('actionServerDescription', 'Target server for the action; can use the format "${columnName} to use a data driven column name.') + }, + user: { + type: 'string', + description: nls.localize('actionUserDescription', 'Target user for the action; can use the format "${columnName} to use a data driven column name.') + } + } + } + } + } + } +}; + +const insightType: IJSONSchema = { + type: 'object', + properties: { + id: { + description: nls.localize('carbon.extension.contributes.insightType.id', 'Identifier of the insight'), + type: 'string' + }, + contrib: insightsSchema + } +}; + +export const insightsContribution: IJSONSchema = { + description: nls.localize('carbon.extension.contributes.insights', "Contributes insights to the dashboard palette."), + oneOf: [ + insightType, + { + type: 'array', + items: insightType + } + ] +}; \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/insights/interfaces.ts b/src/sql/parts/dashboard/widgets/insights/interfaces.ts new file mode 100644 index 0000000000..6b9f0d7955 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/interfaces.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface IStateCondition { + condition: { + if: string, + equals?: string + }; + color?: string; + icon?: string; +} + +export interface IInsightsLabel { + column: string; + icon?: string; + state?: Array; +} + +export interface IInsightsConfigDetails { + query?: string | Array; + queryFile?: string; + label?: string | IInsightsLabel; + value?: string; + actions?: { + types: Array; + database?: string; + server?: string; + user?: string; + }; +} + +export interface IInsightData { + columns: Array; + rows: Array>; +} + +export interface IInsightsView { + data: IInsightData; + config?: { [key: string]: any }; + init?: () => void; +} + +export interface ISize { + x: number; + y: number; +} + +export interface IInsightsConfig { + cacheId?: string; + type: any; + name?: string; + provider?: string; + edition?: number | Array; + gridItemConfig?: ISize; + query?: string | Array; + queryFile?: string; + details?: IInsightsConfigDetails; +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component.ts new file mode 100644 index 0000000000..a9496f8d0d --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component.ts @@ -0,0 +1,286 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ElementRef, OnDestroy, ViewChild } from '@angular/core'; +import { BaseChartDirective } from 'ng2-charts/ng2-charts'; + +/* SQL Imports */ +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; + +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import * as TelemetryUtils from 'sql/common/telemetryUtilities'; +import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import { memoize, unmemoize } from 'sql/base/common/decorators'; + +/* VS Imports */ +import * as colors from 'vs/platform/theme/common/colorRegistry'; +import { mixin } from 'sql/base/common/objects'; +import { Color } from 'vs/base/common/color'; +import * as types from 'vs/base/common/types'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; + +export enum ChartType { + Bar = 'bar', + Doughnut = 'doughnut', + HorizontalBar = 'horizontalBar', + Line = 'line', + Pie = 'pie', + TimeSeries = 'timeSeries', + Scatter = 'scatter' +} + +export enum DataDirection { + Vertical = 'vertical', + Horizontal = 'horizontal' +} + +export enum LegendPosition { + Top = 'top', + Bottom = 'bottom', + Left = 'left', + Right = 'right', + None = 'none' +} + +export function customMixin(destination: any, source: any, overwrite?: boolean): any { + if (types.isObject(source)) { + mixin(destination, source, overwrite, customMixin); + } else if (types.isArray(source)) { + for (let i = 0; i < source.length; i++) { + if (destination[i]) { + mixin(destination[i], source[i], overwrite, customMixin); + } else { + destination[i] = source[i]; + } + } + } else { + destination = source; + } + return destination; +} + +export interface IDataSet { + data: Array; + label?: string; +} + +export interface IPointDataSet { + data: Array<{ x: number | string, y: number }>; + label?: string; + fill: boolean; + backgroundColor?: Color; +} + +export interface IChartConfig { + colorMap?: { [column: string]: string }; + labelFirstColumn?: boolean; + legendPosition?: LegendPosition; + dataDirection?: DataDirection; + columnsAsLabels?: boolean; +} + +export const defaultChartConfig: IChartConfig = { + labelFirstColumn: false, + columnsAsLabels: false, + legendPosition: LegendPosition.Top, + dataDirection: DataDirection.Vertical +}; + +@Component({ + template: `
+ +
` +}) +export abstract class ChartInsight implements IInsightsView, OnDestroy { + private _isDataAvailable: boolean = false; + private _options: any = {}; + + @ViewChild(BaseChartDirective) private _chart: BaseChartDirective; + + protected _defaultConfig = defaultChartConfig; + protected _disposables: Array = []; + protected _config: IChartConfig; + protected _data: IInsightData; + + protected abstract get chartType(): ChartType; + + + constructor( + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef, + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(BOOTSTRAP_SERVICE_ID) protected _bootstrapService: IBootstrapService) { } + + ngOnDestroy() { + this._disposables.forEach(item => item.dispose()); + } + + init() { + this._disposables.push(this._bootstrapService.themeService.onDidColorThemeChange(e => this.updateTheme(e))); + this.updateTheme(this._bootstrapService.themeService.getColorTheme()); + // Note: must use a boolean to not render the canvas until all properties such as the labels and chart type are set. + // This is because chart.js doesn't auto-update anything other than dataset when re-rendering so defaults are used + // hence it's easier to not render until ready + this.options = mixin(this.options, { maintainAspectRatio: false }); + this._isDataAvailable = true; + this._changeRef.detectChanges(); + TelemetryUtils.addTelemetry(this._bootstrapService.telemetryService, TelemetryKeys.ChartCreated, { type: this.chartType }); + } + + /** + * Sets the options for the chart; handles rerendering the chart if needed + */ + public set options(options: any) { + this._options = options; + if (this._isDataAvailable) { + this._options = mixin({}, mixin(this._options, { animation: { duration: 0 } })); + this.refresh(); + } + } + + public get options(): any { + return this._options; + } + + protected updateTheme(e: IColorTheme): void { + let options = { + legend: { + labels: { + fontColor: e.getColor(colors.editorForeground) + } + } + }; + this.options = mixin({}, mixin(this.options, options)); + } + + public refresh() { + // cheaper refresh but causes problems when change data for rerender + this._chart.ngOnChanges({}); + } + + public getCanvasData(): string { + if (this._chart && this._chart.chart) { + return this._chart.chart.toBase64Image(); + } else { + return undefined; + } + } + + @Input() set data(data: IInsightData) { + // unmemoize chart data as the data needs to be recalced + unmemoize(this, 'chartData'); + unmemoize(this, 'labels'); + this._data = data; + + this._changeRef.detectChanges(); + } + + protected clearMemoize(): void { + // unmemoize getters since their result can be changed by a new config + unmemoize(this, 'getChartData'); + unmemoize(this, 'getLabels'); + unmemoize(this, 'colors'); + } + + @Input() set config(config: IChartConfig) { + this.clearMemoize(); + this._config = mixin(config, this._defaultConfig, false); + this.legendPosition = this._config.legendPosition; + if (this._isDataAvailable) { + this._options = mixin({}, mixin(this._options, { animation: false })); + this.refresh(); + } + } + + /* Typescript does not allow you to access getters/setters for super classes. + This is a workaround that allows us to still call base getter */ + @memoize + protected getChartData(): Array { + if (this._config.dataDirection === 'horizontal') { + if (this._config.labelFirstColumn) { + return this._data.rows.map((row) => { + return { + data: row.map(item => Number(item)).slice(1), + label: row[0] + }; + }); + } else { + return this._data.rows.map((row, i) => { + return { + data: row.map(item => Number(item)), + label: 'Series' + i + }; + }); + } + } else { + if (this._config.columnsAsLabels) { + return this._data.rows[0].map((row, i) => { + return { + data: this._data.rows.map(row => Number(row[i])), + label: this._data.columns[i] + }; + }).slice(1); + } else { + return this._data.rows[0].map((row, i) => { + return { + data: this._data.rows.map(row => Number(row[i])), + label: 'Series' + i + }; + }).slice(1); + } + } + } + + public get chartData(): Array { + return this.getChartData(); + } + + @memoize + public getLabels(): Array { + if (this._config.dataDirection === 'horizontal') { + return this._data.columns; + } else { + return this._data.rows.map(row => row[0]); + } + } + + public get labels(): Array { + return this.getLabels(); + } + + + @memoize + private get colors(): { backgroundColor: string[] }[] { + if (this._config && this._config.colorMap) { + let backgroundColor = this.labels.map((item) => { + return this._config.colorMap[item]; + }); + let colorsMap = { backgroundColor }; + return [colorsMap]; + } else { + return undefined; + } + } + + public set legendPosition(input: LegendPosition) { + let options = { + legend: { + display: true, + position: 'top' + } + }; + if (input === 'none') { + options.legend.display = false; + } else { + options.legend.position = input; + } + this.options = mixin(this.options, options); + } +} diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution.ts new file mode 100644 index 0000000000..9c73c1394b --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution.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 { IJSONSchema } from 'vs/base/common/jsonSchema'; +import * as nls from 'vs/nls'; + +export const chartInsightSchema: IJSONSchema = { + type: 'object', + description: nls.localize('chartInsightDescription', 'Displays results of a query as a chart on the dashboard'), + properties: { + colorMap: { + type: 'object', + description: nls.localize('colorMapDescription', 'Maps "column name" -> color. for example add "column1": red to ensure this column uses a red color ') + }, + legendPosition: { + type: 'string', + description: nls.localize('legendDescription', 'Indicates preferred position and visibility of the chart legend. These are the column names from your query, and map to the label of each chart entry'), + default: 'none', + enum: ['top', 'bottom', 'left', 'right', 'none'] + }, + labelFirstColumn: { + type: 'boolean', + description: nls.localize('labelFirstColumnDescription', 'If dataDirection is horizontal, setting this to true uses the first columns value for the legend.'), + default: false + }, + columnsAsLabels: { + type: 'boolean', + description: nls.localize('columnsAsLabels', 'If dataDirection is vertical, setting this to true will use the columns names for the legend.'), + default: false + }, + dataDirection: { + type: 'string', + description: nls.localize('dataDirectionDescription', 'Defines whether the data is read from a column (vertical) or a row (horizontal). For time series this is ignored as direction must be vertical.'), + default: 'vertical', + enum: ['vertical', 'horizontal'], + enumDescriptions: ['When vertical, the first column is used to define the x-axis labels, with other columns expected to be numerical.', 'When horizontal, the column names are used as the x-axis labels.'] + } + } +}; \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/barChart.component.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/barChart.component.ts new file mode 100644 index 0000000000..8afe709ef8 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/barChart.component.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ChartInsight, ChartType, customMixin } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; +import { mixin } from 'sql/base/common/objects'; + +import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import * as colors from 'vs/platform/theme/common/colorRegistry'; +import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry'; + +export default class BarChart extends ChartInsight { + protected readonly chartType: ChartType = ChartType.Bar; + + protected updateTheme(e: IColorTheme): void { + super.updateTheme(e); + let options = { + scales: { + xAxes: [{ + scaleLabel: { + fontColor: e.getColor(colors.editorForeground) + }, + ticks: { + fontColor: e.getColor(colors.editorForeground) + }, + gridLines: { + color: e.getColor(editorLineNumbers) + } + }], + yAxes: [{ + scaleLabel: { + fontColor: e.getColor(colors.editorForeground) + }, + ticks: { + fontColor: e.getColor(colors.editorForeground) + }, + gridLines: { + color: e.getColor(editorLineNumbers) + } + }] + } + }; + + this.options = mixin({}, mixin(this.options, options, true, customMixin)); + } +} diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution.ts new file mode 100644 index 0000000000..7e9ca498ea --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { mixin, clone } from 'vs/base/common/objects'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry'; +import { chartInsightSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution'; + +import BarChart from './barChart.component'; + +const properties: IJSONSchema = { + +}; + +const barSchema = mixin(clone(chartInsightSchema), properties) as IJSONSchema; + +registerInsight('bar', '', barSchema, BarChart); diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/doughnutChart.component.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/doughnutChart.component.ts new file mode 100644 index 0000000000..37faf4eba6 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/doughnutChart.component.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; +import PieChart from './pieChart.component'; + +export default class DoughnutChart extends PieChart { + protected readonly chartType: ChartType = ChartType.Doughnut; +} diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/doughnutChart.contribution.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/doughnutChart.contribution.ts new file mode 100644 index 0000000000..0afe928e4e --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/doughnutChart.contribution.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { mixin, clone } from 'vs/base/common/objects'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry'; +import { chartInsightSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution'; + +import DoughnutChart from './doughnutChart.component'; + +const properties: IJSONSchema = { + +}; + +const doughnutChartSchema = mixin(clone(chartInsightSchema), properties) as IJSONSchema; + +registerInsight('doughnut', '', doughnutChartSchema, DoughnutChart); diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/horizontalBarChart.component.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/horizontalBarChart.component.ts new file mode 100644 index 0000000000..2937513ac0 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/horizontalBarChart.component.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; +import BarChart from './barChart.component'; + +export default class HorizontalBarChart extends BarChart { + protected readonly chartType: ChartType = ChartType.HorizontalBar; +} diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/horizontalBarChart.contribution.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/horizontalBarChart.contribution.ts new file mode 100644 index 0000000000..f098dba6b2 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/horizontalBarChart.contribution.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { mixin, clone } from 'vs/base/common/objects'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry'; +import { chartInsightSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution'; + +import HorizontalBarChart from './horizontalBarChart.component'; + +const properties: IJSONSchema = { + +}; + +const horizontalBarSchema = mixin(clone(chartInsightSchema), properties) as IJSONSchema; + +registerInsight('horizontalBar', '', horizontalBarSchema, HorizontalBarChart); diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component.ts new file mode 100644 index 0000000000..db7ccb48a6 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ChartType, customMixin, IChartConfig, defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; +import BarChart from './barChart.component'; +import { memoize, unmemoize } from 'sql/base/common/decorators'; +import { mixin } from 'sql/base/common/objects'; +import { clone } from 'vs/base/common/objects'; + +export enum DataType { + Number = 'number', + Point = 'point' +} + +export interface ILineConfig extends IChartConfig { + dataType?: DataType; +} + +const defaultLineConfig = mixin(clone(defaultChartConfig), { dataType: 'number' }) as ILineConfig; + +export default class LineChart extends BarChart { + protected readonly chartType: ChartType = ChartType.Line; + protected _config: ILineConfig; + protected _defaultConfig = defaultLineConfig; + + public init() { + if (this._config.dataType === DataType.Point) { + this.addAxisLabels(); + } + super.init(); + } + + public get chartData(): Array { + if (this._config.dataType === DataType.Number) { + return super.getChartData(); + } else { + return this.getDataAsPoint(); + } + } + + protected clearMemoize() { + super.clearMemoize(); + unmemoize(this, 'getDataAsPoint'); + } + + @memoize + protected getDataAsPoint(): Array { + let dataSetMap: { [label: string]: IPointDataSet } = {}; + this._data.rows.map(row => { + if (row && row.length >= 3) { + let legend = row[0]; + if (!dataSetMap[legend]) { + dataSetMap[legend] = { label: legend, data: [], fill: false }; + } + dataSetMap[legend].data.push({ x: Number(row[1]), y: Number(row[2]) }); + } + }); + return Object.values(dataSetMap); + } + + public get labels(): Array { + if (this._config.dataType === DataType.Number) { + return super.getLabels(); + } else { + return []; + } + } + + protected addAxisLabels(): void { + let xLabel = this._data.columns[1] || 'x'; + let yLabel = this._data.columns[2] || 'y'; + let options = { + scales: { + xAxes: [{ + type: 'linear', + position: 'bottom', + display: true, + scaleLabel: { + display: true, + labelString: xLabel + } + }], + + yAxes: [{ + display: true, + scaleLabel: { + display: true, + labelString: yLabel, + } + }] + } + }; + + this.options = mixin(this.options, options, true, customMixin); + } +} diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.contribution.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.contribution.ts new file mode 100644 index 0000000000..9aef08b552 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.contribution.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { mixin, clone } from 'vs/base/common/objects'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry'; +import { chartInsightSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution'; + +import LineChart from './lineChart.component'; + +import * as nls from 'vs/nls'; + +const properties: IJSONSchema = { + properties: { + dataType: { + type: 'string', + description: nls.localize('dataTypeDescription', 'Indicates data property of a data set for a chart.'), + default: 'number', + enum: ['number', 'point'], + enumDescriptions: ['Set "number" if the data values are contained in 1 column.', 'Set "point" if the data is an {x,y} combination requiring 2 columns for each value.'] + }, + } +}; + +export const lineSchema = mixin(clone(chartInsightSchema), properties) as IJSONSchema; + +registerInsight('line', '', lineSchema, LineChart); diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/pieChart.component.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/pieChart.component.ts new file mode 100644 index 0000000000..6186d10130 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/pieChart.component.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ChartInsight, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; + +export default class PieChart extends ChartInsight { + protected readonly chartType: ChartType = ChartType.Pie; +} diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/pieChart.contribution.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/pieChart.contribution.ts new file mode 100644 index 0000000000..1a2caf42d1 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/pieChart.contribution.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { mixin, clone } from 'vs/base/common/objects'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry'; +import { chartInsightSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution'; + +import PieChart from './pieChart.component'; + +const properties: IJSONSchema = { + +}; + +const pieSchema = mixin(clone(chartInsightSchema), properties) as IJSONSchema; + +registerInsight('pie', '', pieSchema, PieChart); diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/scatterChart.component.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/scatterChart.component.ts new file mode 100644 index 0000000000..ad3a7d1503 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/scatterChart.component.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ChartType, defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; +import LineChart, { ILineConfig } from './lineChart.component'; + +import { mixin, clone } from 'vs/base/common/objects'; + +const defaultScatterConfig = mixin(clone(defaultChartConfig), { dataType: 'point', dataDirection: 'horizontal' }) as ILineConfig; + +export default class ScatterChart extends LineChart { + protected readonly chartType: ChartType = ChartType.Scatter; + protected _defaultConfig = defaultScatterConfig; +} diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/scatterChart.contribution.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/scatterChart.contribution.ts new file mode 100644 index 0000000000..5f137f74f2 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/scatterChart.contribution.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { mixin, clone } from 'vs/base/common/objects'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry'; +import { chartInsightSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution'; + +import ScatterChart from './scatterChart.component'; + +const properties: IJSONSchema = { +}; + +const scatterSchema = mixin(clone(chartInsightSchema), properties) as IJSONSchema; + +registerInsight('scatter', '', scatterSchema, ScatterChart); diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/timeSeriesChart.component.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/timeSeriesChart.component.ts new file mode 100644 index 0000000000..5c2dfb9415 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/timeSeriesChart.component.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { defaultChartConfig, IPointDataSet, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; +import LineChart, { ILineConfig } from './lineChart.component'; + +import { mixin, clone } from 'vs/base/common/objects'; +import { Color } from 'vs/base/common/color'; + +const defaultTimeSeriesConfig = mixin(clone(defaultChartConfig), { dataType: 'point', dataDirection: 'horizontal' }) as ILineConfig; + +export default class TimeSeriesChart extends LineChart { + protected _defaultConfig = defaultTimeSeriesConfig; + + protected addAxisLabels(): void { + let xLabel = this.getLabels()[1] || 'x'; + let yLabel = this.getLabels()[2] || 'y'; + + let options = { + scales: { + xAxes: [{ + type: 'time', + display: true, + scaleLabel: { + display: true, + labelString: xLabel + }, + ticks: { + autoSkip: false, + maxRotation: 45, + minRotation: 45 + } + }], + + yAxes: [{ + display: true, + scaleLabel: { + display: true, + labelString: yLabel + } + }] + } + }; + + this.options = Object.assign({}, mixin(this.options, options)); + } + + protected getDataAsPoint(): Array { + let dataSetMap: { [label: string]: IPointDataSet } = {}; + this._data.rows.map(row => { + if (row && row.length >= 3) { + let legend = row[0]; + if (!dataSetMap[legend]) { + dataSetMap[legend] = { label: legend, data: [], fill: false }; + } + dataSetMap[legend].data.push({ x: row[1], y: Number(row[2]) }); + + if (this.chartType === ChartType.Scatter) { + dataSetMap[legend].backgroundColor = Color.cyan; + } + } + }); + return Object.values(dataSetMap); + } +} diff --git a/src/sql/parts/dashboard/widgets/insights/views/charts/types/timeSeriesChart.contribution.ts b/src/sql/parts/dashboard/widgets/insights/views/charts/types/timeSeriesChart.contribution.ts new file mode 100644 index 0000000000..c54e071c82 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/charts/types/timeSeriesChart.contribution.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { mixin, clone } from 'vs/base/common/objects'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry'; +import { chartInsightSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.contribution'; + +import TimeSeriesChart from './timeSeriesChart.component'; + +const properties: IJSONSchema = { +}; + +const timeSeriesSchema = mixin(clone(chartInsightSchema), properties) as IJSONSchema; + +registerInsight('timeSeries', '', timeSeriesSchema, TimeSeriesChart); diff --git a/src/sql/parts/dashboard/widgets/insights/views/countInsight.component.ts b/src/sql/parts/dashboard/widgets/insights/views/countInsight.component.ts new file mode 100644 index 0000000000..4a39b2c72d --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/countInsight.component.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Component, Input, Inject, ChangeDetectorRef, forwardRef } from '@angular/core'; + +import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces'; + +@Component({ + template: ` +
+ {{_values[i]}} + {{_labels[i]}} +
+ ` +}) +export default class CountInsight implements IInsightsView { + private _labels: Array; + private _values: Array; + + constructor( + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef) { } + + @Input() set data(data: IInsightData) { + this._labels = []; + this._labels = data.columns; + this._values = data.rows[0]; + this._changeRef.detectChanges(); + } +} diff --git a/src/sql/parts/dashboard/widgets/insights/views/countInsight.contribution.ts b/src/sql/parts/dashboard/widgets/insights/views/countInsight.contribution.ts new file mode 100644 index 0000000000..5e5ca03994 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/countInsight.contribution.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry'; + +import CountInsight from './countInsight.component'; + +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import * as nls from 'vs/nls'; + +let countInsightSchema: IJSONSchema = { + type: 'null', + description: nls.localize('countInsightDescription', 'For each column in a resultset, displays the value in row 0 as a count followed by the column name. Supports "1 Healthy", "3 Unhealthy" for example, where "Healthy" is the column name and 1 is the value in row 1 cell 1') +}; + +registerInsight('count', '', countInsightSchema, CountInsight); \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/insights/views/imageInsight.component.ts b/src/sql/parts/dashboard/widgets/insights/views/imageInsight.component.ts new file mode 100644 index 0000000000..80562df017 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/imageInsight.component.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ViewChild, OnInit, ElementRef } from '@angular/core'; + +import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces'; + +import { mixin } from 'vs/base/common/objects'; + +interface IConfig { + encoding?: string; + imageFormat?: string; +} + +const defaultConfig: IConfig = { + encoding: 'hex', + imageFormat: 'jpeg' +}; + +@Component({ + template: ` +
+ +
+ ` +}) +export default class ImageInsight implements IInsightsView, OnInit { + private _rawSource: string; + private _config: IConfig; + + @ViewChild('image') private image: ElementRef; + @ViewChild('container') private container: ElementRef; + + constructor( + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef) { } + + ngOnInit() { + let size = Math.min(this.container.nativeElement.parentElement.parentElement.offsetHeight, this.container.nativeElement.parentElement.parentElement.offsetWidth); + this.image.nativeElement.style.width = size + 'px'; + this.image.nativeElement.style.height = size + 'px'; + } + + @Input() set config(config: { [key: string]: any }) { + this._config = mixin(config, defaultConfig, false); + this._changeRef.detectChanges(); + } + + @Input() set data(data: IInsightData) { + let self = this; + if (data.rows && data.rows.length > 0 && data.rows[0].length > 0) { + self._rawSource = data.rows[0][0]; + } else { + this._rawSource = ''; + } + this._changeRef.detectChanges(); + } + + public get hasData(): boolean { + return this._rawSource && this._rawSource !== ''; + } + + public get source(): string { + let img = this._rawSource; + if (this._config.encoding === 'hex') { + img = ImageInsight._hexToBase64(img); + } + return `data:image/${this._config.imageFormat};base64,${img}`; + } + + private static _hexToBase64(hexVal: string) { + + if (hexVal.startsWith('0x')) { + hexVal = hexVal.slice(2); + } + // should be able to be replaced with new Buffer(hexVal, 'hex').toString('base64') + return btoa(String.fromCharCode.apply(null, hexVal.replace(/\r|\n/g, '').replace(/([\da-fA-F]{2}) ?/g, '0x$1 ').replace(/ +$/, '').split(' '))); + } + +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/insights/views/imageInsight.contribution.ts b/src/sql/parts/dashboard/widgets/insights/views/imageInsight.contribution.ts new file mode 100644 index 0000000000..354523d1c4 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/insights/views/imageInsight.contribution.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry'; + +import ImageInsight from './imageInsight.component'; + +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import * as nls from 'vs/nls'; + +let imageInsightSchema: IJSONSchema = { + type: 'object', + description: nls.localize('imageInsightDescription', 'Displays an image, for example one returned by an R query using ggplot2'), + properties: { + imageFormat: { + type: 'string', + description: nls.localize('imageFormatDescription', 'What format is expected - is this a JPEG, PNG or other format?'), + default: 'jpeg', + enum: ['jpeg', 'png'] + }, + encoding: { + type: 'string', + description: nls.localize('encodingDescription', 'Is this encoded as hex, base64 or some other format?'), + default: 'hex', + enum: ['hex', 'base64'] + }, + } +}; + +registerInsight('image', '', imageInsightSchema, ImageInsight); \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/properties/propertiesJson.ts b/src/sql/parts/dashboard/widgets/properties/propertiesJson.ts new file mode 100644 index 0000000000..8b51904262 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/properties/propertiesJson.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { ProviderProperties } from './propertiesWidget.component'; +import * as nls from 'vs/nls'; + +let azureEditionDisplayName = nls.localize('azureEdition', 'Edition'); +let azureType = nls.localize('azureType', 'Type'); + +export const properties: Array = [ + { + provider: 'MSSQL', + flavors: [ + { + flavor: 'on_prem', + condition: { + field: 'isCloud', + operator: '!=', + value: true + }, + databaseProperties: [ + { + displayName: nls.localize('recoveryModel', 'Recovery Model'), + value: 'recoveryModel' + }, + { + displayName: nls.localize('lastDatabaseBackup', 'Last Database Backup'), + value: 'lastBackupDate', + ignore: [ + '1/1/0001 12:00:00 AM' + ] + }, + { + displayName: nls.localize('lastLogBackup', 'Last Log Backup'), + value: 'lastLogBackupDate', + ignore: [ + '1/1/0001 12:00:00 AM' + ] + }, + { + displayName: nls.localize('compatibilityLevel', 'Compatibility Level'), + value: 'compatibilityLevel' + }, + { + displayName: nls.localize('owner', 'Owner'), + value: 'owner' + } + ], + serverProperties: [ + { + displayName: nls.localize('version', 'Version'), + value: 'serverVersion' + }, + { + displayName: nls.localize('edition', 'Edition'), + value: 'serverEdition' + }, + { + displayName: nls.localize('computerName', 'Computer Name'), + value: 'machineName' + }, + { + displayName: nls.localize('osVersion', 'OS Version'), + value: 'osVersion' + } + ] + }, + { + flavor: 'cloud', + condition: { + field: 'isCloud', + operator: '==', + value: true + }, + databaseProperties: [ + { + displayName: azureEditionDisplayName, + value: 'azureEdition' + }, + { + displayName: nls.localize('serviceLevelObjective', 'Pricing Tier'), + value: 'serviceLevelObjective' + }, + { + displayName: nls.localize('compatibilityLevel', 'Compatibility Level'), + value: 'compatibilityLevel' + }, + { + displayName: nls.localize('owner', 'Owner'), + value: 'owner' + } + ], + serverProperties: [ + { + displayName: nls.localize('version', 'Version'), + value: 'serverVersion' + }, + { + displayName: azureType, + value: 'serverEdition' + } + ] + } + ] + }, +]; \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/properties/propertiesWidget.component.html b/src/sql/parts/dashboard/widgets/properties/propertiesWidget.component.html new file mode 100644 index 0000000000..269eb47f69 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/properties/propertiesWidget.component.html @@ -0,0 +1,21 @@ + +
+
+ + + +
{{item.displayName}}
+
{{item.value}}
+
+
+
+
+ + ... + +
diff --git a/src/sql/parts/dashboard/widgets/properties/propertiesWidget.component.ts b/src/sql/parts/dashboard/widgets/properties/propertiesWidget.component.ts new file mode 100644 index 0000000000..9efb01809d --- /dev/null +++ b/src/sql/parts/dashboard/widgets/properties/propertiesWidget.component.ts @@ -0,0 +1,265 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Component, Inject, forwardRef, ChangeDetectorRef, OnInit, ElementRef, OnDestroy, ViewChild } from '@angular/core'; + +import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget'; +import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils'; +import { error } from 'sql/base/common/log'; + +import { properties } from './propertiesJson'; + +import { DatabaseInfo, ServerInfo } from 'data'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { EventType, addDisposableListener } from 'vs/base/browser/dom'; +import * as types from 'vs/base/common/types'; +import * as nls from 'vs/nls'; + +export interface PropertiesConfig { + properties: Array; +} + +export interface FlavorProperties { + flavor: string; + condition?: { + field: string; + operator: '==' | '<=' | '>=' | '!='; + value: string | boolean; + }; + databaseProperties: Array; + serverProperties: Array; +} + +export interface ProviderProperties { + provider: string; + flavors: Array; +} + +export interface Property { + displayName: string; + value: string; + ignore?: Array; + default?: string; +} + +export interface DisplayProperty { + displayName: string; + value: string; +} + +@Component({ + selector: 'properties-widget', + templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/widgets/properties/propertiesWidget.component.html')) +}) +export class PropertiesWidgetComponent extends DashboardWidget implements IDashboardWidget, OnInit, OnDestroy { + private _connection: ConnectionManagementInfo; + private _databaseInfo: DatabaseInfo; + private _clipped: boolean; + private _disposables: Array = []; + private properties: Array; + private _hasInit = false; + + @ViewChild('child', { read: ElementRef }) private _child: ElementRef; + @ViewChild('parent', { read: ElementRef }) private _parent: ElementRef; + + constructor( + @Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface, + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef, + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(WIDGET_CONFIG) protected _config: WidgetConfig, + consoleError?: ((message?: any, ...optionalParams: any[]) => void) + ) { + super(); + if (consoleError) { + this.consoleError = consoleError; + } + this.init(); + } + + ngOnInit() { + this._hasInit = true; + this._disposables.push(addDisposableListener(window, EventType.RESIZE, () => this.handleClipping())); + this._changeRef.detectChanges(); + } + + ngOnDestroy() { + this._disposables.forEach(i => i.dispose()); + } + + public refresh(): void { + this.init(); + } + + private init(): void { + this._connection = this._bootstrap.connectionManagementService.connectionInfo; + this._disposables.push(toDisposableSubscription(this._bootstrap.adminService.databaseInfo.subscribe(data => { + this._databaseInfo = data; + this._changeRef.detectChanges(); + this.parseProperties(); + if (this._hasInit) { + this.handleClipping(); + } + }, error => { + (this._el.nativeElement).innerText = nls.localize('dashboard.properties.error', "Unable to load dashboard properties"); + }))); + } + + private handleClipping(): void { + if (this._child.nativeElement.offsetWidth > this._parent.nativeElement.offsetWidth) { + this._clipped = true; + } else { + this._clipped = false; + } + this._changeRef.detectChanges(); + } + + private parseProperties() { + let provider = this._config.provider; + + let propertyArray: Array; + + // if config exists use that, otherwise use default + if (this._config.widget['properties-widget'] && this._config.widget['properties-widget'].properties) { + let config = this._config.widget['properties-widget']; + propertyArray = config.properties; + } else { + let propertiesConfig: Array = properties; + // ensure we have a properties file + if (!Array.isArray(propertiesConfig)) { + this.consoleError('Could not load properties JSON'); + return; + } + + // filter the properties provided based on provider name + let providerPropertiesArray = propertiesConfig.filter((item) => { + return item.provider === provider; + }); + + // Error handling on provider + if (providerPropertiesArray.length === 0) { + this.consoleError('Could not locate properties for provider: ', provider); + return; + } else if (providerPropertiesArray.length > 1) { + this.consoleError('Found multiple property definitions for provider ', provider); + return; + } + + let providerProperties = providerPropertiesArray[0]; + + let flavor: FlavorProperties; + + // find correct flavor + if (providerProperties.flavors.length === 1) { + flavor = providerProperties.flavors[0]; + } else if (providerProperties.flavors.length === 0) { + this.consoleError('No flavor definitions found for "', provider, + '. If there are not multiple flavors of this provider, add one flavor without a condition'); + return; + } else { + let flavorArray = providerProperties.flavors.filter((item) => { + let condition = this._connection.serverInfo[item.condition.field]; + switch (item.condition.operator) { + case '==': + return condition === item.condition.value; + case '!=': + return condition !== item.condition.value; + case '>=': + return condition >= item.condition.value; + case '<=': + return condition <= item.condition.value; + default: + this.consoleError('Could not parse operator: "', item.condition.operator, + '" on item "', item, '"'); + return false; + } + }); + + if (flavorArray.length === 0) { + this.consoleError('Could not determine flavor'); + return; + } else if (flavorArray.length > 1) { + this.consoleError('Multiple flavors matched correctly for this provider', provider); + return; + } + + flavor = flavorArray[0]; + } + + // determine what context we should be pulling from + if (this._config.context === 'database') { + if (!Array.isArray(flavor.databaseProperties)) { + this.consoleError('flavor', flavor.flavor, ' does not have a definition for database properties'); + } + + if (!Array.isArray(flavor.serverProperties)) { + this.consoleError('flavor', flavor.flavor, ' does not have a definition for server properties'); + } + + propertyArray = flavor.databaseProperties; + } else { + if (!Array.isArray(flavor.serverProperties)) { + this.consoleError('flavor', flavor.flavor, ' does not have a definition for server properties'); + } + + propertyArray = flavor.serverProperties; + } + } + + + let infoObject: ServerInfo | {}; + if (this._config.context === 'database') { + if (this._databaseInfo && this._databaseInfo.options) { + infoObject = this._databaseInfo.options; + } + } else { + infoObject = this._connection.serverInfo; + } + + // iterate over properties and display them + this.properties = []; + for (let i = 0; i < propertyArray.length; i++) { + let property = propertyArray[i]; + let assignProperty = {}; + let propertyObject = this.getValueOrDefault(infoObject, property.value, property.default || '--'); + + // make sure the value we got shouldn't be ignored + if (property.ignore !== undefined && propertyObject !== '--') { + for (let j = 0; j < property.ignore.length; j++) { + // set to default value if we should be ignoring it's value + if (propertyObject === property.ignore[0]) { + propertyObject = property.default || '--'; + break; + } + } + } + assignProperty['displayName'] = property.displayName; + assignProperty['value'] = propertyObject; + this.properties.push(assignProperty); + } + + if (this._hasInit) { + this._changeRef.detectChanges(); + } + } + + private getValueOrDefault(infoObject: ServerInfo | {}, propertyValue: string, defaultVal?: any): T { + let val: T = undefined; + if (infoObject) { + val = infoObject[propertyValue]; + } + if (types.isUndefinedOrNull(val)) { + val = defaultVal; + } + return val; + } + + // overwrittable console.error for testing + private consoleError(message?: any, ...optionalParams: any[]): void { + error(message, optionalParams); + } +} \ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.html b/src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.html new file mode 100644 index 0000000000..8234e14c96 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.html @@ -0,0 +1,21 @@ + +
+ +
+
+ +
{{task.label}}
+
+
+
+
\ No newline at end of file diff --git a/src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.ts b/src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.ts new file mode 100644 index 0000000000..9a07190d64 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/tasks/tasksWidget.component.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/media/icons/common-icons'; + +/* Node Modules */ +import { Component, Inject, forwardRef, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; + +/* SQL imports */ +import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget'; +import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; +import { ITaskRegistry, Extensions, ActionICtor } from 'sql/platform/tasks/taskRegistry'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ITaskActionContext } from 'sql/workbench/common/actions'; + +/* VS imports */ +import { IDisposable } from 'vs/base/common/lifecycle'; +import * as themeColors from 'vs/workbench/common/theme'; +import * as colors from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant, ICssStyleCollector, ITheme } from 'vs/platform/theme/common/themeService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Action } from 'vs/base/common/actions'; +import Severity from 'vs/base/common/severity'; +import * as nls from 'vs/nls'; + +interface IConfig { + tasks: Array; +} + +@Component({ + selector: 'tasks-widget', + templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/widgets/tasks/tasksWidget.component.html')) +}) +export class TasksWidget extends DashboardWidget implements IDashboardWidget, OnInit, OnDestroy { + private _size: number = 100; + private _margins: number = 10; + private _rows: number = 2; + private _isAzure = false; + private _themeDispose: IDisposable; + private _actions: Array = []; + private _profile: IConnectionProfile; + + constructor( + @Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface, + @Inject(forwardRef(() => DomSanitizer)) private _sanitizer: DomSanitizer, + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeref: ChangeDetectorRef, + @Inject(WIDGET_CONFIG) protected _config: WidgetConfig + ) { + super(); + this._profile = this._bootstrap.connectionManagementService.connectionInfo.connectionProfile; + let registry = Registry.as(Extensions.TaskContribution); + let tasksConfig = Object.values(this._config.widget)[0]; + let connInfo = this._bootstrap.connectionManagementService.connectionInfo; + if (tasksConfig.tasks) { + Object.keys(tasksConfig.tasks).forEach((item) => { + if (registry.idToCtorMap[item]) { + let ctor = registry.idToCtorMap[item]; + this._actions.push(this._bootstrap.instantiationService.createInstance(ctor, ctor.ID, ctor.LABEL, ctor.ICON)); + } else { + this._bootstrap.messageService.show(Severity.Warning, nls.localize('missingTask', 'Could not find task {0}; are you missing an extension?', item)); + } + }); + } else { + let actions = Object.values(registry.idToCtorMap).map((item: ActionICtor) => { + + let action = this._bootstrap.instantiationService.createInstance(item, item.ID, item.LABEL, item.ICON); + if (this._bootstrap.CapabilitiesService.isFeatureAvailable(action, connInfo)) { + return action; + } else { + return undefined; + } + }); + + this._actions = actions.filter(x => x !== undefined); + } + + this._isAzure = connInfo.serverInfo.isCloud; + } + + ngOnInit() { + this._themeDispose = registerThemingParticipant(this.registerThemeing); + } + + private registerThemeing(theme: ITheme, collector: ICssStyleCollector) { + let contrastBorder = theme.getColor(colors.contrastBorder); + let sideBarColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND); + if (contrastBorder) { + let contrastBorderString = contrastBorder.toString(); + collector.addRule(`.task-widget .task-tile { border: 1px solid ${contrastBorderString} }`); + } else { + let sideBarColorString = sideBarColor.toString(); + collector.addRule(`.task-widget .task-tile { background-color: ${sideBarColorString} }`); + } + } + + ngOnDestroy() { + this._themeDispose.dispose(); + } + + //tslint:disable-next-line + private calculateTransform(index: number): string { + let marginy = (1 + (index % this._rows)) * this._margins; + let marginx = (1 + (Math.floor(index / 2))) * this._margins; + let posx = (this._size * (Math.floor(index / 2))) + marginx; + let posy = (this._size * (index % this._rows)) + marginy; + return 'translate(' + posx + 'px, ' + posy + 'px)'; + } + + public runTask(task: Action) { + let context: ITaskActionContext = { + profile: this._profile + }; + task.run(context); + } +} diff --git a/src/sql/parts/dashboard/widgets/tasks/tasksWidget.contribution.ts b/src/sql/parts/dashboard/widgets/tasks/tasksWidget.contribution.ts new file mode 100644 index 0000000000..3d0d3d4427 --- /dev/null +++ b/src/sql/parts/dashboard/widgets/tasks/tasksWidget.contribution.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { registerDashboardWidget } from 'sql/platform/dashboard/common/widgetRegistry'; +import { Extensions as TaskExtensions, ITaskRegistry } from 'sql/platform/tasks/taskRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; + +let taskRegistry = Registry.as(TaskExtensions.TaskContribution); + +let tasksSchema: IJSONSchema = { + type: 'object', + properties: { + tasks: { + type: 'object', + properties: taskRegistry.taskSchemas + } + } +}; + +registerDashboardWidget('tasks-widget', '', tasksSchema); \ No newline at end of file diff --git a/src/sql/parts/disasterRecovery/backup/backup.component.html b/src/sql/parts/disasterRecovery/backup/backup.component.html new file mode 100644 index 0000000000..6cf4410276 --- /dev/null +++ b/src/sql/parts/disasterRecovery/backup/backup.component.html @@ -0,0 +1,182 @@ + +
+
+
+
+ {{backupNameLabel}} +
+
+
+
+ {{recoveryModelLabel}} +
+
+
+
+ {{backupTypeLabel}} +
+
+
+
{{copyOnlyLabel}} +
+
+ {{backupDeviceLabel}} +
+
+
+
+
+ + + + + +
+
+
+
+
+ + +
+
+ + + +
+
+
+
+ + +
\ No newline at end of file diff --git a/src/sql/parts/disasterRecovery/backup/backup.component.ts b/src/sql/parts/disasterRecovery/backup/backup.component.ts new file mode 100644 index 0000000000..7f1269e419 --- /dev/null +++ b/src/sql/parts/disasterRecovery/backup/backup.component.ts @@ -0,0 +1,921 @@ +/*--------------------------------------------------------------------------------------------- +* 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!sql/parts/disasterRecovery/backup/media/backupDialog'; + +import { ElementRef, Component, Inject, forwardRef, ViewChild, ChangeDetectorRef } from '@angular/core'; +import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox'; +import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; +import { ListBox } from 'sql/base/browser/ui/listBox/listBox'; +import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox'; +import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper'; +import { ModalFooterStyle } from 'sql/base/browser/ui/modal/modal'; +import { attachListBoxStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'sql/common/theme/styler'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import * as BackupConstants from 'sql/parts/disasterRecovery/backup/constants'; +import { IDisasterRecoveryService, IDisasterRecoveryUiService, TaskExecutionMode } from 'sql/parts/disasterRecovery/common/interfaces'; +import FileValidationConstants = require('sql/parts/fileBrowser/common/fileValidationServiceConstants'); +import { FileBrowserDialog } from 'sql/parts/fileBrowser/fileBrowserDialog'; +import { DashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; +import { Button } from 'vs/base/browser/ui/button/button'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import { attachButtonStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; +import { localize } from 'vs/nls'; +import * as DOM from 'vs/base/browser/dom'; +import * as types from 'vs/base/common/types'; + +export const BACKUP_SELECTOR: string = 'backup-component'; + +export class RestoreItemSource { + restoreItemLocation: string; + restoreItemDeviceType: number; + isLogicalDevice: boolean; + + constructor(location: any) { + this.restoreItemDeviceType = location.restoreItemDeviceType; + this.restoreItemLocation = location.restoreItemLocation; + this.isLogicalDevice = location.isLogicalDevice; + } +} + +interface MssqlBackupInfo { + ownerUri: string; + databaseName: string; + backupType: number; + backupComponent: number; + backupDeviceType: number; + selectedFiles: string; + backupsetName: string; + selectedFileGroup: { [path: string]: string }; + + // List of {key: backup path, value: device type} + backupPathDevices: { [path: string]: number }; + backupPathList: [string]; + isCopyOnly: boolean; + formatMedia: boolean; + initialize: boolean; + skipTapeHeader: boolean; + mediaName: string; + mediaDescription: string; + checksum: boolean; + continueAfterError: boolean; + logTruncation: boolean; + tailLogBackup: boolean; + retainDays: number; + compressionOption: number; + verifyBackupRequired: boolean; + encryptionAlgorithm: number; + encryptorType: number; + encryptorName: string; +} + +@Component({ + selector: BACKUP_SELECTOR, + templateUrl: decodeURI(require.toUrl('sql/parts/disasterRecovery/backup/backup.component.html')) +}) +export class BackupComponent { + @ViewChild('pathContainer', { read: ElementRef }) pathElement; + @ViewChild('backupTypeContainer', { read: ElementRef }) backupTypeElement; + @ViewChild('backupsetName', { read: ElementRef }) backupNameElement; + @ViewChild('compressionContainer', { read: ElementRef }) compressionElement; + @ViewChild('tlogOption', { read: ElementRef }) tlogOptionElement; + @ViewChild('algorithmContainer', { read: ElementRef }) encryptionAlgorithmElement; + @ViewChild('encryptorContainer', { read: ElementRef }) encryptorElement; + @ViewChild('mediaName', { read: ElementRef }) mediaNameElement; + @ViewChild('mediaDescription', { read: ElementRef }) mediaDescriptionElement; + @ViewChild('advancedBody', { read: ElementRef }) advancedBodyElement; + @ViewChild('advancedHeader', { read: ElementRef }) advancedHeaderElement; + @ViewChild('recoveryModelContainer', { read: ElementRef }) recoveryModelElement; + @ViewChild('backupDaysContainer', { read: ElementRef }) backupDaysElement; + @ViewChild('backupButtonContainer', { read: ElementRef }) backupButtonElement; + @ViewChild('cancelButtonContainer', { read: ElementRef }) cancelButtonElement; + @ViewChild('addPathContainer', { read: ElementRef }) addPathElement; + @ViewChild('removePathContainer', { read: ElementRef }) removePathElement; + @ViewChild('copyOnlyContainer', { read: ElementRef }) copyOnlyElement; + @ViewChild('encryptCheckContainer', { read: ElementRef }) encryptElement; + @ViewChild('encryptContainer', { read: ElementRef }) encryptContainerElement; + @ViewChild('verifyContainer', { read: ElementRef }) verifyElement; + @ViewChild('checksumContainer', { read: ElementRef }) checksumElement; + @ViewChild('continueOnErrorContainer', { read: ElementRef }) continueOnErrorElement; + @ViewChild('encryptWarningContainer', { read: ElementRef }) encryptWarningElement; + @ViewChild('inProgressContainer', { read: ElementRef }) inProgressElement; + @ViewChild('modalFooterContainer', { read: ElementRef }) modalFooterElement; + @ViewChild('scriptButtonContainer', { read: ElementRef }) scriptButtonElement; + + // tslint:disable:no-unused-variable + private readonly backupNameLabel: string = localize('backup.backupName', 'Backup name'); + private readonly recoveryModelLabel: string = localize('backup.recoveryModel', 'Recovery model'); + private readonly backupTypeLabel: string = localize('backup.backupType', 'Backup type'); + private readonly backupDeviceLabel: string = localize('backup.backupDevice', 'Backup files'); + private readonly algorithmLabel: string = localize('backup.algorithm', 'Algorithm'); + private readonly certificateOrAsymmetricKeyLabel: string = localize('backup.certificateOrAsymmetricKey', 'Certificate or Asymmetric key'); + private readonly mediaLabel: string = localize('backup.media', 'Media'); + private readonly mediaOptionLabel: string = localize('backup.mediaOption', 'Backup to the existing media set'); + private readonly mediaOptionFormatLabel: string = localize('backup.mediaOptionFormat', 'Backup to a new media set'); + private readonly existingMediaAppendLabel: string = localize('backup.existingMediaAppend', 'Append to the existing backup set'); + private readonly existingMediaOverwriteLabel: string = localize('backup.existingMediaOverwrite', 'Overwrite all existing backup sets'); + private readonly newMediaSetNameLabel: string = localize('backup.newMediaSetName', 'New media set name'); + private readonly newMediaSetDescriptionLabel: string = localize('backup.newMediaSetDescription', 'New media set description'); + private readonly checksumContainerLabel: string = localize('backup.checksumContainer', 'Perform checksum before writing to media'); + private readonly verifyContainerLabel: string = localize('backup.verifyContainer', 'Verify backup when finished'); + private readonly continueOnErrorContainerLabel: string = localize('backup.continueOnErrorContainer', 'Continue on error'); + private readonly expirationLabel: string = localize('backup.expiration', 'Expiration'); + private readonly setBackupRetainDaysLabel: string = localize('backup.setBackupRetainDays', 'Set backup retain days'); + private readonly copyOnlyLabel: string = localize('backup.copyOnly', 'Copy-only backup'); + private readonly advancedConfigurationLabel: string = localize('backup.advancedConfiguration', 'Advanced Configuration'); + private readonly compressionLabel: string = localize('backup.compression', 'Compression'); + private readonly setBackupCompressionLabel: string = localize('backup.setBackupCompression', 'Set backup compression'); + private readonly encryptionLabel: string = localize('backup.encryption', 'Encryption'); + private readonly transactionLogLabel: string = localize('backup.transactionLog', 'Transaction log'); + private readonly reliabilityLabel: string = localize('backup.reliability', 'Reliability'); + private readonly mediaNameRequiredError: string = localize('backup.mediaNameRequired', 'Media name is required'); + private readonly noEncryptorWarning: string = localize('backup.noEncryptorWarning', "No certificate or asymmetric key is available"); + + // tslint:enable:no-unused-variable + + private _disasterRecoveryService: IDisasterRecoveryService; + private _disasterRecoveryUiService: IDisasterRecoveryUiService; + private _uri: string; + private _toDispose: lifecycle.IDisposable[] = []; + + private connection: IConnectionProfile; + private databaseName: string; + private defaultNewBackupFolder: string; + private recoveryModel: string; + private backupEncryptors; + private containsBackupToUrl: boolean; + private fileBrowserDialog: FileBrowserDialog; + private errorMessage: string; + + // UI element disable flag + private disableFileComponent: boolean; + private disableTlog: boolean; + + private selectedBackupComponent: string; + private selectedFilesText: string; + private selectedInitOption: string; + private isTruncateChecked: boolean; + private isTaillogChecked: boolean; + private isFormatChecked: boolean; + private isEncryptChecked: boolean; + // Key: backup path, Value: device type + private backupPathTypePairs: { [path: string]: number }; + + private compressionOptions = [BackupConstants.defaultCompression, BackupConstants.compressionOn, BackupConstants.compressionOff]; + private encryptionAlgorithms = [BackupConstants.aes128, BackupConstants.aes192, BackupConstants.aes256, BackupConstants.tripleDES]; + private existingMediaOptions = ['append', 'overwrite']; + private backupTypeOptions: string[]; + + private backupTypeSelectBox: SelectBox; + private backupNameBox: InputBox; + private recoveryBox: InputBox; + private backupRetainDaysBox: InputBox; + private backupButton: Button; + private cancelButton: Button; + private scriptButton: Button; + private addPathButton: Button; + private removePathButton: Button; + private pathListBox: ListBox; + private compressionSelectBox: SelectBox; + private algorithmSelectBox: SelectBox; + private encryptorSelectBox: SelectBox; + private mediaNameBox: InputBox; + private mediaDescriptionBox: InputBox; + private copyOnlyCheckBox: Checkbox; + private encryptCheckBox: Checkbox; + private verifyCheckBox: Checkbox; + private checksumCheckBox: Checkbox; + private continueOnErrorCheckBox: Checkbox; + + constructor( + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeDetectorRef: ChangeDetectorRef, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService, + ) { + this._disasterRecoveryService = _bootstrapService.disasterRecoveryService; + this._disasterRecoveryUiService = _bootstrapService.disasterRecoveryUiService; + this._disasterRecoveryUiService.onShowBackupEvent((param) => this.onGetBackupConfigInfo(param)); + } + + ngOnInit() { + let self = this; + + this.addFooterButtons(); + + this.recoveryBox = new InputBox(this.recoveryModelElement.nativeElement, this._bootstrapService.contextViewService, { placeholder: this.recoveryModel }); + // Set backup type + this.backupTypeSelectBox = new SelectBox([], ''); + this.backupTypeSelectBox.render(this.backupTypeElement.nativeElement); + + // Set copy-only check box + this.copyOnlyCheckBox = new Checkbox({ + actionClassName: 'backup-checkbox', + title: this.copyOnlyLabel, + isChecked: false, + onChange: (viaKeyboard) => { } + }); + + this.copyOnlyElement.nativeElement.appendChild(this.copyOnlyCheckBox.domNode); + + // Encryption checkbox + this.encryptCheckBox = new Checkbox({ + actionClassName: 'backup-checkbox', + title: 'Encryption', + isChecked: false, + onChange: (viaKeyboard) => self.onChangeEncrypt() + }); + this.encryptElement.nativeElement.appendChild(this.encryptCheckBox.domNode); + + // Verify backup checkbox + this.verifyCheckBox = new Checkbox({ + actionClassName: 'backup-checkbox', + title: 'Verify', + isChecked: false, + onChange: (viaKeyboard) => { } + }); + this.verifyElement.nativeElement.appendChild(this.verifyCheckBox.domNode); + + // Perform checksum checkbox + this.checksumCheckBox = new Checkbox({ + actionClassName: 'backup-checkbox', + title: 'Perform checksum', + isChecked: false, + onChange: (viaKeyboard) => { } + }); + this.checksumElement.nativeElement.appendChild(this.checksumCheckBox.domNode); + + // Continue on error checkbox + this.continueOnErrorCheckBox = new Checkbox({ + actionClassName: 'backup-checkbox', + title: 'Continue on error', + isChecked: false, + onChange: (viaKeyboard) => { } + }); + this.continueOnErrorElement.nativeElement.appendChild(this.continueOnErrorCheckBox.domNode); + + // Set backup name + this.backupNameBox = new InputBox(this.backupNameElement.nativeElement, this._bootstrapService.contextViewService); + this.backupNameBox.focus(); + + // Set backup path list + this.pathListBox = new ListBox([], '', this._bootstrapService.contextViewService); + this.pathListBox.render(this.pathElement.nativeElement); + + // Set backup path add/remove buttons + this.addPathButton = new Button(this.addPathElement.nativeElement); + this.addPathButton.label = '+'; + + this.removePathButton = new Button(this.removePathElement.nativeElement); + this.removePathButton.label = '-'; + + // Set compression + this.compressionSelectBox = new SelectBox(this.compressionOptions, this.compressionOptions[0]); + this.compressionSelectBox.render(this.compressionElement.nativeElement); + + // Set encryption + this.algorithmSelectBox = new SelectBox(this.encryptionAlgorithms, this.encryptionAlgorithms[0]); + this.algorithmSelectBox.render(this.encryptionAlgorithmElement.nativeElement); + this.encryptorSelectBox = new SelectBox([], ''); + this.encryptorSelectBox.render(this.encryptorElement.nativeElement); + + // Set media + this.mediaNameBox = new InputBox(this.mediaNameElement.nativeElement, + this._bootstrapService.contextViewService, + { + validationOptions: { + validation: (value: string) => !value ? ({ type: MessageType.ERROR, content: this.mediaNameRequiredError }) : null + } + }); + + this.mediaDescriptionBox = new InputBox(this.mediaDescriptionElement.nativeElement, this._bootstrapService.contextViewService); + + // Set backup retain days + let invalidInputMessage = localize('invalidInput', 'Invalid input. Value must be greater than or equal 0.'); + this.backupRetainDaysBox = new InputBox(this.backupDaysElement.nativeElement, + this._bootstrapService.contextViewService, + { + placeholder: '0', + type: 'number', + min: '0', + validationOptions: { + validation: (value: string) => { + if (types.isNumber(Number(value)) && Number(value) < 0) { + return { type: MessageType.ERROR, content: invalidInputMessage }; + } else { + return null; + } + } + } + }); + + // disable elements + this.recoveryBox.disable(); + this.mediaNameBox.disable(); + this.mediaDescriptionBox.disable(); + + this.registerListeners(); + this.updateTheme(); + } + + private onGetBackupConfigInfo(param: DashboardComponentParams) { + // Show spinner + this.showSpinner(); + this.backupEnabled = false; + + // Reset backup values + this.backupNameBox.value = ''; + this.pathListBox.setOptions([], 0); + + this.connection = param.connection; + this._uri = param.ownerUri; + + // Get backup configuration info + this._disasterRecoveryService.getBackupConfigInfo(this._uri).then(configInfo => { + if (configInfo) { + this.defaultNewBackupFolder = configInfo.defaultBackupFolder; + this.recoveryModel = configInfo.recoveryModel; + this.backupEncryptors = configInfo.backupEncryptors; + this.initialize(true); + } else { + this.initialize(false); + } + // Hide spinner + this.hideSpinner(); + }); + } + + /** + * Show spinner in the backup dialog + */ + private showSpinner(): void { + this.inProgressElement.nativeElement.style.visibility = 'visible'; + } + + /** + * Hide spinner in the backup dialog + */ + private hideSpinner(): void { + this.inProgressElement.nativeElement.style.visibility = 'hidden'; + } + + private addFooterButtons(): void { + // Set script footer button + this.scriptButton = new Button(this.scriptButtonElement.nativeElement); + this.scriptButton.label = localize('script', 'Script'); + this._toDispose.push(this.addButtonClickHandler(this.scriptButton, () => this.onScript())); + this._toDispose.push(attachButtonStyler(this.scriptButton, this._bootstrapService.themeService)); + this.scriptButton.enabled = false; + + // Set backup footer button + this.backupButton = new Button(this.backupButtonElement.nativeElement); + this.backupButton.label = localize('backup', 'Backup'); + this._toDispose.push(this.addButtonClickHandler(this.backupButton, () => this.onOk())); + + this._toDispose.push(attachButtonStyler(this.backupButton, this._bootstrapService.themeService)); + this.backupEnabled = false; + + // Set cancel footer button + this.cancelButton = new Button(this.cancelButtonElement.nativeElement); + this.cancelButton.label = localize('cancel', 'Cancel'); + this._toDispose.push(this.addButtonClickHandler(this.cancelButton, () => this.onCancel())); + this._toDispose.push(attachButtonStyler(this.cancelButton, this._bootstrapService.themeService)); + } + + private initialize(isMetadataPopulated: boolean): void { + this.databaseName = this.connection.databaseName; + this.selectedBackupComponent = BackupConstants.labelDatabase; + this.backupPathTypePairs = {}; + this.isFormatChecked = false; + this.isEncryptChecked = false; + this.selectedInitOption = this.existingMediaOptions[0]; + this.backupTypeOptions = []; + + if (isMetadataPopulated) { + this.backupEnabled = true; + + // Set recovery model + this.setControlsForRecoveryModel(); + + // Set backup type + this.backupTypeSelectBox.setOptions(this.backupTypeOptions, 0); + + this.setDefaultBackupName(); + this.backupNameBox.focus(); + + // Set backup path list + this.setDefaultBackupPaths(); + var pathlist = []; + for (var i in this.backupPathTypePairs) { + pathlist.push(i); + } + this.pathListBox.setOptions(pathlist, 0); + + // Set encryption + var encryptorItems = this.populateEncryptorCombo(); + this.encryptorSelectBox.setOptions(encryptorItems, 0); + + if (encryptorItems.length === 0) { + // Disable encryption checkbox + this.encryptCheckBox.disable(); + + // Show warning instead of algorithm select boxes + (this.encryptWarningElement.nativeElement).style.display = 'inline'; + (this.encryptContainerElement.nativeElement).style.display = 'none'; + } + else { + // Show algorithm select boxes instead of warning + (this.encryptWarningElement.nativeElement).style.display = 'none'; + (this.encryptContainerElement.nativeElement).style.display = 'inline'; + + // Disable the algorithm select boxes since encryption is not checked by default + this.setEncryptOptionsEnabled(false); + } + + this.setTLogOptions(); + + // disable elements + this.recoveryBox.disable(); + this.mediaNameBox.disable(); + this.mediaDescriptionBox.disable(); + this.recoveryBox.value = this.recoveryModel; + + // show warning message if latest backup file path contains url + if (this.containsBackupToUrl) { + this.pathListBox.setValidation(false, { content: localize('backup.containsBackupToUrlError', 'Only backup to file is supported'), type: MessageType.WARNING }); + this.pathListBox.focus(); + } + } + + this._changeDetectorRef.detectChanges(); + } + + /** + * Reset dialog controls to their initial state. + */ + private resetDialog(): void { + this.isFormatChecked = false; + this.isEncryptChecked = false; + + this.copyOnlyCheckBox.checked = false; + this.copyOnlyCheckBox.enable(); + this.compressionSelectBox.setOptions(this.compressionOptions, 0); + this.encryptCheckBox.checked = false; + this.encryptCheckBox.enable(); + this.onChangeEncrypt(); + + this.mediaNameBox.value = ''; + this.mediaDescriptionBox.value = ''; + this.checksumCheckBox.checked = false; + this.verifyCheckBox.checked = false; + this.continueOnErrorCheckBox.checked = false; + this.backupRetainDaysBox.value = '0'; + this.algorithmSelectBox.setOptions(this.encryptionAlgorithms, 0); + this.selectedInitOption = this.existingMediaOptions[0]; + this.collapseAdvancedOptions(); + this.containsBackupToUrl = false; + this.pathListBox.setValidation(true); + + this.cancelButton.applyStyles(); + this.scriptButton.applyStyles(); + this.backupButton.applyStyles(); + } + + private registerListeners(): void { + // Theme styler + this._toDispose.push(attachInputBoxStyler(this.backupNameBox, this._bootstrapService.themeService)); + this._toDispose.push(attachInputBoxStyler(this.recoveryBox, this._bootstrapService.themeService)); + this._toDispose.push(attachSelectBoxStyler(this.backupTypeSelectBox, this._bootstrapService.themeService)); + this._toDispose.push(attachListBoxStyler(this.pathListBox, this._bootstrapService.themeService)); + this._toDispose.push(attachButtonStyler(this.addPathButton, this._bootstrapService.themeService)); + this._toDispose.push(attachButtonStyler(this.removePathButton, this._bootstrapService.themeService)); + this._toDispose.push(attachSelectBoxStyler(this.compressionSelectBox, this._bootstrapService.themeService)); + this._toDispose.push(attachSelectBoxStyler(this.algorithmSelectBox, this._bootstrapService.themeService)); + this._toDispose.push(attachSelectBoxStyler(this.encryptorSelectBox, this._bootstrapService.themeService)); + this._toDispose.push(attachInputBoxStyler(this.mediaNameBox, this._bootstrapService.themeService)); + this._toDispose.push(attachInputBoxStyler(this.mediaDescriptionBox, this._bootstrapService.themeService)); + this._toDispose.push(attachInputBoxStyler(this.backupRetainDaysBox, this._bootstrapService.themeService)); + this._toDispose.push(attachCheckboxStyler(this.copyOnlyCheckBox, this._bootstrapService.themeService)); + this._toDispose.push(attachCheckboxStyler(this.encryptCheckBox, this._bootstrapService.themeService)); + this._toDispose.push(attachCheckboxStyler(this.verifyCheckBox, this._bootstrapService.themeService)); + this._toDispose.push(attachCheckboxStyler(this.checksumCheckBox, this._bootstrapService.themeService)); + this._toDispose.push(attachCheckboxStyler(this.continueOnErrorCheckBox, this._bootstrapService.themeService)); + + this._toDispose.push(this.backupTypeSelectBox.onDidSelect(selected => this.onBackupTypeChanged())); + this._toDispose.push(this.addButtonClickHandler(this.addPathButton, () => this.onAddClick())); + this._toDispose.push(this.addButtonClickHandler(this.removePathButton, () => this.onRemoveClick())); + this._toDispose.push(this.mediaNameBox.onDidChange(mediaName => { + this.mediaNameChanged(mediaName); + })); + this._toDispose.push(this.backupRetainDaysBox.onDidChange(days => { + this.backupRetainDaysChanged(days); + })); + + this._toDispose.push(this._bootstrapService.themeService.onDidColorThemeChange(e => this.updateTheme())); + } + + // Update theming that is specific to backup dialog + private updateTheme(): void { + // set modal footer style + let footerHtmlElement: HTMLElement = this.modalFooterElement.nativeElement; + footerHtmlElement.style.backgroundColor = ModalFooterStyle.backgroundColor; + footerHtmlElement.style.borderTopWidth = ModalFooterStyle.borderTopWidth; + footerHtmlElement.style.borderTopStyle = ModalFooterStyle.borderTopStyle; + footerHtmlElement.style.borderTopColor = ModalFooterStyle.borderTopColor; + } + + private addButtonClickHandler(button: Button, handler: () => void): lifecycle.IDisposable { + if (button && handler) { + return DOM.addDisposableListener(button.getElement(), DOM.EventType.CLICK, () => { + if (button.enabled) { + handler(); + } + }); + } else { + return undefined; + } + } + + /* + * UI event handlers + */ + private onScript(): void { + this._disasterRecoveryService.backup(this._uri, this.createBackupInfo(), TaskExecutionMode.script); + this.close(); + } + + private onOk(): void { + this._disasterRecoveryService.backup(this._uri, this.createBackupInfo(), TaskExecutionMode.executeAndScript); + this.close(); + } + + private onCancel(): void { + this.close(); + this._bootstrapService.connectionManagementService.disconnect(this._uri); + } + + private close(): void { + this._disasterRecoveryUiService.closeBackup(); + this.resetDialog(); + } + + private onChangeTlog(): void { + this.isTruncateChecked = !this.isTruncateChecked; + this.isTaillogChecked = !this.isTaillogChecked; + this.detectChange(); + } + + private onChangeEncrypt(): void { + if (this.encryptCheckBox.checked) { + this.setEncryptOptionsEnabled(true); + + // Force to choose format media option since otherwise encryption cannot be done + if (!this.isFormatChecked) { + this.onChangeMediaFormat(); + } + } else { + this.setEncryptOptionsEnabled(false); + } + this.isEncryptChecked = this.encryptCheckBox.checked; + this.detectChange(); + } + + private onChangeMediaFormat(): void { + this.isFormatChecked = !this.isFormatChecked; + this.enableMediaInput(this.isFormatChecked); + if (this.isFormatChecked) { + if (DialogHelper.isNullOrWhiteSpace(this.mediaNameBox.value)) { + this.backupEnabled = false; + this.backupButton.enabled = false; + this.mediaNameBox.showMessage({ type: MessageType.ERROR, content: this.mediaNameRequiredError }); + } + } else { + this.enableBackupButton(); + } + this.detectChange(); + } + + private set backupEnabled(value: boolean) { + this.backupButton.enabled = value; + this.scriptButton.enabled = value; + } + + private onAdvancedClick(): void { + if (this.advancedHeaderElement.nativeElement.style['aria-expanded']) { + // collapse + this.collapseAdvancedOptions(); + } else { + // expand + this.expandAdvancedOptions(); + } + + this.detectChange(); + } + + private collapseAdvancedOptions() { + this.advancedHeaderElement.nativeElement.className = 'header collapsible collapsed'; + this.advancedBodyElement.nativeElement.style = 'display: none'; + this.advancedHeaderElement.nativeElement.style['aria-expanded'] = false; + } + + + private expandAdvancedOptions() { + this.advancedHeaderElement.nativeElement.className = 'header collapsible'; + this.advancedBodyElement.nativeElement.style = 'display: inline'; + this.advancedHeaderElement.nativeElement.style['aria-expanded'] = true; + } + + private onBackupTypeChanged(): void { + if (this.getSelectedBackupType() === BackupConstants.labelDifferential) { + this.copyOnlyCheckBox.checked = false; + this.copyOnlyCheckBox.disable(); + } else { + this.copyOnlyCheckBox.enable(); + } + + this.setTLogOptions(); + this.setDefaultBackupName(); + this._changeDetectorRef.detectChanges(); + } + + private onAddClick(): void { + this._bootstrapService.fileBrowserDialogService.showDialog(this._uri, + this.defaultNewBackupFolder, + BackupConstants.fileFiltersSet, + FileValidationConstants.backup, + false, + (filepath => this.handlePathAdded(filepath))); + } + + private handlePathAdded(filepath: string) { + if (filepath && !this.backupPathTypePairs[filepath]) { + if ((this.getBackupPathCount() < BackupConstants.maxDevices)) { + this.backupPathTypePairs[filepath] = BackupConstants.deviceTypeFile; + this.pathListBox.add(filepath); + this.enableBackupButton(); + this.enableAddRemoveButtons(); + + // stop showing error message if the list content was invalid due to no file path + if (!this.pathListBox.isContentValid && this.pathListBox.count === 1) { + this.pathListBox.setValidation(true); + } + + this._changeDetectorRef.detectChanges(); + } + } + } + + private onRemoveClick(): void { + let self = this; + this.pathListBox.selectedOptions.forEach(selected => { + if (self.backupPathTypePairs[selected]) { + if (self.backupPathTypePairs[selected] === BackupConstants.deviceTypeURL) { + // stop showing warning message since url path is getting removed + this.pathListBox.setValidation(true); + this.containsBackupToUrl = false; + } + + delete self.backupPathTypePairs[selected]; + } + }); + + this.pathListBox.remove(); + if (this.pathListBox.count === 0) { + this.backupEnabled = false; + + // show input validation error + this.pathListBox.setValidation(false, { content: localize('backup.backupFileRequired', 'Backup file path is required'), type: MessageType.ERROR }); + this.pathListBox.focus(); + } + + this.enableAddRemoveButtons(); + this._changeDetectorRef.detectChanges(); + } + + private enableAddRemoveButtons(): void { + if (this.pathListBox.count === 0) { + this.removePathButton.enabled = false; + } else if (this.pathListBox.count === BackupConstants.maxDevices) { + this.addPathButton.enabled = false; + } else { + this.removePathButton.enabled = true; + this.addPathButton.enabled = true; + } + } + + /* + * Helper methods + */ + private setControlsForRecoveryModel(): void { + if (this.recoveryModel === BackupConstants.recoveryModelSimple) { + this.selectedBackupComponent = BackupConstants.labelDatabase; + this.disableFileComponent = true; + } else { + this.disableFileComponent = false; + } + + this.populateBackupTypes(); + } + + private populateBackupTypes(): void { + this.backupTypeOptions.push(BackupConstants.labelFull); + if (this.databaseName !== 'master') { + this.backupTypeOptions.push(BackupConstants.labelDifferential); + if (this.recoveryModel !== BackupConstants.recoveryModelSimple) { + this.backupTypeOptions.push(BackupConstants.labelLog); + } + } + } + + private populateEncryptorCombo(): string[] { + var encryptorCombo = []; + this.backupEncryptors.forEach((encryptor) => { + var encryptorTypeStr = (encryptor.encryptorType === 0 ? BackupConstants.serverCertificate : BackupConstants.asymmetricKey); + encryptorCombo.push(encryptor.encryptorName + '(' + encryptorTypeStr + ')'); + }); + return encryptorCombo; + } + + private setDefaultBackupName(): void { + let utc = new Date().toJSON().slice(0, 19); + if (this.backupNameBox) { + this.backupNameBox.value = this.databaseName + '-' + this.getSelectedBackupType() + '-' + utc; + } + } + + private setDefaultBackupPaths(): void { + if (this.defaultNewBackupFolder && this.defaultNewBackupFolder.length > 0) { + + // TEMPORARY WORKAROUND: karlb 5/27 - try to guess path separator on server based on first character in path + let serverPathSeparator: string = '\\'; + if (this.defaultNewBackupFolder[0] === '/') { + serverPathSeparator = '/'; + } + let d: Date = new Date(); + let formattedDateTime: string = `-${d.getFullYear()}${d.getMonth() + 1}${d.getDate()}-${d.getHours()}-${d.getMinutes()}-${d.getSeconds()}`; + let defaultNewBackupLocation = this.defaultNewBackupFolder + serverPathSeparator + this.databaseName + formattedDateTime + '.bak'; + + // Add a default new backup location + this.backupPathTypePairs[defaultNewBackupLocation] = BackupConstants.deviceTypeFile; + } + } + + private isBackupToFile(controllerType: number): boolean { + let isfile = false; + if (controllerType === 102) { + isfile = true; + } else if (controllerType === 105) { + isfile = false; + } else if (controllerType === BackupConstants.backupDeviceTypeDisk) { + isfile = true; + } else if (controllerType === BackupConstants.backupDeviceTypeTape || controllerType === BackupConstants.backupDeviceTypeURL) { + isfile = false; + } + return isfile; + } + + private enableMediaInput(enable: boolean): void { + if (enable) { + this.mediaNameBox.enable(); + this.mediaDescriptionBox.enable(); + } else { + this.mediaNameBox.disable(); + this.mediaDescriptionBox.disable(); + } + } + + private detectChange(): void { + this._changeDetectorRef.detectChanges(); + } + + private setTLogOptions(): void { + if (this.getSelectedBackupType() === BackupConstants.labelLog) { + // Enable log options + this.disableTlog = false; + // Choose the default option + this.isTruncateChecked = true; + } else { + // Unselect log options + this.isTruncateChecked = false; + this.isTaillogChecked = false; + // Disable log options + this.disableTlog = true; + } + } + + private getBackupTypeNumber(): number { + let backupType; + switch (this.getSelectedBackupType()) { + case BackupConstants.labelFull: + backupType = 0; + break; + case BackupConstants.labelDifferential: + backupType = 1; + break; + case BackupConstants.labelLog: + backupType = 2; + break; + } + return backupType; + } + + private getBackupPathCount(): number { + return this.pathListBox.count; + } + + private getSelectedBackupType(): string { + let backupType = ''; + if (this.backupTypeSelectBox) { + backupType = this.backupTypeSelectBox.value; + } + return backupType; + } + + private enableBackupButton(): void { + if (!this.backupButton.enabled) { + if (this.pathListBox.count > 0 && (!this.isFormatChecked || this.mediaNameBox.value) && this.backupRetainDaysBox.validate()) { + this.backupEnabled = true; + } + } + } + + private setEncryptOptionsEnabled(enabled: boolean): void { + if (enabled) { + this.algorithmSelectBox.enable(); + this.encryptorSelectBox.enable(); + } else { + this.algorithmSelectBox.disable(); + this.encryptorSelectBox.disable(); + } + } + + private mediaNameChanged(mediaName: string): void { + if (!mediaName) { + this.backupEnabled = false; + } else { + this.enableBackupButton(); + } + } + + private backupRetainDaysChanged(days: string): void { + if (!this.backupRetainDaysBox.validate()) { + this.backupEnabled = false; + } else { + this.enableBackupButton(); + } + } + + private createBackupInfo(): MssqlBackupInfo { + var backupPathArray = []; + for (var i in this.backupPathTypePairs) { + backupPathArray.push(i); + } + + // get encryptor type and name + var encryptorName = ''; + var encryptorType; + + if (this.encryptCheckBox.checked && this.encryptorSelectBox.value !== '') { + var selectedEncryptor = this.encryptorSelectBox.value; + var encryptorTypeStr = selectedEncryptor.substring(selectedEncryptor.lastIndexOf('(') + 1, selectedEncryptor.lastIndexOf(')')); + encryptorType = (encryptorTypeStr === BackupConstants.serverCertificate ? 0 : 1); + encryptorName = selectedEncryptor.substring(0, selectedEncryptor.lastIndexOf('(')); + } + + let backupInfo = { + ownerUri: this._uri, + databaseName: this.databaseName, + backupType: this.getBackupTypeNumber(), + backupComponent: 0, + backupDeviceType: BackupConstants.backupDeviceTypeDisk, + backupPathList: backupPathArray, + selectedFiles: this.selectedFilesText, + backupsetName: this.backupNameBox.value, + selectedFileGroup: undefined, + backupPathDevices: this.backupPathTypePairs, + isCopyOnly: this.copyOnlyCheckBox.checked, + + // Get advanced options + formatMedia: this.isFormatChecked, + initialize: (this.isFormatChecked ? true : (this.selectedInitOption === this.existingMediaOptions[1])), + skipTapeHeader: this.isFormatChecked, + mediaName: (this.isFormatChecked ? this.mediaNameBox.value : ''), + mediaDescription: (this.isFormatChecked ? this.mediaDescriptionBox.value : ''), + checksum: this.checksumCheckBox.checked, + continueAfterError: this.continueOnErrorCheckBox.checked, + logTruncation: this.isTruncateChecked, + tailLogBackup: this.isTaillogChecked, + retainDays: DialogHelper.isNullOrWhiteSpace(this.backupRetainDaysBox.value) ? 0 : this.backupRetainDaysBox.value, + compressionOption: this.compressionOptions.indexOf(this.compressionSelectBox.value), + verifyBackupRequired: this.verifyCheckBox.checked, + encryptionAlgorithm: (this.encryptCheckBox.checked ? this.encryptionAlgorithms.indexOf(this.algorithmSelectBox.value) : 0), + encryptorType: encryptorType, + encryptorName: encryptorName + }; + + return backupInfo; + } +} diff --git a/src/sql/parts/disasterRecovery/backup/backup.module.ts b/src/sql/parts/disasterRecovery/backup/backup.module.ts new file mode 100644 index 0000000000..f760c7b5b3 --- /dev/null +++ b/src/sql/parts/disasterRecovery/backup/backup.module.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { ApplicationRef, ComponentFactoryResolver, ModuleWithProviders, NgModule, + Inject, forwardRef } from '@angular/core'; +import { APP_BASE_HREF, CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { BackupComponent, BACKUP_SELECTOR } from 'sql/parts/disasterRecovery/backup/backup.component'; + +// work around +const BrowserAnimationsModule = ( require.__$__nodeRequire('@angular/platform-browser/animations')).BrowserAnimationsModule; + +// Backup wizard main angular module +@NgModule({ + declarations: [ + BackupComponent + ], + entryComponents: [BackupComponent], + imports: [ + FormsModule, + CommonModule, + BrowserModule, + BrowserAnimationsModule, + ], + providers: [{ provide: APP_BASE_HREF, useValue: '/' }] +}) +export class BackupModule { + + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { + } + + ngDoBootstrap(appRef: ApplicationRef) { + const factory = this._resolver.resolveComponentFactory(BackupComponent); + const uniqueSelector: string = this._bootstrapService.getUniqueSelector(BACKUP_SELECTOR); + (factory).factory.selector = uniqueSelector; + appRef.bootstrap(factory); + } +} diff --git a/src/sql/parts/disasterRecovery/backup/backupDialog.ts b/src/sql/parts/disasterRecovery/backup/backupDialog.ts new file mode 100644 index 0000000000..7c4b4bef22 --- /dev/null +++ b/src/sql/parts/disasterRecovery/backup/backupDialog.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Modal } from 'sql/base/browser/ui/modal/modal'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { BackupModule } from 'sql/parts/disasterRecovery/backup/backup.module'; +import { BACKUP_SELECTOR } from 'sql/parts/disasterRecovery/backup/backup.component'; +import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import { attachModalDialogStyler } from 'sql/common/theme/styler'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; + +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { Builder } from 'vs/base/browser/builder'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +export class BackupDialog extends Modal { + private _bodyBuilder: Builder; + private _backupTitle: string; + private _uniqueSelector: string; + private _moduleRef: any; + + constructor( + @IBootstrapService private _bootstrapService: IBootstrapService, + @IThemeService private _themeService: IThemeService, + @IPartService partService: IPartService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super('', TelemetryKeys.Backup, partService, telemetryService, contextKeyService, { isAngular: true, hasErrors: true }); + } + + protected renderBody(container: HTMLElement) { + new Builder(container).div({ 'class': 'backup-dialog' }, (builder) => { + this._bodyBuilder = builder; + }); + } + + public render() { + super.render(); + attachModalDialogStyler(this, this._themeService); + + // Add angular component template to dialog body + this.bootstrapAngular(this._bodyBuilder.getHTMLElement()); + } + + /** + * Get the bootstrap params and perform the bootstrap + */ + private bootstrapAngular(bodyContainer: HTMLElement) { + this._uniqueSelector = this._bootstrapService.bootstrap( + BackupModule, + bodyContainer, + BACKUP_SELECTOR, + undefined, + undefined, + (moduleRef) => this._moduleRef = moduleRef); + } + + public hideError() { + this.showError(''); + } + + public showError(err: string) { + this.showError(err); + } + + /* Overwrite escape key behavior */ + protected onClose() { + this.close(); + } + + /** + * Clean up the module and DOM element and close the dialog + */ + public close() { + this.hide(); + } + + public dispose(): void { + super.dispose(); + if (this._moduleRef) { + this._moduleRef.destroy(); + } + } + + /** + * Open the dialog + */ + public open(connection: IConnectionProfile) { + this._backupTitle = 'Backup database - ' + connection.serverName + ':' + connection.databaseName; + this.title = this._backupTitle; + this.show(); + } + + protected layout(height?: number): void { + // Nothing currently laid out in this class + } + +} \ No newline at end of file diff --git a/src/sql/parts/disasterRecovery/backup/constants.ts b/src/sql/parts/disasterRecovery/backup/constants.ts new file mode 100644 index 0000000000..9774c23020 --- /dev/null +++ b/src/sql/parts/disasterRecovery/backup/constants.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; + +// Constants +export const maxDevices: number = 64; + +// Constants for backup physical device type +export const backupDeviceTypeDisk = 2; +export const backupDeviceTypeTape = 5; +export const backupDeviceTypeURL = 9; + +// Constants for backup media device type +export const deviceTypeLogicalDevice = 0; +export const deviceTypeTape = 1; +export const deviceTypeFile = 2; +export const deviceTypeURL = 5; + +export const recoveryModelSimple = 'Simple'; +export const recoveryModelFull = 'Full'; + +// Constants for UI strings +export const labelDatabase = 'Database'; +export const labelFilegroup = 'Files and filegroups'; +export const labelFull = 'Full'; +export const labelDifferential = 'Differential'; +export const labelLog = 'Transaction Log'; +export const labelDisk = 'Disk'; +export const labelUrl = 'Url'; + +export const defaultCompression = 'Use the default server setting'; +export const compressionOn = 'Compress backup'; +export const compressionOff = 'Do not compress backup'; + +export const aes128 = 'AES 128'; +export const aes192 = 'AES 192'; +export const aes256 = 'AES 256'; +export const tripleDES = 'Triple DES'; + +export const serverCertificate = "Server Certificate"; +export const asymmetricKey = "Asymmetric Key"; + +export const fileFiltersSet: [ {label: string, filters: string[]} ] = [ + { label: localize('backup.filterBackupFiles', "Backup Files"), filters: ['*.bak', '*.trn', '*.log'] }, + { label: localize('backup.allFiles', "All Files"), filters: ['*'] } +]; \ No newline at end of file diff --git a/src/sql/parts/disasterRecovery/backup/media/backupDialog.css b/src/sql/parts/disasterRecovery/backup/media/backupDialog.css new file mode 100644 index 0000000000..47a4f43a4b --- /dev/null +++ b/src/sql/parts/disasterRecovery/backup/media/backupDialog.css @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +.backup-path-list { + overflow-x: auto; +} + +.backup-path-table { + border: 0px; + border-collapse: collapse; + border-spacing: 0px; +} + +.backup-path-button { + width: 22px; +} + +.backup-dialog { + height: calc(100% - 15px) +} + +.advanced-main-header { + padding-top: 20px; +} + +.advanced-header { + padding-top: 15px; + font-size: 14px; +} + +input[type="radio"] { + margin-top: -2px; + vertical-align: middle; +} + +.backup-dialog .indent { + margin-left: 7px; +} + +.backup-dialog .radio-indent { + margin-left: 5px; +} + +.backup-dialog .option { + width: 100%; + padding-bottom: 7px; +} + +.backup-dialog .icon.warning { + width: 15px; + height: 15px; + float: left; +} + +.backup-dialog .warning-message{ + padding-left: 20px; +} diff --git a/src/sql/parts/disasterRecovery/common/disasterRecoveryService.ts b/src/sql/parts/disasterRecovery/common/disasterRecoveryService.ts new file mode 100644 index 0000000000..7314d7e092 --- /dev/null +++ b/src/sql/parts/disasterRecovery/common/disasterRecoveryService.ts @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import data = require('data'); +import { IDisasterRecoveryService, TaskExecutionMode } from 'sql/parts/disasterRecovery/common/interfaces'; +import * as Constants from 'sql/common/constants'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import * as TelemetryUtils from 'sql/common/telemetryUtilities'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +export class DisasterRecoveryService implements IDisasterRecoveryService { + + public _serviceBrand: any; + private _providers: { [handle: string]: data.DisasterRecoveryProvider; } = Object.create(null); + + constructor( + @IConnectionManagementService private _connectionService: IConnectionManagementService, + @ITelemetryService private _telemetryService: ITelemetryService + ) { + } + + /** + * Get database metadata needed to populate backup UI + */ + public getBackupConfigInfo(connectionUri: string): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + if (providerId) { + let provider = this._providers[providerId]; + if (provider) { + return provider.getBackupConfigInfo(connectionUri); + } + } + return Promise.resolve(undefined); + } + + /** + * Backup a data source using the provided connection + */ + public backup(connectionUri: string, backupInfo: { [key: string]: any }, taskExecutionMode: TaskExecutionMode): Thenable { + return new Promise((resolve, reject) => { + let providerResult = this.getProvider(connectionUri); + if (providerResult) { + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.BackupCreated, { provider: providerResult.providerName }); + providerResult.provider.backup(connectionUri, backupInfo, taskExecutionMode).then(result => { + resolve(result); + }, error => { + reject(error); + }); + } else { + reject(Constants.InvalidProvider); + } + }); + } + + /** + * Gets restore config Info + */ + getRestoreConfigInfo(connectionUri: string): Thenable { + return new Promise((resolve, reject) => { + let providerResult = this.getProvider(connectionUri); + if (providerResult) { + providerResult.provider.getRestoreConfigInfo(connectionUri).then(result => { + resolve(result); + }, error => { + reject(error); + }); + } else { + reject(Constants.InvalidProvider); + } + }); + } + + /** + * Restore a data source using a backup file or database + */ + restore(connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { + return new Promise((resolve, reject) => { + let providerResult = this.getProvider(connectionUri); + if (providerResult) { + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.RestoreRequested, { provider: providerResult.providerName }); + providerResult.provider.restore(connectionUri, restoreInfo).then(result => { + resolve(result); + }, error => { + reject(error); + }); + } else { + reject(Constants.InvalidProvider); + } + }); + } + + private getProvider(connectionUri: string): { provider: data.DisasterRecoveryProvider, providerName: string } { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + if (providerId) { + return { provider: this._providers[providerId], providerName: providerId }; + } else { + return undefined; + } + } + + /** + * Gets restore plan to do the restore operation on a database + */ + getRestorePlan(connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { + return new Promise((resolve, reject) => { + let providerResult = this.getProvider(connectionUri); + if (providerResult) { + providerResult.provider.getRestorePlan(connectionUri, restoreInfo).then(result => { + resolve(result); + }, error => { + reject(error); + }); + } else { + reject(Constants.InvalidProvider); + + } + }); + } + + /** + * Cancels a restore plan + */ + cancelRestorePlan(connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { + return new Promise((resolve, reject) => { + let providerResult = this.getProvider(connectionUri); + if (providerResult) { + providerResult.provider.cancelRestorePlan(connectionUri, restoreInfo).then(result => { + resolve(result); + }, error => { + reject(error); + }); + } else { + reject(Constants.InvalidProvider); + + } + }); + } + + /** + * Register a disaster recovery provider + */ + public registerProvider(providerId: string, provider: data.DisasterRecoveryProvider): void { + this._providers[providerId] = provider; + } +} diff --git a/src/sql/parts/disasterRecovery/common/disasterRecoveryUiService.ts b/src/sql/parts/disasterRecovery/common/disasterRecoveryUiService.ts new file mode 100644 index 0000000000..8116885c67 --- /dev/null +++ b/src/sql/parts/disasterRecovery/common/disasterRecoveryUiService.ts @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import * as data from 'data'; + +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { OptionsDialog } from 'sql/base/browser/ui/modal/optionsDialog'; +import { BackupDialog } from 'sql/parts/disasterRecovery/backup/backupDialog'; +import { IDisasterRecoveryService, IDisasterRecoveryUiService, TaskExecutionMode } from 'sql/parts/disasterRecovery/common/interfaces'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import ConnectionUtils = require('sql/parts/connection/common/utils'); +import { ProviderConnectionInfo } from 'sql/parts/connection/common/providerConnectionInfo'; +import { DashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import * as Utils from 'sql/parts/connection/common/utils'; + +export class DisasterRecoveryUiService implements IDisasterRecoveryUiService { + public _serviceBrand: any; + private _backupDialogs: { [providerName: string]: BackupDialog | OptionsDialog } = {}; + private _currentProvider: string; + private _optionsMap: { [providerName: string]: data.ServiceOption[] } = {}; + private _optionValues: { [optionName: string]: any } = {}; + private _connectionUri: string; + private static _connectionUniqueId: number = 0; + + private _onShowBackupEvent: Emitter; + public get onShowBackupEvent(): Event { return this._onShowBackupEvent.event; } + + constructor( @IInstantiationService private _instantiationService: IInstantiationService, + @IPartService private _partService: IPartService, + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService, + @IDisasterRecoveryService private _disasterRecoveryService: IDisasterRecoveryService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService) { + this._onShowBackupEvent = new Emitter(); + } + + public showBackup(connection: IConnectionProfile): Promise { + let self = this; + return new Promise((resolve, reject) => { + self.showBackupDialog(connection).then(() => { + resolve(); + }, error => { + reject(); + }); + }); + } + + public showBackupDialog(connection: IConnectionProfile): TPromise { + let self = this; + self._connectionUri = ConnectionUtils.generateUri(connection); + self._currentProvider = connection.providerName; + let backupDialog = self._backupDialogs[self._currentProvider]; + if (!backupDialog) { + let capabilitiesList = this._capabilitiesService.getCapabilities(); + capabilitiesList.forEach(providerCapabilities => { + let backupFeature = providerCapabilities.features.find(feature => feature.featureName === 'backup'); + if (backupFeature && backupFeature.optionsMetadata) { + this._optionsMap[providerCapabilities.providerName] = backupFeature.optionsMetadata; + } + }); + let backupOptions = self._optionsMap[self._currentProvider]; + if (backupOptions) { + backupDialog = self._instantiationService ? self._instantiationService.createInstance( + OptionsDialog, 'Backup database - ' + connection.serverName + ':' + connection.databaseName, 'BackupOptions', undefined) : undefined; + backupDialog.onOk(() => this.handleOptionDialogClosed()); + } + else { + backupDialog = self._instantiationService ? self._instantiationService.createInstance(BackupDialog) : undefined; + } + backupDialog.render(); + self._backupDialogs[self._currentProvider] = backupDialog; + } + + let backupOptions = this._optionsMap[self._currentProvider]; + return new TPromise(() => { + if (backupOptions) { + (backupDialog as OptionsDialog).open(backupOptions, self._optionValues); + } else { + let uri = this._connectionManagementService.getConnectionId(connection) + + ProviderConnectionInfo.idSeparator + + Utils.ConnectionUriBackupIdAttributeName + + ProviderConnectionInfo.nameValueSeparator + + DisasterRecoveryUiService._connectionUniqueId; + + DisasterRecoveryUiService._connectionUniqueId++; + + // Create connection if needed + if (!this._connectionManagementService.isConnected(uri)) { + this._connectionManagementService.connect(connection, uri).then(() => { + this._onShowBackupEvent.fire({connection: connection, ownerUri: uri}); + }); + } + (backupDialog as BackupDialog).open(connection); + } + }); + } + + public closeBackup() { + let self = this; + let backupDialog = self._backupDialogs[self._currentProvider]; + if (backupDialog) { + backupDialog.close(); + } + } + + private handleOptionDialogClosed() { + this._disasterRecoveryService.backup(this._connectionUri, this._optionValues, TaskExecutionMode.executeAndScript); + } + +} diff --git a/src/sql/parts/disasterRecovery/common/interfaces.ts b/src/sql/parts/disasterRecovery/common/interfaces.ts new file mode 100644 index 0000000000..d428e55a9e --- /dev/null +++ b/src/sql/parts/disasterRecovery/common/interfaces.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import Event from 'vs/base/common/event'; +import data = require('data'); + +import { DashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; + + +export enum TaskExecutionMode { + execute = 0, + script = 1, + executeAndScript = 2, +} + +export const SERVICE_ID = 'disasterRecoveryService'; +export const UI_SERVICE_ID = 'disasterRecoveryUiService'; + +export const IDisasterRecoveryUiService = createDecorator(UI_SERVICE_ID); + +export interface IDisasterRecoveryUiService { + _serviceBrand: any; + + /** + * Show backup wizard + */ + showBackup(connection: IConnectionProfile): Promise; + + /** + * On show backup event + */ + onShowBackupEvent: Event; + + /** + * Close backup wizard + */ + closeBackup(); +} + +export const IDisasterRecoveryService = createDecorator(SERVICE_ID); + +export interface IDisasterRecoveryService { + _serviceBrand: any; + + getBackupConfigInfo(connectionUri: string): Thenable; + + /** + * Backup a data source using the provided connection + */ + backup(connectionUri: string, backupInfo: { [key: string]: any }, taskExecutionMode: data.TaskExecutionMode): Thenable; + + /** + * Register a disaster recovery provider + */ + registerProvider(providerId: string, provider: data.DisasterRecoveryProvider): void; + + /** + * Restore a data source using a backup file or database + */ + restore(connectionUri: string, restoreInfo: data.RestoreInfo): Thenable; + + /** + * Gets restore plan to do the restore operation on a database + */ + getRestorePlan(connectionUri: string, restoreInfo: data.RestoreInfo): Thenable; + + /** + * Gets restore config Info + */ + getRestoreConfigInfo(connectionUri: string): Thenable; + + /** + * Cancel restore plan + */ + cancelRestorePlan(connectionUri: string, restoreInfo: data.RestoreInfo): Thenable; +} + +export const IRestoreDialogController = createDecorator('restoreDialogService'); +export interface IRestoreDialogController { + _serviceBrand: any; + showDialog(connection: IConnectionProfile): TPromise; +} \ No newline at end of file diff --git a/src/sql/parts/disasterRecovery/restore/media/restoreDialog.css b/src/sql/parts/disasterRecovery/restore/media/restoreDialog.css new file mode 100644 index 0000000000..ae497ffb7b --- /dev/null +++ b/src/sql/parts/disasterRecovery/restore/media/restoreDialog.css @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.restore-dialog { + padding: 15px +} + +.modal .restore-dialog .dialog-label.header { + font-size: 15px; +} + +.modal .restore-dialog .new-section { + padding-bottom: 50px; +} + +.modal .restore-dialog .sub-section { + padding-left: 15px; + padding-right: 15px; +} + +.modal .restore-dialog .dialog-input-section { + display: flex; + padding-left: 15px; + padding-right: 15px; + padding-top: 10px; + padding-bottom: 10px; +} + +.modal .restore-dialog .dialog-input-section .dialog-label { + flex: 0 0 100px; + align-self: center; +} + +.modal .restore-dialog .dialog-input-section .dialog-input { + flex: 1 1 auto; +} + +.modal .restore-dialog .file-browser { + width: 30px; +} + +.modal .restore-dialog .file-browser .monaco-button { + height: 100%; +} + +.modal .restore-dialog .restore-plan-section { + width: 100%; +} + +.modal .restore-dialog .restore-list { + width: 100%; + height: 500px; + margin-bottom: 15px; + overflow: hidden; + box-sizing: border-box; +} + +.modal .restore-dialog .restore-list .backup-table-content .restore-data { + white-space: nowrap; + padding: 5px; +} + +.modal .restore-dialog .more-options-button { + width: 100%; + display: flex; + flex-direction: row-reverse; +} + +.modal .restore-dialog .more-options-button a.monaco-button.monaco-text-button { + margin-right: 15px; +} diff --git a/src/sql/parts/disasterRecovery/restore/mssqlRestoreInfo.ts b/src/sql/parts/disasterRecovery/restore/mssqlRestoreInfo.ts new file mode 100644 index 0000000000..cbd0b08923 --- /dev/null +++ b/src/sql/parts/disasterRecovery/restore/mssqlRestoreInfo.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import data = require('data'); + +export class MssqlRestoreInfo implements data.RestoreInfo { + + options: { [name: string]: any }; + taskExecutionMode: data.TaskExecutionMode; + + public constructor() { + this.options = {}; + } + + public get sessionId(): string { + return this.options['sessionId']; + } + + public set sessionId(value: string) { + this.options['sessionId'] = value; + } + + public get backupFilePaths(): string { + return this.options['backupFilePaths']; + } + + public set backupFilePaths(value: string) { + this.options['backupFilePaths'] = value; + } + + public get targetDatabaseName(): string { + return this.options['targetDatabaseName']; + } + + public set targetDatabaseName(value: string) { + this.options['targetDatabaseName'] = value; + } + + public get sourceDatabaseName(): string { + return this.options['sourceDatabaseName']; + } + + public set sourceDatabaseName(value: string) { + this.options['sourceDatabaseName'] = value; + } + + public get relocateDbFiles(): boolean { + return this.options['relocateDbFiles']; + } + + public set relocateDbFiles(value: boolean) { + this.options['relocateDbFiles'] = value; + } + + public get dataFileFolder(): string { + return this.options['dataFileFolder']; + } + + public set dataFileFolder(value: string) { + this.options['dataFileFolder'] = value; + } + + public get logFileFolder(): string { + return this.options['logFileFolder']; + } + + public set logFileFolder(value: string) { + this.options['logFileFolder'] = value; + } + + public get selectedBackupSets(): string[] { + return this.options['selectedBackupSets']; + } + + public set selectedBackupSets(value: string[]) { + this.options['selectedBackupSets'] = value; + } + + public get readHeaderFromMedia(): boolean { + return this.options['readHeaderFromMedia']; + } + + public set readHeaderFromMedia(value: boolean) { + this.options['readHeaderFromMedia'] = value; + } + + public get overwriteTargetDatabase(): boolean { + return this.options['overwriteTargetDatabase']; + } + + public set overwriteTargetDatabase(value: boolean) { + this.options['overwriteTargetDatabase'] = value; + } +} \ No newline at end of file diff --git a/src/sql/parts/disasterRecovery/restore/restoreDialog.ts b/src/sql/parts/disasterRecovery/restore/restoreDialog.ts new file mode 100644 index 0000000000..d1caba6966 --- /dev/null +++ b/src/sql/parts/disasterRecovery/restore/restoreDialog.ts @@ -0,0 +1,815 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import 'vs/css!./media/restoreDialog'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { Button } from 'vs/base/browser/ui/button/button'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { MessageType, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { attachButtonStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { localize } from 'vs/nls'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; + +import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox'; +import { InputBox, OnLoseFocusParams } from 'sql/base/browser/ui/inputBox/inputBox'; +import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; +import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin'; +import { CheckboxSelectColumn } from 'sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin'; +import { Table } from 'sql/base/browser/ui/table/table'; +import { TableDataView } from 'sql/base/browser/ui/table/tableDataView'; +import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper'; +import { Modal } from 'sql/base/browser/ui/modal/modal'; +import { attachModalDialogStyler, attachTableStyler, attachInputBoxStyler, attachSelectBoxStyler, attachEditableDropdownStyler } from 'sql/common/theme/styler'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import { ServiceOptionType } from 'sql/parts/connection/common/connectionManagement'; +import * as BackupConstants from 'sql/parts/disasterRecovery/backup/constants'; +import { RestoreViewModel, RestoreOptionParam, SouceDatabaseNamesParam } from 'sql/parts/disasterRecovery/restore/restoreViewModel'; +import * as FileValidationConstants from 'sql/parts/fileBrowser/common/fileValidationServiceConstants'; +import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown'; +import { TabbedPanel, PanelTabIdentifier } from 'sql/base/browser/ui/panel/panel'; +import * as DOM from 'vs/base/browser/dom'; +import * as data from 'data'; + +interface FileListElement { + logicalFileName: string; + fileType: string; + originalFileName: string; + restoreAs: string; +} + +export class RestoreDialog extends Modal { + public viewModel: RestoreViewModel; + + private _scriptButton: Button; + private _restoreButton: Button; + private _closeButton: Button; + private _optionsMap: { [name: string]: Widget } = {}; + private _restoreLabel: string; + private _restoreTitle: string; + private _databaseTitle: string; + private _backupFileTitle: string; + private _ownerUri: string; + private _databaseDropdown: Dropdown; + + // General options + private _filePathInputBox: InputBox; + private _browseFileButton: Button; + private _destinationRestoreToInputBox: InputBox; + private _restoreFromSelectBox: SelectBox; + private _sourceDatabaseSelectBox: SelectBox; + + private _panel: TabbedPanel; + private _generalTabId: PanelTabIdentifier; + + // File option + private readonly _relocateDatabaseFilesOption = 'relocateDbFiles'; + private readonly _relocatedDataFileFolderOption = 'dataFileFolder'; + private readonly _relocatedLogFileFolderOption = 'logFileFolder'; + + // other options + private readonly _withReplaceDatabaseOption = 'replaceDatabase'; + private readonly _withKeepReplicationOption = 'keepReplication'; + private readonly _withRestrictedUserOption = 'setRestrictedUser'; + + private readonly _recoveryStateOption = 'recoveryState'; + private readonly _standbyFileOption = 'standbyFile'; + + private readonly _takeTaillogBackupOption = 'backupTailLog'; + private readonly _tailLogWithNoRecoveryOption = 'tailLogWithNoRecovery'; + private readonly _tailLogBackupFileOption = 'tailLogBackupFile'; + + private readonly _closeExistingConnectionsOption = 'closeExistingConnections'; + + private _restoreFromBackupFileElement: HTMLElement; + + private _fileListTable: Table; + private _fileListData: TableDataView; + + private _restorePlanTable: Table; + private _restorePlanData: TableDataView; + private _restorePlanColumn; + + private _onRestore = new Emitter(); + public onRestore: Event = this._onRestore.event; + + private _onValidate = new Emitter(); + public onValidate: Event = this._onValidate.event; + + private _onCancel = new Emitter(); + public onCancel: Event = this._onCancel.event; + + private _onCloseEvent = new Emitter(); + public onCloseEvent: Event = this._onCloseEvent.event; + + private _onDatabaseListFocused = new Emitter(); + public onDatabaseListFocused: Event = this._onDatabaseListFocused.event; + + constructor( + optionsMetadata: data.ServiceOption[], + @IPartService partService: IPartService, + @IThemeService private _themeService: IThemeService, + @IContextViewService private _contextViewService: IContextViewService, + @IBootstrapService private _bootstrapService: IBootstrapService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(localize('RestoreDialogTitle', 'Restore database'), TelemetryKeys.Restore, partService, telemetryService, contextKeyService, { hasErrors: true, isWide: true, hasSpinner: true }); + this._restoreTitle = localize('restoreTitle', 'Restore database'); + this._databaseTitle = localize('database', 'Database'); + this._backupFileTitle = localize('backupFile', 'Backup file'); + this._restoreLabel = localize('restore', 'Restore'); + + // view model + this.viewModel = new RestoreViewModel(optionsMetadata); + this.viewModel.onSetLastBackupTaken((value) => this.updateLastBackupTaken(value)); + this.viewModel.onSetfilePath((value) => this.updateFilePath(value)); + this.viewModel.onSetSourceDatabaseNames((databaseNamesParam) => this.updateSourceDatabaseName(databaseNamesParam)); + this.viewModel.onSetTargetDatabaseName((value) => this.updateTargetDatabaseName(value)); + this.viewModel.onSetLastBackupTaken((value) => this.updateLastBackupTaken(value)); + this.viewModel.onSetRestoreOption((optionParams) => this.updateRestoreOption(optionParams)); + this.viewModel.onUpdateBackupSetsToRestore((backupSets) => this.updateBackupSetsToRestore(backupSets)); + this.viewModel.onUpdateRestoreDatabaseFiles((files) => this.updateRestoreDatabaseFiles(files)); + } + + public render() { + super.render(); + attachModalDialogStyler(this, this._themeService); + let cancelLabel = localize('cancel', 'Cancel'); + this._scriptButton = this.addFooterButton(localize('script', 'Script'), () => this.restore(true)); + this._restoreButton = this.addFooterButton(this._restoreLabel, () => this.restore(false)); + this._closeButton = this.addFooterButton(cancelLabel, () => this.cancel()); + this.registerListeners(); + this._destinationRestoreToInputBox.disable(); + } + + protected renderBody(container: HTMLElement) { + + let restoreFromElement; + $().div({ class: 'restore-from' }, (restoreFromContainer) => { + restoreFromElement = restoreFromContainer.getHTMLElement(); + this.createLabelElement(restoreFromContainer, localize('source', 'Source'), true); + this._restoreFromSelectBox = this.createSelectBoxHelper(restoreFromContainer, localize('restoreFrom', 'Restore from'), [this._databaseTitle, this._backupFileTitle], this._databaseTitle); + }); + + $().div({ class: 'backup-file-path' }, (filePathContainer) => { + filePathContainer.hide(); + this._restoreFromBackupFileElement = filePathContainer.getHTMLElement(); + let errorMessage = localize('missingBackupFilePathError', 'Backup file path is required.'); + let validationOptions: IInputOptions = { + validationOptions: { + validation: (value: string) => !value ? ({ type: MessageType.ERROR, content: errorMessage }) : null + }, + placeholder: localize('multipleBackupFilePath', 'Please enter one or more file paths separated by commas') + }; + + filePathContainer.div({ class: 'dialog-input-section' }, (inputContainer) => { + inputContainer.div({ class: 'dialog-label' }, (labelContainer) => { + labelContainer.innerHtml(localize('backupFilePath', "Backup file path")); + }); + + inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => { + this._filePathInputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, validationOptions); + }); + + inputContainer.div({ class: 'file-browser' }, (inputCellContainer) => { + this._browseFileButton = new Button(inputCellContainer); + this._browseFileButton.label = '...'; + }); + }); + }); + + let sourceDatabasesElement; + $().div({ class: 'source-database-list' }, (sourceDatabasesContainer) => { + sourceDatabasesElement = sourceDatabasesContainer.getHTMLElement(); + this._sourceDatabaseSelectBox = this.createSelectBoxHelper(sourceDatabasesContainer, localize('database', 'Database'), [], ''); + }); + + // Source section + let sourceElement: HTMLElement; + $().div({ class: 'source-section new-section' }, (sourceContainer) => { + sourceElement = sourceContainer.getHTMLElement(); + sourceContainer.append(restoreFromElement); + sourceContainer.append(this._restoreFromBackupFileElement); + sourceContainer.append(sourceDatabasesElement); + }); + + // Destination section + let destinationElement: HTMLElement; + $().div({ class: 'destination-section new-section' }, (destinationContainer) => { + destinationElement = destinationContainer.getHTMLElement(); + this.createLabelElement(destinationContainer, localize('destination', 'Destination'), true); + + destinationContainer.div({ class: 'dialog-input-section' }, (inputContainer) => { + inputContainer.div({ class: 'dialog-label' }, (labelContainer) => { + labelContainer.innerHtml(localize('targetDatabase', 'Target database')); + }); + + inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => { + // Get the bootstrap params and perform the bootstrap + inputCellContainer.style('width', '100%'); + this._databaseDropdown = new Dropdown(inputCellContainer.getHTMLElement(), this._contextViewService, this._themeService, + { + strictSelection: false + } + ); + this._databaseDropdown.onValueChange(s => { + this.databaseSelected(s); + }); + + this._databaseDropdown.onBlur(() => { + this.databaseSelected(this._databaseDropdown.value); + }); + + this._databaseDropdown.onFocus(() => { + this._onDatabaseListFocused.fire(); + }); + + this._databaseDropdown.value = this.viewModel.targetDatabaseName; + attachEditableDropdownStyler(this._databaseDropdown, this._themeService); + }); + }); + + this._destinationRestoreToInputBox = this.createInputBoxHelper(destinationContainer, localize('restoreTo', 'Restore to')); + }); + + + // Restore plan section + let restorePlanElement: HTMLElement; + $().div({ class: 'restore-plan-section new-section' }, (restorePlanContainer) => { + restorePlanElement = restorePlanContainer.getHTMLElement(); + this.createLabelElement(restorePlanContainer, localize('restorePlan', 'Restore plan'), true); + this.createLabelElement(restorePlanContainer, localize('backupSetsToRestore', 'Backup sets to restore')); + + // Backup sets table + restorePlanContainer.div({ class: 'dialog-input-section restore-list' }, (labelContainer) => { + this._restorePlanData = new TableDataView(); + this._restorePlanTable = new Table(labelContainer.getHTMLElement(), this._restorePlanData, this._restorePlanColumn, { enableColumnReorder: false }); + this._restorePlanTable.setSelectionModel(new RowSelectionModel({ selectActiveRow: false })); + this._restorePlanTable.onSelectedRowsChanged((e, data) => this.backupFileCheckboxChanged(e, data)); + }); + }); + + // Content in general tab + let generalTab = $('.restore-dialog'); + generalTab.append(sourceElement); + generalTab.append(destinationElement); + generalTab.append(restorePlanElement); + + // Content in file tab + let fileContentElement: HTMLElement; + $().div({ class: 'restore-dialog' }, (builder) => { + fileContentElement = builder.getHTMLElement(); + + // Restore database file as section + builder.div({ class: 'new-section' }, (sectionContainer) => { + this.createLabelElement(sectionContainer, localize('restoreDatabaseFileAs', 'Restore database files as'), true); + this.createOptionControl(sectionContainer, this._relocateDatabaseFilesOption); + sectionContainer.div({ class: 'sub-section' }, (subSectionContainer) => { + this.createOptionControl(subSectionContainer, this._relocatedDataFileFolderOption); + this.createOptionControl(subSectionContainer, this._relocatedLogFileFolderOption); + }); + }); + + // Restore database file details section + builder.div({ class: 'new-section' }, (sectionContainer) => { + this.createLabelElement(sectionContainer, localize('restoreDatabaseFileDetails', 'Restore database file details'), true); + // file list table + sectionContainer.div({ class: 'dialog-input-section restore-list' }, (fileNameContainer) => { + let logicalFileName = localize('logicalFileName', 'Logical file Name'); + let fileType = localize('fileType', 'File type'); + let originalFileName = localize('originalFileName', 'Original File Name'); + let restoreAs = localize('restoreAs', 'Restore as'); + var columns = [{ + id: 'logicalFileName', + name: logicalFileName, + field: 'logicalFileName' + }, { + id: 'fileType', + name: fileType, + field: 'fileType' + }, { + id: 'originalFileName', + name: originalFileName, + field: 'originalFileName' + }, { + id: 'restoreAs', + name: restoreAs, + field: 'restoreAs' + }]; + this._fileListData = new TableDataView(); + this._fileListTable = new Table(fileNameContainer.getHTMLElement(), this._fileListData, columns, { enableColumnReorder: false }); + this._fileListTable.setSelectionModel(new RowSelectionModel()); + }); + }); + }); + + // Content in options tab + let optionsContentElement: HTMLElement; + $().div({ class: 'restore-dialog' }, (builder) => { + optionsContentElement = builder.getHTMLElement(); + + // Restore options section + builder.div({ class: 'new-section' }, (sectionContainer) => { + this.createLabelElement(sectionContainer, localize('restoreOptions', 'Restore options'), true); + this.createOptionControl(sectionContainer, this._withReplaceDatabaseOption); + this.createOptionControl(sectionContainer, this._withKeepReplicationOption); + this.createOptionControl(sectionContainer, this._withRestrictedUserOption); + this.createOptionControl(sectionContainer, this._recoveryStateOption); + + sectionContainer.div({ class: 'sub-section' }, (subSectionContainer) => { + this.createOptionControl(subSectionContainer, this._standbyFileOption); + }); + }); + + // Tail-Log backup section + builder.div({ class: 'new-section' }, (sectionContainer) => { + this.createLabelElement(sectionContainer, localize('taillogBackup', 'Tail-Log backup'), true); + this.createOptionControl(sectionContainer, this._takeTaillogBackupOption); + sectionContainer.div({ class: 'sub-section' }, (subSectionContainer) => { + this.createOptionControl(subSectionContainer, this._tailLogWithNoRecoveryOption); + this.createOptionControl(subSectionContainer, this._tailLogBackupFileOption); + }); + }); + + // Server connections section + builder.div({ class: 'new-section' }, (sectionContainer) => { + this.createLabelElement(sectionContainer, localize('serverConnection', 'Server connections'), true); + this.createOptionControl(sectionContainer, this._closeExistingConnectionsOption); + }); + }); + + this._panel = new TabbedPanel(container); + this._generalTabId = this._panel.pushTab({ + identifier: 'general', + title: localize('generalTitle', 'General'), + view: { + render: c => { + generalTab.appendTo(c); + }, + layout: () => { } + } + }); + + let fileTab = this._panel.pushTab({ + identifier: 'fileContent', + title: localize('filesTitle', 'Files'), + view: { + layout: () => { }, + render: c => { + c.appendChild(fileContentElement); + } + } + }); + + this._panel.pushTab({ + identifier: 'options', + title: localize('optionsTitle', 'Options'), + view: { + layout: () => { }, + render: c => { + c.appendChild(optionsContentElement); + } + } + }); + + this._panel.onTabChange(c => { + if (c === fileTab && this._fileListTable) { + this._fileListTable.resizeCanvas(); + this._fileListTable.autosizeColumns(); + } + if (c !== this._generalTabId) { + this._restoreFromSelectBox.hideMessage(); + } + }); + } + + private databaseSelected(dbName: string): void { + if (this.viewModel.targetDatabaseName !== dbName) { + this.viewModel.targetDatabaseName = dbName; + this.validateRestore(false); + } + } + + public set databaseListOptions(vals: string[]) { + this._databaseDropdown.values = vals; + } + + private createLabelElement(container: Builder, content: string, isHeader?: boolean) { + let className = 'dialog-label'; + if (isHeader) { + className += ' header'; + } + container.div({ class: className }, (labelContainer) => { + labelContainer.innerHtml(content); + }); + } + + private createOptionControl(container: Builder, optionName: string): void { + let option = this.viewModel.getOptionMetadata(optionName); + let propertyWidget: any; + switch (option.valueType) { + case ServiceOptionType.boolean: + propertyWidget = this.createCheckBoxHelper(container, option.description, + DialogHelper.getBooleanValueFromStringOrBoolean(option.defaultValue), () => this.onBooleanOptionChecked(optionName)); + this._register(attachCheckboxStyler(propertyWidget, this._themeService)); + break; + case ServiceOptionType.category: + propertyWidget = this.createSelectBoxHelper(container, option.description, option.categoryValues.map(c => c.displayName), DialogHelper.getCategoryDisplayName(option.categoryValues, option.defaultValue)); + this._register(attachSelectBoxStyler(propertyWidget, this._themeService)); + this._register(propertyWidget.onDidSelect(selectedDatabase => { + this.onCatagoryOptionChanged(optionName); + })); + break; + case ServiceOptionType.string: + propertyWidget = this.createInputBoxHelper(container, option.description); + this._register(attachInputBoxStyler(propertyWidget, this._themeService)); + this._register(propertyWidget.onLoseFocus(params => { + this.onStringOptionChanged(optionName, params); + })); + } + + this._optionsMap[optionName] = propertyWidget; + } + + private onBooleanOptionChecked(optionName: string) { + this.viewModel.setOptionValue(optionName, (this._optionsMap[optionName]).checked); + this.validateRestore(false); + } + + private onCatagoryOptionChanged(optionName: string) { + this.viewModel.setOptionValue(optionName, (this._optionsMap[optionName]).value); + this.validateRestore(false); + } + + private onStringOptionChanged(optionName: string, params: OnLoseFocusParams) { + if (params.hasChanged && params.value) { + this.viewModel.setOptionValue(optionName, params.value); + this.validateRestore(false); + } + } + + private createCheckBoxHelper(container: Builder, label: string, isChecked: boolean, onCheck: (viaKeyboard: boolean) => void): Checkbox { + let checkbox: Checkbox; + container.div({ class: 'dialog-input-section' }, (inputCellContainer) => { + checkbox = DialogHelper.createCheckBox(inputCellContainer, label, 'restore-checkbox', isChecked, onCheck); + }); + return checkbox; + } + + private createSelectBoxHelper(container: Builder, label: string, options: string[], selectedOption: string): SelectBox { + let selectBox: SelectBox; + container.div({ class: 'dialog-input-section' }, (inputContainer) => { + inputContainer.div({ class: 'dialog-label' }, (labelContainer) => { + labelContainer.innerHtml(label); + }); + + inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => { + selectBox = new SelectBox(options, selectedOption, inputCellContainer.getHTMLElement(), this._contextViewService); + selectBox.render(inputCellContainer.getHTMLElement()); + }); + }); + return selectBox; + } + + private createInputBoxHelper(container: Builder, label: string, options?: IInputOptions): InputBox { + let inputBox: InputBox; + container.div({ class: 'dialog-input-section' }, (inputContainer) => { + inputContainer.div({ class: 'dialog-label' }, (labelContainer) => { + labelContainer.innerHtml(label); + }); + + inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => { + inputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, options); + }); + }); + return inputBox; + } + + private resetRestoreContent(): void { + this._restorePlanData.clear(); + this._fileListData.clear(); + this._restoreButton.enabled = false; + this._scriptButton.enabled = false; + } + + public onValidateResponseFail(errorMessage: string) { + this.resetRestoreContent(); + if (this.isRestoreFromDatabaseSelected) { + this._sourceDatabaseSelectBox.showMessage({ type: MessageType.ERROR, content: errorMessage }); + } else { + this._sourceDatabaseSelectBox.setOptions([]); + this._filePathInputBox.showMessage({ type: MessageType.ERROR, content: errorMessage }); + } + } + + public removeErrorMessage() { + this._filePathInputBox.hideMessage(); + this._sourceDatabaseSelectBox.hideMessage(); + this._destinationRestoreToInputBox.hideMessage(); + } + + public enableRestoreButton(enabled: boolean) { + this.hideSpinner(); + this._restoreButton.enabled = enabled; + this._scriptButton.enabled = enabled; + } + + public showError(errorMessage: string): void { + this.setError(errorMessage); + } + + private backupFileCheckboxChanged(e: Slick.EventData, data: Slick.OnSelectedRowsChangedEventArgs): void { + let selectedFiles = []; + data.grid.getSelectedRows().forEach(row => { + selectedFiles.push(data.grid.getDataItem(row)['Id']); + }); + + let isSame = false; + if (this.viewModel.selectedBackupSets && this.viewModel.selectedBackupSets.length === selectedFiles.length) { + isSame = this.viewModel.selectedBackupSets.some(item => selectedFiles.includes(item)); + } + + if (!isSame) { + this.viewModel.selectedBackupSets = selectedFiles; + this.validateRestore(false); + } + } + + private registerListeners(): void { + // Theme styler + this._register(attachInputBoxStyler(this._filePathInputBox, this._themeService)); + this._register(attachInputBoxStyler(this._destinationRestoreToInputBox, this._themeService)); + this._register(attachSelectBoxStyler(this._restoreFromSelectBox, this._themeService)); + this._register(attachSelectBoxStyler(this._sourceDatabaseSelectBox, this._themeService)); + this._register(attachButtonStyler(this._browseFileButton, this._themeService)); + this._register(attachButtonStyler(this._scriptButton, this._themeService)); + this._register(attachButtonStyler(this._restoreButton, this._themeService)); + this._register(attachButtonStyler(this._closeButton, this._themeService)); + this._register(attachTableStyler(this._fileListTable, this._themeService)); + this._register(attachTableStyler(this._restorePlanTable, this._themeService)); + + this._register(this._filePathInputBox.onLoseFocus(params => { + this.onFilePathLoseFocus(params); + })); + + this._register(DOM.addDisposableListener(this._browseFileButton.getElement(), DOM.EventType.CLICK, () => { + if (this._browseFileButton.enabled) { + this.onFileBrowserRequested(); + } + })); + + this._register(this._sourceDatabaseSelectBox.onDidSelect(selectedDatabase => { + this.onSourceDatabaseChanged(selectedDatabase.selected); + })); + + this._register(this._restoreFromSelectBox.onDidSelect(selectedRestoreFrom => { + this.onRestoreFromChanged(selectedRestoreFrom.selected); + })); + } + + private onFileBrowserRequested(): void { + this._bootstrapService.fileBrowserDialogService.showDialog(this._ownerUri, + this.viewModel.defaultBackupFolder, + BackupConstants.fileFiltersSet, + FileValidationConstants.restore, + true, + filepath => this.onFileBrowsed(filepath)); + } + + private onFileBrowsed(filepath: string) { + var oldFilePath = this._filePathInputBox.value; + if (DialogHelper.isNullOrWhiteSpace(this._filePathInputBox.value)) { + this._filePathInputBox.value = filepath; + } else { + this._filePathInputBox.value = this._filePathInputBox.value + ', ' + filepath; + } + + if (oldFilePath !== this._filePathInputBox.value) { + this.onFilePathChanged(this._filePathInputBox.value); + } + } + + private onFilePathLoseFocus(params: OnLoseFocusParams) { + if (params.value) { + if (params.hasChanged || (this.viewModel.filePath !== params.value)) { + this.onFilePathChanged(params.value); + } + } + } + + private onFilePathChanged(filePath: string) { + this.viewModel.filePath = filePath; + this.viewModel.selectedBackupSets = null; + this.validateRestore(true); + } + + private onSourceDatabaseChanged(selectedDatabase: string) { + this.viewModel.sourceDatabaseName = selectedDatabase; + this.viewModel.selectedBackupSets = null; + this.viewModel.resetTailLogBackupFile(); + this.validateRestore(true); + } + + private onRestoreFromChanged(selectedRestoreFrom: string) { + this.removeErrorMessage(); + if (selectedRestoreFrom === this._backupFileTitle) { + this.viewModel.onRestoreFromChanged(true); + new Builder(this._restoreFromBackupFileElement).show(); + } else { + this.viewModel.onRestoreFromChanged(false); + new Builder(this._restoreFromBackupFileElement).hide(); + } + this.resetRestoreContent(); + } + + private get isRestoreFromDatabaseSelected(): boolean { + return this._restoreFromSelectBox.value === this._databaseTitle; + } + + public validateRestore(overwriteTargetDatabase: boolean = false): void { + this.showSpinner(); + this._restoreButton.enabled = false; + this._scriptButton.enabled = false; + this._onValidate.fire(overwriteTargetDatabase); + } + + public restore(isScriptOnly: boolean): void { + if (this._restoreButton.enabled) { + this._onRestore.fire(isScriptOnly); + } + } + + public hideError() { + this.setError(''); + } + + /* Overwrite esapce key behavior */ + protected onClose() { + this.cancel(); + } + + /* Overwrite enter key behavior */ + protected onAccept() { + this.restore(false); + } + + public cancel() { + this._onCancel.fire(); + this.close(); + } + + public close() { + this.resetDialog(); + this.hide(); + this._onCloseEvent.fire(); + } + + private resetDialog(): void { + this.hideError(); + this._restoreFromSelectBox.selectWithOptionName(this._databaseTitle); + this.onRestoreFromChanged(this._databaseTitle); + this._sourceDatabaseSelectBox.select(0); + this._panel.showTab(this._generalTabId); + this.removeErrorMessage(); + this.resetRestoreContent(); + } + + public open(serverName: string, ownerUri: string) { + this.title = this._restoreTitle + ' - ' + serverName; + this._ownerUri = ownerUri; + + this.show(); + this._filePathInputBox.focus(); + } + + protected layout(height?: number): void { + // Nothing currently laid out statically in this class + } + + public dispose(): void { + super.dispose(); + for (var key in this._optionsMap) { + var widget: Widget = this._optionsMap[key]; + widget.dispose(); + delete this._optionsMap[key]; + } + } + + private updateLastBackupTaken(value: string) { + this._destinationRestoreToInputBox.value = value; + } + + private updateFilePath(value: string) { + this._filePathInputBox.value = value; + if (!value) { + this._filePathInputBox.hideMessage(); + } + } + + private updateSourceDatabaseName(databaseNamesParam: SouceDatabaseNamesParam) { + // Always adding an empty name as the first item so if the selected db name is not in the list, + // The empty string would be selected and not the first db in the list + let dbNames: string[] = []; + if (this.isRestoreFromDatabaseSelected && databaseNamesParam.databaseNames + && databaseNamesParam.databaseNames.length > 0 && databaseNamesParam.databaseNames[0] !== '') { + dbNames = [''].concat(databaseNamesParam.databaseNames); + } else { + dbNames = databaseNamesParam.databaseNames; + } + this._sourceDatabaseSelectBox.setOptions(dbNames); + this._sourceDatabaseSelectBox.selectWithOptionName(databaseNamesParam.selectedDatabase); + } + + private updateTargetDatabaseName(value: string) { + this._databaseDropdown.value = value; + } + + private updateRestoreOption(optionParam: RestoreOptionParam) { + let widget = this._optionsMap[optionParam.optionName]; + if (widget) { + if (widget instanceof Checkbox) { + (widget).checked = optionParam.value; + this.enableDisableWiget(widget, optionParam.isReadOnly); + } else if (widget instanceof SelectBox) { + (widget).selectWithOptionName(optionParam.value); + this.enableDisableWiget(widget, optionParam.isReadOnly); + } else if (widget instanceof InputBox) { + (widget).value = optionParam.value; + this.enableDisableWiget(widget, optionParam.isReadOnly); + } + } + } + + private enableDisableWiget(widget: Checkbox | SelectBox | InputBox, isReadOnly: boolean) { + if (isReadOnly) { + widget.disable(); + } else { + widget.enable(); + } + } + + private updateRestoreDatabaseFiles(dbFiles: data.RestoreDatabaseFileInfo[]) { + this._fileListData.clear(); + if (dbFiles) { + let data = []; + for (let i = 0; i < dbFiles.length; i++) { + data[i] = { + logicalFileName: dbFiles[i].logicalFileName, + fileType: dbFiles[i].fileType, + originalFileName: dbFiles[i].originalFileName, + restoreAs: dbFiles[i].restoreAsFileName + }; + } + + this._fileListData.push(data); + } + } + + private updateBackupSetsToRestore(backupSetsToRestore: data.DatabaseFileInfo[]) { + this._restorePlanData.clear(); + if (backupSetsToRestore && backupSetsToRestore.length > 0) { + if (!this._restorePlanColumn) { + let firstRow = backupSetsToRestore[0]; + this._restorePlanColumn = firstRow.properties.map(item => { + return { + id: item.propertyName, + name: item.propertyDisplayName, + field: item.propertyName + }; + }); + + let checkboxSelectColumn = new CheckboxSelectColumn({ title: this._restoreLabel }); + this._register(attachCheckboxStyler(checkboxSelectColumn, this._themeService)); + this._restorePlanColumn.unshift(checkboxSelectColumn.getColumnDefinition()); + this._restorePlanTable.columns = this._restorePlanColumn; + this._restorePlanTable.registerPlugin(checkboxSelectColumn); + this._restorePlanTable.autosizeColumns(); + } + + let data = []; + let selectedRow = []; + for (let i = 0; i < backupSetsToRestore.length; i++) { + let backupFile = backupSetsToRestore[i]; + let newData = {}; + for (let j = 0; j < backupFile.properties.length; j++) { + newData[backupFile.properties[j].propertyName] = backupFile.properties[j].propertyValueDisplayName; + } + data.push(newData); + if (backupFile.isSelected) { + selectedRow.push(i); + } + } + this._restorePlanData.push(data); + this._restorePlanTable.setSelectedRows(selectedRow); + } + } +} \ No newline at end of file diff --git a/src/sql/parts/disasterRecovery/restore/restoreDialogController.ts b/src/sql/parts/disasterRecovery/restore/restoreDialogController.ts new file mode 100644 index 0000000000..e9cba66131 --- /dev/null +++ b/src/sql/parts/disasterRecovery/restore/restoreDialogController.ts @@ -0,0 +1,228 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import * as types from 'vs/base/common/types'; + +import { OptionsDialog } from 'sql/base/browser/ui/modal/optionsDialog'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import * as ConnectionConstants from 'sql/parts/connection/common/constants'; +import { ProviderConnectionInfo } from 'sql/parts/connection/common/providerConnectionInfo'; +import { IDisasterRecoveryService, IRestoreDialogController, TaskExecutionMode } from 'sql/parts/disasterRecovery/common/interfaces'; +import { MssqlRestoreInfo } from 'sql/parts/disasterRecovery/restore/mssqlRestoreInfo'; +import { RestoreDialog } from 'sql/parts/disasterRecovery/restore/restoreDialog'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import * as Utils from 'sql/parts/connection/common/utils'; +import * as data from 'data'; + +export class RestoreDialogController implements IRestoreDialogController { + _serviceBrand: any; + + private _restoreDialogs: { [provider: string]: RestoreDialog | OptionsDialog } = {}; + private _currentProvider: string; + private _ownerUri: string; + private _sessionId: string; + private readonly _restoreFeature = 'Restore'; + private _optionValues: { [optionName: string]: any } = {}; + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService, + @IDisasterRecoveryService private _disasterRecoveryService: IDisasterRecoveryService, + @IConnectionManagementService private _connectionService: IConnectionManagementService, + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService + ) { + } + + private handleOnRestore(isScriptOnly: boolean = false): void { + let restoreOption = this.setRestoreOption(); + if (isScriptOnly) { + restoreOption.taskExecutionMode = TaskExecutionMode.script; + } else { + restoreOption.taskExecutionMode = TaskExecutionMode.executeAndScript; + } + + this._disasterRecoveryService.restore(this._ownerUri, restoreOption); + let restoreDialog = this._restoreDialogs[this._currentProvider]; + restoreDialog.close(); + } + + private handleMssqlOnValidateFile(overwriteTargetDatabase: boolean = false): void { + let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog; + this._disasterRecoveryService.getRestorePlan(this._ownerUri, this.setRestoreOption(overwriteTargetDatabase)).then(restorePlanResponse => { + this._sessionId = restorePlanResponse.sessionId; + + if (restorePlanResponse.errorMessage) { + restoreDialog.onValidateResponseFail(restorePlanResponse.errorMessage); + } else { + restoreDialog.removeErrorMessage(); + restoreDialog.viewModel.onRestorePlanResponse(restorePlanResponse); + } + + if (restorePlanResponse.canRestore && !this.isEmptyBackupset()) { + restoreDialog.enableRestoreButton(true); + } else { + restoreDialog.enableRestoreButton(false); + } + }, error => { + restoreDialog.showError(error); + }); + } + + /** + * Temporary fix for bug #2506: Restore button not disabled when there's not backup set to restore + * Will remove this function once there is a fix in the service (bug #2572) + */ + private isEmptyBackupset(): boolean { + let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog; + if (!types.isUndefinedOrNull(restoreDialog.viewModel.selectedBackupSets) && restoreDialog.viewModel.selectedBackupSets.length === 0) { + return true; + } + return false; + } + + private getMssqlRestoreConfigInfo(): Promise { + return new Promise((resolve, reject) => { + let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog; + this._disasterRecoveryService.getRestoreConfigInfo(this._ownerUri).then(restoreConfigInfo => { + restoreDialog.viewModel.updateOptionWithConfigInfo(restoreConfigInfo.configInfo); + resolve(); + }, error => { + restoreDialog.showError(error); + reject(error); + }); + }); + } + + private setRestoreOption(overwriteTargetDatabase: boolean = false): data.RestoreInfo { + let restoreInfo = undefined; + + let providerId: string = this.getCurrentProviderId(); + if (providerId === ConnectionConstants.mssqlProviderName) { + restoreInfo = new MssqlRestoreInfo(); + + if (this._sessionId) { + restoreInfo.sessionId = this._sessionId; + } + + let restoreDialog = this._restoreDialogs[providerId] as RestoreDialog; + restoreInfo.backupFilePaths = restoreDialog.viewModel.filePath; + // todo: Need to change restoreInfo.readHeaderFromMedia when implement restore from database + restoreInfo.readHeaderFromMedia = restoreDialog.viewModel.readHeaderFromMedia; + restoreInfo.selectedBackupSets = restoreDialog.viewModel.selectedBackupSets; + + if (restoreDialog.viewModel.sourceDatabaseName) { + restoreInfo.sourceDatabaseName = restoreDialog.viewModel.sourceDatabaseName; + } + if (restoreDialog.viewModel.targetDatabaseName) { + restoreInfo.targetDatabaseName = restoreDialog.viewModel.targetDatabaseName; + } + restoreInfo.overwriteTargetDatabase = overwriteTargetDatabase; + + // Set other restore options + restoreDialog.viewModel.getRestoreAdvancedOptions(restoreInfo.options); + } else { + restoreInfo = { options: this._optionValues }; + } + + return restoreInfo; + } + + private getRestoreOption(): data.ServiceOption[] { + let options: data.ServiceOption[] = []; + let providerId: string = this.getCurrentProviderId(); + let providerCapabilities = this._capabilitiesService.getCapabilities().find(c => c.providerName === providerId); + + if (providerCapabilities) { + let restoreMetadataProvider = providerCapabilities.features.find(f => f.featureName === this._restoreFeature); + if (restoreMetadataProvider) { + options = restoreMetadataProvider.optionsMetadata; + } + } + return options; + } + + private handleOnClose(): void { + this._connectionService.disconnect(this._ownerUri); + } + + private handleOnCancel(): void { + let restoreInfo = new MssqlRestoreInfo(); + restoreInfo.sessionId = this._sessionId; + this._disasterRecoveryService.cancelRestorePlan(this._ownerUri, restoreInfo).then(() => { + this._connectionService.disconnect(this._ownerUri); + }); + } + + public showDialog(connection: IConnectionProfile): TPromise { + return new TPromise((resolve, reject) => { + let result: void; + + this._ownerUri = this._connectionService.getConnectionId(connection) + + ProviderConnectionInfo.idSeparator + + Utils.ConnectionUriRestoreIdAttributeName + + ProviderConnectionInfo.nameValueSeparator + + '0'; + + if (!this._connectionService.isConnected(this._ownerUri)) { + this._connectionService.connect(connection, this._ownerUri).then(connectionResult => { + this._sessionId = null; + this._currentProvider = this.getCurrentProviderId(); + if (!this._restoreDialogs[this._currentProvider]) { + let newRestoreDialog: RestoreDialog | OptionsDialog = undefined; + if (this._currentProvider === ConnectionConstants.mssqlProviderName) { + let provider = this._currentProvider; + newRestoreDialog = this._instantiationService.createInstance(RestoreDialog, this.getRestoreOption()); + newRestoreDialog.onCancel(() => this.handleOnCancel()); + newRestoreDialog.onRestore((isScriptOnly) => this.handleOnRestore(isScriptOnly)); + newRestoreDialog.onValidate((overwriteTargetDatabase) => this.handleMssqlOnValidateFile(overwriteTargetDatabase)); + newRestoreDialog.onDatabaseListFocused(() => this.fetchDatabases(provider)); + } else { + newRestoreDialog = this._instantiationService.createInstance( + OptionsDialog, 'Restore database - ' + connection.serverName + ':' + connection.databaseName, 'RestoreOptions', undefined); + newRestoreDialog.onOk(() => this.handleOnRestore()); + } + newRestoreDialog.onCloseEvent(() => this.handleOnClose()); + newRestoreDialog.render(); + this._restoreDialogs[this._currentProvider] = newRestoreDialog; + } + + if (this._currentProvider === ConnectionConstants.mssqlProviderName) { + let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog; + restoreDialog.viewModel.resetRestoreOptions(connection.databaseName); + this.getMssqlRestoreConfigInfo().then(() => { + restoreDialog.open(connection.serverName, this._ownerUri); + restoreDialog.validateRestore(); + }, restoreConfigError => { + reject(restoreConfigError); + }); + + } else { + let restoreDialog = this._restoreDialogs[this._currentProvider] as OptionsDialog; + restoreDialog.open(this.getRestoreOption(), this._optionValues); + } + + resolve(result); + }, error => { + reject(error); + }); + } + }); + } + + private getCurrentProviderId(): string { + return this._connectionService.getProviderIdFromUri(this._ownerUri); + } + + private fetchDatabases(provider: string): void { + this._connectionService.listDatabases(this._ownerUri).then(result => { + if (result && result.databaseNames) { + ( this._restoreDialogs[provider]).databaseListOptions = result.databaseNames; + } + }); + } +} diff --git a/src/sql/parts/disasterRecovery/restore/restoreViewModel.ts b/src/sql/parts/disasterRecovery/restore/restoreViewModel.ts new file mode 100644 index 0000000000..f4a40ab866 --- /dev/null +++ b/src/sql/parts/disasterRecovery/restore/restoreViewModel.ts @@ -0,0 +1,309 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as data from 'data'; +import { ServiceOptionType } from 'sql/parts/connection/common/connectionManagement'; +import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper'; +import * as types from 'vs/base/common/types'; + +import Event, { Emitter } from 'vs/base/common/event'; + +export interface RestoreOptionsElement { + optionMetadata: data.ServiceOption; + defaultValue: any; + currentValue: any; +} + +/** + * Parameters for setting the widget in the restore dialog + */ +export interface RestoreOptionParam { + optionName: string; + value: any; + isReadOnly: boolean; +} + +/** + * Parameters for setting the list of source database names and selected database name in the restore dialog + */ +export interface SouceDatabaseNamesParam { + databaseNames: string[]; + selectedDatabase: string; +} + +/** + * View model for restore dialog + */ +export class RestoreViewModel { + public filePath: string; + public sourceDatabaseName: string; + public targetDatabaseName: string; + public lastBackupTaken: string; + public databaseList: string[]; + public readHeaderFromMedia: boolean; + public selectedBackupSets: string[]; + public defaultBackupFolder: string; + + private _onSetLastBackupTaken = new Emitter(); + public onSetLastBackupTaken: Event = this._onSetLastBackupTaken.event; + + private _onSetfilePath = new Emitter(); + public onSetfilePath: Event = this._onSetfilePath.event; + + private _onSetSourceDatabaseNames = new Emitter(); + public onSetSourceDatabaseNames: Event = this._onSetSourceDatabaseNames.event; + + private _onSetTargetDatabaseName = new Emitter(); + public onSetTargetDatabaseName: Event = this._onSetTargetDatabaseName.event; + + private _onSetRestoreOption = new Emitter(); + public onSetRestoreOption: Event = this._onSetRestoreOption.event; + + private _onUpdateBackupSetsToRestore = new Emitter(); + public onUpdateBackupSetsToRestore: Event = this._onUpdateBackupSetsToRestore.event; + + private _onUpdateRestoreDatabaseFiles = new Emitter(); + public onUpdateRestoreDatabaseFiles: Event = this._onUpdateRestoreDatabaseFiles.event; + + private _optionsMap: { [name: string]: RestoreOptionsElement } = {}; + + constructor(optionsMetadata: data.ServiceOption[]) { + optionsMetadata.forEach(optionMetadata => { + let defaultValue = this.getDisplayValue(optionMetadata, optionMetadata.defaultValue); + this._optionsMap[optionMetadata.name] = { + optionMetadata: optionMetadata, + defaultValue: defaultValue, + currentValue: defaultValue + }; + }); + } + + /** + * Get option display value + */ + public getDisplayValue(optionMetadata: data.ServiceOption, optionValue: any): any { + let displayValue: any; + switch (optionMetadata.valueType) { + case ServiceOptionType.boolean: + displayValue = DialogHelper.getBooleanValueFromStringOrBoolean(optionValue); + break; + case ServiceOptionType.category: + let optionName = optionValue; + if (!optionName && optionMetadata.categoryValues[0]) { + optionName = optionMetadata.categoryValues[0].name; + } + displayValue = DialogHelper.getCategoryDisplayName(optionMetadata.categoryValues, optionName); + break; + case ServiceOptionType.string: + displayValue = optionValue ? optionValue : ''; + } + return displayValue; + } + + /** + * On restore from changed set readHeaderFromMedia and reset the source database names and selected database name based on isFromBackupFile value. + */ + public onRestoreFromChanged(isFromBackupFile: boolean) { + this.readHeaderFromMedia = isFromBackupFile; + if (isFromBackupFile) { + this.updateFilePath(''); + this.updateSourceDatabaseNames([], ''); + } else { + this.updateSourceDatabaseNames(this.databaseList, this.databaseList[0]); + } + } + + /** + * Get option metadata from the option map + */ + public getOptionMetadata(optionName: string): data.ServiceOption { + return this._optionsMap[optionName] ? this._optionsMap[optionName].optionMetadata : undefined; + } + + /** + * Set current value for restore option + */ + public setOptionValue(optionName: string, value: any): void { + if (this._optionsMap[optionName]) { + this._optionsMap[optionName].currentValue = value; + } + } + + /** + * Reset tail log backup file (temporary fix for bug#2838) + */ + public resetTailLogBackupFile(): void { + const tailLogBackupFileOption = 'tailLogBackupFile'; + if (this._optionsMap[tailLogBackupFileOption]) { + this._optionsMap[tailLogBackupFileOption].currentValue = null; + this._optionsMap[tailLogBackupFileOption].defaultValue = null; + } + } + + /** + * Get current value for restore option + */ + public getOptionValue(optionName: string): any { + if (this._optionsMap[optionName]) { + return this._optionsMap[optionName].currentValue; + } + return undefined; + } + + /** + * Get restore advanced options. Only return the options that are different from the default options + */ + public getRestoreAdvancedOptions(options: { [name: string]: any }) { + for (let key in this._optionsMap) { + let optionElement = this._optionsMap[key]; + switch (optionElement.optionMetadata.valueType) { + case ServiceOptionType.boolean: + if (optionElement.currentValue !== optionElement.defaultValue) { + options[key] = optionElement.currentValue; + } + break; + case ServiceOptionType.category: + if (optionElement.currentValue !== optionElement.defaultValue) { + options[key] = DialogHelper.getCategoryName(optionElement.optionMetadata.categoryValues, optionElement.currentValue); + } + break; + case ServiceOptionType.string: + if (optionElement.currentValue && optionElement.currentValue !== optionElement.defaultValue) { + options[key] = optionElement.currentValue; + } + } + } + } + + /** + * On restore plan response will update all the information from restore plan response + */ + public onRestorePlanResponse(restorePlanResponse: data.RestorePlanResponse): void { + if (restorePlanResponse.planDetails && restorePlanResponse.planDetails['lastBackupTaken']) { + this.updateLastBackupTaken(restorePlanResponse.planDetails['lastBackupTaken'].currentValue); + } + + if (restorePlanResponse.planDetails && restorePlanResponse.planDetails['targetDatabaseName']) { + this.updateTargetDatabaseName(restorePlanResponse.planDetails['targetDatabaseName'].currentValue); + } + this._onUpdateRestoreDatabaseFiles.fire(restorePlanResponse.dbFiles); + this.updateSourceDatabaseNames(restorePlanResponse.databaseNamesFromBackupSets, restorePlanResponse.planDetails['sourceDatabaseName'].currentValue); + this.updateOptionWithPlanDetail(restorePlanResponse.planDetails); + this.updateBackupSetsToRestore(restorePlanResponse.backupSetsToRestore); + } + + /** + * Update options with plan details + */ + public updateOptionWithPlanDetail(planDetails: { [key: string]: data.RestorePlanDetailInfo }): void { + if (planDetails) { + for (var key in planDetails) { + let optionElement = this._optionsMap[key]; + if (optionElement) { + let planDetailInfo = planDetails[key]; + optionElement.defaultValue = this.getDisplayValue(optionElement.optionMetadata, planDetailInfo.defaultValue); + optionElement.currentValue = this.getDisplayValue(optionElement.optionMetadata, planDetailInfo.currentValue); + this._onSetRestoreOption.fire({ optionName: key, value: this._optionsMap[key].currentValue, isReadOnly: planDetailInfo.isReadOnly }); + } + } + } + } + + /** + * Update options with restore config info. The option values will be both default and current values. + */ + public updateOptionWithConfigInfo(configInfo: { [key: string]: any }): void { + if (configInfo) { + if (configInfo['sourceDatabaseNamesWithBackupSets']) { + let databaseList = configInfo['sourceDatabaseNamesWithBackupSets']; + if (types.isStringArray(databaseList)) { + this.databaseList = databaseList; + this.databaseList.unshift(''); + this.readHeaderFromMedia = false; + this.updateSourceDatabaseNames(this.databaseList, this.sourceDatabaseName); + } + } + if (configInfo['defaultBackupFolder']) { + this.defaultBackupFolder = configInfo['defaultBackupFolder']; + } + + for (var key in configInfo) { + let optionElement = this._optionsMap[key]; + if (optionElement) { + let planDetailInfo = configInfo[key]; + optionElement.defaultValue = this.getDisplayValue(optionElement.optionMetadata, planDetailInfo); + optionElement.currentValue = optionElement.defaultValue; + this._onSetRestoreOption.fire({ optionName: key, value: this._optionsMap[key].currentValue, isReadOnly: true }); + } + } + } + } + + /** + * Update backup sets to restore + */ + public updateBackupSetsToRestore(backupSetsToRestore: data.DatabaseFileInfo[]): void { + this.selectedBackupSets = null; + if (backupSetsToRestore) { + this.selectedBackupSets = []; + backupSetsToRestore.forEach(backupFile => { + if (backupFile.isSelected) { + this.selectedBackupSets.push(backupFile.id); + } + }); + this._onUpdateBackupSetsToRestore.fire(backupSetsToRestore); + } + } + + /** + * Reset restore options to the default value + */ + public resetRestoreOptions(databaseName: string): void { + this.updateTargetDatabaseName(databaseName); + this.updateSourceDatabaseNames([], databaseName); + this.updateFilePath(''); + this.updateLastBackupTaken(''); + this.databaseList = []; + this.selectedBackupSets = null; + for (var key in this._optionsMap) { + this._optionsMap[key].defaultValue = this.getDisplayValue(this._optionsMap[key].optionMetadata, this._optionsMap[key].optionMetadata.defaultValue); + this._optionsMap[key].currentValue = this._optionsMap[key].defaultValue; + this._onSetRestoreOption.fire({ optionName: key, value: this._optionsMap[key].defaultValue, isReadOnly: false }); + } + } + + /** + * Update last backup taken + */ + public updateLastBackupTaken(value: string) { + this.lastBackupTaken = value; + this._onSetLastBackupTaken.fire(value); + } + + /** + * Update file path + */ + public updateFilePath(value: string) { + this.filePath = value; + this._onSetfilePath.fire(value); + } + + /** + * Update source database names and selected database + */ + public updateSourceDatabaseNames(options: string[], selectedDatabase: string) { + this.sourceDatabaseName = selectedDatabase; + this._onSetSourceDatabaseNames.fire({ databaseNames: options, selectedDatabase: selectedDatabase }); + } + + /** + * Update target database name + */ + public updateTargetDatabaseName(value: string) { + this.targetDatabaseName = value; + this._onSetTargetDatabaseName.fire(value); + } +} \ No newline at end of file diff --git a/src/sql/parts/editData/common/editDataInput.ts b/src/sql/parts/editData/common/editDataInput.ts new file mode 100644 index 0000000000..418e53e775 --- /dev/null +++ b/src/sql/parts/editData/common/editDataInput.ts @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; +import { IConnectionManagementService, IConnectableInput, INewConnectionParams } from 'sql/parts/connection/common/connectionManagement'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import { EditSessionReadyParams } from 'data'; +import URI from 'vs/base/common/uri'; +import nls = require('vs/nls'); + +/** + * Input for the EditDataEditor. This input is simply a wrapper around a QueryResultsInput for the QueryResultsEditor + */ +export class EditDataInput extends EditorInput implements IConnectableInput { + public static ID: string = 'workbench.editorinputs.editDataInput'; + private _visible: boolean; + private _hasBootstrapped: boolean; + private _editorContainer: HTMLElement; + private _updateTaskbar: Emitter; + private _editorInitializing: Emitter; + private _showTableView: Emitter; + private _refreshButtonEnabled: boolean; + private _stopButtonEnabled: boolean; + private _setup: boolean; + private _toDispose: IDisposable[]; + private _rowLimit: number; + private _objectType: string; + + constructor(private _uri: URI, private _schemaName, private _tableName, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IQueryModelService private _queryModelService: IQueryModelService, + @IMessageService private _messageService: IMessageService + ) { + super(); + this._visible = false; + this._hasBootstrapped = false; + this._updateTaskbar = new Emitter(); + this._showTableView = new Emitter(); + this._editorInitializing = new Emitter(); + this._setup = false; + this._stopButtonEnabled = false; + this._refreshButtonEnabled = false; + this._toDispose = []; + + //TODO determine is this is a table or a view + this._objectType = 'TABLE'; + + // Attach to event callbacks + if (this._queryModelService) { + let self = this; + + // Register callbacks for the Actions + this._toDispose.push( + this._queryModelService.onRunQueryStart(uri => { + if (self.uri === uri) { + self.initEditStart(); + } + }) + ); + + this._toDispose.push( + this._queryModelService.onEditSessionReady((result) => { + if (self.uri === result.ownerUri) { + self.initEditEnd(result); + } + }) + ); + } + } + + // Getters/Setters + public get tableName(): string { return this._tableName; } + public get schemaName(): string { return this._schemaName; } + public get uri(): string { return this._uri.toString(); } + public get updateTaskbar(): Event { return this._updateTaskbar.event; } + public get editorInitializing(): Event { return this._editorInitializing.event; } + public get showTableView(): Event { return this._showTableView.event; } + public get stopButtonEnabled(): boolean { return this._stopButtonEnabled; } + public get refreshButtonEnabled(): boolean { return this._refreshButtonEnabled; } + public get container(): HTMLElement { return this._editorContainer; } + public get hasBootstrapped(): boolean { return this._hasBootstrapped; } + public get visible(): boolean { return this._visible; } + public get setup(): boolean { return this._setup; } + public get rowLimit(): number { return this._rowLimit; } + public get objectType(): string { return this._objectType; } + public getTypeId(): string { return EditDataInput.ID; } + public setVisibleTrue(): void { this._visible = true; } + public setBootstrappedTrue(): void { this._hasBootstrapped = true; } + public getResource(): URI { return this._uri; } + public getName(): string { return this._uri.path; } + public supportsSplitEditor(): boolean { return false; } + public setupComplete() { this._setup = true; } + public set container(container: HTMLElement) { + this._disposeContainer(); + this._editorContainer = container; + } + + // State Update Callbacks + public initEditStart(): void { + this._editorInitializing.fire(true); + this._refreshButtonEnabled = false; + this._stopButtonEnabled = true; + this._updateTaskbar.fire(this); + } + + public initEditEnd(result: EditSessionReadyParams): void { + if (result.success) { + this._refreshButtonEnabled = true; + this._stopButtonEnabled = false; + } else { + this._refreshButtonEnabled = false; + this._stopButtonEnabled = false; + this._messageService.show(Severity.Error, result.message); + } + this._editorInitializing.fire(false); + this._updateTaskbar.fire(this); + } + + public onConnectStart(): void { + // TODO: Indicate connection started + } + + public onConnectReject(error?: string): void { + if (error) { + this._messageService.show(Severity.Error, nls.localize('connectionFailure', 'Edit Data Session Failed To Connect')); + } + } + + public onConnectSuccess(params?: INewConnectionParams): void { + this._queryModelService.initializeEdit(this.uri, this.schemaName, this.tableName, this._objectType, this._rowLimit); + this._showTableView.fire(this); + } + + public onDisconnect(): void { + // TODO: deal with disconnections + } + + public onRowDropDownSet(rows: number) { + this._rowLimit = rows; + } + + // Boiler Plate Functions + public matches(otherInput: any): boolean { + if (otherInput instanceof EditDataInput) { + return (this.uri === otherInput.uri); + } + + return false; + } + + public resolve(refresh?: boolean): TPromise { + return TPromise.as(null); + } + + public dispose(): void { + this._toDispose = dispose(this._toDispose); + this._disposeContainer(); + super.dispose(); + } + + private _disposeContainer() { + if (this._editorContainer && this._editorContainer.parentElement) { + this._editorContainer.parentElement.removeChild(this._editorContainer); + this._editorContainer = null; + } + } + + public close(): void { + // Dispose our edit session then disconnect our input + this._queryModelService.disposeEdit(this.uri).then(() => { + return this._connectionManagementService.disconnectEditor(this, true); + }).then(() => { + this.dispose(); + super.close(); + }); + } +} diff --git a/src/sql/parts/editData/editor/editDataEditor.ts b/src/sql/parts/editData/editor/editDataEditor.ts new file mode 100644 index 0000000000..c00bc7d9ad --- /dev/null +++ b/src/sql/parts/editData/editor/editDataEditor.ts @@ -0,0 +1,359 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/parts/query/editor/media/queryEditor'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as DOM from 'vs/base/browser/dom'; +import * as nls from 'vs/nls'; +import { Builder, Dimension } from 'vs/base/browser/builder'; + +import { EditorOptions } from 'vs/workbench/common/editor'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { Position } from 'vs/platform/editor/common/editor'; + +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +import { EditDataInput } from 'sql/parts/editData/common/editDataInput'; + +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { + RefreshTableAction, StopRefreshTableAction, + ChangeMaxRowsAction, ChangeMaxRowsActionItem +} from 'sql/parts/editData/execution/editDataActions'; +import { EditDataModule } from 'sql/parts/grid/views/editData/editData.module'; +import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import { EDITDATA_SELECTOR } from 'sql/parts/grid/views/editData/editData.component'; +import { EditDataComponentParams } from 'sql/services/bootstrap/bootstrapParams'; + +/** + * Editor that hosts an action bar and a resultSetInput for an edit data session + */ +export class EditDataEditor extends BaseEditor { + + public static ID: string = 'workbench.editor.editDataEditor'; + + private _dimension: Dimension; + private _container: HTMLElement; + private _taskbar: Taskbar; + private _taskbarContainer: HTMLElement; + private _changeMaxRowsActionItem: ChangeMaxRowsActionItem; + private _stopRefreshTableAction: StopRefreshTableAction; + private _refreshTableAction: RefreshTableAction; + private _changeMaxRowsAction: ChangeMaxRowsAction; + private _spinnerElement: HTMLElement; + private _initialized: boolean = false; + + constructor( + @ITelemetryService _telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IContextMenuService private _contextMenuService: IContextMenuService, + @IQueryModelService private _queryModelService: IQueryModelService, + @IEditorDescriptorService private _editorDescriptorService: IEditorDescriptorService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IBootstrapService private _bootstrapService: IBootstrapService + ) { + super(EditDataEditor.ID, _telemetryService, themeService); + } + + // PUBLIC METHODS //////////////////////////////////////////////////////////// + + // Getters and Setters + public get editDataInput(): EditDataInput { return this.input; } + public get uri(): string { return this.input ? this.editDataInput.uri.toString() : undefined; } + public get tableName(): string { return this.editDataInput.tableName; } + + + /** + * Called to create the editor in the parent builder. + */ + public createEditor(parent: Builder): void { + const parentElement = parent.getHTMLElement(); + DOM.addClass(parentElement, 'side-by-side-editor'); + this._createTaskbar(parentElement); + this._container = document.createElement('div'); + this._container.style.height = 'calc(100% - 28px)'; + DOM.append(parentElement, this._container); + } + + /** + * Sets the input data for this editor. + */ + public setInput(newInput: EditDataInput, options?: EditorOptions): TPromise { + let oldInput = this.input; + if (!newInput.setup) { + this._initialized = false; + this._register(newInput.updateTaskbar((owner) => this._updateTaskbar(owner))); + this._register(newInput.editorInitializing((initializing) => this.onEditorInitializingChanged(initializing))); + this._register(newInput.showTableView(() => this._showTableView())); + newInput.onRowDropDownSet(this._changeMaxRowsActionItem.defaultRowCount); + newInput.setupComplete(); + } + + return super.setInput(newInput, options) + .then(() => this._updateInput(oldInput, newInput, options)); + } + + private onEditorInitializingChanged(initializing: boolean): void { + if (initializing) { + this.showSpinner(); + } else { + this._initialized = true; + this.hideSpinner(); + } + } + + /** + * Show the spinner element that shows something is happening, hidden by default + */ + public showSpinner(): void { + setTimeout(() => { + if (!this._initialized) { + this._spinnerElement.style.visibility = 'visible'; + } + }, 200); + } + + /** + * Hide the spinner element to show that something was happening, hidden by default + */ + public hideSpinner(): void { + this._spinnerElement.style.visibility = 'hidden'; + } + + /** + * Sets this editor and the sub-editors to visible. + */ + public setEditorVisible(visible: boolean, position: Position): void { + super.setEditorVisible(visible, position); + } + + /** + * Changes the position of the editor. + */ + public changePosition(position: Position): void { + super.changePosition(position); + } + + /** + * Called to indicate to the editor that the input should be cleared and resources associated with the + * input should be freed. + */ + public clearInput(): void { + this._disposeEditors(); + super.clearInput(); + } + + /** + * Updates the internal variable keeping track of the editor's size, and re-calculates the sash position. + * To be called when the container of this editor changes size. + */ + public layout(dimension: Dimension): void { + this._dimension = dimension; + let input: EditDataInput = this.input; + if (input) { + let uri: string = input.uri; + if (uri) { + this._queryModelService.resizeResultsets(uri); + } + } + } + + public dispose(): void { + this._disposeEditors(); + super.dispose(); + } + + public close(): void { + this.input.close(); + } + + /** + * Returns true if the results table for the current edit data session is visible + * Public for testing only. + */ + public _isResultsEditorVisible(): boolean { + if (!this.editDataInput) { + return false; + } + return this.editDataInput.visible; + } + + /** + * Makes visible the results table for the current edit data session + */ + private _showTableView(): void { + if (this._isResultsEditorVisible()) { + return; + } + + this._createTableViewContainer(); + this._setTableViewVisible(); + this.setInput(this.editDataInput, this.options); + } + + // PRIVATE METHODS //////////////////////////////////////////////////////////// + private _updateTaskbar(owner: EditDataInput): void { + // Update the taskbar if the owner of this call is being presented + if (owner.matches(this.editDataInput)) { + this._refreshTableAction.enabled = owner.refreshButtonEnabled; + this._stopRefreshTableAction.enabled = owner.stopButtonEnabled; + this._changeMaxRowsActionItem.setCurrentOptionIndex = owner.rowLimit; + } + } + + private _createTaskbar(parentElement: HTMLElement): void { + // Create QueryTaskbar + this._taskbarContainer = DOM.append(parentElement, DOM.$('div')); + this._taskbar = new Taskbar(this._taskbarContainer, this._contextMenuService, { + actionItemProvider: (action: Action) => this._getChangeMaxRowsAction(action) + }); + + // Create Actions for the toolbar + this._stopRefreshTableAction = this._instantiationService.createInstance(StopRefreshTableAction, this); + this._refreshTableAction = this._instantiationService.createInstance(RefreshTableAction, this); + this._changeMaxRowsAction = this._instantiationService.createInstance(ChangeMaxRowsAction, this); + + // Create HTML Elements for the taskbar + let separator = Taskbar.createTaskbarSeparator(); + let textSeperator = Taskbar.createTaskbarText(nls.localize('maxRowTaskbar', 'Max Rows:')); + + this._spinnerElement = Taskbar.createTaskbarSpinner(); + // Set the content in the order we desire + let content: ITaskbarContent[] = [ + { action: this._stopRefreshTableAction }, + { action: this._refreshTableAction }, + { element: separator }, + { element: textSeperator }, + { action: this._changeMaxRowsAction }, + { element: this._spinnerElement } + ]; + this._taskbar.setContent(content); + } + + /** + * Gets the IActionItem for the list of row number drop down + */ + private _getChangeMaxRowsAction(action: Action): IActionItem { + let actionID = ChangeMaxRowsAction.ID; + if (action.id === actionID) { + if (!this._changeMaxRowsActionItem) { + this._changeMaxRowsActionItem = this._instantiationService.createInstance(ChangeMaxRowsActionItem, this); + } + return this._changeMaxRowsActionItem; + } + + return null; + } + + /** + * Handles setting input for this editor. If this new input does not match the old input (e.g. a new file + * has been opened with the same editor, or we are opening the editor for the first time). + */ + private _updateInput(oldInput: EditDataInput, newInput: EditDataInput, options?: EditorOptions): TPromise { + let returnValue: TPromise; + + if (!newInput.matches(oldInput)) { + this._disposeEditors(); + + if (this._isResultsEditorVisible()) { + this._createTableViewContainer(); + let uri: string = newInput.uri; + if (uri) { + this._queryModelService.refreshResultsets(uri); + } + } + + returnValue = this._setNewInput(newInput, options); + } else { + this._setNewInput(newInput, options); + returnValue = TPromise.as(null); + } + + this._updateTaskbar(newInput); + return returnValue; + } + + /** + * Handles setting input and creating editors when this QueryEditor is either: + * - Opened for the first time + * - Opened with a new EditDataInput + */ + private _setNewInput(newInput: EditDataInput, options?: EditorOptions): TPromise { + if (this._isResultsEditorVisible()) { + // If both editors exist, create a joined promise and wait for both editors to be created + return this._bootstrapAngularAndResults(newInput, options); + } + + return TPromise.as(undefined); + } + /** + * Appends the HTML for the edit data table view + */ + private _createTableViewContainer() { + if (!this.editDataInput.container) { + this.editDataInput.container = DOM.append(this._container, DOM.$('.editDataContainer')); + this.editDataInput.container.style.height = '100%'; + } else { + DOM.append(this._container, this.editDataInput.container); + } + } + + private _disposeEditors(): void { + if (this._container) { + new Builder(this._container).clearChildren(); + } + } + + /** + * Load the angular components and record for this input that we have done so + */ + private _bootstrapAngularAndResults(input: EditDataInput, options: EditorOptions): TPromise { + super.setInput(input, options); + if (!input.hasBootstrapped) { + let uri = this.editDataInput.uri; + + // Pass the correct DataService to the new angular component + let dataService = this._queryModelService.getDataService(uri); + if (!dataService) { + throw new Error('DataService not found for URI: ' + uri); + } + + // Mark that we have bootstrapped + input.setBootstrappedTrue(); + + // Get the bootstrap params and perform the bootstrap + // Note: pass in input so on disposal this is cleaned up. + // Otherwise many components will be left around and be subscribed + // to events from the backing data service + const parent = this.editDataInput.container; + let params: EditDataComponentParams = { + dataService: dataService + }; + this._bootstrapService.bootstrap( + EditDataModule, + parent, + EDITDATA_SELECTOR, + params, + this.editDataInput); + } + return TPromise.as(null); + } + + private _setTableViewVisible(): void { + this.editDataInput.setVisibleTrue(); + } +} diff --git a/src/sql/parts/editData/execution/editDataActions.ts b/src/sql/parts/editData/execution/editDataActions.ts new file mode 100644 index 0000000000..52f4cc9422 --- /dev/null +++ b/src/sql/parts/editData/execution/editDataActions.ts @@ -0,0 +1,201 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Action, IActionItem, IActionRunner } from 'vs/base/common/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { EditDataEditor } from 'sql/parts/editData/editor/editDataEditor'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import nls = require('vs/nls'); +import * as dom from 'vs/base/browser/dom'; +const $ = dom.$; + +/** + * Action class that edit data based actions will extend + */ +export abstract class EditDataAction extends Action { + + private _classes: string[]; + + constructor(protected editor: EditDataEditor, id: string, enabledClass: string, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService) { + super(id); + this.enabled = true; + this.setClass(enabledClass); + } + + /** + * This method is executed when the button is clicked. + */ + public abstract run(): TPromise; + + protected setClass(enabledClass: string): void { + this._classes = []; + + if (enabledClass) { + this._classes.push(enabledClass); + } + this.class = this._classes.join(' '); + } + + /** + * Returns the URI of the given editor if it is not undefined and is connected. + */ + public isConnected(editor: EditDataEditor): boolean { + if (!editor || !editor.uri) { + return false; + } + return this._connectionManagementService.isConnected(editor.uri); + } +} + +/** + * Action class that refreshes the table for an edit data session + */ +export class RefreshTableAction extends EditDataAction { + private static EnabledClass = 'refresh'; + public static ID = 'refreshTableAction'; + + constructor(editor: EditDataEditor, + @IQueryModelService private _queryModelService: IQueryModelService, + @IConnectionManagementService _connectionManagementService: IConnectionManagementService, + @IMessageService private _messageService: IMessageService + ) { + super(editor, RefreshTableAction.ID, RefreshTableAction.EnabledClass, _connectionManagementService); + this.label = nls.localize('refresh', 'Refresh'); + } + + public run(): TPromise { + if (this.isConnected(this.editor)) { + let input = this.editor.editDataInput; + this._queryModelService.disposeEdit(input.uri).then((result) => { + this._queryModelService.initializeEdit(input.uri, input.schemaName, input.tableName, input.objectType, input.rowLimit); + }, error => { + this._messageService.show(Severity.Error, nls.localize('disposeEditFailure', 'Dispose Edit Failed With Error: ') + error); + }); + } + return TPromise.as(null); + } +} + +/** + * Action class that cancels the refresh data trigger in an edit data session + */ +export class StopRefreshTableAction extends EditDataAction { + + private static EnabledClass = 'stop'; + public static ID = 'stopRefreshAction'; + + constructor(editor: EditDataEditor, + @IQueryModelService private _queryModelService: IQueryModelService, + @IConnectionManagementService _connectionManagementService: IConnectionManagementService + ) { + super(editor, StopRefreshTableAction.ID, StopRefreshTableAction.EnabledClass, _connectionManagementService); + this.enabled = false; + this.label = nls.localize('stop', 'Stop'); + } + + public run(): TPromise { + let input = this.editor.editDataInput; + this._queryModelService.disposeEdit(input.uri); + return TPromise.as(null); + } +} + +/** + * Action class that is tied with ChangeMaxRowsActionItem + */ +export class ChangeMaxRowsAction extends EditDataAction { + + private static EnabledClass = ''; + public static ID = 'changeMaxRowsAction'; + + constructor(editor: EditDataEditor, + @IQueryModelService private _queryModelService: IQueryModelService, + @IConnectionManagementService _connectionManagementService: IConnectionManagementService + ) { + super(editor, ChangeMaxRowsAction.ID, undefined, _connectionManagementService); + this.enabled = false; + this.class = ChangeMaxRowsAction.EnabledClass; + } + + public run(): TPromise { + + return TPromise.as(null); + } +} + +/* + * Action item that handles the dropdown (combobox) that lists the avaliable number of row selections + * for an edit data session + */ +export class ChangeMaxRowsActionItem extends EventEmitter implements IActionItem { + + public actionRunner: IActionRunner; + public defaultRowCount: number; + private container: HTMLElement; + private start: HTMLElement; + private selectBox: SelectBox; + private toDispose: IDisposable[]; + private context: any; + private _options: string[]; + private _currentOptionsIndex: number; + + constructor(private _editor: EditDataEditor) { + super(); + this._options = ['200', '1000', '10000']; + this._currentOptionsIndex = 0; + this.toDispose = []; + this.selectBox = new SelectBox([], -1); + this._registerListeners(); + this._refreshOptions(); + this.defaultRowCount = Number(this._options[this._currentOptionsIndex]); + } + + public render(container: HTMLElement): void { + this.container = container; + this.selectBox.render(dom.append(container, $('.configuration.listDatabasesSelectBox'))); + } + + public setActionContext(context: any): void { + this.context = context; + } + + public isEnabled(): boolean { + return true; + } + + public set setCurrentOptionIndex(selection: number) { + this._currentOptionsIndex = this._options.findIndex(x => x === selection.toString()); + this._refreshOptions(); + } + + public focus(): void { + this.start.focus(); + } + + public blur(): void { + this.container.blur(); + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } + + private _refreshOptions(databaseIndex?: number): void { + this.selectBox.setOptions(this._options, this._currentOptionsIndex); + } + + private _registerListeners(): void { + this.toDispose.push(this.selectBox.onDidSelect(selection => { + this._currentOptionsIndex = this._options.findIndex(x => x === selection.selected); + this._editor.editDataInput.onRowDropDownSet(Number(selection.selected)); + })); + } +} diff --git a/src/sql/parts/fileBrowser/common/fileBrowserService.ts b/src/sql/parts/fileBrowser/common/fileBrowserService.ts new file mode 100644 index 0000000000..dfff84fa7d --- /dev/null +++ b/src/sql/parts/fileBrowser/common/fileBrowserService.ts @@ -0,0 +1,235 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper'; +import { IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import { FileBrowserTree } from 'sql/parts/fileBrowser/common/fileBrowserTree'; +import { FileNode } from 'sql/parts/fileBrowser/common/fileNode'; +import { FileBrowserDialog } from 'sql/parts/fileBrowser/fileBrowserDialog'; +import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces'; +import * as Constants from 'sql/common/constants'; +import Event, { Emitter } from 'vs/base/common/event'; +import Severity from 'vs/base/common/severity'; +import { localize } from 'vs/nls'; +import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +export class FileBrowserService implements IFileBrowserService { + public _serviceBrand: any; + private _providers: { [handle: string]: data.FileBrowserProvider; } = Object.create(null); + private _onAddFileTree = new Emitter(); + private _onExpandFolder = new Emitter(); + private _onPathValidate = new Emitter(); + private _pathToFileNodeMap: { [path: string]: FileNode } = {}; + private _expandResolveMap: { [key: string]: any } = {}; + static fileNodeId: number = 0; + + constructor(@IConnectionManagementService private _connectionService: IConnectionManagementService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IErrorMessageService private _errorMessageService: IErrorMessageService) { + } + + public registerProvider(providerId: string, provider: data.FileBrowserProvider): void { + this._providers[providerId] = provider; + } + + public get onAddFileTree(): Event { + return this._onAddFileTree.event; + } + + public get onExpandFolder(): Event { + return this._onExpandFolder.event; + } + + public get onPathValidate(): Event { + return this._onPathValidate.event; + } + + public openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable { + return new Promise((resolve, reject) => { + let provider = this.getProvider(ownerUri); + if (provider) { + provider.openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter).then(result => { + resolve(result); + }, error => { + reject(error); + }); + } else { + reject(Constants.InvalidProvider); + } + }); + } + + public onFileBrowserOpened(handle: number, fileBrowserOpenedParams: data.FileBrowserOpenedParams) { + if (fileBrowserOpenedParams.succeeded === true + && fileBrowserOpenedParams.fileTree + && fileBrowserOpenedParams.fileTree.rootNode + && fileBrowserOpenedParams.fileTree.selectedNode + ) { + var fileTree = this.convertFileTree(null, fileBrowserOpenedParams.fileTree.rootNode, fileBrowserOpenedParams.fileTree.selectedNode.fullPath, fileBrowserOpenedParams.ownerUri); + this._onAddFileTree.fire({rootNode: fileTree.rootNode, selectedNode: fileTree.selectedNode, expandedNodes: fileTree.expandedNodes}); + } else { + let genericErrorMessage = localize('fileBrowserErrorMessage', 'An error occured while loading the file browser.'); + let errorDialogTitle = localize('fileBrowserErrorDialogTitle', 'File Browser Error'); + let errorMessage = DialogHelper.isNullOrWhiteSpace(fileBrowserOpenedParams.message) ? genericErrorMessage : fileBrowserOpenedParams.message; + this._errorMessageService.showDialog(Severity.Error, errorDialogTitle, errorMessage); + } + } + + public expandFolderNode(fileNode: FileNode): Thenable { + this._pathToFileNodeMap[fileNode.fullPath] = fileNode; + let self = this; + return new Promise((resolve, reject) => { + let provider = this.getProvider(fileNode.ownerUri); + if (provider) { + provider.expandFolderNode(fileNode.ownerUri, fileNode.fullPath).then(result => { + var mapKey = self.generateResolveMapKey(fileNode.ownerUri, fileNode.fullPath); + self._expandResolveMap[mapKey] = resolve; + }, error => { + reject(error); + }); + } else { + reject(Constants.InvalidProvider); + } + }); + } + + public onFolderNodeExpanded(handle: number, fileBrowserExpandedParams: data.FileBrowserExpandedParams) { + var mapKey = this.generateResolveMapKey(fileBrowserExpandedParams.ownerUri, fileBrowserExpandedParams.expandPath); + var expandResolve = this._expandResolveMap[mapKey]; + if (expandResolve) { + if (fileBrowserExpandedParams.succeeded === true) + { + // get the expanded folder node + var expandedNode = this._pathToFileNodeMap[fileBrowserExpandedParams.expandPath]; + if (expandedNode) { + if (fileBrowserExpandedParams.children && fileBrowserExpandedParams.children.length > 0) { + expandedNode.children = this.convertChildren(expandedNode, fileBrowserExpandedParams.children, fileBrowserExpandedParams.ownerUri); + } + expandResolve(expandedNode.children ? expandedNode.children : []); + this._onExpandFolder.fire(expandedNode); + } else { + expandResolve([]); + } + } else { + expandResolve([]); + } + } + } + + public validateFilePaths(ownerUri: string, serviceType: string, selectedFiles: string[]): Thenable { + return new Promise((resolve, reject) => { + let provider = this.getProvider(ownerUri); + if (provider) { + provider.validateFilePaths(ownerUri, serviceType, selectedFiles).then(result => { + resolve(result); + }, error => { + reject(error); + }); + } else { + reject(Constants.InvalidProvider); + } + }); + } + + public onFilePathsValidated(handle: number, fileBrowserValidatedParams: data.FileBrowserValidatedParams) { + this._onPathValidate.fire(fileBrowserValidatedParams); + } + + public closeFileBrowser(ownerUri: string): Thenable { + let provider = this.getProvider(ownerUri); + if (provider) { + return provider.closeFileBrowser(ownerUri); + } + return Promise.resolve(undefined); + } + + private generateResolveMapKey(ownerUri: string, expandPath: string): string { + return ownerUri + ':' + expandPath; + } + private getProvider(connectionUri: string): data.FileBrowserProvider { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + if (providerId) { + return this._providers[providerId]; + } else { + return undefined; + } + } + + private convertFileTree(parentNode: FileNode, fileTreeNode: data.FileTreeNode, expandPath: string, ownerUri: string): FileBrowserTree { + FileBrowserService.fileNodeId += 1; + var expandedNodes: FileNode[] = []; + var selectedNode: FileNode; + var fileNode = new FileNode(FileBrowserService.fileNodeId.toString(), + fileTreeNode.name, + fileTreeNode.fullPath, + fileTreeNode.isFile, + fileTreeNode.isExpanded, + ownerUri, + parentNode + ); + + if (fileNode.isExpanded === true) { + expandedNodes.push(fileNode); + } + + if (fileTreeNode.children) { + var convertedChildren = []; + for (var i = 0; i < fileTreeNode.children.length; i++) { + var convertedFileTree: FileBrowserTree = this.convertFileTree(fileNode, fileTreeNode.children[i], expandPath, ownerUri); + convertedChildren.push(convertedFileTree.rootNode); + + if (convertedFileTree.expandedNodes.length > 0) { + expandedNodes = expandedNodes.concat(convertedFileTree.expandedNodes); + } + + if (convertedFileTree.selectedNode) { + selectedNode = convertedFileTree.selectedNode; + } + } + + if (convertedChildren.length > 0) { + fileNode.children = convertedChildren; + } + } + + if (!selectedNode && fileTreeNode.fullPath === expandPath) { + selectedNode = fileNode; + } + + // Assume every folder has children initially + if (fileTreeNode.isFile === false) { + fileNode.hasChildren = true; + } + + return { rootNode: fileNode, selectedNode: selectedNode, expandedNodes: expandedNodes }; + } + + private convertChildren(expandedNode: FileNode, childrenToConvert: data.FileTreeNode[], ownerUri: string): FileNode[] { + var childrenNodes = []; + + for (var i = 0; i < childrenToConvert.length; i++) { + FileBrowserService.fileNodeId += 1; + var childNode = new FileNode(FileBrowserService.fileNodeId.toString(), + childrenToConvert[i].name, + childrenToConvert[i].fullPath, + childrenToConvert[i].isFile, + childrenToConvert[i].isExpanded, + ownerUri, + expandedNode + ); + + // Assume every folder has children initially + if (childrenToConvert[i].isFile === false) { + childNode.hasChildren = true; + } + childrenNodes.push(childNode); + } + + return childrenNodes; + } +} diff --git a/src/sql/parts/fileBrowser/common/fileBrowserTree.ts b/src/sql/parts/fileBrowser/common/fileBrowserTree.ts new file mode 100644 index 0000000000..358138635a --- /dev/null +++ b/src/sql/parts/fileBrowser/common/fileBrowserTree.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { FileNode } from 'sql/parts/fileBrowser/common/fileNode'; + +/** + * File tree info needed to render initially + */ +export class FileBrowserTree { + public rootNode: FileNode; + public selectedNode: FileNode; + public expandedNodes: FileNode[]; +} diff --git a/src/sql/parts/fileBrowser/common/fileNode.ts b/src/sql/parts/fileBrowser/common/fileNode.ts new file mode 100644 index 0000000000..fe1a3f79ff --- /dev/null +++ b/src/sql/parts/fileBrowser/common/fileNode.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +'use strict'; +import { generateUuid } from 'vs/base/common/uuid'; + +/** + * File/folder node in file browser + * FileTreeNode is converted to this FileNode for UI interactions + */ +export class FileNode { + /** + * Node id + */ + public id: string; + + /** + * Connection uri + */ + public ownerUri: string; + + /** + * File or folder name + */ + public name: string; + + /** + * Full path of file or folder + */ + public fullPath: string; + + /** + * Parent node + */ + public parent: FileNode; + + /** + * Children nodes + */ + public children: FileNode[]; + + /** + * Is the node expanded + */ + public isExpanded: boolean; + + /** + * Is the node file or folder + */ + public isFile: boolean; + + /** + * Does this node have children + */ + public hasChildren: boolean; + + constructor(id: string, name: string, fullPath: string, isFile: boolean, isExpanded: boolean, ownerUri: string, parent:FileNode) { + if (id) { + this.id = id; + } else { + this.id = generateUuid(); + } + + this.name = name; + this.fullPath = fullPath; + this.isFile = isFile; + this.ownerUri = ownerUri; + this.isExpanded = isExpanded; + this.parent = parent; + } +} \ No newline at end of file diff --git a/src/sql/parts/fileBrowser/common/fileValidationServiceConstants.ts b/src/sql/parts/fileBrowser/common/fileValidationServiceConstants.ts new file mode 100644 index 0000000000..7839dc9bbf --- /dev/null +++ b/src/sql/parts/fileBrowser/common/fileValidationServiceConstants.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* + * List of services that provide file validation callback to file browser service + */ + +export const backup: string = "Backup"; +export const restore: string = "Restore"; \ No newline at end of file diff --git a/src/sql/parts/fileBrowser/common/interfaces.ts b/src/sql/parts/fileBrowser/common/interfaces.ts new file mode 100644 index 0000000000..d017c51d29 --- /dev/null +++ b/src/sql/parts/fileBrowser/common/interfaces.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import { FileBrowserTree } from 'sql/parts/fileBrowser/common/fileBrowserTree'; +import { FileNode } from 'sql/parts/fileBrowser/common/fileNode'; +import Event from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IFileBrowserDialogController = createDecorator('fileBrowserDialogService'); +export interface IFileBrowserDialogController { + _serviceBrand: any; + /** + * Show file browser dialog + */ + showDialog(ownerUri: string, + expandPath: string, + fileFilters: [{label: string, filters: string[]}], + fileValidationServiceType: string, + isWide: boolean, + handleOnOk: (path: string) => void): void; +} + +export const IFileBrowserService = createDecorator('fileBrowserService'); +export interface IFileBrowserService { + _serviceBrand: any; + onAddFileTree: Event; + onExpandFolder: Event; + onPathValidate: Event; + + /** + * Register file browser provider + */ + registerProvider(providerId: string, provider: data.FileBrowserProvider): void; + + /** + * Open file browser + */ + openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable; + + /** + * Event called when file browser is opened + */ + onFileBrowserOpened(handle: number, fileBrowserOpenedParams: data.FileBrowserOpenedParams); + + /** + * Expand folder node + */ + expandFolderNode(fileNode: FileNode): Thenable; + + /** + * Event called when children nodes are retrieved + */ + onFolderNodeExpanded(handle: number, fileBrowserExpandedParams: data.FileBrowserExpandedParams); + + /** + * Validate selected file paths + */ + validateFilePaths(ownerUri: string, serviceType: string, selectedFiles: string[]): Thenable; + + /** + * Event called when the validation is complete + */ + onFilePathsValidated(handle: number, fileBrowserValidatedParams: data.FileBrowserValidatedParams); + + /** + * Close file browser + */ + closeFileBrowser(ownerUri: string): Thenable; +} \ No newline at end of file diff --git a/src/sql/parts/fileBrowser/fileBrowserController.ts b/src/sql/parts/fileBrowser/fileBrowserController.ts new file mode 100644 index 0000000000..ac7c9de5f0 --- /dev/null +++ b/src/sql/parts/fileBrowser/fileBrowserController.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import treedefaults = require('vs/base/parts/tree/browser/treeDefaults'); +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; + +/** + * Extends the tree controller to handle mouse and keyboard events on the tree elements + */ +export class FileBrowserController extends treedefaults.DefaultController { + + constructor() { + super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN }); + } + + protected onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean { + // In file browser, double clicking an element calls tree.dispose(). There should not be any tree events after selection. + if (event.detail === 2) { + var payload = { origin: origin, originalEvent: event }; + if (tree.getInput() === element) { + tree.clearFocus(payload); + tree.clearSelection(payload); + } else { + var isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown'; + if (!isMouseDown) { + event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise + } + event.stopPropagation(); + tree.DOMFocus(); + tree.setSelection([element], payload); + } + return true; + } else { + return super.onLeftClick(tree, element, event, origin); + } + } + + protected onEnter(tree: ITree, event: IKeyboardEvent): boolean { + var payload = { origin: 'keyboard', originalEvent: event }; + + if (tree.getHighlight()) { + return false; + } + var focus = tree.getFocus(); + if (focus) { + // In file browser, pressing enter key on an element will close dialog and call tree.dispose(). There should not be any tree events after selection. + tree.setSelection([focus], payload); + } + return true; + } +} \ No newline at end of file diff --git a/src/sql/parts/fileBrowser/fileBrowserDataSource.ts b/src/sql/parts/fileBrowser/fileBrowserDataSource.ts new file mode 100644 index 0000000000..9740180d76 --- /dev/null +++ b/src/sql/parts/fileBrowser/fileBrowserDataSource.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces'; +import { FileNode } from 'sql/parts/fileBrowser/common/fileNode'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; + +/** + * Implements the DataSource(that returns a parent/children of an element) for the file browser + */ +export class FileBrowserDataSource implements IDataSource { + + constructor( + @IFileBrowserService private _fileBrowserService: IFileBrowserService + ) { + } + + /** + * Returns the unique identifier of the given element. + * No more than one element may use a given identifier. + */ + public getId(tree: ITree, element: any): string { + if (element instanceof FileNode) { + return (element).id; + } else { + return undefined; + } + } + + /** + * Returns a boolean value indicating whether the element has children. + */ + public hasChildren(tree: ITree, element: any): boolean { + if (element instanceof FileNode) { + return (element).hasChildren; + } + return false; + } + + /** + * Returns the element's children as an array in a promise. + */ + public getChildren(tree: ITree, element: any): TPromise { + return new TPromise((resolve) => { + if (element instanceof FileNode) { + var node = element; + if (node.children) { + resolve(node.children); + } else { + this._fileBrowserService.expandFolderNode(node).then((nodeChildren) => { + resolve(nodeChildren); + }, expandError => { + resolve([]); + }); + } + } else { + resolve([]); + } + }); + } + + /** + * Returns the element's parent in a promise. + */ + public getParent(tree: ITree, element: any): TPromise { + return TPromise.as(null); + } +} \ No newline at end of file diff --git a/src/sql/parts/fileBrowser/fileBrowserDialog.ts b/src/sql/parts/fileBrowser/fileBrowserDialog.ts new file mode 100644 index 0000000000..89bec3ea00 --- /dev/null +++ b/src/sql/parts/fileBrowser/fileBrowserDialog.ts @@ -0,0 +1,243 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!sql/media/icons/common-icons'; +import 'vs/css!./media/fileBrowserDialog'; +import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox'; +import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; +import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper'; +import { Modal } from 'sql/base/browser/ui/modal/modal'; +import { attachModalDialogStyler } from 'sql/common/theme/styler'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import { FileNode } from 'sql/parts/fileBrowser/common/fileNode'; +import { FileBrowserTreeView } from 'sql/parts/fileBrowser/fileBrowserTreeView'; +import { FileBrowserViewModel } from 'sql/parts/fileBrowser/fileBrowserViewModel'; + +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { Builder } from 'vs/base/browser/builder'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; +import Event, { Emitter } from 'vs/base/common/event'; +import { localize } from 'vs/nls'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { attachInputBoxStyler, attachButtonStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import * as DOM from 'vs/base/browser/dom'; + +export class FileBrowserDialog extends Modal { + private _viewModel: FileBrowserViewModel; + private _bodyBuilder: Builder; + private _filePathInputBox: InputBox; + private _fileFilterSelectBox: SelectBox; + private _okButton: Button; + private _cancelButton: Button; + private _onOk = new Emitter(); + public onOk: Event = this._onOk.event; + + private _treeContainer: Builder; + private _fileBrowserTreeView: FileBrowserTreeView; + private _selectedFilePath: string; + private _isFolderSelected: boolean; + + constructor(title: string, + @IPartService partService: IPartService, + @IWorkbenchThemeService private _themeService: IWorkbenchThemeService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IContextViewService private _contextViewService: IContextViewService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(title, TelemetryKeys.Backup, partService, telemetryService, contextKeyService, { isFlyout: true, hasTitleIcon: false, hasBackButton: true, hasSpinner: true }); + this._viewModel = this._instantiationService.createInstance(FileBrowserViewModel); + this._viewModel.onAddFileTree(args => this.handleOnAddFileTree(args.rootNode, args.selectedNode, args.expandedNodes)); + this._viewModel.onPathValidate(args => this.handleOnValidate(args.succeeded, args.message)); + } + + protected layout(height?: number): void { + } + + protected renderBody(container: HTMLElement) { + new Builder(container).div({ 'class': 'file-browser-dialog' }, (bodyBuilder) => { + this._bodyBuilder = bodyBuilder; + }); + } + + public render() { + super.render(); + attachModalDialogStyler(this, this._themeService); + + if (this.backButton) { + + this._register(DOM.addDisposableListener(this.backButton.getElement(), DOM.EventType.CLICK, () => { + this.close(); + })); + this._register(attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND })); + } + + this._bodyBuilder.div({ class: 'tree-view' }, (treeContainer) => { + this._treeContainer = treeContainer; + }); + + this._bodyBuilder.div({ class: 'option-section' }, (tableWrapper) => { + tableWrapper.element('table', { class: 'file-table-content' }, (tableContainer) => { + let pathLabel = localize('filebrowser.filepath', 'Selected path'); + let pathBuilder = DialogHelper.appendRow(tableContainer, pathLabel, 'file-input-label', 'file-input-box'); + this._filePathInputBox = new InputBox(pathBuilder.getHTMLElement(), this._contextViewService); + + this._fileFilterSelectBox = new SelectBox(['*'], '*'); + let filterLabel = localize('fileFilter', 'Files of type'); + let filterBuilder = DialogHelper.appendRow(tableContainer, filterLabel, 'file-input-label', 'file-input-box'); + DialogHelper.appendInputSelectBox(filterBuilder, this._fileFilterSelectBox); + }); + }); + + this._okButton = this.addFooterButton(localize('ok', 'OK'), () => this.ok()); + this._okButton.enabled = false; + this._cancelButton = this.addFooterButton(localize('discard', 'Discard'), () => this.close()); + + this.registerListeners(); + this.updateTheme(); + } + + public open(ownerUri: string, + expandPath: string, + fileFilters: [{ label: string, filters: string[] }], + fileValidationServiceType: string, + ) { + this._viewModel.initialize(ownerUri, expandPath, fileFilters, fileValidationServiceType); + this._fileFilterSelectBox.setOptions(this._viewModel.formattedFileFilters); + this._fileFilterSelectBox.select(0); + this._filePathInputBox.value = expandPath; + this._isFolderSelected = true; + this.enableOkButton(); + this.showSpinner(); + this.show(); + + this._fileBrowserTreeView = this._instantiationService.createInstance(FileBrowserTreeView); + this._fileBrowserTreeView.setOnClickedCallback((arg) => this.onClicked(arg)); + this._fileBrowserTreeView.setOnDoubleClickedCallback((arg) => this.onDoubleClicked(arg)); + this._viewModel.openFileBrowser(0, false); + } + + /* enter key */ + protected onAccept() { + if (this._okButton.enabled === true) { + this.ok(); + } + } + + private handleOnAddFileTree(rootNode: FileNode, selectedNode: FileNode, expandedNodes: FileNode[]) { + this.updateFileTree(rootNode, selectedNode, expandedNodes); + this.hideSpinner(); + } + + private enableOkButton() { + if (DialogHelper.isNullOrWhiteSpace(this._selectedFilePath) || this._isFolderSelected === true) { + this._okButton.enabled = false; + } else { + this._okButton.enabled = true; + } + } + + private onClicked(selectedNode: FileNode) { + this._filePathInputBox.value = selectedNode.fullPath; + + if (selectedNode.isFile === true) { + this._isFolderSelected = false; + } else { + this._isFolderSelected = true; + } + + this.enableOkButton(); + } + + private onDoubleClicked(selectedNode: FileNode) { + if (selectedNode.isFile === true) { + this.ok(); + } + } + + private onFilePathChange(filePath: string) { + this._isFolderSelected = false; + this._selectedFilePath = filePath; + + this._filePathInputBox.hideMessage(); + this.enableOkButton(); + } + + private onFilePathBlur(param) { + if (!DialogHelper.isNullOrWhiteSpace(param.value)) { + this._viewModel.validateFilePaths([param.value]); + } + } + + private ok() { + this._onOk.fire(this._selectedFilePath); + this.close(); + } + + private handleOnValidate(succeeded: boolean, errorMessage: string) { + if (succeeded === false) { + if (DialogHelper.isNullOrWhiteSpace(errorMessage)) { + errorMessage = 'The provided path is invalid.'; + } + this._filePathInputBox.showMessage({ type: MessageType.ERROR, content: errorMessage }); + } + } + + private close() { + if (this._fileBrowserTreeView) { + this._fileBrowserTreeView.dispose(); + } + this._onOk.dispose(); + this.hide(); + this._viewModel.closeFileBrowser(); + } + + private updateFileTree(rootNode: FileNode, selectedNode: FileNode, expandedNodes: FileNode[]): void { + this._fileBrowserTreeView.renderBody(this._treeContainer.getHTMLElement(), rootNode, selectedNode, expandedNodes); + this._fileBrowserTreeView.setVisible(true); + this._fileBrowserTreeView.layout(DOM.getTotalHeight(this._treeContainer.getHTMLElement())); + } + + private onFilterSelectChanged(filterIndex) { + this.showSpinner(); + this._viewModel.openFileBrowser(filterIndex, true); + } + + private registerListeners(): void { + this._register(this._fileFilterSelectBox.onDidSelect(selection => { + this.onFilterSelectChanged(selection.index); + })); + this._register(this._filePathInputBox.onDidChange(e => { + this.onFilePathChange(e); + })); + this._register(this._filePathInputBox.onLoseFocus(params => { + this.onFilePathBlur(params); + })); + + // Theme styler + this._register(attachInputBoxStyler(this._filePathInputBox, this._themeService)); + this._register(attachSelectBoxStyler(this._fileFilterSelectBox, this._themeService)); + this._register(attachButtonStyler(this._okButton, this._themeService)); + this._register(attachButtonStyler(this._cancelButton, this._themeService)); + + this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme())); + } + + // Update theming that is specific to file browser + private updateTheme(): void { + if (this._treeContainer) { + this._treeContainer.style('background-color', this.headerAndFooterBackground); + } + } +} \ No newline at end of file diff --git a/src/sql/parts/fileBrowser/fileBrowserDialogController.ts b/src/sql/parts/fileBrowser/fileBrowserDialogController.ts new file mode 100644 index 0000000000..b8d9c112d5 --- /dev/null +++ b/src/sql/parts/fileBrowser/fileBrowserDialogController.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces'; +import { FileBrowserDialog } from 'sql/parts/fileBrowser/fileBrowserDialog'; +import { localize } from 'vs/nls'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +/** + * File browser dialog service + */ +export class FileBrowserDialogController implements IFileBrowserDialogController { + _serviceBrand: any; + private _fileBrowserDialog: FileBrowserDialog; + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService + ) { + } + + public showDialog(ownerUri: string, + expandPath: string, + fileFilters: [{label: string, filters: string[]}], + fileValidationServiceType: string, + isWide: boolean, + handleOnOk: (path: string) => void + ) { + if (!this._fileBrowserDialog) { + this._fileBrowserDialog = this._instantiationService.createInstance(FileBrowserDialog, localize('filebrowser.selectFile', "Select a file")); + this._fileBrowserDialog.render(); + } + + this._fileBrowserDialog.setWide(isWide); + this._fileBrowserDialog.onOk((filepath) => handleOnOk(filepath)); + this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType); + } +} diff --git a/src/sql/parts/fileBrowser/fileBrowserRenderer.ts b/src/sql/parts/fileBrowser/fileBrowserRenderer.ts new file mode 100644 index 0000000000..aa3a2dcad6 --- /dev/null +++ b/src/sql/parts/fileBrowser/fileBrowserRenderer.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as Utils from 'sql/parts/connection/common/utils'; +import { FileNode } from 'sql/parts/fileBrowser/common/fileNode'; +import { $ } from 'vs/base/browser/dom'; +import dom = require('vs/base/browser/dom'); +import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree'; +import { FileKind } from 'vs/platform/files/common/files'; +import URI from 'vs/base/common/uri'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { FileLabel } from 'vs/workbench/browser/labels'; +import { IFileTemplateData } from 'vs/workbench/parts/files/browser/views/explorerViewer'; + +/** + * Renders the tree items. + * Uses the dom template to render file browser. + */ +export class FileBrowserRenderer implements IRenderer { + public static readonly FILE_HEIGHT = 22; + private static readonly FILE_TEMPLATE_ID = 'carbonFileBrowser'; + + constructor( + @IInstantiationService private instantiationService: IInstantiationService + ) { + } + + /** + * Returns the element's height in the tree, in pixels. + */ + public getHeight(tree: ITree, element: any): number { + return FileBrowserRenderer.FILE_HEIGHT; + } + + /** + * Returns a template ID for a given element. + */ + public getTemplateId(tree: ITree, element: any): string { + return FileBrowserRenderer.FILE_TEMPLATE_ID; + } + + /** + * Render template in a dom element based on template id + */ + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): IFileTemplateData { + const label = this.instantiationService.createInstance(FileLabel, container, void 0); + return { label, container }; + } + + /** + * Render a element, given an object bag returned by the template + */ + public renderElement(tree: ITree, element: FileNode, templateId: string, templateData: IFileTemplateData): void { + if (element) { + templateData.label.element.style.display = 'block'; + const extraClasses = ['explorer-item']; + + var fileuri = URI.file(element.fullPath); + var filekind; + if (element.parent === null) { + filekind = FileKind.ROOT_FOLDER; + } else if (element.isFile === false) { + filekind = FileKind.FOLDER; + } else { + filekind = FileKind.FILE; + } + + templateData.label.setFile(fileuri, { hidePath: true, fileKind: filekind, extraClasses }); + } + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: IFileTemplateData): void { + templateData.label.dispose(); + } +} \ No newline at end of file diff --git a/src/sql/parts/fileBrowser/fileBrowserTreeView.ts b/src/sql/parts/fileBrowser/fileBrowserTreeView.ts new file mode 100644 index 0000000000..0fa982463c --- /dev/null +++ b/src/sql/parts/fileBrowser/fileBrowserTreeView.ts @@ -0,0 +1,171 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import { FileBrowserDataSource } from 'sql/parts/fileBrowser/fileBrowserDataSource'; +import { FileBrowserController } from 'sql/parts/fileBrowser/fileBrowserController'; +import { FileBrowserRenderer } from 'sql/parts/fileBrowser/fileBrowserRenderer'; +import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces'; +import { FileNode } from 'sql/parts/fileBrowser/common/fileNode'; +import errors = require('vs/base/common/errors'); +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as DOM from 'vs/base/browser/dom'; +import nls = require('vs/nls'); +import { DefaultFilter, DefaultAccessibilityProvider, DefaultController, DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; + +/** + * Implements tree view for file browser + */ +export class FileBrowserTreeView { + private _tree: ITree; + private _toDispose: IDisposable[] = []; + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService, + @IFileBrowserService private _fileBrowserService: IFileBrowserService, + @IErrorMessageService private _errorMessageService: IErrorMessageService, + @IThemeService private _themeService: IThemeService + ) { + } + + /** + * Render the view body + */ + public renderBody(container: HTMLElement, rootNode: FileNode, selectedNode: FileNode, expandedNodes: FileNode[]): void { + if (!this._tree) { + DOM.addClass(container, 'show-file-icons'); + this._tree = this.createFileBrowserTree(container, this._instantiationService); + this._toDispose.push(this._tree.addListener('selection', (event) => this.onSelected(event))); + this._toDispose.push(this._fileBrowserService.onExpandFolder(fileNode => this._tree.refresh(fileNode))); + this._toDispose.push(attachListStyler(this._tree, this._themeService)); + this._tree.DOMFocus(); + } + + if (rootNode) { + this._tree.setInput(rootNode).then(() => { + if (expandedNodes) { + this._tree.expandAll(expandedNodes); + } + if (selectedNode) { + this._tree.select(selectedNode); + this._tree.setFocus(selectedNode); + } + this._tree.getFocus(); + }, errors.onUnexpectedError); + } + } + + /** + * Create a file browser tree + */ + public createFileBrowserTree(treeContainer: HTMLElement, instantiationService: IInstantiationService): Tree { + const dataSource = instantiationService.createInstance(FileBrowserDataSource); + const renderer = instantiationService.createInstance(FileBrowserRenderer); + const controller = instantiationService.createInstance(FileBrowserController); + const dnd = new DefaultDragAndDrop(); + const filter = new DefaultFilter(); + const sorter = null; + const accessibilityProvider = new DefaultAccessibilityProvider(); + + return new Tree(treeContainer, { + dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider + }, { + indentPixels: 10, + twistiePixels: 12, + ariaLabel: nls.localize({ key: 'regTreeAriaLabel', comment: ['FileBrowserTree'] }, 'File browser tree') + }); + } + + /** + * Refresh the tree + */ + public refreshTree(rootNode: FileNode): void { + let selectedElement: any; + let targetsToExpand: any[]; + + // Focus + this._tree.DOMFocus(); + + if (this._tree) { + let selection = this._tree.getSelection(); + if (selection && selection.length === 1) { + selectedElement = selection[0]; + } + targetsToExpand = this._tree.getExpandedElements(); + } + + if (rootNode) { + this._tree.setInput(rootNode).then(() => { + // Make sure to expand all folders that were expanded in the previous session + if (targetsToExpand) { + this._tree.expandAll(targetsToExpand); + } + if (selectedElement) { + this._tree.select(selectedElement); + this._tree.setFocus(selectedElement); + } + this._tree.getFocus(); + }, errors.onUnexpectedError); + } + } + + private onSelected(event: any) { + let selection = this._tree.getSelection(); + + if (selection && selection.length > 0 && (selection[0] instanceof FileNode)) { + let isMouseOrigin = event.payload && (event.payload.origin === 'mouse'); + let isSingleClick = isMouseOrigin && event.payload.originalEvent && event.payload.originalEvent.detail === 1; + let isDoubleClick = isMouseOrigin && event.payload.originalEvent && event.payload.originalEvent.detail === 2; + if (isSingleClick) { + this.onClickedCallback(event.selection[0]); + } else if (isDoubleClick) { + this.onDoublieClickedCallback(event.selection[0]); + } + } + } + + public onClickedCallback: any; + public setOnClickedCallback(fn: any) { + this.onClickedCallback = fn; + } + + public onDoublieClickedCallback: any; + public setOnDoubleClickedCallback(fn: any) { + this.onDoublieClickedCallback = fn; + } + + /** + * set the layout of the view + */ + public layout(height?: number): void { + this._tree.layout(height); + } + + /** + * set the visibility of the view + */ + public setVisible(visible: boolean): void { + if (visible) { + this._tree.onVisible(); + } else { + this._tree.onHidden(); + } + } + + /** + * dispose the file browser tree view + */ + public dispose(): void { + if (this._tree) { + this._tree.dispose(); + } + this._toDispose = dispose(this._toDispose); + } +} \ No newline at end of file diff --git a/src/sql/parts/fileBrowser/fileBrowserViewModel.ts b/src/sql/parts/fileBrowser/fileBrowserViewModel.ts new file mode 100644 index 0000000000..48a42e0466 --- /dev/null +++ b/src/sql/parts/fileBrowser/fileBrowserViewModel.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces'; +import { localize } from 'vs/nls'; + +/** + * View model for file browser dialog + */ +export class FileBrowserViewModel { + private _ownerUri: string; + private _expandPath: string; + private _fileFilters: [{label: string, filters: string[]}]; + private _fileValidationServiceType: string; + public formattedFileFilters: string[]; + + constructor(@IFileBrowserService private _fileBrowserService: IFileBrowserService) { + } + + public onAddFileTree(onAddFileTreeCallback) { + this._fileBrowserService.onAddFileTree(args => onAddFileTreeCallback(args)); + } + + public onPathValidate(onPathValidateCallback) { + this._fileBrowserService.onPathValidate(args => onPathValidateCallback(args)); + } + + public initialize(ownerUri: string, + expandPath: string, + fileFilters: [ {label: string, filters: string[]} ], + fileValidationServiceType: string, + ) { + this._ownerUri = ownerUri; + this._expandPath = expandPath; + this._fileValidationServiceType = fileValidationServiceType; + + if (!fileFilters) { + this._fileFilters = [ {label: localize('allFiles', 'All files'), filters: ['*']} ]; + } else { + this._fileFilters = fileFilters; + } + this.formattedFileFilters = []; + for (var i = 0; i < this._fileFilters.length; i++) { + var filterStr = this._fileFilters[i].label + '(' + this._fileFilters[i].filters.join(';') + ')'; + this.formattedFileFilters.push(filterStr); + } + } + + public validateFilePaths(selectedFiles: string[]) { + this._fileBrowserService.validateFilePaths(this._ownerUri, this._fileValidationServiceType, selectedFiles); + } + + public openFileBrowser(filterIndex: number, changeFilter: boolean) { + if (this._fileFilters[filterIndex]) { + this._fileBrowserService.openFileBrowser(this._ownerUri, this._expandPath, this._fileFilters[filterIndex].filters, changeFilter); + } + } + + public closeFileBrowser() { + this._fileBrowserService.closeFileBrowser(this._ownerUri); + } +} \ No newline at end of file diff --git a/src/sql/parts/fileBrowser/media/fileBrowserDialog.css b/src/sql/parts/fileBrowser/media/fileBrowserDialog.css new file mode 100644 index 0000000000..7ce4a05929 --- /dev/null +++ b/src/sql/parts/fileBrowser/media/fileBrowserDialog.css @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.file-browser-dialog { + height: 100%; + padding-top: 12px; + padding-left: 12px; + padding-right: 12px; + box-sizing: border-box; +} + +.file-browser-dialog .tree-view { + height: calc(100% - 90px); +} + +.file-table-content { + width: 100%; +} + +.file-browser-dialog .option-section { + padding-top: 10px; + height: 90px; + box-sizing: border-box; +} + +.file-input-label { + width: 50px; + padding-bottom: 5px; +} + +.file-input-box { + width: 200px; + padding-bottom: 5px; +} + +.file-browser-dialog .explorer-item { + height: 22px; + line-height: 22px; +} + +.file-browser-dialog .show-file-icons .monaco-tree-row .content { + display: flex; +} + +.file-browser-dialog .monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content:before { + background-size: 16px; + background-position: 50% 50%; + background-repeat: no-repeat; + padding-right: 6px; + width: 16px; + height: 22px; + display: inline-block; + vertical-align: top; + content: ' '; + position: initial; +} diff --git a/src/sql/parts/grid/common/gridContentEvents.ts b/src/sql/parts/grid/common/gridContentEvents.ts new file mode 100644 index 0000000000..9ad8e5bfad --- /dev/null +++ b/src/sql/parts/grid/common/gridContentEvents.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export let ResizeContents = 'ResizeContents'; +export let RefreshContents = 'RefreshContents'; +export let ToggleResultPane = 'ToggleResultPane'; +export let ToggleMessagePane = 'ToggleMessagePane'; +export let CopySelection = 'CopySelection'; +export let CopyWithHeaders = 'CopyWithHeaders'; +export let CopyMessagesSelection = 'CopyMessagesSelection'; +export let SelectAll = 'SelectAll'; +export let SelectAllMessages = 'SelectAllMessages'; +export let SaveAsCsv = 'SaveAsCSV'; +export let SaveAsJSON = 'SaveAsJSON'; +export let SaveAsExcel = 'SaveAsExcel'; diff --git a/src/sql/parts/grid/common/interfaces.ts b/src/sql/parts/grid/common/interfaces.ts new file mode 100644 index 0000000000..eeb65950ba --- /dev/null +++ b/src/sql/parts/grid/common/interfaces.ts @@ -0,0 +1,124 @@ + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IColumnDefinition, IObservableCollection, IGridDataRow } from 'angular2-slickgrid'; + +export interface ISlickRange { + fromCell: number; + fromRow: number; + toCell: number; + toRow: number; +} + +export interface IGridIcon { + showCondition: () => boolean; + icon: () => string; + hoverText: () => string; + functionality: (batchId: number, resultId: number, index: number) => void; +} + +export interface IMessageLink { + uri: string; + text: string; +} + +export interface IMessage { + batchId?: number; + time: string; + message: string; + isError: boolean; + link?: IMessageLink; +} + +export interface IGridIcon { + showCondition: () => boolean; + icon: () => string; + hoverText: () => string; + functionality: (batchId: number, resultId: number, index: number) => void; +} + +/** + * Simplified interface for a Range object returned by the Rangy javascript plugin + * + * @export + * @interface IRange + */ +export interface IRange { + selectNodeContents(el): void; + /** + * Returns any user-visible text covered under the range, using standard HTML Range API calls + * + * @returns {string} + * + * @memberOf IRange + */ + toString(): string; + /** + * Replaces the current selection with this range. Equivalent to rangy.getSelection().setSingleRange(range). + * + * + * @memberOf IRange + */ + select(): void; + + /** + * Returns the `Document` element containing the range + * + * @returns {Document} + * + * @memberOf IRange + */ + getDocument(): Document; + + /** + * Detaches the range so it's no longer tracked by Rangy using DOM manipulation + * + * + * @memberOf IRange + */ + detach(): void; + + /** + * Gets formatted text under a range. This is an improvement over toString() which contains unnecessary whitespac + * + * @returns {string} + * + * @memberOf IRange + */ + text(): string; +} + +export interface IGridDataSet { + dataRows: IObservableCollection; + columnDefinitions: IColumnDefinition[]; + resized: any; // EventEmitter; + totalRows: number; + batchId: number; + resultId: number; + maxHeight: number | string; + minHeight: number | string; +} + +export enum SaveFormat { + CSV = 'csv', + JSON = 'json', + EXCEL = 'excel', + XML = 'xml' +} + +export interface IGridInfo { + batchIndex: number; + resultSetNumber: number; + selection: ISlickRange[]; + gridIndex: number; + rowIndex?: number; +} +export interface ISaveRequest { + format: SaveFormat; + batchIndex: number; + resultSetNumber: number; + selection: ISlickRange[]; +} \ No newline at end of file diff --git a/src/sql/parts/grid/directives/mousedown.directive.ts b/src/sql/parts/grid/directives/mousedown.directive.ts new file mode 100644 index 0000000000..39b764666a --- /dev/null +++ b/src/sql/parts/grid/directives/mousedown.directive.ts @@ -0,0 +1,26 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +import { ElementRef, Directive, Inject, Output, EventEmitter, forwardRef } from '@angular/core'; + +@Directive({ + selector: '[mousedown]' +}) +export class MouseDownDirective { + @Output('mousedown') onMouseDown = new EventEmitter(); + + constructor(@Inject(forwardRef(() => ElementRef)) private _el: ElementRef) { + const self = this; + setTimeout(() => { + let $gridCanvas = $(this._el.nativeElement).find('.grid-canvas'); + $gridCanvas.on('mousedown', () => { + self.onMouseDown.emit(); + }); + let jQueryCast: any = $; + let mouseDownFuncs: any[] = jQueryCast._data($gridCanvas[0], 'events')['mousedown']; + // reverse the event array so that our event fires first. + mouseDownFuncs.reverse(); + }); + } +} diff --git a/src/sql/parts/grid/directives/scroll.directive.ts b/src/sql/parts/grid/directives/scroll.directive.ts new file mode 100644 index 0000000000..5f512eeb5a --- /dev/null +++ b/src/sql/parts/grid/directives/scroll.directive.ts @@ -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. + * ------------------------------------------------------------------------------------------ */ +import { ElementRef, Directive, Input, Output, EventEmitter, forwardRef, + Inject } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; + +@Directive({ + selector: '[onScroll]' +}) +export class ScrollDirective { + @Input() scrollEnabled: boolean = true; + @Output('onScroll') onScroll = new EventEmitter(); + + constructor(@Inject(forwardRef(() => ElementRef)) private _el: ElementRef) { + const self = this; + Observable.fromEvent(this._el.nativeElement, 'scroll').subscribe((event) => { + if (self.scrollEnabled) { + self.onScroll.emit(self._el.nativeElement.scrollTop); + } + }); + } +} diff --git a/src/sql/parts/grid/load/css/qp.css b/src/sql/parts/grid/load/css/qp.css new file mode 100644 index 0000000000..e91aa988c3 --- /dev/null +++ b/src/sql/parts/grid/load/css/qp.css @@ -0,0 +1,220 @@ +div.qp-node { + background-color: #FFFFCC; + margin: 2px; + padding: 2px; + border: 1px solid black; +} +div.qp-statement-header { + margin: 2px; + padding: 2px; + font-size: 12px; + line-height: normal; + } + +div.qp-node, +div.qp-tt { + font-size: 11px; + line-height: normal; +} + +.qp-node>div { + font-family: Monospace; + text-align: center; +} + +div[class|='qp-icon'] { + height: 32px; + width: 32px; + margin-left: auto; + margin-right: auto; + background-repeat: no-repeat; +} + +.qp-tt { + top: 4em; + left: 2em; + border: 1px solid black; + background-color: #FFFFEE; + padding: 2px; + width: 30em; +} + +.qp-tt div, +.qp-tt table { + font-family: Sans-Serif; + text-align: left; +} + +.qp-tt table { + border-width: 0px; + border-spacing: 0px; + margin-top: 10px; + margin-bottom: 10px; + width: 100%; +} + +.qp-tt td, +.qp-tt th { + font-size: 11px; + border-bottom: solid 1px Black; + padding: 1px; +} + +.qp-tt td { + text-align: right; + padding-left: 10px; +} + +.qp-tt th { + text-align: left; +} + +.qp-bold, +.qp-tt-header { + font-weight: bold; +} + +.qp-tt-header { + text-align: center; +} + +/* Icons */ +.qp-icon-Catchall{background: url('qp_icons.png') -96px -0px } +.qp-icon-ArithmeticExpression{background: url('qp_icons.png') -0px -0px } +.qp-icon-Assert{background: url('qp_icons.png') -32px -0px } +.qp-icon-Assign{background: url('qp_icons.png') -64px -0px } +.qp-icon-Bitmap{background: url('qp_icons.png') -96px -0px } +.qp-icon-BookmarkLookup{background: url('qp_icons.png') -128px -0px } +.qp-icon-ClusteredIndexDelete{background: url('qp_icons.png') -160px -0px } +.qp-icon-ClusteredIndexInsert{background: url('qp_icons.png') -192px -0px } +.qp-icon-ClusteredIndexScan{background: url('qp_icons.png') -224px -0px } +.qp-icon-ClusteredIndexSeek{background: url('qp_icons.png') -256px -0px } +.qp-icon-KeyLookup{background: url('qp_icons.png') -256px -0px } +.qp-icon-ClusteredIndexUpdate{background: url('qp_icons.png') -288px -0px } +.qp-icon-Collapse{background: url('qp_icons.png') -0px -32px } +.qp-icon-ComputeScalar{background: url('qp_icons.png') -32px -32px } +.qp-icon-Concatenation{background: url('qp_icons.png') -64px -32px } +.qp-icon-ConstantScan{background: url('qp_icons.png') -96px -32px } +.qp-icon-Convert{background: url('qp_icons.png') -128px -32px } +.qp-icon-CursorCatchall{background: url('qp_icons.png') -96px -0px } +.qp-icon-Declare{background: url('qp_icons.png') -160px -32px } +.qp-icon-Delete{background: url('qp_icons.png') -288px -160px } +.qp-icon-DistributeStreams{background: url('qp_icons.png') -224px -32px } +.qp-icon-Dynamic{background: url('qp_icons.png') -256px -32px } +.qp-icon-EagerSpool{background: url('qp_icons.png') -192px -160px } +.qp-icon-FetchQuery{background: url('qp_icons.png') -288px -32px } +.qp-icon-Filter{background: url('qp_icons.png') -0px -64px } +.qp-icon-GatherStreams{background: url('qp_icons.png') -32px -64px } +.qp-icon-HashMatch{background: url('qp_icons.png') -64px -64px } +.qp-icon-HashMatchRoot{background: url('qp_icons.png') -64px -64px } +.qp-icon-HashMatchTeam{background: url('qp_icons.png') -64px -64px } +.qp-icon-If{background: url('qp_icons.png') -96px -64px } +.qp-icon-Insert{background: url('qp_icons.png') -0px -192px } +.qp-icon-InsertedScan{background: url('qp_icons.png') -128px -64px } +.qp-icon-Intrinsic{background: url('qp_icons.png') -160px -64px } +.qp-icon-IteratorCatchall{background: url('qp_icons.png') -96px -0px } +.qp-icon-Keyset{background: url('qp_icons.png') -192px -64px } +.qp-icon-LanguageElementCatchall{background: url('qp_icons.png') -96px -0px } +.qp-icon-LazySpool{background: url('qp_icons.png') -192px -160px } +.qp-icon-LogRowScan{background: url('qp_icons.png') -224px -64px } +.qp-icon-MergeInterval{background: url('qp_icons.png') -256px -64px } +.qp-icon-MergeJoin{background: url('qp_icons.png') -288px -64px } +.qp-icon-NestedLoops{background: url('qp_icons.png') -0px -96px } +.qp-icon-NonclusteredIndexDelete{background: url('qp_icons.png') -32px -96px } +.qp-icon-NonclusteredIndexInsert{background: url('qp_icons.png') -64px -96px } +.qp-icon-IndexScan{background: url('qp_icons.png') -96px -96px } +.qp-icon-IndexSeek{background: url('qp_icons.png') -128px -96px } +.qp-icon-NonclusteredIndexSpool{background: url('qp_icons.png') -160px -96px } +.qp-icon-NonclusteredIndexUpdate{background: url('qp_icons.png') -192px -96px } +.qp-icon-OnlineIndexInsert{background: url('qp_icons.png') -224px -96px } +.qp-icon-ParameterTableScan{background: url('qp_icons.png') -256px -96px } +.qp-icon-PopulationQuery{background: url('qp_icons.png') -288px -96px } +.qp-icon-RdiLookup{background: url('qp_icons.png') -0px -128px } +.qp-icon-RefreshQuery{background: url('qp_icons.png') -32px -128px } +.qp-icon-RemoteDelete{background: url('qp_icons.png') -64px -128px } +.qp-icon-RemoteInsert{background: url('qp_icons.png') -96px -128px } +.qp-icon-RemoteQuery{background: url('qp_icons.png') -128px -128px } +.qp-icon-RemoteScan{background: url('qp_icons.png') -160px -128px } +.qp-icon-RemoteUpdate{background: url('qp_icons.png') -192px -128px } +.qp-icon-RepartitionStreams{background: url('qp_icons.png') -224px -128px } +.qp-icon-Result{background: url('qp_icons.png') -256px -128px } +.qp-icon-RowCountSpool{background: url('qp_icons.png') -288px -128px } +.qp-icon-Segment{background: url('qp_icons.png') -0px -160px } +.qp-icon-Sequence{background: url('qp_icons.png') -32px -160px } +.qp-icon-Sequenceproject{background: url('qp_icons.png') -64px -160px } +.qp-icon-Snapshot{background: url('qp_icons.png') -96px -160px } +.qp-icon-Sort{background: url('qp_icons.png') -128px -160px } +.qp-icon-Split{background: url('qp_icons.png') -160px -160px } +.qp-icon-Spool{background: url('qp_icons.png') -192px -160px } +.qp-icon-Statement{background: url('qp_icons.png') -256px -128px } +.qp-icon-StreamAggregate{background: url('qp_icons.png') -224px -160px } +.qp-icon-Switch{background: url('qp_icons.png') -256px -160px } +.qp-icon-TableDelete{background: url('qp_icons.png') -288px -160px } +.qp-icon-TableInsert{background: url('qp_icons.png') -0px -192px } +.qp-icon-TableScan{background: url('qp_icons.png') -32px -192px } +.qp-icon-TableSpool{background: url('qp_icons.png') -64px -192px } +.qp-icon-TableUpdate{background: url('qp_icons.png') -96px -192px } +.qp-icon-TableValuedFunction{background: url('qp_icons.png') -128px -192px } +.qp-icon-Top{background: url('qp_icons.png') -160px -192px } +.qp-icon-Udx{background: url('qp_icons.png') -192px -192px } +.qp-icon-Update{background: url('qp_icons.png') -96px -192px } +.qp-icon-While{background: url('qp_icons.png') -224px -192px } +.qp-icon-PopulateQuery{background: url('qp_icons.png') -96px -0px } +.qp-icon-StmtCursor{background: url('qp_icons.png') -96px -0px } +.qp-icon-SequenceProject{background: url('qp_icons.png') -96px -0px } +.qp-icon-FastForward{background: url('qp_icons.png') -96px -0px } +.qp-icon-SnapShot{background: url('qp_icons.png') -96px -0px } + +/* Layout - can't touch this */ +.qp-tt { + position: absolute; + z-index: 1; + white-space: normal; + -webkit-transition-delay: 0.5s; + transition-delay: 0.5s; +} + +div.qp-node .qp-tt, +.qp-noCssTooltip div.qp-node:hover .qp-tt { + visibility: hidden; +} + +div.qp-node:hover .qp-tt { + visibility: visible; +} + +.qp-tt table { + white-space: nowrap; +} + +.qp-node { + position: relative; + white-space: nowrap; +} + +.qp-tr { + display: table; +} + +.qp-tr>div { + display: table-cell; + padding-left: 15px; +} + +.qp-root { + display: table; + position: relative; + background-color: #fff; + color: #000; +} + + .qp-root svg { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 0; + background: transparent; + pointer-events: none; + } \ No newline at end of file diff --git a/src/sql/parts/grid/load/css/qp_icons.png b/src/sql/parts/grid/load/css/qp_icons.png new file mode 100644 index 0000000000000000000000000000000000000000..fa4aaab8542d5dde35e9c5a680a4a8de478116a8 GIT binary patch literal 63962 zcmZ_0WmH_@vMt&)+PDS}PH+zzJh%l29^4&*JHa8i1}8{xcXtc!?(Pna-zIyX`_BFG zejv^0F<7fsRn1v7=hvZM@ zplp=r0P+IHR9H?J0H};YdeVo5yaw4xYB&G@sNHX0Q2n+AMvy<^JBq10D%+Yky6As5 z2H5GF+c+}W7(0@3FtISPvWdc29s>aP(o&y=zq;xid%=5tRk?jV_L)mK`ovo6q?6+ANyL1lwhirF;6TT{ivkC+1+odw;pRs_k%9GA}n< zc;p0E-t0(p#5x_;?h-1^ZPpc`Xv(o{MA#?jrMs_e2R+pfn7-J9r$W$|c^P21&`QI! zu>DjtwD`T);S3M-eK15snh?LKvHW?zs^mSjOhH*tD1ny}j@*5u#7T$~)c1bFwU3>^ zUSTaz=H<%XX2DbCG=}%4yy53ah~AzZ(z{GDGXXa45c?WCuAb!RVtiiMzX6T34_MqU{!}BxZp(#E+ zBxH-!le4G{d%I7Wdx}9@mr+G4Ir`q+Tmue*_u)NTLdQI$fApc#bXkz@Ut2U1O8V1b zZ`BYYzm>tMMSCKL!Qe|ExrnAoAq|4R|J>`OA25PDnm(fD+HM#7m44CD6Y1icPBJiY z`x}-QakYDWT}j2_hT!l(+4EmWMnntIr!k;ZC)j6B60uh~38L)GEZZK8V=PP~>H%)e2Et9*6c5CW1T&a@ ztZb~T>B2cVn*pDnc11-9cBWc-_0j6b{Rw*fWyH%}Q3aFBI#~QR9Mg!`N<9~ODpP2FMv+E=rSf*L^or*ZJqMUU!@?d>AAFwW3roz|DoTtaD z*`a~671%`gC;y1Ktn9o%+5z|P(vgAJsO-u1{(x7Rz`Y|^J=CS>2n^wSJ0vESZhlIV z91({Nd}sB9lDV%6nb)NlS-c3PMg`H6i#MruciJvnosXyeqG+Gg^RpBlOpyB~yXjaF z?6h7$o(nl724V6PP3r-4?S~8PqlbKf$b!$m6>4GdsHWD?Olc%e?2TEpHqmle^T zH69p#nLn3TId#v5dg#;X)g$X56LE|bg2Wh4%X82exO}@9M_Si*8-SlI`dH9@KahAa zaPEy;=&)1-siXxPb6c>UmfX%>Wcz7eKlX$%t_SVrRoFN}w9}!Boaa@$`*XR+6RhEq zo}>pBT)S?&-rL2d>?D#2A9j4QQxct|waiV2?SsoJs^WC-E+-cX%?tjBR9)qZBMiRd zf9=Xa6A0)h@y`8EX4T>A!KVZ2_lUbsW2g3~Edd5HV|p$VQYy15{NiIw=~#MroO$tC zIp}(XWI{R}AxsmXF9>JLlrGqU4qT(sQo!TZM?^SrVik=HemsqJ09i-6SQ!Q>Jfpi; z_V0Y7YhLH}Cxo^`F5rRyqwf{F@6^=SLEamy`Wi0J22b?;OPLVe2Wvht$KbGh`b+i> z-kUr(!{G~uYjEYO*pbi5R@^VnGd|w}F#mjFMZJ#DT6m|ryLvSqjO#gAg^1f(Oj_of z*r7RD4T@BeQR^S~kv)}smY-M1_^cg9?30|mJ$D8xSYy=jL!uc>jJuVW|X&YUD%ZJf$LcdMi7segE&w{If zH3?nuyNd6QXtcE=QR7P2sPs6S^va(lfy;`npz;}CfQIHv_L+Be%tLA%w8vJ*w3>Zm z#ZNkA)!VNR4J2(TrJ1E|azr&A87N3wPN5%DENduI;Atw_;P&_$mB z!(AjOO~CIv_5KN$Xr+aC8oxEv3y^xaLXm(k@cOJ{Wkcb%H-cmcnzLr`G-v|dx z88=B!@*4RsUv=cJ8>!5SLu&9O1mI@uym)=@y1gNBF&(F{k({4Vrab<^i#;f(xP!8t z$qQ~>uh;E3$vDwTV~v?|zHv`s26Mg13sw~dFWCo{9|h=m-qPg;v_A;bAnztBLY5h-KG&L+6P7pB`z@Vwh9{Dq|s|Bj4;jguKRCo!I-5%%hK=!_JkpU%f^H#Nm_k- zDE7W;s5!;1x5ZA4g2pM3i#M+>e#1fq?PyOToJ)~){9aLfc-Z3Rzm~A_I+pjWy}{(Q zW)RJH>Q&uuX~SECPgu`Kutxm!1>GL}DPQCf;@V*)$`E9ESs2fn|xM!br93Qr`Pecl`>&^J&$t^=6WMW0u2nY2- zYzpThwm_(`ah+8*e+y2BWS=^wo|*g#Mi%{4DgiOz9i%y#seIrB-k_q7eApBBnTDP99Ew#ZZ3*dBvnBMIhlaWli)m(}-9u+&jT8s&^&x z{}+j78Q`G4H>EHts|+15bQUszMpnPh;k0f{;X_@D8=F`%x1N*hcwgkbL05g##3B@w z_fm5^dGU45UX0r&dDAU%?%$>ohvZ5kV_(lF>xI*AWF5pNz~x^KATNV*@*tS`cBIG} zZ=Hk*g(qUczHj2?A>TjF)WVEC-~6QMT_0dWo+*ccCobXxme3P=D5P5rV=kj zhc~qN=KY?|=bR*hjf5oK!v7Q;BNTzMo_}tYg0@H^I@u7(!;JRx_~6elqhg8uMN4 zqg~kd20b&E!(#@RP+0p=$FurW2CM1pMjX-Zim!|Ih?zf@A)ce4+d-%uFx+NYh7fJ7 zR<5H$brdu@o6+{KY2!o1R&ZO_elE<~WMg6fBnH86afP)=gz;xXEca^yza$;205x4V zoCsa(Z~q&*S+@3hw$qxzPoLqo&JTtL-ia8!m0ea06mJSJ4)B~^U5a35eM@z{R*a=r zKKA9}rMPJMW_pd4L4j|3tz4j7xN;7qtY@)m4&BN zYOwb<7q`O;gUIwR+WP+en-DNMD;<9<#YAvMCJrM;g$S zXe^89pYp_C#2JX?a^^)P5g7spOZZUUYo&rZ=GO!%9En!7?;-HG5dx@I(COGv#*Oj0 z4I^hvY|Lk#(cnKD#v$U}NCTvTpdPFQbFuW|$vZwS;Igs^f9N>PTsI5;Q$z`c`mXgc z(h#q)beVP;#Hbevd^1zDq5FzsbEL#m6p zQg6m~zGvtg*)oM0u!^9m@02o6!-~oNySR_iuH+=?o8ESi7gDrv#P;mdszJQvw3Ete z1sdmu2OGBkpyBnvfsHM#Jm%4pOrP~Y#$(t?umFu)S|;K^mHllB>3o#*b=i=ukL12p zL{jPdT1R8g6Ptjg1iSK86$|IvB1N?;`QLDk{BPaj-qpT*wvRYe)6CPdt#GKNQ|?hO{$Cj+_5k-U?HZ}<7nQJ3aEMT-E9F@0cf@v2SM^_|fJ96WihNOq z*O7?%=jbJ6K>!|ZTDSZAqFiijoWe;ktcwZO^TQNkq!q)`O|k6&!r zj<+;MdkbHLzSa>b!* z%xXL@ds6pb!M+!p|;PSF`XV;`msB#+nfG`mi$rH&s zfg{hViN0?Dr~ah?MRhC5qc0r4Xj<-gF?xRVG~~-VTkm9FowkdLnAM}=w@^XrMGje% zQ$IA*XVj~l_O7y)1xcD)<#jeEtDK0_zOr##FG83Gs`8I5TY+y}22RL)A$)%M2hxAaUtugAq3d`By*YNfp!s0??Dp?#yr4K8VaKt#n?4$XD-cX8-dF(@&Qg2mi@Na^PflAD5_d_H*#EPwb z;V>(plltbQB1pZ15!$Ivsmq@{0&0o@KH)AiNb!K z*Y4kFtnQ-iLriS`1$DVD==M;fS%*EO>moylxtJ_>^kx151k0psI?rt}*PW7AcfqXP z1XJJPa*;5062f@iRg}xT{_ftxQQK7MUl8T?Dw*e(ZdEVPj7{mYm(u2ds~{0?UiEwd zR@y>wpPal3Z8USLvg=~{|E8h`=cjk2NwM2}W;qN<2eh5f@n!U|T8?&`84G;L<8 zVVShsa?1{N8o%S)N~H(%IWL+ImCajH4nP7qPX7s{gCTVuDOu-tw)>S~Irh)rltU?3 z*nEjjQ-7)7=fjc-pu^t@*A`?8`b*{Gmm_HBeW}Dnn~a1ZWxa$a=exe`#Sufo3*R=? zH&xKElrH(v(_ae3{Z8w%j8mCAATteBA5TXcG`v=fYJ2A_rE{s1KlVppO(qm4E|7>M z#X`jCq#wLOUF634vHNd8bB6?kQLi*H4*bIQO=A_F?k`>UJ$>vIu50OKK$F4(J)!gn=0?{ zUg!u!-9pvREj;OTBva$xDHQi8BNxdRVt=JFbT_G{GZ;x&CV%o2+R;aeziWbB8Rc)e`x^m`%l{!1q@ABMsU`gwC>N)`S|DAX0%gG z1n#sVoI(G~+l-UGHKDKd5mwgM(W;x$?M+n^qMB0LU~iuyP+Yd0dW?Bj*|EZidc0AW z=Yn?|yd1*Famk-MY zU{UaGpU>gZE(kT#`qYKlra^!8X=XsiV?$=cQXOJADk-xi)No9LibM_W4>Iy3hXF^* z`wj&(P*bIe3ARgKYrGF`8!}WGe8>%8|Ap;=9Oq0@3fI4$={%cAmFz1z!aHHZ=HgI&ZMDk0=DIw2lAhBP10X z5?Y%L2{iH1|K@+b%!ch(wL4`$kMW-c98FiRfC&-Q>S9X&FH=){;HkP;9>>V|J{&P` z)TP!&?WmbWMMDQ2Z|L(odUJQO@0Pvehi&rt0@!xKM%|-^WKk!<3Do`JZGH7NG+^!- zdzF$(AC|K5Yb&tH@ZRSMQE-Z`d4@-?gvy`C9RK`PpT0dOwXi(xmNJ$81#3euocZua zWAhGf69Ge5JN|DC6czU}7c~PTUrJhN{~*-zBzG#PML~E;AWBa=J@+S9=2rnpU>gDtfRdYo|!T@TAasxSkP zB}_i?Nir0^!I^ChPTdg%x)Z!%K>b5QNZL(~7@q#2SRab6vJyE*15Vr}>Fx6}*;7&3 z<@Q^}F~b#I3ZJb&nEGD^*KqD{jwf;~o-*X*_DQ#|R|ga>y!n6pPhtP*7_FeBn<*MC zVSWKO|N5|yFeZ<+8QB^5mAGp7 z+9OxC*JI4q0td1&=@bptEM1G%?>=e}7;ZVHfwm%p;dUGm_OuZ8{i!^rpMt&uJ_*EY z&nduE%B{5%45!pPU$y5@up_A zN3O-hNJzw5-EX>F*1Uo*bNaEt(|-#Tb67^FrmVKYNIW*B1tqMXSnclaH5u6l@hTb; z+dW6Yl)xe^Sr zF<`dmgVUK@X)Av~=UX%Qk?cwhd@9K1aM3NoR7dgF{!x}CVMVCk~0 znD5Vk29rLL{_xg*3zXaZ%m;EI%%w&tQTyGcyJ5YRjIuJRiqyI4{rZUj(51g6`i34E zjc9`vI>}pWad&B+^s>QfScuLN_MWcR=4!Qp+?MS1!4&R6vW9v)E8Y<&jDD%_ka==) zQ3Pkx{TTVS|1gDNrI3kN%jM7mU!3ed$5;Tf=jZy)fD5u%VkV3G>X&qt+&U6*E=fD> z1Hp|{8jUq5l((2BZcEHRvbcL8RExffEbME^?Xyi?2%9ED5vA1u%g+PUTabevi&&Cx=|j*ohvb~M_M{DN$IMi;M7oybp2ezEYVlCnqI zH*<8c{}+5_nt5=<{Mek|ZJYJ<$KGW6KbefCfzZ&w7etm8bvS^j=RELKrz*4P{~6;V9P~ z%G1sG+>mrg@coQ~nL3zH7(A)`iy7zF1RsGZZUJmI=~(MxQ34I)S6Hx2TA1IQPMpX@ z-?jWi+cTr&`lvH>-6#1@#4ee)ZX&48z1||wh)W4|F;9%`XY}PBsi1%{tJ~kN$KjzW zM)ejW?!<(SW=?PKJkD`xFY8)J(iJgeF!{q!%>;RB*-wMlxL)V3V=ZZ#k;dqf9&TYxWxa_f8z=X=6zzEUaKOBS8|?F~8{? zwytYuqhBc76LUo5StH+-odyIryM1axVIknZj$ve4*MvFV4FDn8Q~xG z+TUf8xoa%XswHY+aGG&SDAR85-(}=Oe^-f5wh<#QOt@GTwffwIG-&f+a}riw_OOOg z)sjPR#g(gh|8pH**g^QGJK%XzaBI3TdOj^i3lb|TZ4&|W8%khO;Z*AD7^wWg`}O$_ ze7j_Oqoqsa&sk$k()u_?{^@MarSoPi(RSd~x=~8#yFrkuZkA79BN-P&##5rC;l?YT zEA1}&Xo+qHKV7cbtXduOePwY5L9?Bp^GK~Q)=7)dqpiS@Z|mTQE z8Xr*$HlK_9)qCZiyS}3wenMuY&=Kw$9`mWYd1$XFwUs0ugouQPz|PWc5xcALq6RTC z^@Q3P$QJ7#_6oXqVkYj00VU?^ecH8Kscui!Ox0{beN&=ylCFS1yVu>F!%rwg^I3vHk)x={2d>*dRQn!ARC;qi(A>#g^a zZNlQ8$hwb9Gk9CQA6uiKJlr3gIc~je?9sy2_Dn5-^*A0r_DA+|UFn8rLZxW`E{U#< zLkDgC=h(1p%9rTOD2iTKZcWvIt)1Q?1_lNp9d9VapJ5yA2C#@jk)-ucc&H?Nfh}A4 zF}n7^MlmV_<$?tU-N?yU%=n~tsuyvoN%@;B8Sk6KCq=5Fmp9$c5!v20wvd~lekynf z7zwUqXoD$7MuQuSHsj$%?oQ+r(3@0i+&T}7jVRIx`T<{n~Owjo51VqR)b-9+_W z&C_pr#xTCzm=6N`gIqZ$0jVq>N^bYkjw8GTHg~PGs#ofw=qtExt&G88vxmk_?zO9d z0q-;b0mN|RmucM<$=E7>`cMf*tJ&s;;ok}2?)3d}8mtr)3Q*fQ^qqT%1BlNC%Fp11 zhJ)Yj)3~Df`^%^l&R7=YD?nyWh*0_QXV_TZLnJfnaW5A_#ik@JL8Np~Ype6-E?RGy z6d;m>F$u~CW(P;2(o=;rGL09h#f~d1vjIQ!m6Pl;iaI`nJ7SVzCuQVs0=Ls9UItP6 z*IorhslqTrDPOHRYX{zEPL{PXw3Z?M`p;KtXI#kKC_iIPPOmS_JUB1OQ%3 zEtVu{Y6&`T%U6c)hoOMvjVEsU)9a53KHDF6WO2YGA9Z)MzS83R_~ZrryU?9^dKCA$ zgE}S@3oWN`6|P%K7>K5Q682c+eE!W+iFs;9<%Wq80yw^)md3>OJSSw*Vn%4Vwrr3VBODwIyaptLORyir}lY@?xyd`I` z>PA;%b%SSyK*=+aptneGWg4(N(O50_^S~50O>6gd$DBS} z-Qr$^toyX6#ogd%w4Tp`j{)uy!o{<0_b?D^m-yY}SQ*R_dz^PfWh#l^UeGO5d1h;+ zF2O_Fl(ja&gUv{PMgSxmk?}F+QBUdR=2I^wocQzf(X*CD+3|=ypmMgDioSn6c5^-E zRTGnFJ^<}uKvb1*xZ)|=Pv@wen`W1XoLL)Uz7I0_Gkzv=69uwUqJRnH_=&(P=+Lk#Z$(>@jW+Nv!KC`evWI(_wd$^ z4)hy5?aZq0yYgR>!NY>knT$!_-4ctiNSNd#?rs(kcRQ4Sx>YDYVtI*lwPYk8*1p zbw0k+s#`m#JKbw9-!)WtTk6b8OL|izrrNEK0~nUAx-4^w+w|rzZ4opT8t}cmQu(yG z)B*x1eVF=sw1uuBSI!5sG{COwvYyMf1k}gPN@9DBd!NM5cq0}-xY(d7Te*${PQ$|u zOUinr!4vuh-}&Wg6(Rm5r>T@sRs__v>1fBl1mDzrg}_g!)T@Cq-Q;B;2XSa07+3J9akjzh7Mv zlS7t|ykJxu1vU6wsM$i(=2q`SJ!C}4HH?VFb_aQ1_TKrTud{nsK~iUhjO{c1iA@3!y#plw4tm?;H` zgiM1ZG)}n=JveF@ces1wQrd{*MtGFfmNEgg`=RHKh3Qz()wT(gIo2??;aw!tzWW;Ny^^p3pO+ppnzp$gedP;b8?o{eWcv3-<;6J7$>68?P?0WNf1fu1M zJIslbxlO1#L!#(Aq!d{<*_+rlN^ z^#i2$$8GSvTg7{_e}+BR=nK>pP%P5SOtB?CKqV?)azzJ?QXkUlAT=fH>JP z8*}iIKC$fVYa+z2VvCIG;qRNhDP!2$pM-b2NeCnP>9mdH^|Ttx=Y@{w52pw=Gc#vB z{xQ95Ai?zD*4mf)F`^)<-1~{=C%4TqDTg|*G4tz$rN#j`s<({ z27oBQq9IIm$>IUSa0(~0Y=W4&23iX-B!FOE8sM^LHZ+?&bH{8NTBY&X;{Z+SZ-j|~ z7i3J1#84c!yTU^I&I9v(?&?bnF%Lr@tweEO2nz#^e-AG_j@p_rIj%glI==e0w)4|m zz4m#{1en%7=m)@`Bkec=KBH{)ENlC%P?t!VhiC`t8uoT3O*nraks2rMSa@VM^d=}- zj{~~Oh)d$#@QApky`=oUyBaB|;gZ(UtqwFQ4+OkEZHc!Cuq&Ei^@2T(p#-!t%B5$u zhNXK>_&KNlmb;Hzz^c`~S=yIKYB&#tycFRs=w^9GLCn&Dv` zd|R9v_o27PzE8cjpariItaKwUoYX*`#gx#q^C#7ilyk~N>n1tWEi4UzYh?NpCG#y5%#kR0uawU)21U7EE1NaBIny*gh! z$kUOAc=Ac_A?9E{@fq~)!;?l#)|?JmCv?4mjJK9-h$r63hqaAM?<&Y{Pvepz6Ga8{ zCsnawqbzeM?4a6us_P(fd+v}r<=e()W-+3PYMxW@qN*uyR}&%n^0Dk_!d;l!-G(n; zdiLheA_PRM#w@t4{QZ7L6Q{hTBue{#AT)9l_wYp>V$S$SxyuV%1TAnfh%v)VgM$iU zomva~AjUz#Gd1VjWwLMus`oTt(CT*R=1u@Vx=f%-XF-_h+j zdoIJrhM)o6^E%EIW4HpWwbcXSk)C?)QmCh?v(CT6k2ubGw~E_)-GvmtvB*x4hY}9u|_M_Q^P>Jvq@IZAKgSw?~LwzcOuS1uOfP+%LwUV0Chy=Z;u0)&X z-Ygg1PI^hW-`*fUtInWg{wLUpb%_OXo-(aGZ9R4z2$tmKO~GX3TQG?|Nf7hie_{

-1|{)A92F-QpRKwwn(;D8O$Ss3n+P)jiUKPgYL!ljR0ZeKVF#Xcb9g zCdKVPavd+XrHJ^PWfLH3N8ECO?m;_DgB|7{W>v@n;BetIj6~G-ogsPw^5L)w)O7dS z0j$i7M{SsqsXUUrYMx0>Lge*vciXl8Eo~kFj6A})m5d(!Q1eGvjs!6|Bfs{Lz-4 z;TmoYRt>x#OllyYSDdTdfsB?h=D1(hV0fGSBl!?NVeY_KEZFr3sr6S% z$gt@+^Ae4x;H=gIHF_a62KID#klWd6C0knU_~bw?XRbAh zYiF+SG6I*9`OjiE^iGmPG3XI(O!+2xgSManIes8=XViGGsWjtxA=vA{->a}Xyz^Ih z=?emeKR`-7txmH`Ck+BzkD8#0=UhpSB&aD@u=sE!U)X5z)jtpBoSZg9VUHr_JH^Jw zPd;4#Idq16hKIio5mDdfmIXfuErV~%MHuzGa&TG2wuUm7Nq;gft=D!^3Is5Qgu^uK zR)~AUU5F*tcg|uFW8hu+nly_BPbdtqAI(d%U(jC09ork8`a81=t&$q)`4s*G`6V|_ zCkxq7I>yJ#4<@_R_pGt8)=TdjiIc}TzfhW`plhAn79Ui1oXI}%t5UW5MkJU20*yar zp?XU(otO+Ow1ckLef)c2U(5fBVPcZz=TQo)bzB8{B9|=D2B0Fe> znr-FGOTMZ3&G=Zs>^1*j%+Qxx`atm+!nFzF0}jZfR3W^#un=Otd5i1)K8vgzcQ6Qo zum+Z}?g5Dg+mVJv5QK74PUk)F0a+*$%#)h15E!u$XTwR4gF*27eFvAm=7PS0$4FSz}!# z(84#&JwF=if4)DZ)wN zvW<+PkT3wBZO5+`JBSO4sedrejhEpx_Pyc3pf%yQHnUtvYO{J~|HBV7suNf5tHAA7Z8zxs&Y;x- z7=kZaA|Ej|4I_gNc z4%LkQ6Z>=owZ3?V!VGv^-5PoM^Cz#+Rr96;keDUtHpwr}MR!GUZ|!}V*|08%o0RB5Q9cuQBjWdwOlS%DJc*PtH%B6h+*zT8 zjxn@Y;D(y()j2GLAwM4`zrR1!+YKr)<16b836 zTDSVT4;ASznBjx-hf(OP2;=A0AP;-18_JsKVS0oi?la6cVFFArG}P_K%JRSfcgkJ* z#tsO63j5~EXF7)+TSWlscUn!IzEu5(C&BI{r9d$yYCy9$S<@2Rub;D&{IoWs7K&Z{ zvt?WS_z>>#KK?UpOXVv)YOq(|k)4 zT`+6xnT25Gv8*G;@u7TwVJ@&(6OP&ma&n{s?Qt@ABaZ5Sl-U$7tuA3AnbubWL539{ zsC1z8xswoUn}!XERn9;CQW&pF^UQ_oHK} z=j}!6lAwVt9Of*e&fbJPnQe-VrBZaf;nwL_=JaXzyTt7Cd$C6TlpXAw;ce?Dp+B`( zRk)W5YpWX1ZZ<3B_m?dP?k1*WnE0?D?V{6KS_%nj1MzCaDkUE26yBkWPKbn ztJ4X*h7W%ytK>J3luqOlh&yksS>S|;Wu+@+zTM<;{Uj&Fhe=Z+_*K!Ax`;t3xIsAl zQ&w@uyfrAfrP;H@jvjSib(_(!?1(ia|N4my{<_obcMA@_PPD=)fLH z$IK}SUA)(F7uo&HF6XcJP}Du!KA|{+XxuF*f8y3glvV?ny?i3%kzPsMN2}XVb~OW; z#dpLowgL-hI#<1X;e_*yjsBBuy`)0r=ajBul%0I!_*lR?gAiGd#$$*yhE$&mVKUI8 z*QYwgEN(lTD*6AhCz|1}&1P1EhY}frmO}LHNCdD?8DJ?SkZb*Z=l@Hxv&$SiCs>cD zu5>{kMY~QiFemPNPYkzm4dVC`dSJY>$8!mpd36XQR2}-|Rrp_tIoLUQU&`hLU3fBn z29)4zHkCnlCrb4|fKxPJt17tgyLA`VysiL@7Tuy2#zxC<>hK;|(o?*ye=iValYPK; zEO6`?layJ{%6+{)KM((vk>zvSKvU2iy*<6>}S=u#BxC7E@^=hF2wFTxN`-{Tj+ zmh7FOya$kqmk@O8roErp7T+gHS`NQwMXAiM$Zx{XjL~JZ4QP9ZU&o*tLt^`LgAW*d z${V%O%&a$G=75LkCI0t{ca#=+*;_g$iyv50P|)%Fm`Ftvf&qskkQu|L^;r0&Q-O^! zajwWk+!w->^-?eQgxbv}L~g1k!VEur@!n+ktB}!KiS;2teg!}~G>w6ios;2peLh>> z!#ts5tgEOR-ZhE2ok%jH>*FK-qr77;H64Y|ue@1scur0wc)6$?a`@nUGEb&qV9uUL zNLcs>FSzScXL|H%|9{Ohn53B_PHXYOu&np{r^+|h6T`?-vFV!{70w1w| zV!dQ*|9*E_e3D~VdrytfNtoqE!7&)cEVlj#qvZ|uzg7M9!pX+xo8c>%X%v1z=9d5W+O-2aEJYRp&>*Hgd;_Zima= zhR`CJYzi;*ipNc7XN^_I^IAv!9c?Nb*~i<7Zmm|#*UA|9^Eq42?r8?}{;@RG@^-<8 zX_A=d3R#8I37sE4Ps8}11n3m2J-qVVSG>$MlF7yMX_yr4k1^JFhLUiEd4Ox%UH;76 zclb_`L;poyi00Cp2v{Q^4pK<^Lr%E`yz_&?jJN86er2^(m2{99 zmNjQ{81k4_ddkjcu`#n+8CWBzoqxV2j+N-u7dp~v{QEMc*|& zIikEG8HstS%EPU#rF{G9I1!>h=lA8phO^QJYNECeT||tx$0w0}eg~Bb!+`R6;A_H^ zrm0gbiEc=T3yi#=9Zt1UR@N$QLA%8GC9!|pt&W|vFIAqgb7*nfwFxU6l>JNC#|Esz z`r&KHt@ZZ4c*eNtJs%8K!PoSirqq@>+_rU02!p^rm z3NukHdf;?^M?RHSi|1+0y~?fWSZ2NZb1Uy1O=TgMj@#IztMvcyHvBta|3s_?wY7$Ad*(+(~cJ zz5zCG>#C9OVz4CLjK>?IlsDre-NhHFEpZhG!Fa7|hC(YVamdPZGAoy-&L?OT2WQ<) zuF-v`z#vd_xMw&UO=~Nvp%n6?9PofaxvU<(R*wa^WW0)Cta)F5sUHsycSVHAcBPaZ zSGd!49$M}6UGuYvFlLRV<`NzzvqV4P_J_V76Y_T5DmFB^6RXM!e<+c%(KwpZj~T8p z^)@Xs^#%#t4Y@qvyL;RuLl$r*Pq4%Mt}4bJ!!H8w3n0~W_PRx1sq>muJXz=h6V}QP z+|DuoAfNVSZ%Q5aiHpkl60solz-E>fAHAC0cTfxt+dit+(~;f@x^ZS zN9DY3?w-eYl0;i}Wy|siDeVBHAX?OPb-Htnsi3I={u+8)=l#*g#{ITRfMIXUZnvJF3jMblptfH(b+s-e?urDU+-}f=}&AZf;&a^u6TGihqP3bB<&#z(r zdFF8eO`E0h$qo>mi`Ww}Bf9|8>=)GVjK;PL*%R@i>nPZ>o`@~Mez%=rDG z-as*KrLQzJLc%&3Rkf5t6wurBN6f{A*>G)$YZFIj^kmj-wHyxx-kbhy$3N^D*gGPg zXqu;I5)6`4Gz~5$^L}x=u?gTq&Qa_Loe#wh)5xKUKkusgLAC_)e2dkYrOdDp8q}14qXYnCHHBqGM%G(mQ zW$jy;wA$d9lBDVycsU#-0%z|gNY_=}x{<6wR{hEU+ByAIoS#_4uBf>!i@yne*)R!= z%-Z*|jADO&uH%o^&u`mW%7`%jU2DoF?aUnvbEXnt3W}7{O3$g(jdSo_q`zp5c5);T zQRXxJzCC3oxV5Mgx~6Z?K%Se@bxB0JhTN@YqS40FH$bjtWch$9D5q>2OBPer{{AJ2 ztdGieJ(v%&8AXW-@kyIUD*ZTr zWe{fqF=T+0tReI_#Ax?xhi5xZ$};M88h)>-L&Zrau_U1}oOQ}y)V8lG6}m=(U?QMn z-RqA2)F$e;>6`5|f=EVWgz9hZ_nYr&yb)*>1LheEB!7FTCr=?)lm%|z9Hgagvk!;R45!&(St6Xk%-a!R{yN0uu390FphJAxv)qt#?M`! zE{YL;=^%Y^`sBhj`e_Luk?{n5`Tibfl7vP7wUv#rc_?6{UZoB3RlnX#vt8|Zq>bn| zCyeC1w>U+^;8!!@NN1KR2sd5HmCs!Y*E{b*XulAyvfQo1c+QUe{W`mK=r7T<;Di&q z5X}COyk;0q%9oNKlQi^fOT))56pUO+^w=V4B<5FT-I$J2VG~VqVrF~o1bf@pMs`}v z`^1ph+oDPax>UuJx0bR=tjKRpEbp30eKSHiqx$@IRhoY{O;}KxF%&*wB0r-B)VB0A zaWEarP~PUU=`wPBt_&`6Xk3a~vEMkBa{Trs%WijDamK_J(X&;L2ez#696&^yJT^%l zN2f8DdMGCuKyml2>=rn1gC0wmxpTs7Bn*`OK}g-(65{eQIike3i?tK{+GEL^C>!JZc-~^ps;*uy~*Kuh_wd5-4?u$4=C(1S4(zIHxfIFHsAAcxQB~h%0e=z#Q?^v4qxU4o! zA;;jP(kflQuu<_8yTmLk>D=P9%Q&Vx7Y6>8FxJmsRtBz`R=J@g8EGmN9|NvM6K0pa zM34WP;R!0ouhFjTy7w}NIFOd33BQZl44^cUy|P~x|LLBqGiUSco~NeZgyXbFTYOE? zlGKTd`RzK=N)Uo_f}N|Ne6TdlNOOLJLHH>Z8RQw$R*U|20)JVxR`J8Jn_&e+FP@Wo zF{NDfK_?e)2{_+i)?1nvFIwg(0u{{3r9s?E+#E4mA+9SFvR)>?FGBQMinbE;3D5iH zagS|mAs^0XBu6iO6Uf>@% z?^8eo(vYa&Dn|AQ3CHaDlcdK*HuTxby)Zs6M21<^RYU-ojn>IUhh;eXz6WAXId20e z5^Fs*^%%%9UZdQ%GkgO7Kc8)ot6F6viY^{S9IR;gX@k|>s#KWdIEu`+>9V-Z$=VwWNLA#TQSzG#jvZR>6;Y2A~Tvwm#X;pWkvXbG|F`zd5qJD+@G7DSs)R88&N(Z{;a8MW+HALH2~u znzkK0S#em>E!=ng8jrJTVx@tLusjiUI`wiJ z0v5`ALDp;ob>3nMP)lRl+ZE|H2iiu~hb~T15x~n-IIiCU)giA>N2g2lD!*FH+LySP zf~ht;(-zoBkH6kC=wU<>mOo|BbZ3EsTuQ>`UNjCZ*q3|0dW@>5cvm9$?A2qrqG?Ax zN(HW;v!$GayN=zQKcf6Zu5adb5ME%G>42A?Jlh`-zoTS@CxbKlwfqaGD%l5usd;1E z%!1J%l{kW~*REZhipp^`njBnx`VpL)B8+!BqO~t*YLn&Ta*x~}G0He1h~P#8wSxD0 zVPx8_ZQt(Hh&8 zhGmCnm#%YxxV1I?Pmcje?O!C(LKF{>^nZ&aWLfA|Az~8?u509YOgh2c^3XPrKGWQn zAC#Igjl?qHLD29Ji4~uDdyy?&eXM)xmkrnSj`Dc$CowH+nnRFfsr)$l=UT7ek96&P zd}30uBK11fj?LYS$M}wzntlCLBngVLcMk%TYPlCtb)m&;a0)@*SOd&JDFrl<(`?CR z)%*QjX8Q59ah^PMmnTA>i`b9{0kK|zoW|-sWZ5POA=3xpR1=R7U=rSEreWQf`fM-y zI-@LnS~kln*JX8yhdks^GCdjTBq015dE4x|-U;h*wvlt}=F;Kv9yCVcGaMQ@6MWV z<$E`$0hb{qNWMS=vBA%ncUF@cO>Ex4q49cu!1l1@UyrP%86>K>L?XTA4{@b+=)nHL zypcv9^YL|XfynFAazUas(K4GrCPS_iO8Z8jtajRd(3(Fi5`(Ix$qW}aiopWxVD01B zDJLR4rffljsQR0$gnQptvW79Oh;=P@=Dsf37@GM}9pao1E8-y3UFx+QtSPUNB)KL` ziA$I-Zeg5YWgA=iW~qD19JZ22^1Z$ik>BO{>fv4(GU96X+b!&)^ygr)_TXDUtq48R z>lIbldqZiXz|rV~-cMh0^^39%AHKNvWaDxVUnkr(_D{3p#fqT+ zVMgkbIAgL`_VS+oB%6d71_t zm4zjlPjQNF7N~#AGeCDnwf%^>c@;};5URM_yh%}hk{_-#t&wd1TSjKW{DE!r&3K_d zbNJXDkL@fK2Xyc@l*G1S5WIIh2-9oh?F~Yqa-A!h@CGjBiF_E%lvU6mXGz})d(yF^ zJansv8TcykWC3$ND6LWrvl`Fscaq;QV^k1+gu*Q54kyth9m7gz|{Ya8q3D zOh39h3<4*)e-XLc*zh~4D{G}2IXx^%_|YjOefFnjCn+m?6&K64ROU?e!EDPeBVexk zsQ|2aK)~1%3aQHp70xUWC6p32E?5-p(V6<0e`#gHp5aLTB0D2ZG7`~Iszdg@enQj^ z271d-=WPV-NeZ4%Y!J_8*7@U~>J)cby)R+Im%qw^ z>Q(E4)FVVaic=U_p_k}kc#IzDQGN))-X^J?g$hQnea>UEA0!LZQp2wL`5Z_+pFEbJeFi z+q#kh4n(3Agv3bO-uO80x}+)mM9{-}N4rp6>XL3ni>AhgimUgmB~N9x;K;7ML}2fs zhOLa!A`(mHDP9DC+(?UMEFoAHD?%I_zp0V-{X4P6_q!xmYa3e}bq-2gq!$Ii&07~s zSASuDf_xuGHBk~&m5{L0xXTOa|JOt6 z5(i?YG?Z(iLZKPWXC(bn|2g!JCi17VG7xaz1zR)T`+T$5X*ktn!ZZx zvO9^4bS?rA20Y}>sAskXL#OX=O(eac8CmW)G~=FUZn9ZX7ZfAfG9zH!R@8D&H(@q! zHN_65(3iABNn>}@a+_y{OWyCXGC@7f?qJiJ*#XFE;z7kXD z*8#SG;*uE7{&gf{_h7&*75gpYw?N!=_7rz$dH>BUDu>lZ8Ml8#=e8{^fL zb;$wrC$?bH5k?@EG0_>&VN~MNhn=cy7CQQB?Ra*FOPyX9rcRMxP6Sf#fABM9hZYf+1L59}hh_6LaA%S|_=@APaWg(6 z!S;|iDd3B-yxdOt;GB*d<|p=1WA`53A$h(Z^N-4=4=dON<;_sy+gbzUZ7US@d(5pz z`ZUO33v)g`CgpazALzdV9H174BE)0+$F*ZWggV0`onP!Jx)ry+aP>@y88)8P_kT}0 z)g&uI9C0UnwstiUy*OQqrB;L4tX8q6y)#Ria#cx2JkX~{jZ~_ajy!NM`~3)^r#WPB;DOcSwo{CXhl7pMF?c*2urce!Un!}gV;Ovl zetj6+Jw91ZY58U+2Mr5Fa~FOY`kPLPX+#%!TLAkV+5ox27Q}Q6bwFsXwIfEi*(aE! zkME`U39?_e^?$M|WM1QziU^L=2h&MxpU^Z*^-D#dP|#4NmsSG5QE+L|n2lYEB$rD0 z_TIj`7+g7PDM*y?4p78i;eN5x+u?1m{G9fgYsblXB)YbHZ`gu@H;6?ivGHR1X3QTt>y}==*1(QUTnm{SsRKu22I!{(AC3LUKvTA2o($^FDi=hv+ zhKxOml%I;PCn7daL(zpC%HxT5IH}wrd`{qblVJMgE8-H><(TUxg=ZxtZU&2Dk2Lc2 zkb?{3Po??qL5*L^&0I+hh^rAxuk>XuJXE9HhcC{FvAJ@qCCo+^wLQ<2&lXq$#DG*f z2x6FeFA(A~R{+Hf1-3>oB(?k&nvG@stPWm1|KOK&59^kqd)K%UtAwW@r5QbS?XkOA z<>^Rb%qD$Hj=J-Bt3DkA+xy`8$7pZIA(94c4qqSi$$%w)AoFx~ov8i07 zd_TxKIiZ%Ko#B@KgPLIUxp3AtB_Gr59Y|f%W^j|*g^m^lB`!sMZV<*aTaNEzO=QO|?UmH+N(_~lF|LG%m2N^CFu|t+h@*meY@%)SWs8wt z`XS`m$bS)diC>PHZW@2pbwyY$84$_y#qp$_Tt#bn9Q$@c>LyJVd^#e~#BNb3>OFc; zPP_uWdk~Vfnrtg(_24o0Rp!y9@PR!66|w;YxufhZQxi^yaSMmB`sRK!=+8~{xLiI% za}~aV``C(7SsYxw8fiS^C~Bw|(cB|*g+LGn_R8$vcaY(|#?2*)QYCvq&dHZ-G%eh@ zC&xc<=_{$Bk5rCtoTxoL5M|%V!;w?;=HPxYKT|-ImGW%4Sq|^=Z$De1UDO5;BL8HZ zA1DcD;vncNcSkg8Yl>D0!mAIt-%*AyHYzJdBt9%QE#LAXAbyC+Q1Z2TD4a#bqNcw{ z&1?_>odr;~U3ze0RmuCFP-v%phSTqq!+|W3<1emgE#YhV_^oaK#-h>qlkYAB;zO2j z&sZmKoZqU~`ov8URU;hsmh=~s51{aP90g=~0Iq}0Mcd)z^-IcEe7b7>!tLJqhf40W z=}c>Q929p^ln(*2YtmVD6r!5AaBpfKM^bY>F-ONG8!wMOm`3Y6g-}sst0D3b^_{a3 z7uSv+h~x{YxJznB(%fWHs-u+52Yz%JEaHma5;A2NUSbz!NT~s*vt}EI@z!?=6>T@( zk8DmJ%sWSYl9k^VZTq9jkB|&ecxQrUA*++m_+bE^UPjoa zpcN|Xzl+`Qixn*1h%YLmG&ni3)`$@Sk(ig7z^FvUA`kVos`(g`y#4t~{PhuZI8jiz zAqYl15ocTRA`p;9)B2rluFF3|=Y|rJF-yGd;WumB_>97zQLnk{K-5=6CgEphqb3#$ zRF)&bRs%)){SG02Qi>urw{v$)6(y0g7dJ(oMecf^F)xF_4MW^n?PY!g2DBvyA>3NG zb}Yen`CkA*s}Vj$`*d<5(6LWy&z8sf`P5oLmM7wKIUTEwC9IS!L+9FX_g*hDg=olm zfSKPajKx}|Iek(C6aHk)_WH2(I45N*=3w=#Uu_jFJ;OfF6Y0?W0`X^9VH4wy)3s`8 zsy63jWfLzc4ivphhp#t{xW~C#&2MZ0DJT47$C%HPanpMtG#2G0s<}^)1~}i1qPatm?zUo~7+cJsmDk;(^MsGPG6_)6gb81W9uvQrUCi{bNxB~(3jLhTZW)!cl+M+U}M+pn1Z4W7kL|lz<6~E-ko61ixe_zvCVPX_e7r} zxapG4(pJ;78y*F_@CVdS+kB?9%4K{HInuia0uAsrOdYTK8VP1Nr&R|YymhVW16{clUenhr|5vrcpf>+mEipa;@?^QsaV zwOAyYX@REp9F7^%tIoYXvUR)_*x1_JSV%S%L6To7Q`+{VO)4?*lfr(X^wn^W zlgiG|jK^QCjYzfdt3G#6d}&fq2-rB=lFsGkzXu~ORyI;l7V_ftq_FC!UT_GJc>CB{@S?HZ>1&75qv_*El26^f;=R~{i*=>gg0Pz7L@>(8YEaR}W8o(~ zth1)P4!egLhdf#gkQ1sQ$cBDMXa9^|RgC~Qi7C#I1+fXisR_aFo5#lJ3A|}^^69F$ z<9~mFeMbi7??7*6BFsSKNYL^=8I!a=o0&RJqNd(Gl8u%X3v^J_Xca?mW-~^{J7(%` z$oT@UqlG@br!g5y5Nq1P^Q#_A+z34KRlmQM6SRi^wR^jez@)$P6<(cAJi$Ap`Vr6M zBEjzr;V@WbUD!xviTdi=>9{HUcXYVhN|f4aB;@0iNPl$eBq<`+5K>+l!geBmF1%X1+N_whO1VmjwfcN#Nl zn7L;h!aykmN2i*~$T@8#+%JX4aLyCr1=!}qTr~iEs zjA*(!Qf&m#l&eTj=5F2W+ws?_z4be1zz_<(iDc$RDSV`{;!h91T> zb>9%muCjmGlVSt}QB86Mw7u@Y*>r)PXUhggi(Ya>spjswd97m+sy50=rRqE+b;LW2 z<|BBDw%^ky5Yp`YW++YBJ*L)R6ooKi-}4mjj*GzggB6kG94Rreiooq|^ch)y@S1B7 zc)g2Fr+uD1lsyc{3H3iJcx5iWib?)fvuk=99F5@AI7WJ zS>K6(16{AH2%5T`gMdY6Z7(U?anS`P+a=Ce;VV5Ki=tSzP)Q6~(C&kbcVYHv?-CFj z@WN_tSppd7QF0LqVc0aV=j}@PUK30EqVd;6&UiQ`b0h&4vOQ*<%w{^#KesVAcngz6 zQQV|!t<^yaekt|v+0xqd--6?__GO&o50*JWkg6!`@~EPdQGwsmXMZhS8=L)}*Ll`hTDki$_Q~H8I`NT;%L1rX z-M2YBHx*z!*;^Ynn8_Bi_z!(24j7x-K3nA{Vz|9;^BHAC(V42|IxN}pM}dPLx^m+X z;NY~dn_`2G6RS>(wvD9eXKj=i{73mK@~Zr`w!Gq~kbWp&f&^{3VPDtk7iq8bP z=R_bFT?=n5Rs{8U=M&7Hl}F`rSvBm_SZ(uY@;5iP%&H>dAW0hL?98zSGoOe2k-vML z8~~F|4(E+T#mdBSGp<8~S9cn_l)6&~_Awtz`C~2u z(5hh9mlyN52_$Woz5=to6TT{z9=>zmy&@2!qw0#k--@gE@CD|;80p?f)fI!wa}RBq znl(GydLa?RGG#m&6_?*%a3F-Qv2(PGcfWzO@Bf5)>c&gp{F-zHMxF^jkMWbdFNdW^ zJ~HX~Bih@E%|T4c?3#Sb0C$5A+=Xi8l){3So89OM-6_Ii^9iFk_mWCAKHJUHX4pMN z?CaU{FMn=Msd(!hgu~D^akG*!$P*V?Mx}e>->jt7j{bAiLk8}79xy!a7iN-jLqiDO zuq6%Vx@9jH+Y3TQ`+a2BkdT_Uc3iAI-Te$;W5vXA0G2cqs86@hd0U2v{6#Uz`-A=w zkLzq~ZdA@_02FCJ4T}aBb|%{jqj6UfgDL2B{7b%z`j2wHslm+a^6M?Z-fLpVc9$Og z!|=;?(oT<7SX{3jNx@U_Wz^XlS6i?-W&wHbx#@{cRAThT^DvLQ4x033c(`@5@LAUT zfQ#V8TwaBO-`8PPksmnlEgOB9MjD)syFFh$FT5I??wj9mDm=x2!ep3u6Z&{X51U1HPFe--h_%b*ij z-`2=fJxzT$8m~J_L-gG9{lFPHPFRRaqw+5&Bib9>R1wo-%oLvku4*i;Q)sYbk6==5 zHt)to+A92{1&544s6yF-*E>$_Cf z!yEbz16Yqh88+pRoqrD6n4TfVm5XFP4;15k0rBwQb>gTyt^EMpJW4$>lM%LiCy}P^ z?UY1SfIgkS(&gyCz^n1}Cz(L0m!B9$Nf=~?KjPD4e#@}1+Y}DK+)K!KrQISQzx=_y zPkSXnOgy~9qQ2j^$R+dA-Q*-xlW@BCtASlr0D(^Z_O6t15x;~V>K6hkXtdBNgfVjV zfY)PK8b)Re{b@P@!sl*13}a=B*`2*ocL&qHKO^J(M~Ii>ZU1cc zkG?4(B$-ig=QsRr6qU)F6~p>nzYz>(=q<&~u;W+9 z~eM8EZ$nZJXU*8E(8m6;gDf)%TVNm99};H_IL9q{0MIIDFSpcP_W zs8tw0=8e8Nw2sPdxEc;pJY~4pylkQtHa#HXExw3@Mr19XQo~H0MSDD3(fO_)(%RD( zL5pH|uN2?01YkHXtSBLV5b>#|5wV3KU4r-*2+z+LuK5nQe23tk(a%3}Bf>fhIjkqlfN%Keu>d4F4H$S#O$ z+tOo6jDa&>GFE;QoE`f&(6!qo%O)z=fE&PIKFjBa4%j9JVI@0Sn+!8sTD7L@demYx z86YDDtkzNQo~y*yi>3JbRYGm!nYVDjU5)f{I~ldS8Jjmu$ip4 z9p{Doe!jW43Q2KpOR(clBEu>1v>!JvkUusYq^vZ>o6H4~Rm8p=kh*j!_%4eNj@j>L zeYA79siYBkC_#q;?>COmZdA6rQ_x1Dh8dcL0g8lYP|0lXk3pC|?;{5w&rmhnFs<9&E0&z<7K)6= z4J+_(Jo)-i`r>mBn3?i7B6Qp=7{Cx#Z4pu99E;raQ#+%vmfPaPF*nz6i!6q5Ko#+L z6F=c|hwQpwLwzy#u=vR;tWjh4h32)j@l|PE-U@d54&jB**fFACKIe0mNh#!i`vhBJ z5JFhFW0D)aS;Q-ZwuWlJwu4P6{VuZM>Z^=nE1hErxw@im5RIWcjtf19={N{TptsLvU~bxG3wb7gHXc!M=YJP z$;67dl&!b_oy{_^nplX%(zV% z_aN~&WI4IP=(O0vgOXH+CBb^O=w%Y=Z2pa}9a#n+fX_%RU_ zNbH*_}k(8xR1bgWuY}CiJ_HKgD;vJjMjToL=Ho8C4JToA@SS6cXsCRR%b{)kvXfV2<80z zd{3wramf}p**#y$2?kP=T7!kTjH@OB{AWC68J53o)Xb}C^NV@YcKi* zra@8Nj3=vgLVr9l`w<{}2%PwTpG%+$57zCxNbeM&*nfcA1R!HSP2wRR9I7)Q8y*xf z7BuLjUS&%*v^$A;7x_^v_+%d(vH&RGvd``&pKL&Fo}KV@O|v5CQBaLTNC@3hUn5Da-Q=k{8!1r5?hlhvO?qCVZ#|utMRZZl3CM^U-1C`oa$S?s8 zUNKCZ%J)hdg+^oFmX<6Dy&wP4tJc|Z8vB7eE=9~<4Po-qDrO%i=)7vnpLtwx&(mu~ z%)d2m-n{lw;{I`h6>xQX7$UspvR*!R#s_8lWSLo3C^igvF6^(0et9s_&&M$OJ^<0G ztxLJ3;fwZMFJ=SJAUig4Q$pPuFaRtX(bXG-X#5W@Th*1{8gg3Nc*65SafgO?P2Fw3|FjMt?YYC!B*plF#%tfcv@sf@ z_k=yGbE{S3k-G|{GnIEZaK8S=!bY^hQ$i50H#{2VCfxz_bEW3YZQjPC^7Seq{rC<; z81Uiv##0#`eS?2h^R3^;%kMJ)!N2H@QPUnIGqCtdUEL@kc1K`S4dXW3w}uT=2|Gh* z7){53)0Ll1CVKUD4RTm|?ap@Pg>yY%ikjIT#nPxos^iPuJD-?_t7{**mXvdUzUDSn ztz`^MgP3F1Yh`A;*r+^PRT%T{+)^PwMOAJ2+;|NhjA?(Oda2tvB)mq2@*cWltBsq5 z7As6k7W>IN3ZW*fl7e0J)F+s^KiiJ-61CD4OlW}Q7MDzA9g$Z zLG_svlEVIM8c+dqcbUB@FNmO|@zyD6X@8x|7T|Z~b)Sut#_SzA0hUoiKk&9$jRR8! z?;Wzdk;~=(&4BC^GwCP3%;6l}Y2uX&KZhCNbT9PW;HhWbD(;*DN2A~W7hLdP{|}VJ z=nzw&{0h)=cDI}uUYo>AKQt7bYa#7px)4*!%G74~E5{?MZ|IDGu0jhGW)yJUz%>Jm z5>jIO05P*3cYYLG|55lBz|n)?7ltqqsuGcI|3_q4Y#=V^73T2DY_`3gSZ&S!#xPh_ zmLM~d5?_6xaBgUSo&J-RE4Vb!0rZQa_#{ZW?_t$P| z_oBb3bu)Ip)>yZPJO@`qeH?K>nE?3-|q zfUG=W`qM1PyC5C}QssbLAd3BmG{*u%P}FBV7i=4jFc6~a=tp=$aZiBz6M3|(^RI)` zLykiP#I&w?>W{~P+aU0BSRfcNlw~if+vDX|i|_%q+V^`fwP8_QQ>U&Rw5@LHnRHH^ zl4_az$tVpWHke{1-KYTSVBXto(9O)P^~Y4boko8H!;>EFLT!R~jU!D5Gou<=&6U@G4zDdWrTRyB z!&5u!(aJ3ynkL!$fu>Hzb^@H&A-PT69Y0vCa8l|KKPIcPAufJ_n|9+HO5;)kFZw@}K17JrC?l_dQP!^BYg8yT zS9&*mN(>B`Nhdpa{anhf$Dp%Ql153YHu3JsX_5bS&N^^=O1rNoU3}S%e374fxdBhj zJ~-LP@usGPEH!zCgeuSNepRUWhnM);Yi0o@;0At&M%+mGo2G>Fx(Z_>$Qd+^U<$?xcCJk%i|}g z26hPBZh}5W)VepT73juy#*Kv?tpMu@Fv*I~K)@|i@`~$4zOH#rctX(|J$9HMZ5wTV z(KXzC@)4X}B^U9oYRW9@qqrgC+57i5>&Aoxb%B)BDet#5at;G~K-WJHP))vmhgi*c zAhM-DhIlz@WTn&BWEBrsrNDl3{kCgT+;7IOtv2PgR!rS#;McsCMnnD9=q36Wo{Rvm zjy}nZxH?10QC*;~{haUbo$xC9{OX=htOZYOhS0vSjYF1%>JR)0YC_a#Qk@&hp)^j z=ve<)4ILuDw8*s@$lAP+gd^N@`^y=YQ3`wiREk-}Swp61+9?b?!qfQMP=!T|d7pb= zdDq9ok8j#(&|b%B$hV9cT-UJq;BtIiwX7jrvvJT?%=}zU3;_q3i_@UtO{4?tkyVAI z*RIv8KA*sb9nY=lhh%l%swsbWY+&Ie#_>s*n1KH8MUFV;Ex-|~~!u{M-@!qh!>TFg!$w5_H?ZH)G@m|0nf$^TOeX_-WIhcq z0BGz;(OG|-NC+6)7W!mrnP2Q}e0kNSQb^^+*jPvC(N|Z@azoUA3^Ckm1`#QmrHApw z6U>(Ua?N?JKqSlg|V!+EMr(|Xrmncls zm-?-@QX3`!ElQ32ufrj8j!NYJ!$e9`-ZwHZXXXpX*~YiEd&rqBOJyO_7p=fuPA)@H zgP&jcZ?wQ)si42A+}*x`spEd}C~WU}K!qw=z5Ab|@ZWTw<^OyZsEt1_*nGzl?KYQf z=ulJd|7Le}rSAb1H&o)hhbz#m$KKSUfmr5F#&kJZm|N=g5?Bm-nsQ2_2XZ62iE{=n zc68xnr7lV4`P|9BOKgu2k9mxk0@ao93_=rn_65y8%a_66V)o5=Vg&a5<_|Gcz;eBZ;-GogKx)0aCF4n>&+| zFvojXr;dqr?Bt+&OhOxX3#*@Z(=E!!6M*&O#m23eW7F6=gU%Wjy@xiC#k;^(iup}k z+km~q{fw;ZBs_({IujV*e@j*KN39yBFU^broC(l=056k&EvJMhxvQwi-YqXUmib_~ z*IR8cn7gEvf_r1u#}k11WbmxxB*W!ZhCWU0zr5dnvRDH86k@IIFCVif?-Q3&T zd41-~_T*jap*7@TD^YHXzB&gXzajsvG?Z-(hFiB`f*`EG>1}eG_!_Dw!MoqlMs5$% z2(q6{yant{7Y4N(^5|QB-hZfWum4<8;iPT!aJ65+NKaqB;661S6d{O1aE zQY`eYlkd9xvq#+M%glA2-jCCMwOr$kt$d??w{UF{dYc0#MQ8WJB;yog4D+szux zM#scQB^0EQuLJ=6AGne7c4vvt|Ht>S$pAImz#hohR(66ktmm{3l=a3DVyJ8RUAZ5a z9K!LFs}-B{}_1vn1ZO;P59%GWV6d70*xK z)4e02LA8VXqDD7qi4L@FcJ8M-uOWu=T&?oFDiX<4#6~ZM?yD zv|Q=D!)CX`K$Su?x5#DK*_$i)jeSw+h#7RakvI!qM?%ir%ZbRlQ0KkeJ3$nT5 z{)qHy`}J?r89Of)y(Wn=oP2A-gMhvIdsrF8QHiO9V;MOmjqBraehvO(sc+#8Cx@dt zB2w*GRm6vP{t|Xxt0#EjE#tk@cI4KUF66nC#NPS7$`1xbEDoV7jZbG@L#}5799g|1 zbdGtA*YAO9ozw0JucWdq+j`pYy{z=SSko#wxvj5bIdH;%~z!Bq8Ujn%39A zT$rr=JZUYl@=5bG`cqS#9P-I#SnNyx4-1<4g|40+x~C5FT%L*F7?UdMgPOl3Z`ZKmz5GI%Pc~}dAl!CeK*C#deTEtz(6m_pMY)Nln6CsS-Ggz`6He_v4 zFZG|xA8K-A7_g8nzTS|(sKfP504OeL#NbkQ5+Qz}j-ur#qYr_I0fBkn;54wv0kfUO zqvfc6?>o3={N@8kvZNpfM?!kSp%QV!Pb`8o<@3I~_Xc+-FzNSr1SF1y1&=2WsG4L= zWRT~#vwJiMJ*6qfQjLzHi&hD(z6r-{Y>hZsX=qpCI*p01E*vA6_tUgO$_E!GEp|4Zi*&@?7`6sQ zkAJZd<~)8^my-FfFN{7@%=Q62qr0o7HV?(IJAAMg&q zx!m=Mn*JqJf>8s3H9Pay*$W7p0GlsHyCp#>2Gta{Pk1-9N4*Kf{H)Fo3U?1oZ8OEX zY^$^AlNLkjf3x#PdB9U5zY3?P&pnDtvR>aAh?$~rH!vHhgsfmAT!_M|$}U3DJ^VYO za2VqFK6j5+lgl*{r5&Ar)I|S;hH(JKf*P74&pyECe)ym%L)ov3iEaIw6aKvKm+F$x zW8DDMi%`Wo$O$sU0n9sE{5(_Dg?|UlpA9spv7~YVx0H_uvqyZ9J){+p@P=&=^Cwfh z&~|uqT&l_Sz_?zeui{W`w{15~Z|zz&`FufECh+8-{S&N(vf%hB zBDXo1pw&LI>jHl){b;#Si%N7gpXWiAgWKo^&690k^daZj6vnvKN|Qub8@T-Ah_NxWRAvxeva-EO;ev0lJ!_ybw-# zyA55rkYw6q(9bdZDSew%9sgeOf4eb_%)AI=XODA%8FOh3e0QG*&lDot4`OK<-z9Ep zhDK4}b)ICLT~%Paxfzqb8Df%%ntS|Q+lzL&YSe@#->;E{h^!ZF*T*(v@#~W*=(X`c z*AS^;&VwX@gYo->y;)6nus$6Qz_@riO#J(oEa7 zB+w*d`Hc3=Tl-ESqFK`A*zS5~@{SLplNQ7jO@o5g* zvPDPL)_Eyu>CaN#w5&7#TDHnK6{^8%3pNtWXWL>KI_DC^Fr%* z%~$G@tHbF+{FcWP{x~wh+_pSTsfag0pVQMVh_t}K741RiK*lT}7$x9V!0;$?xv&kz z@eYC8yc6V{9pO?{EA5LT{W$`(@h1YlrEzrDCV3YEp3&Z%)PXF*^Ftaw^}V|t!7myA zZ@Qni7T?k*G{Bvttg9^`k#vl%Kich{OHL6M3EFj3WM7Eu(wa)Kx8)^5p(e(y3%Brs zAW1t)^N2f|v$<2A&Z#6Lx9-k2_gd@RBYLQh=SP!Sb4gbee37YQVd*tQE|6dA7V=LO z#mRF79(sy?((*fks;rCyfqo#nTckp2@xLB1;mbOmz1Xt;RKGb4BdR})dB)xRf_W|= zyz@NpB3QeuIaE3n{hww|uS-?X*2R}1f!~*F`q-BgJidN2!fu=n9&;N+j|7?&Ej@N+ zM-6U|1?6+2y&i5Pl}3zhmutjx6Bc7Uc(nGVqH2VTn@A@d+Q_MoghT6^5ko`6JL0T4 zSiLvQfczAYt@bvzH5ybfUBPyB*-Ivl{i?2V@!b@vHHi}Vu zVH4SyFG=EIz28(Lo%Ab?*@$C2bDk2s8KUnv&D~v_#g0ACv<&2Z?pZUsj-j+LPyL4} z0}@s4q01(7vR0S?o&w85c~KKqJOKBvKLGJmyD$nd+a1?oI-IgsKNEP~SZ(@)np^uB zuQPZM?tDCVRSLVoefn(9SaX;8=!C_Za}@R9z%U%}?o@me#+Ff(h}wU!F)-I0e3RzCtBdH^W$oW@wJP?`HN98t|KoK zhg$Wx6nAUMMC}WN?GizOjQwg0^qdd*_-mWO<62hd;q~bz4)-5h498G|o4lzO#Q0g6Ez1P?9o1Mio?Q2(_!m0DR6F^zI zMG{(AB65I5#Z$G7SdEI1p8XxKW~8+Z@L^tWrU1vEdiKHiYBLXOddNw&2r+h3fuL5N zN+3J!uLt3=#T2MD2{95EzV$s6bynIfv9Hsz5GxQ4rd97RA4=4z(CHpQV5L*&ak)U) z=`BNs!PNdNBbO#JW7K)!?Q_@}NEd`X`q-QA5veC)p|MsLpMeZb9HaSb(^|*73bO~3Cm}PHfyP%SIwTkk3%LQ0S z={_E>dYufSjT(aW#Mm46#^Le5dH-rNm-jC$X=V;2iYJ<#gB-dFCu!SWIDr5y%kWwy z43JygJ4KUaNF)iEzjNv7dRL)aZPAP-&U8r4rxg(}w$HvF>Pe6K?Aoj-Wtf}U}I=UTXtQ@;(H}j93 z81!!Ky(GBUnVGt3kg9=Uj;i)x;ZUsZ^PwsH*Nn%ofd-v}g2Q+Ejowkix-R65CLCfN zRB8VIctJU1wp{s+71DxMVlJ}7z+dC7*VeK$xvpub8}}Ql8Cn_+8W1?zJ#xF(Po31x zxJyyM)-x2~&zkJsOHc38ug*&@VEU^2Mg?ipWD}E>KQ4!km>*p2yckfF^Zxx9a?gt{ z5dZB$m9_k^LGX&(-COp-+rL!vxjMMS)_(nX|7A%P-eOpRiXM*I+1eYpPL{4aJ-y-+ zwG!-r-wVzl@%a}V=2>@}uScBqRw5$5m!3{ra0}t3`RLY)B0p!wT;kVuSsd4 z&*YPvh!`O~0zsYF85)Bye;2N9TK|ijXRU_am>jzWP`&fLUT0WQe`K<>_i$Gx zQ}dk#O3y^6Cf)y`>YX3sYP;{@u(8viv7Iz%Y@2NwG_^GNaXHx{G$gCJd?WPm-%u4>t+mIrSxXqf&8yE0X;C z_=(!Zf@gNOg_UHJuaU_j%HKQzhNYOT@rnzc*mIf!A81lY zHNzcWU*uX^9NcN}2JsLBJ{q6oi2s}JWWR68llX8*Hw%8I1xehOVCkw(KTzooQT4y* z6;0Vz$}j1i9LYwZ{ex`(DoXrb9q0Ce03zKZ|NTBk%&J0>Pw%94?c>qTqelSE4^k zF~7_KOI>jQoq#7&W`lCteaZu~Dtt5U=KI0HD+99Rw52Vw*{oMjBdQxWFk~m&K*?$0 zg}oh|rwGhBV(Rl75?-%j?BOET;O0Zaa_iOsxi<)m)&=n0FhkjE;x$2egu^FXWhgHihqMk{&=Jr| zn|{^%cl~SW|2y&%O5W1{phX1JG;tUe6(ea8m(KKjf+b}MwCnT#cDWmeNg+2k8u4xo z{{AR57Id^1!UJ4xV?gi=KPE!DPugZI`4QaIfE6q}T zR#JtJBZXIo2p~=eMgqLN&qUE1f6Ng{n-T7BnEed%To-0QUXQd6qUImJ^__rA4EwwMgR1w8Jw=BfQ>v1FW!{oZi z2U_#9aIexh*RhZuztRFKK@BdbGI{HE3Kjow~Ix?yU=rwIj za-aBtn5_1|VrZtBOrDm<^V*rmQfTCs`z@bm2Hga5&TbmVrwXnYw<{j4aHfG|%o((o zQW4mcwu*>_B&hkPaSEX8$cG6aEdpI-1OMA@<99U==&LONv66J=XP0oKH&_u>Ze`8a z*3k=<5@OxgPgB))CFlvX7~uNo6NxJuX93j`TOB4lt` zkqWO9DwTgFPFKtcw-ex2%?B))^=xH3Yy9>Rz*lY$E5i;(lM1}`ciKhKfCcL}@9V+- zGo`C&ZX>W;;&TRNuNiC=QPFSidssK@yWY<1AcSr(7yIo%QF6U zzQ%9&k*LC@0e-VRqae9ut%?tV`maV9{l-(H?cNbobPQHzhzA?>2P$eXp2MHJIwi<* z4S3(+mDmt+UlUSW0KnK>*>&eY1o-#4-h(5%mpA(@>`adTTz_cOrcADUch?{O@~O0f z?>F6~x#D`@y`hL>bz}&-7sp6xeq;*X)vy5WH3+B>S6E$Q&aLtD7>0+>}Pyw$v$ z2+z40^zfjW55}tWZOtN$ki4^Lzpqy<8FV^32O&kep~wwd?leFb;{<-qEx_dXk$AblQtWFrR5Za zJ=hS)-2HeKHQjNkIHR894Pna9X(;0ToH-EqEsb9VklMxx38D3yg` z6UaP@EBQFE{SKJl6r?W`I9-d`?MWRWz?{l^PlBK;BinT&PE zg#RTEG|zOXeh@EFu>U9T{T?J=HlF?7@Z2J4+-vAzp9uRM0`Wd8F=vNRbT&GSc`sa= zIRo^nWtYNgE9vilgKH@)Cid?hr-SyA?7BE zmOOgig;DvBA33fqw|;049pGJpW2X={^0UEzQiHYHrLCKs5qPe%_! zEaHd^1GCK78NhjyyIoKW+KW`KnCfD}LH=q5GC^(8CHW^&ly!4r zxS0Yq9Fv)+?=}W98CI#`=wjZtsQpy2hB5UlTTKNK`{bxtO6?9VuF)QURiX8-*1dfrpHE2VOEME+Nvq2R3a(m5?vmoWmd!#k4!u-z}d|h z2#r(cmJGXDzM=9HK?h!g8Q4zPjQzvSz$_EVSMY2B@i3Zm!b z@9* zx_eE-Ryc76Z*%@=S#%6Kv)kx%$UFjt^XfR_I(HnJ^c6AOd1fvZp~v~zKb_3VJ``g- zV{dLkRt^7Mh!>`75mJNf+`@~VG`f$o=i`_89K^6y>r|qdAk7AQJB#~PPTwIQUd3C_ zDUvk_VI+I9kx=#fQZDJ1i_Mft+8J5MK$8t_Qj#GsxPC>*3ZqZR&Ddgj@b4|pV!!l4 zWGEHzcYS(ELJuy|1wQshrNj`Z6~!+|cpUr-^qPYh)vY36K)*hjCY|_ttl@6*9paFR z@)IzHOgMU|gM)|uoqDOzO?{MYTrSKTqGxry74x`pI&vahXe>cEMZdJ+MsWD)TdeRc zei@huTOmnrmk%w)XR?dr6-`t9JMb_9ZEyvOnqN2W4uZvP?4PIb6EU|TMT~Wwbf`1M zg+bpce#0L63EQjkA%vxjXKhpbuerawX|%5nx_@dM&yzN)-_eD;l)K6jfzohgR3iV@ zI77?<>;;+!)FJWIH@011D_z-q#=>H!NaKGwe)t6`#MFxu(+b*y%3?u@A|TKGWy3cm z=~uW%I^5ZF2nHQO0S@Ux9)fUawVMfzV`>N069i5Ob@e){x@Fee`OiPiVJa+3Qqb73`FLyj03-zJZZ?np5f&8=|$|*NH^;=$;Kg-pvt5b0E)r3v?(%$K~vF$l^50Cl0 ztF1?-QiOb9hQGz&S70BU8?HhxaYP*S>AG|$2z4#GOv2?42)Fd*qD`HVLN{901_1mNX+`aqbQyh43ATBT@jPl z(@QlH*6DNfhOu4KwZg4;7eXVvl$){nBZQ(p@JPA07TWNEI(22f zR1@T?3qvgAmX+cA?DR&0na2m=lUX}HHGHeIc^8qy@RW{u#!6@?GAc13H}_^6i0%B4 zr_de$yX{pC81?Pw1Ix>A<_}12SWZ+==VRwlA79>!!=@8n(^wvEY90>lX@=Nk`}Ud^ zv5Nzd(>*6qRn%zCnrG)*|2Y*-UENew)o`C*D|Ow7Vh9n%%+gz7uYug&?aZO;83>Ky?>zm*> zyWw>s*=didCZc?a&oD&LE=@qgLd8+{Q92X0>VaDBRQXC5>qeXGLR@(`KW<( z>DyO}WR(QnWcp8hqIwI^4#wAwyn{MB-!_JaHyZ(hBM={nUOANX(w&9`a4sy@Lc`xE z^0pPdjtpwaazTQ1$M8wfzRH7-fX7@Vxd=Kuk?*C~>1b-DUl{&`|4Z`kwBGan`&}IT zUB`q2u^x(8dWu^XR}qP1%q0I@)sR7xM>$q^#Am#>HSE0Ti?zph-paT4^)w0 zO-@7q_T6i?H63anH4^Klqqv#>D<#pW%0?Lh4@PuDbS=97K1#0i)<7xyQ@>v)MVlXS zfnEGDtx)v0P108#mv89)N|`(+;qS;%6%+ox&EUaMgBXw9*-5o&;lPnnQfXi!3 zn)u`gj~Z%Pu)X2R_SPwSero$+sN90_Z@lHHx*J0SCJkWqTVZeQL+xWzuLm|;0h(MV zNLgB-q{ZiT9E;liPp9vs&#c`j>lp6-?*V?6r2(b_*IDGFvGaRfOyF>Eturli(xGC_vIAIs-Pc8FIeAc={G z7C}vxh{+O8>U6f4hb2Kr$5CZlr{c^CREl;kjjUx?=KRO}YW^DwX2zuQ>YYd_+TYb# zH6!Ps;xTZOK}Rzj+sM}y6Fu+B?xPYT1e@<2(2yQu2VK0#0fWuy%dzV0rccj#cvWDz z3dQ1nd7*l%Ahfd8&MrIN&n4TQhJ(t_eHvy^?zX9Y z`d8v3g_Xy_eJ8^&k`KJM&<;x1Z!}J%vw2XQ z3%f%s)ZrV6b6cgeL+y*7^L81Gz?nT$N2-b*T)I{=)aG|r%$s%`wb1RMh9JMt);Q8$ z=Zy|t@tYT&lCFTz%b}$N?qebkx)qtr3)Lu-I>nNN;v<^tIw;qyu_+`4&0-@BA|VrA zDpBlY=tUXX#8rb={F2dlg_vU|6>S@9cP>MN#OpATc2z+Nr);SD7%**hb5U58$jzJ;oO6T|ABu z-!5ot`^xoHy1uK3KDjQFwW%P-hN&WKHzZeLHrXP;Y!7t_DeuyG*1FFx!Ht4 zE<+--0y}P!q;>(t;qF@TWNcwqSv#!?N~g}rTUNBp;~lW^G%PdT2j_+co6&mgF=ENR zqtK@Cv)M`e5y3-qHX0}p`=Er_2TP2c-${gdEssXyD4(;>Leuj2@b@5UO&mIv{_wi{$wjdvPKL0-Y}=FY0w25B61rY?Nz4+;^t%>dzO zhp>KH_@MV#{5N0dm&j@3`TWEPsFx6YCZ`SZUaL!bSO~(o0ma-;q4kK|PzEVQhU}Zx zpWEQRjLoC#=4qmcofI3Up$;Xh2xffS$(}xz(4;Bb14)3uyFITXB{uw^Z&{gE4TR7A zy)`W$R)&Lg6(f4ZA|LZ+P zn^!zvb^W2UhxAE6MFZQ7Lb+<}1d;4g!=MuC+}dPTvsc)NWA3BoI!e(c7Nj_QA)9@H zHcVrN4g#|Q3q?J>{d+RDGcVp+Ro~YoV>Ls9z2|KNQw*G&@p9_!C-=Vg(6QGHBvQ!~mjrzoiuB87nVmXr=hr`AW9wHRasvoh?glZETBWC9tpcAv! zKq5C zY@1#y9UE?AwRNU17Q8DV$XMYVynl39U77b#q1`M z%Qpt((x{;|3(_qHFz8OSdf(ggrO#a-sw?LS@$mc=x6w@`LX5-p6Rw^3be=p6ki*p` zv-yP`bn*PDwk@-U5B%fO^Z%AY6_ew5kM}VI5QKzNx(I{n2{i5wbrnFbwqu}o`hv(2 z?@35tfskcN4r>9>le#b9Qp=sC14H{u#@gb6)LjE}i%B|Fejm7lrH$vZ2Kk@Rs>OY8 zy*h>nYLP(V!Pd)=xq|qEnvE3?pE+>2st$#l)az4kXs(26OUNQ2hLPA@^%eFU)gc}7 z6Vm8)gu^8t%k^xMU*0-$I`S>mv3Q_9;iuOIyTOXU(7JjMA6%+%sM4^0k1igMwnmhR zDh&yi+*OQhOW@_eP#B)KomaHZYs=}SDk>y%7P3fTyUu@p$3xomL6 z#i_npK_Hfp%^wIK+{ndLZnxnt6ns|3PO}e|P1nf84)7$e4RO@kWTR}9M_if~*?oa` ziD`fmdnEdwGh*g@X`z%!&SfEVg@qAj}S|ByAU*f}0;{~hBt_#>ZJ>$OmL@|%i z5U5fc)PcjM0}uC8`_JS%?6}{nBxZ6_ck!+IMnrL*L0p{R4_jMThAl_)r%-a&9u4BB zfQ?bmA`kH0Xa3+t>?iepWb}HZ|HlAOy+v-}_9K~-%kuo=-fQ4Vna3kvQa202TqYWP z?0R0lb>ZY6DbD)hS(B(~qJWLVGB0<;j(7Lhw87?YVPcuo^NEmZ*%V8U=j(qf zh(xx1ja~)GZW5p`gYWe+mxUq^$F431r>%Gr!H@ywH^o;4F+y0fRd$Ua$eTND*%pt!y4(pD|Pg1 z+q3TW(PJ-geb%;EI`FokI$Sm0=t1d-f*;z58VTTJwd|P3 zqs=p++iKsSd{WZm^aarfX*3p|gmhif-@PZcpZVt^ONu0H5h9&zRRfqw}Mjnocz`5@F$(8IxgKq zhmY0gWd&+x&m6uUZ>`)!P>K$fyS|2!dI2j#Nj?5X9wtn($^(#K56CN&EgO03v|Oxdu-$@myIVmgG((;;DT{^B&e3P9jelX!0KYls@E64 z84Hv29XQS(Rix58r2j`XaI(uZIC4SU3+dwTuzR7h=88nKX#y#^G%_DplO|$;N8de2 zBga{de8KRFiXx$rr7FE93u%mmdDmzAs3kbw}UWuX-cb0K;;dlkWtf;F$grMil zm0vecE5UQEta~D_TQBGM%E<<6nCaG%l9GWffBR$b`kqAyg6d75FyxtTE^rguv~zNM zYz~e3o;N}ub=*H_H_17jN{wJLhwU>e!F4~4@R?U)hw_CVXr`O5sI{?3dc zMYj#p%`Nys4$gix$WG{4T7m37tMM1_kN2(H3}fo;l>}C}`}g%1^ee`g%JUZ=N4cB9 zZUq}`I_2g>-a)y)ziHg+9K#(ick~?w(G)MSJ0A;JDUc*DVdQX;0*j+e>+J?5-NefBB_%0%$Y`<2f_L;XLJKkio1LBXPEIM}6a1JuM!x8iTi>MT z_bNuuEVEI)v7Q26HHoQ^%Xk%jMrP2n(Pc_|D|7v&SA#=La1N=ZtrVaf-+%)^D$L$#joEojxpi~U36ym487)`r;5(Sq&GSv!& zdMmZN4bQIKXPROkPOLgx9d0~v4UWe?Y+V23Up->5_IgC(2U}xU1D>`QKyz;oh9qOE zrB81b42R(LD*;?+*Rl7%t0

zk>@wqc)U@FCMf{Y5+>K10&z=5lxN(ZWnrq?9m_i zl6@`h!0o8j4mJliyX6|*KWw4gmX+zB;aYZKyP^<(sybj~OblBP=9cxY0`m6|5pXs? z#)Xm|lr4Mru{f~}Pl*`Z?}}(X)@ELmx$Im%7N%{!SgrQWCbioV%%)qDhIWnuA5AE? z&qf~myJy>MUo0&)p#QL)PHkgA8+l$K4!%Ocu1DT}3C6u*KQsyT26%+AGXVSD-lZ3y zXDL2qjT|9{VQ6VuIeTHud=xL7XA9eMM662Lk3HD2ve3jS)Rw_H-^*iUZ5LCop3q02 z?9UPv#M<&B*Y`(s@ZCX<5d5Kt0+TAIpPWy6DnKLZPGj?gjEsbyU(jIvsiQhUdT?=O zp>Ws|`d#&XZys06G3GU5TGdhN%hL;6NpYfq{r+z%0TCOEv#G3x6DiN;$=4jVt(U>C zP%?@~iEo9fwkz#C{p;3zRS&=y0%MBjaATWwEUH}>7v><97TXu}xf(@WJ4nUr2HxJnwPTD>TxidgRhKYRZ>3sX#v`rHSJpJv?u-1Xg3aBCmCXo zr2mLS>$k0L_|R~AKMF~(?}o%$Q}W{fdaa6mlQExVunzNXVHA!Cz#iK$ZH+!FFeMDN2Zf0 zjR$pj_9|u$GrLf6s|_oPasss~AIW0i!t6gtsI=!&oO_Sg9{0GX=9c~jupTts>rPa+ zfqG=%(_lS7me?dwB8m`-Ae56`65Ark3p*`D#>SS8zf5$hz6H5o0U4UiG)<8Mrg{#rKq~fh%g%y!FSI=Z;5;&Uf(e9T-8>!0T~; z+HyqTQdRtM{rE@s^07_#^2r^3?OgfvIW)<>r&>@ z_ZnHKy+@i8{WI@cc0=rFAd(7F3j_zJSabU-Nc47MwGMw}4|Ar&s30clR3yh=jcFjEKBmtki3=NkWU(XbJLSu_H{G;Z4n%!K|CT!ym z3RKyA`xTFrNUI_0FH%fPr*0<4UMM>U9FV-Zzdy>q^jNF^$(H;zH?N|DfS>mgdLLnf zgHK&JC5j{nfilK_E9#x7GrH!1SJGYim;!}@`6wR?@$wnz&)s}@cvSxfI|4ud8j|+R znoo_O3pt;Q!y7ybB+V2;`aJb6~XmK+ATlaH{-{6m^7su941*I{P zLrGWvJaKUJ%x@(%p~}n4CE|J>tHpSwAsmBj_fRM)@)ax>EHSh*yjC303Wez6mXDXM za_kYGe$^C4=|zTHFU@c9Rl}`(>~+CIb=hV)6nO^CJEuKhwtHjv+??Rw#rkdh zau>gDX4??p+{cev54r^Dfx2YCVvCoKzlHz87N`mSfbnR5emg=ki@XH+@Gf^o2Y8Fs zD5JK%xs#cq_yrWU8)0#0^ar<+tHHK}Iyb%c{UFLmU+$a%fAoEO*?!;UTaY4x*z{66 zAMRge6Fy`eb@N2retp^mbh2&8g`<6h*(F0{#C@ucoWhmg#tdn?YFd>spCOt5&=Z-g1HAz1w@>L2jB|WW*1Up_Frn?D4Xz)-tdyWZkX>P9`sS zT~uIuM3*3HT11j%!CNzWvIKA5EW=6=Ua+C4|YggflZXR+y7YE_!>dzU~3Uo&K;qbtJcXs*V0y-nKEn(F{RECn2^%z7|(OR5AT=&?4%B(>g zVBVH~$6OH8YZB94@vpYeOndi*Qp@7p;~e)J-}XBhae2RAI2&KRGw#+lK-xhOc6~Hx z?;v%XLF}y+l|A>rMB!Fg(-yMnM*y7IbvxcX+(@|euY^USgJ$)7&rI`E*>*oxo^T$G zF;YX_+~Rvd>fyCUcd{ph51e=cGOCFAIa|QM@h%^&8B?ligKjVrG@!Ti0(^##iG4xK zoom@4%Bwu7L|0Z`KI(sb=k%Y`TV~bkL^?z7y2-31N#aSlDD5KR9DQ_{lp01LazX^V zEK?pv#y#oB20j(cX}|sYjTmrsWe3o>m8&SPPP{W6XF%3%Y0%)VFSBs8BK(N^5hX%i zp2l9<%JUa9xSXdRr+Iq;Nh>MWNfq};3AfS%Ng2O^rV3C$9o zITds2xoHcUStWevI+T|ySiiQPST4$cb8BI)irZZoIL#+*aaMP|o1}`1n*awoI)A*f zi^yx@XvY7ZR_nLtlsJ0}oqZLx@sL6x(~_5!C1*46yFo#0h=gjDv@6crQB}~@TZKq9 z5fyOA+Ua+Nyuv&QLA8*$YSvX8DIx?w;>(oVkB6%urZP&r80f51@CI(egI7{W@9_wh*Up8=af!#sLO9$*vkAEb*z-kO;0Yx zqJUjbUwukS?-#XZmB<;zv7M&&#QJuT&l#@YbcT{CUGXF4ps@-OGq{zdGDm!$32r4s znT;EGeI2U?*hT)-bQ8PZBx^y|2^p(r4sqZNXJslKZJUS-0eVdW} zeQ$Ns!><{i-l&ygeM7Eo5U!*CdC8ec!fg6_H3?(`n#mDM+6>$rRtQt^SWOn7#G8!- z%LTV-e819}6oaA-wFTC@1f72Gi!-tT?6iin-k0VD&QCw~ze=EG3+byg(t629bbJ#M zsMCX-b#gDX8@QVRpQ4IW>zf`E)3{++BrY5RrH!Dkc+2C32>C1-BIZd>Cqh|XUYPaS zX*%&3jZ@T0#KT#!kSwN~D}G9Yrve-V3n_LT8e3N_u2bJ;LTyPa8n-s<75#U}8(txz zBY7$UN=%uL&l7llF45KHE5283c7WZu${8iDE|MRYq_PRy`v0F~`K}<%Qttjj)NRsK zw)xx;u3WtP_u5s#+H62JwJ0iQM1+C|=-ZlAp3vW(94hFkEOxyx1|gyOixg>PO}v96 zQw8fK*fJ5LS6;cvXGKDq6^Z0t;Zrtr#JbIng25T480^9LI=r#$5h^@f4 z6tOULn^cbTNgcSk$Yku|dc%h!37=4+r2mLP}_D&Qb=ie~YqU6L(g<`3v1?M?U zDtlVa1u_A*s-hVSDtVUbypFL9?hzJ-(y9$XEBEI+ZDdr`hFg2#4+hIph_&i0HxJ3O ztN?{1F{fj3`BEnQ28alcCm8?}#L7&qPf_wopS`V)#M+X-JsE zunCsLrJB@ajUxZ|bI3|${=EuntHDfzz z%G6YA74P4qYq(#DQ=iF)`w|!_WP=~4kQ`8Kr>*kn05Qn_?loXmG8>Q%Xoj*n;D;jl zIRu#>2oypd;r72nJNWofu&V4Yhatj478bdza(+>nKlfkdl5TW;m9ETvn%Xf@b90zr zbRfRhdUF$yOypzmrZW!9%W^G^-qTg?FE}*}xMOCRuhb8&dck!y6c@`M9Sydc_!78n z)+isLplyO?mY@|KW(C}Tnhtv&lbjWxx?SPAXAHJF5X0X)dA|Q$YYt6e))m{`wS55- z86liJ*JHUv5976YMR8XTatFsAr4$Nar zE1Q5CW$3-qqU5V7l{_I{=L@p_39*Js(@K+VNiI(uG6m)H7)ONrI;3^Xw-sGUe?|FU zQf6Pj>L?6;RQmd3m6G9R1|JVk0hxrkG7Oti(cQn#TP`*y^-!kw+{t~Z3^ z5jES<_c$5_mi5=PQMSToE*j0OqQZku=&4^?Ba0$5!~>H%V4Xa3$&nY7KEcsbf73R@ zSQ4}T&9Vp`ypEx^HJVnpda8*g4qUNHil@Ar!qZhI-@DkVK{nG0Eg)O`$Oy)xw0z?kcDq58MlmLLkDh@J-qaZxxw;b|{o4sdDti>j+@ zbn#S0HPsHRF|DX8#+5Odci62r#E5B%pV4kPNGmx5l9#wp0_4U#k0;HFn@giPC=|1x z6rDx{5=^J^qYK!?WX}Vk`Zd98nnZ>{hD& zy5(sDub0mlZQE_6+1*J8eMNailG6ewPM-C5t)YHe+V-pXw~?2eOsig!?mXZo`&Z3g ze2va*CKwktEXG+jMp=PEcUP>w4dJ-ek=YzB0FXdi6G=nw3J!l;6xw+I0V&-m?C<%{ zZlLf@*o|t8R|VSIk<&oC)BUIV|B3@I4J=f5$DfvuIX*Y_uEU=G0tN2sal#mwaA9Tk z8xQKe!6Y^Lr!+|%(C9yy;L^PJ1kJv*_#mSOIuVOOeQ|YlVjq+uSa)JS!1XL{glLmN zobq!n7W5tn$ce1HCJ|#6?905lc*~bIJz^p>;0foEGa6NEC#rqVJ{Ez}KvlO9tW>~r z>JZ9WTZZ*@q(#oaU4D&Mn4Y0lfaT!oAPS17XmLK0;snM<7spVhv{y^T+Y#6GW^{Dk zB`4r3!r7QI*5q#7r*h3wSg)>Ug82LzcWHn8pEYD*n6=g1`^`3B@2*j(yP-Ou z10REe%!n{F7GSY3$qWAc@n*E#+KPd8#YpBtf?moo;n)GEId|B?l_APwWqB_6f0f3e z?X=Nda4wc0HJ0%Q2$F;J5bqQ> z6$RiXngFp_CrlFA+LE@Mn8ob8{rkxTN(1e0GRbI1!MqITByVD)0tswW3P(;`(0|7X zp=;!-OZD>!A%6iYPj}vsxulafg8xbM2RRETBSAA@F^epjmf`07nz!nGf>J=5_5rVK{V`zBGivh zbRcSyVSF%5J?}?h_Q=UsG{L%zOge_+}c12G% zMC`~pecHjl0Yj~hU>LIFCpkv$vTgUOxQolVaSxvYtTE)3>9$y&7}47+tvwKa6Ts9emKSa*G{UX4P>Qye z^lB+WE{2qUW50&wMG9`l@OKPOJjs&8#}-sMl6&EGPODkXYE&(PWVtN?C##xw^^Eh@ zq>h%O&vNFnl89tlgwpOm!zgRoe|}>a}ooJ-U>!q{uoq9dg)YIVV@VwvdeX?!ZVw+ZeNlOt^d{i zrm2XkU5`RC%u5sU+xc~NWC8?ixxA8Dep$wTVAa1_M&PS01pduloxki>&? z^ZRwfammg#<8%0KoZ(LMg-HX8zUP+cJ<-7l@BzaQXNEr-r=7=q&7xSJk=-kl^T00* zqY`_9W{!aR$hW&2vP@~u@4FW)=@Y|`nf}xg5ojXxonHWEflhqiT^o8j+QOMSvPnqD zoX|K<$WNCxVCs6r?5KKKM^oKc25Gh=Um(s9K*>Ktg zZgNvb10%Xz1i*RUs=~@Lzr#nNIQo+~K4~-%rzpIZ15oTB=5HkDdS|WG@-Jw6hJnL_rk)|d1*92#Mr&=Geg;eYRlK2goQ$|QO zD(ZhUm2J_-r^G_1GCP{c0+Yl?)f3XDbNa=JaPta?`ia92g~YxLCrXPzt{Y&4zInG{ zb1G#0XFM{UJhoD$5E3N?5Rdu;u$fgg{roulx zy|4`mjlndYLu8tQO6fzWi*6;T+i2SwP=^Gk!xr{>9dESu!Ra_7svm2PG%74>^5ngh zLX^h{CI!DmY%PpUP!D|%aDNC>hva&^DvN+=IW2|WF0q8}$!tV^<6-=e1M^re42zNu4*$-n@>w}b$< zoMN3n23ZB2{PsVi+(39HON$xh3AAn^Fp~F$D|)UJ4NaF*%CG#V_)kYx_sx zGD8U!EmzP6kmfEAu(w{9o~u4*ODmqLhug)gp5k07$jW{#vXCC8YVUJf!$Aj09_%Vh zgkmzTrzqC$-IFkvXCJR8mAKL7+Lr}?<=&o_IhAzYfJ4PxaSE}AME{S_`p18K31Fz; zr?N#c9Y;;nNcrjY5b8m0Nu#=aP(Rc12s5*$^L| z&iV=aZ(I?BMLuFFvqXNQVhAHRKs{(RgGIz5Y#nP>&P~DEdeiy|KKFZr&qls2;2ifUI~MW#M!nJ|TTR*1#;=&ZgFa2t+)^(! zQkgTnAa0x$s-N7-l^q{u5AMaV@|+Ttix*iOkc@c16V(Mla%||(vya_xp|P3xxQQn*cTOs%fZ-4gf<)= zn)5yFyA7)u=J_B5`%TPNI;FKP|9?$=bySqm_BKO~lypcpf^@gc_u{?3yT18v)~t8dde7Ns$MZaUpT|Y@AKpxrWlvAP zn5_!QjY$`iD>UCmFz^>>B;slk)8>f4X~YsJ(oJ#v!o@Ky7-U-vf92kNwd}ECi`2E> z+CZD-GI%LDi6Jwo_J?j{m4M1q_B+iTI`qqmN=9Lc&g@70;;Yr5rxVWhI7UEecj(`= zuV+==BP>lSY<|dFKTPqBNmY^e?eK@kLqGOJ-~dR}?;{zle`$Rq@ku;mH;4L6)9m{= zZAOq53KZBaaod);SUvC{hurJJ>gh@T+7WpCCk?*(2g4%7i+bQn0@Rz8q$?{e zUm5r~(ivduG&{*BdZM8;Amk{nQ_n#~A^8Ej?#-@=?#(Ss_okzn24njjRrSJ$H$ojy zvEN|H0v8FLt?C1T*GI|9tbeVwI>RqUlcTY8Z|8l_nTl{LL6+VuY_8MTL?Kj2|F4gm zxL84nAi}erEm=$cZt73jz~B)@6t!Da-qD|<9R2s*M87_oBUlKMLO|J>AK{l4sVR9kAkkmSA?8p{ z80pD;X(>y-0V#{#VMnewNAcAnoukX=Mt;&+)3&q>al=-kCwJqn*MDt_uhM(+hl(XM zH2tU?f*HzQ=~He!51K-eKtu15*xE~Rs~Y3 zo3|>tZPUgnKV=Nd2ouv{XR+Cf?a)h~HYtd>Snt3z<j)@ zbtkyn+J5{kfGzzWurPx*_K@AWVQ8_yO_gIVfkOyuli6t9|77=8+Xhp?P9MaGt!Y z2Ll5iwG5j*>)El}T>2n2chK~4$Zs)qtIAaOtiLntcHbfiH(Kln4`~g5Z6IfGdb-ZK zRZj0$SjYN9&cAK7{MEl8t?B zZu0dXi13b*U}rY&H394f0Pz9Q&n-W;SI_@6Xi4v{n*BKH7p{Q7km zKegJJX(B(y#qCc|*$C(}M>EMniN~5`*f`%pGXFy?!Dsvwu4s^PnC%*UZ$s@;fkUmaijl~}h-(?4u+^1ay&pA_cR_Bq ze^_G~9E&#v=N#*if=xF>S9EOo-8d@jTTWzGHfedbEK-!Wf|IS+p$w8fUk_M2MY1bDt{406|73#O*k~&e&TyqUjVK z%z=LEVOb0SW-pb)$L)A&l7K}|N)5@Jbe*Dviu)n2qo2@hTjEBrmP4h=yr+n>xP$fe zFl{qRe?4zAwZVmq&V7oT4J3e&A@pZ&QDe|&F%{Bf&^$RGTx6#*d?|%Jem@sJON5{O1%oI+p=laok*|W z-$b8jdIn4D#5ZGE@@Hbv2ki8URQ9ZE1M;Ln{TJBUG!1xS=;H6M1=2;_)oGKyJLSFI z5?+$GDZxSCWrMn53Tvlgk9VNKQv7GBpT;b;;{yKgn8yJ}Nh6 zN%xAJP=aRX5)wp}Db5_^Hay}t5?q}uAhC^Q_`!-ACe+NzY(?{VOO5c2JM+OyY2YqD zvAo_)ST`$9Vy6Au(Y<*)RJ_NQiSBcKu9h_7<@vy#B1ez<|K-*r9p1wPHmoEyJyBGw zBprMNvF=%7XcOI5FZe+@{B0QwpC5px`QLwY>sCde;X*uR{lfR}BAdqa(mcA@K~HGX z#nfbOE$tSsF}=JEF&BiXPczqP&Au@yYOJQ+(r z1ss^4N{F~0*RvUKk4r z{57>ojGt=(047fG%MSNsfNKdta^Kq9FjO3mUqgpgXb&cQc@FH2ijM- zQpat+JIMLtum+25{72MJSyZ*erAKS%6*A zKT7ariO=-D_#XS=Cz7Cg6OZFrzzr010|3PDEel0|5%1p8dhc!JH&~_DL%*-< zAM%3&ZYyb~HktPlYW)7rgwCVI8xcdtuHF!hIhvXK<9YaB?AG(ZhnVaW*r!$ zKC*aPOtYOtyY1!s9r<;Q&}}ktpRT1Kk|kBdUAqcFGk^MhT0cJ!X#VXyN6z#cVY+4J zjxCI%sKy$->&RBaiStXBN%O`_T;OXQJeneuAn5smW^Urqi_A}iUhBk1$+===pc1cs zBG-QGW1C^yHNfnpy*%C)(0`2-F!$#2t^Y2MaM+>_X^)EX(=BKqF!n5MTd?Cnj>W|B z*xSs-*{#-neRqbD{ft)fE!_J>o?6h4ntFowQfPV?Xk@%Lju-n2KjvYOxq6q)*?=>> zGcr*Zux5;z-n=@c-%jMQHO|y=Akm+Zojd&+GE&f!WwHjDKg{y9L6@7gK3hnM4{m2^ z#93A05K#ecX~loq$#PssK!S<&U^1(Y*$)q7t}}_@uM>)Pyz5@gkVH(G3$QjZ?nV^=*!>LjLD1xUMC22#`U55ZGScW@y;>YH z3%-wUm`Y^ULj=A@(^Q&H>a;13;~Sc?!q0(%6q8BVN1u@pSx)gE0fr^zPb4emzB*9b zG?@=c_Bq&_Hm4EoMOOVf>5B~XQRXJ+`o_iDTs+j`d($^idZrVOENw3u@}DSk$F@16 z*#wMF4uQXxbb0t>E_BIQBhc|y62r{&2G;16K}-ANywp-}^m~+A_Nasy0S&pv7&4e28bjI?RvfH&TSn>iYb(}F?4^OA1#dm)vk~^J1HYp9YSNW zp)J|~tl2a%6jfM5!Sg%(bBja1jLCh9E6i;zMf>*`F%(`UTxx?gsdr{TZV7|y2UyxM z?{9Pc!q5dCVs;DKAoyWwMg2z9Izy)nved+@u%)}Y#1$%CtJxPy6a))jMN<}?C%framf3J4c-(u5i+*ZPb>(pv0=&sIxhK9^L> zA}I+CS6fZ_oqV_##k7fPn((Oh5-l~};qN}JwgH0{fWs)m%>RPb%f*1gwYQVPr=vC6AbjM>ypQAzvNu+^PZY%C@&+T|rgwVvl({EkrmjZ4M8au(!(msa=e`Wyx zB@5J>n{+>?T2^s?qo{CeR^)5G(Hb{Ke|!x>AqCgFmd`!tj-OPcO0XnWZm5<~MvQAy zEs7dLmmt_r3SZ8h3dAkP1IL^hBMGNQv!n95JkXW0SXLDhr+G5&34djpD3O~*J)LLB z903&IFu%--NTUed#)wfA_?Wda^Rxq?1+;fT)a9(oMPkFn!Hj7d;_{?*6)&{c z8*|3(!#U`!F3C|o7Lb|0q@x*$q37pgNB>ivEn(3BApZlO?Yq{asHV1@ z6aKvETOvlvAZk1Fv6oDI7VLRWjnRS3v2M?C<_>%u239}F51ii` zYp+}Cc;Cr-%t_JActNEXQw1)$ubSyk#=1uYmfU@v-T=@LSGjav;u{FIpP$u zMxG-Wv}E6eUr^{Af<8XPhrLi%AxcmV6NlvNL^q5n&0S8_04E0mL!bEjW`KUit0H&6 z%t#|K*{OSnY758lZG1I{=rqhyJLq<@Xe!F@xxiY@gaTg6lRC+ zH62gr+RxsVkSmEr7rq@THm^17M*tTHO#Be1Br#Q`O;sWAc*|aJMEp@eA9+Wx@Q#NtJ)CGe=;@q}paSP7lAHmqOpf*Y9P+DY zg4@|(tJDk``k1B?Fp!2neovH<$2sjRAM0YA>wuj0P)@Ea5L`kLz!= z&sy??s|THRhuF(pG@m{0Pcc6fUzvO>II+k=QefB_?h%9y(*1rX{IhX27Q1T;@$Psc zb4`6IY~6B`Jv}!>iAsk_Zqei(t?x4E38Z}%%lQGrwelGQlU^uyqrIr7<7QwPh9`Yn z8h+#Twovt~_!2j$;n=Zev%Cd{VyWLj$ zRO2Zg-!}~swIj19cA|04q9obqvly2!2z4>vc475&Y#O>u}#TlgGl2iqC>Qlvg&y zw*{3L$QHj7fT@#oC<7+fp)Blca0ve}qKv3;$a)phpIq6`=A^+MM-DE&NEj~!g)qsr zR5h5_0H`L2@5-J1mzRA(?gkgS{m$#c)wjM1o?h1p0xmZT)wMZbtUP0R>DQ`mP!Y%? z!w}fiF<0oX{pqc-`rq&3r|pkbgN%Dv<=&8_eIVSRHj{P9q01f}$oziG^(x zG1yAm{nDf1+KCx#z`+)g`~4Np zfZZz}X5YvEJ8`A-ynxG3$H(2(wBujcOJ@*%;T6j$b`zzPjPBh%&l?f_fC zu^j$DV@y-hh|i-%166xZ!Q)bD zyn`?xdgfx}#95!7DSLle% zJirp3wB|2&hVm|WklP(PrECn%hR4PVG0V>-e~UhzEM)(>%EbIRb6s(biH7}6L8oZ! z6T>8kkT$be|5_1mrp(trAQk38dD&jAEObE0la(J5CZhbLVB_cunt%Fo&5QB3LrquJ z_T9=?Xu?yCRC_B*oeB{-*`4yd!Q++0jCT7?SQ8g<8hdc>nxIKQhu(Ix)gyU$y~9$98qFwSkD}$l zmxbdktH0gwAn!FP?uEYQDiy2R@*iPxmNmC29UKP_kI_H*|AfD$6>~TpJAKQ&*CepU zstvX(#)a7FRZ*yg-BPF-+~}pAPwyo@h05ckHPI&*IG}42sDd@J4UEQY)vX!;IH5v= zd=6qmqd^>9y2SgzlN#0`n`rzr&F5vk%NCs0D~kE6+u4^7GTxZB8-D#A=7zNvjDWBl z`9m#3eT_wxfydD-Y`VcjdtC~0-;e3uiFyugB`p44Em&_TqfMqZ^H~GT6H%yE_KPA2 z3vbcsS*9wqby#R!r#13w!cWH^psf5ciShKDCRiw#SeA>a@fff`HckR1ZRY~$>q{6? zd7nFF9a=6oo@)Krm>jqu`+-dD4U$yDb=p2PsE**lw_e#MC2a~?^f{sF+kCoGE^aJ+ zmDZ0H>(GfKyM*J0NnqwlFrm-(JNxEmsIF3DNg%Kr!i^A_^0jbv)pk5DUfxJrJmta+ z`p2iTw&ZbUH?qN^Md5zFS5dbl?umWNA|KyFM0|3)+W@3;-O!Fj^{}m>xAKT-MjJHgW8dCSO1brzTrv*&@+l*|5a=N5ADa zDN>0+p%-JjYn$w)M`@rM8x4eN^o(kR2!2{fBpG$z^RFg`#*k240V_|C}Wj}kR6-%vu23>O?p+n74Hz**P0GB z3||e4;vCBAN$Sclj8z)u8px(H;T-aSX%pTC~>La7Q8PxG0G-PMbS@qg9wTYKwR)_mo zk$oCC$0K4y{zp_35ZxL3D73-%WI1u~Y;W_5fv$_!231@P@2DJN4r<@>qrv)nG*TGO z77mvP>3mZj8c9{~3}*}JPp?Ii)eFFeply-}G`{up#=?N#dtb7yL@NmwvSV53+YD&~ z0zAT#zFS{nsosIh$n867N);BS6FgUx2i1Z^hkR1QGaiP( ze%;MgQE2Zw398?3`+x?D`1&!Ka_V{v)AWZ~=Tt*ftp14ihp-RD@`$5ux)~t-l*hC! zkqoK0QzT4H1MXfQx{ab-Q+k|iK>D34g(0VA;BQNE)uvKdf@s$8auMWg{uEf{kCbFO zGQL8I4Zz1zu^ zs$LeJQ9V zvcwQv4A#=e2@!_pVs_ncxyCp?B>M0Y>jLJ>ny5QFnfXuJHPTsw1^Og>av<_HAbPe5 z>%qFrFFrB%@!k3pI}13(A!3o4ot6j=wEN2VR_|n?sAwNGa1t?vKChH=gNb+8Eh9xK zNp&>9Z!S<1iMx&+GSR2oU{G=l41Xold?2kSIE z@kPI*+vD_9|Ba5G-S=xWyQF1_I13BZLl2_Y%O zME!1#<4>d)Z)@o$KkX~V2MP*ggrkd=k%4`|^WkE*b50ppO6;z^H)Ah5gX}V8Qpw zYNPjWYhClrtLyNPC6<)>72L=NxQYLrg3w5G-O~YikxyPa`EqhL11dc%Q}~D8`#3HH zD*2d_+(v}ZdRde(aB5tV^$Sa8H@$!mH0U=DSGFAe0CV~povMp4rLMU*CFuCTpVuA# ziE7cyocXTx!YE_Akv4kqT^e=ZhO!(`U~qI|ZRQrSfgHb7irgC`7Iu5Um-y0!qEO?T zPen<5W(Ktyasq)$+zgkOGv73Hdm~{cKwocj>?~t{c+G_rSnXF763aU;iI?M_9Yk*y zn2io~m>w74Rz5wm7S%i-a>uj<;gV!c0WJl!LTKQB^IGPQ?Gc3j%T146%yOL?k7BcT z()2qOmeH08eLKHxstBPuhC%5b;plTwvV`H!8>Q2=U!O!UKxI|nkrL~|eQoUJ>p;}pAeeZIbVXv0 zVmnx1)R>dhG#-bg^=xn29V^qqM3Nt#Y&V&WHSN`Ey}NpWKa2A9^m}wb&|@X3mOFJ9 zD<9G_Qb6;sA`(M4_Q?{vVm?d=l(B(A`{e!ERf9KRMtITVMYlJ-6laf$mWYe4=N{1J zp=>d2UEy-@ho1K;q^T8{jvRtdE-DT@rv+aOMN>PIg8wg4f**+o{hJmb_KqnA$-+Vf zr0?-yDv<`AJGC6e{nx?Cw`F+=O-beit79M&n3!>!j9DJ?{m7zyE|Rgv5L^4B*WKUS zs^XqNnC%J!o#FZhN2jUd656qa$IYb+?IHi6W>iQwt?At-Rqk9%``kqh6Nmo2XBlBDSG=a7Iq!yL_l;iivHKY5Z($2C_6tjcUpSy)rs5%;7a2w11LoRZFF31tr*mf8N(g${yA?`8 z@Ee>`uT6!1z~+_YL&Sge2k-_>VSufEm>Xx3Go4xW|&zs%0TRooMFZ0(twzi&x3ys1`WX!l$7$0>Ao zx!cz=3~S20qg6k!Two*-3Cet(s{pfaoN-U3uXNYN_jFXF&-_yChC3ajelqnMELB7P zcMe#akPkn?Ox(^WdaM-h6;Cz%=b8d*Mnf^p?pW_>*16VjApA8+~%am;~Eu&#Hl={3VS zn|wrxwfEbCr&oiujW7fyP4E3-iRGohgMa6G4RhH=Iv5W^-)-(S`N6-t|HZ&#iC{j=k-*lBkJM z$? zp9rEJeyHoG`*Tg(CiIdXT4-4~tdmP5>s;qv1FP5VRaJ~Ru?_DfVn4Q~+4kR}d_EWd zu>JtssPYT0CW`wD4M=PeLzdQeEc1f#o;c7Eq`&5KQbs@^`mGMk6GAzfygTMMn4zL2 z60>d}Z{;g|!hV$3j<(E8%;pq^&HoI26OQmr98JkfwKM1#+-s5U`3Fr3gVb}@oLJjG z;e+t*-V1LZ^))f-s54B~Yf|{KJ1Kn5<7yx(&ZRv|804t_G}3B`xQ(#my(BCOSousg zVoK;QM6syeq9lmXD`Tcz(aW1*t zRS{P1OBT`IFTW=n5Q`xHb&~2NV*56CXJFFlYUKtX{sE3GoW_ru6+%WXiFWAinWs8h z{K_>}M^1Gbsm}hsn7>vIFjx-su?s0GRDZ_wvUqPPKUloXwbE{7DId{>-4l-|Ds8<# z<<4BEMw)y?fiK~aFaERbWg$LPH1BmuxO8s}-RO38YVSdt#=1pcz;i}pVD{fV-yn}8 z-br&t>JCY(p4XR68L~B!5|0neBFGCS4Pe6=%b=~HA2R*e=?Eu7J0qji$jGs=p4saA zGt0{BeHiT{vtqUjNLc>GPQUF5_C{QSnt0#lletg)D70A_$C?l}2z-5WtEWT)&{C8E za5e#YmsST>KY<(jL(&Fa?e)P8TX4}DIHyg<5Nq8+NU&M@|Mpq}f_j233p+W`X+;6T zsy~VVX&RGW+B>W@|K&jS`bH9N$tav7*8f+$C1HI6z4$JqRpCx)DYtE%gN^A-p<4W; zr<&=^kh10ohdZR%`v2;0{fCt4*^V>eRi6rmPKQKkPs5-;ZEbC{+Udcg>n4JRdbYXC z_7aDG=0VyxxF0JG5jlyaW*W zN?A!J!IMXOKVJg}@ujD?{<(A5z}x71l);=z0(xx&az)K99X^-91m+o_gbXE4_SG}y z?7uxKDZ5R}aj-lZuiT~6esKrJtOEOFURN}$1~+ATE-bCwb^jMu53enJI%a!>)H7$W zV>6RWbXEYI(45z_)qjz*BPv%92T|EI-(T7l?C!BP)sg5f_Uj=Z8NMEW0ia9;k?ri3 zgWn!|LcDn2XI0)S!-sA;+SHA__qWq;4)OpQK(Bm`JBfPdW0fp*L~rWAsHy$dc$V!2 zKlSXnbyG=T?S;68^VNK`&X=-q(MxlGjSae+&IEHhQDuEs$lpgkz)gJU52*)yjjXlT zXScGCW~K2$k4YDk{u)uP-UQA44Rh~iCRA+HSb=dA_a-8{!HdSZe9us z!rX>ksT-V4sRo4IW}$wTHs05LosLe`I4_8!`BB!wRh#9D{eG<7Q22DW12+GJljG5< zxzejpyC(|XU$b#}_h04s9zgaEazS(6X4!S<+-m?WH`$I8o{ToRgl1tX*e0okNuh;% z&(#$?9EaQ*(QnOVh&SMsj_8GsPMVPaI~5BJ$IcG!aws;BROIG%=eo^sM*jBpYkDD9 z%rY;k_zGe=?RBy%U`AN#oLC3&d-<)I?Tf2}JlCyNvZfbC<^5VCruGp}D@~zhR@-6<5 zJBjsSfzK7eD*g7c4B+N?7uZDNQkKk2+da!}`zDoWBEydZ*zd1Xr+H*?ngrizA`!^xW-x`y6+Pq-uDHxKdy!IuHUf_GeZ*n1am{(KxMe(<#q#muFb z=fn \ No newline at end of file diff --git a/src/sql/parts/grid/media/collapsedArrow_inverse.svg b/src/sql/parts/grid/media/collapsedArrow_inverse.svg new file mode 100644 index 0000000000..64ee0f206f --- /dev/null +++ b/src/sql/parts/grid/media/collapsedArrow_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/exitFullScreen.svg b/src/sql/parts/grid/media/exitFullScreen.svg new file mode 100644 index 0000000000..c324952a8b --- /dev/null +++ b/src/sql/parts/grid/media/exitFullScreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/exitFullScreen_inverse.svg b/src/sql/parts/grid/media/exitFullScreen_inverse.svg new file mode 100644 index 0000000000..f445344cd0 --- /dev/null +++ b/src/sql/parts/grid/media/exitFullScreen_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/extendFullScreen.svg b/src/sql/parts/grid/media/extendFullScreen.svg new file mode 100644 index 0000000000..1dd39cf4f0 --- /dev/null +++ b/src/sql/parts/grid/media/extendFullScreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/extendFullScreen_inverse.svg b/src/sql/parts/grid/media/extendFullScreen_inverse.svg new file mode 100644 index 0000000000..6b1464b316 --- /dev/null +++ b/src/sql/parts/grid/media/extendFullScreen_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/flexbox.css b/src/sql/parts/grid/media/flexbox.css new file mode 100644 index 0000000000..6c364c1769 --- /dev/null +++ b/src/sql/parts/grid/media/flexbox.css @@ -0,0 +1,79 @@ +.fullsize { + height: 100%; +} + +.headersVisible > .fullsize { + height: calc(100% - 38px); +} + +/* vertical box styles */ +.vertBox { + display: flex; + flex-flow: column; +} + +.vertBox .boxRow.header { + flex: 0 0 auto; +} + +.vertBox .boxRow.content { + flex: 1 1 0; +} + +.edgesPadding { + padding-left: 10px; + padding-right: 10px; + padding-top: 10px; +} + +.messagesTopSpacing { + height: 5px; +} + +.results { + flex: 100 1 0; +} + +.scrollable { + overflow: auto; +} + +.maxHeight { + max-height: fit-content; +} + +.messages { + flex: 1 100 0; + min-height: 25%; +} + +.minHeight { + min-height: fit-content; +} + +/* horizontal box style */ +.horzBox { + display: flex; + flex-flow: row; +} + +.horzBox .boxCol.content { + flex: 1 1 1%; + overflow: auto; + max-width: fit-content; +} + +.hidden { + display: none !important; +} + +/* testing + +.vertBox { + border: 1px solid green; +} + +.horzBox { + border: 1px solid red; +} +*/ \ No newline at end of file diff --git a/src/sql/parts/grid/media/saveCsv.svg b/src/sql/parts/grid/media/saveCsv.svg new file mode 100644 index 0000000000..9d7ce2c951 --- /dev/null +++ b/src/sql/parts/grid/media/saveCsv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/saveCsv_inverse.svg b/src/sql/parts/grid/media/saveCsv_inverse.svg new file mode 100644 index 0000000000..27e755b536 --- /dev/null +++ b/src/sql/parts/grid/media/saveCsv_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/saveExcel.svg b/src/sql/parts/grid/media/saveExcel.svg new file mode 100644 index 0000000000..e3d38398a0 --- /dev/null +++ b/src/sql/parts/grid/media/saveExcel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/saveExcel_inverse.svg b/src/sql/parts/grid/media/saveExcel_inverse.svg new file mode 100644 index 0000000000..06fe80014b --- /dev/null +++ b/src/sql/parts/grid/media/saveExcel_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/saveJson.svg b/src/sql/parts/grid/media/saveJson.svg new file mode 100644 index 0000000000..eac93a7c29 --- /dev/null +++ b/src/sql/parts/grid/media/saveJson.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/saveJson_inverse.svg b/src/sql/parts/grid/media/saveJson_inverse.svg new file mode 100644 index 0000000000..4ed4cc6926 --- /dev/null +++ b/src/sql/parts/grid/media/saveJson_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/slick.grid.css b/src/sql/parts/grid/media/slick.grid.css new file mode 100644 index 0000000000..c808731df9 --- /dev/null +++ b/src/sql/parts/grid/media/slick.grid.css @@ -0,0 +1,190 @@ +/* +IMPORTANT: +In order to preserve the uniform grid appearance, all cell styles need to have padding, margin and border sizes. +No built-in (selected, editable, highlight, flashing, invalid, loading, :focus) or user-specified CSS +classes should alter those! +*/ + +.slick-header.ui-state-default, .slick-headerrow.ui-state-default, .slick-footerrow.ui-state-default { + width: 100%; + overflow: hidden; + border-left: 0px !important; +} + +.slick-header-columns, .slick-headerrow-columns, .slick-footerrow-columns { + position: relative; + white-space: nowrap; + cursor: default; + overflow: hidden; +} + +.slick-header-column.ui-state-default { + position: relative; + display: inline-block; + /*box-sizing: content-box !important; use this for Firefox! */ + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + height: 16px; + line-height: 16px; + margin: 0; + padding: 4px; + border-right: 1px solid silver; + border-left: 0px !important; + border-top: 0px !important; + border-bottom: 2px solid #bbb; + float: left; + background-color: #eee; + box-sizing: content-box; +} + +.slick-headerrow-column.ui-state-default, .slick-footerrow-column.ui-state-default { + padding: 4px; +} + +.slick-header-column-sorted { + font-style: italic; +} + +.slick-sort-indicator { + display: inline-block; + width: 8px; + height: 5px; + margin-left: 4px; + margin-top: 6px; + float: left; +} + +.slick-sort-indicator-desc { + background: url(images/sort-desc.gif); +} + +.slick-sort-indicator-asc { + background: url(images/sort-asc.gif); +} + +.slick-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + cursor: col-resize; + width: 4px; + right: 0px; + top: 0; + height: 100%; +} + +.slick-sortable-placeholder { + background: silver; +} + +.grid-canvas { + position: relative; + outline: 0; +} + +.slick-row.ui-widget-content, .slick-row.ui-state-active { + position: absolute; + border: 0px; + width: 100%; +} + +.slick-cell, .slick-headerrow-column , .slick-footerrow-column{ + position: absolute; + border: 1px solid transparent; + border-right: 1px dotted silver; + border-bottom-color: silver; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + vertical-align: middle; + z-index: 1; + padding: 1px 2px 2px 1px; + margin: 0; + white-space: nowrap; + cursor: default; +} +.slick-cell, .slick-headerrow-column{ + border-bottom-color: silver; +} +.slick-footerrow-column { + border-top-color: silver; +} + +.slick-group { +} + +.slick-group-toggle { + display: inline-block; +} + +.slick-cell.highlighted { + background: lightskyblue; + background: rgba(0, 0, 255, 0.2); + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + -o-transition: all 0.5s; + transition: all 0.5s; +} + +.slick-cell.flashing { + border: 1px solid red !important; +} + +.slick-cell.editable { + z-index: 11; + overflow: visible; + background: white; + border-color: black; + border-style: solid; +} + +.slick-cell:focus { + outline: none; +} + +.slick-cell > .row-number { + color: var(--color-content); + font-style: italic; + font-weight: lighter; + display: flex; + justify-content: flex-end; +} + +.slick-reorder-proxy { + display: inline-block; + background: blue; + opacity: 0.15; + filter: alpha(opacity = 15); + cursor: move; +} + +.slick-reorder-guide { + display: inline-block; + height: 2px; + background: blue; + opacity: 0.7; + filter: alpha(opacity = 70); +} + +.slick-selection { + z-index: 10; + position: absolute; + border: 2px dashed black; +} + +.slick-column-icon { + margin-right: 0.5em; + width: 1em; +} + +.slick-header-column.ui-state-default.slick-header-with-icon { + display: flex; +} + +.slick-header-with-icon .slick-column-name { + width: calc(100% - 1em); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/src/sql/parts/grid/media/slickColorTheme.css b/src/sql/parts/grid/media/slickColorTheme.css new file mode 100644 index 0000000000..868e18910a --- /dev/null +++ b/src/sql/parts/grid/media/slickColorTheme.css @@ -0,0 +1,390 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* + * Theme agnostic + * + */ + +.errorMessage { + color: var(--color-error); +} + +.batchMessage { + padding-left: 20px; +} + +.slick-cell a, a:link { + color: var(--color-grid-link); + text-decoration: underline; +} + +.slick-cell a:hover { + color: var(--color-grid-link-hover); +} + +.resultsMessageValue a, a:link { + color: var(--color-grid-link); + text-decoration: underline; +} + +.resultsMessageValue a:hover { + color: var(--color-grid-link-hover); +} + +.grid .slick-cell.dirtyCell { + color: var(--color-grid-dirty-text); + background-color: var(--color-grid-dirty-background); +} + +.grid .slick-cell.dirtyRowHeader { + background-color: var(--color-grid-dirty-background); +} + + +.slick-cell.dirtyRowHeader > .row-number { + color: var(--color-grid-dirty-text); + font-weight: 500; +} + + +/* + * vs theme + * + */ + +.vs .slickgridContainer { + --color-content: #101010; + --color-content-disabled: #a9a9a9; + --color-error: #E81123; + --color-success: #7CD300; + --color-bg-header: hsla(0,0%,50%,.2); + --color-resize-handle: grey; + --color-bg-content-header: #F5F5F5; /* used for color of grid headers */ + --color-cell-border-active: grey; + --color-cell-bg-grid-selected: rgb(173, 214, 255); + --color-grid-link: #0078D7; + --color-grid-link-hover: #0b93ff; + --color-grid-dirty-background: #CCC; + --color-grid-dirty-text: #101010; +} +/* grid styling */ + +.vs slick-grid.active .grid .slick-cell.active { + border: dotted 1px var(--color-cell-border-active); +} + +.vs slick-grid.active .grid .slick-cell.selected { + background-color: var(--color-cell-bg-grid-selected); +} + +.vs .grid .slick-cell.selected .grid-cell-value-container.missing-value { + color: var(--color-content) !important; +} + +.vs .boxRow.content.horzBox.slickgrid { + border: solid 1px #EEEEF2; +} + +/* icons */ +.vs .gridIcon.extendFullScreen { + /* ExtendToFullScreen_16x_vscode */ + background-image: url("extendFullScreen.svg"); +} + +.vs .gridIcon.exitFullScreen { + /* ExitFullScreen_16x_vscode */ + background-image: url("exitFullScreen.svg"); +} + +.vs .gridIcon.saveJson { + /* ResultToJSON_16x_vscode */ + background-image: url("saveJson.svg"); +} + +.vs .gridIcon.saveCsv { + /* ResultToCSV_16x_vscode */ + background-image: url("saveCsv.svg"); +} + +.vs .gridIcon.saveExcel { + /* ResultToXlsx_16x_vscode */ + background-image: url("saveExcel.svg"); +} + +.vs .gridIcon.viewChart { + /* ResultToXlsx_16x_vscode */ + background-image: url("viewChart.svg"); +} + +/* headers */ +.vs .resultsMessageHeader { + background: var(--color-bg-header); + color: var(--color-content); +} + +.vs .resultsViewCollapsible:not(.collapsed) { + background-image: url("uncollapsedArrow.svg"); + background-repeat: no-repeat; + background-position: 2px; +} + +.vs .resultsViewCollapsible { + background-image: url("collapsedArrow.svg"); + background-repeat: no-repeat; + background-position: 2px; +} + +.vs .queryResultsShortCut { + color: grey; +} + +/* scroll bar */ + +.vs ::-webkit-scrollbar { + width: 14px; + height: 10px; +} +.vs ::-webkit-scrollbar-thumb { + background: hsla(0,0%,47%,.4); +} + +.vs ::-webkit-scrollbar-thumb:hover { + background: hsla(0,0%,39%,.7); +} + +.vs ::-webkit-scrollbar-thumb:active { + background: rgba(85,85,85,0.8); +} + +.vs ::-webkit-scrollbar-track { + background: var(--background-color); +} + +.vs ::-webkit-scrollbar-corner { + background: transparent; +} + +.vs .monaco-workbench input { + color: var(--color-content); +} + +.vs .monaco-workbench .input { + background-color: white; +} + +/* + * vs-dark theme + * + */ + +.vs-dark .slickgridContainer { + --color-content: #E5E5E5; + --color-content-disabled: grey; + --color-error: #E81123; + --color-success: #7CD300; + --color-bg-header: hsla(0,0%,50%,.2); /* used for pane toolbars */ + --color-resize-handle: #4d4d4d; + --color-bg-content-header: #333334; /* used for color of grid headers */ + --color-cell-border-active: white; + --color-cell-bg-grid-selected: rgb(38, 79, 120); + --color-grid-link: #FF6000; + --color-grid-link-hover: #ff8033; + --color-grid-dirty-background: #4d4d4d; + --color-grid-dirty-text: #E5E5E5; +} + +/* grid styling */ + +.vs-dark slick-grid.active .grid .slick-cell.active { + border: dotted 1px var(--color-cell-border-active); +} + +.vs-dark slick-grid.active .grid .slick-cell.selected { + background-color: var(--color-cell-bg-grid-selected); +} + +.vs-dark .grid .slick-cell.selected .grid-cell-value-container.missing-value { + color: var(--color-content) !important; +} + +.vs-dark .boxRow.content.horzBox.slickgrid { + border: solid 1px #2D2D30; +} + +/* icons */ +.vs-dark .gridIcon.extendFullScreen, +.hc-black .gridIcon.extendFullScreen { + /* ExtendToFullScreen_16x_vscode_inverse.svg */ + background-image: url("extendFullScreen_inverse.svg"); +} + +.vs-dark .gridIcon.exitFullScreen, +.hc-black .gridIcon.exitFullScreen { + /* ExitFullScreen_16x_vscode_inverse.svg */ + background-image: url("exitFullScreen_inverse.svg"); +} + +.vs-dark .gridIcon.saveJson, +.hc-black .gridIcon.saveJson { + /* ResultToJSON_16x_vscode_inverse.svg */ + background-image: url("saveJson_inverse.svg"); +} + +.vs-dark .gridIcon.saveCsv, +.hc-black .gridIcon.saveCsv { + /* ResultToCSV_16x_vscode_inverse.svg */ + background-image: url("saveCsv_inverse.svg"); +} + +.vs-dark .gridIcon.saveExcel, +.hc-black .gridIcon.saveExcel { + /* ResultToXlsx_16x_vscode_inverse.svg */ + background-image: url("saveExcel_inverse.svg"); +} + +.vs-dark .gridIcon.viewChart, +.hc-black .gridIcon.viewChart { + /* ResultToXlsx_16x_vscode */ + background-image: url("viewChart_inverse.svg"); +} + + +/* headers */ +.vs-dark .resultsMessageHeader { + background: var(--color-bg-header); + color: var(--color-content); +} + +.vs-dark .resultsViewCollapsible:not(.collapsed), +.hc-black .resultsViewCollapsible:not(.collapsed) { + background-image:url("uncollapsedArrow_inverse.svg"); + background-repeat:no-repeat; + background-position: 2px; +} + +.vs-dark .resultsViewCollapsible, +.hc-black .resultsViewCollapsible { + background-image: url("collapsedArrow_inverse.svg"); + background-repeat:no-repeat; + background-position: 2px; +} + +.vs-dark .queryResultsShortCut { + color: grey; +} + +/* scroll bar */ + +.vs-dark ::-webkit-scrollbar { + width: 14px; + height: 10px; +} + +.vs-dark ::-webkit-scrollbar-thumb { + background: hsla(0,0%,47%,.4); +} + +.vs-dark ::-webkit-scrollbar-thumb:hover { + background: hsla(0,0%,39%,.7); +} + +.vs-dark ::-webkit-scrollbar-thumb:active { + background: rgba(85,85,85,0.8); +} + +.vs-dark ::-webkit-scrollbar-track { + background: var(--background-color); +} + +.vs-dark ::-webkit-scrollbar-corner { + background: transparent; +} + +.vs-dark .monaco-workbench input, .vs-dark .monaco-workbench .input { + color: var(--color-content); + background-color: #3C3C3C; +} + +/* + * hc-black theme + * + */ + + .hc-black .slickgridContainer { + --color-content: #E5E5E5; + --color-content-disabled: grey; + --color-error: #E81123; + --color-success: #7CD300; + --color-bg-header: hsla(0,0%,50%,.2); /* used for pane toolbars */ + --color-resize-handle: #4d4d4d; + --color-bg-content-header: #333334; /* used for color of grid headers */ + --color-cell-border-active: orange; + --color-cell-bg-grid-selected: rgb(38, 79, 120); + --color-grid-link: #FF6000; + --color-grid-link-hover: #ff8033; + --color-grid-dirty-background: #FFF; + --color-grid-dirty-text: #000; +} + +/* grid styling */ + +.hc-black slick-grid.active .grid .slick-cell.active { + border: solid 1px var(--color-cell-border-active); +} + +.hc-black slick-grid.active .grid .slick-cell.selected { + background-color: var(--color-cell-bg-grid-selected); +} + +.hc-black .grid .slick-cell.selected .grid-cell-value-container.missing-value { + color: var(--color-content) !important; +} + +.hc-black .boxRow.content.horzBox.slickgrid { + border: solid 1px #2D2D30; +} + +/* headers */ +.hc-black .resultsMessageHeader { + background: var(--color-bg-header); + color: var(--color-content); +} + +.hc-black .queryResultsShortCut { + color: grey; +} + +/* scroll bar */ + +.hc-black ::-webkit-scrollbar { + width: 14px; + height: 10px; +} + +.hc-black ::-webkit-scrollbar-thumb { + background-color: rgba(111, 195, 223, 0.3); +} + +.hc-black ::-webkit-scrollbar-thumb:hover { + background-color: rgba(111, 195, 223, 0.8); +} + +.hc-black ::-webkit-scrollbar-thumb:active { + background-color: rgba(111, 195, 223, 0.8); +} + +.hc-black ::-webkit-scrollbar-track { + background: var(--background-color); +} + +.hc-black ::-webkit-scrollbar-corner { + background: transparent; +} + +.hc-black .monaco-workbench input { + color: #000; + background-color: #FFF; +} diff --git a/src/sql/parts/grid/media/slickGrid.css b/src/sql/parts/grid/media/slickGrid.css new file mode 100644 index 0000000000..96f5864e21 --- /dev/null +++ b/src/sql/parts/grid/media/slickGrid.css @@ -0,0 +1,116 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.grid-cell-padding { + padding: .5em .8em .4em +} + +.grid-cell { + border-top-style: none; + color: var(--color-content, #101010); + letter-spacing: .03em; + border-color: var(--border-color-default, #000000); + font-size: .95em; + border-bottom-width: 1px; + border-left-style: none; + border-right-color: #ACACAC; + border-right-style: dotted; + padding: .5em .8em .4em +} + +.grid { + width: 100%; + height: 100% +} + +.grid .slick-header-column { + border-top-style: none; + color: var(--color-content, #101010); + letter-spacing: .03em; + border-color: var(--border-color-default, #000000); + font-size: .95em; + border-bottom-width: 1px; + border-left-style: none; + border-right-color: #ACACAC; + border-right-style: dotted; + padding: .5em .8em .4em; + background-color: var( --color-bg-content-header, #F5F5F5); + border-bottom-color: #ACACAC +} + +.grid .slick-cell { + border-top-style: none; + color: var(--color-content, #101010); + letter-spacing: .03em; + border-color: var(--border-color-default, #000000); + font-size: .95em; + border-bottom-width: 1px; + border-left-style: none; + border-right-color: #ACACAC; + border-right-style: dotted; + padding: .5em .8em .4em; + /* background-color: var(--background-color, white); */ +} + +.grid .slick-cell.selected { + background-color: var(--color-cell-bg-grid-selected, rgb(173, 214, 255)); + color: var(--color-content, #101010); +} + +.grid .slick-cell.selected .grid-cell-value-container.missing-value { + color: black +} + +.grid .slick-cell.editable { + padding: 0 +} + +.grid .slick-cell.editable input { + padding: .5em .8em .4em; + height: 100%; + padding: .4em .65em .1em; + letter-spacing: .03em +} + +.grid .slick-cell.editable input:focus { + outline-offset: 0 +} + +.grid .slick-cell .grid-cell-value-container { + display: block; + white-space: pre; + width: 100%; + overflow-x: hidden; + text-overflow: ellipsis +} + +.grid .slick-cell .grid-cell-value-container.override-cell { + color: black +} + +.grid .slick-cell .grid-cell-value-container.highlighted { + color: black +} + +.grid .slick-cell .grid-cell-value-container.blurred { + color: #ABABAB +} + +.grid .slick-cell .grid-cell-value-container.context { + color: darkblue; + font-style: italic +} + +.grid .slick-cell .grid-cell-value-container.loading-cell { + font-style: italic +} + +.grid .slick-cell .grid-cell-value-container.right-justified { + text-align: right +} + +.grid input.editor-text { + width: 100%; +} \ No newline at end of file diff --git a/src/sql/parts/grid/media/styles.css b/src/sql/parts/grid/media/styles.css new file mode 100644 index 0000000000..2645b883ac --- /dev/null +++ b/src/sql/parts/grid/media/styles.css @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* slick grid */ + +.boxRow.content.horzBox.slickgrid { + margin-bottom: 2px; +} + +.boxRow.content.horzBox.slickgrid:last-child { + margin-bottom: 0px; +} + +.boxRow.content.padded { + padding: 15px +} + +/* icon */ +.gridIcon { + padding: 8px; + background-repeat: no-repeat; + background-position: 0px; +} + +.gridIconContainer { + margin: 5px 5px; +} + +/* messages */ +.resultsMessageTable { + font-weight: inherit; + font-size: inherit; + table-layout: fixed; + border-collapse: collapse; + width: 100%; + user-select: initial; + -webkit-user-select: initial; +} + +.wideResultsMessage { + width: 110px; +} + +#messageTable td { + padding-right: 20px; +} + +#messageTable tbody tr:last-child > td { + padding-bottom: 1em; +} + +.resultsMessageValue { + white-space: pre-line; +} + +/* headers */ +.resultsMessageHeader { + height: auto; + overflow: hidden; + height: 22px; + padding-left: 20px; + line-height: 22px; +} + +.resultsMessageHeader > span { + float: left; + font-size: 11px; + font-weight: 700; + text-transform: uppercase; +} + +.resizableHandle { + font-size: 0.1px; + display: block; + float: right; + position: absolute; + cursor: row-resize; + top: 0; + height: 4px; + width: 100%; + z-index: 1; +} + +#resizeHandle { + font-size: 0.1px; + position: absolute; + cursor: row-resize; + height: 4px; + width: 100%; + z-index: 2; + background-color: var(--color-resize-handle); +} + +.header > span.shortCut { + float: right; + padding-right: 5px; +} + +.queryLink { + cursor: pointer; + text-decoration: underline; +} \ No newline at end of file diff --git a/src/sql/parts/grid/media/uncollapsedArrow.svg b/src/sql/parts/grid/media/uncollapsedArrow.svg new file mode 100644 index 0000000000..80f7a942ab --- /dev/null +++ b/src/sql/parts/grid/media/uncollapsedArrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/uncollapsedArrow_inverse.svg b/src/sql/parts/grid/media/uncollapsedArrow_inverse.svg new file mode 100644 index 0000000000..028931fd66 --- /dev/null +++ b/src/sql/parts/grid/media/uncollapsedArrow_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/grid/media/viewChart.svg b/src/sql/parts/grid/media/viewChart.svg new file mode 100644 index 0000000000..c0401a117b --- /dev/null +++ b/src/sql/parts/grid/media/viewChart.svg @@ -0,0 +1 @@ +view_as_chart_16x16 \ No newline at end of file diff --git a/src/sql/parts/grid/media/viewChart_inverse.svg b/src/sql/parts/grid/media/viewChart_inverse.svg new file mode 100644 index 0000000000..e90ccf30a3 --- /dev/null +++ b/src/sql/parts/grid/media/viewChart_inverse.svg @@ -0,0 +1 @@ +view_as_chart_inverse_16x16 \ No newline at end of file diff --git a/src/sql/parts/grid/services/dataService.ts b/src/sql/parts/grid/services/dataService.ts new file mode 100644 index 0000000000..15f62232e8 --- /dev/null +++ b/src/sql/parts/grid/services/dataService.ts @@ -0,0 +1,211 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +import { Observer } from 'rxjs/Observer'; + +import { ResultSetSubset, EditUpdateCellResult, EditSubsetResult, EditCreateRowResult } from 'data'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { ResultSerializer } from 'sql/parts/query/common/resultSerializer'; +import { ISaveRequest } from 'sql/parts/grid/common/interfaces'; + +import { ISlickRange } from 'angular2-slickgrid'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; + +/** + * DataService handles the interactions between QueryModel and app.component. Thus, it handles + * query running and grid interaction communication for a single URI. + */ +export class DataService { + + public queryEventObserver: Subject; + public gridContentObserver: Subject; + private editQueue: Promise; + + constructor( + private _uri: string, + @IInstantiationService private _instantiationService: IInstantiationService, + @IQueryModelService private _queryModel: IQueryModelService, + @IQueryEditorService private _queryEditorService: IQueryEditorService + ) { + this.queryEventObserver = new Subject(); + this.gridContentObserver = new Subject(); + this.editQueue = Promise.resolve(); + } + + /** + * Get a specified number of rows starting at a specified row for + * the current results set. Used for query results only. + * @param start The starting row or the requested rows + * @param numberOfRows The amount of rows to return + * @param batchId The batch id of the batch you are querying + * @param resultId The id of the result you want to get the rows for + */ + getQueryRows(rowStart: number, numberOfRows: number, batchId: number, resultId: number): Observable { + const self = this; + return Observable.create(function (observer: Observer) { + self._queryModel.getQueryRows(self._uri, rowStart, numberOfRows, batchId, resultId).then(results => { + observer.next(results); + }); + }); + } + + /** + * Get a specified number of rows starting at a specified row. Should only + * be used for edit sessions. + * @param rowStart The row to start retrieving from (inclusive) + * @param numberOfRows The maximum number of rows to return + */ + getEditRows(rowStart: number, numberOfRows: number): Observable { + const self = this; + return Observable.create(function (observer: Observer) { + self._queryModel.getEditRows(self._uri, rowStart, numberOfRows).then(results => { + observer.next(results); + }); + }); + } + + updateCell(rowId: number, columnId: number, newValue: string): Thenable { + const self = this; + self.editQueue = self.editQueue.then(() => { + return self._queryModel.updateCell(self._uri, rowId, columnId, newValue).then(result => { + return result; + }, error => { + // Start our editQueue over due to the rejected promise + self.editQueue = Promise.resolve(); + return Promise.reject(error); + }); + }); + return self.editQueue; + } + + commitEdit(): Thenable { + const self = this; + self.editQueue = self.editQueue.then(() => { + return self._queryModel.commitEdit(self._uri).then(result => { + return result; + }, error => { + // Start our editQueue over due to the rejected promise + self.editQueue = Promise.resolve(); + return Promise.reject(error); + }); + }); + return self.editQueue; + } + + createRow(): Thenable { + const self = this; + self.editQueue = self.editQueue.then(() => { + return self._queryModel.createRow(self._uri).then(result => { + return result; + }, error => { + // Start our editQueue over due to the rejected promise + self.editQueue = Promise.resolve(); + return Promise.reject(error); + }); + }); + return self.editQueue; + } + + deleteRow(rowId: number): Thenable { + const self = this; + self.editQueue = self.editQueue.then(() => { + return self._queryModel.deleteRow(self._uri, rowId).then(result => { + return result; + }, error => { + // Start our editQueue over due to the rejected promise + self.editQueue = Promise.resolve(); + self._queryModel.showCommitError(error.message); + return Promise.reject(error); + }); + }); + return self.editQueue; + } + + revertCell(rowId: number, columnId: number): Thenable { + const self = this; + self.editQueue = self.editQueue.then(() => { + return self._queryModel.revertCell(self._uri, rowId, columnId).then(result => { + return result; + }, error => { + // Start our editQueue over due to the rejected promise + self.editQueue = Promise.resolve(); + return Promise.reject(error); + }); + }); + return self.editQueue; + } + + revertRow(rowId: number): Thenable { + const self = this; + self.editQueue = self.editQueue.then(() => { + return self._queryModel.revertRow(self._uri, rowId).then(result => { + return result; + }, error => { + // Start our editQueue over due to the rejected promise + self.editQueue = Promise.resolve(); + return Promise.reject(error); + }); + }); + return self.editQueue; + } + + /** + * send request to save the selected result set as csv + * @param uri of the calling document + * @param batchId The batch id of the batch with the result to save + * @param resultId The id of the result to save as csv + */ + sendSaveRequest(saveRequest: ISaveRequest): void { + let serializer = this._instantiationService.createInstance(ResultSerializer); + serializer.saveResults(this._uri, saveRequest); + } + + /** + * send request to open content in new editor + * @param content The content to be opened + * @param columnName The column name of the content + */ + openLink(content: string, columnName: string, linkType: string): void { + let serializer = this._instantiationService.createInstance(ResultSerializer); + serializer.openLink(content, columnName, linkType); + } + + /** + * Sends a copy request + * @param selection The selection range to copy + * @param batchId The batch id of the result to copy from + * @param resultId The result id of the result to copy from + * @param includeHeaders [Optional]: Should column headers be included in the copy selection + */ + copyResults(selection: ISlickRange[], batchId: number, resultId: number, includeHeaders?: boolean): void { + this._queryModel.copyResults(this._uri, selection, batchId, resultId, includeHeaders); + } + + /** + * Sends a request to set the selection in the QueryEditor. + */ + setEditorSelection(index: number) { + this._queryModel.setEditorSelection(this._uri, index); + } + + showWarning(message: string): void { + } + + showError(message: string): void { + } + + get config(): Promise<{ [key: string]: any }> { + return undefined; + } + + onAngularLoaded(): void { + this._queryModel.onAngularLoaded(this._uri); + } +} diff --git a/src/sql/parts/grid/services/sharedServices.ts b/src/sql/parts/grid/services/sharedServices.ts new file mode 100644 index 0000000000..42d96d20de --- /dev/null +++ b/src/sql/parts/grid/services/sharedServices.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as Utils from 'sql/parts/connection/common/utils'; + +export class DBCellValue { + displayValue: string; + isNull: boolean; + + public static isDBCellValue(object: any): boolean { + return (object !== undefined && object.displayValue !== undefined && object.isNull !== undefined); + } +} + +/** + * Format xml field into a hyperlink and performs HTML entity encoding + */ +export function hyperLinkFormatter(row: number, cell: any, value: any, columnDef: any, dataContext: any): string { + let cellClasses = 'grid-cell-value-container'; + let valueToDisplay: string = ''; + + if (DBCellValue.isDBCellValue(value)) { + valueToDisplay = 'NULL'; + if (!value.isNull) { + cellClasses += ' xmlLink'; + valueToDisplay = Utils.htmlEntities(value.displayValue); + return `${valueToDisplay}`; + } else { + cellClasses += ' missing-value'; + } + } + return `${valueToDisplay}`; +} + +/** + * Format all text to replace all new lines with spaces and performs HTML entity encoding + */ +export function textFormatter(row: number, cell: any, value: any, columnDef: any, dataContext: any): string { + let cellClasses = 'grid-cell-value-container'; + let valueToDisplay: string = ''; + + if (DBCellValue.isDBCellValue(value)) { + valueToDisplay = 'NULL'; + if (!value.isNull) { + valueToDisplay = Utils.htmlEntities(value.displayValue.replace(/(\r\n|\n|\r)/g, ' ')); + } else { + cellClasses += ' missing-value'; + } + } else if (typeof value === 'string'){ + valueToDisplay = value; + + } + + return `${valueToDisplay}`; +} \ No newline at end of file diff --git a/src/sql/parts/grid/views/editData/editData.component.html b/src/sql/parts/grid/views/editData/editData.component.html new file mode 100644 index 0000000000..4a2c3f9eee --- /dev/null +++ b/src/sql/parts/grid/views/editData/editData.component.html @@ -0,0 +1,34 @@ + + +

+
+
+ + +
+
+
\ No newline at end of file diff --git a/src/sql/parts/grid/views/editData/editData.component.ts b/src/sql/parts/grid/views/editData/editData.component.ts new file mode 100644 index 0000000000..9113b725bb --- /dev/null +++ b/src/sql/parts/grid/views/editData/editData.component.ts @@ -0,0 +1,496 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!sql/parts/grid/media/slickColorTheme'; +import 'vs/css!sql/parts/grid/media/flexbox'; +import 'vs/css!sql/parts/grid/media/styles'; +import 'vs/css!sql/parts/grid/media/slick.grid'; +import 'vs/css!sql/parts/grid/media/slickGrid'; +import 'vs/css!./media/editData'; + +import { ElementRef, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, forwardRef, EventEmitter } from '@angular/core'; +import { IGridDataRow, VirtualizedCollection } from 'angular2-slickgrid'; +import { IGridDataSet } from 'sql/parts/grid/common/interfaces'; +import * as Services from 'sql/parts/grid/services/sharedServices'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { EditDataComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { GridParentComponent } from 'sql/parts/grid/views/gridParentComponent'; +import { EditDataGridActionProvider } from 'sql/parts/grid/views/editData/editDataGridActions'; +import { error } from 'sql/base/common/log'; + +import { clone } from 'vs/base/common/objects'; + +export const EDITDATA_SELECTOR: string = 'editdata-component'; + +@Component({ + selector: EDITDATA_SELECTOR, + host: { '(window:keydown)': 'keyEvent($event)', '(window:gridnav)': 'keyEvent($event)' }, + templateUrl: decodeURI(require.toUrl('sql/parts/grid/views/editData/editData.component.html')) +}) + +export class EditDataComponent extends GridParentComponent implements OnInit, OnDestroy { + // CONSTANTS + private scrollTimeOutTime = 200; + private windowSize = 50; + + // FIELDS + // All datasets + private dataSet: IGridDataSet; + private scrollTimeOut: number; + private messagesAdded = false; + private scrollEnabled = true; + private firstRender = true; + private totalElapsedTimeSpan: number; + private complete = false; + private idMapping: { [row: number]: number } = {}; + + private currentCell: { row: number, column: number } = null; + private rowEditInProgress: boolean = false; + private removingNewRow: boolean = false; + + // Edit Data functions + public onCellEditEnd: (event: { row: number, column: number, newValue: any }) => void; + public onCellEditBegin: (event: { row: number, column: number }) => void; + public onRowEditBegin: (event: { row: number }) => void; + public onRowEditEnd: (event: { row: number }) => void; + public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean; + public onIsColumnEditable: (column: number) => boolean; + public overrideCellFn: (rowNumber, columnId, value?, data?) => string; + public loadDataFunction: (offset: number, count: number) => Promise; + + constructor( + @Inject(forwardRef(() => ElementRef)) el: ElementRef, + @Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef, + @Inject(BOOTSTRAP_SERVICE_ID) bootstrapService: IBootstrapService + ) { + super(el, cd, bootstrapService); + this._el.nativeElement.className = 'slickgridContainer'; + let editDataParameters: EditDataComponentParams = this._bootstrapService.getBootstrapParams(this._el.nativeElement.tagName); + this.dataService = editDataParameters.dataService; + this.actionProvider = new EditDataGridActionProvider(this.dataService, this.onGridSelectAll(), this.onDeleteRow(), this.onRevertRow()); + } + + /** + * Called by Angular when the object is initialized + */ + ngOnInit(): void { + const self = this; + this.baseInit(); + + // Add the subscription to the list of things to be disposed on destroy, or else on a new component init + // may get the "destroyed" object still getting called back. + this.subscribeWithDispose(this.dataService.queryEventObserver, (event) => { + switch (event.type) { + case 'start': + self.handleStart(self, event); + break; + case 'complete': + self.handleComplete(self, event); + break; + case 'message': + self.handleMessage(self, event); + break; + case 'resultSet': + self.handleResultSet(self, event); + break; + case 'editSessionReady': + self.handleEditSessionReady(self, event); + break; + default: + error('Unexpected query event type "' + event.type + '" sent'); + break; + } + self._cd.detectChanges(); + }); + + this.dataService.onAngularLoaded(); + } + + protected initShortcuts(shortcuts: { [name: string]: Function }): void { + // TODO add any Edit Data-specific shortcuts here + } + + public ngOnDestroy(): void { + this.baseDestroy(); + } + + handleStart(self: EditDataComponent, event: any): void { + self.dataSet = undefined; + self.placeHolderDataSets = []; + self.renderedDataSets = self.placeHolderDataSets; + self.totalElapsedTimeSpan = undefined; + self.complete = false; + self.messagesAdded = false; + + // Hooking up edit functions + this.onIsCellEditValid = (row, column, value): boolean => { + // TODO can only run sync code + return true; + }; + + this.onCellEditEnd = (event: { row: number, column: number, newValue: any }): void => { + self.rowEditInProgress = true; + + // Update the cell accordingly + self.dataService.updateCell(this.idMapping[event.row], event.column, event.newValue) + .then( + result => { + self.setCellDirtyState(event.row, event.column + 1, result.cell.isDirty); + self.setRowDirtyState(event.row, result.isRowDirty); + }, + error => { + // On error updating cell, jump back to the cell that was being edited + self.focusCell(event.row, event.column + 1); + } + ); + }; + + this.onCellEditBegin = (event: { row: number, column: number }): void => { }; + + this.onRowEditBegin = (event: { row: number }): void => { }; + + this.onRowEditEnd = (event: { row: number }): void => { }; + + this.onIsColumnEditable = (column: number): boolean => { + let result = false; + // Check that our variables exist + if (column !== undefined && !!this.dataSet && !!this.dataSet.columnDefinitions[column]) { + result = this.dataSet.columnDefinitions[column].isEditable; + } + + // If no column definition exists then the row is not editable + return result; + }; + + this.overrideCellFn = (rowNumber, columnId, value?, data?): string => { + let returnVal = ''; + if (Services.DBCellValue.isDBCellValue(value)) { + returnVal = value.displayValue; + } else if (typeof value === 'string') { + returnVal = value; + } + return returnVal; + }; + + // Setup a function for generating a promise to lookup result subsets + this.loadDataFunction = (offset: number, count: number): Promise => { + return new Promise((resolve, reject) => { + self.dataService.getEditRows(offset, count).subscribe(result => { + let rowIndex = offset; + let gridData: IGridDataRow[] = result.subset.map(row => { + self.idMapping[rowIndex] = row.id; + rowIndex++; + return { values: row.cells, row: row.id }; + }); + + // Append a NULL row to the end of gridData + let newLastRow = gridData.length === 0 ? 0 : (gridData[gridData.length - 1].row + 1); + gridData.push({ values: self.dataSet.columnDefinitions.map(cell => { return { displayValue: 'NULL', isNull: false }; }), row: newLastRow }); + resolve(gridData); + }); + }); + }; + } + + onDeleteRow(): (index: number) => void { + const self = this; + return (index: number): void => { + self.dataService.deleteRow(index).then(() => { + self.dataService.commitEdit().then(() => { + self.removeRow(index, 0); + }); + }); + }; + } + + onRevertRow(): (index: number) => void { + const self = this; + return (index: number): void => { + // Force focus to the first cell (completing any active edit operation) + self.focusCell(index, 0, false); + + // Perform a revert row operation + self.dataService.revertRow(index) + .then(() => { self.dataService.commitEdit(); }) + .then(() => { self.refreshResultsets(); }); + }; + } + + onCellSelect(row: number, column: number): void { + let self = this; + + // TODO: We can skip this step if we're allowing multiple commits + if (this.rowEditInProgress) { + // We're in the middle of a row edit, so we need to commit if we move to a different row + if (row !== this.currentCell.row) { + this.dataService.commitEdit() + .then( + result => { + // Committing was successful. Clean the grid, turn off the row edit flag, then select again + self.setGridClean(); + self.rowEditInProgress = false; + self.onCellSelect(row, column); + }, error => { + // Committing failed, so jump back to the last selected cell + self.focusCell(self.currentCell.row, self.currentCell.column); + } + ); + return; + } + } else { + // We're not in the middle of a row edit, so we can move anywhere + // Checking for removing new row makes sure we don't re-add the new row after we've + // jumped to the first cell of the "new row" + if (this.isNullRow(row) && !this.removingNewRow) { + // We moved into the "new row", add another new row + this.addRow(row, column); + } + } + + // Set the cell we moved to as the current cell + this.currentCell = { row: row, column: column }; + } + + handleComplete(self: EditDataComponent, event: any): void { + self.totalElapsedTimeSpan = event.data; + self.complete = true; + self.messagesAdded = true; + } + + handleEditSessionReady(self, event): void { + // TODO: update when edit session is ready + } + + handleMessage(self: EditDataComponent, event: any): void { + // TODO: what do we do with messages? + } + + handleResultSet(self: EditDataComponent, event: any): void { + // Clone the data before altering it to avoid impacting other subscribers + let resultSet = Object.assign({}, event.data); + + // Add an extra 'new row' + resultSet.rowCount++; + // Precalculate the max height and min height + let maxHeight = this.getMaxHeight(resultSet.rowCount); + let minHeight = this.getMinHeight(resultSet.rowCount); + + // Store the result set from the event + let dataSet: IGridDataSet = { + resized: undefined, + batchId: resultSet.batchId, + resultId: resultSet.id, + totalRows: resultSet.rowCount, + maxHeight: maxHeight, + minHeight: minHeight, + dataRows: new VirtualizedCollection( + self.windowSize, + resultSet.rowCount, + this.loadDataFunction, + index => { return { values: [] }; } + ), + columnDefinitions: resultSet.columnInfo.map((c, i) => { + let isLinked = c.isXml || c.isJson; + let linkType = c.isXml ? 'xml' : 'json'; + return { + id: i.toString(), + name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan' + ? 'XML Showplan' + : c.columnName, + type: self.stringToFieldType('string'), + formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter, + asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined, + isEditable: c.isUpdatable + }; + }) + }; + self.dataSet = dataSet; + + // Create a dataSet to render without rows to reduce DOM size + let undefinedDataSet = clone(dataSet); + undefinedDataSet.columnDefinitions = dataSet.columnDefinitions; + undefinedDataSet.dataRows = undefined; + undefinedDataSet.resized = new EventEmitter(); + self.placeHolderDataSets.push(undefinedDataSet); + self.messagesAdded = true; + self.onScroll(0); + + // Reset selected cell state + this.currentCell = null; + this.rowEditInProgress = false; + this.removingNewRow = false; + + // HACK: unsafe reference to the slickgrid object + // TODO: Reimplement by adding selectCell event to angular2-slickgrid + self._cd.detectChanges(); + let slick: any = self.slickgrids.toArray()[0]; + let grid: Slick.Grid = slick._grid; + + grid.onActiveCellChanged.subscribe((event: Slick.EventData, data: Slick.OnActiveCellChangedEventArgs) => { + self.onCellSelect(data.row, data.cell); + }); + } + + /** + * Handles rendering the results to the DOM that are currently being shown + * and destroying any results that have moved out of view + * @param scrollTop The scrolltop value, if not called by the scroll event should be 0 + */ + onScroll(scrollTop): void { + const self = this; + clearTimeout(self.scrollTimeOut); + this.scrollTimeOut = setTimeout(() => { + self.scrollEnabled = false; + for (let i = 0; i < self.placeHolderDataSets.length; i++) { + self.placeHolderDataSets[i].dataRows = self.dataSet.dataRows; + self.placeHolderDataSets[i].resized.emit(); + } + + + self._cd.detectChanges(); + + if (self.firstRender) { + let setActive = function () { + if (self.firstRender && self.slickgrids.toArray().length > 0) { + self.slickgrids.toArray()[0].setActive(); + self.firstRender = false; + } + }; + + setTimeout(() => { + setActive(); + }); + } + }, self.scrollTimeOutTime); + } + + protected tryHandleKeyEvent(e): boolean { + let handled: boolean = false; + // If the esc key was pressed while in a create session + let currentNewRowIndex = this.dataSet.totalRows - 2; + + if (e.keyCode === jQuery.ui.keyCode.ESCAPE && this.currentCell.row === currentNewRowIndex) { + // revert our last new row + this.removingNewRow = true; + + this.dataService.revertRow(this.idMapping[currentNewRowIndex]) + .then(() => { + this.removeRow(currentNewRowIndex, 0); + this.rowEditInProgress = false; + }); + handled = true; + } + return handled; + } + + // Private Helper Functions //////////////////////////////////////////////////////////////////////////// + + // Checks if input row is our NULL new row + private isNullRow(row: number): boolean { + // Null row is always at index (totalRows - 1) + return (row === this.dataSet.totalRows - 1); + } + + // Adds CSS classes to slickgrid cells to indicate a dirty state + private setCellDirtyState(row: number, column: number, dirtyState: boolean): void { + let slick: any = this.slickgrids.toArray()[0]; + let grid = slick._grid; + if (dirtyState) { + // Change cell color + $(grid.getCellNode(row, column)).addClass('dirtyCell').removeClass('selected'); + } else { + $(grid.getCellNode(row, column)).removeClass('dirtyCell'); + } + } + + // Adds CSS classes to slickgrid rows to indicate a dirty state + private setRowDirtyState(row: number, dirtyState: boolean): void { + let slick: any = this.slickgrids.toArray()[0]; + let grid = slick._grid; + if (dirtyState) { + // Change row header color + $(grid.getCellNode(row, 0)).addClass('dirtyRowHeader'); + } else { + $(grid.getCellNode(row, 0)).removeClass('dirtyRowHeader'); + } + } + + // Sets CSS to clean the entire grid of dirty state cells and rows + private setGridClean(): void { + // Remove dirty classes from the entire table + let allRows = $($('.grid-canvas').children()); + let allCells = $(allRows.children()); + allCells.removeClass('dirtyCell').removeClass('dirtyRowHeader'); + } + + // Adds an extra row to the end of slickgrid (just for rendering purposes) + // Then sets the focused call afterwards + private addRow(row: number, column: number): void { + // Add a new row to the edit session in the tools service + this.dataService.createRow(); + + // Adding an extra row for 'new row' functionality + this.rowEditInProgress = true; + this.dataSet.totalRows++; + this.dataSet.maxHeight = this.getMaxHeight(this.dataSet.totalRows); + this.dataSet.minHeight = this.getMinHeight(this.dataSet.totalRows); + this.dataSet.dataRows = new VirtualizedCollection( + this.windowSize, + this.dataSet.totalRows, + this.loadDataFunction, + index => { return { values: [] }; } + ); + + // Refresh grid + this.onScroll(0); + + // Mark the row as dirty once the scroll has completed + setTimeout(() => { + this.setRowDirtyState(row, true); + }, this.scrollTimeOutTime); + } + + // removes a row from the end of slickgrid (just for rendering purposes) + // Then sets the focused call afterwards + private removeRow(row: number, column: number): void { + // Removing the new row + this.dataSet.totalRows--; + this.dataSet.dataRows = new VirtualizedCollection( + this.windowSize, + this.dataSet.totalRows, + this.loadDataFunction, + index => { return { values: [] }; } + ); + + // refresh results view + this.onScroll(0); + + // Set focus to the row index column of the removed row + setTimeout(() => { + this.focusCell(row, 0); + this.removingNewRow = false; + }, this.scrollTimeOutTime); + } + + private focusCell(row: number, column: number, forceEdit: boolean=true): void { + let slick: any = this.slickgrids.toArray()[0]; + let grid = slick._grid; + grid.gotoCell(row, column, forceEdit); + } + + private getMaxHeight(rowCount: number): any { + return rowCount < this._defaultNumShowingRows + ? ((rowCount + 1) * this._rowHeight) + 10 + : 'inherit'; + } + + private getMinHeight(rowCount: number): any { + return rowCount > this._defaultNumShowingRows + ? (this._defaultNumShowingRows + 1) * this._rowHeight + 10 + : this.getMaxHeight(rowCount); + } +} diff --git a/src/sql/parts/grid/views/editData/editData.module.ts b/src/sql/parts/grid/views/editData/editData.module.ts new file mode 100644 index 0000000000..20bcd7bfde --- /dev/null +++ b/src/sql/parts/grid/views/editData/editData.module.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import { ApplicationRef, ComponentFactoryResolver, NgModule, Inject, forwardRef } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { BrowserModule } from '@angular/platform-browser'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; + +import { EditDataComponent, EDITDATA_SELECTOR } from 'sql/parts/grid/views/editData/editData.component'; +import { SlickGrid } from 'angular2-slickgrid'; + +@NgModule({ + + imports: [ + CommonModule, + BrowserModule + ], + + declarations: [ + EditDataComponent, + SlickGrid + ], + + entryComponents: [ + EditDataComponent + ] +}) +export class EditDataModule { + + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { + } + + ngDoBootstrap(appRef: ApplicationRef) { + const factory = this._resolver.resolveComponentFactory(EditDataComponent); + const uniqueSelector: string = this._bootstrapService.getUniqueSelector(EDITDATA_SELECTOR); + (factory).factory.selector = uniqueSelector; + appRef.bootstrap(factory); + } +} diff --git a/src/sql/parts/grid/views/editData/editDataGridActions.ts b/src/sql/parts/grid/views/editData/editDataGridActions.ts new file mode 100644 index 0000000000..aa43ca0e3a --- /dev/null +++ b/src/sql/parts/grid/views/editData/editDataGridActions.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IGridInfo } from 'sql/parts/grid/common/interfaces'; +import { DataService } from 'sql/parts/grid/services/dataService'; +import { GridActionProvider } from 'sql/parts/grid/views/gridActions'; +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction, Action } from 'vs/base/common/actions'; + +export class EditDataGridActionProvider extends GridActionProvider { + + constructor(dataService: DataService, selectAllCallback: (index: number) => void, + private _deleteRowCallback: (index: number) => void, + private _revertRowCallback: (index: number) => void) { + super(dataService, selectAllCallback); + } + /** + * Return actions given a click on an edit data grid + */ + public getGridActions(): TPromise { + let actions: IAction[] = []; + actions.push(new DeleteRowAction(DeleteRowAction.ID, DeleteRowAction.LABEL, this._deleteRowCallback)); + actions.push(new RevertRowAction(RevertRowAction.ID, RevertRowAction.LABEL, this._revertRowCallback)); + + return TPromise.as(actions); + } +} + +export class DeleteRowAction extends Action { + public static ID = 'grid.deleteRow'; + public static LABEL = localize('deleteRow', 'Delete Row'); + + constructor( + id: string, + label: string, + private callback: (index: number) => void + ) { + super(id, label); + } + + public run(gridInfo: IGridInfo): TPromise { + this.callback(gridInfo.rowIndex); + return TPromise.as(true); + } +} + +export class RevertRowAction extends Action { + public static ID = 'grid.revertRow'; + public static LABEL = localize('revertRow', 'Revert Row'); + + constructor( + id: string, + label: string, + private callback: (index: number) => void + ) { + super(id, label); + } + + public run(gridInfo: IGridInfo): TPromise { + this.callback(gridInfo.rowIndex); + return TPromise.as(true); + } +} diff --git a/src/sql/parts/grid/views/editData/media/editData.css b/src/sql/parts/grid/views/editData/media/editData.css new file mode 100644 index 0000000000..9f590f2bb4 --- /dev/null +++ b/src/sql/parts/grid/views/editData/media/editData.css @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.editdata-component * { + box-sizing: border-box; +} \ No newline at end of file diff --git a/src/sql/parts/grid/views/gridActions.ts b/src/sql/parts/grid/views/gridActions.ts new file mode 100644 index 0000000000..d19e7c3ebb --- /dev/null +++ b/src/sql/parts/grid/views/gridActions.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IGridInfo, IRange, SaveFormat } from 'sql/parts/grid/common/interfaces'; +import { DataService } from 'sql/parts/grid/services/dataService'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction, Action } from 'vs/base/common/actions'; + +export const GRID_SAVECSV_ID = 'grid.saveAsCsv'; +export const GRID_SAVEJSON_ID = 'grid.saveAsJson'; +export const GRID_SAVEEXCEL_ID = 'grid.saveAsExcel'; +export const GRID_COPY_ID = 'grid.copySelection'; +export const GRID_COPYWITHHEADERS_ID = 'grid.copyWithHeaders'; +export const GRID_SELECTALL_ID = 'grid.selectAll'; +export const MESSAGES_SELECTALL_ID = 'grid.messages.selectAll'; +export const MESSAGES_COPY_ID = 'grid.messages.copy'; +export const TOGGLERESULTS_ID = 'grid.toggleResultPane'; +export const TOGGLEMESSAGES_ID = 'grid.toggleMessagePane'; + +export class GridActionProvider { + + constructor(protected _dataService: DataService, protected _selectAllCallback: (index: number) => void) { + + } + + /** + * Return actions given a click on a grid + */ + public getGridActions(): TPromise { + let actions: IAction[] = []; + actions.push(new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveFormat.CSV, this._dataService)); + actions.push(new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveFormat.JSON, this._dataService)); + actions.push(new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveFormat.EXCEL, this._dataService)); + actions.push(new SelectAllGridAction(SelectAllGridAction.ID, SelectAllGridAction.LABEL, this._selectAllCallback)); + actions.push(new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false, this._dataService)); + actions.push(new CopyResultAction(CopyResultAction.COPYWITHHEADERS_ID, CopyResultAction.COPYWITHHEADERS_LABEL, true, this._dataService)); + + return TPromise.as(actions); + } + + /** + * Return actions given a click on a messages pane + */ + public getMessagesActions(dataService: DataService, selectAllCallback: () => void): TPromise { + let actions: IAction[] = []; + actions.push(new CopyMessagesAction(CopyMessagesAction.ID, CopyMessagesAction.LABEL)); + actions.push(new SelectAllMessagesAction(SelectAllMessagesAction.ID, SelectAllMessagesAction.LABEL, selectAllCallback)); + return TPromise.as(actions); + } +} + +export class SaveResultAction extends Action { + public static SAVECSV_ID = GRID_SAVECSV_ID; + public static SAVECSV_LABEL = localize('saveAsCsv', 'Save As CSV'); + + public static SAVEJSON_ID = GRID_SAVEJSON_ID; + public static SAVEJSON_LABEL = localize('saveAsJson', 'Save As JSON'); + + public static SAVEEXCEL_ID = GRID_SAVEEXCEL_ID; + public static SAVEEXCEL_LABEL = localize('saveAsExcel', 'Save As Excel'); + + constructor( + id: string, + label: string, + private format: SaveFormat, + private dataService: DataService + ) { + super(id, label); + } + + public run(gridInfo: IGridInfo): TPromise { + this.dataService.sendSaveRequest({ + batchIndex: gridInfo.batchIndex, + resultSetNumber: gridInfo.resultSetNumber, + selection: gridInfo.selection, + format: this.format + }); + return TPromise.as(true); + } +} + +export class CopyResultAction extends Action { + public static COPY_ID = GRID_COPY_ID; + public static COPY_LABEL = localize('copySelection', 'Copy'); + + public static COPYWITHHEADERS_ID = GRID_COPYWITHHEADERS_ID; + public static COPYWITHHEADERS_LABEL = localize('copyWithHeaders', 'Copy With Headers'); + + constructor( + id: string, + label: string, + private copyHeader: boolean, + private dataService: DataService + ) { + super(id, label); + } + + public run(gridInfo: IGridInfo): TPromise { + this.dataService.copyResults(gridInfo.selection, gridInfo.batchIndex, gridInfo.resultSetNumber, this.copyHeader); + return TPromise.as(true); + } +} + +export class SelectAllGridAction extends Action { + public static ID = GRID_SELECTALL_ID; + public static LABEL = localize('selectAll', 'Select All'); + + constructor( + id: string, + label: string, + private selectAllCallback: (index: number) => void + ) { + super(id, label); + } + + public run(gridInfo: IGridInfo): TPromise { + this.selectAllCallback(gridInfo.gridIndex); + return TPromise.as(true); + } +} + +export class SelectAllMessagesAction extends Action { + public static ID = MESSAGES_SELECTALL_ID; + public static LABEL = localize('selectAll', 'Select All'); + + constructor( + id: string, + label: string, + private selectAllCallback: () => void + ) { + super(id, label); + } + + public run(): TPromise { + this.selectAllCallback(); + return TPromise.as(true); + } +} + +export class CopyMessagesAction extends Action { + public static ID = MESSAGES_COPY_ID; + public static LABEL = localize('copyMessages', 'Copy'); + + constructor( + id: string, + label: string + ) { + super(id, label); + } + + public run(selectedRange: IRange): TPromise { + let selectedText = selectedRange.text(); + WorkbenchUtils.executeCopy(selectedText); + return TPromise.as(true); + } +} diff --git a/src/sql/parts/grid/views/gridCommands.ts b/src/sql/parts/grid/views/gridCommands.ts new file mode 100644 index 0000000000..18a6a2d3f2 --- /dev/null +++ b/src/sql/parts/grid/views/gridCommands.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { QueryEditor } from 'sql/parts/query/editor/queryEditor'; +import { EditDataEditor } from 'sql/parts/editData/editor/editDataEditor'; + +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; + +function runActionOnActiveResultsEditor (accessor: ServicesAccessor, eventName: string): void { + let editorService = accessor.get(IWorkbenchEditorService); + const candidates = [editorService.getActiveEditor(), ...editorService.getVisibleEditors()].filter(e => { + if (e) { + let id = e.getId(); + if (id === QueryEditor.ID || id === EditDataEditor.ID) { + // This is a query or edit data editor + return true; + } + } + return false; + }); + + if (candidates.length > 0) { + let queryModelService: IQueryModelService = accessor.get(IQueryModelService); + let uri = ( candidates[0].input).uri; + queryModelService.sendGridContentEvent(uri, eventName); + } +} + +export const copySelection = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.CopySelection); +}; + +export const copyMessagesSelection = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.CopyMessagesSelection); +}; + +export const copyWithHeaders = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.CopyWithHeaders); +}; + +export const toggleMessagePane = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.ToggleMessagePane); +}; + +export const toggleResultsPane = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.ToggleResultPane); +}; + +export const saveAsCsv = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.SaveAsCsv); +}; + +export const saveAsJson = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.SaveAsJSON); +}; + +export const saveAsExcel = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.SaveAsExcel); +}; + +export const selectAll = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.SelectAll); +}; + +export const selectAllMessages = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.SelectAllMessages); +}; + + diff --git a/src/sql/parts/grid/views/gridParentComponent.ts b/src/sql/parts/grid/views/gridParentComponent.ts new file mode 100644 index 0000000000..f22288140a --- /dev/null +++ b/src/sql/parts/grid/views/gridParentComponent.ts @@ -0,0 +1,554 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!sql/parts/grid/media/slickColorTheme'; +import 'vs/css!sql/parts/grid/media/flexbox'; +import 'vs/css!sql/parts/grid/media/styles'; +import 'vs/css!sql/parts/grid/media/slick.grid'; +import 'vs/css!sql/parts/grid/media/slickGrid'; + +import { Subscription, Subject } from 'rxjs/Rx'; +import { ElementRef, QueryList, ChangeDetectorRef, ViewChildren } from '@angular/core'; +import { IGridDataRow, ISlickRange, SlickGrid, FieldType } from 'angular2-slickgrid'; +import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils'; +import * as Constants from 'sql/parts/query/common/constants'; +import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants'; +import { IGridInfo, IRange, IGridDataSet, SaveFormat } from 'sql/parts/grid/common/interfaces'; +import * as Utils from 'sql/parts/connection/common/utils'; +import { DataService } from 'sql/parts/grid/services/dataService'; +import * as actions from 'sql/parts/grid/views/gridActions'; +import * as Services from 'sql/parts/grid/services/sharedServices'; +import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents'; +import { ResultsVisibleContext, ResultsGridFocussedContext, ResultsMessagesFocussedContext } from 'sql/parts/query/common/queryContext'; +import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; +import * as rangy from 'sql/base/node/rangy'; +import { error } from 'sql/base/common/log'; + +import { IAction } from 'vs/base/common/actions'; +import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin'; +import { DragCellSelectionModel } from 'sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin'; + +export abstract class GridParentComponent { + // CONSTANTS + // tslint:disable:no-unused-variable + protected get selectionModel(): DragCellSelectionModel { + return new DragCellSelectionModel(); + } + protected get slickgridPlugins(): Array { + return [ + new AutoColumnSize({}) + ]; + } + protected _rowHeight = 29; + protected _defaultNumShowingRows = 8; + protected Constants = Constants; + protected LocalizedConstants = LocalizedConstants; + protected Utils = Utils; + // tslint:disable-next-line:no-unused-variable + protected startString = new Date().toLocaleTimeString(); + + protected shortcutfunc: { [name: string]: Function }; + + // tslint:enable + + // FIELDS + // Service for interaction with the IQueryModel + protected dataService: DataService; + protected keybindingService: IKeybindingService; + protected scopedContextKeyService: IContextKeyService; + protected contextMenuService: IContextMenuService; + protected actionProvider: actions.GridActionProvider; + + protected toDispose: IDisposable[]; + + + // Context keys to set when keybindings are available + private resultsVisibleContextKey: IContextKey; + private gridFocussedContextKey: IContextKey; + private messagesFocussedContextKey: IContextKey; + + // All datasets + // Place holder data sets to buffer between data sets and rendered data sets + protected placeHolderDataSets: IGridDataSet[] = []; + // Datasets currently being rendered on the DOM + protected renderedDataSets: IGridDataSet[] = this.placeHolderDataSets; + protected resultActive = true; + protected _messageActive = true; + protected activeGrid = 0; + + @ViewChildren('slickgrid') slickgrids: QueryList; + + // Edit Data functions + public onCellEditEnd: (event: { row: number, column: number, newValue: any }) => void; + public onCellEditBegin: (event: { row: number, column: number }) => void; + public onRowEditBegin: (event: { row: number }) => void; + public onRowEditEnd: (event: { row: number }) => void; + public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean; + public onIsColumnEditable: (column: number) => boolean; + public overrideCellFn: (rowNumber, columnId, value?, data?) => string; + public loadDataFunction: (offset: number, count: number) => Promise; + + set messageActive(input: boolean) { + this._messageActive = input; + if (this.resultActive) { + this.resizeGrids(); + } + } + + get messageActive(): boolean { + return this._messageActive; + } + + constructor( + protected _el: ElementRef, + protected _cd: ChangeDetectorRef, + protected _bootstrapService: IBootstrapService + ) { + this.toDispose = []; + } + + protected baseInit(): void { + const self = this; + this.initShortcutsBase(); + if (this._bootstrapService.configurationService) { + let sqlConfig = this._bootstrapService.configurationService.getConfiguration('sql'); + if (sqlConfig) { + this._messageActive = sqlConfig['messagesDefaultOpen']; + } + } + this.subscribeWithDispose(this.dataService.gridContentObserver, (type) => { + switch (type) { + case GridContentEvents.RefreshContents: + self.refreshResultsets(); + break; + case GridContentEvents.ResizeContents: + self.resizeGrids(); + break; + case GridContentEvents.CopySelection: + self.copySelection(); + break; + case GridContentEvents.CopyWithHeaders: + self.copyWithHeaders(); + break; + case GridContentEvents.CopyMessagesSelection: + self.copyMessagesSelection(); + break; + case GridContentEvents.ToggleResultPane: + self.toggleResultPane(); + break; + case GridContentEvents.ToggleMessagePane: + self.toggleMessagePane(); + break; + case GridContentEvents.SelectAll: + self.onSelectAllForActiveGrid(); + break; + case GridContentEvents.SelectAllMessages: + self.selectAllMessages(); + break; + case GridContentEvents.SaveAsCsv: + self.sendSaveRequest(SaveFormat.CSV); + break; + case GridContentEvents.SaveAsJSON: + self.sendSaveRequest(SaveFormat.JSON); + break; + case GridContentEvents.SaveAsExcel: + self.sendSaveRequest(SaveFormat.EXCEL); + break; + default: + error('Unexpected grid content event type "' + type + '" sent'); + break; + } + }); + + this.contextMenuService = this._bootstrapService.contextMenuService; + this.keybindingService = this._bootstrapService.keybindingService; + + this.bindKeys(this._bootstrapService.contextKeyService); + } + + /* + * Add the subscription to the list of things to be disposed on destroy, or else on a new component init + * may get the "destroyed" object still getting called back. + */ + protected subscribeWithDispose(subject: Subject, event: (value: any) => void): void { + let sub: Subscription = subject.subscribe(event); + this.toDispose.push(toDisposableSubscription(sub)); + } + + private bindKeys(contextKeyService: IContextKeyService): void { + if (contextKeyService) { + let gridContextKeyService = this._bootstrapService.contextKeyService.createScoped(this._el.nativeElement); + this.toDispose.push(gridContextKeyService); + this.resultsVisibleContextKey = ResultsVisibleContext.bindTo(gridContextKeyService); + this.resultsVisibleContextKey.set(true); + + this.gridFocussedContextKey = ResultsGridFocussedContext.bindTo(gridContextKeyService); + this.messagesFocussedContextKey = ResultsMessagesFocussedContext.bindTo(gridContextKeyService); + } + } + + protected baseDestroy(): void { + this.toDispose = dispose(this.toDispose); + } + + private toggleResultPane(): void { + this.resultActive = !this.resultActive; + } + + private toggleMessagePane(): void { + this.messageActive = !this.messageActive; + } + + protected onGridFocus() { + this.gridFocussedContextKey.set(true); + } + + protected onGridFocusout() { + this.gridFocussedContextKey.set(false); + } + + protected onMessagesFocus() { + this.messagesFocussedContextKey.set(true); + } + + protected onMessagesFocusout() { + this.messagesFocussedContextKey.set(false); + } + + private copySelection(): void { + let messageText = this.getMessageText(); + if (messageText.length > 0) { + WorkbenchUtils.executeCopy(messageText); + } else { + let activeGrid = this.activeGrid; + let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges(); + this.dataService.copyResults(selection, this.renderedDataSets[activeGrid].batchId, this.renderedDataSets[activeGrid].resultId); + } + } + + private copyWithHeaders(): void { + let activeGrid = this.activeGrid; + let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges(); + this.dataService.copyResults(selection, this.renderedDataSets[activeGrid].batchId, + this.renderedDataSets[activeGrid].resultId, true); + } + + private copyMessagesSelection(): void { + let messageText = this.getMessageText(); + if (messageText.length === 0) { + // Since we know we're specifically copying messages, do a select all if nothing is selected + this.selectAllMessages(); + messageText = this.getMessageText(); + } + if (messageText.length > 0) { + WorkbenchUtils.executeCopy(messageText); + } + } + + private getMessageText(): string { + let range: IRange = this.getSelectedRangeUnderMessages(); + return range ? range.text() : ''; + } + + private initShortcutsBase(): void { + let shortcuts = { + 'ToggleResultPane': () => { + this.toggleResultPane(); + }, + 'ToggleMessagePane': () => { + this.toggleMessagePane(); + }, + 'CopySelection': () => { + this.copySelection(); + }, + 'CopyWithHeaders': () => { + this.copyWithHeaders(); + }, + 'SelectAll': () => { + this.onSelectAllForActiveGrid(); + }, + 'SaveAsCSV': () => { + this.sendSaveRequest(SaveFormat.CSV); + }, + 'SaveAsJSON': () => { + this.sendSaveRequest(SaveFormat.JSON); + }, + 'SaveAsExcel': () => { + this.sendSaveRequest(SaveFormat.EXCEL); + } + }; + + this.initShortcuts(shortcuts); + this.shortcutfunc = shortcuts; + } + + protected abstract initShortcuts(shortcuts: { [name: string]: Function }): void; + + /** + * Send save result set request to service + */ + handleContextClick(event: { type: string, batchId: number, resultId: number, index: number, selection: ISlickRange[] }): void { + switch (event.type) { + case 'savecsv': + this.dataService.sendSaveRequest({ batchIndex: event.batchId, resultSetNumber: event.resultId, format: SaveFormat.CSV, selection: event.selection }); + break; + case 'savejson': + this.dataService.sendSaveRequest({ batchIndex: event.batchId, resultSetNumber: event.resultId, format: SaveFormat.JSON, selection: event.selection }); + break; + case 'saveexcel': + this.dataService.sendSaveRequest({ batchIndex: event.batchId, resultSetNumber: event.resultId, format: SaveFormat.EXCEL, selection: event.selection }); + break; + case 'selectall': + this.activeGrid = event.index; + this.onSelectAllForActiveGrid(); + break; + case 'copySelection': + this.dataService.copyResults(event.selection, event.batchId, event.resultId); + break; + case 'copyWithHeaders': + this.dataService.copyResults(event.selection, event.batchId, event.resultId, true); + break; + default: + break; + } + } + + private sendSaveRequest(format: SaveFormat) { + let activeGrid = this.activeGrid; + let batchId = this.renderedDataSets[activeGrid].batchId; + let resultId = this.renderedDataSets[activeGrid].resultId; + let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges(); + this.dataService.sendSaveRequest({ batchIndex: batchId, resultSetNumber: resultId, format: format, selection: selection }); + } + + protected _keybindingFor(action: IAction): ResolvedKeybinding { + var [kb] = this.keybindingService.lookupKeybindings(action.id); + return kb; + } + + openContextMenu(event, batchId, resultId, index): void { + let selection = this.slickgrids.toArray()[index].getSelectedRanges(); + + let slick: any = this.slickgrids.toArray()[index]; + let grid = slick._grid; + let rowIndex = grid.getCellFromEvent(event).row; + + let actionContext: IGridInfo = { + batchIndex: batchId, + resultSetNumber: resultId, + selection: selection, + gridIndex: index, + rowIndex: rowIndex + }; + + let anchor = { x: event.pageX + 1, y: event.pageY }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this.actionProvider.getGridActions(), + getKeyBinding: (action) => this._keybindingFor(action), + onHide: (wasCancelled?: boolean) => { + }, + getActionsContext: () => (actionContext) + }); + } + + /** + * Returns a function that selects all elements of a grid. This needs to + * return a function in order to capture the scope for this component + * @private + * @returns {(gridIndex: number) => void} + * + * @memberOf QueryComponent + */ + protected onGridSelectAll(): (gridIndex: number) => void { + let self = this; + return (gridIndex: number) => { + self.activeGrid = gridIndex; + self.slickgrids.toArray()[this.activeGrid].selection = true; + }; + } + + private onSelectAllForActiveGrid(): void { + if (this.activeGrid >= 0 && this.slickgrids.length > this.activeGrid) { + this.slickgrids.toArray()[this.activeGrid].selection = true; + } + } + + /** + * Used to convert the string to a enum compatible with SlickGrid + */ + protected stringToFieldType(input: string): FieldType { + let fieldtype: FieldType; + switch (input) { + case 'string': + fieldtype = FieldType.String; + break; + default: + fieldtype = FieldType.String; + break; + } + return fieldtype; + } + + /** + * Makes a resultset take up the full result height if this is not already true + * Otherwise rerenders the result sets from default + */ + magnify(index: number): void { + const self = this; + if (this.renderedDataSets.length > 1) { + this.renderedDataSets = [this.placeHolderDataSets[index]]; + } else { + this.renderedDataSets = this.placeHolderDataSets; + this.onScroll(0); + } + setTimeout(() => { + self.resizeGrids(); + self.slickgrids.toArray()[0].setActive(); + self._cd.detectChanges(); + }); + } + + abstract onScroll(scrollTop): void; + + protected getResultsElement(): any { + return this._el.nativeElement.querySelector('#results'); + } + protected getMessagesElement(): any { + return this._el.nativeElement.querySelector('#messages'); + } + /** + * Force angular to re-render the results grids. Calling this upon unhide (upon focus) fixes UI + * glitches that occur when a QueryRestulsEditor is hidden then unhidden while it is running a query. + */ + refreshResultsets(): void { + let tempRenderedDataSets = this.renderedDataSets; + this.renderedDataSets = []; + this._cd.detectChanges(); + this.renderedDataSets = tempRenderedDataSets; + this._cd.detectChanges(); + } + + getSelectedRangeUnderMessages(): IRange { + let selectedRange: IRange = undefined; + let msgEl = this.getMessagesElement(); + if (msgEl) { + selectedRange = this.getSelectedRangeWithin(msgEl); + } + return selectedRange; + } + + getSelectedRangeWithin(el): IRange { + let selectedRange = undefined; + let sel = rangy.getSelection(); + let elRange = rangy.createRange(); + elRange.selectNodeContents(el); + if (sel.rangeCount) { + selectedRange = sel.getRangeAt(0).intersection(elRange); + } + elRange.detach(); + return selectedRange; + } + + selectAllMessages(): void { + let msgEl = this._el.nativeElement.querySelector('#messages'); + this.selectElementContents(msgEl); + } + + selectElementContents(el): void { + let range = rangy.createRange(); + range.selectNodeContents(el); + let sel = rangy.getSelection(); + sel.setSingleRange(range); + } + + /** + * Add handler for clicking on xml link + */ + xmlLinkHandler = (cellRef: string, row: number, dataContext: JSON, colDef: any) => { + const self = this; + self.handleLink(cellRef, row, dataContext, colDef, 'xml'); + } + + /** + * Add handler for clicking on json link + */ + jsonLinkHandler = (cellRef: string, row: number, dataContext: JSON, colDef: any) => { + const self = this; + self.handleLink(cellRef, row, dataContext, colDef, 'json'); + } + + private handleLink(cellRef: string, row: number, dataContext: JSON, colDef: any, linkType: string): void { + const self = this; + let value = self.getCellValueString(dataContext, colDef); + $(cellRef).children('.xmlLink').click(function (): void { + self.dataService.openLink(value, colDef.name, linkType); + }); + } + + private getCellValueString(dataContext: JSON, colDef: any): string { + let returnVal = ''; + let value = dataContext[colDef.field]; + if (Services.DBCellValue.isDBCellValue(value)) { + returnVal = value.displayValue; + } else if (typeof value === 'string') { + returnVal = value; + } + return returnVal; + } + + /** + * Return asyncPostRender handler based on type + */ + public linkHandler(type: string): Function { + if (type === 'xml') { + return this.xmlLinkHandler; + } else { // default to JSON handler + return this.jsonLinkHandler; + } + } + + keyEvent(e: KeyboardEvent): void { + let self = this; + let handled = self.tryHandleKeyEvent(e); + if (handled) { + e.preventDefault(); + e.stopPropagation(); + } + // Else assume that keybinding service handles routing this to a command + } + + /** + * Called by keyEvent method to give child classes a chance to + * handle key events. + * + * @protected + * @abstract + * @param {any} e + * @returns {boolean} + * + * @memberOf GridParentComponent + */ + protected abstract tryHandleKeyEvent(e): boolean; + + resizeGrids(): void { + const self = this; + setTimeout(() => { + for (let grid of self.renderedDataSets) { + grid.resized.emit(); + } + }); + } + + // Private Helper Functions //////////////////////////////////////////////////////////////////////////// +} diff --git a/src/sql/parts/grid/views/query/chartViewer.component.html b/src/sql/parts/grid/views/query/chartViewer.component.html new file mode 100644 index 0000000000..8a7ae34779 --- /dev/null +++ b/src/sql/parts/grid/views/query/chartViewer.component.html @@ -0,0 +1,57 @@ + +
+
+
+
+ +
+
+
+
+
{{chartTypeLabel}}
+
+
+
+
{{dataTypeLabel}}
+
+
+ {{numberLabel}} +
+
+ {{pointLabel}} +
+
+
+
+
{{dataDirectionLabel}}
+
+
+ {{verticalLabel}} +
+
+ {{horizontalLabel}} +
+
+
+
+
+
+ +
+
+
+ +
{{legendLabel}}
+
+ + +
+
+
+
\ No newline at end of file diff --git a/src/sql/parts/grid/views/query/chartViewer.component.ts b/src/sql/parts/grid/views/query/chartViewer.component.ts new file mode 100644 index 0000000000..d97e46ebba --- /dev/null +++ b/src/sql/parts/grid/views/query/chartViewer.component.ts @@ -0,0 +1,347 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/parts/grid/views/query/chartViewer'; + +import { + Component, Inject, ViewContainerRef, forwardRef, OnInit, + ComponentFactoryResolver, ViewChild, OnDestroy, Input, ElementRef, ChangeDetectorRef +} from '@angular/core'; +import { NgGridItemConfig } from 'angular2-grid'; + +import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; +import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox'; +import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive'; +import { IGridDataSet } from 'sql/parts/grid/common/interfaces'; +import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper'; +import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry'; +import { QueryEditor } from 'sql/parts/query/editor/queryEditor'; +import { DataType, ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component'; +import * as PathUtilities from 'sql/common/pathUtilities'; +import { IChartViewActionContext, CopyAction, CreateInsightAction, SaveImageAction } from 'sql/parts/grid/views/query/chartViewerActions'; + +/* Insights */ +import { + ChartInsight, DataDirection, LegendPosition +} from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Builder } from 'vs/base/browser/builder'; +import { attachSelectBoxStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; +import Severity from 'vs/base/common/severity'; +import URI from 'vs/base/common/uri'; +import * as nls from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { mixin } from 'vs/base/common/objects'; +import * as paths from 'vs/base/common/paths'; +import * as pfs from 'vs/base/node/pfs'; + +const insightRegistry = Registry.as(Extensions.InsightContribution); + +@Component({ + selector: 'chart-viewer', + templateUrl: decodeURI(require.toUrl('sql/parts/grid/views/query/chartViewer.component.html')) +}) +export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewActionContext { + public legendOptions: string[]; + private chartTypesSelectBox: SelectBox; + private legendSelectBox: SelectBox; + private labelFirstColumnCheckBox: Checkbox; + private columnsAsLabelsCheckBox: Checkbox; + + /* UI */ + /* tslint:disable:no-unused-variable */ + private chartTypeLabel: string = nls.localize('chartTypeLabel', 'Chart Type'); + private dataDirectionLabel: string = nls.localize('dataDirectionLabel', 'Data Direction'); + private verticalLabel: string = nls.localize('verticalLabel', 'Vertical'); + private horizontalLabel: string = nls.localize('horizontalLabel', 'Horizontal'); + private dataTypeLabel: string = nls.localize('dataTypeLabel', 'Data Type'); + private numberLabel: string = nls.localize('numberLabel', 'Number'); + private pointLabel: string = nls.localize('pointLabel', 'Point'); + private labelFirstColumnLabel: string = nls.localize('labelFirstColumnLabel', 'Use First Column as row label?'); + private columnsAsLabelsLabel: string = nls.localize('columnsAsLabelsLabel', 'Use Column names as labels?'); + private legendLabel: string = nls.localize('legendLabel', 'Legend Position'); + private chartNotFoundError: string = nls.localize('chartNotFound', 'Could not find chart to save'); + /* tslint:enable:no-unused-variable */ + + private _actionBar: Taskbar; + private _createInsightAction: CreateInsightAction; + private _copyAction: CopyAction; + private _saveAction: SaveImageAction; + private _chartConfig: ILineConfig; + private _disposables: Array = []; + private _dataSet: IGridDataSet; + private _executeResult: IInsightData; + private _chartComponent: ChartInsight; + + @ViewChild(ComponentHostDirective) private componentHost: ComponentHostDirective; + @ViewChild('taskbarContainer', { read: ElementRef }) private taskbarContainer; + @ViewChild('chartTypesContainer', { read: ElementRef }) private chartTypesElement; + @ViewChild('legendContainer', { read: ElementRef }) private legendElement; + @ViewChild('labelFirstColumnContainer', { read: ElementRef }) private labelFirstColumnElement; + @ViewChild('columnsAsLabelsContainer', { read: ElementRef }) private columnsAsLabelsElement; + + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver, + @Inject(forwardRef(() => ViewContainerRef)) private _viewContainerRef: ViewContainerRef, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService, + @Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef + ) { + } + + ngOnInit() { + this._chartConfig = { + dataDirection: 'vertical', + dataType: 'number', + legendPosition: 'none', + labelFirstColumn: false + }; + this.legendOptions = Object.values(LegendPosition); + this.initializeUI(); + } + + private initializeUI() { + // Initialize the taskbar + this._initActionBar(); + + // Init chart type dropdown + this.chartTypesSelectBox = new SelectBox(insightRegistry.getAllIds(), 'horizontalBar'); + this.chartTypesSelectBox.render(this.chartTypesElement.nativeElement); + this.chartTypesSelectBox.onDidSelect(selected => this.onChartChanged()); + this._disposables.push(attachSelectBoxStyler(this.chartTypesSelectBox, this._bootstrapService.themeService)); + + // Init label first column checkbox + // Note: must use 'self' for callback + this.labelFirstColumnCheckBox = DialogHelper.createCheckBox(new Builder(this.labelFirstColumnElement.nativeElement), + this.labelFirstColumnLabel, 'chartView-checkbox', false, () => this.onLabelFirstColumnChanged()); + this._disposables.push(attachCheckboxStyler(this.labelFirstColumnCheckBox, this._bootstrapService.themeService)); + + // Init label first column checkbox + // Note: must use 'self' for callback + this.columnsAsLabelsCheckBox = DialogHelper.createCheckBox(new Builder(this.columnsAsLabelsElement.nativeElement), + this.columnsAsLabelsLabel, 'chartView-checkbox', false, () => this.columnsAsLabelsChanged()); + this._disposables.push(attachCheckboxStyler(this.columnsAsLabelsCheckBox, this._bootstrapService.themeService)); + + // Init legend dropdown + this.legendSelectBox = new SelectBox(this.legendOptions, this._chartConfig.legendPosition); + this.legendSelectBox.render(this.legendElement.nativeElement); + this.legendSelectBox.onDidSelect(selected => this.onLegendChanged()); + this._disposables.push(attachSelectBoxStyler(this.legendSelectBox, this._bootstrapService.themeService)); + } + + private _initActionBar() { + this._createInsightAction = this._bootstrapService.instantiationService.createInstance(CreateInsightAction); + this._copyAction = this._bootstrapService.instantiationService.createInstance(CopyAction); + this._saveAction = this._bootstrapService.instantiationService.createInstance(SaveImageAction); + + let taskbar = this.taskbarContainer.nativeElement; + this._actionBar = new Taskbar(taskbar, this._bootstrapService.contextMenuService); + this._actionBar.context = this; + this._actionBar.setContent([ + { action: this._createInsightAction }, + { action: this._copyAction }, + { action: this._saveAction } + ]); + } + + + public onChartChanged(): void { + if (['scatter', 'timeSeries'].some(item => item === this.chartTypesSelectBox.value)) { + this.dataType = DataType.Point; + this.dataDirection = DataDirection.Horizontal; + } + this.initChart(); + } + + public onLabelFirstColumnChanged(): void { + this._chartConfig.labelFirstColumn = this.labelFirstColumnCheckBox.checked; + this.initChart(); + } + + public columnsAsLabelsChanged(): void { + this._chartConfig.columnsAsLabels = this.columnsAsLabelsCheckBox.checked; + this.initChart(); + } + + public onLegendChanged(): void { + this._chartConfig.legendPosition = this.legendSelectBox.value; + this.initChart(); + } + + public set dataType(type: DataType) { + this._chartConfig.dataType = type; + // Requires full chart refresh + this.initChart(); + } + + public set dataDirection(direction: DataDirection) { + this._chartConfig.dataDirection = direction; + // Requires full chart refresh + this.initChart(); + } + + public copyChart(): void { + let data = this._chartComponent.getCanvasData(); + if (!data) { + this.showError(this.chartNotFoundError); + return; + } + + this._bootstrapService.clipboardService.writeImageDataUrl(data); + } + + public saveChart(): void { + let filePath = this.promptForFilepath(); + let data = this._chartComponent.getCanvasData(); + if (!data) { + this.showError(this.chartNotFoundError); + return; + } + if (filePath) { + let buffer = this.decodeBase64Image(data); + pfs.writeFile(filePath, buffer).then(undefined, (err) => { + if (err) { + this.showError(err.message); + } else { + let fileUri = URI.from({ scheme: PathUtilities.FILE_SCHEMA, path: filePath }); + this._bootstrapService.windowsService.openExternal(fileUri.toString()); + this._bootstrapService.messageService.show(Severity.Info, nls.localize('chartSaved', 'Saved Chart to path: {0}', filePath)); + } + }); + } + } + + private promptForFilepath(): string { + let filepathPlaceHolder = PathUtilities.resolveCurrentDirectory(this.getActiveUriString(), PathUtilities.getRootPath(this._bootstrapService.workspaceContextService)); + filepathPlaceHolder = paths.join(filepathPlaceHolder, 'chart.png'); + + let filePath: string = this._bootstrapService.windowService.showSaveDialog({ + title: nls.localize('saveAsFileTitle', 'Choose Results File'), + defaultPath: paths.normalize(filepathPlaceHolder, true) + }); + return filePath; + } + + private decodeBase64Image(data: string): Buffer { + let matches = data.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/); + return new Buffer(matches[2], 'base64'); + } + + public createInsight(): void { + let uriString: string = this.getActiveUriString(); + if (!uriString) { + this.showError(nls.localize('createInsightNoEditor', 'Cannot create insight as the active editor is not a SQL Editor')); + return; + } + + let uri: URI = URI.parse(uriString); + let dataService = this._bootstrapService.queryModelService.getDataService(uriString); + if (!dataService) { + this.showError(nls.localize('createInsightNoDataService', 'Cannot create insight, backing data model not found')); + return; + } + let queryFile: string = uri.fsPath; + let query: string = undefined; + let type = {}; + type[this.chartTypesSelectBox.value] = this._chartConfig; + // create JSON + let config: IInsightsConfig = { + type, + query, + queryFile + }; + + let widgetConfig = { + name: nls.localize('myWidgetName', 'My-Widget'), + gridItemConfig: this.getGridItemConfig(), + widget: { + 'insights-widget': config + } + }; + + // open in new window as untitled JSON file + dataService.openLink(JSON.stringify(widgetConfig), 'Insight', 'json'); + } + + private showError(errorMsg: string) { + this._bootstrapService.messageService.show(Severity.Error, errorMsg); + } + + private getGridItemConfig(): NgGridItemConfig { + let config: NgGridItemConfig = { + sizex: 2, + sizey: 1 + }; + return config; + } + + private getActiveUriString(): string { + let editorService = this._bootstrapService.editorService; + let editor = editorService.getActiveEditor(); + if (editor && editor instanceof QueryEditor) { + let queryEditor: QueryEditor = editor; + return queryEditor.uri; + } + return undefined; + } + + private get showDataDirection(): boolean { + return ['pie', 'horizontalBar', 'bar', 'doughnut'].some(item => item === this.chartTypesSelectBox.value) || (this.chartTypesSelectBox.value === 'line' && this.dataType === 'number'); + } + + private get showLabelFirstColumn(): boolean { + return this.dataDirection === 'horizontal' && this.dataType !== 'point'; + } + + private get showColumnsAsLabels(): boolean { + return this.dataDirection === 'vertical' && this.dataType !== 'point'; + } + + private get showDataType(): boolean { + return this.chartTypesSelectBox.value === 'line'; + } + + public get dataDirection(): DataDirection { + return this._chartConfig.dataDirection; + } + + public get dataType(): DataType { + return this._chartConfig.dataType; + } + + @Input() set dataSet(dataSet: IGridDataSet) { + // Setup the execute result + this._dataSet = dataSet; + this._executeResult = {}; + this._executeResult.columns = dataSet.columnDefinitions.map(def => def.name); + this._executeResult.rows = dataSet.dataRows.getRange(0, dataSet.dataRows.getLength()).map(gridRow => { + return gridRow.values.map(cell => cell.displayValue); + }); + this.initChart(); + } + + public initChart() { + this._cd.detectChanges(); + if (this._executeResult) { + // Reinitialize the chart component + let componentFactory = this._componentFactoryResolver.resolveComponentFactory(insightRegistry.getCtorFromId(this.chartTypesSelectBox.value)); + this.componentHost.viewContainerRef.clear(); + let componentRef = this.componentHost.viewContainerRef.createComponent(componentFactory); + this._chartComponent = componentRef.instance; + this._chartComponent.config = this._chartConfig; + this._chartComponent.data = this._executeResult; + this._chartComponent.options = mixin(this._chartComponent.options, { animation: { duration: 0 } }); + if (this._chartComponent.init) { + this._chartComponent.init(); + } + } + } + + ngOnDestroy() { + this._disposables.forEach(i => i.dispose()); + } +} \ No newline at end of file diff --git a/src/sql/parts/grid/views/query/chartViewer.css b/src/sql/parts/grid/views/query/chartViewer.css new file mode 100644 index 0000000000..4887c2fb55 --- /dev/null +++ b/src/sql/parts/grid/views/query/chartViewer.css @@ -0,0 +1,56 @@ + +input[type="radio"] { + margin-top: -2px; + vertical-align: middle; +} + +.chart-viewer { + overflow-y: auto; +} +.chart-viewer .indent { + margin-left: 7px; +} + +.chart-viewer .radio-indent { + margin-left: 5px; +} + +.chart-viewer .option { + width: 100%; + padding-bottom: 7px; +} + +.chart-viewer .dialog-label { + width: 100%; + padding-bottom: 5px; +} + +.chart-viewer .input-divider { + width: 100%; + padding-bottom: 20px; +} + +.chart-viewer .footer { + display: flex; +} + +.chart-viewer .footer .right-footer { + display: flex; + flex: 1 1 auto; + justify-content: flex-end; +} + +.chart-viewer .footer-button a.monaco-button.monaco-text-button { + width: 100px; +} + +.vs-dark.monaco-shell .chart-viewer .footer-button a.monaco-button.monaco-text-button { + outline-color: #8e8c8c; +} +.chart-viewer .footer-button { + margin-right: 5px; +} + +.chart-viewer .right-footer .footer-button:last-of-type { + margin-right: none; +} \ No newline at end of file diff --git a/src/sql/parts/grid/views/query/chartViewerActions.ts b/src/sql/parts/grid/views/query/chartViewerActions.ts new file mode 100644 index 0000000000..3724b4d645 --- /dev/null +++ b/src/sql/parts/grid/views/query/chartViewerActions.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; + +import { IMessageService, Severity } from 'vs/platform/message/common/message'; + +export interface IChartViewActionContext { + copyChart(): void; + saveChart(): void; + createInsight(): void; +} + +export class ChartViewActionBase extends Action { + public static BaseClass = 'queryTaskbarIcon'; + private _classes: string[]; + + constructor( + id: string, + label: string, + enabledClass: string, + protected messageService: IMessageService + ) { + super(id, label); + this.enabled = true; + this._setCssClass(enabledClass); + } + protected updateCssClass(enabledClass: string): void { + // set the class, useful on change of label or icon + this._setCssClass(enabledClass); + } + + /** + * Sets the CSS classes combining the parent and child classes. + * Public for testing only. + */ + private _setCssClass(enabledClass: string): void { + this._classes = []; + this._classes.push(ChartViewActionBase.BaseClass); + + if (enabledClass) { + this._classes.push(enabledClass); + } + this.class = this._classes.join(' '); + } + + protected doRun(context: IChartViewActionContext, runAction: Function): TPromise { + if (!context) { + // TODO implement support for finding chart view in active window + this.messageService.show(Severity.Error, nls.localize('chartContextRequired', 'Chart View context is required to run this action')); + return TPromise.as(false); + } + return new TPromise((resolve, reject) => { + runAction(); + resolve(true); + }); + } + +} + +export class CreateInsightAction extends ChartViewActionBase { + public static ID = 'chartview.createInsight'; + public static LABEL = nls.localize('createInsightLabel', "Create Insight"); + + constructor(@IMessageService messageService: IMessageService + ) { + super(CreateInsightAction.ID, CreateInsightAction.LABEL, 'createInsight', messageService); + } + + public run(context: IChartViewActionContext): TPromise { + return this.doRun(context, () => context.createInsight()); + } +} + +export class CopyAction extends ChartViewActionBase { + public static ID = 'chartview.copy'; + public static LABEL = nls.localize('copyChartLabel', "Copy as image"); + + constructor(@IMessageService messageService: IMessageService + ) { + super(CopyAction.ID, CopyAction.LABEL, 'copyImage', messageService); + } + + public run(context: IChartViewActionContext): TPromise { + return this.doRun(context, () => context.copyChart()); + } +} + +export class SaveImageAction extends ChartViewActionBase { + public static ID = 'chartview.saveImage'; + public static LABEL = nls.localize('saveImageLabel', "Save as image"); + + constructor(@IMessageService messageService: IMessageService + ) { + super(SaveImageAction.ID, SaveImageAction.LABEL, 'saveAsImage', messageService); + } + + public run(context: IChartViewActionContext): TPromise { + return this.doRun(context, () => context.saveChart()); + } +} \ No newline at end of file diff --git a/src/sql/parts/grid/views/query/query.component.html b/src/sql/parts/grid/views/query/query.component.html new file mode 100644 index 0000000000..96e8c37b51 --- /dev/null +++ b/src/sql/parts/grid/views/query/query.component.html @@ -0,0 +1,79 @@ + + +
+
+ {{LocalizedConstants.resultPaneLabel}} + {{resultShortcut}} +
+
+
+ + + +
+
+ + +
+
+
+
+
+
+
+ {{LocalizedConstants.messagePaneLabel}} + {{messageShortcut}} +
+
+
+ + + + + + + + + + + + + + + + + + + + +
[{{message.time}}]{{message.message}} {{message.link.text}} +
[{{startString}}] + + {{LocalizedConstants.executeQueryLabel}} +
{{stringsFormat(LocalizedConstants.elapsedTimeLabel, totalElapsedTimeSpan)}}
+
+
+
\ No newline at end of file diff --git a/src/sql/parts/grid/views/query/query.component.ts b/src/sql/parts/grid/views/query/query.component.ts new file mode 100644 index 0000000000..078c3786e7 --- /dev/null +++ b/src/sql/parts/grid/views/query/query.component.ts @@ -0,0 +1,603 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import 'vs/css!sql/media/icons/common-icons'; +import 'vs/css!sql/parts/grid/media/slickColorTheme'; +import 'vs/css!sql/parts/grid/media/flexbox'; +import 'vs/css!sql/parts/grid/media/styles'; +import 'vs/css!sql/parts/grid/media/slick.grid'; +import 'vs/css!sql/parts/grid/media/slickGrid'; + +import { + ElementRef, QueryList, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, + ViewChildren, forwardRef, EventEmitter, Input, ViewChild +} from '@angular/core'; +import { IGridDataRow, SlickGrid, VirtualizedCollection } from 'angular2-slickgrid'; +import * as rangy from 'sql/base/node/rangy'; + +import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants'; +import * as Services from 'sql/parts/grid/services/sharedServices'; +import { IGridIcon, IMessage, IRange, IGridDataSet } from 'sql/parts/grid/common/interfaces'; +import { GridParentComponent } from 'sql/parts/grid/views/gridParentComponent'; +import { GridActionProvider } from 'sql/parts/grid/views/gridActions'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { QueryComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; +import { error } from 'sql/base/common/log'; +import { TabChild } from 'sql/base/browser/ui/panel/tab.component'; + + +import * as strings from 'vs/base/common/strings'; +import { clone } from 'vs/base/common/objects'; +import * as DOM from 'vs/base/browser/dom'; + +export const QUERY_SELECTOR: string = 'query-component'; + +declare type PaneType = 'messages' | 'results'; + +@Component({ + selector: QUERY_SELECTOR, + host: { '(window:keydown)': 'keyEvent($event)', '(window:gridnav)': 'keyEvent($event)' }, + templateUrl: decodeURI(require.toUrl('sql/parts/grid/views/query/query.component.html')), + providers: [{ provide: TabChild, useExisting: forwardRef(() => QueryComponent) }] +}) +export class QueryComponent extends GridParentComponent implements OnInit, OnDestroy { + // CONSTANTS + // tslint:disable-next-line:no-unused-variable + private scrollTimeOutTime: number = 200; + private windowSize: number = 50; + private messagePaneHeight: number = 22; + // tslint:disable-next-line:no-unused-variable + private maxScrollGrids: number = 8; + + // create a function alias to use inside query.component + // tslint:disable-next-line:no-unused-variable + private stringsFormat: any = strings.format; + + // tslint:disable-next-line:no-unused-variable + private dataIcons: IGridIcon[] = [ + { + showCondition: () => { return this.dataSets.length > 1; }, + icon: () => { + return this.renderedDataSets.length === 1 + ? 'exitFullScreen' + : 'extendFullScreen'; + }, + hoverText: () => { + return this.renderedDataSets.length === 1 + ? LocalizedConstants.restoreLabel + : LocalizedConstants.maximizeLabel; + }, + functionality: (batchId, resultId, index) => { + this.magnify(index); + } + }, + { + showCondition: () => { return true; }, + icon: () => { return 'saveCsv'; }, + hoverText: () => { return LocalizedConstants.saveCSVLabel; }, + functionality: (batchId, resultId, index) => { + let selection = this.slickgrids.toArray()[index].getSelectedRanges(); + if (selection.length <= 1) { + this.handleContextClick({ type: 'savecsv', batchId: batchId, resultId: resultId, index: index, selection: selection }); + } else { + this.dataService.showWarning(LocalizedConstants.msgCannotSaveMultipleSelections); + } + } + }, + { + showCondition: () => { return true; }, + icon: () => { return 'saveJson'; }, + hoverText: () => { return LocalizedConstants.saveJSONLabel; }, + functionality: (batchId, resultId, index) => { + let selection = this.slickgrids.toArray()[index].getSelectedRanges(); + if (selection.length <= 1) { + this.handleContextClick({ type: 'savejson', batchId: batchId, resultId: resultId, index: index, selection: selection }); + } else { + this.dataService.showWarning(LocalizedConstants.msgCannotSaveMultipleSelections); + } + } + }, + { + showCondition: () => { return true; }, + icon: () => { return 'saveExcel'; }, + hoverText: () => { return LocalizedConstants.saveExcelLabel; }, + functionality: (batchId, resultId, index) => { + let selection = this.slickgrids.toArray()[index].getSelectedRanges(); + if (selection.length <= 1) { + this.handleContextClick({ type: 'saveexcel', batchId: batchId, resultId: resultId, index: index, selection: selection }); + } else { + this.dataService.showWarning(LocalizedConstants.msgCannotSaveMultipleSelections); + } + } + }, + { + showCondition: () => { return true; }, + icon: () => { return 'viewChart'; }, + hoverText: () => { return LocalizedConstants.viewChartLabel; }, + functionality: (batchId, resultId, index) => { + this.showChartForGrid(index); + } + } + ]; + + // FIELDS + // Service for interaction with the IQueryModel + + // All datasets + private dataSets: IGridDataSet[] = []; + private messages: IMessage[] = []; + private messageStore: IMessage[] = []; + private messageTimeout: number; + private scrollTimeOut: number; + private resizing = false; + private resizeHandleTop: string = '0'; + private scrollEnabled = true; + // tslint:disable-next-line:no-unused-variable + private firstRender = true; + private totalElapsedTimeSpan: number; + private complete = false; + private sentPlans: Map = new Map(); + private hasQueryPlan: boolean = false; + public queryExecutionStatus: EventEmitter = new EventEmitter(); + public queryPlanAvailable: EventEmitter = new EventEmitter(); + public showChartRequested: EventEmitter = new EventEmitter(); + + @Input() public queryParameters: QueryComponentParams; + + @ViewChildren('slickgrid') slickgrids: QueryList; + // tslint:disable-next-line:no-unused-variable + @ViewChild('resultsPane', { read: ElementRef }) private _resultsPane: ElementRef; + + constructor( + @Inject(forwardRef(() => ElementRef)) el: ElementRef, + @Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef, + @Inject(BOOTSTRAP_SERVICE_ID) bootstrapService: IBootstrapService + ) { + super(el, cd, bootstrapService); + this._el.nativeElement.className = 'slickgridContainer'; + } + + /** + * Called by Angular when the object is initialized + */ + ngOnInit(): void { + const self = this; + + this.dataService = this.queryParameters.dataService; + this.actionProvider = new GridActionProvider(this.dataService, this.onGridSelectAll()); + + this.baseInit(); + this.setupResizeBind(); + + this.subscribeWithDispose(this.dataService.queryEventObserver, (event) => { + switch (event.type) { + case 'start': + self.handleStart(self, event); + break; + case 'complete': + self.handleComplete(self, event); + break; + case 'message': + self.handleMessage(self, event); + break; + case 'resultSet': + self.handleResultSet(self, event); + break; + default: + error('Unexpected query event type "' + event.type + '" sent'); + break; + } + self._cd.detectChanges(); + }); + this.dataService.onAngularLoaded(); + } + + public ngOnDestroy(): void { + this.baseDestroy(); + } + + protected initShortcuts(shortcuts: { [name: string]: Function }): void { + shortcuts['event.nextGrid'] = () => { + this.navigateToGrid(this.activeGrid + 1); + }; + shortcuts['event.prevGrid'] = () => { + this.navigateToGrid(this.activeGrid - 1); + }; + shortcuts['event.maximizeGrid'] = () => { + this.magnify(this.activeGrid); + }; + } + + handleStart(self: QueryComponent, event: any): void { + self.messages = []; + self.dataSets = []; + self.placeHolderDataSets = []; + self.renderedDataSets = self.placeHolderDataSets; + self.totalElapsedTimeSpan = undefined; + self.complete = false; + self.activeGrid = 0; + + // reset query plan info and send notification to subscribers + self.hasQueryPlan = false; + self.sentPlans = new Map(); + self.queryExecutionStatus.emit('start'); + self.firstRender = true; + } + + handleComplete(self: QueryComponent, event: any): void { + self.totalElapsedTimeSpan = event.data; + self.complete = true; + } + + handleMessage(self: QueryComponent, event: any): void { + self.messageStore.push(event.data); + clearTimeout(self.messageTimeout); + self.messageTimeout = setTimeout(() => { + self.messages = self.messages.concat(self.messageStore); + self.messageStore = []; + self._cd.detectChanges(); + self.scrollMessages(); + }, 10); + } + + handleResultSet(self: QueryComponent, event: any): void { + let resultSet = event.data; + + // No column info found, so define a column of no name by default + if (!resultSet.columnInfo) { + resultSet.columnInfo = []; + resultSet.columnInfo[0] = { columnName: '' }; + } + // Setup a function for generating a promise to lookup result subsets + let loadDataFunction = (offset: number, count: number): Promise => { + return new Promise((resolve, reject) => { + self.dataService.getQueryRows(offset, count, resultSet.batchId, resultSet.id).subscribe(rows => { + let gridData: IGridDataRow[] = []; + for (let row = 0; row < rows.rows.length; row++) { + // Push row values onto end of gridData for slickgrid + gridData.push({ + values: rows.rows[row] + }); + } + + // if this is a query plan resultset we haven't processed yet then forward to subscribers + if (self.hasQueryPlan && !self.sentPlans[resultSet.batchId]) { + self.sentPlans[resultSet.batchId] = rows.rows[0][0].displayValue; + self.queryPlanAvailable.emit(rows.rows[0][0].displayValue); + } + resolve(gridData); + }); + }); + }; + + // Precalculate the max height and min height + let maxHeight: string = 'inherit'; + if (resultSet.rowCount < self._defaultNumShowingRows) { + let maxHeightNumber: number = Math.max((resultSet.rowCount + 1) * self._rowHeight, self.dataIcons.length * 30) + 10; + maxHeight = maxHeightNumber.toString() + 'px'; + } + + let minHeight: string = maxHeight; + if (resultSet.rowCount >= self._defaultNumShowingRows) { + let minHeightNumber: number = (self._defaultNumShowingRows + 1) * self._rowHeight + 10; + minHeight = minHeightNumber.toString() + 'px'; + } + + // Store the result set from the event + let dataSet: IGridDataSet = { + resized: undefined, + batchId: resultSet.batchId, + resultId: resultSet.id, + totalRows: resultSet.rowCount, + maxHeight: maxHeight, + minHeight: minHeight, + dataRows: new VirtualizedCollection( + self.windowSize, + resultSet.rowCount, + loadDataFunction, + index => { return { values: [] }; } + ), + columnDefinitions: resultSet.columnInfo.map((c, i) => { + let isLinked = c.isXml || c.isJson; + let linkType = c.isXml ? 'xml' : 'json'; + return { + id: i.toString(), + name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan' + ? 'XML Showplan' + : c.columnName, + type: self.stringToFieldType('string'), + formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter, + asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined + }; + }) + }; + self.dataSets.push(dataSet); + + // check if the resultset is for a query plan + for (let i = 0; i < resultSet.columnInfo.length; ++i) { + let column = resultSet.columnInfo[i]; + if (column.columnName === 'Microsoft SQL Server 2005 XML Showplan') { + this.hasQueryPlan = true; + break; + } + } + + // Create a dataSet to render without rows to reduce DOM size + let undefinedDataSet = clone(dataSet); + undefinedDataSet.columnDefinitions = dataSet.columnDefinitions; + undefinedDataSet.dataRows = undefined; + undefinedDataSet.resized = new EventEmitter(); + self.placeHolderDataSets.push(undefinedDataSet); + self.onScroll(0); + } + + /** + * Perform copy and do other actions for context menu on the messages component + */ + handleMessagesContextClick(event: { type: string, selectedRange: IRange }): void { + switch (event.type) { + case 'copySelection': + let selectedText = event.selectedRange.text(); + WorkbenchUtils.executeCopy(selectedText); + break; + case 'selectall': + document.execCommand('selectAll'); + break; + default: + break; + } + } + + openMessagesContextMenu(event: any): void { + let self = this; + event.preventDefault(); + let selectedRange: IRange = this.getSelectedRangeUnderMessages(); + let selectAllFunc = () => self.selectAllMessages(); + let anchor = { x: event.x + 1, y: event.y }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this.actionProvider.getMessagesActions(this.dataService, selectAllFunc), + getKeyBinding: (action) => this._keybindingFor(action), + onHide: (wasCancelled?: boolean) => { + }, + getActionsContext: () => (selectedRange) + }); + } + + + /** + * Handles rendering the results to the DOM that are currently being shown + * and destroying any results that have moved out of view + * @param scrollTop The scrolltop value, if not called by the scroll event should be 0 + */ + onScroll(scrollTop): void { + const self = this; + clearTimeout(self.scrollTimeOut); + this.scrollTimeOut = setTimeout(() => { + if (self.dataSets.length < self.maxScrollGrids) { + self.scrollEnabled = false; + for (let i = 0; i < self.placeHolderDataSets.length; i++) { + self.placeHolderDataSets[i].dataRows = self.dataSets[i].dataRows; + self.placeHolderDataSets[i].resized.emit(); + } + } else { + let gridHeight = self._el.nativeElement.getElementsByTagName('slick-grid')[0].offsetHeight; + let tabHeight = self.getResultsElement().offsetHeight; + let numOfVisibleGrids = Math.ceil((tabHeight / gridHeight) + + ((scrollTop % gridHeight) / gridHeight)); + let min = Math.floor(scrollTop / gridHeight); + let max = min + numOfVisibleGrids; + for (let i = 0; i < self.placeHolderDataSets.length; i++) { + if (i >= min && i < max) { + if (self.placeHolderDataSets[i].dataRows === undefined) { + self.placeHolderDataSets[i].dataRows = self.dataSets[i].dataRows; + self.placeHolderDataSets[i].resized.emit(); + } + } else if (self.placeHolderDataSets[i].dataRows !== undefined) { + self.placeHolderDataSets[i].dataRows = undefined; + } + } + } + + self._cd.detectChanges(); + + if (self.firstRender) { + let setActive = () => { + if (self.firstRender && self.slickgrids.toArray().length > 0) { + self.slickgrids.toArray()[0].setActive(); + self.firstRender = false; + } + }; + + setTimeout(() => { + setActive(); + }); + } + }, self.scrollTimeOutTime); + } + + onSelectionLinkClicked(index: number): void { + this.dataService.setEditorSelection(index); + } + + /** + * Sets up the resize for the messages/results panes bar + */ + setupResizeBind(): void { + const self = this; + + let resizeHandleElement: HTMLElement = self._el.nativeElement.querySelector('#messageResizeHandle'); + let $resizeHandle = $(resizeHandleElement); + let $messages = $(self.getMessagesElement()); + + $resizeHandle.bind('dragstart', (e) => { + self.resizing = true; + self.resizeHandleTop = self.calculateResizeHandleTop(e.pageY); + self._cd.detectChanges(); + return true; + }); + + $resizeHandle.bind('drag', (e) => { + // Update the animation if the drag is within the allowed range. + if (self.isDragWithinAllowedRange(e.pageY, resizeHandleElement)) { + self.resizeHandleTop = self.calculateResizeHandleTop(e.pageY); + self.resizing = true; + self._cd.detectChanges(); + + // Stop the animation if the drag is out of the allowed range. + // The animation is resumed when the drag comes back into the allowed range. + } else { + self.resizing = false; + } + }); + + $resizeHandle.bind('dragend', (e) => { + self.resizing = false; + // Redefine the min size for the messages based on the final position + // if the drag is within the allowed rang + if (self.isDragWithinAllowedRange(e.pageY, resizeHandleElement)) { + let minHeightNumber = this.getMessagePaneHeightFromDrag(e.pageY); + $messages.css('min-height', minHeightNumber + 'px'); + self._cd.detectChanges(); + self.resizeGrids(); + + // Otherwise just update the UI to show that the drag is complete + } else { + self._cd.detectChanges(); + } + }); + } + + /** + * Returns true if the resize of the messagepane given by the drag at top=eventPageY is valid, + * false otherwise. A drag is valid if it is below the bottom of the resultspane and + * this.messagePaneHeight pixels above the bottom of the entire angular component. + */ + isDragWithinAllowedRange(eventPageY: number, resizeHandle: HTMLElement): boolean { + let resultspaneElement: HTMLElement = this._el.nativeElement.querySelector('#resultspane'); + let minHeight = this.getMessagePaneHeightFromDrag(eventPageY); + + if (resultspaneElement && + minHeight > 0 && + resultspaneElement.getBoundingClientRect().bottom < eventPageY + ) { + return true; + } + return false; + } + + /** + * Calculates the position of the top of the resize handle given the Y-axis drag + * coordinate as eventPageY. + */ + calculateResizeHandleTop(eventPageY: number): string { + let resultsWindowTop: number = this._el.nativeElement.getBoundingClientRect().top; + let relativeTop: number = eventPageY - resultsWindowTop; + return relativeTop + 'px'; + } + + /** + * Returns the height the message pane would be if it were resized so that its top would be set to eventPageY. + * This will return a negative value if eventPageY is below the bottom limit. + */ + getMessagePaneHeightFromDrag(eventPageY: number): number { + let bottomDragLimit: number = this._el.nativeElement.getBoundingClientRect().bottom - this.messagePaneHeight; + return bottomDragLimit - eventPageY; + } + + /** + * Ensures the messages tab is scrolled to the bottom + */ + scrollMessages(): void { + let messagesDiv = this.getMessagesElement(); + messagesDiv.scrollTop = messagesDiv.scrollHeight; + } + + /** + * + */ + protected tryHandleKeyEvent(e): boolean { + return false; + } + + /** + * Handles rendering and unrendering necessary resources in order to properly + * navigate from one grid another. Should be called any time grid navigation is performed + * @param targetIndex The index in the renderedDataSets to navigate to + * @returns A boolean representing if the navigation was successful + */ + navigateToGrid(targetIndex: number): boolean { + // check if the target index is valid + if (targetIndex >= this.renderedDataSets.length || targetIndex < 0 || !this.hasFocus()) { + return false; + } + + // Deselect any text since we are navigating to a new grid + // Do this even if not switching grids, since this covers clicking on the grid after message selection + rangy.getSelection().removeAllRanges(); + + // check if you are actually trying to change navigation + if (this.activeGrid === targetIndex) { + return false; + } + + this.slickgrids.toArray()[this.activeGrid].selection = false; + this.slickgrids.toArray()[targetIndex].setActive(); + this.activeGrid = targetIndex; + + // scrolling logic + let resultsWindow = $('#results'); + let scrollTop = resultsWindow.scrollTop(); + let scrollBottom = scrollTop + resultsWindow.height(); + let gridHeight = $(this._el.nativeElement).find('slick-grid').height(); + if (scrollBottom < gridHeight * (targetIndex + 1)) { + scrollTop += (gridHeight * (targetIndex + 1)) - scrollBottom; + resultsWindow.scrollTop(scrollTop); + } + if (scrollTop > gridHeight * targetIndex) { + scrollTop = (gridHeight * targetIndex); + resultsWindow.scrollTop(scrollTop); + } + + return true; + } + + public hasFocus(): boolean { + return DOM.isAncestor(document.activeElement, this._el.nativeElement); + } + + resizeGrids(): void { + const self = this; + setTimeout(() => { + for (let grid of self.renderedDataSets) { + grid.resized.emit(); + } + }); + } + + private showChartForGrid(index: number) { + if (this.renderedDataSets.length > index) { + this.showChartRequested.emit(this.renderedDataSets[index]); + } + } + + /* Helper function to toggle messages and results panes */ + // tslint:disable-next-line:no-unused-variable + private togglePane(pane: PaneType): void { + if (pane === 'messages') { + this._messageActive = !this._messageActive; + } else if (pane === 'results') { + this.resultActive = !this.resultActive; + } + this._cd.detectChanges(); + this.resizeGrids(); + } + + layout() { + this.resizeGrids(); + } +} \ No newline at end of file diff --git a/src/sql/parts/insights/browser/insightsDialogView.ts b/src/sql/parts/insights/browser/insightsDialogView.ts new file mode 100644 index 0000000000..6eb2d3a416 --- /dev/null +++ b/src/sql/parts/insights/browser/insightsDialogView.ts @@ -0,0 +1,381 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/parts/insights/browser/media/insightsDialog'; + +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { Modal } from 'sql/base/browser/ui/modal/modal'; +import { IInsightsConfigDetails } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import { attachModalDialogStyler, attachTableStyler } from 'sql/common/theme/styler'; +import { ITaskRegistry, Extensions as TaskExtensions } from 'sql/platform/tasks/taskRegistry'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import { IInsightsDialogModel, ListResource, IInsightDialogActionContext } from 'sql/parts/insights/common/interfaces'; +import { TableCollapsibleView } from 'sql/base/browser/ui/table/tableView'; +import { TableDataView } from 'sql/base/browser/ui/table/tableDataView'; +import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin'; +import { error } from 'sql/base/common/log'; +import { Table } from 'sql/base/browser/ui/table/table'; +import { CopyInsightDialogSelectionAction } from 'sql/parts/insights/common/insightDialogActions'; + +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import * as DOM from 'vs/base/browser/dom'; +import { SplitView, ViewSizing } from 'vs/base/browser/ui/splitview/splitview'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IListService } from 'vs/platform/list/browser/listService'; +import * as nls from 'vs/nls'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IAction } from 'vs/base/common/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import * as types from 'vs/base/common/types'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { Button } from 'vs/base/browser/ui/button/button'; + +/* Regex that matches the form `${value}` */ +export const insertValueRegex: RegExp = /\${(.*?)\}/; + +const labelDisplay = nls.localize("item", "Item"); +const valueDisplay = nls.localize("value", "Value"); + +function stateFormatter(row: number, cell: number, value: any, columnDef: Slick.Column, resource: ListResource): string { + // template + const icon = DOM.$('span.icon-span'); + const badge = DOM.$('div.badge'); + const badgeContent = DOM.$('div.badge-content'); + DOM.append(badge, badgeContent); + DOM.append(icon, badge); + + // render icon if passed + if (resource.icon) { + icon.classList.add('icon'); + icon.classList.add(resource.icon); + } else { + icon.classList.remove('icon'); + } + + //render state badge if present + if (resource.stateColor) { + badgeContent.style.backgroundColor = resource.stateColor; + badgeContent.classList.remove('icon'); + } else if (resource.stateIcon) { + badgeContent.style.backgroundColor = ''; + badgeContent.classList.add('icon'); + badgeContent.classList.add(resource.stateIcon); + } else { + badgeContent.classList.remove('icon'); + badgeContent.style.backgroundColor = ''; + } + + return icon.outerHTML; +} + +export class InsightsDialogView extends Modal { + + private _connectionProfile: IConnectionProfile; + private _insight: IInsightsConfigDetails; + private _splitView: SplitView; + private _container: HTMLElement; + private _topTable: Table; + private _topTableData: TableDataView; + private _bottomTable: Table; + private _bottomTableData: TableDataView; + private _taskButtonDisposables: IDisposable[] = []; + private _topColumns: Array> = [ + { + name: '', + field: 'state', + id: 'state', + width: 20, + resizable: false, + formatter: stateFormatter + }, + { + name: labelDisplay, + field: 'label', + id: 'label' + }, + { + name: valueDisplay, + field: 'value', + id: 'value' + } + ]; + + private _bottomColumns: Array> = [ + { + name: nls.localize("property", "Property"), + field: 'label', + id: 'label' + }, + { + name: nls.localize("value", "Value"), + field: 'value', + id: 'value' + } + ]; + + constructor( + private _model: IInsightsDialogModel, + @IInstantiationService private _instantiationService: IInstantiationService, + @IThemeService private _themeService: IThemeService, + @IListService private _listService: IListService, + @IPartService partService: IPartService, + @IContextMenuService private _contextMenuService: IContextMenuService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(nls.localize("InsightsDialogTitle", "Insights"), TelemetryKeys.Insights, partService, telemetryService, contextKeyService); + this._model.onDataChange(e => this.build()); + } + + private updateTopColumns(): void { + let labelName = this.labelColumnName ? this.labelColumnName : labelDisplay; + let valueName = this._insight.value ? this._insight.value : valueDisplay; + this._topColumns = [ + { + name: '', + field: 'state', + id: 'state', + width: 20, + resizable: false, + formatter: stateFormatter + }, + { + name: labelName, + field: 'label', + id: 'label' + }, + { + name: valueName, + field: 'value', + id: 'value' + } + ]; + this._topTable.columns = this._topColumns; + } + + protected renderBody(container: HTMLElement) { + this._container = container; + + this._splitView = new SplitView(container); + + this._topTableData = new TableDataView(); + this._bottomTableData = new TableDataView(); + let topTableView = new TableCollapsibleView(nls.localize("insights.dialog.items", "Items"), { sizing: ViewSizing.Flexible, ariaHeaderLabel: 'title' }, this._topTableData, this._topColumns, { forceFitColumns: true }); + this._topTable = topTableView.table; + topTableView.addContainerClass('insights'); + this._topTable.setSelectionModel(new RowSelectionModel()); + let bottomTableView = new TableCollapsibleView(nls.localize("insights.dialog.itemDetails", "Item Details"), { sizing: ViewSizing.Flexible, ariaHeaderLabel: 'title' }, this._bottomTableData, this._bottomColumns, { forceFitColumns: true }); + this._bottomTable = bottomTableView.table; + this._bottomTable.setSelectionModel(new RowSelectionModel()); + + this._register(this._topTable.onSelectedRowsChanged((e: DOMEvent, data: Slick.OnSelectedRowsChangedEventArgs) => { + if (data.rows.length === 1) { + let element = this._topTableData.getItem(data.rows[0]); + let resourceArray: ListResource[] = []; + for (let i = 0; i < this._model.columns.length; i++) { + resourceArray.push({ label: this._model.columns[i], value: element.data[i], data: element.data }); + } + this._bottomTableData.clear(); + this._bottomTableData.push(resourceArray); + this._enableTaskButtons(true); + } else { + this._enableTaskButtons(false); + } + })); + + this._register(this._topTable.onContextMenu((e: DOMEvent, data: Slick.OnContextMenuEventArgs) => { + if (this.hasActions()) { + this._contextMenuService.showContextMenu({ + getAnchor: () => e.target as HTMLElement, + getActions: () => this.insightActions, + getActionsContext: () => this.topInsightContext(this._topTableData.getItem(this._topTable.getCellFromEvent(e).row), this._topTable.getCellFromEvent(e)) + }); + } + })); + + this._register(this._bottomTable.onContextMenu((e: DOMEvent, data: Slick.OnContextMenuEventArgs) => { + this._contextMenuService.showContextMenu({ + getAnchor: () => e.target as HTMLElement, + getActions: () => TPromise.as([this._instantiationService.createInstance(CopyInsightDialogSelectionAction, CopyInsightDialogSelectionAction.ID, CopyInsightDialogSelectionAction.LABEL)]), + getActionsContext: () => this.bottomInsightContext(this._bottomTableData.getItem(this._bottomTable.getCellFromEvent(e).row), this._bottomTable.getCellFromEvent(e)) + }); + })); + + this._splitView.addView(topTableView); + this._splitView.addView(bottomTableView); + + this._register(attachTableStyler(this._topTable, this._themeService)); + this._register(attachTableStyler(this._bottomTable, this._themeService)); + } + + public render() { + super.render(); + let button = this.addFooterButton('Close', () => this.close()); + this._register(attachButtonStyler(button, this._themeService)); + this._register(attachModalDialogStyler(this, this._themeService)); + } + + protected layout(height?: number): void { + this._splitView.layout(DOM.getContentHeight(this._container)); + } + + // insight object + public open(input: IInsightsConfigDetails, connectionProfile: IConnectionProfile): void { + if (types.isUndefinedOrNull(input) || types.isUndefinedOrNull(connectionProfile)) { + return; + } + this._insight = input; + this._connectionProfile = connectionProfile; + this.updateTopColumns(); + this.show(); + } + + private build(): void { + let labelIndex: number; + let valueIndex: number; + let columnName = this.labelColumnName; + if (this._insight.label === undefined || (labelIndex = this._model.columns.indexOf(columnName)) === -1) { + labelIndex = 0; + } + if (this._insight.value === undefined || (valueIndex = this._model.columns.indexOf(this._insight.value)) === -1) { + valueIndex = 1; + } + // convert + let inputArray = this._model.getListResources(labelIndex, valueIndex); + this._topTableData.clear(); + this._topTableData.push(inputArray); + if (this._insight.actions && this._insight.actions.types) { + const taskRegistry = Registry.as(TaskExtensions.TaskContribution); + let tasks = taskRegistry.idToCtorMap; + for (let action of this._insight.actions.types) { + let ctor = tasks[action]; + if (ctor) { + let button = this.addFooterButton(ctor.LABEL, () => { + let element = this._topTable.getSelectedRows(); + let resource: ListResource; + if (element && element.length > 0) { + resource = this._topTableData.getItem(element[0]); + } else { + return; + } + this._instantiationService.createInstance(ctor, ctor.ID, ctor.LABEL, ctor.ICON).run(this.topInsightContext(resource)); + }, 'left'); + button.enabled = false; + this._taskButtonDisposables.push(button); + this._taskButtonDisposables.push(attachButtonStyler(button, this._themeService)); + } + } + } + this.layout(); + } + + public reset(): void { + this._topTableData.clear(); + this._bottomTableData.clear(); + } + + private get labelColumnName(): string { + return typeof this._insight.label === 'object' ? this._insight.label.column : this._insight.label; + } + + + public close() { + this.hide(); + dispose(this._taskButtonDisposables); + this._taskButtonDisposables = []; + } + + private hasActions(): boolean { + return !!(this._insight && this._insight.actions && this._insight.actions.types + && this._insight.actions.types.length > 0); + } + + private get insightActions(): TPromise { + const taskRegistry = Registry.as(TaskExtensions.TaskContribution); + let tasks = taskRegistry.idToCtorMap; + let actions = this._insight.actions.types; + let returnActions: IAction[] = []; + for (let action of actions) { + let ctor = tasks[action]; + if (ctor) { + returnActions.push(this._instantiationService.createInstance(ctor, ctor.ID, ctor.LABEL, ctor.ICON)); + } + } + return TPromise.as(returnActions); + } + + /** + * Creates the context that should be passed to the action passed on the selected element for the top table + * @param element + */ + private topInsightContext(element: ListResource, cell?: Slick.Cell): IInsightDialogActionContext { + let database = this._insight.actions.database || this._connectionProfile.databaseName; + let server = this._insight.actions.server || this._connectionProfile.serverName; + let user = this._insight.actions.user || this._connectionProfile.userName; + let match: Array; + match = database.match(insertValueRegex); + if (match && match.length > 0) { + let index = this._model.columns.indexOf(match[1]); + if (index === -1) { + error('Could not find column', match[1]); + } else { + database = database.replace(match[0], element.data[index]); + } + } + + match = server.match(insertValueRegex); + if (match && match.length > 0) { + let index = this._model.columns.indexOf(match[1]); + if (index === -1) { + error('Could not find column', match[1]); + } else { + server = server.replace(match[0], element.data[index]); + } + } + + match = user.match(insertValueRegex); + if (match && match.length > 0) { + let index = this._model.columns.indexOf(match[1]); + if (index === -1) { + error('Could not find column', match[1]); + } else { + user = user.replace(match[0], element.data[index]); + } + } + + let currentProfile = this._connectionProfile as ConnectionProfile; + let profile = new ConnectionProfile(currentProfile.serverCapabilities, currentProfile); + profile.databaseName = database; + profile.serverName = server; + profile.userName = user; + + return { profile, cellData: undefined }; + } + + /** + * Creates the context that should be passed to the action passed on the selected element for the bottom table + * @param element + */ + private bottomInsightContext(element: ListResource, cell: Slick.Cell): IInsightDialogActionContext { + + let cellData = element[this._bottomColumns[cell.cell].id]; + + return { profile: undefined, cellData }; + } + + private _enableTaskButtons(val: boolean): void { + for (let index = 0; index < this._taskButtonDisposables.length; index++) { + let element = this._taskButtonDisposables[index]; + if (element instanceof Button) { + element.enabled = val; + } + } + } +} \ No newline at end of file diff --git a/src/sql/parts/insights/browser/media/insightsDialog.css b/src/sql/parts/insights/browser/media/insightsDialog.css new file mode 100644 index 0000000000..27685c6bad --- /dev/null +++ b/src/sql/parts/insights/browser/media/insightsDialog.css @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.insights span { + display: inline-block; + text-overflow: ellipsis; + overflow: hidden; +} + +.insights .icon-span { + padding: 10px; +} + +.insights .badge .badge-content { + position: absolute; + top: 7px; + right: 8px; + min-width: 6px; + height: 6px; + border-radius: 3px; + text-align: center; +} + +.insights .badge { + position: absolute; + top: 7px; + left: 5px; + overflow: hidden; + width: 22px; + height: 22px; +} \ No newline at end of file diff --git a/src/sql/parts/insights/common/insightDialogActions.ts b/src/sql/parts/insights/common/insightDialogActions.ts new file mode 100644 index 0000000000..13287c23dc --- /dev/null +++ b/src/sql/parts/insights/common/insightDialogActions.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IInsightDialogActionContext } from 'sql/parts/insights/common/interfaces'; + +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; + +export class CopyInsightDialogSelectionAction extends Action { + public static ID = 'workbench.action.insights.copySelection'; + public static LABEL = nls.localize('workbench.action.insights.copySelection', "Copy Cell"); + + constructor( + id: string, label: string, + @IClipboardService private _clipboardService: IClipboardService + ) { + super(id, label); + } + + public run(event?: IInsightDialogActionContext): TPromise { + this._clipboardService.writeText(event.cellData); + return TPromise.as(void 0); + } +} diff --git a/src/sql/parts/insights/common/insightsDialogModel.ts b/src/sql/parts/insights/common/insightsDialogModel.ts new file mode 100644 index 0000000000..0b78fc5a85 --- /dev/null +++ b/src/sql/parts/insights/common/insightsDialogModel.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IInsightsDialogModel, ListResource } from './interfaces'; +import { IInsightsConfigDetails, IInsightsLabel } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import { Conditional } from 'sql/parts/dashboard/common/interfaces'; + +import Event, { Emitter, debounceEvent } from 'vs/base/common/event'; + +export class InsightsDialogModel implements IInsightsDialogModel { + private _rows: string[][]; + private _columns: string[]; + private _insight: IInsightsConfigDetails; + + private _onDataChangeEmitter: Emitter = new Emitter(); + private _onDataChangeEvent: Event = this._onDataChangeEmitter.event; + public onDataChange: Event = debounceEvent(this._onDataChangeEvent, (last, event) => event, 75, false); + + public set insight(insight: IInsightsConfigDetails) { + this._insight = insight; + } + + public set rows(val: string[][]) { + this._rows = val; + this._onDataChangeEmitter.fire(); + } + + public get rows(): string[][] { + return this._rows; + } + + public set columns(val: string[]) { + this._columns = val; + this._onDataChangeEmitter.fire(); + } + + public get columns(): string[] { + return this._columns; + } + + public reset(): void { + this._columns = []; + this._rows = []; + this._onDataChangeEmitter.fire(); + } + + public getListResources(labelIndex: number, valueIndex: number): ListResource[] { + return this.rows.map((item) => { + let label = item[labelIndex]; + let value = item[valueIndex]; + let state = this.calcInsightState(value); + let data = item; + let icon = typeof this._insight.label === 'object' ? this._insight.label.icon : undefined; + let rval = { title: false, label, value, icon, data }; + if (state) { + rval[state.type] = state.val; + } + return rval; + }); + } + + /** + * Calculates the state of the item value passed based on the insight conditions + * @param item item to determine state for + * @returns json that specifies whether the state is an icon or color and the val of that state + */ + private calcInsightState(item: string): { type: 'stateColor' | 'stateIcon', val: string } { + if (typeof this._insight.label === 'string') { + return undefined; + } else { + let label = this._insight.label; + for (let cond of label.state) { + switch (Conditional[cond.condition.if]) { + case Conditional.always: + return cond.color + ? { type: 'stateColor', val: cond.color } + : { type: 'stateIcon', val: cond.icon }; + case Conditional.equals: + if (item === cond.condition.equals) { + return cond.color + ? { type: 'stateColor', val: cond.color } + : { type: 'stateIcon', val: cond.icon }; + } + break; + case Conditional.notEquals: + if (item !== cond.condition.equals) { + return cond.color + ? { type: 'stateColor', val: cond.color } + : { type: 'stateIcon', val: cond.icon }; + } + break; + case Conditional.greaterThanOrEquals: + if (parseInt(item) >= parseInt(cond.condition.equals)) { + return cond.color + ? { type: 'stateColor', val: cond.color } + : { type: 'stateIcon', val: cond.icon }; + } + break; + case Conditional.greaterThan: + if (parseInt(item) > parseInt(cond.condition.equals)) { + return cond.color + ? { type: 'stateColor', val: cond.color } + : { type: 'stateIcon', val: cond.icon }; + } + break; + case Conditional.lessThanOrEquals: + if (parseInt(item) <= parseInt(cond.condition.equals)) { + return cond.color + ? { type: 'stateColor', val: cond.color } + : { type: 'stateIcon', val: cond.icon }; + } + break; + case Conditional.lessThan: + if (parseInt(item) < parseInt(cond.condition.equals)) { + return cond.color + ? { type: 'stateColor', val: cond.color } + : { type: 'stateIcon', val: cond.icon }; + } + break; + } + } + } + // if we got to this point, there was no matching conditionals therefore no valid state + return undefined; + } +} diff --git a/src/sql/parts/insights/common/interfaces.ts b/src/sql/parts/insights/common/interfaces.ts new file mode 100644 index 0000000000..c71029e258 --- /dev/null +++ b/src/sql/parts/insights/common/interfaces.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 Event from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +import { IInsightsConfigDetails, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ITaskActionContext } from 'sql/workbench/common/actions'; + +export interface IInsightsDialogModel { + rows: string[][]; + columns: string[]; + getListResources(labelIndex: number, valueIndex: number): ListResource[]; + reset(): void; + onDataChange: Event; + insight: IInsightsConfigDetails; +} + +export interface ListResource { + value: string; + label: string; + icon?: string; + data?: string[]; + stateColor?: string; + stateIcon?: string; +} + +export const IInsightsDialogService = createDecorator('insightsDialogService'); + +export interface IInsightsDialogService { + _serviceBrand: any; + show(input: IInsightsConfig, connectionProfile: IConnectionProfile): void; + close(); +} + +export interface IInsightDialogActionContext extends ITaskActionContext { + cellData: string; +} diff --git a/src/sql/parts/insights/insightsDialogService.ts b/src/sql/parts/insights/insightsDialogService.ts new file mode 100644 index 0000000000..cc38a0f89e --- /dev/null +++ b/src/sql/parts/insights/insightsDialogService.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +import { InsightsDialogController } from 'sql/parts/insights/node/insightsDialogController'; +import { InsightsDialogView } from 'sql/parts/insights/browser/insightsDialogView'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import { IInsightsDialogModel, IInsightsDialogService } from 'sql/parts/insights/common/interfaces'; +import { InsightsDialogModel } from 'sql/parts/insights/common/insightsDialogModel'; + +export class InsightsDialogService implements IInsightsDialogService { + _serviceBrand: any; + private _insightsDialogController: InsightsDialogController; + private _insightsDialogView: InsightsDialogView; + private _insightsDialogModel: IInsightsDialogModel; + + constructor( @IInstantiationService private _instantiationService: IInstantiationService) { } + + // query string + public show(input: IInsightsConfig, connectionProfile: IConnectionProfile): void { + if (!this._insightsDialogView) { + this._insightsDialogModel = new InsightsDialogModel(); + this._insightsDialogController = this._instantiationService.createInstance(InsightsDialogController, this._insightsDialogModel); + this._insightsDialogView = this._instantiationService.createInstance(InsightsDialogView, this._insightsDialogModel); + this._insightsDialogView.render(); + } else { + this._insightsDialogModel.reset(); + this._insightsDialogView.reset(); + } + + this._insightsDialogModel.insight = input.details; + this._insightsDialogController.update(input.details, connectionProfile); + this._insightsDialogView.open(input.details, connectionProfile); + } + + public close(): void { + this._insightsDialogView.close(); + } +} diff --git a/src/sql/parts/insights/node/insightsDialogController.ts b/src/sql/parts/insights/node/insightsDialogController.ts new file mode 100644 index 0000000000..ed3a54e186 --- /dev/null +++ b/src/sql/parts/insights/node/insightsDialogController.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { IInsightsConfigDetails } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import QueryRunner from 'sql/parts/query/execution/queryRunner'; +import * as Utils from 'sql/parts/connection/common/utils'; +import { IInsightsDialogModel } from 'sql/parts/insights/common/interfaces'; + +import { DbCellValue, IDbColumn, IResultMessage, QueryExecuteSubsetResult } from 'data'; + +import Severity from 'vs/base/common/severity'; +import * as types from 'vs/base/common/types'; +import * as pfs from 'vs/base/node/pfs'; +import * as nls from 'vs/nls'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { error } from 'sql/base/common/log'; + +export class InsightsDialogController { + private _queryRunner: QueryRunner; + private _connectionProfile: IConnectionProfile; + private _connectionUri: string; + private _columns: IDbColumn[]; + private _rows: DbCellValue[][]; + + constructor( + private _model: IInsightsDialogModel, + @IMessageService private _messageService: IMessageService, + @IErrorMessageService private _errorMessageService: IErrorMessageService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + ) { } + + public update(input: IInsightsConfigDetails, connectionProfile: IConnectionProfile): Thenable { + // execute string + if (typeof input === 'object') { + if (connectionProfile === undefined) { + this._messageService.show(Severity.Error, nls.localize("insightsInputError", "No Connection Profile was passed to insights flyout")); + return Promise.resolve(); + } + if (types.isStringArray(input.query)) { + return this.createQuery(input.query.join(' '), connectionProfile).catch(e => { + this._errorMessageService.showDialog(Severity.Error, nls.localize("insightsError", "Insights Error"), e); + }).then(() => undefined); + } else if (types.isString(input.query)) { + return this.createQuery(input.query, connectionProfile).catch(e => { + this._errorMessageService.showDialog(Severity.Error, nls.localize("insightsError", "Insights Error"), e); + }).then(() => undefined); + } else if (types.isString(input.queryFile)) { + return new Promise((resolve, reject) => { + pfs.readFile(input.queryFile).then( + buffer => { + this.createQuery(buffer.toString(), connectionProfile).catch(e => { + this._errorMessageService.showDialog(Severity.Error, nls.localize("insightsError", "Insights Error"), e); + }).then(() => resolve()); + }, + error => { + this._messageService.show(Severity.Error, nls.localize("insightsFileError", "There was an error reading the query file: ") + error); + resolve(); + } + ); + }); + } else { + error('Error reading details Query: ', input); + this._messageService.show(Severity.Error, nls.localize("insightsConfigError", "There was an error parsing the insight config; could not find query array/string or queryfile")); + return Promise.resolve(); + } + } + + return Promise.resolve(); + } + + private async createQuery(queryString: string, connectionProfile: IConnectionProfile): Promise { + if (this._queryRunner) { + if (!this._queryRunner.hasCompleted) { + await this._queryRunner.cancelQuery(); + } + try { + await this.createNewConnection(connectionProfile); + } catch (e) { + return Promise.reject(e); + } + this._queryRunner.uri = this._connectionUri; + } else { + try { + await this.createNewConnection(connectionProfile); + } catch (e) { + return Promise.reject(e); + } + this._queryRunner = this._instantiationService.createInstance(QueryRunner, this._connectionUri, undefined); + this.addQueryEventListeners(this._queryRunner); + } + + return this._queryRunner.runQuery(queryString); + } + + private async createNewConnection(connectionProfile: IConnectionProfile): Promise { + // determine if we need to create a new connection + if (!this._connectionProfile || connectionProfile.getOptionsKey() !== this._connectionProfile.getOptionsKey()) { + if (this._connectionProfile) { + try { + await this._connectionManagementService.disconnect(this._connectionUri); + } catch (e) { + return Promise.reject(e); + } + } + this._connectionProfile = connectionProfile; + this._connectionUri = Utils.generateUri(this._connectionProfile, 'insights'); + return this._connectionManagementService.connect(this._connectionProfile, this._connectionUri).then(result => undefined); + } + } + + private addQueryEventListeners(queryRunner: QueryRunner): void { + queryRunner.eventEmitter.on('complete', () => { + this.queryComplete().catch(error => { + this._errorMessageService.showDialog(Severity.Error, nls.localize("insightsError", "Insights Error"), error); + }); + }); + queryRunner.eventEmitter.on('message', (message: IResultMessage) => { + if (message.isError) { + this._errorMessageService.showDialog(Severity.Error, nls.localize("insightsError", "Insights Error"), message.message); + } + }); + } + + private async queryComplete(): Promise { + let batches = this._queryRunner.batchSets; + // currently only support 1 batch set 1 resultset + if (batches.length > 0) { + let batch = batches[0]; + if (batch.resultSetSummaries.length > 0 + && batch.resultSetSummaries[0].rowCount > 0 + ) { + let resultset = batch.resultSetSummaries[0]; + this._columns = resultset.columnInfo; + let rows: QueryExecuteSubsetResult; + try { + rows = await this._queryRunner.getQueryRows(0, resultset.rowCount, batch.id, resultset.id); + } catch (e) { + return Promise.reject(e); + } + this._rows = rows.resultSubset.rows; + this.updateModel(); + } + } + // TODO issue #2746 should ideally show a warning inside the dialog if have no data + } + + private updateModel(): void { + let data = this._rows.map(r => r.map(c => c.displayValue)); + let columns = this._columns.map(c => c.columnName); + + this._model.rows = data; + this._model.columns = columns; + } +} diff --git a/src/sql/parts/profiler/contrib/profiler.contribution.ts b/src/sql/parts/profiler/contrib/profiler.contribution.ts new file mode 100644 index 0000000000..4a4eaab322 --- /dev/null +++ b/src/sql/parts/profiler/contrib/profiler.contribution.ts @@ -0,0 +1,137 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import * as nls from 'vs/nls'; + +import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; +import { ProfilerEditor } from 'sql/parts/profiler/editor/profilerEditor'; +import { PROFILER_SESSION_TEMPLATE_SETTINGS, IProfilerSessionTemplate } from 'sql/parts/profiler/service/interfaces'; + +const profilerDescriptor = new EditorDescriptor( + ProfilerEditor.ID, + 'Profiler', + 'sql/parts/profiler/editor/profilerEditor', + 'ProfilerEditor' +); + +Registry.as(EditorExtensions.Editors) + .registerEditor(profilerDescriptor, [new SyncDescriptor(ProfilerInput)]); + +const profilerSessionTemplateSchema: IJSONSchema = { + description: nls.localize('profiler.settings.sessionTemplates', "Specifies session templates"), + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string' + } + } + }, + default: >[ + { + name: 'Standard', + events: [ + { + name: 'Audit Login', + optionalColumns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'ClientProcessID', 'SPID', 'StartTime', 'BinaryData'] + }, + { + name: 'Audit Logout', + optionalColumns: ['ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime'] + }, + { + name: 'ExistingConnection', + optionalColumns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime', 'BinaryData'] + }, + { + name: 'RPC:Completed', + optionalColumns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime', 'BinaryData'] + }, + { + name: 'SQL:BatchCompleted', + optionalColumns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime', 'BinaryData'] + }, + { + name: 'SQL:BatchStarting', + optionalColumns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'ClientProcessID', 'SPID', 'StartTime'] + } + ], + view: { + events: [ + { + name: 'Audit Login', + columns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'ClientProcessID', 'SPID', 'StartTime'] + }, + { + name: 'Audit Logout', + columns: ['ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime'] + }, + { + name: 'ExistingConnection', + columns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'ClientProcessID', 'SPID', 'StartTime'] + }, + { + name: 'RPC:Completed', + columns: ['ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime', 'BinaryData'] + }, + { + name: 'SQL:BatchCompleted', + columns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime', 'BinaryData'] + }, + { + name: 'SQL:BatchStarting', + columns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'ClientProcessID', 'SPID', 'StartTime'] + } + ] + } + }, + { + name: 'TSQL' + }, + { + name: 'Blank' + }, + { + name: 'SP_Counts' + }, + { + name: 'TQL_Duration' + }, + { + name: 'TSQL_Grouped' + }, + { + name: 'TSQL_Locks' + }, + { + name: 'TSQL_Replay' + }, + { + name: 'TSQL_SPs' + }, + { + name: 'Tuning' + } + ] +}; + +const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); +const dashboardConfig: IConfigurationNode = { + id: 'Profiler', + type: 'object', + properties: { + [PROFILER_SESSION_TEMPLATE_SETTINGS]: profilerSessionTemplateSchema + } +}; + +configurationRegistry.registerConfiguration(dashboardConfig); diff --git a/src/sql/parts/profiler/contrib/profilerActions.contribution.ts b/src/sql/parts/profiler/contrib/profilerActions.contribution.ts new file mode 100644 index 0000000000..66ba4f0b80 --- /dev/null +++ b/src/sql/parts/profiler/contrib/profilerActions.contribution.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { GlobalNewProfilerAction } from './profilerWorkbenchActions'; + +import { registerTask } from 'sql/platform/tasks/taskRegistry'; +import { NewProfilerAction } from './profilerActions'; + +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import * as nls from 'vs/nls'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; + +// Contribute Global Actions +const category = nls.localize('profilerCategory', "Profiler"); + +const newProfilerSchema: IJSONSchema = { + description: nls.localize('carbon.actions.newProfiler', 'Open up a new profiler window'), + type: 'null', + default: null +}; + +if (process.env['VSCODE_DEV']) { + const registry = Registry.as(ActionExtensions.WorkbenchActions); + registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewProfilerAction, GlobalNewProfilerAction.ID, GlobalNewProfilerAction.LABEL), 'Profiler: New Profiler', category); + + registerTask('new-profiler', '', newProfilerSchema, NewProfilerAction); +} diff --git a/src/sql/parts/profiler/contrib/profilerActions.ts b/src/sql/parts/profiler/contrib/profilerActions.ts new file mode 100644 index 0000000000..f8b9aec298 --- /dev/null +++ b/src/sql/parts/profiler/contrib/profilerActions.ts @@ -0,0 +1,244 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IProfilerService } from 'sql/parts/profiler/service/interfaces'; +import { IProfilerController } from 'sql/parts/profiler/editor/controller/interfaces'; +import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; +import { ITaskActionContext, TaskAction } from 'sql/workbench/common/actions'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; +import { IEditorAction } from 'vs/editor/common/editorCommon'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +export class ProfilerConnect extends Action { + public static ID = 'profiler.connect'; + public static LABEL = nls.localize('connect', "Connect"); + + private _connected: boolean = false; + + constructor( + id: string, label: string, + @IProfilerService private _profilerService: IProfilerService + ) { + super(id, label, 'connect'); + } + + public run(input: ProfilerInput): TPromise { + this.enabled = false; + if (!this._connected) { + return TPromise.wrap(this._profilerService.connectSession(input.id).then(() => { + this.enabled = true; + this.connected = true; + input.state.change({ isConnected: true, isRunning: false, isPaused: false, isStopped: true }); + return true; + })); + } else { + return TPromise.wrap(this._profilerService.disconnectSession(input.id).then(() => { + this.enabled = true; + this.connected = false; + input.state.change({ isConnected: false, isRunning: false, isPaused: false, isStopped: false }); + return true; + })); + } + } + + public set connected(value: boolean) { + this._connected = value; + this._setClass(value ? 'disconnect' : 'connect'); + this._setLabel(value ? nls.localize('disconnect', 'Disconnected') : nls.localize('connect', "Connect")); + } + + public get connected(): boolean { + return this._connected; + } +} + +export class ProfilerStart extends Action { + public static ID = 'profiler.start'; + public static LABEL = nls.localize('start', "Start"); + + constructor( + id: string, label: string, + @IProfilerService private _profilerService: IProfilerService + ) { + super(id, label, 'start'); + } + + public run(input: ProfilerInput): TPromise { + this.enabled = false; + return TPromise.wrap(this._profilerService.startSession(input.id).then(() => { + input.state.change({ isRunning: true, isStopped: false, isPaused: false }); + return true; + })); + } + +} + +export class ProfilerPause extends Action { + public static ID = 'profiler.pause'; + public static LABEL = nls.localize('pause', "Pause"); + + constructor( + id: string, label: string, + @IProfilerService private _profilerService: IProfilerService + ) { + super(id, label, 'stop'); + } + + public run(input: ProfilerInput): TPromise { + this.enabled = false; + return TPromise.wrap(this._profilerService.pauseSession(input.id).then(() => { + input.state.change({ isPaused: true, isStopped: false, isRunning: false }); + return true; + })); + } +} + +export class ProfilerStop extends Action { + public static ID = 'profiler.stop'; + public static LABEL = nls.localize('stop', "Stop"); + + constructor( + id: string, label: string, + @IProfilerService private _profilerService: IProfilerService + ) { + super(id, label, 'stop'); + } + + public run(input: ProfilerInput): TPromise { + this.enabled = false; + return TPromise.wrap(this._profilerService.stopSession(input.id).then(() => { + input.state.change({ isStopped: true, isPaused: false, isRunning: false }); + return true; + })); + } +} + +export class ProfilerClear extends Action { + public static ID = 'profiler.clear'; + public static LABEL = nls.localize('profiler.clear', "Clear Data"); + + constructor(id: string, label: string) { + super(id, label, 'stop'); + } + + run(input: ProfilerInput): TPromise { + input.data.clear(); + return TPromise.as(null); + } +} + +export class ProfilerAutoScroll extends Action { + public static ID = 'profiler.autoscroll'; + public static LABEL = nls.localize('profiler.toggleAutoscroll', "Toggle Auto Scroll"); + + constructor(id: string, label: string) { + super(id, label, 'stop'); + } + + run(input: ProfilerInput): TPromise { + this.checked = !this.checked; + input.state.change({ autoscroll: this.checked }); + return TPromise.as(true); + } +} + +export class ProfilerCollapsablePanelAction extends Action { + public static ID = 'profiler.toggleCollapsePanel'; + public static LABEL = nls.localize('profiler.toggleCollapsePanel', "Toggle Collapsed Panel"); + + private _collapsed: boolean; + + constructor(id: string, label: string) { + super(id, label, 'minimize-panel-action'); + } + + public run(input: ProfilerInput): TPromise { + this.collapsed = !this._collapsed; + input.state.change({ isPanelCollapsed: this._collapsed }); + return TPromise.as(true); + } + + set collapsed(val: boolean) { + this._collapsed = val === false ? false : true; + this._setClass(this._collapsed ? 'maximize-panel-action' : 'minimize-panel-action'); + } +} + +export class ProfilerEditColumns extends Action { + public static ID = 'profiler.'; + public static LABEL = nls.localize('profiler.editColumns', "Edit Columns"); + + constructor( + id: string, label: string, + @IProfilerService private _profilerService: IProfilerService + ) { + super(id, label); + } + + public run(input: ProfilerInput): TPromise { + return TPromise.wrap(this._profilerService.launchColumnEditor(input)).then(() => true); + } +} + +export class ProfilerFindNext implements IEditorAction { + public readonly id = 'profiler.findNext'; + public readonly label = nls.localize('profiler.findNext', "Find Next String"); + public readonly alias = ''; + + constructor(private profiler: IProfilerController) { } + + run(): TPromise { + this.profiler.findNext(); + return TPromise.as(null); + } + + isSupported(): boolean { + return true; + } +} + +export class ProfilerFindPrevious implements IEditorAction { + public readonly id = 'profiler.findPrevious'; + public readonly label = nls.localize('profiler.findPrevious', "Find Previous String"); + public readonly alias = ''; + + constructor(private profiler: IProfilerController) { } + + run(): TPromise { + this.profiler.findPrevious(); + return TPromise.as(null); + } + + isSupported(): boolean { + return true; + } +} + +export class NewProfilerAction extends TaskAction { + public static ID = 'newProfiler'; + public static LABEL = nls.localize('newProfiler', 'New Profiler'); + public static ICON = 'profile'; + + constructor( + id: string, label: string, icon: string, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(id, label, icon); + } + + run(actionContext: ITaskActionContext): TPromise { + let profilerInput = this._instantiationService.createInstance(ProfilerInput, actionContext.profile); + return this._editorService.openEditor(profilerInput, { pinned: true }, false).then(() => { + return TPromise.as(true); + }); + } +} \ No newline at end of file diff --git a/src/sql/parts/profiler/contrib/profilerWorkbenchActions.ts b/src/sql/parts/profiler/contrib/profilerWorkbenchActions.ts new file mode 100644 index 0000000000..098d23ac20 --- /dev/null +++ b/src/sql/parts/profiler/contrib/profilerWorkbenchActions.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; + +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +export class GlobalNewProfilerAction extends Action { + public static ID = 'explorer.newProfiler'; + public static LABEL = nls.localize('newProfiler', "New Profiler"); + + constructor( + id: string, label: string, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(id, label); + } + + run(context?: any): TPromise { + let profilerInput = this._instantiationService.createInstance(ProfilerInput, context ? context.connectionProfile : undefined); + return this._editorService.openEditor(profilerInput, { pinned: true }, false).then(() => TPromise.as(true)); + } +} diff --git a/src/sql/parts/profiler/dialog/media/profilerDialog.css b/src/sql/parts/profiler/dialog/media/profilerDialog.css new file mode 100644 index 0000000000..e36eaea52b --- /dev/null +++ b/src/sql/parts/profiler/dialog/media/profilerDialog.css @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.profiler-column-tree { + height: calc(100% - 25px); + width: 100%; +} + +.tree-row > * { + display: inline-block; +} diff --git a/src/sql/parts/profiler/dialog/profilerColumnEditorDialog.ts b/src/sql/parts/profiler/dialog/profilerColumnEditorDialog.ts new file mode 100644 index 0000000000..c3c084a552 --- /dev/null +++ b/src/sql/parts/profiler/dialog/profilerColumnEditorDialog.ts @@ -0,0 +1,409 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +import 'vs/css!./media/profilerDialog'; + +import { Modal } from 'sql/base/browser/ui/modal/modal'; +import { attachModalDialogStyler } from 'sql/common/theme/styler'; +import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import * as nls from 'vs/nls'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { Builder } from 'vs/base/browser/builder'; +import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import * as DOM from 'vs/base/browser/dom'; +import { IDataSource, ITree, IRenderer } from 'vs/base/parts/tree/browser/tree'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import Event, { Emitter } from 'vs/base/common/event'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; + +class EventItem { + + constructor( + private _name: string, + private _parent: SessionItem, + private _columns?: Array, + ) { + if (!_columns) { + this._columns = new Array(); + } + } + + public hasChildren(): boolean { + return this._columns && this._columns.length > 0; + } + + public getChildren(): Array { + return this._columns; + } + + public get id(): string { + return this._name; + } + + public addColumn(...columns: Array) { + this._columns = this._columns.concat(columns); + } + + public get parent(): SessionItem { + return this._parent; + } + + public get selected(): boolean { + return this._columns.every(i => i.selected); + } + + public set selected(val: boolean) { + this._columns.forEach(i => i.selected = val); + } + + public get indeterminate(): boolean { + return this._columns.some(i => i.selected) && !this.selected; + } +} + +class ColumnItem { + public selected: boolean; + public readonly indeterminate = false; + constructor( + private _name: string, + private _parent: EventItem + ) { } + + public get id(): string { + return this._name; + } + + public get parent(): EventItem { + return this._parent; + } +} + +class ColumnSortedColumnItem { + constructor( + private _name: string, + private _parent: SessionItem + ) { } + + public get id(): string { + return this._name; + } + + public get parent(): SessionItem { + return this._parent; + } + + public get selected(): boolean { + return this._parent.getUnsortedChildren() + .every(e => e.getChildren().filter(c => c.id === this.id) + .every(c => c.selected)); + } + + public set selected(val: boolean) { + this._parent.getUnsortedChildren() + .forEach(e => e.getChildren() + .filter(c => c.id === this.id) + .forEach(c => c.selected = val)); + } + + public get indeterminate(): boolean { + return this._parent.getUnsortedChildren() + .some(e => e.getChildren() + .filter(c => c.id === this.id) + .some(c => c.selected)) + && !this.selected; + } +} + +class SessionItem { + private _sortedColumnItems: Array = []; + constructor( + private _name: string, + private _sort: 'event' | 'column', + private _events?: Array + ) { + if (!_events) { + this._events = new Array(); + } else { + _events.forEach(e => { + e.getChildren().forEach(c => { + if (!this._sortedColumnItems.some(i => i.id === c.id)) { + this._sortedColumnItems.push(new ColumnSortedColumnItem(c.id, this)); + } + }); + }); + } + } + + public get id(): string { + return this._name; + } + + public hasChildren(): boolean { + if (this._sort === 'event') { + return this._events && this._events.length > 0; + } else { + return this._events && this._events.some(i => i.hasChildren()); + } + } + + public getUnsortedChildren(): Array { + return this._events; + } + + public getChildren(): Array { + if (this._sort === 'event') { + return this._events; + } else { + return this._sortedColumnItems; + } + } + + public addEvents(...events: Array) { + this._events = this._events.concat(events); + events.forEach(e => { + e.getChildren().forEach(c => { + if (!this._sortedColumnItems.some(i => i.id === c.id)) { + this._sortedColumnItems.push(new ColumnSortedColumnItem(c.id, this)); + } + }); + }); + } + + public changeSort(type: 'event' | 'column'): void { + this._sort = type; + } +} + +class TreeRenderer implements IRenderer { + + private _onSelectedChange = new Emitter(); + public onSelectedChange: Event = this._onSelectedChange.event; + + getHeight(tree: ITree, element: any): number { + return 22; + } + + getTemplateId(tree: ITree, element: any): string { + if (element instanceof SessionItem) { + return 'session'; + } else if (element instanceof EventItem) { + return 'event'; + } else if (element instanceof ColumnItem) { + return 'column'; + } else if (element instanceof ColumnSortedColumnItem) { + return 'columnSorted'; + } else { + return undefined; + } + } + + renderTemplate(tree: ITree, templateId: string, container: HTMLElement): RenderTemplate { + let data = Object.create(null); + let row = document.createElement('div'); + row.className = 'tree-row'; + DOM.append(container, row); + data.toDispose = []; + data.checkbox = document.createElement('input'); + DOM.append(row, data.checkbox); + data.checkbox.type = 'checkbox'; + data.toDispose.push(DOM.addStandardDisposableListener(data.checkbox, 'change', () => { + data.context.selected = !data.context.selected; + this._onSelectedChange.fire(data.context); + })); + data.label = document.createElement('div'); + DOM.append(row, data.label); + return data; + } + + renderElement(tree: ITree, element: any, templateId: string, templateData: RenderTemplate): void { + templateData.context = element; + templateData.label.innerText = element.id; + templateData.checkbox.checked = element.selected; + templateData.checkbox.indeterminate = element.indeterminate; + } + + disposeTemplate(tree: ITree, templateId: string, templateData: RenderTemplate): void { + dispose(templateData.toDispose); + } +} + +interface RenderTemplate { + label: HTMLElement; + toDispose: Array; + checkbox: HTMLInputElement; + context?: any; +} + +class TreeDataSource implements IDataSource { + + getId(tree: ITree, element: any): string { + if (element instanceof EventItem) { + return element.parent.id + element.id; + } else if (element instanceof ColumnItem) { + return element.parent.parent.id + element.parent.id + element.id; + } else if (element instanceof SessionItem) { + return element.id; + } else if (element instanceof ColumnSortedColumnItem) { + return element.id; + } else { + return undefined; + } + } + + hasChildren(tree: ITree, element: any): boolean { + if (element instanceof SessionItem) { + return element.hasChildren(); + } else if (element instanceof EventItem) { + return element.hasChildren(); + } else { + return undefined; + } + } + + getChildren(tree: ITree, element: any): TPromise> { + if (element instanceof EventItem) { + return TPromise.as(element.getChildren()); + } else if (element instanceof SessionItem) { + return TPromise.as(element.getChildren()); + } else { + return TPromise.as(null); + } + } + + getParent(tree: ITree, element: any): TPromise { + if (element instanceof ColumnItem) { + return TPromise.as(element.parent); + } else if (element instanceof EventItem) { + return TPromise.as(element.parent); + } else if (element instanceof ColumnSortedColumnItem) { + return TPromise.as(element.parent); + } else { + return TPromise.as(null); + } + } + + shouldAutoexpand?(tree: ITree, element: any): boolean { + return false; + } +} + +export class ProfilerColumnEditorDialog extends Modal { + + private _selectBox: SelectBox; + private _selectedValue: number = 0; + private readonly _options = [ + nls.localize('eventSort', "Sort by event"), + nls.localize('nameColumn', "Sort by column") + ]; + private _tree: Tree; + private _input: ProfilerInput; + private _element: SessionItem; + private _treeContainer: HTMLElement; + + constructor( + @IPartService _partService: IPartService, + @IThemeService private _themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(nls.localize('profiler', 'Profiler'), TelemetryKeys.Profiler, _partService, telemetryService, contextKeyService); + } + + public render(): void { + super.render(); + this._register(attachModalDialogStyler(this, this._themeService)); + this.addFooterButton(nls.localize('ok', "OK"), () => this.onAccept(undefined)); + this.addFooterButton(nls.localize('cancel', "Cancel"), () => this.onClose(undefined)); + } + + protected renderBody(container: HTMLElement): void { + let builder = new Builder(container); + builder.div({}, b => { + this._selectBox = new SelectBox(this._options, 0); + this._selectBox.render(b.getHTMLElement()); + this._register(this._selectBox.onDidSelect(e => { + this._selectedValue = e.index; + this._element.changeSort(e.index === 0 ? 'event' : 'column'); + this._tree.refresh(this._element, true); + })); + }); + + builder.div({ 'class': 'profiler-column-tree' }, b => { + this._treeContainer = b.getHTMLElement(); + let renderer = new TreeRenderer(); + this._tree = new Tree(this._treeContainer, { dataSource: new TreeDataSource(), renderer }); + this._register(renderer.onSelectedChange(e => this._tree.refresh(e, true))); + this._register(attachListStyler(this._tree, this._themeService)); + }); + } + + public open(input: ProfilerInput): void { + super.show(); + this._input = input; + this._updateList(); + } + + protected onAccept(e: StandardKeyboardEvent): void { + this._updateInput(); + super.onAccept(e); + } + + private _updateInput(): void { + this._element.getUnsortedChildren().forEach(e => { + let origEvent = this._input.sessionTemplate.view.events.find(i => i.name === e.id); + if (e.indeterminate) { + e.getChildren().forEach(c => { + if (origEvent.columns.includes(c.id) && !c.selected) { + origEvent.columns = origEvent.columns.filter(i => i !== c.id); + } else if (!origEvent.columns.includes(c.id) && c.selected) { + origEvent.columns.push(c.id); + } + }); + } else { + origEvent.columns = e.getChildren() + .filter(c => c.selected) + .map(c => c.id); + } + }); + let newColumns = this._input.sessionTemplate.view.events.reduce>((p, e) => { + e.columns.forEach(c => { + if (!p.includes(c)) { + p.push(c); + } + }); + return p; + }, []); + newColumns.unshift('EventClass'); + this._input.setColumns(newColumns); + } + + private _updateList(): void { + this._element = new SessionItem(this._input.sessionTemplate.name, this._selectedValue === 0 ? 'event' : 'column'); + this._input.sessionTemplate.events.forEach(item => { + let event = new EventItem(item.name, this._element); + item.optionalColumns.forEach(col => { + let column = new ColumnItem(col, event); + column.selected = this._input.sessionTemplate.view.events.find(i => i.name === event.id).columns.includes(col); + event.addColumn(column); + }); + this._element.addEvents(event); + }); + this._tree.setInput(this._element); + this._tree.layout(DOM.getTotalHeight(this._treeContainer)); + } + + protected layout(height?: number): void { + this._tree.layout(DOM.getContentHeight(this._treeContainer)); + } + +} diff --git a/src/sql/parts/profiler/editor/controller/interfaces.ts b/src/sql/parts/profiler/editor/controller/interfaces.ts new file mode 100644 index 0000000000..23c06c4a54 --- /dev/null +++ b/src/sql/parts/profiler/editor/controller/interfaces.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface IProfilerController { + findNext(): void; + findPrevious(): void; +} diff --git a/src/sql/parts/profiler/editor/controller/profilerFindWidget.ts b/src/sql/parts/profiler/editor/controller/profilerFindWidget.ts new file mode 100644 index 0000000000..0c3782031a --- /dev/null +++ b/src/sql/parts/profiler/editor/controller/profilerFindWidget.ts @@ -0,0 +1,647 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!vs/editor/contrib/find/browser/findWidget'; +import * as nls from 'vs/nls'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import * as platform from 'vs/base/common/platform'; +import * as strings from 'vs/base/common/strings'; +import * as dom from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput'; +import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { Sash, IHorizontalSashLayoutProvider, ISashEvent, Orientation } from 'vs/base/browser/ui/sash/sash'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { FIND_IDS, MATCHES_LIMIT } from 'vs/editor/contrib/find/common/findModel'; +import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/common/findState'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/common/findController'; +import { ITheme, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; +import { Color } from 'vs/base/common/color'; +import { editorFindRangeHighlight, editorFindMatch, editorFindMatchHighlight, activeContrastBorder, contrastBorder, inputBackground, editorWidgetBackground, inputActiveOptionBorder, widgetShadow, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, errorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IEditorAction } from 'vs/editor/common/editorCommon'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); +const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); +const NLS_PREVIOUS_MATCH_BTN_LABEL = nls.localize('label.previousMatchButton', "Previous match"); +const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next match"); +const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close"); +const NLS_MATCHES_COUNT_LIMIT_TITLE = nls.localize('title.matchesCountLimit', "Only the first 999 results are highlighted, but all find operations work on the entire text."); +const NLS_MATCHES_LOCATION = nls.localize('label.matchesLocation', "{0} of {1}"); +const NLS_NO_RESULTS = nls.localize('label.noResults', "No Results"); + +const FIND_WIDGET_INITIAL_WIDTH = 411; +const PART_WIDTH = 275; +const FIND_INPUT_AREA_WIDTH = PART_WIDTH - 54; + +let MAX_MATCHES_COUNT_WIDTH = 69; + +export const ACTION_IDS = { + FIND_NEXT: 'findNext', + FIND_PREVIOUS: 'findPrev' +}; + +export interface ITableController { + focus(): void; + getConfiguration(): any; + layoutOverlayWidget(widget: IOverlayWidget): void; + addOverlayWidget(widget: IOverlayWidget): void; + getAction(id: string): IEditorAction; + onDidChangeConfiguration(fn: (e: IConfigurationChangedEvent) => void): IDisposable; +} + +export interface IConfigurationChangedEvent { + layoutInfo?: boolean; +} + +export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider { + private static ID = 'editor.contrib.findWidget'; + private _tableController: ITableController; + private _state: FindReplaceState; + private _contextViewProvider: IContextViewProvider; + private _keybindingService: IKeybindingService; + + private _domNode: HTMLElement; + private _findInput: FindInput; + + private _matchesCount: HTMLElement; + private _prevBtn: SimpleButton; + private _nextBtn: SimpleButton; + private _closeBtn: SimpleButton; + + private _isVisible: boolean; + + private _focusTracker: dom.IFocusTracker; + private _findInputFocussed: IContextKey; + + private _resizeSash: Sash; + + constructor( + tableController: ITableController, + state: FindReplaceState, + contextViewProvider: IContextViewProvider, + keybindingService: IKeybindingService, + contextKeyService: IContextKeyService, + themeService: IThemeService + ) { + super(); + this._tableController = tableController; + this._state = state; + this._contextViewProvider = contextViewProvider; + this._keybindingService = keybindingService; + + this._isVisible = false; + + this._register(this._state.addChangeListener((e) => this._onStateChanged(e))); + this._buildDomNode(); + this._updateButtons(); + + let checkEditorWidth = () => { + let editorWidth = this._tableController.getConfiguration().layoutInfo.width; + let collapsedFindWidget = false; + let reducedFindWidget = false; + let narrowFindWidget = false; + let widgetWidth = dom.getTotalWidth(this._domNode); + + if (widgetWidth > FIND_WIDGET_INITIAL_WIDTH) { + // as the widget is resized by users, we may need to change the max width of the widget as the editor width changes. + this._domNode.style.maxWidth = `${editorWidth - 28 - 15}px`; + return; + } + + if (FIND_WIDGET_INITIAL_WIDTH + 28 >= editorWidth) { + reducedFindWidget = true; + } + if (FIND_WIDGET_INITIAL_WIDTH + 28 - MAX_MATCHES_COUNT_WIDTH >= editorWidth) { + narrowFindWidget = true; + } + if (FIND_WIDGET_INITIAL_WIDTH + 28 - MAX_MATCHES_COUNT_WIDTH >= editorWidth + 50) { + collapsedFindWidget = true; + } + dom.toggleClass(this._domNode, 'collapsed-find-widget', collapsedFindWidget); + dom.toggleClass(this._domNode, 'narrow-find-widget', narrowFindWidget); + dom.toggleClass(this._domNode, 'reduced-find-widget', reducedFindWidget); + + if (!narrowFindWidget && !collapsedFindWidget) { + // the minimal left offset of findwidget is 15px. + this._domNode.style.maxWidth = `${editorWidth - 28 - 15}px`; + } + + }; + checkEditorWidth(); + + this._register(this._tableController.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { + if (e.layoutInfo) { + checkEditorWidth(); + } + })); + + this._findInputFocussed = CONTEXT_FIND_INPUT_FOCUSED.bindTo(contextKeyService); + this._focusTracker = this._register(dom.trackFocus(this._findInput.inputBox.inputElement)); + this._focusTracker.addFocusListener(() => { + this._findInputFocussed.set(true); + }); + this._focusTracker.addBlurListener(() => { + this._findInputFocussed.set(false); + }); + + this._tableController.addOverlayWidget(this); + + this._applyTheme(themeService.getTheme()); + this._register(themeService.onThemeChange(this._applyTheme.bind(this))); + } + + // ----- IOverlayWidget API + + public getId(): string { + return FindWidget.ID; + } + + public getDomNode(): HTMLElement { + return this._domNode; + } + + public getPosition(): IOverlayWidgetPosition { + if (this._isVisible) { + return { + preference: OverlayWidgetPositionPreference.TOP_RIGHT_CORNER + }; + } + return null; + } + + // ----- React to state changes + + private _onStateChanged(e: FindReplaceStateChangedEvent): void { + if (e.searchString) { + this._findInput.setValue(this._state.searchString); + this._updateButtons(); + } + if (e.isRevealed) { + if (this._state.isRevealed) { + this._reveal(true); + } else { + this._hide(true); + } + } + if (e.isRegex) { + this._findInput.setRegex(this._state.isRegex); + } + if (e.wholeWord) { + this._findInput.setWholeWords(this._state.wholeWord); + } + if (e.matchCase) { + this._findInput.setCaseSensitive(this._state.matchCase); + } + if (e.searchString || e.matchesCount || e.matchesPosition) { + let showRedOutline = (this._state.searchString.length > 0 && this._state.matchesCount === 0); + dom.toggleClass(this._domNode, 'no-results', showRedOutline); + + this._updateMatchesCount(); + } + } + + private _updateMatchesCount(): void { + this._matchesCount.style.minWidth = MAX_MATCHES_COUNT_WIDTH + 'px'; + if (this._state.matchesCount >= MATCHES_LIMIT) { + this._matchesCount.title = NLS_MATCHES_COUNT_LIMIT_TITLE; + } else { + this._matchesCount.title = ''; + } + + // remove previous content + if (this._matchesCount.firstChild) { + this._matchesCount.removeChild(this._matchesCount.firstChild); + } + + let label: string; + if (this._state.matchesCount > 0) { + let matchesCount: string = String(this._state.matchesCount); + if (this._state.matchesCount >= MATCHES_LIMIT) { + matchesCount += '+'; + } + let matchesPosition: string = String(this._state.matchesPosition); + if (matchesPosition === '0') { + matchesPosition = '?'; + } + label = strings.format(NLS_MATCHES_LOCATION, matchesPosition, matchesCount); + } else { + label = NLS_NO_RESULTS; + } + this._matchesCount.appendChild(document.createTextNode(label)); + + MAX_MATCHES_COUNT_WIDTH = Math.max(MAX_MATCHES_COUNT_WIDTH, this._matchesCount.clientWidth); + } + + // ----- actions + + private _updateButtons(): void { + this._findInput.setEnabled(this._isVisible); + this._closeBtn.setEnabled(this._isVisible); + + let findInputIsNonEmpty = (this._state.searchString.length > 0); + this._prevBtn.setEnabled(this._isVisible && findInputIsNonEmpty); + this._nextBtn.setEnabled(this._isVisible && findInputIsNonEmpty); + } + + private _reveal(animate: boolean): void { + if (!this._isVisible) { + this._isVisible = true; + + this._updateButtons(); + + setTimeout(() => { + dom.addClass(this._domNode, 'visible'); + this._domNode.setAttribute('aria-hidden', 'false'); + if (!animate) { + dom.addClass(this._domNode, 'noanimation'); + setTimeout(() => { + dom.removeClass(this._domNode, 'noanimation'); + }, 200); + } + }, 0); + this._tableController.layoutOverlayWidget(this); + } + } + + private _hide(focusTheEditor: boolean): void { + if (this._isVisible) { + this._isVisible = false; + + this._updateButtons(); + + dom.removeClass(this._domNode, 'visible'); + this._domNode.setAttribute('aria-hidden', 'true'); + if (focusTheEditor) { + this._tableController.focus(); + } + this._tableController.layoutOverlayWidget(this); + } + } + + private _applyTheme(theme: ITheme) { + let inputStyles: IFindInputStyles = { + inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), + inputBackground: theme.getColor(inputBackground), + inputForeground: theme.getColor(inputForeground), + inputBorder: theme.getColor(inputBorder), + inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground), + inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder), + inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground), + inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder), + inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground), + inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder) + }; + this._findInput.style(inputStyles); + } + + // ----- Public + + public focusFindInput(): void { + this._findInput.select(); + // Edge browser requires focus() in addition to select() + this._findInput.focus(); + } + + public highlightFindOptions(): void { + this._findInput.highlightFindOptions(); + } + + private _onFindInputMouseDown(e: IMouseEvent): void { + // on linux, middle key does pasting. + if (e.middleButton) { + e.stopPropagation(); + } + } + + private _onFindInputKeyDown(e: IKeyboardEvent): void { + + if (e.equals(KeyCode.Enter)) { + this._tableController.getAction(ACTION_IDS.FIND_NEXT).run().done(null, onUnexpectedError); + e.preventDefault(); + return; + } + + if (e.equals(KeyMod.Shift | KeyCode.Enter)) { + this._tableController.getAction(ACTION_IDS.FIND_NEXT).run().done(null, onUnexpectedError); + e.preventDefault(); + return; + } + + if (e.equals(KeyCode.Tab)) { + this._findInput.focusOnCaseSensitive(); + e.preventDefault(); + return; + } + + if (e.equals(KeyMod.CtrlCmd | KeyCode.DownArrow)) { + this._tableController.focus(); + e.preventDefault(); + return; + } + } + + // ----- sash + public getHorizontalSashTop(sash: Sash): number { + return 0; + } + public getHorizontalSashLeft?(sash: Sash): number { + return 0; + } + public getHorizontalSashWidth?(sash: Sash): number { + return 500; + } + + // ----- initialization + + private _keybindingLabelFor(actionId: string): string { + let kb = this._keybindingService.lookupKeybinding(actionId); + if (!kb) { + return ''; + } + return ` (${kb.getLabel()})`; + } + + private _buildFindPart(): HTMLElement { + // Find input + this._findInput = this._register(new FindInput(null, this._contextViewProvider, { + width: FIND_INPUT_AREA_WIDTH, + label: NLS_FIND_INPUT_LABEL, + placeholder: NLS_FIND_INPUT_PLACEHOLDER, + appendCaseSensitiveLabel: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand), + appendWholeWordsLabel: this._keybindingLabelFor(FIND_IDS.ToggleWholeWordCommand), + appendRegexLabel: this._keybindingLabelFor(FIND_IDS.ToggleRegexCommand), + validation: (value: string): InputBoxMessage => { + if (value.length === 0) { + return null; + } + if (!this._findInput.getRegex()) { + return null; + } + try { + /* tslint:disable:no-unused-expression */ + new RegExp(value); + /* tslint:enable:no-unused-expression */ + return null; + } catch (e) { + return { content: e.message }; + } + } + })); + this._findInput.setRegex(!!this._state.isRegex); + this._findInput.setCaseSensitive(!!this._state.matchCase); + this._findInput.setWholeWords(!!this._state.wholeWord); + this._register(this._findInput.onKeyDown((e) => this._onFindInputKeyDown(e))); + this._register(this._findInput.onInput(() => { + this._state.change({ searchString: this._findInput.getValue() }, true); + })); + this._register(this._findInput.onDidOptionChange(() => { + this._state.change({ + isRegex: this._findInput.getRegex(), + wholeWord: this._findInput.getWholeWords(), + matchCase: this._findInput.getCaseSensitive() + }, true); + })); + if (platform.isLinux) { + this._register(this._findInput.onMouseDown((e) => this._onFindInputMouseDown(e))); + } + + this._matchesCount = document.createElement('div'); + this._matchesCount.className = 'matchesCount'; + this._updateMatchesCount(); + + // Previous button + this._prevBtn = this._register(new SimpleButton({ + label: NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.PreviousMatchFindAction), + className: 'previous', + onTrigger: () => { + this._tableController.getAction(ACTION_IDS.FIND_PREVIOUS).run().done(null, onUnexpectedError); + }, + onKeyDown: (e) => { } + })); + + // Next button + this._nextBtn = this._register(new SimpleButton({ + label: NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.NextMatchFindAction), + className: 'next', + onTrigger: () => { + this._tableController.getAction(ACTION_IDS.FIND_NEXT).run().done(null, onUnexpectedError); + }, + onKeyDown: (e) => { } + })); + + let findPart = document.createElement('div'); + findPart.className = 'find-part'; + findPart.appendChild(this._findInput.domNode); + findPart.appendChild(this._matchesCount); + findPart.appendChild(this._prevBtn.domNode); + findPart.appendChild(this._nextBtn.domNode); + + // Close button + this._closeBtn = this._register(new SimpleButton({ + label: NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.CloseFindWidgetCommand), + className: 'close-fw', + onTrigger: () => { + this._state.change({ isRevealed: false, searchScope: null }, false); + }, + onKeyDown: () => { } + })); + + findPart.appendChild(this._closeBtn.domNode); + + return findPart; + } + + private _buildDomNode(): void { + // Find part + let findPart = this._buildFindPart(); + + // Widget + this._domNode = document.createElement('div'); + this._domNode.className = 'editor-widget find-widget'; + this._domNode.setAttribute('aria-hidden', 'true'); + + this._domNode.appendChild(findPart); + + this._buildSash(); + } + + private _buildSash() { + this._resizeSash = new Sash(this._domNode, this, { orientation: Orientation.VERTICAL }); + let originalWidth = FIND_WIDGET_INITIAL_WIDTH; + + this._register(this._resizeSash.addListener('start', (e: ISashEvent) => { + originalWidth = dom.getTotalWidth(this._domNode); + })); + + this._register(this._resizeSash.addListener('change', (evt: ISashEvent) => { + let width = originalWidth + evt.startX - evt.currentX; + + if (width < FIND_WIDGET_INITIAL_WIDTH) { + // narrow down the find widget should be handled by CSS. + return; + } + + let maxWidth = parseFloat(dom.getComputedStyle(this._domNode).maxWidth) || 0; + if (width > maxWidth) { + return; + } + this._domNode.style.width = `${width}px`; + })); + } +} + +interface ISimpleCheckboxOpts { + parent: HTMLElement; + title: string; + onChange: () => void; +} + +class SimpleCheckbox extends Widget { + + private static _COUNTER = 0; + + private _opts: ISimpleCheckboxOpts; + private _domNode: HTMLElement; + private _checkbox: HTMLInputElement; + private _label: HTMLLabelElement; + + constructor(opts: ISimpleCheckboxOpts) { + super(); + this._opts = opts; + + this._domNode = document.createElement('div'); + this._domNode.className = 'monaco-checkbox'; + this._domNode.title = this._opts.title; + this._domNode.tabIndex = 0; + + this._checkbox = document.createElement('input'); + this._checkbox.type = 'checkbox'; + this._checkbox.className = 'checkbox'; + this._checkbox.id = 'checkbox-' + SimpleCheckbox._COUNTER++; + this._checkbox.tabIndex = -1; + + this._label = document.createElement('label'); + this._label.className = 'label'; + // Connect the label and the checkbox. Checkbox will get checked when the label recieves a click. + this._label.htmlFor = this._checkbox.id; + this._label.tabIndex = -1; + + this._domNode.appendChild(this._checkbox); + this._domNode.appendChild(this._label); + + this._opts.parent.appendChild(this._domNode); + + this.onchange(this._checkbox, (e) => { + this._opts.onChange(); + }); + } + + public get domNode(): HTMLElement { + return this._domNode; + } + + public get checked(): boolean { + return this._checkbox.checked; + } + + public set checked(newValue: boolean) { + this._checkbox.checked = newValue; + } + + public focus(): void { + this._checkbox.focus(); + } + + private enable(): void { + this._checkbox.removeAttribute('disabled'); + } + + private disable(): void { + this._checkbox.disabled = true; + } + + public setEnabled(enabled: boolean): void { + if (enabled) { + this.enable(); + this.domNode.tabIndex = 0; + } else { + this.disable(); + this.domNode.tabIndex = -1; + } + } +} + +interface ISimpleButtonOpts { + label: string; + className: string; + onTrigger: () => void; + onKeyDown: (e: IKeyboardEvent) => void; +} + +class SimpleButton extends Widget { + + private _opts: ISimpleButtonOpts; + private _domNode: HTMLElement; + + constructor(opts: ISimpleButtonOpts) { + super(); + this._opts = opts; + + this._domNode = document.createElement('div'); + this._domNode.title = this._opts.label; + this._domNode.tabIndex = 0; + this._domNode.className = 'button ' + this._opts.className; + this._domNode.setAttribute('role', 'button'); + this._domNode.setAttribute('aria-label', this._opts.label); + + this.onclick(this._domNode, (e) => { + this._opts.onTrigger(); + e.preventDefault(); + }); + this.onkeydown(this._domNode, (e) => { + if (e.equals(KeyCode.Space) || e.equals(KeyCode.Enter)) { + this._opts.onTrigger(); + e.preventDefault(); + return; + } + this._opts.onKeyDown(e); + }); + } + + public get domNode(): HTMLElement { + return this._domNode; + } + + public isEnabled(): boolean { + return (this._domNode.tabIndex >= 0); + } + + public focus(): void { + this._domNode.focus(); + } + + public setEnabled(enabled: boolean): void { + dom.toggleClass(this._domNode, 'disabled', !enabled); + this._domNode.setAttribute('aria-disabled', String(!enabled)); + this._domNode.tabIndex = enabled ? 0 : -1; + } + + public setExpanded(expanded: boolean): void { + this._domNode.setAttribute('aria-expanded', String(!!expanded)); + } + + public toggleClass(className: string, shouldHaveIt: boolean): void { + dom.toggleClass(this._domNode, className, shouldHaveIt); + } +} diff --git a/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts b/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts new file mode 100644 index 0000000000..774bfa9c2b --- /dev/null +++ b/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts @@ -0,0 +1,217 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IProfilerController } from './interfaces'; +import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; +import { Table } from 'sql/base/browser/ui/table/table'; +import { attachTableStyler } from 'sql/common/theme/styler'; +import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin'; +import { IProfilerStateChangedEvent } from 'sql/parts/profiler/editor/profilerState'; +import { FindWidget, ITableController, IConfigurationChangedEvent, ACTION_IDS } from './profilerFindWidget'; +import { ProfilerFindNext, ProfilerFindPrevious } from 'sql/parts/profiler/contrib/profilerActions'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IEditorAction } from 'vs/editor/common/editorCommon'; +import { IOverlayWidget } from 'vs/editor/browser/editorBrowser'; +import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/common/findState'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +export class ProfilerTableEditor extends BaseEditor implements IProfilerController, ITableController { + + public static ID: string = 'workbench.editor.profiler.table'; + protected _input: ProfilerInput; + private _profilerTable: Table; + private _columnListener: IDisposable; + private _stateListener: IDisposable; + private _findCountChangeListener: IDisposable; + private _findState: FindReplaceState; + private _finder: FindWidget; + private _overlay: HTMLElement; + private _currentDimensions: Dimension; + private _actionMap: { [x: string]: IEditorAction } = {}; + + private _onDidChangeConfiguration = new Emitter(); + public onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IWorkbenchThemeService private _themeService: IWorkbenchThemeService, + @IContextViewService private _contextViewService: IContextViewService, + @IKeybindingService private _keybindingService: IKeybindingService, + @IContextKeyService private _contextKeyService: IContextKeyService, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(ProfilerTableEditor.ID, telemetryService, _themeService); + this._actionMap[ACTION_IDS.FIND_NEXT] = this._instantiationService.createInstance(ProfilerFindNext, this); + this._actionMap[ACTION_IDS.FIND_PREVIOUS] = this._instantiationService.createInstance(ProfilerFindPrevious, this); + } + + public createEditor(parent: Builder): void { + + this._overlay = document.createElement('div'); + this._overlay.className = 'overlayWidgets'; + this._overlay.style.width = '100%'; + this._overlay.style.zIndex = '4'; + parent.getHTMLElement().appendChild(this._overlay); + + this._profilerTable = new Table(parent.getHTMLElement()); + this._profilerTable.setSelectionModel(new RowSelectionModel()); + attachTableStyler(this._profilerTable, this._themeService); + + this._findState = new FindReplaceState(); + this._findState.addChangeListener(e => this._onFindStateChange(e)); + + this._finder = new FindWidget( + this, + this._findState, + this._contextViewService, + this._keybindingService, + this._contextKeyService, + this._themeService + ); + } + + public setInput(input: ProfilerInput): TPromise { + this._input = input; + if (this._columnListener) { + this._columnListener.dispose(); + } + this._columnListener = input.onColumnsChanged(e => { + this._profilerTable.columns = e; + this._profilerTable.autosizeColumns(); + }); + if (this._stateListener) { + this._stateListener.dispose(); + } + this._stateListener = input.state.addChangeListener(e => this._onStateChange(e)); + + if (this._findCountChangeListener) { + this._findCountChangeListener.dispose(); + } + this._findCountChangeListener = input.data.onFindCountChange(() => this._updateFinderMatchState()); + + this._profilerTable.setData(input.data); + this._profilerTable.columns = input.columns; + this._profilerTable.autosizeColumns(); + this._input.data.currentFindPosition.then(val => { + this._profilerTable.setActiveCell(val.row, val.col); + this._updateFinderMatchState(); + }, er => { }); + return TPromise.as(null); + } + + public toggleSearch(): void { + this._findState.change({ + isRevealed: true + }, false); + this._finder.focusFindInput(); + } + + public findNext(): void { + this._input.data.findNext().then(p => { + this._profilerTable.setActiveCell(p.row, p.col); + this._updateFinderMatchState(); + }, er => { }); + } + + public findPrevious(): void { + this._input.data.findPrevious().then(p => { + this._profilerTable.setActiveCell(p.row, p.col); + this._updateFinderMatchState(); + }, er => { }); + } + + public getConfiguration() { + return { + layoutInfo: { + width: this._currentDimensions ? this._currentDimensions.width : 0 + } + }; + } + + public layoutOverlayWidget(widget: IOverlayWidget): void { + // no op + } + + public addOverlayWidget(widget: IOverlayWidget): void { + let domNode = widget.getDomNode(); + domNode.style.right = '28px'; + this._overlay.appendChild(widget.getDomNode()); + this._findState.change({ isRevealed: false }, false); + } + + public getAction(id: string): IEditorAction { + return this._actionMap[id]; + } + + public focus(): void { + this._profilerTable.focus(); + } + + public layout(dimension: Dimension): void { + this._currentDimensions = dimension; + this._profilerTable.layout(dimension); + this._onDidChangeConfiguration.fire({ layoutInfo: true }); + } + + public onSelectedRowsChanged(fn: (e: Slick.EventData, args: Slick.OnSelectedRowsChangedEventArgs) => any): void { + if (this._profilerTable) { + this._profilerTable.onSelectedRowsChanged(fn); + } + } + + private _onStateChange(e: IProfilerStateChangedEvent): void { + if (e.autoscroll) { + this._profilerTable.autoScroll = this._input.state.autoscroll; + } + } + + public updateState(): void { + this._onStateChange({ autoscroll: true }); + } + + private _onFindStateChange(e: FindReplaceStateChangedEvent): void { + if (e.isRevealed) { + if (this._findState.isRevealed) { + this._finder.getDomNode().style.top = '0px'; + this._updateFinderMatchState(); + } else { + this._finder.getDomNode().style.top = ''; + } + } + + if (e.searchString) { + if (this._input && this._input.data) { + if (this._findState.searchString) { + this._input.data.find(this._findState.searchString).then(p => { + if (p) { + this._profilerTable.setActiveCell(p.row, p.col); + this._updateFinderMatchState(); + } + }); + } else { + this._input.data.clearFind(); + } + } + } + } + + private _updateFinderMatchState(): void { + if (this._input && this._input.data) { + this._findState.changeMatchInfo(this._input.data.findPosition, this._input.data.findCount, undefined); + } else { + this._findState.changeMatchInfo(0, 0, undefined); + } + } +} diff --git a/src/sql/parts/profiler/editor/interfaces.ts b/src/sql/parts/profiler/editor/interfaces.ts new file mode 100644 index 0000000000..e6857d0744 --- /dev/null +++ b/src/sql/parts/profiler/editor/interfaces.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const CONTEXT_PROFILER_EDITOR = new RawContextKey('inProfilerTableEditor', false); + +export const PROFILER_TABLE_COMMAND_SEARCH = 'profiler.table.action.search'; diff --git a/src/sql/parts/profiler/editor/profilerEditor.ts b/src/sql/parts/profiler/editor/profilerEditor.ts new file mode 100644 index 0000000000..dbef269f21 --- /dev/null +++ b/src/sql/parts/profiler/editor/profilerEditor.ts @@ -0,0 +1,452 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ProfilerInput } from './profilerInput'; + +import { TabbedPanel } from 'sql/base/browser/ui/panel/panel'; +import { Table } from 'sql/base/browser/ui/table/table'; +import { TableDataView } from 'sql/base/browser/ui/table/tableDataView'; +import { IProfilerService, IProfilerSessionTemplate } from 'sql/parts/profiler/service/interfaces'; +import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; +import { attachTableStyler } from 'sql/common/theme/styler'; +import { IProfilerStateChangedEvent } from './profilerState'; +import { ProfilerTableEditor } from './controller/profilerTableEditor'; +import * as Actions from 'sql/parts/profiler/contrib/profilerActions'; +import { CONTEXT_PROFILER_EDITOR, PROFILER_TABLE_COMMAND_SEARCH } from './interfaces'; +import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; + +import * as DOM from 'vs/base/browser/dom'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ProfilerResourceEditor } from './profilerResourceEditor'; +import { SplitView, View, Orientation, IViewOptions } from 'vs/base/browser/ui/splitview/splitview'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IModel, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import URI from 'vs/base/common/uri'; +import { UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import * as nls from 'vs/nls'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Command } from 'vs/editor/common/editorCommonExtensions'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { ContextKeyExpr, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { CommonFindController, FindStartFocusAction } from 'vs/editor/contrib/find/common/findController'; +import * as types from 'vs/base/common/types'; +import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; + +import { ProfilerTestBackend } from 'sql/parts/profiler/service/profilerTestBackend'; + +class BasicView extends View { + private _previousSize: number; + private _collapsed: boolean; + public headerSize: number; + constructor( + initialSize: number, + private _element: HTMLElement, + private _focus: () => void, + private _layout: (size: number, orientation: Orientation) => void, + opts: IViewOptions + ) { + super(initialSize, opts); + } + + render(container: HTMLElement, orientation: Orientation): void { + container.appendChild(this._element); + } + + focus(): void { + this._focus(); + } + + layout(size: number, orientation: Orientation): void { + if (!this.collapsed) { + this._previousSize = size; + } + this._layout(size, orientation); + } + + set collapsed(val: boolean) { + this._collapsed = val === false ? false : true; + if (this.collapsed) { + this._previousSize = this.size; + this.setFixed(this.headerSize); + } else { + this.setFlexible(this._previousSize); + } + } + + get collapsed(): boolean { + return this._collapsed; + } +} + +export interface IDetailData { + label: string; + value: string; +} + +export class ProfilerEditor extends BaseEditor { + public static ID: string = 'workbench.editor.profiler'; + private _editor: ProfilerResourceEditor; + private _editorModel: IModel; + private _editorInput: UntitledEditorInput; + private _splitView: SplitView; + private _container: HTMLElement; + private _body: HTMLElement; + private _header: HTMLElement; + private _actionBar: Taskbar; + private _tabbedPanel: TabbedPanel; + private _profilerTableEditor: ProfilerTableEditor; + private _detailTable: Table; + private _detailTableData: TableDataView; + private _stateListener: IDisposable; + private _panelView: BasicView; + + private _profilerEditorContextKey: IContextKey; + + private _sessionTemplateSelector: SelectBox; + private _sessionTemplates: Array; + + // Actions + private _connectAction: Actions.ProfilerConnect; + private _startAction: Actions.ProfilerStart; + private _pauseAction: Actions.ProfilerPause; + private _stopAction: Actions.ProfilerStop; + private _autoscrollAction: Actions.ProfilerAutoScroll; + private _collapsedPanelAction: Actions.ProfilerCollapsablePanelAction; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IWorkbenchThemeService themeService: IWorkbenchThemeService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IContextMenuService private _contextMenuService: IContextMenuService, + @IModelService private _modelService: IModelService, + @IProfilerService private _profilerService: IProfilerService, + @IContextKeyService private _contextKeyService: IContextKeyService, + @IContextViewService private _contextViewService: IContextViewService + ) { + super(ProfilerEditor.ID, telemetryService, themeService); + this._profilerEditorContextKey = CONTEXT_PROFILER_EDITOR.bindTo(this._contextKeyService); + } + + protected createEditor(parent: Builder): void { + // test backend + this._profilerService.registerProvider('default', this._instantiationService.createInstance(ProfilerTestBackend)); + + this._container = document.createElement('div'); + this._container.className = 'carbon-profiler'; + parent.append(this._container); + + this._createHeader(); + + this._body = document.createElement('div'); + this._body.className = 'profiler-body'; + this._container.appendChild(this._body); + this._splitView = new SplitView(this._body); + + let tableContainer = this._createProfilerTable(); + let paneContainer = this._createProfilerPane(); + this._splitView.addView(new BasicView( + undefined, + tableContainer, + () => this._profilerTableEditor.focus(), + size => this._profilerTableEditor.layout(new Dimension(parseFloat(DOM.getComputedStyle(this._body).width), size)), + {} + )); + + this._panelView = new BasicView( + undefined, + paneContainer, + () => this._tabbedPanel.focus(), + size => this._tabbedPanel.layout(new Dimension(DOM.getTotalWidth(this._body), size)), + { minimumSize: 35 } + ); + this._panelView.headerSize = 35; + this._splitView.addView(this._panelView); + } + + private _createHeader(): void { + this._header = document.createElement('div'); + this._header.className = 'profiler-header'; + this._container.appendChild(this._header); + this._actionBar = new Taskbar(this._header, this._contextMenuService); + this._startAction = this._instantiationService.createInstance(Actions.ProfilerStart, Actions.ProfilerStart.ID, Actions.ProfilerStart.LABEL); + this._startAction.enabled = false; + this._stopAction = this._instantiationService.createInstance(Actions.ProfilerStop, Actions.ProfilerStop.ID, Actions.ProfilerStop.LABEL); + this._stopAction.enabled = false; + this._pauseAction = this._instantiationService.createInstance(Actions.ProfilerPause, Actions.ProfilerPause.ID, Actions.ProfilerPause.LABEL); + this._pauseAction.enabled = false; + this._connectAction = this._instantiationService.createInstance(Actions.ProfilerConnect, Actions.ProfilerConnect.ID, Actions.ProfilerConnect.LABEL); + this._autoscrollAction = this._instantiationService.createInstance(Actions.ProfilerAutoScroll, Actions.ProfilerAutoScroll.ID, Actions.ProfilerAutoScroll.LABEL); + + this._sessionTemplates = this._profilerService.getSessionTemplates(); + this._sessionTemplateSelector = new SelectBox(this._sessionTemplates.map(i => i.name), 'Standard'); + this._register(this._sessionTemplateSelector.onDidSelect(e => { + if (this.input) { + this.input.sessionTemplate = this._sessionTemplates.find(i => i.name === e.selected); + } + })); + let dropdownContainer = document.createElement('div'); + dropdownContainer.style.width = '150px'; + this._sessionTemplateSelector.render(dropdownContainer); + + this._register(attachSelectBoxStyler(this._sessionTemplateSelector, this.themeService)); + + this._actionBar.setContent([ + { action: this._startAction }, + { action: this._pauseAction }, + { action: this._stopAction }, + { action: this._connectAction }, + { element: Taskbar.createTaskbarSeparator() }, + { action: this._autoscrollAction }, + { action: this._instantiationService.createInstance(Actions.ProfilerClear, Actions.ProfilerClear.ID, Actions.ProfilerClear.LABEL) }, + { element: dropdownContainer }, + { action: this._instantiationService.createInstance(Actions.ProfilerEditColumns, Actions.ProfilerEditColumns.ID, Actions.ProfilerEditColumns.LABEL) } + ]); + } + + private _createProfilerTable(): HTMLElement { + let profilerTableContainer = document.createElement('div'); + profilerTableContainer.className = 'profiler-table monaco-editor'; + profilerTableContainer.style.width = '100%'; + profilerTableContainer.style.height = '100%'; + profilerTableContainer.style.overflow = 'hidden'; + profilerTableContainer.style.position = 'relative'; + this._profilerTableEditor = this._instantiationService.createInstance(ProfilerTableEditor); + this._profilerTableEditor.createEditor(new Builder(profilerTableContainer)); + this._profilerTableEditor.onSelectedRowsChanged((e, args) => { + let data = this.input.data.getItem(args.rows[0]); + if (data) { + this._modelService.updateModel(this._editorModel, data['TextData']); + this._detailTableData.clear(); + this._detailTableData.push(Object.keys(data).map(key => { + return { + label: key, + value: data[key] + }; + })); + + if (this.input && types.isUndefinedOrNull(this.input.state.isPanelCollapsed)) { + this.input.state.change({ isPanelCollapsed: false }); + } + } else { + this._modelService.updateModel(this._editorModel, ''); + this._detailTableData.clear(); + } + + }); + + return profilerTableContainer; + } + + private _createProfilerPane(): HTMLElement { + let editorContainer = this._createProfilerEditor(); + let tabbedPanelContainer = document.createElement('div'); + tabbedPanelContainer.className = 'profiler-tabbedPane'; + this._tabbedPanel = new TabbedPanel(tabbedPanelContainer); + this._tabbedPanel.pushTab({ + identifier: 'editor', + title: nls.localize('text', "Text"), + view: { + layout: dim => this._editor.layout(dim), + render: parent => parent.appendChild(editorContainer) + } + }); + + let detailTableContainer = document.createElement('div'); + detailTableContainer.className = 'profiler-detailTable'; + detailTableContainer.style.width = '100%'; + detailTableContainer.style.height = '100%'; + this._detailTableData = new TableDataView(); + this._detailTable = new Table(detailTableContainer, this._detailTableData, [ + { + id: 'label', + name: nls.localize('label', "Label"), + field: 'label' + }, + { + id: 'value', + name: nls.localize('value', "Value"), + field: 'value' + } + ], { forceFitColumns: true }); + + this._tabbedPanel.pushTab({ + identifier: 'detailTable', + title: nls.localize('details', "Details"), + view: { + layout: dim => this._detailTable.layout(dim), + render: parent => parent.appendChild(detailTableContainer) + } + }); + + this._collapsedPanelAction = this._instantiationService.createInstance(Actions.ProfilerCollapsablePanelAction, Actions.ProfilerCollapsablePanelAction.ID, Actions.ProfilerCollapsablePanelAction.LABEL); + + this._tabbedPanel.pushAction(this._collapsedPanelAction, { icon: true, label: false }); + + this._register(attachTableStyler(this._detailTable, this.themeService)); + + return tabbedPanelContainer; + } + + private _createProfilerEditor(): HTMLElement { + this._editor = this._instantiationService.createInstance(ProfilerResourceEditor); + let editorContainer = document.createElement('div'); + editorContainer.className = 'profiler-editor'; + this._editor.create(new Builder(editorContainer)); + this._editor.setVisible(true); + this._editorInput = this._instantiationService.createInstance(UntitledEditorInput, URI.from({ scheme: UNTITLED_SCHEMA }), false, 'sql', '', ''); + this._editor.setInput(this._editorInput, undefined); + this._editorInput.resolve().then(model => this._editorModel = model.textEditorModel); + return editorContainer; + } + + public get input(): ProfilerInput { + return this._input as ProfilerInput; + } + + public setInput(input: ProfilerInput, options?: EditorOptions): TPromise { + this._profilerEditorContextKey.set(true); + if (input instanceof ProfilerInput && input.matches(this.input)) { + return TPromise.as(null); + } + + return super.setInput(input, options).then(() => { + this._profilerTableEditor.setInput(input); + + if (input.sessionTemplate) { + this._sessionTemplateSelector.selectWithOptionName(input.sessionTemplate.name); + } else { + input.sessionTemplate = this._sessionTemplates.find(i => i.name === 'Standard'); + } + + this._actionBar.context = input; + this._tabbedPanel.actionBarContext = input; + if (this._stateListener) { + this._stateListener.dispose(); + } + this._stateListener = input.state.addChangeListener(e => this._onStateChange(e)); + this._onStateChange({ + isConnected: true, + isRunning: true, + isPaused: true, + isStopped: true, + autoscroll: true, + isPanelCollapsed: true + }); + this._profilerTableEditor.updateState(); + this._splitView.layout(); + this._profilerTableEditor.focus(); + }); + } + + public clearInput(): void { + this._profilerEditorContextKey.set(false); + } + + public toggleSearch(): void { + if (this._editor.getControl().isFocused()) { + let editor = this._editor.getControl() as ICommonCodeEditor; + let controller = CommonFindController.get(editor); + if (controller) { + controller.start({ + forceRevealReplace: false, + seedSearchStringFromSelection: (controller.getState().searchString.length === 0), + shouldFocus: FindStartFocusAction.FocusFindInput, + shouldAnimate: true + }); + } + } else { + this._profilerTableEditor.toggleSearch(); + } + } + + private _onStateChange(e: IProfilerStateChangedEvent): void { + if (e.autoscroll) { + this._autoscrollAction.checked = this.input.state.autoscroll; + } + + if (e.isPanelCollapsed) { + this._collapsedPanelAction.collapsed = this.input.state.isPanelCollapsed; + this._tabbedPanel.collapsed = this.input.state.isPanelCollapsed; + this._panelView.collapsed = this.input.state.isPanelCollapsed; + } + + if (e.isConnected) { + this._connectAction.connected = this.input.state.isConnected; + this._startAction.enabled = this.input.state.isConnected; + this._stopAction.enabled = false; + this._pauseAction.enabled = false; + + if (this.input.state.isConnected) { + this._sessionTemplateSelector.disable(); + } else { + this._sessionTemplateSelector.enable(); + } + + return; + } + + if (e.isRunning) { + this._startAction.enabled = !this.input.state.isRunning; + } + + if (e.isStopped || e.isRunning) { + this._stopAction.enabled = !this.input.state.isStopped && this.input.state.isRunning; + } + + if (e.isPaused || e.isRunning) { + this._pauseAction.enabled = !this.input.state.isPaused && this.input.state.isRunning; + } + } + + public layout(dimension: Dimension): void { + this._container.style.width = dimension.width + 'px'; + this._container.style.height = dimension.height + 'px'; + this._body.style.width = dimension.width + 'px'; + this._body.style.height = (dimension.height - (28 + 4)) + 'px'; + this._splitView.layout(dimension.height - (28 + 4)); + } +} + +abstract class SettingsCommand extends Command { + + protected getProfilerEditor(accessor: ServicesAccessor): ProfilerEditor { + const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor(); + if (activeEditor instanceof ProfilerEditor) { + return activeEditor; + } + return null; + + } + +} + +class StartSearchProfilerTableCommand extends SettingsCommand { + + public runCommand(accessor: ServicesAccessor, args: any): void { + const preferencesEditor = this.getProfilerEditor(accessor); + if (preferencesEditor) { + preferencesEditor.toggleSearch(); + } + } + +} + +const command = new StartSearchProfilerTableCommand({ + id: PROFILER_TABLE_COMMAND_SEARCH, + precondition: ContextKeyExpr.and(CONTEXT_PROFILER_EDITOR), + kbOpts: { primary: KeyMod.CtrlCmd | KeyCode.KEY_F } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule(command.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); diff --git a/src/sql/parts/profiler/editor/profilerInput.ts b/src/sql/parts/profiler/editor/profilerInput.ts new file mode 100644 index 0000000000..764d1b74e1 --- /dev/null +++ b/src/sql/parts/profiler/editor/profilerInput.ts @@ -0,0 +1,136 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TableDataView } from 'sql/base/browser/ui/table/tableDataView'; +import { IProfilerSession, IProfilerService, ProfilerSessionID, IProfilerSessionTemplate } from 'sql/parts/profiler/service/interfaces'; +import { ProfilerState } from './profilerState'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; + +import * as data from 'data'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { IEditorModel } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import Event, { Emitter } from 'vs/base/common/event'; +import { generateUuid } from 'vs/base/common/uuid'; + +import * as nls from 'vs/nls'; + +export class ProfilerInput extends EditorInput implements IProfilerSession { + + public static ID: string = 'workbench.editorinputs.profilerinputs'; + public static SCHEMA: string = 'profiler'; + private _data: TableDataView; + private _id: ProfilerSessionID; + private _state: ProfilerState; + private _columns: string[] = []; + private _sessionTemplate: IProfilerSessionTemplate; + + private _onColumnsChanged = new Emitter[]>(); + public onColumnsChanged: Event[]> = this._onColumnsChanged.event; + + constructor( + private _connection: IConnectionProfile, + @IInstantiationService private _instantiationService: IInstantiationService, + @IProfilerService private _profilerService: IProfilerService + ) { + super(); + this._state = new ProfilerState(); + // set inital state + this.state.change({ + isConnected: false, + isStopped: false, + isPaused: false, + isRunning: false, + autoscroll: true + }); + this._id = this._profilerService.registerSession(generateUuid(), this); + let searchFn = (val: { [x: string]: string }, exp: string): Array => { + let ret = new Array(); + for (let i = 0; i < this._columns.length; i++) { + if (val[this._columns[i]].includes(exp)) { + ret.push(i); + } + } + return ret; + }; + this._data = new TableDataView(undefined, searchFn); + } + + public set sessionTemplate(template: IProfilerSessionTemplate) { + if (!this.state.isConnected || this.state.isStopped) { + this._sessionTemplate = template; + let newColumns = this.sessionTemplate.view.events.reduce>((p, e) => { + e.columns.forEach(c => { + if (!p.includes(c)) { + p.push(c); + } + }); + return p; + }, []); + newColumns.unshift('EventClass'); + this.setColumns(newColumns); + } + } + + public get sessionTemplate(): IProfilerSessionTemplate { + return this._sessionTemplate; + } + + public getTypeId(): string { + return ProfilerInput.ID; + } + + public resolve(refresh?: boolean): TPromise { + return undefined; + } + + public getName(): string { + return nls.localize('profiler', 'Profiler'); + } + + public get data(): TableDataView { + return this._data; + } + + public get columns(): Slick.Column[] { + if (this._columns) { + return this._columns.map(i => { + return >{ + id: i, + field: i, + name: i, + sortable: true + }; + }); + } else { + return []; + } + } + + public setColumns(columns: Array) { + this._columns = columns; + this._onColumnsChanged.fire(this.columns); + } + + public get id(): ProfilerSessionID { + return this._id; + } + + public get state(): ProfilerState { + return this._state; + } + + public onMoreRows(rowCount: number, data: data.IProfilerTableRow) { + let validColumns = this.sessionTemplate.view.events.find(i => i.name === data.EventClass).columns; + Object.keys(rowCount).forEach(k => { + if (!validColumns.includes(k)) { + delete rowCount[k]; + } + }); + this._data.push(data); + } +} diff --git a/src/sql/parts/profiler/editor/profilerResourceEditor.ts b/src/sql/parts/profiler/editor/profilerResourceEditor.ts new file mode 100644 index 0000000000..301d2c51bc --- /dev/null +++ b/src/sql/parts/profiler/editor/profilerResourceEditor.ts @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import * as nls from 'vs/nls'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; +import * as editorCommon from 'vs/editor/common/editorCommon'; + +import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { CodeEditor } from 'vs/editor/browser/codeEditor'; +import { IEditorContributionCtor } from 'vs/editor/browser/editorBrowser'; +import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; + +class ProfilerResourceCodeEditor extends CodeEditor { + + protected _getContributions(): IEditorContributionCtor[] { + let contributions = super._getContributions(); + let skipContributions = [FoldingController.prototype]; + contributions = contributions.filter(c => skipContributions.indexOf(c.prototype) === -1); + return contributions; + } + +} + +/** + * Extension of TextResourceEditor that is always readonly rather than only with non UntitledInputs + */ +export class ProfilerResourceEditor extends BaseTextEditor { + + public static ID = 'profiler.editors.textEditor'; + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IInstantiationService instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, + @IThemeService themeService: IThemeService, + @IModeService modeService: IModeService, + @ITextFileService textFileService: ITextFileService, + @IEditorGroupService editorGroupService: IEditorGroupService + + ) { + super(ProfilerResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, modeService, textFileService, editorGroupService); + } + + public createEditorControl(parent: Builder, configuration: IEditorOptions): editorCommon.IEditor { + return this.instantiationService.createInstance(ProfilerResourceCodeEditor, parent.getHTMLElement(), configuration); + } + + protected getConfigurationOverrides(): IEditorOptions { + const options = super.getConfigurationOverrides(); + options.readOnly = true; + if (this.input) { + options.inDiffEditor = true; + options.scrollBeyondLastLine = false; + options.folding = false; + options.renderWhitespace = 'none'; + options.wordWrap = 'on'; + options.renderIndentGuides = false; + options.rulers = []; + options.glyphMargin = true; + options.minimap = { + enabled: false + }; + } + return options; + } + + setInput(input: UntitledEditorInput, options: EditorOptions): TPromise { + return super.setInput(input, options) + .then(() => this.input.resolve() + .then(editorModel => editorModel.load()) + .then(editorModel => this.getControl().setModel((editorModel).textEditorModel))); + } + + protected getAriaLabel(): string { + return nls.localize('profilerTextEditorAriaLabel', 'Profiler editor for event text. Readonly'); + } + + public layout(dimension: Dimension) { + this.getControl().layout(dimension); + } +} diff --git a/src/sql/parts/profiler/editor/profilerState.ts b/src/sql/parts/profiler/editor/profilerState.ts new file mode 100644 index 0000000000..8ab90d229c --- /dev/null +++ b/src/sql/parts/profiler/editor/profilerState.ts @@ -0,0 +1,116 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export interface IProfilerStateChangedEvent { + isConnected?: boolean; + isRunning?: boolean; + isPaused?: boolean; + isStopped?: boolean; + autoscroll?: boolean; + isPanelCollapsed?: boolean; +} + +export interface INewProfilerState { + isConnected?: boolean; + isRunning?: boolean; + isPaused?: boolean; + isStopped?: boolean; + autoscroll?: boolean; + isPanelCollapsed?: boolean; +} + +export class ProfilerState implements IDisposable { + private static _CHANGED_EVENT = 'changed'; + + private _isConnected: boolean; + private _isRunning: boolean; + private _isPaused: boolean; + private _isStopped: boolean; + private _autoscroll: boolean; + private _isPanelCollapsed: boolean; + private _eventEmitter: EventEmitter; + + public get isConnected(): boolean { return this._isConnected; } + public get isRunning(): boolean { return this._isRunning; } + public get isPaused(): boolean { return this._isPaused; } + public get isStopped(): boolean { return this._isStopped; } + public get autoscroll(): boolean { return this._autoscroll; } + public get isPanelCollapsed(): boolean { return this._isPanelCollapsed; } + + constructor() { + this._eventEmitter = new EventEmitter(); + } + + public dispose(): void { + this._eventEmitter.dispose(); + } + + public addChangeListener(listener: (e: IProfilerStateChangedEvent) => void): IDisposable { + return this._eventEmitter.addListener(ProfilerState._CHANGED_EVENT, listener); + } + + public change(newState: INewProfilerState): void { + let changeEvent: IProfilerStateChangedEvent = { + isConnected: false, + isRunning: false, + isPaused: false, + isStopped: false, + autoscroll: false, + isPanelCollapsed: false + }; + let somethingChanged = false; + + if (typeof newState.isConnected !== 'undefined') { + if (this._isConnected !== newState.isConnected) { + this._isConnected = newState.isConnected; + changeEvent.isConnected = true; + somethingChanged = true; + } + } + if (typeof newState.isRunning !== 'undefined') { + if (this._isRunning !== newState.isRunning) { + this._isRunning = newState.isRunning; + changeEvent.isRunning = true; + somethingChanged = true; + } + } + if (typeof newState.isPaused !== 'undefined') { + if (this._isPaused !== newState.isPaused) { + this._isPaused = newState.isPaused; + changeEvent.isPaused = true; + somethingChanged = true; + } + } + if (typeof newState.isStopped !== 'undefined') { + if (this._isStopped !== newState.isStopped) { + this._isStopped = newState.isStopped; + changeEvent.isStopped = true; + somethingChanged = true; + } + } + if (typeof newState.autoscroll !== 'undefined') { + if (this._autoscroll !== newState.autoscroll) { + this._autoscroll = newState.autoscroll; + changeEvent.autoscroll = true; + somethingChanged = true; + } + } + if (typeof newState.isPanelCollapsed !== 'undefined') { + if (this._isPanelCollapsed !== newState.isPanelCollapsed) { + this._isPanelCollapsed = newState.isPanelCollapsed; + changeEvent.isPanelCollapsed = true; + somethingChanged = true; + } + } + + if (somethingChanged) { + this._eventEmitter.emit(ProfilerState._CHANGED_EVENT, changeEvent); + } + } +} diff --git a/src/sql/parts/profiler/service/interfaces.ts b/src/sql/parts/profiler/service/interfaces.ts new file mode 100644 index 0000000000..dd0ae1b0b1 --- /dev/null +++ b/src/sql/parts/profiler/service/interfaces.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import * as data from 'data'; + +const PROFILER_SERVICE_ID = 'profilerService'; +export const IProfilerService = createDecorator(PROFILER_SERVICE_ID); + +export type ProfilerSessionID = string; + +export const PROFILER_SESSION_TEMPLATE_SETTINGS = 'profiler.sessionTemplates'; +export const PROFILER_SETTINGS = 'profiler'; + +/** + * A front end provider for a profiler session + */ +export interface IProfilerSession { + /** + * Called by the service when more rows are available to render + */ + onMoreRows(rowCount: number, data: data.IProfilerTableRow); +} + +/** + * A Profiler Service that handles session communication between the backends and frontends + */ +export interface IProfilerService { + _serviceBrand: any; + /** + * Registers a backend provider for profiler session. ex: mssql + */ + registerProvider(providerId: string, provider: data.IProfilerProvider): void; + /** + * Registers a session with the service that acts as the UI for a profiler session + * @returns An unique id that should be used to make subsequent calls to this service + */ + registerSession(uri: string, session: IProfilerSession): ProfilerSessionID; + /** + * Connects the session specified by the id + */ + connectSession(sessionId: ProfilerSessionID): Thenable; + /** + * Disconnected the session specified by the id + */ + disconnectSession(sessionId: ProfilerSessionID): Thenable; + /** + * Starts the session specified by the id + */ + startSession(sessionId: ProfilerSessionID): Thenable; + /** + * Pauses the session specified by the id + */ + pauseSession(sessionId: ProfilerSessionID): Thenable; + /** + * Stops the session specified by the id + */ + stopSession(sessionId: ProfilerSessionID): Thenable; + /** + * The method called by the service provider for when more rows are available to render + */ + onMoreRows(params: data.IProfilerMoreRowsNotificationParams): void; + /** + * Gets a list of the session templates that are specified in the settings + * @param provider An optional string to limit the session template to a specific + * @returns An array of session templates that match the provider passed, if passed, and generic ones (no provider specified), + * otherwise returns all session templates + */ + getSessionTemplates(providerId?: string): Array; + /** + * Launches the dialog for editing the view columns of a profiler session template for the given input + * @param input input object that contains the necessary information which will be modified based on used input + */ + launchColumnEditor(input: ProfilerInput): Thenable; +} + +export interface IProfilerSettings { + sessionTemplates: Array; +} + +export interface IEventTemplate { + name: string; + optionalColumns: Array; +} + +export interface IEventViewTemplate { + name: string; + columns: Array; +} + +export interface ISessionViewTemplate { + events: Array; +} + +export interface IProfilerSessionTemplate { + name: string; + events: Array; + view: ISessionViewTemplate; +} diff --git a/src/sql/parts/profiler/service/profilerService.ts b/src/sql/parts/profiler/service/profilerService.ts new file mode 100644 index 0000000000..cd65248002 --- /dev/null +++ b/src/sql/parts/profiler/service/profilerService.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { + ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerSessionTemplate, + PROFILER_SETTINGS, IProfilerSettings +} from './interfaces'; +import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; +import { ProfilerColumnEditorDialog } from 'sql/parts/profiler/dialog/profilerColumnEditorDialog'; + +import * as data from 'data'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +class TwoWayMap { + private forwardMap: Map; + private reverseMap: Map; + + constructor() { + this.forwardMap = new Map(); + this.reverseMap = new Map(); + } + + get(input: T): K { + return this.forwardMap.get(input); + } + + reverseGet(input: K): T { + return this.reverseMap.get(input); + } + + set(input: T, input2: K): TwoWayMap { + this.forwardMap.set(input, input2); + this.reverseMap.set(input2, input); + return this; + } +} + +export class ProfilerService implements IProfilerService { + public _serviceBrand: any; + private _providers = new Map(); + private _idMap = new TwoWayMap(); + private _sessionMap = new Map(); + private _dialog: ProfilerColumnEditorDialog; + + constructor( + @IConnectionManagementService private _connectionService: IConnectionManagementService, + @IConfigurationService public _configurationService: IConfigurationService, + @IInstantiationService private _instantiationService: IInstantiationService + ) { } + + public registerProvider(providerId: string, provider: data.IProfilerProvider): void { + this._providers.set(providerId, provider); + } + + public registerSession(uri: string, session: IProfilerSession): ProfilerSessionID { + this._sessionMap.set(uri, session); + this._idMap.set(uri, uri); + return uri; + } + + public onMoreRows(params: data.IProfilerMoreRowsNotificationParams): void { + this._sessionMap.get(this._idMap.reverseGet(params.uri)).onMoreRows(params.rowCount, params.data); + } + + public connectSession(id: ProfilerSessionID): Thenable { + return this._runAction(id, provider => provider.connectSession(this._idMap.get(id))); + } + + public disconnectSession(id: ProfilerSessionID): Thenable { + return this._runAction(id, provider => provider.disconnectSession(this._idMap.get(id))); + } + + public startSession(id: ProfilerSessionID): Thenable { + return this._runAction(id, provider => provider.startSession(this._idMap.get(id))); + } + + public pauseSession(id: ProfilerSessionID): Thenable { + return this._runAction(id, provider => provider.pauseSession(this._idMap.get(id))); + } + + public stopSession(id: ProfilerSessionID): Thenable { + return this._runAction(id, provider => provider.stopSession(this._idMap.get(id))); + } + + private _runAction(id: ProfilerSessionID, action: (handler: data.IProfilerProvider) => Thenable): Thenable { + // let providerId = this._connectionService.getProviderIdFromUri(this._idMap.get(id)); + let providerId = 'default'; + + if (!providerId) { + return TPromise.wrapError(new Error('Connection is required in order to interact with queries')); + } + let handler = this._providers.get(providerId); + if (handler) { + return action(handler); + } else { + return TPromise.wrapError(new Error('No Handler Registered')); + } + } + + public getSessionTemplates(provider?: string): Array { + let config = this._configurationService.getConfiguration(PROFILER_SETTINGS); + + if (provider) { + return config.sessionTemplates; + } else { + return config.sessionTemplates; + } + } + + public launchColumnEditor(input?: ProfilerInput): Thenable { + if (!this._dialog) { + this._dialog = this._instantiationService.createInstance(ProfilerColumnEditorDialog); + this._dialog.render(); + } + + this._dialog.open(input); + return TPromise.as(null); + } +} diff --git a/src/sql/parts/profiler/service/profilerTestBackend.ts b/src/sql/parts/profiler/service/profilerTestBackend.ts new file mode 100644 index 0000000000..a8769d3e04 --- /dev/null +++ b/src/sql/parts/profiler/service/profilerTestBackend.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IProfilerService } from './interfaces'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as pfs from 'vs/base/node/pfs'; +import * as path from 'path'; + +import * as data from 'data'; + +declare var __dirname; + +const columns = [ + 'EventClass', + 'TextData', + 'ApplicationName', + 'NTUserName', + 'LoginName', + 'CPU', + 'Reads', + 'Writes', + 'Duration', + 'ClientProcessID', + 'SPID', + 'StartTime', + 'EndTime', + 'BinaryData' +]; + +export class ProfilerTestBackend implements data.IProfilerProvider { + private index = 0; + private timeOutMap = new Map(); + private testData: Array> = new Array>(); + + constructor( + @IProfilerService private _profilerService: IProfilerService) { } + + startSession(guid: string): Thenable { + this.timeOutMap.set(guid, this.intervalFn(guid)); + return TPromise.as(true); + } + + private intervalFn(guid: string): number { + return setTimeout(() => { + let data = this.testData[this.index++]; + let formattedData = { + EventClass: data[0].trim() + }; + + for (let i = 1; i < data.length; i++) { + formattedData[columns[i]] = data[i]; + } + + this._profilerService.onMoreRows({ uri: guid, rowCount: 1, data: formattedData }); + if (this.index >= this.testData.length) { + this.index = 0; + } + this.timeOutMap.set(guid, this.intervalFn(guid)); + }, Math.floor(Math.random() * 1000) + 300); + } + + stopSession(guid: string): Thenable { + clearTimeout(this.timeOutMap.get(guid)); + this.index = 0; + return TPromise.as(true); + } + + pauseSession(guid: string): Thenable { + clearTimeout(this.timeOutMap.get(guid)); + return TPromise.as(true); + } + + connectSession(): Thenable { + if (this.testData.length === 0) { + return new TPromise((resolve, reject) => { + pfs.readFile(path.join(__dirname, 'testData.tsv')).then(result => { + let tabsep = result.toString().split('\t'); + for (let i = 0; i < tabsep.length; i++) { + if (i % columns.length === 0) { + this.testData[i / columns.length] = new Array(); + } + this.testData[Math.floor(i / columns.length)][i % columns.length] = tabsep[i]; + }; + resolve(true); + }); + }); + } else { + return TPromise.as(true); + } + } + + disconnectSession(guid: string): Thenable { + clearTimeout(this.timeOutMap.get(guid)); + this.index = 0; + return TPromise.as(true); + } +} diff --git a/src/sql/parts/profiler/service/testData.tsv b/src/sql/parts/profiler/service/testData.tsv new file mode 100644 index 0000000000..b00cc8bd49 --- /dev/null +++ b/src/sql/parts/profiler/service/testData.tsv @@ -0,0 +1,3320 @@ +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 2064905136 62 2017-08-04 12:28:56.547 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 2064905136 62 2017-08-04 12:28:56.620 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 2064905136 62 2017-08-04 12:28:56.620 2017-08-04 12:28:56.620 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 2064905136 62 2017-08-04 12:28:56.663 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 2064905136 62 2017-08-04 12:28:56.663 2017-08-04 12:28:56.663 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 2064905136 62 2017-08-04 12:28:56.667 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 2064905136 62 2017-08-04 12:28:56.667 2017-08-04 12:28:56.667 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 2064905136 62 2017-08-04 12:28:56.667 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 2064905136 62 2017-08-04 12:28:56.667 2017-08-04 12:28:56.667 +SQL:BatchStarting SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 2064905136 62 2017-08-04 12:28:56.670 +SQL:BatchCompleted SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 0 0 0 0 2064905136 62 2017-08-04 12:28:56.670 2017-08-04 12:28:56.670 +SQL:BatchStarting set LOCK_TIMEOUT 5000 carbon sa 2064905136 62 2017-08-04 12:28:56.677 +SQL:BatchCompleted set LOCK_TIMEOUT 5000 carbon sa 0 0 0 0 2064905136 62 2017-08-04 12:28:56.677 2017-08-04 12:28:56.677 +SQL:BatchStarting SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 2064905136 62 2017-08-04 12:28:56.680 +SQL:BatchCompleted SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 0 0 0 0 2064905136 62 2017-08-04 12:28:56.680 2017-08-04 12:28:56.680 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 2064905136 62 2017-08-04 12:28:56.683 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 2064905136 62 2017-08-04 12:28:56.683 2017-08-04 12:28:56.683 +SQL:BatchStarting SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 2064905136 62 2017-08-04 12:28:56.687 +SQL:BatchCompleted SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 0 0 0 0 2064905136 62 2017-08-04 12:28:56.687 2017-08-04 12:28:56.687 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 2064905136 63 2017-08-04 12:28:56.763 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 2064905136 65 2017-08-04 12:28:56.837 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 2064905136 65 2017-08-04 12:28:56.837 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 2064905136 65 2017-08-04 12:28:56.837 2017-08-04 12:28:56.837 +SQL:BatchStarting SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 2064905136 65 2017-08-04 12:28:56.840 +SQL:BatchCompleted SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 0 0 0 0 2064905136 65 2017-08-04 12:28:56.840 2017-08-04 12:28:56.840 +SQL:BatchStarting set LOCK_TIMEOUT 5000 carbon sa 2064905136 65 2017-08-04 12:28:56.840 +SQL:BatchCompleted set LOCK_TIMEOUT 5000 carbon sa 0 0 0 0 2064905136 65 2017-08-04 12:28:56.840 2017-08-04 12:28:56.840 +SQL:BatchStarting SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 2064905136 65 2017-08-04 12:28:56.840 +SQL:BatchCompleted SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 0 0 0 0 2064905136 65 2017-08-04 12:28:56.840 2017-08-04 12:28:56.840 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 2064905136 65 2017-08-04 12:28:56.840 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 2064905136 65 2017-08-04 12:28:56.840 2017-08-04 12:28:56.840 +SQL:BatchStarting SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 2064905136 65 2017-08-04 12:28:56.843 +SQL:BatchCompleted SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 0 0 0 0 2064905136 65 2017-08-04 12:28:56.843 2017-08-04 12:28:56.843 +SQL:BatchStarting DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + carbon sa 2064905136 63 2017-08-04 12:28:57.193 +SQL:BatchCompleted DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + carbon sa 0 0 0 0 2064905136 63 2017-08-04 12:28:57.193 2017-08-04 12:28:57.193 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.300 +SQL:BatchStarting DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.300 +SQL:BatchCompleted DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.300 2017-08-04 12:28:57.300 +SQL:BatchStarting select SERVERPROPERTY(N'servername') carbon sa 2064905136 63 2017-08-04 12:28:57.313 +SQL:BatchCompleted select SERVERPROPERTY(N'servername') carbon sa 0 0 0 0 2064905136 63 2017-08-04 12:28:57.313 2017-08-04 12:28:57.313 +SQL:BatchStarting select SERVERPROPERTY(N'servername') Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.330 +SQL:BatchCompleted select SERVERPROPERTY(N'servername') Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.330 2017-08-04 12:28:57.330 +SQL:BatchStarting use [master]; +if (db_id() = 1) +begin +-- contained auth is 0 when connected to master +select 0 +end +else +begin +-- need dynamic sql so that we compile this query only when we know resource db is available +exec('select case when authenticating_database_id = 1 then 0 else 1 end from +sys.dm_exec_sessions where session_id = @@SPID') +end;use [master]; --resetting the context carbon sa 2064905136 63 2017-08-04 12:28:57.330 +SQL:BatchCompleted use [master]; +if (db_id() = 1) +begin +-- contained auth is 0 when connected to master +select 0 +end +else +begin +-- need dynamic sql so that we compile this query only when we know resource db is available +exec('select case when authenticating_database_id = 1 then 0 else 1 end from +sys.dm_exec_sessions where session_id = @@SPID') +end;use [master]; --resetting the context carbon sa 0 0 0 0 2064905136 63 2017-08-04 12:28:57.330 2017-08-04 12:28:57.330 +SQL:BatchStarting SELECT +CAST( + serverproperty(N'Servername') + AS sysname) AS [Name] carbon sa 2064905136 63 2017-08-04 12:28:57.437 +SQL:BatchCompleted SELECT +CAST( + serverproperty(N'Servername') + AS sysname) AS [Name] carbon sa 0 0 0 0 2064905136 63 2017-08-04 12:28:57.437 2017-08-04 12:28:57.437 +SQL:BatchStarting + declare @HkeyLocal nvarchar(18) + declare @ServicesRegPath nvarchar(34) + declare @SqlServiceRegPath sysname + declare @BrowserServiceRegPath sysname + declare @MSSqlServerRegPath nvarchar(31) + declare @InstanceNamesRegPath nvarchar(59) + declare @InstanceRegPath sysname + declare @SetupRegPath sysname + declare @NpRegPath sysname + declare @TcpRegPath sysname + declare @RegPathParams sysname + declare @FilestreamRegPath sysname + + select @HkeyLocal=N'HKEY_LOCAL_MACHINE' + + -- Instance-based paths + select @MSSqlServerRegPath=N'SOFTWARE\Microsoft\MSSQLServer' + select @InstanceRegPath=@MSSqlServerRegPath + N'\MSSQLServer' + select @FilestreamRegPath=@InstanceRegPath + N'\Filestream' + select @SetupRegPath=@MSSqlServerRegPath + N'\Setup' + select @RegPathParams=@InstanceRegPath+'\Parameters' + + -- Services + select @ServicesRegPath=N'SYSTEM\CurrentControlSet\Services' + select @SqlServiceRegPath=@ServicesRegPath + N'\MSSQLSERVER' + select @BrowserServiceRegPath=@ServicesRegPath + N'\SQLBrowser' + + -- InstanceId setting + select @InstanceNamesRegPath=N'SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' + + -- Network settings + select @NpRegPath=@InstanceRegPath + N'\SuperSocketNetLib\Np' + select @TcpRegPath=@InstanceRegPath + N'\SuperSocketNetLib\Tcp' + + + + declare @SmoAuditLevel int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'AuditLevel', @SmoAuditLevel OUTPUT + + + + declare @NumErrorLogs int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'NumErrorLogs', @NumErrorLogs OUTPUT + + + + declare @SmoLoginMode int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'LoginMode', @SmoLoginMode OUTPUT + + + + declare @SmoMailProfile nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'MailAccountName', @SmoMailProfile OUTPUT + + + + declare @BackupDirectory nvarchar(512) + if 1=isnull(cast(SERVERPROPERTY('IsLocalDB') as bit), 0) + select @BackupDirectory=cast(SERVERPROPERTY('instancedefaultdatapath') as nvarchar(512)) + else + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'BackupDirectory', @BackupDirectory OUTPUT + + + + declare @SmoPerfMonMode int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'Performance', @SmoPerfMonMode OUTPUT + + if @SmoPerfMonMode is null + begin + set @SmoPerfMonMode = 1000 + end + + + + declare @InstallSqlDataDir nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLDataRoot', @InstallSqlDataDir OUTPUT + + + + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLPath', @SmoRoot OUTPUT + + + + declare @ServiceStartMode int + EXEC master.sys.xp_instance_regread @HkeyLocal, @SqlServiceRegPath, N'Start', @ServiceStartMode OUTPUT + + + + declare @ServiceAccount nvarchar(512) + EXEC master.sys.xp_instance_regread @HkeyLocal, @SqlServiceRegPath, N'ObjectName', @ServiceAccount OUTPUT + + + + declare @NamedPipesEnabled int + exec master.dbo.xp_instance_regread @HkeyLocal, @NpRegPath, N'Enabled', @NamedPipesEnabled OUTPUT + + + + declare @TcpEnabled int + EXEC master.sys.xp_instance_regread @HkeyLocal, @TcpRegPath, N'Enabled', @TcpEnabled OUTPUT + + + + declare @InstallSharedDirectory nvarchar(512) + EXEC master.sys.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLPath', @InstallSharedDirectory OUTPUT + + + + declare @SqlGroup nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLGroup', @SqlGroup OUTPUT + + + + declare @FilestreamLevel int + exec master.dbo.xp_instance_regread @HkeyLocal, @FilestreamRegPath, N'EnableLevel', @FilestreamLevel OUTPUT + + + + declare @FilestreamShareName nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @FilestreamRegPath, N'ShareName', @FilestreamShareName OUTPUT + + + + declare @cluster_name nvarchar(128) + declare @quorum_type tinyint + declare @quorum_state tinyint + BEGIN TRY + SELECT @cluster_name = cluster_name, + @quorum_type = quorum_type, + @quorum_state = quorum_state + FROM sys.dm_hadr_cluster + END TRY + BEGIN CATCH + --Querying this DMV using a contained auth connection throws error 15562 (Module is untrusted) + --because of lack of trustworthiness by the server. This is expected so we just leave the + --values as default + IF(ERROR_NUMBER() NOT IN (297,300, 15562)) + BEGIN + THROW + END + END CATCH + + +SELECT +@SmoAuditLevel AS [AuditLevel], +ISNULL(@NumErrorLogs, -1) AS [NumberOfLogFiles], +(case when @SmoLoginMode < 3 then @SmoLoginMode else 9 end) AS [LoginMode], +ISNULL(@SmoMailProfile,N'') AS [MailProfile], +@BackupDirectory AS [BackupDirectory], +@SmoPerfMonMode AS [PerfMonMode], +ISNULL(@InstallSqlDataDir,N'') AS [InstallDataDirectory], +CAST(@@SERVICENAME AS sysname) AS [ServiceName], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY('instancedefaultdatapath') AS [DefaultFile], +SERVERPROPERTY('instancedefaultlogpath') AS [DefaultLog], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +@ServiceStartMode AS [ServiceStartMode], +ISNULL(@ServiceAccount,N'') AS [ServiceAccount], +CAST(@NamedPipesEnabled AS bit) AS [NamedPipesEnabled], +CAST(@TcpEnabled AS bit) AS [TcpEnabled], +ISNULL(@InstallSharedDirectory,N'') AS [InstallSharedDirectory], +ISNULL(suser_sname(sid_binary(ISNULL(@SqlGroup,N''))),N'') AS [SqlDomainGroup], +case when 1=msdb.dbo.fn_syspolicy_is_automation_enabled() and exists (select * from msdb.dbo.syspolicy_system_health_state where target_query_expression_with_id like 'Server%' ) then 1 else 0 end AS [PolicyHealthState], +@FilestreamLevel AS [FilestreamLevel], +ISNULL(@FilestreamShareName,N'') AS [FilestreamShareName], +-1 AS [TapeLoadWaitTime], +CAST(SERVERPROPERTY(N'IsHadrEnabled') AS bit) AS [IsHadrEnabled], +SERVERPROPERTY(N'HADRManagerStatus') AS [HadrManagerStatus], +ISNULL(@cluster_name, '') AS [ClusterName], +ISNULL(@quorum_type, 4) AS [ClusterQuorumType], +ISNULL(@quorum_state, 3) AS [ClusterQuorumState], +SUSER_SID(@ServiceAccount, 0) AS [ServiceAccountSid], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled], +N'Windows' AS [HostPlatform], +CAST( + serverproperty(N'Servername') + AS sysname) AS [Name], +CAST( + ISNULL(serverproperty(N'instancename'),N'') + AS sysname) AS [InstanceName], +CAST(0x0001 AS int) AS [Status], +N'\' AS [PathSeparator], +0 AS [IsContainedAuthentication], +CAST(null AS int) AS [ServerType] + + drop table #SVer + + Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.437 +SQL:BatchCompleted + declare @HkeyLocal nvarchar(18) + declare @ServicesRegPath nvarchar(34) + declare @SqlServiceRegPath sysname + declare @BrowserServiceRegPath sysname + declare @MSSqlServerRegPath nvarchar(31) + declare @InstanceNamesRegPath nvarchar(59) + declare @InstanceRegPath sysname + declare @SetupRegPath sysname + declare @NpRegPath sysname + declare @TcpRegPath sysname + declare @RegPathParams sysname + declare @FilestreamRegPath sysname + + select @HkeyLocal=N'HKEY_LOCAL_MACHINE' + + -- Instance-based paths + select @MSSqlServerRegPath=N'SOFTWARE\Microsoft\MSSQLServer' + select @InstanceRegPath=@MSSqlServerRegPath + N'\MSSQLServer' + select @FilestreamRegPath=@InstanceRegPath + N'\Filestream' + select @SetupRegPath=@MSSqlServerRegPath + N'\Setup' + select @RegPathParams=@InstanceRegPath+'\Parameters' + + -- Services + select @ServicesRegPath=N'SYSTEM\CurrentControlSet\Services' + select @SqlServiceRegPath=@ServicesRegPath + N'\MSSQLSERVER' + select @BrowserServiceRegPath=@ServicesRegPath + N'\SQLBrowser' + + -- InstanceId setting + select @InstanceNamesRegPath=N'SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' + + -- Network settings + select @NpRegPath=@InstanceRegPath + N'\SuperSocketNetLib\Np' + select @TcpRegPath=@InstanceRegPath + N'\SuperSocketNetLib\Tcp' + + + + declare @SmoAuditLevel int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'AuditLevel', @SmoAuditLevel OUTPUT + + + + declare @NumErrorLogs int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'NumErrorLogs', @NumErrorLogs OUTPUT + + + + declare @SmoLoginMode int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'LoginMode', @SmoLoginMode OUTPUT + + + + declare @SmoMailProfile nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'MailAccountName', @SmoMailProfile OUTPUT + + + + declare @BackupDirectory nvarchar(512) + if 1=isnull(cast(SERVERPROPERTY('IsLocalDB') as bit), 0) + select @BackupDirectory=cast(SERVERPROPERTY('instancedefaultdatapath') as nvarchar(512)) + else + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'BackupDirectory', @BackupDirectory OUTPUT + + + + declare @SmoPerfMonMode int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'Performance', @SmoPerfMonMode OUTPUT + + if @SmoPerfMonMode is null + begin + set @SmoPerfMonMode = 1000 + end + + + + declare @InstallSqlDataDir nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLDataRoot', @InstallSqlDataDir OUTPUT + + + + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLPath', @SmoRoot OUTPUT + + + + declare @ServiceStartMode int + EXEC master.sys.xp_instance_regread @HkeyLocal, @SqlServiceRegPath, N'Start', @ServiceStartMode OUTPUT + + + + declare @ServiceAccount nvarchar(512) + EXEC master.sys.xp_instance_regread @HkeyLocal, @SqlServiceRegPath, N'ObjectName', @ServiceAccount OUTPUT + + + + declare @NamedPipesEnabled int + exec master.dbo.xp_instance_regread @HkeyLocal, @NpRegPath, N'Enabled', @NamedPipesEnabled OUTPUT + + + + declare @TcpEnabled int + EXEC master.sys.xp_instance_regread @HkeyLocal, @TcpRegPath, N'Enabled', @TcpEnabled OUTPUT + + + + declare @InstallSharedDirectory nvarchar(512) + EXEC master.sys.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLPath', @InstallSharedDirectory OUTPUT + + + + declare @SqlGroup nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLGroup', @SqlGroup OUTPUT + + + + declare @FilestreamLevel int + exec master.dbo.xp_instance_regread @HkeyLocal, @FilestreamRegPath, N'EnableLevel', @FilestreamLevel OUTPUT + + + + declare @FilestreamShareName nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @FilestreamRegPath, N'ShareName', @FilestreamShareName OUTPUT + + + + declare @cluster_name nvarchar(128) + declare @quorum_type tinyint + declare @quorum_state tinyint + BEGIN TRY + SELECT @cluster_name = cluster_name, + @quorum_type = quorum_type, + @quorum_state = quorum_state + FROM sys.dm_hadr_cluster + END TRY + BEGIN CATCH + --Querying this DMV using a contained auth connection throws error 15562 (Module is untrusted) + --because of lack of trustworthiness by the server. This is expected so we just leave the + --values as default + IF(ERROR_NUMBER() NOT IN (297,300, 15562)) + BEGIN + THROW + END + END CATCH + + +SELECT +@SmoAuditLevel AS [AuditLevel], +ISNULL(@NumErrorLogs, -1) AS [NumberOfLogFiles], +(case when @SmoLoginMode < 3 then @SmoLoginMode else 9 end) AS [LoginMode], +ISNULL(@SmoMailProfile,N'') AS [MailProfile], +@BackupDirectory AS [BackupDirectory], +@SmoPerfMonMode AS [PerfMonMode], +ISNULL(@InstallSqlDataDir,N'') AS [InstallDataDirectory], +CAST(@@SERVICENAME AS sysname) AS [ServiceName], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY('instancedefaultdatapath') AS [DefaultFile], +SERVERPROPERTY('instancedefaultlogpath') AS [DefaultLog], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +@ServiceStartMode AS [ServiceStartMode], +ISNULL(@ServiceAccount,N'') AS [ServiceAccount], +CAST(@NamedPipesEnabled AS bit) AS [NamedPipesEnabled], +CAST(@TcpEnabled AS bit) AS [TcpEnabled], +ISNULL(@InstallSharedDirectory,N'') AS [InstallSharedDirectory], +ISNULL(suser_sname(sid_binary(ISNULL(@SqlGroup,N''))),N'') AS [SqlDomainGroup], +case when 1=msdb.dbo.fn_syspolicy_is_automation_enabled() and exists (select * from msdb.dbo.syspolicy_system_health_state where target_query_expression_with_id like 'Server%' ) then 1 else 0 end AS [PolicyHealthState], +@FilestreamLevel AS [FilestreamLevel], +ISNULL(@FilestreamShareName,N'') AS [FilestreamShareName], +-1 AS [TapeLoadWaitTime], +CAST(SERVERPROPERTY(N'IsHadrEnabled') AS bit) AS [IsHadrEnabled], +SERVERPROPERTY(N'HADRManagerStatus') AS [HadrManagerStatus], +ISNULL(@cluster_name, '') AS [ClusterName], +ISNULL(@quorum_type, 4) AS [ClusterQuorumType], +ISNULL(@quorum_state, 3) AS [ClusterQuorumState], +SUSER_SID(@ServiceAccount, 0) AS [ServiceAccountSid], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled], +N'Windows' AS [HostPlatform], +CAST( + serverproperty(N'Servername') + AS sysname) AS [Name], +CAST( + ISNULL(serverproperty(N'instancename'),N'') + AS sysname) AS [InstanceName], +CAST(0x0001 AS int) AS [Status], +N'\' AS [PathSeparator], +0 AS [IsContainedAuthentication], +CAST(null AS int) AS [ServerType] + + drop table #SVer + + Core .Net SqlClient Data Provider sa 16 9 0 16 2064905136 66 2017-08-04 12:28:57.437 2017-08-04 12:28:57.453 +SQL:BatchStarting + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SmoRoot OUTPUT + + + +SELECT +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +N'Windows' AS [HostPlatform], +N'\' AS [PathSeparator], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled] carbon sa 2064905136 63 2017-08-04 12:28:57.480 +SQL:BatchCompleted + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SmoRoot OUTPUT + + + +SELECT +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +N'Windows' AS [HostPlatform], +N'\' AS [PathSeparator], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled] carbon sa 0 5 0 2 2064905136 63 2017-08-04 12:28:57.480 2017-08-04 12:28:57.483 +SQL:BatchStarting SELECT +cfg.name AS [Name], +cfg.configuration_id AS [Number], +cfg.minimum AS [Minimum], +cfg.maximum AS [Maximum], +cfg.is_dynamic AS [Dynamic], +cfg.is_advanced AS [Advanced], +cfg.value AS [ConfigValue], +cfg.value_in_use AS [RunValue], +cfg.description AS [Description] +FROM +sys.configurations AS cfg Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.533 +SQL:BatchCompleted SELECT +cfg.name AS [Name], +cfg.configuration_id AS [Number], +cfg.minimum AS [Minimum], +cfg.maximum AS [Maximum], +cfg.is_dynamic AS [Dynamic], +cfg.is_advanced AS [Advanced], +cfg.value AS [ConfigValue], +cfg.value_in_use AS [RunValue], +cfg.description AS [Description] +FROM +sys.configurations AS cfg Core .Net SqlClient Data Provider sa 0 4 0 0 2064905136 66 2017-08-04 12:28:57.533 2017-08-04 12:28:57.533 +RPC:Completed exec sp_executesql N'SELECT +dtb.name AS [Name] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' Core .Net SqlClient Data Provider sa 0 8 0 0 2064905136 66 2017-08-04 12:28:57.580 2017-08-04 12:28:57.580 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00D400000082001800E7206E0076006100720063006800610072002800380037002900AE000000530045004C004500430054000A006400740062002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A006D00610073007400650072002E007300790073002E0064006100740061006200610073006500730020004100530020006400740062000A00570048004500520045000A0028006400740062002E006E0061006D0065003D0040005F006D00730070006100720061006D005F00300029005A00000082001800 +RPC:Completed exec sp_executesql N'SELECT +dtb.collation_name AS [Collation], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' carbon sa 0 8 0 0 2064905136 63 2017-08-04 12:28:57.580 2017-08-04 12:28:57.580 0X00000000040000001A00730070005F006500780065006300750074006500730071006C002E01000082001A00E7206E0076006100720063006800610072002800310033003100290006010000530045004C004500430054000A006400740062002E0063006F006C006C006100740069006F006E005F006E0061006D00650020004100530020005B0043006F006C006C006100740069006F006E005D002C000A006400740062002E006E0061006D00650020004100530020005B00440061007400610062006100730065004E0061006D00650032005D000A00460052004F004D000A006D00610073007400650072002E007300790073002E006400610074006100 +SQL:BatchStarting SELECT +dtb.name AS [Name], +dtb.database_id AS [ID], +CAST(case when dtb.name in ('master','model','msdb','tempdb') then 1 else dtb.is_distributor end AS bit) AS [IsSystemObject], +dtb.collation_name AS [Collation], +CAST(has_dbaccess(dtb.name) AS bit) AS [IsAccessible], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +ORDER BY +[Name] ASC carbon sa 2064905136 63 2017-08-04 12:28:57.600 +RPC:Completed exec sp_executesql N'SELECT +dtb.compatibility_level AS [CompatibilityLevel], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' Core .Net SqlClient Data Provider sa 0 10 0 0 2064905136 66 2017-08-04 12:28:57.610 2017-08-04 12:28:57.610 0X00000000040000001A00730070005F006500780065006300750074006500730071006C004A01000082001A00E7206E0076006100720063006800610072002800310034003500290022010000530045004C004500430054000A006400740062002E0063006F006D007000610074006900620069006C006900740079005F006C006500760065006C0020004100530020005B0043006F006D007000610074006900620069006C006900740079004C006500760065006C005D002C000A006400740062002E006E0061006D00650020004100530020005B00440061007400610062006100730065004E0061006D00650032005D000A00460052004F004D000A006D00 +SQL:BatchCompleted SELECT +dtb.name AS [Name], +dtb.database_id AS [ID], +CAST(case when dtb.name in ('master','model','msdb','tempdb') then 1 else dtb.is_distributor end AS bit) AS [IsSystemObject], +dtb.collation_name AS [Collation], +CAST(has_dbaccess(dtb.name) AS bit) AS [IsAccessible], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +ORDER BY +[Name] ASC carbon sa 16 174 0 12 2064905136 63 2017-08-04 12:28:57.600 2017-08-04 12:28:57.613 +RPC:Completed exec sp_executesql N'SELECT +ISNULL((case dmi.mirroring_redo_queue_type when N''UNLIMITED'' then 0 else dmi.mirroring_redo_queue end),0) AS [MirroringRedoQueueMaxSize], +ISNULL(dmi.mirroring_connection_timeout,0) AS [MirroringTimeout], +ISNULL(dmi.mirroring_partner_name,'''') AS [MirroringPartner], +ISNULL(dmi.mirroring_partner_instance,'''') AS [MirroringPartnerInstance], +ISNULL(dmi.mirroring_role,0) AS [MirroringRole], +ISNULL(dmi.mirroring_safety_level + 1, 0) AS [MirroringSafetyLevel], +ISNULL(dmi.mirroring_state + 1, 0) AS [MirroringStatus], +ISNULL(dmi.mirroring_witness_name,'''') AS [MirroringWitness], +ISNULL(dmi.mirroring_witness_state + 1, 0) AS [MirroringWitnessStatus], +CAST(case when dmi.mirroring_partner_name is null then 0 else 1 end AS bit) AS [IsMirroringEnabled], +ISNULL(dmi.mirroring_guid,''00000000-0000-0000-0000-0000000000000000'') AS [MirroringID], +ISNULL(dmi.mirroring_role_sequence,0) AS [MirroringRoleSequence], +ISNULL(dmi.mirroring_safety_sequence,0) AS [MirroringSafetySequence], +ISNULL(dmi.mirroring_failover_lsn,0) AS [MirroringFailoverLogSequenceNumber], +dtb.is_ansi_null_default_on AS [AnsiNullDefault], +dtb.is_ansi_nulls_on AS [AnsiNullsEnabled], +dtb.is_ansi_padding_on AS [AnsiPaddingEnabled], +dtb.is_ansi_warnings_on AS [AnsiWarningsEnabled], +dtb.is_arithabort_on AS [ArithmeticAbortEnabled], +dtb.is_auto_shrink_on AS [AutoShrink], +dtb.is_cursor_close_on_commit_on AS [CloseCursorsOnCommitEnabled], +dtb.is_concat_null_yields_null_on AS [ConcatenateNullYieldsNull], +dtb.is_numeric_roundabort_on AS [NumericRoundAbortEnabled], +dtb.is_quoted_identifier_on AS [QuotedIdentifiersEnabled], +dtb.is_read_only AS [ReadOnly], +dtb.is_recursive_triggers_on AS [RecursiveTriggersEnabled], +dtb.user_access AS [UserAccess], +dtb.is_db_chaining_on AS [DatabaseOwnershipChaining], +dtb.is_auto_update_stats_async_on AS [AutoUpdateStatisticsAsync], +dtb.is_date_correlation_on AS [DateCorrelationOptimization], +dtb.is_trustworthy_on AS [Trustworthy], +dtb.name AS [Name], +dtb.database_id AS [ID], +dtb.create_date AS [CreateDate], +dtb.is_auto_create_stats_on AS [AutoCreateStatisticsEnabled], +dtb.is_auto_update_stats_on AS [AutoUpdateStatisticsEnabled], +dtb.is_parameterization_forced AS [IsParameterizationForced], +dtb.is_read_committed_snapshot_on AS [IsReadCommittedSnapshotOn], +CAST(isnull(dtb.source_database_id, 0) AS bit) AS [IsDatabaseSnapshot], +ISNULL(DB_NAME(dtb.source_database_id), N'''') AS [DatabaseSnapshotBaseName], +dtb.is_fulltext_enabled AS [IsFullTextEnabled], +dtb.service_broker_guid AS [ServiceBrokerGuid], +dtb.snapshot_isolation_state AS [SnapshotIsolationState], +(dtb.is_published*1+dtb.is_subscribed*2+dtb.is_merge_published*4) AS [ReplicationOptions], +dtb.is_local_cursor_default AS [LocalCursorsDefault], +dtb.page_verify_option AS [PageVerify], +dtb.recovery_model AS [RecoveryModel], +dtb.is_auto_close_on AS [AutoClose], +dtb.is_broker_enabled AS [BrokerEnabled], +ISNULL(suser_sname(dtb.owner_sid),'''') AS [Owner], +ISNULL(dtb.log_reuse_wait,0) AS [LogReuseWaitStatus], +drs.recovery_fork_guid AS [RecoveryForkGuid], +drs.database_guid AS [DatabaseGuid], +CAST((case when drs.last_log_backup_lsn is not null then 1 else 0 end) AS bit) AS [HasFullBackup], +CAST(case when dtb.name in (''master'',''model'',''msdb'',''tempdb'') then 1 else dtb.is_distributor end AS bit) AS [IsSystemObject], +CAST(case when ctb.database_id is null then 0 else 1 end AS bit) AS [ChangeTrackingEnabled], +CAST(ISNULL(ctb.is_auto_cleanup_on,0) AS bit) AS [ChangeTrackingAutoCleanUp], +ISNULL(ctb.retention_period,0) AS [ChangeTrackingRetentionPeriod], +CAST(ISNULL(ctb.retention_period_units,0) AS tinyint) AS [ChangeTrackingRetentionPeriodUnits], +dtb.containment AS [ContainmentType], +dtb.default_language_lcid AS [DefaultLanguageLcid], +dtb.default_language_name AS [DefaultLanguageName], +dtb.default_fulltext_language_lcid AS [DefaultFullTextLanguageLcid], +ISNULL(dtb.default_fulltext_language_name,N'''') AS [DefaultFullTextLanguageName], +CAST(dtb.is_nested_triggers_on AS bit) AS [NestedTriggersEnabled], +CAST(dtb.is_transform_noise_words_on AS bit) AS [TransformNoiseWords], +dtb.two_digit_year_cutoff AS [TwoDigitYearCutoff], +dtb.target_recovery_time_in_seconds AS [TargetRecoveryTime], +dtb.delayed_durability AS [DelayedDurability], +dtb.is_auto_create_stats_incremental_on AS [AutoCreateIncrementalStatisticsEnabled], + + case + when dtb.collation_name is null then 0x200 + else 0 + end | + case + when 1 = dtb.is_in_standby then 0x40 + else 0 + end | + case dtb.state + when 1 then 0x2 + when 2 then 0x8 + when 3 then 0x4 + when 4 then 0x10 + when 5 then 0x100 + when 6 then 0x20 + else 1 + end + AS [Status], +CAST(( case LOWER(convert( nvarchar(128), DATABASEPROPERTYEX(dtb.name, ''Updateability''))) when ''read_write'' then 1 else 0 end) AS bit) AS [IsUpdateable], +CAST(dtb.is_encrypted AS bit) AS [EncryptionEnabled], +CAST(dtb.is_honor_broker_priority_on AS bit) AS [HonorBrokerPriority], +CAST( + case + when SERVERPROPERTY(''EngineEdition'') = 6 then cast(1 as bit) + else cast(0 as bit) + end + AS bit) AS [IsSqlDw], +CAST(has_dbaccess(dtb.name) AS bit) AS [IsAccessible], +ISNULL(fsopt.directory_name , N'''') AS [FilestreamDirectoryName], +ISNULL(fsopt.non_transacted_access , 0) AS [FilestreamNonTransactedAccess], +CAST(dtb.is_remote_data_archive_enabled AS bit) AS [RemoteDataArchiveEnabled], +NULL AS [RemoteDataArchiveEndpoint], +NULL AS [RemoteDataArchiveLinkedServer], +NULL AS [RemoteDatabaseName], +0 AS [RemoteDataArchiveUseFederatedServiceAccount], +NULL AS [RemoteDataArchiveCredential], +0 AS [MaxDop], +NULL AS [MaxDopForSecondary], +0 AS [LegacyCardinalityEstimation], +2 AS [LegacyCardinalityEstimationForSecondary], +0 AS [ParameterSniffing], +2 AS [ParameterSniffingForSecondary], +0 AS [QueryOptimizerHotfixes], +2 AS [QueryOptimizerHotfixesForSecondary], +dtb.name AS [DatabaseName2], +dtb.containment AS [ContainmentType2], +dtb.name AS [DatabaseName5], +dtb.name AS [DatabaseName6] +FROM +master.sys.databases AS dtb +LEFT OUTER JOIN sys.database_mirroring AS dmi ON dmi.database_id = dtb.database_id +LEFT OUTER JOIN sys.database_recovery_status AS drs ON drs.database_id = dtb.database_id +LEFT OUTER JOIN sys.change_tracking_databases AS ctb ON ctb.database_id = dtb.database_id +LEFT OUTER JOIN sys.database_filestream_options AS fsopt ON fsopt.database_id = dtb.database_id +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' Core .Net SqlClient Data Provider sa 0 47 0 0 2064905136 66 2017-08-04 12:28:57.677 2017-08-04 12:28:57.677 0X00000000040000001A00730070005F006500780065006300750074006500730071006C002E32000082000A0063206E00740065007800740016320000530045004C004500430054000A00490053004E0055004C004C00280028006300610073006500200064006D0069002E006D006900720072006F00720069006E0067005F007200650064006F005F00710075006500750065005F00740079007000650020007700680065006E0020004E00270055004E004C0049004D004900540045004400270020007400680065006E0020003000200065006C0073006500200064006D0069002E006D006900720072006F00720069006E0067005F007200650064006F00 +SQL:BatchStarting select +case + when cfg.configuration_id = 124 -- configuration id for default language + then (select lcid from sys.syslanguages as sl where sl.langid = cfg.value_in_use) -- getting default language LCID from default language langid + else cfg.value_in_use +end as value, +case + when cfg.configuration_id = 124 -- configuration id for default language + then (select name collate catalog_default from sys.syslanguages as sl where sl.langid = cfg.value_in_use) -- getting default language name from default language langid + when cfg.configuration_id = 1126 -- configuration id for default fulltext language + then ISNULL((select name collate catalog_default from sys.fulltext_languages as fl where fl.lcid = cfg.value_in_use), N'') -- getting default fulltext language name from default fulltext language lcid + else null +end as name, +cfg.configuration_id as configuration_id +from sys.configurations as cfg +where cfg.configuration_id in (115, 124, 1126, 1127, 1555) +order by cfg.configuration_id asc Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.693 +SQL:BatchCompleted select +case + when cfg.configuration_id = 124 -- configuration id for default language + then (select lcid from sys.syslanguages as sl where sl.langid = cfg.value_in_use) -- getting default language LCID from default language langid + else cfg.value_in_use +end as value, +case + when cfg.configuration_id = 124 -- configuration id for default language + then (select name collate catalog_default from sys.syslanguages as sl where sl.langid = cfg.value_in_use) -- getting default language name from default language langid + when cfg.configuration_id = 1126 -- configuration id for default fulltext language + then ISNULL((select name collate catalog_default from sys.fulltext_languages as fl where fl.lcid = cfg.value_in_use), N'') -- getting default fulltext language name from default fulltext language lcid + else null +end as name, +cfg.configuration_id as configuration_id +from sys.configurations as cfg +where cfg.configuration_id in (115, 124, 1126, 1127, 1555) +order by cfg.configuration_id asc Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.693 2017-08-04 12:28:57.693 +SQL:BatchStarting SELECT CASE WHEN has_dbaccess(N'master') = 1 THEN 'true' ELSE 'false' END Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.700 +SQL:BatchCompleted SELECT CASE WHEN has_dbaccess(N'master') = 1 THEN 'true' ELSE 'false' END Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.700 2017-08-04 12:28:57.700 +RPC:Completed exec sp_executesql N'SELECT COLLATIONPROPERTY((select collation_name from sys.databases where name = ISNULL(@dbname, db_name())), ''LCID''), COLLATIONPROPERTY((select collation_name from sys.databases where name = ISNULL(@dbname, db_name())), ''ComparisonStyle'')',N'@dbname nvarchar(6)',@dbname=N'master' Core .Net SqlClient Data Provider sa 0 22 0 0 2064905136 66 2017-08-04 12:28:57.713 2017-08-04 12:28:57.713 0X00000000040000001A00730070005F006500780065006300750074006500730071006C000402000082001A00E7206E00760061007200630068006100720028003200330038002900DC010000530045004C00450043005400200043004F004C004C004100540049004F004E00500052004F005000450052005400590028002800730065006C00650063007400200063006F006C006C006100740069006F006E005F006E0061006D0065002000660072006F006D0020007300790073002E0064006100740061006200610073006500730020007700680065007200650020006E0061006D00650020003D002000490053004E0055004C004C002800400064006200 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + Core .Net SqlClient Data Provider sa 2064905136 67 2017-08-04 12:28:57.723 +SQL:BatchStarting SELECT +eds.location AS [RemoteDataArchiveEndpoint], +eds.name AS [RemoteDataArchiveLinkedServer], +rdad.remote_database_name AS [RemoteDatabaseName], +rdad.federated_service_account AS [RemoteDataArchiveUseFederatedServiceAccount], +case when rdad.federated_service_account = 1 then null else cred.name end AS [RemoteDataArchiveCredential] +FROM +sys.remote_data_archive_databases rdad +INNER JOIN sys.external_data_sources eds ON rdad.data_source_id = eds.data_source_id +LEFT OUTER JOIN sys.database_scoped_credentials cred ON eds.credential_id = cred.credential_id Core .Net SqlClient Data Provider sa 2064905136 67 2017-08-04 12:28:57.723 +SQL:BatchCompleted SELECT +eds.location AS [RemoteDataArchiveEndpoint], +eds.name AS [RemoteDataArchiveLinkedServer], +rdad.remote_database_name AS [RemoteDatabaseName], +rdad.federated_service_account AS [RemoteDataArchiveUseFederatedServiceAccount], +case when rdad.federated_service_account = 1 then null else cred.name end AS [RemoteDataArchiveCredential] +FROM +sys.remote_data_archive_databases rdad +INNER JOIN sys.external_data_sources eds ON rdad.data_source_id = eds.data_source_id +LEFT OUTER JOIN sys.database_scoped_credentials cred ON eds.credential_id = cred.credential_id Core .Net SqlClient Data Provider sa 0 2 0 0 2064905136 67 2017-08-04 12:28:57.723 2017-08-04 12:28:57.723 +Audit Logout Core .Net SqlClient Data Provider sa 0 2 0 7 2064905136 67 2017-08-04 12:28:57.723 2017-08-04 12:28:57.730 +SQL:BatchStarting SELECT CASE WHEN has_dbaccess(N'master') = 1 THEN 'true' ELSE 'false' END Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.740 +SQL:BatchCompleted SELECT CASE WHEN has_dbaccess(N'master') = 1 THEN 'true' ELSE 'false' END Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.740 2017-08-04 12:28:57.740 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + Core .Net SqlClient Data Provider sa 2064905136 67 2017-08-04 12:28:57.760 +SQL:BatchStarting SELECT +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'MAXDOP') AS [MaxDop], +(select value_for_secondary from sys.database_scoped_configurations as dsc where dsc.name = 'MAXDOP') AS [MaxDopForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'LEGACY_CARDINALITY_ESTIMATION') AS [LegacyCardinalityEstimation], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'LEGACY_CARDINALITY_ESTIMATION') AS [LegacyCardinalityEstimationForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'PARAMETER_SNIFFING') AS [ParameterSniffing], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'PARAMETER_SNIFFING') AS [ParameterSniffingForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'QUERY_OPTIMIZER_HOTFIXES') AS [QueryOptimizerHotfixes], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'QUERY_OPTIMIZER_HOTFIXES') AS [QueryOptimizerHotfixesForSecondary] Core .Net SqlClient Data Provider sa 2064905136 67 2017-08-04 12:28:57.760 +SQL:BatchCompleted SELECT +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'MAXDOP') AS [MaxDop], +(select value_for_secondary from sys.database_scoped_configurations as dsc where dsc.name = 'MAXDOP') AS [MaxDopForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'LEGACY_CARDINALITY_ESTIMATION') AS [LegacyCardinalityEstimation], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'LEGACY_CARDINALITY_ESTIMATION') AS [LegacyCardinalityEstimationForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'PARAMETER_SNIFFING') AS [ParameterSniffing], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'PARAMETER_SNIFFING') AS [ParameterSniffingForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'QUERY_OPTIMIZER_HOTFIXES') AS [QueryOptimizerHotfixes], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'QUERY_OPTIMIZER_HOTFIXES') AS [QueryOptimizerHotfixesForSecondary] Core .Net SqlClient Data Provider sa 0 44 0 0 2064905136 67 2017-08-04 12:28:57.760 2017-08-04 12:28:57.760 +Audit Logout Core .Net SqlClient Data Provider sa 0 44 0 3 2064905136 67 2017-08-04 12:28:57.760 2017-08-04 12:28:57.763 +SQL:BatchStarting SELECT +(@@microsoftversion / 0x1000000) & 0xff AS [VersionMajor] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.770 +SQL:BatchCompleted SELECT +(@@microsoftversion / 0x1000000) & 0xff AS [VersionMajor] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.770 2017-08-04 12:28:57.770 +SQL:BatchStarting select is_srvrolemember('sysadmin') * 1 +is_srvrolemember('serveradmin') * 2 +is_srvrolemember('setupadmin') * 4 +is_srvrolemember('securityadmin') * 8 +is_srvrolemember('processadmin') * 16 +is_srvrolemember('dbcreator') * 32 +is_srvrolemember('diskadmin') * 64+ is_srvrolemember('bulkadmin') * 128 Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.770 +SQL:BatchCompleted select is_srvrolemember('sysadmin') * 1 +is_srvrolemember('serveradmin') * 2 +is_srvrolemember('setupadmin') * 4 +is_srvrolemember('securityadmin') * 8 +is_srvrolemember('processadmin') * 16 +is_srvrolemember('dbcreator') * 32 +is_srvrolemember('diskadmin') * 64+ is_srvrolemember('bulkadmin') * 128 Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.770 2017-08-04 12:28:57.770 +SQL:BatchStarting USE [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.777 +SQL:BatchCompleted USE [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.777 2017-08-04 12:28:57.777 +SQL:BatchStarting select suser_sname((select sid from sys.database_principals where name = N'dbo')); Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.777 +SQL:BatchCompleted select suser_sname((select sid from sys.database_principals where name = N'dbo')); Core .Net SqlClient Data Provider sa 0 6 0 0 2064905136 66 2017-08-04 12:28:57.777 2017-08-04 12:28:57.777 +RPC:Completed exec sp_executesql N' + create table #tempbackup (database_name nvarchar(128), [type] char(1), backup_finish_date datetime) + insert into #tempbackup select database_name, [type], max(backup_finish_date) from msdb..backupset where [type] = ''D'' or [type] = ''L'' or [type]=''I'' group by database_name, [type] + + + +SELECT +(select backup_finish_date from #tempbackup where type = @_msparam_0 and db_id(database_name) = dtb.database_id) AS [LastBackupDate] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_1) + + drop table #tempbackup + +',N'@_msparam_0 nvarchar(4000),@_msparam_1 nvarchar(4000)',@_msparam_0=N'D',@_msparam_1=N'master' Core .Net SqlClient Data Provider sa 15 221 0 5 2064905136 66 2017-08-04 12:28:57.790 2017-08-04 12:28:57.797 0X00000000050000001A00730070005F006500780065006300750074006500730071006C006C04000082001A00E7206E00760061007200630068006100720028003500340036002900440400000A002000200020002000200020002000200063007200650061007400650020007400610062006C00650020002300740065006D0070006200610063006B007500700020002800640061007400610062006100730065005F006E0061006D00650020006E007600610072006300680061007200280031003200380029002C0020005B0074007900700065005D00200063006800610072002800310029002C0020006200610063006B00750070005F00660069006E00 +RPC:Completed exec sp_executesql N' + create table #tempbackup (database_name nvarchar(128), [type] char(1), backup_finish_date datetime) + insert into #tempbackup select database_name, [type], max(backup_finish_date) from msdb..backupset where [type] = ''D'' or [type] = ''L'' or [type]=''I'' group by database_name, [type] + + + +SELECT +(select backup_finish_date from #tempbackup where type = @_msparam_0 and db_id(database_name) = dtb.database_id) AS [LastLogBackupDate] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_1) + + drop table #tempbackup + +',N'@_msparam_0 nvarchar(4000),@_msparam_1 nvarchar(4000)',@_msparam_0=N'L',@_msparam_1=N'master' Core .Net SqlClient Data Provider sa 0 200 1 3 2064905136 66 2017-08-04 12:28:57.797 2017-08-04 12:28:57.800 0X00000000050000001A00730070005F006500780065006300750074006500730071006C007204000082001A00E7206E007600610072006300680061007200280035003400390029004A0400000A002000200020002000200020002000200063007200650061007400650020007400610062006C00650020002300740065006D0070006200610063006B007500700020002800640061007400610062006100730065005F006E0061006D00650020006E007600610072006300680061007200280031003200380029002C0020005B0074007900700065005D00200063006800610072002800310029002C0020006200610063006B00750070005F00660069006E00 +RPC:Completed exec sp_executesql N'SELECT +dtb.collation_name AS [Collation], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' Core .Net SqlClient Data Provider sa 0 8 0 0 2064905136 66 2017-08-04 12:28:57.803 2017-08-04 12:28:57.803 0X00000000040000001A00730070005F006500780065006300750074006500730071006C002E01000082001A00E7206E0076006100720063006800610072002800310033003100290006010000530045004C004500430054000A006400740062002E0063006F006C006C006100740069006F006E005F006E0061006D00650020004100530020005B0043006F006C006C006100740069006F006E005D002C000A006400740062002E006E0061006D00650020004100530020005B00440061007400610062006100730065004E0061006D00650032005D000A00460052004F004D000A006D00610073007400650072002E007300790073002E006400610074006100 +SQL:BatchStarting + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SmoRoot OUTPUT + + + +SELECT +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +N'Windows' AS [HostPlatform], +N'\' AS [PathSeparator], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.817 +SQL:BatchCompleted + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SmoRoot OUTPUT + + + +SELECT +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +N'Windows' AS [HostPlatform], +N'\' AS [PathSeparator], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled] Core .Net SqlClient Data Provider sa 0 5 0 1 2064905136 66 2017-08-04 12:28:57.817 2017-08-04 12:28:57.817 +RPC:Completed exec sp_executesql N' + create table #tmp_sp_db_vardecimal_storage_format (dbname sysname null, vardecimal_enabled varchar(3) null) + if exists (select o.object_id from sys.system_objects o where o.name=N''sp_db_vardecimal_storage_format'') + begin + insert into #tmp_sp_db_vardecimal_storage_format exec sys.sp_db_vardecimal_storage_format + end + + + +SELECT + + case + when vardec.vardecimal_enabled = ''ON'' then cast(1 as bit) + else cast(0 as bit) + end + AS [IsVarDecimalStorageFormatEnabled] +FROM +master.sys.databases AS dtb +LEFT OUTER JOIN #tmp_sp_db_vardecimal_storage_format as vardec ON dtb.database_id = db_id(vardec.dbname) +WHERE +(dtb.name=@_msparam_0) + + drop table #tmp_sp_db_vardecimal_storage_format + +',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' Core .Net SqlClient Data Provider sa 16 469 1 6 2064905136 66 2017-08-04 12:28:57.820 2017-08-04 12:28:57.827 0X00000000040000001A00730070005F006500780065006300750074006500730071006C002406000082001A00E7206E00760061007200630068006100720028003700360036002900FC0500000A002000200020002000200020002000200063007200650061007400650020007400610062006C0065002000230074006D0070005F00730070005F00640062005F0076006100720064006500630069006D0061006C005F00730074006F0072006100670065005F0066006F0072006D006100740020002800640062006E0061006D00650020007300790073006E0061006D00650020006E0075006C006C002C00200076006100720064006500630069006D006100 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.867 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.867 2017-08-04 12:28:57.867 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'MAXDOP' Core .Net SqlClient Data Provider sa 0 8 0 0 2064905136 66 2017-08-04 12:28:57.867 2017-08-04 12:28:57.867 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00EA00000082001800E7206E0076006100720063006800610072002800390038002900C4000000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A007300790073002E00640061007400610062006100730065005F00730063006F007000650064005F0063006F006E00660069006700750072006100740069006F006E0073002000410053002000730070000A00570048004500520045000A002800730070002E006E0061006D0065003D0040005F006D0073007000 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.870 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.870 2017-08-04 12:28:57.870 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name], +sp.configuration_id AS [Id], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value,''BaseType'') = ''bit'' THEN + CASE value + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value, ''NULL'') + END + AS nvarchar(200)) AS [Value], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value_for_secondary,''BaseType'') = ''bit'' THEN + CASE value_for_secondary + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value_for_secondary, ''PRIMARY'') + END + AS nvarchar(200)) AS [ValueForSecondary] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'MAXDOP' Core .Net SqlClient Data Provider sa 0 10 0 0 2064905136 66 2017-08-04 12:28:57.873 2017-08-04 12:28:57.873 0X00000000040000001A00730070005F006500780065006300750074006500730071006C009A05000082001A00E7206E0076006100720063006800610072002800360039003700290072050000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D002C000A00730070002E0063006F006E00660069006700750072006100740069006F006E005F006900640020004100530020005B00490064005D002C000A00430041005300540028000A0020002000200020002000200043004100530045000A00200020002000200020002000200020005700480045004E002000530051004C005F005600 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.877 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.877 2017-08-04 12:28:57.877 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Legacy_Cardinality_Estimation' Core .Net SqlClient Data Provider sa 0 8 0 0 2064905136 66 2017-08-04 12:28:57.880 2017-08-04 12:28:57.880 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00EA00000082001800E7206E0076006100720063006800610072002800390038002900C4000000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A007300790073002E00640061007400610062006100730065005F00730063006F007000650064005F0063006F006E00660069006700750072006100740069006F006E0073002000410053002000730070000A00570048004500520045000A002800730070002E006E0061006D0065003D0040005F006D0073007000 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.880 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.880 2017-08-04 12:28:57.880 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name], +sp.configuration_id AS [Id], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value,''BaseType'') = ''bit'' THEN + CASE value + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value, ''NULL'') + END + AS nvarchar(200)) AS [Value], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value_for_secondary,''BaseType'') = ''bit'' THEN + CASE value_for_secondary + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value_for_secondary, ''PRIMARY'') + END + AS nvarchar(200)) AS [ValueForSecondary] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Legacy_Cardinality_Estimation' Core .Net SqlClient Data Provider sa 0 10 0 0 2064905136 66 2017-08-04 12:28:57.880 2017-08-04 12:28:57.880 0X00000000040000001A00730070005F006500780065006300750074006500730071006C009A05000082001A00E7206E0076006100720063006800610072002800360039003700290072050000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D002C000A00730070002E0063006F006E00660069006700750072006100740069006F006E005F006900640020004100530020005B00490064005D002C000A00430041005300540028000A0020002000200020002000200043004100530045000A00200020002000200020002000200020005700480045004E002000530051004C005F005600 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.883 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.883 2017-08-04 12:28:57.883 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Parameter_Sniffing' Core .Net SqlClient Data Provider sa 0 8 0 0 2064905136 66 2017-08-04 12:28:57.887 2017-08-04 12:28:57.887 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00EA00000082001800E7206E0076006100720063006800610072002800390038002900C4000000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A007300790073002E00640061007400610062006100730065005F00730063006F007000650064005F0063006F006E00660069006700750072006100740069006F006E0073002000410053002000730070000A00570048004500520045000A002800730070002E006E0061006D0065003D0040005F006D0073007000 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.890 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.890 2017-08-04 12:28:57.890 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name], +sp.configuration_id AS [Id], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value,''BaseType'') = ''bit'' THEN + CASE value + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value, ''NULL'') + END + AS nvarchar(200)) AS [Value], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value_for_secondary,''BaseType'') = ''bit'' THEN + CASE value_for_secondary + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value_for_secondary, ''PRIMARY'') + END + AS nvarchar(200)) AS [ValueForSecondary] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Parameter_Sniffing' Core .Net SqlClient Data Provider sa 0 10 0 0 2064905136 66 2017-08-04 12:28:57.890 2017-08-04 12:28:57.890 0X00000000040000001A00730070005F006500780065006300750074006500730071006C009A05000082001A00E7206E0076006100720063006800610072002800360039003700290072050000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D002C000A00730070002E0063006F006E00660069006700750072006100740069006F006E005F006900640020004100530020005B00490064005D002C000A00430041005300540028000A0020002000200020002000200043004100530045000A00200020002000200020002000200020005700480045004E002000530051004C005F005600 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.893 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.893 2017-08-04 12:28:57.893 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Query_Optimizer_Hotfixes' Core .Net SqlClient Data Provider sa 0 8 0 0 2064905136 66 2017-08-04 12:28:57.893 2017-08-04 12:28:57.893 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00EA00000082001800E7206E0076006100720063006800610072002800390038002900C4000000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A007300790073002E00640061007400610062006100730065005F00730063006F007000650064005F0063006F006E00660069006700750072006100740069006F006E0073002000410053002000730070000A00570048004500520045000A002800730070002E006E0061006D0065003D0040005F006D0073007000 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.897 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.897 2017-08-04 12:28:57.897 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name], +sp.configuration_id AS [Id], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value,''BaseType'') = ''bit'' THEN + CASE value + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value, ''NULL'') + END + AS nvarchar(200)) AS [Value], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value_for_secondary,''BaseType'') = ''bit'' THEN + CASE value_for_secondary + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value_for_secondary, ''PRIMARY'') + END + AS nvarchar(200)) AS [ValueForSecondary] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Query_Optimizer_Hotfixes' Core .Net SqlClient Data Provider sa 0 10 0 0 2064905136 66 2017-08-04 12:28:57.897 2017-08-04 12:28:57.897 0X00000000040000001A00730070005F006500780065006300750074006500730071006C009A05000082001A00E7206E0076006100720063006800610072002800360039003700290072050000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D002C000A00730070002E0063006F006E00660069006700750072006100740069006F006E005F006900640020004100530020005B00490064005D002C000A00430041005300540028000A0020002000200020002000200043004100530045000A00200020002000200020002000200020005700480045004E002000530051004C005F005600 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.903 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.903 2017-08-04 12:28:57.903 +SQL:BatchStarting + DECLARE @is_policy_automation_enabled bit + SET @is_policy_automation_enabled = (SELECT CONVERT(bit, current_value) + FROM msdb.dbo.syspolicy_configuration + WHERE name = 'Enabled') + + + +SELECT +CAST(cast(g.name as varbinary(256)) AS sysname) AS [Name], +g.data_space_id AS [ID], +CAST(g.is_default AS bit) AS [IsDefault], +g.is_read_only AS [ReadOnly], +CAST(ISNULL((select sum(cast(gs.size as float))*convert(float,8) from sys.database_files gs where gs.data_space_id = g.data_space_id), 0) AS float) AS [Size], +CASE g.type WHEN 'FG' THEN 0 WHEN 'PS' THEN 1 WHEN 'FD' THEN 2 WHEN 'FX' THEN 3 END AS [FileGroupType], +CAST(CASE WHEN 'FD'=g.type THEN 1 ELSE 0 END AS bit) AS [IsFileStream], +case when 1=@is_policy_automation_enabled and exists (select * from msdb.dbo.syspolicy_system_health_state where target_query_expression_with_id like 'Server' + '/Database\[@ID=' + convert(nvarchar(20),dtb.database_id) + '\]'+ '/FileGroup\[@ID=' + convert(nvarchar(20),g.data_space_id) + '\]%' ESCAPE '\') then 1 else 0 end AS [PolicyHealthState] +FROM +master.sys.databases AS dtb, +sys.filegroups AS g +WHERE +(dtb.name=db_name()) +ORDER BY +[Name] ASC Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.907 +SQL:BatchCompleted + DECLARE @is_policy_automation_enabled bit + SET @is_policy_automation_enabled = (SELECT CONVERT(bit, current_value) + FROM msdb.dbo.syspolicy_configuration + WHERE name = 'Enabled') + + + +SELECT +CAST(cast(g.name as varbinary(256)) AS sysname) AS [Name], +g.data_space_id AS [ID], +CAST(g.is_default AS bit) AS [IsDefault], +g.is_read_only AS [ReadOnly], +CAST(ISNULL((select sum(cast(gs.size as float))*convert(float,8) from sys.database_files gs where gs.data_space_id = g.data_space_id), 0) AS float) AS [Size], +CASE g.type WHEN 'FG' THEN 0 WHEN 'PS' THEN 1 WHEN 'FD' THEN 2 WHEN 'FX' THEN 3 END AS [FileGroupType], +CAST(CASE WHEN 'FD'=g.type THEN 1 ELSE 0 END AS bit) AS [IsFileStream], +case when 1=@is_policy_automation_enabled and exists (select * from msdb.dbo.syspolicy_system_health_state where target_query_expression_with_id like 'Server' + '/Database\[@ID=' + convert(nvarchar(20),dtb.database_id) + '\]'+ '/FileGroup\[@ID=' + convert(nvarchar(20),g.data_space_id) + '\]%' ESCAPE '\') then 1 else 0 end AS [PolicyHealthState] +FROM +master.sys.databases AS dtb, +sys.filegroups AS g +WHERE +(dtb.name=db_name()) +ORDER BY +[Name] ASC Core .Net SqlClient Data Provider sa 0 14 0 0 2064905136 66 2017-08-04 12:28:57.907 2017-08-04 12:28:57.907 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.913 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.913 2017-08-04 12:28:57.913 +RPC:Completed exec sp_executesql N' with s as ( +select df.[type] as [type], +df.name collate database_default as name, +df.drop_lsn as drop_lsn, +dtb.database_id as database_id, +df.[file_id] as [file_id], +df.max_size as max_size, +df.growth as growth, +df.is_percent_growth as is_percent_growth, +df.is_media_read_only as is_media_read_only, +df.is_read_only as is_read_only, +df.[state] as [state], +df.is_sparse as is_sparse, +df.data_space_id as data_space_id, +df.physical_name collate database_default as physical_name, +df.size as size + from sys.database_files as df inner join sys.databases as dtb + on (db_id() = dtb.database_id) + and (df.type = 2 or df.type = 0) + and (df.drop_lsn is null) + where + (dtb.source_database_id is null) +union +select +mf.[type] as [type], +mf.name collate database_default as name, +mf.drop_lsn as drop_lsn, +mf.database_id as database_id, +mf.[file_id] as [file_id], +mf.max_size as max_size, +mf.growth as growth, +mf.is_percent_growth as is_percent_growth, +mf.is_media_read_only as is_media_read_only, +mf.is_read_only as is_read_only, +mf.[state] as [state], +mf.is_sparse as is_sparse, +mf.data_space_id as data_space_id, +mf.physical_name collate database_default as physical_name, +mf.size as size + from sys.master_files as mf inner join sys.databases as db + on (mf.database_id = db.database_id) + and (mf.type = 2 or mf.type = 0) + and (mf.drop_lsn is null) + and (db.source_database_id is not null) + where mf.database_id = db_id()) + + + +SELECT +s.name AS [Name], +CAST(CASE s.file_id WHEN 1 THEN 1 ELSE 0 END AS bit) AS [IsPrimaryFile], +CAST(CASE when s.growth=0 THEN (CASE WHEN s.type = 2 THEN 0 ELSE 99 END) ELSE s.is_percent_growth END AS int) AS [GrowthType], +s.physical_name AS [FileName], +s.size * CONVERT(float,8) AS [Size], +CASE when s.max_size=-1 then -1 else s.max_size * CONVERT(float,8) END AS [MaxSize], +s.file_id AS [ID], +''Server[@Name='' + quotename(CAST( + serverproperty(N''Servername'') + AS sysname),'''''''') + '']'' + ''/Database[@Name='' + quotename(db_name(),'''''''') + '']'' + ''/FileGroup[@Name='' + quotename(CAST(cast(g.name as varbinary(256)) AS sysname),'''''''') + '']'' + ''/File[@Name='' + quotename(s.name,'''''''') + '']'' AS [Urn], +CAST(CASE s.is_percent_growth WHEN 1 THEN s.growth ELSE s.growth*8 END AS float) AS [Growth], +s.is_media_read_only AS [IsReadOnlyMedia], +s.is_read_only AS [IsReadOnly], +CAST(case s.state when 6 then 1 else 0 end AS bit) AS [IsOffline], +s.is_sparse AS [IsSparse] +FROM +sys.filegroups AS g +INNER JOIN s ON s.data_space_id=g.data_space_id +WHERE +(CAST(cast(g.name as varbinary(256)) AS sysname)=@_msparam_0) +ORDER BY +[Name] ASC',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'PRIMARY' Core .Net SqlClient Data Provider sa 0 12 0 0 2064905136 66 2017-08-04 12:28:57.917 2017-08-04 12:28:57.917 0X00000000040000001A00730070005F006500780065006300750074006500730071006C001615000082001C00E7206E007600610072006300680061007200280032003600370038002900EC14000020002000770069007400680020007300200061007300200028000A00730065006C006500630074002000640066002E005B0074007900700065005D0020006100730020005B0074007900700065005D002C000A00640066002E006E0061006D006500200063006F006C006C006100740065002000640061007400610062006100730065005F00640065006600610075006C00740020006100730020006E0061006D0065002C000A00640066002E0064007200 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.943 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 2064905136 66 2017-08-04 12:28:57.943 2017-08-04 12:28:57.943 +SQL:BatchStarting SELECT +s.name AS [Name], +CAST(FILEPROPERTY(s.name, 'SpaceUsed') AS float)* CONVERT(float,8) AS [UsedSpace], +CAST(CASE when s.growth=0 THEN (CASE WHEN s.type = 2 THEN 0 ELSE 99 END) ELSE s.is_percent_growth END AS int) AS [GrowthType], +s.physical_name AS [FileName], +s.size * CONVERT(float,8) AS [Size], +CASE when s.max_size=-1 then -1 else s.max_size * CONVERT(float,8) END AS [MaxSize], +s.file_id AS [ID], +'Server[@Name=' + quotename(CAST( + serverproperty(N'Servername') + AS sysname),'''') + ']' + '/Database[@Name=' + quotename(db_name(),'''') + ']' + '/LogFile[@Name=' + quotename(s.name,'''') + ']' AS [Urn], +CAST(CASE s.is_percent_growth WHEN 1 THEN s.growth ELSE s.growth*8 END AS float) AS [Growth], +s.is_media_read_only AS [IsReadOnlyMedia], +s.is_read_only AS [IsReadOnly], +CAST(case s.state when 6 then 1 else 0 end AS bit) AS [IsOffline], +s.is_sparse AS [IsSparse] +FROM +sys.database_files AS s +WHERE +(s.type = 1) +ORDER BY +[Name] ASC Core .Net SqlClient Data Provider sa 2064905136 66 2017-08-04 12:28:57.943 +SQL:BatchCompleted SELECT +s.name AS [Name], +CAST(FILEPROPERTY(s.name, 'SpaceUsed') AS float)* CONVERT(float,8) AS [UsedSpace], +CAST(CASE when s.growth=0 THEN (CASE WHEN s.type = 2 THEN 0 ELSE 99 END) ELSE s.is_percent_growth END AS int) AS [GrowthType], +s.physical_name AS [FileName], +s.size * CONVERT(float,8) AS [Size], +CASE when s.max_size=-1 then -1 else s.max_size * CONVERT(float,8) END AS [MaxSize], +s.file_id AS [ID], +'Server[@Name=' + quotename(CAST( + serverproperty(N'Servername') + AS sysname),'''') + ']' + '/Database[@Name=' + quotename(db_name(),'''') + ']' + '/LogFile[@Name=' + quotename(s.name,'''') + ']' AS [Urn], +CAST(CASE s.is_percent_growth WHEN 1 THEN s.growth ELSE s.growth*8 END AS float) AS [Growth], +s.is_media_read_only AS [IsReadOnlyMedia], +s.is_read_only AS [IsReadOnly], +CAST(case s.state when 6 then 1 else 0 end AS bit) AS [IsOffline], +s.is_sparse AS [IsSparse] +FROM +sys.database_files AS s +WHERE +(s.type = 1) +ORDER BY +[Name] ASC Core .Net SqlClient Data Provider sa 0 6 0 0 2064905136 66 2017-08-04 12:28:57.943 2017-08-04 12:28:57.943 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 2064905136 67 2017-08-04 12:28:57.980 +SQL:BatchStarting SELECT s.name AS schema_name, o.[name] AS object_name, o.[type] AS object_type + FROM sys.all_objects o + INNER JOIN sys.schemas s ON o.schema_id = s.schema_id + WHERE (o.[type] = 'P' OR o.[type] = 'V' OR o.[type] = 'U') ORDER BY object_type, schema_name, object_name carbon sa 2064905136 67 2017-08-04 12:28:57.980 +SQL:BatchCompleted SELECT s.name AS schema_name, o.[name] AS object_name, o.[type] AS object_type + FROM sys.all_objects o + INNER JOIN sys.schemas s ON o.schema_id = s.schema_id + WHERE (o.[type] = 'P' OR o.[type] = 'V' OR o.[type] = 'U') ORDER BY object_type, schema_name, object_name carbon sa 31 100 0 42 2064905136 67 2017-08-04 12:28:57.980 2017-08-04 12:28:58.023 +Audit Logout carbon sa 31 100 0 438790 2064905136 67 2017-08-04 12:28:57.980 2017-08-04 12:36:16.770 +Audit Logout carbon sa 16 187 0 495447 2064905136 63 2017-08-04 12:28:56.763 2017-08-04 12:37:12.210 +Audit Logout carbon sa 0 5 0 495374 2064905136 65 2017-08-04 12:28:56.837 2017-08-04 12:37:12.210 +Audit Logout carbon sa 0 15 0 495664 2064905136 62 2017-08-04 12:28:56.547 2017-08-04 12:37:12.210 +Audit Logout Core .Net SqlClient Data Provider sa 47 1113 2 494910 2064905136 66 2017-08-04 12:28:57.300 2017-08-04 12:37:12.210 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 1881496663 51 2017-08-04 14:13:03.783 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1881496663 51 2017-08-04 14:13:03.933 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1881496663 51 2017-08-04 14:13:03.933 2017-08-04 14:13:03.933 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 1881496663 51 2017-08-04 14:13:04.000 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 1881496663 51 2017-08-04 14:13:04.000 2017-08-04 14:13:04.000 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1881496663 51 2017-08-04 14:13:04.003 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1881496663 51 2017-08-04 14:13:04.003 2017-08-04 14:13:04.003 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 1881496663 51 2017-08-04 14:13:04.007 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 1881496663 51 2017-08-04 14:13:04.007 2017-08-04 14:13:04.007 +SQL:BatchStarting SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 1881496663 51 2017-08-04 14:13:04.013 +SQL:BatchCompleted SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 0 0 0 0 1881496663 51 2017-08-04 14:13:04.013 2017-08-04 14:13:04.013 +SQL:BatchStarting set LOCK_TIMEOUT 5000 carbon sa 1881496663 51 2017-08-04 14:13:04.023 +SQL:BatchCompleted set LOCK_TIMEOUT 5000 carbon sa 0 0 0 0 1881496663 51 2017-08-04 14:13:04.023 2017-08-04 14:13:04.023 +SQL:BatchStarting SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 1881496663 51 2017-08-04 14:13:04.030 +SQL:BatchCompleted SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 0 0 0 0 1881496663 51 2017-08-04 14:13:04.030 2017-08-04 14:13:04.030 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1881496663 51 2017-08-04 14:13:04.040 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1881496663 51 2017-08-04 14:13:04.040 2017-08-04 14:13:04.040 +SQL:BatchStarting SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 1881496663 51 2017-08-04 14:13:04.043 +SQL:BatchCompleted SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 0 0 0 0 1881496663 51 2017-08-04 14:13:04.043 2017-08-04 14:13:04.043 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 1881496663 56 2017-08-04 14:13:04.133 +SQL:BatchStarting DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + carbon sa 1881496663 56 2017-08-04 14:13:04.513 +SQL:BatchCompleted DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + carbon sa 0 0 0 0 1881496663 56 2017-08-04 14:13:04.513 2017-08-04 14:13:04.513 +SQL:BatchStarting select SERVERPROPERTY(N'servername') carbon sa 1881496663 56 2017-08-04 14:13:04.657 +SQL:BatchCompleted select SERVERPROPERTY(N'servername') carbon sa 0 0 0 0 1881496663 56 2017-08-04 14:13:04.657 2017-08-04 14:13:04.657 +SQL:BatchStarting use [master]; +if (db_id() = 1) +begin +-- contained auth is 0 when connected to master +select 0 +end +else +begin +-- need dynamic sql so that we compile this query only when we know resource db is available +exec('select case when authenticating_database_id = 1 then 0 else 1 end from +sys.dm_exec_sessions where session_id = @@SPID') +end;use [master]; --resetting the context carbon sa 1881496663 56 2017-08-04 14:13:04.683 +SQL:BatchCompleted use [master]; +if (db_id() = 1) +begin +-- contained auth is 0 when connected to master +select 0 +end +else +begin +-- need dynamic sql so that we compile this query only when we know resource db is available +exec('select case when authenticating_database_id = 1 then 0 else 1 end from +sys.dm_exec_sessions where session_id = @@SPID') +end;use [master]; --resetting the context carbon sa 0 0 0 0 1881496663 56 2017-08-04 14:13:04.683 2017-08-04 14:13:04.683 +SQL:BatchStarting SELECT +CAST( + serverproperty(N'Servername') + AS sysname) AS [Name] carbon sa 1881496663 56 2017-08-04 14:13:04.873 +SQL:BatchCompleted SELECT +CAST( + serverproperty(N'Servername') + AS sysname) AS [Name] carbon sa 0 0 0 0 1881496663 56 2017-08-04 14:13:04.873 2017-08-04 14:13:04.873 +SQL:BatchStarting + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SmoRoot OUTPUT + + + +SELECT +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +N'Windows' AS [HostPlatform], +N'\' AS [PathSeparator], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled] carbon sa 1881496663 56 2017-08-04 14:13:04.937 +SQL:BatchCompleted + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SmoRoot OUTPUT + + + +SELECT +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +N'Windows' AS [HostPlatform], +N'\' AS [PathSeparator], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled] carbon sa 0 5 0 2 1881496663 56 2017-08-04 14:13:04.937 2017-08-04 14:13:04.940 +RPC:Completed exec sp_executesql N'SELECT +dtb.collation_name AS [Collation], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' carbon sa 0 8 0 0 1881496663 56 2017-08-04 14:13:05.107 2017-08-04 14:13:05.107 0X00000000040000001A00730070005F006500780065006300750074006500730071006C002E01000082001A00E7206E0076006100720063006800610072002800310033003100290006010000530045004C004500430054000A006400740062002E0063006F006C006C006100740069006F006E005F006E0061006D00650020004100530020005B0043006F006C006C006100740069006F006E005D002C000A006400740062002E006E0061006D00650020004100530020005B00440061007400610062006100730065004E0061006D00650032005D000A00460052004F004D000A006D00610073007400650072002E007300790073002E006400610074006100 +SQL:BatchStarting SELECT +dtb.name AS [Name], +dtb.database_id AS [ID], +CAST(case when dtb.name in ('master','model','msdb','tempdb') then 1 else dtb.is_distributor end AS bit) AS [IsSystemObject], +dtb.collation_name AS [Collation], +CAST(has_dbaccess(dtb.name) AS bit) AS [IsAccessible], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +ORDER BY +[Name] ASC carbon sa 1881496663 56 2017-08-04 14:13:05.150 +SQL:BatchCompleted SELECT +dtb.name AS [Name], +dtb.database_id AS [ID], +CAST(case when dtb.name in ('master','model','msdb','tempdb') then 1 else dtb.is_distributor end AS bit) AS [IsSystemObject], +dtb.collation_name AS [Collation], +CAST(has_dbaccess(dtb.name) AS bit) AS [IsAccessible], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +ORDER BY +[Name] ASC carbon sa 16 174 0 13 1881496663 56 2017-08-04 14:13:05.150 2017-08-04 14:13:05.163 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 1881496663 57 2017-08-04 14:13:07.510 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1881496663 57 2017-08-04 14:13:07.513 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1881496663 57 2017-08-04 14:13:07.513 2017-08-04 14:13:07.513 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 1881496663 57 2017-08-04 14:13:07.520 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 1881496663 57 2017-08-04 14:13:07.520 2017-08-04 14:13:07.520 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1881496663 57 2017-08-04 14:13:07.523 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1881496663 57 2017-08-04 14:13:07.523 2017-08-04 14:13:07.523 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 1881496663 57 2017-08-04 14:13:07.527 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 1881496663 57 2017-08-04 14:13:07.527 2017-08-04 14:13:07.527 +SQL:BatchStarting SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 1881496663 57 2017-08-04 14:13:07.530 +SQL:BatchCompleted SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 0 0 0 0 1881496663 57 2017-08-04 14:13:07.530 2017-08-04 14:13:07.530 +SQL:BatchStarting set LOCK_TIMEOUT 5000 carbon sa 1881496663 57 2017-08-04 14:13:07.530 +SQL:BatchCompleted set LOCK_TIMEOUT 5000 carbon sa 0 0 0 0 1881496663 57 2017-08-04 14:13:07.530 2017-08-04 14:13:07.530 +SQL:BatchStarting SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 1881496663 57 2017-08-04 14:13:07.533 +SQL:BatchCompleted SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 0 0 0 0 1881496663 57 2017-08-04 14:13:07.533 2017-08-04 14:13:07.533 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1881496663 57 2017-08-04 14:13:07.537 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1881496663 57 2017-08-04 14:13:07.537 2017-08-04 14:13:07.537 +SQL:BatchStarting SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 1881496663 57 2017-08-04 14:13:07.543 +SQL:BatchCompleted SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 0 0 0 0 1881496663 57 2017-08-04 14:13:07.543 2017-08-04 14:13:07.543 +SQL:BatchStarting DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + carbon sa 1881496663 57 2017-08-04 14:13:09.263 +SQL:BatchCompleted DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + carbon sa 0 0 0 0 1881496663 57 2017-08-04 14:13:09.263 2017-08-04 14:13:09.263 +SQL:BatchStarting use [master]; +if (db_id() = 1) +begin +-- contained auth is 0 when connected to master +select 0 +end +else +begin +-- need dynamic sql so that we compile this query only when we know resource db is available +exec('select case when authenticating_database_id = 1 then 0 else 1 end from +sys.dm_exec_sessions where session_id = @@SPID') +end;use [master]; --resetting the context carbon sa 1881496663 57 2017-08-04 14:13:09.270 +SQL:BatchCompleted use [master]; +if (db_id() = 1) +begin +-- contained auth is 0 when connected to master +select 0 +end +else +begin +-- need dynamic sql so that we compile this query only when we know resource db is available +exec('select case when authenticating_database_id = 1 then 0 else 1 end from +sys.dm_exec_sessions where session_id = @@SPID') +end;use [master]; --resetting the context carbon sa 0 0 0 0 1881496663 57 2017-08-04 14:13:09.270 2017-08-04 14:13:09.270 +RPC:Completed exec sp_executesql N'SELECT +dtb.collation_name AS [Collation], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' carbon sa 0 8 0 0 1881496663 57 2017-08-04 14:13:09.273 2017-08-04 14:13:09.273 0X00000000040000001A00730070005F006500780065006300750074006500730071006C002E01000082001A00E7206E0076006100720063006800610072002800310033003100290006010000530045004C004500430054000A006400740062002E0063006F006C006C006100740069006F006E005F006E0061006D00650020004100530020005B0043006F006C006C006100740069006F006E005D002C000A006400740062002E006E0061006D00650020004100530020005B00440061007400610062006100730065004E0061006D00650032005D000A00460052004F004D000A006D00610073007400650072002E007300790073002E006400610074006100 +SQL:BatchStarting select SERVERPROPERTY(N'servername') carbon sa 1881496663 57 2017-08-04 14:13:09.277 +SQL:BatchCompleted select SERVERPROPERTY(N'servername') carbon sa 0 0 0 0 1881496663 57 2017-08-04 14:13:09.277 2017-08-04 14:13:09.277 +RPC:Completed exec sp_executesql N'SELECT +dtb.name AS [Name] +FROM +master.sys.databases AS dtb +WHERE +((CAST(case when dtb.name in (''master'',''model'',''msdb'',''tempdb'') then 1 else dtb.is_distributor end AS bit)=@_msparam_0)) +ORDER BY +[Name] ASC',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'0' carbon sa 16 486 0 8 1881496663 57 2017-08-04 14:13:09.283 2017-08-04 14:13:09.290 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00C201000082001A00E7206E007600610072006300680061007200280032003000350029009A010000530045004C004500430054000A006400740062002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A006D00610073007400650072002E007300790073002E0064006100740061006200610073006500730020004100530020006400740062000A00570048004500520045000A002800280043004100530054002800630061007300650020007700680065006E0020006400740062002E006E0061006D006500 +Audit Logout carbon sa 16 509 0 892300 1881496663 57 2017-08-04 14:13:07.510 2017-08-04 14:27:59.810 +Audit Logout carbon sa 0 15 0 896027 1881496663 51 2017-08-04 14:13:03.783 2017-08-04 14:27:59.810 +Audit Logout carbon sa 16 187 0 895677 1881496663 56 2017-08-04 14:13:04.133 2017-08-04 14:27:59.810 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 1239302740 56 2017-08-04 14:30:06.520 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1239302740 56 2017-08-04 14:30:06.617 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1239302740 56 2017-08-04 14:30:06.617 2017-08-04 14:30:06.617 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 1239302740 56 2017-08-04 14:30:06.670 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 1239302740 56 2017-08-04 14:30:06.670 2017-08-04 14:30:06.670 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1239302740 56 2017-08-04 14:30:06.673 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1239302740 56 2017-08-04 14:30:06.673 2017-08-04 14:30:06.673 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 1239302740 56 2017-08-04 14:30:06.677 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 1239302740 56 2017-08-04 14:30:06.677 2017-08-04 14:30:06.677 +SQL:BatchStarting SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 1239302740 56 2017-08-04 14:30:06.680 +SQL:BatchCompleted SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 0 0 0 0 1239302740 56 2017-08-04 14:30:06.680 2017-08-04 14:30:06.680 +SQL:BatchStarting set LOCK_TIMEOUT 5000 carbon sa 1239302740 56 2017-08-04 14:30:06.693 +SQL:BatchCompleted set LOCK_TIMEOUT 5000 carbon sa 0 0 0 0 1239302740 56 2017-08-04 14:30:06.693 2017-08-04 14:30:06.693 +SQL:BatchStarting SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 1239302740 56 2017-08-04 14:30:06.700 +SQL:BatchCompleted SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 0 0 0 0 1239302740 56 2017-08-04 14:30:06.700 2017-08-04 14:30:06.700 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1239302740 56 2017-08-04 14:30:06.710 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1239302740 56 2017-08-04 14:30:06.710 2017-08-04 14:30:06.710 +SQL:BatchStarting SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 1239302740 56 2017-08-04 14:30:06.713 +SQL:BatchCompleted SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 16 0 0 0 1239302740 56 2017-08-04 14:30:06.713 2017-08-04 14:30:06.713 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 1239302740 57 2017-08-04 14:30:06.780 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 1239302740 58 2017-08-04 14:30:06.823 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1239302740 58 2017-08-04 14:30:06.823 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1239302740 58 2017-08-04 14:30:06.823 2017-08-04 14:30:06.823 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 1239302740 58 2017-08-04 14:30:06.827 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 1239302740 58 2017-08-04 14:30:06.827 2017-08-04 14:30:06.827 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1239302740 58 2017-08-04 14:30:06.830 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1239302740 58 2017-08-04 14:30:06.830 2017-08-04 14:30:06.830 +SQL:BatchStarting SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 1239302740 58 2017-08-04 14:30:06.833 +SQL:BatchCompleted SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1) carbon sa 0 0 0 0 1239302740 58 2017-08-04 14:30:06.833 2017-08-04 14:30:06.833 +SQL:BatchStarting SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 1239302740 58 2017-08-04 14:30:06.837 +SQL:BatchCompleted SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO()) carbon sa 0 0 0 0 1239302740 58 2017-08-04 14:30:06.837 2017-08-04 14:30:06.837 +SQL:BatchStarting set LOCK_TIMEOUT 5000 carbon sa 1239302740 58 2017-08-04 14:30:06.840 +SQL:BatchCompleted set LOCK_TIMEOUT 5000 carbon sa 0 0 0 0 1239302740 58 2017-08-04 14:30:06.840 2017-08-04 14:30:06.840 +SQL:BatchStarting SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 1239302740 58 2017-08-04 14:30:06.843 +SQL:BatchCompleted SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +SET NUMERIC_ROUNDABORT OFF; carbon sa 0 0 0 0 1239302740 58 2017-08-04 14:30:06.843 2017-08-04 14:30:06.843 +SQL:BatchStarting SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 1239302740 58 2017-08-04 14:30:06.847 +SQL:BatchCompleted SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT) carbon sa 0 5 0 0 1239302740 58 2017-08-04 14:30:06.847 2017-08-04 14:30:06.847 +SQL:BatchStarting SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 1239302740 58 2017-08-04 14:30:06.847 +SQL:BatchCompleted SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' ON ', @@version)) carbon sa 0 0 0 0 1239302740 58 2017-08-04 14:30:06.847 2017-08-04 14:30:06.847 +SQL:BatchStarting DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + carbon sa 1239302740 57 2017-08-04 14:30:07.210 +SQL:BatchCompleted DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + carbon sa 0 0 0 0 1239302740 57 2017-08-04 14:30:07.210 2017-08-04 14:30:07.210 +SQL:BatchStarting select SERVERPROPERTY(N'servername') carbon sa 1239302740 57 2017-08-04 14:30:07.357 +SQL:BatchCompleted select SERVERPROPERTY(N'servername') carbon sa 0 0 0 0 1239302740 57 2017-08-04 14:30:07.357 2017-08-04 14:30:07.357 +SQL:BatchStarting use [master]; +if (db_id() = 1) +begin +-- contained auth is 0 when connected to master +select 0 +end +else +begin +-- need dynamic sql so that we compile this query only when we know resource db is available +exec('select case when authenticating_database_id = 1 then 0 else 1 end from +sys.dm_exec_sessions where session_id = @@SPID') +end;use [master]; --resetting the context carbon sa 1239302740 57 2017-08-04 14:30:07.380 +SQL:BatchCompleted use [master]; +if (db_id() = 1) +begin +-- contained auth is 0 when connected to master +select 0 +end +else +begin +-- need dynamic sql so that we compile this query only when we know resource db is available +exec('select case when authenticating_database_id = 1 then 0 else 1 end from +sys.dm_exec_sessions where session_id = @@SPID') +end;use [master]; --resetting the context carbon sa 0 0 0 0 1239302740 57 2017-08-04 14:30:07.380 2017-08-04 14:30:07.380 +SQL:BatchStarting SELECT +CAST( + serverproperty(N'Servername') + AS sysname) AS [Name] carbon sa 1239302740 57 2017-08-04 14:30:07.560 +SQL:BatchCompleted SELECT +CAST( + serverproperty(N'Servername') + AS sysname) AS [Name] carbon sa 0 0 0 0 1239302740 57 2017-08-04 14:30:07.560 2017-08-04 14:30:07.560 +SQL:BatchStarting + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SmoRoot OUTPUT + + + +SELECT +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +N'Windows' AS [HostPlatform], +N'\' AS [PathSeparator], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled] carbon sa 1239302740 57 2017-08-04 14:30:07.630 +SQL:BatchCompleted + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SmoRoot OUTPUT + + + +SELECT +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +N'Windows' AS [HostPlatform], +N'\' AS [PathSeparator], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled] carbon sa 0 5 0 2 1239302740 57 2017-08-04 14:30:07.630 2017-08-04 14:30:07.633 +RPC:Completed exec sp_executesql N'SELECT +dtb.collation_name AS [Collation], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' carbon sa 0 8 0 0 1239302740 57 2017-08-04 14:30:07.797 2017-08-04 14:30:07.797 0X00000000040000001A00730070005F006500780065006300750074006500730071006C002E01000082001A00E7206E0076006100720063006800610072002800310033003100290006010000530045004C004500430054000A006400740062002E0063006F006C006C006100740069006F006E005F006E0061006D00650020004100530020005B0043006F006C006C006100740069006F006E005D002C000A006400740062002E006E0061006D00650020004100530020005B00440061007400610062006100730065004E0061006D00650032005D000A00460052004F004D000A006D00610073007400650072002E007300790073002E006400610074006100 +SQL:BatchStarting SELECT +dtb.name AS [Name], +dtb.database_id AS [ID], +CAST(case when dtb.name in ('master','model','msdb','tempdb') then 1 else dtb.is_distributor end AS bit) AS [IsSystemObject], +dtb.collation_name AS [Collation], +CAST(has_dbaccess(dtb.name) AS bit) AS [IsAccessible], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +ORDER BY +[Name] ASC carbon sa 1239302740 57 2017-08-04 14:30:07.830 +SQL:BatchCompleted SELECT +dtb.name AS [Name], +dtb.database_id AS [ID], +CAST(case when dtb.name in ('master','model','msdb','tempdb') then 1 else dtb.is_distributor end AS bit) AS [IsSystemObject], +dtb.collation_name AS [Collation], +CAST(has_dbaccess(dtb.name) AS bit) AS [IsAccessible], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +ORDER BY +[Name] ASC carbon sa 15 174 0 13 1239302740 57 2017-08-04 14:30:07.830 2017-08-04 14:30:07.843 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.543 +SQL:BatchStarting DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.547 +SQL:BatchCompleted DECLARE @edition sysname; SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname); select case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType'; +SELECT SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition +select N'Windows' as host_platform + Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.547 2017-08-04 14:30:11.547 +SQL:BatchStarting select SERVERPROPERTY(N'servername') Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.560 +SQL:BatchCompleted select SERVERPROPERTY(N'servername') Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.560 2017-08-04 14:30:11.560 +SQL:BatchStarting + declare @HkeyLocal nvarchar(18) + declare @ServicesRegPath nvarchar(34) + declare @SqlServiceRegPath sysname + declare @BrowserServiceRegPath sysname + declare @MSSqlServerRegPath nvarchar(31) + declare @InstanceNamesRegPath nvarchar(59) + declare @InstanceRegPath sysname + declare @SetupRegPath sysname + declare @NpRegPath sysname + declare @TcpRegPath sysname + declare @RegPathParams sysname + declare @FilestreamRegPath sysname + + select @HkeyLocal=N'HKEY_LOCAL_MACHINE' + + -- Instance-based paths + select @MSSqlServerRegPath=N'SOFTWARE\Microsoft\MSSQLServer' + select @InstanceRegPath=@MSSqlServerRegPath + N'\MSSQLServer' + select @FilestreamRegPath=@InstanceRegPath + N'\Filestream' + select @SetupRegPath=@MSSqlServerRegPath + N'\Setup' + select @RegPathParams=@InstanceRegPath+'\Parameters' + + -- Services + select @ServicesRegPath=N'SYSTEM\CurrentControlSet\Services' + select @SqlServiceRegPath=@ServicesRegPath + N'\MSSQLSERVER' + select @BrowserServiceRegPath=@ServicesRegPath + N'\SQLBrowser' + + -- InstanceId setting + select @InstanceNamesRegPath=N'SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' + + -- Network settings + select @NpRegPath=@InstanceRegPath + N'\SuperSocketNetLib\Np' + select @TcpRegPath=@InstanceRegPath + N'\SuperSocketNetLib\Tcp' + + + + declare @SmoAuditLevel int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'AuditLevel', @SmoAuditLevel OUTPUT + + + + declare @NumErrorLogs int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'NumErrorLogs', @NumErrorLogs OUTPUT + + + + declare @SmoLoginMode int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'LoginMode', @SmoLoginMode OUTPUT + + + + declare @SmoMailProfile nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'MailAccountName', @SmoMailProfile OUTPUT + + + + declare @BackupDirectory nvarchar(512) + if 1=isnull(cast(SERVERPROPERTY('IsLocalDB') as bit), 0) + select @BackupDirectory=cast(SERVERPROPERTY('instancedefaultdatapath') as nvarchar(512)) + else + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'BackupDirectory', @BackupDirectory OUTPUT + + + + declare @SmoPerfMonMode int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'Performance', @SmoPerfMonMode OUTPUT + + if @SmoPerfMonMode is null + begin + set @SmoPerfMonMode = 1000 + end + + + + declare @InstallSqlDataDir nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLDataRoot', @InstallSqlDataDir OUTPUT + + + + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLPath', @SmoRoot OUTPUT + + + + declare @ServiceStartMode int + EXEC master.sys.xp_instance_regread @HkeyLocal, @SqlServiceRegPath, N'Start', @ServiceStartMode OUTPUT + + + + declare @ServiceAccount nvarchar(512) + EXEC master.sys.xp_instance_regread @HkeyLocal, @SqlServiceRegPath, N'ObjectName', @ServiceAccount OUTPUT + + + + declare @NamedPipesEnabled int + exec master.dbo.xp_instance_regread @HkeyLocal, @NpRegPath, N'Enabled', @NamedPipesEnabled OUTPUT + + + + declare @TcpEnabled int + EXEC master.sys.xp_instance_regread @HkeyLocal, @TcpRegPath, N'Enabled', @TcpEnabled OUTPUT + + + + declare @InstallSharedDirectory nvarchar(512) + EXEC master.sys.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLPath', @InstallSharedDirectory OUTPUT + + + + declare @SqlGroup nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLGroup', @SqlGroup OUTPUT + + + + declare @FilestreamLevel int + exec master.dbo.xp_instance_regread @HkeyLocal, @FilestreamRegPath, N'EnableLevel', @FilestreamLevel OUTPUT + + + + declare @FilestreamShareName nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @FilestreamRegPath, N'ShareName', @FilestreamShareName OUTPUT + + + + declare @cluster_name nvarchar(128) + declare @quorum_type tinyint + declare @quorum_state tinyint + BEGIN TRY + SELECT @cluster_name = cluster_name, + @quorum_type = quorum_type, + @quorum_state = quorum_state + FROM sys.dm_hadr_cluster + END TRY + BEGIN CATCH + --Querying this DMV using a contained auth connection throws error 15562 (Module is untrusted) + --because of lack of trustworthiness by the server. This is expected so we just leave the + --values as default + IF(ERROR_NUMBER() NOT IN (297,300, 15562)) + BEGIN + THROW + END + END CATCH + + +SELECT +@SmoAuditLevel AS [AuditLevel], +ISNULL(@NumErrorLogs, -1) AS [NumberOfLogFiles], +(case when @SmoLoginMode < 3 then @SmoLoginMode else 9 end) AS [LoginMode], +ISNULL(@SmoMailProfile,N'') AS [MailProfile], +@BackupDirectory AS [BackupDirectory], +@SmoPerfMonMode AS [PerfMonMode], +ISNULL(@InstallSqlDataDir,N'') AS [InstallDataDirectory], +CAST(@@SERVICENAME AS sysname) AS [ServiceName], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY('instancedefaultdatapath') AS [DefaultFile], +SERVERPROPERTY('instancedefaultlogpath') AS [DefaultLog], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +@ServiceStartMode AS [ServiceStartMode], +ISNULL(@ServiceAccount,N'') AS [ServiceAccount], +CAST(@NamedPipesEnabled AS bit) AS [NamedPipesEnabled], +CAST(@TcpEnabled AS bit) AS [TcpEnabled], +ISNULL(@InstallSharedDirectory,N'') AS [InstallSharedDirectory], +ISNULL(suser_sname(sid_binary(ISNULL(@SqlGroup,N''))),N'') AS [SqlDomainGroup], +case when 1=msdb.dbo.fn_syspolicy_is_automation_enabled() and exists (select * from msdb.dbo.syspolicy_system_health_state where target_query_expression_with_id like 'Server%' ) then 1 else 0 end AS [PolicyHealthState], +@FilestreamLevel AS [FilestreamLevel], +ISNULL(@FilestreamShareName,N'') AS [FilestreamShareName], +-1 AS [TapeLoadWaitTime], +CAST(SERVERPROPERTY(N'IsHadrEnabled') AS bit) AS [IsHadrEnabled], +SERVERPROPERTY(N'HADRManagerStatus') AS [HadrManagerStatus], +ISNULL(@cluster_name, '') AS [ClusterName], +ISNULL(@quorum_type, 4) AS [ClusterQuorumType], +ISNULL(@quorum_state, 3) AS [ClusterQuorumState], +SUSER_SID(@ServiceAccount, 0) AS [ServiceAccountSid], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled], +N'Windows' AS [HostPlatform], +CAST( + serverproperty(N'Servername') + AS sysname) AS [Name], +CAST( + ISNULL(serverproperty(N'instancename'),N'') + AS sysname) AS [InstanceName], +CAST(0x0001 AS int) AS [Status], +N'\' AS [PathSeparator], +0 AS [IsContainedAuthentication], +CAST(null AS int) AS [ServerType] + + drop table #SVer + + Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.577 +SQL:BatchCompleted + declare @HkeyLocal nvarchar(18) + declare @ServicesRegPath nvarchar(34) + declare @SqlServiceRegPath sysname + declare @BrowserServiceRegPath sysname + declare @MSSqlServerRegPath nvarchar(31) + declare @InstanceNamesRegPath nvarchar(59) + declare @InstanceRegPath sysname + declare @SetupRegPath sysname + declare @NpRegPath sysname + declare @TcpRegPath sysname + declare @RegPathParams sysname + declare @FilestreamRegPath sysname + + select @HkeyLocal=N'HKEY_LOCAL_MACHINE' + + -- Instance-based paths + select @MSSqlServerRegPath=N'SOFTWARE\Microsoft\MSSQLServer' + select @InstanceRegPath=@MSSqlServerRegPath + N'\MSSQLServer' + select @FilestreamRegPath=@InstanceRegPath + N'\Filestream' + select @SetupRegPath=@MSSqlServerRegPath + N'\Setup' + select @RegPathParams=@InstanceRegPath+'\Parameters' + + -- Services + select @ServicesRegPath=N'SYSTEM\CurrentControlSet\Services' + select @SqlServiceRegPath=@ServicesRegPath + N'\MSSQLSERVER' + select @BrowserServiceRegPath=@ServicesRegPath + N'\SQLBrowser' + + -- InstanceId setting + select @InstanceNamesRegPath=N'SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' + + -- Network settings + select @NpRegPath=@InstanceRegPath + N'\SuperSocketNetLib\Np' + select @TcpRegPath=@InstanceRegPath + N'\SuperSocketNetLib\Tcp' + + + + declare @SmoAuditLevel int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'AuditLevel', @SmoAuditLevel OUTPUT + + + + declare @NumErrorLogs int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'NumErrorLogs', @NumErrorLogs OUTPUT + + + + declare @SmoLoginMode int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'LoginMode', @SmoLoginMode OUTPUT + + + + declare @SmoMailProfile nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'MailAccountName', @SmoMailProfile OUTPUT + + + + declare @BackupDirectory nvarchar(512) + if 1=isnull(cast(SERVERPROPERTY('IsLocalDB') as bit), 0) + select @BackupDirectory=cast(SERVERPROPERTY('instancedefaultdatapath') as nvarchar(512)) + else + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'BackupDirectory', @BackupDirectory OUTPUT + + + + declare @SmoPerfMonMode int + exec master.dbo.xp_instance_regread @HkeyLocal, @InstanceRegPath, N'Performance', @SmoPerfMonMode OUTPUT + + if @SmoPerfMonMode is null + begin + set @SmoPerfMonMode = 1000 + end + + + + declare @InstallSqlDataDir nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLDataRoot', @InstallSqlDataDir OUTPUT + + + + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLPath', @SmoRoot OUTPUT + + + + declare @ServiceStartMode int + EXEC master.sys.xp_instance_regread @HkeyLocal, @SqlServiceRegPath, N'Start', @ServiceStartMode OUTPUT + + + + declare @ServiceAccount nvarchar(512) + EXEC master.sys.xp_instance_regread @HkeyLocal, @SqlServiceRegPath, N'ObjectName', @ServiceAccount OUTPUT + + + + declare @NamedPipesEnabled int + exec master.dbo.xp_instance_regread @HkeyLocal, @NpRegPath, N'Enabled', @NamedPipesEnabled OUTPUT + + + + declare @TcpEnabled int + EXEC master.sys.xp_instance_regread @HkeyLocal, @TcpRegPath, N'Enabled', @TcpEnabled OUTPUT + + + + declare @InstallSharedDirectory nvarchar(512) + EXEC master.sys.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLPath', @InstallSharedDirectory OUTPUT + + + + declare @SqlGroup nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @SetupRegPath, N'SQLGroup', @SqlGroup OUTPUT + + + + declare @FilestreamLevel int + exec master.dbo.xp_instance_regread @HkeyLocal, @FilestreamRegPath, N'EnableLevel', @FilestreamLevel OUTPUT + + + + declare @FilestreamShareName nvarchar(512) + exec master.dbo.xp_instance_regread @HkeyLocal, @FilestreamRegPath, N'ShareName', @FilestreamShareName OUTPUT + + + + declare @cluster_name nvarchar(128) + declare @quorum_type tinyint + declare @quorum_state tinyint + BEGIN TRY + SELECT @cluster_name = cluster_name, + @quorum_type = quorum_type, + @quorum_state = quorum_state + FROM sys.dm_hadr_cluster + END TRY + BEGIN CATCH + --Querying this DMV using a contained auth connection throws error 15562 (Module is untrusted) + --because of lack of trustworthiness by the server. This is expected so we just leave the + --values as default + IF(ERROR_NUMBER() NOT IN (297,300, 15562)) + BEGIN + THROW + END + END CATCH + + +SELECT +@SmoAuditLevel AS [AuditLevel], +ISNULL(@NumErrorLogs, -1) AS [NumberOfLogFiles], +(case when @SmoLoginMode < 3 then @SmoLoginMode else 9 end) AS [LoginMode], +ISNULL(@SmoMailProfile,N'') AS [MailProfile], +@BackupDirectory AS [BackupDirectory], +@SmoPerfMonMode AS [PerfMonMode], +ISNULL(@InstallSqlDataDir,N'') AS [InstallDataDirectory], +CAST(@@SERVICENAME AS sysname) AS [ServiceName], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY('instancedefaultdatapath') AS [DefaultFile], +SERVERPROPERTY('instancedefaultlogpath') AS [DefaultLog], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +@ServiceStartMode AS [ServiceStartMode], +ISNULL(@ServiceAccount,N'') AS [ServiceAccount], +CAST(@NamedPipesEnabled AS bit) AS [NamedPipesEnabled], +CAST(@TcpEnabled AS bit) AS [TcpEnabled], +ISNULL(@InstallSharedDirectory,N'') AS [InstallSharedDirectory], +ISNULL(suser_sname(sid_binary(ISNULL(@SqlGroup,N''))),N'') AS [SqlDomainGroup], +case when 1=msdb.dbo.fn_syspolicy_is_automation_enabled() and exists (select * from msdb.dbo.syspolicy_system_health_state where target_query_expression_with_id like 'Server%' ) then 1 else 0 end AS [PolicyHealthState], +@FilestreamLevel AS [FilestreamLevel], +ISNULL(@FilestreamShareName,N'') AS [FilestreamShareName], +-1 AS [TapeLoadWaitTime], +CAST(SERVERPROPERTY(N'IsHadrEnabled') AS bit) AS [IsHadrEnabled], +SERVERPROPERTY(N'HADRManagerStatus') AS [HadrManagerStatus], +ISNULL(@cluster_name, '') AS [ClusterName], +ISNULL(@quorum_type, 4) AS [ClusterQuorumType], +ISNULL(@quorum_state, 3) AS [ClusterQuorumState], +SUSER_SID(@ServiceAccount, 0) AS [ServiceAccountSid], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled], +N'Windows' AS [HostPlatform], +CAST( + serverproperty(N'Servername') + AS sysname) AS [Name], +CAST( + ISNULL(serverproperty(N'instancename'),N'') + AS sysname) AS [InstanceName], +CAST(0x0001 AS int) AS [Status], +N'\' AS [PathSeparator], +0 AS [IsContainedAuthentication], +CAST(null AS int) AS [ServerType] + + drop table #SVer + + Core .Net SqlClient Data Provider sa 16 9 0 15 1239302740 59 2017-08-04 14:30:11.577 2017-08-04 14:30:11.593 +SQL:BatchStarting SELECT +cfg.name AS [Name], +cfg.configuration_id AS [Number], +cfg.minimum AS [Minimum], +cfg.maximum AS [Maximum], +cfg.is_dynamic AS [Dynamic], +cfg.is_advanced AS [Advanced], +cfg.value AS [ConfigValue], +cfg.value_in_use AS [RunValue], +cfg.description AS [Description] +FROM +sys.configurations AS cfg Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.610 +SQL:BatchCompleted SELECT +cfg.name AS [Name], +cfg.configuration_id AS [Number], +cfg.minimum AS [Minimum], +cfg.maximum AS [Maximum], +cfg.is_dynamic AS [Dynamic], +cfg.is_advanced AS [Advanced], +cfg.value AS [ConfigValue], +cfg.value_in_use AS [RunValue], +cfg.description AS [Description] +FROM +sys.configurations AS cfg Core .Net SqlClient Data Provider sa 0 4 0 0 1239302740 59 2017-08-04 14:30:11.610 2017-08-04 14:30:11.610 +RPC:Completed exec sp_executesql N'SELECT +dtb.name AS [Name] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' Core .Net SqlClient Data Provider sa 0 8 0 0 1239302740 59 2017-08-04 14:30:11.620 2017-08-04 14:30:11.620 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00D400000082001800E7206E0076006100720063006800610072002800380037002900AE000000530045004C004500430054000A006400740062002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A006D00610073007400650072002E007300790073002E0064006100740061006200610073006500730020004100530020006400740062000A00570048004500520045000A0028006400740062002E006E0061006D0065003D0040005F006D00730070006100720061006D005F00300029005A00000082001800 +RPC:Completed exec sp_executesql N'SELECT +dtb.compatibility_level AS [CompatibilityLevel], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' Core .Net SqlClient Data Provider sa 0 10 0 0 1239302740 59 2017-08-04 14:30:11.643 2017-08-04 14:30:11.643 0X00000000040000001A00730070005F006500780065006300750074006500730071006C004A01000082001A00E7206E0076006100720063006800610072002800310034003500290022010000530045004C004500430054000A006400740062002E0063006F006D007000610074006900620069006C006900740079005F006C006500760065006C0020004100530020005B0043006F006D007000610074006900620069006C006900740079004C006500760065006C005D002C000A006400740062002E006E0061006D00650020004100530020005B00440061007400610062006100730065004E0061006D00650032005D000A00460052004F004D000A006D00 +RPC:Completed exec sp_executesql N'SELECT +ISNULL((case dmi.mirroring_redo_queue_type when N''UNLIMITED'' then 0 else dmi.mirroring_redo_queue end),0) AS [MirroringRedoQueueMaxSize], +ISNULL(dmi.mirroring_connection_timeout,0) AS [MirroringTimeout], +ISNULL(dmi.mirroring_partner_name,'''') AS [MirroringPartner], +ISNULL(dmi.mirroring_partner_instance,'''') AS [MirroringPartnerInstance], +ISNULL(dmi.mirroring_role,0) AS [MirroringRole], +ISNULL(dmi.mirroring_safety_level + 1, 0) AS [MirroringSafetyLevel], +ISNULL(dmi.mirroring_state + 1, 0) AS [MirroringStatus], +ISNULL(dmi.mirroring_witness_name,'''') AS [MirroringWitness], +ISNULL(dmi.mirroring_witness_state + 1, 0) AS [MirroringWitnessStatus], +CAST(case when dmi.mirroring_partner_name is null then 0 else 1 end AS bit) AS [IsMirroringEnabled], +ISNULL(dmi.mirroring_guid,''00000000-0000-0000-0000-0000000000000000'') AS [MirroringID], +ISNULL(dmi.mirroring_role_sequence,0) AS [MirroringRoleSequence], +ISNULL(dmi.mirroring_safety_sequence,0) AS [MirroringSafetySequence], +ISNULL(dmi.mirroring_failover_lsn,0) AS [MirroringFailoverLogSequenceNumber], +dtb.is_ansi_null_default_on AS [AnsiNullDefault], +dtb.is_ansi_nulls_on AS [AnsiNullsEnabled], +dtb.is_ansi_padding_on AS [AnsiPaddingEnabled], +dtb.is_ansi_warnings_on AS [AnsiWarningsEnabled], +dtb.is_arithabort_on AS [ArithmeticAbortEnabled], +dtb.is_auto_shrink_on AS [AutoShrink], +dtb.is_cursor_close_on_commit_on AS [CloseCursorsOnCommitEnabled], +dtb.is_concat_null_yields_null_on AS [ConcatenateNullYieldsNull], +dtb.is_numeric_roundabort_on AS [NumericRoundAbortEnabled], +dtb.is_quoted_identifier_on AS [QuotedIdentifiersEnabled], +dtb.is_read_only AS [ReadOnly], +dtb.is_recursive_triggers_on AS [RecursiveTriggersEnabled], +dtb.user_access AS [UserAccess], +dtb.is_db_chaining_on AS [DatabaseOwnershipChaining], +dtb.is_auto_update_stats_async_on AS [AutoUpdateStatisticsAsync], +dtb.is_date_correlation_on AS [DateCorrelationOptimization], +dtb.is_trustworthy_on AS [Trustworthy], +dtb.name AS [Name], +dtb.database_id AS [ID], +dtb.create_date AS [CreateDate], +dtb.is_auto_create_stats_on AS [AutoCreateStatisticsEnabled], +dtb.is_auto_update_stats_on AS [AutoUpdateStatisticsEnabled], +dtb.is_parameterization_forced AS [IsParameterizationForced], +dtb.is_read_committed_snapshot_on AS [IsReadCommittedSnapshotOn], +CAST(isnull(dtb.source_database_id, 0) AS bit) AS [IsDatabaseSnapshot], +ISNULL(DB_NAME(dtb.source_database_id), N'''') AS [DatabaseSnapshotBaseName], +dtb.is_fulltext_enabled AS [IsFullTextEnabled], +dtb.service_broker_guid AS [ServiceBrokerGuid], +dtb.snapshot_isolation_state AS [SnapshotIsolationState], +(dtb.is_published*1+dtb.is_subscribed*2+dtb.is_merge_published*4) AS [ReplicationOptions], +dtb.is_local_cursor_default AS [LocalCursorsDefault], +dtb.page_verify_option AS [PageVerify], +dtb.recovery_model AS [RecoveryModel], +dtb.is_auto_close_on AS [AutoClose], +dtb.is_broker_enabled AS [BrokerEnabled], +ISNULL(suser_sname(dtb.owner_sid),'''') AS [Owner], +ISNULL(dtb.log_reuse_wait,0) AS [LogReuseWaitStatus], +drs.recovery_fork_guid AS [RecoveryForkGuid], +drs.database_guid AS [DatabaseGuid], +CAST((case when drs.last_log_backup_lsn is not null then 1 else 0 end) AS bit) AS [HasFullBackup], +CAST(case when dtb.name in (''master'',''model'',''msdb'',''tempdb'') then 1 else dtb.is_distributor end AS bit) AS [IsSystemObject], +CAST(case when ctb.database_id is null then 0 else 1 end AS bit) AS [ChangeTrackingEnabled], +CAST(ISNULL(ctb.is_auto_cleanup_on,0) AS bit) AS [ChangeTrackingAutoCleanUp], +ISNULL(ctb.retention_period,0) AS [ChangeTrackingRetentionPeriod], +CAST(ISNULL(ctb.retention_period_units,0) AS tinyint) AS [ChangeTrackingRetentionPeriodUnits], +dtb.containment AS [ContainmentType], +dtb.default_language_lcid AS [DefaultLanguageLcid], +dtb.default_language_name AS [DefaultLanguageName], +dtb.default_fulltext_language_lcid AS [DefaultFullTextLanguageLcid], +ISNULL(dtb.default_fulltext_language_name,N'''') AS [DefaultFullTextLanguageName], +CAST(dtb.is_nested_triggers_on AS bit) AS [NestedTriggersEnabled], +CAST(dtb.is_transform_noise_words_on AS bit) AS [TransformNoiseWords], +dtb.two_digit_year_cutoff AS [TwoDigitYearCutoff], +dtb.target_recovery_time_in_seconds AS [TargetRecoveryTime], +dtb.delayed_durability AS [DelayedDurability], +dtb.is_auto_create_stats_incremental_on AS [AutoCreateIncrementalStatisticsEnabled], + + case + when dtb.collation_name is null then 0x200 + else 0 + end | + case + when 1 = dtb.is_in_standby then 0x40 + else 0 + end | + case dtb.state + when 1 then 0x2 + when 2 then 0x8 + when 3 then 0x4 + when 4 then 0x10 + when 5 then 0x100 + when 6 then 0x20 + else 1 + end + AS [Status], +CAST(( case LOWER(convert( nvarchar(128), DATABASEPROPERTYEX(dtb.name, ''Updateability''))) when ''read_write'' then 1 else 0 end) AS bit) AS [IsUpdateable], +CAST(dtb.is_encrypted AS bit) AS [EncryptionEnabled], +CAST(dtb.is_honor_broker_priority_on AS bit) AS [HonorBrokerPriority], +CAST( + case + when SERVERPROPERTY(''EngineEdition'') = 6 then cast(1 as bit) + else cast(0 as bit) + end + AS bit) AS [IsSqlDw], +CAST(has_dbaccess(dtb.name) AS bit) AS [IsAccessible], +ISNULL(fsopt.directory_name , N'''') AS [FilestreamDirectoryName], +ISNULL(fsopt.non_transacted_access , 0) AS [FilestreamNonTransactedAccess], +CAST(dtb.is_remote_data_archive_enabled AS bit) AS [RemoteDataArchiveEnabled], +NULL AS [RemoteDataArchiveEndpoint], +NULL AS [RemoteDataArchiveLinkedServer], +NULL AS [RemoteDatabaseName], +0 AS [RemoteDataArchiveUseFederatedServiceAccount], +NULL AS [RemoteDataArchiveCredential], +0 AS [MaxDop], +NULL AS [MaxDopForSecondary], +0 AS [LegacyCardinalityEstimation], +2 AS [LegacyCardinalityEstimationForSecondary], +0 AS [ParameterSniffing], +2 AS [ParameterSniffingForSecondary], +0 AS [QueryOptimizerHotfixes], +2 AS [QueryOptimizerHotfixesForSecondary], +dtb.name AS [DatabaseName2], +dtb.containment AS [ContainmentType2], +dtb.name AS [DatabaseName5], +dtb.name AS [DatabaseName6] +FROM +master.sys.databases AS dtb +LEFT OUTER JOIN sys.database_mirroring AS dmi ON dmi.database_id = dtb.database_id +LEFT OUTER JOIN sys.database_recovery_status AS drs ON drs.database_id = dtb.database_id +LEFT OUTER JOIN sys.change_tracking_databases AS ctb ON ctb.database_id = dtb.database_id +LEFT OUTER JOIN sys.database_filestream_options AS fsopt ON fsopt.database_id = dtb.database_id +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' Core .Net SqlClient Data Provider sa 0 47 0 0 1239302740 59 2017-08-04 14:30:11.657 2017-08-04 14:30:11.657 0X00000000040000001A00730070005F006500780065006300750074006500730071006C002E32000082000A0063206E00740065007800740016320000530045004C004500430054000A00490053004E0055004C004C00280028006300610073006500200064006D0069002E006D006900720072006F00720069006E0067005F007200650064006F005F00710075006500750065005F00740079007000650020007700680065006E0020004E00270055004E004C0049004D004900540045004400270020007400680065006E0020003000200065006C0073006500200064006D0069002E006D006900720072006F00720069006E0067005F007200650064006F00 +SQL:BatchStarting select +case + when cfg.configuration_id = 124 -- configuration id for default language + then (select lcid from sys.syslanguages as sl where sl.langid = cfg.value_in_use) -- getting default language LCID from default language langid + else cfg.value_in_use +end as value, +case + when cfg.configuration_id = 124 -- configuration id for default language + then (select name collate catalog_default from sys.syslanguages as sl where sl.langid = cfg.value_in_use) -- getting default language name from default language langid + when cfg.configuration_id = 1126 -- configuration id for default fulltext language + then ISNULL((select name collate catalog_default from sys.fulltext_languages as fl where fl.lcid = cfg.value_in_use), N'') -- getting default fulltext language name from default fulltext language lcid + else null +end as name, +cfg.configuration_id as configuration_id +from sys.configurations as cfg +where cfg.configuration_id in (115, 124, 1126, 1127, 1555) +order by cfg.configuration_id asc Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.673 +SQL:BatchCompleted select +case + when cfg.configuration_id = 124 -- configuration id for default language + then (select lcid from sys.syslanguages as sl where sl.langid = cfg.value_in_use) -- getting default language LCID from default language langid + else cfg.value_in_use +end as value, +case + when cfg.configuration_id = 124 -- configuration id for default language + then (select name collate catalog_default from sys.syslanguages as sl where sl.langid = cfg.value_in_use) -- getting default language name from default language langid + when cfg.configuration_id = 1126 -- configuration id for default fulltext language + then ISNULL((select name collate catalog_default from sys.fulltext_languages as fl where fl.lcid = cfg.value_in_use), N'') -- getting default fulltext language name from default fulltext language lcid + else null +end as name, +cfg.configuration_id as configuration_id +from sys.configurations as cfg +where cfg.configuration_id in (115, 124, 1126, 1127, 1555) +order by cfg.configuration_id asc Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.673 2017-08-04 14:30:11.673 +SQL:BatchStarting SELECT CASE WHEN has_dbaccess(N'master') = 1 THEN 'true' ELSE 'false' END Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.683 +SQL:BatchCompleted SELECT CASE WHEN has_dbaccess(N'master') = 1 THEN 'true' ELSE 'false' END Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.683 2017-08-04 14:30:11.683 +RPC:Completed exec sp_executesql N'SELECT COLLATIONPROPERTY((select collation_name from sys.databases where name = ISNULL(@dbname, db_name())), ''LCID''), COLLATIONPROPERTY((select collation_name from sys.databases where name = ISNULL(@dbname, db_name())), ''ComparisonStyle'')',N'@dbname nvarchar(6)',@dbname=N'master' Core .Net SqlClient Data Provider sa 0 16 0 0 1239302740 59 2017-08-04 14:30:11.700 2017-08-04 14:30:11.700 0X00000000040000001A00730070005F006500780065006300750074006500730071006C000402000082001A00E7206E00760061007200630068006100720028003200330038002900DC010000530045004C00450043005400200043004F004C004C004100540049004F004E00500052004F005000450052005400590028002800730065006C00650063007400200063006F006C006C006100740069006F006E005F006E0061006D0065002000660072006F006D0020007300790073002E0064006100740061006200610073006500730020007700680065007200650020006E0061006D00650020003D002000490053004E0055004C004C002800400064006200 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + Core .Net SqlClient Data Provider sa 1239302740 60 2017-08-04 14:30:11.737 +SQL:BatchStarting SELECT +eds.location AS [RemoteDataArchiveEndpoint], +eds.name AS [RemoteDataArchiveLinkedServer], +rdad.remote_database_name AS [RemoteDatabaseName], +rdad.federated_service_account AS [RemoteDataArchiveUseFederatedServiceAccount], +case when rdad.federated_service_account = 1 then null else cred.name end AS [RemoteDataArchiveCredential] +FROM +sys.remote_data_archive_databases rdad +INNER JOIN sys.external_data_sources eds ON rdad.data_source_id = eds.data_source_id +LEFT OUTER JOIN sys.database_scoped_credentials cred ON eds.credential_id = cred.credential_id Core .Net SqlClient Data Provider sa 1239302740 60 2017-08-04 14:30:11.737 +SQL:BatchCompleted SELECT +eds.location AS [RemoteDataArchiveEndpoint], +eds.name AS [RemoteDataArchiveLinkedServer], +rdad.remote_database_name AS [RemoteDatabaseName], +rdad.federated_service_account AS [RemoteDataArchiveUseFederatedServiceAccount], +case when rdad.federated_service_account = 1 then null else cred.name end AS [RemoteDataArchiveCredential] +FROM +sys.remote_data_archive_databases rdad +INNER JOIN sys.external_data_sources eds ON rdad.data_source_id = eds.data_source_id +LEFT OUTER JOIN sys.database_scoped_credentials cred ON eds.credential_id = cred.credential_id Core .Net SqlClient Data Provider sa 0 2 0 0 1239302740 60 2017-08-04 14:30:11.737 2017-08-04 14:30:11.737 +Audit Logout Core .Net SqlClient Data Provider sa 0 2 0 14 1239302740 60 2017-08-04 14:30:11.737 2017-08-04 14:30:11.750 +SQL:BatchStarting SELECT CASE WHEN has_dbaccess(N'master') = 1 THEN 'true' ELSE 'false' END Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.763 +SQL:BatchCompleted SELECT CASE WHEN has_dbaccess(N'master') = 1 THEN 'true' ELSE 'false' END Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.763 2017-08-04 14:30:11.763 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + Core .Net SqlClient Data Provider sa 1239302740 60 2017-08-04 14:30:11.807 +SQL:BatchStarting SELECT +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'MAXDOP') AS [MaxDop], +(select value_for_secondary from sys.database_scoped_configurations as dsc where dsc.name = 'MAXDOP') AS [MaxDopForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'LEGACY_CARDINALITY_ESTIMATION') AS [LegacyCardinalityEstimation], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'LEGACY_CARDINALITY_ESTIMATION') AS [LegacyCardinalityEstimationForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'PARAMETER_SNIFFING') AS [ParameterSniffing], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'PARAMETER_SNIFFING') AS [ParameterSniffingForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'QUERY_OPTIMIZER_HOTFIXES') AS [QueryOptimizerHotfixes], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'QUERY_OPTIMIZER_HOTFIXES') AS [QueryOptimizerHotfixesForSecondary] Core .Net SqlClient Data Provider sa 1239302740 60 2017-08-04 14:30:11.807 +SQL:BatchCompleted SELECT +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'MAXDOP') AS [MaxDop], +(select value_for_secondary from sys.database_scoped_configurations as dsc where dsc.name = 'MAXDOP') AS [MaxDopForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'LEGACY_CARDINALITY_ESTIMATION') AS [LegacyCardinalityEstimation], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'LEGACY_CARDINALITY_ESTIMATION') AS [LegacyCardinalityEstimationForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'PARAMETER_SNIFFING') AS [ParameterSniffing], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'PARAMETER_SNIFFING') AS [ParameterSniffingForSecondary], +(select value from sys.database_scoped_configurations as dsc where dsc.name = 'QUERY_OPTIMIZER_HOTFIXES') AS [QueryOptimizerHotfixes], +(select ISNULL(value_for_secondary, 2) from sys.database_scoped_configurations as dsc where dsc.name = 'QUERY_OPTIMIZER_HOTFIXES') AS [QueryOptimizerHotfixesForSecondary] Core .Net SqlClient Data Provider sa 0 44 0 0 1239302740 60 2017-08-04 14:30:11.807 2017-08-04 14:30:11.807 +Audit Logout Core .Net SqlClient Data Provider sa 0 44 0 4 1239302740 60 2017-08-04 14:30:11.807 2017-08-04 14:30:11.810 +SQL:BatchStarting SELECT +(@@microsoftversion / 0x1000000) & 0xff AS [VersionMajor] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.820 +SQL:BatchCompleted SELECT +(@@microsoftversion / 0x1000000) & 0xff AS [VersionMajor] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.820 2017-08-04 14:30:11.820 +SQL:BatchStarting select is_srvrolemember('sysadmin') * 1 +is_srvrolemember('serveradmin') * 2 +is_srvrolemember('setupadmin') * 4 +is_srvrolemember('securityadmin') * 8 +is_srvrolemember('processadmin') * 16 +is_srvrolemember('dbcreator') * 32 +is_srvrolemember('diskadmin') * 64+ is_srvrolemember('bulkadmin') * 128 Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.827 +SQL:BatchCompleted select is_srvrolemember('sysadmin') * 1 +is_srvrolemember('serveradmin') * 2 +is_srvrolemember('setupadmin') * 4 +is_srvrolemember('securityadmin') * 8 +is_srvrolemember('processadmin') * 16 +is_srvrolemember('dbcreator') * 32 +is_srvrolemember('diskadmin') * 64+ is_srvrolemember('bulkadmin') * 128 Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.827 2017-08-04 14:30:11.827 +SQL:BatchStarting USE [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.837 +SQL:BatchCompleted USE [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.837 2017-08-04 14:30:11.837 +SQL:BatchStarting select suser_sname((select sid from sys.database_principals where name = N'dbo')); Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.840 +SQL:BatchCompleted select suser_sname((select sid from sys.database_principals where name = N'dbo')); Core .Net SqlClient Data Provider sa 0 6 0 0 1239302740 59 2017-08-04 14:30:11.840 2017-08-04 14:30:11.840 +RPC:Completed exec sp_executesql N' + create table #tempbackup (database_name nvarchar(128), [type] char(1), backup_finish_date datetime) + insert into #tempbackup select database_name, [type], max(backup_finish_date) from msdb..backupset where [type] = ''D'' or [type] = ''L'' or [type]=''I'' group by database_name, [type] + + + +SELECT +(select backup_finish_date from #tempbackup where type = @_msparam_0 and db_id(database_name) = dtb.database_id) AS [LastBackupDate] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_1) + + drop table #tempbackup + +',N'@_msparam_0 nvarchar(4000),@_msparam_1 nvarchar(4000)',@_msparam_0=N'D',@_msparam_1=N'master' Core .Net SqlClient Data Provider sa 0 221 1 5 1239302740 59 2017-08-04 14:30:11.857 2017-08-04 14:30:11.863 0X00000000050000001A00730070005F006500780065006300750074006500730071006C006C04000082001A00E7206E00760061007200630068006100720028003500340036002900440400000A002000200020002000200020002000200063007200650061007400650020007400610062006C00650020002300740065006D0070006200610063006B007500700020002800640061007400610062006100730065005F006E0061006D00650020006E007600610072006300680061007200280031003200380029002C0020005B0074007900700065005D00200063006800610072002800310029002C0020006200610063006B00750070005F00660069006E00 +RPC:Completed exec sp_executesql N' + create table #tempbackup (database_name nvarchar(128), [type] char(1), backup_finish_date datetime) + insert into #tempbackup select database_name, [type], max(backup_finish_date) from msdb..backupset where [type] = ''D'' or [type] = ''L'' or [type]=''I'' group by database_name, [type] + + + +SELECT +(select backup_finish_date from #tempbackup where type = @_msparam_0 and db_id(database_name) = dtb.database_id) AS [LastLogBackupDate] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_1) + + drop table #tempbackup + +',N'@_msparam_0 nvarchar(4000),@_msparam_1 nvarchar(4000)',@_msparam_0=N'L',@_msparam_1=N'master' Core .Net SqlClient Data Provider sa 16 200 0 3 1239302740 59 2017-08-04 14:30:11.870 2017-08-04 14:30:11.873 0X00000000050000001A00730070005F006500780065006300750074006500730071006C007204000082001A00E7206E007600610072006300680061007200280035003400390029004A0400000A002000200020002000200020002000200063007200650061007400650020007400610062006C00650020002300740065006D0070006200610063006B007500700020002800640061007400610062006100730065005F006E0061006D00650020006E007600610072006300680061007200280031003200380029002C0020005B0074007900700065005D00200063006800610072002800310029002C0020006200610063006B00750070005F00660069006E00 +RPC:Completed exec sp_executesql N'SELECT +dtb.collation_name AS [Collation], +dtb.name AS [DatabaseName2] +FROM +master.sys.databases AS dtb +WHERE +(dtb.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' Core .Net SqlClient Data Provider sa 0 8 0 0 1239302740 59 2017-08-04 14:30:11.883 2017-08-04 14:30:11.883 0X00000000040000001A00730070005F006500780065006300750074006500730071006C002E01000082001A00E7206E0076006100720063006800610072002800310033003100290006010000530045004C004500430054000A006400740062002E0063006F006C006C006100740069006F006E005F006E0061006D00650020004100530020005B0043006F006C006C006100740069006F006E005D002C000A006400740062002E006E0061006D00650020004100530020005B00440061007400610062006100730065004E0061006D00650032005D000A00460052004F004D000A006D00610073007400650072002E007300790073002E006400610074006100 +SQL:BatchStarting + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SmoRoot OUTPUT + + + +SELECT +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +N'Windows' AS [HostPlatform], +N'\' AS [PathSeparator], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.900 +SQL:BatchCompleted + declare @MasterPath nvarchar(512) + declare @LogPath nvarchar(512) + declare @ErrorLog nvarchar(512) + declare @ErrorLogPath nvarchar(512) + + select @MasterPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'master' + select @LogPath=substring(physical_name, 1, len(physical_name) - charindex('\', reverse(physical_name))) from master.sys.database_files where name=N'mastlog' + select @ErrorLog=cast(SERVERPROPERTY(N'errorlogfilename') as nvarchar(512)) + select @ErrorLogPath=substring(@ErrorLog, 1, len(@ErrorLog) - charindex('\', reverse(@ErrorLog))) + + + + declare @SmoRoot nvarchar(512) + exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SmoRoot OUTPUT + + + +SELECT +CAST(case when 'a' <> 'A' then 1 else 0 end AS bit) AS [IsCaseSensitive], +@@MAX_PRECISION AS [MaxPrecision], +@ErrorLogPath AS [ErrorLogPath], +@SmoRoot AS [RootDirectory], +N'Windows' AS [HostPlatform], +N'\' AS [PathSeparator], +CAST(FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') AS bit) AS [IsFullTextInstalled], +@LogPath AS [MasterDBLogPath], +@MasterPath AS [MasterDBPath], +SERVERPROPERTY(N'ProductVersion') AS [VersionString], +CAST(SERVERPROPERTY(N'Edition') AS sysname) AS [Edition], +CAST(SERVERPROPERTY(N'ProductLevel') AS sysname) AS [ProductLevel], +CAST(SERVERPROPERTY('IsSingleUser') AS bit) AS [IsSingleUser], +CAST(SERVERPROPERTY('EngineEdition') AS int) AS [EngineEdition], +convert(sysname, serverproperty(N'collation')) AS [Collation], +CAST(SERVERPROPERTY(N'MachineName') AS sysname) AS [NetName], +CAST(SERVERPROPERTY('IsClustered') AS bit) AS [IsClustered], +SERVERPROPERTY(N'ResourceVersion') AS [ResourceVersionString], +SERVERPROPERTY(N'ResourceLastUpdateDateTime') AS [ResourceLastUpdateDateTime], +SERVERPROPERTY(N'CollationID') AS [CollationID], +SERVERPROPERTY(N'ComparisonStyle') AS [ComparisonStyle], +SERVERPROPERTY(N'SqlCharSet') AS [SqlCharSet], +SERVERPROPERTY(N'SqlCharSetName') AS [SqlCharSetName], +SERVERPROPERTY(N'SqlSortOrder') AS [SqlSortOrder], +SERVERPROPERTY(N'SqlSortOrderName') AS [SqlSortOrderName], +SERVERPROPERTY(N'BuildClrVersion') AS [BuildClrVersionString], +SERVERPROPERTY(N'ComputerNamePhysicalNetBIOS') AS [ComputerNamePhysicalNetBIOS], +CAST(SERVERPROPERTY('IsPolyBaseInstalled') AS bit) AS [IsPolyBaseInstalled] Core .Net SqlClient Data Provider sa 0 5 0 2 1239302740 59 2017-08-04 14:30:11.900 2017-08-04 14:30:11.903 +RPC:Completed exec sp_executesql N' + create table #tmp_sp_db_vardecimal_storage_format (dbname sysname null, vardecimal_enabled varchar(3) null) + if exists (select o.object_id from sys.system_objects o where o.name=N''sp_db_vardecimal_storage_format'') + begin + insert into #tmp_sp_db_vardecimal_storage_format exec sys.sp_db_vardecimal_storage_format + end + + + +SELECT + + case + when vardec.vardecimal_enabled = ''ON'' then cast(1 as bit) + else cast(0 as bit) + end + AS [IsVarDecimalStorageFormatEnabled] +FROM +master.sys.databases AS dtb +LEFT OUTER JOIN #tmp_sp_db_vardecimal_storage_format as vardec ON dtb.database_id = db_id(vardec.dbname) +WHERE +(dtb.name=@_msparam_0) + + drop table #tmp_sp_db_vardecimal_storage_format + +',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'master' Core .Net SqlClient Data Provider sa 15 469 2 6 1239302740 59 2017-08-04 14:30:11.913 2017-08-04 14:30:11.920 0X00000000040000001A00730070005F006500780065006300750074006500730071006C002406000082001A00E7206E00760061007200630068006100720028003700360036002900FC0500000A002000200020002000200020002000200063007200650061007400650020007400610062006C0065002000230074006D0070005F00730070005F00640062005F0076006100720064006500630069006D0061006C005F00730074006F0072006100670065005F0066006F0072006D006100740020002800640062006E0061006D00650020007300790073006E0061006D00650020006E0075006C006C002C00200076006100720064006500630069006D006100 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.953 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.953 2017-08-04 14:30:11.953 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'MAXDOP' Core .Net SqlClient Data Provider sa 0 8 0 0 1239302740 59 2017-08-04 14:30:11.957 2017-08-04 14:30:11.957 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00EA00000082001800E7206E0076006100720063006800610072002800390038002900C4000000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A007300790073002E00640061007400610062006100730065005F00730063006F007000650064005F0063006F006E00660069006700750072006100740069006F006E0073002000410053002000730070000A00570048004500520045000A002800730070002E006E0061006D0065003D0040005F006D0073007000 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.967 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.967 2017-08-04 14:30:11.967 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name], +sp.configuration_id AS [Id], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value,''BaseType'') = ''bit'' THEN + CASE value + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value, ''NULL'') + END + AS nvarchar(200)) AS [Value], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value_for_secondary,''BaseType'') = ''bit'' THEN + CASE value_for_secondary + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value_for_secondary, ''PRIMARY'') + END + AS nvarchar(200)) AS [ValueForSecondary] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'MAXDOP' Core .Net SqlClient Data Provider sa 0 10 0 0 1239302740 59 2017-08-04 14:30:11.970 2017-08-04 14:30:11.970 0X00000000040000001A00730070005F006500780065006300750074006500730071006C009A05000082001A00E7206E0076006100720063006800610072002800360039003700290072050000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D002C000A00730070002E0063006F006E00660069006700750072006100740069006F006E005F006900640020004100530020005B00490064005D002C000A00430041005300540028000A0020002000200020002000200043004100530045000A00200020002000200020002000200020005700480045004E002000530051004C005F005600 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.977 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.977 2017-08-04 14:30:11.977 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Legacy_Cardinality_Estimation' Core .Net SqlClient Data Provider sa 0 8 0 0 1239302740 59 2017-08-04 14:30:11.980 2017-08-04 14:30:11.980 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00EA00000082001800E7206E0076006100720063006800610072002800390038002900C4000000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A007300790073002E00640061007400610062006100730065005F00730063006F007000650064005F0063006F006E00660069006700750072006100740069006F006E0073002000410053002000730070000A00570048004500520045000A002800730070002E006E0061006D0065003D0040005F006D0073007000 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:11.990 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:11.990 2017-08-04 14:30:11.990 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name], +sp.configuration_id AS [Id], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value,''BaseType'') = ''bit'' THEN + CASE value + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value, ''NULL'') + END + AS nvarchar(200)) AS [Value], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value_for_secondary,''BaseType'') = ''bit'' THEN + CASE value_for_secondary + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value_for_secondary, ''PRIMARY'') + END + AS nvarchar(200)) AS [ValueForSecondary] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Legacy_Cardinality_Estimation' Core .Net SqlClient Data Provider sa 0 10 0 0 1239302740 59 2017-08-04 14:30:11.997 2017-08-04 14:30:11.997 0X00000000040000001A00730070005F006500780065006300750074006500730071006C009A05000082001A00E7206E0076006100720063006800610072002800360039003700290072050000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D002C000A00730070002E0063006F006E00660069006700750072006100740069006F006E005F006900640020004100530020005B00490064005D002C000A00430041005300540028000A0020002000200020002000200043004100530045000A00200020002000200020002000200020005700480045004E002000530051004C005F005600 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:12.007 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:12.007 2017-08-04 14:30:12.007 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Parameter_Sniffing' Core .Net SqlClient Data Provider sa 0 8 0 0 1239302740 59 2017-08-04 14:30:12.010 2017-08-04 14:30:12.010 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00EA00000082001800E7206E0076006100720063006800610072002800390038002900C4000000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A007300790073002E00640061007400610062006100730065005F00730063006F007000650064005F0063006F006E00660069006700750072006100740069006F006E0073002000410053002000730070000A00570048004500520045000A002800730070002E006E0061006D0065003D0040005F006D0073007000 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:12.017 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:12.017 2017-08-04 14:30:12.017 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name], +sp.configuration_id AS [Id], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value,''BaseType'') = ''bit'' THEN + CASE value + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value, ''NULL'') + END + AS nvarchar(200)) AS [Value], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value_for_secondary,''BaseType'') = ''bit'' THEN + CASE value_for_secondary + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value_for_secondary, ''PRIMARY'') + END + AS nvarchar(200)) AS [ValueForSecondary] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Parameter_Sniffing' Core .Net SqlClient Data Provider sa 0 10 0 0 1239302740 59 2017-08-04 14:30:12.020 2017-08-04 14:30:12.020 0X00000000040000001A00730070005F006500780065006300750074006500730071006C009A05000082001A00E7206E0076006100720063006800610072002800360039003700290072050000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D002C000A00730070002E0063006F006E00660069006700750072006100740069006F006E005F006900640020004100530020005B00490064005D002C000A00430041005300540028000A0020002000200020002000200043004100530045000A00200020002000200020002000200020005700480045004E002000530051004C005F005600 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:12.027 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:12.027 2017-08-04 14:30:12.027 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Query_Optimizer_Hotfixes' Core .Net SqlClient Data Provider sa 0 8 0 0 1239302740 59 2017-08-04 14:30:12.030 2017-08-04 14:30:12.030 0X00000000040000001A00730070005F006500780065006300750074006500730071006C00EA00000082001800E7206E0076006100720063006800610072002800390038002900C4000000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D000A00460052004F004D000A007300790073002E00640061007400610062006100730065005F00730063006F007000650064005F0063006F006E00660069006700750072006100740069006F006E0073002000410053002000730070000A00570048004500520045000A002800730070002E006E0061006D0065003D0040005F006D0073007000 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:12.047 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:12.047 2017-08-04 14:30:12.047 +RPC:Completed exec sp_executesql N'SELECT +sp.name AS [Name], +sp.configuration_id AS [Id], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value,''BaseType'') = ''bit'' THEN + CASE value + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value, ''NULL'') + END + AS nvarchar(200)) AS [Value], +CAST( + CASE + WHEN SQL_VARIANT_PROPERTY(value_for_secondary,''BaseType'') = ''bit'' THEN + CASE value_for_secondary + WHEN 1 THEN ''ON'' + WHEN 0 THEN ''OFF'' + END + ELSE ISNULL(value_for_secondary, ''PRIMARY'') + END + AS nvarchar(200)) AS [ValueForSecondary] +FROM +sys.database_scoped_configurations AS sp +WHERE +(sp.name=@_msparam_0)',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'Query_Optimizer_Hotfixes' Core .Net SqlClient Data Provider sa 0 10 0 0 1239302740 59 2017-08-04 14:30:12.050 2017-08-04 14:30:12.050 0X00000000040000001A00730070005F006500780065006300750074006500730071006C009A05000082001A00E7206E0076006100720063006800610072002800360039003700290072050000530045004C004500430054000A00730070002E006E0061006D00650020004100530020005B004E0061006D0065005D002C000A00730070002E0063006F006E00660069006700750072006100740069006F006E005F006900640020004100530020005B00490064005D002C000A00430041005300540028000A0020002000200020002000200043004100530045000A00200020002000200020002000200020005700480045004E002000530051004C005F005600 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:12.063 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:12.063 2017-08-04 14:30:12.063 +SQL:BatchStarting + DECLARE @is_policy_automation_enabled bit + SET @is_policy_automation_enabled = (SELECT CONVERT(bit, current_value) + FROM msdb.dbo.syspolicy_configuration + WHERE name = 'Enabled') + + + +SELECT +CAST(cast(g.name as varbinary(256)) AS sysname) AS [Name], +g.data_space_id AS [ID], +CAST(g.is_default AS bit) AS [IsDefault], +g.is_read_only AS [ReadOnly], +CAST(ISNULL((select sum(cast(gs.size as float))*convert(float,8) from sys.database_files gs where gs.data_space_id = g.data_space_id), 0) AS float) AS [Size], +CASE g.type WHEN 'FG' THEN 0 WHEN 'PS' THEN 1 WHEN 'FD' THEN 2 WHEN 'FX' THEN 3 END AS [FileGroupType], +CAST(CASE WHEN 'FD'=g.type THEN 1 ELSE 0 END AS bit) AS [IsFileStream], +case when 1=@is_policy_automation_enabled and exists (select * from msdb.dbo.syspolicy_system_health_state where target_query_expression_with_id like 'Server' + '/Database\[@ID=' + convert(nvarchar(20),dtb.database_id) + '\]'+ '/FileGroup\[@ID=' + convert(nvarchar(20),g.data_space_id) + '\]%' ESCAPE '\') then 1 else 0 end AS [PolicyHealthState] +FROM +master.sys.databases AS dtb, +sys.filegroups AS g +WHERE +(dtb.name=db_name()) +ORDER BY +[Name] ASC Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:12.067 +SQL:BatchCompleted + DECLARE @is_policy_automation_enabled bit + SET @is_policy_automation_enabled = (SELECT CONVERT(bit, current_value) + FROM msdb.dbo.syspolicy_configuration + WHERE name = 'Enabled') + + + +SELECT +CAST(cast(g.name as varbinary(256)) AS sysname) AS [Name], +g.data_space_id AS [ID], +CAST(g.is_default AS bit) AS [IsDefault], +g.is_read_only AS [ReadOnly], +CAST(ISNULL((select sum(cast(gs.size as float))*convert(float,8) from sys.database_files gs where gs.data_space_id = g.data_space_id), 0) AS float) AS [Size], +CASE g.type WHEN 'FG' THEN 0 WHEN 'PS' THEN 1 WHEN 'FD' THEN 2 WHEN 'FX' THEN 3 END AS [FileGroupType], +CAST(CASE WHEN 'FD'=g.type THEN 1 ELSE 0 END AS bit) AS [IsFileStream], +case when 1=@is_policy_automation_enabled and exists (select * from msdb.dbo.syspolicy_system_health_state where target_query_expression_with_id like 'Server' + '/Database\[@ID=' + convert(nvarchar(20),dtb.database_id) + '\]'+ '/FileGroup\[@ID=' + convert(nvarchar(20),g.data_space_id) + '\]%' ESCAPE '\') then 1 else 0 end AS [PolicyHealthState] +FROM +master.sys.databases AS dtb, +sys.filegroups AS g +WHERE +(dtb.name=db_name()) +ORDER BY +[Name] ASC Core .Net SqlClient Data Provider sa 0 14 0 0 1239302740 59 2017-08-04 14:30:12.067 2017-08-04 14:30:12.067 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:12.083 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:12.083 2017-08-04 14:30:12.083 +RPC:Completed exec sp_executesql N' with s as ( +select df.[type] as [type], +df.name collate database_default as name, +df.drop_lsn as drop_lsn, +dtb.database_id as database_id, +df.[file_id] as [file_id], +df.max_size as max_size, +df.growth as growth, +df.is_percent_growth as is_percent_growth, +df.is_media_read_only as is_media_read_only, +df.is_read_only as is_read_only, +df.[state] as [state], +df.is_sparse as is_sparse, +df.data_space_id as data_space_id, +df.physical_name collate database_default as physical_name, +df.size as size + from sys.database_files as df inner join sys.databases as dtb + on (db_id() = dtb.database_id) + and (df.type = 2 or df.type = 0) + and (df.drop_lsn is null) + where + (dtb.source_database_id is null) +union +select +mf.[type] as [type], +mf.name collate database_default as name, +mf.drop_lsn as drop_lsn, +mf.database_id as database_id, +mf.[file_id] as [file_id], +mf.max_size as max_size, +mf.growth as growth, +mf.is_percent_growth as is_percent_growth, +mf.is_media_read_only as is_media_read_only, +mf.is_read_only as is_read_only, +mf.[state] as [state], +mf.is_sparse as is_sparse, +mf.data_space_id as data_space_id, +mf.physical_name collate database_default as physical_name, +mf.size as size + from sys.master_files as mf inner join sys.databases as db + on (mf.database_id = db.database_id) + and (mf.type = 2 or mf.type = 0) + and (mf.drop_lsn is null) + and (db.source_database_id is not null) + where mf.database_id = db_id()) + + + +SELECT +s.name AS [Name], +CAST(CASE s.file_id WHEN 1 THEN 1 ELSE 0 END AS bit) AS [IsPrimaryFile], +CAST(CASE when s.growth=0 THEN (CASE WHEN s.type = 2 THEN 0 ELSE 99 END) ELSE s.is_percent_growth END AS int) AS [GrowthType], +s.physical_name AS [FileName], +s.size * CONVERT(float,8) AS [Size], +CASE when s.max_size=-1 then -1 else s.max_size * CONVERT(float,8) END AS [MaxSize], +s.file_id AS [ID], +''Server[@Name='' + quotename(CAST( + serverproperty(N''Servername'') + AS sysname),'''''''') + '']'' + ''/Database[@Name='' + quotename(db_name(),'''''''') + '']'' + ''/FileGroup[@Name='' + quotename(CAST(cast(g.name as varbinary(256)) AS sysname),'''''''') + '']'' + ''/File[@Name='' + quotename(s.name,'''''''') + '']'' AS [Urn], +CAST(CASE s.is_percent_growth WHEN 1 THEN s.growth ELSE s.growth*8 END AS float) AS [Growth], +s.is_media_read_only AS [IsReadOnlyMedia], +s.is_read_only AS [IsReadOnly], +CAST(case s.state when 6 then 1 else 0 end AS bit) AS [IsOffline], +s.is_sparse AS [IsSparse] +FROM +sys.filegroups AS g +INNER JOIN s ON s.data_space_id=g.data_space_id +WHERE +(CAST(cast(g.name as varbinary(256)) AS sysname)=@_msparam_0) +ORDER BY +[Name] ASC',N'@_msparam_0 nvarchar(4000)',@_msparam_0=N'PRIMARY' Core .Net SqlClient Data Provider sa 0 12 0 0 1239302740 59 2017-08-04 14:30:12.087 2017-08-04 14:30:12.087 0X00000000040000001A00730070005F006500780065006300750074006500730071006C001615000082001C00E7206E007600610072006300680061007200280032003600370038002900EC14000020002000770069007400680020007300200061007300200028000A00730065006C006500630074002000640066002E005B0074007900700065005D0020006100730020005B0074007900700065005D002C000A00640066002E006E0061006D006500200063006F006C006C006100740065002000640061007400610062006100730065005F00640065006600610075006C00740020006100730020006E0061006D0065002C000A00640066002E0064007200 +SQL:BatchStarting use [master] Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:12.117 +SQL:BatchCompleted use [master] Core .Net SqlClient Data Provider sa 0 0 0 0 1239302740 59 2017-08-04 14:30:12.117 2017-08-04 14:30:12.117 +SQL:BatchStarting SELECT +s.name AS [Name], +CAST(FILEPROPERTY(s.name, 'SpaceUsed') AS float)* CONVERT(float,8) AS [UsedSpace], +CAST(CASE when s.growth=0 THEN (CASE WHEN s.type = 2 THEN 0 ELSE 99 END) ELSE s.is_percent_growth END AS int) AS [GrowthType], +s.physical_name AS [FileName], +s.size * CONVERT(float,8) AS [Size], +CASE when s.max_size=-1 then -1 else s.max_size * CONVERT(float,8) END AS [MaxSize], +s.file_id AS [ID], +'Server[@Name=' + quotename(CAST( + serverproperty(N'Servername') + AS sysname),'''') + ']' + '/Database[@Name=' + quotename(db_name(),'''') + ']' + '/LogFile[@Name=' + quotename(s.name,'''') + ']' AS [Urn], +CAST(CASE s.is_percent_growth WHEN 1 THEN s.growth ELSE s.growth*8 END AS float) AS [Growth], +s.is_media_read_only AS [IsReadOnlyMedia], +s.is_read_only AS [IsReadOnly], +CAST(case s.state when 6 then 1 else 0 end AS bit) AS [IsOffline], +s.is_sparse AS [IsSparse] +FROM +sys.database_files AS s +WHERE +(s.type = 1) +ORDER BY +[Name] ASC Core .Net SqlClient Data Provider sa 1239302740 59 2017-08-04 14:30:12.120 +SQL:BatchCompleted SELECT +s.name AS [Name], +CAST(FILEPROPERTY(s.name, 'SpaceUsed') AS float)* CONVERT(float,8) AS [UsedSpace], +CAST(CASE when s.growth=0 THEN (CASE WHEN s.type = 2 THEN 0 ELSE 99 END) ELSE s.is_percent_growth END AS int) AS [GrowthType], +s.physical_name AS [FileName], +s.size * CONVERT(float,8) AS [Size], +CASE when s.max_size=-1 then -1 else s.max_size * CONVERT(float,8) END AS [MaxSize], +s.file_id AS [ID], +'Server[@Name=' + quotename(CAST( + serverproperty(N'Servername') + AS sysname),'''') + ']' + '/Database[@Name=' + quotename(db_name(),'''') + ']' + '/LogFile[@Name=' + quotename(s.name,'''') + ']' AS [Urn], +CAST(CASE s.is_percent_growth WHEN 1 THEN s.growth ELSE s.growth*8 END AS float) AS [Growth], +s.is_media_read_only AS [IsReadOnlyMedia], +s.is_read_only AS [IsReadOnly], +CAST(case s.state when 6 then 1 else 0 end AS bit) AS [IsOffline], +s.is_sparse AS [IsSparse] +FROM +sys.database_files AS s +WHERE +(s.type = 1) +ORDER BY +[Name] ASC Core .Net SqlClient Data Provider sa 0 6 0 0 1239302740 59 2017-08-04 14:30:12.120 2017-08-04 14:30:12.120 +Audit Login -- network protocol: TCP/IP +set quoted_identifier on +set arithabort off +set numeric_roundabort off +set ansi_warnings on +set ansi_padding on +set ansi_nulls on +set concat_null_yields_null on +set cursor_close_on_commit off +set implicit_transactions off +set language us_english +set dateformat mdy +set datefirst 7 +set transaction isolation level read committed + carbon sa 1239302740 60 2017-08-04 14:30:12.173 +SQL:BatchStarting SELECT s.name AS schema_name, o.[name] AS object_name, o.[type] AS object_type + FROM sys.all_objects o + INNER JOIN sys.schemas s ON o.schema_id = s.schema_id + WHERE (o.[type] = 'P' OR o.[type] = 'V' OR o.[type] = 'U') ORDER BY object_type, schema_name, object_name carbon sa 1239302740 60 2017-08-04 14:30:12.177 +SQL:BatchCompleted SELECT s.name AS schema_name, o.[name] AS object_name, o.[type] AS object_type + FROM sys.all_objects o + INNER JOIN sys.schemas s ON o.schema_id = s.schema_id + WHERE (o.[type] = 'P' OR o.[type] = 'V' OR o.[type] = 'U') ORDER BY object_type, schema_name, object_name carbon sa 31 100 1 42 1239302740 60 2017-08-04 14:30:12.177 2017-08-04 14:30:12.220 +Audit Logout carbon sa 31 100 1 434583 1239302740 60 2017-08-04 14:30:12.173 2017-08-04 14:37:26.757 +Audit Logout carbon sa 16 15 0 1815690 1239302740 56 2017-08-04 14:30:06.520 2017-08-04 15:00:22.210 +Audit Logout carbon sa 0 15 0 1815387 1239302740 58 2017-08-04 14:30:06.823 2017-08-04 15:00:22.210 +Audit Logout carbon sa 15 187 0 1815433 1239302740 57 2017-08-04 14:30:06.780 2017-08-04 15:00:22.213 +Audit Logout Core .Net SqlClient Data Provider sa 47 1107 3 1813947 1239302740 59 2017-08-04 14:30:11.543 2017-08-04 15:00:25.490 diff --git a/src/sql/parts/query/common/constants.ts b/src/sql/parts/query/common/constants.ts new file mode 100644 index 0000000000..07d0e37075 --- /dev/null +++ b/src/sql/parts/query/common/constants.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const copyIncludeHeaders = 'copyIncludeHeaders'; +export const configSaveAsCsv = 'saveAsCsv'; +export const configCopyRemoveNewLine = 'copyRemoveNewLine'; +export const configShowBatchTime = 'showBatchTime'; + diff --git a/src/sql/parts/query/common/flavorStatus.ts b/src/sql/parts/query/common/flavorStatus.ts new file mode 100644 index 0000000000..7c9a38dc2e --- /dev/null +++ b/src/sql/parts/query/common/flavorStatus.ts @@ -0,0 +1,218 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { $, append, show, hide } from 'vs/base/browser/dom'; +import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { IEditorCloseEvent } from 'vs/workbench/common/editor'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Action } from 'vs/base/common/actions'; +import errors = require('vs/base/common/errors'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { getCodeEditor as getEditorWidget } from 'vs/editor/common/services/codeEditorService'; +import nls = require('vs/nls'); + +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; + +import { DidChangeLanguageFlavorParams } from 'data'; + +export interface ISqlProviderEntry extends IPickOpenEntry { + providerId: string; +} + +// Query execution status +class SqlProviderEntry implements ISqlProviderEntry { + constructor(public providerId: string, private _providerDisplayName?: string) { + } + + public get label(): string { + // If display name is provided, use it. Else use default + if (this._providerDisplayName) { + return this._providerDisplayName; + } + + if (!this.providerId) { + return SqlProviderEntry.getDefaultLabel(); + } + // Note: consider adding API to connection management service to + // support getting display name for provider so this is consistent + switch (this.providerId) { + case 'MSSQL': + return 'MSSQL'; + default: + return this.providerId; + } + } + + public static getDefaultLabel(): string { + return nls.localize('chooseSqlLang', 'Choose SQL Language'); + } +} + +// Shows SQL flavor status in the editor +export class SqlFlavorStatusbarItem implements IStatusbarItem { + + private _element: HTMLElement; + private _flavorElement: HTMLElement; + private _sqlStatusEditors: { [editorUri: string]: SqlProviderEntry }; + private _toDispose: IDisposable[]; + + constructor( + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IEditorGroupService private _editorGroupService: IEditorGroupService, + @IQuickOpenService private _quickOpenService: IQuickOpenService, + @IInstantiationService private _instantiationService: IInstantiationService, + ) { + this._sqlStatusEditors = {}; + } + + public render(container: HTMLElement): IDisposable { + this._element = append(container, $('.query-statusbar-group')); + this._flavorElement = append(this._element, $('a.editor-status-selection')); + this._flavorElement.title = nls.localize('changeProvider', "Change SQL language provider"); + this._flavorElement.onclick = () => this._onSelectionClick(); + hide(this._flavorElement); + + this._toDispose = []; + this._toDispose.push( + this._connectionManagementService.onLanguageFlavorChanged((changeParams: DidChangeLanguageFlavorParams) => this._onFlavorChanged(changeParams)), + this._editorGroupService.onEditorsChanged(() => this._onEditorsChanged()), + this._editorGroupService.getStacksModel().onEditorClosed(event => this._onEditorClosed(event)) + ); + + return combinedDisposable(this._toDispose); + } + + private _onSelectionClick() { + const action = this._instantiationService.createInstance(ChangeFlavorAction, ChangeFlavorAction.ID, ChangeFlavorAction.LABEL); + + action.run().done(null, errors.onUnexpectedError); + action.dispose(); + } + + private _onEditorClosed(event: IEditorCloseEvent): void { + let uri = WorkbenchUtils.getEditorUri(event.editor); + if (uri && uri in this._sqlStatusEditors) { + // If active editor is being closed, hide the query status. + let activeEditor = this._editorService.getActiveEditor(); + if (activeEditor) { + let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); + if (uri === currentUri) { + hide(this._flavorElement); + } + } + // note: intentionally not removing language flavor. This is preserved across close/open events at present + // delete this._sqlStatusEditors[uri]; + } + } + + private _onEditorsChanged(): void { + let activeEditor = this._editorService.getActiveEditor(); + if (activeEditor) { + let uri = WorkbenchUtils.getEditorUri(activeEditor.input); + + // Show active editor's language flavor status + if (uri) { + this._showStatus(uri); + } else { + hide(this._flavorElement); + } + } else { + hide(this._flavorElement); + } + } + + private _onFlavorChanged(changeParams: DidChangeLanguageFlavorParams): void { + if (changeParams) { + this._updateStatus(changeParams.uri, new SqlProviderEntry(changeParams.flavor)); + } + } + + // Update query status for the editor + private _updateStatus(uri: string, newStatus: SqlProviderEntry): void { + if (uri) { + this._sqlStatusEditors[uri] = newStatus; + this._showStatus(uri); + } + } + + // Show/hide query status for active editor + private _showStatus(uri: string): void { + let activeEditor = this._editorService.getActiveEditor(); + if (activeEditor) { + let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); + if (uri === currentUri) { + let flavor: SqlProviderEntry = this._sqlStatusEditors[uri]; + if (flavor) { + this._flavorElement.textContent = flavor.label; + } else { + this._flavorElement.textContent = SqlProviderEntry.getDefaultLabel(); + } + show(this._flavorElement); + } + } + } +} + +export class ChangeFlavorAction extends Action { + + public static ID = 'sql.action.editor.changeProvider'; + public static LABEL = nls.localize('changeSqlProvider', "Change SQL Engine Provider"); + + constructor( + actionId: string, + actionLabel: string, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IQuickOpenService private _quickOpenService: IQuickOpenService, + @IMessageService private _messageService: IMessageService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService + ) { + super(actionId, actionLabel); + } + + public run(): TPromise { + let activeEditor = this._editorService.getActiveEditor(); + let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); + if (this._connectionManagementService.isConnected(currentUri)) { + let currentProvider = this._connectionManagementService.getProviderIdFromUri(currentUri); + return this._showMessage(Severity.Info, nls.localize('alreadyConnected', + "A connection using engine {0} exists. To change please disconnect or change connection", currentProvider)); + } + const editorWidget = getEditorWidget(activeEditor); + if (!editorWidget) { + return this._showMessage(Severity.Info, nls.localize('noEditor', "No text editor active at this time")); + } + + // TODO #1334 use connectionManagementService.GetProviderNames here. The challenge is that the credentials provider is returned + // so we need a way to filter this using a capabilities check, with isn't yet implemented + const ProviderOptions: ISqlProviderEntry[] = [ + new SqlProviderEntry('MSSQL') + ]; + + // TODO: select the current language flavor + return this._quickOpenService.pick(ProviderOptions, { placeHolder: nls.localize('pickSqlProvider', "Select SQL Language Provider"), autoFocus: { autoFocusIndex: 0 } }).then(provider => { + if (provider) { + activeEditor = this._editorService.getActiveEditor(); + const editorWidget = getEditorWidget(activeEditor); + if (editorWidget) { + if (currentUri) { + this._connectionManagementService.doChangeLanguageFlavor(currentUri, 'sql', provider.providerId); + } + } + } + }); + } + + private _showMessage(sev: Severity, message: string): TPromise { + this._messageService.show(sev, message); + return TPromise.as(undefined); + } +} diff --git a/src/sql/parts/query/common/localizedConstants.ts b/src/sql/parts/query/common/localizedConstants.ts new file mode 100644 index 0000000000..8ce32ea212 --- /dev/null +++ b/src/sql/parts/query/common/localizedConstants.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + import { localize } from 'vs/nls'; + +// localizable strings + +export const runQueryBatchStartMessage = localize('runQueryBatchStartMessage', 'Started executing query at '); +export const runQueryBatchStartLine = localize('runQueryBatchStartLine', 'Line {0}'); + +export const msgCancelQueryFailed = localize('msgCancelQueryFailed', 'Canceling the query failed: {0}'); + +export const filepathPrompt = localize('filepathPrompt', 'File path'); +export const filepathPlaceholder = localize('filepathPlaceholder', 'File name'); +export const filepathMessage = localize('filepathMessage', 'File name'); +export const overwritePrompt = localize('overwritePrompt', 'A file with this name already exists. Do you want to replace the existing file?'); +export const overwritePlaceholder = localize('overwritePlaceholder', 'A file with this name already exists'); + +export const msgSaveResultInProgress = localize('msgSaveResultInProgress', 'A save request is already executing. Please wait for its completion.'); +export const msgCannotOpenContent = localize('msgCannotOpenContent', 'Error occurred opening content in editor.'); +export const msgSaveStarted = localize('msgSaveStarted', 'Started saving results to '); +export const msgSaveFailed = localize('msgSaveFailed', 'Failed to save results. '); +export const msgSaveSucceeded = localize('msgSaveSucceeded', 'Successfully saved results to '); + + +export const msgStatusRunQueryInProgress = localize('msgStatusRunQueryInProgress', 'Executing query...'); + +// /** Results Pane Labels */ +export const maximizeLabel = localize('maximizeLabel', 'Maximize'); +export const restoreLabel = localize('restoreLabel', 'Restore'); +export const saveCSVLabel = localize('saveCSVLabel', 'Save as CSV'); +export const saveJSONLabel = localize('saveJSONLabel', 'Save as JSON'); +export const saveExcelLabel = localize('saveExcelLabel', 'Save as Excel'); +export const viewChartLabel = localize('viewChartLabel', 'View as Chart'); +export const resultPaneLabel = localize('resultPaneLabel', 'Results'); +export const selectAll = localize('selectAll', 'Select all'); +export const copyLabel = localize('copyLabel', 'Copy'); +export const executeQueryLabel = localize('executeQueryLabel', 'Executing query '); + +/** Messages Pane Labels */ +export const messagePaneLabel = localize('messagePaneLabel', 'Messages'); +export const elapsedTimeLabel = localize('elapsedTimeLabel', 'Total execution time: {0}'); + +/** Warning message for save icons */ +export const msgCannotSaveMultipleSelections = localize('msgCannotSaveMultipleSelections', 'Save results command cannot be used with multiple selections.'); + + diff --git a/src/sql/parts/query/common/query.contribution.ts b/src/sql/parts/query/common/query.contribution.ts new file mode 100644 index 0000000000..dff171a631 --- /dev/null +++ b/src/sql/parts/query/common/query.contribution.ts @@ -0,0 +1,262 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Registry } from 'vs/platform/registry/common/platform'; +import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; +import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; + +import { QueryEditor } from 'sql/parts/query/editor/queryEditor'; +import { QueryResultsEditor } from 'sql/parts/query/editor/queryResultsEditor'; +import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; +import * as queryContext from 'sql/parts/query/common/queryContext'; +import { QueryInput } from 'sql/parts/query/common/queryInput'; +import { EditDataEditor } from 'sql/parts/editData/editor/editDataEditor'; +import { EditDataInput } from 'sql/parts/editData/common/editDataInput'; +import { RunQueryKeyboardAction, RunCurrentQueryKeyboardAction, CancelQueryKeyboardAction, RefreshIntellisenseKeyboardAction } from 'sql/parts/query/execution/keyboardQueryActions'; +import * as gridActions from 'sql/parts/grid/views/gridActions'; +import * as gridCommands from 'sql/parts/grid/views/gridCommands'; +import { QueryPlanEditor } from 'sql/parts/queryPlan/queryPlanEditor'; +import { QueryPlanInput } from 'sql/parts/queryPlan/queryPlanInput'; +import { localize } from 'vs/nls'; + +const gridCommandsWeightBonus = 100; // give our commands a little bit more weight over other default list/tree commands + +export const QueryEditorVisibleCondition = ContextKeyExpr.has(queryContext.queryEditorVisibleId); +export const ResultsGridFocusCondition = ContextKeyExpr.and(ContextKeyExpr.has(queryContext.resultsVisibleId), ContextKeyExpr.has(queryContext.resultsGridFocussedId)); +export const ResultsMessagesFocusCondition = ContextKeyExpr.and(ContextKeyExpr.has(queryContext.resultsVisibleId), ContextKeyExpr.has(queryContext.resultsMessagesFocussedId)); + +// Editor +const queryResultsEditorDescriptor = new EditorDescriptor( + QueryResultsEditor.ID, + 'QueryResults', + 'sql/parts/query/editor/queryResultsEditor', + 'QueryResultsEditor' +); + +Registry.as(EditorExtensions.Editors) + .registerEditor(queryResultsEditorDescriptor, [new SyncDescriptor(QueryResultsInput)]); + +// Editor +const queryEditorDescriptor = new EditorDescriptor( + QueryEditor.ID, + 'Query', + 'sql/parts/query/editor/queryEditor', + 'QueryEditor' +); + +Registry.as(EditorExtensions.Editors) + .registerEditor(queryEditorDescriptor, [new SyncDescriptor(QueryInput)]); + +// Query Plan editor registration + +const createLoginEditorDescriptor = new EditorDescriptor( + QueryPlanEditor.ID, + 'QueryPlan', + 'sql/parts/queryPlan/queryPlanEditor', + 'QueryPlanEditor' +); + +Registry.as(EditorExtensions.Editors) + .registerEditor(createLoginEditorDescriptor, [new SyncDescriptor(QueryPlanInput)]); + +// Editor +const editDataEditorDescriptor = new EditorDescriptor( + EditDataEditor.ID, + 'EditData', + 'sql/parts/editData/editor/editDataEditor', + 'EditDataEditor' +); + +Registry.as(EditorExtensions.Editors) + .registerEditor(editDataEditorDescriptor, [new SyncDescriptor(EditDataInput)]); + +let actionRegistry = Registry.as(Extensions.WorkbenchActions); + +// Query Actions +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor( + RunQueryKeyboardAction, + RunQueryKeyboardAction.ID, + RunQueryKeyboardAction.LABEL, + { primary: KeyCode.F5 } + ), + RunQueryKeyboardAction.LABEL + ); + +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor( + RunCurrentQueryKeyboardAction, + RunCurrentQueryKeyboardAction.ID, + RunCurrentQueryKeyboardAction.LABEL, + { primary:KeyMod.CtrlCmd | KeyCode.F5 } + ), + RunCurrentQueryKeyboardAction.LABEL + ); + +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor( + CancelQueryKeyboardAction, + CancelQueryKeyboardAction.ID, + CancelQueryKeyboardAction.LABEL, + { primary: KeyMod.Alt | KeyCode.PauseBreak } + ), + CancelQueryKeyboardAction.LABEL + ); + +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor( + RefreshIntellisenseKeyboardAction, + RefreshIntellisenseKeyboardAction.ID, + RefreshIntellisenseKeyboardAction.LABEL + ), + RefreshIntellisenseKeyboardAction.LABEL + ); + +// Grid actions + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: gridActions.GRID_COPY_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(gridCommandsWeightBonus), + when: ResultsGridFocusCondition, + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + handler: gridCommands.copySelection +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: gridActions.MESSAGES_SELECTALL_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(gridCommandsWeightBonus), + when: ResultsMessagesFocusCondition, + primary: KeyMod.CtrlCmd | KeyCode.KEY_A, + handler: gridCommands.selectAllMessages +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: gridActions.GRID_SELECTALL_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(gridCommandsWeightBonus), + when: ResultsGridFocusCondition, + primary: KeyMod.CtrlCmd | KeyCode.KEY_A, + handler: gridCommands.selectAll +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: gridActions.MESSAGES_COPY_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(gridCommandsWeightBonus), + when: ResultsMessagesFocusCondition, + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + handler: gridCommands.copyMessagesSelection +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: gridActions.GRID_SAVECSV_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(gridCommandsWeightBonus), + when: ResultsGridFocusCondition, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_R, KeyMod.CtrlCmd | KeyCode.KEY_C), + handler: gridCommands.saveAsCsv +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: gridActions.GRID_SAVEJSON_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(gridCommandsWeightBonus), + when: ResultsGridFocusCondition, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_R, KeyMod.CtrlCmd | KeyCode.KEY_J), + handler: gridCommands.saveAsJson +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: gridActions.GRID_SAVEEXCEL_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(gridCommandsWeightBonus), + when: ResultsGridFocusCondition, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_R, KeyMod.CtrlCmd | KeyCode.KEY_E), + handler: gridCommands.saveAsExcel +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: gridActions.TOGGLERESULTS_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(gridCommandsWeightBonus), + when: QueryEditorVisibleCondition, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R, + handler: gridCommands.toggleResultsPane +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: gridActions.TOGGLEMESSAGES_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(gridCommandsWeightBonus), + when: QueryEditorVisibleCondition, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_Y, + handler: gridCommands.toggleMessagePane +}); + +// Intellisense and other configuration options + +let configurationRegistry = Registry.as(ConfigExtensions.Configuration); +configurationRegistry.registerConfiguration({ + 'id': 'sqlEditor', + 'title': 'SQL Editor', + 'type': 'object', + 'properties': { + 'sql.messagesDefaultOpen': { + 'type': 'boolean', + 'description': localize('sql.messagesDefaultOpen', 'True for the messages pane to be open by default; false for closed'), + 'default': true + }, + 'sql.saveAsCsv.includeHeaders': { + 'type': 'boolean', + 'description': localize('sql.saveAsCsv.includeHeaders', '[Optional] When true, column headers are included when saving results as CSV'), + 'default': true + }, + 'sql.copyIncludeHeaders': { + 'type': 'boolean', + 'description': localize('sql.copyIncludeHeaders', '[Optional] Configuration options for copying results from the Results View'), + 'default': false + }, + 'sql.copyRemoveNewLine': { + 'type': 'boolean', + 'description': localize('sql.copyRemoveNewLine', '[Optional] Configuration options for copying multi-line results from the Results View'), + 'default': true + }, + 'sql.showBatchTime': { + 'type': 'boolean', + 'description': localize('sql.showBatchTime', '[Optional] Should execution time be shown for individual batches'), + 'default': false + }, + 'sql.intelliSense.enableIntelliSense': { + 'type': 'boolean', + 'default': true, + 'description': localize('sql.intelliSense.enableIntelliSense', 'Should IntelliSense be enabled') + }, + 'sql.intelliSense.enableErrorChecking': { + 'type': 'boolean', + 'default': true, + 'description': localize('sql.intelliSense.enableErrorChecking', 'Should IntelliSense error checking be enabled') + }, + 'sql.intelliSense.enableSuggestions': { + 'type': 'boolean', + 'default': true, + 'description': localize('sql.intelliSense.enableSuggestions', 'Should IntelliSense suggestions be enabled') + }, + 'sql.intelliSense.enableQuickInfo': { + 'type': 'boolean', + 'default': true, + 'description': localize('sql.intelliSense.enableQuickInfo', 'Should IntelliSense quick info be enabled') + }, + 'sql.intelliSense.lowerCaseSuggestions': { + 'type': 'boolean', + 'default': false, + 'description': localize('sql.intelliSense.lowerCaseSuggestions', 'Should IntelliSense suggestions be lowercase') + } + } +}); + + + diff --git a/src/sql/parts/query/common/queryContext.ts b/src/sql/parts/query/common/queryContext.ts new file mode 100644 index 0000000000..bf0009eac3 --- /dev/null +++ b/src/sql/parts/query/common/queryContext.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +/** + * Context Keys to use with keybindings for the results grid and messages used in query and edit data views + */ +export const queryEditorVisibleId = 'queryEditorVisible'; +export const resultsVisibleId = 'resultsVisible'; +export const resultsGridFocussedId = 'resultsGridFocussed'; +export const resultsMessagesFocussedId = 'resultsMessagesFocussed'; + +export const QueryEditorVisibleContext = new RawContextKey(queryEditorVisibleId, false); +export const ResultsVisibleContext = new RawContextKey(resultsVisibleId, false); +export const ResultsGridFocussedContext = new RawContextKey(resultsGridFocussedId, false); +export const ResultsMessagesFocussedContext = new RawContextKey(resultsMessagesFocussedId, false); \ No newline at end of file diff --git a/src/sql/parts/query/common/queryEditorService.ts b/src/sql/parts/query/common/queryEditorService.ts new file mode 100644 index 0000000000..457997b14d --- /dev/null +++ b/src/sql/parts/query/common/queryEditorService.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IConnectableInput } from 'sql/parts/connection/common/connectionManagement'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { QueryEditorService } from 'sql/parts/query/services/queryEditorService'; + +export interface IQueryEditorOptions extends IEditorOptions { + + // Tells IQueryEditorService.queryEditorCheck to not open this input in the QueryEditor. + // Used when the user changes the Language Mode to not-SQL for files with .sql extensions. + denyQueryEditor?: boolean; +} + +export const IQueryEditorService = createDecorator('QueryEditorService'); + +export interface IQueryEditorService { + + _serviceBrand: any; + + // Creates new untitled document for SQL queries and opens it in a new editor tab + newSqlEditor(sqlContent?: string, connectionProviderName?: string): Promise; + + // Creates a new query plan document + newQueryPlanEditor(xmlShowPlan: string): Promise; + + // Creates new edit data session + newEditDataEditor(schemaName: string, tableName: string): Promise; + + // Clears any QueryEditor data for the given URI held by this service + onQueryInputClosed(uri: string): void; +} \ No newline at end of file diff --git a/src/sql/parts/query/common/queryInput.ts b/src/sql/parts/query/common/queryInput.ts new file mode 100644 index 0000000000..d46d183fce --- /dev/null +++ b/src/sql/parts/query/common/queryInput.ts @@ -0,0 +1,249 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput, EditorModel, ConfirmResult, EncodingMode, IEncodingSupport } from 'vs/workbench/common/editor'; +import { IConnectionManagementService, IConnectableInput, INewConnectionParams, RunQueryOnConnectionMode } from 'sql/parts/connection/common/connectionManagement'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import URI from 'vs/base/common/uri'; +import { ISelectionData, ExecutionPlanOptions } from 'data'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; + +/** + * Input for the QueryEditor. This input is simply a wrapper around a QueryResultsInput for the QueryResultsEditor + * and a UntitledEditorInput for the SQL File Editor. + */ +export class QueryInput extends EditorInput implements IEncodingSupport, IConnectableInput, IDisposable { + + public static ID: string = 'workbench.editorinputs.queryInput'; + public static SCHEMA: string = 'sql'; + + private _runQueryEnabled: boolean; + private _cancelQueryEnabled: boolean; + private _connectEnabled: boolean; + private _disconnectEnabled: boolean; + private _changeConnectionEnabled: boolean; + private _listDatabasesConnected: boolean; + + private _updateTaskbar: Emitter; + private _showQueryResultsEditor: Emitter; + private _updateSelection: Emitter; + + private _toDispose: IDisposable[]; + private _currentEventCallbacks: IDisposable[]; + + constructor( + private _name: string, + private _description: string, + private _sql: UntitledEditorInput, + private _results: QueryResultsInput, + private _connectionProviderName: string, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IQueryModelService private _queryModelService: IQueryModelService, + @IQueryEditorService private _queryEditorService: IQueryEditorService + ) { + super(); + let self = this; + this._updateTaskbar = new Emitter(); + this._showQueryResultsEditor = new Emitter(); + this._updateSelection = new Emitter(); + + this._toDispose = []; + this._currentEventCallbacks = []; + // re-emit sql editor events through this editor if it exists + if (this._sql) { + this._toDispose.push(this._sql.onDidChangeDirty(() => this._onDidChangeDirty.fire())); + } + + // Attach to event callbacks + if (this._queryModelService) { + // Register callbacks for the Actions + this._toDispose.push( + this._queryModelService.onRunQueryStart(uri => { + if (self.uri === uri) { + self.onRunQuery(); + } + }) + ); + + this._toDispose.push( + this._queryModelService.onRunQueryComplete(uri => { + if (self.uri === uri) { + self.onQueryComplete(); + } + }) + ); + } + + if (this._connectionManagementService) { + this._toDispose.push(self._connectionManagementService.onDisconnect(result => { + if (result.connectionUri === self.uri) { + self.onDisconnect(); + } + })); + if (self.uri) { + if (this._connectionProviderName) { + this._connectionManagementService.doChangeLanguageFlavor(self.uri, 'sql', this._connectionProviderName); + } else { + this._connectionManagementService.ensureDefaultLanguageFlavor(self.uri); + } + } + } + + this.onDisconnect(); + this.onQueryComplete(); + } + + // Getters for private properties + public get uri(): string { return this.getResource().toString(); } + public get sql(): UntitledEditorInput { return this._sql; } + public get results(): QueryResultsInput { return this._results; } + public get updateTaskbarEvent(): Event { return this._updateTaskbar.event; } + public get showQueryResultsEditorEvent(): Event { return this._showQueryResultsEditor.event; } + public get updateSelectionEvent(): Event { return this._updateSelection.event; } + public get runQueryEnabled(): boolean { return this._runQueryEnabled; } + public get cancelQueryEnabled(): boolean { return this._cancelQueryEnabled; } + public get connectEnabled(): boolean { return this._connectEnabled; } + public get disconnectEnabled(): boolean { return this._disconnectEnabled; } + public get changeConnectionEnabled(): boolean { return this._changeConnectionEnabled; } + public get listDatabasesConnected(): boolean { return this._listDatabasesConnected; } + public getQueryResultsInputResource(): string { return this._results.uri; } + public showQueryResultsEditor(): void { this._showQueryResultsEditor.fire(); } + public updateSelection(selection: ISelectionData): void { this._updateSelection.fire(selection); } + public getTypeId(): string { return UntitledEditorInput.ID; } + public getDescription(): string { return this._description; } + public supportsSplitEditor(): boolean { return false; } + public getModeId(): string { return QueryInput.SCHEMA; } + public revert(): TPromise { return this._sql.revert(); } + + public matches(otherInput: any): boolean { + if (otherInput instanceof QueryInput) { + return this._sql.matches(otherInput.sql); + } + + return this._sql.matches(otherInput); + } + + // Forwarding resource functions to the inline sql file editor + public get onDidModelChangeContent(): Event { return this._sql.onDidModelChangeContent; } + public get onDidModelChangeEncoding(): Event { return this._sql.onDidModelChangeEncoding; } + public resolve(refresh?: boolean): TPromise { return this._sql.resolve(); } + public save(): TPromise { return this._sql.save(); } + public isDirty(): boolean { return this._sql.isDirty(); } + public confirmSave(): ConfirmResult { return this._sql.confirmSave(); } + public getResource(): URI { return this._sql.getResource(); } + public getEncoding(): string { return this._sql.getEncoding(); } + public suggestFileName(): string { return this._sql.suggestFileName(); } + public getName(): string { return this._sql.getName(); } + public hasAssociatedFilePath(): boolean { return this._sql.hasAssociatedFilePath; } + + public setEncoding(encoding: string, mode: EncodingMode /* ignored, we only have Encode */): void { + this._sql.setEncoding(encoding, mode); + } + + // State update funtions + public runQuery(selection: ISelectionData, executePlanOptions?: ExecutionPlanOptions): void { + this._queryModelService.runQuery(this.uri, selection, this.uri, this, executePlanOptions); + this.showQueryResultsEditor(); + } + + public runQueryStatement(selection: ISelectionData): void { + this._queryModelService.runQueryStatement(this.uri, selection, this.uri, this); + this.showQueryResultsEditor(); + } + + public onConnectStart(): void { + this._runQueryEnabled = false; + this._cancelQueryEnabled = false; + this._connectEnabled = false; + this._disconnectEnabled = true; + this._changeConnectionEnabled = false; + this._listDatabasesConnected = false; + this._updateTaskbar.fire(); + } + + public onConnectReject(): void { + this.onDisconnect(); + this._updateTaskbar.fire(); + } + + public onConnectSuccess(params?: INewConnectionParams): void { + this._runQueryEnabled = true; + this._connectEnabled = false; + this._disconnectEnabled = true; + this._changeConnectionEnabled = true; + this._listDatabasesConnected = true; + + let isRunningQuery = this._queryModelService.isRunningQuery(this.uri); + if (!isRunningQuery && params && params.runQueryOnCompletion) { + let selection: ISelectionData = params ? params.querySelection : undefined; + if (params.runQueryOnCompletion === RunQueryOnConnectionMode.executeCurrentQuery) { + this.runQueryStatement(selection); + } else if (params.runQueryOnCompletion === RunQueryOnConnectionMode.executeQuery) { + this.runQuery(selection); + } else if (params.runQueryOnCompletion === RunQueryOnConnectionMode.estimatedQueryPlan) { + this.runQuery(selection, { displayEstimatedQueryPlan: true }); + } + } + this._updateTaskbar.fire(); + } + + public onDisconnect(): void { + this._runQueryEnabled = true; + this._cancelQueryEnabled = false; + this._connectEnabled = true; + this._disconnectEnabled = false; + this._changeConnectionEnabled = false; + this._listDatabasesConnected = false; + this._updateTaskbar.fire(); + } + + public onRunQuery(): void { + this._runQueryEnabled = false; + this._cancelQueryEnabled = true; + this._updateTaskbar.fire(); + } + + public onQueryComplete(): void { + this._runQueryEnabled = true; + this._cancelQueryEnabled = false; + this._updateTaskbar.fire(); + } + + // Clean up functions + public dispose(): void { + this._queryModelService.disposeQuery(this.uri); + this._sql.dispose(); + this._results.dispose(); + this._toDispose = dispose(this._toDispose); + this._currentEventCallbacks = dispose(this._currentEventCallbacks); + super.dispose(); + } + + public close(): void { + this._queryEditorService.onQueryInputClosed(this.uri); + this._connectionManagementService.disconnectEditor(this, true); + + this._sql.close(); + this._results.close(); + super.close(); + } + + /** + * Unsubscribe all events in _currentEventCallbacks and set the new callbacks + * to be unsubscribed the next time this method is called. + * + * This method is used to ensure that all callbacks point to the current QueryEditor + * in the case that this QueryInput is moved between different QueryEditors. + */ + public setEventCallbacks(callbacks: IDisposable[]): void { + this._currentEventCallbacks = dispose(this._currentEventCallbacks); + this._currentEventCallbacks = callbacks; + } +} \ No newline at end of file diff --git a/src/sql/parts/query/common/queryManagement.ts b/src/sql/parts/query/common/queryManagement.ts new file mode 100644 index 0000000000..ebb0a0c8a5 --- /dev/null +++ b/src/sql/parts/query/common/queryManagement.ts @@ -0,0 +1,306 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import QueryRunner from 'sql/parts/query/execution/queryRunner'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import data = require('data'); +import { TPromise } from 'vs/base/common/winjs.base'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import * as TelemetryUtils from 'sql/common/telemetryUtilities'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +export const SERVICE_ID = 'queryManagementService'; + +export const IQueryManagementService = createDecorator(SERVICE_ID); + +export interface IQueryManagementService { + _serviceBrand: any; + + addQueryRequestHandler(queryType: string, runner: QueryRequestHandler): IDisposable; + registerRunner(runner: QueryRunner, uri: string): void; + + cancelQuery(ownerUri: string): Thenable; + runQuery(ownerUri: string, selection: data.ISelectionData, runOptions?: data.ExecutionPlanOptions): Thenable; + runQueryStatement(ownerUri: string, line: number, column: number): Thenable; + runQueryString(ownerUri: string, queryString: string): Thenable; + runQueryAndReturn(ownerUri: string, queryString: string): Thenable; + getQueryRows(rowData: data.QueryExecuteSubsetParams): Thenable; + disposeQuery(ownerUri: string): Thenable; + saveResults(requestParams: data.SaveResultsRequestParams): Thenable; + + // Callbacks + onQueryComplete(result: data.QueryExecuteCompleteNotificationResult): void; + onBatchStart(batchInfo: data.QueryExecuteBatchNotificationParams): void; + onBatchComplete(batchInfo: data.QueryExecuteBatchNotificationParams): void; + onResultSetComplete(resultSetInfo: data.QueryExecuteResultSetCompleteNotificationParams): void; + onMessage(message: data.QueryExecuteMessageParams): void; + + // Edit Data Callbacks + onEditSessionReady(ownerUri: string, success: boolean, message: string): void; + + // Edit Data Functions + initializeEdit(ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): Thenable; + disposeEdit(ownerUri: string): Thenable; + updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable; + commitEdit(ownerUri): Thenable; + createRow(ownerUri: string): Thenable; + deleteRow(ownerUri: string, rowId: number): Thenable; + revertCell(ownerUri: string, rowId: number, columnId: number): Thenable; + revertRow(ownerUri: string, rowId: number): Thenable; + getEditRows(rowData: data.EditSubsetParams): Thenable; +} + +/* + * An object that can handle basic request-response actions related to queries + */ +export interface QueryRequestHandler { + cancelQuery(ownerUri: string): Thenable; + runQuery(ownerUri: string, selection: data.ISelectionData, runOptions?: data.ExecutionPlanOptions): Thenable; + runQueryStatement(ownerUri: string, line: number, column: number): Thenable; + runQueryString(ownerUri: string, queryString: string): Thenable; + runQueryAndReturn(ownerUri: string, queryString: string): Thenable; + getQueryRows(rowData: data.QueryExecuteSubsetParams): Thenable; + disposeQuery(ownerUri: string): Thenable; + saveResults(requestParams: data.SaveResultsRequestParams): Thenable; + + // Edit Data actions + initializeEdit(ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): Thenable; + disposeEdit(ownerUri: string): Thenable; + updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable; + commitEdit(ownerUri): Thenable; + createRow(ownerUri: string): Thenable; + deleteRow(ownerUri: string, rowId: number): Thenable; + revertCell(ownerUri: string, rowId: number, columnId: number): Thenable; + revertRow(ownerUri: string, rowId: number): Thenable; + getEditRows(rowData: data.EditSubsetParams): Thenable; +} + +export class QueryManagementService implements IQueryManagementService { + public static readonly DefaultQueryType = 'MSSQL'; + public _serviceBrand: any; + + private _requestHandlers = new Map(); + // public for testing only + public _queryRunners = new Map(); + + // public for testing only + public _handlerCallbackQueue: ((run: QueryRunner) => void)[] = []; + + constructor( + @IConnectionManagementService private _connectionService: IConnectionManagementService, + @ITelemetryService private _telemetryService: ITelemetryService + ) { + } + + // Registers queryRunners with their uris to distribute notifications. + // Ensures that notifications are handled in the correct order by handling + // enqueued handlers first. + // public for testing only + public registerRunner(runner: QueryRunner, uri: string): void { + // If enqueueOrRun was called before registerRunner for the current query, + // _handlerCallbackQueue will be non-empty. Run all handlers in the queue first + // so that notifications are handled in order they arrived + while (this._handlerCallbackQueue.length > 0) { + let handler = this._handlerCallbackQueue.shift(); + handler(runner); + } + + // Set the runner for any other handlers if the runner is in use by the + // current query or a subsequent query + if (!runner.hasCompleted) { + this._queryRunners.set(uri, runner); + } + } + + // Handles logic to run the given handlerCallback at the appropriate time. If the given runner is + // undefined, the handlerCallback is put on the _handlerCallbackQueue to be run once the runner is set + // public for testing only + private enqueueOrRun(handlerCallback: (runnerParam: QueryRunner) => void, runner: QueryRunner): void { + if (runner === undefined) { + this._handlerCallbackQueue.push(handlerCallback); + } else { + handlerCallback(runner); + } + } + + private _notify(ownerUri: string, sendNotification: (runner: QueryRunner) => void): void { + let runner = this._queryRunners.get(ownerUri); + this.enqueueOrRun(sendNotification, runner); + } + + public addQueryRequestHandler(queryType: string, handler: QueryRequestHandler): IDisposable { + this._requestHandlers.set(queryType, handler); + + return { + dispose: () => { + } + }; + } + + private addTelemetry(eventName: string, ownerUri: string, runOptions?: data.ExecutionPlanOptions): void { + let providerId: string = this._connectionService.getProviderIdFromUri(ownerUri); + let data: TelemetryUtils.IConnectionTelemetryData = { + provider: providerId, + }; + if (runOptions) { + data = Object.assign({}, data, { + displayEstimatedQueryPlan: runOptions.displayEstimatedQueryPlan, + displayActualQueryPlan: runOptions.displayActualQueryPlan + }); + } + TelemetryUtils.addTelemetry(this._telemetryService, eventName, data); + } + + private _runAction(uri: string, action: (handler: QueryRequestHandler) => Thenable): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(uri); + + if (!providerId) { + return TPromise.wrapError(new Error('Connection is required in order to interact with queries')); + } + let handler = this._requestHandlers.get(providerId); + if (handler) { + return action(handler); + } else { + return TPromise.wrapError(new Error('No Handler Registered')); + } + } + + public cancelQuery(ownerUri: string): Thenable { + this.addTelemetry(TelemetryKeys.CancelQuery, ownerUri); + return this._runAction(ownerUri, (runner) => { + return runner.cancelQuery(ownerUri); + }); + } + public runQuery(ownerUri: string, selection: data.ISelectionData, runOptions?: data.ExecutionPlanOptions): Thenable { + this.addTelemetry(TelemetryKeys.RunQuery, ownerUri, runOptions); + return this._runAction(ownerUri, (runner) => { + return runner.runQuery(ownerUri, selection, runOptions); + }); + } + public runQueryStatement(ownerUri: string, line: number, column: number): Thenable { + this.addTelemetry(TelemetryKeys.RunQueryStatement, ownerUri); + return this._runAction(ownerUri, (runner) => { + return runner.runQueryStatement(ownerUri, line, column); + }); + } + public runQueryString(ownerUri: string, queryString: string): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.runQueryString(ownerUri, queryString); + }); + } + public runQueryAndReturn(ownerUri: string, queryString: string): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.runQueryAndReturn(ownerUri, queryString); + }); + } + public getQueryRows(rowData: data.QueryExecuteSubsetParams): Thenable { + return this._runAction(rowData.ownerUri, (runner) => { + return runner.getQueryRows(rowData); + }); + } + public disposeQuery(ownerUri: string): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.disposeQuery(ownerUri); + }); + } + + public saveResults(requestParams: data.SaveResultsRequestParams): Thenable { + return this._runAction(requestParams.ownerUri, (runner) => { + return runner.saveResults(requestParams); + }); + } + + public onQueryComplete(result: data.QueryExecuteCompleteNotificationResult): void { + this._notify(result.ownerUri, (runner: QueryRunner) => { + runner.handleQueryComplete(result); + }); + } + public onBatchStart(batchInfo: data.QueryExecuteBatchNotificationParams): void { + this._notify(batchInfo.ownerUri, (runner: QueryRunner) => { + runner.handleBatchStart(batchInfo); + }); + } + + public onBatchComplete(batchInfo: data.QueryExecuteBatchNotificationParams): void { + this._notify(batchInfo.ownerUri, (runner: QueryRunner) => { + runner.handleBatchComplete(batchInfo); + }); + } + + public onResultSetComplete(resultSetInfo: data.QueryExecuteResultSetCompleteNotificationParams): void { + this._notify(resultSetInfo.ownerUri, (runner: QueryRunner) => { + runner.handleResultSetComplete(resultSetInfo); + }); + } + + public onMessage(message: data.QueryExecuteMessageParams): void { + this._notify(message.ownerUri, (runner: QueryRunner) => { + runner.handleMessage(message); + }); + } + + // Edit Data Functions + public initializeEdit(ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.initializeEdit(ownerUri, schemaName, objectName, objectType, rowLimit); + }); + } + + public onEditSessionReady(ownerUri: string, success: boolean, message: string): void { + this._notify(ownerUri, (runner: QueryRunner) => { + runner.handleEditSessionReady(ownerUri, success, message); + }); + } + + public updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.updateCell(ownerUri, rowId, columnId, newValue); + }); + } + + public commitEdit(ownerUri: string): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.commitEdit(ownerUri); + }); + } + + public createRow(ownerUri: string): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.createRow(ownerUri); + }); + } + + public deleteRow(ownerUri: string, rowId: number): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.deleteRow(ownerUri, rowId); + }); + } + + public disposeEdit(ownerUri: string): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.disposeEdit(ownerUri); + }); + } + + public revertCell(ownerUri: string, rowId: number, columnId: number): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.revertCell(ownerUri, rowId, columnId); + }); + } + + public revertRow(ownerUri: string, rowId: number): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.revertRow(ownerUri, rowId); + }); + } + + public getEditRows(rowData: data.EditSubsetParams): Thenable { + return this._runAction(rowData.ownerUri, (runner) => { + return runner.getEditRows(rowData); + }); + } +} \ No newline at end of file diff --git a/src/sql/parts/query/common/queryResultsInput.ts b/src/sql/parts/query/common/queryResultsInput.ts new file mode 100644 index 0000000000..d1102a60e9 --- /dev/null +++ b/src/sql/parts/query/common/queryResultsInput.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput } from 'vs/workbench/common/editor'; + +/** + * Input for the QueryResultsEditor. This input helps with logic for the viewing and editing of + * data in the results grid. + */ +export class QueryResultsInput extends EditorInput { + + // Tracks if the editor that holds this input should be visible (i.e. true if a query has been run) + private _visible: boolean; + + // Tracks if the editor has holds this input has has bootstrapped angular yet + private _hasBootstrapped: boolean; + + // Holds the HTML content for the editor when the editor discards this input and loads another + private _editorContainer: HTMLElement; + + constructor(private _uri: string) { + super(); + this._visible = false; + this._hasBootstrapped = false; + } + + getTypeId(): string { + return QueryResultsInput.ID; + } + + getName(): string { + return localize('extensionsInputName', 'Extension'); + } + + matches(other: any): boolean { + if (other instanceof QueryResultsInput) { + return (other._uri === this._uri); + } + + return false; + } + + resolve(refresh?: boolean): TPromise { + return TPromise.as(null); + } + + supportsSplitEditor(): boolean { + return false; + } + + public setVisibleTrue(): void { + this._visible = true; + } + + public setBootstrappedTrue(): void { + this._hasBootstrapped = true; + } + + public dispose(): void { + this._disposeContainer(); + super.dispose(); + } + + private _disposeContainer() { + if (!this._editorContainer) { + return; + } + + let parentContainer = this._editorContainer.parentNode; + if (parentContainer) { + parentContainer.removeChild(this._editorContainer); + this._editorContainer = null; + } + } + + //// Properties + + static get ID() { + return 'workbench.query.queryResultsInput'; + } + + set container(container: HTMLElement) { + this._disposeContainer(); + this._editorContainer = container; + } + + get container(): HTMLElement { + return this._editorContainer; + } + + get hasBootstrapped(): boolean { + return this._hasBootstrapped; + } + + get visible(): boolean { + return this._visible; + } + + get uri(): string { + return this._uri; + } + +} \ No newline at end of file diff --git a/src/sql/parts/query/common/resultSerializer.ts b/src/sql/parts/query/common/resultSerializer.ts new file mode 100644 index 0000000000..1452d52513 --- /dev/null +++ b/src/sql/parts/query/common/resultSerializer.ts @@ -0,0 +1,300 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ConnectionConstants from 'sql/parts/connection/common/constants'; +import * as Constants from 'sql/parts/query/common/constants'; +import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; +import { SaveResultsRequestParams } from 'data'; +import { IQueryManagementService } from 'sql/parts/query/common/queryManagement'; +import { ISaveRequest, SaveFormat } from 'sql/parts/grid/common/interfaces'; +import * as PathUtilities from 'sql/common/pathUtilities'; + +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IOutputService, IOutputChannel, IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/parts/output/common/output'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { Registry } from 'vs/platform/registry/common/platform'; +import URI from 'vs/base/common/uri'; +import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import * as paths from 'vs/base/common/paths'; +import * as nls from 'vs/nls'; +import * as pretty from 'pretty-data'; + +import { ISlickRange } from 'angular2-slickgrid'; +import * as path from 'path'; + +/** + * Handles save results request from the context menu of slickGrid + */ +export class ResultSerializer { + public static tempFileCount: number = 1; + private static MAX_FILENAMES = 100; + + private _uri: string; + private _filePath: string; + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService, + @IMessageService private _messageService: IMessageService, + @IOutputService private _outputService: IOutputService, + @IQueryManagementService private _queryManagementService: IQueryManagementService, + @IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IWorkspaceContextService private _contextService: IWorkspaceContextService, + @IWindowsService private _windowsService: IWindowsService, + @IWindowService private _windowService: IWindowService, + @IUntitledEditorService private _untitledEditorService: IUntitledEditorService + ) { } + + /** + * Handle save request by getting filename from user and sending request to service + */ + public saveResults(uri: string, saveRequest: ISaveRequest): Thenable { + const self = this; + this._uri = uri; + + // prompt for filepath + let filePath = self.promptForFilepath(saveRequest); + if (filePath) { + return self.sendRequestToService(filePath, saveRequest.batchIndex, saveRequest.resultSetNumber, saveRequest.format, saveRequest.selection ? saveRequest.selection[0] : undefined); + } + return Promise.resolve(undefined); + } + + /** + * Open a xml/json link - Opens the content in a new editor pane + */ + public openLink(content: string, columnName: string, linkType: string): void { + let fileMode: string = undefined; + let fileUri = this.getUntitledFileUri(columnName); + + if (linkType === SaveFormat.XML) { + fileMode = SaveFormat.XML; + try { + content = pretty.pd.xml(content); + } catch (e) { + // If Xml fails to parse, fall back on original Xml content + } + } else if (linkType === SaveFormat.JSON) { + let jsonContent: string = undefined; + fileMode = SaveFormat.JSON; + try { + jsonContent = JSON.parse(content); + } catch (e) { + // If Json fails to parse, fall back on original Json content + } + if (jsonContent) { + // If Json content was valid and parsed, pretty print content to a string + content = JSON.stringify(jsonContent, undefined, 4); + } + } + + this.openUntitledFile(fileMode, content, fileUri); + } + + private getUntitledFileUri(columnName: string): URI { + let fileName = columnName; + + let uri: URI = URI.from({ scheme: UNTITLED_SCHEMA, path: fileName }); + + // If the current filename is taken, try another up to a max number + if (this._untitledEditorService.exists(uri)) { + let i = 1; + while (i < ResultSerializer.MAX_FILENAMES + && this._untitledEditorService.exists(uri)) { + fileName = [columnName, i.toString()].join('-'); + uri = URI.from({ scheme: UNTITLED_SCHEMA, path: fileName }); + i++; + } + if (this._untitledEditorService.exists(uri)) { + // If this fails, return undefined and let the system figure out the right name + uri = undefined; + } + } + return uri; + } + + private ensureOutputChannelExists(): void { + Registry.as(OutputExtensions.OutputChannels) + .registerChannel(ConnectionConstants.outputChannelName, ConnectionConstants.outputChannelName); + } + + private get outputChannel(): IOutputChannel { + this.ensureOutputChannelExists(); + return this._outputService.getChannel(ConnectionConstants.outputChannelName); + } + + private get rootPath(): string { + return PathUtilities.getRootPath(this._contextService); + } + + private logToOutputChannel(message: string): void { + this.outputChannel.append(message); + } + + private promptForFilepath(saveRequest: ISaveRequest): string { + let filepathPlaceHolder = PathUtilities.resolveCurrentDirectory(this._uri, this.rootPath); + filepathPlaceHolder = path.join(filepathPlaceHolder, this.getResultsDefaultFilename(saveRequest)); + + let filePath: string = this._windowService.showSaveDialog({ + title: nls.localize('saveAsFileTitle', 'Choose Results File'), + defaultPath: paths.normalize(filepathPlaceHolder, true) + }); + return filePath; + } + + private getResultsDefaultFilename(saveRequest: ISaveRequest): string { + let fileName = 'Results'; + switch (saveRequest.format) { + case SaveFormat.CSV: + fileName = fileName + '.csv'; + break; + case SaveFormat.JSON: + fileName = fileName + '.json'; + break; + case SaveFormat.EXCEL: + fileName = fileName + '.xlsx'; + break; + case SaveFormat.XML: + fileName = fileName + '.xml'; + break; + default: + fileName = fileName + '.txt'; + } + return fileName; + } + + private getConfigForCsv(): SaveResultsRequestParams { + let saveResultsParams = { resultFormat: SaveFormat.CSV as string }; + + // get save results config from vscode config + let saveConfig = WorkbenchUtils.getSqlConfigSection(this._workspaceConfigurationService, Constants.configSaveAsCsv); + // if user entered config, set options + if (saveConfig) { + if (saveConfig.includeHeaders !== undefined) { + saveResultsParams.includeHeaders = saveConfig.includeHeaders; + } + } + return saveResultsParams; + } + + private getConfigForJson(): SaveResultsRequestParams { + // JSON does not currently have special conditions + let saveResultsParams = { resultFormat: SaveFormat.JSON as string }; + return saveResultsParams; + } + + private getConfigForExcel(): SaveResultsRequestParams { + // get save results config from vscode config + // Note: we are currently using the configSaveAsCsv setting since it has the option mssql.saveAsCsv.includeHeaders + // and we want to have just 1 setting that lists this. + let config = this.getConfigForCsv(); + config.resultFormat = SaveFormat.EXCEL; + return config; + } + + private getParameters(filePath: string, batchIndex: number, resultSetNo: number, format: string, selection: ISlickRange): SaveResultsRequestParams { + let saveResultsParams: SaveResultsRequestParams; + if (!path.isAbsolute(filePath)) { + this._filePath = PathUtilities.resolveFilePath(this._uri, filePath, this.rootPath); + } else { + this._filePath = filePath; + } + + if (format === SaveFormat.CSV) { + saveResultsParams = this.getConfigForCsv(); + } else if (format === SaveFormat.JSON) { + saveResultsParams = this.getConfigForJson(); + } else if (format === SaveFormat.EXCEL) { + saveResultsParams = this.getConfigForExcel(); + } + + saveResultsParams.filePath = this._filePath; + saveResultsParams.ownerUri = this._uri; + saveResultsParams.resultSetIndex = resultSetNo; + saveResultsParams.batchIndex = batchIndex; + if (this.isSelected(selection)) { + saveResultsParams.rowStartIndex = selection.fromRow; + saveResultsParams.rowEndIndex = selection.toRow; + saveResultsParams.columnStartIndex = selection.fromCell; + saveResultsParams.columnEndIndex = selection.toCell; + } + return saveResultsParams; + } + + /** + * Check if a range of cells were selected. + */ + private isSelected(selection: ISlickRange): boolean { + return (selection && !((selection.fromCell === selection.toCell) && (selection.fromRow === selection.toRow))); + } + + /** + * Send request to sql tools service to save a result set + */ + private sendRequestToService(filePath: string, batchIndex: number, resultSetNo: number, format: string, selection: ISlickRange): Thenable { + let saveResultsParams = this.getParameters(filePath, batchIndex, resultSetNo, format, selection); + + this.logToOutputChannel(LocalizedConstants.msgSaveStarted + this._filePath); + + // send message to the sqlserverclient for converting results to the requested format and saving to filepath + return this._queryManagementService.saveResults(saveResultsParams).then(result => { + if (result.messages) { + this._messageService.show(Severity.Error, LocalizedConstants.msgSaveFailed + result.messages); + this.logToOutputChannel(LocalizedConstants.msgSaveFailed + result.messages); + } else { + this._messageService.show(Severity.Info, LocalizedConstants.msgSaveSucceeded + this._filePath); + this.logToOutputChannel(LocalizedConstants.msgSaveSucceeded + filePath); + this.openSavedFile(this._filePath, format); + } + // TODO telemetry for save results + // Telemetry.sendTelemetryEvent('SavedResults', { 'type': format }); + + }, error => { + this._messageService.show(Severity.Error, LocalizedConstants.msgSaveFailed + error); + this.logToOutputChannel(LocalizedConstants.msgSaveFailed + error); + }); + } + + /** + * Open the saved file in a new vscode editor pane + */ + private openSavedFile(filePath: string, format: string): void { + if (format === SaveFormat.EXCEL) { + // This will not open in VSCode as it's treated as binary. Use the native file opener instead + // Note: must use filePath here, URI does not open correctly + // TODO see if there is an alternative opener that includes error handling + let fileUri = URI.from({ scheme: PathUtilities.FILE_SCHEMA, path: filePath }); + this._windowsService.openExternal(fileUri.toString()); + } else { + let uri = URI.file(filePath); + this._editorService.openEditor({ resource: uri }).then((result) => { + + }, (error: any) => { + this._messageService.show(Severity.Error, error); + }); + } + } + + /** + * Open the saved file in a new vscode editor pane + */ + private openUntitledFile(fileMode: string, contents: string, fileUri: URI = undefined): void { + const input = this._untitledEditorService.createOrGet(fileUri, fileMode, contents); + + this._editorService.openEditor(input, { pinned: true }) + .then( + (success) => { + }, + (error: any) => { + this._messageService.show(Severity.Error, error); + } + ); + } +} diff --git a/src/sql/parts/query/editor/editorDescriptorService.ts b/src/sql/parts/query/editor/editorDescriptorService.ts new file mode 100644 index 0000000000..147476c055 --- /dev/null +++ b/src/sql/parts/query/editor/editorDescriptorService.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { EditorInput, Extensions, IEditorRegistry, IEditorDescriptor } from 'vs/workbench/common/editor'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export interface IEditorDescriptorService { + getEditor(input: EditorInput): IEditorDescriptor; +} + +export class EditorDescriptorService implements IEditorDescriptorService { + + constructor() { + } + + public getEditor(input: EditorInput): IEditorDescriptor { + return Registry.as(Extensions.Editors).getEditor(input); + } +} + +export const SERVICE_ID = 'editorDescriptorService'; + +export const IEditorDescriptorService = createDecorator(SERVICE_ID); \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/binarydiffeditor.css b/src/sql/parts/query/editor/media/binarydiffeditor.css new file mode 100644 index 0000000000..976880f0c6 --- /dev/null +++ b/src/sql/parts/query/editor/media/binarydiffeditor.css @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .binarydiff-left { + float: left; +} + +.monaco-workbench .binarydiff-right { + border-left: 3px solid #DDD; +} + +.vs-dark .monaco-workbench .binarydiff-right { + border-left: 3px solid rgb(20, 20, 20); +} + +.hc-black .monaco-workbench .binarydiff-right { + border-left: 3px solid #6FC3DF; +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/close-dirty-inverse.svg b/src/sql/parts/query/editor/media/close-dirty-inverse.svg new file mode 100644 index 0000000000..02dafab76f --- /dev/null +++ b/src/sql/parts/query/editor/media/close-dirty-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/close-dirty.svg b/src/sql/parts/query/editor/media/close-dirty.svg new file mode 100644 index 0000000000..409e5fa539 --- /dev/null +++ b/src/sql/parts/query/editor/media/close-dirty.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/close-inverse.svg b/src/sql/parts/query/editor/media/close-inverse.svg new file mode 100644 index 0000000000..751e89b3b0 --- /dev/null +++ b/src/sql/parts/query/editor/media/close-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/close.svg b/src/sql/parts/query/editor/media/close.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/sql/parts/query/editor/media/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/editorGroupsControl.css b/src/sql/parts/query/editor/media/editorGroupsControl.css new file mode 100644 index 0000000000..5a2cc4d9cd --- /dev/null +++ b/src/sql/parts/query/editor/media/editorGroupsControl.css @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.vs .monaco-workbench > .editor > .content.drag { + background-color: #ECECEC; +} + +.vs-dark .monaco-workbench > .editor > .content.drag { + background-color: #2D2D2D; +} + +.monaco-workbench > .editor > .content.dragging > .monaco-sash { + display: none; /* hide sashes while dragging editors around */ +} + +#monaco-workbench-editor-move-overlay, +#monaco-workbench-editor-drop-overlay { + position: absolute; + left: 0; + width: 100%; + z-index: 10000; +} + +#monaco-workbench-editor-drop-overlay { + opacity: 0; /* initially not visible until moving around */ +} + +.vs #monaco-workbench-editor-drop-overlay, +.vs .monaco-workbench > .editor.empty > .content.dropfeedback { + background-color: rgba(51,153,255, 0.18); +} + +.vs-dark #monaco-workbench-editor-drop-overlay, +.vs-dark .monaco-workbench > .editor.empty > .content.dropfeedback { + background-color: rgba(83, 89, 93, 0.5); +} + +.hc-black #monaco-workbench-editor-drop-overlay, +.hc-black .monaco-workbench > .editor.empty > .content.dropfeedback { + background: none !important; + outline: 2px dashed #f38518; + outline-offset: -2px; +} + +.monaco-workbench > .editor > .content > .one-editor-silo { + position: absolute; + box-sizing: border-box; /* use border box to be able to draw a border as separator between editors */ +} + +.monaco-workbench > .editor > .content > .one-editor-silo.editor-one { + left: 0; + top: 0; +} + +.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three { + right: 0; +} + +.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three { + bottom: 0; +} + +.monaco-workbench > .editor > .content > .one-editor-silo.dragging { + z-index: 70; + box-sizing: content-box; +} + +.vs .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.dragging { + border-left: 1px solid #E7E7E7; + border-right: 1px solid #E7E7E7; +} + +.vs .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.dragging { + border-top: 1px solid #E7E7E7; + border-bottom: 1px solid #E7E7E7; +} + +.vs .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-two, +.vs .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three { + border-left: 1px solid #E7E7E7; +} + +.vs .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-two, +.vs .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three { + border-top: 1px solid #E7E7E7; +} + +.vs-dark .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.dragging { + border-left: 1px solid #444; + border-right: 1px solid #444; +} + +.vs-dark .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.dragging { + border-top: 1px solid #444; + border-bottom: 1px solid #444; +} + +.vs-dark .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-two, +.vs-dark .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three { + border-left: 1px solid #444; +} + +.vs-dark .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-two, +.vs-dark .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three { + border-top: 1px solid #444; +} + +.hc-black .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.dragging { + border-left: 1px solid #6FC3DF; + border-right: 1px solid #6FC3DF; +} + +.hc-black .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.dragging { + border-top: 1px solid #6FC3DF; + border-bottom: 1px solid #6FC3DF; +} + +.hc-black .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-two, +.hc-black .monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three { + border-left: 1px solid #6FC3DF; +} + +.hc-black .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-two, +.hc-black .monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three { + border-top: 1px solid #6FC3DF; +} + +.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.draggedunder { + transition: left 200ms ease-out; +} + +.monaco-workbench > .editor > .content.vertical-layout > .editor-three.draggedunder { + transition-property: right; +} + +.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.draggedunder { + transition: top 200ms ease-out; +} + +.monaco-workbench > .editor > .content.horizontal-layout > .editor-three.draggedunder { + transition-property: bottom; +} + +.monaco-workbench > .editor > .content > .one-editor-silo > .container { + height: 100%; +} + +.monaco-workbench > .editor > .content > .one-editor-silo > .container > .editor-container { + height: calc(100% - 35px); /* Editor is below editor title */ +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/editorpart.css b/src/sql/parts/query/editor/media/editorpart.css new file mode 100644 index 0000000000..9beb3c5442 --- /dev/null +++ b/src/sql/parts/query/editor/media/editorpart.css @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.vs .monaco-workbench .monaco-editor-background { + background-color: white; +} + +.vs-dark .monaco-workbench .monaco-editor-background { + background-color: #1E1E1E; +} + +.hc-black .monaco-workbench .monaco-editor-background { + background-color: #000; +} + +.monaco-workbench .part.editor { + background-repeat: no-repeat; + background-position: 50% 50%; +} + +.monaco-workbench .part.editor.empty { + background-image: url('letterpress.svg'); +} + +.vs-dark .monaco-workbench .part.editor.empty { + background-image: url('letterpress-dark.svg'); +} + +.hc-black .monaco-workbench .part.editor.empty { + background-image: url('letterpress-hc.svg'); +} + +@media +(-webkit-min-device-pixel-ratio: 2), +(min-resolution: 192dppx) { + .monaco-workbench .part.editor { + background-size: 260px 260px; + } +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/editorpicker.css b/src/sql/parts/query/editor/media/editorpicker.css new file mode 100644 index 0000000000..c02d6e8c0a --- /dev/null +++ b/src/sql/parts/query/editor/media/editorpicker.css @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry.editor-preview { + font-style: italic; +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/editorstatus.css b/src/sql/parts/query/editor/media/editorstatus.css new file mode 100644 index 0000000000..b889f317b8 --- /dev/null +++ b/src/sql/parts/query/editor/media/editorstatus.css @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .editor-statusbar-item > a:not(:first-child) { + margin-left: 5px; +} + +.monaco-workbench .editor-statusbar-item > .editor-status-mode, +.monaco-workbench .editor-statusbar-item > .editor-status-encoding, +.monaco-workbench .editor-statusbar-item > .editor-status-eol, +.monaco-workbench .editor-statusbar-item > .editor-status-selection, +.monaco-workbench .editor-statusbar-item > .editor-status-indentation, +.monaco-workbench .editor-statusbar-item > .editor-status-metadata { + padding: 0 5px 0 5px; +} + +.monaco-workbench .editor-statusbar-item > .editor-status-metadata { + cursor: default; +} + +.monaco-workbench .editor-statusbar-item > .editor-status-tabfocusmode { + padding: 0 5px 0 5px; + background-color: brown !important; +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/letterpress-dark.svg b/src/sql/parts/query/editor/media/letterpress-dark.svg new file mode 100644 index 0000000000..5cd4a656db --- /dev/null +++ b/src/sql/parts/query/editor/media/letterpress-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/letterpress-hc.svg b/src/sql/parts/query/editor/media/letterpress-hc.svg new file mode 100644 index 0000000000..38b46ee704 --- /dev/null +++ b/src/sql/parts/query/editor/media/letterpress-hc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/letterpress.svg b/src/sql/parts/query/editor/media/letterpress.svg new file mode 100644 index 0000000000..41a76de75b --- /dev/null +++ b/src/sql/parts/query/editor/media/letterpress.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/next-diff-inverse.svg b/src/sql/parts/query/editor/media/next-diff-inverse.svg new file mode 100644 index 0000000000..16a6dc9063 --- /dev/null +++ b/src/sql/parts/query/editor/media/next-diff-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/next-diff.svg b/src/sql/parts/query/editor/media/next-diff.svg new file mode 100644 index 0000000000..8befbea7ba --- /dev/null +++ b/src/sql/parts/query/editor/media/next-diff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/notabstitle.css b/src/sql/parts/query/editor/media/notabstitle.css new file mode 100644 index 0000000000..deec5a1e4d --- /dev/null +++ b/src/sql/parts/query/editor/media/notabstitle.css @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Title Label */ + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label { + line-height: 35px; + overflow: hidden; + text-overflow: ellipsis; + position: relative; + padding-left: 20px; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .monaco-icon-label::before { + height: 35px; /* tweak the icon size of the editor labels when icons are enabled */ +} + +/* Title Actions */ +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions { + display: flex; + flex: initial; + opacity: 0.5; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .title-actions { + opacity: 1; +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action { + background: url('close-dirty.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action, +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action { + background: url('close-dirty-inverse.svg') center center no-repeat; +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action:hover { + background: url('close.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action:hover, +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action:hover { + background: url('close-inverse.svg') center center no-repeat; +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/parseQuery.svg b/src/sql/parts/query/editor/media/parseQuery.svg new file mode 100644 index 0000000000..8237d08f43 --- /dev/null +++ b/src/sql/parts/query/editor/media/parseQuery.svg @@ -0,0 +1 @@ +ParseQuery_16x \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/previous-diff-inverse.svg b/src/sql/parts/query/editor/media/previous-diff-inverse.svg new file mode 100644 index 0000000000..db3d321841 --- /dev/null +++ b/src/sql/parts/query/editor/media/previous-diff-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/previous-diff.svg b/src/sql/parts/query/editor/media/previous-diff.svg new file mode 100644 index 0000000000..508d57b2f1 --- /dev/null +++ b/src/sql/parts/query/editor/media/previous-diff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/queryEditor.css b/src/sql/parts/query/editor/media/queryEditor.css new file mode 100644 index 0000000000..bb492783f6 --- /dev/null +++ b/src/sql/parts/query/editor/media/queryEditor.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. + *--------------------------------------------------------------------------------------------*/ + +.vs-dark .side-by-side-editor > .master-editor-container { + box-shadow: -6px 0 5px -5px black; +} + +.side-by-side-editor > .master-editor-container { + box-shadow: -6px 0 5px -5px #DDD; +} + +.vs-dark .side-by-side-editor > .master-editor-container-horizontal { + box-shadow: 0 -6px 5px -5px black; +} + +.side-by-side-editor > .master-editor-container-horizontal { + box-shadow: 0 -6px 5px -5px #DDD; +} + +.editDataEditor { + height: inherit +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/runWithoutDebug.svg b/src/sql/parts/query/editor/media/runWithoutDebug.svg new file mode 100644 index 0000000000..e223af6c57 --- /dev/null +++ b/src/sql/parts/query/editor/media/runWithoutDebug.svg @@ -0,0 +1 @@ +StartWithoutDebug@2x \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/sidebysideEditor.css b/src/sql/parts/query/editor/media/sidebysideEditor.css new file mode 100644 index 0000000000..8dc1e4e111 --- /dev/null +++ b/src/sql/parts/query/editor/media/sidebysideEditor.css @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.vs-dark .side-by-side-editor > .master-editor-container { + box-shadow: -6px 0 5px -5px black; +} + +.side-by-side-editor > .master-editor-container { + box-shadow: -6px 0 5px -5px #DDD; +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/split-editor-horizontal-inverse.svg b/src/sql/parts/query/editor/media/split-editor-horizontal-inverse.svg new file mode 100644 index 0000000000..4969d2e785 --- /dev/null +++ b/src/sql/parts/query/editor/media/split-editor-horizontal-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/split-editor-horizontal.svg b/src/sql/parts/query/editor/media/split-editor-horizontal.svg new file mode 100644 index 0000000000..c307f0142b --- /dev/null +++ b/src/sql/parts/query/editor/media/split-editor-horizontal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/split-editor-vertical-inverse.svg b/src/sql/parts/query/editor/media/split-editor-vertical-inverse.svg new file mode 100644 index 0000000000..4eab753669 --- /dev/null +++ b/src/sql/parts/query/editor/media/split-editor-vertical-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/split-editor-vertical.svg b/src/sql/parts/query/editor/media/split-editor-vertical.svg new file mode 100644 index 0000000000..3eeaf7c536 --- /dev/null +++ b/src/sql/parts/query/editor/media/split-editor-vertical.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/stackview-inverse.svg b/src/sql/parts/query/editor/media/stackview-inverse.svg new file mode 100644 index 0000000000..8d9a603ae8 --- /dev/null +++ b/src/sql/parts/query/editor/media/stackview-inverse.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/sql/parts/query/editor/media/stackview.svg b/src/sql/parts/query/editor/media/stackview.svg new file mode 100644 index 0000000000..9ea38002d9 --- /dev/null +++ b/src/sql/parts/query/editor/media/stackview.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/sql/parts/query/editor/media/tabstitle.css b/src/sql/parts/query/editor/media/tabstitle.css new file mode 100644 index 0000000000..c7ae079e16 --- /dev/null +++ b/src/sql/parts/query/editor/media/tabstitle.css @@ -0,0 +1,220 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Title Container */ + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.tabs { + background: #F3F3F3; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.tabs { + background: #252526; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.tabs > .monaco-scrollable-element { + flex: 1; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.tabs > .monaco-scrollable-element .scrollbar { + z-index: 3; /* on top of tabs */ + cursor: default; +} + +/* Tabs Container */ + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container { + display: flex; + height: 35px; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container.scroll { + overflow: scroll !important; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container::-webkit-scrollbar { + display: none; +} + +/* Tab */ + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab { + display: flex; + width: 120px; + min-width: fit-content; + white-space: nowrap; + cursor: pointer; + height: 35px; + box-sizing: border-box; + border: 1px solid transparent; + padding-left: 10px; +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:not(.active) { + background-color: #ECECEC; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:not(.active) { + background-color: #2D2D2D; +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab { + border-left-color: #F3F3F3; +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active:last-child { + border-right-color: #F3F3F3; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab { + border-left-color: #252526; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active:last-child { + border-right-color: #252526; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:first-child, +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:first-child { + border-left-color: transparent; +} + +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab { + border-left-color: #6FC3DF; +} + +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active { + outline: 2px solid #f38518; + outline-offset: -1px; +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container.dropfeedback, +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dropfeedback { + background-color: #DDECFF; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container.dropfeedback, +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dropfeedback { + background-color: #383B3D; +} + +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container.dropfeedback, +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dropfeedback { + background: none !important; + outline: 2px dashed #f38518; + outline-offset: -2px; +} + +/* Tab Label */ + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label { + margin-top: auto; + margin-bottom: auto; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .monaco-icon-label::before { + height: 16px; /* tweak the icon size of the editor labels when icons are enabled */ +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label { + opacity: 0.7 !important; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label { + opacity: 0.5 !important; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active .tab-label, +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dropfeedback .tab-label { + opacity: 1 !important; +} + +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label { + opacity: 1 !important; +} + +/* Tab Close */ + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab > .tab-close { + margin-top: auto; + margin-bottom: auto; + width: 28px; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button > .tab-close { + display: none; /* hide the close action bar when we are configured to hide it */ +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.active > .tab-close .action-label, /* always show it for active tab */ +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab > .tab-close .action-label:focus, /* always show it on focus */ +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab:hover > .tab-close .action-label, /* always show it on hover */ +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.active:hover > .tab-close .action-label, /* always show it on hover */ +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.dirty > .tab-close .action-label { /* always show it for dirty tabs */ + opacity: 1; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active > .tab-close .action-label, /* show dimmed for inactive group */ +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* show dimmed for inactive group */ +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty > .tab-close .action-label, /* show dimmed for inactive group */ +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:hover > .tab-close .action-label { /* show dimmed for inactive group */ + opacity: 0.5; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab > .tab-close .action-label { + opacity: 0; + display: block; + height: 16px; + width: 16px; + background-size: 16px; + background-position: center center; + background-repeat: no-repeat; + margin-right: 0.5em; +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action { + background: url('close-dirty.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action, +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action { + background: url('close-dirty-inverse.svg') center center no-repeat; +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover { + background: url('close.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover, +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover { + background: url('close-inverse.svg') center center no-repeat; +} + +/* No Tab Close Button */ + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button { + padding-right: 28px; /* make room for dirty indication when we are running without close button */ +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button.dirty { + background-repeat: no-repeat; + background-position-y: center; + background-position-x: calc(100% - 6px); /* to the right of the tab label */ +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button.dirty { + background-image: url('close-dirty.svg'); +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button.dirty, +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button.dirty { + background-image: url('close-dirty-inverse.svg'); +} + +/* Editor Actions */ + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions { + cursor: default; + flex: initial; + padding-left: 4px; +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/textdiffeditor.css b/src/sql/parts/query/editor/media/textdiffeditor.css new file mode 100644 index 0000000000..48300eef8b --- /dev/null +++ b/src/sql/parts/query/editor/media/textdiffeditor.css @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.vs .monaco-workbench .textdiff-editor-action.next { + background: url('next-diff.svg') center center no-repeat; +} + +.vs .monaco-workbench .textdiff-editor-action.previous { + background: url('previous-diff.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .textdiff-editor-action.next, +.hc-black .monaco-workbench .textdiff-editor-action.next { + background: url('next-diff-inverse.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .textdiff-editor-action.previous, +.hc-black .monaco-workbench .textdiff-editor-action.previous { + background: url('previous-diff-inverse.svg') center center no-repeat; +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/media/titlecontrol.css b/src/sql/parts/query/editor/media/titlecontrol.css new file mode 100644 index 0000000000..4e89271f38 --- /dev/null +++ b/src/sql/parts/query/editor/media/titlecontrol.css @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Editor Label */ + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label, +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label { + white-space: nowrap; + flex: 1; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a, +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a { + text-decoration: none; + font-size: 13px; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .monaco-icon-label::before, +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .monaco-icon-label::before, +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a, +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a, +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label span, +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label span { + cursor: pointer; +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a, +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a { + color: rgba(51, 51, 51, 0.5); +} + +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .title-label a, +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab .tab-label a { + color: #333333; +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a, +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a { + color: rgba(255, 255, 255, 0.5); +} + +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .title-label a, +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab .tab-label a { + color: white; +} + +/* Title Actions */ + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label, +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label { + display: block; + height: 35px; + line-height: 35px; + min-width: 28px; + background-size: 16px; + background-position: center center; + background-repeat: no-repeat; +} + +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label, +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label { + line-height: initial; +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label .label, +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label .label { + display: none; +} + +/* Drag Cursor */ +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo > .container > .title, +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo > .container > .title.tabs .scrollbar .slider, +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo > .container > .title .monaco-icon-label::before, +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo > .container > .title .title-label a, +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo > .container > .title .title-label span { + cursor: -webkit-grab; +} + +#monaco-workbench-editor-move-overlay, +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag, +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag > .container > .title, +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag > .container > .title.tabs .scrollbar .slider, +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag > .container > .title .monaco-icon-label::before, +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag > .container > .title .title-label a, +.monaco-workbench > .part.editor > .content.multiple-editors > .one-editor-silo.drag > .container > .title .title-label span { + cursor: -webkit-grabbing; +} + +/* Actions */ + +.monaco-workbench .close-editor-action { + background: url('close.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .close-editor-action, +.hc-black .monaco-workbench .close-editor-action { + background: url('close-inverse.svg') center center no-repeat; +} + +.monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action { + background: url('split-editor-vertical.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action, +.hc-black .monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action { + background: url('split-editor-vertical-inverse.svg') center center no-repeat; +} + +.monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action { + background: url('split-editor-horizontal.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action, +.hc-black .monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action { + background: url('split-editor-horizontal-inverse.svg') center center no-repeat; +} + +.monaco-workbench .show-group-editors-action { + background: url('stackview.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .show-group-editors-action, +.hc-black .monaco-workbench .show-group-editors-action { + background: url('stackview-inverse.svg') center center no-repeat; +} \ No newline at end of file diff --git a/src/sql/parts/query/editor/queryEditor.ts b/src/sql/parts/query/editor/queryEditor.ts new file mode 100644 index 0000000000..7faf1c975d --- /dev/null +++ b/src/sql/parts/query/editor/queryEditor.ts @@ -0,0 +1,855 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/parts/query/editor/media/queryEditor'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as strings from 'vs/base/common/strings'; +import * as DOM from 'vs/base/browser/dom'; +import { Builder, Dimension, withElementById } from 'vs/base/browser/builder'; + +import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; +import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IEditorControl, Position, IEditor } from 'vs/platform/editor/common/editor'; +import { VerticalFlexibleSash, HorizontalFlexibleSash, IFlexibleSash } from 'sql/parts/query/views/flexibleSash'; +import { Orientation } from 'vs/base/browser/ui/sash/sash'; + +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; + +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { ISelectionData } from 'data'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { CodeEditor } from 'vs/editor/browser/codeEditor'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IRange } from 'vs/editor/common/core/range'; + +import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; +import { QueryInput } from 'sql/parts/query/common/queryInput'; +import { QueryResultsEditor } from 'sql/parts/query/editor/queryResultsEditor'; +import * as queryContext from 'sql/parts/query/common/queryContext'; +import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar'; +import { ITextFileService, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { + RunQueryAction, CancelQueryAction, ListDatabasesAction, ListDatabasesActionItem, + ConnectDatabaseAction, ToggleConnectDatabaseAction, EstimatedQueryPlanAction +} from 'sql/parts/query/execution/queryActions'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService'; +import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { attachEditableDropdownStyler } from 'sql/common/theme/styler'; + +/** + * Editor that hosts 2 sub-editors: A TextResourceEditor for SQL file editing, and a QueryResultsEditor + * for viewing and editing query results. This editor is based off SideBySideEditor. + */ +export class QueryEditor extends BaseEditor { + + public static ID: string = 'workbench.editor.queryEditor'; + + // The height of the tabs above the editor + private readonly _tabHeight: number = 35; + + // The height of the taskbar above the editor + private readonly _taskbarHeight: number = 28; + + // The minimum width/height of the editors hosted in the QueryEditor + private readonly _minEditorSize: number = 220; + + private _sash: IFlexibleSash; + private _editorTopOffset: number; + private _orientation: Orientation; + private _dimension: Dimension; + + private _resultsEditor: QueryResultsEditor; + private _resultsEditorContainer: HTMLElement; + + private _sqlEditor: TextResourceEditor; + private _sqlEditorContainer: HTMLElement; + + private _taskbar: Taskbar; + private _taskbarContainer: HTMLElement; + private _listDatabasesActionItem: ListDatabasesActionItem; + + + private queryEditorVisible: IContextKey; + + private _runQueryAction: RunQueryAction; + private _cancelQueryAction: CancelQueryAction; + private _toggleConnectDatabaseAction: ToggleConnectDatabaseAction; + private _changeConnectionAction: ConnectDatabaseAction; + private _listDatabasesAction: ListDatabasesAction; + private _estimatedQueryPlanAction: EstimatedQueryPlanAction; + + constructor( + @ITelemetryService _telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IContextMenuService private _contextMenuService: IContextMenuService, + @IQueryModelService private _queryModelService: IQueryModelService, + @IEditorDescriptorService private _editorDescriptorService: IEditorDescriptorService, + @IEditorGroupService private _editorGroupService: IEditorGroupService, + @IContextKeyService contextKeyService: IContextKeyService, + @ITextFileService private _textFileService: ITextFileService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + editorOrientation?: Orientation + ) { + super(QueryEditor.ID, _telemetryService, themeService); + + if (editorOrientation) { + this._orientation = editorOrientation; + } else { + this._orientation = Orientation.HORIZONTAL; + } + + if (contextKeyService) { + this.queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService); + } + + if (this._textFileService && this._textFileService.models) { + this._textFileService.models.onModelSaved(event => this._onModelSaved(event)); + } + } + + // PROPERTIES ////////////////////////////////////////////////////////// + /** + * Returns the URI of this editor if it is connected. + * @returns {string} URI of the editor if connected, undefined otherwise + */ + public get connectedUri(): string { + return this._connectionManagementService.isConnected(this.uri) + ? this.uri + : undefined; + } + + /** + * Returns the URI of this editor if an input is associated with it + * @return {string} URI of this if input is associated, undefined otherwise + */ + get uri(): string { + let input: QueryInput = this.input; + return input + ? input.getQueryResultsInputResource() + : undefined; + } + + private _onModelSaved(event: TextFileModelChangeEvent): void { + if (event.resource.toString() !== this.uri) { + TaskUtilities.replaceConnection(this.uri, event.resource.toString(), this._connectionManagementService).then(result => { + if (result && result.connected) { + this.currentQueryInput.onConnectSuccess(); + } else { + this.currentQueryInput.onConnectReject(); + } + }); + } + } + + // PUBLIC METHODS //////////////////////////////////////////////////////////// + public get currentQueryInput(): QueryInput { + return this.input; + } + + /** + * Called to create the editor in the parent builder. + */ + public createEditor(parent: Builder): void { + const parentElement = parent.getHTMLElement(); + DOM.addClass(parentElement, 'side-by-side-editor'); + this._createTaskbar(parentElement); + } + + /** + * Sets the input data for this editor. + */ + public setInput(newInput: QueryInput, options?: EditorOptions): TPromise { + const oldInput = this.input; + + if (newInput.matches(oldInput)) { + return TPromise.as(undefined); + } + + // Make sure all event callbacks will be sent to this QueryEditor in the case that this QueryInput was moved from + // another QueryEditor + let taskbarCallback: IDisposable = newInput.updateTaskbarEvent(() => this._updateTaskbar()); + let showResultsCallback: IDisposable = newInput.showQueryResultsEditorEvent(() => this._showQueryResultsEditor()); + let selectionCallback: IDisposable = newInput.updateSelectionEvent((selection) => this._setSelection(selection)); + newInput.setEventCallbacks([taskbarCallback, showResultsCallback, selectionCallback]); + + return super.setInput(newInput, options) + .then(() => this._updateInput(oldInput, newInput, options)); + } + + /** + * Sets this editor and the 2 sub-editors to visible. + */ + public setEditorVisible(visible: boolean, position: Position): void { + if (this._resultsEditor) { + this._resultsEditor.setVisible(visible, position); + } + if (this._sqlEditor) { + this._sqlEditor.setVisible(visible, position); + } + super.setEditorVisible(visible, position); + + // Note: must update after calling super.setEditorVisible so that the accurate count is handled + this.updateQueryEditorVisible(visible); + } + + + private updateQueryEditorVisible(currentEditorIsVisible: boolean): void { + if (this.queryEditorVisible) { + let visible = currentEditorIsVisible; + if (!currentEditorIsVisible) { + // Current editor is closing but still tracked as visible. Check if any other editor is visible + const candidates = [...this._editorService.getVisibleEditors()].filter(e => { + if (e && e.getId) { + return e.getId() === QueryEditor.ID; + } + return false; + }); + // Note: require 2 or more candidates since current is closing but still + // counted as visible + visible = candidates.length > 1; + } + this.queryEditorVisible.set(visible); + } + } + + + /** + * Changes the position of the editor. + */ + public changePosition(position: Position): void { + if (this._resultsEditor) { + this._resultsEditor.changePosition(position); + } + if (this._sqlEditor) { + this._sqlEditor.changePosition(position); + } + super.changePosition(position); + } + + /** + * Called to indicate to the editor that the input should be cleared and resources associated with the + * input should be freed. + */ + public clearInput(): void { + if (this._resultsEditor) { + this._resultsEditor.clearInput(); + } + if (this._sqlEditor) { + this._sqlEditor.clearInput(); + } + this._disposeEditors(); + super.clearInput(); + } + + /** + * Sets focus on this editor. Specifically, it sets the focus on the hosted text editor. + */ + public focus(): void { + if (this._sqlEditor) { + this._sqlEditor.focus(); + } + } + + /** + * Updates the internal variable keeping track of the editor's size, and re-calculates the sash position. + * To be called when the container of this editor changes size. + */ + public layout(dimension: Dimension): void { + this._dimension = dimension; + + if (this._sash) { + this._setSashDimension(); + this.sash.layout(); + } + + this._doLayout(); + this._resizeGridContents(); + } + + /** + * Returns the editor control for the text editor. + */ + public getControl(): IEditorControl { + if (this._sqlEditor) { + return this._sqlEditor.getControl(); + } + return null; + } + + public getQueryResultsEditor(): QueryResultsEditor { + return this._resultsEditor; + } + + public getSqlEditor(): TextResourceEditor { + return this._sqlEditor; + } + + public dispose(): void { + this._disposeEditors(); + super.dispose(); + } + + public close(): void { + let queryInput: QueryInput = this.input; + queryInput.sql.close(); + queryInput.results.close(); + } + + /** + * Makes visible the QueryResultsEditor for the current QueryInput (if it is not + * already visible). + */ + public _showQueryResultsEditor(): void { + if (this._isResultsEditorVisible()) { + return; + } + + this._editorGroupService.pinEditor(this.position, this.input); + + let input = this.input; + this._createResultsEditorContainer(); + + this._createEditor(input.results, this._resultsEditorContainer) + .then(result => { + this._onResultsEditorCreated(result, input.results, this.options); + this._setResultsEditorVisible(); + this._doLayout(); + }); + } + + /** + * Returns the underlying SQL editor's text selection in a 0-indexed format. Returns undefined if there + * is no selected text. + */ + public getSelection(checkIfRange: boolean = true): ISelectionData { + if (this._sqlEditor && this._sqlEditor.getControl()) { + let vscodeSelection = this._sqlEditor.getControl().getSelection(); + + // If the selection is a range of characters rather than just a cursor position, return the range + let isRange: boolean = + !(vscodeSelection.getStartPosition().lineNumber === vscodeSelection.getEndPosition().lineNumber && + vscodeSelection.getStartPosition().column === vscodeSelection.getEndPosition().column); + if (!checkIfRange || isRange) { + let sqlToolsServiceSelection: ISelectionData = { + startLine: vscodeSelection.getStartPosition().lineNumber - 1, + startColumn: vscodeSelection.getStartPosition().column - 1, + endLine: vscodeSelection.getEndPosition().lineNumber - 1, + endColumn: vscodeSelection.getEndPosition().column - 1, + }; + return sqlToolsServiceSelection; + } + } + + // Otherwise return undefined because there is no selected text + return undefined; + } + + public isSelectionEmpty(): boolean { + if (this._sqlEditor && this._sqlEditor.getControl()) { + let control = this._sqlEditor.getControl(); + let codeEditor: CodeEditor = control; + + if (codeEditor) { + let value = codeEditor.getValue(); + if (value !== undefined && value.length > 0) { + return false; + } + } + } + return true; + } + + /** + * Calls the run method of this editor's RunQueryAction + */ + public runQuery(): void { + this._runQueryAction.run(); + } + + /** + * Calls the runCurrent method of this editor's RunQueryAction + */ + public runCurrentQuery(): void { + this._runQueryAction.runCurrent(); + } + + /** + * Calls the run method of this editor's CancelQueryAction + */ + public cancelQuery(): void { + this._cancelQueryAction.run(); + } + + public rebuildIntelliSenseCache(): void { + this._connectionManagementService.rebuildIntelliSenseCache(this.connectedUri); + } + + // PRIVATE METHODS //////////////////////////////////////////////////////////// + + /** + * Creates the query execution taskbar that appears at the top of the QueryEditor + */ + private _createTaskbar(parentElement: HTMLElement): void { + // Create QueryTaskbar + this._taskbarContainer = DOM.append(parentElement, DOM.$('div')); + this._taskbar = new Taskbar(this._taskbarContainer, this._contextMenuService, { + actionItemProvider: (action: Action) => this._getActionItemForAction(action), + }); + + // Create Actions for the toolbar + this._runQueryAction = this._instantiationService.createInstance(RunQueryAction, this); + this._cancelQueryAction = this._instantiationService.createInstance(CancelQueryAction, this); + this._toggleConnectDatabaseAction = this._instantiationService.createInstance(ToggleConnectDatabaseAction, this, false); + this._changeConnectionAction = this._instantiationService.createInstance(ConnectDatabaseAction, this, true); + this._listDatabasesAction = this._instantiationService.createInstance(ListDatabasesAction, this); + this._estimatedQueryPlanAction = this._instantiationService.createInstance(EstimatedQueryPlanAction, this); + + // Create HTML Elements for the taskbar + let separator = Taskbar.createTaskbarSeparator(); + + // Set the content in the order we desire + let content: ITaskbarContent[] = [ + { action: this._runQueryAction }, + { action: this._cancelQueryAction }, + { element: separator }, + { action: this._toggleConnectDatabaseAction }, + { action: this._changeConnectionAction }, + { action: this._listDatabasesAction }, + { element: separator }, + { action: this._estimatedQueryPlanAction }, + ]; + this._taskbar.setContent(content); + } + + /** + * Gets the IActionItem for the List Databases dropdown if provided the associated Action. + * Otherwise returns null. + */ + private _getActionItemForAction(action: Action): IActionItem { + if (action.id === ListDatabasesAction.ID) { + return this.listDatabasesActionItem; + } + + return null; + } + + /** + * Public for testing purposes only + */ + public get listDatabasesActionItem(): ListDatabasesActionItem { + if (!this._listDatabasesActionItem) { + this._listDatabasesActionItem = this._instantiationService.createInstance(ListDatabasesActionItem, this, this._listDatabasesAction); + this._register(attachEditableDropdownStyler(this._listDatabasesActionItem, this.themeService)); + } + return this._listDatabasesActionItem; + } + + /** + * Handles setting input for this editor. + */ + private _updateInput(oldInput: QueryInput, newInput: QueryInput, options?: EditorOptions): TPromise { + + if (oldInput) { + this._disposeEditors(); + } + + this._createSqlEditorContainer(); + if (this._isResultsEditorVisible()) { + this._createResultsEditorContainer(); + + let uri: string = newInput.getQueryResultsInputResource(); + if (uri) { + this._queryModelService.refreshResultsets(uri); + } + } + + if (this._sash) { + if (this._isResultsEditorVisible()) { + this._sash.show(); + } else { + this._sash.hide(); + } + } + + this._updateTaskbar(); + return this._setNewInput(newInput, options); + } + + /** + * Handles setting input and creating editors when this QueryEditor is either: + * - Opened for the first time + * - Opened with a new QueryInput + * This will create only the SQL editor if the results editor does not yet exist for the + * given QueryInput. + */ + private _setNewInput(newInput: QueryInput, options?: EditorOptions): TPromise { + + // Promises that will ensure proper ordering of editor creation logic + let createEditors: () => TPromise; + let onEditorsCreated: (result) => TPromise; + + // If both editors exist, create joined promises - one for each editor + if (this._isResultsEditorVisible()) { + createEditors = () => { + return TPromise.join([ + this._createEditor(newInput.results, this._resultsEditorContainer), + this._createEditor(newInput.sql, this._sqlEditorContainer) + ]); + }; + onEditorsCreated = (result: IEditor[]) => { + return TPromise.join([ + this._onResultsEditorCreated(result[0], newInput.results, options), + this._onSqlEditorCreated(result[1], newInput.sql, options) + ]); + }; + + // If only the sql editor exists, create a promise and wait for the sql editor to be created + } else { + createEditors = () => { + return this._createEditor(newInput.sql, this._sqlEditorContainer); + }; + onEditorsCreated = (result: TextResourceEditor) => { + return this._onSqlEditorCreated(result, newInput.sql, options); + }; + } + + // Create a promise to re render the layout after the editor creation logic + let doLayout: () => TPromise = () => { + this._doLayout(); + return TPromise.as(undefined); + }; + + // Run all three steps synchronously + return createEditors() + .then(onEditorsCreated) + .then(doLayout); + } + + /** + * Create a single editor based on the type of the given EditorInput. + */ + private _createEditor(editorInput: EditorInput, container: HTMLElement): TPromise { + const descriptor = this._editorDescriptorService.getEditor(editorInput); + if (!descriptor) { + return TPromise.wrapError(new Error(strings.format('Can not find a registered editor for the input {0}', editorInput))); + } + return this._instantiationService.createInstance(descriptor) + .then((editor: BaseEditor) => { + editor.create(new Builder(container)); + editor.setVisible(this.isVisible(), this.position); + return editor; + }); + } + + /** + * Sets input for the SQL editor after it has been created. + */ + private _onSqlEditorCreated(sqlEditor: TextResourceEditor, sqlInput: UntitledEditorInput, options: EditorOptions): TPromise { + this._sqlEditor = sqlEditor; + return this._sqlEditor.setInput(sqlInput, options); + } + + /** + * Sets input for the results editor after it has been created. + */ + private _onResultsEditorCreated(resultsEditor: QueryResultsEditor, resultsInput: QueryResultsInput, options: EditorOptions): TPromise { + this._resultsEditor = resultsEditor; + return this._resultsEditor.setInput(resultsInput, options); + } + + /** + * Appends the HTML for the SQL editor. Creates new HTML every time. + */ + private _createSqlEditorContainer() { + const parentElement = this.getContainer().getHTMLElement(); + this._sqlEditorContainer = DOM.append(parentElement, DOM.$('.details-editor-container')); + this._sqlEditorContainer.style.position = 'absolute'; + } + + /** + * Appends the HTML for the QueryResultsEditor to the QueryEditor. If the HTML has not yet been + * created, it creates it and appends it. If it has already been created, it locates it and + * appends it. + */ + private _createResultsEditorContainer() { + this._createSash(); + + const parentElement = this.getContainer().getHTMLElement(); + let input = this.input; + + if (!input.results.container) { + let cssClass: string = '.master-editor-container'; + if (this._orientation === Orientation.HORIZONTAL) { + cssClass = '.master-editor-container-horizontal'; + } + + this._resultsEditorContainer = DOM.append(parentElement, DOM.$(cssClass)); + this._resultsEditorContainer.style.position = 'absolute'; + + input.results.container = this._resultsEditorContainer; + } else { + this._resultsEditorContainer = DOM.append(parentElement, input.results.container); + } + } + + /** + * Creates the sash with the requested orientation and registers sash callbacks + */ + private _createSash(): void { + if (!this._sash) { + let parentElement: HTMLElement = this.getContainer().getHTMLElement(); + + if (this._orientation === Orientation.HORIZONTAL) { + this._sash = this._register(new HorizontalFlexibleSash(parentElement, this._minEditorSize)); + } else { + this._sash = this._register(new VerticalFlexibleSash(parentElement, this._minEditorSize)); + this._sash.setEdge(this._taskbarHeight + this._tabHeight); + } + this._setSashDimension(); + + this._register(this._sash.onPositionChange(position => this._doLayout())); + } + + this.sash.show(); + } + + private _setSashDimension(): void { + if (!this._dimension) { + return; + } + if (this._orientation === Orientation.HORIZONTAL) { + this._sash.setDimenesion(this._dimension); + } else { + this._sash.setDimenesion(new Dimension(this._dimension.width, this._dimension.height - this._taskbarHeight)); + } + + } + + /** + * Updates the size of the 2 sub-editors. Uses agnostic dimensions due to the fact that + * the IFlexibleSash could be horizontal or vertical. The same logic is used for horizontal + * and vertical sashes. + */ + private _doLayout(): void { + if (!this._isResultsEditorVisible() && this._sqlEditor) { + this._doLayoutSql(); + return; + } + if (!this._sqlEditor || !this._resultsEditor || !this._dimension || !this._sash) { + return; + } + + if (this._orientation === Orientation.HORIZONTAL) { + this._doLayoutHorizontal(); + } else { + this._doLayoutVertical(); + } + + this._resizeGridContents(); + } + + private _doLayoutHorizontal(): void { + let splitPointTop: number = this._sash.getSplitPoint(); + let parent: ClientRect = this.getContainer().getHTMLElement().getBoundingClientRect(); + + let sqlEditorHeight = splitPointTop - (parent.top + this._taskbarHeight); + + let titleBar = withElementById('workbench.parts.titlebar'); + if (titleBar) { + sqlEditorHeight += DOM.getContentHeight(titleBar.getHTMLElement()); + } + + let queryResultsEditorHeight = parent.bottom - splitPointTop; + + this._sqlEditorContainer.style.height = `${sqlEditorHeight}px`; + this._sqlEditorContainer.style.width = `${this._dimension.width}px`; + this._sqlEditorContainer.style.top = `${this._editorTopOffset}px`; + + this._resultsEditorContainer.style.height = `${queryResultsEditorHeight}px`; + this._resultsEditorContainer.style.width = `${this._dimension.width}px`; + this._resultsEditorContainer.style.top = `${splitPointTop}px`; + + this._sqlEditor.layout(new Dimension(this._dimension.width, sqlEditorHeight)); + this._resultsEditor.layout(new Dimension(this._dimension.width, queryResultsEditorHeight)); + } + + private _doLayoutVertical(): void { + let splitPointLeft: number = this._sash.getSplitPoint(); + let parent: ClientRect = this.getContainer().getHTMLElement().getBoundingClientRect(); + + let sqlEditorWidth = splitPointLeft; + let queryResultsEditorWidth = parent.width - splitPointLeft; + + this._sqlEditorContainer.style.width = `${sqlEditorWidth}px`; + this._sqlEditorContainer.style.height = `${this._dimension.height - this._taskbarHeight}px`; + this._sqlEditorContainer.style.left = `0px`; + + this._resultsEditorContainer.style.width = `${queryResultsEditorWidth}px`; + this._resultsEditorContainer.style.height = `${this._dimension.height - this._taskbarHeight}px`; + this._resultsEditorContainer.style.left = `${splitPointLeft}px`; + + this._sqlEditor.layout(new Dimension(sqlEditorWidth, this._dimension.height - this._taskbarHeight)); + this._resultsEditor.layout(new Dimension(queryResultsEditorWidth, this._dimension.height - this._taskbarHeight)); + } + + private _doLayoutSql() { + if (this._dimension) { + this._sqlEditor.layout(new Dimension(this._dimension.width, this._dimension.height - this._taskbarHeight)); + } + } + + private _resizeGridContents(): void { + if (this._isResultsEditorVisible()) { + let queryInput: QueryInput = this.input; + let uri: string = queryInput.getQueryResultsInputResource(); + if (uri) { + this._queryModelService.resizeResultsets(uri); + } + } + } + + private _disposeEditors(): void { + if (this._sqlEditor) { + this._sqlEditor.dispose(); + this._sqlEditor = null; + } + if (this._resultsEditor) { + this._resultsEditor.dispose(); + this._resultsEditor = null; + } + + let thisEditorParent: HTMLElement = this.getContainer().getHTMLElement(); + + if (this._sqlEditorContainer) { + let sqlEditorParent: HTMLElement = this._sqlEditorContainer.parentElement; + if (sqlEditorParent && sqlEditorParent === thisEditorParent) { + this._sqlEditorContainer.parentElement.removeChild(this._sqlEditorContainer); + } + this._sqlEditorContainer = null; + } + + if (this._resultsEditorContainer) { + let resultsEditorParent: HTMLElement = this._resultsEditorContainer.parentElement; + if (resultsEditorParent && resultsEditorParent === thisEditorParent) { + this._resultsEditorContainer.parentElement.removeChild(this._resultsEditorContainer); + } + this._resultsEditorContainer = null; + } + } + + /** + * Returns true if the QueryResultsInput has denoted that the results editor + * should be visible. + * Public for testing only. + */ + public _isResultsEditorVisible(): boolean { + let input: QueryInput = this.input; + + if (!input) { + return false; + } + return input.results.visible; + } + + private _setResultsEditorVisible(): void { + let input: QueryInput = this.input; + input.results.setVisibleTrue(); + } + + /** + * Update the buttons on the taskbar to reflect the state of the current input. + */ + private _updateTaskbar(): void { + let queryInput: QueryInput = this.input; + + if (queryInput) { + this._cancelQueryAction.enabled = queryInput.cancelQueryEnabled; + this._changeConnectionAction.enabled = queryInput.changeConnectionEnabled; + + // For the toggle database action, it should always be enabled since it's a toggle. + // We use inverse of connect enabled state for now, should refactor queryInput in the future to + // define connected as a boolean instead of using the enabled flag + this._toggleConnectDatabaseAction.enabled = true; + this._toggleConnectDatabaseAction.connected = !queryInput.connectEnabled; + this._runQueryAction.enabled = queryInput.runQueryEnabled; + if (queryInput.listDatabasesConnected) { + this.listDatabasesActionItem.onConnected(); + } else { + this.listDatabasesActionItem.onDisconnect(); + } + } + } + + /** + * Sets the text selection for the SQL editor based on the given ISelectionData. + */ + private _setSelection(selection: ISelectionData): void { + let rangeConversion: IRange = { + startLineNumber: selection.startLine + 1, + startColumn: selection.startColumn + 1, + endLineNumber: selection.endLine + 1, + endColumn: selection.endColumn + 1 + }; + let editor = this._sqlEditor.getControl(); + editor.revealRange(rangeConversion); + editor.setSelection(rangeConversion); + editor.focus(); + } + + // TESTING PROPERTIES //////////////////////////////////////////////////////////// + + public get resultsEditor(): QueryResultsEditor { + return this._resultsEditor; + } + + public get sqlEditor(): TextResourceEditor { + return this._sqlEditor; + } + + public get taskbar(): Taskbar { + return this._taskbar; + } + + public get sash(): IFlexibleSash { + return this._sash; + } + + public get resultsEditorContainer(): HTMLElement { + return this._resultsEditorContainer; + } + + public get sqlEditorContainer(): HTMLElement { + return this._sqlEditorContainer; + } + + public get taskbarContainer(): HTMLElement { + return this._taskbarContainer; + } + + public get runQueryAction(): RunQueryAction { + return this._runQueryAction; + } + + public get cancelQueryAction(): CancelQueryAction { + return this._cancelQueryAction; + } + + public get changeConnectionAction(): ConnectDatabaseAction { + return this._changeConnectionAction; + } +} diff --git a/src/sql/parts/query/editor/queryResultsEditor.ts b/src/sql/parts/query/editor/queryResultsEditor.ts new file mode 100644 index 0000000000..181ade974f --- /dev/null +++ b/src/sql/parts/query/editor/queryResultsEditor.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { Builder, Dimension } from 'vs/base/browser/builder'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import { QueryComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { QueryOutputModule } from 'sql/parts/query/views/queryOutput.module'; +import { QUERY_OUTPUT_SELECTOR } from 'sql/parts/query/views/queryOutput.component'; + +export const TextCompareEditorVisible = new RawContextKey('textCompareEditorVisible', false); + +/** + * Editor associated with viewing and editing the data of a query results grid. + */ +export class QueryResultsEditor extends BaseEditor { + + public static ID: string = 'workbench.editor.queryResultsEditor'; + public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer'; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IQueryModelService private _queryModelService: IQueryModelService, + @IBootstrapService private _bootstrapService: IBootstrapService + ) { + super(QueryResultsEditor.ID, telemetryService, themeService); + } + + createEditor(parent: Builder): void { + } + + layout(dimension: Dimension): void { + } + + setInput(input: QueryResultsInput, options: EditorOptions): TPromise { + super.setInput(input, options); + if (!input.hasBootstrapped) { + this._bootstrapAngular(); + } + return TPromise.as(null); + } + + /** + * Load the angular components and record for this input that we have done so + */ + private _bootstrapAngular(): void { + let input = this.input; + let uri = input.uri; + + // Pass the correct DataService to the new angular component + let dataService = this._queryModelService.getDataService(uri); + if (!dataService) { + throw new Error('DataService not found for URI: ' + uri); + } + + // Mark that we have bootstrapped + input.setBootstrappedTrue(); + + // Get the bootstrap params and perform the bootstrap + // Note: pass in input so on disposal this is cleaned up. + // Otherwise many components will be left around and be subscribed + // to events from the backing data service + let params: QueryComponentParams = { dataService: dataService }; + this._bootstrapService.bootstrap( + QueryOutputModule, + this.getContainer().getHTMLElement(), + QUERY_OUTPUT_SELECTOR, + params, + input); + } + + public dispose(): void { + super.dispose(); + } +} \ No newline at end of file diff --git a/src/sql/parts/query/execution/keyboardQueryActions.ts b/src/sql/parts/query/execution/keyboardQueryActions.ts new file mode 100644 index 0000000000..78a11da8b1 --- /dev/null +++ b/src/sql/parts/query/execution/keyboardQueryActions.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); + +import { Action} from 'vs/base/common/actions'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { QueryEditor } from 'sql/parts/query/editor/queryEditor'; +import { TPromise } from 'vs/base/common/winjs.base'; + +/** + * Locates the active editor and calls runQuery() on the editor if it is a QueryEditor. + */ +export class RunQueryKeyboardAction extends Action { + + public static ID = 'runQueryKeyboardAction'; + public static LABEL = nls.localize('runQueryKeyboardAction', 'Run Query'); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService + ) { + super(id, label); + this.enabled = true; + } + + public run(): TPromise { + let editor = this._editorService.getActiveEditor(); + if (editor && editor instanceof QueryEditor) { + let queryEditor: QueryEditor = editor; + queryEditor.runQuery(); + } + return TPromise.as(null); + } +} + +/** + * Locates the active editor and calls runCurrentQuery() on the editor if it is a QueryEditor. + */ +export class RunCurrentQueryKeyboardAction extends Action { + public static ID = 'runCurrentQueryKeyboardAction'; + public static LABEL = nls.localize('runCurrentQueryKeyboardAction', 'Run Current Query'); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService + ) { + super(id, label); + this.enabled = true; + } + + public run(): TPromise { + let editor = this._editorService.getActiveEditor(); + if (editor && editor instanceof QueryEditor) { + let queryEditor: QueryEditor = editor; + queryEditor.runCurrentQuery(); + } + return TPromise.as(null); + } +} + +/** + * Locates the active editor and calls cancelQuery() on the editor if it is a QueryEditor. + */ +export class CancelQueryKeyboardAction extends Action { + + public static ID = 'cancelQueryKeyboardAction'; + public static LABEL = nls.localize('cancelQueryKeyboardAction', 'Cancel Query'); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService + ) { + super(id, label); + this.enabled = true; + } + + public run(): TPromise { + let editor = this._editorService.getActiveEditor(); + if (editor && editor instanceof QueryEditor) { + let queryEditor: QueryEditor = editor; + queryEditor.cancelQuery(); + } + return TPromise.as(null); + } +} + +/** + * Refresh the IntelliSense cache + */ +export class RefreshIntellisenseKeyboardAction extends Action { + public static ID = 'refreshIntellisenseKeyboardAction'; + public static LABEL = nls.localize('refreshIntellisenseKeyboardAction', 'Refresh IntelliSense Cache'); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService + ) { + super(id, label); + this.enabled = true; + } + + public run(): TPromise { + let editor = this._editorService.getActiveEditor(); + if (editor && editor instanceof QueryEditor) { + let queryEditor: QueryEditor = editor; + queryEditor.rebuildIntelliSenseCache(); + } + return TPromise.as(null); + } +} diff --git a/src/sql/parts/query/execution/queryActions.ts b/src/sql/parts/query/execution/queryActions.ts new file mode 100644 index 0000000000..63a58581ef --- /dev/null +++ b/src/sql/parts/query/execution/queryActions.ts @@ -0,0 +1,543 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown'; +import { Action, IActionItem, IActionRunner } from 'vs/base/common/actions'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +import { ISelectionData } from 'data'; +import { + IConnectionManagementService, + IConnectionParams, + INewConnectionParams, + ConnectionType, + RunQueryOnConnectionMode +} from 'sql/parts/connection/common/connectionManagement'; +import { QueryEditor } from 'sql/parts/query/editor/queryEditor'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; + +/** + * Action class that query-based Actions will extend. This base class automatically handles activating and + * deactivating the button when a SQL file is opened. + */ +export abstract class QueryTaskbarAction extends Action { + + private _classes: string[]; + + constructor( + protected _connectionManagementService: IConnectionManagementService, + protected editor: QueryEditor, + id: string, + enabledClass: string + ) { + super(id); + this.enabled = true; + this._setCssClass(enabledClass); + } + + /** + * This method is executed when the button is clicked. + */ + public abstract run(): TPromise; + + protected updateCssClass(enabledClass: string): void { + // set the class, useful on change of label or icon + this._setCssClass(enabledClass); + } + + /** + * Sets the CSS classes combining the parent and child classes. + * Public for testing only. + */ + private _setCssClass(enabledClass: string): void { + this._classes = []; + + if (enabledClass) { + this._classes.push(enabledClass); + } + this.class = this._classes.join(' '); + } + + /** + * Returns the URI of the given editor if it is not undefined and is connected. + * Public for testing only. + */ + public isConnected(editor: QueryEditor): boolean { + if (!editor || !editor.currentQueryInput) { + return false; + } + return this._connectionManagementService.isConnected(editor.currentQueryInput.uri); + } + + /** + * Connects the given editor to it's current URI. + * Public for testing only. + */ + protected connectEditor(editor: QueryEditor, runQueryOnCompletion?: RunQueryOnConnectionMode, selection?: ISelectionData): void { + let params: INewConnectionParams = { + input: editor.currentQueryInput, + connectionType: ConnectionType.editor, + runQueryOnCompletion: runQueryOnCompletion ? runQueryOnCompletion : RunQueryOnConnectionMode.none, + querySelection: selection + }; + this._connectionManagementService.showConnectionDialog(params); + } +} + +/** + * Action class that runs a query in the active SQL text document. + */ +export class RunQueryAction extends QueryTaskbarAction { + + public static EnabledClass = 'start'; + public static ID = 'runQueryAction'; + + constructor( + editor: QueryEditor, + @IQueryModelService private _queryModelService: IQueryModelService, + @IConnectionManagementService connectionManagementService: IConnectionManagementService + ) { + super(connectionManagementService, editor, RunQueryAction.ID, RunQueryAction.EnabledClass); + this.label = nls.localize('runQueryLabel', 'Run'); + } + + public run(): TPromise { + if (!this.editor.isSelectionEmpty()) { + if (this.isConnected(this.editor)) { + // If we are already connected, run the query + this.runQuery(this.editor); + } else { + // If we are not already connected, prompt for connection and run the query if the + // connection succeeds. "runQueryOnCompletion=true" will cause the query to run after connection + this.connectEditor(this.editor, RunQueryOnConnectionMode.executeQuery, this.editor.getSelection()); + } + } + return TPromise.as(null); + } + + public runCurrent(): TPromise { + if (!this.editor.isSelectionEmpty()) { + if (this.isConnected(this.editor)) { + // If we are already connected, run the query + this.runQuery(this.editor, true); + } else { + // If we are not already connected, prompt for connection and run the query if the + // connection succeeds. "runQueryOnCompletion=true" will cause the query to run after connection + this.connectEditor(this.editor, RunQueryOnConnectionMode.executeCurrentQuery, this.editor.getSelection(false)); + } + } + return TPromise.as(null); + } + + public runQuery(editor: QueryEditor, runCurrentStatement: boolean = false) { + if (!editor) { + editor = this.editor; + } + + if (this.isConnected(editor)) { + // if the selection isn't empty then execute the selection + // otherwise, either run the statement or the script depending on parameter + let selection: ISelectionData = editor.getSelection(false); + if (runCurrentStatement && selection && this.isCursorPosition(selection)) { + editor.currentQueryInput.runQueryStatement(selection); + } else { + // get the selection again this time with trimming + selection = editor.getSelection(); + editor.currentQueryInput.runQuery(selection); + } + } + } + + private isCursorPosition(selection: ISelectionData) { + return selection.startLine === selection.endLine + && selection.startColumn === selection.endColumn; + } +} + +/** + * Action class that cancels the running query in the current SQL text document. + */ +export class CancelQueryAction extends QueryTaskbarAction { + + public static EnabledClass = 'stop'; + public static ID = 'cancelQueryAction'; + + constructor( + editor: QueryEditor, + @IQueryModelService private _queryModelService: IQueryModelService, + @IConnectionManagementService connectionManagementService: IConnectionManagementService + ) { + super(connectionManagementService, editor, CancelQueryAction.ID, CancelQueryAction.EnabledClass); + this.enabled = false; + this.label = nls.localize('cancelQueryLabel', 'Cancel'); + } + + public run(): TPromise { + if (this.isConnected(this.editor)) { + this._queryModelService.cancelQuery(this.editor.currentQueryInput.uri); + } + return TPromise.as(null); + } +} + +/** + * Action class that runs a query in the active SQL text document. + */ +export class EstimatedQueryPlanAction extends QueryTaskbarAction { + + public static EnabledClass = 'estimatedQueryPlan'; + public static ID = 'estimatedQueryPlanAction'; + + constructor( + editor: QueryEditor, + @IQueryModelService private _queryModelService: IQueryModelService, + @IConnectionManagementService connectionManagementService: IConnectionManagementService + ) { + super(connectionManagementService, editor, EstimatedQueryPlanAction.ID, EstimatedQueryPlanAction.EnabledClass); + this.label = nls.localize('estimatedQueryPlan', 'Explain'); + } + + public run(): TPromise { + if (!this.editor.isSelectionEmpty()) { + if (this.isConnected(this.editor)) { + // If we are already connected, run the query + this.runQuery(this.editor); + } else { + // If we are not already connected, prompt for connection and run the query if the + // connection succeeds. "runQueryOnCompletion=true" will cause the query to run after connection + this.connectEditor(this.editor, RunQueryOnConnectionMode.estimatedQueryPlan, this.editor.getSelection()); + } + } + return TPromise.as(null); + } + + public runQuery(editor: QueryEditor) { + if (!editor) { + editor = this.editor; + } + + if (this.isConnected(editor)) { + editor.currentQueryInput.runQuery(editor.getSelection(), { + displayEstimatedQueryPlan: true + }); + } + } +} + +/** + * Action class that disconnects the connection associated with the current query file. + */ +export class DisconnectDatabaseAction extends QueryTaskbarAction { + + public static EnabledClass = 'disconnect'; + public static ID = 'disconnectDatabaseAction'; + + constructor( + editor: QueryEditor, + @IConnectionManagementService connectionManagementService: IConnectionManagementService + ) { + super(connectionManagementService, editor, DisconnectDatabaseAction.ID, DisconnectDatabaseAction.EnabledClass); + this.label = nls.localize('disconnectDatabaseLabel', 'Disconnect'); + } + + public run(): TPromise { + // Call disconnectEditor regardless of the connection state and let the ConnectionManagementService + // determine if we need to disconnect, cancel an in-progress conneciton, or do nothing + this._connectionManagementService.disconnectEditor(this.editor.currentQueryInput); + return TPromise.as(null); + } +} + +/** + * Action class that launches a connection dialogue for the current query file + */ +export class ConnectDatabaseAction extends QueryTaskbarAction { + + public static EnabledDefaultClass = 'connect'; + public static EnabledChangeClass = 'changeConnection'; + public static ID = 'connectDatabaseAction'; + + constructor( + editor: QueryEditor, + isChangeConnectionAction: boolean, + @IConnectionManagementService connectionManagementService: IConnectionManagementService + ) { + let label: string; + let enabledClass: string; + + if (isChangeConnectionAction) { + enabledClass = ConnectDatabaseAction.EnabledChangeClass; + label = nls.localize('changeConnectionDatabaseLabel', 'Change Connection'); + } else { + enabledClass = ConnectDatabaseAction.EnabledDefaultClass; + label = nls.localize('connectDatabaseLabel', 'Connect'); + } + + super(connectionManagementService, editor, ConnectDatabaseAction.ID, enabledClass); + + this.label = label; + } + + public run(): TPromise { + this.connectEditor(this.editor); + return TPromise.as(null); + } +} + +/** + * Action class that either launches a connection dialogue for the current query file, + * or disconnects the active connection + */ +export class ToggleConnectDatabaseAction extends QueryTaskbarAction { + + public static ConnectClass = 'connect'; + public static DisconnectClass = 'disconnect'; + public static ID = 'toggleConnectDatabaseAction'; + + private _connected: boolean; + private _connectLabel: string; + private _disconnectLabel: string; + constructor( + editor: QueryEditor, + isConnected: boolean, + @IConnectionManagementService connectionManagementService: IConnectionManagementService + ) { + let enabledClass: string; + + super(connectionManagementService, editor, ToggleConnectDatabaseAction.ID, enabledClass); + + this._connectLabel = nls.localize('connectDatabaseLabel', 'Connect'); + this._disconnectLabel = nls.localize('disconnectDatabaseLabel', 'Disconnect'); + + this.connected = isConnected; + } + + public get connected(): boolean { + return this._connected; + } + + public set connected(value: boolean) { + // intentionally always updating, since parent class handles skipping if values + this._connected = value; + this.updateLabelAndIcon(); + } + + private updateLabelAndIcon(): void { + if (this._connected) { + // We are connected, so show option to disconnect + this.label = this._disconnectLabel; + this.updateCssClass(ToggleConnectDatabaseAction.DisconnectClass); + } else { + this.label = this._connectLabel; + this.updateCssClass(ToggleConnectDatabaseAction.ConnectClass); + } + } + + public run(): TPromise { + if (this.connected) { + // Call disconnectEditor regardless of the connection state and let the ConnectionManagementService + // determine if we need to disconnect, cancel an in-progress connection, or do nothing + this._connectionManagementService.disconnectEditor(this.editor.currentQueryInput); + } else { + this.connectEditor(this.editor); + } + return TPromise.as(null); + } +} + +/** + * Action class that is tied with ListDatabasesActionItem. + */ +export class ListDatabasesAction extends QueryTaskbarAction { + + public static EnabledClass = ''; + public static ID = 'listDatabaseQueryAction'; + + constructor( + editor: QueryEditor, + @IConnectionManagementService connectionManagementService: IConnectionManagementService + ) { + super(connectionManagementService, editor, ListDatabasesAction.ID, undefined); + this.enabled = false; + this.class = ListDatabasesAction.EnabledClass; + } + + public run(): TPromise { + return TPromise.as(null); + } +} + +/* + * Action item that handles the dropdown (combobox) that lists the available databases. + * Based off StartDebugActionItem. + */ +export class ListDatabasesActionItem extends EventEmitter implements IActionItem { + public static ID = 'listDatabaseQueryActionItem'; + + public actionRunner: IActionRunner; + private _toDispose: IDisposable[]; + private _context: any; + private _currentDatabaseName: string; + private _isConnected: boolean; + private $databaseListDropdown: Builder; + private _dropdown: Dropdown; + + // CONSTRUCTOR ///////////////////////////////////////////////////////// + constructor( + private _editor: QueryEditor, + private _action: ListDatabasesAction, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IMessageService private _messageService: IMessageService, + @IContextViewService contextViewProvider: IContextViewService, + @IThemeService themeService: IThemeService + ) { + super(); + this._toDispose = []; + this.$databaseListDropdown = $('.databaseListDropdown'); + this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, { + strictSelection: true, + placeholder: nls.localize("selectDatabase", "Select Database") + }); + this._dropdown.onValueChange(s => this.databaseSelected(s)); + + // Register event handlers + let self = this; + this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); })); + this._toDispose.push(this._connectionManagementService.onConnectionChanged(params => { self.onConnectionChanged(params); })); + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + public render(container: HTMLElement): void { + this.$databaseListDropdown.appendTo(container); + } + + public style(styles) { + this._dropdown.style(styles); + } + + public setActionContext(context: any): void { + this._context = context; + } + + public isEnabled(): boolean { + return !!this._isConnected; + } + + public focus(): void { + this._dropdown.focus(); + } + + public blur(): void { + this._dropdown.blur(); + } + + public dispose(): void { + this._toDispose = dispose(this._toDispose); + } + + // EVENT HANDLERS FROM EDITOR ////////////////////////////////////////// + public onConnected(): void { + let dbName = this.getCurrentDatabaseName(); + this.updateConnection(dbName); + } + + public onDisconnect(): void { + this._isConnected = false; + this._dropdown.enabled = false; + this._currentDatabaseName = undefined; + this._dropdown.value = ''; + } + + // PRIVATE HELPERS ///////////////////////////////////////////////////// + private databaseSelected(dbName: string): void { + let uri = this._editor.connectedUri; + if (!uri) { + return; + } + + let profile = this._connectionManagementService.getConnectionProfile(uri); + if (!profile) { + return; + } + + this._connectionManagementService.changeDatabase(this._editor.uri, dbName) + .then( + result => { + if (!result) { + this.resetDatabaseName(); + this._messageService.show(Severity.Error, nls.localize('changeDatabase.failed', "Failed to change database")); + } + }, + error => { + this.resetDatabaseName(); + this._messageService.show(Severity.Error, nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)); + } + ); + } + + private getCurrentDatabaseName() { + let uri = this._editor.connectedUri; + if (uri) { + let profile = this._connectionManagementService.getConnectionProfile(uri); + if (profile) { + return profile.databaseName; + } + } + return undefined; + } + + private resetDatabaseName() { + this._dropdown.value = this.getCurrentDatabaseName(); + } + + private onConnectionChanged(connParams: IConnectionParams): void { + if (!connParams) { + return; + } + + let uri = this._editor.connectedUri; + if (uri !== connParams.connectionUri) { + return; + } + + this.updateConnection(connParams.connectionProfile.databaseName); + } + + private onDropdownFocus(): void { + let self = this; + + let uri = self._editor.connectedUri; + if (!uri) { + return; + } + + self._connectionManagementService.listDatabases(uri) + .then(result => { + if (result && result.databaseNames) { + this._dropdown.values = result.databaseNames; + } + }); + } + + private updateConnection(databaseName: string) { + this._isConnected = true; + this._dropdown.enabled = true; + this._currentDatabaseName = databaseName; + this._dropdown.value = databaseName; + } + + // TESTING PROPERTIES ////////////////////////////////////////////////// + public get currentDatabaseName(): string { + return this._currentDatabaseName; + } + +} diff --git a/src/sql/parts/query/execution/queryModel.ts b/src/sql/parts/query/execution/queryModel.ts new file mode 100644 index 0000000000..c5119d67fc --- /dev/null +++ b/src/sql/parts/query/execution/queryModel.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import QueryRunner from 'sql/parts/query/execution/queryRunner'; +import { DataService } from 'sql/parts/grid/services/dataService'; +import { ISlickRange } from 'angular2-slickgrid'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import Event from 'vs/base/common/event'; +import { QueryInput } from 'sql/parts/query/common/queryInput'; +import { + ISelectionData, + ResultSetSubset, + EditUpdateCellResult, + EditSessionReadyParams, + EditSubsetResult, + EditCreateRowResult, + EditRevertCellResult, + ExecutionPlanOptions +} from 'data'; + +export const SERVICE_ID = 'queryModelService'; + +export const IQueryModelService = createDecorator(SERVICE_ID); + +/** + * Interface for the logic of handling running queries and grid interactions for all URIs. + */ +export interface IQueryModelService { + _serviceBrand: any; + + getConfig(): Promise<{ [key: string]: any }>; + getShortcuts(): Promise; + getQueryRows(uri: string, rowStart: number, numberOfRows: number, batchId: number, resultId: number): Thenable; + runQuery(uri: string, selection: ISelectionData, title: string, queryInput: QueryInput, runOptions?: ExecutionPlanOptions): void; + runQueryStatement(uri: string, selection: ISelectionData, title: string, queryInput: QueryInput): void; + cancelQuery(input: QueryRunner | string): void; + disposeQuery(uri: string): Thenable; + isRunningQuery(uri: string): boolean; + + getDataService(uri: string): DataService; + refreshResultsets(uri: string): void; + sendGridContentEvent(uri: string, eventName: string): void; + resizeResultsets(uri: string): void; + onAngularLoaded(uri: string): void; + + copyResults(uri: string, selection: ISlickRange[], batchId: number, resultId: number, includeHeaders?: boolean): void; + setEditorSelection(uri: string, index: number): void; + showWarning(uri: string, message: string): void; + showError(uri: string, message: string): void; + showCommitError(error: string): void; + + onRunQueryStart: Event; + onRunQueryComplete: Event; + + + // Edit Data Functions + initializeEdit(ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): void; + disposeEdit(ownerUri: string): Thenable; + updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable; + commitEdit(ownerUri): Thenable; + createRow(ownerUri: string): Thenable; + deleteRow(ownerUri: string, rowId: number): Thenable; + revertCell(ownerUri: string, rowId: number, columnId: number): Thenable; + revertRow(ownerUri: string, rowId: number): Thenable; + getEditRows(ownerUri: string, rowStart: number, numberOfRows: number): Thenable; + + // Edit Data Callbacks + onEditSessionReady: Event; +} diff --git a/src/sql/parts/query/execution/queryModelService.ts b/src/sql/parts/query/execution/queryModelService.ts new file mode 100644 index 0000000000..5d06c822b4 --- /dev/null +++ b/src/sql/parts/query/execution/queryModelService.ts @@ -0,0 +1,529 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents'; +import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants'; +import QueryRunner from 'sql/parts/query/execution/queryRunner'; +import { DataService } from 'sql/parts/grid/services/dataService'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { QueryInput } from 'sql/parts/query/common/queryInput'; +import { QueryStatusbarItem } from 'sql/parts/query/execution/queryStatus'; +import { SqlFlavorStatusbarItem } from 'sql/parts/query/common/flavorStatus'; + +import { + ISelectionData, ResultSetSubset, EditSubsetResult, ExecutionPlanOptions, + EditUpdateCellResult, EditSessionReadyParams, EditCreateRowResult, EditRevertCellResult +} from 'data'; +import { ISlickRange } from 'angular2-slickgrid'; + +import * as nls from 'vs/nls'; +import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar'; +import * as platform from 'vs/platform/registry/common/platform'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import Event, { Emitter } from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as strings from 'vs/base/common/strings'; + +interface QueryEvent { + type: string; + data: any; +} + +/** + * Holds information about the state of a query runner + */ +class QueryInfo { + public queryRunner: QueryRunner; + public dataService: DataService; + public queryEventQueue: QueryEvent[]; + public selection: Array; + public queryInput: QueryInput; + + // Notes if the angular components have obtained the DataService. If not, all messages sent + // via the data service will be lost. + public dataServiceReady: boolean; + + constructor() { + this.dataServiceReady = false; + this.queryEventQueue = []; + this.selection = []; + } +} + +/** + * Handles running queries and grid interactions for all URIs. Interacts with each URI's results grid via a DataService instance + */ +export class QueryModelService implements IQueryModelService { + _serviceBrand: any; + + // MEMBER VARIABLES //////////////////////////////////////////////////// + private _queryInfoMap: Map; + private _onRunQueryStart: Emitter; + private _onRunQueryComplete: Emitter; + private _onEditSessionReady: Emitter; + + // EVENTS ///////////////////////////////////////////////////////////// + public get onRunQueryStart(): Event { return this._onRunQueryStart.event; } + public get onRunQueryComplete(): Event { return this._onRunQueryComplete.event; } + public get onEditSessionReady(): Event { return this._onEditSessionReady.event; } + + // CONSTRUCTOR ///////////////////////////////////////////////////////// + constructor( + @IInstantiationService private _instantiationService: IInstantiationService, + @IMessageService private _messageService: IMessageService + ) { + this._queryInfoMap = new Map(); + this._onRunQueryStart = new Emitter(); + this._onRunQueryComplete = new Emitter(); + this._onEditSessionReady = new Emitter(); + + // Register Statusbar items + (platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( + QueryStatusbarItem, + statusbar.StatusbarAlignment.RIGHT, + 100 /* High Priority */ + )); + + // (platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( + // SqlFlavorStatusbarItem, + // statusbar.StatusbarAlignment.RIGHT, + // 90 /* Should appear to the right of the SQL editor status */ + // )); + } + + // IQUERYMODEL ///////////////////////////////////////////////////////// + public getDataService(uri: string): DataService { + let dataService = this._getQueryInfo(uri).dataService; + if (!dataService) { + throw new Error('Could not find data service for uri: ' + uri); + } + + return dataService; + } + + /** + * Force all grids to re-render. This is needed to re-render the grids when switching + * between different URIs. + */ + public refreshResultsets(uri: string): void { + this._fireGridContentEvent(uri, GridContentEvents.RefreshContents); + } + + /** + * Resize the grid UI to fit the current screen size. + */ + public resizeResultsets(uri: string): void { + this._fireGridContentEvent(uri, GridContentEvents.ResizeContents); + } + + public sendGridContentEvent(uri: string, eventName: string): void { + this._fireGridContentEvent(uri, eventName); + } + + /** + * To be called by an angular component's DataService when the component has finished loading. + * Sends all previously enqueued query events to the DataService and signals to stop enqueuing + * any further events. This prevents QueryEvents from getting lost if they are sent before + * angular is listening for them. + */ + public onAngularLoaded(uri: string) { + let info = this._getQueryInfo(uri); + info.dataServiceReady = true; + this._sendQueuedEvents(uri); + } + + /** + * Get more data rows from the current resultSets from the service layer + */ + public getQueryRows(uri: string, rowStart: number, numberOfRows: number, batchId: number, resultId: number): Thenable { + return this._getQueryInfo(uri).queryRunner.getQueryRows(rowStart, numberOfRows, batchId, resultId).then(results => { + return results.resultSubset; + }); + } + + public getEditRows(uri: string, rowStart: number, numberOfRows: number): Thenable { + return this._queryInfoMap.get(uri).queryRunner.getEditRows(rowStart, numberOfRows).then(results => { + return results; + }); + } + + public getConfig(): Promise<{ [key: string]: any }> { + return undefined; + } + + public getShortcuts(): Promise { + return undefined; + } + + public copyResults(uri: string, selection: ISlickRange[], batchId: number, resultId: number, includeHeaders?: boolean): void { + this._queryInfoMap.get(uri).queryRunner.copyResults(selection, batchId, resultId, includeHeaders); + } + + public setEditorSelection(uri: string, index: number): void { + let info: QueryInfo = this._queryInfoMap.get(uri); + if (info && info.queryInput) { + info.queryInput.updateSelection(info.selection[index]); + } + } + + public showWarning(uri: string, message: string): void { + } + + public showError(uri: string, message: string): void { + } + + public showCommitError(error: string): void { + this._messageService.show(Severity.Error, nls.localize('commitEditFailed', 'Commit row failed: ') + error); + } + + public isRunningQuery(uri: string): boolean { + return !this._queryInfoMap.has(uri) + ? false + : this._getQueryInfo(uri).queryRunner.isExecuting; + } + + /** + * Run a query for the given URI with the given text selection + */ + public runQuery(uri: string, selection: ISelectionData, + title: string, queryInput: QueryInput, runOptions?: ExecutionPlanOptions): void { + this.doRunQuery(uri, selection, title, queryInput, false, runOptions); + } + + /** + * Run the current SQL statement for the given URI + */ + public runQueryStatement(uri: string, selection: ISelectionData, + title: string, queryInput: QueryInput): void { + this.doRunQuery(uri, selection, title, queryInput, true); + } + + /** + * Run Query implementation + */ + private doRunQuery(uri: string, selection: ISelectionData, + title: string, queryInput: QueryInput, + runCurrentStatement: boolean, runOptions?: ExecutionPlanOptions): void { + // Reuse existing query runner if it exists + let queryRunner: QueryRunner; + let info: QueryInfo; + + if (this._queryInfoMap.has(uri)) { + info = this._getQueryInfo(uri); + let existingRunner: QueryRunner = info.queryRunner; + + // If the query is already in progress, don't attempt to send it + if (existingRunner.isExecuting) { + return; + } + + // If the query is not in progress, we can reuse the query runner + queryRunner = existingRunner; + info.selection = []; + } else { + // We do not have a query runner for this editor, so create a new one + // and map it to the results uri + info = new QueryInfo(); + queryRunner = this.initQueryRunner(uri, title, info); + } + + this._getQueryInfo(uri).queryInput = queryInput; + + if (runCurrentStatement) { + queryRunner.runQueryStatement(selection); + } else { + queryRunner.runQuery(selection, runOptions); + } + } + + private initQueryRunner(uri: string, title: string, info: QueryInfo): QueryRunner { + let queryRunner: QueryRunner; + queryRunner = this._instantiationService.createInstance(QueryRunner, uri, title); + queryRunner.eventEmitter.on('resultSet', (resultSet) => { + this._fireQueryEvent(uri, 'resultSet', resultSet); + }); + queryRunner.eventEmitter.on('batchStart', (batch) => { + let link = undefined; + if (batch.selection) { + link = { + text: strings.format(LocalizedConstants.runQueryBatchStartLine, batch.selection.startLine + 1) + }; + } + let message = { + message: LocalizedConstants.runQueryBatchStartMessage, + batchId: batch.id, + isError: false, + time: new Date().toLocaleTimeString(), + link: link + }; + this._fireQueryEvent(uri, 'message', message); + info.selection.push(this._validateSelection(batch.selection)); + }); + queryRunner.eventEmitter.on('message', (message) => { + this._fireQueryEvent(uri, 'message', message); + }); + queryRunner.eventEmitter.on('complete', (totalMilliseconds) => { + this._onRunQueryComplete.fire(uri); + this._fireQueryEvent(uri, 'complete', totalMilliseconds); + }); + queryRunner.eventEmitter.on('start', () => { + this._onRunQueryStart.fire(uri); + this._fireQueryEvent(uri, 'start'); + }); + + info = new QueryInfo(); + info.queryRunner = queryRunner; + info.dataService = this._instantiationService.createInstance(DataService, uri); + this._queryInfoMap.set(uri, info); + return queryRunner; + } + + public cancelQuery(input: QueryRunner | string): void { + let queryRunner: QueryRunner; + + if (typeof input === 'string') { + if (this._queryInfoMap.has(input)) { + queryRunner = this._getQueryInfo(input).queryRunner; + } + } else { + queryRunner = input; + } + + if (queryRunner === undefined || !queryRunner.isExecuting) { + // TODO: Cannot cancel query as no query is running. + return; + } + + // Switch the spinner to canceling, which will be reset when the query execute sends back its completed event + // TODO indicate on the status bar that the query is being canceled + + // Cancel the query + queryRunner.cancelQuery().then(success => undefined, error => { + // On error, show error message and notify that the query is complete so that buttons and other status indicators + // can be correct + this._messageService.show(Severity.Error, strings.format(LocalizedConstants.msgCancelQueryFailed, error)); + this._fireQueryEvent(queryRunner.uri, 'complete', 0); + }); + + } + + public disposeQuery(ownerUri: string): Thenable { + // Get existing query runner + let queryRunner = this._getQueryRunner(ownerUri); + if (queryRunner) { + return queryRunner.dispose(); + } + return TPromise.as(null); + } + + // EDIT DATA METHODS ///////////////////////////////////////////////////// + initializeEdit(ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): void { + // Reuse existing query runner if it exists + let queryRunner: QueryRunner; + let info: QueryInfo; + + if (this._queryInfoMap.has(ownerUri)) { + info = this._getQueryInfo(ownerUri); + let existingRunner: QueryRunner = info.queryRunner; + + // If the initialization is already in progress + if (existingRunner.isExecuting) { + return; + } + + queryRunner = existingRunner; + } else { + // We do not have a query runner for this editor, so create a new one + // and map it to the results uri + queryRunner = this._instantiationService.createInstance(QueryRunner, ownerUri, ownerUri); + queryRunner.eventEmitter.on('resultSet', (resultSet) => { + this._fireQueryEvent(ownerUri, 'resultSet', resultSet); + }); + queryRunner.eventEmitter.on('batchStart', (batch) => { + let link = undefined; + if (batch.selection) { + link = { + text: strings.format(LocalizedConstants.runQueryBatchStartLine, batch.selection.startLine + 1), + uri: '' + }; + } + let message = { + message: LocalizedConstants.runQueryBatchStartMessage, + batchId: undefined, + isError: false, + time: new Date().toLocaleTimeString(), + link: link + }; + this._fireQueryEvent(ownerUri, 'message', message); + }); + queryRunner.eventEmitter.on('message', (message) => { + this._fireQueryEvent(ownerUri, 'message', message); + }); + queryRunner.eventEmitter.on('complete', (totalMilliseconds) => { + this._onRunQueryComplete.fire(ownerUri); + this._fireQueryEvent(ownerUri, 'complete', totalMilliseconds); + }); + queryRunner.eventEmitter.on('start', () => { + this._onRunQueryStart.fire(ownerUri); + this._fireQueryEvent(ownerUri, 'start'); + }); + queryRunner.eventEmitter.on('editSessionReady', (ownerUri, success, message) => { + this._onEditSessionReady.fire({ ownerUri: ownerUri, success: success, message: message }); + this._fireQueryEvent(ownerUri, 'editSessionReady'); + }); + + info = new QueryInfo(); + info.queryRunner = queryRunner; + info.dataService = this._instantiationService.createInstance(DataService, ownerUri); + this._queryInfoMap.set(ownerUri, info); + } + + queryRunner.initializeEdit(ownerUri, schemaName, objectName, objectType, rowLimit); + } + + public cancelInitializeEdit(input: QueryRunner | string): void { + // TODO: Implement query cancellation service + } + + public disposeEdit(ownerUri: string): Thenable { + // Get existing query runner + let queryRunner = this._getQueryRunner(ownerUri); + if (queryRunner) { + return queryRunner.disposeEdit(ownerUri); + } + return TPromise.as(null); + } + + public updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable { + // Get existing query runner + let queryRunner = this._getQueryRunner(ownerUri); + if (queryRunner) { + return queryRunner.updateCell(ownerUri, rowId, columnId, newValue).then((result) => result, error => { + this._messageService.show(Severity.Error, nls.localize('updateCellFailed', 'Update cell failed: ') + error.message); + return Promise.reject(error); + }); + } + return TPromise.as(null); + } + + public commitEdit(ownerUri): Thenable { + // Get existing query runner + let queryRunner = this._getQueryRunner(ownerUri); + if (queryRunner) { + return queryRunner.commitEdit(ownerUri).then(() => { }, error => { + this._messageService.show(Severity.Error, nls.localize('commitEditFailed', 'Commit row failed: ') + error.message); + return Promise.reject(error); + }); + } + return TPromise.as(null); + } + + public createRow(ownerUri: string): Thenable { + // Get existing query runner + let queryRunner = this._getQueryRunner(ownerUri); + if (queryRunner) { + return queryRunner.createRow(ownerUri); + } + return TPromise.as(null); + } + + public deleteRow(ownerUri: string, rowId: number): Thenable { + // Get existing query runner + let queryRunner = this._getQueryRunner(ownerUri); + if (queryRunner) { + return queryRunner.deleteRow(ownerUri, rowId); + } + return TPromise.as(null); + } + + public revertCell(ownerUri: string, rowId: number, columnId: number): Thenable { + // Get existing query runner + let queryRunner = this._getQueryRunner(ownerUri); + if (queryRunner) { + return queryRunner.revertCell(ownerUri, rowId, columnId); + } + return TPromise.as(null); + } + + public revertRow(ownerUri: string, rowId: number): Thenable { + // Get existing query runner + let queryRunner = this._getQueryRunner(ownerUri); + if (queryRunner) { + return queryRunner.revertRow(ownerUri, rowId); + } + return TPromise.as(null); + } + + // PRIVATE METHODS ////////////////////////////////////////////////////// + + private _getQueryRunner(ownerUri): QueryRunner { + let queryRunner: QueryRunner = undefined; + if (this._queryInfoMap.has(ownerUri)) { + let existingRunner = this._getQueryInfo(ownerUri).queryRunner; + // If the query is not already executing then set it up + if (!existingRunner.isExecuting) { + queryRunner = this._getQueryInfo(ownerUri).queryRunner; + } + } + // return undefined if not found or is already executing + return queryRunner; + } + + private _fireGridContentEvent(uri: string, type: string): void { + let info: QueryInfo = this._getQueryInfo(uri); + + if (info && info.dataServiceReady) { + let service: DataService = this.getDataService(uri); + if (service) { + // There is no need to queue up these events like there is for the query events because + // if the DataService is not yet ready there will be no grid content to update + service.gridContentObserver.next(type); + } + } + } + + private _fireQueryEvent(uri: string, type: string, data?: any) { + let info: QueryInfo = this._getQueryInfo(uri); + + if (info.dataServiceReady) { + let service: DataService = this.getDataService(uri); + service.queryEventObserver.next({ + type: type, + data: data + }); + } else { + let queueItem: QueryEvent = { type: type, data: data }; + info.queryEventQueue.push(queueItem); + } + } + + private _sendQueuedEvents(uri: string): void { + let info: QueryInfo = this._getQueryInfo(uri); + while (info.queryEventQueue.length > 0) { + let event: QueryEvent = info.queryEventQueue.shift(); + this._fireQueryEvent(uri, event.type, event.data); + } + } + + private _getQueryInfo(uri: string): QueryInfo { + return this._queryInfoMap.get(uri); + } + + // TODO remove this funciton and its usages when #821 in vscode-mssql is fixed and + // the SqlToolsService version is updated in this repo - coquagli 4/19/2017 + private _validateSelection(selection: ISelectionData): ISelectionData { + if (!selection) { + selection = {}; + } + selection.endColumn = selection ? Math.max(0, selection.endColumn) : 0; + selection.endLine = selection ? Math.max(0, selection.endLine) : 0; + selection.startColumn = selection ? Math.max(0, selection.startColumn) : 0; + selection.startLine = selection ? Math.max(0, selection.startLine) : 0; + return selection; + } +} diff --git a/src/sql/parts/query/execution/queryRunner.ts b/src/sql/parts/query/execution/queryRunner.ts new file mode 100644 index 0000000000..1f5e21aa6d --- /dev/null +++ b/src/sql/parts/query/execution/queryRunner.ts @@ -0,0 +1,525 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { + BatchSummary, + QueryCancelResult, + QueryExecuteBatchNotificationParams, + QueryExecuteCompleteNotificationResult, + QueryExecuteResultSetCompleteNotificationParams, + QueryExecuteMessageParams, + QueryExecuteSubsetParams, QueryExecuteSubsetResult, + EditSubsetParams, EditSubsetResult, EditUpdateCellResult, EditCreateRowResult, + EditRevertCellResult, ISelectionData, IResultMessage, ExecutionPlanOptions +} from 'data'; + +import { EventEmitter } from 'events'; +import * as Constants from 'sql/parts/query/common/constants'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; +import { IQueryManagementService } from 'sql/parts/query/common/queryManagement'; +import { ISlickRange } from 'angular2-slickgrid'; +import * as Utils from 'sql/parts/connection/common/utils'; +import { error as consoleError } from 'sql/base/common/log'; + +import { IMessageService } from 'vs/platform/message/common/message'; +import Severity from 'vs/base/common/severity'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import * as nls from 'vs/nls'; + +import * as os from 'os'; + +/* +* Query Runner class which handles running a query, reports the results to the content manager, +* and handles getting more rows from the service layer and disposing when the content is closed. +*/ +export default class QueryRunner { + // MEMBER VARIABLES //////////////////////////////////////////////////// + private _batchSets: BatchSummary[] = []; + private _isExecuting: boolean; + private _uri: string; + private _title: string; + private _resultLineOffset: number; + private _totalElapsedMilliseconds: number; + private _hasCompleted: boolean; + public eventEmitter: EventEmitter = new EventEmitter(); + + // CONSTRUCTOR ///////////////////////////////////////////////////////// + + constructor(private _ownerUri: string, + private _editorTitle: string, + @IQueryManagementService private _queryManagementService: IQueryManagementService, + @IMessageService private _messageService: IMessageService, + @IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService + ) { + + // Store the state + this._uri = _ownerUri; + this._title = _editorTitle; + this._isExecuting = false; + this._totalElapsedMilliseconds = 0; + this._hasCompleted = false; + } + + // PROPERTIES ////////////////////////////////////////////////////////// + + get uri(): string { + return this._uri; + } + + set uri(uri: string) { + this._uri = uri; + } + + get title(): string { + return this._title; + } + + set title(title: string) { + this._title = title; + } + + get batchSets(): BatchSummary[] { + return this._batchSets; + } + + set batchSets(batchSets: BatchSummary[]) { + this._batchSets = batchSets; + } + + get isExecuting(): boolean { + return this._isExecuting; + } + + get hasCompleted(): boolean { + return this._hasCompleted; + } + + // PUBLIC METHODS ====================================================== + + /** + * Cancels the running query, if there is one + */ + public cancelQuery(): Thenable { + return this._queryManagementService.cancelQuery(this._uri); + } + + /** + * Runs the query with the provided query + * @param input Query string to execute + */ + public runQuery(input: string, runOptions?: ExecutionPlanOptions): Thenable; + /** + * Runs the query by pulling the query from the document using the provided selection data + * @param input selection data + */ + public runQuery(input: ISelectionData, runOptions?: ExecutionPlanOptions): Thenable; + public runQuery(input, runOptions?: ExecutionPlanOptions): Thenable { + return this.doRunQuery(input, false, runOptions); + } + + /** + * Runs the current SQL statement by pulling the query from the document using the provided selection data + * @param input selection data + */ + public runQueryStatement(input: ISelectionData): Thenable { + return this.doRunQuery(input, true); + } + + /** + * Implementation that runs the query with the provided query + * @param input Query string to execute + */ + private doRunQuery(input: string, runCurrentStatement: boolean, runOptions?: ExecutionPlanOptions): Thenable; + private doRunQuery(input: ISelectionData, runCurrentStatement: boolean, runOptions?: ExecutionPlanOptions): Thenable; + private doRunQuery(input, runCurrentStatement: boolean, runOptions?: ExecutionPlanOptions): Thenable { + let ownerUri = this._uri; + this.batchSets = []; + this._hasCompleted = false; + if (typeof input === 'object' || input === undefined) { + // Update internal state to show that we're executing the query + this._resultLineOffset = input ? input.startLine : 0; + this._isExecuting = true; + this._totalElapsedMilliseconds = 0; + // TODO issue #228 add statusview callbacks here + + // Send the request to execute the query + return runCurrentStatement + ? this._queryManagementService.runQueryStatement(ownerUri, input.startLine, input.startColumn).then(this.handleSuccessRunQueryResult(), this.handleFailureRunQueryResult()) + : this._queryManagementService.runQuery(ownerUri, input, runOptions).then(this.handleSuccessRunQueryResult(), this.handleFailureRunQueryResult()); + } else if (typeof input === 'string') { + // Update internal state to show that we're executing the query + this._isExecuting = true; + this._totalElapsedMilliseconds = 0; + + return this._queryManagementService.runQueryString(ownerUri, input).then(this.handleSuccessRunQueryResult(), this.handleFailureRunQueryResult()); + } else { + return Promise.reject('Unknown input'); + } + } + + private handleSuccessRunQueryResult() { + let self = this; + return () => { + // The query has started, so lets fire up the result pane + self.eventEmitter.emit('start'); + self._queryManagementService.registerRunner(self, self._uri); + }; + } + + private handleFailureRunQueryResult() { + let self = this; + return (error: any) => { + // Attempting to launch the query failed, show the error message + + // TODO issue #228 add statusview callbacks here + self._isExecuting = false; + + self._messageService.show(Severity.Error, nls.localize('query.ExecutionFailedError', 'Execution failed: {0}', error)); + }; + } + + /** + * Handle a QueryComplete from the service layer + */ + public handleQueryComplete(result: QueryExecuteCompleteNotificationResult): void { + + // Store the batch sets we got back as a source of "truth" + this._isExecuting = false; + this._hasCompleted = true; + this._batchSets = result.batchSummaries ? result.batchSummaries : []; + + this._batchSets.map((batch) => { + if (batch.selection) { + batch.selection.startLine = batch.selection.startLine + this._resultLineOffset; + batch.selection.endLine = batch.selection.endLine + this._resultLineOffset; + } + }); + + // We're done with this query so shut down any waiting mechanisms + this.eventEmitter.emit('complete', Utils.parseNumAsTimeString(this._totalElapsedMilliseconds)); + } + + /** + * Handle a BatchStart from the service layer + */ + public handleBatchStart(result: QueryExecuteBatchNotificationParams): void { + let batch = result.batchSummary; + + // Recalculate the start and end lines, relative to the result line offset + if (batch.selection) { + batch.selection.startLine += this._resultLineOffset; + batch.selection.endLine += this._resultLineOffset; + } + + // Set the result sets as an empty array so that as result sets complete we can add to the list + batch.resultSetSummaries = []; + + // Store the batch + this._batchSets[batch.id] = batch; + this.eventEmitter.emit('batchStart', batch); + } + + /** + * Handle a BatchComplete from the service layer + */ + public handleBatchComplete(result: QueryExecuteBatchNotificationParams): void { + let batch: BatchSummary = result.batchSummary; + + // Store the batch again to get the rest of the data + this._batchSets[batch.id] = batch; + let executionTime = (Utils.parseTimeString(batch.executionElapsed) || 0); + this._totalElapsedMilliseconds += executionTime; + if (executionTime > 0) { + // send a time message in the format used for query complete + this.sendBatchTimeMessage(batch.id, Utils.parseNumAsTimeString(executionTime)); + } + this.eventEmitter.emit('batchComplete', batch); + } + + /** + * Handle a ResultSetComplete from the service layer + */ + public handleResultSetComplete(result: QueryExecuteResultSetCompleteNotificationParams): void { + if (result && result.resultSetSummary) { + let resultSet = result.resultSetSummary; + let batchSet: BatchSummary; + if (!resultSet.batchId) { + // Missing the batchId. In this case, default to always using the first batch in the list + // or create one in the case the DMP extension didn't obey the contract perfectly + if (this._batchSets.length > 0) { + batchSet = this._batchSets[0]; + } else { + batchSet = { + id: 0, + selection: undefined, + hasError: false, + resultSetSummaries: [] + }; + this._batchSets[0] = batchSet; + } + } else { + batchSet = this._batchSets[resultSet.batchId]; + } + if (batchSet) { + // Store the result set in the batch and emit that a result set has completed + batchSet.resultSetSummaries[resultSet.id] = resultSet; + this.eventEmitter.emit('resultSet', resultSet); + } + } + } + + /** + * Handle a Mssage from the service layer + */ + public handleMessage(obj: QueryExecuteMessageParams): void { + let message = obj.message; + message.time = new Date(message.time).toLocaleTimeString(); + + // Send the message to the results pane + this.eventEmitter.emit('message', message); + } + + /** + * Get more data rows from the current resultSets from the service layer + */ + public getQueryRows(rowStart: number, numberOfRows: number, batchIndex: number, resultSetIndex: number): Thenable { + const self = this; + let rowData: QueryExecuteSubsetParams = { + ownerUri: this.uri, + resultSetIndex: resultSetIndex, + rowsCount: numberOfRows, + rowsStartIndex: rowStart, + batchIndex: batchIndex + }; + + return new Promise((resolve, reject) => { + self._queryManagementService.getQueryRows(rowData).then(result => { + resolve(result); + }, error => { + self._messageService.show(Severity.Error, nls.localize('query.gettingRowsFailedError', 'Something went wrong getting more rows: {0}', error)); + reject(error); + }); + }); + } + + /* + * Handle a session ready event for Edit Data + */ + public initializeEdit(ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): Thenable { + const self = this; + + // Update internal state to show that we're executing the query + this._isExecuting = true; + this._totalElapsedMilliseconds = 0; + // TODO issue #228 add statusview callbacks here + + return this._queryManagementService.initializeEdit(ownerUri, schemaName, objectName, objectType, rowLimit).then(result => { + // The query has started, so lets fire up the result pane + self.eventEmitter.emit('start'); + self._queryManagementService.registerRunner(self, ownerUri); + }, error => { + // Attempting to launch the query failed, show the error message + + // TODO issue #228 add statusview callbacks here + self._isExecuting = false; + + self._messageService.show(Severity.Error, nls.localize('query.initEditExecutionFailed', 'Init Edit Execution failed: ') + error); + }); + } + + /** + * Retrieves a number of rows from an edit session + * @param rowStart The index of the row to start returning (inclusive) + * @param numberOfRows The number of rows to return + */ + public getEditRows(rowStart: number, numberOfRows: number): Thenable { + const self = this; + let rowData: EditSubsetParams = { + ownerUri: this.uri, + rowCount: numberOfRows, + rowStartIndex: rowStart + }; + + return new Promise((resolve, reject) => { + self._queryManagementService.getEditRows(rowData).then(result => { + if (!result.hasOwnProperty('rowCount')) { + let error = `Nothing returned from subset query`; + self._messageService.show(Severity.Error, error); + reject(error); + } + resolve(result); + }, error => { + let errorMessage = nls.localize('query.moreRowsFailedError', 'Something went wrong getting more rows:'); + self._messageService.show(Severity.Error, `${errorMessage} ${error}`); + reject(error); + }); + }); + } + + public handleEditSessionReady(ownerUri: string, success: boolean, message: string): void { + this.eventEmitter.emit('editSessionReady', ownerUri, success, message); + } + + public updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable { + return this._queryManagementService.updateCell(ownerUri, rowId, columnId, newValue); + } + + public commitEdit(ownerUri): Thenable { + return this._queryManagementService.commitEdit(ownerUri); + } + + public createRow(ownerUri: string): Thenable { + return this._queryManagementService.createRow(ownerUri).then(result => { + return result; + }); + } + + public deleteRow(ownerUri: string, rowId: number): Thenable { + return this._queryManagementService.deleteRow(ownerUri, rowId); + } + + public revertCell(ownerUri: string, rowId: number, columnId: number): Thenable { + return this._queryManagementService.revertCell(ownerUri, rowId, columnId).then(result => { + return result; + }); + } + + public revertRow(ownerUri: string, rowId: number): Thenable { + return this._queryManagementService.revertRow(ownerUri, rowId); + } + + public disposeEdit(ownerUri: string): Thenable { + return this._queryManagementService.disposeEdit(ownerUri); + } + + /** + * Disposes the Query from the service client + * @returns A promise that will be rejected if a problem occured + */ + public dispose(): Promise { + const self = this; + return new Promise((resolve, reject) => { + self._queryManagementService.disposeQuery(self.uri).then(result => { + resolve(); + }, error => { + consoleError('Failed disposing query: ' + error); + reject(error); + }); + }); + } + + get totalElapsedMilliseconds(): number { + return this._totalElapsedMilliseconds; + } + + /** + * Sends a copy request + * @param selection The selection range to copy + * @param batchId The batch id of the result to copy from + * @param resultId The result id of the result to copy from + * @param includeHeaders [Optional]: Should column headers be included in the copy selection + */ + copyResults(selection: ISlickRange[], batchId: number, resultId: number, includeHeaders?: boolean): void { + const self = this; + let copyString = ''; + + // create a mapping of the ranges to get promises + let tasks = selection.map((range, i) => { + return () => { + return self.getQueryRows(range.fromRow, range.toRow - range.fromRow + 1, batchId, resultId).then((result) => { + if (self.shouldIncludeHeaders(includeHeaders)) { + let columnHeaders = self.getColumnHeaders(batchId, resultId, range); + if (columnHeaders !== undefined) { + copyString += columnHeaders.join('\t') + os.EOL; + } + } + + // Iterate over the rows to paste into the copy string + for (let rowIndex: number = 0; rowIndex < result.resultSubset.rows.length; rowIndex++) { + let row = result.resultSubset.rows[rowIndex]; + let cellObjects = row.slice(range.fromCell, (range.toCell + 1)); + // Remove newlines if requested + let cells = self.shouldRemoveNewLines() + ? cellObjects.map(x => self.removeNewLines(x.displayValue)) + : cellObjects.map(x => x.displayValue); + copyString += cells.join('\t'); + if (rowIndex < result.resultSubset.rows.length - 1) { + copyString += os.EOL; + } + } + }); + }; + }); + + if (tasks.length > 0) { + let p = tasks[0](); + for (let i = 1; i < tasks.length; i++) { + p = p.then(tasks[i]); + } + p.then(() => { + WorkbenchUtils.executeCopy(copyString); + }); + } + } + + private shouldIncludeHeaders(includeHeaders: boolean): boolean { + if (includeHeaders !== undefined) { + // Respect the value explicity passed into the method + return includeHeaders; + } + // else get config option from vscode config + includeHeaders = WorkbenchUtils.getSqlConfigValue(this._workspaceConfigurationService, Constants.copyIncludeHeaders); + return !!includeHeaders; + } + + private shouldRemoveNewLines(): boolean { + // get config copyRemoveNewLine option from vscode config + let removeNewLines: boolean = WorkbenchUtils.getSqlConfigValue(this._workspaceConfigurationService, Constants.configCopyRemoveNewLine); + return !!removeNewLines; + } + + private getColumnHeaders(batchId: number, resultId: number, range: ISlickRange): string[] { + let headers: string[] = undefined; + let batchSummary: BatchSummary = this.batchSets[batchId]; + if (batchSummary !== undefined) { + let resultSetSummary = batchSummary.resultSetSummaries[resultId]; + headers = resultSetSummary.columnInfo.slice(range.fromCell, range.toCell + 1).map((info, i) => { + return info.columnName; + }); + } + return headers; + } + + private removeNewLines(inputString: string): string { + // This regex removes all newlines in all OS types + // Windows(CRLF): \r\n + // Linux(LF)/Modern MacOS: \n + // Old MacOs: \r + if (!inputString) { + return 'null'; + } + + let outputString: string = inputString.replace(/(\r\n|\n|\r)/gm, ''); + return outputString; + } + + private sendBatchTimeMessage(batchId: number, executionTime: string): void { + // get config copyRemoveNewLine option from vscode config + let showBatchTime: boolean = WorkbenchUtils.getSqlConfigValue(this._workspaceConfigurationService, Constants.configShowBatchTime); + if (showBatchTime) { + let message: IResultMessage = { + batchId: batchId, + message: nls.localize('elapsedBatchTime', 'Batch execution time: {0}', executionTime), + time: undefined, + isError: false + }; + // Send the message to the results pane + this.eventEmitter.emit('message', message); + } + } + +} diff --git a/src/sql/parts/query/execution/queryStatus.ts b/src/sql/parts/query/execution/queryStatus.ts new file mode 100644 index 0000000000..30ae6f454f --- /dev/null +++ b/src/sql/parts/query/execution/queryStatus.ts @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { $, append, show, hide } from 'vs/base/browser/dom'; +import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { IEditorCloseEvent } from 'vs/workbench/common/editor'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import LocalizedConstants = require('sql/parts/query/common/localizedConstants'); +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; + +// Query execution status +enum QueryExecutionStatus{ + Executing, + Completed +} + +// Shows query status in the editor +export class QueryStatusbarItem implements IStatusbarItem { + + private _element: HTMLElement; + private _queryElement: HTMLElement; + private _queryStatusEditors: { [editorUri: string]: QueryExecutionStatus }; + private _toDispose: IDisposable[]; + + constructor( + @IQueryModelService private _queryModelService: IQueryModelService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IEditorGroupService private _editorGroupService: IEditorGroupService, + ) { + this._queryStatusEditors = {}; + } + + public render(container: HTMLElement): IDisposable { + this._element = append(container, $('.query-statusbar-group')); + this._queryElement = append(this._element, $('div.query-statusbar-item')); + hide(this._queryElement); + + this._toDispose = []; + this._toDispose.push( + this._queryModelService.onRunQueryStart((uri:string) => this._onRunQueryStart(uri)), + this._queryModelService.onRunQueryComplete((uri:string) => this._onRunQueryComplete(uri)), + this._editorGroupService.onEditorsChanged(() => this._onEditorsChanged()), + this._editorGroupService.getStacksModel().onEditorClosed(event => this._onEditorClosed(event)) + ); + + return combinedDisposable(this._toDispose); + } + + private _onEditorClosed(event: IEditorCloseEvent): void{ + let uri = WorkbenchUtils.getEditorUri(event.editor); + if (uri && uri in this._queryStatusEditors) { + // If active editor is being closed, hide the query status. + let activeEditor = this._editorService.getActiveEditor(); + if (activeEditor) { + let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); + if (uri === currentUri) { + hide(this._queryElement); + } + } + delete this._queryStatusEditors[uri]; + } + } + + private _onEditorsChanged(): void{ + let activeEditor = this._editorService.getActiveEditor(); + if (activeEditor) { + let uri = WorkbenchUtils.getEditorUri(activeEditor.input); + + // Show active editor's query status + if (uri && uri in this._queryStatusEditors){ + this._showStatus(uri); + } else { + hide(this._queryElement); + } + } else { + hide(this._queryElement); + } + } + + private _onRunQueryStart(uri: string): void { + this._updateStatus(uri, QueryExecutionStatus.Executing); + } + + private _onRunQueryComplete(uri: string): void { + this._updateStatus(uri, QueryExecutionStatus.Completed); + } + + // Update query status for the editor + private _updateStatus(uri: string, newStatus: QueryExecutionStatus){ + if (uri) { + this._queryStatusEditors[uri] = newStatus; + this._showStatus(uri); + } + } + + // Show/hide query status for active editor + private _showStatus(uri: string): void{ + let activeEditor = this._editorService.getActiveEditor(); + if (activeEditor) { + let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); + if (uri === currentUri) { + switch(this._queryStatusEditors[uri]){ + case QueryExecutionStatus.Executing: + this._queryElement.textContent = LocalizedConstants.msgStatusRunQueryInProgress; + show(this._queryElement); + break; + default: + hide(this._queryElement); + } + } + } + } +} diff --git a/src/sql/parts/query/services/queryEditorService.ts b/src/sql/parts/query/services/queryEditorService.ts new file mode 100644 index 0000000000..f07c998a43 --- /dev/null +++ b/src/sql/parts/query/services/queryEditorService.ts @@ -0,0 +1,337 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEditorGroup } from 'vs/workbench/common/editor'; +import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; +import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; +import { QueryInput } from 'sql/parts/query/common/queryInput'; +import { EditDataInput } from 'sql/parts/editData/common/editDataInput'; +import URI from 'vs/base/common/uri'; +import { IConnectableInput } from 'sql/parts/connection/common/connectionManagement'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IMode } from 'vs/editor/common/modes'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IEditor, IEditorInput, Position } from 'vs/platform/editor/common/editor'; +import { CodeEditor } from 'vs/editor/browser/codeEditor'; +import { IQueryEditorService, IQueryEditorOptions } from 'sql/parts/query/common/queryEditorService'; +import { IMessageService } from 'vs/platform/message/common/message'; +import Severity from 'vs/base/common/severity'; +import nls = require('vs/nls'); +import { QueryPlanInput } from 'sql/parts/queryPlan/queryPlanInput'; +import { sqlModeId, untitledFilePrefix, getSupportedInputResource } from 'sql/parts/common/customInputConverter'; + +const fs = require('fs'); + +/** + * Service wrapper for opening and creating SQL documents as sql editor inputs + */ +export class QueryEditorService implements IQueryEditorService { + + public _serviceBrand: any; + + private static CHANGE_UNSUPPORTED_ERROR_MESSAGE = nls.localize( + 'queryEditorServiceChangeUnsupportedError', + 'Change Language Mode is not supported for unsaved queries' + ); + + private static CHANGE_ERROR_MESSAGE = nls.localize( + 'queryEditorServiceChangeError', + 'Please save or discard changes before switching to/from the SQL Language Mode' + ); + + // service references for static functions + private static editorService: IWorkbenchEditorService; + private static instantiationService: IInstantiationService; + private static editorGroupService: IEditorGroupService; + private static messageService: IMessageService; + + constructor( + @IUntitledEditorService private _untitledEditorService: IUntitledEditorService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IEditorGroupService private _editorGroupService: IEditorGroupService, + @IMessageService private _messageService: IMessageService, + ) { + QueryEditorService.editorService = _editorService; + QueryEditorService.instantiationService = _instantiationService; + QueryEditorService.editorGroupService = _editorGroupService; + QueryEditorService.messageService = _messageService; + } + + ////// Public functions + + /** + * Creates new untitled document for SQL query and opens in new editor tab + */ + public newSqlEditor(sqlContent?: string, connectionProviderName?: string): Promise { + return new Promise((resolve, reject) => { + try { + // Create file path and file URI + let filePath = this.createUntitledSqlFilePath(); + let docUri: URI = URI.from({ scheme: UNTITLED_SCHEMA, path: filePath }); + + // Create a sql document pane with accoutrements + const fileInput = this._untitledEditorService.createOrGet(docUri, 'sql'); + fileInput.resolve().then(m => { + if (sqlContent) { + m.textEditorModel.setValue(sqlContent); + } + }); + + //input.resolve().then(model => this.backupFileService.backupResource(resource, model.getValue(), model.getVersionId())).done(null, errors.onUnexpectedError); + + const queryResultsInput: QueryResultsInput = this._instantiationService.createInstance(QueryResultsInput, docUri.toString()); + let queryInput: QueryInput = this._instantiationService.createInstance(QueryInput, fileInput.getName(), '', fileInput, queryResultsInput, connectionProviderName); + + this._editorService.openEditor(queryInput, { pinned: true }) + .then((editor) => { + let params = editor.input; + resolve(params); + }, (error) => { + reject(error); + }); + } catch (error) { + reject(error); + } + }); + } + + // Creates a new query plan document + public newQueryPlanEditor(xmlShowPlan: string): Promise { + const self = this; + return new Promise((resolve, reject) => { + let queryPlanInput: QueryPlanInput = self._instantiationService.createInstance(QueryPlanInput, xmlShowPlan, 'aaa', undefined); + self._editorService.openEditor(queryPlanInput, { pinned: true }, false); + resolve(true); + }); + } + + /** + * Creates new edit data session + */ + public newEditDataEditor(schemaName: string, tableName: string): Promise { + + return new Promise((resolve, reject) => { + try { + // Create file path and file URI + let objectName = schemaName ? schemaName + '.' + tableName : tableName; + let filePath = this.createEditDataFileName(objectName); + let docUri: URI = URI.from({ scheme: UNTITLED_SCHEMA, path: filePath }); + + // Create an EditDataInput for editing + let editDataInput: EditDataInput = this._instantiationService.createInstance(EditDataInput, docUri, schemaName, tableName); + + this._editorService.openEditor(editDataInput, { pinned: true }) + .then((editor) => { + let params = editor.input; + resolve(params); + }, (error) => { + reject(error); + }); + } catch (error) { + reject(error); + } + }); + } + + /** + * Clears any QueryEditor data for the given URI held by this service + */ + public onQueryInputClosed(uri: string): void { + } + + ////// Public static functions + // These functions are static to reduce extra lines needed in the vscode code base + + /** + * Checks if the Language Mode is being changed to/from SQL. If so, swaps out the input of the + * given editor with a new input, opens a new editor, then returns the new editor's IModel. + * + * Returns an immediately resolved promise if the SQL Language mode is not involved. In this case, + * the calling function in editorStatus.ts will handle the language change normally. + * + * Returns an immediately resolved promise with undefined if SQL is involved in the language change + * and the editor is dirty. In this case, the calling function in editorStatus.ts will not perform + * the language change. TODO: change this - tracked by issue #727 + * + * In all other cases (when SQL is involved in the language change and the editor is not dirty), + * returns a promise that will resolve when the old editor has been replaced by a new editor. + */ + public static sqlLanguageModeCheck(model: IModel, mode: IMode, editor: IEditor): Promise { + if (!model || !mode || !editor) { + return Promise.resolve(undefined); + } + + let newLanguage: string = mode.getLanguageIdentifier().language; + let oldLanguage: string = model.getLanguageIdentifier().language; + let changingToSql = sqlModeId === newLanguage; + let changingFromSql = sqlModeId === oldLanguage; + let changingLanguage = newLanguage !== oldLanguage; + + if (!changingLanguage) { + return Promise.resolve(model); + } + if (!changingFromSql && !changingToSql) { + return Promise.resolve(model); + } + + let uri: URI = QueryEditorService._getEditorChangeUri(editor.input, changingToSql); + if(uri.scheme === UNTITLED_SCHEMA && editor.input instanceof QueryInput) + { + QueryEditorService.messageService.show(Severity.Error, QueryEditorService.CHANGE_UNSUPPORTED_ERROR_MESSAGE); + return Promise.resolve(undefined); + } + + // Return undefined to notify the calling funciton to not perform the language change + // TODO change this - tracked by issue #727 + if (editor.input.isDirty()) { + QueryEditorService.messageService.show(Severity.Error, QueryEditorService.CHANGE_ERROR_MESSAGE); + return Promise.resolve(undefined); + } + + let group: IEditorGroup = QueryEditorService.editorGroupService.getStacksModel().groupAt(editor.position); + let index: number = group.indexOf(editor.input); + let position: Position = editor.position; + let options: IQueryEditorOptions = editor.options ? editor.options : {}; + options.index = index; + options.pinned = group.isPinned(index); + + // Return a promise that will resovle when the old editor has been replaced by a new editor + return new Promise((resolve, reject) => { + let newEditorInput = QueryEditorService._getNewEditorInput(changingToSql, editor.input, uri); + + // Override queryEditorCheck to not open this file in a QueryEditor + if (!changingToSql) { + options.denyQueryEditor = true; + } + + // Close the current editor + QueryEditorService.editorService.closeEditor(position, editor.input).then(() => { + + // Reopen a new editor in the same position/index + QueryEditorService.editorService.openEditor(newEditorInput, options, position).then((editor) => { + resolve(QueryEditorService._onEditorOpened(editor, uri.toString(), position, options.pinned)); + }, + (error) => { + reject(error); + }); + }); + }); + } + + ////// Private functions + + private createUntitledSqlFilePath(): string { + let sqlFileName = (counter: number): string => { + return `${untitledFilePrefix}${counter}`; + }; + + let counter = 1; + // Get document name and check if it exists + let filePath = sqlFileName(counter); + while (fs.existsSync(filePath)) { + counter++; + filePath = sqlFileName(counter); + } + + // check if this document name already exists in any open documents + let untitledEditors = this._untitledEditorService.getAll(); + while (untitledEditors.find(x => x.getName().toUpperCase() === filePath.toUpperCase())) { + counter++; + filePath = sqlFileName(counter); + } + + return filePath; + } + + private createEditDataFileName(tableName: string): string { + let editDataFileName = (counter: number): string => { + return encodeURIComponent(`${tableName}_${counter}`); + }; + + let counter = 1; + // Get document name and check if it exists + let filePath = editDataFileName(counter); + while (fs.existsSync(filePath)) { + counter++; + filePath = editDataFileName(counter); + } + + // TODO: check if this document name already exists in any open documents tabs + let fileNames: string[] = []; + this._editorGroupService.getStacksModel().groups.map(group => group.getEditors().map(editor => fileNames.push(editor.getName()))); + while (fileNames.find(x => x.toUpperCase() === filePath.toUpperCase())) { + counter++; + filePath = editDataFileName(counter); + } + + return filePath; + } + + ////// Private static functions + + /** + * Returns a QueryInput if we are changingToSql. Returns a FileEditorInput if we are !changingToSql. + */ + private static _getNewEditorInput(changingToSql: boolean, input: IEditorInput, uri: URI): IEditorInput { + if (!uri) { + return undefined; + } + + let newEditorInput: IEditorInput = undefined; + if (changingToSql) { + const queryResultsInput: QueryResultsInput = QueryEditorService.instantiationService.createInstance(QueryResultsInput, uri.toString()); + let queryInput: QueryInput = QueryEditorService.instantiationService.createInstance(QueryInput, input.getName(), '', input, queryResultsInput, undefined); + newEditorInput = queryInput; + } else { + let uriCopy: URI = URI.from( { scheme: uri.scheme, authority: uri.authority, path: uri.path, query: uri.query, fragment: uri.fragment } ); + newEditorInput = QueryEditorService.instantiationService.createInstance(FileEditorInput, uriCopy, undefined); + } + + return newEditorInput; + } + + /** + * Gets the URI for this IEditorInput or returns undefined if one does not exist. + */ + private static _getEditorChangeUri(input: IEditorInput, changingToSql: boolean): URI { + let uriSource: IEditorInput = input; + + // It is assumed that if we got here, !changingToSql is logically equivalent to changingFromSql + let changingFromSql = !changingToSql; + if (input instanceof QueryInput && changingFromSql) { + let queryInput: QueryInput = input; + uriSource = queryInput.sql; + } + return getSupportedInputResource(uriSource); + } + + /** + * Handle all cleanup actions that need to wait until the editor is fully open. + */ + private static _onEditorOpened(editor: IEditor, uri: string, position: Position, isPinned: boolean): IModel { + + // Reset the editor pin state + // TODO: change this so it happens automatically in openEditor in sqlLanguageModeCheck. Performing this here + // causes the text on the tab to slightly flicker for unpinned files (from non-italic to italic to non-italic). + // This is currently unavoidable because vscode ignores "pinned" on IEditorOptions if "index" is not undefined, + // and we need to specify "index"" so the editor tab remains in the same place + let group: IEditorGroup = QueryEditorService.editorGroupService.getStacksModel().groupAt(position); + if (isPinned) { + QueryEditorService.editorGroupService.pinEditor(group, editor.input); + } else { + QueryEditorService.editorGroupService.unpinEditor(group, editor.input); + } + + // Grab and returns the IModel that will be used to resolve the sqlLanguageModeCheck promise. + let control = editor.getControl(); + let codeEditor: CodeEditor = control; + let newModel = codeEditor ? codeEditor.getModel() : undefined; + return newModel; + } +} diff --git a/src/sql/parts/query/views/flexibleSash.ts b/src/sql/parts/query/views/flexibleSash.ts new file mode 100644 index 0000000000..68b21f9e50 --- /dev/null +++ b/src/sql/parts/query/views/flexibleSash.ts @@ -0,0 +1,271 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Disposable } from 'vs/base/common/lifecycle'; +import { Dimension } from 'vs/base/browser/builder'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IHorizontalSashLayoutProvider, IVerticalSashLayoutProvider, + ISashEvent, Orientation, VSash, Sash } from 'vs/base/browser/ui/sash/sash'; +// There is no need to import the sash CSS - 'vs/base/browser/ui/sash/sash' already includes it + +/** + * Interface describing a sash that could be horizontal or vertical. This interface allows classes + * using the sash to have UI logic that is agnostic of the orientation of the sash. + */ +export interface IFlexibleSash { + + // Get the value of the CSS property denoted by getMajorPosition() + getSplitPoint(): number; + + // Sets the Dimension containing the height and width of the editor this sash will separate + setDimenesion(dimension: Dimension); + + // Re-calculates the width and height of the sash + layout(): void; + + // Hides the sash + hide(): void; + + // Shows/unhides the sash + show(): void; + + // Sets the top or left property of this sash + setEdge(edge: number); + + // Fired when the position of this sash changes + onPositionChange: Event; +} + +/** + * A simple Vertical Sash that computes the position of the sash when it is moved between the given dimension. + * Triggers onPositionChange event when the position is changed. Implements IFlexibleSash to enable classes to be + * agnostic of the fact that this sash is vertical. + */ + + +export class VerticalFlexibleSash extends Disposable implements IVerticalSashLayoutProvider, IFlexibleSash { + + private sash: Sash; + private ratio: number; + private startPosition: number; + private position: number; + private dimension: Dimension; + private top: number; + + private _onPositionChange: Emitter = new Emitter(); + public get onPositionChange(): Event { return this._onPositionChange.event; } + + constructor(container: HTMLElement, private minWidth: number) { + super(); + this.ratio = 0.5; + this.top = 0; + this.sash = new Sash(container, this); + + this._register(this.sash.addListener('start', () => this.onSashDragStart())); + this._register(this.sash.addListener('change', (e: ISashEvent) => this.onSashDrag(e))); + this._register(this.sash.addListener('end', () => this.onSashDragEnd())); + this._register(this.sash.addListener('reset', () => this.onSashReset())); + } + + public getSplitPoint(): number { + return this.getVerticalSashLeft(); + } + + public layout(): void { + this.sash.layout(); + } + + public show(): void { + this.sash.show(); + } + + public hide(): void { + this.sash.hide(); + } + + public getVerticalSashTop(): number { + return this.top; + } + + public getVerticalSashLeft(): number { + return this.position; + } + + public getVerticalSashHeight(): number { + return this.dimension.height; + } + + public setDimenesion(dimension: Dimension) { + this.dimension = dimension; + this.compute(this.ratio); + } + + public setEdge(edge: number) { + this.top = edge; + } + + private onSashDragStart(): void { + this.startPosition = this.position; + } + + private onSashDrag(e: ISashEvent): void { + this.compute((this.startPosition + (e.currentX - e.startX)) / this.dimension.width); + } + + private compute(ratio: number) { + this.computeSashPosition(ratio); + this.ratio = this.position / this.dimension.width; + this._onPositionChange.fire(this.position); + } + + private onSashDragEnd(): void { + this.sash.layout(); + } + + private onSashReset(): void { + this.ratio = 0.5; + this._onPositionChange.fire(this.position); + this.sash.layout(); + } + + private computeSashPosition(sashRatio: number = this.ratio) { + let contentWidth = this.dimension.width; + let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth); + let midPoint = Math.floor(0.5 * contentWidth); + + if (contentWidth > this.minWidth * 2) { + if (sashPosition < this.minWidth) { + sashPosition = this.minWidth; + } + if (sashPosition > contentWidth - this.minWidth) { + sashPosition = contentWidth - this.minWidth; + } + } else { + sashPosition = midPoint; + } + if (this.position !== sashPosition) { + this.position = sashPosition; + this.sash.layout(); + } + } + +} + +/** + * A simple Horizontal Sash that computes the position of the sash when it is moved between the given dimension. + * Triggers onPositionChange event when the position is changed. Implements IFlexibleSash to enable classes to be + * agnostic of the fact that this sash is horizontal. Based off the VSash class. + */ +export class HorizontalFlexibleSash extends Disposable implements IHorizontalSashLayoutProvider, IFlexibleSash { + + private sash: Sash; + private ratio: number; + private startPosition: number; + private position: number; + private dimension: Dimension; + private left: number; + + private _onPositionChange: Emitter = new Emitter(); + public get onPositionChange(): Event { return this._onPositionChange.event; } + + constructor(container: HTMLElement, private minHeight: number) { + super(); + this.ratio = 0.5; + this.left = 0; + this.sash = new Sash(container, this, { orientation: Orientation.HORIZONTAL }); + + this._register(this.sash.addListener('start', () => this.onSashDragStart())); + this._register(this.sash.addListener('change', (e: ISashEvent) => this.onSashDrag(e))); + this._register(this.sash.addListener('end', () => this.onSashDragEnd())); + this._register(this.sash.addListener('reset', () => this.onSashReset())); + } + + public getSplitPoint(): number { + return this.getHorizontalSashTop(); + } + + public getHorizontalSashLeft(): number { + return this.left; + } + + public getHorizontalSashTop(): number { + return this.position; + } + + public layout(): void { + this.sash.layout(); + } + + public show(): void { + this.sash.show(); + } + + public hide(): void { + this.sash.hide(); + } + + public getHorizontalSashWidth?(): number { + return this.dimension.width; + } + + public setDimenesion(dimension: Dimension) { + this.dimension = dimension; + this.compute(this.ratio); + } + + public setEdge(edge: number) { + this.left = edge; + } + + private onSashDragStart(): void { + this.startPosition = this.position; + } + + private onSashDrag(e: ISashEvent): void { + this.compute((this.startPosition + (e.currentY - e.startY)) / this.dimension.height); + } + + private compute(ratio: number) { + this.computeSashPosition(ratio); + this.ratio = this.position / this.dimension.height; + this._onPositionChange.fire(this.position); + } + + private onSashDragEnd(): void { + this.sash.layout(); + } + + private onSashReset(): void { + this.ratio = 0.5; + this._onPositionChange.fire(this.position); + this.sash.layout(); + } + + /** + * Computes where the sash should be located and re-renders the sash. + */ + private computeSashPosition(sashRatio: number = this.ratio) { + let contentHeight = this.dimension.height; + let sashPosition = Math.floor((sashRatio || 0.5) * contentHeight); + let midPoint = Math.floor(0.5 * contentHeight); + + if (contentHeight > this.minHeight * 2) { + if (sashPosition < this.minHeight) { + sashPosition = this.minHeight; + } + if (sashPosition > contentHeight - this.minHeight) { + sashPosition = contentHeight - this.minHeight; + } + } else { + sashPosition = midPoint; + } + if (this.position !== sashPosition) { + this.position = sashPosition; + this.sash.layout(); + } + } +} \ No newline at end of file diff --git a/src/sql/parts/query/views/queryOutput.component.html b/src/sql/parts/query/views/queryOutput.component.html new file mode 100644 index 0000000000..bf96d28a0d --- /dev/null +++ b/src/sql/parts/query/views/queryOutput.component.html @@ -0,0 +1,33 @@ + + + + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ + +
+
+
\ No newline at end of file diff --git a/src/sql/parts/query/views/queryOutput.component.ts b/src/sql/parts/query/views/queryOutput.component.ts new file mode 100644 index 0000000000..0172a669a7 --- /dev/null +++ b/src/sql/parts/query/views/queryOutput.component.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!sql/parts/grid/media/slickColorTheme'; +import 'vs/css!sql/parts/grid/media/flexbox'; +import 'vs/css!sql/parts/grid/media/styles'; +import 'vs/css!sql/parts/grid/media/slick.grid'; +import 'vs/css!sql/parts/grid/media/slickGrid'; + +import { ElementRef, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, forwardRef, ViewChild } from '@angular/core'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { QueryComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { QueryComponent } from 'sql/parts/grid/views/query/query.component'; +import { QueryPlanComponent } from 'sql/parts/queryPlan/queryPlan.component'; +import { TopOperationsComponent } from 'sql/parts/queryPlan/topOperations.component'; +import { ChartViewerComponent } from 'sql/parts/grid/views/query/chartViewer.component'; +import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils'; +import { PanelComponent, IPanelOptions } from 'sql/base/browser/ui/panel/panel.component'; + +import * as nls from 'vs/nls'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export const QUERY_OUTPUT_SELECTOR: string = 'query-output-component'; + +declare type PaneType = 'messages' | 'results'; + +@Component({ + selector: QUERY_OUTPUT_SELECTOR, + templateUrl: decodeURI(require.toUrl('sql/parts/query/views/queryOutput.component.html')) +}) +export class QueryOutputComponent implements OnInit, OnDestroy { + + @ViewChild('queryComponent') queryComponent: QueryComponent; + + @ViewChild('queryPlanComponent') queryPlanComponent: QueryPlanComponent; + + @ViewChild('topOperationsComponent') topOperationsComponent: TopOperationsComponent; + + @ViewChild('chartViewerComponent') chartViewerComponent: ChartViewerComponent; + + @ViewChild(PanelComponent) private _panel: PanelComponent; + + // tslint:disable:no-unused-variable + private readonly queryComponentTitle: string = nls.localize('results', 'Results'); + private readonly queryPlanTitle: string = nls.localize('queryPlan', 'Query Plan'); + private readonly topOperationsTitle: string = nls.localize('topOperations', 'Top Operations'); + private readonly chartViewerTitle: string = nls.localize('chartViewer', 'Chart Viewer'); + + private readonly resultsTabIdentifier = 'results'; + private readonly queryPlanTabIdentifier = 'queryPlan'; + private readonly chartViewerTabIdentifier = 'chartViewer'; + // tslint:enable:no-unused-variable + + private hasQueryPlan = false; + private showChartView = false; + + // tslint:disable-next-line:no-unused-variable + private readonly panelOpt: IPanelOptions = { + showTabsWhenOne: false + }; + + public queryParameters: QueryComponentParams; + + private _disposables: Array = []; + + constructor( + @Inject(forwardRef(() => ElementRef)) el: ElementRef, + @Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef, + @Inject(BOOTSTRAP_SERVICE_ID) bootstrapService: IBootstrapService + ) { + this.queryParameters = bootstrapService.getBootstrapParams(el.nativeElement.tagName); + } + + /** + * Called by Angular when the object is initialized + */ + public ngOnInit(): void { + this._disposables.push(toDisposableSubscription(this.queryComponent.queryPlanAvailable.subscribe((xml) => { + this.hasQueryPlan = true; + this._cd.detectChanges(); + this._panel.selectTab(this.queryPlanTabIdentifier); + this.queryPlanComponent.planXml = xml; + this.topOperationsComponent.planXml = xml; + }))); + + this._disposables.push(toDisposableSubscription(this.queryComponent.showChartRequested.subscribe((dataSet) => { + this.showChartView = true; + this._cd.detectChanges(); + this.chartViewerComponent.dataSet = dataSet; + this._panel.selectTab(this.chartViewerTabIdentifier); + }))); + + this._disposables.push(toDisposableSubscription(this.queryComponent.queryExecutionStatus.subscribe(status => { + if (status === 'start') { + this._panel.selectTab(this.resultsTabIdentifier); + this.hasQueryPlan = false; + this.showChartView = false; + this._cd.detectChanges(); + } + }))); + } + + public ngOnDestroy(): void { + this._disposables.forEach(i => i.dispose()); + } +} diff --git a/src/sql/parts/query/views/queryOutput.module.ts b/src/sql/parts/query/views/queryOutput.module.ts new file mode 100644 index 0000000000..ed74b367c0 --- /dev/null +++ b/src/sql/parts/query/views/queryOutput.module.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + + +import { ApplicationRef, ComponentFactoryResolver, forwardRef, NgModule, Inject } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; +import { SlickGrid } from 'angular2-slickgrid'; +import { ChartsModule } from 'ng2-charts/ng2-charts'; + +const BrowserAnimationsModule = (require.__$__nodeRequire('@angular/platform-browser/animations')).BrowserAnimationsModule; + +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry'; + +import { Registry } from 'vs/platform/registry/common/platform'; + + +import { QueryOutputComponent, QUERY_OUTPUT_SELECTOR } from 'sql/parts/query/views/queryOutput.component'; +import { QueryPlanComponent, } from 'sql/parts/queryPlan/queryPlan.component'; +import { QueryComponent } from 'sql/parts/grid/views/query/query.component'; +import { TopOperationsComponent } from 'sql/parts/queryPlan/topOperations.component'; + +import { ChartViewerComponent } from 'sql/parts/grid/views/query/chartViewer.component'; + +import { PanelModule } from 'sql/base/browser/ui/panel/panel.module'; + +/* Directives */ +import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive'; +import { MouseDownDirective } from 'sql/parts/grid/directives/mousedown.directive'; +import { ScrollDirective } from 'sql/parts/grid/directives/scroll.directive'; + +let baseComponents = [QueryComponent, ComponentHostDirective, QueryOutputComponent, QueryPlanComponent, TopOperationsComponent, ChartViewerComponent]; +/* Insights */ +let insightComponents = Registry.as(Extensions.InsightContribution).getAllCtors(); + +@NgModule({ + imports: [ + CommonModule, + BrowserModule, + FormsModule, + BrowserAnimationsModule, + ChartsModule, + PanelModule + ], + declarations: [ + ...baseComponents, + ...insightComponents, + SlickGrid, + ScrollDirective, + MouseDownDirective + ], + entryComponents: [ + QueryOutputComponent, + ...insightComponents + ] +}) +export class QueryOutputModule { + + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { + } + + ngDoBootstrap(appRef: ApplicationRef) { + const factory = this._resolver.resolveComponentFactory(QueryOutputComponent); + const uniqueSelector: string = this._bootstrapService.getUniqueSelector(QUERY_OUTPUT_SELECTOR); + (factory).factory.selector = uniqueSelector; + appRef.bootstrap(factory); + } +} diff --git a/src/sql/parts/queryPlan/planXmlParser.ts b/src/sql/parts/queryPlan/planXmlParser.ts new file mode 100644 index 0000000000..2a86a83142 --- /dev/null +++ b/src/sql/parts/queryPlan/planXmlParser.ts @@ -0,0 +1,295 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export class RunTimeInformation { + runtimePerThreads: RuntimePerThread[]; + public get actualRows(): number { + let total = 0; + if (this.runtimePerThreads) { + this.runtimePerThreads.forEach(element => { + total += element.actualRow; + }); + } + + return total; + } + + public get actualExecutions(): number { + let total = 0; + if (this.runtimePerThreads) { + this.runtimePerThreads.forEach(element => { + total += element.actualExecutions; + }); + } + return total; + } +} + +export class RuntimePerThread { + threadId: number; + actualRow: number; + actualExecutionMode: string; + actualExecutions: number; +} + +export class IndexObject { + database: string; + schema: string; + table: string; + index: string; + indexKind: string; + + public get title() { + let title: string = ''; + if (this.database && this.schema && this.table) { + title = `${this.database}.${this.schema}.${this.table}.${this.index}`; + if (this.indexKind && this.indexKind !== '') { + title += `(${this.indexKind})`; + } + } + return title; + } +} + +export class PlanNode { + root: PlanNode; + subtreeCost: number; + private childrenNodes: PlanNode[]; + parent: PlanNode; + physicalOp: string; + logicalOp: string; + id: number; + estimateRows: string; + estimateIo: string; + estimateCpu: string; + parallel: boolean; + partitioned: boolean; + estimateRewinds: string; + estimateRebinds: string; + runtimeInfo: RunTimeInformation; + indexObject: IndexObject; + + public addChildren(children: PlanNode[]): void { + if(children) { + children.forEach(element => { + element.parent = this; + }); + } + this.childrenNodes = children; + } + + public get totalSubTreeCost(): number { + let total = this.subtreeCost; + if (total === 0) { + this.children.forEach(element => { + total += element.subtreeCost; + }); + } + return total; + } + + public get children(): PlanNode[] { + return this.childrenNodes; + } + + public get cost(): number { + let total = this.subtreeCost; + if (this.children && total !== 0) { + this.children.forEach(element => { + total -= element.subtreeCost; + }); + } + return total; + } + + public get relativeCost(): number { + let overallCost = this.root.totalSubTreeCost; + return overallCost > 0 ? this.cost / overallCost : 0; + } + + public get estimatedOperatorCost(): number { + return Math.round(this.relativeCost * 100); + } + + public get estimatedSubtreeCost(): number { + let total = this.estimatedOperatorCost; + if (this.children) { + this.children.forEach(element => { + total += element.estimatedSubtreeCost; + }); + } + return total; + } + + public get title(): string { + if (this.physicalOp === this.logicalOp) { + return this.physicalOp; + } else { + return `${this.physicalOp}(${this.logicalOp})`; + } + } + + public get treeViewPrefix(): string { + return this.parent === undefined ? '' : `${this.parent.treeViewPrefix}-----`; + } + + public get treeViewTitle(): string { + return `${this.treeViewPrefix}${this.title}`; + } +} + +export class PlanXmlParser { + parser: DOMParser = new DOMParser(); + doc: Document; + planXml: string + root: PlanNode; + + constructor(planXml: string) { + + this.doc = this.parser.parseFromString(planXml, 'application/xml'); + this.planXml = planXml; + let queryPlanNode = this.findChildren(this.doc.childNodes[0], 'QueryPlan'); + if (queryPlanNode && queryPlanNode.length > 0) { + this.root = new PlanNode(); + let ops = this.createPlanNodes(queryPlanNode[0], 'RelOp', this.root); + + this.root.addChildren(ops); + this.root.subtreeCost = 0; + } + } + + public get topOperations(): PlanNode[] { + let operations: PlanNode[] = []; + if (this.root && this.root.children) { + operations = this.addOperationsToList(operations, this.root.children); + operations.sort((a, b) => { + if (a.estimatedOperatorCost > b.estimatedOperatorCost) { + return -1; + } else if (a.estimatedOperatorCost <= b.estimatedOperatorCost) { + return 1; + } else { + return 0; + } + }); + } + return operations; + } + + public get toTreeViewList(): PlanNode[] { + let operations: PlanNode[] = []; + operations = this.addOperationsToList(operations, this.root.children); + + return operations; + } + + private addOperationsToList(list: PlanNode[], nodes: PlanNode[]): PlanNode[] { + list = list.concat(nodes); + nodes.forEach(element => { + list = this.addOperationsToList(list, element.children); + }); + return list; + } + + private findChildren(node: Node, elementName: string, untilNode: string = undefined): Node[] { + let nodes: Node[] = []; + if (node === undefined) { + return undefined; + } + + for (var index = 0; index < node.childNodes.length; index++) { + if (node.childNodes[index].nodeName.toLocaleLowerCase() === elementName.toLocaleLowerCase()) { + nodes = nodes.concat(node.childNodes[index]); + } + } + if (nodes.length > 0) { + return nodes; + } + for (var index = 0; index < node.childNodes.length; index++) { + if (untilNode && node.childNodes[index].nodeName === untilNode) { + continue; + } + let result = this.findChildren(node.childNodes[index], elementName, untilNode); + if (result !== undefined) { + return result; + } + } + + return undefined; + } + + private createPlanNodes(node: Node, elementName: string, root: PlanNode): PlanNode[] { + let nodePlans: PlanNode[] = []; + + let children = this.findChildren(node, elementName); + if (children) { + for (var index = 0; index < children.length; index++) { + let childNode = children[index]; + + let planNode = this.convertToPlanNode(childNode); + planNode.root = root; + planNode.addChildren(this.createPlanNodes(childNode, elementName, root)); + planNode.runtimeInfo = new RunTimeInformation(); + planNode.indexObject = new IndexObject(); + + let runtimeInfoNodes = this.findChildren(childNode, 'RunTimeCountersPerThread'); + if (runtimeInfoNodes) { + planNode.runtimeInfo.runtimePerThreads = runtimeInfoNodes.map(x => this.convertToRuntimeInfo(x)); + } + + let objectNodes = this.findChildren(childNode, 'Object', 'RelOp'); + if (objectNodes && objectNodes.length > 0) { + planNode.indexObject = this.convertToObject(objectNodes[0]); + } + nodePlans = nodePlans.concat(planNode); + } + } + + return nodePlans; + } + + private convertToPlanNode(node: Node): PlanNode { + let planNode = new PlanNode(); + planNode.id = this.findAttribute(node.attributes, 'NodeId'); + planNode.logicalOp = this.findAttribute(node.attributes, 'LogicalOp'); + planNode.physicalOp = this.findAttribute(node.attributes, 'PhysicalOp'); + planNode.subtreeCost = +this.findAttribute(node.attributes, 'EstimatedTotalSubtreeCost'); + planNode.estimateRows = this.findAttribute(node.attributes, 'EstimateRows'); + planNode.estimateCpu = this.findAttribute(node.attributes, 'EstimateCPU'); + planNode.estimateIo = this.findAttribute(node.attributes, 'EstimateIO'); + planNode.estimateRebinds = this.findAttribute(node.attributes, 'EstimateRebinds'); + planNode.estimateRewinds = this.findAttribute(node.attributes, 'EstimateRewinds'); + planNode.parallel = this.findAttribute(node.attributes, 'Parallel') === '1'; + planNode.partitioned = this.findAttribute(node.attributes, 'Partitioned') === '1'; + return planNode; + } + + private convertToRuntimeInfo(node: Node): RuntimePerThread { + let runtimeNode = new RuntimePerThread(); + runtimeNode.actualExecutionMode = this.findAttribute(node.attributes, 'ActualExecutionMode'); + runtimeNode.actualExecutions = +this.findAttribute(node.attributes, 'ActualExecutions'); + runtimeNode.actualRow = +this.findAttribute(node.attributes, 'ActualRows'); + runtimeNode.threadId = +this.findAttribute(node.attributes, 'Thread'); + return runtimeNode; + } + + private convertToObject(node: Node): IndexObject { + let objectNode = new IndexObject(); + objectNode.database = this.findAttribute(node.attributes, 'Database'); + objectNode.index = this.findAttribute(node.attributes, 'Index'); + objectNode.indexKind = this.findAttribute(node.attributes, 'IndexKind'); + objectNode.schema = this.findAttribute(node.attributes, 'Schema'); + objectNode.table = this.findAttribute(node.attributes, 'Table'); + return objectNode; + } + + private findAttribute(attributes: NamedNodeMap, attName: string): any { + for (var index = 0; index < attributes.length; index++) { + var attribute = attributes[index]; + if (attribute.name === attName) { + return attribute.value; + } + } + } +} \ No newline at end of file diff --git a/src/sql/parts/queryPlan/queryPlan.component.ts b/src/sql/parts/queryPlan/queryPlan.component.ts new file mode 100644 index 0000000000..469fea9a17 --- /dev/null +++ b/src/sql/parts/queryPlan/queryPlan.component.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- +* 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!sql/parts/grid/load/css/qp'; + +import { ElementRef, Component, Inject, forwardRef, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import * as QP from 'html-query-plan'; + +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { QueryPlanParams } from 'sql/services/bootstrap/bootstrapParams'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { registerThemingParticipant, ICssStyleCollector, ITheme } from 'vs/platform/theme/common/themeService'; +import * as colors from 'vs/platform/theme/common/colorRegistry'; + +export const QUERYPLAN_SELECTOR: string = 'queryplan-component'; + +@Component({ + selector: QUERYPLAN_SELECTOR, + template: ` +
+
+ ` +}) +export class QueryPlanComponent implements OnDestroy, OnInit { + + private _planXml: string; + private _disposables: Array = []; + @ViewChild('container', { read: ElementRef }) _container: ElementRef; + + constructor( + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { } + + ngOnDestroy() { + dispose(this._disposables); + } + + ngOnInit() { + let parameters: QueryPlanParams = this._bootstrapService.getBootstrapParams(this._el.nativeElement.tagName); + if (parameters) { + this.planXml = parameters.planXml; + } + this._disposables.push(registerThemingParticipant(this._updateTheme)); + } + + public set planXml(val: string) { + this._planXml = val; + QP.showPlan(this._container.nativeElement, this._planXml, { + jsTooltips: false + }); + } + + private _updateTheme(theme: ITheme, collector: ICssStyleCollector) { + let backgroundColor = theme.getColor(colors.editorBackground); + let foregroundColor = theme.getColor(colors.editorForeground); + + if (backgroundColor) { + collector.addRule(`div.qp-node, .qp-tt, .qp-root { background-color: ${backgroundColor} }`); + } + + if (foregroundColor) { + collector.addRule(`.qp-root { color: ${foregroundColor} }`); + } + } +} diff --git a/src/sql/parts/queryPlan/queryPlan.module.ts b/src/sql/parts/queryPlan/queryPlan.module.ts new file mode 100644 index 0000000000..cc5c180cd6 --- /dev/null +++ b/src/sql/parts/queryPlan/queryPlan.module.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { NgModule, Inject, forwardRef, ApplicationRef, ComponentFactoryResolver } from '@angular/core'; +import { APP_BASE_HREF, CommonModule } from '@angular/common'; +import { BrowserModule } from '@angular/platform-browser'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { QueryPlanComponent, QUERYPLAN_SELECTOR } from 'sql/parts/queryPlan/queryPlan.component'; + +// Connection Dashboard main angular module +@NgModule({ + declarations: [ + QueryPlanComponent + ], + entryComponents: [QueryPlanComponent], + imports: [ + CommonModule, + BrowserModule + ], + providers: [{ provide: APP_BASE_HREF, useValue: '/' }] +}) +export class QueryPlanModule { + + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { + } + + ngDoBootstrap(appRef: ApplicationRef) { + const factory = this._resolver.resolveComponentFactory(QueryPlanComponent); + const uniqueSelector: string = this._bootstrapService.getUniqueSelector(QUERYPLAN_SELECTOR); + (factory).factory.selector = uniqueSelector; + appRef.bootstrap(factory); + } +} diff --git a/src/sql/parts/queryPlan/queryPlanEditor.ts b/src/sql/parts/queryPlan/queryPlanEditor.ts new file mode 100644 index 0000000000..e4acbc8296 --- /dev/null +++ b/src/sql/parts/queryPlan/queryPlanEditor.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/parts/query/editor/media/queryEditor'; +import * as DOM from 'vs/base/browser/dom'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { QueryPlanInput } from './queryPlanInput'; +import { QueryPlanModule } from './queryPlan.module'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { IMetadataService } from 'sql/services/metadata/metadataService'; +import { IScriptingService } from 'sql/services/scripting/scriptingService'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import { QueryPlanParams } from 'sql/services/bootstrap/bootstrapParams'; +import { QUERYPLAN_SELECTOR } from 'sql/parts/queryPlan/queryPlan.component'; + +declare let QP; + +export class QueryPlanEditor extends BaseEditor { + + public static ID: string = 'workbench.editor.queryplan'; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IInstantiationService private instantiationService: IInstantiationService, + @IConnectionManagementService private _connectionService: IConnectionManagementService, + @IMetadataService private _metadataService: IMetadataService, + @IScriptingService private _scriptingService: IScriptingService, + @IQueryEditorService private _queryEditorService: IQueryEditorService, + @IBootstrapService private _bootstrapService: IBootstrapService + ) { + super(QueryPlanEditor.ID, telemetryService, themeService); + } + + /** + * Called to create the editor in the parent builder. + */ + public createEditor(parent: Builder): void { + //Enable scrollbars when drawing area is larger than viewport + parent.overflow('auto'); + //Set background of parent to white (same as .qp-root from src\sql\parts\grid\load\css\qp.css) + //This is because the bottom-most tooltips can extend past the drawing area, which causes the + //scrolling area to have gaps on the bottom and left. So if the colors aren't matched then + //these gaps show up as different colors and look bad. + //Another option would be to check the tooltip positions and reposition them if necessary + //during the load - but changing the background color was the simplest and least error prone + //(plus it's probable that we won't be using this control in the future anyways if development) + //continues on the Query plan feature + parent.background('#fff'); + } + + /** + * Sets focus on this editor. Specifically, it sets the focus on the hosted text editor. + */ + public focus(): void { + } + + /** + * Updates the internal variable keeping track of the editor's size, and re-calculates the sash position. + * To be called when the container of this editor changes size. + */ + public layout(dimension: Dimension): void { + } + + public setInput(input: QueryPlanInput, options: EditorOptions): TPromise { + if (this.input instanceof QueryPlanInput && this.input.matches(input)) { + return TPromise.as(undefined); + } + + if (!input.hasInitialized) { + this.bootstrapAngular(input); + } + this.revealElementWithTagName(input.uniqueSelector, this.getContainer().getHTMLElement()); + + return super.setInput(input, options); + } + + /** + * Reveal the child element with the given tagName and hide all other elements. + */ + private revealElementWithTagName(tagName: string, parent: HTMLElement): void { + let elementToReveal: HTMLElement; + + for (let i = 0; i < parent.children.length; i++) { + let child: HTMLElement = parent.children[i]; + if (child.tagName && child.tagName.toLowerCase() === tagName && !elementToReveal) { + elementToReveal = child; + } else { + child.style.display = 'none'; + } + } + + if (elementToReveal) { + elementToReveal.style.display = ''; + } + } + + /** + * Load the angular components and record for this input that we have done so + */ + private bootstrapAngular(input: QueryPlanInput): void { + // Get the bootstrap params and perform the bootstrap + let params: QueryPlanParams = { + planXml: input.planXml + }; + + let uniqueSelector = this._bootstrapService.bootstrap( + QueryPlanModule, + this.getContainer().getHTMLElement(), + QUERYPLAN_SELECTOR, + params); + input.setUniqueSelector(uniqueSelector); + } + + public dispose(): void { + super.dispose(); + } +} diff --git a/src/sql/parts/queryPlan/queryPlanInput.ts b/src/sql/parts/queryPlan/queryPlanInput.ts new file mode 100644 index 0000000000..022e1fa8ff --- /dev/null +++ b/src/sql/parts/queryPlan/queryPlanInput.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; + +export class QueryPlanInput extends EditorInput { + + public static ID: string = 'workbench.editorinputs.queryplan'; + public static SCHEMA: string = 'queryplan'; + + private _uniqueSelector: string; + + constructor(private _xml: string, private _uri: string, private _connection: ConnectionManagementInfo) { + super(); + } + + public setUniqueSelector(uniqueSelector: string): void { + this._uniqueSelector = uniqueSelector; + } + + public getTypeId(): string { + return UntitledEditorInput.ID; + } + + public getName(): string { + return 'Query Plan'; + } + + public get planXml(): string { + return this._xml; + } + + public getUri(): string { + return this._uri; + } + + public supportsSplitEditor(): boolean { + return false; + } + + public getConnectionProfile(): IConnectionProfile { + //return this._connection.connectionProfile; + return undefined; + } + + public resolve(refresh?: boolean): TPromise { + return undefined; + } + + public get hasInitialized(): boolean { + return !!this._uniqueSelector; + } + + public get uniqueSelector(): string { + return this._uniqueSelector; + } + + public getConnectionInfo(): ConnectionManagementInfo { + return this._connection; + } +} diff --git a/src/sql/parts/queryPlan/topOperations.component.ts b/src/sql/parts/queryPlan/topOperations.component.ts new file mode 100644 index 0000000000..3a3dc2c840 --- /dev/null +++ b/src/sql/parts/queryPlan/topOperations.component.ts @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { ElementRef, Component, Inject, forwardRef, OnDestroy, Input, OnInit } from '@angular/core'; +import { Subscription, Subject } from 'rxjs/Rx'; + +import { PlanXmlParser, PlanNode } from 'sql/parts/queryPlan/planXmlParser'; +import { TabChild } from 'sql/base/browser/ui/panel/tab.component'; +import { Table } from 'sql/base/browser/ui/table/table'; +import { attachTableStyler } from 'sql/common/theme/styler'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { QueryComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents'; +import { DataService } from 'sql/parts/grid/services/dataService'; +import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils'; + +import { localize } from 'vs/nls'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; + +export const TOP_OPERATIONS_SELECTOR: string = 'top-operations-component'; + +@Component({ + selector: TOP_OPERATIONS_SELECTOR, + template: '', + providers: [{ provide: TabChild, useExisting: forwardRef(() => TopOperationsComponent) }] +}) +export class TopOperationsComponent extends TabChild implements OnDestroy, OnInit { + + private _operations: Array = []; + private _table: Table; + private _dataService: DataService; + private toDispose: Array = []; + private _columns: Array> = [ + { name: localize('topOperations.operation', 'Operation'), field: 'operation' }, + { name: localize('topOperations.object', 'Object'), field: 'object' }, + { name: localize('topOperations.estCost', 'Est Cost'), field: 'estCost' }, + { name: localize('topOperations.estSubtreeCost', 'Est Subtree Cost'), field: 'estSubtreeCost' }, + { name: localize('topOperations.actualRows', 'Actual Rows'), field: 'actualRows' }, + { name: localize('topOperations.estRows', 'Est Rows'), field: 'estRows' }, + { name: localize('topOperations.actualExecutions', 'Actual Executions'), field: 'actualExecutions' }, + { name: localize('topOperations.estCPUCost', 'Est CPU Cost'), field: 'estCPUCost' }, + { name: localize('topOperations.estIOCost', 'Est IO Cost'), field: 'estIOCost' }, + { name: localize('topOperations.parallel', 'Parallel'), field: 'parallel' }, + { name: localize('topOperations.actualRebinds', 'Actual Rebinds'), field: 'actualRebinds' }, + { name: localize('topOperations.estRebinds', 'Est Rebinds'), field: 'estRebinds' }, + { name: localize('topOperations.actualRewinds', 'Actual Rewinds'), field: 'actualRewinds' }, + { name: localize('topOperations.estRewinds', 'Est Rewinds'), field: 'estRewinds' }, + { name: localize('topOperations.partitioned', 'Partitioned'), field: 'partitioned' } + ]; + + @Input() public queryParameters: QueryComponentParams; + + private _disposables: Array = []; + + constructor( + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService, + + ) { + super(); + } + + ngOnInit() { + this._dataService = this.queryParameters.dataService; + this.subscribeWithDispose(this._dataService.gridContentObserver, (type) => { + switch (type) { + case GridContentEvents.ResizeContents: + this.layout(); + break; + } + }); + } + + ngOnDestroy() { + dispose(this._disposables); + } + + public set planXml(val: string) { + let parser: PlanXmlParser = new PlanXmlParser(val); + this._operations = parser.topOperations; + let data = this._operations.map(i => { + return { + operation: i.title, + object: i.indexObject.title, + estCost: i.estimatedOperatorCost, + estSubtreeCost: i.subtreeCost, + actualRows: i.runtimeInfo.actualRows, + estRows: i.estimateRows, + actualExecutions: i.runtimeInfo.actualExecutions, + estCPUCost: i.estimateCpu, + estIOCost: i.estimateIo, + parallel: i.parallel, + actualRebinds: '', + estRebinds: i.estimateRebinds, + actualRewinds: '', + estRewinds: i.estimateRewinds, + partitioned: i.partitioned + }; + }); + if (!this._table) { + this._table = new Table(this._el.nativeElement, data, this._columns); + this._disposables.push(attachTableStyler(this._table, this._bootstrapService.themeService)); + } + } + + public layout(): void { + if (this._table) { + setTimeout(() => { + this._table.resizeCanvas(); + this._table.autosizeColumns(); + }); + } + } + + protected subscribeWithDispose(subject: Subject, event: (value: any) => void): void { + let sub: Subscription = subject.subscribe(event); + this.toDispose.push(toDisposableSubscription(sub)); + } +} diff --git a/src/sql/parts/registeredServer/common/nodeType.ts b/src/sql/parts/registeredServer/common/nodeType.ts new file mode 100644 index 0000000000..6f0921a285 --- /dev/null +++ b/src/sql/parts/registeredServer/common/nodeType.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +export class NodeType { + public static Folder = 'Folder'; + public static Root = 'root'; + public static Database = 'Database'; + public static Server = 'Server'; + public static ScalarValuedFunction = 'ScalarValuedFunction'; + public static TableValuedFunction = 'TableValuedFunction'; + public static AggregateFunction = 'AggregateFunction'; + public static FileGroup = 'FileGroup'; + public static StoredProcedure = 'StoredProcedure'; + public static UserDefinedTableType = 'UserDefinedTableType'; + public static View = 'View'; + public static Table = 'Table'; + public static HistoryTable = 'HistoryTable'; + public static ServerLevelLinkedServerLogin = 'ServerLevelLinkedServerLogin'; + public static ServerLevelServerAudit = 'ServerLevelServerAudit'; + public static ServerLevelCryptographicProvider = 'ServerLevelCryptographicProvider'; + public static ServerLevelCredential = 'ServerLevelCredential'; + public static ServerLevelServerRole = 'ServerLevelServerRole'; + public static ServerLevelLogin = 'ServerLevelLogin'; + public static ServerLevelServerAuditSpecification = 'ServerLevelServerAuditSpecification'; + public static ServerLevelServerTrigger = 'ServerLevelServerTrigger'; + public static ServerLevelLinkedServer = 'ServerLevelLinkedServer'; + public static ServerLevelEndpoint = 'ServerLevelEndpoint'; + public static Synonym = 'Synonym'; + public static DatabaseTrigger = 'DatabaseTrigger'; + public static Assembly = 'Assembly'; + public static MessageType = 'MessageType'; + public static Contract = 'Contract'; + public static Queue = 'Queue'; + public static Service = 'Service'; + public static Route = 'Route'; + public static DatabaseAndQueueEventNotification = 'DatabaseAndQueueEventNotification'; + public static RemoteServiceBinding = 'RemoteServiceBinding'; + public static BrokerPriority = 'BrokerPriority'; + public static FullTextCatalog = 'FullTextCatalog'; + public static FullTextStopList = 'FullTextStopList'; + public static SqlLogFile = 'SqlLogFile'; + public static PartitionFunction = 'PartitionFunction'; + public static PartitionScheme = 'PartitionScheme'; + public static SearchPropertyList = 'SearchPropertyList'; + public static User = 'User'; + public static Schema = 'Schema'; + public static AsymmetricKey = 'AsymmetricKey'; + public static Certificate = 'Certificate'; + public static SymmetricKey = 'SymmetricKey'; + public static DatabaseEncryptionKey = 'DatabaseEncryptionKey'; + public static MasterKey = 'MasterKey'; + public static DatabaseAuditSpecification = 'DatabaseAuditSpecification'; + public static Column = 'Column'; + public static Key = 'Key'; + public static Constraint = 'Constraint'; + public static Trigger = 'Trigger'; + public static Index = 'Index'; + public static Statistic = 'Statistic'; + public static UserDefinedDataType = 'UserDefinedDataType'; + public static UserDefinedType = 'UserDefinedType'; + public static XmlSchemaCollection = 'XmlSchemaCollection'; + public static SystemExactNumeric = 'SystemExactNumeric'; + public static SystemApproximateNumeric = 'SystemApproximateNumeric'; + public static SystemDateAndTime = 'SystemDateAndTime'; + public static SystemCharacterString = 'SystemCharacterString'; + public static SystemUnicodeCharacterString = 'SystemUnicodeCharacterString'; + public static SystemBinaryString = 'SystemBinaryString'; + public static SystemOtherDataType = 'SystemOtherDataType'; + public static SystemClrDataType = 'SystemClrDataType'; + public static SystemSpatialDataType = 'SystemSpatialDataType'; + public static UserDefinedTableTypeColumn = 'UserDefinedTableTypeColumn'; + public static UserDefinedTableTypeKey = 'UserDefinedTableTypeKey'; + public static UserDefinedTableTypeConstraint = 'UserDefinedTableTypeConstraint'; + public static StoredProcedureParameter = 'StoredProcedureParameter'; + public static TableValuedFunctionParameter = 'TableValuedFunctionParameter'; + public static ScalarValuedFunctionParameter = 'ScalarValuedFunctionParameter'; + public static AggregateFunctionParameter = 'AggregateFunctionParameter'; + public static DatabaseRole = 'DatabaseRole'; + public static ApplicationRole = 'ApplicationRole'; + public static FileGroupFile = 'FileGroupFile'; + public static SystemMessageType = 'SystemMessageType'; + public static SystemContract = 'SystemContract'; + public static SystemService = 'SystemService'; + public static SystemQueue = 'SystemQueue'; + public static Sequence = 'Sequence'; + public static SecurityPolicy = 'SecurityPolicy'; + public static DatabaseScopedCredential = 'DatabaseScopedCredential'; + public static ExternalResource = 'ExternalResource'; + public static ExternalDataSource = 'ExternalDataSource'; + public static ExternalFileFormat = 'ExternalFileFormat'; + public static ExternalTable = 'ExternalTable'; + public static ColumnMasterKey = 'ColumnMasterKey'; + public static ColumnEncryptionKey = 'ColumnEncryptionKey'; +} diff --git a/src/sql/parts/registeredServer/common/objectExplorerService.ts b/src/sql/parts/registeredServer/common/objectExplorerService.ts new file mode 100644 index 0000000000..45cafc49d8 --- /dev/null +++ b/src/sql/parts/registeredServer/common/objectExplorerService.ts @@ -0,0 +1,352 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import Event, { Emitter } from 'vs/base/common/event'; +import * as data from 'data'; +import * as nls from 'vs/nls'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import * as TelemetryUtils from 'sql/common/telemetryUtilities'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { warn, error } from 'sql/base/common/log'; + +export const SERVICE_ID = 'ObjectExplorerService'; + +export const IObjectExplorerService = createDecorator(SERVICE_ID); + +export interface IObjectExplorerService { + _serviceBrand: any; + + createNewSession(providerId: string, connection: ConnectionProfile): Thenable; + + closeSession(providerId: string, session: data.ObjectExplorerSession): Thenable; + + expandNode(providerId: string, session: data.ObjectExplorerSession, nodePath: string): Thenable; + + refreshNode(providerId: string, session: data.ObjectExplorerSession, nodePath: string): Thenable; + + expandTreeNode(session: data.ObjectExplorerSession, parentTree: TreeNode): Thenable; + + refreshTreeNode(session: data.ObjectExplorerSession, parentTree: TreeNode): Thenable; + + onSessionCreated(handle: number, sessionResponse: data.ObjectExplorerSession); + + onNodeExpanded(handle: number, sessionResponse: data.ObjectExplorerExpandInfo); + + /** + * Register a ObjectExplorer provider + */ + registerProvider(providerId: string, provider: data.ObjectExplorerProvider): void; + + getObjectExplorerNode(connection: IConnectionProfile): TreeNode; + + updateObjectExplorerNodes(connectionProfile: IConnectionProfile): Promise; + + deleteObjectExplorerNode(connection: IConnectionProfile): void; + + onUpdateObjectExplorerNodes: Event; +} + +interface SessionStatus { + nodes: { [nodePath: string]: NodeStatus }; + connection: ConnectionProfile; + +} + +interface NodeStatus { + expandHandler: (result: data.ObjectExplorerExpandInfo) => void; +} + +export interface ObjectExplorerNodeEventArgs { + connection: IConnectionProfile; + errorMessage: string; +} + + +export class ObjectExplorerService implements IObjectExplorerService { + + public _serviceBrand: any; + + private _disposables: IDisposable[] = []; + + private _providers: { [handle: string]: data.ObjectExplorerProvider; } = Object.create(null); + + private _activeObjectExplorerNodes: { [id: string]: TreeNode }; + private _sessions: { [sessionId: string]: SessionStatus }; + + private _onUpdateObjectExplorerNodes: Emitter; + + constructor( + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @ITelemetryService private _telemetryService: ITelemetryService + ) { + this._onUpdateObjectExplorerNodes = new Emitter(); + this._activeObjectExplorerNodes = {}; + this._sessions = {}; + this._providers = {}; + } + + public get onUpdateObjectExplorerNodes(): Event { + return this._onUpdateObjectExplorerNodes.event; + } + + public updateObjectExplorerNodes(connection: IConnectionProfile): Promise { + return this._connectionManagementService.addSavedPassword(connection).then(withPassword => { + let connectionProfile = ConnectionProfile.convertToConnectionProfile( + this._connectionManagementService.getCapabilities(connection.providerName), withPassword); + return this.updateNewObjectExplorerNode(connectionProfile); + }); + } + + public deleteObjectExplorerNode(connection: IConnectionProfile): void { + let self = this; + var connectionUri = connection.id; + var nodeTree = this._activeObjectExplorerNodes[connectionUri]; + if (nodeTree) { + self.closeSession(connection.providerName, nodeTree.getSession()).then(() => { + delete self._activeObjectExplorerNodes[connectionUri]; + delete self._sessions[nodeTree.getSession().sessionId]; + }); + } + } + + /** + * Gets called when expanded node response is ready + */ + public onNodeExpanded(handle: number, expandResponse: data.ObjectExplorerExpandInfo) { + + if (expandResponse.errorMessage) { + error(expandResponse.errorMessage); + } + + let nodeStatus = this._sessions[expandResponse.sessionId].nodes[expandResponse.nodePath]; + if (nodeStatus && nodeStatus.expandHandler) { + nodeStatus.expandHandler(expandResponse); + } else { + warn(`Cannot find node status for session: ${expandResponse.sessionId} and node path: ${expandResponse.nodePath}`); + } + } + + /** + * Gets called when session is created + */ + public onSessionCreated(handle: number, session: data.ObjectExplorerSession) { + let connection: ConnectionProfile = undefined; + let errorMessage: string = undefined; + if (this._sessions[session.sessionId]) { + connection = this._sessions[session.sessionId].connection; + + if (session && session.success && session.rootNode) { + let server = this.toTreeNode(session.rootNode, null); + server.connection = connection; + server.session = session; + this._activeObjectExplorerNodes[connection.id] = server; + } else { + errorMessage = session && session.errorMessage ? session.errorMessage : + nls.localize('OeSessionFailedError', 'Failed to create Object Explorer session'); + error(errorMessage); + } + + } else { + warn(`cannot find session ${session.sessionId}`); + } + + this.sendUpdateNodeEvent(connection, errorMessage); + } + + private sendUpdateNodeEvent(connection: ConnectionProfile, errorMessage: string = undefined) { + let eventArgs: ObjectExplorerNodeEventArgs = { + connection: connection, + errorMessage: errorMessage + }; + this._onUpdateObjectExplorerNodes.fire(eventArgs); + } + + private updateNewObjectExplorerNode(connection: ConnectionProfile): Promise { + let self = this; + return new Promise((resolve, reject) => { + if (self._activeObjectExplorerNodes[connection.id]) { + this.sendUpdateNodeEvent(connection); + resolve(); + } else { + // Create session will send the event or reject the promise + this.createNewSession(connection.providerName, connection).then(response => { + resolve(); + }, error => { + this.sendUpdateNodeEvent(connection, error); + reject(error); + }); + } + }); + } + + public getObjectExplorerNode(connection: IConnectionProfile): TreeNode { + return this._activeObjectExplorerNodes[connection.id]; + } + + public createNewSession(providerId: string, connection: ConnectionProfile): Thenable { + let self = this; + return new Promise((resolve, reject) => { + let provider = this._providers[providerId]; + if (provider) { + provider.createNewSession(connection.toConnectionInfo()).then(result => { + self._sessions[result.sessionId] = { + connection: connection, + nodes: {} + }; + resolve(result); + }, error => { + reject(error); + }); + } else { + reject(`Provider doesn't exist. id: ${providerId}`); + } + }); + } + + public expandNode(providerId: string, session: data.ObjectExplorerSession, nodePath: string): Thenable { + return new Promise((resolve, reject) => { + let provider = this._providers[providerId]; + if (provider) { + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.ObjectExplorerExpand, { refresh: 0, provider: providerId }); + this.expandOrRefreshNode(provider, session, nodePath).then(result => { + resolve(result); + }, error => { + reject(error); + }); + } else { + reject(`Provider doesn't exist. id: ${providerId}`); + } + }); + } + private callExpandOrRefreshFromProvider(provider: data.ObjectExplorerProvider, nodeInfo: data.ExpandNodeInfo, refresh: boolean = false) { + if (refresh) { + return provider.refreshNode(nodeInfo); + } else { + return provider.expandNode(nodeInfo); + } + } + + private expandOrRefreshNode( + provider: data.ObjectExplorerProvider, + session: data.ObjectExplorerSession, + nodePath: string, + refresh: boolean = false): Thenable { + let self = this; + return new Promise((resolve, reject) => { + if (session.sessionId in self._sessions && self._sessions[session.sessionId]) { + self._sessions[session.sessionId].nodes[nodePath] = { + expandHandler: ((expandResult) => { + if (expandResult && !expandResult.errorMessage) { + resolve(expandResult); + } + else { + reject(expandResult ? expandResult.errorMessage : undefined); + } + delete self._sessions[session.sessionId].nodes[nodePath]; + }) + }; + self.callExpandOrRefreshFromProvider(provider, { + sessionId: session ? session.sessionId : undefined, + nodePath: nodePath + }, refresh).then(result => { + }, error => { + reject(error); + }); + } else { + reject(`session cannot find to expand node. id: ${session.sessionId} nodePath: ${nodePath}`); + } + }); + } + + public refreshNode(providerId: string, session: data.ObjectExplorerSession, nodePath: string): Thenable { + let provider = this._providers[providerId]; + if (provider) { + TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.ObjectExplorerExpand, { refresh: 1, provider: providerId }); + return this.expandOrRefreshNode(provider, session, nodePath, true); + } + return Promise.resolve(undefined); + } + + public closeSession(providerId: string, session: data.ObjectExplorerSession): Thenable { + let provider = this._providers[providerId]; + if (provider) { + return provider.closeSession({ + sessionId: session ? session.sessionId : undefined + }); + } + + return Promise.resolve(undefined); + } + + /** + * Register a ObjectExplorer provider + */ + public registerProvider(providerId: string, provider: data.ObjectExplorerProvider): void { + this._providers[providerId] = provider; + } + + public dispose(): void { + this._disposables = dispose(this._disposables); + } + + public expandTreeNode(session: data.ObjectExplorerSession, parentTree: TreeNode): Thenable { + return this.expandOrRefreshTreeNode(session, parentTree); + } + + public refreshTreeNode(session: data.ObjectExplorerSession, parentTree: TreeNode): Thenable { + return this.expandOrRefreshTreeNode(session, parentTree, true); + } + + private callExpandOrRefreshFromService(providerId: string, session: data.ObjectExplorerSession, nodePath: string, refresh: boolean = false): Thenable { + if (refresh) { + return this.refreshNode(providerId, session, nodePath); + } else { + return this.expandNode(providerId, session, nodePath); + } + } + + private expandOrRefreshTreeNode( + session: data.ObjectExplorerSession, + parentTree: TreeNode, + refresh: boolean = false): Thenable { + return new Promise((resolve, reject) => { + this.callExpandOrRefreshFromService(parentTree.getConnectionProfile().providerName, session, parentTree.nodePath, refresh).then(expandResult => { + let children: TreeNode[] = []; + if (expandResult && expandResult.nodes) { + children = expandResult.nodes.map(node => { + return this.toTreeNode(node, parentTree); + }); + parentTree.children = children.filter(c => c !== undefined); + resolve(children); + } else { + reject(expandResult && expandResult.errorMessage ? expandResult.errorMessage : 'Failed to expand node'); + } + }, error => { + reject(error); + }); + }); + } + + private toTreeNode(nodeInfo: data.NodeInfo, parent: TreeNode): TreeNode { + // make Database nodes with a status field non-expandable + let isLeaf: boolean = nodeInfo.isLeaf; + if (nodeInfo.nodeType === 'Database' && nodeInfo.nodeStatus) { + nodeInfo.label = nodeInfo.label + ' (' + nodeInfo.nodeStatus + ')'; + isLeaf = true; + // set to common status so we can have a single 'Unavailable' db icon + nodeInfo.nodeStatus = 'Unavailable'; + } + + return new TreeNode(nodeInfo.nodeType, nodeInfo.label, isLeaf, nodeInfo.nodePath, + nodeInfo.nodeSubType, nodeInfo.nodeStatus, parent, nodeInfo.metadata); + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/common/registeredServer.contribution.ts b/src/sql/parts/registeredServer/common/registeredServer.contribution.ts new file mode 100644 index 0000000000..dbe713e700 --- /dev/null +++ b/src/sql/parts/registeredServer/common/registeredServer.contribution.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/media/actionBarLabel'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { localize } from 'vs/nls'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ToggleViewletAction } from 'vs/workbench/browser/viewlet'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; + +import { VIEWLET_ID } from 'sql/parts/connection/common/connectionManagement'; + +// Viewlet Action +export class OpenConnectionsViewletAction extends ToggleViewletAction { + public static ID = VIEWLET_ID; + public static LABEL = 'Show Servers'; + + constructor( + id: string, + label: string, + @IViewletService viewletService: IViewletService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService + ) { + super(id, label, VIEWLET_ID, viewletService, editorService); + } +} + +// Viewlet +const viewletDescriptor = new ViewletDescriptor( + 'sql/parts/registeredServer/viewlet/connectionViewlet', + 'ConnectionViewlet', + VIEWLET_ID, + 'Servers', + 'connectionViewlet', + -100 +); + +Registry.as(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor); + +Registry.as(ViewletExtensions.Viewlets).setDefaultViewletId(VIEWLET_ID); + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction( + new SyncActionDescriptor( + OpenConnectionsViewletAction, + OpenConnectionsViewletAction.ID, + OpenConnectionsViewletAction.LABEL, + { primary: KeyMod.CtrlCmd | KeyCode.Shift | KeyCode.KEY_C }), + 'View: Show Servers', + localize('view', "View") +); + +let configurationRegistry = Registry.as(Extensions.Configuration); +configurationRegistry.registerConfiguration({ + 'id': 'databaseConnections', + 'title': localize('databaseConnections', 'Database Connections'), + 'type': 'object', + 'properties': { + 'datasource.connections': { + 'description': localize('datasource.connections', 'data source connections'), + 'type': 'array' + }, + 'datasource.connectionGroups': { + 'description': localize('datasource.connectionGroups', 'data source groups'), + 'type': 'array' + } + } +}); +configurationRegistry.registerConfiguration({ + 'id': 'startupConfig', + 'title': localize('startupConfig', 'Startup Configuration'), + 'type': 'object', + 'properties': { + 'startup.alwaysShowServersView': { + 'type': 'boolean', + 'description': localize('startup.alwaysShowServersView', 'True for the Servers view to be shown on launch of SQL Operations Studio default; false if the last opened view should be shown'), + 'default': true + } + } +}); diff --git a/src/sql/parts/registeredServer/common/treeNode.ts b/src/sql/parts/registeredServer/common/treeNode.ts new file mode 100644 index 0000000000..b7bf0fd350 --- /dev/null +++ b/src/sql/parts/registeredServer/common/treeNode.ts @@ -0,0 +1,123 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { NodeType } from 'sql/parts/registeredServer/common/nodeType'; +import * as data from 'data'; + +import * as UUID from 'vs/base/common/uuid'; + +export class TreeNode { + /** + * id for TreeNode + */ + public id: string; + + /** + * string defining the type of the node - for example Server, Database, Folder, Table + */ + public nodeTypeId: string; + + /** + * Label to display to the user, describing this node + */ + public label: string; + + /** + * Is this a leaf node (in which case no children can be generated) or is it expandable? + */ + public isAlwaysLeaf: boolean; + + /** + * Message to show if this Node is in an error state. This indicates + * that children could be retrieved + */ + public errorStateMessage: string; + + /** + * Parent of this node + */ + public parent: TreeNode; + + /** + * Path identifying this node + */ + public nodePath: string; + + /** + * Node sub type + */ + public nodeSubType: string; + + /** + * Node Status + */ + public nodeStatus: string; + + /** + * Children of this node + */ + public children: TreeNode[]; + + + public connection: ConnectionProfile; + + public session: data.ObjectExplorerSession; + + public metadata: data.ObjectMetadata; + + public getConnectionProfile(): ConnectionProfile { + var currentNode: TreeNode = this; + while (!currentNode.connection && currentNode.parent) { + currentNode = currentNode.parent; + } + return currentNode.connection; + } + + public getDatabaseName(): string { + if (this.connection) { + return undefined; + } + var currentNode: TreeNode = this; + while (currentNode.nodeTypeId !== NodeType.Database && currentNode.nodeTypeId !== NodeType.Server) { + currentNode = currentNode.parent; + } + + if (currentNode.nodeTypeId === NodeType.Database) { + return currentNode.metadata ? currentNode.metadata.name : null; + } + return undefined; + } + + public getSession(): data.ObjectExplorerSession { + var currentNode: TreeNode = this; + while (!currentNode.session && currentNode.parent) { + currentNode = currentNode.parent; + } + return currentNode.session; + } + + public isTopLevel(): boolean { + if (this.parent && this.parent.nodeTypeId === NodeType.Root) { + return true; + } + return false; + } + + constructor(nodeTypeId: string, label: string, isAlwaysLeaf: boolean, nodePath: string, + nodeSubType: string, nodeStatus: string, parent: TreeNode, metadata: data.ObjectMetadata) { + this.nodeTypeId = nodeTypeId; + this.label = label; + this.isAlwaysLeaf = isAlwaysLeaf; + this.nodePath = nodePath; + this.parent = parent; + this.metadata = metadata; + this.id = UUID.generateUuid(); + this.nodeSubType = nodeSubType; + this.nodeStatus = nodeStatus; + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/serverGroupDialog/media/serverGroupDialog.css b/src/sql/parts/registeredServer/serverGroupDialog/media/serverGroupDialog.css new file mode 100644 index 0000000000..019712cce5 --- /dev/null +++ b/src/sql/parts/registeredServer/serverGroupDialog/media/serverGroupDialog.css @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.group-color-options { + display: flex; + width: 100%; +} + +.group-color-options .server-group-color { + flex: 1 1 auto; + height: 20px; + margin: 5px; +} + +.server-group-dialog { + padding: 15px +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/serverGroupDialog/serverGroup.contribution.ts b/src/sql/parts/registeredServer/serverGroupDialog/serverGroup.contribution.ts new file mode 100644 index 0000000000..be58acff53 --- /dev/null +++ b/src/sql/parts/registeredServer/serverGroupDialog/serverGroup.contribution.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IConfigurationRegistry, Extensions, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; + +const configurationRegistry = Registry.as(Extensions.Configuration); + +export const SERVER_GROUP_CONFIG = 'serverGroup'; +export const SERVER_GROUP_COLORS_CONFIG = 'colors'; + +const serverGroupConfig: IConfigurationNode = { + id: 'Server Groups', + type: 'object', + properties: { + [SERVER_GROUP_CONFIG + '.' + SERVER_GROUP_COLORS_CONFIG]: { + type: 'array', + items: 'string', + default: [ + '#A1634D', + '#7F0000', + '#914576', + '#85AE72', + '#98AFC7', + '#4452A6', + '#6A6599', + '#515151' + ] + } + } +}; + +configurationRegistry.registerConfiguration(serverGroupConfig); \ No newline at end of file diff --git a/src/sql/parts/registeredServer/serverGroupDialog/serverGroupController.ts b/src/sql/parts/registeredServer/serverGroupDialog/serverGroupController.ts new file mode 100644 index 0000000000..d9b340d237 --- /dev/null +++ b/src/sql/parts/registeredServer/serverGroupDialog/serverGroupController.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { + IConnectionManagementService, IErrorMessageService, + IServerGroupController, IServerGroupDialogCallbacks +} from 'sql/parts/connection/common/connectionManagement'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { ServerGroupDialog } from 'sql/parts/registeredServer/serverGroupDialog/serverGroupDialog'; +import { ServerGroupViewModel } from 'sql/parts/registeredServer/serverGroupDialog/serverGroupViewModel'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import Severity from 'vs/base/common/severity'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { SERVER_GROUP_CONFIG, SERVER_GROUP_COLORS_CONFIG } from './serverGroup.contribution'; + +export class ServerGroupController implements IServerGroupController { + _serviceBrand: any; + + private _serverGroupDialog: ServerGroupDialog; + private _connectionManagementService: IConnectionManagementService; + private _callbacks: IServerGroupDialogCallbacks; + private _group: ConnectionProfileGroup; + private _viewModel: ServerGroupViewModel; + + constructor( + @IPartService private _partService: IPartService, + @IErrorMessageService private _errorMessageService: IErrorMessageService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IConfigurationService private _configurationService: IConfigurationService + ) { + } + + private handleOnAddServerGroup(): void { + if (this._group) { + let tempGroup: ConnectionProfileGroup = this.copyConnectionProfileGroup(this._group); + this._group.name = this._viewModel.groupName; + this._group.color = this._viewModel.groupColor; + this._group.description = this._viewModel.groupDescription; + this._connectionManagementService.editGroup(this._group).then(() => { + this._serverGroupDialog.close(); + }).catch(err => { + // rollback changes made + this._group = tempGroup; + this._errorMessageService.showDialog(Severity.Error, '', err); + }); + + } else { + let newGroup: IConnectionProfileGroup = { + name: this._viewModel.groupName, + id: undefined, + parentId: undefined, + color: this._viewModel.groupColor, + description: this._viewModel.groupDescription + }; + this._connectionManagementService.saveProfileGroup(newGroup).then(groupId => { + if (this._callbacks) { + this._callbacks.onAddGroup(this._serverGroupDialog.groupName); + } + this._serverGroupDialog.close(); + }).catch(err => { + this._errorMessageService.showDialog(Severity.Error, '', err); + }); + } + } + + private copyConnectionProfileGroup(group: ConnectionProfileGroup): ConnectionProfileGroup { + return new ConnectionProfileGroup(group.name, group.parent, group.id, group.color, group.description); + } + + private handleOnClose(): void { + if (this._callbacks) { + this._callbacks.onClose(); + } + } + + + public showCreateGroupDialog(connectionManagementService: IConnectionManagementService, callbacks?: IServerGroupDialogCallbacks): TPromise { + this._connectionManagementService = connectionManagementService; + this._group = null; + this._viewModel = new ServerGroupViewModel(undefined, this._configurationService.getConfiguration(SERVER_GROUP_CONFIG)[SERVER_GROUP_COLORS_CONFIG]); + this._callbacks = callbacks ? callbacks : undefined; + return this.openServerGroupDialog(); + } + + public showEditGroupDialog(connectionManagementService: IConnectionManagementService, group: ConnectionProfileGroup): TPromise { + this._connectionManagementService = connectionManagementService; + this._group = group; + this._viewModel = new ServerGroupViewModel(group, this._configurationService.getConfiguration(SERVER_GROUP_CONFIG)[SERVER_GROUP_COLORS_CONFIG]); + return this.openServerGroupDialog(); + } + + private openServerGroupDialog(): TPromise { + if (!this._serverGroupDialog) { + this._serverGroupDialog = this._instantiationService.createInstance(ServerGroupDialog); + this._serverGroupDialog.viewModel = this._viewModel; + this._serverGroupDialog.onCancel(() => { }); + this._serverGroupDialog.onAddServerGroup(() => this.handleOnAddServerGroup()); + this._serverGroupDialog.onCloseEvent(() => this.handleOnClose()); + this._serverGroupDialog.render(); + } else { + // reset the view model in the view + this._serverGroupDialog.viewModel = this._viewModel; + } + + return new TPromise(() => { + this._serverGroupDialog.open(); + }); + } +} diff --git a/src/sql/parts/registeredServer/serverGroupDialog/serverGroupDialog.ts b/src/sql/parts/registeredServer/serverGroupDialog/serverGroupDialog.ts new file mode 100644 index 0000000000..5063ba1665 --- /dev/null +++ b/src/sql/parts/registeredServer/serverGroupDialog/serverGroupDialog.ts @@ -0,0 +1,388 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import 'vs/css!./media/serverGroupDialog'; +import { Builder } from 'vs/base/browser/builder'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; +import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; +import * as DOM from 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachInputBoxStyler, attachButtonStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { localize } from 'vs/nls'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; + +import { Modal } from 'sql/base/browser/ui/modal/modal'; +import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox'; +import { ServerGroupViewModel } from 'sql/parts/registeredServer/serverGroupDialog/serverGroupViewModel'; +import { attachModalDialogStyler } from 'sql/common/theme/styler'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; + +export class ServerGroupDialog extends Modal { + private _bodyBuilder: Builder; + private _addServerButton: Button; + private _closeButton: Button; + private _colorCheckBoxesMap: Array<{ color: string, checkbox: Checkbox }> = []; + private _selectedColorOption: number; + private _groupNameInputBox: InputBox; + private _groupDescriptionInputBox: InputBox; + private _viewModel: ServerGroupViewModel; + private _skipGroupNameValidation: boolean = false; + private $serverGroupContainer: Builder; + + private _onAddServerGroup = new Emitter(); + public onAddServerGroup: Event = this._onAddServerGroup.event; + + private _onCancel = new Emitter(); + public onCancel: Event = this._onCancel.event; + + private _onCloseEvent = new Emitter(); + public onCloseEvent: Event = this._onCloseEvent.event; + + constructor( + @IPartService partService: IPartService, + @IThemeService private _themeService: IThemeService, + @IContextViewService private _contextViewService: IContextViewService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(localize('ServerGroupsDialogTitle', 'Server Groups'), TelemetryKeys.ServerGroups, partService, telemetryService, contextKeyService); + } + + public render() { + super.render(); + attachModalDialogStyler(this, this._themeService); + let okLabel = localize('ok', 'OK'); + let cancelLabel = localize('cancel', 'Cancel'); + this._addServerButton = this.addFooterButton(okLabel, () => this.addGroup()); + this._closeButton = this.addFooterButton(cancelLabel, () => this.cancel()); + this.registerListeners(); + } + + protected layout(height?: number): void { + // NO OP + } + + protected renderBody(container: HTMLElement) { + new Builder(container).div({ class: 'server-group-dialog' }, (builder) => { + this._bodyBuilder = builder; + }); + // Connection Group Name + this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => { + let serverGroupNameLabel = localize('connectionGroupName', 'Server group name'); + labelContainer.innerHtml(serverGroupNameLabel); + }); + this._bodyBuilder.div({ class: 'input-divider' }, (inputCellContainer) => { + let errorMessage = localize('MissingGroupNameError', 'Group name is required.'); + this._groupNameInputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, { + validationOptions: { + validation: (value: string) => !value && !this._skipGroupNameValidation ? ({ type: MessageType.ERROR, content: errorMessage }) : null + } + }); + }); + + // Connection Group Description + this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => { + let groupDescriptionLabel = localize('groupDescription', 'Group description'); + labelContainer.innerHtml(groupDescriptionLabel); + }); + this._bodyBuilder.div({ class: 'input-divider' }, (inputCellContainer) => { + this._groupDescriptionInputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService); + }); + + // Connection Group Color + this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => { + let groupColorLabel = localize('groupColor', 'Group color'); + labelContainer.innerHtml(groupColorLabel); + }); + + this._bodyBuilder.div({ class: 'group-color-options' }, (groupColorContainer) => { + this.$serverGroupContainer = groupColorContainer; + this.fillGroupColors(groupColorContainer.getHTMLElement()); + }); + + this._bodyBuilder.on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + let event = new StandardKeyboardEvent(e); + if (event.equals(KeyMod.Shift | KeyCode.Tab)) { + this.preventDefaultKeyboardEvent(e); + this.focusPrevious(); + } else if (event.equals(KeyCode.Tab)) { + this.preventDefaultKeyboardEvent(e); + this.focusNext(); + } else if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.LeftArrow)) { + this.preventDefaultKeyboardEvent(e); + this.focusNextColor(event.equals(KeyCode.RightArrow)); + } + }); + } + + private preventDefaultKeyboardEvent(e: KeyboardEvent) { + e.preventDefault(); + e.stopPropagation(); + } + + private isFocusOnColors(): boolean { + + /* + // + // intentionally leaving this code in for future work on tab and keyboard support + // 7/28/2017: kenvh + // + this._colorCheckBoxesMap.forEach((checkbox: Checkbox, color: string) => { + if (document.activeElement === checkbox.getElement === false) { + checkbox.checked = true; + } + }); + */ + + return false; + } + + private focusNext(): void { + /* + // + // intentionally leaving this code in for future work on tab and keyboard support + // 7/28/2017: kenvh + // + if (this._groupNameInputBox.hasFocus()) { + this._groupDescriptionInputBox.focus(); + } else if (this._groupDescriptionInputBox.hasFocus()) { + this._colorCheckBoxesMap[this._viewModel.groupColor].checkbox.focus(); + } else if (this.isFocusOnColors()) { + this._addServerButton.focus(); + } else if (document.activeElement === this._addServerButton.getElement()) { + this._closeButton.focus(); + } + else if (document.activeElement === this._closeButton.getElement()) { + this._groupNameInputBox.focus(); + } + */ + } + + private focusPrevious(): void { + /* + // + // intentionally leaving this code in for future work on tab and keyboard support + // 7/28/2017: kenvh + // + if (document.activeElement === this._closeButton.getElement()) { + this._addServerButton.focus(); + } else if (document.activeElement === this._addServerButton.getElement()) { + this._colorCheckBoxesMap[this._viewModel.groupColor].checkbox.focus(); + } else if (this.isFocusOnColors()) { + this._groupDescriptionInputBox.focus(); + } else if (this._groupDescriptionInputBox.hasFocus()) { + this._groupNameInputBox.focus(); + } else if (this._groupNameInputBox.hasFocus()) { + this._closeButton.focus(); + } + */ + } + + private focusNextColor(moveRight: boolean): void { + /* + // + // intentionally leaving this code in for future work on tab and keyboard support + // 7/28/2017: kenvh + // + let focusIndex: number = -1; + this._colorCheckBoxesMap.forEach((checkbox: Checkbox, color: string) => { + if (document.activeElement === checkbox.domNode. === false) { + checkbox.checked = true; + } + }); + + + for (let i = 0; i < this._colorCheckBoxesMap.size; i++) { + if (this._colorCheckBoxesMap.values[i].hasFocus()) { + focusIndex = i; + break; + } + } + + if (focusIndex >= 0) { + if (moveRight) { + focusIndex++; + } + else { + focusIndex--; + } + + // check for wraps + if (focusIndex < 0) { + focusIndex = this._colorCheckBoxesMap.size - 1; + } else if (focusIndex >= this._colorCheckBoxesMap.size) { + focusIndex = 0; + } + } + */ + } + + private onSelectGroupColor(colorToSelect: string): void { + this._viewModel.groupColor = colorToSelect; + this._selectedColorOption = this._viewModel.colors.indexOf(colorToSelect); + this.updateView(); + } + + private registerListeners(): void { + // Theme styler + this._register(attachInputBoxStyler(this._groupNameInputBox, this._themeService)); + this._register(attachInputBoxStyler(this._groupDescriptionInputBox, this._themeService)); + this._register(attachButtonStyler(this._addServerButton, this._themeService)); + this._register(attachButtonStyler(this._closeButton, this._themeService)); + + // handler for name change events + this._register(this._groupNameInputBox.onDidChange(groupName => { + this.groupNameChanged(groupName); + })); + + // handler for description change events + this._register(this._groupDescriptionInputBox.onDidChange(groupDescription => { + this.groupDescriptionChanged(groupDescription); + })); + } + + private fillGroupColors(container: HTMLElement): void { + for (let i = 0; i < this._viewModel.colors.length; i++) { + let color = this._viewModel.colors[i]; + + let colorCheckBox = new Checkbox({ + actionClassName: 'server-group-color', + title: color, + isChecked: false, + onChange: (viaKeyboard) => { + this.onSelectGroupColor(color); + } + }); + colorCheckBox.domNode.style.backgroundColor = color; + container.appendChild(colorCheckBox.domNode); + + // Theme styler + this._register(attachCheckboxStyler(colorCheckBox, this._themeService)); + + // add the new checkbox to the color map + this._colorCheckBoxesMap[i] = { color, checkbox: colorCheckBox }; + } + } + + private groupNameChanged(groupName: string) { + this._viewModel.groupName = groupName; + this.updateView(); + } + + private groupDescriptionChanged(groupDescription: string) { + this._viewModel.groupDescription = groupDescription; + this.updateView(); + } + + public get groupName(): string { + return this._groupNameInputBox.value; + } + + public get groupDescription(): string { + return this._groupDescriptionInputBox.value; + } + + public get selectedColor(): string { + return this._colorCheckBoxesMap[this._selectedColorOption].color; + } + + public get viewModel(): ServerGroupViewModel { + return this._viewModel; + } + public set viewModel(theViewModel: ServerGroupViewModel) { + this._viewModel = theViewModel; + if (this.$serverGroupContainer) { + this.$serverGroupContainer.clearChildren(); + this.fillGroupColors(this.$serverGroupContainer.getHTMLElement()); + } + } + + public addGroup(): void { + if (this._addServerButton.enabled) { + if (this.validateInputs()) { + this._onAddServerGroup.fire(); + } + } + } + + public hideError() { + this.setError(''); + } + + private validateInputs(): boolean { + let validate = this._groupNameInputBox.validate(); + if (!validate) { + this._groupNameInputBox.focus(); + } + return validate; + } + + // initialize the view based on the current state of the view model + private initializeView(): void { + this.title = this._viewModel.getDialogTitle(); + + this._skipGroupNameValidation = true; + this._groupNameInputBox.value = this._viewModel.groupName; + this._skipGroupNameValidation = false; + + this._groupDescriptionInputBox.value = this._viewModel.groupDescription; + + this.updateView(); + } + + // update UI elements that have derivative behaviors based on other state changes + private updateView(): void { + // check the color buttons and if their checked state does not match the view model state then correct it + for (let i = 0; i < this._colorCheckBoxesMap.length; i++) { + let { checkbox, color } = this._colorCheckBoxesMap[i]; + if ((this._viewModel.groupColor === color) && (checkbox.checked === false)) { + checkbox.checked = true; + this._selectedColorOption = i; + } else if ((this._viewModel.groupColor !== color) && (checkbox.checked === true)) { + checkbox.checked = false; + } + } + + // OK button state - enabled if there are pending changes that can be saved + this._addServerButton.enabled = this._viewModel.hasPendingChanges(); + } + + /* Overwrite escape key behavior */ + protected onClose() { + this.cancel(); + } + + /* Overwrite enter key behavior */ + protected onAccept() { + this.addGroup(); + } + + public cancel() { + this._onCancel.fire(); + this.close(); + } + + public close() { + this.hide(); + this._groupNameInputBox.hideMessage(); + this._onCloseEvent.fire(); + } + + public open() { + // reset the dialog + this.hideError(); + this.initializeView(); + this.show(); + this._groupNameInputBox.focus(); + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/serverGroupDialog/serverGroupViewModel.ts b/src/sql/parts/registeredServer/serverGroupDialog/serverGroupViewModel.ts new file mode 100644 index 0000000000..79914862e0 --- /dev/null +++ b/src/sql/parts/registeredServer/serverGroupDialog/serverGroupViewModel.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper'; +import * as TypeChecker from 'vs/base/common/types'; +import { localize } from 'vs/nls'; + +export class ServerGroupViewModel { + public groupName: string; + public groupDescription: string; + public groupColor: string; + public colors: string[] = ['#515151', '#004760', '#771b00', '#700060', '#a17d01', '#006749', '#654502', '#3A0293']; + + private _domainModel: IConnectionProfileGroup; + private _editMode: boolean; + private readonly _addServerGroupTitle: string = localize('addServerGroup', 'Add server group'); + private readonly _editServerGroupTitle: string = localize('editServerGroup', 'Edit server group'); + private readonly _defaultColor: string = '#515151'; + + constructor(domainModel?: IConnectionProfileGroup, colors?: string[]) { + // keep reference to domain model to be able to see if there are pending changes + if (domainModel) { + this._domainModel = domainModel; + + // initialize the view model properties + this.groupName = domainModel.name; + this.groupColor = domainModel.color; + this.groupDescription = domainModel.description; + + this._editMode = true; + } + else { + // initialize defaults for a new group + this.groupName = ''; + this.groupDescription = ''; + this.groupColor = this._defaultColor; + + this._editMode = false; + } + + if (colors) { + this.colors = colors; + } + } + + // check to see if the current state of the view model is different than the data in the domain model + public hasPendingChanges(): boolean { + if (!TypeChecker.isUndefinedOrNull(this._domainModel)) { + return ((DialogHelper.isNullOrWhiteSpace(this.groupName) === false) && + ((this.groupName !== this._domainModel.name) || + (this.groupDescription !== this._domainModel.description) || + (this.groupColor !== this._domainModel.color))); + } + else { + return (DialogHelper.isNullOrWhiteSpace(this.groupName) === false); + } + } + + public getDialogTitle(): string { + if (this._editMode === true) { + return this._editServerGroupTitle; + } else { + return this._addServerGroupTitle; + } + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/connectionTreeAction.ts b/src/sql/parts/registeredServer/viewlet/connectionTreeAction.ts new file mode 100644 index 0000000000..cd5d9ed4fb --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/connectionTreeAction.ts @@ -0,0 +1,453 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { ServerTreeView } from 'sql/parts/registeredServer/viewlet/serverTreeView'; +import { ConnectionViewlet } from 'sql/parts/registeredServer/viewlet/connectionViewlet'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import * as Constants from 'sql/parts/connection/common/constants'; +import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; +import Severity from 'vs/base/common/severity'; +import { ObjectExplorerActionsContext, ObjectExplorerActionUtilities } from 'sql/parts/registeredServer/viewlet/objectExplorerActions'; + +export class RefreshAction extends Action { + + public static ID = 'objectExplorer.refresh'; + public static LABEL = localize('refresh', 'Refresh'); + private _tree: ITree; + + constructor( + id: string, + label: string, + tree: ITree, + private element: ConnectionProfile | TreeNode, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IObjectExplorerService private _objectExplorerService: IObjectExplorerService, + @IErrorMessageService private _errorMessageService: IErrorMessageService + ) { + super(id, label); + this._tree = tree; + } + public run(): TPromise { + var treeNode: TreeNode; + if (this.element instanceof ConnectionProfile) { + let connection: ConnectionProfile = this.element; + if (this._connectionManagementService.isConnected(undefined, connection)) { + treeNode = this._objectExplorerService.getObjectExplorerNode(connection); + if (treeNode === undefined) { + this._objectExplorerService.updateObjectExplorerNodes(connection.toIConnectionProfile()).then(() => { + treeNode = this._objectExplorerService.getObjectExplorerNode(connection); + }); + } + } + } else if (this.element instanceof TreeNode) { + treeNode = this.element; + } + + if (treeNode) { + this._tree.collapse(this.element).then(() => { + this._objectExplorerService.refreshTreeNode(treeNode.getSession(), treeNode).then(() => { + + this._tree.refresh(this.element).then(() => { + this._tree.expand(this.element); + }, refreshError => { + return TPromise.as(true); + }); + }, error => { + this.showError(error); + return TPromise.as(true); + }); + }, collapseError => { + return TPromise.as(true); + }); + } + return TPromise.as(true); + } + + private showError(errorMessage: string) { + if (this._errorMessageService) { + this._errorMessageService.showDialog(Severity.Error, '', errorMessage); + } + } +} + +export class DisconnectConnectionAction extends Action { + public static ID = 'objectExplorer.disconnect'; + public static LABEL = localize('DisconnectAction', 'Disconnect'); + + private _disposables: IDisposable[] = []; + private _connectionProfile: ConnectionProfile; + + private _container: HTMLElement; + + constructor( + id: string, + label: string, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IObjectExplorerService private _objectExplorerService: IObjectExplorerService, + @IErrorMessageService private _errorMessageService: IErrorMessageService + ) { + super(id, label); + const self = this; + this._disposables.push(this._connectionManagementService.onConnect(() => { + self.setLabel(); + }) + ); + this._disposables.push(this._connectionManagementService.onDisconnect((disconnectParams) => { + if (this._connectionProfile) { + this._connectionProfile.isDisconnecting = false; + } + self.setLabel(); + self._connectionManagementService.closeDashboard(disconnectParams.connectionUri); + }) + ); + if (this._objectExplorerService && this._objectExplorerService.onUpdateObjectExplorerNodes) { + this._disposables.push(this._objectExplorerService.onUpdateObjectExplorerNodes((args) => { + self.removeSpinning(args.connection); + if (args.errorMessage !== undefined) { + self.showError(args.errorMessage); + } + }) + ); + } + } + + private showError(errorMessage: string) { + if (this._errorMessageService) { + this._errorMessageService.showDialog(Severity.Error, '', errorMessage); + } + } + + private setLabel(): void { + if (!this._connectionProfile) { + this.label = 'Connect'; + return; + } + this.label = this._connectionManagementService.isProfileConnected(this._connectionProfile) ? 'Disconnect' : 'Connect'; + } + + private removeSpinning(connection: IConnectionProfile): void { + if (this._connectionProfile) { + if (connection.id === this._connectionProfile.id && this._container) { + ObjectExplorerActionUtilities.hideLoadingIcon(this._container, ObjectExplorerActionUtilities.connectionElementClass); + } + } + } + + run(actionContext: ObjectExplorerActionsContext): TPromise { + return new TPromise((resolve, reject) => { + if (actionContext instanceof ObjectExplorerActionsContext) { + //set objectExplorerTreeNode for context menu clicks + this._connectionProfile = actionContext.connectionProfile; + this._container = actionContext.container; + resolve(true); + } + + if (!this._connectionProfile) { + resolve(true); + } + if (this._connectionManagementService.isProfileConnected(this._connectionProfile)) { + this._connectionProfile.isDisconnecting = true; + this._connectionManagementService.disconnect(this._connectionProfile).then((value) => { + resolve(true); + } + ).catch(disconnectError => { + reject(disconnectError); + }); + } else { + resolve(true); + } + }); + } + + dispose(): void { + super.dispose(); + this._disposables = dispose(this._disposables); + } +} + + +/** + * Actions to add a server to the group + */ +export class AddServerAction extends Action { + public static ID = 'registeredServers.addConnection'; + public static LABEL = localize('addConnection', 'New Connection'); + + constructor( + id: string, + label: string, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService + ) { + super(id, label); + this.class = 'add-server-action'; + } + + public run(element: ConnectionProfileGroup): TPromise { + let connection: IConnectionProfile = { + serverName: undefined, + databaseName: undefined, + userName: undefined, + password: undefined, + authenticationType: undefined, + groupId: undefined, + groupFullName: element.fullName, + savePassword: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: '', + options: {}, + saveProfile: true, + id: element.id + }; + this._connectionManagementService.showConnectionDialog(undefined, connection); + return TPromise.as(true); + } +} + +/** + * Actions to add a server to the group + */ +export class AddServerGroupAction extends Action { + public static ID = 'registeredServers.addServerGroup'; + public static LABEL = localize('addServerGroup', 'New Server Group'); + + constructor( + id: string, + label: string, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService + ) { + super(id, label); + this.class = 'add-server-group-action'; + } + + public run(): TPromise { + this._connectionManagementService.showCreateServerGroupDialog(); + return TPromise.as(true); + } +} + +/** + * Actions to edit a server group + */ +export class EditServerGroupAction extends Action { + public static ID = 'registeredServers.editServerGroup'; + public static LABEL = localize('editServerGroup', 'Edit Server Group'); + + constructor( + id: string, + label: string, + private _group: ConnectionProfileGroup, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService + ) { + super(id, label); + this.class = 'edit-server-group-action'; + } + + public run(): TPromise { + this._connectionManagementService.showEditServerGroupDialog(this._group); + return TPromise.as(true); + } +} + +/** + * Display active connections in the tree + */ +export class ActiveConnectionsFilterAction extends Action { + public static ID = 'registeredServers.recentConnections'; + public static LABEL = localize('activeConnections', 'Show Active Connections'); + private static enabledClass = 'active-connections-action'; + private static disabledClass = 'icon server-page'; + private static clearAllLabel = localize('clearAll', 'Clear All'); + private _isSet: boolean; + public static readonly ACTIVE = 'active'; + public get isSet(): boolean { + return this._isSet; + } + public set isSet(value: boolean) { + this._isSet = value; + this.class = (!this._isSet) ? + ActiveConnectionsFilterAction.enabledClass : ActiveConnectionsFilterAction.disabledClass; + } + + constructor( + id: string, + label: string, + private view: ServerTreeView, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService + ) { + super(id, label); + this.class = ActiveConnectionsFilterAction.enabledClass; + } + + public run(): TPromise { + if (!this.view) { + // return without doing anything + return TPromise.as(true); + } + if (this.class === ActiveConnectionsFilterAction.enabledClass) { + // show active connections in the tree + this.view.showFilteredTree(ActiveConnectionsFilterAction.ACTIVE); + this.isSet = true; + this.label = ActiveConnectionsFilterAction.clearAllLabel; + } else { + // show full tree + this.view.refreshTree(); + this.isSet = false; + this.label = ActiveConnectionsFilterAction.LABEL; + } + return TPromise.as(true); + } +} + +/** + * Display recent connections in the tree + */ +export class RecentConnectionsFilterAction extends Action { + public static ID = 'registeredServers.recentConnections'; + public static LABEL = localize('recentConnections', 'Recent Connections'); + private static enabledClass = 'recent-connections-action'; + private static disabledClass = 'recent-connections-action-set'; + private _isSet: boolean; + public get isSet(): boolean { + return this._isSet; + } + public set isSet(value: boolean) { + this._isSet = value; + this.class = (!this._isSet) ? + RecentConnectionsFilterAction.enabledClass : RecentConnectionsFilterAction.disabledClass; + } + constructor( + id: string, + label: string, + private view: ServerTreeView, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService + ) { + super(id, label); + this.class = RecentConnectionsFilterAction.enabledClass; + this._isSet = false; + } + + public run(): TPromise { + if (!this.view) { + // return without doing anything + return TPromise.as(true); + } + if (this.class === RecentConnectionsFilterAction.enabledClass) { + // show recent connections in the tree + this.view.showFilteredTree('recent'); + this.isSet = true; + } else { + // show full tree + this.view.refreshTree(); + this.isSet = false; + } + return TPromise.as(true); + } +} + +export class NewQueryAction extends Action { + public static ID = 'registeredServers.newQuery'; + public static LABEL = localize('newQuery', 'New Query'); + private _connectionProfile: ConnectionProfile; + get connectionProfile(): ConnectionProfile { + return this._connectionProfile; + } + set connectionProfile(profile: ConnectionProfile) { + this._connectionProfile = profile; + } + + constructor( + id: string, + label: string, + @IQueryEditorService private queryEditorService: IQueryEditorService, + @IConnectionManagementService private connectionManagementService: IConnectionManagementService + ) { + super(id, label); + this.class = 'extension-action update'; + } + + public run(actionContext: ObjectExplorerActionsContext): TPromise { + if (actionContext instanceof ObjectExplorerActionsContext) { + this._connectionProfile = actionContext.connectionProfile; + } + + TaskUtilities.newQuery(this._connectionProfile, this.connectionManagementService, this.queryEditorService); + return TPromise.as(true); + } +} + +/** + * Actions to delete a server/group + */ +export class DeleteConnectionAction extends Action { + public static ID = 'registeredServers.deleteConnection'; + public static DELETE_CONNECTION_LABEL = localize('deleteConnection', 'Delete Connection'); + public static DELETE_CONNECTION_GROUP_LABEL = localize('deleteConnectionGroup', 'Delete Group'); + + constructor( + id: string, + label: string, + private element: ConnectionProfile | ConnectionProfileGroup, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService + ) { + super(id, label); + this.class = 'delete-connection-action'; + if (element instanceof ConnectionProfileGroup && element.id === Constants.unsavedGroupId) { + this.enabled = false; + } + + if (element instanceof ConnectionProfile) { + element = element; + let parent: ConnectionProfileGroup = element.parent; + if (parent && parent.id === Constants.unsavedGroupId) { + this.enabled = false; + } + } + } + + public run(): TPromise { + if (this.element instanceof ConnectionProfile) { + this._connectionManagementService.deleteConnection(this.element); + } else if (this.element instanceof ConnectionProfileGroup) { + this._connectionManagementService.deleteConnectionGroup(this.element); + } + return TPromise.as(true); + } +} + +/** + * Action to clear search results + */ +export class ClearSearchAction extends Action { + public static ID = 'registeredServers.clearSearch'; + public static LABEL = localize('clearSearch', 'Clear Search'); + + constructor( + id: string, + label: string, + private _viewlet: ConnectionViewlet, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService + ) { + super(id, label); + this.class = 'icon close'; + this.enabled = false; + } + + public run(): TPromise { + this._viewlet.clearSearch(); + return TPromise.as(true); + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/connectionViewlet.ts b/src/sql/parts/registeredServer/viewlet/connectionViewlet.ts new file mode 100644 index 0000000000..683757ec11 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/connectionViewlet.ts @@ -0,0 +1,154 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/connectionViewlet'; +import { ThrottledDelayer } from 'vs/base/common/async'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Builder, Dimension } from 'vs/base/browser/builder'; +import { Viewlet } from 'vs/workbench/browser/viewlet'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IAction } from 'vs/base/common/actions'; +import { toggleClass } from 'vs/base/browser/dom'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; +import Severity from 'vs/base/common/severity'; +import { IConnectionsViewlet, IConnectionManagementService, VIEWLET_ID } from 'sql/parts/connection/common/connectionManagement'; +import { ServerTreeView } from 'sql/parts/registeredServer/viewlet/serverTreeView'; +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ClearSearchAction, AddServerAction, AddServerGroupAction, ActiveConnectionsFilterAction } from 'sql/parts/registeredServer/viewlet/connectionTreeAction'; + +export class ConnectionViewlet extends Viewlet implements IConnectionsViewlet { + + private _searchDelayer: ThrottledDelayer; + private _root: HTMLElement; + private _searchBox: InputBox; + private _toDisposeViewlet: IDisposable[] = []; + private _serverTreeView: ServerTreeView; + private _viewletContainer: Builder; + private _searchBoxContainer: Builder; + private _clearSearchAction: ClearSearchAction; + private _addServerAction: IAction; + private _addServerGroupAction: IAction; + private _activeConnectionsFilterAction: ActiveConnectionsFilterAction; + private _searchTerm: string; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService private _themeService: IThemeService, + @IConnectionManagementService private connectionManagementService: IConnectionManagementService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IViewletService private viewletService: IViewletService, + @IMessageService private messageService: IMessageService + ) { + super(VIEWLET_ID, telemetryService, _themeService); + this._searchDelayer = new ThrottledDelayer(500); + + this._clearSearchAction = this._instantiationService.createInstance(ClearSearchAction, ClearSearchAction.ID, ClearSearchAction.LABEL, this); + this._addServerAction = this._instantiationService.createInstance(AddServerAction, + AddServerAction.ID, + AddServerAction.LABEL); + this._addServerGroupAction = this._instantiationService.createInstance(AddServerGroupAction, + AddServerGroupAction.ID, + AddServerGroupAction.LABEL); + this._serverTreeView = this._instantiationService.createInstance(ServerTreeView); + this._activeConnectionsFilterAction = this._serverTreeView.activeConnectionsFilterAction; + } + + private onError(err: any): void { + if (isPromiseCanceledError(err)) { + return; + } + this.messageService.show(Severity.Error, err); + } + + public create(parent: Builder): TPromise { + super.create(parent); + this._root = parent.getHTMLElement(); + parent.div({ class: 'server-explorer-viewlet' }, (viewletContainer) => { + this._viewletContainer = viewletContainer; + viewletContainer.div({ class: 'search-box' }, (searchBoxContainer) => { + this._searchBoxContainer = searchBoxContainer; + this._searchBox = new InputBox( + searchBoxContainer.getHTMLElement(), + null, + { + placeholder: 'Search server names', + actions: [this._clearSearchAction] + } + ); + this._searchTerm = ''; + + this._searchBox.onDidChange(() => { + this.search(this._searchBox.value); + }); + + // Theme styler + this._toDisposeViewlet.push(attachInputBoxStyler(this._searchBox, this._themeService)); + + }); + viewletContainer.div({ Class: 'object-explorer-view' }, (viewContainer) => { + this._serverTreeView.renderBody(viewContainer.getHTMLElement()); + }); + }); + return TPromise.as(null); + } + + public search(value: string): void { + if (value) { + this._clearSearchAction.enabled = true; + this._serverTreeView.searchTree(value); + } else { + this.clearSearch(); + } + } + + public setVisible(visible: boolean): TPromise { + return super.setVisible(visible).then(() => { + this._serverTreeView.setVisible(visible); + }); + } + + /** + * Return actions for the viewlet + */ + public getActions(): IAction[] { + return [this._addServerAction, this._addServerGroupAction, this._activeConnectionsFilterAction]; + } + + public focus(): void { + super.focus(); + } + + public layout({ height, width }: Dimension): void { + this._searchBox.layout(); + this._serverTreeView.layout(height - 36); // account for search box + toggleClass(this._root, 'narrow', width <= 350); + } + + public getOptimalWidth(): number { + return 400; + } + + public clearSearch() { + this._searchTerm = ''; + this._serverTreeView.refreshTree(); + this._searchBox.value = ''; + this._clearSearchAction.enabled = false; + this._searchBox.focus(); + } + + public dispose(): void { + this._serverTreeView.dispose(); + this._toDisposeViewlet = dispose(this._toDisposeViewlet); + } + +} diff --git a/src/sql/parts/registeredServer/viewlet/dragAndDropController.ts b/src/sql/parts/registeredServer/viewlet/dragAndDropController.ts new file mode 100644 index 0000000000..8f436d5d5e --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/dragAndDropController.ts @@ -0,0 +1,206 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { ITree, IDragAndDrop, IDragAndDropData, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree'; +import * as Constants from 'sql/parts/connection/common/constants'; +import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; +import { TreeUpdateUtils } from 'sql/parts/registeredServer/viewlet/treeUpdateUtils'; + +/** + * Implements drag and drop for the server tree + */ +export class ServerTreeDragAndDrop implements IDragAndDrop { + + constructor(@IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + } + + /** + * Returns a uri if the given element should be allowed to drag. + * Returns null, otherwise. + */ + public getDragURI(tree: ITree, element: any): string { + if (element instanceof ConnectionProfile) { + return (element).id; + } + else if (element instanceof ConnectionProfileGroup) { + return (element).id; + } + return null; + } + + /** + * Returns a label(name) to display when dragging the element. + */ + public getDragLabel(tree: ITree, elements: any[]): string { + if (elements[0] instanceof ConnectionProfile) { + return (elements[0]).serverName; + } else if (elements[0] instanceof ConnectionProfileGroup) { + return (elements[0]).name; + } else { + return undefined; + } + } + + /** + * Called when the drag operation starts. + */ + public onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: DragMouseEvent): void { + TreeUpdateUtils.isInDragAndDrop = true; + return; + } + + /** + * Returns a DragOverReaction indicating whether sources can be + * dropped into target or some parent of the target. + * Returns DRAG_OVER_ACCEPT_BUBBLE_DOWN when element is a connection group or connection + */ + public onDragOver(tree: ITree, data: IDragAndDropData, targetElement: any, originalEvent: DragMouseEvent): IDragOverReaction { + + let canDragOver: boolean = true; + if (targetElement instanceof ConnectionProfile || targetElement instanceof ConnectionProfileGroup) { + let targetConnectionProfileGroup = this.getTargetGroup(targetElement); + //Verify if the connection can be moved to the target group + const source = data.getData()[0]; + if (source instanceof ConnectionProfile) { + if (!this._connectionManagementService.canChangeConnectionConfig(source, targetConnectionProfileGroup.id)) { + canDragOver = false; + } + } + + } else { + canDragOver = false; + } + + if (canDragOver) { + return DRAG_OVER_ACCEPT_BUBBLE_DOWN(true); + } else { + return DRAG_OVER_REJECT; + } + } + + /** + * Handle a drop in the server tree. + */ + public drop(tree: ITree, data: IDragAndDropData, targetElement: any, originalEvent: DragMouseEvent): void { + TreeUpdateUtils.isInDragAndDrop = false; + + let targetConnectionProfileGroup: ConnectionProfileGroup = this.getTargetGroup(targetElement); + + const source = data.getData()[0]; + let oldParent: ConnectionProfileGroup = source.getParent(); + const self = this; + if (this.isDropAllowed(targetConnectionProfileGroup, oldParent, source)) { + + if (source instanceof ConnectionProfile) { + // Change group id of profile + this._connectionManagementService.changeGroupIdForConnection(source, targetConnectionProfileGroup.id).then(() => { + TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService, targetConnectionProfileGroup); + }); + } else if (source instanceof ConnectionProfileGroup) { + // Change parent id of group + this._connectionManagementService.changeGroupIdForConnectionGroup(source, targetConnectionProfileGroup).then(() => { + TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService); + }); + } + } + + return; + } + + public dropAbort(tree: ITree, data: IDragAndDropData): void { + TreeUpdateUtils.isInDragAndDrop = false; + } + + private getTargetGroup(targetElement: any): ConnectionProfileGroup { + let targetConnectionProfileGroup: ConnectionProfileGroup; + if (targetElement instanceof ConnectionProfile) { + targetConnectionProfileGroup = (targetElement).getParent(); + } + else { + targetConnectionProfileGroup = targetElement; + } + + return targetConnectionProfileGroup; + } + + private isDropAllowed(targetConnectionProfileGroup: ConnectionProfileGroup, + oldParent: ConnectionProfileGroup, + source: ConnectionProfile | ConnectionProfileGroup): boolean { + + let isDropToItself = source && targetConnectionProfileGroup && (source instanceof ConnectionProfileGroup) && source.name === targetConnectionProfileGroup.name; + let isDropToSameLevel = oldParent && oldParent.equals(targetConnectionProfileGroup); + let isUnsavedDrag = source && (source instanceof ConnectionProfileGroup) && (source.id === Constants.unsavedGroupId); + return (!isDropToSameLevel && !isDropToItself && !isUnsavedDrag); + } +} + +/** + * Implements drag and drop for the connection tree + */ +export class RecentConnectionsDragAndDrop implements IDragAndDrop { + + constructor( @IConnectionManagementService private connectionManagementService: IConnectionManagementService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + } + + /** + * Returns a uri if the given element should be allowed to drag. + * Returns null, otherwise. + */ + public getDragURI(tree: ITree, element: any): string { + if (element instanceof ConnectionProfile) { + return (element).id; + } + else if (element instanceof ConnectionProfileGroup) { + return (element).id; + } + return null; + } + + /** + * Returns a label(name) to display when dragging the element. + */ + public getDragLabel(tree: ITree, elements: any[]): string { + if (elements[0] instanceof ConnectionProfile) { + return (elements[0]).serverName; + } + else if (elements[0] instanceof ConnectionProfileGroup) { + return (elements[0]).name; + } + return undefined; + } + + /** + * Sent when the drag operation is starting. + */ + public onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: DragMouseEvent): void { + return; + } + + /** + * Returns a DragOverReaction indicating whether sources can be + * dropped into target or some parent of the target. + */ + public onDragOver(tree: ITree, data: IDragAndDropData, targetElement: any, originalEvent: DragMouseEvent): IDragOverReaction { + return DRAG_OVER_REJECT; + } + + /** + * Handle drop in the server tree. + */ + public drop(tree: ITree, data: IDragAndDropData, targetElement: any, originalEvent: DragMouseEvent): void { + // No op + } + + public dropAbort(tree: ITree, data: IDragAndDropData): void { } +} diff --git a/src/sql/parts/registeredServer/viewlet/media/add_server.svg b/src/sql/parts/registeredServer/viewlet/media/add_server.svg new file mode 100644 index 0000000000..e0e14084b3 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/add_server.svg @@ -0,0 +1 @@ +add_server_16x16 \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/add_server_inverse.svg b/src/sql/parts/registeredServer/viewlet/media/add_server_inverse.svg new file mode 100644 index 0000000000..e5d69121a4 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/add_server_inverse.svg @@ -0,0 +1 @@ +add_server_inverse_16x16 \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/collapsed-dark.svg b/src/sql/parts/registeredServer/viewlet/media/collapsed-dark.svg new file mode 100644 index 0000000000..cf5c3641aa --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/collapsed-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/connected_active_server.svg b/src/sql/parts/registeredServer/viewlet/media/connected_active_server.svg new file mode 100644 index 0000000000..c8fc5dd61c --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/connected_active_server.svg @@ -0,0 +1 @@ +connected_active_server_16x16 \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/connected_active_server_inverse.svg b/src/sql/parts/registeredServer/viewlet/media/connected_active_server_inverse.svg new file mode 100644 index 0000000000..5f9888af87 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/connected_active_server_inverse.svg @@ -0,0 +1 @@ +connected_active_server_inverse_16x16 \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/connectionViewlet.css b/src/sql/parts/registeredServer/viewlet/media/connectionViewlet.css new file mode 100644 index 0000000000..28b6166d0f --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/connectionViewlet.css @@ -0,0 +1,136 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* --- Registered servers tree viewlet --- */ +.server-explorer-viewlet .monaco-tree .monaco-tree-row .content .server-group { + cursor: default; + width: 100%; + display: flex; + align-items: center; +} + +/* Bold font style does not go well with CJK fonts */ +.server-explorer-viewlet:lang(zh-Hans) .monaco-tree .monaco-tree-row .server-group, +.server-explorer-viewlet:lang(zh-Hant) .monaco-tree .monaco-tree-row .server-group, +.server-explorer-viewlet:lang(ja) .monaco-tree .monaco-tree-row .server-group, +.server-explorer-viewlet:lang(ko) .monaco-tree .monaco-tree-row .server-group { font-weight: normal; } + +/* High Contrast Theming */ +.hc-black .monaco-workbench .server-explorer-viewlet .server-group { + line-height: 20px; +} + +.monaco-workbench > .activitybar .monaco-action-bar .action-label.serverTree { + background-size: 22px; + background-repeat: no-repeat; + background-position: 50% !important; +} + +.server-explorer-viewlet .object-explorer-view { + height: calc(100% - 36px); +} + +.server-explorer-viewlet .server-group { + height: 38px; + line-height: 38px; + color: #ffffff; +} + +.server-explorer-viewlet .monaco-action-bar .action-label { + margin-right: 0; + margin-left: 0.3em; + line-height: 15px; +} + +/* Add space beneath the button */ +.new-connection .monaco-text-button { +margin-bottom: 2px; +} + +/* display action buttons on hover */ +.server-explorer-viewlet .monaco-tree .monaco-tree-row > .content { + display: flex; +} + +/* Added to display the tree in connection dialog */ +.server-explorer-viewlet { + height: 100%; +} + +.explorer-servers { + height: 100%; +} + +/* search box */ +.server-explorer-viewlet .search-box { + padding-bottom: 4px; + margin: auto; + width: 95%; +} + +/* OE and connection element group */ +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile, +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group { + padding: 5px; + overflow: hidden; +} + +/* OE and connection label */ +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .label, +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .label { + text-overflow: ellipsis; + overflow: hidden; +} + +/* OE and connection icon */ +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon, +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .icon { + float: left; + height: 16px; + width: 16px; + padding-right: 10px; +} + +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected { + background: url('connected_active_server.svg') center center no-repeat; +} + +.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected, +.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected{ + background: url('connected_active_server_inverse.svg') center center no-repeat; +} + +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected { + background: url('disconnected_server.svg') center center no-repeat; +} + +.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected, +.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected{ + background: url('disconnected_server_inverse.svg') center center no-repeat; +} + +/* loading for OE node */ +.server-explorer-viewlet .monaco-tree .monaco-tree-rows > .monaco-tree-row > .icon.in-progress .connection-tile:before, +.server-explorer-viewlet .monaco-tree .monaco-tree-rows > .monaco-tree-row > .icon.in-progress .object-element-group:before { + position: absolute; + display: block; + width: 36px; + height: 100%; + top: 0; + left: -35px; +} + +.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.expanded.has-children > .content.server-group:before { + background: url('expanded-dark.svg') 50% 50% no-repeat; +} + +.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content.server-group:before { + background: url('collapsed-dark.svg') 50% 50% no-repeat; +} + +/* Add connection button */ +.server-explorer-viewlet .button-section { + padding: 20px; +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/disconnected_server.svg b/src/sql/parts/registeredServer/viewlet/media/disconnected_server.svg new file mode 100644 index 0000000000..da2a3cada1 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/disconnected_server.svg @@ -0,0 +1 @@ +disconnected_server_16x16 \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/disconnected_server_inverse.svg b/src/sql/parts/registeredServer/viewlet/media/disconnected_server_inverse.svg new file mode 100644 index 0000000000..ebed595c90 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/disconnected_server_inverse.svg @@ -0,0 +1 @@ +disconnected_server_inverse_16x16 \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/expanded-dark.svg b/src/sql/parts/registeredServer/viewlet/media/expanded-dark.svg new file mode 100644 index 0000000000..73d41e6399 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/expanded-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/new_servergroup.svg b/src/sql/parts/registeredServer/viewlet/media/new_servergroup.svg new file mode 100644 index 0000000000..060223c5db --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/new_servergroup.svg @@ -0,0 +1 @@ +new_servergroup_16x16 \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/new_servergroup_inverse.svg b/src/sql/parts/registeredServer/viewlet/media/new_servergroup_inverse.svg new file mode 100644 index 0000000000..78c01d667a --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/new_servergroup_inverse.svg @@ -0,0 +1 @@ +new_servergroup_inverse_16x16 \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/media/serverTreeActions.css b/src/sql/parts/registeredServer/viewlet/media/serverTreeActions.css new file mode 100644 index 0000000000..a74f8ce461 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/media/serverTreeActions.css @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Icons for various registered servers actions */ +.monaco-workbench .add-server-action { + background-image: url('add_server.svg'); +} + +.vs-dark .monaco-workbench .add-server-action, +.hc-black .monaco-workbench .add-server-action { + background-image: url('add_server_inverse.svg'); +} + +.monaco-workbench .add-server-group-action { + background-image: url('new_servergroup.svg'); +} + +.vs-dark .monaco-workbench .add-server-group-action, +.hc-black .monaco-workbench .add-server-group-action { + background-image: url('new_servergroup_inverse.svg'); +} + +.monaco-workbench .active-connections-action { + background-image: url('connected_active_server.svg'); +} + +.vs-dark .monaco-workbench .active-connections-action, +.hc-black .monaco-workbench .active-connections-action{ + background-image: url('connected_active_server_inverse.svg'); +} diff --git a/src/sql/parts/registeredServer/viewlet/objectExplorerActions.ts b/src/sql/parts/registeredServer/viewlet/objectExplorerActions.ts new file mode 100644 index 0000000000..5ebc17af2b --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/objectExplorerActions.ts @@ -0,0 +1,370 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { IConnectionManagementService, IConnectionCompletionOptions, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, ScriptDeleteAction } from 'sql/workbench/common/actions'; +import { NodeType } from 'sql/parts/registeredServer/common/nodeType'; +import { TreeUpdateUtils } from 'sql/parts/registeredServer/viewlet/treeUpdateUtils'; +import { TreeSelectionHandler } from 'sql/parts/registeredServer/viewlet/treeSelectionHandler'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IScriptingService } from 'sql/services/scripting/scriptingService'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; + +export class ObjectExplorerActionsContext { + public treeNode: TreeNode; + public connectionProfile: ConnectionProfile; + public container: HTMLElement; + public tree: ITree; +} + +export class OENewQueryAction extends NewQueryAction { + public static ID = 'objectExplorer.' + NewQueryAction.ID; + private _objectExplorerTreeNode: TreeNode; + private _container: HTMLElement; + private _treeSelectionHandler: TreeSelectionHandler; + + constructor( + id: string, label: string, icon: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(id, label, icon, _queryEditorService, _connectionManagementService); + } + + public run(actionContext: any): TPromise { + this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); + if (actionContext instanceof ObjectExplorerActionsContext) { + //set objectExplorerTreeNode for context menu clicks + this._objectExplorerTreeNode = actionContext.treeNode; + this._container = actionContext.container; + } + this._treeSelectionHandler.onTreeActionStateChange(true); + var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode); + + return super.run({ profile: connectionProfile }).then(() => { + this._treeSelectionHandler.onTreeActionStateChange(false); + return true; + }); + } +} + +export class ManageConnectionAction extends Action { + public static ID = 'objectExplorer.manage'; + public static LABEL = localize('ManageAction', 'Manage'); + + private _connectionProfile: ConnectionProfile; + private _objectExplorerTreeNode: TreeNode; + private _treeSelectionHandler: TreeSelectionHandler; + + protected _container: HTMLElement; + + constructor( + id: string, + label: string, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IObjectExplorerService private _objectExplorerService?: IObjectExplorerService, + ) { + super(id, label); + } + + run(actionContext: ObjectExplorerActionsContext): TPromise { + this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); + this._treeSelectionHandler.onTreeActionStateChange(true); + let promise = new TPromise((resolve, reject) => { + this.doManage(actionContext).then((success) => { + this.done(); + resolve(success); + }, error => { + this.done(); + reject(error); + }); + }); + return promise; + } + + private doManage(actionContext: ObjectExplorerActionsContext): Thenable { + if (actionContext instanceof ObjectExplorerActionsContext) { + //set objectExplorerTreeNode for context menu clicks + this._connectionProfile = actionContext.connectionProfile; + this._objectExplorerTreeNode = actionContext.treeNode; + if (this._connectionProfile === undefined && TreeUpdateUtils.isDatabaseNode(this._objectExplorerTreeNode)) { + this._connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode); + } + this._container = actionContext.container; + } + + if (!this._connectionProfile) { + // This should never happen. There should be always a valid connection if the manage action is called for + // an OE node or a database node + return TPromise.wrap(true); + } + + let options: IConnectionCompletionOptions = { + params: undefined, + saveTheConnection: false, + showConnectionDialogOnError: true, + showDashboard: true, + showFirewallRuleOnError: true + }; + + // If it's a database node just open a database connection and open dashboard, + // the node is already from an open OE session we don't need to create new session + if (TreeUpdateUtils.isAvailableDatabaseNode(this._objectExplorerTreeNode)) { + return this._connectionManagementService.showDashboard(this._connectionProfile); + } else { + return TreeUpdateUtils.connectAndCreateOeSession(this._connectionProfile, options, this._connectionManagementService, this._objectExplorerService, actionContext.tree); + } + } + + private done() { + this._treeSelectionHandler.onTreeActionStateChange(false); + } + + dispose(): void { + super.dispose(); + } +} + +export class OEScriptSelectAction extends ScriptSelectAction { + public static ID = 'objectExplorer.' + ScriptSelectAction.ID; + private _objectExplorerTreeNode: TreeNode; + private _container: HTMLElement; + private _treeSelectionHandler: TreeSelectionHandler; + + constructor( + id: string, label: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + @IScriptingService protected _scriptingService: IScriptingService, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(id, label, _queryEditorService, _connectionManagementService, _scriptingService); + } + + public run(actionContext: any): TPromise { + this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); + if (actionContext instanceof ObjectExplorerActionsContext) { + //set objectExplorerTreeNode for context menu clicks + this._objectExplorerTreeNode = actionContext.treeNode; + this._container = actionContext.container; + } + this._treeSelectionHandler.onTreeActionStateChange(true); + var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode); + var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile); + ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile); + var metadata = (this._objectExplorerTreeNode).metadata; + + return super.run({ profile: connectionProfile, object: metadata, uri: ownerUri }).then((result) => { + this._treeSelectionHandler.onTreeActionStateChange(false); + return result; + }); + } +} + +export class OEEditDataAction extends EditDataAction { + public static ID = 'objectExplorer.' + EditDataAction.ID; + private _objectExplorerTreeNode: TreeNode; + private _container: HTMLElement; + private _treeSelectionHandler: TreeSelectionHandler; + + constructor( + id: string, label: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(id, label, _queryEditorService, _connectionManagementService); + } + + public run(actionContext: any): TPromise { + this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); + if (actionContext instanceof ObjectExplorerActionsContext) { + //set objectExplorerTreeNode for context menu clicks + this._objectExplorerTreeNode = actionContext.treeNode; + this._container = actionContext.container; + } + this._treeSelectionHandler.onTreeActionStateChange(true); + var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode); + var metadata = (this._objectExplorerTreeNode).metadata; + + return super.run({ profile: connectionProfile, object: metadata }).then((result) => { + this._treeSelectionHandler.onTreeActionStateChange(false); + return true; + }); + } +} + +export class OEScriptCreateAction extends ScriptCreateAction { + public static ID = 'objectExplorer.' + ScriptCreateAction.ID; + private _objectExplorerTreeNode: TreeNode; + private _container: HTMLElement; + private _treeSelectionHandler: TreeSelectionHandler; + + constructor( + id: string, label: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + @IScriptingService protected _scriptingService: IScriptingService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IErrorMessageService protected _errorMessageService: IErrorMessageService + ) { + super(id, label, _queryEditorService, _connectionManagementService, _scriptingService, _errorMessageService); + } + + public run(actionContext: any): TPromise { + this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); + if (actionContext instanceof ObjectExplorerActionsContext) { + //set objectExplorerTreeNode for context menu clicks + this._objectExplorerTreeNode = actionContext.treeNode; + this._container = actionContext.container; + } + this._treeSelectionHandler.onTreeActionStateChange(true); + var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode); + var metadata = (this._objectExplorerTreeNode).metadata; + var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile); + ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile); + + return super.run({ profile: connectionProfile, object: metadata, uri: ownerUri }).then((result) => { + this._treeSelectionHandler.onTreeActionStateChange(false); + return result; + }); + } +} + +export class OEScriptDeleteAction extends ScriptDeleteAction { + public static ID = 'objectExplorer.' + ScriptDeleteAction.ID; + private _objectExplorerTreeNode: TreeNode; + private _container: HTMLElement; + private _treeSelectionHandler: TreeSelectionHandler; + + constructor( + id: string, label: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + @IScriptingService protected _scriptingService: IScriptingService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IErrorMessageService protected _errorMessageService: IErrorMessageService + ) { + super(id, label, _queryEditorService, _connectionManagementService, _scriptingService, _errorMessageService); + } + + public run(actionContext: any): TPromise { + this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); + if (actionContext instanceof ObjectExplorerActionsContext) { + //set objectExplorerTreeNode for context menu clicks + this._objectExplorerTreeNode = actionContext.treeNode; + this._container = actionContext.container; + } + this._treeSelectionHandler.onTreeActionStateChange(true); + var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode); + var metadata = (this._objectExplorerTreeNode).metadata; + var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile); + ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile); + + return super.run({ profile: connectionProfile, object: metadata, uri: ownerUri }).then((result) => { + this._treeSelectionHandler.onTreeActionStateChange(false); + return result; + }); + } +} + +export class DisconnectAction extends Action { + public static ID = 'objectExplorer.disconnect'; + public static LABEL = localize('disconnect', 'Disconnect'); + private _objectExplorerTreeNode: TreeNode; + private _container: HTMLElement; + private _treeSelectionHandler: TreeSelectionHandler; + + constructor( + id: string, + label: string, + @IConnectionManagementService private connectionManagementService: IConnectionManagementService, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(id, label); + } + + public run(actionContext: any): TPromise { + this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); + if (actionContext instanceof ObjectExplorerActionsContext) { + //set objectExplorerTreeNode for context menu clicks + this._objectExplorerTreeNode = actionContext.treeNode; + this._container = actionContext.container; + } + + var connectionProfile = (this._objectExplorerTreeNode).getConnectionProfile(); + if (this.connectionManagementService.isProfileConnected(connectionProfile)) { + this._treeSelectionHandler.onTreeActionStateChange(true); + + this.connectionManagementService.disconnect(connectionProfile).then(() => { + this._treeSelectionHandler.onTreeActionStateChange(false); + }); + } + + return TPromise.as(true); + } +} + +export class ObjectExplorerActionUtilities { + + public static readonly objectExplorerElementClass = 'object-element-group'; + public static readonly connectionElementClass = 'connection-tile'; + + + private static getGroupContainer(container: HTMLElement, elementName: string): HTMLElement { + var element = container; + while (element && element.className !== elementName) { + element = element.parentElement; + } + return element ? element.parentElement : undefined; + } + + public static showLoadingIcon(container: HTMLElement, elementName: string): void { + if (container) { + let groupContainer = this.getGroupContainer(container, elementName); + if (groupContainer) { + groupContainer.classList.add('icon'); + groupContainer.classList.add('in-progress'); + } + } + } + + public static hideLoadingIcon(container: HTMLElement, elementName: string): void { + if (container) { + let element = this.getGroupContainer(container, elementName); + if (element && element.classList) { + element.classList.remove('icon'); + element.classList.remove('in-progress'); + } + } + } + + public static getScriptMap(): Map { + var scriptMap = new Map(); + var basicScripting = [OEScriptCreateAction, OEScriptDeleteAction]; + scriptMap.set(NodeType.AggregateFunction, basicScripting); + scriptMap.set(NodeType.PartitionFunction, basicScripting); + scriptMap.set(NodeType.ScalarValuedFunction, basicScripting); + scriptMap.set(NodeType.Schema, basicScripting); + scriptMap.set(NodeType.StoredProcedure, basicScripting); + scriptMap.set(NodeType.Table, [OEScriptSelectAction, OEEditDataAction, OEScriptCreateAction, OEScriptDeleteAction]); + scriptMap.set(NodeType.TableValuedFunction, basicScripting); + scriptMap.set(NodeType.User, basicScripting); + scriptMap.set(NodeType.UserDefinedTableType, basicScripting); + scriptMap.set(NodeType.View, [OEScriptSelectAction, OEScriptCreateAction, OEScriptDeleteAction]); + return scriptMap; + } +} + diff --git a/src/sql/parts/registeredServer/viewlet/recentConnectionDataSource.ts b/src/sql/parts/registeredServer/viewlet/recentConnectionDataSource.ts new file mode 100644 index 0000000000..7abdca61c5 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/recentConnectionDataSource.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; +import { TPromise } from 'vs/base/common/winjs.base'; + +/** + * Implements the DataSource(that returns a parent/children of an element) for the recent connection tree + */ +export class RecentConnectionDataSource implements IDataSource { + + /** + * Returns the unique identifier of the given element. + * No more than one element may use a given identifier. + */ + public getId(tree: ITree, element: any): string { + if (element instanceof ConnectionProfile) { + return (element).id; + } else if (element instanceof ConnectionProfileGroup) { + return (element).id; + } else { + return undefined; + } + } + + /** + * Returns a boolean value indicating whether the element has children. + */ + public hasChildren(tree: ITree, element: any): boolean { + if (element instanceof ConnectionProfile) { + return false; + } else if (element instanceof ConnectionProfileGroup) { + return (element).hasChildren(); + } + return false; + } + + /** + * Returns the element's children as an array in a promise. + */ + public getChildren(tree: ITree, element: any): TPromise { + if (element instanceof ConnectionProfile) { + return TPromise.as(null); + } else if (element instanceof ConnectionProfileGroup) { + return TPromise.as((element).getChildren()); + } else { + return TPromise.as(null); + } + } + + /** + * Returns the element's parent in a promise. + */ + public getParent(tree: ITree, element: any): TPromise { + if (element instanceof ConnectionProfile) { + return TPromise.as((element).getParent()); + } else if (element instanceof ConnectionProfileGroup) { + return TPromise.as((element).getParent()); + } else { + return TPromise.as(null); + } + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/serverTreeActionProvider.ts b/src/sql/parts/registeredServer/viewlet/serverTreeActionProvider.ts new file mode 100644 index 0000000000..ffd2079b4b --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/serverTreeActionProvider.ts @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { ContributableActionProvider } from 'vs/workbench/browser/actions'; +import { IAction } from 'vs/base/common/actions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +import { + DisconnectConnectionAction, AddServerAction, NewQueryAction, + DeleteConnectionAction, RefreshAction, EditServerGroupAction +} + from 'sql/parts/registeredServer/viewlet/connectionTreeAction'; +import { + OENewQueryAction, DisconnectAction, ObjectExplorerActionUtilities, + ManageConnectionAction +} from 'sql/parts/registeredServer/viewlet/objectExplorerActions'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; +import { NodeType } from 'sql/parts/registeredServer/common/nodeType'; +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { NewProfilerAction } from 'sql/parts/profiler/contrib/profilerActions'; +import { TreeUpdateUtils } from 'sql/parts/registeredServer/viewlet/treeUpdateUtils'; + +/** + * Provides actions for the server tree elements + */ +export class ServerTreeActionProvider extends ContributableActionProvider { + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(); + } + + public hasActions(tree: ITree, element: any): boolean { + return element instanceof ConnectionProfileGroup || (element instanceof ConnectionProfile) || (element instanceof TreeNode); + } + + /** + * Return actions given an element in the tree + */ + public getActions(tree: ITree, element: any): TPromise { + if (element instanceof ConnectionProfile) { + return TPromise.as(this.getConnectionActions(tree, element)); + } + if (element instanceof ConnectionProfileGroup) { + return TPromise.as(this.getConnectionProfileGroupActions(tree, element)); + } + if (element instanceof TreeNode) { + var treeNode = element; + return TPromise.as(this.getObjectExplorerNodeActions(tree, treeNode)); + } + + return TPromise.as([]); + } + + public hasSecondaryActions(tree: ITree, element: any): boolean { + return false; + } + + public getSecondaryActions(tree: ITree, element: any): TPromise { + return super.getSecondaryActions(tree, element); + } + + /** + * Return actions for connection elements + */ + public getConnectionActions(tree: ITree, element: ConnectionProfile): IAction[] { + let actions: IAction[] = [ + this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL), + this._instantiationService.createInstance(NewQueryAction, NewQueryAction.ID, NewQueryAction.LABEL), + this._instantiationService.createInstance(DisconnectConnectionAction, DisconnectConnectionAction.ID, DisconnectConnectionAction.LABEL), + this._instantiationService.createInstance(DeleteConnectionAction, DeleteConnectionAction.ID, DeleteConnectionAction.DELETE_CONNECTION_LABEL, element), + this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, tree, element) + ]; + + if (process.env['VSCODE_DEV']) { + actions.push(this._instantiationService.createInstance(NewProfilerAction, NewProfilerAction.ID, NewProfilerAction.LABEL, NewProfilerAction.ICON)); + } + + return actions; + } + + /** + * Return actions for connection group elements + */ + public getConnectionProfileGroupActions(tree: ITree, element: ConnectionProfileGroup): IAction[] { + return [ + this._instantiationService.createInstance(AddServerAction, AddServerAction.ID, AddServerAction.LABEL), + this._instantiationService.createInstance(EditServerGroupAction, EditServerGroupAction.ID, EditServerGroupAction.LABEL, element), + this._instantiationService.createInstance(DeleteConnectionAction, DeleteConnectionAction.ID, DeleteConnectionAction.DELETE_CONNECTION_GROUP_LABEL, element) + ]; + } + + /** + * Return actions for OE elements + */ + public getObjectExplorerNodeActions(tree: ITree, treeNode: TreeNode): IAction[] { + let actions = []; + if (TreeUpdateUtils.isDatabaseNode(treeNode)) { + if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) { + actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL)); + } else { + return actions; + } + } + actions.push(this._instantiationService.createInstance(OENewQueryAction, OENewQueryAction.ID, OENewQueryAction.LABEL, OENewQueryAction.ICON)); + let scriptMap: Map = ObjectExplorerActionUtilities.getScriptMap(); + let supportedActions = scriptMap.get(treeNode.nodeTypeId); + let self = this; + + if (supportedActions !== null && supportedActions !== undefined) { + supportedActions.forEach(action => { + actions.push(self._instantiationService.createInstance(action, action.ID, action.LABEL)); + }); + } + actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, tree, treeNode)); + + + if (treeNode.isTopLevel()) { + actions.push(this._instantiationService.createInstance(DisconnectAction, DisconnectAction.ID, DisconnectAction.LABEL)); + } + + return actions; + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/serverTreeController.ts b/src/sql/parts/registeredServer/viewlet/serverTreeController.ts new file mode 100644 index 0000000000..27fcb9fb60 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/serverTreeController.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { ITree, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree'; +import treedefaults = require('vs/base/parts/tree/browser/treeDefaults'); +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { ServerTreeActionProvider } from 'sql/parts/registeredServer/viewlet/serverTreeActionProvider'; +import { ObjectExplorerActionsContext } from 'sql/parts/registeredServer/viewlet/objectExplorerActions'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; + +/** + * Extends the tree controller to handle clicks on the tree elements + */ +export class ServerTreeController extends treedefaults.DefaultController { + + constructor(private actionProvider: ServerTreeActionProvider, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IContextMenuService private contextMenuService: IContextMenuService, + @ITelemetryService private telemetryService: ITelemetryService, + @IKeybindingService private keybindingService: IKeybindingService + ) { + super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN }); + } + + public onClick(tree: ITree, element: any, event: IMouseEvent): boolean { + return super.onClick(tree, element, event); + } + + protected onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean { + return super.onLeftClick(tree, element, event, origin); + } + + // Do not allow left / right to expand and collapse groups #7848 + protected onLeft(tree: ITree, event: IKeyboardEvent): boolean { + return true; + } + + protected onRight(tree: ITree, event: IKeyboardEvent): boolean { + return true; + } + + protected onEnter(tree: ITree, event: IKeyboardEvent): boolean { + return super.onEnter(tree, event); + } + + /** + * Return actions in the context menu + */ + public onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean { + if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { + return false; + } + // Check if clicked on some element + if (element === tree.getInput()) { + return false; + } + + event.preventDefault(); + event.stopPropagation(); + + tree.setFocus(element); + let parent: ConnectionProfileGroup = undefined; + if (element instanceof ConnectionProfileGroup) { + parent = element; + } + else if (element instanceof ConnectionProfile) { + parent = (element).parent; + } + + var actionContext: any; + if (element instanceof TreeNode) { + actionContext = new ObjectExplorerActionsContext(); + actionContext.container = event.target; + actionContext.treeNode = element; + actionContext.tree = tree; + } else if (element instanceof ConnectionProfile) { + actionContext = new ObjectExplorerActionsContext(); + actionContext.container = event.target; + actionContext.connectionProfile = element; + actionContext.tree = tree; + } else { + actionContext = element; + } + + let anchor = { x: event.posx + 1, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this.actionProvider.getActions(tree, element), + getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id), + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + tree.DOMFocus(); + } + }, + getActionsContext: () => (actionContext) + }); + + return true; + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/serverTreeDataSource.ts b/src/sql/parts/registeredServer/viewlet/serverTreeDataSource.ts new file mode 100644 index 0000000000..e6c6024cc2 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/serverTreeDataSource.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; +import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { TreeUpdateUtils } from 'sql/parts/registeredServer/viewlet/treeUpdateUtils'; +import { IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import Severity from 'vs/base/common/severity'; + +/** + * Implements the DataSource(that returns a parent/children of an element) for the server tree + */ +export class ServerTreeDataSource implements IDataSource { + + constructor( + @IObjectExplorerService private _objectExplorerService: IObjectExplorerService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IErrorMessageService private _errorMessageService: IErrorMessageService + ) { + } + + /** + * Returns the unique identifier of the given element. + * No more than one element may use a given identifier. + */ + public getId(tree: ITree, element: any): string { + if (element instanceof ConnectionProfile) { + return (element).id; + } else if (element instanceof ConnectionProfileGroup) { + return (element).id; + } else if (element instanceof TreeNode) { + return (element).id; + } else { + return undefined; + } + } + + /** + * Returns a boolean value indicating whether the element has children. + */ + public hasChildren(tree: ITree, element: any): boolean { + if (element instanceof ConnectionProfile) { + return true; + } else if (element instanceof ConnectionProfileGroup) { + return (element).hasChildren(); + } else if (element instanceof TreeNode) { + return !(element).isAlwaysLeaf; + } + return false; + } + + /** + * Returns the element's children as an array in a promise. + */ + public getChildren(tree: ITree, element: any): TPromise { + return new TPromise((resolve) => { + if (element instanceof ConnectionProfile) { + TreeUpdateUtils.getObjectExplorerNode(element, this._connectionManagementService, this._objectExplorerService).then(nodes => { + resolve(nodes); + }, error => { + resolve([]); + }); + } else if (element instanceof ConnectionProfileGroup) { + resolve((element).getChildren()); + } else if (element instanceof TreeNode) { + var node = element; + if (node.children) { + resolve(node.children); + } else { + this._objectExplorerService.expandTreeNode(node.getSession(), node).then(() => { + resolve(node.children); + }, expandError => { + this.showError(expandError); + resolve([]); + }); + } + } else { + resolve([]); + } + }); + } + + /** + * Returns the element's parent in a promise. + */ + public getParent(tree: ITree, element: any): TPromise { + if (element instanceof ConnectionProfile) { + return TPromise.as((element).getParent()); + } else if (element instanceof ConnectionProfileGroup) { + return TPromise.as((element).getParent()); + } else if (element instanceof TreeNode) { + return TPromise.as(TreeUpdateUtils.getObjectExplorerParent(element, this._connectionManagementService)); + } else { + return TPromise.as(null); + } + } + + private showError(errorMessage: string) { + if (this._errorMessageService) { + this._errorMessageService.showDialog(Severity.Error, '', errorMessage); + } + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/serverTreeRenderer.ts b/src/sql/parts/registeredServer/viewlet/serverTreeRenderer.ts new file mode 100644 index 0000000000..8d333e477b --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/serverTreeRenderer.ts @@ -0,0 +1,196 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import 'vs/css!sql/media/objectTypes/objecttypes'; +import 'vs/css!sql/media/icons/common-icons'; + +import * as dom from 'vs/base/browser/dom'; +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree'; +import { IConnectionProfileGroupTemplateData, IConnectionTemplateData, IObjectExplorerTemplateData } from 'sql/parts/registeredServer/viewlet/templateData'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; + +/** + * Renders the tree items. + * Uses the dom template to render connection groups and connections. + */ +export class ServerTreeRenderer implements IRenderer { + + public static CONNECTION_HEIGHT = 28; + public static CONNECTION_GROUP_HEIGHT = 38; + private static CONNECTION_TEMPLATE_ID = 'connectionProfile'; + private static CONNECTION_GROUP_TEMPLATE_ID = 'connectionProfileGroup'; + public static OBJECTEXPLORER_HEIGHT = 28; + private static OBJECTEXPLORER_TEMPLATE_ID = 'objectExplorer'; + /** + * _isCompact is used to render connections tiles with and without the action buttons. + * When set to true, like in the connection dialog recent connections tree, the connection + * tile is rendered without the action buttons( such as connect, new query). + */ + private _isCompact: boolean = false; + + constructor(isCompact: boolean, + @IInstantiationService private _instantiationService: IInstantiationService, + @IContextViewService private _contextViewService: IContextViewService, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IThemeService private _themeService: IThemeService + ) { + // isCompact defaults to false unless explicitly set by instantiation call. + if (isCompact) { + this._isCompact = isCompact; + } + } + + /** + * Returns the element's height in the tree, in pixels. + */ + public getHeight(tree: ITree, element: any): number { + if (element instanceof ConnectionProfileGroup) { + return ServerTreeRenderer.CONNECTION_GROUP_HEIGHT; + } else if (element instanceof ConnectionProfile) { + return ServerTreeRenderer.CONNECTION_HEIGHT; + } + return ServerTreeRenderer.OBJECTEXPLORER_HEIGHT; + } + + /** + * Returns a template ID for a given element. + */ + public getTemplateId(tree: ITree, element: any): string { + if (element instanceof ConnectionProfileGroup) { + return ServerTreeRenderer.CONNECTION_GROUP_TEMPLATE_ID; + } else if (element instanceof ConnectionProfile) { + return ServerTreeRenderer.CONNECTION_TEMPLATE_ID; + } + return ServerTreeRenderer.OBJECTEXPLORER_TEMPLATE_ID; + } + + /** + * Render template in a dom element based on template id + */ + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + + if (templateId === ServerTreeRenderer.CONNECTION_TEMPLATE_ID) { + const connectionTemplate: IObjectExplorerTemplateData = Object.create(null); + connectionTemplate.root = dom.append(container, dom.$('.connection-tile')); + connectionTemplate.icon = dom.append(connectionTemplate.root, dom.$('div.icon server-page')); + connectionTemplate.label = dom.append(connectionTemplate.root, dom.$('div.label')); + return connectionTemplate; + } else if (templateId === ServerTreeRenderer.CONNECTION_GROUP_TEMPLATE_ID) { + container.classList.add('server-group'); + const groupTemplate: IConnectionProfileGroupTemplateData = Object.create(null); + groupTemplate.root = dom.append(container, dom.$('.server-group')); + groupTemplate.name = dom.append(groupTemplate.root, dom.$('span.name')); + return groupTemplate; + } else { + const objectExplorerTemplate: IObjectExplorerTemplateData = Object.create(null); + objectExplorerTemplate.root = dom.append(container, dom.$('.object-element-group')); + objectExplorerTemplate.icon = dom.append(objectExplorerTemplate.root, dom.$('div.object-icon')); + objectExplorerTemplate.label = dom.append(objectExplorerTemplate.root, dom.$('div.label')); + return objectExplorerTemplate; + } + } + + /** + * Render a element, given an object bag returned by the template + */ + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + if (templateId === ServerTreeRenderer.CONNECTION_TEMPLATE_ID) { + this.renderConnection(tree, element, templateData); + } else if (templateId === ServerTreeRenderer.CONNECTION_GROUP_TEMPLATE_ID) { + this.renderConnectionProfileGroup(tree, element, templateData); + } else { + this.renderObjectExplorer(tree, element, templateData); + } + } + + private renderObjectExplorer(tree: ITree, treeNode: TreeNode, templateData: IObjectExplorerTemplateData): void { + var iconName = treeNode.nodeTypeId; + if (treeNode.nodeStatus) { + iconName = treeNode.nodeTypeId + '_' + treeNode.nodeStatus; + } + if (treeNode.nodeSubType) { + iconName = treeNode.nodeTypeId + '_' + treeNode.nodeSubType; + } + + let tokens: string[] = []; + for (var index = 1; index < templateData.icon.classList.length; index++) { + tokens.push(templateData.icon.classList.item(index)); + } + templateData.icon.classList.remove(...tokens); + templateData.icon.classList.add('icon'); + let iconLoweCaseName = iconName.toLocaleLowerCase(); + templateData.icon.classList.add(iconLoweCaseName); + + templateData.label.textContent = treeNode.label; + templateData.root.title = treeNode.label; + } + + + private renderConnection(tree: ITree, connection: ConnectionProfile, templateData: IConnectionTemplateData): void { + if (!this._isCompact) { + if (this._connectionManagementService.isConnected(undefined, connection)) { + templateData.icon.classList.remove('disconnected'); + templateData.icon.classList.add('connected'); + } else { + templateData.icon.classList.remove('connected'); + templateData.icon.classList.add('disconnected'); + } + } + + let databaseName = connection.databaseName ? connection.databaseName : ''; + let userName = connection.userName ? connection.userName : "Windows Authentication"; + let label = connection.serverName + ', ' + databaseName + ' (' + userName + ')'; + + templateData.label.textContent = label; + templateData.root.title = label; + templateData.connectionProfile = connection; + } + + private renderConnectionProfileGroup(tree: ITree, connectionProfileGroup: ConnectionProfileGroup, templateData: IConnectionProfileGroupTemplateData): void { + + var rowElement = this.findParentElement(templateData.root, 'monaco-tree-row'); + if (rowElement) { + if (connectionProfileGroup.color) { + rowElement.style.background = connectionProfileGroup.color; + } else { + // If the group doesn't contain specific color, assign the default color + rowElement.style.background = '#515151'; + } + } + if (connectionProfileGroup.description && (connectionProfileGroup.description !== '')) { + templateData.root.title = connectionProfileGroup.description; + } + templateData.name.hidden = false; + templateData.name.textContent = connectionProfileGroup.name; + } + + /** + * Returns the first parent which contains the className + */ + private findParentElement(container: HTMLElement, className: string): HTMLElement { + var currentElement = container; + while (currentElement) { + if (currentElement.className.includes(className)) { + break; + } + currentElement = currentElement.parentElement; + } + return currentElement; + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + // no op + // InputBox disposed in wrapUp + + } +} + diff --git a/src/sql/parts/registeredServer/viewlet/serverTreeView.ts b/src/sql/parts/registeredServer/viewlet/serverTreeView.ts new file mode 100644 index 0000000000..fb509c720a --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/serverTreeView.ts @@ -0,0 +1,387 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/serverTreeActions'; +import * as errors from 'vs/base/common/errors'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import * as ConnectionUtils from 'sql/parts/connection/common/utils'; +import { ActiveConnectionsFilterAction } from 'sql/parts/registeredServer/viewlet/connectionTreeAction'; +import { IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import * as builder from 'vs/base/browser/builder'; +import Severity from 'vs/base/common/severity'; +import { TreeCreationUtils } from 'sql/parts/registeredServer/viewlet/treeCreationUtils'; +import { TreeUpdateUtils } from 'sql/parts/registeredServer/viewlet/treeUpdateUtils'; +import { TreeSelectionHandler } from 'sql/parts/registeredServer/viewlet/treeSelectionHandler'; +import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import * as Utils from 'sql/parts/connection/common/utils'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; + +const $ = builder.$; + +/** + * ServerTreeview implements the dynamic tree view. + */ +export class ServerTreeView { + + public messages: builder.Builder; + private _buttonSection: builder.Builder; + private _treeSelectionHandler: TreeSelectionHandler; + private _activeConnectionsFilterAction: ActiveConnectionsFilterAction; + private _tree: ITree; + private _toDispose: IDisposable[] = []; + + constructor( + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IObjectExplorerService private _objectExplorerService: IObjectExplorerService, + @IThemeService private _themeService: IThemeService, + @IErrorMessageService private _errorMessageService: IErrorMessageService + ) { + this._activeConnectionsFilterAction = this._instantiationService.createInstance( + ActiveConnectionsFilterAction, + ActiveConnectionsFilterAction.ID, + ActiveConnectionsFilterAction.LABEL, + this); + this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); + } + + /** + * Get active connections filter action + */ + public get activeConnectionsFilterAction(): ActiveConnectionsFilterAction { + return this._activeConnectionsFilterAction; + } + /** + * Render the view body + */ + public renderBody(container: HTMLElement): void { + // Add div to display no connections found message and hide it by default + this.messages = $('div.title').appendTo(container); + $('span').style('padding-left', '10px').text('No connections found.').appendTo(this.messages); + this.messages.hide(); + + if (!this._connectionManagementService.hasRegisteredServers()) { + this._activeConnectionsFilterAction.enabled = false; + this._buttonSection = $('div.button-section').appendTo(container); + var connectButton = new Button(this._buttonSection); + connectButton.label = 'Add Connection'; + connectButton.addListener('click', () => { + this._connectionManagementService.showConnectionDialog(); + }); + } + + this._tree = TreeCreationUtils.createRegisteredServersTree(container, this._instantiationService); + this._toDispose.push(this._tree.addListener('selection', (event) => this.onSelected(event))); + + // Theme styler + this._toDispose.push(attachListStyler(this._tree, this._themeService)); + + const self = this; + // Refresh Tree when these events are emitted + this._toDispose.push(this._connectionManagementService.onAddConnectionProfile((newProfile: IConnectionProfile) => { + self.handleAddConnectionProfile(newProfile); + }) + ); + this._toDispose.push(this._connectionManagementService.onDeleteConnectionProfile(() => { + self.refreshTree(); + }) + ); + this._toDispose.push(this._connectionManagementService.onDisconnect((connectionParams) => { + if (self.isObjectExplorerConnectionUri(connectionParams.connectionUri)) { + self.deleteObjectExplorerNodeAndRefreshTree(connectionParams.connectionProfile); + } + })); + + if (this._objectExplorerService && this._objectExplorerService.onUpdateObjectExplorerNodes) { + this._toDispose.push(this._objectExplorerService.onUpdateObjectExplorerNodes(args => { + if (args.errorMessage) { + this.showError(args.errorMessage); + } + if (args.connection) { + self.onObjectExplorerSessionCreated(args.connection); + } + })); + } + self.refreshTree(); + } + + private isObjectExplorerConnectionUri(uri: string): boolean { + let isBackupRestoreUri: boolean = uri.indexOf(Utils.ConnectionUriBackupIdAttributeName) >= 0 || + uri.indexOf(Utils.ConnectionUriRestoreIdAttributeName) >= 0; + return uri && uri.startsWith(ConnectionUtils.uriPrefixes.default) && !isBackupRestoreUri; + } + + private handleAddConnectionProfile(newProfile: IConnectionProfile) { + if (newProfile) { + let groups = this._connectionManagementService.getConnectionGroups(); + let profile = ConnectionUtils.findProfileInGroup(newProfile, groups); + if (profile) { + newProfile = profile; + } + } + + if (this._buttonSection) { + this._buttonSection.getHTMLElement().style.display = 'none'; + this._activeConnectionsFilterAction.enabled = true; + } + let currentSelections = this._tree.getSelection(); + let currentSelectedElement = currentSelections && currentSelections.length >= 1 ? currentSelections[0] : undefined; + let newProfileIsSelected = currentSelectedElement && newProfile ? currentSelectedElement.id === newProfile.id : false; + if (newProfile && currentSelectedElement && !newProfileIsSelected) { + this._tree.clearSelection(); + } + this.refreshTree(); + if (newProfile && !newProfileIsSelected) { + this._tree.reveal(newProfile); + this._tree.select(newProfile); + } + } + + private showError(errorMessage: string) { + if (this._errorMessageService) { + this._errorMessageService.showDialog(Severity.Error, '', errorMessage); + } + } + + private getConnectionInTreeInput(connectionId: string): ConnectionProfile { + let root = TreeUpdateUtils.getTreeInput(this._connectionManagementService); + let connections = ConnectionProfileGroup.getConnectionsInGroup(root); + let results = connections.filter(con => { + if (connectionId === con.id) { + return true; + } else { + return false; + } + }); + if (results && results.length > 0) { + return results[0]; + } + return null; + } + + private onObjectExplorerSessionCreated(connection: IConnectionProfile) { + var conn = this.getConnectionInTreeInput(connection.id); + if (conn) { + this._tree.refresh(conn).then(() => { + return this._tree.expand(conn).then(() => { + return this._tree.reveal(conn, 0.5).then(() => { + this._treeSelectionHandler.onTreeActionStateChange(false); + }); + }); + }).done(null, errors.onUnexpectedError); + } + } + + public addObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): void { + this.messages.hide(); + if (!this._objectExplorerService.getObjectExplorerNode(connection)) { + this._objectExplorerService.updateObjectExplorerNodes(connection).then(() => { + // The oe request is sent. an event will be raised when the session is created + }, error => { + }); + } + } + + public deleteObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): void { + if (connection) { + var conn = this.getConnectionInTreeInput(connection.id); + if (conn) { + this._objectExplorerService.deleteObjectExplorerNode(conn); + this._tree.collapse(conn); + this._tree.refresh(conn); + } + } + } + + public refreshTree(): void { + this.messages.hide(); + this.clearOtherActions(); + TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService); + } + + /** + * Filter connections based on view (recent/active) + */ + private filterConnections(treeInput: ConnectionProfileGroup[], view: string): ConnectionProfileGroup[] { + if (!treeInput || treeInput.length === 0) { + return undefined; + } + let result = treeInput.map(group => { + // Keep active/recent connections and remove the rest + if (group.connections) { + group.connections = group.connections.filter(con => { + if (view === 'active') { + return this._connectionManagementService.isConnected(undefined, con); + } else if (view === 'recent') { + return this._connectionManagementService.isRecent(con); + } + return false; + }); + } + group.children = this.filterConnections(group.children, view); + // Remove subgroups that are undefined + if (group.children) { + group.children = group.children.filter(group => { + return (group) ? true : false; + }); + } + // Return a group only if it has a filtered result or subgroup. + if ((group.connections && group.connections.length > 0) || (group.children && group.children.length > 0)) { + return group; + } + return undefined; + }); + return result; + } + + /** + * Set tree elements based on the view (recent/active) + */ + public showFilteredTree(view: string): void { + + const self = this; + this.messages.hide(); + // Clear other action views if user switched between two views + this.clearOtherActions(view); + let root = TreeUpdateUtils.getTreeInput(this._connectionManagementService); + let treeInput: ConnectionProfileGroup = null; + if (root) { + // Filter results based on view + let filteredResults = this.filterConnections([root], view); + if (!filteredResults || !filteredResults[0]) { + this.messages.show(); + this.messages.domFocus(); + } else { + treeInput = filteredResults[0]; + } + this._tree.setInput(treeInput).done(() => { + if (this.messages.isHidden()) { + self._tree.getFocus(); + self._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput)); + } else { + self._tree.clearFocus(); + } + }, errors.onUnexpectedError); + } else { + //no op + } + } + + /** + * Searches and sets the tree input to the results + */ + public searchTree(searchString: string): void { + if (!searchString) { + return; + } + const self = this; + this.messages.hide(); + // Clear other actions if user searched during other views + this.clearOtherActions(); + // Filter connections based on search + let filteredResults = this.searchConnections(searchString); + if (!filteredResults || filteredResults.length === 0) { + this.messages.show(); + this.messages.domFocus(); + } + // Add all connections to tree root and set tree input + let treeInput = new ConnectionProfileGroup('searchroot', undefined, 'searchroot', undefined, undefined); + treeInput.addConnections(filteredResults); + this._tree.setInput(treeInput).done(() => { + if (this.messages.isHidden()) { + self._tree.getFocus(); + self._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput)); + } else { + self._tree.clearFocus(); + } + }, errors.onUnexpectedError); + } + + /** + * Searches through all the connections and returns a list of matching connections + */ + private searchConnections(searchString: string): ConnectionProfile[] { + + let root = TreeUpdateUtils.getTreeInput(this._connectionManagementService); + let connections = ConnectionProfileGroup.getConnectionsInGroup(root); + let results = connections.filter(con => { + if (searchString && (searchString.length > 0)) { + return this.isMatch(con, searchString); + } else { + return false; + } + }); + return results; + } + + /** + * Returns true if the connection matches the search string. + * For now, the search criteria is true if the + * server name or database name contains the search string (ignores case). + */ + private isMatch(connection: ConnectionProfile, searchString: string): boolean { + searchString = searchString.trim().toLocaleUpperCase(); + if (this.checkIncludes(searchString, connection.databaseName) || this.checkIncludes(searchString, connection.serverName)) { + return true; + } + return false; + } + + private checkIncludes(searchString: string, candidate: string): boolean { + if (candidate && searchString) { + return candidate.toLocaleUpperCase().includes(searchString); + } + return false; + } + + /** + * Clears the toggle icons for active and recent + */ + private clearOtherActions(view?: string) { + if (!view) { + this._activeConnectionsFilterAction.isSet = false; + } + if (view === 'recent') { + this._activeConnectionsFilterAction.isSet = false; + } + } + + private onSelected(event: any): void { + this._treeSelectionHandler.onTreeSelect(event, this._tree, this._connectionManagementService, this._objectExplorerService); + } + + /** + * set the layout of the view + */ + public layout(height: number): void { + this._tree.layout(height); + } + + /** + * set the visibility of the view + */ + public setVisible(visible: boolean): void { + if (visible) { + this._tree.onVisible(); + } else { + this._tree.onHidden(); + } + } + + /** + * dispose the server tree view + */ + public dispose(): void { + this._tree.dispose(); + this._toDispose = dispose(this._toDispose); + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/templateData.ts b/src/sql/parts/registeredServer/viewlet/templateData.ts new file mode 100644 index 0000000000..e0d29b5521 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/templateData.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; + +export interface IConnectionTemplateData { + root: HTMLElement; + label: HTMLSpanElement; + icon: HTMLElement; + connectionProfile: ConnectionProfile; +} + +export interface IConnectionProfileGroupTemplateData { + root: HTMLElement; + name: HTMLSpanElement; + inputBox: InputBox; +} + +export interface IObjectExplorerTemplateData { + root: HTMLElement; + label: HTMLSpanElement; + icon: HTMLElement; + treeNode: TreeNode; +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/treeCreationUtils.ts b/src/sql/parts/registeredServer/viewlet/treeCreationUtils.ts new file mode 100644 index 0000000000..b87715855b --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/treeCreationUtils.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/serverTreeActions'; +import nls = require('vs/nls'); +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServerTreeRenderer } from 'sql/parts/registeredServer/viewlet/serverTreeRenderer'; +import { ServerTreeDataSource } from 'sql/parts/registeredServer/viewlet/serverTreeDataSource'; +import { ServerTreeController } from 'sql/parts/registeredServer/viewlet/serverTreeController'; +import { ServerTreeActionProvider } from 'sql/parts/registeredServer/viewlet/serverTreeActionProvider'; +import { DefaultFilter, DefaultAccessibilityProvider, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults'; +import { IController } from 'vs/base/parts/tree/browser/tree'; +import { ServerTreeDragAndDrop, RecentConnectionsDragAndDrop } from 'sql/parts/registeredServer/viewlet/dragAndDropController'; +import { RecentConnectionDataSource } from 'sql/parts/registeredServer/viewlet/recentConnectionDataSource'; + +export class TreeCreationUtils { + /** + * Create a Recent Connections tree + */ + public static createConnectionTree(treeContainer: HTMLElement, instantiationService: IInstantiationService, useController?: IController): Tree { + const dataSource = instantiationService.createInstance(RecentConnectionDataSource); + const renderer = instantiationService.createInstance(ServerTreeRenderer, true); + const controller = useController ? useController : new DefaultController(); + const dnd = instantiationService.createInstance(RecentConnectionsDragAndDrop); + const filter = new DefaultFilter(); + const sorter = undefined; + const accessibilityProvider = new DefaultAccessibilityProvider(); + + return new Tree(treeContainer, { dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider }, + { + indentPixels: 10, + twistiePixels: 20, + ariaLabel: nls.localize('treeAriaLabel', "Recent Connections") + }); + } + + /** + * Create a Servers viewlet tree + */ + public static createRegisteredServersTree(treeContainer: HTMLElement, instantiationService: IInstantiationService): Tree { + + const dataSource = instantiationService.createInstance(ServerTreeDataSource); + const actionProvider = instantiationService.createInstance(ServerTreeActionProvider); + const renderer = instantiationService.createInstance(ServerTreeRenderer, false); + const controller = instantiationService.createInstance(ServerTreeController, actionProvider); + const dnd = instantiationService.createInstance(ServerTreeDragAndDrop); + const filter = new DefaultFilter(); + const sorter = undefined; + const accessibilityProvider = new DefaultAccessibilityProvider(); + + return new Tree(treeContainer, { dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider }, + { + indentPixels: 10, + twistiePixels: 20, + ariaLabel: nls.localize('regTreeAriaLabel', "Servers") + }); + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/treeSelectionHandler.ts b/src/sql/parts/registeredServer/viewlet/treeSelectionHandler.ts new file mode 100644 index 0000000000..b05d1863ea --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/treeSelectionHandler.ts @@ -0,0 +1,115 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConnectionManagementService, IConnectionCompletionOptions } from 'sql/parts/connection/common/connectionManagement'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; + +import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; +import { TreeUpdateUtils } from 'sql/parts/registeredServer/viewlet/treeUpdateUtils'; + +export class TreeSelectionHandler { + progressRunner: IProgressRunner; + + private _clicks: number = 0; + private _doubleClickTimeoutId: number = -1; + + constructor( @IProgressService private _progressService: IProgressService) { + + } + + public onTreeActionStateChange(started: boolean): void { + if (this.progressRunner) { + this.progressRunner.done(); + } + + if (started) { + this.progressRunner = this._progressService.show(true); + } else { + this.progressRunner = null; + } + } + + private isMouseEvent(event: any): boolean { + return event && event.payload && event.payload.origin === 'mouse'; + } + + /** + * Handle selection of tree element + */ + public onTreeSelect(event: any, tree: ITree, connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService) { + if (this.isMouseEvent(event)) { + this._clicks++; + } + + // clear pending click timeouts to avoid sending multiple events on double-click + if (this._doubleClickTimeoutId !== -1) { + clearTimeout(this._doubleClickTimeoutId); + } + + let isKeyboard = event && event.payload && event.payload.origin === 'keyboard'; + + // grab the current selection for use later + let selection = tree.getSelection(); + + this._doubleClickTimeoutId = setTimeout(() => { + // don't send tree update events while dragging + if (!TreeUpdateUtils.isInDragAndDrop) { + let isDoubleClick = this._clicks > 1; + this.handleTreeItemSelected(connectionManagementService, objectExplorerService, isDoubleClick, isKeyboard, selection, tree); + } + this._clicks = 0; + this._doubleClickTimeoutId = -1; + }, 300); + } + + /** + * + * @param connectionManagementService + * @param objectExplorerService + * @param isDoubleClick + * @param isKeyboard + * @param selection + */ + private handleTreeItemSelected(connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, isDoubleClick: boolean, isKeyboard: boolean, selection: any[], tree: ITree): void { + let connectionProfile: ConnectionProfile = undefined; + let options: IConnectionCompletionOptions = { + params: undefined, + saveTheConnection: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: true, + showDashboard: isDoubleClick // only show the dashboard if the action is double click + }; + if (selection && selection.length > 0 && (selection[0] instanceof ConnectionProfile)) { + connectionProfile = selection[0]; + + if (connectionProfile) { + this.onTreeActionStateChange(true); + + TreeUpdateUtils.connectAndCreateOeSession(connectionProfile, options, connectionManagementService, objectExplorerService, tree).then(sessionCreated => { + if (!sessionCreated) { + this.onTreeActionStateChange(false); + } + }, error => { + this.onTreeActionStateChange(false); + }); + } + } else if (isDoubleClick && selection && selection.length > 0 && (selection[0] instanceof TreeNode)) { + let treeNode = selection[0]; + if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) { + connectionProfile = TreeUpdateUtils.getConnectionProfile(treeNode); + if (connectionProfile) { + connectionManagementService.showDashboard(connectionProfile); + } + } + } + + if (isKeyboard) { + tree.toggleExpansion(selection[0]); + } + } +} \ No newline at end of file diff --git a/src/sql/parts/registeredServer/viewlet/treeUpdateUtils.ts b/src/sql/parts/registeredServer/viewlet/treeUpdateUtils.ts new file mode 100644 index 0000000000..7942d80813 --- /dev/null +++ b/src/sql/parts/registeredServer/viewlet/treeUpdateUtils.ts @@ -0,0 +1,274 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { IConnectionManagementService, IConnectionCompletionOptions, IConnectionCallbacks } from 'sql/parts/connection/common/connectionManagement'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; +import { NodeType } from 'sql/parts/registeredServer/common/nodeType'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; +import errors = require('vs/base/common/errors'); + +export class TreeUpdateUtils { + + public static isInDragAndDrop: boolean = false; + + /** + * Set input for the tree. + */ + public static structuralTreeUpdate(tree: ITree, viewKey: string, connectionManagementService: IConnectionManagementService): void { + let selectedElement: any; + let targetsToExpand: any[]; + if (tree) { + let selection = tree.getSelection(); + if (selection && selection.length === 1) { + selectedElement = selection[0]; + } + targetsToExpand = tree.getExpandedElements(); + } + let groups; + if (viewKey === 'recent') { + groups = connectionManagementService.getRecentConnections(); + } else if (viewKey === 'active') { + groups = connectionManagementService.getActiveConnections(); + } + const treeInput = new ConnectionProfileGroup('root', null, undefined, undefined, undefined); + treeInput.addConnections(groups); + tree.setInput(treeInput).done(() => { + // Make sure to expand all folders that where expanded in the previous session + if (targetsToExpand) { + tree.expandAll(targetsToExpand); + } + if (selectedElement) { + tree.select(selectedElement); + } + tree.getFocus(); + }); + } + + /** + * Set input for the registered servers tree. + */ + public static registeredServerUpdate(tree: ITree, connectionManagementService: IConnectionManagementService, elementToSelect?: any): void { + let selectedElement: any = elementToSelect; + let targetsToExpand: any[]; + + // Focus + tree.DOMFocus(); + + if (tree) { + let selection = tree.getSelection(); + if (!selectedElement) { + if (selection && selection.length === 1) { + selectedElement = selection[0]; + } + } + targetsToExpand = tree.getExpandedElements(); + if (selectedElement && targetsToExpand.indexOf(selectedElement) === -1) { + targetsToExpand.push(selectedElement); + } + } + + let treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService); + if (treeInput) { + if (treeInput !== tree.getInput()) { + tree.setInput(treeInput).then(() => { + // Make sure to expand all folders that where expanded in the previous session + if (targetsToExpand) { + tree.expandAll(targetsToExpand); + } + if (selectedElement) { + tree.select(selectedElement); + } + tree.getFocus(); + }, errors.onUnexpectedError); + } + } + } + + public static getTreeInput(connectionManagementService: IConnectionManagementService): ConnectionProfileGroup { + + let groups = connectionManagementService.getConnectionGroups(); + if (groups && groups.length > 0) { + let treeInput = groups[0]; + treeInput.name = 'root'; + return treeInput; + } + // Should never get to this case. + return undefined; + } + + public static hasObjectExplorerNode(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService): boolean { + let isConnected = connectionManagementService.isConnected(undefined, connection); + return isConnected; + } + + public static connectIfNotConnected( + connection: ConnectionProfile, + options: IConnectionCompletionOptions, + connectionManagementService: IConnectionManagementService, + tree: ITree): TPromise { + return new TPromise((resolve, reject) => { + if (!connectionManagementService.isProfileConnected(connection)) { + // don't try to reconnect if currently connecting + if (connectionManagementService.isProfileConnecting(connection)) { + resolve(undefined); + + // else if we aren't connected or connecting then try to connect + } else { + let callbacks: IConnectionCallbacks = undefined; + if (tree) { + // Show the spinner in OE by adding the 'loading' trait to the connection, and set up callbacks to hide the spinner + tree.addTraits('loading', [connection]); + callbacks = { + onConnectStart: undefined, + onConnectReject: () => { + tree.collapse(connection); + tree.removeTraits('loading', [connection]); + }, + onConnectSuccess: () => tree.removeTraits('loading', [connection]), + onDisconnect: undefined + }; + } + connectionManagementService.connect(connection, undefined, options, callbacks).then(result => { + if (result.connected) { + let existingConnection = connectionManagementService.findExistingConnection(connection); + resolve(existingConnection); + } else { + reject('connection failed'); + } + }, connectionError => { + reject(connectionError); + }); + } + } else { + let existingConnection = connectionManagementService.findExistingConnection(connection); + if (options && options.showDashboard) { + connectionManagementService.showDashboard(connection).then((value) => { + resolve(existingConnection); + }); + } else { + resolve(existingConnection); + } + } + }); + } + + /** + * Makes a connection if the not already connected and try to create new object explorer session + * I the profile is already connected, tries to do the action requested in the options (e.g. open dashboard) + * Returns true if new object explorer session created for the connection, otherwise returns false + * @param connection Connection Profile + * @param options Includes the actions to happened after connection is made + * @param connectionManagementService Connection management service instance + * @param objectExplorerService Object explorer service instance + */ + public static connectAndCreateOeSession(connection: ConnectionProfile, options: IConnectionCompletionOptions, + connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, tree: ITree): TPromise { + return new TPromise((resolve, reject) => { + TreeUpdateUtils.connectIfNotConnected(connection, options, connectionManagementService, tree).then(connectedConnection => { + if (connectedConnection) { + // append group ID and original display name to build unique OE session ID + connectedConnection.options['groupId'] = connection.groupId; + connectedConnection.options['databaseDisplayName'] = connection.databaseName; + + var rootNode: TreeNode = objectExplorerService.getObjectExplorerNode(connectedConnection); + if (!rootNode) { + objectExplorerService.updateObjectExplorerNodes(connectedConnection).then(() => { + rootNode = objectExplorerService.getObjectExplorerNode(connectedConnection); + resolve(true); + // The oe request is sent. an event will be raised when the session is created + }, error => { + reject('session failed'); + }); + } else { + resolve(false); + } + } else { + resolve(false); + } + }, connectionError => { + reject(connectionError); + }); + }); + } + + public static getObjectExplorerNode(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService): TPromise { + return new TPromise((resolve, reject) => { + if (connection.isDisconnecting) { + resolve([]); + } else { + var rootNode = objectExplorerService.getObjectExplorerNode(connection); + if (rootNode) { + objectExplorerService.expandTreeNode(rootNode.getSession(), rootNode).then(() => { + resolve(rootNode.children); + }, expandError => { + resolve([]); + }); + + } else { + resolve([]); + } + } + }); + } + + public static getObjectExplorerParent(objectExplorerNode: TreeNode, connectionManagementService: IConnectionManagementService): any { + if (objectExplorerNode && objectExplorerNode.parent) { + // if object explorer node's parent is root, return connection profile + if (!objectExplorerNode.parent.parent) { + var connectionId = objectExplorerNode.getConnectionProfile().id; + + // get connection profile from connection profile groups + let root = TreeUpdateUtils.getTreeInput(connectionManagementService); + let connections = ConnectionProfileGroup.getConnectionsInGroup(root); + let results = connections.filter(con => { + if (connectionId === con.id) { + return true; + } else { + return false; + } + }); + if (results && results.length > 0) { + return results[0]; + } + } else { + return objectExplorerNode.parent; + } + } + return null; + } + + /** + * + * @param treeNode Returns true if the tree node is a database node + */ + public static isDatabaseNode(treeNode: TreeNode): boolean { + return treeNode && treeNode.nodeTypeId === NodeType.Database; + } + + /** + * + * @param treeNode Returns true if the tree node is an available database node + */ + public static isAvailableDatabaseNode(treeNode: TreeNode): boolean { + return treeNode && treeNode.nodeTypeId === NodeType.Database && treeNode.nodeStatus !== 'Unavailable'; + } + + /** + * Get connection profile with the current database + */ + public static getConnectionProfile(treeNode: TreeNode): ConnectionProfile { + var connectionProfile = treeNode.getConnectionProfile(); + var databaseName = treeNode.getDatabaseName(); + if (databaseName !== undefined && connectionProfile.databaseName !== databaseName) { + connectionProfile = connectionProfile.cloneWithDatabase(databaseName); + } + return connectionProfile; + } +} \ No newline at end of file diff --git a/src/sql/parts/taskHistory/common/taskHistory.contribution.ts b/src/sql/parts/taskHistory/common/taskHistory.contribution.ts new file mode 100644 index 0000000000..a12cce41d6 --- /dev/null +++ b/src/sql/parts/taskHistory/common/taskHistory.contribution.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import 'vs/css!sql/media/actionBarLabel'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { localize } from 'vs/nls'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ToggleViewletAction } from 'vs/workbench/browser/viewlet'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IViewlet } from 'vs/workbench/common/viewlet'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { VIEWLET_ID } from 'sql/parts/taskHistory/viewlet/taskHistoryViewlet'; +import lifecycle = require('vs/base/common/lifecycle'); +import ext = require('vs/workbench/common/contributions'); +import { ITaskService } from 'sql/parts/taskHistory/common/taskService'; +import { IActivityBarService, NumberBadge } from 'vs/workbench/services/activity/common/activityBarService'; + +export class StatusUpdater implements ext.IWorkbenchContribution { + static ID = 'data.taskhistory.statusUpdater'; + + private badgeHandle: lifecycle.IDisposable; + private toDispose: lifecycle.IDisposable[]; + + constructor( + @IActivityBarService private activityBarService: IActivityBarService, + @ITaskService private _taskService: ITaskService, + @IViewletService private _viewletService: IViewletService + ) { + this.toDispose = []; + + this.toDispose.push(this._taskService.onAddNewTask(args => { + this.showTasksViewlet(); + this.onServiceChange(); + })); + + this.toDispose.push(this._taskService.onTaskComplete(task => { + this.onServiceChange(); + })); + + } + + private showTasksViewlet(): void { + let activeViewlet: IViewlet = this._viewletService.getActiveViewlet(); + if (!activeViewlet || activeViewlet.getId() !== VIEWLET_ID) { + this._viewletService.openViewlet(VIEWLET_ID, true); + } + } + + private onServiceChange(): void { + lifecycle.dispose(this.badgeHandle); + let numOfInProgressTask: number = this._taskService.getNumberOfInProgressTasks(); + let badge: NumberBadge = new NumberBadge(numOfInProgressTask, n => localize('inProgressTasksChangesBadge', "{0} in progress tasks", n)); + this.badgeHandle = this.activityBarService.showActivity(VIEWLET_ID, badge, 'taskhistory-viewlet-label'); + } + + public getId(): string { + return StatusUpdater.ID; + } + + public dispose(): void { + this.toDispose = lifecycle.dispose(this.toDispose); + lifecycle.dispose(this.badgeHandle); + } +} + + +// Viewlet Action +export class TaskHistoryViewletAction extends ToggleViewletAction { + public static ID = VIEWLET_ID; + public static LABEL = nls.localize({ key: 'showTaskHistory', comment: ['Show Task History'] }, 'Show Task History'); + + constructor( + id: string, + label: string, + @IViewletService viewletService: IViewletService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService + ) { + super(id, label, VIEWLET_ID, viewletService, editorService); + } +} + +// Viewlet +const viewletDescriptor = new ViewletDescriptor( + 'sql/parts/taskHistory/viewlet/taskHistoryViewlet', + 'TaskHistoryViewlet', + VIEWLET_ID, + 'Task History', + 'taskHistoryViewlet', + -90 +); + +Registry.as(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor); + +// Register StatusUpdater +(Registry.as(ext.Extensions.Workbench)).registerWorkbenchContribution(StatusUpdater); + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction( + new SyncActionDescriptor( + TaskHistoryViewletAction, + TaskHistoryViewletAction.ID, + TaskHistoryViewletAction.LABEL, + { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), + 'View: Show Task Histry', + localize('view', "View") +); + +let configurationRegistry = Registry.as(Extensions.Configuration); +configurationRegistry.registerConfiguration({ + 'id': 'taskHistory', + 'title': localize('taskHistory', 'Task History'), + 'type': 'object', + 'properties': { + 'datasource.task': { + 'description': localize('datasource.task', 'Operation Task Status'), + 'type': 'array' + } + } +}); \ No newline at end of file diff --git a/src/sql/parts/taskHistory/common/taskNode.ts b/src/sql/parts/taskHistory/common/taskNode.ts new file mode 100644 index 0000000000..b14c7d851f --- /dev/null +++ b/src/sql/parts/taskHistory/common/taskNode.ts @@ -0,0 +1,115 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +'use strict'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { generateUuid } from 'vs/base/common/uuid'; + +export enum TaskStatus { + notStarted = 0, + inProgress = 1, + succeeded = 2, + succeededWithWarning = 3, + failed = 4, + canceled = 5, + canceling = 6 +} + +export enum TaskExecutionMode { + execute = 0, + script = 1, + executeAndScript = 2, +} + +export class TaskNode { + /** + * id for TaskNode + */ + public id: string; + + /** + * string defining the type of the task - for example Backup, Restore + */ + public taskName: string; + + /** + * sever name + */ + public serverName: string; + + /** + * Database Name + */ + public databaseName: string; + + /** + * Provider Name + */ + public providerName: string; + + + /** + * The start time of the task + */ + public startTime: string; + + /** + * The end time of the task + */ + public endTime: string; + + /** + * The timer for the task + */ + public timer: StopWatch; + + /** + * Does this node have children + */ + public hasChildren: boolean; + + /** + * Children of this node + */ + public children: TaskNode[]; + + /** + * Task's message + */ + public message: string; + + /** + * Status of the task + */ + public status: TaskStatus; + + /** + * Execution mode of task + */ + public taskExecutionMode: TaskExecutionMode; + + /** + * Indicates if the task can be canceled + */ + public isCancelable: boolean; + + /** + * Script of task operation + */ + public script: string; + + constructor(taskName: string, serverName: string, databaseName: string, taskId: string = undefined, taskExecutionMode: TaskExecutionMode = TaskExecutionMode.execute, isCancelable: boolean = true) { + this.id = taskId || generateUuid(); + + this.taskName = taskName; + this.serverName = serverName; + this.databaseName = databaseName; + this.timer = StopWatch.create(); + this.startTime = new Date().toLocaleTimeString(); + this.status = TaskStatus.inProgress; + this.hasChildren = false; + this.taskExecutionMode = taskExecutionMode; + this.isCancelable = isCancelable; + } +} diff --git a/src/sql/parts/taskHistory/common/taskService.ts b/src/sql/parts/taskHistory/common/taskService.ts new file mode 100644 index 0000000000..c2567218be --- /dev/null +++ b/src/sql/parts/taskHistory/common/taskService.ts @@ -0,0 +1,226 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as data from 'data'; +import { TaskNode, TaskStatus, TaskExecutionMode } from 'sql/parts/taskHistory/common/taskNode'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import Event, { Emitter } from 'vs/base/common/event'; +export const SERVICE_ID = 'taskHistoryService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IChoiceService } from 'vs/platform/message/common/message'; +import { localize } from 'vs/nls'; +import Severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; + +export const ITaskService = createDecorator(SERVICE_ID); + +export interface ITaskService { + _serviceBrand: any; + onTaskComplete: Event; + onAddNewTask: Event; + handleNewTask(task: TaskNode): void; + handleTaskComplete(eventArgs: TaskStatusChangeArgs): void; + getAllTasks(): TaskNode; + getNumberOfInProgressTasks(): number; + onNewTaskCreated(handle: number, taskInfo: data.TaskInfo); + onTaskStatusChanged(handle: number, taskProgressInfo: data.TaskProgressInfo); + cancelTask(providerId: string, taskId: string): Thenable; + /** + * Register a ObjectExplorer provider + */ + registerProvider(providerId: string, provider: data.TaskServicesProvider): void; +} + +export interface TaskStatusChangeArgs { + taskId: string; + status: data.TaskStatus; + message?: string; + script?: string; +} + +export class TaskService implements ITaskService { + public _serviceBrand: any; + private _taskQueue: TaskNode; + private _onTaskComplete = new Emitter(); + private _onAddNewTask = new Emitter(); + private _providers: { [handle: string]: data.TaskServicesProvider; } = Object.create(null); + + constructor( + @ILifecycleService lifecycleService: ILifecycleService, + @IChoiceService private choiceService: IChoiceService, + @IQueryEditorService private queryEditorService: IQueryEditorService + ) { + this._taskQueue = new TaskNode('Root', undefined, undefined); + this._onTaskComplete = new Emitter(); + this._onAddNewTask = new Emitter(); + + lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown())); + + } + + /** + * Register a ObjectExplorer provider + */ + public registerProvider(providerId: string, provider: data.TaskServicesProvider): void { + this._providers[providerId] = provider; + } + + public onNewTaskCreated(handle: number, taskInfo: data.TaskInfo) { + let node: TaskNode = new TaskNode(taskInfo.name, taskInfo.serverName, taskInfo.databaseName, taskInfo.taskId, taskInfo.taskExecutionMode, taskInfo.isCancelable); + node.providerName = taskInfo.providerName; + this.handleNewTask(node); + } + + public onTaskStatusChanged(handle: number, taskProgressInfo: data.TaskProgressInfo) { + this.handleTaskComplete({ + taskId: taskProgressInfo.taskId, + status: taskProgressInfo.status, + message: taskProgressInfo.message, + script: taskProgressInfo.script + }); + } + + public cancelTask(providerId: string, taskId: string): Thenable { + let task = this.getTaskInQueue(taskId); + task.status = TaskStatus.canceling; + this._onTaskComplete.fire(task); + let provider = this._providers[providerId]; + if (provider) { + return provider.cancelTask({ + taskId: taskId + }); + } + return Promise.resolve(undefined); + } + + private cancelAllTasks(): Thenable { + return new TPromise((resolve, reject) => { + let promises = this._taskQueue.children.map(task => { + if (task.status === TaskStatus.inProgress || task.status === TaskStatus.notStarted) { + return this.cancelTask(task.providerName, task.id); + } + return Promise.resolve(true); + }); + + Promise.all(promises).then(result => { + resolve(undefined); + }).catch(error => { + reject(error); + }); + }); + } + + public handleNewTask(task: TaskNode): void { + if (this._taskQueue.hasChildren) { + this._taskQueue.children.unshift(task); + } else { + this._taskQueue.hasChildren = true; + this._taskQueue.children = [task]; + } + this._onAddNewTask.fire(task); + } + + public beforeShutdown(): TPromise { + const message = localize('InProgressWarning', '1 or more tasks are in progress. Are you sure you want to quit?'); + const options = [ + localize('yes', "Yes"), + localize('no', "No") + ]; + + return new TPromise((resolve, reject) => { + let numOfInprogressTasks = this.getNumberOfInProgressTasks(); + if (numOfInprogressTasks > 0) { + this.choiceService.choose(Severity.Warning, message, options, 0, false).done(choice => { + switch (choice) { + case 0: + let timeoutId: number; + let isTimeout = false; + this.cancelAllTasks().then(() => { + clearTimeout(timeoutId); + if (!isTimeout) { + resolve(false); + } + }, error => { + clearTimeout(timeoutId); + if (!isTimeout) { + resolve(false); + }; + }); + timeoutId = setTimeout(function () { + isTimeout = true; + resolve(false); + }, 2000); + break; + case 1: + resolve(true); + } + }); + } else { + resolve(false); + } + }); + } + + public handleTaskComplete(eventArgs: TaskStatusChangeArgs): void { + var task = this.getTaskInQueue(eventArgs.taskId); + if (task) { + task.status = eventArgs.status; + if (eventArgs.message) { + task.message = eventArgs.message; + } + switch (task.status) { + case TaskStatus.canceled: + case TaskStatus.succeeded: + case TaskStatus.succeededWithWarning: + case TaskStatus.failed: + task.endTime = new Date().toLocaleTimeString(); + task.timer.stop(); + this._onTaskComplete.fire(task); + break; + default: + break; + } + + if ((task.status === TaskStatus.succeeded || task.status === TaskStatus.succeededWithWarning) + && eventArgs.script && eventArgs.script !== '') { + if (task.taskExecutionMode === TaskExecutionMode.script) { + this.queryEditorService.newSqlEditor(eventArgs.script); + } else if (task.taskExecutionMode === TaskExecutionMode.executeAndScript) { + task.script = eventArgs.script; + } + } + } + + } + + private getTaskInQueue(taskId: string): TaskNode { + if (this._taskQueue.hasChildren) { + return this._taskQueue.children.find(x => x.id === taskId); + } + return undefined; + } + + public get onTaskComplete(): Event { + return this._onTaskComplete.event; + } + + public get onAddNewTask(): Event { + return this._onAddNewTask.event; + } + + public getNumberOfInProgressTasks(): number { + if (this._taskQueue.hasChildren) { + var inProgressTasks = this._taskQueue.children.filter(x => x.status === TaskStatus.inProgress); + return inProgressTasks ? inProgressTasks.length : 0; + } + return 0; + } + + public getAllTasks(): TaskNode { + return this._taskQueue; + } +} \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/media/status_queuedtask.svg b/src/sql/parts/taskHistory/viewlet/media/status_queuedtask.svg new file mode 100644 index 0000000000..2ce73a7646 --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/media/status_queuedtask.svg @@ -0,0 +1 @@ +queuedtask_16x16 \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/media/status_queuedtask_inverse.svg b/src/sql/parts/taskHistory/viewlet/media/status_queuedtask_inverse.svg new file mode 100644 index 0000000000..01b22c60fb --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/media/status_queuedtask_inverse.svg @@ -0,0 +1 @@ +queuedtask_inverse_16x16 \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/media/taskHistoryViewlet.css b/src/sql/parts/taskHistory/viewlet/media/taskHistoryViewlet.css new file mode 100644 index 0000000000..cce4819631 --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/media/taskHistoryViewlet.css @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.empty-task-message { + padding: 10px 22px 0 22px; + opacity: 0.5; +} + +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group { + padding: 5px; + overflow: hidden; +} + +/* task title and description */ +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-details > .title { + text-overflow: ellipsis; + overflow: hidden; + font-weight: 700; +} + +/* style for server name | database name */ +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-details > .description { + text-overflow: ellipsis; + overflow: hidden; + padding-left: 25px +} + +/* style for timing */ +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-details > .time { + padding-left: 25px; + opacity: .6; + font-size: 80%; + text-overflow: ellipsis; + overflow: hidden; +} + +/* task icon status */ +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon { + float: left; + height: 16px; + width: 16px; + padding-right: 10px; +} + +.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.not-started { + content: url('status_queuedtask.svg'); +} + +.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.not-started, +.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .task-group > .task-icon.not-started { + content: url('status_queuedtask_inverse.svg'); +} \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/taskAction.ts b/src/sql/parts/taskHistory/viewlet/taskAction.ts new file mode 100644 index 0000000000..5de3d36e89 --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/taskAction.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { ITaskService } from 'sql/parts/taskHistory/common/taskService'; +import { TaskNode } from 'sql/parts/taskHistory/common/taskNode'; +import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import Severity from 'vs/base/common/severity'; + +export class CancelAction extends Action { + public static ID = 'taskHistory.cancel'; + public static LABEL = localize('cancel', 'Cancel'); + + constructor( + id: string, + label: string, + @ITaskService private _taskService: ITaskService, + @IErrorMessageService private _errorMessageService: IErrorMessageService + ) { + super(id, label); + } + public run(element: TaskNode): TPromise { + if (element instanceof TaskNode) { + this._taskService.cancelTask(element.providerName, element.id).then((result) => { + if (!result) { + let error = localize('errorMsgFromCancelTask', 'The task is failed to cancel.'); + this.showError(error); + } + }, error => { + this.showError(error); + return TPromise.as(true); + }); + } + return TPromise.as(true); + } + + private showError(errorMessage: string) { + if (this._errorMessageService) { + this._errorMessageService.showDialog(Severity.Error, '', errorMessage); + } + } +} + +export class ScriptAction extends Action { + public static ID = 'taskHistory.script'; + public static LABEL = localize('script', 'Script'); + + constructor( + id: string, + label: string, + @IQueryEditorService private _queryEditorService: IQueryEditorService + ) { + super(id, label); + } + + public run(element: TaskNode): TPromise { + if (element instanceof TaskNode) { + if (element.script && element.script !== '') { + this._queryEditorService.newSqlEditor(element.script); + } + } + return TPromise.as(true); + } +} \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts b/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts new file mode 100644 index 0000000000..cf25409142 --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { ContributableActionProvider } from 'vs/workbench/browser/actions'; +import { IAction } from 'vs/base/common/actions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TaskNode, TaskStatus, TaskExecutionMode } from 'sql/parts/taskHistory/common/taskNode'; +import { CancelAction, ScriptAction } from 'sql/parts/taskHistory/viewlet/taskAction'; + +/** + * Provides actions for the history tasks + */ +export class TaskHistoryActionProvider extends ContributableActionProvider { + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(); + } + + public hasActions(tree: ITree, element: any): boolean { + return element instanceof TaskNode; + } + + /** + * Return actions given an element in the tree + */ + public getActions(tree: ITree, element: any): TPromise { + if (element instanceof TaskNode) { + return TPromise.as(this.getTaskHistoryActions(tree, element)); + } + return TPromise.as([]); + } + + public hasSecondaryActions(tree: ITree, element: any): boolean { + return false; + } + + public getSecondaryActions(tree: ITree, element: any): TPromise { + return super.getSecondaryActions(tree, element); + } + + /** + * Return actions for history task + */ + public getTaskHistoryActions(tree: ITree, element: TaskNode): IAction[] { + var actions = []; + + // get actions for tasks in progress + if (element.status === TaskStatus.inProgress && element.isCancelable) { + actions.push(this._instantiationService.createInstance(CancelAction, CancelAction.ID, CancelAction.LABEL)); + } + + // get actions for tasks succeeded + if (element.status === TaskStatus.succeeded || element.status === TaskStatus.succeededWithWarning) { + if (element.taskExecutionMode === TaskExecutionMode.executeAndScript) { + actions.push(this._instantiationService.createInstance(ScriptAction, ScriptAction.ID, ScriptAction.LABEL)); + } + } + + return actions; + } +} \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/taskHistoryController.ts b/src/sql/parts/taskHistory/viewlet/taskHistoryController.ts new file mode 100644 index 0000000000..57a7f2afb9 --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/taskHistoryController.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { ITree, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree'; +import treedefaults = require('vs/base/parts/tree/browser/treeDefaults'); +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { TaskHistoryActionProvider } from 'sql/parts/taskHistory/viewlet/taskHistoryActionProvider'; + +/** + * Extends the tree controller to handle clicks on the tree elements + */ +export class TaskHistoryController extends treedefaults.DefaultController { + + constructor(private actionProvider: TaskHistoryActionProvider, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IContextMenuService private contextMenuService: IContextMenuService, + @ITelemetryService private telemetryService: ITelemetryService, + @IKeybindingService private keybindingService: IKeybindingService + ) { + super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN }); + } + + public onClick(tree: ITree, element: any, event: IMouseEvent): boolean { + return super.onClick(tree, element, event); + } + + protected onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean { + return super.onLeftClick(tree, element, event, origin); + } + + // Do not allow left / right to expand and collapse groups #7848 + protected onLeft(tree: ITree, event: IKeyboardEvent): boolean { + return true; + } + + protected onRight(tree: ITree, event: IKeyboardEvent): boolean { + return true; + } + + protected onEnter(tree: ITree, event: IKeyboardEvent): boolean { + return super.onEnter(tree, event); + } + + /** + * Return actions in the context menu + */ + public onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean { + if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { + return false; + } + // Check if clicked on some element + if (element === tree.getInput()) { + return false; + } + + event.preventDefault(); + event.stopPropagation(); + + tree.setFocus(element); + + let anchor = { x: event.posx + 1, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this.actionProvider.getActions(tree, element), + getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id), + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + tree.DOMFocus(); + } + }, + getActionsContext: () => (element) + }); + + return true; + } +} \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/taskHistoryDataSource.ts b/src/sql/parts/taskHistory/viewlet/taskHistoryDataSource.ts new file mode 100644 index 0000000000..e18a535757 --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/taskHistoryDataSource.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; +import { TaskNode } from 'sql/parts/taskHistory/common/taskNode'; +import { TPromise } from 'vs/base/common/winjs.base'; + +/** + * Implements the DataSource(that returns a parent/children of an element) for the task history + */ +export class TaskHistoryDataSource implements IDataSource { + + /** + * Returns the unique identifier of the given element. + * No more than one element may use a given identifier. + */ + public getId(tree: ITree, element: any): string { + if (element instanceof TaskNode) { + return (element).id; + } else { + return undefined; + } + } + + /** + * Returns a boolean value indicating whether the element has children. + */ + public hasChildren(tree: ITree, element: any): boolean { + if (element instanceof TaskNode) { + return (element).hasChildren; + } + return false; + } + + /** + * Returns the element's children as an array in a promise. + */ + public getChildren(tree: ITree, element: any): TPromise { + if (element instanceof TaskNode) { + return TPromise.as((element).children); + } + return TPromise.as(null); + } + + /** + * Returns the element's parent in a promise. + */ + public getParent(tree: ITree, element: any): TPromise { + return TPromise.as(null); + } +} \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/taskHistoryRenderer.ts b/src/sql/parts/taskHistory/viewlet/taskHistoryRenderer.ts new file mode 100644 index 0000000000..01a7f08bef --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/taskHistoryRenderer.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { $ } from 'vs/base/browser/dom'; +import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree'; +import { ITaskHistoryTemplateData } from 'sql/parts/taskHistory/viewlet/templateData'; +import { TaskNode, TaskStatus } from 'sql/parts/taskHistory/common/taskNode'; +import dom = require('vs/base/browser/dom'); +import { localize } from 'vs/nls'; +import * as Utils from 'sql/parts/connection/common/utils'; + +/** + * Renders the tree items. + * Uses the dom template to render task history. + */ +export class TaskHistoryRenderer implements IRenderer { + + public static readonly TASKOBJECT_HEIGHT = 65; + private static readonly ICON_CLASS = 'task-icon icon'; + private static readonly TASKOBJECT_TEMPLATE_ID = 'carbonTask'; + private static readonly FAIL_CLASS = 'error'; + private static readonly SUCCESS_CLASS = 'success'; + private static readonly INPROGRESS_CLASS = 'in-progress'; + private static readonly NOTSTARTED_CLASS = 'not-started'; + private static readonly CANCELED_CLASS = 'canceled'; + + /** + * Returns the element's height in the tree, in pixels. + */ + public getHeight(tree: ITree, element: any): number { + return TaskHistoryRenderer.TASKOBJECT_HEIGHT; + } + + /** + * Returns a template ID for a given element. + */ + public getTemplateId(tree: ITree, element: any): string { + return TaskHistoryRenderer.TASKOBJECT_TEMPLATE_ID; + } + + /** + * Render template in a dom element based on template id + */ + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + const taskTemplate: ITaskHistoryTemplateData = Object.create(null); + taskTemplate.root = dom.append(container, $('.task-group')); + taskTemplate.icon = dom.append(taskTemplate.root, $('img.task-icon')); + var titleContainer = dom.append(taskTemplate.root, $('div.task-details')); + taskTemplate.title = dom.append(titleContainer, $('div.title')); + taskTemplate.description = dom.append(titleContainer, $('div.description')); + taskTemplate.time = dom.append(titleContainer, $('div.time')); + return taskTemplate; + } + + /** + * Render a element, given an object bag returned by the template + */ + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + this.renderTask(tree, element, templateData); + } + + private renderTask(tree: ITree, taskNode: TaskNode, templateData: ITaskHistoryTemplateData): void { + let taskStatus; + if (taskNode) { + templateData.icon.className = TaskHistoryRenderer.ICON_CLASS; + switch (taskNode.status) { + case TaskStatus.succeeded: + templateData.icon.classList.add(TaskHistoryRenderer.SUCCESS_CLASS); + taskStatus = localize('succeeded', "succeeded"); + break; + case TaskStatus.failed: + templateData.icon.classList.add(TaskHistoryRenderer.FAIL_CLASS); + taskStatus = localize('failed', "failed"); + break; + case TaskStatus.inProgress: + templateData.icon.classList.add(TaskHistoryRenderer.INPROGRESS_CLASS); + taskStatus = localize('inProgress', "in progress"); + break; + case TaskStatus.notStarted: + templateData.icon.classList.add(TaskHistoryRenderer.NOTSTARTED_CLASS); + taskStatus = localize('notStarted', "not started"); + break; + case TaskStatus.canceled: + templateData.icon.classList.add(TaskHistoryRenderer.CANCELED_CLASS); + taskStatus = localize('canceled', "canceled"); + break; + case TaskStatus.canceling: + templateData.icon.classList.add(TaskHistoryRenderer.INPROGRESS_CLASS); + taskStatus = localize('canceling', "canceling"); + break; + } + // Set hover text for icon to same as task status + templateData.icon.title = taskStatus; + + // Determine the task title and set hover text equal to that + templateData.title.textContent = taskNode.taskName + ' ' + taskStatus; + templateData.title.title = templateData.title.textContent; + + // Determine the target name and set hover text equal to that + let description = taskNode.serverName; + if (taskNode.databaseName) { + description += ' | ' + taskNode.databaseName; + } + templateData.description.textContent = description; + templateData.description.title = templateData.description.textContent; + + this.timer(taskNode, templateData); + let self = this; + setInterval(function () { + self.timer(taskNode, templateData); + }, 1000); + } + } + + public timer(taskNode: TaskNode, templateData: ITaskHistoryTemplateData) { + let timeLabel = ''; + if (taskNode.status === TaskStatus.failed) { + timeLabel += taskNode.startTime + ' Error: ' + taskNode.message; + } else { + if (taskNode.startTime) { + timeLabel = taskNode.startTime; + } + if (taskNode.endTime) { + timeLabel += ' - ' + taskNode.endTime; + } + + if (taskNode.timer) { + // Round task duration to seconds and then convert back to milliseconds + let duration = Math.floor(taskNode.timer.elapsed() / 1000) * 1000; + timeLabel += ' (' + Utils.parseNumAsTimeString(duration) + ')'; + } + } + templateData.time.textContent = timeLabel; + templateData.time.title = timeLabel; + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + // no op + // InputBox disposed in wrapUp + + } +} + diff --git a/src/sql/parts/taskHistory/viewlet/taskHistoryView.ts b/src/sql/parts/taskHistory/viewlet/taskHistoryView.ts new file mode 100644 index 0000000000..246a982f30 --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/taskHistoryView.ts @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import errors = require('vs/base/common/errors'); +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import Severity from 'vs/base/common/severity'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import * as builder from 'vs/base/browser/builder'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DefaultFilter, DefaultDragAndDrop, DefaultAccessibilityProvider } from 'vs/base/parts/tree/browser/treeDefaults'; +import { localize } from 'vs/nls'; + +import { TaskHistoryRenderer } from 'sql/parts/taskHistory/viewlet/taskHistoryRenderer'; +import { TaskHistoryDataSource } from 'sql/parts/taskHistory/viewlet/taskHistoryDataSource'; +import { TaskHistoryController } from 'sql/parts/taskHistory/viewlet/taskHistoryController'; +import { TaskHistoryActionProvider } from 'sql/parts/taskHistory/viewlet/taskHistoryActionProvider'; +import { ITaskService } from 'sql/parts/taskHistory/common/taskService'; +import { TaskNode, TaskStatus } from 'sql/parts/taskHistory/common/taskNode'; +import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; + +const $ = builder.$; + +/** + * TaskHistoryView implements the dynamic tree view. + */ +export class TaskHistoryView { + private _messages: builder.Builder; + private _tree: ITree; + private _toDispose: IDisposable[] = []; + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService, + @ITaskService private _taskService: ITaskService, + @IErrorMessageService private _errorMessageService: IErrorMessageService, + @IThemeService private _themeService: IThemeService + ) { + } + + /** + * Render the view body + */ + public renderBody(container: HTMLElement): void { + + let taskNode = this._taskService.getAllTasks(); + + // Add div to display no task executed message + this._messages = $('div.empty-task-message').appendTo(container); + + if (taskNode && taskNode.hasChildren) { + this._messages.hide(); + } + let noTaskMessage = localize('noTaskMessage', 'No task history to display. Try backup or restore task to view its execution status.'); + $('span').text(noTaskMessage).appendTo(this._messages); + + this._tree = this.createTaskHistoryTree(container, this._instantiationService); + this._toDispose.push(this._tree.addListener('selection', (event) => this.onSelected(event))); + + // Theme styler + this._toDispose.push(attachListStyler(this._tree, this._themeService)); + + this._toDispose.push(this._taskService.onAddNewTask(args => { + this._messages.hide(); + this.refreshTree(); + })); + this._toDispose.push(this._taskService.onTaskComplete(task => { + this.updateTask(task); + })); + + // Refresh Tree when these events are emitted + this.refreshTree(); + } + + /** + * Create a task history tree + */ + public createTaskHistoryTree(treeContainer: HTMLElement, instantiationService: IInstantiationService): Tree { + const dataSource = instantiationService.createInstance(TaskHistoryDataSource); + const actionProvider = instantiationService.createInstance(TaskHistoryActionProvider); + const renderer = instantiationService.createInstance(TaskHistoryRenderer); + const controller = instantiationService.createInstance(TaskHistoryController, actionProvider); + const dnd = new DefaultDragAndDrop(); + const filter = new DefaultFilter(); + const sorter = null; + const accessibilityProvider = new DefaultAccessibilityProvider(); + + return new Tree(treeContainer, { + dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider + }, { + indentPixels: 10, + twistiePixels: 20, + ariaLabel: nls.localize({ key: 'regTreeAriaLabel', comment: ['TaskHistory'] }, 'Task history') + }); + } + + private updateTask(task: TaskNode): void { + this._tree.refresh(task); + } + + public refreshTree(): void { + let selectedElement: any; + let targetsToExpand: any[]; + + // Focus + this._tree.DOMFocus(); + + if (this._tree) { + let selection = this._tree.getSelection(); + if (selection && selection.length === 1) { + selectedElement = selection[0]; + } + targetsToExpand = this._tree.getExpandedElements(); + } + + //Get the tree Input + let treeInput = this._taskService.getAllTasks(); + if (treeInput) { + this._tree.setInput(treeInput).then(() => { + // Make sure to expand all folders that where expanded in the previous session + if (targetsToExpand) { + this._tree.expandAll(targetsToExpand); + } + if (selectedElement) { + this._tree.select(selectedElement); + } + this._tree.getFocus(); + }, errors.onUnexpectedError); + } + } + + private onSelected(event: any) { + let selection = this._tree.getSelection(); + + if (selection && selection.length > 0 && (selection[0] instanceof TaskNode)) { + let task = selection[0]; + let isMouseOrigin = event.payload && (event.payload.origin === 'mouse'); + let isDoubleClick = isMouseOrigin && event.payload.originalEvent && event.payload.originalEvent.detail === 2; + if (isDoubleClick) { + if (task.status === TaskStatus.failed) { + var err = task.taskName + ': ' + task.message; + this._errorMessageService.showDialog(Severity.Error, nls.localize('taskError','Task Error'), err); + } + } + } + } + + /** + * set the layout of the view + */ + public layout(height: number): void { + this._tree.layout(height); + } + + /** + * set the visibility of the view + */ + public setVisible(visible: boolean): void { + if (visible) { + this._tree.onVisible(); + } else { + this._tree.onHidden(); + } + } + + /** + * dispose the server tree view + */ + public dispose(): void { + this._tree.dispose(); + this._toDispose = dispose(this._toDispose); + } +} \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/taskHistoryViewlet.ts b/src/sql/parts/taskHistory/viewlet/taskHistoryViewlet.ts new file mode 100644 index 0000000000..732d2b25bc --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/taskHistoryViewlet.ts @@ -0,0 +1,82 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import 'vs/css!sql/media/icons/common-icons'; +import 'vs/css!./media/taskHistoryViewlet'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Builder, Dimension } from 'vs/base/browser/builder'; +import { Viewlet } from 'vs/workbench/browser/viewlet'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { toggleClass } from 'vs/base/browser/dom'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; +import Severity from 'vs/base/common/severity'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { TaskHistoryView } from 'sql/parts/taskHistory/viewlet/taskHistoryView'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; + +export const VIEWLET_ID = 'workbench.view.taskHistory'; + +export class TaskHistoryViewlet extends Viewlet { + + private _root: HTMLElement; + private _toDisposeViewlet: IDisposable[] = []; + private _taskHistoryView: TaskHistoryView; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IConnectionManagementService private connectionManagementService: IConnectionManagementService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IViewletService private viewletService: IViewletService, + @IMessageService private messageService: IMessageService + ) { + super(VIEWLET_ID, telemetryService, themeService); + } + + private onError(err: any): void { + if (isPromiseCanceledError(err)) { + return; + } + this.messageService.show(Severity.Error, err); + } + + public create(parent: Builder): TPromise { + super.create(parent); + this._root = parent.getHTMLElement(); + this._taskHistoryView = this._instantiationService.createInstance(TaskHistoryView); + this._taskHistoryView.renderBody(parent.getHTMLElement()); + + return TPromise.as(null); + } + + public setVisible(visible: boolean): TPromise { + return super.setVisible(visible).then(() => { + this._taskHistoryView.setVisible(visible); + }); + } + + public focus(): void { + super.focus(); + } + + public layout({ height, width }: Dimension): void { + this._taskHistoryView.layout(height); + toggleClass(this._root, 'narrow', width <= 350); + } + + public getOptimalWidth(): number { + return 400; + } + + public dispose(): void { + this._toDisposeViewlet = dispose(this._toDisposeViewlet); + } + +} diff --git a/src/sql/parts/taskHistory/viewlet/templateData.ts b/src/sql/parts/taskHistory/viewlet/templateData.ts new file mode 100644 index 0000000000..c2ee9f6c96 --- /dev/null +++ b/src/sql/parts/taskHistory/viewlet/templateData.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +export interface ITaskHistoryTemplateData { + root: HTMLElement; + icon: HTMLElement; + title: HTMLSpanElement; + description: HTMLSpanElement; + time: HTMLSpanElement; +} diff --git a/src/sql/parts/tasks/common/tasks.contribution.ts b/src/sql/parts/tasks/common/tasks.contribution.ts new file mode 100644 index 0000000000..727342021f --- /dev/null +++ b/src/sql/parts/tasks/common/tasks.contribution.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { TaskDialogEditor } from 'sql/parts/tasks/dialog/taskDialogEditor'; +import { TaskDialogInput } from 'sql/parts/tasks/dialog/taskDialogInput'; +import { CreateLoginEditor } from 'sql/parts/admin/security/createLoginEditor'; +import { CreateLoginInput } from 'sql/parts/admin/security/createLoginInput'; + +// Task Dialog registration +const taskDialogEditorDescriptor = new EditorDescriptor( + TaskDialogEditor.ID, + 'Task Dialog', + 'sql/parts/tasks/dialog/taskDialogEditor', + 'TaskDialogEditor' +); + +// Create Login registration +const createLoginEditorDescriptor = new EditorDescriptor( + CreateLoginEditor.ID, + 'CreateLogin', + 'sql/parts/admin/security/createLoginEditor', + 'CreateLoginEditor' +); + +Registry.as(EditorExtensions.Editors) + .registerEditor(createLoginEditorDescriptor, [new SyncDescriptor(CreateLoginInput)]); + +Registry.as(EditorExtensions.Editors) + .registerEditor(taskDialogEditorDescriptor, [new SyncDescriptor(TaskDialogInput)]); \ No newline at end of file diff --git a/src/sql/parts/tasks/common/tasks.ts b/src/sql/parts/tasks/common/tasks.ts new file mode 100644 index 0000000000..22c8220e89 --- /dev/null +++ b/src/sql/parts/tasks/common/tasks.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TaskDialogComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +/** + * Interface for task dialog component events + */ +export interface ITaskDialogComponent { + onOk(): void; + + onGenerateScript(): void; + + onCancel(): void; + + injectBootstapper(parameters: TaskDialogComponentParams ): void; +} diff --git a/src/sql/parts/tasks/dialog/taskDialog.component.html b/src/sql/parts/tasks/dialog/taskDialog.component.html new file mode 100644 index 0000000000..5efde3d263 --- /dev/null +++ b/src/sql/parts/tasks/dialog/taskDialog.component.html @@ -0,0 +1,10 @@ + + +
+ +
diff --git a/src/sql/parts/tasks/dialog/taskDialog.component.ts b/src/sql/parts/tasks/dialog/taskDialog.component.ts new file mode 100644 index 0000000000..3c75910960 --- /dev/null +++ b/src/sql/parts/tasks/dialog/taskDialog.component.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import { ITaskDialogComponent } from 'sql/parts/tasks/common/tasks'; +import { TaskDialogComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { ElementRef, Component, Inject, forwardRef } from '@angular/core'; + +export const TASKDIALOG_SELECTOR: string = 'taskdialog-component'; + +@Component({ + selector: TASKDIALOG_SELECTOR, + templateUrl: decodeURI(require.toUrl('sql/parts/tasks/dialog/taskDialog.component.html')), +}) +export class TaskDialogComponent { + + private _currentComponent: ITaskDialogComponent; + + private _parameters: TaskDialogComponentParams; + + public ownerUri: string; + + public connection: ConnectionManagementInfo; + + constructor( + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { + this._parameters = this._bootstrapService.getBootstrapParams(this._el.nativeElement.tagName); + this.ownerUri = this._parameters.ownerUri; + + } + + public onActivate(component: any) { + // validate the component implements ITaskDialogComponent (or at least part of the interface) + if ((component).onOk) { + this._currentComponent = component; + this._currentComponent.injectBootstapper(this._parameters); + } else { + this._currentComponent = undefined; + } + } + + public onDeactivate(component: any) { + + } +} diff --git a/src/sql/parts/tasks/dialog/taskDialog.module.ts b/src/sql/parts/tasks/dialog/taskDialog.module.ts new file mode 100644 index 0000000000..cf8bc41475 --- /dev/null +++ b/src/sql/parts/tasks/dialog/taskDialog.module.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { Routes, RouterModule } from '@angular/router'; +import { ApplicationRef, ComponentFactoryResolver, ModuleWithProviders, NgModule, + Inject, forwardRef } from '@angular/core'; +import { APP_BASE_HREF, CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; + +import { TaskDialogComponent, TASKDIALOG_SELECTOR } from 'sql/parts/tasks/dialog/taskDialog.component'; +import { CreateDatabaseComponent } from 'sql/parts/admin/database/create/createDatabase.component'; + +// Setup routes for various child components +const appRoutes: Routes = [ + { path: 'create-database', component: CreateDatabaseComponent }, + { + path: '', + redirectTo: '/create-database', + pathMatch: 'full' + }, + { path: '**', component: CreateDatabaseComponent } +]; + + +@NgModule({ + declarations: [ + TaskDialogComponent, + CreateDatabaseComponent + ], + entryComponents: [TaskDialogComponent], + imports: [ + FormsModule, + CommonModule, + BrowserModule, + RouterModule.forRoot(appRoutes) + ], + providers: [{ provide: APP_BASE_HREF, useValue: '/' }] +}) +export class TaskDialogModule { + + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { + } + + ngDoBootstrap(appRef: ApplicationRef) { + const factory = this._resolver.resolveComponentFactory(TaskDialogComponent); + const uniqueSelector: string = this._bootstrapService.getUniqueSelector(TASKDIALOG_SELECTOR); + (factory).factory.selector = uniqueSelector; + appRef.bootstrap(factory); + } +} diff --git a/src/sql/parts/tasks/dialog/taskDialogEditor.ts b/src/sql/parts/tasks/dialog/taskDialogEditor.ts new file mode 100644 index 0000000000..1a7f285d68 --- /dev/null +++ b/src/sql/parts/tasks/dialog/taskDialogEditor.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * 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!sql/parts/query/editor/media/queryEditor'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TaskDialogInput } from './taskDialogInput'; +import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import { TaskDialogComponentParams } from 'sql/services/bootstrap/bootstrapParams'; +import { TaskDialogModule } from 'sql/parts/tasks/dialog/taskDialog.module'; +import { TASKDIALOG_SELECTOR } from 'sql/parts/tasks/dialog/taskDialog.component'; + +export class TaskDialogEditor extends BaseEditor { + + public static ID: string = 'workbench.editor.taskdialog'; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IInstantiationService private instantiationService: IInstantiationService, + @IBootstrapService private _bootstrapService: IBootstrapService + ) { + super(TaskDialogEditor.ID, telemetryService, themeService); + } + + /** + * Called to create the editor in the parent builder. + */ + public createEditor(parent: Builder): void { + } + + /** + * Sets focus on this editor. Specifically, it sets the focus on the hosted text editor. + */ + public focus(): void { + } + + /** + * Updates the internal variable keeping track of the editor's size, and re-calculates the sash position. + * To be called when the container of this editor changes size. + */ + public layout(dimension: Dimension): void { + } + + public setInput(input: TaskDialogInput, options: EditorOptions): TPromise { + if (this.input instanceof TaskDialogInput && this.input.matches(input)) { + return TPromise.as(undefined); + } + + if (!input.hasInitialized) { + this.bootstrapAngular(input); + } + this.revealElementWithTagName(input.uniqueSelector, this.getContainer().getHTMLElement()); + + return super.setInput(input, options); + } + + /** + * Reveal the child element with the given tagName and hide all other elements. + */ + private revealElementWithTagName(tagName: string, parent: HTMLElement): void { + let elementToReveal: HTMLElement; + + for(let i = 0; i < parent.children.length; i++) { + let child: HTMLElement = parent.children[i]; + if (child.tagName && child.tagName.toLowerCase() === tagName && !elementToReveal) { + elementToReveal = child; + } else { + child.style.display = 'none'; + } + } + + if (elementToReveal) { + elementToReveal.style.display = ''; + } + } + + /** + * Load the angular components and record for this input that we have done so + */ + private bootstrapAngular(input: TaskDialogInput): void { + + // Get the bootstrap params and perform the bootstrap + let params: TaskDialogComponentParams = { + ownerUri: input.getUri() + }; + let uniqueSelector = this._bootstrapService.bootstrap( + TaskDialogModule, + this.getContainer().getHTMLElement(), + TASKDIALOG_SELECTOR, + params); + input.setUniqueSelector(uniqueSelector); + } + + public dispose(): void { + super.dispose(); + } +} diff --git a/src/sql/parts/tasks/dialog/taskDialogInput.ts b/src/sql/parts/tasks/dialog/taskDialogInput.ts new file mode 100644 index 0000000000..40583479b5 --- /dev/null +++ b/src/sql/parts/tasks/dialog/taskDialogInput.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; + +export class TaskDialogInput extends EditorInput { + + public static ID: string = 'workbench.editorinputs.taskdialoginputs'; + public static SCHEMA: string = 'taskdialog'; + + private _uniqueSelector: string; + + constructor(private _uri: string, private _connection: IConnectionProfile) { + super(); + } + + public setUniqueSelector(uniqueSelector: string): void { + this._uniqueSelector = uniqueSelector; + } + + public getTypeId(): string { + return UntitledEditorInput.ID; + } + + public getName(): string { + return this._connection.serverName + ':' + this._connection.databaseName; + } + + public getUri(): string { + return this._uri; + } + + public supportsSplitEditor(): boolean { + return false; + } + + public getConnectionProfile(): IConnectionProfile { + return this._connection; + } + + public resolve(refresh?: boolean): TPromise { + return undefined; + } + + public get hasInitialized(): boolean { + return !!this._uniqueSelector; + } + + public get uniqueSelector(): string { + return this._uniqueSelector; + } +} diff --git a/src/sql/platform/clipboard/common/clipboardService.ts b/src/sql/platform/clipboard/common/clipboardService.ts new file mode 100644 index 0000000000..e158e364fe --- /dev/null +++ b/src/sql/platform/clipboard/common/clipboardService.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IClipboardService as vsIClipboardService } from 'vs/platform/clipboard/common/clipboardService'; + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IClipboardService = createDecorator('sqlclipboardService'); + +export interface IClipboardService extends vsIClipboardService { + /** + * Writes the input image as a dataurl to the clipbaord + */ + writeImageDataUrl(data: string): void; +} \ No newline at end of file diff --git a/src/sql/platform/clipboard/electron-browser/clipboardService.ts b/src/sql/platform/clipboard/electron-browser/clipboardService.ts new file mode 100644 index 0000000000..e0ad7e61e2 --- /dev/null +++ b/src/sql/platform/clipboard/electron-browser/clipboardService.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService'; +import { IClipboardService as vsIClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { clipboard, nativeImage } from 'electron'; + +export class ClipboardService implements IClipboardService { + _serviceBrand: any; + + constructor( + @vsIClipboardService private _vsClipboardService: vsIClipboardService + ) { } + + /** + * Writes the input image as a dataurl to the clipbaord + */ + writeImageDataUrl(data: string): void { + let image = nativeImage.createFromDataURL(data); + clipboard.writeImage(image); + } + + writeText(text: string): void { + this._vsClipboardService.writeText(text); + } +} diff --git a/src/sql/platform/dashboard/common/insightRegistry.ts b/src/sql/platform/dashboard/common/insightRegistry.ts new file mode 100644 index 0000000000..a7f10c7c28 --- /dev/null +++ b/src/sql/platform/dashboard/common/insightRegistry.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Type } from '@angular/core'; +import { IInsightsConfig, IInsightsView } from 'sql/parts/dashboard/widgets/insights/interfaces'; + +import * as platform from 'vs/platform/registry/common/platform'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import * as nls from 'vs/nls'; + +export type InsightIdentifier = string; + +export const Extensions = { + InsightContribution: 'dashboard.contributions.insights' +}; + +export interface IInsightRegistry { + insightSchema: IJSONSchema; + registerInsight(id: string, description: string, schema: IJSONSchema, ctor: Type): InsightIdentifier; + registerExtensionInsight(id: string, val: IInsightsConfig): void; + getRegisteredExtensionInsights(id: string): IInsightsConfig; + getCtorFromId(id: string): Type; + getAllCtors(): Array>; + getAllIds(): Array; +} + +class InsightRegistry implements IInsightRegistry { + private _insightSchema: IJSONSchema = { type: 'object', description: nls.localize('schema.dashboardWidgets', 'Widget used in the dashboards'), properties: {}, additionalProperties: false }; + private _extensionInsights: { [x: string]: IInsightsConfig } = {}; + private _idToCtor: { [x: string]: Type } = {}; + + /** + * Register a dashboard widget + * @param id id of the widget + * @param description description of the widget + * @param schema config schema of the widget + */ + public registerInsight(id: string, description: string, schema: IJSONSchema, ctor: Type): InsightIdentifier { + this._insightSchema.properties[id] = schema; + this._idToCtor[id] = ctor; + return id; + } + + public registerExtensionInsight(id: string, val: IInsightsConfig): void { + this._extensionInsights[id] = val; + } + + public getRegisteredExtensionInsights(id: string): IInsightsConfig { + return this._extensionInsights[id]; + } + + public getCtorFromId(id: string): Type { + return this._idToCtor[id]; + } + + public getAllCtors(): Array> { + return Object.values(this._idToCtor); + } + + public getAllIds(): Array { + return Object.keys(this._idToCtor); + } + + public get insightSchema(): IJSONSchema { + return this._insightSchema; + } +} + +const insightRegistry = new InsightRegistry(); +platform.Registry.add(Extensions.InsightContribution, insightRegistry); + +export function registerInsight(id: string, description: string, schema: IJSONSchema, ctor: Type): InsightIdentifier { + return insightRegistry.registerInsight(id, description, schema, ctor); +} diff --git a/src/sql/platform/dashboard/common/widgetRegistry.ts b/src/sql/platform/dashboard/common/widgetRegistry.ts new file mode 100644 index 0000000000..2f18cb9666 --- /dev/null +++ b/src/sql/platform/dashboard/common/widgetRegistry.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces'; + +import * as platform from 'vs/platform/registry/common/platform'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import * as nls from 'vs/nls'; + +export type WidgetIdentifier = string; + +export const Extensions = { + DashboardWidgetContribution: 'dashboard.contributions.widgets' +}; + +export interface IDashboardWidgetRegistry { + databaseWidgetSchema: IJSONSchema; + serverWidgetSchema: IJSONSchema; + registerWidget(id: string, description: string, schema: IJSONSchema, context?: 'database' | 'server'): WidgetIdentifier; + registerNonCustomDashboardWidget(id: string, description: string, val: IInsightsConfig, context?: 'database' | 'server'): WidgetIdentifier; +} + +class DashboardWidgetRegistry implements IDashboardWidgetRegistry { + private _dashboardWidgetSchema: IJSONSchema = { type: 'object', description: nls.localize('schema.dashboardWidgets', 'Widget used in the dashboards'), properties: {}, additionalProperties: false }; + private _serverWidgetSchema: IJSONSchema = { type: 'object', description: nls.localize('schema.dashboardWidgets', 'Widget used in the dashboards'), properties: {}, additionalProperties: false }; + /** + * Register a dashboard widget + * @param id id of the widget + * @param description description of the widget + * @param schema config schema of the widget + * @param context either 'database' or 'server' for what page to register for; if not specified, will register for both + */ + public registerWidget(id: string, description: string, schema: IJSONSchema, context?: 'database' | 'server'): WidgetIdentifier { + if (context === undefined || context === 'database') { + this._dashboardWidgetSchema.properties[id] = schema; + } + + if (context === undefined || context === 'server') { + this._serverWidgetSchema.properties[id] = schema; + } + + return id; + } + + /** + * Register a non custom dashboard widget + * @param id id of the widget + * @param description description of the widget + * @param val cal for default + * @param context either 'database' or 'server' for what page to register for; if not specified, will register for both + */ + registerNonCustomDashboardWidget(id: string, description: string, val: IInsightsConfig, context?: 'database' | 'server'): WidgetIdentifier { + if (context === undefined || context === 'database') { + this._dashboardWidgetSchema.properties[id] = { type: 'null', default: null }; + } + + if (context === undefined || context === 'server') { + this._serverWidgetSchema.properties[id] = { type: 'null', default: null }; + } + + return id; + } + + public get databaseWidgetSchema(): IJSONSchema { + return this._dashboardWidgetSchema; + } + + public get serverWidgetSchema(): IJSONSchema { + return this._serverWidgetSchema; + } +} + +const dashboardWidgetRegistry = new DashboardWidgetRegistry(); +platform.Registry.add(Extensions.DashboardWidgetContribution, dashboardWidgetRegistry); + +export function registerDashboardWidget(id: string, description: string, schema: IJSONSchema, context?: 'database' | 'server'): WidgetIdentifier { + return dashboardWidgetRegistry.registerWidget(id, description, schema, context); +} + +export function registerNonCustomDashboardWidget(id: string, description: string, val: IInsightsConfig): WidgetIdentifier { + return dashboardWidgetRegistry.registerNonCustomDashboardWidget(id, description, val); +} \ No newline at end of file diff --git a/src/sql/platform/tasks/taskRegistry.ts b/src/sql/platform/tasks/taskRegistry.ts new file mode 100644 index 0000000000..7f4fa2afcd --- /dev/null +++ b/src/sql/platform/tasks/taskRegistry.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as platform from 'vs/platform/registry/common/platform'; +import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; +import { Action } from 'vs/base/common/actions'; +import { IConstructorSignature3 } from 'vs/platform/instantiation/common/instantiation'; +import * as nls from 'vs/nls'; + +export type TaskIdentifier = string; + +export interface ActionICtor extends IConstructorSignature3 { + ID: string; + LABEL: string; + ICON: string; +} + +export const Extensions = { + TaskContribution: 'workbench.contributions.tasks' +}; + +export interface ITaskRegistry { + /** + * Returns a map of action ids to their contructors; + */ + idToCtorMap: { [id: string]: ActionICtor }; + + /** + * Returns array of registered ids + */ + ids: Array; + + /** + * Schemas of the tasks registered + */ + taskSchemas: IJSONSchemaMap; + + /** + * Registers an action as a task which can be ran given the schema as an input + * @param id id of the task + * @param description desciption of the task + * @param schema schema of expected input + * @param ctor contructor of the action + */ + registerTask(id: string, description: string, schema: IJSONSchema, ctor: ActionICtor): TaskIdentifier; +} + +class TaskRegistry implements ITaskRegistry { + private _idCtorMap: { [id: string]: ActionICtor } = {}; + private _taskSchema: IJSONSchema = { type: 'object', description: nls.localize('schema.taskSchema', 'Task actions specific for sql'), properties: {}, additionalProperties: false }; + + get idToCtorMap(): { [id: string]: ActionICtor } { + return this._idCtorMap; + } + + get ids(): Array { + return Object.keys(this._idCtorMap); + } + + get taskSchemas(): IJSONSchemaMap { + return this._taskSchema.properties; + } + + /** + * Registers an action as a task which can be ran given the schema as an input + * @param id id of the task + * @param description desciption of the task + * @param schema schema of expected input + * @param ctor contructor of the action + */ + registerTask(id: string, description: string, schema: IJSONSchema, ctor: ActionICtor): TaskIdentifier { + this._idCtorMap[id] = ctor; + this._taskSchema.properties[id] = schema; + return id; + } +} + +const taskRegistry = new TaskRegistry(); +platform.Registry.add(Extensions.TaskContribution, taskRegistry); + +export function registerTask(id: string, description: string, schema: IJSONSchema, ctor: ActionICtor): TaskIdentifier { + return taskRegistry.registerTask(id, description, schema, ctor); +} \ No newline at end of file diff --git a/src/sql/platform/views/fixedCollapsibleView.ts b/src/sql/platform/views/fixedCollapsibleView.ts new file mode 100644 index 0000000000..e21b455244 --- /dev/null +++ b/src/sql/platform/views/fixedCollapsibleView.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import types = require('vs/base/common/types'); +import objects = require('vs/base/common/objects'); + +import { + ICollapsibleViewOptions, AbstractCollapsibleView, ViewSizing, CollapsibleState +} from 'vs/base/browser/ui/splitview/splitview'; + +export interface IFixedCollapsibleViewOptions extends ICollapsibleViewOptions { + expandedBodySize?: number; +} + +export abstract class FixedCollapsibleView extends AbstractCollapsibleView { + private _expandedBodySize: number; + + constructor(initialSize: number, opts: IFixedCollapsibleViewOptions) { + super(initialSize, objects.mixin({ sizing: ViewSizing.Fixed }, opts)); + this._expandedBodySize = types.isUndefined(opts.expandedBodySize) ? 22 : opts.expandedBodySize; + } + + get fixedSize(): number { return this.state === CollapsibleState.EXPANDED ? this.expandedSize : this.headerSize; } + private get expandedSize(): number { return this.expandedBodySize + this.headerSize; } + + get expandedBodySize(): number { return this._expandedBodySize; } + set expandedBodySize(size: number) { + this._expandedBodySize = size; + this.setFixed(this.fixedSize); + } + + protected changeState(state: CollapsibleState): void { + super.changeState(state); + this.setFixed(this.fixedSize); + } +} diff --git a/src/sql/platform/views/fixedListView.ts b/src/sql/platform/views/fixedListView.ts new file mode 100644 index 0000000000..d5d1699ba6 --- /dev/null +++ b/src/sql/platform/views/fixedListView.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { CollapsibleView, ICollapsibleViewOptions } from 'vs/workbench/parts/views/browser/views'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { IAction, ActionRunner } from 'vs/base/common/actions'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { $ } from 'vs/base/browser/builder'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; +import { CollapsibleState } from 'vs/base/browser/ui/splitview/splitview'; + +export class FixedListView extends CollapsibleView { + private _badge: CountBadge; + private _disposables: IDisposable[] = []; + + constructor( + initialSize: number, + initiallyCollapsed: boolean, + private _viewTitle: string, + private _list: List, + private _bodyContainer: HTMLElement, + headerSize: number, + private _actions: IAction[], + actionRunner: ActionRunner, + contextMenuService: IContextMenuService, + keybindingService: IKeybindingService, + private _themeService: IThemeService + ) { + super(initialSize, { + id: _viewTitle, + name: _viewTitle, + actionRunner: actionRunner, + collapsed: initiallyCollapsed, + ariaHeaderLabel: _viewTitle, + sizing: headerSize, + initialBodySize: undefined + }, keybindingService, contextMenuService); + } + + // RENDER METHODS ////////////////////////////////////////////////////// + public renderBody(container: HTMLElement): void { + container.appendChild(this._bodyContainer); + } + + public renderHeader(container: HTMLElement): void { + const titleDiv = $('div.title').appendTo(container); + $('span').text(this._viewTitle).appendTo(titleDiv); + super.renderHeader(container); + + // show the badge + this._badge = new CountBadge($('.count-badge-wrapper').appendTo(container).getHTMLElement()); + this._disposables.push(attachBadgeStyler(this._badge, this._themeService)); + } + + public updateList(content: T[]) { + this._list.splice(0, this._list.length, content); + this._badge.setCount(this._list.length); + this._list.layout(this._list.contentHeight); + this.setFixed(this.fixedSize); + } + + public listContentHeight(): number { + return this._list.contentHeight; + } + + public get fixedSize(): number { + return this.state === CollapsibleState.EXPANDED ? this.expandedSize : this.headerSize; + } + + private get expandedSize(): number { + if (this._list && this._list.contentHeight) { + return this._list.contentHeight + this.headerSize; + } + + return this.headerSize; + } + + protected changeState(state: CollapsibleState): void { + super.changeState(state); + this.setFixed(this.fixedSize); + } + + /** + * Return actions for the view + */ + public getActions(): IAction[] { + return this._actions; + } + + public dispose(): void { + this._disposables = dispose(this._disposables); + super.dispose(); + } +} \ No newline at end of file diff --git a/src/sql/services/accountManagement/accountManagementService.ts b/src/sql/services/accountManagement/accountManagementService.ts new file mode 100644 index 0000000000..0fcbf93d59 --- /dev/null +++ b/src/sql/services/accountManagement/accountManagementService.ts @@ -0,0 +1,369 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import * as nls from 'vs/nls'; +import * as platform from 'vs/platform/registry/common/platform'; +import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar'; +import AccountStore from 'sql/services/accountManagement/accountStore'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { Memento, Scope as MementoScope } from 'vs/workbench/common/memento'; +import { ISqlOAuthService } from 'sql/common/sqlOAuthService'; +import { AccountDialogController } from 'sql/parts/accountManagement/accountDialog/accountDialogController'; +import { AccountListStatusbarItem } from 'sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem'; +import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes'; +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; +import { warn } from 'sql/base/common/log'; + +export class AccountManagementService implements IAccountManagementService { + // CONSTANTS /////////////////////////////////////////////////////////// + private static ACCOUNT_MEMENTO = 'AccountManagement'; + + // MEMBER VARIABLES //////////////////////////////////////////////////// + public _providers: { [id: string]: AccountProviderWithMetadata } = {}; + public _serviceBrand: any; + private _accountStore: AccountStore; + private _accountDialogController: AccountDialogController; + private _mementoContext: Memento; + private _oAuthCallbacks: { [eventId: string]: { resolve, reject } } = {}; + private _oAuthEventId: number = 0; + + // EVENT EMITTERS ////////////////////////////////////////////////////// + private _addAccountProviderEmitter: Emitter; + public get addAccountProviderEvent(): Event { return this._addAccountProviderEmitter.event; } + + private _removeAccountProviderEmitter: Emitter; + public get removeAccountProviderEvent(): Event { return this._removeAccountProviderEmitter.event; } + + private _updateAccountListEmitter: Emitter; + public get updateAccountListEvent(): Event { return this._updateAccountListEmitter.event; } + + // CONSTRUCTOR ///////////////////////////////////////////////////////// + constructor( + private _mementoObj: object, + @IInstantiationService private _instantiationService: IInstantiationService, + @IStorageService private _storageService: IStorageService, + @ISqlOAuthService private _oAuthService: ISqlOAuthService + ) { + let self = this; + + // Create the account store + if (!this._mementoObj) { + this._mementoContext = new Memento(AccountManagementService.ACCOUNT_MEMENTO); + this._mementoObj = this._mementoContext.getMemento(this._storageService, MementoScope.GLOBAL); + } + this._accountStore = this._instantiationService.createInstance(AccountStore, this._mementoObj); + + // Setup the event emitters + this._addAccountProviderEmitter = new Emitter(); + this._removeAccountProviderEmitter = new Emitter(); + this._updateAccountListEmitter = new Emitter(); + + // Register status bar item + // FEATURE FLAG TOGGLE + if (process.env['VSCODE_DEV']) { + let statusbarDescriptor = new statusbar.StatusbarItemDescriptor( + AccountListStatusbarItem, + statusbar.StatusbarAlignment.LEFT, + 15000 /* Highest Priority */ + ); + (platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(statusbarDescriptor); + } + + // Register event handler for OAuth completion + this._oAuthService.registerOAuthCallback((event, args) => { + self.onOAuthResponse(args); + }); + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + /** + * Asks the requested provider to prompt for an account + * @param {string} providerId ID of the provider to ask to prompt for an account + * @return {Thenable} Promise to return an account + */ + public addAccount(providerId: string): Thenable { + let self = this; + + return this.doWithProvider(providerId, (provider) => { + return provider.provider.prompt() + .then(account => self._accountStore.addOrUpdate(account)) + .then(result => { + if (result.accountAdded) { + // Add the account to the list + provider.accounts.push(result.changedAccount); + } + if (result.accountModified) { + // Find the updated account and splice the updated on in + let indexToRemove: number = provider.accounts.findIndex(account => { + return account.key.accountId === result.changedAccount.key.accountId; + }); + if (indexToRemove >= 0) { + provider.accounts.splice(indexToRemove, 1, result.changedAccount); + } + } + + self.fireAccountListUpdate(provider, result.accountAdded); + return result.changedAccount; + }); + }); + } + + /** + * Retrieves metadata of all providers that have been registered + * @returns {Thenable} Registered account providers + */ + public getAccountProviderMetadata(): Thenable { + return Promise.resolve(Object.values(this._providers).map(provider => provider.metadata)); + } + + /** + * Retrieves the accounts that belong to a specific provider + * @param {string} providerId ID of the provider the returned accounts belong to + * @returns {Thenable} Promise to return a list of accounts + */ + public getAccountsForProvider(providerId: string): Thenable { + let self = this; + + // Make sure the provider exists before attempting to retrieve accounts + if (!this._providers[providerId]) { + return Promise.reject(new Error(nls.localize('accountManagementNoProvider', 'Account provider does not exist'))).then(); + } + + // 1) Get the accounts from the store + // 2) Update our local cache of accounts + return this.doWithProvider(providerId, provider => { + return self._accountStore.getAccountsByProvider(provider.metadata.id) + .then(accounts => { + self._providers[providerId].accounts = accounts; + return accounts; + }); + }); + } + + /** + * Generates a security token by asking the account's provider + * @param {Account} account Account to generate security token for + * @return {Thenable<{}>} Promise to return the security token + */ + public getSecurityToken(account: data.Account): Thenable<{}> { + return this.doWithProvider(account.key.providerId, provider => { + return provider.provider.getSecurityToken(account); + }); + } + + /** + * Removes an account from the account store and clears sensitive data in the provider + * @param {AccountKey} accountKey Key for the account to remove + * @returns {Thenable} Promise with result of account removal, true if account was + * removed, false otherwise. + */ + public removeAccount(accountKey: data.AccountKey): Thenable { + let self = this; + + // Step 1) Remove the account + // Step 2) Clear the sensitive data from the provider (regardless of whether the account was removed) + // Step 3) Update the account cache and fire an event + return this.doWithProvider(accountKey.providerId, provider => { + return this._accountStore.remove(accountKey) + .then(result => { + provider.provider.clear(accountKey); + return result; + }) + .then(result => { + if (!result) { + return result; + } + + let indexToRemove: number = provider.accounts.findIndex(account => { + return account.key.accountId === accountKey.accountId; + }); + + if (indexToRemove >= 0) { + provider.accounts.splice(indexToRemove, 1); + self.fireAccountListUpdate(provider, false); + } + return result; + }); + }); + } + + // UI METHODS ////////////////////////////////////////////////////////// + /** + * Opens the account list dialog + * @return {TPromise} Promise that finishes when the account list dialog opens + */ + public openAccountListDialog(): Thenable { + let self = this; + + return new Promise((resolve, reject) => { + try { + // If the account list dialog hasn't been defined, create a new one + if (!self._accountDialogController) { + self._accountDialogController = self._instantiationService.createInstance(AccountDialogController); + } + + self._accountDialogController.openAccountDialog(); + resolve(); + } catch(e) { + reject(e); + } + }); + } + + /** + * Opens a browser window to perform the OAuth authentication + * @param {string} url URL to visit that will perform the OAuth authentication + * @param {boolean} silent Whether or not to perform authentication silently using browser's cookies + * @return {Thenable} Promise to return a authentication token on successful authentication + */ + public performOAuthAuthorization(url: string, silent: boolean): Thenable { + let self = this; + return new Promise((resolve, reject) => { + // TODO: replace with uniqid + let eventId: string = `oauthEvent${self._oAuthEventId++}`; + self._oAuthCallbacks[eventId] = { + resolve: resolve, + reject: reject + }; + + self._oAuthService.performOAuthAuthorization(eventId, url, silent); + }); + } + + // SERVICE MANAGEMENT METHODS ////////////////////////////////////////// + /** + * Called by main thread to register an account provider from extension + * @param {data.AccountProviderMetadata} providerMetadata Metadata of the provider that is being registered + * @param {data.AccountProvider} provider References to the methods of the provider + */ + public registerProvider(providerMetadata: data.AccountProviderMetadata, provider: data.AccountProvider): Thenable { + let self = this; + + // Store the account provider + this._providers[providerMetadata.id] = { + metadata: providerMetadata, + provider: provider, + accounts: [] + }; + + // Initialize the provider: + // 1) Get all the accounts that were stored + // 2) Give those accounts to the provider for rehydration + // 3) Add the accounts to our local store of accounts + // 4) Write the accounts back to the store + // 5) Fire the event to let folks know we have another account provider now + return this._accountStore.getAccountsByProvider(providerMetadata.id) + .then((accounts: data.Account[]) => { + return provider.initialize(accounts); + }) + .then((accounts: data.Account[]) => { + self._providers[providerMetadata.id].accounts = accounts; + let writePromises = accounts.map(account => { + return self._accountStore.addOrUpdate(account); + }); + return Promise.all(writePromises); + }) + .then(() => { + let provider = self._providers[providerMetadata.id]; + self._addAccountProviderEmitter.fire({ + addedProvider: provider.metadata, + initialAccounts: provider.accounts.slice(0) // Slice here to make sure no one can modify our cache + }); + }); + + // TODO: Add stale event handling to the providers + } + + /** + * Handler for when shutdown of the application occurs. Writes out the memento. + */ + public shutdown(): void { + if (this._mementoContext) { + this._mementoContext.saveMemento(); + } + } + + public unregisterProvider(providerMetadata: data.AccountProviderMetadata): void { + // Delete this account provider + delete this._providers[providerMetadata.id]; + + // Alert our listeners that we've removed a provider + this._removeAccountProviderEmitter.fire(providerMetadata); + } + + // TODO: Support for orphaned accounts (accounts with no provider) + + // PRIVATE HELPERS ///////////////////////////////////////////////////// + private doWithProvider(providerId: string, op: (provider: AccountProviderWithMetadata) => Thenable): Thenable { + // Make sure the provider exists before attempting to retrieve accounts + let provider = this._providers[providerId]; + if (!provider) { + return Promise.reject(new Error(nls.localize('accountManagementNoProvider', 'Account provider does not exist'))).then(); + } + + return op(provider); + } + + private fireAccountListUpdate(provider: AccountProviderWithMetadata, sort: boolean) { + // Step 1) Get and sort the list + if (sort) { + provider.accounts.sort((a: data.Account, b: data.Account) => { + if (a.displayInfo.displayName < b.displayInfo.displayName) { + return -1; + } + if (a.displayInfo.displayName > b.displayInfo.displayName) { + return 1; + } + return 0; + }); + } + + // Step 2) Fire the event + let eventArg: UpdateAccountListEventParams = { + providerId: provider.metadata.id, + accountList: provider.accounts + }; + this._updateAccountListEmitter.fire(eventArg); + } + + private onOAuthResponse(args: object): void { + // Verify the arguments are correct + if (!args || args['eventId'] === undefined) { + warn('Received invalid OAuth event response args'); + return; + } + + // Find the event + let eventId: string = args['eventId']; + let eventCallbacks = this._oAuthCallbacks[eventId]; + if (!eventCallbacks) { + warn('Received OAuth event response for non-existent eventId'); + return; + } + + // Parse the args + let error: string = args['error']; + let code: string = args['code']; + if (error) { + eventCallbacks.reject(error); + } else { + eventCallbacks.resolve(code); + } + } +} + +/** + * Joins together an account provider, its metadata, and its accounts, used in the provider list + */ +export interface AccountProviderWithMetadata { + metadata: data.AccountProviderMetadata; + provider: data.AccountProvider; + accounts: data.Account[]; +} diff --git a/src/sql/services/accountManagement/accountStore.ts b/src/sql/services/accountManagement/accountStore.ts new file mode 100644 index 0000000000..952c77011e --- /dev/null +++ b/src/sql/services/accountManagement/accountStore.ts @@ -0,0 +1,204 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import { AccountAdditionResult } from 'sql/services/accountManagement/eventTypes'; +import { IAccountStore } from 'sql/services/accountManagement/interfaces'; + +export default class AccountStore implements IAccountStore { + // CONSTANTS /////////////////////////////////////////////////////////// + public static MEMENTO_KEY: string = 'Microsoft.SqlTools.Accounts'; + + // MEMBER VARIABLES //////////////////////////////////////////////////// + private _activeOperation: Thenable; + + constructor(private _memento: object) { } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + public addOrUpdate(newAccount: data.Account): Thenable { + let self = this; + + return this.doOperation(() => { + return self.readFromMemento() + .then(accounts => { + // Determine if account exists and proceed accordingly + let match = accounts.findIndex(account => AccountStore.findAccountByKey(account.key, newAccount.key)); + return match < 0 + ? self.addToAccountList(accounts, newAccount) + : self.updateAccountList(accounts, newAccount.key, (matchAccount) => { AccountStore.mergeAccounts(newAccount, matchAccount); }); + }) + .then(result => self.writeToMemento(result.updatedAccounts).then(() => result)) + .then(result => result); + }); + } + + public getAccountsByProvider(providerId: string): Thenable { + let self = this; + + return this.doOperation(() => { + return self.readFromMemento() + .then(accounts => accounts.filter(account => account.key.providerId === providerId)); + }); + } + + public getAllAccounts(): Thenable { + let self = this; + + return this.doOperation(() => { + return self.readFromMemento(); + }); + } + + public remove(key: data.AccountKey): Thenable { + let self = this; + + return this.doOperation(() => { + return self.readFromMemento() + .then(accounts => self.removeFromAccountList(accounts, key)) + .then(result => self.writeToMemento(result.updatedAccounts).then(() => result)) + .then(result => result.accountRemoved); + }); + } + + public update(key: data.AccountKey, updateOperation: (account: data.Account) => void): Thenable { + let self = this; + + return this.doOperation(() => { + return self.readFromMemento() + .then(accounts => self.updateAccountList(accounts, key, updateOperation)) + .then(result => self.writeToMemento(result.updatedAccounts).then(() => result)) + .then(result => result.accountModified); + }); + } + + // PRIVATE METHODS ///////////////////////////////////////////////////// + private static findAccountByKey(key1: data.AccountKey, key2: data.AccountKey): boolean { + // Provider ID and Account ID must match + return key1.providerId === key2.providerId && key1.accountId === key2.accountId; + } + + private static mergeAccounts(source: data.Account, target: data.Account): void { + // Take any display info changes + target.displayInfo = source.displayInfo; + + // Take all property changes + target.properties = source.properties; + + // Take any stale changes + target.isStale = source.isStale; + } + + private doOperation(op: () => Thenable) { + // Initialize the active operation to an empty promise if necessary + let activeOperation = this._activeOperation || Promise.resolve(null); + + // Chain the operation to perform to the end of the existing promise + activeOperation = activeOperation.then(op); + + // Add a catch at the end to make sure we can continue after any errors + activeOperation = activeOperation.then(null, (err) => { + // TODO: Log the error + }); + + // Point the current active operation to this one + this._activeOperation = activeOperation; + return >this._activeOperation; + } + + private addToAccountList(accounts: data.Account[], accountToAdd: data.Account): AccountListOperationResult { + // Check if the entry already exists + let match = accounts.findIndex(account => AccountStore.findAccountByKey(account.key, accountToAdd.key)); + if (match >= 0) { + // Account already exists, we won't do anything + return { + accountAdded: false, + accountModified: false, + accountRemoved: false, + changedAccount: null, + updatedAccounts: accounts + }; + } + + // Add the account to the store + accounts.push(accountToAdd); + return { + accountAdded: true, + accountModified: false, + accountRemoved: false, + changedAccount: accountToAdd, + updatedAccounts: accounts + }; + } + + private removeFromAccountList(accounts: data.Account[], accountToRemove: data.AccountKey): AccountListOperationResult { + // Check if the entry exists + let match = accounts.findIndex(account => AccountStore.findAccountByKey(account.key, accountToRemove)); + if (match >= 0) { + // Account exists, remove it from the account list + accounts.splice(match, 1); + } + + return { + accountAdded: false, + accountModified: false, + accountRemoved: match >= 0, + changedAccount: null, + updatedAccounts: accounts + }; + } + + private updateAccountList(accounts: data.Account[], accountToUpdate: data.AccountKey, updateOperation: (account: data.Account) => void): AccountListOperationResult { + // Check if the entry exists + let match = accounts.findIndex(account => AccountStore.findAccountByKey(account.key, accountToUpdate)); + if (match < 0) { + // Account doesn't exist, we won't do anything + return { + accountAdded: false, + accountModified: false, + accountRemoved: false, + changedAccount: null, + updatedAccounts: accounts + }; + } + + + // Account exists, apply the update operation to it + updateOperation(accounts[match]); + return { + accountAdded: false, + accountModified: true, + accountRemoved: false, + changedAccount: accounts[match], + updatedAccounts: accounts + }; + } + + // MEMENTO IO METHODS ////////////////////////////////////////////////// + private readFromMemento(): Thenable { + // Initialize the account list if it isn't already + let accounts = this._memento[AccountStore.MEMENTO_KEY]; + if (!accounts) { + accounts = []; + } + + // Make a deep copy of the account list to ensure that the memento list isn't obliterated + accounts = JSON.parse(JSON.stringify(accounts)); + + return Promise.resolve(accounts); + } + + private writeToMemento(accounts: data.Account[]): Thenable { + // Store a shallow copy of the account list to disconnect the memento list from the active list + this._memento[AccountStore.MEMENTO_KEY] = JSON.parse(JSON.stringify(accounts)); + return Promise.resolve(); + } +} + +interface AccountListOperationResult extends AccountAdditionResult { + accountRemoved: boolean; + updatedAccounts: data.Account[]; +} diff --git a/src/sql/services/accountManagement/eventTypes.ts b/src/sql/services/accountManagement/eventTypes.ts new file mode 100644 index 0000000000..300d985cdf --- /dev/null +++ b/src/sql/services/accountManagement/eventTypes.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; + +/** + * Result from calling add/update on the account store + */ +export interface AccountAdditionResult { + /** + * Whether or not an account was added in the add/update process + */ + accountAdded: boolean; + + /** + * Whether or not an account was updated in the add/update process + */ + accountModified: boolean; + + /** + * The account that was added/updated (with any updates applied) + */ + changedAccount: data.Account; +} + +/** + * Parameters that go along with an account provider being added + */ +export interface AccountProviderAddedEventParams { + /** + * The provider that was registered + */ + addedProvider: data.AccountProviderMetadata; + + /** + * The accounts that were rehydrated with the provider + */ + initialAccounts: data.Account[]; +} + +/** + * Parameters that go along when a provider's account list changes + */ +export interface UpdateAccountListEventParams { + /** + * ID of the provider who's account list changes + */ + providerId: string; + + /** + * Updated list of accounts, sorted appropriately + */ + accountList: data.Account[]; +} diff --git a/src/sql/services/accountManagement/interfaces.ts b/src/sql/services/accountManagement/interfaces.ts new file mode 100644 index 0000000000..0fa17072cc --- /dev/null +++ b/src/sql/services/accountManagement/interfaces.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import Event from 'vs/base/common/event'; +import { AccountAdditionResult, AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const SERVICE_ID = 'accountManagementService'; + +export const IAccountManagementService = createDecorator(SERVICE_ID); + +export interface IAccountManagementService { + _serviceBrand: any; + + // ACCOUNT MANAGEMENT METHODS ////////////////////////////////////////// + addAccount(providerId: string): Thenable; + getAccountProviderMetadata(): Thenable; + getAccountsForProvider(providerId: string): Thenable; + getSecurityToken(account: data.Account): Thenable<{}>; + removeAccount(accountKey: data.AccountKey): Thenable; + + // UI METHODS ////////////////////////////////////////////////////////// + openAccountListDialog(): Thenable; + performOAuthAuthorization(url: string, silent: boolean): Thenable; + + // SERVICE MANAGEMENT METHODS ///////////////////////////////////////// + registerProvider(providerMetadata: data.AccountProviderMetadata, provider: data.AccountProvider): void; + shutdown(): void; + unregisterProvider(providerMetadata: data.AccountProviderMetadata): void; + + // EVENTING //////////////////////////////////////////////////////////// + readonly addAccountProviderEvent: Event; + readonly removeAccountProviderEvent: Event; + readonly updateAccountListEvent: Event; +} + +export interface IAccountStore { + /** + * Adds the provided account if the account doesn't exist. Updates the account if it already exists + * @param {Account} account Account to add/update + * @return {Thenable} Results of the add/update operation + */ + addOrUpdate(account: data.Account): Thenable; + + /** + * Retrieves all accounts, filtered by provider ID + * @param {string} providerId ID of the provider to filter by + * @return {Thenable} Promise to return all accounts that belong to the provided provider + */ + getAccountsByProvider(providerId: string): Thenable; + + /** + * Retrieves all accounts in the store. Returns empty array if store is not initialized + * @return {Thenable} Promise to return all accounts + */ + getAllAccounts(): Thenable; + + /** + * Removes an account. + * Returns false if the account was not found. + * Otherwise, returns true. + * @param key - The key of an account. + * @returns True if the account was removed, false if the account doesn't exist + */ + remove(key: data.AccountKey): Thenable; + + /** + * Updates the custom properties stored with an account. + * Returns null if no account was found to update. + * Otherwise, returns a new updated account instance. + * @param key - The key of an account. + * @param updateOperation - Operation to perform on the matching account + * @returns True if the account was modified, false if the account doesn't exist + */ + update(key: data.AccountKey, updateOperation: (account: data.Account) => void): Thenable; +} diff --git a/src/sql/services/angularEventing/angularEventingService.ts b/src/sql/services/angularEventing/angularEventingService.ts new file mode 100644 index 0000000000..384b93851d --- /dev/null +++ b/src/sql/services/angularEventing/angularEventingService.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Subscription } from 'rxjs/Subscription'; +import { Subject } from 'rxjs/Subject'; +import { warn } from 'sql/base/common/log'; + +const ANGULAREVENTING_SERVICE_ID = 'angularEventingService'; +export const IAngularEventingService = createDecorator(ANGULAREVENTING_SERVICE_ID); + +export enum AngularEventType { + NAV_DATABASE, + NAV_SERVER +} + +export interface IAngularEventingService { + _serviceBrand: any; + /** + * Adds a listener for the dashboard to send events, should only be called once for each dashboard by the dashboard itself + * @param uri Uri of the dashboard + * @param cb Listening function + * @returns + */ + onAngularEvent(uri: string, cb: (event: AngularEventType) => void): Subscription; + + /** + * Send an event to the dashboard; no op if the dashboard has not started listening yet + * @param uri Uri of the dashboard to send the event to + * @param event event to send + */ + sendAngularEvent(uri: string, event: AngularEventType): void; +} + +export class AngularEventingService implements IAngularEventingService { + public _serviceBrand: any; + private _angularMap = new Map>(); + + public onAngularEvent(uri: string, cb: (event: AngularEventType) => void): Subscription { + let subject: Subject; + if (!this._angularMap.has(uri)) { + subject = new Subject(); + this._angularMap.set(uri, subject); + } else { + subject = this._angularMap.get(uri); + } + let sub = subject.subscribe(cb); + return sub; + } + + public sendAngularEvent(uri: string, event: AngularEventType): void { + if (!this._angularMap.has(uri)) { + warn('Got request to send an event to a dashboard that has not started listening'); + } else { + this._angularMap.get(uri).next(event); + } + } +} diff --git a/src/sql/services/bootstrap/bootstrapParams.ts b/src/sql/services/bootstrap/bootstrapParams.ts new file mode 100644 index 0000000000..9a69b43dd0 --- /dev/null +++ b/src/sql/services/bootstrap/bootstrapParams.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DataService } from 'sql/parts/grid/services/dataService'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; + +export interface BootstrapParams { +} + +export interface QueryComponentParams extends BootstrapParams { + dataService: DataService; +} + +export interface EditDataComponentParams extends BootstrapParams { + dataService: DataService; +} + +export interface DashboardComponentParams extends BootstrapParams { + connection: IConnectionProfile; + ownerUri: string; +} + +export interface TaskDialogComponentParams extends BootstrapParams { + ownerUri: string; +} + +export interface QueryPlanParams extends BootstrapParams { + planXml: string; +} diff --git a/src/sql/services/bootstrap/bootstrapService.ts b/src/sql/services/bootstrap/bootstrapService.ts new file mode 100644 index 0000000000..d2bfa586a1 --- /dev/null +++ b/src/sql/services/bootstrap/bootstrapService.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { NgModuleRef } from '@angular/core'; +import { BootstrapParams } from 'sql/services/bootstrap/bootstrapParams'; +import { IConnectionManagementService, IConnectionDialogService, IErrorMessageService } + from 'sql/parts/connection/common/connectionManagement'; +import { IMetadataService } from 'sql/services/metadata/metadataService'; +import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { IAngularEventingService } from 'sql/services/angularEventing/angularEventingService'; +import { IScriptingService } from 'sql/services/scripting/scriptingService'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { IQueryManagementService } from 'sql/parts/query/common/queryManagement'; +import { IAdminService } from 'sql/parts/admin/common/adminService'; +import { IDisasterRecoveryService, IDisasterRecoveryUiService, IRestoreDialogController } from 'sql/parts/disasterRecovery/common/interfaces'; +import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces'; +import { ISqlOAuthService } from 'sql/common/sqlOAuthService'; +import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces'; +import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; + +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IEditorInput } from 'vs/platform/editor/common/editor'; +import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; +import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService } from 'vs/platform/storage/common/storage'; + +export const BOOTSTRAP_SERVICE_ID = 'bootstrapService'; +export const IBootstrapService = createDecorator(BOOTSTRAP_SERVICE_ID); + +/* + * Handles logic for bootstrapping and passing singleton services to Angular components. + */ +export interface IBootstrapService { + + _serviceBrand: any; + + connectionManagementService: IConnectionManagementService; + metadataService: IMetadataService; + objectExplorerService: IObjectExplorerService; + scriptingService: IScriptingService; + queryEditorService: IQueryEditorService; + connectionDialogService: IConnectionDialogService; + queryModelService: IQueryModelService; + adminService: IAdminService; + disasterRecoveryService: IDisasterRecoveryService; + disasterRecoveryUiService: IDisasterRecoveryUiService; + keybindingService: IKeybindingService; + contextKeyService: IContextKeyService; + contextMenuService: IContextMenuService; + themeService: IWorkbenchThemeService; + editorService: IWorkbenchEditorService; + errorMessageService: IErrorMessageService; + partService: IPartService; + queryManagementService: IQueryManagementService; + instantiationService: IInstantiationService; + angularEventingService: IAngularEventingService; + configurationService: IConfigurationService; + insightsDialogService: IInsightsDialogService; + contextViewService: IContextViewService; + restoreDialogService: IRestoreDialogController; + messageService: IMessageService; + workspaceContextService: IWorkspaceContextService; + accountManagementService: IAccountManagementService; + windowsService: IWindowsService; + sqlOAuthService: ISqlOAuthService; + windowService: IWindowService; + fileBrowserService: IFileBrowserService; + fileBrowserDialogService: IFileBrowserDialogController; + telemetryService: ITelemetryService; + storageService: IStorageService; + clipboardService: IClipboardService; + capabilitiesService: ICapabilitiesService; + + /* + * Bootstraps the Angular module described. Components that need singleton services should inject the + * 'BootstrapService' dependency to obtain a reference to this class. Components that need dynamic parameters + * should wrap them in an object and pass them in through the "params" parameter. + * + * moduleType: The TypeScript type of the module to bootstrap + * container: The HTML container to append the selector HTMLElement + * selectorString: The tag name and class used to create the element, e.g. 'tagName.cssClassName' + * params: The parameters to be associated with the given id + * input: Optional editor input. If specified, will listen to its onDispose event and destroy the module when this happens + * callbackSetModule:Optional. If specified, will be used to set the moduleRef + * Returns the unique selector string that this module will bootstrap with. + */ + bootstrap(moduleType: any, container: HTMLElement, selectorString: string, params: BootstrapParams, input?: IEditorInput, callbackSetModule?: (value: NgModuleRef<{}>) => void): string; + + /* + * Gets the "params" entry associated with the given id and unassociates the id/entry pair. + * Returns undefined if no entry is found. + */ + getBootstrapParams(id: string): any; + + /* + * Gets the next unique selector given the baseSelectorString. A unique selector is the baseSelectorString with a + * number appended. E.g. if baseSelectorString='query', valid unique selectors could be query0, query1, query2, etc. + */ + getUniqueSelector(baseSelectorString: string): string; +} diff --git a/src/sql/services/bootstrap/bootstrapServiceImpl.ts b/src/sql/services/bootstrap/bootstrapServiceImpl.ts new file mode 100644 index 0000000000..617a802f55 --- /dev/null +++ b/src/sql/services/bootstrap/bootstrapServiceImpl.ts @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { NgModuleRef } from '@angular/core'; +import { platformBrowserDynamic, } from '@angular/platform-browser-dynamic'; + +import { BootstrapParams } from 'sql/services/bootstrap/bootstrapParams'; +import { IConnectionManagementService, IConnectionDialogService, IErrorMessageService } + from 'sql/parts/connection/common/connectionManagement'; +import { IMetadataService } from 'sql/services/metadata/metadataService'; +import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { IScriptingService } from 'sql/services/scripting/scriptingService'; +import { IQueryManagementService } from 'sql/parts/query/common/queryManagement'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { IAdminService } from 'sql/parts/admin/common/adminService'; +import { IDisasterRecoveryService, IDisasterRecoveryUiService, IRestoreDialogController } from 'sql/parts/disasterRecovery/common/interfaces'; +import { IAngularEventingService } from 'sql/services/angularEventing/angularEventingService'; +import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces'; +import { ISqlOAuthService } from 'sql/common/sqlOAuthService'; +import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces'; +import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; + +import { $ } from 'vs/base/browser/dom'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IEditorInput } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from './bootstrapService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; +import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService } from 'vs/platform/storage/common/storage'; + +export class BootstrapService implements IBootstrapService { + + public _serviceBrand: any; + + // Maps uniqueSelectors (as opposed to selectors) to BootstrapParams + private _bootstrapParameterMap: Map; + + // Maps selectors (as opposed to uniqueSelectors) to a queue of uniqueSelectors + private _selectorQueueMap: Map; + + // Maps selectors (as opposed to uniqueSelectors) to the next available uniqueSelector ID number + private _selectorCountMap: Map; + + constructor( + @IConnectionManagementService public connectionManagementService: IConnectionManagementService, + @IMetadataService public metadataService: IMetadataService, + @IObjectExplorerService public objectExplorerService: IObjectExplorerService, + @IScriptingService public scriptingService: IScriptingService, + @IQueryEditorService public queryEditorService: IQueryEditorService, + @IAdminService public adminService: IAdminService, + @IWorkbenchThemeService public themeService: IWorkbenchThemeService, + @IWorkbenchEditorService public editorService: IWorkbenchEditorService, + @IDisasterRecoveryService public disasterRecoveryService: IDisasterRecoveryService, + @IDisasterRecoveryUiService public disasterRecoveryUiService: IDisasterRecoveryUiService, + @IRestoreDialogController public restoreDialogService: IRestoreDialogController, + @IConnectionDialogService public connectionDialogService: IConnectionDialogService, + @IQueryModelService public queryModelService: IQueryModelService, + @IKeybindingService public keybindingService: IKeybindingService, + @IContextKeyService public contextKeyService: IContextKeyService, + @IContextMenuService public contextMenuService: IContextMenuService, + @IErrorMessageService public errorMessageService: IErrorMessageService, + @IPartService public partService: IPartService, + @IQueryManagementService public queryManagementService: IQueryManagementService, + @IInstantiationService public instantiationService: IInstantiationService, + @IAngularEventingService public angularEventingService: IAngularEventingService, + @IConfigurationService public configurationService: IConfigurationService, + @IInsightsDialogService public insightsDialogService: IInsightsDialogService, + @IContextViewService public contextViewService: IContextViewService, + @IMessageService public messageService: IMessageService, + @IWorkspaceContextService public workspaceContextService: IWorkspaceContextService, + @IAccountManagementService public accountManagementService: IAccountManagementService, + @IWindowsService public windowsService: IWindowsService, + @ISqlOAuthService public sqlOAuthService: ISqlOAuthService, + @IFileBrowserService public fileBrowserService: IFileBrowserService, + @IFileBrowserDialogController public fileBrowserDialogService: IFileBrowserDialogController, + @IWindowService public windowService: IWindowService, + @ITelemetryService public telemetryService: ITelemetryService, + @IStorageService public storageService: IStorageService, + @IClipboardService public clipboardService: IClipboardService, + @ICapabilitiesService public capabilitiesService: ICapabilitiesService + ) { + this._bootstrapParameterMap = new Map(); + this._selectorQueueMap = new Map(); + this._selectorCountMap = new Map(); + } + + public bootstrap(moduleType: any, container: HTMLElement, selectorString: string, params: BootstrapParams, input?: IEditorInput, callbackSetModule?: (value: NgModuleRef<{}>) => void): string { + // Create the uniqueSelectorString + let uniqueSelectorString: string = this._getUniqueSelectorString(selectorString); + let selector: HTMLElement = $(uniqueSelectorString); + container.appendChild(selector); + + // Associate the elementId + this._setUniqueSelector(selectorString, uniqueSelectorString); + + // Associate the params + this._bootstrapParameterMap.set(uniqueSelectorString, params); + + // Perform the bootsrap + let providers = [{ provide: BOOTSTRAP_SERVICE_ID, useValue: this }]; + + platformBrowserDynamic(providers).bootstrapModule(moduleType).then(moduleRef => { + if (input) { + input.onDispose(() => { + moduleRef.destroy(); + }); + } + if (callbackSetModule) { + callbackSetModule(moduleRef); + } + }); + + return uniqueSelectorString; + } + + public getBootstrapParams(id: string): any { + let idLowercase = id.toLowerCase(); + let params: BootstrapParams = this._bootstrapParameterMap.get(idLowercase); + this._bootstrapParameterMap.delete(idLowercase); + return params; + } + + public getUniqueSelector(selectorString: string): string { + let idArray = this._selectorQueueMap.get(selectorString); + if (!idArray) { + return undefined; + } + + let id: string = idArray.shift(); + + if (idArray.length === 0) { + this._selectorQueueMap.delete(selectorString); + } else { + this._selectorQueueMap.set(selectorString, idArray); + } + + return id; + } + + private _getUniqueSelectorString(selectorString: string): string { + let count: number = this._selectorCountMap.get(selectorString); + if (!count) { + this._selectorCountMap.set(selectorString, 1); + count = 0; + } else { + this._selectorCountMap.set(selectorString, count + 1); + } + let casedString = selectorString + count.toString(); + return casedString.toLowerCase(); + } + + private _setUniqueSelector(selectorString: string, elementId: string) { + let idArray = this._selectorQueueMap.get(selectorString); + if (!idArray) { + idArray = []; + } + idArray.push(elementId); + this._selectorQueueMap.set(selectorString, idArray); + } +} diff --git a/src/sql/services/capabilities/capabilitiesService.ts b/src/sql/services/capabilities/capabilitiesService.ts new file mode 100644 index 0000000000..1ccf7dc464 --- /dev/null +++ b/src/sql/services/capabilities/capabilitiesService.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import * as Constants from 'sql/common/constants'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import data = require('data'); +import Event, { Emitter } from 'vs/base/common/event'; +import { Action } from 'vs/base/common/actions'; +import { Deferred } from 'sql/base/common/promise'; + +export const SERVICE_ID = 'capabilitiesService'; +export const HOST_NAME = 'sqlops'; +export const HOST_VERSION = '1.0'; + +export const ICapabilitiesService = createDecorator(SERVICE_ID); + +/** + * Interface for managing provider capabilities + */ +export interface ICapabilitiesService { + _serviceBrand: any; + + /** + * Retrieve a list of registered capabilities providers + */ + getCapabilities(): data.DataProtocolServerCapabilities[]; + + /** + * Register a capabilities provider + */ + registerProvider(provider: data.CapabilitiesProvider): void; + + /** + * Returns true if the feature is available for given connection + */ + isFeatureAvailable(action: Action, connectionManagementInfo: ConnectionManagementInfo): boolean; + + /** + * Event raised when a provider is registered + */ + onProviderRegisteredEvent: Event; + + /** + * Promise fulfilled when Capabilities are ready + */ + onCapabilitiesReady(): Promise; + +} + +/** + * Capabilities service implementation class. This class provides the ability + * to discover the DMP capabilties that a DMP provider offers. + */ +export class CapabilitiesService implements ICapabilitiesService { + + public _serviceBrand: any; + + private _providers: data.CapabilitiesProvider[] = []; + + private _capabilities: data.DataProtocolServerCapabilities[] = []; + + private _onProviderRegistered: Emitter; + + private _clientCapabilties: data.DataProtocolClientCapabilities = { + + hostName: HOST_NAME, + hostVersion: HOST_VERSION + }; + + private disposables: IDisposable[] = []; + + private _onCapabilitiesReady: Deferred; + + // Due to absence of a way to infer the number of expected data tools extensions from the package.json, this is being hard-coded + // TODO: a better mechanism to populate the expected number of capabilities + private _expectedCapabilitiesCount: number = 1; + + private _registeredCapabilities: number = 0; + + constructor() { + this._onProviderRegistered = new Emitter(); + this.disposables.push(this._onProviderRegistered); + this._onCapabilitiesReady = new Deferred(); + } + + public onCapabilitiesReady(): Promise { + return this._onCapabilitiesReady.promise; + } + + /** + * Retrieve a list of registered server capabilities + */ + public getCapabilities(): data.DataProtocolServerCapabilities[] { + return this._capabilities; + } + + /** + * Register the capabilities provider and query the provider for its capabilities + * @param provider + */ + public registerProvider(provider: data.CapabilitiesProvider): void { + this._providers.push(provider); + + // request the capabilities from server + provider.getServerCapabilities(this._clientCapabilties).then(serverCapabilities => { + this._capabilities.push(serverCapabilities); + this._onProviderRegistered.fire(serverCapabilities); + this._registeredCapabilities++; + this.resolveCapabilitiesIfReady(); + }); + } + + private resolveCapabilitiesIfReady(): void { + if (this._registeredCapabilities === this._expectedCapabilitiesCount) { + this._onCapabilitiesReady.resolve(); + } + } + + /** + * Returns true if the feature is available for given connection + * @param featureComponent a component which should have the feature name + * @param connectionManagementInfo connectionManagementInfo + */ + public isFeatureAvailable(action: Action, connectionManagementInfo: ConnectionManagementInfo): boolean { + let isCloud = connectionManagementInfo && connectionManagementInfo.serverInfo && connectionManagementInfo.serverInfo.isCloud; + let isMssql = connectionManagementInfo.connectionProfile.providerName === 'MSSQL'; + // TODO: The logic should from capabilities service. + if (action) { + let featureName: string = action.id; + switch (featureName) { + case Constants.BackupFeatureName: + if (isMssql) { + return connectionManagementInfo.connectionProfile.databaseName && !isCloud; + } else { + return !!connectionManagementInfo.connectionProfile.databaseName; + } + case Constants.RestoreFeatureName: + if (isMssql) { + return !isCloud; + } else { + return !!connectionManagementInfo.connectionProfile.databaseName; + } + default: + return true; + } + } else { + return true; + } + + } + + // Event Emitters + public get onProviderRegisteredEvent(): Event { + return this._onProviderRegistered.event; + } + + public dispose(): void { + this.disposables = dispose(this.disposables); + } +} diff --git a/src/sql/services/credentials/credentialsService.ts b/src/sql/services/credentials/credentialsService.ts new file mode 100644 index 0000000000..82e8f768ac --- /dev/null +++ b/src/sql/services/credentials/credentialsService.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import data = require('data'); +import { Deferred } from 'sql/base/common/promise'; + +export const SERVICE_ID = 'credentialsService'; + +export interface CredentialManagementEvents { + onSaveCredential(credentialId: string, password: string): Thenable; + + onReadCredential(credentialId: string): Thenable; + + onDeleteCredential(credentialId: string): Thenable; +} + +export const ICredentialsService = createDecorator(SERVICE_ID); + +export interface ICredentialsService { + _serviceBrand: any; + + saveCredential(credentialId: string, password: string): Thenable; + + readCredential(credentialId: string): Thenable; + + deleteCredential(credentialId: string): Thenable; + + addEventListener(handle: number, events: CredentialManagementEvents): IDisposable; +} + +export class CredentialsService implements ICredentialsService { + + _serviceBrand: any; + + private disposables: IDisposable[] = []; + + private _serverEvents: { [handle: number]: CredentialManagementEvents; } = Object.create(null); + + private _lastHandle: number; + + private _onServerEventsReady: Deferred = new Deferred(); + + constructor() { + } + + public addEventListener(handle: number, events: CredentialManagementEvents): IDisposable { + this._lastHandle = handle; + + this._serverEvents[handle] = events; + + this._onServerEventsReady.resolve(); + + return { + dispose: () => { + } + }; + } + + public saveCredential(credentialId: string, password: string): Thenable { + return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onSaveCredential(credentialId, password)); + } + + public readCredential(credentialId: string): Thenable { + return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onReadCredential(credentialId)); + } + + public deleteCredential(credentialId: string): Thenable { + return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onDeleteCredential(credentialId)); + } + + public dispose(): void { + this.disposables = dispose(this.disposables); + } +} diff --git a/src/sql/services/metadata/metadataService.ts b/src/sql/services/metadata/metadataService.ts new file mode 100644 index 0000000000..2ad43f41be --- /dev/null +++ b/src/sql/services/metadata/metadataService.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import data = require('data'); + +export const SERVICE_ID = 'metadataService'; + +export const IMetadataService = createDecorator(SERVICE_ID); + +export interface IMetadataService { + _serviceBrand: any; + + getMetadata(connectionUri: string): Thenable; + + getDatabaseNames(connectionUri: string): Thenable; + + getTableInfo(connectionUri: string, metadata: data.ObjectMetadata): Thenable; + + getViewInfo(connectionUri: string, metadata: data.ObjectMetadata): Thenable; + + /** + * Register a metadata provider + */ + registerProvider(providerId: string, provider: data.MetadataProvider): void; +} + +export class MetadataService implements IMetadataService { + + public _serviceBrand: any; + + private _disposables: IDisposable[] = []; + + private _providers: { [handle: string]: data.MetadataProvider; } = Object.create(null); + + constructor(@IConnectionManagementService private _connectionService: IConnectionManagementService) { + } + + public getMetadata(connectionUri: string): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + if (providerId) { + let provider = this._providers[providerId]; + if (provider) { + return provider.getMetadata(connectionUri); + } + } + + return Promise.resolve(undefined); + } + + public getDatabaseNames(connectionUri: string): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + if (providerId) { + let provider = this._providers[providerId]; + if (provider) { + return provider.getDatabases(connectionUri); + } + } + + return Promise.resolve(undefined); + } + + public getTableInfo(connectionUri: string, metadata: data.ObjectMetadata): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + if (providerId) { + let provider = this._providers[providerId]; + if (provider) { + return provider.getTableInfo(connectionUri, metadata); + } + } + + return Promise.resolve(undefined); + } + + public getViewInfo(connectionUri: string, metadata: data.ObjectMetadata): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + if (providerId) { + let provider = this._providers[providerId]; + if (provider) { + return provider.getViewInfo(connectionUri, metadata); + } + } + + return Promise.resolve(undefined); + } + + /** + * Register a metadata provider + */ + public registerProvider(providerId: string, provider: data.MetadataProvider): void { + this._providers[providerId] = provider; + } + + public dispose(): void { + this._disposables = dispose(this._disposables); + } +} diff --git a/src/sql/services/scripting/scriptingService.ts b/src/sql/services/scripting/scriptingService.ts new file mode 100644 index 0000000000..eb06c21bb3 --- /dev/null +++ b/src/sql/services/scripting/scriptingService.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { ScriptOperation } from 'sql/workbench/common/taskUtilities'; +import data = require('data'); +import { warn, error } from 'sql/base/common/log'; +export const SERVICE_ID = 'scriptingService'; + +export const IScriptingService = createDecorator(SERVICE_ID); + +export interface IScriptingService { + _serviceBrand: any; + + script(connectionUri: string, metadata: data.ObjectMetadata, operation: ScriptOperation, paramDetails: data.ScriptingParamDetails): Thenable; + + /** + * Register a scripting provider + */ + registerProvider(providerId: string, provider: data.ScriptingProvider): void; + + /** + * Callback method for when scripting is complete + */ + onScriptingComplete(handle: number, scriptingCompleteResult: data.ScriptingCompleteResult): void; + + /** + * Returns the result for an operation if the operation failed + */ + getOperationFailedResult(operationId: string): data.ScriptingCompleteResult; +} + +export class ScriptingService implements IScriptingService { + + public _serviceBrand: any; + + private disposables: IDisposable[] = []; + + private _providers: { [handle: string]: data.ScriptingProvider; } = Object.create(null); + + private failedScriptingOperations: { [operationId: string]: data.ScriptingCompleteResult } = {}; + constructor( @IConnectionManagementService private _connectionService: IConnectionManagementService) { } + + /** + * Call the service for scripting based on provider and scripting operation + * @param connectionUri + * @param metadata + * @param operation + * @param paramDetails + */ + public script(connectionUri: string, metadata: data.ObjectMetadata, operation: ScriptOperation, paramDetails: data.ScriptingParamDetails): Thenable { + let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri); + + if (providerId) { + let provider = this._providers[providerId]; + if (provider) { + switch (operation) { + case (ScriptOperation.Select): + return provider.scriptAsSelect(connectionUri, metadata, paramDetails); + case (ScriptOperation.Create): + return provider.scriptAsCreate(connectionUri, metadata, paramDetails); + case (ScriptOperation.Delete): + return provider.scriptAsDelete(connectionUri, metadata, paramDetails); + default: + return Promise.resolve(undefined); + } + } + } + return Promise.resolve(undefined); + } + + /** + * Callback method for when scripting is complete + * @param handle + * @param scriptingCompleteResult + */ + public onScriptingComplete(handle: number, scriptingCompleteResult: data.ScriptingCompleteResult): void { + if (scriptingCompleteResult && scriptingCompleteResult.hasError && scriptingCompleteResult.errorMessage) { + error(`Scripting failed. error: ${scriptingCompleteResult.errorMessage}`); + if (scriptingCompleteResult.operationId) { + this.failedScriptingOperations[scriptingCompleteResult.operationId] = scriptingCompleteResult; + } + } + } + + /** + * Returns the result for an operation if the operation failed + * @param operationId Operation Id + */ + public getOperationFailedResult(operationId: string): data.ScriptingCompleteResult { + if (operationId && operationId in this.failedScriptingOperations) { + return this.failedScriptingOperations[operationId]; + } else { + return undefined; + } + } + + /** + * Register a scripting provider + */ + public registerProvider(providerId: string, provider: data.ScriptingProvider): void { + this._providers[providerId] = provider; + } + + public dispose(): void { + this.disposables = dispose(this.disposables); + } +} diff --git a/src/sql/services/serialization/serializationService.ts b/src/sql/services/serialization/serializationService.ts new file mode 100644 index 0000000000..e1d1e0417d --- /dev/null +++ b/src/sql/services/serialization/serializationService.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import * as Constants from 'sql/common/constants'; +import * as data from 'data'; + +export const SERVICE_ID = 'serializationService'; + +export interface SerializationProviderEvents { + onSaveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable; +} + +export const ISerializationService = createDecorator(SERVICE_ID); + +export interface ISerializationService { + _serviceBrand: any; + + saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable; + + disabledSaveAs(): Thenable; + + addEventListener(handle: number, events: SerializationProviderEvents): IDisposable; + + getSerializationFeatureMetadataProvider(ownerUri: string): data.FeatureMetadataProvider; +} + +export class SerializationService implements ISerializationService { + + _serviceBrand: any; + + private disposables: IDisposable[] = []; + + private _serverEvents: { [handle: number]: SerializationProviderEvents; } = Object.create(null); + + private _lastHandle: number; + + constructor( + @IConnectionManagementService private _connectionService: IConnectionManagementService, + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService + ) { + } + + public addEventListener(handle: number, events: SerializationProviderEvents): IDisposable { + this._lastHandle = handle; + + this._serverEvents[handle] = events; + + return { + dispose: () => { + } + }; + } + + public saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable { + if (this._serverEvents === undefined || this._serverEvents[this._lastHandle] === undefined) { + return this.disabledSaveAs(); + } + + return this._serverEvents[this._lastHandle].onSaveAs(saveFormat, savePath, results, appendToFile); + } + + public disabledSaveAs(): Thenable { + return Promise.resolve({messages: Constants.SerializationDisabled}); + + } + + public getSerializationFeatureMetadataProvider(ownerUri: string) : data.FeatureMetadataProvider { + let providerId: string = this._connectionService.getProviderIdFromUri(ownerUri); + let providerCapabilities = this._capabilitiesService.getCapabilities().find(c => c.providerName === providerId); + + if (providerCapabilities) { + return providerCapabilities.features.find(f => f.featureName === SERVICE_ID); + } + + return undefined; + } + + public dispose(): void { + this.disposables = dispose(this.disposables); + } +} diff --git a/src/sql/workbench/api/node/extHostAccountManagement.ts b/src/sql/workbench/api/node/extHostAccountManagement.ts new file mode 100644 index 0000000000..f8c5a5c811 --- /dev/null +++ b/src/sql/workbench/api/node/extHostAccountManagement.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import {TPromise} from 'vs/base/common/winjs.base'; +import {IThreadService} from 'vs/workbench/services/thread/common/threadService'; +import {Disposable} from 'vs/workbench/api/node/extHostTypes'; +import { + ExtHostAccountManagementShape, + MainThreadAccountManagementShape, + SqlMainContext, +} from 'sql/workbench/api/node/sqlExtHost.protocol'; + +export class ExtHostAccountManagement extends ExtHostAccountManagementShape { + private _handlePool: number = 0; + private _proxy: MainThreadAccountManagementShape; + private _providers: {[handle: number]: AccountProviderWithMetadata} = {}; + + constructor(threadService: IThreadService) { + super(); + this._proxy = threadService.get(SqlMainContext.MainThreadAccountManagement); + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + // - MAIN THREAD AVAILABLE METHODS ///////////////////////////////////// + public $clear(handle: number, accountKey: data.AccountKey): Thenable { + return this._withProvider(handle, (provider: data.AccountProvider) => provider.clear(accountKey)); + } + + public $getSecurityToken(handle: number, account: data.Account): Thenable<{}> { + return this._withProvider(handle, (provider: data.AccountProvider) => provider.getSecurityToken(account)); + } + + public $initialize(handle: number, restoredAccounts: data.Account[]): Thenable { + return this._withProvider(handle, (provider: data.AccountProvider) => provider.initialize(restoredAccounts)); + } + + public $prompt(handle: number): Thenable { + return this._withProvider(handle, (provider: data.AccountProvider) => provider.prompt()); + } + + public $refresh(handle: number, account: data.Account): Thenable { + return this._withProvider(handle, (provider: data.AccountProvider) => provider.refresh(account)); + } + + // - EXTENSION HOST AVAILABLE METHODS ////////////////////////////////// + public $performOAuthAuthorization(url: string, silent: boolean): Thenable { + return this._proxy.$performOAuthAuthorization(url, silent); + } + + public $registerAccountProvider(providerMetadata: data.AccountProviderMetadata, provider: data.AccountProvider): Disposable { + let self = this; + + // Look for any account providers that have the same provider ID + let matchingProviderIndex = Object.values(this._providers).findIndex((provider: AccountProviderWithMetadata) => { + return provider.metadata.id === providerMetadata.id; + }); + if (matchingProviderIndex >= 0) { + throw new Error(`Account Provider with ID '${providerMetadata.id}' has already been registered`); + } + + // Create the handle for the provider + let handle: number = this._nextHandle(); + this._providers[handle] = { + metadata: providerMetadata, + provider: provider + }; + + // Register the provider in the main thread via the proxy + this._proxy.$registerAccountProvider(providerMetadata, handle); + + // Return a disposable to cleanup the provider + return new Disposable(() => { + delete self._providers[handle]; + self._proxy.$unregisterAccountProvider(handle); + }); + } + + /** + * This method is for testing only, it is not exposed via the shape. + * @return {number} Number of providers that are currently registered + */ + public getProviderCount(): number { + return Object.keys(this._providers).length; + } + + // PRIVATE METHODS ///////////////////////////////////////////////////// + private _nextHandle(): number { + return this._handlePool++; + } + + private _withProvider(handle: number, callback: (provider: data.AccountProvider) => Thenable): Thenable { + let provider = this._providers[handle]; + if (provider === undefined) { + return TPromise.wrapError(new Error(`Provider ${handle} not found.`)); + } + return callback(provider.provider); + } +} + +interface AccountProviderWithMetadata { + metadata: data.AccountProviderMetadata; + provider: data.AccountProvider; +} + + diff --git a/src/sql/workbench/api/node/extHostCredentialManagement.ts b/src/sql/workbench/api/node/extHostCredentialManagement.ts new file mode 100644 index 0000000000..cff45a12f3 --- /dev/null +++ b/src/sql/workbench/api/node/extHostCredentialManagement.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; +import { SqlMainContext, MainThreadCredentialManagementShape, ExtHostCredentialManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; +import * as vscode from 'vscode'; +import * as data from 'data'; +import { Disposable } from 'vs/workbench/api/node/extHostTypes'; + +class CredentialAdapter { + public provider: data.CredentialProvider; + + constructor(provider: data.CredentialProvider) { + this.provider = provider; + } + + public saveCredential(credentialId: string, password: string): Thenable { + return this.provider.saveCredential(credentialId, password); + } + + public readCredential(credentialId: string): Thenable { + return this.provider.readCredential(credentialId); + } + + public deleteCredential(credentialId: string): Thenable { + return this.provider.deleteCredential(credentialId); + } +} + +type Adapter = CredentialAdapter; + +export class ExtHostCredentialManagement extends ExtHostCredentialManagementShape { + // MEMBER VARIABLES //////////////////////////////////////////////////// + private _adapter: { [handle: number]: Adapter } = Object.create(null); + private _handlePool: number = 0; + private _proxy: MainThreadCredentialManagementShape; + private _registrationPromise: Promise; + private _registrationPromiseResolve; + + constructor(threadService: IThreadService) { + super(); + + let self = this; + + this._proxy = threadService.get(SqlMainContext.MainThreadCredentialManagement); + + // Create a promise to resolve when a credential provider has been registered. + // HACK: this gives us a deferred promise + this._registrationPromise = new Promise((resolve) => { self._registrationPromiseResolve = resolve; }); + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + public $registerCredentialProvider(provider: data.CredentialProvider): vscode.Disposable { + // Store the credential provider + provider.handle = this._nextHandle(); + this._adapter[provider.handle] = new CredentialAdapter(provider); + + // Register the credential provider with the main thread + this._proxy.$registerCredentialProvider(provider.handle); + + // Resolve the credential provider registration promise + this._registrationPromiseResolve(); + return this._createDisposable(provider.handle); + } + + public $getCredentialProvider(namespaceId: string): Thenable { + let self = this; + + if (!namespaceId) { + return TPromise.wrapError(new Error('A namespace must be provided when retrieving a credential provider')); + } + + // When the registration promise has finished successfully, + return this._registrationPromise.then(() => + self._withAdapter(0, CredentialAdapter, adapter => self._createNamespacedCredentialProvider(namespaceId, adapter)) + ); + } + + public $saveCredential(credentialId: string, password: string): Thenable { + return this._withAdapter(0, CredentialAdapter, adapter => adapter.saveCredential(credentialId, password)); + } + + public $readCredential(credentialId: string): Thenable { + return this._withAdapter(0, CredentialAdapter, adapter => adapter.readCredential(credentialId)); + } + + public $deleteCredential(credentialId: string): Thenable { + return this._withAdapter(0, CredentialAdapter, adapter => adapter.deleteCredential(credentialId)); + } + + /** + * Helper method for tests. Not exposed via shape. + * @return {number} Number of providers registered + */ + public getProviderCount(): number { + return Object.keys(this._adapter).length; + } + + // PRIVATE HELPERS ///////////////////////////////////////////////////// + private static _getNamespacedCredentialId(namespaceId: string, credentialId: string) { + return `${namespaceId}|${credentialId}`; + } + + private _createNamespacedCredentialProvider(namespaceId: string, adapter: CredentialAdapter): Thenable { + // Create a provider that wraps the methods in a namespace + let provider: data.CredentialProvider = { + handle: adapter.provider.handle, + deleteCredential: (credentialId: string) => { + let namespacedId = ExtHostCredentialManagement._getNamespacedCredentialId(namespaceId, credentialId); + return adapter.provider.deleteCredential(namespacedId); + }, + readCredential: (credentialId: string) => { + let namespacedId = ExtHostCredentialManagement._getNamespacedCredentialId(namespaceId, credentialId); + return adapter.provider.readCredential(namespacedId); + }, + saveCredential: (credentialId: string, credential: string) => { + let namespacedId = ExtHostCredentialManagement._getNamespacedCredentialId(namespaceId, credentialId); + return adapter.provider.saveCredential(namespacedId, credential); + } + }; + return Promise.resolve(provider); + } + + private _createDisposable(handle: number): Disposable { + return new Disposable(() => { + delete this._adapter[handle]; + this._proxy.$unregisterCredentialProvider(handle); + }); + } + + private _nextHandle(): number { + return this._handlePool++; + } + + private _withAdapter(handle: number, ctor: { new (...args: any[]): A }, callback: (adapter: A) => Thenable): Thenable { + let adapter = this._adapter[handle]; + if (!(adapter instanceof ctor)) { + return TPromise.wrapError(new Error('no adapter found')); + } + return callback(adapter); + } +} diff --git a/src/sql/workbench/api/node/extHostDataProtocol.ts b/src/sql/workbench/api/node/extHostDataProtocol.ts new file mode 100644 index 0000000000..18a2986114 --- /dev/null +++ b/src/sql/workbench/api/node/extHostDataProtocol.ts @@ -0,0 +1,541 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event, { Emitter } from 'vs/base/common/event'; +import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; +import { SqlMainContext, MainThreadDataProtocolShape, ExtHostDataProtocolShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; +import * as vscode from 'vscode'; +import * as data from 'data'; +import { Disposable } from 'vs/workbench/api/node/extHostTypes'; + +export class ExtHostDataProtocol extends ExtHostDataProtocolShape { + + private readonly _onDidChangeLanguageFlavor = new Emitter(); + + readonly onDidChangeLanguageFlavor: Event = this._onDidChangeLanguageFlavor.event; + + private _proxy: MainThreadDataProtocolShape; + + private static _handlePool: number = 0; + private _adapter = new Map(); + + constructor( + threadService: IThreadService + ) { + super(); + this._proxy = threadService.get(SqlMainContext.MainThreadDataProtocol); + } + + private _createDisposable(handle: number): Disposable { + return new Disposable(() => { + this._adapter.delete(handle); + this._proxy.$unregisterProvider(handle); + }); + } + + private _nextHandle(): number { + return ExtHostDataProtocol._handlePool++; + } + + private _runWithProvider(handle: number, action: (p: data.DataProtocolProvider) => Thenable): Thenable { + let provider = this._adapter.get(handle); + return provider !== undefined + ? action(provider) + : undefined; + } + + + $registerProvider(provider: data.DataProtocolProvider): vscode.Disposable { + provider.handle = this._nextHandle(); + this._adapter.set(provider.handle, provider); + + this._proxy.$registerProvider(provider.providerId, provider.handle); + return this._createDisposable(provider.handle); + } + + // Capabilities Discovery handlers + $getServerCapabilities(handle: number, client: data.DataProtocolClientCapabilities): Thenable { + return this._runWithProvider(handle, provider => { + return provider.capabilitiesProvider ? provider.capabilitiesProvider.getServerCapabilities(client) + : undefined; + }); + } + + // Connection Management handlers + $connect(handle: number, connectionUri: string, connection: data.ConnectionInfo): Thenable { + return this._runWithProvider(handle, provider => { + return provider.connectionProvider ? provider.connectionProvider.connect(connectionUri, connection) + : undefined; + }); + } + + $disconnect(handle: number, connectionUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.connectionProvider ? provider.connectionProvider.disconnect(connectionUri) + : undefined; + }); + } + + $cancelConnect(handle: number, connectionUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.connectionProvider ? provider.connectionProvider.cancelConnect(connectionUri) + : undefined; + }); + } + + $changeDatabase(handle: number, connectionUri: string, newDatabase: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.connectionProvider ? provider.connectionProvider.changeDatabase(connectionUri, newDatabase) + : undefined; + }); + } + + $listDatabases(handle: number, connectionUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.connectionProvider ? provider.connectionProvider.listDatabases(connectionUri) + : undefined; + }); + } + + $rebuildIntelliSenseCache(handle: number, connectionUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.connectionProvider ? provider.connectionProvider.rebuildIntelliSenseCache(connectionUri) + : undefined; + }); + } + + $onConnectComplete(handle: number, connectionInfoSummary: data.ConnectionInfoSummary): void { + this._proxy.$onConnectionComplete(handle, connectionInfoSummary); + } + + public $onIntelliSenseCacheComplete(handle: number, connectionUri: string): void { + this._proxy.$onIntelliSenseCacheComplete(handle, connectionUri); + } + + public $onConnectionChanged(handle: number, changedConnInfo: data.ChangedConnectionInfo): void { + this._proxy.$onConnectionChangeNotification(handle, changedConnInfo); + } + + // Protocol-wide Event Handlers + public $languageFlavorChanged(params: data.DidChangeLanguageFlavorParams): void { + this._onDidChangeLanguageFlavor.fire(params); + } + + // Query Management handlers + + $cancelQuery(handle: number, ownerUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.cancelQuery(ownerUri); + }); + } + + $runQuery(handle: number, ownerUri: string, selection: data.ISelectionData, runOptions?: data.ExecutionPlanOptions): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.runQuery(ownerUri, selection, runOptions); + }); + } + + $runQueryStatement(handle: number, ownerUri: string, line: number, column: number): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.runQueryStatement(ownerUri, line, column); + }); + } + + $runQueryString(handle: number, ownerUri: string, queryString: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.runQueryString(ownerUri, queryString); + }); + } + + $runQueryAndReturn(handle: number, ownerUri: string, queryString: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.runQueryAndReturn(ownerUri, queryString); + }); + } + + $getQueryRows(handle: number, rowData: data.QueryExecuteSubsetParams): Thenable { + return this._runWithProvider(handle, (provider) => { + return provider.queryProvider.getQueryRows(rowData); + }); + } + + $disposeQuery(handle: number, ownerUri: string): Thenable { + return this._runWithProvider(handle, (provider) => { + return provider.queryProvider.disposeQuery(ownerUri); + }); + } + + $onQueryComplete(handle: number, result: data.QueryExecuteCompleteNotificationResult): void { + this._proxy.$onQueryComplete(handle, result); + } + $onBatchStart(handle: number, batchInfo: data.QueryExecuteBatchNotificationParams): void { + this._proxy.$onBatchStart(handle, batchInfo); + } + $onBatchComplete(handle: number, batchInfo: data.QueryExecuteBatchNotificationParams): void { + this._proxy.$onBatchComplete(handle, batchInfo); + } + $onResultSetComplete(handle: number, resultSetInfo: data.QueryExecuteResultSetCompleteNotificationParams): void { + this._proxy.$onResultSetComplete(handle, resultSetInfo); + } + $onQueryMessage(handle: number, message: data.QueryExecuteMessageParams): void { + this._proxy.$onQueryMessage(handle, message); + } + + $saveResults(handle: number, requestParams: data.SaveResultsRequestParams): Thenable { + return this._runWithProvider(handle, (provider) => { + return provider.queryProvider.saveResults(requestParams); + }); + } + + // Edit Data handlers + $commitEdit(handle: number, ownerUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.commitEdit(ownerUri); + }); + } + + $createRow(handle: number, ownerUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.createRow(ownerUri); + }); + } + + $deleteRow(handle: number, ownerUri: string, rowId: number): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.deleteRow(ownerUri, rowId); + }); + } + + $disposeEdit(handle: number, ownerUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.disposeEdit(ownerUri); + }); + } + + $initializeEdit(handle: number, ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.initializeEdit(ownerUri, schemaName, objectName, objectType, rowLimit); + }); + } + + $revertCell(handle: number, ownerUri: string, rowId: number, columnId: number): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.revertCell(ownerUri, rowId, columnId); + }); + } + + $revertRow(handle: number, ownerUri: string, rowId: number): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.revertRow(ownerUri, rowId); + }); + } + + $updateCell(handle: number, ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.queryProvider.updateCell(ownerUri, rowId, columnId, newValue); + }); + } + + $getEditRows(handle: number, rowData: data.EditSubsetParams): Thenable { + return this._runWithProvider(handle, (provider) => { + return provider.queryProvider.getEditRows(rowData); + }); + } + + $onEditSessionReady(handle: number, ownerUri: string, success: boolean, message: string): void { + this._proxy.$onEditSessionReady(handle, ownerUri, success, message); + } + + // Metadata handlers + public $getMetadata(handle: number, connectionUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.metadataProvider ? provider.metadataProvider.getMetadata(connectionUri) + : Promise.resolve(undefined); + }); + } + + // Object Explorer Service + public $createObjectExplorerSession(handle: number, connInfo: data.ConnectionInfo): Thenable { + return this._runWithProvider(handle, provider => { + return provider.objectExplorerProvider ? provider.objectExplorerProvider.createNewSession(connInfo) + : Promise.resolve(undefined); + }); + } + + public $expandObjectExplorerNode(handle: number, nodeInfo: data.ExpandNodeInfo): Thenable { + return this._runWithProvider(handle, provider => { + return provider.objectExplorerProvider ? provider.objectExplorerProvider.expandNode(nodeInfo) + : Promise.resolve(undefined); + }); + } + + public $refreshObjectExplorerNode(handle: number, nodeInfo: data.ExpandNodeInfo): Thenable { + return this._runWithProvider(handle, provider => { + return provider.objectExplorerProvider ? provider.objectExplorerProvider.refreshNode(nodeInfo) + : Promise.resolve(undefined); + }); + } + + public $closeObjectExplorerSession(handle: number, closeSessionInfo: data.ObjectExplorerCloseSessionInfo): Thenable { + return this._runWithProvider(handle, provider => { + return provider.objectExplorerProvider ? provider.objectExplorerProvider.closeSession(closeSessionInfo) + : Promise.resolve(undefined); + }); + } + + public $onObjectExplorerSessionCreated(handle: number, response: data.ObjectExplorerSession): void { + this._proxy.$onObjectExplorerSessionCreated(handle, response); + } + + public $onObjectExplorerNodeExpanded(handle: number, response: data.ObjectExplorerExpandInfo): void { + this._proxy.$onObjectExplorerNodeExpanded(handle, response); + } + + // Task Service + public $getAllTasks(handle: number, listTasksParams: data.ListTasksParams): Thenable { + return this._runWithProvider(handle, provider => { + return provider.taskServicesProvider ? provider.taskServicesProvider.getAllTasks(listTasksParams) + : Promise.resolve(undefined); + }); + } + + public $cancelTask(handle: number, cancelTaskParams: data.CancelTaskParams): Thenable { + return this._runWithProvider(handle, provider => { + return provider.taskServicesProvider ? provider.taskServicesProvider.cancelTask(cancelTaskParams) + : Promise.resolve(undefined); + }); + } + + public $onTaskStatusChanged(handle: number, response: data.TaskProgressInfo): void { + this._proxy.$onTaskStatusChanged(handle, response); + } + + public $onTaskCreated(handle: number, response: data.TaskInfo): void { + this._proxy.$onTaskCreated(handle, response); + } + + public $getDatabases(handle: number, connectionUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.metadataProvider ? provider.metadataProvider.getDatabases(connectionUri) + : Promise.resolve(undefined); + }); + } + + public $getTableInfo(handle: number, connectionUri: string, metadata: data.ObjectMetadata): Thenable { + return this._runWithProvider(handle, provider => { + return provider.metadataProvider ? provider.metadataProvider.getTableInfo(connectionUri, metadata) + : Promise.resolve(undefined); + }); + } + + public $getViewInfo(handle: number, connectionUri: string, metadata: data.ObjectMetadata): Thenable { + return this._runWithProvider(handle, provider => { + return provider.metadataProvider ? provider.metadataProvider.getViewInfo(connectionUri, metadata) + : Promise.resolve(undefined); + }); + } + + // Scripting handlers + public $scriptAsSelect(handle: number, connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { + return this._runWithProvider(handle, provider => { + return provider.scriptingProvider ? provider.scriptingProvider.scriptAsSelect(connectionUri, metadata, paramDetails) + : Promise.resolve(undefined); + }); + } + + public $scriptAsCreate(handle: number, connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { + return this._runWithProvider(handle, provider => { + return provider.scriptingProvider ? provider.scriptingProvider.scriptAsCreate(connectionUri, metadata, paramDetails) + : Promise.resolve(undefined); + }); + } + + public $scriptAsUpdate(handle: number, connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { + return this._runWithProvider(handle, provider => { + return provider.scriptingProvider ? provider.scriptingProvider.scriptAsUpdate(connectionUri, metadata, paramDetails) + : Promise.resolve(undefined); + }); + } + + public $scriptAsInsert(handle: number, connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { + return this._runWithProvider(handle, provider => { + return provider.scriptingProvider ? provider.scriptingProvider.scriptAsInsert(connectionUri, metadata, paramDetails) + : Promise.resolve(undefined); + }); + } + + public $scriptAsDelete(handle: number, connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { + return this._runWithProvider(handle, provider => { + return provider.scriptingProvider ? provider.scriptingProvider.scriptAsDelete(connectionUri, metadata, paramDetails) + : Promise.resolve(undefined); + }); + } + + public $onScriptingComplete(handle: number, scriptingCompleteResult: data.ScriptingCompleteResult): void { + this._proxy.$onScriptingComplete(handle, scriptingCompleteResult); + } + + /** + * Create a new database on the provided connection + */ + public $createDatabase(handle: number, connectionUri: string, database: data.DatabaseInfo): Thenable { + return this._runWithProvider(handle, provider => { + return provider.adminServicesProvider ? provider.adminServicesProvider.createDatabase(connectionUri, database) + : Promise.resolve(undefined); + }); + } + + /** + * Create a new database on the provided connection + */ + public $getDefaultDatabaseInfo(handle: number, connectionUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.adminServicesProvider ? provider.adminServicesProvider.getDefaultDatabaseInfo(connectionUri) + : Promise.resolve(undefined); + }); + } + + /** + * Get the info on a database + */ + public $getDatabaseInfo(handle: number, connectionUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.adminServicesProvider ? provider.adminServicesProvider.getDatabaseInfo(connectionUri) + : Promise.resolve(undefined); + }); + } + + /** + * Create a new login on the provided connection + */ + public $createLogin(handle: number, connectionUri: string, login: data.LoginInfo): Thenable { + return this._runWithProvider(handle, provider => { + return provider.adminServicesProvider ? provider.adminServicesProvider.createLogin(connectionUri, login) + : Promise.resolve(undefined); + }); + } + + /** + * Backup a database + */ + public $backup(handle: number, connectionUri: string, backupInfo: { [key: string]: any }, taskExecutionMode: data.TaskExecutionMode): Thenable { + return this._runWithProvider(handle, provider => { + return provider.disasterRecoveryProvider ? provider.disasterRecoveryProvider.backup(connectionUri, backupInfo, taskExecutionMode) + : Promise.resolve(undefined); + }); + } + + /** + * Create a new database on the provided connection + */ + public $getBackupConfigInfo(handle: number, connectionUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.disasterRecoveryProvider ? provider.disasterRecoveryProvider.getBackupConfigInfo(connectionUri) + : Promise.resolve(undefined); + }); + } + + /** + * Restores a database + */ + public $restore(handle: number, connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { + return this._runWithProvider(handle, provider => { + return provider.disasterRecoveryProvider ? provider.disasterRecoveryProvider.restore(connectionUri, restoreInfo) + : Promise.resolve(undefined); + }); + } + + /** + * Gets a plan for restoring a database + */ + public $getRestorePlan(handle: number, connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { + return this._runWithProvider(handle, provider => { + return provider.disasterRecoveryProvider ? provider.disasterRecoveryProvider.getRestorePlan(connectionUri, restoreInfo) + : Promise.resolve(undefined); + }); + } + + /** + * cancels a restore plan + */ + public $cancelRestorePlan(handle: number, connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { + return this._runWithProvider(handle, provider => { + return provider.disasterRecoveryProvider ? provider.disasterRecoveryProvider.cancelRestorePlan(connectionUri, restoreInfo) + : Promise.resolve(undefined); + }); + } + + /** + * Gets restore config Info + */ + public $getRestoreConfigInfo(handle: number, connectionUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.disasterRecoveryProvider ? provider.disasterRecoveryProvider.getRestoreConfigInfo(connectionUri) + : Promise.resolve(undefined); + }); + } + + /** + * Open a file browser + */ + public $openFileBrowser(handle: number, ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable { + return this._runWithProvider(handle, provider => { + return provider.fileBrowserProvider ? provider.fileBrowserProvider.openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter) + : Promise.resolve(undefined); + }); + } + + /** + * Send event when opening browser is complete + */ + public $onFileBrowserOpened(handle: number, response: data.FileBrowserOpenedParams): void { + this._proxy.$onFileBrowserOpened(handle, response); + } + + /** + * Expand a folder node + */ + public $expandFolderNode(handle: number, ownerUri: string, expandPath: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.fileBrowserProvider ? provider.fileBrowserProvider.expandFolderNode(ownerUri, expandPath) + : Promise.resolve(undefined); + }); + } + + /** + * Send event when expansion is complete + */ + public $onFolderNodeExpanded(handle: number, response: data.FileBrowserExpandedParams): void { + this._proxy.$onFolderNodeExpanded(handle, response); + } + + /** + * Validate selected file path + */ + public $validateFilePaths(handle: number, ownerUri: string, serviceType: string, selectedFiles: string[]): Thenable { + return this._runWithProvider(handle, provider => { + return provider.fileBrowserProvider ? provider.fileBrowserProvider.validateFilePaths(ownerUri, serviceType, selectedFiles) + : Promise.resolve(undefined); + }); + } + + /** + * Send event when validation is complete + */ + public $onFilePathsValidated(handle: number, response: data.FileBrowserValidatedParams) { + this._proxy.$onFilePathsValidated(handle, response); + } + + /** + * Close file browser + */ + public $closeFileBrowser(handle: number, ownerUri: string): Thenable { + return this._runWithProvider(handle, provider => { + return provider.fileBrowserProvider ? provider.fileBrowserProvider.closeFileBrowser(ownerUri) + : Promise.resolve(undefined); + }); + } +} diff --git a/src/sql/workbench/api/node/extHostResourceProvider.ts b/src/sql/workbench/api/node/extHostResourceProvider.ts new file mode 100644 index 0000000000..ab5f6779a0 --- /dev/null +++ b/src/sql/workbench/api/node/extHostResourceProvider.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import {TPromise} from 'vs/base/common/winjs.base'; +import {IThreadService} from 'vs/workbench/services/thread/common/threadService'; +import {Disposable} from 'vs/workbench/api/node/extHostTypes'; +import { + ExtHostResourceProviderShape, + MainThreadResourceProviderShape, + SqlMainContext, +} from 'sql/workbench/api/node/sqlExtHost.protocol'; + +export class ExtHostResourceProvider extends ExtHostResourceProviderShape { + private _handlePool: number = 0; + private _proxy: MainThreadResourceProviderShape; + private _providers: {[handle: number]: ResourceProviderWithMetadata} = {}; + + constructor(threadService: IThreadService) { + super(); + this._proxy = threadService.get(SqlMainContext.MainThreadResourceProvider); + } + + // PUBLIC METHODS ////////////////////////////////////////////////////// + // - MAIN THREAD AVAILABLE METHODS ///////////////////////////////////// + public $createFirewallRule(handle: number, account: data.Account, firewallRuleInfo: data.FirewallRuleInfo): Thenable { + return this._withProvider(handle, (provider: data.ResourceProvider) => provider.createFirewallRule(account, firewallRuleInfo)); + } + public $handleFirewallRule(handle: number, errorCode: number, errorMessage: string, connectionTypeId: string): Thenable { + return this._withProvider(handle, (provider: data.ResourceProvider) => provider.handleFirewallRule(errorCode, errorMessage, connectionTypeId)); + } + + // - EXTENSION HOST AVAILABLE METHODS ////////////////////////////////// + public $registerResourceProvider(providerMetadata: data.ResourceProviderMetadata, provider: data.ResourceProvider): Disposable { + let self = this; + + // Look for any account providers that have the same provider ID + let matchingProviderIndex = Object.values(this._providers).findIndex((provider: ResourceProviderWithMetadata) => { + return provider.metadata.id === providerMetadata.id; + }); + if (matchingProviderIndex >= 0) { + throw new Error(`Resource Provider with ID '${providerMetadata.id}' has already been registered`); + } + + // Create the handle for the provider + let handle: number = this._nextHandle(); + this._providers[handle] = { + metadata: providerMetadata, + provider: provider + }; + + // Register the provider in the main thread via the proxy + this._proxy.$registerResourceProvider(providerMetadata, handle); + + // Return a disposable to cleanup the provider + return new Disposable(() => { + delete self._providers[handle]; + self._proxy.$unregisterResourceProvider(handle); + }); + } + + /** + * This method is for testing only, it is not exposed via the shape. + * @return {number} Number of providers that are currently registered + */ + public getProviderCount(): number { + return Object.keys(this._providers).length; + } + + // PRIVATE METHODS ///////////////////////////////////////////////////// + private _nextHandle(): number { + return this._handlePool++; + } + + private _withProvider(handle: number, callback: (provider: data.ResourceProvider) => Thenable): Thenable { + let provider = this._providers[handle]; + if (provider === undefined) { + return TPromise.wrapError(new Error(`Provider ${handle} not found.`)); + } + return callback(provider.provider); + } +} + +interface ResourceProviderWithMetadata { + metadata: data.ResourceProviderMetadata; + provider: data.ResourceProvider; +} + + diff --git a/src/sql/workbench/api/node/extHostSerializationProvider.ts b/src/sql/workbench/api/node/extHostSerializationProvider.ts new file mode 100644 index 0000000000..017ac3775d --- /dev/null +++ b/src/sql/workbench/api/node/extHostSerializationProvider.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; +import { SqlMainContext, MainThreadSerializationProviderShape, ExtHostSerializationProviderShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; +import * as vscode from 'vscode'; +import * as data from 'data'; +import { Disposable } from 'vs/workbench/api/node/extHostTypes'; + +class SerializationAdapter { + private _provider: data.SerializationProvider; + + constructor(provider: data.SerializationProvider) { + this._provider = provider; + } + + public saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable { + return this._provider.saveAs(saveFormat, savePath, results, appendToFile); + } + +} + +type Adapter = SerializationAdapter; + +export class ExtHostSerializationProvider extends ExtHostSerializationProviderShape { + + private _proxy: MainThreadSerializationProviderShape; + + private static _handlePool: number = 0; + private _adapter: { [handle: number]: Adapter } = Object.create(null); + + private _createDisposable(handle: number): Disposable { + return new Disposable(() => { + delete this._adapter[handle]; + this._proxy.$unregisterSerializationProvider(handle); + }); + } + + private _nextHandle(): number { + return ExtHostSerializationProvider._handlePool++; + } + + private _withAdapter(handle: number, ctor: { new (...args: any[]): A }, callback: (adapter: A) => Thenable): Thenable { + let adapter = this._adapter[handle]; + if (!(adapter instanceof ctor)) { + return TPromise.wrapError(new Error('no adapter found')); + } + return callback(adapter); + } + + constructor( + threadService: IThreadService + ) { + super(); + this._proxy = threadService.get(SqlMainContext.MainThreadSerializationProvider); + } + + public $registerSerializationProvider(provider: data.SerializationProvider): vscode.Disposable { + provider.handle = this._nextHandle(); + this._adapter[provider.handle] = new SerializationAdapter(provider); + this._proxy.$registerSerializationProvider(provider.handle); + return this._createDisposable(provider.handle); + } + + public $saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable { + return this._withAdapter(0, SerializationAdapter, adapter => adapter.saveAs(saveFormat, savePath, results, appendToFile)); + } + +} diff --git a/src/sql/workbench/api/node/mainThreadAccountManagement.ts b/src/sql/workbench/api/node/mainThreadAccountManagement.ts new file mode 100644 index 0000000000..74bd57677b --- /dev/null +++ b/src/sql/workbench/api/node/mainThreadAccountManagement.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as data from 'data'; +import {TPromise} from 'vs/base/common/winjs.base'; +import {IAccountManagementService} from 'sql/services/accountManagement/interfaces'; +import {dispose, IDisposable} from 'vs/base/common/lifecycle'; +import { + ExtHostAccountManagementShape, + MainThreadAccountManagementShape, + SqlExtHostContext, + SqlMainContext +} from 'sql/workbench/api/node/sqlExtHost.protocol'; +import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; + + +@extHostNamedCustomer(SqlMainContext.MainThreadAccountManagement) +export class MainThreadAccountManagement extends MainThreadAccountManagementShape { + private _providerMetadata: {[handle: number]: data.AccountProviderMetadata}; + private _proxy: ExtHostAccountManagementShape; + private _toDispose: IDisposable[]; + + constructor( + extHostContext: IExtHostContext, + @IAccountManagementService private _accountManagementService: IAccountManagementService + ) { + super(); + this._providerMetadata = {}; + if (extHostContext) { + this._proxy = extHostContext.get(SqlExtHostContext.ExtHostAccountManagement); + } + this._toDispose = []; + } + + public $performOAuthAuthorization(url, silent: boolean): Thenable { + return this._accountManagementService.performOAuthAuthorization(url, silent); + } + + public $registerAccountProvider(providerMetadata: data.AccountProviderMetadata, handle: number): Thenable { + let self = this; + + // Create the account provider that interfaces with the extension via the proxy and register it + let accountProvider: data.AccountProvider = { + clear(accountKey: data.AccountKey): Thenable { + return self._proxy.$clear(handle, accountKey); + }, + getSecurityToken(account: data.Account): Thenable<{}> { + return self._proxy.$getSecurityToken(handle, account); + }, + initialize(restoredAccounts: data.Account[]): Thenable { + return self._proxy.$initialize(handle, restoredAccounts); + }, + prompt(): Thenable { + return self._proxy.$prompt(handle); + }, + refresh(account: data.Account): Thenable { + return self._proxy.$refresh(handle, account); + } + }; + this._accountManagementService.registerProvider(providerMetadata, accountProvider); + this._providerMetadata[handle] = providerMetadata; + + return TPromise.as(null); + } + + public $unregisterAccountProvider(handle: number): Thenable { + this._accountManagementService.unregisterProvider(this._providerMetadata[handle]); + return TPromise.as(null); + } + + public dispose(): void { + this._toDispose = dispose(this._toDispose); + } +} diff --git a/src/sql/workbench/api/node/mainThreadCredentialManagement.ts b/src/sql/workbench/api/node/mainThreadCredentialManagement.ts new file mode 100644 index 0000000000..ad92559571 --- /dev/null +++ b/src/sql/workbench/api/node/mainThreadCredentialManagement.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { + SqlExtHostContext, ExtHostCredentialManagementShape, + MainThreadCredentialManagementShape, SqlMainContext } from 'sql/workbench/api/node/sqlExtHost.protocol'; +import { ICredentialsService } from 'sql/services/credentials/credentialsService'; +import * as data from 'data'; +import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; + +@extHostNamedCustomer(SqlMainContext.MainThreadCredentialManagement) +export class MainThreadCredentialManagement extends MainThreadCredentialManagementShape { + + private _proxy: ExtHostCredentialManagementShape; + + private _toDispose: IDisposable[]; + + private _registrations: { [handle: number]: IDisposable; } = Object.create(null); + + constructor( + extHostContext: IExtHostContext, + @ICredentialsService private credentialService: ICredentialsService + ) { + super(); + if (extHostContext) { + this._proxy = extHostContext.get(SqlExtHostContext.ExtHostCredentialManagement); + } + } + + public dispose(): void { + this._toDispose = dispose(this._toDispose); + } + + public $registerCredentialProvider(handle: number): TPromise { + let self = this; + + this._registrations[handle] = this.credentialService.addEventListener(handle, { + onSaveCredential(credentialId: string, password: string): Thenable { + return self._proxy.$saveCredential(credentialId, password); + }, + onReadCredential(credentialId: string): Thenable { + return self._proxy.$readCredential(credentialId); + }, + onDeleteCredential(credentialId: string): Thenable { + return self._proxy.$deleteCredential(credentialId); + } + }); + + return undefined; + } + + public $unregisterCredentialProvider(handle: number): TPromise { + let registration = this._registrations[handle]; + if (registration) { + registration.dispose(); + delete this._registrations[handle]; + } + return undefined; + } +} diff --git a/src/sql/workbench/api/node/mainThreadDataProtocol.ts b/src/sql/workbench/api/node/mainThreadDataProtocol.ts new file mode 100644 index 0000000000..1121d63d2a --- /dev/null +++ b/src/sql/workbench/api/node/mainThreadDataProtocol.ts @@ -0,0 +1,353 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { + SqlExtHostContext, ExtHostDataProtocolShape, + MainThreadDataProtocolShape, SqlMainContext +} from 'sql/workbench/api/node/sqlExtHost.protocol'; +import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { IQueryManagementService } from 'sql/parts/query/common/queryManagement'; +import * as data from 'data'; +import { IMetadataService } from 'sql/services/metadata/metadataService'; +import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; +import { IScriptingService } from 'sql/services/scripting/scriptingService'; +import { IAdminService } from 'sql/parts/admin/common/adminService'; +import { IDisasterRecoveryService } from 'sql/parts/disasterRecovery/common/interfaces'; +import { ITaskService } from 'sql/parts/taskHistory/common/taskService'; +import { IProfilerService } from 'sql/parts/profiler/service/interfaces'; +import { ISerializationService } from 'sql/services/serialization/serializationService'; +import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces'; +import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; + +/** + * Main thread class for handling data protocol management registration. + */ +@extHostNamedCustomer(SqlMainContext.MainThreadDataProtocol) +export class MainThreadDataProtocol extends MainThreadDataProtocolShape { + + private _proxy: ExtHostDataProtocolShape; + + private _toDispose: IDisposable[]; + + private _capabilitiesRegistrations: { [handle: number]: IDisposable; } = Object.create(null); + + constructor( + extHostContext: IExtHostContext, + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService, + @IQueryManagementService private _queryManagementService: IQueryManagementService, + @IMetadataService private _metadataService: IMetadataService, + @IObjectExplorerService private _objectExplorerService: IObjectExplorerService, + @IScriptingService private _scriptingService: IScriptingService, + @IAdminService private _adminService: IAdminService, + @IDisasterRecoveryService private _disasterRecoveryService: IDisasterRecoveryService, + @ITaskService private _taskService: ITaskService, + @IProfilerService private _profilerService: IProfilerService, + @ISerializationService private _serializationService: ISerializationService, + @IFileBrowserService private _fileBrowserService: IFileBrowserService + ) { + super(); + if (extHostContext) { + this._proxy = extHostContext.get(SqlExtHostContext.ExtHostDataProtocol); + } + if (this._connectionManagementService) { + this._connectionManagementService.onLanguageFlavorChanged(e => this._proxy.$languageFlavorChanged(e), this, this._toDispose); + } + } + + public dispose(): void { + this._toDispose = dispose(this._toDispose); + } + + public $registerProvider(providerId: string, handle: number): TPromise { + let self = this; + + // register connection management provider + this._connectionManagementService.registerProvider(providerId, { + connect(connectionUri: string, connectionInfo: data.ConnectionInfo): Thenable { + return self._proxy.$connect(handle, connectionUri, connectionInfo); + }, + disconnect(connectionUri: string): Thenable { + return self._proxy.$disconnect(handle, connectionUri); + }, + changeDatabase(connectionUri: string, newDatabase: string): Thenable { + return self._proxy.$changeDatabase(handle, connectionUri, newDatabase); + }, + cancelConnect(connectionUri: string): Thenable { + return self._proxy.$cancelConnect(handle, connectionUri); + }, + listDatabases(connectionUri: string): Thenable { + return self._proxy.$listDatabases(handle, connectionUri); + }, + rebuildIntelliSenseCache(connectionUri: string): Thenable { + return self._proxy.$rebuildIntelliSenseCache(handle, connectionUri); + } + }); + + this._capabilitiesService.registerProvider({ + getServerCapabilities(client: data.DataProtocolClientCapabilities): Thenable { + return self._proxy.$getServerCapabilities(handle, client); + } + }); + + // register query provider + this._queryManagementService.addQueryRequestHandler(providerId, { + cancelQuery(ownerUri: string): Thenable { + return self._proxy.$cancelQuery(handle, ownerUri); + }, + runQuery(ownerUri: string, selection: data.ISelectionData, runOptions?: data.ExecutionPlanOptions): Thenable { + return self._proxy.$runQuery(handle, ownerUri, selection, runOptions); + }, + runQueryStatement(ownerUri: string, line: number, column: number): Thenable { + return self._proxy.$runQueryStatement(handle, ownerUri, line, column); + }, + runQueryString(ownerUri: string, queryString: string): Thenable { + return self._proxy.$runQueryString(handle, ownerUri, queryString); + }, + runQueryAndReturn(ownerUri: string, queryString: string): Thenable { + return self._proxy.$runQueryAndReturn(handle, ownerUri, queryString); + }, + getQueryRows(rowData: data.QueryExecuteSubsetParams): Thenable { + return self._proxy.$getQueryRows(handle, rowData); + }, + disposeQuery(ownerUri: string): Thenable { + return self._proxy.$disposeQuery(handle, ownerUri); + }, + saveResults(requestParams: data.SaveResultsRequestParams): Thenable { + let serializationProvider = self._serializationService.getSerializationFeatureMetadataProvider(requestParams.ownerUri); + if (serializationProvider && serializationProvider.enabled) { + return self._proxy.$saveResults(handle, requestParams); + } + else if (serializationProvider && !serializationProvider.enabled) { + return self._serializationService.disabledSaveAs(); + } + else { + return self._serializationService.saveAs(requestParams.resultFormat, requestParams.filePath, undefined, true); + } + }, + initializeEdit(ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): Thenable { + return self._proxy.$initializeEdit(handle, ownerUri, schemaName, objectName, objectType, rowLimit); + }, + updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable { + return self._proxy.$updateCell(handle, ownerUri, rowId, columnId, newValue); + }, + commitEdit(ownerUri): Thenable { + return self._proxy.$commitEdit(handle, ownerUri); + }, + createRow(ownerUri: string): Thenable { + return self._proxy.$createRow(handle, ownerUri); + }, + deleteRow(ownerUri: string, rowId: number): Thenable { + return self._proxy.$deleteRow(handle, ownerUri, rowId); + }, + disposeEdit(ownerUri: string): Thenable { + return self._proxy.$disposeEdit(handle, ownerUri); + }, + revertCell(ownerUri: string, rowId: number, columnId: number): Thenable { + return self._proxy.$revertCell(handle, ownerUri, rowId, columnId); + }, + revertRow(ownerUri: string, rowId: number): Thenable { + return self._proxy.$revertRow(handle, ownerUri, rowId); + }, + getEditRows(rowData: data.EditSubsetParams): Thenable { + return self._proxy.$getEditRows(handle, rowData); + } + }); + + this._metadataService.registerProvider(providerId, { + getMetadata(connectionUri: string): Thenable { + return self._proxy.$getMetadata(handle, connectionUri); + }, + getDatabases(connectionUri: string): Thenable { + return self._proxy.$getDatabases(handle, connectionUri); + }, + getTableInfo(connectionUri: string, metadata: data.ObjectMetadata): Thenable { + return self._proxy.$getTableInfo(handle, connectionUri, metadata); + }, + getViewInfo(connectionUri: string, metadata: data.ObjectMetadata): Thenable { + return self._proxy.$getViewInfo(handle, connectionUri, metadata); + } + }); + + this._objectExplorerService.registerProvider(providerId, { + createNewSession(connection: data.ConnectionInfo): Thenable { + return self._proxy.$createObjectExplorerSession(handle, connection); + }, + expandNode(nodeInfo: data.ExpandNodeInfo): Thenable { + return self._proxy.$expandObjectExplorerNode(handle, nodeInfo); + }, + refreshNode(nodeInfo: data.ExpandNodeInfo): Thenable { + return self._proxy.$refreshObjectExplorerNode(handle, nodeInfo); + }, + closeSession(closeSessionInfo: data.ObjectExplorerCloseSessionInfo): Thenable { + return self._proxy.$closeObjectExplorerSession(handle, closeSessionInfo); + } + }); + + this._taskService.registerProvider(providerId, { + getAllTasks(listTasksParams: data.ListTasksParams): Thenable { + return self._proxy.$getAllTasks(handle, listTasksParams); + }, + cancelTask(cancelTaskParams: data.CancelTaskParams): Thenable { + return self._proxy.$cancelTask(handle, cancelTaskParams); + } + }); + + this._scriptingService.registerProvider(providerId, { + scriptAsSelect(connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { + return self._proxy.$scriptAsSelect(handle, connectionUri, metadata, paramDetails); + }, + scriptAsCreate(connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { + return self._proxy.$scriptAsCreate(handle, connectionUri, metadata, paramDetails); + }, + scriptAsInsert(connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { + return self._proxy.$scriptAsInsert(handle, connectionUri, metadata, paramDetails); + }, + scriptAsUpdate(connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { + return self._proxy.$scriptAsUpdate(handle, connectionUri, metadata, paramDetails); + }, + scriptAsDelete(connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { + return self._proxy.$scriptAsDelete(handle, connectionUri, metadata, paramDetails); + } + }); + + this._adminService.registerProvider(providerId, { + createDatabase(connectionUri: string, database: data.DatabaseInfo): Thenable { + return self._proxy.$createDatabase(handle, connectionUri, database); + }, + getDefaultDatabaseInfo(connectionUri: string): Thenable { + return self._proxy.$getDefaultDatabaseInfo(handle, connectionUri); + }, + getDatabaseInfo(connectionUri: string): Thenable { + return self._proxy.$getDatabaseInfo(handle, connectionUri); + }, + createLogin(connectionUri: string, login: data.LoginInfo): Thenable { + return self._proxy.$createLogin(handle, connectionUri, login); + } + }); + + this._disasterRecoveryService.registerProvider(providerId, { + backup(connectionUri: string, backupInfo: { [key: string]: any }, taskExecutionMode: data.TaskExecutionMode): Thenable { + return self._proxy.$backup(handle, connectionUri, backupInfo, taskExecutionMode); + }, + getBackupConfigInfo(connectionUri: string): Thenable { + return self._proxy.$getBackupConfigInfo(handle, connectionUri); + }, + getRestorePlan(connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { + return self._proxy.$getRestorePlan(handle, connectionUri, restoreInfo); + }, + cancelRestorePlan(connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { + return self._proxy.$cancelRestorePlan(handle, connectionUri, restoreInfo); + }, + restore(connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { + return self._proxy.$restore(handle, connectionUri, restoreInfo); + }, + getRestoreConfigInfo(connectionUri: string): Thenable { + return self._proxy.$getRestoreConfigInfo(handle, connectionUri); + } + }); + + this._fileBrowserService.registerProvider(providerId, { + openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable { + return self._proxy.$openFileBrowser(handle, ownerUri, expandPath, fileFilters, changeFilter); + }, + expandFolderNode(ownerUri: string, expandPath: string): Thenable { + return self._proxy.$expandFolderNode(handle, ownerUri, expandPath); + }, + validateFilePaths(ownerUri: string, serviceType: string, selectedFiles: string[]): Thenable { + return self._proxy.$validateFilePaths(handle, ownerUri, serviceType, selectedFiles); + }, + closeFileBrowser(ownerUri: string): Thenable { + return self._proxy.$closeFileBrowser(handle, ownerUri); + } + }); + + return undefined; + } + + // Connection Management handlers + public $onConnectionComplete(handle: number, connectionInfoSummary: data.ConnectionInfoSummary): void { + this._connectionManagementService.onConnectionComplete(handle, connectionInfoSummary); + } + + public $onIntelliSenseCacheComplete(handle: number, connectionUri: string): void { + this._connectionManagementService.onIntelliSenseCacheComplete(handle, connectionUri); + } + + public $onConnectionChangeNotification(handle: number, changedConnInfo: data.ChangedConnectionInfo): void { + this._connectionManagementService.onConnectionChangedNotification(handle, changedConnInfo); + } + + // Query Management handlers + public $onQueryComplete(handle: number, result: data.QueryExecuteCompleteNotificationResult): void { + this._queryManagementService.onQueryComplete(result); + } + public $onBatchStart(handle: number, batchInfo: data.QueryExecuteBatchNotificationParams): void { + this._queryManagementService.onBatchStart(batchInfo); + } + public $onBatchComplete(handle: number, batchInfo: data.QueryExecuteBatchNotificationParams): void { + this._queryManagementService.onBatchComplete(batchInfo); + } + public $onResultSetComplete(handle: number, resultSetInfo: data.QueryExecuteResultSetCompleteNotificationParams): void { + this._queryManagementService.onResultSetComplete(resultSetInfo); + } + public $onQueryMessage(handle: number, message: data.QueryExecuteMessageParams): void { + this._queryManagementService.onMessage(message); + } + public $onEditSessionReady(handle: number, ownerUri: string, success: boolean, message: string): void { + this._queryManagementService.onEditSessionReady(ownerUri, success, message); + } + + // Script Handlers + public $onScriptingComplete(handle: number, scriptingCompleteResult: data.ScriptingCompleteResult): void { + this._scriptingService.onScriptingComplete(handle, scriptingCompleteResult); + } + + //OE handlers + public $onObjectExplorerSessionCreated(handle: number, sessionResponse: data.ObjectExplorerSession): void { + this._objectExplorerService.onSessionCreated(handle, sessionResponse); + } + + public $onObjectExplorerNodeExpanded(handle: number, expandResponse: data.ObjectExplorerExpandInfo): void { + this._objectExplorerService.onNodeExpanded(handle, expandResponse); + } + + //Tasks handlers + public $onTaskCreated(handle: number, taskInfo: data.TaskInfo): void { + this._taskService.onNewTaskCreated(handle, taskInfo); + } + + public $onTaskStatusChanged(handle: number, taskProgressInfo: data.TaskProgressInfo): void { + this._taskService.onTaskStatusChanged(handle, taskProgressInfo); + } + + //File browser handlers + public $onFileBrowserOpened(handle: number, response: data.FileBrowserOpenedParams): void { + this._fileBrowserService.onFileBrowserOpened(handle, response); + } + + public $onFolderNodeExpanded(handle: number, response: data.FileBrowserExpandedParams): void { + this._fileBrowserService.onFolderNodeExpanded(handle, response); + } + + public $onFilePathsValidated(handle: number, response: data.FileBrowserValidatedParams): void { + this._fileBrowserService.onFilePathsValidated(handle, response); + } + + public $unregisterProvider(handle: number): TPromise { + let capabilitiesRegistration = this._capabilitiesRegistrations[handle]; + if (capabilitiesRegistration) { + capabilitiesRegistration.dispose(); + delete this._capabilitiesRegistrations[handle]; + } + + return undefined; + } +} diff --git a/src/sql/workbench/api/node/mainThreadResourceProvider.ts b/src/sql/workbench/api/node/mainThreadResourceProvider.ts new file mode 100644 index 0000000000..158669b390 --- /dev/null +++ b/src/sql/workbench/api/node/mainThreadResourceProvider.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as data from 'data'; +import {TPromise} from 'vs/base/common/winjs.base'; +import { IResourceProviderService } from 'sql/parts/accountManagement/common/interfaces'; +import {dispose, IDisposable} from 'vs/base/common/lifecycle'; +import { + ExtHostResourceProviderShape, + MainThreadResourceProviderShape, + SqlExtHostContext, + SqlMainContext +} from 'sql/workbench/api/node/sqlExtHost.protocol'; +import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; + + +@extHostNamedCustomer(SqlMainContext.MainThreadResourceProvider) +export class MainThreadResourceProvider extends MainThreadResourceProviderShape { + private _providerMetadata: {[handle: number]: data.AccountProviderMetadata}; + private _proxy: ExtHostResourceProviderShape; + private _toDispose: IDisposable[]; + + constructor( + extHostContext: IExtHostContext, + @IResourceProviderService private _resourceProviderService: IResourceProviderService + ) { + super(); + this._providerMetadata = {}; + if (extHostContext) { + this._proxy = extHostContext.get(SqlExtHostContext.ExtHostResourceProvider); + } + this._toDispose = []; + } + + public $registerResourceProvider(providerMetadata: data.ResourceProviderMetadata, handle: number): Thenable { + let self = this; + + // Create the account provider that interfaces with the extension via the proxy and register it + let resourceProvider: data.ResourceProvider = { + createFirewallRule(account: data.Account, firewallruleInfo: data.FirewallRuleInfo): Thenable { + return self._proxy.$createFirewallRule(handle, account, firewallruleInfo); + }, + handleFirewallRule(errorCode: number, errorMessage: string, connectionTypeId: string): Thenable { + return self._proxy.$handleFirewallRule(handle, errorCode, errorMessage, connectionTypeId); + } + }; + this._resourceProviderService.registerProvider(providerMetadata.id, resourceProvider); + this._providerMetadata[handle] = providerMetadata; + + return TPromise.as(null); + } + + public $unregisterResourceProvider(handle: number): Thenable { + this._resourceProviderService.unregisterProvider(this._providerMetadata[handle].id); + return TPromise.as(null); + } + + public dispose(): void { + this._toDispose = dispose(this._toDispose); + } +} diff --git a/src/sql/workbench/api/node/mainThreadSerializationProvider.ts b/src/sql/workbench/api/node/mainThreadSerializationProvider.ts new file mode 100644 index 0000000000..0c7f09896c --- /dev/null +++ b/src/sql/workbench/api/node/mainThreadSerializationProvider.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; +import { + SqlExtHostContext, ExtHostSerializationProviderShape, + MainThreadSerializationProviderShape, SqlMainContext } from 'sql/workbench/api/node/sqlExtHost.protocol'; +import { ISerializationService } from 'sql/services/serialization/serializationService'; +import * as data from 'data'; +import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; + +@extHostNamedCustomer(SqlMainContext.MainThreadSerializationProvider) +export class MainThreadSerializationProvider extends MainThreadSerializationProviderShape { + + private _proxy: ExtHostSerializationProviderShape; + + private _toDispose: IDisposable[]; + + private _registrations: { [handle: number]: IDisposable; } = Object.create(null); + + constructor( + extHostContext: IExtHostContext, + @ISerializationService private serializationService: ISerializationService + + ) { + super(); + if (extHostContext) { + this._proxy = extHostContext.get(SqlExtHostContext.ExtHostSerializationProvider); + } + } + + public dispose(): void { + this._toDispose = dispose(this._toDispose); + } + + public $registerSerializationProvider(handle: number): TPromise { + let self = this; + + this._registrations[handle] = this.serializationService.addEventListener(handle, { + onSaveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable { + return self._proxy.$saveAs(saveFormat, savePath, results, appendToFile); + } + }); + + return undefined; + } + + public $unregisterSerializationProvider(handle: number): TPromise { + let registration = this._registrations[handle]; + if (registration) { + registration.dispose(); + delete this._registrations[handle]; + } + return undefined; + } +} diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts new file mode 100644 index 0000000000..1d81b3644c --- /dev/null +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -0,0 +1,288 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as extHostApi from 'vs/workbench/api/node/extHost.api.impl'; +import { TrieMap } from 'vs/base/common/map'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { realpath } from 'fs'; +import * as extHostTypes from 'vs/workbench/api/node/extHostTypes'; + +import * as data from 'data'; +import * as vscode from 'vscode'; +import { SqlExtHostContext } from 'sql/workbench/api/node/sqlExtHost.protocol'; +import { ExtHostAccountManagement } from 'sql/workbench/api/node/extHostAccountManagement'; +import { ExtHostCredentialManagement } from 'sql/workbench/api/node/extHostCredentialManagement'; +import { ExtHostDataProtocol } from 'sql/workbench/api/node/extHostDataProtocol'; +import { ExtHostSerializationProvider } from 'sql/workbench/api/node/extHostSerializationProvider'; +import { ExtHostResourceProvider } from 'sql/workbench/api/node/extHostResourceProvider'; +import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService'; +import * as sqlExtHostTypes from 'sql/workbench/api/node/sqlExtHostTypes'; + +export interface ISqlExtensionApiFactory { + vsCodeFactory(extension: IExtensionDescription): typeof vscode; + dataFactory(extension: IExtensionDescription): typeof data; +} + +/** + * This method instantiates and returns the extension API surface. This overrides the default ApiFactory by extending it to add Carbon-related functions + */ +export function createApiFactory( + initData: IInitData, + threadService: ExtHostThreadService, + extensionService: ExtHostExtensionService +): ISqlExtensionApiFactory { + let vsCodeFactory = extHostApi.createApiFactory(initData, threadService, extensionService); + + // Addressable instances + const extHostAccountManagement = threadService.set(SqlExtHostContext.ExtHostAccountManagement, new ExtHostAccountManagement(threadService)); + const extHostCredentialManagement = threadService.set(SqlExtHostContext.ExtHostCredentialManagement, new ExtHostCredentialManagement(threadService)); + const extHostDataProvider = threadService.set(SqlExtHostContext.ExtHostDataProtocol, new ExtHostDataProtocol(threadService)); + const extHostSerializationProvider = threadService.set(SqlExtHostContext.ExtHostSerializationProvider, new ExtHostSerializationProvider(threadService)); + const extHostResourceProvider = threadService.set(SqlExtHostContext.ExtHostResourceProvider, new ExtHostResourceProvider(threadService)); + + return { + vsCodeFactory: vsCodeFactory, + dataFactory: function (extension: IExtensionDescription): typeof data { + // namespace: accounts + const accounts: typeof data.accounts = { + performOAuthAuthorization(url: string, silent: boolean): Thenable { + return extHostAccountManagement.$performOAuthAuthorization(url, silent); + }, + registerAccountProvider(providerMetadata: data.AccountProviderMetadata, provider: data.AccountProvider): vscode.Disposable { + return extHostAccountManagement.$registerAccountProvider(providerMetadata, provider); + }, + }; + + // namespace: credentials + const credentials: typeof data.credentials = { + registerProvider(provider: data.CredentialProvider): vscode.Disposable { + return extHostCredentialManagement.$registerCredentialProvider(provider); + }, + getProvider(namespaceId: string): Thenable { + return extHostCredentialManagement.$getCredentialProvider(namespaceId); + } + }; + + // namespace: serialization + const serialization: typeof data.serialization = { + registerProvider(provider: data.SerializationProvider): vscode.Disposable { + return extHostSerializationProvider.$registerSerializationProvider(provider); + }, + }; + + // namespace: serialization + const resources: typeof data.resources = { + registerResourceProvider(providerMetadata: data.ResourceProviderMetadata, provider: data.ResourceProvider): vscode.Disposable { + return extHostResourceProvider.$registerResourceProvider(providerMetadata, provider); + } + }; + + // namespace: dataprotocol + const dataprotocol: typeof data.dataprotocol = { + registerProvider(provider: data.DataProtocolProvider): vscode.Disposable { + // Connection callbacks + provider.connectionProvider.registerOnConnectionComplete((connSummary: data.ConnectionInfoSummary) => { + extHostDataProvider.$onConnectComplete(provider.handle, connSummary); + }); + + provider.connectionProvider.registerOnIntelliSenseCacheComplete((connectionUri: string) => { + extHostDataProvider.$onIntelliSenseCacheComplete(provider.handle, connectionUri); + }); + + provider.connectionProvider.registerOnConnectionChanged((changedConnInfo: data.ChangedConnectionInfo) => { + extHostDataProvider.$onConnectionChanged(provider.handle, changedConnInfo); + }); + + // Query callbacks + provider.queryProvider.registerOnQueryComplete((result: data.QueryExecuteCompleteNotificationResult) => { + extHostDataProvider.$onQueryComplete(provider.handle, result); + }); + + provider.queryProvider.registerOnBatchStart((batchInfo: data.QueryExecuteBatchNotificationParams) => { + extHostDataProvider.$onBatchStart(provider.handle, batchInfo); + }); + + provider.queryProvider.registerOnBatchComplete((batchInfo: data.QueryExecuteBatchNotificationParams) => { + extHostDataProvider.$onBatchComplete(provider.handle, batchInfo); + }); + + provider.queryProvider.registerOnResultSetComplete((resultSetInfo: data.QueryExecuteResultSetCompleteNotificationParams) => { + extHostDataProvider.$onResultSetComplete(provider.handle, resultSetInfo); + }); + + provider.queryProvider.registerOnMessage((message: data.QueryExecuteMessageParams) => { + extHostDataProvider.$onQueryMessage(provider.handle, message); + }); + + //OE callbacks + provider.objectExplorerProvider.registerOnSessionCreated((response: data.ObjectExplorerSession) => { + extHostDataProvider.$onObjectExplorerSessionCreated(provider.handle, response); + }); + + provider.objectExplorerProvider.registerOnExpandCompleted((response: data.ObjectExplorerExpandInfo) => { + extHostDataProvider.$onObjectExplorerNodeExpanded(provider.handle, response); + }); + + //Tasks callbacks + provider.taskServicesProvider.registerOnTaskCreated((response: data.TaskInfo) => { + extHostDataProvider.$onTaskCreated(provider.handle, response); + }); + + provider.taskServicesProvider.registerOnTaskStatusChanged((response: data.TaskProgressInfo) => { + extHostDataProvider.$onTaskStatusChanged(provider.handle, response); + }); + + // Edit Data callbacks + provider.queryProvider.registerOnEditSessionReady((ownerUri: string, success: boolean, message: string) => { + extHostDataProvider.$onEditSessionReady(provider.handle, ownerUri, success, message); + }); + + // File browser callbacks + provider.fileBrowserProvider.registerOnFileBrowserOpened((response: data.FileBrowserOpenedParams) => { + extHostDataProvider.$onFileBrowserOpened(provider.handle, response); + }); + + provider.fileBrowserProvider.registerOnFolderNodeExpanded((response: data.FileBrowserExpandedParams) => { + extHostDataProvider.$onFolderNodeExpanded(provider.handle, response); + }); + + provider.fileBrowserProvider.registerOnFilePathsValidated((response: data.FileBrowserValidatedParams) => { + extHostDataProvider.$onFilePathsValidated(provider.handle, response); + }); + + // Scripting callbacks + provider.scriptingProvider.registerOnScriptingComplete((response: data.ScriptingCompleteResult) => { + extHostDataProvider.$onScriptingComplete(provider.handle, response); + }); + + // Complete registration + return extHostDataProvider.$registerProvider(provider); + }, + onDidChangeLanguageFlavor(listener: (e: data.DidChangeLanguageFlavorParams) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) { + return extHostDataProvider.onDidChangeLanguageFlavor(listener, thisArgs, disposables); + } + }; + + return { + accounts, + credentials, + resources, + serialization, + dataprotocol, + ServiceOptionType: sqlExtHostTypes.ServiceOptionType, + ConnectionOptionSpecialType: sqlExtHostTypes.ConnectionOptionSpecialType, + EditRowState: sqlExtHostTypes.EditRowState, + MetadataType: sqlExtHostTypes.MetadataType, + TaskStatus: sqlExtHostTypes.TaskStatus, + TaskExecutionMode: sqlExtHostTypes.TaskExecutionMode + }; + } + }; +} + +export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: ISqlExtensionApiFactory): TPromise { + return createExtensionPathIndex(extensionService).then(trie => defineAPI(apiFactory, trie)); +} + +function createExtensionPathIndex(extensionService: ExtHostExtensionService): TPromise> { + + // create trie to enable fast 'filename -> extension id' look up + const trie = new TrieMap(TrieMap.PathSplitter); + const extensions = extensionService.getAllExtensionDescriptions().map(ext => { + if (!ext.main) { + return undefined; + } + return new TPromise((resolve, reject) => { + realpath(ext.extensionFolderPath, (err, path) => { + if (err) { + reject(err); + } else { + trie.insert(path, ext); + resolve(void 0); + } + }); + }); + }); + + return TPromise.join(extensions).then(() => trie); +} + +function defineAPI(factory: ISqlExtensionApiFactory, extensionPaths: TrieMap): void { + type ApiImpl = typeof vscode | typeof data; + + // each extension is meant to get its own api implementation + const extApiImpl = new Map(); + const dataExtApiImpl = new Map(); + let defaultApiImpl: typeof vscode; + let defaultDataApiImpl: typeof data; + + // The module factory looks for an entry in the API map for an extension. If found, it reuses this. + // If not, it loads it & saves it in the map + let getModuleFactory = function (apiMap: Map, + createApi: (extensionDescription: IExtensionDescription) => ApiImpl, + defaultImpl: ApiImpl, + setDefaultApiImpl: (defaultImpl: ApiImpl) => void, + parent: any): ApiImpl { + // get extension id from filename and api for extension + const ext = extensionPaths.findSubstr(parent.filename); + if (ext) { + let apiImpl = apiMap.get(ext.id); + if (!apiImpl) { + apiImpl = createApi(ext); + apiMap.set(ext.id, apiImpl); + } + return apiImpl; + } + + // fall back to a default implementation + if (!defaultImpl) { + defaultImpl = createApi(nullExtensionDescription); + setDefaultApiImpl(defaultImpl); + } + return defaultImpl; + }; + + const node_module = require.__$__nodeRequire('module'); + const original = node_module._load; + + // TODO look into de-duplicating this code + node_module._load = function load(request, parent, isMain) { + if (request === 'vscode') { + return getModuleFactory(extApiImpl, (ext) => factory.vsCodeFactory(ext), + defaultApiImpl, + (impl) => defaultApiImpl = impl, + parent); + } else if (request === 'data') { + return getModuleFactory(dataExtApiImpl, + (ext) => factory.dataFactory(ext), + defaultDataApiImpl, + (impl) => defaultDataApiImpl = impl, + parent); + } else { + // Allow standard node_module load to occur + return original.apply(this, arguments); + } + }; +} + + +const nullExtensionDescription: IExtensionDescription = { + id: 'nullExtensionDescription', + name: 'Null Extension Description', + publisher: 'vscode', + activationEvents: undefined, + contributes: undefined, + enableProposedApi: false, + engines: undefined, + extensionDependencies: undefined, + extensionFolderPath: undefined, + isBuiltin: false, + main: undefined, + version: undefined +}; diff --git a/src/sql/workbench/api/node/sqlExtHost.contribution.ts b/src/sql/workbench/api/node/sqlExtHost.contribution.ts new file mode 100644 index 0000000000..c42d080d30 --- /dev/null +++ b/src/sql/workbench/api/node/sqlExtHost.contribution.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +// --- SQL contributions +import 'sql/workbench/api/node/mainThreadCredentialManagement'; +import 'sql/workbench/api/node/mainThreadDataProtocol'; +import 'sql/workbench/api/node/mainThreadSerializationProvider'; +import 'sql/workbench/api/node/mainThreadResourceProvider'; +import './mainThreadAccountManagement'; + +export class SqlExtHostContribution implements IWorkbenchContribution { + + constructor( + @IInstantiationService private instantiationService: IInstantiationService + ) { + } + + public getId(): string { + return 'sql.api.sqlExtHost'; + } +} + +// Register File Tracker +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + SqlExtHostContribution +); diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts new file mode 100644 index 0000000000..69ca319f4d --- /dev/null +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -0,0 +1,430 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { + createMainContextProxyIdentifier as createMainId, + createExtHostContextProxyIdentifier as createExtId, + ProxyIdentifier, IThreadService +} from 'vs/workbench/services/thread/common/threadService'; + +import * as data from 'data'; + +import { TPromise } from 'vs/base/common/winjs.base'; + +export abstract class ExtHostAccountManagementShape { + $clear(handle: number, accountKey: data.AccountKey): Thenable { throw ni(); } + $getSecurityToken(handle: number, account: data.Account): Thenable<{}> { throw ni(); } + $initialize(handle: number, restoredAccounts: data.Account[]): Thenable { throw ni(); } + $prompt(handle: number): Thenable { throw ni(); } + $refresh(handle: number, account: data.Account): Thenable { throw ni(); } +} + +export abstract class ExtHostDataProtocolShape { + + /** + * Establish a connection to a data source using the provided ConnectionInfo instance. + */ + $connect(handle: number, connectionUri: string, connection: data.ConnectionInfo): Thenable { throw ni(); } + + /** + * Disconnect from a data source using the provided connectionUri string. + */ + $disconnect(handle: number, connectionUri: string): Thenable { throw ni(); } + + /** + * Cancel a connection to a data source using the provided connectionUri string. + */ + $cancelConnect(handle: number, connectionUri: string): Thenable { throw ni(); } + + /** + * Change the database for the connection. + */ + $changeDatabase(handle: number, connectionUri: string, newDatabase: string): Thenable { throw ni(); } + + /** + * List databases for a data source using the provided connectionUri string. + * @param handle the handle to use when looking up a provider + * @param connectionUri URI identifying a connected resource + */ + $listDatabases(handle: number, connectionUri: string): Thenable { throw ni(); } + + /** + * Notifies all listeners on the Extension Host side that a language change occurred + * for a dataprotocol language. The sub-flavor is the specific implementation used for query + * and other events + * @param params information on what URI was changed and the new language + */ + $languageFlavorChanged(params: data.DidChangeLanguageFlavorParams): void { throw ni(); } + + /** + * Callback when a connection request has completed + */ + $onConnectComplete(handle: number, connectionInfoSummary: data.ConnectionInfoSummary): void { throw ni(); } + + /** + * Callback when a IntelliSense cache has been built + */ + $onIntelliSenseCacheComplete(handle: number, connectionUri: string): void { throw ni(); } + + $getServerCapabilities(handle: number, client: data.DataProtocolClientCapabilities): Thenable { throw ni(); } + + /** + * Metadata service methods + * + */ + $getMetadata(handle: number, connectionUri: string): Thenable { throw ni(); } + + $getDatabases(handle: number, connectionUri: string): Thenable { throw ni(); } + + $getTableInfo(handle: number, connectionUri: string, metadata: data.ObjectMetadata): Thenable { throw ni(); } + + $getViewInfo(handle: number, connectionUri: string, metadata: data.ObjectMetadata): Thenable { throw ni(); } + + /** + * Object Explorer + */ + $createObjectExplorerSession(handle: number, connInfo: data.ConnectionInfo): Thenable { throw ni(); } + + $expandObjectExplorerNode(handle: number, nodeInfo: data.ExpandNodeInfo): Thenable { throw ni(); } + + $refreshObjectExplorerNode(handle: number, nodeInfo: data.ExpandNodeInfo): Thenable { throw ni(); } + + $closeObjectExplorerSession(handle: number, closeSessionInfo: data.ObjectExplorerCloseSessionInfo): Thenable { throw ni(); } + + /** + * Tasks + */ + $getAllTasks(handle: number, listTasksParams: data.ListTasksParams): Thenable { throw ni(); } + $cancelTask(handle: number, cancelTaskParams: data.CancelTaskParams): Thenable { throw ni(); } + + /** + * Scripting methods + */ + $scriptAsSelect(handle: number, connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { throw ni(); } + $scriptAsCreate(handle: number, connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { throw ni(); } + $scriptAsUpdate(handle: number, connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { throw ni(); } + $scriptAsInsert(handle: number, connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { throw ni(); } + $scriptAsDelete(handle: number, connectionUri: string, metadata: data.ObjectMetadata, paramDetails: data.ScriptingParamDetails): Thenable { throw ni(); } + + /** + * Cancels the currently running query for a URI + */ + $cancelQuery(handle: number, ownerUri: string): Thenable { throw ni(); } + + /** + * Runs a query for a text selection inside a document + */ + $runQuery(handle: number, ownerUri: string, selection: data.ISelectionData, runOptions?: data.ExecutionPlanOptions): Thenable { throw ni(); } + /** + * Runs the current SQL statement query for a text document + */ + $runQueryStatement(handle: number, ownerUri: string, line: number, column: number): Thenable { throw ni(); } + /** + * Runs a query for a provided query + */ + $runQueryString(handle: number, ownerUri: string, queryString: string): Thenable { throw ni(); } + /** + * Runs a query for a provided query and returns result + */ + $runQueryAndReturn(handle: number, ownerUri: string, queryString: string): Thenable { throw ni(); } + /** + * Gets a subset of rows in a result set in order to display in the UI + */ + $getQueryRows(handle: number, rowData: data.QueryExecuteSubsetParams): Thenable { throw ni(); } + + /** + * Disposes the cached information regarding a query + */ + $disposeQuery(handle: number, ownerUri: string): Thenable { throw ni(); } + + /** + * Refreshes the IntelliSense cache + */ + $rebuildIntelliSenseCache(handle: number, ownerUri: string): Thenable { throw ni(); } + + /** + * Callback when a query has completed + */ + $onQueryComplete(handle: number, result: data.QueryExecuteCompleteNotificationResult): void { throw ni(); } + /** + * Callback when a batch has started. This enables the UI to display when batch execution has started + */ + $onBatchStart(handle: number, batchInfo: data.QueryExecuteBatchNotificationParams): void { throw ni(); } + /** + * Callback when a batch is complete. This includes updated information on result sets, time to execute, and + * other relevant batch information + */ + $onBatchComplete(handle: number, batchInfo: data.QueryExecuteBatchNotificationParams): void { throw ni(); } + /** + * Callback when a result set has been returned from query execution and can be displayed + */ + $onResultSetComplete(handle: number, resultSetInfo: data.QueryExecuteResultSetCompleteNotificationParams): void { throw ni(); } + /** + * Callback when a message generated during query execution is issued + */ + $onQueryMessage(handle: number, message: data.QueryExecuteMessageParams): void { throw ni(); } + + /** + * Requests saving of the results from a result set into a specific format (CSV, JSON, Excel) + */ + $saveResults(handle: number, requestParams: data.SaveResultsRequestParams): Thenable { throw ni(); } + + /** + * Commits all pending edits in an edit session + */ + $commitEdit(handle: number, ownerUri: string): Thenable { throw ni(); } + + /** + * Creates a new row in the edit session + */ + $createRow(handle: number, ownerUri: string): Thenable { throw ni(); } + + /** + * Marks the selected row for deletion in the edit session + */ + $deleteRow(handle: number, ownerUri: string, rowId: number): Thenable { throw ni(); } + + /** + * Initializes a new edit data session for the requested table/view + */ + $initializeEdit(handle: number, ownerUri: string, schemaName: string, objectName: string, objectType: string, rowLimit: number): Thenable { throw ni(); } + + /** + * Reverts any pending changes for the requested cell and returns the original value + */ + $revertCell(handle: number, ownerUri: string, rowId: number, columnId: number): Thenable { throw ni(); } + + /** + * Reverts any pending changes for the requested row + */ + $revertRow(handle: number, ownerUri: string, rowId: number): Thenable { throw ni(); } + + /** + * Updates a cell value in the requested row. Returns if there are any corrections to the value + */ + $updateCell(handle: number, ownerUri: string, rowId: number, columId: number, newValue: string): Thenable { throw ni(); } + + /** + * Gets a subset of rows in a result set, merging pending edit changes in order to display in the UI + */ + $getEditRows(handle: number, rowData: data.EditSubsetParams): Thenable { throw ni(); } + + /** + * Diposes an initialized edit session and cleans up pending edits + */ + $disposeEdit(handle: number, ownerUri: string): Thenable { throw ni(); } + + /** + * Create a new database on the provided connection + */ + $createDatabase(handle: number, connectionUri: string, database: data.DatabaseInfo): Thenable { throw ni(); } + + /** + * Get the default database prototype + */ + $getDefaultDatabaseInfo(handle: number, connectionUri: string): Thenable { throw ni(); } + + /** + * Get the database info + */ + $getDatabaseInfo(handle: number, connectionUri: string): Thenable { throw ni(); } + + /** + * Create a new login on the provided connection + */ + $createLogin(handle: number, connectionUri: string, login: data.LoginInfo): Thenable { throw ni(); } + + /** + * Backup a database + */ + $backup(handle: number, connectionUri: string, backupInfo: { [key: string]: any }, taskExecutionMode: data.TaskExecutionMode): Thenable { throw ni(); } + + /** + * Get the extended database prototype + */ + $getBackupConfigInfo(handle: number, connectionUri: string): Thenable { throw ni(); } + + /** + * Restores a database + */ + $restore(handle: number, connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { throw ni(); } + + /** + * Gets a plan for restoring a database + */ + $getRestorePlan(handle: number, connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { throw ni(); } + + /** + * Cancels a plan + */ + $cancelRestorePlan(handle: number, connectionUri: string, restoreInfo: data.RestoreInfo): Thenable { throw ni(); } + + /** + * Gets restore config Info + */ + $getRestoreConfigInfo(handle: number, connectionUri: string): Thenable { throw ni(); } + + + /** + * Open a file browser + */ + $openFileBrowser(handle: number, ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable { throw ni(); } + + + /** + * Expand a folder node + */ + $expandFolderNode(handle: number, ownerUri: string, expandPath: string): Thenable { throw ni(); } + + /** + * Validate selected file paths + */ + $validateFilePaths(handle: number, ownerUri: string, serviceType: string, selectedFiles: string[]): Thenable { throw ni(); } + + /** + * Close file browser + */ + $closeFileBrowser(handle: number, ownerUri: string): Thenable { throw ni(); } +} + + +/** + * ResourceProvider extension host class. + */ +export abstract class ExtHostResourceProviderShape { + /** + * Create a firewall rule + */ + $createFirewallRule(handle: number, account: data.Account, firewallRuleInfo: data.FirewallRuleInfo): Thenable { throw ni(); } + + /** + * Handle firewall rule + */ + $handleFirewallRule(handle: number, errorCode: number, errorMessage: string, connectionTypeId: string): Thenable { throw ni(); } + +} + +/** + * Credential Management extension host class. + */ +export abstract class ExtHostCredentialManagementShape { + $saveCredential(credentialId: string, password: string): Thenable { throw ni(); } + + $readCredential(credentialId: string): Thenable { throw ni(); } + + $deleteCredential(credentialId: string): Thenable { throw ni(); } +} + +/** + * Serialization provider extension host class. + */ +export abstract class ExtHostSerializationProviderShape { + $saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable { throw ni(); } +} + +export abstract class MainThreadAccountManagementShape { + $registerAccountProvider(providerMetadata: data.AccountProviderMetadata, handle: number): Thenable { throw ni(); } + $unregisterAccountProvider(handle: number): Thenable { throw ni(); } + + $performOAuthAuthorization(url: string, silent: boolean): Thenable { throw ni(); } +} + +export abstract class MainThreadResourceProviderShape { + $registerResourceProvider(providerMetadata: data.ResourceProviderMetadata, handle: number): Thenable { throw ni(); } + $unregisterResourceProvider(handle: number): Thenable { throw ni(); } +} + +export abstract class MainThreadDataProtocolShape { + $registerProvider(providerId: string, handle: number): TPromise { throw ni(); } + $unregisterProvider(handle: number): TPromise { throw ni(); } + $onConnectionComplete(handle: number, connectionInfoSummary: data.ConnectionInfoSummary): void { throw ni(); } + $onIntelliSenseCacheComplete(handle: number, connectionUri: string): void { throw ni(); } + $onConnectionChangeNotification(handle: number, changedConnInfo: data.ChangedConnectionInfo): void { throw ni(); } + $onQueryComplete(handle: number, result: data.QueryExecuteCompleteNotificationResult): void { throw ni(); } + $onBatchStart(handle: number, batchInfo: data.QueryExecuteBatchNotificationParams): void { throw ni(); } + $onBatchComplete(handle: number, batchInfo: data.QueryExecuteBatchNotificationParams): void { throw ni(); } + $onResultSetComplete(handle: number, resultSetInfo: data.QueryExecuteResultSetCompleteNotificationParams): void { throw ni(); } + $onQueryMessage(handle: number, message: data.QueryExecuteMessageParams): void { throw ni(); } + $onObjectExplorerSessionCreated(handle: number, message: data.ObjectExplorerSession): void { throw ni(); } + $onObjectExplorerNodeExpanded(handle: number, message: data.ObjectExplorerExpandInfo): void { throw ni(); } + $onTaskCreated(handle: number, sessionResponse: data.TaskInfo): void { throw ni(); } + $onTaskStatusChanged(handle: number, sessionResponse: data.TaskProgressInfo): void { throw ni(); } + $onFileBrowserOpened(handle: number, response: data.FileBrowserOpenedParams): void { throw ni(); } + $onFolderNodeExpanded(handle: number, response: data.FileBrowserExpandedParams): void { throw ni(); } + $onFilePathsValidated(handle: number, response: data.FileBrowserValidatedParams): void { throw ni(); } + $onScriptingComplete(handle: number, message: data.ScriptingCompleteResult): void { throw ni(); } + + /** + * Callback when a session has completed initialization + */ + $onEditSessionReady(handle: number, ownerUri: string, success: boolean, message: string) { throw ni(); } +} + +export abstract class MainThreadCredentialManagementShape { + $registerCredentialProvider(handle: number): TPromise { throw ni(); } + $unregisterCredentialProvider(handle: number): TPromise { throw ni(); } +} + +export abstract class MainThreadSerializationProviderShape { + $registerSerializationProvider(handle: number): TPromise { throw ni(); } + $unregisterSerializationProvider(handle: number): TPromise { throw ni(); } +} + +// export class SqlInstanceCollection { +// private _items: { [id: string]: any; }; + +// constructor() { +// this._items = Object.create(null); +// } + +// public define(id: ProxyIdentifier): InstanceSetter { +// let that = this; +// return new class { +// set(value: T): R { +// that._set(id, value); +// return value; +// } +// }; +// } + +// _set(id: ProxyIdentifier, value: T): void { +// this._items[id.id] = value; +// } + +// public finish(isMain: boolean, threadService: IThreadService): void { +// let expected = (isMain ? SqlMainContext : SqlExtHostContext); +// Object.keys(expected).forEach((key) => { +// let id = expected[key]; +// let value = this._items[id.id]; + +// if (!value) { +// throw new Error(`Missing actor ${key} (isMain: ${id.isMain}, id: ${id.id})`); +// } +// threadService.set(id, value); +// }); +// } +// } + +function ni() { return new Error('Not implemented'); } + +// --- proxy identifiers + +export const SqlMainContext = { + // SQL entries + MainThreadAccountManagement: createMainId('MainThreadAccountManagement'), + MainThreadCredentialManagement: createMainId('MainThreadCredentialManagement'), + MainThreadDataProtocol: createMainId('MainThreadDataProtocol'), + MainThreadSerializationProvider: createMainId('MainThreadSerializationProvider'), + MainThreadResourceProvider: createMainId('MainThreadResourceProvider') +}; + +export const SqlExtHostContext = { + ExtHostAccountManagement: createExtId('ExtHostAccountManagement'), + ExtHostCredentialManagement: createExtId('ExtHostCredentialManagement'), + ExtHostDataProtocol: createExtId('ExtHostDataProtocol'), + ExtHostSerializationProvider: createExtId('ExtHostSerializationProvider'), + ExtHostResourceProvider: createExtId('ExtHostResourceProvider') +}; diff --git a/src/sql/workbench/api/node/sqlExtHostTypes.ts b/src/sql/workbench/api/node/sqlExtHostTypes.ts new file mode 100644 index 0000000000..fddf46992c --- /dev/null +++ b/src/sql/workbench/api/node/sqlExtHostTypes.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +// SQL added extension host types +export enum ServiceOptionType { + string = 0, + multistring = 1, + password = 2, + number = 3, + category = 4, + boolean = 5, + object = 6 +} + +export enum ConnectionOptionSpecialType { + serverName = 0, + databaseName = 1, + authType = 2, + userName = 3, + password = 4, + appName = 5 +} + + +export enum MetadataType { + Table = 0, + View = 1, + SProc = 2, + Function = 3 +} + +export enum EditRowState { + clean = 0, + dirtyInsert = 1, + dirtyDelete = 2, + dirtyUpdate = 3 +} + + +export enum TaskStatus { + notStarted = 0, + inProgress = 1, + succeeded = 2, + succeededWithWarning = 3, + failed = 4, + canceled = 5 +} + +export enum TaskExecutionMode { + execute = 0, + script = 1, + executeAndScript = 2, +} + +export enum ScriptOperation { + Select = 0, + Create = 1, + Insert = 2, + Update = 3, + Delete = 4 +} \ No newline at end of file diff --git a/src/sql/workbench/common/actions.contribution.ts b/src/sql/workbench/common/actions.contribution.ts new file mode 100644 index 0000000000..d3900a8455 --- /dev/null +++ b/src/sql/workbench/common/actions.contribution.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { registerTask } from 'sql/platform/tasks/taskRegistry'; +import * as Actions from './actions'; + +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import * as nls from 'vs/nls'; + +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { ShowCurrentReleaseNotesAction, ProductContribution } from 'sql/workbench/update/releaseNotes'; + +const backupSchema: IJSONSchema = { + description: nls.localize('carbon.actions.back', 'Open up backup dialog'), + type: 'null', + default: null +}; + +const restoreSchema: IJSONSchema = { + description: nls.localize('carbon.actions.restore', 'Open up restore dialog'), + type: 'null', + default: null +}; + +const newQuerySchema: IJSONSchema = { + description: nls.localize('carbon.actions.newQuery', 'Open a new query window'), + type: 'null', + default: null +}; + +const configureDashboardSchema: IJSONSchema = { + description: nls.localize('carbon.actions.configureDashboard', 'Configure the Management Dashboard'), + type: 'null', + default: null +}; + +registerTask('backup', '', backupSchema, Actions.BackupAction); +registerTask('restore', '', restoreSchema, Actions.RestoreAction); +registerTask('new-query', '', newQuerySchema, Actions.NewQueryAction); +registerTask('configure-dashboard', '', configureDashboardSchema, Actions.ConfigureDashboardAction); + +// add product update and release notes contributions +Registry.as(WorkbenchExtensions.Workbench) +.registerWorkbenchContribution(ProductContribution); + +Registry.as(ActionExtensions.WorkbenchActions) + .registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), 'Show Getting Started'); diff --git a/src/sql/workbench/common/actions.ts b/src/sql/workbench/common/actions.ts new file mode 100644 index 0000000000..b0836ed50b --- /dev/null +++ b/src/sql/workbench/common/actions.ts @@ -0,0 +1,369 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import * as TaskUtilities from './taskUtilities'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import { IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import { IScriptingService } from 'sql/services/scripting/scriptingService'; +import { IDisasterRecoveryUiService, IRestoreDialogController } from 'sql/parts/disasterRecovery/common/interfaces'; +import { IAngularEventingService, AngularEventType } from 'sql/services/angularEventing/angularEventingService'; +import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces'; +import { IAdminService } from 'sql/parts/admin/common/adminService'; +import * as Constants from 'sql/common/constants'; +import { ObjectMetadata } from 'data'; +import { ScriptOperation } from 'sql/workbench/common/taskUtilities'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; + +import * as nls from 'vs/nls'; + +export class TaskAction extends Action { + constructor(id: string, label: string, private _icon: string) { + super(id, label); + } + + get icon(): string { + return this._icon; + } +} + +export interface BaseActionContext extends ITaskActionContext { + uri?: string; + object?: ObjectMetadata; + connInfo?: ConnectionManagementInfo; +} + +export interface ITaskActionContext { + profile: IConnectionProfile; +} + +export interface InsightActionContext extends BaseActionContext { + insight: IInsightsConfig; +} + +// --- actions +export class NewQueryAction extends TaskAction { + public static ID = 'newQuery'; + public static LABEL = nls.localize('newQuery', 'New Query'); + public static ICON = 'new-query'; + + constructor( + id: string, label: string, icon: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService + ) { + super(id, label, icon); + } + + public run(actionContext: ITaskActionContext): TPromise { + return new TPromise((resolve, reject) => { + TaskUtilities.newQuery( + actionContext.profile, + this._connectionManagementService, + this._queryEditorService + ).then( + result => { + resolve(true); + }, + error => { + resolve(false); + } + ); + }); + } +} + +export class ScriptSelectAction extends Action { + public static ID = 'selectTop'; + public static LABEL = nls.localize('scriptSelect', 'Select Top 1000'); + + constructor( + id: string, label: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + @IScriptingService protected _scriptingService: IScriptingService + ) { + super(id, label); + } + + public run(actionContext: BaseActionContext): TPromise { + return new TPromise((resolve, reject) => { + TaskUtilities.scriptSelect( + actionContext.profile, + actionContext.object, + actionContext.uri, + this._connectionManagementService, + this._queryEditorService, + this._scriptingService + ).then( + result => { + resolve(true); + }, + error => { + resolve(false); + }); + }); + } +} + +export class EditDataAction extends Action { + public static ID = 'editData'; + public static LABEL = nls.localize('editData', 'Edit Data'); + + constructor( + id: string, label: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService + ) { + super(id, label); + } + + public run(actionContext: BaseActionContext): TPromise { + return new TPromise((resolve, reject) => { + TaskUtilities.editData( + actionContext.profile, + actionContext.object.name, + actionContext.object.schema, + this._connectionManagementService, + this._queryEditorService + ).then( + result => { + resolve(true); + }, + error => { + resolve(false); + } + ); + }); + } +} + +export class ScriptCreateAction extends Action { + public static ID = 'scriptCreate'; + public static LABEL = nls.localize('scriptCreate', "Script as Create"); + + constructor( + id: string, label: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + @IScriptingService protected _scriptingService: IScriptingService, + @IErrorMessageService protected _errorMessageService: IErrorMessageService + ) { + super(id, label); + } + + public run(actionContext: BaseActionContext): TPromise { + return new TPromise((resolve, reject) => { + TaskUtilities.script( + actionContext.profile, + actionContext.object, + actionContext.uri, + this._connectionManagementService, + this._queryEditorService, + this._scriptingService, + ScriptOperation.Create, + this._errorMessageService + ).then( + result => { + resolve(true); + }, + error => { + resolve(false); + } + ); + }); + } +} + +export class ScriptDeleteAction extends Action { + public static ID = 'scriptDelete'; + public static LABEL = nls.localize('scriptDelete', 'Script as Delete'); + + constructor( + id: string, label: string, + @IQueryEditorService protected _queryEditorService: IQueryEditorService, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + @IScriptingService protected _scriptingService: IScriptingService, + @IErrorMessageService protected _errorMessageService: IErrorMessageService + ) { + super(id, label); + } + + public run(actionContext: BaseActionContext): TPromise { + return new TPromise((resolve, reject) => { + TaskUtilities.script( + actionContext.profile, + actionContext.object, + actionContext.uri, + this._connectionManagementService, + this._queryEditorService, + this._scriptingService, + ScriptOperation.Delete, + this._errorMessageService + ).then( + result => { + resolve(true); + }, + error => { + resolve(false); + } + ); + }); + } +} + +export class BackupAction extends TaskAction { + public static ID = Constants.BackupFeatureName; + public static LABEL = nls.localize('backup', 'Backup'); + public static ICON = Constants.BackupFeatureName; + + constructor( + id: string, label: string, icon: string, + @IDisasterRecoveryUiService protected _disasterRecoveryService: IDisasterRecoveryUiService + ) { + super(id, label, icon); + } + + run(actionContext: ITaskActionContext): TPromise { + return new TPromise((resolve, reject) => { + TaskUtilities.showBackup( + actionContext.profile, + this._disasterRecoveryService, + ).then( + result => { + resolve(true); + }, + error => { + resolve(false); + } + ); + }); + } +} + +export class RestoreAction extends TaskAction { + public static ID = Constants.RestoreFeatureName; + public static LABEL = nls.localize('restore', 'Restore'); + public static ICON = Constants.RestoreFeatureName; + + constructor( + id: string, label: string, icon: string, + @IRestoreDialogController protected _restoreService: IRestoreDialogController + ) { + super(id, label, icon); + } + + run(actionContext: ITaskActionContext): TPromise { + return new TPromise((resolve, reject) => { + TaskUtilities.showRestore( + actionContext.profile, + this._restoreService + ).then( + result => { + resolve(true); + }, + error => { + resolve(false); + } + ); + }); + } +} + +export class ManageAction extends Action { + public static ID = 'manage'; + public static LABEL = nls.localize('manage', 'Manage'); + + constructor( + id: string, label: string, + @IConnectionManagementService protected _connectionManagementService: IConnectionManagementService, + @IAngularEventingService protected _angularEventingService: IAngularEventingService + ) { + super(id, label); + } + + run(actionContext: BaseActionContext): TPromise { + let self = this; + return new TPromise((resolve, reject) => { + self._connectionManagementService.connect(actionContext.profile, actionContext.uri, { showDashboard: true, saveTheConnection: false, params: undefined, showConnectionDialogOnError: false, showFirewallRuleOnError: true }).then( + () => { + self._angularEventingService.sendAngularEvent(actionContext.uri, AngularEventType.NAV_DATABASE); + resolve(true); + }, + error => { + resolve(error); + } + ); + }); + } +} + +export class InsightAction extends Action { + public static ID = 'showInsight'; + public static LABEL = nls.localize('showDetails', 'Show Details'); + + constructor( + id: string, label: string, + @IInsightsDialogService protected _insightsDialogService: IInsightsDialogService + ) { + super(id, label); + } + + run(actionContext: InsightActionContext): TPromise { + let self = this; + return new TPromise((resolve, reject) => { + self._insightsDialogService.show(actionContext.insight, actionContext.profile); + resolve(true); + }); + } +} + +export class NewDatabaseAction extends TaskAction { + public static ID = 'newDatabase'; + public static LABEL = nls.localize('newDatabase', 'New Database'); + public static ICON = 'new-database'; + + constructor( + id: string, label: string, icon: string, + @IAdminService private _adminService: IAdminService, + @IErrorMessageService private _errorMessageService: IErrorMessageService, + ) { + super(id, label, icon); + } + + run(actionContext: ITaskActionContext): TPromise { + return new TPromise((resolve, reject) => { + TaskUtilities.showCreateDatabase(actionContext.profile, this._adminService, this._errorMessageService); + }); + } +} + +export class ConfigureDashboardAction extends TaskAction { + public static ID = 'configureDashboard'; + public static LABEL = nls.localize('configureDashboard', 'Configure'); + public static ICON = 'configure-dashboard'; + private static readonly configHelpUri = 'https://aka.ms/sqldashboardconfig'; + constructor( + id: string, label: string, icon: string, + @IWindowsService private _windowsService + ) { + super(id, label, icon); + } + + run(actionContext: ITaskActionContext): TPromise { + return new TPromise((resolve, reject) => { + this._windowsService.openExternal(ConfigureDashboardAction.configHelpUri).then((result) => { + resolve(result); + }, err => { + resolve(err); + }); + }); + } +} diff --git a/src/sql/workbench/common/sqlWorkbenchUtils.ts b/src/sql/workbench/common/sqlWorkbenchUtils.ts new file mode 100644 index 0000000000..c85db01a06 --- /dev/null +++ b/src/sql/workbench/common/sqlWorkbenchUtils.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import ConnectionConstants = require('sql/parts/connection/common/constants'); +import { QueryInput } from 'sql/parts/query/common/queryInput'; + +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IEditorInput } from 'vs/platform/editor/common/editor'; +import URI from 'vs/base/common/uri'; + +/** + * Gets the 'sql' configuration section for use in looking up settings. Note that configs under + * 'mssql' or other sections are not available from this. + * + * @export + * @param {IWorkspaceConfigurationService} workspaceConfigService + * @param {string} sectionName + * @returns {*} + */ +export function getSqlConfigSection(workspaceConfigService: IWorkspaceConfigurationService, sectionName: string): any { + let config = workspaceConfigService.getConfiguration(ConnectionConstants.sqlConfigSectionName); + return config ? config[sectionName] : {}; +} + +export function getSqlConfigValue(workspaceConfigService: IWorkspaceConfigurationService, configName: string): T { + let config = workspaceConfigService.getConfiguration(ConnectionConstants.sqlConfigSectionName); + return config[configName]; +} + +/** + * Executes a copy operation for an arbitrary string, by creating a temp div, copying + * the text to it, and calling copy on the document. This will clear any existing selection + * so if being called where selection state needs to be preserved, it's recommended to + * cache the existing selection first and re-set it after this method is called + * + * @export + * @param {string} text + */ +export function executeCopy(text: string): void { + let input = document.createElement('textarea'); + document.body.appendChild(input); + input.value = text; + input.style.position = 'absolute'; + input.style.bottom = '100%'; + input.focus(); + input.select(); + document.execCommand('copy'); + input.remove(); +} + +export function getEditorUri(input: IEditorInput): string{ + let uri: URI; + if (input instanceof QueryInput) { + let queryCast: QueryInput = input; + if (queryCast) { + uri = queryCast.getResource(); + } + } + + if (uri) { + return uri.toString(); + } + return undefined; +} diff --git a/src/sql/workbench/common/taskUtilities.ts b/src/sql/workbench/common/taskUtilities.ts new file mode 100644 index 0000000000..0d8c98f4b8 --- /dev/null +++ b/src/sql/workbench/common/taskUtilities.ts @@ -0,0 +1,374 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { + IConnectableInput, IConnectionManagementService, + IConnectionCompletionOptions, ConnectionType, IErrorMessageService, + RunQueryOnConnectionMode, IConnectionResult +} from 'sql/parts/connection/common/connectionManagement'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { IScriptingService } from 'sql/services/scripting/scriptingService'; +import { EditDataInput } from 'sql/parts/editData/common/editDataInput'; +import { IAdminService } from 'sql/parts/admin/common/adminService'; +import { IDisasterRecoveryUiService, IRestoreDialogController } from 'sql/parts/disasterRecovery/common/interfaces'; +import { IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import Severity from 'vs/base/common/severity'; +import data = require('data'); +import nls = require('vs/nls'); +import os = require('os'); +import path = require('path'); + +// map for the version of SQL Server (default is 140) +let scriptCompatibilityOptionMap = new Map(); +scriptCompatibilityOptionMap.set(90, "Script90Compat"); +scriptCompatibilityOptionMap.set(100, "Script100Compat"); +scriptCompatibilityOptionMap.set(105, "Script105Compat"); +scriptCompatibilityOptionMap.set(110, "Script110Compat"); +scriptCompatibilityOptionMap.set(120, "Script120Compat"); +scriptCompatibilityOptionMap.set(130, "Script130Compat"); +scriptCompatibilityOptionMap.set(140, "Script140Compat"); + +// map for the target database engine edition (default is Enterprise) +let targetDatabaseEngineEditionMap = new Map(); +targetDatabaseEngineEditionMap.set(0, "SqlServerEnterpriseEdition"); +targetDatabaseEngineEditionMap.set(1, "SqlServerPersonalEdition"); +targetDatabaseEngineEditionMap.set(2, "SqlServerStandardEdition"); +targetDatabaseEngineEditionMap.set(3, "SqlServerEnterpriseEdition"); +targetDatabaseEngineEditionMap.set(4, "SqlServerExpressEdition"); +targetDatabaseEngineEditionMap.set(5, "SqlAzureDatabaseEdition"); +targetDatabaseEngineEditionMap.set(6, "SqlDatawarehouseEdition"); +targetDatabaseEngineEditionMap.set(7, "SqlServerStretchEdition"); + +// map for object types for scripting +let objectScriptMap = new Map(); +objectScriptMap.set("Table", "Table"); +objectScriptMap.set("View", "View"); +objectScriptMap.set("StoredProcedure", "Procedure"); +objectScriptMap.set("UserDefinedFunction", "Function"); +objectScriptMap.set("UserDefinedDataType", "Type"); +objectScriptMap.set("User", "User"); +objectScriptMap.set("Default", "Default"); +objectScriptMap.set("Rule", "Rule"); +objectScriptMap.set("DatabaseRole", "Role"); +objectScriptMap.set("ApplicationRole", "Application Role"); +objectScriptMap.set("SqlAssembly", "Assembly"); +objectScriptMap.set("DdlTrigger", "Trigger"); +objectScriptMap.set("Synonym", "Synonym"); +objectScriptMap.set("XmlSchemaCollection", "Xml Schema Collection"); +objectScriptMap.set("Schema", "Schema"); +objectScriptMap.set("PlanGuide", "sp_create_plan_guide"); +objectScriptMap.set("UserDefinedType", "Type"); +objectScriptMap.set("UserDefinedAggregate", "Aggregate"); +objectScriptMap.set("FullTextCatalog", "Fulltext Catalog"); +objectScriptMap.set("UserDefinedTableType", "Type"); +objectScriptMap.set("MaterializedView", "Materialized View"); + +export enum ScriptOperation { + Select = 0, + Create = 1, + Insert = 2, + Update = 3, + Delete = 4 +} + +export function GetScriptOperationName(operation: ScriptOperation) { + let defaultName: string = ScriptOperation[operation]; + switch(operation) { + case ScriptOperation.Select: + return nls.localize('selectOperationName', 'Select'); + case ScriptOperation.Create: + return nls.localize('createOperationName', 'Create'); + case ScriptOperation.Insert: + return nls.localize('insertOperationName', 'Insert'); + case ScriptOperation.Update: + return nls.localize('updateOperationName', 'Update'); + case ScriptOperation.Delete: + return nls.localize('deleteOperationName', 'Delete'); + default: + // return the raw, non-localized string name + return defaultName; + } +} + +export function connectIfNotAlreadyConnected(connectionProfile: IConnectionProfile, connectionService: IConnectionManagementService): Promise { + return new Promise((resolve, reject) => { + let connectionID = connectionService.getConnectionId(connectionProfile); + let uri: string = connectionService.getFormattedUri(connectionID, connectionProfile); + if (!connectionService.isConnected(uri)) { + let options: IConnectionCompletionOptions = { + params: { connectionType: ConnectionType.editor, runQueryOnCompletion: RunQueryOnConnectionMode.executeQuery, input: undefined }, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: false, + showFirewallRuleOnError: true + }; + connectionService.connect(connectionProfile, uri, options).then(() => { + setTimeout(function () { + resolve(); + }, 2000); + }).catch(connectionError => { + reject(connectionError); + }); + } else { + resolve(); + } + }); +} + +/** + * Select the top rows from an object + */ +export function scriptSelect(connectionProfile: IConnectionProfile, metadata: data.ObjectMetadata, ownerUri: string, connectionService: IConnectionManagementService, queryEditorService: IQueryEditorService, scriptingService: IScriptingService): Promise { + return new Promise((resolve, reject) => { + connectIfNotAlreadyConnected(connectionProfile, connectionService).then(connectionResult => { + let paramDetails: data.ScriptingParamDetails = getScriptingParamDetails(connectionService, ownerUri, metadata); + scriptingService.script(ownerUri, metadata, ScriptOperation.Select, paramDetails).then(result => { + if (result.script) { + queryEditorService.newSqlEditor(result.script).then((owner: IConnectableInput) => { + // Connect our editor to the input connection + let options: IConnectionCompletionOptions = { + params: { connectionType: ConnectionType.editor, runQueryOnCompletion: RunQueryOnConnectionMode.executeQuery, input: owner }, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: true + }; + connectionService.connect(connectionProfile, owner.uri, options).then(() => { + resolve(); + }); + }).catch(editorError => { + reject(editorError); + }); + } else { + let errMsg: string = nls.localize('scriptSelectNotFound', 'No script was returned when calling select script on object '); + reject(errMsg.concat(metadata.metadataTypeName)); + } + }, scriptError => { + reject(scriptError); + }) + }); + }); +} + +/** + * Opens a new Edit Data session + */ +export function editData(connectionProfile: IConnectionProfile, tableName: string, schemaName: string, connectionService: IConnectionManagementService, queryEditorService: IQueryEditorService): Promise { + return new Promise((resolve) => { + queryEditorService.newEditDataEditor(schemaName, tableName).then((owner: EditDataInput) => { + // Connect our editor + let options: IConnectionCompletionOptions = { + params: { connectionType: ConnectionType.editor, runQueryOnCompletion: RunQueryOnConnectionMode.none, input: owner }, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: true + }; + connectionService.connect(connectionProfile, owner.uri, options).then(() => { + resolve(); + }); + }); + }); +} + +/** + * Script the object as a statement based on the provided action (except Select) + */ +export function script(connectionProfile: IConnectionProfile, metadata: data.ObjectMetadata, ownerUri: string, + connectionService: IConnectionManagementService, + queryEditorService: IQueryEditorService, + scriptingService: IScriptingService, + operation: ScriptOperation, + errorMessageService: IErrorMessageService): Promise { + return new Promise((resolve, reject) => { + connectIfNotAlreadyConnected(connectionProfile, connectionService).then(connectionResult => { + let paramDetails = getScriptingParamDetails(connectionService, ownerUri, metadata); + scriptingService.script(ownerUri, metadata, operation, paramDetails).then(result => { + if (result) { + let script: string = result.script; + let startPos: number = 0; + if (connectionProfile.providerName === "MSSQL") { + startPos = getStartPos(script, operation, metadata.metadataTypeName); + } + if (startPos >= 0) { + script = script.substring(startPos); + queryEditorService.newSqlEditor(script, connectionProfile.providerName).then(() => { + resolve(); + }).catch(editorError => { + reject(editorError); + }); + } + else { + let scriptNotFoundMsg = nls.localize('scriptNotFoundForObject', 'No script was returned when scripting as {0} on object {1}', + GetScriptOperationName(operation), metadata.metadataTypeName); + let operationResult = scriptingService.getOperationFailedResult(result.operationId); + if (operationResult && operationResult.hasError && operationResult.errorMessage) { + scriptNotFoundMsg = operationResult.errorMessage; + } + if (errorMessageService) { + let title = nls.localize('scriptingFailed', 'Scripting Failed'); + errorMessageService.showDialog(Severity.Error, title, scriptNotFoundMsg); + } + reject(scriptNotFoundMsg); + } + } else { + reject(nls.localize('scriptNotFound', 'No script was returned when scripting as {0}', GetScriptOperationName(operation))); + } + }, scriptingError => { + reject(scriptingError); + }); + }).catch(connectionError => { + reject(connectionError); + }); + }); +} + +export function newQuery( + connectionProfile: IConnectionProfile, + connectionService: IConnectionManagementService, + queryEditorService: IQueryEditorService, + sqlContent?: string, + executeOnOpen: RunQueryOnConnectionMode = RunQueryOnConnectionMode.none +): Promise { + return new Promise((resolve) => { + queryEditorService.newSqlEditor(sqlContent).then((owner: IConnectableInput) => { + // Connect our editor to the input connection + let options: IConnectionCompletionOptions = { + params: { connectionType: ConnectionType.editor, runQueryOnCompletion: executeOnOpen, input: owner }, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: true + }; + connectionService.connect(connectionProfile, owner.uri, options).then(() => { + resolve(); + }); + }); + }); +} + +export function replaceConnection(oldUri: string, newUri: string, connectionService: IConnectionManagementService): Promise { + return new Promise((resolve, reject) => { + let defaultResult: IConnectionResult = { + connected: false, + errorMessage: undefined, + errorCode: undefined + }; + if (connectionService) { + let connectionProfile = connectionService.getConnectionProfile(oldUri); + if (connectionProfile) { + let options: IConnectionCompletionOptions = { + params: { connectionType: ConnectionType.editor, runQueryOnCompletion: RunQueryOnConnectionMode.none }, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: true + }; + connectionService.disconnect(oldUri).then(() => { + connectionService.connect(connectionProfile, newUri, options).then(result => { + resolve(result); + }, connectError => { + reject(connectError); + }); + }, disconnectError => { + reject(disconnectError); + }); + + } else { + resolve(defaultResult); + } + } else { + resolve(defaultResult); + } + }); +} + +export function showCreateDatabase( + connection: IConnectionProfile, + adminService: IAdminService, + errorMessageService: IErrorMessageService): Promise { + + return new Promise((resolve) => { + // show not implemented + errorMessageService.showDialog(Severity.Info, + 'Coming Soon', + 'This feature is not yet implemented. It will be available in an upcoming release.'); + + // adminService.showCreateDatabaseWizard(uri, connection); + }); +} + +export function showCreateLogin(uri: string, connection: IConnectionProfile, adminService: IAdminService): Promise { + return new Promise((resolve) => { + adminService.showCreateLoginWizard(uri, connection); + }); +} + +export function showBackup(connection: IConnectionProfile, disasterRecoveryUiService: IDisasterRecoveryUiService): Promise { + return new Promise((resolve) => { + disasterRecoveryUiService.showBackup(connection); + }); +} + +export function showRestore(connection: IConnectionProfile, restoreDialogService: IRestoreDialogController): Promise { + return new Promise((resolve) => { + restoreDialogService.showDialog(connection); + }); +} + +export function openInsight(query: IInsightsConfig, profile: IConnectionProfile, insightDialogService: IInsightsDialogService) { + insightDialogService.show(query, profile); +} + +/* Helper Methods */ +function getStartPos(script: string, operation: ScriptOperation, typeName: string): number { + let objectTypeName = objectScriptMap.get(typeName); + if (objectTypeName && script) { + let scriptTypeName = objectTypeName.toLowerCase(); + switch (operation) { + case (ScriptOperation.Create): + return script.toLowerCase().indexOf(`create ${scriptTypeName}`); + case (ScriptOperation.Delete): + return script.toLowerCase().indexOf(`drop ${scriptTypeName}`); + default: + /* script wasn't found for that object */ + return -1; + } + } else { + return -1; + } +} + + +function getScriptingParamDetails(connectionService: IConnectionManagementService, ownerUri: string, metadata: data.ObjectMetadata): data.ScriptingParamDetails { + let serverInfo: data.ServerInfo = getServerInfo(connectionService, ownerUri); + let paramDetails: data.ScriptingParamDetails = { + filePath: getFilePath(metadata), + scriptCompatibilityOption: scriptCompatibilityOptionMap[serverInfo.serverMajorVersion], + targetDatabaseEngineEdition: targetDatabaseEngineEditionMap[serverInfo.engineEditionId], + targetDatabaseEngineType: serverInfo.isCloud ? 'SqlAzure' : 'SingleInstance' + } + return paramDetails; +} + +function getFilePath(metadata: data.ObjectMetadata): string { + let schemaName: string = metadata.schema; + let objectName: string = metadata.name; + let timestamp = Date.now().toString(); + if (schemaName !== null) { + return path.join(os.tmpdir(), `${schemaName}.${objectName}_${timestamp}.txt`); + } else { + return path.join(os.tmpdir(), `${objectName}_${timestamp}.txt`); + } +} + +function getServerInfo(connectionService: IConnectionManagementService, ownerUri: string): data.ServerInfo { + let connection: ConnectionManagementInfo = connectionService.getConnectionInfo(ownerUri); + return connection.serverInfo; +} \ No newline at end of file diff --git a/src/sql/workbench/electron-browser/splashscreen/splash.png b/src/sql/workbench/electron-browser/splashscreen/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..0a9c0f65a4a3bcf414fadf0aacbfbbf22a4f348c GIT binary patch literal 325356 zcmYhjdpwi>{|9_+V=fFAIiK1kIuVj{rENx2DkM4NymAORr4pIhW`!C_>FAVH$RWpC zA!RB#B*#(-Ii!?Bp~L;&=li?w$Nfi-9u>9s^?n_mr>oNrySGb8s7gQ(BxOgVIzbSg z3_&90FdqC)@(n8j_(?pR<`xA(iaNp{7&|BJ83-amc2u(S@qsV=lXD069sOp}YxVi} zseLwYzU`k#edNfxv)NY6P)fd$Xm>nwW14?!nAEA%+|+c~_c-d6*Pj-vd+`g0w~ie+TpHh_ znS}n>e6pl&EcRZz*Q~CK_uN8>!*}lY$AXH-JIWI18}E(Xa33{L?Q;Ek^y!|GurK3% zrCAr79(27tw&U;KwxW?eSIjgz-5!SpeLW&~b7=lga_84Gi@$$)Cm0p17n@x5PJ8C{ zmHn2<()aA7*@*rR7sCmQe`5)gF`G|AJ?H2b2sd8Je^%|k9$A0NwAlM=^yX_je*T^M z_scH9&|as0I&~^!ws4i@wXnK9_@u<&|E>?cZ!xis{gLS76mh*P^MGne{$GQ4zm`Ab zJ}Eo)J`ugShxN05@17%1mX6xp>s^ubmlRu)P!XT@cjv#^J^!4Kz59{z;mxtQ9vnx! z>zr&x%6pw3x?LVmk6C&~G;@w>>^(0Ma6_%IGVEP_LGRS+;t&&B#htL8C0t#?jo|?~ z{^=cAp}yaS;**{v_c|&4dHw6lnSB~QZ4*}qtOcfW1G<&}ej5DyZD7qe)qi&($>-YN zd;k7C{+EPoo5#BSxqg6QYjQq$oH1T3#$(zo-k#;X`4|8D)+;&g*Vv?x@6W$sIW!BA z@s%_6jYqE(P_(YiLy-^>x2=r)Vn4%mfS@&DwD#`KJ~X>mGFklMUW(V zLkp}X4ia?=ZqvsCEWICo_`K(De?jzTgB}0QHOexl5k@-4Ptza5`mOeOKYHtxwe!DY z=yMD=vikU_!MBXsM+$kkjzOA30Monb?tsY_m{{-T;nRQ0^Rq6wbZg1+OFKPX3g*;* z9(=qQSa9UvnQxKGW9K>)M{;C%gYUm|}U|1xDQ!(i1`ckMZ%$Z-C)0{Q$5 z>51Agk@=hRuugeE!QT5uIlo%DDSK-hL~Z}<EqCd9C+WwM9vygQA{KvH}H`q1^Yfcan_L4 zkolB_?nUw>N#-RMKW5J^x$e9EQ-oy?aSQHkKN6aW7v5!Q+pvw zxz$WvJnxB?uX}_FI?y8cCvB06Z zA$_4__^In^_4+0qg0Iszt&vEd_&<04uCj1Vn)~4O!iu@^*2gvrrx%68?_XCX@iYg# zS73J@|8mSeY_|x`y68itk5`Sh&cNrQ; zoQo%Hy|&msn%G!^tUN_r>;0(&iF@z^YHt24@yS?CIWOxh&5{>=kxO1pw$w4Cnr%3V zG>OWu>&tt-liGs9xepbFPo6R>8FtVTWz!||r2G*r3LXuvUb8ge$oBc+*eL}S7=z+o z^C}-A{m@AIR)Rd7V}mgeibXU|&09~pzbCSua6I{EztFf3xtyu+xM#-4^%SVfCk zoqQ+mr$D@vcXT%fZo;?6-!c)bn%IZq^+zW$&;=Z0JWLypCb9*QcuL&v5X(hB2J-C6 zXJxIFhRo_@fhq>cojf1CwTPReK|x6@ssc@*OGa^g-WDi(TMI)~xvA{E zJ4f)GB(PjPC6*kEO2x0Xi=7W}r}~q-CBJuQVqHI%RpcuW3tlN<<;H`9i8*22TN-(8 zveB=94=QTy6lc6N&|qc{x9LXk91PZZ9th$9{Y_5owB@>#_Cc>Q$PU{;W6Yl}vwP4Mj3; z9C5hP8#Lq&H9uGQ$P%o_B7f$(Wv&gVYwdhfTBJmAw;J(4C!uETdmR+|!65Kl>wYug zbd$X@ifBbehwBuhJ+bni2dV;3Q0Ar3fpX#`+dNDrwb$bB*qeb?QMvT{5G!+8E0L~J zNNiT*{0h;#BeuU1YBu`!fkNLmvR4VaQ^yn4yL%ymtG9;83U4Urko=CNm>=oe2%7Ks z2TQ6;x1{(jbyqQ~N~nOIdrq1XGN}5e1U%w(iw}%o$8xht_I)*uq2h`CR-tSEuEr79 z$s}xYJ)(?5{9i9lvAM76RiV81S??yk0q$@7^;F^AwT$1eX1a^dP1^iJMqS>5|N5HS7qV zm03-7u~S(CHESx~H>GB6La<1x7dHxN@_pj0+I6-)EXQl7Tn&@ktXVBz{=Yi zegZ3#egTJ0*C`mi5ot|CZ~+lA=_VLrUM?Bbri7hA!5p4@bGq1uT(Kd_Xz zu)Zrlp5oC|v-XWt#l&?Ojvzyh{#b$`|GH2VwfCV}z6@d*k8Vg#HYUN2TVq4>56n@N z-&Y4-@ZxGi#O_hz^~TkZ_R|g98)DJCO8w=gcjK!zOYjlG`x*y>=0-C6is=-1{3{5z zyQEZc$V&e;d-zBcWR!50{O;JFDYiWscIh`y)Y=4^3BG+;^1iTR8bkJfcY!Z!tU$0G z(uh%=+(s+;8)E2X(Sx^@u>Flkr5QydosoQ=trX&?JRJV%w4&CFbub!&yJXm`27kAJ zdo6famf?`FXMA=L4e11uv1r-^2~ILeR)n%6Zkod6;_arP#vFl>hkw*PkMtWW#WEq* zbF=99+v*JBXN^l<0ue@LJxPgEG%fpaOTI7vY3kF(zULGEf^oAV`s1f!%Eybf_r+B?9 ze3J$1nY_+(d0yPlp;Q)nV8LTA($sb^&)fI$+cSEc2X2SP_~PDcf7b#P_`Xgoz<6`efd<42yw*?dlI~m z7#~PLLs|Gf^6w@}%R@QnE z@>&+$+iP_U!sD71ScI5{F5s@E2RJ3fr>PJe@x+^BR6bH)9d;bjoTtn@UjtE%BuP8I zNa*Qh$8+_d^ytG7Z1av?sDynkJc!SQ4fcFqFyhp!7}&y8){?CBeCmN#2dLVBStJMB zP5yHiqZmt08c};h7}^dwMQaN-lK4Dv==;{((@WMrc;I5ddceFk?f-~EowEc=oOIKy z?yGcLSVai!4vAU+(o<*ZZMTF@DF2Jui>F9`zq4xJ<2?}k9V~?7<>E++cMjmIO?c~k3|Qdf-d1SDlAdcWNhannZJLSkyQUX(K68RQC zsIl_ez8cA`2y>%8`?gXSOd)avKOUrU(TayIqz&*#8S4z&RToHbKH|2?jI>@%=KkUR ziR0|dA?W*-KyKDJ`#4yRxHJXWLIAT2J0v5pl-jWCiRoT6Li)i2XQO%Sh79YBw?Bwf z!|64%4+OGMy76=^Oj#@a#Y0nT=fnlYCPZ=G0Yj2#HIzJP7hBeaz8t)%JM*QT+HsNh z0?F~>eycuxd3U!YSvMQW#&g3?hLwA9tswM^~c3PJ&2=R zQm+8}eIqsNeVn_Z)?q9uP&Dlpk>#dwfv@{o4x$*YNh5LZ@7T-{8qKg6*d8~S}!oXwVo>_#-=%@?F7bL_#fFjUbJwu0*rzA<0hiS`s$kf2F=n8WS*Th z!R}5%^iI|`@;KhcOkQC%srP4~4^1HaYVNE=ktq5$&N5zZG%Z68^!3^v+g;I+<@UAe z^;rb<=l%6?Ivci*gjj+YQUza}W3FD7Hb?1WAhw3uViaus(DqSM0Ff0utL_WCEgkR$ zgOj#qtBwri(woj^K{n#beB?DUgEYV_sPWVRy>2WP3$?1)4M`$beBr1 zym})|p(|kEia%g@l24|@qRyWZWErlR_jF=WEjwFPqWV;+Ml4!6eNR)^3!v+Ri3Oz_ zI?f&i-w=Az$2~k@_KqC;9HhOwMJ zJuTMFAkcNL3@~(`NwC6IpsmS*j1LIQ;fra$7aVluQ-M;3G-=_+bqq0VG9RWWzj4fl z>vQg1+z79GwbRi6Eb~jn2Dc|05r%w7XlDsBI#1lBOgXMwpvoXFuL9^O@JJ|}{&b48 z^)=-7%)lG$M?i!+SNd^_?K+s*8$%^1MNjT%WPv%*KF9^-R40{2WS!sh^r~U~ zbEcyTOIf~~-e_5eVHzL5Pc|s1+=3y#!D%H<2VS|ym>1pwWLiUsB(emhywxiVgb3r; z5g80vK@d_i? zl~>H#sF-~#SDJEp0Q=7M|Hf?cqRM4C_8&-Ceo;tR{iAWOjPFQ!OD)G3~mOwu5 zl^lsI!Vfbfm{S$^^N|=L3qt@~Q<7k<%{VYPg$P%73*shH==$)0BI>t|g8^h(gx49gLebhlWY9)=-cXUe=k%RTBxOGlQqh2n3?GSynBy`o!s@0y028>Y z6^Fcw-MH2pGK^cVele6z!EyO>vRp} zYJV<)(s0;9?&yOti91qYvJTvL)KwrW{lbaq@_C$FSVrc`OXg)7I#Q=%(O_JIGAlZ< zwWsl1()}x|2#z(k zx*6Px2L3^@xKnd$uq*2uhxYd-cO+B`Nhkst#0QTQ%zOvTcJMp(q=Uf?}gLQdg!-kBo({R0A z20F<$oUQ?sBRGk-(1dJ4y1Es|m%O?qa|2}#XmSDq2Z+t@maNFK24;AGcdtRn$8e1Q z3az`PWUWTtt4KU4)vN-{yM=YT*UsV^w!m%-4|G?nv<#N3GknkQ($cN_vTY2+z`On# z;`Vni=@#O)0@CXIY~QV)v9O)k+=z*<$4CAu{xtcfhA+XA|9lg+{x!}vl;a#RE_vQl z4%g3_?T>mbdHW6H(n(sXSZs{7zEmnu!c$ms{ z{U0k=AzE(-MngN_bTe#;+S4buy!EaBPnnX(@T5OO@HZP@0A3OYF#M0_7dF+B_O%EM z3#7qeb?zW;nT9O#zqDAiPVoYUm)UZl4a`+VhQq6pxFh?Vamby&8rJkGcLUZwuHH#& z*0?Jcxf3%f!x_PF$(unpg)Nmp3~d~mBSop5-Y^qVjz>TIb%9BMv{YF%@#-U8HEXXjc+VdB@{SDLcz~hweUm++T*N>IJkZQPi9g1u z-I9(5-vZxxKt%hoU~d4!0BG)&bi@%;|M-enE3*Y>V$rYDT>}-b&g3l5tl6gQBt;^u zx0+7=~A;aV3j>N0FHR?)gqvkjmpAPkXr*6KDWj)zOH^Ku@_IQzcLTD zPR7kRSa1655jCw8uuNbqhwFqih2(|D>(YPek-GMpH@NZ;w@PC3=a0Jy=}&XEBJU(R z70I6!%=Q(=Gd`kEALRy~#4ya2Pa=;GgkaI9C)NQPc4yG$wPdz7ss_R5>jp+Q>c`thpbiPP-q2GA&p^?tYN)kQNoV~a*0v~j%X@I2V0a7 zdd$e@N3W!;f2dld>@LoEmwgpU8{wemC&?6gTN2N%a~0wHn~Q(Ol3?rm82FHdY)j8Z z*zv2{(WaSrQ7L5D1BKOM6cDGY7qtkdV^N9O&KU$EJn8#YUuzR2N-FA+JM}p@3Zi)8 z^`Zbu^ISIBQY%9<0C>TndzUg=F$ZxI?=dCQO{`iN;d7K{MD6m|Xfc(onyl_B z(SdS(j-W|MjcKiPE=2kA93_*OL(sFBekPLs5<(KYZU>2ZiN&H9%t&#K$TPI!(iEFc zr2E_GO^CA5N}@KbObM}8fiYmo*&eE6q4twGUjkep*84m|F-l~-^^+JKXt-ztdf$Ja z@E~A;c1zyAl*~znMmi3Ao3&piCUhqDOq~oYTw^&U3&H#TftK(cXDbi+!6XB!_PcaE z_rte67vB3uuk(leI?e(tRZ?PyefJe+aVIO!IV>nDSJR0F$%BgOUuT1?=^L5otB|vX z+DDiSYP4QkneKKGE ziP$%2%SR4FP4v=dzr%ehyv}3p2bitYx5o(lPCo{?!+D}=Cpe5}J39QUkq6Lq!=mpg zFrs&^vCA3s+;4u&Z3J<13oO?3x&k`u`bAG!j!(diL}Z)QF2SZQQ8W!Lqa>o62DJwd z?{`-va?qE?_?rOTea*h+AYz<|*sfsT^+0Ed)Cnm6X)L+|F)9)m0U9K%uKb8HEiqmm z7I_(|b>Q$dF?4#r!ud~f|haE-H6`*K+-_BDGow_6(&94pcOo?Z;41x^bzR6j#dqUfF@-Ud#~$@q381e-hC_+Y%;(85#C2Le!C+6` zhFDa6&;v-ZS`#BCBr5H%@yr9}X&Z>*=nF>HevcLD>}RS|hb}ZAKmX23o|z%Tj0cF@ zVkF?8R?9=CB`+e#Tz5-x{@AneAS~(*iQH`htr7SRBCoaXmh2SSx6s~~5KN?#@h{gS zPGEu=@_DNf><0jZePf&aH?RKb7-bO&HP77Ly6uWWrExG>PmJ z@5Uk4bjQBSIP__ZZ_Hid7Y{W0KOq0k7JR*H|8OEw6{&5`c8RNzU~$6$0N53n>qWa| zh9jw4CjoGwLseXxgfM!%^VJ?y6If8-IQxEx5x6$5ISS>G!titNuX7Z-CDi|ZvzgH+ z36HM!Y`kZI1Pg)+k~ghwu&ES%S2DWI5CaDB)=9SV;LbuN=08Cr%G(V_v6Rp2WL(|+ zrFIk18;eIJNC&>1$@ZE8<#t=;y$BG=RiA62z>n#1V_F9C?f z^&9L}eZ6);@stlN1qTI7M*DkqOMWc>#;)$hSi-mOT@J@1Y2Mviat#fuAl7&B9$R>BOi#gY%F6Y@^j)fFWQ7 zQ!-+kSZCt@k1T{#HjAO7^BEn^d0KEQ26_3E4EWx)I>c4`|2>Ft@mcBr5R3$?Tf?Vp zF5Ms0mjzYvNn6wlUs?=qWI`1@O_k3zYhNbvuuz0sZ=4TT3dS@5w&e>y1{)QkUoM=( zT3;Gxm-Pll$;cT=@pKC2z+&7r6b2^g+`TN%vvZV;-K&{l!^t@m`t4;;?RNwQ3x5)< zo9iW_2=>CV^iMGPi=^}Z-z>l>lI({6*1@BTw?HR_#zYh8mJ8a^?6BiC4=_G|+PmdD zOi!IS9*Y8fO_pOmK#1s+>;zQJ(5EMnw}>MwBrCzD<4gn)dgUY6J-{;*BN=}7xSm4E zUcih%^!l;dYnEX?=~i?Vtacpxdm@@Of3Eufz^cmWvEWs9gu; z{W|`YARb^j_5zP3zh1{3{?mwD?AOU&`%ry76Oo=0U-D3Jk`4}g9nrUa z;~oPb2oxDSMk_{U$`*gzWyhj&+61wrx}#XmX`s8V)3A+2jdr0LrEhLMSxwElQhh;M zjCeD;@-x`fV8MVdNuXRfb8^M(nuz`5--#D7yzjjWZA6j75g&lx`nB$v*7$uxX4uA@ zQ}G_{#{g3#P?}*DZO!PRN96sYXiA<^o+LP;Jth?bzZzEh2^+PI-pVaGCuNDWx2 z050k|5IOY*E{85SxigWM^Gbk!XG^2gKVS61jg6Y%+zMunTS%S>Q2$sW&ss!LVWhi(hD zfkEyD8BML(`rrAQ5Q{A=Eal9s9Dw(Y_ocM@ryLBlA7G4(^*yF2^watb^_McGDzL0Z zOL2H6HkKA%9j3SCmZE4wlG7f!r_^*r4|r@cC#73zC@&`sSo2wSQ7LG}<*|;;UwFYUyQ>P86WF<|yAxp< z3|DtY2-Qj6^h%cyfbkX~a!e!P^ePw#=ttP{WI@vtWFLZ^Ronf_s-ld%gO_~cm9%ME zMQSV>duZ{og4SzP@Yf3qIYp5gV(bN7YjMQA3E2!p>FD$x0}(Y=$B;yBvE*vBBGapU zwnPJJz11(F?1U#3COwm9nZF;83~t{v-J;4WvIGyVZ$u5W-Jdg={7hou|tu_|T#tZOo&!+AEC0g4d+1Y;5(VCt{ru9aW8%DoF6h(j>OQ(AQY|sNe zWftE*bcw*&vQ_H+@cIORVT~ZCm5Q&VS@-uEh^!DYR^aIZ2d?9e$`&Z2#+oD1IRy!# zJJvFhk9B)aUi024lnTH?)}MR<5Q%*{nf5CdeM6+bDg`6rYO2}Cakjv@sq8Ulzs!aX zNxS6uwwe4=3cX5{!&2AMon+G?`uP3e6+a|svHem!e2cv$OA&5#SFe2|>8MCHyOxRr z^mrv9-;4I~+K?;|AXKBu501Esu|_IFj&Sw-5u-roRa2T#{zK|GBzN_;R0P(6pf6On%hpP{_ z9&FX%TA=#gj{AI1~ycr0XOnL!85_8qP^t zfB6l@_7`clLm0T~JytwjCsw7qLoF_aNY$NMU<)!PAaPdFfRZ)6|CbKv(1N5KB_j+f z*C8{8lS&Ew*3 zqM2A^K8~IN1tuY>PgLqX(AS9TLtl|e(ieHoadqRX?>(x6rX3f z`uqWJFx~(xQciA|DN@Q%A_qk0i~%}z4H&fX9~itCw%{^@_x&N~O$-ofa{BJ_vx>05 zSPZap8Xx)Vl0vkUhv&cKTcsImFzok!II*H=^*Ny~)c?j-+3C%8&d`~Q&bR#1 zW4knh7_MRNB_#MB;`UUHsOcjzne)NJfb}WoBXG!7;Bq$F*e1W&f@g^+XzhF_eHbxn z1=JyL>c#ryaW|;xw&zk38VnT-?O6cTrcnzXEd)#kMc;u;w89yJ=T#Zm|G99|s`ErT zW}X7P{bcn)MJ%DwsO1<}FLmRYD6XC#pQjEt&WZLSP7?YmNjzIq)@`NX<5vTBaf_Fl z#!u(G9^wvTtqUR6$!on;Y;cm^Ow$Q==De&3DKJU*^0k8O*jpZ2%&>Bv2_AJ#!HM{O z-|Yz(o4%C@0>UyynVI!(3%u^p17KL#lWF=dVkX$BWG?k~25dc-QQm%6l@T6%@v9FP z!BPX4Kae&lWf-{C9N*o$5$r|sg>|U(I#DH;TYXWyh`k$PmfIgc) z(B@{)49-&}^XlysJ6t{G^gM+w5qbQ0nEh(=OETjwG-+1%fTlIw;@)N(-fb}wJcR*3 z`c-0-1%w$mwWrQ1yqXGzi19T0eF;xq4UzbxBzF`i@ANGiQ$IDMRN(>!N{sK=+D~UD zK)R^(Scmy4LhjX$musXG%g8Rjc41f}o5Z(KvkEtu616WL1RrZ;;1omvR^2=$HDhCU zj3btqX;pFcNcmnRh*ylWI7vF!acqXfO|cBmS8{^T^AZgw&dfRiT`3X$q(-zp6#5cO zM|;Rf!+4U+U2r$}jkxVCk6!7c1;+NTrzKtjCh+bnuq?eWZm|<0XHEZp`@v!%Yal_!DtmvvG#7xz_n}k|kn)j(nDqRyyQjU@o80cjBA${vPi?}WwDJcU zEP)*VC^2?Q-E6lzUwY~HE~SZ-QY9x*%8yYQGip}ih9?1)-Nx9hrs2%Fakt- zr|l>FIaBCkMMo~kA&3%_X=8eEh} z?@vvkRF}&O3K;HZQ|?>EqFyb)UmskLzwkhuLF_LY&uiAZQ1`EtK+-b_A$uj4E~M?V}r0I=)Mc>*hzNa zBY?Zc{yva{-H3c71j&n@O+3aeK7lZ+%K*Mg&A2U-sL-`9=<1@&O2&t*^NK7mkhKo3 z^y!0y*^1%&0mpu`()>yGSC=EfA(IutQ_}fI_G%=U#6m}cQohXC@5E_m0$Huzc6LvfC{66t ziwqokW@;=eS-6uavY@G!tb{bmazyr!=p7OCu!R5O@PF_$V^b!Gu!Zf>fg%QuotR+v z9cKI*50D1KJqe_#oC25Wd-ooHD`A(7Ybr2JulVdD{2V2%QNYxaL(^)!Xj_F!lKwz=A*O=UacnSODWmX@3N#$4DZQ4e2;`tsR~V0F z9_l#YkgJia(@wwq8*mUBuEl0ZH+1a~g>&CXwx;B)(jzI#aUi$9Kz6ACQB0z?_nMM7 zlDQBHk*C2jPN1XzE?@uoFpmImlqZaJHJ^@oqxETh=)~rbzL5j;&oY( zSDVazYB0p^gL5L*8JfT$*4v3;1s1F`Pg6U-s01z#sG_^uVUD3hdKE1ebvjG&w`&Pa zyXQp%%hX&tY%N(@54)bTRIvX45-__nX(0{K(7p-pVS3`9x^Q_7CriZ zletYDPJ_5r`v%u#DiCV&)VO*pa9kb5Y9|gS@ql8aA8HJgOj}DnZ@7$%A@QDr zOOgNc)%bVfgBWLV3aGHqKR_8?eYgnEM>Zf7`sPZ+X`V=JSpc~}z#Q_EM%1D5vO1es zv<)rOBHVtNZ(^uDg1Frec$jM^SK*y(L+xz3H-?@mpJE$rq6w=DKpbF86x?wN`6! zp=Ae>XO6FOFem`EdXPNM2}Js=#a#`WL8>Ph@^f9t znWN;$a|l(`4$mh%hXvsjc)ZRy;Idr7SlsA))Xrb)nhM0MPSn}r93}z|C&?J%5Ew;X z{QI5^u%Nnsd)!jWt0LnWUt@P6PA?1gp%vho^ml$Wq>oLBiweit%pIlSHGD91Lz-lv z+5^ogU5IN_;yQf!^ufz)C2cXl+n}J=#gdd~hwYkgwSl54EX2TmQ-t$`98<;Z4jYhXUl z?*Z*KhS90?HTRuFM6FO-(X2_S4XBdSF1W3}Xr`X_VQKH<-AXwRN-bq|L6*a{lt3Z; z_*Npe(SW(zc61eCquB*HKri&lDRh-YUfiOGf>sLHZ$NRKxnvwo5>nl-GHbvd5!XMM z|2W-HJkh4!@#%(JAj$1*(5wt{9=w5vH|;^px; zqN+jFz!FkqLC1R}#Zf9q4v~T4aZGAojBYcY6%JBKmpQ}>y12;xNa^&0T|FQJ+PQQNvsOUymzonDxi-Nz*Ia{GJu?v_ zDw>|3V)t#GDBC7VEDoDUe)vtd6@+V!hhaIsd@{v7H~oAvx+|PM$^8VM+lT>Q#87=U$5A2p%J9st?q!62sp?qq}BJkx~CsPYA7S& zA{0xB3BQAF9tLy?3d>Kpsp>=ePf8*otU=I0XPb`C*isbYn~*y+6dOn+sw?S z@G`s?B=Q<@vw)bv?!aez`J`VCP~*PpiFDmR=vgCtQ^M`$5v8X$-KujWBTLGx62sh zom7ch!XgHRt_r_PaD2n*H~$(IT)1cMMYOD2mki#uP^ppv^5ip!s!EqC+A{h4Nvnd{Y9vH)bN2^=Y-QvQ1MF0RP{_SjR!2Bvs0(;;>;U8ez z*f%|4SAN@T?=6I_gEki`olGF3ZQr6c&+d$TW*o#AH&5Bi)mw!a#n$}5!_ST$Ex592 z$cNi*b362JVai+<@To}f?i*5G2F*Hfw!HUfihshMLfJeSilV0h)eR@|r!1xaS;&~i zNr=gJv~iV>6-BqqQyfSFfTsFz$n)xtYp5=G5@#R?5aRU;B@z_l&&fp7&->6-EyRMx zXFx=<;sd-&moiya^8mB{K?}||9?A3He2A;J+mK%H#}y(T2#q+pYAAOg^~%N9D}`mK z{4BLf6z!Q36n^Jk^b#10WYLFA-`=zkC60NMD)|a|q7?bh$zVhM5T48BKU}(YG_b@A z#>1%lzZ3N)APwOh`WbUFHD2dV;q_cUSZ|N%`Gt2sq6?ws`BQq}m4Yo->sC*)jpr5A zAg4!Svs353MBjTYIGZrG-Zq+ z6q2JmRs!->MQ}eERixx3LbO`&*Z=&{VUA)u0bIXxg|IQ?^fqa6c6%3rWN%6E=kp3) z6DVU4cZsrLl1){njP6f4c66661G#FYRtyHH``UV|Zu#}|-&(d;6Sd!hc;Sfw43e@j zZQ9lf)9JPv8H_p2fwY|`S70R>FOQ5V zHzA=&UfpLHtYDZT9|Gjs`x7dSh%H(HqEAdIN>i_7xflv^8YEmsU_ay9^q1Jsu?pfpdciM^+=ePYMH_Bi)lQSXIBPUZ)X z8CVfn=e%G`ILHosGZbN3JSz<%;@C(n`yIH@z6rK+A|mxz3;;KfrtVXor;NV_=}hTf z2dPJ@rD!6c5*i5_N5m-6Au@Xm=zS|0#o#ucnS=KffPRWTY6BDaS_xQ5ZYy#`h@(-D zHYaF@`G`D0+zt}ySW>s@ns10(^@{s@_|h$l`9l)7E4KpC9xFTqosW1EwfDz)p%o!y zc%X?XP5`g75SN$#7&!Ge@XGN5R8x0ziy=!)*eHSOVZKb>cS|E5WhZgcRjH}pJ~TYZ zY5Q3pusoGnK3^;@mDFXDgh8^HU*MT>e|FxPpwR*SmfTje`U{!Xs5EGJ5q?K0Z_}EHQvaiS4`6u1nlIC~Wwfr&csLB6EMO?RMIlGs zRqkB9m{=gVxtz47uj8y(D~iI4$pG;HN_3`OjN`*V?z7pTJ}tJ`!=$oj#uxqD9>SLS zmu8!QgDx)A5uO_CFDJv#-7CVjfZ*>Nm~>ToYRequIgnLXU1AF=@aVK;`aUU8Qgn=U z?ZTTPB{_~4ZLeh|kySVIARp(uMJTs`z;b7KD-et%mHos>abH@%klx_|e*L>rLk23S z70aLB&a6o!7*k!kFy#}s6plKoHzFCYWPQ1Hw=)f!Kl$8_)de_259FbbAlQc8LRQBr zQZfShS@-UXS=%tR4fL46z1W zYz>c^=eM#RV_q@J4nkduXP)kUHU{;x)-bnlRaF`fv!YI99=5o z0)uxwgw>3vRyn?L)vLq;6iWK|39|!fQs&5m=-1^9!nY>2;}{N`DXFEOmdDT6wLX;- z9!fd%)oYBPksPH4h;-;5)#ZyAg1A04Ywi4QO_rgUh4iDdEjZM&dbUGSWnr248uLLz z?tyeV`<@b0MtCv*MqK^h@8kbT!G!FpN8qA#Rq(bn9)4e@I_$_Sb;PD6d|GT6~w(}Tp1Lt~O zoG3enwuSukIsTWHbDojFm;SjJ02Q2X_f})A4VeMjdWXHdET_ZX*(829sqorfNec2q z#?F8x3AZY|Hy~s)kvqSBo4&)|`#q^Oo(kBD{-dK9uEEOeu)v5(&KluaOmcI(s$^lb zg8ZTIFlbFpYzk0dgJc#!#mN#V^jsz{rU5ZQ@_bgE;ObqBMeYByeTSFU?+nmnIi!99 zR4WgIZn36c057|lq=$FnYgvK}p7b)b`*HZWSX6I8UHC>!LX#Rp{-@8vUz*Poynsa8 z#RR2rL5vvncsd$j-lh={nqeKv1GqqY-l>u#$DfC;=?urnIsh0!6d_p~u)>+{gWIBt zPK#(T&UtYyp@_=k<(hj-W_JK{!(gRjsPmQCbSEP`dJ0?y5{b~r+!haz71t_>9}RGA z0XaK6g0;_LV9Rx1SQTy@i2Aki!5cCyDNTD>LSwr=K$~HYz-x(m*VZEywP*!Uzgq?z ziFGA^N9}R+oUF3|VV?CBjNFpz`lt%^x2@G*E(bMKeQoN(8g0J9jV17(1pM#+1>o@@ zuxHGUSc}7$6NuCKoW06e?Zy2#UUmS1B9}O1p4257VZ{SdnCgvT5E1rNK$yIQwP;0G zm18w9(?5exjW9O{Ela201f{L1Socf94$}7I1$zH5z!N@70!fj4V(A3ZrKt>jM7^HX zmQzv!q{D*6)^_zfQ2R=PkR2-XX!6(^_37&cT6Z-R;CO@4Y*Q##oim_`8N+VxW#3b}fbn(ot#7FFHU|p_BsqtUeEQND4FZ{OIc0LJ z``~S~I8Zq}GfY{Fqb09W+XGg6ZCBvtz zfcyDTh|xCvxK#KfzX0bDJ8e}^Tdu6;p0cnRgT8!5xOLP}tUij!I{th0Lj@xR^2NWC z%xoa?K)GQ@HZV~-QTu>0z~=HHbE;aBKn(0#1cn3%Ln|C5vZ6flUrV+oA(WLMaPKIS zc(b-$24Hw`#;+^)K=CGRO?LfHw*KBP1NSiRj3Si?{h^AUC~6|k$x*6!D3TX&yrIL6 zRP3=$6r~zAKi@urR>VC)ZlpK?%{_ODeJK%HiB_m@z}GT)cHj3s>LOpylpu;6tGFqh zZi6Gm=|9@gCU+ES?p)n+q9yl%LPjEwZhR=*lTrT$=q5O2fg5Ux;mTiW8o zN47%40gt-$Pl7i%{igsG$;2Ty{y---z`=&v>)sJun7qp(bwI5!XnPB7{Nhkaq{pT0 z_+Yxb3UM2tz*YRpXG{2MnGx;~|gz=~%4>^1)i-nLCb#(+^qT01ShhMJ`b`Hq>TMGs;COE|~@ zX#_IOI@?1<=yc~~u39V1xi!_5bdH0l0bDNQJV%kQY{wyI7UV92riv~*1e4;vZUTOh z_Z7BvZ(p3?@V9%1pr(V5(>AJhPqDQxs5Js9{vV_+5TH3$i(C=K;Mk z`Ij?KX^?02RZBjLMX!=+rN6%V0GZQQUh6PDLtPW!zwN_azvH7O9P0sy73e>v!TE3{ zLkxu;D&>Uv03v|CdXMJ-xA%;d&qv7?@FD=fL7f}mbQ@_(Qxdu$Ov)Xf+EXHt{k$z~W5gM9AKbLnuiaBzq;Dl?IcjOHOBL=biWolxF&O!rYhuq$JlUpH;BcnQ(-gm!P)Tv zXNS;R=i>B;l<*ZS5-VY^pXw*1LHyF02>PD1fz$gcI=>Xb=(1~Y=sb%#NFqC=mL@Mx z_J3;c(7yW7$R8ZT)>1(LEzWcUGX$y`z|cr`|HTk#HpI<>tA_y1SyusCVtRQ431MKi z^N#Q(I5JK^5vVC8kP}>?;4qp{snJICzGFwxha&BNz%p-_6p2${rC;}$aDEI#(up)9 z@OOuoK?`f0TtgZ>&QQ!N1N0pX;zw%JxK}*jHCf<6|9oQk57>7x2YQ?YCkIkY27?1>c74eu_T9&jdTcN{nW_IZ`{V8JwlawEyLXh_|4?JY z4}Z#27m=fS16=7;7F0|_7&@-D0-3zB21-OV8-Y@7P-7AyEJ}kune<}FTp;q(PUIYg z=B6m%Cc}V5f}Yu7A(29c1FDJ+B`BT6xgdW@kjwV=?p72@6dIr_q%al9!2@3?S`@W< zDXaidd6MOm0lE#a{|g!&>CH5F5v*AN9D<1Naxx>*+=)sE`SEz?$erY3O-u|J;xW_! zuoh&%Y_*J`!|y#3sqAp|Beg_0O!S!dIr}<vK4?lM~5wJuQ&#@ zQ7>>%JCkDzcQ|N=$k`A&g!g(?eUkK`gQFqxy-p$i*Xl&YFFO{l5yfv1c^s zL4ervBw4_StVLL6i)X82ClQ8%B9WE>qJZKk5!FBduOIJFo*p~k_dS0t>G9bi!gGNP z2d_c))HWy!H;iifUO>P+-RKkNLgoc*s`_;`@gMgZw(Q^X`ve+HGA0#s)xLLWtwa-AC7Xl+vllsymrK+Z-x0Nz>E<$Qf} zojMUh&bGh0jz}a$LBEie+2>zkG$Jj4$T||zIBxvDBj623np3OTIfPt^@4OK%h_BxQ z?L5KX=Pr|?H8mJ0gs9g=i$ckAPU$ z3+I2JBnB#H3s)wRyw$nn&?}t1Kzjti5A8tY5bUUp?)-elg}nWa?^Vsm{M4W4H;Z{8 z_9zhu?Jmb38Qet<-K%p#2cd7Fn#-C%)vCmB15iOeCdeaTo#lXnv3s6J-~p24{RifB z855geWRnL5WB@^y*dNxskhydY7#1Qn$K(<8FWcq|Xt$KZ&4|fQ@q5oq0Q>f`0Zj0J zoE>00zNM;-GK8I!b#G0 zV~}>W8I3Gl%fzMa>12eDM%dcR3=;6>${KJs#VoL+8jW^m zUUhjDIwV(#`UH|-W%r`^OY+{s(!DFlX}7SlSUQ5&x!9~OnE}uNpSulGTKm-dcr|)J zspAM-2q92|gVJJa;(^M%oHlM21`(RfA5A|=r-2$+dgL8{lMv9}$n*`DL(s3ogkw)O zffCg18OwzAZ)AE>sq)53NvjBaUpJhCB-0-V^anbN{)JCD0{5l=tr;dj<*jr&6bvwq z7MLdiY0Ex}6h*4)=_D`TH#iGmn~Ci%IQ;5^fNSI8X0(KPT?L(R!4cHK>2i%E%Ki9QXw?kI9;(&JfUK{g!h(OP?JH^B~;t zqyZ-Z@3m_z)6c=o>4*3ocCpnI_0HEkh=xVq4?8qK=Qo}v3#D8609sKbthL^`hmv2F z$(n@+c2=jaOE6y#MrVRVbGAB3K=D{qrb*j}#2{wNvOQ_hS-XHpQTJ?t-a?EUg3#sa01B3%x>ugmlu+nNzCVJqu{@36PJ`gPgd< z5I1-@%_1zWU^Kn$Dwso2e^}0Vf2OO)3Nxwv){vA^4 z)KW)^ETEd=q;##gfQ4bG4=}(_K!Q#eWF4AVD+=tBp@OSW*W-&Jz%d7}QHio5_N0pD zJLxjoOw`9*N6c@eks5`x@=%~@tfJ7x|8+>2JZ@SdfrpmvCdI7TD`j|ghvFkLk9A}8<1+@y0fIab6p^43+#uVo~{@nx*eIFR}?bhQ3*p@7t z{RJu@T)%8RbbR0r-BfQXNJQ$l`k<>-EHC)amWg#Oy1$=xM3_r2g!Bf*fM8+6fKaB4 zqLrl-kVO zT>*Y0PRik{GRU^bcq=CoEZeVtzs>>zJd0x#$bgi?O@|~onvep>I4hAQI!3JNIj{%& zsrX4^lG|!Nf0j?(uPBpg1jp*S&=+9(5?O`>7)T2uxl5sZxojVf1||&)?$n1x+JNVD zNtO&P>#^TW)qwf|4pje<44C~WhkrCdc^ec;_fgsW*ApuCr=bN==o-D-`0!1Kjq@pj zit;TtuFBOQi7D#s!hcb!-*cCHsjicwT@Ws7)c9o`S3uiR?Ium6tzYRATovtRZI@8( zlLz~d2@I*G8P7ELj>oxRKy7#cY(4Icv8NG1)+R%AomW<`xtUh>k-t-YE=5if_ zp=m1#pu=$-q~`=^&*OS{U%W;@su}s{dL$`C@}5K)+Hwpr4UCyy(ICydB@F-L7y=YWZM5`^m} zP4<)lepRpu;Q3h*C&ln{pJ&~a5lld3ow9qDHMZK9dF>yYg_KNCsC>d`fb+)sx``D4 zAE284lWqN<y3nrDMv%bLWNUX(Gt+Xtf3Gp+}u=m@}C zWhD#{>Wd{)KsQ>41S3di9>gZq)I)XM_ZJ4SDRZ|?eC2;*cp8WjU0-dP2u{Y5I=4oyt4q@LMuNWGZaR;kT_F@W^-t7MAo+F!Qw!Y6P@plt|qL zK#=r{B1=`V^oD^sKz#X`KquR169pIR#F%P0um440vw7yY>K=LWh!hO)3Bp=0aROYN zJix(0ea?!=_iV>a2nr7+KKe&l1W;R|?57ZWfR?`XLl{O5l}zUJ_&UlP45+>@!Z2`d zZk*BKg3^={koq9C#NdXvu|o)ug`}REJVe1S;P({ajjaY3@OQcH+>NwRT#N->8?&x_ z66}LtU8zX@Hz?32;Mn#ZFaEsH|E(BNnOg(WzPOec%RLILUnE&7cwxavGIss?|6y4G z4m=<}G{Sa*0P)H=E|(niDT9n^MF3Ir+(jtJ^H36G!LQuGn1zNn?cYm`b|KX!M=;kF z?>2phJHL4E<3CEUH_kvcMNV7==pp`rKt&K1ENGc?n?P09cWwnn4^~LNXr>OwTCJKTqz$QMdatQ_luPwCxb` zpEnr56AHQi8}%Z2sust{@K59W`KcQ~VG!c{o;D`@qkJuZu^NL}?U0Rrw7@V8FbCDlZh=&7k2Al( zLpAA?)KTYBq3di~4wz63$haq`ava^|dP#1{pfv?UR{r19h-Ch8u%Z;zVqN|Q51vVk z3Z+gy(EOvRL!m-|9+eBxVb4BM$6V5g`~@He4fXZCTeb9rxdE#TW^-2o)eP4{s@RaO zMDSuNe;SM9W1LJ8k89`0Z~v3k57Je~~HYWn7z&{#bH$&LG^8uO%%>N{r7fQ*1*R)joI}%Ju%jlbs+jQAp zVXdFY(aHV#aF2F;OhW@{3~itI#)hAUCeQ$1gEKjB0xIa;9SdN^vUt$~YP9d1-qh8Q zm9iYLejW2BeMJ{&8g4`3m=5BMZg_x231~{gv2IJ07-2%@2+)SxhX7HCDjK)ygRV^C z2yywT?1AahJ;OWhhZE&c@vkH}5J{!Rwunn^w`v1v1ws5x@aAVgTm*c4xJ+9nl_)0^ zaaa4l^Wx5uPKsqx6$bQlzn9yfaEF2PB3B2F%ZGJGF@!!atC?RzW;Q8i2CIlw^1tIQ zJGjE$rJi@dGY4{Bnm6#at0?@2z(}urGX%}XrrYgew=~!_Mn1>5FTy14KSHQS#?%_Z zs#JuE|CG0_Dwg}9Xhr6%vi&5XZkuhas-p^!Shi+F0$&su0RrMyCxw^~Zs%nnv)VnO z!^AnStL$|K06>F*I`)x$AIB(VA_<^ihkRh>Q8iy*SJeRW0MqcRz&wo!Bwb*}^3oJI zD&05>!MWT9p~s<*c9wy^94+mvI0l$2@d-zpUVD=HIAmpLG0n$_PUO8)tvM<@Kq8m$JQ7do5F(D~aK z5R;i$LkVm=0=5+1fZ`_v97E2GWTk*BWFZ_q{xoJR2$^OPh>_HyAnaY;P5;jl;mWiV zAbV3|rN5Lcb!$?DVQ3>h@b-)YjL?-JvW;cz1gfdRdus0gF4rKRxYB*HiGZo}LWq6^ zW?+5?+&p~L&go|e`@26dtLf0#=7M}Q-=UgwjKlAgvI1^aAd)j}k1{r-=sMss7;f&; z^9WEymRjmK2(kp-=j#LObk@Y-rlfY>x@3eDsf9HzV*Pp_m@j|% z585~#=aHX1-d4{i64WcRuZ)C`GZ#{gTOYs`tn^zz9@Gf{lcP$SXQap|Um>78z6_}f zXEfWZFhZIstn^!5V&v$?8|qG;0`Phf)p$un*F0R3q8|KUrz&XajBD|NoKx3-jun9O z*#C&w`D7qU!H`6%i(>YKtU)r+y8#vi;`fzcE@>1T zb%`bp^kqoUHxtxN>RbDnLLUSHn?QdsrGJ-7PYoKy5g+~M0%HmR{T9}`BqA#hKW{g2 z5(YLdj2-_c^2>jZ5u{gh$Q7OWnh7FIrc#lH4nWAit|S%+^9eWDA5Gw_Hfl5_#Z<$K zztcbV$wl*w4TbfmJCORW(~nh=JP(eD6CghV6w}xWR9&%x zv#?e>z11N+vXfo=g)CbjvXJB~zi$?`Ea--xgFfsejVn{@t^iA2BNIwFCuI%xE$pTG zyUc_=4yr4_*ux0O3P9veQ34;H$%F%WE~w)c^AEPb0QPR%{ip!C+^l84ZR}JlAH)a| z9Q;5mjG7r{;jx8dLh}zvbFZcQIW@yj?Z+tRo}V+2MnKs05ksJw@lZG)(z@}P8H8Fy zSBkvN78ZogcWr54y7cQtZpIOf@=F2WiG=J1hKV;w0Z~l&t+B>M$PDPtUnk7vlNNmi z?3t3fr2_D;=QjsgfN1}Tg-Lgb2yY`&c;2=NX>fL;cG1NPkj9A=3yScZDBk^@46WEg z0z5BDfzJm=zWJG78gUk&k@T9!D8i;}-rG@NM*D_?x z0$MrK^)qCl=sgHt(k}g#cUG4Yx@UxYe$f9lJ2HbDKjq0fc-jG^1wNN5E~F6JA)+^GOXp9~`{7&^bnm|`_; zz{M&!*gW^RY*tc+9G$M7|ArAn&g=eLIy7u&O9pIrNiZ9A-$~F9mK&_X1=*1kwt^@? z7eHYBUdw^e!ZL{OmmI$U8zN)t@$yn0sH;``7d;{tp*Z$|88j358}%cIvNIXX#Sn0? zoBvwbshO>U1)_#(mA_24pnDFQ_l_Ljrrf5--v-a51Q;)|+aTK(HISbbAXdZ7OlM4O zVAk+_#l_=o;It=wg>g8C&}9VdX&rcIP;nzwl5%LF#9A@RNM8BB7t1C2d+ajljWo}6 zv%<&kW0yUZvx7nN5D;%kF&nF?)9^L_trK=gQ z+?*;%@J|PXrmu*{t8N6=N!ZZiO;bwuQuNLS{zpup8FM=fW%aA)wAL=R0MZzZ&#Z>= zxEC9%bArWTgEZR!+O*dlK2ydte}4jqO8>7Q@JD3Z+_(M-5qbx?DvVpxgJS?DykrCc zzl}ZAk4rbG8a6o)&qXYM5QKvG=|jD4*0I2BY4LaCuTcZ>%5ROYJ=vsaGcZlk_0R=- z>k+xkEGk8$ChMqJwiq$f!6FQXnT*q1mB!10fy{cKp@4>#$*Hures z=z}H!-t1A5>>!}z@9+Izec)bZJ77tMeT-0HVkKPZv z$RPZyJJRHmUl<+P{g%faNuhcM?Gu(%uZ5Pkp);pqtWX12`M>S34R+L%`RB3OF-xoJ zE@aulA(OI+?UhM3)a{8b-OpPZ7JgkGsIDa8OYt%sFIBHQRPgdyRUD{tRFXd!S>=D- zq&__PfI@s4^k?twD8xtdfTmcx(6nj}8XjL+gS`Ze&Bb^^LO3ikc@F8h%57g`YT#~k=`u6QQx?KF?OGb#cV}E+ri2|bAEqQvWV8X zI)AQZU*=BdK;5%|T%v_Re8!f<@j+aZB8HDWDfh<N`k8KTLfgCq*>05}BgtPImd zE1c|>&I2Lqf%vjD92>V%sZ+?59}x8SBw~IF7VZ!rX?-oA1(I=PMUT4;q5+C0xt)zIt0!1`yoS??=g?PPzd2I9#*FAw&VSoKec2@CIFTtd zY}^f6%SJ{caL>^VU#F2s&w7h!9L^yzHVT68= z)g6U&KJwj>>hR^gCFl4x>+(;f{i&RBZN>GRNbl;EQs%}*TYIROv4=+qR^2ao+0yE;Rbb5efy zDiH&HS8>R*Z|bH&gmwP@9CH?qgm9aAW-Xd!?8?#m)`vEHO*=hzLfeFDkPx(T*v(b3 zc;5u?yGpc(f6ojBNj%X?v*+U=9oF_$o+s|eZ`psAPZGzJaczq^%?PI;mi_+u*OVM@ znSk{kg2@T{@MDj;DTePz(|Q#!FB`Ig~#S=4pUyc9?s{~r_1|t9V~*V zcgyzDr=5^Aa1Ip_a>4;JhMrBIw`C&kFxCF5AN~6`a_x=p3f{hjo2qf2T6}o z4#HH?W0hu%PU49{_I&chtC2aoD|un4At~%yTg~*`Y-3{1G0CFy;_q*#b&f>Xz~Jq$O0D)&Gj-E$6H~EGGsl(D^fP`OPJreIH4`@SAe1c!!*z z1EG40S2(oaXirpnHQ7XYb38sHif~Px^PKh32LIZqjzRTm1YLbi+Ufa!yCGlxbViFw zVWGG#lRW>*?YWENFJBl4ed=7TJH1sg7gx@IYRrlMSQhq;h`@_!pYlrD&Vx@@lmEDm z4sg1%5rdV}SEM(oH~n<=^hVNTKKu;>Zdy=*LnyHC>n{`8paKgZW1h=&sbIaw@1^?( zQVioJzpsxbG_q2SzOa)!rIpDXWVn(}j(>Y%@tNLyf`J|M2FKlMeb8ms1-%#D*E)$K zCC6XW!jcO7<&MS-aO9`EEs6-?Ptx_N$EYcEjtas<68}0V?<|Evn0K% zHx~OPfRbx(DNF~_7%W*POpbLJ5GjbKNy1GMt0(p#13rJ+Vc5%8YNy!igWl;}87>G* zVXmw8{w+d+nj{U;0=q?#dN@j{tX7pF5-EFXKjOl;RC`RmcVdAf^Bmn#egNH*!KL^z z{qr8CSrb0p>n}XqVIRw6z}>N*q{C;@W969aMmFSaA<>>?PGdFRTSJR!Wd|3?>#_e1z+)Pg(1%uGq&gU)M6pmGHeE*J%jMgSkciGp*jkv** zDP8Cnj+cES%ZAR&OB(wnFiN6ybkaY5AGd`|CyDt~wl@?ZgjYI#B{#OpeJE?Vok|S+ z`}e}ozB3#Pb$=_Ne|LSn+BM+w2d&@lvPm&+!zxcnCJi?M=UlNZy8zZ`L(5YZLJz^- zA~L9s$wg(tw4q8)sz6M8=vz{>#D&X-Zg6&FMw(-yQzahGN|=*&Bo9UP`C=KZQ0ipUeE6Ya zl5g#9bAz;K6^EObl>G_R6pP;3U5#2^x=q=7t-(rET&7fsA1=i#5Xl*0@2TSQDX zSUCVUz$ZG1#%#Ry35&T^MmKXbLUDs!WsW(^Z9i<=PHA~|JhpD{;@%(kI#PfjbyEm} z|31g$;_iexo_yzS3M=qcFU83#_E&GxtQ|Z0|5*I|=9;$>Gr?a8UXewSg-t zYKVWamV@=k=08sxw^14Uwe@zZEc;=*h9}v2xg(Tjd+tFIv&B+1Y-evDaJ`iL;y|ES z`F5e8c7j;6-nBn#7p8u)FnNR9#s@}5_7L-Ty=8rfJ zV4U>cjGhurLNAv~OE5;My+w%{SdNN`G}arJvZLI`%Yhm#^|UO_`KtWJ*z8zwxnZMc zDz0sDJg^g&4ipl=5P|`ZtdjiZ?S4aME8=m=aeAeV4Dj8zRj|6xQY|bHxJFpr;dG?27bKz=Ucy11@1t)+SL?ehXC>a_Ph4!6z$#?pWdj} zPWh1oY}+&I*Eaayh>lNghfi{(s!foZj$V5Dvn_oANk`kw;g$teh#2-hQVw~z$P$A) zN$!punkJ+tZ}aJ>(3QOcNvb5DH1GZ^tYH(=A!+8@dhJKHsWpdbf$FC_+)v}V?~(kz zeUC52i8{Z4z2?+ZoUkL7C`l@4j^0bqrSeEBZCHjX+f z(HT~v`{ycP=-l3Z@pR9-jO*BN?>9(G!%?UD7f)Bb=j7I!?<zZW$rJ=&F~!?8iGc$wo(r}_h-kSeabJe3*wwj zF=EvC@+{yYGHy$>fZ zbHR8;xmxqG?l*6=F2-)PUFPqBAv=y~JpJB?wOIV!e~$Sz?XyxOGwISp%Ll>#N*Epx z^gnDsgkhf>nEsD{w0P!ZSg`+oJ_G8as>kklw<`rHnE|yu5r0Xhz!;wbW;*9uV&)3j zk*YBI^L>=umlSDccxj4I;W{41;Y*B0F#dcKgqOEJ#+?<5t2l3>`|d zw?nj6)du`sJsw?GnEMihu0Tz7wcsG@8jw@6(|t~#JM3gVu|iyy+337pILSNw=Gb+% zU9Ww=WyJNYW+UDG_sOrd%99AI*adKU^l-PnW_Oj}*~ps=?5E*Zb&d)L-<*Elm~G%? zPRoI->gq{0rj#4fpw3q;YS-C8pwTAul*wirUHE&$Z>`RcIpFy3>U9=52>AWCo&<~( zJgGF|`OItt7611uB1}SzJpImd`5*c2OY$9FI5wx`1OX_BQ+WSj#nH7dA@+P9VX-O9 z%Mb*ZSIBdq#PIXa8BS2NWKhbfTysslH{x!Tim?@?gUXVnQsr2LlCAtlYMpRgL)J7 zAau6t{sd=Vr;@P3?Dw@dodNKG<7r0}gf6^$qOiUYWCT{J6W0W}NBMdQn5-L|K*@R4( zcJVr^^7Eec2ComeE_Gcs3e)ZQ;1k4N&@r#WAvm^Fe+Dp9M zFD}(iKbuw=U356O!r}YNv5}n^V8n%=GVJdahX2L1xNlinc{4v{iG z;kJfae_s)N*Yr=m##8B^`|j*mNZrOWY2Oz1*N#?8)lVswYL_2p)s}mWo5dxnoEy(I z=m~4>CPk@>+u8nFE+s|%wb27I^;!lVDAC{Hp#I?*sD0OO-_#^#=6k%PE%~LOejkpU z^1JY1+q;MIL(%R2Gk=dW$ZE2`q^>re{I7s+XeU44E=U-+V&P<-F+&+?+`1@K3#|;e zD}b+4c6*xaAB_~+51-yDgL%MsPn`O<#dzil=mcfJ{RstX4Y_vL{!%%EHMSc6P5-|M4tmNq@; zqv{gG`Hw_ANxFq;-ShTY6?0mS#KBvVH*O8M&!$N{t~{I(u-@Wi!%*v;aSb&iR~2h^ zAw%Cu^|68b0Spc`n>~@=M!h#5-jTqb<#b?{?8|jR3sz5He286%M0-`?{haKF-deCz zsMZ>pR|$&_N8xepY5M~-;fm)4hZ1xsfnWFvtz7oOrGnp$A!vZX8deN&+UM~9JWut; zL+V_v8y+5d*7x}C_7_M3_tmeR{!|?SgY07uE1YC^s=(cH}IkG zN2m0q_%fSP)TKsb3LhQz|1Hs%D}l%+MZ*uwE}-&l;H)a3BYN(J&|u3r+%Hw{z9`3@ zSVVlhL3kV)#7=$A-fk<8IXw%;BhvO;?~F9x?5N-STy2Fsyxmg$3VZ~CeP*}oEuboc z3;yl|BOv%v-j^Mz0@rAN1|52<(o~7?g9}=sDuR$Fva-R!tYYp1FV@uT}YbfcZz+D^4B0``}(3dY(K4_9I)8L%86K2%&Nm%tqdxBqKw`fvO&~ zW=>_3-5aJ$DF*Wd*vBuH8)hZcZG$Tghb0+KwJ;iV4;&T#`JQq~9|pCt9hV%&=iTJ{ z3i?6R2{rS7OX~)!R-x*7&ebs63yx34s|xlbCP6EMJ?c1wYOJX4Gjj>iNZR(sN?{g_Saveb@Lzwkb)|_dWIe#`+rN?WohdXZniOEcO;0ey+O#PV`31OZtY4V zr_+gMfCN~PL6@yqr`fqDq3;moiD`OLjzM%prYDH}PoQdN@H@~}yxo6v8KoLGh@0F# zy}q>0`(8*`U*_|)NHY)jMF=~=CgguN==J&Uc*fr|n-BTR&xO1%V?96`f$^=ZX5<;xIAsTmGHgqC&IjRHr? z%O@jd_s;+VXp`*zReO04iT&$A2AXSjk9g%RM_jfx&$=TWex2z18i$?YOXh(1@@BTf zsK0~o{l!V4xA8%o3FtMZy(99W%B%85-C2{pjy4Tus~EKRWGWU$>QwrVF5z!exVht2 zzBKKK1w5aVZysml;MP9;;Q_2LT}&x38reK)u0t$%4OQ2FuX*_c>=ib@$nwlx=2Zjg z?gj>(_~_jq7;_gG?E;Rz>;T~CL!&=?p-`~Tz`QxzhCB5nl z9TCn`NyOv`rrU}Z^XX87p0Gw8>)u`^WZw$|{Iima!Ysit3UvKpKPs2nzl3}?h!S`E zgvd5}bNh}d^9Bz!3zh#`uaFP;A8u!ct=Fdg2fORG%U`Qe>z~7} z9bU7#lD}TEIiWYOEMxi+ft~niJW^6zTy#lLck9FaCTJdge7F9&|EhIrz}4#IZq3Jw z;LSaz9q^b~9~Znivv{&|+C*d{we4i6s%r7KkOLxL5@cLay*sHe4E6Ny9paNXbH$9{ z3WpYvCkh!zec+gK8Mgz7Es^;o=+}|J_TdU&bqw$lxXp`4r|=?ce2^Pp^g-G`x~2X% z>9i-Hc1AM%#j@WP_5~_)a7Tgel*Z7rN8R?wRrJ6Vz9-ropr#(m;c|53tam^6KYrEFDgU;K|_>5>m3bWDsZ!NmEIozY}{)@++lN1H~gUp1kqcaFwv7m6O__2m;RA19? zje}W;x(JA|eoL(Qf$Nw4WIw9hxRkZ;CI|7rZe5>T=#P<01U?-l#u}W656|oX0m>sN zN(6o(^in%&3Sf@MhZ3j5#G_}vbyUQNNLl2)w15(vkgEN8uoumi6IK8|iK-zFuU&j{ z;6|b5*#|>a>GWFCE!)T3P{7b%suc)A248>kk?V_he^#mEsOM?_SV7gQiUVCJ8`^ib ziCwrVdq-+mTZs%bzh78Y(E{I2V3@K%A?xt@Bv1jeDd=evu4$D6Nw31SA9tzKqEm$k zP*25$f{G1Nr=5K{*Hc61X^OE*c88F&em3aCYd>iM)5fe})GM6~Cc?n(FsL!7r*^cw zH3Wl;H69|1!(G1}xc;pg{Zu9ubzbvwzsct2m%CxOZJ#OfD`}C;a=~xSg zSUsVWYLt=l0Dq0e_GgXsj3`#?#+kf8O;E4cxDHx%IC_uTnK#q+*YGo*`v%0jJaH>t zk%8ZKo*Ru$*6OU4ew(ts4^6!9f|IC-;xxi$#uBnBhLULld9B6XFp+B>_h)L=y6*3) znY^PV-zl?ABR+;82rBBjD8gm@{#-?WVM|9&#Qk#lJ3a%rua2pH<7_^UBh^qxZ#uGiW zIPe-`b(kT&;$yJBl%DDPIcCnIPE=Eoj<~v(QV;8?lOyI!AT8b>SdUwNQ*QpFT={Hh z687UDW_t62ux>iIFQiTX)A8{WfA;VBemn-ziswCyx4M6std4j>qh9-~^sHiR_9cN2 z_CNd9?Ije>jypE&^n6lo;r-&xy5v1)fN)E#C5Fh_~B@(aD(pcLo+5)CWaW5Y{DsoQsrg^t3XptsJk7`bkVVU9& z{XTF~e4*2PaKqZ$=tKQ#r&?FIYfPFaIVDwx@_gp%xkUDo>;&_{$-J(i6S6?Zm|3Ox zYbKFaZE4PP>pq$%Yad!Wv0si>L9gP>Pc^&b%C?>kd zF#X%GV2oyOH_8rMsbi23u6gEQODSxk6Srj}74JJ{sEke^2Uk4y^0F+w9zgf4yEiG$ zby}cnT15SpXy@<85OgMj`aj3RE zAts(4UG3I+&r!)Fe6zNVg*(&pLAaJ`Yh|RPzM7n&MDC;H(Hqfu4ONw+7U${B4R!z((l^L{S8JqC&>+Aok{)N{ojbcQ?OtRz5Ihev~&eTqfjq zdvMyQ`YD%Z?$A&jW`CJ(BENCfY((=511W9Sz4Cb@L(fTjmWLapaF3;#s|JqZh;O4y z7qQ8aH-7GQb!UtC8|ZpVPw-&H6K2I*zA;jBslA^k?X}BqO8?&Gyb|)tdHV14Q@ev0 zU)z!=uXPq$1iFGSwuflAM|Jc;d6^m579#c?oYKp`ufN5$n1AIw3i4gdf}8S~#WDVV z=S;JEC8jLc#C)T4`mI~L;ZZ@E&^f5+wD5gEk6cB%I0tWcUqE=Cop)21TDr2Got92d z3E$1#Zo{}&Jhq*q$B#-6WX5IR;)X|_4wiJ1rQ|ri5VU|)Cse0Du_<1-4nzHG9)Hhr zO_4L6^_-8WNw@%iv9wlkejsotk zY6h?-7~f&84*N>mn~4@sU^s3Av|@G<$FA1Us16~@o2Yu z@wdCgstmp@C$z_EUC`B`BGgB|6_4DIjZjPFlb^oh$1f<&yzi~D-`;QqnZEeiFm@pr zea<&Mo6hO`U)Qq*oBKK*md;;dZ9MJ7Wt-Y%rD`|C3^p5J~++s>3)mSxXvm4d~2 zr*xhobZM}Pn<2-L`S~B@to97iL#pp|#fte@?_fQIfK?!L4fyljV3L9e26Y2h5yWmw ztD)I6Jre41Q?jT)B-Nk-O=t`WI{mWjH4_9O5Pe(py#M;#50Yy#o*5z{&GQ*Mt3yF( zCUAdw1l3Yp5s|)1r1GoN99j;QsaL_Kd0hA^W0p>dGvs22_;HO-d)pj>D|9Ok=odd` z>z#U7E?d3Q9J}L8MZF&IAY1~8z3$E0d^PFFB%c1>o9HjGAHljbI#HYr6;-xa z?p8JmCWCnHOJ2M7@41%B{CEyHPQf65~F?SAa5%)E){r^V504A(C}^kVNf9jn+u(EHcMGxq897F{jU1yU_e zHG->DkfIC^Z=4p>)-#i1lFKH~vowDC%z~UCXKApA+ay@UDpF|quv4Q+vCV;KS3x1e z;i0;h7gZ9nGQz{QX?T2O3nxjxE*<90uY&bd4me?@ZpuQkq{Hyh6Mx=x@E7kfb$=C1 zmz&zPw&-&UKxu$w5!o%HAn<#EAm>Vky$6Fe`s;T(Q=*#vz9b?VTltsjsnEzc-KpA| zlyl@&BI^s^{_$XM@!_JKxgtpJfA4=-vQAS#kLH~e^2ki^GLiTFL09o}vntZjbhMG; z;{8l^pN|x>_`AFPnDFDKAn$VF2{w9B-x>FTj1mifN{e6ntIP;J zZx`0Okm7soYNp;3Ybe}uP$JQlhZ6rW0G@*zoSjQ=Sc}t{(AXHG`pJf{d{VDaOUuO& z!eKf3~E+HH3Kzb7uFtU%m=YH7*Yn;f?yG zv-U693XugHz1?Z5`W8vS-O`+U@KIs}zwHk{frF+(e%y215o&%$n@##^rcwg!9<_YcGth=DV|xPGxHI zN#>G%U{J+qjLawv{f>x`c1-5wZWg~*P1EmdSbo2sHa(RTE3RIb@UkYxKxxH)jUKz< zy5pSZm(OOuyfpReB-xI=*hxS?1wz-jSH@Bb%tUbJG#B?o)7Bhn@F02o6u)g=$AaXX z|HX=8WnS^}y2q_MB?lm?G-d5fIA^>)(2?E0H#KGH8f(?163xT1^dgP*k8f=#m+Z_K z)f3mj;D$TDtNiJi6E{QYA~Ty^Vm{qmi@W_e`%Wzf@1(E}F1n$YE5#6qKE={3$nlr* z->I60>-i;l9U?;RIKeTOhDz%h^=D4*Z{i0B^u{K;(-Zcw=9qBQpaMmxG)T2tTLHDm ztAjQ5%oEvRZmrC58}W`966D6W8($mXJ#N}T>Gg&hv2P#(OSQv=NFC7~k`^`d1WL`T zh(-txb$xv{W2*DBVztss+VjapLkMZSSluRf8(b%B?su*Eohdz8ckyaZh0IUQw6EJS zF!X18lSkRjw{HJz$k03w@ER$`(wN7K8xfna*h~D(Q}XmIfFlQL4c^(i!R2)JA4xC| z^1bB#Zc2S85c!_)(P*+bplP+M!lHa~x9zgY+Yk=H?lP;ezv(YR-N^CczyR$x-JZG_={ z%bQbFHz6$ir_dYW=Gz zkSz%Tk@m^u7$C-ke;VC2~``UZYRUbz7F#X^BjvdC!V_ zm+9LLAac8zfFi;_R+zwZBi_XiEtJ{n(xc)6sZ0dMn9kBO*T}(G zc+5swNzk$VpSLq)x^IpnT#=XlY{h0CQ*#3@Gn>cTwq&WLuNA#!pEyI~ERp{oQCArj zWz=nl9(w488A3r?I)tI5K>?*3q@=q+hVE_6DP}knS$&uKW6Z_dfS0Kjgvv zp0m&1YpuODWdRWZ6^kKKRf-U2fwoenZy(Rtg^&Hp?6SrwhiU;H7&< zR>>Tf1diL_mm+{7I$PjIcN`oGP#K01i^=blMshFYg;lwvA6i;~S&5Fl*%7jYHyzYa z;bwfNGqvbp_@{DDxNCUC38u|hqz{bcKUX=CexL`02y-fW@PTPmea8!_KZH@V$1y|3 zo3GT-nwv!U0`!^Nc0qVsdZmygl9x;}fA=#t1HOFf&Kiyv=`&5sPxIOUtaNScA5FQ-kq zZ)uYFi0Z!z0{{?#B5z4E49&aME| z#eJxNH<9p}Z%C?|nNmYP97V09<9QjiEZ@4g+@cxD*3d4Zc5 z)o~-4mGNV~fr^PU0>=lIVGmk4YcQMVZA=8B%}bB)T=4e)sSu2&bryF_ksoDg(eQ4T zKs2g0Xse|2pl?hcosI`X7#V(`j+>BC=$wEDszYLf`MQvidfn|6BTl!** z$dEM^Rw3!g?$Gz4G*hZie7ae|j@;4jo`}b*<-LC@j38 z2tJbcP>w7A#Sh!@T!o38L`j4LzY^W(Trt4}M;1Est?*?IpL1Q_1R4Zz?S)#GSNX{H z1njXBKW8FC(HkQ%B+13%N|&3H73q56#uw^uLgdGLJk;EZz3@?>(+uQo7$l=p1|UQm z$K4A{{=~#W)>t2-w zrqIT^#Kl@g9dhu^t;7Nq1<7D^+9JJYxmLz%wnE)bsMKf}=*n34u8e zv1@^-^^@HZr`R!P#%Dj-KuESk4Zkz}B+Ae<4Z7sHU27G?Aexbt2YeS(pw^uh@&G^TxUC&!l_Eg|4&Qa$^IOs51nhjGMA$r3wKT@f(Qit!l>2^iw+)0KRS~(% z|N32Jn=Bdti6{D`;pw}3l%G``?5~J^|scO+`Yl%CJDjM8lv4)ZKj#$gbRUSQLhtPHCX*d)I zDL=?K&V^Nyc|bDMlg3qw^9>ZaW3(tEMpjH|1so|&IDv~)Hyk*3v@wMt&Eo1RrO1)?Bw zRksu92c{%o(?MGU2!3elypF-k;rs7rA9Tea+WWQdUcH5Gn304kc8I-J7TK)*+XZZH zKD5dB6~^V>EjoH`tas%z`cn1Mh)!mzMCwQg{raVe?Z&P18_{8*8>gW5)Frl!W2SXk z#oA0}Z7=ZO1RK^J2EN~$M1r&cxJPeKP6ZyH^&~HQ-?hTOT||$U1*M^(IT)MCBPwAr zVphtliO!~8^z?%B9r7nLzG}HtHU^ndB4IXmPwG42LAY6}T$F<=*S)u;@B`|temc!1 zq5zLSOok`DWhDyR%BJ3z?^igM0k+{hT>M~1dCy4@X~P)U)CcRsv5-@EN&)=?d^$%f?hSJR;E|G1E}a5!cpM7 zNf9#j6?4@O7d!B;A~=Y1>UXyFlV_@LjM*^vR6%e;{~tOh&FiGd%5>?x*=h8w73&Nd zt3o}8K1+}03$4S8++=hF^xtCF_9ne!BnHUA2(LH7n`%Xr`vUfgCnbS2O%*FCLNR-1 z+h$)r_M1DxBXF89QDD84INWsEyJ|fx9t*+9L!;50lYF~p*6sJ&gX~i+HPO3%p?rn3 zAl|xzO(H((NN}H=C=pSb^+3+UU`b1_pu|Vwvd|8?ES5Lt?e3M^GxbK?-K?3JVrol9Yu+(_d`P#4-f%<>tJ zs}j?htAl+mlT1qZf%LzsqOhPh-_{TX9TzzUY3FZ9Q!|0+o-3;i=_-gdogSOIXK^fiJ#z4>c{I8?X!B=b-Qow8{=FiWT~tM=M@EQ`@L{(ftPg-0uzkXRrc=a(A;$@( z|NJ1A3H+8pB%+?j-j;J(OgMNV<1l1|7$rnn-L$fx1Ix|rcj4n+av~EUD!907uS_as zQ91yEAU>fO?rHhDLj56JcfPFx(o-gSEIQ9y_r2qjGq=s{yFqr|zaGoRSG-7nhKx7= zr3NW_h$I&;P3;)yWsy5LFK1ovn>7%}`$uZx80vNn)g;DO6~4uk-uNo!K)Mjrqv|`- zL7rb$@6RnjF$9Z)x|F>)>kS=9K#mfzq~Fe%M7x+|k~=r$!ilv>tLBTk37MO|u~ebs zAy-ui&f@$K);g#Zmq`%;K23(RxXd&{5o-uv&+tC>W3Yo)s;qGQ@f}gOzL6vJK_O22 zgnm95BjDKGJ%hZd5)IyUjbX=9#mgP)NXy@sLpcy#(&3CdswVqtl4c9+Xj=Am*uuMd zU4wDw(}bDhi*O)v+|h;I5q`f`gqk)Di~W0{Nh)r%m}+fSnO09!?t;&yIh0}6spbO0 z=Q(!2FY{bR?jB~DDZB}O7O+6W@&xS$EhgHBk`U(n4$QKYnQ`-x} z0ui`ocN*Wl?#JHhc=m@6Dow$~*pTA^l|I#x%6?qh{?tk=zVH?aN{IJ5>qb%oBt;ki zS+!88yV|}i1n#WTk-))>`K|8amTk;_bWxX64_U90ZioMZg(|0v$}r^F?i`+<)nd`U zXh8EJR4;cqH@eLJ9-Tq9LUw$zr3P0bGR(YgK{)ln7zfNvft{RUGYpbDQ2JAa|NZ?B z^YZ5k3FY0l`E&PEjI$ccTiBQ=P~i=SWls>WcLo$++F9_yrzkQ6%LsMBZh2*~6o~*T z?L1B(R;3R3G`h3)9TtON%{V&Fj`=bQa`4PW{}opk2^29erh!VHfP;`Tu#sylxPj2=Wz&ZGy`H5;ffBl`%X4*gSdb&Z ze2H<4(Ik-P-9>Jr<@g79^$niyy3Jwiy90-Z&Aq8IjaOC3wucz$`!ELi)K%facy!#8M1gJI&eMu#+e9$*;H~G=cuk4VH zP8Q}kdhCee-f`5}CY!e~%AnjhQ+M};k2!_{$4g5~ZP(}^?JBj#uZi^gF*g%K7Zc~) zpWl8e&OpsBzy?WHrNr;aaf1*8#sa^~CmRnOaKWoZSW6gMJWy#QE=H_?o#*5(`JU~8 zZ<;%>0_22&jj2*yF(ttxfOS)We#O_#0EO;Yw|l#RCDqY6x8C!stydV3g9Adg%oOzU z0KM*nLHEiQjju^UV59N)%-$iJW!O`AVGg+%If70&m0 zN>~sRqf9$jLoN_9qCw@wU&9VPAWtp+^d@Jq`QqLVsUp_SIImPjgPZk#`JC;E4vKHV z7>~2637^Ip{@eks#~k^!5cZj-h4i`i9}uV~Fej&1e0OcG&MWnsLi-^;HSy!qU%QyY z)6$?q719n%(o+{n3e4Vu&BWsGQU=3+T2Jm8go3!Yz0R5^{5`xa*6v!7;G_Y6f51YC z6AN*%7p*AAM0+pr+p{PVhc15Jj0E^mkD`OT0N6m9Wp!|=t}@&O6^=7yZB8XGtVRxk zo#o-|H{S=!-)nN1?M8{cOPwUO0xF(@ZJYKx6v-9{3Y1adT{bjn6k3lA)x#anw;tM9 z8QhDN_UkoW;h{I%-MM*oLa+MOD*RiPUo!ypqx;94T|_XBfAO2(AI!Kxk-gZ)6-NPD z?nnlMPyt`0lhM&jhLamU)hBB8ANQOb^a6jcN7JJ+#u`4gY;5Do^wmeEcMAfap_j@J z9rW39qk)-C5W}-`ctva>J3I7k%{@i%fd&J=r0iWMYL$!3RskxA@!LeBK;37@J8x!M ze@u1TbH?452d}rt6KZ>$rq}+8MMR0}s|a_;`F&EE-nFyf1{fwm582T4_X_fT1w}aI zs~_%kICtVfh&Kjb&rC~8nSEZ63H+wO;YO8Yq!Z;R7xDo?11v!)mLf8nWCF@L6WH+ z8{M-(g)Ch`6^ZRaR?A{XC(n;%fA~b8(xv|3T}Q?p>7ha)@&r(T$LdFzVW^nM^8?&g zNd2ha#-gP)n+d&6d)4<}WG{Q!_(<_H2`Vx3lD`8u!YrhBb05-2mLPsWJfNZFYJVgx zJ5693Xi`$?F^nP6I((^b+8PHWQ{AGDZ{Dl{F)OPTL!n;u1TgJW^Vr7}Zf2j8w;g9RLaPy;>LQrP!-0)Zo(FEt{E{KG0=0=qTMr6!>}zX zNUKlm5Y)6JdC?)gf<%c|j&CnP#jTtfrm3Thy z*DP)ijwk}4e|dx`cent>7Ta9A)`t%OaokTn4!irk(J$P6H7d3E>CT#=shnZbS*g)a zcOEZ)Z!CH6Lh!k~YLBz5e*_x=jRFY%HB(Wl6{61rf;YFuoutmn>NI$?LP+&39g;a2 zok3hs){EsroNxI)uv$16zj$IY7wr6dw58Gl&_u7@5#gL>lyy z$6}E~nJTGaFWVsojgy!FW@Ac<1Yh$F>9#M`7l#YQ>7U_pQDEb58(k7ZOiHoUq0)0` zkzyc%q*9=95RtODx*6ZEsxRXSoiRRNsVr>mF##S~XP{8_P_|p;q#G~V4(b31! zkxg=p2y5*fY-2m-&?}CUJ8ocWJ6$OyMX_)_v}{d@=U=R}9nN3jMkz+ih#FBJ>f#vo zCddfnnH{(fU8pxvlpPvUX7PA|9x@#`ZbrE!Y7-f~yMKW}<0wa)1H@*8Db&zYr>4`Z zUC}N_0fd2`HCw3I)CIA&BV10wnoXkC>^D+=_+)6K@d2>fNNE;rNEu-8HU`I}^({Y? zR2I~`diuahODgvG+Z0QqeqAmCxo_H0*M+5W@)+%>MN+=mf=pL=dcnqRsL@8)&*1(= zw`0XgBH2$KJx387|AOa^ktsUsNV8C2u1*9+#NRz!nlzXG>E~Y zCV#YduN#7?y|hO_yp1-o$Jhm|tf7!%TAGm%9=@kFH7J%^1!I+SXT640BpDB=UFIz)iM1bz)2O@Up)|ELQM zUHc4H?#JV8qB~R|r8N{u!{TG*h5~Yi;vI*r8EthA(f>yDB0gbiFMeN(5h1aoa z>*Wct0?nsJ6pZQsQU{|(A%f_sBPy_&75wTgJ_miv z(Gs`4@OzCVhz{Y*Yp!EC{q}~raK)%7j5CSX_b>SWS>Qy zA6a{<{h11d*q-n(_1qVCa5xr%tC|4~l=t@AU8<7{aPXGL@I%NS?647Xj{7FR;sDtZ z%>MdK45)!0{)#841c~=}d9CyFw!UlF4_pr``U zM#um`$BMc>m8nP!B*d)%1zMosYOew;=iAU$d(7f zRHhd0@SV%sh;#X0g^q`RVgC+xqxo$lwm>m;J=qliy!Kp|Hk<4@F=v=QRq=X_|vR#Nk zuw?bWS))GM@GW_c*c#;4@ar$M$sBK(f$q*OdwJ$3G8V2f-5XZ=LD*U|P?SUuY$u*` zL@U$1bO=0eQG0o5B3ixk{`z6o>Z9hyLj|gohhf)i9h?hl^-Gk4J&Qm2<|BSvUXwt$ zHgV)oLOg8BVzir>$W0au$U(b~R$)`9Qe7aZ!R6h?%R3K&f7$CDu*V7R|q8ifTZQj>Df+2zpXkrBa?A>To;Zp$}>M@3t z+#C^Bz#CW9XBOWVNy6noUHg8^23tzCNwK9qumn%K`dZcNa!w+0@XCy1W_j-h_87&t zjbF0_BLsy0VUwqWZsD9+M)JLJG1PyCCC#Bv zE-8vuLeG>KWVQ69D&|%UQ<||=cY2WGGCn~2bTA7S_bi6_Ut6VdT_Pt%J>WI?(Szj9 zmQ>Y6zR8%?5iN7e%4IFBBSG?6jQKw7#*^s`P-c0h_mxF1tz(~@Yu&yKM0#TFAfUH# zmS3Gn3k`ESEM5h36KJ@gf)foER3&Cy(oU#rFCnlrZGMwfaM!G z97{c~X8`hl)&~ZI#cQ%HI_GjqPfKxJ<8c0cK6sHv(Hb=#tnXI9MvWQk{A{I&RFlxL zi3XDJhQ=xvwO8dGlj$7_M(~pW1-SAR{M13zj*g$1BS3pQGtp0^mAWWy5&bi}=#j36 z#d@bg(^Lw|Q&){MUFCC0_xz?PqyWwVJ#mKNE|E5W1>1*%I~;-KN}M5Y@t<5TIxa$k z*%k$u0T0Nkv6H`O%_)4Sz0{1xZ44s9d4NLZZI#Q-O(f>U@KA~zt0OW(+BX4zT+Imc z77~7y#T5nOw$7#6+5C%?SZ1v}x;j}hxV6v82PiQ-c33$n#3M?6|GLy~G5E4g4ucS; z)ckKl*khZj9POW&lvu^E3a%%_?$@g zowg{=MUXf-AribmPw$N7-?Oqi$to0A@Z(oQ@Vfp?+dv&vgUy$ZTnNL>KBF+aSIq+9 zNYI3b23zO92k#b<;LUzZDl~(GOHzgfQpoL)p>s!hBShw5A_!K=oga<0%GSgNSfYMD zzFX{3$B&E0O`MTu-m8${c5BHoIPm$B9KYj~#+NYARCpWg1w1hAP&CpWOQ$ZOf1ypg4$4#tfJi?43$3B7Pt>=^Ogx;`@o(i)O45b(;{*rNQw*$? z_m#OkQ$$SihLrzWEmfP0dVb4}H_; z%VAvwvG$*Y5}i4`2LZd*Z5G~x1#G@A@p^wo+J88lb(mXP-p)w0VgAWwmIY&GC%?sF z6KN5tb+?at&Id~P@N9nW(O%=#NrLy10v7pNilHlm45>U5pv@K-6^md)0y(Y*e20<~ zqQh1AXSBm+WE(eqP-TN1vJ%4Ygbj@3UP2__77thnXkm^aeYR77(JsUS>FCEs1{JDv z$P6jArFw|TxBa7MB2!AaK=5(@W%kB@nP6^DLFv9-`B_Qj$8#zaxR>4ufwjGxt7?Yz z1&_^J{tE@-*fvB-PH^24;)1gUsGx#4T$=&xIpOS0C?;qve>l7yqM!ET4F>4swvjR> z<|6&Ex7x7|uZ53Eb=xV-9dbDkEtm~=6RC2MYA}No>sEh}_z2_xWNnmE{PvbA&r9&SDs#s(p(m-!ig zG71IbB0;cPY;dYrg>#gEWj+~yd1c>W3`jJx#^w7jfNSK@E1n;aDShWghU>lbQr;NY zNG?W(GB$GE=sW9kTWv15d}y&`@yAaryvy2^#Ddva13fY3!Hn(d%b3}O!n#_Ts%y!d z(j?_pHrs>o=qiilWk6x*$q!VW;t77{8)44Jr;(~-`^o=WT2a6Yb;&@}x0YtSOIx9Tvs8(zhGj9;xp1pq%sN>ns8Tc1*rhcH09>sMesHRv1vg_S9ZFQ`&x)#m63 zEo3OikWEn$H8Kh23m`a_=*mO|Vicw;G*D@aam7=SK6n_4KA0w_tKSnKNn``H4l=*M z!+zfc@lh7IoEG+8Y%`nOExl;dX+k3UkrSKJ;{Rn*xsAh0@nk0Qcr;(P@8Y`@J&3;F zpVj2KTGwZr&kg2u@5}sR8m749Z^_@ODALwYy#oMwWn3E2+FGk}q6Nwx3Uo|5(Ea4{OTTi77WZhW|^;M>~)I-V+$4i@qYXHgP3v7 zAxjcHD;5GEf(THc9J`~z#M+l2uuU#(yw4dt~L}8T-01>wN=IdejzDqm1Io! zEoKY9doL_Z|8=>&_ds|m{nqJ5=N^O02E7JD>nrguog&!k?qB$#yo)XUbwE|@$SALw z2bAdbg^JstFvT+sWJ9?$@+BMAYJp4UbPbxlZR}N>f6_Ln?~SBf0OT|91b6*QK=9VM z6qMZcrBFkt5Xu`m$|wcdZ35n-r{}&jEQ=eMzqEINBkXvzR!s3jsdV6MN?ZcKEO7iY zx&AiE7i^2(j`Wg^Hb7uv`9pO4ISFA}3c(8bFFj?rvkp zmngOrQ+c`2?bY~ZLR;gcL0xy!8{d+ZWNDmySZsbk)VUB; z^#BJ2&hCz=WN*u=9w$DhKhnsn3!-)0{fY@Li*Cl!`RkMbAt~gLnF}Ql0C2Fo{(`Wx zV)RloPZx9NI-Bo?*^3AZn%|7Qd&So9*&Xv&Mv4%*mwW0Jo^E4UY7H3VlmOS)0?^w% zw*z3b)O1`C^)&#eQtwCx>^VIMWvLm*W80!3CheC(#`{u*Qwq)QzgE{N{yF_#Lsenf zr-1d-4J(BCocq((fgS;lTiyp#8ldsb1Hcz816R7bojC1@fr^6@3#ELCC=le1CtA%L zzxynmca|7QYHw-bQ}^MKBfY#oZkg?2geNUv^pQlj&d~K{^NqspjLE4_jr2mH+r8+8!CzPaJSpH<&jHl>I!9ON)c0zIW%0i^#xz1vJ zo+7KCXsxr1Q4a?BLyMJ`q3($C9NL)$)g6sGNIE(HrK51?tGEHGn1;HA-wO(9jK1L= zeoE?-b{@0I&&JTK9c`@XK^+fzn<3&BYuK1iMOI>;ywN#HE-snHwjz0zsmV<&?{`r{ zs734`NF7yRFI*gX+G8+?&}rwi;1kSF7$Z85D&w#{UH*0cfH9ue^uOM&%7*acYv-3o zyXQ`^GW}(iykQ=TBL13WEh>Vw?v9yAz(7C++u&6H0@sO6C>?o6+f&9?5I=O9q{(bt zWCZ(^8AMn%DiFW`Su5m`IU4Xm0ygJ(955`N(kwMNe87+b)E^l0AXtXW)X55p)lBO^ z{OD^q_(zh~i!$@R!acGn^7FP4{u^gJ6=A@<+Gmb=LfjUk&CFW-xdoUsX=KCuuZCUD z#DT!BS!SIBreDpLyIw)otMzY5XH9-$&bII zVK8uFL9fnu#h@kKvW;N*7dR$epeD0QX_>0V38W*xx-+Ds%kv0?5qFOq+St(cHU(Y9 z(nNxw9}a(lc&m-T+uRIxVt?BK|7r;iWugW9_{22yN_VV~3h7|T-f$HrP@pr$HkE#b zL{ICf__qBmdySg$Xmb0fzh5MNpN`&mxZ-s%*e;jl1tM2@6a|`aFhOJlUUsVwgf-iC zrCOmNdY1~St~K_CL)GsYHP_6ch_{@>f^ZM4Y5b60X5k4jxt~bDt^6e6+VDtSG^q|6 z1+?|mwKPtmOMP&;vmDjY4~nF=`U*nQbz$PUX}W1kzJghu3k7Vtttr}{g3s*aqR@?n z++T={Sc$dfCEAXyZF)El>qx7UprMWJ zkq&1*a~zgF%oa;Dr6;F1T1ZzntDHrp0<8rPB>{XP-#)Aq%J00su`1zW<{Dl9sR(2y2tH#TwXpN zdYTQge?0Q@ITObKexQag9rL7_nW%;{b#j#LRijHwOK;WNjvnPe zuZ0S*YUzcnwBC+5Anpy%Jq)SaNxx=>nLZ}=_do+Xe7@kq!ZQqHFej3?0^uMS;O$@7 z9h2HWL^q0~=HTVYx%an`(*0olhjb-N6%Hp!a}xak1n|KUh`x~|q=yS$(e0S}z#!-$ zTz$-w&%c901Z`E`aWS4Oh7`rJf06i`CJ;?eiRkRurV86?@fUcw(e*4Q`1pBY3w!lD zksm0Gl8T6LL&3iFpRjnHrD~8Kp1>+k21RV#A^weuX%*XOgjp^#zEFs^%azANgeh)! zkTJjcek}j*e?HVbLkV6bzw+0_V)O@8&>R~=MKj%w;92RZ8MTh(p9 z(~b{!LZi%l)3$57r9-ylMRa-=S~y*#C{G1w^*gGbDwG0c$Vd`MePER7O2#n|<+mcI zMgC{uME{f&g?Ne}M8N~^IyUzvhyc#K@cdd*OU+DsKqZBKz!n(Uf*jXF$lA&Bogv{P zdRuh3Yj50Jk7fL2D<;n40V0cNYM;X}>?D?`+jqLU5kAq=&4&qeuCB+Y{q0(O0I&}2 z!p(L@ma4`f0(BK6xPT--o}1tV^iuBwCvNSsQssf*Ml2B+xu_SQ_sHTSN$CgPG-i+e zt(jv73oOKO7&=lwDCyApN55~f!5~p}vnl&WhM&BE0xj;yQZVzp?liRNZ(AhRh~@yg z-WAHKn&Xois)$Is^*~==cVHXpxWz=_eM4jdXIEa>d&V6V*A=T)$dG~uEnH5V++->x zR*yLZuVI*s-jge8PFw{mAg=eXV=E*e097BZA$Ja8@kt!hR5v5&XC4H&bi**ba~Ee` zirR(+V0cPm3`*PxWTlE;p~HoocN^F+OKs+V_d|nDIUeooqPdfPNapcaUHy9=vgC%n zTKcazGeQ@&P!?jaqX1pyD_}D1Jvx{4oS#CpBgYFtR!6$;;it0Afe?(w3J=ljFK`gl z{+fcB(z6_cle;$%(mDne9icWD6`*U4gHK=#CR{sL(S$FL)+aU`-ZvZn-J>wJqyIxV z(@M1GJUQAoHTNfG&#>%bIO}a~%lR5M3;^ip5VjAf9FzVokW9~tt>bv)Ob-YK)0+xHFk z0;Hj^6iW!QA+W+k3+W|OD)HMp2R|Q}iZcB@TrlEyeRZvW@^uI=NV?-gKT(O@buGgS znfC*&{%MBPI3iQ^l-5w`rDs#|s%X?8sJty^EI1xNWE2VdvS$v-P>xP9-n^c_o{~fo z8zDFv97w*4_X+gtAYIe@oRHzdl$HTtCwS#J1;A*s#)imJY&p2-P4#kbU;0I{a#R9i zb%Ylm^&)zrjmz~zX;1Pf{iz(r_`lOWFzH+#T>Z&<>vE`y(F(Bufm=9QI>{1_(-yCc!_!Hp)an2V8eSFX`IqKk{dFukXLx1 zw~?IE)B<7Ae@#JhVOnmcLcMBcyXA$wpK!tB-c~Dkpfw+ZTjxQ}#Gq_h!N$H;Ahew7 zz^NGg?tp_}_V{`(ga%KTJH<_kqf|`5<0}UGO`!=MnfJ{{ZaP@9S?0~<@=EbF`?32c z7j168A7-P0WGO~{my6Qn4`@Wtu?@laYA#ceLZj=yiM6}o;8@GJ5=|9&-wgKdb|nB( zL>&J9cr6KZEiN)^KtDg|3Y;1y>7cavcLdKNMF2Sc8Y-QX>)*V*e7ddL zylYC&78`z(0Z_U~(GT9E2jYMt5J=KKD0tgqL>$&q&T_+h8L1EF{O| zp^*V9eef*V&(0h^AXwra+xH^H9$GyJ%c&}y{DvXz@pe)yE^g}R71zdDEoZ}|Ixv*f z?OUquUjX2?6wK6)q|Pnvy-&$k9i}4~joLoQIj`bLs%mDGG48;4OfNrbVO6 z2+Rr@#}B!d{zM>s^s?p%Y97nh3Sl60VPR<`R*p~pg_O@7u>|nndK`7vlDFz++C34k zL2++lMY9E0u&pd_$@yw@mHcyG`4XOn=5|}TM#O^T*=;eN%>4wbKtwx-+IYa>9CrT_ zWO}DLT2&P|C=p?6gj&>l+C{6(-WvBl(h~{9lp0n2H`){`J@^@zI*fK-x~^%@wKy53 z8p;2-DnCG-~= ztY+kR*3gV#t_efvKhJW>d!@Q>M^zoN00cJ1c^nvoFnHDqXc~`fU1NrV=^XG79=3Gt z%WrQ?NUXoLH+%k{7J!55weU$W^-q`t8EtY$2N^w7CIyBm9Q)%2h@8-mpFlrM!_@_p zk;ZQwAS9>9r(|sZ=_~M9)!$1~J7)tYYVf@7h&b!FvO56rL|_e9iwQnndL&h|u7?7q&|`pG)XSIv8k3K}iPMk~vNZJdi4fN1QD$Ku2M5O5qD5c|WKc zOPBm1l{`LjNf0Xikr!O($6FBJde{zW6W84l)J+Dahky#N@~*Qll|kIT{`>@BF#k1q zJkt|ZSMdI{LMq7rqMUb=kNV~LB&bp@aC|`Fvahf^t`EFvp8Ke8VISdP#dve~2L_6A zp-ee|DUkk;v)~Ui z@#Ve(bx4(xYd5k5nOXLbDF+tHr%64qRFx_SCd0f>wsG1&Ibm^qQrD0J5?P!;)T@r4 z<|9ZwjU1Go2Dx&rv%san}yf1l-b=dh~cJ9F|;8E#t0z z*?_&`efdrtRe(tKP;1)xLTiVpQ zvYS_fB(uLTQMEnhl>rz~Zg$x!1OLXev*Nfmvtg3Va>1%$d!ic09XEbm^sC28asM#d z6%9P&IJh1?KyV^33&@6De@{S?&5a=8#90gNynP?oF}k4~DrmRt&6l=ckYTcp1p3I= zbYox1DM1MMQGapo+UmjW3qvs7_E8zJ_9{5;c-o=Gl`#M|c=&VJ(^=x+i$uBfj~u6p@g#n2AJw?;g@TlGOP3pU@9VMW(v&JzK36C% zGTIsL26Em|@E^L`>l$()zDV?Ltf3$TsdyFXEkU>z_uy&n^L(^bD%Zf zlYG@02aFBtdHo%s_(YQIF0wqJmI4e^zY)D`()(wpaOjaWP;^cs7B?f-&wacetQf@% zAzUNGO%OP68O#w&c8EFM!an)K^!D+XhYQfErRwy_4fCNG{=z^7&_tvFBb$P@%BF2&4vPceP>m_6cK_e)QhmEX<{V!jw_U3CgruK z)B4ye17I+c3@+^HD^1c!L4y2S-K-D<-8%9Dpv~8vNT;}vHPH{i8Bf@AiR46t#sVEB zA$|b)v;+oXriSyGOo~2lFX#8aprPN{mF8MO`L4RPneT-xZj&J>%+Kd}ys69+Bb5DZ zMb4Kk`8S6k#7I4r#Idx-x4NC)-m@XyL~mg}zQW=7wzE(8)if_L(nyEdzueXbazZ2x zWM?;?s_t9-_U+(j4LqWNy}%$!kc>zQC@g&O>MU4pmx>dD3i_a&88tgm3|{C70bFG! zDld1L^vuean{jC*EKnN&0j3nt5spq{`6{qc>{E?+(mYc%tgFNWun3d#!#qS7)A(1& z5DlM#J`dr%-r0Glp0&b8_2+#Xn|X=;EB5=6|0huh81DPQ7T2hT3j@moSZXUNz^A-TSz8o|j+c|7tuf9m50MWzU3@$AJ zKu{(q$v}vf$iy&C$8bR?K_BJuI7JA&HbOxSOjzHHnt8kZd0p-g{-DYetAly4us>Yy z%zRm}^YmtA5PBETxqsU`7po%^w1N8ytxz*64~Q@wM6&2i&%bt4#(uutKMxRqUB|#| z`G9fH$WRf0!TbCZ$ft;_vYH1)z=k18_lKs&QgHsKpNKc%qIA{5Q@1Y4J^=}W%k?*?+s%UQT75k*gSfKqB6ND#N%%(ULH)JC9$w%D_| z*dx2X>Fi0#x7@f{xS+xhDE0k-Cg8chw4lHvHHj=q9Rb8x`i9+|<(YTmSupsL9Y6^v zw^c}Kj!~pQtDvxG{WA>7aeSP<0;&pTNFGlVJGMmB%%v-XKiEZtxqesjRs78d(0G>p!vRXwmR1U2yKa&S?5=B$!O#ubJ6DB=>`Nc!p-J zal5klmYe&;yRWgxOd0K7c8m>x0wE6j!2{#@&CtA_;7A6@YjMCna)Mo%uN7(ZCDKC| zK`Q?Z$OqWd4j!pdNxm`HZ0kFs6bvcW(bQ0H!W^J$O;ogh5R$66xUhIrxgkF}X=eh_ zkJSGl{qKUD^hd^iap=<4GsAVGef_W{E^H_(SkPfTdxyEl-@@wZHr%jExdl$C>PyY@ zx)3HIl?!y%k=0*(#`dE*mBPi_@~zNbEiW{U&{dkA^>=zFigysI|LREhT(N&liRL3& zoiJ7Hk;$ITLy+(7OCeE1YY41|c z8Sm8p3z@@Bs??Xl<)}~$4l(2AA)wXP1}B+R9}^utU}ho%8wDmTiRzvlaVcLhm_97r zgZ0{>o_b0v5OQ{@&Ot&*I}LP$TPlHI9C^Ft%D;j^NGC>NOH#j(fUETF1zG?M9Yc;= zMT(I;Ke6t}aw)vY&imFQ2Lpz@78$v-!%l#uMf9Pt}oc! z7Q;Dy{t$SIsPWrZK@;eE>Dony6TU24eei^#gLh99yiGwgK^DvM>tLT#W{7njXRB5T z%IGS#b^Ei@;ZbQoz}mnsv$L_=%JUpIASD^8WjHtz^R2NjP~)X#bp$W)>}^j0Sctgk7eknv4{=~ zr~a%}z%QtwphKr__3Ep^L%va6j?T^69U0e@mgrxTjoY6z#e`@58M6d9`Cb?H=@-es zqr}FX8)(k@NBCU)h2H7$S-hV=Kj~l-6883D7)e0n^e(`2`~Hs3^+a1PJrG?V|WoPyX%R-MF3Q1#KW zne7@ow#f9)UK+s2w+<`2x{-YU>Iq|Dn{3%v>e~ZBLGM~7CF?tU?Cqh7W<;64HwNI$ zknlxw?a%?ey7XhivwmC8)5|A-6jF zZ1kI9TW>Ir(Q0!#K#Ngqx`;f4o?QZF4S};f{S{lgx$6UMd@FeoH}^-f`oUV~*@vNfBsY8iND*Ln#?*+QD#|l4K2pVRE>+_I zI`mD}zvTG6_@cTQcfT^ue?HAXCr|;`>Yu4ZZ>Bt)(OoIsg_;-kB2)X9uva{wGICKO zC48oAHEbXXHUftK)~3Z=vtK4um6a%bHM!ttt(j*qR%n^6kI* zy`xv>@Oy;mC;~UJ;`%RDoWMEpdIR@4F#xd4*&*lgBr}HH{O$cL5qika^-WsQrwUM1O}mQ555IUJO2+3FVamMVWOcHl z^Mpz=Ye(tqJOG{#Bi-fsb9w~RyT*qv z-*dDhg?<(RidwGVx?7-lP>UV`L?1=v*L|k66p}oe3kvZ}FX*K=&zf2W)9-~-w|rn; zU^$h*-^o9|X!e}5RC!b%fpB(&kbE9+LmL2re}2k(d^-JrSNs1k_2%JFcmMzRYpi3R zRMs$KSN60>G6pqBg(BvJGZLi5YA5EQON2EZLG}M1-;=WG5l}I{c3N{rz0m z@1Og+uj{@sXU;j#=kxJ+KAw-$bJxx@T~1UGI9uI+oK{^rq>}3N!x8sMV6CWfmH`zF zWyJ+ryiVOk0A4iwui}QXO>`tIex`DEoZVvUqO;go z;4b~wFQkaW$faF4{2++-MJQ;d% zZq3wsqc7g-0rzR4z|d#T?X;LvQD;6mf|Jo%H!bTlvi^qgmk|(^QIpxAVPeA4 zhBx-c8F;eQ!z7VMd()!-w8-O49)&!H>7U{b)lLGS*$GB)QY2hKa49o?z~ptwXt*#2XSPh@gNW`4iRGTV)w zPY_G&zP-QBd#<%x#th7r*bU{17bt(7_7juP>VNb8y*-cz_0|=dnmy~sp1@A<@i1Wi z_SU%S65sDw4E(B*f;7NJ;KY62R8=Hu<*47)1;dxpMUu0BgOz;csEmg;8BJf3gG`6P z6btvwS9POrGX1V_(l2GTd_EM%XhT$dhnxP z?>R!!{YQq8?jOIeca7g)QoU(KgK5ab5}O;Ytu`zFQ4i zc(Cz`+b-1`JlssS3Uq|aUJ^HDrR7{m!yONh!Q1mTOBZ(~d4L7eg0?;~K)mHDy;3FmW`x`EF_*^9i*Sr08Hv}!M;18;gn&>hH^UWQo^w#4Mhq(}D zd+(pvNwxFSHzE^(rY7eS7yU#bHG8e$`MdWCrSZ3m3j-mo+2GXdM_t1f0RyZZ4^C$e zzHDV6SDP+t66w5`N~e>gE-w!c?ylx+qOsR=HLI0mFo{c6widTt&yV`qzMnq(sYoQ> zw3(6{}YW zsu!J#8}<2GpOMeN*e%)4&2?9&bOVnU6}mv)k$>>R^gUy@(rHb@Z!!NK%I%`6>>Wc+M5|Jb7QAi zfT&8afg!}T!0u0io*ADJTWZWwcXw6FGSAtAZi86Q%wgeUg_C z)g(O!&LjatKEUDDuC+ci%br^Zu00`;{)Ap2QHl6QA)mklk^NrAGlf?v7fUxHS`A94 zq$*q^oebXIe&@>$90ejaip3z*c<`B2cV2FjKj)o2X4I5_D#gU#1Cfs-En`a(8DFdWR%)^yR%R`xo4y9bG+a)lR3T|Ycxy!nD;EkX~B-gcF z{j3fL*6?QRH;vI)vzOPeLfHLN>n8v~L$D9@Fb{NBnLSpI=sR&&I@KO%+t$67SK6|~79U-g2NH5hFgfFi?5w>TI_{F3ByMSOWU3L$c>+g#48ef{)z`&Dzz^^{Kc)8{kM4>iN`142Y9R>@fKTYP%|y=GtG#DVa!8*P&vNmXomDvH(8@i*3}t`ler}n(aK5rQ z;1i-Jz4x(U>_g_^oR<@EW6FRmJLZP3q7ScX=I-rd8$r?=JsPI7g7*^T2<7I1!dhI7 zpbwtml%jnVXp;;ef{{-x#&mLB^wa3TkNOq_Thr*_Okn2tj7qTeUK)^17+nrf84aXr zgK_%j$Fbkdz|cTR?!-0U!;x8#Ce@rXi~Sb6?{59fY{DmKplvoc#wwj3?DVGoi*~pj zkv?h|Z02hybqp%|Lwp=AiO}Y^>jwU2=hbLbwqDjwXzx{%Re3s+x?1{wUei~+FRC^0 znOaGU(P_27-&EVB<&sNrG;^j7>yef2mU+s$vYut={jtbTf$)@y4$yDE(eei_cy%ZJ z4!kuxt=7zRGw|uE%4vaxE6fCd6oJd&Ugs*9q`JP12ICfVRpcDpo5tH@pTyt1Y@j)Z z;n)^Br@OPbl{52dAa|(y*DzO>Tbpe6!n>d~l^6aCb%G{)<|MhL1oWi;-NW897|}kB z5gR<|2qp1D`rS+K?Q1-qmaehlfPA_4Nj4@R9QQ|eJ}uM=F2z)1C@};2sX!#)n;i!l zS-|smt$1oNjpW;g0uI0S^@zlLL{q67&}cA0!u|?Y<1B8jyvzI%Ph@0e+OyRM*9|N0 zG0u!`TFM9`Q+Ah`?SL z27j#ISD^d&m+8l|YcY*QU(c-k`a+}#c|Xl@ljAkS`#emt2K(a-I}%&wOpMRgl_%?f zA>JeAr{}XB?%@&mAATQmkw!yln-fnm4?YiO2A^&nRtPTd2c0`IpEA4CanO*H<|^n< z%i8nu-ZbgQ0zU*)Y%u~?c0VB`Y|ep}XO1kDoF{@g+LFkDUtCFLUE}+dQ&9jOc#zu# z14y*t!0WIFsElR~2!=Kmufe{=xg*}C8z2yrgJMHs@Ud1qkBCpie%vwB;BCijb- z9X#oNH#58)R?E}M=rj4*o8Itte*E&mZ3ynH_OZFK>Ksv(=B+0H-be1AvA79vw!5V&S+knd9aUPMNw z>LPPzhBrM(9xTDWjw*a92ca`{nOX0tXF9grfei`YN~R?5o&fOdej)isq+S%r7A^Wk zEc&GvbG9l$SFg2sUbp1^hhDB15Z+`Uh@-rSD#+)vs4+795iwZJ@pbRe;msCEHIiW8 zk4CEeG!E6W4}c+t&BXeX+@TM5+J(HCxyH`*5#J@2SC2gYH`=*G3iZhpw#THl2AqAskxE#nGI$b+OFt=+ot5hkEoAAN)0NAw}8dHUOVIM6i77& z?Q8Mc+x-o-yeD-t(4Z%|+=}t3=RTc!iTupGprGX9qTH!LPv8d%&$k_m!c!HWlYC->muI!Oc;F$d`f=aX+>mMSn4likdAEb+}^b7nCb2#h! zW?ot|eYEo#pv)+U92o6$^FwskjoYd;+`7UmHxO5fYl;lITw^iT_j{YNQnj@Hoiq>@fz4&GxmTC*H7tS)ZgZt$T(5}hQ8!<(R6!HD6UlC@zF8) zeD=wFYq~#^I^wYq=er~@?)c{sAdBz18k&SgwZC$0t{m?c{984)fE#-H^i{Wfz#ZA; zvhW%{Hb@#z;$e)qe9Qccwb|g1p1T@0*avyUGhMaV)hNubJJi>DPTKI~N)Tkn&cM^W7j(La5ewXBP3>-ghL`W$QkK><+?;ys zd+kM|yo$f7_=QKcCjk*)fy2#?et@$L`&hT+=)MS%?-3guB0gmdicU$g+}Qr*@yNL- z=)Mf_X#l@J2~N)EhVvq?K9c%8K6OLW=bjUgB3_u%UoL7EgtC&JaPSD199~67C|wX4 zOWHBG+7#7k`fpnEFYl0483y%xP(gxeZKt9b^R#ekQ=``^o;m}j&lIe3 z0h4eI!VYI0MFdDFyb@prrTo6Y;bUq9u0gmO6DHCRoE(O zE$MZUWQ_?3kc}G=uP77`q+;Z?#rK_6- z-i3aEL!rSpUt6ki&&=t{h&cthiO2K?pxVLMeoBbB*;;Z}B@fTU)mfmo^)}+6i55Dp z1n2ua86bC|;L1OCFO9=po`x0%OW+|JsO!>1%8YBH%0$$JK<#Kb@ z;4`CU&mGo+)SHhtRmOajf7ok5g(4W);UE-;=o^oQ+>!fU?H~Eil11FopX1Fy{Y;8% zF78q@Eef=f_@9IZ$rEl;xvbz?N8m|6=>T!(jODGTN~(i?Ja^7ywB_s0zVutp^W3`| zTIVz*dPN_})@e7j4_qPW=F1-(AiV>87FN+S`wMVyw@C&QOTUmPq+2|96b zLOwj4vO4?t1#PpW$g}l5)}*b;(w}J%va9umNj>5$QZvQjOKEVXvwu8p<=^6|Gdwn4 zak*_;_`AwyD$n;^#+DqHNLjSgjey1-@#C9J*%&3-I>Tq#nA`9N@|q8CNBA~rTu6Jp zaSAzCxfD+~vZ%{jbBVJNF?`YvKdn<=w)MS8hG`)^4q> z8qE(^G!{{-_QNyBYj$GVUOf?xj6Zv@r77QRF6U+DGoI$QroUnL&Q)G?(CftX$sbjq zGy>xfTEQ!=?V)Z{Cmhl?-?0F< z_`Pq({@$MZYj2MGs#`pf)?_2St9=saXsX|Q)Z#FjNFz_`_2M?J@s|#2|HwH*46~?1 zNBmx~sllCaxCRIr%^s)6*Zz~c*>*~t)@;leV1|?LGu!a5jB9e3_%|8Ykwa0ZhP4^c z(powVCrqx6^iL|It3nkD;?n0PZt5nTFriw(GyNKi1+D=@L$q4ZiEsXd_E)Vo>SW;# z?)HK=W|cSxel~ZuJgv09BmJ9TG%&rP(Im%3bK~a#@ml!z+RX>%L7%%_cRDkLOFV?? z{?dWpGn`M#$$wmC#KXh+_i4Wx$GPGI{a>#O8{iup?-&oCcjT!>^u#SS?;2M!CAFNciYF3L`5h$TGj@#aa% z1`pmpl}b9`Armti$%b;~yo^BQH%6BSbc4EltIq#yOVmq&8gfn~7Z8oV6Kz!#eQQJF zuKRuNMvq67+qC&^?Gr8spPXhG*B3Osz106adg0YOD#D1mTlwPrf>D~q#iL%)S9lQR zF7CP0IDMMM-6)507j^24&(zN6ugg|H3wJL1Px%6}I|S`RwTd`icjV+9v|!Qn3h2@3 zVv-C1od6+(kM(h|j!GRl?L8CKaDi5izLJ68-a1a+0RvVz-~`}YF@N?p6qQx z>9d94GE!wMn%_g)`)m(?_ObD};YH{0Le|eSe!EKV=Au&QgqWsMzCW}AkH7lWbQ#MQ zOwHHmc1iEP&tEFxpD7!TGZd?T>mvlb)mAQf6TJl{`dnJ)<_E#u?*6N0D>NW?P z-dA|-KxnKkGn?7RoPECA#eS;Mv}pF~06-~K`OFu)F8AWH{(f<*df4lGoyUv-a*?8g z8n{m(>3g@fx?H|H2WEzzNNW_LNt~KbaiQKi!CLURjH|qO@5+}V9%66DHDxtKeK|ic z4~nL};>rEDuHoP2QEeJhy>%t>X=bt3{7Kte=T|u6%?%ngz1gAXAGdN2Mn>|3A6ziy z2qb>HaLv4*_U6ajv0O*qWVDfJO4n@I`QM^*Z4KfG?f$gbQlrwMyr&x)gI5&IA3nU0 z^V<6?K&iJL-(&hBp71jNw=|&1p6lKOZgoU--!=Qe6$Nlq{=rn?Tcx!I*6;sP9W`A> z_ly(Io&wfnaNux#q*CsTjR%z`7)VF2-9xbw2q!H4cPOQQ1{O+=IPl|kfZb#RS6EAL zG1Z|C=Kx$)fjfsi2&_jl>?MKRsDUTUvxqWbQdSzI}X@P+0A_ zM)kaWNuUJZhhvwf88Q&+bFNZlS<}Q7evHx|DAs@RwxMRryQ*9Hmn-^Zb(%Fgs zetvrhaSXzIulO=GKc(nkx*c?v`!q*#hUdRXhw<~W_wD9;zv4Ya_+DDC(~kcPFg=Z+ zN!|23R`;VlGspP~4C>+4>J{7Qn{$hH-b#yrkTuPl&pT5p0@K}iJP&MNui0m+%VzuK z>!zh<|FMIi)5}5!Up4mf{t)>Y^H0Es4GIWr3edUgweV(0fco(Wf>wrSK8-EaqI~5I z1vHmT<|ELheuvxVNKsRc0Py7b3^nsvl0{{ZUe5XjFgVo@2==|5Q;L0keCf_f4%{dYK+xXgN*-XoL0M}t#O5;6INlz)A*}yUqHbXjqQ}S zUep73;^SzEx(-JH!;`=JoZD1^nemsQnwiaCI&XmHj`FAXmOukJ)mdUttf<{!-+TI=`jbvo1^asQl@H_&E9TKr>%9eK9Kxlqd%j^r7Zd` z%1%4+RU_G^Z4&(%{(+2!(`>6u*+>XUP==8delawn@rE#+Fmh17<)ZsV#S`RM;cw?1 z*xP5#x0b#<8~y0BH9BHZ`QGT=9}714#N`J*Ki=pX8~-R?IOG;QTf{~asj#28g z*lCjFApEOrlY}3`$*pz{zZ(&{k#D9oi5cBmIEJ@-_72p6w3zs23G?(mBw?>EsY+tP z&w_?TSe1dor(nqO4T5s!+j3FQ5Yl$km7Qw2z<$>C&(XbiJ>I?iBG=q^G0=$S402Oc zwISa92K6%PP#>Bgi^t(8^G?w*`mP%+$-e%$NF1Dbn@D)^(vYXloj)wElDBB=A1zYa zR;-1(?)MP}_xMW}gxbGyuD0*e!9@@DxVz>%jI&PZ!1%EdUmZwm_1>VD!z!H^vIxaHB~oV{$xUAy5jYOS zSU<*!fGQ1CmOqr@AdwVNTMSRTTgObI^uIQiDC;On=|$+C2%mZ^bt)){PT>CT%L`D} zXe}9!Iwn<&gDJ^9IM6J$y;qcUQ6{Q!F;x)Kl=SA{p&7Em-hk&=si&klWu*>VLQ_g7 z&s9D;4MUlyo||Jt*PM=oq3E}`6SoS=+)KWSyib%7bBOVC0=|GxQytH0XLY*c;$L^hb%gYm*JQ??>-Vf0< zXsNyTY^}kA795AHnbP|YJ1K1Fi9hYkvlqPfm8_PpDTDZ5d)a}h}9{0gcRt6-dw0pljo6)N4gSEGqyyo;>-AoP!7|F1{ z1`S_aL(rjXcppo{$xCt=Dp??WDxm1T6?WyGu67Cw!S#o(C66KNRtLDUS5k!8U;m4)9@&QM^)f$%n1}eM&!>{ouMnkG$fU}Qf?Hv#g*npfT8e!yp$CA zK4oF;k4kr^jrQd;RV_HROj^2^4;Z(6^@YAK(o)jIj4yp?Z5Tw-&DA! zxXZ?1m#_S5HKbzjIN`Uz?$u-U_!;s&SjMu1Xi3DbhIJ51;<)F8x3L)cWa8m86QZT` zZ6bvxpVNeSQYY|=(HZ>4CAU9B#u(DMxiFh!wO@+e^Zi)P$91a*9VNCYY+oU?d| z!yc-osKOwoh`e1QLfR&|KJhBuK~Lmb$0ha#k_a@Ukf9J$&(iW4(ra=m1h~)AigBjzw!Ckg~hE=M7HT6ml7ok?(lQtu0GO;oHm+R@za?Nb$sxKb&L8wurs829C zj`C2QqC2_^|AljcVdEM`6R>tWx>gAvM=9cDFBpcVx1CiZYJi$<^4KP;ii(Mb=$tHh zvOdX8ZLi&D{Pz!f>Hii2xJZ*2=QKa=_kNmc>`O<5gcK6a)OGfYBGI+ zgY&{o7+8}TP(f>-#F|L`B2)DCHx1;Q9k#4+L31lyuSQ)Bq{;Wd-+bW56?9q@PMG>D z=N^RWUs6?vZ>eyk?LE``3|tY%Y!#{Ezi4xH?0WEsXrJTG4d+I;ypf_W-9@RGLRA$Q z&5pI^5?ehx-gUF=4>xDvqYaj0Iv#&+gE?2!62}+OvS>IYka(OG3dnW}l;pZ{KFphRsI0v$@TH{it@JuKJkTs%t>g_!}RJUgctgt9)m zRN|ZGQ)jBFNORk|2Y8f&_}m@gGZlet>e$B5;(xhqLfva26q7u`K)B=ln)qBb^^*J4 z*k5Q#h)(G5(Te1faqw|r&Z=FfqNJv3O`|bd!m!8ZNlSY9DLS;%mQ$o|PQfaR0={w? z`hCeTxLbsrFeaCq!7SAxM`!FP0VxOL+TtI;F`jZa?!vGal=Q<$r^&C%PC2b%-`&&w zxyaLH3&xaRpF8dbUpu;1krytE@0QY{GS<#z=`CD@I4^EVf0b)4JPv~!=b6j1Mlzr} z5-7}!sHpCnI5=YpEmX?;C-YAodf2BIis<)xaQE_rd@~cqGo4xClQKIR>pLCQMpi86 zSB6bk6!3|b@vJ`bhPGmo8D{?5ur5Os#6qI1MvXNQm};V`&v(!0nI2RH(SH~UQH|qV ze_X_4nLQ*{hJZa9y8@BSNm0UVObw({$Lp9i+`C0@N}!uoBU9sJLJ*MiwSF_GEU`W6 z1Oa=+@MQS6Ts2ya+zaQFepm>FlR^<)=i*pd-NF!=in1)>=Aj6?IR@Brn;k?mey>NC z{39Jw_o6;gQhgqT(npFu2p&%Y&oH4V-!&Q6Ha}g-_oQB{=YFw!P5|opIC^V=Tag8G zaa50~I5D509zxL(S2v}2qBQG{BbBY`Lirj~ejY6Fi%-4Evj5 zEsb1XKwLfONh{vOKGtIMT2s=l9AH)8xUNGY#`rlof_aL$bUi*1MT6MfZ53P z1+7aErs_hRf0q|3=a+=t_S5ZHlv%~o4QLvuZyWoX3tmdqFHV#{!c@Y+xEciDRL^_5RW{u`u z3r4hehV7@-qpU#*IH-^p{ctgAXA4G|x~brC6ghSzcAL(g*9tiPZnG19p|G0r6o;lMQn@OzdaH3=DZJ z}7v)=tfPIQ@0xm+_bhUu}BP#z4mN^O)0 z5p#Q;N=v}@-%FrmYd7o?uXouF6!ZLd#nj;k|GQ#7rrDE7QL8v{MWY~yQ$O)n9;R0B zTQOQ#mxUDtLv^gOlSczheWcRj+$_!T9ZYK=k$72)gkw%9K z>Dk>SdP0Pfv++yZ6Ld(0d1j%l`$?45uWBd5<(;FA8b}rFziE)=EtKlyVqo zWvaBajxfttCCIv?;Z~$Hh=W6I6|C~2toY{X$wOi+3xOhiqv`s${Wn;o423&P@v{T(7_qB1?z}-L4T!VnLE;Q| zZ#pz2N_Ybcm;e4TNs|k`!P2gz&9z&j!j2b}Uab|MsYep>I0!IA$X6!lK7!_!SlNlB z&s)m_vgG&mwhX9yJH+M?MD8OMa7z1-&1b(Na3M)T%+c@2A=y@k91O^`ZX7@@X)ndX zNNFCPk0T2`5fj`=@6R8@W4mWL|BS5N##%zzUSE0pTRDSJ2S~!?h|)?8%F#*=5ev~4 z()&KnuotsQ&NC0MjcZA+y1<8*?-#feDF2BBvVT5fQ4`5YE*=JD1u!Ln=)#5$xKwS> zK+63}L&uW_wM+FR@CzoMCQrZl_=h`I$xV*|xial@vojD44?KLvh@|NbgTq0jIoDm)t~G#=-9Rh%O1QCqAT1{7zx3RRgEGG zdm?$I;8J691TKQrqt4$DvU)*{?T?5sG#I8@@!>cRpKb3rHzy;;*D3<_=yxm4U%nd5 zIYy?!HE~o3s?_@aCX0hN#KFNU5vjYzBBd_u_&ORK?j7by2)!w|^*YETX;`lLENk&` zWY&gdi6$2xKGjDYia;L-AMa%iLRoT}{EGX`tRXou0RtPBd5DoZL1@0iDFayt2?EQE zsk30&_CIrWiD@%hKC0!O(uK>tpGkc)>DH3A$@0xhOR!pW#F#eo@MZtUc*!K@Yy#^3 z$dcoUl^}ye%Oyk9W=V!HKzZx_F~f6cY${9*A_qL_t8FuVLJ)!4Pkx*^NE1Asr)hK% zKJI$~#*?B~TfUdtdYo|a(&91vU4A!}(pe%`18Ii-_ju_A&Z)=fBCu;1;kUcQITEGp zD=%a%SqZ+z0+a*wPf6qN2z>+LXYi{pmD3;-`WX(Too6>+y6+OF5-DG-e->HwkZX!8 zdGAA3*M=z|gg36=X+YF$f@rh2KX-}{FD`QJ3^{=vlQ_%W z%ZQQ!tIZs)2>p=Aj7Le$m(n1*9v23o@}iw1LCR9T^c)9233t%@mPuUDtkmQZymsq5 z=f@eh%T(UUSjtmc6s#Lo6KpldkXBB^5JvJKO#ttS-Tu&h%q7U(Th8XHzz2niR~-#Bq|rJQL|C`J^_RQ17W$>uHP%6+5wBxsvA>f-#TeAAVnC$} ziEgu;DWbo3BeL)_HPsmMZu=)3y~eWgxF}!Z7&Wu&F&;kEZMw|q0OGif1f_mW%bQ7h zceM{sns;NWd;n<{E+E8RZXy1!c*0pz#NcFKiF-xQOyb6F{|8S##^qhcauBL65N|lV zrbR3TX~aLG)bu$iLWrWH&2qlB9ESM1(P1tZ_Ac@wLXjQq{^JRI148qxGd`NLyLxl0 z;X1$J9w?q*%S&q4?zwbEQx&HJ(1yN^lX#_vfS4!+goYQGbWi_j<&mwHYE z7AJ_*4+{smtogA?LyxSy)9awTF2K3C>_M8PxJU}20-RSPKX22d7wVgHQkKec-$m@$LII z(LQ;OMeb6orJ(>6;!xd2!^QMD(oY)hu-vk!w_W%1CNnFV(#r*k6N1bBN1Fzc38Ks@ zvM};Z4n3+Fz#Y*tQ^e}TuM>P3kbZA#>`pY*?lz5Mq`aoSQ?I-ogsB)PM%U5dNnH5L zQi68!?9pVn^J_u{Cfh)kl zHNescR;%i48VnHOVQ4d(#tWQIAP31xk}}PY8s}F0G0WDtsIu7f_VAR4`a*uD;;z>e zpJTv5+Q<=XfnBCWa+^F1MOiN|Oa>|-r#xvic4rGLOYxjEpKnQiQ#wabZv5BVbDPhZ9O$nTXrRvs`SUc@c zBo;(=T~P8K6U|)}4vh5OlCN>G2XAu@j+KHyFv@Z={8pO=y@qLnQI;wjYf2A7>4q)L z+k1ZufMH@I;*H!V@Gl{yI6!C4iHm%F&ja%G<{faRn9%vHXP$GKMDGt2Ln+kGZZWF} z;Rb{f7=9zO-D0t2y!S>ku}YJRuuYtLF(<MGlr)CGi`H|0k4iFab4xk@nncNlp@Q7_ zSJ=mtG!UFlp0Y_$|C!xjNJuaihA(_4T|oV#xxscFQBE^O;oz%{6J zXX*;SRe_bIEbGK=h)9!Pv4_h?wS!~_`H7VkF@~F`K`RFEGk1hLI0&8o_}qESTQlkB zp(L?Cx-7bO7I8SNKBvhszd~~)q1Mpr)KzsX(?&Q*H{5 zkvINZvyYhri2mmtEuRz^N*&U)<}aUmwpb%mqug;R`YX>DVOTYTF$!qUhQbW!Y^Y@c$)4pHeN9mKiI`3*@< z7?vZxoi*OiVqN+=Uzf&>k_mR&OBPXLLOzWrbOm1KaJ$ z!V^4A70Pb_%`y+;sfy$=`Xmp!j)uOwoWGLCt(52(PpC0DDojOi$uO!3T?b z^oi5t?QrBS_A5?*gow3E_s0{aHe#k;(iMcUzD_HPTq*@ zj&!*Tu4m{_t-`6ZnqPA63U{y&Bnx9&I$1m}tM$=8{2%w(h|q+}e}*bJwgg_1$>8B> zf?+!M4+O?r|8!Yz2hyPurGm?bneN$&-^5Fj@=bGb5zCoGDuyCjW?*~MqM1{-e^9lt zFEHihOY!b!bFTh};0+JXHSTvR-E50e4J3O=G42qw2jdTw0X<`i9Di zxRiF+Q{On==d-(Z+MFh93u?b=VR*;JjkgfOou1P~0Io3oqs?Z9sQkzFg^&=Woxc^u zX%Ye1f4Igu0mDE=?Q#S@!iyo_f_;p4M)+7D!krxanlX(oJSyRRFb&=73u@(`5qC*A ztYxIM89}rEdY!J0ofp)+niyHJ^n!{3Fx>9ojc8fd>hj^uW?IOEwj(?UfaPCHBuR3M zs`7UZ4cB}2u!u)LPKTocp z1v`OCx)114SMGrl&}0U0|I>@#tG;EISOL+ktcN73!N(fObA_;iY1nX#WMs8H<+&a9TE{Gu8YK_lecyvK<*$hESHBfaPXg9t8F7 zX|fmjtOT_3&J=*p`{nfzas+3B#!h5kn6)XPMJdnNlXjB9(mWSStr|$mh_ZY`R8abD zE<*eiw*iK~?KLkgT6k3kg!@x?&tNth)c0w9N%BeHm-Mp|pda6$8{%{MA4iE+fLNNJ znW#rUj#FBO?kI$943DSG($jF=#f z2Lr=gFF8S64)yM*ZElmZr6aJ~oJNGF>00f`a>|Me3@mXAU*<7tKp@DJON_|Rm2`&5 z@q5G<{78BVPbjKy zy^9ioC)d40G?0?=c%g|ZW00H@8SVTyP3~Q`1jaIZfccTkMd_MB8bH$U;E$ z*ezdV3ql>svPi~7n9=C=6d)k7A!-e>yE4c{K<396w!0Z1c?eT?0|2D|FW8+vlPU+V z=8P;1KoYj&<;~*a=eV2L(86D0&4gKutj>02T90THf}(?*=4(+ z-YrHJqtyVoyRrmytgHXTiwknIM$)6iFXmTMKaQ~fK8UPWoIz858xc88z>4JLD#A(z z3lL-acfvG{7Y?R z1mF)B`1UdTs_3F?!W}~R>su?w;q61$+?A%+KdPQ0V7ESk{>X<@=4)8JduuS1WtI#r z&^!FEAOfu)1*nSiXp7ptPSYK&O7vASJBI6shK2*mvF9YPgm1SvtUbC!HWMA(Y#8^g z`3m0|D=Z79sg?Ek$?t=i-hmA);a}N-W_sOBOnzzybqwFxoNFY!0P23h-{U7I%}>E$pwSvR4FbsSFuYNS;Csh^2N3dT03qJuB796oKtI+% zJr*P=B;6}r8q_JOL^*h|>WE9Mp46d5DlcTV$=Sidrvd*F(p_g!w*|lDWw)H_dFwsS zyJ-$& ziB*@;Cmt~~?%m!!P_m$LRCE9@6O9Bb>e!J%+cU_NikUg050%@}lz4i*$DPq?iF7FC zwv~0?R;vg(xPrENo$rg69*uAuqAB6<*M{OWtm)h*PpV-8<;yZR#=}qB4kj5(;p<~$ zUV+AADF!p#ijQdsZ{=oxI&t_}>LDRmcFpm1mgGyHB+f;Glr%D?v(A!5i%gdIM35&i z;#tE!Eeia$ubYiO7VGjQ>#`vq5LWnrwKywd4lKbTm4vS_zcc4?r$L>e_<)G8JO*AW zpCzDfXYJq`ee`-)}2Q8f8Mn|FII=AvPv}eypzDknmCQ@`w*GCn18!Q+XKZJJ!wuLS*{0 ztgt_vwomPTMwl1#0(eOz4g`JoG_QrGoB~Y8I`JgO zH5Qog|G%~|0Jwpin)0GtXf0i{zGAKlK*45$b2N4p$ZK1IuH zToLLZy4pm-k4Z-s7tHK#b#uUA#tM+Au$lqFK%7-s?W7_j#%V7pBGg*~Z6MA_Cm_r~ zaNQxU!y?MRx@+4B7#CJwk_6>I`#AT+@P(>B>>Ltk%giiOSaN}jV(~3uM=#pG%^O7l ziE~Fsz2#U=uBOoj3waE$4Sq(3d@lWh%X!GLK+gVTI&;W;b?PZ-{?V;p70#&9YZcn4 zv>8v&qaK@Rf-Qg)$Ajgxguz=-(%!@=!9hsz(Ix0g%}5>GDZxKKgE z<8F@F0(GC9JNIAiP+1N{ble$GW5Pek=zRb9ovsjM@|8iia)!I_tcGxR)k&I0?2!A< zmxEn+#8Qo+B$)`r!nu1E2^0gfXi`HGq+!IAn!*xKDf^&LgW=q@NEEkTXVGRrCeL!z z?=Z-WU{uj_U!E^~{QM%ztK4!>uG!QEkzI0yIgQ(r2C=lFV@Qpe_G3p&POidGsl;^< z!=wGrfO3jX?D7IEmL(jCw*9P5c_}0|b+}6RuU7YOCFa^1kO5GdM-RI}ePGzlpLawn z7iM_d#)0;BV4#Yn&DG2A z0_1RMp%aE-2t25T?*3N`5nr7k_!7fg)-3{&1cxj6yy3_X{F>ezCf_;TV`g)NJcM9y z@x(Abg8dT|&3Fm^=tnuLqBzd0FvaM=ERu{Db_T!ix}&b#rpSX2;709x5zJcqT*0^HI=0TWhm z@^nB64WdRGdOD603tB)o@jNq^q^zua-u`6AM}_Br#3F}0u|q8Rzk+U;c*6HIftesZ zbvwcmVE{tnzHcB6GI^k-y6xwZ?a~i8q%C#rarOU4)tkpd*@tic*Dx4MjFBkYjD{An zSCklI%Fse9O7^9)N0w}ZAzBzz6p>O=QfRSnSw;~_A-gQu_kH`FbAP|j^Spll-2dG7 ztL|&A&vKr}`96-z5b7RDSeDl8gg>LkHAPC|wEi7j{WBHkBRrqbDBE~3wHi@El{Tkr z!rfnHx1%&QYtNqXeCmi3Fh1~0k@Y@Has{roa`t4nh9sAJ?6emw zEfUFk@3|P6{N1>~^hP2X_;$Z^*o#Z>yv7JC20Pwwfb z!Q=H_*3IfKvqzsG`hOCllJJ#XhY>=;l3DV=BpM$x@O@9r{>RV*3I=iSD_5*MV7beX zMs<3;caGPz8&QkQ7MT=L@n1GGu(YyPW^FejkO?20d&fm^+f9{_T8%vqY8x_-oNQFB zU?^cLiji~pm@3Zy*F%(yigT<-@jw-H_#hv_N-Wc8L{v zY`KAdKWx&>?ulBY#6y21$vev{h$5Vq9#wNPLNNw1P5V$7J+qy|??V_#e|`Kf2d+K& z9R2L;&Xx)2RAZLzlYs9lTbICOQv|d)*%%4j+`Hjq>Jf>x!iwn5?LT&7jb`Ti4^ z`R>sVq{z(qPh2FNjp850{@vaV)kjF=16N?J!VxcS9J@xYU=?EDE}Dp+o#(_qAHTqc zF@0}=IgBxVR^ka#*wsz)sj$om#MnFJSJvt|5X7#uTUuQ_Z1Bz_l%=O;pS!dGV51KZ zRzLCmP_eZBC#PyFrg7m18iuYQn3``rnOa9lR8#1V)uuXK{-MYP$_jAV0GlMi+l!FA z@H6{i`c4>g-RgX1oox2Y&JXF=eD{VyB%>wCGb?crJYKDLJ4sj$0H+TE0=NPm;mx_Ru})R<%P^O^_j<+QM4 z_>Sv?9&-r&IX_W{YC1V`+`G0@vS*`Q_1^vRV@yTct#tI@s(&CG*?c~q%o(!ImIB5D4$k+M45E6scmU2i-70JD!Hq31DIO-16OX( z@)9IPVBo|EO&FcyEwAUa{1{^Q&s8XaR)L}!$zM?|W2j>*By5i5Z}Y;Q`Q2w~2mmRf<2chRh)Xv=8ZR^~G9Uu;vaD0m zynSori-?8S~))r?ZozKhS%NfIll7r_jo;8y2wha&BR-r>EHjWu&&>8hPrQc!DTMm z0ZN&?#|U0)S>c>CFU*u`kow120kE|Iti7Q`w>q&9%VcxY`EVSO&gOn<>=(ZUPVTd? zbrL8KcVkQ+vpV8ujsjKbWOW^CFl%d|EvIp&=Z+Ssqt4t;68>xAjsnr=Xx4SKfpC2Y zYu%M8qTQ|QcK1Gc$sS(EhK1zFx}l3m$Vea0shUhWfJhsfQfCW!@)0~L_zg7UdEPks z-L3rt+&z0L*=?bPQ4r06Lgv4|0ww}ZHkaU(M&Xvi#-CY&pjy9=lq(V<2)HE*vAWU? zcN`%`=3iUtS4H~t2h-aYO#5*Fxn_AUM;*YT@X8)F&6%+z==87~Y+%J|5eK`DTiWiu; zs86TmV{UX>db zG3D??yx|}4w%WNFrdG^*i`JQ!TCX_v6X9)m4 zzfv<>-v3Pv`SI}~Pl^LI6pQSeyvn{9U6hCb;v9O(-uekUK`|O8D%MddJf47Fs>Eu; zz7Kg5M);!U?4hRPUFjqHWFTIo2H)Vn7cFS%ad2Po1HPR&YeP0=?ttxrJqylUNa~et zBr;PzkkAnq57mvsjOegPQ#|vJ(|07ov-`*XU{@6L&}5Mb65bfh00j=p@_xnr`!(TH zQ}mZdwyuOF)a&fHb9UQqheCP?qnMrE?-T#xVGMUTI`L^d(LuOS%T$B-9`iM}O_9h) zmlq8ecWq!J8@%eyF#8UKdD>YK>6l-SpNcUTPpf$Zwvnh^iytC+DNTyR8`rT2kPs^I zpBWven8! zmuI|JjW;}k{$!zk+Yh46sNJ>MR=TA8^Ewv7>pI{VEOHPa(t)q_BF`TpB}szFF@2pTVo3<)4$uFBuyR1zfCe z#N>=pni5N&kM(Y<84zh2LnTj?XL}rc;tInpk032ByyWr~2D7iOI}@4U$C>=7r+nD+ zq+XHkds;X)=)FZmzxBCiT`y!UH>Bu2_5mt7+DUx+ci#Tb0{U9gVW%Zb<|5~{8&IVI zuHt;S=aS4LV^*DH0mn86Cf`N0DTMK47bagNGIJ+#bpf3{XmVI}PE7HCpAU#R^+*vk(865?v2Jku5vHx9gXE(LGG`&`AT^J;8)~983TF z5W|L;<-Kt9`jvyGttoJTb4^2e@}&lXO}wC8wH$V0-c^;Iq-lNPknG9EA$90q9chK$ zH*qG#KTVjwv?R0N#4(%3SZBPtOpaSSJAVBWN$OLPrl^WS@Qou%07tngen6*TIq_-l z!=8UF0;y5oLXdAe4Lev+%de~Xo2v^?=`9S~5^jXTPRzf-TXJ*a`DPbRu<$baO6Qxo z`}z-k!~0eccI>r~k}Hw&nkYtFn#TmY%0Rp@ov9UPU+^8+2huGI=1)gjx*n~DYihOb z2(Zu}gZHTI0o3&q)a5VKg_}^=3e;O$n3*G^mjn>VFK#3wXHO4TBVM- z(ThE$?TKfl<(}FYmolci;XGNq0iU4t@p!LRiZ%01M!1>jAVdFp$&Q~Lr$^#U?<&zR zOUr$>VEV@vtdp;wdU~a+K>``xK&{vPjO$jtE!+HM;3$nN;${6_OATSN069+tTUXF1 zf~VZO_fHqo?VWVSLfBe z4M?wJlg-)3lEXK*ae!_R(eg^xYj5S}cfjVfb$>UoR4&6pyQ-j(xWSDLuWti_3YKDf z^v4^j*Jow;^kbN8%u$gFj<+(FR2t=Xf8{ARTqtf{K^gVM%cD?$KW0MxVNYV~-XX4^MBg z=kA;?s_l=J$6lvp&?{j}he=M^ zu!xF^P64CL`cKlp)F@{8iR%VCqLMNp&nFlp2xAcASUA~b@BQS)>J>H=1wACj2y>At zjSU2Q8!M~eFcGej0E4Ites20QdfDOWvRM+-uu|lAeFCVshPpn3E)l z=S$4JA1EQd>@A6x-M#6~I#_X~OENpeWMg5>H+>Z(-Q^E{KX9i1mX7X4YV$3p-u}js zsNnXA!&3^RnqL3BPNE)K92hcg5JPu^vje9K1C8TKf&#Ts@b= z(t@GFIwzg~i;Hk4j#*));r#rwc%Kqckfv^9L9@VIV<%8!zYj+S`HQ0n(2x1v446UO z!z8zm5m<%}kD}tVFL>OgoIiwMapYa=!g?ss+Zc z_-CaSOHDtDD#b1Yx)bSCMUCE2I4e-e8H-DQtzw zG>=S96SPiBXo18>ewJ}E=cZ5b&pIc8s?_p_u*^fXeZgu^&LJ08q!#xHhDV^VgT4eS zGh3uG-+rH&^l@QKs;^e~o7*)tUOSt;t@V_!tLu?vifIC*?*~g?X1%;>SWAHxhzN`r z5TqK>)f!D7ZVBOs0wi{hM7?e}<|H4ZZ<`8z2{N`AqNqqN<^L)&Ih|6Bil~tm22?~n z_Vl<2SySN(bZT76bJ>Nc2*a^&)4aX^yPwjks^G{NjRLs~SOG=xJGF0EL{$oRxKAi! z6x=DSLwZj&Nm>}sU7baZckN#{p= zX{F_l69^-1DCwJGWq?cy&vt~7prU-_od%1*j5w?S9V7~8YPv?w+jV>+5PEM%Yn|aO zB(^^RgT6l}L@ESG*A+zlzvwH)h(KFXwG;-AFe37-@I(tSMrKT;kAs{bC+V?ZbB%Sj zv)Q9*#Og~07SwKb;@Ch?ZNFz5&xyt#Nlhs!EbKH;BO9jl`{ z@OfM{WldZjaOgBPthX6(EjQk!QGOR$MC^@}mtiS;C&c!dBwJu4N73;`dn9nDxbbPA zjVpo#?Pg{z^$ut1IZgmU2HYGMiWW5=XnX9yJ{#z4iqEz3Nsou7{91@)oHHtrAU{AN z<;uXwW{3*x13v;?pM8(?|#fUjk zO@^!enrMu#&HjJs`IbyLf7oWyziXmtC&CNytipZRkH*mC0<|(>iy-1Y)bLk?oXre@ z`#w8hPpAcESbqQ9oqdrvQ}S&4b>bo>UM(pKYux^c}uNR^i?E>!rB z^A5bi{GnkjEbYXJudBvfWcjIpaXDokAOJqZ_pz;!7ocZOy~!;+A)>-bVy3Xm0=PGA z|J})Bi`!zhS1$KJ)SaJT`ExOVdg)BR$n_V{W5VaEjYF0jN`uy9a`G&JM{$y^=6$i_ zb6+pLMuwMlgrm=3bnxh>D`8g}(krmnMR90y!ssy+Un)D{QJ0-Q&IAO%9Z5*4rhfVI z_r`INCIg#d>)pA(r`sQNS-lf5POkb1cc=*MN`mS)s%`IxY#Gb)rZM|PSd`z!H#N{M zDe?Fz)_$tk7sK)8UNAom>qJS~r3@deNR(*8nF8c>6Z`d${t2efBHPrQ(u~WA0B-%| zu)6~(ZhK*_=NCMLZ^>Zfi`Ec@`H{3uYh7D9;ji<44cR-VlA-jP=iR={GJV$u4VqS( z1rjj!xtvP>S`%I=5jk12PdN5gy2{E~67oNVvv9WTyQG8>Y+b7Tty}>L?dw2 zU!BHh-1@VY+c9s;shzOW87F{284-ZVWusgE{3vB0MaQ3)+@nN49`fa2qT@WT&hGDu zw(yK=%r)FsxJ&)J<3I9rdf*K^;A^o!n{ z#gH>k{`OA}3IP%0a3ZwW^M`jKse#<7{wZ60`3r^cTc#-&fxj0GPuNn{*&fK)37j-c zovqByh4lvk*^)O7lo`Fi2!QLd@g0qI^*=^37|U*KCCzngaPBMg@(%3&&-wCU=qylM zmQ)tb|Q@Wksn zqnnL#|EGgV8`_DX5&2)65_8zr$&HAMOZmj&ty<5DmakC6*w1i-AsP@;4t*$U2S*B4 z!sNdyqCbn#ejQS7=2#B1^kYL8r(O*s%=Y>OW<1)$>RzOW#AG?>=R}_!PT|p|T1K=T z3t=@cXk8x?rgu#izFs{C^p$(!R9^)5iY6aSIrnJya}b*6cV|y-1_)Rx`wc zeF@f%A<5&uFHRHy4Z@3Mdn!c#;J)3Vd}Sc_0G5tKP;KG8`S|kt(=Z1v3;xlEWr|=0 zdteful;~f#Gt|D6;S`sa+dXEEB&U$jcV&(YRVrD2RqS_Tt4#K#nUryhvQV!$oJomT zJxo#m$FAhQuGSc51*fU?-=}WS|7^g3(&~&=gj^Di6ah6gJMEw{o@iWp>Ei#kF#c9D z!Pww?QoUaA2KP)$-c8U)Fth$f*Pa|k@IR%fG7sLey8QEK`hD0cC^E6+Zi*%<2O?!l zHfy;^=BJzkd>i`|u;PToLVAf%W>27@5_74dd5h?=x(7tpC!6*XxEwaM#&vRj%6J z+?KE!ZK5t|am}8C(7~IS=O8CZrz$psz`bJaM;LgTP~-b<Y zU{iWQHfFy&o2|T!_J7sqif$mb;&gP9*k&-K6-W_@no`{I_4CI!of?l@+9jz1adKTV zEL7v~bOL`^aZ+@;)C!(XdbwwzweGJYEnXx<6my`on#9Bpn}z4m?M|#@Jhfn+E3MCY zRp+yJfQ@EUT*d?V2q$Pey(pC&b%U6fa#c_&k*sTi;fBPF9=X;st)>@#R9+f`MNjHKlEkhj&jd6#8>Em5MObaL%q-gyr>G3;q zKCXtH2*w^6-6wQjU6T(nj7CnfyLU+@98iKXCUr_gza!%PNkECsOPz40sdgRS+YIYvxD_ z8(pd89+4#;WDWiJ81711u#RO)FO&LgLnJRfv<0T}osP_3T~FWYpxwM=0~#R)kET^C z691qBKq5W6i=^aX)}imXz^kkZoy+1yx$9w#VhAUmj!>4IVzs&qsg{CEchU~rc19#8 zF8%cMd~k1O4`YE>-?u9Z)X&|U0S)NR79s0e7B~5(J_7wP=rObuDvmT`*UxSd0pg4} zCR;)*OVS#H0KmWDZ}f^scqJ_Demox?XqEA2`eLghQIu9!W4cbZZXZ-R zqQP3T;=?i_j8=iP=P0WFyjbizcdF&UAz_%La6%Q6fsNZ%)|<>vuS^sE2dAdM)C{Gg ztnxpLlK+*1mmT5;m%bJn*ydZY3N^&C^e~Fi^V%HGr@_WB9Vn3tCFlvwDM)0vLkR*s zR|JEJyFYPwOC!N7I|ZQNKkzE`$o)mmm-?lSo$Y8ZncnUgF}4bl$OhBxc@?a5eIgY{OlKB(gT(_0koF=NDp>NF2|qAfkC$qG?{|J(ScDGh z4q{s+Fh6j)mEqYox1nvWB>;J|ZWqg9-$R*-baA?pvF73qdmJ76!Iz8l_^yq?H4dRK z*j%=bk_Wqgj9P44K1(pO;Yg)i!1L8|k?*_yPKVL@bTm17*0Q~MW&DK%7wLNaTXa|C zh*bp2%sCuSd~Ojzi0?@NSR3D0fi8}>fA-_-(xi|M^IQ|O$RciQvbhk6)fMru5#{1yc+{UJ1Wr6*co^fkTM_}+vBw}nTFlXnOsVZL4z`LW_+ zbx6iP@Qx|UrHgGxr1>Um&~ks!>E}0gvieAj`Vk&%NO7&YC<>{#TzcPl6m@2>Evo1rtlX)j?Y+8tBY7Z>lHN4G!vgjij;4sIWGv| ziJr-$Y_H<;o*;dXEI=5s$y@lbTfoxxP)T zJ%bT(J*m`sh!kRhypsBZ8|)m9%- z^funIh+Is%dFe6fVf%azMhfuj{+k9o{Q*|(j@=hfOs_Q+sTLYpPV8vu~7uZ_u&!F zkO^-#!mA=zp7evj($d?#aP&I+M9Jt& zqVd+MQ^VO0CLR?Fw7UdsuEz-BkB1WKH8Chlcwe&L*YNOqmg139Nyrb_dt9p}R;^)N z7^{smaBHxDR_f1V|A8`Y`(Bf0(gAQpEI>(I z51c;-lc?{UR{_Ic(Ck=j>i=-)(13+KFwh<*?aZLR_laVcs@XHGmLu3DuwPO@H5W z12?_q&q@9+vG%yEU-oQP}xT&vc}^rwwtFWbb@df*na4v{w;}6}oh7=z>O9B&<125(XJge(zpt ziPhp%MQT*E$XQ}(I=Lenf;Qimz|?0^*@*!}TLTnJ%RgeG!LfDsGIj^#%Q-&N(o zcjbz<6B z(D`C@FG77h&wJXGzkAI=`&8GQCi#6w3FSsk06+aM%yVV}n9BCi^C@Cw^J1i-s>fDR zAQ@Ac4X41S;ZOciOZ%~WfV>rXl;oj^g!hb~N#bpcq}DS>QcA%Yk6_8~cR8&cP{*v3 z6-ZR}si{yQ3~7QNAh=O+x+S>@ieHY(z!;9eF6wAy z`sRnp2W1RPEL6iPgXd*(t~3eI2<+Hr1h4ODM`pBWShZ0|8A_JK8?yo4Ml~qno#U-1oNo=PbwX2p8xMN>hDUq3)j?|(q9y= zApic7N7%sGh073j);UHiHVI3?+B$95vz|Ec&8)Q z2OkWzbewSR@36Fuf(z~;=A*0&v>!Id(kKsY z0$ESw%#r!oq4z$U?ow0r{IT~i>V$WBUR1?D-R0CS$>z%Mk3Rh6@)B4;v@)I9x@~x& z?P-TSkQcP`A{QS-5v4v$o-TK|7KkFN&HXG&?8P8g4yYF2!i3xUF4kn7uRhuMWHNn3 zM4ItIjZfL~!X?ax`r#e{S!X4_CzQDZG6=HJ`1MboH71-8^Y%%nqAed?{cEKH%S%dp zp~3Tz`KR=53RIXZkq>*xRLMqKM4tBn^X8JyXgYz}mHoqiiYwJF> z@RVySG;H+I{ie4V4Cdod0`SPsPr!(TDA_0nxDgvZYA6bLhJt)ei|aPLe6CnF?B;`; zAyZ3F5m4e4iEcGhI7HUI2)=&;o%8&zQxE_>rvhz<1^uK7`2>DAHlN z&!EDN+n%htal(P0u0A8Q$eS9=wX@{28tW3k#RXlvglDdViH|sbwN(^2+0tuPHe5d1(2-PZCSVKad?e>bg2F?QR z1e;r>H9UX-f+FT8cXN##+h;T&LdO1c*H`0ZEm@xfUlida73sp$@g+ZQ=!LI5LE}^H zz=`l|vQl2D-|gC7*(Z@HV#!~RthSH5=WcFY?!Fa*^`a7D8af6udFkqs@Ry&7vY@IV zk+s?%wTQ$Vairhp29)~ApJCu#PXjxnj7n|7L)HmOjQewcO|b01PQ2~)({5m*1YXr9 zurX-c<ZNuhByx)JtY|PgPLEP1aJ<|sT4HL_VyE* zVL=3^QEGmd#U-j$7Xoq%I&#mozbJCrklEaPYsD76rSXfO?!B;z_{@FCy}R&QDK?`f zq(!kGN2fFo3!Z*|;FZRjUn5sI#_}W{Gi`D%?nVW)=US{S2(9mFu5Q)h*`Pn;Aon*Jbq7CoO(w%u)Ep@=(-m;5!g%Ummm;DnsY3B3h2}lT<#U z+U$_Juj_-6G=lH92aK2b+fn!(f5C)h7uTY^qieg`?1B>ZOftCmCa(4+0^)O5?05BxM`$v*QrKqk{dp$e=Fiw^2;-*spz627nQA%f@$R(O zVP5g7Tn^O|RoOyLXqO0x$~KP7+}$m54N=)~N(ZclfG+rh(CpC$g-`#-LI*Vd(I5Qq zAq3kkG!i^JDh&w9L0aO$uaPaIk{VZX`6A^X`}%Q_l^itwjVg1}_$Ib+63`7hAU_s| z;QnkAS4#VkA7zvrr$pDv^2PSWN2R)+bK1Wmz`Mo4(j)ugT>uR3GkG`&P_&-;s_>DU zXBn*2XK^NGp{c+Oyw&-g=W0ZH`~z>RkP^F2MtU~5Tv)Y9<+dYp|Buj#;KFRLKLwM} z?%YGbtqpRmJNyv#aw)~uo=l!|h!$qK1Umlb3e-3pIhA}AhTax0E7r)B2=(OwOo=o? z?O)4*;E-SSwP*?6h=WVXBO>HYzJ^UsW3jSdavbZCvYWprD@X-*e?QJRLXLe(eFvET z-*ZRvC=Z24{f;m4_O|IILB!^;=nM}yrt5Bejoi}q!`uf=h*8wI8Y%B3ien>d&U}@Q zxiJt(C={W!9aSH)eDy=IQv4K6tv5URvOd1@7pshec4=oib0P?79MczZ?B}h217a#n zsKydce>w2n6}?LSSlrD^KXGd8--;es@+WK90`Wv>OaO54WG)XcTX$(`e?=Hi^KGi{ z-T9A7`*_*sH5A6f-_v2CxFfL{Th4p zj?~*I@2mE~lAEiyvT{1|{zPq@W4S6*_wf{4Fd=T{R82+NE~@QLKu_{{+~L!ppCIFu zEzL4WC*`D@=^=A@jQf3kGsxE*1B+)wR;Oy&Pa(JA{-Z7WptYEN+*R|qTJ>rJ$wIQ- zQ+X)zW!VaThWF~OpEmgZRTu1av&;eiNC36?z*3ggsaQE77KFVn1DS=as{$5i;MsxY zAJ510zMMX%XN?fu&0d2c`I}c2kf}&*Gv{pL4u}P?ipn^}8blJ`%0Q*J*%SOnr8FbKxOw`NRy| zLq6SyJmwH;LfOK4Ae;7`_P=I#r+qH~ElLE5oqUE869J@2cs8kVQSv^!L-=&r}eg=Y%=l1*rz)1f*!T#B{8 zAevPnKy2XRPugx2#;d{&KN!La>zav8`0!Vc$l89;_OX$e?x`?Wf!znEY@hFOKfMo4 zCaZKKRd8ju6n0LQA2{hY<~ivAIQ!FTMG7PV7ao_%Qnh~f9o=~?152?MBWg)p%AT*_-u~7mMpiq3gZ= zby9$~M76xtvOmiOsh8bM7-829K`;(d{nNZL_aO0O>B9>tjK&L_wT!OHZh$XsiEsZDCq9Vo(IZ zJNsu2S^wyP`?Oq4tqKAOFYm&JaHsP8=QW54%;)7G6s(c6Acf9{jS{L!`PUJnZoePS z&%3Pi)0`Ko`6F|uXuHK!b3T+eb7Q}sBtX7OWuD?G8u}$Pg|Ra-FlE{(3TqiAVorPQ z1zWGCoED_oI-%yTRDH z#z}=iXRRJ}cD`D;+4lwCc%ej*LG=9{0D`TvyOcorjnMk99X{!7iPZf8_?tbPURon< zll-8pFPyNWIufpUO4#FWmifbI@?_e5dmPPYP-{wmNduOW{Ae|O+x3*~aG z1=f0S)JqnVdu_8zt?t3$Y{Fq4vh`uE&z=uRTH7v0L&_6#&;PbgvK#N)>#3HfKL+UF zOxckNjmYGs9|+y3`SLK%6f3}DBU_1(>$lYLZKs7B8zJ~Au*cRR1TXI?j9`0X!YUR1 zC9?GRsa$(&{~YgW$mID34oj7VX-JFFka?dqevbNz`;blNl^EvC!?M0ZZ7+{X-*JuF z9yhq3HMV6u9ZXZaB}PMMO@{aO9u0(^K#o;RQQEe~6#+G~s`$OL7$)1IvbE=S9qwJAGHznXb> zVny6Wy20e?;_56-nsD}Q9~W=OOO&z3Dqsg{aG{Y zaOcJzceB=0Q9@Te{wWE0e$OWxczaa|8|g(N{R=OH4gD8v%uCmo44uk~3PL9XUAGjr zM8CgTaw}-PbBpGYhKADV=aXrlk6%A)un;CQSoI{j#_zzwpUy=jq9Hf^N}XlozO6J& z^U0nymAc}}ofG^t#6mmL^tk=_;S!G%-?|z)$HxU7-GuwE-4eo}d^o&6M~Z*R zB-*cSV;jD6QS(k(HcDx#I<5&j2-t8FbZZAvq$%jdU-Rs-O3Gh!{Iwog6hYGN@qjS2 zwgiF8_M$2XzC(8~g34-BjS>2!SFmVRQJlRPHUBr~S; zocp!V)9@z;JGglE3h^{QT9I8T%~`IYtb~oIWAuFu@kSvnZ5-1_H`E%c5PF3374^Ye z3Zm9F2e(EX_$W`DYl{iojwW4KO!BYbPTEYdID*W)uqX#$t^~S#ZCwf4{zm2~QUW(4zo?R}>Hr8T%Z-XxQC=n-#@uKbDIXC{X(T#p5 zK5+F;yr_X$8nL-*6aI1S@5yr;7i1+J?UD~B2sY$w9|@SUVsnrh@abZ!BwlzUiHRKF zS@d_y?Lk*m%Nrp~q(SU(Taw>4=epdZ>(uo6^M6qh{bq#yNz-{HOS59Ffql+P*$u@g zL+=#Yc;dmS@n1uh#n*aL{5M5gkT6;U27)4UU}qR=ZQJ zP#=kXzi`1jtnwI&gdVNAUO*FXO6I^9&B{G0-WKKQp{aP_S@AyIY3GLr-meZ_#a8@D znL6cgBXBJ6t9#0R9%Mc3Bo=3ML zc1yS3SSa=&TEiSf%wbpin?b{he7aHi%6#to$R3J$BSi@YwG{sAL?yblP25!>a7p=e zmXw_(4B~@7b_-Tlp>^$bFy}i>!#$nJZ4wKYB{2GTfAT!3YgQv~vo+na zG>~Jne>I3>q{_Q9VA8N>+9E&%b|%l#xb{`MPse(xVT6L&=}@?|1h9N0^4HTl_)I@K zT07dcQ|F3%RrSVG=K$ z;ov~4e|U*cZH$NSg#D=v6P@Gy>-+YPLI~p=2VPlufQ_*AM*8?U=9&PLM6I;G_aVxJ zL}g0%U9@5)xIGRV(S(@d00Ll~=BP|JP3>`|%9kKj?r-rQTSMrsSySIHDNYSrZ0N&o zDq&l-Nuf_VXdzmw@2d$Jf^ zv~2#jjlI)AA@}jV7hgck*>7?@IBYR+*f@~0w{z3_<}Coi2WPlvf*4c08teq~HiZsF z$Qis#n20x?5W(?~igFZ1!#ySOg>-H%-8C)k?hYOrBVTP!-*$@*)%q^DYG$9MLwd-` zYti##XhLcL-JA6>89l7ZBgn9hG-)+pXUrW~3bP2}klK7C6(+UuPo(E?d&9ZyZ^(*k z3n^^kr|$L~)OIqAx?kk;{b5Rx1(O4uz%TmG671P%@=v88ta5q=In9OwGc3%U&NxGX zgGw*d40^~#qi0RloYTl7Mcm@Tv~AEoO@D$0=c}q!{mKTHR8e~<-hn&w;CQ%Y?xJEG z2&RszXu|hQ5rvpAjGK22LYC&rZ<5LJI!sIVdwDHx$>2$z5Z{SLXR_CJs|XQH>gz%k zOUR|wznfiw&DQ4}&%jn30u|pNpv#^_*pJaqYF#FZa_WkbB3i?%al{-L>y{o^YIcx^ z>uLGU)OW+p-D}I8@x6}kf+R;icED@yMMa38x}(|UFzjtNDC5z`TD*@Ps*T_(y7K4j zh&jw1uJ0u>A-Q_u=H9dl?4z75I+&BD;F7(6_o|@OBqNkz1U@D0)T?zbxCj#txfP7} zww=Dko8`t`m!l7Sxp_o@wDQ(2eZ%=Xk&9c-R`%^>cDLD)_otl=wLc49&9^3jqI*=! zlHEmnHT5H8P2UnCP&;->=-m6|=Y8BDD52mWu|8rA@XyEq-A{C zmi;qfef`Fgg($CX#EO0M`}+N|D`|(<#R|KnlB4&JtJ`Iatg8f#KK2yI;eF>_*v?Mo zCCkoQ`%2L}MiV~Q*BK_w>&;L<|E0{XVHm*{-E>IG2RD^Ya?K=VZegIdWxP?3jeG`)7}`L$zdzpQ#qC)A=+fKSd~~nM6&H5n(`#S7 zeQ~yKY{aA0D3v6>N7VGV>T7JdmtR?6`apiyb1 zYd?Pl?zQ!3H$Yw7p-r?hnAcah1zZPOiWyY!5wGZ+&M>o+cpL^94HIFoP? zgiXRn=XiZLL*EtMnVJal{c|z}!g(88eggB)K`zQ+ebt7G%(wI{%$=`k&$`T>>Acor zO_IFLFZVBuvbiTnWy~jgQE7hiU{fOi*jwP?GC|%DVRhZsnSAW5L*!-=s6sOh-D32J zx_He$N8pm#lu?5_SBKd1D(gM>?_S7wdg-K;X#Ym;A_a^%XL zKeKJjE|@_?$hh0!khDdlB@RPO$QFd~M=JG1?n)SCs1TqbcWhtWB!r6k9WchSzBZzV zRJh;lc}&c)4}_l*9V@xmr<|7=2&VdZ~-PZW%5Tnb-jWEVQ7epq@;mNYOEu3|FnqDGqBda!>$tn1y3H3oDMtffubh2+ z-(i*hBb;RW*#gji1-RwI-TUHFO2ZV_|3rT&U5`u&-E>%#gpzh!^BeR2mY=%3l#=O> zl;KPWr}sXCU~{Aas7pgADqi1AWG-1Fu{*qv2%oXSShT$aM5-heA=VV`mJTOkuS4JY z6~;WCNj$Z`QU=z^r?9rUE~P#b2u3ii5u?kAwEdSZxbwec4k&O4NERR#u-_ z=TTQ#%==pH4dud_k{O(-|9Fo8*&0R7@;*)3sL^V-|uk}X9{i13yW>w&%9q&paStgmJIm<9#EIJkatVspQMvJ z@I>Wm7GBOy*3tW zTi1e@g_q)1Q|LE;9k2hjh+D31$@0jSjJ@^I(pww--mj`kxT`}Lh5xz|{KjAfp$9}g z%G0jMj}mgNxxP+*=&=`F_NS8%qroqhfe?pQcUuRzT8QH-ku2Bp?z#fFb)y|&&+H%G z7UI7w^+%06le|v$2f6In9r!pH5Ba;xg})*d+1wg2gR??q6K$%Of<=x#eD~C^l)!;! zOpR4NJZy!7+|p0AYWY9mb|?LJp&xgOh2+au$)DMG-{t(Dy=XNDpIIh66Wig-ifqU+ zpxES*;ovX~%e;+o&408M&%O}j)TQ3uiZZr9N zhYed>f=$^VB34ftv7?U)n6{YYxqT_`9n;n&nm9VDje4wIg89v%yfi;uRBrR+|{8~X2XBz^smkI6e0R1Ls+lCX~ zs{I!58}75pO;NP~`MZ8Q2H+p7QOO?;P#Eoz)7`#Yys3jlr~cWRuzZzYH0OX2?6?%i zDPFKDqK1vU=3utd5dBtw>ZvM!LfWyJo#JM50rI|Y@{UI`0?7O2AprY`V2<#}EEo?V zCHwJr5Ce_s^v>u|M>-5LC=a55vC@Td&g8&_?l(Qp&A$~#k@^=m zvTxmDvO&Fk3`PQ5cn*l-65IgO^uIy)!~graflJ?sMmat!mzwMFdA8Y72pBv8Df@@c z`|nNjMHL2dk~|?pbO!U=3fOM>FDk$%^;vZ z%8Hi4nJt@?jI+)ThwM<2QFaUA%#b8I*|PWENbiOVU~(V+JBUj6`^ z1)m?s&jEjqwOtbJUe~ji;(wI33;lr1;@k}&irO}K62EWwbI=9~TRe)l@+Wv-Nd9~Q zz-?=1+k%`dRS)+Qfp4|uE8(l4{c2Eh)y{2tFc?u50XC$K0`tm@12DEA$e7D_UtrlG zs$+-b#Mk#ldeKds)Q~t%`osoe9}_{ANJW)QuD!N%E@1myX;v%JqVOi?0vM0sO>Q zb&8|ok|6L#LwQ$a(0{l#&Qul^>D|bPJWlA0-N8dewt&)3wCTdTOjEMTWz25#ne(_HQ!|t2a8D=eY+pqT^UKOi>#MH}}$DBcUquq!8 zdUK4OjF4{^uC?MjF*dlTJ1uojR3POKmcnW&KgKoFM2MBrAI^?O5NDpaxZX7F9l3En zg;?C;;f2>-S(h8X#G?r_t;sTY*u&vK<0+f!e>I(III6YO_(JDcjA5~)^^x#Gx`&0v zbA5)IXX>Wk#=esN6w+iq@5V= zdKC;?1a5Q4KYbBqV_!UP$vVCLUo1ed80b!-VDS+D5yVPW+8sJBkP%Kz zguY9^&hNk+0Bh!rcLokDiSr5$wL-`!Ol0>AFOe!Qs;CL$hoo#Q1WApp zvmo^C?^R)wS}o~3s{p&P1GIrUzzFlx9WrrB(_na_bHLPYh+;%qJsHe)mzwRMFV@!}ZEE|G zemge*Hujc9U`pS1e^>#=+LKdDz_wR4v({bM%VG(IWj37;fd8uY^9Q0={7_7jye!XV zV{YKge3z2O^VuSfy>1sg?@8Z-)63fjAbaPOIIEI*jl%5AN)NM^fQ}YeT)q{+78sBF z71i4Oy_lLphKuSP8J~DR5ejk-Xh)hKy|CC4bq9o z1HWM|_5vw`0$I>WHV|5C@6XVp1#_$UDA8|{Rrx}4>M>x`japCHx=&Wm0$z?r@~^D! zSh;#h6f?N9iNLa|M-!BBzxDGDO+lKQbuxqt@b?CQxRdsDki7Rr?R$Gad{>TN<=f3E ze_02A3W(SqUU){$wE>{>Fvqd2eh{AmEc;!j~7lBbzq}QQP-F+4JdunX_%n6AWuc8~^MS0+r zfYkvnym;lWKg8oc2R#Jcjx1SvHwY28x2^K$WCQmh{`U6!+u!L(CpRTYn;KC3R>o13 z=UGC;2MO_DoY&ohh3rv zEi2;mnY8-UA5~)@mvT;1x>hDc>^@I`XIYzA@_NLos<|Y{a4WL354$Py@ivl5+gc9oT--n%PL?Kw-&k(HPyXdF$IbuAYB&BY{b#1sl&+C^H)CoD zeBnCdo$v-+&k|d!&U4y;QfS+jR~XiIJuj~OIT)R*j%)zmDc z{rj>WAc(c>loR6=zy1ItYsqT^G};G{sb!$9@skQOR(-vG* znmGy#(ywSip^`e9+Qh~JGz)ixbQ{6EdUEkz;&I^mjaE)bs=g34Y$D$@jN-EbgeIzt z5*hpYv38aKiB@~-6J_J}2{oxJ3=v2kvzbYG7y`7{nu@Q^Pw@byP~9}`|Mr_l7Ag=enHdaH95gm zW3|kKv{#*x;2iwm{`~sB={390kSV6olfCy1vEF?S5&!MTHrN%PQ`q==rrlCBP{xpF zBy4CgKpJ*`F&D)5of|*66!>ScfH-$n#Qyb`bTevn_^9#pWC3V-_s#42U53I>IgJq5 zT-EMvVTCENIACGeV*-3}4TG>0?j8S~ApSGtkh3$HX87B<{LEmpv%S9$5K#~RB0HXS z-82e8eeXMTg2Un#vh;!DxupD z`Z5D@J|73_)#Lu;i$~xUO2VtD1?=Na51SH@te~|p1GQDJP|9(kjJ!cb4@_p-{)o{> z7=nNyIL!@a4iE~{$bTQlxX3WRi!UCKt6m7$YgVK{`cB6!CRbYN0^g2*#|r@nB)72o z?j?9=A$)u~B7gx6m}Ee6umkmHh~gM1!(gD{x8*^&pZj_N_Nd`M`$#lgx?(x)7(=iD zr~z+OIX#)P0&i5cSUiA_BIoy8uG$T5Q=J@)o!&UR7XzOyx}?o|Az0^s0hh9X>|brv z$K>FWUoBw5Ya$?b3X}jD|%#)*V&-fK9UE z@|u<5g`@0T*Y>Y0Xr=x@qpXS2kW!yqYNTbe=>mtl2VJQp%O{Tp)F1AEoL&QcHTxiB z2F0z(Y=GA#q%e@cv?`pVk4&(<`s^fuDk=h$hVuaca!)vXzPn!lrw8!Z`TejuB5ps{ zpXX#S_L%+1>h{Eyng!3#pgA=`hHy`(p4vBuz$2fRBq_N&CRS5Ebbi_N>&AX_ATj(2 zhYMSv9H`bkJV5%It81zYIUE*%#+ zvhbhVL%s<=z83((1@FGLQ*%0(H{PkS`*pmN7tg$K6Flm45QzGLL?Ql=M0qq5mOu^z zo`Gp-mT4IpO_2e6lWiRq9>Ae9AE6MsXB6_g*}^C!e}}Fg$A1RHnev*&QT>rB+hld$ z&e)5QYw5n41$mh6$-6EH27>N(XO5Qa#STR{bJ{a7oTvhZ8b5^v-7ebE{@)7#B@y-D z@tLR3El+=y)X?zN>~gQ6`A(S6(#!V)a~11FjP*yqT(aunbUV}A&;Z?ZhkF^%y`aRp z-O|W|NYW`;0EO^9Yna6_TWJ0!9Z+X{v&fmPf@4FSy zaq7kp(Z>VwM^S&ldO)s33#3EeV)(_zTc!|pLZeV1Az}rP^9w(paF0R(x@a#2_)|4S zJqBRLdW0SE9YuioWC4-*tO}@FgLvS9u#=7g$mWh9;?|M>f(N(Prr2tuhQAl%f7{VM z+fd^FIRwdfd`9&gRAPE1c4Y>51BuMmnryQreHVvJn1^9UaQyUT$_v>I#C%mZ<^YQC zT0m!?0`qWz^|r%q3i8iocVLB%e7*^qwn6r~MvKRc>R!)AbeyFmtvkp5?HhexX{;mb zn7&{fQ|3s0D$g;XtA6g2w8FOsu%UdnX1YYr$?B;=ncI^T&jsvASLvmJoQ1Uj)JZXBKQCd;&DzS^F&N0k=%4ux$%C zVosBu&)AM5I(j9mmw;jduq=MCuc~GUfiIlMxtCle!5vRsOvpaiahAKDHKIVMqTCDl zgqRXq!HbQ|${YYQt{cSw`uC(nmVO6P7&Rj)48XoL=szJAVwcI2lPvV zXy)BP3LmS)ifAe^6o$eEMzMBWQ(C%Dv^Qn|5bkB-o0=J9D?+UR`oQ|kn~B}aR{@m3 zmCMfwz=aSnDT-5)D74pHUsNEMfE}RMyK8b2u0i0vxOtYUxIn%K5%ZtjQDbBFBZ2w@ zz#ShROFH+{=$OC8g)6|6Mh@FLoyLn64zHJ-X4NRreOVS+RXt7hS^e0lw5z-OI0>$+ zj{$x;0#K?U{ilQ9r{(1(Y#SIPzmR|Z@b-OL>4oEfw_{5&_;QtlH=+k2#rct6R2=KB zONf`ABcl;O3dlm@i+`(*h*^w0S3F$$_-v;!v!Cct#8ZnND3uXlFwgkd|HFFvM`9{z z`}kCqV)4k&pDdz)#$+bq-CMX9aPkNP77Or) zcT9k#P~XozuSfkwJf9HIyCgWrB{0qlSz0O943EtjdPwnNcX_c3e62VD0^E#Q?_K*a zf<_UG#S?L@C!_R)R%tjG^B~`P$l${3e;0Lz@1Jru5&`3JnD5RJCbFIP7))m9<^E<} zA&7(x{b2*vGgUvH(CS0F7P+DmHTJ)iv<$4r%^fnC>Pu1TEflRbPvzc2x|}+MyZEQ> z`LS+{JaFIFQrmtA#6CIA&5?oA#$Io2x@uh-i)(&MlHfg`Im2+UJU*eY2ay(LAw)Lj zJNmor2T4M+C$+rDX{WRph}*V3!(bK#VJX>*;5yT}=6Ik=U9?H(Hk|I52t?aFQQ^hx z0Aek15yi$7uY|V%pi3s?7oG0Q0mfU=0KBOxz>Q6$V801uijgOs858xFnUFEN#YK2z zdpJDSU55n!TPC4<+_8c4A;CqNyYHyuswrQ3npW>ejjHnJj8|sq;rvinam}oz`kw{e zrbNThCph?(OnO!SbCNhqGWbpHc6s2DZAm6})h;9*ckoi>s@?TZT5=GYo3|?VLx`6N zKa0&S1-^r&hu5Mz_%RZ_y;*Ouw=OvifV-M?{2M(g;<|ZiLo$UQV`%ZW-7ghLBbpur z^#ey|4!8+Wc-m%mjKd}oqZH`1*st?P$@-7cgh|2N^8N1#mWycWE_?#MKbE<39L&JH zo1MVQ+kYXV5Kwj*EcsnWTJYz0V@4xVY$8Q-g-~t-W7_!Rl5YZWg6yl=k2)?5@o-KG z2eMh~{!tQ|t_^CD8-RsYK;c>C()%I#G+Z#LgKZa43}W}MaKngxY*I_j5a6=7a4N>v;R%RdM*I((U7_4@*XEtL_5;SC{xSv?vHg&KMJbwXUq82_cZi5UA?OjrPc|e6PFm>h8$8m#6L?5 zx-+A3)A()K?5n89ueMU}bxoHDndTq2ZN$4A@mYq|#GYsY({`|%Z{xiO&{iANrz*V$ zU#T)l?mwT$OIr30mj^kYUz8HQNR1G3T9Fzna>DbyUZuT0q~@v8Qhv#I_wHjZx~0Ql z4G2_Q>D-4bwaZ%w1qQEOgBpsYS-*{ubl0M&B`x|d4K=~K&ZvKT^#{r>#zSmJRm$ow z_btOyjT6Qi*yts(KH-S-OX2KGiF95gRFtv^3fG|&+2{3tZWvW)u&R^4XCTZmT!_9O zCo4|=6|sP;-R+&>iu-75UMr0tw+(utn$5LGmN+8hpEcV^OHi!NFHBGK@aSFps|25{ zRQyDCU@l1}H^VpioScQeYqliIykpZvn*L3z1~6shcs>$%{?_H@_UAhMc zfvmZAE62HtGQsFH%h<)o<#h)%`&o56Ct_%sqFu<5 z6irsaZ>|{^1bYpeAZHQSzlqu1NGT)2+(Y#wt2$XWgSf(B7p_C7WZM=|G1IJ!Ft1RDT@AKT2b>$)<^_w;w&=7}n-QvV)*Uj&IBh-SnPNim-sf zjfTG9V<)$+hp}&=F%08tt@~O>RO?2ooPq^m!1v(o-p))3A{Ru6f}4*#xO*5+p}AQ{ zxIpdOy=5LYy9{u7{EbDZcGI{GHN>XJ5Rk-}LVy1Z=BMwvX4L_0-Ec7ow?and+_|Oq z>s{H;{?$H6)0e`IgjRQIq^1I@9GkMxXBJy-d!9c|u#LNz36H#?5ce02s?-Wpf-&U} zKM@9bR)Hlj0ThI#o*L3=O559Bs8B+ctICf~4P$G2$q9>#uVLD{V$IO%Q-1i{C2ELBkDqw8(y>r02HYWPow%1z)@OMR9r zxJ`1KVcTHgdvAU6SUK-A6ruIhcWF~t;)ghyUF*P1?efFV3=4Rw}A+V?GzyZI>^0Dx1E<;>UW* zcl1=Ax|`pNlW@`86y@|JOQgccdDpkO;_3Kc1$)lXMxono8<01SdEgxbs;uI>-dF8t zL}!HN8rCvpMT43*%SGYk%JqB!fBFq1w}e}|y@6WL$*^%mVew(e%U3@@yEmC*{AeNh z9{&e`K2d_(xJrY0aB04!ZiIL&4JFV(n4Y^)w%*?Zg%9}xV?L*|H^$%=3V|YEZ)uW< ztS~5#TX%s3I|SxQnPSH51dVD1c5z3a_a*G z2xaUb6uvAT!+sa2ccBT@%VY;3y2}k&RX^}@5^Y(Ky zzWE5QGF&TjfPVOoF(9G4oO_zq0C#~;u4O;2!iBRFJ^}W30`QG;8lWJ-yQE0OtMJKh zt#qp#?u%%P$Trfkg|Gi^$Jqwh+)X}5{&<)tiC#%-_R)id4DRPIp)W_;=_K72|vOR zez7!di|Mwb_ntC9sL@mhYBSJ0BpxYr9cg+Uf(e-8=w|1{eA21_T6r{$<2UoLNmove zp{1S>PI^uuw@d$(H35L+W(tJ35lxfkA7&!);Gz*ej>+tYa0Y^_%}c`X)wiQYPc&-i zp4DvZx_MUz|Cic$Uh6BrL%(@j307e?7?V$YOj8Ko1_|Hx&l1t05Gbu&9l8k?aAzc8 zrx7kS_tt-$|NF^hgsfA`@S^Z1bpu&p-phJyH#;bi3j+oEudI|qX*0|Oj3 zP5(xl7TXRG8fuFl($U97z~J>te_ZR}xvh~WkUuGCNqPAP2o&g~FpI(-Md5UtS3e_i z#Qj@BbRPb9*IQQ~ZPAq}C;tE=bV9|i%6xW;13J<~i-OcqkrMj4E`X`X$C}6G|FmhIR@KLA3(o6^fd~PjPl<>*^wAw z5u>i0FhWXy(M&!ZiKHGPIF(Z|n>heoIk7mwXsWwEO7PFV1X^Pbi-iNC!1nkZK!4of z{{S}G^ko1%*t;+U7d}goB*+)kadXhfD`3yD+Orj%qX7v`pKk=KBY{Kl5le+}T#QXS z!q7hrrqhA}%}M6hE731#p?_C7Y^3Yox1+I}5?_AtZ(ebLPgY50N0WV3V?ksfy=7o^L=8H&03k%m&6tj6m{ zB$gJ#V|l{>1ON(TFOY)Z{{SLcv;ArzE}RWkX3oj98y%diS3RwPhdkpu5cw|{*rbuN zoU7h7ATq47pIsv`oQKh=X)qoq~J(?QRUCo)Lx3JTkpiF^-qMU#I8$=s*Us z@ia<9#|h>Tc(yjfddV>QSyGQ9#5X<9in6rT2jdTVgh_^1?bi20bidJh?}e~JiS~Nz zh@3FjG+!im9gi@q_ql7zT?XUGK(t&4vo`=LL=iOJ;TM7NhiY$d;c~F`RFBF&A0hv3w_z1;;s06N zkLs*KRGIkfb26Sj$3G7K4C=e-RHK8L{v8%>7-jEroxjW@}!RL85X&tCK4e)}__>C3_=$r%H#jp`X18kCcJLzkpa*C=`K_L&jT5 zL{)n~6-IxkkSao49!>@>jIQnXOSxY>VrWI>E|*+n7GbVz% z2ra}&g4@DPZpF@Vn3&1;ul)uWObjw$`SLq@Or8%W3dmqv_-+bC$<^{IwM8u!% zEOWgP*V{Kh9#;H1W0nmH?=Fy-Hc$2cSIQbdWP2BaN8VbT4NX-0bChPg8niaZ|Hq+? z96p1$prbE~jf@M1z7P;4U|l>|h&5@WLImK<4BwBMnZBZak@48b^@C5LUWnqxZ6PT0MhiPb52ute*VHP z-5?)%igdtPvSW%W-?_qm7A}9!kPMm4KHEKHvPZQ*eXurQ9ZRjTCsyn1%xP%+@lVc%L8NyL!SluE- z4hU_90NW6YCBbXgYo<8qIQi!bK2(V;^f8~-c`PhCt+AVo;EnUFa z{SihNx<$nWVYb?A_krKvq3a@px64bABY`4K-79yk{ns%x_^7Sa~$3OQ|<-u zzdBi*;=t70F{h3Ye{C5d&PXB_+zo;^_dU33)yl2mC1`lMB)ryIIq+YY7Zv)56^Nib z2capOH&VmpW;~l!)%5MX@VGE+$WmvEZXy+Dn)D6)7#T=|ugm!v4ikw$dwHW3r+ylY z{;H`a3H}t`(4_!U8GzfaLu0!+u0l92K;#vUN<=ALC83z4u%|#r)tXr@^-d57?Vo^r z%QCR}-VU5+HKvt~yF$79(*E~@_|}V*h@^dvKw@`~3Yg%N!ct{#F>as#9zt}LSqiuX zYSP2P+V>Zs$C!mz@Nzx7)JJV!2GE4xjjc~#K!90GWYuUIZ|-XzYG86 z+Rb*Ex~Q_L0Oy*&;T^1}4O39v3(Md_2PKa@!HbdMr`W45>F<1fS4x5Sxtj~cBQaOQ zwMaH6j<;eCw89^mkps17C{lBa?$M8Lkw*O24*Ux8mE5?X{0~vaO+17xpS1L^xq#&j z*uF&?$+&QXZPi-uhv_(7BjG^7@GE&BJDoG+b_U||QAh3n9lj9cH8?-hjqDFgN`*sc zJAVo}jEM_XH`fM>fe!VIA z@ZE-&4XDAb9PsiS`jD zC9^4R2E)7w@$~^sRh$J0X;CaQ_qe6 zN{5x*Yw+^(CHcpgrpD>geuUVpCS-KYex5nBZVd#Kp#6M=n6aI;)O zM+HERR)xE!vY*|M$6MhgOs-i@=Rfha3q|3&W>eD{4z_2x#({`yeL}VcC}xTmb3B>N z_zST~=jcT#5xvJBrZUUV$un?_=>jVgr=YYT_(nc_=8H#%fBehu?~(r9CwiamoL}w8 zDFc4Nc;w1{NiZ(lD2Eq5j7CvoC{l5)4r6Yxg=7u(V~zjbELgWb^WxQS35C$`b`_4I zAfz({_2e5UtH$jaI@KMuANW`R zBmx{G)Wvp;8QydO;8k^itQ?QRUOOvF%#ORR@Gp*#@PfR4L#tO#W6x4up@tmi+UBDR z6ZD!qV^=P%(ScM$4Fc|0w{!>kRl0wiboL$oQ@CG;m()~eyb7p{D13N-Rx@y zCO%Xcl*j4&2`-PDcMB*9;JZkC$`CK|v?nEb{Bx&HaOUGQp~58f!9?S30z-BNf^lMf z-!p-OkiyYhZ7}$I^+9(MT!7`tB<#h+%o5l7`}-oC%;U{68IYdqmNuoInawEd`Bt@- zGoSs`07F$EDO7UdXeOn-ij1dXs&e3=@lk&OfgZY4s&dpSEqLowY6C6yi{1&9CD7I49?h>$L zKi5N0FaqynJpk2=y}%=@S8IJUyYsF79=j<;LDr)+T}S4mp-2#+<0+6qRrKJhC}vH~ zhcMr1>QYuGzxK{ZQuGI^q zn7#WYphnrElQP=^VHHb{&0onU%#aP0z;S`UxIEjbQMY z3e~e)hrp97VIV)bHv;0#pwd36b1GB*@j`IN7G2ksfIqmNW;6;gN7Qp^{jwB~Fvcvf ziMe!8|LorVV0+LLr*FE~R;BHTyyd{A{j=vWDi@d1Z-!VFC z8_O#S;fH0>M0s$GKk@IBambQZ`gEZ^>PkmT1k-QsU*8$o`2Bkp54MbA%VV#692lMn z?Ay27S9vM6%^;(8yJGBXbGD}F>B)2&$)x&Ufa%GWpXGF}T~u4Od*S=nTnX4Vv3ng_ zt*)<&-lk=xWJK~--=%1|DJJ3VGzEtZx})US&(#*#j&0YUV{@{R+Z@`1!5yH=Yc9NmHNtFBF&t{V zhkE8z2=7NI{jfQxgQu$t4R6eCKR~18tiAUGb+QGcX{SZf5z5 z(qD%TjcnOczYD05DwPtczRVnI?$N^_bb4@my|wZKlc`L+*G$=<##1x|txl>QoSfUN zn`>C8Fl`AmZK3(@AGOJ7sZ5S)J2h;GU%Z7du;6%eS6^y6^GmvOmnIwq(QS=JHqK_z zhyJn9bP4)Z7P#0Yanao8cYh;!sK`o{Cs(LQxd*m{IxeyF%gC$H$C@Cfw7r+;<3-4C zcMj;hp(PX+r&1$_k3WPy_?b)U5Wp;uBVQiZNN{F9_IgKf`Bpnv|H1gJIR#9#=-KcK zIZ9-EdQ0j4d$k-Gzp+jZ>&t5#Oz~AOu5r;WXtA?pizYMCUw38|-&$9K3_FnQxVTbDvv3wt87Q?=gnssjnjmsqRX;2)3J&PX#+K)4h8vv_ zt|Qxf90SxjUce;FM1N{n-GJ?5vK3E{8d~HdL5(m^m*!1$dU7LNPt8~TXrh=~d(dg7;#>?ZNGbDj zC+33nkEoZ?ppVB{cqjf?|_a2-~5s1R~Ha^^x1oCT4Ff+^V+mvP?R84&L z%lY7VkfNnd$4KlAXs5Sm6)b1oO||)0`J22+AuYrzxRaXja_34h+-XL^h@J581B+&M z`={tuE3DoR9fGUwlYL)Nj=9uFLD(euQ%@j-cY522*-WtW9`N8>$B}3|YbM6Vw-iAt zW4SqOTJhynb_b^(Gg|QW58tw_@@UIwo5ic=N6kOGrgv-h9JHZ}B2x&uSIi%!iy$Q2*|n55$`*JW2NBhx+6YD1Bx|*x!;pt3x#Y-7ZP2}o!0EoB@y=bl zg)=b5E_yl2qgD&r@2|RkY@o>CPiy`d)3{I8%1(HBe+0CN=UMA`e-G&f6U}cqfJX+n z(9Cin2uO8ivs}u89yH-vb`-aIkQu*NmPQc{Hj(nuX#oY$jLYC{r(CI?QV5W;aeuTv zo7HghFxL9tQM=-4gVa*yXmh2^A%r569HOf!GJNz`inn%`wNyj?)wAsEETvzaK{1qU zn_seBD%qK&M0KTAj4hs#yFQd?^c*?I6w}mukYl;|or@wa_`JMB_y%wUkfuf;heR6S zrv(lKBCyo&n(5DQ zF`7briMMu8NdG;FBOrr#U;6r2;O>RJI^&{)Ks|d1(Cj;O^QkGXRkrNoA6^LRp!y)) zX(|XQo4YoQP64R`Z_F>2*silm&k#&sLd^Pru1jkcXxYLE0gC=(=xNZ)M^X4U9 zb*aF=2wPRW@0y;@pskuYn5|PY-C{O5ePi zr>O4s$(XChqhHSbobdLsvo-XT=#)@P%`xHn zi(+r?hirwWr@VL?wVP8V5_j|O3q5$YF|Ju?S3jws}UaV0Jpcju z89r76bAnv_gj`53HsB@GYckFT{y6)!@fm)Mqc98Tb_35!=52W>vc3Hi_`Nw%M;OJaAlm=;jlekJ z?@HQk{kPM-j0%q}^(I-PJfq3q?HWz}WsRIQzO=$I9jc2lxqpocGy6=|-XwW!eyy2d zUHSI!Rf|(`?P|sb9dcBb3n8G{-QT2~Eq(99g0{5P8VsCDW4EVx z0`wnlSntwV|3&CqdRHhd;@ZE;vX6+uzP}QI6Uk!~R zRPnQ89%`@d@w(`Z$yMpDPZvK2kOzP-gaThkwVuih44QuWb4j^(E`P-FR6k+B&KRQ( zqMB}Ko0QZJk_S-?cDQ!7&ls~qn9HO2>DRNWE_0gPaP}e5IdhtjaIJ*~-hq5<0J9=< z7IV&OEgB5_rgQVw&u#|Gre$hSn!ExG4PJyF$>1P&FJwwHywNU5oB!I76dAz1M~xA^|P42<2+c8YVxH_uZ*-uy*?KD_vTmNLV7-3XjY%)w8?aTXYY!(>qQAZBxI^bMPOig=ZG7Uw^Yn zCq<3C1G-0!*zg{8m*{`WE$TG8Y7W3)OJiU^feBHYzGbjZ*9Fc-bo7)^ymsYTLpPP| z7+I?Eo(fsn?A~*9_C?Ic?rcWwH1PzuI%$42;HRtm&MwJ0^-SMAIX5ZT=H+3V4#De} z`_*Gi3dtldC_ih4>`gzNgVfDc$-sajmC#lrMioGOgv1V{1$L4{LLvRRM%qTS~-(V=e4Ku zVE()Jl?ZVJ=sCZmU}Z|#kIY|mP6>?T9wnsMOkQ2+Q_xo3H`Z2g5Se-2-25Z`br?hm zls>t<21%{bmDC+T^^6>zbu1L+ab6-$@ZeYpa{ZjwjfY2=Ld{m?y^uLgSZOG;S?|9a z-;OW`1v&$7$YrvEe>g-2T`8&u^E5=$6WhjZeh0u55t-KuiR|vpQ0;d$fJu8LrKw8bV$?$lXndu#Zba#3gciqQyrkjt(g69?8$7_w~Swtju2Ddc2M*!{s)=*bKU%S(t=a%Sk{ntD5L#e#MYrB zS54f@K!eM{bNp%>CbtXHMQ?4!I6VC|(W))Xd63suEt3UmL!1f3bxW!BZ70-YUmU#V z#x7nx^pV)N+bJ-XOB6Ka=~~jc(@@>Ha_~jd7QvB<+4roZ)@8AVotk@7YVr)d25Wu_ z*xD`&fIP-dK+Udzd`Y1*bT4EpnXXh@MYHqP;V)^MH_jv?20kgBTI#2p5A2pUEI~G8 zRps(LRp^?Xy@J3vqjnMmo46~rfOi~G2VKAHP0~LC*Y;~1o zi)jEYqq4x!4RpGLc3AQZf717_?kks8hRk!1(rHR3iK-Z=+%LG2DD5Z6~&apg$dw{o;;SYv!SEM2-VU;%JfyJdU=?>RB3ir(;YNI<61Uy53Pbm*aG8CS2&x z5*v{AwgtjDdOpX1d7d>y)7O%L0*?e2iE>wG7HXr?N(l&?WCa4~8 z`EK@&bC>{emCd4TA1oN;0wG0j4aFnn{KIE@Cy@tY+bdw;%&&PD7{Y&3+-lgB^j!(_ zT8}VQ6zrwSisz~#<`BlP4N z`P>D()lNDLYa_jUTMMB}9C^`D9jT_d@H}bJ=`A3%;g-N(ZU@m_XAQOW_>>A7vCMat zfumF-GS~3GKogG(hc_eaQg3e2S$E33#@@;*Q3q&{H-hCaG{f8Nvfts6cR{F$SO~!B zc2kVZPyYro7zULWq2bo}df;*M_S03MsMwzEbOzvi7o5kT|XI7+X&CyIO`wIk)@~< z#_PTlr;V(X^nQ37BEZ!br-CbrVH;EmZ!Km{_)v#ZE%L|p0SHp|jviUu zeEOJD!p}%veyREH4=$JQv|Xg=Frt?TojXSpMv~j6d-NHb^w0O%g(FOkI0fQhS3cR! z&;x*Fq2UJ4hn6`esWE5B)}TWa<pA6!>_|=HCS*Jt9;p^h<1K*H3HP08VGPssuhVG# zT#wEH{!;pB*Ulb!di}G{Y9nRI8T~-6ddklVO9N{|%jci9%D2uMmHkI0Y5djOVr2H|O~zD-x}&TC$BZUA z4aFifCpY~gFxRuVj(k{+)9u<$jyI!YjJr3+=sEp4TvP}#PiF$yXT##m@Zbv`5F54L z!c?5@2jE$^dV4^yoE{t*pS*P7$^mJ5y8OqG1VAe$axRa1zkRpMx~b{x*rBBxcwT>t zjwXRmhJHRlBZM_~)RNguzDQ1El$KDTFC=0W&>qxVrj|)g`v#u<3tD)2MNS+JmWoZ%PXE%4 zCfv<-xPU63IZb>Xc9sGMs$5ycignMMRvc8iOfJP{Hi5#sHTsvH<{O{2a;FlyT%Ut=ahsI$W0YF7U=Ct8gG#?JO+`ky%v@*jHn{^2)5Sh%v6Xm8PK z%hov;DhA8Qlzx>WRN2CxiiuWJO-CLA(Qn2^2Z+P;30$=+%H?|s$=R#vxj=%efN|gI zc|e8-ZZpbK5wTCx733ByOp6Rr3N!cTX`5RvlZcYj54;Tz9Ie%WUdAnJwa6dzouJb#Q~y$UWI>OCbMNe5}z82<`H8@L1RFt|HyoB^6B-afmBD*puVHRmmW z9YN=qFdG7uYC5q=5AtnemIf0Nd`24Ba8GZdf#vruUk&-Q81?YnJZ+ezkk!E%AtpzokH8zF0bgNQY`6_)<@pH?8#jMCt?*9N z`!{%`8N|d)pO3Sf`D`E{Imfn24z!z!*66wdi58_?#M_yp2u>3iM*P72AF7;r|C{V* zC``{%8UX34549y4%(5|#nZe%2nDdO9hFJ|_nFq9%>b`b@&)yXk%X%$HliHyS*Vj)1 zc%O=2s({e&4$lK8@)`mgdwqy>!lv9|nINxzd3zuP*BDMt$IzkQ{G5veZD`b=ieD7k-fH2ake9qSKXl8s zqMIh)nslu)LE!0RpD05MPckWxLXl;C4k=Y{F{-Nxpndhp9@Ijl2L!Y2O^*BgW}v!w z1GL$nLSB(y7RWAfF5qRp2xeNo*UrAskFJl{U6rwX;!7Iu5FBhUinjxhX#mayf8Q3? zgrLapL=%kpKa5eA0;ZkNz&QBHC+?$&*sEs&3ev>wi$y;l%H|9r9@zwooPz3{=>O~( zGkUn{T(^9V5|JJ|5*3wsC=sLf42l;2AaL!k3AJ4OGD7y!>*r)Qv&ZsG*p^zS684VI z-OgyL!TIRn9-G1&Oz9{ z><5?0jC88!yq*xk-aak8e53Qj=e=OCCD|Wcx^xYK8C!7zn$cGR^6NSuB!H@Wpu!AJ zW>q6t?vBWr^v>9!yAD;6AYpHjQgl9H`P(ZxE;;`+>=wW>`Fci0hUHA)k%@>VPv$;= zv=NmSnxou*d8_0c9?8BeGfYNB?w5W?rwo41yyaXqn0ix<=< z<*y8O>B^{(Dwl-;B_a5k#(>S(#Z%|~km_06+Y>vZSAOR|nUO8VuW&1#H0DYEO}ELh zNTlgNUwa+8qt%0TpE}8*#p9kDo+Ovl%<%9YfA5*L4M@W*Wl+n1=}3AL4KDu~;>Xze z>Y&9v)ngHFBMs$EhUc5A%61hYDavV~Lv?T30~1=voL?EM$J#>(Zx}$$@J0j_VV{>o zAq@j7PC-bU-(ke~O2((x_{GGcKkrC_>DPN@OZSrZVt6Frjk^CV##L0d>6PcB%Hz42 z{|`;q9S`*%|Ia@AY)RbNA}iVRoSc!BaD_uBiLxVmoRx5gND4_(i6~L_NQbg#R#x`j z`}g+!{eJ&=JnG@`!23R*_v`g~zMi93x6E`m5!=C#|qLQe{uTSF%IG43c+*J)t&(8!T91Sb%^sUh{G1L ztA*uCe*`2&vn!#DV`jC4-Jm6dkw^QYU8zEL{?r4!`yzJ!(@KfYAqfo+ZELm+nPk_G zJ8g7Q)vJ)V^7#XQyBz(MNXopgP|(7Q=k-`J0Dq*jWX=ZttKn4EL|--l{Ew_VkyLBc zHW|NdADdd)#XtL=4xXLpiq*ur^P=n>uf-t_{xt#&4q^sd^a$xd1Z2Y14@gIT44ae= zD=0%6Hcz5%oVWfO3djH$n0d8G*)zp5fZo9H79+b?(Kxf+-3MR}^XBRZdCCXwW`~rl z9%-OmArIkhKoH+7b0>nX4Bqr+tccl2KlI&qD?>;w!@?>+$qxTk)=@c%7qJ8GN_+=( z;q8}C&S=u|Za?`(uXuG^yE5rK0q<$<2T)~W&|lc0vJ!!TAXv5ef2pCt8D!B_+Z3t^ zWZ+c`m6m?wL@w`TVpPA}WkM&JRIDUMYsz{W^kXKl8G^%PzQ6?7dPM*<^q$!`Hz1}h zw$ZFm^Hr!CIQh!I`%wIOr0)HcME{WIg2 z!BU)A0pl~{_TlD38vu{U`E~-%CtgYNyVq^%fu;s+fI^A^Zm{J;F#3PU2V@5@g|$gf zH{ViNAH1bW_QcgDerb7f^6g6fR@ds($o#iy4%~JwG85;!d2^J$@iuB-)(Tq_Gh4e^ z(c`~Fi5zS;Xgj*}$``<3BdK1l*?AWITW>WexAXl3@|p6G-dlw8q7QEugW^=61Dawn zgVF7~fM-#!KDUhsIQ}zStWGgI|N!h?jy5=3st*+{mP1Y^WT*N`iUV? z7Xm8H`f3FGo|imQ!0;AilacH(Ve1SYid-zM*CV5_;cVh?vJHcUKfZP?Tb~_3HP0wu zGX|F^fa-5sHYq5&v9Gz>M70>uJH_qzekJnTdQ|9L01Iqt1k-J&vqE+|Q5GuO(TEmk zbE(mW>MRgQtu=?=I1hFAB^HW%8~38BHnxM+PJ3$Od!}9n{F@A{tT>1ZZHVL7h`Su9 z`|?(rBGETfg-=2tv-C zs0tOIjUAlKo$ubrE2@2$@7#E)mIFe&KXb9qDHml~xzL5ICD>5LzONpA+NT=B7oB&l zbK)=8hv(y~5g<`V8-4JsES2=062URx{T2DQ!273gslmyJwEGjz!zfcd02h)b>+e6u zLd#H=^=0f3SxCSq2tq1F49P7ZEc5av!xv6$;uk=Os8CM#$mLd27A|eI6M}-r&9gd$ zzYc6425ny7X2n>7R%vDgB#iTdGm6v`_)s_+3}Ca@NT1acLVUeBtjDO909c`J$A6@! zGQ6)q2$$b_wg{evZFcbe=rVIe;6|mgepVXTIl=V~tcfc3y>mXDE?rg%44NDr-+ zK*0*Zsk*?Oh$YD_+l5t4wSh}by@8M>>bva=gl0vv>Mle*74p19X{&e71q^UyANWQ> z5UAB#A1TvT4-y40HckI2YCSja?FDIi=b|)_bRSHc5riPyDhFuhETU%*ZVy3TTnQ9B zLVgz@QVkwNZCz76T{F9F8+ZIgTWOXLWJ4su{cqL67S)5L|he!}%< zy%1DHFq_!z?CeDiLXN=@sm5g8d+K{S)iGK57R&#zCKwd7;HBEB{YFs_uaY=-HiwBX zt#WJ2ofV7iPt{ z=@{B2h%zHLd@&NWu-CDK$A>jLA&85=)O*<9x?AnlfiRxTE#33v8q(sIp!&T0P11O4 zoi5RXW?H= zV`u!0H7qv_eaw$x^-XbAQC{U41FIcqUD7z3aM&#>ffHZYzIyV0i2G1Z1?v|=ZVBg zrXmTs@qn=H2rKpX$P^(uSz~~)0y{LLJ6$6Eqi*0OK`nzECPfC9=UCFeI}H>lmKBav ztq9*;dQ2Fhnm)_EP!I%U!kcnmB!HgliE=Z%NsU>s7Umo$854pHcy|UIhQDt)ojn=7 zQgMH)US@Ot^4s;tKTlNcl!E9w=u=Tx3w`5PlcTN>d4pXBe8_jf_Xn2i)&7l7r6*#_ z`F$(=qRs3nI zOp`wO2aHUUvA_qFRUgXO#u#qPL(MK}6f<(`Tyeq~1^nHdA>iLzX9e#z%Ye9^n(%8I z^g|iaDtfOcr$7geqeV7*!x~JBMqY8uUINI5I)1tO-YYEWPCw}@XP z?thq>6Tm`Zfq=_jSrPoqNsJg3;-3@qBG+W45+E@lgiFUZOC&sRtxg?n&VoB-exi7y zX-aPmS;{aZ$MN-@WY%uZq=nF|-t$}5Wddxa)9<>E?TVySYJZ+CToGM6me!o{TxFaLt}a$j!G z*u7e@v1Ya2t093mY&90nx3==Q;o5F`tR_2gT~7Eb!JKe*9-MC4 zrcB7^GL{M;?zE@?=F=mMTW?bHP@JBi$qf%k>G`h%UKo<>=3QjcS{NyO<@Oj5H;V!& zcfbb*|K;R-*AE+4c#j#T=K;jJ4AK`bYN5YNod9Sg7KA1e7|#+L~K4 zDl1%$IuYq`TyenK3La=e@{1J&n%>y(nIVofKGm-F$8gmyKF6Zz!nk|0eM`Up`;QGs z-dvH348kI#owAJ~OUeUt;8a&{sVw0^D$%JU{HQVLSY8Gd6#9f0Sa9>pU3yP)1cxFJ*?&sKE}Y)=mL;C9Mqo2PK2VR(`KYH`GZgjFQ+AeS*^Q!+ zF_JAxht2xMr`}=C#Fq{E+_@Dm)8VM^eU0zNHba>e!j9vdHR8rG9JegoGzOp5eb*Lbmq+@`N9xup)}&t34mt>G$yeA8d@mNdpu&+l z-#UIMa~`6+)l~k;b2zk_NR##BuH@zGhO(Iz(jllFXFD75J1j2*_64^z+s3N)ST@zz zG!3Rh;ja>qm?Lz?3Fai^ug=ShaS{e{7HEk21GCw04=UQw~_(BY)CCUO5YQKP3;yK4xe-_Jil&5ds}UxT5h^ zmWd;pR!eEt2Q_f{JHtSbypM*)?cmIXiD$Jf;>Hjmt)X*&Y|kmQGlXsJwYgTG_C$pI z+$cbkCPeV5mdmgIR+cSShTN_)@rF%W);vCzU|BeZy|2wI3Y^cQ7k?nQ}Z9Xhx! zytHMg1kvUKNCp9l?|?Ir0QH>5a{gqdt~+@q!w(|BZpBH~4N2pyskPp>@&ZE_Vh7mg z;NFyw4v>amoz!hj@iP{dGF$u}&A17`s5hL3g2<&-;5q;vakt%axOW0+Q;Je9P zlKTmZt^pl?^_a6ntNp$n|CSVJax#va<3jGAh`%V;*5t&vqzyqw!O^%5V*5UDwC}ZbnNS^^dRJPCTQBIt-9tU344F!a(ElT)Tr(Mi= zR2&^xPx8+^`HIHv|E6i=xS_?QILm@y`(Wg9gj@i8uaUP-D3Vl8{+A9{Fg3Q6V2_^2 zf`-byyE2gj#fo{V%i;am=a)9RukME=aps5s{2hL+7IWfFw~KKm#t;HPVHS7Z?9hA4 z3i>_6F_ho2C+*F^&J6xur$x5n23IK@>A7t`Ezyf4bj)+*`mfN4LrbiE$bN4F*#%i8 z=zQ-uDFPbFJ#e8RvunpEx12Q}(blLOK<+Ds>6hhSfNrhS?q81yFlGF*A@YV4J4IcYImsukbAY@m^()Q_O-%=ZcJPuI~0+2 zxbg)XOV{mr*tfklS|}A4Q~*rl;1`7|wG>^vFG02U>3a_9eTV+uUYn7@z$74!6m!ci zTCs+D{k++f@ISdSs3<}e1HC;|*Mj8X&7@^>@JyJ&ES34aYhLQ?w{K>MQi#sJl`*}0!a zC7v)OlfMR@p$4PlQ4N|;AY(d=BJlx3!zuL#Ac|6c0}>~bcTzwo#C)Hr>T~r(>0M!X1ybcROe)$r)Ew~d_gy_^#vTHOf&cV!HhHsr z^>%JdP2BqJq{${bM>|TYOG^dh7v7x~5^@ZFH|E5MywNbZL6i3DkfB>xHPC+@%hZ#o zV;4_u1%$8K9Kxmk&Eyo^tZRlFYDTP^HD5Jp;H~e+CX0qUD_1k0)vQ{*8r%1d6}MOM zQT`;a$NVWgnG)V6xw!YZ>}3@kA!7aTHbm$bJI!};PK*1=qyB32I6yc!9oz4`&nJkKImigojBg#DOqGusi~?iQpJ zmQzfdS#Sp$l7`ZKa`k~gr+1R-&)HSc;Rko95;4YTZonjD?-JFB zF9MRje?KL)Nd07-=YR?p#uO)sU#E8|LG?ea-EC6}Y9EIl*OmOtS*d1o?pV2f?jkYj z!naeCOAc(CVW^Vp_B#JQPOStaSAx^w5h$X+7M|l{MV$p=G!H&WVMU8 zCHgxvucZE<>$CfhuR*;J%zrkg?qVNiZ@&zrS^|Gr#_%*(!xwF%jRKr@>@EJqR4h~+ zDt@=k7{sve`nvR1f5G<9wl&b-kLi^5e`ovm{sN+vUu2XkLHF$vXaZijIBalnjBylJ zTwvernkF<|P2`5$yr$O{u=N>e#7p<2f{oZu|Kqv4o>)Fw-shJfi#6ew2(ZLoSM+Sx zGi8Q0yl#!~l6ihyr1&fxHmhganaI)qmj2J7>?n=MZTJ;h&Z^z&Ez^9z6whkPPqj2Q zO0%Zyn{25qS4bOvZr2z6p8c$rNVz{b=hwWySPZ1#>j_FruK1802C3>OHg5ySLHN zZz2_HS9wgCs20);LR+2)i_8`!n%yH`dKq~*K=e&`+Aw9ReYxGNRyBCF5@y+>H%WW5 z==h?#mgFB}-bZvBo1t|3<=GThXIa?X0p}{CI!SU2dlQn}mh2r8CFA?%yWL;sZzn;h zu-snTq~xQ(a}e{N_Z8B9GxV&iK$;ZY+q_9Tp-PKl_xvsHz)@(Ip`WObtchU_BtEq4 zZX3`LXMWyhpA9xy-t4z=-q@8G{r)Ojqy7iOc{mtgfKkA$ut~@JUg-T$z$`-4U! z0CFn7uUl;Azy8|KeswL$!_l3I-28$z&Qc^!jDT|{Qm2?tK<<~#ihYjz1lzCu@Ln$N zm!`Amh{ufV(O>?=2F;tYcxDxdHKq zD_W})tAH7@^5xs_K|QM}7hyw2&N+WeQ^@6A9JWaMHU1fb>U)%#i6H7yXa%*6 zg72RU)PwtFr8j$ReUrdmd)AS;kVd-s{0(;P2~>8@u&mld`B2r?0zG~pBnROB-@idP z&@(jdX#}h>{KM}M#E=3Y%uvfbgG{Mc22r1^Rgm`C$^wlr^sK7u6u( zOA3|JNpb&?Zlc6nFjZejCUr-^T7MUC9sq-eu+5$f`7{{QkF}^XF0#||$|6^TB=Jq) zZj22+J?z=}@tU|myLC@zewgRhBi^8CSEcQZ5b}Gm_x_r(Ul5h7{Uc7yx>);A|CEcP zs|M+pD=D5|cP0Hr|*m1vp&d_HQprRbk%eR5$S?X)19>&S_A(TI$qiFGU}*Bw_}kcKl6d$&s@ zA?xpA4EZHe%GY`M+_LmH=DJk?|8&+$|Mt1^k1iA}XdG>abIH<3o_rcysxlqL*;Cn) zXOkl9apD$0lcayOL`moy;2Jrq3zst4iO3#jVC&{fBAH2JP%Disr@zD4AMd78l&4uP z>q`iCvznV1{!tcZdz13apwEn*WahMx{PgY3RuiXoJ-e*;6$}}RIa)~9{m&I&Ncti7 z-HknV4mArx(s)2bL~pMdPrPGcA@Gcm3w`aGcrZFYECt9FX%L~L#HBY9t(%;BSrJD| z&hVP`A0Vpx1-Pj94+@xp!gOagi}||7c>fQ;P5oG)2Q)G?4_yBxg3U7oov?WEz4vz! zB313I5mQWD;Nrm0v2dwrEh-J>?NS)>>~O^`s&mUPCJ>u$$8~9h;a&zd)>EAqgs``8 zGS_X^lK$e;QhwKE!Pz=x&PB3`PV z&`GNwb@K)Z9jnS^@dGv#l?oy3dI2>rrh@oO;)?I32%c`bd#NXRE5Da!Z}@XN+kLlx zDE-o1LYg5XoUoY__sipg1ciq5G!SjlR|3^%feUrmT!(KY;)nj&3DuUy;knqid{>Q7 z^M7a5f_<2@;D@Np8{n$q?eG#DCV3l6-84@UhOFCMrup{!MU*vIBf?+x-&kDXnuPlC z&X#%Iwq6SU4E8qSFxENS&j1bFKN8YMVC>QLR`V}}+6wq?WWLV$5wPPS2y;~O@YIWf z(s7%bJps-}E-Abahl^G#-T1C+dD1$xZv1GyFlb_U^UjPttqz(-WdJSS;e6Gi+8(M< z>ZXNu`l?`th{MS zC^6yR+osrCh{Bety13&FPiNvQJO)OU{CH@4Hr#xoKSB70Vwo7{Nax<9quf$;@1Y4= z2{!VyrD4u}3hv+kom2A~kUgwfqSEni{*-}H0TJ-37xB`ey9I=3V4!r=CqF~x=v6c- zqN`hN!k2mOZRkvGa{(+juc)T|b<)CkB(QF(#6&@LUP!Sypr;nP7RNEnZ2!*%aDo80 zL(HINqQh!<;Bbp}EzbMJVU0XX^q0S5%44j`g9Q-84t6`Go}kjrir<25?TovyWi{N4HGo|2hDRRT=+fn%cc;meOH$8M(F`5veed>&P zN83C0s7AJB4#W}pbH)hA2S~K^=n6iA5GIY#!lf(U8P6twCc#|+kq!%�Q?)bP)6o zCO0E-fJ%qQzq+{|wz3!x(RFyQXF%TOaQ(Y1E5?V47>S~cgf>P+T zYR3lhzXqAZWOR8z=b@>%|IF_KFs}cgb3l`IP2dAGT4q!YY!M}3j(h*6Y;3#_&*=8e z3%s{!CGT)g9!2E+$un7McUx4`a+_#)z8{4TB6@(Dt}vp7zJ7!(zy^WMr~kJ*?w^Qh zu7DIOg7|gjT!Ie}xSw{~uNTraLW5&D_T2URR4)4XwR)Pj6k`)w*FTnFO@``%FhPf2 z@t44r4Ld=xFrL!9>P@29a3-4(e<(MO^~SYik~v#JwfBW{60d=!pXA23FCGy1!9j`v z4s@$WZJIGhA8KRG5E`K5xByjwB{^(2#I)bHV3ha8xU{m|v zsietb!CEZ(3e0Svo7MLE?Q-7*;S5Ec? z21%79%Id#JFt1Y0UNIN=AoREpY{8}AO=80;fL-yRh6#Un^QPc`x7&)*9UhW2kqYn$ zU;AlJv`$usuJPRFCwvr)kv)2Q$}F0t>I~74>(rrF!%ezlro1e|5^k<~wjKP&0 zEv`@X?>mJ*X>wI$nj%fTrOlcF^i{BUVDm`1O262+> zYktmVh%SjHaq(g_p)atVJ*?B+5@CSDU`2p?M{x{inDx@KPbW~YY8C$LV2SSaT`e${ zr9g;yCncVd?a_bzeuGbz6!b=?9+usF7ZCh(1LuyAQh%rw6z6G*&O!#oKYhD0{KV8K zI2p$ZYI-ak#GGhyTc{L|!2lBD4_p8R_k*4*NxoBxAkl{w89=>l%T^1!1SPAR9B}wt zQjh5BW37cvYV7v!x=%ga|Iqh#CBb998r$$d;ovoU5H2T%boG%C89D2VdTO7$5Iymc zPAx`j2qQ5^?Az_f9hK_~T<~pQLu^8x&eUAw&o(h4rRlfv(^xU;h#@+S^@T!cP^p8lR zm=xfYePcQ_z}qOS_do~`trtS1;uWc5165lD-h#rG#)|pQNPKzeZ!Sd>g%!LOc5hB* z?P2QnzB#D9z&Q)J}Th z9wXw|z~Ey+mMHh>Rs6-S<7&ESq189&@$9C3Fy18w9UeZ3Uj+?~e8xR(G#)WF#oOkX zHgxuhLpS}#a>O2}dJf#53*Na5)62eU6dea=MJ;C=`V$}7#JC-RcV$Tme6e1Xy-|ll zS~ELf3!{N6WD8~M!(RozrJMAWBd6BV_Mi7nTZzb0UKE$6B;O3F)6- z%UFJ#Pyw>~NsTa6c4WBZlBLcOGL_Xd@U|ihVC~$Ct$^n1)iUqzzT@^uZT3TE#<5;;Nl&+|yw zhL}L2KD(0zNsT=3?WPJm0x?aIaG(Fy^;%dfmk}M_qXD+^o4XSgB}VncFJ2pJT8!en zbt0VcG=gAsQl<|)m|#6y?bz?!Sk=SJKWJX4gd46L8S;kiL4jI9j)I3Z`X9YDA%ai0 z>jILk4?&mMgEnrlBO82nddo7LA>&lPM%8;(sxAgS3-W}8E@f<|eaNa~+s%e5>ASNs zn0<~Vj3A!~Y~%@tAQIvE+rYy^ws#EE#<=|59vwG!w2TcqG6^Mrp%VSfpu4`!rRegg zC!~Ejg}kYe_Q{Z;Dt%(0es4u`GSgILUMk zOCV`%Xn@^|D-?#V7ox3%N{zJ9f*|HhZKZmLQ-k zKPEp^W5x6w%aTD#F3|w@4DeJKL-O+du36*9!pp2FKCxp1GhnX zSi@K718`McB+r}P6N0*^FnES&iwI~L8FjwP(ti+F*bGP(ZYs((Xu$OXbe-1%pfGQQ zBSheNYhJz&KFJmtJUMgI@jQ>+e~k=Tx?VoHZa~h+)N5$r9PW#}3~q0!`m&3ny71h57hTHUnnKEtsF>H?d@e$b?CKDQMl-A8myU7tINZ+X;ck_-{Yx0 z>KNSEA3eS+h{hc}e|NNLCbSy#L&txM<`91yGYsZAa-`T#gVy~kw-3+m)!7Sc$Dbka zcfQwO%HYSOQ6qjd)3c|N&+}u@hOk`ptSlJjCjw+I;$MkThmFwVEvor}N*GCv%tdkD zwmk-W8%Wb$mgjHr^OIb;GMBYQ$>3M2TzQ0vPFU*sATO>XD^Ott$#VHZIGI><27~qBD7xYE- zKi-$z{s0hxJP$PlEG7EL#iH|5JBA^ueuKgt<1xQ0Pndq1FcQ+M3{b(6e`%8W_{FIC z3uF-3G&2e{pBx>|@Wf;i#MBGgo+d2^p{Pe*Z^uRcZIa%t1e~d zq9OOdfgwLGv-tSxi~ckI=2oxwcHrJ&RIH$hcF2hdVx^WsR=E3>#9VO( z1%f4-ay&<%)(DSC-T$RUN-g1A27*yC#PfacLAt9zOi()uiS1LL`p&ft1cd@x3lWxI z2VDOg_7>L~KBp-Gc3eW+IX4TRPFH?=MT3}*|EmSugCLF9a>l)DLpbq}OS|;qDRmoU zseHR7z$cvg5h#5(0J_uh_zS4v;`TufOi27~jSHLfC1(RlgfznhVeVcys^8H8^7m=& zeu~Sv!gW5Wz%_*zRl5#Pb`*1>`2Q3zIKE<7h5rJ7x$&maX4<4IJU13;`#*B~Uye>~k34eM5$CQS-s2I)t=S4FOOjSu+O) z^Uq(v({;Y?M4B(_jzmi+XhrX?4m3Bdiin;W&5bl$?bc6G)YiOAt5@;lO%IxlS2~&2 zpPSI@-&36+GpE)_CQvl+2IPh5Y2)-*=FnFk;G8}@c}4yB%~vnTo57%C+X1y$J$|Dg z7GUF+CzJOPaPMJqPDR%^JZZ9G9EF`oLB%mt`eUmV?{El9z;#Ad=$F> z$x|J1Z zfCx~y2{;Yse`T>OFg_L8i?Z0!8$B;gkG#26COd%Y=fU62iu!(xME|Jr0dbh_77+ImmW;WPQe%PaxczX2)H_=Y0U7?-sj&?c~y4%qztgn$j{DZl1m^_ zEo#hJlVY7zZM~R!`6@Hn*$DxH=s-O9qAMIKw%Q<_Zg!bu+IR5jM!$iP-j}LLyqs$QTP?+|W`9cUnh=R`Bf@Dq} ze^D+Xzzc}j={6t4gMH(+)@ihVIZEqvmZ6G*WxGT z^hqjkYq4VO<*MVC^tnpqJ!0$#$zYB)L%~>)j6c6qkV=h{fW)ckwQ9vsKTy(Ubh(q0 zbwDN+vR*S8n4ACElU;rCn^@mx5HgYm{6zuTF%rb41FppO13m^%6+iGUVNdvB<9h=A z3ilsux~GA$0ICz=Zc4<8fB@m3Ijz`MUBM9t6V6!i< zdOOq=zs%;d%v;L1`aHkdRW1L zZaKjz@8)A;`zi1Oat(RIF$B_k;9TLC`@HonDEGbMx+#Ay!X`1qulr|8>>roAtZQN8 zjC5e#b|~2k%%T*c0erWS!oM)JvZ+b-WY?q5@08~!|07vOfiG)<9QU3PzqHm7FRs0c z5AkO@or_C|rT_+wLE*zEHdU7X?hql@u{SJe9JfQ+Ughuvq z`xbv9!68)rRL+(=2X<4_#>pp6<~h7yNWVy1!QxKb&+&wnK~@v6V8va~LJR z7EpHZzYBX43uZt?G-r-shEjkuTH!g+`o2b`EtU-n5og50oxD0C8VM1kON(6H6a(h3sG znErds{Ez-&DU5l4&Ws)O#N4-$RBw&N+76{4-&*9koRrc>rBT`mZracpYucm|xxWo1#%J_ns8y3Wu;y-3E0u8X zzm%v(wZE@vE0GQJ?fbx>C|V$-wEJqu_}9Plz22P>vtkA~7Ob=ZPL^GmrLUP9`DdO> zjsn3J03%0nBdx8fDLH>;xd0sTpA1S|D`9s8MDyC|B&f9)OE05rO}b6anX+b zC*TVp7)WfRVZT%Nw=!EsB$}E%W~!W-n;d4btKF*oWFf=t34lE%7FGTOf~MyU^MVl& z2GB$?(erP#psumn_8fnc=hPlL=k&-Q_%kaw@&2PztO<@?Rey9zYGhOWNpm#^oRsmh zkmioE#rZ*xnq?xd}Ww&1og2 z&;z(y!oI&{LEELUQF=n?{w&Ndl*%<7Sx=b9ak2nC01s5BlY^V}y){d8#dIUZ|IITv z8pN|mSkdq;MFxkJRbU4KT&HX!5i_gl=O7qVm`D9A(_U0BH-~@=r!jySYneISRvs|X zYsKc<04Y6Es^^@k+?Oo|V zFzV%V3A{sb3IcCJ1ZM{&@NotQDGoCq7?`t7A<2E{S!w6+Uq6{;ChwQHpC2HIggCmE)R&ho;+T-VsW(`YoC}x47&4v*gvm4#{$}dz zd-s*9sW%qOQBB`J<=Al7j43m^x+>W*dsUnQbXyq@z%1{ZgC&Y`hW9-AA389C+p0&Tygg&4KC`3O?`QN2J->B41$U0$kJ)s8k{7uXgfs1>-8MNQq)WLa$ba()?EeuBHap+6frUK+p!T(ZZ!0=$2 zEzcZ@vK*Dkn;!8y?S-1CXIzL$so{zD)8aVN;tPRKT_}w$wC0o~yP79bYd(jtG{1S# zwLP&4l`hpl0iLR%)ou?R<|}wfIod7(IU+-lN)9InEsk`*ST> zjQFEkmg}DPC5!WqdK|-F_g`{nhEgHft+ml0T0sQ_Ov-%aH?)Xt@y@d4 zVkd*;%chr^JiH}fc$4Fq#M0T&GVq%H?0ySiQR8hF?QSTomf)ishyfH}hNQP?Bvwbu zXuIM`SD&AOotz*IxW3Qwo&&IGHo(dVP#}#YR>g@k%NH8CIdVENe{VB5W{#yK)Jlhj6o8@FCY_OD8ohTNt?-c{5GVy8dPKoj;Q z2@QjF^KmEBfZ<1}BUnx2y)Om)*RmC`a%X`cLehP^rGQpkQ{gE*XPblvSJ`NXSpIi$uyw^ z47aYpg&+*;HBxL0K(?B8TYvaJTX?@o;5NVEP4yQ$O2?~Oo$zJrMzbJAZe18}-Bv0ro>W+1%R#G7)Cn1rir2%ZW#PDEQXhyDJX(ocHO^y;jq-(PAUKnO7FPf~$ z41GYEGsJW0q?}Eq?(Je!fjgmFRiW;XJa@+a8(+~UREmZM7J+`??2-0 z9$u7iD5K=V48EecAV$rrYcB}ege$Dp%FL|cigxRie0#IrO_^8d-NAfQ%K6`vqb;Vd zmQ_-^&uSa*`G8uhz$p`S7v!j@X|mWFyXG!P}Ekms(Xrq~b1Ka{)1 z#nDhk_Ct;T-=8J0n8w}63MT*?a}{8o-VzD>SJMj^j|0qD#x(qtZ0||lg$`#ZkOx2- zOhZ|6uBlFN32+kOS?6B>11#FTH?;{^=y0U1X^}7>BIf@Kq%> zAt3v*#ti>%L^8vE>*_l|IT)a8M#&_Br@iB|=j^ONVnVjMpmE1LJzaDicl{m+9tG1- z$>KNQSa2(5j*A>}EFsmZ7n(QyK@J9KklWCqK$&X^+`gOjSc|MXgb!^aO0P~A7Q^%~ z70Hx<_}3#c^QOb1efej_1Ig<^c1zpCefNtXcFWorF$WM-a&O+s|uo6C_m%^jaZ z3Vg_01D&unCWTBFYJaFj1qrXIC0tto>l*K4;I_%pwP$6)J|sbgaGg8zO={X7nCF_@ zT#=we)G(vjj*wa}YKNjU)@pk8PQ0xU_eHW&Ymg$K#``^qTDoDpVsJ;OkTQpL{0Dr3 zLZHWv?@gzm=*OpV3+Ssa&}NctPbv6@b~vfb#PxGNzaKWSd8C*X=;-z^DM(|Y z#Jl1qrwFf%5IuNe%T*K$c(|W}6zqG1>G-~HUu1+_B38^5Qm%RIzv<9ESw2lDGvWO& z{S|oTp0}7z2OP&zcPVW&+k%-ULl=08zG8H^zrtWfcdsfvW8Azy z`#bG0xlTisiSV_v)7r_6c+Z!3<61bZM=^O~!Vuy(+#D`c`j{^nxRkKy3ZVqZo4R!H zZPIvG*y0OptoGP60I{A=a{)QH8{Ht{EChwPwXfJOohNVuY!Ja&DkvxkeQ%Zanafol z2|e;9>4pk9L~}C1r)bR~nmC1fc^4N1U2!!VWSG`l8CuL2mIxU4l*e-k<`qO`P1k2x z#NWWoB`Nfzo8=Ex(2(}Mdo)Oh4`|Avz_b^(dgRRqe^tZu0E}#5Unt=}oId=4fU4JZ z4wlPgYuqawYY(I)uR^IoE<< zvx|?qlcJROVJvGgn?#-2-QxiCg8e`Jr@Cf0bn~{FF)`s4a9E`X%a zj%8N8Ulnrg*x0uWfwH5LZ~JPL(a#ieSi@lY6NSFv^Bl=(Kn07r_Y8}EM`_vUfc~(; z29CEdMC#jXBrkO|0BFeSkC8r|jB48Kst9yYRC5d!%tMNQzO2l%!B6Yi!7IL=?FiO7wUpk zUwBFamSn)LbL)i#C3IG=%-6O@;bPMuMcyk`2QY`0^*NPKu!Ha4d#xZ8F%;f7M3hK_pv1iJ`)oyzZ}1K%Uscc8hI`35~n)n*vpgO3&nszMG`7;X{cqUi66!)S*TSFwoX$ z>xN`Nb-o;%(IcOmGBa~eX0&QUy@!1$quCAYJwq&R4Q#ql>bPC&JTJ+@35*ZFg&?|X zs;)&NniQjP#HmTHhF%)ry6Ke?0(j~Lq7uG`QEvAsA{h_$zr*Unl_BCm3bwCpjJU$U zTl^~dqiYq&JhH}uhh5tY`0s$W08X6gAsA2~-8TI$-O&MiQ!TKaAq#WGD%TJ21sogX zzlY&*ihFD<+{%sKj25QPU8MvxP*i(pBy&df!*E1pVb~ey;ASdrrCC5|>JBIn%C*4P zxk^9cWDZ&^{7D{x7LcGq@{T1WY|^iP`?Zwk2#}#FUqEW+3GG^i{SISg=Pg#;xs^)X1i^OXmTG*(*LK{->~!$$PMd zk@S0jY*_sjEx8OfcBbAI(CddB4FoEPCvn&f3~J|B*j{2|eX!G4z!XSs?jfmlNw~D4 zhCx4^knH8u^N+fdziw1aB@dMrdDfM3*AR8hD4`6B%oh$_08U(YWb}Qj1U$k!0xUo+ zq40DTY#VpZ^O(E??M}RlQdej~q9O9_NZ% zYVeDnCe1irnwmmf3{op4ir%D^zLpxB8nDWIUnut1Fa9=$9=2^(*4Kfn5g$$BcSmyP z;Fq8nAbQkFXipC#cyy??1Pb7>BpmZ#t?5)SbJ-jR$9eXa&|OKz=6Oi&iaLvStbUJj z2)Qve@@{q{VEX?z&3tT^O%b`6$Aj<1B!M*WUs}-H*n18Z06ei_)Ot~R_XCy$d?Q82 z?hg;HyD);y{7eGCm+|zbIshID;O2CYphX(AQUM=m-D+{+QoQyzn+@^!nwD?^Yh&~B zau6kY=ENVQdGHFCqEbm4{3EVFnNE(-g2ffyJVy482=7QoD5$Vzb_2Y9CNFBg?IDhPzP&rOro zWSW@pg^COrx?xW;_>(yIqaZwx$xS8~u$BUzBtk89?8itocWdC^aZBfK?(#-}8`Bj? zULIfSZYfhO;UfhtEcXKz^aS#b;pd|Xj8c~X8$T6@`-8Vae85^vKxT2bAD(xnc!cA{ zPp=x_;-;5|Sn+$tBf!4PO+jI4Q?oL-@90J0FAllpSvmDr$r!GG9sqsB{6w^cYb8q4cJuBO>DA6}h72L+pC9Vw)FyGoK2bUt~RFyhr zz~wZ4p}!-S5sJB9OAvx|S-z9Xeg=EQuG`<@ERXqr=i}EWGLCJg+^-nE}{((0Pkj zKdea+$*+{4bb+)RrNAmf%r`gDqvPWE&7p0 z#4D~aAE-vGr+b&UN`c|a_W-vCkmH(L>VLq$eeEBlW!ZWNlc9e^J?X6i4c5Tt267#n zzh`7I>qy{71AxdpAawY*dh=HpR@EvP`j-K@aKNMf0@bhjKs`z?$>zNxdq;i5lie7k z%7GxqN3uMlHU#S$h0ETj*dxLan(=OqPXAGr7SD%Yoi;S8rhmoF>6B3GCjUmtmqsE1 zXyCEr4#>oGKonhBVrGWODE%A;8=tKPd$$yI`_rn47w@H@oL(Uyg(ALu;wFpvzQ%OnKORIAP~O&PngB%T{Sy@oFaAbs(IMI~NH7lHXS=c={y5wD5I4IN$bN z&_mW2Q{C~h(ubAqy(a%cd!5>AE1xv!Ebq_Qasx)){WrpdSM(lsPtO-M){%i`;YZAzKl#`Ok*NE}` zoqHMVx~{Q+F)l|U#zVSz)6lAowr83nwOM{L5Yhuj?P=1yB#=tU5EEH`$-8(7hIiy} z4*IG;ob4~-?YbmZ1cl5HO(6LZ58D}q!SOrFz&>N-y!SQkw&#mnxgsg&7+ip5fj(|1 z5|*>bikSvR1bd7TWGavbjT11Fzy}O}Ddr6%xTJu2*?8}a;UugLjNI|VZS|9IhfX_- zOpf8k0T-i%MKNR&xV&;E%`toon*SP;@#$fD2*|!;6DY7LyV@#P! ziDZe4y;ZVhH-o{5GE|lnS(2he3$pLiYN<%}eJlHJhTrYH@B91jT-Q0LbFO)w=f1z+ z&#J7qKJ+tAu_H2ik732~4y$xr&=h{<>O2BhykSh;3F0c7as!hG4E@wm_9!5;IIu4n zI59R886@U!WqNH%1OS;{AWi-|Y^+zaThMtNt3YqrXG~akqH~k~YE3N`h_XF^p`I^3 z=Ll2Uik{k$t5yML4ORGQOh(2}RX1OxLoiBYC1|EzK+v+&aer9QHgs#s<>?^H>842F z_tD*q4&=Vsp@OIquuJD!3$B}hwerLQ31C_z7!&Q}N|iD{-^l;~MBQp_;nv ze-jrr#`3FR!AXpV-x&@_sCvblYmhH{kx8E0A`3E9VbXtG&2n^;Gju;$aci&8-2DGW z3?l<}zAZ~#s$ko5_Afr1DowYiT*UT#pT1;Fh?Of%kwWjEuf4YseMJ__?L_btXBQO_ zFh(xw8Z2tv-UA&rjuqep3t260O3XhR*K20myUrQmn44%yg$f7%(fR3}gD@;UDu(vR zi5w{kl^nVTEPykWCz9&{rs7gYFL})c8o!=uX`CMXBj-TqR!&;oeY>vERw~yWG&z~s zSiSzM@X%K@ghLp^1bAHTC!f4>ip#shVajOK+TlLeP5h|9?{^V1AC8rBq-h^D#*RR@ z>a0+9totd!7gpzJS{gaRqUl;}0esTm7crfUMMW=fdTi~VlbYL!Uk-z*;yH(|g@pEo zRcEtY?h>%uAC7S%W1wOp(in*;!&`%_h-@hiUjK`qWRZ(O53~hU*?~)5d2T?6qB@rK z1mESo3~XUIJ@1RPw6z_fz`o)5+VH6PTISYIs9%oczY}1-b1GrtlXXFuQG$i!)MnVh zyy6EI5@|SIXP9A2oof|C!bBRWJfz)azS{On%j9^tEx?G zIxER^inPiF^I75QDXMG~hI%y@{yqwnm6S<(2s)_zAh%!4u#i;*u3SdQ7HIZ>E+{?L zROJ4cZ%4rn#x?L{IOcE=C7K3X9a@@w<7tmW{kV;GtCo39ax)odXKZ&5Lwz;U<*O=R zRPw4xJ(6^m!Tzuh2Ld!>_Hk|&Mr>{dI!GGFS#O=1{ShH$cCJ1tsm+w6p`es&@UGni zroXkyuiKv8BXMXaDBM4BcX500z~k$(+tJ)uA584Z#Rv6%p}#D3@yiazaja*N8Ev3-Gw1Hu>nDTqE*nZb`7ei?h-_VE`R zcnuM};VV=+cMUn|#q4m$XXIW4Uv#sYqKtS(F*H<>&@l`kmPT^zI zHZQ0y@NRpLFsV)yTQG;l_FkZ}!LS&Z5=|CTPpQ@QB>zb=OVNg*3Z4~@{r0;A)ACy| z5#({$r7$!M(_Im;7s$H0%jlcv3R7UOr7c#eL74n|!5lzyfJPi_QNcr3TKLhC zHT|rM)uV=z-f;A>@h^xNzfN00%Er;{uS`#V4vLjXLm0L>>uYoat%aR3c8c5!Q5jQ;@JAcg=G^QC#XV2*^K=f{fq;m`yTib=d`XED%J{aQ{P^`g zBtQR5QatdN)cyK(-sT4BHZcff;2OZbN0gz_Ei<0RFSNw} z3FGMf-Gwk1W4zpocGF8d^(QJhEgbVYqZ=`RP!d1)K*6Xx)g zXv)dMFhm+F2*_Kldmuo@`)u37T4nK&#Iue>kEtkXjSanL+axnf`TVHw!|96pWfQ^S zh$O3&&v}=)4EDQg?|xv{x1za1j$_>9dSo!t%)l5CUc=Nd*^m6C>wj}jfr2@O!aX!P zmk9!w*M{Z$vjfk9Y&h(VWg-aNZepP*x+uOU(wUND^UTXb!$PY|Pt++pw+?e6vnAQx zx1-&}1V{X=tuBhQ{*)D=D1jJ3Hj3)nGjah+6T;l6qW5c+>x0ySg#l|&_W4CIi1(Ba zSVIa-L5y5Ai#!OtQigOcWoX{=&He797>?Nosh>}K#YVw{CQ!h{Sn(}1$d$e%A{#HB zk>Fz-KvM=E>)O}S43Uf1qFA@09~%>d1!UACF*UW4FcefCvZi)$yqF?k{M34Qu;Vxh zsdLmY_WKHB#fz(_u-@a8td?fQ0oR~H$D^OMv*Ny{NYwA=P7H3Mo97Yl-Ck*%+HNbJ zTO6!iGWckCKMJ2hhEdLq^F0_B_JL#A7vREkcWvg^5A0hRN;MT46VA~zp1fmwiopn4 z=AjR6-sJ>!FLR<42^k^k#7m%jVE4Tsb3KqXf+jIztdew`3%5kZEJKun#bj>2#HBzM zTKn?hdzT3h=}XmlVa!6jwZ4czC(MI^J7&MGy6|m=gHmvZZjopUGUXtbg9)5_=Z`N? z0g+*kRVk9?%?Wqx(ui~f<>17%6!uH`0(KxmwU3PpT*O;w%984#H-8Pk-P#oh?Bm&4 zAu*mC>@3Af+ia5(WToHCgt1G?@mHIk9Ncgi?}?ClAX3jqsY?t`SK~8OXlL;la^}#@ zzcsG-iWs!^{$%g{mGq&4nz}_@E}U7k!%HGvgpx<)gniQw3dC2|hUos4vs1AE@c74h z7tdi>1M78x2AUSBJ%Xn9^Kf2|;6Ku_(?(C58vk*ch;$aJx&^XEf!~B5GO;=Xl!gkBs8a zmU=8;2N!~xXoG{$!$g>?tUWh$pL4==SfR|wE``$r+D{gkd3d|pD?-@ z&WSq$(z|V)6Jn+klaIMd;^A2Z)FXaB4jOnz*xf+Hi*W%1~I1g+dezEiI>685( z(yr~urTL0UPtgO~d*yzPoimq!j@cxR*rg zp>#teC^J(7=0d%9+m*&*z|j7f^tk}lrkU=bBXtV!!Xj<8JF63X=$aM#0^#OdB0FyF z;OebP5XYByU}Ug0&~wa|PXLu{Y!K>rtwZi47cOl(c`5zZ$ihOM#Psaq=~(J|&V5j1 z{5H!GcxcU~3-^ixMJa@^kkUrfy~{P1cRWF$y8lr17+9gE5Gk9|x~NDsq+*?l8-&da z-#$(38&cVwY;%G{*mhe`Wa@pllyhH*#;iwGml%*jhct7(vVqT8RiitSFFFqtD8+L6 znO$lOVdgcf42JTkg#TIS3n*P>2W_bro%|&+r2R5UnrxWt)(HQ zS7O$f6KA;<-G0&yPzWtGM}Kokz>rV&55eV1ze8hW2j1b$oX(}V530h-%I}iS8o9*T zzNsLlACHC(0wMC-X)`VKg-lvw9o>*K~cM7pKI;V9EO*!n6y%n7ho(!7f z=~goF>Xs(nudM4~?aEzxb3|FvZ3KOt1MRlIV5?aDBr=Xmjuvv#EU{*(gS(gT zwQGsCKcCH10y)6Gn0AK?GV#++uYtfNlGe0}v}?UlNciT9bzpa zY7I1_8d%2s!WQG#H{!Jn`(M4$W%28XWp{-}rgqgJDYE5}(^rP5hZ)+>RZMH&LefmX zEq>*bRncL`%cde^W?tM*Q?#qGEOXK?G{b<17p&qL#su89Z~ z+H%EW^3Zb51~crd3v>of!k=YKLMUd{F=AcsFpP+_lt1M1NVmm z4^PhtH&nbhId;aW-}pxEv98N;JnlI%Po;d(d*&lW@@(OAEpS64ByM^YNBm2<+HqC1 zCQt(CKu-B``;Z)TZYvM(N#Bucg(QtlR)t_@i1v4qgC|CGW%$ zg+us``5r6*>@QVlIk&kEh~``B(xZw`KOTR3(lyeNiAzqH1y8>7-cCFrx)_Mc#oIX` zwJ@V|GS_)2BBgSbiM~wyom@o9!pYXr9dH)?dhe?Fl1NzP;z&Qtxx@w1xSoMl7INB$ zC+t89rQD~I=b6ZliLKVzO%t$Nzl5M>lfu{HrPUzXXma5^OkE9Iwz`{kcSq!X8+63B zA+g5j)9>F^yMs|wKAsg3d|-midCIh%{Qg?jn>D+GaCRi>Fl z^F<*0^>sjo>vt8m8iW2@V$>_ji@TY$!`6uCp)gYiFff zoRSbK`Ki>0EPF;#k951#pM<$ZZb($_$wisNLfnh2=)Q5Fyq-HgTFKYB6VbZA1goib zp_VpUn*cTW<${Iu==2{l$|Pl&rH7W!PZkLmd3nBUzz^Lt?s*-km7<_Ba^HwZ--+7;h4zHnl||#Aer$>ws%lCCLc*-twX(zirV{)w$BU= z-zy6mAVtJievfrm;Z8aU0_{K(&Hlq4q&PpmM=XCDYZ$@hp@!#+JZdkqVLpU?f|0qC zg*d)^4Q;cx;V9Z4_742x2TVxPV=JB~;3AffL9`LeJ-{yS4)&gz4B+o33IWr3LBq91ed^=6h3csx$np` zqA!{<`-ZBOH6z^Mw_Cc%6?ahCe~1ku<4yD-wrXx0{K-FKy9SPH_NR*y<6t_vCibpU zkFDZcg3JLhD%f#6+abs8i$V2yf=G_$jDeq$sd|Up-b&T+C1JP^ble*wQQ*)okKkZ+1>WI89!@ZN+Yhbv zDn(i*@N!24yf<91!Up~5X-8-b$}t=ffb9KT*EmLen@mXDj!I@Sc(_wUREa7qI5`g z4}kZqc%?3y;O42vp|nKhHy955*TKP|u~QGri&s~q1Rkx@W| zkLCg@VU7x5e3%v%2rmBkMVzt$%7uaBjMbF|C3B^)=>iFK8R?)JU?y{3g8b;2VCGCQ zN9g)LzIBKyAvKaUR@^cm6@}z;8kx<$yVHI*B6D%GX?=8M{&w1lLUo<|FAqF z@cm#~1x2RmV>5R)yR}GTJ?;RlS(NhPO%5JFBkD#NxIe%$qI;k>W&Rc%ePlWxBs+s0 zvy-3Pq>qxVny;TdR=LuwwSyeVAU^YoiLIpn%bficLA#x0Jeb$^ya5%na)c*F;!&GRXs47e$ zaQ9ugzB?g~Byn6*v;p`*^s~%RVAy0aQ8$}UX5e^v{xC7Pho3>wc_iw4X>TwY(Li%s zPBR@iQwX6;S}uWqHra!E!FWEo3t%>=tqk$4{9O?;?I!jT6L(^8u!wwF>xb4UgVJ}e zTOqPYjWogp&NhsoXbUNP1Y&%|=V9UGCwL+Kt>0kx`|SXpGcuL_xgE4oQfQ5|c19fU z29VaNlT69FT-cbaq!|qXA~UuvVBPFD3rdYmSXNXi+m;m9@m54RrbAkmm~@|U!DuU2 zu<`D9MEJY&QNEr(V_z?}Dolw@Wm6=tnbSzSNxLxtBtPNuEs>X+djDU3p@~JXh_agrD z6nn~!9&Oy^fM5zqQe;DTUM)q>ltj(eN>ki2;WF|wlzo2U;Y>@{XM547vF`(rx3&XF zrr^iBcqYY*AYv8c@ts;l~4^Uw>1uX{sm-r}wUj-Fs+b~Jv zaNG@NDmw%td(|$dJ6f@>KyKVRY?kM%+b&KS7`O1*M#U%iV>=^-^SL}hrwv}9)D=L(bV)=Hj+rs@WO_U*S1@&hLC(2>!K7o9@TzXbwuF?0+gJx|HK}Hb$NH)9piALXEaZSeLWv|`x zfZk}f7>Adj=U0Lki#*>G)&*0{#YxToL?dqc`fLZr-rj`_>+luEV=G+KZO=6zd`14t zaLj=Gc8d*WXRJ2)=ThhczjD^Gt?3{X6H+Yu!cJ+rk^2=yA)R#d9uoycwqSzQKrZUY ze(|3x)9L-W3xcB^zMN&nQnxPhT43HnP5xmQETvb&(o?7ZDz;uukJ3$CX>+i>k*0X8 z0IwC}kkZns()3;|H#Y7QOOt%oZvnfU_%1+zChA`x67R}l%g5s-#D008#EhvXS&IYb z02=yK?Ne8)`UCo@VPSO$gJ75ct4bM7H`QiHF84_xK40jY2sowxXv2=SFULI}vQ zX6eQAXI5J5hhc>Hs|dQ>A(P=>*%F7JdmAR#ie+S#eOzhDpVV#i%~;Jm8tM^JUKl4f z&0PMsyB>Szm2))4nv_LG<(H<)C26wbJSN807J1VcnU#Na2yWAJmG9Wma@TF_S*onF_o7_*x3kuF;NnwYe&&S>2W>`zGwT5mo!;g2HqUP_puzt4hg(mj zKVrwY+?MNPLEQl)u6aAkmC8snx(R9$>&)$VE@c zw9|>49ITjMDsppR8_X$75}-JIz+1o7ia?$Ja{%g8c%3o*Jjt1khErsNvroBg8knf7 z$7Cu0S>Q{~$Bo9_Bg}#{5&h$2g0T&0iKk$EIKNIU*wrOv&bIhIJ9VhQ6kbbPWh?)f z|4%~(v?KuK*tBcvh_DMG1j12yWxxayX+ms|L?u?pTwp2=B8;MFQOx;=0=8?Lq`ATB z7z~tj9)e#nYj0JUbeRH_!Mk6-pEsV^x#ko;!23mA$8WYVQK)O7ku~)9A=kTkbpwo+ z0A;@Gcv~X37#eI0BF4y6B7vaJpO-v=r5A{pLZ$IJ}dt^R@kH!$F>UjrgIa8~l2YqGt};Sd1uRrtP#-@K_R zpB3Q_7Lg_-4gy|O5rAIktpeyY&%2uiow-TcZoX^^jWKACU}$E=S+m`I1Um2zG=2Ki z#V#l{~egU;YUlJCdt26%7*!#uNdW314qWst;-_9uaXTVwPb0B}D$M(+7W zBMyV!**f_JF7F_3d{M+2wz5a%y9&kGcb;ERre9LkNIR0R$)zBt?CvXFTccP@Ycdb0<^D1x}&(R^+!RpGAQU5<|?JGEQal?Or> zbs-KlRh86huFLhlX9X`{jwW0B9Ln~Gi!p;Pi84Dp1r_pVUv5879_SQ(CeB+K%^O-^ zHu$VTJ8Yvdt3Ry2(8x-}WuuaxqWaJv006?6nR&3iJ?-f@!G7PX3p74ay%ETcumg6L zLVj1=LhpS%?G|UWA<$G-JLd@UFhsjf^WY}BAVJETS3LVB&i@C*q=1eC1SWNQZ;Hkl zedN`Xgg(8vnV-dP8h!K|f;=^udnjp~1Wu9HRiKD|(s5 zDS@JR4P=Mp>y;g%I7~*Sn*~TXzN5$B3>;knkfxSZMCyaO)wi;!VQn{ni{PQkZ6My) zvpE;8le5RstaO5wn^s11s}aX5cDfv=n_IBq2JcUS3sD99F9>{({4NYg%PPJy4m*MN zfE!8F$NJP*Jaa#gz1mG$FpAo;6c>Z7t+o?1{y&4Jv24l<1PPAKqqK=y%@@9~#gvG5y$$chjS{Wg`V=Ph=gJDlm4 zxO~0fX3093sB#vJ&y)PWVEF_us`e=-O)!&cMS}~c`<@Hy(QWd!)FFO5CN3eh&t8HIO>OU)afGR$4#I z4M>v^c4ZlmRA5}4IKB0p2*4n{vT6BhUj#wM3!cJLa27bCzIGN1s%vRWDT%_UCKUib zg?q@CT4%^2`OWs>K>cClqKViuwk7~)44=F&fB;N6|5OCZ_@_dR=jiz$wp!G66np`6 z=Zx%ek=q6fOVy4Dg00op+Reo7J4o8VOs^2xiI7}yEv^uO|uZpjRDhk?^56OfMVxO-CQ#I@^N1>)rZG?W!_QHJ4>^$)fm z&j{x*{mJ0%(GMu-&>QkebI6k5J3#t^;Kl3(AMs&2X#wfEY%=MMSnH=J)A<| zaJbaLOBDb#jfJ}S&`irRI>onRdQ=#vlq6pubqYXawtHV-5}tY5H>=tiIhkX*eEu@3 z^cH|4@pRX~o&MFet;rL#krj!3HzB+&MBC9=vf@>S_-{jaVdgN4(E|(VeDN#Y1I3pH zM+$c*xl-QSN~ux;OTb2HP43G(9CA;!=?|iMf1J`GkAGVGRh`3zh}EuoRxRd2qeor^^pf=#b7c8xUE1&XrP9_a>S%0Qs+1s;pry> z0(x#nwxQja2%L8Ao+u{=FzrW@fJ6n^a%*y9za7PxdGP%3emV0o38Z2{ECrfM8GO+X>&lAV@pvplSC1&r1ep&FAL)>`UN8xecIZ-cS!#=~4Qm{& z_&1O$1T0g2ss?`)PuYXf?{-#@ZVH%VyzH_BYup3Q5O1G9TWIDAnGohet^p6=;Zh=9 z&&woqy6o$xOj%<%SW`m>-&S1&;CJ0lX8Co1U>{YLYh1zKWHv9BvSD(Al|HHDv7kMD zSCno@OLwd>xlYDkpc3h#Tj*{6*;?Q?B=)Y}0wzInE;w!7 zS#%9IgzJ8>1%7yPmTc@5vwse({VX=!w~X@E51Ii$vn?DTB9>KnA=;5c3ZSu!%tjeg zND}0a0SDmq9f{@yOh>c0;8NK<=`0_12#G~JWkuuI;Itk7rh?E99nz!U?g%n&{bpPl zU<>gNiQio`_3AY$Gw9BCsRq&>T|nv22>bL=4I^j|TISQYq61%D-8VVUd?E{7?e#cz z_d`a`loNsQUbOe@YxHZ!*($oF>$))vD+!zkFo zUG*j|_*fB&)|LwH??b}smyb4Aca5pR(dND_XZdh=jQ{eMUAKZOq}~&P`u42=5GCX8 z%Up529_r=Jp8)eO^cE;)Zge-689w3rEi@r4Bll+R7-(Pawwd{)V*9__ozDAI5VPap z=eXDH{C_A&e_$FJ^>XLgrgGt5j(UwCu;l+3Ujm1)O@nOGYCQ4xsGb*z`ywRc0HYJS zuEBLEEi7_%(50^lv2m92{0tG(5VP>zmA2ZzHQ~9<^4HB>_Cs5ni|3Ymt_SLV(|AI{un-?uXAyT(5*6t zU`W^!$gO*41182RuOBmUSUwZNv8X#icn+N3U6Wg=Bv&g!?Z?UxC;81~ViA$P7|dMG zqVSUoLeOSXb&XUwL$$N%^XE6UVBrRbPh^Ne9AxpF zgusL>RAEI5Rc!OUxUe4U`<<69Y)7p{EWvm2^O4p4vP%!?y2Rf1&FFg^CRDxr*^G+I zP|lTN7?Pi|`bJS0IiTtR$|@shBC3?rGDQISPXOAIJOKvI!8OT-o8Dm9e7FWtlC(h(sD@;`4EggwGBRFdg^JJFCU~{Psi5qYr`JrxWNBQt53x}+1nx`cJEsEAvq$jU=&}fAjvQ0t~*M9vgC+b z`SoM?$C#-9PR0s5I*0Gj4W4$*Fn@-=P^NFO;KmixUG8Gy@6h{e75%$`KaP{9GMzb5 zK1z$MjkFKH$lyQ{VD$c#D|HL_B7W{f`{qeEepAn_*O$gQe^=$C?K}HcSQE)%0L*tc z6vB4{BFY8=w|?k(-Cx&}M{+nOIzoF#5UlxyfZg0DH^lcU(4Hr1Hx}Xw>cO|wSTWB) zX_+gL@3axLr-P2;8`4)H&%BCS@=bRMxGzV@g%(tf0 zZ!E)rx_N*`EC64Xj+4?knOOks)1@E34pWLC(v<*ZPK>E|5<+W2lSk&{$ ziE!u4We?S{qozFi*s0Kmee$Kq`O0}IV-{>ntU@T7a^Mg?9P||IbaO=hPCdhgEBelw z0>sH&IKoR?kmAYheNrL!V2%?iIUAp%2|hX_1m@UfqE4ISr7bIP9Pv4)Cm!Oq13`}O zz8q9_JqGB-SN5-?lGEc(Lcfbxw2}<>g#j^3j$Tv1%&Rbz%QBYU15ELO98T38WE9#Q zq_V~ponGSN&>4edxnTy}*-Lv{!^gEt1w<-X634T^0024dZY<`e)xjT1bUB^fZy>vT ze?9gyOWyXb^|*59+79s$ggmi%@AEM&>t?e&>T13T;Qlx6dc0kFVMC&LkwznS&Uu8f zdNc1dKbm~^9Yi$I9(_wf&c++FP^NGF6sO?Riaki;Vx?`$8qHyW`_gt_WF0=!{vz}t z2=Gq>=R1V|KH>dn9|$J;kruSfjv{T0FOxv}oGdb(xvB09wq=a{uH#d@5b6ji^-P8e zn2B$(?#)Sx7P-1CsBap+aGb~Y8lUNamxVfDNIEx?m>$M49@+#9D4hxKZG9akfTi%& zE%5IZCikvdbONMn;;*pFqF_%V2Y|Wo(aeG zRgui2YFOG4X%OOe0Of-LiE8L3P0~G=7SJn9tlfwrBB=Vl^?$-y*l^F!rNW1|;EM<-kdN z8P~lB>Nrh^z&H0{+UKNOGkuWJ+qSc`JREZ|fxCU1edU~SIL4KaM&l^}2u7EqixMbd04fhVRHyvsBT1}Jfr_&9(|aIv29q%YSFn}p z{My6rmUc=T2gMjVnjI2P4Zbq2GO| zzpOgONaw*v1n0$i+6SFa60h~wERRX22n&M$8E^=_{-4d`AsETA$Y0Ap)w6_RkhEJO zol?AFC>P(ghXsS0ciG7Jnd^cvs?RTdAX2$Wr^^A=NaVTvWpGs%>v8v_&Ww4Vb0lxs zWwZ7$t*0PjVHxX`R^_w6RkDZRHaWATsjD~SSAr+f^w!+=GA1I!Mgsdk%Leb>8w-x1 zXZWtg#H_7^y7isz)?bj@Usm;`sV1PBN4L6h|AEtlf}tEyQq(udi3jU2)cW+^-1=;) zBXc=@UT@`j93rLI3iVy!$_MnA3uDjT%jOQ$Kr@%oPhsdI?nD}q`V&IACTncIjYLhT z$xh{jeLvpRtejg8vxg;JATRyc6x#9lWH|kCeZ);DSjGz%D-t)KL$>Rx5dAi z-+K^GL}Shk%nh{wSVLzcn%>i>Fq;6>yqn+xk-53US$3e3OPG&FvC;(!goya@ii_t}u%1kW-_bJkuvJdIqF4%VL z|Fi(#vRs*XrT(0B^PT2mhr0=%!BqcJ=O#jxA`_guD+;<=>TI6v#!e5}Uzy8(H5uE> zQ_URC;2r%&o7>_X+0r4@Khu7#(e!QB+P9^d;HDEnJCt<1)S2flX9*kHY`N&TSO*g~ zcKcw&_~R$pgC$X@JDOCE$RAR7xQ&rR(t<9&gI~y)0r1Jh70n1Ids-Y?ggg;o9c}7@(1kz zE2`=CAE}#Mroec@Go^%7`&kS=kBl#3p!TVrY`9PCxZ5?un{i6UtLFd#We51XZ=E*7 z{~lUxQO@~*C-!@S_!?}Cv*vuyidC0Biil_P$(Wq5ObdfLIf2Mq?|@iK%u>M0!l)mU znN>9%IKQ8y$AZcj3{J371i$i!t>8V(BI@Y0z=ypLhWHUAA7=V{Hd?KLUJr>cnh;uw z>3VE;AJQ&redLa9PAc6pigr#!U>k~CTY_WcOpYih0cpCvXE@N3DxoO{4>gIB+uuDm zK%Rt5A|V2$#u`ATKfWKAxxS;-`SEB8c$aPp0hRu>Y;74E3knmCI6Lk6%N6P8$-m=W^?;Q z-i|N6aO{oG6CAkA>MqG^f0%}^h{zwq1zN~YPyeP)39D0~;UgiZK!5ra1mZ1_@4Ab) zjumTv6aBPpz3|aGtu!;cK>frk#@y98`s*^|k=tfd=LNHYPu zNEfu8LdgJRqcyi`3qWDl{!`0b!$%9Azch*iy+c2>*)S3R1VfPz!1(u?1%u1`G+24P zj7b&5c-(w zFaM;02--ttuon<}GmZ43Xie_x>CfMSNLjNJezOb42xLHy#yVG$jgWxt1JyAE&YHga zM&}-pDn|J_Rtq>b+7)Docn-M=KppCGy?7Hc@^jY4h(8#E!}6K5yH;g zitGX3j|O*=BTwc-ytPX$isLwfFm+cEMC}u}pb=ZXUY-I?WO@)9$p!*%?!Gg9&=uSd zW}Jh_hP$pnHQdH=6r303i6?KHKZ=wuWkXX6%q=C=OBzw%-p}ThAQ~m6{#+I~=~lfI z*Nub-#ffFEqOYtqc)20B*TvjItGsMe_va7h7>Niyg%;LHTPY;njKkCn8{}4gWm|gr zG)S0Kl?wnN&wHA~-@G>;Kyn^EK%p7Q#N7bLdiSwYBAwgw-wu=e&|AZpRj;@l+zQ(5 z2uaCWCLYu;0cq;U&jM7Ln@ps}G1Q39iLRV}3pnjf$RsZgcz_?V{hNzmCE=&91K!Eo zkn^AlXv}pRAMO}(1f*?d6OG_{^M48^vr$z~)LX}n|He^~#}c<(%8xx)Z; zXX=g=#D55a^FZ}em7_~nAiCS@F$kT17xvF(=X`NZv~+l@uSp1#p$MmN@Bg^cYKZjS z_7qPheGkVhz%Zf@!s37i1IxR(bQ!c=h00fDvM)er7}I3a-a7$n=b6qY?$T!uvU@-D zoJo|u@nw#g>^5M5sYg@t?toLU0hJ1Vl75q@+v{`^--8P?$H{N3dz8JkQ0i~*`M&xg z2d7l3fL~90^ya5<}s46p{*xF!#yWuoyy97x4Bl`-Mk zH?8*bVYjAO)zysWd2b^2LwTux@$ka#LAE=2VQ+G1b(S!iaS3Ztx9`5qLJI^zm4UKm zx6N{3J{c@84`+4p)x4oKzsTSINdetdgKkaX1*wf+RmGrX@CX9p1@y6{rjlQ9g}9fcbZKO*l9ni$eebJMs#gI@8u*r+7B@_m+k3Y& zbU-N_Q|AuC>{ir(Y7@{CjFTTzGVtB8M%%-~GJ!G?zm&7g6sAzXgebQ! z=pyHJ81m^lWD#b%nvq`PAxj$XzufQWg4$h?T-OJR#Q;fHJ!{Xc9ANP8z6w-LS0a9C z`tBFC>5<%awFbFK?JQ`S<2xJ-+(fd2XD~!LMw$=%jG#1WA9az^s7K?md^nFK;!`40 zbdM<*ucwv%7ujA#svx+PMA3FG)*J`p@}po6l;8+Z6s&*sj9OcC2B=ySjr`(ME)Wjh%GruIC3@VdhzJtHsHzI76T&&H7<^bMxUU6JBb~yXThV=v zq#N&$QXym{48JY>6>8U|zqV`w1xMeKim%B@0vUv@RPB;QPQ==Z_zMweN$3D`M(+*hxG zrvQLmZNSWuCT9P##RZ@&@$3}*2|C3?S+{j}gZBnF*X{St;*lRSFx zklStLuhsRU%r$P@m2V}v4-DL1AGphzJ{!=l>o5z$v6__6Nmk=iIuIZ*v}s?=6=Onz ziR^^h=;DnMc1%i$;{0MJA2%3Fo9tttTCTSXb$iT z&;i?e220>xUf=@$q{1I@rj*8Xbs+8r?%03(Tu!?^=^jx)35>Q=ukhp;9QDx`0rV!e z9B;j&I)=nasq43bk82T7uTv0NSol7j}{j+C3h6wZGg4bse*ml&ZS;0d|nmu|J z-%TYfFpoWx;_xZI%wyai-t>0>jN`aG?8dR96Mw8Ta%q*4pH$qsSen*(R)Dm`ht+|| zpT@BK)#$+jbBt3`Msm;(+#wRM+ZWkG!Il0IY>ANtBJ(BVPs6UU;tD3-00CXfpe#G) z!=~>^H-qur^NSjdIvxvyY>@ieCox9ZXZE~#&|!PK35DdfPZdG4`t#g`an&z<&TfMC|2C;i6zW=wRfI$ z+l-(AlkL02`NarY4*2l@>;q(@e)R(j3=3)`-CPyQ4j+io)a5{41*)P3buX*9qUlg8 z5S|1k)^48Iwu~su-rA3f_c`syj~?QZ<_q6<(pKWpIlrPzCA!D)U2?o)lDs=RPL5fp z^i5@KrS=4f%3=e}TBVQU!Mj2iS_K-WM?|V7a#7-N4{!?L-T%&|V+kM`p*Cn+BMpwa z-xE|@eZf#h7s@$FnzNhZ#3-^?EZT%d>uZ%>h0URYPk|QgWwpM z;$EruccA8st9tY1Jz+D0ft;%?%GY4K>RnVNxhnhR_W}ub*2UzzEq`f#SnH& zmHJf15ZK81!r~a;7|=B!!T06Tw;#DD-E`_`0ID4mO_Se>z5yIAG5?)x60-^-Oq{l~ zLh9Te<6bCeE)I5#(n@ygWD!dF&x|gWG1B%g%@xSx>~^2XJ*pZR3?GZ6wot>lsP9mo z%C!4dU+$v5*EYLU&2>jpaK<{Q-vfCo;fB=9#c-c93jAO_AoL!>60n!=*sXzRcLjwh zylUiyvmNULJHry` zZl=P@Z4pUJ*DZme&g>-R;;OK~(Px_(=H8ts`~6H)$S`0P&p9@ly9L@QX^s2Fp6MD) z91_lfqwCk-0|Fhu53~48dPd#KLtxJ{%Xr)gEXdb~wjKova3;||gXh13X&C!YXZ;31 zPZ;v-Wm8mxKv?6rFuaE4b^j%H94^9#u-$==uG;z39UH6Rg1|NrK3uy&nS*0nQ1YiC zbzkaO5Obx{>&y=$=A;tjEp2}lXl>9{^@FI2w9l50_vXe=AeU~sh|Z* zOx`dXOW%loTmzo=q}EFMF&+8M=ps*WD;VOOM1j2mEO#^6R%v+aIFL_gijuKO`1e&Q z>9_$~n-oR@-E>=ta@vh)6<&Jz%8{5aa%y)_qAaxgMpP~+;vC6v^ZX&G;cY&%#m4Jb zoMSEgMloaiL$gQ2wNToaQB^GqN$Ph?;s6=r;?D@Fy!^*6 zWm4G1Gjc9u@cf>yiIx_7V*0;Wf>ks(JqDVwMO^RFLQ}s4e#`0;ySD!JD7oc9<_5eDcme)6^7WI32UQ`O z$1e$lC#*khz8?=#&1_SIlqQ7TqZWH-eAmwVHkH=sXvsG2FdS_*cN{ zTII@|d&-^z{J3|ouNw19hqr{uBJKLUX(-a*jb#>5`>vNr>QxWZvd0TtfcWd5$phfe z(#id#2TY2lJ(`^}l%zpGy92r4@-}vhxQWnaA0+r2TuX`Ej*0ekF)U*@f6?1~NdP*g z>?vU5^XOxoKz>acbjq%U+Z=6P9h`@>GT((nm!5;=1C99+%4w0Iy@(+*RgREbBFSUH?Y&DZQ$d2Q76V};VpEd!c8}!2Uj_7tNo=aG?!+VgY zE_`n3S4b}w23Uj*AL|S-UT6dS;{Fj^nvAAuqv#q#X&=`rZ(KE%#S|sg2+U{#!b@*8 za`Q+rs(sxGocX=N5(TRDS!igDj^}>Px>mOXAMea*K-NRc4A$H+m5PC%xkF%F?id0A zIyZ#r-7e+h2I1u7YZ|aK%EzH~*>p;f5}-ed(=xOtA)0M>yf^|Dd2XC0z0}zQnq#Vm za*oRuQTK26m>-CYF>(BrE&(0Ua-0PpeNnzh_IjcMV(!}YaE2wh{lZJ~wSOLdv!xO^ zv0!m!j$O9Fcncol#RV(C%{i$eU82I!BB<)enTLGxtof4r1w&^9i^C+YQlG>bjE^*s z?r&^iu|}0zs*Oq(OSG^7zwj{(bOrN{M3l)Ia1e75nPuh+dDV6aAr>ICrcG#_zqXZv zmrUyGpa&MI6kJZF31Dp24sRu@eu=JY*q#<4qF9b_s9K2K4hi4eAhtvOpp4XA#-UKU z=RaY=sM`^c9HjYh|JiD#d`X-x(mBRcg_bQ=1^^*2?8dZH(uz4Sst1cGEzL0TdY{FL zPvhDXrV%{7be@%VGBuM=s0eL^ zemjIqhj7kK9XNo&r~SJ%&ovQ_9lkgReeOsR5Bu(MHJREn-bnQ2e2w|&nJ!VZ6`E*d zX%s*p1N@ZluTR}$bjn?aMtOz0L5MV zb6JQ$A>_pTv?WDAQLT%~h_XhTUI^1g;nE~Ozyk>RY{vn2%tty}G8xj{gvBTVcM7mWf0>!CeULl}& zs$F+erF6rl>1{F#_XXvnZ;coJ@b+w(en{OdvLXuqXy-BEge%(hdb{WYTzs>|WZ>6(>O?9)+u~P!fV@;sd+A=D1K$#~8M1 z)|uy5^l2w_PpfJ|0H-YTrNE5)-CzB|mKEK_{dp*ZGPOBG6pVx@R33z12rF4Mm$hRQu8qyd8fBTxG>~E@T{g2xogoD7|4V;(mwi4Srm}(30+{ z-Ue_mbi<{YeU^TZe5&B3-TK(5Xko>z&$Fk*U78=7#QyyniuK0j$bQr#>mKJb=xOxj z0Rt*Us52I?|BEnKi37fk+p>$~4^)qk3!lr1FR!n1Vm|hFc4$_+o&EdQheqTNbYyj{Vs##09h!-c<%D=eDsMiX_UeM##haI;`>^)3i+3V%d4K zwpyqzVw}_4453M(4dpz~pco_8?^YhAbCC94P6tIdlvaZTXWKlq18p0#oWRW493pvM zHNba2YeQ?)>e_%$vH|KP_p_e0o(ZD9?Jx}VzF9K>|qFbflM%TkIh&~oAE!MtCy zyvHa$aq+LIn->(eF}-yPoy~-N;O#!-=9N5g6s9QU9~gvettUC^!eRV0K<-AWdfppV zhWkNa)K`D7PzgPdjB8-0+ekHCn$!7%q|9;QpPYs%-Ck^V7+x$jO-8^}qOLQGOm0f<1tfoIA41@ZPBM}9fWTV?(l`L!GaLu z8!cpt{V#(M_~*N(`9TIc60yc!{}_z-VQvcOQ#}rW;=sMZe?37RR4r8GGEAkmENVDG zzi!4sbal60;_|c~j%)>*EUOECY@ZEK0jAmW0oC_;Kn9j*Mh9;m-8j8edTIO@kuVYAlejJ)#x2W7J;kJDR{a zgrWxx?$k8e;^e!uu4jS9{+<(>_!Hg>*C-qUtN`DZnsv`6)jDcIP&q}-6 zKPqHLfLR8Jtxog7l~r;$k0D?MB)(nE>?cJvM_Cj1Cc`&B;?GO9JTa)X5rHd^G@j%7 zs4vyzWmdew)Mfi6gKxrWsn<{D)RG#2U-I56IWhYuZZ>%Fa%Ie%*bGAWa5;Z4@?^^| zFR$Q_$cSH%_f}lI&|Bzq4<_5)fdNrDMz*-44*TnJq$tz@+j4M+BNXiC$H~^wDW2ox zFlF?u)EX74_x$v|5HA*nQfRDuvw<{31|H!drjO1vEpU0V;LV)(fSezybi z7R1rNhOP3|d4MnkA95aQ(BG+SDrI_f@M6H`n|s_AcOY;>9%dW*q#_8S@qIgV|=PtA^V2d29Z)+%-8PDv<15#rV7i6D%vBC6?%^Dw;$PXE$OK+ zJ=lK#Iw2)VghOiRTZC+VNtZFm0?MZ~lK>=jsk+2MjJ^6o#6tgtrp*o%vAp%RT}ar0 zciWM69WSTWeOVjYwfT3qV?<9+K(Tyax02DJ>T925m?Vh-QWbeRZkqw)%Px)^Q2!6_ zLb62)^l>s5tGxpUtWnq-hZb@5xexbpx4*b%1UZSKd1he7@-;J?vD=H%6d?f*CdyZ_Aet z{T;?rOcEA?=)qq@!t<-hVCKQ`+vRit%aBzkNM`p+e=d!vty1U+_S+>5r`@}OnE4Fq zq3p4?@@l~KA9LWLtAyct)nu%tg}%mGEQ;20&B(#?)J8=V}87uzjAqp=NXf~*AV2J}HqB)h6=F7H^arrt2v zC^0W}RvS8R0Q~6CsQLZry7@=L)7)9;I&L3k(UH)G#z<%F-#99XtV!&D`Hn+jRQ@{!;WMe*{=m^Q<*|tnK8i6#F zPp{*h{;5$Y-zd2dN~Z)hgHl*fF1|tQ#M-@cPc{296B$59i zT%IWWhXd!(fP|o$A-OmZtl%8?>z{D`e7=n7jAqvPLU@$9Y!qV34}-FK%EJYX{EkSb87|+Cg&4Ny>Mii z_8%z{d99YNwcUF@pFV(UhX%*iKCX1y&6y6?038`;v#}xWiLmwS243uxZzl#i(oNuwhFlVy<5gTHw49EY8{>_tWyz&mD!E5hii=i_H zPp6Er*Vi7xu@W}p{GmT4+;BQ%bU-G7k>I#D^gx%qEP$Yk24K@aBvY)uz*SylQTZ{@ zBp5$jG>#z_^h!ZIwyKdL^W3Qt+sVoJ+6e9ZUie-4$6R^9NG*c@MSNBH_uHC>V91R) z&aYv69_RP#eB^+t*D@TpbTw8=ws>mSUC7%JqLP8T0M^Z1L<<0dqzSx(T9(Iv^|WSF zu7$?V>R1)borggkCzXcT(p`JsKK|wsR{dXX!{gNmYAE(Y9Z*aJEYk!n%sj!PU$zjw z@e8;86}H-hVm7?S@KIIX2Dqg5SF2W9whVOMD(w}T{>%HeF-YN|CO3>7@2Y~sni!u& z3546gB>{#yWC5!esxp6=x9z51$Nq&{t()}&Wd0aXdlbIYjsJ^E{f%~*gUkb?i^fVG zcOs&@+Qjr}(}71-&ys{)lcG*+#||aD30O-H89&NGAX7LG`60BAGW{2w=pJKq*KU;d;~`c0?=(*GZxUGr`Ee5LO6P&_p6dmX z5U|ZP@&&K$=w&=2?~Z3{{v0X*TP;ri8_FMbS{}q>N*ip`DS%(ev3E? ztD%$R18|b9`6e+iX52$1NT23eLi(0Gs!cf9831Bw(ctPHqHFql$-CyR4^DvyqA%lf zfD;;@_=-vI^{<8ynVTXB&;jV}rSdWx+^5fKu{ZorAUQnW^WF}`f-@=SC981uLVnW{-Yn~_7mxK09>3=7S0nT+4!wI( zix1nybLAulPdsDqSLR%pdnv;$n+@1TaPN5wGAqn~ir}J8QASctS#b|rI=V1P?BwoD zHF4~NgzvMN9C&Zlk%vodj@t;-b*(mDLT;ndm$nFdFS!9gwO@*7h<;Nc`vkf15d+hE zXWX8waCy=^c*~RBxC&lu&FX+l&MOW~m(3_pzV@%3F#Bxq=m(r%OQ0%eKT`o@2ToWN zz7xTC05XgCYzyr%Eyd3RMLIO1_>rs!r=F|NhGIXl`8XWPLS!=S(g~#4zxSZyPxDl_ z`f+hgSfEG=1P*_F5CSDst)i*^=j4AfFvPXXFlvBnjw`-n?)eawN$ARc$Br{x+TsFs z<1-1&+~pOho|?P&#WSw&JqrAXbSLNKIgqR@vEM}JQ8eELH5IVom49i5T#I^^w=5ah z#X@O*-6V$afBEH{bY$E;I{2@_kEP0E@Ga7+`7l;xhDmY8v~P#%I6nKPvmBSeae=}e z!8R^}|L;L=AO{+O-{j`&?xhdUf-1bhrPwZvFY+@)zSJoezc?eoofWe!`hdp77|T^Y z?D3a&vKSMQ9Yjuob}z&+J1)LF9gHt$3643S+}-s;f_NJ%_{YpA-k{w zr9MhipBWe;Toj3;!{xe(T$l^ibU{cQ_p$@O1|5X6JI_Y=A}djT>`;_9Xwi(#zcNSP zWKB)G1j8gE`dNrcf?4+_k$cUp3niY`1jEjS^%`v5wH&dd4(9K~-Oj`^Wr+Q()bXke zuyx5bOqy!WjKH}1DpzAO!H%&?CBMh_?nP(`n!!GUsJa{4NU61m<}hsUJYUnwn!U9G z!k7q9`U*UvhvEps(C6w+uu{-145@n_LRN@}g=ctHX z=JQ@3Rzl#IiE?xSBN+dT=W`5_qE~OaC;ouuKpCGh@s}-4pRv03`m{*LRK-}V{m6s> zIJq!~7}*udoRO)fBzq0`2tXpebt6V~sl16Dhj?rR zPnPGh0%#R$E%mAQgp>ksH^BD#GV>2p5A9{8N$;q?QMeOiYB7lCBH4@BJ`O0 zj1Fk;qh>@)=8yVDGjnBOb2HD~M)P!48E(qSmM1;6mkF3(j<)f~OD5-U2eyG}6+MbM zqw%z1UUDZkiFX7nIlMM0TlzY$43mBrIRfS2( z9f9_HLru0=U^N1f#$JD~D#HXZ3pIqpz?Q%uhZMprqQq+b)8YQdYs?ueI_^#PbEVaU+Yp%y&AHog4gzs` z!t1sk2a)y&WB}AW74!WWqSTzEcVfSjXC%ilh zw8BJt-t}cJbD2*&b~xG@N-^i1L+}?vaPGt-V5k@9ePm59vB%V|d%7|Sr4&FCdUQ2V zc*8ZppTgM(0}q8_@!{`Jmkj(yMU5vJ6BooJt9MND5d2LW*w9#Hwbi%NEVUhWPlD3| z2VUqGVAHqQ4SMS4=)NBLqVn+v&R-?nyjyciYDfK?kS+_i`4I}HtxBRtGry-*GX?mh z+{p?U&Ylyw2lroZuG>i+QH(#}`$Jrx@mIE|Kj3CxhWy75fpt}owb+x!L7`54ImYuN zv;T+ar=wrfTOOe)GQZz=Jq0$kz@s#nS-S?AYDhN3GXO_u>3aMlXV8I{gfiI8k3$6` z4XbdUYTH3^5q??E$335q{g$=8A%{+qnq2>6-qCQDeUEXpVoX)#?{I9>dZO8&7Vs5~ z6hXfBafvPw*sghc>74lQ^Y*?f$jR6HP8QgcB&i2qDx|*i_3EVs9pJ6DF6)oDO2OfhULdbs+bB*m%3&JK0+c35c83E!M zMz7gWKG%Qj^38RDs~OHP#{(8g(mU9YFZL*fyuHcJcvdrX7x*D5aR)G-6K!Vn{Yi#G z3Ye^&l45qx)+6cFwGac#`jBZU(^Ps7(9D9XjxZfTR1Q7u`h5xjC34) ztWdWrsU3XK97>8UJ)9xSJ5Xf!psFlCk|L8)0$H)u*xx3X0?XXkh^Hf>Cxji%`JP5w z|0ZBU4Ta>mFW8YE&vVUb|7opNVg2i*o^#s?7Eu`8l+O75Di07M-H?A1@8Y1r`7nI# zZXP(Lx$|3XL$cnwpMCQ;sAKY3mDj#A|6=9h5AYcr$B`qv6BJWSARnC8_FzUP^c(2E z3s>rZa#et$1?j@b#>MRdmw|?6Fvv3XL?;N1dDCeGMqq`~aej|I0KoN9#i^=F?u>z% zBRPL>g6HEY&Vujp@~o8pZPbG!zt{*C%}>?~ru4zN^P{`_r}I+J_y~PH#EyP#VAAfX zz+ii$bmX{yCNQpRBk|9qk)I-_rw3T^G1v0mw@iT`S$^F5wz8)jc8Do_?&JqUc1|iO zwW#)&FfecNU?D5$qzOikwFn*8V@dm|$#PxBcbpc&FICX5p7j2ZUDrHs`+!etrwS{e7OYHTpM-fOa_=EFNMF${ zMB~}DdVEF?nqYFP?gGBihwj}KGZtdm4T@j1xAGSxrJoStYQ_vi6 zaY8I=5wH;Gd2Cqv6O-bWQ4Q!9N~jAFl4pvHXC9t7{f+UY~siX>E9X& z%4WNnf72O&xaE}w2#X%UP_dmKpOvfA84uX-w+vko#?&FF3rsZIabuHDA>bZk=z)T9 z$iZ+rWsJq-G^W>}?fG;ifo2+_4DHuW%mwW~WpFH}$rMkaNKF7Pjpy<(Oul4*eEc^A zjnDOAdS$es?4d1T79W(%o#DD|RO%g*yC?%#eIA4&p|Syq$gLdcb+NoI?GzT^ zoY}xj$FOTAs^{3-TJGK$1R9`(D+cAcCHCER`%3tIxDo|fzRf5`@M9QyO^ehedq?rH zoj+(TJb?a*Y!c^_7(0JK@lA4nq1`{Z55wuooTlyPBX}Vr4<1h$tw+e9fRIT2eH3L- zi;zBPtWdyOmn?uvK&?7osg=guoG5$E6u#t2@?JJ;OM{W)YV$U%W` zA+U*|>S3lAFJQSw?Ne{P1b3)164{y6!2S5e3B#p~t)n1zw2=7Wml5#@up{{`V2(2t zpk;BkPy(>Evi!LZRxE|S8D2A$^m+L8Hd_9rtcT0o#RX9yXZ{*X=K8+}d~0(z{ArN7 zmIXH;#Or}v<@R|HN@1t58>=2)vrP*TVuO&4Y?iY&am9Jyq&&W>8!%JwqK8l(CpbV%_^RKGW zbhqgW*PcaP$atn;DB5-wy)(d{s88ddad8)!)%XLF#unmZJZ{c+uD z@eTB_$8m-Oo+U;OIb_Q+knV=V$^>I{ox zm35FfUusV??tl3*4oCfRaNzC3?IiM&J_8;+k8OEnU~B&^G;ZYTnZG=(ZPm*`QN1>@ z#Q@uh=`(g*d?$IZN7@(w2g*2sR=R|IdXmVm+=Njbujd-C@J=fu4y?qqW1odF2A?x^s1#74-LqRK1MtbI_~5$f zG_pI9p&sb_D$G|z6ONBdI*{zrsqR@%WDMO~4do9!>3FbyufEjPci9TbGb^%dVE#~O zeVCjA{XMLL)s3*HLB^K>GG)BJQ#O34o0Q{9K=x}83>^sQj)`X+`yT41SY%Sjv)&<=-a>Tw7f={UPzzwWKG!veDa!N&<9gCVUdr7bDdzCiPa-*rne3^ zWVOr3foG1=VF>E^<9f(cBth`MgZwT7T}vSKYA++CDR~~8X|!SyG2r`?cMPF{WNz_a z^0#+B2@zc2Xx-BZ9r5Z| z9RSGlU(8X<9D_gOFJue24+r`NeTJz)zpReW_@xMg`v->_45K&>BA7iEOR(o zk6<+9U%*FfAaJ}-v?#dyT6rJ z6$gw<5yU3jpqIU2pAGW_ZP!yIGUrnc?+XG}uiMn65zYxGsLsGxeDMS0h;-~7jcQ`W z>93SQuhu1Cw)Rw?wuk4#n^I2M;(`mbJ~KR>4)4BD58tbJgo>9zWN;xMLd%4Sc43@v zLNOnKHt|Ln$Pg&}rgIz|&fk8#mAuG8gkf}ykGKn$LC?kGXoWMUNkp)iLbR?8(FmmG z3bnB>8V}7ZQ*Q>Gj#lhhn)dYZLGjqtm4}KAwL?x+Rx&fn>Se57q`hIGUlf?x*=ZrCU|1|llZE} zQ|F?NK^)iDPhNgKwne@o?aZSW<0Ng7=L)}D2-=5PI_62`Y=v#wEgOSkv=8L6dhW7EO{>)F<1 zm&zdaf|p9croV&}CbP_wjRQ_nu(i4urrMx3i%EbDX>1TQm}YucZOuL>^BPoC?nXwu z9};LX%_?W#-G8x6U?n>CO>Q#){K~N`nU@9U9(0P`$b%J*(yBaGymEN+j>zva+p#+w z&v@5ev+>4J+euv}XBBfKmCdDMu#sXQGzsO1%K~3cS-}4eAfA>rrzAT-_>EL!8!PSs z$>Q=!zC|elMNzi4a{W3k)8<;}hs_PX%@RIq1Z%V1+eK(Ro#L%;j$83P=9dEdrfMoJ z>d_HwG5BU|M<&LAM~B)-%)AYD@H_3{ZgFt57HJ)5xj@oe}AW6-I<6{d$h z(qqRn?`rH(#PQt4iSud~nZ9J4T4|*^-`fCgKglTBFBBfH-0hg^HMjGWlmZ;sSui~+ zX32n&o?zF^PZ7~N{vCC)@eP~?WfD{q$g16#fD^G9kprnl?uWVK3zuswt?ZB4i+omo zz5eeQE5S?xuMy-DuWo6`+ydAaU5X)@Z1~*$+W_~z%7t_^4X=MoU*v25OC>+l|2}hs zQ?auJRaerZo3ROJVjVW->MQY3dif@}%Y2);L}220f?Yp#rD0xkq{0J8)kwni=}+2{ zKoc_xhTxM9+r|_=E{w^<6Z7k*F(R{b8%evzy{Dz>gythNBAEree?+w6JD3SEYNQQ6 z=X@;w?qgRez~?HU6|dSmuz zU^1v0M<5_thky{$)eB)Wr~ZLw?%nCi0bZTbg2pRsINPq?YX`KwcC3W~a^RvOjZu3X z59f9wXN6Puiwxk34@!Kx@$fy$pg@Ok6nNvw{l@{zjKoSDHHLtY&36LS``aqv;iNG? zRRXMm7Zh~~o6uys6WUirNde{;j=lM58bkR$rIoKwYF<`E!8|w)oFKig^@=!4<|=*L zRfasb*;Y+|&4PE~)UbjrQucb1mAVRMLosf>y+qWWgSBOr8g+_RhiORLt}gwvK}j>fAP{xpA?wAC z7n+vn%eoI>=P&qhjOJIf0Tk*HMUP{VAl-m@##;`De=hMlkQxF+4DrOXQ-{D!ZseD6 z$Nrk>ezSMGl>e6t;DI7ZaE|vj3ZU{@VmW9-ayW7gWhjj{6ifZ8Q_5wz; z7km15Qo$fR8yo}u2CPDvP&AOoV{bo=`H%b$UYlIR(co0|z0|oZS2iLN9Ibduo5mEL zQ#Wc961Chu2IE~3nWZo-+zACl{Y~5`Z0B062H=t*H&-JB!DB^x=9gRThSY}-<5C`{ zv{-i93hz<6gCl3gHh?Hz)1H5e2!he<{_w+B0m+WS=bnq_dw{{dx8-}P0R~asOEQJ7 z#bdW_JqgB_I(7J0w6?3rpt%cgS4|jG45L{WC!FFRU1LXNP8aL%bC=6VMhNO!By7CQ zQJ|>t!}kJ1o80~CbaMPvjLJ=w7T-(yq(SCZR!bZm9rbe%_i8T8fhjLkeIT^umPwasS$)Iot`ulWfuJ}C#>1u4=CMOd}XFj524iKyS#7^h@;c7=?GyV!)msSTD z5WzOh#T!a78yb}%fSWFcWUuujl;)8-Fqi9AAw6Vy=G18ct4>_~-OEzd*m5ux)pag~Ml?V^;YQ@%1otVFfce=w&SJUCT%1pi zz@eJEu@AoK=&gwNbf1vTOG4B?$vV53)K`EpOg{ZOq_>{~4EEv?jKB(ilXf^l+pThz z`MjnGUae;sNJt+b!`P+NIeESHBjAeAAp0GZVCNAk1AT`)--5Or+9q028siy?4^_p% zU;N|qB2B<*H;|qG3aP8fEOWIRQNDNTxI7vzp@0vE{)7!%Ck#6Bcs_7aPUnl2vp7Q;w~se_*5jNrH!lQR+uG zyHl_N*Y-yNG2F5&=eh0y8=d1L?{vjw%zSz8Slv$*Pmbg8B zH~o(ya7bk9AcDK&pWP>5Ws76nZM%3Fjhk_rpS%|IUR#;N%}JdgJYbhsd5r6fIU9<5 zZrTS1!v0?73AdS=4d4+v@BFDW@{QfGgon{_T?xXUAs29(r*PoNck(EXIYTL?08x*u z{Euxd0_+=Up7xGw5vxM>NE8Pf=BGB9;e4Fs7PO-XU2NtQfHxyxDJ6G{jkqlof62+s z9h+c%UX)iiK zRaS-}v4uuF;`3G%8Pd{){=JNMB4Y5Z3Jm?xJ(@69yKQ*(VlY06z3lChw#?ui<~C+s zYfj00dt+~%We$QxZ0W<^fgtaI|px z#uy6yf$>#j*EB$+Z}NUs&b|Z35JVUse*=wo6u+S4ywY1LV5Lf-_jMkI5pRcfs~;sN z4A1$9Xo+I1S_!d$U42_k;0IierAV;@X!Si=aIIHms*6qE^Z`q{`LPy)A7*nO-yK7e z;`#&mn&X4d4@x|Eg_@Sw#JwX|W9u%gX0hOUboDVlN8=g(2aPD$Z`IJmOul)P3~;`d zu;)eu-AzJ7O8NmWrTNN~e+_ot$WR7s3+}NYDbEUGSd)J5>kSt8!jdyMX|YKJ%0BV% zj(B^TFerY2J^eP-oRzk)K9JWR^xwh!t7kwPg&jKp#i!lzQ}7?5#OuSz^r(gMpuEkp!__b_Nwm7ovOkW0A(oSfeRIAF;tA`=(K{mj z`cqH-6Ji&U+7cRQ4#sQ53+}}xytAh@IrbNgD=9L;^`D33&F^PsND*Q$jZl|N0LWuu zfm_yv6ZE)-48U@;O~|r9X0Riu!}z20WhZODZ<;cIe(%_JerQPh$f|P{DaIBPfd|d=s+#ffZj3 zNNO>J?IWLHlS;(g^ zQ+)mfYhI4VG{BJ0XNCH`CjF$8X{1smD4IddeI+zN|34odDA00`nUqA_zPd&oYersm zgto1G&fALp68;jnk#t3kIvCb`Hl?V>ev}7-XsSJJmfgoS?D+awhb*{SOwZl?aO0#c z3e&XYzma5o4~XmLrE;N$=YWqh0QLFFrH3Os9ld_N`LO%27%v;XUbUW`Qusp8QT!>? zKl$N@LlQTD{BJWR2yd1hfBfSIumfr~NM2;N&vxk>=RZ{_3PUzEQmgYr?z~D9v{&^n zu1ZqtV>=llkr39(wVYi1=#?@2Tkryw?~O_n-Y#-+{Q{FfxtDf1z}kl=#RC?Nm8$L0 z@gu;*u>mxfKYWq;1CIE@+t0^ZVPL?wZM(NnxPKjdPg2bXJz&^5Ml1d<8Y&Jwkdm1w zAs`5GIcOX?AjMO1AA)^q_4pu3x%jZ)kr$yY_a3Ej z0r4J#;p{V5QV~Yf&xa_Um*N^sV4$JFjt>6g#fov)!~7Ofw~ae=#WGG|Z2+V-#DIHrQ87P+C@p8r}r zT*Wne5)`>CJMBbY;mDOWO8qMJ>Z$uMm$%q?V<5~jIO@A=pZCcN>kyIWXJ*EH2+$G+N3fI3CEb&zO(1@7oDi_;gKuMw zCzK0V8LkA~0NJ^_s!XIkDeSK+8lMdOH57hFq@gKAkTO!3BwMD^x_g|Tm%)LP(cu25 znt{S(`E@#bzx0Y?<{>?C)4o9a8q(`9TqF1CZX+D+@c^)gnyS{ev{Mx0Nx_58;XLn~;{gxo7f&nxZpFz&t`L&{jb0Q)30zmT z1+uVl@0sX=vK7}GY(>Euu6aYU#prp+M>yu-1)32+GF)c@fJm(@ybJmGQ+Mr?TLqeK zd39A66W#wh;8Vk zFf1s#3pZ3&YV$pq*Z3vJh*Lb;R=8JWVdv&gicsq%SVU`~^Adg41Uxz^;|NO>9kWnU zSN^KhGzZ6YiOcS>RQ|4#!{%{Np4n^Sd#O`!$POMXV@4HIQAZJhigp{gryEzff`Zmc zs>#>Py01C`!nojQEwNODIk)MTMh!V+=%(bmfjcs+I9a#!L^q9RhDpBQ%Nt><$Dv^} z@H%nVSbidKtb!oH>jjUq9t*u3&2f~hG@v45DAi=^b2z#HD?;>>7yrT}#MKu*1fg9{ zx8&{Q8|GxN21G#YN?Tk@Fr@YHn`g#ir-bLfTY?|ubHlT6CVu%1{|CeGHPG+Zy#$)5_rnAi$GyNi zb`6`c3VAlq-`5oLhb!w&7>?0+)y%?_EW5}BzTTx-(@k*e7v;>Ka7tDBO!~JTup>%@ zq8M>zq)J~Zp0VxEBuSacN2<>fg-@F`eRlGEX>>pZFmq1hsDF5Vb0R4U9^;mZ{UFF* zlRUc&)Q+-4C5yqiivm{AK&9z39*nDAMg13A;`hVLhNVbWq2hIrO8D}}@hXf7JkyeC z`EhM_^;_G3=y25r9hOL}aXD3NbqdDy z)2V-@Dqkvs^rS)p#!N2CpITIMu7$O#N5Xwh%a}r|Q4bK?c5rg^EFWD_lk^d40f~GM zJ{T*$>e?*@DFV%bq#douX`hO#GHpiRjg8c$@3WyX1Zx9j%a#mOvhwXgyy1%~jBZ(MTOas2C} z`N18}5Sd_nmVg!Io?+)-U?%=}69m^ePu;?H(etrGSZp6mUdVH z`8Wx4zX_NVeVSnB}rEQ=|3sKzQWf2G&4^fa!FcfNl7B9xe*)Ay+VUB=B_M#|v_P4WS}a89=qAK9I_ zkqUPyMGR-R=~$9aY&A?GS{&-2EYntyH?z~HEq{dgLH3>P#G zc-3}(m!P50JBVOEzkYc}9j?8n5wcJCNs2wD`$!`Y@GU7+4*qzVVsLFsNn~`OunCu* z80o-8Jq5w`mvp^pj?^_rkv@P^_MFN^d2ak6u>-P?Sn)|W6;O=6$OG_Jw6N-lpF44( z_vV#u!dsImm?8^U!ECWCtW6Cgn(GOq4Dep@xNoE(u^m8Hz~eB(L2Yh4BM+v)Dt~xG zXrq@afgw=qnr&wr^tJ-bVQ#I<-q{8qK~`HuT2DBPQXIpSZ;7uYv{xp31w3r!Ey)Pjp00_*e7kKYMUI}Cc^ zzaIMw*FxO|$JrQh%w@Y7i0AXD;yDlW>GXeqvAOBMC$HPD{E1nG|9M7Bah*49zx%L7 z1*{0Z_pPKkV7!Xu5cM@P)#bSFUL}qDu4)i7>#H1Ch>kyg)HC4%vNxZQLVU#9jS*Hz z+VEL1tw=GoFy512)dVK9XngIy7BSgk;swUvf0t%k(q&Cv?5l2*mfm+w>CMx(`reN? zxHugUtU{3|Sx_xk&En$Dlu>;R|IeGx)ODJ}W7RFxuNTJBP-O^i*D)*-`r`M~w<7{s zQHzr4fEAEjc_oMox^!?Egn zBNqpd_P||%dmTv(Ewe@`uKQ>eYHw`4%HlSB%cjZ)sb}BsVDNJb#QFss7yjaFUDrO{95nncQb&D4i9pRf7{=~&EOekBrT2%DyHRB2f_xjCtRa8uY=sc0(6&PEI zMXA-usgUiFH=5`+C9Oz!MZdA$t(N@5Kp7xgM}4d3@+=;|bx&Abo+?&VXsOOhzf*jo zR%%io^Zv7%*HZoubdwKXUHXD}nAz_r+ZKi4o=R-84W6)H$cob1r1pr2NfsWelQO-q zB`Qdi8AbKKvT2ZNf@dJa6xhWf#6x+Fmd0pj92YJt0;C@NivJtdNsaWF_71Hvf=V;6qLo*4(GbEu9O7!dfaL zRkQ507!mZ!Sky=fi}rciAH#G%{PVcT(FkluFkj!mov9sC0tSW}`O-J~*z_;O;7=}> z9Y>SWqS8eC@O_RSYPdZ2gbApSNf@nugkLbez8&SUpn32$$meh8GQX%!(N^lMm zYS2ga+)KA{;C}P(*v47L71pp;afQ7SUAk=a6uEmyDj}S;gpn|Ah8D6bvLf5*=u}U8 zUJR8DeDpy1;2j zK6F`T{~=Q63x$Yxd-`WbuN>^&hI?q$+AP&%g>bHIDmyit#k5f>yU_!1z2vwfur$auL&z#x`1*sf24Rtz_j*UpW6V7csS}R6m%GO3!6F~#!QOjw zT-FaS?KXHLJ>$YWzk^eV&K$8;?8>3@d&~XjasJ%u1i^Z2)Lece*X3>aQ5lN-qLo@O z`i*6@@R0Sg$s94~cd`t1|GtL@RC4fC)lU-9MnX83_yz%ZP@VBU7EM(B)7cN?_yZcV zCfjiMjEqz{W6WHT&)lBK8gooaict~`5y+pazZ)-iYp{}D#3Gj1P;`|O?;$^I>xXy4 z=gi`sUx{x>af$f)U7wwlAANlMEapBcrA4Q(SG~e(C}MRuqU`J2v$!FmAD#zAwXxqf z9HH37!H&66Heo}4xBA=&tHOy(Uw*7c!%JxNn_H73MkBs{@>yan^EV??Fr9X#mGp4z z^`+8bc3N%kcl@;oWM3hpxb&1ztCM;qRzu;jv=t{F>KAq0E)T1IOwBM>xVYLEOS~u* zNy6ZAy!>Ntkk`9*c{10n*St4Bar6V1miBfVOuXf!)RPc7u7b}&QGYj5`q}r$fFnk{ z^S+bT8Yk_Dj1fP)#gZGvu3d@h5{-8ZZ}3D*8GKe@NIf$}irlm7x%e{}2QmjvjYg2H z$ldeATj(-TM~Zw5K6?!_N8HH`-5NzXh?|5aDUOxUE3x4d^F&s~3dgb?371sc(oxSI zHx>zM0SJVCk%5XadpXdpm!epl*6Q<7%JiZhNr?EM8sEm{`Gi+L^~pXw)nYkif%u(> zDVcHXaWLV+b&mC7ZsLEVN#k2pD)@Te4gbm=7D8Zj4jZy8U8Jpn;n#^nLK3WLc@#y5Zz8F zh*GVSQkWwuj<#o^sk}$Z{t)-`-Q}&Mf8n89RLz#4@$G@_yQGCWyR$hVbtO|N6JrYrHju4C?L*%_QwM;iez@T=4 zn7KRh;Jx-_7bScoRL+*-Wb;K|hBf)dHoQ}{p4WL=B3Auu73n7a0h(czbP;`nj3nL^ zBoJq|+!o-22EKWjx30)__ifjmEoa9{>@_s4?e)~Zt=MY)TkoZwJ)WwVC$3Ym%e$S~ zE9w1J|8Asc%@KVzI(e`caqSyRnnvdtqg556!(x5>_ZJ0JSZ6A+$KN&9a{XM?caAA= z)Vt1Nc;$Tt-Kg{W&BB^uYF^AW`iJ%V@e`lSC9t)5=YH+FbLP5!C3cSGeZ3z(hTHNN z&(E3yw+L)R1=mL|tLGGFuIt+JRQcN}^;~nRytei^*IzNN^Bb_T{CJ_#SV+abFzU1n zi6d}N>>!FbXK<+)8e&oy;05wdlDRwCUp#{MeexI0jyoP!_3p^K0eCzaEo3#ohRty6 z-r7i6_&`^Tbg?AAYLa(|f29DUT0)9&tqV!AlD=JR7{s0<~+j$d0(ChlC!c_&1hi(_rE79v141Audp4PJU3$s z{tr`c9uHL;{*TXChp`;W3}u-!sl+g7kz_lTv@c4A3^8aWB!moR7)v!uC`C!TijgJ8 z+F;676501HOWCvbyFH)J=llBo_x$rbk2&YQuIs&B_bKJFo%M)}GXye^f;oQ4@70IG zb^eykucI<r;;BZIPAy!HAuymzl>7*wZfOm5yJ|p?oc{P@QXUKio@4T4!Gj{89|7Q@GoTqll9=L^~cHB~~~U z_4@lueZfv%ef*~u`wB~Vd)pWT-%dhUS2`9rd^@s=5IZ|fvG^bkxnp6oH}VQz6#X$s zD-2&Q3+nZ_fLevXDyWKZ1oLd{NdQck8G_bI_Jg$*Iz4-Y*v$kT8;Q2bnnF|=P?sh$GGke>Q1gmk zRnm0)AXw$wZ|mhNq`pUia4XUR445~+4ed+jJ~ST?VEG2~u4@Qk_}KeTENl`{4U7H( zkeND6ll$XD+8QBIT*jpuX`6_&4H6h6pMjyH&Q`t5E5@=(_;S##TvH{Ch>*+8gg$G_|PrkQjfBu-lqmKBmj7e+om6ewq z!pYRzvSiiv9-|#(-hht4H1d$i20d7-#8h^za&S8(74F>qeK0y+=VL~xWe#Oe(-$ey-Sh@F&Vxw?3ysj%nHu+tqJp)~MTcRFsUb35*=tfXmDYQoqWLrQiU zyoPP>+%&97%o@Udsj@>oxutTH%T}eoeD-_g*TIQkP{o5)Sjs@DGrB28YOAG{$;BO# zF&VSsd>`uZckf6{HV+#$3Mm_qd9Eq4DKGSw$|_n-G3VVj2$LDE6%Ge&5UUy-B@7gG z%oOzi=JKE-l1Q$>NbS&3i2E=Q~k4 zaYqT9ZKJiUtNh7xalUiQS~yAqmwgvPNG<@&A9=B1Pyehq95;>B39*aCSk+In9u9L4 zuL=(A6(g7h-)Rjw(|J|yrQ>BP>tx&QmF!()1{D=^cnH8!cRiCJard|>WJ>PXInQHq zo&sCYs=3x#pZ0;*#0Jps!xwuJwsL0C^ydF%0a|aI!XrqRxEJ2+P@f1U`^@Rnr58p< z765Sqr}{;@~$!N;?vL52p?4OSU+*(-6I<=+j}dCaqQQTbISLlQ4K9u#c`B6 z0oGiQ$|%KonDUIdR@dyrXsF?W-`ii?Vq~xx)gR^V+&qFDpGFiJXnU%a83yiGk_6w7 z+c|jn;SrcO_qd;*of%;N;WSF2^-}#qymwc`q?Ke&$0lf>iw# zSwtW&u=&QfG~?vXjf7b5w_rLwE5LdvTouDncDeg3ajNREXP0Z7eIca3)o?=$sZOEn z0Oo8$@C6)29l;FLjIf`p(QRiP>Z<)u2v|uBMJJIsI~b5t0~b04n~&J=#lns(OfJO0 z3i=rM+1Szhc<(I?NF?*M1#VNfS}hjLHk84iqURPky2F0RL}XW+dYJ>^rA5LlD z0Q_G<8LuY)ht#oKhc@DD1@c(jskNDNM1WPf<+C)oKVSh{5wL6XkHeRHnKM^&SfI@c zV0@Mpnb11Z!ug55#gIH%lS&V|HWPZAD12s4drUvop49iGW2YwP11QrxguWjPHhI{4 zI^B?LK5`A^u1phyzxOlQnw*nNsOK7pr~^jVHX)fK#Fn4?o5g}3Abj%x_yWTEWT$ts z=`PCI`k$NlI*f+vj8kIm&US@q!VI{bFAtrCop|47}d6$1+DZ2Z618pA5aE>EMij z|L9z*6ZJKM@p(I{?(zZ!zF?AqC-+Z2eUmY?h0h~#>Y_7hup{dMGrZcvoEel!!k^gS zINYDF_H#x9{IQ zgt;P&lO|nz>OB@d-XT7MGg}m9XDw@f$$3}SP!tVE;qL(IJJP5Yo0oD`7Ex_0vN>wq z#{}TYt>xzXEb|_wME?_YFpVIyjc*hHxx0NdW7GTpDcbu_S27+TtltH4Uk*BewdTNT z&P{Pb<6=^jMFORJaPA#{F@%0DPR?B73GEqp2yzIf=x#MuM`jv1a8ggpT>3z6o|s)o z*Tn08H7-}>V85VehXQyT+ib_*hFT;y?@-nc&;V$5%Q0=ombw=8coFf!f8m5G4uaEcph;W1s1* z32(F*na*1PV22PZykGWPw00Ft#%6};wZ^lSkeu)g{uPUeU9_A zZ*r}~i}wTrEPo!v4u<#&?=Akd;D2r+JZrs)n(GU3Sfgqmprbf#!5Gp(1J|CtZlAFF z>kwL-;Snm|ME#mcmb=JK_b=FOermssk9BU78J##G1U2X@-wKBL+uh=J;;EPl;+r|eaD?TpAS(Fy7LKB* zk<^!Z1i@_oIkLEA(M|Lc_N#?jow4Gi=!XZh_$P2?*O}}j(9J3o0&<F(~_qcNl@pF?cCcLACRS5|n( z@Sk-YmfNktsy!^Vqp}S9G5FN(!AExvecw!FEuw1yMB9IsF7FVNkqbE+?h|`wCZc`% zB4PWNh1<)6`!PDVItWp)m++M!JDQ~E?Sc8dC#L00B`6tA3Afj;6?xf`!9eUWRG~dU z$((&Y=Lb=1c;WZ@?MlhnsM^B`>`eOT3 z8S){s-m)eKdbD)U#$-GYwlQ>dzqe0(;*2_()@{RoN5B9(aW9@~74-l@YE!Qt`}I*y zmlo<;B;$ust>j@_E>*(A7pT2S3E*@}Q>~zwjHoH3j+Cjo2$U=E*(WJ`t$NU`Qd1yu z|2|{2j=>M{RwKLqDfI+PWs|{ENV90_(M!mMu3E%oXo0WUN|f6FJ}O}4?zOY?oU&T; z^C5F4dAm7Aun^{A+|H)193Hy>N`rTU2j2i8>GK@J zfr}WzAGYThxBwu%{rX<_ze&kMcgTCiVQa|j7s9&jz45WyqA--D{xC`28MhOiPRY&4 z{%V7xoZv!Hht;1!BW`^ zl*Z*vVK|DjGizM(^*e8iIxNsZUdF&Wb~GhV`tH4c+B)XWO4CZ*2Yvn`s8-diGm6fQ3tZxq&MUt;!Yv<*b9~F z5q~rJdxGAb#>8!`9$1wc7&OZ(NN(GbrA5EG%VEs(xuZOpmkjk5dguqtf}!s^hymZv zi27?|^&WL%^mzvO?Vk>_@#VQD8+w>)9MgQmpE`)VgTxVe^SV#(Hi9`&+2cv``Ou*V@3q|eQaIuLMy%k#A)ub7CK1RC#q`?@Jf{Wi=a(=rbv90J7#F9&DfRXb z-0n=}x81&>G*ouhawM>1@s5+W<=;~L=vxTY;}0h+qzFM}1$P1V5rdn2);}2ojk*tK zirEyQ$Yeh;H0@=oQduTOcl(*K;_#hu>YEopQ+Bn-$Yl6k2(+Zdohm<>vgD4i4wb0Q z`;`uwS%##J-uQu{+(tPU`5nShsNY9$_v_tbbBqcZ(35LkGA6ue+57ScWI{KUNq=g+ znaVH4+l%IJB6aM>NEU?*7LWH8czI{?)sMHFQW`JMsCaR!W>;HyN5VxE45ZmU{R7s* zen$mZrKfUu*z323J36FyFK6nUfQmr&|n2<~i=^1A$l@+<52lT^# z^Qw$u1+9~)%G2gc8nuV2?A|MLzJy<~j#XJXB`4IgObxnY4- zw77wtze)R+^>zm3xTHe8$2_*7%`?O|LBuEljESsitLZwz0V>P9&+30H7hAh;Y6&_b zKt?$y4mZ_%)97RBqgn6hdo<{eFUS4JX~E_43rYmP$o86E_jqjH|5a`~S%@>6QH#y$ zBps+Z{#5u6p7qniUe6@o=hq=1PoT|v=sT~=sFBG1-v$%8Uh#KMEtccwmltEB@v_4v z;KB6ZvR{k=SIDVjzfz?dVl|{_Q3I6gt0B$5-$L{mj&T-Dl+9{Lxiq*fzyQpoqDT#I9=a5U{cu8$4|qix0~uaq>l<7=hovg&{9!sKdxE zJJroRrtaG%z;9Dt-L({sYM4CJ>TGo^CL^@Iqa(+og?HWM?Bk;srjT@ejcE)iRBr`t ztg2jHIN=Ysb)bYEZDd5>$0KU>_fct?p$eBL0NQ%6@O)-CYgALa&S;-EY5zMPEk|kB zxF70;_fW*+ep;q2O`bn7aHH>IYt+a;kE=M!Wteu(^R7R@wM2G3kicT(YJQ!UQx}6H zc$I4i^uyrmP!5nNVKI19NmhxcIKjs8^tt)&&!(u6pk-Q|-10&#@oRRaTEsC1JFe>+ z&cyt6J?NazPy#zV!p=p*I`*{VI9vQ=+7^a{l6L;O^@(`rc+d+?5PQs%rUf^@?wFaz z*~jw4d~F_YO$U3rLQL3*edrV^=w5!|)1|h<>-}LPO%CeN9Xz7hydTx0nEB!hR^j|z zQ#9}SnVKP~OL4PpsYR?Yf^PlQ6*n#GdSFR(5#Jc5l6(k`tXShFzs)4hl-yy=lbYkA znCp2c-R1UY=BstHH~!9RfuDa=dq0(R<)c)81`hxnDQw$6dRK8*PB3 zX7mLeOAtg@PDO1Gb&y)Ks$J@SYI^%%-h_g(5L71_IB-q+4nqifQ{(~~!uA2CykHWk zJG*iV7nGo2iS?M20IO;!+Cf6+V-8B-`}xQLXjysOABZsE#9i&Y0Tl?N)a+^7CXoUL z^w=2F0+~d#zTfxwJBRN*$owGv2siLc*5tY#YS)0oS#Tr&)rw1Ta!?^&;5k3f{kEvAP6Ob8UFh5bES%!ey}Npt zM8=1qSjjIPqgPfo0}t+VsCU)kh05%a;Q%!NZ|_eb*MyJ7E1i}tzd5sTBZTZ|9@-72 zM3&$#^05cKUsFXFm)XKiKR?yn!KbOwXWqGj|C`}&Ah^uyleY9Q)fuRP8`v?=zF~6K zA4E|I>h@p9SaG{h@BT=Wb4f}c9D}jB(&Dj~(FX^ZNw19-!xltNY5U}fk}>+$RDOr1 z14_YRd8<>LYb`eJA%AlKe2Y0*hdrLjCmM@>B988^pVu`>S~y3c-K@QN!lglR?<6)V z?@G`gB)(H#KOJAP6Y)8Dz-;8yLPX~i^LTvk2`&&6EENc$p3MCIRI)U}s*t9klmUw3 zHwCWevVrh_?kG^R@Rium2lB)askjFhC7p1F2rDEeL)Sk-?b)MB+PE;pVWSSHZG?o3YrnKr#*5S;Vt|-7y3-iQfP?zkipJMlpm}EqhnOo z9|o%7C)XDg+D8`r&gXI>l$NPJGwS~ zob0v-(_!uOA1E$DtXS?hP5npk$gqIbV|Q^lVmFo$`N!(FWz&9%C+*_r^-cge62S3} zVoues#b2NS$L<9e15{Fa)fg8XFnIL`<=C0No z{@I@9^~F*S9R8|&9`HrN4q#phu}@!L3ja)@39mP?8k&*+Wp#amb?&W>f)ftilqcVs zxP|dYh;35eAA={V(ovGy1-15uQTTFf(}&^@FPv4_#6m|nw(ZgLtP|7p71LV zaqefvm##Prd{QrlBpc)WGx;y%%WL%ZtdU=>cs!>=z;7NIh&GY%GUU&ge%+bDSFK=w zTloIuhO_`Cwrpd?lGT3W;GTnlRp9%Q8>zZv-k%C+N)n0+E~qR!j9|{!K0W>Bh6s!J zeiM)eO)=X(k6z>OdvAWS)#Q-sqrr%l{Ug%#7(!kd2vb?MrH=NPZ)Z}$(@|XXaKN;5 zJ!!vTJ6eAL_uy_1lV*}DL8f1|d1I8QUAI6DyfpJR4qFmnX`SZ~V2FV|1i(UD8jzRP zn@DAUwL$|ueKqi59!DJ2miEuD5xnuPnF?%xuW0*a0@>)iJ?%We8S44=@ij(gt(Sp; zs2E;ohodxcAyN6c#x-&4+v4wWTz-*z&4|E?MGPJ(wrI&QauY@KTn~x#GIw`~E2x>J z2-d$lT2|w|H81YKD`Ug!4^6jQS+;Fdn;87z!Ng?T&0S(tO0rST4BHN!MSC;MtCdPN zs`rD+Gh;xaXu-mFhjJcw{8aV>C9q%OZ0fickoshi9CD4xQ6TpyDW4iSlTq1R?N?O```eBies zKHz&_uurtEZ0}*NA27uz?ZzPQA=nGP10 z9Plg8rfmS4sMhT=iVGl6$%ao3Edk!b1%epD4+8D~qsrKK%$5j7zMO2HCOWQ{SU~X!G1)-O&b7kRj=1T3tGE8Ly0($?dyHGJ7e$sd>q{m)U+v z{pIB38}~zyI<3VmyYoA_Ef~3nZ;(E4L7ZUdswaMTLkzxmv{GRHEBLdB*k>>0%UepD zSI-u{WqdE|VsqtWKgt7bVMk>@5Rn%++vti7??V(m^|-FSI+Ib*d@}Xg{wTaI*H2Z) z#2GSsoElOrl(P4Nj>(zDhbAqT1K6DzOv1-4u4QuyJ(| z4#?x%8SfE;=@oB*A?5Y+ZcPPoRKR~tkl6eOyk=H|zlyqnzJWtvb-*;TMQyFj31rpk zvk6B9>uN9D>(4iK_*=XEOTp~D)BQM#$W9z*@0+bIZFtGS`~L7)xYhBLSy`W71VRU?0IO@dLC9d9@ME@k@BmkdD@fJALPO8<)(KgL1fmOSOtfJtD zg1JO8&nXE_G}^M0#9;)0Y)kr*zR^-;J^PNwV(s82dG2}r{V^nzX#gy6k*ibceSRAq zbI;H%CSxFNF)$a^q;Y?F2#`eMu+ND~0ozF&Tl*r_2%g;7pU>ElqX6;%!)gA^FB`vA zP0kqx)SOLtI<}>I@71Q%As+UA{@SrtZ9JavDWelLwNLxa#oF?S*Q$9%uW}%7P_VCt z1P!uNPHao~qrJUl*M(kYrl{yF0VqRzj7G!py&uYQcB?fCv!4vLT@!;FKP4Vk$?Ima zie-YAw|^73m@!7ms&o@{ve#-m>%ZovhJEMxuX!!L)FgoBPzw(T^mDPFK^|kP2w%Ie zd(&u$>4Aa~6-TaFTk`#F(eS!lzTKcnihl79o$WgPYV?OktjrKh_l=F`fpIu{$_%4F zI9^Yz-ie6=#r$+b<&)a2rPwT4Y}huFMkv0WP*Z>>lVI+f^&gOL*GEz+tcou0Tby{hU;2qNl=+RA19ZDN3Kh` zRd}cguxj_}`BGWvgf|zmAD?aBr+oKLUY}wWwRo6DzZ$&oZ9;Eto~_s$2HvOY@cKlI z)Tyi&f|NFa>V;=wN4Zit%U)XG*Y95stE!UIbD4}6_VDPN=tm8m`-Q~O~RG{^cN3!X-BI)h8l6GX8AVaOm6LWz}$Z< z4*~~t5ZuaIZIC;nwz>8CvOZ_x)5~yqSxvH#7ln3Q3MoSPr9Gy##A?^@HeW%SK~USX z_Y%o4EL2JhfHz+;-a`L50|JsG<9ERn`|O*zDdI}l1UdjNs_^~5Z}rfKJGR)Z7={-} z40vfX;aV+Fjmh0~{!qcK<7cUSqUgKSQzkCur#+4H%?>P%Ji0B@^fKEp`3U=vN9rd1 z1l8t`aaRk_4cqY|*~8-g8mjf7q|twiFQIaWw#~n^Vl5IzWDwLwD(hx8-LmB1S`e=M z(UGb}z0;Wc8vn_AJ*WQoX%R7IsY;L4{s~m;S06vQ6kmc>dD&a2R{825ZX39%wxxcYLd?SuWq-H` z9Ci+0H{qI1`aUO*E1r7h$KB0$Y>-1o+{_`?}3=pLb`h z^ncV9=+FlUWxKxmSE6*5iFXE>pz^jnHT~LBRwEw(G=`krx+Y`At4~$lvhUnjtXTCM z8Bg_x_n`0PqI7rlyTcK&%O7DnG_@5Ao7l!=xLvWniwCO9j_Fi!z=kzIIAQg7UHn>3 zgfz!M#`_}iC%`gNM%JA*jkny7d<>;vU(Hc;o8@ky$3Aio!ocEQgdc_<(T9|K7IAyl z>U-Nsc%|Y?#uBz1M?sZOBC1jpnLlf>WwAd9P;@b5+Ete*8B0voO1cO0DlT|zzE#hJ zK}zMjs8MK>nj9@Ao+xiV^ar^P5691z`u@({G5&v9fRvd391G`*aywn9{3Mj_iFo6` z!;YIacg);PEV*bL0_3Q#J)b171Q*w&9b_6I=^pu*LoAyqE;9Klh5Z}e=!d5HPJ+8#5RS$SYw?`?J=_^;_>2=I=Z{6&=sSrX?$w|B+7{j(5M{+Z;|B!tcAMp#Wj@3lRui`dW|5@(BbWON>a@#}M_U<0Mhx|?aM zXbxU#=pGdVrz@g=Z0X~wbcuU$9Is#7s@yb0*W+c(wCk`i#p(RgazsDVN(B+p`)AGJ z3lK|!KlQI^^wqwWWk=wAizY~r@k7+zDbAPi*q|~WH@{zKGOqKwUxjCS%O7MfDqwU( zm5zCP+B%P~``lU7YiR2FY7>I#TL6T@Hon6Wv!4r98LnFVLC%mk5!FYs*HU{0i{k=r zD}SNu-yWI`P}8&X_!~xl&z4Av!DwN>9|2D9xa>m#$`5S!h!ftCl>+4uB=??0F>wuj zKGJyE@$}qVLIiDwoD_OfCmU7g$vrOUXt(CLydE!id2RAD;jExs7`|Z}_~4mj&-Q(= z`Scd4kMH(%Iu{k@WtKJ+%QfuG4F*|du1B~X4HWIPbMvY8x{ZX&8;X{nuwEeIzuu(# ztEibhO~1b*AKkla(D?5U+_sSydjEb{&)0IIEpUk)5D1wcLU|==Tpzj(iSBqV-;j?c z(!POIaQD|CRjEe_T*xKS_!Q>+ps^Q~Wu01bmC70@K9fJz2}VB5n{KJokp|JezAlie zl)jkZ)AF&U_9ou>!c%_exL#s>i#TDEZ@&WuotDR!U55nCSL=5oJ$nq}(XTTX25Kg- zCvcR;o0ouabK#g(*^SqzOqW&@05aP>EQQ!l1awH8U2V}ay@EEL(~8e`b^e~X;C%B+ zX6vi>py~a7GV1IPKXhUpgE|^3uWS`$=u2%a!z#2N7OZ|YK!pXUn1DmNyN<*t`d`!B zHCVC~UIj*UhgQfwkEb0w@eBP-mK|-I?R(NR8qJt&H^Wf?Lc_H@q&SIDuIh`y)BQ}K z#7Zw5O~Ql2xp(Dxid@=hD*rx|rXQ*P2jK~6KAVj zI;E&5pZx3PQzyiVx0dh%tBlym_}k$qV$+#H`LjJjB>+6F65fE|UZOdiKBNakCK02+ zz>xg6y;I2&04OnRe|!{;$&dvU`+M%2>B|Q~njfBbQJX}xBJsq9=f+Q^J*t3HzxPEs zcEsS2k`Oy=3D*x+EMh=XUIz&jK`kkD1}|!)rzhXIF)cPpKeU$%jhoibww)*MhouT4m>Uf^lbHI!$i(?+40{1w}1vw zG722M^Lo~;CF$@rquZn$VJPmr?guDsXBT$h4Uqx58Py08V13=>Iw!h2dc6K55Rye= zlJMTL2*ab-3JhEUjGk3D7-(4dgZoq-6}78tfj*f51UB(M+8A|)O?#2;EPHsXT`xPP14gW{sADohCcWT z!U|Qp6N)q${i#OI2S_lNy_uYT z5EBQPU6M|5{pL)Ua8*dQz=3okulc6%#1o(zWi+^nwzunl_+A6}*)A#5(|9u?;7>7v zLWZNOLBHF&KX^O*UKImnd^F_U`VTV-Z6UoJfB4O!By==14Wt2Vz|%Jki4z>n>2kjv zfp2iQb_1M}L>ieReuwSnNZCgWik5kyuc(LA?L5B*{1`bS_qE;z98 z<=fia>f8V`6#L^iPi6#OG(*I;x z^WpTo?TynDzkb^NSH|~`DB0~jv1Q#d$hGH%XMELUZimf>w@7XN6I}SJb2!&16Ziy| zuMHvm5xSQ4gT+Pv-;$8M4$n;hjF7_HXSB(@f-~Y_-}j?rGRRXyAOycQriy=qGsML{ zv84?JM51(O6kS|6OMb;m9daicjuyVrJ2(1*w_z7HhE)F3eolF~Z#O8b&zvLzmb$;;{tPQ^EBOBgHiBViR zqPx|}49X%8tMI<+DU{~5?!|ADHpqn3YWIWqrVgV3RCj;aRjmCs#i^xzeRLeYVPf5e zb&0ZM^NZ7|z)m)5Sd2V^Hh)>1VWc8P<|RpqV8&WE?};JB=(KE(=*NN&J}cxa$VL{V z_Dy@Ptlu{BTxGdB5z}FQAMgBJEn+6f4+O$NNcWPF;~G3VgQ8p<$G{N}okq|C)!^`* zVX$2~pL%M?071CU;+-?5@32op{e@|-9fC?mKrGG!2O*`eL>j0Gr4k{mH zkiGA`np}2LshGn~yIz(vp9Kb)r%gCpX$i3UH@O{5`jH&Ugb3;}8BZbSy@OHu{hM2E z$xXjDet9}?^+rq#R+D8YkOeF(Y&crZBeRKIJ)d_WbpF#f5lh z<9yd6KSf~yGA$PEJY54GHQ@Mf>e>of;legqd9gl)Q^*JDD~WD#a?yC=#|dv9bW84@ z>y^Olw+!pP4d$ma-NgX|J|qXJ3r%~0yc-?weNE6j?QK;6ywv=Djb`w-rr7RC1>8Zh z$NEz0_qc>=^-|?2cHfdebQw>tMoD7bNPG32;yPk2ftrP(1>10`8E&2 zE*F}`ysS65=`hskLf4<$*xa8L+HOx})&2(d-l?TaU~z~W<&%xhHF&<;^79d(-)&($ zlp0Q0NEiSf{8)5Fhjuaq^5-nL#+wps&c-|c@Z9Xl)J`$8zS8Ou+7=jTlJv#`mt%CM z0!p*6aX{%DaA}Y~6piQDqUzsi7;F^m$yKH%hfIO}ZL{mkO)i$xVj{G~!KGKWw4EMw zhQtxea;&}>VOQEK_n3?bh}fP=QIQ6Li0ZBko;<9rgT@4-8+-v<9LTJk8 zliTeFb8Uff+u-$`H>e*Wbxu|(Ht^sREyivo#)Z_vmK`DA7flY1xj?^M#_NwUp$ox@ z=fntt!TY}uu^5n?-7}5akIv3 zn>_rC$P5jJ|Czo^_2_TWu*l{EB6~YHLDpG(5UY*`)@A&5+{Ua@9K{zlFB%0=Fy1MY z*z}?Mcl`nD;oSo9*9IK&)7OKZTp(=YxPmROJmn8RzaFuYV$)^jOHC;4=rBboU~25E zE<0#)h&{}6?c!)HKJg;hK&pKgAP9CrB&;R8&c8SYp1E*>%r6f>W%Dmz`B3^r;@iq0 z@CUrD^?d&iO#J66{|3MNuLgPMk*C2dW8J~)enE+?JkQl2gYQSkyV7kZHU?vyT1+wD z3Ie_?=8q*eE8b+XJ-N8)9lV z5>!K51{Pd~m}~_?v@^>(zy&fBu&04M$}G|0jzvN|s`uZ0&SWQipPmH!JqRG4&d#5-dx5R>&IDC27<9$~& z(zDy~9&x8|L!b@hoC&i?h(Ptem&%Sb>$JUM9)s7#(Y!Nl8@idXgGxGU3$_Yxe&0pu zp0qSu)|tF1xV5x;Bz#kaRP{+Fp%HZF`LOskdvl`lwg@CX^^VZU{UdKD-8!^)ND`R8N)Ezba^ zbkkt`P5NYJM+1D+ie(SGxa?$GB(kI$5bIK?%=mkwLfLI1kBx;Ag{h>w>l{iUm7FaXqOW5HV z$Q~AYUV!G16=FEmZ=o13=#hm@^Ut|5tV}CkG-nMY_YX?_oJhJT>fSkg@ue-u+lgR& z_SVILW=aR2jdzNheX8<)^3uu%Pymi&Azw~km76^6Y^Mo4iT z$}2!3PN!vg`FF{r3m|mrT2^S8K*7r~8DP~*FvFWrFTbQ%YjSVOuL{U80+<&^)_XD= zne4KKn!Ylf9b{F<^1THo^k*70IjT*aAP{sKRnthE?_jL4ZaNA zl@u`tVE6!K9a(h`)#|u1?`Ei2ckVSop7?~;<+uv}z!>XF9XMT!F46R{_AO-gZqK#U z@t~(hlgM9}LpsNFe5rh}ug8iT$$R+m@atl}tCFpBEZXJ`PLP*-24qm3yP5}?{Q)uP zUfJ}!ig$kuy`Kn)il6x|h&LaG4DjIHmG1ErXJiE5s_fBt40bcoG=*}ZeR9CBMywn| zfW*e{B`{cBGn@~A??I71ZoSNNlYpwQ{E|~Zl+Z#lVdB#dlZ!WJYp^;BUrf;q>Q=D$ ziH{+DQHV}DDFl5sl+UidWz;MT$O(h3-z6S*$x&R8rtZvA&`o>kxfmI?X%|`(rm}p6 z8G}HynhTDG!9ta^Ag$2Y7^ARH=i(T>OsTXkk16%9IyE)}Um1t(ZrPa$<-dK(!!AsQ zR0CX8usG}!lUo1*5wRWhxyGtpH$CxV)$(l>hb9mDxekIPu#nav&RKl%GAxCpagj7R zxy;rZksLg?mMB#{gxY)rBzIu7QYIbJg#tTd;_u)6u)YB8*dOA|KL`!=5#JNNZiCf~c8DL-M_4D7Up zWDp@%4uH*G!Hh-RsYGMqfvbJ~|BOWy|5t{b+N8Vle@?l+@6*LIB zt+YqsPlP}Ax#50<%Wg%xe7w|wLQhtPfi$_;@sz*G3#O>gm0|Z0XUw!-S4e$#U-QT= z*0se`ldn>Ya8JQzRNY!e&Bfxh_=>QIF?YOTe zAzzE3)-E2v4-v;v1ZM#~RTfa2{O@4gvFcl0dl~{i^O)>c(Qq`5vgOUH`)2x;#cbzx z6QHstqk7LC*|-jAst3`WN&9zdgG=wE?D2?DW#X?r>tB0Z76!p`e)ZPu#mVbmdx`$= z1003%sdzn^cId-yF&O2MyV5;#E$$KM$W>=|5kWh{03??nn0hHrm;k(^IkXmtkc7L? z!LZBe5D|vj=JH6MAdBKc5Ap@2Wc2#-$8-`0^|zb(L74qb=kw3?;&!x^&c2d0MI^Fh zRl;a8>he*QWR^w3dNNPmDQC>RKlX?;$i6-7K#jaIV5~P2ng)ksaj|W)Elo3_R3_ma zDPlJT^d_*%xqI-FjM)YR6R4g7biz>liCiYzwrRJf4sT9lb|ayn$#DWm?;ySuh9R5? zj+q_?`egT^iDw>r9^{WVa@m^)hV}z0TdW zi!T+`6Ex`y4L^2_irH-7w$$MnD(z^%)kITbNMtTl^1p&tWw@r4Db0KKTFc;ZVd%m~ zMG`0M53*-f(^0Z*@aCKxn#yWQ>bIpU3PYd#zwe5-XnNB{*t{|`Qa`jQ_)<9+qyoxE zL@=Mkoq-;LUuS`ux+FI>eOk`$l}z;bg{z+$<_;uyN=9&TYvt-%RHg2u|eI7~!Jv$e2sh z_-TRn9`w_BqPw~2p6@)Vn50bkDb)*Ij5KpDEj4O>=a~ z3|WaWPHCF*qQzW;-DrW!YOnz2l~lSkrC5_AaTpT?)QFaeS|$JKb(djt*S)}>x-itB z*SVz%dzO3th>`WVdOdKS|L1~~q081{@NMa{N`1D5%Hy|{riTaC#?M#1VQ!EgO?g<( zWO*;1;&knmK^tCpV{ELvz)G}_-H>t)$nd!R6F*@b{mL5Lz~Js;*To8m!V;9*;*yRI zF82YF>+wh28)OSCqB@8%ww5lhPmNSubjo|b8d#XO4r#rujip+lgZu@@q|E!;mOe=; zpu5_^Oa$+V&1KJ_w^L{qa725%3eU882p~0ip&lm5yJ{g+_T&SwfC0RZ%PJC8*nmmr z(E5^DYcN_@r<%J66Xd9+O`}FKV1Gh`4yB%#A{y1|J*3N?Z}*2^3$T6{H>MsPObNMdk#JXD zwsq}x9v;|773;2$%p@l#H#wXoGd@&}0RMI$I$f^2sHHKZdwPQNm@qcQl_dyotWly|ysKXwz-a_ABM&Oqt)!-aA_FqeH~?S$w} zgXLl#7QVeecjkYDh+Y~r5Q&ncS~*~jKhiJ@_Up$bQcnK+r$(1|blC>>0h}eXZ}>PJ ziMGHTSFdi#D%!%J7x+E=XkkM^wY07-3|-OS$)jRkOFi$rwP}Ioy^uVuaSE`{f60|o z`VnD*iaO~+f67M1S37d{`^q3&s%SH`kCReCiZo}?a*}!wpceqrsD-9FyP9M zRF(U(WS-Y8E{4jAv1syd0WplbkaMLoF?8waxk9~?F0fjFb@H1gFlOrRDG%+RbpO3L zlz=Kv5q0BXDH&&N>S^=rU!TLl;yt9hEr!JOjt>ljbhpQ30G95yIKErk(M>nTCGh)g zkW5(~>^{9d0KSY)_qcY56!s94g!_?Pz%*I0GrYj#vOyOq5xFg0p>Q`=lo$;g zFHXv(y!{^^bg9el;gFEj`3pG8;BsDbfN$U-m*9sC3!BK_SKgy~wNKB~slZZ~Z#lDc zt|3h-{Dj) zo0CBpUJA?I-!TEEvzN*Xo7GH!MASl0i)x8Ka|DpSjQ%1=z9DZi6bL7PG1_?s*8P_N zstv9csGif`5dh~}M}bA4I2FP$GJDZMPG$-L_-g?j_U=5|IlunsU;ojPlEq5twUE!G zr_s%$LCzPuPJ;#s++0P=TjSf~NE~p-16vrHNMUlCrveDRkl7K$YVcV4t4?u!a-i7B zJni?&0m}8jVG3V!n8Lu^5CfOaaJXieIM)23|AYnp(#2HY{=XgI-jAY&GmpPdyymhi zPJ!E|-m(tLnC)Rer64O8!G-QXy2tc@W6G8%gqn_vgU$MrzijwpYdn45+Cm^9esPy{ z=FHODzKFb)!iT0#LQ7_sTMWesyNp`(f-|o=a-j%|gqQNYK4yR~js-OTq+MEE0vnEj z|4wbwDdo@Dzc8`YOs(iOMP=Cuy47Iy3(@7l*oNJ=_E-K@IQKj`B!CGZd18s(o(VEj zI%@-${e#0Pm6a>m3P808-zPHlqX|IjqLx;Jf0m~?{Bk0=Orx^T-{T>H)pzB8LyWU> zAac@GD~5hr1F1e>)Vn72G%h7#{8Hmlqq?v+Mn^P%dSf3&4N0*rk2+3lai zm~|KfU6bgBvyw>)WYzjec(}>a)I03Qo;+lAF0#1vO5!7GM#$-N|Bc*(w?5@NXeJk4 zo=m2LKT%$hFswWcu6OAND~I_MhA@ZU0GZ(y$~IKHDT0VNWeNd#K7hlLOl~TZX~Q;F zt~Y1XLvHU9(=mS@d}Q`R$*aMe#aWNzOC~m)&MQre{oxP7BXA7qBHG+v`c+E&mxiqj z_WZ5wV4WQ#MJ5M&C~t+?$Hw2>b+oLzI>lv&5^_|@=FZ_e{>mjgHAjhyaPtkN=i0QU zBrREeZ=q9E3@m36gVt>XA=T?X(wA~)boVHUCvl-B*L$GEw90`1v>xlopjGJ0oofC9*RZ zkw%I{6xt<5p~zB}DQk;;SIEBa+k57D-}mq5^Xc=r=e~cxbFOn;-(wk@@i#)wk}_dP zr9%+#1`j^94f*2bt%*t2d76x(Id^j$<#7?kkt16ea$|B-tmOXE`>QBx*bZIF@N;QN zZUAB0CElQ$$=MfiUuL?(_6D2&fKa)6Z`iXMhxle>huCVK6MxB}9>h~i_dCC5COC^e z7b;{$^C0kYs!>%H_&>w31Y~8un8QNR zrwR9ZjDP^FJ5n~rDc3&4qDFf>4NI_B2<+1y@UQXFu_W?`cVh2U6FxsHKa>sW`TOY* ztf-Fqj$7^%YMV-lQnX-Aq(zWxvI5}WU7hlc%XrC7XJobhX%Bst8Iodq3{AKn@2!Hr zg8`J3(-)|S>^_p&_eL_xK5`vMeG{+TD4Q;(d{N@0B|J-(o9H*FO1|3>)Es zX1=;Ou<#jqyScn3CMoe%9XQMT8>_n?64&pRXAalt#5OySaeCci&|IzF(+m5rL&E-@HTJrD zrzylm24isPfqz(YpN-~sWpNs5J={|-^rp$H?AU?udKj$TWYZr#@S16J^J$Fw?5v=!DjvUo2<`}kApiwM^u2EHSk}tJ`0>i zn77xV+$-uX&5}7eh7u4NqE@Bl)`T4dYRS#iXn+xL)tTkk2GF1?HGa7`IQ(D+lT(ig z1=Wy^s1G5I+&eqm|NCeJ!cK&|@YZgFR@U9jKxm=E;?7$UHG1%A1kodu;jqZ1rwx1= z0+n@`z))NNQhf8w_~~i#Lk%Gq2*jai`Tx|m+c9T2jlao9)#oNdk$!pGp+H5=NF6K6 z!P^804P7!cw^iL|);C)dP1Y{Kgqqj#E;qiC3j#=%LI#p@ZZJpCI#Hk2Q}lQ*5sLHv zMVF`eBgS^dFR)JPe4twPD>3R^x0J4J_$+MouG&7;iLIx?EB{v~{#tvA6OF(_z|Evx zvZOpH?u0OmdNEex->3%ZJ$sb=47tghOzN)+tA-RqZ~Ps+o#Mv&_Y( ziXqg?_I7qwOPtoLi6~h-Igj}W2wtZGw5%K+%zrJdRSGG{uG5(+V5xv@ak8uc$a8hcctesozW>V`-|AKG! z2qL~@|4_j6(pSt%r^IToTVtR!-U1Vo#gfeBs0 znrGv=FC|M6sMpei!8jLAHU{6#o;O$iVLXC(mg{bcZ))2sa9sqU|Cg2hV__#Ls&S?N zKq2!rl`i8}CUINXiSasMu;dNs%XF7WG>pKe6!eSn1k90NCgg!OX3`#D zNkLu)Bg^WikJ@EOa>u{b0_H$LjLX({8ZXwF@?C&~NcpR0q0l#yFnO?g)Kq1%<6W+w zizP*;Y|9_6X1gBUio(rpHz&hM5`V;g#%Yl1%5CJ|+nGkN8sTe;<3VhD=e6jENjwl@ z;~@z7!Q1NjE%+1~`Ifj>>~G*Kxz4rBy8+=h-{nj<9B~_gG}5IxvRongDik5UCre7O ziP((5aAh!VYY!eC1C;9XtMH^C?fvX2x(7GN!@3fy-@Qg*`%G@4Y z)PBTqeDnQi^>D-}h8l&_>xc<9hWWXP9l(a{EIFGWMrD_(fq~sMVYM6=#kDo7SHi&!&^$g zESO`B?StNKPW+ZI>`0KN&Tl;Dw?qF&b`;C!zZ$<^F65e!lZ1!%vH9@Bm}~=6MBdw_ zQ$kj7-@Or>o`2QtZB&EFTlqIVk(nfMe|gpjhqu+<7+xw32m4j*2mQ;zM|CPMY@m#% z8tb68E_iWXSwthmwo`M4tW~QoL{rNh?)K3sDgLjU?hBPPuO+Ay<|IDGEBWee*+ZJp~Ad?xTk%H{YB?? zoNG$|`-%vij4J8n4FI<^tD3KNBq&nn@Bg^Ol(s`VbA;-uQmvFHht`I~+s0i+6?{WZ z0tVzLLcV)7Mc~#ZupunQI=0dCd`1QIDm8S)?9j61tr8=FQdUlWNA1@xRNEh`TuhSs z(;yT!skpPagZpxQZB%eAd|7H2z8b&x9EeD*ub2McQ^)nB6Z$Bc@ZY1Uv)D{>MBECJ z(Y*jg9iMAj`y9)6YB(CWaGyXmuJRw)uGbKO{d1~5vC(Hwi0}D7*)^|qW3m$vFxKKt08S*z$UE9<%z7Hj?zig-I-)HHci$nwO(+e(c5 zcUWz7HDND!2X>T46~2(7o?BZ1voIA)2(219<)9E*v_rw%j`plAqZ-rSg>afByB~lL z_lvptxDWTmcmo{iYU@2v$4&@YCKjHw1b@m6XZAei>Spk__sge*Mfo)pNVESI! zBVfmTv58&&Hyo+{Dahzz&b3P>FwRFP#|cX&bAb=z_EKdPi+h`@Ba`XI%o65i$f@B58cXN4 z)N=w&eo5A5irjNi;-vMFbaUOv%pXXLu9S=cXwd^<@_Nn(teUDF zb3t2>H*ox0;+3Z#B_Z4i8ndG+W#PTk;L-bmU@h<_RNlxDTXy%cJY%GkJER!-ep9CG zbvMGX%Ti~3O_Nv|WzfP~g|#5CPu$oc&>CNFcl9a#X0Prs9{fH3PoyiCVvt@fF6|!A zfNlZhohA6Zb%o`Lpt=o!ZNs>?@^oLs4upH*A2I-{8GyQYK-Tb-29uZmvq&rs<~d9Y zTizd?O&!6(UqEOIN2r}I`{`s%D}2oDrz~_1o5ilTUchuUDWTsmMRS-KSDKPB@7$j&3!fUJrmVciNK2yv+6(R3zBVC8Nd5a z0OmF+xA&6HJ@zK!4;(}?gRypaX2$rIhWa>+N==+Li+nrHYa zNNRG~^34T5f+@0cuw;3Siyw@ds`CCvP}X z_`p~+^sr07P+3;2H!TK^JjEj-fv-3~OpOb6>-7E-fUm0rA zyvwVvk3HokjlsfBo7mK^mAq1}Hk99s%;|l;@h@}OBmoAXR1BFLKG)pquRgE|#1N>S zn(%)K{!J89`O9j5+_^%(C-#WEhj+oK{Hu#il<-$kNbLsLuLxP2>zo^&O(B7VvvyFZ z{FV6;TX^w`C8a(>SPh%y^YPiW5+p;Ri!}XrwSe14pr=|#m3&p>72C=rrf(3zrvGBzBd z=uf6Ponqtwu+NlOQbfZ&N>(gb4bpULdWCRlZk{C(WY9Myj6kOaRf$6I`tG`Zz{-Wx zg@ ziBSK>mr?tGmx$Mh3kSkWXSO}Me_Wn_E0~TPc+*=2&<^e+!-;`?Ct+yWrJF^i8z$zp z3RE3&eVSU1Se?f$olp8k;L9KVFd16V$$ z_rR6t0t0i4z=eQiR-;sR6;ff5J8{zOJO$83_Tts>U&MFe|Wx`nioRQ~+ zz&j=qW#sdL$y1NYgTxRK&>qnB zPq%+y?u6J*t?!-?YU!OU`N*bHC)towjRctg+t1z2!lLS^!Z;E-)aQXd3(;sk&?pjvHhTN-j1{M?{AT-4ei(7yJ{)n~%HgBsX&7``jkkw${4{*dglUnY zHx{TyMDdrWK!`imHyZ!5Uu>@vbHto=o^Wl&S0iVvrcU^aq#$(!Ri!EZfVJBXD)Oev zX**^SmVEIh)PUaueScWKZ&a%u&C~vCQ9hb%*PdCx-1%k+WP^QF`nNf978R?5(b}4e zEzh^+!IL`qYLD`cH9^QqCO~f&eEx*@7R@F}}g^;3S68sgIAC|71VGWOkEDb;MTec*fjLEjcD5`Xr zA@`2Ko9v6CE!b7u6LwlK|2)ybKVwa3oC*$url@vJc6qcgNy_;Ym>2hk%uj%J%X`G5 z=|lBcGQw%*IJ^Oe?P&Q}r-Ri1fUx?@2M3BK_Uc^Ymi+X*ThGnj)%)LTQC{#|_*Zk8 zJPVPZE2lmI7usHHJDGjB$wA0!JzvTeJ;K+8w2}##585~HCS4|EHOz~xV(@H3JBSoyV|xQB&H{Kh_e@4WgUv684tv@4_ZMe|@oisNa$TazL1+y?Xq! zgN+Ag&`5nHM(mWZ&C2m1V&uarEW520@5suM=YMYCJNFIr(3hq< zWos}{+U`J7w}*4*@g1c+$OjgLhBB)7XR@&H9L@d{lAI{5dSWUx4AE%IM4iH`OaH?z_!d& zhY<~b5;Jjg4{VFLS}ZJ~JY6HP<_#TT>Cziw|Mv7Y+>%X>#oukY4WSX9ZC6jgU}8n= z0E%W{_hXiPs67g(K;P6HKI0&{*eC&e0v=*B6eDfQw#}{R-cY!(D{k@}@6i%Es7rBV z3J(PF@Ve&OBN$p9S>g|0XiLGqIHoC~pkjd7Rh>!xzf|3$}J-08S@|4C~O_Ksf zn|&t_o;sX93ufjJtHX}N2{Q*IS+hU^Bh@U$(4<1ar7s_I?vxlLZYP2!*CbDUAM`@* zGK$Hh43tcRsQSbjeTkCfOZX~*0W2AWQLOXJf1*nlT8J+;4^5N7+9@KR)oGid#5P0D zF)v?m9&!_M7l3w25mT$P-1<{@qx(u$R{G|GMw-=mZdJb5)%%okEyvt?H_dzxFS^KH z+l=Etm#CefKpP{@B&B zd=~c}y%+_@16U360w+%+RQYTvhoSj<7dm)D%q3lsNI?~n-$&4of>rJNX`v7+f!1WR z7q-tY#t+Jcl)q2Lw+Q|P9;qOa9g0_PJXMCSvN3hFZ>J1|qHpb^oy*kR?=>UvbcYB-fg*(81|{ z@ebYAL%MXvyA7kh(PVc-wA(Rh8zUIB)0dopQFUpC42Xy5O%*_-$x!D@4qdzLKfXLx z*`>(H=D?n0g-0KIUFISDopYm<(7kjesj%|_-kRGYUQQuJ-F6LK3e1_I@GOHwfs3i& zwZ(BEvo0P^*}>G7BU0ovugO`QMrm}umOPO{Mft9LT1{CntkED+DkU+A8_1zlfsE6J z@0`${06e8cz(SN>9vG^ztz1+(0SiZz7$yjL2fu4xe) z*l6y7Ks%=EG=L8@_9I60j`2i+lxBiZeN?c&uJ7K0X2PRjeiUsvRBncBDdGGUSy#6c zu#7rC+Ki-%1n(Wi5GK9vtgNS_Jc7)g8!x0t2hLrDcu|po(4TCgG8H{bE@^#bTYQs; zP~&+nk;i*bzlt8PEk8i$X8U%cerYNIURMHkfsG#sfkGs7p!x+nrYQU4Eq~Fq`jv~m z1l(qPU`&}T@YRuwsjWcRIG!^xd^J8IN=rG$1T9~IC9S4O;p}Lp!7pFbix+jD7kijB zM)Ov|vg~y9&EEKtn*1A#s-GurHU`~osf007%`W^|m;>D!6S0|9ihLNi3s!^}8?#cUpcmY8c@UT( z@ZRjP7VvR-cnAf5o~&5SO>d%zJIK$S!0Q}hmC5rX$+4r#`a|0|*BU!E z$T5HD!lO%qkfO+iXskXm>%|FRTj6$P{dk9E+ZMx-OH`2PG2l!y{bs!7Ww#8H0wlni z&-lIuA=0VpJ0@?3b+tP#l^vT@82I$2I=NO7qcis9M@?`sayFuG&ZrLa)VE5QW4gbd zWWAVO2&a19BL9o@t>CgFfD-s2tIIc5z5;Xj!l{FlwhmeFPUR_fy7L#N>*f%m#R&0_B3eyP*oQI^U5ywD{9 zA#BBvjUF?Er={$32bS!mZ|6fUd^$&d|Fg0ZYyaN|QxwPRa3WP~dyZ|1d2O3-CK|u^ zM`7%+w3a0W{;>FqVn^-JaoBmCvP@$btH7&mVE*}=ZQ_F$TV5+*H>D9!81^^881NJ3 zUbn8`EK%d^kDwMXZ>I|X^kb_$&HJOxm@%4^hjoqGlKXyHx%&<`AvOFL9F;ehc9UsO zce{gPUb0xSw7JuMlX;irGsq9#lnNN_Zq4OOU0N7KJs6ZW+1xbxDX9Q$uNA6Or_1dP zd-&;50F3$(_^N)hCYjgDE^$th=d{Sf_Cn>|rf*LivKAb?Lg_Wbk#c$sqgAz8`H)(> z3Hiodho1PT#+f4T{ir5ZkX zTd!^>le+V{UP`e&TvvUq%_Eu{z-h5uxLh;?P)H%tOOE{?9!>E6R^g&UGxN=wC-&=J zs57sX`+8b`-tCZ;Z6Q6X1u01YW1x1pn4&MWmQ{xJHI?mm2UsObd?FXqV4D+)SovIs zEt*i6GODESTETEWJlB8`c=;~E@1bm5IswCaZ2G|cCmZ*A+yWZzlpvrbR|frAK#s++ zMp_`-K82?lhtLGvmFQZ_Xc{YwN?YS4M)_dfclq~4yd1~aR3VsFlm{Hwf_t}yy#5v5 z(Sr#P$F*7RBK!aM0{E-D7rxTus~g^A_gyV$$U48>++<_6D2tEW79n2HsRXg@Z!EixUZIJ!-F8rY5 zVYf#A;xA_Cj|kRKOiiUHBbNzV(SB}xkO&A4{lBbDS#Myo`*E{MdPa6jbH!m#$Nw4; zO*_IT8|!ChBuKXafa5iv2b@SnxF(z@@> z#z))(gxTam=Wpwh8r7{#gwyvqEWNGWQ&Qt!+{kq{{nN42?S)sjAQ$@nzLH~$ht9R@ z0a+s8jP=2igj`ps!sM(Lt4>94B$85;+dl0oH5id=O-d0C>o5NcYV5;sdaeqs%M6RC zI5XxSe=c#yAjw}yCby2Cj5ZKs+e&^ceAtnNg!3Z&=U{jR@^R6}si-HrUU~W9*x(dl zj1wGwEvXly+T5ba0D|NP+cbnn;l|1;!Co8U6phL5RoLPIgIsXH4BzHNS?5g4NJH=& zZo*RM-hU%n!(4wN+T!x_H2OLr|Juuv1hZ2ff5phxf;^0_LVK*VR{huj>&%39m{ec) zyYhfp>mx(w{bLk`RrTdHANdk){mE5KLD6P@KkpgP=@KrTt+-78EU1RGGcJv^vc(Ls z2pf&NqeYx}>qbrSwK~HwDo!*jaPnRn-X4Wjn_mCf>?EX8D?2)N-f&kP8H&L>r$vNR z0Th9n8;nPq;k3b?UO0>4by*I?yZ)@t^BFu!1`7- zu|sFNDTfMsdEfNaOWwu%NR;>&eT1k|ik(_aB%?*5mZM=`Tm)S?CL01CCLeFrgmdqS zT({p^I&;Xn%n&L;jk{&cD>ZqA!?j;O-NKQ@-ZA}$S^f+d{zeObX|&fw4_}UN23YQF z`f_JOm3Yxdo$>uwYh~0Cbt&o8m4UJ)^;dkkh9+-R;qT7Ui8e`REeXMqd@A!q_V!K0 zgB2o#S6j6181G1mMb2KNzRVU(PDj)QXoTwW?4D^Nm-O8N&BD;IKp+bEg zo5~R?IxQrSj!MHX{(qo0j_hmHnZ65{jvn<;YN-hb7V#S zJ{YgsKfVPq%OxIgLLOJ8lzr7m+kg<0sqXrM>-1yYTU8Rf^qh<@T_)3-96d)_MoMZ? zS~%Fp(+PQN^K2@8V+7PP-n4a5u9*2X#eu?bA!bC zn1rs6curOQA{%c2^=&8V|Isn&Z3g-apw6?`|MW+o8-J|;HTG51x?W7wnGej2%1@~0 zO=Ox+XDknX*i9fED+$@jO2o`ufanxm;HiO-|DU&Y82KO0p$4=rlijIED|+D@5ie*h zqmOhZ-dsw?l9iFqqo-9{OVgG54&;`aolZjKZL4P z_PMYeu>)B4CbXq=Og2D~e_j2xw&pILesOdAKm?r~8&P`ZovboY^d7%$J{pe9=$%sABLr%fT07X|*b+cJ z3@I{51nnAmMB>diicg5<2)e_thQxrovrf!*dvJfm{aiIwrC z<~EEk?YF#@)i~8D86!=-enT4QI5px5u*0C1pLcWFIXDjVnWdlwnSix1iSN5l75{{P zx}^-9n((Is2pc#jJW7#rT$ed*1CK``BxJ??a4WtGQ!3Nh_6`A zp;u0@GC~nD%z6k>h1RAiz>brqQfejHv|vlu6Rgv&EMsUK?zXRbSpEK1A1LUXfU|xM z)Aq|6JEcSCs2h|AXI*ltd(=K0W<@(ik=>@B?D4TqwbvQLDeTy zHxI0dn}-3K?Pk6xjkVoVlO6dU#mCYnIFM^15lbm)fZog_9=(BzKu7@l^YFw{@Q_@{ zp^bl(AqOpDRt@XCeqOEWDQgO+5D#3yZ%4Vcgq({DE)7+50&uxxQ; zrdNexN~my5ZhnARc3pLKdv~N9l{dEVA3(~$w(C~A)MNjm6EFTVIA}_Hxw58^F?YH( zU87k1!WTgHf7*0Di}tj-y8|VjEsSTC%e5x!hVl-N4{o=k(GX6ZHydi*4=>3k;7BKa z0s69XrE9x75e@@+ydS zIboj~`ubA53!mMPG=Tz{t0X6A^KWf#j{1ZkCFKfWsVBpxgwerWUbOg%k3fNZO~4^m z_Az|)tpfuKi3F$qh_-QS%@S-!{(5)tX9H*&e)TS+y3lF@etTS?KqoO5`0?Oc^rH~mQ}v(JDt#gRkaZ^f|D-o zT*^xZHGBDNa14fx%p{4*LaLz4OBK&U5U>4r4pc%LSoUPW_2aj3%LrA8!RQF+&=lF3 z?1eIgRS~NTz#drI)z^IItBq!E&#l1-m9S(UA*-pfq<)OY`Zl*)ZHrf*$Ja>>H?F?}OdPP<9=&-{<@7v5#XoSX?O`(?o zAtXEf_Qvn)z66L`PiwFY6y6V~K9Z(~NUe<&2xk4*`-k0+umk<+!e-BEq!&C7a+*Y@ z067HY&4h}-ch8Nzl&6YRCY^Wro~W{N#xY5J?fwj#7MQnwZvqmBlI70x+daf``3u-F zf4&|&^oGxFn{Mz(il&jJ7Favu)F2jWwl(L&-{n3iH2iIv3`*2fU&1@SF1(Kw?2EPO z{W6IoHH=z3Ni7e@-Xl`O?jHE6cR5WcD12a2j#|W{n5M*F`g4?lioUiL0`rk}H>{6J zOjBiw?S~hR9I__N9NrILiTeQT3=V{d8h{?Xn?Lg49uPO&p@xnBJ3yP+v;T2zc&WG>7IB)DZFe~aRW`T^fH@m@bB`^#@CHU{yLl|lViv14aPBcmI6 z)0(z2!GbaE=RnVq?LHJ8Ie~m6I!$IgdU7dHg_4*L+5J0uoj1;Sr3np1bh(9Cyz39@ zgz`RHrG#F#;TfmvyA}nK)j|(vp7`a;@|DwQ(RC9=$n>%3o3z?nS=UHrQ1=$(U3U>{ zJ#!36**6B}+lB7i?I?|#VSgem_RLKtIUZh`8i+nA$4i#ALwB-H+ElG0(R}Dnwcf{Q zgiSU|{ljVoqk{bE)KX3fcKJVE+;R$;k*3DV zKZO@%=i-_2C%O6U=R!@t3MyrVsSC`KEtAf{?C2dW<5hEK25e2DL_K>cH?bN*HE0n? zOe0cJDE0paRSnfn<&syxV0CRMawzJ%{W|bTUJ$qXonI(>d_K_{!GarmzI}{>5qYkX z*f39h{yYb4f@LrcD(-)6YRP*VYO{aNRYFA{3pJBQXoMkF8!*#bvp;&LW=HrU@HYgs zmR=rSBDi@dasZN|?in|Vvn2nVpmP4;quVXrSfX@@K?@^@4JrxcI{}{y*QV3|!G=>z zdGYgd9lS-CPD(DWad8^wwZ1BrKTmNvmU>mf@ykN-;vtcH z!uRZvZ(i_ds3QbolJNrZiwNPs z5gH^%#_?Ahm68&$C>d30A;{U(BaZOGe#+#p2Ns%b%YZoVVN`{2jE%}+DWf*-r~Ko* zI2));Bm<=Fpi`)USN~xnr!IBE(?t#{~brd zG=5b!(v`o(m!D6ab)G>#kZSX@Wt|cSB$Q75rrT^o={`h?ICWFDsIj^Qwjx3GA z%Jptj172{~hlH$!lDi|oO2|Q9)inS3K^-<#4cvJKUZ7O|Of3162>wwHSNOS~GE_uQ z1U-R6NTVRLu(i=QK})8u(eIa(G?8L_^`iX?LsLZm?`^KXbgo~MF?0txaH3?VkY%Ns zR+&}H-s8a6jC>Xadl4-0`o4sy()U2v*5l%JPk$ii44X63N~LFnf~^qmruZv@j4*HH zz~JOmKJzB?ztQFOEZAf85SkhRw0;^lA+AyPeGjlAXS=6#D};NNcfnA~upNIw&#f;! z#a3f$+>Aw`qpIIEf<))}d1EDpF)z0Dv(UOF4zrh@*#8@V5^dPLYKl}}IXzi6>H8u-Pag_W7A^;3PMx?83LWS<;P&aML%fXRXB1wS2mkDpt@eBS zwLJK7Wl7>N5o3>WE)K?$cXIKLQ~@vi#N+9Ojj!9Vi4niV{0Ps|YH}bZ_7JNks!G9! zIA}NACDY~zQO@!E`R$k0pYJ^>G1`O`{9-)M1k z&$lifA$cOz)>(=-#+Ezanl|dl3#Ai_F4+@_nP^m7Ws1{rVsA|1(gX?dELv(|qR>07-6I7Y|`EaH<45z*P%2 z9BDX#=Q`o~j#~I_f)L=1)SSTBplqaE3WZ2w4K8}=kM40KcEp^Zj)bL_O&mM&GodCH z(967ZQESRJZoH%`V(`)@V^Jhik4GP};`{Srt@WRv}gM-6)s~aSPdlvZKl0 zy?NtCxJUTHv8yEs*n0x4J149OU3vKbXnL2O&_flN5#z1()~#5mY%I3cUNrfAeXO!a zW54C6haze)B=F0vzV}F>ieEpr#OY5&s(VP$*~rsk>fj@)YB&N5B|i__s3;+++I%Pw zu*EgghXXa+j1` zDRms29V!taW|^O)%#fIp3XQMBEmVI`#^F6cX++{7DfXD>svHbXQ=a5F!RURY<2Smj z>figLsXJM(QG3_Ua5$iIO8@?l9ILyR^QUHC6@rho=AQSjwgKEOsYZlKzdO4{Js@mw zby4ZMjnmEJPBz8xqqKy1v}q<&))HV<03?<=T-R$q}=%RmoZIO|R1X zW&cYk$wh?ygmPeqcHdBoz5PE?jbDN!#p;WYFX&Bk@G8L`dGj(~-QZ+L$sQGwBxsHk z{gD}tfDKWuSOh&2>Wr}~4lB%&_4axejt2%trZL&Tvb%^O z(*)R$3Y!bXl46<+D;U@cm!vkVZ+Q;6*@-s)*yC(8M0USa zqLvpZ9JQu!!1kL6dJJqM+@S|~p{;iRU#?d7X6xo=Xv{kADV>{7g>Gk@3Vk_fw&UeX zC;5xmn6{mlq?4aVu#+F9jU?x-q*rI_7T&3UQLv)Yk~#8&oQcv8rQjd%k6njk3I+-_J6lvTWC=qW@W6!lr4aN319q33Y;0hwV z=)_SUt}cxWI|VO3@G1s=Q+pA<3p^b-E5FiR>H&4<6OK_t+vK?H80Yh8G0 zA;b^6%_cmxjlT_n*4y&#KcF1$GEHR;36lduQ0wHKh|04@M_x*~c4EVxNM_g@>!E6w zI;!C}aK{VBm}PLKspxC|{xKYSQZfFZ{x&el|{lMmHo zEE@T7n|^-(Jy4oKUE6rE8j-DW+e)i{Enyzxnf-#Bn)?-t>5}+OtUN9GX8l`hSxMD{ zW;fO&YX0y-=}KhV*z9bK7S>7Y#8na8`#hI>%*)}}5aiC7a{sF9bDvwL#Z#INBXA`R zPVUSdIIA0+#uv-Bb-Kma`*T7AGqJ+dT8r@AN~{MS#|&iz$uQl=cf6cPWhWIi=t}yj zrs%hf-sVE&{SH(Mf|XK=Ds=sp=^#RMLoEl{41sO=kn;AZ4T<*bt`QRdG!INWj2_Hj zB9lkwg6yg76~sz3>ALQLZL9u6!e)Pqt*$6+Xir^SC-3ccuzK{&HPIlZk@WaGQTmHv zmUE(bM_FUIz46D4E5{tTD+&dUPYMqptve+4BWNDl(qYKohaETw!@r68T;$au%c%!- zLCXh=P#K-G)4ANyPKZ*I>H;gDAzG}HazRZtpb@p0`I5m00sf;e!sg!ZqDmSD-`#>4J zLs3V{U8FJ8p4OtM^TD3hsCwor)Z-rh-|HQA|awjLH(It;9%IKjoj1!Xx zuU(F$RJuLw*275dTNxos%nh=>5lVqf>b)=az2GGJVsSf>`cL>CSyEx(W8e|yDTKa% zZzLzdWjCW4;r3-r_};;z>Ep%r#t)E^F6GbFe-ov{^v!AZG`W7G1tDiT-;}%38uif3S3&$9!Du&|tk8_U?y5To32wE(s^9CQpA(_)t8*e0U8W znenvc$EYB}dG73{O)bwd`owvw7w0<~(0U^7!Fgf618075;=iSb$FD?Wp}){uBT9!g z);E?HMG-%~ml=L~e%5XHR$Ty+##_H*fr^|GSdE}Pt7nrA8?I{GAZHZ%zdq=*h*6!W zNF~vjQC#@(D}_D@nql;-+^20srf!9|-B5eMA^U2}^f!_6nZtcAqSC6hMAq5ib`XJ# zQd1wZa+VXp9g(bMCufI-!7p{NdmOdC(5cpiyVk`P=v0Q4-zJJ@7x{A9=tZ62s?+bF z^jmT1!{wZBw{Z^I$gh*horO12?ZD*dVOECC@qBbmj0b&mP*{zopO|9L)F}H{dFEX( z(mUFEbFwAh_m|#_#mz5jd$tGJg=1^Bjh3!Pw2{y%YY_+?alF{Hdl=5jja$(q+}kx` zhXUo~RzEMPBt6^`XB8Jj2sft4Ok++XX`UWm7ZhLc{_{^On?=N{b%O;sCg1e@PX5e6 z!YObP?mpPznjHk)<%LB||2zNX+8k-D85_N=$(8oOy7(v!Pq~J`weg>|mMc7>83}ti zi|lFjZ17wHPQsyqAfM0WFnQ-ojJ1_vti}qS4x@K@Hnls$4`+@fAdX5NQz;ePXI@6% z8FF)-T1om#atM-}Y(jDHD+1S~WQpS|{Bg})dn1fmz?maUvc{24|sO#j-g$OI0 zg3m|K>$I_{&zC2O*- zE%H8sJ!+|u?P`-c0F7gAZJrXr$!qQvY4AYdt3R7E&)2;$#T}zGBPiN+Ix>MeZk^HR zI7q0)II$)7M5f6#-mRp4i!H<~e(FVa(&Dbj`7juw(5E+(R+NkvnebXD>5!!795fxv z=mr-M_#GlRl_JlT2pXb{uH=KPmy}gJwNy#WdbfEV`;!Z%ZWLrDoq5l?&qo45=t&8j z!CXSnn;e?Z8|14K$54d(Grw-CNj$Y@{y3_Oc5J6u+9e+-eChN5UVxJm!sNFWZ`S`l z<8AC&>k?CsvDqA`k*F;0xxvfG+F4uTxRT9D5O;Qg_o;FJ<2MvX2{iY4sZTlTk{;h* z68E_kD)G~4NHzEcL}ub<+0=k>d?P@H!8c{J7`l}kh0xd6>}iu>AeMM9HB^LKOCDQf z!~W`tyg?F+*B25Esmw{L)zWm|T{Wskx1TP#d1;+&C=VUpUs#!3-TaZ)y2syBKS6}{!*i(J;P9i0F zqT|_;*72AEooURgBgTd{PvX82rLPaD2fSkgsY2qdy>Va5YZP8ltJK7P(9)QI9yQM$WC={?oY`sVhG0qZ#1smwJ(lQc+>gtSeMuN z+7@(8p42^iV>ark*7x_Fy;mdHb4F~2jH83FxI^YA;KxFe0+!4O=fpLeGS^lXE7?Nk ze;*U>W0V-nADcpo|+R1j(*`AF`*C8!xC}gpb z4f#pylgA9tV;LOwD9V#CVT-r;%+sjaa|gC5)JE)`Tk^096P_r zy<;EAxbXvHIRYQ5pIBS@A_3nNO1Iodne`+U&`&zoDqR~hRz0$GFm)RmefTq_KY{xX zJUa|W2)p7#tMI#e%Mtgzc|@u6chXB+b=At~nF&9oq^nCz9%oih348q+LbVFpf3+Ea zFsz@#;knM5#9apS$NtzPU4id(5fhI%Q8<5Kp;SXEp84~fOL04r-p4Z;Z*Q!E82Pxw z9<4YNS*SOzfH?Ru0bj1pS%$6IJS54*ltkt%_tlF>Xm|A!uf6#rBF#A7 z>da1{&aGY0`PdTw>`sSzDG!OaY*=l-Sf+mBr0jl#qm9bRZq~Wq@*)mT-5bB(T;!*E za5G-IrNm^!ct>=$C*b&^J9Ca zG4VgQQO6VX&3A=dtKH^ranKQ)n=hoxF)3cti??1US2|0C!ezFmt>WG)?rc;!jQ_$` z6izrpXQD0=zsXs&#Fz8@`hIs<7*{;;z4tPFUF(ZFo#N%|I_ik0)!Up3?kKr81;)9S z(b0{uMnxI+k_s(iU$A4BgSBhpclU5nRzj9k4S%sShm?ev%01yfz6Gn+KNF2BET1?t zfY>J(tyn93U{_yt^z49ILL}o?xI-wq=bkQd*K>G=?c0R7g`;V&;6A22X@WEAN^_vA zakL^-Rjj)!cwcljox`W`;N|VtGrG3F&^JFTg3A@DH)!mvMRNL|S;AO(m|)GQ#5Buw zGInY84$kVk=g$8g<4UO`pGdSlZGB`(1*(7#e~$T%x3BG9#r!m;TsnfL?@_4iPd+{f zSUzm}lgfvVG$l4awp20yRNV*m#!kN+5N$8T@zIR!zjXx_XoZ111Ewd_|D5&?mS!M? z%dk;NVHxjtyV$l3BXMkzu{M%#;*q#a;>kv0mPE*@(T8S|R~9ix57b0X)?N`&d#|6U zTO=sSmWaV`$8sg1;AYiw=qtWJ^rBUHweER?d3YE-(h@A(t^+NSK9&oAB6E(ac4|zG zP2Z%RGilEBV7lorDTuHQ;nbF`_|l!8AYS^KKi3%&g@x;!g}})h{fKHSHDe8 z``8Y95v@Nas7kHavYSx0@#)cItjEdLW{at=-&HE#PIMvA&${7#+kF0LgZ#6e;22oI ziEpmCil=aVp-`RW0m9#Y1@$il4IKn{Cc)RF#qDlRt?18#lSAm5(f5dH;r2uHm~zn)RPq zuLDQ#+Mej0_Oa1Em|uU-Nr+o`bkCmYV~xU!s>Cgi z*K0~MBsq}h5Rzuyyo0_`OH%%e!6?3|&aH}TfvGuD%Q+w5BN*~<;U*PyUSCprq%U-) zTN^<&t5GcpoLXDh{4|9yHl9|y{urt>eO-_H&Z+!mbUwtZ8XO-Mx2C1{+WD2mX_Df; zQdfUAazpjA3&w?Gxf67jaZae0O# zx{UsXlN_*478}!q=H#t(7(H(-;(e(svg@$2h6wLO5q}WDuapfx%K7z@qOK9nsyQ;x zF|lFZeD?P`xg$nd2_3!vomm>|pBvx5+AiVVWz%;$KoIIpJKw>#zN>n|hgO5&a8*DP z8bVL}aU~(7mQCHhtUgDQ#nQgX8&giJkxJZ8?v2iFQZ@75tpAjP{PIY&<4mST$?o6SNA zH^|FN@@&mI*WbQc4;&977$VH>%G=qPIhB?FaPIxe(%>li2J^-^E}RD zIqzrTP_x4?3i;x}4bX-F6^_d^!GSYM#{QI_LbT01e&{rX2+im=5r7^fh z+SJLvy<2)~k*0%Iya0661_5>g4!LvMPyLXNQL5y!jQjLB@dWwq7gaKy(&R^BdNpZ* zSJ#j#4>z^HB_@Y}ALAAJR${|RwVJ3h!H}dsdQcg+ct`&spDb?He&T)2CGtCf{TE%d zM;~RCk!2cS8$E*+NE6l4fW1^B7jmUUvI&~UnNYoBDXQF$qjxfHKWOQw@;4_OmXxpR z^?G05DQH8bqD!;>`n>56?M-=UT;J{;#sHEb1*-izE=XH_c=6agvXENXh_Ys`zmOjO zcs-@*j`a-*NvEa1KRGh$Sb%&w4{2zKqI4`DbZJULLjf5MSCzsa=A6-i;peDIswwKV zl?>;<8ie%M6ME9Dqoh^_^BvTRGwI{v%-;93LF#sWeUVFBB|j@UZc{vv=Rt#WrX? zqu6r~h>NHeRD^r`wI&4`_nj zG_~qvy`unBog4gab7NC6;Id5o8%}oQ#_9{+fT4QLKdUw)|5j8D!vHUGYVe@wSFmpE zzTp!U*T!Z+;;Mzwh*`V*%5 zLOE=M{@TWe{rl4<#wNvOv8K=*@8R*D!f-^3RFCUyZJ+cy{pM9_@etdy&5Xlp`?Z& z^#V@{wn)RwB=q5v`HFMvk@8{5KU>+nrtsVp=!XD&Fp!y!7>6@XWF2xj`X`sQn9K!Y$VW8J^DZgfEx*46yi z)-7Z69%~;Uu5PpnKw>YZ%V+FPFd4S4;iv4-m9{H4@kNajkX4?Zb8FBD+Ua&%qDLmA zW|)AAB1zoa6*>#(47F%7hg0F4H1a8xVYMJrcz%akL8zz2L@@GJblzadGz#{Sve)oS zfzyh@8fXFRT4ec0o>g}p$^Q;fS@ru(qI_%$-)f{O45w;@@NUPb_RCG|Jcxb7^$f#o zLoIi!UMY-9%P&Cp=v3O$l&_kMqU)jHVXH>>(%SJ4DhDkP+rt5O6{L{Ed%%CT%WK$@ zn(M-a;1|!hF-g%j-wGbihxV>3RsSNcsasygxfOiMXX%NAnE}~@+ z$waP8?Hp(DxTdxVRnzy1w@tnc{+L1!Z6lIf7(eZJ=p*Gh`t!)I9AJ}u;iO-Jc+`{z zL2!Lo$DLC3%R|$T$$(;%vi1G%53nN@lhz^`aML!wqnucR599Pz7`?Uk&H4}x%y|>8 z$DocatPFv*H~8y$D{f2cC0Q4+RB z%QJ>y0fRAtgPc?wRg;-m&N>F7?m31DbzfoFu!eP`)D17RA50;ojCm^xK}DqOS(zq* zkN^5B55k5Z6Y&yz#6r0mn9}7jvS<3VyB>aYJ+n#FfEU^ZY}xwu|Ha_p=4ubKgu8D{ ztGfKagXef7R1tATx+V-?bOcrI?LL5cC9Ls+)CKrVkw76B-!d+O{ zSs(O@f<6D^7&k4TM0LKZ4mnLzBUj6Wg9Cry`TjHbq5yPw==`?w{HnfTEtnWp`!^)@ zHU^ddxH8V+%u+h62;br&`{(aXrjh4%;GLicfPmym?o;3s>6N|*ej*j0s3$f4cXV%R zQW&|LFWCJR3)@;%o>FtKhVapQd;Oib?mEREZcfyCYft}0lp5g4KL4djQcYY#vGJK9 zKmr_-QdKFEF`!RTwch^ZT|mbCI8s^;rr&tE#KY{!@?Y!n6aY^h(_Ak#4}K+>riYO2 zUcwsvLLa$6uVWve5FgTeGl7;t#U6%XxlRGT#+x0f^}VkB&(Tt(yf&|k6Cj51MY1#K z4j5LT<7_$ws}xI4nC!8X!ge?h4+!rOOlDPlPpbWwG-W!7t}h(Su4VLcRnHzWS8H6U zsWdQlUIyl(j$-QaQr{x?335|7_SFB*LfCT252~cC)g7MVY^&4r`{#WPYQUSqk(C%V zQVpt&z9;1z)Xl~aC%F3AZ_aBF1ctrT1b+Xrk()#Qcxwhr!cbqI`k6ox9r`ekHk$u5 zd0`wR>zQUROV7hh*|r;MWE{#`MG9jy>6AJtr8QVpA@GD7Bjx>Wgo0I&DVhpO4FFu> z!b0FY5(~JTI%}*uxOy{x0sN94=O(=SUMR}SSE-LeH=oxSTb|{{P7Ch)p-SFw#-}u1 z{vigtBi9Vcj6Ml2m50;)Ps;)lz|+Q}T{W8rt5duP3Ubm3a*08A7~~nF)OoCaeyi`S zOo4lnPoYc^Lh&phH;i@9!sw-+?V3OQP$k)PDMLejHBX%Hj78y^aW??}`sz`KIgP{dMVCIy zxCueWwWM7i$=+xAL}J%CborDz--}Hc92PCop~+hc6efuJhPQf}OrYy;^+_$E6?*lD zdU518xxpg%#AKJ}14r}nXx}ij-#SD%!te9tKpK5Mg7N9~=5b8+W&`3sa8>!?S{-BX zlvaVg=`_54^hK?5;`3Ti5=a1f=ikjVqP&6=Z?@GjC|FZW;`8@{$_Vei)q`u1zjQhR zs69ty)$v90rxniQ%&eOAMuPypR&(1cfU+#MXNe)K$@=2QnyF)e((8JJqN%Bh{0RR! zzSBXU*6#17(B3yH@3K}prLAojIv-+5<|CeefBZ&FUM@hSkP&^-pw2n3=Oc<*e(SD! z9rP!U;Q{yykHyc-y+V3nQ)z9SsaMt%*#-eGYd-@rT8oE4;wH>6y7e@!8XC-nJUans zRaz3f{`88%YGj^@E(c^Lq1>AeIX`B!s{%#_;EieNPMM!9AXI>) zEsUm-3U|feTh%QCmH4`Y_b%ytz>p-%MP)#F3z$Z5O^cNA1%eKfll7Sh+R0~&F!88> z@TYZP^1l_MbRpvj2exi593`J6)wZO(ToXxe@Dl}pP_tnzIF6XcTZ*+%_I2~%b4sCTkF0IPXFDlZY`-^ zv*!|on`gr|iDZSl+#gc=j*2O^fc6N^TE_9zitjK|$4@JyF^;Drol+0k{(2jx>p%R> z&VS2^dwyh=V7uL@6zZZ_qEJEKqo|Wl!QMbw`+!0?Bk%m)OAGB=`)Qm)y3hLP({r-@ zXnIA$kDcGz+-Mw3qCA?2t$ zyC5mhUTqLge1I?NmHlh05Dc0h4JqJlcTc`04~{7OTrCq?e#9L7Yg7vB`dVZb7dAG% zZXlu}z#q^~Wh4fk>YW{;GB$*G`e(1LPyaDB+lcTU9z2*;VVt`s)C z{je5!0tmI)L%Zvj1cYDfU1CCfgEz%p^(Jig-#AiRIL@uyjUmwQUsH-v5)v)4R4dHxjrE5Fcc)5vof{#+F7*^iR2GZCBdZjnG2_`rqXUu41? zux9{+2zl_B;zUuSX^%IF8;>UR*pXJ`2Pw$LHhGQv*%(@YWFeehsv`@b3a3Lnt>+G& z4}j+D3>tvwKCt6R_Z-2rfXVAJ zKXV{4xo1Z&@M1?E(j(Z;4uG3W(^)@39(CcNpGb=IlIV7SaU2WyL?)zlf_%-gPZoOW zVLx#D*dp>b1L4MX{YdTWvX9M>tcrD+(@;mlZtn1s%J1)x3nP+ z2K%Fg17Pq7A|`FG1|U`6b7IfVu}m*{nj}A|fuA~6|HXM>n|rX&x1W+MAH#9eLc-`05#sg$mh zF-o;FI1yM6W%jwB2WKz+#uoxYQ{W7<^ao7tg?9w(+yZE1p^HJP4L<+gW3!ryfbjvd9d2DO>&i_!5>ISC_L4|MjP#O2tVW*6qDH!E|VQWahQ z#o8dUWdFwPTwXHZ8+`--3I3XI9j1mCmO&_>PK~^o6y>1S7BQ!(3{#KU+pl|?8Nx^R znU$CBafJ24%DvCtm_xjLsQ5+~$Ze_iris%xM#bD%*S0F6^#B(HW!h#wDFhS_;5mH& zmn#Yejb`y#b8EG!M{(s8tmX}sIX4sgCII6+xBn>zEDxin?HrXTSPzJUL_M@=_eo#THYw=qvdDVad^7yAe#6QHh)d=v;Dw^0l6*s#u396gMF4-S$fkD! zIQ|+#z`1o$uvfz&sqZqn8F_M(&3Lac$)A7;+5;o% zUAZYbr1jQZENXQ}jYJ5btbeKDAs#Q&|Ew*+nKe7wY)NDBia|7JprKpC3BQfo`U_Te zl`7%d*JH=CWg(5*OekRr=?V=M*NiX7|AMv11uxKAt#-6eK^S(?JF}!SeRKEa8#Pmf zu6K7U9N(S25b(#TmNCtvRb$+{>?N;h&JjgV;Y#VO__kCfJde~a7dRuq^(%*^hOZ!X zwNtSAX3B9aRs8KrlUG5tm(GznUYfUy{~5=2i*V9LOhrcW4-$@DAzlV>14dLceSC0v zENgl&W-%lH?x7jZ3! zmhQ@g&=B#}xxlJ7q@P*2;`@c>gVGrGrWxS8E(S`i01Jt)(!L0L#9o@{82(sH9vS>@ z_Q2vYJ0G%6!M25{Hw>?Az;=f9l{F35c`rU&Em}`Gr$EJj$aJtQZCEVs7gm_~_>;1L>1%uJ3iq8Y6 zNG<9?bIB(d_+dXlHRSxxi9i3`(Ui%ss@bjJK#u?#HQ1n~OBeIEH07keGq_jxiZjmF zoR>xtSoFsiG!IpOeHG8bq@a~Bc)Q2N@|&wx!@Gy>h1dMx$bXy#C!|MI{<7vjQE+@^ zr*_H6SF-j3he+10QC{{Cng~o!k=UF__4$>vz;0EGvN1HF0ENn1D`)xry%Rk2-xU!K zEbz9ZR`OYg#V}HB!@yd_F#r?mRc+jvqwe52Y>edU3Pc_C~`G zX}nM1^eRd!VExSP0ebRL(h^cV44Tx5aE+KaLh;eRcYpFK5Jr~nI)zh$>osf2znDy+ ztZ`Mhf1cxjJxT9Ao7%u;RI?MX{Tuy(r-$UrE-z0Z$3CgzwH^ub!yMf+&zO?6XTl!k z`*J=-7`BaU67TzXiOE|&4BGp&0}$2)@yB^$@&MY{3PC%`g}KC5FS|ynf( z7rkV`O)O11J-`w}Ube;>v}MbINZNq=k=prO6QMhgLmCpRC$05eVM9HRcP@-?&bRn3 z^IjZ3c+@}exblf?a>52tM-20+-SG~1uKrw@x07cRHa|Ozscv5eKDO$j*#@UCMpBc@ z3qxS9$`$7ya$H~9p3$(sr&a#B^AfU}FHisc10M2s47tuQH1TA+iELu>1u1OckoM(V z;WEjRynq-v?}SvjdAjF^`iaQ+OGa<&;+A28efK?B`8LL75rq|+`Gs`ogVa`fAl z?TNvWceLMNb3srUw@mGC4&(Kjd`@iR(#idtG{ug<|rI984d>QNQ)hEg`kFxbARHCoHXi(FN#mj$-%H zWr|R@Z^eQBKDB~t3u~$`@Vv9jIN*<{w^t!Ikz=%&BXLq7J`p|j8< z!zg>Y1r&Y`_xeHt58}xpZv-!5Z3Adav|HxW=(CTq%oZNB@EAXkJ&>Ol3C0Y})w-$8 zE5*+*la8WYp{I}UiE?Hbn~{GI=qsxR8h}3+{g=$_|FnhSyVE62>)3Vn%I*^BFP$Y~ zv~r#$+sx!MJN7TS%eiU*EF@(A)+{{tl8$v^N2W_Wc4^{j{ai}1$-}%2l^D{bgswy_ z+FHdSraW%2j4SDa`9rZ4WivxdiI ziX;ZD1Y(qw5rqL%mU0BzL?9-QPQm6Fj%XKfPro@RouAVm*BGrcY>@p}c?-5)$ zvy14HaO9LiVlotUn!q~0NqpI@&W^w6U#{cx(2*Wrfw(S z|CrLaZYwbH%>7sh^&Fnb=zM0EiOW}({IcGDj@7^73JKX}@}^t4gh=G(XMk4aC;jX; zHTz{yPwSoI8^k;*Ok$CR)}~H)chf^3ulL-9^)}U$a{G!&T|nO{eEX=V&9g5bOjVPj zuCT`6H3`2|NkT-4cyVB^oZWA7_$7u`_C>7q*CZgl-K@{S=x^asRBR*R9FQ|xI7v{1 zRw2F!lrZ9bI!5^t3Jkm$yoqO)j_BpnoZM1?8;bB+n-xS{3%xiDA9AzwjKCV9tV{16 zALOtp%lhCe!`b-6FwS!4{1?WH`6rwJ@tk-0vpK4#ajo1@i(!nc`4AwqSJ<4V+Y%v1z(rY*e_!=tXGiZovv%2A$SA`dl+WP%#pBXc`4abFM zjhDa88teTt%Ch>xdGgc0*U~0}>FL0c^@b~cwqlk$Vp3|W$mvjwQW4*}b`ncRYbtU|}HFL=SRdVXJe)(?*Q z%R)u{9GbbX#+zqPV!Fy>kW0wpfU;PEU9<2iEFuEsN>uJ-cTCJ7EGv4!1x^|TS-<3n$@(Hn*k1=%4J65R$onM9?JULmZ|n<;9ov>Q~RLV zisrS&xM=~KK zBKKsw-E-El88oWsn6CZ}BkV{0>0H;s@bWDiR>qo=*V5gE!K5tHF>@&M@Mmo>*4hk? zobn&Qo$U$%z=jBOf{Uanl=|hmRUSM}z95|lGu>VrUX4_H$94dAZ*}|^b--TXhssIT z;v~t~z6qee9$gKQE+nOBmBXyUj!l-Ifth}K-%-MEfN5lY5o3F^ckbbJ9z<;2xvQ3l z+DFf`RA75Vopk@;G+|I57y2Adu~8KtMM`OV`UtbzQTIqJDJ4slS^~a45H%HRPwzRd zdzfI#cWhmcVC7(nV=~&gZ*dcB*<~|9wrVs*MJ~}od%9aOX%2af3bi%;tpU;lmGRY# zytWppw~}h37dRWcqYG?&kn`Rwbf#6uL@xr(PTXO?zTjgP2xeP7pGA7j%0+7&;gAG=SQtP|Ov+Pdm^#gv`pm49Lh?VcE*%!cXQ`OuN!;Sgaz%R<}rEdV5&q}Q@yw_jqDp~p?^`<8)~Gy15ih&>oX2U zkMDCEz4~3W)o$(BE|JoK3z$ZH*k1E|IZ)GjsUV4(ktTFJ;s~I?z$HYrYbqcI1rdcM z`9MHa;c5PZF5>p8{Iu~BPPbsVWK*zM@n^VV({S#^Xz+@HZBLG(pK@a})X4T5#4ekC z2)=y-A%8CX(cDz-o_{tLdZ%IZv33u-ur!AG@|~?%)L}RSx-SmACruBBYBKFRa&d5Qr--oR1B=0zb(9#kx_V zHt)AQY|&N3E6NmvAIy$UVom%QSWOSz%K@sBAv^N)N+qaFqGe}hy9JEDQ2Nx261)Z? zwPz0Bw`U2`ls>8Uhy>zb^?ke{7So)>Uz6$DlBu3?{b3C8md)NiBhAk+WA}kw{58P0 zaSaHbYNR+|O}F_MeYOu4lZO#aBH+xBz-{=m&!PDq%ZHDswjSwzKnRM*v}UYE?)mZ7 z4qvB==Zf-D)_Ypdm}`1zAG8#l@lh4n)AWA+kWV}eM4}pS(ewLr%sg%<%mPzKEm)UR z?(G}HYrpBabCT*6J)1m(7yY(2Zvz|-Oeo$BpXLF>BVms-dz1R>?lb>{&7T1P;&-iC zpEE~U84QlryO1q5r5Zgq`oHW6Q27w6SD{^H-(mocDh&E(5L-DD1B`Vkya=w7+6|M0d3~nDA}_VxncJOI1hqZz8_p*k3fx5`F8+__Y2i21a`=L`&~N)7mG~ zkIuBsH>=B+LqUf_nLs{!P@xkVIE>t z%gU1tqLR@?c-3m3v0nAM*^XhL!)&%rdSk_Zb9NFOA}}NbG)FU7cleB7ZCiPQ%t7tf zn10_+CG`=-fT zZB1wYGiL@18ip_#W103tKZ2NX0JU$g@iHR@Hs+46{_zE_7D0sA6H>~`>hWON?ny~l z?k(miOygNcFgowyS-Q^5f-y2UMHK1L0})fz_76e~o6qj^vD!a3Df&ybvDm$=cHZuD zFOkbqGntga^odYc%(kjm)$Lk(2*fY%m*=<%ag!6}A^j8RBo&NF!n4m5?AS+D(#V=1 zRlsKpcz3^H$b~fdf3PJnytw^P##w^IYB&`1xOlMCbgeRLSvuF=6nhKUm=gCMI^D8} zhq`|$K=U%7=j?)60uMT{aI*z#)uOQ{KKBCzX0O)H;fpOHiur*=>Zya2hV8e8{eAW%d=`uQUAgNj|0mQ@MmR)(XgJmPD& zw%KFHy>4G_8RotQ7A4FLonzbtb734>Kn%Q^xxQr^IU?+$>QwJ&mKqhh0)z(Oy9$SX z5=%F|&B2(mReKq!ZqDbT5vnx_8*w5kTNzeo53jtLsS z$~-=Gb2?RQ0G|$KT8IbyATh7$PuAGW3kOEoeQQ&4q1z}a<{0k-vnrqAztHyWf#~@; zanCSQjbH0LHX^$hH(mMwFy0Kf1Kd_ig8L?GWPihnw-4xXGa2`u!C+_W6ILkjp~8gE zbIx(kKXR#me7b9|RvIwp-{WQNaX>_nEDOW$XPiC529#5UI}awO$6{nmIi6%m5=GhV zJg#~6^?=G)9Ir?dVXRC)_D^QhGIDn;2d<5wY)Lo+Ee{%}JT6|pd~$abehKNA z!s>uSoix*MEX_>{NZ`<~&s<(hLvQU~flk$Uyff(Q^aR>z{REe&Q*!f!HA{8@IX@+I z9L&~7B%)?{j+3o>)!V2Z?||@IQhOcmLIVjLFq4UUhq|Mr1$<#JcfJCqLvsJ zfUm-z>QN;j22Z5HI}Bd#rfHQ19%4dwfHy0u)FU{3DGX}O<{#MVTS%L~-SQWWGRB!{ zW;}49w`5t4RKnsa#`)hq^EmU{d;2Uq($;o-0Fz5VFltpBO>hSQ9}R|>?c3I4FAyke zm_7C4N+%CuUTX`#L0t6R{(P}JVDLM$?teMcRn1JR(&jC`+(;lwUN zJ*V2%cgbGWY{^b5zg}aar~}>eR^H^{qx=WXxKK+_R^Qw#R1*&iGNGp(wep+9D=|th z7SP(H>d=)7GNv~Nl-qE?o+eH`--IAieGepF2!|4RGc`K9^u41lu5&B2b{O$%^;pq=4(8$G-2vx^|Gq#-$0u3y`=tsxVo?HOT4G^41^|J%bcgxq+CmxOwB#*xk|>QlL{zu8G5F&f8)Ym!IYx9zH>iIr^3x zbK!%CVzGMx#7oYN0u7P3AeMk>b46=Mm34Dnsaj_i6xbcUsVqT*TsbZZk z?wNC70Vp~xaxxBOZ8HudBE&a96AlbNOnzU!%!@UHb#%sG_RMLSL(YpuOUe|156I;V zjEQ-zh35c??cK9~$oU6*j$`m-^^e%Z8cTUWRxSmEul(gqKqNOOzY!p^e4VM6(8@}lMNaNiI3pZUIdfJ(6p}IR;{o*k_*mX6@djRMXi7_ zF+!;Z>G}H)8kc?L*xt>Rd(OUt(`eLz#P16t%}vjb8lTtvav#(9Fy`O|;PK&o#sNMe zBiU{8r6r-s(R|+Et>cY?aK`2`a;^Dd+3H}#K}3w*)&`mxn-IDh<=CJB-!VH4saXn4 zql*mm7hv9xg}<{J&TtdUHMP$xSjL|Wu?AP6fQRzSCI1Te3@FZiv(Uy~dlqF~^a(aT z-5T>;sp@iv6Mx=_88=ny!Hd~9_H-{823{zgJ-+Y^po|;Mz{6Jm4`pt;J!S2m1T!V{ z$^AtWxiNmG&xB~OG0=Lsi4qoRiR{7mpz1SN2mT#|)h9jAN`pWQm;3L-ZW#XHMlF{( zmsbIX3Ypa^Uj2F(lpMJDr7*j}w>;P|BTr{WxRye|aq^jXO^uj5D++c(TEptf*Nj`_ z+-EKelb!Jrple#z`WPLOJMa4bqmo_mHSJUiwkMFL^z!z@8qWhc_gVZjEC?xjZ(w5b zz{qF65n5IbJj)J1WbC#u44X`lOyiVyUO{3b7`xe#FJ=E4j9s%<1{{ZcL(|#cAgd|| zTzpU0D223u)jn4L_N>0X;g!I=uX{X1=(Cqx1=P5_+f>VH6FCS@Z&HaF)88oA_X?bN zBTLiSO9Aw(B$$dVT!eRimJOp||5%+VqDrI#8~8jjMjKf7j!bOsduC5R2;+OnCT$RH z;ii%um@p`PzvxpCt-v1keapVTNesX75FWL7_-rD4^36}oj4l==HxX#jp`MvbFsExK zeH?5DxP(;sKmbr%K@R}a@jeq`DgYWL&0*@^8hk?ti3@i@**$WIwxEAVq_5u0{rYdW?sL3LXChM6q9a zU4lbVu4fA`^`<@d)E~1UdcCnYaAZX9MN936+_l%QTOsBaoIJax*Z3%-zj{;hJUEdz z6U*!cg{tlX+}fxcDo)kXza6xL;DVyG%JLZFX5?fTUa_Np1(44G-d=XzJEs3I97u;o z-=s45S{lL2q#qR|*Rz)(6oRiey7>Sn6?&u?;V-QPR5}QxcJ7N|Y_^T`?vO;5{**YH z5+q;elR3(L>Np7Ie&5_Vg!oOMB#=S&k!&uT=9H%M)07UjMP^l3P9|uBX$0Z}1C)=w z08p1u5ARTw?iB*-)fLc9;jQ(XT_vFo9N4>&;gec}|Ly@CVmkQurLF1vsx$8a&U^FT zv3LfKtu;77?kj&^Yq33J0&cJEW!)OucN^)J2BH3m_b;69vkruUsCx5YoOni$HMUm( zids0ood>v}V6&<~U`Kr+S6e7WrN0&;>Mc8xXSep#c{90+&W4}$A;gK%1TNjEp86rj zH~aw%n5?@}R4v2X&T~jd9g9NuBfRIb0wvTM$CeR;Ll-di0{#QM$t;ejWY#|56jzIC z)RqN#?70;oj}Hgwqd23=%DCu;wMbZxOCuQ~7; zK{*dbFwE#J-&8qDcRfmT}dTV_(rB#D&-qwy1A zrCBTe>_{%MJ{U@riewM@;Z|udUC_pX#2ed+qFQO3V2PjU0YMo84mr1g^xmWgB)ISq zQ{Ff9h!%-HrrNWv?D~~Oh3sSU&V$g#NzY%lL(e@oB~mYzsiy{v+8Bm%p$E23*;ZF! z-K~yIpnWRzTPY5^{!*o*&WmB(`%gs8zQ%AlB(d&{fI``yR#}!Xx8B0gU*d%FL?!!^<_B;f7-iP*pTu;GcF|&@FMBA}0OCCCuI8&MR z-KB_v1#0+L;N1i)j1lZ2H|7KW)k{M$){e=g&v`ZPPC5bMC5SIl!jODF#<@rXE$Yzr zdESExcN-r+r~aXRl1bb5u`2F7s*CWdZ9zA0A4(-y+Xh;^Lk(39vMs4N>*-}jx4wI@qxzk; z^B4ZKEAt<@yoIVxf}AN2Cf>QeAH<*DR$4u@O_l5(xWC(x1IX^Mr(1T2KkS*)E5JuU zMp@qvDtke}9)0@(R<{;`ov^>dfkRh!*ma@9dLk9<8SX#i32DajQWOBfda5+ufX%#>$I0{m3KTXG#DiZzsrJPWJq1KS=Z z&+dzTC($R(WT2Trblw}mfjrp6*0Rtwk>C-3L5Uk|ka<0&DTCNO??9g|$++=J_Aelb z@fDcHpqlB|snPcIBsgv+@L=N-NV(^~qezpeQUb=nghp=CMK=2D(`)^-$7HjatekFY#E62lQGK~)3(!5k`J_|?X zxx7sz>Bv(Xl3N~??gqZ9e0#dtTtq?so~AG4pKfsTo)Pe0`UGFrnQl{uNGYsW2f=sh zo;`Y(15m)rX9Og5QS#jCW0brR>*!GLO#}cjg{F^KvyJxkGVw?8d zxH+*(G}oRLJ5Wt~t!ckwtlgf>G>jV9_{81v(*X4T&W}ochv#;yE7GoqOL0blMFYU6 z6btqR81Q((=H1#bL4OjkZ$mkTGNSoxZamF%@=7x@ox*um(M10VXTv%{79Iq4o$45k zVW`wvXJDO8&%A(h1J7y?tfR8@q-fs5Jd((8@$c^;xbp1R3d_r0)2!Ck&eyEFbHe4PRAhxWv(RZJAp_+fSdbNPp`J`Mw;9F3sZA zi%$jOAOZK9lVHBmj7dBUJWtVV|0hYcK2_ZYFSt%z%ZqKi0t7tNONGheTFr0<@Ex8e zujs|wgh2|!RioPAib`)S?6b(<;@|NjEEcb%Vj8*m<36d9xUq#Ate0vd-x|0cD~p~5 zqXo8pK_|WCqHkqvL$`6&Xag%_OR3S=g0t0_vmAkdwOM0_RZI3*hL<&(C?-DFX5Qna zBSOzxM;Ig)5zVDAZ{1Eutv1~_j{p?WbzM09(=O@*C)Fn_UrGkzK(zUqT7syFt|y3I zu$#C!KMFv7>c5JK%U~MaqfN^A`p2;Kfw`U7G=WuLJi(Svbm7HX->;Yg)V~@38`dSc z!>f^6+_C#tBO4j4>#c+mnS%9r^dVSD@4SOI50N@=O5U}Tn`C@yPX+yF9aE1m7D?l# zTD{c;^i?fk{6L~iqY5XvdYYEtr0kTm9toN4BHF&X-=T{C zg4l~i>?4v zTcuzc{ZI91iUO%K&DSn?(x54iQF9yQ6EHbQbV_N@!JVB0BQt&@K;p83Me{}tCJjGY zutiy)0&bO-Tawv27#e4k)hlgwWRFk@RGUkJsu2IVRI&j&Z@JqLk;7MUM6q_b|Fox_ zN6O6lkz2Llw?J_{_P<3>0o2U+_x-T^{)vx~zN+a}B2Q{^o@_GNL%yl5P*PU#xa(|- z^hTJo+7}&5u%ZEq{{h2NGf63m-xRkCXE-s9lY{qJNkalOMz6Nlz2Yh^@7ZT8sZFjT z6AE&u#>D@!G;tsCe0@@#IuE4L0F{PL{@DIMFMtnwuY<3I0&B2XiZob}?r1M7;}tCD zHSpe5t?@wE-heHI^~kF-1spS9r_dVg$kk>>`0SasQlShru>NIn%>N$i&i5Jbw$K!K zhjaQ2Hj(?Zk?C%=Q;uzb*TCS-GG@8FzqJK-kjr|ez^T0EcH9gEUN-ebnia0mkB|csflZm=TTO5X>!P%zs$9{Il6i5PB+k&>3+4N<0txZP9bH0YEQ60I_5ymxrP2_6R?*TLghKRP(XL`KVQW&@go@=&xA!d^l*bl^~4&Mi7*pX@jnWvc$ z3idVg6TflK-JhKprTyZA3q?n_h>WiZ8YSD{{mBY)>P|3>Vv~ipT3ZgNOc9Caftb#yJ9O zu%h||Aa3393cXqBN8t{L&9l`6`mHCm)99i7yPLAYEI6=qJp3nZzj~0HR{nMv>Q<%Q{se?-ZUZg!WHMI@UG2zXd~zf1OgQvop!xo!d|h@X0RXKDF%Uvt#&#W5=bLP;_!lMgDuA54Jr!kpgEfejjG;HJ+)-XDbN zi99bcpk@!$yA=MR};}pqLCEg!khF{Rv!2b zLuZEofcJb7H80=k%s1xr%mwau;YInR4B`8%S8!80Ibm$AMLzL6NYVx;EO)3j+5)(j zJzE9{u_@GNV6J;Qr#aYlXzI`=@sfQZ2Q+c%-hgU0ACc+>q|`cyIS>=UxhL@Qb?se= z$?70?cz`qe_9NYZ+OE@cz|>E+MpLhHY6^(KYBcj5Q&c!2%wm9jL>#;0ipQp{^*7ET z3#EKB0YVgyJIfI(LFS{O-6_~ae9?72r8W}j6i9BoS>VRrJ>oAG1pMwCIK(x5iw~Pb zQQ6Scu>t^JbmUj^RuI53043%8HKMob2!dG^>S<0h|POJ{r-GT+0X zPOK#wJHz%PCj@i-SPHG=l#Z5Jg(l)tVZ`3hSx)Sie^$PhtgEeGPY(Vq3E0%RIrE+S zffG5B^;rxy|1fF=iqdkSlA^Ez-I9Vkm89kUsq0fR7b@?4X_L-+1PT;W60|#DQ_`e9 z>Q}{&$hrEx;Dm-kKmh-11S13xaJ;aU%av8Ep3G+<$*k*~jKN!OlWgY=3ik1fS07;t zba9g}S!KgZhdyHw0{LT zpX=Pfi9c;LxN7#)!POXuPVAbm}g0h~Vz2td(6J?cscbH1SN+v)mF-j*M>59g`NMgMr zm^$hR76W(gFd9`ZT5u%|u|C)!lUTx{9yVb_g7$8TGh6LGLzg_y@`(>C)|J&>kpvs3 zb`dqUX9@rtfQkqE1Sr^Vk))8+seq)ZDv~W z#S4)AD|c;Fi{Ev>utVyJE+@7%7P>gVHQx+|u7BQi`ylB?eNXtEh?$g9IFV}??Q}3v zC;42#+dEcId8nrh*W_@cj??aGST zT$oo}DWV5NBUu#elQCZQnhWb8#&Q4z*dIR0;-{9+ByxN)ycLsog~zm!tzv7>aU)M+ z@+mJC%l0(i7e)S>DKk(30(LYGLU`NpIi&aXY!-O9JhuAnW)6v(@#bQ%w~v>z+5>10 z!YctYF^>Fn&XNAR_@k0Odb1@Cm{*)`o#G3?-)CJS*XAxV1s`ODWFF5d2@^7a|M~1yl$!Q6{C4jO<)%H2#5MSf4g&aJ9rUtQ?)?b6xp0h`< zmfN!gh#mL07atoodQ$#SU$o&@kP2q1h_9b6Oz(sUCIx?_mmjJA9TCjuy<|j>?hl*# z3^oV-5h7M?O+%?OKdZxj!O#!QcPS*ld85THdq>+28uhtN>=+)r}|9>Zq7JCq8$vnKNFYqQKI^Lls>SndkI<6*JlhqYeskp`Qbr)16wJ+NfqzQg{f=H1QEu4 zbiO4Z`+8--y9I9@Yr|4qOk?u)(H7G~r8kM@nJylX)8B;0##6v)S?8X9R^%xcMxcB5 zul`>YuzvYj9Vfo^s-M8VS%77}VeospYK`kZnBWvO1QI)T(_j>oweY{4C}W~bJx>zr z;Z9S1o>|T0rD-ju0mI2hX;Ta-?e(|eJPUv4_`&iSMB5#it4b;$hcW0!?hY0HaZfrr zptTcPsS(pE&m-LRu}!u~uM^Bd65H3!(>HgXARZoXZYkxq%%`66GaokTyge2S-eL{d zW&k5<8M2#038F*X1o^R;J*$)Y?_QHqR5(YjGx>DH1^9Y?3hXlOF+0J8qI5FOF`>k< zl&tq}Ua=>EBM@WjAq^%@X&P8b0fiZ^<3dKY8qIWlvKGn?63F##GaILu-*G=r%r*`Y zu$SlZ8YRBA_)y?{#NgKZA4#jOt={onwQrnn>z?R-blk)DxM51ghh(_W-7^M`&Qwa# z3j?@Er;W#TCWDrcZT!=0g;K+!c6tmt$Vxsub-T3e(4nKX^EbEmIPpppD{VaZ((q!o zx>T4s<(;eAS)8Pb z5uAmq^mBTT@ALAaH@7N1y}x(Q!&L5XpFJOQyWc)exJr>r9=191t)eh@Q}bH*hmK$Y zdchW}G*D!LaHeEkh{?a!kpC}kZ_>4@-#z=YTvbN|2*mD7aY0W8^B*nG0Yva2G{K42 z9B&bH=Q^Y6uVc8uO`b>h6Elp`=GMo@(ADz{()202vR1A!e}lIJfsbG$p7Y5oX}gcN z4fwpt#TSggHsf`+cXyR>)ZPjyTIh)_+Kw+&85FlSSovX5-FfnFFt9AiBj<1Nm^$UY zzv5yt2%Jn9n9ZMvRFWk%Ubp=t0-J7l9;YZT{GyK+2lkN#cdJzU{gML%b6v}=+lny9 z3c=YUa^&|Qr9{5KV>)Bs0*))qwJdQr%F5&v6DmF8_lbWeb+H|zZ)u58iz8Vk?=I?Z z&kqG@ib}((Ipwf23B&0BwrW-FoOx;Hwh2hq^o>p)1g=y-wY{)$I)*p_{QS|a3n8t2 zzV78-+(R{*##Q6T$qAd{NIo8n%9zmOd|{W!t0&u?xo-j> zo|Ftk%&2A{X^pl^gY7JdKp~lV%nqy+!(hc|Rq&A~_@aGMm{ZXbFYM_BS7@{uraA~~ zl$k>_emMfGCzT+Jd6?KCMN;3!gZSgu@X<<dqFpI37gW*j z7EW3^Uw!xX_~4X>{fF}{S@Q;im&>-_|44CeJ~HRwWb?>u%+EU0fvve6RCV%z$q`G|hnqnIOA1~- zhuYV)d-oHsm=!VoCifb;ZCSY({L(4?R_I6ZkktN}lYj60qicbsMtgJMjP`h9-`^7s=SqRle0AM< ztoP+(y9ND*-^$YM><8WRIuT9QZ1mfm?E~GD1(#!6*1m6@S#o^LZwR8WErt^-c@_+yMInKec53<)ecG+9DLs3Y{Y}m3xk-ZO*;*d>N zMr4HSJ+fyu84*Hu_WHg1d>_Am&*RbKICuAb-PilNUa#ltr=8R@#FR|2HOq!%!t-!A8B4~eLg3W?xqUuXZqYXYZYV02b~UK-4}ESKR|{LJdpVCH1}=3_rzD{3Fz zCVT5wh+eOp%O^J`e#Cd$ty$iIwSU@xSvls*y4PK9_9yMk%h^S(F^Nm3qf}$cl%zX| z2eiuCe#~5sc#)+ttJ!I_o*1n-gt}CgzRrUni@l%8-0qrg$TMzr6DoqC0w+qIKISI< z#4wo7YsjUFh2&kHmr)bzF0##)N`yArZk%>_g($oXs($SzD+Lge7O62$Hs|F1t4t;b zw&%8JAf;)1Jw6M2k0k^rt16vXRj&2UHhC#`4J~nQAM23fiAqN*hws-hYlMB!I)9W~ z^Gc9Di~M|`<*Aml_QhQeSi2h~k31c3Z3aq-!VBg~kHJUB?|b7%{$moOCpUK`Rc8!!bms+1_vDIIL`AB!3sLNIXgHKkZ zHS1lsg*-JPe^sRaI0u+R2{Dfzd$2g$z+%V|`BK^|7R~|43i7!e zDb=#{<+yEpcnWhT@tS9p%7*U6nr=TN9BSq>@u+e0=j5x}5;~H1RoVp^k(LUA2Ufx} zbfyrLrmrMDf%6|0&i$9_g%7_%@*21F>Dj5PWVu#o$U@=mWa2d3DH+}+*D2u~r{s3N z%PmLe#E`Y8deVc0U>Ed7E+SM~6Cd+yE%}QMSVeQ}l3Z_Ij;67n`xVBf^ISdj>5$Z| z2BYbsTY=T4VFkV=am{-kb&BHqnftVSY6Jl*6kzyfqqZvLn56W;d#^-lEVqiC*{tg} z9v$(**7Sw)uO6K*jfvYtLI`S%)2j}4KA3c-QNxp@t>2vqv-}ov7Y*PIF3r|`%k3#C)^Vt=vNsWy{P?fTk~!Nq3Z&u()TibJ%#TI zYZ?ob!mxYulE^Hdrl4z$sBB|9hViFkKR7J+rSETjyw0ae$TZp{rKEmF-wF|MfCNpx z1}NbHgPXl@qS@?75kA$MxexFnm|Fi9R;VW=@d8m~TK}$b6>6NRjB=i}LU0LRm8k9` z!B_CFst4I9F{LJo4f3_h+i6ulKjMpqZyYXoL=8IGGF-6_GDFpylfTnOko-x{E<~=M zL@={gmB@cx;9_KUpGqM4YaJp!DD-A|YtZU;SeTysDqYu7Y?cd|N&so3NJayl^Qj|A zz;UH=*2y=&pTAp?Yv0wE-HBZYlq_vRl1#%61;&Rms#fas0T-t9x}hS3SEq^N5tUKL zyKqi875obU6ia_kn~i@A31NYQvv67pnXCZ zDtpY>lKvWTP~voTVb8l1k{`z@20L5x_2EwS=lA-!?$vmDg^g8I;VGV$yfOI%+2i_h zLrgWkWX2aK0EvbnaiTBlH+^kcSZDM6-g}pOr<@(fzUo^h&LXxc(qBGUL9)N@gX64n zOkS_&qfz!0Q7k`fcCcRjpHO)CSaoq_b*v!t zTxg?kNFnZJEG3xwF8qEL1YtV(9BtZl3YS)RQ+?CDeo~|v7h)H*_1>(PC3TdA15KI_ zQ8$)8r5+G&_h(ioRQda{77^QJZo{!%%*Mex_wxH~0`)AEuq-JNV0fMfLX^A=Xhpcv z?rd%eM&|?}lM{K}-*P+!5JI|xk3^lxX1pc51)efoFjaEr%@zXfvQ^_p&tF7C4fLIR zk$d94BqDELNK(EM*H(N4tGcHobw3^}!)?cO;H`5sA35>hq6Xr8G+IubLd&6&ocA!z zyWedTf_3g!wm`MH8P0UyrAP|hi&TC&8<68 ztK-%VGfFVPesul&m_#ht5M~bG9hR@Oh-xe z$Q2RHF|AB3{&@+_sD@u7w4T2%xM_N)ZM5#b&Xn1dbEeZ-cx27dE0}|1UB3sukf_yr zIMmWE^j2I=^oOTvXWIwX1@$z^&sLV_H{K!-q>8r1`v<=zsfA59>bTB4;+={n)23XO zUKeo-U2DkQNS4$Ke&9G?OwK6+FLm(WP~ZG2Nsd8oHFQ>gi=io-N2y-!IHCQI$9_uX zi}a67@HFqb92*DaeS_WmW|L~n?=7c<5gmUm{^GFceL%Idl~f75$g}#0j%dcX$E*{Y zw37s@^7OJ?a;@};+Hi^+++)sq4~>xy=zX3#Cm-s3(?2>Iituzp6aIFu9SmDpd*Q2N zjjb+9v{b4XX~7Np9=_lHgTWn2km8HdTX5%&s`C&Pb~ht!*^V7s78wH&g9{Oz=InnH zhfURqFk7jR#|-0B1R?0M$FGs}F(3O7(Pk9v8m6rAiH_<@GcW2H1JtjqaZ5>ue)<@? z%VeUz-<2sK3>lGyDKxLWi}2C4uV$p^7hxDYo}nshy@}_HBXZVlk_tlN8;)KT!O$3zn^-ThF{eTlr|kYU-OONG?AOMT&})v>=5X{E zHZ?*FV+IH__s)nTlKkXvO^9yawIo1~mtCA*L4SBzF{g~uqbz;xx*#_@g|iKzGxQsX ztx64i71bufFiRWv?S@;6l?gAZMFKX#X>Otwc~hw_iZ-;11!6=SU$RL@N*P-sDXP>u zp!kTM-B#$T_rL`PE_`wanaaQOTcgUuafP*qjFU zx)tPmb0HXUZ*fU)+!5AsDq#I%)7q-MiYD=S|m}w?Dsq zQ*m@~>}5qGPh3@%9UJLEx#Pz1SgE^zf}4EYhK+CDgU$l7=?TGtCB7yDsSh72Tc?gT z_J6<1Ua!fttvXyO9eZBDsXxnOdR*kC&`a>BOKd8-<7O{)V&w2~TBk=V?fuv@DFWtM zPCR6!mR*}2OsBceSYgP z9^apH(ds)K_|cAofzwYT^msJF8toR}y0l+s@be_pN3v)AZF9BoUuDG89P*t}LUqfO zAFU)UdPzWj8j5=ga$_JG9L_3hU*aKpXM?xMpVZuM{PP>nP}S5qobsC6sgj9lqcxsQ z01j=2uqvcFJi~0#X^g?z39Cc+8xxB;sv91!D(Z56sg|OewoWd87e?|*$MVu3t_(4KMuiH~%_sp@;cQV26U1OukatJ9VVkL zkIW20>aH3nY{1YGsSkc4!0e0|@xbq)f%WJ7rB{R@uBdD>RQ4X#t-PWD@toFL z%s@qhSiHR{bSP5L(Wi`)dgDqKzCyRrZ$IA~vJPE5J|tzT$Zee6ppwqfjX|iq(S)LX z4^WmNVNj*$S3~7w0rJpgVl39OhJM?-nLQS+!dxCwK#H`84ylyxl&G;^1j?RD?XbUKbCxdARSp|; zc?zRP8=>p}oi*?QN!n%s8_AEK*-TyM?Vhm#@lHAE7Ki zkM_ecj7FjaNS!D;0fqrd<~6WamDEd^{VwVf7w=d{B>DGQ2=7vm1Rtkq9~h99pD&)J zy)?$qFTNxdh3rn!p&yvWp9w7RcMg0J`BnQw%)tWgc8n*mxVX$hzt?!JL;mf^w5ZDr z$z_00&AkU@n7-BR0h?kW9hXB-4D-89D~o<7k`IK4gKK9v8ur?9zi1Up-vZj7MrhGb zcP#n`FN9UE^-^IG(-JP@^5*(of`uAT^t!}bkVPOZo<*z_S|8eCs&2nfBB#VSDDQrw zsMFjW8D7Xr4;&6`&8=L~-;%;Pl1UWU!VAd)g4y|dzj@5V=DS2X_*r1nz4fw!1RBr5 zVb$PXA__tShw9*K#BskS<6%|0rGoFxFkEh>60*#=0IYs zgXB^JSZVO@I~muZceDo^g7UVnxL2r3e)t}LG;G+X?s|ji#{$#m|>fb<2JyL42dLsD$T7a;6Nr$nJGjDx*~4?5?|zMd5pD3_NOZl{d`b6p>xc(Tuc~W~B^G zRCvT6*Qip>J$@mT)r>1FrU0IUk$nRHYgwb8U(ZY&F;) z%3&6qHQP>9=4BZECuJkZ7Xf(e9#Po6%HM;uFX9Ro4SkXZn7{%R#7w6uCadaqt#ASl z2MkL}q#`Hz+re@Av>s#ywBYC?qUgcW_%CA00guNMpm!-fHUlH#`UwWF##rh%)&Uj4 zWBFHL9%}yLgawiRJY0|iZiByJB}S4(hQTuam=~{7-F%CjIwY7{a^@{qx`z) zkDppW8uMlo9}50h>G?n-Ov1u)2F^U@o`Ws)!RUUvZ_=CVDLAzUjb`E+HvjxJj$Ajx zZB{ZJX*)qPzF}M)2qlbAlu`m9}o>%La1Jvq>D;VHx2x_V=6#ES`XB~b4^;m>C6HC z`{h4LjP#`>z{xm+hvXZ1fXBDxfpimfYAm)DSxo=hznhWp6+ZeQSPFXvyj5PY4=cpg zgRRT&iLljC8em4j(pP`QZ>T_F84=>C{_FpMN>IECJwOexWoaTn$@_L_yy!KT91umS zf{!4`*ZZBgESnSWyi-PB}IQ{-V)&CAjHAZ>sK`#EbA;KEv+%V*auET4~N1MfuHwf!jgb>6e#L2WkSI1{U0td)`eX%gmL63Rl1`Hk<}B{V7nlN(7t$GVNN;%h&f4sXT5xzKs1e4-#wHh zu(@UiAoE)O`DTIGcQ_E2{~v}cGitLO=pkq!m}-CHino{(b3GXQ*OkFY@p$6L%u4Dr9Pmh1v@a^#h5SH{8_9k@ z7`J}5S$TiC^hTTO)FJx=s44_u>-1R`b*JiFOnU9o*MwvtPn8pGCx(Wrw+KZ*&z==6cHjmu6{%Mp;ZV*!B8Ix zQj$c3CKBF7Fq=n<&9Q0D38`MYiL);9c6xYAdKKXDpBeF^-?TD*P6V>|XHVp61CR%d z9qiF@l)wvqF1+%C?xi2NA!qNL(ORjTgb1^~2P9H(gryiDAk1r#2AliUUpA+aQ6^ot z!D-z3=kJkl>a!4jd}sEb0s_kd(GuoOI1R!&R3kF&$O#Y;-ZdGKU?FXHEs3sHxvRB$ z2l1C)qi7_QO9 z9V~lNr~+`oPOFg6FHq#5Z>GCgJ29~pE#&*r3^11d$x)40nR(YDWVuddbc-AZ30swB zu}XbQ0#p`wZFZcRwU>Yz2UJ=3wEtAx<$}!qo`i-@=N<9Nf8|R>qiVF`3s(Y-}hsa6ygdF%t3j3?5AiarQ#_ak^Q2 zHWaKkrEkEUcVAEdJd-~tB8pd@dn7H=W?`EoDF75c6a;HM$NLh7lES{NWb>HmP;q&uyywZMkUd*!$f zAqqlgG7%xu*P>5Dela-L^cA}>@NlovEjrI{F>jY`U_sEp?It88px=^UeAa1i^0nLh zNcmrF3_9*)A8$bBUxbd{2RP0G#V}${(B{9aulzxi z40X>R%{z63y#bA-!%5hrQrM5f0AxNCEig2^gNW8T;iK_Fl-ifvrX`EDe*i=pD7f<; z(OO*R_hYQha_Kb;w*Z=>KrUX`DhCk}J4T9myZoCQO^p?8r1(RZbpW};;KKxTb90A~ zJBe`^BMnXx2i@0Q0VD>fy`Wvd4G6nB`Ilt(=`IX-o0n<%BNzj8*}l(r9ep|jGBwC& zRbMv9@cbDP1gaRJ?`b99X4m+2!hl$_O$XBZ_R^psCOI*Tt4q`5pmI8)dRN4ohaqSX zspIjmT?9}UxC!PhLQg|}_kG77s4@Xi8=~$PB6IG8w|;{-l{>%Zp;SiicS$JgY=1i6 z+J{gE3V$AHN+M_nE*bEfm*m{NS$US}7Cp{o)dY|jmL)ad9{&y)`mK`Ir%$e&{m}F0 zuW2gh&$wA@4mtX`bgt`5d##gHdt4-OBLb7HGNK>H2z+!Mo?iLdpg4_ECDDI`$`&BZ z>3;*Xw~`scfyl9$b_6DQinojm%c!F>6VG#J24*=ETSjH0%tV71#!bH~s=12m)ESuB zA%cQ(0+B@w`1vaL_31GF)z1U4tx6}hFMxn_Zggz;-ox`1f z?vV$mM*qHxj;5i%26R~L2c8#UivP^1)2|_4ZjiQIf{E)QooKykJTEa7L}dO9Xgp|P z>jAhl={~@Tf!oNNGeH1zC+vjzzSfX$#~OIoU3&wMr^98zSDwnOD}bBZu?UI|ZSlC@ zgv&U5^_OJI@)>fm!uc%<$ctVvplZ|0nn`-F!rVz@d+gkC~Ew3d3~+b z?hvvxA~+-R7G)?O1Z==x{IK@zbMUmA33L9zMWd`vnCX=fDJBSmw<<~I-Gs#WXeBw^ z0^pOJE`sPn;$XtC9bf0een4KYcIktF8zlh1{oIERY?(mAt$?JecP0%$$H8fVtmYEh zzycf*?BWcl;@YCd)u`%ddLT}}H_$_jL0C}8lDaT>1I^5jn>XY!*j4G3o23+hmiOdD zABA<@1Z;xu7nf3vu5!H=-vp23;3q9t_;gzh^Uq_A<%IxY(xz`j@1{Van1+lIs|>Qt z4TkokehC9A;vrseOf^nW}X;H``e}}cue@|{`Ninu;e(7wLQZ!ZB`OS zPyPe3-grrM*4}4{tirc9nGiP{iGKKf6N5#4e-#a@b0a{!*40^rAngOR-wpgt%;4!I z&a8ZD*@UACX#Ncac@;qnq5rzMnWq!;2}KTst!ez4?Djwz=tabTw7-AHGNk_&IM`9v z4Q5KQqKU~6=&#!6g-Dyu@V+&V8;GCuGty~+eXs!McG3<&eE{F!ZCz>jeC`+UGUDaM z-i=rM4MWfQp?!k|t!JcvD84c2;)apaM$<-y+Hcs;GX~t}mJn_s8CkoHismxig}D5& zU`IZ4<1PYWHO>x0Zo+_Tf;=oYPn;Ww{L*c4z3h>$CqJ?rqp5(t=E=WV&FxJfZ^b-x*8!hC zm&sS{Nh{?N?_Q1*LGjB^OWwdpSOnL%A$VT6@R&Wka2h_ls_PBlEZ`joEbV^+AODR|@U%KQe&CNh=3HzIRwecAtKCEE0n*z{$Jy0stA6Qxh z`A11F6+bC;t%c^11xG$0mw9}X^ez2IP--D&;oI+?viDk{ueE@9p?*|*t@Au*lQ0B}LZ8S$UbGy=*EolueK(tc8-kr9 z0BQPy$_Nc$l>ZfC1Pr1$X^Dph6vgbAPOrm8Aq&CXo>`MK;dbw)fW=n)RQ%mcLNv`^ z8zu78J97C(#P>CuhY2xA0&rceW@4KQ2FD1lZCX74w{w0zq?GU~x3hAY+FVpKLCerQ zt`iR&bqGLzp&d4`t#a{}H_B?CDF3p!ACcK(;dvW=2*Gxl_@&!^P+^#FD?eTTz5x}z zO-}_5xadGGyaI8zCZtu7>+ant!FDCnYvIL!T(=^xxjp?N%v5*2(W!vSXk}mH;Y`i| zu@OJQTTK;kQG>VUHO82ReqNQ5k_Vq6mD&zT7oI{4O}T-6BtYK1*o86V9(j z&kg~q`8+bZSCDL*6X7p_w%e^aap?`BW8oMe%tjDlQ#t;DpT}mN8hz*WLx;IvkOFMz zMMd-DsHU*qayk9_7bgl1b7zW+txT*KGD_E$6PY1$S5q}I3_L*Vp^OHdhf@Gjq8#@0 zc!&_A>x8_X!f3BT<(xX;; zIsWrlh60CDF%7L;nxylRa65T{g9Z!*Jp+-nKh8G1F82We9uX=os)R9B-9I0R>!Pxz zG9)r>2F+6NrJqVTNo48?b$PXjr-s zRsd2U{oQDofgo^G*#OkhbjOHL_6-RHdOTXAQ5|CVj0-}fuFVag{q*03u(ihp5Ou-6 z2(|dCK74RkN;@gMbh?39`&`}<-0rQgw@c!(c7v9Fhy>U&$)dVFGy$zsl=7EG9YXjb3cdvE}GY15uREn8_D79vS=|)oa-T8^C+Xg zoZTh1b*y1BLf^=+ISchT#nM-u6r-@hl3Fy_^Yd?)W@&(}JQ~VspT`*Bu|+m@G{%AZ z=Q0}H_?J}@3cOZ6#2o6bANZP(!s%CXAXpPno8D3xqJDKG~1kh%{&j!bQ+V5aJ{zn$tsHm9&lGcC6l*^K>1(wf3gSl&zy~FZO z!9LsRiwkpSv3}KK^d9!2m`XiilYe2J3an&*Xe<#C;Ndw4iS5mP_duU48u&UaoPR{b-l15Lmm zvQExuu6u!(H=T@B4oMy)XUxQzflgQ;+N~JqtT}7hN3R3&qD>RB?GAthCLC%hvq!WE zsY1*}#H_!;L!s}1;{FLA^82s?nzM{k6WMipq_-}&W(Wj}0#a~1eI$gqaSgHpXY;>vVs7egyKp+7-FCZruBBnFVQ zL12FNMG`_v$>8h@pc6udw-^7_F%VyL0GgrYZVpa0_h(a(t#t4^b|&Vpb@}fI_^eU; zzg|PQK2i9#GI2?kcE0Y!&evFJAB1~^s#&CrG5GbN;3~K8IDlOvi``{RRMzP{m&;|W zk6@TS*T7fZ6g1*iMY>|T8SgtWTpYRRsGL6HXfn^j+&uM%tR%#q{24QR*)NJK4Ad>W z3-*aiMYIxF%8YJ%@^k*8rXs|4xr2+Gaz$HxHDso>ME%y~!@O*9)jVv5u#t4Br40$~ z(}z4uvpUhw*A6d?l&=PH4->vX)I2$OizsyJ&l$!t@4?ZBzk9)_A%;+7=OIg(Nf@Nv zy!d2tQZFlMPKu`qcUW@|h1FvJT(`y0TLJ3XX>n1= z09)ac+utc+%W@7xQhy??fQV_B#q1NVGGixxpj~H9FEdf^bi0du@g-HInpP6wel@Uk zvfFMTgx`3uoxUXHbjgy-@x$e6yeZ&(y{TjUT^?B!GUK_N@i~JdGAphF1H)#J-?VHq zOr^no5>*bMMdU-1-l!%J{5xpkq^3XH4n(TqAfwAwb}uaTMqI^3EgY1?aty*=$rkMX zyV@lR_OtWX%P}|hdv5{`r@*hOOZP6eb2ZbZgC`^R$8T0T=JW12TuVXx*& zUh{oFA=G~LdtT_*Uz18ydzP;ua2_jljK8h}7PjYn5Op`P=EemgX!=rm zRf(8NG&uz#)9G59K?4N~GoFr z8(SX>UBc6^CT9{Ol{))*N-w6_7S>6cjJtPmQ7Px#g}lLApDd*;59T3uVGsW9FAnOX zR8Ot3x-9^1JX$;@Y%ByYmQ1W%pZ+SKZ=Kv2`CH&>;Fn6WSMQi?+)LbIp;E@$2d$k2*+&`D`DzQZm-d`I``|N zDtFHq5t;S4&@DQhq{%(f>Qriz@qX7~ZpiT1gDS`tz>0A1cR8~ayf15YmW7?%vIkbZ z$xdmmGWf@A%)B3Ziqzx9uFYXbg$)yU*2EB{AC{=G&>k|2kHTCCh7{HK#|?A7?t$A^ zoz};zY*+8Q`8XGLcOtH+7Nsu9^x3>m6Z7xKxde6dHxSd`yCV3lj9`Rd68ay!PcMU` zzvE%m^9SIzcOFTIS?rAnJ6c+Q-9kI1Bu$_+=i)nKR6Y6Ec%$$MJqz~+X{K&4dGap{ z&Oxz;)*)DN4QrXNQ*3Gb4i4dkZTBdY3faDeah72r&om0|5ji?Wr-I5*JhyW^*;%v& zs1McSUK!V=Jc?$$Nk3`%poQ1*DSf%^k_CxC(gGZdAA{583OZ=r%UYahszyz@*p+#jRfHXJiXs2<+YAWFBPM?Pp=p(mz^7q_rXc9GG3jH3#gIZg|fAt|xkYA0MrEazjO5L$9&mdKB04M{V`q?8aJ+ zK8f750N{Zc=E>e&P1aV|t1M7qjUdKoPB?$q@QVFb)_b{AFU1$mZywN08z9|~KtQSj z1Z*f}Q}`|R2u9|ePYKB8gOQPGNvz3hE7XuZi^a$mY=?y5zZq=~K%wfW8V*FcaKIDa z8-l=6FhydvYEkW&xBdtiuXiU#%n+L~U;TKVktiZt|Io$YGpJOeR9&_14tWQ(i?Uj$ z*~eK;r^(VakHjq-Oj2%n?~w#nnS@bc4acMTqi=!q8&tGjGfsuz3BL^!E(dbhNYdF1 z2mvm3+PlyZy zapoK5BpA)BEfUPzy+_}+8J}(5umvs82I+_%N_CZ*T{Cno&LJvZPQOA+V$bl_(;NaU z{c>qWjltOABk1FTJ;RFhmjO7zHdpN^+$5@dd2p9v* zIMd1@9?sIXFu)bM;g}WMObq3Ca@*Qm*wPsc>`c@RPR9Mvy(dd)+HgDfGS%^=YVs{Q zb_hR^^JV;eVM?DdfLMRSve&c(xL*JY0U!vs&Z_^myxsgTQy4*p7#3{i*BVk`it0=} za};}=49VX1dY`Tg;14P3JfKp;(ZF*g*Li(Og<)gwX@;OdKSO#{ zH1j2%`*+f1HV!10!Zz?#syP?VOl3+mFDU=OItF{$`QGt!J zdM9(MepT~7Fyh(nk^e-ue?7uqwjZE@tYW=ZKCXhgZG#HQz_gGx_#oQMf>=1rhgU`y1h*rEL`E@maMR2nIbVb@#wF&o#0C5mZyzb2r zScs&Ch$sXDz)EVFxs1FL=Ge`U0Pn*hvEcHV-!W!tL}y|v8fJ%q)Az!F6%VTJ_9NhC z#LVsw@HVxBU^f~C7A2vlXTRul0&paY9Qz4fRL9#_6y0B*kEEv%E%fu5ij+6;F+t34 zB&c*$MA`p7L~=u7Fb$4dfiGfQi&9;WYg|h>4wB1uQ!bz$6_S5F*LM3jRZ=vX2!--D z1p+nkHhir(=3V5?s=so|*>C#Zxq@;zo>l#|R}1Hudu|or`b3+mcMPADbHtJ($HZHO z3dZ>TXF9`%jhsB;)_60zYQw_#h?~fF?wLaLmYdjz7ZvbVL-ze@AKHB8S<*HO?M$Ex z7TN(PeMQXOPn(*Lgf0fodJ@Kuf1gHpygItBsg_~U^zA*{g-P{A_e|%(=l0?Bg15ox zbJ|&_S2W!n<4&q=@9%ypd5~ss(O?q`-Sn^ee=R_8Pm@${*Nv3ILaM}1mnk-}4=&E^ zkk`G>>k4a)dVA)>^cxC}TyEr5=S}Boe}y(7)zsNlKMH)d>h17-b9(vN%W>-X=uw9H zuC;(F-DX+OLCU9c$`1~aYunj^XZP&}==}DhGT=W+JA19Wfd8j?C0^KF)bX#3fF9}U ztGlLm4%fer8$UU~onGqfL!z-SEUT?qbI$LkJeieF&Un}v#2Kk`_e8YW5NDPqYg=-y z{9N4v9VQfu|H|Goz@``ttB|!#E4cb-dfkM1sq0(v^kYtkZ$j#w-GvBkNid*8Ukrdao5%Bw&9eTxQO5@9 zmmL58{~Ft8=`MM~OwQ6vy@}D=k;jQeluHeTUt?+7EqNtI>=Xpj$kgry6fn|C&FVYaIgrO&mvvz*F1l zU5M*i2Q6Z0g+{q+qhKI$`H}P<~>so#Uq0~9r z_$od>dtLk2bDjJl#*UH7cz0PoG%b|&x38h?x!*T?OuP`xksXTr5Cw2POwZ@B|C8jd1 zED4Gy6oywz?$O(aac^;jF)q!~lsf&V$OF=lXSdZi=08ty_B%xU5oycn>p{M$Ehu|< z&8@)7B=rx2z15r3ImElSv5%0Pvc(~9HZ(jP=39O0^^9S+rNr()63a2k45*=0rygQk zyjS^MhzIyE1Oxh>DC2i#dJ9B-xBC^rUZ;QN?gfr?n|w>+o!N(n`;c&4T+5y@aUu0$zTwfCN9{mV03@Te^Pq@CD4m;G_^Yx7|SE3Z{rty0*h z0BSy@sH_#@Q+8=`a|<3gEj%2p!&9&uQo_gFXrjDf%Qpy_<3im@65n|B8j6D_Kzk z_6=!L$FX~l0X6&*ZHk@!gS?#;af6Wh^WYJEVvOjJ@4@wG{IG9*)B)9RL!fs6XHV7e zUDa=Zkx3xC9{ugwN0$#USYU<>;%6fD*u=4~HKKJmUJJ?2ET=SpAKG^f1YRB79R47C z5<)L|?Ti{xT6!1`ocI^(?MKT`@X;9NPIdG?`^{-?5TF}r>T*4hOO9>@kU+9EOdG-2 z%hU)R+aIn~w+>nhHjX2rNYrfzy-pC4sEn|e9>iTJhh?TJw<0@VJ`+24zuKeqKcDlD zSVMTo`G<~0T{;yyZEx(rY%J|8qfjb@n4Shz8q{w&Ou>X6^tI*^%XgO#1%zRsO?{wU zq#bs{9^a83M)n&?lb`=cUtNE&nYTp9ERIH}p;%8p3?Gr-LK~!wkVTjs5>EY4ZoEjq zZC|job@0WSgjwi!851f=k1IUMyM;w_K`1Z1AC{yauF-uzK&Oxgz!8F7&PVbmA;(*F z<_U`Rg}6>qWQQb+D5)(JdA17`Z%O1Vlkr2j^dRz+a~=i{Dy+J+B10>?IltV5h=I{-t?_Z?<_Pe_s-;UB zKa_+Ztjy-Xf_;WQ77Ov`r^nRf{&_x9M@WXqbQnDi0{Ov463E5j+y`?^l_|95vUJc} z_DFNyJOkvf2cd+do~O4XO)d}YefBT@s~ew>t_WtD&cuG5sc_U~c9GjYGfur^8?tpvqd(&_S0K$dhQB#Su*9(UutD(`ynXKQ>@Uehp1zU;Wn%=g_o%I~;Ihm&%pprO|K zVN?a;RU>ca3A%~bL^UQH_tzNEd!6%GpF;uDDyuo41B41-s;oW|?3xpI#qn-*L5Rpy z*a;C1Jym!pC>|*_DTsdwg{q8F8EQHve^njdCBz)u8^c4^?a~^v_nKdIKT$|mrQN)= zxMP{GjOaXFIA(K2VDbm~VG0-RS2y1d3t6r#KwlhE89k*&x;r-#GJLwY!}wYJlr);= zyPL5Il~FQV;j`w=Mp{pEC^3E&MccI7IIgjf>1Lacn{iQM_|qzps*k_2L2 z<)jWG;*|8mMyj8#ZbdL8H^c*7! zndt4I7 zK8myKjfnVTC?V0DkPG1!K#Y^|>&xTwayKE<n_cPQ+umyLQlYywRdA*QW$W5<#BQoDMoF^=xtLrGeRf5xP z;VmHg!O?l&B+^ilf5* zdOm3N_8*pO@uly)NF3PiL3TR&T`pR7vlOYDa95}H-sh%ARzIE>i?9Snt`0gMZ231| zycYVQ95xPNaWmp8YEpv*R0rV}rS7WyEfmNZJAc<=+3=Y4^BO391Q~r>L&>>f-4-91 zzyw?Qa%T{&cuKkSV@+loA5cl5h~=l>N)La^yfgF@nfR4QF-Ds}j^b3p-2jW7UVGEV zHxCsy3_;a8Pw@o=S|bC6ARJ_H*0cB?koRrbQ~4VM?b;b51?d_A>TB)Q0y+tL7%eHL;Jr8>nnfDH8r2ICIv1-iYCep-`@X+KeI3SYUa>-tVt ziiS$gogxg9JH!CZFHSrEm5mUYKiv zBVdiz&4P6&LnVaiHZ?f0^Y?xK>`(Wd-`Cr}@7!bijJ7F?RHa&}>p7tVTjB&VI!GK1 zMw^pKQf1E*5}MPPu+nq>c7Est167cxWr~2SsQ%sKpeV*{G!XeUWE|HAo&X1)Fs7%PME~?fJ25SQ=I7X;T@k0_gOxum%Mvdb>-b z;FjY{O$$$ZK>G^`J^!bj^7ZWEo z*&;E-%?pB0y(Tm`6yHK%k7a%G9i4`Ip|x*mWqdHB46N(?@3|5APe`8b8TP-$k$duk zVG&G68f0)*R*wkJigINCeKzb+H>DKzJI!Q|zBxEZD+VhnD8$pk}^qjbfTzKTBn1bP3A_Aa>nMtIt9vwmKu zju{iX`Do9Opv_f))RuoyhHQjIDa^Llkqo2ii}xk&TaP<#@ZOF^71ux<2qZFuAw$d5 z?b(V)_$>#qLonQn^d?{w0Ws{&7kQk~Zbly1E+t>NcA3_aKR?wiZ_&TJcUC;P3`R7I zq!{{*bfBkAw2Hh8dA=k7lDRmebA*aOdUf&qlrqvzp0?bAK%F}@X$*-M+9$6$#9&U) zP-Ql1Eo|O|Ms=euKy#$sI%(!A;}n0kW=*P6 zEErJq=l78{XY?2z`Yj3hVZR<&KH-i-2IUsg&)3J`DtK(4DH?+>q8h_5+E%XNoOU2tE?Wd`us=w!O-p7NKq2WIS{YFUf4@PqT;|+_Uk#U zX8!Tkw>8IA0%f1|(nM%_b=KKa;KQ)L4V>g6!N${Hs;q@|nvenr+r}=i@}7i8$QHZQ z=z0gZ3NTlmfeW@q5&KS=l>R#&DpclfBvc&i$F)_jxBaX72$D|zl5PSOAEj?=QU-L* z_a6QIEEb672hqd>$TD;FrIX_`A^>mJXY(OE^FJTtp?|U7%#Fkw1+ZWUH9n+Emx1F?&WI%p*6xVw1Seo@)wd9;NKq&VpxAyx9fGdxl!+xcDl>5FXm< z=@W3b^?eU9tLw+dkjvtqXB2X|zSIDEaS7_bqfPZuAOQ`IqNU3Z!kM;|1fGNdZoTyn zI4PJZm~<_0mSN)3A;4sYTv^>ZV2z+q@lXo$tOqsw=hiG-#SX&R`oU`TUwp{d*9I z5Z<(tM0kE)@?$}<;Y#9vkEnZ85;{dfMhpXKF!Pf5l&+@~IL@QWV{5fX^uQ83S|%ob zd#%4LZqM)r3f3S;D;~Ngkd7mGVlJU66^L6OH-cdtetqUe{rVE(bI1bjwY;|dHg|!{ zl*e8`M77@rQU3`i%qU^3%Va|tmn(+wan zj^XaJH_Gh2OxSitl3lJcR%L|6$#r$h(r=F(R?}Qf)$@qFQ1qB!RIZmR6xkt-5!$}X z=9yaqNxK12XFGaP>4)Zs?Gg>btwiv9FS*r0A{ z^dt%tzB8p5kReuv;Z32Qf?}A{d%?6_6O~=QsRnca_~0Rp6bnLY&*R<)dSO91b4Slz z*vtJ;SfGUbRtoEh;27z(T5AsPIa2{MYuC`VJVmMH(nt3*9Di>)8vdQCSNw@imxY^q2X zC93VE>MJl{;ni1-TxEjEsBC*7T9InulMquNxd0j=VCzW!_;h8xTKNB%dhc*5{P%zS zIM(5aLs=2$h)OmQ*@rTtQljLbgN8klG7cRp#Ss||Qd-E&-h0!sXM~VFj_r4Qy+7aU z`u+2|E`Q|ZJkN97_v3zy6jpq71QkqZ2X@eA2+U2_uaib0*whD*wKZMFQkMp@D35Wf zs}y)*oukoOgQNGu;<6iE(v6Op#@Zqaq+C*5EE73h_1Kd3Hr}rHd#U+4OH7j#*>>BT zQHlW=ptiK&tHT!C{qJC~#TSU^YgjGo&jQ*5lH4lyK|_xSS=BGu>2mu^GNUZ>E}9OpX`G! z%c)$Jy<)m#cHpnctKlWF+cCGFH3T+GA9*~S?CTHYPWVmYt9Z!6Bo8p(B5LM6^zN>K4mv zu}i15-7*g>5N0yn{kirrnMBY#k;#1XV^nl9F=&0bV!$!-*~^p^k?B%4szpexRh-D7p2pEzT!ZP#onFyY7LHS;_?&xOdbyJNg3)*ZgDB2RU3y3xQ!2w} znuuoK=g-W{`;QA>Hie+HH@-X9eqB4PVc;I2yfc_a#rngMj`ZXIo7PPO3Igy1Ok8D_ zy(;%5#8K<)Iq;8M^27?8E!;&&YXb$K{s2R_`j>JA!B^T`)IQZEcjE)Wt#+6eC0A8G z+F)(e39dWOahjo{k+-*kSPX<_P#SA2%Taqf-8j@LuNwjryyG`N-!v`nfY9xhw;62d z)hBwM@`3vC9QYGkewo$K-SAi43_>B|!hcSbZFCq)*Yc82oVf&Xizel|C!(G-eB~i47vOn=;L13b3=mFG}!`1~}7S&49F_g7A$p|8h7VD(O z<103pe70%`RUUL_x~S{x%jylw1xcC{MX#;TSjBO^jY+yh3_>_oem_b0&1rMD^MU4ufL=z)W$A}?D zFG{tmvTm3cT9hJ43U(6$<1aN({b!n&y?i$yOM$lKdHFURx@@0Rivh4LUJ;I}L*i|o z1}Sow-@#Oy?Lxg$$UQGv!3t*F$N!z}#MFM24Ch^#=xoxZxQ@vZd2$*_#|>VH_t0tG znoNABZ92vNtp7%2^ir|mIbh4MNZtnf8S;IV3eaifv0wz9ks%>+_bMm=FI%Sn!i|3d3e0+9ujd&Bs)B3Z-7dV8lNxp8QrSJR=(nQ&oh#U;7>2(OGBFo7 zKBY{HANA|?Bdt!OpZhQQLt;YZsejFl4+>jE>P z29f;YpZQA$^^oCrYZN_reju>PYi{ZA^g53%|E#1LL6YOUu0C2pC;ZonH8XoZIY`vG zEsp!q$ZS2FR+uCvdj!R#JOou4b!pt``U4WGz4&1z#hC{e>ZqEEAa8BRrOQ0@HkP#4 z(Yl$j1SFWy+0)3d^cUvEwnuL26Z2#>9E2~aRJcj?3#$iGEH>3EDAFmS0iB+0nl+R} z;hdiO5ir0{#n#@*;DgyBH_G+4SJK+!)u9C@p~lV9K{}j6z_UhRJ(Z_ETY2ce8C)Az z?o(;JR-vM28>C`c9&%i8`}jaP^=*${(k=|6Q!%3}Ud$Vxjl9K^K~px9YWVZOg|k=H zpN4t!kwf)I4KBvE|GLpNUvZGE@jVgYe=hWqPUGxDis1GLO5LKt_e7ZZl8^R(Ei85W zb-7Yr7rpAi3AQFPt56Zt;+;nUTR|W!&e-2^McYil>v3S)KMhAc<$ zQ21RBb2yud`lzihKj>Z`2v#irSd7!{hTD(4+{@T=m9eZo6{(@v0)5BdNrnL2z`02{ zn2h_~8A@6kLdH3X!gfJ_IPR~$p`gS6asTiMxJ45>bIuEtf!9h|0c9ew@VN|-uWE*N zzI_W7z}%^ab*mN{sLy35Zk`lk!5|VXXt=`>0nhY=(!6~Z4U`tLjek2Q`P2Ml|7$k< zdn(QO_p0)|Re@5%sgs}zRKq1OpY&+FDe8YU=3R8d9Sx2zDyChOX=M7JY;CPk1Nzgc ze#g$$tV}yUXh*Rl75`xgrL-Hl74&oJZ#(p~fF@Nu;e>Wj2sk|mKV&1Y|9mz30`@CC zZEM}*s72n>0Z0}oAc9Wr@o(etdLDHY(hm}5>A}5qV15Ts=3Ec|9SRxc783b$?|^rs z>O_%?Ic|7;bN(70)9G1^Kv8?%T8Noq{oXerc+apv$tMkonuk%Y={HfpthH!Yo_JCz z4RyFj*@8sB-LVMw;4Da#e9dX2Ql?XZF>T5R+H{KT78NQKB<^+i#@5+yR;m6lQR=HhEit0VcP$=(8a7p=*`l*Dk=YL+W+Ga*Yy`2+% z8{N=+cmbc_8t}kdU!6T#CAM9_@^6reHe@o!tV&t#ox~S z(ZL9b+!%c#6m^A2`sI<%6dvK6jyu+@{PagNy*CD7eT?~00jzeX-I+q`qnTIu;Z_?jfu})v zWYv8_X6m5N-xeT=%?&t^b`)F?L44%(uTLF&JHf&V5&z9^dEC{f>D8Vq3s4!LgPuv# z6ACZd))Q)}*w*DR$A8sCc}pleRA7`bz_|fZYnm_Np0u`8H)U&}Xq!j6mMyT=zVdj+ zz=4XQB=w((aqnB7LoB-2Fq~DP{9%n9KoDw^BlxQ()6Uwyn-$GY**P9LlFJTBxIFyU zx-hC>LF;tn!-WbZC#E5sm2B4Ld2|&Gg!{OA)Z3Y6^9TCR8;ntpVMcc>R}v;MpU;0r zyOv8CCzj(D>pIj&jxS4%^$vcN23Rg~sOn|r9)pzMz3>{GjzlP-rTtsNs~I1>;lkU( z#OaPtm6TwW*e>ZK&s(D(90|%v{Koe|i^eC#fv4hhN~MK{?&`c)fWSdilvEOp)Nl|y zuscthaQi`gm3}%esqAkot0ty1a;|WgJ$wJaQXO#Asz5@R9&t(oZn&r#413xCt~i?D zP!<~{lLp(4UsA%XD4EEDuOd0y&0bNBsd`r4<;U@xLHxMJ`Q2plSGvRD%m}?IF`>%x zzKz3T#s^oLq=;}os3f`NJ`GD?#;0?iI(*P0id{ehY>ZE$zFP`GK9BfaU8~Oo?4Nu+ zhG072H|IZZSdI>``txlEua9|leJxx~BnQ(VRm%;j0O7J3LhC^T7B4hW25*SZ8t)t_ zpjK|K%Tw*uk1vE1b`;CKs3HSS$BWXj4b~x@ErK>1XC;$-{~s4%iShpGt|PC{i9Ny_ zoVtj3uH2bF*)J0K0fIWCwfMmvb-^x2k@tg`X6UENNC2l2Z_>bINS60~CPCA0UExoQ zS3sMpqvSXVSA|r40+5^r{>yj%nX|o>Q5w2&jJhXyY-sUzBo(?oG@GVSn(F}|WXU;E ztoBX`0y^8)r|M>vD#c?1tl3qEY4D9+p4CK+EyImwB!K19&5S&8qc1X+)9uu`y+L)K zZLxza<|kw|E=B3sJU*%@*uuL&)@G9 zp)CMzXO4EPHH}&eY38igu_N3pOp;OK<_(|xa1p`oufp?^w}L#N+r9hr!hQ_0cncqB zBDqo47A~*&Sc=LRyOhT__Ey9Ue5TRH3k6g{by}0Ua4Q_<=&g!|IeX`uz6F{FsrsLu ze23XixH@-#d0&M*HlUg1#z_^OYo1CwQfb7;r{wT2CjLoitvntMy%nEVprLD;S-^>E z`gLv!k=3tJ{(xa)SN&9_4)UgKj|GuWd-{ZZSr-DMuVlejpBUKIC4g1cC3d#{u#In! zf$YkID4FN#8$rO+mq~g^09nc-v$n8;Qy?pbQNX=Y9V&WCODGM2-KdfgKPVXd6^DAm zfD313N_QvY=n-M33Y1=}>k+@V3j*TMP6yXtPu?5R^WWqmnWS^HO_}h;eMw+B-;BsU z5O3s%`?Gs%aA*=#rw|f7p=6VE1j$l#jaMVopk(VSx2++d+{Hym$%!bWi2Xlqw})Ox z?2++y?)5%@cZAxa`}%}VMKE+54whqWt6qx>|9CBj9T)#5BtPP2uJQPy65C98hl}id zR_?aQbdmSNRgtVWQGar1aCH^eNt74n4{A>M76kaL$~8`^GYBMx3=e#SuWvcxD-z2x zPD#`Qd6sw@f>Ie^K3w*S4YinR2W0SigeEuY`~DJ1$H52Ph!b3Bnc^U$*8NW%8iCS8 zr(D5}albU-0S4^*ik8_;lt!tRDdY|v4vgtO!ZJb@Aw`hX}{=yl_3MG)o~<^ z(5S}fbh=^hqvYc&n!g~xswF($M5ra%mzB`~Gn#9y$QyD}B+H^a>%}(y-|5PmOXy^~ zAn7M@T&V0>ZusdUiQ?<`LhjS-+)vALN1g?PKR=F=PqLl!S%Ze|v#yoWb5)us#fAy_ zAq#&tHg#1R_As5FF3+utjRevdBn}5pU0S_b0;XH8(SZ^zO)Gf0HMCyUu(4waV2)8U#-n#FSs4 z#YK!qc1qPUkpDKrtsdW^&bUAIVr1s&(g>YDqg3fmeGs|$^`Z~PM!@;qGBlk%8Mx(v zG9Ocn4Z%x}jwS-}r|quS!4;Sy#3OXcMQt0053(dDDW8N8B4>C09qD^4*py=H%Wyf; zr$1OgEB&dQozcG#{eshN^9GYfXopm&Hka#iG^mJDA`^h|Jj4Yo&VH#)$jFK3%y}V5 zdnn}9=fNu#f)K@xMp-k(yb9AcJ&wqhq9*xyhZ*lIMK@^)|H<}#`m$RcVY$tdsn+*) z{4-#icXlhfj?=xks}S%=Qy`7$?8$Fu|2`Rf`NQnXzeBz4_o31HwO97(=LEc{a*ZXC zr$*k*_zK=Kk%kRr%Ajj}$-=UNO*raG3nKZKsu0WV08GOYpqFm0<4kyq?hzVV{zuV` zHUeofA~`R%ih4S3G;2`bn~a6h>^EF$`xlv>rjS_egQ*w@+OpQfo8#GMD!|1xH0tW{F`&%^F(Ho^^h4`0I3)l=e6~J^&}T7*A8p74PMk z1Uh1Ki+PJ}e4uU6r{+;4E}}!k_P>91CjUl%xa&aw+T1Bn>Ud!D9Nwr7*_cxRbbx31bHPdU37G=9Sr9OKCA+&uzuAG5^-zlY?>vE6q zM9(lW$6%(zOBPp>FfiJ0mM+_%o~&76z8`g-Ns?w`+IM6_t}$+&c7ws}iwd8jG|M6q zr#3HhPB+_}w&ciI@Lz!RFGV*34hLFVv@{l(fkvJcFk@>e=oxtcmlQ_ZXeBhhw$ zL7>NG>>S;Pr9<0PxQo@zETj2?fyDb?HKIFz;N^LP1LVTi+ao1}HPFmmxKC$k%UW7W zyT+(CW>+(?wsMfSd_9kM%~(yo$qrhsit6J9!!sm7~sHYc7o4K4gKTl&yG&S))pym3N| zLMzcqP zNn8)TMwI|O8mHjszuA(|PMfmv>?6a3@0m$h;=9Wl3Fgqn02tlxXU#XjMx{sJrPMKj znQh74hZpM)xi4hXgJw)V_@QU%STYPOXBpBbFfI$i=LLelHDlx!PdpWQ$Ul?Xz**ekstcT*T6@94 zID+oZfC8Ni4Q1(Y_0+|>`}Z_6J6jQYq8Q_{df}+E>R#f0I1bO-;{E9uJ~oUgPc>!3 zswAp2kP@*bC8uFwxs+|KJCcqWn*x-yBT(fnA@s6Tpi4#7#r~kXhHL{;M0r{<)D(Yo zvMdjeHG*%y#7L2hx&gT>88gJ9Y@0GJf9lw(sPNxP3I?SxRXp7&CaY!e`+LLsK}*GoywUwKi@1mT#V8N z3(#Q&u(DZ^Iw=YtDnZ7K!oT*N7UT(rh=-e7E8csrfZme3qGsI)yX%Jl{Mlx-AHYh{ z{J31LBxv(0Sk9o5hsL1LPazLX+uS6}@F|Ib z3p<2aP9k6fjP}iC%%0^nG3Nq8dZ-Gb8@_>!~2U($N%4qx>-B&laNT=7->;KKE z4yX>>m}sy+XVW=AMu@at*d(tX{%|!Tdsy&v4KetVnHOBv#!}fH>eOw zm#DycEkY;j?7g7l3}9Kogy(NIHWF&do%Mm~pszeM()-O(ua0DtEi9m?8)LBvoeKSeHF zVbKwgALjL;tlqD;K+*Jyp?u3$Ff+!_yeR{G2~Cs&xU5(M0;GI+3pZL1FCG2ZWoEwPG$N2Jy_VrbmQS)IbEa4(6-G&n(~KC z8!n9LgpfhHuNtWAC)xT$pi=+Yio6u@)miKJ{W2VEaHIp4?0KZ_iW(P@#Gd=+H`+M0dC}XzOP^X7}E}%anFikrHW8l?-YusD+*Dv*c|2@O&c8P#B z>NLt#UNcEJCvff&1C=GP5)&C#dpgU69@ob0W-a|Grt)#j#;#WzO< zY`)y`O!5<|nN*WnY)T@G&@Q6yZ_NRiQC1JjG^1RK^M717p(J4G=<4P57gF+(8P1(RbLCC{5p{s`f zEMS`rOChXU*RZv}4~qis3Y4@|fW>Mnd~67B0oy58K3_r6fqdV4Y%*Q)aZ?>q+P`T= zPZ@ul&=0iH>Mj7~} zoYM3fD7_}72uS2L!GcnOyT^=@{BS>Vb`s#6(g`S3I?el^cis!v{;y*^YT?rN5umWg z_Xn~2L-y~CKrnj~jTC`~!rY<&anxANGi-Kx?7r8I`S!n6XtrA$HU$K0I;8stt+%G- z6<+ecyb-l=ktaovTVm9OG~g7_c6jNx{DRA5_oP=Txst9w*nnD^Tj-7xX3=-_Bh4(nF$OmCYQolhGa3A#ekvlUiRa^X2j_FTbx#sA^Pww^Y$vLdr+A+0zx%I}&9p_24F57*_E1s5w z(EC~M()z4h2mjkWx;-cD-*Lk3-!ZHXM*jX`vMs;rVV4l_hFtU}zhU3h-vKDw+4&_(<8n zv`A?q8>vU!uMwn$-)hHRC3yIJ2DYwkS>qgx-E1o;-7m*Vt0Z45fMLur)MDR-y4c2? zBUcltuI}{du~ulF15Hxe`XhC!=hfObdf$(6za8>ToRI6~Y*pFuF0Z_v)Uwd^lGmW!I!?|q|%b=)m_!?9b>iN zk|Y)Cyqx7&F<_Q;$U-yo45~`A-jU$+D+uz`J|ib#yzgtf3aK+hQ@Jjg#c|WNAgKl%p)2jr-PO)_4OzjhoeM zL@0HZ;L>_kH4hN6ZUoJ*I6~;`xszZiQGXuhbvz2S-*@mxB8azi-*As!*v2~<@#4~R z`~$f7Eu>Rg5xvTZXQ1w$m~^O%=Pi^w-^S#s9qT`@`*$~35hHbj^mCRp-63oUg#YS} zv6h-Yo8mutzHOc#a<67sKFAcA@yp?#&@_TrON9n6PpcgqF9sm3Ti-if>%E853{I$h zHZRAUZQN#%OThoHAE7eiPt5$e;QqD6!tpm>G98Y8Oyb}D<29I*2pO)p2tp_yo%PI1 zCO`{S&VPe;ZFd@oi`c^~q6po4o0_c3nCg7Zn!wY%DE>B%nb`|U=?Vrop~XkwZ}(hVVL?Z$i9 zpQ455cT%6d<8Qhw(W&QsgnXgf$5P$E!O&C_^?9gV^N@{F#`2Ecfq(g>?(F?L)XNbI zMSF6KXy~gZysOh*B)5DF#00|>3N!H^Wv+Zo9zr1hU)L}p2olh|ijX7oL*T@TsJ)xP zntbf7G>{#8Z zWzbpw1&5L}&a7A0*E%EaKF%m=|6O#yt?Y$T^SHU=8iKXK`|_v0SBtjVcchx*-AW_- z8Li54`3d3J3GkNuWz!tD?4^!>Gq^?a@w^Cv2l zt1~{iFlo4D2?lNzfu!WrOuP7+nQv+ESHH{u#xw0Iqivm+ImKG1O18`A&MveaDO0EA zV41pxIn!#`*%FILadqY1-=GI$HTmMWvu9~f%qc6=yKy?I9fXM7)%Uzihj_Uo5=m$8 ztJzEOj9$>uoc$U&BzQjjs+<3(6{d(V2nRe7k=FVBR~H{;qY~4yTgm?Sw!~KnP2E`0 z)VVFZ#{sSjT%!uh@ngON<-lI;`y6<0SS^6#(f^Z$w zMF;!+D>lvY9YzJ;k`F%P8Fmn2B^XrO*5b6P7J-wvGXYe2aN zh0<#|7(b6i)Y@fOF)J|kDqaeF`*9EGWl7S)7dG-IuR#f>Y9h@ONZk6UyNTcCQJzHSPq+)K~gOZZ6GQp$;L)?*<*ayIbd( zLJaK67=E^-LpWtk`@7aV42|(t+DYS7Zz*4UPZu@X%*^&;Bsqd_jHS}XuJYYzJUdJ? zsuo`1iE4$FlnDK4gzhR_h!eXbCWy1I(K>9Eqx9nzr`H9{5#lrl<_uq5FvqE({cmg? ztoW1~h>38(^*pf;NyjX4SF}BKZaABU)iz&6RNd}Mhg-U$#uH#gjubDN(irZ7`EIA< zk?5m{25P77TU}6J#7>B{qx7Oh`d`2QJ?ta_(m?TB7XNt%Q}phJt}*$`ZJKC zF&=(_YMhYEIeGmN+#-6J?^beSS2Ru8-}vB7Q4Iq1#ifUG@%(d1Ecn7)JR)D?_55xh zGuee0L~cULRb!a67<5^9sSmx}% z#c9W4Dt-K3aTfbo24dPXgTznI2lDvbYhrGawNlDOOSjUw6sb5Ws31#Tz0q~izq$f1 z`0}OY7;U#esFoBjL!_c_C@()f>f#!@(KF*W8gp7T@rh=GJ&Ms z<;0eL`ms09wzP2L)%nL8ez~8CBp2EO?OYqf@k8d(&(5^-R-AH9d@01jj$no8kZXRS zcj`&`{8;jd@16`C=Cao|4)Mt95zTKz)8T!(T>=u7EgIf^cx8ma&(8mDD7ma+Uvz`E zNZSlGp3A)|I(%M8>}E;FuN}ODipFNF*2a!#rdE@=OrPVUDDjy83PfU6`~y z|I$mf5pZx{L?dL_<{)Olu89tp`lbf~36XqFabA_bF@@(phsVeJ;UrtOl68sR=~}uk zZ_+|!sjRdK)L#5zC9e;YJ*qKyjL2m>?E|DjI;L~e+?R+3`c-B|R$=KtkbZ)#wN2!S zB7}k|!inLB2Lu!=vDMFkeC+XW(uVQF32AM8tK8$)V$k^L{hZ`iI*uBshAmR@y*#LU zDJ4i!?H#X{qGyNavn!e!}irX^wAiFMvH`UU6bp zMc)RHkF;as2q@Sp^-|i)C5KS~egY_P`38W%GxcoQ9T>kJgRcHxz}Hbg7*l$rT{oW9 zuAr?un4_Niu_WF*X3uyjb>wK22v4dAqP1AFv1nso&^|rbs%Tm$ceIPos^ER5R!?`I znx=5D+v7K7tw)-ZS^t5fCMelqiDfo|6dz$imQ2+{U!Cs%^Qge?2$4IxMkz-l32sUVg6E;R7!&^K& zVCCm?1P}GvZG4W-&#r|_7NBV706DjYZfu>HdOXWPKrCSncfudCskiYqYF@+Xn1rO1 zY-Fczs2{E!D-5uou2o5Z%Aaw?RcEU8RuECMzuY-29Vku2-hQ8&qKN4{pNzGaz;m5+ z(<5HlyQ7xYn1wmE$U!dD<#2WW#uM0)D@0rSk@obi@4cn-?B!Lp8h$v3ADoFxCMk&X zRD(8)Ik2;+AK`W@QqjBVX`A6~B&4;B?)~RNhcl-lcL0pPhSkpdWUMQcc1pA3h=MOJ z$MI7*4-Dd5Wi`8B&4au_ILBrq=)#29%Npc=#%Q#w`*abK`dhnQO-!&SoRFD`_AO9@@L_+wBeI>mXR~rka zf!@BQ5OV-Cdhs___CjnBCx@B&RZ^Y!{%z4*?|Mx$(E{P`(Y<~+v^;phbv>x? zg=$N5Y(?Ebu$mQ$`l6ka-)%ie5%z?kat?sXPXkj1DHuU{@=S69ykzBc>-ZI6I}t>&Sbvxgb6|oWh1}cn99RFQ{M< zb?L<$bfGPetoWz)_Wee_&{`J{zwGAwgXB*`ZBK62O{}aQ?wN1f37Vbp?pEWH6Z?>3 z87vJW;Vl8Q3ih zcqC(IFql%&;~l74h8<;e*rLwzD%M~!<%~`8|KkGO@oFya*j3F|<|lTw#G|LUZN_M1 zKWN-1%s0Wdd2K)9F+UnlBjivXxD3%*F8q(=G}BzfD=06RiqpR5*jgP;yHBR9Ow#HR z;R15omr;+|hG(s8{`h>JF6b*mjtfZ>IwMXc-CvoK9 zFWXH`{^QQ+9b+f+OgA2jl83W(6m>CA&#HnF%ZQr%AayzafG4i|H@A>g@-;qWFzjnj zjLn8m8Ju<#eeDX&U678fW{t~tzhpp66-PoP1$@t6vKm7AQBf1I@VU(r-fyi11#AYX z3>?%%c_zICV=|2j%Kh=AXl+v~kVoJG9^KKRG@j9I^c||XFV`A&g$>iO6f(p8c4Nx| zpCO2)Av$Dz*uOhj3N0&pReTLC*>CROyvYSgPLgJ=#d3$~#u+#Rdc=;P^eUx5(?7l{ zbdO@zoC8z@$NUZ;lc!CSGrz$JsZg7s*Md%4KX*kApgC~RIda{P@91j zP9ZdYfE1M zvutXwTHw@KhEO9Ru@z;Tzq4_dHhhO#AY@t=Jf4dA?Aw_|Rqzvm@p@H7n#vNArgF*^ znBzP)7n{VD_4h*S>w8L$J^=vORPa=cb{9yk__;dtz_O9`i}_~4W{|@gVu=AI^Gl|f zfC}jd1ipzpis=ux5KU>RsvQYcpB~@tEoIFkD`mNvym=i}N!$#Hf!~q#oHky9PqPi+ z7?0PJba=xVIsCAhbHHF?Pry0wQWFiR`%O4xL#v42XWmwul~c!1C%&(`_-_+hGesV) z2c;zC9Xcn3Mon^aBo9VIn(sgb7nzPV^}SHk`{K(?_;_ThT3mKJ!d1|D@SE(#uyh6l z&E>8}Q98F95g+JGO@zcIl!czJfd|$9up?O-aoGlL2Eg+=kJg@y4t)3YfO;dsqE)Qw za3q%8N#~GS)5LNuG=hE(s#x^5b%zBLDmr_x@%^zVkwdF;7U^`j;+cd^dTS0n5RBzM z5+J|&ObF!!PlUQq4)ZGr8;4;aTQhA2wbHv(x<2~7+L_|$e-i@D`DF>fsFi8v9=}9` z7vC~=_QUbdu+$LFJi?I93*F6CitWf(`rXb<^H!hOX0-F;Z0aL9v$K-+WH9T)*5++v z9!_bDL)tM0@0%~M{0Bx0m;F%t!yhz2(ZpH~^WsIb7w@UkLoh!3-7E;EbcqEBU3ONM zs}0}$aL3pv*_9Nv_VG`9pvBsgJApub{!K4)|60+xPVvEYPa-6cvsB(;_06Z%i47c= zPZPksemN^3l2a)jCBJ_!WS$Na?>9__l1(0CBa;DtB-57Kxx`i@#|(O0@R^7~nBmsBMBj|3-w)`3bzw6e>dP?NEzZsW^YV*&^!RM;m1MZ6AcMN!ydZI@KrBXmm zgPweUMR525H-hZWVuBP&uWTDrlXZtW&z_YPG%tGNc?BrtCer&&Px3hetZ@nrvq>u|&T zx;V~VedN_#9XLA{ut7RT=doB&r6Xz!t%(lvF_52usJ(1NO2x`K8)l8brAc`g`+b6c6 zT|6pp7%{U&(q!%MV))EFWiL^;iiYN;$m1J3L<1;bg{>|;-L^J{w@m~ECr`H34>VtQ^N`)M_l+jpZM!ZaJ zw?8=`He6~1t@@Ah0dwXf-JnjZbB8O~J7oC)St8I!VpJ)~=A)pH7T!9udI{JT#$%{`r zfjpuZX!3H9iA)r5Y@6a9%{HGzNq#me^IiD1b(KDz`KQk3CPZ>2rr8Dy#p{@g?Q+L|-FECxO2=NHSbjVE64_^WgXq^iHb5*Z@PRuvIiU{rSWiu2?o6&kKuIyOkPE|Ni zEW`JLpv&(*Exq6R>%w40+Tgf7QFfq$dRWDUqC*w~x_)wocRa00+tPBhPNV2zX2F|l z`u`2Y2+GZErtJSFW3>wuyJ6RAloZHnthn1M@Pxg$V?Y_~(}qs$_x=B9;0I9XaS@ma zQZwkgMq%lOJjf3jnrToGUBf(s*EAlv3_X&=BaCV9fuk$?{DS0dym;Vk-}NAed;116 zV`_!_2kHZq9r74Fi3S)ebIQV5J_-%AYv|9mBn)k&WdJf{I2=Q2axJ$u=ibyM@=ox; zWg^NrwutVXHFjsHS^rD}tyN^Dl<>iij`=Hm7|l}b8-H$wUIgpFJ{6q>jEWaZZqyys zCN1_BmrqhM2)dQ&@1Y>DbL3T$eviPB4(!(>26a-xrvotEvciAP{iYG}8QSsHBgzy! zoT3r>BAMVi^e{`t{#m6BB)h>JWp^lAe4P3{R*%TQWEHjX$CJIqp3;~KCqsA|tu@gr zL#r+QzKg!;(l-keCj6<2^Yv2X{zkqRhOgXL&@vLK?Z1Frg}*%Mz8=K0$&;skpZ?=X z;7$}W;6lKFng=SR#Zo8Mk?|ugisXl3tZn=0H{MPPnpATwVUZ~M$fq8_Qf{}`rAUE1 zlFmAGSzZNHbIRwr5^8cSGGPrl0&(ImOCuuBJ>IqbHlAM|cX2BSbNn$6hxwKSmRk8_ z-8I2^<6q<>&9`Yx_`oX6aQ~E?7+q-8|3Mx$>eT5MX*Bp?u_*fT2SCwk*}01db=|q; z&i5IZZ@)z2C^LD34P13z4e3;YQQuo*G}X^JVGy&jn@AMhd$ql9tn@vJ1`Iox@MJ#@ z&qZ{CeWlGgmOu{)8zg`O2@K>DelNi*@})=6$L&J~(m2mB@ev0&SDK-9?!0hOGvVpB z11Yd?K7&eqM&Fwb8w61vZ{kgb#AGTcb$Up#NlR^Lzo5~o-g#fpcCBjpwb}M_F7kTJ zyG=cDHUvm0ejKeGGtW>q{8l!cWO%c1!z&r2UHaH3?io{VJu)aCgdc*&5PLA7E~cxOviAn~N+8q9|W;~Z`Ur#-H@E1g5w9Na=d z1g~TRK`8B=QBgXP)YGkcv&_q>0(14?3RETKLngl9UKLc>dHr}mXNw$mzh&1QI}L5> zviT8GmJ|xpvA+)vjenfru72_U&D6~lh2uU!vB3M4^25chPOgEH-&}v_JzN95f<8!9 zdLUyIWT&Ryl=Z{a)_I}H9u@`Q0DwUns!3}Dv7dAsp8B!}l*r;5O)mB}dv4u#m&r)O z5B{OQ!9CmPA04jJT%Xvf5jwsThY+S@%z>A!cAbf6tp!foA}k$dadf>t%vg0g1der_ zF#uPa9s}_M1DWSghOeNkFW9d!I25GcixS>4;1N*oa8S0&+y}?)W=FnsX8WOzMcGEU zX2HiqgkB%a{kHsy`kPs!r-22a%}g)DlO4`5fV=+TDWfj4bggYoDjn{z-eqn~d`AQ! z?HavieAf)%80i|owiwM@cNIv-S%qe5bHS*yW;DSuyH-5PGY=Vyj=UE>%RS1Yz0_CB z&G+;q%9Tmd%z^72Y;Z#5Incr;q#;nA=?@%uv~-D8%VihjG$&iFat~Z4PdmfD+R(X8 zi?2s$@e@7QN*9m{ZZyOH}ltG*84Fw+*{t>9uQtnD7}_Jz@gas|?>RP72Im zsyWP4)^d1EyiQ^+ZMm`tPmPn8Tp@HVvMcNt|g(hO2@Ce2(zK^IY#b zlwWrPlMg8j+o!BcU8I5nMJV$@4;ixk$P}y>E?Cbak*1|Jvl@`BLZ9bYk^_TOVvJXt!w%{dK1NpPxiMnBhW*t zh?h^@4hC{lxe&s37g*SL@P{fcA~8u%58AQ=+FK|u`XBC|(6AHvONv-Qt8jN!zWc5L z8$|k^GI+{6uwHZUyCS6NwD%ICuqjgPLA88MVgt zoA>^IW}suguh5`GmaS3XGu$e4RNHgu!VaDvN%Jp35x)ObvngPjTpVr@$$IgShpkVg z1FAxYwT8UrRw~-$I*Zg5 zKcfPBJYGUvBh<+-;+quF3yJGY0{M-T@w^zn`&nk#9s*cN-mkSqLitcLyD?}KNQFku`E&+Y%6raoN!|bA8!6~db82%qb63ZrU58Z?qJpc$M zdv*%I=WFlDIm%*vJ(m&c=4bVnUbl)w$#Yd;xCebYG{3*p?n>ASvXG3ln@U+(V&8zu zLi!N}w-MyBE*`2AiTOgub@74NuL+4@?JgBs){K_^L@V7(Fi`n$_7hS;E6M@_9E_K82TT%(&ojk2_K;?&o5bIZ-;zwZQs zLcLB`IHw&DNvNQ`Zrt5*I(VmMUQ>-$Crlhinz50y-1wf}h{#?*yIS%bgON^qU+=|D z9ipG3q@q>m`Do~6R6Z1e3}jS}>GdvH(*-`O!kyWqcMUi{%a-2>L(g-9X*a53l0lJ_ z5inU$`Ma{PQm~NFD5UIU_$4rbYd$!43BA~>OBIdkl&Mez0)~?0$GR2tV&Se6c5Do#r0gBj16`hK&iO^YdE35#)&O8`&X&u?QV-e=x|F%l zf_u`@m2TLU55Da6h~#)6hzlK+a28Z8Sc^Ep({+{ z?NZIrRP=94#{JT8)c5Fb6l5aF78644?>WhzsNyI!yeNdude+!%^j&mb>IxM5JlL}f zZ|Y%`bZYqXc}#V--uov(7dXsW$LI40vE*XHc(MPy{QXdbmW>}yoazhT4(rT9+uTJOEU8$-jI<$enoemDw?PpQ&hAGTyjG71O`{85mIc=DxVePC;qqcEQBdgABO zb614!E}?lHHbnJ^y$DK``Xjs8z~4KrWh&K?<1AePnmHz6c)vQlrSm9CbT0w!pS!JvPJST$OVcBvYH_Wds!Kxo-> zTJ-UX+jy1f&&voLJ#inHezwSUFqjBs4K;^f=HN<@6)jGF3~oY7h8Bs+*(!z5Gx2e(a&US!0Lzc5(*`NrIr zYcga79i{YV=HUn>sZ>fqhSrTMI}#A4IzeTcP=Ei9%@_9YQJbyk?-W>OT(k>9z(YxD znV+$|v#897kbP`#1tj1t*z#O2{enW5AFtvCLm1dQLYKvn}Aq0AuGYI9nm7y z4u9is6mt;p`^?sX#r~=Ojd4jrC9SKLB+sT+xAAKY6#VuJurP?R#%=t>D4hmET_dh> zry%i2a-y+hK=}KK<1&DVa(iBeeMg$z|8g4 z0H6uL?ciL@JWqsjobNQ$3Ix=9<}h0c(TGAn+@Ub>W`TNpM_$oSDM;K9SmwO|+r9@) z{7bvLC%#`df7le_Xgt>TmC9GP&{36{O;lIFVA%Ck03W!w*ljr zK#$nai*r@hoh@{G<(EWK%Hsl4qQ9k?{r{Etq0&O8B#nadGqhppu&I>Stj+xnl7WW! zl%Pwaak-b(+YhFmxcvG~I?0@Z#PLa`EK7CPTEv*@*nJ(v8}HC07u8%?6brv2Wh&~m zR5bf2PdrBPX2PknR(@TD-|AX%ZyZUuA#_cxa1M-Gds=-T`P)OW{XFbon^f&Zq44dX zW)S*IUahq0veX4ntjFL3_?OM-e^mqtstB(;nHQvmi6l{Y_l%HYJ*AU;8lfkmJl(G_ z3W8WRkCms8pz9}VkkqEqt^Ny`B3t`U;B7VL?^Ig*ARtQekM-SjjCxCGEJX+g75uQE z!L!Wc^S#xxv}5Pv=FefOId3hMIRS(4z4;m1C!u*&#)DGA7m8qgLVH4vqhw;#K}46J zHB_O{qB;)Jf{FXl#5&IQ+eZN*8G;xTa{sr;0+#Ge!{n)o7eO!5s=Ho$JQdi%9}7!| zY?RA1sL4`!uyk>i#b0$4xPz_jaw%O*;s?7|X);ed_i;?l!mNhY0-DCeN&vd8MS0=; zs7B;p&1Aa&oWPJ;;>dp&xAAa7}0PP0&4#SWKXTt&B}^ zd)w;b-MNVhuK-Gppq%*QX8`xCU!=k3w7T!eafOMu$SA?X4ZJO@_->UhcbJ;B#T^*A z*qRB}<@dQ6m@Gsf`j{Ty4tNF~?$?qD@F)AF=u|kIUL1uvTK6@!MCsas-lKHSJ+QSG z{4&ATb!+ZzbqF(J>Q*#Fx;>L1^5s9_U6DZGZxH?p8gYFYOuUbvAm$e=CvL$cgEw0H zYH0p18p`EFf?$Wn8}?TwhTGh^H@URaNOG%ac7P;;Mgq7U;DP^Gq)(*rA>4(Vd}5^1 zY#U#DaoSGir}IdA+dfHSfbfjB&koi>E4mT?&UX@z3po2Xni!TI3J9=%&7Kn zKA&c`BUrMM#-!(s$KHhvO!64yH<276$8S)%`!dEez`Ou26rK~}YH>9<7LCVlyhQsjmRW!U&BU@JPR>PNt%!Sa5oUbd1{z3UdYV( zwopg-l8V({y&j_H!2*#XqwLlHA5q^P&-5Stzs)d)-fkNy_Fja-NVyjCMv+L8Ow4Vi zt6aKJxo-?zri)7ni70Z*rL^2~SyU=QrQFFawX zhsw&MgXc?fMD+V(&8c;^N@)}^l5!(g$*X{bz4sJIQFh<|8Y-z$a+}1c35C6K|0DGP zMbNgo8CbuXo{%H2yR?o|aO)HaI~~*Jy0A`>@6A$Y_~QzRJ-+h@ijy|{lQh-&U$b}L zEZAe|a}jf60&dG2p4z<<0_-QQ_rG)!XpZ}u2d#j4!44aFUJuepRk~AqTkB>KNw-?XpBWsQzd8k2!DS3}`L0|P=h_-PopmqqbsZ!EtrIjC$cHMZkiY<}Zik7AS)@z2mX z0Qk>Uy$gJ>PsYrQ2T*1iQ{M>a#%z?3;m&5I7yny<*BR-(q_Q9U8~leDq1l$N97#+u z^W;ZPK62#itsNra_Ffxq>>+GV&c~2;z5Dp)jypXHF0&Yf-Rz#r3?UuA%D_h} zTs(DLCEpC!x%p1OcPl8}vFBBdN1abk8EPK6-X%;O8Mys>8K&}j$o#%n6;pZPa zowQp6)@5wTe2aQeTHVt`kRQ2RbOAKQvJID9R}hDr{0eeI$01eJO^4j8IZDSu%k7jy z0x&^ac}J{9_+j#?mjZt-i`x%hO8KppV9A%jjse-aAoc2`0&t>-t2y_84fk~ZVSeL` z0_c_R<(!xKNKuYi&kPc_z)8`-f%41OeXz1=^#8N~HKszZ!YJqzC4n8T-p@}JsD9Ru zwCK1Z;_7;-{iQ*V5HOio*ZJ%Q7-1;@7nf$JcLJm9^G0TPBzFkp_JUxk_>Rhf7tt_C znTLdVorNidk1_Dwl~cE8j=d08TMeuhR=iuKHq&^y(WDbFCCV1*;{&ecOW;8Q)p??Y zlXN&XsB_&fdEj1358Zk#+WoDVydK|&6P0j?$`&zVa(DsV-5?=|pz#ye(W)ODezQyF zi9P&Q|NPy6^uMANBT&?Hp3&jY6$5=D11-6St2rF>qemi@6Lh;R-;y4`0ve7NC#*ac zBg^lp%0m5y6~(0|I!(b~`cVw5pje-{$(|dW_eoLv?>;~Ax+BMe;FB16 zYGm+soIJ?3RMcbuK`(^JOa}Re!Z8AV_F(*zQRR=A_rN`e5bD#2u*HPxLb}q7S|?EK znU(skAHXy3yWCDHbI&W^G_`_@jezxp5SKNux1Pl5hN+_qa)i|R^`Kvs;QCzX{Qe=n zQ7KnxaB<}AAGPre6e8U|_Jk6>W1Px6x1PhsbVIv*koZ`6qSPMdm9QTEKh(iOi4J3r(&+I zFw!I52r#kcei&HStH>bSg0R@osqv}g!CghzNxOa?4zy#8HbXy;0=@yxAHVw5pu2Lo`+^PGkva6HeQH zICHeoQB|{K6R!A2Q9v#975}9$MDv&VkYwGK1QjE~bI>dYB(%V|wO>87cZY&e7wCV^}b3S}JG?%>zH>2UB7794seuuUH^p4O; zz~->mj`DRP$9zD^$G;RWo*`pE*)85gO86|o|GhYL?RUkle39327r8zf3!!J=h#idJ zbTGG(n~r|*E63x?Zf+6)(!fZwLyH9pS>#RiCtvTK#?R2@qQQ{B+c}_vLyGopn>Wxz zfmZPG1~btQ&wZ!&Oc3jeBhF~yzyN}_>m%UF5QJU%AQ$enP5H;ob7BS=%Qo7D?{v&} zeHq@>Fm*oXhpQ8c-Sp~P2QUUX-J=3jH-3eA@$U@mfR$oCNRC2phPcu!j6^ijmxpr? zTf}Od+5G(t_It(tuN^55Q^7YJ%z4{J+ucQB8e9TDmM`MNEp`L+%K6vw)qe=+I(=|O zLM~I7q&LMNi@nJf#Gx~7`0dCG0#Lz=)tPRw_RG~5rcs6?RD*9jp8Pj+Xm$vHVBrw} z-s^*e)PMKMZ#SB|KJ>EvsE)vJV9WCKZ~iul_7gL|qMlIM@4Pr_2}7@@?Yb!Spyn$5 z`=GqchI&jHs2t<78~=jMw=@Q7zM1v91^3*gac^#r>eY03_IvV3@RmGU&Ac!8!L?Z|wbuN}q@=;BT4D)TR z3su6PP`6LcSpIGOE#ub}e+6-oMb=2wf@C%{h?7H9~j+7Yj?}7yQ{f;`CxY4iSQq|{7@zqePxS@Ko-=k2t zJ3f0X@?U_U#&F_!G4Pu|Q^Z2v=9o^*Jxm+F(r7Fc@(j6sNQq-XNM4#i+I6YU+n(?9 z_65N2{=)FjaZ{^jz>rb?l@O_)!F$r>o@gBakMj@+35qwqdgtSkCcP1D=Fx|Fi^m!K zgzKt;VoR@s`YX$y%e5s)ICudTSCy~r*wh|I(ysp#VE2DYKIAcf@Z0w$knNKG6n|0D zwGV0e^IkOg^g}%qT)t5d8hW9E(`oRLOI!9`_xMgtf3oVzvfJ=?_rj!_hN~T(K#~O< zfyUP4{cfMVYd?|_;yS;<15}go1M*!R53z8n`+HU3rgkSAjBCBNlorLO#VmWIXjOn; z?PSd1%?x5S?0`dgwzqxYU?{x&T7>y_V*37{Q|q^H05pwf zq+udY(b-=ei0R1-cPixt_YUtC>LERNly^}>-Gs2IJyQZ1GtHy)L z5sdI68$V|xZavOgGTI`M*StV25P-Fg-r!L{00ljcOsU&PB{C%{{$K=I^aLuC0`xqyOJ z>Ipx!zKx+P^R*PROv_dCWq=a#eGCg+ObRMBy32XbB|8YFf;zaICzf!>$2L&Q{RAXl0i^B68tT7+RojjMRf#8P1T0BI zOVuZTCg&Ra-0T-Mn%Z-lXLJq_#48AU6Mse??m;5p_Lzx03<#hdU3*3)>nIHx@iDWh;oUd07eUC z5iDNcDk@?a?Y8fOcP+@n)k6145QM=);8KXcV{wRxEB+FG9XO`%iwM5@0jSyDH^I0A zcnRe(Vu`DBIArM;3rND5vJiV9pe)NHDO+p?aA4MsFd>khouDLLE8gRVxUUlM5<#=BcN``{nw-O@P*m>VI2s z#R8G>L#o$#o(7tdg<>^` zsJa(U#c%Qf_uFs-q=tRXqUiRB-~?JYr=Z3G+(o^WrUU$JbLOY8iNHN$X;rF6=nI1- zra{F5AWV#!HL1MQ!>~=`JTeBHVar~*Mjfsz3TV|`8;^Fufv%O$7}D(*ali*)J+tdi z1#q1LRF0(~=;rmW65{hgq;BtYazX>voIB{1F@uu4Ua==!rt;tsQvJn)?J*XFHlcz` zqX2Vgp56$6WBvcK$Aqe6peXyj(PMq8yA>{0<5$_vzzJ}>J)4s-96v$Q)n!uW1z^r{ zcO`E$_h@+Xw^wI@XkvMJI9O}*&s&E<@tixe9>6~Bb3lrjI;rm{w=K$&x@%f@qVl>F z^KTs#V`=jQ_)7^m*|dx_^2fE5%y+qh#QPd>8!0<$+@7FPlMFEa0MM7eeX{FGD-h0r z^1gF;Py{sY2fLQFJZ!=YRRX_5|JR&mrK_m=KldAoaG&Qjffv~SCft6DtYoUyo|ixi zQ`hdpg^Twj-sOb9y`m_y7J2{m+@6JtTHl|kM-2V58|*4tUsVbWHvn`3+645xu?Itd z4DhY&OU>zNqklrimer7sqns3|?#{~H1dKK)o0Hrb;V5SEK1MZk7Q(!|$nL5bQd z^M$Cnot7SQWbi+Xo&*Ml%%VhOYo@Vg@8(j+Pb_PJJRR7}S_8e>@&7v9!)b~IArJQh z*#XA+>Az-?%?a7Ku`NK2m6|aZ3Y5cJa4@#&um0HWajB8G@55l?Fg7ppKZc_6^^(n^ z$qKRrZej331r=w$n2jYI-nTi^9Vg5D*{{1CB^2IOc3DY^_WgjCJF4zEJx#f!M!>w; zS1)5EX-T(e2Gz&32Kd|oYme-9VaHCO{UHU%=5NE*l^rV#V!CQ<+!hQ1&{|Dg4l$;D z42aV`w(M{LT3)|6dAjV3=(xFx@hI%_$hS`EYc%z?w<9J6lJf zz9bd&CIOFQHe?rDFe!>ayN3qw1`w@l0*Wj`odDVeLPJr% zlzz|N)M`!tAz`Ag-z65`d4GP2jFkaxjp2BJI%U& zI{=M;b?+f^UJK|mPuZ>t1zZn>X+UDR6aXH0zbKelBMhVhW&8HT0sdv6!LvAP*IwYC z!hPal6$wtuMJJxg*(m7qKhV|l{Mtti(>GtV(K{XNfwN=-&fK@)x_tbgAOakx94fb3 z@RhBREq}9wwwyQlP>U5&-b>^G*%M98h z4;;=752WpSEo3K*>=rQJsa5d}RX8R8ClW~C!85@D%3-P7d@H7=**PGmy5%msV8VNJ zWgAZYPlzz@oxpNXd>=9MhLaOOZC`o);>=E6W50*(*09f*DsQro*%#R&l^Cw zJc@E*a<;LXQKaj-#mY2oVB#IFI=CsZXAej1{C{tT^RLSm{6pk*Ahdu}Gv;dEaGN)) zR*i+z&TH(VV*6(1M~Ip9fG2$Hrl8R9$<*vE@DS^QTmIsX7)NFaRi?OMatcFrkQc5@ zegN!kM9V}i#J^B19I5Q_&5;0;EA<>dNRHRFsckphIL1>?xWjeF9gV2(C6)l*Ghp*| z_5dhFYV^*8E(q)5M*nL)1C@By4Sq+$^Y`A&Wt?V$%Fbtquh#hHf&7x~pkVbaxRIjq z063QheFy767GUCWpe?HX<%+q7{Yl{oyGWcL3NlR?>R8)HYf2tCX#GgQctkTb)H+py02H0{}=JXOXjb}H^ zdJc#o3}BrjoBPdl!9En896Cls^`#B~JQ3@`0g|iKj#t2t-BGy`a9}C58^g1{3dgPe z0+OF#Fj)7YYyzz6rQUpL4qsz1sa^1sSukC8Cg!&gkS#F=L&e=o1@|2gzmD%@i5wXpr-G-?9$G zz#(-|O9KioCfP(f`)(7nB`IpACAi~~;eM$*mKE9OSaWqc zMOY)n;M%Hf$5(Nn16_XdTnJ%?+>7~&?fU%%9050XPai3sPw3FK&lEm3ao_D>x^9&1 z++0p)&B^TKDX>cZH$CP?*VRcfov|B1VU>j$!w4T%vTok zX~w~2UnsyAB>O@*G^khmB?6wW-j=#O6!d52KQ(L2#V}PInlo~pU#;KCQO;k( zA=?KX?(dB?TrMT?j3PL!>50=o^&NNN>XwAhQSR(#|Js2fYvo_6z}y|B?ZDr3vmFq{ zfFqc_$4Y3NlI5(zZ|v~z&i5&~iI&|qnbm8T%^y(NZsC23=PZ}o~H^buwje(=1 z6O^EN z0;Bdo_li>w2ke0V=3DQbKQEIQ8$lQb)`pMQnY2Q#TKX1BxFO=FA<$nnp?wOkhLrkZyw<5$87={OrYI<)t?h*|5aSOSe5-#U4Ub>iq@T5q300%WR@GAR`C4k>$A6a@ z3YzJ@{dg>Jq8aa5uqcGi%F9@H{BydaSgj~2a8zL6G7Z5U7K1fh;%1P|2-{igV^@%V z9lq}?Em)tTIg5mMG<}(8oKCnSs?4WD*!F~;)0HpXgJ*MGl-#VE_6pH? zzU!v_i=+igoEbW^^AKWHn=OBY@d^h&HbluZK=C%}E6ETBwUv_P7*1BAD7!C-&mxKl zZyqzf+-8NU&3~|^pCyJ^g^{(XB{YWNZuhXMDYsubbjJ0M$v#P^Kh9s-E$C%cU^Gqh zcsI(B8li0zv!B@ARJ2=RLoQ;)ke1OT4dgA73A9bHvRp1L7@nf6WzQj^G{&7?Ry~uW zGKY<>(7FZ{N7dDXL?1^LaY22Is$< zKz>SNoH44tVfgrm{P@+o!SHgE2zcf)<1|Vx7>B=>{OM#WN}%nCgXyrvmgvE*!S#>Q zWe^W}0`0+XpZ9pPF!gFjI{!zmt@fZg^Q6oVl|(;b5(8F+{MhK%hemP^5$|X7H?_CT-a4}?b7-q0b7)(e|!*g zwr&V7MtL1=YKyc?BnqWR5bFrPw@o9JE8j73udbIu=G%@ic40Zr_K_;le!Ko`E#iHh zL`evHhG#<*i$JYQqHqmP%fCm(FbICjtrCHg4XF>WZz<>P6@e!%hBvxqzNi%rU1|u1 zJvRM$NWMrQxQS0AOIvG3)_(NljkKEPXgtHgs6(?zfok|%*wp#J{;nvUdT40dqs;dE zCtkU{U3WKduo*AeZF-Q7BSikFwEb$Z(*6t~INO+P`~xA|+sD5K!QIpvN8F6B)JPrv z<5z7EG#nI<46`OKT+EXw%SfKO_#zmNj5ShYDmU*Zn3vKwHxp<__E}alb^_V_bELtgpb#l zEaUoI|GQGX@YT;edmoE)Y(GnT{MRLn!OF#U+H(Y_6OwdTY&1@A+lW^}86#dRJa`5% z5-uA5dFt8C#fo8;haBl?t3dbiy6+*LPj`6gx8h@Q@aJj7fpVmA7-=EnZ)=vs%^|rV z_c%>X!SKsT64RsFLgPdNtB-{fEE$*WT`z?=M2O~V(pUuAvP5s4RrL-!;D8b%Gmeyq zIJTr+@{t_*oeNyC=~nALoE3sCq}2AdA3g)lk2J~dLF}nM@P!xrxYY+gk8Ijqb+Vtu z#lda!*+Y+V9q3l}IO})l47F%y*GS4>YY(b=H41-HD}~taB5Qt(hc7PTt;dtvwDbvx z#VJg-$*mUh_$48OV7vHO7H264zW>MI-KD|~T^Ext4!10wTDY8dr0~)%Atx@h1sf9U z6zmOK92AQsaE>PkauSGA5E2=2x0}nM7CkqlEx|s0-SInFhc8on8@pSv%$7 zu6Gr^?YG(>bL~m>u2fecSox$*mncXXN37GkE~0YoO&leZ@%p)9_&G!kI%lJ2_LM*~ zgX@#tO9jE}U;-PGRVmhmW5v4d-Mdrh?u(N36mD;Hd=HXF|ukq-3nZz`B!&?;M;>mD?wYBR+t^^ z*`yL)oi_Dt1E*P|L?Y%gr~0cjJ|l3OjS(@J1%>AmXdWpNI?tD65ThSmsShm)mN?I9 zf#kq96G8A^^i47s+8SSg5NH)!Yo^vnzb01@9QDgV*h5<)hv596XTEdUI>F?%!S-R+ zxhGRQ8+0VHhvi0f6Ma7len_H3JNarK^xvVs z(zcvw^3`;GAM39iDc{gm-94*GrkAA*1p66TLUf#y^Np3LDS2?(Hrbn}x?8kb?DQEB zLNkrm`BxU+u}pLbGc>_M*95~P=T8gE=>q>+49hQ=rq3{wysie{A}OJO*w%D@)e ziBdi3pJ@y%8U)kP$~UX-LJo-_>66Q$O??jC{rI;J zy!nt;G!qNGGeKROM+=7$Vr+;qkAss%;fdcW?(d}unjqvhg}C56up#Nd0XpvChP9;#WAQ_G zZS55(SWV{fX{1!ZlR%&y((h4M?e5Wc7s_TkT5AFRlw1K}Cm#Xu*(9!t(y+N=X znjvw9dTr)(goE{qj8otV-S*!-a-{kY&RwW3xlqQ$Z?xq4!SPGQSE}>#ah?jl=cf@h z49{VSYK1C38AVvu{v&S!Tijk13xfAM8yzz*cBJDx(H>q2D!r`S`wroe1ezS3z1yBA z?Iupd|w!V4^&DuRhri#h&(RN50hsZ~S>h|58|PMI`Zvs9;BIpEOR zrn0X^y-tK^^!9mjMPS}#>(3uJase-Anb05O@Q+DPhFDf5_IHUlt7c~rWdbeD#}3Oe zJsN=4QLZCj7KKrITnUy*l(p&c2+QuZ$D;7n--7;STH^+8!7xY~OD#JZ#Ut*p%FT`! z^qgy1Z{BeRl0v#B58ow5zVYhEGjpKG@AhglNY}LL@CN!n9k0O9i(V0P>X;)*br)#%RRG5rDR#vEt zqmxYyd{N!^hi;-QSYGolOPpIe)M zBst=SK3+pOVBEO2PJb&!*>Bxml=a)3Ou;a30*i8txxt9d~9d(*M!n ztON1+Pr!34lN@u0h~Ztb5FC#6;)PybcRFAxD%&z~AhbbkkmXu{8i|lZRwG}bD1+J& z8LD$K$-?;>B^tr-kMwh>wSU)`-(!Y%tI*k(NaABfJR=AG(@=Ml2&^_XK=lt1+(-yj z!&GJZZ_K(ji1Ip}6@G|nZ%^XdNKgY&|MBV^*8w9=j2gS7Kfg;dNUZ_L1W0zBD2{_f0vVi+jH}6 zDRYE>3vPWOk8j(B_Sz}299q;kCO)j88Ly#V{5n8z_HfIqudQnSg}YZr>hTY7B%2da z9MrZ79mjz;ggEHtH6l{PE9qrnSa9d%iU9fW_^W)QFHp2=8*J@x%mS#e)#_nr5e zU{}8Amra}voI`#vp=$?W-3p#)wrL>U>MOa0L+p>r*!r|#>GofF8SsE4i+SayJ=BMos ze*0T2J|@s&Y%NfISbgYT!vq)Vf3%ivhJvphc;C%Fb84JweUw0J5~%4>BCU!l)O;Z0 z&uNxq0_h!uY={Bb(mnASarOntJ1&fb1`ZAzQnBg!n>$2ukFZ#$?>fJ~y?1(2iFG2W zdq)WQzz^-7rVAek5gpBQW{3oBY|t#(JBQTlp4emgOH8Bk3Wh!FqVXOd0qzUuJ9&I* zm$OIqC!O^%XA$=2D-D;|F4BfO93px@|L{rSasc}9dGlnet{TSbJct43jjocJ>`#TM z#BCCKiVGqiKiwrtRX!!t*|||~Q;Ct~qqzyhNUPJ;@*;5E$e|W|?2vx|i^=Ks;>}^% z)Oz#Jwje`9rC25gdg3bAU-?0lxhFuu{vBHQ4nneWJbQgGEb{VguPLZ*#X~lnOnX16 zs7k$btk>Kl;Sk6cUSGs8w`~UXVfcMHP84Xt5l3)=CMVN?vp}0U9!4p*DE_|uV4DtD z_XAyn@zv31Ka=qp8kG9T&Y93J)5vE1Zmkb%tNV@I_ns;)vLujL9I+ha5yooU#)lcp z@sQUJ_XC?bnEs@>?kgu~jEEA;j%K{I>HY!b_Xly3qWgbrQ9F?^1O?t% zH#;JYJPJuQ(9%};J+r~?Sh&?JxSKNW>(C=~MZ2KezA;2{L}K~7L-@X{=vlzUFBvF{#~x#Jfjc*?5v zU3#_Aug{lux2PUDSpq;^SY3!GwSULH{o(nMUK=B`1j2V6#61Sp@s-UV$>ZK(3(bjF zo*odSC_@^)*NKQoK1f8Kaydz-s?mRog)4KTtoM@W2I4x{wzOMecu~0Oiux>aePhk& zW(D1>ot{Ck#OyS(b4_V$IHyUic!M(C>N?e0kP8(;B@8+aK$DH#HIGfwQJGSIk9U#B z8!-48i9Oe>ZR0h?>40 zeLh+EnQW6n#B9ecIoR!}*C-l3_aw97w^D0w>i<;9gc>(oaCg)q`A5)2?F%JUc4F9bm6*eON%Hjbhh0~)JA`&?57u!*o~g*IVGfgSFx0B zRIk9QfveWLtG5^Pnj7$vzlrW8-!yBQZhYL?ENh;zEndSn#GeTr>p9&(-m1n_%c`D5 zurzy7j!Yk`x2Jez5(CLnIg_3z63#DSpVOJABQ5W8 zp$kK*RjG&H5$96|Y_lHS-v(IWEPi=G3ApCJrefH?a&&mV{WB(Mot#ZGwVr2ht=$S|S zt;mFa`lm#OY^7Luwwf0j7s!1|uNJ}JJlDKE6V5Mg7KPs)g!iK@N(ikWDUm_)hx4X& zU-p~r8>)PlwwC>E`F24yKsq*n`g6oY`{}LsnmOy*x2>}(Fc&WR#;{9Y8sdf%>mE

dR;C7keFSP2ZAeV#X(ZsZ!=_eC5m+3c)x#|Aq@)v__uO}B zPQPY>LTXx8JLP(3+qHV=CM0#_HMRr$Lz;9`$44XDf(7L*j_sPd-z?`I47ZdosLR%; zxp=&CX3V#Y3Ea5+7-KaaLVYH_cO(oXRbRfLmWTd5zscr81wnpY68%s|p3HJ~j4Xxe z((^$4$cgxTfFp=1{BwNiF?+lYZ)P$A!uFH#iX)(o-nrT|6b&ILUJmkSV~xn@P~nzn z`{hGSVy1lR1&Q9U(9O?9KV`&p*c9xhvjs{*Zf(Ecw6;$aRtbW;F2 zFjM@N2n8FMUTjG~&Bo6;aG}VcK3PO&%q9E%CItiVRsXKhK@Eww!_~6k<##`^xZ1i$ z0E2h&IBkF~`SRhL^%>2gS;S(x#wDam5mVl5oObw&ka@l8MzubR29RV(RRDhBYDd{)b9uWn|EIUjVV* zf{@!Cf(2QYPABL%+_I}R9p`miF!SQdB6fP-`0R&0_lR|%MQb!eo>WF-h%czJpqGnJ zVj$@(Ast2;|M7W%7cdKpgYm_@yJ;xe(&Ml$)}Up7LIn zqfdftP=zl7Aj7g8FO9Zf9DMI-t#j+lok}ymnG2SpuxLc27x>Jm4G(Ti5k-SVT<_u7HbmoDs5BF%kT`}3LCdTk?~i;Ovp>u{rs`Hwv!2ELYbPMns6ja zYc5r+*f%O!utQYw>_PifQ8-((1cl=xZML_2KgL6d8~aWvf2~G)g>~gp?huhup41GY zwlhlj8CjY6_g6Z2=XDOdDIq+ewYSC8K%5AvEBNR!5K=)oIq#A__#%5V9aq|bKXCcY zjnErX$n$u)$CiXYZkcM)D5?uCc5qz8Y>E{@9{lG}PsV>!itf3xVL%Mhe2~wybkd<) znZ@VZ3QrHj1Zd}js>>NCIr*YYvm|#}upwT931Bk1?x9A!&fbMXsM?*cr?>iox}hA} zmKC^b9k?Fch`ZbfIS^z<^%301`-PRR1Ljvi7ErQS2!}jD&CXtpbV=4ZFV7*C z@)H{ap_j=ZNSH1B^Knx((A{koy)gCkD8 zLr8W8i9~qKc(bzfpx4T%;5}jBVE5Oe2dz`H`1wPo(SBzJ8mbnvW5bUuZJxkKz%{65gHQov42+=6zTN6?+FAN|taIw5_}6 z32MwRfaS-L)D@CFq5*4qJf;HyA>zR=4tdvPj9!UX=ONDLErI+w{ z*0FdEaXQaWP_rl(U3G`(phRK(UM`0EuMWp0YLr|73y_~Q$Wn9FfGyw^l{v&U&x`j{ z=q4(1qwe`x@qwz2P2yy|lk*c<+MIWb`F@9S17_bGsMH{dJ}b4tq# zH*zTzMcb>CTPz-sQ$3G^8@`6m4ek6$#*;QqtP_RXSzXQeg!58$>Vmp-t1aV^g}oX< zaAs|+SVtxk>RD0IDCuVCXo|FBF%uOEH6sfaIQf+eJ+@d*SEGZRMQ$8X>Th6YgJzM# z(9u|!Fttsb&Mx|Kh+D5aja(NFuMU&)JygOg8Dv>S{#@sL$sRYl>xgiZTJ;6{M>3Rp zae}k)eUpd&UVxSXTqxIj&*)AjYQdKT6gCt5^6CSmEJt$j z;srLfb$$;u!ROzRKd7~4*tSjtEJg+3)<1vm zFkHV+4DGGMlzuLbgLBwm?y;C;XF}cXl?#G@=iQ^B%u+30EncUXM?TkRIE~Ql!ks_6 z0oB6VB@k4qcdb;VW8SPUdJ;|~OCf%v|0KhpdyU|?i!La4ZuJT#LzSAGP;3vKq!SKvObgK`TmM6c)p7Ab8BX;Q@Oa(bo=sZwkorbYVb_;*L z-RzwK(GEy1)?Q7D4plpVByFca)={&wvNHY%@4`Wvf(*z+%2D?ulrkKi@iOE1r8CPi zv3ysIAcgr%2jrK1;VtcBCoZq8ySx)`*OxnkC`a6V<2>1?#k_JFRdu%H9r_D7&x_9H z!%8h{p);Yjqd(`|c6hP>OO-9o3akd~VnL|1`{}zgvdDBE;3LkWmh4R3-+@{)Wmygj zO>uL%)9q5tACx|i6!D%witi=OE5&PWq*Isu7>!&GKsy!3K@9?U(^}h59Xju1g6x$h z6?swzy8Kc-XEmU*38wUXaIg9m7WIKc3zq}C-9Pz5FznmdmqbCAy&Hy=6*;@z088J+*rLVim@fJ$@wmF05g@haWWtU&1D?Xu=MUoc9Y{7vuH3>%kd>xp zqOn`)>`yJuyZ+)V=awQ39=Mc5xcru)Mp)fisOmLcrjw|kXks1y zaR>R-_D5FGxt8ARp1rrXt)02>A)SIM5co+KY_AHcX>KNOopA-RwDmIaUF%KFl7K(z zE&e}Fk}9Xr1r-5+e6B6!VWO5^&Toga=1q!30f{RNWdz**^clfXKJI@w}4~a#@1;eo( zi`8p&={28Ac}P;;Av(^o_fRv@uvhk(;d(rr`r_nfF6a9nIuV634U<6F2dZ3(d7`4E zv<1#%xleKwi=hzdT%SDFuAz;~%Y}j^%uhIgA^9H&*}r*Fz=GK`CT4tL;JP zw@SHBvu_ergfoFW+hHd}Pp~BH!J(65Y`=bL`fvvhmzflh4}#sj(aMg_0Bzj&|4V7z zqKC2u%IWp$4R}u%8*qUYF}SVvnU2>Pi7Et|@3wAj_ll|AIgL9bWUjr7$d=1|74bln z^aEAbj8EC|A0)|D$eMIQQy3cII}5<}s=0S~u?HOiu)Z+D+mtgrxc3iHyJ`sdrV(BCD<=XTuA7>MnL))hH={dD(P> zt{DapF^FafxH*)w7n7vS9tCd+eu4~uvc=(1i z$u{kD!WBcM;2)hTI{NneSZ1^bm{f-qF2i44{v=@~p)VJo2fp zH$pJHfu%)hkFcT>s{XqUc4%7Y3=aGimMT0cqB5cDqLCs+pxFiIx7XkS!>X)`e!Aqm zTPG_f0gAMGX&A4u`%xh=-2EWTfC)4kP(af>tbh0ee(WAeV} z^@2tC`>|87w_6uuY@uUO@;vy<)x!Y%r3W2;i}$P@E9OGO8ES9v!ID%&J|ON79;36B zNEzfvQHvB1oA`X^YDbVCqo!NkQ7^CcfBpKEjHliwX8yUU+s|UpW`n|07BN6yjRPCB zBG>=lT6jZ9YN&B+k8t~WJm8gqMoq^VC@oN5$y)&*3biY^xy3HiLEn4oS)Ie}fDo5P zq)sFOHlXy!Z@06ERUfss8DBiavH-SOCXe1m8AnGP6h?iE(79k3(`E-4dVqC&(%DnW z1A;F717A13G;yL^rCio+P57L(1|b?FhM!hKg6USEv&v;c!0^ZUvY|DZ;+m81YX}Sf zrv-2ktKF#zF;+yisa6pi86V1@TPAKg%@rAOkaC4^g=eR|bd;kk3FpNw_z|gTs?_azRmCWzfeYCH`a?KA|INBcV2V}no!O-?&dhm9USL!0JP*c{G=GnS6}X#lV@p`EYW{m1GjB#gL#fZ z&5%)>o=2$R+WBkRRD&EDAgFWBk-;pwVJH^w>~`0dKgLlkJ}B+)G(**z+)z-fz5P&% z&MO!AsW(em2G^+FKjMqF#|T&JSi~K3F;6bhaq0fxE*GGQeZXmN(?0++XMH)P_Yy4= zN75z44KNcI*_FH#pskF;{8EwE%d#3-=HtzTX8h=~ z8Scxc&OJ}+e1Pt!KTZ^WI*Z5zZG3@|0Y!AFM#=jez-f+OOf9oaM8W{Y)^+~pP^t`q zBGlt&MB%^ZSi5V?T{f0penP%j%`>z+azbG5Y7QvTcL5Z)o^$UepL=P({$OUs8J)GL zN5a0UDnN~FT@C)3;Uog8hL7NL{AF~Q>;D~(;8)R?{`IuU_Rd*cp=0#46kAz*WT@uM zbj12|Ju=r#oBCMwdOvyMj-BX#%c_|7V@!AIQlxi%FjeMiLg>ol`J35k&b@3{wA?he z-}aN42p?stTk5)og-((t)S1jB#x*Wf|Cj@Wto`=_ss8x}J`10xn(+~*fiANc90m3= z>SOdKRSMRBSvs*wF!9WtuXrtC3YL~+250$Ycnq<4CDY_^T#x@}EF0VUU5s$IQ#!;7 z2I8mNmK`98J&OReFaR8mAI*66!_nE;Oml<>C_Yfhyz#OS51|f(UPpbpqM)pIwg5!eAy-lp(2Pk0cU8ijc^Z?2@!7YsgMysqdVg=Y5aw z`~IsVqq&#sy3X^rorvgdN5?+KCFzMSmme5Li4wGr3kyI50xejlqxWQGz)H3DhGz!n zu%xZL58`)bzSX+9=mc%9w1RmNds`FX`^g)Mmtm`jxsc50ZYkLJ+W(j-O{Kme2Pr7f2p<+B;obdNB%cH)5plfsXt zQFWo3amE^vD@#l}vpy%)I;?~~P zeE-8;K<`UHV~XPN**oM;l!D%q%z<-Vn|Sz1+oW&Kwv@wrnk9dxjI^d%Sn5?*Rbp zNk7A-IELLnvKKGYsby*EzUwt1C>5iBU%6EC%*|OenQL)4hp+bSDX9Qex}|OVWBT^B z28Wc2HI$ECmsDHX?0`3Z@uWmTq^@I6g;LFQjr4N(FfW6(A8^!$5=_J+!nGo|qNyx_ z6GeW23iTv`jEM@wi|>bbwDjWDaGtXQnzaeODWTHmcXmamjXi<&Z{k2JZDp7nur8r(p+l+R`o8|eP zaRzs3jRUpx9h|Z#(~Vhw^t;~`QuwD{;8Mcwz{^6S%Z*nN>j;YL25KCrMLft$=ZEW~ z$t}G%a7>p=x^z}kqV?8&oYIZex-*!GCp``-5!b&zqLf0!@ieLo>zzA8IjKzr>i*5VL224Kh!>OQ95$*E{dHUMmDhq}~*_3gC;V}LUGW!_DPXmWY;k?1l%J|zo?OoZ?5T4zk62CnKA zcmZ$J@>Sk_Ns%NsyHEB~*lR2JZB)|`jKFpMrlox(9+pE-%kwoCF^kV*xRKt&w;z8? zY=#ba>oE&~cIzRzFlxJC zG3R2aaikXvN%Wb&3GK#jIkvWNsS17JyH=yracY;=OSkTSI# zYFko_u~&4yD2EY>ZqM2|o>y1i zj(xoUxjzh$kuQnlA1&>&d3>~vN%*`O_nk!lK}JI3-=je!NfNh#qJ42 zk38EgsZ8C8m&so`4cIMS!d^&L5DDxh1Axu5vmam`x82hy{*5jJ(T8c1tXEi<>i28g zRHbGgAfhJn_b6QrA}-ZW_}f9xEpiC|qdWlgmIB|u$NsMR#37d*^o}G9{Zg&(*rQLP zcjk&YpS7t(_8&_EsoZ?30G8%g$wDRUq0nzRmcIP{S>L`LUT5mD-WL{hrnInb-J33P zG^wp=DU$6A7f9SIXR}vi*(J$PI(DyUHR|o6;B&!sU?j-TPs(1diC21Vx`)}pW}`0;_lAW-|)2x}ZQ zvwPktdngF&_qmq`DHgwjQ~zLkpNwN9N9PBTJnvQ= z`X|7MO{2Rv-6rB#2#PX%7{uC@^zCwhiGz;@dCZe|Q?ebSOs(Le34Qak6sjb>{xm2E z`et!d(v3O8jms3qZ5fNH;#=HEM{cc+x&A12iz9XLr` zvV^@E$?)Ea0D;y2rx|;tDs4?2YkFjB0@CNDusJ$EHa3n&Vn~DlXUjEW=1diT=acvM zT{?n*ZoF8q&6$~geFF$6K=SzdD<%0(+a$aK&%Y3S;*S6|>Ajv$({wYot}gT7y-7Wt z==^b)@BmHyc%qwyFW*9k{BhENk6ARK>D^^cH@P&{*xPtjro*pn$K{es#V35jPl4L# z`-bp}-foUutJ9qCVBrrFewp}{l2bPqG_ZanJS~b%1d?w%xC08Ms>Pj|KrWM_^E<|O z(CEJ6(Zp^owLsFg8>|-hg|j%i*E*SRj8m6B=DZcy7vzBW98`vgw>1rCByw<}#g*%r89lOHaQ|Nwi?~i_y~W+MQGUNwBvkeTFK} zr6I;5Y>Jp%LC09`rKj60ClC14Ds^p7HFlC(EQ1f@C^40fxDR-`DvXwLQ zCP%g+qAc+`Oh*u9_ID}D(^Qu%tTD#f$Ek7iAI!2~J2}v83I~*7P=5N?W@A3fzAvW> zr<&dpETHLhAcP)Qb=g_dzG3$n4RF-VVTM~u-fyuLvT=4io~I7z%6M6^@z>$Rxkt7ix&; z&^y9QJjs8QYj;+Qh+fw3+~4OU_hVd#c^V0>f$omeXgOeT|LvF^r%)Aw4)|g)>dFHY zjK1&Ga5Lef@7HS2KGTbGp!~kD!l2YjBpHw$3QPI1dCbN79HFzQaP(ZfRs^dNij9Pqc)8ed?-OcBC+IFHzM_IY)@!N++jgds)Cy%^{{Wx1W5^o zZ{<)kev6@6Im35hS8w=igbyd~%fHHC-6Y2Ub$+z>0SWoCla4gaQ5K|t9E3I?aD&81 z+UW&`!m?Vd-BI0iVC>NY$);eb;=#GmupE_yJD~0h~u4&4PH4T3+4l%Jl9{ z<^FHZ*TFu~L^fr}j9Ns3({7(IHtV>zEbA^$ykvy&Jxgc&(gH{!^H{OEu?&{98~dzq z;V1mQbw5(8?l_07a%)J_2_*FSrYJfFwevs4Hv+u?=p6!kOKLcRhT8%7Eg#j6g}Fu_^jmtP;!xlRie4P&hcygNx@%fiY9z#U~2yU4qFZTuw&)h z9eb)0P%~c0t_w>?N!;m`+G4b86bT!A+fT0S(ZU4Ehc}yP2@e__hOo4Kz2AD=lZ2uE zbWO3u4_x2}ShFUhc(pSDi{8N-fjDO)fMn8l<9#k1tE}c3@{cCHu0*0rrh?b%anDL_ zGrO(I1}K<8v;+@^T#X}Ggj|Hyy=s3Eer^b5^PM2_b9RN=k-O9Lvke=}C$b`LM921T z+GK>Kx4P$py0;$5t3MCchgm}tLM2jyWS)rQF)yXq2xEo;MiG^|0p*FyzYXFfiz}U< z1ElAbP;S*$v2KW$acHHht1q|H*`{Srn+YR*!jBFKj--Eqone{r>vO^H7)zhl1 zXLDX+1`4Rrnt;}L$pu6YZBejDj6Yr;6?|r)YYdNqXnI<*;ZzCFGiHyUpFkt2yJJUN5kv9s9elZdY>Qb1%doX3y*H*e~Wdy!&DP zb@d1Z@ONyTtX{U0{Jmi1W0IceSc!AlQ zoeBbFvGAOg+IHo-urL$MbhfTL=)N&U4^kUuCtI@*bXQ7 zD&!wZ%GsS^<+G6w26*G<6Spti<687W4sRIJ&Muj91jEAot=fHBi%*ll{koag`+3_H zMUQ=h-xz0>B#+>KQw2!kv#6X;8)$I+#4CIAxpjC~(z zNt3X1zo&C!eVTy&lXU1YiNo%3@2AyNfi2B=rW9OV$PUZPXMO^wPvz|_z)D2`Q{~$7 z$>owVg>wt`I2)N@+6p3v0!cj*_DZl9-SxS=CLxg`q2Ne$wc`N^S$LTUIN7#60OD#L zL5A*U`-WgV&xcz5nVod1~=unZz46jjWi-dy50qCj=kTzTQM@OiV03yJk#Zvt~a ziZUvP`q5t~LUc zDuOC_{gMvHHIIXNhErnq{*EAjgwy@PiqK}-0KPzBE-DL=1z^!haAa}a?KnxxC2Pt= zXqW}hG>D|AKV9a#!8ED z=uim545q|hIQ}YOvoacJd=C4#gfboZIrsw=G?2KWSJqT zle9Ml6rKdCFbcs*e;2r8dITEvc?3Q0_+5IxQN?+V$P9IQVk`Lo=;9z1y+d_O-XM|{ zfo!9mUnN1**`r9W8>)}bLFvIR$Q7A>zQ>iwjcLHCW-EW{Sa*Sf*vI zxg=qiH2J)XTzqkHGp$n=zx#OH`}N9l=kC&=vP&Jbf%@nHCgL_8w1I7Q=k{|JwA6x5 z7vJrW%!|%OY&UN(;z-~LLkGQzpBA!Ze-%O9JC#|GO!WKAwX%xU_@s@|*vQ0IC%fT) zOrgIv+gv>8t8g(c=d1S7q+ERvA`_nm2ms4 z^jh|yN37~TXMDfw?tk2_o9XYsWQ@qh3r#2EcvkjqB#=m|a@%O;!y_JF6rxEs`WMCR z&i{lO_c!v--GAzmv|-4}%+LmudFoN@;-0m>bTtkf z6rFFy9pPI$_MlDHE1~-?ruK`w15X1)6vQKHeBjcG1lYb$0mYCzp0RQh#$JdFl#VE;G3-;6VEvVGG=WPs~9_5c-pH z8prq%oAkZ!C(!8pHgY&OO~RhWWD;j_4sH zB^*)025r7*^1xAQ6T#xs9KY#)aB1JEL*@^s$pW=VXa08RffwOKd@{Oj^|&SeP9!B! zX0=0+Of#%7cwJ)%d&L7x?Z-$*US`6OO9`y~;7It=hYD^I-PCm)wLi9B)oy=O%~jy_ zldDhtM^Gh;Z!^_H-J{4oSrVB8BV18$12@vB!}8I|ua_l=l)ML#J6V}7(`8;gvBusd zu9|y|;qy{*F@_~^5blUPcS_BsvnE^>4?u~sagP-3Z%{I(NaD$^t)t)?-NjMlGELK| z6+DZ>lM7Y*PS8#R+-pJnsF1{Q?N+V%JzL~N{1|LMK8 zGerCIbBcs9KAc^V6Y9*2`_H>zk+R8T*p)wgN=KG17Fs=0f_)|HMW&PAY;CHrE608= z2#CpW;FwkG9ttOvvEOdJjO_6M?5F$YS1$Sdc!~2s4=;P=`-L+<2 zU?hp;=_>2+TJB7vplyO2xoGpxN@N80g-Jx6?o|V`x0HiolKL6RI)Cw`zFZ}FV5BB+i3~A?4S+tbK+k=Ma5Qdy&d$feEd`(Mu7<$p)74@qg%t3uc3QP z+4OUn^6ad`K`9qHD|;H^I*gP;(ha3z%xImvE}P7tI#+Pa_Zklp!R!)aMDTh^$WTuL zib{Sp5#D}CeBe(zAkvtDg?$eLCeXXbf1Ngn&L1AK2=-4@XgN=qn%op-GvIlg2U@)4 z5Y{W%Jop0=Id{2|HO>ptF^rB7hb}FDRVB4;fB_bi>is|xV&`w`!mj*{Pzbw4f>LpE zS40|#_ncHLQhdglZH*T(DV+N-i&D zVr7S9B|H6qbH9EZricn65KbY=^gV zdEd@wGMCdkT){Xxts{6*!hW<8xmbAYS14L?~{6 zm!1CTKF6iZ8b68XJvTlU&m=K1(4oH^Z0r0F7d#U`$i!X-1ZY2gk3u_Mnfp^UumT@? zF}gae!;j8>CT@c?hn|2zhBX|F2$#y!3sSUAx;$Pw-l_gGC_P9BHt0dd9A;`v*N8-g zTiC(>ZS3ivqer&2ZdpYzTWBf^^WM?qXpGeNXJk^{4c@blkl6jBJVARG10vwGES$0e zqpjRu@EaN}9TWx3v6ZjWl!0L}SkD%)7-_C7iMu6712iS{j-CjS8B@$)OrtfZph)Va zR#3(Ah#vT@34s@9dyJYk@9=xD`JwZsnU_>&U3C)u;SJC;tgwy}v+!3B3DQm3j7Y931$O~hd zu%R2?kyEJ$OraBLsgRiWUbb=XnUgBIMrk{VkkO&$!eF(B$mCFLjPQ2=%E#$HhV@;% z!$4lobBqh%xv6)Zw~qSvF*M8v8I^ji;!6hNl`Nw~D|lj9de5dS=&^$!o5spnyga?? zcxRzqP?_!;2T2iG+oH+Fv^_VVxWWV-1)?QG8tmA;`?kv~Q`PnhE!(#3+Dd2k7RB}2 zHWD)A?4^|HmWRRAA24=9M3lds_=Js9aN~&N>Z}~^Rl+G1;r#?E_hG9V+`*4(h2qY5 zM-V%H7#1v$Ne%hU+bzw58gaCLd5u>Px6*gB?p124S&@EjH4rT$uT0-YCx;XJ;7B{A z+oYb3mnpqY8h>5~uXPt6nEK6$?*SoJP#!o3e(#bc1L&JOWE;2B4429ugxwlq$UNpO zOqhSy2(mzZYVpJT=Ks+G=pZUy|GLz6Af6lSWXx5?^)>=GhbkPnQOSzq>59gvEv=qU zlRzb25nQvpPR0$!C>3)b*LWWacNLB*)54)wiMlxy2`fP;z>{bGpY<|TMHta- zQLVBlgV>kn%oUw2&+U{~^(c^(o8}TkVyXw>WeWPA9)l({{r#SYW->gH_k&0RHj>+v z?XOeBRP9PW;k?zqFN1bC2HYfAs@CJ5SfK;2*YAgdE>Jw=j44zCec<)0Hax=L1Y_Y~7C-be0(hfMn5TC$F}1n- z5Id^)HwKfz>aJ86WVDVAtnR!^Lo!8LYL6V^I|8=Zn?$u}t2I{#a|B4c=dfPTap66R zggM*yskZvV$7b>$@2n|3eOl0~3mY71okYxBeP?cQlU(cxPSVf?w{S*`sCYl)ls%Qw zRwt+V2Tuxr z>jj?R&P96136QKlKlxNZ%&;H| za*fX43V|uV^6vn$cAHmuz%`t)CIQ&?p8e$xpsb|z`{H|K(*QNpuu%feGNZ8w%pm?2 z(CgGCz15_}3dgptSNH^3Gdap|Y`MT1#*^|t`tyFeB(G-&zWmpA*Sli z?oe>v;nl~gu%F>9Ia1N0MlLRQGa?RfQ|QRp#7t6cOdYhgYMX56hX1NMGeKj1lG0qb z7*|%=4P(vwU1lx(h;5)?Gb$Lzn+X`oP%Yu*)it%3Qj{00EHl?cMo{S8vW*|)*9Cle z>4X{Cjg#uG*#pVW5wKI}RT}fxk#hn^-&`02{>8z|>$*8}7 z+pBQoZ^kiR?{AqhDiLrWfK7Y&aXC-o=oR^)qc?Z5)x`AHp! zgkJzj=>x8}mR($igL2p7C|y5;NZ|d=WopGgd^&VOPUc6q9={3xy8~xA2J#?oRjF_@ zzL4y*&XS9{)u})I$WVao{+9)klllV1XG;4IftCVkUcTbE=jJr@)X`~ zZsmcqxPc*7fYPaI;5f~&bs`M{0qDyqlrFE~@Qsl^TnWyXyo&~a3$KkzKh7l%)wN>G zi3ttC$|usQXEuoI;eTHQaoxibj-375&YHXayBgl+2a?*aT`dFay=@&{fK>Dc_x4ze zJH&)>oW(2{)#HpeVj!bQPPt!n!@l?wDl9bLA^I(1&%CH3{n$FP{ZW+H5J$iQ&i*Qj zO_Xt}EXVNDSaZiU8>+vT@41fp4$eT}2Efh$MI`%To_baIxLAax3F4Ok2k>R79BcMc zN;3J^iuFA{UkC(d@>!lmt@_z-^zodt@hJ6}A!6VRNoP*C5Cef}v}{9-Zdlv*k=slA zhtHBKkxm~$XJ*ev5PbwLzp=u^@kT@6A+qkl{VoR19lJ9uod=;-RiZ`|3H$s7G53v! zPtsUf#ajeNLmt1s#gIR30szjnX9`n?hA}#!+^`R2O4xg5?kb8+slupV@4CLF4tY;Z zj&2AH)uki-df~5gSwt-T$$eb?TC?>d1TJnfLjKMt8c>vJo~%Tf`YMz=#CF#kKwory z8QjeP!+98~``3q`{Uz2PgEJ7zgR78^G_UQ9SA5TRUtZ6_m+R5U)U4o{u}1qr@yS=_ z&VnL*nQBwO#q1eos7S4=nEE9k(-UN^W&BdeI;Uqi0^tg#Ov$^*2=$i zhq9r{<>SUDhZzcx=O+mU@{CZ3#Dn0Ty7OR-^?P1FJl7P8puWWAYZ(IrFKjVwfs-Ch z`d}72jm))6IGF_4t^xPQ=X4}f`j7=^M-m1UL6cK!#vs3!%^P@;)So;nxQ3KEt zXuC?RU1ykD40R>#)cf55t=4S9M=f$8?MxEQ4QrvOOuu=+XCK_@cIefKYxS~T%JQ}P z#G#;Q2M1T68BHM?3mzHA76JXa*0e{Qmeq*5L$r!3;_#z0OI!Jr2l_Y5((B&3LT4n_ z!sH||&}WkSt?_!|686IT+2ir<_YSW9_&)og22(T?FmSX3dn_h!t=_JVr{#XrD0xO> zMSB}(*#lp^K{MzwhpD;~83@*mXYMgjujVtM?>q`!?`k0!7e3Ma0C>0fe0-O#Fjc8B zf|L7`Z@lqQ5Gm)=Cj7-NEeF8h$;IkIF4Y^j0SFg}aDO9LFV$5Y=w z$|~1+zO+vu*8cn?0m#64b&%n`Ht_%+Zg+Iyt$@d7_*{5}!uu}8fvz>{D`WEMh^`I~ z;(4OPGw!C*=54f7S*LyCPP`$gma(5ZAtZ$XJjmy)}tJPe6w=2A*r$^(~p7WWdH zk5i!B;*pR{vqZG4-VsO+R3G(89A4AtvVuRLgp2*Q&iMNZn&e_=w7mB$(2}goKL1@S zMt0h)P`Ac+AFY2vCvEGKbU9u*4)rJd@WM{c4jd7-hp=SAhtVxW+2}uAV9|zp@SO&c zQmeJ;tTD>V*Rjt}E*w1#8l=;w%A!h5di|A6Aj|ae#&q+~ABg+~fvIs~s9=PmV08X< zC*RA6+ZQKis|SYoHn!zq341R@?80Ga!NQ-|fAFFCL|ITyj_6M0K;)-KWa|qttl{=e zk7B-e?kr9bt(`$7kNr92NR3Nfx8%+tSbrHaI>z7Q@&Y%C*K;-V`jTm&5*i3ke_wqq zkd)!o0lW&=!fsJ}!IxqkwX)IAf*@=uW+A*3HtoI2^q#H4Si=>at-R5xD060H?dkJO z+!xnP&9@%P{}wZ<=F*TpF_@;UY-m2YH3zkvJdj;g2@b3Pnkb44o{A^-J)587DGZpK z$s4bRIF%R%a#QoF=H%9OGPU!`Z$q^o z;fa%=ba_TD-V3O+;A=pgUpgHpWf?Br8PeFtoV~lvd#gFYIm%y z_GK#{)_j7w64cr5m#;cN;>WBOUgq{=Q@MS#1G}e(gs@h>q}> zpasHa+L;g5;AIgyKx<_D&DidqntzojTf}Z>SID>JAbu)B@1`IfvD}G{vNMB7_Bi+b zH>(_|-wSe)_Y| zmMcXF>m}Tir~?dc9+q{*2bVaChyk*?JbeSYxn5md_>O2R60i#E12zO++6Qn3#RlX) zN#Gs$REUu^O+~W81V~q7uKj31bENO%@P3~k@*LeS+~dpVMb6}r8kP4SW|+t4Ll{l& zeV$oV8n|Wi)n*!Tro5_pLjG7=fEI+m7~w7Y8T4QB)fm&>r55of@uZQp^-Rb!aZi>% zPLtWEYs<^_oDOR4fWe8y)GUb5i2E4(5q8Fz6z0C#>ykP!~vkh9A;qn;B{>it*5 zfqJ5!fq7B&#?9#GGlxlQFK1?7F6(S5cqu@4EBYZCsWpySCl;ipCuXP%uNKh`ZhU5I zB9teW%mV(sZJ^MD2#LM6cJ@?bQ=HmT_rh>Us664hW)MkY`Gww{i&F&B&j3>yCvmJ{ z`FWc!v4oD^uu1`$la<$DLoKNI#FH*XAW3ZEaT%)D4I7)wG)m{__lr3e?5+Jz-rrEt z#I=z~a<-gJpnbPE>RgAo{>eq=o;}sSifw-`S#0NQ1Stkxu(^Gbt{FZ-4IexeP>|FB zcYXur=hIi`$kMHV7uZ4snwVZ;#M;bu6>s zgbHJ7We@I;R|@UGbXhIA}aS zDixbblH?Lh8zt<+R~7(}t9B(BddAKFCVV>=u?;sm8tk!uX*qTuX7&7*N~xVZc<5Gj zSI6w|s&o=g(dfZb&*V zWiIoh)Q(yzkd`)-;qWiMA-Cboww~Dle9bC1`_9GW^7pa(Htbg zUG6ZAmVC{X&X5H){?qxLekE0PU1jVZZ%pV$c^r>?k&((|dDx@1c{fBHu(!q`K!l!i z-CS2y)ZnTe_5E7{_INb;WR<0{!z@}pzl@;jDQ=s5fy`S%|6B9E1^bc!$#$UISWdd6 z<{?wlwJd7>C@|f76t?e2%p$vb&V#KOQn63AEE*kDzTJLjOO^uN?)SKE-Hg&@%) z`S{PcKdk|qv2x<)M0xL6n<^@Bt6K(Cjzf+(mu0IXpg5|=AQ7^7bFWbM_7u9Ul--U5 zrQ-1NY^u^_Ltn_MbUWL!GYYUH!#seeK6Z%kkj%O24`Q$~mOl5)yphP$)7X!Hqji92rn{AHXvUax<>kBv5+hOM z!h{sb;`f7)MpJSC!Z6O>NW4p&3BqbPF$0{Eh_y|`OkIs~u4|{+TAsfP??Oy(L`NCd z;zBI9ZjCC}j?QnN{hi<)WaZ> z=(OAa_PaevuLv2uueY8c0A;u2T;O6d@VW?)yaagkEHm;MfqKml0|XU{Vt0G7We?73 z*YAkBNdc-G(O*q0WM^K#nTuq;UGYeO_g*@`Hd&=b9Gt=)t_;>l_wN;G%97u`V-bsW z`y^%R>zRNoRBbGr1O9;p5YA4YZw)78=yn(W^XM-!V2{-$Qt(Aa)rPCtsUmApjI&ZP zSc_I4jq9WbDT_x;tWV{0@Ye0!`LLg%Qngal2fAxr<-5~ulP~1;$oX0iBWk>0ySJT}|5+i;2n!zg0rt}EcaJ38?1=!?#`NSRRIY*d&& zIVgC)PBabT0QBmkG4YEbYWtl5qq4)Ftqb+}<-8@!iU)jM1wS;aAoeCg(i|eURLeG^ z%pXoy6%K6YoQHMhf)-yeemud34%*+qKeWuxntfWRbP}|iVtT%J+QC>2$`TS-xqdA- zSE)rc8sV{aiyW!gY!v@s^z)NMxP7!*JozdicWF}aS_Mn8a+0E#Od(`Hdd8ok?z4oez8OV(o zd@t*_iHt{N9cFVLo93v+-2!qAijRVFppZ*>^m9=osPN@h2XZenk(q!Of0sLC^QLuV zCUmFxB2u4_fuGpu%)CtLk`5xphAmZMl25ah^_4C)ZI(q_Uqis)j0;UGymw-^u$w9; zwI-7Dru$w0xGA?1c>vktN{v9SI1M)eemO6svKK2lI>}QHNsr_-M3i8y0Uho&(|VP+ z$M$hJbHm{aFLPyn1>dx9NSahUuPF&j-i`S_;)#MX{aH~)epCpGw~P4>IzGA|B)esc1jXSp?1}3HdRQZc<)-@J)g2_eua_JHX z6$kt%tt@^(&da$j!Rt zJ~z0ecSRkfAkTgcHn@&~b#C?vJ86S5y-0sqE4WL#e~mByG5>AKhLC|jQQL6-20rT$ zUABZ`cn{#KMQ;3Y*DvIM$wfP30rkC|W^YH;cY=Q!+SqETIUS)`K~afR8Lk6lu44ZE zEVt1ECvhM=JCAeNHh54|roH%{_XE~Oo~t>#x2}QM#u*g-&(e-^a0DAh=krTzQH>D6 zkJtNFZD9n)+C&|d9nRdyl_|BgASsZceJM zz|HWprv{j3Q@TLN=8um+!@~4`G1?!;5&g!ne}ZdO_2hCmqB#Yc){2N8x#2r_?)C{4 zH+z7wC?WOA30k^R@%mwg#-5@Y)c4QuJ5W(${;V48D12Z~1qv*pi0#%@I%%`hnF(4P z1DWkj@B_!V?5}rEfcsPZ6K!{wHAYpT8WO4_B!LploUMqThqRHGO`6*X?T0E6Sg${H zO>(aNmV~wgBZ*InFGK&_wdQz6fNBOlzV|PWMv8dvl^(B6jZKe-FD!4a8T2FSb$|2@ zCtCJo9`w-m2FV^jDS!9hvC}bz2vvWl0FcaeK)hOy94upx@}SI<>r6iHYx>Kz7*h4+ zZ)E6eI4w!ox>0S=T)ox0_n%Id2UG+62zBEIieSdC5TMJTuUCYjv&&6aiE2kddzNyp z)w~`t47UQKDykZ93!TT3z?SirOk^wzxqXMA?j$=4iB`$3NB5i#9#mmgwIu+77D*Wyb& zVIL1&5YyXXGC2vy^r!P6{ejfe(yh<`zP97mrw)Ap>K&&E!Wl~in(gyx2cZZZI-=0c zyE|EMJy-Q(LvSF4Rdz@m@SXcjC91r7>@{#<+GZ0RT7s&Co6#}#JJFkrz$;P2in zjOf99iZgR~L6sz@QN8CiLD4O4H_;g{U$bX3hYnO-_X_NXV6)DNZHghv8s|AU<7b{N z$ChI>;*Wc zvf7d3AQO>T*7o_H4USwGB>1k+^4SXXqOQ%0)5^5|7Tn0eF(GaV`?$LgP`uxc@tLSm zuH2AgPyT~TL23GC@hBvtZ2}vp&PR0bHi0m1BY5AUrt>mDV!xw zdjrs5i}~(71qa_sn&&XX8q^Mt9)fbTLJ6Wvb1ztYlndXK?eey4`FQyI02GFFD2G#Q zU{{SnL{oaQ$74tnt!am`J9z-+F6@i@0e2)WEFsD1^ zTh@Rvb+@=eYGAujn#b|Q{$mTmO0NSFL{aOnA(sPk6s;{72=nQ|TX_}t2o8GqqKO@k z72g>N!U6I-G965VM}pH@UnpX!OEGu#VDuTd#(r?AVh!6AsJ`1PK@mVKLWy6F6 z-{oMIj$hocz|h}S8~x5i?&D<80lHj{w8aiQ3+@s{#TYUJuaA9hG3XcFPUjw8fYrbK zz+otE-pLX)iT1PT{eZvI;cHRDNlQabu+65^S=~uA`_NJi=Dme+Px`Hh9MI&jn_LQ7 zQzL;6`pV5RcG~dQ?W8|sXBQxSzm4)5JHL2512^4(VsJgm(Q59RXbJ*hX>P`vlJ*?D zO#j&V12G!CQ%yE17QbjXT05ii+!L1T>3X?F1%N ze=8rRLg-uXraBh^2x>Q(ce?%mwVhC33FDt%eW1P;xQ2yvLwrPmEd9(|;&b{Xy#q2&jDRpjNTxqiL2_akHHDqpauEt{0 zTPe+(G_9ruwhkYe7k{HkG%a~b24t3|Q6xYLKa|6($}MpJSfeE1Q;QTUQ1<(*Kt9lQ z*~{i*Nf3XADf|?}Je`yy#vI=xs5?2`pYadkEM}76wQRrk zHcKZJHR4;TDR?be04hLn)F>oI#*+M1w$V;dv(A(@fNb_F7(r(F_k#qzq_!pgd+Oh`0P#@!TeqSuQBf zB)!ms3wPfE2Tvoo-Uc(^y!akeb?H(nAC|t(+q$2j2Vj_Sk}rO7UxVOVY9B*xZ0|`& zy`S%dv2_7@5joN7ivUMJo1%jCxDpumk`sc&>>0UYv~Mq_d%X6TU0xGcy!*)^h88y` z?Ap58TpYdhTNK8Kye9Q6oJQG+(EkrFTm}gQ6y(Qj`WO$CJs}$%tZo;Xp=N;A`99J) z`Y)X8%#z(;C0I~k{+IN1%&;UE{n;Mt-dOiOhB^H1d3v^c;SV*!QH6i$+;eIH_OBmM zU5FC@OE#S+*JmJ0g7M=pL>ht$nF!y2eWOwyMq7-OZHo8-jq$k))j zsTR7|6ZA{caw#?(iV6dYAUnbDb}WS}SiJ|;IX7Ky-44EA;Xwd%Auxwv-E5Nhwx}N* zdM5Po8;-DoneNo)zXF>dH;<@u29I{;r_v4-qJ#SRaZ0ED9!i|pPB zN#*;Ogh|{>#W_&*x_E|{o>R>Xa5e&#>Z94xI(uqJTGaHTA{B^&R2yV)PkelaJyjVl zr_4>RLmrxQyZCZC-lK9RJXdqX^a4lu9ebgF0DFR`uFb~Map?z;LVV5Q#S4BJ_Tlsg zLY!UjOUT!nca2q1xdhVHL1mc2qQ0P~tf?TpE{JrsA^ZR7N$>h!h;D{v#LBlx)w`ZR z5}TZ(EbmSI(qIB}c)CAM0<%jZ$h;ki9_^~l1ZVOhbf_xpS15R_dFc-|#|pVo(^|qa z=ZWQB?eT;|4L%(|GnTG$RD#?6>r0ynZ$!8c^OH&wzHezkhMCU=#|P{ z-Zc|c9ReNfIl@Sr=)XW*4ZXhakI}sks*?>F+LsBA)D)d{oSN>4Z`^x1W$L*Nx|n7n zZA_eM3+HBbKyOQRRLA{V83@ASNN_GbYd{wkt<+L>P1+mG_wEUaz8z7da8%&t6_RD* zGNcGK?%R)7{K6Z%Vg+{BrKf*lk<2dM{cbRw%{l27NSogB*Y@5{@L@jBKLwH38L{_dNjuLf~HZNHTvt zDP;RO!D(!l4?sQM^}6^l;Fn3z!3VOZ`$2wt@lB=2Q-FrdvITMMZrs783C`~t+_R<_ z-*bI);<3kTNpjBjg%Wyn(h$E5bk<&ac%_@smJcwwWDnj%1T`srX@Gfl0vBt>KuXok z2N<83$lScO(ItRh8TvIp>D)FiW2n!TD>Q$oe3mN)}708#I%`x8C*PxfDYF&Vv}dvCsti^8p6aJHB}npQY>u`XN- zno}@ylv+C*J<>PNM?f8Rv7B%Dbja5^i1{{=QkX&?;9!ifT|Mj9P>}<4p6eZ}__{*5c^x7huh1M8Ub%1i~Kv=KnUQ zz*mEJuaY6 zxF7q>4K-3!9q8&ctM6=$75sH6+PJzah!z}nxRUkw$pMVMbZHu(7jH2n<)qDRJ7$Ov zc_r-SAkCB^DHCtC11!F&4cmf9CLEd-1Ry|ttc@s8)Y%Q97`;}dGOPa3HXoqLF{zSz zVDluN6o&CTXIvpf0;}4F%fm&J&uL235M0x$^xsi>a!wzGWMD}Vl(}jbA_!yeE`c5D z_%C~#O#uB5EKJ_TxRqKA7YHl;YQM731k)ahBt;-OXjT}4VNOl#JYZ{%|9!((zB$-9 zIv>uAD-!lQMGQ>R9=5U%_~@HoN8H}?s$%8bR~dA8@N}+zLT25S@RNb09a#Ng-aFEn-tau|tFC~S zL;5l1E$7f;f8s}crgPPu@v?uxx*k4`EAxDfGCw9lrbDfB7I$cGzyj^gOYv+?{CD3DrH|Q4c2`aIV(YE;et{^*~tgKCIijF{S`%U3<&> z^V?9SHMjmiuhyZzvUI9(k=>yk$+aLm^amySP*d(K_7aVbLA3sE%wHg@WeohKypA%-4Im;xeO#tZ2H5D30e!tST4T&4OKZM!+V zXOizn&ml(4Jw%m6ZfmtK%`IO;f!*#RSzbpK>8PH$4Ugirr?zHT1+ zwvl%;Y0oO6I8q(U%2t3Mj{e!y@*|kmcvHA{=&1G-5AtYlD}wH-M|Nxa|V_Go>J@pda658w}9qj%OS7#m$WgEZ!p}{a@Y(r9JCPYXoD##bxhGYUB_EEn8Q#))VyDg0p$SkuzZ{A1y-u{85Q2t%=UYFa zBi$+22H)GFTr0V)ly#!zl*o+H<&+M5Uqnpk*R$#;ciR^I1(2pULVZgr#>{0kF0jms zimB5bkgJurTy@SI-W85Vq;dT@BIpMF_Ke-RG-S#Y5j@KGQ_SE<#t=WegZu1{io1oSq{qF7lOA|1bZ$Xxz0AnE*qhG%|ugFmkOGH`# z4S-|rN@tlFKgmI8oEG){Y+u0PWR~^o*=Ess;PBNDmBTZ^w;z{-FZR2~uL}Ej-S1Nc z3P#jkrE?vNU-UWV<^={1qi~Zso>_Jb`xk)_RnfF+UeNgd79ViG_XJD#UA>vcOJr=# zx3wAG-@8aZ&wY~%-#At>^@B_3%`N=%NBK+Z7hk4}1RW5^vuIaw_b9sv;;9cdKeM`V z2ZTUk5sbu307B&u{X%p+4D^QEF%ZVFXLiHgo>|{2ToC8ROwiLKpx}MH`&*)V4A19i z#4tK4BYe+Siur$;5+?U`60iOAN&N=2Vrh!Rs>mg^9Dq8iCYFg_oe31-UZ-gJf&TVuEyljJfS|8O0&n;??|*d5UCYfJWRV#!!q~)a|1>v z3Mbk)>377J1#sQyL-`dTx#Gs$ce!^b*%%U&!*tFkzS7F;ej!{=_PqAqf}`Rf@73_J z-=Y9E3HD03sq^pC*thJeME-zt=H=r(<#fUhLO7K_&8UmvA)eA05fBg=F2W3_)vt~P zU8+*^ww@9Cy)-aS{LKR83A>%>Anj-6TKLj98pATIZz3yu2MWMU!~)Ks#~Y!Mc0A9me>uL-rVy$pIV8B6f%=qFzYhns1@OPQB@9P|WKValH2Q zD9Jm-vzZXRlsf$4N%{_)gbNG@E+|s(J`$_OT=rVSQ+=;Kd;u=x{R4=NrMCMk&v#1M zlcRoQ^+MWKWgP4tE9Su3s>>R_pFb}ru(du2d?g}N57|jb_lduMr@Vhv&5bl!IOJSi zo|XN51~2h(U-7?75@sMT=dd5=Y^csHIv_PH<~cllXCJk`!|rM-+E?py2#hdI!t9$|4Gt1fU`1m@u>sAS_c$(pEGZYyn+Ot zlHDxcthlrCz9bcqZRP9ed3;N?H&eX(sDgt8wL~KF>$3t&hvdV-ZhNhu6dB!-HWB#g z4b1!IU&9jCGE|UdG$0hhdZ2jm29*Y@u5_?G?+{4l0avqg$jud&jY`gYAb<~sf+sd- z;|LW0A5KuVr{y^Po4Gd<(6zwaYF}MZyUMEF8_9$Fn1eajrKLH1gAME0=S2=-Co}-e z@bvqjJ&gezCcCvblXwe!`D;p?K#O%{D!rP{{tsvZDG!RwC7JY?{*v{rp9&Cx20Qot zkudOJZeWGh9z8=W9OKsz^V}VGiTwvg3k4Y)zn}vp!1$6J75chsNW8m1rwd0NOf8E) zi@*2w!r3SKT$~UV2uq#RdG0he^N)Yj?);V{LWSCTq8(a1_{>V=BQr|a(-6aM+YEV! zq!lDeona~my+&i5hjKOj!)&Y-<<0g5qsaq1yWyvjBnK(NtU$x~c>K??w%=o?;+BbH zw4*TPnF+JI!L58{fssGUUyV`NMkUO_!#CMM=E>V;Tl1aXXhkZ&!447pC%zj8z-v23 zL&!(vIK-JjQjMvh(i}0Uz;|RYOZrjmz+q$B1*1?Z=68w$>7w_dT)?bMM#OWYG{oJ? zGp=!tS4<6jlLyo*jqVou^4|Ls=~q(#>Yn-AZI~7x6aZO0rT`owyS$CKm}w_hR2wN=G>cH~ zrLtkun_&Vhb`?&ceIuXt2s%#CR)hx^&T`R#iVn#MQZhFWn6N0+zl~x!_-mxl<})n1 zG*K!=-y0N6bQC3|sfhSO2CGdpizu{=nDpR%u&;rO(el{;w7T*|#hYTMq-1}eL(!kA6jDh!iO4`GA8?xid= z(eo&LuE=mea+f!B-PM|(axnb*;%H6JztLAYt{s_z1YZ|)`L!L6calzLvo?CuA*9^n;RBnuWRr1|xdQ@q=l!A7(Ol@bGkb=aL zz^(i1@RXjcx*2yal`@)uBCru0J>NL7QV6V2kWqt$l*uSthuyNt12-d>V-Rap$WgC! z606#FfK~EY4ag+^TgDP*B84yn1+swA&oOMAOG+=_Cl#j*-t0O)3G~Y{tC0C8Qxx$FlC#fL9l~vsRvmhrskm7Y%DdKQVuThoP)yHx<)ek8UqwnA#kxXXX0_@kY1% z!UOPUSR^{$zx~u0EqdWetNjNlFGO#P!|{)TTAkz&Jpy>n=QHeU_yjr?ekdB?k=AX; zfc!FRW=GflojTn5RCJLCcKL4|@Fs1LKUEQ`v-YrY{MN_*x%IDmsgG~%1#xJqF#kh! zvD){>>4}2t0S5pCE&j`&|<&Im7@Gu{-qs^)V1f2Vz zt}gAc!uSLc8ReVVth$EowXe67QTX7kRNg#9dfXjRxuV48>1@bPQ5tVuK}L0{erMtn zD8Iwl3KB)ahiRA^f*l7lbLZWgU3M7Xh_f&)L5*L5R`(lBlPb5XZqitYGb1q?D5*Pr zt6ltBm5Rt+luJYBjxlL|Hsc3!b)pmLI+Fs5c;pGH&m1xDdv3D|I|TC*@7w?5SNxoM zIHHK7dST~MUN)C1pVz~Z;MYHvLG#KhVKC*?M^U)4hq%`}z=V#q*nbamHk_%IRk3v} zg{k20-Q7t#<{GHe>YiYaJ~IhBt3WUuObu;cuUT?D?vbO=^Pb2UIVQ%Uow-Z68GYrD z-W#D@)ejqxz?2`52N69|{npFCsZ-wEr}5_>@gKWxOEKwDfR>&AlPM7GltVz#Pu)DA zPmTh=_VBN=-tQ5cDQf2MAE?~oz2z-0yoEEj&)*EA%x{@;S;ggw7_(EEevr@q6|^t^I()Q1C3E4F=2Y}D)UiYl zRu=92-SN!U83wt9j3b~EGvI#WF$_(|)raNG)l^%u@UT6_MD#mVm~|`we=ax4Vs|u? z>V`@9)5{BY#?x#&-RGV#jQ5iN(DxgOl15(43He#4k)DI;IG})B^K*un_4}ck(8&ef zI(|p$5XGR#HagFgxnCt@D=Zkx4lxa#oy$pHPqXZpJ7ka#*C1Y;kJxaEfEBeCA})#* z7!BYrFz+iP10{*bCz0B6vYsA=2@hnVl`RL4Md6jwE>E!xf{Vj%Aq|lnEkdmDI8i$> zM&7Gvr2B#;?+PH2L69tI<@@DeWLDc_;-}KC0H_VF_6IM1Ig5?tnIq~3By~PrMn?US zVKdS|+B2`?s7xJf#+nv9toM5mz>4_ovZGWlpS=rzqO(N%lg3&g<5r?1&~d^uXpsfV%-fc z(<^m;0xa6sO(QI?34t1+MgYh5=BRISjlVCVU>6!MijIOkk{&^~Itl*8{6yW=0BBop z5#bWp2nbYGR#-aBcpWa-fGqwlW> zF809QGmRdZOyTB17beBCd`MtcKo^hP&}%PX>{}x1Y^s5YoOMY`(q&%A-w1XOA>i_t z##0|Exc*U6z6M;6$Cx5WJ^si^e`w>Gp?DEkTli2QqSa3m#&|pcGV<#jvT41+C+jSJ zm&gUf1tRAEebPtFCv96kk@Rg_t#CNUFxRyU)4yMqRY`Zgi{h|5FbQi!@K|*zC!rOz zy@O$`*8z!`%<4Pv)AMc%5uv;0oWjT?$r=0LSX?u=fuINr%!FolWa17NF7UqtQWlb(mbNz+gyjsTf)clw+pXk zaN(m%7!U9e10JCpysYJjB{3?b4^PBV=_lojq}?_?e%nLU?$Z15FoBEdp&a#*pg5;> zn(VvRSjoCrHP4B;clEqK=LEl2_%qvsY^w(?9Q?et-OD`XW3>$r7K#CZpBW{!ba`j()%fcPJJE}5EB)ul?RJOc5s&f2(y^eelRrBEl zCtdf%t)UL+Igf7B_y$mkt4(ay15ove-ZL};+7RnUi8~;P)=c}t1S!~rWeS?Sgavz{ z(9RblV8c5|x^k1;2TB$}xSlovMcTRi9EwiZ=E6Du`*Tb+eT2glbtdWECv|xSniE72 zs`FJ6aQuydR;#lm0kW{ZLLPVcrc{@+8pqua|CbA(7se_CLA*WG+BvA)MyKPqZ>9~{ zNK&b}Id;)AmlEVojN;&5?)?mSI@uNs}ptw zyVg=J!7yEuIm>}?KY`1`zZF}9I&K?NFxu9?Pd*s_YrR8Zv|K!|kUHhi8!Wm=1l337 zx|kx>{_nXx3iZxIE{;rxkA-|S@HTW|DZ6DFc)u~E4dV|SW00scmG3rU58nKP!ZrKW zuNi`ZBSVCr`;Yx@v#)ja5a{yT*WffcO11X$(*2zlX#8d#6rs1W3^2@#8pt8izQ7yJ z+&R1DIopybHW8<6$RUK+@%Uoz^L05iz<$HnEI66(38q1fo}D2rS7Y}CS6uY`R(uo< zfm+cI6>svK;HJ-u)np3dwcQ32syQYWGmY(9csTK2V#45+eBe`=xc~^mHW zS_g|1!KDt~Ys~6_<9`+URoU8?C$3P=!%y;&aCl8t5=giRvWFBLw!C>7rT<#_xPQF> zyQ)k!Y(-l|H3ptSs!_3N@h%;G~mbf`6sD?uBT=NDO%6pTOv4@1}@*% zffl-_nTzB^k$~i4{OC78UCKJZB8r6A@l3ffu0muO%ZdsYPOr$R(H}A|VZeH#x`j0g z_x0i~1tc7y{7Fwrv;@VRX~IQf`ZEN|(f3C`5#EbDikNIP6+csV(Q+0Y8Evd!spq=B@AXFC(L-Tq>_ajRtATP zF&)rS+i~F^2^Y!PQas`uy{1bK?XhExnk|FG79m9mhcKb4lLn|lk!_EJI~wSw#5y@j z%HDvTdU;{6QGCdTJ?Z|$(gFXM0|u7gyTF9k%mJ+KX9xbmKl_@YQgmMa95JygK;i!9 zq%zrk+4hGKt~T$+#cDrB+gX9m%*f@t7k2lk&#)2djPzbBWG#a*ZG^)AIW<9}Zl!>I z$J!GC3O4Y+(X`*~l%{n4PR0~$7_*u4nY6(KB= zv%dz;<0!h_*11~yCMLE+wt^a7+nPI0EqC6L_nP_#X)5(0NmzlphdStCJ3$ll^o51R z1HjF*NOXpZ$7ndi&!{~w7PU>nEJP30p1KK-AAqlDpeM5&YzyqR*yvu?*dsgoB|qF` zdY15gf3K**=r5tc2{jWxba{zWBI`&&5J$xOeQT z6k8;TZm4eYDrabCZ^Ho`Wqs;wTKytcn$ASX?D z!I_dsLd~Bq-5)LY0&{ma>mwLmw{MF_4_E4Wd{;+8_RtK+;_bPZaK+W#Ht^p3wZLaA z5>*s3uw>L9fuh=d$zx_cgy}EV-}mY59_F%eNEoKcvC&6QCfl!lh6FD+y?=JEpCXW@g$0Pmu_pl!GK_NuhaSWG15SM0qKEyJ`j1kTQa)ljk@HGtGhqiiv6~&v`sG zS&}J_y)dwIgqCgLZ~{*4L+rQZifZy{n2&iF3RrT9_p1bm^fip{q6C)2tym90ABe|$ zzvMp~-&PAdgsW9Yk&gn~Qh^WRMJk?8xu>9*TK~Fwud)+D& zzoPZ@3-Hb%>QGrA3U5yeGMgio$Q4Q3xD6}8&kwbz zWBn30md>xX?_2j`gR31XD8f_CK_?wDUz>1{^ zdAy~5%&-kkDA<}K{n|YIbQ9JuKqxssF+DKbAdE-*uhwtY^Z5Eli#MLbP)CGD71u&W<%yLjR^2Bj z?)(tZn-T=v(z)!t2WIUCbdYJc=7^YGN*rkeWR{Sc4-qm1GHTMo!^M$ChgsA|(5d)T z)5mHS9Lu<#qgc;70VQ3j0$N(aHisFX&JyVs^poa(;y-0*z)^^pJdheZeO(QOXKmG1 z0$rS$K4fM5E`ANj5Vv+X)(e{m9U||4=Fva(@P7+UiA5Y>(-1#?b3q~CFQsmT40SV7ruw1*B;@mmX>J*Kqs#bw%ilkWUo*mR)ipBiCn{IB1 zC2hO#I9FidM!Sf|FOU@|@xF}a#z@KvB=Ve-GdX|!e;V1SRh6Z)XwaIV&g@he_2Ve; zX|DOuj2`N2KStH*8lH87Q+41j@SOUqHPqAyiX!PE>DMRaYta=S z5$zYAN=(0Kz$dKv&=$@TQIsb-SYh%kVp*jFQfg!M=O0>h5%kZZrtBD%RFAmK??c=7 zhplWmU^K?twwpo(hOJ+jCn=(q9IiiI&qRqv9uM1r}VL+iCyvO{#)n5 z4Rpb(j36zSMT*q9evI^o;9}}oqjyk1pspC1Y+uR+1R;kjCL+K|)w=RjBzZW+kZVK=CT>f=to`ny{T?Tm!cRY*P31rop!e30xnNactr_ zEy{zmQ4rCcct&3W+)cKpG_(WAjp@O=q`6g57uQ&=bFs`*p{OSmCYurZGtv9&g)0l-q~VPefYdOtS&6b9Yj^Tgok+xiPacKyhK0qUC?=I z#o$VY@%27fQ$$l-Ka|z7jDia9L~C;l=GWKyfv@_{W>}*k1Evk_zmT?L4bNLc#2X{nQ)%@y(oOkS}H*7IhQPy90Of&skSE zH$+A{+coQ0(bK;!fO+y5!_CU!$`N@lA!5oIFX6|>qVueEQA6=s8_(+rkZ`AMt7QfT zxQ^NQzjeWXc3!^v?QQdyX0RYRT#BKpWSAl^E?CG@_$%D#7j zC|x#>-$wo52=v4nk)mE}8a?co*aDKBy~}S8k@0zf2o}i1db;YhN-Kvaq0<5ydRM1z zytjORHQax7)HTenwX!vGmiPSs7JM_;c?(&iUT(J%)PhZv-fOtj^CA%NTuC-h;a`~a z*Mvu*-d_A1h@>_839^<6J@Q3#zY84W%L*ZAIeqM@B3Uk`&i=(yy=OOp6D~nH?k@m_ zpBDpLuiiKv-gnF1$!(8czn$v&t7<2sGQ@tI2RxB<%~V=1LH1j0D_Zm708DDTRNs}- zew)FuFHgn&!Do!icbjv}{gG$Uyf|}>A{~j_G~cNp;TksPZ`ORJTnW@za3(4e*)^5i z2OjMH+Rcr^j|cAVkZ-*l2R_Kb)nY3LAy40&C7Iv7@d(rB`s#!jmg!9^G!8p0M~dg; z!uJTggxK}_!Uq+Rbd%R*kp07n8$P&mlok}YWMz}d1uz0Xa?}yrUG7*LUUS$7KrI0U z9nbc`CCB#J*lVxl=ZWKfg26A7Np3ic#njuv3%};Ndlc`B-2J7|irE$!kKx~U%EvhU z1DelV>bOGvq@gr)e)@{_uJ7c45=W&QE)rQo_W(`>OY$lVxW2|dHy=xV?9*rg`he-s zli*<@;YDv)eJI%fQ@w@sbe4+w5WP*ArBc(kCFylc~l&;+U+Og`ajK-yOuv- zilp;fuMTmRSVm(&T<`N}?1H@vHGWjqxi4DOA0L42TWz}kyVrMD#G$HFPrv6c5~W$A zR(2n*Lk9%1tyJ9Sl>1($eq3R6==zIA;-_xk`m6Qf+xQjh-|8TCM1H9pqWb7~Z?S$n zxu48{D)4nTkhrI}4-YT?@*4Ysfc!nx=3*y+A%wOQqa4jqPebAN;2sB*(^wE)g3&vy z0K?yx>k$T}Wk(xx8zcVAVt%caRlo9$;H!&EeGrob&itQ$w+F}--QD<_q|*g?M(jIa z8_B+dbN5|h)YS`7Z{(Z=wE*I?KXN7AEFlOuRgZSC>Sk{y_OJ5!kMs@8d*vbQ%38D3 zi!lc^1N89;g%)(-j$)$o8#oZoC~D>UMayyQR^Jj^dXXgT?O%byS#}3~HMjdR~qPIH+rS~@r<@dn7X85sj#qP0@`}vr?~LO4d}mOj&Ggk=Yo~V45sjP>_S>2 z=b*Zt{R;?FS9Bh)6gR%FcI;9m$6Yr$8ojRVNP?To0whmHV4IdXaoELz$a#aCJNF;b zxMdLtnfDc(xLe)TZa=U_7*6BraSK3qDwcg2H2A6Jbk- zD~%Rcq1!#|8MWajKot9A&kFEn#kFf#qKPI^6Z2XHLJIdmt8l^R@WicBk-DPKYgl0- z;%8A5TGE=h6zlP0B5U$jNUTIl4x21A(k9A{Nd2u4Gq8(elp zD+8dK)HG*|Ly^1hJa~d<___6VA;q^xConbh5feW5S<+q30D%h9iqMnYUDvIABQw>T z@%D=K>x{-x8vL{!PS8>~;j2EVKy~}l@mL32xr_Nl^=p@YhR?b3(qW|=0LSPQ=!}-6 z;+9!Ia$}OX!-tuz98^{zCuVZL+UZWv=0aoT)TOEMLp1K8>2I9)o+~n&bFFltgGS>p z;q`z5eEy_9znQ6FA-{&V-_$AA#%7sif7X==ea}bTu2tHoZ{a#!(6d}{9J6P`{8H|` z97nUI2<99CbGAdApZ{$XE=)za4Lx72*%yEh``=df5@6Qa-p(%3x;$Qr5Rzq6YOb^o z?H{H!n}-jtotIJ2ej5AXNPE6N{$`sYYQE*yUXbrn>1HQn+G6jL@Nbu1E0T5j{#8at z?db$+btVnd`QTDv*nXS11}xET@vSv}GWY16n`WkaAfnhKj)gzz-To0<_zBIeAzxzZ zEOG^ZsMWuGUoNfruYSF+W3M=>%Cdg%)PFVX?L}f98vt=it3YsP!fvRG$Qj%bRuc(C ztS2-3mQ0Ema0iYj+puVM2T&HKtv_DKAbyIziT}u>_SMp%Us0pP{jumct-y0U(I3y} z1{;1h^E4m0y})zFLj5}91lEwXG6#xtG-+7lKEDF>zb;mLV1N@}Q?cOyFKZb;QEbmz z7W6KQT87*(2KxKsO8|aT$sSAOa^lM=wR$2JhbbcaK)v|XKx1A+%UkuZKVF~3fpm3J zN61fqzrhh-vhHsILD0K8eZ~^;L#2w&mPmNe5FB ztF=Y69Hj_LVguSLeiTZpcQbYy0vE0ojzA-2;r`)&I@#UbF*_2;N27J~`}+)gO2v5a z^*p;#*B_Im`cOZZPb@LlU5IX-?2x zy?&K>e(Rb$twG7Eo+cyfKx#Vak6*OVyRPMw{l@99Al|`xzYd~Zl+wj?Nt>?x&H5Y) zU6VWhHeh;ik?{qGt+{ZSs~Wvfi@**r@U(>u$wAEq#`vDCxeLU{F#X|&{M-si+_OB} zjPcFakc4^uoTy|f^M`E5EPU8d93Qbm@ru3{(a9*&Hj z9F*Kv?{c-|-o;`SvaU3OX^{l>1hOO^AZ0pPM6zxaiya{n^}V$fjB*$af?u8?H*DVR zZqKv(!|%Gj%tHo!Zu#2_H*JHS1$upQl*_nKaQ3& z_|}w6$=;A$t~n5Zmu9Iy+j?l8_yDW}n}Ita7tT#+p={gMbq1Fr4GwL=E$q&pHRHIe zjb$rP_M9h1z$lu98h9I98Bd_fv0=`8&5_Dn9i6QG&KNi7?=4z zE=iaGnqb6%DU323rMh4bGRVSGd`^?`Fg+QE3Ucwd?# z{p(KdIPE7+p5lD_X`a0>0G5N7={A&X_vf->v;qz<*oVaEbb$&86!0G9J>xEH9H5Ja z3FFo7yd=wdxde^#puW!!;SExkp{nkHK>;xmUpQmhcxTk`JZ%%ynUdtFuGhvJ6^lgZ z$1)+N0z=GR0L;(0Q}uNI`>;q0k^}M+iuO0 zd<2N>{eMMC#_S0m?^V5wE+uWgQAb?7#5ml^J$$~YRAg_`3u076HP6p#%taX?2%SOF zEgxhgC-4hEur6J+9`lRyfDs}z*5yj}+@j&LEjZ$SlZc=6QT9p;L^6kZby=g@CxBD! zjtu6Yyxaa3@fIa)p3GZnvGk$O;F#sR2?JG}o=!?nC9I+G# z+yY9Ld<_QNZ9a^pI*` z&$NPGFg&px4Ilz9A__OZBy;A8OZe1QbukUo@--#TprOCp3>B7%8PU1tl`FA9!*&YhAaVUA?QM1zLT_3 z6cTw4E9ULTDQO(n3!dwdbu+Ofvw0$xLt)~er9=K&A*_Q2_g^)>tK#~))EW(3mTt8x zhyAb5tnIZkV}+n5$iT3E0s`!QNcqdk<|0RB3>^8le`H*B zn4rN73O;jm*Vi&n#H{%5qsB!)Q>rna1hlRygvuABIQ-oE$0_zWN5U%f0lT~J9LLvB zwS8;2^hrc-vSCg%1?Fwa4QLW4b6)^We!%so{CI~H#O7ZIH4#~tyCv(KGc?RCMEWs* z*CNIPgaQN~UYp;14*$+Krxi8yZqq>Y)NQ*o?)`-0446RPZ6EAT-O2)K6{Y~AMNQ`Y zGDqyX@#Ze1POp(U73!@I!RWQ{SIT#*UdaF$TJyU-bXKL*J$&A@1LdHoo%=?>^XcA+ zgtI@Ezbv;C8TTw40Grc_J`>a^#qRDAo#${o6_U(LKkIYijhQtt{@qyeD(uhJs^nVl zT|m;3DNgpy?{D}AI8dSxOEEsMoEhbfhus5E%m6vp*3A+h$#wYTs-eWoiJDMK(*YC*L)|~nY?I^k3X93Z$yY# zGW`I3h`EEv7Zsud>GO!7CoITmiv{n0%r-d|1!UDsIu$d}r;KIdv?|^ZQOaH|fT@|` zJ?bgzBbNq@MG<#$z8o`T!KBi~{h6=;QF@JgDzY4s)ThvhR)l)Z#X}n2+6*f5=WscJ z4HNM=HyV!uST~#vKg_`2CqOLd&ioRLXy642gv7t=%yD0*og_6X?VHrQGElVsO<~1a zScX+$<%=SRipbR}0Xt#AGsIlHbpQ%_@`!*yILM}XZ5`Nok1nTA$r z+1*m>qXzv~-ERu>qde~HY5yqR)5OL3f(c!*8iiD@epp@XfW)#NN`{D$9opux?XO+k zpt+eV;#k?ZROm5!{o{VX=G(@c!A-(+(&j0aw zi&?qcx|lWB7zof4@Mpi0N;ZoS2xl=xdmTOhw`!;;>_Jwv%KETxcoNjN;{#{^wPLTY z3xY^4UZ_q;qol{wRjh&hM|@N5?9j;c830Y5@w|$tb^{DUA)+Z~gLw)9r_>1v%=#0rl9h;K*nWwFkpAMBy9F z{gKX{?M(!?jeCp8xhp|}?IgEVD6pUdZ8QEp3!uoU9r{5bo_B@oRCSYJIaNE$OIh3z za62FL;uKGHMC`?|qsIGt*_@;vJBRMLH7|Ea1GW4H>gM;=e#YRnBBAl|(~WN~5yG|8 zSf+0mgkd^V6(k5$Uqx;8?WbgI3*@J?DUa2ob-Lsza$PaUVgAkoe>Tg7@WF|kmdfZq z=V99|wKga^s&4!%)*dFY0hfHp>(>lW?N}}EWtL~(VN(bb?^ikI)mfkRT8a@O?No!G z@bM5!NM^`|t>#;$Q$8;&qVS)Qb88!QwYZBzxluLGNgM4L9Aq;)vzaclq&m3jzYfuCcNt zp}L%9L)6W4gE>GGJF57qAI7hm=BS&(;T0EPc+P=G_D<~l$MYUgQj#kY+uAG??vaNv zpwIF=t)a^1%~6v_kTd~lWYDgbxZo}c!4gttJu4J0K;Pra3^WWq7NfF_JFk`{ z?HvdquViVErXfRN1(0{Kw?ESBH zjUh!xVd_-$lS=Y^J?}riS(GLS-02{rE+^P(Y9#K=>z`4}CfyS;O~1^8cL4ZC8^mUk zY!bxYM^1d0ykyb8uv7~1uYXj&LbH05WqrbD$=veCf%-vO_BnqD0MRHYKSoALKF)NA zOL!myw{--V|v~0b5+*RWhR-$Yl|~;K}p4g+!`U1%`>nCrvS zSvp-vVHO9mWV0T~+y2TDwDtEq;&f&Dn4lK@SNrx`19AoI>2&fwStR`b`#7=3uGvz7 z*wf?35$)38`X@-_QSCXDiz&Az7vAp{-Y@9+7!77GJrS?tF$ePiiu(6u)m&CO4AWXY zF9E#tg`U<#=F%qy7fZH2W?17>8R+Y@Jqp{#ZxN`fXoXQtlnhXQIZ6&L5Wj9ypC4D9 zz77K@&?CV7lRG-|G34>x**Qd$+v2rE-Hc+6`6o@D_rt;_PsiNh0xTGC6Uu9E2Oyi8 zTjP`2L9FTY{;TVkQ-ss~X3vJc8mA4))>^>~imM@gxh*X>dfP-x@K1kds_?;|`*+~P z)5^z>HT=mfX{#l;$xx8*lkChHpXZ3&yf;hdiFaYXWF6ty9Qozfb_2+Pc8|HABm3`W z+xTlh!hbZyNRl{lJ-1tBBAa1U{_`9>`q7mYkT44%)+@O1k_|DiZ#xotlvUS##9~eL z7|wJ0`Y!NAO)r!+Gw;qIp0)@wLKIEXe7kvcu0RW#-m=KO^_|_KTrw}upzZR&v zXSvWA@?kde4w7}7S@qdahNLo}YR8~Zm3ob=&r%Z>h>WG@%>>z=?L*hYJ%AsW2#RAP z1is4}UN;E#FhV3de}3gc)*TfW2X&cKgA*Wywq2)Zxs<`>i8#1H8iV{F4mUojSL zrhVSJ0A`-tJ_C2hZdMs;K`XzMyKH}NrfYaqYVId|uLThGaR1kWrJA7KLe@QQ%vY|+LR0ES`D`RsAKvccjlfMqh0A?ROKqk4l|%S-hg}}*k3>hf$&40O z{jaB_sxi?V5_axl#)nar7AU<=0c)=^hH0{RK2s9|0Lm5l>*C-#Qe=k8f3?O%4_S=UrV zCy-`GtLc5LQGaxH-2;-dBkuXKc9$nX5y88Xs&H2NiBEf1K-v_NsX1J{Io)^eNn zEtsC`DE~1PF?dg!dfA$L9`t>N5LpZokK+nR5R%ens-86PTcUE{RpoO?+WLE#+->jC zb=JKaGsaED!u%teWpeRmsZQ4qR-kq#s=pS{i0YmN>BOoBK2o|aA7~9gm?hnQyK|dE zhwL*)$ev}7D(DBPSk#zJS9(9%l=+y2OX+{EpdpdR*cGF7P%H+j;1NHLvFDTTOIrmD zwidU4MrGuExm$RQl&nnDwFXmADGDFpS`<}zpI@Vdqbds%E~KG5CP1ab$W;#7o67Ej zK@S-07T0u1eoFgZ)z%j#&3%N0VZ-xLcYNJ+mVJY z>!y`Lz+lciJep_>ePYaa8y8TzRG6_x{jvP8gO{y1>Z$@CT28Yn7m}Sc-uO~l5pQ4k z@IdVny+(0)I5b3V>9IJFc$&xI5-`R)R+Z8-_B}WZnzC)BAg_%$Ed54XI9Nm({ z1&`90i6QW5NpOfjcH!*>q7!@6L)G9H<(y#n^oB)r=~w2{*Y%mUaM_mZfu3T5rkZ!G zBk&j$XzKS5Rd-?&z8EGr^8F$szSpe5-~Y=4+{~MwJvip3g=+_?Ulhk_7D!*BPx&J8 za*rXFm^6D9J4EZ_#J3)nmAJO^ouiZ`1DJVW3(Qf4&G()UJq9}#5~epZGel-)mW$+bNY>}1I@te1LD}7x)TFX#`>@` z9=WILG_sbap>N?86o3;Ub7}XznFN8p(9F#r)%2jZgmetZns1@dxf9lb_E@aOq$5l} z2bYMVE!d+HF-1N_!YC_%s@z$NDTh6Sx`Oun15m-(jz?taBz9Q^wn2ZS2!`mr&vncl z;WuxFx)NR?>iUpfLEG&_yG6_gJ5-islu9e+_owpewcs*vE2J0*e z_hBKK$S`NJQ?-j{l;p|{VOz@@CFt-*l8h1x-=DvHa1ZtKeW=|3{H9 zcXiJs;e)YvxamI*VF}awUDm-0mi=X>8Uw4cOxx{15r|aYL@+|=^3592N1G@eDIoJ&v4d7l*zz*2d{cMp1NHfD# zTpeWuM`(G=AbQtkGam@RPr5~}t{Wk4TDQ@*s==~;^u^E12i#6f_FYSep=nfE7BRS9 z%)&I&jpNzCS-GA=wP|+}ZYz}z9C3)#;nxtH7TW0vd*(lD(Fmf{T zvGoA2Sq`%J<#r~<4#o>oWL*tJyT~c1HzRe*?mKVEQL9ywTfQX17mB2OzogCjtRn*6 zDTx;#{++_bjXvJ_!M9R?nB>>lYzkS0#Cf9XmNFWjS0Qd;43gFVT28svwh$JHS64Y$ ze?Z?JGf|FU&AAd89C_x=UEt3y66+rh!C%-2PrE!V+FCOF+=!dc!l8v5l(mWDDqkV7 z$q3P&bnotlq!Zc4A1@C}sd0WLKh+XN;^8#otGBb%QHT9Rh|jujHAWJ$rWG?{jfP6n0$Z6Y(4ZqjymwhT7DO^G>~RQ7=p|fE6@R&H z({bzEL#wvDV`rEHB0i3@t}rPv*xdK84o%fqion$6*@H%lWxR!R9PYuv*x3$fLpf4D z5u!h$lgGI+M!ODKIT&D4nhY>xOCa{SswIF~2z8&OUKh zbl3pq<^w;4d8>T494iMth;LLh`FynTfQGk|wgF;pZBm$+3R!1pyqt+X$hab8q_CtO zBHw($tznwY{_`+N=7!-|GkPBIZt%eKh$Tx0aJm$;B_2g3^J@u;pX|btF0e>ccG>@c z_v5$g*=a}AyT%}9@L=Vxdha$o3$jp+zIuWK#4SeG@sq7i>t3s{Ty}7xm@KnqO?(8& z=A}3`lSev<#wZqhzIuYk*u@M1A_Lmax0ccXJ1wUEP0BgPy@QV%(rZ$eY4(H3u5ZzJb`HIN`YY*z0?~m-!ozQ}+E}AYyX^e& z{B63Imr?bJ0S3At0=DFR?8KZ-#r@C9Q?4;s>=b2SR{J|GV?cI-#*Jn{mY#yaWzzk^ zB1VrmB4j-Z72jj-aCalqdky9;wp*yb^_V-DgQG?Lig&J^q|w)I7D$qHI|=$Q2+D>~ zCVpF+dg7-FGtUFEu#BfZIH$+G1Nra3I&Jcp3vbOmWaVJ^nRet#>JNc>z1#NB5$Q^? zpO(4n;JJ6h#3?Zb)8Xe@_P_0<{RcFQWVBqv;Qk|%!J>QGI>SkLn4Ch4hSGAd-{B&L zYl5(MjYeZb&ZU?_1 zhu3gRK-W@97v0fhJEV%ko&!rfT+C%T0YZYIU_eMFTJ@XSvY$d%)4x=RsKrjHJg{;w zt4A}x=BJNCzB+yrI<;qq+9u9y0 zZ*27Y(i7=QZ3Lg$>)~dYlbh<_W9RS^@m;ln68)h5CTN(|@e)d=o0Xhd3;Jfz*5O70 z=_76EGnO&;H#U^6BQe9P+Ap3?Y=JeoLO_Gy!vNH^&Z@4GMMjCsB$lMtW-+`7X01^v zicPog>DQtKjGkYJ?E^1ZLDcF5tSPyof@D577Np-V_*Md=kn%-fg*m&4*?bmc7j?Q? zK%UxXOqB<;=#@$?eB@fyr6HO#8s7Hv1ZwjR+=H+xej+0z`x%dt1I_Dy`%A>~^6(WkD&zlph#%9?dA@a%jOS-i&g>SqaMQAAI^iU zri)$LXhqv@6zn6TW)Ee2Y!SYBPT{5mwa+uQ44ku&yxVrUc=K>}Oe9<4hX?0OxWOps z!Zzc*$D}WuFI|QT31)0wymR}{EQ?|ce?zA9t__QY)2+SRqn)bV@_GXa~Hv; zyt?8|FN@fBR@SJ7l+M=J&)^2xY%@d*sUAAuQv~f!7r-k5m0ySuK5(_8x^$nPdbx(w z6|LOXy z1E%S9TS6g*#F4#h<>&bX)J>9WS*&D+AtD($O>DuEG^0KcFmXyj(zuc1h zg%Ay4(3-lqgfa{W7GO$sTeY!40dCcxU$!Aq!r?iKiu(#gVVIPn7f; z3lYoRoj(xV*QulC9Uw$}}PjYx`h$3aB9e?<$bbq9TT)_{lR?PU1TFT?HM4=)q(bZM!CSsC5vDN)u zaTH#SW%N%~&UQtKYw9o9=!x&c6Pa&!r$-zZHG|x7!j`ahn1Aqaj3&MN9Gs37Z#!3&w|+j!7WKKS^auc7HI>?F-{d<=84&6 zFmCeR&9WJUs7(&#>3p!i!s(=BFk7>$kt%Wma-+;qoN|&mwwlD zKHt~tcm6)7)66{2eP7pm{cf`z4#rojTl$mTA!Gy`@0LFI^@(nDfw;y%7NzKp5;1OOu!@f}oDOvI)l%0Q4s7 zs#&7B&EKzzn}z3@vU!mE#5X+ok2CN>Q*&uv zXZ0x0E2RObg1^)KF`7LM7clYK$1|Zzlt_g`__h2Y3**PA?2lwX=UgC0mx-J5RHK@G zuPHCU6`*_t5WF}os!L@09gdWM4<%to z@N!qSiDoMiMBEah#P5I*#WOn~G_zd`A9!QBs;c&N#*Mnp86fZyi?-zWL)*EDZj&M6 zioA1FBfD|;EWppwDYwSSX|l|J4sr-kmx<13E-ghdK?Q)a=Vj5<6Lk4Oh_W zjsrtKTXl(UlJ_jWe~%np&Fc9(7#x3m;b~o*Fq`+^`ehV|8_{*-=rfGwbf9X*j$><)qooh&qE?p z+fM$vLObxF0h_?rUh>RFgY^2_a~A5&u$V36`W1*Ts{%UwP1A^G5($d8k_2+$i;Ia&xC{UlD75q)+fK_>Uw zc;H4n5Sh#%pBX|G4yAol9-YI|cY9T5gnI$BM(a%#WhzQ9!?fELlTeZ6uHlx3t{0R) z&y;Vjb6?BhlY1IZoBL%@?RNh##15aWq9@`;d?NO}2C$cGK0(XbT@XGKC3zi)%!x`E zzQB3in^`-uXY^7~V{`=J9+dqg@nU`sdf9XROdW830%-2c4!W4DS|lv!A)gy=X26A7 z&fRPi1!?PRypIaG!5275@8O799={5do`8c{wna3m{t4f8@e$3|19ARGvK$J4y~;Rw zPuwN&`Th|13p{o8hh7A!F>JOz#;|wD@dkQ6j%^>)?;HXVRnv~zUqmR9+mhtA*8;cD zZHwxVol0`S?85|Y(_lhFpq@BUfzN27JL@7FtCg@nHEGl~xc%BAgn9;ONnujA59|0W zN&UW}xuc3|n^x!fD7#o$jmxL$xp|&j`;85SX6&^2#;}wefjvAiC^19Ayk2T(Op-+Xw*YZZibbP2IL}xM3p=xF&9dGFpED^=I^?RXu`cs@n4o=TXlZ1U}b9I?}U2;dI{D!r)!SRr2-qQlR9 zuRbdf6&|5niZ|^3_9qbsE$zOv4QU{l1t|J8O1SHRd<Xv&k4nFn0F9$X|id!lz z*hBpUNtk5Cq}zl-s=iZ1TsfplTxC89HI@)k0*Xlb;KL^@Fo36EG_Fzv7Hfx}<4eOOI?i-QmH*N$6cR%Q2 z1)cutx1yZm>h?i`q@>&t=g?z;9w1elT~hvd|7@bT$l_Z$2Q`f!p8ux>h_N^UmYD2q z?8)+B5F(2xJYpAxPXdfGfU$M7%+EYgRsgoAR~w*#NF72Dqz|e|9hlZwq~X5l=jm*u z@EHNR)eJE*ewW=j%Y2f@hw5Vz_z=0f`*SBga2m=g!_C`#WwHR8(+(DAIXgtRUpGP&G#{E`9~oJLixIpUU|BL#V}$J8n@02$ZIfWP-OXi zh<&?o?bbsWFlykrz#VaqQ{_4$9HX znLDe%B5e-V)!L&~I_)phyQLWXZ=DHj-&!^)>e0ZuxJ}W`aV8-0>H$V$xLWJ!rNO5kxv~ueL;i;BQ#UM_*NgQn z2tt5nc5Xvm3>P~g(ol$yAtdQSr_}Y zRXtjDJ&sfG zdOm98o6sZmlfARBgntdacSHQvB;{^`_^sG}{Y}I%6{`880~$yAp0hKHf(WHXF@#&F z>`kGi)$*m8fbFjvxw}N|+nmSk3Rv&_j(qr>))+4+1xnvuv5zNMX!*Qq39C=Xp}Q9k zDfnNCc97CP%g2Depn2yoP5q1=G_Q|DxYEG0|M-08Kff0NwUhmn?_^>Vwi8B!(5qtG zyH9}Kzpf=8mA&0Ss|Blbb>vJP12mA|f9bi_kDT#jVoSajJyc_;h^C(7U@@zX_65uS ze3{9_{H&)Szg}f51{^o zmY>vz5CuuajXYy%itv_g50!Ct;gzezvkkEE_X{V@}we?RfL7FMzym9d4m~(*X1eaL$zRHm=-!Vph!|ujQE?VePd#ukGl5_D!*-`amxNq-3P$nrjbMrV z<)J9qX6cQ_JIict0;F<^-IykS*7#=fqB@v7bg*E$HNX zckh552$xYMDR)eR>AR&vjnO-~Jq02#6g$Rf7**q-Z3Db=CBN)s3RTKjFE<(%|JYr@ z;!tYF>Qn`B;h2Fzd=6&MSO?oCuV3s?A&&jJZmRqY!e_N$jHA*YL7*=vYu7I7T*!R8 z6Oy8&^0rg%&Q*NteICCjEdf>TZx`$l6%aNC_nN4&5(tq~#a%Qa^PuC}D_Ce8t%2sa5FC8$<;tfY+L<|+WyE`q$Uk&F?Z-o}8EHHN4JwmHzF zuhs^>kjx|eO`aX0(bjb|;H zymQ>fex50!#sC1;{xw1&{o=lqa7Wk%u}HJzblV%R~tD| z1XnE>H6Pc~S!ds=_vi&`TvAn=sEy-94 zuur8Z{nc}=+6I%?#-Yg=xtqwO4d^J&AASb6 zP5AKEK*X+?^;RKLPu(zfx!JA#^(k?AAM;}jW0pbdhOv_z@> zaQGyVq9X4?Kt)vRageZlw{S;9g@y8^@WQrL^x2>iA@N>Aw%AjY1o6VCT4fN9I~<)| zzcWq?xyq4xq@(bIn6c7ADI33i1F`Eaa&q*(|qH+(1&_#YocL@cDx2xyqrzH43RoQRD&SRcGWBkC(b)Cg7OPm zFzFi1m-(RW({*f9I#O~Iu3dw8ESlNSbgSELRmf-z7_9Q(Qb<|wkI5d|Y3Eco_AFPJ zyF(3RyIHuU4p0cpZyH+6Ro91uKxYblb=@NUHZL1I>j)yijLBl+`p^IEiGSVfB>b?y z%(NTC>>u1@A9+QJlSE0f%{!|eH3<*3CnRc}bh7$VDSrY0>(Eg$(pjy|Ki9i#rdZvm z7W#L@T?|f>TnV}R#h&u>l|;^*(;nC|qFL_SCO!nnae(edW`sC26fVp@0bd7~w8@4h z@Y!Tnf8--FH&)$~L{!LpnH%=RubbS;rQF?MgqXbVkxO0aY5x|Iffi}GBGO+2cpKPk z#2wC)FMfh6qi8Pr$4|CI=E&4UE=uI=6DyW_1lC7W#y{_qFy! zt>L%)dz=FiACSeZ+w5`#A@w6zB%+0T4RszZjITZnM0(1X5!bi%E;q>C)z~7)1F7ra<5PW@=$#+m3tk9J1 z#xjya6p@olzaOL)PKuFcD<{=dxyV^lRdhhZLGw*mUl{&!xn@JmbHU3s5B&bHd=kFk z>oj9=*`FTW0W&A8Oq%{wx-ySZK;-0!`LEK9AX|>8yzgmjm7;HIO5Pk%I4TM7*DEk6 zqq6mBKIN=>O+#;-h_|iK&TBDZ@LWHDRfdiOMCEdi!YoPTy2X5Ee!qM^9$4@-{rgc& zT+3N+(BeVxk%VpUU^fVylMnp@cC^GR zah{ZG27z;GjaZEko?lSn^o)xuO&+7#@`eD5;Y9I<#ig{Xc5t8HT|3HlRU}8l-Y=Xm zyX@R<9WL%0DhZ3Hy+MS#y<)|n<*Rc*_n*L|7&M(JVF0sU;Io59Z+cN*^T-m-^|AIa z+0BcM%m;w88JA72hBVzC<3pH)GUt2S{qu%p6JIvkVulUh!NO|rNQ+8m%Oj|`nZ*rT zH59Bfzy@|J&hkVR{hf0qBx`WP1{?wom4#OvF}ZFh_~8cRXTb&qzk$xQr6eP@FY|jOyw}RQE}H&qHHa= zabsTqY9(kIGRm`NNsBORFWJtuZ;D!%Ad<`qcQFaY6)x>2Yg7HXj{R0n`n_NE-`(49 z#h$gmsdfVx84VBv(>LsgRWcpWT@qlTtQ9{bFut53R=^V=0dRT5bQaKtZ zWaN3O)EAaogA7?Lp-3Y9u`3Y%UyMPswK%!`ONtjzq5=nrX9d{AGBb#B);od?4h;SH z^p@k$a9k*q{dI-a)isDrg`r2>S}jW1slboh=e{{st{qIZaZQ9%77H?D5^^q%aTA)`PR>SCAm8emhhplhx!pr^X9ngWG)YDGK} z!7T4^S114nP`Jc(v?vZ+ zYP6ap;_ZSN-*rdC7-#fOnvf=Oca&fS;sM)jM_?Q0a$tGJqB-gd=pt2gdk7$hmLhn| zO%nhvuzNyAVlO|RT>i7->TjU&brgniA~5Q&PRh+ErWBr`@*IpnBQd~*|^;)LClqM+MRaBh+p~R1}PE?U`=Jj#82=(WVO61 zJv77ab$=2q&Vh{~B)7{?HewEZUWkKM+fYr*5Ov8P(_mI)DtdL_9$q^X*V^uCk9x7E zSt%NFEN}(8prjf7j=;oDf|e)6JXO>m05XO!6!5{YsT53uKe3x1 zce#X7p@7e!(9C{a%LJCP?^-k+x;wd{2(gl!0FPmlssrJbpXGO_x6l?Cp(h zrtndImw(ULf_o)O+PUnMiW;bSdz02Tm7>nU`uqzp3wf?G z;GcQUkGpiiu!F68qBcy38Ygyt9;u?w=~BfEDcaRhj>_;WEw`$1j z2h>sCCA(BC*Nbt6ChP~`$s~GWHcNqehQ>y8cTQZ;0QmNh(VoRveeKIY9K{PR{YN6I zOBKN9h}(bW8kTJmqsU@_?Q#Za^nLa4*vrxa$kQGdW$9gxKx`zjtCGRj;74MVg6W&PP0c}RB4d^_@ zL=EfD-YeXTH8q3p@sa_Al|4HOCCawENaFlcZu;V{W+;CsuOf74}& zAY<+aYd6AOAo0xwwe#UZ~)X@39C{yd^Dx3hwItBVQ%in?l@%_v*GIStl zX+b3m>?-Ef_H2kATJPQ6N2${ZtJe5aHKJbV93S`x`hR zItnk%`yF#nJO1xY{y6cpksFeho_U#_d^StkX&}Wuvha9gXb5G5qR|Sc#`pb0zqfnN z@`?WeWkH>(yZ3E)bI*%K3GyOYgEvhuSG|`Rq{n4Y@4@bg@`1iBkQ|nbwI4FV+~~g#;tOzxv$UFK9RrEqGVejn zT4zts+MT@2B5+8v|595>_=Fy(gYz#iJ!A}_>PXy$;Yz8-wnix z%)fDI(H!fu(`&TBaHzWo|hvrc_TAtfAiaxPETZYX%vxwhvj zQtHBT5+q$PgziTHWJ+cyBU)sw+5@JSPvGZBZC~Js(l%?~lzeBJG{1qeCOb5m*MIf9 zLzX-wx5^In2sQY)Ot^tWuS@=r_Ad6{)B)9!yqg}pVWM*Y*>jz@gY6Oe{k*b8xFIKPfsEXOT7-7cwZ-ze+9V06rZXh2}G+r%L?9(9{@h8@WClXqxf+AjweN=h}Oh z^f*G&^hU$UO$4xqfcWU1F`NKzv64lSanoCYj4z+e*3cM8OKcqE|HNl3!kUDYoB8qt?SQdEm7E3N=+|Gyw+=&x^1UxU_QtbQAgvbV zG0UGf>--(?Q>s?uWqiS)wOhg*KRZK_oV--P_7&3 zQM~?RDmI!6bHzZkM>w8zjBPR4+EM@bt{C_Si1sq=tf|NrJURCtP86dHQT7lH1SRwE z6P|zO32i%&^TW*t_>W2Wg#ZqW%T;i|snlU_cu>q5Go7XYU>QyAh9B`zry7gypa-(ZNo$tm zd+LF8@I#24Yqsg-Bjx}+?;lr=V-&3H_)UVl>P>ku*r^Y?-r>SCOz~Uq0$|cH)TE72 z>8w{DAzv9%oqjcK-RP!faA1Jm2`xYtZ<$CH_xDn;h9WJx23v{D-^gk#p4|9fyki7w zOce$fgO%fWvJqOrGJs)f4bs5Z&I#yxQO?wPd&dP+=o%>7w6BW(oxs5OF+gyT2F-NX zrJB7;mktd%euq4b5CD!!aoJ*r-rYQ`N7zn>e9NARk`@L$x4Ng(IPaQs;XYly49w`~ z8gUFFwfl*OgISwTC)gH*4ZS6?fJ)kyu=scp5WA$%=pSClYMeO458cS+kYQQ&c$Q=Z z7bEb-viYMl^}+zXQ{6`?`D^1}c?GTHBgaPA&XjtI$ts+i-ys{fF8N6HNz>(Mh%H(I zx~XQch$E`;{?ErMK2hjMD9$q`p@t1=B4|?PobXZFH>0tj#`Xi|-z>ye-;W_d%pt*C z9fVX53Lt1|srZ%E2YPYnzG0e;JI(&PDk!8O{WKKb0qeG-T2*ucN7UIpT}99RAn7#z z_H%Hvesjv8(XF|;T0gyisefxHeYsxL@QGIh!!(JL@~bGhe9L}mTR<`4Oum}$u0Qlg z`pYvPxYS+D@(e_=Qoxe3LTY7X)F7gp`7go4@H)gb%V)IJ;-euWSbHq6YW?-aRaHt$ z1~gV-1t*mNM1Hlq zUz5lkibbJB-ySeAvdU^RG}C_#pvKff^4}0y|L>L%+4#;;5OsC!`31wW-F0c=zZaju zE(r8u9(W-q%U7iiqK2nv>+6=LTnBaoR$iO|;b~5wl#Z4-mTYRhO~b7Y^=5GWU<0VG zgCtQ`e|#{~yVT0lYhumfKXhK_zDuZpnuFQT%ZcBcbU1TPw7vcj03khtj_AvrM=@an zt+{iG3eLB*@Q5g%1-}A z8b@dNr)4qykV&%OnLQtbK(N@T7AG@oYMuOV8cnSn$CE+9-bh9U${ikmw#Jj2HrB2v zj_5qNv>9X?GKCzhCJps=F6k%P&`ulL(VmYA#~)0=lR?@d5si3XM-7&egWBw7NZ}uM zqx>O;wG+6Uq3yU!zOgsAX$u7Dh|ZDSK1L%i*$mp)nkWp5a$nt<^n*W#bveOgvO0+q z^frJ_hZ;)mvwMe!E4Q|TaF6Z>0-^We`p5qSaA+{mwek5V&GYim{{>(AFURC{36aUK z0BF34>p6&dBfPi10&;mo{+0n|wChmVMH^GHi3y)f5h-dTpQ(^(7x+OQ_w+1EwAf#h z1yS8$Hr~G=DK}QTkQ9jU|2uf`8OQmZLvA{vt{I>eU;6Z}TYO4L5oZi?R&D3P&G+$S zdM4b70oBOrD)01VCo8soOjA*EwkcMeBf+zDL<*+$9OoFq1WJ&YA6gcdHk27;-6ZCi zY}(yFh~K>7W-D833U5ddlByZ!J>2i^ZG!R+`s9cvx=qxs=~Dd8l{Y%j64H)8VP{MD zAUR!U_}K4?sr8dp3CuBk#~oO|szQii7`*Ld!CKY^Bm{}Ca9O%e|rAX7%~{8!DJJTaxZKku~zDE*jt zbP;rl|NAP$?4GxwhSfXF?HmB7SD_FT$JeSg5Nh$0YP183t(2?o8{qf_B5FUb29y^l zt5J$n=-`;sRRzcYcRW*j&WsnC96SSa)ke}iF=LL1M|_aK2D`#{zbV;GkT#c#Bk@`7 z&#nWJSV!QclRPZNr!}ugEN^~=Pmw?yAF1xOm{^~fIDxwOZ5spaLa4ge1nsvD<*1B4a;Qfd?!$QZP}UUBeJ&l$AhhbK}xLKU94G zhRKFPtriz~P;oASHcU&bVBhi9mCEEWK=5Z)p2DjwDc2`?KC0ruI>JdDn^?%_PI_JR z4OE?&`8O*388{->6cZPt(}GV0=g+bBMFb*4+EivD1-d=0R_7pDpX8&u5A*oGI0rZ- zFYrh$0!W;@8{R0Kp{zGR2=H%=Y$WaEypW4eZo|HaotrHau;!i&P}Sg4r4V{fJ;oio z!hbs6wE(XC;7I^Wf4hQ2c~dr&MS#LdVK;`=md$k>R2XG6P`uu86)q7aWpD5s2_!VI z=gx40cnA&9u;3*s;Hi*#l)bkT9MNrF-?hXDjC+uc+C2c%NNB_7B1xN1RaP$meeLI> zA9b!_+frtmqR}I#e+-Bcnf3Bm(eY?{^25tMF#q~Cqa-Cp52??iQ{Muy> ziHG?pQfTvOgeuy6ChR2>3@Z%qd-m#v3zL9PfY}ssu(w`_ew@So|Jko*L@NM-zr^PK zj;vjp5n`X-$@LN&QgOfKA~EB!V#KjU?rHy9L=(_2?+j0iuEM<+F?=HmKXJ&byw6w5 zTPq>DG@Et@qNp>RTUrSRQCaiBqgC`ueq4P>3+TUHV+!7-7JxMF)jkNvaQzqoZmxU& z%*`tib@Y=GLhW_f7of%vr?3+^AVDxwIEL^96<&1-^7nH^0!|_kIv-Oj`IPo}TFvb% zC6P!`f9AV9CTC&%rgL|9Po*f;EUohuSL$#5CbU2{^s>a2N;LiALfrKKLT3vMKJ6s~ zItt@7$Pp-)>a>G;w&liK;^F)3W*qMgYCCaEAf*RGuP`{_5B45H9=qu<^N+_F7nNnn5iZpslInas_}b!p;7 zDekW5r(Y|p%YxHC=y@o4Yp&GtESP?}0}&aAd*5}=!k1Hbs#_rOsY1oMvS`87;N)cS z`e&>@Ky>7(osh5aHV}{6>}ZG$N1C=AgwG$wM_?Gl$4kn!X$-o|&DGGrMbsjH$d13RKoQm_ z>5Yl*9hsjUPohmaYgoP~0+B>{$sgKN=*XVt<@UGl88)AkLZv2&~hbiLenJdx%osc+#6X% z*JjJjYG^kNBQl8vHxwZMTTmyjFwVirO3UnVYIpm1%?Ca9b5O!1$`1uK2@cnPyJq5J z3ig^KZUDH&;*Pzxy$i6)^K@ogwEy9$MohjGkYnf}=qd~~=8tkxlP8hPb}kyp#6tmO zyy`FcD~A;)XGbyW>0X}Mw_Ku*qF&@@nDEzedEcEhQLDmPg-TZ8;x_Qy7k7D;;u9q< z;z}uAPM$3&f!K~UjSF$F*TFL{#W|UkefqWSr@Z!G@Al%}uFQotpt7dKK=lAvALhH4 zOM=*Aaaytmqh}C9WY>wPUX1-1g%}HX`uA(?fdM-U!@l0&cDU#eCB5Bs-*HsJ)J1c< zn*>Wc6xsX;16MDl1ak3W63CdIG|Aoo1S(nR&Z#Y+?GHWwb4QK@I|`G8QT{BXg*gUQ zucsE+pv=E$Yo%TL@0Q5nj1{D}Y_P~RO*;Nv`mBT5TY34d)VnjJsL*V&q^=5~=F7G-FI(e>iT zt>ou{6OE%3^;>IOdOHu~a+t>uZb*dZ^%QMJUFR&}BRODhXc>jh8&~4VfK2egC|HG4 z7d6gnZ-O)9ZH!p#%$>Zis3TXgz@T;Rm2kj-{Gnjs#ul`Pf!lw07Q*)Kib=RocftRc-sVg**IeGauT}lLk2<_8Ju1U+9w< z^58Rk1Kna(P?g$w;csua73^kU+v+p;3Fu%f4i8?)#ui?=7QUdpryz#haf4bC7oH56 zbMHJ7P4SG)$Ahce+kMn;i@4<_ir3?P*p9gSomgdyf6KXZkooDn0 zo@{_{r7y~wQR(iVpE0rUENKnz}+w=36X=i?2?=3;1vh4LlMmzB%%^gUVub+eYg#-h;UFgcp(Z>t8zD+ z5+%R@&Rp*MW(Q37!ZxZ!UzMOBP=QUZdOp{WHNkRX*!B&AHm=2~tV8eR+pUpY-@laC zjzl5nq^szX;p_e*J!zrD63O!^4Y)!SsZSNB#(4KS1~xS4A!1MPUT|`2lHIbn=!O~I zo1?8ArH}Q%H#p#+xSQywow<)?SiCbJh{TCUz9UvTX34pm+OS$92eG?H@uc$Jf+&ihuI5}Z{kJ@U3XUM zA2;=LCjT8YXBQ{c-W1Qi(0bk21-g0{zT%*Ri;BxP^v^sy`HPF2Zuag#3zk=1_9qqB z82E04781H9X1H@lDIh#DJ#8x1_W2-aE{8J;*MPU)hJMJGepwMNfk1v-b9n{pr8_0n zaI5ZVj|6UDXKJdO**|(+xOlxx1kBDsyHpDkAQEEXJdX=Ytxa>4mKigj9KET0v5)e~ z`mBrDq9dgG`{FVGn_EvqSdu)2+1#&A9NW&BL($HJnO*l#Qi+FwU>@M>C_$Cy`wmNP z7X{)w3X$N!1p9@Lfxr+PO(Hp{?Iaf0wNl@m_dqgF5%2&Gh{6`=#G_z(~8;(T=F$F;G2(W*io#(VO!y276m|u_A%d4Ia>&nB` zKe^HXjg5%Ol@(p$*o@R@VvP{$?TTaME)NBa?z1x>7-Snj0GI3G{=G+AA1MLz6x?qL z`&U_GvH&>_0(Xb;RUhj1eBlk0p$TAvN@A9d=3gjg5?bOhJ5mlhd|u0weG@ zc{)R1o&a2wKZbBZgUXmcM-l%$$31*bT?~p2L)4ArkI+F5YhW8vm~1KWPLyPGq9{?EvH5G5=MR*OXm#YD z)qQI}c53L^tNwtr53uRkO2f4yftUmnlCH3PffS$lph7e;#&}ut^YW*w09chC4u<#p z{|r3YoANFTqnVjSxmEOwu^0qG8{^}fuN)Q#@o}f!ohBvXneg@6yn)z%-;eDf+!AuB z12i>T?*@=R@=zuRw#!L@~|K+JLrhc}XP&K7~nWXTNL zsJlr?aLm6m%2e*`Xku?= zOdsoZY!KLW+OQhWSbThP3BCl`k%Dz7qI%N;aW&fQN4^=MR7Ppf^HGe2y@T5A%6IfP zS@J76C!C;)%YQv?`S9FGwT+Ei$->v(EUJuaL?xfxCBqlJ@-be*yZZmO8+Cr0ngheCv4S2=JhG67x7 zcfslV*FS`3;CcYYJLs=V`%!RHboCr$Yu6rE9?WE}g?5aLZmE}c<>D{bEPXOT10^=M z9D^l)Vv;uqY1&9a@fLV~gvn~{&K1czRSMU4=R0B#8nNiUKK%0P^7?Kl813Hei*3>!zPD(U&mXJ`E)(%Lt|D&%mM@*|6Q1H(I(<3H@B&YEI2Md$% A1poj5 literal 0 HcmV?d00001 diff --git a/src/sql/workbench/electron-browser/splashscreen/splashscreen.css b/src/sql/workbench/electron-browser/splashscreen/splashscreen.css new file mode 100644 index 0000000000..535499b6f3 --- /dev/null +++ b/src/sql/workbench/electron-browser/splashscreen/splashscreen.css @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +body, html { + margin: 0px; + height: 100%; + width: 100%; +} + +.splash-modal { + background-image: url("splash.png"); + background-repeat: no-repeat; + background-size: 100% 100%; +} diff --git a/src/sql/workbench/electron-browser/splashscreen/splashscreen.html b/src/sql/workbench/electron-browser/splashscreen/splashscreen.html new file mode 100644 index 0000000000..c8c6538c79 --- /dev/null +++ b/src/sql/workbench/electron-browser/splashscreen/splashscreen.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/sql/workbench/errorMessageDialog/errorMessageDialog.ts b/src/sql/workbench/errorMessageDialog/errorMessageDialog.ts new file mode 100644 index 0000000000..fe8e5afb0e --- /dev/null +++ b/src/sql/workbench/errorMessageDialog/errorMessageDialog.ts @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import 'vs/css!sql/media/icons/common-icons'; +import 'vs/css!./media/errorMessageDialog'; +import { Modal } from 'sql/base/browser/ui/modal/modal'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import { attachModalDialogStyler } from 'sql/common/theme/styler'; + +import { Builder } from 'vs/base/browser/builder'; +import Severity from 'vs/base/common/severity'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import Event, { Emitter } from 'vs/base/common/event'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; + +export class ErrorMessageDialog extends Modal { + private _body: HTMLElement; + private _severity: Severity; + private _message: string; + + private _onOk = new Emitter(); + public onOk: Event = this._onOk.event; + + constructor( + @IThemeService private _themeService: IThemeService, + @IClipboardService private _clipboardService: IClipboardService, + @IPartService partService: IPartService, + @ITelemetryService telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super('', TelemetryKeys.ErrorMessage, partService, telemetryService, contextKeyService, { isFlyout: false, hasTitleIcon: true }); + } + + protected renderBody(container: HTMLElement) { + new Builder(container).div({ 'class': 'error-dialog' }, (bodyBuilder) => { + this._body = bodyBuilder.getHTMLElement();; + }); + } + + public render() { + super.render(); + attachModalDialogStyler(this, this._themeService); + let copyButton = this.addFooterButton('Copy to Clipboard', () => this._clipboardService.writeText(this._message), 'left'); + copyButton.icon = 'icon scriptToClipboard'; + attachButtonStyler(copyButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND }); + let okButton = this.addFooterButton('OK', () => this.ok()); + attachButtonStyler(okButton, this._themeService); + } + + protected layout(height?: number): void { + // Nothing to re-layout + } + + private updateDialogBody(): void { + let builder = new Builder(this._body).empty(); + builder.div({ class: 'error-message' }, (errorContainer) => { + errorContainer.innerHtml(this._message); + }); + } + + private updateIconTitle(): void { + switch (this._severity) { + case Severity.Error: + this.titleIconClassName = 'icon error'; + break; + case Severity.Warning: + this.titleIconClassName = 'icon warning'; + break; + case Severity.Info: + this.titleIconClassName = 'icon info'; + break; + } + } + + /* espace key */ + protected onClose() { + this.ok(); + } + + /* enter key */ + protected onAccept() { + this.ok(); + } + + public ok(): void { + this._onOk.fire(); + this.close(); + } + + public close() { + this.hide(); + } + + public open(severity: Severity, headerTitle: string, message: string) { + this._severity = severity; + this._message = message; + this.title = headerTitle; + this.updateIconTitle(); + this.updateDialogBody(); + this.show(); + } + + public dispose(): void { + } +} \ No newline at end of file diff --git a/src/sql/workbench/errorMessageDialog/errorMessageService.ts b/src/sql/workbench/errorMessageDialog/errorMessageService.ts new file mode 100644 index 0000000000..538e24272f --- /dev/null +++ b/src/sql/workbench/errorMessageDialog/errorMessageService.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import Severity from 'vs/base/common/severity'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import { ErrorMessageDialog } from 'sql/workbench/errorMessageDialog/errorMessageDialog'; + +export class ErrorMessageService implements IErrorMessageService { + + _serviceBrand: any; + + private _errorDialog: ErrorMessageDialog; + + private handleOnOk(): void { + } + + constructor( + @IInstantiationService private _instantiationService: IInstantiationService + ) { } + + public showDialog(severity: Severity, headerTitle: string, message: string): void { + this.doShowDialog(severity, headerTitle, message); + } + + private doShowDialog(severity: Severity, headerTitle: string, message: string): void { + if (!this._errorDialog) { + this._errorDialog = this._instantiationService.createInstance(ErrorMessageDialog); + this._errorDialog.onOk(() => this.handleOnOk()); + this._errorDialog.render(); + } + + let title = headerTitle ? headerTitle : this.getDefaultTitle(severity); + return this._errorDialog.open(severity, title, message); + } + + private getDefaultTitle(severity: Severity) { + let defaultTitle: string; + switch (severity) { + case Severity.Error: + defaultTitle = localize('error', 'Error'); + break; + case Severity.Warning: + defaultTitle = localize('warning', 'Warning'); + break; + case Severity.Info: + defaultTitle = localize('info', 'Info'); + } + return defaultTitle; + } +} \ No newline at end of file diff --git a/src/sql/workbench/errorMessageDialog/media/errorMessageDialog.css b/src/sql/workbench/errorMessageDialog/media/errorMessageDialog.css new file mode 100644 index 0000000000..38223fb0bb --- /dev/null +++ b/src/sql/workbench/errorMessageDialog/media/errorMessageDialog.css @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + .error-dialog { + padding: 15px; + overflow: auto; + height: 200px; +} + +.error-dialog .icon.error, .error-dialog .icon.warning , .error-dialog .icon.info { + width: 20px; + height: 20px; + float: left; + margin-right: 10px; +} \ No newline at end of file diff --git a/src/sql/workbench/update/releaseNotes.ts b/src/sql/workbench/update/releaseNotes.ts new file mode 100644 index 0000000000..ceaf7adfc8 --- /dev/null +++ b/src/sql/workbench/update/releaseNotes.ts @@ -0,0 +1,82 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { IMessageService, CloseAction, Severity } from 'vs/platform/message/common/message'; +import pkg from 'vs/platform/node/package'; +import product from 'vs/platform/node/product'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ReleaseNotesInput } from 'vs/workbench/parts/update/electron-browser/releaseNotesInput'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import URI from 'vs/base/common/uri'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { AbstractShowReleaseNotesAction, loadReleaseNotes } from 'vs/workbench/parts/update/electron-browser/update'; + +export class OpenGettingStartedInBrowserAction extends Action { + + constructor( + @IOpenerService private openerService: IOpenerService + ) { + super('update.openGettingStartedGuide', nls.localize('gettingStarted', "Get Started"), null, true); + } + + run(): TPromise { + const uri = URI.parse(product.releaseNotesUrl); + return this.openerService.open(uri); + } +} + +export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction { + + static ID = 'update.showCurrentCarbonReleaseNotes'; + static LABEL = nls.localize('showReleaseNotes', "Show Getting Started"); + + constructor( + id = ShowCurrentReleaseNotesAction.ID, + label = ShowCurrentReleaseNotesAction.LABEL, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(id, label, true, pkg.version, editorService, instantiationService); + } +} + +export class ProductContribution implements IWorkbenchContribution { + + private static KEY = 'releaseNotes/carbonLastVersion'; + getId() { return 'carbon.product'; } + + constructor( + @IStorageService storageService: IStorageService, + @IInstantiationService instantiationService: IInstantiationService, + @IMessageService messageService: IMessageService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService + ) { + const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, ''); + + // was there an update? if so, open release notes + if (product.releaseNotesUrl && pkg.version !== lastVersion) { + instantiationService.invokeFunction(loadReleaseNotes, pkg.version).then( + text => editorService.openEditor(instantiationService.createInstance(ReleaseNotesInput, pkg.version, text), { pinned: true }), + () => { + messageService.show(Severity.Info, { + message: nls.localize('read the release notes', "Welcome to {0} Public Preview 1! Would you like to view the Getting Started Guide?", product.nameLong, pkg.version), + actions: [ + instantiationService.createInstance(OpenGettingStartedInBrowserAction), + CloseAction + ] + }); + }); + } + + storageService.store(ProductContribution.KEY, pkg.version, StorageScope.GLOBAL); + } +} diff --git a/src/sqltest/common/telemetryUtilities.test.ts b/src/sqltest/common/telemetryUtilities.test.ts new file mode 100644 index 0000000000..c6f69a8e8c --- /dev/null +++ b/src/sqltest/common/telemetryUtilities.test.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as TelemetryUtils from 'sql/common/telemetryUtilities'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { TelemetryServiceStub } from 'sqltest/stubs/telemetryServiceStub'; +import * as TypeMoq from 'typemoq'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as assert from 'assert'; + +suite('SQL Telemetry Utilities tests', () => { + let telemetryService: TypeMoq.Mock; + let none: void; + let providerName: string = 'provider name'; + let telemetryKey: string = 'tel key'; + + let connectionProfile = { + databaseName: '', + serverName: '', + authenticationType: '', + getOptionsKey: () => '', + matches: undefined, + groupFullName: '', + groupId: '', + id: '', + options: {}, + password: '', + providerName: providerName, + savePassword: true, + saveProfile: true, + userName: '' + }; + + setup(() => { + telemetryService = TypeMoq.Mock.ofType(TelemetryServiceStub, TypeMoq.MockBehavior.Strict); + telemetryService.setup(x => x.publicLog(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(x => TPromise.as(none)); + }); + + test('addTelemetry should add provider id using the connection', (done) => { + let data: TelemetryUtils.IConnectionTelemetryData = { + }; + + TelemetryUtils.addTelemetry(telemetryService.object, telemetryKey, data, connectionProfile).then(() => { + telemetryService.verify(x => x.publicLog(TypeMoq.It.is(a => a === telemetryKey), TypeMoq.It.is(b => b.provider === providerName)), TypeMoq.Times.once()); + done(); + }).catch(err => { + assert.fail(err); + done(err); + }); + }); + + test('addTelemetry should pass the telemetry data to telemetry service', (done) => { + let data: TelemetryUtils.IConnectionTelemetryData = { + target: 'target', + from: 'from' + }; + data.test1 = '1'; + + TelemetryUtils.addTelemetry(telemetryService.object, telemetryKey, data, connectionProfile).then(() => { + telemetryService.verify(x => x.publicLog( + TypeMoq.It.is(a => a === telemetryKey), + TypeMoq.It.is(b => b.provider === providerName + && b.from === data.from + && b.target === data.target + && b.test1 === data.test1 + && b.connection === undefined)), TypeMoq.Times.once()); + done(); + }).catch(err => { + assert.fail(err); + done(err); + }); + }); + + test('addTelemetry should not crash not given data', (done) => { + TelemetryUtils.addTelemetry(telemetryService.object, telemetryKey).then(() => { + telemetryService.verify(x => x.publicLog( + TypeMoq.It.is(a => a === telemetryKey), + TypeMoq.It.is(b => b !== undefined)), TypeMoq.Times.once()); + done(); + }).catch(err => { + assert.fail(err); + done(err); + }); + }); + + test('addTelemetry should try to get the provider name from data first', (done) => { + let data: TelemetryUtils.IConnectionTelemetryData = { + connection: connectionProfile + }; + data.provider = providerName + '1'; + + TelemetryUtils.addTelemetry(telemetryService.object, telemetryKey, data, connectionProfile).then(() => { + telemetryService.verify(x => x.publicLog(TypeMoq.It.is(a => a === telemetryKey), TypeMoq.It.is(b => b.provider === data.provider)), TypeMoq.Times.once()); + done(); + }).catch(err => { + assert.fail(err); + done(err); + }); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/accountManagement/accountActions.test.ts b/src/sqltest/parts/accountManagement/accountActions.test.ts new file mode 100644 index 0000000000..2e3a1b88db --- /dev/null +++ b/src/sqltest/parts/accountManagement/accountActions.test.ts @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as data from 'data'; +import * as TypeMoq from 'typemoq'; +import { AddAccountAction, RemoveAccountAction } from 'sql/parts/accountManagement/common/accountActions'; +import { AccountManagementTestService } from 'sqltest/stubs/accountManagementStubs'; +import { MessageServiceStub } from 'sqltest/stubs/messageServiceStub'; +import { ErrorMessageServiceStub } from 'sqltest/stubs/errorMessageServiceStub'; + +let testAccount = { + key: { + providerId: 'azure', + accountId: 'testAccount' + }, + displayInfo: { + contextualLogo: { light: '', dark: '' }, + displayName: 'Test Account', + contextualDisplayName: 'Azure Account' + }, + isStale: false +}; + +suite('Account Management Dialog Actions Tests', () => { + test('AddAccount - Success', (done) => { + // Setup: Create an AddAccountAction object + let param = 'azure'; + let mocks = createAddAccountAction(true, true, param); + + // If: I run the action when it will resolve + mocks.action.run() + .then(result => { + // Then: + // ... I should have gotten true back + assert.ok(result); + + // ... The account management service should have gotten a add account request + mocks.accountMock.verify(x => x.addAccount(param), TypeMoq.Times.once()); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('AddAccount - Failure', (done) => { + // Setup: Create an AddAccountAction object + let param = 'azure'; + let mocks = createAddAccountAction(false, true, param); + + // If: I run the action when it will reject + mocks.action.run().then(result => { + // Then: + // ... The result should be false since the operation failed + assert.ok(!result); + // ... The account management service should have gotten a add account request + mocks.accountMock.verify(x => x.addAccount(param), TypeMoq.Times.once()); + done(); + }, error => { + // Should fail as rejected actions cause the debugger to crash + done(error); + }); + }); + + test('RemoveAccount - Confirm Success', (done) => { + // Setup: Create an AddAccountAction object + let ams = getMockAccountManagementService(true); + let ms = getMockMessageService(true); + let es = getMockErrorMessageService(); + let action = new RemoveAccountAction(testAccount, ms.object, es.object, ams.object); + + // If: I run the action when it will resolve + action.run() + .then(result => { + // Then: + // ... I should have gotten true back + assert.ok(result); + + // ... A confirmation dialog should have opened + ms.verify(x => x.confirm(TypeMoq.It.isAny()), TypeMoq.Times.once()); + + // ... The account management service should have gotten a remove account request + ams.verify(x => x.removeAccount(TypeMoq.It.isValue(testAccount.key)), TypeMoq.Times.once()); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('RemoveAccount - Declined Success', (done) => { + // Setup: Create an AddAccountAction object + let ams = getMockAccountManagementService(true); + let ms = getMockMessageService(false); + let es = getMockErrorMessageService(); + let action = new RemoveAccountAction(testAccount, ms.object, es.object, ams.object); + + // If: I run the action when it will resolve + action.run() + .then(result => { + try { + // Then: + // ... I should have gotten false back + assert.ok(!result); + + // ... A confirmation dialog should have opened + ms.verify(x => x.confirm(TypeMoq.It.isAny()), TypeMoq.Times.once()); + + // ... The account management service should not have gotten a remove account request + ams.verify(x => x.removeAccount(TypeMoq.It.isAny()), TypeMoq.Times.never()); + + done(); + } catch (e) { + done(e); + } + }); + }); + + test('RemoveAccount - Failure', (done) => { + // Setup: Create an AddAccountAction object + let ams = getMockAccountManagementService(false); + let ms = getMockMessageService(true); + let es = getMockErrorMessageService(); + let action = new RemoveAccountAction(testAccount, ms.object, es.object, ams.object); + + // If: I run the action when it will reject + action.run().then(result => { + // Then: + // ... The result should be false since the operation failed + assert.ok(!result); + // ... The account management service should have gotten a remove account request + ams.verify(x => x.removeAccount(TypeMoq.It.isValue(testAccount.key)), TypeMoq.Times.once()); + done(); + }, error => { + // Should fail as rejected actions cause the debugger to crash + done(error); + }); + }); +}); + +function createAddAccountAction(resolve: boolean, confirm: boolean, param: string): IAddActionMocks { + let ams = getMockAccountManagementService(resolve); + let mockMessageService = getMockMessageService(confirm); + let mockErrorMessageService = getMockErrorMessageService(); + return { + accountMock: ams, + messageMock: mockMessageService, + errorMessageMock: mockErrorMessageService, + action: new AddAccountAction(param, mockMessageService.object, + mockErrorMessageService.object, ams.object) + }; +} + +function getMockAccountManagementService(resolve: boolean): TypeMoq.Mock { + let mockAccountManagementService = TypeMoq.Mock.ofType(AccountManagementTestService); + + mockAccountManagementService.setup(x => x.addAccount(TypeMoq.It.isAnyString())) + .returns(resolve ? () => Promise.resolve(null) : () => Promise.reject(null)); + mockAccountManagementService.setup(x => x.removeAccount(TypeMoq.It.isAny())) + .returns(resolve ? () => Promise.resolve(true) : () => Promise.reject(null).then()); + + return mockAccountManagementService; +} + +function getMockMessageService(confirm: boolean): TypeMoq.Mock { + let mockMessageService = TypeMoq.Mock.ofType(MessageServiceStub); + + mockMessageService.setup(x => x.confirm(TypeMoq.It.isAny())) + .returns(() => confirm); + + return mockMessageService; +} + +function getMockErrorMessageService(): TypeMoq.Mock { + let mockMessageService = TypeMoq.Mock.ofType(ErrorMessageServiceStub); + mockMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())); + return mockMessageService; +} + +interface IAddActionMocks +{ + accountMock: TypeMoq.Mock; + messageMock: TypeMoq.Mock; + errorMessageMock: TypeMoq.Mock; + action: AddAccountAction; +} \ No newline at end of file diff --git a/src/sqltest/parts/accountManagement/accountDialogController.test.ts b/src/sqltest/parts/accountManagement/accountDialogController.test.ts new file mode 100644 index 0000000000..05fb00dc2e --- /dev/null +++ b/src/sqltest/parts/accountManagement/accountDialogController.test.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as TypeMoq from 'typemoq'; +import { Emitter } from 'vs/base/common/event'; +import { AccountDialog } from 'sql/parts/accountManagement/accountDialog/accountDialog'; +import { AccountDialogController } from 'sql/parts/accountManagement/accountDialog/accountDialogController'; +import { AccountViewModel } from 'sql/parts/accountManagement/accountDialog/accountViewModel'; +import { AccountManagementTestService } from 'sqltest/stubs/accountManagementStubs'; +import { ErrorMessageServiceStub } from 'sqltest/stubs/errorMessageServiceStub'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { AccountListRenderer } from 'sql/parts/accountManagement/common/accountListRenderer'; +import { ContextKeyServiceStub } from 'sqltest/stubs/contextKeyServiceStub'; + +// TESTS /////////////////////////////////////////////////////////////////// +suite('Account Management Dialog Controller Tests', () => { + test('Open Account Dialog - Dialog Doesn\'t Exist', () => { + // Setup: Create instance of the controller + let instantiationService = createInstantiationService(); + let controller = new AccountDialogController(instantiationService, undefined); + assert.strictEqual(controller.accountDialog, undefined); + + // If: I open the account dialog when one hasn't been opened + controller.openAccountDialog(); + + // Then: + // ... The account dialog should be defined + assert.notStrictEqual(controller.accountDialog, undefined); + }); + + test('Open Account Dialog - Dialog Exists', () => { + // Setup: Create instance of the controller with an account dialog already loaded + let instantiationService = createInstantiationService(); + let controller = new AccountDialogController(instantiationService, undefined); + controller.openAccountDialog(); + let accountDialog = controller.accountDialog; + + // If: I open the account dialog when one has already been opened + controller.openAccountDialog(); + + // Then: It should be the same dialog that already existed + assert.equal(controller.accountDialog, accountDialog); + }); + + test('Add Account Failure - Error Message Shown', () => { + // Setup: + // ... Create instantiation service that returns mock emitter for account dialog + let mockEventEmitter = new Emitter(); + let instantiationService = createInstantiationService(mockEventEmitter); + + // ... Create a mock instance of the error message service + let errorMessageServiceStub = new ErrorMessageServiceStub(); + let mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub); + mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())); + + // ... Create instance of the controller with an opened dialog + let controller = new AccountDialogController(instantiationService, mockErrorMessageService.object); + controller.openAccountDialog(); + + // If: The account dialog reports a failure adding an account + mockEventEmitter.fire('Error message'); + + // Then: An error dialog should have been opened + mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); + }); +}); + +function createInstantiationService(addAccountFailureEmitter?: Emitter): InstantiationService { + // Create a mock account dialog view model + let accountViewModel = new AccountViewModel(new AccountManagementTestService()); + let mockAccountViewModel = TypeMoq.Mock.ofInstance(accountViewModel); + let mockEvent = new Emitter(); + mockAccountViewModel.setup(x => x.addProviderEvent).returns(() => mockEvent.event); + mockAccountViewModel.setup(x => x.removeProviderEvent).returns(() => mockEvent.event); + mockAccountViewModel.setup(x => x.updateAccountListEvent).returns(() => mockEvent.event); + + // Create a mocked out instantiation service + let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountViewModel))) + .returns(() => mockAccountViewModel.object); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountListRenderer))) + .returns(() => undefined); + + // Create a mock account dialog + let accountDialog = new AccountDialog(null, null, null, instantiationService.object, null, null, null, new ContextKeyServiceStub()); + let mockAccountDialog = TypeMoq.Mock.ofInstance(accountDialog); + mockAccountDialog.setup(x => x.onAddAccountErrorEvent) + .returns(() => { return addAccountFailureEmitter ? addAccountFailureEmitter.event : mockEvent.event; }); + mockAccountDialog.setup(x => x.onCloseEvent) + .returns(() => mockEvent.event); + mockAccountDialog.setup(x => x.render()) + .returns(() => undefined); + mockAccountDialog.setup(x => x.open()) + .returns(() => undefined); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountDialog))) + .returns(() => mockAccountDialog.object); + + return instantiationService.object; +} \ No newline at end of file diff --git a/src/sqltest/parts/accountManagement/accountViewModel.test.ts b/src/sqltest/parts/accountManagement/accountViewModel.test.ts new file mode 100644 index 0000000000..667f830177 --- /dev/null +++ b/src/sqltest/parts/accountManagement/accountViewModel.test.ts @@ -0,0 +1,224 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as data from 'data'; +import * as TypeMoq from 'typemoq'; +import { EventVerifierSingle } from 'sqltest/utils/eventVerifier'; +import { Emitter } from 'vs/base/common/event'; +import { AccountViewModel } from 'sql/parts/accountManagement/accountDialog/accountViewModel'; +import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes'; +import { AccountManagementTestService } from 'sqltest/stubs/accountManagementStubs'; + +// SUITE STATE ///////////////////////////////////////////////////////////// +let mockAddProviderEmitter: Emitter; +let mockRemoveProviderEmitter: Emitter; +let mockUpdateAccountEmitter: Emitter; + +let providers: data.AccountProviderMetadata[]; +let accounts: data.Account[]; + +suite('Account Management Dialog ViewModel Tests', () => { + + suiteSetup(() => { + providers = [{ + id: 'azure', + displayName: 'Azure' + }]; + + let account1 = { + key: { providerId: 'azure', accountId: 'account1' }, + name: 'Account 1', + displayInfo: { + contextualDisplayName: 'Microsoft Account', + contextualLogo: null, + displayName: 'Account 1' + }, + properties: [], + isStale: false + }; + let account2 = { + key: { providerId: 'azure', accountId: 'account2' }, + name: 'Account 2', + displayInfo: { + contextualDisplayName: 'Work/School Account', + contextualLogo: null, + displayName: 'Account 2' + }, + properties: [], + isStale: true + }; + accounts = [account1, account2]; + + // Setup event mocks for the account management service + mockAddProviderEmitter = new Emitter(); + mockRemoveProviderEmitter = new Emitter(); + mockUpdateAccountEmitter = new Emitter(); + }); + + test('Construction - Events are properly defined', () => { + // If: I create an account viewmodel + let mockAccountManagementService = getMockAccountManagementService(false, false); + let vm = new AccountViewModel(mockAccountManagementService.object); + + // Then: + // ... All the events for the view models should be properly initialized + assert.notEqual(vm.addProviderEvent, undefined); + assert.notEqual(vm.removeProviderEvent, undefined); + assert.notEqual(vm.updateAccountListEvent, undefined); + + // ... All the events should properly fire + let argAddProvider: AccountProviderAddedEventParams = { addedProvider: providers[0], initialAccounts: [] }; + let evAddProvider = new EventVerifierSingle(); + vm.addProviderEvent(evAddProvider.eventHandler); + mockAddProviderEmitter.fire(argAddProvider); + evAddProvider.assertFired(argAddProvider); + + let argRemoveProvider = providers[0]; + let evRemoveProvider = new EventVerifierSingle(); + vm.removeProviderEvent(evRemoveProvider.eventHandler); + mockRemoveProviderEmitter.fire(argRemoveProvider); + evRemoveProvider.assertFired(argRemoveProvider); + + let argUpdateAccounts: UpdateAccountListEventParams = { providerId: providers[0].id, accountList: accounts }; + let evUpdateAccounts = new EventVerifierSingle(); + vm.updateAccountListEvent(evUpdateAccounts.eventHandler); + mockUpdateAccountEmitter.fire(argUpdateAccounts); + evUpdateAccounts.assertFired(argUpdateAccounts); + }); + + test('Initialize - Success', done => { + // Setup: Create a viewmodel with event handlers + let mockAccountManagementService = getMockAccountManagementService(true, true); + let evAddProvider = new EventVerifierSingle(); + let evRemoveProvider = new EventVerifierSingle(); + let evUpdateAccounts = new EventVerifierSingle(); + let vm = getViewModel(mockAccountManagementService.object, evAddProvider, evRemoveProvider, evUpdateAccounts); + + // If: I initialize the view model + vm.initialize() + .then(results => { + // Then: + // ... None of the events should have fired + assertNoEventsFired(evAddProvider, evRemoveProvider, evUpdateAccounts); + + // ... The account management service should have been called + mockAccountManagementService.verify(x => x.getAccountProviderMetadata(), TypeMoq.Times.once()); + mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.once()); + + // ... The results that were returned should be an array of account provider added event params + assert.ok(Array.isArray(results)); + assert.equal(results.length, 1); + assert.equal(results[0].addedProvider, providers[0]); + assert.equal(results[0].initialAccounts, accounts); + }).then( + () => done(), + err => done(err) + ); + }); + + test('Initialize - Get providers fails', done => { + // Setup: Create a mock account management service that rejects looking up providers + let mockAccountManagementService = getMockAccountManagementService(false, true); + let evAddProvider = new EventVerifierSingle(); + let evRemoveProvider = new EventVerifierSingle(); + let evUpdateAccounts = new EventVerifierSingle(); + let vm = getViewModel(mockAccountManagementService.object, evAddProvider, evRemoveProvider, evUpdateAccounts); + + // If: I initialize the view model + vm.initialize() + .then(results => { + // Then + // ... None of the events should have fired + assertNoEventsFired(evAddProvider, evRemoveProvider, evUpdateAccounts); + + // ... The account management service should have been called for providers, but not accounts + mockAccountManagementService.verify(x => x.getAccountProviderMetadata(), TypeMoq.Times.once()); + mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.never()); + + // ... The results that were returned should be an empty array + assert.ok(Array.isArray(results)); + assert.equal(results.length, 0); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Initialize - Get accounts fails', done => { + // Setup: Create a mock account management service that rejects the promise + let mockAccountManagementService = getMockAccountManagementService(true, false); + let evAddProvider = new EventVerifierSingle(); + let evRemoveProvider = new EventVerifierSingle(); + let evUpdateAccounts = new EventVerifierSingle(); + let vm = getViewModel(mockAccountManagementService.object, evAddProvider, evRemoveProvider, evUpdateAccounts); + + // If: I initialize the view model + vm.initialize() + .then(result => { + // Then: + // ... None of the events should have fired + assertNoEventsFired(evAddProvider, evRemoveProvider, evUpdateAccounts); + + // ... The account management service should have been called + mockAccountManagementService.verify(x => x.getAccountProviderMetadata(), TypeMoq.Times.once()); + mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.once()); + + // ... The results should include the provider + assert.ok(Array.isArray(result)); + assert.equal(result.length, 1); + assert.equal(result[0].addedProvider, providers[0]); + assert.equal(result[0].initialAccounts, accounts); + }).then( + () => done(), + err => done() + ); + }); +}); + +function getMockAccountManagementService(resolveProviders: boolean, resolveAccounts: boolean): TypeMoq.Mock { + let mockAccountManagementService = TypeMoq.Mock.ofType(AccountManagementTestService); + + mockAccountManagementService.setup(x => x.getAccountProviderMetadata()) + .returns(() => resolveProviders ? Promise.resolve(providers) : Promise.reject(null).then()); + mockAccountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny())) + .returns(() => resolveAccounts ? Promise.resolve(accounts) : Promise.reject(null).then()); + + mockAccountManagementService.setup(x => x.addAccountProviderEvent) + .returns(() => mockAddProviderEmitter.event); + mockAccountManagementService.setup(x => x.removeAccountProviderEvent) + .returns(() => mockRemoveProviderEmitter.event); + mockAccountManagementService.setup(x => x.updateAccountListEvent) + .returns(() => mockUpdateAccountEmitter.event); + + return mockAccountManagementService; +} + +function getViewModel( + ams: AccountManagementTestService, + evAdd: EventVerifierSingle, + evRemove: EventVerifierSingle, + evUpdate: EventVerifierSingle +): AccountViewModel { + let vm = new AccountViewModel(ams); + vm.addProviderEvent(evAdd.eventHandler); + vm.removeProviderEvent(evRemove.eventHandler); + vm.updateAccountListEvent(evUpdate.eventHandler); + + return vm; +} + +function assertNoEventsFired( + evAdd: EventVerifierSingle, + evRemove: EventVerifierSingle, + evUpdate: EventVerifierSingle +): void { + evAdd.assertNotFired(); + evRemove.assertNotFired(); + evUpdate.assertNotFired(); +} \ No newline at end of file diff --git a/src/sqltest/parts/admin/adminService.test.ts b/src/sqltest/parts/admin/adminService.test.ts new file mode 100644 index 0000000000..9145b456e7 --- /dev/null +++ b/src/sqltest/parts/admin/adminService.test.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import data = require('data'); +import * as TypeMoq from 'typemoq'; + +import { AdminService } from 'sql/parts/admin/common/adminService'; + +suite('SQL AdminService tests', () => { + + let adminService: AdminService; + setup(() => { + adminService = new AdminService( + undefined, undefined, undefined, undefined + ); + }); + + test('createDatabase should call tools service provider', done => { + done(); + // adminService.createDatabase(undefined, undefined).then((result) => { + // assert.notEqual(result, undefined, 'Result is undefined'); + // done(); + // }); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/common/optionsDialogHelper.test.ts b/src/sqltest/parts/common/optionsDialogHelper.test.ts new file mode 100644 index 0000000000..e5c42c181f --- /dev/null +++ b/src/sqltest/parts/common/optionsDialogHelper.test.ts @@ -0,0 +1,415 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as OptionsDialogHelper from 'sql/base/browser/ui/modal/optionsDialogHelper'; +import { ServiceOptionType } from 'sql/parts/connection/common/connectionManagement'; +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import data = require('data'); +import { Builder, $ } from 'vs/base/browser/builder'; +import * as TypeMoq from 'typemoq'; +import * as assert from 'assert'; + +suite('Advanced options helper tests', () => { + var possibleInputs: string[]; + let options: { [name: string]: any }; + var categoryOption: data.ServiceOption; + var booleanOption: data.ServiceOption; + var numberOption: data.ServiceOption; + var stringOption: data.ServiceOption; + var defaultGroupOption: data.ServiceOption; + var isValid: boolean; + var inputValue: string; + var inputBox: TypeMoq.Mock; + + var optionsMap: { [optionName: string]: OptionsDialogHelper.IOptionElement }; + + setup(() => { + options = {}; + optionsMap = {}; + + categoryOption = { + name: 'applicationIntent', + displayName: 'Application Intent', + description: 'Declares the application workload type when connecting to a server', + groupName: 'Initialization', + categoryValues: [ + { displayName: 'ReadWrite', name: 'ReadWrite' }, + { displayName: 'ReadOnly', name: 'ReadOnly' } + ], + defaultValue: null, + isRequired: false, + valueType: ServiceOptionType.category, + objectType: undefined, + isArray: undefined + }; + + booleanOption = { + name: 'asynchronousProcessing', + displayName: 'Asynchronous processing enabled', + description: 'When true, enables usage of the Asynchronous functionality in the .Net Framework Data Provider', + groupName: 'Initialization', + categoryValues: null, + defaultValue: null, + isRequired: false, + valueType: ServiceOptionType.boolean, + objectType: undefined, + isArray: undefined + }; + + numberOption = { + name: 'connectTimeout', + displayName: 'Connect Timeout', + description: 'The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error', + groupName: 'Initialization', + categoryValues: null, + defaultValue: '15', + isRequired: false, + valueType: ServiceOptionType.number, + objectType: undefined, + isArray: undefined + }; + + stringOption = { + name: 'currentLanguage', + displayName: 'Current Language', + description: 'The SQL Server language record name', + groupName: 'Initialization', + categoryValues: null, + defaultValue: null, + isRequired: false, + valueType: ServiceOptionType.string, + objectType: undefined, + isArray: undefined + }; + + defaultGroupOption = { + name: 'defaultGroupOption', + displayName: 'Default Group', + description: 'Test string option', + groupName: undefined, + categoryValues: null, + defaultValue: null, + isRequired: false, + valueType: ServiceOptionType.string, + objectType: undefined, + isArray: undefined + }; + + + let builder: Builder = $().div(); + inputBox = TypeMoq.Mock.ofType(InputBox, TypeMoq.MockBehavior.Loose, builder.getHTMLElement(), null, null); + inputBox.callBase = true; + inputBox.setup(x => x.validate()).returns(() => isValid); + inputBox.setup(x => x.value).returns(() => inputValue); + }); + + test('create default but not required category options should set the option value and possible inputs correctly', () => { + categoryOption.defaultValue = 'ReadWrite'; + categoryOption.isRequired = false; + possibleInputs = []; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(categoryOption, options, possibleInputs); + assert.equal(optionValue, 'ReadWrite'); + assert.equal(possibleInputs.length, 3); + assert.equal(possibleInputs[0], ''); + assert.equal(possibleInputs[1], 'ReadWrite'); + assert.equal(possibleInputs[2], 'ReadOnly'); + }); + + test('create default and required category options should set the option value and possible inputs correctly', () => { + categoryOption.defaultValue = 'ReadWrite'; + categoryOption.isRequired = true; + possibleInputs = []; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(categoryOption, options, possibleInputs); + assert.equal(optionValue, 'ReadWrite'); + assert.equal(possibleInputs.length, 2); + assert.equal(possibleInputs[0], 'ReadWrite'); + assert.equal(possibleInputs[1], 'ReadOnly'); + }); + + test('create no default and not required category options should set the option value and possible inputs correctly', () => { + categoryOption.defaultValue = null; + categoryOption.isRequired = false; + possibleInputs = []; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(categoryOption, options, possibleInputs); + assert.equal(optionValue, ''); + assert.equal(possibleInputs.length, 3); + assert.equal(possibleInputs[0], ''); + assert.equal(possibleInputs[1], 'ReadWrite'); + assert.equal(possibleInputs[2], 'ReadOnly'); + }); + + test('create no default but required category options should set the option value and possible inputs correctly', () => { + categoryOption.defaultValue = null; + categoryOption.isRequired = true; + possibleInputs = []; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(categoryOption, options, possibleInputs); + assert.equal(optionValue, 'ReadWrite'); + assert.equal(possibleInputs.length, 2); + assert.equal(possibleInputs[0], 'ReadWrite'); + assert.equal(possibleInputs[1], 'ReadOnly'); + }); + + test('create not required category options with option value should set the option value and possible inputs correctly', () => { + categoryOption.defaultValue = null; + categoryOption.isRequired = false; + possibleInputs = []; + options['applicationIntent'] = 'ReadOnly'; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(categoryOption, options, possibleInputs); + assert.equal(optionValue, 'ReadOnly'); + assert.equal(possibleInputs.length, 3); + assert.equal(possibleInputs[0], ''); + assert.equal(possibleInputs[1], 'ReadWrite'); + assert.equal(possibleInputs[2], 'ReadOnly'); + }); + + test('create required category options with option value should set the option value and possible inputs correctly', () => { + categoryOption.defaultValue = null; + categoryOption.isRequired = true; + possibleInputs = []; + options['applicationIntent'] = 'ReadOnly'; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(categoryOption, options, possibleInputs); + assert.equal(optionValue, 'ReadOnly'); + assert.equal(possibleInputs.length, 2); + assert.equal(possibleInputs[0], 'ReadWrite'); + assert.equal(possibleInputs[1], 'ReadOnly'); + }); + + test('create default but not required boolean options should set the option value and possible inputs correctly', () => { + booleanOption.defaultValue = 'False'; + booleanOption.isRequired = false; + possibleInputs = []; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(booleanOption, options, possibleInputs); + assert.equal(optionValue, 'False'); + assert.equal(possibleInputs.length, 3); + assert.equal(possibleInputs[0], ''); + assert.equal(possibleInputs[1], 'True'); + assert.equal(possibleInputs[2], 'False'); + }); + + test('create default and required boolean options should set the option value and possible inputs correctly', () => { + booleanOption.defaultValue = 'False'; + booleanOption.isRequired = true; + possibleInputs = []; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(booleanOption, options, possibleInputs); + assert.equal(optionValue, 'False'); + assert.equal(possibleInputs.length, 2); + assert.equal(possibleInputs[0], 'True'); + assert.equal(possibleInputs[1], 'False'); + }); + + test('create no default and not required boolean options should set the option value and possible inputs correctly', () => { + booleanOption.defaultValue = null; + booleanOption.isRequired = false; + possibleInputs = []; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(booleanOption, options, possibleInputs); + assert.equal(optionValue, ''); + assert.equal(possibleInputs.length, 3); + assert.equal(possibleInputs[0], ''); + assert.equal(possibleInputs[1], 'True'); + assert.equal(possibleInputs[2], 'False'); + }); + + test('create no default but required boolean options should set the option value and possible inputs correctly', () => { + booleanOption.defaultValue = null; + booleanOption.isRequired = true; + possibleInputs = []; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(booleanOption, options, possibleInputs); + assert.equal(optionValue, 'True'); + assert.equal(possibleInputs.length, 2); + assert.equal(possibleInputs[0], 'True'); + assert.equal(possibleInputs[1], 'False'); + }); + + test('create not required boolean options with option value should set the option value and possible inputs correctly', () => { + booleanOption.defaultValue = null; + booleanOption.isRequired = false; + possibleInputs = []; + options['asynchronousProcessing'] = true; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(booleanOption, options, possibleInputs); + assert.equal(optionValue, 'True'); + assert.equal(possibleInputs.length, 3); + assert.equal(possibleInputs[0], ''); + assert.equal(possibleInputs[1], 'True'); + assert.equal(possibleInputs[2], 'False'); + }); + + test('create required boolean options with option value should set the option value and possible inputs correctly', () => { + booleanOption.defaultValue = null; + booleanOption.isRequired = true; + possibleInputs = []; + options['asynchronousProcessing'] = 'False'; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(booleanOption, options, possibleInputs); + assert.equal(optionValue, 'False'); + assert.equal(possibleInputs.length, 2); + assert.equal(possibleInputs[0], 'True'); + assert.equal(possibleInputs[1], 'False'); + }); + + test('create default number options should set the option value and possible inputs correctly', () => { + numberOption.defaultValue = '15'; + numberOption.isRequired = true; + possibleInputs = []; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(numberOption, options, possibleInputs); + assert.equal(optionValue, '15'); + }); + + test('create number options with option value should set the option value and possible inputs correctly', () => { + numberOption.defaultValue = '15'; + numberOption.isRequired = false; + possibleInputs = []; + options['connectTimeout'] = '45'; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(numberOption, options, possibleInputs); + assert.equal(optionValue, '45'); + }); + + test('create default string options should set the option value and possible inputs correctly', () => { + stringOption.defaultValue = 'Japanese'; + stringOption.isRequired = true; + possibleInputs = []; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(stringOption, options, possibleInputs); + assert.equal(optionValue, 'Japanese'); + }); + + test('create string options with option value should set the option value and possible inputs correctly', () => { + stringOption.defaultValue = 'Japanese'; + stringOption.isRequired = false; + possibleInputs = []; + options['currentLanguage'] = 'Spanish'; + var optionValue = OptionsDialogHelper.getOptionValueAndCategoryValues(stringOption, options, possibleInputs); + assert.equal(optionValue, 'Spanish'); + }); + + test('validate undefined and optional number input should return no error', () => { + isValid = true; + inputValue = ''; + numberOption.isRequired = false; + optionsMap = {}; + optionsMap['connectTimeout'] = { + optionWidget: inputBox.object, + option: numberOption, + optionValue: null + }; + + var error = OptionsDialogHelper.validateInputs(optionsMap); + assert.equal(error, true); + }); + + test('validate a valid optional number input should return no error', () => { + isValid = true; + inputValue = '30'; + numberOption.isRequired = false; + optionsMap = {}; + optionsMap['connectTimeout'] = { + optionWidget: inputBox.object, + option: numberOption, + optionValue: null + }; + + var error = OptionsDialogHelper.validateInputs(optionsMap); + assert.equal(error, true); + }); + + test('validate a valid required number input should return no error', () => { + isValid = true; + inputValue = '30'; + numberOption.isRequired = true; + optionsMap = {}; + optionsMap['connectTimeout'] = { + optionWidget: inputBox.object, + option: numberOption, + optionValue: null + }; + var error = OptionsDialogHelper.validateInputs(optionsMap); + assert.equal(error, true); + }); + + test('validate invalid optional number option should return an expected error', () => { + isValid = false; + inputValue = 'abc'; + numberOption.isRequired = false; + optionsMap = {}; + optionsMap['connectTimeout'] = { + optionWidget: inputBox.object, + option: numberOption, + optionValue: null + }; + + var error = OptionsDialogHelper.validateInputs(optionsMap); + assert.equal(error, false); + }); + + test('validate required optional number option should return an expected error', () => { + isValid = false; + inputValue = ''; + numberOption.isRequired = true; + optionsMap = {}; + optionsMap['connectTimeout'] = { + optionWidget: inputBox.object, + option: numberOption, + optionValue: null + }; + + var error = OptionsDialogHelper.validateInputs(optionsMap); + assert.equal(error, false); + }); + + test('update options should delete option entry if the input value is an empty string', () => { + isValid = true; + inputValue = ''; + numberOption.isRequired = false; + optionsMap = {}; + optionsMap['connectTimeout'] = { + optionWidget: inputBox.object, + option: numberOption, + optionValue: '45' + }; + options['connectTimeout'] = '45'; + OptionsDialogHelper.updateOptions(options, optionsMap); + assert.equal(options['connectTimeout'], undefined); + }); + + test('update options should update correct option value', () => { + isValid = true; + inputValue = '50'; + numberOption.isRequired = false; + optionsMap = {}; + optionsMap['connectTimeout'] = { + optionWidget: inputBox.object, + option: numberOption, + optionValue: '45' + }; + options['connectTimeout'] = '45'; + OptionsDialogHelper.updateOptions(options, optionsMap); + assert.equal(options['connectTimeout'], 50); + }); + + test('update options should add the option value to options', () => { + isValid = true; + inputValue = '50'; + numberOption.isRequired = false; + optionsMap = {}; + optionsMap['connectTimeout'] = { + optionWidget: inputBox.object, + option: numberOption, + optionValue: '45' + }; + options = {}; + OptionsDialogHelper.updateOptions(options, optionsMap); + assert.equal(options['connectTimeout'], 50); + }); + + test('groupOptionsByCategory converts a list of options to a map of category names to lists of options', () => { + let optionsList = [categoryOption, booleanOption, numberOption, stringOption, defaultGroupOption]; + let optionsMap = OptionsDialogHelper.groupOptionsByCategory(optionsList); + let categoryNames = Object.keys(optionsMap); + assert.equal(categoryNames.includes('Initialization'), true); + assert.equal(categoryNames.includes('General'), true); + assert.equal(categoryNames.length, 2); + assert.equal(optionsMap['Initialization'].length, 4); + assert.equal(optionsMap['General'].length, 1); + }); + +}); \ No newline at end of file diff --git a/src/sqltest/parts/connection/advancedPropertiesDialog.test.ts b/src/sqltest/parts/connection/advancedPropertiesDialog.test.ts new file mode 100644 index 0000000000..0024e32497 --- /dev/null +++ b/src/sqltest/parts/connection/advancedPropertiesDialog.test.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { OptionsDialog } from 'sql/base/browser/ui/modal/optionsDialog'; +import { AdvancedPropertiesController } from 'sql/parts/connection/connectionDialog/advancedPropertiesController'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { ContextKeyServiceStub } from 'sqltest/stubs/contextKeyServiceStub'; +import * as data from 'data'; +import * as TypeMoq from 'typemoq'; +import * as assert from 'assert'; + +suite('Advanced properties dialog tests', () => { + var advancedController: AdvancedPropertiesController; + var providerOptions: data.ConnectionOption[]; + + setup(() => { + advancedController = new AdvancedPropertiesController(() => { }, null); + providerOptions = [ + { + name: 'a1', + displayName: undefined, + description: undefined, + groupName: 'a', + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: null, + valueType: 0 + }, + { + name: 'b1', + displayName: undefined, + description: undefined, + groupName: 'b', + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: null, + valueType: 0 + }, + { + name: 'noType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: null, + valueType: 0 + }, + { + name: 'a2', + displayName: undefined, + description: undefined, + groupName: 'a', + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: null, + valueType: 0 + }, + { + name: 'b2', + displayName: undefined, + description: undefined, + groupName: 'b', + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: null, + valueType: 0 + } + ]; + }); + + test('advanced dialog should open when showDialog in advancedController get called', () => { + var isAdvancedDialogCalled = false; + let options: { [name: string]: any } = {}; + let builder: Builder = $().div(); + let advanceDialog = TypeMoq.Mock.ofType(OptionsDialog, TypeMoq.MockBehavior.Strict, + '', // title + '', // name + {}, // options + undefined, // partsService + undefined, // themeService + undefined, // Context view service + undefined, // telemetry service + new ContextKeyServiceStub() // contextkeyservice + ); + advanceDialog.setup(x => x.open(TypeMoq.It.isAny(), TypeMoq.It.isAny())).callback(() => { + isAdvancedDialogCalled = true; + }); + advancedController.advancedDialog = advanceDialog.object; + advancedController.showDialog(providerOptions, builder.getHTMLElement(), options); + assert.equal(isAdvancedDialogCalled, true); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/connection/connectionConfig.test.ts b/src/sqltest/parts/connection/connectionConfig.test.ts new file mode 100644 index 0000000000..69254fd680 --- /dev/null +++ b/src/sqltest/parts/connection/connectionConfig.test.ts @@ -0,0 +1,931 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + + +import * as TypeMoq from 'typemoq'; +import { ConnectionConfig, ISaveGroupResult } from 'sql/parts/connection/common/connectionConfig'; +import { IConnectionProfile, IConnectionProfileStore } from 'sql/parts/connection/common/interfaces'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { ConfigurationTarget, IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationValue as TConfigurationValue } from 'vs/platform/configuration/common/configuration'; +import { WorkspaceConfigurationTestService } from 'sqltest/stubs/workspaceConfigurationTestService'; +import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; +import * as Constants from 'sql/parts/connection/common/constants'; +import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as assert from 'assert'; +import { CapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import data = require('data'); +import { Emitter } from 'vs/base/common/event'; + +suite('SQL ConnectionConfig tests', () => { + let capabilitiesService: TypeMoq.Mock; + let workspaceConfigurationServiceMock: TypeMoq.Mock; + let configEditingServiceMock: TypeMoq.Mock; + let msSQLCapabilities: data.DataProtocolServerCapabilities; + let capabilities: data.DataProtocolServerCapabilities[]; + let onProviderRegistered = new Emitter(); + + let configValueToConcat: TConfigurationValue = { + workspace: [{ + name: 'g1', + id: 'g1', + parentId: 'ROOT', + color: 'pink', + description: 'g1' + }, + { + name: 'g1-1', + id: 'g1-1', + parentId: 'g1', + color: 'blue', + description: 'g1-1' + } + ], + user: [{ + name: 'ROOT', + id: 'ROOT', + parentId: '', + color: 'white', + description: 'ROOT' + }, { + name: 'g2', + id: 'g2', + parentId: 'ROOT', + color: 'green', + description: 'g2' + }, + { + name: 'g2-1', + id: 'g2-1', + parentId: 'g2', + color: 'yellow', + description: 'g2' + }, + { + name: 'g3', + id: 'g3', + parentId: '', + color: 'orange', + description: 'g3' + }, + { + name: 'g3-1', + id: 'g3-1', + parentId: 'g3', + color: 'purple', + description: 'g3-1' + } + ], + value: [], + default: [], + folder: [] + }; + + let configValueToMerge: TConfigurationValue = { + workspace: [ + { + name: 'g1', + id: 'g1', + parentId: '', + color: 'pink', + description: 'g1' + }, + { + name: 'g1-1', + id: 'g1-1', + parentId: 'g1', + color: 'blue', + description: 'g1-1' + } + ], + user: [ + { + name: 'g2', + id: 'g2', + parentId: '', + color: 'green', + description: 'g2' + }, + { + name: 'g2-1', + id: 'g2-1', + parentId: 'g2', + color: 'yellow', + description: 'g2' + }, + { + name: 'g1', + id: 'g1', + parentId: '', + color: 'pink', + description: 'g1' + }, + { + name: 'g1-2', + id: 'g1-2', + parentId: 'g1', + color: 'silver', + description: 'g1-2' + }], + value: [], + default: [], + folder: [] + }; + + let connections: TConfigurationValue = { + workspace: [{ + options: { + serverName: 'server1', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '' + }, + providerName: 'MSSQL', + groupId: 'test', + savePassword: true, + id: 'server1' + } + + + ], + user: [{ + options: { + serverName: 'server2', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '' + }, + providerName: 'MSSQL', + groupId: 'test', + savePassword: true, + id: 'server2' + }, { + options: { + serverName: 'server3', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '' + }, + providerName: 'MSSQL', + groupId: 'g3', + savePassword: true, + id: 'server3' + } + ], + value: [], + default: [], + folder: [] + }; + setup(() => { + capabilitiesService = TypeMoq.Mock.ofType(CapabilitiesService); + capabilities = []; + let connectionProvider: data.ConnectionProviderOptions = { + options: [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 0, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 1, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 3, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 2, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 4, + valueType: 0 + } + ] + }; + msSQLCapabilities = { + protocolVersion: '1', + providerName: 'MSSQL', + providerDisplayName: 'MSSQL', + connectionProvider: connectionProvider, + adminServicesProvider: undefined, + features: undefined + }; + capabilities.push(msSQLCapabilities); + + capabilitiesService.setup(x => x.getCapabilities()).returns(() => capabilities); + capabilitiesService.setup(x => x.onProviderRegisteredEvent).returns(() => onProviderRegistered.event); + + workspaceConfigurationServiceMock = TypeMoq.Mock.ofType(WorkspaceConfigurationTestService); + workspaceConfigurationServiceMock.setup(x => x.reloadConfiguration()) + .returns(() => TPromise.as<{}>({})); + + configEditingServiceMock = TypeMoq.Mock.ofType(ConfigurationEditingService); + let nothing: void; + configEditingServiceMock.setup(x => x.writeConfiguration(ConfigurationTarget.USER, TypeMoq.It.isAny())).returns(() => TPromise.as(nothing)); + configEditingServiceMock.setup(x => x.writeConfiguration(ConfigurationTarget.WORKSPACE, TypeMoq.It.isAny())).returns(() => TPromise.as(nothing)); + }); + + function groupsAreEqual(groups1: IConnectionProfileGroup[], groups2: IConnectionProfileGroup[]): Boolean { + if (!groups1 && !groups2) { + return true; + } else if ((!groups1 && groups2 && groups2.length === 0) || (!groups2 && groups1 && groups1.length === 0)) { + return true; + } + + if (groups1.length !== groups2.length) { + return false; + } + + let areEqual = true; + + groups1.map(g1 => { + if (areEqual) { + let g2 = groups2.find(g => g.name === g1.name); + if (!g2) { + areEqual = false; + } else { + let result = groupsAreEqual(groups1.filter(a => a.parentId === g1.id), groups2.filter(b => b.parentId === g2.id)); + if (result === false) { + areEqual = false; + } + } + } + }); + + return areEqual; + } + + test('allGroups should return groups from user and workspace settings', () => { + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionGroupsArrayName)) + .returns(() => configValueToConcat); + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + let allGroups = config.getAllGroups(); + + + assert.notEqual(allGroups, undefined); + assert.equal(allGroups.length, configValueToConcat.workspace.length + configValueToConcat.user.length); + }); + + test('allGroups should merge groups from user and workspace settings', () => { + let expectedAllGroups: IConnectionProfileGroup[] = [ + { + name: 'g1', + id: 'g1', + parentId: '', + color: 'pink', + description: 'g1' + }, + { + name: 'g1-1', + id: 'g1-1', + parentId: 'g1', + color: 'blue', + description: 'g1-1' + }, + { + name: 'g2', + id: 'g2', + parentId: '', + color: 'yellow', + description: 'g2' + }, + { + name: 'g2-1', + id: 'g2-1', + parentId: 'g2', + color: 'red', + description: 'g2' + }, + { + name: 'g1-2', + id: 'g1-2', + parentId: 'g1', + color: 'green', + description: 'g1-2' + }]; + + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionGroupsArrayName)) + .returns(() => configValueToMerge); + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + let allGroups = config.getAllGroups(); + + + assert.notEqual(allGroups, undefined); + assert.equal(groupsAreEqual(allGroups, expectedAllGroups), true); + }); + + test('addConnection should add the new profile to user settings if does not exist', done => { + let newProfile: IConnectionProfile = { + serverName: 'new server', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: undefined, + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }; + + let expectedNumberOfConnections = connections.user.length + 1; + + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => connections); + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionGroupsArrayName)) + .returns(() => configValueToConcat); + + let connectionProfile = new ConnectionProfile(msSQLCapabilities, newProfile); + connectionProfile.options['databaseDisplayName'] = 'database'; + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + config.addConnection(connectionProfile).then(savedConnectionProfile => { + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileStore[]).length === expectedNumberOfConnections)), TypeMoq.Times.once()); + assert.notEqual(savedConnectionProfile.id, undefined); + done(); + }).catch(error => { + assert.fail(); + done(); + }); + }); + + test('addConnection should not add the new profile to user settings if already exists', done => { + let profileFromConfig = connections.user[0]; + let newProfile: IConnectionProfile = { + serverName: profileFromConfig.options['serverName'], + databaseName: profileFromConfig.options['databaseName'], + userName: profileFromConfig.options['userName'], + password: profileFromConfig.options['password'], + authenticationType: profileFromConfig.options['authenticationType'], + groupId: profileFromConfig.groupId, + savePassword: true, + groupFullName: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }; + + let expectedNumberOfConnections = connections.user.length; + + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => connections); + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionGroupsArrayName)) + .returns(() => configValueToConcat); + + let connectionProfile = new ConnectionProfile(msSQLCapabilities, newProfile); + connectionProfile.options['databaseDisplayName'] = profileFromConfig.options['databaseName']; + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + config.addConnection(connectionProfile).then(savedConnectionProfile => { + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileStore[]).length === expectedNumberOfConnections)), TypeMoq.Times.once()); + assert.equal(savedConnectionProfile.id, profileFromConfig.id); + done(); + }).catch(error => { + assert.fail(); + done(); + }); + }); + + test('addConnection should add the new group to user settings if does not exist', done => { + let newProfile: IConnectionProfile = { + serverName: 'new server', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: 'g2/g2-2', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }; + + let expectedNumberOfConnections = connections.user.length + 1; + let expectedNumberOfGroups = configValueToConcat.user.length + 1; + + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => connections); + + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionGroupsArrayName)) + .returns(() => configValueToConcat); + + let connectionProfile = new ConnectionProfile(msSQLCapabilities, newProfile); + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + config.addConnection(connectionProfile).then(success => { + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileStore[]).length === expectedNumberOfConnections)), TypeMoq.Times.once()); + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileGroup[]).length === expectedNumberOfGroups)), TypeMoq.Times.once()); + done(); + }).catch(error => { + assert.fail(); + done(); + }); + }); + + test('getConnections should return connections from user and workspace settings given getWorkspaceConnections set to true', () => { + let getWorkspaceConnections: boolean = true; + + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => connections); + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + let allConnections = config.getConnections(getWorkspaceConnections); + assert.equal(allConnections.length, connections.user.length + connections.workspace.length); + }); + + test('getConnections should return connections from user settings given getWorkspaceConnections set to false', () => { + let getWorkspaceConnections: boolean = false; + + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => connections); + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + let allConnections = config.getConnections(getWorkspaceConnections); + assert.equal(allConnections.length, connections.user.length); + }); + + test('getConnections should return connections with a valid id', () => { + let getWorkspaceConnections: boolean = false; + let connectionsWithNoId: TConfigurationValue = { + user: connections.user.map(c => { + c.id = undefined; + return c; + }), + default: connections.default, + workspace: connections.workspace.map(c => { + c.id = c.options['serverName']; + return c; + }), + value: connections.value, + folder: [] + }; + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => connectionsWithNoId); + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + let allConnections = config.getConnections(getWorkspaceConnections); + assert.equal(allConnections.length, connections.user.length); + allConnections.forEach(connection => { + let userConnection = connectionsWithNoId.user.find(u => u.options['serverName'] === connection.serverName); + if (userConnection !== undefined) { + assert.notEqual(connection.id, connection.getOptionsKey()); + assert.notEqual(connection.id, undefined); + } else { + let workspaceConnection = connectionsWithNoId.workspace.find(u => u.options['serverName'] === connection.serverName); + assert.notEqual(connection.id, connection.getOptionsKey()); + assert.equal(workspaceConnection.id, connection.id); + } + }); + }); + + test('getConnections update the capabilities in each profile when the provider capabilities is registered', () => { + let oldOptionName: string = 'oldOptionName'; + let optionsMetadataFromConfig = capabilities[0].connectionProvider.options.concat({ + name: oldOptionName, + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 0, + valueType: 0 + }); + + let capabilitiesFromConfig: data.DataProtocolServerCapabilities[] = []; + let connectionProvider: data.ConnectionProviderOptions = { + options: optionsMetadataFromConfig + }; + let msSQLCapabilities2 = { + protocolVersion: '1', + providerName: 'MSSQL', + providerDisplayName: 'MSSQL', + connectionProvider: connectionProvider, + adminServicesProvider: undefined, + features: undefined + }; + capabilitiesFromConfig.push(msSQLCapabilities2); + let connectionUsingOldMetadata = connections.user.map(c => { + c.options[oldOptionName] = 'oldOptionValue'; + return c; + }); + let configValue = Object.assign({}, connections, { user: connectionUsingOldMetadata }); + let capabilitiesService2: TypeMoq.Mock = TypeMoq.Mock.ofType(CapabilitiesService); + capabilitiesService2.setup(x => x.getCapabilities()).returns(() => []); + capabilitiesService2.setup(x => x.onProviderRegisteredEvent).returns(() => onProviderRegistered.event); + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => configValue); + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService2.object, capabilitiesFromConfig); + let allConnections = config.getConnections(false); + allConnections.forEach(element => { + assert.notEqual(element.serverName, undefined); + assert.notEqual(element.getOptionsKey().indexOf('oldOptionValue|'), -1); + }); + + onProviderRegistered.fire(msSQLCapabilities); + allConnections.forEach(element => { + assert.notEqual(element.serverName, undefined); + assert.equal(element.getOptionsKey().indexOf('oldOptionValue|'), -1); + }); + }); + + test('saveGroup should save the new groups to tree and return the id of the last group name', () => { + let config = new ConnectionConfig(undefined, undefined, undefined, undefined); + let groups: IConnectionProfileGroup[] = configValueToConcat.user; + let expectedLength = configValueToConcat.user.length + 2; + let newGroups: string = 'ROOT/g1/g1-1'; + let color: string = 'red'; + + let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups); + assert.notEqual(result, undefined); + assert.equal(result.groups.length, expectedLength, 'The result groups length is invalid'); + let newGroup = result.groups.find(g => g.name === 'g1-1'); + assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid'); + }); + + test('saveGroup should only add the groups that are not in the tree', () => { + let config = new ConnectionConfig(undefined, undefined, undefined, undefined); + let groups: IConnectionProfileGroup[] = configValueToConcat.user; + let expectedLength = configValueToConcat.user.length + 1; + let newGroups: string = 'ROOT/g2/g2-5'; + let color: string = 'red'; + + let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups); + assert.notEqual(result, undefined); + assert.equal(result.groups.length, expectedLength, 'The result groups length is invalid'); + let newGroup = result.groups.find(g => g.name === 'g2-5'); + assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid'); + }); + + test('saveGroup should not add any new group if tree already has all the groups in the full path', () => { + let config = new ConnectionConfig(undefined, undefined, undefined, undefined); + let groups: IConnectionProfileGroup[] = configValueToConcat.user; + let expectedLength = configValueToConcat.user.length; + let newGroups: string = 'ROOT/g2/g2-1'; + let color: string = 'red'; + + let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups); + assert.notEqual(result, undefined); + assert.equal(result.groups.length, expectedLength, 'The result groups length is invalid'); + let newGroup = result.groups.find(g => g.name === 'g2-1'); + assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid'); + }); + + test('deleteConnection should remove the connection from config', done => { + let newProfile: IConnectionProfile = { + serverName: 'server3', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: 'g3', + groupId: 'g3', + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }; + + let expectedNumberOfConnections = connections.user.length - 1; + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => connections); + + let connectionProfile = new ConnectionProfile(msSQLCapabilities, newProfile); + connectionProfile.options['databaseDisplayName'] = 'database'; + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + config.deleteConnection(connectionProfile).then(() => { + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileStore[]).length === expectedNumberOfConnections)), TypeMoq.Times.once()); + done(); + }).catch(error => { + assert.fail(); + done(); + }); + }); + + test('deleteConnectionGroup should remove the children connections and subgroups from config', done => { + let newProfile: IConnectionProfile = { + serverName: 'server3', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: 'g3', + groupId: 'g3', + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }; + let connectionProfile = new ConnectionProfile(msSQLCapabilities, newProfile); + connectionProfile.options['databaseDisplayName'] = 'database'; + + let connectionProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined); + let childGroup = new ConnectionProfileGroup('g3-1', connectionProfileGroup, 'g3-1', undefined, undefined); + connectionProfileGroup.addGroups([childGroup]); + connectionProfileGroup.addConnections([connectionProfile]); + + let expectedNumberOfConnections = connections.user.length - 1; + let expectedNumberOfGroups = configValueToConcat.user.length - 2; + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => connections); + + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionGroupsArrayName)) + .returns(() => configValueToConcat); + + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + config.deleteGroup(connectionProfileGroup).then(() => { + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileStore[]).length === expectedNumberOfConnections)), TypeMoq.Times.once()); + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileGroup[]).length === expectedNumberOfGroups)), TypeMoq.Times.once()); + done(); + }).catch(error => { + assert.fail(); + done(); + }); + }); + + test('deleteConnection should not throw error for connection not in config', done => { + let newProfile: IConnectionProfile = { + serverName: 'server3', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: 'g3', + groupId: 'newid', + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }; + + let expectedNumberOfConnections = connections.user.length; + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => connections); + + let connectionProfile = new ConnectionProfile(msSQLCapabilities, newProfile); + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + config.deleteConnection(connectionProfile).then(() => { + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileStore[]).length === expectedNumberOfConnections)), TypeMoq.Times.once()); + done(); + }).catch(error => { + assert.fail(); + done(); + }); + }); + + test('renameGroup should change group name', done => { + + let expectedNumberOfConnections = configValueToConcat.user.length; + let calledValue: any; + let called: boolean = false; + let nothing: void; + let configEditingServiceMock: TypeMoq.Mock = TypeMoq.Mock.ofType(ConfigurationEditingService); + configEditingServiceMock.setup(x => x.writeConfiguration(ConfigurationTarget.USER, TypeMoq.It.isAny())).callback((x: any, val: any) => { + calledValue = val.value as IConnectionProfileStore[]; + }).returns(() => TPromise.as(nothing)); + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionGroupsArrayName)) + .returns(() => configValueToConcat); + + let connectionProfileGroup = new ConnectionProfileGroup('g-renamed', undefined, 'g2', undefined, undefined); + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + config.editGroup(connectionProfileGroup).then(() => { + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileStore[]).length === expectedNumberOfConnections)), TypeMoq.Times.once()); + calledValue.forEach(con => { + if (con.id === 'g2') { + assert.equal(con.name, 'g-renamed', 'Group was not renamed'); + called = true; + } + }); + assert.equal(called, true, 'group was not renamed'); + }).then(() => done(), (err) => done(err)); + }); + + + test('change group(parent) for connection group', done => { + let expectedNumberOfConnections = configValueToConcat.user.length; + let calledValue: any; + let nothing: void; + let configEditingServiceMock: TypeMoq.Mock = TypeMoq.Mock.ofType(ConfigurationEditingService); + configEditingServiceMock.setup(x => x.writeConfiguration(ConfigurationTarget.USER, TypeMoq.It.isAny())).callback((x: any, val: any) => { + calledValue = val.value as IConnectionProfileStore[]; + }).returns(() => TPromise.as(nothing)); + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionGroupsArrayName)) + .returns(() => configValueToConcat); + + let sourceProfileGroup = new ConnectionProfileGroup('g2', undefined, 'g2', undefined, undefined); + let targetProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined); + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + config.changeGroupIdForConnectionGroup(sourceProfileGroup, targetProfileGroup).then(() => { + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileStore[]).length === expectedNumberOfConnections)), TypeMoq.Times.once()); + calledValue.forEach(con => { + if (con.id === 'g2') { + assert.equal(con.parentId, 'g3', 'Group parent was not changed'); + } + }); + }).then(() => done(), (err) => done(err)); + }); + + + test('change group(parent) for connection', done => { + let newProfile: IConnectionProfile = { + serverName: 'server3', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: 'g3', + groupId: 'g3', + getOptionsKey: () => { return 'connectionId'; }, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: 'test' + }; + + let expectedNumberOfConnections = connections.user.length; + workspaceConfigurationServiceMock.setup(x => x.lookup( + Constants.connectionsArrayName)) + .returns(() => connections); + + let connectionProfile = new ConnectionProfile(msSQLCapabilities, newProfile); + let newId = 'newid'; + let calledValue: any; + let nothing: void; + let configEditingServiceMock: TypeMoq.Mock = TypeMoq.Mock.ofType(ConfigurationEditingService); + configEditingServiceMock.setup(x => x.writeConfiguration(ConfigurationTarget.USER, TypeMoq.It.isAny())).callback((x: any, val: any) => { + calledValue = val.value as IConnectionProfileStore[]; + }).returns(() => TPromise.as(nothing)); + configEditingServiceMock.setup(x => x.writeConfiguration(ConfigurationTarget.WORKSPACE, TypeMoq.It.isAny())).callback((x: any, val: any) => { + + }).returns(() => TPromise.as(nothing)); + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + config.changeGroupIdForConnection(connectionProfile, newId).then(() => { + configEditingServiceMock.verify(y => y.writeConfiguration(ConfigurationTarget.USER, + TypeMoq.It.is(c => (c.value as IConnectionProfileStore[]).length === expectedNumberOfConnections)), TypeMoq.Times.atLeastOnce()); + calledValue.forEach(con => { + }); + }).then(() => done(), (err) => done(err)); + }); + + test('fixConnectionIds should replace duplicate ids with new ones', (done) => { + let profiles: IConnectionProfileStore[] = [ + { + options: {}, + groupId: '1', + id: '1', + providerName: undefined, + savePassword: true, + }, { + options: {}, + groupId: '1', + id: '2', + providerName: undefined, + savePassword: true, + }, { + options: {}, + groupId: '1', + id: '3', + providerName: undefined, + savePassword: true, + }, { + options: {}, + groupId: '1', + id: '2', + providerName: undefined, + savePassword: true, + }, { + options: {}, + groupId: '1', + id: '4', + providerName: undefined, + savePassword: true, + }, { + options: {}, + groupId: '1', + id: '3', + providerName: undefined, + savePassword: true, + }, { + options: {}, + groupId: '1', + id: '2', + providerName: undefined, + savePassword: true, + } + ]; + + let config = new ConnectionConfig(configEditingServiceMock.object, workspaceConfigurationServiceMock.object, capabilitiesService.object); + config.fixConnectionIds(profiles); + let ids = profiles.map(x => x.id); + for (var index = 0; index < ids.length; index++) { + var id = ids[index]; + assert.equal(ids.lastIndexOf(id), index); + } + done(); + }); +}); + diff --git a/src/sqltest/parts/connection/connectionDialogService.test.ts b/src/sqltest/parts/connection/connectionDialogService.test.ts new file mode 100644 index 0000000000..2106698eec --- /dev/null +++ b/src/sqltest/parts/connection/connectionDialogService.test.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ConnectionDialogService } from 'sql/parts/connection/connectionDialog/connectionDialogService'; +import { ConnectionDialogWidget } from 'sql/parts/connection/connectionDialog/connectionDialogWidget'; +import { ConnectionManagementService } from 'sql/parts/connection/common/connectionManagementService'; +import { ConnectionType, IConnectableInput, IConnectionResult, INewConnectionParams } from 'sql/parts/connection/common/connectionManagement'; +import { ContextKeyServiceStub } from 'sqltest/stubs/contextKeyServiceStub'; +import { ErrorMessageServiceStub } from 'sqltest/stubs/errorMessageServiceStub'; + +import * as TypeMoq from 'typemoq'; + +suite('ConnectionDialogService tests', () => { + + let connectionDialogService: ConnectionDialogService; + let mockConnectionManagementService: TypeMoq.Mock; + let mockConnectionDialog: TypeMoq.Mock; + + setup(() => { + let errorMessageService = getMockErrorMessageService(); + connectionDialogService = new ConnectionDialogService(undefined, undefined, undefined, errorMessageService.object, undefined); + mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Strict, {}, {}); + (connectionDialogService as any)._connectionManagementService = mockConnectionManagementService.object; + mockConnectionDialog = TypeMoq.Mock.ofType(ConnectionDialogWidget, TypeMoq.MockBehavior.Strict, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + new ContextKeyServiceStub() + ); + mockConnectionDialog.setup(c => c.resetConnection()); + (connectionDialogService as any)._connectionDialog = mockConnectionDialog.object; + }); + + function getMockErrorMessageService(): TypeMoq.Mock { + let mockMessageService = TypeMoq.Mock.ofType(ErrorMessageServiceStub); + mockMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())); + return mockMessageService; + } + + function testHandleDefaultOnConnectUri(isEditor: boolean): Thenable { + let testUri = 'test_uri'; + let connectionParams = { + connectionType: isEditor ? ConnectionType.editor : ConnectionType.default, + input: { + uri: testUri, + onConnectStart: undefined, + onConnectSuccess: undefined, + onConnectReject: undefined, + onDisconnect: undefined + }, + runQueryOnCompletion: undefined, + querySelection: undefined + }; + mockConnectionManagementService.setup(x => x.connectAndSaveProfile(undefined, TypeMoq.It.is(_ => true), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns( + () => Promise.resolve({ connected: true, errorMessage: undefined, errorCode: undefined })); + + // If I call handleDefaultOnConnect with the given parameters + let thenable: Thenable = (connectionDialogService as any).handleDefaultOnConnect(connectionParams, undefined); + return thenable.then(() => { + // Then the Connection Management Service's connect method was called with the expected URI + let expectedUri = isEditor ? testUri : undefined; + mockConnectionManagementService.verify( + x => x.connectAndSaveProfile(undefined, TypeMoq.It.is(uri => uri === expectedUri), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + TypeMoq.Times.once()); + }); + } + + test('handleDefaultOnConnect uses params URI for editor connections', done => { + testHandleDefaultOnConnectUri(true).then(() => done(), err => { + done(err); + }); + }); + + test('handleDefaultOnConnect uses undefined URI for non-editor connections', done => { + testHandleDefaultOnConnectUri(false).then(() => done(), err => { + done(err); + }); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/connection/connectionManagementService.test.ts b/src/sqltest/parts/connection/connectionManagementService.test.ts new file mode 100644 index 0000000000..96ebb477e6 --- /dev/null +++ b/src/sqltest/parts/connection/connectionManagementService.test.ts @@ -0,0 +1,563 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ConnectionDialogTestService } from 'sqltest/stubs/connectionDialogTestService'; +import { ConnectionManagementService } from 'sql/parts/connection/common/connectionManagementService'; +import { ConnectionStatusManager } from 'sql/parts/connection/common/connectionStatusManager'; +import { ConnectionStore } from 'sql/parts/connection/common/connectionStore'; +import { + INewConnectionParams, ConnectionType, + IConnectionCompletionOptions, IConnectionResult, + RunQueryOnConnectionMode +} from 'sql/parts/connection/common/connectionManagement'; +import * as Constants from 'sql/parts/connection/common/constants'; +import * as Utils from 'sql/parts/connection/common/utils'; + +import { WorkbenchEditorTestService } from 'sqltest/stubs/workbenchEditorTestService'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { EditorGroupTestService } from 'sqltest/stubs/editorGroupService'; +import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; +import { ConnectionProviderStub } from 'sqltest/stubs/connectionProviderStub'; + +import * as data from 'data'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { WorkspaceConfigurationTestService } from 'sqltest/stubs/workspaceConfigurationTestService'; + +import * as assert from 'assert'; +import * as TypeMoq from 'typemoq'; + +suite('SQL ConnectionManagementService tests', () => { + + let capabilitiesService: CapabilitiesTestService; + let connectionDialogService: TypeMoq.Mock; + let connectionStore: TypeMoq.Mock; + let workbenchEditorService: TypeMoq.Mock; + let editorGroupService: TypeMoq.Mock; + let connectionStatusManager: ConnectionStatusManager; + let mssqlConnectionProvider: TypeMoq.Mock; + let otherConnectionProvider: TypeMoq.Mock; + let workspaceConfigurationServiceMock: TypeMoq.Mock; + + let none: void; + + let connectionProfile: IConnectionProfile = { + serverName: 'new server', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: 'integrated', + savePassword: true, + groupFullName: 'g2/g2-2', + groupId: 'group id', + getOptionsKey: () => { return 'connectionId'; }, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }; + let connectionProfileWithEmptySavedPassword: IConnectionProfile = + Object.assign({}, connectionProfile, { password: '', serverName: connectionProfile.serverName + 1 }); + let connectionProfileWithEmptyUnsavedPassword: IConnectionProfile = + Object.assign({}, connectionProfile, { password: '', serverName: connectionProfile.serverName + 2, savePassword: false }); + + let connectionManagementService: ConnectionManagementService; + let configResult: { [key: string]: any } = {}; + + setup(() => { + + capabilitiesService = new CapabilitiesTestService(); + connectionDialogService = TypeMoq.Mock.ofType(ConnectionDialogTestService); + connectionStore = TypeMoq.Mock.ofType(ConnectionStore); + workbenchEditorService = TypeMoq.Mock.ofType(WorkbenchEditorTestService); + editorGroupService = TypeMoq.Mock.ofType(EditorGroupTestService); + connectionStatusManager = new ConnectionStatusManager(capabilitiesService); + mssqlConnectionProvider = TypeMoq.Mock.ofType(ConnectionProviderStub); + otherConnectionProvider = TypeMoq.Mock.ofType(ConnectionProviderStub); + + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined)).returns(() => TPromise.as(none)); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined)).returns(() => TPromise.as(none)); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => TPromise.as(none)); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny())).returns(() => TPromise.as(none)); + + connectionStore.setup(x => x.addActiveConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + connectionStore.setup(x => x.saveProfile(TypeMoq.It.isAny())).returns(() => Promise.resolve(connectionProfile)); + workbenchEditorService.setup(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => TPromise.as(undefined)); + editorGroupService.setup(x => x.getStacksModel()).returns(() => undefined); + connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is( + c => c.serverName === connectionProfile.serverName))).returns(() => Promise.resolve({ profile: connectionProfile, savedCred: true })); + connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is( + c => c.serverName === connectionProfileWithEmptySavedPassword.serverName))).returns( + () => Promise.resolve({ profile: connectionProfileWithEmptySavedPassword, savedCred: true })); + connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is( + c => c.serverName === connectionProfileWithEmptyUnsavedPassword.serverName))).returns( + () => Promise.resolve({ profile: connectionProfileWithEmptyUnsavedPassword, savedCred: false })); + connectionStore.setup(x => x.isPasswordRequired(TypeMoq.It.isAny())).returns(() => true); + + mssqlConnectionProvider.setup(x => x.connect(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => undefined); + otherConnectionProvider.setup(x => x.connect(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => undefined); + + // setup configuration to return a config that can be modified later. + + workspaceConfigurationServiceMock = TypeMoq.Mock.ofType(WorkspaceConfigurationTestService); + workspaceConfigurationServiceMock.setup(x => x.getConfiguration(Constants.sqlConfigSectionName)) + .returns(() => configResult); + + connectionManagementService = createConnectionManagementService(); + + connectionManagementService.registerProvider('MSSQL', mssqlConnectionProvider.object); + }); + + function createConnectionManagementService(): ConnectionManagementService { + let connectionManagementService = new ConnectionManagementService( + undefined, + connectionStore.object, + connectionDialogService.object, + undefined, + undefined, + undefined, + workbenchEditorService.object, + undefined, + undefined, + undefined, + undefined, + workspaceConfigurationServiceMock.object, + undefined, + capabilitiesService, + undefined, + editorGroupService.object, + undefined, + undefined, + undefined, + undefined + ); + return connectionManagementService; + } + + function verifyShowDialog(connectionProfile: IConnectionProfile, connectionType: ConnectionType, uri: string, error?: string, didShow: boolean = true): void { + + if (connectionProfile) { + connectionDialogService.verify(x => x.showDialog( + TypeMoq.It.isAny(), + TypeMoq.It.is(p => p.connectionType === connectionType && (uri === undefined || p.input.uri === uri)), + TypeMoq.It.is(c => c.serverName === connectionProfile.serverName), error), + didShow ? TypeMoq.Times.once() : TypeMoq.Times.never()); + + } else { + connectionDialogService.verify(x => x.showDialog( + TypeMoq.It.isAny(), + TypeMoq.It.is(p => p.connectionType === connectionType && ((uri === undefined && p.input === undefined) || p.input.uri === uri)), + undefined, error), didShow ? TypeMoq.Times.once() : TypeMoq.Times.never()); + } + + } + + function verifyOptions(options?: IConnectionCompletionOptions, fromDialog?: boolean): void { + + if (options) { + if (options.saveTheConnection) { + connectionStore.verify(x => x.saveProfile(TypeMoq.It.isAny()), TypeMoq.Times.once()); + } + if (options.showDashboard) { + workbenchEditorService.verify(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); + } + } + + if (fromDialog !== undefined && !fromDialog) { + connectionStore.verify(x => x.addSavedPassword(TypeMoq.It.isAny()), TypeMoq.Times.once()); + } + + } + + function connect(uri: string, options?: IConnectionCompletionOptions, fromDialog?: boolean, connection?: IConnectionProfile, error?: string): Promise { + let connectionToUse = connection ? connection : connectionProfile; + return new Promise((resolve, reject) => { + let id = connectionToUse.getOptionsKey(); + let defaultUri = 'connection://' + (id ? id : connection.serverName + ':' + connection.databaseName); + connectionManagementService.onConnectionRequestSent(() => { + let info: data.ConnectionInfoSummary = { + connectionId: error ? undefined : 'id', + connectionSummary: { + databaseName: connectionToUse.databaseName, + serverName: connectionToUse.serverName, + userName: connectionToUse.userName + }, + errorMessage: error, + errorNumber: undefined, + messages: error, + ownerUri: uri ? uri : defaultUri, + serverInfo: undefined + }; + connectionManagementService.onConnectionComplete(0, info); + }); + connectionManagementService.cancelConnectionForUri(uri).then(() => { + if (fromDialog) { + resolve(connectionManagementService.connectAndSaveProfile(connectionToUse, uri, options)); + } else { + resolve(connectionManagementService.connect(connectionToUse, uri, options)); + } + }); + }); + } + + test('showConnectionDialog should open the dialog with default type given no parameters', done => { + connectionManagementService.showConnectionDialog().then(() => { + verifyShowDialog(undefined, ConnectionType.default, undefined); + done(); + }).catch(err => { + done(err); + }); + }); + + test('showConnectionDialog should open the dialog with given type given valid input', done => { + let params: INewConnectionParams = { + connectionType: ConnectionType.editor, + input: { + onConnectReject: undefined, + onConnectStart: undefined, + onDisconnect: undefined, + onConnectSuccess: undefined, + uri: 'Editor Uri' + }, + runQueryOnCompletion: RunQueryOnConnectionMode.executeQuery + }; + connectionManagementService.showConnectionDialog(params).then(() => { + verifyShowDialog(undefined, params.connectionType, params.input.uri); + done(); + }).catch(err => { + done(err); + }); + }); + + test('showConnectionDialog should pass the model to the dialog if there is a model assigned to the uri', done => { + let params: INewConnectionParams = { + connectionType: ConnectionType.editor, + input: { + onConnectReject: undefined, + onConnectStart: undefined, + onDisconnect: undefined, + onConnectSuccess: undefined, + uri: 'Editor Uri' + }, + runQueryOnCompletion: RunQueryOnConnectionMode.executeQuery + }; + + connect(params.input.uri).then(() => { + let saveConnection = connectionManagementService.getConnectionProfile(params.input.uri); + + assert.notEqual(saveConnection, undefined, `profile was not added to the connections`); + assert.equal(saveConnection.serverName, connectionProfile.serverName, `Server names are different`); + connectionManagementService.showConnectionDialog(params).then(() => { + verifyShowDialog(connectionProfile, params.connectionType, params.input.uri); + done(); + }).catch(err => { + done(err); + }); + }); + }); + + test('connect should save profile given options with saveProfile set to true', done => { + let uri: string = 'Editor Uri'; + let options: IConnectionCompletionOptions = { + params: undefined, + saveTheConnection: true, + showDashboard: false, + showConnectionDialogOnError: false, + showFirewallRuleOnError: false + }; + + connect(uri, options).then(() => { + verifyOptions(options); + done(); + }).catch(err => { + done(err); + }); + }); + + /* Andresse 10/5/17 commented this test out since it was only working before my changes by the chance of how Promises work + If we want to continue to test this, the connection logic needs to be rewritten to actually wait for everything to be done before it resolves */ + // test('connect should show dashboard given options with showDashboard set to true', done => { + // let uri: string = 'Editor Uri'; + // let options: IConnectionCompletionOptions = { + // params: undefined, + // saveTheConnection: false, + // showDashboard: true, + // showConnectionDialogOnError: false + // }; + + // connect(uri, options).then(() => { + // verifyOptions(options); + // done(); + // }).catch(err => { + // done(err); + // }); + // }); + + test('connect should pass the params in options to onConnectSuccess callback', done => { + let uri: string = 'Editor Uri'; + let paramsInOnConnectSuccess: INewConnectionParams; + let options: IConnectionCompletionOptions = { + params: { + connectionType: ConnectionType.editor, + input: { + onConnectSuccess: (params?: INewConnectionParams) => { + paramsInOnConnectSuccess = params; + }, + onConnectReject: undefined, + onConnectStart: undefined, + onDisconnect: undefined, + uri: uri + }, + querySelection: undefined, + runQueryOnCompletion: RunQueryOnConnectionMode.none + }, + saveTheConnection: true, + showDashboard: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: false + }; + + connect(uri, options).then(() => { + verifyOptions(options); + assert.notEqual(paramsInOnConnectSuccess, undefined); + assert.equal(paramsInOnConnectSuccess.connectionType, options.params.connectionType); + done(); + }).catch(err => { + done(err); + }); + }); + + test('connectAndSaveProfile should show not load the password', done => { + let uri: string = 'Editor Uri'; + let options: IConnectionCompletionOptions = undefined; + + connect(uri, options, true).then(() => { + verifyOptions(options, true); + done(); + }).catch(err => { + done(err); + }); + }); + + test('connect with undefined uri and options should connect using the default uri', done => { + let uri = undefined; + let options: IConnectionCompletionOptions = undefined; + + connect(uri, options).then(() => { + assert.equal(connectionManagementService.isProfileConnected(connectionProfile), true); + done(); + }).catch(err => { + done(err); + }); + }); + + test('failed connection should open the dialog if connection fails', done => { + let uri = undefined; + let error: string = 'error'; + let expectedConnection: boolean = false; + let expectedError: string = error; + let options: IConnectionCompletionOptions = { + params: undefined, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: false + }; + + connect(uri, options, false, connectionProfile, error).then(result => { + assert.equal(result.connected, expectedConnection); + assert.equal(result.errorMessage, expectedError); + verifyShowDialog(connectionProfile, ConnectionType.default, uri, error); + done(); + }).catch(err => { + done(err); + }); + }); + + test('failed connection should not open the dialog if the option is set to false even if connection fails', done => { + let uri = undefined; + let error: string = 'error when options set to false'; + let expectedConnection: boolean = false; + let expectedError: string = error; + let options: IConnectionCompletionOptions = { + params: undefined, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: false, + showFirewallRuleOnError: false + }; + + connect(uri, options, false, connectionProfile, error).then(result => { + assert.equal(result.connected, expectedConnection); + assert.equal(result.errorMessage, expectedError); + // TODO: not sure how to verify not called + done(); + }).catch(err => { + done(err); + }); + }); + + + test('connect when password is empty and unsaved should open the dialog', done => { + let uri = undefined; + let expectedConnection: boolean = false; + let expectedError: string = undefined; + let options: IConnectionCompletionOptions = { + params: undefined, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: false + }; + + connect(uri, options, false, connectionProfileWithEmptyUnsavedPassword).then(result => { + assert.equal(result.connected, expectedConnection); + assert.equal(result.errorMessage, expectedError); + verifyShowDialog(connectionProfileWithEmptyUnsavedPassword, ConnectionType.default, uri, expectedError); + done(); + }).catch(err => { + done(err); + }); + }); + + test('connect when password is empty and saved should not open the dialog', done => { + let uri = undefined; + let expectedConnection: boolean = true; + let expectedError: string = undefined; + let options: IConnectionCompletionOptions = { + params: undefined, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: false + }; + + connect(uri, options, false, connectionProfileWithEmptySavedPassword).then(result => { + assert.equal(result.connected, expectedConnection); + assert.equal(result.errorMessage, expectedError); + verifyShowDialog(connectionProfileWithEmptySavedPassword, ConnectionType.default, uri, expectedError, false); + done(); + }).catch(err => { + done(err); + }); + }); + + test('connect from editor when empty password when it is required and saved should not open the dialog', done => { + let uri = 'editor 3'; + let expectedConnection: boolean = true; + let expectedError: string = undefined; + let options: IConnectionCompletionOptions = { + params: { + connectionType: ConnectionType.editor, + input: { + onConnectSuccess: undefined, + onConnectReject: undefined, + onConnectStart: undefined, + onDisconnect: undefined, + uri: uri + }, + querySelection: undefined, + runQueryOnCompletion: RunQueryOnConnectionMode.none + }, + saveTheConnection: true, + showDashboard: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: false + }; + + connect(uri, options, false, connectionProfileWithEmptySavedPassword).then(result => { + assert.equal(result.connected, expectedConnection); + assert.equal(result.errorMessage, expectedError); + verifyShowDialog(connectionProfileWithEmptySavedPassword, ConnectionType.editor, uri, expectedError, false); + done(); + }).catch(err => { + done(err); + }); + }); + + test('doChangeLanguageFlavor should throw on unknown provider', done => { + // given a provider that will never exist + let invalidProvider = 'notaprovider'; + // when I call doChangeLanguageFlavor + // Then I expect it to throw + assert.throws(() => connectionManagementService.doChangeLanguageFlavor('file://my.sql', 'sql', invalidProvider)); + done(); + }); + + test('doChangeLanguageFlavor should send event for known provider', done => { + // given a provider that is registered + let uri = 'file://my.sql'; + let language = 'sql'; + let flavor = 'MSSQL'; + // when I call doChangeLanguageFlavor + try { + let called = false; + connectionManagementService.onLanguageFlavorChanged((changeParams: data.DidChangeLanguageFlavorParams) => { + called = true; + assert.equal(changeParams.uri, uri); + assert.equal(changeParams.language, language); + assert.equal(changeParams.flavor, flavor); + }); + connectionManagementService.doChangeLanguageFlavor(uri, language, flavor); + assert.ok(called, 'expected onLanguageFlavorChanged event to be sent'); + done(); + } catch (error) { + done(error); + } + }); + + test('ensureDefaultLanguageFlavor should not send event if uri is connected', done => { + let uri: string = 'Editor Uri'; + let options: IConnectionCompletionOptions = { + params: undefined, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: false, + showFirewallRuleOnError: false + }; + let connectionManagementService = createConnectionManagementService(); + let called = false; + connectionManagementService.onLanguageFlavorChanged((changeParams: data.DidChangeLanguageFlavorParams) => { + called = true; + }); + connect(uri, options).then(() => { + connectionManagementService.ensureDefaultLanguageFlavor(uri); + assert.equal(called, false, 'do not expect flavor change to be called'); + done(); + }).catch(err => { + done(err); + }); + }); + + test('getConnectionId returns the URI associated with a connection that has had its database filled in', done => { + // Set up the connection management service with a connection corresponding to a default database + let dbName = 'master'; + let serverName = 'test_server'; + let userName = 'test_user'; + let connectionProfileWithoutDb: IConnectionProfile = Object.assign(connectionProfile, + { serverName: serverName, databaseName: '', userName: userName, getOptionsKey: () => undefined }); + let connectionProfileWithDb: IConnectionProfile = Object.assign(connectionProfileWithoutDb, { databaseName: dbName }); + // Save the database with a URI that has the database name filled in, to mirror Carbon's behavior + let ownerUri = Utils.generateUri(connectionProfileWithDb); + connect(ownerUri, undefined, false, connectionProfileWithoutDb).then(() => { + try { + // If I get the URI for the connection with or without a database from the connection management service + let actualUriWithDb = connectionManagementService.getConnectionId(connectionProfileWithDb); + let actualUriWithoutDb = connectionManagementService.getConnectionId(connectionProfileWithoutDb); + + // Then the retrieved URIs should match the one on the connection + let expectedUri = Utils.generateUri(connectionProfileWithoutDb); + assert.equal(actualUriWithDb, expectedUri); + assert.equal(actualUriWithoutDb, expectedUri); + done(); + } catch (err) { + done(err); + } + }, err => done(err)); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/connection/connectionProfile.test.ts b/src/sqltest/parts/connection/connectionProfile.test.ts new file mode 100644 index 0000000000..74752999c0 --- /dev/null +++ b/src/sqltest/parts/connection/connectionProfile.test.ts @@ -0,0 +1,206 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + + +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { IConnectionProfile, IConnectionProfileStore } from 'sql/parts/connection/common/interfaces'; +import data = require('data'); +import * as assert from 'assert'; + +suite('SQL ConnectionProfileInfo tests', () => { + let msSQLCapabilities: data.DataProtocolServerCapabilities; + + let connectionProfile: IConnectionProfile = { + serverName: 'new server', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: 'g2/g2-2', + groupId: 'group id', + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }; + + let storedProfile: IConnectionProfileStore = { + groupId: 'groupId', + id: 'id', + options: { + serverName: 'new server', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '' + }, + providerName: 'MSSQL', + savePassword: true + }; + + setup(() => { + let capabilities: data.DataProtocolServerCapabilities[] = []; + let connectionProvider: data.ConnectionProviderOptions = { + options: [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 0, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 1, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 3, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 2, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 4, + valueType: 0 + } + ] + }; + msSQLCapabilities = { + protocolVersion: '1', + providerName: 'MSSQL', + providerDisplayName: 'MSSQL', + connectionProvider: connectionProvider, + adminServicesProvider: undefined, + features: undefined + }; + capabilities.push(msSQLCapabilities); + }); + + test('set properties should set the values correctly', () => { + let conn = new ConnectionProfile(msSQLCapabilities, undefined); + assert.equal(conn.serverName, undefined); + conn.serverName = connectionProfile.serverName; + conn.databaseName = connectionProfile.databaseName; + conn.authenticationType = connectionProfile.authenticationType; + conn.password = connectionProfile.password; + conn.userName = connectionProfile.userName; + conn.groupId = connectionProfile.groupId; + conn.groupFullName = connectionProfile.groupFullName; + conn.savePassword = connectionProfile.savePassword; + assert.equal(conn.serverName, connectionProfile.serverName); + assert.equal(conn.databaseName, connectionProfile.databaseName); + assert.equal(conn.authenticationType, connectionProfile.authenticationType); + assert.equal(conn.password, connectionProfile.password); + assert.equal(conn.userName, connectionProfile.userName); + assert.equal(conn.groupId, connectionProfile.groupId); + assert.equal(conn.groupFullName, connectionProfile.groupFullName); + assert.equal(conn.savePassword, connectionProfile.savePassword); + }); + + test('constructor should initialize the options given a valid model', () => { + let conn = new ConnectionProfile(msSQLCapabilities, connectionProfile); + + assert.equal(conn.serverName, connectionProfile.serverName); + assert.equal(conn.databaseName, connectionProfile.databaseName); + assert.equal(conn.authenticationType, connectionProfile.authenticationType); + assert.equal(conn.password, connectionProfile.password); + assert.equal(conn.userName, connectionProfile.userName); + assert.equal(conn.groupId, connectionProfile.groupId); + assert.equal(conn.groupFullName, connectionProfile.groupFullName); + assert.equal(conn.savePassword, connectionProfile.savePassword); + }); + + test('getOptionsKey should create a valid unique id', () => { + let conn = new ConnectionProfile(msSQLCapabilities, connectionProfile); + let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user|databaseDisplayName:database|group:group id'; + let id = conn.getOptionsKey(); + assert.equal(id, expectedId); + }); + + test('createFromStoredProfile should create connection profile from stored profile', () => { + let savedProfile = storedProfile; + let connectionProfile = ConnectionProfile.createFromStoredProfile(savedProfile, msSQLCapabilities); + assert.equal(savedProfile.groupId, connectionProfile.groupId); + assert.deepEqual(savedProfile.options, connectionProfile.options); + assert.deepEqual(savedProfile.providerName, connectionProfile.providerName); + assert.deepEqual(savedProfile.savePassword, connectionProfile.savePassword); + assert.deepEqual(savedProfile.id, connectionProfile.id); + }); + + test('createFromStoredProfile should set the id to new guid if not set in stored profile', () => { + let savedProfile = Object.assign({}, storedProfile, { id: undefined }); + let connectionProfile = ConnectionProfile.createFromStoredProfile(savedProfile, msSQLCapabilities); + assert.equal(savedProfile.groupId, connectionProfile.groupId); + assert.deepEqual(savedProfile.options, connectionProfile.options); + assert.deepEqual(savedProfile.providerName, connectionProfile.providerName); + assert.equal(savedProfile.savePassword, connectionProfile.savePassword); + assert.notEqual(connectionProfile.id, undefined); + assert.equal(savedProfile.id, undefined); + }); + + test('withoutPassword should create a new instance without password', () => { + let conn = new ConnectionProfile(msSQLCapabilities, connectionProfile); + assert.notEqual(conn.password, ''); + let withoutPassword = conn.withoutPassword(); + assert.equal(withoutPassword.password, ''); + }); + + test('unique id should not include password', () => { + let conn = new ConnectionProfile(msSQLCapabilities, connectionProfile); + let withoutPassword = conn.withoutPassword(); + assert.equal(withoutPassword.getOptionsKey(), conn.getOptionsKey()); + }); + + test('cloneWithDatabase should create new profile with new id', () => { + let conn = new ConnectionProfile(msSQLCapabilities, connectionProfile); + let newProfile = conn.cloneWithDatabase('new db'); + assert.notEqual(newProfile.id, conn.id); + assert.equal(newProfile.databaseName, 'new db'); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/connection/connectionProfileGroup.test.ts b/src/sqltest/parts/connection/connectionProfileGroup.test.ts new file mode 100644 index 0000000000..d2926d5f08 --- /dev/null +++ b/src/sqltest/parts/connection/connectionProfileGroup.test.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + + +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import * as assert from 'assert'; + +suite('SQL ConnectionProfileGroup tests', () => { + let root: ConnectionProfileGroup; + let Groups1 = 'G1'; + let Groups11 = 'G11'; + let Groups2 = 'G2'; + let group1Node: ConnectionProfileGroup; + let group11Node: ConnectionProfileGroup; + let group2Node: ConnectionProfileGroup; + setup(() => { + root = new ConnectionProfileGroup(ConnectionProfileGroup.RootGroupName, undefined, ConnectionProfileGroup.RootGroupName, undefined, undefined); + + group1Node = new ConnectionProfileGroup(Groups1, root, Groups1, undefined, undefined); + group2Node = new ConnectionProfileGroup(Groups2, root, Groups2, undefined, undefined); + group11Node = new ConnectionProfileGroup(Groups11, root, Groups11, undefined, undefined); + root.addGroups([group1Node]); + group1Node.addGroups([group11Node]); + root.addGroups([group2Node]); + }); + + test('Root name should be returned as empty string', () => { + assert.equal(root.name, ''); + }); + + test('Fullname should return the group full name correctly', () => { + assert.equal(group1Node.fullName, 'G1'); + assert.equal(group2Node.fullName, 'G2'); + assert.equal(group11Node.fullName, 'G1/G11'); + }); + + test('getGroupFullNameParts should return a list With ROOT in it given an empty string', () => { + let groupFullName: string = ''; + let expected: string[] = [ConnectionProfileGroup.RootGroupName]; + let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName); + assert.deepEqual(actual, expected); + }); + + test('getGroupFullNameParts should return a list With ROOT in it given null', () => { + let groupFullName: string = undefined; + let expected: string[] = [ConnectionProfileGroup.RootGroupName]; + let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName); + assert.deepEqual(actual, expected); + }); + + test('getGroupFullNameParts should return a list With ROOT in it given /', () => { + let groupFullName: string = '/'; + let expected: string[] = [ConnectionProfileGroup.RootGroupName]; + let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName); + assert.deepEqual(actual, expected); + }); + + test('getGroupFullNameParts should add ROOT as first item if not added already and string starts with /', () => { + let groupFullName: string = '/Groups/Group1'; + let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1']; + let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName); + assert.deepEqual(actual, expected); + }); + + test('getGroupFullNameParts should add ROOT as first item if not added already', () => { + let groupFullName: string = 'Groups/Group1'; + let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1']; + let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName); + assert.deepEqual(actual, expected); + }); + + test('getGroupFullNameParts should not add ROOT if already added and string starts with /', () => { + let groupFullName: string = '/ROOT/Groups/Group1'; + let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1']; + let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName); + assert.deepEqual(actual, expected); + }); + + test('getGroupFullNameParts should not add ROOT if already added', () => { + let groupFullName: string = 'ROOT/Groups/Group1'; + let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1']; + let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName); + assert.deepEqual(actual, expected); + }); + + test('getGroupFullNameParts should not add ROOT if already added and it is not uppercase', () => { + let groupFullName: string = 'rOOT/Groups/Group1'; + let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1']; + let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName); + assert.deepEqual(actual, expected); + }); + + test('isRoot should return true given empty string', () => { + let name: string = ''; + let expected: boolean = true; + let actual = ConnectionProfileGroup.isRoot(name); + assert.deepEqual(actual, expected); + }); + + test('isRoot should return true given null', () => { + let name: string = undefined; + let expected: boolean = true; + let actual = ConnectionProfileGroup.isRoot(name); + assert.deepEqual(actual, expected); + }); + + test('isRoot should return true given /', () => { + let name: string = '/'; + let expected: boolean = true; + let actual = ConnectionProfileGroup.isRoot(name); + assert.deepEqual(actual, expected); + }); + + test('isRoot should return true given root', () => { + let name: string = 'root'; + let expected: boolean = true; + let actual = ConnectionProfileGroup.isRoot(name); + assert.deepEqual(actual, expected); + }); + + test('sameGroupName should return true given root', () => { + let name1: string = '/'; + let name2: string = ''; + let expected: boolean = true; + let actual = ConnectionProfileGroup.sameGroupName(name1, name2); + assert.deepEqual(actual, expected); + }); + + test('sameGroupName should return true given same group names', () => { + let name1: string = '/group1'; + let name2: string = '/Group1'; + let expected: boolean = true; + let actual = ConnectionProfileGroup.sameGroupName(name1, name2); + assert.deepEqual(actual, expected); + }); + + test('sameGroupName should return false given two different groups', () => { + let name1: string = '/'; + let name2: string = '/Group1'; + let expected: boolean = false; + let actual = ConnectionProfileGroup.sameGroupName(name1, name2); + assert.deepEqual(actual, expected); + }); +}); diff --git a/src/sqltest/parts/connection/connectionStatusManager.test.ts b/src/sqltest/parts/connection/connectionStatusManager.test.ts new file mode 100644 index 0000000000..6c7f4c3d43 --- /dev/null +++ b/src/sqltest/parts/connection/connectionStatusManager.test.ts @@ -0,0 +1,239 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import data = require('data'); +import { ConnectionStatusManager } from 'sql/parts/connection/common/connectionStatusManager'; +import * as Utils from 'sql/parts/connection/common/utils'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; + +let connections: ConnectionStatusManager; +let capabilitiesService: CapabilitiesTestService; +let connectionProfileObject: ConnectionProfile; +let connectionProfile: IConnectionProfile = { + serverName: 'new server', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: 'g2/g2-2', + groupId: 'group id', + getOptionsKey: () => 'connection1', + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined +}; +let editorConnectionProfile: IConnectionProfile = { + serverName: 'new server', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: 'g2/g2-2', + groupId: 'group id', + getOptionsKey: () => 'connection2', + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined +}; +let connectionProfileWithoutDbName: IConnectionProfile = { + serverName: 'new server', + databaseName: '', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: 'g2/g2-2', + groupId: 'group id', + getOptionsKey: () => 'connection1', + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined +}; + +let connection1Id: string; +let connection2Id: string; +let connection3Id: string; + +suite('SQL ConnectionStatusManager tests', () => { + setup(() => { + capabilitiesService = new CapabilitiesTestService(); + connectionProfileObject = new ConnectionProfile(capabilitiesService.getCapabilities().find(x => x.providerName === 'MSSQL') + , connectionProfile); + connections = new ConnectionStatusManager(capabilitiesService); + connection1Id = Utils.generateUri(connectionProfile); + connection2Id = 'connection2Id'; + connection3Id = 'connection3Id'; + connections.addConnection(connectionProfile, connection1Id); + connections.addConnection(editorConnectionProfile, connection2Id); + connections.addConnection(connectionProfileWithoutDbName, connection3Id); + }); + + test('findConnection should return undefined given invalid id', () => { + let id: string = 'invalid id'; + let expected = undefined; + let actual = connections.findConnection(id); + assert.equal(actual, expected); + }); + + test('findConnection should return connection given valid id', () => { + let id: string = connection1Id; + let expected = connectionProfileObject; + let actual = connections.findConnection(id); + assert.deepEqual(actual.connectionProfile, expected); + }); + + test('getConnectionProfile should return undefined given invalid id', () => { + let id: string = 'invalid id'; + let expected = undefined; + let actual = connections.getConnectionProfile(id); + assert.equal(actual, expected); + }); + + test('getConnectionProfile should return connection given valid id', () => { + let id: string = connection1Id; + let expected = connectionProfileObject; + let actual = connections.getConnectionProfile(id); + assert.deepEqual(actual, expected); + }); + + test('hasConnection should return false given invalid id', () => { + let id: string = 'invalid id'; + let expected = false; + let actual = connections.hasConnection(id); + assert.equal(actual, expected); + }); + + test('hasConnection should return true given valid id', () => { + let id: string = connection1Id; + let expected = true; + let actual = connections.hasConnection(id); + assert.equal(actual, expected); + }); + + test('addConnection should set connecting to true', () => { + let expected = true; + let summary: data.ConnectionInfoSummary = { + ownerUri: connection1Id, + connectionId: connection1Id, + messages: undefined, + errorMessage: undefined, + errorNumber: undefined, + serverInfo: undefined, + connectionSummary: undefined + }; + connections.onConnectionComplete(summary); + let actual = connections.addConnection(connectionProfile, connection1Id).connecting; + assert.equal(actual, expected); + }); + + test('onConnectionComplete should set connecting to false', () => { + let expected = false; + let summary: data.ConnectionInfoSummary = { + ownerUri: connection1Id, + connectionId: connection1Id, + messages: undefined, + errorMessage: undefined, + errorNumber: undefined, + serverInfo: undefined, + connectionSummary: undefined + }; + connections.onConnectionComplete(summary); + let actual = connections.findConnection(connection1Id).connecting; + assert.equal(actual, expected); + actual = connections.isConnecting(connection1Id); + assert.equal(actual, expected); + }); + + test('updateConnection should update the connection info', () => { + let expected = connectionProfile.groupId + '1'; + let expectedConnectionId = 'new id'; + connections.addConnection(connectionProfile, connection1Id); + + let updatedConnection = Object.assign({}, connectionProfile, { groupId: expected, getOptionsKey: () => connectionProfile.getOptionsKey() + expected, id: expectedConnectionId }); + let actualId = connections.updateConnectionProfile(updatedConnection, connection1Id); + + let newId = Utils.generateUri(updatedConnection); + let actual = connections.getConnectionProfile(newId).groupId; + let actualConnectionId = connections.getConnectionProfile(newId).id; + assert.equal(actual, expected); + assert.equal(actualId, newId); + assert.equal(actualConnectionId, expectedConnectionId); + }); + + test('updateDatabaseName should update the database name in connection', () => { + let dbName: string = 'db name'; + let summary: data.ConnectionInfoSummary = { + connectionSummary: { + databaseName: dbName, + serverName: undefined, + userName: undefined + } + , ownerUri: connection3Id, + connectionId: 'connection id', + errorMessage: undefined, + errorNumber: undefined, + messages: undefined, + serverInfo: undefined + }; + + //The original connection didn't have database name + let connectionStatus = connections.findConnection(connection3Id); + connectionStatus.connectionProfile.databaseName = ''; + + //Verify database name changed after connection is complete + connections.updateDatabaseName(summary); + connectionStatus = connections.findConnection(connection3Id); + assert.equal(connectionStatus.connectionProfile.databaseName, dbName); + }); + + test('getOriginalOwnerUri should return the original uri given uri with db name', () => { + let dbName: string = 'db name'; + let summary: data.ConnectionInfoSummary = { + connectionSummary: { + databaseName: dbName, + serverName: undefined, + userName: undefined + } + , ownerUri: connection3Id, + connectionId: 'connection id', + errorMessage: undefined, + errorNumber: undefined, + messages: undefined, + serverInfo: undefined + }; + + //The original connection didn't have database name + let connectionStatus = connections.findConnection(connection3Id); + connectionStatus.connectionProfile.databaseName = ''; + + //Verify database name changed after connection is complete + connections.updateDatabaseName(summary); + connectionStatus = connections.findConnection(connection3Id); + let ownerUriWithDbName = Utils.generateUriWithPrefix(connectionStatus.connectionProfile, 'connection://'); + + //The uri assigned to connection without db name should be the original one + let connectionWitDbStatus = connections.getOriginalOwnerUri(ownerUriWithDbName); + assert.equal(connectionWitDbStatus, connection3Id); + }); + + test('getOriginalOwnerUri should return given uri if the original uri is the same as the given uri', () => { + + let connectionStatus = connections.getOriginalOwnerUri(connection2Id); + assert.equal(connectionStatus, connection2Id); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/connection/connectionStore.test.ts b/src/sqltest/parts/connection/connectionStore.test.ts new file mode 100644 index 0000000000..1e1308f3ac --- /dev/null +++ b/src/sqltest/parts/connection/connectionStore.test.ts @@ -0,0 +1,459 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as TypeMoq from 'typemoq'; +import { ConnectionConfig } from 'sql/parts/connection/common/connectionConfig'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { WorkspaceConfigurationTestService } from 'sqltest/stubs/workspaceConfigurationTestService'; +import * as Constants from 'sql/parts/connection/common/constants'; +import { StorageTestService } from 'sqltest/stubs/storageTestService'; +import { ConnectionStore } from 'sql/parts/connection/common/connectionStore'; +import { CredentialsService } from 'sql/services/credentials/credentialsService'; +import * as assert from 'assert'; +import { Memento } from 'vs/workbench/common/memento'; +import { CapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import * as data from 'data'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { Emitter } from 'vs/base/common/event'; +import { IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; + +suite('SQL ConnectionStore tests', () => { + let defaultNamedProfile: IConnectionProfile; + let defaultUnnamedProfile: IConnectionProfile; + let context: TypeMoq.Mock; + let credentialStore: TypeMoq.Mock; + let connectionConfig: TypeMoq.Mock; + let workspaceConfigurationServiceMock: TypeMoq.Mock; + let storageServiceMock: TypeMoq.Mock; + let capabilitiesService: TypeMoq.Mock; + let mementoArray: any = []; + let maxRecent = 5; + let msSQLCapabilities: data.DataProtocolServerCapabilities; + let defaultNamedConnectionProfile: ConnectionProfile; + let onProviderRegistered = new Emitter(); + + + setup(() => { + defaultNamedProfile = Object.assign({}, { + serverName: 'namedServer', + databaseName: 'bcd', + authenticationType: 'SqlLogin', + userName: 'cde', + password: 'asdf!@#$', + savePassword: true, + groupId: '', + groupFullName: '', + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }); + + defaultUnnamedProfile = Object.assign({}, { + serverName: 'unnamedServer', + databaseName: undefined, + authenticationType: 'SqlLogin', + userName: 'aUser', + password: 'asdf!@#$', + savePassword: true, + groupId: '', + groupFullName: '', + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }); + + let momento = new Memento('ConnectionManagement'); + context = TypeMoq.Mock.ofInstance(momento); + context.setup(x => x.getMemento(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => mementoArray); + + credentialStore = TypeMoq.Mock.ofType(CredentialsService); + connectionConfig = TypeMoq.Mock.ofType(ConnectionConfig); + + // setup configuration to return maxRecent for the #MRU items + + let configResult: { [key: string]: any } = {}; + configResult[Constants.configMaxRecentConnections] = maxRecent; + + workspaceConfigurationServiceMock = TypeMoq.Mock.ofType(WorkspaceConfigurationTestService); + workspaceConfigurationServiceMock.setup(x => x.getConfiguration(Constants.sqlConfigSectionName)) + .returns(() => configResult); + + storageServiceMock = TypeMoq.Mock.ofType(StorageTestService); + + capabilitiesService = TypeMoq.Mock.ofType(CapabilitiesService); + let capabilities: data.DataProtocolServerCapabilities[] = []; + let connectionProvider: data.ConnectionProviderOptions = { + options: [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 0, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 1, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 3, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 2, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 4, + valueType: 0 + } + ] + }; + msSQLCapabilities = { + protocolVersion: '1', + providerName: 'MSSQL', + providerDisplayName: 'MSSQL', + connectionProvider: connectionProvider, + adminServicesProvider: undefined, + features: undefined + }; + capabilities.push(msSQLCapabilities); + capabilitiesService.setup(x => x.getCapabilities()).returns(() => capabilities); + capabilitiesService.setup(x => x.onProviderRegisteredEvent).returns(() => onProviderRegistered.event); + connectionConfig.setup(x => x.getCapabilities('MSSQL')).returns(() => msSQLCapabilities); + let groups: IConnectionProfileGroup[] = [ + { + id: 'root', + name: 'root', + parentId: '', + color: '', + description: '' + }, + { + id: 'g1', + name: 'g1', + parentId: 'root', + color: 'blue', + description: 'g1' + } + ]; + connectionConfig.setup(x => x.getAllGroups()).returns(() => groups); + + defaultNamedConnectionProfile = new ConnectionProfile(msSQLCapabilities, defaultNamedProfile); + }); + + test('addActiveConnection should limit recent connection saves to the MaxRecentConnections amount', (done) => { + // Given 5 is the max # creds + let numCreds = 6; + + // setup memento for MRU to return a list we have access to + credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(() => Promise.resolve(true)); + + // When saving 4 connections + // Expect all of them to be saved even if size is limited to 3 + let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, + credentialStore.object, capabilitiesService.object, connectionConfig.object); + let promise = Promise.resolve(); + for (let i = 0; i < numCreds; i++) { + let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + i }); + let connectionProfile = new ConnectionProfile(msSQLCapabilities, cred); + promise = promise.then(() => { + return connectionStore.addActiveConnection(connectionProfile); + }).then(() => { + let current = connectionStore.getRecentlyUsedConnections(); + if (i >= maxRecent) { + assert.equal(current.length, maxRecent, `expect only top ${maxRecent} creds to be saved`); + } else { + assert.equal(current.length, i + 1, `expect all credentials to be saved ${current.length}|${i + 1} `); + } + assert.equal(current[0].serverName, cred.serverName, 'Expect most recently saved item to be first in list'); + assert.ok(!current[0].password); + }); + } + promise.then(() => { + credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(numCreds)); + let recentConnections = connectionStore.getActiveConnections(); + assert.equal(numCreds, recentConnections.length, `expect number of active connection ${numCreds}|${recentConnections.length} `); + done(); + }, err => { + // Must call done here so test indicates it's finished if errors occur + done(err); + }); + }); + + test('addActiveConnection should add same connection exactly once', (done) => { + // setup memento for MRU to return a list we have access to + credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(() => Promise.resolve(true)); + + // Given we save the same connection twice + // Then expect the only 1 instance of that connection to be listed in the MRU + let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, + credentialStore.object, capabilitiesService.object, connectionConfig.object); + connectionStore.clearActiveConnections(); + connectionStore.clearRecentlyUsed(); + let promise = Promise.resolve(); + let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + 1 }); + let connectionProfile = new ConnectionProfile(msSQLCapabilities, cred); + promise = promise.then(() => { + return connectionStore.addActiveConnection(defaultNamedConnectionProfile); + }).then(() => { + return connectionStore.addActiveConnection(connectionProfile); + }).then(() => { + return connectionStore.addActiveConnection(connectionProfile); + }).then(() => { + let current = connectionStore.getRecentlyUsedConnections(); + assert.equal(current.length, 2, 'expect 2 unique credentials to have been added'); + assert.equal(current[0].serverName, cred.serverName, 'Expect most recently saved item to be first in list'); + assert.ok(!current[0].password); + }).then(() => done(), err => done(err)); + }); + + test('addActiveConnection should save password to credential store', (done) => { + + // Setup credential store to capture credentials sent to it + let capturedCreds: any; + credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .callback((cred: string, pass: any) => { + capturedCreds = { + 'credentialId': cred, + 'password': pass + }; + }) + .returns(() => Promise.resolve(true)); + + // Given we save 1 connection with password and multiple other connections without + let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, + credentialStore.object, capabilitiesService.object, connectionConfig.object); + connectionStore.clearActiveConnections(); + connectionStore.clearRecentlyUsed(); + let integratedCred = Object.assign({}, defaultNamedProfile, { + serverName: defaultNamedProfile.serverName + 'Integrated', + authenticationType: 'Integrated', + userName: '', + password: '' + }); + let noPwdCred = Object.assign({}, defaultNamedProfile, { + serverName: defaultNamedProfile.serverName + 'NoPwd', + password: '' + }); + let connectionProfile = new ConnectionProfile(msSQLCapabilities, defaultNamedProfile); + + let expectedCredCount = 0; + let promise = Promise.resolve(); + promise = promise.then(() => { + expectedCredCount++; + return connectionStore.addActiveConnection(connectionProfile); + }).then(() => { + let current = connectionStore.getRecentlyUsedConnections(); + // Then verify that since its password based we save the password + credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); + assert.strictEqual(capturedCreds.password, defaultNamedProfile.password); + let credId: string = capturedCreds.credentialId; + assert.ok(credId.includes(ConnectionStore.CRED_PROFILE_USER), 'Expect credential to be marked as an Profile cred'); + assert.ok(!current[0].password); + }).then(() => { + // When add integrated auth connection + expectedCredCount++; + let integratedCredConnectionProfile = new ConnectionProfile(msSQLCapabilities, integratedCred); + return connectionStore.addActiveConnection(integratedCredConnectionProfile); + }).then(() => { + let current = connectionStore.getRecentlyUsedConnections(); + // then expect no to have credential store called, but MRU count upped to 2 + credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); + assert.equal(current.length, expectedCredCount, `expect ${expectedCredCount} unique credentials to have been added`); + }).then(() => { + // When add connection without password + expectedCredCount++; + let noPwdCredConnectionProfile = new ConnectionProfile(msSQLCapabilities, noPwdCred); + return connectionStore.addActiveConnection(noPwdCredConnectionProfile); + }).then(() => { + let current = connectionStore.getRecentlyUsedConnections(); + // then expect no to have credential store called, but MRU count upped to 3 + credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); + assert.equal(current.length, expectedCredCount, `expect ${expectedCredCount} unique credentials to have been added`); + }).then(() => done(), err => done(err)); + }); + + test('can clear connections list', (done) => { + connectionConfig.setup(x => x.getConnections(TypeMoq.It.isAny())).returns(() => []); + + let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, + credentialStore.object, capabilitiesService.object, connectionConfig.object); + + // When we clear the connections list and get the list of available connection items + connectionStore.clearActiveConnections(); + connectionStore.clearRecentlyUsed(); + // Expect no connection items + let result = connectionStore.getActiveConnections(); + let expectedCount = 0; // 1 for create connection profile + assert.equal(result.length, expectedCount); + result = connectionStore.getRecentlyUsedConnections(); + assert.equal(result.length, expectedCount); + // Then test is complete + done(); + }); + + test('isPasswordRequired should return true for MSSQL SqlLogin', () => { + + let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, + credentialStore.object, capabilitiesService.object, connectionConfig.object); + + let expected: boolean = true; + let actual = connectionStore.isPasswordRequired(defaultNamedProfile); + + assert.equal(expected, actual); + }); + + test('isPasswordRequired should return true for MSSQL SqlLogin for connection profile object', () => { + let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, + credentialStore.object, capabilitiesService.object, connectionConfig.object); + let connectionProfile = new ConnectionProfile(msSQLCapabilities, defaultNamedProfile); + let expected: boolean = true; + let actual = connectionStore.isPasswordRequired(connectionProfile); + + assert.equal(expected, actual); + }); + + test('isPasswordRequired should return false if the password is not required in capabilities', () => { + let providerName: string = 'providername'; + let connectionProvider: data.ConnectionProviderOptions = { + options: msSQLCapabilities.connectionProvider.options.map(o => { + if (o.name === 'password') { + o.isRequired = false; + } + return o; + }) + }; + let providerCapabilities = { + protocolVersion: '1', + providerName: providerName, + providerDisplayName: providerName, + connectionProvider: connectionProvider, + adminServicesProvider: undefined, + features: undefined + }; + connectionConfig.setup(x => x.getCapabilities(providerName)).returns(() => providerCapabilities); + + let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, + credentialStore.object, capabilitiesService.object, connectionConfig.object); + let connectionProfile: IConnectionProfile = Object.assign({}, defaultNamedProfile, { providerName: providerName }); + let expected: boolean = false; + let actual = connectionStore.isPasswordRequired(connectionProfile); + + assert.equal(expected, actual); + }); + + test('saveProfile should save the password after the profile is saved', done => { + let password: string = 'asdf!@#$'; + let groupId: string = 'group id'; + let connectionProfile: IConnectionProfile = Object.assign({}, defaultNamedProfile, { password: password }); + let savedConnection: IConnectionProfile = Object.assign({}, connectionProfile, { groupId: groupId, password: '' }); + connectionConfig.setup(x => x.addConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve(savedConnection)); + credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + + let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, + credentialStore.object, capabilitiesService.object, connectionConfig.object); + + connectionStore.saveProfile(connectionProfile).then(profile => { + // add connection should be called with a profile without password + connectionConfig.verify(x => x.addConnection(TypeMoq.It.is(c => c.password === '')), TypeMoq.Times.once()); + credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); + assert.equal(profile.password, password, 'The returned profile should still keep the password'); + assert.equal(profile.groupId, groupId, 'Group id should be set in the profile'); + done(); + }).catch(err => { + assert.fail(err); + done(err); + }); + }); + + test('addConnectionToMemento should not add duplicate items', () => { + let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, + credentialStore.object, capabilitiesService.object, connectionConfig.object); + let mementoKey = 'RECENT_CONNECTIONS2'; + connectionStore.clearFromMemento(mementoKey); + let connectionProfile: IConnectionProfile = Object.assign({}, defaultNamedProfile); + connectionStore.addConnectionToMemento(connectionProfile, mementoKey); + + connectionProfile = Object.assign({}, defaultNamedProfile, { authenticationType: 'Integrated', userName: '' }); + connectionStore.addConnectionToMemento(connectionProfile, mementoKey); + + let currentList = connectionStore.getConnectionsFromMemento(mementoKey); + assert.equal(currentList.length, 2, 'Adding same connection with different auth'); + + connectionProfile = Object.assign({}, defaultNamedProfile, { groupFullName: 'new group' }); + connectionStore.addConnectionToMemento(connectionProfile, mementoKey); + + currentList = connectionStore.getConnectionsFromMemento(mementoKey); + assert.equal(currentList.length, 3, 'Adding same connection with different group name'); + + connectionProfile = Object.assign({}, defaultNamedProfile, + { groupFullName: defaultNamedProfile.groupFullName.toUpperCase() }); + connectionStore.addConnectionToMemento(connectionProfile, mementoKey); + + currentList = connectionStore.getConnectionsFromMemento(mementoKey); + assert.equal(currentList.length, 3, 'Adding same connection with same group name but uppercase'); + + connectionProfile = Object.assign({}, defaultNamedProfile, + { groupFullName: '' }); + connectionStore.addConnectionToMemento(connectionProfile, mementoKey); + + currentList = connectionStore.getConnectionsFromMemento(mementoKey); + assert.equal(currentList.length, 3, 'Adding same connection with group empty string'); + + connectionProfile = Object.assign({}, defaultNamedProfile, + { groupFullName: '/' }); + connectionStore.addConnectionToMemento(connectionProfile, mementoKey); + + currentList = connectionStore.getConnectionsFromMemento(mementoKey); + assert.equal(currentList.length, 3, 'Adding same connection with group /'); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/connection/connectionTreeActions.test.ts b/src/sqltest/parts/connection/connectionTreeActions.test.ts new file mode 100644 index 0000000000..4d66f10e5d --- /dev/null +++ b/src/sqltest/parts/connection/connectionTreeActions.test.ts @@ -0,0 +1,516 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as TypeMoq from 'typemoq'; +import * as assert from 'assert'; +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { + RefreshAction, AddServerAction, DeleteConnectionAction, DisconnectConnectionAction, + ActiveConnectionsFilterAction, RecentConnectionsFilterAction +} + from 'sql/parts/registeredServer/viewlet/connectionTreeAction'; +import { TestConnectionManagementService } from 'sqltest/stubs/connectionManagementService.test'; +import { ErrorMessageServiceStub } from 'sqltest/stubs/errorMessageServiceStub'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { ServerTreeView } from 'sql/parts/registeredServer/viewlet/serverTreeView'; +import * as Constants from 'sql/parts/connection/common/constants'; +import * as LocalizedConstants from 'sql/parts/connection/common/localizedConstants'; +import { ObjectExplorerService, ObjectExplorerNodeEventArgs } from 'sql/parts/registeredServer/common/objectExplorerService'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; +import { NodeType } from 'sql/parts/registeredServer/common/nodeType'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { ServerTreeDataSource } from 'sql/parts/registeredServer/viewlet/serverTreeDataSource'; +import { Builder, $ } from 'vs/base/browser/builder'; +import WinJS = require('vs/base/common/winjs.base'); +import { Emitter } from 'vs/base/common/event'; +import Severity from 'vs/base/common/severity'; +import { ObjectExplorerActionsContext, ManageConnectionAction } from 'sql/parts/registeredServer/viewlet/objectExplorerActions'; +import { IConnectionResult, IConnectionParams } from 'sql/parts/connection/common/connectionManagement'; +import { TreeSelectionHandler } from 'sql/parts/registeredServer/viewlet/treeSelectionHandler'; + +suite('SQL Connection Tree Action tests', () => { + let errorMessageService: TypeMoq.Mock; + let connectionResult: IConnectionResult = { + connected: true, + errorMessage: undefined, + errorCode: undefined + }; + setup(() => { + errorMessageService = TypeMoq.Mock.ofType(ErrorMessageServiceStub, TypeMoq.MockBehavior.Loose); + let nothing: void; + errorMessageService.setup(x => x.showDialog(Severity.Error, TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => nothing); + }); + + function createConnectionManagementService(isConnectedReturnValue: boolean): TypeMoq.Mock { + let connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict); + connectionManagementService.callBase = true; + connectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => isConnectedReturnValue); + connectionManagementService.setup(x => x.connect(TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny(), undefined)).returns(() => Promise.resolve(connectionResult)); + connectionManagementService.setup(x => x.disconnect(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + connectionManagementService.setup(x => x.findExistingConnection(TypeMoq.It.isAny())).returns(() => undefined); + connectionManagementService.setup(x => x.showDashboard(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + connectionManagementService.setup(x => x.isProfileConnected(TypeMoq.It.isAny())).returns(() => isConnectedReturnValue); + connectionManagementService.setup(x => x.isProfileConnecting(TypeMoq.It.isAny())).returns(() => false); + connectionManagementService.setup(x => x.showConnectionDialog(undefined, TypeMoq.It.isAny())).returns(() => new Promise((resolve, reject) => resolve())); + connectionManagementService.setup(x => x.onConnect).returns(() => new Emitter().event); + connectionManagementService.setup(x => x.onDisconnect).returns(() => new Emitter().event); + connectionManagementService.setup(x => x.deleteConnectionGroup(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + connectionManagementService.setup(x => x.deleteConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + + return connectionManagementService; + } + + function createObjectExplorerService(connectionManagementService: TestConnectionManagementService): TypeMoq.Mock { + let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Strict, connectionManagementService); + objectExplorerService.callBase = true; + objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => new TreeNode('', '', false, '', '', '', undefined, undefined)); + objectExplorerService.setup(x => x.getObjectExplorerNode(undefined)).returns(() => new TreeNode('', '', false, '', '', '', undefined, undefined)); + objectExplorerService.setup(x => x.onUpdateObjectExplorerNodes).returns(() => new Emitter().event); + + objectExplorerService.setup(x => x.onUpdateObjectExplorerNodes).returns(() => new Emitter().event); + return objectExplorerService; + } + + test('ManageConnectionAction - test if connect is called for manage action if not already connected', (done) => { + let isConnectedReturnValue: boolean = false; + + let connectionManagementService = createConnectionManagementService(isConnectedReturnValue); + let objectExplorerService = createObjectExplorerService(connectionManagementService.object); + let treeSelectionMock = TypeMoq.Mock.ofType(TreeSelectionHandler); + let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { + return treeSelectionMock.object; + }); + + let manageConnectionAction: ManageConnectionAction = new ManageConnectionAction(ManageConnectionAction.ID, + ManageConnectionAction.LABEL, connectionManagementService.object, instantiationService.object, objectExplorerService.object); + let connection: ConnectionProfile = new ConnectionProfile(undefined, { + savePassword: false, + groupFullName: 'testGroup', + serverName: 'testServerName', + databaseName: 'testDatabaseName', + authenticationType: 'integrated', + password: 'test', + userName: 'testUsername', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: 'testId' + }); + var actionContext = new ObjectExplorerActionsContext(); + actionContext.connectionProfile = connection; + manageConnectionAction.run(actionContext).then((value) => { + connectionManagementService.verify(x => x.connect(TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny(), undefined), TypeMoq.Times.once()); + }).then(() => done(), (err) => done(err)); + }); + + test('ManageConnectionAction - test if connect is called for manage action on database node if not already connected', (done) => { + let isConnectedReturnValue: boolean = false; + let connectionManagementService = createConnectionManagementService(isConnectedReturnValue); + let objectExplorerService = createObjectExplorerService(connectionManagementService.object); + let treeSelectionMock = TypeMoq.Mock.ofType(TreeSelectionHandler); + let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { + return treeSelectionMock.object; + }); + + let manageConnectionAction: ManageConnectionAction = new ManageConnectionAction(ManageConnectionAction.ID, + ManageConnectionAction.LABEL, connectionManagementService.object, instantiationService.object, objectExplorerService.object); + let connection: ConnectionProfile = new ConnectionProfile(undefined, { + savePassword: false, + groupFullName: 'testGroup', + serverName: 'testServerName', + databaseName: 'testDatabaseName', + authenticationType: 'integrated', + password: 'test', + userName: 'testUsername', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: 'testId' + }); + let treeNode = new TreeNode(NodeType.Database, 'db node', false, '', '', '', undefined, undefined); + treeNode.connection = connection; + var actionContext = new ObjectExplorerActionsContext(); + actionContext.treeNode = treeNode; + manageConnectionAction.run(actionContext).then((value) => { + connectionManagementService.verify(x => x.showDashboard(TypeMoq.It.isAny()), TypeMoq.Times.once()); + }).then(() => done(), (err) => done(err)); + }); + + + test('DisconnectConnectionAction - test if disconnect is called when profile is connected', (done) => { + let isConnectedReturnValue: boolean = true; + let connectionManagementService = createConnectionManagementService(isConnectedReturnValue); + let objectExplorerService = createObjectExplorerService(connectionManagementService.object); + + let changeConnectionAction: DisconnectConnectionAction = new DisconnectConnectionAction(DisconnectConnectionAction.ID, DisconnectConnectionAction.LABEL, connectionManagementService.object, objectExplorerService.object, errorMessageService.object); + let connection: ConnectionProfile = new ConnectionProfile(undefined, { + savePassword: false, + groupFullName: 'testGroup', + serverName: 'testServerName', + databaseName: 'testDatabaseName', + authenticationType: 'integrated', + password: 'test', + userName: 'testUsername', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: 'testId' + }); + var actionContext = new ObjectExplorerActionsContext(); + actionContext.connectionProfile = connection; + changeConnectionAction.run(actionContext).then((value) => { + connectionManagementService.verify(x => x.isProfileConnected(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); + connectionManagementService.verify(x => x.disconnect(TypeMoq.It.isAny()), TypeMoq.Times.once()); + }).then(() => done(), (err) => done(err)); + }); + + test('AddServerAction - test if show connection dialog is called', (done) => { + let connectionManagementService = createConnectionManagementService(true); + + let connectionTreeAction: AddServerAction = new AddServerAction(AddServerAction.ID, AddServerAction.LABEL, connectionManagementService.object); + let conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined); + connectionTreeAction.run(conProfGroup).then((value) => { + connectionManagementService.verify(x => x.showConnectionDialog(undefined, TypeMoq.It.isAny()), TypeMoq.Times.once()); + }).then(() => done(), (err) => done(err)); + }); + + test('ActiveConnectionsFilterAction - test if view is called to display filtered results', (done) => { + let connectionManagementService = createConnectionManagementService(true); + + let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { + return new TPromise((resolve) => resolve({})); + }); + + let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined); + serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString())); + serverTreeView.setup(x => x.refreshTree()); + let connectionTreeAction: ActiveConnectionsFilterAction = new ActiveConnectionsFilterAction(ActiveConnectionsFilterAction.ID, ActiveConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object); + connectionTreeAction.run().then((value) => { + serverTreeView.verify(x => x.showFilteredTree('active'), TypeMoq.Times.once()); + }).then(() => done(), (err) => done(err)); + }); + + test('ActiveConnectionsFilterAction - test if view is called refresh results if action is toggled', (done) => { + let connectionManagementService = createConnectionManagementService(true); + + let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { + return new TPromise((resolve) => resolve({})); + }); + + let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined); + serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString())); + serverTreeView.setup(x => x.refreshTree()); + let connectionTreeAction: ActiveConnectionsFilterAction = new ActiveConnectionsFilterAction(ActiveConnectionsFilterAction.ID, ActiveConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object); + connectionTreeAction.isSet = true; + connectionTreeAction.run().then((value) => { + serverTreeView.verify(x => x.refreshTree(), TypeMoq.Times.once()); + }).then(() => done(), (err) => done(err)); + }); + + test('RecentConnectionsFilterAction - test if view is called to display filtered results', (done) => { + let connectionManagementService = createConnectionManagementService(true); + + let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { + return new TPromise((resolve) => resolve({})); + }); + + let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined); + serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString())); + serverTreeView.setup(x => x.refreshTree()); + let connectionTreeAction: RecentConnectionsFilterAction = new RecentConnectionsFilterAction(RecentConnectionsFilterAction.ID, RecentConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object); + connectionTreeAction.run().then((value) => { + serverTreeView.verify(x => x.showFilteredTree('recent'), TypeMoq.Times.once()); + }).then(() => done(), (err) => done(err)); + }); + + test('RecentConnectionsFilterAction - test if view is called refresh results if action is toggled', (done) => { + let connectionManagementService = createConnectionManagementService(true); + + let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { + return new TPromise((resolve) => resolve({})); + }); + + let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined); + serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString())); + serverTreeView.setup(x => x.refreshTree()); + let connectionTreeAction: RecentConnectionsFilterAction = new RecentConnectionsFilterAction(RecentConnectionsFilterAction.ID, RecentConnectionsFilterAction.LABEL, serverTreeView.object, connectionManagementService.object); + connectionTreeAction.isSet = true; + connectionTreeAction.run().then((value) => { + serverTreeView.verify(x => x.refreshTree(), TypeMoq.Times.once()); + }).then(() => done(), (err) => done(err)); + }); + + test('DeleteConnectionAction - test delete connection', (done) => { + let connectionManagementService = createConnectionManagementService(true); + + let connection: ConnectionProfile = new ConnectionProfile(undefined, { + savePassword: false, + groupFullName: 'testGroup', + serverName: 'testServerName', + databaseName: 'testDatabaseName', + authenticationType: 'integrated', + password: 'test', + userName: 'testUsername', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: 'testId' + }); + let connectionAction: DeleteConnectionAction = new DeleteConnectionAction(DeleteConnectionAction.ID, + DeleteConnectionAction.DELETE_CONNECTION_LABEL, + connection, + connectionManagementService.object); + + connectionAction.run().then((value) => { + connectionManagementService.verify(x => x.deleteConnection(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); + }).then(() => done(), (err) => done(err)); + + }); + + test('DeleteConnectionAction - test delete connection group', (done) => { + let isConnectedReturnValue: boolean = false; + let connectionManagementService = createConnectionManagementService(isConnectedReturnValue); + let conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined); + let connectionAction: DeleteConnectionAction = new DeleteConnectionAction(DeleteConnectionAction.ID, + DeleteConnectionAction.DELETE_CONNECTION_LABEL, + conProfGroup, + connectionManagementService.object); + + connectionAction.run().then((value) => { + connectionManagementService.verify(x => x.deleteConnectionGroup(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); + }).then(() => done(), (err) => done(err)); + + }); + + test('DeleteConnectionAction - delete should not be called if connect is an unsaved connection', (done) => { + let isConnectedReturnValue: boolean = false; + let connectionManagementService = createConnectionManagementService(isConnectedReturnValue); + + let connection: ConnectionProfile = new ConnectionProfile(undefined, { + savePassword: false, + groupFullName: 'testGroup', + serverName: 'testServerName', + databaseName: 'testDatabaseName', + authenticationType: 'integrated', + password: 'test', + userName: 'testUsername', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: 'testId' + }); + connection.parent = new ConnectionProfileGroup(LocalizedConstants.unsavedGroupLabel, undefined, Constants.unsavedGroupId, undefined, undefined); + let connectionAction: DeleteConnectionAction = new DeleteConnectionAction(DeleteConnectionAction.ID, + DeleteConnectionAction.DELETE_CONNECTION_LABEL, + connection, + connectionManagementService.object); + + assert.equal(connectionAction.enabled, false, 'delete action should be disabled.'); + done(); + }); + + test('RefreshConnectionAction - refresh should be called if connection status is connect', (done) => { + let isConnectedReturnValue: boolean = true; + let sqlProvider = { + protocolVersion: '1', + providerName: 'MSSQL', + providerDisplayName: 'MSSQL', + connectionProvider: { options: [] }, + adminServicesProvider: { databaseInfoOptions: [], databaseFileInfoOptions: [], fileGroupInfoOptions: [] }, + features: undefined + }; + + var connection = new ConnectionProfile(sqlProvider, { + savePassword: false, + groupFullName: 'testGroup', + serverName: 'testServerName', + databaseName: 'testDatabaseName', + authenticationType: 'inetgrated', + password: 'test', + userName: 'testUsername', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: 'testID' + }); + var conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined); + conProfGroup.connections = [connection]; + var connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict); + connectionManagementService.callBase = true; + connectionManagementService.setup(x => x.getConnectionGroups()).returns(() => [conProfGroup]); + connectionManagementService.setup(x => x.getActiveConnections()).returns(() => [connection]); + connectionManagementService.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(() => new Promise((resolve) => { + resolve(connection); + })); + connectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => isConnectedReturnValue); + + var objectExplorerSession = { + success: true, + sessionId: '1234', + rootNode: { + nodePath: 'testServerName\tables', + nodeType: NodeType.Folder, + label: 'Tables', + isLeaf: false, + metadata: null, + nodeSubType: '', + nodeStatus: '', + errorMessage: '' + }, + errorMessage: '' + }; + + var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', null, null); + tablesNode.connection = connection; + tablesNode.session = objectExplorerSession; + var table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null); + var table2Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null); + tablesNode.children = [table1Node, table2Node]; + let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object); + objectExplorerService.callBase = true; + objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => tablesNode); + objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => TPromise.as([table1Node, table2Node])); + let builder: Builder = $().div(); + var dataSource = new ServerTreeDataSource(objectExplorerService.object, connectionManagementService.object, undefined); + let tree = TypeMoq.Mock.ofType(Tree, TypeMoq.MockBehavior.Loose, builder.getHTMLElement(), { dataSource }); + tree.callBase = true; + + tree.setup(x => x.refresh(TypeMoq.It.isAny())).returns(() => WinJS.TPromise.as(null)); + tree.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => WinJS.TPromise.as(null)); + tree.setup(x => x.collapse(TypeMoq.It.isAny())).returns(() => WinJS.TPromise.as(null)); + let connectionAction: RefreshAction = new RefreshAction(RefreshAction.ID, + RefreshAction.LABEL, + tree.object, + connection, + connectionManagementService.object, + objectExplorerService.object, + undefined); + + connectionAction.run().then((value) => { + connectionManagementService.verify(x => x.isConnected(undefined, TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); + objectExplorerService.verify(x => x.getObjectExplorerNode(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); + objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); + tree.verify(x => x.refresh(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); + tree.verify(x => x.expand(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); + }).then(() => done(), (err) => done(err)); + }); + + test('RefreshConnectionAction - refresh should not be called if connection status is not connect', (done) => { + let isConnectedReturnValue: boolean = false; + let sqlProvider = { + protocolVersion: '1', + providerName: 'MSSQL', + providerDisplayName: 'MSSQL', + connectionProvider: { options: [] }, + adminServicesProvider: { databaseInfoOptions: [], databaseFileInfoOptions: [], fileGroupInfoOptions: [] }, + features: undefined + }; + + var connection = new ConnectionProfile(sqlProvider, { + savePassword: false, + groupFullName: 'testGroup', + serverName: 'testServerName', + databaseName: 'testDatabaseName', + authenticationType: 'inetgrated', + password: 'test', + userName: 'testUsername', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: 'testID' + }); + var conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined); + conProfGroup.connections = [connection]; + var connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict); + connectionManagementService.callBase = true; + connectionManagementService.setup(x => x.getConnectionGroups()).returns(() => [conProfGroup]); + connectionManagementService.setup(x => x.getActiveConnections()).returns(() => [connection]); + connectionManagementService.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(() => new Promise((resolve) => { + resolve(connection); + })); + connectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => isConnectedReturnValue); + + var objectExplorerSession = { + success: true, + sessionId: '1234', + rootNode: { + nodePath: 'testServerName\tables', + nodeType: NodeType.Folder, + label: 'Tables', + isLeaf: false, + metadata: null, + nodeSubType: '', + nodeStatus: '', + errorMessage: '' + }, + errorMessage: '' + }; + + var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', null, null); + tablesNode.connection = connection; + tablesNode.session = objectExplorerSession; + var table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null); + var table2Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null); + tablesNode.children = [table1Node, table2Node]; + let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object); + objectExplorerService.callBase = true; + objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => tablesNode); + objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => TPromise.as([table1Node, table2Node])); + let builder: Builder = $().div(); + var dataSource = new ServerTreeDataSource(objectExplorerService.object, connectionManagementService.object, undefined); + let tree = TypeMoq.Mock.ofType(Tree, TypeMoq.MockBehavior.Loose, builder.getHTMLElement(), { dataSource }); + tree.callBase = true; + + tree.setup(x => x.refresh(TypeMoq.It.isAny())).returns(() => WinJS.TPromise.as(null)); + tree.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => WinJS.TPromise.as(null)); + let connectionAction: RefreshAction = new RefreshAction(RefreshAction.ID, + RefreshAction.LABEL, + tree.object, + connection, + connectionManagementService.object, + objectExplorerService.object, + undefined); + + connectionAction.run().then((value) => { + connectionManagementService.verify(x => x.isConnected(undefined, TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); + objectExplorerService.verify(x => x.getObjectExplorerNode(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0)); + objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(0)); + tree.verify(x => x.refresh(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0)); + tree.verify(x => x.expand(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0)); + }).then(() => done(), (err) => done(err)); + }); + +}); \ No newline at end of file diff --git a/src/sqltest/parts/connection/objectExplorerService.test.ts b/src/sqltest/parts/connection/objectExplorerService.test.ts new file mode 100644 index 0000000000..fb1083f894 --- /dev/null +++ b/src/sqltest/parts/connection/objectExplorerService.test.ts @@ -0,0 +1,450 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { ObjectExplorerProviderTestService } from 'sqltest/stubs/objectExplorerProviderTestService'; +import { TestConnectionManagementService } from 'sqltest/stubs/connectionManagementService.test'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; +import { NodeType } from 'sql/parts/registeredServer/common/nodeType'; +import { TreeNode } from 'sql/parts/registeredServer/common/treeNode'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as data from 'data'; +import * as TypeMoq from 'typemoq'; +import * as assert from 'assert'; + +suite('SQL Object Explorer Service tests', () => { + var sqlOEProvider: TypeMoq.Mock; + let connectionManagementService: TypeMoq.Mock; + let connection: ConnectionProfile; + let connectionToFail: ConnectionProfile; + let conProfGroup: ConnectionProfileGroup; + let objectExplorerService: ObjectExplorerService; + let objectExplorerSession: data.ObjectExplorerSession; + let objectExplorerFailedSession: data.ObjectExplorerSession; + let objectExplorerCloseSessionResponse: data.ObjectExplorerCloseSessionResponse; + let objectExplorerExpandInfo: data.ObjectExplorerExpandInfo; + let objectExplorerExpandInfoRefresh: data.ObjectExplorerExpandInfo; + let sessionId = '1234'; + let failedSessionId = '12345'; + let numberOfFailedSession: number = 0; + + setup(() => { + + let NodeInfoTable1 = { + nodePath: 'testServerName\tables\dbo.Table1', + nodeType: NodeType.Table, + label: 'dbo.Table1', + isLeaf: false, + metadata: null, + nodeSubType: '', + nodeStatus: '', + errorMessage: '' + }; + let NodeInfoTable2 = { + nodePath: 'testServerName\tables\dbo.Table2', + nodeType: NodeType.Table, + label: 'dbo.Table2', + isLeaf: false, + metadata: null, + nodeSubType: '', + nodeStatus: '', + errorMessage: '' + }; + + let NodeInfoTable3 = { + nodePath: 'testServerName\tables\dbo.Table3', + nodeType: NodeType.Table, + label: 'dbo.Table3', + isLeaf: false, + metadata: null, + nodeSubType: '', + nodeStatus: '', + errorMessage: '' + }; + + objectExplorerSession = { + success: true, + sessionId: sessionId, + rootNode: { + nodePath: 'testServerName\tables', + nodeType: NodeType.Folder, + label: 'Tables', + isLeaf: false, + metadata: null, + nodeSubType: '', + nodeStatus: '', + errorMessage: '' + }, + errorMessage: '' + }; + + objectExplorerFailedSession = { + success: false, + sessionId: failedSessionId, + rootNode: undefined, + errorMessage: 'Connection Failed' + }; + + objectExplorerCloseSessionResponse = { + success: true, + sessionId: sessionId, + }; + + objectExplorerExpandInfo = { + sessionId: sessionId, + nodes: [NodeInfoTable1, NodeInfoTable2], + errorMessage: '', + nodePath: objectExplorerSession.rootNode.nodePath + }; + + objectExplorerExpandInfoRefresh = { + sessionId: sessionId, + nodes: [NodeInfoTable1, NodeInfoTable3], + errorMessage: '', + nodePath: objectExplorerSession.rootNode.nodePath + }; + let response: data.ObjectExplorerSessionResponse = { + sessionId: objectExplorerSession.sessionId + }; + + let failedResponse: data.ObjectExplorerSessionResponse = { + sessionId: failedSessionId + }; + + sqlOEProvider = TypeMoq.Mock.ofType(ObjectExplorerProviderTestService, TypeMoq.MockBehavior.Loose); + sqlOEProvider.callBase = true; + + + let sqlProvider = { + protocolVersion: '1', + providerName: 'MSSQL', + providerDisplayName: 'MSSQL', + connectionProvider: { + options: [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 0, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 1, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 3, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 2, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 4, + valueType: 0 + }, + { + name: 'encrypt', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: false, + isRequired: false, + specialValueType: undefined, + valueType: 0 + }] + }, + adminServicesProvider: { databaseInfoOptions: [], databaseFileInfoOptions: [], fileGroupInfoOptions: [] }, + features: undefined + }; + + connection = new ConnectionProfile(sqlProvider, { + savePassword: false, + groupFullName: 'testGroup', + serverName: 'testServerName', + databaseName: 'testDatabaseName', + authenticationType: 'inetgrated', + password: 'test', + userName: 'testUsername', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: 'testID' + }); + conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined); + + connectionToFail = new ConnectionProfile(sqlProvider, { + savePassword: false, + groupFullName: 'testGroup', + serverName: 'testServerName2', + databaseName: 'testDatabaseName2', + authenticationType: 'inetgrated', + password: 'test', + userName: 'testUsername', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: 'testID2' + }); + conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined); + conProfGroup.connections = [connection]; + connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict); + connectionManagementService.setup(x => x.getConnectionGroups()).returns(() => [conProfGroup]); + connectionManagementService.setup(x => x.getActiveConnections()).returns(() => [connection]); + connectionManagementService.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(() => new Promise((resolve) => { + resolve(connection); + })); + + connectionManagementService.setup(x => x.getCapabilities('MSSQL')).returns(() => undefined); + + objectExplorerService = new ObjectExplorerService(connectionManagementService.object, undefined); + objectExplorerService.registerProvider('MSSQL', sqlOEProvider.object); + sqlOEProvider.setup(x => x.createNewSession(TypeMoq.It.is(x => x.options['serverName'] === connection.serverName))).returns(() => new Promise((resolve) => { + resolve(response); + })); + sqlOEProvider.setup(x => x.createNewSession(TypeMoq.It.is(x => x.options['serverName'] === connectionToFail.serverName))).returns(() => new Promise((resolve) => { + resolve(failedResponse); + })); + sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => { + objectExplorerService.onNodeExpanded(1, objectExplorerExpandInfo); + }).returns(() => TPromise.as(true)); + sqlOEProvider.setup(x => x.refreshNode(TypeMoq.It.isAny())).callback(() => { + objectExplorerService.onNodeExpanded(1, objectExplorerExpandInfoRefresh); + }).returns(() => TPromise.as(true)); + sqlOEProvider.setup(x => x.closeSession(TypeMoq.It.isAny())).returns(() => TPromise.as(objectExplorerCloseSessionResponse)); + + objectExplorerService.onUpdateObjectExplorerNodes(args => { + if (args && args.errorMessage !== undefined) { + numberOfFailedSession++; + } + }); + }); + + test('create new session should create session successfully', (done) => { + objectExplorerService.createNewSession('MSSQL', connection).then(session => { + assert.equal(session !== null || session !== undefined, true); + assert.equal(session.sessionId, '1234'); + objectExplorerService.onSessionCreated(1, objectExplorerSession); + let node = objectExplorerService.getObjectExplorerNode(connection); + assert.notEqual(node, undefined); + assert.equal(node.session.success, true); + done(); + }, err => { + // Must call done here so test indicates it's finished if errors occur + done(err); + }); + }); + + test('create new session should raise failed event for failed session', (done) => { + objectExplorerService.createNewSession('MSSQL', connectionToFail).then(session => { + assert.equal(session !== null || session !== undefined, true); + assert.equal(session.sessionId, failedSessionId); + let currentNumberOfFailedSession = numberOfFailedSession; + objectExplorerService.onSessionCreated(1, objectExplorerFailedSession); + let node = objectExplorerService.getObjectExplorerNode(connection); + assert.equal(node, undefined); + assert.equal(currentNumberOfFailedSession + 1, numberOfFailedSession); + done(); + }, err => { + // Must call done here so test indicates it's finished if errors occur + done(err); + }); + }); + + test('close session should close session successfully', (done) => { + objectExplorerService.closeSession('MSSQL', objectExplorerSession).then(session => { + assert.equal(session !== null || session !== undefined, true); + assert.equal(session.success, true); + assert.equal(session.sessionId, '1234'); + done(); + }, err => { + // Must call done here so test indicates it's finished if errors occur + done(err); + }); + }); + + test('expand node should expand node correctly', (done) => { + objectExplorerService.createNewSession('MSSQL', connection).then(result => { + objectExplorerService.onSessionCreated(1, objectExplorerSession); + objectExplorerService.expandNode('MSSQL', objectExplorerSession, 'testServerName\tables').then(expandInfo => { + assert.equal(expandInfo !== null || expandInfo !== undefined, true); + assert.equal(expandInfo.sessionId, '1234'); + assert.equal(expandInfo.nodes.length, 2); + var children = expandInfo.nodes; + assert.equal(children[0].label, 'dbo.Table1'); + assert.equal(children[1].label, 'dbo.Table2'); + done(); + }, err => { + // Must call done here so test indicates it's finished if errors occur + done(err); + }); + }); + }); + + test('refresh node should refresh node correctly', (done) => { + objectExplorerService.createNewSession('MSSQL', connection).then(result => { + objectExplorerService.onSessionCreated(1, objectExplorerSession); + objectExplorerService.refreshNode('MSSQL', objectExplorerSession, 'testServerName\tables').then(expandInfo => { + assert.equal(expandInfo !== null || expandInfo !== undefined, true); + assert.equal(expandInfo.sessionId, '1234'); + assert.equal(expandInfo.nodes.length, 2); + var children = expandInfo.nodes; + assert.equal(children[0].label, 'dbo.Table1'); + assert.equal(children[1].label, 'dbo.Table3'); + done(); + }, err => { + // Must call done here so test indicates it's finished if errors occur + done(err); + }); + }); + }); + + test('expand tree node should children correctly', (done) => { + var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\tables', '', '', null, null); + tablesNode.connection = connection; + objectExplorerService.createNewSession('MSSQL', connection).then(result => { + objectExplorerService.onSessionCreated(1, objectExplorerSession); + objectExplorerService.expandTreeNode(objectExplorerSession, tablesNode).then(children => { + assert.equal(children !== null || children !== undefined, true); + assert.equal(children[0].label, 'dbo.Table1'); + assert.equal(children[0].parent, tablesNode); + assert.equal(children[0].nodePath, 'testServerName\tables\dbo.Table1'); + assert.equal(children[1].label, 'dbo.Table2'); + assert.equal(children[1].parent, tablesNode); + assert.equal(children[1].nodePath, 'testServerName\tables\dbo.Table2'); + done(); + }, err => { + // Must call done here so test indicates it's finished if errors occur + done(err); + }); + }); + }); + + test('refresh tree node should children correctly', (done) => { + var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\tables', '', '', null, null); + tablesNode.connection = connection; + objectExplorerService.createNewSession('MSSQL', connection).then(result => { + objectExplorerService.onSessionCreated(1, objectExplorerSession); + objectExplorerService.refreshTreeNode(objectExplorerSession, tablesNode).then(children => { + assert.equal(children !== null || children !== undefined, true); + assert.equal(children[0].label, 'dbo.Table1'); + assert.equal(children[0].parent, tablesNode); + assert.equal(children[0].nodePath, 'testServerName\tables\dbo.Table1'); + assert.equal(children[1].label, 'dbo.Table3'); + assert.equal(children[1].parent, tablesNode); + assert.equal(children[1].nodePath, 'testServerName\tables\dbo.Table3'); + done(); + }, err => { + // Must call done here so test indicates it's finished if errors occur + done(err); + }); + }); + }); + + test('update object explorer nodes should get active connection, create session, add to the active OE nodes successfully', (done) => { + objectExplorerService.createNewSession('MSSQL', connection).then(result => { + objectExplorerService.onSessionCreated(1, objectExplorerSession); + objectExplorerService.updateObjectExplorerNodes(connection).then(() => { + var treeNode = objectExplorerService.getObjectExplorerNode(connection); + assert.equal(treeNode !== null || treeNode !== undefined, true); + assert.equal(treeNode.getSession(), objectExplorerSession); + assert.equal(treeNode.getConnectionProfile(), connection); + assert.equal(treeNode.label, 'Tables'); + assert.equal(treeNode.nodePath, 'testServerName\tables'); + done(); + }, err => { + // Must call done here so test indicates it's finished if errors occur + done(err); + }); + }); + }); + + test('delete object explorerNode nodes should delete session, delete the root node to the active OE node', (done) => { + objectExplorerService.createNewSession('MSSQL', connection).then(result => { + objectExplorerService.onSessionCreated(1, objectExplorerSession); + objectExplorerService.updateObjectExplorerNodes(connection).then(() => { + var treeNode = objectExplorerService.getObjectExplorerNode(connection); + assert.equal(treeNode !== null && treeNode !== undefined, true); + objectExplorerService.deleteObjectExplorerNode(connection); + treeNode = objectExplorerService.getObjectExplorerNode(connection); + assert.equal(treeNode === null || treeNode === undefined, true); + done(); + }, err => { + // Must call done here so test indicates it's finished if errors occur + done(err); + }); + }); + }); + + test('children tree nodes should return correct object explorer session, connection profile and database name', () => { + var databaseMetaData = { + metadataType: 0, + metadataTypeName: 'Database', + urn: '//server/db1/', + name: 'Db1', + schema: null + }; + var databaseNode = new TreeNode(NodeType.Database, 'Db1', false, 'testServerName\Db1', '', '', null, databaseMetaData); + databaseNode.connection = connection; + databaseNode.session = objectExplorerSession; + var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', databaseNode, null); + databaseNode.children = [tablesNode]; + var table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\Db1\tables\dbo.Table1', '', '', tablesNode, null); + var table2Node = new TreeNode(NodeType.Table, 'dbo.Table2', false, 'testServerName\Db1\tables\dbo.Table2', '', '', tablesNode, null); + tablesNode.children = [table1Node, table2Node]; + assert.equal(table1Node.getSession(), objectExplorerSession); + assert.equal(table1Node.getConnectionProfile(), connection); + assert.equal(table1Node.getDatabaseName(), 'Db1'); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/connection/providerConnectionInfo.test.ts b/src/sqltest/parts/connection/providerConnectionInfo.test.ts new file mode 100644 index 0000000000..ac86b961bd --- /dev/null +++ b/src/sqltest/parts/connection/providerConnectionInfo.test.ts @@ -0,0 +1,248 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + + +import { ProviderConnectionInfo } from 'sql/parts/connection/common/providerConnectionInfo'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import data = require('data'); +import * as assert from 'assert'; + +suite('SQL ProviderConnectionInfo tests', () => { + let msSQLCapabilities: data.DataProtocolServerCapabilities; + + let connectionProfile: IConnectionProfile = { + serverName: 'new server', + databaseName: 'database', + userName: 'user', + password: 'password', + authenticationType: '', + savePassword: true, + groupFullName: 'g2/g2-2', + groupId: undefined, + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: undefined, + saveProfile: true, + id: undefined + }; + + setup(() => { + let capabilities: data.DataProtocolServerCapabilities[] = []; + let connectionProvider: data.ConnectionProviderOptions = { + options: [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 0, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 1, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 3, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 2, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 4, + valueType: 0 + }, + { + name: 'encrypt', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: false, + isRequired: false, + specialValueType: undefined, + valueType: 0 + } + ] + }; + msSQLCapabilities = { + protocolVersion: '1', + providerName: 'MSSQL', + providerDisplayName: 'MSSQL', + connectionProvider: connectionProvider, + adminServicesProvider: undefined, + features: undefined + }; + capabilities.push(msSQLCapabilities); + }); + + test('constructor should accept undefined parameters', () => { + let conn = new ProviderConnectionInfo(undefined, undefined); + assert.equal(conn.serverName, undefined); + }); + + test('set properties should set the values correctly', () => { + let conn = new ProviderConnectionInfo(msSQLCapabilities, undefined); + assert.equal(conn.serverName, undefined); + conn.serverName = connectionProfile.serverName; + conn.databaseName = connectionProfile.databaseName; + conn.authenticationType = connectionProfile.authenticationType; + conn.password = connectionProfile.password; + conn.userName = connectionProfile.userName; + assert.equal(conn.serverName, connectionProfile.serverName); + assert.equal(conn.databaseName, connectionProfile.databaseName); + assert.equal(conn.authenticationType, connectionProfile.authenticationType); + assert.equal(conn.password, connectionProfile.password); + assert.equal(conn.userName, connectionProfile.userName); + }); + + test('set properties should store the values in the options', () => { + let conn = new ProviderConnectionInfo(msSQLCapabilities, undefined); + assert.equal(conn.serverName, undefined); + conn.serverName = connectionProfile.serverName; + conn.databaseName = connectionProfile.databaseName; + conn.authenticationType = connectionProfile.authenticationType; + conn.password = connectionProfile.password; + conn.userName = connectionProfile.userName; + assert.equal(conn.getOptionValue('serverName'), connectionProfile.serverName); + assert.equal(conn.getOptionValue('databaseName'), connectionProfile.databaseName); + assert.equal(conn.getOptionValue('authenticationType'), connectionProfile.authenticationType); + assert.equal(conn.getOptionValue('password'), connectionProfile.password); + assert.equal(conn.getOptionValue('userName'), connectionProfile.userName); + }); + + test('constructor should initialize the options given a valid model', () => { + let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + + assert.equal(conn.serverName, connectionProfile.serverName); + assert.equal(conn.databaseName, connectionProfile.databaseName); + assert.equal(conn.authenticationType, connectionProfile.authenticationType); + assert.equal(conn.password, connectionProfile.password); + assert.equal(conn.userName, connectionProfile.userName); + }); + + test('clone should create a new instance that equals the old one', () => { + let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + + let conn2 = conn.clone(); + assert.equal(conn.serverName, conn2.serverName); + assert.equal(conn.databaseName, conn2.databaseName); + assert.equal(conn.authenticationType, conn2.authenticationType); + assert.equal(conn.password, conn2.password); + assert.equal(conn.userName, conn2.userName); + }); + + test('Changing the cloned object should not change the original one', () => { + let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + + let conn2 = conn.clone(); + conn2.serverName = conn.serverName + '1'; + assert.notEqual(conn.serverName, conn2.serverName); + }); + + test('constructor should initialize the options given a valid model with options', () => { + let options = {}; + options['encrypt'] = 'test value'; + let conn2 = Object.assign({}, connectionProfile, { options: options }); + let conn = new ProviderConnectionInfo(msSQLCapabilities, conn2); + + assert.equal(conn.serverName, conn2.serverName); + assert.equal(conn.databaseName, conn2.databaseName); + assert.equal(conn.authenticationType, conn2.authenticationType); + assert.equal(conn.password, conn2.password); + assert.equal(conn.userName, conn2.userName); + assert.equal(conn.options['encrypt'], 'test value'); + }); + + test('getOptionsKey should create a valid unique id', () => { + let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user'; + let id = conn.getOptionsKey(); + assert.equal(id, expectedId); + }); + + test('getOptionsKey should create different id for different server names', () => { + let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + let conn2 = new ProviderConnectionInfo(msSQLCapabilities, Object.assign({}, connectionProfile, { serverName: connectionProfile.serverName + '1' })); + + assert.notEqual(conn.getOptionsKey(), conn2.getOptionsKey()); + }); + + test('titleParts should return server, database and auth type as first items', () => { + let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + let titleParts = conn.titleParts; + assert.equal(titleParts.length, 4); + assert.equal(titleParts[0], connectionProfile.serverName); + assert.equal(titleParts[1], connectionProfile.databaseName); + assert.equal(titleParts[2], connectionProfile.authenticationType); + assert.equal(titleParts[3], connectionProfile.userName); + }); + + test('getProviderFromOptionsKey should return the provider name from the options key successfully', () => { + let optionsKey = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user'; + let expectedProviderId: string = 'MSSQL'; + let actual = ProviderConnectionInfo.getProviderFromOptionsKey(optionsKey); + + assert.equal(expectedProviderId, actual); + }); + + test('getProviderFromOptionsKey should return empty string give null', () => { + let optionsKey = undefined; + let expectedProviderId: string = ''; + let actual = ProviderConnectionInfo.getProviderFromOptionsKey(optionsKey); + + assert.equal(expectedProviderId, actual); + }); + + test('getProviderFromOptionsKey should return empty string give key without provider name', () => { + let optionsKey = 'providerName2:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user'; + let expectedProviderId: string = ''; + let actual = ProviderConnectionInfo.getProviderFromOptionsKey(optionsKey); + + assert.equal(expectedProviderId, actual); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/dashboard/widgets/explorerWidget.component.test.ts b/src/sqltest/parts/dashboard/widgets/explorerWidget.component.test.ts new file mode 100644 index 0000000000..7acbf8a90a --- /dev/null +++ b/src/sqltest/parts/dashboard/widgets/explorerWidget.component.test.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ObjectMetadataWrapper } from 'sql/parts/dashboard/widgets/explorer/explorerWidget.component'; +import { MetadataType } from 'sql/parts/connection/common/connectionManagement'; + +import * as assert from 'assert'; + +suite('Explorer Widget Tests', () => { + test('Sorting dashboard search objects works correctly', () => { + let testMetadata = ObjectMetadataWrapper.createFromObjectMetadata( + [ + { + metadataType: MetadataType.View, + metadataTypeName: undefined, + urn: undefined, + name: 'testView', + schema: undefined + }, + { + metadataType: MetadataType.Table, + metadataTypeName: undefined, + urn: undefined, + name: 'testTable', + schema: undefined + }, + { + metadataType: MetadataType.SProc, + metadataTypeName: undefined, + urn: undefined, + name: 'testSProc', + schema: undefined + }, + { + metadataType: MetadataType.Function, + metadataTypeName: undefined, + urn: undefined, + name: 'testFunction', + schema: undefined + }, + { + metadataType: MetadataType.View, + metadataTypeName: undefined, + urn: undefined, + name: 'firstView', + schema: undefined + } + ]); + + // If I sort the object metadata wrapper list using ExplorerWidget's sort function + let sortedMetadata = testMetadata.slice().sort(ObjectMetadataWrapper.sort); + + // Then the resulting list is sorted by type, with Table > View > Stored Procedures > Function, then by name + let expectedList = [testMetadata[1], testMetadata[4], testMetadata[0], testMetadata[2], testMetadata[3]]; + expectedList.forEach((expectedWrapper, index) => assert.equal(sortedMetadata[index], expectedWrapper)); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/dashboard/widgets/propertiesWidget.component.test.ts b/src/sqltest/parts/dashboard/widgets/propertiesWidget.component.test.ts new file mode 100644 index 0000000000..a21f7288b1 --- /dev/null +++ b/src/sqltest/parts/dashboard/widgets/propertiesWidget.component.test.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { ChangeDetectorRef } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +// of is not on Observable by default, need to import it +import 'rxjs/add/observable/of'; + +import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget'; +import { DashboardServiceInterface, SingleAdminService, SingleConnectionManagementService } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; +import { PropertiesWidgetComponent } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; + +import * as TypeMoq from 'typemoq'; +import * as assert from 'assert'; + +class TestChangeDetectorRef extends ChangeDetectorRef { + reattach(): void { + return; + } + checkNoChanges(): void { + return; + } + detectChanges(): void { + return; + } + detach(): void { + return; + } + markForCheck(): void { + return; + } +} + +suite('Dashboard Properties Widget Tests', () => { + test('Parses good config', (done) => { + let propertiesConfig = { + properties: [ + { + + displayName: 'Test', + value: 'testProperty' + } + ] + }; + + let serverInfo = { + isCloud: false, + testProperty: 'Test Property', + serverMajorVersion: undefined, + serverMinorVersion: undefined, + serverReleaseVersion: undefined, + engineEditionId: undefined, + serverVersion: undefined, + serverLevel: undefined, + serverEdition: undefined, + azureVersion: undefined, + osVersion: undefined, + }; + + let databaseInfo = { + options: { + testProperty: 'Test Property' + } + }; + + let widgetConfig: WidgetConfig = { + widget: { + 'properties-widget': propertiesConfig + }, + context: 'server', + provider: 'MSSQL', + edition: 0 + }; + + let dashboardService = TypeMoq.Mock.ofType(DashboardServiceInterface, TypeMoq.MockBehavior.Loose, [{}]); + + let singleAdminService = TypeMoq.Mock.ofType(SingleAdminService); + singleAdminService.setup(x => x.databaseInfo).returns(() => Observable.of(databaseInfo)); + + dashboardService.setup(x => x.adminService).returns(() => singleAdminService.object); + + let connectionManagementinfo = TypeMoq.Mock.ofType(ConnectionManagementInfo); + connectionManagementinfo.object.serverInfo = serverInfo; + + let singleConnectionService = TypeMoq.Mock.ofType(SingleConnectionManagementService); + singleConnectionService.setup(x => x.connectionInfo).returns(() => connectionManagementinfo.object); + + dashboardService.setup(x => x.connectionManagementService).returns(() => singleConnectionService.object); + + let consoleError = (message?: any, ...optionalParams: any[]): void => { + assert.fail('Called console Error unexpectedly'); + }; + + let testComponent = new PropertiesWidgetComponent(dashboardService.object, new TestChangeDetectorRef(), undefined, widgetConfig, consoleError); + + // because config parsing is done async we need to put our asserts on the thread stack + setTimeout(() => { + // because properties is private we need to do some work arounds to access it. + assert.equal((testComponent).properties.length, 1); + assert.equal((testComponent).properties[0].displayName, 'Test'); + assert.equal((testComponent).properties[0].value, 'Test Property'); + done(); + }); + // for some reason mocha thinks this test takes 26 seconds even though it doesn't, so it says this failed because it took longer than 2 seconds + }).timeout(30000); +}); diff --git a/src/sqltest/parts/disasterRecovery/restoreViewModel.test.ts b/src/sqltest/parts/disasterRecovery/restoreViewModel.test.ts new file mode 100644 index 0000000000..46a317cde9 --- /dev/null +++ b/src/sqltest/parts/disasterRecovery/restoreViewModel.test.ts @@ -0,0 +1,266 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import data = require('data'); +import * as assert from 'assert'; +import { ServiceOptionType } from 'sql/parts/connection/common/connectionManagement'; +import { RestoreViewModel } from 'sql/parts/disasterRecovery/restore/restoreViewModel'; + +suite('Restore Dialog view model tests', () => { + let option1String = 'option1'; + let option2Category = 'option2'; + let option3Boolean = 'option3'; + let options: { [name: string]: any }; + let stringServiceOption: data.ServiceOption; + let categoryServiceOption: data.ServiceOption; + let booleanServiceOption: data.ServiceOption; + + let viewModel: RestoreViewModel; + + let planDetails: { [key: string]: data.RestorePlanDetailInfo }; + let backupSets: data.DatabaseFileInfo[]; + + setup(() => { + options = {}; + planDetails = {}; + backupSets = []; + planDetails['lastBackupTaken'] = { + name: 'lastBackupTaken', + currentValue: '8/16/2017', + isReadOnly: true, + isVisible: true, + defaultValue: null + }; + + planDetails['targetDatabaseName'] = { + name: 'targetDatabaseName', + currentValue: 'db1', + isReadOnly: true, + isVisible: true, + defaultValue: 'db1' + }; + + planDetails['sourceDatabaseName'] = { + name: 'sourceDatabaseName', + currentValue: 'dbSource', + isReadOnly: true, + isVisible: true, + defaultValue: 'dbSource' + }; + + planDetails[option1String] = { + name: option1String, + currentValue: 'newOptionValue', + isReadOnly: true, + isVisible: true, + defaultValue: 'newDefault' + }; + + backupSets.push( + { + properties: [], + id: 'file1', + isSelected: false + }, + { + properties: [], + id: 'file2', + isSelected: true + }); + + stringServiceOption = { + name: option1String, + displayName: 'Option 1', + description: null, + groupName: null, + valueType: ServiceOptionType.string, + defaultValue: 'default', + objectType: null, + categoryValues: null, + isRequired: false, + isArray: false + }; + + categoryServiceOption = { + name: option2Category, + displayName: 'Option 2', + description: null, + groupName: null, + valueType: ServiceOptionType.category, + defaultValue: 'catagory1', + objectType: null, + categoryValues: [{ + displayName: 'Catagory 1', + name: 'catagory1' + }, + { + displayName: 'Catagory 2', + name: 'catagory2' + }], + isRequired: false, + isArray: false + }; + + booleanServiceOption = { + name: option3Boolean, + displayName: 'Option 3', + description: null, + groupName: null, + valueType: ServiceOptionType.boolean, + defaultValue: 'true', + objectType: null, + categoryValues: null, + isRequired: false, + isArray: false + }; + viewModel = new RestoreViewModel([booleanServiceOption, categoryServiceOption, stringServiceOption]); + }); + + test('get boolean option type should return correct display value', () => { + let falseStringOptionValue = 'False'; + assert.equal(false, viewModel.getDisplayValue(booleanServiceOption, falseStringOptionValue)); + + let falseBooleanOptionValue = false; + assert.equal(false, viewModel.getDisplayValue(booleanServiceOption, falseBooleanOptionValue)); + + let trueStringOptionValue = 'true'; + assert.equal(true, viewModel.getDisplayValue(booleanServiceOption, trueStringOptionValue)); + + let undefinedOptionValue = undefined; + assert.equal(false, viewModel.getDisplayValue(booleanServiceOption, undefinedOptionValue)); + }); + + test('get category option type should return correct display value', () => { + let categoryOptionValue = 'catagory2'; + assert.equal('Catagory 2', viewModel.getDisplayValue(categoryServiceOption, categoryOptionValue)); + + let undefinedOptionValue = undefined; + assert.equal('Catagory 1', viewModel.getDisplayValue(categoryServiceOption, undefinedOptionValue)); + }); + + test('get string option type should return correct display value', () => { + let stringOptionValue = 'string1'; + assert.equal(stringOptionValue, viewModel.getDisplayValue(stringServiceOption, stringOptionValue)); + + let undefinedOptionValue = undefined; + assert.equal('', viewModel.getDisplayValue(stringServiceOption, undefinedOptionValue)); + }); + + test('get option meta data should return the correct one', () => { + assert.equal(stringServiceOption, viewModel.getOptionMetadata(option1String)); + assert.equal(categoryServiceOption, viewModel.getOptionMetadata(option2Category)); + assert.equal(booleanServiceOption, viewModel.getOptionMetadata(option3Boolean)); + assert.equal(undefined, viewModel.getOptionMetadata('option4')); + }); + + test('get restore advanced option should return the only the options that have been changed and are different from the default value', () => { + viewModel.setOptionValue(option1String, 'default'); + viewModel.setOptionValue(option2Category, 'Catagory 2'); + viewModel.setOptionValue(option3Boolean, false); + options = {}; + viewModel.getRestoreAdvancedOptions(options); + assert.equal(undefined, options[option1String]); + assert.equal('catagory2', options[option2Category]); + assert.equal(false, options[option3Boolean]); + }); + + test('on restore plan response should update all options from restore plan response correctly', () => { + let restorePlanResponse: data.RestorePlanResponse = { + sessionId: '123', + backupSetsToRestore: backupSets, + canRestore: true, + errorMessage: null, + dbFiles: [], + databaseNamesFromBackupSets: ['dbSource', 'dbSource2'], + planDetails: planDetails + }; + + viewModel.onRestorePlanResponse(restorePlanResponse); + + // verify that source database, target databasem and last backup get set correctly + assert.equal('dbSource', viewModel.sourceDatabaseName); + assert.equal('db1', viewModel.targetDatabaseName); + assert.equal('8/16/2017', viewModel.lastBackupTaken); + + // verify that advanced options get set correctly + options = {}; + viewModel.getRestoreAdvancedOptions(options); + assert.equal('newOptionValue', options[option1String]); + + // verify that selected backup sets get set correctly + let selectedBackupSets = viewModel.selectedBackupSets; + assert.equal(1, selectedBackupSets.length); + assert.equal('file2', selectedBackupSets[0]); + }); + + + test('on reset restore options should reset all options', () => { + let restorePlanResponse: data.RestorePlanResponse = { + sessionId: '123', + backupSetsToRestore: backupSets, + canRestore: true, + errorMessage: null, + dbFiles: [], + databaseNamesFromBackupSets: ['dbSource', 'dbSource2'], + planDetails: planDetails + }; + viewModel.filePath = 'filepath1'; + viewModel.onRestorePlanResponse(restorePlanResponse); + + //reset restore options + viewModel.resetRestoreOptions('db2'); + + // verify that file path, source database, target databasem and last backup get set correctly + assert.equal('', viewModel.lastBackupTaken); + assert.equal('db2', viewModel.sourceDatabaseName); + assert.equal('db2', viewModel.targetDatabaseName); + assert.equal('', viewModel.lastBackupTaken); + assert.equal(0, viewModel.databaseList.length); + + // verify that advanced options get set correctly + options = {}; + viewModel.getRestoreAdvancedOptions(options); + assert.equal(undefined, options[option1String]); + + // verify that selected backup sets get set correctly + let selectedBackupSets = viewModel.selectedBackupSets; + assert.equal(undefined, selectedBackupSets); + }); + + test('update options with config info should update option correctly', () => { + let databaseList = ['db1', 'db2']; + let configInfo: { [key: string]: any } = {}; + configInfo['sourceDatabaseNamesWithBackupSets'] = databaseList; + configInfo[option1String] = 'option1 from config info'; + viewModel.updateOptionWithConfigInfo(configInfo); + assert.equal(3, viewModel.databaseList.length); + assert.equal('', viewModel.databaseList[0]); + assert.equal(databaseList[1], viewModel.databaseList[1]); + assert.equal(databaseList[2], viewModel.databaseList[2]); + assert.equal('option1 from config info', viewModel.getOptionValue(option1String)); + + // verify that the options from get restore advanced options doesn't contain option1String + options = {}; + viewModel.getRestoreAdvancedOptions(options); + assert.equal(undefined, options[option1String]); + }); + + test('on restore from changed should set readHeaderFromMedia and reset the source database names and selected database name correctly', () => { + viewModel.databaseList = ['', 'db1', 'db2']; + viewModel.sourceDatabaseName = 'sourceDatabase'; + viewModel.filePath = 'filepath'; + viewModel.readHeaderFromMedia = false; + viewModel.onRestoreFromChanged(true); + assert.equal(true, viewModel.readHeaderFromMedia); + assert.equal('', viewModel.sourceDatabaseName); + assert.equal('', viewModel.filePath); + + viewModel.sourceDatabaseName = 'sourceDatabase2'; + viewModel.onRestoreFromChanged(false); + assert.equal(false, viewModel.readHeaderFromMedia); + assert.equal('', viewModel.sourceDatabaseName); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/insights/insightsDialogController.test.ts b/src/sqltest/parts/insights/insightsDialogController.test.ts new file mode 100644 index 0000000000..ace6c38e64 --- /dev/null +++ b/src/sqltest/parts/insights/insightsDialogController.test.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { InsightsDialogController } from 'sql/parts/insights/node/insightsDialogController'; +import { InsightsDialogModel } from 'sql/parts/insights/common/insightsDialogModel'; +import QueryRunner from 'sql/parts/query/execution/queryRunner'; +import { ConnectionManagementService } from 'sql/parts/connection/common/connectionManagementService'; +import { IInsightsConfigDetails } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; + +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; + +import { IDbColumn, BatchSummary, QueryExecuteSubsetResult, ResultSetSubset } from 'data'; +import { EventEmitter } from 'events'; +import { equal } from 'assert'; +import { Mock, MockBehavior, It } from 'typemoq'; + +const testData: string[][] = [ + ['1', '2', '3', '4'], + ['5', '6', '7', '8'] +]; + +const testColumns: string[] = [ + 'col1', + 'col2' +]; + +suite('Insights Dialog Controller Tests', () => { + test('updates correctly with good input', done => { + + let model = new InsightsDialogModel(); + + let { runner, complete } = getPrimedQueryRunner(testData, testColumns); + + let instMoq = Mock.ofType(InstantiationService, MockBehavior.Strict); + instMoq.setup(x => x.createInstance(It.isValue(QueryRunner), It.isAny(), undefined)) + .returns(() => runner); + + let connMoq = Mock.ofType(ConnectionManagementService, MockBehavior.Strict, {}, {}); + connMoq.setup(x => x.connect(It.isAny(), It.isAny())) + .returns(() => Promise.resolve(undefined)); + + let controller = new InsightsDialogController( + model, + undefined, + undefined, + instMoq.object, + connMoq.object + ); + + let profile: IConnectionProfile = { + serverName: 'server', + databaseName: 'database', + userName: 'user', + password: '', + authenticationType: '', + savePassword: true, + groupFullName: '', + groupId: '', + getOptionsKey: () => '', + matches: undefined, + providerName: '', + saveProfile: true, + id: '', + options: {} + }; + + controller.update({ query: 'query' }, profile).then(() => { + // Once we update the controller, listen on when it changes the model and verify the data it + // puts in is correct + model.onDataChange(() => { + for (let i = 0; i < testData.length; i++) { + for (let j = 0; j < testData[i].length; j++) { + equal(testData[i][j], model.rows[i][j]); + } + } + done(); + }); + // Fake the query Runner telling the controller the query is complete + complete(); + }); + }); +}); + +interface IPrimedQueryRunner { + runner: QueryRunner; + complete: () => void; +} + +/** +* Returns a mock of query runner than will recreate what a query runner does to return data +*/ +function getPrimedQueryRunner(data: string[][], columns: string[]): IPrimedQueryRunner { + let eventEmitter = new EventEmitter(); + let querymock = Mock.ofType(QueryRunner, MockBehavior.Strict); + querymock.setup(x => x.eventEmitter).returns(x => eventEmitter); + querymock.setup(x => x.batchSets).returns(x => { + return >[ + { + id: 0, + resultSetSummaries: [ + { + columnInfo: >columns.map(c => { return { columnName: c }; }), + id: 0, + rowCount: data.length + } + ] + } + ]; + }); + + querymock.setup(x => x.getQueryRows(It.isAnyNumber(), It.isAnyNumber(), It.isAnyNumber(), It.isAnyNumber())) + .returns(x => Promise.resolve({ + resultSubset: { + rowCount: data.length, + rows: data.map(r => r.map(c => { return { displayValue: c }; })) + } + })); + + querymock.setup(x => x.runQuery(It.isAnyString())).returns(x => Promise.resolve()); + + let complete = () => { + eventEmitter.emit('complete'); + }; + + return { + runner: querymock.object, + complete + }; +} \ No newline at end of file diff --git a/src/sqltest/parts/insights/insightsDialogModel.test.ts b/src/sqltest/parts/insights/insightsDialogModel.test.ts new file mode 100644 index 0000000000..c3230710c1 --- /dev/null +++ b/src/sqltest/parts/insights/insightsDialogModel.test.ts @@ -0,0 +1,253 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IInsightsLabel, IInsightsConfigDetails } from 'sql/parts/dashboard/widgets/insights/interfaces'; +import { InsightsDialogModel } from 'sql/parts/insights/common/insightsDialogModel'; +import { isUndefinedOrNull } from 'vs/base/common/types'; + +import * as assert from 'assert'; + +suite('Insights Dialog Model Tests', () => { + test('does parse condition right', () => { + let insightsDialogModel = new InsightsDialogModel(); + + let label: IInsightsLabel = { + column: undefined, + state: [ + { + condition: { + if: 'always' + }, + color: 'green' + } + ] + } as IInsightsLabel; + insightsDialogModel.insight = { label } as IInsightsConfigDetails; + insightsDialogModel.rows = [ + ['label1', 'value1'], + ['label2', 'value2'], + ['label3', 'value3'] + ]; + let result = insightsDialogModel.getListResources(0, 1); + for (let resource of result) { + assert.equal(resource.stateColor, 'green', 'always Condition did not return val as expected'); + } + + label.state = [ + { + condition: { + if: 'equals', + equals: 'specific value' + }, + color: 'green' + } + ]; + + insightsDialogModel.insight = { label } as IInsightsConfigDetails; + insightsDialogModel.rows = [ + ['label1', 'specific value'], + ['label2', 'value2'], + ['label3', 'value3'] + ]; + result = insightsDialogModel.getListResources(0, 1); + assert.equal(result[0].stateColor, 'green', 'always Condition did not return val as expected'); + assert.equal(isUndefinedOrNull(result[1].stateColor), true, 'always Condition did not return val as expected'); + assert.equal(isUndefinedOrNull(result[2].stateColor), true, 'always Condition did not return val as expected'); + + label.state = [ + { + condition: { + if: 'equals', + equals: 'specific value' + }, + color: 'green' + }, + { + condition: { + if: 'equals', + equals: 'specific value2' + }, + color: 'red' + } + ]; + + insightsDialogModel.insight = { label } as IInsightsConfigDetails; + insightsDialogModel.rows = [ + ['label1', 'specific value'], + ['label2', 'specific value2'], + ['label3', 'value3'] + ]; + result = insightsDialogModel.getListResources(0, 1); + assert.equal(result[0].stateColor, 'green', 'always Condition did not return val as expected'); + assert.equal(result[1].stateColor, 'red', 'always Condition did not return val as expected'); + assert.equal(isUndefinedOrNull(result[2].stateColor), true, 'always Condition did not return val as expected'); + + label.state = [ + { + condition: { + if: 'greaterThan', + equals: '2' + }, + color: 'green' + }, + { + condition: { + if: 'equals', + equals: 'specific value2' + }, + color: 'red' + } + ]; + + insightsDialogModel.insight = { label } as IInsightsConfigDetails; + insightsDialogModel.rows = [ + ['label1', '3'], + ['label2', 'specific value2'], + ['label3', 'value3'] + ]; + result = insightsDialogModel.getListResources(0, 1); + assert.equal(result[0].stateColor, 'green', 'always Condition did not return val as expected'); + assert.equal(result[1].stateColor, 'red', 'always Condition did not return val as expected'); + assert.equal(isUndefinedOrNull(result[2].stateColor), true, 'always Condition did not return val as expected'); + + label.state = [ + { + condition: { + if: 'greaterThanOrEquals', + equals: '2' + }, + color: 'green' + }, + { + condition: { + if: 'equals', + equals: 'specific value2' + }, + color: 'red' + } + ]; + + insightsDialogModel.insight = { label } as IInsightsConfigDetails; + insightsDialogModel.rows = [ + ['label1', '2'], + ['label2', 'specific value2'], + ['label3', 'value3'] + ]; + result = insightsDialogModel.getListResources(0, 1); + assert.equal(result[0].stateColor, 'green', 'always Condition did not return val as expected'); + assert.equal(result[1].stateColor, 'red', 'always Condition did not return val as expected'); + assert.equal(isUndefinedOrNull(result[2].stateColor), true, 'always Condition did not return val as expected'); + + label.state = [ + { + condition: { + if: 'lessThan', + equals: '8' + }, + color: 'green' + }, + { + condition: { + if: 'equals', + equals: 'specific value2' + }, + color: 'red' + } + ]; + + insightsDialogModel.insight = { label } as IInsightsConfigDetails; + insightsDialogModel.rows = [ + ['label1', '5'], + ['label2', 'specific value2'], + ['label3', 'value3'] + ]; + result = insightsDialogModel.getListResources(0, 1); + assert.equal(result[0].stateColor, 'green', 'always Condition did not return val as expected'); + assert.equal(result[1].stateColor, 'red', 'always Condition did not return val as expected'); + assert.equal(isUndefinedOrNull(result[2].stateColor), true, 'always Condition did not return val as expected'); + + label.state = [ + { + condition: { + if: 'lessThanOrEquals', + equals: '8' + }, + color: 'green' + }, + { + condition: { + if: 'equals', + equals: 'specific value2' + }, + color: 'red' + } + ]; + + insightsDialogModel.insight = { label } as IInsightsConfigDetails; + insightsDialogModel.rows = [ + ['label1', '8'], + ['label2', 'specific value2'], + ['label3', 'value3'] + ]; + result = insightsDialogModel.getListResources(0, 1); + assert.equal(result[0].stateColor, 'green', 'always Condition did not return val as expected'); + assert.equal(result[1].stateColor, 'red', 'always Condition did not return val as expected'); + assert.equal(isUndefinedOrNull(result[2].stateColor), true, 'always Condition did not return val as expected'); + + label.state = [ + { + condition: { + if: 'notEquals', + equals: '9' + }, + color: 'green' + }, + { + condition: { + if: 'equals', + equals: 'specific value2' + }, + color: 'red' + } + ]; + + insightsDialogModel.insight = { label } as IInsightsConfigDetails; + insightsDialogModel.rows = [ + ['label1', '8'], + ['label2', '9'], + ['label3', 'value3'] + ]; + result = insightsDialogModel.getListResources(0, 1); + assert.equal(result[0].stateColor, 'green', 'always Condition did not return val as expected'); + assert.equal(isUndefinedOrNull(result[1].stateColor), true, 'always Condition did not return val as expected'); + assert.equal(result[2].stateColor, 'green', 'always Condition did not return val as expected'); + + label.state = [ + { + condition: { + if: 'notEquals', + equals: '9' + }, + color: 'green' + }, + { + condition: { + if: 'always' + }, + color: 'red' + } + ]; + + insightsDialogModel.insight = { label } as IInsightsConfigDetails; + insightsDialogModel.rows = [ + ['label1', '8'], + ['label2', 'specific value2'], + ['label3', 'value3'] + ]; + result = insightsDialogModel.getListResources(0, 1); + assert.equal(result[0].stateColor, 'green', 'always Condition did not return val as expected'); + assert.equal(result[1].stateColor, 'green', 'always Condition did not return val as expected'); + assert.equal(result[2].stateColor, 'green', 'always Condition did not return val as expected'); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/query/editor/queryActions.test.ts b/src/sqltest/parts/query/editor/queryActions.test.ts new file mode 100644 index 0000000000..4bb18b7a0d --- /dev/null +++ b/src/sqltest/parts/query/editor/queryActions.test.ts @@ -0,0 +1,567 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { Emitter } from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; + +import { ISelectionData } from 'data'; + +import { + IConnectionManagementService, + IConnectionParams, + INewConnectionParams, + ConnectionType, + RunQueryOnConnectionMode +} from 'sql/parts/connection/common/connectionManagement'; +import { ConnectionDialogService } from 'sql/parts/connection/connectionDialog/connectionDialogService'; +import { + RunQueryAction, CancelQueryAction, ListDatabasesActionItem, + DisconnectDatabaseAction, ConnectDatabaseAction, QueryTaskbarAction +} from 'sql/parts/query/execution/queryActions'; +import { QueryInput } from 'sql/parts/query/common/queryInput'; +import { QueryEditor } from 'sql/parts/query/editor/queryEditor'; +import { QueryModelService } from 'sql/parts/query/execution/queryModelService'; +import { ConnectionManagementService } from 'sql/parts/connection/common/connectionManagementService'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; + +import { TestThemeService } from 'sqltest/stubs/themeTestService'; + +import * as TypeMoq from 'typemoq'; +import * as assert from 'assert'; + +let none: void; + +suite('SQL QueryAction Tests', () => { + + let testUri: string = 'testURI'; + let editor: TypeMoq.Mock; + let calledRunQueryOnInput: boolean = undefined; + let testQueryInput: TypeMoq.Mock; + + setup(() => { + // Setup a reusable mock QueryInput + testQueryInput = TypeMoq.Mock.ofType(QueryInput, TypeMoq.MockBehavior.Strict); + testQueryInput.setup(x => x.uri).returns(() => testUri); + testQueryInput.setup(x => x.runQuery(undefined)).callback(() => { calledRunQueryOnInput = true; }); + + // Setup a reusable mock QueryEditor + editor = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Strict, undefined, new TestThemeService()); + editor.setup(x => x.connectedUri).returns(() => testUri); + editor.setup(x => x.currentQueryInput).returns(() => testQueryInput.object); + editor.setup(x => x.uri).returns(() => testUri); + + editor.setup(x => x.getSelection()).returns(() => undefined); + editor.setup(x => x.getSelection(false)).returns(() => undefined); + editor.setup(x => x.isSelectionEmpty()).returns(() => false); + }); + + test('setClass sets child CSS class correctly', (done) => { + // If I create a RunQueryAction + let queryAction: QueryTaskbarAction = new RunQueryAction(undefined, undefined, undefined); + + // "class should automatically get set to include the base class and the RunQueryAction class + let className = RunQueryAction.EnabledClass; + assert.equal(queryAction.class, className, 'CSS class not properly set'); + done(); + }); + + test('getConnectedQueryEditorUri returns connected URI only if connected', (done) => { + // ... Create assert variables + let isConnectedReturnValue: boolean = false; + + // ... Mock "isConnected in ConnectionManagementService + let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}); + connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnectedReturnValue); + + // ... Create an editor + let editor = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Loose, undefined, new TestThemeService()); + editor.setup(x => x.currentQueryInput).returns(() => testQueryInput.object); + + // If I create a QueryTaskbarAction and I pass a non-connected editor to _getConnectedQueryEditorUri + let queryAction: QueryTaskbarAction = new RunQueryAction(undefined, undefined, connectionManagementService.object); + let connected: boolean = queryAction.isConnected(editor.object); + + // I should get an unconnected state + assert(!connected, 'Non-connected editor should get back an undefined URI'); + + // If I run with a connected URI + isConnectedReturnValue = true; + connected = queryAction.isConnected(editor.object); + + // I should get a connected state + assert(connected, 'Connected editor should get back a non-undefined URI'); + done(); + }); + + test('RunQueryAction calls runQuery() only if URI is connected', (done) => { + // ... Create assert variables + let isConnected: boolean = undefined; + let connectionParams: INewConnectionParams = undefined; + let calledRunQuery: boolean = false; + let countCalledShowDialog: number = 0; + + // ... Mock "showDialog" ConnectionDialogService + let connectionDialogService = TypeMoq.Mock.ofType(ConnectionDialogService, TypeMoq.MockBehavior.Loose); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined)) + .callback((service: IConnectionManagementService, params: INewConnectionParams) => { + connectionParams = params; + countCalledShowDialog++; + }) + .returns(() => TPromise.as(none)); + + // ... Mock "isConnected" in ConnectionManagementService + let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, connectionDialogService.object); + connectionManagementService.callBase = true; + connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); + + // ... Mock QueryModelService + let queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose); + queryModelService.setup(x => x.runQuery(TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).callback(() => { + calledRunQuery = true; + }); + + // If I call run on RunQueryAction when I am not connected + let queryAction: RunQueryAction = new RunQueryAction(editor.object, queryModelService.object, connectionManagementService.object); + isConnected = false; + calledRunQueryOnInput = false; + queryAction.run(); + + // runQuery should not be run + assert.equal(calledRunQueryOnInput, false, 'run should not call runQuery'); + testQueryInput.verify(x => x.runQuery(undefined), TypeMoq.Times.never()); + + // and the conneciton dialog should open with the correct parameter details + assert.equal(countCalledShowDialog, 1, 'run should call showDialog'); + assert.equal(connectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); + assert.equal(connectionParams.runQueryOnCompletion, RunQueryOnConnectionMode.executeQuery, 'runQueryOnCompletion should be true`'); + assert.equal(connectionParams.input.uri, testUri, 'URI should be set to the test URI'); + assert.equal(connectionParams.input, editor.object.currentQueryInput, 'Editor should be set to the mock editor'); + + // If I call run on RunQueryAction when I am connected + isConnected = true; + queryAction.run(); + + //runQuery should be run, and the conneciton dialog should not open + assert.equal(calledRunQueryOnInput, true, 'run should call runQuery'); + testQueryInput.verify(x => x.runQuery(undefined), TypeMoq.Times.once()); + assert.equal(countCalledShowDialog, 1, 'run should not call showDialog'); + done(); + }); + + test('Queries are only run if the QueryEditor selection is not empty', (done) => { + // ... Create assert variables + let isSelectionEmpty: boolean = undefined; + let countCalledRunQuery: number = 0; + + // ... Mock "isSelectionEmpty" in QueryEditor + let queryInput: TypeMoq.Mock = TypeMoq.Mock.ofType(QueryInput, TypeMoq.MockBehavior.Strict); + queryInput = TypeMoq.Mock.ofType(QueryInput, TypeMoq.MockBehavior.Strict); + queryInput.setup(x => x.uri).returns(() => testUri); + queryInput.setup(x => x.runQuery(undefined)).callback(() => { + countCalledRunQuery++; + }); + let queryEditor: TypeMoq.Mock = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Strict, undefined, new TestThemeService()); + queryEditor.setup(x => x.currentQueryInput).returns(() => queryInput.object); + queryEditor.setup(x => x.getSelection()).returns(() => undefined); + queryEditor.setup(x => x.getSelection(false)).returns(() => undefined); + queryEditor.setup(x => x.isSelectionEmpty()).returns(() => isSelectionEmpty); + + // ... Mock "isConnected" in ConnectionManagementService + let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}); + connectionManagementService.callBase = true; + connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => true); + + // ... Mock QueryModelService + let queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose); + + // If I call run on RunQueryAction when I have a non empty selection + let queryAction: RunQueryAction = new RunQueryAction(queryEditor.object, queryModelService.object, connectionManagementService.object); + isSelectionEmpty = false; + queryAction.run(); + + //runQuery should be run + assert.equal(countCalledRunQuery, 1, 'runQuery should be called'); + + // If I call run on RunQueryAction when I have an empty selection + isSelectionEmpty = true; + queryAction.run(); + + //runQuery should not be run again + assert.equal(countCalledRunQuery, 1, 'runQuery should not be called again'); + done(); + }); + + test('ISelectionData is properly passed when queries are run', (done) => { + + /// Setup Test /// + + // ... Create assert variables + let isConnected: boolean = undefined; + let countCalledShowDialog: number = 0; + let countCalledRunQuery: number = 0; + let showDialogConnectionParams: INewConnectionParams = undefined; + let runQuerySelection: ISelectionData = undefined; + let selectionToReturnInGetSelection: ISelectionData = undefined; + let predefinedSelection: ISelectionData = { startLine: 1, startColumn: 2, endLine: 3, endColumn: 4 }; + + // ... Mock "showDialog" ConnectionDialogService + let connectionDialogService = TypeMoq.Mock.ofType(ConnectionDialogService, TypeMoq.MockBehavior.Loose); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined)) + .callback((service: IConnectionManagementService, params: INewConnectionParams) => { + showDialogConnectionParams = params; + countCalledShowDialog++; + }) + .returns(() => TPromise.as(none)); + + // ... Mock "getSelection" in QueryEditor + let queryInput = TypeMoq.Mock.ofType(QueryInput, TypeMoq.MockBehavior.Loose); + queryInput.setup(x => x.uri).returns(() => testUri); + queryInput.setup(x => x.runQuery(TypeMoq.It.isAny())).callback((selection: ISelectionData) => { + runQuerySelection = selection; + countCalledRunQuery++; + }); + queryInput.setup(x => x.runQuery(undefined)).callback((selection: ISelectionData) => { + runQuerySelection = selection; + countCalledRunQuery++; + }); + + // ... Mock "getSelection" in QueryEditor + let queryEditor: TypeMoq.Mock = TypeMoq.Mock.ofType(QueryEditor, TypeMoq.MockBehavior.Loose, undefined, new TestThemeService()); + queryEditor.setup(x => x.currentQueryInput).returns(() => queryInput.object); + queryEditor.setup(x => x.getSelection()).returns(() => { + return selectionToReturnInGetSelection; + }); + + // ... Mock "isConnected" in ConnectionManagementService + let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, connectionDialogService.object); + connectionManagementService.callBase = true; + connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); + + /// End Setup Test /// + + ////// If I call run on RunQueryAction while disconnected and with an undefined selection + let queryAction: RunQueryAction = new RunQueryAction(queryEditor.object, undefined, connectionManagementService.object); + isConnected = false; + selectionToReturnInGetSelection = undefined; + queryAction.run(); + + // The conneciton dialog should open with an undefined seleciton + assert.equal(countCalledShowDialog, 1, 'run should call showDialog'); + assert.equal(countCalledRunQuery, 0, 'run should not call runQuery'); + assert.equal(showDialogConnectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); + assert.equal(showDialogConnectionParams.querySelection, undefined, 'querySelection should be undefined'); + + ////// If I call run on RunQueryAction while disconnected and with a defined selection + isConnected = false; + selectionToReturnInGetSelection = predefinedSelection; + queryAction.run(); + + // The conneciton dialog should open with the correct seleciton + assert.equal(countCalledShowDialog, 2, 'run should call showDialog again'); + assert.equal(countCalledRunQuery, 0, 'run should not call runQuery'); + assert.equal(showDialogConnectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); + assert.notEqual(showDialogConnectionParams.querySelection, undefined, 'There should not be an undefined selection in runQuery'); + assert.equal(showDialogConnectionParams.querySelection.startLine, selectionToReturnInGetSelection.startLine, 'startLine should match'); + assert.equal(showDialogConnectionParams.querySelection.startColumn, selectionToReturnInGetSelection.startColumn, 'startColumn should match'); + assert.equal(showDialogConnectionParams.querySelection.endLine, selectionToReturnInGetSelection.endLine, 'endLine should match'); + assert.equal(showDialogConnectionParams.querySelection.endColumn, selectionToReturnInGetSelection.endColumn, 'endColumn should match'); + + ////// If I call run on RunQueryAction while connected and with an undefined selection + isConnected = true; + selectionToReturnInGetSelection = undefined; + queryAction.run(); + + // The query should run with an undefined selection + assert.equal(countCalledShowDialog, 2, 'run should not call showDialog'); + assert.equal(countCalledRunQuery, 1, 'run should call runQuery'); + assert.equal(runQuerySelection, undefined, 'There should be an undefined selection in runQuery'); + + ////// If I call run on RunQueryAction while connected and with a defined selection + isConnected = true; + selectionToReturnInGetSelection = predefinedSelection; + queryAction.run(); + + // The query should run with the given seleciton + assert.equal(countCalledShowDialog, 2, 'run should not call showDialog'); + assert.equal(countCalledRunQuery, 2, 'run should call runQuery again'); + assert.notEqual(runQuerySelection, undefined, 'There should not be an undefined selection in runQuery'); + assert.equal(runQuerySelection.startLine, selectionToReturnInGetSelection.startLine, 'startLine should match'); + assert.equal(runQuerySelection.startColumn, selectionToReturnInGetSelection.startColumn, 'startColumn should match'); + assert.equal(runQuerySelection.endLine, selectionToReturnInGetSelection.endLine, 'endLine should match'); + assert.equal(runQuerySelection.endColumn, selectionToReturnInGetSelection.endColumn, 'endColumn should match'); + + done(); + }); + + test('CancelQueryAction calls cancelQuery() only if URI is connected', (done) => { + // ... Create assert variables + let isConnected: boolean = undefined; + let calledCancelQuery: boolean = false; + + // ... Mock "isConnected" in ConnectionManagementService + let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}); + connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); + + // ... Mock QueryModelService + let queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose); + queryModelService.setup(x => x.cancelQuery(TypeMoq.It.isAny())).callback(() => { + calledCancelQuery = true; + }); + + // If I call run on CancelQueryAction when I am not connected + let queryAction: CancelQueryAction = new CancelQueryAction(editor.object, queryModelService.object, connectionManagementService.object); + isConnected = false; + queryAction.run(); + + // cancelQuery should not be run + assert.equal(calledCancelQuery, false, 'run should not call cancelQuery'); + + // If I call run on CancelQueryAction when I am connected + isConnected = true; + queryAction.run(); + + // cancelQuery should be run + assert.equal(calledCancelQuery, true, 'run should call cancelQuery'); + done(); + }); + + // We want to call disconnectEditor regardless of connection to be able to cancel in-progress connections + test('DisconnectDatabaseAction calls disconnectEditor regardless of URI being connected', (done) => { + // ... Create assert variables + let isConnected: boolean = undefined; + let countCalledDisconnectEditor: number = 0; + + // ... Mock "isConnected" and "disconnectEditor" in ConnectionManagementService + let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}); + connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); + connectionManagementService.setup(x => x.disconnectEditor(TypeMoq.It.isAny())).callback(() => { + countCalledDisconnectEditor++; + }); + + // If I call run on DisconnectDatabaseAction when I am not connected + let queryAction: DisconnectDatabaseAction = new DisconnectDatabaseAction(editor.object, connectionManagementService.object); + isConnected = false; + queryAction.run(); + + // disconnectEditor should be run + assert.equal(countCalledDisconnectEditor, 1, 'disconnectEditor should be called when URI is not connected'); + + // If I call run on DisconnectDatabaseAction when I am connected + isConnected = true; + queryAction.run(); + + // disconnectEditor should be run again + assert.equal(countCalledDisconnectEditor, 2, 'disconnectEditor should be called when URI is connected'); + done(); + }); + + test('ConnectDatabaseAction opens dialog regardless of URI connection state', (done) => { + // ... Create assert variables + let isConnected: boolean = undefined; + let connectionParams: INewConnectionParams = undefined; + let countCalledShowDialog: number = 0; + + // ... Mock "showDialog" ConnectionDialogService + let connectionDialogService = TypeMoq.Mock.ofType(ConnectionDialogService, TypeMoq.MockBehavior.Loose); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined)) + .callback((service: IConnectionManagementService, params: INewConnectionParams) => { + connectionParams = params; + countCalledShowDialog++; + }) + .returns(() => TPromise.as(none)); + + // ... Mock "isConnected" in ConnectionManagementService + let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, connectionDialogService.object); + connectionManagementService.callBase = true; + connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); + + // If I call run on ConnectDatabaseAction when I am not connected + let queryAction: ConnectDatabaseAction = new ConnectDatabaseAction(editor.object, false, connectionManagementService.object); + isConnected = false; + queryAction.run(); + + // The conneciton dialog should open with the correct parameter details + assert.equal(countCalledShowDialog, 1, 'run should call showDialog'); + assert.equal(connectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); + assert.equal(connectionParams.runQueryOnCompletion, false, 'runQueryOnCompletion should be false`'); + assert.equal(connectionParams.input.uri, testUri, 'URI should be set to the test URI'); + assert.equal(connectionParams.input, editor.object.currentQueryInput, 'Editor should be set to the mock editor'); + + // If I call run on ConnectDatabaseAction when I am connected + isConnected = true; + queryAction.run(); + + // The conneciton dialog should open again with the correct parameter details + assert.equal(countCalledShowDialog, 2, 'run should call showDialog'); + assert.equal(connectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); + assert.equal(connectionParams.runQueryOnCompletion, false, 'runQueryOnCompletion should be false`'); + assert.equal(connectionParams.input.uri, testUri, 'URI should be set to the test URI'); + assert.equal(connectionParams.input, editor.object.currentQueryInput, 'Editor should be set to the mock editor'); + done(); + }); + + test('ChangeConnectionAction connects regardless of URI being connected', (done) => { + // ... Create assert variables + let queryAction: ConnectDatabaseAction = undefined; + let isConnected: boolean = undefined; + let connectionParams: INewConnectionParams = undefined; + let calledShowDialog: number = 0; + + // ... Mock "showDialog" ConnectionDialogService + let connectionDialogService = TypeMoq.Mock.ofType(ConnectionDialogService, TypeMoq.MockBehavior.Loose); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined)) + .callback((service: IConnectionManagementService, params: INewConnectionParams) => { + calledShowDialog++; + connectionParams = params; + }) + .returns(() => TPromise.as(none)); + + // ... Mock "isConnected" in ConnectionManagementService + let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, connectionDialogService.object); + connectionManagementService.callBase = true; + connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); + + // If I call run on ChangeConnectionAction when I am not connected + queryAction = new ConnectDatabaseAction(editor.object, false, connectionManagementService.object); + isConnected = false; + queryAction.run(); + + // The conneciton dialog should open with the params set as below + assert.equal(calledShowDialog, 1, 'showDialog should be called when URI is connected'); + assert.equal(connectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); + assert.equal(connectionParams.runQueryOnCompletion, false, 'runQueryOnCompletion should be false`'); + assert.equal(connectionParams.input.uri, testUri, 'URI should be set to the test URI'); + assert.equal(connectionParams.input, editor.object.currentQueryInput, 'Editor should be set to the mock editor'); + // Then if I call run on ChangeConnectionAction when I am connected + isConnected = true; + queryAction.run(); + + // The conneciton dialog should open with the params set as below + assert.equal(calledShowDialog, 2, 'showDialog should be called when URI is connected'); + assert.equal(connectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor'); + assert.equal(connectionParams.runQueryOnCompletion, false, 'runQueryOnCompletion should be false`'); + assert.equal(connectionParams.input.uri, testUri, 'URI should be set to the test URI'); + assert.equal(connectionParams.input, editor.object.currentQueryInput, 'Editor should be set to the mock editor'); + done(); + }); + + test('ListDatabaseItem shows items as expected', (done) => { + // ... Create assert variables + let listItem: ListDatabasesActionItem = undefined; + let isConnected: boolean = undefined; + let databaseName: string = undefined; + + // ... Mock "isConnected" in ConnectionManagementService + let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}); + connectionManagementService.callBase = true; + connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); + connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => { + databaseName: databaseName + }); + + // If I query without having initialized anything, state should be clear + listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined); + + assert.equal(listItem.isEnabled(), false, 'do not expect dropdown enabled unless connected'); + assert.equal(listItem.currentDatabaseName, undefined, 'do not expect dropdown to have entries unless connected'); + + // When I connect, database name should be returned in the dropdown and this should be enabled + isConnected = true; + databaseName = 'master'; + listItem.onConnected(); + assert.equal(listItem.isEnabled(), true, 'expect dropdown enabled when connected'); + assert.equal(listItem.currentDatabaseName, 'master', 'expect dropdown to have current DB name when connected'); + + // When I disconnect, state should return to default + isConnected = false; + databaseName = undefined; + listItem.onDisconnect(); + assert.equal(listItem.isEnabled(), false, 'do not expect dropdown enabled unless connected'); + assert.equal(listItem.currentDatabaseName, undefined, 'do not expect dropdown to have entries unless connected'); + + done(); + }); + + test('ListDatabaseItem - null event params', () => { + // Setup: + // ... Create event emitter we can use to trigger db changed event + let dbChangedEmitter = new Emitter(); + + // ... Create mock connection management service + let databaseName = 'foobar'; + let cms = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}); + cms.callBase = true; + cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event); + cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => { databaseName: databaseName }); + + // ... Create a database dropdown that has been connected + let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null); + listItem.onConnected(); + + // If: I raise a connection changed event + let eventParams = null; + dbChangedEmitter.fire(eventParams); + + // Then: The selected database should not have changed + assert.equal(listItem.currentDatabaseName, databaseName); + }); + + test('ListDatabaseItem - wrong uri', () => { + // Setup: + // ... Create event emitter we can use to trigger db changed event + let dbChangedEmitter = new Emitter(); + + // ... Create mock connection management service that will not claim it's connected + let databaseName = 'foobar'; + let cms = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}); + cms.callBase = true; + cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event); + cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => { databaseName: databaseName }); + + // ... Create a database dropdown that has been connected + let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null); + listItem.onConnected(); + + // If: I raise a connection changed event for the 'wrong' URI + let eventParams = { + connectionProfile: { + databaseName: 'foobarbaz' + }, + connectionUri: 'foobarUri' + }; + dbChangedEmitter.fire(eventParams); + + // Then: The selected database should not have changed + assert.equal(listItem.currentDatabaseName, databaseName); + }); + + test('ListDatabaseItem - updates when connected and uri matches', () => { + // Setup: + // ... Create event emitter we can use to trigger db changed event + let dbChangedEmitter = new Emitter(); + + // ... Create mock connection management service + let cms = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}); + cms.callBase = true; + cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event); + + // ... Create a database dropdown + let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null); + + // If: I raise a connection changed event + let eventParams = { + connectionProfile: { + databaseName: 'foobarbaz' + }, + connectionUri: editor.object.uri + }; + dbChangedEmitter.fire(eventParams); + + // Then: + // ... The connection should have changed to the provided database + assert.equal(listItem.currentDatabaseName, eventParams.connectionProfile.databaseName); + }); +}); diff --git a/src/sqltest/parts/query/editor/queryEditor.test.ts b/src/sqltest/parts/query/editor/queryEditor.test.ts new file mode 100644 index 0000000000..dd26fede43 --- /dev/null +++ b/src/sqltest/parts/query/editor/queryEditor.test.ts @@ -0,0 +1,391 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { TestMessageService, TestEditorGroupService } from 'vs/workbench/test/workbenchTestServices'; +import { IEditorDescriptor, EditorInput } from 'vs/workbench/common/editor'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import * as DOM from 'vs/base/browser/dom'; +import { Memento } from 'vs/workbench/common/memento'; +import { Builder } from 'vs/base/browser/builder'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; + +import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; +import { QueryEditor } from 'sql/parts/query/editor/queryEditor'; +import { QueryModelService } from 'sql/parts/query/execution/queryModelService'; +import { QueryInput } from 'sql/parts/query/common/queryInput'; +import { INewConnectionParams, ConnectionType, RunQueryOnConnectionMode } from 'sql/parts/connection/common/connectionManagement'; +import { ConnectionManagementService } from 'sql/parts/connection/common/connectionManagementService'; +import { RunQueryAction, ListDatabasesActionItem } from 'sql/parts/query/execution/queryActions'; +import { EditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService'; + +import { TestThemeService } from 'sqltest/stubs/themeTestService'; + +import * as TypeMoq from 'typemoq'; +import * as assert from 'assert'; + +suite('SQL QueryEditor Tests', () => { + let queryModelService: QueryModelService; + let instantiationService: TypeMoq.Mock; + let themeService: TestThemeService = new TestThemeService(); + let messageService: TypeMoq.Mock; + let editorDescriptorService: TypeMoq.Mock; + let connectionManagementService: TypeMoq.Mock; + let memento: TypeMoq.Mock; + + let queryInput: QueryInput; + let queryInput2: QueryInput; + let parentBuilder: Builder; + let mockEditor: any; + + let getQueryEditor = function (): QueryEditor { + return new QueryEditor( + undefined, + themeService, + instantiationService.object, + undefined, + undefined, + undefined, + editorDescriptorService.object, + undefined, + undefined, + undefined, + undefined); + }; + + setup(() => { + // Setup DOM elements + let element = DOM.$('queryEditorParent'); + parentBuilder = new Builder(element); + + // Create object to mock the Editor classes + // QueryResultsEditor fails at runtime due to the way we are importing Angualar, + // so a {} mock is used here. This mock simply needs to have empty method stubs + // for all called runtime methods + mockEditor = { + _bootstrapAngular: function () { }, + setInput: function () { }, + createEditor: function () { }, + create: function () { }, + setVisible: function () { }, + layout: function () { }, + dispose: function () { } + }; + + // Mock InstantiationService to give us our mockEditor + instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { + return new TPromise((resolve) => resolve(mockEditor)); + }); + instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((input) => { + return new TPromise((resolve) => resolve(new RunQueryAction(undefined, undefined, undefined))); + }); + // Setup hook to capture calls to create the listDatabase action + instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => { + if (classDef.ID) { + if (classDef.ID === 'listDatabaseQueryActionItem') { + return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined); + } + } + // Default + return new RunQueryAction(undefined, undefined, undefined); + }); + + // Mock EditorDescriptorService to give us a mock editor description + let descriptor: IEditorDescriptor = { + getId: function (): string { return 'id'; }, + getName: function (): string { return 'name'; }, + describes: function (obj: any): boolean { return true; } + }; + editorDescriptorService = TypeMoq.Mock.ofType(EditorDescriptorService, TypeMoq.MockBehavior.Loose); + editorDescriptorService.setup(x => x.getEditor(TypeMoq.It.isAny())).returns(() => descriptor); + + // Create a QueryInput + let filePath = 'someFile.sql'; + let uri: URI = URI.parse(filePath); + let fileInput = new UntitledEditorInput(uri, false, '', '', '', instantiationService.object, undefined, undefined, undefined); + let queryResultsInput: QueryResultsInput = new QueryResultsInput(uri.fsPath); + queryInput = new QueryInput('first', 'first', fileInput, queryResultsInput, undefined, undefined, undefined, undefined); + + // Create a QueryInput to compare to the previous one + let filePath2 = 'someFile2.sql'; + let uri2: URI = URI.parse(filePath2); + let fileInput2 = new UntitledEditorInput(uri2, false, '', '', '', instantiationService.object, undefined, undefined, undefined); + let queryResultsInput2: QueryResultsInput = new QueryResultsInput(uri2.fsPath); + queryInput2 = new QueryInput('second', 'second', fileInput2, queryResultsInput2, undefined, undefined, undefined, undefined); + + // Mock IMessageService + messageService = TypeMoq.Mock.ofType(TestMessageService, TypeMoq.MockBehavior.Loose); + + // Mock ConnectionManagementService + memento = TypeMoq.Mock.ofType(Memento, TypeMoq.MockBehavior.Loose, ''); + memento.setup(x => x.getMemento(TypeMoq.It.isAny())).returns(() => void 0); + connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined); + connectionManagementService.callBase = true; + connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAny())).returns(() => false); + + // Create a QueryModelService + queryModelService = new QueryModelService(instantiationService.object, messageService.object); + }); + + test('createEditor creates only the taskbar', (done) => { + // If I call createEditor + let editor: QueryEditor = getQueryEditor(); + editor.createEditor(parentBuilder); + + // The taskbar should be created + assert.equal(!!editor.taskbar, true); + assert.equal(!!editor.taskbarContainer, true); + + // But Nothing else should be created + assert.equal(!!editor.getContainer(), false); + assert.equal(!!editor.sqlEditor, false); + assert.equal(!!editor.sqlEditorContainer, false); + assert.equal(!!editor.resultsEditor, false); + assert.equal(!!editor.resultsEditorContainer, false); + assert.equal(!!editor.sash, false); + assert.equal(!!editor._isResultsEditorVisible(), false); + done(); + }); + + test('setInput creates SQL components', (done) => { + let assertInput = function () { + // The taskbar SQL, and parent should be created + assert.equal(!!editor.taskbar, true); + assert.equal(!!editor.taskbarContainer, true); + assert.equal(!!editor.getContainer(), true); + assert.equal(!!editor.sqlEditor, true); + assert.equal(!!editor.sqlEditorContainer, true); + + // But the results componenets should not + assert.equal(!!editor.resultsEditor, false); + assert.equal(!!editor.resultsEditorContainer, false); + assert.equal(!!editor.sash, false); + assert.equal(!!editor._isResultsEditorVisible(), false); + }; + + // If I call create a QueryEditor + let editor: QueryEditor = getQueryEditor(); + editor.create(parentBuilder); + + return editor.setInput(queryInput) // Then I set the input + .then(assertInput) // Only the taskbar SQL, and parent should be created + .then(() => done(), (err) => done(err)); + }); + + test('showQueryResultsEditor creates all components and pins editor', (done) => { + // Mock EditorGroupService to get call count of pinEditor + let editorGroupService = TypeMoq.Mock.ofType(TestEditorGroupService, TypeMoq.MockBehavior.Loose); + + // Make the call to showQueryResultsEditor thenable + let showQueryResultsEditor = function () { + return editor._showQueryResultsEditor(); + }; + + // Make the asserts thenable + let assertInput = function () { + assert.equal(!!editor.taskbar, true); + assert.equal(!!editor.taskbarContainer, true); + assert.equal(!!editor.getContainer(), true); + assert.equal(!!editor.sqlEditor, true); + assert.equal(!!editor.sqlEditorContainer, true); + assert.equal(!!editor.resultsEditor, true); + assert.equal(!!editor.resultsEditorContainer, true); + assert.equal(!!editor.sash, true); + assert.equal(!!editor._isResultsEditorVisible(), true); + editorGroupService.verify(x => x.pinEditor(undefined, TypeMoq.It.isAny()), TypeMoq.Times.once()); + }; + + // If I create a QueryEditor + let editor: QueryEditor = new QueryEditor( + undefined, + themeService, + instantiationService.object, + undefined, + undefined, + undefined, + editorDescriptorService.object, + editorGroupService.object, + undefined, + undefined, + undefined, + undefined); + editor.create(parentBuilder); + + return editor.setInput(queryInput) // Then I set the input + .then(showQueryResultsEditor) // Then call showQueryResultsEditor + .then(assertInput) // Both editor windows should be created, and the editor should be pinned + .then(() => done(), (err) => done(err)); + }); + + test('Can switch between different input files', (done) => { + // Setup + let firstInput: EditorInput; + let firstContainer: HTMLElement; + let secondInput: EditorInput; + let secondContainer: HTMLElement; + const firstContainerId = 'firstContainerId'; + const secondContainerId = 'secondContainerId'; + + let recordFirstInput = function () { + let input = editor.input; + firstInput = input.sql; + firstContainer = editor.sqlEditorContainer; + firstContainer.id = firstContainerId; + }; + + let assertFirstInputIsSet = function () { + assert.notEqual(firstContainer.parentElement, undefined); + }; + + let setSecondInput = function () { + return editor.setInput(queryInput2); + }; + + let assertFirstInputIsRemoved = function () { + let input = editor.input; + secondInput = input.sql; + secondContainer = editor.sqlEditorContainer; + secondContainer.id = secondContainerId; + + // The inputs should not match + assert.notEqual(firstInput.getName(), secondInput.getName()); + assert.notEqual(firstContainer.id, secondContainer.id); + assert.equal(firstContainer.id, firstContainerId); + + // The first input should be disposed + assert.notEqual(firstContainer.parentElement, secondContainer.parentElement); + assert.equal(firstContainer.parentElement, undefined); + + // The second input should be added into the DOM + assert.notEqual(secondContainer.parentElement, undefined); + }; + + let setFirstInputAgain = function () { + return editor.setInput(queryInput); + }; + + let assertFirstInputIsAddedBack = function () { + let input = editor.input; + firstInput = input.sql; + firstContainer = editor.sqlEditorContainer; + + // The inputs should not match + assert.notEqual(firstInput.getName(), secondInput.getName()); + assert.notEqual(firstContainer.id, secondContainer.id); + assert.equal(secondContainer.id, secondContainerId); + + // The first input should be added into the DOM + assert.equal(secondContainer.parentElement, undefined); + + // The second input should be disposed + assert.notEqual(firstContainer.parentElement, secondContainer.parentElement); + assert.notEqual(firstContainer.parentElement, undefined); + }; + + // If I create a QueryEditor + let editor: QueryEditor = getQueryEditor(); + editor.create(parentBuilder); + + return editor.setInput(queryInput) // and I set the input + .then(recordFirstInput) // then I record what the input is + .then(assertFirstInputIsSet) // the input should be set + .then(setSecondInput) // then if I set the input to a new file + .then(assertFirstInputIsRemoved) // the inputs should not match, and the first input should be removed from the DOM + .then(setFirstInputAgain) // then if I set the input back to the original + .then(assertFirstInputIsAddedBack) // the inputs should not match, and the second input should be removed from the DOM + .then(() => done(), (err) => done(err)); + }); + + suite('Action Tests', () => { + let queryActionInstantiationService: TypeMoq.Mock; + let queryConnectionService: TypeMoq.Mock; + let queryModelService: TypeMoq.Mock; + let queryInput: QueryInput; + setup(() => { + + // Mock ConnectionManagementService but don't set connected state + memento = TypeMoq.Mock.ofType(Memento, TypeMoq.MockBehavior.Loose, ''); + memento.setup(x => x.getMemento(TypeMoq.It.isAny())).returns(() => void 0); + queryConnectionService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined); + queryConnectionService.callBase = true; + + // Mock InstantiationService to give us the actions + queryActionInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); + + queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { + return new TPromise((resolve) => resolve(mockEditor)); + }); + + queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((input) => { + // Default + return new RunQueryAction(undefined, undefined, undefined); + }); + + // Setup hook to capture calls to create the listDatabase action + queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((definition, editor, action, selectBox) => { + if (definition.ID === 'listDatabaseQueryActionItem') { + let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined); + return item; + } + // Default + return new RunQueryAction(undefined, undefined, undefined); + }); + + let fileInput = new UntitledEditorInput(URI.parse('testUri'), false, '', '', '', instantiationService.object, undefined, undefined, undefined); + queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose, undefined, undefined); + queryModelService.callBase = true; + queryInput = new QueryInput( + 'testUri', + '', + fileInput, + undefined, + undefined, + undefined, + queryModelService.object, + undefined + ); + }); + + test('Taskbar buttons are set correctly upon standard load', (done) => { + queryConnectionService.setup(x => x.isConnected(TypeMoq.It.isAny())).returns(() => false); + queryModelService.setup(x => x.isRunningQuery(TypeMoq.It.isAny())).returns(() => false); + // If I use the created QueryEditor with no changes since creation + // Buttons should be set as if disconnected + assert.equal(queryInput.runQueryEnabled, true, 'runQueryAction button should be enabled'); + assert.equal(queryInput.cancelQueryEnabled, false, 'cancelQueryAction button should not be enabled'); + assert.equal(queryInput.connectEnabled, true, 'connectDatabaseAction button should be enabled'); + assert.equal(queryInput.disconnectEnabled, false, 'disconnectDatabaseAction button should not be enabled'); + assert.equal(queryInput.changeConnectionEnabled, false, 'changeConnectionAction button should not be enabled'); + assert.equal(queryInput.listDatabasesConnected, false); + done(); + }); + + test('Taskbar buttons are set correctly upon connect', (done) => { + let params: INewConnectionParams = { connectionType: ConnectionType.editor, runQueryOnCompletion: RunQueryOnConnectionMode.none }; + queryInput.onConnectSuccess(params); + queryModelService.setup(x => x.isRunningQuery(TypeMoq.It.isAny())).returns(() => false); + assert.equal(queryInput.runQueryEnabled, true, 'runQueryAction button should be enabled'); + assert.equal(queryInput.cancelQueryEnabled, false, 'cancelQueryAction button should not be enabled'); + assert.equal(queryInput.connectEnabled, false, 'connectDatabaseAction button should not be enabled'); + assert.equal(queryInput.disconnectEnabled, true, 'disconnectDatabaseAction button should be enabled'); + assert.equal(queryInput.changeConnectionEnabled, true, 'changeConnectionAction button should be enabled'); + assert.equal(queryInput.listDatabasesConnected, true); + done(); + }); + test('Test that we attempt to dispose query when the queryInput is disposed', (done) => { + let queryResultsInput = new QueryResultsInput('testUri'); + queryInput['_results'] = queryResultsInput; + queryInput.dispose(); + queryModelService.verify(x => x.disposeQuery(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); + done(); + }); + }); +}); \ No newline at end of file diff --git a/src/sqltest/parts/registeredServer/viewlet/serverTreeView.test.ts b/src/sqltest/parts/registeredServer/viewlet/serverTreeView.test.ts new file mode 100644 index 0000000000..6e57f605bb --- /dev/null +++ b/src/sqltest/parts/registeredServer/viewlet/serverTreeView.test.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ServerTreeView } from 'sql/parts/registeredServer/viewlet/serverTreeView'; +import { ConnectionManagementService } from 'sql/parts/connection/common/connectionManagementService'; + +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; + +import * as TypeMoq from 'typemoq'; + +suite('ServerTreeView onAddConnectionProfile handler tests', () => { + + let serverTreeView: ServerTreeView; + let mockTree: TypeMoq.Mock; + let mockRefreshTreeMethod: TypeMoq.Mock; + + setup(() => { + let instantiationService = new TestInstantiationService(); + let mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Strict, {}, {}); + mockConnectionManagementService.setup(x => x.getConnectionGroups()).returns(x => []); + serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, undefined, undefined); + let tree = { + clearSelection() { }, + getSelection() { }, + select(selection) { }, + reveal(reveal) { } + }; + mockTree = TypeMoq.Mock.ofInstance(tree); + (serverTreeView as any)._tree = mockTree.object; + mockRefreshTreeMethod = TypeMoq.Mock.ofType(Function); + (serverTreeView as any).refreshTree = mockRefreshTreeMethod.object; + mockTree.setup(x => x.clearSelection()); + mockTree.setup(x => x.select(TypeMoq.It.isAny())); + }); + + function runAddConnectionProfileHandler(oldSelection, newProfile) { + mockTree.setup(x => x.getSelection()).returns(() => [oldSelection] || []); + (serverTreeView as any).handleAddConnectionProfile(newProfile); + } + + test('onAddConnectionProfile handler selects the new profile when no profile is already selected', () => { + let newProfile = { + id: 'test_connection' + }; + runAddConnectionProfileHandler(undefined, newProfile); + mockRefreshTreeMethod.verify(x => x(), TypeMoq.Times.once()); + mockTree.verify(x => x.clearSelection(), TypeMoq.Times.never()); + mockTree.verify(x => x.select(TypeMoq.It.is(profile => profile === newProfile)), TypeMoq.Times.once()); + }); + + test('onAddConnectionProfile handler selects the new profile when a different profile is already selected', () => { + let oldProfile = { + id: 'old_connection' + }; + let newProfile = { + id: 'test_connection' + }; + runAddConnectionProfileHandler(oldProfile, newProfile); + mockRefreshTreeMethod.verify(x => x(), TypeMoq.Times.once()); + mockTree.verify(x => x.clearSelection(), TypeMoq.Times.once()); + mockTree.verify(x => x.select(TypeMoq.It.is(profile => profile === newProfile)), TypeMoq.Times.once()); + }); + + test('onAddConnectionProfile handler does not clear the selection when the new profile is already selected', () => { + let selectionId = 'test_connection'; + let oldProfile = { + id: selectionId + }; + let newProfile = { + id: selectionId + }; + runAddConnectionProfileHandler(oldProfile, newProfile); + mockRefreshTreeMethod.verify(x => x(), TypeMoq.Times.once()); + mockTree.verify(x => x.clearSelection(), TypeMoq.Times.never()); + mockTree.verify(x => x.select(TypeMoq.It.isAny()), TypeMoq.Times.never()); + }); + + test('onAddConnectionProfile handler does not clear the previously selected profile if there is no new one', () => { + let oldProfile = { + id: 'test_connection' + }; + runAddConnectionProfileHandler(oldProfile, undefined); + mockRefreshTreeMethod.verify(x => x(), TypeMoq.Times.once()); + mockTree.verify(x => x.clearSelection(), TypeMoq.Times.never()); + mockTree.verify(x => x.select(TypeMoq.It.isAny()), TypeMoq.Times.never()); + }); +}); \ No newline at end of file diff --git a/src/sqltest/services/accountManagement/accountManagementService.test.ts b/src/sqltest/services/accountManagement/accountManagementService.test.ts new file mode 100644 index 0000000000..a511346001 --- /dev/null +++ b/src/sqltest/services/accountManagement/accountManagementService.test.ts @@ -0,0 +1,508 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as data from 'data'; +import * as TypeMoq from 'typemoq'; +import AccountStore from 'sql/services/accountManagement/accountStore'; +import { SqlOAuthTestService } from 'sqltest/stubs/sqlOauthServiceStub'; +import { AccountDialogController } from 'sql/parts/accountManagement/accountDialog/accountDialogController'; +import { AccountManagementService } from 'sql/services/accountManagement/accountManagementService'; +import { AccountAdditionResult, AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes'; +import { IAccountStore } from 'sql/services/accountManagement/interfaces'; +import { AccountProviderStub } from 'sqltest/stubs/accountManagementStubs'; +import { EventVerifierSingle } from 'sqltest/utils/eventVerifier'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; + +// SUITE CONSTANTS ///////////////////////////////////////////////////////// +const hasAccountProvider: data.AccountProviderMetadata = { + id: 'hasAccounts', + displayName: 'Provider with Accounts' +}; +const noAccountProvider: data.AccountProviderMetadata = { + id: 'noAccounts', + displayName: 'Provider without Accounts' +}; + +const account: data.Account = { + key: { + providerId: hasAccountProvider.id, + accountId: 'testAccount1' + }, + displayInfo: { + displayName: 'Test Account 1', + contextualLogo: {light: '', dark: ''}, + contextualDisplayName: 'Azure Account' + }, + isStale: false, + properties: {} +}; +const accountList: data.Account[] = [account]; + +suite('Account Management Service Tests:', () => { + test('Constructor', () => { + // If: I construct an account management service + let ams = getTestState().accountManagementService; + + // Then: + // ... It should be created successfully + // ... Events should be available to register + assert.ok(ams.addAccountProviderEvent); + assert.ok(ams.removeAccountProviderEvent); + assert.ok(ams.updateAccountListEvent); + }); + + test('Add account - provider exists, account does not exist', done => { + // Setup: + // ... Create account management service with a provider + let state = getTestState(); + let mockProvider = getMockAccountProvider(); + state.accountManagementService._providers[hasAccountProvider.id] = { + accounts: [], + provider: mockProvider.object, + metadata: hasAccountProvider + }; + + // ... Add add/update handler to the account store that says account was new + state.mockAccountStore.setup(x => x.addOrUpdate(TypeMoq.It.isValue(account))) + .returns(() => Promise.resolve({ + accountModified: false, + accountAdded: true, + changedAccount: account + })); + + // If: I ask to add an account + return state.accountManagementService.addAccount(hasAccountProvider.id) + .then(result => { + // Then: + // ... The provider should have been prompted + mockProvider.verify(x => x.prompt(), TypeMoq.Times.once()); + + // ... The account store should have added/updated + state.mockAccountStore.verify(x => x.addOrUpdate(TypeMoq.It.isValue(account)), TypeMoq.Times.once()); + + // ... The account list change should have been fired + state.eventVerifierUpdate.assertFiredWithVerify(param => { + assert.equal(param.providerId, hasAccountProvider.id); + assert.ok(Array.isArray(param.accountList)); + assert.equal(param.accountList.length, 1); + assert.equal(param.accountList[0], account); + }); + + // ... The returned account should be the new one + assert.equal(result, account); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Add account - provider exists, account exists', done => { + // Setup: + // ... Create account management service with a provider + let state = getTestState(); + let mockProvider = getMockAccountProvider(); + state.accountManagementService._providers[hasAccountProvider.id] = { + accounts: [account], + provider: mockProvider.object, + metadata: hasAccountProvider + }; + + // ... Add add/update handler to the account store that says account was not new + state.mockAccountStore.setup(x => x.addOrUpdate(TypeMoq.It.isValue(account))) + .returns(() => Promise.resolve({ + accountModified: true, + accountAdded: false, + changedAccount: account + })); + + // If: I ask to add an account + return state.accountManagementService.addAccount(hasAccountProvider.id) + .then(result => { + // Then: + // ... The provider should have been prompted + mockProvider.verify(x => x.prompt(), TypeMoq.Times.once()); + + // ... The account store should have added/updated + state.mockAccountStore.verify(x => x.addOrUpdate(TypeMoq.It.isValue(account)), TypeMoq.Times.once()); + + // ... The account list change should have been fired + state.eventVerifierUpdate.assertFiredWithVerify(param => { + assert.equal(param.providerId, hasAccountProvider.id); + assert.ok(Array.isArray(param.accountList)); + assert.equal(param.accountList.length, 1); + assert.equal(param.accountList[0], account); + }); + + // ... The returned account should be the new one + assert.equal(result, account); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Add account - provider doesn\'t exist', done => { + // Setup: Create account management service + let ams = getTestState().accountManagementService; + + // If: I add an account when the provider doesn't exist + // Then: It should be rejected + ams.addAccount('doesNotExist') + .then( + () => done('Promise resolved when it should have rejected'), + () => done() + ); + + }); + + test('Get account provider metadata - providers exist', done => { + // Setup: Create account management service with a provider + let state = getTestState(); + state.accountManagementService._providers[noAccountProvider.id] = { + accounts: [], + provider: null, // Doesn't matter + metadata: noAccountProvider + }; + + // If: I ask for all the account provider metadata + return state.accountManagementService.getAccountProviderMetadata() + .then(result => { + // Then: The list should have the one account provider in it + assert.ok(Array.isArray(result)); + assert.equal(result.length, 1); + assert.equal(result[0], noAccountProvider); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Get account provider metadata - no providers', done => { + // Setup: Create account management service + let ams = getTestState().accountManagementService; + + // If: I ask for account provider metadata when there isn't any providers + ams.getAccountProviderMetadata() + .then(result => { + // Then: The results should be an empty array + assert.ok(Array.isArray(result)); + assert.equal(result.length, 0); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Get accounts by provider - provider does not exist', done => { + // Setup: Create account management service + let ams = getTestState().accountManagementService; + + // If: I get accounts when the provider doesn't exist + // Then: It should be rejected + ams.getAccountsForProvider('doesNotExist') + .then( + () => done('Promise resolved when it should have rejected'), + () => done() + ); + }); + + test('Get accounts by provider - provider exists, no accounts', done => { + // Setup: Create account management service + let ams = getTestState().accountManagementService; + ams._providers[noAccountProvider.id] = { + accounts: [], + provider: null, // Doesn't matter + metadata: noAccountProvider + }; + + // If: I ask for the accounts for a provider with no accounts + ams.getAccountsForProvider(noAccountProvider.id) + .then(result => { + // Then: I should get back an empty array + assert.ok(Array.isArray(result)); + assert.equal(result.length, 0); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Get accounts by provider - provider exists, has accounts', done => { + // Setup: Create account management service + let ams = getTestState().accountManagementService; + ams._providers[hasAccountProvider.id] = { + accounts: [account], + provider: null, // Doesn't matter + metadata: hasAccountProvider + }; + + // If: I ask for the accounts for a provider with accounts + ams.getAccountsForProvider(hasAccountProvider.id) + .then(result => { + // Then: I should get back the list of accounts + assert.equal(result, accountList); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Remove account - account exists', done => { + // Setup: + // ... Create account management service and to fake removing an account that exists + let state = getTestState(); + state.mockAccountStore.setup(x => x.remove(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(true)); + + // ... Register a account provider with the management service + let mockProvider = TypeMoq.Mock.ofType(AccountProviderStub); + mockProvider.setup(x => x.clear(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + state.accountManagementService._providers[hasAccountProvider.id] = { + accounts: [account], + provider: mockProvider.object, + metadata: hasAccountProvider + }; + + // If: I remove an account that exists + state.accountManagementService.removeAccount(account.key) + .then( result => { + // Then: + // ... I should have gotten true back + assert.ok(result); + + // ... The account store should have had remove called + state.mockAccountStore.verify(x => x.remove(TypeMoq.It.isValue(account.key)), TypeMoq.Times.once()); + + // ... The provider should have had clear called + mockProvider.verify(x => x.clear(TypeMoq.It.isValue(account.key)), TypeMoq.Times.once()); + + // ... The updated account list event should have fired + state.eventVerifierUpdate.assertFiredWithVerify((params: UpdateAccountListEventParams) => { + assert.equal(params.providerId, hasAccountProvider.id); + assert.ok(Array.isArray(params.accountList)); + assert.equal(params.accountList.length, 0); + }); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Remove account - account doesn\'t exist', done => { + // Setup: + // ... Create account management service and to fake removing an account that doesn't exist + let state = getTestState(); + state.mockAccountStore.setup(x => x.remove(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(false)); + + // ... Register a account provider with the management service + let mockProvider = getMockAccountProvider(); + mockProvider.setup(x => x.clear(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + state.accountManagementService._providers[noAccountProvider.id] = { + accounts: [], + provider: mockProvider.object, + metadata: noAccountProvider + }; + + // If: I remove an account that doesn't exist + let accountKey = {providerId: noAccountProvider.id, accountId: 'foobar'}; + state.accountManagementService.removeAccount(accountKey) + .then(result => { + // Then: + // ... I should have gotten false back + assert.ok(!result); + + // ... The account store should have had remove called + state.mockAccountStore.verify(x => x.remove(TypeMoq.It.isValue(accountKey)), TypeMoq.Times.once()); + + // ... The provider should have had clear called + mockProvider.verify(x => x.clear(TypeMoq.It.isValue(accountKey)), TypeMoq.Times.once()); + + // ... The updated account list event should not have fired + state.eventVerifierUpdate.assertNotFired(); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Open account dialog - first call', done => { + // Setup: + // ... Create account management ervice + let state = getTestState(); + + // ... Add mocking for instantiating an account dialog controller + let mockDialogController = TypeMoq.Mock.ofType(AccountDialogController); + mockDialogController.setup(x => x.openAccountDialog()); + state.instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountDialogController))) + .returns(() => mockDialogController.object); + + // If: I open the account dialog when it doesn't exist + state.accountManagementService.openAccountListDialog() + .then(() => { + // Then: + // ... The instantiation service should have been called once + state.instantiationService.verify(x => x.createInstance(TypeMoq.It.isValue(AccountDialogController)), TypeMoq.Times.once()); + + // ... The dialog should have been opened + mockDialogController.verify(x => x.openAccountDialog(), TypeMoq.Times.once()); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Open account dialog - subsequent calls', done => { + // Setup: + // ... Create account management ervice + let state = getTestState(); + + // ... Add mocking for instantiating an account dialog controller + let mockDialogController = TypeMoq.Mock.ofType(AccountDialogController); + mockDialogController.setup(x => x.openAccountDialog()); + state.instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountDialogController))) + .returns(() => mockDialogController.object); + + // If: I open the account dialog for a second time + state.accountManagementService.openAccountListDialog() + .then(() => state.accountManagementService.openAccountListDialog()) + .then(() => { + // Then: + // ... The instantiation service should have only been called once + state.instantiationService.verify(x => x.createInstance(TypeMoq.It.isValue(AccountDialogController)), TypeMoq.Times.once()); + + // ... The dialog should have been opened twice + mockDialogController.verify(x => x.openAccountDialog(), TypeMoq.Times.exactly(2)); + }) + .then( + () => done(), + err => done(err) + ); + }); + + // test('Perform oauth - success', done => { + // TODO: implement this test properly once we remove direct IPC calls + // }); + + test('Register provider - success', done => { + // Setup: + // ... Create ams, account store that will accept account add/update + let mocks = getTestState(); + mocks.mockAccountStore.setup(x => x.addOrUpdate(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(undefined)); + + // ... Create mock account provider + let mockProvider = getMockAccountProvider(); + + // If: I register a new provider + mocks.accountManagementService.registerProvider(noAccountProvider, mockProvider.object) + .then(() => { + // Then: + // ... Account store should have been called to get dehydrated accounts + mocks.mockAccountStore.verify(x => x.getAccountsByProvider(TypeMoq.It.isValue(noAccountProvider.id)), TypeMoq.Times.once()); + + // ... The provider should have been initialized + mockProvider.verify(x => x.initialize(TypeMoq.It.isAny()), TypeMoq.Times.once()); + + // ... The provider added event should have fired + mocks.eventVerifierProviderAdded.assertFiredWithVerify((param: AccountProviderAddedEventParams) => { + assert.equal(param.addedProvider, noAccountProvider); + assert.ok(Array.isArray(param.initialAccounts)); + assert.equal(param.initialAccounts.length, 0); + }); + }) + .then( + () => done(), + err => done(err) + ); + }); + + test('Unregister provider - success', done => { + // Setup: + // ... Create ams + let mocks = getTestState(); + + // ... Register a provider to remove + let mockProvider = getMockAccountProvider(); + mocks.accountManagementService.registerProvider(noAccountProvider, mockProvider.object) + .then((success) => { + // If: I remove an account provider + mocks.accountManagementService.unregisterProvider(noAccountProvider); + + // Then: The provider removed event should have fired + mocks.eventVerifierProviderRemoved.assertFired(noAccountProvider); + }, error => { + }).then(() => done(), err => done(err)); + + }); +}); + +function getTestState(): AccountManagementState { + // Create mock account store + let mockAccountStore = TypeMoq.Mock.ofType(AccountStore); + mockAccountStore.setup(x => x.getAccountsByProvider(TypeMoq.It.isValue(noAccountProvider.id))) + .returns(() => Promise.resolve([])); + mockAccountStore.setup(x => x.getAccountsByProvider(TypeMoq.It.isValue(hasAccountProvider.id))) + .returns(() => Promise.resolve(accountList)); + + // Create instantiation service + let mockInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict); + mockInstantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountStore), TypeMoq.It.isAny())) + .returns(() => mockAccountStore.object); + + // Create mock memento + let mockMemento = {}; + + // Create the account management service + let ams = new AccountManagementService(mockMemento, mockInstantiationService.object, null, new SqlOAuthTestService()); + + // Wire up event handlers + let evUpdate = new EventVerifierSingle(); + let evAddProvider = new EventVerifierSingle(); + let evRemoveProvider = new EventVerifierSingle(); + ams.updateAccountListEvent(evUpdate.eventHandler); + ams.addAccountProviderEvent(evAddProvider.eventHandler); + ams.removeAccountProviderEvent(evRemoveProvider.eventHandler); + + // Create the account management service + return { + accountManagementService: ams, + instantiationService: mockInstantiationService, + mockAccountStore: mockAccountStore, + eventVerifierUpdate: evUpdate, + eventVerifierProviderAdded: evAddProvider, + eventVerifierProviderRemoved: evRemoveProvider + }; +} + +function getMockAccountProvider(): TypeMoq.Mock { + let mockProvider = TypeMoq.Mock.ofType(AccountProviderStub); + mockProvider.setup(x => x.clear(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + mockProvider.setup(x => x.initialize(TypeMoq.It.isAny())).returns(param => Promise.resolve(param)); + mockProvider.setup(x => x.prompt()).returns(() => Promise.resolve(account)); + + return mockProvider; +} + +interface AccountManagementState { + accountManagementService: AccountManagementService; + instantiationService: TypeMoq.Mock; + mockAccountStore: TypeMoq.Mock; + eventVerifierUpdate: EventVerifierSingle; + eventVerifierProviderAdded: EventVerifierSingle; + eventVerifierProviderRemoved: EventVerifierSingle; +} \ No newline at end of file diff --git a/src/sqltest/services/accountManagement/accountStore.test.ts b/src/sqltest/services/accountManagement/accountStore.test.ts new file mode 100644 index 0000000000..a43f80f9bf --- /dev/null +++ b/src/sqltest/services/accountManagement/accountStore.test.ts @@ -0,0 +1,442 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as data from 'data'; +import AccountStore from 'sql/services/accountManagement/accountStore'; +import {EventVerifierSingle} from 'sqltest/utils/eventVerifier'; + +suite('Account Store Tests', () => { + test('AddOrUpdate - Uninitialized memento', done => { + // Setup: Create account store w/o initialized memento + let memento = {}; + let as = new AccountStore(memento); + + // If: I add an account to the store + as.addOrUpdate(account1) + .then(result => { + // Then: + // ... I should have gotten back a result indicating the account was added + assert.ok(result.accountAdded); + assert.ok(!result.accountModified); + assertAccountEqual(result.changedAccount, account1); + + // ... The memento should have been initialized and account added + assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY])); + assert.equal(memento[AccountStore.MEMENTO_KEY].length, 1); + assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account1); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('AddOrUpdate - Adds to accounts', done => { + // Setup: Create account store with initialized memento with accounts + let memento = {}; + memento[AccountStore.MEMENTO_KEY] = []; + let as = new AccountStore(memento); + + // If: I add an account to the store + as.addOrUpdate(account1) + .then(result => { + // Then: + // ... I should have gotten back a result indicating the account was added + assert.ok(result.accountAdded); + assert.ok(!result.accountModified); + assertAccountEqual(result.changedAccount, account1); + + // ... The memento should have the account added + assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY])); + assert.equal(memento[AccountStore.MEMENTO_KEY].length, 1); + assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account1); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('AddOrUpdate - Updates account', done => { + // Setup: Create account store with initialized memento with accounts + let memento = getTestMemento(); + let as = new AccountStore(memento); + + // If: I add an account to the store that already exists + let param = { + key: account2.key, + displayInfo: account1.displayInfo, + isStale: account1.isStale + }; + as.addOrUpdate(param) + .then(result => { + // Then: + // ... I should have gotten back a result indicating the account was updated + assert.ok(result.accountModified); + assert.ok(!result.accountAdded); + assertAccountEqual(result.changedAccount, param); + + // ... The memento should have been initialized and account updated + assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY])); + assert.equal(memento[AccountStore.MEMENTO_KEY].length, 2); + assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account1); + assertAccountEqual(memento[AccountStore.MEMENTO_KEY][1], param); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('GetAccountsByProvider - Uninitialized memento', done => { + // Setup: Create account store w/o initialized memento + let memento = {}; + let as = new AccountStore(memento); + + // If: I get accounts by provider + as.getAccountsByProvider('azure') + .then(result => { + // Then: + // ... I should get back an empty array + assert.ok(Array.isArray(result)); + assert.equal(result.length, 0); + + // ... Memento should not have been written + assert.equal(Object.keys(memento).length, 0); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('GetAccountsByProvider - No accounts', done => { + // Setup: Create account store with initialized memento with accounts + let memento = {}; + memento[AccountStore.MEMENTO_KEY] = []; + let as = new AccountStore(memento); + + // If: I get accounts when there aren't any accounts + as.getAccountsByProvider('azure') + .then(result => { + // Then: I should get back an empty array + assert.ok(Array.isArray(result)); + assert.equal(result.length, 0); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('GetAccountsByProvider - Accounts, but no accounts for provider', done => { + // Setup: Create account store with initialized memento with accounts + let memento = getTestMemento(); + let as = new AccountStore(memento); + + // If: I get accounts by provider that doesn't have accounts + as.getAccountsByProvider('cloudycloud') + .then(result => { + // Then: I should get back an empty array + assert.ok(Array.isArray(result)); + assert.equal(result.length, 0); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('GetAccountsByProvider - Accounts for provider', done => { + // Setup: Create account store with initialized memento with accounts + let memento = getTestMemento(); + let as = new AccountStore(memento); + + // If: I get accounts by provider that has accounts + as.getAccountsByProvider('azure') + .then(result => { + // Then: I should get the accounts + assert.ok(Array.isArray(result)); + assert.equal(result.length, 2); + assertAccountEqual(result[0], memento[AccountStore.MEMENTO_KEY][0]); + assertAccountEqual(result[1], memento[AccountStore.MEMENTO_KEY][1]); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('GetAllAccounts - Uninitialized memento', done => { + // Setup: Create account store w/o initialized memento + let memento = {}; + let as = new AccountStore(memento); + + // If: I get accounts + as.getAllAccounts() + .then(result => { + // Then: + // ... I should get back an empty array + assert.ok(Array.isArray(result)); + assert.equal(result.length, 0); + + // ... Memento should not have been written + assert.equal(Object.keys(memento).length, 0); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('GetAllAccounts - No accounts', done => { + // Setup: Create account store with initialized memento with accounts + let memento = {}; + memento[AccountStore.MEMENTO_KEY] = []; + let as = new AccountStore(memento); + + // If: I get accounts when there aren't any accounts + as.getAllAccounts() + .then(result => { + // Then: I should get back an empty array + assert.ok(Array.isArray(result)); + assert.equal(result.length, 0); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('GetAllAccounts - Accounts', done => { + // Setup: Create account store with initialized memento with accounts + let memento = getTestMemento(); + let as = new AccountStore(memento); + + // If: I get accounts + as.getAllAccounts() + .then(result => { + // Then: I should get the accounts + assert.ok(Array.isArray(result)); + assert.equal(result.length, 2); + assertAccountEqual(result[0], memento[AccountStore.MEMENTO_KEY][0]); + assertAccountEqual(result[1], memento[AccountStore.MEMENTO_KEY][1]); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('Remove - Uninitialized menento', done => { + // Setup: Create account store w/o initialized memento + let memento = {}; + let as = new AccountStore(memento); + + // If: I remove an account when there's an uninitialized memento + as.remove(account1.key) + .then(result => { + // Then: + // ... I should get back false (no account removed) + assert.ok(!result); + + // ... The memento should have been initialized + assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY])); + assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('Remove - Account does not exist', done => { + // Setup: Create account store with initialized memento with accounts + let memento = {}; + memento[AccountStore.MEMENTO_KEY] = []; + let as = new AccountStore(memento); + + // If: I remove an account that doesn't exist + as.remove({providerId: 'cloudyCloud', accountId: 'testyTest'}) + .then(result => { + // Then: + // ... I should get back false (no account removed) + assert.ok(!result); + + // ... The memento should still be empty + assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY])); + assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('Remove - Account exists', done => { + // Setup: Create account store with initialized memento with accounts + let memento = getTestMemento(); + let as = new AccountStore(memento); + + // If: I remove an account that does exist + as.remove(account1.key) + .then(result => { + // Then: + // ... I should get back true (account removed) + assert.ok(result); + + // ... The memento should have removed the first account + assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY])); + assert.equal(memento[AccountStore.MEMENTO_KEY].length, 1); + assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account2); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('Update - Uninitialized menento', done => { + // Setup: + // ... Create account store w/o initialized memento + let memento = {}; + let as = new AccountStore(memento); + + // ... Create a callback that we can verify was called + let updateCallback = new EventVerifierSingle(); + + // If: I update an account + as.update(account1.key, updateCallback.eventHandler) + .then(result => { + // Then: + // ... I should get back false (account did not change) + assert.ok(!result); + + // ... The memento should have been initialized + assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY])); + assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0); + + // ... The callback shouldn't have been called + updateCallback.assertNotFired(); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('Update - Account does not exist', done => { + // Setup: Create account store with initialized memento with accounts + let memento = {}; + memento[AccountStore.MEMENTO_KEY] = []; + let as = new AccountStore(memento); + + // ... Create a callback that we can verify was called + let updateCallback = new EventVerifierSingle(); + + // If: I update an account that doesn't exist + as.update({accountId: 'testyTest', providerId: 'cloudyCloud'}, updateCallback.eventHandler) + .then(result => { + // Then: + // ... I should get back false (account did not change) + assert.ok(!result); + + // ... The memento should still be empty + assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY])); + assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0); + + // ... The callback shouldn't have been called + updateCallback.assertNotFired(); + }) + .then( + () => done(), + e => done(e) + ); + }); + + test('Update - Account exists', done => { + // Setup: Create account store with initialized memento with accounts + let memento = getTestMemento(); + let as = new AccountStore(memento); + + // ... Create a callback to update the account + let newDisplayName = 'Display Name Changed!'; + let updateCallback = (arg: data.Account) => { + arg.displayInfo.displayName = newDisplayName; + }; + + // If: I update an account that exists + as.update(account1.key, updateCallback) + .then(result => { + // Then: + // ... I should get back true (account did change) + assert.ok(result); + + // ... The memento still contains two accounts + assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY])); + assert.equal(memento[AccountStore.MEMENTO_KEY].length, 2); + + // ... Account 1 should have been updated + assert.equal(memento[AccountStore.MEMENTO_KEY][0].displayInfo.displayName, newDisplayName); + + // ... Account 2 should have stayed the same + assertAccountEqual(memento[AccountStore.MEMENTO_KEY][1], account2); + }) + .then( + () => done(), + e => done(e) + ); + }); + + // TODO: Test to make sure operations occur sequentially +}); + +// TODO: Reinstate contextual logo once UI changes are checked in +const account1 = { + key: { + providerId: 'azure', + accountId: 'testAccount1' + }, + displayInfo: { + displayName: 'Test Account 1', + //contextualLogo: {light: '', dark: ''}, + contextualDisplayName: 'Azure Account' + }, + isStale: false +}; + +const account2 = { + key: { + providerId: 'azure', + accountId: 'testAccount2' + }, + displayInfo: { + displayName: 'Test Account 2', + //contextualLogo: {light: '', dark: ''}, + contextualDisplayName: 'Azure Account' + }, + isStale: false +}; + +function getTestMemento() { + let memento = {}; + memento[AccountStore.MEMENTO_KEY] = [account1, account2]; + + return memento; +} + +function assertAccountEqual(a: data.Account, b: data.Account) { + assert.equal(a.key.providerId, b.key.providerId); + assert.equal(a.key.accountId, b.key.accountId); + + assert.equal(a.displayInfo.contextualDisplayName, b.displayInfo.contextualDisplayName); + //assert.equal(a.displayInfo.contextualLogo, b.displayInfo.contextualLogo); + assert.equal(a.displayInfo.displayName, b.displayInfo.displayName); + + assert.equal(a.isStale, b.isStale); +} diff --git a/src/sqltest/stubs/accountManagementStubs.ts b/src/sqltest/stubs/accountManagementStubs.ts new file mode 100644 index 0000000000..b9034fdc36 --- /dev/null +++ b/src/sqltest/stubs/accountManagementStubs.ts @@ -0,0 +1,82 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import Event from 'vs/base/common/event'; +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; +import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes'; +import { TPromise } from 'vs/base/common/winjs.base'; + +export class AccountManagementTestService implements IAccountManagementService { + _serviceBrand: any; + + public get addAccountProviderEvent(): Event {return () => {return undefined;};} + public get removeAccountProviderEvent(): Event {return () => {return undefined;};} + public get updateAccountListEvent(): Event {return () => {return undefined;};} + + addAccount(providerId: string): Thenable { + return undefined; + } + + getAccountProviderMetadata(): Thenable { + return undefined; + } + + getAccountsForProvider(providerId: string): Thenable { + return undefined; + } + + getSecurityToken(account: data.Account): Thenable<{}> { + return undefined; + } + + removeAccount(accountKey: data.AccountKey): Thenable { + return undefined; + } + + openAccountListDialog(): TPromise { + return undefined; + } + + performOAuthAuthorization(url: string, silent: boolean): Thenable { + return undefined; + } + + registerProvider(providerMetadata: data.AccountProviderMetadata, provider: data.AccountProvider): void { + return undefined; + } + + shutdown(): void { + return undefined; + } + + unregisterProvider(providerMetadata: data.AccountProviderMetadata): void { + return undefined; + } +} + +export class AccountProviderStub implements data.AccountProvider { + clear(account: data.AccountKey): Thenable { + return Promise.resolve(); + } + + getSecurityToken(account: data.Account): Thenable<{}> { + return Promise.resolve({}); + } + + initialize(storedAccounts: data.Account[]): Thenable { + return Promise.resolve(storedAccounts); + } + + prompt(): Thenable { + return Promise.resolve(undefined); + } + + refresh(account: data.Account): Thenable { + return Promise.resolve(account); + } +} diff --git a/src/sqltest/stubs/capabilitiesTestService.ts b/src/sqltest/stubs/capabilitiesTestService.ts new file mode 100644 index 0000000000..08957319ac --- /dev/null +++ b/src/sqltest/stubs/capabilitiesTestService.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import data = require('data'); +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import Event from 'vs/base/common/event'; +import { Action } from 'vs/base/common/actions'; + + +export class CapabilitiesTestService implements ICapabilitiesService { + + public _serviceBrand: any; + + private _providers: data.CapabilitiesProvider[] = []; + + private _capabilities: data.DataProtocolServerCapabilities[] = []; + + + constructor() { + + let connectionProvider: data.ConnectionProviderOptions = { + options: [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 0, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 1, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 3, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 2, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: 4, + valueType: 0 + } + ] + }; + let msSQLCapabilities = { + protocolVersion: '1', + providerName: 'MSSQL', + providerDisplayName: 'MSSQL', + connectionProvider: connectionProvider, + adminServicesProvider: undefined, + features: undefined + }; + this._capabilities.push(msSQLCapabilities); + + } + + /** + * Retrieve a list of registered server capabilities + */ + public getCapabilities(): data.DataProtocolServerCapabilities[] { + return this._capabilities; + } + + /** + * Register the capabilities provider and query the provider for its capabilities + * @param provider + */ + public registerProvider(provider: data.CapabilitiesProvider): void { + } + + // Event Emitters + public get onProviderRegisteredEvent(): Event { + return undefined; + } + + public isFeatureAvailable(featureName: Action, connectionManagementInfo: ConnectionManagementInfo): boolean { + return true; + } + + public onCapabilitiesReady(): Promise { + return Promise.resolve(null); + } +} + diff --git a/src/sqltest/stubs/connectionDialogTestService.ts b/src/sqltest/stubs/connectionDialogTestService.ts new file mode 100644 index 0000000000..169f4266f7 --- /dev/null +++ b/src/sqltest/stubs/connectionDialogTestService.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { + IConnectionDialogService, IConnectionManagementService, INewConnectionParams +} from 'sql/parts/connection/common/connectionManagement'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; + +export class ConnectionDialogTestService implements IConnectionDialogService { + _serviceBrand: any; + + public showDialog(connectionManagementService: IConnectionManagementService, + params: INewConnectionParams, model: IConnectionProfile, error?: string): TPromise { + let none: void; + return TPromise.as(none); + } + +} \ No newline at end of file diff --git a/src/sqltest/stubs/connectionManagementService.test.ts b/src/sqltest/stubs/connectionManagementService.test.ts new file mode 100644 index 0000000000..5cae6720f4 --- /dev/null +++ b/src/sqltest/stubs/connectionManagementService.test.ts @@ -0,0 +1,236 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConnectionManagementService, IConnectableInput, IConnectionCompletionOptions, IConnectionCallbacks, IConnectionResult, INewConnectionParams } + from 'sql/parts/connection/common/connectionManagement'; +import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; +import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; +import data = require('data'); +import Event, { Emitter } from 'vs/base/common/event'; + +// Test stubs for commonly used objects + +export class TestConnectionManagementService implements IConnectionManagementService { + _serviceBrand: any; + onAddConnectionProfile = undefined; + onDeleteConnectionProfile = undefined; + onConnectionChanged = undefined; + onLanguageFlavorChanged = undefined; + + public get onConnect(): Event { + let conEvent = new Emitter(); + return conEvent.event; + } + + public get onDisconnect(): Event { + let conEvent = new Emitter(); + return conEvent.event; + } + + registerProvider(providerId: string, provider: data.ConnectionProvider): void { + + } + + showConnectionDialog(params?: INewConnectionParams, model?: IConnectionProfile, error?: string): Promise { + return undefined; + } + + showCreateServerGroupDialog(): Promise { + return undefined; + } + + showEditServerGroupDialog(group: ConnectionProfileGroup): Promise { + return undefined; + } + + onConnectionComplete(handle: number, connectionInfoSummary: data.ConnectionInfoSummary): void { + + } + + onIntelliSenseCacheComplete(handle: number, connectionUri: string): void { + + } + + public onConnectionChangedNotification(handle: number, changedConnInfo: data.ChangedConnectionInfo): void { + + } + + getCurrentConnectionSummary(): data.ConnectionSummary { + return undefined; + } + + getConnectionGroups(): ConnectionProfileGroup[] { + return []; + } + + getActiveConnections(): ConnectionProfile[] { + return []; + } + + saveProfileGroup(profile: IConnectionProfileGroup): Promise { + return undefined; + } + + getRecentConnections(): ConnectionProfile[] { + return []; + } + + public clearRecentConnectionsList(): void { + return; + } + + getUnsavedConnections(): ConnectionProfile[] { + return []; + } + + changeGroupIdForConnectionGroup(source: IConnectionProfileGroup, target: IConnectionProfileGroup): Promise { + return Promise.resolve(); + } + + changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise { + return Promise.resolve(); + } + + deleteConnection(connection: ConnectionProfile): Promise { + return new Promise((resolve, reject) => { + resolve(true); + }); + } + + deleteConnectionGroup(group: ConnectionProfileGroup): Promise { + return new Promise((resolve, reject) => { + resolve(true); + }); + } + + getAdvancedProperties(): data.ConnectionOption[] { + return []; + } + + getConnectionId(connectionProfile: ConnectionProfile): string { + return undefined; + } + + getFormattedUri(uri: string, connectionProfile: ConnectionProfile): string { + return undefined; + } + + isConnected(fileUri: string, connectionProfile?: ConnectionProfile): boolean { + return false; + } + + isRecent(connectionProfile: ConnectionProfile): boolean { + return false; + } + + isProfileConnected(connectionProfile: IConnectionProfile): boolean { + return false; + } + + isProfileConnecting(connectionProfile: IConnectionProfile): boolean { + return false; + } + + findExistingConnection(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): ConnectionProfile { + return undefined; + } + + connect(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise { + return new Promise((resolve, reject) => { + resolve({ connected: true, errorMessage: undefined, errorCode: undefined }); + }); + } + + connectAndSaveProfile(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise { + return new Promise(() => true); + } + + disconnectEditor(owner: IConnectableInput): Promise { + return new Promise(() => true); + } + + disconnect(connection: IConnectionProfile); + disconnect(uri: string); + disconnect(input: any): Promise { + return new Promise((resolve, reject) => { + resolve(true); + }); + } + + getConnectionProfile(fileUri: string): IConnectionProfile { + return undefined; + } + + getConnectionInfo(fileUri: string): ConnectionManagementInfo { + return undefined; + } + + addSavedPassword(connectionProfile: IConnectionProfile): Promise { + return new Promise(() => connectionProfile); + } + + public listDatabases(connectionUri: string): Thenable { + return Promise.resolve(undefined); + } + + cancelConnection(connection: IConnectionProfile): Thenable { + return undefined; + } + + cancelEditorConnection(owner: IConnectableInput): Thenable { + return undefined; + } + + showDashboard(connection: ConnectionProfile): Promise { + return new Promise(() => true); + } + + closeDashboard(uri: string): void { + } + + changeDatabase(connectionUri: string, databaseName: string): Thenable { + return new Promise(() => true); + } + + editGroup(group: ConnectionProfileGroup): Promise { + return Promise.resolve(); + } + + getProviderIdFromUri(ownerUri: string): string { + return undefined; + } + hasRegisteredServers(): boolean { + return true; + } + + getCapabilities(providerName: string): data.DataProtocolServerCapabilities { + return undefined; + } + + canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean { + return true; + } + + doChangeLanguageFlavor(uri: string, language: string, flavor: string): void { + + } + ensureDefaultLanguageFlavor(uri: string): void { + + } + + public getProviderNames(): string[] { + return []; + } + + connectIfNotConnected(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): Promise { + return undefined; + } + + rebuildIntelliSenseCache(uri: string): Thenable { + return undefined; + } +} \ No newline at end of file diff --git a/src/sqltest/stubs/connectionProviderStub.ts b/src/sqltest/stubs/connectionProviderStub.ts new file mode 100644 index 0000000000..3258ac11cc --- /dev/null +++ b/src/sqltest/stubs/connectionProviderStub.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import data = require('data'); + +export class ConnectionProviderStub implements data.ConnectionProvider { + handle: number = 1; + + connect(connectionUri: string, connectionInfo: data.ConnectionInfo): Thenable { + return undefined; + } + + disconnect(connectionUri: string): Thenable { + return undefined; + } + + cancelConnect(connectionUri: string): Thenable { + return undefined; + } + + listDatabases(connectionUri: string): Thenable { + return undefined; + } + + changeDatabase(connectionUri: string, newDatabase: string): Thenable { + return undefined; + } + + rebuildIntelliSenseCache(connectionUri: string): Thenable { + return undefined; + } + + registerOnConnectionComplete(handler: (connSummary: data.ConnectionInfoSummary) => any) { + return undefined; + } + + registerOnIntelliSenseCacheComplete(handler: (connectionUri: string) => any) { + return undefined; + } + + registerOnConnectionChanged(handler: (changedConnInfo: data.ChangedConnectionInfo) => any) { + return undefined; + } +} \ No newline at end of file diff --git a/src/sqltest/stubs/contextKeyServiceStub.ts b/src/sqltest/stubs/contextKeyServiceStub.ts new file mode 100644 index 0000000000..27e6b37436 --- /dev/null +++ b/src/sqltest/stubs/contextKeyServiceStub.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IContextKeyService, IContextKeyServiceTarget, IContextKey, ContextKeyExpr, IContext } from 'vs/platform/contextkey/common/contextkey'; +import Event from 'vs/base/common/event'; + +export class ContextKeyServiceStub implements IContextKeyService { + _serviceBrand: any; + + dispose(): void { + // + } + + onDidChangeContext: Event; + + createKey(key: string, defaultValue: T): IContextKey { + return undefined; + } + + contextMatchesRules(rules: ContextKeyExpr): boolean { + return undefined; + } + + getContextKeyValue(key: string): T { + return undefined; + } + + createScoped(target?: IContextKeyServiceTarget): IContextKeyService { + return undefined; + } + + getContext(target: IContextKeyServiceTarget): IContext { + return undefined; + } + +} \ No newline at end of file diff --git a/src/sqltest/stubs/credentialsTestStubs.ts b/src/sqltest/stubs/credentialsTestStubs.ts new file mode 100644 index 0000000000..45d8386bd9 --- /dev/null +++ b/src/sqltest/stubs/credentialsTestStubs.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as data from 'data'; +import { TPromise } from 'vs/base/common/winjs.base'; +import {CredentialManagementEvents, ICredentialsService} from "sql/services/credentials/credentialsService"; +import {IDisposable} from "vs/base/common/lifecycle"; + +export class CredentialsTestProvider implements data.CredentialProvider { + handle: number; + + public storedCredentials: {[K: string]: data.Credential} = {}; + + saveCredential(credentialId: string, password: string): Thenable { + this.storedCredentials[credentialId] = { + credentialId: credentialId, + password: password + }; + return TPromise.as(true); + } + + readCredential(credentialId: string): Thenable { + return TPromise.as(this.storedCredentials[credentialId]); + } + + deleteCredential(credentialId: string): Thenable { + let exists = this.storedCredentials[credentialId] !== undefined; + delete this.storedCredentials[credentialId]; + return TPromise.as(exists); + } +} + +export class CredentialsTestService implements ICredentialsService { + _serviceBrand: any; + + saveCredential(credentialId: string, password: string): Thenable { + return undefined; + } + + readCredential(credentialId: string): Thenable { + return undefined; + } + + deleteCredential(credentialId: string): Thenable { + return undefined; + } + + addEventListener(handle: number, events: CredentialManagementEvents): IDisposable { + return undefined; + } + +} \ No newline at end of file diff --git a/src/sqltest/stubs/editorGroupService.ts b/src/sqltest/stubs/editorGroupService.ts new file mode 100644 index 0000000000..0953917ae4 --- /dev/null +++ b/src/sqltest/stubs/editorGroupService.ts @@ -0,0 +1,134 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { Position, IEditorInput } from 'vs/platform/editor/common/editor'; +import { IEditorStacksModel, IEditorGroup, EditorInput } from 'vs/workbench/common/editor'; +import Event from 'vs/base/common/event'; +import { ITabOptions, GroupArrangement, GroupOrientation } from 'vs/workbench/services/group/common/groupService'; +import { IEditorGroupService, IMoveOptions } from 'vs/workbench/services/group/common/groupService'; +import { EditorGroup } from "vs/workbench/common/editor/editorStacksModel"; + +export class EditorGroupTestService implements IEditorGroupService { + _serviceBrand: ServiceIdentifier; + + /** + * Emitted when editors or inputs change. Examples: opening, closing of editors. Active editor change. + */ + onEditorsChanged: Event; + + /** + * Emitted when opening an editor fails. + */ + onEditorOpenFail: Event; + + /** + * Emitted when a editors are moved to another position. + */ + onEditorsMoved: Event; + + /** + * Emitted when the editor group orientation was changed. + */ + onGroupOrientationChanged: Event; + + /** + * Emitted when tab options changed. + */ + onTabOptionsChanged: Event; + + /** + * Keyboard focus the editor group at the provided position. + */ + public focusGroup(group: EditorGroup): void; + public focusGroup(position: Position): void; + public focusGroup(arg1: any) { + return; + } + + /** + * Activate the editor group at the provided position without moving focus. + */ + public activateGroup(group: EditorGroup): void; + public activateGroup(position: Position): void; + public activateGroup(arg1: any): void { + } + /** + * Allows to move the editor group from one position to another. + */ + + + public moveGroup(from: EditorGroup, to: EditorGroup): void; + public moveGroup(from: Position, to: Position): void; + public moveGroup(arg1: any, arg2: any): void { + } + + /** + * Allows to arrange editor groups according to the GroupArrangement enumeration. + */ + arrangeGroups(arrangement: GroupArrangement): void { + + } + + /** + * Changes the editor group layout between vertical and horizontal orientation. Only applies + * if more than one editor is opened. + */ + setGroupOrientation(orientation: GroupOrientation): void { + + } + + /** + * Returns the current editor group layout. + */ + getGroupOrientation(): GroupOrientation { + return undefined; + } + + /** + * Moves an editor from one group to another. The index in the group is optional. + */ + moveEditor(input: IEditorInput, from: IEditorGroup, to: IEditorGroup, moveOptions?: IMoveOptions): void; + moveEditor(input: IEditorInput, from: Position, to: Position, moveOptions?: IMoveOptions): void; + moveEditor(input: EditorInput, arg2: any, arg3: any, index?: IMoveOptions): void { + + } + + /** + * Provides access to the editor stacks model + */ + getStacksModel(): IEditorStacksModel { + return undefined; + } + + /** + * Returns true if tabs are shown, false otherwise. + */ + getTabOptions(): ITabOptions { + return undefined; + } + + public pinEditor(group: EditorGroup, input: EditorInput): void; + public pinEditor(position: Position, input: EditorInput): void; + public pinEditor(arg1: any, input: EditorInput): void { + + } + + public unpinEditor(group: EditorGroup, input: EditorInput): void; + public unpinEditor(position: Position, input: EditorInput): void; + public unpinEditor(arg1: any, input: EditorInput): void { + + } + + /** + * Resize visible editor groups + */ + public resizeGroup(position: Position, groupSizeChange: number): void { + + } + +} \ No newline at end of file diff --git a/src/sqltest/stubs/errorMessageServiceStub.ts b/src/sqltest/stubs/errorMessageServiceStub.ts new file mode 100644 index 0000000000..4640a1a5d3 --- /dev/null +++ b/src/sqltest/stubs/errorMessageServiceStub.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. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement'; +import Severity from 'vs/base/common/severity'; + +export class ErrorMessageServiceStub implements IErrorMessageService { + _serviceBrand: any; + showDialog(severity: Severity, headerTitle: string, message: string): void { + } +} \ No newline at end of file diff --git a/src/sqltest/stubs/messageServiceStub.ts b/src/sqltest/stubs/messageServiceStub.ts new file mode 100644 index 0000000000..964dd097e7 --- /dev/null +++ b/src/sqltest/stubs/messageServiceStub.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import Severity from 'vs/base/common/severity'; +import { IConfirmation, IMessageService, IMessageWithAction } from 'vs/platform/message/common/message'; + +export class MessageServiceStub implements IMessageService{ + _serviceBrand: any; + + show(sev: Severity, message: string): () => void; + show(sev: Severity, message: Error): () => void; + show(sev: Severity, message: string[]): () => void; + show(sev: Severity, message: Error[]): () => void; + show(sev: Severity, message: IMessageWithAction): () => void; + show(sev: Severity, message): () => void { + return undefined; + } + + hideAll(): void { + return undefined; + } + + confirm(confirmation: IConfirmation): boolean { + return undefined; + } +} \ No newline at end of file diff --git a/src/sqltest/stubs/objectExplorerProviderTestService.ts b/src/sqltest/stubs/objectExplorerProviderTestService.ts new file mode 100644 index 0000000000..ddabd70f8b --- /dev/null +++ b/src/sqltest/stubs/objectExplorerProviderTestService.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import data = require('data'); + +// Test stubs for commonly used objects + +export class ObjectExplorerProviderTestService implements data.ObjectExplorerProvider { + + public createNewSession(connInfo: data.ConnectionInfo): Thenable { + return Promise.resolve(undefined); + } + + public expandNode(nodeInfo: data.ExpandNodeInfo): Thenable { + return Promise.resolve(undefined); + } + + public refreshNode(nodeInfo: data.ExpandNodeInfo): Thenable { + return Promise.resolve(undefined); + } + + public closeSession(closeSessionInfo: data.ObjectExplorerCloseSessionInfo): Thenable { + return Promise.resolve(undefined); + } + + public registerOnSessionCreated(handler: (response: data.ObjectExplorerSession) => any): void { + + } + + public registerOnExpandCompleted(handler: (response: data.ObjectExplorerExpandInfo) => any): void { + + } +} \ No newline at end of file diff --git a/src/sqltest/stubs/sqlOauthServiceStub.ts b/src/sqltest/stubs/sqlOauthServiceStub.ts new file mode 100644 index 0000000000..d874a3d225 --- /dev/null +++ b/src/sqltest/stubs/sqlOauthServiceStub.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ISqlOAuthService } from 'sql/common/sqlOAuthService'; + +export class SqlOAuthTestService implements ISqlOAuthService { + _serviceBrand: any; + + performOAuthAuthorization(eventId: string, url: string, silent: boolean): void { + } + + registerOAuthCallback(handler: (event, args) => void): void { + } +} diff --git a/src/sqltest/stubs/storageTestService.ts b/src/sqltest/stubs/storageTestService.ts new file mode 100644 index 0000000000..c205a367a4 --- /dev/null +++ b/src/sqltest/stubs/storageTestService.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; + +export class StorageTestService implements IStorageService { + _serviceBrand: any; + + /** + * Store a string value under the given key to local storage. + * + * The optional scope argument allows to define the scope of the operation. + */ + store(key: string, value: any, scope?: StorageScope): void { + + } + + /** + * Swap the value of a stored element to one of the two provided + * values and use the defaultValue if no element with the given key + * exists. + * + * The optional scope argument allows to define the scope of the operation. + */ + swap(key: string, valueA: any, valueB: any, scope?: StorageScope, defaultValue?: any): void { + + } + + /** + * Delete an element stored under the provided key from local storage. + * + * The optional scope argument allows to define the scope of the operation. + */ + remove(key: string, scope?: StorageScope): void { + + } + + /** + * Retrieve an element stored with the given key from local storage. Use + * the provided defaultValue if the element is null or undefined. + * + * The optional scope argument allows to define the scope of the operation. + */ + get(key: string, scope?: StorageScope, defaultValue?: string): string { + return undefined; + } + + /** + * Retrieve an element stored with the given key from local storage. Use + * the provided defaultValue if the element is null or undefined. The element + * will be converted to a number using parseInt with a base of 10. + * + * The optional scope argument allows to define the scope of the operation. + */ + getInteger(key: string, scope?: StorageScope, defaultValue?: number): number { + return 0; + } + + /** + * Retrieve an element stored with the given key from local storage. Use + * the provided defaultValue if the element is null or undefined. The element + * will be converted to a boolean. + * + * The optional scope argument allows to define the scope of the operation. + */ + getBoolean(key: string, scope?: StorageScope, defaultValue?: boolean): boolean { + return true; + } +} \ No newline at end of file diff --git a/src/sqltest/stubs/telemetryServiceStub.ts b/src/sqltest/stubs/telemetryServiceStub.ts new file mode 100644 index 0000000000..02ee36c4a8 --- /dev/null +++ b/src/sqltest/stubs/telemetryServiceStub.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { ITelemetryService, ITelemetryData, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; +import { TPromise } from 'vs/base/common/winjs.base'; + +// Test stubs for commonly used objects + +export class TelemetryServiceStub implements ITelemetryService { + + _serviceBrand: any; + + /** + * Sends a telemetry event that has been privacy approved. + * Do not call this unless you have been given approval. + */ + publicLog(eventName: string, data?: ITelemetryData): TPromise { + return undefined; + } + + getTelemetryInfo(): TPromise { + return undefined; + } + + isOptedIn: boolean; +} \ No newline at end of file diff --git a/src/sqltest/stubs/themeTestService.ts b/src/sqltest/stubs/themeTestService.ts new file mode 100644 index 0000000000..c477577222 --- /dev/null +++ b/src/sqltest/stubs/themeTestService.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IThemeService, ITheme, IThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { Color } from 'vs/base/common/color'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; + +export class TestTheme implements ITheme { + selector: string; + type: 'light' | 'dark' | 'hc'; + + getColor(color: string, useDefault?: boolean): Color { + return Color.white; + } + + isDefault(color: string): boolean { + throw new Error('Method not implemented.'); + } + + defines(color: ColorIdentifier): boolean { + throw new Error('Method not implemented.'); + } +} + +const testTheme = new TestTheme(); + +export class TestThemeService implements IThemeService { + + _serviceBrand: any; + + getTheme(): ITheme { + return testTheme; + } + + onThemeChange(participant: IThemingParticipant): IDisposable { + return { dispose: () => { } }; + } +} \ No newline at end of file diff --git a/src/sqltest/stubs/workbenchEditorTestService.ts b/src/sqltest/stubs/workbenchEditorTestService.ts new file mode 100644 index 0000000000..0df11b0247 --- /dev/null +++ b/src/sqltest/stubs/workbenchEditorTestService.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IEditorService, IEditor, IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IResourceInput, IResourceDiffInput, IResourceSideBySideInput } + from 'vs/platform/editor/common/editor'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorInput, EditorOptions, IFileEditorInput, TextEditorOptions, IEditorRegistry, Extensions, SideBySideEditorInput } from 'vs/workbench/common/editor'; + +export class WorkbenchEditorTestService implements IWorkbenchEditorService { + _serviceBrand: ServiceIdentifier; + + /** + * Returns the currently active editor or null if none. + */ + getActiveEditor(): IEditor { + return undefined; + } + + /** + * Returns the currently active editor input or null if none. + */ + getActiveEditorInput(): IEditorInput { + return undefined; + } + + /** + * Returns an array of visible editors. + */ + getVisibleEditors(): IEditor[] { + return undefined; + } + + /** + * Returns iff the provided input is currently visible. + * + * @param includeDiff iff set to true, will also consider diff editors to find out if the provided + * input is opened either on the left or right hand side of the diff editor. + */ + isVisible(input: IEditorInput, includeDiff: boolean): boolean { + return undefined; + } + + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { + return undefined; + } + + /** + * Opens an Editor on the given input with the provided options at the given position. If sideBySide parameter + * is provided, causes the editor service to decide in what position to open the input. + */ + public openEditor(input: IEditorInput, options?: IEditorOptions, sideBySide?: boolean): TPromise; + public openEditor(input: IEditorInput, options?: IEditorOptions, position?: Position): TPromise; + public openEditor(input: IResourceInput | IResourceDiffInput | IResourceSideBySideInput, position?: Position): TPromise; + public openEditor(input: IResourceInput | IResourceDiffInput | IResourceSideBySideInput, sideBySide?: boolean): TPromise; + public openEditor(input: any, arg2?: any, arg3?: any): TPromise { + return undefined; + } + + public openEditors(editors: { input: IResourceInput | IResourceDiffInput | IResourceSideBySideInput, position: Position }[]): TPromise; + public openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions }[]): TPromise; + public openEditors(editors: any[]): TPromise { + return undefined; + } + + public replaceEditors(editors: { toReplace: IResourceInput | IResourceDiffInput | IResourceSideBySideInput, replaceWith: IResourceInput | IResourceDiffInput | IResourceSideBySideInput }[], position?: Position): TPromise; + public replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions }[], position?: Position): TPromise; + public replaceEditors(editors: any[], position?: Position): TPromise { + return undefined; + } + + /** + * Closes the editor at the provided position. + */ + closeEditor(position: Position, input: IEditorInput): TPromise { + return undefined; + } + + /** + * Closes editors of a specific group at the provided position. If the optional editor is provided to exclude, it + * will not be closed. The direction can be used in that case to control if all other editors should get closed, + * or towards a specific direction. + */ + closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise { + return undefined; + } + + /** + * Closes all editors across all groups. The optional position allows to keep one group alive. + */ + closeAllEditors(except?: Position): TPromise { + return undefined; + } + + /** + * Allows to resolve an untyped input to a workbench typed instanceof editor input + */ + createInput(input: IResourceInput | IResourceDiffInput | IResourceSideBySideInput): IEditorInput { + return undefined; + } +} \ No newline at end of file diff --git a/src/sqltest/stubs/workspaceConfigurationTestService.ts b/src/sqltest/stubs/workspaceConfigurationTestService.ts new file mode 100644 index 0000000000..8934946c5d --- /dev/null +++ b/src/sqltest/stubs/workspaceConfigurationTestService.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { + IConfigurationValue, IConfigurationData, IConfigurationKeys, + IConfigurationServiceEvent, IConfigurationValues } from 'vs/platform/configuration/common/configuration'; +import { TPromise } from 'vs/base/common/winjs.base'; +import Event from 'vs/base/common/event'; + +export class WorkspaceConfigurationTestService implements IWorkspaceConfigurationService { + _serviceBrand: any; + + + getConfigurationData(): IConfigurationData { + return undefined; + } + + /** + * Returns untrusted configuration keys for the current workspace. + */ + getUnsupportedWorkspaceKeys(): string[] { + return []; + } + + /** + * Returns iff the workspace has configuration or not. + */ + hasWorkspaceConfiguration(): boolean { + return true; + } + + /** + * Returns untrusted configuration keys for the current workspace. + */ + getUntrustedConfigurations(): string[] { + return []; + } + + /** + * Returns if the user explicitly configured to not trust the current workspace. + */ + isExplicitlyUntrusted(): boolean { + return true; + } + + /** + * Override for the IConfigurationService#lookup() method that adds information about workspace settings. + */ + lookup(key: string): IConfigurationValue { + return undefined; + } + + getConfiguration(section?: string): T { + return undefined; + } + + reloadConfiguration(section?: string): TPromise { + return undefined; + } + + /** + * Override for the IConfigurationService#keys() method that adds information about workspace settings. + */ + keys(): IConfigurationKeys { + return undefined; + } + + /** + * Returns the defined values of configurations in the different scopes. + */ + values(): IConfigurationValues { + return undefined; + } + + onDidUpdateConfiguration: Event; +} \ No newline at end of file diff --git a/src/sqltest/utils/eventVerifier.ts b/src/sqltest/utils/eventVerifier.ts new file mode 100644 index 0000000000..55ca2496f8 --- /dev/null +++ b/src/sqltest/utils/eventVerifier.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; + +export class EventVerifierSingle { + private _eventArgument: T; + private _eventFired: boolean; + + constructor() { + this._eventFired = false; + } + + public get eventArgument(): T { + return this._eventArgument; + } + + public get eventFired(): boolean { + return this._eventFired; + } + + public assertFired(expectedArgument?: T) { + assert.ok(this._eventFired); + if (expectedArgument) { + assert.equal(this._eventArgument, expectedArgument); + } + } + + public assertFiredWithVerify(argumentVerification: (arg: T) => void) { + assert.ok(this._eventFired); + argumentVerification(this._eventArgument); + } + + public assertNotFired() { + assert.equal(this._eventFired, false); + } + + public get eventHandler(): (arg: T) => void { + let self = this; + return (arg: T) => { + self._eventArgument = arg; + self._eventFired = true; + }; + } +} + +export class EventVerifierMultiple { + private _eventArguments: T[]; + + constructor() { + this._eventArguments = []; + } + + public get eventArguments(): T[] { + return this._eventArguments; + } + + public get eventFired(): boolean { + return this._eventArguments.length > 0; + } + + public assertFired(expectedArgument?: T) { + assert.ok(this.eventFired); + if (expectedArgument) { + assert.ok(this._eventArguments.includes(expectedArgument)); + } + } + + public assertNotFired(expectedArgument?: T) { + if (expectedArgument) { + assert.ok(!this._eventArguments.includes(expectedArgument)); + } else { + assert.ok(!this.eventFired); + } + } + + public eventHandler(): (arg: T) => void { + let self = this; + return (arg: T) => { + self._eventArguments.push(arg); + }; + } +} diff --git a/src/sqltest/workbench/api/extHostAccountManagement.test.ts b/src/sqltest/workbench/api/extHostAccountManagement.test.ts new file mode 100644 index 0000000000..7515858de3 --- /dev/null +++ b/src/sqltest/workbench/api/extHostAccountManagement.test.ts @@ -0,0 +1,273 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as data from 'data'; +import * as TypeMoq from 'typemoq'; +import { AccountProviderStub, AccountManagementTestService } from 'sqltest/stubs/accountManagementStubs'; +import { ExtHostAccountManagement } from 'sql/workbench/api/node/extHostAccountManagement'; +import { TestThreadService } from 'vs/workbench/test/electron-browser/api/testThreadService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; +import { SqlMainContext } from 'sql/workbench/api/node/sqlExtHost.protocol'; +import { MainThreadAccountManagement } from 'sql/workbench/api/node/mainThreadAccountManagement'; +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +const IThreadService = createDecorator('threadService'); + +// SUITE STATE ///////////////////////////////////////////////////////////// +let instantiationService: TestInstantiationService; +let mockAccountMetadata: data.AccountProviderMetadata; +let mockAccount: data.Account; +let threadService: TestThreadService; + +// TESTS /////////////////////////////////////////////////////////////////// +suite('ExtHostAccountManagement', () => { + suiteSetup(() => { + threadService = new TestThreadService(); + const accountMgmtStub = new AccountManagementTestService(); + + instantiationService = new TestInstantiationService(); + instantiationService.stub(IThreadService, threadService); + instantiationService.stub(IAccountManagementService, accountMgmtStub); + + const accountMgmtService = instantiationService.createInstance(MainThreadAccountManagement); + threadService.setTestInstance(SqlMainContext.MainThreadAccountManagement, accountMgmtService); + + mockAccountMetadata = { + args: {}, + displayName: 'Test Account Provider', + id: 'test_account_provider', + settings: {} + }; + mockAccount = { + key: { + providerId: mockAccountMetadata.id, + providerArgs: {}, + accountId: 'test_account' + }, + properties: {}, + displayInfo: { + displayName: 'Test Account', + contextualDisplayName: 'Test Kind Of Account', + contextualLogo: { light: '', dark: '' } + }, + isStale: false + }; + }); + + test('Constructor', () => { + // If: I construct a new extension host account management + let extHost = new ExtHostAccountManagement(threadService); + + // Then: There shouldn't be any account providers registered + assert.equal(extHost.getProviderCount(), 0); + }); + + // REGISTER TESTS ////////////////////////////////////////////////////// + test('Register Account Provider - Success', () => { + // Setup: Create an extension host account management + let extHost = new ExtHostAccountManagement(threadService); + let mockProvider = getMockAccountProvider(); + + // If: I register a mock account provider + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + + // Then: The account provider should be registered + assert.equal(extHost.getProviderCount(), 1); + }); + + test('Register Account Provider - Account Provider Already Registered', () => { + // Setup: Create an extension host account management and register an account provider + let extHost = new ExtHostAccountManagement(threadService); + let mockProvider = getMockAccountProvider(); + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + + // If: I register an account provider again + // Then + // ... It should throw + assert.throws(() => { + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + }); + + // ... There should only be one account provider + assert.equal(extHost.getProviderCount(), 1); + }); + + // TODO: Test for unregistering a provider + + // CLEAR TESTS ///////////////////////////////////////////////////////// + test('Clear - Success', (done) => { + // Setup: Create ext host account management with registered account provider + let extHost = new ExtHostAccountManagement(threadService); + let mockProvider = getMockAccountProvider(); + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + + // If: I clear an account + extHost.$clear(0, mockAccount.key) + .then(() => { + // Then: The call should have been passed to the provider + mockProvider.verify( + (obj) => obj.clear(TypeMoq.It.isValue(mockAccount.key)), + TypeMoq.Times.once() + ); + }) + .then(() => done(), (err) => done(err)); + }); + + test('Clear - Handle does not exist', (done) => { + // Setup: Create ext host account management with registered account provider + let extHost = new ExtHostAccountManagement(threadService); + let mockProvider = getMockAccountProvider(); + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + + // If: I clear an account for a handle that doesn't exist + // Then: It should fail + extHost.$clear(1, mockAccount.key) + .then(() => done('Clear succeeded when it should have failed')) + .then(null, () => { + // The provider's clear should not have been called + mockProvider.verify( + (obj) => obj.clear(TypeMoq.It.isAny()), + TypeMoq.Times.never() + ); + }) + .then(() => done(), (err) => done(err)); + }); + + // INITIALIZE TESTS //////////////////////////////////////////////////// + test('Initialize - Success', (done) => { + // Setup: Create ext host account management with registered account provider + let extHost = new ExtHostAccountManagement(threadService); + let mockProvider = getMockAccountProvider(); + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + + // If: I initialize the provider + extHost.$initialize(0, [mockAccount]) + .then(() => { + // Then: The call should have been passed to the provider + mockProvider.verify( + (obj) => obj.initialize(TypeMoq.It.isValue([mockAccount])), + TypeMoq.Times.once() + ); + }) + .then(() => done(), (err) => done(err)); + }); + + test('Initialize - Handle does not exist', (done) => { + // Setup: Create ext host account management with registered account provider + let extHost = new ExtHostAccountManagement(threadService); + let mockProvider = getMockAccountProvider(); + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + + // If: I initialize for a handle that doesn't exist + // Then: It should fail + extHost.$initialize(1, [mockAccount]) + .then(() => done('Initialize succeeded when it should have failed')) + .then(null, () => { + // The provider's clear should not have been called + mockProvider.verify( + (obj) => obj.initialize(TypeMoq.It.isAny()), + TypeMoq.Times.never() + ); + }) + .then(() => done(), (err) => done(err)); + }); + + // PROMPT TESTS //////////////////////////////////////////////////////// + test('Prompt - Success', (done) => { + // Setup: Create ext host account management with registered account provider + let extHost = new ExtHostAccountManagement(threadService); + let mockProvider = getMockAccountProvider(); + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + + // If: I prompt for an account + extHost.$prompt(0) + .then(() => { + // Then: The call should have been passed to the provider + mockProvider.verify( + (obj) => obj.prompt(), + TypeMoq.Times.once() + ); + }) + .then(() => done(), (err) => done(err)); + }); + + test('Prompt - Handle does not exist', (done) => { + // Setup: Create ext host account management with registered account provider + let extHost = new ExtHostAccountManagement(threadService); + let mockProvider = getMockAccountProvider(); + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + + // If: I prompt with a handle that doesn't exist + // Then: It should fail + extHost.$prompt(1) + .then(() => done('Prompt succeeded when it should have failed')) + .then(null, () => { + // The provider's clear should not have been called + mockProvider.verify( + (obj) => obj.prompt(), + TypeMoq.Times.never() + ); + }) + .then(() => done(), (err) => done(err)); + }); + + // REFRESH TESTS /////////////////////////////////////////////////////// + test('Refresh - Success', (done) => { + // Setup: Create ext host account management with registered account provider + let extHost = new ExtHostAccountManagement(threadService); + let mockProvider = getMockAccountProvider(); + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + + // If: I refresh an account + extHost.$refresh(0, mockAccount) + .then(() => { + // Then: The call should have been passed to the provider + mockProvider.verify( + (obj) => obj.refresh(TypeMoq.It.isValue(mockAccount)), + TypeMoq.Times.once() + ); + }) + .then(() => done(), (err) => done(err)); + }); + + test('Refresh - Handle does not exist', (done) => { + // Setup: Create ext host account management with registered account provider + let extHost = new ExtHostAccountManagement(threadService); + let mockProvider = getMockAccountProvider(); + extHost.$registerAccountProvider(mockAccountMetadata, mockProvider.object); + + // If: I refresh an account for a handle that doesn't exist + // Then: It should fail + extHost.$refresh(1, mockAccount) + .then(() => done('Refresh succeeded when it should have failed')) + .then(null, () => { + // The provider's clear should not have been called + mockProvider.verify( + (obj) => obj.refresh(TypeMoq.It.isAny()), + TypeMoq.Times.never() + ); + }) + .then(() => done(), (err) => done(err)); + }); +}); + +function getMockAccountProvider(): TypeMoq.Mock { + let mock = TypeMoq.Mock.ofType(AccountProviderStub); + mock.setup((obj) => obj.clear(TypeMoq.It.isValue(mockAccount.key))) + .returns(() => Promise.resolve(undefined)); + mock.setup((obj) => obj.refresh(TypeMoq.It.isValue(mockAccount))) + .returns(() => Promise.resolve(undefined)); + mock.setup((obj) => obj.initialize(TypeMoq.It.isValue([mockAccount]))) + .returns(() => Promise.resolve(undefined)); + mock.setup((obj) => obj.prompt()) + .returns(() => Promise.resolve(undefined)); + + return mock; +} diff --git a/src/sqltest/workbench/api/extHostCredentialManagement.test.ts b/src/sqltest/workbench/api/extHostCredentialManagement.test.ts new file mode 100644 index 0000000000..e68aef9cdf --- /dev/null +++ b/src/sqltest/workbench/api/extHostCredentialManagement.test.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import {TestThreadService} from 'vs/workbench/test/electron-browser/api/testThreadService'; +import {TestInstantiationService} from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import {ExtHostCredentialManagement} from 'sql/workbench/api/node/extHostCredentialManagement'; +import {SqlMainContext} from 'sql/workbench/api/node/sqlExtHost.protocol'; +import {IThreadService} from 'vs/workbench/services/thread/common/threadService'; +import {MainThreadCredentialManagement} from 'sql/workbench/api/node/mainThreadCredentialManagement'; +import {CredentialsTestProvider, CredentialsTestService} from 'sqltest/stubs/credentialsTestStubs'; +import {ICredentialsService} from 'sql/services/credentials/credentialsService'; +import {Credential, CredentialProvider} from 'data'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +const IThreadService = createDecorator('threadService'); + +// SUITE STATE ///////////////////////////////////////////////////////////// +let credentialServiceStub: CredentialsTestService; +let instantiationService: TestInstantiationService; +let threadService: TestThreadService; + +// TESTS /////////////////////////////////////////////////////////////////// +suite('ExtHostCredentialManagement', () => { + suiteSetup(() => { + threadService = new TestThreadService(); + credentialServiceStub = new CredentialsTestService(); + + instantiationService = new TestInstantiationService(); + instantiationService.stub(IThreadService, threadService); + instantiationService.stub(ICredentialsService, credentialServiceStub); + + const credentialService = instantiationService.createInstance(MainThreadCredentialManagement); + threadService.setTestInstance(SqlMainContext.MainThreadCredentialManagement, credentialService); + }); + + test('Construct ExtHostCredentialManagement', () => { + // If: I construct an credential management extension host + let extHost = new ExtHostCredentialManagement(threadService); + + // Then: The extension host should not have any providers registered + assert.equal(extHost.getProviderCount(), 0); + }); + + test('Register Credential Provider', () => { + // Setup: Create a mock credential provider + let extHost = new ExtHostCredentialManagement(threadService); + let mockCredentialProvider = new CredentialsTestProvider(); + + // If: I register the credential provider with the extension host + extHost.$registerCredentialProvider(mockCredentialProvider); + + // Then: There should be one provider registered + assert.equal(extHost.getProviderCount(), 1); + }); + + test('Get Credential Provider - Success', (done) => { + // Setup: Register a mock credential provider + let extHost = new ExtHostCredentialManagement(threadService); + let mockCredentialProvider = new CredentialsTestProvider(); + extHost.$registerCredentialProvider(mockCredentialProvider); + + // If: I get the credential provider + let namespaceId = 'test_namespace'; + let credentialId = 'test_id'; + let credential = 'test_credential'; + let expectedCredentialId = `${namespaceId}|${credentialId}`; + let credProvider: CredentialProvider; + extHost.$getCredentialProvider(namespaceId) + .then((provider) => { + // Then: There should still only be one provider registered + assert.equal(extHost.getProviderCount(), 1); + credProvider = provider; + }) + .then(() => { + // If: I write a credential + return credProvider.saveCredential(credentialId, credential); + }) + .then(() => { + // Then: The credential should have been stored with its namespace + assert.notStrictEqual(mockCredentialProvider.storedCredentials[expectedCredentialId], undefined); + assert.equal(mockCredentialProvider.storedCredentials[expectedCredentialId].credentialId, expectedCredentialId); + assert.equal(mockCredentialProvider.storedCredentials[expectedCredentialId].password, credential); + }) + .then(() => { + // If: I read a credential + return credProvider.readCredential(credentialId); + }) + .then((returnedCredential: Credential) => { + // Then: The credential ID should be namespaced + assert.equal(returnedCredential.credentialId, expectedCredentialId); + assert.equal(returnedCredential.password, credential); + }) + .then(() => { + // If: I delete a credential + return credProvider.deleteCredential(credentialId); + }) + .then(() => { + // Then: The credential with its namespace should no longer exist + assert.strictEqual(mockCredentialProvider.storedCredentials[expectedCredentialId], undefined); + }) + .then(() => done(), (err) => done(err)); + }); + + test('Get Credential Provider - No Namespace', (done) => { + // Setup: Register a mock credential provider + let extHost = new ExtHostCredentialManagement(threadService); + let mockCredentialProvider = new CredentialsTestProvider(); + extHost.$registerCredentialProvider(mockCredentialProvider); + + // If: I get a credential provider with an invalid namespace ID + // Then: I should get an error + extHost.$getCredentialProvider(undefined) + .then( + () => { done('Provider was returned from undefined'); }, + () => { /* Swallow error, this is success path */ } + ) + .then(() => { return extHost.$getCredentialProvider(null); }) + .then( + () => { done('Provider was returned from null'); }, + () => { /* Swallow error, this is success path */ } + ) + .then(() => {return extHost.$getCredentialProvider(''); }) + .then( + () => { done('Provider was returned from \'\''); }, + () => { done(); } + ); + }); +}); diff --git a/src/sqltest/workbench/api/extHostDataProtocol.test.ts b/src/sqltest/workbench/api/extHostDataProtocol.test.ts new file mode 100644 index 0000000000..5d03e4e57f --- /dev/null +++ b/src/sqltest/workbench/api/extHostDataProtocol.test.ts @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { setUnexpectedErrorHandler, errorHandler } from 'vs/base/common/errors'; +import URI from 'vs/base/common/uri'; +import * as EditorCommon from 'vs/editor/common/editorCommon'; +import { Model as EditorModel } from 'vs/editor/common/model/model'; +import { TestThreadService } from 'vs/workbench/test/electron-browser/api/testThreadService'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; +import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; +import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands'; +import { IHeapService } from 'vs/workbench/api/electron-browser/mainThreadHeapService'; +import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; +import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; +import { MainContext, ExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; +import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; +import * as vscode from 'vscode'; + +import * as data from 'data'; +import { ExtHostDataProtocol } from 'sql/workbench/api/node/extHostDataProtocol'; +import { SqlExtHostContext } from 'sql/workbench/api/node/sqlExtHost.protocol'; + +const IThreadService = createDecorator('threadService'); + +const model: EditorCommon.IModel = EditorModel.createFromString( + [ + 'This is the first line', + 'This is the second line', + 'This is the third line', + ].join('\n'), + undefined, + undefined, + URI.parse('far://testing/file.a')); + +let extHost: ExtHostDataProtocol; +let disposables: vscode.Disposable[] = []; +let threadService: TestThreadService; +let originalErrorHandler: (e: any) => any; + +suite('ExtHostDataProtocol', function () { + + suiteSetup(() => { + + threadService = new TestThreadService(); + let instantiationService = new TestInstantiationService(); + instantiationService.stub(IThreadService, threadService); + instantiationService.stub(IMarkerService, MarkerService); + instantiationService.stub(IHeapService, { + _serviceBrand: undefined, + trackRecursive(args) { + // nothing + return args; + } + }); + + originalErrorHandler = errorHandler.getUnexpectedErrorHandler(); + setUnexpectedErrorHandler(() => { }); + + const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(threadService); + extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ + addedDocuments: [{ + isDirty: false, + versionId: model.getVersionId(), + modeId: model.getLanguageIdentifier().language, + url: model.uri, + lines: model.getValue().split(model.getEOL()), + EOL: model.getEOL(), + }] + }); + const extHostDocuments = new ExtHostDocuments(threadService, extHostDocumentsAndEditors); + threadService.set(ExtHostContext.ExtHostDocuments, extHostDocuments); + + const heapService = new ExtHostHeapService(); + + const commands = new ExtHostCommands(threadService, heapService); + threadService.set(ExtHostContext.ExtHostCommands, commands); + threadService.setTestInstance(MainContext.MainThreadCommands, instantiationService.createInstance(MainThreadCommands)); + + const diagnostics = new ExtHostDiagnostics(threadService); + threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics); + + extHost = new ExtHostDataProtocol(threadService); + threadService.set(SqlExtHostContext.ExtHostDataProtocol, extHost); + }); + + suiteTeardown(() => { + setUnexpectedErrorHandler(originalErrorHandler); + model.dispose(); + }); + + teardown(function () { + while (disposables.length) { + disposables.pop().dispose(); + } + return threadService.sync(); + }); + + // --- outline + + test('DataProvider, language flavor changed', function () { + assert.equal(DocumentSymbolProviderRegistry.all(model).length, 0); + let expectedParams = { + uri: 'myuri', + language: 'sql' + }; + let actualParams: data.DidChangeLanguageFlavorParams = undefined; + extHost.onDidChangeLanguageFlavor((e => { + actualParams = e; + })); + + extHost.$languageFlavorChanged(expectedParams); + + assert.ok(actualParams !== undefined); + assert.equal(actualParams.uri, expectedParams.uri); + assert.equal(actualParams.flavor, expectedParams.flavor); + assert.equal(actualParams.language, expectedParams.language); + }); +}); diff --git a/src/tsconfig.json b/src/tsconfig.json new file mode 100644 index 0000000000..ed888ce8a0 --- /dev/null +++ b/src/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "amd", + "moduleResolution": "classic", + "noImplicitAny": false, + "removeComments": false, + "preserveConstEnums": true, + "target": "es5", + "sourceMap": false, + "experimentalDecorators": true, + "declaration": true, + "noImplicitReturns": true, + "baseUrl": ".", + "typeRoots": [ + "typings" + ] + } +} \ No newline at end of file diff --git a/src/typings/OSSREADME.json b/src/typings/OSSREADME.json new file mode 100644 index 0000000000..63488ec550 --- /dev/null +++ b/src/typings/OSSREADME.json @@ -0,0 +1,9 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +// All OSS in this folder is development time only +[{ + "name": "definitelytyped", + "repositoryURL": "https://github.com/DefinitelyTyped/DefinitelyTyped", + "license": "MIT", + "isDev": true +}] \ No newline at end of file diff --git a/src/typings/ansi-regex.d.ts b/src/typings/ansi-regex.d.ts new file mode 100644 index 0000000000..2bf8b8a433 --- /dev/null +++ b/src/typings/ansi-regex.d.ts @@ -0,0 +1,5 @@ +declare module 'ansi-regex' { + function result(): RegExp; + module result { } + export = result; +} diff --git a/src/typings/applicationInsights.d.ts b/src/typings/applicationInsights.d.ts new file mode 100644 index 0000000000..9f4ccb2e1d --- /dev/null +++ b/src/typings/applicationInsights.d.ts @@ -0,0 +1,66 @@ +/** + * The singleton meta class for the default client of the client. This class is used to setup/start and configure + * the auto-collection behavior of the application insights module. + */ +declare module ApplicationInsights { + var client: any; + /** + * Initializes a client with the given instrumentation key, if this is not specified, the value will be + * read from the environment variable APPINSIGHTS_INSTRUMENTATIONKEY + * @returns {ApplicationInsights/Client} a new client + */ + function getClient(instrumentationKey?: string): any /*Client*/; + /** + * Initializes the default client of the client and sets the default configuration + * @param instrumentationKey the instrumentation key to use. Optional, if this is not specified, the value will be + * read from the environment variable APPINSIGHTS_INSTRUMENTATIONKEY + * @returns {ApplicationInsights} this class + */ + function setup(instrumentationKey?: string): typeof ApplicationInsights; + /** + * Starts automatic collection of telemetry. Prior to calling start no telemetry will be collected + * @returns {ApplicationInsights} this class + */ + function start(): typeof ApplicationInsights; + /** + * Sets the state of console tracking (enabled by default) + * @param value if true console activity will be sent to Application Insights + * @returns {ApplicationInsights} this class + */ + function setAutoCollectConsole(value: boolean): typeof ApplicationInsights; + /** + * Sets the state of exception tracking (enabled by default) + * @param value if true uncaught exceptions will be sent to Application Insights + * @returns {ApplicationInsights} this class + */ + function setAutoCollectExceptions(value: boolean): typeof ApplicationInsights; + /** + * Sets the state of performance tracking (enabled by default) + * @param value if true performance counters will be collected every second and sent to Application Insights + * @returns {ApplicationInsights} this class + */ + function setAutoCollectPerformance(value: boolean): typeof ApplicationInsights; + /** + * Sets the state of request tracking (enabled by default) + * @param value if true requests will be sent to Application Insights + * @returns {ApplicationInsights} this class + */ + function setAutoCollectRequests(value: boolean): typeof ApplicationInsights; + + /** + * Sets the state of enabling offline mode to cache event when client is offline (disabled by default) + * @param value if true events that happen while client is offline will be cahced on disk, + * client will retry to send events when back online + * @returns {ApplicationInsights} this class + */ + function setOfflineMode(value: boolean): typeof ApplicationInsights; + /** + * Enables verbose debug logging + * @returns {ApplicationInsights} this class + */ + function enableVerboseLogging(): typeof ApplicationInsights; +} + +declare module 'applicationinsights' { + export = ApplicationInsights; +} diff --git a/src/typings/bootstrap.d.ts b/src/typings/bootstrap.d.ts new file mode 100644 index 0000000000..e7d8b9c21e --- /dev/null +++ b/src/typings/bootstrap.d.ts @@ -0,0 +1,124 @@ +// Generated by typings +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/e94e9a86308b7306bb74a973c4e18f37895f7298/bootstrap/index.d.ts +interface ModalOptions { + backdrop?: boolean|string; + keyboard?: boolean; + show?: boolean; + remote?: string; +} + +interface ModalOptionsBackdropString { + backdrop?: string; // for "static" + keyboard?: boolean; + show?: boolean; + remote?: string; +} + +interface ScrollSpyOptions { + offset?: number; + target?: string; +} + +interface TooltipOptions { + animation?: boolean; + html?: boolean; + placement?: string | Function; + selector?: string; + title?: string | Function; + trigger?: string; + template?: string; + delay?: number | Object; + container?: string | boolean; + viewport?: string | Function | Object; +} + +interface PopoverOptions { + animation?: boolean; + html?: boolean; + placement?: string | Function; + selector?: string; + trigger?: string; + title?: string | Function; + template?: string; + content?: any; + delay?: number | Object; + container?: string | boolean; + viewport?: string | Function | Object; +} + +interface CollapseOptions { + parent?: any; + toggle?: boolean; +} + +interface CarouselOptions { + interval?: number; + pause?: string; + wrap?: boolean; + keybord?: boolean; +} + +interface TypeaheadOptions { + source?: any; + items?: number; + minLength?: number; + matcher?: (item: any) => boolean; + sorter?: (items: any[]) => any[]; + updater?: (item: any) => any; + highlighter?: (item: any) => string; +} + +interface AffixOptions { + offset?: number | Function | Object; + target?: any; +} + +interface TransitionEventNames { + end: string; +} + +interface JQuery { + modal(options?: ModalOptions): JQuery; + modal(options?: ModalOptionsBackdropString): JQuery; + modal(command: string): JQuery; + + dropdown(): JQuery; + dropdown(command: string): JQuery; + + scrollspy(command: string): JQuery; + scrollspy(options?: ScrollSpyOptions): JQuery; + + tab(): JQuery; + tab(command: string): JQuery; + + tooltip(options?: TooltipOptions): JQuery; + tooltip(command: string): JQuery; + + popover(options?: PopoverOptions): JQuery; + popover(command: string): JQuery; + + alert(): JQuery; + alert(command: string): JQuery; + + button(): JQuery; + button(command: string): JQuery; + + collapse(options?: CollapseOptions): JQuery; + collapse(command: string): JQuery; + + carousel(options?: CarouselOptions): JQuery; + carousel(command: string): JQuery; + + typeahead(options?: TypeaheadOptions): JQuery; + + affix(options?: AffixOptions): JQuery; + + emulateTransitionEnd(duration: number): JQuery; +} + +interface JQuerySupport { + transition: boolean | TransitionEventNames; +} + +declare module "bootstrap" { +} diff --git a/src/typings/chokidar.d.ts b/src/typings/chokidar.d.ts new file mode 100644 index 0000000000..89816f46ea --- /dev/null +++ b/src/typings/chokidar.d.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'chokidar' { + + /** + * takes paths to be watched recursively and options + */ + export function watch(paths: string, options: IOptions): FSWatcher; + + export interface IOptions { + + /** + * (regexp or function) files to be ignored. This function or regexp is tested against the whole path, not just filename. + * If it is a function with two arguments, it gets called twice per path - once with a single argument (the path), second time with two arguments (the path and the fs.Stats object of that path). + */ + ignored?: any; + + /** + * (default: false). Indicates whether the process should continue to run as long as files are being watched. + */ + persistent?: boolean; + + /** + * (default: false). Indicates whether to watch files that don't have read permissions. + */ + ignorePermissionErrors?: boolean; + + /** + * (default: false). Indicates whether chokidar should ignore the initial add events or not. + */ + ignoreInitial?: boolean; + + /** + * (default: 100). Interval of file system polling. + */ + interval?: number; + + /** + * (default: 300). Interval of file system polling for binary files (see extensions in src/is-binary). + */ + binaryInterval?: number; + + /** + * (default: false on Windows, true on Linux and OS X). Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU utilization, consider setting this to false. + */ + usePolling?: boolean; + + /** + * (default: true on OS X). Whether to use the fsevents watching interface if available. When set to true explicitly and fsevents is available this supercedes the usePolling setting. When set to false on OS X, usePolling: true becomes the default. + */ + useFsEvents?: boolean; + + /** + * (default: true). When false, only the symlinks themselves will be watched for changes instead of following the link references and bubbling events through the link's path. + */ + followSymlinks?: boolean; + + /** + * (default: false). If set to true then the strings passed to .watch() and .add() are treated as literal path names, even if they look like globs. + */ + disableGlobbing?: boolean; + } + + export interface FSWatcher { + + add(fileDirOrGlob: string): void; + add(filesDirsOrGlobs: Array): void; + + unwatch(fileDirOrGlob: string): void; + unwatch(filesDirsOrGlobs: Array): void; + + /** + * Listen for an FS event. Available events: add, addDir, change, unlink, unlinkDir, error. Additionally all is available which gets emitted for every non-error event. + */ + on(event: string, clb: (type: string, path: string) => void): void; + on(event: string, clb: (error: Error) => void): void; + + /** + * Removes all listeners from watched files. + */ + close(): void; + + options: IOptions; + } +} \ No newline at end of file diff --git a/src/typings/comment-json.d.ts b/src/typings/comment-json.d.ts new file mode 100644 index 0000000000..c3a4b2203c --- /dev/null +++ b/src/typings/comment-json.d.ts @@ -0,0 +1,10 @@ +// Type definitions for comment-json 1.1 +// Project: https://github.com/kaelzhang/node-comment-json +// Definitions by: Jason Dent +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module "comment-json" { +export type Reviver = (k: number | string, v: any) => any; +export function parse(json: string, reviver?: Reviver, removes_comments?: boolean): any; +export function stringify(value: any, replacer?: any, space?: string | number): string; +} \ No newline at end of file diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts new file mode 100644 index 0000000000..c4c8a73ecf --- /dev/null +++ b/src/typings/electron.d.ts @@ -0,0 +1,6038 @@ +// Type definitions for Electron v1.4.6 +// Project: http://electron.atom.io/ +// Definitions by: jedmao , rhysd , Milan Burda , aliib +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/// + +declare namespace Electron { + + interface Event { + preventDefault: Function; + sender: NodeJS.EventEmitter; + } + + type Point = { + x: number; + y: number; + } + + type Size = { + width: number; + height: number; + } + + type Rectangle = { + x: number; + y: number; + width: number; + height: number; + } + + interface Destroyable { + /** + * Destroys the object. + */ + destroy(): void; + /** + * @returns Whether the object is destroyed. + */ + isDestroyed(): boolean; + } + + // https://github.com/electron/electron/blob/master/docs/api/app.md + + /** + * The app module is responsible for controlling the application's lifecycle. + */ + interface App extends NodeJS.EventEmitter { + /** + * Emitted when the application has finished basic startup. + * On Windows and Linux, the will-finish-launching event + * is the same as the ready event; on macOS, this event represents + * the applicationWillFinishLaunching notification of NSApplication. + * You would usually set up listeners for the open-file and open-url events here, + * and start the crash reporter and auto updater. + * + * In most cases, you should just do everything in the ready event handler. + */ + on(event: 'will-finish-launching', listener: Function): this; + /** + * Emitted when Electron has finished initialization. + */ + on(event: 'ready', listener: (event: Event, launchInfo: Object) => void): this; + /** + * Emitted when all windows have been closed. + * + * If you do not subscribe to this event and all windows are closed, + * the default behavior is to quit the app; however, if you subscribe, + * you control whether the app quits or not. + * If the user pressed Cmd + Q, or the developer called app.quit(), + * Electron will first try to close all the windows and then emit the will-quit event, + * and in this case the window-all-closed event would not be emitted. + */ + on(event: 'window-all-closed', listener: Function): this; + /** + * Emitted before the application starts closing its windows. + * Calling event.preventDefault() will prevent the default behaviour, which is terminating the application. + */ + on(event: 'before-quit', listener: (event: Event) => void): this; + /** + * Emitted when all windows have been closed and the application will quit. + * Calling event.preventDefault() will prevent the default behaviour, which is terminating the application. + */ + on(event: 'will-quit', listener: (event: Event) => void): this; + /** + * Emitted when the application is quitting. + */ + on(event: 'quit', listener: (event: Event, exitCode: number) => void): this; + /** + * Emitted when the user wants to open a file with the application. + * The open-file event is usually emitted when the application is already open + * and the OS wants to reuse the application to open the file. + * open-file is also emitted when a file is dropped onto the dock and the application + * is not yet running. Make sure to listen for the open-file event very early + * in your application startup to handle this case (even before the ready event is emitted). + * + * You should call event.preventDefault() if you want to handle this event. + * + * Note: This is only implemented on macOS. + */ + on(event: 'open-file', listener: (event: Event, url: string) => void): this; + /** + * Emitted when the user wants to open a URL with the application. + * The URL scheme must be registered to be opened by your application. + * + * You should call event.preventDefault() if you want to handle this event. + * + * Note: This is only implemented on macOS. + */ + on(event: 'open-url', listener: (event: Event, url: string) => void): this; + /** + * Emitted when the application is activated, which usually happens when clicks on the applications’s dock icon. + * Note: This is only implemented on macOS. + */ + on(event: 'activate', listener: Function): this; + /** + * Emitted during Handoff when an activity from a different device wants to be resumed. + * You should call event.preventDefault() if you want to handle this event. + */ + on(event: 'continue-activity', listener: (event: Event, type: string, userInfo: Object) => void): this; + /** + * Emitted when a browserWindow gets blurred. + */ + on(event: 'browser-window-blur', listener: (event: Event, browserWindow: BrowserWindow) => void): this; + /** + * Emitted when a browserWindow gets focused. + */ + on(event: 'browser-window-focus', listener: (event: Event, browserWindow: BrowserWindow) => void): this; + /** + * Emitted when a new browserWindow is created. + */ + on(event: 'browser-window-created', listener: (event: Event, browserWindow: BrowserWindow) => void): this; + /** + * Emitted when a new webContents is created. + */ + on(event: 'web-contents-created', listener: (event: Event, webContents: WebContents) => void): this; + /** + * Emitted when failed to verify the certificate for url, to trust the certificate + * you should prevent the default behavior with event.preventDefault() and call callback(true). + */ + on(event: 'certificate-error', listener: (event: Event, + webContents: WebContents, + url: string, + error: string, + certificate: Certificate, + callback: (trust: boolean) => void + ) => void): this; + /** + * Emitted when a client certificate is requested. + * + * The url corresponds to the navigation entry requesting the client certificate + * and callback needs to be called with an entry filtered from the list. + * Using event.preventDefault() prevents the application from using the first certificate from the store. + */ + on(event: 'select-client-certificate', listener: (event: Event, + webContents: WebContents, + url: string, + certificateList: Certificate[], + callback: (certificate: Certificate) => void + ) => void): this; + /** + * Emitted when webContents wants to do basic auth. + * + * The default behavior is to cancel all authentications, to override this + * you should prevent the default behavior with event.preventDefault() + * and call callback(username, password) with the credentials. + */ + on(event: 'login', listener: (event: Event, + webContents: WebContents, + request: LoginRequest, + authInfo: LoginAuthInfo, + callback: (username: string, password: string) => void + ) => void): this; + /** + * Emitted when the gpu process crashes. + */ + on(event: 'gpu-process-crashed', listener: (event: Event, killed: boolean) => void): this; + /** + * Emitted when Chrome's accessibility support changes. + * + * Note: This API is only available on macOS and Windows. + */ + on(event: 'accessibility-support-changed', listener: (event: Event, accessibilitySupportEnabled: boolean) => void): this; + on(event: string, listener: Function): this; + /** + * Try to close all windows. The before-quit event will first be emitted. + * If all windows are successfully closed, the will-quit event will be emitted + * and by default the application would be terminated. + * + * This method guarantees all beforeunload and unload handlers are correctly + * executed. It is possible that a window cancels the quitting by returning + * false in beforeunload handler. + */ + quit(): void; + /** + * Exits immediately with exitCode. + * All windows will be closed immediately without asking user + * and the before-quit and will-quit events will not be emitted. + */ + exit(exitCode?: number): void; + /** + * Relaunches the app when current instance exits. + * + * By default the new instance will use the same working directory + * and command line arguments with current instance. + * When args is specified, the args will be passed as command line arguments instead. + * When execPath is specified, the execPath will be executed for relaunch instead of current app. + * + * Note that this method does not quit the app when executed, you have to call app.quit + * or app.exit after calling app.relaunch to make the app restart. + * + * When app.relaunch is called for multiple times, multiple instances + * will be started after current instance exited. + */ + relaunch(options?: { + args?: string[], + execPath?: string + }): void; + /** + * @returns Whether Electron has finished initializing. + */ + isReady(): boolean; + /** + * On Linux, focuses on the first visible window. + * On macOS, makes the application the active app. + * On Windows, focuses on the application’s first window. + */ + focus(): void; + /** + * Hides all application windows without minimizing them. + * Note: This is only implemented on macOS. + */ + hide(): void; + /** + * Shows application windows after they were hidden. Does not automatically focus them. + * Note: This is only implemented on macOS. + */ + show(): void; + /** + * Returns the current application directory. + */ + getAppPath(): string; + /** + * @returns The path to a special directory or file associated with name. + * On failure an Error would throw. + */ + getPath(name: AppPathName): string; + /** + * Overrides the path to a special directory or file associated with name. + * If the path specifies a directory that does not exist, the directory will + * be created by this method. On failure an Error would throw. + * + * You can only override paths of names defined in app.getPath. + * + * By default web pages' cookies and caches will be stored under userData + * directory, if you want to change this location, you have to override the + * userData path before the ready event of app module gets emitted. + */ + setPath(name: AppPathName, path: string): void; + /** + * @returns The version of loaded application, if no version is found in + * application's package.json, the version of current bundle or executable. + */ + getVersion(): string; + /** + * @returns The current application's name, the name in package.json would be used. + * Usually the name field of package.json is a short lowercased name, according to + * the spec of npm modules. So usually you should also specify a productName field, + * which is your application's full capitalized name, and it will be preferred over + * name by Electron. + */ + getName(): string; + /** + * Overrides the current application's name. + */ + setName(name: string): void; + /** + * @returns The current application locale. + */ + getLocale(): string; + /** + * Adds path to recent documents list. + * + * This list is managed by the system, on Windows you can visit the list from + * task bar, and on macOS you can visit it from dock menu. + * + * Note: This is only implemented on macOS and Windows. + */ + addRecentDocument(path: string): void; + /** + * Clears the recent documents list. + * + * Note: This is only implemented on macOS and Windows. + */ + clearRecentDocuments(): void; + /** + * Sets the current executable as the default handler for a protocol (aka URI scheme). + * Once registered, all links with your-protocol:// will be opened with the current executable. + * The whole link, including protocol, will be passed to your application as a parameter. + * + * On Windows you can provide optional parameters path, the path to your executable, + * and args, an array of arguments to be passed to your executable when it launches. + * + * @param protocol The name of your protocol, without ://. + * @param path Defaults to process.execPath. + * @param args Defaults to an empty array. + * + * Note: This is only implemented on macOS and Windows. + * On macOS, you can only register protocols that have been added to your app's info.plist. + */ + setAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; + /** + * Removes the current executable as the default handler for a protocol (aka URI scheme). + * + * @param protocol The name of your protocol, without ://. + * @param path Defaults to process.execPath. + * @param args Defaults to an empty array. + * + * Note: This is only implemented on macOS and Windows. + */ + removeAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; + /** + * @param protocol The name of your protocol, without ://. + * @param path Defaults to process.execPath. + * @param args Defaults to an empty array. + * + * @returns Whether the current executable is the default handler for a protocol (aka URI scheme). + * + * Note: This is only implemented on macOS and Windows. + */ + isDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; + /** + * Adds tasks to the Tasks category of JumpList on Windows. + * + * Note: This API is only available on Windows. + */ + setUserTasks(tasks: Task[]): boolean; + /** + * Note: This API is only available on Windows. + */ + getJumpListSettings(): JumpListSettings; + /** + * Sets or removes a custom Jump List for the application. + * + * If categories is null the previously set custom Jump List (if any) will be replaced + * by the standard Jump List for the app (managed by Windows). + * + * Note: This API is only available on Windows. + */ + setJumpList(categories: JumpListCategory[]): SetJumpListResult; + /** + * This method makes your application a Single Instance Application instead of allowing + * multiple instances of your app to run, this will ensure that only a single instance + * of your app is running, and other instances signal this instance and exit. + */ + makeSingleInstance(callback: (args: string[], workingDirectory: string) => void): boolean; + /** + * Releases all locks that were created by makeSingleInstance. This will allow + * multiple instances of the application to once again run side by side. + */ + releaseSingleInstance(): void; + /** + * Creates an NSUserActivity and sets it as the current activity. + * The activity is eligible for Handoff to another device afterward. + * + * @param type Uniquely identifies the activity. Maps to NSUserActivity.activityType. + * @param userInfo App-specific state to store for use by another device. + * @param webpageURL The webpage to load in a browser if no suitable app is + * installed on the resuming device. The scheme must be http or https. + * + * Note: This API is only available on macOS. + */ + setUserActivity(type: string, userInfo: Object, webpageURL?: string): void; + /** + * @returns The type of the currently running activity. + * + * Note: This API is only available on macOS. + */ + getCurrentActivityType(): string; + /** + * Changes the Application User Model ID to id. + * + * Note: This is only implemented on Windows. + */ + setAppUserModelId(id: string): void; + /** + * Imports the certificate in pkcs12 format into the platform certificate store. + * @param callback Called with the result of import operation, a value of 0 indicates success + * while any other value indicates failure according to chromium net_error_list. + * + * Note: This API is only available on Linux. + */ + importCertificate(options: ImportCertificateOptions, callback: (result: number) => void): void; + /** + * Disables hardware acceleration for current app. + * This method can only be called before app is ready. + */ + disableHardwareAcceleration(): void; + /** + * @returns whether current desktop environment is Unity launcher. (Linux) + * + * Note: This API is only available on Linux. + */ + isUnityRunning(): boolean; + /** + * Returns a Boolean, true if Chrome's accessibility support is enabled, false otherwise. + * This API will return true if the use of assistive technologies, such as screen readers, + * has been detected. + * See https://www.chromium.org/developers/design-documents/accessibility for more details. + * + * Note: This API is only available on macOS and Windows. + */ + isAccessibilitySupportEnabled(): boolean; + /** + * @returns an Object with the login item settings of the app. + * + * Note: This API is only available on macOS and Windows. + */ + getLoginItemSettings(): LoginItemSettings; + /** + * Set the app's login item settings. + * + * Note: This API is only available on macOS and Windows. + */ + setLoginItemSettings(settings: LoginItemSettings): void; + /** + * Set the about panel options. This will override the values defined in the app's .plist file. + * See the Apple docs for more details. + * + * Note: This API is only available on macOS. + */ + setAboutPanelOptions(options: AboutPanelOptions): void; + commandLine: CommandLine; + /** + * Note: This API is only available on macOS. + */ + dock: Dock; + } + + type AppPathName = 'home' | 'appData' | 'userData' | 'temp' | 'exe' | 'module' | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos' | 'pepperFlashSystemPlugin'; + + interface ImportCertificateOptions { + /** + * Path for the pkcs12 file. + */ + certificate: string; + /** + * Passphrase for the certificate. + */ + password: string; + } + + interface CommandLine { + /** + * Append a switch [with optional value] to Chromium's command line. + * + * Note: This will not affect process.argv, and is mainly used by developers + * to control some low-level Chromium behaviors. + */ + appendSwitch(_switch: string, value?: string): void; + /** + * Append an argument to Chromium's command line. The argument will quoted properly. + * + * Note: This will not affect process.argv. + */ + appendArgument(value: string): void; + } + + interface Dock { + /** + * When critical is passed, the dock icon will bounce until either the + * application becomes active or the request is canceled. + * + * When informational is passed, the dock icon will bounce for one second. + * However, the request remains active until either the application becomes + * active or the request is canceled. + * + * @param type The default is informational. + * @returns An ID representing the request. + */ + bounce(type?: 'critical' | 'informational'): number; + /** + * Cancel the bounce of id. + * + * Note: This API is only available on macOS. + */ + cancelBounce(id: number): void; + /** + * Bounces the Downloads stack if the filePath is inside the Downloads folder. + * + * Note: This API is only available on macOS. + */ + downloadFinished(filePath: string): void; + /** + * Sets the string to be displayed in the dock’s badging area. + * + * Note: This API is only available on macOS. + */ + setBadge(text: string): void; + /** + * Returns the badge string of the dock. + * + * Note: This API is only available on macOS. + */ + getBadge(): string; + /** + * Sets the counter badge for current app. Setting the count to 0 will hide the badge. + * + * @returns True when the call succeeded, otherwise returns false. + * + * Note: This API is only available on macOS and Linux. + */ + setBadgeCount(count: number): boolean; + /** + * @returns The current value displayed in the counter badge. + * + * Note: This API is only available on macOS and Linux. + */ + getBadgeCount(): number; + /** + * Hides the dock icon. + * + * Note: This API is only available on macOS. + */ + hide(): void; + /** + * Shows the dock icon. + * + * Note: This API is only available on macOS. + */ + show(): void; + /** + * @returns Whether the dock icon is visible. + * The app.dock.show() call is asynchronous so this method might not return true immediately after that call. + * + * Note: This API is only available on macOS. + */ + isVisible(): boolean; + /** + * Sets the application dock menu. + * + * Note: This API is only available on macOS. + */ + setMenu(menu: Menu): void; + /** + * Sets the image associated with this dock icon. + * + * Note: This API is only available on macOS. + */ + setIcon(icon: NativeImage | string): void; + } + + interface Task { + /** + * Path of the program to execute, usually you should specify process.execPath + * which opens current program. + */ + program: string; + /** + * The arguments of command line when program is executed. + */ + arguments: string; + /** + * The string to be displayed in a JumpList. + */ + title: string; + /** + * Description of this task. + */ + description?: string; + /** + * The absolute path to an icon to be displayed in a JumpList, it can be + * arbitrary resource file that contains an icon, usually you can specify + * process.execPath to show the icon of the program. + */ + iconPath: string; + /** + * The icon index in the icon file. If an icon file consists of two or more + * icons, set this value to identify the icon. If an icon file consists of + * one icon, this value is 0. + */ + iconIndex?: number; + } + + /** + * ok - Nothing went wrong. + * error - One or more errors occured, enable runtime logging to figure out the likely cause. + * invalidSeparatorError - An attempt was made to add a separator to a custom category in the Jump List. + * Separators are only allowed in the standard Tasks category. + * fileTypeRegistrationError - An attempt was made to add a file link to the Jump List + * for a file type the app isn't registered to handle. + * customCategoryAccessDeniedError - Custom categories can't be added to the Jump List + * due to user privacy or group policy settings. + */ + type SetJumpListResult = 'ok' | 'error' | 'invalidSeparatorError' | 'fileTypeRegistrationError' | 'customCategoryAccessDeniedError'; + + interface JumpListSettings { + /** + * The minimum number of items that will be shown in the Jump List. + */ + minItems: number; + /** + * Items that the user has explicitly removed from custom categories in the Jump List. + */ + removedItems: JumpListItem[]; + } + + interface JumpListCategory { + /** + * tasks - Items in this category will be placed into the standard Tasks category. + * frequent - Displays a list of files frequently opened by the app, the name of the category and its items are set by Windows. + * recent - Displays a list of files recently opened by the app, the name of the category and its items are set by Windows. + * custom - Displays tasks or file links, name must be set by the app. + */ + type?: 'tasks' | 'frequent' | 'recent' | 'custom'; + /** + * Must be set if type is custom, otherwise it should be omitted. + */ + name?: string; + /** + * Array of JumpListItem objects if type is tasks or custom, otherwise it should be omitted. + */ + items?: JumpListItem[]; + } + + interface JumpListItem { + /** + * task - A task will launch an app with specific arguments. + * separator - Can be used to separate items in the standard Tasks category. + * file - A file link will open a file using the app that created the Jump List. + */ + type: 'task' | 'separator' | 'file'; + /** + * Path of the file to open, should only be set if type is file. + */ + path?: string; + /** + * Path of the program to execute, usually you should specify process.execPath which opens the current program. + * Should only be set if type is task. + */ + program?: string; + /** + * The command line arguments when program is executed. Should only be set if type is task. + */ + args?: string; + /** + * The text to be displayed for the item in the Jump List. Should only be set if type is task. + */ + title?: string; + /** + * Description of the task (displayed in a tooltip). Should only be set if type is task. + */ + description?: string; + /** + * The absolute path to an icon to be displayed in a Jump List, which can be an arbitrary + * resource file that contains an icon (e.g. .ico, .exe, .dll). + * You can usually specify process.execPath to show the program icon. + */ + iconPath?: string; + /** + * The index of the icon in the resource file. If a resource file contains multiple icons + * this value can be used to specify the zero-based index of the icon that should be displayed + * for this task. If a resource file contains only one icon, this property should be set to zero. + */ + iconIndex?: number; + } + + interface LoginItemSettings { + /** + * True if the app is set to open at login. + */ + openAtLogin: boolean; + /** + * True if the app is set to open as hidden at login. This setting is only supported on macOS. + */ + openAsHidden: boolean; + /** + * True if the app was opened at login automatically. This setting is only supported on macOS. + */ + wasOpenedAtLogin?: boolean; + /** + * True if the app was opened as a hidden login item. This indicates that the app should not + * open any windows at startup. This setting is only supported on macOS. + */ + wasOpenedAsHidden?: boolean; + /** + * True if the app was opened as a login item that should restore the state from the previous session. + * This indicates that the app should restore the windows that were open the last time the app was closed. + * This setting is only supported on macOS. + */ + restoreState?: boolean; + } + + interface AboutPanelOptions { + /** + * The app's name. + */ + applicationName?: string; + /** + * The app's version. + */ + applicationVersion?: string; + /** + * Copyright information. + */ + copyright?: string; + /** + * Credit information. + */ + credits?: string; + /** + * The app's build version number. + */ + version?: string; + } + + // https://github.com/electron/electron/blob/master/docs/api/auto-updater.md + + /** + * This module provides an interface for the Squirrel auto-updater framework. + */ + interface AutoUpdater extends NodeJS.EventEmitter { + /** + * Emitted when there is an error while updating. + */ + on(event: 'error', listener: (error: Error) => void): this; + /** + * Emitted when checking if an update has started. + */ + on(event: 'checking-for-update', listener: Function): this; + /** + * Emitted when there is an available update. The update is downloaded automatically. + */ + on(event: 'update-available', listener: Function): this; + /** + * Emitted when there is no available update. + */ + on(event: 'update-not-available', listener: Function): this; + /** + * Emitted when an update has been downloaded. + * Note: On Windows only releaseName is available. + */ + on(event: 'update-downloaded', listener: (event: Event, releaseNotes: string, releaseName: string, releaseDate: Date, updateURL: string) => void): this; + on(event: string, listener: Function): this; + /** + * Set the url and initialize the auto updater. + */ + setFeedURL(url: string, requestHeaders?: Headers): void; + /** + * @returns The current update feed URL. + */ + getFeedURL(): string; + /** + * Ask the server whether there is an update, you have to call setFeedURL + * before using this API + */ + checkForUpdates(): void; + /** + * Restarts the app and installs the update after it has been downloaded. + * It should only be called after update-downloaded has been emitted. + */ + quitAndInstall(): void; + } + + // https://github.com/electron/electron/blob/master/docs/api/browser-window.md + + /** + * The BrowserWindow class gives you ability to create a browser window. + * You can also create a window without chrome by using Frameless Window API. + */ + class BrowserWindow extends NodeJS.EventEmitter implements Destroyable { + /** + * Emitted when the document changed its title, + * calling event.preventDefault() would prevent the native window’s title to change. + */ + on(event: 'page-title-updated', listener: (event: Event, title: string) => void): this; + /** + * Emitted when the window is going to be closed. It’s emitted before the beforeunload + * and unload event of the DOM. Calling event.preventDefault() will cancel the close. + */ + on(event: 'close', listener: (event: Event) => void): this; + /** + * Emitted when the window is closed. After you have received this event + * you should remove the reference to the window and avoid using it anymore. + */ + on(event: 'closed', listener: Function): this; + /** + * Emitted when the web page becomes unresponsive. + */ + on(event: 'unresponsive', listener: Function): this; + /** + * Emitted when the unresponsive web page becomes responsive again. + */ + on(event: 'responsive', listener: Function): this; + /** + * Emitted when the window loses focus. + */ + on(event: 'blur', listener: Function): this; + /** + * Emitted when the window gains focus. + */ + on(event: 'focus', listener: Function): this; + /** + * Emitted when the window is shown. + */ + on(event: 'show', listener: Function): this; + /** + * Emitted when the window is hidden. + */ + on(event: 'hide', listener: Function): this; + /** + * Emitted when the web page has been rendered and window can be displayed without visual flash. + */ + on(event: 'ready-to-show', listener: Function): this; + /** + * Emitted when window is maximized. + */ + on(event: 'maximize', listener: Function): this; + /** + * Emitted when the window exits from maximized state. + */ + on(event: 'unmaximize', listener: Function): this; + /** + * Emitted when the window is minimized. + */ + on(event: 'minimize', listener: Function): this; + /** + * Emitted when the window is restored from minimized state. + */ + on(event: 'restore', listener: Function): this; + /** + * Emitted when the window is getting resized. + */ + on(event: 'resize', listener: Function): this; + /** + * Emitted when the window is getting moved to a new position. + */ + on(event: 'move', listener: Function): this; + /** + * Emitted when the window enters full screen state. + */ + on(event: 'enter-full-screen', listener: Function): this; + /** + * Emitted when the window leaves full screen state. + */ + on(event: 'leave-full-screen', listener: Function): this; + /** + * Emitted when the window enters full screen state triggered by HTML API. + */ + on(event: 'enter-html-full-screen', listener: Function): this; + /** + * Emitted when the window leaves full screen state triggered by HTML API. + */ + on(event: 'leave-html-full-screen', listener: Function): this; + /** + * Emitted when an App Command is invoked. These are typically related + * to keyboard media keys or browser commands, as well as the "Back" / + * "Forward" buttons built into some mice on Windows. + * Note: This is only implemented on Windows. + */ + on(event: 'app-command', listener: (event: Event, command: string) => void): this; + /** + * Emitted when scroll wheel event phase has begun. + * Note: This is only implemented on macOS. + */ + on(event: 'scroll-touch-begin', listener: Function): this; + /** + * Emitted when scroll wheel event phase has ended. + * Note: This is only implemented on macOS. + */ + on(event: 'scroll-touch-end', listener: Function): this; + /** + * Emitted when scroll wheel event phase filed upon reaching the edge of element. + * Note: This is only implemented on macOS. + */ + on(event: 'scroll-touch-edge', listener: Function): this; + /** + * Emitted on 3-finger swipe. + * Note: This is only implemented on macOS. + */ + on(event: 'swipe', listener: (event: Event, direction: SwipeDirection) => void): this; + on(event: string, listener: Function): this; + /** + * Creates a new BrowserWindow with native properties as set by the options. + */ + constructor(options?: BrowserWindowOptions); + /** + * @returns All opened browser windows. + */ + static getAllWindows(): BrowserWindow[]; + /** + * @returns The window that is focused in this application. + */ + static getFocusedWindow(): BrowserWindow; + /** + * Find a window according to the webContents it owns. + */ + static fromWebContents(webContents: WebContents): BrowserWindow; + /** + * Find a window according to its ID. + */ + static fromId(id: number): BrowserWindow; + /** + * Adds devtools extension located at path. The extension will be remembered + * so you only need to call this API once, this API is not for programming use. + * @returns The extension's name. + * + * Note: This API cannot be called before the ready event of the app module is emitted. + */ + static addDevToolsExtension(path: string): string; + /** + * Remove a devtools extension. + * @param name The name of the devtools extension to remove. + * + * Note: This API cannot be called before the ready event of the app module is emitted. + */ + static removeDevToolsExtension(name: string): void; + /** + * @returns devtools extensions. + * + * Note: This API cannot be called before the ready event of the app module is emitted. + */ + static getDevToolsExtensions(): DevToolsExtensions; + /** + * The WebContents object this window owns, all web page related events and + * operations would be done via it. + * Note: Users should never store this object because it may become null when + * the renderer process (web page) has crashed. + */ + webContents: WebContents; + /** + * Get the unique ID of this window. + */ + id: number; + /** + * Force closing the window, the unload and beforeunload event won't be emitted + * for the web page, and close event would also not be emitted for this window, + * but it would guarantee the closed event to be emitted. + * You should only use this method when the renderer process (web page) has crashed. + */ + destroy(): void; + /** + * Try to close the window, this has the same effect with user manually clicking + * the close button of the window. The web page may cancel the close though, + * see the close event. + */ + close(): void; + /** + * Focus on the window. + */ + focus(): void; + /** + * Remove focus on the window. + */ + blur(): void; + /** + * @returns Whether the window is focused. + */ + isFocused(): boolean; + /** + * @returns Whether the window is destroyed. + */ + isDestroyed(): boolean; + /** + * Shows and gives focus to the window. + */ + show(): void; + /** + * Shows the window but doesn't focus on it. + */ + showInactive(): void; + /** + * Hides the window. + */ + hide(): void; + /** + * @returns Whether the window is visible to the user. + */ + isVisible(): boolean; + /** + * @returns Whether the window is a modal window. + */ + isModal(): boolean; + /** + * Maximizes the window. + */ + maximize(): void; + /** + * Unmaximizes the window. + */ + unmaximize(): void; + /** + * @returns Whether the window is maximized. + */ + isMaximized(): boolean; + /** + * Minimizes the window. On some platforms the minimized window will be + * shown in the Dock. + */ + minimize(): void; + /** + * Restores the window from minimized state to its previous state. + */ + restore(): void; + /** + * @returns Whether the window is minimized. + */ + isMinimized(): boolean; + /** + * Sets whether the window should be in fullscreen mode. + */ + setFullScreen(flag: boolean): void; + /** + * @returns Whether the window is in fullscreen mode. + */ + isFullScreen(): boolean; + /** + * This will have a window maintain an aspect ratio. + * The extra size allows a developer to have space, specified in pixels, + * not included within the aspect ratio calculations. + * This API already takes into account the difference between a window’s size and its content size. + * + * Note: This API is available only on macOS. + */ + setAspectRatio(aspectRatio: number, extraSize?: Size): void; + /** + * Uses Quick Look to preview a file at a given path. + * + * @param path The absolute path to the file to preview with QuickLook. + * @param displayName The name of the file to display on the Quick Look modal view. + * Note: This API is available only on macOS. + */ + previewFile(path: string, displayName?: string): void; + /** + * Resizes and moves the window to width, height, x, y. + */ + setBounds(options: Rectangle, animate?: boolean): void; + /** + * @returns The window's width, height, x and y values. + */ + getBounds(): Rectangle; + /** + * Resizes and moves the window's client area (e.g. the web page) to width, height, x, y. + */ + setContentBounds(options: Rectangle, animate?: boolean): void; + /** + * @returns The window's client area (e.g. the web page) width, height, x and y values. + */ + getContentBounds(): Rectangle; + /** + * Resizes the window to width and height. + */ + setSize(width: number, height: number, animate?: boolean): void; + /** + * @returns The window's width and height. + */ + getSize(): number[]; + /** + * Resizes the window's client area (e.g. the web page) to width and height. + */ + setContentSize(width: number, height: number, animate?: boolean): void; + /** + * @returns The window's client area's width and height. + */ + getContentSize(): number[]; + /** + * Sets the minimum size of window to width and height. + */ + setMinimumSize(width: number, height: number): void; + /** + * @returns The window's minimum width and height. + */ + getMinimumSize(): number[]; + /** + * Sets the maximum size of window to width and height. + */ + setMaximumSize(width: number, height: number): void; + /** + * @returns The window's maximum width and height. + */ + getMaximumSize(): number[]; + /** + * Sets whether the window can be manually resized by user. + */ + setResizable(resizable: boolean): void; + /** + * @returns Whether the window can be manually resized by user. + */ + isResizable(): boolean; + /** + * Sets whether the window can be moved by user. On Linux does nothing. + * Note: This API is available only on macOS and Windows. + */ + setMovable(movable: boolean): void; + /** + * Note: This API is available only on macOS and Windows. + * @returns Whether the window can be moved by user. On Linux always returns true. + */ + isMovable(): boolean; + /** + * Sets whether the window can be manually minimized by user. On Linux does nothing. + * Note: This API is available only on macOS and Windows. + */ + setMinimizable(minimizable: boolean): void; + /** + * Note: This API is available only on macOS and Windows. + * @returns Whether the window can be manually minimized by user. On Linux always returns true. + */ + isMinimizable(): boolean; + /** + * Sets whether the window can be manually maximized by user. On Linux does nothing. + * Note: This API is available only on macOS and Windows. + */ + setMaximizable(maximizable: boolean): void; + /** + * Note: This API is available only on macOS and Windows. + * @returns Whether the window can be manually maximized by user. On Linux always returns true. + */ + isMaximizable(): boolean; + /** + * Sets whether the maximize/zoom window button toggles fullscreen mode or maximizes the window. + */ + setFullScreenable(fullscreenable: boolean): void; + /** + * @returns Whether the maximize/zoom window button toggles fullscreen mode or maximizes the window. + */ + isFullScreenable(): boolean; + /** + * Sets whether the window can be manually closed by user. On Linux does nothing. + * Note: This API is available only on macOS and Windows. + */ + setClosable(closable: boolean): void; + /** + * Note: This API is available only on macOS and Windows. + * @returns Whether the window can be manually closed by user. On Linux always returns true. + */ + isClosable(): boolean; + /** + * Sets whether the window should show always on top of other windows. After + * setting this, the window is still a normal window, not a toolbox window + * which can not be focused on. + */ + setAlwaysOnTop(flag: boolean, level?: WindowLevel): void; + /** + * @returns Whether the window is always on top of other windows. + */ + isAlwaysOnTop(): boolean; + /** + * Moves window to the center of the screen. + */ + center(): void; + /** + * Moves window to x and y. + */ + setPosition(x: number, y: number, animate?: boolean): void; + /** + * @returns The window's current position. + */ + getPosition(): number[]; + /** + * Changes the title of native window to title. + */ + setTitle(title: string): void; + /** + * Note: The title of web page can be different from the title of the native window. + * @returns The title of the native window. + */ + getTitle(): string; + /** + * Changes the attachment point for sheets on macOS. + * Note: This API is available only on macOS. + */ + setSheetOffset(offsetY: number, offsetX?: number): void; + /** + * Starts or stops flashing the window to attract user's attention. + */ + flashFrame(flag: boolean): void; + /** + * Makes the window do not show in Taskbar. + */ + setSkipTaskbar(skip: boolean): void; + /** + * Enters or leaves the kiosk mode. + */ + setKiosk(flag: boolean): void; + /** + * @returns Whether the window is in kiosk mode. + */ + isKiosk(): boolean; + /** + * The native type of the handle is HWND on Windows, NSView* on macOS, + * and Window (unsigned long) on Linux. + * @returns The platform-specific handle of the window as Buffer. + */ + getNativeWindowHandle(): Buffer; + /** + * Hooks a windows message. The callback is called when the message is received in the WndProc. + * Note: This API is available only on Windows. + */ + hookWindowMessage(message: number, callback: Function): void; + /** + * @returns Whether the message is hooked. + */ + isWindowMessageHooked(message: number): boolean; + /** + * Unhook the window message. + */ + unhookWindowMessage(message: number): void; + /** + * Unhooks all of the window messages. + */ + unhookAllWindowMessages(): void; + /** + * Sets the pathname of the file the window represents, and the icon of the + * file will show in window's title bar. + * Note: This API is available only on macOS. + */ + setRepresentedFilename(filename: string): void; + /** + * Note: This API is available only on macOS. + * @returns The pathname of the file the window represents. + */ + getRepresentedFilename(): string; + /** + * Specifies whether the window’s document has been edited, and the icon in + * title bar will become grey when set to true. + * Note: This API is available only on macOS. + */ + setDocumentEdited(edited: boolean): void; + /** + * Note: This API is available only on macOS. + * @returns Whether the window's document has been edited. + */ + isDocumentEdited(): boolean; + focusOnWebView(): void; + blurWebView(): void; + /** + * Captures the snapshot of page within rect, upon completion the callback + * will be called. Omitting the rect would capture the whole visible page. + * Note: Be sure to read documents on remote buffer in remote if you are going + * to use this API in renderer process. + * @param callback Supplies the image that stores data of the snapshot. + */ + capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + /** + * Captures the snapshot of page within rect, upon completion the callback + * will be called. Omitting the rect would capture the whole visible page. + * Note: Be sure to read documents on remote buffer in remote if you are going + * to use this API in renderer process. + * @param callback Supplies the image that stores data of the snapshot. + */ + capturePage(callback: (image: NativeImage) => void): void; + /** + * Same as webContents.loadURL(url). + */ + loadURL(url: string, options?: LoadURLOptions): void; + /** + * Same as webContents.reload. + */ + reload(): void; + /** + * Sets the menu as the window top menu. + * Note: This API is not available on macOS. + */ + setMenu(menu: Menu): void; + /** + * Sets the progress value in the progress bar. + * On Linux platform, only supports Unity desktop environment, you need to + * specify the *.desktop file name to desktopName field in package.json. + * By default, it will assume app.getName().desktop. + * @param progress Valid range is [0, 1.0]. If < 0, the progress bar is removed. + * If greater than 0, it becomes indeterminate. + */ + setProgressBar(progress: number, options?: { + /** + * Mode for the progress bar. + * Note: This is only implemented on Windows. + */ + mode: 'none' | 'normal' | 'indeterminate' | 'error' | 'paused' + }): void; + /** + * Sets a 16px overlay onto the current Taskbar icon, usually used to convey + * some sort of application status or to passively notify the user. + * Note: This API is only available on Windows 7 or above. + * @param overlay The icon to display on the bottom right corner of the Taskbar + * icon. If this parameter is null, the overlay is cleared + * @param description Provided to Accessibility screen readers. + */ + setOverlayIcon(overlay: NativeImage, description: string): void; + /** + * Sets whether the window should have a shadow. On Windows and Linux does nothing. + * Note: This API is available only on macOS. + */ + setHasShadow(hasShadow: boolean): void; + /** + * Note: This API is available only on macOS. + * @returns whether the window has a shadow. On Windows and Linux always returns true. + */ + hasShadow(): boolean; + /** + * Add a thumbnail toolbar with a specified set of buttons to the thumbnail image + * of a window in a taskbar button layout. + * @returns Whether the thumbnail has been added successfully. + * + * Note: This API is available only on Windows. + */ + setThumbarButtons(buttons: ThumbarButton[]): boolean; + /** + * Sets the region of the window to show as the thumbnail image displayed when hovering + * over the window in the taskbar. You can reset the thumbnail to be the entire window + * by specifying an empty region: {x: 0, y: 0, width: 0, height: 0}. + * + * Note: This API is available only on Windows. + */ + setThumbnailClip(region: Rectangle): boolean; + /** + * Sets the toolTip that is displayed when hovering over the window thumbnail in the taskbar. + * Note: This API is available only on Windows. + */ + setThumbnailToolTip(toolTip: string): boolean; + /** + * Sets the application id, app icon, relaunch command and relaunch display name + * for the given window. appIconIndex should be set to 0 if the app icon + * file only has a single icon. + */ + setAppDetails(options: { + appId?: string; + appIconPath?: string; + appIconIndex?: number; + relaunchCommand?: string; + relaunchDisplayName?: string; + }): void; + /** + * Same as webContents.showDefinitionForSelection(). + * Note: This API is available only on macOS. + */ + showDefinitionForSelection(): void; + /** + * Changes window icon. + * Note: This API is not available on macOS. + */ + setIcon(icon: NativeImage): void; + /** + * Sets whether the window menu bar should hide itself automatically. Once set + * the menu bar will only show when users press the single Alt key. + * If the menu bar is already visible, calling setAutoHideMenuBar(true) won't + * hide it immediately. + */ + setAutoHideMenuBar(hide: boolean): void; + /** + * @returns Whether menu bar automatically hides itself. + */ + isMenuBarAutoHide(): boolean; + /** + * Sets whether the menu bar should be visible. If the menu bar is auto-hide, + * users can still bring up the menu bar by pressing the single Alt key. + */ + setMenuBarVisibility(visibile: boolean): void; + /** + * @returns Whether the menu bar is visible. + */ + isMenuBarVisible(): boolean; + /** + * Sets whether the window should be visible on all workspaces. + * Note: This API does nothing on Windows. + */ + setVisibleOnAllWorkspaces(visible: boolean): void; + /** + * Note: This API always returns false on Windows. + * @returns Whether the window is visible on all workspaces. + */ + isVisibleOnAllWorkspaces(): boolean; + /** + * Makes the window ignore all mouse events. + * + * All mouse events happened in this window will be passed to the window below this window, + * but if this window has focus, it will still receive keyboard events. + */ + setIgnoreMouseEvents(ignore: boolean): void; + /** + * Prevents the window contents from being captured by other apps. + * + * On macOS it sets the NSWindow's sharingType to NSWindowSharingNone. + * On Windows it calls SetWindowDisplayAffinity with WDA_MONITOR. + */ + setContentProtection(enable: boolean): void; + /** + * Changes whether the window can be focused. + * Note: This API is available only on Windows. + */ + setFocusable(focusable: boolean): void; + /** + * Sets parent as current window's parent window, + * passing null will turn current window into a top-level window. + * Note: This API is not available on Windows. + */ + setParentWindow(parent: BrowserWindow): void; + /** + * @returns The parent window. + */ + getParentWindow(): BrowserWindow; + /** + * @returns All child windows. + */ + getChildWindows(): BrowserWindow[]; + } + + type WindowLevel = 'normal' | 'floating' | 'torn-off-menu' | 'modal-panel' | 'main-menu' | 'status' | 'pop-up-menu' | 'screen-saver' | 'dock'; + type SwipeDirection = 'up' | 'right' | 'down' | 'left'; + type ThumbarButtonFlags = 'enabled' | 'disabled' | 'dismissonclick' | 'nobackground' | 'hidden' | 'noninteractive'; + + interface ThumbarButton { + icon: NativeImage | string; + click: Function; + tooltip?: string; + flags?: ThumbarButtonFlags[]; + } + + interface DevToolsExtensions { + [name: string]: { + name: string; + value: string; + } + } + + interface WebPreferences { + /** + * Whether to enable DevTools. + * If it is set to false, can not use BrowserWindow.webContents.openDevTools() to open DevTools. + * Default: true. + */ + devTools?: boolean; + /** + * Whether node integration is enabled. + * Default: true. + */ + nodeIntegration?: boolean; + /** + * Specifies a script that will be loaded before other scripts run in the page. + * This script will always have access to node APIs no matter whether node integration is turned on or off. + * The value should be the absolute file path to the script. + * When node integration is turned off, the preload script can reintroduce + * Node global symbols back to the global scope. + */ + preload?: string; + /** + * Sets the session used by the page. Instead of passing the Session object directly, + * you can also choose to use the partition option instead, which accepts a partition string. + * When both session and partition are provided, session would be preferred. + * Default: the default session. + */ + session?: Session; + /** + * Sets the session used by the page according to the session’s partition string. + * If partition starts with persist:, the page will use a persistent session available + * to all pages in the app with the same partition. if there is no persist: prefix, + * the page will use an in-memory session. By assigning the same partition, + * multiple pages can share the same session. + * Default: the default session. + */ + partition?: string; + /** + * The default zoom factor of the page, 3.0 represents 300%. + * Default: 1.0. + */ + zoomFactor?: number; + /** + * Enables JavaScript support. + * Default: true. + */ + javascript?: boolean; + /** + * When setting false, it will disable the same-origin policy (Usually using testing + * websites by people), and set allowDisplayingInsecureContent and allowRunningInsecureContent + * to true if these two options are not set by user. + * Default: true. + */ + webSecurity?: boolean; + /** + * Allow an https page to display content like images from http URLs. + * Default: false. + */ + allowDisplayingInsecureContent?: boolean; + /** + * Allow a https page to run JavaScript, CSS or plugins from http URLs. + * Default: false. + */ + allowRunningInsecureContent?: boolean; + /** + * Enables image support. + * Default: true. + */ + images?: boolean; + /** + * Make TextArea elements resizable. + * Default: true. + */ + textAreasAreResizable?: boolean; + /** + * Enables WebGL support. + * Default: true. + */ + webgl?: boolean; + /** + * Enables WebAudio support. + * Default: true. + */ + webaudio?: boolean; + /** + * Whether plugins should be enabled. + * Default: false. + */ + plugins?: boolean; + /** + * Enables Chromium’s experimental features. + * Default: false. + */ + experimentalFeatures?: boolean; + /** + * Enables Chromium’s experimental canvas features. + * Default: false. + */ + experimentalCanvasFeatures?: boolean; + /** + * Enables DirectWrite font rendering system on Windows. + * Default: true. + */ + directWrite?: boolean; + /** + * Enables scroll bounce (rubber banding) effect on macOS. + * Default: false. + */ + scrollBounce?: boolean; + /** + * A list of feature strings separated by ",", like CSSVariables,KeyboardEventKey to enable. + */ + blinkFeatures?: string; + /** + * A list of feature strings separated by ",", like CSSVariables,KeyboardEventKey to disable. + */ + disableBlinkFeatures?: string; + /** + * Sets the default font for the font-family. + */ + defaultFontFamily?: { + /** + * Default: Times New Roman. + */ + standard?: string; + /** + * Default: Times New Roman. + */ + serif?: string; + /** + * Default: Arial. + */ + sansSerif?: string; + /** + * Default: Courier New. + */ + monospace?: string; + }; + /** + * Default: 16. + */ + defaultFontSize?: number; + /** + * Default: 13. + */ + defaultMonospaceFontSize?: number; + /** + * Default: 0. + */ + minimumFontSize?: number; + /** + * Default: ISO-8859-1. + */ + defaultEncoding?: string; + /** + * Whether to throttle animations and timers when the page becomes background. + * Default: true. + */ + backgroundThrottling?: boolean; + /** + * Whether to enable offscreen rendering for the browser window. + * Default: false. + */ + offscreen?: boolean; + /** + * Whether to enable Chromium OS-level sandbox. + * Default: false. + */ + sandbox?: boolean; + } + + interface BrowserWindowOptions { + /** + * Window’s width in pixels. + * Default: 800. + */ + width?: number; + /** + * Window’s height in pixels. + * Default: 600. + */ + height?: number; + /** + * Window’s left offset from screen. + * Default: center the window. + */ + x?: number; + /** + * Window’s top offset from screen. + * Default: center the window. + */ + y?: number; + /** + * The width and height would be used as web page’s size, which means + * the actual window’s size will include window frame’s size and be slightly larger. + * Default: false. + */ + useContentSize?: boolean; + /** + * Show window in the center of the screen. + * Default: true + */ + center?: boolean; + /** + * Window’s minimum width. + * Default: 0. + */ + minWidth?: number; + /** + * Window’s minimum height. + * Default: 0. + */ + minHeight?: number; + /** + * Window’s maximum width. + * Default: no limit. + */ + maxWidth?: number; + /** + * Window’s maximum height. + * Default: no limit. + */ + maxHeight?: number; + /** + * Whether window is resizable. + * Default: true. + */ + resizable?: boolean; + /** + * Whether window is movable. + * Note: This is not implemented on Linux. + * Default: true. + */ + movable?: boolean; + /** + * Whether window is minimizable. + * Note: This is not implemented on Linux. + * Default: true. + */ + minimizable?: boolean; + /** + * Whether window is maximizable. + * Note: This is not implemented on Linux. + * Default: true. + */ + maximizable?: boolean; + /** + * Whether window is closable. + * Note: This is not implemented on Linux. + * Default: true. + */ + closable?: boolean; + /** + * Whether the window can be focused. + * On Windows setting focusable: false also implies setting skipTaskbar: true. + * On Linux setting focusable: false makes the window stop interacting with wm, + * so the window will always stay on top in all workspaces. + * Default: true. + */ + focusable?: boolean; + /** + * Whether the window should always stay on top of other windows. + * Default: false. + */ + alwaysOnTop?: boolean; + /** + * Whether the window should show in fullscreen. + * When explicitly set to false the fullscreen button will be hidden or disabled on macOS. + * Default: false. + */ + fullscreen?: boolean; + /** + * Whether the window can be put into fullscreen mode. + * On macOS, also whether the maximize/zoom button should toggle full screen mode or maximize window. + * Default: true. + */ + fullscreenable?: boolean; + /** + * Whether to show the window in taskbar. + * Default: false. + */ + skipTaskbar?: boolean; + /** + * The kiosk mode. + * Default: false. + */ + kiosk?: boolean; + /** + * Default window title. + * Default: "Electron". + */ + title?: string; + /** + * The window icon, when omitted on Windows the executable’s icon would be used as window icon. + */ + icon?: NativeImage | string; + /** + * Whether window should be shown when created. + * Default: true. + */ + show?: boolean; + /** + * Specify false to create a Frameless Window. + * Default: true. + */ + frame?: boolean; + /** + * Specify parent window. + * Default: null. + */ + parent?: BrowserWindow; + /** + * Whether this is a modal window. This only works when the window is a child window. + * Default: false. + */ + modal?: boolean; + /** + * Whether the web view accepts a single mouse-down event that simultaneously activates the window. + * Default: false. + */ + acceptFirstMouse?: boolean; + /** + * Whether to hide cursor when typing. + * Default: false. + */ + disableAutoHideCursor?: boolean; + /** + * Auto hide the menu bar unless the Alt key is pressed. + * Default: true. + */ + autoHideMenuBar?: boolean; + /** + * Enable the window to be resized larger than screen. + * Default: false. + */ + enableLargerThanScreen?: boolean; + /** + * Window’s background color as Hexadecimal value, like #66CD00 or #FFF or #80FFFFFF (alpha is supported). + * Default: #FFF (white). + */ + backgroundColor?: string; + /** + * Whether window should have a shadow. + * Note: This is only implemented on macOS. + * Default: true. + */ + hasShadow?: boolean; + /** + * Forces using dark theme for the window. + * Note: Only works on some GTK+3 desktop environments. + * Default: false. + */ + darkTheme?: boolean; + /** + * Makes the window transparent. + * Default: false. + */ + transparent?: boolean; + /** + * The type of window, default is normal window. + */ + type?: BrowserWindowType; + /** + * The style of window title bar. + */ + titleBarStyle?: 'default' | 'hidden' | 'hidden-inset'; + /** + * Use WS_THICKFRAME style for frameless windows on Windows + */ + thickFrame?: boolean; + /** + * Settings of web page’s features. + */ + webPreferences?: WebPreferences; + /** + * Tab group name, allows opening the window as a native tab on macOS 10.12+. + * Windows with the same tabbing identifier will be grouped together. + */ + tabbingIdentifier?: string; + } + + type BrowserWindowType = BrowserWindowTypeLinux | BrowserWindowTypeMac | BrowserWindowTypeWindows; + type BrowserWindowTypeLinux = 'desktop' | 'dock' | 'toolbar' | 'splash' | 'notification'; + type BrowserWindowTypeMac = 'desktop' | 'textured'; + type BrowserWindowTypeWindows = 'toolbar'; + + // https://github.com/electron/electron/blob/master/docs/api/clipboard.md + + /** + * The clipboard module provides methods to perform copy and paste operations. + */ + interface Clipboard { + /** + * @returns The contents of the clipboard as plain text. + */ + readText(type?: ClipboardType): string; + /** + * Writes the text into the clipboard as plain text. + */ + writeText(text: string, type?: ClipboardType): void; + /** + * @returns The contents of the clipboard as markup. + */ + readHTML(type?: ClipboardType): string; + /** + * Writes markup to the clipboard. + */ + writeHTML(markup: string, type?: ClipboardType): void; + /** + * @returns The contents of the clipboard as a NativeImage. + */ + readImage(type?: ClipboardType): NativeImage; + /** + * Writes the image into the clipboard. + */ + writeImage(image: NativeImage, type?: ClipboardType): void; + /** + * @returns The contents of the clipboard as RTF. + */ + readRTF(type?: ClipboardType): string; + /** + * Writes the text into the clipboard in RTF. + */ + writeRTF(text: string, type?: ClipboardType): void; + /** + * Clears everything in clipboard. + */ + clear(type?: ClipboardType): void; + /** + * @returns Array available formats for the clipboard type. + */ + availableFormats(type?: ClipboardType): string[]; + /** + * Returns whether the clipboard supports the format of specified data. + * Note: This API is experimental and could be removed in future. + * @returns Whether the clipboard has data in the specified format. + */ + has(format: string, type?: ClipboardType): boolean; + /** + * Reads the data in the clipboard of the specified format. + * Note: This API is experimental and could be removed in future. + */ + read(format: string, type?: ClipboardType): string | NativeImage; + /** + * Writes data to the clipboard. + */ + write(data: { + text?: string; + rtf?: string; + html?: string; + image?: NativeImage; + }, type?: ClipboardType): void; + /** + * @returns An Object containing title and url keys representing the bookmark in the clipboard. + * + * Note: This API is available on macOS and Windows. + */ + readBookmark(): Bookmark; + /** + * Writes the title and url into the clipboard as a bookmark. + * + * Note: This API is available on macOS and Windows. + */ + writeBookmark(title: string, url: string, type?: ClipboardType): void; + /** + * The text on the find pasteboard. This method uses synchronous IPC when called from the renderer process. + * The cached value is reread from the find pasteboard whenever the application is activated. + * + * Note: This API is available on macOS. + */ + readFindText(): string; + /** + * Writes the text into the find pasteboard as plain text. + * This method uses synchronous IPC when called from the renderer process. + * + * Note: This API is available on macOS. + */ + writeFindText(text: string): void; + } + + type ClipboardType = '' | 'selection'; + + interface Bookmark { + title: string; + url: string; + } + + // https://github.com/electron/electron/blob/master/docs/api/content-tracing.md + + /** + * The content-tracing module is used to collect tracing data generated by the underlying Chromium content module. + * This module does not include a web interface so you need to open chrome://tracing/ + * in a Chrome browser and load the generated file to view the result. + */ + interface ContentTracing { + /** + * Get a set of category groups. The category groups can change as new code paths are reached. + * + * @param callback Called once all child processes have acknowledged the getCategories request. + */ + getCategories(callback: (categoryGroups: string[]) => void): void; + /** + * Start recording on all processes. Recording begins immediately locally and asynchronously + * on child processes as soon as they receive the EnableRecording request. + * + * @param callback Called once all child processes have acknowledged the startRecording request. + */ + startRecording(options: ContentTracingOptions, callback: Function): void; + /** + * Stop recording on all processes. Child processes typically are caching trace data and + * only rarely flush and send trace data back to the main process. That is because it may + * be an expensive operation to send the trace data over IPC, and we would like to avoid + * much runtime overhead of tracing. So, to end tracing, we must asynchronously ask all + * child processes to flush any pending trace data. + * + * @param resultFilePath Trace data will be written into this file if it is not empty, + * or into a temporary file. + * @param callback Called once all child processes have acknowledged the stopRecording request. + */ + stopRecording(resultFilePath: string, callback: (filePath: string) => void): void; + /** + * Start monitoring on all processes. Monitoring begins immediately locally and asynchronously + * on child processes as soon as they receive the startMonitoring request. + * + * @param callback Called once all child processes have acked to the startMonitoring request. + */ + startMonitoring(options: ContentTracingOptions, callback: Function): void; + /** + * Stop monitoring on all processes. + * + * @param callback Called once all child processes have acknowledged the stopMonitoring request. + */ + stopMonitoring(callback: Function): void; + /** + * Get the current monitoring traced data. Child processes typically are caching trace data + * and only rarely flush and send trace data back to the main process. That is because it may + * be an expensive operation to send the trace data over IPC, and we would like to avoid much + * runtime overhead of tracing. So, to end tracing, we must asynchronously ask all child + * processes to flush any pending trace data. + * + * @param callback Called once all child processes have acknowledged the captureMonitoringSnapshot request. + */ + captureMonitoringSnapshot(resultFilePath: string, callback: (filePath: string) => void): void; + /** + * Get the maximum usage across processes of trace buffer as a percentage of the full state. + * + * @param callback Called when the TraceBufferUsage value is determined. + */ + getTraceBufferUsage(callback: Function): void; + /** + * @param callback Called every time the given event occurs on any process. + */ + setWatchEvent(categoryName: string, eventName: string, callback: Function): void; + /** + * Cancel the watch event. This may lead to a race condition with the watch event callback if tracing is enabled. + */ + cancelWatchEvent(): void; + } + + interface ContentTracingOptions { + /** + * Filter to control what category groups should be traced. + * A filter can have an optional - prefix to exclude category groups + * that contain a matching category. Having both included and excluded + * category patterns in the same list is not supported. + * + * Examples: + * test_MyTest* + * test_MyTest*,test_OtherStuff + * -excluded_category1,-excluded_category2 + */ + categoryFilter: string; + /** + * Controls what kind of tracing is enabled, it is a comma-delimited list. + * + * Possible options are: + * record-until-full + * record-continuously + * trace-to-console + * enable-sampling + * enable-systrace + * + * The first 3 options are trace recoding modes and hence mutually exclusive. + * If more than one trace recording modes appear in the traceOptions string, + * the last one takes precedence. If none of the trace recording modes are specified, + * recording mode is record-until-full. + * + * The trace option will first be reset to the default option (record_mode set + * to record-until-full, enable_sampling and enable_systrace set to false) + * before options parsed from traceOptions are applied on it. + */ + traceOptions: string; + } + + // https://github.com/electron/electron/blob/master/docs/api/crash-reporter.md + + /** + * The crash-reporter module enables sending your app's crash reports. + */ + interface CrashReporter { + /** + * You are required to call this method before using other crashReporter APIs. + * + * Note: On macOS, Electron uses a new crashpad client, which is different from breakpad + * on Windows and Linux. To enable the crash collection feature, you are required to call + * the crashReporter.start API to initialize crashpad in the main process and in each + * renderer process from which you wish to collect crash reports. + */ + start(options: CrashReporterStartOptions): void; + /** + * @returns The crash report. When there was no crash report + * sent or the crash reporter is not started, null will be returned. + */ + getLastCrashReport(): CrashReport; + /** + * @returns All uploaded crash reports. + */ + getUploadedReports(): CrashReport[]; + } + + interface CrashReporterStartOptions { + /** + * Default: app.getName() + */ + productName?: string; + companyName: string; + /** + * URL that crash reports would be sent to as POST. + */ + submitURL: string; + /** + * Send the crash report without user interaction. + * Default: true. + */ + autoSubmit?: boolean; + /** + * Default: false. + */ + ignoreSystemCrashHandler?: boolean; + /** + * An object you can define that will be sent along with the report. + * Only string properties are sent correctly, nested objects are not supported. + */ + extra?: { [prop: string]: string }; + + /** + * Path to a folder where the crashes will be temporarily stored by the electron crash reporter + * Applies only to child processes that need crash reporting. + * Electron figures out the crashesDirectory on its own for Main and Renderer process + */ + crashesDirectory?: string; + } + + interface CrashReport { + id: string; + date: Date; + } + + // https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md + + /** + * The desktopCapturer module can be used to get available sources + * that can be used to be captured with getUserMedia. + */ + interface DesktopCapturer { + /** + * Starts a request to get all desktop sources. + * + * Note: There is no guarantee that the size of source.thumbnail is always + * the same as the thumnbailSize in options. It also depends on the scale of the screen or window. + */ + getSources(options: DesktopCapturerOptions, callback: (error: Error, sources: DesktopCapturerSource[]) => any): void; + } + + interface DesktopCapturerOptions { + /** + * The types of desktop sources to be captured. + */ + types?: ('screen' | 'window')[]; + /** + * The suggested size that thumbnail should be scaled. + * Default: {width: 150, height: 150} + */ + thumbnailSize?: Size; + } + + interface DesktopCapturerSource { + /** + * The id of the captured window or screen used in navigator.webkitGetUserMedia. + * The format looks like window:XX or screen:XX where XX is a random generated number. + */ + id: string; + /** + * The described name of the capturing screen or window. + * If the source is a screen, the name will be Entire Screen or Screen ; + * if it is a window, the name will be the window’s title. + */ + name: string; + /** + * A thumbnail image. + */ + thumbnail: NativeImage; + } + + // https://github.com/electron/electron/blob/master/docs/api/dialog.md + + /** + * The dialog module provides APIs to show native system dialogs, such as opening files or alerting, + * so web applications can deliver the same user experience as native applications. + */ + interface Dialog { + /** + * Note: On Windows and Linux an open dialog can not be both a file selector and a directory selector, + * so if you set properties to ['openFile', 'openDirectory'] on these platforms, a directory selector will be shown. + * + * @param callback If supplied, the API call will be asynchronous. + * @returns On success, returns an array of file paths chosen by the user, + * otherwise returns undefined. + */ + showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: (fileNames: string[]) => void): string[]; + /** + * Note: On Windows and Linux an open dialog can not be both a file selector and a directory selector, + * so if you set properties to ['openFile', 'openDirectory'] on these platforms, a directory selector will be shown. + * + * @param callback If supplied, the API call will be asynchronous. + * @returns On success, returns an array of file paths chosen by the user, + * otherwise returns undefined. + */ + showOpenDialog(options: OpenDialogOptions, callback?: (fileNames: string[]) => void): string[]; + /** + * @param callback If supplied, the API call will be asynchronous. + * @returns On success, returns the path of file chosen by the user, otherwise + * returns undefined. + */ + showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions, callback?: (fileName: string) => void): string; + /** + * @param callback If supplied, the API call will be asynchronous. + * @returns On success, returns the path of file chosen by the user, otherwise + * returns undefined. + */ + showSaveDialog(options: SaveDialogOptions, callback?: (fileName: string) => void): string; + /** + * Shows a message box. It will block until the message box is closed. + * @param callback If supplied, the API call will be asynchronous. + * @returns The index of the clicked button. + */ + showMessageBox(browserWindow: BrowserWindow, options: ShowMessageBoxOptions, callback?: (response: number) => void): number; + /** + * Shows a message box. It will block until the message box is closed. + * @param callback If supplied, the API call will be asynchronous. + * @returns The index of the clicked button. + */ + showMessageBox(options: ShowMessageBoxOptions, callback?: (response: number) => void): number; + /** + * Displays a modal dialog that shows an error message. + * + * This API can be called safely before the ready event the app module emits, + * it is usually used to report errors in early stage of startup. + * If called before the app readyevent on Linux, the message will be emitted to stderr, + * and no GUI dialog will appear. + */ + showErrorBox(title: string, content: string): void; + } + + interface OpenDialogOptions { + title?: string; + defaultPath?: string; + /** + * Custom label for the confirmation button, when left empty the default label will be used. + */ + buttonLabel?: string; + /** + * File types that can be displayed or selected. + */ + filters?: { + name: string; + /** + * Extensions without wildcards or dots (e.g. 'png' is good but '.png' and '*.png' are bad). + * To show all files, use the '*' wildcard (no other wildcard is supported). + */ + extensions: string[]; + }[]; + /** + * Contains which features the dialog should use. + */ + properties?: ('openFile' | 'openDirectory' | 'multiSelections' | 'createDirectory' | 'showHiddenFiles')[]; + } + + interface SaveDialogOptions { + title?: string; + defaultPath?: string; + /** + * Custom label for the confirmation button, when left empty the default label will be used. + */ + buttonLabel?: string; + /** + * File types that can be displayed, see dialog.showOpenDialog for an example. + */ + filters?: { + name: string; + extensions: string[]; + }[]; + } + + interface ShowMessageBoxOptions { + /** + * On Windows, "question" displays the same icon as "info", unless you set an icon using the "icon" option. + */ + type?: 'none' | 'info' | 'error' | 'question' | 'warning'; + /** + * Texts for buttons. On Windows, an empty array will result in one button labeled "OK". + */ + buttons?: string[]; + /** + * Index of the button in the buttons array which will be selected by default when the message box opens. + */ + defaultId?: number; + /** + * Title of the message box (some platforms will not show it). + */ + title?: string; + /** + * Contents of the message box. + */ + message?: string; + /** + * Extra information of the message. + */ + detail?: string; + icon?: NativeImage; + /** + * The value will be returned when user cancels the dialog instead of clicking the buttons of the dialog. + * By default it is the index of the buttons that have "cancel" or "no" as label, + * or 0 if there is no such buttons. On macOS and Windows the index of "Cancel" button + * will always be used as cancelId, not matter whether it is already specified. + */ + cancelId?: number; + /** + * On Windows Electron will try to figure out which one of the buttons are common buttons + * (like "Cancel" or "Yes"), and show the others as command links in the dialog. + * This can make the dialog appear in the style of modern Windows apps. + * If you don’t like this behavior, you can set noLink to true. + */ + noLink?: boolean; + } + + // https://github.com/electron/electron/blob/master/docs/api/download-item.md + + /** + * DownloadItem represents a download item in Electron. + */ + interface DownloadItem extends NodeJS.EventEmitter { + /** + * Emitted when the download has been updated and is not done. + */ + on(event: 'updated', listener: (event: Event, state: 'progressing' | 'interrupted') => void): this; + /** + * Emits when the download is in a terminal state. This includes a completed download, + * a cancelled download (via downloadItem.cancel()), and interrupted download that can’t be resumed. + */ + on(event: 'done', listener: (event: Event, state: 'completed' | 'cancelled' | 'interrupted') => void): this; + on(event: string, listener: Function): this; + /** + * Set the save file path of the download item. + * Note: The API is only available in session’s will-download callback function. + * If user doesn’t set the save path via the API, Electron will use the original + * routine to determine the save path (Usually prompts a save dialog). + */ + setSavePath(path: string): void; + /** + * @returns The save path of the download item. + * This will be either the path set via downloadItem.setSavePath(path) or the path selected from the shown save dialog. + */ + getSavePath(): string; + /** + * Pauses the download. + */ + pause(): void; + /** + * @returns Whether the download is paused. + */ + isPaused(): boolean; + /** + * Resumes the download that has been paused. + */ + resume(): void; + /** + * @returns Whether the download can resume. + */ + canResume(): boolean; + /** + * Cancels the download operation. + */ + cancel(): void; + /** + * @returns The origin url where the item is downloaded from. + */ + getURL(): string; + /** + * @returns The mime type. + */ + getMimeType(): string; + /** + * @returns Whether the download has user gesture. + */ + hasUserGesture(): boolean; + /** + * @returns The file name of the download item. + * Note: The file name is not always the same as the actual one saved in local disk. + * If user changes the file name in a prompted download saving dialog, + * the actual name of saved file will be different. + */ + getFilename(): string; + /** + * @returns The total size in bytes of the download item. If the size is unknown, it returns 0. + */ + getTotalBytes(): number; + /** + * @returns The received bytes of the download item. + */ + getReceivedBytes(): number; + /** + * @returns The Content-Disposition field from the response header. + */ + getContentDisposition(): string; + /** + * @returns The current state. + */ + getState(): 'progressing' | 'completed' | 'cancelled' | 'interrupted'; + } + + // https://github.com/electron/electron/blob/master/docs/api/global-shortcut.md + + /** + * The globalShortcut module can register/unregister a global keyboard shortcut + * with the operating system so that you can customize the operations for various shortcuts. + * Note: The shortcut is global; it will work even if the app does not have the keyboard focus. + * You should not use this module until the ready event of the app module is emitted. + */ + interface GlobalShortcut { + /** + * Registers a global shortcut of accelerator. + * @param accelerator Represents a keyboard shortcut. It can contain modifiers + * and key codes, combined by the "+" character. + * @param callback Called when the registered shortcut is pressed by the user. + */ + register(accelerator: string, callback: Function): void; + /** + * @param accelerator Represents a keyboard shortcut. It can contain modifiers + * and key codes, combined by the "+" character. + * @returns Whether the accelerator is registered. + */ + isRegistered(accelerator: string): boolean; + /** + * Unregisters the global shortcut of keycode. + * @param accelerator Represents a keyboard shortcut. It can contain modifiers + * and key codes, combined by the "+" character. + */ + unregister(accelerator: string): void; + /** + * Unregisters all the global shortcuts. + */ + unregisterAll(): void; + } + + // https://github.com/electron/electron/blob/master/docs/api/ipc-main.md + + /** + * The ipcMain module handles asynchronous and synchronous messages + * sent from a renderer process (web page). + * Messages sent from a renderer will be emitted to this module. + */ + interface IpcMain extends NodeJS.EventEmitter { + addListener(channel: string, listener: IpcMainEventListener): this; + on(channel: string, listener: IpcMainEventListener): this; + once(channel: string, listener: IpcMainEventListener): this; + removeListener(channel: string, listener: IpcMainEventListener): this; + removeAllListeners(channel?: string): this; + } + + type IpcMainEventListener = (event: IpcMainEvent, ...args: any[]) => void; + + interface IpcMainEvent { + /** + * Set this to the value to be returned in a synchronous message. + */ + returnValue?: any; + /** + * Returns the webContents that sent the message, you can call sender.send + * to reply to the asynchronous message. + */ + sender: WebContents; + } + + // https://github.com/electron/electron/blob/master/docs/api/ipc-renderer.md + + /** + * The ipcRenderer module provides a few methods so you can send synchronous + * and asynchronous messages from the render process (web page) to the main process. + * You can also receive replies from the main process. + */ + interface IpcRenderer extends NodeJS.EventEmitter { + addListener(channel: string, listener: IpcRendererEventListener): this; + on(channel: string, listener: IpcRendererEventListener): this; + once(channel: string, listener: IpcRendererEventListener): this; + removeListener(channel: string, listener: IpcRendererEventListener): this; + removeAllListeners(channel?: string): this; + /** + * Send ...args to the renderer via channel in asynchronous message, the main + * process can handle it by listening to the channel event of ipc module. + */ + send(channel: string, ...args: any[]): void; + /** + * Send ...args to the renderer via channel in synchronous message, and returns + * the result sent from main process. The main process can handle it by listening + * to the channel event of ipc module, and returns by setting event.returnValue. + * Note: Usually developers should never use this API, since sending synchronous + * message would block the whole renderer process. + * @returns The result sent from the main process. + */ + sendSync(channel: string, ...args: any[]): any; + /** + * Like ipc.send but the message will be sent to the host page instead of the main process. + * This is mainly used by the page in to communicate with host page. + */ + sendToHost(channel: string, ...args: any[]): void; + } + + type IpcRendererEventListener = (event: IpcRendererEvent, ...args: any[]) => void; + + interface IpcRendererEvent { + /** + * You can call sender.send to reply to the asynchronous message. + */ + sender: IpcRenderer; + } + + // https://github.com/electron/electron/blob/master/docs/api/menu-item.md + // https://github.com/electron/electron/blob/master/docs/api/accelerator.md + + /** + * The MenuItem allows you to add items to an application or context menu. + */ + class MenuItem { + /** + * Create a new menu item. + */ + constructor(options: MenuItemOptions); + + click: (menuItem: MenuItem, browserWindow: BrowserWindow, event: Event & Modifiers) => void; + /** + * Read-only property. + */ + type: MenuItemType; + /** + * Read-only property. + */ + role: MenuItemRole | MenuItemRoleMac; + /** + * Read-only property. + */ + accelerator: string; + /** + * Read-only property. + */ + icon: NativeImage | string; + /** + * Read-only property. + */ + submenu: Menu | MenuItemOptions[]; + + label: string; + sublabel: string; + enabled: boolean; + visible: boolean; + checked: boolean; + } + + type MenuItemType = 'normal' | 'separator' | 'submenu' | 'checkbox' | 'radio'; + type MenuItemRole = 'undo' | 'redo' | 'cut' | 'copy' | 'paste' | 'pasteandmatchstyle' | 'selectall' | 'delete' | 'minimize' | 'close' | 'quit' | 'togglefullscreen' | 'resetzoom' | 'zoomin' | 'zoomout'; + type MenuItemRoleMac = 'about' | 'hide' | 'hideothers' | 'unhide' | 'startspeaking' | 'stopspeaking' | 'front' | 'zoom' | 'window' | 'help' | 'services'; + + interface MenuItemOptions { + /** + * Callback when the menu item is clicked. + */ + click?: (menuItem: MenuItem, browserWindow: BrowserWindow, event: Event & Modifiers) => void; + /** + * Can be normal, separator, submenu, checkbox or radio. + */ + type?: MenuItemType; + label?: string; + sublabel?: string; + /** + * An accelerator is string that represents a keyboard shortcut, it can contain + * multiple modifiers and key codes, combined by the + character. + * + * Examples: + * CommandOrControl+A + * CommandOrControl+Shift+Z + * + * Platform notice: + * On Linux and Windows, the Command key would not have any effect, + * you can use CommandOrControl which represents Command on macOS and Control on + * Linux and Windows to define some accelerators. + * + * Use Alt instead of Option. The Option key only exists on macOS, whereas + * the Alt key is available on all platforms. + * + * The Super key is mapped to the Windows key on Windows and Linux and Cmd on macOS. + * + * Available modifiers: + * Command (or Cmd for short) + * Control (or Ctrl for short) + * CommandOrControl (or CmdOrCtrl for short) + * Alt + * Option + * AltGr + * Shift + * Super + * + * Available key codes: + * 0 to 9 + * A to Z + * F1 to F24 + * Punctuations like ~, !, @, #, $, etc. + * Plus + * Space + * Tab + * Backspace + * Delete + * Insert + * Return (or Enter as alias) + * Up, Down, Left and Right + * Home and End + * PageUp and PageDown + * Escape (or Esc for short) + * VolumeUp, VolumeDown and VolumeMute + * MediaNextTrack, MediaPreviousTrack, MediaStop and MediaPlayPause + * PrintScreen + */ + accelerator?: string; + /** + * In Electron for the APIs that take images, you can pass either file paths + * or NativeImage instances. When passing null, an empty image will be used. + */ + icon?: NativeImage | string; + /** + * If false, the menu item will be greyed out and unclickable. + */ + enabled?: boolean; + /** + * If false, the menu item will be entirely hidden. + */ + visible?: boolean; + /** + * Should only be specified for 'checkbox' or 'radio' type menu items. + */ + checked?: boolean; + /** + * Should be specified for submenu type menu item, when it's specified the + * type: 'submenu' can be omitted for the menu item + */ + submenu?: Menu | MenuItemOptions[]; + /** + * Unique within a single menu. If defined then it can be used as a reference + * to this item by the position attribute. + */ + id?: string; + /** + * This field allows fine-grained definition of the specific location within + * a given menu. + */ + position?: string; + /** + * Define the action of the menu item, when specified the click property will be ignored + */ + role?: MenuItemRole | MenuItemRoleMac; + } + + // https://github.com/electron/electron/blob/master/docs/api/menu.md + + /** + * The Menu class is used to create native menus that can be used as application + * menus and context menus. This module is a main process module which can be used + * in a render process via the remote module. + * + * Each menu consists of multiple menu items, and each menu item can have a submenu. + */ + class Menu extends NodeJS.EventEmitter { + /** + * Creates a new menu. + */ + constructor(); + /** + * Sets menu as the application menu on macOS. On Windows and Linux, the menu + * will be set as each window's top menu. + */ + static setApplicationMenu(menu: Menu): void; + /** + * @returns The application menu if set, or null if not set. + */ + static getApplicationMenu(): Menu; + /** + * Sends the action to the first responder of application. + * This is used for emulating default Cocoa menu behaviors, + * usually you would just use the role property of MenuItem. + * + * Note: This method is macOS only. + */ + static sendActionToFirstResponder(action: string): void; + /** + * @param template Generally, just an array of options for constructing MenuItem. + * You can also attach other fields to element of the template, and they will + * become properties of the constructed menu items. + */ + static buildFromTemplate(template: MenuItemOptions[]): Menu; + /** + * Pops up this menu as a context menu in the browserWindow. You can optionally + * provide a (x,y) coordinate to place the menu at, otherwise it will be placed + * at the current mouse cursor position. + * @param x Horizontal coordinate where the menu will be placed. + * @param y Vertical coordinate where the menu will be placed. + */ + popup(browserWindow?: BrowserWindow, x?: number, y?: number): void; + /** + * Appends the menuItem to the menu. + */ + append(menuItem: MenuItem): void; + /** + * Inserts the menuItem to the pos position of the menu. + */ + insert(position: number, menuItem: MenuItem): void; + /** + * @returns an array containing the menu’s items. + */ + items: MenuItem[]; + } + + // https://github.com/electron/electron/blob/master/docs/api/native-image.md + + /** + * This class is used to represent an image. + */ + class NativeImage { + /** + * Creates an empty NativeImage instance. + */ + static createEmpty(): NativeImage; + /** + * Creates a new NativeImage instance from file located at path. + * This method returns an empty image if the path does not exist, cannot be read, or is not a valid image. + */ + static createFromPath(path: string): NativeImage; + /** + * Creates a new NativeImage instance from buffer. + * @param scaleFactor 1.0 by default. + */ + static createFromBuffer(buffer: Buffer, scaleFactor?: number): NativeImage; + /** + * Creates a new NativeImage instance from dataURL + */ + static createFromDataURL(dataURL: string): NativeImage; + /** + * @returns Buffer that contains the image's PNG encoded data. + */ + toPNG(): Buffer; + /** + * @returns Buffer that contains the image's JPEG encoded data. + */ + toJPEG(quality: number): Buffer; + /** + * @returns Buffer that contains a copy of the image's raw bitmap pixel data. + */ + toBitmap(): Buffer; + /** + * @returns Buffer that contains the image's raw bitmap pixel data. + * + * The difference between getBitmap() and toBitmap() is, getBitmap() does not copy the bitmap data, + * so you have to use the returned Buffer immediately in current event loop tick, + * otherwise the data might be changed or destroyed. + */ + getBitmap(): Buffer; + /** + * @returns The data URL of the image. + */ + toDataURL(): string; + /** + * The native type of the handle is NSImage* on macOS. + * Note: This is only implemented on macOS. + * @returns The platform-specific handle of the image as Buffer. + */ + getNativeHandle(): Buffer; + /** + * @returns Whether the image is empty. + */ + isEmpty(): boolean; + /** + * @returns The size of the image. + */ + getSize(): Size; + /** + * Marks the image as template image. + */ + setTemplateImage(option: boolean): void; + /** + * Returns a boolean whether the image is a template image. + */ + isTemplateImage(): boolean; + } + + // https://github.com/electron/electron/blob/master/docs/api/net.md + + /** + * The net module is a client-side API for issuing HTTP(S) requests. + * It is similar to the HTTP and HTTPS modules of Node.js but uses Chromium’s native + * networking library instead of the Node.js implementation, offering better support + * for web proxies. + * The following is a non-exhaustive list of why you may consider using the net module + * instead of the native Node.js modules: + * - Automatic management of system proxy configuration, support of the wpad protocol + * and proxy pac configuration files. + * - Automatic tunneling of HTTPS requests. + * - Support for authenticating proxies using basic, digest, NTLM, Kerberos or negotiate + * authentication schemes. + * - Support for traffic monitoring proxies: Fiddler-like proxies used for access control + * and monitoring. + * + * The net module API has been specifically designed to mimic, as closely as possible, + * the familiar Node.js API. The API components including classes, methods, + * properties and event names are similar to those commonly used in Node.js. + * + * The net API can be used only after the application emits the ready event. + * Trying to use the module before the ready event will throw an error. + */ + interface Net extends NodeJS.EventEmitter { + /** + * @param options The ClientRequest constructor options. + * @param callback A one time listener for the response event. + * + * @returns a ClientRequest instance using the provided options which are directly + * forwarded to the ClientRequest constructor. + */ + request(options: string | RequestOptions, callback?: (response: IncomingMessage) => void): ClientRequest; + } + + /** + * The RequestOptions interface allows to define various options for an HTTP request. + */ + interface RequestOptions { + /** + * The HTTP request method. Defaults to the GET method. + */ + method?: string; + /** + * The request URL. Must be provided in the absolute form with the protocol + * scheme specified as http or https. + */ + url?: string; + /** + * The Session instance with which the request is associated. + */ + session?: Session; + /** + * The name of the partition with which the request is associated. + * Defaults to the empty string. The session option prevails on partition. + * Thus if a session is explicitly specified, partition is ignored. + */ + partition?: string; + /** + * The protocol scheme in the form ‘scheme:’. Currently supported values are ‘http:’ or ‘https:’. + * Defaults to ‘http:’. + */ + Protocol?: 'http:' | 'https:'; + /** + * The server host provided as a concatenation of the hostname and the port number ‘hostname:port’. + */ + host?: string; + /** + * The server host name. + */ + hostname?: string; + /** + * The server’s listening port number. + */ + port?: number; + /** + * The path part of the request URL. + */ + path?: string; + /** + * A map specifying extra HTTP header name/value. + */ + headers?: { [key: string]: any }; + } + + /** + * The ClientRequest class represents an HTTP request. + */ + class ClientRequest extends NodeJS.EventEmitter { + /** + * Emitted when an HTTP response is received for the request. + */ + on(event: 'response', listener: (response: IncomingMessage) => void): this; + /** + * Emitted when an authenticating proxy is asking for user credentials. + * The callback function is expected to be called back with user credentials. + * Providing empty credentials will cancel the request and report an authentication + * error on the response object. + */ + on(event: 'login', listener: (authInfo: LoginAuthInfo, callback: (username?: string, password?: string) => void) => void): this; + /** + * Emitted just after the last chunk of the request’s data has been written into + * the request object. + */ + on(event: 'finish', listener: () => void): this; + /** + * Emitted when the request is aborted. The abort event will not be fired if the + * request is already closed. + */ + on(event: 'abort', listener: () => void): this; + /** + * Emitted when the net module fails to issue a network request. + * Typically when the request object emits an error event, a close event will + * subsequently follow and no response object will be provided. + */ + on(event: 'error', listener: (error: Error) => void): this; + /** + * Emitted as the last event in the HTTP request-response transaction. + * The close event indicates that no more events will be emitted on either the + * request or response objects. + */ + on(event: 'close', listener: () => void): this; + on(event: string, listener: Function): this; + /** + * A Boolean specifying whether the request will use HTTP chunked transfer encoding or not. + * Defaults to false. The property is readable and writable, however it can be set only before + * the first write operation as the HTTP headers are not yet put on the wire. + * Trying to set the chunkedEncoding property after the first write will throw an error. + * + * Using chunked encoding is strongly recommended if you need to send a large request + * body as data will be streamed in small chunks instead of being internally buffered + * inside Electron process memory. + */ + chunkedEncoding: boolean; + /** + * @param options If options is a String, it is interpreted as the request URL. + * If it is an object, it is expected to be a RequestOptions. + * @param callback A one time listener for the response event. + */ + constructor(options: string | RequestOptions, callback?: (response: IncomingMessage) => void); + /** + * Adds an extra HTTP header. The header name will issued as it is without lowercasing. + * It can be called only before first write. Calling this method after the first write + * will throw an error. + * @param name An extra HTTP header name. + * @param value An extra HTTP header value. + */ + setHeader(name: string, value: string): void; + /** + * @param name Specify an extra header name. + * @returns The value of a previously set extra header name. + */ + getHeader(name: string): string; + /** + * Removes a previously set extra header name. This method can be called only before first write. + * Trying to call it after the first write will throw an error. + * @param name Specify an extra header name. + */ + removeHeader(name: string): void; + /** + * Adds a chunk of data to the request body. The first write operation may cause the + * request headers to be issued on the wire. + * After the first write operation, it is not allowed to add or remove a custom header. + * @param chunk A chunk of the request body’s data. If it is a string, it is converted + * into a Buffer using the specified encoding. + * @param encoding Used to convert string chunks into Buffer objects. Defaults to ‘utf-8’. + * @param callback Called after the write operation ends. + */ + write(chunk: string | Buffer, encoding?: string, callback?: Function): boolean; + /** + * Sends the last chunk of the request data. Subsequent write or end operations will not be allowed. + * The finish event is emitted just after the end operation. + * @param chunk A chunk of the request body’s data. If it is a string, it is converted into + * a Buffer using the specified encoding. + * @param encoding Used to convert string chunks into Buffer objects. Defaults to ‘utf-8’. + * @param callback Called after the write operation ends. + * + */ + end(chunk?: string | Buffer, encoding?: string, callback?: Function): boolean; + /** + * Cancels an ongoing HTTP transaction. If the request has already emitted the close event, + * the abort operation will have no effect. + * Otherwise an ongoing event will emit abort and close events. + * Additionally, if there is an ongoing response object,it will emit the aborted event. + */ + abort(): void + } + + /** + * An IncomingMessage represents an HTTP response. + */ + interface IncomingMessage extends NodeJS.ReadableStream { + /** + * The data event is the usual method of transferring response data into applicative code. + */ + on(event: 'data', listener: (chunk: Buffer) => void): this; + /** + * Indicates that response body has ended. + */ + on(event: 'end', listener: () => void): this; + /** + * Emitted when a request has been canceled during an ongoing HTTP transaction. + */ + on(event: 'aborted', listener: () => void): this; + /** + * Emitted when an error was encountered while streaming response data events. + * For instance, if the server closes the underlying while the response is still + * streaming, an error event will be emitted on the response object and a close + * event will subsequently follow on the request object. + */ + on(event: 'error', listener: (error: Error) => void): this; + on(event: string, listener: Function): this; + /** + * An Integer indicating the HTTP response status code. + */ + statusCode: number; + /** + * A String representing the HTTP status message. + */ + statusMessage: string; + /** + * An object representing the response HTTP headers. The headers object is formatted as follows: + * - All header names are lowercased. + * - Each header name produces an array-valued property on the headers object. + * - Each header value is pushed into the array associated with its header name. + */ + headers: Headers; + /** + * A string indicating the HTTP protocol version number. Typical values are ‘1.0’ or ‘1.1’. + */ + httpVersion: string; + /** + * An integer-valued read-only property that returns the HTTP major version number. + */ + httpVersionMajor: number; + /** + * An integer-valued read-only property that returns the HTTP minor version number. + */ + httpVersionMinor: number; + } + + // https://github.com/electron/electron/blob/master/docs/api/power-monitor.md + + /** + * The power-monitor module is used to monitor power state changes. + * You should not use this module until the ready event of the app module is emitted. + */ + interface PowerMonitor extends NodeJS.EventEmitter { + /** + * Emitted when the system is suspending. + */ + on(event: 'suspend', listener: Function): this; + /** + * Emitted when system is resuming. + */ + on(event: 'resume', listener: Function): this; + /** + * Emitted when the system changes to AC power. + */ + on(event: 'on-ac', listener: Function): this; + /** + * Emitted when system changes to battery power. + */ + on(event: 'on-battery', listener: Function): this; + on(event: string, listener: Function): this; + } + + // https://github.com/electron/electron/blob/master/docs/api/power-save-blocker.md + + /** + * The powerSaveBlocker module is used to block the system from entering + * low-power (sleep) mode and thus allowing the app to keep the system and screen active. + */ + interface PowerSaveBlocker { + /** + * Starts preventing the system from entering lower-power mode. + * @returns The blocker ID that is assigned to this power blocker. + * Note: prevent-display-sleep has higher has precedence over prevent-app-suspension. + */ + start(type: 'prevent-app-suspension' | 'prevent-display-sleep'): number; + /** + * @param id The power save blocker id returned by powerSaveBlocker.start. + * Stops the specified power save blocker. + */ + stop(id: number): void; + /** + * @param id The power save blocker id returned by powerSaveBlocker.start. + * @returns Whether the corresponding powerSaveBlocker has started. + */ + isStarted(id: number): boolean; + } + + // https://github.com/electron/electron/blob/master/docs/api/protocol.md + + /** + * The protocol module can register a custom protocol or intercept an existing protocol. + */ + interface Protocol { + /** + * Registers custom schemes as standard schemes. + */ + registerStandardSchemes(schemes: string[]): void; + /** + * Registers custom schemes to handle service workers. + */ + registerServiceWorkerSchemes(schemes: string[]): void; + /** + * Registers a protocol of scheme that will send the file as a response. + */ + registerFileProtocol(scheme: string, handler: FileProtocolHandler, completion?: (error: Error) => void): void; + /** + * Registers a protocol of scheme that will send a Buffer as a response. + */ + registerBufferProtocol(scheme: string, handler: BufferProtocolHandler, completion?: (error: Error) => void): void; + /** + * Registers a protocol of scheme that will send a String as a response. + */ + registerStringProtocol(scheme: string, handler: StringProtocolHandler, completion?: (error: Error) => void): void; + /** + * Registers a protocol of scheme that will send an HTTP request as a response. + */ + registerHttpProtocol(scheme: string, handler: HttpProtocolHandler, completion?: (error: Error) => void): void; + /** + * Unregisters the custom protocol of scheme. + */ + unregisterProtocol(scheme: string, completion?: (error: Error) => void): void; + /** + * The callback will be called with a boolean that indicates whether there is already a handler for scheme. + */ + isProtocolHandled(scheme: string, callback: (handled: boolean) => void): void; + /** + * Intercepts scheme protocol and uses handler as the protocol’s new handler which sends a file as a response. + */ + interceptFileProtocol(scheme: string, handler: FileProtocolHandler, completion?: (error: Error) => void): void; + /** + * Intercepts scheme protocol and uses handler as the protocol’s new handler which sends a Buffer as a response. + */ + interceptBufferProtocol(scheme: string, handler: BufferProtocolHandler, completion?: (error: Error) => void): void; + /** + * Intercepts scheme protocol and uses handler as the protocol’s new handler which sends a String as a response. + */ + interceptStringProtocol(scheme: string, handler: StringProtocolHandler, completion?: (error: Error) => void): void; + /** + * Intercepts scheme protocol and uses handler as the protocol’s new handler which sends a new HTTP request as a response. + */ + interceptHttpProtocol(scheme: string, handler: HttpProtocolHandler, completion?: (error: Error) => void): void; + /** + * Remove the interceptor installed for scheme and restore its original handler. + */ + uninterceptProtocol(scheme: string, completion?: (error: Error) => void): void; + } + + type FileProtocolHandler = (request: ProtocolRequest, callback: FileProtocolCallback) => void; + type BufferProtocolHandler = (request: ProtocolRequest, callback: BufferProtocolCallback) => void; + type StringProtocolHandler = (request: ProtocolRequest, callback: StringProtocolCallback) => void; + type HttpProtocolHandler = (request: ProtocolRequest, callback: HttpProtocolCallback) => void; + + interface ProtocolRequest { + url: string; + referrer: string; + method: string; + uploadData?: { + /** + * Content being sent. + */ + bytes: Buffer, + /** + * Path of file being uploaded. + */ + file: string, + /** + * UUID of blob data. Use session.getBlobData method to retrieve the data. + */ + blobUUID: string; + }[]; + } + + interface ProtocolCallback { + (error: number): void; + (obj: { + error: number + }): void; + (): void; + } + + interface FileProtocolCallback extends ProtocolCallback { + (filePath: string): void; + (obj: { + path: string + }): void; + } + + interface BufferProtocolCallback extends ProtocolCallback { + (buffer: Buffer): void; + (obj: { + data: Buffer, + mimeType: string, + charset?: string + }): void; + } + + interface StringProtocolCallback extends ProtocolCallback { + (str: string): void; + (obj: { + data: string, + mimeType: string, + charset?: string + }): void; + } + + interface HttpProtocolCallback extends ProtocolCallback { + (redirectRequest: { + url: string; + method: string; + session?: Object; + uploadData?: { + contentType: string; + data: string; + }; + }): void; + } + + // https://github.com/electron/electron/blob/master/docs/api/remote.md + + /** + * The remote module provides a simple way to do inter-process communication (IPC) + * between the renderer process (web page) and the main process. + */ + interface Remote extends CommonElectron { + /** + * @returns The object returned by require(module) in the main process. + */ + require(module: string): any; + /** + * @returns The BrowserWindow object which this web page belongs to. + */ + getCurrentWindow(): BrowserWindow; + /** + * @returns The WebContents object of this web page. + */ + getCurrentWebContents(): WebContents; + /** + * @returns The global variable of name (e.g. global[name]) in the main process. + */ + getGlobal(name: string): any; + /** + * Returns the process object in the main process. This is the same as + * remote.getGlobal('process'), but gets cached. + */ + process: NodeJS.Process; + } + + // https://github.com/electron/electron/blob/master/docs/api/screen.md + + /** + * The Display object represents a physical display connected to the system. + * A fake Display may exist on a headless system, or a Display may correspond to a remote, virtual display. + */ + interface Display { + /** + * Unique identifier associated with the display. + */ + id: number; + bounds: Rectangle; + workArea: Rectangle; + size: Size; + workAreaSize: Size; + /** + * Output device’s pixel scale factor. + */ + scaleFactor: number; + /** + * Can be 0, 90, 180, 270, represents screen rotation in clock-wise degrees. + */ + rotation: number; + touchSupport: 'available' | 'unavailable' | 'unknown'; + } + + type DisplayMetrics = 'bounds' | 'workArea' | 'scaleFactor' | 'rotation'; + + /** + * The screen module retrieves information about screen size, displays, cursor position, etc. + * You can not use this module until the ready event of the app module is emitted. + */ + interface Screen extends NodeJS.EventEmitter { + /** + * Emitted when newDisplay has been added. + */ + on(event: 'display-added', listener: (event: Event, newDisplay: Display) => void): this; + /** + * Emitted when oldDisplay has been removed. + */ + on(event: 'display-removed', listener: (event: Event, oldDisplay: Display) => void): this; + /** + * Emitted when one or more metrics change in a display. + */ + on(event: 'display-metrics-changed', listener: (event: Event, display: Display, changedMetrics: DisplayMetrics[]) => void): this; + on(event: string, listener: Function): this; + /** + * @returns The current absolute position of the mouse pointer. + */ + getCursorScreenPoint(): Point; + /** + * @returns The primary display. + */ + getPrimaryDisplay(): Display; + /** + * @returns An array of displays that are currently available. + */ + getAllDisplays(): Display[]; + /** + * @returns The display nearest the specified point. + */ + getDisplayNearestPoint(point: Point): Display; + /** + * @returns The display that most closely intersects the provided bounds. + */ + getDisplayMatching(rect: Rectangle): Display; + } + + // https://github.com/electron/electron/blob/master/docs/api/session.md + + /** + * The session module can be used to create new Session objects. + * You can also access the session of existing pages by using + * the session property of webContents which is a property of BrowserWindow. + */ + class Session extends NodeJS.EventEmitter { + /** + * @returns a new Session instance from partition string. + */ + static fromPartition(partition: string, options?: FromPartitionOptions): Session; + /** + * @returns the default session object of the app. + */ + static defaultSession: Session; + /** + * Emitted when Electron is about to download item in webContents. + * Calling event.preventDefault() will cancel the download + * and item will not be available from next tick of the process. + */ + on(event: 'will-download', listener: (event: Event, item: DownloadItem, webContents: WebContents) => void): this; + on(event: string, listener: Function): this; + /** + * The cookies gives you ability to query and modify cookies. + */ + cookies: SessionCookies; + /** + * @returns the session’s current cache size. + */ + getCacheSize(callback: (size: number) => void): void; + /** + * Clears the session’s HTTP cache. + */ + clearCache(callback: Function): void; + /** + * Clears the data of web storages. + */ + clearStorageData(callback: Function): void; + /** + * Clears the data of web storages. + */ + clearStorageData(options: ClearStorageDataOptions, callback: Function): void; + /** + * Writes any unwritten DOMStorage data to disk. + */ + flushStorageData(): void; + /** + * Sets the proxy settings. + */ + setProxy(config: ProxyConfig, callback: Function): void; + /** + * Resolves the proxy information for url. + */ + resolveProxy(url: URL, callback: (proxy: string) => void): void; + /** + * Sets download saving directory. + * By default, the download directory will be the Downloads under the respective app folder. + */ + setDownloadPath(path: string): void; + /** + * Emulates network with the given configuration for the session. + */ + enableNetworkEmulation(options: NetworkEmulationOptions): void; + /** + * Disables any network emulation already active for the session. + * Resets to the original network configuration. + */ + disableNetworkEmulation(): void; + /** + * Sets the certificate verify proc for session, the proc will be called + * whenever a server certificate verification is requested. + * + * Calling setCertificateVerifyProc(null) will revert back to default certificate verify proc. + */ + setCertificateVerifyProc(proc: (hostname: string, cert: Certificate, callback: (accepted: boolean) => void) => void): void; + /** + * Sets the handler which can be used to respond to permission requests for the session. + */ + setPermissionRequestHandler(handler: (webContents: WebContents, permission: Permission, callback: (allow: boolean) => void) => void): void; + /** + * Clears the host resolver cache. + */ + clearHostResolverCache(callback: Function): void; + /** + * Dynamically sets whether to always send credentials for HTTP NTLM or Negotiate authentication. + * @param domains Comma-seperated list of servers for which integrated authentication is enabled. + */ + allowNTLMCredentialsForDomains(domains: string): void; + /** + * Overrides the userAgent and acceptLanguages for this session. + * The acceptLanguages must a comma separated ordered list of language codes, for example "en-US,fr,de,ko,zh-CN,ja". + * This doesn't affect existing WebContents, and each WebContents can use webContents.setUserAgent to override the session-wide user agent. + */ + setUserAgent(userAgent: string, acceptLanguages?: string): void; + /** + * @returns The user agent for this session. + */ + getUserAgent(): string; + /** + * Returns the blob data associated with the identifier. + */ + getBlobData(identifier: string, callback: (result: Buffer) => void): void; + /** + * The webRequest API set allows to intercept and modify contents of a request at various stages of its lifetime. + */ + webRequest: WebRequest; + /** + * @returns An instance of protocol module for this session. + */ + protocol: Protocol; + } + + type Permission = 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal'; + + interface FromPartitionOptions { + /** + * Whether to enable cache. + */ + cache?: boolean; + } + + interface ClearStorageDataOptions { + /** + * Should follow window.location.origin’s representation scheme://host:port. + */ + origin?: string; + /** + * The types of storages to clear. + */ + storages?: ('appcache' | 'cookies' | 'filesystem' | 'indexdb' | 'localstorage' | 'shadercache' | 'websql' | 'serviceworkers')[]; + /** + * The types of quotas to clear. + */ + quotas?: ('temporary' | 'persistent' | 'syncable')[]; + } + + interface ProxyConfig { + /** + * The URL associated with the PAC file. + */ + pacScript?: string; + /** + * Rules indicating which proxies to use. + */ + proxyRules?: string; + /** + * Rules indicating which URLs should bypass the proxy settings. + */ + proxyBypassRules?: string; + } + + interface NetworkEmulationOptions { + /** + * Whether to emulate network outage. + * Default: false. + */ + offline?: boolean; + /** + * RTT in ms. + * Default: 0, which will disable latency throttling. + */ + latency?: number; + /** + * Download rate in Bps. + * Default: 0, which will disable download throttling. + */ + downloadThroughput?: number; + /** + * Upload rate in Bps. + * Default: 0, which will disable upload throttling. + */ + uploadThroughput?: number; + } + + interface CookieFilter { + /** + * Retrieves cookies which are associated with url. Empty implies retrieving cookies of all urls. + */ + url?: string; + /** + * Filters cookies by name. + */ + name?: string; + /** + * Retrieves cookies whose domains match or are subdomains of domains. + */ + domain?: string; + /** + * Retrieves cookies whose path matches path. + */ + path?: string; + /** + * Filters cookies by their Secure property. + */ + secure?: boolean; + /** + * Filters out session or persistent cookies. + */ + session?: boolean; + } + + interface Cookie { + /** + * Emitted when a cookie is changed because it was added, edited, removed, or expired. + */ + on(event: 'changed', listener: (event: Event, cookie: Cookie, cause: CookieChangedCause) => void): this; + on(event: string, listener: Function): this; + /** + * The name of the cookie. + */ + name: string; + /** + * The value of the cookie. + */ + value: string; + /** + * The domain of the cookie. + */ + domain: string; + /** + * Whether the cookie is a host-only cookie. + */ + hostOnly: string; + /** + * The path of the cookie. + */ + path: string; + /** + * Whether the cookie is marked as secure. + */ + secure: boolean; + /** + * Whether the cookie is marked as HTTP only. + */ + httpOnly: boolean; + /** + * Whether the cookie is a session cookie or a persistent cookie with an expiration date. + */ + session: boolean; + /** + * The expiration date of the cookie as the number of seconds since the UNIX epoch. + * Not provided for session cookies. + */ + expirationDate?: number; + } + + type CookieChangedCause = 'explicit' | 'overwrite' | 'expired' | 'evicted' | 'expired-overwrite'; + + interface CookieDetails { + /** + * The URL associated with the cookie. + */ + url: string; + /** + * The name of the cookie. + * Default: empty. + */ + name?: string; + /** + * The value of the cookie. + * Default: empty. + */ + value?: string; + /** + * The domain of the cookie. + * Default: empty. + */ + domain?: string; + /** + * The path of the cookie. + * Default: empty. + */ + path?: string; + /** + * Whether the cookie should be marked as secure. + * Default: false. + */ + secure?: boolean; + /** + * Whether the cookie should be marked as HTTP only. + * Default: false. + */ + httpOnly?: boolean; + /** + * The expiration date of the cookie as the number of seconds since the UNIX epoch. + * If omitted, the cookie becomes a session cookie. + */ + expirationDate?: number; + } + + interface SessionCookies { + /** + * Sends a request to get all cookies matching filter. + */ + get(filter: CookieFilter, callback: (error: Error, cookies: Cookie[]) => void): void; + /** + * Sets the cookie with details. + */ + set(details: CookieDetails, callback: (error: Error) => void): void; + /** + * Removes the cookies matching url and name. + */ + remove(url: string, name: string, callback: (error: Error) => void): void; + } + + /** + * Each API accepts an optional filter and a listener, the listener will be called when the API's event has happened. + * Passing null as listener will unsubscribe from the event. + * + * The filter will be used to filter out the requests that do not match the URL patterns. + * If the filter is omitted then all requests will be matched. + * + * For certain events the listener is passed with a callback, + * which should be called with an response object when listener has done its work. + */ + interface WebRequest { + /** + * The listener will be called when a request is about to occur. + */ + onBeforeRequest(listener: (details: WebRequest.BeforeRequestDetails, callback: WebRequest.BeforeRequestCallback) => void): void; + /** + * The listener will be called when a request is about to occur. + */ + onBeforeRequest(filter: WebRequest.Filter, listener: (details: WebRequest.BeforeRequestDetails, callback: WebRequest.BeforeRequestCallback) => void): void; + /** + * The listener will be called before sending an HTTP request, once the request headers are available. + * This may occur after a TCP connection is made to the server, but before any http data is sent. + */ + onBeforeSendHeaders(listener: (details: WebRequest.BeforeSendHeadersDetails, callback: WebRequest.BeforeSendHeadersCallback) => void): void; + /** + * The listener will be called before sending an HTTP request, once the request headers are available. + * This may occur after a TCP connection is made to the server, but before any http data is sent. + */ + onBeforeSendHeaders(filter: WebRequest.Filter, listener: (details: WebRequest.BeforeSendHeadersDetails, callback: WebRequest.BeforeSendHeadersCallback) => void): void; + /** + * The listener will be called just before a request is going to be sent to the server, + * modifications of previous onBeforeSendHeaders response are visible by the time this listener is fired. + */ + onSendHeaders(listener: (details: WebRequest.SendHeadersDetails) => void): void; + /** + * The listener will be called just before a request is going to be sent to the server, + * modifications of previous onBeforeSendHeaders response are visible by the time this listener is fired. + */ + onSendHeaders(filter: WebRequest.Filter, listener: (details: WebRequest.SendHeadersDetails) => void): void; + /** + * The listener will be called when HTTP response headers of a request have been received. + */ + onHeadersReceived(listener: (details: WebRequest.HeadersReceivedDetails, callback: WebRequest.HeadersReceivedCallback) => void): void; + /** + * The listener will be called when HTTP response headers of a request have been received. + */ + onHeadersReceived(filter: WebRequest.Filter, listener: (details: WebRequest.HeadersReceivedDetails, callback: WebRequest.HeadersReceivedCallback) => void): void; + /** + * The listener will be called when first byte of the response body is received. + * For HTTP requests, this means that the status line and response headers are available. + */ + onResponseStarted(listener: (details: WebRequest.ResponseStartedDetails) => void): void; + /** + * The listener will be called when first byte of the response body is received. + * For HTTP requests, this means that the status line and response headers are available. + */ + onResponseStarted(filter: WebRequest.Filter, listener: (details: WebRequest.ResponseStartedDetails) => void): void; + /** + * The listener will be called when a server initiated redirect is about to occur. + */ + onBeforeRedirect(listener: (details: WebRequest.BeforeRedirectDetails) => void): void; + /** + * The listener will be called when a server initiated redirect is about to occur. + */ + onBeforeRedirect(filter: WebRequest.Filter, listener: (details: WebRequest.BeforeRedirectDetails) => void): void; + /** + * The listener will be called when a request is completed. + */ + onCompleted(listener: (details: WebRequest.CompletedDetails) => void): void; + /** + * The listener will be called when a request is completed. + */ + onCompleted(filter: WebRequest.Filter, listener: (details: WebRequest.CompletedDetails) => void): void; + /** + * The listener will be called when an error occurs. + */ + onErrorOccurred(listener: (details: WebRequest.ErrorOccurredDetails) => void): void; + /** + * The listener will be called when an error occurs. + */ + onErrorOccurred(filter: WebRequest.Filter, listener: (details: WebRequest.ErrorOccurredDetails) => void): void; + } + + namespace WebRequest { + interface Filter { + urls: string[]; + } + + interface Details { + id: number; + url: string; + method: string; + resourceType: string; + timestamp: number; + } + + interface UploadData { + /** + * Content being sent. + */ + bytes: Buffer; + /** + * Path of file being uploaded. + */ + file: string; + /** + * UUID of blob data. Use session.getBlobData method to retrieve the data. + */ + blobUUID: string; + } + + interface BeforeRequestDetails extends Details { + uploadData?: UploadData[]; + } + + type BeforeRequestCallback = (response: { + cancel?: boolean; + /** + * The original request is prevented from being sent or completed, and is instead redirected to the given URL. + */ + redirectURL?: string; + }) => void; + + interface BeforeSendHeadersDetails extends Details { + requestHeaders: Headers; + } + + type BeforeSendHeadersCallback = (response: { + cancel?: boolean; + /** + * When provided, request will be made with these headers. + */ + requestHeaders?: Headers; + }) => void; + + interface SendHeadersDetails extends Details { + requestHeaders: Headers; + } + + interface HeadersReceivedDetails extends Details { + statusLine: string; + statusCode: number; + responseHeaders: Headers; + } + + type HeadersReceivedCallback = (response: { + cancel?: boolean; + /** + * When provided, the server is assumed to have responded with these headers. + */ + responseHeaders?: Headers; + /** + * Should be provided when overriding responseHeaders to change header status + * otherwise original response header's status will be used. + */ + statusLine?: string; + }) => void; + + interface ResponseStartedDetails extends Details { + responseHeaders: Headers; + fromCache: boolean; + statusCode: number; + statusLine: string; + } + + interface BeforeRedirectDetails extends Details { + redirectURL: string; + statusCode: number; + ip?: string; + fromCache: boolean; + responseHeaders: Headers; + } + + interface CompletedDetails extends Details { + responseHeaders: Headers; + fromCache: boolean; + statusCode: number; + statusLine: string; + } + + interface ErrorOccurredDetails extends Details { + fromCache: boolean; + error: string; + } + } + + // https://github.com/electron/electron/blob/master/docs/api/shell.md + + /** + * The shell module provides functions related to desktop integration. + */ + interface Shell { + /** + * Show the given file in a file manager. If possible, select the file. + * @returns Whether the item was successfully shown. + */ + showItemInFolder(fullPath: string): boolean; + /** + * Open the given file in the desktop's default manner. + * @returns Whether the item was successfully shown. + */ + openItem(fullPath: string): boolean; + /** + * Open the given external protocol URL in the desktop's default manner + * (e.g., mailto: URLs in the default mail user agent). + * @returns Whether an application was available to open the URL. + */ + openExternal(url: string, options?: { + /** + * Bring the opened application to the foreground. + * Default: true. + */ + activate: boolean; + }): boolean; + /** + * Move the given file to trash. + * @returns Whether the item was successfully moved to the trash. + */ + moveItemToTrash(fullPath: string): boolean; + /** + * Play the beep sound. + */ + beep(): void; + /** + * Creates or updates a shortcut link at shortcutPath. + * + * Note: This API is available only on Windows. + */ + writeShortcutLink(shortcutPath: string, options: ShortcutLinkOptions): boolean; + /** + * Creates or updates a shortcut link at shortcutPath. + * + * Note: This API is available only on Windows. + */ + writeShortcutLink(shortcutPath: string, operation: 'create' | 'update' | 'replace', options: ShortcutLinkOptions): boolean; + /** + * Resolves the shortcut link at shortcutPath. + * An exception will be thrown when any error happens. + * + * Note: This API is available only on Windows. + */ + readShortcutLink(shortcutPath: string): ShortcutLinkOptions; + } + + interface ShortcutLinkOptions { + /** + * The target to launch from this shortcut. + */ + target: string; + /** + * The working directory. + * Default: empty. + */ + cwd?: string; + /** + * The arguments to be applied to target when launching from this shortcut. + * Default: empty. + */ + args?: string; + /** + * The description of the shortcut. + * Default: empty. + */ + description?: string; + /** + * The path to the icon, can be a DLL or EXE. icon and iconIndex have to be set together. + * Default: empty, which uses the target's icon. + */ + icon?: string; + /** + * The resource ID of icon when icon is a DLL or EXE. + * Default: 0. + */ + iconIndex?: number; + /** + * The Application User Model ID. + * Default: empty. + */ + appUserModelId?: string; + } + + // https://github.com/electron/electron/blob/master/docs/api/system-preferences.md + + type SystemColor = + '3d-dark-shadow' | // Dark shadow for three-dimensional display elements. + '3d-face' | // Face color for three-dimensional display elements and for dialog box backgrounds. + '3d-highlight' | // Highlight color for three-dimensional display elements. + '3d-light' | // Light color for three-dimensional display elements. + '3d-shadow' | // Shadow color for three-dimensional display elements. + 'active-border' | // Active window border. + 'active-caption' | // Active window title bar. Specifies the left side color in the color gradient of an active window's title bar if the gradient effect is enabled. + 'active-caption-gradient' | // Right side color in the color gradient of an active window's title bar. + 'app-workspace' | // Background color of multiple document interface (MDI) applications. + 'button-text' | // Text on push buttons. + 'caption-text' | // Text in caption, size box, and scroll bar arrow box. + 'desktop' | // Desktop background color. + 'disabled-text' | // Grayed (disabled) text. + 'highlight' | // Item(s) selected in a control. + 'highlight-text' | // Text of item(s) selected in a control. + 'hotlight' | // Color for a hyperlink or hot-tracked item. + 'inactive-border' | // Inactive window border. + 'inactive-caption' | // Inactive window caption. Specifies the left side color in the color gradient of an inactive window's title bar if the gradient effect is enabled. + 'inactive-caption-gradient' | // Right side color in the color gradient of an inactive window's title bar. + 'inactive-caption-text' | // Color of text in an inactive caption. + 'info-background' | // Background color for tooltip controls. + 'info-text' | // Text color for tooltip controls. + 'menu' | // Menu background. + 'menu-highlight' | // The color used to highlight menu items when the menu appears as a flat menu. + 'menubar' | // The background color for the menu bar when menus appear as flat menus. + 'menu-text' | // Text in menus. + 'scrollbar' | // Scroll bar gray area. + 'window' | // Window background. + 'window-frame' | // Window frame. + 'window-text'; // Text in windows. + + /** + * Get system preferences. + */ + interface SystemPreferences { + /** + * Note: This is only implemented on Windows. + */ + on(event: 'accent-color-changed', listener: (event: Event, newColor: string) => void): this; + /** + * Note: This is only implemented on Windows. + */ + on(event: 'color-changed', listener: (event: Event) => void): this; + /** + * Note: This is only implemented on Windows. + */ + on(event: 'inverted-color-scheme-changed', listener: ( + event: Event, + /** + * @param invertedColorScheme true if an inverted color scheme, such as a high contrast theme, is being used, false otherwise. + */ + invertedColorScheme: boolean + ) => void): this; + on(event: string, listener: Function): this; + /** + * @returns Whether the system is in Dark Mode. + * + * Note: This is only implemented on macOS. + */ + isDarkMode(): boolean; + /** + * @returns Whether the Swipe between pages setting is on. + * + * Note: This is only implemented on macOS. + */ + isSwipeTrackingFromScrollEventsEnabled(): boolean; + /** + * Posts event as native notifications of macOS. + * The userInfo contains the user information dictionary sent along with the notification. + * + * Note: This is only implemented on macOS. + */ + postNotification(event: string, userInfo: Object): void; + /** + * Posts event as native notifications of macOS. + * The userInfo contains the user information dictionary sent along with the notification. + * + * Note: This is only implemented on macOS. + */ + postLocalNotification(event: string, userInfo: Object): void; + /** + * Subscribes to native notifications of macOS, callback will be called when the corresponding event happens. + * The id of the subscriber is returned, which can be used to unsubscribe the event. + * + * Note: This is only implemented on macOS. + */ + subscribeNotification(event: string, callback: (event: Event, userInfo: Object) => void): number; + /** + * Removes the subscriber with id. + * + * Note: This is only implemented on macOS. + */ + unsubscribeNotification(id: number): void; + /** + * Same as subscribeNotification, but uses NSNotificationCenter for local defaults. + */ + subscribeLocalNotification(event: string, callback: (event: Event, userInfo: Object) => void): number; + /** + * Same as unsubscribeNotification, but removes the subscriber from NSNotificationCenter. + */ + unsubscribeLocalNotification(id: number): void; + /** + * Get the value of key in system preferences. + * + * Note: This is only implemented on macOS. + */ + getUserDefault(key: string, type: 'string' | 'boolean' | 'integer' | 'float' | 'double' | 'url' | 'array' | 'dictionary'): any; + /** + * @returns Whether DWM composition (Aero Glass) is enabled. + * + * Note: This is only implemented on Windows. + */ + isAeroGlassEnabled(): boolean; + /** + * @returns The users current system wide color preference in the form of an RGBA hexadecimal string. + * + * Note: This is only implemented on Windows. + */ + getAccentColor(): string; + /** + * @returns true if an inverted color scheme, such as a high contrast theme, is active, false otherwise. + * + * Note: This is only implemented on Windows. + */ + isInvertedColorScheme(): boolean; + /** + * @returns The system color setting in RGB hexadecimal form (#ABCDEF). See the Windows docs for more details. + * + * Note: This is only implemented on Windows. + */ + getColor(color: SystemColor): string; + } + + // https://github.com/electron/electron/blob/master/docs/api/tray.md + + /** + * A Tray represents an icon in an operating system's notification area. + */ + class Tray extends NodeJS.EventEmitter implements Destroyable { + /** + * Emitted when the tray icon is clicked. + * Note: The bounds payload is only implemented on macOS and Windows. + */ + on(event: 'click', listener: (modifiers: Modifiers, bounds: Rectangle) => void): this; + /** + * Emitted when the tray icon is right clicked. + * Note: This is only implemented on macOS and Windows. + */ + on(event: 'right-click', listener: (modifiers: Modifiers, bounds: Rectangle) => void): this; + /** + * Emitted when the tray icon is double clicked. + * Note: This is only implemented on macOS and Windows. + */ + on(event: 'double-click', listener: (modifiers: Modifiers, bounds: Rectangle) => void): this; + /** + * Emitted when the tray balloon shows. + * Note: This is only implemented on Windows. + */ + on(event: 'balloon-show', listener: Function): this; + /** + * Emitted when the tray balloon is clicked. + * Note: This is only implemented on Windows. + */ + on(event: 'balloon-click', listener: Function): this; + /** + * Emitted when the tray balloon is closed because of timeout or user manually closes it. + * Note: This is only implemented on Windows. + */ + on(event: 'balloon-closed', listener: Function): this; + /** + * Emitted when any dragged items are dropped on the tray icon. + * Note: This is only implemented on macOS. + */ + on(event: 'drop', listener: Function): this; + /** + * Emitted when dragged files are dropped in the tray icon. + * Note: This is only implemented on macOS + */ + on(event: 'drop-files', listener: (event: Event, files: string[]) => void): this; + /** + * Emitted when dragged text is dropped in the tray icon. + * Note: This is only implemented on macOS + */ + on(event: 'drop-text', listener: (event: Event, text: string) => void): this; + /** + * Emitted when a drag operation enters the tray icon. + * Note: This is only implemented on macOS + */ + on(event: 'drag-enter', listener: Function): this; + /** + * Emitted when a drag operation exits the tray icon. + * Note: This is only implemented on macOS + */ + on(event: 'drag-leave', listener: Function): this; + /** + * Emitted when a drag operation ends on the tray or ends at another location. + * Note: This is only implemented on macOS + */ + on(event: 'drag-end', listener: Function): this; + on(event: string, listener: Function): this; + /** + * Creates a new tray icon associated with the image. + */ + constructor(image: NativeImage | string); + /** + * Destroys the tray icon immediately. + */ + destroy(): void; + /** + * Sets the image associated with this tray icon. + */ + setImage(image: NativeImage | string): void; + /** + * Sets the image associated with this tray icon when pressed. + */ + setPressedImage(image: NativeImage): void; + /** + * Sets the hover text for this tray icon. + */ + setToolTip(toolTip: string): void; + /** + * Sets the title displayed aside of the tray icon in the status bar. + * Note: This is only implemented on macOS. + */ + setTitle(title: string): void; + /** + * Sets when the tray's icon background becomes highlighted. + * Note: This is only implemented on macOS. + */ + setHighlightMode(mode: 'selection' | 'always' | 'never'): void; + /** + * Displays a tray balloon. + * Note: This is only implemented on Windows. + */ + displayBalloon(options?: { + icon?: NativeImage; + title?: string; + content?: string; + }): void; + /** + * Pops up the context menu of tray icon. When menu is passed, + * the menu will showed instead of the tray's context menu. + * The position is only available on Windows, and it is (0, 0) by default. + * Note: This is only implemented on macOS and Windows. + */ + popUpContextMenu(menu?: Menu, position?: Point): void; + /** + * Sets the context menu for this icon. + */ + setContextMenu(menu: Menu): void; + /** + * @returns The bounds of this tray icon. + */ + getBounds(): Rectangle; + /** + * @returns Whether the tray icon is destroyed. + */ + isDestroyed(): boolean; + } + + interface Modifiers { + altKey: boolean; + shiftKey: boolean; + ctrlKey: boolean; + metaKey: boolean; + } + + interface DragItem { + /** + * The absolute path of the file to be dragged + */ + file: string; + /** + * The image showing under the cursor when dragging. + */ + icon: NativeImage; + } + + // https://github.com/electron/electron/blob/master/docs/api/web-contents.md + + interface WebContentsStatic { + /** + * @returns An array of all WebContents instances. This will contain web contents for all windows, + * webviews, opened devtools, and devtools extension background pages. + */ + getAllWebContents(): WebContents[]; + /** + * @returns The web contents that is focused in this application, otherwise returns null. + */ + getFocusedWebContents(): WebContents; + /** + * Find a WebContents instance according to its ID. + */ + fromId(id: number): WebContents; + } + + /** + * A WebContents is responsible for rendering and controlling a web page. + */ + interface WebContents extends NodeJS.EventEmitter { + /** + * Emitted when the navigation is done, i.e. the spinner of the tab has stopped spinning, + * and the onload event was dispatched. + */ + on(event: 'did-finish-load', listener: Function): this; + /** + * This event is like did-finish-load but emitted when the load failed or was cancelled, + * e.g. window.stop() is invoked. + */ + on(event: 'did-fail-load', listener: (event: Event, errorCode: number, errorDescription: string, validatedURL: string, isMainFrame: boolean) => void): this; + /** + * Emitted when a frame has done navigation. + */ + on(event: 'did-frame-finish-load', listener: (event: Event, isMainFrame: boolean) => void): this; + /** + * Corresponds to the points in time when the spinner of the tab started spinning. + */ + on(event: 'did-start-loading', listener: Function): this; + /** + * Corresponds to the points in time when the spinner of the tab stopped spinning. + */ + on(event: 'did-stop-loading', listener: Function): this; + /** + * Emitted when details regarding a requested resource are available. + * status indicates the socket connection to download the resource. + */ + on(event: 'did-get-response-details', listener: (event: Event, + status: boolean, + newURL: string, + originalURL: string, + httpResponseCode: number, + requestMethod: string, + referrer: string, + headers: Headers, + resourceType: string + ) => void): this; + /** + * Emitted when a redirect is received while requesting a resource. + */ + on(event: 'did-get-redirect-request', listener: (event: Event, + oldURL: string, + newURL: string, + isMainFrame: boolean, + httpResponseCode: number, + requestMethod: string, + referrer: string, + headers: Headers + ) => void): this; + /** + * Emitted when the document in the given frame is loaded. + */ + on(event: 'dom-ready', listener: (event: Event) => void): this; + /** + * Emitted when page receives favicon URLs. + */ + on(event: 'page-favicon-updated', listener: (event: Event, favicons: string[]) => void): this; + /** + * Emitted when the page requests to open a new window for a url. + * It could be requested by window.open or an external link like . + * + * By default a new BrowserWindow will be created for the url. + * + * Calling event.preventDefault() will prevent creating new windows. + */ + on(event: 'new-window', listener: (event: Event, + url: string, + frameName: string, + disposition: NewWindowDisposition, + options: BrowserWindowOptions + ) => void): this; + /** + * Emitted when a user or the page wants to start navigation. + * It can happen when the window.location object is changed or a user clicks a link in the page. + * + * This event will not emit when the navigation is started programmatically with APIs like + * webContents.loadURL and webContents.back. + * + * It is also not emitted for in-page navigations, such as clicking anchor links + * or updating the window.location.hash. Use did-navigate-in-page event for this purpose. + * + * Calling event.preventDefault() will prevent the navigation. + */ + on(event: 'will-navigate', listener: (event: Event, url: string) => void): this; + /** + * Emitted when a navigation is done. + * + * This event is not emitted for in-page navigations, such as clicking anchor links + * or updating the window.location.hash. Use did-navigate-in-page event for this purpose. + */ + on(event: 'did-navigate', listener: (event: Event, url: string) => void): this; + /** + * Emitted when an in-page navigation happened. + * + * When in-page navigation happens, the page URL changes but does not cause + * navigation outside of the page. Examples of this occurring are when anchor links + * are clicked or when the DOM hashchange event is triggered. + */ + on(event: 'did-navigate-in-page', listener: (event: Event, url: string, isMainFrame: boolean) => void): this; + /** + * Emitted when the renderer process has crashed. + */ + on(event: 'crashed', listener: (event: Event, killed: boolean) => void): this; + /** + * Emitted when a plugin process has crashed. + */ + on(event: 'plugin-crashed', listener: (event: Event, name: string, version: string) => void): this; + /** + * Emitted when webContents is destroyed. + */ + on(event: 'destroyed', listener: Function): this; + /** + * Emitted when DevTools is opened. + */ + on(event: 'devtools-opened', listener: Function): this; + /** + * Emitted when DevTools is closed. + */ + on(event: 'devtools-closed', listener: Function): this; + /** + * Emitted when DevTools is focused / opened. + */ + on(event: 'devtools-focused', listener: Function): this; + /** + * Emitted when failed to verify the certificate for url. + * The usage is the same with the "certificate-error" event of app. + */ + on(event: 'certificate-error', listener: (event: Event, + url: string, + error: string, + certificate: Certificate, + callback: (trust: boolean) => void + ) => void): this; + /** + * Emitted when a client certificate is requested. + * The usage is the same with the "select-client-certificate" event of app. + */ + on(event: 'select-client-certificate', listener: (event: Event, + url: string, + certificateList: Certificate[], + callback: (certificate: Certificate) => void + ) => void): this; + /** + * Emitted when webContents wants to do basic auth. + * The usage is the same with the "login" event of app. + */ + on(event: 'login', listener: (event: Event, + request: LoginRequest, + authInfo: LoginAuthInfo, + callback: (username: string, password: string) => void + ) => void): this; + /** + * Emitted when a result is available for webContents.findInPage request. + */ + on(event: 'found-in-page', listener: (event: Event, result: FoundInPageResult) => void): this; + /** + * Emitted when media starts playing. + */ + on(event: 'media-started-playing', listener: Function): this; + /** + * Emitted when media is paused or done playing. + */ + on(event: 'media-paused', listener: Function): this; + /** + * Emitted when a page’s theme color changes. This is usually due to encountering a meta tag: + * + */ + on(event: 'did-change-theme-color', listener: Function): this; + /** + * Emitted when mouse moves over a link or the keyboard moves the focus to a link. + */ + on(event: 'update-target-url', listener: (event: Event, url: string) => void): this; + /** + * Emitted when the cursor’s type changes. + * If the type parameter is custom, the image parameter will hold the custom cursor image + * in a NativeImage, and scale, size and hotspot will hold additional information about the custom cursor. + */ + on(event: 'cursor-changed', listener: (event: Event, type: CursorType, image?: NativeImage, scale?: number, size?: Size, hotspot?: Point) => void): this; + /** + * Emitted when there is a new context menu that needs to be handled. + */ + on(event: 'context-menu', listener: (event: Event, params: ContextMenuParams) => void): this; + /** + * Emitted when bluetooth device needs to be selected on call to navigator.bluetooth.requestDevice. + * To use navigator.bluetooth api webBluetooth should be enabled. + * If event.preventDefault is not called, first available device will be selected. + * callback should be called with deviceId to be selected, + * passing empty string to callback will cancel the request. + */ + on(event: 'select-bluetooth-device', listener: (event: Event, deviceList: BluetoothDevice[], callback: (deviceId: string) => void) => void): this; + /** + * Emitted when a new frame is generated. Only the dirty area is passed in the buffer. + */ + on(event: 'paint', listener: (event: Event, dirtyRect: Rectangle, image: NativeImage) => void): this; + on(event: string, listener: Function): this; + /** + * Loads the url in the window. + * @param url Must contain the protocol prefix (e.g., the http:// or file://). + */ + loadURL(url: string, options?: LoadURLOptions): void; + /** + * Initiates a download of the resource at url without navigating. + * The will-download event of session will be triggered. + */ + downloadURL(url: string): void; + /** + * @returns The URL of current web page. + */ + getURL(): string; + /** + * @returns The title of web page. + */ + getTitle(): string; + /** + * @returns Whether the web page is destroyed. + */ + isDestroyed(): boolean; + /** + * @returns Whether the web page is focused. + */ + isFocused(): boolean; + /** + * @returns Whether web page is still loading resources. + */ + isLoading(): boolean; + /** + * @returns Whether the main frame (and not just iframes or frames within it) is still loading. + */ + isLoadingMainFrame(): boolean; + /** + * @returns Whether web page is waiting for a first-response for the main + * resource of the page. + */ + isWaitingForResponse(): boolean; + /** + * Stops any pending navigation. + */ + stop(): void; + /** + * Reloads current page. + */ + reload(): void; + /** + * Reloads current page and ignores cache. + */ + reloadIgnoringCache(): void; + /** + * @returns Whether the web page can go back. + */ + canGoBack(): boolean; + /** + * @returns Whether the web page can go forward. + */ + canGoForward(): boolean; + /** + * @returns Whether the web page can go to offset. + */ + canGoToOffset(offset: number): boolean; + /** + * Clears the navigation history. + */ + clearHistory(): void; + /** + * Makes the web page go back. + */ + goBack(): void; + /** + * Makes the web page go forward. + */ + goForward(): void; + /** + * Navigates to the specified absolute index. + */ + goToIndex(index: number): void; + /** + * Navigates to the specified offset from the "current entry". + */ + goToOffset(offset: number): void; + /** + * @returns Whether the renderer process has crashed. + */ + isCrashed(): boolean; + /** + * Overrides the user agent for this page. + */ + setUserAgent(userAgent: string): void; + /** + * @returns The user agent for this web page. + */ + getUserAgent(): string; + /** + * Injects CSS into this page. + */ + insertCSS(css: string): void; + /** + * Evaluates code in page. + * @param code Code to evaluate. + * + * @returns Promise + */ + executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; + /** + * Mute the audio on the current web page. + */ + setAudioMuted(muted: boolean): void; + /** + * @returns Whether this page has been muted. + */ + isAudioMuted(): boolean; + /** + * Changes the zoom factor to the specified factor. + * Zoom factor is zoom percent divided by 100, so 300% = 3.0. + */ + setZoomFactor(factor: number): void; + /** + * Sends a request to get current zoom factor. + */ + getZoomFactor(callback: (zoomFactor: number) => void): void; + /** + * Changes the zoom level to the specified level. + * The original size is 0 and each increment above or below represents + * zooming 20% larger or smaller to default limits of 300% and 50% of original size, respectively. + */ + setZoomLevel(level: number): void; + /** + * Sends a request to get current zoom level. + */ + getZoomLevel(callback: (zoomLevel: number) => void): void; + /** + * Sets the maximum and minimum zoom level. + */ + setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; + /** + * Executes the editing command undo in web page. + */ + undo(): void; + /** + * Executes the editing command redo in web page. + */ + redo(): void; + /** + * Executes the editing command cut in web page. + */ + cut(): void; + /** + * Executes the editing command copy in web page. + */ + copy(): void; + /** + * Copy the image at the given position to the clipboard. + */ + copyImageAt(x: number, y: number): void; + /** + * Executes the editing command paste in web page. + */ + paste(): void; + /** + * Executes the editing command pasteAndMatchStyle in web page. + */ + pasteAndMatchStyle(): void; + /** + * Executes the editing command delete in web page. + */ + delete(): void; + /** + * Executes the editing command selectAll in web page. + */ + selectAll(): void; + /** + * Executes the editing command unselect in web page. + */ + unselect(): void; + /** + * Executes the editing command replace in web page. + */ + replace(text: string): void; + /** + * Executes the editing command replaceMisspelling in web page. + */ + replaceMisspelling(text: string): void; + /** + * Inserts text to the focused element. + */ + insertText(text: string): void; + /** + * Starts a request to find all matches for the text in the web page. + * The result of the request can be obtained by subscribing to found-in-page event. + * @returns The request id used for the request. + */ + findInPage(text: string, options?: FindInPageOptions): number; + /** + * Stops any findInPage request for the webContents with the provided action. + */ + stopFindInPage(action: StopFindInPageAtion): void; + /** + * Checks if any serviceworker is registered. + */ + hasServiceWorker(callback: (hasServiceWorker: boolean) => void): void; + /** + * Unregisters any serviceworker if present. + */ + unregisterServiceWorker(callback: (isFulfilled: boolean) => void): void; + /** + * Prints window's web page. When silent is set to false, Electron will pick up system's default printer and default settings for printing. + * Calling window.print() in web page is equivalent to call WebContents.print({silent: false, printBackground: false}). + * Note: On Windows, the print API relies on pdf.dll. If your application doesn't need print feature, you can safely remove pdf.dll in saving binary size. + */ + print(options?: PrintOptions): void; + /** + * Prints windows' web page as PDF with Chromium's preview printing custom settings. + */ + printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; + /** + * Adds the specified path to DevTools workspace. + */ + addWorkSpace(path: string): void; + /** + * Removes the specified path from DevTools workspace. + */ + removeWorkSpace(path: string): void; + /** + * Opens the developer tools. + */ + openDevTools(options?: { + /** + * Opens the devtools with specified dock state. Defaults to last used dock state. + */ + mode?: 'right' | 'bottom' | 'undocked' | 'detach' + }): void; + /** + * Closes the developer tools. + */ + closeDevTools(): void; + /** + * Returns whether the developer tools are opened. + */ + isDevToolsOpened(): boolean; + /** + * Returns whether the developer tools are focused. + */ + isDevToolsFocused(): boolean; + /** + * Toggle the developer tools. + */ + toggleDevTools(): void; + /** + * Starts inspecting element at position (x, y). + */ + inspectElement(x: number, y: number): void; + /** + * Opens the developer tools for the service worker context. + */ + inspectServiceWorker(): void; + /** + * Send args.. to the web page via channel in asynchronous message, the web page + * can handle it by listening to the channel event of ipc module. + * Note: + * 1. The IPC message handler in web pages do not have a event parameter, + * which is different from the handlers on the main process. + * 2. There is no way to send synchronous messages from the main process + * to a renderer process, because it would be very easy to cause dead locks. + */ + send(channel: string, ...args: any[]): void; + /** + * Enable device emulation with the given parameters. + */ + enableDeviceEmulation(parameters: DeviceEmulationParameters): void; + /** + * Disable device emulation. + */ + disableDeviceEmulation(): void; + /** + * Sends an input event to the page. + */ + sendInputEvent(event: SendInputEvent): void; + /** + * Begin subscribing for presentation events and captured frames, + * The callback will be called when there is a presentation event. + */ + beginFrameSubscription(onlyDirty: boolean, callback: BeginFrameSubscriptionCallback): void; + /** + * Begin subscribing for presentation events and captured frames, + * The callback will be called when there is a presentation event. + */ + beginFrameSubscription(callback: BeginFrameSubscriptionCallback): void; + /** + * End subscribing for frame presentation events. + */ + endFrameSubscription(): void; + /** + * @returns If the process of saving page has been initiated successfully. + */ + savePage(fullPath: string, saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML', callback?: (eror: Error) => void): boolean; + /** + * Shows pop-up dictionary that searches the selected word on the page. + * Note: This API is available only on macOS. + */ + showDefinitionForSelection(): void; + /** + * @returns Whether offscreen rendering is enabled. + */ + isOffscreen(): boolean; + /** + * If offscreen rendering is enabled and not painting, start painting. + */ + startPainting(): void; + /** + * If offscreen rendering is enabled and painting, stop painting. + */ + stopPainting(): void; + /** + * If offscreen rendering is enabled returns whether it is currently painting. + */ + isPainting(): boolean; + /** + * If offscreen rendering is enabled sets the frame rate to the specified number. + * Only values between 1 and 60 are accepted. + */ + setFrameRate(fps: number): void; + /** + * If offscreen rendering is enabled returns the current frame rate. + */ + getFrameRate(): number; + /** + * If offscreen rendering is enabled invalidates the frame and generates a new one through the 'paint' event. + */ + invalidate(): void; + /** + * Sets the item as dragging item for current drag-drop operation. + */ + startDrag(item: DragItem): void; + /** + * Captures a snapshot of the page within rect. + */ + capturePage(callback: (image: NativeImage) => void): void; + /** + * Captures a snapshot of the page within rect. + */ + capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + /** + * @returns The unique ID of this WebContents. + */ + id: number; + /** + * @returns The session object used by this webContents. + */ + session: Session; + /** + * @returns The WebContents that might own this WebContents. + */ + hostWebContents: WebContents; + /** + * @returns The WebContents of DevTools for this WebContents. + * Note: Users should never store this object because it may become null + * when the DevTools has been closed. + */ + devToolsWebContents: WebContents; + /** + * @returns Debugger API + */ + debugger: Debugger; + } + + interface BeginFrameSubscriptionCallback { + ( + /** + * The frameBuffer is a Buffer that contains raw pixel data. + * On most machines, the pixel data is effectively stored in 32bit BGRA format, + * but the actual representation depends on the endianness of the processor + * (most modern processors are little-endian, on machines with big-endian + * processors the data is in 32bit ARGB format). + */ + frameBuffer: Buffer, + /** + * The dirtyRect is an object with x, y, width, height properties that describes which part of the page was repainted. + * If onlyDirty is set to true, frameBuffer will only contain the repainted area. onlyDirty defaults to false. + */ + dirtyRect?: Rectangle + ): void + } + + interface ContextMenuParams { + /** + * x coordinate + */ + x: number; + /** + * y coordinate + */ + y: number; + /** + * URL of the link that encloses the node the context menu was invoked on. + */ + linkURL: string; + /** + * Text associated with the link. May be an empty string if the contents of the link are an image. + */ + linkText: string; + /** + * URL of the top level page that the context menu was invoked on. + */ + pageURL: string; + /** + * URL of the subframe that the context menu was invoked on. + */ + frameURL: string; + /** + * Source URL for the element that the context menu was invoked on. + * Elements with source URLs are images, audio and video. + */ + srcURL: string; + /** + * Type of the node the context menu was invoked on. + */ + mediaType: 'none' | 'image' | 'audio' | 'video' | 'canvas' | 'file' | 'plugin'; + /** + * Parameters for the media element the context menu was invoked on. + */ + mediaFlags: { + /** + * Whether the media element has crashed. + */ + inError: boolean; + /** + * Whether the media element is paused. + */ + isPaused: boolean; + /** + * Whether the media element is muted. + */ + isMuted: boolean; + /** + * Whether the media element has audio. + */ + hasAudio: boolean; + /** + * Whether the media element is looping. + */ + isLooping: boolean; + /** + * Whether the media element's controls are visible. + */ + isControlsVisible: boolean; + /** + * Whether the media element's controls are toggleable. + */ + canToggleControls: boolean; + /** + * Whether the media element can be rotated. + */ + canRotate: boolean; + } + /** + * Whether the context menu was invoked on an image which has non-empty contents. + */ + hasImageContents: boolean; + /** + * Whether the context is editable. + */ + isEditable: boolean; + /** + * These flags indicate whether the renderer believes it is able to perform the corresponding action. + */ + editFlags: { + /** + * Whether the renderer believes it can undo. + */ + canUndo: boolean; + /** + * Whether the renderer believes it can redo. + */ + canRedo: boolean; + /** + * Whether the renderer believes it can cut. + */ + canCut: boolean; + /** + * Whether the renderer believes it can copy + */ + canCopy: boolean; + /** + * Whether the renderer believes it can paste. + */ + canPaste: boolean; + /** + * Whether the renderer believes it can delete. + */ + canDelete: boolean; + /** + * Whether the renderer believes it can select all. + */ + canSelectAll: boolean; + } + /** + * Text of the selection that the context menu was invoked on. + */ + selectionText: string; + /** + * Title or alt text of the selection that the context was invoked on. + */ + titleText: string; + /** + * The misspelled word under the cursor, if any. + */ + misspelledWord: string; + /** + * The character encoding of the frame on which the menu was invoked. + */ + frameCharset: string; + /** + * If the context menu was invoked on an input field, the type of that field. + */ + inputFieldType: 'none' | 'plainText' | 'password' | 'other'; + /** + * Input source that invoked the context menu. + */ + menuSourceType: 'none' | 'mouse' | 'keyboard' | 'touch' | 'touchMenu'; + } + + interface BluetoothDevice { + deviceName: string; + deviceId: string; + } + + interface Headers { + [key: string]: string; + } + + type NewWindowDisposition = 'default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'; + + /** + * Specifies the action to take place when ending webContents.findInPage request. + * 'clearSelection' - Clear the selection. + * 'keepSelection' - Translate the selection into a normal selection. + * 'activateSelection' - Focus and click the selection node. + */ + type StopFindInPageAtion = 'clearSelection' | 'keepSelection' | 'activateSelection'; + + type CursorType = 'default' | 'crosshair' | 'pointer' | 'text' | 'wait' | 'help' | 'e-resize' | 'n-resize' | 'ne-resize' | 'nw-resize' | 's-resize' | 'se-resize' | 'sw-resize' | 'w-resize' | 'ns-resize' | 'ew-resize' | 'nesw-resize' | 'nwse-resize' | 'col-resize' | 'row-resize' | 'm-panning' | 'e-panning' | 'n-panning' | 'ne-panning' | 'nw-panning' | 's-panning' | 'se-panning' | 'sw-panning' | 'w-panning' | 'move' | 'vertical-text' | 'cell' | 'context-menu' | 'alias' | 'progress' | 'nodrop' | 'copy' | 'none' | 'not-allowed' | 'zoom-in' | 'zoom-out' | 'grab' | 'grabbing' | 'custom'; + + interface LoadURLOptions { + /** + * HTTP Referrer URL. + */ + httpReferrer?: string; + /** + * User agent originating the request. + */ + userAgent?: string; + /** + * Extra headers separated by "\n" + */ + extraHeaders?: string; + } + + interface PrintOptions { + /** + * Don't ask user for print settings. + * Defaults: false. + */ + silent?: boolean; + /** + * Also prints the background color and image of the web page. + * Defaults: false. + */ + printBackground?: boolean; + } + + interface PrintToPDFOptions { + /** + * Specify the type of margins to use. + * 0 - default + * 1 - none + * 2 - minimum + * Default: 0 + */ + marginsType?: number; + /** + * Specify page size of the generated PDF. + * Default: A4. + */ + pageSize?: 'A3' | 'A4' | 'A5' | 'Legal' | 'Letter' | 'Tabloid' | Size; + /** + * Whether to print CSS backgrounds. + * Default: false. + */ + printBackground?: boolean; + /** + * Whether to print selection only. + * Default: false. + */ + printSelectionOnly?: boolean; + /** + * true for landscape, false for portrait. + * Default: false. + */ + landscape?: boolean; + } + + interface Certificate { + /** + * PEM encoded data. + */ + data: string; + /** + * Issuer's Common Name. + */ + issuerName: string; + /** + * Subject's Common Name. + */ + subjectName: string; + /** + * Hex value represented string. + */ + serialNumber: string; + /** + * Start date of the certificate being valid in seconds. + */ + validStart: number; + /** + * End date of the certificate being valid in seconds. + */ + validExpiry: number; + /** + * Fingerprint of the certificate. + */ + fingerprint: string; + } + + interface LoginRequest { + method: string; + url: string; + referrer: string; + } + + interface LoginAuthInfo { + isProxy: boolean; + scheme: string; + host: string; + port: number; + realm: string; + } + + interface FindInPageOptions { + /** + * Whether to search forward or backward, defaults to true + */ + forward?: boolean; + /** + * Whether the operation is first request or a follow up, defaults to false. + */ + findNext?: boolean; + /** + * Whether search should be case-sensitive, defaults to false. + */ + matchCase?: boolean; + /** + * Whether to look only at the start of words. defaults to false. + */ + wordStart?: boolean; + /** + * When combined with wordStart, accepts a match in the middle of a word + * if the match begins with an uppercase letter followed by a lowercase + * or non-letter. Accepts several other intra-word matches, defaults to false. + */ + medialCapitalAsWordStart?: boolean; + } + + interface FoundInPageResult { + requestId: number; + /** + * Indicates if more responses are to follow. + */ + finalUpdate: boolean; + /** + * Position of the active match. + */ + activeMatchOrdinal?: number; + /** + * Number of Matches. + */ + matches?: number; + /** + * Coordinates of first match region. + */ + selectionArea?: Rectangle; + } + + interface DeviceEmulationParameters { + /** + * Specify the screen type to emulated + * Default: desktop + */ + screenPosition?: 'desktop' | 'mobile'; + /** + * Set the emulated screen size (screenPosition == mobile) + */ + screenSize?: Size; + /** + * Position the view on the screen (screenPosition == mobile) + * Default: {x: 0, y: 0} + */ + viewPosition?: Point; + /** + * Set the device scale factor (if zero defaults to original device scale factor) + * Default: 0 + */ + deviceScaleFactor: number; + /** + * Set the emulated view size (empty means no override). + */ + viewSize?: Size; + /** + * Whether emulated view should be scaled down if necessary to fit into available space + * Default: false + */ + fitToView?: boolean; + /** + * Offset of the emulated view inside available space (not in fit to view mode) + * Default: {x: 0, y: 0} + */ + offset?: Point; + /** + * Scale of emulated view inside available space (not in fit to view mode) + * Default: 1 + */ + scale: number; + } + + interface SendInputEvent { + type: 'mouseDown' | 'mouseUp' | 'mouseEnter' | 'mouseLeave' | 'contextMenu' | 'mouseWheel' | 'mouseMove' | 'keyDown' | 'keyUp' | 'char'; + modifiers: ('shift' | 'control' | 'alt' | 'meta' | 'isKeypad' | 'isAutoRepeat' | 'leftButtonDown' | 'middleButtonDown' | 'rightButtonDown' | 'capsLock' | 'numLock' | 'left' | 'right')[]; + } + + interface SendInputKeyboardEvent extends SendInputEvent { + keyCode: string; + } + + interface SendInputMouseEvent extends SendInputEvent { + x: number; + y: number; + button?: 'left' | 'middle' | 'right'; + globalX?: number; + globalY?: number; + movementX?: number; + movementY?: number; + clickCount?: number; + } + + interface SendInputMouseWheelEvent extends SendInputEvent { + deltaX?: number; + deltaY?: number; + wheelTicksX?: number; + wheelTicksY?: number; + accelerationRatioX?: number; + accelerationRatioY?: number; + hasPreciseScrollingDeltas?: boolean; + canScroll?: boolean; + } + + /** + * Debugger API serves as an alternate transport for remote debugging protocol. + */ + interface Debugger extends NodeJS.EventEmitter { + /** + * Attaches the debugger to the webContents. + * @param protocolVersion Requested debugging protocol version. + */ + attach(protocolVersion?: string): void; + /** + * @returns Whether a debugger is attached to the webContents. + */ + isAttached(): boolean; + /** + * Detaches the debugger from the webContents. + */ + detach(): void; + /** + * Send given command to the debugging target. + * @param method Method name, should be one of the methods defined by the remote debugging protocol. + * @param commandParams JSON object with request parameters. + * @param callback Response defined by the ‘returns’ attribute of the command description in the remote debugging protocol. + */ + sendCommand(method: string, commandParams?: any, callback?: (error: Error, result: any) => void): void; + /** + * Emitted when debugging session is terminated. This happens either when + * webContents is closed or devtools is invoked for the attached webContents. + */ + on(event: 'detach', listener: (event: Event, reason: string) => void): this; + /** + * Emitted whenever debugging target issues instrumentation event. + * Event parameters defined by the ‘parameters’ attribute in the remote debugging protocol. + */ + on(event: 'message', listener: (event: Event, method: string, params: any) => void): this; + on(event: string, listener: Function): this; + } + + // https://github.com/electron/electron/blob/master/docs/api/web-frame.md + + /** + * The web-frame module allows you to customize the rendering of the current web page. + */ + interface WebFrame { + /** + * Changes the zoom factor to the specified factor, zoom factor is + * zoom percent / 100, so 300% = 3.0. + */ + setZoomFactor(factor: number): void; + /** + * @returns The current zoom factor. + */ + getZoomFactor(): number; + /** + * Changes the zoom level to the specified level, 0 is "original size", and each + * increment above or below represents zooming 20% larger or smaller to default + * limits of 300% and 50% of original size, respectively. + */ + setZoomLevel(level: number): void; + /** + * @returns The current zoom level. + */ + getZoomLevel(): number; + /** + * Sets the maximum and minimum zoom level. + */ + setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; + /** + * Sets a provider for spell checking in input fields and text areas. + */ + setSpellCheckProvider(language: string, autoCorrectWord: boolean, provider: { + /** + * @returns Whether the word passed is correctly spelled. + */ + spellCheck: (text: string) => boolean; + }): void; + /** + * Sets the scheme as secure scheme. Secure schemes do not trigger mixed content + * warnings. For example, https and data are secure schemes because they cannot be + * corrupted by active network attackers. + */ + registerURLSchemeAsSecure(scheme: string): void; + /** + * Resources will be loaded from this scheme regardless of the current page’s Content Security Policy. + */ + registerURLSchemeAsBypassingCSP(scheme: string): void; + /** + * Registers the scheme as secure, bypasses content security policy for resources, + * allows registering ServiceWorker and supports fetch API. + */ + registerURLSchemeAsPrivileged(scheme: string, options?: RegisterURLSchemeOptions): void; + /** + * Inserts text to the focused element. + */ + insertText(text: string): void; + /** + * Evaluates `code` in page. + * In the browser window some HTML APIs like `requestFullScreen` can only be + * invoked by a gesture from the user. Setting `userGesture` to `true` will remove + * this limitation. + * + * @returns Promise + */ + executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; + /** + * @returns Object describing usage information of Blink’s internal memory caches. + */ + getResourceUsage(): ResourceUsages; + /** + * Attempts to free memory that is no longer being used (like images from a previous navigation). + */ + clearCache(): void; + } + + interface ResourceUsages { + fonts: ResourceUsage; + images: ResourceUsage; + cssStyleSheets: ResourceUsage; + xslStyleSheets: ResourceUsage; + scripts: ResourceUsage; + other: ResourceUsage; + } + + interface ResourceUsage { + count: number; + decodedSize: number; + liveSize: number; + purgeableSize: number; + purgedSize: number; + size: number; + } + + interface RegisterURLSchemeOptions { + secure?: boolean; + bypassCSP?: boolean; + allowServiceWorkers?: boolean; + supportFetchAPI?: boolean; + corsEnabled?: boolean; + } + + // https://github.com/electron/electron/blob/master/docs/api/web-view-tag.md + + /** + * Use the webview tag to embed 'guest' content (such as web pages) in your Electron app. + * The guest content is contained within the webview container. + * An embedded page within your app controls how the guest content is laid out and rendered. + * + * Unlike an iframe, the webview runs in a separate process than your app. + * It doesn't have the same permissions as your web page and all interactions between your app + * and embedded content will be asynchronous. This keeps your app safe from the embedded content. + */ + interface WebViewElement extends HTMLElement { + /** + * Returns the visible URL. Writing to this attribute initiates top-level navigation. + * Assigning src its own value will reload the current page. + * The src attribute can also accept data URLs, such as data:text/plain,Hello, world!. + */ + src: string; + /** + * If "on", the webview container will automatically resize within the bounds specified + * by the attributes minwidth, minheight, maxwidth, and maxheight. + * These constraints do not impact the webview unless autosize is enabled. + * When autosize is enabled, the webview container size cannot be less than + * the minimum values or greater than the maximum. + */ + autosize: string; + /** + * If "on", the guest page in webview will have node integration and can use node APIs + * like require and process to access low level system resources. + */ + nodeintegration: string; + /** + * If "on", the guest page in webview will be able to use browser plugins. + */ + plugins: string; + /** + * Specifies a script that will be loaded before other scripts run in the guest page. + * The protocol of script's URL must be either file: or asar:, + * because it will be loaded by require in guest page under the hood. + * + * When the guest page doesn't have node integration this script will still have access to all Node APIs, + * but global objects injected by Node will be deleted after this script has finished executing. + */ + preload: string; + /** + * Sets the referrer URL for the guest page. + */ + httpreferrer: string; + /** + * Sets the user agent for the guest page before the page is navigated to. + * Once the page is loaded, use the setUserAgent method to change the user agent. + */ + useragent: string; + /** + * If "on", the guest page will have web security disabled. + */ + disablewebsecurity: string; + /** + * Sets the session used by the page. If partition starts with persist:, + * the page will use a persistent session available to all pages in the app with the same partition. + * If there is no persist: prefix, the page will use an in-memory session. + * By assigning the same partition, multiple pages can share the same session. + * If the partition is unset then default session of the app will be used. + * + * This value can only be modified before the first navigation, + * since the session of an active renderer process cannot change. + * Subsequent attempts to modify the value will fail with a DOM exception. + */ + partition: string; + /** + * If "on", the guest page will be allowed to open new windows. + */ + allowpopups: string; + /** + * A list of strings which specifies the web preferences to be set on the webview, separated by ,. + */ + webpreferences: string; + /** + * A list of strings which specifies the blink features to be enabled separated by ,. + */ + blinkfeatures: string; + /** + * A list of strings which specifies the blink features to be disabled separated by ,. + */ + disableblinkfeatures: string; + /** + * A value that links the webview to a specific webContents. + * When a webview first loads a new webContents is created and this attribute is set + * to its instance identifier. Setting this attribute on a new or existing webview connects + * it to the existing webContents that currently renders in a different webview. + * + * The existing webview will see the destroy event and will then create a new webContents when a new url is loaded. + */ + guestinstance: string; + /** + * Loads the url in the webview, the url must contain the protocol prefix, e.g. the http:// or file://. + */ + loadURL(url: string, options?: LoadURLOptions): void; + /** + * @returns URL of guest page. + */ + getURL(): string; + /** + * @returns The title of guest page. + */ + getTitle(): string; + /** + * @returns Whether the web page is destroyed. + */ + isDestroyed(): boolean; + /** + * @returns Whether the web page is focused. + */ + isFocused(): boolean; + /** + * @returns Whether guest page is still loading resources. + */ + isLoading(): boolean; + /** + * Returns a boolean whether the guest page is waiting for a first-response for the main resource of the page. + */ + isWaitingForResponse(): boolean; + /** + * Stops any pending navigation. + */ + stop(): void; + /** + * Reloads the guest page. + */ + reload(): void; + /** + * Reloads the guest page and ignores cache. + */ + reloadIgnoringCache(): void; + /** + * @returns Whether the guest page can go back. + */ + canGoBack(): boolean; + /** + * @returns Whether the guest page can go forward. + */ + canGoForward(): boolean; + /** + * @returns Whether the guest page can go to offset. + */ + canGoToOffset(offset: number): boolean; + /** + * Clears the navigation history. + */ + clearHistory(): void; + /** + * Makes the guest page go back. + */ + goBack(): void; + /** + * Makes the guest page go forward. + */ + goForward(): void; + /** + * Navigates to the specified absolute index. + */ + goToIndex(index: number): void; + /** + * Navigates to the specified offset from the "current entry". + */ + goToOffset(offset: number): void; + /** + * @returns Whether the renderer process has crashed. + */ + isCrashed(): boolean; + /** + * Overrides the user agent for the guest page. + */ + setUserAgent(userAgent: string): void; + /** + * @returns The user agent for guest page. + */ + getUserAgent(): string; + /** + * Injects CSS into the guest page. + */ + insertCSS(css: string): void; + /** + * Evaluates code in page. If userGesture is set, it will create the user gesture context in the page. + * HTML APIs like requestFullScreen, which require user action, can take advantage of this option for automation. + * + * @returns Promise + */ + executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; + /** + * Opens a DevTools window for guest page. + */ + openDevTools(): void; + /** + * Closes the DevTools window of guest page. + */ + closeDevTools(): void; + /** + * @returns Whether guest page has a DevTools window attached. + + isDevToolsOpened(): boolean; + /** + * @returns Whether DevTools window of guest page is focused. + */ + isDevToolsFocused(): boolean; + /** + * Starts inspecting element at position (x, y) of guest page. + */ + inspectElement(x: number, y: number): void; + /** + * Opens the DevTools for the service worker context present in the guest page. + */ + inspectServiceWorker(): void; + /** + * Set guest page muted. + */ + setAudioMuted(muted: boolean): void; + /** + * @returns Whether guest page has been muted. + */ + isAudioMuted(): boolean; + /** + * Executes editing command undo in page. + */ + undo(): void; + /** + * Executes editing command redo in page. + */ + redo(): void; + /** + * Executes editing command cut in page. + */ + cut(): void; + /** + * Executes editing command copy in page. + */ + copy(): void; + /** + * Executes editing command paste in page. + */ + paste(): void; + /** + * Executes editing command pasteAndMatchStyle in page. + */ + pasteAndMatchStyle(): void; + /** + * Executes editing command delete in page. + */ + delete(): void; + /** + * Executes editing command selectAll in page. + */ + selectAll(): void; + /** + * Executes editing command unselect in page. + */ + unselect(): void; + /** + * Executes editing command replace in page. + */ + replace(text: string): void; + /** + * Executes editing command replaceMisspelling in page. + */ + replaceMisspelling(text: string): void; + /** + * Inserts text to the focused element. + */ + insertText(text: string): void; + /** + * Starts a request to find all matches for the text in the web page. + * The result of the request can be obtained by subscribing to found-in-page event. + * @returns The request id used for the request. + */ + findInPage(text: string, options?: FindInPageOptions): number; + /** + * Stops any findInPage request for the webview with the provided action. + */ + stopFindInPage(action: StopFindInPageAtion): void; + /** + * Prints webview's web page. Same with webContents.print([options]). + */ + print(options?: PrintOptions): void; + /** + * Prints webview's web page as PDF, Same with webContents.printToPDF(options, callback) + */ + printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; + /** + * Send an asynchronous message to renderer process via channel, you can also send arbitrary arguments. + * The renderer process can handle the message by listening to the channel event with the ipcRenderer module. + * See webContents.send for examples. + */ + send(channel: string, ...args: any[]): void; + /** + * Sends an input event to the page. + * See webContents.sendInputEvent for detailed description of event object. + */ + sendInputEvent(event: SendInputEvent): void + /** + * Changes the zoom factor to the specified factor. + * Zoom factor is zoom percent divided by 100, so 300% = 3.0. + */ + setZoomFactor(factor: number): void; + /** + * Changes the zoom level to the specified level. + * The original size is 0 and each increment above or below represents + * zooming 20% larger or smaller to default limits of 300% and 50% of original size, respectively. + */ + setZoomLevel(level: number): void; + /** + * Shows pop-up dictionary that searches the selected word on the page. + * Note: This API is available only on macOS. + */ + showDefinitionForSelection(): void; + /** + * @returns The WebContents associated with this webview. + */ + getWebContents(): WebContents; + /** + * Captures a snapshot of the webview's page. Same as webContents.capturePage([rect, ]callback). + */ + capturePage(callback: (image: NativeImage) => void): void; + /** + * Captures a snapshot of the webview's page. Same as webContents.capturePage([rect, ]callback). + */ + capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + /** + * Fired when a load has committed. This includes navigation within the current document + * as well as subframe document-level loads, but does not include asynchronous resource loads. + */ + addEventListener(type: 'load-commit', listener: (event: WebViewElement.LoadCommitEvent) => void, useCapture?: boolean): void; + /** + * Fired when the navigation is done, i.e. the spinner of the tab will stop spinning, and the onload event is dispatched. + */ + addEventListener(type: 'did-finish-load', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * This event is like did-finish-load, but fired when the load failed or was cancelled, e.g. window.stop() is invoked. + */ + addEventListener(type: 'did-fail-load', listener: (event: WebViewElement.DidFailLoadEvent) => void, useCapture?: boolean): void; + /** + * Fired when a frame has done navigation. + */ + addEventListener(type: 'did-frame-finish-load', listener: (event: WebViewElement.DidFrameFinishLoadEvent) => void, useCapture?: boolean): void; + /** + * Corresponds to the points in time when the spinner of the tab starts spinning. + */ + addEventListener(type: 'did-start-loading', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Corresponds to the points in time when the spinner of the tab stops spinning. + */ + addEventListener(type: 'did-stop-loading', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when details regarding a requested resource is available. + * status indicates socket connection to download the resource. + */ + addEventListener(type: 'did-get-response-details', listener: (event: WebViewElement.DidGetResponseDetails) => void, useCapture?: boolean): void; + /** + * Fired when a redirect was received while requesting a resource. + */ + addEventListener(type: 'did-get-redirect-request', listener: (event: WebViewElement.DidGetRedirectRequestEvent) => void, useCapture?: boolean): void; + /** + * Fired when document in the given frame is loaded. + */ + addEventListener(type: 'dom-ready', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when page title is set during navigation. explicitSet is false when title is synthesized from file URL. + */ + addEventListener(type: 'page-title-updated', listener: (event: WebViewElement.PageTitleUpdatedEvent) => void, useCapture?: boolean): void; + /** + * Fired when page receives favicon URLs. + */ + addEventListener(type: 'page-favicon-updated', listener: (event: WebViewElement.PageFaviconUpdatedEvent) => void, useCapture?: boolean): void; + /** + * Fired when page enters fullscreen triggered by HTML API. + */ + addEventListener(type: 'enter-html-full-screen', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when page leaves fullscreen triggered by HTML API. + */ + addEventListener(type: 'leave-html-full-screen', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when the guest window logs a console message. + */ + addEventListener(type: 'console-message', listener: (event: WebViewElement.ConsoleMessageEvent) => void, useCapture?: boolean): void; + /** + * Fired when a result is available for webview.findInPage request. + */ + addEventListener(type: 'found-in-page', listener: (event: WebViewElement.FoundInPageEvent) => void, useCapture?: boolean): void; + /** + * Fired when the guest page attempts to open a new browser window. + */ + addEventListener(type: 'new-window', listener: (event: WebViewElement.NewWindowEvent) => void, useCapture?: boolean): void; + /** + * Emitted when a user or the page wants to start navigation. + * It can happen when the window.location object is changed or a user clicks a link in the page. + * + * This event will not emit when the navigation is started programmatically with APIs + * like .loadURL and .back. + * + * It is also not emitted during in-page navigation, such as clicking anchor links + * or updating the window.location.hash. Use did-navigate-in-page event for this purpose. + * + * Calling event.preventDefault() does NOT have any effect. + */ + addEventListener(type: 'will-navigate', listener: (event: WebViewElement.WillNavigateEvent) => void, useCapture?: boolean): void; + /** + * Emitted when a navigation is done. + * + * This event is not emitted for in-page navigations, such as clicking anchor links + * or updating the window.location.hash. Use did-navigate-in-page event for this purpose. + */ + addEventListener(type: 'did-navigate', listener: (event: WebViewElement.DidNavigateEvent) => void, useCapture?: boolean): void; + /** + * Emitted when an in-page navigation happened. + * + * When in-page navigation happens, the page URL changes but does not cause + * navigation outside of the page. Examples of this occurring are when anchor links + * are clicked or when the DOM hashchange event is triggered. + */ + addEventListener(type: 'did-navigate-in-page', listener: (event: WebViewElement.DidNavigateInPageEvent) => void, useCapture?: boolean): void; + /** + * Fired when the guest page attempts to close itself. + */ + addEventListener(type: 'close', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when the guest page has sent an asynchronous message to embedder page. + */ + addEventListener(type: 'ipc-message', listener: (event: WebViewElement.IpcMessageEvent) => void, useCapture?: boolean): void; + /** + * Fired when the renderer process is crashed. + */ + addEventListener(type: 'crashed', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when the gpu process is crashed. + */ + addEventListener(type: 'gpu-crashed', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when a plugin process is crashed. + */ + addEventListener(type: 'plugin-crashed', listener: (event: WebViewElement.PluginCrashedEvent) => void, useCapture?: boolean): void; + /** + * Fired when the WebContents is destroyed. + */ + addEventListener(type: 'destroyed', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Emitted when media starts playing. + */ + addEventListener(type: 'media-started-playing', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Emitted when media is paused or done playing. + */ + addEventListener(type: 'media-paused', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Emitted when a page's theme color changes. This is usually due to encountering a meta tag: + * + */ + addEventListener(type: 'did-change-theme-color', listener: (event: WebViewElement.DidChangeThemeColorEvent) => void, useCapture?: boolean): void; + /** + * Emitted when mouse moves over a link or the keyboard moves the focus to a link. + */ + addEventListener(type: 'update-target-url', listener: (event: WebViewElement.UpdateTargetUrlEvent) => void, useCapture?: boolean): void; + /** + * Emitted when DevTools is opened. + */ + addEventListener(type: 'devtools-opened', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Emitted when DevTools is closed. + */ + addEventListener(type: 'devtools-closed', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Emitted when DevTools is focused / opened. + */ + addEventListener(type: 'devtools-focused', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + addEventListener(type: string, listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + } + + namespace WebViewElement { + type Event = ElectronPrivate.GlobalEvent; + + interface LoadCommitEvent extends Event { + url: string; + isMainFrame: boolean; + } + + interface DidFailLoadEvent extends Event { + errorCode: number; + errorDescription: string; + validatedURL: string; + isMainFrame: boolean; + } + + interface DidFrameFinishLoadEvent extends Event { + isMainFrame: boolean; + } + + interface DidGetResponseDetails extends Event { + status: boolean; + newURL: string; + originalURL: string; + httpResponseCode: number; + requestMethod: string; + referrer: string; + headers: Headers; + resourceType: string; + } + + interface DidGetRedirectRequestEvent extends Event { + oldURL: string; + newURL: string; + isMainFrame: boolean; + httpResponseCode: number; + requestMethod: string; + referrer: string; + headers: Headers; + } + + interface PageTitleUpdatedEvent extends Event { + title: string; + explicitSet: string; + } + + interface PageFaviconUpdatedEvent extends Event { + favicons: string[]; + } + + interface ConsoleMessageEvent extends Event { + level: number; + message: string; + line: number; + sourceId: string; + } + + interface FoundInPageEvent extends Event { + result: FoundInPageResult; + } + + interface NewWindowEvent extends Event { + url: string; + frameName: string; + disposition: NewWindowDisposition; + options: BrowserWindowOptions; + } + + interface WillNavigateEvent extends Event { + url: string; + } + + interface DidNavigateEvent extends Event { + url: string; + } + + interface DidNavigateInPageEvent extends Event { + url: string; + isMainFrame: boolean; + } + + interface IpcMessageEvent extends Event { + channel: string; + args: any[]; + } + + interface PluginCrashedEvent extends Event { + name: string; + version: string; + } + + interface DidChangeThemeColorEvent extends Event { + themeColor: string; + } + + interface UpdateTargetUrlEvent extends Event { + url: string; + } + } + + /** + * The BrowserWindowProxy object is returned from window.open and provides limited functionality with the child window. + */ + interface BrowserWindowProxy { + /** + * Removes focus from the child window. + */ + blur(): void; + /** + * Forcefully closes the child window without calling its unload event. + */ + close(): void; + /** + * Set to true after the child window gets closed. + */ + closed: boolean; + /** + * Evaluates the code in the child window. + */ + eval(code: string): void; + /** + * Focuses the child window (brings the window to front). + */ + focus(): void; + /** + * Sends a message to the child window with the specified origin or * for no origin preference. + * In addition to these methods, the child window implements window.opener object with no + * properties and a single method. + */ + postMessage(message: string, targetOrigin: string): void; + /** + * Invokes the print dialog on the child window. + */ + print(): void; + } + + // https://github.com/electron/electron/blob/master/docs/api/synopsis.md + + interface CommonElectron { + clipboard: Electron.Clipboard; + crashReporter: Electron.CrashReporter; + nativeImage: typeof Electron.NativeImage; + shell: Electron.Shell; + + app: Electron.App; + autoUpdater: Electron.AutoUpdater; + BrowserWindow: typeof Electron.BrowserWindow; + contentTracing: Electron.ContentTracing; + dialog: Electron.Dialog; + ipcMain: Electron.IpcMain; + globalShortcut: Electron.GlobalShortcut; + Menu: typeof Electron.Menu; + MenuItem: typeof Electron.MenuItem; + net: Electron.Net; + powerMonitor: Electron.PowerMonitor; + powerSaveBlocker: Electron.PowerSaveBlocker; + protocol: Electron.Protocol; + screen: Electron.Screen; + session: typeof Electron.Session; + systemPreferences: Electron.SystemPreferences; + Tray: typeof Electron.Tray; + webContents: Electron.WebContentsStatic; + } + + interface ElectronMainAndRenderer extends CommonElectron { + desktopCapturer: Electron.DesktopCapturer; + ipcRenderer: Electron.IpcRenderer; + remote: Electron.Remote; + webFrame: Electron.WebFrame; + } +} + +declare namespace ElectronPrivate { + type GlobalEvent = Event; +} + +interface Document { + createElement(tagName: 'webview'): Electron.WebViewElement; +} + +// https://github.com/electron/electron/blob/master/docs/api/window-open.md + +interface Window { + /** + * Creates a new window. + */ + open(url: string, frameName?: string, features?: string): Electron.BrowserWindowProxy; +} + +// https://github.com/electron/electron/blob/master/docs/api/file-object.md + +interface File { + /** + * Exposes the real path of the filesystem. + */ + path: string; +} + +// https://github.com/electron/electron/blob/master/docs/api/process.md + +declare namespace NodeJS { + + interface ProcessVersions { + /** + * Electron's version string. + */ + electron: string; + /** + * Chrome's version string. + */ + chrome: string; + } + + interface Process { + /** + * Setting this to true can disable the support for asar archives in Node's built-in modules. + */ + noAsar?: boolean; + /** + * Process's type + */ + type: 'browser' | 'renderer'; + /** + * Path to JavaScript source code. + */ + resourcesPath: string; + /** + * For Mac App Store build, this value is true, for other builds it is undefined. + */ + mas?: boolean; + /** + * If the app is running as a Windows Store app (appx), this value is true, for other builds it is undefined. + */ + windowsStore?: boolean; + /** + * When app is started by being passed as parameter to the default app, + * this value is true in the main process, otherwise it is undefined. + */ + defaultApp?: boolean; + /** + * Emitted when Electron has loaded its internal initialization script + * and is beginning to load the web page or the main script. + */ + on(event: 'loaded', listener: Function): this; + on(event: string, listener: Function): this; + /** + * Causes the main thread of the current process crash; + */ + crash(): void; + /** + * Causes the main thread of the current process hang. + */ + hang(): void; + /** + * Sets the file descriptor soft limit to maxDescriptors or the OS hard limit, + * whichever is lower for the current process. + * + * Note: This API is only available on macOS and Linux. + */ + setFdLimit(maxDescriptors: number): void; + /** + * @returns Object giving memory usage statistics about the current process. + * Note: All statistics are reported in Kilobytes. + */ + getProcessMemoryInfo(): ProcessMemoryInfo; + /** + * @returns Object giving memory usage statistics about the entire system. + * Note: All statistics are reported in Kilobytes. + */ + getSystemMemoryInfo(): SystemMemoryInfo; + } + + interface ProcessMemoryInfo { + /** + * The amount of memory currently pinned to actual physical RAM. + */ + workingSetSize: number; + /** + * The maximum amount of memory that has ever been pinned to actual physical RAM. + */ + peakWorkingSetSize: number; + /** + * The amount of memory not shared by other processes, such as JS heap or HTML content. + */ + privateBytes: number; + /** + * The amount of memory shared between processes, typically memory consumed by the Electron code itself. + */ + sharedBytes: number; + } + + interface SystemMemoryInfo { + /** + * The total amount of physical memory available to the system. + */ + total: number; + /** + * The total amount of memory not being used by applications or disk cache. + */ + free: number; + /** + * The total amount of swap memory available to the system. + */ + swapTotal: number; + /** + * The free amount of swap memory available to the system. + */ + swapFree: number; + } +} + +declare module 'electron' { + var electron: Electron.ElectronMainAndRenderer; + export = electron; +} + +// interface NodeRequireFunction { +// (moduleName: 'electron'): Electron.ElectronMainAndRenderer; +// } \ No newline at end of file diff --git a/src/typings/error-ex.d.ts b/src/typings/error-ex.d.ts new file mode 100644 index 0000000000..09b5f75ffa --- /dev/null +++ b/src/typings/error-ex.d.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'error-ex' { + export function errorEx(name) : void; +} diff --git a/src/typings/fast-plist.d.ts b/src/typings/fast-plist.d.ts new file mode 100644 index 0000000000..537e7c2e8e --- /dev/null +++ b/src/typings/fast-plist.d.ts @@ -0,0 +1,7 @@ + +declare module "fast-plist" { + /** + * A very fast plist parser + */ + export function parse(content: string): any; +} diff --git a/src/typings/gc-signals.d.ts b/src/typings/gc-signals.d.ts new file mode 100644 index 0000000000..be8992af28 --- /dev/null +++ b/src/typings/gc-signals.d.ts @@ -0,0 +1,19 @@ +declare module 'gc-signals' { + export interface GCSignal { + } + /** + * Create a new GC signal. When being garbage collected the passed + * value is stored for later consumption. + */ + export const GCSignal: { + new (id: number): GCSignal; + }; + /** + * Consume ids of garbage collected signals. + */ + export function consumeSignals(): number[]; + export function onDidGarbageCollectSignals(callback: (ids: number[]) => any): { + dispose(): void; + }; + export function trackGarbageCollection(obj: any, id: number): number; +} \ No newline at end of file diff --git a/src/typings/getmac.d.ts b/src/typings/getmac.d.ts new file mode 100644 index 0000000000..98f0458a20 --- /dev/null +++ b/src/typings/getmac.d.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module getmac { + export function getMac(callback: (error: Error, macAddress: string) => void): void; +} + +declare module 'getmac' { + export = getmac; +} \ No newline at end of file diff --git a/src/typings/globals/core-js/index.d.ts b/src/typings/globals/core-js/index.d.ts new file mode 100644 index 0000000000..80a6c45112 --- /dev/null +++ b/src/typings/globals/core-js/index.d.ts @@ -0,0 +1,3052 @@ +// Generated by typings +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/3c56e866d6edd234416a92c3d35c15ccd7fe6b72/core-js/index.d.ts +declare type PropertyKey = string | number | symbol; + +// ############################################################################################# +// ECMAScript 6: Object & Function +// Modules: es6.object.assign, es6.object.is, es6.object.set-prototype-of, +// es6.object.to-string, es6.function.name and es6.function.has-instance. +// ############################################################################################# + +interface ObjectConstructor { + /** + * Copy the values of all of the enumerable own properties from one or more source objects to a + * target object. Returns the target object. + * @param target The target object to copy to. + * @param source The source object from which to copy properties. + */ + assign(target: T, source: U): T & U; + + /** + * Copy the values of all of the enumerable own properties from one or more source objects to a + * target object. Returns the target object. + * @param target The target object to copy to. + * @param source1 The first source object from which to copy properties. + * @param source2 The second source object from which to copy properties. + */ + assign(target: T, source1: U, source2: V): T & U & V; + + /** + * Copy the values of all of the enumerable own properties from one or more source objects to a + * target object. Returns the target object. + * @param target The target object to copy to. + * @param source1 The first source object from which to copy properties. + * @param source2 The second source object from which to copy properties. + * @param source3 The third source object from which to copy properties. + */ + assign(target: T, source1: U, source2: V, source3: W): T & U & V & W; + + /** + * Copy the values of all of the enumerable own properties from one or more source objects to a + * target object. Returns the target object. + * @param target The target object to copy to. + * @param sources One or more source objects from which to copy properties + */ + assign(target: any, ...sources: any[]): any; + + /** + * Returns true if the values are the same value, false otherwise. + * @param value1 The first value. + * @param value2 The second value. + */ + is(value1: any, value2: any): boolean; + + /** + * Sets the prototype of a specified object o to object proto or null. Returns the object o. + * @param o The object to change its prototype. + * @param proto The value of the new prototype or null. + * @remarks Requires `__proto__` support. + */ + setPrototypeOf(o: any, proto: any): any; +} + +interface Function { + /** + * Returns the name of the function. Function names are read-only and can not be changed. + */ + name: string; + + /** + * Determines if a constructor object recognizes an object as one of the + * constructor’s instances. + * @param value The object to test. + */ + [Symbol.hasInstance](value: any): boolean; +} + +// ############################################################################################# +// ECMAScript 6: Array +// Modules: es6.array.from, es6.array.of, es6.array.copy-within, es6.array.fill, es6.array.find, +// and es6.array.find-index +// ############################################################################################# + +interface Array { + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: T, index: number, obj: Array) => boolean, thisArg?: any): T; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: T) => boolean, thisArg?: any): number; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(value: T, start?: number, end?: number): T[]; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(target: number, start: number, end?: number): T[]; + + [Symbol.unscopables]: any; +} + +interface ArrayConstructor { + /** + * Creates an array from an array-like object. + * @param arrayLike An array-like object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(arrayLike: ArrayLike, mapfn: (v: T, k: number) => U, thisArg?: any): Array; + + /** + * Creates an array from an iterable object. + * @param iterable An iterable object to convert to an array. + * @param mapfn A mapping function to call on every element of the array. + * @param thisArg Value of 'this' used to invoke the mapfn. + */ + from(iterable: Iterable, mapfn: (v: T, k: number) => U, thisArg?: any): Array; + + /** + * Creates an array from an array-like object. + * @param arrayLike An array-like object to convert to an array. + */ + from(arrayLike: ArrayLike): Array; + + /** + * Creates an array from an iterable object. + * @param iterable An iterable object to convert to an array. + */ + from(iterable: Iterable): Array; + + /** + * Returns a new array from a set of elements. + * @param items A set of elements to include in the new array object. + */ + of(...items: T[]): Array; +} + +// ############################################################################################# +// ECMAScript 6: String & RegExp +// Modules: es6.string.from-code-point, es6.string.raw, es6.string.code-point-at, +// es6.string.ends-with, es6.string.includes, es6.string.repeat, +// es6.string.starts-with, and es6.regexp +// ############################################################################################# + +interface String { + /** + * Returns a nonnegative integer Number less than 1114112 (0x110000) that is the code point + * value of the UTF-16 encoded code point starting at the string element at position pos in + * the String resulting from converting this object to a String. + * If there is no element at that position, the result is undefined. + * If a valid UTF-16 surrogate pair does not begin at pos, the result is the code unit at pos. + */ + codePointAt(pos: number): number; + + /** + * Returns true if searchString appears as a substring of the result of converting this + * object to a String, at one or more positions that are + * greater than or equal to position; otherwise, returns false. + * @param searchString search string + * @param position If position is undefined, 0 is assumed, so as to search all of the String. + */ + includes(searchString: string, position?: number): boolean; + + /** + * Returns true if the sequence of elements of searchString converted to a String is the + * same as the corresponding elements of this object (converted to a String) starting at + * endPosition – length(this). Otherwise returns false. + */ + endsWith(searchString: string, endPosition?: number): boolean; + + /** + * Returns a String value that is made from count copies appended together. If count is 0, + * T is the empty String is returned. + * @param count number of copies to append + */ + repeat(count: number): string; + + /** + * Returns true if the sequence of elements of searchString converted to a String is the + * same as the corresponding elements of this object (converted to a String) starting at + * position. Otherwise returns false. + */ + startsWith(searchString: string, position?: number): boolean; +} + +interface StringConstructor { + /** + * Return the String value whose elements are, in order, the elements in the List elements. + * If length is 0, the empty string is returned. + */ + fromCodePoint(...codePoints: number[]): string; + + /** + * String.raw is intended for use as a tag function of a Tagged Template String. When called + * as such the first argument will be a well formed template call site object and the rest + * parameter will contain the substitution values. + * @param template A well-formed template string call site representation. + * @param substitutions A set of substitution values. + */ + raw(template: TemplateStringsArray, ...substitutions: any[]): string; +} + +interface RegExp { + /** + * Returns a string indicating the flags of the regular expression in question. This field is read-only. + * The characters in this string are sequenced and concatenated in the following order: + * + * - "g" for global + * - "i" for ignoreCase + * - "m" for multiline + * - "u" for unicode + * - "y" for sticky + * + * If no flags are set, the value is the empty string. + */ + flags: string; +} + +// ############################################################################################# +// ECMAScript 6: Number & Math +// Modules: es6.number.constructor, es6.number.statics, and es6.math +// ############################################################################################# + +interface NumberConstructor { + /** + * The value of Number.EPSILON is the difference between 1 and the smallest value greater than 1 + * that is representable as a Number value, which is approximately: + * 2.2204460492503130808472633361816 x 10‍−‍16. + */ + EPSILON: number; + + /** + * Returns true if passed value is finite. + * Unlike the global isFininte, Number.isFinite doesn't forcibly convert the parameter to a + * number. Only finite values of the type number, result in true. + * @param number A numeric value. + */ + isFinite(number: number): boolean; + + /** + * Returns true if the value passed is an integer, false otherwise. + * @param number A numeric value. + */ + isInteger(number: number): boolean; + + /** + * Returns a Boolean value that indicates whether a value is the reserved value NaN (not a + * number). Unlike the global isNaN(), Number.isNaN() doesn't forcefully convert the parameter + * to a number. Only values of the type number, that are also NaN, result in true. + * @param number A numeric value. + */ + isNaN(number: number): boolean; + + /** + * Returns true if the value passed is a safe integer. + * @param number A numeric value. + */ + isSafeInteger(number: number): boolean; + + /** + * The value of the largest integer n such that n and n + 1 are both exactly representable as + * a Number value. + * The value of Number.MIN_SAFE_INTEGER is 9007199254740991 2^53 − 1. + */ + MAX_SAFE_INTEGER: number; + + /** + * The value of the smallest integer n such that n and n − 1 are both exactly representable as + * a Number value. + * The value of Number.MIN_SAFE_INTEGER is −9007199254740991 (−(2^53 − 1)). + */ + MIN_SAFE_INTEGER: number; + + /** + * Converts a string to a floating-point number. + * @param string A string that contains a floating-point number. + */ + parseFloat(string: string): number; + + /** + * Converts A string to an integer. + * @param s A string to convert into a number. + * @param radix A value between 2 and 36 that specifies the base of the number in numString. + * If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal. + * All other strings are considered decimal. + */ + parseInt(string: string, radix?: number): number; +} + +interface Math { + /** + * Returns the number of leading zero bits in the 32-bit binary representation of a number. + * @param x A numeric expression. + */ + clz32(x: number): number; + + /** + * Returns the result of 32-bit multiplication of two numbers. + * @param x First number + * @param y Second number + */ + imul(x: number, y: number): number; + + /** + * Returns the sign of the x, indicating whether x is positive, negative or zero. + * @param x The numeric expression to test + */ + sign(x: number): number; + + /** + * Returns the base 10 logarithm of a number. + * @param x A numeric expression. + */ + log10(x: number): number; + + /** + * Returns the base 2 logarithm of a number. + * @param x A numeric expression. + */ + log2(x: number): number; + + /** + * Returns the natural logarithm of 1 + x. + * @param x A numeric expression. + */ + log1p(x: number): number; + + /** + * Returns the result of (e^x - 1) of x (e raised to the power of x, where e is the base of + * the natural logarithms). + * @param x A numeric expression. + */ + expm1(x: number): number; + + /** + * Returns the hyperbolic cosine of a number. + * @param x A numeric expression that contains an angle measured in radians. + */ + cosh(x: number): number; + + /** + * Returns the hyperbolic sine of a number. + * @param x A numeric expression that contains an angle measured in radians. + */ + sinh(x: number): number; + + /** + * Returns the hyperbolic tangent of a number. + * @param x A numeric expression that contains an angle measured in radians. + */ + tanh(x: number): number; + + /** + * Returns the inverse hyperbolic cosine of a number. + * @param x A numeric expression that contains an angle measured in radians. + */ + acosh(x: number): number; + + /** + * Returns the inverse hyperbolic sine of a number. + * @param x A numeric expression that contains an angle measured in radians. + */ + asinh(x: number): number; + + /** + * Returns the inverse hyperbolic tangent of a number. + * @param x A numeric expression that contains an angle measured in radians. + */ + atanh(x: number): number; + + /** + * Returns the square root of the sum of squares of its arguments. + * @param values Values to compute the square root for. + * If no arguments are passed, the result is +0. + * If there is only one argument, the result is the absolute value. + * If any argument is +Infinity or -Infinity, the result is +Infinity. + * If any argument is NaN, the result is NaN. + * If all arguments are either +0 or −0, the result is +0. + */ + hypot(...values: number[]): number; + + /** + * Returns the integral part of the a numeric expression, x, removing any fractional digits. + * If x is already an integer, the result is x. + * @param x A numeric expression. + */ + trunc(x: number): number; + + /** + * Returns the nearest single precision float representation of a number. + * @param x A numeric expression. + */ + fround(x: number): number; + + /** + * Returns an implementation-dependent approximation to the cube root of number. + * @param x A numeric expression. + */ + cbrt(x: number): number; +} + +// ############################################################################################# +// ECMAScript 6: Symbols +// Modules: es6.symbol +// ############################################################################################# + +interface Symbol { + /** Returns a string representation of an object. */ + toString(): string; + + [Symbol.toStringTag]: string; +} + +interface SymbolConstructor { + /** + * A reference to the prototype. + */ + prototype: Symbol; + + /** + * Returns a new unique Symbol value. + * @param description Description of the new Symbol object. + */ + (description?: string|number): symbol; + + /** + * Returns a Symbol object from the global symbol registry matching the given key if found. + * Otherwise, returns a new symbol with this key. + * @param key key to search for. + */ + for(key: string): symbol; + + /** + * Returns a key from the global symbol registry matching the given Symbol if found. + * Otherwise, returns a undefined. + * @param sym Symbol to find the key for. + */ + keyFor(sym: symbol): string; + + // Well-known Symbols + + /** + * A method that determines if a constructor object recognizes an object as one of the + * constructor’s instances. Called by the semantics of the instanceof operator. + */ + hasInstance: symbol; + + /** + * A Boolean value that if true indicates that an object should flatten to its array elements + * by Array.prototype.concat. + */ + isConcatSpreadable: symbol; + + /** + * A method that returns the default iterator for an object. Called by the semantics of the + * for-of statement. + */ + iterator: symbol; + + /** + * A regular expression method that matches the regular expression against a string. Called + * by the String.prototype.match method. + */ + match: symbol; + + /** + * A regular expression method that replaces matched substrings of a string. Called by the + * String.prototype.replace method. + */ + replace: symbol; + + /** + * A regular expression method that returns the index within a string that matches the + * regular expression. Called by the String.prototype.search method. + */ + search: symbol; + + /** + * A function valued property that is the constructor function that is used to create + * derived objects. + */ + species: symbol; + + /** + * A regular expression method that splits a string at the indices that match the regular + * expression. Called by the String.prototype.split method. + */ + split: symbol; + + /** + * A method that converts an object to a corresponding primitive value.Called by the ToPrimitive + * abstract operation. + */ + toPrimitive: symbol; + + /** + * A String value that is used in the creation of the default string description of an object. + * Called by the built-in method Object.prototype.toString. + */ + toStringTag: symbol; + + /** + * An Object whose own property names are property names that are excluded from the with + * environment bindings of the associated objects. + */ + unscopables: symbol; + + /** + * Non-standard. Use simple mode for core-js symbols. See https://github.com/zloirock/core-js/#caveats-when-using-symbol-polyfill + */ + useSimple(): void; + + /** + * Non-standard. Use setter mode for core-js symbols. See https://github.com/zloirock/core-js/#caveats-when-using-symbol-polyfill + */ + userSetter(): void; +} + +declare var Symbol: SymbolConstructor; + +interface Object { + /** + * Determines whether an object has a property with the specified name. + * @param v A property name. + */ + hasOwnProperty(v: PropertyKey): boolean; + + /** + * Determines whether a specified property is enumerable. + * @param v A property name. + */ + propertyIsEnumerable(v: PropertyKey): boolean; +} + +interface ObjectConstructor { + /** + * Returns an array of all symbol properties found directly on object o. + * @param o Object to retrieve the symbols from. + */ + getOwnPropertySymbols(o: any): symbol[]; + + /** + * Gets the own property descriptor of the specified object. + * An own property descriptor is one that is defined directly on the object and is not + * inherited from the object's prototype. + * @param o Object that contains the property. + * @param p Name of the property. + */ + getOwnPropertyDescriptor(o: any, propertyKey: PropertyKey): PropertyDescriptor; + + /** + * Adds a property to an object, or modifies attributes of an existing property. + * @param o Object on which to add or modify the property. This can be a native JavaScript + * object (that is, a user-defined object or a built in object) or a DOM object. + * @param p The property name. + * @param attributes Descriptor for the property. It can be for a data property or an accessor + * property. + */ + defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any; +} + +interface Math { + [Symbol.toStringTag]: string; +} + +interface JSON { + [Symbol.toStringTag]: string; +} + +// ############################################################################################# +// ECMAScript 6: Collections +// Modules: es6.map, es6.set, es6.weak-map, and es6.weak-set +// ############################################################################################# + +interface Map { + clear(): void; + delete(key: K): boolean; + forEach(callbackfn: (value: V, index: K, map: Map) => void, thisArg?: any): void; + get(key: K): V; + has(key: K): boolean; + set(key: K, value?: V): Map; + size: number; +} + +interface MapConstructor { + new (): Map; + new (iterable: Iterable<[K, V]>): Map; + prototype: Map; +} + +declare var Map: MapConstructor; + +interface Set { + add(value: T): Set; + clear(): void; + delete(value: T): boolean; + forEach(callbackfn: (value: T, index: T, set: Set) => void, thisArg?: any): void; + has(value: T): boolean; + size: number; +} + +interface SetConstructor { + new (): Set; + new (iterable: Iterable): Set; + prototype: Set; +} + +declare var Set: SetConstructor; + +interface WeakMap { + delete(key: K): boolean; + get(key: K): V; + has(key: K): boolean; + set(key: K, value?: V): WeakMap; +} + +interface WeakMapConstructor { + new (): WeakMap; + new (iterable: Iterable<[K, V]>): WeakMap; + prototype: WeakMap; +} + +declare var WeakMap: WeakMapConstructor; + +interface WeakSet { + add(value: T): WeakSet; + delete(value: T): boolean; + has(value: T): boolean; +} + +interface WeakSetConstructor { + new (): WeakSet; + new (iterable: Iterable): WeakSet; + prototype: WeakSet; +} + +declare var WeakSet: WeakSetConstructor; + +// ############################################################################################# +// ECMAScript 6: Iterators +// Modules: es6.string.iterator, es6.array.iterator, es6.map, es6.set, web.dom.iterable +// ############################################################################################# + +interface IteratorResult { + done: boolean; + value?: T; +} + +interface Iterator { + next(value?: any): IteratorResult; + return?(value?: any): IteratorResult; + throw?(e?: any): IteratorResult; +} + +interface Iterable { + [Symbol.iterator](): Iterator; +} + +interface IterableIterator extends Iterator { + [Symbol.iterator](): IterableIterator; +} + +interface String { + /** Iterator */ + [Symbol.iterator](): IterableIterator; +} + +interface Array { + /** Iterator */ + [Symbol.iterator](): IterableIterator; + + /** + * Returns an array of key, value pairs for every entry in the array + */ + entries(): IterableIterator<[number, T]>; + + /** + * Returns an list of keys in the array + */ + keys(): IterableIterator; + + /** + * Returns an list of values in the array + */ + values(): IterableIterator; +} + +interface Map { + entries(): IterableIterator<[K, V]>; + keys(): IterableIterator; + values(): IterableIterator; + [Symbol.iterator](): IterableIterator<[K, V]>; +} + +interface Set { + entries(): IterableIterator<[T, T]>; + keys(): IterableIterator; + values(): IterableIterator; + [Symbol.iterator](): IterableIterator; +} + +interface NodeList { + [Symbol.iterator](): IterableIterator; +} + +interface $for extends IterableIterator { + of(callbackfn: (value: T, key: any) => void, thisArg?: any): void; + array(): T[]; + array(callbackfn: (value: T, key: any) => U, thisArg?: any): U[]; + filter(callbackfn: (value: T, key: any) => boolean, thisArg?: any): $for; + map(callbackfn: (value: T, key: any) => U, thisArg?: any): $for; +} + +declare function $for(iterable: Iterable): $for; + +// ############################################################################################# +// ECMAScript 6: Promises +// Modules: es6.promise +// ############################################################################################# + +interface PromiseLike { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike; + then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => void): PromiseLike; +} + +/** + * Represents the completion of an asynchronous operation + */ +interface Promise { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): Promise; + then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => void): Promise; + + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: (reason: any) => T | PromiseLike): Promise; + catch(onrejected?: (reason: any) => void): Promise; +} + +interface PromiseConstructor { + /** + * A reference to the prototype. + */ + prototype: Promise; + + /** + * Creates a new Promise. + * @param executor A callback used to initialize the promise. This callback is passed two arguments: + * a resolve callback used resolve the promise with a value or the result of another promise, + * and a reject callback used to reject the promise with a provided reason or error. + */ + new (executor: (resolve: (value?: T | PromiseLike) => void, reject: (reason?: any) => void) => void): Promise; + + /** + * Creates a Promise that is resolved with an array of results when all of the provided Promises + * resolve, or rejected when any Promise is rejected. + * @param values An array of Promises. + * @returns A new Promise. + */ + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike, T10 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike, T6 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike]): Promise<[T1, T2, T3, T4, T5]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike ]): Promise<[T1, T2, T3, T4]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike]): Promise<[T1, T2, T3]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; + all(values: Iterable>): Promise; + + /** + * Creates a Promise that is resolved or rejected when any of the provided Promises are resolved + * or rejected. + * @param values An array of Promises. + * @returns A new Promise. + */ + race(values: Iterable>): Promise; + + /** + * Creates a new rejected promise for the provided reason. + * @param reason The reason the promise was rejected. + * @returns A new rejected Promise. + */ + reject(reason: any): Promise; + + /** + * Creates a new rejected promise for the provided reason. + * @param reason The reason the promise was rejected. + * @returns A new rejected Promise. + */ + reject(reason: any): Promise; + + /** + * Creates a new resolved promise for the provided value. + * @param value A promise. + * @returns A promise whose internal state matches the provided promise. + */ + resolve(value: T | PromiseLike): Promise; + + /** + * Creates a new resolved promise . + * @returns A resolved promise. + */ + resolve(): Promise; +} + +declare var Promise: PromiseConstructor; + +// ############################################################################################# +// ECMAScript 6: Reflect +// Modules: es6.reflect +// ############################################################################################# + +declare namespace Reflect { + function apply(target: Function, thisArgument: any, argumentsList: ArrayLike): any; + function construct(target: Function, argumentsList: ArrayLike, newTarget?: any): any; + function defineProperty(target: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean; + function deleteProperty(target: any, propertyKey: PropertyKey): boolean; + function enumerate(target: any): IterableIterator; + function get(target: any, propertyKey: PropertyKey, receiver?: any): any; + function getOwnPropertyDescriptor(target: any, propertyKey: PropertyKey): PropertyDescriptor; + function getPrototypeOf(target: any): any; + function has(target: any, propertyKey: PropertyKey): boolean; + function isExtensible(target: any): boolean; + function ownKeys(target: any): Array; + function preventExtensions(target: any): boolean; + function set(target: any, propertyKey: PropertyKey, value: any, receiver?: any): boolean; + function setPrototypeOf(target: any, proto: any): boolean; +} + +// ############################################################################################# +// ECMAScript 7 +// Modules: es7.array.includes, es7.string.at, es7.string.pad-start, es7.string.pad-end, +// es7.object.to-array, es7.object.get-own-property-descriptors, es7.regexp.escape, +// es7.map.to-json, and es7.set.to-json +// ############################################################################################# + +interface Array { + includes(value: T, fromIndex?: number): boolean; +} + +interface String { + at(index: number): string; + padStart(length: number, fillStr?: string): string; + padEnd(length: number, fillStr?: string): string; +} + +interface ObjectConstructor { + values(object: any): any[]; + entries(object: any): [string, any][]; + getOwnPropertyDescriptors(object: any): PropertyDescriptorMap; +} + +interface RegExpConstructor { + escape(str: string): string; +} + +interface Map { + toJSON(): any; +} + +interface Set { + toJSON(): any; +} + +// ############################################################################################# +// Mozilla JavaScript: Array generics +// Modules: js.array.statics +// ############################################################################################# + +interface ArrayConstructor { + /** + * Appends new elements to an array, and returns the new length of the array. + * @param items New elements of the Array. + */ + push(array: ArrayLike, ...items: T[]): number; + /** + * Removes the last element from an array and returns it. + */ + pop(array: ArrayLike): T; + /** + * Combines two or more arrays. + * @param items Additional items to add to the end of array1. + */ + concat(array: ArrayLike, ...items: (T[]| T)[]): T[]; + /** + * Adds all the elements of an array separated by the specified separator string. + * @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma. + */ + join(array: ArrayLike, separator?: string): string; + /** + * Reverses the elements in an Array. + */ + reverse(array: ArrayLike): T[]; + /** + * Removes the first element from an array and returns it. + */ + shift(array: ArrayLike): T; + /** + * Returns a section of an array. + * @param start The beginning of the specified portion of the array. + * @param end The end of the specified portion of the array. + */ + slice(array: ArrayLike, start?: number, end?: number): T[]; + + /** + * Sorts an array. + * @param compareFn The name of the function used to determine the order of the elements. If omitted, the elements are sorted in ascending, ASCII character order. + */ + sort(array: ArrayLike, compareFn?: (a: T, b: T) => number): T[]; + + /** + * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. + * @param start The zero-based location in the array from which to start removing elements. + */ + splice(array: ArrayLike, start: number): T[]; + + /** + * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. + * @param start The zero-based location in the array from which to start removing elements. + * @param deleteCount The number of elements to remove. + * @param items Elements to insert into the array in place of the deleted elements. + */ + splice(array: ArrayLike, start: number, deleteCount: number, ...items: T[]): T[]; + + /** + * Inserts new elements at the start of an array. + * @param items Elements to insert at the start of the Array. + */ + unshift(array: ArrayLike, ...items: T[]): number; + + /** + * Returns the index of the first occurrence of a value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0. + */ + indexOf(array: ArrayLike, searchElement: T, fromIndex?: number): number; + + /** + * Returns the index of the last occurrence of a specified value in an array. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at the last index in the array. + */ + lastIndexOf(array: ArrayLike, earchElement: T, fromIndex?: number): number; + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param callbackfn A function that accepts up to three arguments. The every method calls the callbackfn function for each element in array1 until the callbackfn returns false, or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + every(array: ArrayLike, callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param callbackfn A function that accepts up to three arguments. The some method calls the callbackfn function for each element in array1 until the callbackfn returns true, or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + some(array: ArrayLike, callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + forEach(array: ArrayLike, callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; + + /** + * Calls a defined callback function on each element of an array, and returns an array that contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + map(array: ArrayLike, callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + filter(array: ArrayLike, callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[]; + + /** + * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + reduce(array: ArrayLike, callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + reduce(array: ArrayLike, callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + reduceRight(array: ArrayLike, callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + + /** + * Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + reduceRight(array: ArrayLike, callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; + + /** + * Returns an array of key, value pairs for every entry in the array + */ + entries(array: ArrayLike): IterableIterator<[number, T]>; + + /** + * Returns an list of keys in the array + */ + keys(array: ArrayLike): IterableIterator; + + /** + * Returns an list of values in the array + */ + values(array: ArrayLike): IterableIterator; + + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(array: ArrayLike, predicate: (value: T, index: number, obj: Array) => boolean, thisArg?: any): T; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(array: ArrayLike, predicate: (value: T) => boolean, thisArg?: any): number; + + /** + * Returns the this object after filling the section identified by start and end with value + * @param value value to fill array section with + * @param start index to start filling the array at. If start is negative, it is treated as + * length+start where length is the length of the array. + * @param end index to stop filling the array at. If end is negative, it is treated as + * length+end. + */ + fill(array: ArrayLike, value: T, start?: number, end?: number): T[]; + + /** + * Returns the this object after copying a section of the array identified by start and end + * to the same array starting at position target + * @param target If target is negative, it is treated as length+target where length is the + * length of the array. + * @param start If start is negative, it is treated as length+start. If end is negative, it + * is treated as length+end. + * @param end If not specified, length of the this object is used as its default value. + */ + copyWithin(array: ArrayLike, target: number, start: number, end?: number): T[]; + + includes(array: ArrayLike, value: T, fromIndex?: number): boolean; + turn(array: ArrayLike, callbackfn: (memo: U, value: T, index: number, array: Array) => void, memo?: U): U; + turn(array: ArrayLike, callbackfn: (memo: Array, value: T, index: number, array: Array) => void, memo?: Array): Array; +} + +// ############################################################################################# +// Object - https://github.com/zloirock/core-js/#object +// Modules: core.object +// ############################################################################################# + +interface ObjectConstructor { + /** + * Non-standard. + */ + isObject(value: any): boolean; + + /** + * Non-standard. + */ + classof(value: any): string; + + /** + * Non-standard. + */ + define(target: T, mixin: any): T; + + /** + * Non-standard. + */ + make(proto: T, mixin?: any): T; +} + +// ############################################################################################# +// Console - https://github.com/zloirock/core-js/#console +// Modules: core.log +// ############################################################################################# + +interface Log extends Console { + (message?: any, ...optionalParams: any[]): void; + enable(): void; + disable(): void; +} + +/** + * Non-standard. + */ +declare var log: Log; + +// ############################################################################################# +// Dict - https://github.com/zloirock/core-js/#dict +// Modules: core.dict +// ############################################################################################# + +interface Dict { + [key: string]: T; + [key: number]: T; + //[key: symbol]: T; +} + +interface DictConstructor { + prototype: Dict; + + new (value?: Dict): Dict; + new (value?: any): Dict; + (value?: Dict): Dict; + (value?: any): Dict; + + isDict(value: any): boolean; + values(object: Dict): IterableIterator; + keys(object: Dict): IterableIterator; + entries(object: Dict): IterableIterator<[PropertyKey, T]>; + has(object: Dict, key: PropertyKey): boolean; + get(object: Dict, key: PropertyKey): T; + set(object: Dict, key: PropertyKey, value: T): Dict; + forEach(object: Dict, callbackfn: (value: T, key: PropertyKey, dict: Dict) => void, thisArg?: any): void; + map(object: Dict, callbackfn: (value: T, key: PropertyKey, dict: Dict) => U, thisArg?: any): Dict; + mapPairs(object: Dict, callbackfn: (value: T, key: PropertyKey, dict: Dict) => [PropertyKey, U], thisArg?: any): Dict; + filter(object: Dict, callbackfn: (value: T, key: PropertyKey, dict: Dict) => boolean, thisArg?: any): Dict; + some(object: Dict, callbackfn: (value: T, key: PropertyKey, dict: Dict) => boolean, thisArg?: any): boolean; + every(object: Dict, callbackfn: (value: T, key: PropertyKey, dict: Dict) => boolean, thisArg?: any): boolean; + find(object: Dict, callbackfn: (value: T, key: PropertyKey, dict: Dict) => boolean, thisArg?: any): T; + findKey(object: Dict, callbackfn: (value: T, key: PropertyKey, dict: Dict) => boolean, thisArg?: any): PropertyKey; + keyOf(object: Dict, value: T): PropertyKey; + includes(object: Dict, value: T): boolean; + reduce(object: Dict, callbackfn: (previousValue: U, value: T, key: PropertyKey, dict: Dict) => U, initialValue: U): U; + reduce(object: Dict, callbackfn: (previousValue: T, value: T, key: PropertyKey, dict: Dict) => T, initialValue?: T): T; + turn(object: Dict, callbackfn: (memo: Dict, value: T, key: PropertyKey, dict: Dict) => void, memo: Dict): Dict; + turn(object: Dict, callbackfn: (memo: Dict, value: T, key: PropertyKey, dict: Dict) => void, memo?: Dict): Dict; +} + +/** + * Non-standard. + */ +declare var Dict: DictConstructor; + +// ############################################################################################# +// Partial application - https://github.com/zloirock/core-js/#partial-application +// Modules: core.function.part +// ############################################################################################# + +interface Function { + /** + * Non-standard. + */ + part(...args: any[]): any; +} + +// ############################################################################################# +// Date formatting - https://github.com/zloirock/core-js/#date-formatting +// Modules: core.date +// ############################################################################################# + +interface Date { + /** + * Non-standard. + */ + format(template: string, locale?: string): string; + + /** + * Non-standard. + */ + formatUTC(template: string, locale?: string): string; +} + +// ############################################################################################# +// Array - https://github.com/zloirock/core-js/#array +// Modules: core.array.turn +// ############################################################################################# + +interface Array { + /** + * Non-standard. + */ + turn(callbackfn: (memo: U, value: T, index: number, array: Array) => void, memo?: U): U; + + /** + * Non-standard. + */ + turn(callbackfn: (memo: Array, value: T, index: number, array: Array) => void, memo?: Array): Array; +} + +// ############################################################################################# +// Number - https://github.com/zloirock/core-js/#number +// Modules: core.number.iterator +// ############################################################################################# + +interface Number { + /** + * Non-standard. + */ + [Symbol.iterator](): IterableIterator; +} + +// ############################################################################################# +// Escaping characters - https://github.com/zloirock/core-js/#escaping-characters +// Modules: core.string.escape-html +// ############################################################################################# + +interface String { + /** + * Non-standard. + */ + escapeHTML(): string; + + /** + * Non-standard. + */ + unescapeHTML(): string; +} + +// ############################################################################################# +// delay - https://github.com/zloirock/core-js/#delay +// Modules: core.delay +// ############################################################################################# + +declare function delay(msec: number): Promise; + +declare namespace core { + var version: string; + + namespace Reflect { + function apply(target: Function, thisArgument: any, argumentsList: ArrayLike): any; + function construct(target: Function, argumentsList: ArrayLike): any; + function defineProperty(target: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean; + function deleteProperty(target: any, propertyKey: PropertyKey): boolean; + function enumerate(target: any): IterableIterator; + function get(target: any, propertyKey: PropertyKey, receiver?: any): any; + function getOwnPropertyDescriptor(target: any, propertyKey: PropertyKey): PropertyDescriptor; + function getPrototypeOf(target: any): any; + function has(target: any, propertyKey: string): boolean; + function has(target: any, propertyKey: symbol): boolean; + function isExtensible(target: any): boolean; + function ownKeys(target: any): Array; + function preventExtensions(target: any): boolean; + function set(target: any, propertyKey: PropertyKey, value: any, receiver?: any): boolean; + function setPrototypeOf(target: any, proto: any): boolean; + } + + var Object: { + getPrototypeOf(o: any): any; + getOwnPropertyDescriptor(o: any, p: string): PropertyDescriptor; + getOwnPropertyNames(o: any): string[]; + create(o: any, properties?: PropertyDescriptorMap): any; + defineProperty(o: any, p: string, attributes: PropertyDescriptor): any; + defineProperties(o: any, properties: PropertyDescriptorMap): any; + seal(o: T): T; + freeze(o: T): T; + preventExtensions(o: T): T; + isSealed(o: any): boolean; + isFrozen(o: any): boolean; + isExtensible(o: any): boolean; + keys(o: any): string[]; + assign(target: any, ...sources: any[]): any; + is(value1: any, value2: any): boolean; + setPrototypeOf(o: any, proto: any): any; + getOwnPropertySymbols(o: any): symbol[]; + getOwnPropertyDescriptor(o: any, propertyKey: PropertyKey): PropertyDescriptor; + defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any; + values(object: any): any[]; + entries(object: any): any[]; + getOwnPropertyDescriptors(object: any): PropertyDescriptorMap; + isObject(value: any): boolean; + classof(value: any): string; + define(target: T, mixin: any): T; + make(proto: T, mixin?: any): T; + }; + + var Function: { + bind(target: Function, thisArg: any, ...argArray: any[]): any; + part(target: Function, ...args: any[]): any; + }; + + var Array: { + from(arrayLike: ArrayLike, mapfn: (v: T, k: number) => U, thisArg?: any): Array; + from(iterable: Iterable, mapfn: (v: T, k: number) => U, thisArg?: any): Array; + from(arrayLike: ArrayLike): Array; + from(iterable: Iterable): Array; + of(...items: T[]): Array; + push(array: ArrayLike, ...items: T[]): number; + pop(array: ArrayLike): T; + concat(array: ArrayLike, ...items: (T[]| T)[]): T[]; + join(array: ArrayLike, separator?: string): string; + reverse(array: ArrayLike): T[]; + shift(array: ArrayLike): T; + slice(array: ArrayLike, start?: number, end?: number): T[]; + sort(array: ArrayLike, compareFn?: (a: T, b: T) => number): T[]; + splice(array: ArrayLike, start: number): T[]; + splice(array: ArrayLike, start: number, deleteCount: number, ...items: T[]): T[]; + unshift(array: ArrayLike, ...items: T[]): number; + indexOf(array: ArrayLike, searchElement: T, fromIndex?: number): number; + lastIndexOf(array: ArrayLike, earchElement: T, fromIndex?: number): number; + every(array: ArrayLike, callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; + some(array: ArrayLike, callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; + forEach(array: ArrayLike, callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; + map(array: ArrayLike, callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; + filter(array: ArrayLike, callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[]; + reduce(array: ArrayLike, callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; + reduce(array: ArrayLike, callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + reduceRight(array: ArrayLike, callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; + reduceRight(array: ArrayLike, callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + entries(array: ArrayLike): IterableIterator<[number, T]>; + keys(array: ArrayLike): IterableIterator; + values(array: ArrayLike): IterableIterator; + find(array: ArrayLike, predicate: (value: T, index: number, obj: Array) => boolean, thisArg?: any): T; + findIndex(array: ArrayLike, predicate: (value: T) => boolean, thisArg?: any): number; + fill(array: ArrayLike, value: T, start?: number, end?: number): T[]; + copyWithin(array: ArrayLike, target: number, start: number, end?: number): T[]; + includes(array: ArrayLike, value: T, fromIndex?: number): boolean; + turn(array: ArrayLike, callbackfn: (memo: Array, value: T, index: number, array: Array) => void, memo?: Array): Array; + turn(array: ArrayLike, callbackfn: (memo: U, value: T, index: number, array: Array) => void, memo?: U): U; + }; + + var String: { + codePointAt(text: string, pos: number): number; + includes(text: string, searchString: string, position?: number): boolean; + endsWith(text: string, searchString: string, endPosition?: number): boolean; + repeat(text: string, count: number): string; + fromCodePoint(...codePoints: number[]): string; + raw(template: TemplateStringsArray, ...substitutions: any[]): string; + startsWith(text: string, searchString: string, position?: number): boolean; + at(text: string, index: number): string; + lpad(text: string, length: number, fillStr?: string): string; + rpad(text: string, length: number, fillStr?: string): string; + escapeHTML(text: string): string; + unescapeHTML(text: string): string; + }; + + var Date: { + now(): number; + toISOString(date: Date): string; + format(date: Date, template: string, locale?: string): string; + formatUTC(date: Date, template: string, locale?: string): string; + }; + + var Number: { + EPSILON: number; + isFinite(number: number): boolean; + isInteger(number: number): boolean; + isNaN(number: number): boolean; + isSafeInteger(number: number): boolean; + MAX_SAFE_INTEGER: number; + MIN_SAFE_INTEGER: number; + parseFloat(string: string): number; + parseInt(string: string, radix?: number): number; + clz32(x: number): number; + imul(x: number, y: number): number; + sign(x: number): number; + log10(x: number): number; + log2(x: number): number; + log1p(x: number): number; + expm1(x: number): number; + cosh(x: number): number; + sinh(x: number): number; + tanh(x: number): number; + acosh(x: number): number; + asinh(x: number): number; + atanh(x: number): number; + hypot(...values: number[]): number; + trunc(x: number): number; + fround(x: number): number; + cbrt(x: number): number; + random(lim?: number): number; + }; + + var Math: { + clz32(x: number): number; + imul(x: number, y: number): number; + sign(x: number): number; + log10(x: number): number; + log2(x: number): number; + log1p(x: number): number; + expm1(x: number): number; + cosh(x: number): number; + sinh(x: number): number; + tanh(x: number): number; + acosh(x: number): number; + asinh(x: number): number; + atanh(x: number): number; + hypot(...values: number[]): number; + trunc(x: number): number; + fround(x: number): number; + cbrt(x: number): number; + }; + + var RegExp: { + escape(str: string): string; + }; + + var Map: MapConstructor; + var Set: SetConstructor; + var WeakMap: WeakMapConstructor; + var WeakSet: WeakSetConstructor; + var Promise: PromiseConstructor; + var Symbol: SymbolConstructor; + var Dict: DictConstructor; + var global: any; + var log: Log; + var _: boolean; + + function setTimeout(handler: any, timeout?: any, ...args: any[]): number; + + function setInterval(handler: any, timeout?: any, ...args: any[]): number; + + function setImmediate(expression: any, ...args: any[]): number; + + function clearImmediate(handle: number): void; + + function $for(iterable: Iterable): $for; + + function isIterable(value: any): boolean; + + function getIterator(iterable: Iterable): Iterator; + + interface Locale { + weekdays: string; + months: string; + } + + function addLocale(lang: string, locale: Locale): typeof core; + + function locale(lang?: string): string; + + function delay(msec: number): Promise; +} + +declare module "core-js" { + export = core; +} +declare module "core-js/shim" { + export = core; +} +declare module "core-js/core" { + export = core; +} +declare module "core-js/core/$for" { + import $for = core.$for; + export = $for; +} +declare module "core-js/core/_" { + var _: typeof core._; + export = _; +} +declare module "core-js/core/array" { + var Array: typeof core.Array; + export = Array; +} +declare module "core-js/core/date" { + var Date: typeof core.Date; + export = Date; +} +declare module "core-js/core/delay" { + var delay: typeof core.delay; + export = delay; +} +declare module "core-js/core/dict" { + var Dict: typeof core.Dict; + export = Dict; +} +declare module "core-js/core/function" { + var Function: typeof core.Function; + export = Function; +} +declare module "core-js/core/global" { + var global: typeof core.global; + export = global; +} +declare module "core-js/core/log" { + var log: typeof core.log; + export = log; +} +declare module "core-js/core/number" { + var Number: typeof core.Number; + export = Number; +} +declare module "core-js/core/object" { + var Object: typeof core.Object; + export = Object; +} +declare module "core-js/core/string" { + var String: typeof core.String; + export = String; +} +declare module "core-js/fn/$for" { + import $for = core.$for; + export = $for; +} +declare module "core-js/fn/_" { + var _: typeof core._; + export = _; +} +declare module "core-js/fn/clear-immediate" { + var clearImmediate: typeof core.clearImmediate; + export = clearImmediate; +} +declare module "core-js/fn/delay" { + var delay: typeof core.delay; + export = delay; +} +declare module "core-js/fn/dict" { + var Dict: typeof core.Dict; + export = Dict; +} +declare module "core-js/fn/get-iterator" { + var getIterator: typeof core.getIterator; + export = getIterator; +} +declare module "core-js/fn/global" { + var global: typeof core.global; + export = global; +} +declare module "core-js/fn/is-iterable" { + var isIterable: typeof core.isIterable; + export = isIterable; +} +declare module "core-js/fn/log" { + var log: typeof core.log; + export = log; +} +declare module "core-js/fn/map" { + var Map: typeof core.Map; + export = Map; +} +declare module "core-js/fn/promise" { + var Promise: typeof core.Promise; + export = Promise; +} +declare module "core-js/fn/set" { + var Set: typeof core.Set; + export = Set; +} +declare module "core-js/fn/set-immediate" { + var setImmediate: typeof core.setImmediate; + export = setImmediate; +} +declare module "core-js/fn/set-interval" { + var setInterval: typeof core.setInterval; + export = setInterval; +} +declare module "core-js/fn/set-timeout" { + var setTimeout: typeof core.setTimeout; + export = setTimeout; +} +declare module "core-js/fn/weak-map" { + var WeakMap: typeof core.WeakMap; + export = WeakMap; +} +declare module "core-js/fn/weak-set" { + var WeakSet: typeof core.WeakSet; + export = WeakSet; +} +declare module "core-js/fn/array" { + var Array: typeof core.Array; + export = Array; +} +declare module "core-js/fn/array/concat" { + var concat: typeof core.Array.concat; + export = concat; +} +declare module "core-js/fn/array/copy-within" { + var copyWithin: typeof core.Array.copyWithin; + export = copyWithin; +} +declare module "core-js/fn/array/entries" { + var entries: typeof core.Array.entries; + export = entries; +} +declare module "core-js/fn/array/every" { + var every: typeof core.Array.every; + export = every; +} +declare module "core-js/fn/array/fill" { + var fill: typeof core.Array.fill; + export = fill; +} +declare module "core-js/fn/array/filter" { + var filter: typeof core.Array.filter; + export = filter; +} +declare module "core-js/fn/array/find" { + var find: typeof core.Array.find; + export = find; +} +declare module "core-js/fn/array/find-index" { + var findIndex: typeof core.Array.findIndex; + export = findIndex; +} +declare module "core-js/fn/array/for-each" { + var forEach: typeof core.Array.forEach; + export = forEach; +} +declare module "core-js/fn/array/from" { + var from: typeof core.Array.from; + export = from; +} +declare module "core-js/fn/array/includes" { + var includes: typeof core.Array.includes; + export = includes; +} +declare module "core-js/fn/array/index-of" { + var indexOf: typeof core.Array.indexOf; + export = indexOf; +} +declare module "core-js/fn/array/join" { + var join: typeof core.Array.join; + export = join; +} +declare module "core-js/fn/array/keys" { + var keys: typeof core.Array.keys; + export = keys; +} +declare module "core-js/fn/array/last-index-of" { + var lastIndexOf: typeof core.Array.lastIndexOf; + export = lastIndexOf; +} +declare module "core-js/fn/array/map" { + var map: typeof core.Array.map; + export = map; +} +declare module "core-js/fn/array/of" { + var of: typeof core.Array.of; + export = of; +} +declare module "core-js/fn/array/pop" { + var pop: typeof core.Array.pop; + export = pop; +} +declare module "core-js/fn/array/push" { + var push: typeof core.Array.push; + export = push; +} +declare module "core-js/fn/array/reduce" { + var reduce: typeof core.Array.reduce; + export = reduce; +} +declare module "core-js/fn/array/reduce-right" { + var reduceRight: typeof core.Array.reduceRight; + export = reduceRight; +} +declare module "core-js/fn/array/reverse" { + var reverse: typeof core.Array.reverse; + export = reverse; +} +declare module "core-js/fn/array/shift" { + var shift: typeof core.Array.shift; + export = shift; +} +declare module "core-js/fn/array/slice" { + var slice: typeof core.Array.slice; + export = slice; +} +declare module "core-js/fn/array/some" { + var some: typeof core.Array.some; + export = some; +} +declare module "core-js/fn/array/sort" { + var sort: typeof core.Array.sort; + export = sort; +} +declare module "core-js/fn/array/splice" { + var splice: typeof core.Array.splice; + export = splice; +} +declare module "core-js/fn/array/turn" { + var turn: typeof core.Array.turn; + export = turn; +} +declare module "core-js/fn/array/unshift" { + var unshift: typeof core.Array.unshift; + export = unshift; +} +declare module "core-js/fn/array/values" { + var values: typeof core.Array.values; + export = values; +} +declare module "core-js/fn/date" { + var Date: typeof core.Date; + export = Date; +} +declare module "core-js/fn/date/add-locale" { + var addLocale: typeof core.addLocale; + export = addLocale; +} +declare module "core-js/fn/date/format" { + var format: typeof core.Date.format; + export = format; +} +declare module "core-js/fn/date/formatUTC" { + var formatUTC: typeof core.Date.formatUTC; + export = formatUTC; +} +declare module "core-js/fn/function" { + var Function: typeof core.Function; + export = Function; +} +declare module "core-js/fn/function/has-instance" { + var hasInstance: (value: any) => boolean; + export = hasInstance; +} +declare module "core-js/fn/function/name" +{ +} +declare module "core-js/fn/function/part" { + var part: typeof core.Function.part; + export = part; +} +declare module "core-js/fn/math" { + var Math: typeof core.Math; + export = Math; +} +declare module "core-js/fn/math/acosh" { + var acosh: typeof core.Math.acosh; + export = acosh; +} +declare module "core-js/fn/math/asinh" { + var asinh: typeof core.Math.asinh; + export = asinh; +} +declare module "core-js/fn/math/atanh" { + var atanh: typeof core.Math.atanh; + export = atanh; +} +declare module "core-js/fn/math/cbrt" { + var cbrt: typeof core.Math.cbrt; + export = cbrt; +} +declare module "core-js/fn/math/clz32" { + var clz32: typeof core.Math.clz32; + export = clz32; +} +declare module "core-js/fn/math/cosh" { + var cosh: typeof core.Math.cosh; + export = cosh; +} +declare module "core-js/fn/math/expm1" { + var expm1: typeof core.Math.expm1; + export = expm1; +} +declare module "core-js/fn/math/fround" { + var fround: typeof core.Math.fround; + export = fround; +} +declare module "core-js/fn/math/hypot" { + var hypot: typeof core.Math.hypot; + export = hypot; +} +declare module "core-js/fn/math/imul" { + var imul: typeof core.Math.imul; + export = imul; +} +declare module "core-js/fn/math/log10" { + var log10: typeof core.Math.log10; + export = log10; +} +declare module "core-js/fn/math/log1p" { + var log1p: typeof core.Math.log1p; + export = log1p; +} +declare module "core-js/fn/math/log2" { + var log2: typeof core.Math.log2; + export = log2; +} +declare module "core-js/fn/math/sign" { + var sign: typeof core.Math.sign; + export = sign; +} +declare module "core-js/fn/math/sinh" { + var sinh: typeof core.Math.sinh; + export = sinh; +} +declare module "core-js/fn/math/tanh" { + var tanh: typeof core.Math.tanh; + export = tanh; +} +declare module "core-js/fn/math/trunc" { + var trunc: typeof core.Math.trunc; + export = trunc; +} +declare module "core-js/fn/number" { + var Number: typeof core.Number; + export = Number; +} +declare module "core-js/fn/number/epsilon" { + var EPSILON: typeof core.Number.EPSILON; + export = EPSILON; +} +declare module "core-js/fn/number/is-finite" { + var isFinite: typeof core.Number.isFinite; + export = isFinite; +} +declare module "core-js/fn/number/is-integer" { + var isInteger: typeof core.Number.isInteger; + export = isInteger; +} +declare module "core-js/fn/number/is-nan" { + var isNaN: typeof core.Number.isNaN; + export = isNaN; +} +declare module "core-js/fn/number/is-safe-integer" { + var isSafeInteger: typeof core.Number.isSafeInteger; + export = isSafeInteger; +} +declare module "core-js/fn/number/max-safe-integer" { + var MAX_SAFE_INTEGER: typeof core.Number.MAX_SAFE_INTEGER; + export = MAX_SAFE_INTEGER; +} +declare module "core-js/fn/number/min-safe-integer" { + var MIN_SAFE_INTEGER: typeof core.Number.MIN_SAFE_INTEGER; + export = MIN_SAFE_INTEGER; +} +declare module "core-js/fn/number/parse-float" { + var parseFloat: typeof core.Number.parseFloat; + export = parseFloat; +} +declare module "core-js/fn/number/parse-int" { + var parseInt: typeof core.Number.parseInt; + export = parseInt; +} +declare module "core-js/fn/number/random" { + var random: typeof core.Number.random; + export = random; +} +declare module "core-js/fn/object" { + var Object: typeof core.Object; + export = Object; +} +declare module "core-js/fn/object/assign" { + var assign: typeof core.Object.assign; + export = assign; +} +declare module "core-js/fn/object/classof" { + var classof: typeof core.Object.classof; + export = classof; +} +declare module "core-js/fn/object/create" { + var create: typeof core.Object.create; + export = create; +} +declare module "core-js/fn/object/define" { + var define: typeof core.Object.define; + export = define; +} +declare module "core-js/fn/object/define-properties" { + var defineProperties: typeof core.Object.defineProperties; + export = defineProperties; +} +declare module "core-js/fn/object/define-property" { + var defineProperty: typeof core.Object.defineProperty; + export = defineProperty; +} +declare module "core-js/fn/object/entries" { + var entries: typeof core.Object.entries; + export = entries; +} +declare module "core-js/fn/object/freeze" { + var freeze: typeof core.Object.freeze; + export = freeze; +} +declare module "core-js/fn/object/get-own-property-descriptor" { + var getOwnPropertyDescriptor: typeof core.Object.getOwnPropertyDescriptor; + export = getOwnPropertyDescriptor; +} +declare module "core-js/fn/object/get-own-property-descriptors" { + var getOwnPropertyDescriptors: typeof core.Object.getOwnPropertyDescriptors; + export = getOwnPropertyDescriptors; +} +declare module "core-js/fn/object/get-own-property-names" { + var getOwnPropertyNames: typeof core.Object.getOwnPropertyNames; + export = getOwnPropertyNames; +} +declare module "core-js/fn/object/get-own-property-symbols" { + var getOwnPropertySymbols: typeof core.Object.getOwnPropertySymbols; + export = getOwnPropertySymbols; +} +declare module "core-js/fn/object/get-prototype-of" { + var getPrototypeOf: typeof core.Object.getPrototypeOf; + export = getPrototypeOf; +} +declare module "core-js/fn/object/is" { + var is: typeof core.Object.is; + export = is; +} +declare module "core-js/fn/object/is-extensible" { + var isExtensible: typeof core.Object.isExtensible; + export = isExtensible; +} +declare module "core-js/fn/object/is-frozen" { + var isFrozen: typeof core.Object.isFrozen; + export = isFrozen; +} +declare module "core-js/fn/object/is-object" { + var isObject: typeof core.Object.isObject; + export = isObject; +} +declare module "core-js/fn/object/is-sealed" { + var isSealed: typeof core.Object.isSealed; + export = isSealed; +} +declare module "core-js/fn/object/keys" { + var keys: typeof core.Object.keys; + export = keys; +} +declare module "core-js/fn/object/make" { + var make: typeof core.Object.make; + export = make; +} +declare module "core-js/fn/object/prevent-extensions" { + var preventExtensions: typeof core.Object.preventExtensions; + export = preventExtensions; +} +declare module "core-js/fn/object/seal" { + var seal: typeof core.Object.seal; + export = seal; +} +declare module "core-js/fn/object/set-prototype-of" { + var setPrototypeOf: typeof core.Object.setPrototypeOf; + export = setPrototypeOf; +} +declare module "core-js/fn/object/values" { + var values: typeof core.Object.values; + export = values; +} +declare module "core-js/fn/reflect" { + var Reflect: typeof core.Reflect; + export = Reflect; +} +declare module "core-js/fn/reflect/apply" { + var apply: typeof core.Reflect.apply; + export = apply; +} +declare module "core-js/fn/reflect/construct" { + var construct: typeof core.Reflect.construct; + export = construct; +} +declare module "core-js/fn/reflect/define-property" { + var defineProperty: typeof core.Reflect.defineProperty; + export = defineProperty; +} +declare module "core-js/fn/reflect/delete-property" { + var deleteProperty: typeof core.Reflect.deleteProperty; + export = deleteProperty; +} +declare module "core-js/fn/reflect/enumerate" { + var enumerate: typeof core.Reflect.enumerate; + export = enumerate; +} +declare module "core-js/fn/reflect/get" { + var get: typeof core.Reflect.get; + export = get; +} +declare module "core-js/fn/reflect/get-own-property-descriptor" { + var getOwnPropertyDescriptor: typeof core.Reflect.getOwnPropertyDescriptor; + export = getOwnPropertyDescriptor; +} +declare module "core-js/fn/reflect/get-prototype-of" { + var getPrototypeOf: typeof core.Reflect.getPrototypeOf; + export = getPrototypeOf; +} +declare module "core-js/fn/reflect/has" { + var has: typeof core.Reflect.has; + export = has; +} +declare module "core-js/fn/reflect/is-extensible" { + var isExtensible: typeof core.Reflect.isExtensible; + export = isExtensible; +} +declare module "core-js/fn/reflect/own-keys" { + var ownKeys: typeof core.Reflect.ownKeys; + export = ownKeys; +} +declare module "core-js/fn/reflect/prevent-extensions" { + var preventExtensions: typeof core.Reflect.preventExtensions; + export = preventExtensions; +} +declare module "core-js/fn/reflect/set" { + var set: typeof core.Reflect.set; + export = set; +} +declare module "core-js/fn/reflect/set-prototype-of" { + var setPrototypeOf: typeof core.Reflect.setPrototypeOf; + export = setPrototypeOf; +} +declare module "core-js/fn/regexp" { + var RegExp: typeof core.RegExp; + export = RegExp; +} +declare module "core-js/fn/regexp/escape" { + var escape: typeof core.RegExp.escape; + export = escape; +} +declare module "core-js/fn/string" { + var String: typeof core.String; + export = String; +} +declare module "core-js/fn/string/at" { + var at: typeof core.String.at; + export = at; +} +declare module "core-js/fn/string/code-point-at" { + var codePointAt: typeof core.String.codePointAt; + export = codePointAt; +} +declare module "core-js/fn/string/ends-with" { + var endsWith: typeof core.String.endsWith; + export = endsWith; +} +declare module "core-js/fn/string/escape-html" { + var escapeHTML: typeof core.String.escapeHTML; + export = escapeHTML; +} +declare module "core-js/fn/string/from-code-point" { + var fromCodePoint: typeof core.String.fromCodePoint; + export = fromCodePoint; +} +declare module "core-js/fn/string/includes" { + var includes: typeof core.String.includes; + export = includes; +} +declare module "core-js/fn/string/lpad" { + var lpad: typeof core.String.lpad; + export = lpad; +} +declare module "core-js/fn/string/raw" { + var raw: typeof core.String.raw; + export = raw; +} +declare module "core-js/fn/string/repeat" { + var repeat: typeof core.String.repeat; + export = repeat; +} +declare module "core-js/fn/string/rpad" { + var rpad: typeof core.String.rpad; + export = rpad; +} +declare module "core-js/fn/string/starts-with" { + var startsWith: typeof core.String.startsWith; + export = startsWith; +} +declare module "core-js/fn/string/unescape-html" { + var unescapeHTML: typeof core.String.unescapeHTML; + export = unescapeHTML; +} +declare module "core-js/fn/symbol" { + var Symbol: typeof core.Symbol; + export = Symbol; +} +declare module "core-js/fn/symbol/for" { + var _for: typeof core.Symbol.for; + export = _for; +} +declare module "core-js/fn/symbol/has-instance" { + var hasInstance: typeof core.Symbol.hasInstance; + export = hasInstance; +} +declare module "core-js/fn/symbol/is-concat-spreadable" { + var isConcatSpreadable: typeof core.Symbol.isConcatSpreadable; + export = isConcatSpreadable; +} +declare module "core-js/fn/symbol/iterator" { + var iterator: typeof core.Symbol.iterator; + export = iterator; +} +declare module "core-js/fn/symbol/key-for" { + var keyFor: typeof core.Symbol.keyFor; + export = keyFor; +} +declare module "core-js/fn/symbol/match" { + var match: typeof core.Symbol.match; + export = match; +} +declare module "core-js/fn/symbol/replace" { + var replace: typeof core.Symbol.replace; + export = replace; +} +declare module "core-js/fn/symbol/search" { + var search: typeof core.Symbol.search; + export = search; +} +declare module "core-js/fn/symbol/species" { + var species: typeof core.Symbol.species; + export = species; +} +declare module "core-js/fn/symbol/split" { + var split: typeof core.Symbol.split; + export = split; +} +declare module "core-js/fn/symbol/to-primitive" { + var toPrimitive: typeof core.Symbol.toPrimitive; + export = toPrimitive; +} +declare module "core-js/fn/symbol/to-string-tag" { + var toStringTag: typeof core.Symbol.toStringTag; + export = toStringTag; +} +declare module "core-js/fn/symbol/unscopables" { + var unscopables: typeof core.Symbol.unscopables; + export = unscopables; +} +declare module "core-js/es5" { + export = core; +} +declare module "core-js/es6" { + export = core; +} +declare module "core-js/es6/array" { + var Array: typeof core.Array; + export = Array; +} +declare module "core-js/es6/function" { + var Function: typeof core.Function; + export = Function; +} +declare module "core-js/es6/map" { + var Map: typeof core.Map; + export = Map; +} +declare module "core-js/es6/math" { + var Math: typeof core.Math; + export = Math; +} +declare module "core-js/es6/number" { + var Number: typeof core.Number; + export = Number; +} +declare module "core-js/es6/object" { + var Object: typeof core.Object; + export = Object; +} +declare module "core-js/es6/promise" { + var Promise: typeof core.Promise; + export = Promise; +} +declare module "core-js/es6/reflect" { + var Reflect: typeof core.Reflect; + export = Reflect; +} +declare module "core-js/es6/regexp" { + var RegExp: typeof core.RegExp; + export = RegExp; +} +declare module "core-js/es6/set" { + var Set: typeof core.Set; + export = Set; +} +declare module "core-js/es6/string" { + var String: typeof core.String; + export = String; +} +declare module "core-js/es6/symbol" { + var Symbol: typeof core.Symbol; + export = Symbol; +} +declare module "core-js/es6/weak-map" { + var WeakMap: typeof core.WeakMap; + export = WeakMap; +} +declare module "core-js/es6/weak-set" { + var WeakSet: typeof core.WeakSet; + export = WeakSet; +} +declare module "core-js/es7" { + export = core; +} +declare module "core-js/es7/array" { + var Array: typeof core.Array; + export = Array; +} +declare module "core-js/es7/map" { + var Map: typeof core.Map; + export = Map; +} +declare module "core-js/es7/object" { + var Object: typeof core.Object; + export = Object; +} +declare module "core-js/es7/regexp" { + var RegExp: typeof core.RegExp; + export = RegExp; +} +declare module "core-js/es7/set" { + var Set: typeof core.Set; + export = Set; +} +declare module "core-js/es7/string" { + var String: typeof core.String; + export = String; +} +declare module "core-js/js" { + export = core; +} +declare module "core-js/js/array" { + var Array: typeof core.Array; + export = Array; +} +declare module "core-js/web" { + export = core; +} +declare module "core-js/web/dom" { + export = core; +} +declare module "core-js/web/immediate" { + export = core; +} +declare module "core-js/web/timers" { + export = core; +} +declare module "core-js/library" { + export = core; +} +declare module "core-js/library/shim" { + export = core; +} +declare module "core-js/library/core" { + export = core; +} +declare module "core-js/library/core/$for" { + import $for = core.$for; + export = $for; +} +declare module "core-js/library/core/_" { + var _: typeof core._; + export = _; +} +declare module "core-js/library/core/array" { + var Array: typeof core.Array; + export = Array; +} +declare module "core-js/library/core/date" { + var Date: typeof core.Date; + export = Date; +} +declare module "core-js/library/core/delay" { + var delay: typeof core.delay; + export = delay; +} +declare module "core-js/library/core/dict" { + var Dict: typeof core.Dict; + export = Dict; +} +declare module "core-js/library/core/function" { + var Function: typeof core.Function; + export = Function; +} +declare module "core-js/library/core/global" { + var global: typeof core.global; + export = global; +} +declare module "core-js/library/core/log" { + var log: typeof core.log; + export = log; +} +declare module "core-js/library/core/number" { + var Number: typeof core.Number; + export = Number; +} +declare module "core-js/library/core/object" { + var Object: typeof core.Object; + export = Object; +} +declare module "core-js/library/core/string" { + var String: typeof core.String; + export = String; +} +declare module "core-js/library/fn/$for" { + import $for = core.$for; + export = $for; +} +declare module "core-js/library/fn/_" { + var _: typeof core._; + export = _; +} +declare module "core-js/library/fn/clear-immediate" { + var clearImmediate: typeof core.clearImmediate; + export = clearImmediate; +} +declare module "core-js/library/fn/delay" { + var delay: typeof core.delay; + export = delay; +} +declare module "core-js/library/fn/dict" { + var Dict: typeof core.Dict; + export = Dict; +} +declare module "core-js/library/fn/get-iterator" { + var getIterator: typeof core.getIterator; + export = getIterator; +} +declare module "core-js/library/fn/global" { + var global: typeof core.global; + export = global; +} +declare module "core-js/library/fn/is-iterable" { + var isIterable: typeof core.isIterable; + export = isIterable; +} +declare module "core-js/library/fn/log" { + var log: typeof core.log; + export = log; +} +declare module "core-js/library/fn/map" { + var Map: typeof core.Map; + export = Map; +} +declare module "core-js/library/fn/promise" { + var Promise: typeof core.Promise; + export = Promise; +} +declare module "core-js/library/fn/set" { + var Set: typeof core.Set; + export = Set; +} +declare module "core-js/library/fn/set-immediate" { + var setImmediate: typeof core.setImmediate; + export = setImmediate; +} +declare module "core-js/library/fn/set-interval" { + var setInterval: typeof core.setInterval; + export = setInterval; +} +declare module "core-js/library/fn/set-timeout" { + var setTimeout: typeof core.setTimeout; + export = setTimeout; +} +declare module "core-js/library/fn/weak-map" { + var WeakMap: typeof core.WeakMap; + export = WeakMap; +} +declare module "core-js/library/fn/weak-set" { + var WeakSet: typeof core.WeakSet; + export = WeakSet; +} +declare module "core-js/library/fn/array" { + var Array: typeof core.Array; + export = Array; +} +declare module "core-js/library/fn/array/concat" { + var concat: typeof core.Array.concat; + export = concat; +} +declare module "core-js/library/fn/array/copy-within" { + var copyWithin: typeof core.Array.copyWithin; + export = copyWithin; +} +declare module "core-js/library/fn/array/entries" { + var entries: typeof core.Array.entries; + export = entries; +} +declare module "core-js/library/fn/array/every" { + var every: typeof core.Array.every; + export = every; +} +declare module "core-js/library/fn/array/fill" { + var fill: typeof core.Array.fill; + export = fill; +} +declare module "core-js/library/fn/array/filter" { + var filter: typeof core.Array.filter; + export = filter; +} +declare module "core-js/library/fn/array/find" { + var find: typeof core.Array.find; + export = find; +} +declare module "core-js/library/fn/array/find-index" { + var findIndex: typeof core.Array.findIndex; + export = findIndex; +} +declare module "core-js/library/fn/array/for-each" { + var forEach: typeof core.Array.forEach; + export = forEach; +} +declare module "core-js/library/fn/array/from" { + var from: typeof core.Array.from; + export = from; +} +declare module "core-js/library/fn/array/includes" { + var includes: typeof core.Array.includes; + export = includes; +} +declare module "core-js/library/fn/array/index-of" { + var indexOf: typeof core.Array.indexOf; + export = indexOf; +} +declare module "core-js/library/fn/array/join" { + var join: typeof core.Array.join; + export = join; +} +declare module "core-js/library/fn/array/keys" { + var keys: typeof core.Array.keys; + export = keys; +} +declare module "core-js/library/fn/array/last-index-of" { + var lastIndexOf: typeof core.Array.lastIndexOf; + export = lastIndexOf; +} +declare module "core-js/library/fn/array/map" { + var map: typeof core.Array.map; + export = map; +} +declare module "core-js/library/fn/array/of" { + var of: typeof core.Array.of; + export = of; +} +declare module "core-js/library/fn/array/pop" { + var pop: typeof core.Array.pop; + export = pop; +} +declare module "core-js/library/fn/array/push" { + var push: typeof core.Array.push; + export = push; +} +declare module "core-js/library/fn/array/reduce" { + var reduce: typeof core.Array.reduce; + export = reduce; +} +declare module "core-js/library/fn/array/reduce-right" { + var reduceRight: typeof core.Array.reduceRight; + export = reduceRight; +} +declare module "core-js/library/fn/array/reverse" { + var reverse: typeof core.Array.reverse; + export = reverse; +} +declare module "core-js/library/fn/array/shift" { + var shift: typeof core.Array.shift; + export = shift; +} +declare module "core-js/library/fn/array/slice" { + var slice: typeof core.Array.slice; + export = slice; +} +declare module "core-js/library/fn/array/some" { + var some: typeof core.Array.some; + export = some; +} +declare module "core-js/library/fn/array/sort" { + var sort: typeof core.Array.sort; + export = sort; +} +declare module "core-js/library/fn/array/splice" { + var splice: typeof core.Array.splice; + export = splice; +} +declare module "core-js/library/fn/array/turn" { + var turn: typeof core.Array.turn; + export = turn; +} +declare module "core-js/library/fn/array/unshift" { + var unshift: typeof core.Array.unshift; + export = unshift; +} +declare module "core-js/library/fn/array/values" { + var values: typeof core.Array.values; + export = values; +} +declare module "core-js/library/fn/date" { + var Date: typeof core.Date; + export = Date; +} +declare module "core-js/library/fn/date/add-locale" { + var addLocale: typeof core.addLocale; + export = addLocale; +} +declare module "core-js/library/fn/date/format" { + var format: typeof core.Date.format; + export = format; +} +declare module "core-js/library/fn/date/formatUTC" { + var formatUTC: typeof core.Date.formatUTC; + export = formatUTC; +} +declare module "core-js/library/fn/function" { + var Function: typeof core.Function; + export = Function; +} +declare module "core-js/library/fn/function/has-instance" { + var hasInstance: (value: any) => boolean; + export = hasInstance; +} +declare module "core-js/library/fn/function/name" { +} +declare module "core-js/library/fn/function/part" { + var part: typeof core.Function.part; + export = part; +} +declare module "core-js/library/fn/math" { + var Math: typeof core.Math; + export = Math; +} +declare module "core-js/library/fn/math/acosh" { + var acosh: typeof core.Math.acosh; + export = acosh; +} +declare module "core-js/library/fn/math/asinh" { + var asinh: typeof core.Math.asinh; + export = asinh; +} +declare module "core-js/library/fn/math/atanh" { + var atanh: typeof core.Math.atanh; + export = atanh; +} +declare module "core-js/library/fn/math/cbrt" { + var cbrt: typeof core.Math.cbrt; + export = cbrt; +} +declare module "core-js/library/fn/math/clz32" { + var clz32: typeof core.Math.clz32; + export = clz32; +} +declare module "core-js/library/fn/math/cosh" { + var cosh: typeof core.Math.cosh; + export = cosh; +} +declare module "core-js/library/fn/math/expm1" { + var expm1: typeof core.Math.expm1; + export = expm1; +} +declare module "core-js/library/fn/math/fround" { + var fround: typeof core.Math.fround; + export = fround; +} +declare module "core-js/library/fn/math/hypot" { + var hypot: typeof core.Math.hypot; + export = hypot; +} +declare module "core-js/library/fn/math/imul" { + var imul: typeof core.Math.imul; + export = imul; +} +declare module "core-js/library/fn/math/log10" { + var log10: typeof core.Math.log10; + export = log10; +} +declare module "core-js/library/fn/math/log1p" { + var log1p: typeof core.Math.log1p; + export = log1p; +} +declare module "core-js/library/fn/math/log2" { + var log2: typeof core.Math.log2; + export = log2; +} +declare module "core-js/library/fn/math/sign" { + var sign: typeof core.Math.sign; + export = sign; +} +declare module "core-js/library/fn/math/sinh" { + var sinh: typeof core.Math.sinh; + export = sinh; +} +declare module "core-js/library/fn/math/tanh" { + var tanh: typeof core.Math.tanh; + export = tanh; +} +declare module "core-js/library/fn/math/trunc" { + var trunc: typeof core.Math.trunc; + export = trunc; +} +declare module "core-js/library/fn/number" { + var Number: typeof core.Number; + export = Number; +} +declare module "core-js/library/fn/number/epsilon" { + var EPSILON: typeof core.Number.EPSILON; + export = EPSILON; +} +declare module "core-js/library/fn/number/is-finite" { + var isFinite: typeof core.Number.isFinite; + export = isFinite; +} +declare module "core-js/library/fn/number/is-integer" { + var isInteger: typeof core.Number.isInteger; + export = isInteger; +} +declare module "core-js/library/fn/number/is-nan" { + var isNaN: typeof core.Number.isNaN; + export = isNaN; +} +declare module "core-js/library/fn/number/is-safe-integer" { + var isSafeInteger: typeof core.Number.isSafeInteger; + export = isSafeInteger; +} +declare module "core-js/library/fn/number/max-safe-integer" { + var MAX_SAFE_INTEGER: typeof core.Number.MAX_SAFE_INTEGER; + export = MAX_SAFE_INTEGER; +} +declare module "core-js/library/fn/number/min-safe-integer" { + var MIN_SAFE_INTEGER: typeof core.Number.MIN_SAFE_INTEGER; + export = MIN_SAFE_INTEGER; +} +declare module "core-js/library/fn/number/parse-float" { + var parseFloat: typeof core.Number.parseFloat; + export = parseFloat; +} +declare module "core-js/library/fn/number/parse-int" { + var parseInt: typeof core.Number.parseInt; + export = parseInt; +} +declare module "core-js/library/fn/number/random" { + var random: typeof core.Number.random; + export = random; +} +declare module "core-js/library/fn/object" { + var Object: typeof core.Object; + export = Object; +} +declare module "core-js/library/fn/object/assign" { + var assign: typeof core.Object.assign; + export = assign; +} +declare module "core-js/library/fn/object/classof" { + var classof: typeof core.Object.classof; + export = classof; +} +declare module "core-js/library/fn/object/create" { + var create: typeof core.Object.create; + export = create; +} +declare module "core-js/library/fn/object/define" { + var define: typeof core.Object.define; + export = define; +} +declare module "core-js/library/fn/object/define-properties" { + var defineProperties: typeof core.Object.defineProperties; + export = defineProperties; +} +declare module "core-js/library/fn/object/define-property" { + var defineProperty: typeof core.Object.defineProperty; + export = defineProperty; +} +declare module "core-js/library/fn/object/entries" { + var entries: typeof core.Object.entries; + export = entries; +} +declare module "core-js/library/fn/object/freeze" { + var freeze: typeof core.Object.freeze; + export = freeze; +} +declare module "core-js/library/fn/object/get-own-property-descriptor" { + var getOwnPropertyDescriptor: typeof core.Object.getOwnPropertyDescriptor; + export = getOwnPropertyDescriptor; +} +declare module "core-js/library/fn/object/get-own-property-descriptors" { + var getOwnPropertyDescriptors: typeof core.Object.getOwnPropertyDescriptors; + export = getOwnPropertyDescriptors; +} +declare module "core-js/library/fn/object/get-own-property-names" { + var getOwnPropertyNames: typeof core.Object.getOwnPropertyNames; + export = getOwnPropertyNames; +} +declare module "core-js/library/fn/object/get-own-property-symbols" { + var getOwnPropertySymbols: typeof core.Object.getOwnPropertySymbols; + export = getOwnPropertySymbols; +} +declare module "core-js/library/fn/object/get-prototype-of" { + var getPrototypeOf: typeof core.Object.getPrototypeOf; + export = getPrototypeOf; +} +declare module "core-js/library/fn/object/is" { + var is: typeof core.Object.is; + export = is; +} +declare module "core-js/library/fn/object/is-extensible" { + var isExtensible: typeof core.Object.isExtensible; + export = isExtensible; +} +declare module "core-js/library/fn/object/is-frozen" { + var isFrozen: typeof core.Object.isFrozen; + export = isFrozen; +} +declare module "core-js/library/fn/object/is-object" { + var isObject: typeof core.Object.isObject; + export = isObject; +} +declare module "core-js/library/fn/object/is-sealed" { + var isSealed: typeof core.Object.isSealed; + export = isSealed; +} +declare module "core-js/library/fn/object/keys" { + var keys: typeof core.Object.keys; + export = keys; +} +declare module "core-js/library/fn/object/make" { + var make: typeof core.Object.make; + export = make; +} +declare module "core-js/library/fn/object/prevent-extensions" { + var preventExtensions: typeof core.Object.preventExtensions; + export = preventExtensions; +} +declare module "core-js/library/fn/object/seal" { + var seal: typeof core.Object.seal; + export = seal; +} +declare module "core-js/library/fn/object/set-prototype-of" { + var setPrototypeOf: typeof core.Object.setPrototypeOf; + export = setPrototypeOf; +} +declare module "core-js/library/fn/object/values" { + var values: typeof core.Object.values; + export = values; +} +declare module "core-js/library/fn/reflect" { + var Reflect: typeof core.Reflect; + export = Reflect; +} +declare module "core-js/library/fn/reflect/apply" { + var apply: typeof core.Reflect.apply; + export = apply; +} +declare module "core-js/library/fn/reflect/construct" { + var construct: typeof core.Reflect.construct; + export = construct; +} +declare module "core-js/library/fn/reflect/define-property" { + var defineProperty: typeof core.Reflect.defineProperty; + export = defineProperty; +} +declare module "core-js/library/fn/reflect/delete-property" { + var deleteProperty: typeof core.Reflect.deleteProperty; + export = deleteProperty; +} +declare module "core-js/library/fn/reflect/enumerate" { + var enumerate: typeof core.Reflect.enumerate; + export = enumerate; +} +declare module "core-js/library/fn/reflect/get" { + var get: typeof core.Reflect.get; + export = get; +} +declare module "core-js/library/fn/reflect/get-own-property-descriptor" { + var getOwnPropertyDescriptor: typeof core.Reflect.getOwnPropertyDescriptor; + export = getOwnPropertyDescriptor; +} +declare module "core-js/library/fn/reflect/get-prototype-of" { + var getPrototypeOf: typeof core.Reflect.getPrototypeOf; + export = getPrototypeOf; +} +declare module "core-js/library/fn/reflect/has" { + var has: typeof core.Reflect.has; + export = has; +} +declare module "core-js/library/fn/reflect/is-extensible" { + var isExtensible: typeof core.Reflect.isExtensible; + export = isExtensible; +} +declare module "core-js/library/fn/reflect/own-keys" { + var ownKeys: typeof core.Reflect.ownKeys; + export = ownKeys; +} +declare module "core-js/library/fn/reflect/prevent-extensions" { + var preventExtensions: typeof core.Reflect.preventExtensions; + export = preventExtensions; +} +declare module "core-js/library/fn/reflect/set" { + var set: typeof core.Reflect.set; + export = set; +} +declare module "core-js/library/fn/reflect/set-prototype-of" { + var setPrototypeOf: typeof core.Reflect.setPrototypeOf; + export = setPrototypeOf; +} +declare module "core-js/library/fn/regexp" { + var RegExp: typeof core.RegExp; + export = RegExp; +} +declare module "core-js/library/fn/regexp/escape" { + var escape: typeof core.RegExp.escape; + export = escape; +} +declare module "core-js/library/fn/string" { + var String: typeof core.String; + export = String; +} +declare module "core-js/library/fn/string/at" { + var at: typeof core.String.at; + export = at; +} +declare module "core-js/library/fn/string/code-point-at" { + var codePointAt: typeof core.String.codePointAt; + export = codePointAt; +} +declare module "core-js/library/fn/string/ends-with" { + var endsWith: typeof core.String.endsWith; + export = endsWith; +} +declare module "core-js/library/fn/string/escape-html" { + var escapeHTML: typeof core.String.escapeHTML; + export = escapeHTML; +} +declare module "core-js/library/fn/string/from-code-point" { + var fromCodePoint: typeof core.String.fromCodePoint; + export = fromCodePoint; +} +declare module "core-js/library/fn/string/includes" { + var includes: typeof core.String.includes; + export = includes; +} +declare module "core-js/library/fn/string/lpad" { + var lpad: typeof core.String.lpad; + export = lpad; +} +declare module "core-js/library/fn/string/raw" { + var raw: typeof core.String.raw; + export = raw; +} +declare module "core-js/library/fn/string/repeat" { + var repeat: typeof core.String.repeat; + export = repeat; +} +declare module "core-js/library/fn/string/rpad" { + var rpad: typeof core.String.rpad; + export = rpad; +} +declare module "core-js/library/fn/string/starts-with" { + var startsWith: typeof core.String.startsWith; + export = startsWith; +} +declare module "core-js/library/fn/string/unescape-html" { + var unescapeHTML: typeof core.String.unescapeHTML; + export = unescapeHTML; +} +declare module "core-js/library/fn/symbol" { + var Symbol: typeof core.Symbol; + export = Symbol; +} +declare module "core-js/library/fn/symbol/for" { + var _for: typeof core.Symbol.for; + export = _for; +} +declare module "core-js/library/fn/symbol/has-instance" { + var hasInstance: typeof core.Symbol.hasInstance; + export = hasInstance; +} +declare module "core-js/library/fn/symbol/is-concat-spreadable" { + var isConcatSpreadable: typeof core.Symbol.isConcatSpreadable; + export = isConcatSpreadable; +} +declare module "core-js/library/fn/symbol/iterator" { + var iterator: typeof core.Symbol.iterator; + export = iterator; +} +declare module "core-js/library/fn/symbol/key-for" { + var keyFor: typeof core.Symbol.keyFor; + export = keyFor; +} +declare module "core-js/library/fn/symbol/match" { + var match: typeof core.Symbol.match; + export = match; +} +declare module "core-js/library/fn/symbol/replace" { + var replace: typeof core.Symbol.replace; + export = replace; +} +declare module "core-js/library/fn/symbol/search" { + var search: typeof core.Symbol.search; + export = search; +} +declare module "core-js/library/fn/symbol/species" { + var species: typeof core.Symbol.species; + export = species; +} +declare module "core-js/library/fn/symbol/split" { + var split: typeof core.Symbol.split; + export = split; +} +declare module "core-js/library/fn/symbol/to-primitive" { + var toPrimitive: typeof core.Symbol.toPrimitive; + export = toPrimitive; +} +declare module "core-js/library/fn/symbol/to-string-tag" { + var toStringTag: typeof core.Symbol.toStringTag; + export = toStringTag; +} +declare module "core-js/library/fn/symbol/unscopables" { + var unscopables: typeof core.Symbol.unscopables; + export = unscopables; +} +declare module "core-js/library/es5" { + export = core; +} +declare module "core-js/library/es6" { + export = core; +} +declare module "core-js/library/es6/array" { + var Array: typeof core.Array; + export = Array; +} +declare module "core-js/library/es6/function" { + var Function: typeof core.Function; + export = Function; +} +declare module "core-js/library/es6/map" { + var Map: typeof core.Map; + export = Map; +} +declare module "core-js/library/es6/math" { + var Math: typeof core.Math; + export = Math; +} +declare module "core-js/library/es6/number" { + var Number: typeof core.Number; + export = Number; +} +declare module "core-js/library/es6/object" { + var Object: typeof core.Object; + export = Object; +} +declare module "core-js/library/es6/promise" { + var Promise: typeof core.Promise; + export = Promise; +} +declare module "core-js/library/es6/reflect" { + var Reflect: typeof core.Reflect; + export = Reflect; +} +declare module "core-js/library/es6/regexp" { + var RegExp: typeof core.RegExp; + export = RegExp; +} +declare module "core-js/library/es6/set" { + var Set: typeof core.Set; + export = Set; +} +declare module "core-js/library/es6/string" { + var String: typeof core.String; + export = String; +} +declare module "core-js/library/es6/symbol" { + var Symbol: typeof core.Symbol; + export = Symbol; +} +declare module "core-js/library/es6/weak-map" { + var WeakMap: typeof core.WeakMap; + export = WeakMap; +} +declare module "core-js/library/es6/weak-set" { + var WeakSet: typeof core.WeakSet; + export = WeakSet; +} +declare module "core-js/library/es7" { + export = core; +} +declare module "core-js/library/es7/array" { + var Array: typeof core.Array; + export = Array; +} +declare module "core-js/library/es7/map" { + var Map: typeof core.Map; + export = Map; +} +declare module "core-js/library/es7/object" { + var Object: typeof core.Object; + export = Object; +} +declare module "core-js/library/es7/regexp" { + var RegExp: typeof core.RegExp; + export = RegExp; +} +declare module "core-js/library/es7/set" { + var Set: typeof core.Set; + export = Set; +} +declare module "core-js/library/es7/string" { + var String: typeof core.String; + export = String; +} +declare module "core-js/library/js" { + export = core; +} +declare module "core-js/library/js/array" { + var Array: typeof core.Array; + export = Array; +} +declare module "core-js/library/web" { + export = core; +} +declare module "core-js/library/web/dom" { + export = core; +} +declare module "core-js/library/web/immediate" { + export = core; +} +declare module "core-js/library/web/timers" { + export = core; +} diff --git a/src/typings/globals/core-js/typings.json b/src/typings/globals/core-js/typings.json new file mode 100644 index 0000000000..8643d5650a --- /dev/null +++ b/src/typings/globals/core-js/typings.json @@ -0,0 +1,8 @@ +{ + "resolution": "main", + "tree": { + "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/3c56e866d6edd234416a92c3d35c15ccd7fe6b72/core-js/index.d.ts", + "raw": "registry:dt/core-js#0.9.7+20161130133742", + "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/3c56e866d6edd234416a92c3d35c15ccd7fe6b72/core-js/index.d.ts" + } +} diff --git a/src/typings/globals/jqueryui/index.d.ts b/src/typings/globals/jqueryui/index.d.ts new file mode 100644 index 0000000000..47a0d971c7 --- /dev/null +++ b/src/typings/globals/jqueryui/index.d.ts @@ -0,0 +1,1909 @@ +// Generated by typings +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/ead8376ca80553332af7872f9fe723c6fbb4e412/jqueryui/index.d.ts +declare namespace JQueryUI { + // Accordion ////////////////////////////////////////////////// + + interface AccordionOptions extends AccordionEvents { + active?: any; // boolean or number + animate?: any; // boolean, number, string or object + collapsible?: boolean; + disabled?: boolean; + event?: string; + header?: string; + heightStyle?: string; + icons?: any; + } + + interface AccordionUIParams { + newHeader: JQuery; + oldHeader: JQuery; + newPanel: JQuery; + oldPanel: JQuery; + } + + interface AccordionEvent { + (event: Event, ui: AccordionUIParams): void; + } + + interface AccordionEvents { + activate?: AccordionEvent; + beforeActivate?: AccordionEvent; + create?: AccordionEvent; + } + + interface Accordion extends Widget, AccordionOptions { + } + + + // Autocomplete ////////////////////////////////////////////////// + + interface AutocompleteOptions extends AutocompleteEvents { + appendTo?: any; //Selector; + autoFocus?: boolean; + delay?: number; + disabled?: boolean; + minLength?: number; + position?: any; // object + source?: any; // [], string or () + } + + interface AutocompleteUIParams { + /** + * The item selected from the menu, if any. Otherwise the property is null + */ + item?: any; + } + + interface AutocompleteEvent { + (event: Event, ui: AutocompleteUIParams): void; + } + + interface AutocompleteEvents { + change?: AutocompleteEvent; + close?: AutocompleteEvent; + create?: AutocompleteEvent; + focus?: AutocompleteEvent; + open?: AutocompleteEvent; + response?: AutocompleteEvent; + search?: AutocompleteEvent; + select?: AutocompleteEvent; + } + + interface Autocomplete extends Widget, AutocompleteOptions { + escapeRegex: (value: string) => string; + filter: (array: any, term: string) => any; + } + + + // Button ////////////////////////////////////////////////// + + interface ButtonOptions { + disabled?: boolean; + icons?: any; + label?: string; + text?: string|boolean; + click?: (event?: Event) => void; + } + + interface Button extends Widget, ButtonOptions { + } + + + // Datepicker ////////////////////////////////////////////////// + + interface DatepickerOptions { + /** + * An input element that is to be updated with the selected date from the datepicker. Use the altFormat option to change the format of the date within this field. Leave as blank for no alternate field. + */ + altField?: any; // Selector, jQuery or Element + /** + * The dateFormat to be used for the altField option. This allows one date format to be shown to the user for selection purposes, while a different format is actually sent behind the scenes. For a full list of the possible formats see the formatDate function + */ + altFormat?: string; + /** + * The text to display after each date field, e.g., to show the required format. + */ + appendText?: string; + /** + * Set to true to automatically resize the input field to accommodate dates in the current dateFormat. + */ + autoSize?: boolean; + /** + * A function that takes an input field and current datepicker instance and returns an options object to update the datepicker with. It is called just before the datepicker is displayed. + */ + beforeShow?: (input: Element, inst: any) => JQueryUI.DatepickerOptions; + /** + * A function that takes a date as a parameter and must return an array with: + * [0]: true/false indicating whether or not this date is selectable + * [1]: a CSS class name to add to the date's cell or "" for the default presentation + * [2]: an optional popup tooltip for this date + * The function is called for each day in the datepicker before it is displayed. + */ + beforeShowDay?: (date: Date) => any[]; + /** + * A URL of an image to use to display the datepicker when the showOn option is set to "button" or "both". If set, the buttonText option becomes the alt value and is not directly displayed. + */ + buttonImage?: string; + /** + * Whether the button image should be rendered by itself instead of inside a button element. This option is only relevant if the buttonImage option has also been set. + */ + buttonImageOnly?: boolean; + /** + * The text to display on the trigger button. Use in conjunction with the showOn option set to "button" or "both". + */ + buttonText?: string; + /** + * A function to calculate the week of the year for a given date. The default implementation uses the ISO 8601 definition: weeks start on a Monday; the first week of the year contains the first Thursday of the year. + */ + calculateWeek?: (date: Date) => string; + /** + * Whether the month should be rendered as a dropdown instead of text. + */ + changeMonth?: boolean; + /** + * Whether the year should be rendered as a dropdown instead of text. Use the yearRange option to control which years are made available for selection. + */ + changeYear?: boolean; + /** + * The text to display for the close link. Use the showButtonPanel option to display this button. + */ + closeText?: string; + /** + * When true, entry in the input field is constrained to those characters allowed by the current dateFormat option. + */ + constrainInput?: boolean; + /** + * The text to display for the current day link. Use the showButtonPanel option to display this button. + */ + currentText?: string; + /** + * The format for parsed and displayed dates. For a full list of the possible formats see the formatDate function. + */ + dateFormat?: string; + /** + * The list of long day names, starting from Sunday, for use as requested via the dateFormat option. + */ + dayNames?: string[]; + /** + * The list of minimised day names, starting from Sunday, for use as column headers within the datepicker. + */ + dayNamesMin?: string[]; + /** + * The list of abbreviated day names, starting from Sunday, for use as requested via the dateFormat option. + */ + dayNamesShort?: string[]; + /** + * Set the date to highlight on first opening if the field is blank. Specify either an actual date via a Date object or as a string in the current dateFormat, or a number of days from today (e.g. +7) or a string of values and periods ('y' for years, 'm' for months, 'w' for weeks, 'd' for days, e.g. '+1m +7d'), or null for today. + * Multiple types supported: + * Date: A date object containing the default date. + * Number: A number of days from today. For example 2 represents two days from today and -1 represents yesterday. + * String: A string in the format defined by the dateFormat option, or a relative date. Relative dates must contain value and period pairs; valid periods are "y" for years, "m" for months, "w" for weeks, and "d" for days. For example, "+1m +7d" represents one month and seven days from today. + */ + defaultDate?: any; // Date, number or string + /** + * Control the speed at which the datepicker appears, it may be a time in milliseconds or a string representing one of the three predefined speeds ("slow", "normal", "fast"). + */ + duration?: string; + /** + * Set the first day of the week: Sunday is 0, Monday is 1, etc. + */ + firstDay?: number; + /** + * When true, the current day link moves to the currently selected date instead of today. + */ + gotoCurrent?: boolean; + /** + * Normally the previous and next links are disabled when not applicable (see the minDate and maxDate options). You can hide them altogether by setting this attribute to true. + */ + hideIfNoPrevNext?: boolean; + /** + * Whether the current language is drawn from right to left. + */ + isRTL?: boolean; + /** + * The maximum selectable date. When set to null, there is no maximum. + * Multiple types supported: + * Date: A date object containing the maximum date. + * Number: A number of days from today. For example 2 represents two days from today and -1 represents yesterday. + * String: A string in the format defined by the dateFormat option, or a relative date. Relative dates must contain value and period pairs; valid periods are "y" for years, "m" for months, "w" for weeks, and "d" for days. For example, "+1m +7d" represents one month and seven days from today. + */ + maxDate?: any; // Date, number or string + /** + * The minimum selectable date. When set to null, there is no minimum. + * Multiple types supported: + * Date: A date object containing the minimum date. + * Number: A number of days from today. For example 2 represents two days from today and -1 represents yesterday. + * String: A string in the format defined by the dateFormat option, or a relative date. Relative dates must contain value and period pairs; valid periods are "y" for years, "m" for months, "w" for weeks, and "d" for days. For example, "+1m +7d" represents one month and seven days from today. + */ + minDate?: any; // Date, number or string + /** + * The list of full month names, for use as requested via the dateFormat option. + */ + monthNames?: string[]; + /** + * The list of abbreviated month names, as used in the month header on each datepicker and as requested via the dateFormat option. + */ + monthNamesShort?: string[]; + /** + * Whether the prevText and nextText options should be parsed as dates by the formatDate function, allowing them to display the target month names for example. + */ + navigationAsDateFormat?: boolean; + /** + * The text to display for the next month link. With the standard ThemeRoller styling, this value is replaced by an icon. + */ + nextText?: string; + /** + * The number of months to show at once. + * Multiple types supported: + * Number: The number of months to display in a single row. + * Array: An array defining the number of rows and columns to display. + */ + numberOfMonths?: any; // number or number[] + /** + * Called when the datepicker moves to a new month and/or year. The function receives the selected year, month (1-12), and the datepicker instance as parameters. this refers to the associated input field. + */ + onChangeMonthYear?: (year: number, month: number, inst: any) => void; + /** + * Called when the datepicker is closed, whether or not a date is selected. The function receives the selected date as text ("" if none) and the datepicker instance as parameters. this refers to the associated input field. + */ + onClose?: (dateText: string, inst: any) => void; + /** + * Called when the datepicker is selected. The function receives the selected date as text and the datepicker instance as parameters. this refers to the associated input field. + */ + onSelect?: (dateText: string, inst: any) => void; + /** + * The text to display for the previous month link. With the standard ThemeRoller styling, this value is replaced by an icon. + */ + prevText?: string; + /** + * Whether days in other months shown before or after the current month are selectable. This only applies if the showOtherMonths option is set to true. + */ + selectOtherMonths?: boolean; + /** + * The cutoff year for determining the century for a date (used in conjunction with dateFormat 'y'). Any dates entered with a year value less than or equal to the cutoff year are considered to be in the current century, while those greater than it are deemed to be in the previous century. + * Multiple types supported: + * Number: A value between 0 and 99 indicating the cutoff year. + * String: A relative number of years from the current year, e.g., "+3" or "-5". + */ + shortYearCutoff?: any; // number or string + /** + * The name of the animation used to show and hide the datepicker. Use "show" (the default), "slideDown", "fadeIn", any of the jQuery UI effects. Set to an empty string to disable animation. + */ + showAnim?: string; + /** + * Whether to display a button pane underneath the calendar. The button pane contains two buttons, a Today button that links to the current day, and a Done button that closes the datepicker. The buttons' text can be customized using the currentText and closeText options respectively. + */ + showButtonPanel?: boolean; + /** + * When displaying multiple months via the numberOfMonths option, the showCurrentAtPos option defines which position to display the current month in. + */ + showCurrentAtPos?: number; + /** + * Whether to show the month after the year in the header. + */ + showMonthAfterYear?: boolean; + /** + * When the datepicker should appear. The datepicker can appear when the field receives focus ("focus"), when a button is clicked ("button"), or when either event occurs ("both"). + */ + showOn?: string; + /** + * If using one of the jQuery UI effects for the showAnim option, you can provide additional settings for that animation via this option. + */ + showOptions?: any; // TODO + /** + * Whether to display dates in other months (non-selectable) at the start or end of the current month. To make these days selectable use the selectOtherMonths option. + */ + showOtherMonths?: boolean; + /** + * When true, a column is added to show the week of the year. The calculateWeek option determines how the week of the year is calculated. You may also want to change the firstDay option. + */ + showWeek?: boolean; + /** + * Set how many months to move when clicking the previous/next links. + */ + stepMonths?: number; + /** + * The text to display for the week of the year column heading. Use the showWeek option to display this column. + */ + weekHeader?: string; + /** + * The range of years displayed in the year drop-down: either relative to today's year ("-nn:+nn"), relative to the currently selected year ("c-nn:c+nn"), absolute ("nnnn:nnnn"), or combinations of these formats ("nnnn:-nn"). Note that this option only affects what appears in the drop-down, to restrict which dates may be selected use the minDate and/or maxDate options. + */ + yearRange?: string; + /** + * Additional text to display after the year in the month headers. + */ + yearSuffix?: string; + /** + * Set to true to automatically hide the datepicker. + */ + autohide?: boolean; + /** + * Set to date to automatically enddate the datepicker. + */ + endDate?: Date; + } + + interface DatepickerFormatDateOptions { + dayNamesShort?: string[]; + dayNames?: string[]; + monthNamesShort?: string[]; + monthNames?: string[]; + } + + interface Datepicker extends Widget, DatepickerOptions { + regional: { [languageCod3: string]: any; }; + setDefaults(defaults: DatepickerOptions): void; + formatDate(format: string, date: Date, settings?: DatepickerFormatDateOptions): string; + parseDate(format: string, date: string, settings?: DatepickerFormatDateOptions): Date; + iso8601Week(date: Date): number; + noWeekends(date: Date): any[]; + } + + + // Dialog ////////////////////////////////////////////////// + + interface DialogOptions extends DialogEvents { + autoOpen?: boolean; + buttons?: { [buttonText: string]: (event?: Event) => void } | DialogButtonOptions[]; + closeOnEscape?: boolean; + closeText?: string; + appendTo?: string; + dialogClass?: string; + disabled?: boolean; + draggable?: boolean; + height?: number | string; + hide?: boolean | number | string | DialogShowHideOptions; + maxHeight?: number; + maxWidth?: number; + minHeight?: number; + minWidth?: number; + modal?: boolean; + position?: any; // object, string or [] + resizable?: boolean; + show?: boolean | number | string | DialogShowHideOptions; + stack?: boolean; + title?: string; + width?: any; // number or string + zIndex?: number; + + open?: DialogEvent; + close?: DialogEvent; + } + + interface DialogButtonOptions { + icons?: any; + showText?: string | boolean; + text?: string; + click?: (eventObject: JQueryEventObject) => any; + [attr: string]: any; // attributes for the +

+
...write your results into #results...
+ + + + diff --git a/src/vs/base/test/node/extfs/fixtures/site.css b/src/vs/base/test/node/extfs/fixtures/site.css new file mode 100644 index 0000000000..c5cea74684 --- /dev/null +++ b/src/vs/base/test/node/extfs/fixtures/site.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------- +The base color for this template is #5c87b2. If you'd like +to use a different color start by replacing all instances of +#5c87b2 with your new color. +----------------------------------------------------------*/ +body +{ + background-color: #5c87b2; + font-size: .75em; + font-family: Segoe UI, Verdana, Helvetica, Sans-Serif; + margin: 8px; + padding: 0; + color: #696969; +} + +h1, h2, h3, h4, h5, h6 +{ + color: #000; + font-size: 40px; + margin: 0px; +} + +textarea +{ + font-family: Consolas +} + +#results +{ + margin-top: 2em; + margin-left: 2em; + color: black; + font-size: medium; +} + diff --git a/src/vs/base/test/node/flow.test.ts b/src/vs/base/test/node/flow.test.ts new file mode 100644 index 0000000000..09b3db375c --- /dev/null +++ b/src/vs/base/test/node/flow.test.ts @@ -0,0 +1,490 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import flow = require('vs/base/node/flow'); + +const loop = flow.loop; +const sequence = flow.sequence; +const parallel = flow.parallel; + +suite('Flow', () => { + function assertCounterEquals(counter, expected): void { + assert.ok(counter === expected, 'Expected ' + expected + ' assertions, but got ' + counter); + } + + function syncThrowsError(callback): void { + callback(new Error('foo'), null); + } + + function syncSequenceGetThrowsError(value, callback) { + sequence( + function onError(error) { + callback(error, null); + }, + + function getFirst() { + syncThrowsError(this); + }, + + function handleFirst(first) { + //Foo + } + ); + } + + function syncGet(value, callback): void { + callback(null, value); + } + + function syncGetError(value, callback): void { + callback(new Error(''), null); + } + + function asyncGet(value, callback): void { + process.nextTick(function () { + callback(null, value); + }); + } + + function asyncGetError(value, callback): void { + process.nextTick(function () { + callback(new Error(''), null); + }); + } + + test('loopSync', function (done: () => void) { + const elements = ['1', '2', '3']; + loop(elements, function (element, callback, index, total) { + assert.ok(index === 0 || index === 1 || index === 2); + assert.deepEqual(3, total); + callback(null, element); + }, function (error, result) { + assert.equal(error, null); + assert.deepEqual(result, elements); + + done(); + }); + }); + + test('loopByFunctionSync', function (done: () => void) { + const elements = function (callback) { + callback(null, ['1', '2', '3']); + }; + + loop(elements, function (element, callback) { + callback(null, element); + }, function (error, result) { + assert.equal(error, null); + assert.deepEqual(result, ['1', '2', '3']); + + done(); + }); + }); + + test('loopByFunctionAsync', function (done: () => void) { + const elements = function (callback) { + process.nextTick(function () { + callback(null, ['1', '2', '3']); + }); + }; + + loop(elements, function (element, callback) { + callback(null, element); + }, function (error, result) { + assert.equal(error, null); + assert.deepEqual(result, ['1', '2', '3']); + + done(); + }); + }); + + test('loopSyncErrorByThrow', function (done: () => void) { + const elements = ['1', '2', '3']; + loop(elements, function (element, callback) { + if (element === '2') { + throw new Error('foo'); + } else { + callback(null, element); + } + }, function (error, result) { + assert.ok(error); + assert.ok(!result); + + done(); + }); + }); + + test('loopSyncErrorByCallback', function (done: () => void) { + const elements = ['1', '2', '3']; + loop(elements, function (element, callback) { + if (element === '2') { + callback(new Error('foo'), null); + } else { + callback(null, element); + } + }, function (error, result) { + assert.ok(error); + assert.ok(!result); + + done(); + }); + }); + + test('loopAsync', function (done: () => void) { + const elements = ['1', '2', '3']; + loop(elements, function (element, callback) { + process.nextTick(function () { + callback(null, element); + }); + }, function (error, result) { + assert.equal(error, null); + assert.deepEqual(result, elements); + + done(); + }); + }); + + test('loopAsyncErrorByCallback', function (done: () => void) { + const elements = ['1', '2', '3']; + loop(elements, function (element, callback) { + process.nextTick(function () { + if (element === '2') { + callback(new Error('foo'), null); + } else { + callback(null, element); + } + }); + }, function (error, result) { + assert.ok(error); + assert.ok(!result); + + done(); + }); + }); + + test('sequenceSync', function (done: () => void) { + let assertionCount = 0; + let errorCount = 0; + + sequence( + function onError(error) { + errorCount++; + }, + + function getFirst() { + syncGet('1', this); + }, + + function handleFirst(first) { + assert.deepEqual('1', first); + assertionCount++; + syncGet('2', this); + }, + + function handleSecond(second) { + assert.deepEqual('2', second); + assertionCount++; + syncGet(null, this); + }, + + function handleThird(third) { + assert.ok(!third); + assertionCount++; + + assertCounterEquals(assertionCount, 3); + assertCounterEquals(errorCount, 0); + done(); + } + ); + }); + + test('sequenceAsync', function (done: () => void) { + let assertionCount = 0; + let errorCount = 0; + + sequence( + function onError(error) { + errorCount++; + }, + + function getFirst() { + asyncGet('1', this); + }, + + function handleFirst(first) { + assert.deepEqual('1', first); + assertionCount++; + asyncGet('2', this); + }, + + function handleSecond(second) { + assert.deepEqual('2', second); + assertionCount++; + asyncGet(null, this); + }, + + function handleThird(third) { + assert.ok(!third); + assertionCount++; + + assertCounterEquals(assertionCount, 3); + assertCounterEquals(errorCount, 0); + done(); + } + ); + }); + + test('sequenceSyncErrorByThrow', function (done: () => void) { + let assertionCount = 0; + let errorCount = 0; + + sequence( + function onError(error) { + errorCount++; + + assertCounterEquals(assertionCount, 1); + assertCounterEquals(errorCount, 1); + done(); + }, + + function getFirst() { + syncGet('1', this); + }, + + function handleFirst(first) { + assert.deepEqual('1', first); + assertionCount++; + syncGet('2', this); + }, + + function handleSecond(second) { + if (true) { + throw new Error(''); + } + // assertionCount++; + // syncGet(null, this); + }, + + function handleThird(third) { + throw new Error('We should not be here'); + } + ); + }); + + test('sequenceSyncErrorByCallback', function (done: () => void) { + let assertionCount = 0; + let errorCount = 0; + + sequence( + function onError(error) { + errorCount++; + + assertCounterEquals(assertionCount, 1); + assertCounterEquals(errorCount, 1); + done(); + }, + + function getFirst() { + syncGet('1', this); + }, + + function handleFirst(first) { + assert.deepEqual('1', first); + assertionCount++; + syncGetError('2', this); + }, + + function handleSecond(second) { + throw new Error('We should not be here'); + } + ); + }); + + test('sequenceAsyncErrorByThrow', function (done: () => void) { + let assertionCount = 0; + let errorCount = 0; + + sequence( + function onError(error) { + errorCount++; + + assertCounterEquals(assertionCount, 1); + assertCounterEquals(errorCount, 1); + done(); + }, + + function getFirst() { + asyncGet('1', this); + }, + + function handleFirst(first) { + assert.deepEqual('1', first); + assertionCount++; + asyncGet('2', this); + }, + + function handleSecond(second) { + if (true) { + throw new Error(''); + } + // assertionCount++; + // asyncGet(null, this); + }, + + function handleThird(third) { + throw new Error('We should not be here'); + } + ); + }); + + test('sequenceAsyncErrorByCallback', function (done: () => void) { + let assertionCount = 0; + let errorCount = 0; + + sequence( + function onError(error) { + errorCount++; + + assertCounterEquals(assertionCount, 1); + assertCounterEquals(errorCount, 1); + done(); + }, + + function getFirst() { + asyncGet('1', this); + }, + + function handleFirst(first) { + assert.deepEqual('1', first); + assertionCount++; + asyncGetError('2', this); + }, + + function handleSecond(second) { + throw new Error('We should not be here'); + } + ); + }); + + test('syncChainedSequenceError', function (done: () => void) { + sequence( + function onError(error) { + done(); + }, + + function getFirst() { + syncSequenceGetThrowsError('1', this); + } + ); + }); + + test('tolerateBooleanResults', function (done: () => void) { + let assertionCount = 0; + let errorCount = 0; + + sequence( + function onError(error) { + errorCount++; + }, + + function getFirst() { + this(true); + }, + + function getSecond(result) { + assert.equal(result, true); + this(false); + }, + + function last(result) { + assert.equal(result, false); + assertionCount++; + + assertCounterEquals(assertionCount, 1); + assertCounterEquals(errorCount, 0); + done(); + } + ); + }); + + test('loopTolerateBooleanResults', function (done: () => void) { + let elements = ['1', '2', '3']; + loop(elements, function (element, callback) { + process.nextTick(function () { + (callback)(true); + }); + }, function (error, result) { + assert.equal(error, null); + assert.deepEqual(result, [true, true, true]); + + done(); + }); + }); + + test('parallel', function (done: () => void) { + let elements = [1, 2, 3, 4, 5]; + let sum = 0; + + parallel(elements, function (element, callback) { + sum += element; + callback(null, element * element); + }, function (errors, result) { + assert.ok(!errors); + + assert.deepEqual(sum, 15); + assert.deepEqual(result, [1, 4, 9, 16, 25]); + + done(); + }); + }); + + test('parallel - setTimeout', function (done: () => void) { + let elements = [1, 2, 3, 4, 5]; + let timeouts = [10, 30, 5, 0, 4]; + let sum = 0; + + parallel(elements, function (element, callback) { + setTimeout(function () { + sum += element; + callback(null, element * element); + }, timeouts.pop()); + }, function (errors, result) { + assert.ok(!errors); + + assert.deepEqual(sum, 15); + assert.deepEqual(result, [1, 4, 9, 16, 25]); + + done(); + }); + }); + + test('parallel - with error', function (done: () => void) { + const elements = [1, 2, 3, 4, 5]; + const timeouts = [10, 30, 5, 0, 4]; + let sum = 0; + + parallel(elements, function (element, callback) { + setTimeout(function () { + if (element === 4) { + callback(new Error('error!'), null); + } else { + sum += element; + callback(null, element * element); + } + }, timeouts.pop()); + }, function (errors, result) { + assert.ok(errors); + assert.deepEqual(errors, [null, null, null, new Error('error!'), null]); + + assert.deepEqual(sum, 11); + assert.deepEqual(result, [1, 4, 9, null, 25]); + + done(); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts new file mode 100644 index 0000000000..04c08c3b08 --- /dev/null +++ b/src/vs/base/test/node/glob.test.ts @@ -0,0 +1,887 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import * as path from 'path'; +import glob = require('vs/base/common/glob'); + +suite('Glob', () => { + + // test('perf', function () { + + // let patterns = [ + // '{**/*.cs,**/*.json,**/*.csproj,**/*.sln}', + // '{**/*.cs,**/*.csproj,**/*.sln}', + // '{**/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.es6,**/*.mjs}', + // '**/*.go', + // '{**/*.ps,**/*.ps1}', + // '{**/*.c,**/*.cpp,**/*.h}', + // '{**/*.fsx,**/*.fsi,**/*.fs,**/*.ml,**/*.mli}', + // '{**/*.js,**/*.jsx,**/*.es6,**/*.mjs}', + // '{**/*.ts,**/*.tsx}', + // '{**/*.php}', + // '{**/*.php}', + // '{**/*.php}', + // '{**/*.php}', + // '{**/*.py}', + // '{**/*.py}', + // '{**/*.py}', + // '{**/*.rs,**/*.rslib}', + // '{**/*.cpp,**/*.cc,**/*.h}', + // '{**/*.md}', + // '{**/*.md}', + // '{**/*.md}' + // ]; + + // let paths = [ + // '/DNXConsoleApp/Program.cs', + // 'C:\\DNXConsoleApp\\foo\\Program.cs', + // 'test/qunit', + // 'test/test.txt', + // 'test/node_modules', + // '.hidden.txt', + // '/node_module/test/foo.js' + // ]; + + // let results = 0; + // let c = 1000; + // console.profile('glob.match'); + // while (c-- > 0) { + // for (let path of paths) { + // for (let pattern of patterns) { + // let r = glob.match(pattern, path); + // if (r) { + // results += 42; + // } + // } + // } + // } + // console.profileEnd(); + // }); + + test('simple', function () { + let p = 'node_modules'; + + assert(glob.match(p, 'node_modules')); + assert(!glob.match(p, 'node_module')); + assert(!glob.match(p, '/node_modules')); + assert(!glob.match(p, 'test/node_modules')); + + p = 'test.txt'; + assert(glob.match(p, 'test.txt')); + assert(!glob.match(p, 'test?txt')); + assert(!glob.match(p, '/text.txt')); + assert(!glob.match(p, 'test/test.txt')); + + p = 'test(.txt'; + assert(glob.match(p, 'test(.txt')); + assert(!glob.match(p, 'test?txt')); + + p = 'qunit'; + + assert(glob.match(p, 'qunit')); + assert(!glob.match(p, 'qunit.css')); + assert(!glob.match(p, 'test/qunit')); + + // Absolute + + p = '/DNXConsoleApp/**/*.cs'; + assert(glob.match(p, '/DNXConsoleApp/Program.cs')); + assert(glob.match(p, '/DNXConsoleApp/foo/Program.cs')); + + p = 'C:/DNXConsoleApp/**/*.cs'; + assert(glob.match(p, 'C:\\DNXConsoleApp\\Program.cs')); + assert(glob.match(p, 'C:\\DNXConsoleApp\\foo\\Program.cs')); + }); + + test('dot hidden', function () { + let p = '.*'; + + assert(glob.match(p, '.git')); + assert(glob.match(p, '.hidden.txt')); + assert(!glob.match(p, 'git')); + assert(!glob.match(p, 'hidden.txt')); + assert(!glob.match(p, 'path/.git')); + assert(!glob.match(p, 'path/.hidden.txt')); + + p = '**/.*'; + assert(glob.match(p, '.git')); + assert(glob.match(p, '.hidden.txt')); + assert(!glob.match(p, 'git')); + assert(!glob.match(p, 'hidden.txt')); + assert(glob.match(p, 'path/.git')); + assert(glob.match(p, 'path/.hidden.txt')); + assert(!glob.match(p, 'path/git')); + assert(!glob.match(p, 'pat.h/hidden.txt')); + + p = '._*'; + + assert(glob.match(p, '._git')); + assert(glob.match(p, '._hidden.txt')); + assert(!glob.match(p, 'git')); + assert(!glob.match(p, 'hidden.txt')); + assert(!glob.match(p, 'path/._git')); + assert(!glob.match(p, 'path/._hidden.txt')); + + p = '**/._*'; + assert(glob.match(p, '._git')); + assert(glob.match(p, '._hidden.txt')); + assert(!glob.match(p, 'git')); + assert(!glob.match(p, 'hidden._txt')); + assert(glob.match(p, 'path/._git')); + assert(glob.match(p, 'path/._hidden.txt')); + assert(!glob.match(p, 'path/git')); + assert(!glob.match(p, 'pat.h/hidden._txt')); + }); + + test('file pattern', function () { + let p = '*.js'; + + assert(glob.match(p, 'foo.js')); + assert(!glob.match(p, 'folder/foo.js')); + assert(!glob.match(p, '/node_modules/foo.js')); + assert(!glob.match(p, 'foo.jss')); + assert(!glob.match(p, 'some.js/test')); + + p = 'html.*'; + assert(glob.match(p, 'html.js')); + assert(glob.match(p, 'html.txt')); + assert(!glob.match(p, 'htm.txt')); + + p = '*.*'; + assert(glob.match(p, 'html.js')); + assert(glob.match(p, 'html.txt')); + assert(glob.match(p, 'htm.txt')); + assert(!glob.match(p, 'folder/foo.js')); + assert(!glob.match(p, '/node_modules/foo.js')); + + p = 'node_modules/test/*.js'; + assert(glob.match(p, 'node_modules/test/foo.js')); + assert(!glob.match(p, 'folder/foo.js')); + assert(!glob.match(p, '/node_module/test/foo.js')); + assert(!glob.match(p, 'foo.jss')); + assert(!glob.match(p, 'some.js/test')); + }); + + test('star', function () { + let p = 'node*modules'; + + assert(glob.match(p, 'node_modules')); + assert(glob.match(p, 'node_super_modules')); + assert(!glob.match(p, 'node_module')); + assert(!glob.match(p, '/node_modules')); + assert(!glob.match(p, 'test/node_modules')); + + p = '*'; + assert(glob.match(p, 'html.js')); + assert(glob.match(p, 'html.txt')); + assert(glob.match(p, 'htm.txt')); + assert(!glob.match(p, 'folder/foo.js')); + assert(!glob.match(p, '/node_modules/foo.js')); + }); + + test('questionmark', function () { + let p = 'node?modules'; + + assert(glob.match(p, 'node_modules')); + assert(!glob.match(p, 'node_super_modules')); + assert(!glob.match(p, 'node_module')); + assert(!glob.match(p, '/node_modules')); + assert(!glob.match(p, 'test/node_modules')); + + p = '?'; + assert(glob.match(p, 'h')); + assert(!glob.match(p, 'html.txt')); + assert(!glob.match(p, 'htm.txt')); + assert(!glob.match(p, 'folder/foo.js')); + assert(!glob.match(p, '/node_modules/foo.js')); + }); + + test('globstar', function () { + let p = '**/*.js'; + + assert(glob.match(p, 'foo.js')); + assert(glob.match(p, 'folder/foo.js')); + assert(glob.match(p, '/node_modules/foo.js')); + assert(!glob.match(p, 'foo.jss')); + assert(!glob.match(p, 'some.js/test')); + assert(!glob.match(p, '/some.js/test')); + assert(!glob.match(p, '\\some.js\\test')); + + p = '**/project.json'; + + assert(glob.match(p, 'project.json')); + assert(glob.match(p, '/project.json')); + assert(glob.match(p, 'some/folder/project.json')); + assert(!glob.match(p, 'some/folder/file_project.json')); + assert(!glob.match(p, 'some/folder/fileproject.json')); + // assert(!glob.match(p, '/rrproject.json')); TODO@ben this still fails if T1-3 are disabled + assert(!glob.match(p, 'some/rrproject.json')); + // assert(!glob.match(p, 'rrproject.json')); + // assert(!glob.match(p, '\\rrproject.json')); + assert(!glob.match(p, 'some\\rrproject.json')); + + p = 'test/**'; + assert(glob.match(p, 'test')); + assert(glob.match(p, 'test/foo.js')); + assert(glob.match(p, 'test/other/foo.js')); + assert(!glob.match(p, 'est/other/foo.js')); + + p = '**'; + assert(glob.match(p, 'foo.js')); + assert(glob.match(p, 'folder/foo.js')); + assert(glob.match(p, '/node_modules/foo.js')); + assert(glob.match(p, 'foo.jss')); + assert(glob.match(p, 'some.js/test')); + + p = 'test/**/*.js'; + assert(glob.match(p, 'test/foo.js')); + assert(glob.match(p, 'test/other/foo.js')); + assert(glob.match(p, 'test/other/more/foo.js')); + assert(!glob.match(p, 'test/foo.ts')); + assert(!glob.match(p, 'test/other/foo.ts')); + assert(!glob.match(p, 'test/other/more/foo.ts')); + + p = '**/**/*.js'; + + assert(glob.match(p, 'foo.js')); + assert(glob.match(p, 'folder/foo.js')); + assert(glob.match(p, '/node_modules/foo.js')); + assert(!glob.match(p, 'foo.jss')); + assert(!glob.match(p, 'some.js/test')); + + p = '**/node_modules/**/*.js'; + + assert(!glob.match(p, 'foo.js')); + assert(!glob.match(p, 'folder/foo.js')); + assert(glob.match(p, 'node_modules/foo.js')); + assert(glob.match(p, 'node_modules/some/folder/foo.js')); + assert(!glob.match(p, 'node_modules/some/folder/foo.ts')); + assert(!glob.match(p, 'foo.jss')); + assert(!glob.match(p, 'some.js/test')); + + p = '{**/node_modules/**,**/.git/**,**/bower_components/**}'; + + assert(glob.match(p, 'node_modules')); + assert(glob.match(p, '/node_modules')); + assert(glob.match(p, '/node_modules/more')); + assert(glob.match(p, 'some/test/node_modules')); + assert(glob.match(p, 'some\\test\\node_modules')); + assert(glob.match(p, 'C:\\\\some\\test\\node_modules')); + assert(glob.match(p, 'C:\\\\some\\test\\node_modules\\more')); + + assert(glob.match(p, 'bower_components')); + assert(glob.match(p, 'bower_components/more')); + assert(glob.match(p, '/bower_components')); + assert(glob.match(p, 'some/test/bower_components')); + assert(glob.match(p, 'some\\test\\bower_components')); + assert(glob.match(p, 'C:\\\\some\\test\\bower_components')); + assert(glob.match(p, 'C:\\\\some\\test\\bower_components\\more')); + + assert(glob.match(p, '.git')); + assert(glob.match(p, '/.git')); + assert(glob.match(p, 'some/test/.git')); + assert(glob.match(p, 'some\\test\\.git')); + assert(glob.match(p, 'C:\\\\some\\test\\.git')); + + assert(!glob.match(p, 'tempting')); + assert(!glob.match(p, '/tempting')); + assert(!glob.match(p, 'some/test/tempting')); + assert(!glob.match(p, 'some\\test\\tempting')); + assert(!glob.match(p, 'C:\\\\some\\test\\tempting')); + + p = '{**/package.json,**/project.json}'; + assert(glob.match(p, 'package.json')); + assert(glob.match(p, '/package.json')); + assert(!glob.match(p, 'xpackage.json')); + assert(!glob.match(p, '/xpackage.json')); + }); + + test('brace expansion', function () { + let p = '*.{html,js}'; + + assert(glob.match(p, 'foo.js')); + assert(glob.match(p, 'foo.html')); + assert(!glob.match(p, 'folder/foo.js')); + assert(!glob.match(p, '/node_modules/foo.js')); + assert(!glob.match(p, 'foo.jss')); + assert(!glob.match(p, 'some.js/test')); + + p = '*.{html}'; + + assert(glob.match(p, 'foo.html')); + assert(!glob.match(p, 'foo.js')); + assert(!glob.match(p, 'folder/foo.js')); + assert(!glob.match(p, '/node_modules/foo.js')); + assert(!glob.match(p, 'foo.jss')); + assert(!glob.match(p, 'some.js/test')); + + p = '{node_modules,testing}'; + assert(glob.match(p, 'node_modules')); + assert(glob.match(p, 'testing')); + assert(!glob.match(p, 'node_module')); + assert(!glob.match(p, 'dtesting')); + + p = '**/{foo,bar}'; + assert(glob.match(p, 'foo')); + assert(glob.match(p, 'bar')); + assert(glob.match(p, 'test/foo')); + assert(glob.match(p, 'test/bar')); + assert(glob.match(p, 'other/more/foo')); + assert(glob.match(p, 'other/more/bar')); + + p = '{foo,bar}/**'; + assert(glob.match(p, 'foo')); + assert(glob.match(p, 'bar')); + assert(glob.match(p, 'foo/test')); + assert(glob.match(p, 'bar/test')); + assert(glob.match(p, 'foo/other/more')); + assert(glob.match(p, 'bar/other/more')); + + p = '{**/*.d.ts,**/*.js}'; + + assert(glob.match(p, 'foo.js')); + assert(glob.match(p, 'testing/foo.js')); + assert(glob.match(p, 'testing\\foo.js')); + assert(glob.match(p, '/testing/foo.js')); + assert(glob.match(p, '\\testing\\foo.js')); + assert(glob.match(p, 'C:\\testing\\foo.js')); + + assert(glob.match(p, 'foo.d.ts')); + assert(glob.match(p, 'testing/foo.d.ts')); + assert(glob.match(p, 'testing\\foo.d.ts')); + assert(glob.match(p, '/testing/foo.d.ts')); + assert(glob.match(p, '\\testing\\foo.d.ts')); + assert(glob.match(p, 'C:\\testing\\foo.d.ts')); + + assert(!glob.match(p, 'foo.d')); + assert(!glob.match(p, 'testing/foo.d')); + assert(!glob.match(p, 'testing\\foo.d')); + assert(!glob.match(p, '/testing/foo.d')); + assert(!glob.match(p, '\\testing\\foo.d')); + assert(!glob.match(p, 'C:\\testing\\foo.d')); + + p = '{**/*.d.ts,**/*.js,path/simple.jgs}'; + + assert(glob.match(p, 'foo.js')); + assert(glob.match(p, 'testing/foo.js')); + assert(glob.match(p, 'testing\\foo.js')); + assert(glob.match(p, '/testing/foo.js')); + assert(glob.match(p, 'path/simple.jgs')); + assert(!glob.match(p, '/path/simple.jgs')); + assert(glob.match(p, '\\testing\\foo.js')); + assert(glob.match(p, 'C:\\testing\\foo.js')); + + p = '{**/*.d.ts,**/*.js,foo.[0-9]}'; + + assert(glob.match(p, 'foo.5')); + assert(glob.match(p, 'foo.8')); + assert(!glob.match(p, 'bar.5')); + assert(!glob.match(p, 'foo.f')); + assert(glob.match(p, 'foo.js')); + + p = 'prefix/{**/*.d.ts,**/*.js,foo.[0-9]}'; + + assert(glob.match(p, 'prefix/foo.5')); + assert(glob.match(p, 'prefix/foo.8')); + assert(!glob.match(p, 'prefix/bar.5')); + assert(!glob.match(p, 'prefix/foo.f')); + assert(glob.match(p, 'prefix/foo.js')); + }); + + test('expression support (single)', function () { + let siblings = ['test.html', 'test.txt', 'test.ts', 'test.js']; + + // { "**/*.js": { "when": "$(basename).ts" } } + let expression: glob.IExpression = { + '**/*.js': { + when: '$(basename).ts' + } + }; + + assert.strictEqual('**/*.js', glob.match(expression, 'test.js', () => siblings)); + assert.strictEqual(glob.match(expression, 'test.js', () => []), null); + assert.strictEqual(glob.match(expression, 'test.js', () => ['te.ts']), null); + assert.strictEqual(glob.match(expression, 'test.js'), null); + + expression = { + '**/*.js': { + when: '' + } + }; + + assert.strictEqual(glob.match(expression, 'test.js', () => siblings), null); + + expression = { + '**/*.js': { + } + }; + + assert.strictEqual('**/*.js', glob.match(expression, 'test.js', () => siblings)); + + expression = {}; + + assert.strictEqual(glob.match(expression, 'test.js', () => siblings), null); + }); + + test('expression support (multiple)', function () { + let siblings = ['test.html', 'test.txt', 'test.ts', 'test.js']; + + // { "**/*.js": { "when": "$(basename).ts" } } + let expression: glob.IExpression = { + '**/*.js': { when: '$(basename).ts' }, + '**/*.as': true, + '**/*.foo': false, + '**/*.bananas': { bananas: true } + }; + + assert.strictEqual('**/*.js', glob.match(expression, 'test.js', () => siblings)); + assert.strictEqual('**/*.as', glob.match(expression, 'test.as', () => siblings)); + assert.strictEqual('**/*.bananas', glob.match(expression, 'test.bananas', () => siblings)); + assert.strictEqual('**/*.bananas', glob.match(expression, 'test.bananas')); + assert.strictEqual(glob.match(expression, 'test.foo', () => siblings), null); + }); + + test('brackets', function () { + let p = 'foo.[0-9]'; + + assert(glob.match(p, 'foo.5')); + assert(glob.match(p, 'foo.8')); + assert(!glob.match(p, 'bar.5')); + assert(!glob.match(p, 'foo.f')); + + p = 'foo.[^0-9]'; + + assert(!glob.match(p, 'foo.5')); + assert(!glob.match(p, 'foo.8')); + assert(!glob.match(p, 'bar.5')); + assert(glob.match(p, 'foo.f')); + }); + + test('full path', function () { + let p = 'testing/this/foo.txt'; + + assert(glob.match(p, nativeSep('testing/this/foo.txt'))); + }); + + test('prefix agnostic', function () { + let p = '**/*.js'; + + assert(glob.match(p, 'foo.js')); + assert(glob.match(p, '/foo.js')); + assert(glob.match(p, '\\foo.js')); + assert(glob.match(p, 'testing/foo.js')); + assert(glob.match(p, 'testing\\foo.js')); + assert(glob.match(p, '/testing/foo.js')); + assert(glob.match(p, '\\testing\\foo.js')); + assert(glob.match(p, 'C:\\testing\\foo.js')); + + assert(!glob.match(p, 'foo.ts')); + assert(!glob.match(p, 'testing/foo.ts')); + assert(!glob.match(p, 'testing\\foo.ts')); + assert(!glob.match(p, '/testing/foo.ts')); + assert(!glob.match(p, '\\testing\\foo.ts')); + assert(!glob.match(p, 'C:\\testing\\foo.ts')); + + assert(!glob.match(p, 'foo.js.txt')); + assert(!glob.match(p, 'testing/foo.js.txt')); + assert(!glob.match(p, 'testing\\foo.js.txt')); + assert(!glob.match(p, '/testing/foo.js.txt')); + assert(!glob.match(p, '\\testing\\foo.js.txt')); + assert(!glob.match(p, 'C:\\testing\\foo.js.txt')); + + assert(!glob.match(p, 'testing.js/foo')); + assert(!glob.match(p, 'testing.js\\foo')); + assert(!glob.match(p, '/testing.js/foo')); + assert(!glob.match(p, '\\testing.js\\foo')); + assert(!glob.match(p, 'C:\\testing.js\\foo')); + + p = '**/foo.js'; + + assert(glob.match(p, 'foo.js')); + assert(glob.match(p, '/foo.js')); + assert(glob.match(p, '\\foo.js')); + assert(glob.match(p, 'testing/foo.js')); + assert(glob.match(p, 'testing\\foo.js')); + assert(glob.match(p, '/testing/foo.js')); + assert(glob.match(p, '\\testing\\foo.js')); + assert(glob.match(p, 'C:\\testing\\foo.js')); + }); + + test('cached properly', function () { + let p = '**/*.js'; + + assert(glob.match(p, 'foo.js')); + assert(glob.match(p, 'testing/foo.js')); + assert(glob.match(p, 'testing\\foo.js')); + assert(glob.match(p, '/testing/foo.js')); + assert(glob.match(p, '\\testing\\foo.js')); + assert(glob.match(p, 'C:\\testing\\foo.js')); + + assert(!glob.match(p, 'foo.ts')); + assert(!glob.match(p, 'testing/foo.ts')); + assert(!glob.match(p, 'testing\\foo.ts')); + assert(!glob.match(p, '/testing/foo.ts')); + assert(!glob.match(p, '\\testing\\foo.ts')); + assert(!glob.match(p, 'C:\\testing\\foo.ts')); + + assert(!glob.match(p, 'foo.js.txt')); + assert(!glob.match(p, 'testing/foo.js.txt')); + assert(!glob.match(p, 'testing\\foo.js.txt')); + assert(!glob.match(p, '/testing/foo.js.txt')); + assert(!glob.match(p, '\\testing\\foo.js.txt')); + assert(!glob.match(p, 'C:\\testing\\foo.js.txt')); + + assert(!glob.match(p, 'testing.js/foo')); + assert(!glob.match(p, 'testing.js\\foo')); + assert(!glob.match(p, '/testing.js/foo')); + assert(!glob.match(p, '\\testing.js\\foo')); + assert(!glob.match(p, 'C:\\testing.js\\foo')); + + // Run again and make sure the regex are properly reused + + assert(glob.match(p, 'foo.js')); + assert(glob.match(p, 'testing/foo.js')); + assert(glob.match(p, 'testing\\foo.js')); + assert(glob.match(p, '/testing/foo.js')); + assert(glob.match(p, '\\testing\\foo.js')); + assert(glob.match(p, 'C:\\testing\\foo.js')); + + assert(!glob.match(p, 'foo.ts')); + assert(!glob.match(p, 'testing/foo.ts')); + assert(!glob.match(p, 'testing\\foo.ts')); + assert(!glob.match(p, '/testing/foo.ts')); + assert(!glob.match(p, '\\testing\\foo.ts')); + assert(!glob.match(p, 'C:\\testing\\foo.ts')); + + assert(!glob.match(p, 'foo.js.txt')); + assert(!glob.match(p, 'testing/foo.js.txt')); + assert(!glob.match(p, 'testing\\foo.js.txt')); + assert(!glob.match(p, '/testing/foo.js.txt')); + assert(!glob.match(p, '\\testing\\foo.js.txt')); + assert(!glob.match(p, 'C:\\testing\\foo.js.txt')); + + assert(!glob.match(p, 'testing.js/foo')); + assert(!glob.match(p, 'testing.js\\foo')); + assert(!glob.match(p, '/testing.js/foo')); + assert(!glob.match(p, '\\testing.js\\foo')); + assert(!glob.match(p, 'C:\\testing.js\\foo')); + }); + + test('invalid glob', function () { + let p = '**/*(.js'; + + assert(!glob.match(p, 'foo.js')); + }); + + test('split glob aware', function () { + assert.deepEqual(glob.splitGlobAware('foo,bar', ','), ['foo', 'bar']); + assert.deepEqual(glob.splitGlobAware('foo', ','), ['foo']); + assert.deepEqual(glob.splitGlobAware('{foo,bar}', ','), ['{foo,bar}']); + assert.deepEqual(glob.splitGlobAware('foo,bar,{foo,bar}', ','), ['foo', 'bar', '{foo,bar}']); + assert.deepEqual(glob.splitGlobAware('{foo,bar},foo,bar,{foo,bar}', ','), ['{foo,bar}', 'foo', 'bar', '{foo,bar}']); + + assert.deepEqual(glob.splitGlobAware('[foo,bar]', ','), ['[foo,bar]']); + assert.deepEqual(glob.splitGlobAware('foo,bar,[foo,bar]', ','), ['foo', 'bar', '[foo,bar]']); + assert.deepEqual(glob.splitGlobAware('[foo,bar],foo,bar,[foo,bar]', ','), ['[foo,bar]', 'foo', 'bar', '[foo,bar]']); + }); + + test('expression with disabled glob', function () { + let expr = { '**/*.js': false }; + + assert.strictEqual(glob.match(expr, 'foo.js'), null); + }); + + test('expression with two non-trivia globs', function () { + let expr = { + '**/*.j?': true, + '**/*.t?': true + }; + + assert.strictEqual(glob.match(expr, 'foo.js'), '**/*.j?'); + assert.strictEqual(glob.match(expr, 'foo.as'), null); + }); + + test('expression with empty glob', function () { + let expr = { '': true }; + + assert.strictEqual(glob.match(expr, 'foo.js'), null); + }); + + test('expression with other falsy value', function () { + let expr = { '**/*.js': 0 }; + + assert.strictEqual(glob.match(expr, 'foo.js'), '**/*.js'); + }); + + test('expression with two basename globs', function () { + let expr = { + '**/bar': true, + '**/baz': true + }; + + assert.strictEqual(glob.match(expr, 'bar'), '**/bar'); + assert.strictEqual(glob.match(expr, 'foo'), null); + assert.strictEqual(glob.match(expr, 'foo/bar'), '**/bar'); + assert.strictEqual(glob.match(expr, 'foo\\bar'), '**/bar'); + assert.strictEqual(glob.match(expr, 'foo/foo'), null); + }); + + test('expression with two basename globs and a siblings expression', function () { + let expr = { + '**/bar': true, + '**/baz': true, + '**/*.js': { when: '$(basename).ts' } + }; + + let sibilings = () => ['foo.ts', 'foo.js', 'foo', 'bar']; + + assert.strictEqual(glob.match(expr, 'bar', sibilings), '**/bar'); + assert.strictEqual(glob.match(expr, 'foo', sibilings), null); + assert.strictEqual(glob.match(expr, 'foo/bar', sibilings), '**/bar'); + assert.strictEqual(glob.match(expr, 'foo\\bar', sibilings), '**/bar'); + assert.strictEqual(glob.match(expr, 'foo/foo', sibilings), null); + assert.strictEqual(glob.match(expr, 'foo.js', sibilings), '**/*.js'); + assert.strictEqual(glob.match(expr, 'bar.js', sibilings), null); + }); + + test('expression with multipe basename globs', function () { + let expr = { + '**/bar': true, + '{**/baz,**/foo}': true + }; + + assert.strictEqual(glob.match(expr, 'bar'), '**/bar'); + assert.strictEqual(glob.match(expr, 'foo'), '{**/baz,**/foo}'); + assert.strictEqual(glob.match(expr, 'baz'), '{**/baz,**/foo}'); + assert.strictEqual(glob.match(expr, 'abc'), null); + }); + + test('falsy expression/pattern', function () { + assert.strictEqual(glob.match(null, 'foo'), false); + assert.strictEqual(glob.match('', 'foo'), false); + assert.strictEqual(glob.parse(null)('foo'), false); + assert.strictEqual(glob.parse('')('foo'), false); + }); + + test('falsy path', function () { + assert.strictEqual(glob.parse('foo')(null), false); + assert.strictEqual(glob.parse('foo')(''), false); + assert.strictEqual(glob.parse('**/*.j?')(null), false); + assert.strictEqual(glob.parse('**/*.j?')(''), false); + assert.strictEqual(glob.parse('**/*.foo')(null), false); + assert.strictEqual(glob.parse('**/*.foo')(''), false); + assert.strictEqual(glob.parse('**/foo')(null), false); + assert.strictEqual(glob.parse('**/foo')(''), false); + assert.strictEqual(glob.parse('{**/baz,**/foo}')(null), false); + assert.strictEqual(glob.parse('{**/baz,**/foo}')(''), false); + assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(null), false); + assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(''), false); + }); + + test('expression/pattern basename', function () { + assert.strictEqual(glob.parse('**/foo')('bar/baz', 'baz'), false); + assert.strictEqual(glob.parse('**/foo')('bar/foo', 'foo'), true); + + assert.strictEqual(glob.parse('{**/baz,**/foo}')('baz/bar', 'bar'), false); + assert.strictEqual(glob.parse('{**/baz,**/foo}')('baz/foo', 'foo'), true); + + let expr = { '**/*.js': { when: '$(basename).ts' } }; + let sibilings = () => ['foo.ts', 'foo.js']; + + assert.strictEqual(glob.parse(expr)('bar/baz.js', 'baz.js', sibilings), null); + assert.strictEqual(glob.parse(expr)('bar/foo.js', 'foo.js', sibilings), '**/*.js'); + }); + + test('expression/pattern basename terms', function () { + assert.deepStrictEqual(glob.getBasenameTerms(glob.parse('**/*.foo')), []); + assert.deepStrictEqual(glob.getBasenameTerms(glob.parse('**/foo')), ['foo']); + assert.deepStrictEqual(glob.getBasenameTerms(glob.parse('**/foo/')), ['foo']); + assert.deepStrictEqual(glob.getBasenameTerms(glob.parse('{**/baz,**/foo}')), ['baz', 'foo']); + assert.deepStrictEqual(glob.getBasenameTerms(glob.parse('{**/baz/,**/foo/}')), ['baz', 'foo']); + + assert.deepStrictEqual(glob.getBasenameTerms(glob.parse({ + '**/foo': true, + '{**/bar,**/baz}': true, + '{**/bar2/,**/baz2/}': true, + '**/bulb': false + })), ['foo', 'bar', 'baz', 'bar2', 'baz2']); + assert.deepStrictEqual(glob.getBasenameTerms(glob.parse({ + '**/foo': { when: '$(basename).zip' }, + '**/bar': true + })), ['bar']); + }); + + test('expression/pattern optimization for basenames', function () { + assert.deepStrictEqual(glob.getBasenameTerms(glob.parse('**/foo/**')), []); + assert.deepStrictEqual(glob.getBasenameTerms(glob.parse('**/foo/**', { trimForExclusions: true })), ['foo']); + + testOptimizationForBasenames('**/*.foo/**', [], [['baz/bar.foo/bar/baz', true]]); + testOptimizationForBasenames('**/foo/**', ['foo'], [['bar/foo', true], ['bar/foo/baz', false]]); + testOptimizationForBasenames('{**/baz/**,**/foo/**}', ['baz', 'foo'], [['bar/baz', true], ['bar/foo', true]]); + + testOptimizationForBasenames({ + '**/foo/**': true, + '{**/bar/**,**/baz/**}': true, + '**/bulb/**': false + }, ['foo', 'bar', 'baz'], [ + ['bar/foo', '**/foo/**'], + ['foo/bar', '{**/bar/**,**/baz/**}'], + ['bar/nope', null] + ]); + + const siblingsFn = () => ['baz', 'baz.zip', 'nope']; + testOptimizationForBasenames({ + '**/foo/**': { when: '$(basename).zip' }, + '**/bar/**': true + }, ['bar'], [ + ['bar/foo', null], + ['bar/foo/baz', null], + ['bar/foo/nope', null], + ['foo/bar', '**/bar/**'], + ], [ + null, + siblingsFn, + siblingsFn + ]); + }); + + function testOptimizationForBasenames(pattern: string | glob.IExpression, basenameTerms: string[], matches: [string, string | boolean][], siblingsFns: (() => string[])[] = []) { + const parsed = glob.parse(pattern, { trimForExclusions: true }); + assert.deepStrictEqual(glob.getBasenameTerms(parsed), basenameTerms); + matches.forEach(([text, result], i) => { + assert.strictEqual(parsed(text, null, siblingsFns[i]), result); + }); + } + + test('trailing slash', function () { + // Testing existing (more or less intuitive) behavior + assert.strictEqual(glob.parse('**/foo/')('bar/baz', 'baz'), false); + assert.strictEqual(glob.parse('**/foo/')('bar/foo', 'foo'), true); + assert.strictEqual(glob.parse('**/*.foo/')('bar/file.baz', 'file.baz'), false); + assert.strictEqual(glob.parse('**/*.foo/')('bar/file.foo', 'file.foo'), true); + assert.strictEqual(glob.parse('{**/foo/,**/abc/}')('bar/baz', 'baz'), false); + assert.strictEqual(glob.parse('{**/foo/,**/abc/}')('bar/foo', 'foo'), true); + assert.strictEqual(glob.parse('{**/foo/,**/abc/}')('bar/abc', 'abc'), true); + assert.strictEqual(glob.parse('{**/foo/,**/abc/}', { trimForExclusions: true })('bar/baz', 'baz'), false); + assert.strictEqual(glob.parse('{**/foo/,**/abc/}', { trimForExclusions: true })('bar/foo', 'foo'), true); + assert.strictEqual(glob.parse('{**/foo/,**/abc/}', { trimForExclusions: true })('bar/abc', 'abc'), true); + }); + + test('expression/pattern path', function () { + assert.strictEqual(glob.parse('**/foo/bar')(nativeSep('foo/baz'), 'baz'), false); + assert.strictEqual(glob.parse('**/foo/bar')(nativeSep('foo/bar'), 'bar'), true); + assert.strictEqual(glob.parse('**/foo/bar')(nativeSep('bar/foo/bar'), 'bar'), true); + assert.strictEqual(glob.parse('**/foo/bar/**')(nativeSep('bar/foo/bar'), 'bar'), true); + assert.strictEqual(glob.parse('**/foo/bar/**')(nativeSep('bar/foo/bar/baz'), 'baz'), true); + assert.strictEqual(glob.parse('**/foo/bar/**', { trimForExclusions: true })(nativeSep('bar/foo/bar'), 'bar'), true); + assert.strictEqual(glob.parse('**/foo/bar/**', { trimForExclusions: true })(nativeSep('bar/foo/bar/baz'), 'baz'), false); + + assert.strictEqual(glob.parse('foo/bar')(nativeSep('foo/baz'), 'baz'), false); + assert.strictEqual(glob.parse('foo/bar')(nativeSep('foo/bar'), 'bar'), true); + assert.strictEqual(glob.parse('foo/bar/baz')(nativeSep('foo/bar/baz'), 'baz'), true); // #15424 + assert.strictEqual(glob.parse('foo/bar')(nativeSep('bar/foo/bar'), 'bar'), false); + assert.strictEqual(glob.parse('foo/bar/**')(nativeSep('foo/bar/baz'), 'baz'), true); + assert.strictEqual(glob.parse('foo/bar/**', { trimForExclusions: true })(nativeSep('foo/bar'), 'bar'), true); + assert.strictEqual(glob.parse('foo/bar/**', { trimForExclusions: true })(nativeSep('foo/bar/baz'), 'baz'), false); + }); + + test('expression/pattern paths', function () { + assert.deepStrictEqual(glob.getPathTerms(glob.parse('**/*.foo')), []); + assert.deepStrictEqual(glob.getPathTerms(glob.parse('**/foo')), []); + assert.deepStrictEqual(glob.getPathTerms(glob.parse('**/foo/bar')), ['*/foo/bar']); + assert.deepStrictEqual(glob.getPathTerms(glob.parse('**/foo/bar/')), ['*/foo/bar']); + // Not supported + // assert.deepStrictEqual(glob.getPathTerms(glob.parse('{**/baz/bar,**/foo/bar,**/bar}')), ['*/baz/bar', '*/foo/bar']); + // assert.deepStrictEqual(glob.getPathTerms(glob.parse('{**/baz/bar/,**/foo/bar/,**/bar/}')), ['*/baz/bar', '*/foo/bar']); + + const parsed = glob.parse({ + '**/foo/bar': true, + '**/foo2/bar2': true, + // Not supported + // '{**/bar/foo,**/baz/foo}': true, + // '{**/bar2/foo/,**/baz2/foo/}': true, + '**/bulb': true, + '**/bulb2': true, + '**/bulb/foo': false + }); + assert.deepStrictEqual(glob.getPathTerms(parsed), ['*/foo/bar', '*/foo2/bar2']); + assert.deepStrictEqual(glob.getBasenameTerms(parsed), ['bulb', 'bulb2']); + assert.deepStrictEqual(glob.getPathTerms(glob.parse({ + '**/foo/bar': { when: '$(basename).zip' }, + '**/bar/foo': true, + '**/bar2/foo2': true + })), ['*/bar/foo', '*/bar2/foo2']); + }); + + test('expression/pattern optimization for paths', function () { + assert.deepStrictEqual(glob.getPathTerms(glob.parse('**/foo/bar/**')), []); + assert.deepStrictEqual(glob.getPathTerms(glob.parse('**/foo/bar/**', { trimForExclusions: true })), ['*/foo/bar']); + + testOptimizationForPaths('**/*.foo/bar/**', [], [[nativeSep('baz/bar.foo/bar/baz'), true]]); + testOptimizationForPaths('**/foo/bar/**', ['*/foo/bar'], [[nativeSep('bar/foo/bar'), true], [nativeSep('bar/foo/bar/baz'), false]]); + // Not supported + // testOptimizationForPaths('{**/baz/bar/**,**/foo/bar/**}', ['*/baz/bar', '*/foo/bar'], [[nativeSep('bar/baz/bar'), true], [nativeSep('bar/foo/bar'), true]]); + + testOptimizationForPaths({ + '**/foo/bar/**': true, + // Not supported + // '{**/bar/bar/**,**/baz/bar/**}': true, + '**/bulb/bar/**': false + }, ['*/foo/bar'], [ + [nativeSep('bar/foo/bar'), '**/foo/bar/**'], + // Not supported + // [nativeSep('foo/bar/bar'), '{**/bar/bar/**,**/baz/bar/**}'], + [nativeSep('/foo/bar/nope'), null] + ]); + + const siblingsFn = () => ['baz', 'baz.zip', 'nope']; + testOptimizationForPaths({ + '**/foo/123/**': { when: '$(basename).zip' }, + '**/bar/123/**': true + }, ['*/bar/123'], [ + [nativeSep('bar/foo/123'), null], + [nativeSep('bar/foo/123/baz'), null], + [nativeSep('bar/foo/123/nope'), null], + [nativeSep('foo/bar/123'), '**/bar/123/**'], + ], [ + null, + siblingsFn, + siblingsFn + ]); + }); + + function testOptimizationForPaths(pattern: string | glob.IExpression, pathTerms: string[], matches: [string, string | boolean][], siblingsFns: (() => string[])[] = []) { + const parsed = glob.parse(pattern, { trimForExclusions: true }); + assert.deepStrictEqual(glob.getPathTerms(parsed), pathTerms); + matches.forEach(([text, result], i) => { + assert.strictEqual(parsed(text, null, siblingsFns[i]), result); + }); + } + + function nativeSep(slashPath: string): string { + return slashPath.replace(/\//g, path.sep); + } + + test('mergeExpressions', () => { + // Empty => empty + assert.deepEqual(glob.mergeExpressions(), glob.getEmptyExpression()); + + // Doesn't modify given expressions + const expr1 = { 'a': true }; + glob.mergeExpressions(expr1, { 'b': true }); + assert.deepEqual(expr1, { 'a': true }); + + // Merges correctly + assert.deepEqual(glob.mergeExpressions({ 'a': true }, { 'b': true }), { 'a': true, 'b': true }); + + // Ignores null/undefined portions + assert.deepEqual(glob.mergeExpressions(undefined, { 'a': true }, null, { 'b': true }), { 'a': true, 'b': true }); + + // Later expressions take precedence + assert.deepEqual(glob.mergeExpressions({ 'a': true, 'b': false, 'c': true }, { 'a': false, 'b': true }), { 'a': false, 'b': true, 'c': true }); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/node/mime/fixtures/some.cp1252.txt b/src/vs/base/test/node/mime/fixtures/some.cp1252.txt new file mode 100644 index 0000000000..f40494d17e --- /dev/null +++ b/src/vs/base/test/node/mime/fixtures/some.cp1252.txt @@ -0,0 +1,23 @@ +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Data; +using System.Data.OleDb; +using System.Data.Odbc; +using System.IO; +using System.Net.Mail; +using System.Text.RegularExpressions; +using System.DirectoryServices; +using System.Diagnostics; +using System.Resources; +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization.Formatters.Binary; +using System.Runtime.Serialization; + + +ObjectCount = LoadObjects("ffentlicher Ordner"); + +Private = "Persnliche Information" \ No newline at end of file diff --git a/src/vs/base/test/node/mime/fixtures/some.css.qwoff b/src/vs/base/test/node/mime/fixtures/some.css.qwoff new file mode 100644 index 0000000000..cbd1fe228a --- /dev/null +++ b/src/vs/base/test/node/mime/fixtures/some.css.qwoff @@ -0,0 +1,35 @@ +/*---------------------------------------------------------- +The base color for this template is #5c87b2. If you'd like +to use a different color start by replacing all instances of +#5c87b2 with your new color. +----------------------------------------------------------*/ +body +{ + background-color: #5c87b2; + font-size: .75em; + font-family: Segoe UI, Verdana, Helvetica, Sans-Serif; + margin: 8px; + padding: 0; + color: #696969; +} + +h1, h2, h3, h4, h5, h6 +{ + color: #000; + font-size: 40px; + margin: 0px; +} + +textarea +{ + font-family: Consolas +} + +#results +{ + margin-top: 2em; + margin-left: 2em; + color: black; + font-size: medium; +} + diff --git a/src/vs/base/test/node/mime/fixtures/some.json.png b/src/vs/base/test/node/mime/fixtures/some.json.png new file mode 100644 index 0000000000..995a3273ae --- /dev/null +++ b/src/vs/base/test/node/mime/fixtures/some.json.png @@ -0,0 +1,11 @@ +{ + "type": "typescript", + + "sources": [ + "examples/company.ts", + "examples/conway.ts", + "examples/employee.ts", + "examples/large.ts", + "examples/small.ts" + ] +} \ No newline at end of file diff --git a/src/vs/base/test/node/mime/fixtures/some.pdf b/src/vs/base/test/node/mime/fixtures/some.pdf new file mode 100644 index 0000000000000000000000000000000000000000..657feb97a5a273de2959a55df65c0c35b9d5c94e GIT binary patch literal 35327 zcmdSBcRZEhA2*JakQ7B_mX$bWku7CqWgLfNlVr|_(^ZJd~^G|fU@9Vy<&-H$P_I0y7dLRSi<>Dt{`8v5eIiIzb_Wf%k z$!$g+Mq6VG5>Zh`ZbefYGsmZlcYsT3jNCHjR*t6jz)ve9N7DzUCbo}F8O6m(931UU zjjTyr@i{ckYTVS|xqbS&I=}j9()&#Q=^Fla{-UQ3e0tI+di)~0zS8=qE2iC|q*NE9 zbWk{bijgpzU+@RNAU~d^QpaT1R@)s7yJn&U>a%!(zb|ExnA$x4H#qPcI45u-;Lp2! z;NQUC`2J(I8g6!`jNFfm%y9qOo7y-s^8cN=nyG`Wlf8+l1Eav-3-@hp9D%<(FoLTE zmZoI-*xX3U)|F9*2e@!Y=nmuE+kAQ?!0P_J2Yl{WDv#`KP1H>t8Fhe{J&lnEa=8u7`xqU~Fkz3Bx-0Z0%<6VA!;5ER)?Cos9Eh2HWcLGFl-`2|3 zUfs^f#FSC!@5taifN;1UnF2Dgb96GYV&qm=GIFqF=&eQ4PDPNbcTsbR&LNo1CxSFpmEHG8Rpr?V(U0aQk&5 z70O}*kJR|p(v!I1Y!7rp{*QhEN88b*dzA#2OY0v=_z0Y9%rq}?CY}A@Br5v*YanHc z+YSEOh^rTOsx_TkhsU>{ZK+&hxFJ^DJ?b`Hw!ARexlt-Hdaz{RyZQnUMo zztd}RrP~o3v-iUtPAZVu|MZj=D?Wu5mG73!_COfI^z9E@X+&WD^UbZ1t@u8~x!svb z39r$t+O5jVXKeEFX4;-mF#7smyz)6`877@jQAA&{&?65Qn5`Id|<#E_$bV4 z{}5(L0_Flze3TQl`+@=i@IPq+4aJpAA4_52}I%% zhu3idzw6BuaubK+5nm#Hlq=0?#SD(AclJ?k<#!VU@MSkC`ooa5JMFgK^E#tf52php zJb5N+V_K{XlM+4O(|=EAg1NTjO~bc4+~m3krn!|r8NfxyvG}TDcehtM6JgyhqdVV5njJ$&+7FI(%fP24Jy%b zUBOVNR>cK$Y z#Ao=S=aR#bZp-eN@IB1j?8`}SLcRBy-NYXz8~HZ|xUj#tkz8hm9KcRV5x>dMj6i)X z8X(%v70(G$#{Br&lsRuZ&(@Fn)14~bTX4r;bve)-e%=d4uAzZW!Q*&-JMCv`E+B(o z7M@6CLsWWi?RIc&+w$XP8y}6y&T5ZX?dVl4j)dxp-Jv^E=eR>$5IKl4#5{&}V)w^x z&f>~cOB?WXHxu}D(5gPXcNR5jTfOmuv$OB$@WaZ-UqcuE(UJehjRB&U9LAV z!udA&y;=EQn_C8B&hzVsSO*zq9%0{=o?FImWiNc8&&HDpz-{l73rX>-`k07d81IR-B1}Je;8>z-f%;>o55+JMdZ2 zq9CNH3VgIWl4>Wz(o3JLI)SdTF)yILF#^XEIsjL?|Oy4gc6Dp^h zxX@l@(U*$oMPzctTW`UNF8^7pJz6`OVmne%&83jUc)ZP&2O~}%$32}PuO-CCA?2-b(h1d#a<&1+lO9}0rUuZZD!Tuj? zj9}a_D7Jfb+85OpD|<@!$0ah!iLF*r&rO61V6Xc;Qv|imYhp>=xk#)sH9v*=BFA4A@70Dxq!p`oF2xu)_n zpis#SdEXK~Q2~c|tdTr85s=Ppaj+8Rz2Bw4U45`QG(XK~_GyBWNPM-cl{+r#tiI=B zgPExHDk$WLX@k@59=yL~SpHHzjEyKHAS62hp&r!3F zZr4l5XH~Y0&xe>w9CtJ?aAf`*x9`xl2B`TX-#H?isH zqG3zSB67s=%sEhD3*f(2(2G=cvWQZl$!$~CU#R8wmKy6G^=|i)8Uwf*HMB$DJ7If+ zO^#sbf}Q3|jobqnm~FA)=5^28cbuFBI>l-3t#UalX@XXMG}h5Mkv8>jU7)WdQW zo+Yre?Ru90v}soNL9}5uX)smoeggsEgN&$1QxP|DV;D=T({ICJ_65=i3dVsh}o zH-ES9lQL-d$jO&vL&E}?aQG7dmyE4B6%3Qu|Mfz}^LLc@3bnU(m2k$h|LNh^mszGj zL-QKxQaJzLGonlH?K7xt(I0Ne8{&+64opkV&=W@+K)~oui6isN3sfSf#>ao11s29! zK!$H7Er+u+w3z{(!^?z3lQrnC~^P zvM)H1!7VQPPZ1&99!Fm}_=D|r7Q57UkLc66q$D^RM!wL&1MIUr1-OISA2grMQrB*C z7b7Oi7Cw0$?d2cU=O3wV>XjAGzk7XN*qKKuPuw(3ykc^9kS_ILuPnuTvyo`^*{P!m z@10ot*UntiOnR$RJ|#Z^Le;o@{9vyO44(T}arB@T=dhzyYkB)S9WIB++m-%$Wk}w% zsE_a>QP)3`B&!~988Mpq*(iylaf#jC_m>d06R|O7N3*2zahviOox_D?1U|Z`I2pGv zMlL6Ljd#fKTpy1uu1O`19i~)+_D=KwdUY&8f`>7+HK8>izX&;5wcuodNl}!^Rr%VH z@O-EENWMDDqFytaZGSmHoe6ZZK+t19A}?U3I`dmBk7^KWRK91(xi29~0%Ukmt{Wd> z_1*Ut%Mj<;QmpjHvZyz_x7iFl_HPr-cUz(Iewau3+FIqlY$Ijnf#ZNAEr`-Ps`uXZ z*sH3*0KVz{dzUf#y1G?+OO^AihRNo?yVU1pCH4l)j*7>!;gvZ}@B6d?{Ts*l4{GL@ zDqGa&*E+qY%p~?2bZieK=?z>~=J2t?f9es*S>3xMWgfX3OXxdUu(Bh~IWFr{3IJ52 zaO^62m|GNmOSs+pa4w%KgYhW37d3W$Q9X+TWwlAT>V4YK6m7cG@3U|vL*=vG@?5 zM63ZbWAx<$_sxk~$EK&O@@+X5l}|Q7OyE#yoy1Li?gsR7`Vy%#xFJSP=ID_Hzw)PY z^k80!BN3QZXd<-m?XfcDs@-4Y-Wj4Hb4@T86z%$YS-mu!6An5>8r>IRH+uTSoyHGR_w;X7gE3X6 ziT;NI@zq_Vibu1VwS@3Oi;ys|hYujJ(!y-M&d*_;EZV2uYJZfr)~3N2<_{K;$%W-J zU>(X>f+PSTQR2)cZUgCqvNq;YdIo6?n#6qX4?Bh`5YSwN#A;ijrctm4{2fJJ_tBIG zQ1*3kC$r`KypS|h6T}^ZQ6=S@3MyK{EQ!Kc0r~b5;ZTPIjX0jQSR;vi>1q!itVzqV zr9DN`%hGDzt&j;Aj2@?DaC`4DR(~C**-3GPq?vsdI|l1*bxy)- zSy&}z&U;!UGpi~GfpGOY4ZPJDdrSDBU1V9@eVzZ=)K=|L#oNC92t!o#(M`Y|pppd5 zEi8LO-g(NsQZ1Kso4SDocUD+FTZSQWYY%&~Qg)hR50?$N#~Cgo0G@y% z=|aq3$XCndz&>1A_5R4rBM8daLOyp^EN6p(Tq^^)JnC)wz4rCSs;;-wcf4~qfPLiv zr$&M91=P3q`g?{}2H1qfO4-+m_I}fSwo||1LPD19i~CEdhbgp7HzgQ$R}nx&*f)BR z>hOO5aH8z!S08!Jb_5MW6YOXr^#JqO0pD_UjRKhVJKVH2bG4%r5A}VJ#fZL-8=nz} z0Z04ykA}ifG~AQqht%F0)@S!;IF1%L%2hA9kCTgUSQF2PVqyr+Z{!Dz15(Pt?X?sJ zg+QP%CJJnwQ<~;)s1i6fM$veU1avf;P9#kTl-GxbI zfbhE>fwdJWR>tzW;h*&@asXl(Kxy7;Nis?~V7z92$&H+&7RIvEy{C;mg@RjF;t#z> z+-SvTQi?DKkWk73ZV1J#iZDA8wZz{4UGUI8D2D2HYde zpc=hw+QHMw9B8iw+`?=}_HQM1d;gvR1)kn^bt;}!tpi7mV==n!;V6{=Diruzp?cBo z2QAv*TtQ;Xe|(^UXUl-IEm6mdH9KA#sGx|WUz3b2;tYW~v{-0uU{LjrLKUC1=DEt~ z{=WI4Q2CxrafjftXO4}C$E1;s=dSH?N|J%D-cJ;l=dX}ab6Ix51NspO@4O1H`QJaf zGYb!Nx<{&9Z{#NYD!E!Q+U5KtkWen|!Ldx#k>76&M)K&qpJEZKIa-No{CJ~&+NL&o zkY#n9xy%fIYE#38@9CfJZ(ALS=YpCup1V42+-0#6?&Uq8T5JRS?K+abE-Wxfj^5ix zQ;&<+Iw&(ORfh*QjRyquTgT@IDoUjCC=DVO%Gwxs5RwEYl*fDehHN@~NBU_N+v=?v zFTK?LCn+Xy7i4%jRcn{lWU~lyR@ShXZ%;!;JE=`Yi4Ms@%>DFV{meY4pf~c5RgPbZ z+C_asz$RX1BiVnHQn!{CO2pqm?8WZF4qjRTk;F$FCvQ5uatj@!&vZ#!?S5|+AG3vY zny*83zIDpSG{9?+ikZVyI!qy0m8og9P)CSD^XPSHmkzBc~V_1CrMn;#I-;-~iw%`w+_9IHt|jK;mxtmy+T`tto3*FPGoc`qmd6tb<)SW#1F*Y(-9H)p>CyMX3LKSILeRetX*eEuDnC+2zu`NmKD=1?1e1 z5a!8`OU0gbWwTu=?%$JM{P~$|I}?9)!*|S?Cq~|o71~37qpF?TQ4#z1&|%Box?A)O zdKhQs)%CASDpQ-x^{g+>1pn;l)OYJo4wctQ4#6^1j2iRBE%m8&ur;>KY}}?Ft#t58 z34tLnkM!kM!nygWf|+@kpr!Nwt{TvYYDc}$#Uvi{Ci7cgaj>k)9n0ynxjzR3^AmPq zgQI#Ds5!eIvx}{*`vtpG$%*DOteG7VD9f-+Y@rsTuQmjfwxIP_QJ1GR@*~n3o{C&)uq6_+jIQil5!+SUcq=I1g!au=;y z5a{Y&fsb?*=9b8o;k7;O4H46#VfT@RqA8cp>sMWi{coGB*;2$&Lr8RcH)2@&J8z0^ zZNFD#i9@X=e;!*nse~6Ou&FS~sx$mU-&#V#E@8n8pk**@QPXbKIwe{(Q-FpbnK&c2 zFRCMeQb4QeOwW~v1e6fg;xEW5OP_UoqjZDWt_RWqp}QSQb}Jh5gtjDEj*Jk~PVtTx zZ~#ejj=F-Kvk;GHe;xde1UUHwmvjQ{4nOIaB+#k32$B2lm%Rp-C_LgeKS%>my#$cW z=*t9=$nNDtqJAFK?v9GpXW&1xXD(oY?uPZX z**r|U*=YRU`I&!+pPqrHykR*sdTTXo52>yCrlAqeKX}s>PEi_F_Zt_yfM%O>$)<#~ zB_Jikc3E(GH+SFu5acZNK`Sr*pengyxeY*CPptPf&fvmXu5Tnwfp|jE6ADE7E~iL# zvU@i(3m*``js>Dp`NlswGQX`b3p+P4W-eAHO`BN7CPCrm9ywI3;oxyg@ak=?&H@|r z@}&k!yuRryq)q~7grvf;VocL({POU*L3xaZp2r1LqviGMkq$W>T=Zhr<^yPCuisaH zQ=0Enrh$Z^$Eki=~u06qh^7khd<> z^i+(e#3uoY{x^``f1;3VQWB&<>UKH1qCwq~+*b*03X^lyD#fo}w_@~tkBfpqA4ABm zGA1;=gFLUu6ggrUlEMR8sSn~ke!AU7R954tA~G8yLvU8m(8jhY+bFlo+{=UWeA1EF z;9YemtNHuHMKKN_6;$&r-VN!E@XdQlG1_)gy|8|-{Pr}(U3UmH?`u5>L8Yz=DJ4qN z7IVOfp%pupb?1*!F#^D7#odHp5Xd)K0x0>=lw;vc&n(m919nF7Ei|zQ@%Bg%vMR@x z$6?(i3=epU_2Ka@7^W)d4oGhkl*m`^HT*HPGr&O( zW9zGSefw*7UpsOWZ)x6=9~*!`gmFl?#PWB1ayI3RsxyLwMoTsP`u*1PNu-)nFs0t) z%ErPMi|?7(BC5y!MgwOa-$*QdT18b#yibh0YZilURI;X#GC`8Uz+ zZ2uBQ1&){xg+dMm1#AT<1;>Y0A~EgZE16XMaO?F}p?sG7t0e^%u+rf()7JsBlZgvl znKb~j^N{gdd@#uaQnw_H;osR9LHR_@R%G~}0;^Ml<#z@hPgxXDEnH}jp)qU~=nwgF zi&)X;7?oxO`;945?nJjFKDES{yXD_;|I$<4>zp^o0&BVhv-~WOcgwPXDlfnNHCEqJ z#IBf@vTFX5wSA17p%k=jtY0sRT;^3-yCq&*pVA#%greTan(OaXpio_h+t?Oi>F%hJ z4lk{WYfMuTxOtRb$q7NqIbb`PlQNGC!WG&H=O+6N)kmKEyiMyZXJ`+BFY0|WF&HXH zN*|NKXDj?~Aau+Zex;10%b@cHhRL7;g#sb47y+8o%)qzBKvc^JAr=aRj-#5}5%!t@ zogmXvcN_>EyIB7p1vRrtPfTmR+qRl7rx>;R4ulZ0Djl!eu)^j13?E;ovX!8V`_7_i#4ImheAllssSj%-uq))?<4A z>%R8`4~*b-0iXyCxdc@9Bj~pm+%~)Ui-x8pElV#?3TP2^$-$af{xQj}HvAR8 zN3L5UqP`1$7+z-#0he<#@3l8-7|#v=pPAZmsunKYct@yhS#BSdyYvx(`M$_y8{aHB zOILUq-@5Y5(_g~RuQ0llY4#QAW1R{m30j$scSL(+aV!WhBQf_N1YIrjK?4R#iyl%y zt2RW42SE68?nL&VPV1fqEW#?Zs2CTKPd5NNVv5Vcfex?k^DeGM{ zt=VzAlW}I+0nLDXgzC+eU1;ylG>az;je$gHyqs`N1u=u{d0ASmRT?t8vZ2_CR4U&< zQI*oGiWC9*%I^8%zpRV)D%qtzxY_9h{>*lVoP1PpREo;SJ0060u%vb)9=E68UzkTxbetR&Ruyn*@FTMOw zm@R=E(YzuK#MVaNE;_|<0()wgztMC=#*9gT{bb7eJQ;--iuahPj)mBu={j4tYdG;Q3fAbf_J^lnP1g@Y%BmxWvK9&5wQG;Hsqu zCkvP03I$Skk_7jmy?VtN&I{`Q0&j>2d*2!^;t~gEA-05{=6eO&o=sdC6lZjgKHa?m z+MaW`!Yk;ST`iJq1TKWv9YSzhV4#-8H4t5g;CY{_=?X6CApv3dVEp%Ma1J~#A)q%A zTrCcj+<5+C%IAwyA+Av&30mtPQ@%*tY~+sxQ>^lc7Vz(U2wy>Ky;9RQAl+-Q%N9Tq zw|t=E)>4OU$er89cQenYYj53mWL0pg_LxeO{Gom(?_#iv&k%p33Yyf)gP3!XElOv| zymq%oKv!y!wrGW3ESI(t`?~$6lO{idx%sr!;Ns6gpIiM5cP+D8_=wwrpmpWQ+45=M zNpZG^0SY)UZ*|*UGgUQ?-x3M45=&Q@K?&5xV8;BM&U1#>SwW8M6NTq>U!_H`LOvUo z(~qyOX=JMbAw4JPNvQyy6i5JL{V1h4i?iZB1aQ4s! z!Svt{uRRhJ;2q5K|Lt>t9!&TD$qVljXDc&mr=4TZU4mcvvJO*@3t{G=gmhax&xmQl zEZxWbv0FNslhvBf=f5SifVs2e0DY{Khc)`*ov=&FDyv@k8CC(kc;o7KRV8iH9s6RoN3Gf(15%X5?XLwon|CHB z7vHTZik#W$Ce1SVRPmdQa9zKc#|4^EdA-E`eiTK!&GZtb+Bch&ThD#)MdI71>*$$% zaFs7mx%&9omAFkfJgR8+@hzxEc8{Z%mvj0JBWHoTxAAKSZ5W`3xJ+m9U&CiD*7;F0RWdII1LT{#l0U; z8$cif>3bf0uOwWT!X=2EB=bg;AjuScAdnDlKO+Ij@`&-+-m&KdDBSSV`Z6%(~uGhV4?WE zv8hQG^3zx5H~E+MAy`j^c{h+m4(!JsNz@!YyMx-!J#<$88?d0t#iA3>T}!HIG*Izs zW=dS@`hMPM&>98eN>!6%i6K#b>>93L2Z#*{x45%09;Mlq@>nd}2NLR#J}Ezzuza&* zqP`C7=rCo09z}a& zb*Wx!_M?RQNZV6@bC32veAR&yUt6kxv&{43Gf6}v{CE?8yusB}zdwrsd!SxDQ9D(0%VMfhK{CxYwOVc?otMUy9Zye(W4i70|CqVU*Wc` z8QxrfJO77Hn*Q9rcK!k)V$L`SX@1MCDKVu0H0}TX2{b1v8LxUDvEX173+E zgfxTvg$ZUrvCE}FE?2h>84Y;FkOFd9Snq{L0go~ILJppa?Hw!JmH*c!^|o%8+e6zM z9#sHzR6=Cb{z|9y{YG!A!vXfR(U_W?B%}j4x6e;S<#)#(CFUpgF0ZfQ{3=ujark@v z5|iipM#gVzbTAboBDO5^d9x#-SZl%T)p8j7Kzpl#M~jaqzjMk9-N0 z1v8}@PSz83Pau-76+tLrOD(lY%WKh_MW#`swQGgD@5&fb*Zv3+b4EaxJWw!0JL>gE zVUF*v&7$8>nI((|L6fX1nCpzSy`p9hgBq*RYIjt=n8ygPVo71Ub-Go^jbGdpK%NEc z5Cdy1nzhT*m&9s1DUc0T(=ll z737@a%F&mrHettKCH%Ook5PG{>B7-sU2y(=gxX!PAL@C7a%ACLnLp2HtYxSA-jO6V zXDseZ5OftmINbs)EZ+&o)?%7aU!|}%lI~20zXQ3{+hPp*&?dQCz0rwQPO=zX_@}BW zvD?;rMlZU#b@i~eNb}O&>fCi=e70X!wrwXL9@33LiRXuFHfqgP z8l&niLgcyYx5vLqQHHQ|ukM`gjIbr|?u>)P+4z%cGFLqDnjdG| z8v}+wR{0nc4ij6Utw7->juR4Qv)bfWfIOi|$0JTREz`xS0M3^-WYVHOv;+bMj8+Kp z{~|_3xKaVe6`e3nA(|4XOy0FWQDn6)y5fM_mDFIopo&gAEX`h+8od?gzWOi z5yn8JtKtkA-aSk|_m85<0q!@XjK0cw`;@6wG^FaPZ9j=QU3{nqj8zZR8)Mm1i zq(0EiQ)YL$cdtDLokGJGQb1j_s*Q+9PT@$BV`pi;c*u5AdGLc+X_`>eSh>jPcP5H$ z|2#62!{?6B+JdWf>@>S>5_!nGn{djeu=Xgl zqS;gY30GGd%0(btQ0>_DDt9MU+meL1x7_fZokeL9^H z!tkd|of`=#g_LcC%+_n5}DO8%53)-+6h_b{6>SkAG6yp zo)J=L@j=*U8I6096JjBD*78T_IygfK2}dz|R=KU*imftZ1YSVISC+eNu&G89tlm50FV7BGbvx$Msy! zSs6PRz-0x0u-eR-j!H{yLs(xE7@DKS+-Skbz| z>j7mJ59zBo;&JQDc!Ng>Hj)yv{oH+j%QTJq$9vDy%V@R#Z0o1IQ@np|n0T0t+w+G$ zHV{bftU(o!cW5(7z(xNekhb0!ZJKB z8n!%auG|!f-no5sNIGihl!34*U7Hrtc`-5B2=^w?>PKjs;6vZm;(~d^1`6 zOhDX*Yc&Le0JEC@@%Z4^sM!7~|8l?;R5HKEN7bs7VsT&6%V~>HEmmz1`M)yIrqBEo zYI8HNPibV+IG%9oq~|52NiZHWCr4WD0+d!Ydw|kvfD=LqUd2XKHBx>Mv#!vIMo@3a zpabRE)t!K8{j~W88ONj0wRMd)1_~`gNTJ_Ys@s(=NqtJuhh4r3U%r9y$Yl$O$f;B; zU$cvo)p-WLu~Dd5s?(XM*Sp^vSHoR8so0j5T-^)!5G^^vh(7*baEds52uD}{4BG_# zj{nPCW%X?C>LSE9=QocRCt8$BH_4t~ws&7l1xl{B!m@WL3C!lcpP|>O&zhW&pEgC2NSYNA6qbf_DF@eH$P@6V(RaN*ZqRjIqmr&DcMXUwYy1q z&HYrY-#WF*C%u$fwEzZ^OM}8%EA>y&)`xjLU%6K=(5H8FUB9Z8Q1AX|0V!s>jj| z_EA}b7s3syi8@#%1usmn3)*s)kTq>dQE4`Yb>$+~OUJSr396}c7CRNZbF1ek*+}CQ%k^2F$Ij4pG?u4tq zVk_JkpYx;rlD;OMi6OtPtG7Z)5PT5u&HopKL%S<+<~M!Uc~4bt2$wL7%Z+`4#-{Dd zP55fjJ#M+>>rB!8n_14#0@@(;7v()WGcqr&&VENLRJZCfLA1; zATNy(Y_r89HiR5SLscFc@JbrA3Ht|G5Q>%)&$)NHknghIqTpXGUcn5}UPz)OdkQ zb@u^TKj)PhbA@_+%%p%H3!sp!A*qyV|IyujJ%cJHXb;3r32EN>cv>4#_;aGUlJJm6 zbBM*P;6;c35FUl!7Yg~1b;^^m9kiaRO-ginDAw1azu4TU(( zc^A@&C)r*|YN=^=fr>wndxkW^)fz<^oI)3eK(#=5=^IX=|AKH(AKNU4)1_?C0-pQ* z)&l3i0|i88+k98$!OYwqR1_^ZNiQEeXy*)AnQ*ot^#o7EK_)ocU9O_D zXTbSg-VvufHxuY)ntW$*y1#XssW!{_y^|8>zy1N35M7g+9+-dI*d3&=gH1YqcNUmV zc=;UTTvHMAB(IgVpM=*1|-FMkL%XWYIeP0pH1l^8F%Gi}%5C$@6Yi>{r%h zHb)>azZ+O_;pe$B<5yO!zCut}ZrjD}R}OhwQXgVPb>^-PEIMS~!3NpWiy~H_{Jt9W1xSbAF23$yDN$&*>yPH}l9F8J zCRKnmvUXQU+zL{oG%^3(!Q(Ge&j(%*W-3BVYB3CZYBPksV=X(Q{N>`sakVh0H)8ti zQYUT9r*}vvo%_z@GgzUYh8@pnpndy~GiAqp;3r4OAhZjRKEd@-UrB}Z%*lzpV;xm+ zau)A6r+t-yTo#wU`_Bj-I{kOt-q851{vu9W{690Tcd~}{w&!Lx%uW&0o`y8U`%m}P zbsih!9#Dx)Zy>Chfr9->pWo@>fE~^c`7K_-dhNStfk;ydtqahTr-zAZ-+Hb(ZR8#- zUVpyple6NAn1mouuzUU`52;?H ztib+GY^su9n1i|C#7+iRbI@tS(KtBwXw1G_LCTqb^)RE}`H8+|N%EFbuT#I=d)nk9 zrCuM4)tSjr<^oqprLm0CsTwC&O{roMBYO-JNo3?{9)0ya;9 z3(RIAy{%9yR8-?Wul8{DCYZ?!^VhTlGkL2POzpih103cwrPU?rIrs-sEwuvglzmQ9 zXbD4`&@e(I$^n;93z~M&Kz$$dp=50qsDBnUMK; z(vs8-ettp}vYfw{&r<{CULJLbwEU(W=0I;LC)Ac_w&D^%3RGz0PI3bAeA6YsLaCs( zyyERj{)W>D%M^qvwpDFofqZntU!k}Dp5!G^HWbAVjlFu_msp+=(mR1v-{zSvTv72E zH>Jlvr5;)54$WddR$v~zGcoU&Dd{kU;WV3MKLsS>P~$`so*au>sZP;1Gw#Dt^M`Hs zJn+iicHbKsu(hPvB-E0-L3F9fe7Id@x+hnOuXFdj{pv}sv$)?QcHOkY7RY~-^`=CD z)Y#FAu`15^W>pGi-%N74@wFDigjybU^6I7*Wc7BqX>FvDFOSxw5J=RfMxl(*vtNx*dnlqfBA9ud1cdl6q`DO#G^A5!D|G z^b(X@1+wT@PfSDj));%R87hh>WCiHULCTRvegYa4Zs&I#{IgKy|18vQR#b+NMFIcE zMJ*r{Vuc;C{ufNv$TD+Fe@HF;<&KOpdD67Ya#HG*fYd&fQs+lNlMSAyQv&- z0GX&HX^J=|2o1Epws)u9xi#FIP2TOIo@+6DR8u0OY@(z z=Hqo3p$cc#%q^1kbVrKaDqB%~lFSC2B-Meou=oSLk}jvl;+ZDWIj~}jWR5p5Mw^&W zJ~RW3lvW3_j7u=KE3KhOK)_!ra+9ApOOTs;IYlGvvyx|$ko2Db!YL&v$hTcj5(DsV z58<72tJKaEVA9%2n~I=&M+$)Va%h8MJaj(s0Zej(_}4}wW(H)k3v{7r0{zCiSAmhM zApT8-q!a@gaV;)LK}?QNBU<;{1xA)Tv083(^ zc*(H5%%20uh{p*+60iE^%%G2GgG>$bMuX}lp$#)#;a}uf27pRc213k^EC0Rh$%Tuv1FA0yP&*;^@3;t zzSD8V#w3iJ5W%D9UnA=O+&znhHY4bur3!?L;Lw@U&^{*U{R&w}+% zV4#2J^H^Knb^`+bzpPDrFEwUouJ(Yu18gtah3IxL*_Ll=$uSsO`ct;x&_Y5V8Kf5! z(y(O34D@|NKH|&DGEBaetj^MXZEp+%H>`oHq0!g{I6QLF_pNxi^kGeDLDgqf`e1_{ zudgqA{|mJ(Ja%3kuc$JQYfi@Dm6P}*k#8+mIqZ8j5<=azeEzIN5XYy&NuusotaUaI zbHs`|u}=UT_-MQh^b<)!3Tft6ruO$2AHUj8o8^lt7Uz&hJ%kM;ZK^(M$zr41vdACW zLN7(1>T_0XmS z);(?6^irK~ltK$}mQZz9*kQ5(^3Ra8UvjmNav* zf!O*KN6+r!|Eb>z2}%ZquZ$y9(dMp+S;DegK$%1*u3?RWYV*2Qa~1letG z+S>iNr$sL|wrtJv2>8xI7s95>%`JNfmG^$s@(!yN(`jv6%~ELzz3nV9+Vb4wpA~nl-DsbeW0}1aq#jPN_ich;*^BaV#oP|la$wS^Y%$v>^QtRNqHUf z{v<7ST;I{U)Q0<@z)eVwCuyZ}Pj}4)z@vNw>Doug@3#jZ7_{#YxO@7D zIe$-PVq`nN9@Y}6Ueb|Jubbh?u_ct-!6b)PRB3?gc%&JP_{oK^x#W}Rb-5LBg)Mdo zaeG{26e0y3(Bqc$jQF8|`|2?-RM#IQ*ccX)=#)*DdpvD*De3{$$Y!*| zxgnF%E?)mdl`@548yfZUp%05~;6dw6Xs1gJHtF5nUnFN`X%5|Vc-qD))9!vLd8DSQ zjT~(|Ql3p-VO~?351Wr4Om4LBKZtR1Lvif`M?id_ZjO6+S6~6ygc$YDqAV63x!+{B zI+vJ}19FCE`Iqp>F+Py^MNLQ6Q|Og3Fo~OmGiD_3D1F0goATvP@Ih z1aXFi^ebnCyj=m!wL-$@N9aYhHYUJw?R~CS zl`|ZGbaZRY9bGy=xSkMh+Ned$fJ3MSR**|%I(jWH6fjdY&<0EeC-Z~OmH@Q3MYd!J z;M6pa0R-2zhK_j9**bBF2FhNcK}U)ka%Ognr`teh3-TC?7j){epd)qSqIniNOo7f8 zW#c^+Ns zfK}@8tWB*14v>H!AvmUO+XmGoEYj*6;*)ef;G~({6+Q*LR1?Y&=V*0LBnH!S2)t4l zNJjv!8ZT`!KJZEoXawMn+X*2#tG=AGk9|EUG;184o^jksZVAM*&ynO(xI=g2J6X8i z>2^bxE1vl!OOEFiqA@}lX~NesE#%)Dl>6S&I1m-q75im=VUknMPzKtwsHq$arPB^U zXhkFvXu!ovvrluDNE0ggK-L>Qe-j=|9~T|!^Fv*4_kF4cn~PS-z`*k#wKGi0r5XeX>&JD^@8TA^J$1WFnJ#>#g4TzNt5}APRyjOkl zI;#6%|9UPT6Ir*PwXS*x`dM`XKOsn%VvF^puOX4gR*2{199#hl{mqt#+$#5UN*EV` zV|KuE&O&;RYb?Aiy#S5H8*$@A1dfJALp{+(Mg7sDC3?jH)ntK)kN%brq_qg``V zpEasanevrX+m}Ac*%#2hhBnWZt*x=CytMYVBEuZ_K@%Pb&|`4=8$8m zYQ$|A+gXy+)h0hVMRd}In~+SrUfg%E-$H$#tVsRl>~^Z;K_=Kb0Ts83EmWhPQe#q5 zCT#1Kk*$Y4Y|R>4&(8QZYP5bI&-)q!`h9O3y+&gSYOkxv7ZmeoEGA*wU{Z^>*Shv==d`w(2_F0hgnHH!+zYubPa z@E}WtS6r7_{G_`GrcsH%`tWF~A?kLKenpKJ5Z!bg{#-1Z+hi>gzoB^}F{UCPFca!? zetKMB8ByzaQS_A;VgNNQXDAF=fXo*W{NN!3e1&5N%7pq0A6ODm3^-m_ogWjb)Y;5?>YnBLh|@F^ZT$d+_}Zi)wn4)=lbrG=zMxQ{Mo zp1?-phH?Eg};sLDEdB`fw8crwz zfR?L+Qa%0tw0h;cDU#k&pMR*1`Qbu8C}MMT?&dRC5q4YxBbRqqugVexL&-Ajp5(h@ z#-+GWe#K}0_jj-j{kTLPP%mIOu@Dag^E*wFV%LUB`m)XdsY-U3lZNeXlCpjQ+fA7*F6~A)j{oDh8162fu+!k%9M90mKQdvZ#lA0u%8#iqrER@ zNDN6Jss=y3rkgT4E2-Hy%;1N!wvh^69-vDbVyyFZd(p@O-g+8qZs0s&;D5;D`tSJeDEILm>-3bQ zEW}KE*n;(3FM7$r`K@L22O#a|wyJ~J`HX4r*2k*32XOeCpMhyyPuz&+8q^~d*0^=M z-U(oruRlbtxb^C6Q3p3Sk=i0nx6&JY#ng$pq{S^=MX=l4M?hveySN3LSx7%v(qokH z&YeSv)o}FQd|TFN9}wQKJMnTni6EppsTj_Bn7rhlb+fkzAj$-BNvSz-n0l;XwotiJqRlA7H&KC|ti{Qsl9?*NKwY5oO7 zi4qh+39=}`1T4D?h)T|qMRLwLNE8qOC0qm~vw#93Npe)sC7D131te#YBsqiRH)oaQ z>iw#|SMOE*tNvA&vQF(8XXZ@kp6Ti7{xwjIbRYPcXNCwmZVS~EnS~TXiUp$a55uyC zM{r$2eznP}P*SktQ3t|VE7t=^zBsU``*M%HJ&y9FqCuCB)B4n6mCi=U)-rm-!c0$3 zChal7g3M9#UbcRF>hH3Ikb;v>y0)8TrP5XahC!-}MJcLe*Eu|-j&ar)?VsIlOVO7s z2D?w|FuTNKRTjSn+=?w9Xl!*xHfsEtI*E$U_|l~8@J+g4tKR7PL~;mL98^g2OqOvl$?YXeybnt~~JQB+pue9LjFumRz7%z&OdNKIeR4%iZI~X~V*;nXCO_ zcCGx^w#4MwZI$P_rjy>o*IUAbJ#YM7<#ybLicZa9ucL4}KhajpJYS9Tht#tFr11685pinVU-x!wbhr5M{^x;NZBKf%Ogciz;}j!5V&$?wH}y;aR_o$;&o<|4iM*$^YZ zYKn2Pe{;k)cUu3VBLjpt>yL3}r?fln*`Xd~e9^5+>A2?6N$e@!eQjXQHGHj5-{58X zz~8lTPjE7O3zx|~w#XZ+y;}N#;L0?LL$m6T>n7N-wB5(Ztl^w87|wEs#gx0lQ#rf- zB0j`z8B7y3@h+(_rgR)cQZkYV1CrN z?J~6X5c*W4_1aln1y^quGLsi5u8d->aK)}jNQ zMRe8iCA*IDMDg9kc|7@{Qtjuw7KNs%dU~TW0>f-c5A}9j9-g-)Sou0rIVCFoLH4kM z;^bYm_zqX$@Y$t0Q)*{pR=C}MSCeJaW`#&U(iNE3XCetc>wBD2Mfu{3->@iZ9@}|@KA8&TekyQ9b z*HwXXp1omk*AsjqA9q+-ORVN?5$#b9$9k^qa4FF2{b?CQNoD12)_CPaA9R^B8JjbJ zp5m*Gb0y{k);QlYt~j4{v*VVUQiB@YiCb0k^mY%LPtXfmvd08Eqd(LBnA#Z!{}b;I^5qDIGuqJKoYpi=Kod&gE>};1O9`SU+LVncY0iN4R zmytEZ58Vg+KrwBufn|{cpA!i#d2m}QLU0wgHFg^f%z(QUuv_9rD)d7@Hg>c59cc=i zbYLA${SwX#@z%@%Zw0xQ0t9xzZaDNfXIuVf{Ftnj}5LxlD#xZ8y{*OdQM$?E;B4kdxv5#Z)m@l-PzRxyc z<9;QRH=|80r&i~aXmL`tMgUjW&b9Z$6hWb9qidaT%MQCYLnyrOcy=d$W^TU9)!b61 z!anDcU#FlJ=oED2k==~@BzXfwJAZ22g4Lum3mnQc2FPdGbQHl=&*bapZJMrg$zkfsdae3amhPOEw=badu7u@*S++fJdEPRc&&aCYySu!! zG$;GK%hOxWY7G^v!f8R)anaKwBPFT7uKOXvWAckc^7lSX1-XGPw^kNiz#aOvj#S4q zg>c-lTB_LiEIDt`OZcXj(K*C3`CTt47UcM@UGB`;fPL zB};G9^w`T?>qh}>pmiiTqwka9Y0p;cg?ASL#`?-ypF<&IM$}+PVbzbBFv>d%_nEg> z*6*&bJXexsK|k-ZN!h9D|3T!lvMSx{>775z>@jIp%CZ23>qvPA0jp=16F*Zw6bA0o z$#9c+J3=x)YWwwlq8kxIV}25z{K*Y(nr?UxS?Hr>&JRII(;BND0Gz%3*|#r@Fb!Kw zj9NPV@=8O>-Ys>W4(-fl^<(ikC?Vg)59S~`hRqXo@2v(>hIqbV)X^^j2!R!K%I>ZW zbStU^yix1>nuzq_K|mLL^7_6a0O=ALik1I&a3s~uec>VYHbWdocY$0{x4`_f79D7e zw zL@Z9%a2dP99H^gno_@20+5nH)*9KU8KLgJ1AZ-gLgccdxWW*XM2jX@Zay`}&Y(r~s zTiRO4gEVX#lyMx98glKX?&?uLCq@=ZBY}3NuNAD02a?fIGG4OCTRQw(RaHXC{+hk{lW;`f3(!@2RZnJC1f90EeRycQfUjo{=JU;t!zH<{B-q?#L&x*u+@39&&(>2TeQ%ce3yH-e>~E z0l-UfyGzs<3!FLl*P7jJuWhVR`%eIC$oMt^ zrlbgPS$zrpYflP8mpYaA!N*wP3cv;!HBSZs-NJ3a?>q-)4#+Rw;iiQ({kiT)ZJFl&3Fo#hx(?$}$)b?7a^KkQysZJ0CU zy7@%g{UvXMB<0{%zR31~m(S+LdR}5#e_iGJ;9H_ywFaM6>kh%8K=-L!jhbACkm|TS zZlC_aXFeU%#)Z4nb3Uo&(r6mb4!20H6IsP%r@*()a2h(Y!8pjWI(^xOrL3EP6S*)wMDYSGi*zbrKvl$BO_a_so{ zp?bA?9w!11W|$u*@mK#|b5aN`)wrsg>$BtwV663_MLSMxx1qVX*WM)kQh4FU>y9I% zY~8dvpEXv?F{3$LYKu9U#^8$G@Uq{y&$kaXkRLI*!MIH1ukwtxJ&Fx4-87rX99yPz1z+=A(N{L1w6@@E48W3Mc+1(c{*V`0$f_~sGF=05 z*uf{6559~v+EZzHKW%D@nR`B^rW3)k=|xQ6Qgb0;`E&j|OY_`LHPw#CkUGFtwDu&m zI{AM0)q|r1YS>sAC5t>|$&P_mu&*{Qj~^#EroM-2j_Zx;;Pkdb@2WA$kZNr9)-~Jf-s8z-^#kSA+0h95Mk-^*EYj%@XTR1 zt)njjSUBJHaHvt4vQJuBmK=UVU%rv8PykLNc>8E*eo{d{=NbpDER(_a5(2`;Avn`B zo!OIu06PKZT8BAd@@^l7V}dR`*}gNR6*_zF7sw2ATbOy_*hs5)M9E^vPfokBy60Bq z7B5>@~OE?6kMu%>dkcU_XE2Zhrtca zIj`k!venNx5wk%rv-y=iX*COwS5$X$A^LOluUt)f3t$E8iO3tdN zbf*xS^^OO2=1c3f_Edo6X{B}ty;RReovJgx#qhu5iki{7Y3=MUP%(z+UYb$m|M2h`@GlqqPjEr4w;`QkM9n|0g+8nXNMjJrsdsFdqot4K(W4TRc|%;)ni z4<_!^YYp{Gly~)5%zJme!c*S9NBG7?6W)6L8H>GlI*M)ZeFL*yki42hpj!Nj5Z=t_FDC0RqYuvP2&o1o6br9%8i@_9XD0 z)}i7d{ZL(LnIBo`9FFkZo!De1(|uq+EH8RSb$pFH+|-Hf>7`R?L;kj}364b&xjZ37 z`EkJsE*TzW^hG)z8ie8b`^r!qiaW$5LtrIBSRyLajYlfV(~a+T-`Wwc-OsayQ1Kq0 z;}Km#joPD*WaPfmBBX(@<>2^(epbZy7N|}<70rpDO{Yu{>!f1PCiV~)RlZDf=)HJw z1XYyCMhpA1lNaviq>3vGuAFtDe?_c!fAa}Z3k-E+2jAPDI-a1-=x8_I3*VlitBl0b zZyLW!+&=M`h_1G8_<9Ac5JmYLx?zR|yh{NFZ=#7sJ`f*jmC__;Sd>+p_vlX!J`x^X^pDx}}W zy$s`y;!I7BJYhEyKI%tdobS{P!wq$c(8Y(kjb^SgQq@-T(870Mf6a}uo6!8%85YM=A^qGCTW={O8 zByFWC3GeIP+|toV^fG#+AwN1d(k6Bq?Zu`P(}i|N8*rs?7_j(U-_6{vAW(TN@>j=X^z{y^S1l@S^piU|#0 zMvcF09%=65E40$zt2`gOal!t=o!B$6=CSy(;W_eZ4jnWdNjY{oO6_oKc55T+9_y_R zuH3NZtY-J*<|G5u-spDFRMD$o&x%d!)wffEQ_=K^^wspI=+)@as5;(LiS~&NiDQWq zsQNNXZL;Tt+GE-i&-LN433E+1&Dlyr1XA-0X0te3h0+Id1@ldwm1l9Hd2UL#$jwPs*uV{c4qljrH)F>>E zS~s4zp3f4xA`~nXCUm^otvc76`-jmE!H(PJz;f5Z#Aer)65(aSJd*FE>4Z{*>O?|h z2$ExmT?ya5#(#~u$ISi3fK|7hmOyM<+%4c52`{@yw}YPav+b|>UC zmXr{Ghmi^Y@a2PaNEodgt-Dl%^n1w;X$46yNwlOKTJw@t>8Mnwb|@@Ukd~R@gcqxu zQ}+6o0lA=&nQx>cb6iN>xYC(kr{`nYjm-rS2CublT{z9a%rr!`7kvCp_LzLZior|w zF~Y8BOwd-;MBT-SutmKeX4{d3X#W!bRtfYwyQ)+WYg^q}aX9#W8_iu)}p4KDubqgBV8+VQ6%t&#HQoVCm`@eg<9 zS7kz3AF8-CcK_e4UcX&5VgKCCO-;NSRg;dTJ8>iV533J*Pi*RKaSyJ&@*aD* zm&9-=d89AGo z!mu*z4Vd13p8ayyuEDr}>WY+&EX6iziH3LQ34h-o@;;%Rw3b{W80Ix^OsL6nfZ04^!yj1U)992Ml^>Ko2wMVFf*~ z`HXPCOb=?^SInI|NZKF%bIMLYg=XfC`yT`}qR?cWlehwP5spM$=0PI)d6C+1I0yK5 zkQ374VRIfN?H@vPVCT*s%;SDW?3i1**qXt3pkcfL4kfNGmJUuZ9;n~oRn^tl+ROyZ zAk<>;uI%Js>S|&JGC+d~62zU%j9eU?NO_?4@4tg-l7P-N*rtO-1qajL-sQ|Z+#Q?% zK_GtV9YsZz<*)3gk|KUm$pMYrb}}<3g~Jd?QZTB&{$PB(yhvV{IqYW|62*%Ilmz|3 z?0==f;XDGMnZKnWQE-0Hm*3LB3-rl9(s=$uJ|w99&wR+spzFVtMF{*e4SW~%=Qj`n zC`cLmTUjIz;xd?uKhlu!|IiO;@P5yS;^F&e8Upc;`cMcyFu#A4MM6{fM;Z$L&$1}Q zpW}=~p?Lq%4-`t^&;Eirf=k<-alNz=mdKI;Vst0AU#_oB#j- literal 0 HcmV?d00001 diff --git a/src/vs/base/test/node/mime/fixtures/some.qwoff.txt b/src/vs/base/test/node/mime/fixtures/some.qwoff.txt new file mode 100644 index 0000000000000000000000000000000000000000..93b4c0821cb54f0b934763a49211ff5c71ee41d1 GIT binary patch literal 18824 zcmY&SCFkTMsZMo2sqT8( zSw>hG00j6cVj%#8|7y>g|I7bt|Nli;SV0N^0J8W|v44Q0&U(u#A}A#MqlNr%ydMw% z!2>{L#r$xB9}puv{`xktba466YJTRo{#if72!Z6x!dm~w zC-~z5YW~5Hqyf!=rLN1*x{rI|wrFoxitnD2D0O5xp zzWy_3*P1B(rH!58&sfo!A7A5-PD%{XFXpnQucM=50Px%YX4!z4{c}t;<2Tm^(13(! z2K?7&prf+`q6Z0%prfY)+84|>0etjx9k7o;D1iU<8+A>y`T8dM`Z}VO(}RHIa|IZ6 zjl>KM^z`%$^o$LG=Bc33(N#F>=-BA_`1y_H>48Il2X0eYzUvi02r>~i;e3kwYC3_%s?OFE}Kli84mkcpCykdu*@ke^VhkhhQxP*%t)$RWA) z+@v3Vd*`S^@iC$SO{((Ok?l>PyX^t@692|A;={FVeUs7gczK)>-v$ zg)ibNam#V`KJJuCbt2DcvvvY4gNP^Oi&XR_jo}!krw1Tci>%aR-Q&vhM zogZPV5?{95QW8b3DR@&)Cp=vIISv`(Z6y z6wZ&gH~Zho#8A@Q_|((|*EHia6}d>+R^ek-@suh-U^{G@3EC9tTD(&D6~-Gw=Zc_nMU!MG1J=IoU% zjAZ1nzLq5=rS6Luxi@gsK~cVO;$^MZv#$O`C!S)`7lN%v$rTDODw-5!u9~c>Ii;VpXL%NiuHE!RF5KgaiH--eq z%Sg}~2BIFP9=Q*s;9p_I*C4FvPAc#Nx%X+l1fRpxX79{j4Oc6I9SMz&QS?f`+`QI3 zj+0TH%k47J1FyCe7ylK?r@?n-ct(qGaiazK5BafXKYvC7H?mS$F`2-D3b?VleT$ec zA}YJmdKh#AQ5n}F{X~?XAoCL5hmE+=P80xn#@YhpSl#jQ{?We9kxe$B{ zAb+DDkQ2rs&B@rYi0`!xb0;GUA{D2l&1BTKp`^@<%Ec>Wmf2%T?Ia=(Wk7CkxN}468U4h02nU(@7cH51@@uTy{D&YX((Z(A zw`T?hQbQqK$3|xPi6A&p#P;`EP?V4gG9O~jN2kSTU^o{k~|d-k)^013;; zGH1X)@kRDeBBrWF<*W?#`%O^Dp-inxp+ydv(@#Tm=Enrk?V^3^Oy8FWtdH`?^7-Y* za^IGl1gL)Vw`%jl@BDc)31eO@({swwEd(%xX2XUSne*`zTyww^oHl8G;u{m^&)LyB zon4=^Pdo1@!93v!Lf5}cfIAASnPS+)vQNWLssMEh{qwl zeh~F;m9vhcn@Dr_6YtLhQT>Xty7wcR{HAwg^5(Z>b$>(>+w{)7*Qn`%rsBS9<2Oss?Dkr@Mo7-S$69~=bo?#+%9`%)mlI#3ZtGpc(cGt~ zFyWK8^>!9Gk&A~vPs zk03udmlC;{H&&7?d}yzWsr}GL(+2D3+WD?w3nDAfea^9TCfOm;vto|y_%-f1jyD`K zQ5ryIEk#YvDzZTHBuE+y%AA2*75kdS6{CC5^29oWuM1vJQZc1XXcWgY=F)X3cjeWP z^V61+L+~t3JR$Kt4yTmIU;Sarj)3GM%GAPmRnU7Oh#Wt9lQ^g4zjQLo8Z+kz%Yn$!E8sKK=5WOahnKFW*poGPUyFS?ij^kIl!XA*NY&>tE8#R|ZT!+6`_mUkCdIE|WL4BAe}Rny;W8wL>VhvU%hE zvnf30=P>Jd_)5kvA(t1HSXr2x3)kh#m5kMBzwtG%yAOIPd4>h~l;@7KlLmI$f)H#z z+Bgt^%t-Fs^Cut)TEv70&|{kXp6Ka~p^ZpjUAgj!^$00g8#NLDGwrnT5_# zZv(q8KG!$)J3BQ=)|(^++uhW_tuN2oJZlVFiqoY%yoXoko3X7ig0FBomKqsm?@mA6 zk%p85V0X`w&J6Btl$S@&ou9NCbDN(><6FCKhq7cIcgm^#o1I~=Wg0Njr!Ux^86_wJ+N9NoENfhzG2TgZdg5yAIZds&;k%%EHIkOTsh764uh9hI-x$B;Vh8g+RLj zUq54aKKR&01c>^Xi2qH_v`@o#NI>=~;L2?^as;+ZYx$RjoJ~|UD%R=u`LCowSQPA> zpYRRApx3P$!K1T`1X;D+yrk0dME;yGHL1*k7`eRVPwi>^`C<5G_S?u9ZSpV&5t}vW z5pLblBZ&j=1mz!x!3M6=? zw9|-0Z`VT+rz-F1rA~YKy{s@5Qs8?Qae6nf%R|G6@E)aU^8O ziz=Bx#KmCeOO}qN)mKgYUqIQgD`Vx-;aZSvN8TFmpX!*yJ=9>_pxQXyh+?atP*#qH5=D&AtY`fMp;8j*fvN=*H6SDkD&sLI8Y4)w_x5T zmo{cMa$zG_qavB3;3Jr$qFBaaKi}J{&L%3ZH=3*MJ&W!sd~(Gl0_XQs_Xq_<8L^~J zSs*7fGxP*J$dOb)S-@y5;h2CnBm-CC+kO$Qq)vn)Z6T9$k%qdElf%XE!E`cV+zNTd zIt(P=K^8|g*AG>j5KwZZGjA24s$$W33B`EHg?saR{mmlqnlWNWUGxbVqllRqsi^WP z^7Bs1n<~LEHv3BphMI%4<30;(v^>@XR}|6LpioOLg?nz&)>t56#tax11k`QlZ#tu@eOsq~-PZkT zn#96(aQ2n2(BN5pc~W!^D5YaGMX=bDsJT1^1CSgZwEOSozEQI@M5P%cm2^X24o<=_ zNZ7~kBpmLgeM_PLDys0=g{SfTw}r47>s^$@#*4*#nBukKD0{(_ed-crCj?{VlWP++YDKkfa(gJm%s+Gb9Xa&9hJ~4oC@&zKUN@K?-sd_Ye|bLSaj+Opv%_!ZE?I zmHa^lPucmo)wspE%1_tcgDIR|8!v;cpYiK59nAE%N1>NL4R-q5rO?a1)cuBwjjzw+ zh}P=bKRcCm`^dbXZJy~ zo!VlI?d$gH98#Nc2-M`mdn*6MM7G+pFg20s@2n$?j)lLo3j+tv3f<&;iVXWmnuSnaH@^<{XvbaxLelv=6{ zuZ^D?cI0n)&v9H^Ahv{51bguW*w;k6V9W(oN&wJ`L!G74E2JP2%zmw*?1D6a5Q#;9 z0Kx(pzn=^^B@mRNSm$a$-}v!Y0)K9s;#`@u>m$dy5H&Dg2{1CW@4rbNOau+-71tJU zC%lJK1`y#!GdDRDQ~~bh#ruK9j4MDXQo~}%TV!(Q#AU(NGj>12Ab*4FrSt#K3Rbw+B&N2=%K@>I`PxeuCF;HUeDbk}zV zNa}P}S}nU_FvMysq|onFdxqQ-hkW7(QYKd=dlE-6A>Y#Eo5*T&?_=&JTlMlIlSK=P zJ^aQ9TsDU_BWE`CdtqiaGTDR}CKBN}^4i1O;d*EY(?{j4eB-)@tP0(K{2WhT-Bb3f z^WZKVLpKfYV=iUFT#J1wnE-S}(!MgIM~)sjO;jFb<^2$u_Or5!i0;`yA%msE*wB-C z|A_)U9PLMlTZG6L9~OVanVq>>Yy?EW`6#SA9)RF_wMKV(A*5EwLUToW93a4|fr!fe z&>UY44973**#CtGg4qbuu@fDuf#SSkHcD1)PowO+t`@njmOhg-c}B0|Mo>Daz#V7@ zrPK|_BTr8UZzv*^kQ!XB9@{IRI zG9ABWOC1X8MUd-{58k$}T~&7uJO%61K?}u4jrf^mp2Z9Q-BRXrVO}B4P4>=D^U0~MEQBTmfz)@bdN4_mH-8Tk^ zWrU5u8j$Ndg-05Pi z{K$H9^twVD#Cz$SZ|sQ4uo|gBtQI5lHi5f%xa0A+Za#c$Og^+IAuT7aF8HpP1a}gl zKA8bp{JnA%{CZGrt;T#t%+7JD&R$QtZeW8?an&bnoU0>wkYeZab-4qduO?V~zZ+iw zuaN1Sl3$=Ga8sJV_Tg;JAjef=CReHnt6mLMuSk3`K9Q<}mJ?+GTF@aScJCjIdHDq$ zz;wB}=jqsdv2bx1-mZllkxpivjX&2cQzy3!~ZIt5~FEMLjhp5ZEV#Ygh;5I&QLY zsN>~b2ku+4Is6c?6nf)*h`RWh?9$D+tX`HK_8I-wAwz{BHyq(2N(?e_rrn^dQJ0X6 zYav7QU-5+ZmV2wOEW3{uOD#PxhBcMCGRXLPGA5lAgYLlcNl;ELhl5g{FWim=xF|`C z#24ypgiSE`#T1YDtavuPd@1s79Kk$>l0eeKqW;0^%d>jSCmA-x$b{=r<&nKn zqCJH86{Qy%TlAL1{7DRkfVgS{J6O^L(T8uw9q)L*d1;QaXfxX;QvXBEP|TRB4%aWA zpL))^`MFa>w2ig>b&c56YAEpop zO?`LX9`=`AD~GRN2Yie0IcUsFwI6?UJ2u#tkIcPM1LC)&rh#h)U;s*v2L{az>!lde zKslehUI8rzko|YE9ka{N2;6l!t0HU7swPLBSIj! z1=@DRjz%(>IIZ`W6e$B{Io$AZ`+758OWSqNl7rj1Z&%l}r6u}Q1dr=+Pfw;y6gaw< zXH|P?td{f1waDZ)Lj-HLmpvLT>Y9*1(S6;Gn}<9p)}%#PuzqdLjK+ugr8@7h3+3Ms zre6&vAl24G4J?p}a-}HYi)P|x!VTjrbR4+k2ds1)rAOj5HDDZG-Jgg2MYo1G)EQ+F zV_J3^f}Z7UG+7?KmRsQdJw}1{{8lScj0#z38B7Sv#Ew`WPkbJ0QQo_krVvr#g7!^w zk2LYURO*dACOyWnB^ZeLCN*JmJD!X~CRbW+Z(rYH?f7VFXj9|z(2ozSe9;0mGPD=< z2@Al>o4R(I7!A1r2`d)a>8eW9;(R{i7a5BKs@yOIix~@jWq81GP%lSCxV{J~WshC# zm3RYD(f67qcqilG(tj~%fH5sG^DB+IwXSt^`M@sa=&t?95-kmLv7mwk-Q5)Jk=uXQ zNno#R8tx^-Y18dJ%tZfztGJFUkd$zqzGkuBdZ@W&+I*=oH!-n)(oAbJ_0Y&#qvxrA z3^x_~CDh*Ka4#?SeFfs#34INH?Dllg))tHfG-m0rBk<~I1oDtl3xjD%j@l|NjNJ*` zyuFi=UZqGmGZ;bAKma{)I?i8q*2qu;Lk?T_A>#qjX%Ua#;}n_484~b~ z^js2*Z7yBF8maEpBy_T$J|`1^9Yn32{<`Qrhk?Jz)YTp0WxdIS6&__HlYR>5$p(6{ zkB6#DJJ?X_@ChUtmxWizgIlBo7@}%WQPc!eYzVMptfl##N2Bb4#18pQ(+Sez6gJ7X zJ7qF#y@$z4&|Yx9FJfDNoUM8e#osMdU1MD1a+_&ixyKR6bn8y)oApPiVfHRl2|GPW~ng4oP=TQ}S z@A~!%J0k=A<}%@D!G{ljTy8ftSz%IJO1BMVYSlJTMEKdjz_eid2dQkxk)fezS3+gbKFI+h>Z(OtM;BWbV$I=be3j zk(bXbg3Ozn!9Xy;7rmm%mrbKBWvS_A0$eUbSkSk>pxS+nz9|O1!G%-}2@f&-WA#8UE zrmbGP-&<(+$pX#WZ@0|smMR=uA%;JWa`N7deE$(@#}Phn(vm>}Y~QYC)!|Q00jNV| z{Qwjx!M4vih=u$ZjHE<;?Mi9klXO9o56$aEk`uA)Kgadv@6X)x6J*cqPWsHQ+j`GE zsrdV^%bbTBr7Z0Xn55xWe8@GxLjjr zCI`78pAgsrn!6M~ERiXkVtsbspI+k6-K6C%C4qwX}(Lf9-;V{Nr;A9{M zhQi+rlfwXPe+>VmjeAekpq}m2ZW2lup6}Fy3(`t<_DcE{Ha1@%M==0K*-`Gz-3YH? zN|s}jo`K99s+D)PB<|avSbq_-MbuR|eTMm7>1131pLwL{&&D~^zIrg9IweTD z6^JB7HweJhdjzNR3-Tc+N*$1{g6N!#c8de~(*(Rd-j6R8BTJ(!vHimu zTqy+vpN#kxb-Fjf_OnZ(L4~WZT3?Tl(Gkp9wf!2^o2yHyqmA^j*EhBhCiUrdoceSG zEGrJnVk!6P{Fn%gbw~n~;`UA=M(i1L-=GAIN5E~&CPbcy#9*$JLHtX^hOV{>wm<>p zG!O{vKuV9lopOadl4Sc~#lcevZhk36*cdpgkdhL-Z|q)W2`Dn}jtQ(TCHWE!sq(#{ zx$Vz<4yiMF@alA)kEYXllriOiJ8>I!@MAA~b(F%3>FiYFudX!TC^Bw^zi7+w2-#JJ zd`iO%H|ANKOZs||(?`~7u+I}8@XqQBJ0w!4goUy&R&IGbO9tdb!)TBgWhB&SfYJ3AOh*2o=8D_1NYfYV1xSx0wN zt?l;*T`19GM5Ry+LoM^q69U3QWlFX2qm*|8e^{vki)p>KM&xOwBj=uyWLJ4t%gNu~ zUn}jiEX1*8o23+SX8uFbgwmW`fm;3b5xA{=WSXB0o76Lk-tDl9PyNEd=8;JScHp4Z zu;ri&6wQ|iz(5o4l~%J;pV|fw)=a{2d7q~JfNqysFy-vE1J=`T{`nZ|bZFflt$vhj zk*0mVDJ0!>K8J*zL<mbWOoTB>pCrVR*CifRR zSQmBXMA#=c^Ww0Hb6+S&Ryr#xz$n|0V}4ZkWv?e)fHYBDClD}q>Ep1wFuEwyv1%Uc zAK?TQQ$&;CTS@3w8RsvxgJ!e>7xou9gT*(xyi53b{&P4oio7NTE5JL;TW2>SW`~`r z=bkq8(t&#v@xVB}P!{ZJK(%SE%-dxj!om?ahM2pM(kW#-s0m3iW}~DBa$2&wfW8x{ zAXX>+B=bA+W#XfHfKQ2oj{WsZ*Qg$o`@!@OW2BUcUpVm}AIU#y`rn4>nX-N=?S6x< zI_;CcKGHK91Qm#X9l&T&;JRNj>x;9qyuK&bB`?8+6v2*Dh>}<164bw_3pIk!GNm8nfQle%&hd`=3g`F|DRn>=y`y~`w1h@tc)-OAff>-OnLYH1Arn@teO)wse zYIoze+tc93dxAwd2r@56EVOb^802HL@d+mXCH(f?PL1{%m%l#_oTl>Vy<={Qq2j9_ zDnJdp1;5WLkt7C7!H>I8iWF67ix$R|W;uG!3&+D7AI}%0&N*n0{thRKM&c9+{X|Lt z&6|C<28+PBpJ2HFbUJ8=gDmVD_zWA{@r>1X?R@Ekz$Ul&rTQEC#4;ofA`pp5zMN4L zcyF3CjPW$|rkC%;xj>c5R*ZxdO?t$c`sq7X7#=#_ILs~_iBWnR70DD@#PLAkdE4?-pHYNg=InhHC;XkppV)&?kXlta{la+ZQA zCgs@PwWMlc4|FE%Xkk&zX6qax@6>$97FcnmyE<1lif;%v6_dF}zVgQqOj~omKrl9i z*bfZz^b9lDJwgn8PIw+*QP@=EF4?-?RPzTF>R(ukolF}U@*>g__}|~AptFxK*525X z@F->srH4_%`;!>w`c^U_OO)#?@gP67_os&$PQd;m20iCh>G8D?$^s4nNopu(vfas8 zGzWrc)CtX`i|<6eUxD+tG(; zhB`kQrWFD?4ut@MK&%5U+=bA~Mf4~-bTk7ya?hM`}ys8Z=gyAeMiakug}7HEgT+EOKv!)>xp28d5G#htOy zWPG}7@l>lu45|XSXVgmsF|?J8M!6_`=ij)BIdz>@N()sonIp3BkQoXe{#-t&Nwn_N zBKZE|>^BuPJeEnbA~_xj3_(>h?vrrPZ5|GuDdY=~+Y23cHy;kVEYb#pQ&URMEIbTR z>{F^a#kCV1word`d(LZ)ne~|F+N2!F+FEcW3*#uNd(AWBN7R}F)H1E8DslHXMJ$GH zL$IMwZp_LJR?Fw0^b{g&I6$Fm2d0hQdQWKNg7Sc?6}e(Bd3C7qi1|qzN~>6eVeVg~ z(p34vgRFzp%U+d(rJK8ng^@$mJD_I!FLkA^_O@Bc6p4WnI-apCtcXG@sbNO$7fI@5 z513AW>1Nvj;+0ydPLL#kZd=eQiEe~t{T2-E@;*B@aOuQg{nD;#=h`r+4l_J|PPeb$ zKcV0)a6qlE!ZN7*ODI<-T1Z$@4@fPKLqndki^RB8G=7KDN7xKYU9tOR)vn^RpP8-5 zTx8)gp2x?2Uta

B9x4mBjW=tf`c%og$i72FFYl3S7(V*nF(1Gu%B_1RmR}L7O}0@eb@)>xC)b9pLz#qO~IE z{i2vm5*&5*bQp9#k~q?K9B zr?f)n3;}K_8-se{GpI7SW{n9EP!UrIA%lSpW~z(Vph1qy&=mWwK1 zptO_a$y{SRbX79eYrcf7Nfwu~0k&_zu6#1y`nTHGni;HgA*>A-`P@#pycZ6C@_JeG zutXnAxrYu0AzSQC`)i=7a~Y58*7hJMi<`)@hmzZ6v)!33))+>d1t{xS zXIgQj-D*oY@qQ;tLbYylkkgv0zJh5ieZayJ)ml$iuX$_~?#A10cd0o+uALXzH0eZfyEm5T+7 zO{gKp$hD7p(AT*0%)&)Dc$LbPap;FLV|+5|GbC|0gi!(&7cE}9pQXzx4;eW_snG$r zn%-eUl8Q148?Q=Dm5Jq6-{BG}Eke{=(vpX7QP)758&&$NveK$!KSXNFP)p4elkFs| zW*h9#a-U`#XdG`Zd5D<0gT+OQUCcX4eOq!Z!z@2e*HPUd12E zSafl2{|>w5oiA6n+X@VDHBAW~5j)$0g$R9`WL-saOtgE!oPD<<+jt~bm`W4gCh17Ti9j86-*{n_cK@x1Jb5lT2uIS+t@ ztjYu9D$NFo!^YQ)CM##)T$!b_CC(aeLQK(z{<$72bvbed2t+jQl{TZB%l=Tib$R)# zorj~FXq>srGD$I&j#4+0nXr zX*tmV|H|aOP*d&w7h*^3?o1SC!_x%%q|E8Cb_}aGzA0TzP1K>k^x8$i8XbAjVAw4o z_x%Q5aJI7v)&tYT%#?%E<|~Z~l3-MXsH}$7K=u6zMwUbbvHUr+jDO8`VPKw~@jX6B z`AyW>C?78Hq)CIFkM;93Yw!YbcIag%{C}g|{Vx9dvE%N2cfXk|BvRw% z=swfZe2g71NSO)Y#RIh?=`&M_CWk@K(d&p4^=i3onILwV51f`0*d9&3$u`eQc<12| z(bo}ekC)VG2~%gxl+(9%;FxB8NC(O-McK(QcqZwSm8w(AYF|PXitac7)FgEqE-U;IhQXZ zzq!;7Pd95mc}nr9z4oFQAcx*KLoOv-Y#GMw(;4~ncbFj+E^+-LV%GTYAyoGPw>S?_ zWEI7{FKkj>ocooQi0VE+Br}-xurl}@weqv|ddMz9N7c3Y$3WwY5Eoq<-pSdy9yNIW z>A5+J;3{)SV9jkxgW{I>o&odG+xb8F0C_j0QJ&OtiMp*!H9lR9onpoUzVT#Y`&*Q) zP)0ffBsF;+H$U-!omH3>Gy#d7Jmkqbg`dQh+T=j2^a(mhyzn0Xu%lK8qXOtbNDL4% zX{B3##EE(&lyqf;f(xB*jBo~;ahN9Jbu|daS~D2;$VYf1h$=l#bWXQwM!f4)ZUk%7 zQ22|h*k-J14)cAkD~wytqwHn~BCZeAYDcVNQ|OJuNwmhN!21=0UFQ(<3*Qh_{Yd{! zdpRDdK7(jXu|6{TCo|n~+o|aYtnOvR?A*T??@eQJWH~9-qk4bRQVjk;fX26u(eY@g z!Xdya*X7fa%_tRyX=+SFRf;npB;|NH(iLd4;|-0ysz}20gDel$`=34l8nL^JpQ$>2 zZ!n2@{|gDzT()YE!Lm$g4)wdNgS2R|yTfTNbSYTWMgtCH-*;FAsqvyOz!ZSg@iB9XL7MHgDmyR2)vdM4Vb*uf^Ps+?7aND;;>a!P4CtRO{cZ zNNFh~L7k%^SUp{=!PWY2<~bGk85PR86(4&m;Y8(TzviN`LD|F9FmM*N-?Wie-Y8(T zU1%Yu)X+)O2wo1*C4=TQGO!r-zo0Kdr@y6=)nw<}+Khd`nCanfBIk6d8lSMH3h@as z3A{tr$lX}#_iq~gok@riMFNJ}8`Xy!i2VIU8ftAnuY`9+6*bqrF)zM9q9Sb9gQ@Dy zc)7Zaq&VVkELz*Uzppk@H_)p)pTgBQLPguk%|C3ugG+nWG#d7hy}+npb9|w8UY`;_AfRYeA-B4Wt?98jELIt8ctK2Dy6l`=h%i3wf1@-FjyR@`OC5 zPJ51Vq`x+P)a?07mUh+E5O~Bec-gv#VU_(zkFg5Jfn#FCY~(@-FG;T=Te!R!bqI_w|Yx zq`1#A=@~u}R9W_36HgGfEyk%iEI;kWM;^<;U@Fi8#G!4Li>|-0IK^FLhWH~C_*IfI zTY{Ig87U^FUG{fVu<@#IOH2L`a2Gh}JPNM_+#d&7-1>8u*`Cjrw~<4YefFC8j9O}( zefm6P6ErpUsNy4kM5o5xX80_;Q;5(ODDi7|cJ(&741pGK)?B*A;nestpwT0|Q<=P^ zKI0oQba}mZ`ubdRKzG>dzj0k?L~q(xLEyH>Q>rYJuJR|9;IQHQ{~8K%bG0V$#^sO& zZ#J$@=;lipAB!NZB;$x7gn-LUZ}NJi~(#{Y~dBV@B(ZV2f3l?#LilbwDejsdP(ZthnwUs}L2xE#0f$r9Z z=0&pIY*U1w)^iUO0P3pl0&=4qt@g`t8Up_Yp=AyTzHj%hJmH1oF7+FeMxrV=XdAAN zDa19LXzm0RD5Ne1bp0PA|4xInxurv52Blb6UnJ-kSL-z75HzaxaEz%XM{G2b^y8*u==H4UKMFu%5 zDa|nYFt~-IBQgCP6N3vk$z@6>iHQp#eW$$(WO|Fi)I0E#wf$~)P_IFzTR%QL*VVkV z-J&*W^;=57@I2Qq)IW$v7i&V&skf_CPIr8HFAOt6UCjg&CXBZAnD9f?gA(yn2BylA zYQtlRRa8bhD_Y!V{e`Mzm0;Q;NC==%aLEGVdjaGD>6~-qZ+V7%+zy_y&wHP#&iHk0 z==HA_S+<%+4^_Ut?ZTW8s_%RA%~tMvHzCKr!J(D5O~Om13V;Z7h?IRzrjCa9&*1D5 zG4h@>^3D-?V1=$@J~*dioKI7jt2}6Vzo-<^@W*+t$D*TSx8peJ7==Q(;B9Drd)dJ^ zvpPhx4h}F?*P<4*Vm6;q?Xt9{uO3|ZV0}~7`S$FjBY#lU!gXOlUC$Nbx+AG<)Eya; zI)^&Q-j}tBw6R@2y@1@BqJD>g;^i`0MK=p-n(4gXX$t5igpp@+X`d)?q_;ITW8CsJ9vY|_*0d{dk_r4%rSyjWqm)<-e#pMEya-a6$)8tnEifx(P zW>A%|D4X!+o4HZpJRI4E8j&VmUrHUe*E|6I)h(CW+E58QPK@COb!iEa+|-=OBehMB zmnWcXWrqxBTpu)l=D?VN#+8WvDWy^w@*uIL>49S#+Jx<|9=9)vp?9Of0e6$|;hI6R zqm|Li_)6pd$1{94qBFlE{kzf_?0wJtb=&T>@|Hd)pRH`lEtmNS*!Mu_ zZ0GY+2p2M0H2u+iur3Y2_=A6vb}LUYz6Ng-*?2aCyzOr!XMwyZ8P(KDA>j9t4KXc( zRjwO2Gji|33~LiD2Zx_q=?S!)R$&?4&?5Dp>QL&FOR*$J(4OA|dMc@*4=q29)KT0e zK*!ji^5q&LlIrv_NtNn{BW`1*HiyBl*L$Z~E!{eYHUCt0ImqOhE+ zR9y~_c6;gFYRoldoyB$no!guu)Pp*|SJ_{L1%&<7pZfhRueXoToTNV-3~ChHmM7ID zd}vkh0sFO!MI9RxF4m`PV-j)4++MV)-p81IuF;W{$Dd0t7_Pwux*3Zy_1kLwL(X%? zr`$%`;e8MCBukBn2hiVOzW6$H`{cBUeDCfxvZZ6eLeVm`s`|=$}o=IA8WdK{m}vy|_{eX<=b4vv9D|5{e^gYj3SBI@>)Rvbq-P z-Io`+gLt)%@7OA=!H#zIOz6So-s}i27|_!EA6G4F8C0@2r^+uU3_JlEpekzq)>dtG z{5Ab=p7v>fa+TkDpmq4!WhQ#1kNO|bIn|rdC^KsC3V!FWdb$)uY*)L#>_#8G&6gSk zFYe%WcED=r%KU1bOlRd@n75k;Jn8x#D=RJld7az@K61l%j;VEAZ}nhzY`;$yQtz}6 zUDsY1zw-2`R*RuOQ%z~JL^;gXvsur9b+ab^Y+V}J%AOtJPuz;cH1qm+`gp;xq@T*c z)8sP$u1grYRm0gP>l*hq-leV(3cTg1x<4KLxaJ*MYM9k9XX#$*W25B$r%D|z@GbO~jduwqwmumV ztE*(2gj$lJK-f%}yy9Z<>Nc5xzLTX2LPMZvcn-7I`{W{($C2<tXOq~iLMyRIG8+AF>wH(K-a8w&Xc z4mc*N93^j603{^~$;C@y6#PHAht_vii>|Qcj^cRSXYI~I3a=|=kuw&pm-RSRV&);= zEJ+V$l3hBqfesgrEzS`Nq0H#)=x5lDi8IHHU{`YKA`-4|%21xMssW#Y^Kig$1&#mg zm{EL?8%E{Lt^Vt6=EJNI5id_7zIRXt?OW#h{l5NzqHsHv$fTEX`75Uk6;?@dMC9B& zRq!kn3%4~d-+57n3RKu)6;1z)FkxskQy`unAq`L+H*gzZpSY=Wq5WWkL{T#L%u*vA{q-vsoDdRZ$v#6cn@+0OZ*7EIi_i< zDuMDeDx|#-59DjnSl693=F89u>=Zm8R|m@IR*3u8?9Gy$b=KRI^6F09GKtkVg=5g| zRNZ$EUH0&qb418rW_`(2l7QSkRv~zuZO({O7`xr~AkX?#itD6Q%*#peCfHVy7zd;X zk$87zc{m092WSTAX@6yfP9A@QC-(fdxgU@mkw{NRQNQQFqA)L>mTKUp*GPZ5hgZMs zvB|4!U;@U6?9TpZpU(L7euDFq@({$>zm5StlBP9v=o4Q3ZHw$loqjki;fcTZx}7#{ zTp(~@e5V`|?T`Z>YFtlgANgXJw;sP6>WmhnCZXKu z+>pGQ@UQN|7{dc9P=3+Sa!XRu?J}-^@g*FJ_xMi^ z(91-c9|X)HNatX2r(sgY-H^T}op(-I7oE4qEtP8|7tp=-;JcnN7sJ#5BkkT?!2+xE`7CBSlTfZ>o5l~SP{BtzC%b?uMQpwK9L%oFHps= z@nX43Ur0mV9`3&4`}(j~JxQj%n&i%$|%#5Hs%O`--OMx@PHc_%8uR)NjEG{~Fn#v4JJHyCDk zDI(w^qWX@>IQty{KF_~6<2gZN+SewkL5ax0IXep|QmHs{+jpVBKt*3xtQ0_CR$Nvw z6Yw3lb-=o{J1_JO;_m#0_B3LE&vA{}lX(AJ@VW!_#b;`PtW8UrtV7M#Qy%BILan_I z+s}L;;7#zHyWmhQ1{A6q^J6hv6+Y97G4^A%BNkaiLQGP{WYEd1^nSn>?ONb7+!B3a zGw|l<3279xFmV!b>HTV+(I0xD4zz$tuKd0=F824VVJo&sG#*h?(WL3IbFOO5;O-eS z=TcD$<}f6Qs5B^aqE!D$S{*Cni@?X#94&J+%5${PafDK*X?P+oSF284Z1=ElrAeAS zRhwk&VAibAq4fh36RS`)E=+8vov1Su>2z*x%Js1JCH0Kk zR{0wGf_QmRMZVw6tFrHyHA}oZn!I7QYyk ze9sbo8wDHZdi85$>(N~XKT5D}vQ~7`pyIU(|6=h9bK|Zy_{xUk4-s>%Q4O{PWbJgV z#)|kYbrQ%qon@SJPgLp)ozp~s;jm6F2v`>q45L_#HXNQ(FB)Bh9J2r2gj zl6p0(xn!kdVS&|av%YuUPMKa>J{HDtA!f{fg|$?67RtwtW`*|_6yd$Bv81JPe%~c~ z7&ZHiU=J+TCOTQdqs+s~%rW8NNLr`esywZ%fDg|S9xewaJY4yNhl}k2H^5hq1+!&r z^=7RbKdZdB3JzC~>wV;r;vGnLnf;%abcMdZe{m7}S>^xD@H`;uMcnrhiG07mArYPzN(n2I8qJ_AS7OsL8L--fC3i`1K zBV4qIR#FflE?S5nE=8h+hJlC+sn@wrC&&kfbKm#wJ>R+K-se0*^ys(53ZPn0RzFb_ z7mz#X(%bN33EWccsHr6k5h=A2)#ECGk~7VE2P*mrE}B^c>H}Q4iHfYE)7eF!_Ea~js*P)(!pJ;oPMtVL+$EWCN^C|uL?4(bpvvWV(CYLuhiz&e($ij)j~|VX;JfRX zlx2=L|A*=bcT@&_>|0lv@C7p;lW+WX4k{hyLV%>z5k*~%9M3Xm(&4`84fp>7TG^-K0C?JCV1NTB zh7yJn#stP)%x=tS%!`=su{5w8V)@4^!Ro|X!g`JM4_gyE6MGo@F%B<|22M53HJq=w zoVdEU)^L5|PT{fOS;MQr+rxW}Pl(Th?*u;szZ`!a{|)|s0#O3<1Q`VV1fL1{2|W-N z5DpPuCc+}JOyq~CiD-i8B+(;cd}3N+F=B_rRm5AwpGgErY>;%3oF;ieN>8dwYK63t zbdmHq86%k~GACrCWT(mAlQWQ8AnzbQN&bw2jY5RN2}KtmyryKMbV8X;`H=D{l{l3O zRUOqD)fUwr)qQF_Y9?wGY7J@~>T>FO>UQdW>POTssNYk6rT$HWNuxlcMx#Sxn#LlH zbsD=gPH0@wc%bQ}8K#+}S)f^?*`YZ_OHW%wTR~e-+e+I_J4kzxj)IPkj+Ks^PLNKV zPL@uYPLs|AojE!ybROx-=xXSi=sM{7=tk(K=oaYK=yvEO>E-EF>9y%i(wnC*pf96u zr9Vf1h5i-;CIbr`FvuXspv_>O!8U_WhDL@3hBu7djJ6m(GPVN39^(flLO^)LG6%UYmZmNYNcx6R`YwRN6e)S#QIh?Qa90l=>^xEv4_ri=6bQZ4f)sIJ68_0k#!sGy^UrC zt`f;5c20rn*_Qj8Q>jA5)&(yjC;y~EjrHcp2$_0A)?h5!(9s0d9if1EM_fbcR(k#b!N8Nj0C?Ix z&N~i*KoADtUBKmyJeF9QppC^g#4`X5g~p0a^aS?SHYPTDmchz+IUQt|ZoWS=f96fU zH=qyFg0t^>N}sZCnCMmz%NF8-3;v_UJ-8z-7Nw2oXW55Fx@yVUCFG5fozeV}faXHbieW7i5Y| cwi7dCT2AE1Y&|hY@?28_7eB@SP5b}=0EP7&bN~PV literal 0 HcmV?d00001 diff --git a/src/vs/base/test/node/mime/fixtures/some.shiftjis.txt b/src/vs/base/test/node/mime/fixtures/some.shiftjis.txt new file mode 100644 index 0000000000..60a14566c2 --- /dev/null +++ b/src/vs/base/test/node/mime/fixtures/some.shiftjis.txt @@ -0,0 +1 @@ +VSCODE͍ō̃GfB^B \ No newline at end of file diff --git a/src/vs/base/test/node/mime/fixtures/some.xml.png b/src/vs/base/test/node/mime/fixtures/some.xml.png new file mode 100644 index 0000000000..1a5b8e2915 --- /dev/null +++ b/src/vs/base/test/node/mime/fixtures/some.xml.png @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/vs/base/test/node/mime/mime.test.ts b/src/vs/base/test/node/mime/mime.test.ts new file mode 100644 index 0000000000..f242a68342 --- /dev/null +++ b/src/vs/base/test/node/mime/mime.test.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import assert = require('assert'); + +import mimeCommon = require('vs/base/common/mime'); +import mime = require('vs/base/node/mime'); + +suite('Mime', () => { + + test('detectMimesFromFile (JSON saved as PNG)', function (done: (err?: any) => void) { + const file = require.toUrl('./fixtures/some.json.png'); + mime.detectMimesFromFile(file).then(mimes => { + assert.deepEqual(mimes.mimes, ['text/plain']); + done(); + }, done); + }); + + test('detectMimesFromFile (PNG saved as TXT)', function (done: (err?: any) => void) { + mimeCommon.registerTextMime({ id: 'text', mime: 'text/plain', extension: '.txt' }); + const file = require.toUrl('./fixtures/some.png.txt'); + mime.detectMimesFromFile(file).then(mimes => { + assert.deepEqual(mimes.mimes, ['text/plain', 'application/octet-stream']); + done(); + }, done); + }); + + test('detectMimesFromFile (XML saved as PNG)', function (done: (err?: any) => void) { + const file = require.toUrl('./fixtures/some.xml.png'); + mime.detectMimesFromFile(file).then(mimes => { + assert.deepEqual(mimes.mimes, ['text/plain']); + done(); + }, done); + }); + + test('detectMimesFromFile (QWOFF saved as TXT)', function (done: (err?: any) => void) { + const file = require.toUrl('./fixtures/some.qwoff.txt'); + mime.detectMimesFromFile(file).then(mimes => { + assert.deepEqual(mimes.mimes, ['text/plain', 'application/octet-stream']); + done(); + }, done); + }); + + test('detectMimesFromFile (CSS saved as QWOFF)', function (done: (err?: any) => void) { + const file = require.toUrl('./fixtures/some.css.qwoff'); + mime.detectMimesFromFile(file).then(mimes => { + assert.deepEqual(mimes.mimes, ['text/plain']); + done(); + }, done); + }); + + test('detectMimesFromFile (PDF)', function (done: () => void) { + const file = require.toUrl('./fixtures/some.pdf'); + mime.detectMimesFromFile(file).then(mimes => { + assert.deepEqual(mimes.mimes, ['application/octet-stream']); + done(); + }, done); + }); + + test('autoGuessEncoding (ShiftJIS)', function (done: () => void) { + const file = require.toUrl('./fixtures/some.shiftjis.txt'); + mime.detectMimesFromFile(file, { autoGuessEncoding: true }).then(mimes => { + assert.equal(mimes.encoding, 'shiftjis'); + done(); + }, done); + }); + + test('autoGuessEncoding (CP1252)', function (done: () => void) { + const file = require.toUrl('./fixtures/some.cp1252.txt'); + mime.detectMimesFromFile(file, { autoGuessEncoding: true }).then(mimes => { + assert.equal(mimes.encoding, 'windows1252'); + done(); + }, done); + }); +}); diff --git a/src/vs/base/test/node/pfs.test.ts b/src/vs/base/test/node/pfs.test.ts new file mode 100644 index 0000000000..063767fa60 --- /dev/null +++ b/src/vs/base/test/node/pfs.test.ts @@ -0,0 +1,147 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; + +import assert = require('assert'); +import os = require('os'); + +import path = require('path'); +import fs = require('fs'); + +import uuid = require('vs/base/common/uuid'); +import extfs = require('vs/base/node/extfs'); +import { onError } from 'vs/base/test/common/utils'; +import * as pfs from 'vs/base/node/pfs'; + +suite('PFS', () => { + + test('writeFile', function (done: () => void) { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'writefile.txt'); + + extfs.mkdirp(newDir, 493, (error) => { + if (error) { + return onError(error, done); + } + + assert.ok(fs.existsSync(newDir)); + + pfs.writeFile(testFile, 'Hello World', null).done(() => { + assert.equal(fs.readFileSync(testFile), 'Hello World'); + + extfs.del(parentDir, os.tmpdir(), () => { }, done); + }, error => onError(error, done)); + }); + }); + + test('writeFile - parallel write on different files works', function (done: () => void) { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile1 = path.join(newDir, 'writefile1.txt'); + const testFile2 = path.join(newDir, 'writefile2.txt'); + const testFile3 = path.join(newDir, 'writefile3.txt'); + const testFile4 = path.join(newDir, 'writefile4.txt'); + const testFile5 = path.join(newDir, 'writefile5.txt'); + + extfs.mkdirp(newDir, 493, (error) => { + if (error) { + return onError(error, done); + } + + assert.ok(fs.existsSync(newDir)); + + TPromise.join([ + pfs.writeFile(testFile1, 'Hello World 1', null), + pfs.writeFile(testFile2, 'Hello World 2', null), + pfs.writeFile(testFile3, 'Hello World 3', null), + pfs.writeFile(testFile4, 'Hello World 4', null), + pfs.writeFile(testFile5, 'Hello World 5', null) + ]).done(() => { + assert.equal(fs.readFileSync(testFile1), 'Hello World 1'); + assert.equal(fs.readFileSync(testFile2), 'Hello World 2'); + assert.equal(fs.readFileSync(testFile3), 'Hello World 3'); + assert.equal(fs.readFileSync(testFile4), 'Hello World 4'); + assert.equal(fs.readFileSync(testFile5), 'Hello World 5'); + + extfs.del(parentDir, os.tmpdir(), () => { }, done); + }, error => onError(error, done)); + }); + }); + + test('writeFile - parallel write on same files works and is sequentalized', function (done: () => void) { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'writefile.txt'); + + extfs.mkdirp(newDir, 493, (error) => { + if (error) { + return onError(error, done); + } + + assert.ok(fs.existsSync(newDir)); + + TPromise.join([ + pfs.writeFile(testFile, 'Hello World 1', null), + pfs.writeFile(testFile, 'Hello World 2', null), + TPromise.timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 3', null)), + pfs.writeFile(testFile, 'Hello World 4', null), + TPromise.timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 5', null)) + ]).done(() => { + assert.equal(fs.readFileSync(testFile), 'Hello World 5'); + + extfs.del(parentDir, os.tmpdir(), () => { }, done); + }, error => onError(error, done)); + }); + }); + + test('rimraf - simple', function (done: () => void) { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'extfs', id); + + extfs.mkdirp(newDir, 493, (error) => { + if (error) { + return onError(error, done); + } + + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + + pfs.rimraf(newDir).then(() => { + assert.ok(!fs.existsSync(newDir)); + done(); + }, error => onError(error, done)); + }); // 493 = 0755 + }); + + test('rimraf - recursive folder structure', function (done: () => void) { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'extfs', id); + + extfs.mkdirp(newDir, 493, (error) => { + if (error) { + return onError(error, done); + } + + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + + fs.mkdirSync(path.join(newDir, 'somefolder')); + fs.writeFileSync(path.join(newDir, 'somefolder', 'somefile.txt'), 'Contents'); + + pfs.rimraf(newDir).then(() => { + assert.ok(!fs.existsSync(newDir)); + done(); + }, error => onError(error, done)); + }); // 493 = 0755 + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/node/port.test.ts b/src/vs/base/test/node/port.test.ts new file mode 100644 index 0000000000..a92affb07a --- /dev/null +++ b/src/vs/base/test/node/port.test.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as net from 'net'; +import ports = require('vs/base/node/ports'); + +suite('Ports', () => { + test('Finds a free port (no timeout)', function (done: () => void) { + this.timeout(1000 * 10); // higher timeout for this test + + if (process.env['VSCODE_PID']) { + return done(); // this test fails when run from within VS Code + } + + // get an initial freeport >= 7000 + ports.findFreePort(7000, 100, 300000, (initialPort) => { + assert.ok(initialPort >= 7000); + + // create a server to block this port + const server = net.createServer(); + server.listen(initialPort, null, null, () => { + + // once listening, find another free port and assert that the port is different from the opened one + ports.findFreePort(7000, 50, 300000, (freePort) => { + assert.ok(freePort >= 7000 && freePort !== initialPort); + server.close(); + + done(); + }); + }); + }); + }); +}); diff --git a/src/vs/base/test/node/processes/fixtures/fork.ts b/src/vs/base/test/node/processes/fixtures/fork.ts new file mode 100644 index 0000000000..63ad74fc1e --- /dev/null +++ b/src/vs/base/test/node/processes/fixtures/fork.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import processes = require('vs/base/node/processes'); + +const sender = processes.createQueuedSender(process); + +process.on('message', msg => { + sender.send(msg); +}); + +sender.send('ready'); \ No newline at end of file diff --git a/src/vs/base/test/node/processes/fixtures/fork_large.ts b/src/vs/base/test/node/processes/fixtures/fork_large.ts new file mode 100644 index 0000000000..23692f0b90 --- /dev/null +++ b/src/vs/base/test/node/processes/fixtures/fork_large.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import processes = require('vs/base/node/processes'); + +const sender = processes.createQueuedSender(process); + +process.on('message', msg => { + sender.send(msg); + sender.send(msg); + sender.send(msg); + sender.send('done'); +}); + +sender.send('ready'); \ No newline at end of file diff --git a/src/vs/base/test/node/processes/processes.test.ts b/src/vs/base/test/node/processes/processes.test.ts new file mode 100644 index 0000000000..1218555c70 --- /dev/null +++ b/src/vs/base/test/node/processes/processes.test.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as cp from 'child_process'; +import * as objects from 'vs/base/common/objects'; +import * as platform from 'vs/base/common/platform'; +import URI from 'vs/base/common/uri'; +import processes = require('vs/base/node/processes'); + +function fork(id: string): cp.ChildProcess { + const opts: any = { + env: objects.mixin(objects.clone(process.env), { + AMD_ENTRYPOINT: id, + PIPE_LOGGING: 'true', + VERBOSE_LOGGING: true + }) + }; + + return cp.fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=processTests'], opts); +} + +suite('Processes', () => { + test('buffered sending - simple data', function (done: () => void) { + if (process.env['VSCODE_PID']) { + return done(); // this test fails when run from within VS Code + } + + const child = fork('vs/base/test/node/processes/fixtures/fork'); + const sender = processes.createQueuedSender(child); + + let counter = 0; + + const msg1 = 'Hello One'; + const msg2 = 'Hello Two'; + const msg3 = 'Hello Three'; + + child.on('message', msgFromChild => { + if (msgFromChild === 'ready') { + sender.send(msg1); + sender.send(msg2); + sender.send(msg3); + } else { + counter++; + + if (counter === 1) { + assert.equal(msgFromChild, msg1); + } else if (counter === 2) { + assert.equal(msgFromChild, msg2); + } else if (counter === 3) { + assert.equal(msgFromChild, msg3); + + child.kill(); + done(); + } + } + }); + }); + + test('buffered sending - lots of data (potential deadlock on win32)', function (done: () => void) { + if (!platform.isWindows || process.env['VSCODE_PID']) { + return done(); // test is only relevant for Windows and seems to crash randomly on some Linux builds + } + + const child = fork('vs/base/test/node/processes/fixtures/fork_large'); + const sender = processes.createQueuedSender(child); + + const largeObj = Object.create(null); + for (let i = 0; i < 10000; i++) { + largeObj[i] = 'some data'; + } + + const msg = JSON.stringify(largeObj); + child.on('message', msgFromChild => { + if (msgFromChild === 'ready') { + sender.send(msg); + sender.send(msg); + sender.send(msg); + } else if (msgFromChild === 'done') { + child.kill(); + done(); + } + }); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/node/stream/fixtures/empty.txt b/src/vs/base/test/node/stream/fixtures/empty.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/vs/base/test/node/stream/fixtures/file.css b/src/vs/base/test/node/stream/fixtures/file.css new file mode 100644 index 0000000000..c5cea74684 --- /dev/null +++ b/src/vs/base/test/node/stream/fixtures/file.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------- +The base color for this template is #5c87b2. If you'd like +to use a different color start by replacing all instances of +#5c87b2 with your new color. +----------------------------------------------------------*/ +body +{ + background-color: #5c87b2; + font-size: .75em; + font-family: Segoe UI, Verdana, Helvetica, Sans-Serif; + margin: 8px; + padding: 0; + color: #696969; +} + +h1, h2, h3, h4, h5, h6 +{ + color: #000; + font-size: 40px; + margin: 0px; +} + +textarea +{ + font-family: Consolas +} + +#results +{ + margin-top: 2em; + margin-left: 2em; + color: black; + font-size: medium; +} + diff --git a/src/vs/base/test/node/stream/stream.test.ts b/src/vs/base/test/node/stream/stream.test.ts new file mode 100644 index 0000000000..fe83578fcc --- /dev/null +++ b/src/vs/base/test/node/stream/stream.test.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import assert = require('assert'); +import fs = require('fs'); + +import stream = require('vs/base/node/stream'); + +suite('Stream', () => { + test('readExactlyByFile - ANSI', function (done: (err?) => void) { + const file = require.toUrl('./fixtures/file.css'); + + stream.readExactlyByFile(file, 10).then(({ buffer, bytesRead }) => { + assert.equal(bytesRead, 10); + assert.equal(buffer.toString(), '/*--------'); + done(); + }, done); + }); + + test('readExactlyByFile - empty', function (done: (err?: any) => void) { + const file = require.toUrl('./fixtures/empty.txt'); + + stream.readExactlyByFile(file, 10).then(({ bytesRead }) => { + assert.equal(bytesRead, 0); + done(); + }, done); + }); + + test('readExactlyByStream - ANSI', function (done: (err?: any) => void) { + const file = require.toUrl('./fixtures/file.css'); + + stream.readExactlyByStream(fs.createReadStream(file), 10).then(({ buffer, bytesRead }) => { + assert.equal(bytesRead, 10); + assert.equal(buffer.toString(), '/*--------'); + done(); + }, done); + }); + + test('readExactlyByStream - empty', function (done: (err?: any) => void) { + const file = require.toUrl('./fixtures/empty.txt'); + + stream.readExactlyByStream(fs.createReadStream(file), 10).then(({ bytesRead }) => { + assert.equal(bytesRead, 0); + done(); + }, done); + }); + + test('readToMatchingString - ANSI', function (done: (err?: any) => void) { + const file = require.toUrl('./fixtures/file.css'); + + stream.readToMatchingString(file, '\n', 10, 100).then((result: string) => { + // \r may be present on Windows + assert.equal(result.replace('\r', ''), '/*---------------------------------------------------------------------------------------------'); + done(); + }, done); + }); + + test('readToMatchingString - empty', function (done: (err?: any) => void) { + const file = require.toUrl('./fixtures/empty.txt'); + + stream.readToMatchingString(file, '\n', 10, 100).then((result: string) => { + assert.equal(result, null); + + done(); + }, done); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/node/zip/fixtures/extract.zip b/src/vs/base/test/node/zip/fixtures/extract.zip new file mode 100644 index 0000000000000000000000000000000000000000..96a008781c5492df736ef239d4d750dd8830f636 GIT binary patch literal 146 zcmWIWW@h1H00Du9XC7b%l;C0zU`VYfNzE(H%+J#gjo@KeEm|ASz+W5wl~IHtz?+dt klo^*!9+*K6ZyiBQWOF#+<^*`NvVmk7fzSm=TYxwW0N&;oDF6Tf literal 0 HcmV?d00001 diff --git a/src/vs/base/test/node/zip/zip.test.ts b/src/vs/base/test/node/zip/zip.test.ts new file mode 100644 index 0000000000..f6e7df6220 --- /dev/null +++ b/src/vs/base/test/node/zip/zip.test.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as path from 'path'; +import * as os from 'os'; +import URI from 'vs/base/common/uri'; +import { extract } from 'vs/base/node/zip'; +import { generateUuid } from 'vs/base/common/uuid'; +import { rimraf, exists } from 'vs/base/node/pfs'; + +const fixtures = URI.parse(require.toUrl('./fixtures')).fsPath; + +suite('Zip', () => { + + test('extract should handle directories', () => { + const fixture = path.join(fixtures, 'extract.zip'); + const target = path.join(os.tmpdir(), generateUuid()); + + return extract(fixture, target) + .then(() => exists(path.join(target, 'extension'))) + .then(exists => assert(exists)) + .then(() => rimraf(target)); + }); +}); \ No newline at end of file diff --git a/src/vs/base/worker/defaultWorkerFactory.ts b/src/vs/base/worker/defaultWorkerFactory.ts new file mode 100644 index 0000000000..56f6479922 --- /dev/null +++ b/src/vs/base/worker/defaultWorkerFactory.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { globals } from 'vs/base/common/platform'; +import { logOnceWebWorkerWarning, IWorker, IWorkerCallback, IWorkerFactory } from 'vs/base/common/worker/simpleWorker'; + +// Option for hosts to overwrite the worker script url (used in the standalone editor) +const getCrossOriginWorkerScriptUrl: (workerId: string, label: string) => string = environment('getWorkerUrl', null); + +function environment(name: string, fallback: any = false): any { + if (globals.MonacoEnvironment && globals.MonacoEnvironment.hasOwnProperty(name)) { + return globals.MonacoEnvironment[name]; + } + + return fallback; +} + +function defaultGetWorkerUrl(workerId: string, label: string): string { + return require.toUrl('./' + workerId) + '#' + label; +} +var getWorkerUrl = getCrossOriginWorkerScriptUrl || defaultGetWorkerUrl; + +/** + * A worker that uses HTML5 web workers so that is has + * its own global scope and its own thread. + */ +class WebWorker implements IWorker { + + private id: number; + private worker: Worker; + + constructor(moduleId: string, id: number, label: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void) { + this.id = id; + this.worker = new Worker(getWorkerUrl('workerMain.js', label)); + this.postMessage(moduleId); + this.worker.onmessage = function (ev: any) { + onMessageCallback(ev.data); + }; + if (typeof this.worker.addEventListener === 'function') { + this.worker.addEventListener('error', onErrorCallback); + } + } + + public getId(): number { + return this.id; + } + + public postMessage(msg: string): void { + if (this.worker) { + this.worker.postMessage(msg); + } + } + + public dispose(): void { + this.worker.terminate(); + this.worker = null; + } +} + +export class DefaultWorkerFactory implements IWorkerFactory { + + private static LAST_WORKER_ID = 0; + + private _label: string; + private _webWorkerFailedBeforeError: any; + + constructor(label: string) { + this._label = label; + this._webWorkerFailedBeforeError = false; + } + + public create(moduleId: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker { + let workerId = (++DefaultWorkerFactory.LAST_WORKER_ID); + + if (this._webWorkerFailedBeforeError) { + throw this._webWorkerFailedBeforeError; + } + + return new WebWorker(moduleId, workerId, this._label || 'anonymous' + workerId, onMessageCallback, (err) => { + logOnceWebWorkerWarning(err); + this._webWorkerFailedBeforeError = err; + onErrorCallback(err); + }); + } +} diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts new file mode 100644 index 0000000000..53a0fdd296 --- /dev/null +++ b/src/vs/base/worker/workerMain.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +(function () { + 'use strict'; + + let MonacoEnvironment = (self).MonacoEnvironment; + let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../'; + + if (typeof (self).define !== 'function' || !(self).define.amd) { + importScripts(monacoBaseUrl + 'vs/loader.js'); + } + + require.config({ + baseUrl: monacoBaseUrl, + catchError: true + }); + + let loadCode = function (moduleId: string) { + require([moduleId], function (ws) { + setTimeout(function () { + let messageHandler = ws.create((msg: any) => { + (self).postMessage(msg); + }, null); + + self.onmessage = (e) => messageHandler.onmessage(e.data); + while (beforeReadyMessages.length > 0) { + self.onmessage(beforeReadyMessages.shift()); + } + }, 0); + }); + }; + + let isFirstMessage = true; + let beforeReadyMessages: MessageEvent[] = []; + self.onmessage = (message) => { + if (!isFirstMessage) { + beforeReadyMessages.push(message); + return; + } + + isFirstMessage = false; + loadCode(message.data); + }; +})(); diff --git a/src/vs/buildunit.json b/src/vs/buildunit.json new file mode 100644 index 0000000000..88fe75c555 --- /dev/null +++ b/src/vs/buildunit.json @@ -0,0 +1,13 @@ +{ + "name": "vs", + "dependencies": [ + ], + "libs": [ + "lib.core.d.ts" + ], + "sources": [ + ], + "declares": [ + "vs/nls.d.ts" + ] +} \ No newline at end of file diff --git a/src/vs/code/buildfile.js b/src/vs/code/buildfile.js new file mode 100644 index 0000000000..c5548d9afe --- /dev/null +++ b/src/vs/code/buildfile.js @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +function createModuleDescription(name, exclude) { + var result= {}; + var excludes = ['vs/css', 'vs/nls']; + result.name= name; + if (Array.isArray(exclude) && exclude.length > 0) { + excludes = excludes.concat(exclude); + } + result.exclude= excludes; + return result; +} + +exports.collectModules= function() { + return [ + createModuleDescription('vs/code/electron-main/main', []), + createModuleDescription('vs/code/node/cli', []), + createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']), + createModuleDescription('vs/code/electron-browser/sharedProcessMain', []) + ]; +}; \ No newline at end of file diff --git a/src/vs/code/electron-browser/sharedProcess.html b/src/vs/code/electron-browser/sharedProcess.html new file mode 100644 index 0000000000..be70dede20 --- /dev/null +++ b/src/vs/code/electron-browser/sharedProcess.html @@ -0,0 +1,17 @@ + + + + + + + + + + + Shared Process + + + + + + \ No newline at end of file diff --git a/src/vs/code/electron-browser/sharedProcess.js b/src/vs/code/electron-browser/sharedProcess.js new file mode 100644 index 0000000000..e08019842e --- /dev/null +++ b/src/vs/code/electron-browser/sharedProcess.js @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const path = require('path'); + +function assign(destination, source) { + return Object.keys(source) + .reduce(function (r, key) { r[key] = source[key]; return r; }, destination); +} + +function parseURLQueryArgs() { + const search = window.location.search || ''; + + return search.split(/[?&]/) + .filter(function (param) { return !!param; }) + .map(function (param) { return param.split('='); }) + .filter(function (param) { return param.length === 2; }) + .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); +} + +function createScript(src, onload) { + const script = document.createElement('script'); + script.src = src; + script.addEventListener('load', onload); + + const head = document.getElementsByTagName('head')[0]; + head.insertBefore(script, head.lastChild); +} + +function uriFromPath(_path) { + var pathName = path.resolve(_path).replace(/\\/g, '/'); + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = '/' + pathName; + } + + return encodeURI('file://' + pathName); +} + +function main() { + const args = parseURLQueryArgs(); + const configuration = JSON.parse(args['config'] || '{}') || {}; + + // Correctly inherit the parent's environment + assign(process.env, configuration.userEnv); + + // Get the nls configuration into the process.env as early as possible. + var nlsConfig = { availableLanguages: {} }; + const config = process.env['VSCODE_NLS_CONFIG']; + if (config) { + process.env['VSCODE_NLS_CONFIG'] = config; + try { + nlsConfig = JSON.parse(config); + } catch (e) { /*noop*/ } + } + + var locale = nlsConfig.availableLanguages['*'] || 'en'; + if (locale === 'zh-tw') { + locale = 'zh-Hant'; + } else if (locale === 'zh-cn') { + locale = 'zh-Hans'; + } + + window.document.documentElement.setAttribute('lang', locale); + + // Load the loader and start loading the workbench + const rootUrl = uriFromPath(configuration.appRoot) + '/out'; + + // In the bundled version the nls plugin is packaged with the loader so the NLS Plugins + // loads as soon as the loader loads. To be able to have pseudo translation + createScript(rootUrl + '/vs/loader.js', function () { + define('fs', ['original-fs'], function (originalFS) { return originalFS; }); // replace the patched electron fs with the original node fs for all AMD code + + window.MonacoEnvironment = {}; + + require.config({ + baseUrl: rootUrl, + 'vs/nls': nlsConfig, + nodeCachedDataDir: configuration.nodeCachedDataDir, + nodeModules: [/*BUILD->INSERT_NODE_MODULES*/] + }); + + if (nlsConfig.pseudo) { + require(['vs/nls'], function (nlsPlugin) { + nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); + }); + } + + require(['vs/code/electron-browser/sharedProcessMain'], function () { }); + }); +} + +main(); diff --git a/src/vs/code/electron-browser/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcessMain.ts new file mode 100644 index 0000000000..061379f60c --- /dev/null +++ b/src/vs/code/electron-browser/sharedProcessMain.ts @@ -0,0 +1,186 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import * as platform from 'vs/base/common/platform'; +import product from 'vs/platform/node/product'; +import pkg from 'vs/platform/node/package'; +import { serve, Server, connect } from 'vs/base/parts/ipc/node/ipc.net'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { IRequestService } from 'vs/platform/request/node/request'; +import { RequestService } from 'vs/platform/request/electron-browser/requestService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { resolveCommonProperties, machineIdStorageKey } from 'vs/platform/telemetry/node/commonProperties'; +import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc'; +import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; +import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; +import { IChoiceService } from 'vs/platform/message/common/message'; +import { ChoiceChannelClient } from 'vs/platform/message/common/messageIpc'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc'; +import { ipcRenderer } from 'electron'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { StorageService, inMemoryLocalStorageInstance } from 'vs/platform/storage/common/storageService'; + +interface ISharedProcessInitData { + sharedIPCHandle: string; + args: ParsedArgs; +} + +class ActiveWindowManager implements IDisposable { + private disposables: IDisposable[] = []; + private _activeWindowId: number; + + constructor( @IWindowsService windowsService: IWindowsService) { + windowsService.onWindowOpen(this.setActiveWindow, this, this.disposables); + windowsService.onWindowFocus(this.setActiveWindow, this, this.disposables); + } + + private setActiveWindow(windowId: number) { + this._activeWindowId = windowId; + } + + public get activeClientId(): string { + return `window:${this._activeWindowId}`; + } + + public dispose() { + this.disposables = dispose(this.disposables); + } +} + +const eventPrefix = 'monacoworkbench'; + +function main(server: Server, initData: ISharedProcessInitData): void { + const services = new ServiceCollection(); + + services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService, initData.args, process.execPath)); + services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); + services.set(IRequestService, new SyncDescriptor(RequestService)); + + const windowsChannel = server.getChannel('windows', { route: () => 'main' }); + const windowsService = new WindowsChannelClient(windowsChannel); + services.set(IWindowsService, windowsService); + + const activeWindowManager = new ActiveWindowManager(windowsService); + + const choiceChannel = server.getChannel('choice', { route: () => activeWindowManager.activeClientId }); + services.set(IChoiceService, new ChoiceChannelClient(choiceChannel)); + + const instantiationService = new InstantiationService(services); + + instantiationService.invokeFunction(accessor => { + const appenders: AppInsightsAppender[] = []; + + if (product.aiConfig && product.aiConfig.asimovKey) { + appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey)); + } + + // It is important to dispose the AI adapter properly because + // only then they flush remaining data. + process.once('exit', () => appenders.forEach(a => a.dispose())); + + const appender = combinedAppender(...appenders); + server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appender)); + + const services = new ServiceCollection(); + const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, extensionTestsPath } = accessor.get(IEnvironmentService); + + if (isBuilt && !extensionDevelopmentPath && product.enableTelemetry) { + const disableStorage = !!extensionTestsPath; // never keep any state when running extension tests! + const storage = disableStorage ? inMemoryLocalStorageInstance : window.localStorage; + const storageService = new StorageService(storage, storage); + + const config: ITelemetryServiceConfig = { + appender, + commonProperties: resolveCommonProperties(product.commit, pkg.version) + .then(result => Object.defineProperty(result, 'common.machineId', { + get: () => storageService.get(machineIdStorageKey), + enumerable: true + })), + piiPaths: [appRoot, extensionsPath] + }; + + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + } else { + services.set(ITelemetryService, NullTelemetryService); + } + + services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); + + const instantiationService2 = instantiationService.createChild(services); + + instantiationService2.invokeFunction(accessor => { + const extensionManagementService = accessor.get(IExtensionManagementService); + const channel = new ExtensionManagementChannel(extensionManagementService); + server.registerChannel('extensions', channel); + + // clean up deprecated extensions + (extensionManagementService as ExtensionManagementService).removeDeprecatedExtensions(); + }); + }); +} + +function setupIPC(hook: string): TPromise { + function setup(retry: boolean): TPromise { + return serve(hook).then(null, err => { + if (!retry || platform.isWindows || err.code !== 'EADDRINUSE') { + return TPromise.wrapError(err); + } + + // should retry, not windows and eaddrinuse + + return connect(hook, '').then( + client => { + // we could connect to a running instance. this is not good, abort + client.dispose(); + return TPromise.wrapError(new Error('There is an instance already running.')); + }, + err => { + // it happens on Linux and OS X that the pipe is left behind + // let's delete it, since we can't connect to it + // and the retry the whole thing + try { + fs.unlinkSync(hook); + } catch (e) { + return TPromise.wrapError(new Error('Error deleting the shared ipc hook.')); + } + + return setup(false); + } + ); + }); + } + + return setup(true); +} + +function startHandshake(): TPromise { + return new TPromise((c, e) => { + ipcRenderer.once('handshake:hey there', (_, r) => c(r)); + ipcRenderer.send('handshake:hello'); + }); +} + +function handshake(): TPromise { + return startHandshake() + .then((data) => setupIPC(data.sharedIPCHandle).then(server => main(server, data))) + .then(() => ipcRenderer.send('handshake:im ready')); +} + +handshake(); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts new file mode 100644 index 0000000000..cf8e5c6a69 --- /dev/null +++ b/src/vs/code/electron-main/app.ts @@ -0,0 +1,421 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { app, ipcMain as ipc, BrowserWindow } from 'electron'; +import * as platform from 'vs/base/common/platform'; +import { WindowsManager } from 'vs/code/electron-main/windows'; +import { IWindowsService, OpenContext } from 'vs/platform/windows/common/windows'; +import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; +import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; +import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { CodeMenu } from 'vs/code/electron-main/menus'; +import { getShellEnvironment } from 'vs/code/node/shellEnv'; +import { IUpdateService } from 'vs/platform/update/common/update'; +import { UpdateChannel } from 'vs/platform/update/common/updateIpc'; +import { UpdateService } from 'vs/platform/update/electron-main/updateService'; +import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main'; +import { Server, connect, Client } from 'vs/base/parts/ipc/node/ipc.net'; +import { SharedProcess } from 'vs/code/electron-main/sharedProcess'; +import { Mutex } from 'windows-mutex'; +import { LaunchService, LaunchChannel, ILaunchService } from './launch'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IStorageService } from 'vs/platform/storage/node/storage'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IURLService } from 'vs/platform/url/common/url'; +import { URLChannel } from 'vs/platform/url/common/urlIpc'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; +import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { CredentialsService } from 'vs/platform/credentials/node/credentialsService'; +import { CredentialsChannel } from 'vs/platform/credentials/node/credentialsIpc'; +import { resolveCommonProperties, machineIdStorageKey, machineIdIpcChannel } from 'vs/platform/telemetry/node/commonProperties'; +import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; +import product from 'vs/platform/node/product'; +import pkg from 'vs/platform/node/package'; +import { ProxyAuthHandler } from './auth'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; +import { IHistoryMainService } from 'vs/platform/history/common/history'; +import { isUndefinedOrNull } from 'vs/base/common/types'; +import { CodeWindow } from 'vs/code/electron-main/window'; +import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard'; +import URI from 'vs/base/common/uri'; +import { WorkspacesChannel } from 'vs/platform/workspaces/common/workspacesIpc'; +import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; +import { dirname, join } from 'path'; +import { touch } from 'vs/base/node/pfs'; + +// {{SQL CARBON EDIT}} +import { ProxyOAuthHandler } from 'sql/code/electron-main/oauth'; + +export class CodeApplication { + + private static APP_ICON_REFRESH_KEY = 'macOSAppIconRefresh'; + + private toDispose: IDisposable[]; + private windowsMainService: IWindowsMainService; + + private electronIpcServer: ElectronIPCServer; + + private sharedProcess: SharedProcess; + private sharedProcessClient: TPromise; + + constructor( + private mainIpcServer: Server, + private userEnv: platform.IProcessEnvironment, + @IInstantiationService private instantiationService: IInstantiationService, + @ILogService private logService: ILogService, + @IEnvironmentService private environmentService: IEnvironmentService, + @ILifecycleService private lifecycleService: ILifecycleService, + @IConfigurationService private configurationService: ConfigurationService, + @IStorageService private storageService: IStorageService, + @IHistoryMainService private historyService: IHistoryMainService + ) { + this.toDispose = [mainIpcServer, configurationService]; + + this.registerListeners(); + } + + private registerListeners(): void { + + // We handle uncaught exceptions here to prevent electron from opening a dialog to the user + process.on('uncaughtException', (err: any) => { + if (err) { + + // take only the message and stack property + const friendlyError = { + message: err.message, + stack: err.stack + }; + + // handle on client side + if (this.windowsMainService) { + this.windowsMainService.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); + } + } + + this.logService.error(`[uncaught exception in main]: ${err}`); + if (err.stack) { + this.logService.error(err.stack); + } + }); + + app.on('will-quit', () => { + this.logService.log('App#will-quit: disposing resources'); + + this.dispose(); + }); + + app.on('accessibility-support-changed', (event: Event, accessibilitySupportEnabled: boolean) => { + if (this.windowsMainService) { + this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled); + } + }); + + app.on('activate', (event: Event, hasVisibleWindows: boolean) => { + this.logService.log('App#activate'); + + // Mac only event: open new window when we get activated + if (!hasVisibleWindows && this.windowsMainService) { + this.windowsMainService.openNewWindow(OpenContext.DOCK); + } + }); + + const isValidWebviewSource = (source: string) => + !source || (URI.parse(source.toLowerCase()).toString() as any).startsWith(URI.file(this.environmentService.appRoot.toLowerCase()).toString()); + + app.on('web-contents-created', (event, contents) => { + contents.on('will-attach-webview', (event, webPreferences, params) => { + delete webPreferences.preload; + webPreferences.nodeIntegration = false; + + // Verify URLs being loaded + if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) { + return; + } + + // Otherwise prevent loading + this.logService.error('webContents#web-contents-created: Prevented webview attach'); + event.preventDefault(); + }); + + contents.on('will-navigate', event => { + this.logService.error('webContents#will-navigate: Prevented webcontent navigation'); + event.preventDefault(); + }); + }); + + let macOpenFiles: string[] = []; + let runningTimeout: number = null; + app.on('open-file', (event: Event, path: string) => { + this.logService.log('App#open-file: ', path); + event.preventDefault(); + + // Keep in array because more might come! + macOpenFiles.push(path); + + // Clear previous handler if any + if (runningTimeout !== null) { + clearTimeout(runningTimeout); + runningTimeout = null; + } + + // Handle paths delayed in case more are coming! + runningTimeout = setTimeout(() => { + if (this.windowsMainService) { + this.windowsMainService.open({ + context: OpenContext.DOCK /* can also be opening from finder while app is running */, + cli: this.environmentService.args, + pathsToOpen: macOpenFiles, + preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ + }); + macOpenFiles = []; + runningTimeout = null; + } + }, 100); + }); + + app.on('new-window-for-tab', () => { + this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button + }); + + ipc.on('vscode:exit', (event, code: number) => { + this.logService.log('IPC#vscode:exit', code); + + this.dispose(); + this.lifecycleService.kill(code); + }); + + ipc.on(machineIdIpcChannel, (event, machineId: string) => { + this.logService.log('IPC#vscode-machineId'); + this.storageService.setItem(machineIdStorageKey, machineId); + }); + + ipc.on('vscode:fetchShellEnv', (event, windowId) => { + const { webContents } = BrowserWindow.fromId(windowId); + getShellEnvironment().then(shellEnv => { + if (!webContents.isDestroyed()) { + webContents.send('vscode:acceptShellEnv', shellEnv); + } + }, err => { + if (!webContents.isDestroyed()) { + webContents.send('vscode:acceptShellEnv', {}); + } + + this.logService.error('Error fetching shell env', err); + }); + }); + + ipc.on('vscode:broadcast', (event, windowId: number, broadcast: { channel: string; payload: any; }) => { + if (this.windowsMainService && broadcast.channel && !isUndefinedOrNull(broadcast.payload)) { + this.logService.log('IPC#vscode:broadcast', broadcast.channel, broadcast.payload); + + // Handle specific events on main side + this.onBroadcast(broadcast.channel, broadcast.payload); + + // Send to all windows (except sender window) + this.windowsMainService.sendToAll('vscode:broadcast', broadcast, [windowId]); + } + }); + + // Keyboard layout changes + KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(isISOKeyboard => { + if (this.windowsMainService) { + this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', isISOKeyboard); + } + }); + } + + private onBroadcast(event: string, payload: any): void { + + // Theme changes + if (event === 'vscode:changeColorTheme' && typeof payload === 'string') { + let data = JSON.parse(payload); + + this.storageService.setItem(CodeWindow.themeStorageKey, data.id); + this.storageService.setItem(CodeWindow.themeBackgroundStorageKey, data.background); + } + } + + public startup(): void { + this.logService.log('Starting VS Code in verbose mode'); + this.logService.log(`from: ${this.environmentService.appRoot}`); + this.logService.log('args:', this.environmentService.args); + + // Make sure we associate the program with the app user model id + // This will help Windows to associate the running program with + // any shortcut that is pinned to the taskbar and prevent showing + // two icons in the taskbar for the same app. + if (platform.isWindows && product.win32AppUserModelId) { + app.setAppUserModelId(product.win32AppUserModelId); + } + + // Create Electron IPC Server + this.electronIpcServer = new ElectronIPCServer(); + + // Spawn shared process + this.sharedProcess = new SharedProcess(this.environmentService, this.userEnv); + this.toDispose.push(this.sharedProcess); + this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main')); + + // Services + const appInstantiationService = this.initServices(); + + // Setup Auth Handler + const authHandler = appInstantiationService.createInstance(ProxyAuthHandler); + this.toDispose.push(authHandler); + + // {{SQL CARBON EDIT}} + // Setup OAuth Handler + const oauthHandler = appInstantiationService.createInstance(ProxyOAuthHandler); + this.toDispose.push(oauthHandler); + + // Open Windows + appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor)); + + // Post Open Windows Tasks + appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor)); + } + + private initServices(): IInstantiationService { + const services = new ServiceCollection(); + + services.set(IUpdateService, new SyncDescriptor(UpdateService)); + services.set(IWindowsMainService, new SyncDescriptor(WindowsManager)); + services.set(IWindowsService, new SyncDescriptor(WindowsService, this.sharedProcess)); + services.set(ILaunchService, new SyncDescriptor(LaunchService)); + services.set(ICredentialsService, new SyncDescriptor(CredentialsService)); + + // Telemtry + if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !!product.enableTelemetry) { + const channel = getDelayedChannel(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender'))); + const appender = new TelemetryAppenderClient(channel); + const commonProperties = resolveCommonProperties(product.commit, pkg.version) + .then(result => Object.defineProperty(result, 'common.machineId', { + get: () => this.storageService.getItem(machineIdStorageKey), + enumerable: true + })); + const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; + const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths }; + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + } else { + services.set(ITelemetryService, NullTelemetryService); + } + + return this.instantiationService.createChild(services); + } + + private openFirstWindow(accessor: ServicesAccessor): void { + const appInstantiationService = accessor.get(IInstantiationService); + + // TODO@Joao: unfold this + this.windowsMainService = accessor.get(IWindowsMainService); + + // TODO@Joao: so ugly... + this.windowsMainService.onWindowsCountChanged(e => { + if (!platform.isMacintosh && e.newCount === 0) { + this.sharedProcess.dispose(); + } + }); + + // Register more Main IPC services + const launchService = accessor.get(ILaunchService); + const launchChannel = new LaunchChannel(launchService); + this.mainIpcServer.registerChannel('launch', launchChannel); + + // Register more Electron IPC services + const updateService = accessor.get(IUpdateService); + const updateChannel = new UpdateChannel(updateService); + this.electronIpcServer.registerChannel('update', updateChannel); + + const urlService = accessor.get(IURLService); + const urlChannel = appInstantiationService.createInstance(URLChannel, urlService); + this.electronIpcServer.registerChannel('url', urlChannel); + + const workspacesService = accessor.get(IWorkspacesMainService); + const workspacesChannel = appInstantiationService.createInstance(WorkspacesChannel, workspacesService); + this.electronIpcServer.registerChannel('workspaces', workspacesChannel); + + const windowsService = accessor.get(IWindowsService); + const windowsChannel = new WindowsChannel(windowsService); + this.electronIpcServer.registerChannel('windows', windowsChannel); + this.sharedProcessClient.done(client => client.registerChannel('windows', windowsChannel)); + + const credentialsService = accessor.get(ICredentialsService); + const credentialsChannel = new CredentialsChannel(credentialsService); + this.electronIpcServer.registerChannel('credentials', credentialsChannel); + + // Lifecycle + this.lifecycleService.ready(); + + // Propagate to clients + this.windowsMainService.ready(this.userEnv); + + // Open our first window + const args = this.environmentService.args; + const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; + if (args['new-window'] && args._.length === 0) { + this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths + } else if (global.macOpenFiles && global.macOpenFiles.length && (!args._ || !args._.length)) { + this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup + } else { + this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!args._.length && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli + } + } + + private afterWindowOpen(accessor: ServicesAccessor): void { + const appInstantiationService = accessor.get(IInstantiationService); + + // Setup Windows mutex + let windowsMutex: Mutex = null; + if (platform.isWindows) { + try { + const Mutex = (require.__$__nodeRequire('windows-mutex') as any).Mutex; + windowsMutex = new Mutex(product.win32MutexName); + this.toDispose.push({ dispose: () => windowsMutex.release() }); + } catch (e) { + // noop + } + } + + // Install Menu + appInstantiationService.createInstance(CodeMenu); + + // Jump List + this.historyService.updateWindowsJumpList(); + this.historyService.onRecentlyOpenedChange(() => this.historyService.updateWindowsJumpList()); + + // Start shared process here + this.sharedProcess.spawn(); + + // Helps application icon refresh after an update with new icon is installed (macOS) + // TODO@Ben remove after a couple of releases + if (platform.isMacintosh) { + if (!this.storageService.getItem(CodeApplication.APP_ICON_REFRESH_KEY)) { + this.storageService.setItem(CodeApplication.APP_ICON_REFRESH_KEY, true); + + // 'exe' => /Applications/Visual Studio Code - Insiders.app/Contents/MacOS/Electron + const appPath = dirname(dirname(dirname(app.getPath('exe')))); + const infoPlistPath = join(appPath, 'Contents', 'Info.plist'); + touch(appPath).done(null, error => { /* ignore */ }); + touch(infoPlistPath).done(null, error => { /* ignore */ }); + } + } + } + + private dispose(): void { + this.toDispose = dispose(this.toDispose); + } +} diff --git a/src/vs/code/electron-main/auth.html b/src/vs/code/electron-main/auth.html new file mode 100644 index 0000000000..e27ec2fc02 --- /dev/null +++ b/src/vs/code/electron-main/auth.html @@ -0,0 +1,125 @@ + + + + + + + + + + +

+
+

+
+

+

+

+ + +

+
+
+ + + + + \ No newline at end of file diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts new file mode 100644 index 0000000000..d9756869e7 --- /dev/null +++ b/src/vs/code/electron-main/auth.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; +import { fromEventEmitter } from 'vs/base/node/event'; +import { BrowserWindow, app } from 'electron'; + +type LoginEvent = { + event: Electron.Event; + webContents: Electron.WebContents; + req: Electron.LoginRequest; + authInfo: Electron.LoginAuthInfo; + cb: (username: string, password: string) => void; +}; + +type Credentials = { + username: string; + password: string; +}; + +export class ProxyAuthHandler { + + _serviceBrand: any; + + private retryCount = 0; + private disposables: IDisposable[] = []; + + constructor( + @IWindowsMainService private windowsService: IWindowsMainService + ) { + const onLogin = fromEventEmitter(app, 'login', (event, webContents, req, authInfo, cb) => ({ event, webContents, req, authInfo, cb })); + onLogin(this.onLogin, this, this.disposables); + } + + private onLogin({ event, authInfo, cb }: LoginEvent): void { + if (!authInfo.isProxy) { + return; + } + + if (this.retryCount++ > 1) { + return; + } + + event.preventDefault(); + + const opts: any = { + alwaysOnTop: true, + skipTaskbar: true, + resizable: false, + width: 450, + height: 220, + show: true, + title: 'VS Code' + }; + + const focusedWindow = this.windowsService.getFocusedWindow(); + + if (focusedWindow) { + opts.parent = focusedWindow.win; + opts.modal = true; + } + + const win = new BrowserWindow(opts); + const config = {}; + const baseUrl = require.toUrl('./auth.html'); + const url = `${baseUrl}?config=${encodeURIComponent(JSON.stringify(config))}`; + const proxyUrl = `${authInfo.host}:${authInfo.port}`; + const title = localize('authRequire', "Proxy Authentication Required"); + const message = localize('proxyauth', "The proxy {0} requires authentication.", proxyUrl); + const data = { title, message }; + const javascript = 'promptForCredentials(' + JSON.stringify(data) + ')'; + + const onWindowClose = () => cb('', ''); + win.on('close', onWindowClose); + + win.loadURL(url); + win.webContents.executeJavaScript(javascript, true).then(({ username, password }: Credentials) => { + cb(username, password); + win.removeListener('close', onWindowClose); + win.close(); + }); + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} \ No newline at end of file diff --git a/src/vs/code/electron-main/keyboard.ts b/src/vs/code/electron-main/keyboard.ts new file mode 100644 index 0000000000..20c6a3036c --- /dev/null +++ b/src/vs/code/electron-main/keyboard.ts @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nativeKeymap from 'native-keymap'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { isMacintosh } from 'vs/base/common/platform'; +import { IStorageService } from 'vs/platform/storage/node/storage'; +import Event, { Emitter, once } from 'vs/base/common/event'; +import { ConfigWatcher } from 'vs/base/node/config'; +import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ipcMain as ipc } from 'electron'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; +import { ILogService } from 'vs/platform/log/common/log'; + +export class KeyboardLayoutMonitor { + + public static readonly INSTANCE = new KeyboardLayoutMonitor(); + + private _emitter: Emitter; + private _registered: boolean; + private _isISOKeyboard: boolean; + + private constructor() { + this._emitter = new Emitter(); + this._registered = false; + this._isISOKeyboard = this._readIsISOKeyboard(); + } + + public onDidChangeKeyboardLayout(callback: (isISOKeyboard: boolean) => void): IDisposable { + if (!this._registered) { + this._registered = true; + + nativeKeymap.onDidChangeKeyboardLayout(() => { + this._emitter.fire(this._isISOKeyboard); + }); + + if (isMacintosh) { + // See https://github.com/Microsoft/vscode/issues/24153 + // On OSX, on ISO keyboards, Chromium swaps the scan codes + // of IntlBackslash and Backquote. + // + // The C++ methods can give the current keyboard type (ISO or not) + // only after a NSEvent was handled. + // + // We therefore poll. + setInterval(() => { + let newValue = this._readIsISOKeyboard(); + if (this._isISOKeyboard === newValue) { + // no change + return; + } + + this._isISOKeyboard = newValue; + this._emitter.fire(this._isISOKeyboard); + + }, 3000); + } + } + return this._emitter.event(callback); + } + + private _readIsISOKeyboard(): boolean { + if (isMacintosh) { + return nativeKeymap.isISOKeyboard(); + } + return false; + } + + public isISOKeyboard(): boolean { + return this._isISOKeyboard; + } +} + +export interface IKeybinding { + id: string; + label: string; + isNative: boolean; +} + +export class KeybindingsResolver { + + private static lastKnownKeybindingsMapStorageKey = 'lastKnownKeybindings'; + + private commandIds: Set; + private keybindings: { [commandId: string]: IKeybinding }; + private keybindingsWatcher: ConfigWatcher; + + private _onKeybindingsChanged = new Emitter(); + onKeybindingsChanged: Event = this._onKeybindingsChanged.event; + + constructor( + @IStorageService private storageService: IStorageService, + @IEnvironmentService environmentService: IEnvironmentService, + @IWindowsMainService private windowsService: IWindowsMainService, + @ILogService private logService: ILogService + ) { + this.commandIds = new Set(); + this.keybindings = this.storageService.getItem<{ [id: string]: string; }>(KeybindingsResolver.lastKnownKeybindingsMapStorageKey) || Object.create(null); + this.keybindingsWatcher = new ConfigWatcher(environmentService.appKeybindingsPath, { changeBufferDelay: 100, onError: error => this.logService.error(error) }); + + this.registerListeners(); + } + + private registerListeners(): void { + + // Listen to resolved keybindings from window + ipc.on('vscode:keybindingsResolved', (event, rawKeybindings: string) => { + let keybindings: IKeybinding[] = []; + try { + keybindings = JSON.parse(rawKeybindings); + } catch (error) { + // Should not happen + } + + // Fill hash map of resolved keybindings and check for changes + let keybindingsChanged = false; + let keybindingsCount = 0; + const resolvedKeybindings: { [commandId: string]: IKeybinding } = Object.create(null); + keybindings.forEach(keybinding => { + keybindingsCount++; + + resolvedKeybindings[keybinding.id] = keybinding; + + if (!this.keybindings[keybinding.id] || keybinding.label !== this.keybindings[keybinding.id].label) { + keybindingsChanged = true; + } + }); + + // A keybinding might have been unassigned, so we have to account for that too + if (Object.keys(this.keybindings).length !== keybindingsCount) { + keybindingsChanged = true; + } + + if (keybindingsChanged) { + this.keybindings = resolvedKeybindings; + this.storageService.setItem(KeybindingsResolver.lastKnownKeybindingsMapStorageKey, this.keybindings); // keep to restore instantly after restart + + this._onKeybindingsChanged.fire(); + } + }); + + // Resolve keybindings when any first window is loaded + const onceOnWindowReady = once(this.windowsService.onWindowReady); + onceOnWindowReady(win => this.resolveKeybindings(win)); + + // Resolve keybindings again when keybindings.json changes + this.keybindingsWatcher.onDidUpdateConfiguration(() => this.resolveKeybindings()); + + // Resolve keybindings when window reloads because an installed extension could have an impact + this.windowsService.onWindowReload(() => this.resolveKeybindings()); + } + + private resolveKeybindings(win = this.windowsService.getLastActiveWindow()): void { + if (this.commandIds.size && win) { + const commandIds = []; + this.commandIds.forEach(id => commandIds.push(id)); + win.sendWhenReady('vscode:resolveKeybindings', JSON.stringify(commandIds)); + } + } + + public getKeybinding(commandId: string): IKeybinding { + if (!commandId) { + return void 0; + } + + if (!this.commandIds.has(commandId)) { + this.commandIds.add(commandId); + } + + return this.keybindings[commandId]; + } +} \ No newline at end of file diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts new file mode 100644 index 0000000000..864aed39fd --- /dev/null +++ b/src/vs/code/electron-main/launch.ts @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IURLService } from 'vs/platform/url/common/url'; +import { IProcessEnvironment } from 'vs/base/common/platform'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { OpenContext } from 'vs/platform/windows/common/windows'; +import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; + +export const ID = 'launchService'; +export const ILaunchService = createDecorator(ID); + +export interface IStartArguments { + args: ParsedArgs; + userEnv: IProcessEnvironment; +} + +export interface ILaunchService { + _serviceBrand: any; + start(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise; + getMainProcessId(): TPromise; +} + +export interface ILaunchChannel extends IChannel { + call(command: 'start', arg: IStartArguments): TPromise; + call(command: 'get-main-process-id', arg: null): TPromise; + call(command: string, arg: any): TPromise; +} + +export class LaunchChannel implements ILaunchChannel { + + constructor(private service: ILaunchService) { } + + public call(command: string, arg: any): TPromise { + switch (command) { + case 'start': + const { args, userEnv } = arg as IStartArguments; + return this.service.start(args, userEnv); + + case 'get-main-process-id': + return this.service.getMainProcessId(); + } + + return undefined; + } +} + +export class LaunchChannelClient implements ILaunchService { + + _serviceBrand: any; + + constructor(private channel: ILaunchChannel) { } + + public start(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise { + return this.channel.call('start', { args, userEnv }); + } + + public getMainProcessId(): TPromise { + return this.channel.call('get-main-process-id', null); + } +} + +export class LaunchService implements ILaunchService { + + _serviceBrand: any; + + constructor( + @ILogService private logService: ILogService, + @IWindowsMainService private windowsService: IWindowsMainService, + @IURLService private urlService: IURLService + ) { } + + public start(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise { + this.logService.log('Received data from other instance: ', args, userEnv); + + // Check early for open-url which is handled in URL service + const openUrlArg = args['open-url'] || []; + const openUrl = typeof openUrlArg === 'string' ? [openUrlArg] : openUrlArg; + if (openUrl.length > 0) { + openUrl.forEach(url => this.urlService.open(url)); + + return TPromise.as(null); + } + + // Otherwise handle in windows service + const context = !!userEnv['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; + let usedWindows: ICodeWindow[]; + if (!!args.extensionDevelopmentPath) { + this.windowsService.openExtensionDevelopmentHostWindow({ context, cli: args, userEnv }); + } else if (args._.length === 0 && (args['new-window'] || args['unity-launch'])) { + usedWindows = this.windowsService.open({ context, cli: args, userEnv, forceNewWindow: true, forceEmpty: true }); + } else if (args._.length === 0) { + usedWindows = [this.windowsService.focusLastActive(args, context)]; + } else { + usedWindows = this.windowsService.open({ + context, + cli: args, + userEnv, + forceNewWindow: args.wait || args['new-window'], + preferNewWindow: !args['reuse-window'], + forceReuseWindow: args['reuse-window'], + diffMode: args.diff, + addMode: args.add + }); + } + + // If the other instance is waiting to be killed, we hook up a window listener if one window + // is being used and only then resolve the startup promise which will kill this second instance + if (args.wait && usedWindows.length === 1 && usedWindows[0]) { + return this.windowsService.waitForWindowClose(usedWindows[0].id); + } + + return TPromise.as(null); + } + + public getMainProcessId(): TPromise { + this.logService.log('Received request for process ID from other instance.'); + + return TPromise.as(process.pid); + } +} \ No newline at end of file diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts new file mode 100644 index 0000000000..4069a01a98 --- /dev/null +++ b/src/vs/code/electron-main/main.ts @@ -0,0 +1,217 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { app } from 'electron'; +import { assign } from 'vs/base/common/objects'; +import * as platform from 'vs/base/common/platform'; +import { parseMainProcessArgv } from 'vs/platform/environment/node/argv'; +import { mkdirp } from 'vs/base/node/pfs'; +import { validatePaths } from 'vs/code/node/paths'; +import { LifecycleService, ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ILaunchChannel, LaunchChannelClient } from './launch'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ILogService, LogMainService } from 'vs/platform/log/common/log'; +import { IStorageService, StorageService } from 'vs/platform/storage/node/storage'; +import { IBackupMainService } from 'vs/platform/backup/common/backup'; +import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; +import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { IRequestService } from 'vs/platform/request/node/request'; +import { RequestService } from 'vs/platform/request/electron-main/requestService'; +import { IURLService } from 'vs/platform/url/common/url'; +import { URLService } from 'vs/platform/url/electron-main/urlService'; +import * as fs from 'original-fs'; +import { CodeApplication } from 'vs/code/electron-main/app'; +import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; +import { IHistoryMainService } from 'vs/platform/history/common/history'; +import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; +import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; + +function createServices(args: ParsedArgs): IInstantiationService { + const services = new ServiceCollection(); + + services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService, args, process.execPath)); + services.set(ILogService, new SyncDescriptor(LogMainService)); + services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService)); + services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService)); + services.set(ILifecycleService, new SyncDescriptor(LifecycleService)); + services.set(IStorageService, new SyncDescriptor(StorageService)); + services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); + services.set(IRequestService, new SyncDescriptor(RequestService)); + services.set(IURLService, new SyncDescriptor(URLService, args['open-url'])); + services.set(IBackupMainService, new SyncDescriptor(BackupMainService)); + + return new InstantiationService(services, true); +} + +function createPaths(environmentService: IEnvironmentService): TPromise { + const paths = [ + environmentService.appSettingsHome, + environmentService.extensionsPath, + environmentService.nodeCachedDataDir + ]; + return TPromise.join(paths.map(p => p && mkdirp(p))) as TPromise; +} + +class ExpectedError extends Error { + public readonly isExpected = true; +} + +function setupIPC(accessor: ServicesAccessor): TPromise { + const logService = accessor.get(ILogService); + const environmentService = accessor.get(IEnvironmentService); + + function allowSetForegroundWindow(service: LaunchChannelClient): TPromise { + let promise = TPromise.as(void 0); + if (platform.isWindows) { + promise = service.getMainProcessId() + .then(processId => { + logService.log('Sending some foreground love to the running instance:', processId); + + try { + const { allowSetForegroundWindow } = require.__$__nodeRequire('windows-foreground-love'); + allowSetForegroundWindow(processId); + } catch (e) { + // noop + } + }); + } + + return promise; + } + + function setup(retry: boolean): TPromise { + return serve(environmentService.mainIPCHandle).then(server => { + if (platform.isMacintosh) { + app.dock.show(); // dock might be hidden at this case due to a retry + } + + return server; + }, err => { + if (err.code !== 'EADDRINUSE') { + return TPromise.wrapError(err); + } + + // Since we are the second instance, we do not want to show the dock + if (platform.isMacintosh) { + app.dock.hide(); + } + + // there's a running instance, let's connect to it + return connect(environmentService.mainIPCHandle, 'main').then( + client => { + + // Tests from CLI require to be the only instance currently + if (environmentService.extensionTestsPath && !environmentService.debugExtensionHost.break) { + const msg = 'Running extension tests from the command line is currently only supported if no other instance of Code is running.'; + logService.error(msg); + client.dispose(); + + return TPromise.wrapError(new Error(msg)); + } + + logService.log('Sending env to running instance...'); + + const channel = client.getChannel('launch'); + const service = new LaunchChannelClient(channel); + + return allowSetForegroundWindow(service) + .then(() => service.start(environmentService.args, process.env)) + .then(() => client.dispose()) + .then(() => TPromise.wrapError(new ExpectedError('Sent env to running instance. Terminating...'))); + }, + err => { + if (!retry || platform.isWindows || err.code !== 'ECONNREFUSED') { + return TPromise.wrapError(err); + } + + // it happens on Linux and OS X that the pipe is left behind + // let's delete it, since we can't connect to it + // and the retry the whole thing + try { + fs.unlinkSync(environmentService.mainIPCHandle); + } catch (e) { + logService.log('Fatal error deleting obsolete instance handle', e); + return TPromise.wrapError(e); + } + + return setup(false); + } + ); + }); + } + + return setup(true); +} + +function quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void { + const logService = accessor.get(ILogService); + const lifecycleService = accessor.get(ILifecycleService); + + let exitCode = 0; + + if (reason) { + if ((reason as ExpectedError).isExpected) { + logService.log(reason.message); + } else { + exitCode = 1; // signal error to the outside + + if (reason.stack) { + console.error(reason.stack); + } else { + console.error(`Startup error: ${reason.toString()}`); + } + } + } + + lifecycleService.kill(exitCode); +} + +function main() { + let args: ParsedArgs; + + try { + args = parseMainProcessArgv(process.argv); + args = validatePaths(args); + } catch (err) { + console.error(err.message); + app.exit(1); + + return; + } + + const instantiationService = createServices(args); + + return instantiationService.invokeFunction(accessor => { + + // Patch `process.env` with the instance's environment + const environmentService = accessor.get(IEnvironmentService); + const instanceEnv: typeof process.env = { + VSCODE_PID: String(process.pid), + VSCODE_IPC_HOOK: environmentService.mainIPCHandle, + VSCODE_NLS_CONFIG: process.env['VSCODE_NLS_CONFIG'] + }; + assign(process.env, instanceEnv); + + // Startup + return instantiationService.invokeFunction(a => createPaths(a.get(IEnvironmentService))) + .then(() => instantiationService.invokeFunction(setupIPC)) + .then(mainIpcServer => { + const app = instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnv); + app.startup(); + }); + }).done(null, err => instantiationService.invokeFunction(quit, err)); +} + +main(); \ No newline at end of file diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts new file mode 100644 index 0000000000..2d9472fe75 --- /dev/null +++ b/src/vs/code/electron-main/menus.ts @@ -0,0 +1,1225 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import { isMacintosh, isLinux, isWindows, language } from 'vs/base/common/platform'; +import * as arrays from 'vs/base/common/arrays'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem, BrowserWindow } from 'electron'; +import { OpenContext } from 'vs/platform/windows/common/windows'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IFilesConfiguration, AutoSaveConfiguration } from 'vs/platform/files/common/files'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IUpdateService, State as UpdateState } from 'vs/platform/update/common/update'; +import product from 'vs/platform/node/product'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { mnemonicMenuLabel as baseMnemonicLabel, unmnemonicLabel, getPathLabel } from 'vs/base/common/labels'; +import { KeybindingsResolver } from 'vs/code/electron-main/keyboard'; +import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows'; +import { IHistoryMainService } from 'vs/platform/history/common/history'; +import { IWorkspaceIdentifier, IWorkspacesMainService, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +interface IExtensionViewlet { + id: string; + label: string; +} + +interface IConfiguration extends IFilesConfiguration { + window: { + enableMenuBarMnemonics: boolean; + }; + workbench: { + sideBar: { + location: 'left' | 'right'; + }, + statusBar: { + visible: boolean; + }, + activityBar: { + visible: boolean; + } + }; + editor: { + multiCursorModifier: 'ctrlCmd' | 'alt' + }; +} + +const telemetryFrom = 'menu'; + +export class CodeMenu { + + private static MAX_MENU_RECENT_ENTRIES = 10; + + private currentAutoSaveSetting: string; + private currentMultiCursorModifierSetting: string; + private currentSidebarLocation: 'left' | 'right'; + private currentStatusbarVisible: boolean; + private currentActivityBarVisible: boolean; + private currentEnableMenuBarMnemonics: boolean; + + private isQuitting: boolean; + private appMenuInstalled: boolean; + + private menuUpdater: RunOnceScheduler; + + private keybindingsResolver: KeybindingsResolver; + + private extensionViewlets: IExtensionViewlet[]; + + private closeFolder: Electron.MenuItem; + private closeWorkspace: Electron.MenuItem; + private saveWorkspaceAs: Electron.MenuItem; + + constructor( + @IUpdateService private updateService: IUpdateService, + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService private configurationService: IConfigurationService, + @IWindowsMainService private windowsService: IWindowsMainService, + @IEnvironmentService private environmentService: IEnvironmentService, + @ITelemetryService private telemetryService: ITelemetryService, + @IHistoryMainService private historyService: IHistoryMainService, + @IWorkspacesMainService private workspacesService: IWorkspacesMainService + ) { + this.extensionViewlets = []; + + this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0); + this.keybindingsResolver = instantiationService.createInstance(KeybindingsResolver); + + this.onConfigurationUpdated(this.configurationService.getConfiguration()); + + this.install(); + + this.registerListeners(); + } + + private registerListeners(): void { + + // Keep flag when app quits + app.on('will-quit', () => { + this.isQuitting = true; + }); + + // Listen to some events from window service to update menu + this.historyService.onRecentlyOpenedChange(() => this.updateMenu()); + this.windowsService.onWindowsCountChanged(e => this.onWindowsCountChanged(e)); + this.windowsService.onActiveWindowChanged(() => this.updateWorkspaceMenuItems()); + this.windowsService.onWindowReady(() => this.updateWorkspaceMenuItems()); + this.windowsService.onWindowClose(() => this.updateWorkspaceMenuItems()); + + // Listen to extension viewlets + ipc.on('vscode:extensionViewlets', (event, rawExtensionViewlets) => { + let extensionViewlets: IExtensionViewlet[] = []; + try { + extensionViewlets = JSON.parse(rawExtensionViewlets); + } catch (error) { + // Should not happen + } + + if (extensionViewlets.length) { + this.extensionViewlets = extensionViewlets; + this.updateMenu(); + } + }); + + // Update when auto save config changes + this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration(), true /* update menu if changed */)); + + // Listen to update service + this.updateService.onStateChange(() => this.updateMenu()); + + // Listen to keybindings change + this.keybindingsResolver.onKeybindingsChanged(() => this.updateMenu()); + } + + private onConfigurationUpdated(config: IConfiguration, handleMenu?: boolean): void { + let updateMenu = false; + const newAutoSaveSetting = config && config.files && config.files.autoSave; + if (newAutoSaveSetting !== this.currentAutoSaveSetting) { + this.currentAutoSaveSetting = newAutoSaveSetting; + updateMenu = true; + } + + const newMultiCursorModifierSetting = config && config.editor && config.editor.multiCursorModifier; + if (newMultiCursorModifierSetting !== this.currentMultiCursorModifierSetting) { + this.currentMultiCursorModifierSetting = newMultiCursorModifierSetting; + updateMenu = true; + } + + const newSidebarLocation = config && config.workbench && config.workbench.sideBar && config.workbench.sideBar.location || 'left'; + if (newSidebarLocation !== this.currentSidebarLocation) { + this.currentSidebarLocation = newSidebarLocation; + updateMenu = true; + } + + let newStatusbarVisible = config && config.workbench && config.workbench.statusBar && config.workbench.statusBar.visible; + if (typeof newStatusbarVisible !== 'boolean') { + newStatusbarVisible = true; + } + if (newStatusbarVisible !== this.currentStatusbarVisible) { + this.currentStatusbarVisible = newStatusbarVisible; + updateMenu = true; + } + + let newActivityBarVisible = config && config.workbench && config.workbench.activityBar && config.workbench.activityBar.visible; + if (typeof newActivityBarVisible !== 'boolean') { + newActivityBarVisible = true; + } + if (newActivityBarVisible !== this.currentActivityBarVisible) { + this.currentActivityBarVisible = newActivityBarVisible; + updateMenu = true; + } + + let newEnableMenuBarMnemonics = config && config.window && config.window.enableMenuBarMnemonics; + if (typeof newEnableMenuBarMnemonics !== 'boolean') { + newEnableMenuBarMnemonics = true; + } + if (newEnableMenuBarMnemonics !== this.currentEnableMenuBarMnemonics) { + this.currentEnableMenuBarMnemonics = newEnableMenuBarMnemonics; + updateMenu = true; + } + + if (handleMenu && updateMenu) { + this.updateMenu(); + } + } + + private updateMenu(): void { + this.menuUpdater.schedule(); // buffer multiple attempts to update the menu + } + + private doUpdateMenu(): void { + + // Due to limitations in Electron, it is not possible to update menu items dynamically. The suggested + // workaround from Electron is to set the application menu again. + // See also https://github.com/electron/electron/issues/846 + // + // Run delayed to prevent updating menu while it is open + if (!this.isQuitting) { + setTimeout(() => { + if (!this.isQuitting) { + this.install(); + } + }, 10 /* delay this because there is an issue with updating a menu when it is open */); + } + } + + private onWindowsCountChanged(e: IWindowsCountChangedEvent): void { + if (!isMacintosh) { + return; + } + + // Update menu if window count goes from N > 0 or 0 > N to update menu item enablement + if ((e.oldCount === 0 && e.newCount > 0) || (e.oldCount > 0 && e.newCount === 0)) { + this.updateMenu(); + } + } + + private updateWorkspaceMenuItems(): void { + const window = this.windowsService.getLastActiveWindow(); + const isInWorkspaceContext = window && !!window.openedWorkspace; + const isInFolderContext = window && !!window.openedFolderPath; + + this.closeWorkspace.visible = isInWorkspaceContext; + this.closeFolder.visible = !isInWorkspaceContext; + this.closeFolder.enabled = isInFolderContext; + this.saveWorkspaceAs.enabled = isInFolderContext || isInWorkspaceContext; + } + + private install(): void { + + // Menus + const menubar = new Menu(); + + // Mac: Application + let macApplicationMenuItem: Electron.MenuItem; + if (isMacintosh) { + const applicationMenu = new Menu(); + // {{ SQL CARBON EDIT}} + macApplicationMenuItem = new MenuItem({ label: product.nameLong, submenu: applicationMenu }); + this.setMacApplicationMenu(applicationMenu); + } + + // File + const fileMenu = new Menu(); + const fileMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mFile', comment: ['&& denotes a mnemonic'] }, "&&File")), submenu: fileMenu }); + this.setFileMenu(fileMenu); + + // Edit + const editMenu = new Menu(); + const editMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mEdit', comment: ['&& denotes a mnemonic'] }, "&&Edit")), submenu: editMenu }); + this.setEditMenu(editMenu); + // {{SQL CARBON EDIT}} + // Selection + // const selectionMenu = new Menu(); + // const selectionMenuItem = new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection")), submenu: selectionMenu }); + // this.setSelectionMenu(selectionMenu); + + // View + const viewMenu = new Menu(); + const viewMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mView', comment: ['&& denotes a mnemonic'] }, "&&View")), submenu: viewMenu }); + this.setViewMenu(viewMenu); + + // Goto + const gotoMenu = new Menu(); + const gotoMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go")), submenu: gotoMenu }); + this.setGotoMenu(gotoMenu); + + // {{SQL CARBON EDIT}} + // Debug + // const debugMenu = new Menu(); + // const debugMenuItem = new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'mDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug")), submenu: debugMenu }); + // this.setDebugMenu(debugMenu); + + + // Mac: Window + let macWindowMenuItem: Electron.MenuItem; + if (isMacintosh) { + const windowMenu = new Menu(); + macWindowMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize('mWindow', "Window")), submenu: windowMenu, role: 'window' }); + this.setMacWindowMenu(windowMenu); + } + + // Help + const helpMenu = new Menu(); + const helpMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help")), submenu: helpMenu, role: 'help' }); + this.setHelpMenu(helpMenu); + + // {{SQL CARBON EDIT}} + // Tasks + //const taskMenu = new Menu(); + //const taskMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTask', comment: ['&& denotes a mnemonic'] }, "&&Tasks")), submenu: taskMenu }); + //this.setTaskMenu(taskMenu); + + // Menu Structure + if (macApplicationMenuItem) { + menubar.append(macApplicationMenuItem); + } + + // {{SQL CARBON EDIT}} + menubar.append(fileMenuItem); + menubar.append(editMenuItem); + //menubar.append(selectionMenuItem); + menubar.append(viewMenuItem); + menubar.append(gotoMenuItem); + //menubar.append(debugMenuItem); + //menubar.append(taskMenuItem); + + if (macWindowMenuItem) { + menubar.append(macWindowMenuItem); + } + + menubar.append(helpMenuItem); + + Menu.setApplicationMenu(menubar); + + // Dock Menu + if (isMacintosh && !this.appMenuInstalled) { + this.appMenuInstalled = true; + + const dockMenu = new Menu(); + dockMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.DOCK) })); + + app.dock.setMenu(dockMenu); + } + } + + private setMacApplicationMenu(macApplicationMenu: Electron.Menu): void { + const about = new MenuItem({ label: nls.localize('mAbout', "About {0}", product.nameLong), role: 'about' }); + const checkForUpdates = this.getUpdateMenuItems(); + const preferences = this.getPreferencesMenu(); + const servicesMenu = new Menu(); + const services = new MenuItem({ label: nls.localize('mServices', "Services"), role: 'services', submenu: servicesMenu }); + const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' }); + const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideothers', accelerator: 'Command+Alt+H' }); + const showAll = new MenuItem({ label: nls.localize('mShowAll', "Show All"), role: 'unhide' }); + const quit = new MenuItem(this.likeAction('workbench.action.quit', { label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => this.windowsService.quit() })); + + const actions = [about]; + actions.push(...checkForUpdates); + actions.push(...[ + __separator__(), + preferences, + __separator__(), + services, + __separator__(), + hide, + hideOthers, + showAll, + __separator__(), + quit + ]); + + actions.forEach(i => macApplicationMenu.append(i)); + } + + private setFileMenu(fileMenu: Electron.Menu): void { + const hasNoWindows = (this.windowsService.getWindowCount() === 0); + + // {{SQL CARBON EDIT}} + let newFile: Electron.MenuItem; + if (hasNoWindows) { + newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: this.mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New Query")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) })); + } else { + newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New Query"), 'workbench.action.files.newUntitledFile'); + } + + const open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsService.pickFileFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); + const openWorkspace = new MenuItem(this.likeAction('workbench.action.openWorkspace', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open Workspace...")), click: () => this.windowsService.openWorkspace() })); + const openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsService.pickFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); + + let openFile: Electron.MenuItem; + if (hasNoWindows) { + openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: (menuItem, win, event) => this.windowsService.pickFileAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); + } else { + openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), ['workbench.action.files.openFile', 'workbench.action.files.openFileInNewWindow']); + } + + const openRecentMenu = new Menu(); + this.setOpenRecentMenu(openRecentMenu); + const openRecent = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent")), submenu: openRecentMenu, enabled: openRecentMenu.items.length > 0 }); + + const isMultiRootEnabled = (product.quality !== 'stable'); // TODO@Ben multi root + + this.saveWorkspaceAs = this.createMenuItem(nls.localize({ key: 'miSaveWorkspaceAs', comment: ['&& denotes a mnemonic'] }, "&&Save Workspace As..."), 'workbench.action.saveWorkspaceAs'); + const addFolder = this.createMenuItem(nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Add Folder to Workspace..."), 'workbench.action.addRootFolder'); + + const saveFile = this.createMenuItem(nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save"), 'workbench.action.files.save'); + const saveFileAs = this.createMenuItem(nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As..."), 'workbench.action.files.saveAs'); + const saveAllFiles = this.createMenuItem(nls.localize({ key: 'miSaveAll', comment: ['&& denotes a mnemonic'] }, "Save A&&ll"), 'workbench.action.files.saveAll'); + + const autoSaveEnabled = [AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE].some(s => this.currentAutoSaveSetting === s); + const autoSave = new MenuItem(this.likeAction('vscode.toggleAutoSave', { label: this.mnemonicLabel(nls.localize('miAutoSave', "Auto Save")), type: 'checkbox', checked: autoSaveEnabled, enabled: this.windowsService.getWindowCount() > 0, click: () => this.windowsService.sendToFocused('vscode.toggleAutoSave') }, false)); + + const preferences = this.getPreferencesMenu(); + + const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) })); + const revertFile = this.createMenuItem(nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), 'workbench.action.files.revert'); + const closeWindow = new MenuItem(this.likeAction('workbench.action.closeWindow', { label: this.mnemonicLabel(nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window")), click: () => this.windowsService.getLastActiveWindow().win.close(), enabled: this.windowsService.getWindowCount() > 0 })); + + this.closeWorkspace = this.createMenuItem(nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace"), 'workbench.action.closeFolder'); + this.closeFolder = this.createMenuItem(nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), 'workbench.action.closeFolder'); + + const closeEditor = this.createMenuItem(nls.localize({ key: 'miCloseEditor', comment: ['&& denotes a mnemonic'] }, "&&Close Editor"), 'workbench.action.closeActiveEditor'); + + const exit = new MenuItem(this.likeAction('workbench.action.quit', { label: this.mnemonicLabel(nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit")), click: () => this.windowsService.quit() })); + + this.updateWorkspaceMenuItems(); + + arrays.coalesce([ + newFile, + newWindow, + __separator__(), + isMacintosh ? open : null, + !isMacintosh ? openFile : null, + !isMacintosh ? openFolder : null, + isMultiRootEnabled ? openWorkspace : null, + openRecent, + isMultiRootEnabled ? __separator__() : null, + isMultiRootEnabled ? addFolder : null, + isMultiRootEnabled ? this.saveWorkspaceAs : null, + __separator__(), + saveFile, + saveFileAs, + saveAllFiles, + __separator__(), + autoSave, + __separator__(), + !isMacintosh ? preferences : null, + !isMacintosh ? __separator__() : null, + revertFile, + closeEditor, + this.closeWorkspace, + this.closeFolder, + closeWindow, + !isMacintosh ? __separator__() : null, + !isMacintosh ? exit : null + ]).forEach(item => fileMenu.append(item)); + } + + private getPreferencesMenu(): Electron.MenuItem { + const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openGlobalSettings'); + const kebindingSettings = this.createMenuItem(nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts"), 'workbench.action.openGlobalKeybindings'); + // {{SQL CARBON EDIT}} + // const keymapExtensions = this.createMenuItem(nls.localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions"), 'workbench.extensions.action.showRecommendedKeymapExtensions'); + // const snippetsSettings = this.createMenuItem(nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets"), 'workbench.action.openSnippets'); + const colorThemeSelection = this.createMenuItem(nls.localize({ key: 'miSelectColorTheme', comment: ['&& denotes a mnemonic'] }, "&&Color Theme"), 'workbench.action.selectTheme'); + const iconThemeSelection = this.createMenuItem(nls.localize({ key: 'miSelectIconTheme', comment: ['&& denotes a mnemonic'] }, "File &&Icon Theme"), 'workbench.action.selectIconTheme'); + + const preferencesMenu = new Menu(); + preferencesMenu.append(settings); + preferencesMenu.append(__separator__()); + preferencesMenu.append(kebindingSettings); + // {{SQL CARBON EDIT}} + // preferencesMenu.append(keymapExtensions); + // preferencesMenu.append(__separator__()); + // preferencesMenu.append(snippetsSettings); + preferencesMenu.append(__separator__()); + preferencesMenu.append(colorThemeSelection); + preferencesMenu.append(iconThemeSelection); + + return new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu }); + } + + private setOpenRecentMenu(openRecentMenu: Electron.Menu): void { + openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), 'workbench.action.reopenClosedEditor')); + + const { workspaces, files } = this.historyService.getRecentlyOpened(); + + // Workspaces + if (workspaces.length > 0) { + openRecentMenu.append(__separator__()); + + for (let i = 0; i < CodeMenu.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) { + openRecentMenu.append(this.createOpenRecentMenuItem(workspaces[i], 'openRecentWorkspace', false)); + } + } + + // Files + if (files.length > 0) { + openRecentMenu.append(__separator__()); + + for (let i = 0; i < CodeMenu.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) { + openRecentMenu.append(this.createOpenRecentMenuItem(files[i], 'openRecentFile', true)); + } + } + + if (workspaces.length || files.length) { + openRecentMenu.append(__separator__()); + openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More..."), 'workbench.action.openRecent')); + openRecentMenu.append(__separator__()); + openRecentMenu.append(new MenuItem(this.likeAction('workbench.action.clearRecentFiles', { label: this.mnemonicLabel(nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened")), click: () => this.historyService.clearRecentlyOpened() }))); + } + } + + private createOpenRecentMenuItem(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, commandId: string, isFile: boolean): Electron.MenuItem { + let label: string; + let path: string; + if (isSingleFolderWorkspaceIdentifier(workspace) || typeof workspace === 'string') { + label = unmnemonicLabel(getPathLabel(workspace, null, this.environmentService)); + path = workspace; + } else { + label = getWorkspaceLabel(workspace, this.environmentService, { verbose: true }); + path = workspace.configPath; + } + + return new MenuItem(this.likeAction(commandId, { + label, + click: (menuItem, win, event) => { + const openInNewWindow = this.isOptionClick(event); + const success = this.windowsService.open({ + context: OpenContext.MENU, + cli: this.environmentService.args, + pathsToOpen: [path], forceNewWindow: openInNewWindow, + forceOpenWorkspaceAsFile: isFile + }).length > 0; + + if (!success) { + this.historyService.removeFromRecentlyOpened([isSingleFolderWorkspaceIdentifier(workspace) ? workspace : workspace.configPath]); + } + } + }, false)); + } + + private isOptionClick(event: Electron.Event & Electron.Modifiers): boolean { + return event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey))); + } + + private createRoleMenuItem(label: string, commandId: string, role: Electron.MenuItemRole): Electron.MenuItem { + const options: Electron.MenuItemOptions = { + label: this.mnemonicLabel(label), + role, + enabled: true + }; + + return new MenuItem(this.withKeybinding(commandId, options)); + } + + private setEditMenu(winLinuxEditMenu: Electron.Menu): void { + let undo: Electron.MenuItem; + let redo: Electron.MenuItem; + let cut: Electron.MenuItem; + let copy: Electron.MenuItem; + let paste: Electron.MenuItem; + + if (isMacintosh) { + undo = this.createDevToolsAwareMenuItem(nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), 'undo', devTools => devTools.undo()); + redo = this.createDevToolsAwareMenuItem(nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), 'redo', devTools => devTools.redo()); + cut = this.createRoleMenuItem(nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), 'editor.action.clipboardCutAction', 'cut'); + copy = this.createRoleMenuItem(nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), 'editor.action.clipboardCopyAction', 'copy'); + paste = this.createRoleMenuItem(nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), 'editor.action.clipboardPasteAction', 'paste'); + } else { + undo = this.createMenuItem(nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), 'undo'); + redo = this.createMenuItem(nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), 'redo'); + cut = this.createMenuItem(nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), 'editor.action.clipboardCutAction'); + copy = this.createMenuItem(nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), 'editor.action.clipboardCopyAction'); + paste = this.createMenuItem(nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), 'editor.action.clipboardPasteAction'); + } + + const find = this.createMenuItem(nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"), 'actions.find'); + const replace = this.createMenuItem(nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace"), 'editor.action.startFindReplaceAction'); + const findInFiles = this.createMenuItem(nls.localize({ key: 'miFindInFiles', comment: ['&& denotes a mnemonic'] }, "Find &&in Files"), 'workbench.action.findInFiles'); + const replaceInFiles = this.createMenuItem(nls.localize({ key: 'miReplaceInFiles', comment: ['&& denotes a mnemonic'] }, "Replace &&in Files"), 'workbench.action.replaceInFiles'); + + // {{SQL CARBON EDIT}} + // const emmetExpandAbbreviation = this.createMenuItem(nls.localize({ key: 'miEmmetExpandAbbreviation', comment: ['&& denotes a mnemonic'] }, "Emmet: E&&xpand Abbreviation"), 'editor.emmet.action.expandAbbreviation'); + // const showEmmetCommands = this.createMenuItem(nls.localize({ key: 'miShowEmmetCommands', comment: ['&& denotes a mnemonic'] }, "E&&mmet..."), 'workbench.action.showEmmetCommands'); + // const toggleLineComment = this.createMenuItem(nls.localize({ key: 'miToggleLineComment', comment: ['&& denotes a mnemonic'] }, "&&Toggle Line Comment"), 'editor.action.commentLine'); + // const toggleBlockComment = this.createMenuItem(nls.localize({ key: 'miToggleBlockComment', comment: ['&& denotes a mnemonic'] }, "Toggle &&Block Comment"), 'editor.action.blockComment'); + + [ + undo, + redo, + __separator__(), + cut, + copy, + paste, + __separator__(), + find, + replace, + __separator__(), + findInFiles, + replaceInFiles, + // {{SQL CARBON EDIT}} + // __separator__(), + // toggleLineComment, + // toggleBlockComment, + // emmetExpandAbbreviation, + // showEmmetCommands + ].forEach(item => winLinuxEditMenu.append(item)); + } + + // {{SQL CARBON EDIT}} + /* + private setSelectionMenu(winLinuxEditMenu: Electron.Menu): void { + let multiCursorModifierLabel: string; + if (this.currentMultiCursorModifierSetting === 'ctrlCmd') { + // The default has been overwritten + multiCursorModifierLabel = nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor"); + } else { + multiCursorModifierLabel = ( + isMacintosh + ? nls.localize('miMultiCursorCmd', "Switch to Cmd+Click for Multi-Cursor") + : nls.localize('miMultiCursorCtrl', "Switch to Ctrl+Click for Multi-Cursor") + ); + } + + const multicursorModifier = this.createMenuItem(multiCursorModifierLabel, 'workbench.action.toggleMultiCursorModifier'); + const insertCursorAbove = this.createMenuItem(nls.localize({ key: 'miInsertCursorAbove', comment: ['&& denotes a mnemonic'] }, "&&Add Cursor Above"), 'editor.action.insertCursorAbove'); + const insertCursorBelow = this.createMenuItem(nls.localize({ key: 'miInsertCursorBelow', comment: ['&& denotes a mnemonic'] }, "A&&dd Cursor Below"), 'editor.action.insertCursorBelow'); + const insertCursorAtEndOfEachLineSelected = this.createMenuItem(nls.localize({ key: 'miInsertCursorAtEndOfEachLineSelected', comment: ['&& denotes a mnemonic'] }, "Add C&&ursors to Line Ends"), 'editor.action.insertCursorAtEndOfEachLineSelected'); + const addSelectionToNextFindMatch = this.createMenuItem(nls.localize({ key: 'miAddSelectionToNextFindMatch', comment: ['&& denotes a mnemonic'] }, "Add &&Next Occurrence"), 'editor.action.addSelectionToNextFindMatch'); + const addSelectionToPreviousFindMatch = this.createMenuItem(nls.localize({ key: 'miAddSelectionToPreviousFindMatch', comment: ['&& denotes a mnemonic'] }, "Add P&&revious Occurrence"), 'editor.action.addSelectionToPreviousFindMatch'); + const selectHighlights = this.createMenuItem(nls.localize({ key: 'miSelectHighlights', comment: ['&& denotes a mnemonic'] }, "Select All &&Occurrences"), 'editor.action.selectHighlights'); + + const copyLinesUp = this.createMenuItem(nls.localize({ key: 'miCopyLinesUp', comment: ['&& denotes a mnemonic'] }, "&&Copy Line Up"), 'editor.action.copyLinesUpAction'); + const copyLinesDown = this.createMenuItem(nls.localize({ key: 'miCopyLinesDown', comment: ['&& denotes a mnemonic'] }, "Co&&py Line Down"), 'editor.action.copyLinesDownAction'); + const moveLinesUp = this.createMenuItem(nls.localize({ key: 'miMoveLinesUp', comment: ['&& denotes a mnemonic'] }, "Mo&&ve Line Up"), 'editor.action.moveLinesUpAction'); + const moveLinesDown = this.createMenuItem(nls.localize({ key: 'miMoveLinesDown', comment: ['&& denotes a mnemonic'] }, "Move &&Line Down"), 'editor.action.moveLinesDownAction'); + + let selectAll: Electron.MenuItem; + if (isMacintosh) { + selectAll = this.createDevToolsAwareMenuItem(nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), 'editor.action.selectAll', (devTools) => devTools.selectAll()); + } else { + selectAll = this.createMenuItem(nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), 'editor.action.selectAll'); + } + const smartSelectGrow = this.createMenuItem(nls.localize({ key: 'miSmartSelectGrow', comment: ['&& denotes a mnemonic'] }, "&&Expand Selection"), 'editor.action.smartSelect.grow'); + const smartSelectshrink = this.createMenuItem(nls.localize({ key: 'miSmartSelectShrink', comment: ['&& denotes a mnemonic'] }, "&&Shrink Selection"), 'editor.action.smartSelect.shrink'); + + [ + selectAll, + smartSelectGrow, + smartSelectshrink, + __separator__(), + copyLinesUp, + copyLinesDown, + moveLinesUp, + moveLinesDown, + __separator__(), + multicursorModifier, + insertCursorAbove, + insertCursorBelow, + insertCursorAtEndOfEachLineSelected, + addSelectionToNextFindMatch, + addSelectionToPreviousFindMatch, + selectHighlights, + ].forEach(item => winLinuxEditMenu.append(item)); + } + */ + + private setViewMenu(viewMenu: Electron.Menu): void { + // {{SQL CARBON EDIT}} + const servers = this.createMenuItem(nls.localize({ key: 'miViewRegisteredServers', comment: ['&& denotes a mnemonic'] }, "&&Servers"), 'workbench.view.connections'); + const tasks = this.createMenuItem(nls.localize({ key: 'miViewTasks', comment: ['&& denotes a mnemonic'] }, "&&Tasks"), 'workbench.view.taskHistory'); + const explorer = this.createMenuItem(nls.localize({ key: 'miViewExplorer', comment: ['&& denotes a mnemonic'] }, "&&Explorer"), 'workbench.view.explorer'); + const search = this.createMenuItem(nls.localize({ key: 'miViewSearch', comment: ['&& denotes a mnemonic'] }, "&&Search"), 'workbench.view.search'); + const scm = this.createMenuItem(nls.localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM"), 'workbench.view.scm'); + // {{SQL CARBON EDIT}} + // const debug = this.createMenuItem(nls.localize({ key: 'miViewDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug"), 'workbench.view.debug'); + // const extensions = this.createMenuItem(nls.localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions"), 'workbench.view.extensions'); + const output = this.createMenuItem(nls.localize({ key: 'miToggleOutput', comment: ['&& denotes a mnemonic'] }, "&&Output"), 'workbench.action.output.toggleOutput'); + // {{SQL CARBON EDIT}} + // const debugConsole = this.createMenuItem(nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console"), 'workbench.debug.action.toggleRepl'); + const integratedTerminal = this.createMenuItem(nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Integrated Terminal"), 'workbench.action.terminal.toggleTerminal'); + const problems = this.createMenuItem(nls.localize({ key: 'miMarker', comment: ['&& denotes a mnemonic'] }, "&&Problems"), 'workbench.actions.view.problems'); + + let additionalViewlets: Electron.MenuItem; + if (this.extensionViewlets.length) { + const additionalViewletsMenu = new Menu(); + + this.extensionViewlets.forEach(viewlet => { + additionalViewletsMenu.append(this.createMenuItem(viewlet.label, viewlet.id)); + }); + + additionalViewlets = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miAdditionalViews', comment: ['&& denotes a mnemonic'] }, "Additional &&Views")), submenu: additionalViewletsMenu, enabled: true }); + } + + const commands = this.createMenuItem(nls.localize({ key: 'miCommandPalette', comment: ['&& denotes a mnemonic'] }, "&&Command Palette..."), 'workbench.action.showCommands'); + + const fullscreen = new MenuItem(this.withKeybinding('workbench.action.toggleFullScreen', { label: this.mnemonicLabel(nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen")), click: () => this.windowsService.getLastActiveWindow().toggleFullScreen(), enabled: this.windowsService.getWindowCount() > 0 })); + const toggleZenMode = this.createMenuItem(nls.localize('miToggleZenMode', "Toggle Zen Mode"), 'workbench.action.toggleZenMode'); + const toggleMenuBar = this.createMenuItem(nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar"), 'workbench.action.toggleMenuBar'); + // {{SQL CARBON EDIT}} + //const splitEditor = this.createMenuItem(nls.localize({ key: 'miSplitEditor', comment: ['&& denotes a mnemonic'] }, "Split &&Editor"), 'workbench.action.splitEditor'); + const toggleEditorLayout = this.createMenuItem(nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Toggle Editor Group &&Layout"), 'workbench.action.toggleEditorGroupLayout'); + const toggleSidebar = this.createMenuItem(nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar"), 'workbench.action.toggleSidebarVisibility'); + + let moveSideBarLabel: string; + if (this.currentSidebarLocation !== 'right') { + moveSideBarLabel = nls.localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right"); + } else { + moveSideBarLabel = nls.localize({ key: 'miMoveSidebarLeft', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left"); + } + + const moveSidebar = this.createMenuItem(moveSideBarLabel, 'workbench.action.toggleSidebarPosition'); + + // {{SQL CARBON EDIT}} + // const togglePanel = this.createMenuItem(nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel"), 'workbench.action.togglePanel'); + + let statusBarLabel: string; + if (this.currentStatusbarVisible) { + statusBarLabel = nls.localize({ key: 'miHideStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Hide Status Bar"); + } else { + statusBarLabel = nls.localize({ key: 'miShowStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Show Status Bar"); + } + const toggleStatusbar = this.createMenuItem(statusBarLabel, 'workbench.action.toggleStatusbarVisibility'); + + let activityBarLabel: string; + if (this.currentActivityBarVisible) { + activityBarLabel = nls.localize({ key: 'miHideActivityBar', comment: ['&& denotes a mnemonic'] }, "Hide &&Activity Bar"); + } else { + activityBarLabel = nls.localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar"); + } + const toggleActivtyBar = this.createMenuItem(activityBarLabel, 'workbench.action.toggleActivityBarVisibility'); + + // {{SQL CARBON EDIT}} + // const toggleWordWrap = this.createMenuItem(nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap"), 'editor.action.toggleWordWrap'); + // const toggleMinimap = this.createMenuItem(nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap"), 'editor.action.toggleMinimap'); + // const toggleRenderWhitespace = this.createMenuItem(nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace"), 'editor.action.toggleRenderWhitespace'); + // const toggleRenderControlCharacters = this.createMenuItem(nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters"), 'editor.action.toggleRenderControlCharacter'); + + const zoomIn = this.createMenuItem(nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In"), 'workbench.action.zoomIn'); + const zoomOut = this.createMenuItem(nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "Zoom O&&ut"), 'workbench.action.zoomOut'); + const resetZoom = this.createMenuItem(nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom"), 'workbench.action.zoomReset'); + + arrays.coalesce([ + commands, + __separator__(), + servers, + tasks, + explorer, + search, + scm, + // {{SQL CARBON EDIT}} + // debug, + // extensions, + additionalViewlets, + __separator__(), + output, + problems, + // {{SQL CARBON EDIT}} + // debugConsole, + integratedTerminal, + __separator__(), + fullscreen, + toggleZenMode, + isWindows || isLinux ? toggleMenuBar : void 0, + __separator__(), + // {{SQL CARBON EDIT}} + //splitEditor, + toggleEditorLayout, + moveSidebar, + toggleSidebar, + // togglePanel, + toggleStatusbar, + toggleActivtyBar, + // {{SQL CARBON EDIT}} + // __separator__(), + // toggleWordWrap, + // toggleMinimap, + // toggleRenderWhitespace, + // toggleRenderControlCharacters, + __separator__(), + zoomIn, + zoomOut, + resetZoom + ]).forEach(item => viewMenu.append(item)); + } + + private setGotoMenu(gotoMenu: Electron.Menu): void { + const back = this.createMenuItem(nls.localize({ key: 'miBack', comment: ['&& denotes a mnemonic'] }, "&&Back"), 'workbench.action.navigateBack'); + const forward = this.createMenuItem(nls.localize({ key: 'miForward', comment: ['&& denotes a mnemonic'] }, "&&Forward"), 'workbench.action.navigateForward'); + + const switchEditorMenu = new Menu(); + + const nextEditor = this.createMenuItem(nls.localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor"), 'workbench.action.nextEditor'); + const previousEditor = this.createMenuItem(nls.localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor"), 'workbench.action.previousEditor'); + const nextEditorInGroup = this.createMenuItem(nls.localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group"), 'workbench.action.openNextRecentlyUsedEditorInGroup'); + const previousEditorInGroup = this.createMenuItem(nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group"), 'workbench.action.openPreviousRecentlyUsedEditorInGroup'); + + [ + nextEditor, + previousEditor, + __separator__(), + nextEditorInGroup, + previousEditorInGroup + ].forEach(item => switchEditorMenu.append(item)); + + const switchEditor = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor")), submenu: switchEditorMenu, enabled: true }); + + const switchGroupMenu = new Menu(); + + const focusFirstGroup = this.createMenuItem(nls.localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "&&First Group"), 'workbench.action.focusFirstEditorGroup'); + const focusSecondGroup = this.createMenuItem(nls.localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "&&Second Group"), 'workbench.action.focusSecondEditorGroup'); + const focusThirdGroup = this.createMenuItem(nls.localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "&&Third Group"), 'workbench.action.focusThirdEditorGroup'); + const nextGroup = this.createMenuItem(nls.localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group"), 'workbench.action.focusNextGroup'); + const previousGroup = this.createMenuItem(nls.localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group"), 'workbench.action.focusPreviousGroup'); + + [ + focusFirstGroup, + focusSecondGroup, + focusThirdGroup, + __separator__(), + nextGroup, + previousGroup + ].forEach(item => switchGroupMenu.append(item)); + + const switchGroup = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group")), submenu: switchGroupMenu, enabled: true }); + + const gotoFile = this.createMenuItem(nls.localize({ key: 'miGotoFile', comment: ['&& denotes a mnemonic'] }, "Go to &&File..."), 'workbench.action.quickOpen'); + // {{SQL CARBON EDIT}} + // const gotoSymbolInFile = this.createMenuItem(nls.localize({ key: 'miGotoSymbolInFile', comment: ['&& denotes a mnemonic'] }, "Go to &&Symbol in File..."), 'workbench.action.gotoSymbol'); + // const gotoSymbolInWorkspace = this.createMenuItem(nls.localize({ key: 'miGotoSymbolInWorkspace', comment: ['&& denotes a mnemonic'] }, "Go to Symbol in &&Workspace..."), 'workbench.action.showAllSymbols'); + // const gotoDefinition = this.createMenuItem(nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition"), 'editor.action.goToDeclaration'); + const gotoTypeDefinition = this.createMenuItem(nls.localize({ key: 'miGotoTypeDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Type Definition"), 'editor.action.goToTypeDefinition'); + const goToImplementation = this.createMenuItem(nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementation"), 'editor.action.goToImplementation'); + const gotoLine = this.createMenuItem(nls.localize({ key: 'miGotoLine', comment: ['&& denotes a mnemonic'] }, "Go to &&Line..."), 'workbench.action.gotoLine'); + + [ + back, + forward, + __separator__(), + switchEditor, + switchGroup, + __separator__(), + gotoFile, + // {{SQL CARBON EDIT}} + // gotoSymbolInFile, + // gotoSymbolInWorkspace, + // gotoDefinition, + // gotoTypeDefinition, + // goToImplementation, + gotoLine + ].forEach(item => gotoMenu.append(item)); + } + + private setDebugMenu(debugMenu: Electron.Menu): void { + const start = this.createMenuItem(nls.localize({ key: 'miStartDebugging', comment: ['&& denotes a mnemonic'] }, "&&Start Debugging"), 'workbench.action.debug.start'); + const startWithoutDebugging = this.createMenuItem(nls.localize({ key: 'miStartWithoutDebugging', comment: ['&& denotes a mnemonic'] }, "Start &&Without Debugging"), 'workbench.action.debug.run'); + const stop = this.createMenuItem(nls.localize({ key: 'miStopDebugging', comment: ['&& denotes a mnemonic'] }, "&&Stop Debugging"), 'workbench.action.debug.stop'); + const restart = this.createMenuItem(nls.localize({ key: 'miRestart Debugging', comment: ['&& denotes a mnemonic'] }, "&&Restart Debugging"), 'workbench.action.debug.restart'); + + const openConfigurations = this.createMenuItem(nls.localize({ key: 'miOpenConfigurations', comment: ['&& denotes a mnemonic'] }, "Open &&Configurations"), 'workbench.action.debug.configure'); + const addConfiguration = this.createMenuItem(nls.localize({ key: 'miAddConfiguration', comment: ['&& denotes a mnemonic'] }, "Add Configuration..."), 'debug.addConfiguration'); + + const stepOver = this.createMenuItem(nls.localize({ key: 'miStepOver', comment: ['&& denotes a mnemonic'] }, "Step &&Over"), 'workbench.action.debug.stepOver'); + const stepInto = this.createMenuItem(nls.localize({ key: 'miStepInto', comment: ['&& denotes a mnemonic'] }, "Step &&Into"), 'workbench.action.debug.stepInto'); + const stepOut = this.createMenuItem(nls.localize({ key: 'miStepOut', comment: ['&& denotes a mnemonic'] }, "Step O&&ut"), 'workbench.action.debug.stepOut'); + const continueAction = this.createMenuItem(nls.localize({ key: 'miContinue', comment: ['&& denotes a mnemonic'] }, "&&Continue"), 'workbench.action.debug.continue'); + + const toggleBreakpoint = this.createMenuItem(nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breakpoint"), 'editor.debug.action.toggleBreakpoint'); + const breakpointsMenu = new Menu(); + breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Conditional Breakpoint..."), 'editor.debug.action.conditionalBreakpoint')); + breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miColumnBreakpoint', comment: ['&& denotes a mnemonic'] }, "C&&olumn Breakpoint"), 'editor.debug.action.toggleColumnBreakpoint')); + breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint..."), 'workbench.debug.viewlet.action.addFunctionBreakpointAction')); + const newBreakpoints = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&New Breakpoint")), submenu: breakpointsMenu }); + const enableAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Enable All Breakpoints"), 'workbench.debug.viewlet.action.enableAllBreakpoints'); + const disableAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints"), 'workbench.debug.viewlet.action.disableAllBreakpoints'); + const removeAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints"), 'workbench.debug.viewlet.action.removeAllBreakpoints'); + + const installAdditionalDebuggers = this.createMenuItem(nls.localize({ key: 'miInstallAdditionalDebuggers', comment: ['&& denotes a mnemonic'] }, "&&Install Additional Debuggers..."), 'debug.installAdditionalDebuggers'); + [ + start, + startWithoutDebugging, + stop, + restart, + __separator__(), + openConfigurations, + addConfiguration, + __separator__(), + stepOver, + stepInto, + stepOut, + continueAction, + __separator__(), + toggleBreakpoint, + newBreakpoints, + enableAllBreakpoints, + disableAllBreakpoints, + removeAllBreakpoints, + __separator__(), + installAdditionalDebuggers + ].forEach(item => debugMenu.append(item)); + } + + private setMacWindowMenu(macWindowMenu: Electron.Menu): void { + const minimize = new MenuItem({ label: nls.localize('mMinimize', "Minimize"), role: 'minimize', accelerator: 'Command+M', enabled: this.windowsService.getWindowCount() > 0 }); + const zoom = new MenuItem({ label: nls.localize('mZoom', "Zoom"), role: 'zoom', enabled: this.windowsService.getWindowCount() > 0 }); + const bringAllToFront = new MenuItem({ label: nls.localize('mBringToFront', "Bring All to Front"), role: 'front', enabled: this.windowsService.getWindowCount() > 0 }); + const switchWindow = this.createMenuItem(nls.localize({ key: 'miSwitchWindow', comment: ['&& denotes a mnemonic'] }, "Switch &&Window..."), 'workbench.action.switchWindow'); + + [ + minimize, + zoom, + switchWindow, + __separator__(), + bringAllToFront + ].forEach(item => macWindowMenu.append(item)); + } + + private toggleDevTools(): void { + const w = this.windowsService.getFocusedWindow(); + if (w && w.win) { + const contents = w.win.webContents; + if (w.hasHiddenTitleBarStyle() && !w.win.isFullScreen() && !contents.isDevToolsOpened()) { + contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647 + } else { + contents.toggleDevTools(); + } + } + } + + private setHelpMenu(helpMenu: Electron.Menu): void { + const toggleDevToolsItem = new MenuItem(this.likeAction('workbench.action.toggleDevTools', { + label: this.mnemonicLabel(nls.localize({ key: 'miToggleDevTools', comment: ['&& denotes a mnemonic'] }, "&&Toggle Developer Tools")), + click: () => this.toggleDevTools(), + enabled: (this.windowsService.getWindowCount() > 0) + })); + + const showAccessibilityOptions = new MenuItem(this.likeAction('accessibilityOptions', { + label: this.mnemonicLabel(nls.localize({ key: 'miAccessibilityOptions', comment: ['&& denotes a mnemonic'] }, "Accessibility &&Options")), + accelerator: null, + click: () => { + this.openAccessibilityOptions(); + } + }, false)); + + let reportIssuesItem: Electron.MenuItem = null; + if (product.reportIssueUrl) { + const label = nls.localize({ key: 'miReportIssues', comment: ['&& denotes a mnemonic'] }, "Report &&Issues"); + + if (this.windowsService.getWindowCount() > 0) { + reportIssuesItem = this.createMenuItem(label, 'workbench.action.reportIssues'); + } else { + reportIssuesItem = new MenuItem({ label: this.mnemonicLabel(label), click: () => this.openUrl(product.reportIssueUrl, 'openReportIssues') }); + } + } + + // {{SQL CARBON EDIT}} + // const keyboardShortcutsUrl = isLinux ? product.keyboardShortcutsUrlLinux : isMacintosh ? product.keyboardShortcutsUrlMac : product.keyboardShortcutsUrlWin; + arrays.coalesce([ + // new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showWelcomePage') }), + // {{SQL CARBON EDIT}} + product.releaseNotesUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "Getting &&Started")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'update.showCurrentCarbonReleaseNotes') }) : null, + product.documentationUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.openDocumentationUrl') }) : null, + // __separator__(), + // keyboardShortcutsUrl ? new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.keybindingsReference') }) : null, + // product.introductoryVideosUrl ? new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.openIntroductoryVideosUrl') }) : null, + // (product.introductoryVideosUrl || keyboardShortcutsUrl) ? __separator__() : null, + // product.twitterUrl ? new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join us on Twitter")), click: () => this.openUrl(product.twitterUrl, 'openTwitterUrl') }) : null, + // product.requestFeatureUrl ? new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests")), click: () => this.openUrl(product.requestFeatureUrl, 'openUserVoiceUrl') }) : null, + reportIssuesItem, + // (product.twitterUrl || product.requestFeatureUrl || product.reportIssueUrl) ? __separator__() : null, + // product.licenseUrl ? new MenuItem({ + // label: mnemonicLabel(nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License")), click: () => { + // if (language) { + // const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; + // this.openUrl(`${product.licenseUrl}${queryArgChar}lang=${language}`, 'openLicenseUrl'); + // } else { + // this.openUrl(product.licenseUrl, 'openLicenseUrl'); + // } + // } + // }) : null, + // product.privacyStatementUrl ? new MenuItem({ + // label: mnemonicLabel(nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "&&Privacy Statement")), click: () => { + // if (language) { + // const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; + // this.openUrl(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`, 'openPrivacyStatement'); + // } else { + // this.openUrl(product.privacyStatementUrl, 'openPrivacyStatement'); + // } + // } + // }) : null, + // (product.licenseUrl || product.privacyStatementUrl) ? __separator__() : null, + toggleDevToolsItem, + isWindows && product.quality !== 'stable' ? showAccessibilityOptions : null + ]).forEach(item => helpMenu.append(item)); + + if (!isMacintosh) { + const updateMenuItems = this.getUpdateMenuItems(); + if (updateMenuItems.length) { + helpMenu.append(__separator__()); + updateMenuItems.forEach(i => helpMenu.append(i)); + } + + helpMenu.append(__separator__()); + helpMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About")), click: () => this.openAboutDialog() })); + } + } + + private setTaskMenu(taskMenu: Electron.Menu): void { + // {{SQL CARBON EDIT}} + /* + const runTask = this.createMenuItem(nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task..."), 'workbench.action.tasks.runTask'); + const buildTask = this.createMenuItem(nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task..."), 'workbench.action.tasks.build'); + const showTasks = this.createMenuItem(nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks..."), 'workbench.action.tasks.showTasks'); + const restartTask = this.createMenuItem(nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task..."), 'workbench.action.tasks.restartTask'); + const terminateTask = this.createMenuItem(nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task..."), 'workbench.action.tasks.terminate'); + // const testTask = this.createMenuItem(nls.localize({ key: 'miTestTask', comment: ['&& denotes a mnemonic'] }, "Run Test T&&ask..."), 'workbench.action.tasks.test'); + // const showTaskLog = this.createMenuItem(nls.localize({ key: 'miShowTaskLog', comment: ['&& denotes a mnemonic'] }, "&&Show Task Log"), 'workbench.action.tasks.showLog'); + const configureTask = this.createMenuItem(nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks"), 'workbench.action.tasks.configureTaskRunner'); + const configureBuildTask = this.createMenuItem(nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task"), 'workbench.action.tasks.configureDefaultBuildTask'); + // const configureTestTask = this.createMenuItem(nls.localize({ key: 'miConfigureTestTask', comment: ['&& denotes a mnemonic'] }, "Configure Defau&< Test Task"), 'workbench.action.tasks.configureDefaultTestTask'); + + [ + //__separator__(), + runTask, + buildTask, + // testTask, + __separator__(), + terminateTask, + restartTask, + showTasks, + __separator__(), + //showTaskLog, + configureTask, + configureBuildTask + // configureTestTask + ].forEach(item => taskMenu.append(item)); + */ + } + + private openAccessibilityOptions(): void { + let win = new BrowserWindow({ + alwaysOnTop: true, + skipTaskbar: true, + resizable: false, + width: 450, + height: 300, + show: true, + title: nls.localize('accessibilityOptionsWindowTitle', "Accessibility Options") + }); + + win.setMenuBarVisibility(false); + + win.loadURL('chrome://accessibility'); + } + + private getUpdateMenuItems(): Electron.MenuItem[] { + switch (this.updateService.state) { + case UpdateState.Uninitialized: + return []; + + case UpdateState.UpdateDownloaded: + return [new MenuItem({ + label: nls.localize('miRestartToUpdate', "Restart to Update..."), click: () => { + this.reportMenuActionTelemetry('RestartToUpdate'); + this.updateService.quitAndInstall(); + } + })]; + + case UpdateState.CheckingForUpdate: + return [new MenuItem({ label: nls.localize('miCheckingForUpdates', "Checking For Updates..."), enabled: false })]; + + case UpdateState.UpdateAvailable: + if (isLinux) { + return [new MenuItem({ + label: nls.localize('miDownloadUpdate', "Download Available Update"), click: () => { + this.updateService.quitAndInstall(); + } + })]; + } + + const updateAvailableLabel = isWindows + ? nls.localize('miDownloadingUpdate', "Downloading Update...") + : nls.localize('miInstallingUpdate', "Installing Update..."); + + return [new MenuItem({ label: updateAvailableLabel, enabled: false })]; + + default: + const result = [new MenuItem({ + label: nls.localize('miCheckForUpdates', "Check for Updates..."), click: () => setTimeout(() => { + this.reportMenuActionTelemetry('CheckForUpdate'); + this.updateService.checkForUpdates(true); + }, 0) + })]; + + return result; + } + } + + private createMenuItem(label: string, commandId: string | string[], enabled?: boolean, checked?: boolean): Electron.MenuItem; + private createMenuItem(label: string, click: () => void, enabled?: boolean, checked?: boolean): Electron.MenuItem; + private createMenuItem(arg1: string, arg2: any, arg3?: boolean, arg4?: boolean): Electron.MenuItem { + const label = this.mnemonicLabel(arg1); + const click: () => void = (typeof arg2 === 'function') ? arg2 : (menuItem, win, event) => { + let commandId = arg2; + if (Array.isArray(arg2)) { + commandId = this.isOptionClick(event) ? arg2[1] : arg2[0]; // support alternative action if we got multiple action Ids and the option key was pressed while invoking + } + + this.windowsService.sendToFocused('vscode:runAction', commandId); + }; + const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsService.getWindowCount() > 0; + const checked = typeof arg4 === 'boolean' ? arg4 : false; + + let commandId: string; + if (typeof arg2 === 'string') { + commandId = arg2; + } + + const options: Electron.MenuItemOptions = { + label, + click, + enabled + }; + + if (checked) { + options['type'] = 'checkbox'; + options['checked'] = checked; + } + + return new MenuItem(this.withKeybinding(commandId, options)); + } + + private createDevToolsAwareMenuItem(label: string, commandId: string, devToolsFocusedFn: (contents: Electron.WebContents) => void): Electron.MenuItem { + return new MenuItem(this.withKeybinding(commandId, { + label: this.mnemonicLabel(label), + enabled: this.windowsService.getWindowCount() > 0, + click: () => { + const windowInFocus = this.windowsService.getFocusedWindow(); + if (!windowInFocus) { + return; + } + + if (windowInFocus.win.webContents.isDevToolsFocused()) { + devToolsFocusedFn(windowInFocus.win.webContents.devToolsWebContents); + } else { + this.windowsService.sendToFocused('vscode:runAction', commandId); + } + } + })); + } + + private withKeybinding(commandId: string, options: Electron.MenuItemOptions): Electron.MenuItemOptions { + const binding = this.keybindingsResolver.getKeybinding(commandId); + + // Apply binding if there is one + if (binding && binding.label) { + + // if the binding is native, we can just apply it + if (binding.isNative) { + options.accelerator = binding.label; + } + + // the keybinding is not native so we cannot show it as part of the accelerator of + // the menu item. we fallback to a different strategy so that we always display it + else { + const bindingIndex = options.label.indexOf('['); + if (bindingIndex >= 0) { + options.label = `${options.label.substr(0, bindingIndex)} [${binding.label}]`; + } else { + options.label = `${options.label} [${binding.label}]`; + } + } + } + + // Unset bindings if there is none + else { + options.accelerator = void 0; + } + + return options; + } + + private likeAction(commandId: string, options: Electron.MenuItemOptions, setAccelerator = !options.accelerator): Electron.MenuItemOptions { + if (setAccelerator) { + options = this.withKeybinding(commandId, options); + } + + const originalClick = options.click; + options.click = (item, window, event) => { + this.reportMenuActionTelemetry(commandId); + if (originalClick) { + originalClick(item, window, event); + } + }; + + return options; + } + + private openAboutDialog(): void { + const lastActiveWindow = this.windowsService.getFocusedWindow() || this.windowsService.getLastActiveWindow(); + + dialog.showMessageBox(lastActiveWindow && lastActiveWindow.win, { + title: product.nameLong, + type: 'info', + message: product.nameLong, + detail: nls.localize('aboutDetail', + "\nVersion {0}\nCommit {1}\nDate {2}\nShell {3}\nRenderer {4}\nNode {5}\nArchitecture {6}", + app.getVersion(), + product.commit || 'Unknown', + product.date || 'Unknown', + process.versions['electron'], + process.versions['chrome'], + process.versions['node'], + process.arch + ), + buttons: [nls.localize('okButton', "OK")], + noLink: true + }, result => null); + + this.reportMenuActionTelemetry('showAboutDialog'); + } + + private openUrl(url: string, id: string): void { + shell.openExternal(url); + this.reportMenuActionTelemetry(id); + } + + private reportMenuActionTelemetry(id: string): void { + this.telemetryService.publicLog('workbenchActionExecuted', { id, from: telemetryFrom }); + } + + private mnemonicLabel(label: string): string { + return baseMnemonicLabel(label, !this.currentEnableMenuBarMnemonics); + } +} + +function __separator__(): Electron.MenuItem { + return new MenuItem({ type: 'separator' }); +} diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts new file mode 100644 index 0000000000..08d304a528 --- /dev/null +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -0,0 +1,115 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { assign } from 'vs/base/common/objects'; +import { memoize } from 'vs/base/common/decorators'; +import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IProcessEnvironment } from 'vs/base/common/platform'; +import { BrowserWindow, ipcMain } from 'electron'; +import { PromiseSource } from 'vs/base/common/async'; +import { ISharedProcess } from 'vs/platform/windows/electron-main/windows'; + +export class SharedProcess implements ISharedProcess { + + private spawnPromiseSource: PromiseSource; + + private window: Electron.BrowserWindow; + private disposables: IDisposable[] = []; + + @memoize + private get _whenReady(): TPromise { + this.window = new BrowserWindow({ + show: false, + webPreferences: { + images: false, + webaudio: false, + webgl: false + } + }); + const config = assign({ + appRoot: this.environmentService.appRoot, + nodeCachedDataDir: this.environmentService.nodeCachedDataDir, + userEnv: this.userEnv + }); + + const url = `${require.toUrl('vs/code/electron-browser/sharedProcess.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; + this.window.loadURL(url); + + // Prevent the window from dying + const onClose = e => { + if (this.window.isVisible()) { + e.preventDefault(); + this.window.hide(); + } + }; + + this.window.on('close', onClose); + this.disposables.push(toDisposable(() => this.window.removeListener('close', onClose))); + + this.disposables.push(toDisposable(() => { + + // Electron seems to crash on Windows without this setTimeout :| + setTimeout(() => { + try { + this.window.close(); + } catch (err) { + // ignore, as electron is already shutting down + } + + this.window = null; + }, 0); + })); + + return new TPromise((c, e) => { + ipcMain.once('handshake:hello', ({ sender }) => { + sender.send('handshake:hey there', { + sharedIPCHandle: this.environmentService.sharedIPCHandle, + args: this.environmentService.args + }); + + ipcMain.once('handshake:im ready', () => c(null)); + }); + }); + } + + constructor( + private environmentService: IEnvironmentService, + private userEnv: IProcessEnvironment + ) { + this.spawnPromiseSource = new PromiseSource(); + } + + public spawn(): void { + this.spawnPromiseSource.complete(); + } + + public whenReady(): TPromise { + return this.spawnPromiseSource.value.then(() => this._whenReady); + } + + public toggle(): void { + if (this.window.isVisible()) { + this.hide(); + } else { + this.show(); + } + } + + public show(): void { + this.window.show(); + this.window.webContents.openDevTools(); + } + + public hide(): void { + this.window.webContents.closeDevTools(); + this.window.hide(); + } + + public dispose(): void { + this.disposables = dispose(this.disposables); + } +} diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts new file mode 100644 index 0000000000..1625b3e8a8 --- /dev/null +++ b/src/vs/code/electron-main/window.ts @@ -0,0 +1,868 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as path from 'path'; +import * as objects from 'vs/base/common/objects'; +import { stopProfiling } from 'vs/base/node/profiler'; +import nls = require('vs/nls'); +import URI from 'vs/base/common/uri'; +import { IStorageService } from 'vs/platform/storage/node/storage'; +import { shell, screen, BrowserWindow, systemPreferences, app } from 'electron'; +import { TPromise, TValueCallback } from 'vs/base/common/winjs.base'; +import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { parseArgs } from 'vs/platform/environment/node/argv'; +import product from 'vs/platform/node/product'; +import pkg from 'vs/platform/node/package'; +import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState } from 'vs/platform/windows/common/windows'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard'; +import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; +import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; +import { IWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; +import { IBackupMainService } from 'vs/platform/backup/common/backup'; + +export interface IWindowState { + width?: number; + height?: number; + x?: number; + y?: number; + mode?: WindowMode; + display?: number; +} + +export interface IWindowCreationOptions { + state: IWindowState; + extensionDevelopmentPath?: string; + isExtensionTestHost?: boolean; +} + +export enum WindowMode { + Maximized, + Normal, + Minimized, // not used anymore, but also cannot remove due to existing stored UI state (needs migration) + Fullscreen +} + +export const defaultWindowState = function (mode = WindowMode.Normal): IWindowState { + return { + width: 1024, + height: 768, + mode + }; +}; + +interface IWorkbenchEditorConfiguration { + workbench: { + editor: { + swipeToNavigate: boolean + } + }; +} + +export class CodeWindow implements ICodeWindow { + + public static themeStorageKey = 'theme'; + public static themeBackgroundStorageKey = 'themeBackground'; + + private static MIN_WIDTH = 200; + private static MIN_HEIGHT = 120; + + private hiddenTitleBarStyle: boolean; + private showTimeoutHandle: any; + private _id: number; + private _win: Electron.BrowserWindow; + private _lastFocusTime: number; + private _readyState: ReadyState; + private windowState: IWindowState; + private currentMenuBarVisibility: MenuBarVisibility; + private toDispose: IDisposable[]; + private representedFilename: string; + + private whenReadyCallbacks: TValueCallback[]; + + private currentConfig: IWindowConfiguration; + private pendingLoadConfig: IWindowConfiguration; + + constructor( + config: IWindowCreationOptions, + @ILogService private logService: ILogService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IConfigurationService private configurationService: IConfigurationService, + @IStorageService private storageService: IStorageService, + @IWorkspacesMainService private workspaceService: IWorkspacesMainService, + @IBackupMainService private backupService: IBackupMainService + ) { + this._lastFocusTime = -1; + this._readyState = ReadyState.NONE; + this.whenReadyCallbacks = []; + this.toDispose = []; + + // create browser window + this.createBrowserWindow(config); + + // respect configured menu bar visibility + this.onConfigurationUpdated(); + + // Eventing + this.registerListeners(); + } + + private createBrowserWindow(config: IWindowCreationOptions): void { + + // Load window state + this.windowState = this.restoreWindowState(config.state); + + // in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below) + const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen); + + const options: Electron.BrowserWindowOptions = { + width: this.windowState.width, + height: this.windowState.height, + x: this.windowState.x, + y: this.windowState.y, + backgroundColor: this.getBackgroundColor(), + minWidth: CodeWindow.MIN_WIDTH, + minHeight: CodeWindow.MIN_HEIGHT, + show: !isFullscreenOrMaximized, + title: product.nameLong, + webPreferences: { + 'backgroundThrottling': false, // by default if Code is in the background, intervals and timeouts get throttled, + disableBlinkFeatures: 'Auxclick' // disable auxclick events (see https://developers.google.com/web/updates/2016/10/auxclick) + } + }; + + if (isLinux) { + options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); // Windows and Mac are better off using the embedded icon(s) + } + + const windowConfig = this.configurationService.getConfiguration('window'); + + let useNativeTabs = false; + if (windowConfig && windowConfig.nativeTabs) { + options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs + useNativeTabs = true; + } + + let useCustomTitleStyle = false; + + // {{SQL CARBON EDIT}} + // turn-off custom menus to avoid bug calculating size of SQL editor + /* + if (isMacintosh && (!windowConfig || !windowConfig.titleBarStyle || windowConfig.titleBarStyle === 'custom')) { + const isDev = !this.environmentService.isBuilt || !!config.extensionDevelopmentPath; + if (!isDev) { + useCustomTitleStyle = true; // not enabled when developing due to https://github.com/electron/electron/issues/3647 + } + } + */ + + if (useNativeTabs) { + useCustomTitleStyle = false; // native tabs on sierra do not work with custom title style + } + + if (useCustomTitleStyle) { + options.titleBarStyle = 'hidden'; + this.hiddenTitleBarStyle = true; + } + + // Create the browser window. + this._win = new BrowserWindow(options); + this._id = this._win.id; + + if (useCustomTitleStyle) { + this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any + } + + // Set relaunch command + if (isWindows && product.win32AppUserModelId && typeof this._win.setAppDetails === 'function') { + this._win.setAppDetails({ + appId: product.win32AppUserModelId, + relaunchCommand: `"${process.execPath}" -n`, + relaunchDisplayName: product.nameLong + }); + } + + if (isFullscreenOrMaximized) { + this._win.maximize(); + + if (this.windowState.mode === WindowMode.Fullscreen) { + this._win.setFullScreen(true); + } + + if (!this._win.isVisible()) { + this._win.show(); // to reduce flicker from the default window size to maximize, we only show after maximize + } + } + + this._lastFocusTime = Date.now(); // since we show directly, we need to set the last focus time too + } + + public hasHiddenTitleBarStyle(): boolean { + return this.hiddenTitleBarStyle; + } + + public get isExtensionDevelopmentHost(): boolean { + return !!this.config.extensionDevelopmentPath; + } + + public get isExtensionTestHost(): boolean { + return !!this.config.extensionTestsPath; + } + + public get extensionDevelopmentPath(): string { + return this.config.extensionDevelopmentPath; + } + + public get config(): IWindowConfiguration { + return this.currentConfig; + } + + public get id(): number { + return this._id; + } + + public get win(): Electron.BrowserWindow { + return this._win; + } + + public setRepresentedFilename(filename: string): void { + if (isMacintosh) { + this.win.setRepresentedFilename(filename); + } else { + this.representedFilename = filename; + } + } + + public getRepresentedFilename(): string { + if (isMacintosh) { + return this.win.getRepresentedFilename(); + } + + return this.representedFilename; + } + + public focus(): void { + if (!this._win) { + return; + } + + if (this._win.isMinimized()) { + this._win.restore(); + } + + this._win.focus(); + } + + public get lastFocusTime(): number { + return this._lastFocusTime; + } + + public get backupPath(): string { + return this.currentConfig ? this.currentConfig.backupPath : void 0; + } + + public get openedWorkspace(): IWorkspaceIdentifier { + return this.currentConfig ? this.currentConfig.workspace : void 0; + } + + public get openedFolderPath(): string { + return this.currentConfig ? this.currentConfig.folderPath : void 0; + } + + public get openedFilePath(): string { + return this.currentConfig && this.currentConfig.filesToOpen && this.currentConfig.filesToOpen[0] && this.currentConfig.filesToOpen[0].filePath; + } + + public setReady(): void { + this._readyState = ReadyState.READY; + + // inform all waiting promises that we are ready now + while (this.whenReadyCallbacks.length) { + this.whenReadyCallbacks.pop()(this); + } + } + + public ready(): TPromise { + return new TPromise((c) => { + if (this._readyState === ReadyState.READY) { + return c(this); + } + + // otherwise keep and call later when we are ready + this.whenReadyCallbacks.push(c); + }); + } + + public get readyState(): ReadyState { + return this._readyState; + } + + private registerListeners(): void { + const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; + const headers = { + 'X-Market-Client-Id': `VSCode ${pkg.version}`, + 'User-Agent': `VSCode ${pkg.version}`, + 'X-Market-User-Id': this.environmentService.machineUUID + }; + + this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => { + cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) }); + }); + + // Prevent loading of svgs + this._win.webContents.session.webRequest.onBeforeRequest((details, callback) => { + if (details.url.indexOf('.svg') > 0) { + const uri = URI.parse(details.url); + if (uri && !uri.scheme.match(/file/i) && (uri.path as any).endsWith('.svg')) { + return callback({ cancel: true }); + } + } + + return callback({}); + }); + + this._win.webContents.session.webRequest.onHeadersReceived((details, callback) => { + const contentType: string[] = (details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']) as any; + if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { + return callback({ cancel: true }); + } + + return callback({ cancel: false, responseHeaders: details.responseHeaders }); + }); + + // Remember that we loaded + this._win.webContents.on('did-finish-load', () => { + this._readyState = ReadyState.LOADING; + + // Associate properties from the load request if provided + if (this.pendingLoadConfig) { + this.currentConfig = this.pendingLoadConfig; + + this.pendingLoadConfig = null; + } + + // To prevent flashing, we set the window visible after the page has finished to load but before Code is loaded + if (!this._win.isVisible()) { + if (this.windowState.mode === WindowMode.Maximized) { + this._win.maximize(); + } + + if (!this._win.isVisible()) { // maximize also makes visible + this._win.show(); + } + } + }); + + // App commands support + this.registerNavigationListenerOn('app-command', 'browser-backward', 'browser-forward', false); + + // Handle code that wants to open links + this._win.webContents.on('new-window', (event: Event, url: string) => { + event.preventDefault(); + + shell.openExternal(url); + }); + + // Window Focus + this._win.on('focus', () => { + this._lastFocusTime = Date.now(); + }); + + // Window Fullscreen + this._win.on('enter-full-screen', () => { + this.sendWhenReady('vscode:enterFullScreen'); + }); + + this._win.on('leave-full-screen', () => { + this.sendWhenReady('vscode:leaveFullScreen'); + }); + + // Window Failed to load + this._win.webContents.on('did-fail-load', (event: Event, errorCode: string, errorDescription: string) => { + this.logService.warn('[electron event]: fail to load, ', errorDescription); + }); + + // Prevent any kind of navigation triggered by the user! + // But do not touch this in dev version because it will prevent "Reload" from dev tools + if (this.environmentService.isBuilt) { + this._win.webContents.on('will-navigate', (event: Event) => { + if (event) { + event.preventDefault(); + } + }); + } + + // Handle configuration changes + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated())); + + // Handle Workspace events + this.toDispose.push(this.workspaceService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e))); + } + + private onUntitledWorkspaceDeleted(workspace: IWorkspaceIdentifier): void { + + // Make sure to update our workspace config if we detect that it + // was deleted + if (this.openedWorkspace && this.openedWorkspace.id === workspace.id) { + this.currentConfig.workspace = void 0; + } + } + + private onConfigurationUpdated(): void { + const newMenuBarVisibility = this.getMenuBarVisibility(); + if (newMenuBarVisibility !== this.currentMenuBarVisibility) { + this.currentMenuBarVisibility = newMenuBarVisibility; + this.setMenuBarVisibility(newMenuBarVisibility); + } + + // Swipe command support (macOS) + if (isMacintosh) { + const config = this.configurationService.getConfiguration(); + if (config && config.workbench && config.workbench.editor && config.workbench.editor.swipeToNavigate) { + this.registerNavigationListenerOn('swipe', 'left', 'right', true); + } else { + this._win.removeAllListeners('swipe'); + } + } + }; + + private registerNavigationListenerOn(command: 'swipe' | 'app-command', back: 'left' | 'browser-backward', forward: 'right' | 'browser-forward', acrossEditors: boolean) { + this._win.on(command, (e, cmd) => { + if (this.readyState !== ReadyState.READY) { + return; // window must be ready + } + + if (cmd === back) { + this.send('vscode:runAction', acrossEditors ? 'workbench.action.openPreviousRecentlyUsedEditor' : 'workbench.action.navigateBack'); + } else if (cmd === forward) { + this.send('vscode:runAction', acrossEditors ? 'workbench.action.openNextRecentlyUsedEditor' : 'workbench.action.navigateForward'); + } + }); + } + + public load(config: IWindowConfiguration, isReload?: boolean): void { + + // If this is the first time the window is loaded, we associate the paths + // directly with the window because we assume the loading will just work + if (this.readyState === ReadyState.NONE) { + this.currentConfig = config; + } + + // Otherwise, the window is currently showing a folder and if there is an + // unload handler preventing the load, we cannot just associate the paths + // because the loading might be vetoed. Instead we associate it later when + // the window load event has fired. + else { + this.pendingLoadConfig = config; + this._readyState = ReadyState.NAVIGATING; + } + + // Clear Document Edited if needed + if (isMacintosh && this._win.isDocumentEdited()) { + if (!isReload || !this.backupService.isHotExitEnabled()) { + this._win.setDocumentEdited(false); + } + } + + // Clear Title and Filename if needed + if (!isReload) { + if (this.getRepresentedFilename()) { + this.setRepresentedFilename(''); + } + + this._win.setTitle(product.nameLong); + } + + // Load URL + this._win.loadURL(this.getUrl(config)); + + // Make window visible if it did not open in N seconds because this indicates an error + // Only do this when running out of sources and not when running tests + if (!this.environmentService.isBuilt && !this.environmentService.extensionTestsPath) { + this.showTimeoutHandle = setTimeout(() => { + if (this._win && !this._win.isVisible() && !this._win.isMinimized()) { + this._win.show(); + this._win.focus(); + this._win.webContents.openDevTools(); + } + }, 10000); + } + + // (--prof-startup) save profile to disk + const { profileStartup } = this.environmentService; + if (profileStartup) { + stopProfiling(profileStartup.dir, profileStartup.prefix).done(undefined, err => this.logService.error(err)); + } + } + + public reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void { + + // If config is not provided, copy our current one + if (!configuration) { + configuration = objects.mixin({}, this.currentConfig); + } + + // Delete some properties we do not want during reload + delete configuration.filesToOpen; + delete configuration.filesToCreate; + delete configuration.filesToDiff; + + // Some configuration things get inherited if the window is being reloaded and we are + // in extension development mode. These options are all development related. + if (this.isExtensionDevelopmentHost && cli) { + configuration.verbose = cli.verbose; + configuration.debugPluginHost = cli.debugPluginHost; + configuration.debugBrkPluginHost = cli.debugBrkPluginHost; + configuration.debugId = cli.debugId; + configuration['extensions-dir'] = cli['extensions-dir']; + } + + configuration.isInitialStartup = false; // since this is a reload + + // Load config + this.load(configuration, true); + } + + private getUrl(windowConfiguration: IWindowConfiguration): string { + + // Set zoomlevel + const windowConfig = this.configurationService.getConfiguration('window'); + const zoomLevel = windowConfig && windowConfig.zoomLevel; + if (typeof zoomLevel === 'number') { + windowConfiguration.zoomLevel = zoomLevel; + } + + // Set fullscreen state + windowConfiguration.fullscreen = this._win.isFullScreen(); + + // Set Accessibility Config + windowConfiguration.highContrast = isWindows && systemPreferences.isInvertedColorScheme() && (!windowConfig || windowConfig.autoDetectHighContrast); + windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled(); + + // Set Keyboard Config + windowConfiguration.isISOKeyboard = KeyboardLayoutMonitor.INSTANCE.isISOKeyboard(); + + // Theme + windowConfiguration.baseTheme = this.getBaseTheme(); + windowConfiguration.backgroundColor = this.getBackgroundColor(); + + // Perf Counters + windowConfiguration.perfStartTime = global.perfStartTime; + windowConfiguration.perfAppReady = global.perfAppReady; + windowConfiguration.perfWindowLoadTime = Date.now(); + + // Config (combination of process.argv and window configuration) + const environment = parseArgs(process.argv); + const config = objects.assign(environment, windowConfiguration); + for (let key in config) { + if (!config[key]) { + delete config[key]; // only send over properties that have a true value + } + } + + return `${require.toUrl('vs/workbench/electron-browser/bootstrap/index.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; + } + + private getBaseTheme(): string { + if (isWindows && systemPreferences.isInvertedColorScheme()) { + return 'hc-black'; + } + + const theme = this.storageService.getItem(CodeWindow.themeStorageKey, 'vs-dark'); + + return theme.split(' ')[0]; + } + + private getBackgroundColor(): string { + if (isWindows && systemPreferences.isInvertedColorScheme()) { + return '#000000'; + } + + const background = this.storageService.getItem(CodeWindow.themeBackgroundStorageKey, null); + if (!background) { + const baseTheme = this.getBaseTheme(); + + return baseTheme === 'hc-black' ? '#000000' : (baseTheme === 'vs' ? '#FFFFFF' : (isMacintosh ? '#171717' : '#1E1E1E')); // https://github.com/electron/electron/issues/5150 + } + + return background; + } + + public serializeWindowState(): IWindowState { + + // fullscreen gets special treatment + if (this._win.isFullScreen()) { + const display = screen.getDisplayMatching(this.getBounds()); + + return { + mode: WindowMode.Fullscreen, + display: display ? display.id : void 0, + + // still carry over window dimensions from previous sessions! + width: this.windowState.width, + height: this.windowState.height, + x: this.windowState.x, + y: this.windowState.y + }; + } + + const state: IWindowState = Object.create(null); + let mode: WindowMode; + + // get window mode + if (!isMacintosh && this._win.isMaximized()) { + mode = WindowMode.Maximized; + } else { + mode = WindowMode.Normal; + } + + // we don't want to save minimized state, only maximized or normal + if (mode === WindowMode.Maximized) { + state.mode = WindowMode.Maximized; + } else { + state.mode = WindowMode.Normal; + } + + // only consider non-minimized window states + if (mode === WindowMode.Normal || mode === WindowMode.Maximized) { + const bounds = this.getBounds(); + + state.x = bounds.x; + state.y = bounds.y; + state.width = bounds.width; + state.height = bounds.height; + } + + return state; + } + + private restoreWindowState(state?: IWindowState): IWindowState { + if (state) { + try { + state = this.validateWindowState(state); + } catch (err) { + this.logService.log(`Unexpected error validating window state: ${err}\n${err.stack}`); // somehow display API can be picky about the state to validate + } + } + + if (!state) { + state = defaultWindowState(); + } + + return state; + } + + private validateWindowState(state: IWindowState): IWindowState { + if (!state) { + return null; + } + + if ([state.x, state.y, state.width, state.height].some(n => typeof n !== 'number')) { + return null; + } + + if (state.width <= 0 || state.height <= 0) { + return null; + } + + const displays = screen.getAllDisplays(); + + // Single Monitor: be strict about x/y positioning + if (displays.length === 1) { + const displayBounds = displays[0].bounds; + + // Careful with maximized: in that mode x/y can well be negative! + if (state.mode !== WindowMode.Maximized && displayBounds.width > 0 && displayBounds.height > 0 /* Linux X11 sessions sometimes report wrong display bounds */) { + if (state.x < displayBounds.x) { + state.x = displayBounds.x; // prevent window from falling out of the screen to the left + } + + if (state.y < displayBounds.y) { + state.y = displayBounds.y; // prevent window from falling out of the screen to the top + } + + if (state.x > (displayBounds.x + displayBounds.width)) { + state.x = displayBounds.x; // prevent window from falling out of the screen to the right + } + + if (state.y > (displayBounds.y + displayBounds.height)) { + state.y = displayBounds.y; // prevent window from falling out of the screen to the bottom + } + + if (state.width > displayBounds.width) { + state.width = displayBounds.width; // prevent window from exceeding display bounds width + } + + if (state.height > displayBounds.height) { + state.height = displayBounds.height; // prevent window from exceeding display bounds height + } + } + + if (state.mode === WindowMode.Maximized) { + return defaultWindowState(WindowMode.Maximized); // when maximized, make sure we have good values when the user restores the window + } + + return state; + } + + // Multi Montior (fullscreen): try to find the previously used display + if (state.display && state.mode === WindowMode.Fullscreen) { + const display = displays.filter(d => d.id === state.display)[0]; + if (display && display.bounds && typeof display.bounds.x === 'number' && typeof display.bounds.y === 'number') { + const defaults = defaultWindowState(WindowMode.Fullscreen); // make sure we have good values when the user restores the window + defaults.x = display.bounds.x; // carefull to use displays x/y position so that the window ends up on the correct monitor + defaults.y = display.bounds.y; + + return defaults; + } + } + + // Multi Monitor (non-fullscreen): be less strict because metrics can be crazy + const bounds = { x: state.x, y: state.y, width: state.width, height: state.height }; + const display = screen.getDisplayMatching(bounds); + if (display && display.bounds.x + display.bounds.width > bounds.x && display.bounds.y + display.bounds.height > bounds.y) { + if (state.mode === WindowMode.Maximized) { + const defaults = defaultWindowState(WindowMode.Maximized); // when maximized, make sure we have good values when the user restores the window + defaults.x = state.x; // carefull to keep x/y position so that the window ends up on the correct monitor + defaults.y = state.y; + + return defaults; + } + + return state; + } + + return null; + } + + public getBounds(): Electron.Rectangle { + const pos = this._win.getPosition(); + const dimension = this._win.getSize(); + + return { x: pos[0], y: pos[1], width: dimension[0], height: dimension[1] }; + } + + public toggleFullScreen(): void { + const willBeFullScreen = !this._win.isFullScreen(); + + // set fullscreen flag on window + this._win.setFullScreen(willBeFullScreen); + + // respect configured menu bar visibility or default to toggle if not set + this.setMenuBarVisibility(this.currentMenuBarVisibility, false); + } + + private getMenuBarVisibility(): MenuBarVisibility { + const windowConfig = this.configurationService.getConfiguration('window'); + if (!windowConfig || !windowConfig.menuBarVisibility) { + return 'default'; + } + + let menuBarVisibility = windowConfig.menuBarVisibility; + if (['visible', 'toggle', 'hidden'].indexOf(menuBarVisibility) < 0) { + menuBarVisibility = 'default'; + } + + return menuBarVisibility; + } + + public setMenuBarVisibility(visibility: MenuBarVisibility, notify: boolean = true): void { + if (isMacintosh) { + return; // ignore for macOS platform + } + + const isFullscreen = this._win.isFullScreen(); + + switch (visibility) { + case ('default'): + this._win.setMenuBarVisibility(!isFullscreen); + this._win.setAutoHideMenuBar(isFullscreen); + break; + + case ('visible'): + this._win.setMenuBarVisibility(true); + this._win.setAutoHideMenuBar(false); + break; + + case ('toggle'): + this._win.setMenuBarVisibility(false); + this._win.setAutoHideMenuBar(true); + + if (notify) { + this.send('vscode:showInfoMessage', nls.localize('hiddenMenuBar', "You can still access the menu bar by pressing the **Alt** key.")); + }; + break; + + case ('hidden'): + // for some weird reason that I have no explanation for, the menu bar is not hiding when calling + // this without timeout (see https://github.com/Microsoft/vscode/issues/19777). there seems to be + // a timing issue with us opening the first window and the menu bar getting created. somehow the + // fact that we want to hide the menu without being able to bring it back via Alt key makes Electron + // still show the menu. Unable to reproduce from a simple Hello World application though... + setTimeout(() => { + this._win.setMenuBarVisibility(false); + this._win.setAutoHideMenuBar(false); + }); + break; + }; + } + + public onWindowTitleDoubleClick(): void { + + // Respect system settings on mac with regards to title click on windows title + if (isMacintosh) { + const action = systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string'); + switch (action) { + case 'Minimize': + this.win.minimize(); + break; + case 'None': + break; + case 'Maximize': + default: + this.win.maximize(); + } + } + + // Linux/Windows: just toggle maximize/minimized state + else { + if (this.win.isMaximized()) { + this.win.unmaximize(); + } else { + this.win.maximize(); + } + } + } + + public close(): void { + if (this._win) { + this._win.close(); + } + } + + public sendWhenReady(channel: string, ...args: any[]): void { + this.ready().then(() => { + this.send(channel, ...args); + }); + } + + public send(channel: string, ...args: any[]): void { + this._win.webContents.send(channel, ...args); + } + + public dispose(): void { + if (this.showTimeoutHandle) { + clearTimeout(this.showTimeoutHandle); + } + + this.toDispose = dispose(this.toDispose); + + this._win = null; // Important to dereference the window object to allow for GC + } +} \ No newline at end of file diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts new file mode 100644 index 0000000000..f67ea4c3bb --- /dev/null +++ b/src/vs/code/electron-main/windows.ts @@ -0,0 +1,1720 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as path from 'path'; +import * as fs from 'original-fs'; +import { localize } from 'vs/nls'; +import * as arrays from 'vs/base/common/arrays'; +import { assign, mixin, equals } from 'vs/base/common/objects'; +import { IBackupMainService } from 'vs/platform/backup/common/backup'; +import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { IStorageService } from 'vs/platform/storage/node/storage'; +import { CodeWindow, IWindowState as ISingleWindowState, defaultWindowState, WindowMode } from 'vs/code/electron-main/window'; +import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, app } from 'electron'; +import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/paths'; +import { ILifecycleService, UnloadReason, IWindowUnloadEvent } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, ReadyState } from 'vs/platform/windows/common/windows'; +import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderPath } from 'vs/code/node/windowsFinder'; +import CommonEvent, { Emitter } from 'vs/base/common/event'; +import product from 'vs/platform/node/product'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { isEqual } from 'vs/base/common/paths'; +import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows'; +import { IHistoryMainService } from 'vs/platform/history/common/history'; +import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IWorkspacesMainService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { mnemonicButtonLabel } from 'vs/base/common/labels'; + +enum WindowError { + UNRESPONSIVE, + CRASHED +} + +interface INewWindowState extends ISingleWindowState { + hasDefaultState?: boolean; +} + +interface ILegacyWindowState extends IWindowState { + workspacePath?: string; +} + +interface IWindowState { + workspace?: IWorkspaceIdentifier; + folderPath?: string; + backupPath: string; + uiState: ISingleWindowState; +} + +interface ILegacyWindowsState extends IWindowsState { + openedFolders?: IWindowState[]; +} + +interface IWindowsState { + lastActiveWindow?: IWindowState; + lastPluginDevelopmentHostWindow?: IWindowState; + openedWindows: IWindowState[]; +} + +type RestoreWindowsSetting = 'all' | 'folders' | 'one' | 'none'; + +interface IOpenBrowserWindowOptions { + userEnv?: IProcessEnvironment; + cli?: ParsedArgs; + + workspace?: IWorkspaceIdentifier; + folderPath?: string; + + initialStartup?: boolean; + + filesToOpen?: IPath[]; + filesToCreate?: IPath[]; + filesToDiff?: IPath[]; + + forceNewWindow?: boolean; + windowToUse?: CodeWindow; + + emptyWindowBackupFolder?: string; +} + +interface IPathToOpen extends IPath { + + // the workspace for a Code instance to open + workspace?: IWorkspaceIdentifier; + + // the folder path for a Code instance to open + folderPath?: string; + + // the backup spath for a Code instance to use + backupPath?: string; + + // indicator to create the file path in the Code instance + createFilePath?: boolean; +} + +export class WindowsManager implements IWindowsMainService { + + _serviceBrand: any; + + private static windowsStateStorageKey = 'windowsState'; + + private static WINDOWS: CodeWindow[] = []; + + private initialUserEnv: IProcessEnvironment; + + private windowsState: IWindowsState; + private lastClosedWindowState: IWindowState; + + private fileDialog: FileDialog; + + private _onWindowReady = new Emitter(); + onWindowReady: CommonEvent = this._onWindowReady.event; + + private _onWindowClose = new Emitter(); + onWindowClose: CommonEvent = this._onWindowClose.event; + + private _onActiveWindowChanged = new Emitter(); + onActiveWindowChanged: CommonEvent = this._onActiveWindowChanged.event; + + private _onWindowReload = new Emitter(); + onWindowReload: CommonEvent = this._onWindowReload.event; + + private _onWindowsCountChanged = new Emitter(); + onWindowsCountChanged: CommonEvent = this._onWindowsCountChanged.event; + + constructor( + @ILogService private logService: ILogService, + @IStorageService private storageService: IStorageService, + @IEnvironmentService private environmentService: IEnvironmentService, + @ILifecycleService private lifecycleService: ILifecycleService, + @IBackupMainService private backupService: IBackupMainService, + @ITelemetryService private telemetryService: ITelemetryService, + @IConfigurationService private configurationService: IConfigurationService, + @IHistoryMainService private historyService: IHistoryMainService, + @IWorkspacesMainService private workspacesService: IWorkspacesMainService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + this.windowsState = this.storageService.getItem(WindowsManager.windowsStateStorageKey) || { openedWindows: [] }; + this.fileDialog = new FileDialog(environmentService, telemetryService, storageService, this); + + this.migrateLegacyWindowState(); + } + + private migrateLegacyWindowState(): void { + const state: ILegacyWindowsState = this.windowsState; + + // TODO@Ben migration from previous openedFolders to new openedWindows property + if (Array.isArray(state.openedFolders) && state.openedFolders.length > 0) { + state.openedWindows = state.openedFolders; + state.openedFolders = void 0; + } else if (!state.openedWindows) { + state.openedWindows = []; + } + + // TODO@Ben migration from previous workspacePath in window state to folderPath + const states: ILegacyWindowState[] = []; + states.push(state.lastActiveWindow); + states.push(state.lastPluginDevelopmentHostWindow); + states.push(...state.openedWindows); + states.forEach(state => { + if (!state) { + return; + } + + if (typeof state.workspacePath === 'string') { + state.folderPath = state.workspacePath; + state.workspacePath = void 0; + } + + // TODO@Ben migration to new workspace ID + if (state.workspace) { + state.workspace.id = this.workspacesService.getWorkspaceId(state.workspace.configPath); + } + }); + } + + public ready(initialUserEnv: IProcessEnvironment): void { + this.initialUserEnv = initialUserEnv; + + this.registerListeners(); + } + + private registerListeners(): void { + + // React to windows focus changes + app.on('browser-window-focus', () => { + setTimeout(() => { + this._onActiveWindowChanged.fire(this.getLastActiveWindow()); + }); + }); + + // React to workbench loaded events from windows + ipc.on('vscode:workbenchLoaded', (event, windowId: number) => { + this.logService.log('IPC#vscode-workbenchLoaded'); + + const win = this.getWindowById(windowId); + if (win) { + win.setReady(); + + // Event + this._onWindowReady.fire(win); + } + }); + + // React to HC color scheme changes (Windows) + if (isWindows) { + systemPreferences.on('inverted-color-scheme-changed', () => { + if (systemPreferences.isInvertedColorScheme()) { + this.sendToAll('vscode:enterHighContrast'); + } else { + this.sendToAll('vscode:leaveHighContrast'); + } + }); + } + + // Handle various lifecycle events around windows + this.lifecycleService.onBeforeWindowUnload(e => this.onBeforeWindowUnload(e)); + this.lifecycleService.onBeforeWindowClose(win => this.onBeforeWindowClose(win as CodeWindow)); + this.lifecycleService.onBeforeQuit(() => this.onBeforeQuit()); + this.onWindowsCountChanged(e => { + if (e.newCount - e.oldCount > 0) { + // clear last closed window state when a new window opens. this helps on macOS where + // otherwise closing the last window, opening a new window and then quitting would + // use the state of the previously closed window when restarting. + this.lastClosedWindowState = void 0; + } + }); + } + + // Note that onBeforeQuit() and onBeforeWindowClose() are fired in different order depending on the OS: + // - macOS: since the app will not quit when closing the last window, you will always first get + // the onBeforeQuit() event followed by N onbeforeWindowClose() events for each window + // - other: on other OS, closing the last window will quit the app so the order depends on the + // user interaction: closing the last window will first trigger onBeforeWindowClose() + // and then onBeforeQuit(). Using the quit action however will first issue onBeforeQuit() + // and then onBeforeWindowClose(). + // + // Here is the behaviour on different OS dependig on action taken (Electron 1.7.x): + // + // Legend + // - quit(N): quit application with N windows opened + // - close(1): close one window via the window close button + // - closeAll: close all windows via the taskbar command + // - onBeforeQuit(N): number of windows reported in this event handler + // - onBeforeWindowClose(N, M): number of windows reported and quitRequested boolean in this event handler + // + // macOS + // - quit(1): onBeforeQuit(1), onBeforeWindowClose(1, true) + // - quit(2): onBeforeQuit(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true) + // - quit(0): onBeforeQuit(0) + // - close(1): onBeforeWindowClose(1, false) + // + // Windows + // - quit(1): onBeforeQuit(1), onBeforeWindowClose(1, true) + // - quit(2): onBeforeQuit(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true) + // - close(1): onBeforeWindowClose(2, false)[not last window] + // - close(1): onBeforeWindowClose(1, false), onBeforequit(0)[last window] + // - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeQuit(0) + // + // Linux + // - quit(1): onBeforeQuit(1), onBeforeWindowClose(1, true) + // - quit(2): onBeforeQuit(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true) + // - close(1): onBeforeWindowClose(2, false)[not last window] + // - close(1): onBeforeWindowClose(1, false), onBeforequit(0)[last window] + // - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeQuit(0) + // + private onBeforeQuit(): void { + const currentWindowsState: ILegacyWindowsState = { + openedWindows: [], + openedFolders: [], // TODO@Ben migration so that old clients do not fail over data (prevents NPEs) + lastPluginDevelopmentHostWindow: this.windowsState.lastPluginDevelopmentHostWindow, + lastActiveWindow: this.lastClosedWindowState + }; + + // 1.) Find a last active window (pick any other first window otherwise) + if (!currentWindowsState.lastActiveWindow) { + let activeWindow = this.getLastActiveWindow(); + if (!activeWindow || activeWindow.isExtensionDevelopmentHost) { + activeWindow = WindowsManager.WINDOWS.filter(w => !w.isExtensionDevelopmentHost)[0]; + } + + if (activeWindow) { + currentWindowsState.lastActiveWindow = this.toWindowState(activeWindow); + } + } + + // 2.) Find extension host window + const extensionHostWindow = WindowsManager.WINDOWS.filter(w => w.isExtensionDevelopmentHost && !w.isExtensionTestHost)[0]; + if (extensionHostWindow) { + currentWindowsState.lastPluginDevelopmentHostWindow = this.toWindowState(extensionHostWindow); + } + + // 3.) All windows (except extension host) for N >= 2 to support restoreWindows: all or for auto update + // + // Carefull here: asking a window for its window state after it has been closed returns bogus values (width: 0, height: 0) + // so if we ever want to persist the UI state of the last closed window (window count === 1), it has + // to come from the stored lastClosedWindowState on Win/Linux at least + if (this.getWindowCount() > 1) { + currentWindowsState.openedWindows = WindowsManager.WINDOWS.filter(w => !w.isExtensionDevelopmentHost).map(w => this.toWindowState(w)); + } + + // Persist + this.storageService.setItem(WindowsManager.windowsStateStorageKey, currentWindowsState); + } + + // See note on #onBeforeQuit() for details how these events are flowing + private onBeforeWindowClose(win: CodeWindow): void { + if (this.lifecycleService.isQuitRequested()) { + return; // during quit, many windows close in parallel so let it be handled in the before-quit handler + } + + // On Window close, update our stored UI state of this window + const state: IWindowState = this.toWindowState(win); + if (win.isExtensionDevelopmentHost && !win.isExtensionTestHost) { + this.windowsState.lastPluginDevelopmentHostWindow = state; // do not let test run window state overwrite our extension development state + } + + // Any non extension host window with same workspace or folder + else if (!win.isExtensionDevelopmentHost && (!!win.openedWorkspace || !!win.openedFolderPath)) { + this.windowsState.openedWindows.forEach(o => { + const sameWorkspace = win.openedWorkspace && o.workspace && o.workspace.id === win.openedWorkspace.id; + const sameFolder = win.openedFolderPath && isEqual(o.folderPath, win.openedFolderPath, !isLinux /* ignorecase */); + + if (sameWorkspace || sameFolder) { + o.uiState = state.uiState; + } + }); + } + + // On Windows and Linux closing the last window will trigger quit. Since we are storing all UI state + // before quitting, we need to remember the UI state of this window to be able to persist it. + // On macOS we keep the last closed window state ready in case the user wants to quit right after or + // wants to open another window, in which case we use this state over the persisted one. + if (this.getWindowCount() === 1) { + this.lastClosedWindowState = state; + } + } + + private toWindowState(win: CodeWindow): IWindowState { + return { + workspace: win.openedWorkspace, + folderPath: win.openedFolderPath, + backupPath: win.backupPath, + uiState: win.serializeWindowState() + }; + } + + public open(openConfig: IOpenConfiguration): CodeWindow[] { + openConfig = this.validateOpenConfig(openConfig); + + let pathsToOpen = this.getPathsToOpen(openConfig); + + // When run with --add, take the folders that are to be opened as + // folders that should be added to the currently active window. + let foldersToAdd = []; + if (openConfig.addMode && product.quality !== 'stable') { // TODO@Ben multi root + foldersToAdd = pathsToOpen.filter(path => !!path.folderPath).map(path => ({ filePath: path.folderPath })); + pathsToOpen = pathsToOpen.filter(path => !path.folderPath); + } + + let filesToOpen = pathsToOpen.filter(path => !!path.filePath && !path.createFilePath); + let filesToCreate = pathsToOpen.filter(path => !!path.filePath && path.createFilePath); + + // When run with --diff, take the files to open as files to diff + // if there are exactly two files provided. + let filesToDiff: IPath[] = []; + if (openConfig.diffMode && filesToOpen.length === 2) { + filesToDiff = filesToOpen; + filesToOpen = []; + filesToCreate = []; // diff ignores other files that do not exist + } + + // + // These are windows to open to show workspaces + // + const workspacesToOpen = arrays.distinct(pathsToOpen.filter(win => !!win.workspace).map(win => win.workspace), workspace => workspace.id); // prevent duplicates + + // + // These are windows to open to show either folders or files (including diffing files or creating them) + // + const foldersToOpen = arrays.distinct(pathsToOpen.filter(win => win.folderPath && !win.filePath).map(win => win.folderPath), folder => isLinux ? folder : folder.toLowerCase()); // prevent duplicates + + // + // These are windows to restore because of hot-exit or from previous session (only performed once on startup!) + // + let foldersToRestore: string[] = []; + let workspacesToRestore: IWorkspaceIdentifier[] = []; + let emptyToRestore: string[] = []; + if (openConfig.initialStartup && !openConfig.cli.extensionDevelopmentPath) { + foldersToRestore = this.backupService.getFolderBackupPaths(); + + workspacesToRestore = this.backupService.getWorkspaceBackups(); // collect from workspaces with hot-exit backups + workspacesToRestore.push(...this.workspacesService.getUntitledWorkspacesSync()); // collect from previous window session + + emptyToRestore = this.backupService.getEmptyWindowBackupPaths(); + emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderPath && w.backupPath).map(w => path.basename(w.backupPath))); // add empty windows with backupPath + emptyToRestore = arrays.distinct(emptyToRestore); // prevent duplicates + } + + // + // These are empty windows to open + // + const emptyToOpen = pathsToOpen.filter(win => !win.workspace && !win.folderPath && !win.filePath && !win.backupPath).length; + + // Open based on config + const usedWindows = this.doOpen(openConfig, workspacesToOpen, workspacesToRestore, foldersToOpen, foldersToRestore, emptyToRestore, emptyToOpen, filesToOpen, filesToCreate, filesToDiff, foldersToAdd); + + // Make sure the last active window gets focus if we opened multiple + if (usedWindows.length > 1 && this.windowsState.lastActiveWindow) { + let lastActiveWindw = usedWindows.filter(w => w.backupPath === this.windowsState.lastActiveWindow.backupPath); + if (lastActiveWindw.length) { + lastActiveWindw[0].focus(); + } + } + + // Remember in recent document list (unless this opens for extension development) + // Also do not add paths when files are opened for diffing, only if opened individually + if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.cli.diff) { + const recentlyOpenedWorkspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[] = []; + const recentlyOpenedFiles: string[] = []; + + pathsToOpen.forEach(win => { + if (win.workspace || win.folderPath) { + recentlyOpenedWorkspaces.push(win.workspace || win.folderPath); + } else if (win.filePath) { + recentlyOpenedFiles.push(win.filePath); + } + }); + + this.historyService.addRecentlyOpened(recentlyOpenedWorkspaces, recentlyOpenedFiles); + } + + // If we got started with --wait from the CLI, we need to signal to the outside when the window + // used for the edit operation is closed so that the waiting process can continue. We do this by + // deleting the waitMarkerFilePath. + if (openConfig.context === OpenContext.CLI && openConfig.cli.wait && openConfig.cli.waitMarkerFilePath && usedWindows.length === 1 && usedWindows[0]) { + this.waitForWindowClose(usedWindows[0].id).done(() => fs.unlink(openConfig.cli.waitMarkerFilePath, error => void 0)); + } + + return usedWindows; + } + + private validateOpenConfig(config: IOpenConfiguration): IOpenConfiguration { + + // Make sure addMode is only enabled if we have an active window + if (config.addMode && (config.initialStartup || !this.getLastActiveWindow())) { + config.addMode = false; + } + + return config; + } + + private doOpen( + openConfig: IOpenConfiguration, + workspacesToOpen: IWorkspaceIdentifier[], + workspacesToRestore: IWorkspaceIdentifier[], + foldersToOpen: string[], + foldersToRestore: string[], + emptyToRestore: string[], + emptyToOpen: number, + filesToOpen: IPath[], + filesToCreate: IPath[], + filesToDiff: IPath[], + foldersToAdd: IPath[] + ) { + const usedWindows: CodeWindow[] = []; + + // Settings can decide if files/folders open in new window or not + let { openFolderInNewWindow, openFilesInNewWindow } = this.shouldOpenNewWindow(openConfig); + + // Handle folders to add by looking for the last active workspace (not on initial startup) + if (!openConfig.initialStartup && foldersToAdd.length > 0) { + const lastActiveWindow = this.getLastActiveWindow(); + if (lastActiveWindow) { + usedWindows.push(this.doAddFoldersToExistingWidow(lastActiveWindow, foldersToAdd)); + } + + // Reset because we handled them + foldersToAdd = []; + } + + // Handle files to open/diff or to create when we dont open a folder and we do not restore any folder/untitled from hot-exit + const potentialWindowsCount = foldersToOpen.length + foldersToRestore.length + workspacesToOpen.length + workspacesToRestore.length + emptyToRestore.length; + if (potentialWindowsCount === 0 && (filesToOpen.length > 0 || filesToCreate.length > 0 || filesToDiff.length > 0)) { + + // Find suitable window or folder path to open files in + const fileToCheck = filesToOpen[0] || filesToCreate[0] || filesToDiff[0]; + const bestWindowOrFolder = findBestWindowOrFolderForFile({ + windows: WindowsManager.WINDOWS, + newWindow: openFilesInNewWindow, + reuseWindow: openConfig.forceReuseWindow, + context: openConfig.context, + filePath: fileToCheck && fileToCheck.filePath, + userHome: this.environmentService.userHome, + workspaceResolver: workspace => this.workspacesService.resolveWorkspaceSync(workspace.configPath) + }); + + // We found a window to open the files in + if (bestWindowOrFolder instanceof CodeWindow) { + + // Window is workspace + if (bestWindowOrFolder.openedWorkspace) { + workspacesToOpen.push(bestWindowOrFolder.openedWorkspace); + } + + // Window is single folder + else if (bestWindowOrFolder.openedFolderPath) { + foldersToOpen.push(bestWindowOrFolder.openedFolderPath); + } + + // Window is empty + else { + + // Do open files + usedWindows.push(this.doOpenFilesInExistingWindow(bestWindowOrFolder, filesToOpen, filesToCreate, filesToDiff)); + + // Reset these because we handled them + filesToOpen = []; + filesToCreate = []; + filesToDiff = []; + } + } + + // We found a suitable folder to open: add it to foldersToOpen + else if (typeof bestWindowOrFolder === 'string') { + foldersToOpen.push(bestWindowOrFolder); + } + + // Finally, if no window or folder is found, just open the files in an empty window + else { + usedWindows.push(this.openInBrowserWindow({ + userEnv: openConfig.userEnv, + cli: openConfig.cli, + initialStartup: openConfig.initialStartup, + filesToOpen, + filesToCreate, + filesToDiff, + forceNewWindow: true + })); + + // Reset these because we handled them + filesToOpen = []; + filesToCreate = []; + filesToDiff = []; + } + } + + // Handle workspaces to open (instructed and to restore) + const allWorkspacesToOpen = arrays.distinct([...workspacesToOpen, ...workspacesToRestore], workspace => workspace.id); // prevent duplicates + if (allWorkspacesToOpen.length > 0) { + + // Check for existing instances + const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, workspaceToOpen))); + if (windowsOnWorkspace.length > 0) { + const windowOnWorkspace = windowsOnWorkspace[0]; + + // Do open files + usedWindows.push(this.doOpenFilesInExistingWindow(windowOnWorkspace, filesToOpen, filesToCreate, filesToDiff)); + + // Reset these because we handled them + filesToOpen = []; + filesToCreate = []; + filesToDiff = []; + + openFolderInNewWindow = true; // any other folders to open must open in new window then + } + + // Open remaining ones + allWorkspacesToOpen.forEach(workspaceToOpen => { + if (windowsOnWorkspace.some(win => win.openedWorkspace.id === workspaceToOpen.id)) { + return; // ignore folders that are already open + } + + // Do open folder + usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { workspace: workspaceToOpen }, openFolderInNewWindow, filesToOpen, filesToCreate, filesToDiff)); + + // Reset these because we handled them + filesToOpen = []; + filesToCreate = []; + filesToDiff = []; + + openFolderInNewWindow = true; // any other folders to open must open in new window then + }); + } + + // Handle folders to open (instructed and to restore) + const allFoldersToOpen = arrays.distinct([...foldersToOpen, ...foldersToRestore], folder => isLinux ? folder : folder.toLowerCase()); // prevent duplicates + if (allFoldersToOpen.length > 0) { + + // Check for existing instances + const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, folderToOpen))); + if (windowsOnFolderPath.length > 0) { + const windowOnFolderPath = windowsOnFolderPath[0]; + + // Do open files + usedWindows.push(this.doOpenFilesInExistingWindow(windowOnFolderPath, filesToOpen, filesToCreate, filesToDiff)); + + // Reset these because we handled them + filesToOpen = []; + filesToCreate = []; + filesToDiff = []; + + openFolderInNewWindow = true; // any other folders to open must open in new window then + } + + // Open remaining ones + allFoldersToOpen.forEach(folderToOpen => { + if (windowsOnFolderPath.some(win => isEqual(win.openedFolderPath, folderToOpen, !isLinux /* ignorecase */))) { + return; // ignore folders that are already open + } + + // Do open folder + usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderPath: folderToOpen }, openFolderInNewWindow, filesToOpen, filesToCreate, filesToDiff)); + + // Reset these because we handled them + filesToOpen = []; + filesToCreate = []; + filesToDiff = []; + + openFolderInNewWindow = true; // any other folders to open must open in new window then + }); + } + + // Handle empty to restore + if (emptyToRestore.length > 0) { + emptyToRestore.forEach(emptyWindowBackupFolder => { + usedWindows.push(this.openInBrowserWindow({ + userEnv: openConfig.userEnv, + cli: openConfig.cli, + initialStartup: openConfig.initialStartup, + filesToOpen, + filesToCreate, + filesToDiff, + forceNewWindow: true, + emptyWindowBackupFolder + })); + + // Reset these because we handled them + filesToOpen = []; + filesToCreate = []; + filesToDiff = []; + + openFolderInNewWindow = true; // any other folders to open must open in new window then + }); + } + + // Handle empty to open (only if no other window opened) + if (usedWindows.length === 0) { + for (let i = 0; i < emptyToOpen; i++) { + usedWindows.push(this.openInBrowserWindow({ + userEnv: openConfig.userEnv, + cli: openConfig.cli, + initialStartup: openConfig.initialStartup, + forceNewWindow: openFolderInNewWindow + })); + + openFolderInNewWindow = true; // any other window to open must open in new window then + } + } + + return arrays.distinct(usedWindows); + } + + private doOpenFilesInExistingWindow(window: CodeWindow, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[]): CodeWindow { + window.focus(); // make sure window has focus + + window.ready().then(readyWindow => { + readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff }); + }); + + return window; + } + + private doAddFoldersToExistingWidow(window: CodeWindow, foldersToAdd: IPath[]): CodeWindow { + window.focus(); // make sure window has focus + + window.ready().then(readyWindow => { + readyWindow.send('vscode:addFolders', { foldersToAdd }); + }); + + return window; + } + + private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, openInNewWindow: boolean, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[], windowToUse?: CodeWindow): CodeWindow { + const browserWindow = this.openInBrowserWindow({ + userEnv: openConfig.userEnv, + cli: openConfig.cli, + initialStartup: openConfig.initialStartup, + workspace: folderOrWorkspace.workspace, + folderPath: folderOrWorkspace.folderPath, + filesToOpen, + filesToCreate, + filesToDiff, + forceNewWindow: openInNewWindow, + windowToUse + }); + + return browserWindow; + } + + private getPathsToOpen(openConfig: IOpenConfiguration): IPathToOpen[] { + let windowsToOpen: IPathToOpen[]; + let isCommandLineOrAPICall = false; + + // Extract paths: from API + if (openConfig.pathsToOpen && openConfig.pathsToOpen.length > 0) { + windowsToOpen = this.doExtractPathsFromAPI(openConfig); + isCommandLineOrAPICall = true; + } + + // Check for force empty + else if (openConfig.forceEmpty) { + windowsToOpen = [Object.create(null)]; + } + + // Extract paths: from CLI + else if (openConfig.cli._.length > 0) { + windowsToOpen = this.doExtractPathsFromCLI(openConfig.cli); + isCommandLineOrAPICall = true; + } + + // Extract windows: from previous session + else { + windowsToOpen = this.doGetWindowsFromLastSession(); + } + + // Convert multiple folders into workspace (if opened via API or CLI) + // This will ensure to open these folders in one window instead of multiple + // If we are in addMode, we should not do this because in that case all + // folders should be added to the existing window. + if (!openConfig.addMode && isCommandLineOrAPICall && product.quality !== 'stable') { // TODO@Ben multi root + const foldersToOpen = windowsToOpen.filter(path => !!path.folderPath); + if (foldersToOpen.length > 1) { + const workspace = this.workspacesService.createWorkspaceSync(foldersToOpen.map(folder => folder.folderPath)); + + // Add workspace and remove folders thereby + windowsToOpen.push({ workspace }); + windowsToOpen = windowsToOpen.filter(path => !path.folderPath); + } + } + + return windowsToOpen; + } + + private doExtractPathsFromAPI(openConfig: IOpenConfiguration): IPath[] { + let pathsToOpen = openConfig.pathsToOpen.map(pathToOpen => { + const path = this.parsePath(pathToOpen, { gotoLineMode: openConfig.cli && openConfig.cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile }); + + // Warn if the requested path to open does not exist + if (!path) { + const options: Electron.ShowMessageBoxOptions = { + title: product.nameLong, + type: 'info', + buttons: [localize('ok', "OK")], + message: localize('pathNotExistTitle', "Path does not exist"), + detail: localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", pathToOpen), + noLink: true + }; + + const activeWindow = BrowserWindow.getFocusedWindow(); + if (activeWindow) { + dialog.showMessageBox(activeWindow, options); + } else { + dialog.showMessageBox(options); + } + } + + return path; + }); + + // get rid of nulls + pathsToOpen = arrays.coalesce(pathsToOpen); + + return pathsToOpen; + } + + private doExtractPathsFromCLI(cli: ParsedArgs): IPath[] { + const pathsToOpen = arrays.coalesce(cli._.map(candidate => this.parsePath(candidate, { ignoreFileNotFound: true, gotoLineMode: cli.goto }))); + if (pathsToOpen.length > 0) { + return pathsToOpen; + } + + // No path provided, return empty to open empty + return [Object.create(null)]; + } + + private doGetWindowsFromLastSession(): IPathToOpen[] { + const restoreWindows = this.getRestoreWindowsSetting(); + const lastActiveWindow = this.windowsState.lastActiveWindow; + + switch (restoreWindows) { + + // none: we always open an empty window + case 'none': + return [Object.create(null)]; + + // one: restore last opened workspace/folder or empty window + case 'one': + if (lastActiveWindow) { + + // workspace + const candidateWorkspace = lastActiveWindow.workspace; + if (candidateWorkspace) { + const validatedWorkspace = this.parsePath(candidateWorkspace.configPath); + if (validatedWorkspace && validatedWorkspace.workspace) { + return [validatedWorkspace]; + } + } + + // folder (if path is valid) + else if (lastActiveWindow.folderPath) { + const validatedFolder = this.parsePath(lastActiveWindow.folderPath); + if (validatedFolder && validatedFolder.folderPath) { + return [validatedFolder]; + } + } + + // otherwise use backup path to restore empty windows + else if (lastActiveWindow.backupPath) { + return [{ backupPath: lastActiveWindow.backupPath }]; + } + } + break; + + // all: restore all windows + // folders: restore last opened folders only + case 'all': + case 'folders': + const windowsToOpen: IPathToOpen[] = []; + + // Workspaces + const workspaceCandidates = this.windowsState.openedWindows.filter(w => !!w.workspace).map(w => w.workspace); + if (lastActiveWindow && lastActiveWindow.workspace) { + workspaceCandidates.push(lastActiveWindow.workspace); + } + windowsToOpen.push(...workspaceCandidates.map(candidate => this.parsePath(candidate.configPath)).filter(window => window && window.workspace)); + + // Folders + const folderCandidates = this.windowsState.openedWindows.filter(w => !!w.folderPath).map(w => w.folderPath); + if (lastActiveWindow && lastActiveWindow.folderPath) { + folderCandidates.push(lastActiveWindow.folderPath); + } + windowsToOpen.push(...folderCandidates.map(candidate => this.parsePath(candidate)).filter(window => window && window.folderPath)); + + // Windows that were Empty + if (restoreWindows === 'all') { + const lastOpenedEmpty = this.windowsState.openedWindows.filter(w => !w.workspace && !w.folderPath && w.backupPath).map(w => w.backupPath); + const lastActiveEmpty = lastActiveWindow && !lastActiveWindow.workspace && !lastActiveWindow.folderPath && lastActiveWindow.backupPath; + if (lastActiveEmpty) { + lastOpenedEmpty.push(lastActiveEmpty); + } + + windowsToOpen.push(...lastOpenedEmpty.map(backupPath => ({ backupPath }))); + } + + if (windowsToOpen.length > 0) { + return windowsToOpen; + } + + break; + } + + // Always fallback to empty window + return [Object.create(null)]; + } + + private getRestoreWindowsSetting(): RestoreWindowsSetting { + let restoreWindows: RestoreWindowsSetting; + if (this.lifecycleService.wasRestarted) { + restoreWindows = 'all'; // always reopen all windows when an update was applied + } else { + const windowConfig = this.configurationService.getConfiguration('window'); + restoreWindows = ((windowConfig && windowConfig.restoreWindows) || 'one') as RestoreWindowsSetting; + + if (restoreWindows === 'one' /* default */ && windowConfig && windowConfig.reopenFolders) { + restoreWindows = windowConfig.reopenFolders; // TODO@Ben migration + } + + if (['all', 'folders', 'one', 'none'].indexOf(restoreWindows) === -1) { + restoreWindows = 'one'; + } + } + + return restoreWindows; + } + + private parsePath(anyPath: string, options?: { ignoreFileNotFound?: boolean, gotoLineMode?: boolean, forceOpenWorkspaceAsFile?: boolean; }): IPathToOpen { + if (!anyPath) { + return null; + } + + let parsedPath: IPathWithLineAndColumn; + + const gotoLineMode = options && options.gotoLineMode; + if (options && options.gotoLineMode) { + parsedPath = parseLineAndColumnAware(anyPath); + anyPath = parsedPath.path; + } + + const candidate = path.normalize(anyPath); + try { + const candidateStat = fs.statSync(candidate); + if (candidateStat) { + if (candidateStat.isFile()) { + + // Workspace (unless disabled via flag) + if (!options || !options.forceOpenWorkspaceAsFile) { + const workspace = this.workspacesService.resolveWorkspaceSync(candidate); + if (workspace) { + return { workspace: { id: workspace.id, configPath: workspace.configPath } }; + } + } + + // File + return { + filePath: candidate, + lineNumber: gotoLineMode ? parsedPath.line : void 0, + columnNumber: gotoLineMode ? parsedPath.column : void 0 + }; + } + + // Folder + return { + folderPath: candidate + }; + } + } catch (error) { + this.historyService.removeFromRecentlyOpened([candidate]); // since file does not seem to exist anymore, remove from recent + + if (options && options.ignoreFileNotFound) { + return { filePath: candidate, createFilePath: true }; // assume this is a file that does not yet exist + } + } + + return null; + } + + private shouldOpenNewWindow(openConfig: IOpenConfiguration): { openFolderInNewWindow: boolean; openFilesInNewWindow: boolean; } { + + // let the user settings override how folders are open in a new window or same window unless we are forced + const windowConfig = this.configurationService.getConfiguration('window'); + const openFolderInNewWindowConfig = (windowConfig && windowConfig.openFoldersInNewWindow) || 'default' /* default */; + const openFilesInNewWindowConfig = (windowConfig && windowConfig.openFilesInNewWindow) || 'off' /* default */; + + let openFolderInNewWindow = (openConfig.preferNewWindow || openConfig.forceNewWindow) && !openConfig.forceReuseWindow; + if (!openConfig.forceNewWindow && !openConfig.forceReuseWindow && (openFolderInNewWindowConfig === 'on' || openFolderInNewWindowConfig === 'off')) { + openFolderInNewWindow = (openFolderInNewWindowConfig === 'on'); + } + + // let the user settings override how files are open in a new window or same window unless we are forced (not for extension development though) + let openFilesInNewWindow: boolean; + if (openConfig.forceNewWindow || openConfig.forceReuseWindow) { + openFilesInNewWindow = openConfig.forceNewWindow && !openConfig.forceReuseWindow; + } else { + if (openConfig.context === OpenContext.DOCK) { + openFilesInNewWindow = true; // only on macOS do we allow to open files in a new window if this is triggered via DOCK context + } + + if (!openConfig.cli.extensionDevelopmentPath && (openFilesInNewWindowConfig === 'on' || openFilesInNewWindowConfig === 'off')) { + openFilesInNewWindow = (openFilesInNewWindowConfig === 'on'); + } + } + + return { openFolderInNewWindow, openFilesInNewWindow }; + } + + public openExtensionDevelopmentHostWindow(openConfig: IOpenConfiguration): void { + + // Reload an existing extension development host window on the same path + // We currently do not allow more than one extension development window + // on the same extension path. + const existingWindow = findWindowOnExtensionDevelopmentPath(WindowsManager.WINDOWS, openConfig.cli.extensionDevelopmentPath); + if (existingWindow) { + this.reload(existingWindow, openConfig.cli); + existingWindow.focus(); // make sure it gets focus and is restored + + return; + } + + // Fill in previously opened workspace unless an explicit path is provided and we are not unit testing + if (openConfig.cli._.length === 0 && !openConfig.cli.extensionTestsPath) { + const extensionDevelopmentWindowState = this.windowsState.lastPluginDevelopmentHostWindow; + const workspaceToOpen = extensionDevelopmentWindowState && (extensionDevelopmentWindowState.workspace || extensionDevelopmentWindowState.folderPath); + if (workspaceToOpen) { + openConfig.cli._ = [isSingleFolderWorkspaceIdentifier(workspaceToOpen) ? workspaceToOpen : workspaceToOpen.configPath]; + } + } + + // Make sure we are not asked to open a workspace or folder that is already opened + if (openConfig.cli._.some(path => !!findWindowOnWorkspaceOrFolderPath(WindowsManager.WINDOWS, path))) { + openConfig.cli._ = []; + } + + // Open it + this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: openConfig.cli._.length === 0, userEnv: openConfig.userEnv }); + } + + private openInBrowserWindow(options: IOpenBrowserWindowOptions): CodeWindow { + + // Build IWindowConfiguration from config and options + const configuration: IWindowConfiguration = mixin({}, options.cli); // inherit all properties from CLI + configuration.appRoot = this.environmentService.appRoot; + configuration.execPath = process.execPath; + configuration.userEnv = assign({}, this.initialUserEnv, options.userEnv || {}); + configuration.isInitialStartup = options.initialStartup; + configuration.workspace = options.workspace; + configuration.folderPath = options.folderPath; + configuration.filesToOpen = options.filesToOpen; + configuration.filesToCreate = options.filesToCreate; + configuration.filesToDiff = options.filesToDiff; + configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir; + + // if we know the backup folder upfront (for empty windows to restore), we can set it + // directly here which helps for restoring UI state associated with that window. + // For all other cases we first call into registerEmptyWindowBackupSync() to set it before + // loading the window. + if (options.emptyWindowBackupFolder) { + configuration.backupPath = path.join(this.environmentService.backupHome, options.emptyWindowBackupFolder); + } + + let window: CodeWindow; + if (!options.forceNewWindow) { + window = options.windowToUse || this.getLastActiveWindow(); + if (window) { + window.focus(); + } + } + + // New window + if (!window) { + const windowConfig = this.configurationService.getConfiguration('window'); + const state = this.getNewWindowState(configuration); + + // Window state is not from a previous session: only allow fullscreen if we inherit it or user wants fullscreen + let allowFullscreen: boolean; + if (state.hasDefaultState) { + allowFullscreen = (windowConfig && windowConfig.newWindowDimensions && ['fullscreen', 'inherit'].indexOf(windowConfig.newWindowDimensions) >= 0); + } + + // Window state is from a previous session: only allow fullscreen when we got updated or user wants to restore + else { + allowFullscreen = this.lifecycleService.wasRestarted || (windowConfig && windowConfig.restoreFullscreen); + } + + if (state.mode === WindowMode.Fullscreen && !allowFullscreen) { + state.mode = WindowMode.Normal; + } + + window = this.instantiationService.createInstance(CodeWindow, { + state, + extensionDevelopmentPath: configuration.extensionDevelopmentPath, + isExtensionTestHost: !!configuration.extensionTestsPath + }); + + // Add to our list of windows + WindowsManager.WINDOWS.push(window); + + // Indicate number change via event + this._onWindowsCountChanged.fire({ oldCount: WindowsManager.WINDOWS.length - 1, newCount: WindowsManager.WINDOWS.length }); + + // Window Events + window.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own + window.win.webContents.on('devtools-reload-page', () => this.reload(window)); + window.win.webContents.on('crashed', () => this.onWindowError(window, WindowError.CRASHED)); + window.win.on('unresponsive', () => this.onWindowError(window, WindowError.UNRESPONSIVE)); + window.win.on('closed', () => this.onWindowClosed(window)); + + // Lifecycle + this.lifecycleService.registerWindow(window); + } + + // Existing window + else { + + // Some configuration things get inherited if the window is being reused and we are + // in extension development host mode. These options are all development related. + const currentWindowConfig = window.config; + if (!configuration.extensionDevelopmentPath && currentWindowConfig && !!currentWindowConfig.extensionDevelopmentPath) { + configuration.extensionDevelopmentPath = currentWindowConfig.extensionDevelopmentPath; + configuration.verbose = currentWindowConfig.verbose; + configuration.debugBrkPluginHost = currentWindowConfig.debugBrkPluginHost; + configuration.debugId = currentWindowConfig.debugId; + configuration.debugPluginHost = currentWindowConfig.debugPluginHost; + configuration['extensions-dir'] = currentWindowConfig['extensions-dir']; + } + } + + // Only load when the window has not vetoed this + this.lifecycleService.unload(window, UnloadReason.LOAD).done(veto => { + if (!veto) { + + // Register window for backups + if (!configuration.extensionDevelopmentPath) { + if (configuration.workspace) { + configuration.backupPath = this.backupService.registerWorkspaceBackupSync(configuration.workspace); + } else if (configuration.folderPath) { + configuration.backupPath = this.backupService.registerFolderBackupSync(configuration.folderPath); + } else { + configuration.backupPath = this.backupService.registerEmptyWindowBackupSync(options.emptyWindowBackupFolder); + } + } + + // Load it + window.load(configuration); + } + }); + + return window; + } + + private getNewWindowState(configuration: IWindowConfiguration): INewWindowState { + const lastActive = this.getLastActiveWindow(); + + // Restore state unless we are running extension tests + if (!configuration.extensionTestsPath) { + + // extension development host Window - load from stored settings if any + if (!!configuration.extensionDevelopmentPath && this.windowsState.lastPluginDevelopmentHostWindow) { + return this.windowsState.lastPluginDevelopmentHostWindow.uiState; + } + + // Known Workspace - load from stored settings + if (configuration.workspace) { + const stateForWorkspace = this.windowsState.openedWindows.filter(o => o.workspace && o.workspace.id === configuration.workspace.id).map(o => o.uiState); + if (stateForWorkspace.length) { + return stateForWorkspace[0]; + } + } + + // Known Folder - load from stored settings + if (configuration.folderPath) { + const stateForFolder = this.windowsState.openedWindows.filter(o => isEqual(o.folderPath, configuration.folderPath, !isLinux /* ignorecase */)).map(o => o.uiState); + if (stateForFolder.length) { + return stateForFolder[0]; + } + } + + // Empty windows with backups + else if (configuration.backupPath) { + const stateForEmptyWindow = this.windowsState.openedWindows.filter(o => o.backupPath === configuration.backupPath).map(o => o.uiState); + if (stateForEmptyWindow.length) { + return stateForEmptyWindow[0]; + } + } + + // First Window + const lastActiveState = this.lastClosedWindowState || this.windowsState.lastActiveWindow; + if (!lastActive && lastActiveState) { + return lastActiveState.uiState; + } + } + + // + // In any other case, we do not have any stored settings for the window state, so we come up with something smart + // + + // We want the new window to open on the same display that the last active one is in + let displayToUse: Electron.Display; + const displays = screen.getAllDisplays(); + + // Single Display + if (displays.length === 1) { + displayToUse = displays[0]; + } + + // Multi Display + else { + + // on mac there is 1 menu per window so we need to use the monitor where the cursor currently is + if (isMacintosh) { + const cursorPoint = screen.getCursorScreenPoint(); + displayToUse = screen.getDisplayNearestPoint(cursorPoint); + } + + // if we have a last active window, use that display for the new window + if (!displayToUse && lastActive) { + displayToUse = screen.getDisplayMatching(lastActive.getBounds()); + } + + // fallback to primary display or first display + if (!displayToUse) { + displayToUse = screen.getPrimaryDisplay() || displays[0]; + } + } + + let state = defaultWindowState() as INewWindowState; + state.x = displayToUse.bounds.x + (displayToUse.bounds.width / 2) - (state.width / 2); + state.y = displayToUse.bounds.y + (displayToUse.bounds.height / 2) - (state.height / 2); + + // Check for newWindowDimensions setting and adjust accordingly + const windowConfig = this.configurationService.getConfiguration('window'); + let ensureNoOverlap = true; + if (windowConfig && windowConfig.newWindowDimensions) { + if (windowConfig.newWindowDimensions === 'maximized') { + state.mode = WindowMode.Maximized; + ensureNoOverlap = false; + } else if (windowConfig.newWindowDimensions === 'fullscreen') { + state.mode = WindowMode.Fullscreen; + ensureNoOverlap = false; + } else if (windowConfig.newWindowDimensions === 'inherit' && lastActive) { + const lastActiveState = lastActive.serializeWindowState(); + if (lastActiveState.mode === WindowMode.Fullscreen) { + state.mode = WindowMode.Fullscreen; // only take mode (fixes https://github.com/Microsoft/vscode/issues/19331) + } else { + state = lastActiveState; + } + + ensureNoOverlap = false; + } + } + + if (ensureNoOverlap) { + state = this.ensureNoOverlap(state); + } + + state.hasDefaultState = true; // flag as default state + + return state; + } + + private ensureNoOverlap(state: ISingleWindowState): ISingleWindowState { + if (WindowsManager.WINDOWS.length === 0) { + return state; + } + + const existingWindowBounds = WindowsManager.WINDOWS.map(win => win.getBounds()); + while (existingWindowBounds.some(b => b.x === state.x || b.y === state.y)) { + state.x += 30; + state.y += 30; + } + + return state; + } + + public reload(win: CodeWindow, cli?: ParsedArgs): void { + + // Only reload when the window has not vetoed this + this.lifecycleService.unload(win, UnloadReason.RELOAD).done(veto => { + if (!veto) { + win.reload(void 0, cli); + + // Emit + this._onWindowReload.fire(win.id); + } + }); + } + + public closeWorkspace(win: CodeWindow): void { + this.openInBrowserWindow({ + cli: this.environmentService.args, + windowToUse: win + }); + } + + public saveAndOpenWorkspace(window: CodeWindow, path: string): TPromise { + if (!window || !window.win || window.readyState !== ReadyState.READY || !window.openedWorkspace || !path) { + return TPromise.as(null); // return early if the window is not ready or disposed or does not have a workspace + } + + return this.doSaveAndOpenWorkspace(window, window.openedWorkspace, path); + } + + public createAndOpenWorkspace(window: CodeWindow, folders?: string[], path?: string): TPromise { + if (!window || !window.win || window.readyState !== ReadyState.READY) { + return TPromise.as(null); // return early if the window is not ready or disposed + } + + return this.workspacesService.createWorkspace(folders).then(workspace => { + return this.doSaveAndOpenWorkspace(window, workspace, path); + }); + } + + private doSaveAndOpenWorkspace(window: CodeWindow, workspace: IWorkspaceIdentifier, path?: string): TPromise { + let savePromise: TPromise; + if (path) { + savePromise = this.workspacesService.saveWorkspace(workspace, path); + } else { + savePromise = TPromise.as(workspace); + } + + return savePromise.then(workspace => { + window.focus(); + + // Only open workspace when the window has not vetoed this + return this.lifecycleService.unload(window, UnloadReason.RELOAD, workspace).done(veto => { + if (!veto) { + + // Register window for backups and migrate current backups over + let backupPath: string; + if (window.config && !window.config.extensionDevelopmentPath) { + backupPath = this.backupService.registerWorkspaceBackupSync(workspace, window.config.backupPath); + } + + // Craft a new window configuration to use for the transition + const configuration: IWindowConfiguration = mixin({}, window.config); + configuration.folderPath = void 0; + configuration.workspace = workspace; + configuration.backupPath = backupPath; + + // Reload + window.reload(configuration); + } + }); + }); + } + + public openWorkspace(window: CodeWindow = this.getLastActiveWindow()): void { + let defaultPath: string; + if (window && window.openedWorkspace && !this.workspacesService.isUntitledWorkspace(window.openedWorkspace)) { + defaultPath = path.dirname(window.openedWorkspace.configPath); + } else { + defaultPath = this.getWorkspaceDialogDefaultPath(window ? (window.openedWorkspace || window.openedFolderPath) : void 0); + } + + this.pickFileAndOpen({ + windowId: window ? window.id : void 0, + dialogOptions: { + buttonLabel: mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open")), + title: localize('openWorkspaceTitle', "Open Workspace"), + filters: WORKSPACE_FILTER, + properties: ['openFile'], + defaultPath + } + }); + } + + private getWorkspaceDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string { + let defaultPath: string; + if (workspace) { + if (isSingleFolderWorkspaceIdentifier(workspace)) { + defaultPath = path.dirname(workspace); + } else { + const resolvedWorkspace = this.workspacesService.resolveWorkspaceSync(workspace.configPath); + if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) { + defaultPath = path.dirname(resolvedWorkspace.folders[0].path); + } + } + } + + return defaultPath; + } + + private onBeforeWindowUnload(e: IWindowUnloadEvent): void { + const windowClosing = e.reason === UnloadReason.CLOSE; + const windowLoading = e.reason === UnloadReason.LOAD; + if (!windowClosing && !windowLoading) { + return; // only interested when window is closing or loading + } + + const workspace = e.window.openedWorkspace; + if (!workspace || !this.workspacesService.isUntitledWorkspace(workspace)) { + return; // only care about untitled workspaces to ask for saving + } + + if (windowClosing && !isMacintosh && this.getWindowCount() === 1) { + return; // Windows/Linux: quits when last window is closed, so do not ask then + } + + this.promptToSaveUntitledWorkspace(e, workspace); + } + + private promptToSaveUntitledWorkspace(e: IWindowUnloadEvent, workspace: IWorkspaceIdentifier): void { + enum ConfirmResult { + SAVE, + DONT_SAVE, + CANCEL + } + + const save = { label: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), result: ConfirmResult.SAVE }; + const dontSave = { label: mnemonicButtonLabel(localize({ key: 'doNotSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save")), result: ConfirmResult.DONT_SAVE }; + const cancel = { label: localize('cancel', "Cancel"), result: ConfirmResult.CANCEL }; + + const buttons: { label: string; result: ConfirmResult; }[] = []; + if (isWindows) { + buttons.push(save, dontSave, cancel); + } else if (isLinux) { + buttons.push(dontSave, cancel, save); + } else { + buttons.push(save, cancel, dontSave); + } + + const options: Electron.ShowMessageBoxOptions = { + title: this.environmentService.appNameLong, + message: localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"), + detail: localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."), + noLink: true, + type: 'warning', + buttons: buttons.map(button => button.label), + cancelId: buttons.indexOf(cancel) + }; + + if (isLinux) { + options.defaultId = 2; + } + + const res = dialog.showMessageBox(e.window.win, options); + + switch (buttons[res].result) { + + // Cancel: veto unload + case ConfirmResult.CANCEL: + e.veto(true); + break; + + // Don't Save: delete workspace + case ConfirmResult.DONT_SAVE: + this.workspacesService.deleteUntitledWorkspaceSync(workspace); + e.veto(false); + break; + + // Save: save workspace, but do not veto unload + case ConfirmResult.SAVE: { + const target = dialog.showSaveDialog(e.window.win, { + buttonLabel: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), + title: localize('saveWorkspace', "Save Workspace"), + filters: WORKSPACE_FILTER, + defaultPath: this.getWorkspaceDialogDefaultPath(workspace) + }); + + if (target) { + e.veto(this.workspacesService.saveWorkspace(workspace, target).then(() => false, () => false)); + } else { + e.veto(true); // keep veto if no target was provided + } + } + } + } + + public focusLastActive(cli: ParsedArgs, context: OpenContext): CodeWindow { + const lastActive = this.getLastActiveWindow(); + if (lastActive) { + lastActive.focus(); + + return lastActive; + } + + // No window - open new empty one + return this.open({ context, cli, forceEmpty: true })[0]; + } + + public getLastActiveWindow(): CodeWindow { + return getLastActiveWindow(WindowsManager.WINDOWS); + } + + public openNewWindow(context: OpenContext): void { + this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true }); + } + + public waitForWindowClose(windowId: number): TPromise { + return new TPromise(c => { + const toDispose = this.onWindowClose(id => { + if (id === windowId) { + toDispose.dispose(); + c(null); + } + }); + }); + } + + public sendToFocused(channel: string, ...args: any[]): void { + const focusedWindow = this.getFocusedWindow() || this.getLastActiveWindow(); + + if (focusedWindow) { + focusedWindow.sendWhenReady(channel, ...args); + } + } + + public sendToAll(channel: string, payload?: any, windowIdsToIgnore?: number[]): void { + WindowsManager.WINDOWS.forEach(w => { + if (windowIdsToIgnore && windowIdsToIgnore.indexOf(w.id) >= 0) { + return; // do not send if we are instructed to ignore it + } + + w.sendWhenReady(channel, payload); + }); + } + + public getFocusedWindow(): CodeWindow { + const win = BrowserWindow.getFocusedWindow(); + if (win) { + return this.getWindowById(win.id); + } + + return null; + } + + public getWindowById(windowId: number): CodeWindow { + const res = WindowsManager.WINDOWS.filter(w => w.id === windowId); + if (res && res.length === 1) { + return res[0]; + } + + return null; + } + + public getWindows(): CodeWindow[] { + return WindowsManager.WINDOWS; + } + + public getWindowCount(): number { + return WindowsManager.WINDOWS.length; + } + + private onWindowError(window: CodeWindow, error: WindowError): void { + this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive'); + + // Unresponsive + if (error === WindowError.UNRESPONSIVE) { + dialog.showMessageBox(window.win, { + title: product.nameLong, + type: 'warning', + buttons: [localize('reopen', "Reopen"), localize('wait', "Keep Waiting"), localize('close', "Close")], + message: localize('appStalled', "The window is no longer responding"), + detail: localize('appStalledDetail', "You can reopen or close the window or keep waiting."), + noLink: true + }, result => { + if (!window.win) { + return; // Return early if the window has been going down already + } + + if (result === 0) { + window.reload(); + } else if (result === 2) { + this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually + window.win.destroy(); // make sure to destroy the window as it is unresponsive + } + }); + } + + // Crashed + else { + dialog.showMessageBox(window.win, { + title: product.nameLong, + type: 'warning', + buttons: [localize('reopen', "Reopen"), localize('close', "Close")], + message: localize('appCrashed', "The window has crashed"), + detail: localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."), + noLink: true + }, result => { + if (!window.win) { + return; // Return early if the window has been going down already + } + + if (result === 0) { + window.reload(); + } else if (result === 1) { + this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually + window.win.destroy(); // make sure to destroy the window as it has crashed + } + }); + } + } + + private onWindowClosed(win: CodeWindow): void { + + // Tell window + win.dispose(); + + // Remove from our list so that Electron can clean it up + const index = WindowsManager.WINDOWS.indexOf(win); + WindowsManager.WINDOWS.splice(index, 1); + + // Emit + this._onWindowsCountChanged.fire({ oldCount: WindowsManager.WINDOWS.length + 1, newCount: WindowsManager.WINDOWS.length }); + this._onWindowClose.fire(win.id); + } + + public pickFileFolderAndOpen(options: INativeOpenDialogOptions): void { + this.doPickAndOpen(options, true /* pick folders */, true /* pick files */); + } + + public pickFolderAndOpen(options: INativeOpenDialogOptions): void { + this.doPickAndOpen(options, true /* pick folders */, false /* pick files */); + } + + public pickFileAndOpen(options: INativeOpenDialogOptions): void { + this.doPickAndOpen(options, false /* pick folders */, true /* pick files */); + } + + private doPickAndOpen(options: INativeOpenDialogOptions, pickFolders: boolean, pickFiles: boolean): void { + const internalOptions = options as IInternalNativeOpenDialogOptions; + + internalOptions.pickFolders = pickFolders; + internalOptions.pickFiles = pickFiles; + + if (!internalOptions.dialogOptions) { + internalOptions.dialogOptions = Object.create(null); + } + + if (!internalOptions.dialogOptions.title) { + if (pickFolders && pickFiles) { + internalOptions.dialogOptions.title = localize('open', "Open"); + } else if (pickFolders) { + internalOptions.dialogOptions.title = localize('openFolder', "Open Folder"); + } else { + internalOptions.dialogOptions.title = localize('openFile', "Open File"); + } + } + + if (!internalOptions.telemetryEventName) { + if (pickFolders && pickFiles) { + internalOptions.telemetryEventName = 'openFileFolder'; + } else if (pickFolders) { + internalOptions.telemetryEventName = 'openFolder'; + } else { + internalOptions.telemetryEventName = 'openFile'; + } + } + + this.fileDialog.pickAndOpen(internalOptions); + } + + public quit(): void { + + // If the user selected to exit from an extension development host window, do not quit, but just + // close the window unless this is the last window that is opened. + const window = this.getFocusedWindow(); + if (window && window.isExtensionDevelopmentHost && this.getWindowCount() > 1) { + window.win.close(); + } + + // Otherwise: normal quit + else { + setTimeout(() => { + this.lifecycleService.quit(); + }, 10 /* delay to unwind callback stack (IPC) */); + } + } +} + +interface IInternalNativeOpenDialogOptions extends INativeOpenDialogOptions { + pickFolders?: boolean; + pickFiles?: boolean; +} + +class FileDialog { + + private static workingDirPickerStorageKey = 'pickerWorkingDir'; + + constructor( + private environmentService: IEnvironmentService, + private telemetryService: ITelemetryService, + private storageService: IStorageService, + private windowsMainService: IWindowsMainService + ) { + } + + public pickAndOpen(options: INativeOpenDialogOptions): void { + this.getFileOrFolderPaths(options, (paths: string[]) => { + const numberOfPaths = paths ? paths.length : 0; + + // Telemetry + if (options.telemetryEventName) { + this.telemetryService.publicLog(options.telemetryEventName, { + ...options.telemetryExtraData, + outcome: numberOfPaths ? 'success' : 'canceled', + numberOfPaths + }); + } + + // Open + if (numberOfPaths) { + this.windowsMainService.open({ + context: OpenContext.DIALOG, + cli: this.environmentService.args, + pathsToOpen: paths, + forceNewWindow: options.forceNewWindow, + forceOpenWorkspaceAsFile: options.dialogOptions && !equals(options.dialogOptions.filters, WORKSPACE_FILTER) + }); + } + }); + } + + public getFileOrFolderPaths(options: IInternalNativeOpenDialogOptions, clb: (paths: string[]) => void): void { + + // Ensure dialog options + if (!options.dialogOptions) { + options.dialogOptions = Object.create(null); + } + + // Ensure defaultPath + if (!options.dialogOptions.defaultPath) { + options.dialogOptions.defaultPath = this.storageService.getItem(FileDialog.workingDirPickerStorageKey); + } + + // Ensure properties + if (typeof options.pickFiles === 'boolean' || typeof options.pickFolders === 'boolean') { + options.dialogOptions.properties = void 0; // let it override based on the booleans + + if (options.pickFiles && options.pickFolders) { + options.dialogOptions.properties = ['multiSelections', 'openDirectory', 'openFile', 'createDirectory']; + } + } + + if (!options.dialogOptions.properties) { + options.dialogOptions.properties = ['multiSelections', options.pickFolders ? 'openDirectory' : 'openFile', 'createDirectory']; + } + + // Show Dialog + const focusedWindow = this.windowsMainService.getWindowById(options.windowId) || this.windowsMainService.getFocusedWindow(); + dialog.showOpenDialog(focusedWindow && focusedWindow.win, options.dialogOptions, paths => { + if (paths && paths.length > 0) { + + // Remember path in storage for next time + this.storageService.setItem(FileDialog.workingDirPickerStorageKey, path.dirname(paths[0])); + + // Return + return clb(paths); + } + + return clb(void (0)); + }); + } +} \ No newline at end of file diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts new file mode 100644 index 0000000000..091efdab25 --- /dev/null +++ b/src/vs/code/node/cli.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { spawn } from 'child_process'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { assign } from 'vs/base/common/objects'; +import { parseCLIProcessArgv, buildHelpMessage } from 'vs/platform/environment/node/argv'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import product from 'vs/platform/node/product'; +import pkg from 'vs/platform/node/package'; + +import * as fs from 'fs'; +import * as paths from 'path'; +import * as os from 'os'; + +function shouldSpawnCliProcess(argv: ParsedArgs): boolean { + return argv['list-extensions'] || !!argv['install-extension'] || !!argv['uninstall-extension']; +} + +interface IMainCli { + main: (argv: ParsedArgs) => TPromise; +} + +export function main(argv: string[]): TPromise { + let args: ParsedArgs; + + try { + args = parseCLIProcessArgv(argv); + } catch (err) { + console.error(err.message); + return TPromise.as(null); + } + + if (args.help) { + console.log(buildHelpMessage(product.nameLong, product.applicationName, pkg.version)); + } else if (args.version) { + console.log(`${pkg.version}\n${product.commit}`); + } else if (shouldSpawnCliProcess(args)) { + const mainCli = new TPromise(c => require(['vs/code/node/cliProcessMain'], c)); + return mainCli.then(cli => cli.main(args)); + } else { + const env = assign({}, process.env, { + // this will signal Code that it was spawned from this module + 'VSCODE_CLI': '1', + 'ELECTRON_NO_ATTACH_CONSOLE': '1' + }); + + delete env['ELECTRON_RUN_AS_NODE']; + + if (args.verbose) { + env['ELECTRON_ENABLE_LOGGING'] = '1'; + } + + // If we are started with --wait create a random temporary file + // and pass it over to the starting instance. We can use this file + // to wait for it to be deleted to monitor that the edited file + // is closed and then exit the waiting process. + let waitMarkerFilePath: string; + if (args.wait) { + let waitMarkerError: Error; + const randomTmpFile = paths.join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); + try { + fs.writeFileSync(randomTmpFile, ''); + waitMarkerFilePath = randomTmpFile; + argv.push('--waitMarkerFilePath', waitMarkerFilePath); + } catch (error) { + waitMarkerError = error; + } + + if (args.verbose) { + if (waitMarkerError) { + console.error(`Failed to create marker file for --wait: ${waitMarkerError.toString()}`); + } else { + console.log(`Marker file for --wait created: ${waitMarkerFilePath}`); + } + } + } + + const options = { + detached: true, + env + }; + + if (!args.verbose) { + options['stdio'] = 'ignore'; + } + + const child = spawn(process.execPath, argv.slice(2), options); + + if (args.verbose) { + child.stdout.on('data', (data: Buffer) => console.log(data.toString('utf8').trim())); + child.stderr.on('data', (data: Buffer) => console.log(data.toString('utf8').trim())); + } + + if (args.verbose) { + return new TPromise(c => child.once('exit', () => c(null))); + } + + if (args.wait && waitMarkerFilePath) { + return new TPromise(c => { + + // Complete when process exits + child.once('exit', () => c(null)); + + // Complete when wait marker file is deleted + const interval = setInterval(() => { + fs.exists(waitMarkerFilePath, exists => { + if (!exists) { + clearInterval(interval); + c(null); + } + }); + }, 1000); + }); + } + } + + return TPromise.as(null); +} + +function eventuallyExit(code: number): void { + setTimeout(() => process.exit(code), 0); +} + +main(process.argv) + .then(() => eventuallyExit(0)) + .then(null, err => { + console.error(err.stack ? err.stack : err); + eventuallyExit(1); + }); diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts new file mode 100644 index 0000000000..088de46ac5 --- /dev/null +++ b/src/vs/code/node/cliProcessMain.ts @@ -0,0 +1,201 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import product from 'vs/platform/node/product'; +import pkg from 'vs/platform/node/package'; +import * as path from 'path'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { sequence } from 'vs/base/common/async'; +import { IPager } from 'vs/base/common/paging'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { IExtensionManagementService, IExtensionGalleryService, IExtensionManifest, IGalleryExtension, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; +import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; +import { IRequestService } from 'vs/platform/request/node/request'; +import { RequestService } from 'vs/platform/request/node/requestService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; +import { mkdirp } from 'vs/base/node/pfs'; +import { IChoiceService } from 'vs/platform/message/common/message'; +import { ChoiceCliService } from 'vs/platform/message/node/messageCli'; + +const notFound = id => localize('notFound', "Extension '{0}' not found.", id); +const notInstalled = id => localize('notInstalled', "Extension '{0}' is not installed.", id); +const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, eg: {0}", 'ms-vscode.csharp'); + +function getId(manifest: IExtensionManifest, withVersion?: boolean): string { + if (withVersion) { + return `${manifest.publisher}.${manifest.name}@${manifest.version}`; + } else { + return `${manifest.publisher}.${manifest.name}`; + } +} + +type Task = { (): TPromise }; + +class Main { + + constructor( + @IExtensionManagementService private extensionManagementService: IExtensionManagementService, + @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService + ) { } + + run(argv: ParsedArgs): TPromise { + // TODO@joao - make this contributable + + if (argv['list-extensions']) { + return this.listExtensions(argv['show-versions']); + } else if (argv['install-extension']) { + const arg = argv['install-extension']; + const args: string[] = typeof arg === 'string' ? [arg] : arg; + return this.installExtension(args); + } else if (argv['uninstall-extension']) { + const arg = argv['uninstall-extension']; + const ids: string[] = typeof arg === 'string' ? [arg] : arg; + return this.uninstallExtension(ids); + } + return undefined; + } + + private listExtensions(showVersions: boolean): TPromise { + return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => { + extensions.forEach(e => console.log(getId(e.manifest, showVersions))); + }); + } + + private installExtension(extensions: string[]): TPromise { + const vsixTasks: Task[] = extensions + .filter(e => /\.vsix$/i.test(e)) + .map(id => () => { + const extension = path.isAbsolute(id) ? id : path.join(process.cwd(), id); + + return this.extensionManagementService.install(extension).then(() => { + console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed!", path.basename(extension))); + }); + }); + + const galleryTasks: Task[] = extensions + .filter(e => !/\.vsix$/i.test(e)) + .map(id => () => { + return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(installed => { + const isInstalled = installed.some(e => getId(e.manifest) === id); + + if (isInstalled) { + console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", id)); + return TPromise.as(null); + } + + return this.extensionGalleryService.query({ names: [id] }) + .then>(null, err => { + if (err.responseText) { + try { + const response = JSON.parse(err.responseText); + return TPromise.wrapError(response.message); + } catch (e) { + // noop + } + } + + return TPromise.wrapError(err); + }) + .then(result => { + const [extension] = result.firstPage; + + if (!extension) { + return TPromise.wrapError(new Error(`${notFound(id)}\n${useId}`)); + } + + console.log(localize('foundExtension', "Found '{0}' in the marketplace.", id)); + console.log(localize('installing', "Installing...")); + + return this.extensionManagementService.installFromGallery(extension, false) + .then(() => console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.version))); + }); + }); + }); + + return sequence([...vsixTasks, ...galleryTasks]); + } + + private uninstallExtension(ids: string[]): TPromise { + return sequence(ids.map(id => () => { + return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(installed => { + const [extension] = installed.filter(e => getId(e.manifest) === id); + + if (!extension) { + return TPromise.wrapError(new Error(`${notInstalled(id)}\n${useId}`)); + } + + console.log(localize('uninstalling', "Uninstalling {0}...", id)); + + return this.extensionManagementService.uninstall(extension, true) + .then(() => console.log(localize('successUninstall', "Extension '{0}' was successfully uninstalled!", id))); + }); + })); + } +} + +const eventPrefix = 'monacoworkbench'; + +export function main(argv: ParsedArgs): TPromise { + const services = new ServiceCollection(); + services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService, argv, process.execPath)); + + const instantiationService: IInstantiationService = new InstantiationService(services); + + return instantiationService.invokeFunction(accessor => { + const envService = accessor.get(IEnvironmentService); + + return TPromise.join([envService.appSettingsHome, envService.extensionsPath].map(p => mkdirp(p))).then(() => { + const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt } = envService; + + const services = new ServiceCollection(); + services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); + services.set(IRequestService, new SyncDescriptor(RequestService)); + services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); + services.set(IChoiceService, new SyncDescriptor(ChoiceCliService)); + + if (isBuilt && !extensionDevelopmentPath && product.enableTelemetry) { + const appenders: AppInsightsAppender[] = []; + + if (product.aiConfig && product.aiConfig.asimovKey) { + appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey)); + } + + // It is important to dispose the AI adapter properly because + // only then they flush remaining data. + process.once('exit', () => appenders.forEach(a => a.dispose())); + + const config: ITelemetryServiceConfig = { + appender: combinedAppender(...appenders), + commonProperties: resolveCommonProperties(product.commit, pkg.version), + piiPaths: [appRoot, extensionsPath] + }; + + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + } else { + services.set(ITelemetryService, NullTelemetryService); + } + + const instantiationService2 = instantiationService.createChild(services); + const main = instantiationService2.createInstance(Main); + + return main.run(argv); + }); + }); +} diff --git a/src/vs/code/node/paths.ts b/src/vs/code/node/paths.ts new file mode 100644 index 0000000000..0515b1170e --- /dev/null +++ b/src/vs/code/node/paths.ts @@ -0,0 +1,141 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as path from 'path'; +import * as arrays from 'vs/base/common/arrays'; +import * as strings from 'vs/base/common/strings'; +import * as paths from 'vs/base/common/paths'; +import * as platform from 'vs/base/common/platform'; +import * as types from 'vs/base/common/types'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { realpathSync } from 'vs/base/node/extfs'; + +export function validatePaths(args: ParsedArgs): ParsedArgs { + + // Realpath/normalize paths and watch out for goto line mode + const paths = doValidatePaths(args._, args.goto); + + // Update environment + args._ = paths; + args.diff = args.diff && paths.length === 2; + + return args; +} + +function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { + const cwd = process.env['VSCODE_CWD'] || process.cwd(); + const result = args.map(arg => { + let pathCandidate = String(arg); + + let parsedPath: IPathWithLineAndColumn; + if (gotoLineMode) { + parsedPath = parseLineAndColumnAware(pathCandidate); + pathCandidate = parsedPath.path; + } + + if (pathCandidate) { + pathCandidate = preparePath(cwd, pathCandidate); + } + + let realPath: string; + try { + realPath = realpathSync(pathCandidate); + } catch (error) { + // in case of an error, assume the user wants to create this file + // if the path is relative, we join it to the cwd + realPath = path.normalize(path.isAbsolute(pathCandidate) ? pathCandidate : path.join(cwd, pathCandidate)); + } + + const basename = path.basename(realPath); + if (basename /* can be empty if code is opened on root */ && !paths.isValidBasename(basename)) { + return null; // do not allow invalid file names + } + + if (gotoLineMode) { + parsedPath.path = realPath; + return toPath(parsedPath); + } + + return realPath; + }); + + const caseInsensitive = platform.isWindows || platform.isMacintosh; + const distinct = arrays.distinct(result, e => e && caseInsensitive ? e.toLowerCase() : e); + + return arrays.coalesce(distinct); +} + +function preparePath(cwd: string, p: string): string { + + // Trim trailing quotes + if (platform.isWindows) { + p = strings.rtrim(p, '"'); // https://github.com/Microsoft/vscode/issues/1498 + } + + // Trim whitespaces + p = strings.trim(strings.trim(p, ' '), '\t'); + + if (platform.isWindows) { + + // Resolve the path against cwd if it is relative + p = path.resolve(cwd, p); + + // Trim trailing '.' chars on Windows to prevent invalid file names + p = strings.rtrim(p, '.'); + } + + return p; +} + +export interface IPathWithLineAndColumn { + path: string; + line?: number; + column?: number; +} + +export function parseLineAndColumnAware(rawPath: string): IPathWithLineAndColumn { + const segments = rawPath.split(':'); // C:\file.txt:: + + let path: string; + let line: number = null; + let column: number = null; + + segments.forEach(segment => { + const segmentAsNumber = Number(segment); + if (!types.isNumber(segmentAsNumber)) { + path = !!path ? [path, segment].join(':') : segment; // a colon can well be part of a path (e.g. C:\...) + } else if (line === null) { + line = segmentAsNumber; + } else if (column === null) { + column = segmentAsNumber; + } + }); + + if (!path) { + throw new Error('Format for `--goto` should be: `FILE:LINE(:COLUMN)`'); + } + + return { + path: path, + line: line !== null ? line : void 0, + column: column !== null ? column : line !== null ? 1 : void 0 // if we have a line, make sure column is also set + }; +} + +function toPath(p: IPathWithLineAndColumn): string { + const segments = [p.path]; + + if (types.isNumber(p.line)) { + segments.push(String(p.line)); + } + + if (types.isNumber(p.column)) { + segments.push(String(p.column)); + } + + return segments.join(':'); +} \ No newline at end of file diff --git a/src/vs/code/node/shellEnv.ts b/src/vs/code/node/shellEnv.ts new file mode 100644 index 0000000000..0d6670b020 --- /dev/null +++ b/src/vs/code/node/shellEnv.ts @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as cp from 'child_process'; +import { assign } from 'vs/base/common/objects'; +import { generateUuid } from 'vs/base/common/uuid'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { isWindows } from 'vs/base/common/platform'; + +function getUnixShellEnvironment(): TPromise { + const promise = new TPromise((c, e) => { + const runAsNode = process.env['ELECTRON_RUN_AS_NODE']; + const noAttach = process.env['ELECTRON_NO_ATTACH_CONSOLE']; + const mark = generateUuid().replace(/-/g, '').substr(0, 12); + const regex = new RegExp(mark + '(.*)' + mark); + + const env = assign({}, process.env, { + ELECTRON_RUN_AS_NODE: '1', + ELECTRON_NO_ATTACH_CONSOLE: '1' + }); + + const command = `'${process.execPath}' -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`; + const child = cp.spawn(process.env.SHELL, ['-ilc', command], { + detached: true, + stdio: ['ignore', 'pipe', process.stderr], + env + }); + + const buffers: Buffer[] = []; + child.on('error', () => c({})); + child.stdout.on('data', b => buffers.push(b as Buffer)); + + child.on('close', (code: number, signal: any) => { + if (code !== 0) { + return e(new Error('Failed to get environment')); + } + + const raw = Buffer.concat(buffers).toString('utf8'); + const match = regex.exec(raw); + const rawStripped = match ? match[1] : '{}'; + + try { + const env = JSON.parse(rawStripped); + + if (runAsNode) { + env['ELECTRON_RUN_AS_NODE'] = runAsNode; + } else { + delete env['ELECTRON_RUN_AS_NODE']; + } + + if (noAttach) { + env['ELECTRON_NO_ATTACH_CONSOLE'] = noAttach; + } else { + delete env['ELECTRON_NO_ATTACH_CONSOLE']; + } + + c(env); + } catch (err) { + e(err); + } + }); + }); + + // swallow errors + return promise.then(null, () => ({})); +} + + +let _shellEnv: TPromise; + +/** + * We need to get the environment from a user's shell. + * This should only be done when Code itself is not launched + * from within a shell. + */ +export function getShellEnvironment(): TPromise { + if (_shellEnv === undefined) { + if (isWindows) { + _shellEnv = TPromise.as({}); + } else if (process.env['VSCODE_CLI'] === '1') { + _shellEnv = TPromise.as({}); + } else { + _shellEnv = getUnixShellEnvironment(); + } + } + + return _shellEnv; +} diff --git a/src/vs/code/node/windowsFinder.ts b/src/vs/code/node/windowsFinder.ts new file mode 100644 index 0000000000..dc018f3b99 --- /dev/null +++ b/src/vs/code/node/windowsFinder.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as path from 'path'; +import * as fs from 'fs'; +import * as platform from 'vs/base/common/platform'; +import * as paths from 'vs/base/common/paths'; +import { OpenContext } from 'vs/platform/windows/common/windows'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IResolvedWorkspace } from 'vs/platform/workspaces/common/workspaces'; + +export interface ISimpleWindow { + openedWorkspace?: IWorkspaceIdentifier; + openedFolderPath?: string; + openedFilePath?: string; + extensionDevelopmentPath?: string; + lastFocusTime: number; +} + +export interface IBestWindowOrFolderOptions { + windows: W[]; + newWindow: boolean; + reuseWindow: boolean; + context: OpenContext; + filePath?: string; + userHome?: string; + codeSettingsFolder?: string; + workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace; +} + +export function findBestWindowOrFolderForFile({ windows, newWindow, reuseWindow, context, filePath, userHome, codeSettingsFolder, workspaceResolver }: IBestWindowOrFolderOptions): W | string { + if (!newWindow && filePath && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) { + const windowOnFilePath = findWindowOnFilePath(windows, filePath, workspaceResolver); + + // 1) window wins if it has a workspace opened + if (windowOnFilePath && !!windowOnFilePath.openedWorkspace) { + return windowOnFilePath; + } + + // 2) window wins if it has a folder opened that is more specific than settings folder + const folderWithCodeSettings = !reuseWindow && findFolderWithCodeSettings(filePath, userHome, codeSettingsFolder); + if (windowOnFilePath && !(folderWithCodeSettings && folderWithCodeSettings.length > windowOnFilePath.openedFolderPath.length)) { + return windowOnFilePath; + } + + // 3) finally return path to folder with settings + if (folderWithCodeSettings) { + return folderWithCodeSettings; + } + } + + return !newWindow ? getLastActiveWindow(windows) : null; +} + +function findWindowOnFilePath(windows: W[], filePath: string, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace): W { + + // First check for windows with workspaces that have a parent folder of the provided path opened + const workspaceWindows = windows.filter(window => !!window.openedWorkspace); + for (let i = 0; i < workspaceWindows.length; i++) { + const window = workspaceWindows[i]; + const resolvedWorkspace = workspaceResolver(window.openedWorkspace); + if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => paths.isEqualOrParent(filePath, folder.path, !platform.isLinux /* ignorecase */))) { + return window; + } + } + + // Then go with single folder windows that are parent of the provided file path + const singleFolderWindowsOnFilePath = windows.filter(window => typeof window.openedFolderPath === 'string' && paths.isEqualOrParent(filePath, window.openedFolderPath, !platform.isLinux /* ignorecase */)); + if (singleFolderWindowsOnFilePath.length) { + return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderPath.length - b.openedFolderPath.length))[0]; + } + + return null; +} + +function findFolderWithCodeSettings(filePath: string, userHome?: string, codeSettingsFolder?: string): string { + let folder = path.dirname(paths.normalize(filePath, true)); + let homeFolder = userHome && paths.normalize(userHome, true); + if (!platform.isLinux) { + homeFolder = homeFolder && homeFolder.toLowerCase(); + } + + let previous = null; + while (folder !== previous) { + if (hasCodeSettings(folder, homeFolder, codeSettingsFolder)) { + return folder; + } + + previous = folder; + folder = path.dirname(folder); + } + + return null; +} + +// {{SQL CARBON EDIT}} +function hasCodeSettings(folder: string, normalizedUserHome?: string, codeSettingsFolder = '.sqlops') { + try { + if ((platform.isLinux ? folder : folder.toLowerCase()) === normalizedUserHome) { + return fs.statSync(path.join(folder, codeSettingsFolder, 'settings.json')).isFile(); // ~/.vscode/extensions is used for extensions + } + + return fs.statSync(path.join(folder, codeSettingsFolder)).isDirectory(); + } catch (err) { + // assume impossible to access + } + + return false; +} + +export function getLastActiveWindow(windows: W[]): W { + const lastFocusedDate = Math.max.apply(Math, windows.map(window => window.lastFocusTime)); + + return windows.filter(window => window.lastFocusTime === lastFocusedDate)[0]; +} + +export function findWindowOnWorkspace(windows: W[], workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)): W { + return windows.filter(window => { + + // match on folder + if (isSingleFolderWorkspaceIdentifier(workspace)) { + if (typeof window.openedFolderPath === 'string' && (paths.isEqual(window.openedFolderPath, workspace, !platform.isLinux /* ignorecase */))) { + return true; + } + } + + // match on workspace + else { + if (window.openedWorkspace && window.openedWorkspace.id === workspace.id) { + return true; + } + } + + return false; + })[0]; +} + +export function findWindowOnExtensionDevelopmentPath(windows: W[], extensionDevelopmentPath: string): W { + return windows.filter(window => { + + // match on extension development path + if (paths.isEqual(window.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) { + return true; + } + + return false; + })[0]; +} + +export function findWindowOnWorkspaceOrFolderPath(windows: W[], path: string): W { + return windows.filter(window => { + + // check for workspace config path + if (window.openedWorkspace && paths.isEqual(window.openedWorkspace.configPath, path, !platform.isLinux /* ignorecase */)) { + return true; + } + + // check for folder path + if (window.openedFolderPath && paths.isEqual(window.openedFolderPath, path, !platform.isLinux /* ignorecase */)) { + return true; + } + + return false; + })[0]; +} \ No newline at end of file diff --git a/src/vs/code/test/node/argv.test.ts b/src/vs/code/test/node/argv.test.ts new file mode 100644 index 0000000000..ab0c4acc7f --- /dev/null +++ b/src/vs/code/test/node/argv.test.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import assert = require('assert'); +import { formatOptions } from 'vs/platform/environment/node/argv'; + +suite('formatOptions', () => { + test('Text should display small columns correctly', () => { + assert.equal(formatOptions({ 'foo': 'bar' }, 80), ' foo bar'); + assert.equal( + formatOptions({ + 'f': 'bar', + 'fo': 'ba', + 'foo': 'b' + }, 80), + ' f bar\n' + + ' fo ba\n' + + ' foo b'); + }); + + test('Text should wrap', () => { + assert.equal( + formatOptions({ + 'foo': ('bar ').repeat(9) + }, 40), + ' foo bar bar bar bar bar bar bar bar\n' + + ' bar'); + }); + + test('Text should revert to the condensed view when the terminal is too narrow', () => { + assert.equal( + formatOptions({ + 'foo': ('bar ').repeat(9) + }, 30), + ' foo\n' + + ' bar bar bar bar bar bar bar bar bar '); + }); +}); diff --git a/src/vs/code/test/node/fixtures/no_vscode_folder/file.txt b/src/vs/code/test/node/fixtures/no_vscode_folder/file.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/vs/code/test/node/fixtures/vscode_folder/_vscode/keepfolder.txt b/src/vs/code/test/node/fixtures/vscode_folder/_vscode/keepfolder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/vs/code/test/node/fixtures/vscode_folder/file.txt b/src/vs/code/test/node/fixtures/vscode_folder/file.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/vs/code/test/node/fixtures/vscode_folder/nested_vscode_folder/_vscode/keepfolder.txt b/src/vs/code/test/node/fixtures/vscode_folder/nested_vscode_folder/_vscode/keepfolder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/vs/code/test/node/fixtures/vscode_home_folder/_vscode/settings.json b/src/vs/code/test/node/fixtures/vscode_home_folder/_vscode/settings.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/src/vs/code/test/node/fixtures/vscode_home_folder/_vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/vs/code/test/node/fixtures/vscode_home_folder/file.txt b/src/vs/code/test/node/fixtures/vscode_home_folder/file.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/vs/code/test/node/windowsFinder.test.ts b/src/vs/code/test/node/windowsFinder.test.ts new file mode 100644 index 0000000000..6a9cd999cd --- /dev/null +++ b/src/vs/code/test/node/windowsFinder.test.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import assert = require('assert'); +import path = require('path'); +import { findBestWindowOrFolderForFile, ISimpleWindow, IBestWindowOrFolderOptions } from 'vs/code/node/windowsFinder'; +import { OpenContext } from 'vs/platform/windows/common/windows'; +import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +const fixturesFolder = require.toUrl('./fixtures'); + +const testWorkspace: IWorkspaceIdentifier = { + id: Date.now().toString(), + configPath: path.join(fixturesFolder, 'workspaces.json') +}; + +function options(custom?: Partial>): IBestWindowOrFolderOptions { + return { + windows: [], + newWindow: false, + reuseWindow: false, + context: OpenContext.CLI, + codeSettingsFolder: '_vscode', + workspaceResolver: workspace => { return workspace === testWorkspace ? { id: testWorkspace.id, configPath: workspace.configPath, folders: [{ path: path.join(fixturesFolder, 'vscode_workspace_1_folder') }, { path: path.join(fixturesFolder, 'vscode_workspace_2_folder') }] } : null; }, + ...custom + }; +} + +const vscodeFolderWindow = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'vscode_folder') }; +const lastActiveWindow = { lastFocusTime: 3, openedFolderPath: null }; +const noVscodeFolderWindow = { lastFocusTime: 2, openedFolderPath: path.join(fixturesFolder, 'no_vscode_folder') }; +const windows = [ + vscodeFolderWindow, + lastActiveWindow, + noVscodeFolderWindow, +]; + +suite('WindowsFinder', () => { + + test('New window without folder when no windows exist', () => { + assert.equal(findBestWindowOrFolderForFile(options()), null); + assert.equal(findBestWindowOrFolderForFile(options({ + filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt') + })), null); + assert.equal(findBestWindowOrFolderForFile(options({ + filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), + newWindow: true // We assume this implies 'editor' work mode, might need separate CLI option later. + })), null); + assert.equal(findBestWindowOrFolderForFile(options({ + filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), + reuseWindow: true // We assume this implies 'editor' work mode, might need separate CLI option later. + })), null); + assert.equal(findBestWindowOrFolderForFile(options({ + filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), + context: OpenContext.API + })), null); + }); + + test('New window with folder when no windows exist', () => { + assert.equal(findBestWindowOrFolderForFile(options({ + filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt') + })), path.join(fixturesFolder, 'vscode_folder')); + assert.equal(findBestWindowOrFolderForFile(options({ + filePath: path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt') + })), path.join(fixturesFolder, 'vscode_folder')); + }); + + test('New window without folder when windows exist', () => { + assert.equal(findBestWindowOrFolderForFile(options({ + windows, + filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'), + newWindow: true + })), null); + }); + + test('Last active window', () => { + assert.equal(findBestWindowOrFolderForFile(options({ + windows + })), lastActiveWindow); + assert.equal(findBestWindowOrFolderForFile(options({ + windows, + filePath: path.join(fixturesFolder, 'no_vscode_folder2', 'file.txt') + })), lastActiveWindow); + assert.equal(findBestWindowOrFolderForFile(options({ + windows: [lastActiveWindow, noVscodeFolderWindow], + filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), + reuseWindow: true + })), lastActiveWindow); + assert.equal(findBestWindowOrFolderForFile(options({ + windows, + filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'), + context: OpenContext.API + })), lastActiveWindow); + }); + + test('Existing window with folder', () => { + assert.equal(findBestWindowOrFolderForFile(options({ + windows, + filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt') + })), noVscodeFolderWindow); + assert.equal(findBestWindowOrFolderForFile(options({ + windows, + filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt') + })), vscodeFolderWindow); + }); + + test('Existing window wins over vscode folder if more specific', () => { + const window = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder') }; + assert.equal(findBestWindowOrFolderForFile(options({ + windows: [window], + filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt') + })), window); + // check + assert.equal(findBestWindowOrFolderForFile(options({ + windows: [window], + filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder2', 'subfolder', 'file.txt') + })), path.join(fixturesFolder, 'vscode_folder')); + }); + + test('More specific existing window wins', () => { + const window = { lastFocusTime: 2, openedFolderPath: path.join(fixturesFolder, 'no_vscode_folder') }; + const nestedFolderWindow = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder') }; + assert.equal(findBestWindowOrFolderForFile(options({ + windows: [window, nestedFolderWindow], + filePath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder', 'subfolder', 'file.txt') + })), nestedFolderWindow); + }); + + test('VSCode folder wins over existing window if more specific', () => { + const window = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'vscode_folder') }; + assert.equal(findBestWindowOrFolderForFile(options({ + windows: [window], + filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder', 'subfolder', 'file.txt') + })), path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder')); + // check + assert.equal(findBestWindowOrFolderForFile(options({ + windows: [window], + filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt') + })), window); + }); + + test('More specific VSCode folder wins', () => { + assert.equal(findBestWindowOrFolderForFile(options({ + filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder', 'subfolder', 'file.txt') + })), path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder')); + }); + + test('VSCode folder in home folder needs settings.json', () => { + // Because ~/.vscode/extensions is used for extensions, ~/.vscode is not enough as a hint. + assert.equal(findBestWindowOrFolderForFile(options({ + filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), + userHome: path.join(fixturesFolder, 'vscode_folder') + })), null); + assert.equal(findBestWindowOrFolderForFile(options({ + filePath: path.join(fixturesFolder, 'vscode_home_folder', 'file.txt'), + userHome: path.join(fixturesFolder, 'vscode_home_folder') + })), path.join(fixturesFolder, 'vscode_home_folder')); + }); + + test('Workspace folder wins', () => { + const window = { lastFocusTime: 1, openedWorkspace: testWorkspace }; + assert.equal(findBestWindowOrFolderForFile(options({ + windows: [window], + filePath: path.join(fixturesFolder, 'vscode_workspace_2_folder', 'nested_vscode_folder', 'subfolder', 'file.txt') + })), window); + }); +}); diff --git a/src/vs/css.build.js b/src/vs/css.build.js new file mode 100644 index 0000000000..a3dc73f3d0 --- /dev/null +++ b/src/vs/css.build.js @@ -0,0 +1,362 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + * Please make sure to make edits in the .ts file at https://github.com/Microsoft/vscode-loader/ + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------*/ +'use strict'; +var _cssPluginGlobal = this; +var CSSBuildLoaderPlugin; +(function (CSSBuildLoaderPlugin) { + var global = _cssPluginGlobal || {}; + /** + * Known issue: + * - In IE there is no way to know if the CSS file loaded successfully or not. + */ + var BrowserCSSLoader = (function () { + function BrowserCSSLoader() { + this._pendingLoads = 0; + } + BrowserCSSLoader.prototype.attachListeners = function (name, linkNode, callback, errorback) { + var unbind = function () { + linkNode.removeEventListener('load', loadEventListener); + linkNode.removeEventListener('error', errorEventListener); + }; + var loadEventListener = function (e) { + unbind(); + callback(); + }; + var errorEventListener = function (e) { + unbind(); + errorback(e); + }; + linkNode.addEventListener('load', loadEventListener); + linkNode.addEventListener('error', errorEventListener); + }; + BrowserCSSLoader.prototype._onLoad = function (name, callback) { + this._pendingLoads--; + callback(); + }; + BrowserCSSLoader.prototype._onLoadError = function (name, errorback, err) { + this._pendingLoads--; + errorback(err); + }; + BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) { + this._pendingLoads++; + var head = document.head || document.getElementsByTagName('head')[0]; + var other = head.getElementsByTagName('link') || document.head.getElementsByTagName('script'); + if (other.length > 0) { + head.insertBefore(linkNode, other[other.length - 1]); + } + else { + head.appendChild(linkNode); + } + }; + BrowserCSSLoader.prototype.createLinkTag = function (name, cssUrl, externalCallback, externalErrorback) { + var _this = this; + var linkNode = document.createElement('link'); + linkNode.setAttribute('rel', 'stylesheet'); + linkNode.setAttribute('type', 'text/css'); + linkNode.setAttribute('data-name', name); + var callback = function () { return _this._onLoad(name, externalCallback); }; + var errorback = function (err) { return _this._onLoadError(name, externalErrorback, err); }; + this.attachListeners(name, linkNode, callback, errorback); + linkNode.setAttribute('href', cssUrl); + return linkNode; + }; + BrowserCSSLoader.prototype._linkTagExists = function (name, cssUrl) { + var i, len, nameAttr, hrefAttr, links = document.getElementsByTagName('link'); + for (i = 0, len = links.length; i < len; i++) { + nameAttr = links[i].getAttribute('data-name'); + hrefAttr = links[i].getAttribute('href'); + if (nameAttr === name || hrefAttr === cssUrl) { + return true; + } + } + return false; + }; + BrowserCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) { + if (this._linkTagExists(name, cssUrl)) { + externalCallback(); + return; + } + var linkNode = this.createLinkTag(name, cssUrl, externalCallback, externalErrorback); + this._insertLinkNode(linkNode); + }; + return BrowserCSSLoader; + }()); + var NodeCSSLoader = (function () { + function NodeCSSLoader() { + this.fs = require.nodeRequire('fs'); + } + NodeCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) { + var contents = this.fs.readFileSync(cssUrl, 'utf8'); + // Remove BOM + if (contents.charCodeAt(0) === NodeCSSLoader.BOM_CHAR_CODE) { + contents = contents.substring(1); + } + externalCallback(contents); + }; + return NodeCSSLoader; + }()); + NodeCSSLoader.BOM_CHAR_CODE = 65279; + // ------------------------------ Finally, the plugin + var CSSPlugin = (function () { + function CSSPlugin(cssLoader) { + this.cssLoader = cssLoader; + } + CSSPlugin.prototype.load = function (name, req, load, config) { + config = config || {}; + var myConfig = config['vs/css'] || {}; + global.inlineResources = myConfig.inlineResources; + global.inlineResourcesLimit = myConfig.inlineResourcesLimit || 5000; + var cssUrl = req.toUrl(name + '.css'); + this.cssLoader.load(name, cssUrl, function (contents) { + // Contents has the CSS file contents if we are in a build + if (config.isBuild) { + CSSPlugin.BUILD_MAP[name] = contents; + CSSPlugin.BUILD_PATH_MAP[name] = cssUrl; + } + load({}); + }, function (err) { + if (typeof load.error === 'function') { + load.error('Could not find ' + cssUrl + ' or it was empty'); + } + }); + }; + CSSPlugin.prototype.write = function (pluginName, moduleName, write) { + // getEntryPoint is a Monaco extension to r.js + var entryPoint = write.getEntryPoint(); + // r.js destroys the context of this plugin between calling 'write' and 'writeFile' + // so the only option at this point is to leak the data to a global + global.cssPluginEntryPoints = global.cssPluginEntryPoints || {}; + global.cssPluginEntryPoints[entryPoint] = global.cssPluginEntryPoints[entryPoint] || []; + global.cssPluginEntryPoints[entryPoint].push({ + moduleName: moduleName, + contents: CSSPlugin.BUILD_MAP[moduleName], + fsPath: CSSPlugin.BUILD_PATH_MAP[moduleName], + }); + write.asModule(pluginName + '!' + moduleName, 'define([\'vs/css!' + entryPoint + '\'], {});'); + }; + CSSPlugin.prototype.writeFile = function (pluginName, moduleName, req, write, config) { + if (global.cssPluginEntryPoints && global.cssPluginEntryPoints.hasOwnProperty(moduleName)) { + var fileName = req.toUrl(moduleName + '.css'); + var contents = [ + '/*---------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' + ], entries = global.cssPluginEntryPoints[moduleName]; + for (var i = 0; i < entries.length; i++) { + if (global.inlineResources) { + contents.push(Utilities.rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, global.inlineResources === 'base64', global.inlineResourcesLimit)); + } + else { + contents.push(Utilities.rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); + } + } + write(fileName, contents.join('\r\n')); + } + }; + CSSPlugin.prototype.getInlinedResources = function () { + return global.cssInlinedResources || []; + }; + return CSSPlugin; + }()); + CSSPlugin.BUILD_MAP = {}; + CSSPlugin.BUILD_PATH_MAP = {}; + CSSBuildLoaderPlugin.CSSPlugin = CSSPlugin; + var Utilities = (function () { + function Utilities() { + } + Utilities.startsWith = function (haystack, needle) { + return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; + }; + /** + * Find the path of a file. + */ + Utilities.pathOf = function (filename) { + var lastSlash = filename.lastIndexOf('/'); + if (lastSlash !== -1) { + return filename.substr(0, lastSlash + 1); + } + else { + return ''; + } + }; + /** + * A conceptual a + b for paths. + * Takes into account if `a` contains a protocol. + * Also normalizes the result: e.g.: a/b/ + ../c => a/c + */ + Utilities.joinPaths = function (a, b) { + function findSlashIndexAfterPrefix(haystack, prefix) { + if (Utilities.startsWith(haystack, prefix)) { + return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); + } + return 0; + } + var aPathStartIndex = 0; + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); + function pushPiece(pieces, piece) { + if (piece === './') { + // Ignore + return; + } + if (piece === '../') { + var prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); + if (prevPiece && prevPiece === '/') { + // Ignore + return; + } + if (prevPiece && prevPiece !== '../') { + // Pop + pieces.pop(); + return; + } + } + // Push + pieces.push(piece); + } + function push(pieces, path) { + while (path.length > 0) { + var slashIndex = path.indexOf('/'); + var piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); + path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); + pushPiece(pieces, piece); + } + } + var pieces = []; + push(pieces, a.substr(aPathStartIndex)); + if (b.length > 0 && b.charAt(0) === '/') { + pieces = []; + } + push(pieces, b); + return a.substring(0, aPathStartIndex) + pieces.join(''); + }; + Utilities.commonPrefix = function (str1, str2) { + var len = Math.min(str1.length, str2.length); + for (var i = 0; i < len; i++) { + if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { + break; + } + } + return str1.substring(0, i); + }; + Utilities.commonFolderPrefix = function (fromPath, toPath) { + var prefix = Utilities.commonPrefix(fromPath, toPath); + var slashIndex = prefix.lastIndexOf('/'); + if (slashIndex === -1) { + return ''; + } + return prefix.substring(0, slashIndex + 1); + }; + Utilities.relativePath = function (fromPath, toPath) { + if (Utilities.startsWith(toPath, '/') || Utilities.startsWith(toPath, 'http://') || Utilities.startsWith(toPath, 'https://')) { + return toPath; + } + // Ignore common folder prefix + var prefix = Utilities.commonFolderPrefix(fromPath, toPath); + fromPath = fromPath.substr(prefix.length); + toPath = toPath.substr(prefix.length); + var upCount = fromPath.split('/').length; + var result = ''; + for (var i = 1; i < upCount; i++) { + result += '../'; + } + return result + toPath; + }; + Utilities._replaceURL = function (contents, replacer) { + // Use ")" as the terminator as quotes are oftentimes not used at all + return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, function (_) { + var matches = []; + for (var _i = 1; _i < arguments.length; _i++) { + matches[_i - 1] = arguments[_i]; + } + var url = matches[0]; + // Eliminate starting quotes (the initial whitespace is not captured) + if (url.charAt(0) === '"' || url.charAt(0) === '\'') { + url = url.substring(1); + } + // The ending whitespace is captured + while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { + url = url.substring(0, url.length - 1); + } + // Eliminate ending quotes + if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { + url = url.substring(0, url.length - 1); + } + if (!Utilities.startsWith(url, 'data:') && !Utilities.startsWith(url, 'http://') && !Utilities.startsWith(url, 'https://')) { + url = replacer(url); + } + return 'url(' + url + ')'; + }); + }; + Utilities.rewriteUrls = function (originalFile, newFile, contents) { + return this._replaceURL(contents, function (url) { + var absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); + return Utilities.relativePath(newFile, absoluteUrl); + }); + }; + Utilities.rewriteOrInlineUrls = function (originalFileFSPath, originalFile, newFile, contents, forceBase64, inlineByteLimit) { + var fs = require.nodeRequire('fs'); + var path = require.nodeRequire('path'); + return this._replaceURL(contents, function (url) { + if (/\.(svg|png)$/.test(url)) { + var fsPath = path.join(path.dirname(originalFileFSPath), url); + var fileContents = fs.readFileSync(fsPath); + if (fileContents.length < inlineByteLimit) { + global.cssInlinedResources = global.cssInlinedResources || []; + var normalizedFSPath = fsPath.replace(/\\/g, '/'); + if (global.cssInlinedResources.indexOf(normalizedFSPath) >= 0) { + console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); + } + global.cssInlinedResources.push(normalizedFSPath); + var MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; + var DATA = ';base64,' + fileContents.toString('base64'); + if (!forceBase64 && /\.svg$/.test(url)) { + // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris + var newText = fileContents.toString() + .replace(/"/g, '\'') + .replace(//g, '%3E') + .replace(/&/g, '%26') + .replace(/#/g, '%23') + .replace(/\s+/g, ' '); + var encodedData = ',' + newText; + if (encodedData.length < DATA.length) { + DATA = encodedData; + } + } + return '"data:' + MIME + DATA + '"'; + } + } + var absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); + return Utilities.relativePath(newFile, absoluteUrl); + }); + }; + return Utilities; + }()); + CSSBuildLoaderPlugin.Utilities = Utilities; + (function () { + var cssLoader = null; + var isElectron = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions['electron'] !== 'undefined'); + if (typeof process !== 'undefined' && process.versions && !!process.versions.node && !isElectron) { + cssLoader = new NodeCSSLoader(); + } + else { + cssLoader = new BrowserCSSLoader(); + } + define('vs/css', new CSSPlugin(cssLoader)); + })(); +})(CSSBuildLoaderPlugin || (CSSBuildLoaderPlugin = {})); diff --git a/src/vs/css.d.ts b/src/vs/css.d.ts new file mode 100644 index 0000000000..e99b7c43cc --- /dev/null +++ b/src/vs/css.d.ts @@ -0,0 +1,5 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + diff --git a/src/vs/css.js b/src/vs/css.js new file mode 100644 index 0000000000..a87ccc354a --- /dev/null +++ b/src/vs/css.js @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + * Please make sure to make edits in the .ts file at https://github.com/Microsoft/vscode-loader/ + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------*/ +'use strict'; +var CSSLoaderPlugin; +(function (CSSLoaderPlugin) { + /** + * Known issue: + * - In IE there is no way to know if the CSS file loaded successfully or not. + */ + var BrowserCSSLoader = (function () { + function BrowserCSSLoader() { + this._pendingLoads = 0; + } + BrowserCSSLoader.prototype.attachListeners = function (name, linkNode, callback, errorback) { + var unbind = function () { + linkNode.removeEventListener('load', loadEventListener); + linkNode.removeEventListener('error', errorEventListener); + }; + var loadEventListener = function (e) { + unbind(); + callback(); + }; + var errorEventListener = function (e) { + unbind(); + errorback(e); + }; + linkNode.addEventListener('load', loadEventListener); + linkNode.addEventListener('error', errorEventListener); + }; + BrowserCSSLoader.prototype._onLoad = function (name, callback) { + this._pendingLoads--; + callback(); + }; + BrowserCSSLoader.prototype._onLoadError = function (name, errorback, err) { + this._pendingLoads--; + errorback(err); + }; + BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) { + this._pendingLoads++; + var head = document.head || document.getElementsByTagName('head')[0]; + var other = head.getElementsByTagName('link') || document.head.getElementsByTagName('script'); + if (other.length > 0) { + head.insertBefore(linkNode, other[other.length - 1]); + } + else { + head.appendChild(linkNode); + } + }; + BrowserCSSLoader.prototype.createLinkTag = function (name, cssUrl, externalCallback, externalErrorback) { + var _this = this; + var linkNode = document.createElement('link'); + linkNode.setAttribute('rel', 'stylesheet'); + linkNode.setAttribute('type', 'text/css'); + linkNode.setAttribute('data-name', name); + var callback = function () { return _this._onLoad(name, externalCallback); }; + var errorback = function (err) { return _this._onLoadError(name, externalErrorback, err); }; + this.attachListeners(name, linkNode, callback, errorback); + linkNode.setAttribute('href', cssUrl); + return linkNode; + }; + BrowserCSSLoader.prototype._linkTagExists = function (name, cssUrl) { + var i, len, nameAttr, hrefAttr, links = document.getElementsByTagName('link'); + for (i = 0, len = links.length; i < len; i++) { + nameAttr = links[i].getAttribute('data-name'); + hrefAttr = links[i].getAttribute('href'); + if (nameAttr === name || hrefAttr === cssUrl) { + return true; + } + } + return false; + }; + BrowserCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) { + if (this._linkTagExists(name, cssUrl)) { + externalCallback(); + return; + } + var linkNode = this.createLinkTag(name, cssUrl, externalCallback, externalErrorback); + this._insertLinkNode(linkNode); + }; + return BrowserCSSLoader; + }()); + // ------------------------------ Finally, the plugin + var CSSPlugin = (function () { + function CSSPlugin() { + this._cssLoader = new BrowserCSSLoader(); + } + CSSPlugin.prototype.load = function (name, req, load) { + var cssUrl = req.toUrl(name + '.css'); + this._cssLoader.load(name, cssUrl, function (contents) { + load({}); + }, function (err) { + if (typeof load.error === 'function') { + load.error('Could not find ' + cssUrl + ' or it was empty'); + } + }); + }; + return CSSPlugin; + }()); + CSSLoaderPlugin.CSSPlugin = CSSPlugin; + function init() { + define('vs/css', new CSSPlugin()); + } + CSSLoaderPlugin.init = init; + ; + if (typeof doNotInitLoader === 'undefined') { + init(); + } +})(CSSLoaderPlugin || (CSSLoaderPlugin = {})); diff --git a/src/vs/editor/browser/codeEditor.ts b/src/vs/editor/browser/codeEditor.ts new file mode 100644 index 0000000000..acf7df3347 --- /dev/null +++ b/src/vs/editor/browser/codeEditor.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IEditorContributionCtor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { EditorAction, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { EditorBrowserRegistry } from 'vs/editor/browser/editorBrowserExtensions'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +export class CodeEditor extends CodeEditorWidget { + + constructor( + domElement: HTMLElement, + options: IEditorOptions, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @ICommandService commandService: ICommandService, + @IContextKeyService contextKeyService: IContextKeyService, + @IThemeService themeService: IThemeService + ) { + super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService, themeService); + } + + protected _getContributions(): IEditorContributionCtor[] { + return [].concat(EditorBrowserRegistry.getEditorContributions()).concat(CommonEditorRegistry.getEditorContributions()); + } + + protected _getActions(): EditorAction[] { + return CommonEditorRegistry.getEditorActions(); + } +} diff --git a/src/vs/editor/browser/config/charWidthReader.ts b/src/vs/editor/browser/config/charWidthReader.ts new file mode 100644 index 0000000000..c355088681 --- /dev/null +++ b/src/vs/editor/browser/config/charWidthReader.ts @@ -0,0 +1,159 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; + +export const enum CharWidthRequestType { + Regular = 0, + Italic = 1, + Bold = 2 +} + +export class CharWidthRequest { + + public readonly chr: string; + public readonly type: CharWidthRequestType; + public width: number; + + constructor(chr: string, type: CharWidthRequestType) { + this.chr = chr; + this.type = type; + this.width = 0; + } + + public fulfill(width: number) { + this.width = width; + } +} + +interface ICharWidthReader { + read(): void; +} + +class DomCharWidthReader implements ICharWidthReader { + + private readonly _bareFontInfo: BareFontInfo; + private readonly _requests: CharWidthRequest[]; + + private _container: HTMLElement; + private _testElements: HTMLSpanElement[]; + + constructor(bareFontInfo: BareFontInfo, requests: CharWidthRequest[]) { + this._bareFontInfo = bareFontInfo; + this._requests = requests; + + this._container = null; + this._testElements = null; + } + + public read(): void { + // Create a test container with all these test elements + this._createDomElements(); + + // Add the container to the DOM + document.body.appendChild(this._container); + + // Read character widths + this._readFromDomElements(); + + // Remove the container from the DOM + document.body.removeChild(this._container); + + this._container = null; + this._testElements = null; + } + + private _createDomElements(): void { + let container = document.createElement('div'); + container.style.position = 'absolute'; + container.style.top = '-50000px'; + container.style.width = '50000px'; + + let regularDomNode = document.createElement('div'); + regularDomNode.style.fontFamily = this._bareFontInfo.fontFamily; + regularDomNode.style.fontWeight = this._bareFontInfo.fontWeight; + regularDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; + regularDomNode.style.lineHeight = this._bareFontInfo.lineHeight + 'px'; + regularDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; + container.appendChild(regularDomNode); + + let boldDomNode = document.createElement('div'); + boldDomNode.style.fontFamily = this._bareFontInfo.fontFamily; + boldDomNode.style.fontWeight = 'bold'; + boldDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; + boldDomNode.style.lineHeight = this._bareFontInfo.lineHeight + 'px'; + boldDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; + container.appendChild(boldDomNode); + + let italicDomNode = document.createElement('div'); + italicDomNode.style.fontFamily = this._bareFontInfo.fontFamily; + italicDomNode.style.fontWeight = this._bareFontInfo.fontWeight; + italicDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; + italicDomNode.style.lineHeight = this._bareFontInfo.lineHeight + 'px'; + italicDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; + italicDomNode.style.fontStyle = 'italic'; + container.appendChild(italicDomNode); + + let testElements: HTMLSpanElement[] = []; + for (let i = 0, len = this._requests.length; i < len; i++) { + const request = this._requests[i]; + + let parent: HTMLElement; + if (request.type === CharWidthRequestType.Regular) { + parent = regularDomNode; + } + if (request.type === CharWidthRequestType.Bold) { + parent = boldDomNode; + } + if (request.type === CharWidthRequestType.Italic) { + parent = italicDomNode; + } + + parent.appendChild(document.createElement('br')); + + let testElement = document.createElement('span'); + DomCharWidthReader._render(testElement, request); + parent.appendChild(testElement); + + testElements[i] = testElement; + } + + this._container = container; + this._testElements = testElements; + } + + private static _render(testElement: HTMLElement, request: CharWidthRequest): void { + if (request.chr === ' ') { + let htmlString = ' '; + // Repeat character 256 (2^8) times + for (let i = 0; i < 8; i++) { + htmlString += htmlString; + } + testElement.innerHTML = htmlString; + } else { + let testString = request.chr; + // Repeat character 256 (2^8) times + for (let i = 0; i < 8; i++) { + testString += testString; + } + testElement.textContent = testString; + } + } + + private _readFromDomElements(): void { + for (let i = 0, len = this._requests.length; i < len; i++) { + const request = this._requests[i]; + const testElement = this._testElements[i]; + + request.fulfill(testElement.offsetWidth / 256); + } + } +} + +export function readCharWidths(bareFontInfo: BareFontInfo, requests: CharWidthRequest[]): void { + let reader = new DomCharWidthReader(bareFontInfo, requests); + reader.read(); +} diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts new file mode 100644 index 0000000000..bb65179044 --- /dev/null +++ b/src/vs/editor/browser/config/configuration.ts @@ -0,0 +1,364 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event, { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import * as browser from 'vs/base/browser/browser'; +import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; +import { IDimension } from 'vs/editor/common/editorCommon'; +import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo'; +import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; +import { FastDomNode } from 'vs/base/browser/fastDomNode'; +import { CharWidthRequest, CharWidthRequestType, readCharWidths } from 'vs/editor/browser/config/charWidthReader'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; + +class CSSBasedConfigurationCache { + + private _keys: { [key: string]: BareFontInfo; }; + private _values: { [key: string]: FontInfo; }; + + constructor() { + this._keys = Object.create(null); + this._values = Object.create(null); + } + + public has(item: BareFontInfo): boolean { + let itemId = item.getId(); + return !!this._values[itemId]; + } + + public get(item: BareFontInfo): FontInfo { + let itemId = item.getId(); + return this._values[itemId]; + } + + public put(item: BareFontInfo, value: FontInfo): void { + let itemId = item.getId(); + this._keys[itemId] = item; + this._values[itemId] = value; + } + + public remove(item: BareFontInfo): void { + let itemId = item.getId(); + delete this._keys[itemId]; + delete this._values[itemId]; + } + + public getKeys(): BareFontInfo[] { + return Object.keys(this._keys).map(id => this._keys[id]); + } + + public getValues(): FontInfo[] { + return Object.keys(this._keys).map(id => this._values[id]); + } +} + +export function readFontInfo(bareFontInfo: BareFontInfo): FontInfo { + return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo); +} + +export function restoreFontInfo(storageService: IStorageService): void { + let strStoredFontInfo = storageService.get('editorFontInfo', StorageScope.GLOBAL); + if (typeof strStoredFontInfo !== 'string') { + return; + } + let storedFontInfo: ISerializedFontInfo[] = null; + try { + storedFontInfo = JSON.parse(strStoredFontInfo); + } catch (err) { + return; + } + if (!Array.isArray(storedFontInfo)) { + return; + } + CSSBasedConfiguration.INSTANCE.restoreFontInfo(storedFontInfo); +} + +export function saveFontInfo(storageService: IStorageService): void { + let knownFontInfo = CSSBasedConfiguration.INSTANCE.saveFontInfo(); + storageService.store('editorFontInfo', JSON.stringify(knownFontInfo), StorageScope.GLOBAL); +} + +export interface ISerializedFontInfo { + readonly zoomLevel: number; + readonly fontFamily: string; + readonly fontWeight: string; + readonly fontSize: number; + readonly lineHeight: number; + readonly letterSpacing: number; + readonly isMonospace: boolean; + readonly typicalHalfwidthCharacterWidth: number; + readonly typicalFullwidthCharacterWidth: number; + readonly spaceWidth: number; + readonly maxDigitWidth: number; +} + +class CSSBasedConfiguration extends Disposable { + + public static INSTANCE = new CSSBasedConfiguration(); + + private _cache: CSSBasedConfigurationCache; + private _evictUntrustedReadingsTimeout: number; + + private _onDidChange = this._register(new Emitter()); + public onDidChange: Event = this._onDidChange.event; + + constructor() { + super(); + + this._cache = new CSSBasedConfigurationCache(); + this._evictUntrustedReadingsTimeout = -1; + } + + public dispose(): void { + if (this._evictUntrustedReadingsTimeout !== -1) { + clearTimeout(this._evictUntrustedReadingsTimeout); + this._evictUntrustedReadingsTimeout = -1; + } + super.dispose(); + } + + private _writeToCache(item: BareFontInfo, value: FontInfo): void { + this._cache.put(item, value); + + if (!value.isTrusted && this._evictUntrustedReadingsTimeout === -1) { + // Try reading again after some time + this._evictUntrustedReadingsTimeout = setTimeout(() => { + this._evictUntrustedReadingsTimeout = -1; + this._evictUntrustedReadings(); + }, 5000); + } + } + + private _evictUntrustedReadings(): void { + let values = this._cache.getValues(); + let somethingRemoved = false; + for (let i = 0, len = values.length; i < len; i++) { + let item = values[i]; + if (!item.isTrusted) { + somethingRemoved = true; + this._cache.remove(item); + } + } + if (somethingRemoved) { + this._onDidChange.fire(); + } + } + + public saveFontInfo(): ISerializedFontInfo[] { + // Only save trusted font info (that has been measured in this running instance) + return this._cache.getValues().filter(item => item.isTrusted); + } + + public restoreFontInfo(savedFontInfo: ISerializedFontInfo[]): void { + // Take all the saved font info and insert them in the cache without the trusted flag. + // The reason for this is that a font might have been installed on the OS in the meantime. + for (let i = 0, len = savedFontInfo.length; i < len; i++) { + let fontInfo = new FontInfo(savedFontInfo[i], false); + this._writeToCache(fontInfo, fontInfo); + } + } + + public readConfiguration(bareFontInfo: BareFontInfo): FontInfo { + if (!this._cache.has(bareFontInfo)) { + let readConfig = CSSBasedConfiguration._actualReadConfiguration(bareFontInfo); + + if (readConfig.typicalHalfwidthCharacterWidth <= 2 || readConfig.typicalFullwidthCharacterWidth <= 2 || readConfig.spaceWidth <= 2 || readConfig.maxDigitWidth <= 2) { + // Hey, it's Bug 14341 ... we couldn't read + readConfig = new FontInfo({ + zoomLevel: browser.getZoomLevel(), + fontFamily: readConfig.fontFamily, + fontWeight: readConfig.fontWeight, + fontSize: readConfig.fontSize, + lineHeight: readConfig.lineHeight, + letterSpacing: readConfig.letterSpacing, + isMonospace: readConfig.isMonospace, + typicalHalfwidthCharacterWidth: Math.max(readConfig.typicalHalfwidthCharacterWidth, 5), + typicalFullwidthCharacterWidth: Math.max(readConfig.typicalFullwidthCharacterWidth, 5), + spaceWidth: Math.max(readConfig.spaceWidth, 5), + maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5), + }, false); + } + + this._writeToCache(bareFontInfo, readConfig); + } + return this._cache.get(bareFontInfo); + } + + private static createRequest(chr: string, type: CharWidthRequestType, all: CharWidthRequest[], monospace: CharWidthRequest[]): CharWidthRequest { + let result = new CharWidthRequest(chr, type); + all.push(result); + if (monospace) { + monospace.push(result); + } + return result; + } + + private static _actualReadConfiguration(bareFontInfo: BareFontInfo): FontInfo { + let all: CharWidthRequest[] = []; + let monospace: CharWidthRequest[] = []; + + const typicalHalfwidthCharacter = this.createRequest('n', CharWidthRequestType.Regular, all, monospace); + const typicalFullwidthCharacter = this.createRequest('\uff4d', CharWidthRequestType.Regular, all, null); + const space = this.createRequest(' ', CharWidthRequestType.Regular, all, monospace); + const digit0 = this.createRequest('0', CharWidthRequestType.Regular, all, monospace); + const digit1 = this.createRequest('1', CharWidthRequestType.Regular, all, monospace); + const digit2 = this.createRequest('2', CharWidthRequestType.Regular, all, monospace); + const digit3 = this.createRequest('3', CharWidthRequestType.Regular, all, monospace); + const digit4 = this.createRequest('4', CharWidthRequestType.Regular, all, monospace); + const digit5 = this.createRequest('5', CharWidthRequestType.Regular, all, monospace); + const digit6 = this.createRequest('6', CharWidthRequestType.Regular, all, monospace); + const digit7 = this.createRequest('7', CharWidthRequestType.Regular, all, monospace); + const digit8 = this.createRequest('8', CharWidthRequestType.Regular, all, monospace); + const digit9 = this.createRequest('9', CharWidthRequestType.Regular, all, monospace); + + // monospace test: used for whitespace rendering + this.createRequest('→', CharWidthRequestType.Regular, all, monospace); + this.createRequest('·', CharWidthRequestType.Regular, all, monospace); + + // monospace test: some characters + this.createRequest('|', CharWidthRequestType.Regular, all, monospace); + this.createRequest('/', CharWidthRequestType.Regular, all, monospace); + this.createRequest('-', CharWidthRequestType.Regular, all, monospace); + this.createRequest('_', CharWidthRequestType.Regular, all, monospace); + this.createRequest('i', CharWidthRequestType.Regular, all, monospace); + this.createRequest('l', CharWidthRequestType.Regular, all, monospace); + this.createRequest('m', CharWidthRequestType.Regular, all, monospace); + + // monospace italic test + this.createRequest('|', CharWidthRequestType.Italic, all, monospace); + this.createRequest('_', CharWidthRequestType.Italic, all, monospace); + this.createRequest('i', CharWidthRequestType.Italic, all, monospace); + this.createRequest('l', CharWidthRequestType.Italic, all, monospace); + this.createRequest('m', CharWidthRequestType.Italic, all, monospace); + this.createRequest('n', CharWidthRequestType.Italic, all, monospace); + + // monospace bold test + this.createRequest('|', CharWidthRequestType.Bold, all, monospace); + this.createRequest('_', CharWidthRequestType.Bold, all, monospace); + this.createRequest('i', CharWidthRequestType.Bold, all, monospace); + this.createRequest('l', CharWidthRequestType.Bold, all, monospace); + this.createRequest('m', CharWidthRequestType.Bold, all, monospace); + this.createRequest('n', CharWidthRequestType.Bold, all, monospace); + + readCharWidths(bareFontInfo, all); + + const maxDigitWidth = Math.max(digit0.width, digit1.width, digit2.width, digit3.width, digit4.width, digit5.width, digit6.width, digit7.width, digit8.width, digit9.width); + + let isMonospace = true; + let referenceWidth = monospace[0].width; + for (let i = 1, len = monospace.length; i < len; i++) { + const diff = referenceWidth - monospace[i].width; + if (diff < -0.001 || diff > 0.001) { + isMonospace = false; + break; + } + } + + // let's trust the zoom level only 2s after it was changed. + const canTrustBrowserZoomLevel = (browser.getTimeSinceLastZoomLevelChanged() > 2000); + return new FontInfo({ + zoomLevel: browser.getZoomLevel(), + fontFamily: bareFontInfo.fontFamily, + fontWeight: bareFontInfo.fontWeight, + fontSize: bareFontInfo.fontSize, + lineHeight: bareFontInfo.lineHeight, + letterSpacing: bareFontInfo.letterSpacing, + isMonospace: isMonospace, + typicalHalfwidthCharacterWidth: typicalHalfwidthCharacter.width, + typicalFullwidthCharacterWidth: typicalFullwidthCharacter.width, + spaceWidth: space.width, + maxDigitWidth: maxDigitWidth + }, canTrustBrowserZoomLevel); + } +} + +export class Configuration extends CommonEditorConfiguration { + + public static applyFontInfoSlow(domNode: HTMLElement, fontInfo: BareFontInfo): void { + domNode.style.fontFamily = fontInfo.fontFamily; + domNode.style.fontWeight = fontInfo.fontWeight; + domNode.style.fontSize = fontInfo.fontSize + 'px'; + domNode.style.lineHeight = fontInfo.lineHeight + 'px'; + domNode.style.letterSpacing = fontInfo.letterSpacing + 'px'; + } + + public static applyFontInfo(domNode: FastDomNode, fontInfo: BareFontInfo): void { + domNode.setFontFamily(fontInfo.fontFamily); + domNode.setFontWeight(fontInfo.fontWeight); + domNode.setFontSize(fontInfo.fontSize); + domNode.setLineHeight(fontInfo.lineHeight); + domNode.setLetterSpacing(fontInfo.letterSpacing); + } + + private readonly _elementSizeObserver: ElementSizeObserver; + + constructor(options: IEditorOptions, referenceDomElement: HTMLElement = null) { + super(options); + + this._elementSizeObserver = this._register(new ElementSizeObserver(referenceDomElement, () => this._onReferenceDomElementSizeChanged())); + + this._register(CSSBasedConfiguration.INSTANCE.onDidChange(() => this._onCSSBasedConfigurationChanged())); + + if (this._validatedOptions.automaticLayout) { + this._elementSizeObserver.startObserving(); + } + + this._register(browser.onDidChangeZoomLevel(_ => this._recomputeOptions())); + this._register(browser.onDidChangeAccessibilitySupport(() => this._recomputeOptions())); + + this._recomputeOptions(); + } + + private _onReferenceDomElementSizeChanged(): void { + this._recomputeOptions(); + } + + private _onCSSBasedConfigurationChanged(): void { + this._recomputeOptions(); + } + + public observeReferenceElement(dimension?: IDimension): void { + this._elementSizeObserver.observe(dimension); + } + + public dispose(): void { + super.dispose(); + } + + private _getExtraEditorClassName(): string { + let extra = ''; + if (browser.isIE) { + extra += 'ie '; + } else if (browser.isFirefox) { + extra += 'ff '; + } else if (browser.isEdge) { + extra += 'edge '; + } + if (platform.isMacintosh) { + extra += 'mac '; + } + return extra; + } + + protected _getEnvConfiguration(): IEnvConfiguration { + return { + extraEditorClassName: this._getExtraEditorClassName(), + outerWidth: this._elementSizeObserver.getWidth(), + outerHeight: this._elementSizeObserver.getHeight(), + emptySelectionClipboard: browser.isWebKit, + pixelRatio: browser.getPixelRatio(), + zoomLevel: browser.getZoomLevel(), + accessibilitySupport: browser.getAccessibilitySupport() + }; + } + + protected readConfiguration(bareFontInfo: BareFontInfo): FontInfo { + return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo); + } +} diff --git a/src/vs/editor/browser/config/elementSizeObserver.ts b/src/vs/editor/browser/config/elementSizeObserver.ts new file mode 100644 index 0000000000..4f464c1086 --- /dev/null +++ b/src/vs/editor/browser/config/elementSizeObserver.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IDimension } from 'vs/editor/common/editorCommon'; + +export class ElementSizeObserver extends Disposable { + + private referenceDomElement: HTMLElement; + private measureReferenceDomElementToken: number; + private changeCallback: () => void; + private width: number; + private height: number; + + constructor(referenceDomElement: HTMLElement, changeCallback: () => void) { + super(); + this.referenceDomElement = referenceDomElement; + this.changeCallback = changeCallback; + this.measureReferenceDomElementToken = -1; + this.width = -1; + this.height = -1; + this.measureReferenceDomElement(false); + } + + public dispose(): void { + this.stopObserving(); + super.dispose(); + } + + public getWidth(): number { + return this.width; + } + + public getHeight(): number { + return this.height; + } + + public startObserving(): void { + if (this.measureReferenceDomElementToken === -1) { + this.measureReferenceDomElementToken = setInterval(() => this.measureReferenceDomElement(true), 100); + } + } + + public stopObserving(): void { + if (this.measureReferenceDomElementToken !== -1) { + clearInterval(this.measureReferenceDomElementToken); + this.measureReferenceDomElementToken = -1; + } + } + + public observe(dimension?: IDimension): void { + this.measureReferenceDomElement(true, dimension); + } + + private measureReferenceDomElement(callChangeCallback: boolean, dimension?: IDimension): void { + let observedWidth = 0; + let observedHeight = 0; + if (dimension) { + observedWidth = dimension.width; + observedHeight = dimension.height; + } else if (this.referenceDomElement) { + observedWidth = this.referenceDomElement.clientWidth; + observedHeight = this.referenceDomElement.clientHeight; + } + observedWidth = Math.max(5, observedWidth); + observedHeight = Math.max(5, observedHeight); + if (this.width !== observedWidth || this.height !== observedHeight) { + this.width = observedWidth; + this.height = observedHeight; + if (callChangeCallback) { + this.changeCallback(); + } + } + } + +} \ No newline at end of file diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts new file mode 100644 index 0000000000..a97024d666 --- /dev/null +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -0,0 +1,573 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Disposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import * as browser from 'vs/base/browser/browser'; +import * as dom from 'vs/base/browser/dom'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { MouseTarget, MouseTargetFactory, IViewZoneData } from 'vs/editor/browser/controller/mouseTarget'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { TimeoutTimer, RunOnceScheduler } from 'vs/base/common/async'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; +import { EditorMouseEventFactory, GlobalEditorMouseMoveMonitor, EditorMouseEvent, createEditorPagePosition, ClientCoordinates } from 'vs/editor/browser/editorDom'; +import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { EditorZoom } from 'vs/editor/common/config/editorZoom'; +import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { ViewController } from 'vs/editor/browser/view/viewController'; + +/** + * Merges mouse events when mouse move events are throttled + */ +function createMouseMoveEventMerger(mouseTargetFactory: MouseTargetFactory) { + return function (lastEvent: EditorMouseEvent, currentEvent: EditorMouseEvent): EditorMouseEvent { + let targetIsWidget = false; + if (mouseTargetFactory) { + targetIsWidget = mouseTargetFactory.mouseTargetIsWidget(currentEvent); + } + if (!targetIsWidget) { + currentEvent.preventDefault(); + } + return currentEvent; + }; +} + +export interface IPointerHandlerHelper { + viewDomNode: HTMLElement; + linesContentDomNode: HTMLElement; + + focusTextArea(): void; + + /** + * Get the last rendered information of the cursors. + */ + getLastViewCursorsRenderData(): IViewCursorRenderData[]; + + shouldSuppressMouseDownOnViewZone(viewZoneId: number): boolean; + shouldSuppressMouseDownOnWidget(widgetId: string): boolean; + + /** + * Decode a position from a rendered dom node + */ + getPositionFromDOMInfo(spanNode: HTMLElement, offset: number): Position; + + visibleRangeForPosition2(lineNumber: number, column: number): HorizontalRange; + getLineWidth(lineNumber: number): number; +} + +export class MouseHandler extends ViewEventHandler { + + static MOUSE_MOVE_MINIMUM_TIME = 100; // ms + + protected _context: ViewContext; + protected viewController: ViewController; + protected viewHelper: IPointerHandlerHelper; + protected mouseTargetFactory: MouseTargetFactory; + private _asyncFocus: RunOnceScheduler; + + private _mouseDownOperation: MouseDownOperation; + private lastMouseLeaveTime: number; + + constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { + super(); + + this._context = context; + this.viewController = viewController; + this.viewHelper = viewHelper; + this.mouseTargetFactory = new MouseTargetFactory(this._context, viewHelper); + + this._mouseDownOperation = this._register(new MouseDownOperation( + this._context, + this.viewController, + this.viewHelper, + (e, testEventTarget) => this._createMouseTarget(e, testEventTarget), + (e) => this._getMouseColumn(e) + )); + + this._asyncFocus = this._register(new RunOnceScheduler(() => this.viewHelper.focusTextArea(), 0)); + + this.lastMouseLeaveTime = -1; + + let mouseEvents = new EditorMouseEventFactory(this.viewHelper.viewDomNode); + + this._register(mouseEvents.onContextMenu(this.viewHelper.viewDomNode, (e) => this._onContextMenu(e, true))); + + this._register(mouseEvents.onMouseMoveThrottled(this.viewHelper.viewDomNode, + (e) => this._onMouseMove(e), + createMouseMoveEventMerger(this.mouseTargetFactory), MouseHandler.MOUSE_MOVE_MINIMUM_TIME)); + + this._register(mouseEvents.onMouseUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e))); + + this._register(mouseEvents.onMouseLeave(this.viewHelper.viewDomNode, (e) => this._onMouseLeave(e))); + + this._register(mouseEvents.onMouseDown(this.viewHelper.viewDomNode, (e) => this._onMouseDown(e))); + + let onMouseWheel = (browserEvent: MouseWheelEvent) => { + if (!this._context.configuration.editor.viewInfo.mouseWheelZoom) { + return; + } + let e = new StandardMouseWheelEvent(browserEvent); + if (e.browserEvent.ctrlKey || e.browserEvent.metaKey) { + let zoomLevel: number = EditorZoom.getZoomLevel(); + let delta = e.deltaY > 0 ? 1 : -1; + EditorZoom.setZoomLevel(zoomLevel + delta); + e.preventDefault(); + e.stopPropagation(); + } + }; + this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, 'mousewheel', onMouseWheel, true)); + this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, 'DOMMouseScroll', onMouseWheel, true)); + + this._context.addEventHandler(this); + } + + public dispose(): void { + this._context.removeEventHandler(this); + super.dispose(); + } + + // --- begin event handlers + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + this._mouseDownOperation.onCursorStateChanged(e); + return false; + } + private _isFocused = false; + public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { + this._isFocused = e.isFocused; + return false; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + this._mouseDownOperation.onScrollChanged(); + return false; + } + // --- end event handlers + + public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget { + let clientPos = new ClientCoordinates(clientX, clientY); + let pos = clientPos.toPageCoordinates(); + let editorPos = createEditorPagePosition(this.viewHelper.viewDomNode); + + if (pos.y < editorPos.y || pos.y > editorPos.y + editorPos.height || pos.x < editorPos.x || pos.x > editorPos.x + editorPos.width) { + return null; + } + + let lastViewCursorsRenderData = this.viewHelper.getLastViewCursorsRenderData(); + return this.mouseTargetFactory.createMouseTarget(lastViewCursorsRenderData, editorPos, pos, null); + } + + protected _createMouseTarget(e: EditorMouseEvent, testEventTarget: boolean): editorBrowser.IMouseTarget { + let lastViewCursorsRenderData = this.viewHelper.getLastViewCursorsRenderData(); + return this.mouseTargetFactory.createMouseTarget(lastViewCursorsRenderData, e.editorPos, e.pos, testEventTarget ? e.target : null); + } + + private _getMouseColumn(e: EditorMouseEvent): number { + return this.mouseTargetFactory.getMouseColumn(e.editorPos, e.pos); + } + + protected _onContextMenu(e: EditorMouseEvent, testEventTarget: boolean): void { + this.viewController.emitContextMenu({ + event: e, + target: this._createMouseTarget(e, testEventTarget) + }); + } + + private _onMouseMove(e: EditorMouseEvent): void { + if (this._mouseDownOperation.isActive()) { + // In selection/drag operation + return; + } + let actualMouseMoveTime = e.timestamp; + if (actualMouseMoveTime < this.lastMouseLeaveTime) { + // Due to throttling, this event occurred before the mouse left the editor, therefore ignore it. + return; + } + + this.viewController.emitMouseMove({ + event: e, + target: this._createMouseTarget(e, true) + }); + } + + private _onMouseLeave(e: EditorMouseEvent): void { + this.lastMouseLeaveTime = (new Date()).getTime(); + this.viewController.emitMouseLeave({ + event: e, + target: null + }); + } + + public _onMouseUp(e: EditorMouseEvent): void { + this.viewController.emitMouseUp({ + event: e, + target: this._createMouseTarget(e, true) + }); + } + + public _onMouseDown(e: EditorMouseEvent): void { + let t = this._createMouseTarget(e, true); + + let targetIsContent = (t.type === editorBrowser.MouseTargetType.CONTENT_TEXT || t.type === editorBrowser.MouseTargetType.CONTENT_EMPTY); + let targetIsGutter = (t.type === editorBrowser.MouseTargetType.GUTTER_GLYPH_MARGIN || t.type === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS || t.type === editorBrowser.MouseTargetType.GUTTER_LINE_DECORATIONS); + let targetIsLineNumbers = (t.type === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS); + let selectOnLineNumbers = this._context.configuration.editor.viewInfo.selectOnLineNumbers; + let targetIsViewZone = (t.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || t.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE); + let targetIsWidget = (t.type === editorBrowser.MouseTargetType.CONTENT_WIDGET); + + let shouldHandle = e.leftButton; + if (platform.isMacintosh && e.ctrlKey) { + shouldHandle = false; + } + + let focus = () => { + // In IE11, if the focus is in the browser's address bar and + // then you click in the editor, calling preventDefault() + // will not move focus properly (focus remains the address bar) + if (browser.isIE && !this._isFocused) { + this._asyncFocus.schedule(); + } else { + e.preventDefault(); + this.viewHelper.focusTextArea(); + } + }; + + if (shouldHandle && (targetIsContent || (targetIsLineNumbers && selectOnLineNumbers))) { + focus(); + this._mouseDownOperation.start(t.type, e); + + } else if (targetIsGutter) { + // Do not steal focus + e.preventDefault(); + } else if (targetIsViewZone) { + let viewZoneData = t.detail; + if (this.viewHelper.shouldSuppressMouseDownOnViewZone(viewZoneData.viewZoneId)) { + focus(); + this._mouseDownOperation.start(t.type, e); + e.preventDefault(); + } + } else if (targetIsWidget && this.viewHelper.shouldSuppressMouseDownOnWidget(t.detail)) { + focus(); + e.preventDefault(); + } + + this.viewController.emitMouseDown({ + event: e, + target: t + }); + } +} + +class MouseDownOperation extends Disposable { + + private readonly _context: ViewContext; + private readonly _viewController: ViewController; + private readonly _viewHelper: IPointerHandlerHelper; + private readonly _createMouseTarget: (e: EditorMouseEvent, testEventTarget: boolean) => editorBrowser.IMouseTarget; + private readonly _getMouseColumn: (e: EditorMouseEvent) => number; + + private readonly _mouseMoveMonitor: GlobalEditorMouseMoveMonitor; + private readonly _onScrollTimeout: TimeoutTimer; + private readonly _mouseState: MouseDownState; + + private _currentSelection: Selection; + private _isActive: boolean; + private _lastMouseEvent: EditorMouseEvent; + + constructor( + context: ViewContext, + viewController: ViewController, + viewHelper: IPointerHandlerHelper, + createMouseTarget: (e: EditorMouseEvent, testEventTarget: boolean) => editorBrowser.IMouseTarget, + getMouseColumn: (e: EditorMouseEvent) => number + ) { + super(); + this._context = context; + this._viewController = viewController; + this._viewHelper = viewHelper; + this._createMouseTarget = createMouseTarget; + this._getMouseColumn = getMouseColumn; + + this._mouseMoveMonitor = this._register(new GlobalEditorMouseMoveMonitor(this._viewHelper.viewDomNode)); + this._onScrollTimeout = this._register(new TimeoutTimer()); + this._mouseState = new MouseDownState(); + + this._currentSelection = new Selection(1, 1, 1, 1); + this._isActive = false; + this._lastMouseEvent = null; + } + + public dispose(): void { + super.dispose(); + } + + public isActive(): boolean { + return this._isActive; + } + + private _onMouseDownThenMove(e: EditorMouseEvent): void { + this._lastMouseEvent = e; + this._mouseState.setModifiers(e); + + let position = this._findMousePosition(e, true); + if (!position) { + // Ignoring because position is unknown + return; + } + + if (this._mouseState.isDragAndDrop) { + this._viewController.emitMouseDrag({ + event: e, + target: position + }); + } else { + this._dispatchMouse(position, true); + } + } + + public start(targetType: editorBrowser.MouseTargetType, e: EditorMouseEvent): void { + this._lastMouseEvent = e; + + this._mouseState.setStartedOnLineNumbers(targetType === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS); + this._mouseState.setModifiers(e); + let position = this._findMousePosition(e, true); + if (!position) { + // Ignoring because position is unknown + return; + } + + this._mouseState.trySetCount(e.detail, position.position); + + // Overwrite the detail of the MouseEvent, as it will be sent out in an event and contributions might rely on it. + e.detail = this._mouseState.count; + + if (!this._context.configuration.editor.readOnly + && this._context.configuration.editor.dragAndDrop + && !this._mouseState.altKey // we don't support multiple mouse + && e.detail < 2 // only single click on a selection can work + && !this._isActive // the mouse is not down yet + && !this._currentSelection.isEmpty() // we don't drag single cursor + && this._currentSelection.containsPosition(position.position) // single click on a selection + ) { + this._mouseState.isDragAndDrop = true; + this._isActive = true; + + this._mouseMoveMonitor.startMonitoring( + createMouseMoveEventMerger(null), + (e) => this._onMouseDownThenMove(e), + () => { + let position = this._findMousePosition(this._lastMouseEvent, true); + + this._viewController.emitMouseDrop({ + event: this._lastMouseEvent, + target: position ? this._createMouseTarget(this._lastMouseEvent, true) : null // Ignoring because position is unknown, e.g., Content View Zone + }); + + this._stop(); + } + ); + + return; + } + + this._mouseState.isDragAndDrop = false; + this._dispatchMouse(position, e.shiftKey); + + if (!this._isActive) { + this._isActive = true; + this._mouseMoveMonitor.startMonitoring( + createMouseMoveEventMerger(null), + (e) => this._onMouseDownThenMove(e), + () => this._stop() + ); + } + } + + private _stop(): void { + this._isActive = false; + this._onScrollTimeout.cancel(); + } + + public onScrollChanged(): void { + if (!this._isActive) { + return; + } + this._onScrollTimeout.setIfNotSet(() => { + let position = this._findMousePosition(this._lastMouseEvent, false); + if (!position) { + // Ignoring because position is unknown + return; + } + if (this._mouseState.isDragAndDrop) { + // Ignoring because users are dragging the text + return; + } + this._dispatchMouse(position, true); + }, 10); + } + + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): void { + this._currentSelection = e.selections[0]; + } + + private _getPositionOutsideEditor(e: EditorMouseEvent): MouseTarget { + const editorContent = e.editorPos; + const model = this._context.model; + const viewLayout = this._context.viewLayout; + + const mouseColumn = this._getMouseColumn(e); + + if (e.posy < editorContent.y) { + let aboveLineNumber = viewLayout.getLineNumberAtVerticalOffset(Math.max(viewLayout.getCurrentScrollTop() - (editorContent.y - e.posy), 0)); + return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(aboveLineNumber, 1)); + } + + if (e.posy > editorContent.y + editorContent.height) { + let belowLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + (e.posy - editorContent.y)); + return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber))); + } + + let possibleLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + (e.posy - editorContent.y)); + + if (e.posx < editorContent.x) { + return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, 1)); + } + + if (e.posx > editorContent.x + editorContent.width) { + return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber))); + } + + return null; + } + + private _findMousePosition(e: EditorMouseEvent, testEventTarget: boolean): MouseTarget { + let positionOutsideEditor = this._getPositionOutsideEditor(e); + if (positionOutsideEditor) { + return positionOutsideEditor; + } + + let t = this._createMouseTarget(e, testEventTarget); + let hintedPosition = t.position; + if (!hintedPosition) { + return null; + } + + if (t.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || t.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE) { + // Force position on view zones to go above or below depending on where selection started from + let selectionStart = new Position(this._currentSelection.selectionStartLineNumber, this._currentSelection.selectionStartColumn); + let viewZoneData = t.detail; + let positionBefore = viewZoneData.positionBefore; + let positionAfter = viewZoneData.positionAfter; + + if (positionBefore && positionAfter) { + if (positionBefore.isBefore(selectionStart)) { + return new MouseTarget(t.element, t.type, t.mouseColumn, positionBefore, null, t.detail); + } else { + return new MouseTarget(t.element, t.type, t.mouseColumn, positionAfter, null, t.detail); + } + } + } + + return t; + } + + private _dispatchMouse(position: MouseTarget, inSelectionMode: boolean): void { + this._viewController.dispatchMouse({ + position: position.position, + mouseColumn: position.mouseColumn, + startedOnLineNumbers: this._mouseState.startedOnLineNumbers, + + inSelectionMode: inSelectionMode, + mouseDownCount: this._mouseState.count, + altKey: this._mouseState.altKey, + ctrlKey: this._mouseState.ctrlKey, + metaKey: this._mouseState.metaKey, + shiftKey: this._mouseState.shiftKey, + }); + } +} + +class MouseDownState { + + private static CLEAR_MOUSE_DOWN_COUNT_TIME = 400; // ms + + private _altKey: boolean; + public get altKey(): boolean { return this._altKey; } + + private _ctrlKey: boolean; + public get ctrlKey(): boolean { return this._ctrlKey; } + + private _metaKey: boolean; + public get metaKey(): boolean { return this._metaKey; } + + private _shiftKey: boolean; + public get shiftKey(): boolean { return this._shiftKey; } + + private _startedOnLineNumbers: boolean; + public get startedOnLineNumbers(): boolean { return this._startedOnLineNumbers; } + + private _lastMouseDownPosition: Position; + private _lastMouseDownPositionEqualCount: number; + private _lastMouseDownCount: number; + private _lastSetMouseDownCountTime: number; + public isDragAndDrop: boolean; + + constructor() { + this._altKey = false; + this._ctrlKey = false; + this._metaKey = false; + this._shiftKey = false; + this._startedOnLineNumbers = false; + this._lastMouseDownPosition = null; + this._lastMouseDownPositionEqualCount = 0; + this._lastMouseDownCount = 0; + this._lastSetMouseDownCountTime = 0; + this.isDragAndDrop = false; + } + + public get count(): number { + return this._lastMouseDownCount; + } + + public setModifiers(source: EditorMouseEvent) { + this._altKey = source.altKey; + this._ctrlKey = source.ctrlKey; + this._metaKey = source.metaKey; + this._shiftKey = source.shiftKey; + } + + public setStartedOnLineNumbers(startedOnLineNumbers: boolean): void { + this._startedOnLineNumbers = startedOnLineNumbers; + } + + public trySetCount(setMouseDownCount: number, newMouseDownPosition: Position): void { + // a. Invalidate multiple clicking if too much time has passed (will be hit by IE because the detail field of mouse events contains garbage in IE10) + let currentTime = (new Date()).getTime(); + if (currentTime - this._lastSetMouseDownCountTime > MouseDownState.CLEAR_MOUSE_DOWN_COUNT_TIME) { + setMouseDownCount = 1; + } + this._lastSetMouseDownCountTime = currentTime; + + // b. Ensure that we don't jump from single click to triple click in one go (will be hit by IE because the detail field of mouse events contains garbage in IE10) + if (setMouseDownCount > this._lastMouseDownCount + 1) { + setMouseDownCount = this._lastMouseDownCount + 1; + } + + // c. Invalidate multiple clicking if the logical position is different + if (this._lastMouseDownPosition && this._lastMouseDownPosition.equals(newMouseDownPosition)) { + this._lastMouseDownPositionEqualCount++; + } else { + this._lastMouseDownPositionEqualCount = 1; + } + this._lastMouseDownPosition = newMouseDownPosition; + + // Finally set the lastMouseDownCount + this._lastMouseDownCount = Math.min(setMouseDownCount, this._lastMouseDownPositionEqualCount); + } + +} diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts new file mode 100644 index 0000000000..0d3bae8400 --- /dev/null +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -0,0 +1,923 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Position } from 'vs/editor/common/core/position'; +import { Range as EditorRange } from 'vs/editor/common/core/range'; +import { MouseTargetType, IMouseTarget } from 'vs/editor/browser/editorBrowser'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; +import { EditorMouseEvent, PageCoordinates, ClientCoordinates, EditorPagePosition } from 'vs/editor/browser/editorDom'; +import * as browser from 'vs/base/browser/browser'; +import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; +import { PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; +import { ViewLine } from 'vs/editor/browser/viewParts/lines/viewLine'; + +export interface IViewZoneData { + viewZoneId: number; + positionBefore: Position; + positionAfter: Position; + position: Position; + afterLineNumber: number; +} + +interface IETextRange { + boundingHeight: number; + boundingLeft: number; + boundingTop: number; + boundingWidth: number; + htmlText: string; + offsetLeft: number; + offsetTop: number; + text: string; + collapse(start?: boolean): void; + compareEndPoints(how: string, sourceRange: IETextRange): number; + duplicate(): IETextRange; + execCommand(cmdID: string, showUI?: boolean, value?: any): boolean; + execCommandShowHelp(cmdID: string): boolean; + expand(Unit: string): boolean; + findText(string: string, count?: number, flags?: number): boolean; + getBookmark(): string; + getBoundingClientRect(): ClientRect; + getClientRects(): ClientRectList; + inRange(range: IETextRange): boolean; + isEqual(range: IETextRange): boolean; + move(unit: string, count?: number): number; + moveEnd(unit: string, count?: number): number; + moveStart(unit: string, count?: number): number; + moveToBookmark(bookmark: string): boolean; + moveToElementText(element: Element): void; + moveToPoint(x: number, y: number): void; + parentElement(): Element; + pasteHTML(html: string): void; + queryCommandEnabled(cmdID: string): boolean; + queryCommandIndeterm(cmdID: string): boolean; + queryCommandState(cmdID: string): boolean; + queryCommandSupported(cmdID: string): boolean; + queryCommandText(cmdID: string): string; + queryCommandValue(cmdID: string): any; + scrollIntoView(fStart?: boolean): void; + select(): void; + setEndPoint(how: string, SourceRange: IETextRange): void; +} + +declare var IETextRange: { + prototype: IETextRange; + new(): IETextRange; +}; + +interface IHitTestResult { + position: Position; + hitTarget: Element; +} + +export class MouseTarget implements IMouseTarget { + + public readonly element: Element; + public readonly type: MouseTargetType; + public readonly mouseColumn: number; + public readonly position: Position; + public readonly range: EditorRange; + public readonly detail: any; + + constructor(element: Element, type: MouseTargetType, mouseColumn: number = 0, position: Position = null, range: EditorRange = null, detail: any = null) { + this.element = element; + this.type = type; + this.mouseColumn = mouseColumn; + this.position = position; + if (!range && position) { + range = new EditorRange(position.lineNumber, position.column, position.lineNumber, position.column); + } + this.range = range; + this.detail = detail; + } + + private static _typeToString(type: MouseTargetType): string { + if (type === MouseTargetType.TEXTAREA) { + return 'TEXTAREA'; + } + if (type === MouseTargetType.GUTTER_GLYPH_MARGIN) { + return 'GUTTER_GLYPH_MARGIN'; + } + if (type === MouseTargetType.GUTTER_LINE_NUMBERS) { + return 'GUTTER_LINE_NUMBERS'; + } + if (type === MouseTargetType.GUTTER_LINE_DECORATIONS) { + return 'GUTTER_LINE_DECORATIONS'; + } + if (type === MouseTargetType.GUTTER_VIEW_ZONE) { + return 'GUTTER_VIEW_ZONE'; + } + if (type === MouseTargetType.CONTENT_TEXT) { + return 'CONTENT_TEXT'; + } + if (type === MouseTargetType.CONTENT_EMPTY) { + return 'CONTENT_EMPTY'; + } + if (type === MouseTargetType.CONTENT_VIEW_ZONE) { + return 'CONTENT_VIEW_ZONE'; + } + if (type === MouseTargetType.CONTENT_WIDGET) { + return 'CONTENT_WIDGET'; + } + if (type === MouseTargetType.OVERVIEW_RULER) { + return 'OVERVIEW_RULER'; + } + if (type === MouseTargetType.SCROLLBAR) { + return 'SCROLLBAR'; + } + if (type === MouseTargetType.OVERLAY_WIDGET) { + return 'OVERLAY_WIDGET'; + } + return 'UNKNOWN'; + } + + public static toString(target: IMouseTarget): string { + return this._typeToString(target.type) + ': ' + target.position + ' - ' + target.range + ' - ' + target.detail; + } + + public toString(): string { + return MouseTarget.toString(this); + } +} + +class ElementPath { + + public static isTextArea(path: Uint8Array): boolean { + return ( + path.length === 2 + && path[0] === PartFingerprint.OverflowGuard + && path[1] === PartFingerprint.TextArea + ); + } + + public static isChildOfViewLines(path: Uint8Array): boolean { + return ( + path.length >= 4 + && path[0] === PartFingerprint.OverflowGuard + && path[3] === PartFingerprint.ViewLines + ); + } + + public static isChildOfScrollableElement(path: Uint8Array): boolean { + return ( + path.length >= 2 + && path[0] === PartFingerprint.OverflowGuard + && path[1] === PartFingerprint.ScrollableElement + ); + } + + public static isChildOfMinimap(path: Uint8Array): boolean { + return ( + path.length >= 2 + && path[0] === PartFingerprint.OverflowGuard + && path[1] === PartFingerprint.Minimap + ); + } + + public static isChildOfContentWidgets(path: Uint8Array): boolean { + return ( + path.length >= 4 + && path[0] === PartFingerprint.OverflowGuard + && path[3] === PartFingerprint.ContentWidgets + ); + } + + public static isChildOfOverflowingContentWidgets(path: Uint8Array): boolean { + return ( + path.length >= 1 + && path[0] === PartFingerprint.OverflowingContentWidgets + ); + } + + public static isChildOfOverlayWidgets(path: Uint8Array): boolean { + return ( + path.length >= 2 + && path[0] === PartFingerprint.OverflowGuard + && path[1] === PartFingerprint.OverlayWidgets + ); + } +} + +class HitTestContext { + + public readonly model: IViewModel; + public readonly layoutInfo: EditorLayoutInfo; + public readonly viewDomNode: HTMLElement; + public readonly lineHeight: number; + public readonly typicalHalfwidthCharacterWidth: number; + public readonly lastViewCursorsRenderData: IViewCursorRenderData[]; + + private readonly _context: ViewContext; + private readonly _viewHelper: IPointerHandlerHelper; + + constructor(context: ViewContext, viewHelper: IPointerHandlerHelper, lastViewCursorsRenderData: IViewCursorRenderData[]) { + this.model = context.model; + this.layoutInfo = context.configuration.editor.layoutInfo; + this.viewDomNode = viewHelper.viewDomNode; + this.lineHeight = context.configuration.editor.lineHeight; + this.typicalHalfwidthCharacterWidth = context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth; + this.lastViewCursorsRenderData = lastViewCursorsRenderData; + this._context = context; + this._viewHelper = viewHelper; + } + + public getZoneAtCoord(mouseVerticalOffset: number): IViewZoneData { + // The target is either a view zone or the empty space after the last view-line + let viewZoneWhitespace = this._context.viewLayout.getWhitespaceAtVerticalOffset(mouseVerticalOffset); + + if (viewZoneWhitespace) { + let viewZoneMiddle = viewZoneWhitespace.verticalOffset + viewZoneWhitespace.height / 2, + lineCount = this._context.model.getLineCount(), + positionBefore: Position = null, + position: Position, + positionAfter: Position = null; + + if (viewZoneWhitespace.afterLineNumber !== lineCount) { + // There are more lines after this view zone + positionAfter = new Position(viewZoneWhitespace.afterLineNumber + 1, 1); + } + if (viewZoneWhitespace.afterLineNumber > 0) { + // There are more lines above this view zone + positionBefore = new Position(viewZoneWhitespace.afterLineNumber, this._context.model.getLineMaxColumn(viewZoneWhitespace.afterLineNumber)); + } + + if (positionAfter === null) { + position = positionBefore; + } else if (positionBefore === null) { + position = positionAfter; + } else if (mouseVerticalOffset < viewZoneMiddle) { + position = positionBefore; + } else { + position = positionAfter; + } + + return { + viewZoneId: viewZoneWhitespace.id, + afterLineNumber: viewZoneWhitespace.afterLineNumber, + positionBefore: positionBefore, + positionAfter: positionAfter, + position: position + }; + } + return null; + } + + public getFullLineRangeAtCoord(mouseVerticalOffset: number): { range: EditorRange; isAfterLines: boolean; } { + if (this._context.viewLayout.isAfterLines(mouseVerticalOffset)) { + // Below the last line + let lineNumber = this._context.model.getLineCount(); + let maxLineColumn = this._context.model.getLineMaxColumn(lineNumber); + return { + range: new EditorRange(lineNumber, maxLineColumn, lineNumber, maxLineColumn), + isAfterLines: true + }; + } + + let lineNumber = this._context.viewLayout.getLineNumberAtVerticalOffset(mouseVerticalOffset); + let maxLineColumn = this._context.model.getLineMaxColumn(lineNumber); + return { + range: new EditorRange(lineNumber, 1, lineNumber, maxLineColumn), + isAfterLines: false + }; + } + + public getLineNumberAtVerticalOffset(mouseVerticalOffset: number): number { + return this._context.viewLayout.getLineNumberAtVerticalOffset(mouseVerticalOffset); + } + + public isAfterLines(mouseVerticalOffset: number): boolean { + return this._context.viewLayout.isAfterLines(mouseVerticalOffset); + } + + public getVerticalOffsetForLineNumber(lineNumber: number): number { + return this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber); + } + + public findAttribute(element: Element, attr: string): string { + return HitTestContext._findAttribute(element, attr, this._viewHelper.viewDomNode); + } + + private static _findAttribute(element: Element, attr: string, stopAt: Element): string { + while (element && element !== document.body) { + if (element.hasAttribute && element.hasAttribute(attr)) { + return element.getAttribute(attr); + } + if (element === stopAt) { + return null; + } + element = element.parentNode; + } + return null; + } + + public getLineWidth(lineNumber: number): number { + return this._viewHelper.getLineWidth(lineNumber); + } + + public visibleRangeForPosition2(lineNumber: number, column: number) { + return this._viewHelper.visibleRangeForPosition2(lineNumber, column); + } + + public getPositionFromDOMInfo(spanNode: HTMLElement, offset: number): Position { + return this._viewHelper.getPositionFromDOMInfo(spanNode, offset); + } + + public getCurrentScrollTop(): number { + return this._context.viewLayout.getCurrentScrollTop(); + } + + public getCurrentScrollLeft(): number { + return this._context.viewLayout.getCurrentScrollLeft(); + } +} + +abstract class BareHitTestRequest { + + public readonly editorPos: EditorPagePosition; + public readonly pos: PageCoordinates; + public readonly mouseVerticalOffset: number; + public readonly isInMarginArea: boolean; + public readonly isInContentArea: boolean; + public readonly mouseContentHorizontalOffset: number; + + protected readonly mouseColumn: number; + + constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates) { + this.editorPos = editorPos; + this.pos = pos; + + this.mouseVerticalOffset = Math.max(0, ctx.getCurrentScrollTop() + pos.y - editorPos.y); + this.mouseContentHorizontalOffset = ctx.getCurrentScrollLeft() + pos.x - editorPos.x - ctx.layoutInfo.contentLeft; + this.isInMarginArea = (pos.x - editorPos.x < ctx.layoutInfo.contentLeft); + this.isInContentArea = !this.isInMarginArea; + this.mouseColumn = Math.max(0, MouseTargetFactory._getMouseColumn(this.mouseContentHorizontalOffset, ctx.typicalHalfwidthCharacterWidth)); + } +} + +class HitTestRequest extends BareHitTestRequest { + private readonly _ctx: HitTestContext; + public readonly target: Element; + public readonly targetPath: Uint8Array; + + constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, target: Element) { + super(ctx, editorPos, pos); + this._ctx = ctx; + + if (target) { + this.target = target; + this.targetPath = PartFingerprints.collect(target, ctx.viewDomNode); + } else { + this.target = null; + this.targetPath = new Uint8Array(0); + } + } + + public toString(): string { + return `pos(${this.pos.x},${this.pos.y}), editorPos(${this.editorPos.x},${this.editorPos.y}), mouseVerticalOffset: ${this.mouseVerticalOffset}, mouseContentHorizontalOffset: ${this.mouseContentHorizontalOffset}\n\ttarget: ${this.target ? (this.target).outerHTML : null}`; + } + + public fulfill(type: MouseTargetType, position: Position = null, range: EditorRange = null, detail: any = null): MouseTarget { + return new MouseTarget(this.target, type, this.mouseColumn, position, range, detail); + } + + public withTarget(target: Element): HitTestRequest { + return new HitTestRequest(this._ctx, this.editorPos, this.pos, target); + } +} + +export class MouseTargetFactory { + + private _context: ViewContext; + private _viewHelper: IPointerHandlerHelper; + + constructor(context: ViewContext, viewHelper: IPointerHandlerHelper) { + this._context = context; + this._viewHelper = viewHelper; + } + + public mouseTargetIsWidget(e: EditorMouseEvent): boolean { + let t = e.target; + let path = PartFingerprints.collect(t, this._viewHelper.viewDomNode); + + // Is it a content widget? + if (ElementPath.isChildOfContentWidgets(path) || ElementPath.isChildOfOverflowingContentWidgets(path)) { + return true; + } + + // Is it an overlay widget? + if (ElementPath.isChildOfOverlayWidgets(path)) { + return true; + } + + return false; + } + + public createMouseTarget(lastViewCursorsRenderData: IViewCursorRenderData[], editorPos: EditorPagePosition, pos: PageCoordinates, target: HTMLElement): IMouseTarget { + const ctx = new HitTestContext(this._context, this._viewHelper, lastViewCursorsRenderData); + const request = new HitTestRequest(ctx, editorPos, pos, target); + try { + let r = MouseTargetFactory._createMouseTarget(ctx, request, false); + // console.log(r.toString()); + return r; + } catch (err) { + // console.log(err); + return request.fulfill(MouseTargetType.UNKNOWN); + } + } + + private static _createMouseTarget(ctx: HitTestContext, request: HitTestRequest, domHitTestExecuted: boolean): MouseTarget { + + // console.log(`${domHitTestExecuted ? '=>' : ''}CAME IN REQUEST: ${request}`); + + // First ensure the request has a target + if (request.target === null) { + if (domHitTestExecuted) { + // Still no target... and we have already executed hit test... + return request.fulfill(MouseTargetType.UNKNOWN); + } + + const hitTestResult = MouseTargetFactory._doHitTest(ctx, request); + + if (hitTestResult.position) { + return MouseTargetFactory.createMouseTargetFromHitTestPosition(ctx, request, hitTestResult.position.lineNumber, hitTestResult.position.column); + } + + return this._createMouseTarget(ctx, request.withTarget(hitTestResult.hitTarget), true); + } + + let result: MouseTarget = null; + + result = result || MouseTargetFactory._hitTestContentWidget(ctx, request); + result = result || MouseTargetFactory._hitTestOverlayWidget(ctx, request); + result = result || MouseTargetFactory._hitTestMinimap(ctx, request); + result = result || MouseTargetFactory._hitTestScrollbarSlider(ctx, request); + result = result || MouseTargetFactory._hitTestViewZone(ctx, request); + result = result || MouseTargetFactory._hitTestMargin(ctx, request); + result = result || MouseTargetFactory._hitTestViewCursor(ctx, request); + result = result || MouseTargetFactory._hitTestTextArea(ctx, request); + result = result || MouseTargetFactory._hitTestViewLines(ctx, request, domHitTestExecuted); + result = result || MouseTargetFactory._hitTestScrollbar(ctx, request); + + return (result || request.fulfill(MouseTargetType.UNKNOWN)); + } + + private static _hitTestContentWidget(ctx: HitTestContext, request: HitTestRequest): MouseTarget { + // Is it a content widget? + if (ElementPath.isChildOfContentWidgets(request.targetPath) || ElementPath.isChildOfOverflowingContentWidgets(request.targetPath)) { + let widgetId = ctx.findAttribute(request.target, 'widgetId'); + if (widgetId) { + return request.fulfill(MouseTargetType.CONTENT_WIDGET, null, null, widgetId); + } else { + return request.fulfill(MouseTargetType.UNKNOWN); + } + } + return null; + } + + private static _hitTestOverlayWidget(ctx: HitTestContext, request: HitTestRequest): MouseTarget { + // Is it an overlay widget? + if (ElementPath.isChildOfOverlayWidgets(request.targetPath)) { + let widgetId = ctx.findAttribute(request.target, 'widgetId'); + if (widgetId) { + return request.fulfill(MouseTargetType.OVERLAY_WIDGET, null, null, widgetId); + } else { + return request.fulfill(MouseTargetType.UNKNOWN); + } + } + return null; + } + + private static _hitTestViewCursor(ctx: HitTestContext, request: HitTestRequest): MouseTarget { + + if (request.target) { + // Check if we've hit a painted cursor + const lastViewCursorsRenderData = ctx.lastViewCursorsRenderData; + + for (let i = 0, len = lastViewCursorsRenderData.length; i < len; i++) { + const d = lastViewCursorsRenderData[i]; + + if (request.target === d.domNode) { + return request.fulfill(MouseTargetType.CONTENT_TEXT, d.position); + } + } + } + + if (request.isInContentArea) { + // Edge has a bug when hit-testing the exact position of a cursor, + // instead of returning the correct dom node, it returns the + // first or last rendered view line dom node, therefore help it out + // and first check if we are on top of a cursor + + const lastViewCursorsRenderData = ctx.lastViewCursorsRenderData; + const mouseContentHorizontalOffset = request.mouseContentHorizontalOffset; + const mouseVerticalOffset = request.mouseVerticalOffset; + + for (let i = 0, len = lastViewCursorsRenderData.length; i < len; i++) { + const d = lastViewCursorsRenderData[i]; + + if (mouseContentHorizontalOffset < d.contentLeft) { + // mouse position is to the left of the cursor + continue; + } + if (mouseContentHorizontalOffset > d.contentLeft + d.width) { + // mouse position is to the right of the cursor + continue; + } + + const cursorVerticalOffset = ctx.getVerticalOffsetForLineNumber(d.position.lineNumber); + + if ( + cursorVerticalOffset <= mouseVerticalOffset + && mouseVerticalOffset <= cursorVerticalOffset + d.height + ) { + return request.fulfill(MouseTargetType.CONTENT_TEXT, d.position); + } + } + } + + return null; + } + + private static _hitTestViewZone(ctx: HitTestContext, request: HitTestRequest): MouseTarget { + let viewZoneData = ctx.getZoneAtCoord(request.mouseVerticalOffset); + if (viewZoneData) { + let mouseTargetType = (request.isInContentArea ? MouseTargetType.CONTENT_VIEW_ZONE : MouseTargetType.GUTTER_VIEW_ZONE); + return request.fulfill(mouseTargetType, viewZoneData.position, null, viewZoneData); + } + + return null; + } + + private static _hitTestTextArea(ctx: HitTestContext, request: HitTestRequest): MouseTarget { + // Is it the textarea? + if (ElementPath.isTextArea(request.targetPath)) { + return request.fulfill(MouseTargetType.TEXTAREA); + } + return null; + } + + private static _hitTestMargin(ctx: HitTestContext, request: HitTestRequest): MouseTarget { + if (request.isInMarginArea) { + let res = ctx.getFullLineRangeAtCoord(request.mouseVerticalOffset); + let pos = res.range.getStartPosition(); + + let offset = Math.abs(request.pos.x - request.editorPos.x); + if (offset <= ctx.layoutInfo.glyphMarginWidth) { + // On the glyph margin + return request.fulfill(MouseTargetType.GUTTER_GLYPH_MARGIN, pos, res.range, res.isAfterLines); + } + offset -= ctx.layoutInfo.glyphMarginWidth; + + if (offset <= ctx.layoutInfo.lineNumbersWidth) { + // On the line numbers + return request.fulfill(MouseTargetType.GUTTER_LINE_NUMBERS, pos, res.range, res.isAfterLines); + } + offset -= ctx.layoutInfo.lineNumbersWidth; + + // On the line decorations + return request.fulfill(MouseTargetType.GUTTER_LINE_DECORATIONS, pos, res.range, res.isAfterLines); + } + return null; + } + + private static _hitTestViewLines(ctx: HitTestContext, request: HitTestRequest, domHitTestExecuted: boolean): MouseTarget { + if (!ElementPath.isChildOfViewLines(request.targetPath)) { + return null; + } + + // Check if it is below any lines and any view zones + if (ctx.isAfterLines(request.mouseVerticalOffset)) { + // This most likely indicates it happened after the last view-line + const lineCount = ctx.model.getLineCount(); + const maxLineColumn = ctx.model.getLineMaxColumn(lineCount); + return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(lineCount, maxLineColumn)); + } + + if (domHitTestExecuted) { + // We have already executed hit test... + return request.fulfill(MouseTargetType.UNKNOWN); + } + + const hitTestResult = MouseTargetFactory._doHitTest(ctx, request); + + if (hitTestResult.position) { + return MouseTargetFactory.createMouseTargetFromHitTestPosition(ctx, request, hitTestResult.position.lineNumber, hitTestResult.position.column); + } + + return this._createMouseTarget(ctx, request.withTarget(hitTestResult.hitTarget), true); + } + + private static _hitTestMinimap(ctx: HitTestContext, request: HitTestRequest): MouseTarget { + if (ElementPath.isChildOfMinimap(request.targetPath)) { + const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); + const maxColumn = ctx.model.getLineMaxColumn(possibleLineNumber); + return request.fulfill(MouseTargetType.SCROLLBAR, new Position(possibleLineNumber, maxColumn)); + } + return null; + } + + private static _hitTestScrollbarSlider(ctx: HitTestContext, request: HitTestRequest): MouseTarget { + if (ElementPath.isChildOfScrollableElement(request.targetPath)) { + if (request.target && request.target.nodeType === 1) { + let className = request.target.className; + if (className && /\b(slider|scrollbar)\b/.test(className)) { + const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); + const maxColumn = ctx.model.getLineMaxColumn(possibleLineNumber); + return request.fulfill(MouseTargetType.SCROLLBAR, new Position(possibleLineNumber, maxColumn)); + } + } + } + return null; + } + + private static _hitTestScrollbar(ctx: HitTestContext, request: HitTestRequest): MouseTarget { + // Is it the overview ruler? + // Is it a child of the scrollable element? + if (ElementPath.isChildOfScrollableElement(request.targetPath)) { + const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); + const maxColumn = ctx.model.getLineMaxColumn(possibleLineNumber); + return request.fulfill(MouseTargetType.SCROLLBAR, new Position(possibleLineNumber, maxColumn)); + } + + return null; + } + + public getMouseColumn(editorPos: EditorPagePosition, pos: PageCoordinates): number { + let layoutInfo = this._context.configuration.editor.layoutInfo; + let mouseContentHorizontalOffset = this._context.viewLayout.getCurrentScrollLeft() + pos.x - editorPos.x - layoutInfo.contentLeft; + return MouseTargetFactory._getMouseColumn(mouseContentHorizontalOffset, this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth); + } + + public static _getMouseColumn(mouseContentHorizontalOffset: number, typicalHalfwidthCharacterWidth: number): number { + if (mouseContentHorizontalOffset < 0) { + return 1; + } + let chars = Math.round(mouseContentHorizontalOffset / typicalHalfwidthCharacterWidth); + return (chars + 1); + } + + private static createMouseTargetFromHitTestPosition(ctx: HitTestContext, request: HitTestRequest, lineNumber: number, column: number): MouseTarget { + let pos = new Position(lineNumber, column); + + let lineWidth = ctx.getLineWidth(lineNumber); + + if (request.mouseContentHorizontalOffset > lineWidth) { + if (browser.isEdge && pos.column === 1) { + // See https://github.com/Microsoft/vscode/issues/10875 + return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(lineNumber, ctx.model.getLineMaxColumn(lineNumber))); + } + return request.fulfill(MouseTargetType.CONTENT_EMPTY, pos); + } + + let visibleRange = ctx.visibleRangeForPosition2(lineNumber, column); + + if (!visibleRange) { + return request.fulfill(MouseTargetType.UNKNOWN, pos); + } + + let columnHorizontalOffset = visibleRange.left; + + if (request.mouseContentHorizontalOffset === columnHorizontalOffset) { + return request.fulfill(MouseTargetType.CONTENT_TEXT, pos); + } + + let mouseIsBetween: boolean; + if (column > 1) { + let prevColumnHorizontalOffset = visibleRange.left; + mouseIsBetween = false; + mouseIsBetween = mouseIsBetween || (prevColumnHorizontalOffset < request.mouseContentHorizontalOffset && request.mouseContentHorizontalOffset < columnHorizontalOffset); // LTR case + mouseIsBetween = mouseIsBetween || (columnHorizontalOffset < request.mouseContentHorizontalOffset && request.mouseContentHorizontalOffset < prevColumnHorizontalOffset); // RTL case + if (mouseIsBetween) { + let rng = new EditorRange(lineNumber, column, lineNumber, column - 1); + return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, rng); + } + } + + let lineMaxColumn = ctx.model.getLineMaxColumn(lineNumber); + if (column < lineMaxColumn) { + let nextColumnVisibleRange = ctx.visibleRangeForPosition2(lineNumber, column + 1); + if (nextColumnVisibleRange) { + let nextColumnHorizontalOffset = nextColumnVisibleRange.left; + mouseIsBetween = false; + mouseIsBetween = mouseIsBetween || (columnHorizontalOffset < request.mouseContentHorizontalOffset && request.mouseContentHorizontalOffset < nextColumnHorizontalOffset); // LTR case + mouseIsBetween = mouseIsBetween || (nextColumnHorizontalOffset < request.mouseContentHorizontalOffset && request.mouseContentHorizontalOffset < columnHorizontalOffset); // RTL case + if (mouseIsBetween) { + let rng = new EditorRange(lineNumber, column, lineNumber, column + 1); + return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, rng); + } + } + } + + return request.fulfill(MouseTargetType.CONTENT_TEXT, pos); + } + + /** + * Most probably WebKit browsers and Edge + */ + private static _doHitTestWithCaretRangeFromPoint(ctx: HitTestContext, request: BareHitTestRequest): IHitTestResult { + + // In Chrome, especially on Linux it is possible to click between lines, + // so try to adjust the `hity` below so that it lands in the center of a line + let lineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); + let lineVerticalOffset = ctx.getVerticalOffsetForLineNumber(lineNumber); + let lineCenteredVerticalOffset = lineVerticalOffset + Math.floor(ctx.lineHeight / 2); + let adjustedPageY = request.pos.y + (lineCenteredVerticalOffset - request.mouseVerticalOffset); + + if (adjustedPageY <= request.editorPos.y) { + adjustedPageY = request.editorPos.y + 1; + } + if (adjustedPageY >= request.editorPos.y + ctx.layoutInfo.height) { + adjustedPageY = request.editorPos.y + ctx.layoutInfo.height - 1; + } + + let adjustedPage = new PageCoordinates(request.pos.x, adjustedPageY); + + let r = this._actualDoHitTestWithCaretRangeFromPoint(ctx, adjustedPage.toClientCoordinates()); + if (r.position) { + return r; + } + + // Also try to hit test without the adjustment (for the edge cases that we are near the top or bottom) + return this._actualDoHitTestWithCaretRangeFromPoint(ctx, request.pos.toClientCoordinates()); + } + + private static _actualDoHitTestWithCaretRangeFromPoint(ctx: HitTestContext, coords: ClientCoordinates): IHitTestResult { + + let range: Range = document.caretRangeFromPoint(coords.clientX, coords.clientY); + + if (!range || !range.startContainer) { + return { + position: null, + hitTarget: null + }; + } + + // Chrome always hits a TEXT_NODE, while Edge sometimes hits a token span + let startContainer = range.startContainer; + let hitTarget: HTMLElement; + + if (startContainer.nodeType === startContainer.TEXT_NODE) { + // startContainer is expected to be the token text + let parent1 = startContainer.parentNode; // expected to be the token span + let parent2 = parent1 ? parent1.parentNode : null; // expected to be the view line container span + let parent3 = parent2 ? parent2.parentNode : null; // expected to be the view line div + let parent3ClassName = parent3 && parent3.nodeType === parent3.ELEMENT_NODE ? (parent3).className : null; + + if (parent3ClassName === ViewLine.CLASS_NAME) { + let p = ctx.getPositionFromDOMInfo(parent1, range.startOffset); + return { + position: p, + hitTarget: null + }; + } else { + hitTarget = startContainer.parentNode; + } + } else if (startContainer.nodeType === startContainer.ELEMENT_NODE) { + // startContainer is expected to be the token span + let parent1 = startContainer.parentNode; // expected to be the view line container span + let parent2 = parent1 ? parent1.parentNode : null; // expected to be the view line div + let parent2ClassName = parent2 && parent2.nodeType === parent2.ELEMENT_NODE ? (parent2).className : null; + + if (parent2ClassName === ViewLine.CLASS_NAME) { + let p = ctx.getPositionFromDOMInfo(startContainer, (startContainer).textContent.length); + return { + position: p, + hitTarget: null + }; + } else { + hitTarget = startContainer; + } + } + + return { + position: null, + hitTarget: hitTarget + }; + } + + /** + * Most probably Gecko + */ + private static _doHitTestWithCaretPositionFromPoint(ctx: HitTestContext, coords: ClientCoordinates): IHitTestResult { + let hitResult: { offsetNode: Node; offset: number; } = (document).caretPositionFromPoint(coords.clientX, coords.clientY); + + if (hitResult.offsetNode.nodeType === hitResult.offsetNode.TEXT_NODE) { + // offsetNode is expected to be the token text + let parent1 = hitResult.offsetNode.parentNode; // expected to be the token span + let parent2 = parent1 ? parent1.parentNode : null; // expected to be the view line container span + let parent3 = parent2 ? parent2.parentNode : null; // expected to be the view line div + let parent3ClassName = parent3 && parent3.nodeType === parent3.ELEMENT_NODE ? (parent3).className : null; + + if (parent3ClassName === ViewLine.CLASS_NAME) { + let p = ctx.getPositionFromDOMInfo(hitResult.offsetNode.parentNode, hitResult.offset); + return { + position: p, + hitTarget: null + }; + } else { + return { + position: null, + hitTarget: hitResult.offsetNode.parentNode + }; + } + } + + return { + position: null, + hitTarget: hitResult.offsetNode + }; + } + + /** + * Most probably IE + */ + private static _doHitTestWithMoveToPoint(ctx: HitTestContext, coords: ClientCoordinates): IHitTestResult { + let resultPosition: Position = null; + let resultHitTarget: Element = null; + + let textRange: IETextRange = (document.body).createTextRange(); + try { + textRange.moveToPoint(coords.clientX, coords.clientY); + } catch (err) { + return { + position: null, + hitTarget: null + }; + } + + textRange.collapse(true); + + // Now, let's do our best to figure out what we hit :) + let parentElement = textRange ? textRange.parentElement() : null; + let parent1 = parentElement ? parentElement.parentNode : null; + let parent2 = parent1 ? parent1.parentNode : null; + + let parent2ClassName = parent2 && parent2.nodeType === parent2.ELEMENT_NODE ? (parent2).className : ''; + + if (parent2ClassName === ViewLine.CLASS_NAME) { + let rangeToContainEntireSpan = textRange.duplicate(); + rangeToContainEntireSpan.moveToElementText(parentElement); + rangeToContainEntireSpan.setEndPoint('EndToStart', textRange); + + resultPosition = ctx.getPositionFromDOMInfo(parentElement, rangeToContainEntireSpan.text.length); + // Move range out of the span node, IE doesn't like having many ranges in + // the same spot and will act badly for lines containing dashes ('-') + rangeToContainEntireSpan.moveToElementText(ctx.viewDomNode); + } else { + // Looks like we've hit the hover or something foreign + resultHitTarget = parentElement; + } + + // Move range out of the span node, IE doesn't like having many ranges in + // the same spot and will act badly for lines containing dashes ('-') + textRange.moveToElementText(ctx.viewDomNode); + + return { + position: resultPosition, + hitTarget: resultHitTarget + }; + } + + private static _doHitTest(ctx: HitTestContext, request: BareHitTestRequest): IHitTestResult { + // State of the art (18.10.2012): + // The spec says browsers should support document.caretPositionFromPoint, but nobody implemented it (http://dev.w3.org/csswg/cssom-view/) + // Gecko: + // - they tried to implement it once, but failed: https://bugzilla.mozilla.org/show_bug.cgi?id=654352 + // - however, they do give out rangeParent/rangeOffset properties on mouse events + // Webkit: + // - they have implemented a previous version of the spec which was using document.caretRangeFromPoint + // IE: + // - they have a proprietary method on ranges, moveToPoint: https://msdn.microsoft.com/en-us/library/ie/ms536632(v=vs.85).aspx + + // 24.08.2016: Edge has added WebKit's document.caretRangeFromPoint, but it is quite buggy + // - when hit testing the cursor it returns the first or the last line in the viewport + // - it inconsistently hits text nodes or span nodes, while WebKit only hits text nodes + // - when toggling render whitespace on, and hit testing in the empty content after a line, it always hits offset 0 of the first span of the line + + // Thank you browsers for making this so 'easy' :) + + if (document.caretRangeFromPoint) { + + return this._doHitTestWithCaretRangeFromPoint(ctx, request); + + } else if ((document).caretPositionFromPoint) { + + return this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates()); + + } else if ((document.body).createTextRange) { + + return this._doHitTestWithMoveToPoint(ctx, request.pos.toClientCoordinates()); + + } + + return { + position: null, + hitTarget: null + }; + } +} \ No newline at end of file diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts new file mode 100644 index 0000000000..5956bd2ede --- /dev/null +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -0,0 +1,248 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import * as dom from 'vs/base/browser/dom'; +import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; +import { MouseHandler, IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; +import { IMouseTarget } from 'vs/editor/browser/editorBrowser'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { EditorMouseEvent } from 'vs/editor/browser/editorDom'; +import { ViewController } from 'vs/editor/browser/view/viewController'; + +interface IThrottledGestureEvent { + translationX: number; + translationY: number; +} + +function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent, currentEvent: MSGestureEvent): IThrottledGestureEvent { + let r = { + translationY: currentEvent.translationY, + translationX: currentEvent.translationX + }; + if (lastEvent) { + r.translationY += lastEvent.translationY; + r.translationX += lastEvent.translationX; + } + return r; +}; + +/** + * Basically IE10 and IE11 + */ +class MsPointerHandler extends MouseHandler implements IDisposable { + + private _lastPointerType: string; + private _installGestureHandlerTimeout: number; + + constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { + super(context, viewController, viewHelper); + + this.viewHelper.linesContentDomNode.style.msTouchAction = 'none'; + this.viewHelper.linesContentDomNode.style.msContentZooming = 'none'; + + // TODO@Alex -> this expects that the view is added in 100 ms, might not be the case + // This handler should be added when the dom node is in the dom tree + this._installGestureHandlerTimeout = window.setTimeout(() => { + this._installGestureHandlerTimeout = -1; + if ((window).MSGesture) { + let touchGesture = new MSGesture(); + let penGesture = new MSGesture(); + touchGesture.target = this.viewHelper.linesContentDomNode; + penGesture.target = this.viewHelper.linesContentDomNode; + this.viewHelper.linesContentDomNode.addEventListener('MSPointerDown', (e: MSPointerEvent) => { + // Circumvent IE11 breaking change in e.pointerType & TypeScript's stale definitions + let pointerType = e.pointerType; + if (pointerType === ((e).MSPOINTER_TYPE_MOUSE || 'mouse')) { + this._lastPointerType = 'mouse'; + return; + } else if (pointerType === ((e).MSPOINTER_TYPE_TOUCH || 'touch')) { + this._lastPointerType = 'touch'; + touchGesture.addPointer(e.pointerId); + } else { + this._lastPointerType = 'pen'; + penGesture.addPointer(e.pointerId); + } + }); + this._register(dom.addDisposableThrottledListener(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger)); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'MSGestureTap', (e) => this._onCaptureGestureTap(e), true)); + } + }, 100); + this._lastPointerType = 'mouse'; + } + + public _onMouseDown(e: EditorMouseEvent): void { + if (this._lastPointerType === 'mouse') { + super._onMouseDown(e); + } + } + + private _onCaptureGestureTap(rawEvent: MSGestureEvent): void { + let e = new EditorMouseEvent(rawEvent, this.viewHelper.viewDomNode); + let t = this._createMouseTarget(e, false); + if (t.position) { + this.viewController.moveTo(t.position); + } + // IE does not want to focus when coming in from the browser's address bar + if ((e.browserEvent).fromElement) { + e.preventDefault(); + this.viewHelper.focusTextArea(); + } else { + // TODO@Alex -> cancel this is focus is lost + setTimeout(() => { + this.viewHelper.focusTextArea(); + }); + } + } + + private _onGestureChange(e: IThrottledGestureEvent): void { + this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); + } + + public dispose(): void { + window.clearTimeout(this._installGestureHandlerTimeout); + super.dispose(); + } +} + +/** + * Basically Edge but should be modified to handle any pointerEnabled, even without support of MSGesture + */ +class StandardPointerHandler extends MouseHandler implements IDisposable { + + private _lastPointerType: string; + private _installGestureHandlerTimeout: number; + + constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { + super(context, viewController, viewHelper); + + this.viewHelper.linesContentDomNode.style.touchAction = 'none'; + + // TODO@Alex -> this expects that the view is added in 100 ms, might not be the case + // This handler should be added when the dom node is in the dom tree + this._installGestureHandlerTimeout = window.setTimeout(() => { + this._installGestureHandlerTimeout = -1; + + // TODO@Alex: replace the usage of MSGesture here with something that works across all browsers + if ((window).MSGesture) { + let touchGesture = new MSGesture(); + let penGesture = new MSGesture(); + touchGesture.target = this.viewHelper.linesContentDomNode; + penGesture.target = this.viewHelper.linesContentDomNode; + this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: MSPointerEvent) => { + let pointerType = e.pointerType; + if (pointerType === 'mouse') { + this._lastPointerType = 'mouse'; + return; + } else if (pointerType === 'touch') { + this._lastPointerType = 'touch'; + touchGesture.addPointer(e.pointerId); + } else { + this._lastPointerType = 'pen'; + penGesture.addPointer(e.pointerId); + } + }); + this._register(dom.addDisposableThrottledListener(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger)); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'MSGestureTap', (e) => this._onCaptureGestureTap(e), true)); + } + }, 100); + this._lastPointerType = 'mouse'; + } + + public _onMouseDown(e: EditorMouseEvent): void { + if (this._lastPointerType === 'mouse') { + super._onMouseDown(e); + } + } + + private _onCaptureGestureTap(rawEvent: MSGestureEvent): void { + let e = new EditorMouseEvent(rawEvent, this.viewHelper.viewDomNode); + let t = this._createMouseTarget(e, false); + if (t.position) { + this.viewController.moveTo(t.position); + } + // IE does not want to focus when coming in from the browser's address bar + if ((e.browserEvent).fromElement) { + e.preventDefault(); + this.viewHelper.focusTextArea(); + } else { + // TODO@Alex -> cancel this is focus is lost + setTimeout(() => { + this.viewHelper.focusTextArea(); + }); + } + } + + private _onGestureChange(e: IThrottledGestureEvent): void { + this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); + } + + public dispose(): void { + window.clearTimeout(this._installGestureHandlerTimeout); + super.dispose(); + } +} + +class TouchHandler extends MouseHandler { + + private gesture: Gesture; + + constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { + super(context, viewController, viewHelper); + + this.gesture = new Gesture(this.viewHelper.linesContentDomNode); + + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e))); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e))); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Contextmenu, (e: MouseEvent) => this._onContextMenu(new EditorMouseEvent(e, this.viewHelper.viewDomNode), false))); + + } + + public dispose(): void { + this.gesture.dispose(); + super.dispose(); + } + + private onTap(event: GestureEvent): void { + event.preventDefault(); + + this.viewHelper.focusTextArea(); + + let target = this._createMouseTarget(new EditorMouseEvent(event, this.viewHelper.viewDomNode), false); + + if (target.position) { + this.viewController.moveTo(target.position); + } + } + + private onChange(e: GestureEvent): void { + this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); + } +} + +export class PointerHandler implements IDisposable { + private handler: MouseHandler; + + constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { + if (window.navigator.msPointerEnabled) { + this.handler = new MsPointerHandler(context, viewController, viewHelper); + } else if ((window).TouchEvent) { + this.handler = new TouchHandler(context, viewController, viewHelper); + } else if (window.navigator.pointerEnabled) { + this.handler = new StandardPointerHandler(context, viewController, viewHelper); + } else { + this.handler = new MouseHandler(context, viewController, viewHelper); + } + } + + public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget { + return this.handler.getTargetAtClientPoint(clientX, clientY); + } + + public dispose(): void { + this.handler.dispose(); + } +} diff --git a/src/vs/editor/browser/controller/textAreaHandler.css b/src/vs/editor/browser/controller/textAreaHandler.css new file mode 100644 index 0000000000..86ffbb922e --- /dev/null +++ b/src/vs/editor/browser/controller/textAreaHandler.css @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .inputarea { + min-width: 0; + min-height: 0; + margin: 0; + padding: 0; + position: absolute; + outline: none !important; + resize: none; + border: none; + overflow: hidden; + color: transparent; + background-color: transparent; +} +/*.monaco-editor .inputarea { + position: fixed !important; + width: 800px !important; + height: 500px !important; + top: initial !important; + left: initial !important; + bottom: 0 !important; + right: 0 !important; + color: black !important; + background: white !important; + line-height: 15px !important; + font-size: 14px !important; +}*/ +.monaco-editor .inputarea.ime-input { + z-index: 10; +} diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts new file mode 100644 index 0000000000..d237598d75 --- /dev/null +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -0,0 +1,508 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./textAreaHandler'; +import * as platform from 'vs/base/common/platform'; +import * as browser from 'vs/base/browser/browser'; +import { TextAreaInput, ITextAreaInputHost, IPasteData, ICompositionData } from 'vs/editor/browser/controller/textAreaInput'; +import { ISimpleModel, ITypeData, TextAreaState, PagedScreenReaderStrategy } from 'vs/editor/browser/controller/textAreaState'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; +import { Configuration } from 'vs/editor/browser/config/configuration'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { HorizontalRange, RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { ViewController } from 'vs/editor/browser/view/viewController'; +import { EndOfLinePreference, ScrollType } from 'vs/editor/common/editorCommon'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { PartFingerprints, PartFingerprint, ViewPart } from 'vs/editor/browser/view/viewPart'; +import { Margin } from 'vs/editor/browser/viewParts/margin/margin'; +import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers'; +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; + +export interface ITextAreaHandlerHelper { + visibleRangeForPositionRelativeToEditor(lineNumber: number, column: number): HorizontalRange; +} + +class VisibleTextAreaData { + _visibleTextAreaBrand: void; + + public readonly top: number; + public readonly left: number; + public readonly width: number; + + constructor(top: number, left: number, width: number) { + this.top = top; + this.left = left; + this.width = width; + } + + public setWidth(width: number): VisibleTextAreaData { + return new VisibleTextAreaData(this.top, this.left, width); + } +} + +const canUseZeroSizeTextarea = (browser.isEdgeOrIE || browser.isFirefox); + +export class TextAreaHandler extends ViewPart { + + private readonly _viewController: ViewController; + private readonly _viewHelper: ITextAreaHandlerHelper; + + private _pixelRatio: number; + private _accessibilitySupport: platform.AccessibilitySupport; + private _contentLeft: number; + private _contentWidth: number; + private _contentHeight: number; + private _scrollLeft: number; + private _scrollTop: number; + private _fontInfo: BareFontInfo; + private _lineHeight: number; + private _emptySelectionClipboard: boolean; + + /** + * Defined only when the text area is visible (composition case). + */ + private _visibleTextArea: VisibleTextAreaData; + private _selections: Selection[]; + private _lastCopiedValue: string; + private _lastCopiedValueIsFromEmptySelection: boolean; + + public readonly textArea: FastDomNode; + public readonly textAreaCover: FastDomNode; + private readonly _textAreaInput: TextAreaInput; + + constructor(context: ViewContext, viewController: ViewController, viewHelper: ITextAreaHandlerHelper) { + super(context); + + this._viewController = viewController; + this._viewHelper = viewHelper; + + const conf = this._context.configuration.editor; + + this._pixelRatio = conf.pixelRatio; + this._accessibilitySupport = conf.accessibilitySupport; + this._contentLeft = conf.layoutInfo.contentLeft; + this._contentWidth = conf.layoutInfo.contentWidth; + this._contentHeight = conf.layoutInfo.contentHeight; + this._scrollLeft = 0; + this._scrollTop = 0; + this._fontInfo = conf.fontInfo; + this._lineHeight = conf.lineHeight; + this._emptySelectionClipboard = conf.emptySelectionClipboard; + + this._visibleTextArea = null; + this._selections = [new Selection(1, 1, 1, 1)]; + this._lastCopiedValue = null; + this._lastCopiedValueIsFromEmptySelection = false; + + // Text Area (The focus will always be in the textarea when the cursor is blinking) + this.textArea = createFastDomNode(document.createElement('textarea')); + PartFingerprints.write(this.textArea, PartFingerprint.TextArea); + this.textArea.setClassName('inputarea'); + this.textArea.setAttribute('wrap', 'off'); + this.textArea.setAttribute('autocorrect', 'off'); + this.textArea.setAttribute('autocapitalize', 'off'); + this.textArea.setAttribute('autocomplete', 'off'); + this.textArea.setAttribute('spellcheck', 'false'); + this.textArea.setAttribute('aria-label', conf.viewInfo.ariaLabel); + this.textArea.setAttribute('role', 'textbox'); + this.textArea.setAttribute('aria-multiline', 'true'); + this.textArea.setAttribute('aria-haspopup', 'false'); + this.textArea.setAttribute('aria-autocomplete', 'both'); + + this.textAreaCover = createFastDomNode(document.createElement('div')); + this.textAreaCover.setPosition('absolute'); + + const simpleModel: ISimpleModel = { + getLineCount: (): number => { + return this._context.model.getLineCount(); + }, + getLineMaxColumn: (lineNumber: number): number => { + return this._context.model.getLineMaxColumn(lineNumber); + }, + getValueInRange: (range: Range, eol: EndOfLinePreference): string => { + return this._context.model.getValueInRange(range, eol); + } + }; + + const textAreaInputHost: ITextAreaInputHost = { + getPlainTextToCopy: (): string => { + const whatToCopy = this._context.model.getPlainTextToCopy(this._selections, this._emptySelectionClipboard); + + if (this._emptySelectionClipboard) { + if (browser.isFirefox) { + // When writing "LINE\r\n" to the clipboard and then pasting, + // Firefox pastes "LINE\n", so let's work around this quirk + this._lastCopiedValue = whatToCopy.replace(/\r\n/g, '\n'); + } else { + this._lastCopiedValue = whatToCopy; + } + + let selections = this._selections; + this._lastCopiedValueIsFromEmptySelection = (selections.length === 1 && selections[0].isEmpty()); + } + + return whatToCopy; + }, + + getHTMLToCopy: (): string => { + return this._context.model.getHTMLToCopy(this._selections, this._emptySelectionClipboard); + }, + + getScreenReaderContent: (currentState: TextAreaState): TextAreaState => { + + if (browser.isIPad) { + // Do not place anything in the textarea for the iPad + return TextAreaState.EMPTY; + } + + if (this._accessibilitySupport === platform.AccessibilitySupport.Disabled) { + // We know for a fact that a screen reader is not attached + return TextAreaState.EMPTY; + } + + return PagedScreenReaderStrategy.fromEditorSelection(currentState, simpleModel, this._selections[0]); + }, + + deduceModelPosition: (viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position => { + return this._context.model.deduceModelPositionRelativeToViewPosition(viewAnchorPosition, deltaOffset, lineFeedCnt); + } + }; + + this._textAreaInput = this._register(new TextAreaInput(textAreaInputHost, this.textArea)); + + this._register(this._textAreaInput.onKeyDown((e: IKeyboardEvent) => { + this._viewController.emitKeyDown(e); + })); + + this._register(this._textAreaInput.onKeyUp((e: IKeyboardEvent) => { + this._viewController.emitKeyUp(e); + })); + + this._register(this._textAreaInput.onPaste((e: IPasteData) => { + let pasteOnNewLine = false; + if (this._emptySelectionClipboard) { + pasteOnNewLine = (e.text === this._lastCopiedValue && this._lastCopiedValueIsFromEmptySelection); + } + this._viewController.paste('keyboard', e.text, pasteOnNewLine); + })); + + this._register(this._textAreaInput.onCut(() => { + this._viewController.cut('keyboard'); + })); + + this._register(this._textAreaInput.onType((e: ITypeData) => { + if (e.replaceCharCnt) { + this._viewController.replacePreviousChar('keyboard', e.text, e.replaceCharCnt); + } else { + this._viewController.type('keyboard', e.text); + } + })); + + this._register(this._textAreaInput.onSelectionChangeRequest((modelSelection: Selection) => { + this._viewController.setSelection('keyboard', modelSelection); + })); + + this._register(this._textAreaInput.onCompositionStart(() => { + const lineNumber = this._selections[0].startLineNumber; + const column = this._selections[0].startColumn; + + this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( + new Range(lineNumber, column, lineNumber, column), + viewEvents.VerticalRevealType.Simple, + true, + ScrollType.Immediate + )); + + // Find range pixel position + const visibleRange = this._viewHelper.visibleRangeForPositionRelativeToEditor(lineNumber, column); + + if (visibleRange) { + this._visibleTextArea = new VisibleTextAreaData( + this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber), + visibleRange.left, + canUseZeroSizeTextarea ? 0 : 1 + ); + this._render(); + } + + // Show the textarea + this.textArea.setClassName('inputarea ime-input'); + + this._viewController.compositionStart('keyboard'); + })); + + this._register(this._textAreaInput.onCompositionUpdate((e: ICompositionData) => { + if (browser.isEdgeOrIE) { + // Due to isEdgeOrIE (where the textarea was not cleared initially) + // we cannot assume the text consists only of the composited text + this._visibleTextArea = this._visibleTextArea.setWidth(0); + } else { + // adjust width by its size + this._visibleTextArea = this._visibleTextArea.setWidth(measureText(e.data, this._fontInfo)); + } + this._render(); + })); + + this._register(this._textAreaInput.onCompositionEnd(() => { + + this._visibleTextArea = null; + this._render(); + + this.textArea.setClassName('inputarea'); + this._viewController.compositionEnd('keyboard'); + })); + + this._register(this._textAreaInput.onFocus(() => { + this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(true)); + })); + + this._register(this._textAreaInput.onBlur(() => { + this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(false)); + })); + } + + public dispose(): void { + super.dispose(); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + const conf = this._context.configuration.editor; + + if (e.fontInfo) { + this._fontInfo = conf.fontInfo; + } + if (e.viewInfo) { + this.textArea.setAttribute('aria-label', conf.viewInfo.ariaLabel); + } + if (e.layoutInfo) { + this._contentLeft = conf.layoutInfo.contentLeft; + this._contentWidth = conf.layoutInfo.contentWidth; + this._contentHeight = conf.layoutInfo.contentHeight; + } + if (e.lineHeight) { + this._lineHeight = conf.lineHeight; + } + if (e.pixelRatio) { + this._pixelRatio = conf.pixelRatio; + } + if (e.accessibilitySupport) { + this._accessibilitySupport = conf.accessibilitySupport; + this._textAreaInput.writeScreenReaderContent('strategy changed'); + } + if (e.emptySelectionClipboard) { + this._emptySelectionClipboard = conf.emptySelectionClipboard; + } + + return true; + } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + this._selections = e.selections.slice(0); + this._textAreaInput.writeScreenReaderContent('selection changed'); + return true; + } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + // true for inline decorations that can end up relayouting text + return true; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return true; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + this._scrollLeft = e.scrollLeft; + this._scrollTop = e.scrollTop; + return true; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + // --- end event handlers + + // --- begin view API + + public isFocused(): boolean { + return this._textAreaInput.isFocused(); + } + + public focusTextArea(): void { + this._textAreaInput.focusTextArea(); + } + + public setAriaActiveDescendant(id: string): void { + if (id) { + this.textArea.setAttribute('role', 'combobox'); + if (this.textArea.getAttribute('aria-activedescendant') !== id) { + this.textArea.setAttribute('aria-haspopup', 'true'); + this.textArea.setAttribute('aria-activedescendant', id); + } + } else { + this.textArea.setAttribute('role', 'textbox'); + this.textArea.removeAttribute('aria-activedescendant'); + this.textArea.removeAttribute('aria-haspopup'); + } + } + + // --- end view API + + private _primaryCursorVisibleRange: HorizontalRange = null; + + public prepareRender(ctx: RenderingContext): void { + if (this._accessibilitySupport === platform.AccessibilitySupport.Enabled) { + // Do not move the textarea with the cursor, as this generates accessibility events that might confuse screen readers + // See https://github.com/Microsoft/vscode/issues/26730 + this._primaryCursorVisibleRange = null; + } else { + const primaryCursorPosition = new Position(this._selections[0].positionLineNumber, this._selections[0].positionColumn); + this._primaryCursorVisibleRange = ctx.visibleRangeForPosition(primaryCursorPosition); + } + } + + public render(ctx: RestrictedRenderingContext): void { + this._textAreaInput.writeScreenReaderContent('render'); + this._render(); + } + + private _render(): void { + if (this._visibleTextArea) { + // The text area is visible for composition reasons + this._renderInsideEditor( + this._visibleTextArea.top - this._scrollTop, + this._contentLeft + this._visibleTextArea.left - this._scrollLeft, + this._visibleTextArea.width, + this._lineHeight, + true + ); + return; + } + + if (!this._primaryCursorVisibleRange) { + // The primary cursor is outside the viewport => place textarea to the top left + this._renderAtTopLeft(); + return; + } + + const left = this._contentLeft + this._primaryCursorVisibleRange.left - this._scrollLeft; + if (left < this._contentLeft || left > this._contentLeft + this._contentWidth) { + // cursor is outside the viewport + this._renderAtTopLeft(); + return; + } + + const top = this._context.viewLayout.getVerticalOffsetForLineNumber(this._selections[0].positionLineNumber) - this._scrollTop; + if (top < 0 || top > this._contentHeight) { + // cursor is outside the viewport + this._renderAtTopLeft(); + return; + } + + // The primary cursor is in the viewport (at least vertically) => place textarea on the cursor + this._renderInsideEditor( + top, left, + canUseZeroSizeTextarea ? 0 : 1, canUseZeroSizeTextarea ? 0 : 1, + false + ); + } + + private _renderInsideEditor(top: number, left: number, width: number, height: number, useEditorFont: boolean): void { + const ta = this.textArea; + const tac = this.textAreaCover; + + if (useEditorFont) { + Configuration.applyFontInfo(ta, this._fontInfo); + } else { + ta.setFontSize(1); + ta.setLineHeight(this._fontInfo.lineHeight); + } + + ta.setTop(top); + ta.setLeft(left); + ta.setWidth(width); + ta.setHeight(height); + + tac.setTop(0); + tac.setLeft(0); + tac.setWidth(0); + tac.setHeight(0); + } + + private _renderAtTopLeft(): void { + const ta = this.textArea; + const tac = this.textAreaCover; + + Configuration.applyFontInfo(ta, this._fontInfo); + ta.setTop(0); + ta.setLeft(0); + tac.setTop(0); + tac.setLeft(0); + + if (canUseZeroSizeTextarea) { + ta.setWidth(0); + ta.setHeight(0); + tac.setWidth(0); + tac.setHeight(0); + return; + } + + // (in WebKit the textarea is 1px by 1px because it cannot handle input to a 0x0 textarea) + // specifically, when doing Korean IME, setting the textare to 0x0 breaks IME badly. + + ta.setWidth(1); + ta.setHeight(1); + tac.setWidth(1); + tac.setHeight(1); + + if (this._context.configuration.editor.viewInfo.glyphMargin) { + tac.setClassName('monaco-editor-background textAreaCover ' + Margin.CLASS_NAME); + } else { + if (this._context.configuration.editor.viewInfo.renderLineNumbers) { + tac.setClassName('monaco-editor-background textAreaCover ' + LineNumbersOverlay.CLASS_NAME); + } else { + tac.setClassName('monaco-editor-background textAreaCover'); + } + } + } +} + +function measureText(text: string, fontInfo: BareFontInfo): number { + // adjust width by its size + const canvasElem = document.createElement('canvas'); + const context = canvasElem.getContext('2d'); + context.font = createFontString(fontInfo); + const metrics = context.measureText(text); + + if (browser.isFirefox) { + return metrics.width + 2; // +2 for Japanese... + } else { + return metrics.width; + } +} + +function createFontString(bareFontInfo: BareFontInfo): string { + return doCreateFontString('normal', bareFontInfo.fontWeight, bareFontInfo.fontSize, bareFontInfo.lineHeight, bareFontInfo.fontFamily); +} + +function doCreateFontString(fontStyle: string, fontWeight: string, fontSize: number, lineHeight: number, fontFamily: string): string { + // The full font syntax is: + // style | variant | weight | stretch | size/line-height | fontFamily + // (https://developer.mozilla.org/en-US/docs/Web/CSS/font) + // But it appears Edge and IE11 cannot properly parse `stretch`. + return `${fontStyle} normal ${fontWeight} ${fontSize}px / ${lineHeight}px ${fontFamily}`; +} diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts new file mode 100644 index 0000000000..b7a89e9c8e --- /dev/null +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -0,0 +1,585 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { RunOnceScheduler } from 'vs/base/common/async'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as strings from 'vs/base/common/strings'; +import Event, { Emitter } from 'vs/base/common/event'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ITypeData, TextAreaState, ITextAreaWrapper } from 'vs/editor/browser/controller/textAreaState'; +import * as browser from 'vs/base/browser/browser'; +import * as platform from 'vs/base/common/platform'; +import * as dom from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { FastDomNode } from 'vs/base/browser/fastDomNode'; + +export interface ICompositionData { + data: string; +} + +export const CopyOptions = { + forceCopyWithSyntaxHighlighting: false +}; + +const enum ReadFromTextArea { + Type, + Paste +} + +export interface IPasteData { + text: string; +} + +export interface ITextAreaInputHost { + getPlainTextToCopy(): string; + getHTMLToCopy(): string; + getScreenReaderContent(currentState: TextAreaState): TextAreaState; + deduceModelPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position; +} + +/** + * Writes screen reader content to the textarea and is able to analyze its input events to generate: + * - onCut + * - onPaste + * - onType + * + * Composition events are generated for presentation purposes (composition input is reflected in onType). + */ +export class TextAreaInput extends Disposable { + + private _onFocus = this._register(new Emitter()); + public onFocus: Event = this._onFocus.event; + + private _onBlur = this._register(new Emitter()); + public onBlur: Event = this._onBlur.event; + + private _onKeyDown = this._register(new Emitter()); + public onKeyDown: Event = this._onKeyDown.event; + + private _onKeyUp = this._register(new Emitter()); + public onKeyUp: Event = this._onKeyUp.event; + + private _onCut = this._register(new Emitter()); + public onCut: Event = this._onCut.event; + + private _onPaste = this._register(new Emitter()); + public onPaste: Event = this._onPaste.event; + + private _onType = this._register(new Emitter()); + public onType: Event = this._onType.event; + + private _onCompositionStart = this._register(new Emitter()); + public onCompositionStart: Event = this._onCompositionStart.event; + + private _onCompositionUpdate = this._register(new Emitter()); + public onCompositionUpdate: Event = this._onCompositionUpdate.event; + + private _onCompositionEnd = this._register(new Emitter()); + public onCompositionEnd: Event = this._onCompositionEnd.event; + + private _onSelectionChangeRequest = this._register(new Emitter()); + public onSelectionChangeRequest: Event = this._onSelectionChangeRequest.event; + + // --- + + private readonly _host: ITextAreaInputHost; + private readonly _textArea: TextAreaWrapper; + private readonly _asyncTriggerCut: RunOnceScheduler; + + private _textAreaState: TextAreaState; + + private _hasFocus: boolean; + private _isDoingComposition: boolean; + private _nextCommand: ReadFromTextArea; + + constructor(host: ITextAreaInputHost, textArea: FastDomNode) { + super(); + this._host = host; + this._textArea = this._register(new TextAreaWrapper(textArea)); + this._asyncTriggerCut = this._register(new RunOnceScheduler(() => this._onCut.fire(), 0)); + + this._textAreaState = TextAreaState.EMPTY; + this.writeScreenReaderContent('ctor'); + + this._hasFocus = false; + this._isDoingComposition = false; + this._nextCommand = ReadFromTextArea.Type; + + this._register(dom.addStandardDisposableListener(textArea.domNode, 'keydown', (e: IKeyboardEvent) => { + if (this._isDoingComposition && e.equals(KeyCode.KEY_IN_COMPOSITION)) { + // Stop propagation for keyDown events if the IME is processing key input + e.stopPropagation(); + } + + if (e.equals(KeyCode.Escape)) { + // Prevent default always for `Esc`, otherwise it will generate a keypress + // See https://msdn.microsoft.com/en-us/library/ie/ms536939(v=vs.85).aspx + e.preventDefault(); + } + this._onKeyDown.fire(e); + })); + + this._register(dom.addStandardDisposableListener(textArea.domNode, 'keyup', (e: IKeyboardEvent) => { + this._onKeyUp.fire(e); + })); + + this._register(dom.addDisposableListener(textArea.domNode, 'compositionstart', (e: CompositionEvent) => { + if (this._isDoingComposition) { + return; + } + this._isDoingComposition = true; + + // In IE we cannot set .value when handling 'compositionstart' because the entire composition will get canceled. + if (!browser.isEdgeOrIE) { + this._setAndWriteTextAreaState('compositionstart', TextAreaState.EMPTY); + } + + this._onCompositionStart.fire(); + })); + + /** + * Deduce the typed input from a text area's value and the last observed state. + */ + const deduceInputFromTextAreaValue = (couldBeEmojiInput: boolean): [TextAreaState, ITypeData] => { + const oldState = this._textAreaState; + const newState = this._textAreaState.readFromTextArea(this._textArea); + return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput)]; + }; + + /** + * Deduce the composition input from a string. + */ + const deduceComposition = (text: string): [TextAreaState, ITypeData] => { + const oldState = this._textAreaState; + const newState = TextAreaState.selectedText(text); + const typeInput: ITypeData = { + text: newState.value, + replaceCharCnt: oldState.selectionEnd - oldState.selectionStart + }; + return [newState, typeInput]; + }; + + this._register(dom.addDisposableListener(textArea.domNode, 'compositionupdate', (e: CompositionEvent) => { + if (browser.isChromev56) { + // See https://github.com/Microsoft/monaco-editor/issues/320 + // where compositionupdate .data is broken in Chrome v55 and v56 + // See https://bugs.chromium.org/p/chromium/issues/detail?id=677050#c9 + // The textArea doesn't get the composition update yet, the value of textarea is still obsolete + // so we can't correct e at this moment. + return; + } + + if (browser.isEdgeOrIE && e.locale === 'ja') { + // https://github.com/Microsoft/monaco-editor/issues/339 + // Multi-part Japanese compositions reset cursor in Edge/IE, Chinese and Korean IME don't have this issue. + // The reason that we can't use this path for all CJK IME is IE and Edge behave differently when handling Korean IME, + // which breaks this path of code. + const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false); + this._textAreaState = newState; + this._onType.fire(typeInput); + this._onCompositionUpdate.fire(e); + return; + } + + const [newState, typeInput] = deduceComposition(e.data); + this._textAreaState = newState; + this._onType.fire(typeInput); + this._onCompositionUpdate.fire(e); + })); + + this._register(dom.addDisposableListener(textArea.domNode, 'compositionend', (e: CompositionEvent) => { + if (browser.isEdgeOrIE && e.locale === 'ja') { + // https://github.com/Microsoft/monaco-editor/issues/339 + const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false); + this._textAreaState = newState; + this._onType.fire(typeInput); + } + else { + const [newState, typeInput] = deduceComposition(e.data); + this._textAreaState = newState; + this._onType.fire(typeInput); + } + + // Due to isEdgeOrIE (where the textarea was not cleared initially) and isChrome (the textarea is not updated correctly when composition ends) + // we cannot assume the text at the end consists only of the composited text + if (browser.isEdgeOrIE || browser.isChrome) { + this._textAreaState = this._textAreaState.readFromTextArea(this._textArea); + } + + if (!this._isDoingComposition) { + return; + } + this._isDoingComposition = false; + + this._onCompositionEnd.fire(); + })); + + this._register(dom.addDisposableListener(textArea.domNode, 'input', () => { + // Pretend here we touched the text area, as the `input` event will most likely + // result in a `selectionchange` event which we want to ignore + this._textArea.setIgnoreSelectionChangeTime('received input event'); + + if (this._isDoingComposition) { + // See https://github.com/Microsoft/monaco-editor/issues/320 + if (browser.isChromev56) { + const [newState, typeInput] = deduceComposition(this._textArea.getValue()); + this._textAreaState = newState; + + this._onType.fire(typeInput); + let e: ICompositionData = { + data: typeInput.text + }; + this._onCompositionUpdate.fire(e); + } + return; + } + + const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh); + if (typeInput.replaceCharCnt === 0 && typeInput.text.length === 1 && strings.isHighSurrogate(typeInput.text.charCodeAt(0))) { + // Ignore invalid input but keep it around for next time + return; + } + + this._textAreaState = newState; + // console.log('==> DEDUCED INPUT: ' + JSON.stringify(typeInput)); + if (this._nextCommand === ReadFromTextArea.Type) { + if (typeInput.text !== '') { + this._onType.fire(typeInput); + } + } else { + if (typeInput.text !== '') { + this._onPaste.fire({ + text: typeInput.text + }); + } + this._nextCommand = ReadFromTextArea.Type; + } + })); + + // --- Clipboard operations + + this._register(dom.addDisposableListener(textArea.domNode, 'cut', (e: ClipboardEvent) => { + // Pretend here we touched the text area, as the `cut` event will most likely + // result in a `selectionchange` event which we want to ignore + this._textArea.setIgnoreSelectionChangeTime('received cut event'); + + this._ensureClipboardGetsEditorSelection(e); + this._asyncTriggerCut.schedule(); + })); + + this._register(dom.addDisposableListener(textArea.domNode, 'copy', (e: ClipboardEvent) => { + this._ensureClipboardGetsEditorSelection(e); + })); + + this._register(dom.addDisposableListener(textArea.domNode, 'paste', (e: ClipboardEvent) => { + // Pretend here we touched the text area, as the `paste` event will most likely + // result in a `selectionchange` event which we want to ignore + this._textArea.setIgnoreSelectionChangeTime('received paste event'); + + if (ClipboardEventUtils.canUseTextData(e)) { + const pastePlainText = ClipboardEventUtils.getTextData(e); + if (pastePlainText !== '') { + this._onPaste.fire({ + text: pastePlainText + }); + } + } else { + if (this._textArea.getSelectionStart() !== this._textArea.getSelectionEnd()) { + // Clean up the textarea, to get a clean paste + this._setAndWriteTextAreaState('paste', TextAreaState.EMPTY); + } + this._nextCommand = ReadFromTextArea.Paste; + } + })); + + this._register(dom.addDisposableListener(textArea.domNode, 'focus', () => this._setHasFocus(true))); + this._register(dom.addDisposableListener(textArea.domNode, 'blur', () => this._setHasFocus(false))); + + + // See https://github.com/Microsoft/vscode/issues/27216 + // When using a Braille display, it is possible for users to reposition the + // system caret. This is reflected in Chrome as a `selectionchange` event. + // + // The `selectionchange` event appears to be emitted under numerous other circumstances, + // so it is quite a challenge to distinguish a `selectionchange` coming in from a user + // using a Braille display from all the other cases. + // + // The problems with the `selectionchange` event are: + // * the event is emitted when the textarea is focused programmatically -- textarea.focus() + // * the event is emitted when the selection is changed in the textarea programatically -- textarea.setSelectionRange(...) + // * the event is emitted when the value of the textarea is changed programmatically -- textarea.value = '...' + // * the event is emitted when tabbing into the textarea + // * the event is emitted asynchronously (sometimes with a delay as high as a few tens of ms) + // * the event sometimes comes in bursts for a single logical textarea operation + + // `selectionchange` events often come multiple times for a single logical change + // so throttle multiple `selectionchange` events that burst in a short period of time. + let previousSelectionChangeEventTime = 0; + this._register(dom.addDisposableListener(document, 'selectionchange', (e) => { + if (!this._hasFocus) { + return; + } + if (this._isDoingComposition) { + return; + } + if (!browser.isChrome || !platform.isWindows) { + // Support only for Chrome on Windows until testing happens on other browsers + OS configurations + return; + } + + const now = Date.now(); + + const delta1 = now - previousSelectionChangeEventTime; + previousSelectionChangeEventTime = now; + if (delta1 < 5) { + // received another `selectionchange` event within 5ms of the previous `selectionchange` event + // => ignore it + return; + } + + const delta2 = now - this._textArea.getIgnoreSelectionChangeTime(); + this._textArea.resetSelectionChangeTime(); + if (delta2 < 100) { + // received a `selectionchange` event within 100ms since we touched the textarea + // => ignore it, since we caused it + return; + } + + if (!this._textAreaState.selectionStartPosition || !this._textAreaState.selectionEndPosition) { + // Cannot correlate a position in the textarea with a position in the editor... + return; + } + + const newValue = this._textArea.getValue(); + if (this._textAreaState.value !== newValue) { + // Cannot correlate a position in the textarea with a position in the editor... + return; + } + + const newSelectionStart = this._textArea.getSelectionStart(); + const newSelectionEnd = this._textArea.getSelectionEnd(); + if (this._textAreaState.selectionStart === newSelectionStart && this._textAreaState.selectionEnd === newSelectionEnd) { + // Nothing to do... + return; + } + + const _newSelectionStartPosition = this._textAreaState.deduceEditorPosition(newSelectionStart); + const newSelectionStartPosition = this._host.deduceModelPosition(_newSelectionStartPosition[0], _newSelectionStartPosition[1], _newSelectionStartPosition[2]); + + const _newSelectionEndPosition = this._textAreaState.deduceEditorPosition(newSelectionEnd); + const newSelectionEndPosition = this._host.deduceModelPosition(_newSelectionEndPosition[0], _newSelectionEndPosition[1], _newSelectionEndPosition[2]); + + const newSelection = new Selection( + newSelectionStartPosition.lineNumber, newSelectionStartPosition.column, + newSelectionEndPosition.lineNumber, newSelectionEndPosition.column + ); + + this._onSelectionChangeRequest.fire(newSelection); + })); + } + + public dispose(): void { + super.dispose(); + } + + public focusTextArea(): void { + // Setting this._hasFocus and writing the screen reader content + // will result in a focus() and setSelectionRange() in the textarea + this._setHasFocus(true); + } + + public isFocused(): boolean { + return this._hasFocus; + } + + private _setHasFocus(newHasFocus: boolean): void { + if (this._hasFocus === newHasFocus) { + // no change + return; + } + this._hasFocus = newHasFocus; + + if (this._hasFocus) { + if (browser.isEdge) { + // Edge has a bug where setting the selection range while the focus event + // is dispatching doesn't work. To reproduce, "tab into" the editor. + this._setAndWriteTextAreaState('focusgain', TextAreaState.EMPTY); + } else { + this.writeScreenReaderContent('focusgain'); + } + } + + if (this._hasFocus) { + this._onFocus.fire(); + } else { + this._onBlur.fire(); + } + } + + private _setAndWriteTextAreaState(reason: string, textAreaState: TextAreaState): void { + if (!this._hasFocus) { + textAreaState = textAreaState.collapseSelection(); + } + + textAreaState.writeToTextArea(reason, this._textArea, this._hasFocus); + this._textAreaState = textAreaState; + } + + public writeScreenReaderContent(reason: string): void { + if (this._isDoingComposition) { + // Do not write to the text area when doing composition + return; + } + + this._setAndWriteTextAreaState(reason, this._host.getScreenReaderContent(this._textAreaState)); + } + + private _ensureClipboardGetsEditorSelection(e: ClipboardEvent): void { + const copyPlainText = this._host.getPlainTextToCopy(); + if (!ClipboardEventUtils.canUseTextData(e)) { + // Looks like an old browser. The strategy is to place the text + // we'd like to be copied to the clipboard in the textarea and select it. + this._setAndWriteTextAreaState('copy or cut', TextAreaState.selectedText(copyPlainText)); + return; + } + + let copyHTML: string = null; + if (!browser.isEdgeOrIE && (copyPlainText.length < 65536 || CopyOptions.forceCopyWithSyntaxHighlighting)) { + copyHTML = this._host.getHTMLToCopy(); + } + ClipboardEventUtils.setTextData(e, copyPlainText, copyHTML); + } +} + +class ClipboardEventUtils { + + public static canUseTextData(e: ClipboardEvent): boolean { + if (e.clipboardData) { + return true; + } + if ((window).clipboardData) { + return true; + } + return false; + } + + public static getTextData(e: ClipboardEvent): string { + if (e.clipboardData) { + e.preventDefault(); + return e.clipboardData.getData('text/plain'); + } + + if ((window).clipboardData) { + e.preventDefault(); + return (window).clipboardData.getData('Text'); + } + + throw new Error('ClipboardEventUtils.getTextData: Cannot use text data!'); + } + + public static setTextData(e: ClipboardEvent, text: string, richText: string): void { + if (e.clipboardData) { + e.clipboardData.setData('text/plain', text); + if (richText !== null) { + e.clipboardData.setData('text/html', richText); + } + e.preventDefault(); + return; + } + + if ((window).clipboardData) { + (window).clipboardData.setData('Text', text); + e.preventDefault(); + return; + } + + throw new Error('ClipboardEventUtils.setTextData: Cannot use text data!'); + } +} + +class TextAreaWrapper extends Disposable implements ITextAreaWrapper { + + private readonly _actual: FastDomNode; + private _ignoreSelectionChangeTime: number; + + constructor(_textArea: FastDomNode) { + super(); + this._actual = _textArea; + this._ignoreSelectionChangeTime = 0; + } + + public setIgnoreSelectionChangeTime(reason: string): void { + this._ignoreSelectionChangeTime = Date.now(); + } + + public getIgnoreSelectionChangeTime(): number { + return this._ignoreSelectionChangeTime; + } + + public resetSelectionChangeTime(): void { + this._ignoreSelectionChangeTime = 0; + } + + public getValue(): string { + // console.log('current value: ' + this._textArea.value); + return this._actual.domNode.value; + } + + public setValue(reason: string, value: string): void { + const textArea = this._actual.domNode; + if (textArea.value === value) { + // No change + return; + } + // console.log('reason: ' + reason + ', current value: ' + textArea.value + ' => new value: ' + value); + this.setIgnoreSelectionChangeTime('setValue'); + textArea.value = value; + } + + public getSelectionStart(): number { + return this._actual.domNode.selectionStart; + } + + public getSelectionEnd(): number { + return this._actual.domNode.selectionEnd; + } + + public setSelectionRange(reason: string, selectionStart: number, selectionEnd: number): void { + const textArea = this._actual.domNode; + + const currentIsFocused = (document.activeElement === textArea); + const currentSelectionStart = textArea.selectionStart; + const currentSelectionEnd = textArea.selectionEnd; + + if (currentIsFocused && currentSelectionStart === selectionStart && currentSelectionEnd === selectionEnd) { + // No change + return; + } + + // console.log('reason: ' + reason + ', setSelectionRange: ' + selectionStart + ' -> ' + selectionEnd); + + if (currentIsFocused) { + // No need to focus, only need to change the selection range + this.setIgnoreSelectionChangeTime('setSelectionRange'); + textArea.setSelectionRange(selectionStart, selectionEnd); + return; + } + + // If the focus is outside the textarea, browsers will try really hard to reveal the textarea. + // Here, we try to undo the browser's desperate reveal. + try { + const scrollState = dom.saveParentsScrollTop(textArea); + this.setIgnoreSelectionChangeTime('setSelectionRange'); + textArea.focus(); + textArea.setSelectionRange(selectionStart, selectionEnd); + dom.restoreParentsScrollTop(textArea, scrollState); + } catch (e) { + // Sometimes IE throws when setting selection (e.g. textarea is off-DOM) + } + } +} diff --git a/src/vs/editor/browser/controller/textAreaState.ts b/src/vs/editor/browser/controller/textAreaState.ts new file mode 100644 index 0000000000..09a3c6e914 --- /dev/null +++ b/src/vs/editor/browser/controller/textAreaState.ts @@ -0,0 +1,289 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { EndOfLinePreference } from 'vs/editor/common/editorCommon'; +import * as strings from 'vs/base/common/strings'; + +export interface ITextAreaWrapper { + getValue(): string; + setValue(reason: string, value: string): void; + + getSelectionStart(): number; + getSelectionEnd(): number; + setSelectionRange(reason: string, selectionStart: number, selectionEnd: number): void; +} + +export interface ISimpleModel { + getLineCount(): number; + getLineMaxColumn(lineNumber: number): number; + getValueInRange(range: Range, eol: EndOfLinePreference): string; +} + +export interface ITypeData { + text: string; + replaceCharCnt: number; +} + +export class TextAreaState { + + public static EMPTY = new TextAreaState('', 0, 0, null, null); + + public readonly value: string; + public readonly selectionStart: number; + public readonly selectionEnd: number; + public readonly selectionStartPosition: Position; + public readonly selectionEndPosition: Position; + + constructor(value: string, selectionStart: number, selectionEnd: number, selectionStartPosition: Position, selectionEndPosition: Position) { + this.value = value; + this.selectionStart = selectionStart; + this.selectionEnd = selectionEnd; + this.selectionStartPosition = selectionStartPosition; + this.selectionEndPosition = selectionEndPosition; + } + + public equals(other: TextAreaState): boolean { + if (other instanceof TextAreaState) { + return ( + this.value === other.value + && this.selectionStart === other.selectionStart + && this.selectionEnd === other.selectionEnd + && Position.equals(this.selectionStartPosition, other.selectionStartPosition) + && Position.equals(this.selectionEndPosition, other.selectionEndPosition) + ); + } + return false; + } + + public toString(): string { + return '[ <' + this.value + '>, selectionStart: ' + this.selectionStart + ', selectionEnd: ' + this.selectionEnd + ']'; + } + + public readFromTextArea(textArea: ITextAreaWrapper): TextAreaState { + return new TextAreaState(textArea.getValue(), textArea.getSelectionStart(), textArea.getSelectionEnd(), null, null); + } + + public collapseSelection(): TextAreaState { + return new TextAreaState(this.value, this.value.length, this.value.length, null, null); + } + + public writeToTextArea(reason: string, textArea: ITextAreaWrapper, select: boolean): void { + // console.log(Date.now() + ': applyToTextArea ' + reason + ': ' + this.toString()); + textArea.setValue(reason, this.value); + if (select) { + textArea.setSelectionRange(reason, this.selectionStart, this.selectionEnd); + } + } + + public deduceEditorPosition(offset: number): [Position, number, number] { + if (offset <= this.selectionStart) { + const str = this.value.substring(offset, this.selectionStart); + return this._finishDeduceEditorPosition(this.selectionStartPosition, str, -1); + } + if (offset >= this.selectionEnd) { + const str = this.value.substring(this.selectionEnd, offset); + return this._finishDeduceEditorPosition(this.selectionEndPosition, str, 1); + } + const str1 = this.value.substring(this.selectionStart, offset); + if (str1.indexOf(String.fromCharCode(8230)) === -1) { + return this._finishDeduceEditorPosition(this.selectionStartPosition, str1, 1); + } + const str2 = this.value.substring(offset, this.selectionEnd); + return this._finishDeduceEditorPosition(this.selectionEndPosition, str2, -1); + } + + private _finishDeduceEditorPosition(anchor: Position, deltaText: string, signum: number): [Position, number, number] { + let lineFeedCnt = 0; + let lastLineFeedIndex = -1; + while ((lastLineFeedIndex = deltaText.indexOf('\n', lastLineFeedIndex + 1)) !== -1) { + lineFeedCnt++; + } + return [anchor, signum * deltaText.length, lineFeedCnt]; + } + + public static selectedText(text: string): TextAreaState { + return new TextAreaState(text, 0, text.length, null, null); + } + + public static deduceInput(previousState: TextAreaState, currentState: TextAreaState, couldBeEmojiInput: boolean): ITypeData { + if (!previousState) { + // This is the EMPTY state + return { + text: '', + replaceCharCnt: 0 + }; + } + + // console.log('------------------------deduceInput'); + // console.log('PREVIOUS STATE: ' + previousState.toString()); + // console.log('CURRENT STATE: ' + currentState.toString()); + + let previousValue = previousState.value; + let previousSelectionStart = previousState.selectionStart; + let previousSelectionEnd = previousState.selectionEnd; + let currentValue = currentState.value; + let currentSelectionStart = currentState.selectionStart; + let currentSelectionEnd = currentState.selectionEnd; + + // Strip the previous suffix from the value (without interfering with the current selection) + const previousSuffix = previousValue.substring(previousSelectionEnd); + const currentSuffix = currentValue.substring(currentSelectionEnd); + const suffixLength = strings.commonSuffixLength(previousSuffix, currentSuffix); + currentValue = currentValue.substring(0, currentValue.length - suffixLength); + previousValue = previousValue.substring(0, previousValue.length - suffixLength); + + const previousPrefix = previousValue.substring(0, previousSelectionStart); + const currentPrefix = currentValue.substring(0, currentSelectionStart); + const prefixLength = strings.commonPrefixLength(previousPrefix, currentPrefix); + currentValue = currentValue.substring(prefixLength); + previousValue = previousValue.substring(prefixLength); + currentSelectionStart -= prefixLength; + previousSelectionStart -= prefixLength; + currentSelectionEnd -= prefixLength; + previousSelectionEnd -= prefixLength; + + // console.log('AFTER DIFFING PREVIOUS STATE: <' + previousValue + '>, selectionStart: ' + previousSelectionStart + ', selectionEnd: ' + previousSelectionEnd); + // console.log('AFTER DIFFING CURRENT STATE: <' + currentValue + '>, selectionStart: ' + currentSelectionStart + ', selectionEnd: ' + currentSelectionEnd); + + if (couldBeEmojiInput && currentSelectionStart === currentSelectionEnd && previousValue.length > 0) { + // on OSX, emojis from the emoji picker are inserted at random locations + // the only hints we can use is that the selection is immediately after the inserted emoji + // and that none of the old text has been deleted + + let potentialEmojiInput: string = null; + + if (currentSelectionStart === currentValue.length) { + // emoji potentially inserted "somewhere" after the previous selection => it should appear at the end of `currentValue` + if (strings.startsWith(currentValue, previousValue)) { + // only if all of the old text is accounted for + potentialEmojiInput = currentValue.substring(previousValue.length); + } + } else { + // emoji potentially inserted "somewhere" before the previous selection => it should appear at the start of `currentValue` + if (strings.endsWith(currentValue, previousValue)) { + // only if all of the old text is accounted for + potentialEmojiInput = currentValue.substring(0, currentValue.length - previousValue.length); + } + } + + if (potentialEmojiInput !== null && potentialEmojiInput.length > 0) { + // now we check that this is indeed an emoji + // emojis can grow quite long, so a length check is of no help + // e.g. 1F3F4 E0067 E0062 E0065 E006E E0067 E007F ; fully-qualified # 🏴󠁧󠁢󠁥󠁮󠁧󠁿 England + + // Oftentimes, emojis use Variation Selector-16 (U+FE0F), so that is a good hint + // http://emojipedia.org/variation-selector-16/ + // > An invisible codepoint which specifies that the preceding character + // > should be displayed with emoji presentation. Only required if the + // > preceding character defaults to text presentation. + if (/\uFE0F/.test(potentialEmojiInput) || strings.containsEmoji(potentialEmojiInput)) { + return { + text: potentialEmojiInput, + replaceCharCnt: 0 + }; + } + } + } + + if (currentSelectionStart === currentSelectionEnd) { + // composition accept case (noticed in FF + Japanese) + // [blahblah] => blahblah| + if ( + previousValue === currentValue + && previousSelectionStart === 0 + && previousSelectionEnd === previousValue.length + && currentSelectionStart === currentValue.length + && currentValue.indexOf('\n') === -1 + ) { + if (strings.containsFullWidthCharacter(currentValue)) { + return { + text: '', + replaceCharCnt: 0 + }; + } + } + + // no current selection + const replacePreviousCharacters = (previousPrefix.length - prefixLength); + // console.log('REMOVE PREVIOUS: ' + (previousPrefix.length - prefixLength) + ' chars'); + + return { + text: currentValue, + replaceCharCnt: replacePreviousCharacters + }; + } + + // there is a current selection => composition case + const replacePreviousCharacters = previousSelectionEnd - previousSelectionStart; + return { + text: currentValue, + replaceCharCnt: replacePreviousCharacters + }; + } +} + +export class PagedScreenReaderStrategy { + private static _LINES_PER_PAGE = 10; + + private static _getPageOfLine(lineNumber: number): number { + return Math.floor((lineNumber - 1) / PagedScreenReaderStrategy._LINES_PER_PAGE); + } + + private static _getRangeForPage(page: number): Range { + let offset = page * PagedScreenReaderStrategy._LINES_PER_PAGE; + let startLineNumber = offset + 1; + let endLineNumber = offset + PagedScreenReaderStrategy._LINES_PER_PAGE; + return new Range(startLineNumber, 1, endLineNumber + 1, 1); + } + + public static fromEditorSelection(previousState: TextAreaState, model: ISimpleModel, selection: Range): TextAreaState { + + let selectionStartPage = PagedScreenReaderStrategy._getPageOfLine(selection.startLineNumber); + let selectionStartPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionStartPage); + + let selectionEndPage = PagedScreenReaderStrategy._getPageOfLine(selection.endLineNumber); + let selectionEndPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionEndPage); + + let pretextRange = selectionStartPageRange.intersectRanges(new Range(1, 1, selection.startLineNumber, selection.startColumn)); + let pretext = model.getValueInRange(pretextRange, EndOfLinePreference.LF); + + let lastLine = model.getLineCount(); + let lastLineMaxColumn = model.getLineMaxColumn(lastLine); + let posttextRange = selectionEndPageRange.intersectRanges(new Range(selection.endLineNumber, selection.endColumn, lastLine, lastLineMaxColumn)); + let posttext = model.getValueInRange(posttextRange, EndOfLinePreference.LF); + + let text: string = null; + if (selectionStartPage === selectionEndPage || selectionStartPage + 1 === selectionEndPage) { + // take full selection + text = model.getValueInRange(selection, EndOfLinePreference.LF); + } else { + let selectionRange1 = selectionStartPageRange.intersectRanges(selection); + let selectionRange2 = selectionEndPageRange.intersectRanges(selection); + text = ( + model.getValueInRange(selectionRange1, EndOfLinePreference.LF) + + String.fromCharCode(8230) + + model.getValueInRange(selectionRange2, EndOfLinePreference.LF) + ); + } + + // Chromium handles very poorly text even of a few thousand chars + // Cut text to avoid stalling the entire UI + const LIMIT_CHARS = 500; + if (pretext.length > LIMIT_CHARS) { + pretext = pretext.substring(pretext.length - LIMIT_CHARS, pretext.length); + } + if (posttext.length > LIMIT_CHARS) { + posttext = posttext.substring(0, LIMIT_CHARS); + } + if (text.length > 2 * LIMIT_CHARS) { + text = text.substring(0, LIMIT_CHARS) + String.fromCharCode(8230) + text.substring(text.length - LIMIT_CHARS, text.length); + } + + return new TextAreaState(pretext + text + posttext, pretext.length, pretext.length + text.length, new Position(selection.startLineNumber, selection.startColumn), new Position(selection.endLineNumber, selection.endColumn)); + } +} diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts new file mode 100644 index 0000000000..8934802921 --- /dev/null +++ b/src/vs/editor/browser/editorBrowser.ts @@ -0,0 +1,483 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; + +/** + * A view zone is a full horizontal rectangle that 'pushes' text down. + * The editor reserves space for view zones when rendering. + */ +export interface IViewZone { + /** + * The line number after which this zone should appear. + * Use 0 to place a view zone before the first line number. + */ + afterLineNumber: number; + /** + * The column after which this zone should appear. + * If not set, the maxLineColumn of `afterLineNumber` will be used. + */ + afterColumn?: number; + /** + * Suppress mouse down events. + * If set, the editor will attach a mouse down listener to the view zone and .preventDefault on it. + * Defaults to false + */ + suppressMouseDown?: boolean; + /** + * The height in lines of the view zone. + * If specified, `heightInPx` will be used instead of this. + * If neither `heightInPx` nor `heightInLines` is specified, a default of `heightInLines` = 1 will be chosen. + */ + heightInLines?: number; + /** + * The height in px of the view zone. + * If this is set, the editor will give preference to it rather than `heightInLines` above. + * If neither `heightInPx` nor `heightInLines` is specified, a default of `heightInLines` = 1 will be chosen. + */ + heightInPx?: number; + /** + * The dom node of the view zone + */ + domNode: HTMLElement; + /** + * An optional dom node for the view zone that will be placed in the margin area. + */ + marginDomNode?: HTMLElement; + /** + * Callback which gives the relative top of the view zone as it appears (taking scrolling into account). + */ + onDomNodeTop?: (top: number) => void; + /** + * Callback which gives the height in pixels of the view zone. + */ + onComputedHeight?: (height: number) => void; +} +/** + * An accessor that allows for zones to be added or removed. + */ +export interface IViewZoneChangeAccessor { + /** + * Create a new view zone. + * @param zone Zone to create + * @return A unique identifier to the view zone. + */ + addZone(zone: IViewZone): number; + /** + * Remove a zone + * @param id A unique identifier to the view zone, as returned by the `addZone` call. + */ + removeZone(id: number): void; + /** + * Change a zone's position. + * The editor will rescan the `afterLineNumber` and `afterColumn` properties of a view zone. + */ + layoutZone(id: number): void; +} + +/** + * A positioning preference for rendering content widgets. + */ +export enum ContentWidgetPositionPreference { + /** + * Place the content widget exactly at a position + */ + EXACT, + /** + * Place the content widget above a position + */ + ABOVE, + /** + * Place the content widget below a position + */ + BELOW +} +/** + * A position for rendering content widgets. + */ +export interface IContentWidgetPosition { + /** + * Desired position for the content widget. + * `preference` will also affect the placement. + */ + position: IPosition; + /** + * Placement preference for position, in order of preference. + */ + preference: ContentWidgetPositionPreference[]; +} +/** + * A content widget renders inline with the text and can be easily placed 'near' an editor position. + */ +export interface IContentWidget { + /** + * Render this content widget in a location where it could overflow the editor's view dom node. + */ + allowEditorOverflow?: boolean; + + suppressMouseDown?: boolean; + /** + * Get a unique identifier of the content widget. + */ + getId(): string; + /** + * Get the dom node of the content widget. + */ + getDomNode(): HTMLElement; + /** + * Get the placement of the content widget. + * If null is returned, the content widget will be placed off screen. + */ + getPosition(): IContentWidgetPosition; +} + +/** + * A positioning preference for rendering overlay widgets. + */ +export enum OverlayWidgetPositionPreference { + /** + * Position the overlay widget in the top right corner + */ + TOP_RIGHT_CORNER, + + /** + * Position the overlay widget in the bottom right corner + */ + BOTTOM_RIGHT_CORNER, + + /** + * Position the overlay widget in the top center + */ + TOP_CENTER +} +/** + * A position for rendering overlay widgets. + */ +export interface IOverlayWidgetPosition { + /** + * The position preference for the overlay widget. + */ + preference: OverlayWidgetPositionPreference; +} +/** + * An overlay widgets renders on top of the text. + */ +export interface IOverlayWidget { + /** + * Get a unique identifier of the overlay widget. + */ + getId(): string; + /** + * Get the dom node of the overlay widget. + */ + getDomNode(): HTMLElement; + /** + * Get the placement of the overlay widget. + * If null is returned, the overlay widget is responsible to place itself. + */ + getPosition(): IOverlayWidgetPosition; +} + +/** + * Type of hit element with the mouse in the editor. + */ +export enum MouseTargetType { + /** + * Mouse is on top of an unknown element. + */ + UNKNOWN, + /** + * Mouse is on top of the textarea used for input. + */ + TEXTAREA, + /** + * Mouse is on top of the glyph margin + */ + GUTTER_GLYPH_MARGIN, + /** + * Mouse is on top of the line numbers + */ + GUTTER_LINE_NUMBERS, + /** + * Mouse is on top of the line decorations + */ + GUTTER_LINE_DECORATIONS, + /** + * Mouse is on top of the whitespace left in the gutter by a view zone. + */ + GUTTER_VIEW_ZONE, + /** + * Mouse is on top of text in the content. + */ + CONTENT_TEXT, + /** + * Mouse is on top of empty space in the content (e.g. after line text or below last line) + */ + CONTENT_EMPTY, + /** + * Mouse is on top of a view zone in the content. + */ + CONTENT_VIEW_ZONE, + /** + * Mouse is on top of a content widget. + */ + CONTENT_WIDGET, + /** + * Mouse is on top of the decorations overview ruler. + */ + OVERVIEW_RULER, + /** + * Mouse is on top of a scrollbar. + */ + SCROLLBAR, + /** + * Mouse is on top of an overlay widget. + */ + OVERLAY_WIDGET, + /** + * Mouse is outside of the editor. + */ + OUTSIDE_EDITOR, +} + +/** + * Target hit with the mouse in the editor. + */ +export interface IMouseTarget { + /** + * The target element + */ + readonly element: Element; + /** + * The target type + */ + readonly type: MouseTargetType; + /** + * The 'approximate' editor position + */ + readonly position: Position; + /** + * Desired mouse column (e.g. when position.column gets clamped to text length -- clicking after text on a line). + */ + readonly mouseColumn: number; + /** + * The 'approximate' editor range + */ + readonly range: Range; + /** + * Some extra detail. + */ + readonly detail: any; +} +/** + * A mouse event originating from the editor. + */ +export interface IEditorMouseEvent { + readonly event: IMouseEvent; + readonly target: IMouseTarget; +} + +/** + * @internal + */ +export type IEditorContributionCtor = IConstructorSignature1; + +/** + * An overview ruler + * @internal + */ +export interface IOverviewRuler { + getDomNode(): HTMLElement; + dispose(): void; + setZones(zones: OverviewRulerZone[]): void; + setLayout(position: editorOptions.OverviewRulerPosition): void; +} + +/** + * A rich code editor. + */ +export interface ICodeEditor extends editorCommon.ICommonCodeEditor { + /** + * An event emitted on a "mouseup". + * @event + */ + onMouseUp(listener: (e: IEditorMouseEvent) => void): IDisposable; + /** + * An event emitted on a "mousedown". + * @event + */ + onMouseDown(listener: (e: IEditorMouseEvent) => void): IDisposable; + /** + * An event emitted on a "mousedrag". + * @internal + * @event + */ + onMouseDrag(listener: (e: IEditorMouseEvent) => void): IDisposable; + /** + * An event emitted on a "mousedrop". + * @internal + * @event + */ + onMouseDrop(listener: (e: IEditorMouseEvent) => void): IDisposable; + /** + * An event emitted on a "contextmenu". + * @event + */ + onContextMenu(listener: (e: IEditorMouseEvent) => void): IDisposable; + /** + * An event emitted on a "mousemove". + * @event + */ + onMouseMove(listener: (e: IEditorMouseEvent) => void): IDisposable; + /** + * An event emitted on a "mouseleave". + * @event + */ + onMouseLeave(listener: (e: IEditorMouseEvent) => void): IDisposable; + /** + * An event emitted on a "keyup". + * @event + */ + onKeyUp(listener: (e: IKeyboardEvent) => void): IDisposable; + /** + * An event emitted on a "keydown". + * @event + */ + onKeyDown(listener: (e: IKeyboardEvent) => void): IDisposable; + /** + * An event emitted when the layout of the editor has changed. + * @event + */ + onDidLayoutChange(listener: (e: editorOptions.EditorLayoutInfo) => void): IDisposable; + /** + * An event emitted when the scroll in the editor has changed. + * @event + */ + onDidScrollChange(listener: (e: editorCommon.IScrollEvent) => void): IDisposable; + + /** + * Returns the editor's dom node + */ + getDomNode(): HTMLElement; + + /** + * Add a content widget. Widgets must have unique ids, otherwise they will be overwritten. + */ + addContentWidget(widget: IContentWidget): void; + /** + * Layout/Reposition a content widget. This is a ping to the editor to call widget.getPosition() + * and update appropiately. + */ + layoutContentWidget(widget: IContentWidget): void; + /** + * Remove a content widget. + */ + removeContentWidget(widget: IContentWidget): void; + + /** + * Add an overlay widget. Widgets must have unique ids, otherwise they will be overwritten. + */ + addOverlayWidget(widget: IOverlayWidget): void; + /** + * Layout/Reposition an overlay widget. This is a ping to the editor to call widget.getPosition() + * and update appropiately. + */ + layoutOverlayWidget(widget: IOverlayWidget): void; + /** + * Remove an overlay widget. + */ + removeOverlayWidget(widget: IOverlayWidget): void; + + /** + * Change the view zones. View zones are lost when a new model is attached to the editor. + */ + changeViewZones(callback: (accessor: IViewZoneChangeAccessor) => void): void; + + /** + * Returns the range that is currently centered in the view port. + */ + getCenteredRangeInViewport(): Range; + + /** + * Get the view zones. + * @internal + */ + getWhitespaces(): IEditorWhitespace[]; + + /** + * Get the horizontal position (left offset) for the column w.r.t to the beginning of the line. + * This method works only if the line `lineNumber` is currently rendered (in the editor's viewport). + * Use this method with caution. + */ + getOffsetForColumn(lineNumber: number, column: number): number; + + /** + * Force an editor render now. + */ + render(): void; + + /** + * Get the vertical position (top offset) for the line w.r.t. to the first line. + */ + getTopForLineNumber(lineNumber: number): number; + + /** + * Get the vertical position (top offset) for the position w.r.t. to the first line. + */ + getTopForPosition(lineNumber: number, column: number): number; + + /** + * Get the hit test target at coordinates `clientX` and `clientY`. + * The coordinates are relative to the top-left of the viewport. + * + * @returns Hit test target or null if the coordinates fall outside the editor or the editor has no model. + */ + getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget; + + /** + * Get the visible position for `position`. + * The result position takes scrolling into account and is relative to the top left corner of the editor. + * Explanation 1: the results of this method will change for the same `position` if the user scrolls the editor. + * Explanation 2: the results of this method will not change if the container of the editor gets repositioned. + * Warning: the results of this method are innacurate for positions that are outside the current editor viewport. + */ + getScrolledVisiblePosition(position: IPosition): { top: number; left: number; height: number; }; + + /** + * Set the model ranges that will be hidden in the view. + * @internal + */ + setHiddenAreas(ranges: IRange[]): void; + + /** + * @internal + */ + setAriaActiveDescendant(id: string): void; + + /** + * Apply the same font settings as the editor to `target`. + */ + applyFontInfo(target: HTMLElement): void; +} + +/** + * A rich diff editor. + */ +export interface IDiffEditor extends editorCommon.ICommonDiffEditor { + /** + * @see ICodeEditor.getDomNode + */ + getDomNode(): HTMLElement; +} diff --git a/src/vs/editor/browser/editorBrowserExtensions.ts b/src/vs/editor/browser/editorBrowserExtensions.ts new file mode 100644 index 0000000000..308cc5eda6 --- /dev/null +++ b/src/vs/editor/browser/editorBrowserExtensions.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Registry } from 'vs/platform/registry/common/platform'; +import { IEditorContributionCtor } from 'vs/editor/browser/editorBrowser'; + +export function editorContribution(ctor: IEditorContributionCtor): void { + EditorContributionRegistry.INSTANCE.registerEditorBrowserContribution(ctor); +} + +export namespace EditorBrowserRegistry { + export function getEditorContributions(): IEditorContributionCtor[] { + return EditorContributionRegistry.INSTANCE.getEditorBrowserContributions(); + } +} + +const Extensions = { + EditorContributions: 'editor.contributions' +}; + +class EditorContributionRegistry { + + public static INSTANCE = new EditorContributionRegistry(); + + private editorContributions: IEditorContributionCtor[]; + + constructor() { + this.editorContributions = []; + } + + public registerEditorBrowserContribution(ctor: IEditorContributionCtor): void { + this.editorContributions.push(ctor); + } + + public getEditorBrowserContributions(): IEditorContributionCtor[] { + return this.editorContributions.slice(0); + } +} + +Registry.add(Extensions.EditorContributions, EditorContributionRegistry.INSTANCE); diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts new file mode 100644 index 0000000000..c18b3cb4d1 --- /dev/null +++ b/src/vs/editor/browser/editorDom.ts @@ -0,0 +1,180 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import * as dom from 'vs/base/browser/dom'; +import { GlobalMouseMoveMonitor } from 'vs/base/browser/globalMouseMoveMonitor'; + +/** + * Coordinates relative to the whole document (e.g. mouse event's pageX and pageY) + */ +export class PageCoordinates { + _pageCoordinatesBrand: void; + public readonly x: number; + public readonly y: number; + + constructor(x: number, y: number) { + this.x = x; + this.y = y; + } + + public toClientCoordinates(): ClientCoordinates { + return new ClientCoordinates(this.x - dom.StandardWindow.scrollX, this.y - dom.StandardWindow.scrollY); + } +} + +/** + * Coordinates within the application's client area (i.e. origin is document's scroll position). + * + * For example, clicking in the top-left corner of the client area will + * always result in a mouse event with a client.x value of 0, regardless + * of whether the page is scrolled horizontally. + */ +export class ClientCoordinates { + _clientCoordinatesBrand: void; + + public readonly clientX: number; + public readonly clientY: number; + + constructor(clientX: number, clientY: number) { + this.clientX = clientX; + this.clientY = clientY; + } + + public toPageCoordinates(): PageCoordinates { + return new PageCoordinates(this.clientX + dom.StandardWindow.scrollX, this.clientY + dom.StandardWindow.scrollY); + } +} + +/** + * The position of the editor in the page. + */ +export class EditorPagePosition { + _editorPagePositionBrand: void; + + public readonly x: number; + public readonly y: number; + public readonly width: number; + public readonly height: number; + + constructor(x: number, y: number, width: number, height: number) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } +} + +export function createEditorPagePosition(editorViewDomNode: HTMLElement): EditorPagePosition { + let editorPos = dom.getDomNodePagePosition(editorViewDomNode); + return new EditorPagePosition(editorPos.left, editorPos.top, editorPos.width, editorPos.height); +} + +export class EditorMouseEvent extends StandardMouseEvent { + _editorMouseEventBrand: void; + + /** + * Coordinates relative to the whole document. + */ + public readonly pos: PageCoordinates; + + /** + * Editor's coordinates relative to the whole document. + */ + public readonly editorPos: EditorPagePosition; + + constructor(e: MouseEvent, editorViewDomNode: HTMLElement) { + super(e); + this.pos = new PageCoordinates(this.posx, this.posy); + this.editorPos = createEditorPagePosition(editorViewDomNode); + } +} + +export interface EditorMouseEventMerger { + (lastEvent: EditorMouseEvent, currentEvent: EditorMouseEvent): EditorMouseEvent; +} + +export class EditorMouseEventFactory { + + private _editorViewDomNode: HTMLElement; + + constructor(editorViewDomNode: HTMLElement) { + this._editorViewDomNode = editorViewDomNode; + } + + private _create(e: MouseEvent): EditorMouseEvent { + return new EditorMouseEvent(e, this._editorViewDomNode); + } + + public onContextMenu(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableListener(target, 'contextmenu', (e: MouseEvent) => { + callback(this._create(e)); + }); + } + + public onMouseUp(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableListener(target, 'mouseup', (e: MouseEvent) => { + callback(this._create(e)); + }); + } + + public onMouseDown(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableListener(target, 'mousedown', (e: MouseEvent) => { + callback(this._create(e)); + }); + } + + public onMouseLeave(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableNonBubblingMouseOutListener(target, (e: MouseEvent) => { + callback(this._create(e)); + }); + } + + public onMouseMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { + let myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { + return merger(lastEvent, this._create(currentEvent)); + }; + return dom.addDisposableThrottledListener(target, 'mousemove', callback, myMerger, minimumTimeMs); + } +} + +export class GlobalEditorMouseMoveMonitor extends Disposable { + + private _editorViewDomNode: HTMLElement; + private _globalMouseMoveMonitor: GlobalMouseMoveMonitor; + private _keydownListener: IDisposable; + + constructor(editorViewDomNode: HTMLElement) { + super(); + this._editorViewDomNode = editorViewDomNode; + this._globalMouseMoveMonitor = this._register(new GlobalMouseMoveMonitor()); + this._keydownListener = null; + } + + public startMonitoring(merger: EditorMouseEventMerger, mouseMoveCallback: (e: EditorMouseEvent) => void, onStopCallback: () => void): void { + + // Add a <> keydown event listener that will cancel the monitoring + // if something other than a modifier key is pressed + this._keydownListener = dom.addStandardDisposableListener(document, 'keydown', (e) => { + const kb = e.toKeybinding(); + if (kb.isModifierKey()) { + // Allow modifier keys + return; + } + this._globalMouseMoveMonitor.stopMonitoring(true); + }, true); + + let myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { + return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode)); + }; + + this._globalMouseMoveMonitor.startMonitoring(myMerger, mouseMoveCallback, () => { + this._keydownListener.dispose(); + onStopCallback(); + }); + } +} diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts new file mode 100644 index 0000000000..0c6ade142e --- /dev/null +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -0,0 +1,476 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import URI from 'vs/base/common/uri'; +import * as dom from 'vs/base/browser/dom'; +import { + IDecorationRenderOptions, IModelDecorationOptions, IModelDecorationOverviewRulerOptions, IThemeDecorationRenderOptions, + IContentDecorationRenderOptions, OverviewRulerLane, TrackedRangeStickiness, isThemeColor +} from 'vs/editor/common/editorCommon'; +import { AbstractCodeEditorService } from 'vs/editor/common/services/abstractCodeEditorService'; +import { IDisposable, dispose as disposeAll } from 'vs/base/common/lifecycle'; +import { IThemeService, ITheme, ThemeColor } from 'vs/platform/theme/common/themeService'; + +export class CodeEditorServiceImpl extends AbstractCodeEditorService { + + private _styleSheet: HTMLStyleElement; + private _decorationOptionProviders: { [key: string]: IModelDecorationOptionsProvider }; + private _themeService: IThemeService; + + constructor( @IThemeService themeService: IThemeService, styleSheet = dom.createStyleSheet()) { + super(); + this._styleSheet = styleSheet; + this._decorationOptionProviders = Object.create(null); + this._themeService = themeService; + } + + public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void { + let provider = this._decorationOptionProviders[key]; + if (!provider) { + let providerArgs: ProviderArguments = { + styleSheet: this._styleSheet, + key: key, + parentTypeKey: parentTypeKey, + options: options + }; + if (!parentTypeKey) { + provider = new DecorationTypeOptionsProvider(this._themeService, providerArgs); + } else { + provider = new DecorationSubTypeOptionsProvider(this._themeService, providerArgs); + } + this._decorationOptionProviders[key] = provider; + } + provider.refCount++; + } + + public removeDecorationType(key: string): void { + let provider = this._decorationOptionProviders[key]; + if (provider) { + provider.refCount--; + if (provider.refCount <= 0) { + delete this._decorationOptionProviders[key]; + provider.dispose(); + this.listCodeEditors().forEach((ed) => ed.removeDecorations(key)); + } + } + } + + public resolveDecorationOptions(decorationTypeKey: string, writable: boolean): IModelDecorationOptions { + let provider = this._decorationOptionProviders[decorationTypeKey]; + if (!provider) { + throw new Error('Unknown decoration type key: ' + decorationTypeKey); + } + return provider.getOptions(this, writable); + } + +} + +interface IModelDecorationOptionsProvider extends IDisposable { + refCount: number; + getOptions(codeEditorService: AbstractCodeEditorService, writable: boolean): IModelDecorationOptions; +} + +class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvider { + + public refCount: number; + + private _parentTypeKey: string; + private _beforeContentRules: DecorationCSSRules; + private _afterContentRules: DecorationCSSRules; + + constructor(themeService: IThemeService, providerArgs: ProviderArguments) { + this._parentTypeKey = providerArgs.parentTypeKey; + this.refCount = 0; + + this._beforeContentRules = new DecorationCSSRules(ModelDecorationCSSRuleType.BeforeContentClassName, providerArgs, themeService); + this._afterContentRules = new DecorationCSSRules(ModelDecorationCSSRuleType.AfterContentClassName, providerArgs, themeService); + } + + public getOptions(codeEditorService: AbstractCodeEditorService, writable: boolean): IModelDecorationOptions { + let options = codeEditorService.resolveDecorationOptions(this._parentTypeKey, true); + if (this._beforeContentRules) { + options.beforeContentClassName = this._beforeContentRules.className; + } + if (this._afterContentRules) { + options.afterContentClassName = this._afterContentRules.className; + } + return options; + } + + public dispose(): void { + if (this._beforeContentRules) { + this._beforeContentRules.dispose(); + this._beforeContentRules = null; + } + if (this._afterContentRules) { + this._afterContentRules.dispose(); + this._afterContentRules = null; + } + } +} + +interface ProviderArguments { + styleSheet: HTMLStyleElement; + key: string; + parentTypeKey?: string; + options: IDecorationRenderOptions; +} + + +class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { + + private _disposables: IDisposable[]; + public refCount: number; + + public className: string; + public inlineClassName: string; + public beforeContentClassName: string; + public afterContentClassName: string; + public glyphMarginClassName: string; + public isWholeLine: boolean; + public overviewRuler: IModelDecorationOverviewRulerOptions; + public stickiness: TrackedRangeStickiness; + + constructor(themeService: IThemeService, providerArgs: ProviderArguments) { + this.refCount = 0; + this._disposables = []; + + let createCSSRules = (type: ModelDecorationCSSRuleType) => { + let rules = new DecorationCSSRules(type, providerArgs, themeService); + if (rules.hasContent) { + this._disposables.push(rules); + return rules.className; + } + return void 0; + }; + + this.className = createCSSRules(ModelDecorationCSSRuleType.ClassName); + this.inlineClassName = createCSSRules(ModelDecorationCSSRuleType.InlineClassName); + this.beforeContentClassName = createCSSRules(ModelDecorationCSSRuleType.BeforeContentClassName); + this.afterContentClassName = createCSSRules(ModelDecorationCSSRuleType.AfterContentClassName); + this.glyphMarginClassName = createCSSRules(ModelDecorationCSSRuleType.GlyphMarginClassName); + + let options = providerArgs.options; + this.isWholeLine = Boolean(options.isWholeLine); + this.stickiness = options.rangeBehavior; + + let lightOverviewRulerColor = options.light && options.light.overviewRulerColor || options.overviewRulerColor; + let darkOverviewRulerColor = options.dark && options.dark.overviewRulerColor || options.overviewRulerColor; + if ( + typeof lightOverviewRulerColor !== 'undefined' + || typeof darkOverviewRulerColor !== 'undefined' + ) { + this.overviewRuler = { + color: lightOverviewRulerColor || darkOverviewRulerColor, + darkColor: darkOverviewRulerColor || lightOverviewRulerColor, + position: options.overviewRulerLane || OverviewRulerLane.Center + }; + } + } + + public getOptions(codeEditorService: AbstractCodeEditorService, writable: boolean): IModelDecorationOptions { + if (!writable) { + return this; + } + return { + inlineClassName: this.inlineClassName, + beforeContentClassName: this.beforeContentClassName, + afterContentClassName: this.afterContentClassName, + className: this.className, + glyphMarginClassName: this.glyphMarginClassName, + isWholeLine: this.isWholeLine, + overviewRuler: this.overviewRuler, + stickiness: this.stickiness + }; + } + + public dispose(): void { + this._disposables = disposeAll(this._disposables); + } +} + + +const _CSS_MAP = { + color: 'color:{0} !important;', + backgroundColor: 'background-color:{0};', + + outline: 'outline:{0};', + outlineColor: 'outline-color:{0};', + outlineStyle: 'outline-style:{0};', + outlineWidth: 'outline-width:{0};', + + border: 'border:{0};', + borderColor: 'border-color:{0};', + borderRadius: 'border-radius:{0};', + borderSpacing: 'border-spacing:{0};', + borderStyle: 'border-style:{0};', + borderWidth: 'border-width:{0};', + + textDecoration: 'text-decoration:{0};', + cursor: 'cursor:{0};', + letterSpacing: 'letter-spacing:{0};', + + gutterIconPath: 'background:url(\'{0}\') center center no-repeat;', + gutterIconSize: 'background-size:{0};', + + contentText: 'content:\'{0}\';', + contentIconPath: 'content:url(\'{0}\');', + margin: 'margin:{0};', + width: 'width:{0};', + height: 'height:{0};' +}; + + +class DecorationCSSRules { + + private _theme: ITheme; + private _className: string; + private _unThemedSelector: string; + private _hasContent: boolean; + private _ruleType: ModelDecorationCSSRuleType; + private _themeListener: IDisposable; + private _providerArgs: ProviderArguments; + private _usesThemeColors: boolean; + + public constructor(ruleType: ModelDecorationCSSRuleType, providerArgs: ProviderArguments, themeService: IThemeService) { + this._theme = themeService.getTheme(); + this._ruleType = ruleType; + this._providerArgs = providerArgs; + this._usesThemeColors = false; + this._hasContent = false; + + let className = CSSNameHelper.getClassName(this._providerArgs.key, ruleType); + if (this._providerArgs.parentTypeKey) { + className = className + ' ' + CSSNameHelper.getClassName(this._providerArgs.parentTypeKey, ruleType); + } + this._className = className; + + this._unThemedSelector = CSSNameHelper.getSelector(this._providerArgs.key, this._providerArgs.parentTypeKey, ruleType); + + this._buildCSS(); + + if (this._usesThemeColors) { + this._themeListener = themeService.onThemeChange(theme => { + this._theme = themeService.getTheme(); + this._removeCSS(); + this._buildCSS(); + }); + } + } + + public dispose() { + if (this._hasContent) { + this._removeCSS(); + this._hasContent = false; + } + if (this._themeListener) { + this._themeListener.dispose(); + this._themeListener = null; + } + } + + public get hasContent(): boolean { + return this._hasContent; + } + + public get className(): string { + return this._className; + } + + private _buildCSS(): void { + let options = this._providerArgs.options; + let unthemedCSS, lightCSS, darkCSS: string; + switch (this._ruleType) { + case ModelDecorationCSSRuleType.ClassName: + unthemedCSS = this.getCSSTextForModelDecorationClassName(options); + lightCSS = this.getCSSTextForModelDecorationClassName(options.light); + darkCSS = this.getCSSTextForModelDecorationClassName(options.dark); + break; + case ModelDecorationCSSRuleType.InlineClassName: + unthemedCSS = this.getCSSTextForModelDecorationInlineClassName(options); + lightCSS = this.getCSSTextForModelDecorationInlineClassName(options.light); + darkCSS = this.getCSSTextForModelDecorationInlineClassName(options.dark); + break; + case ModelDecorationCSSRuleType.GlyphMarginClassName: + unthemedCSS = this.getCSSTextForModelDecorationGlyphMarginClassName(options); + lightCSS = this.getCSSTextForModelDecorationGlyphMarginClassName(options.light); + darkCSS = this.getCSSTextForModelDecorationGlyphMarginClassName(options.dark); + break; + case ModelDecorationCSSRuleType.BeforeContentClassName: + unthemedCSS = this.getCSSTextForModelDecorationContentClassName(options.before); + lightCSS = this.getCSSTextForModelDecorationContentClassName(options.light && options.light.before); + darkCSS = this.getCSSTextForModelDecorationContentClassName(options.dark && options.dark.before); + break; + case ModelDecorationCSSRuleType.AfterContentClassName: + unthemedCSS = this.getCSSTextForModelDecorationContentClassName(options.after); + lightCSS = this.getCSSTextForModelDecorationContentClassName(options.light && options.light.after); + darkCSS = this.getCSSTextForModelDecorationContentClassName(options.dark && options.dark.after); + break; + default: + throw new Error('Unknown rule type: ' + this._ruleType); + } + let sheet = this._providerArgs.styleSheet.sheet; + + let hasContent = false; + if (unthemedCSS.length > 0) { + sheet.insertRule(`${this._unThemedSelector} {${unthemedCSS}}`, 0); + hasContent = true; + } + if (lightCSS.length > 0) { + sheet.insertRule(`.vs${this._unThemedSelector} {${lightCSS}}`, 0); + hasContent = true; + } + if (darkCSS.length > 0) { + sheet.insertRule(`.vs-dark${this._unThemedSelector}, .hc-black${this._unThemedSelector} {${darkCSS}}`, 0); + hasContent = true; + } + this._hasContent = hasContent; + } + + private _removeCSS(): void { + dom.removeCSSRulesContainingSelector(this._unThemedSelector, this._providerArgs.styleSheet); + } + + /** + * Build the CSS for decorations styled via `className`. + */ + private getCSSTextForModelDecorationClassName(opts: IThemeDecorationRenderOptions): string { + if (!opts) { + return ''; + } + let cssTextArr: string[] = []; + this.collectCSSText(opts, ['backgroundColor'], cssTextArr); + this.collectCSSText(opts, ['outline', 'outlineColor', 'outlineStyle', 'outlineWidth'], cssTextArr); + this.collectBorderSettingsCSSText(opts, cssTextArr); + return cssTextArr.join(''); + } + + /** + * Build the CSS for decorations styled via `inlineClassName`. + */ + private getCSSTextForModelDecorationInlineClassName(opts: IThemeDecorationRenderOptions): string { + if (!opts) { + return ''; + } + let cssTextArr: string[] = []; + this.collectCSSText(opts, ['textDecoration', 'cursor', 'color', 'letterSpacing'], cssTextArr); + return cssTextArr.join(''); + } + + /** + * Build the CSS for decorations styled before or after content. + */ + private getCSSTextForModelDecorationContentClassName(opts: IContentDecorationRenderOptions): string { + if (!opts) { + return ''; + } + let cssTextArr: string[] = []; + + if (typeof opts !== 'undefined') { + this.collectBorderSettingsCSSText(opts, cssTextArr); + if (typeof opts.contentIconPath === 'string') { + cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, URI.file(opts.contentIconPath).toString().replace(/'/g, '%27'))); + } else if (opts.contentIconPath instanceof URI) { + cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, opts.contentIconPath.toString(true).replace(/'/g, '%27'))); + } + if (typeof opts.contentText === 'string') { + const truncated = opts.contentText.match(/^.*$/m)[0]; // only take first line + const escaped = truncated.replace(/['\\]/g, '\\$&'); + + cssTextArr.push(strings.format(_CSS_MAP.contentText, escaped)); + } + this.collectCSSText(opts, ['textDecoration', 'color', 'backgroundColor', 'margin'], cssTextArr); + if (this.collectCSSText(opts, ['width', 'height'], cssTextArr)) { + cssTextArr.push('display:inline-block;'); + } + } + + return cssTextArr.join(''); + } + + /** + * Build the CSS for decorations styled via `glpyhMarginClassName`. + */ + private getCSSTextForModelDecorationGlyphMarginClassName(opts: IThemeDecorationRenderOptions): string { + if (!opts) { + return ''; + } + let cssTextArr = []; + + if (typeof opts.gutterIconPath !== 'undefined') { + if (typeof opts.gutterIconPath === 'string') { + cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.file(opts.gutterIconPath).toString())); + } else { + cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, opts.gutterIconPath.toString(true).replace(/'/g, '%27'))); + } + if (typeof opts.gutterIconSize !== 'undefined') { + cssTextArr.push(strings.format(_CSS_MAP.gutterIconSize, opts.gutterIconSize)); + } + } + + return cssTextArr.join(''); + } + + private collectBorderSettingsCSSText(opts: any, cssTextArr: string[]): boolean { + if (this.collectCSSText(opts, ['border', 'borderColor', 'borderRadius', 'borderSpacing', 'borderStyle', 'borderWidth'], cssTextArr)) { + cssTextArr.push(strings.format('box-sizing: border-box;')); + return true; + } + return false; + } + + private collectCSSText(opts: any, properties: string[], cssTextArr: string[]): boolean { + let lenBefore = cssTextArr.length; + for (let property of properties) { + let value = this.resolveValue(opts[property]); + if (typeof value === 'string') { + cssTextArr.push(strings.format(_CSS_MAP[property], value)); + } + } + return cssTextArr.length !== lenBefore; + } + + private resolveValue(value: string | ThemeColor): string { + if (isThemeColor(value)) { + this._usesThemeColors = true; + let color = this._theme.getColor(value.id); + if (color) { + return color.toString(); + } + return 'transparent'; + } + return value; + } +} + +const enum ModelDecorationCSSRuleType { + ClassName = 0, + InlineClassName = 1, + GlyphMarginClassName = 2, + BeforeContentClassName = 3, + AfterContentClassName = 4 +} + +class CSSNameHelper { + + public static getClassName(key: string, type: ModelDecorationCSSRuleType): string { + return 'ced-' + key + '-' + type; + } + + public static getSelector(key: string, parentKey: string, ruleType: ModelDecorationCSSRuleType): string { + let selector = '.monaco-editor .' + this.getClassName(key, ruleType); + if (parentKey) { + selector = selector + '.' + this.getClassName(parentKey, ruleType); + } + if (ruleType === ModelDecorationCSSRuleType.BeforeContentClassName) { + selector += '::before'; + } else if (ruleType === ModelDecorationCSSRuleType.AfterContentClassName) { + selector += '::after'; + } + return selector; + } +} diff --git a/src/vs/editor/browser/view/dynamicViewOverlay.ts b/src/vs/editor/browser/view/dynamicViewOverlay.ts new file mode 100644 index 0000000000..cdb9d4213d --- /dev/null +++ b/src/vs/editor/browser/view/dynamicViewOverlay.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { RenderingContext } from 'vs/editor/common/view/renderingContext'; + +export abstract class DynamicViewOverlay extends ViewEventHandler { + + public abstract prepareRender(ctx: RenderingContext): void; + + public abstract render(startLineNumber: number, lineNumber: number): string; + +} diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts new file mode 100644 index 0000000000..b83b3093df --- /dev/null +++ b/src/vs/editor/browser/view/viewController.ts @@ -0,0 +1,313 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; +import { CoreNavigationCommands, CoreEditorCommand } from 'vs/editor/common/controller/coreCommands'; +import { Configuration } from 'vs/editor/browser/config/configuration'; + +export interface ExecCoreEditorCommandFunc { + (editorCommand: CoreEditorCommand, args: any): void; +} + +export interface IMouseDispatchData { + position: Position; + /** + * Desired mouse column (e.g. when position.column gets clamped to text length -- clicking after text on a line). + */ + mouseColumn: number; + startedOnLineNumbers: boolean; + + inSelectionMode: boolean; + mouseDownCount: number; + altKey: boolean; + ctrlKey: boolean; + metaKey: boolean; + shiftKey: boolean; +} + +export class ViewController { + + private readonly configuration: Configuration; + private readonly viewModel: IViewModel; + private readonly _execCoreEditorCommandFunc: ExecCoreEditorCommandFunc; + private readonly outgoingEvents: ViewOutgoingEvents; + private readonly commandService: ICommandService; + + constructor( + configuration: Configuration, + viewModel: IViewModel, + execCommandFunc: ExecCoreEditorCommandFunc, + outgoingEvents: ViewOutgoingEvents, + commandService: ICommandService + ) { + this.configuration = configuration; + this.viewModel = viewModel; + this._execCoreEditorCommandFunc = execCommandFunc; + this.outgoingEvents = outgoingEvents; + this.commandService = commandService; + } + + private _execMouseCommand(editorCommand: CoreEditorCommand, args: any): void { + args.source = 'mouse'; + this._execCoreEditorCommandFunc(editorCommand, args); + } + + public paste(source: string, text: string, pasteOnNewLine: boolean): void { + this.commandService.executeCommand(editorCommon.Handler.Paste, { + text: text, + pasteOnNewLine: pasteOnNewLine, + }); + } + + public type(source: string, text: string): void { + this.commandService.executeCommand(editorCommon.Handler.Type, { + text: text + }); + } + + public replacePreviousChar(source: string, text: string, replaceCharCnt: number): void { + this.commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, { + text: text, + replaceCharCnt: replaceCharCnt + }); + } + + public compositionStart(source: string): void { + this.commandService.executeCommand(editorCommon.Handler.CompositionStart, {}); + } + + public compositionEnd(source: string): void { + this.commandService.executeCommand(editorCommon.Handler.CompositionEnd, {}); + } + + public cut(source: string): void { + this.commandService.executeCommand(editorCommon.Handler.Cut, {}); + } + + public setSelection(source: string, modelSelection: Selection): void { + this._execCoreEditorCommandFunc(CoreNavigationCommands.SetSelection, { + source: source, + selection: modelSelection + }); + } + + private _validateViewColumn(viewPosition: Position): Position { + let minColumn = this.viewModel.getLineMinColumn(viewPosition.lineNumber); + if (viewPosition.column < minColumn) { + return new Position(viewPosition.lineNumber, minColumn); + } + return viewPosition; + } + + private _hasMulticursorModifier(data: IMouseDispatchData): boolean { + switch (this.configuration.editor.multiCursorModifier) { + case 'altKey': + return data.altKey; + case 'ctrlKey': + return data.ctrlKey; + case 'metaKey': + return data.metaKey; + } + return false; + } + + private _hasNonMulticursorModifier(data: IMouseDispatchData): boolean { + switch (this.configuration.editor.multiCursorModifier) { + case 'altKey': + return data.ctrlKey || data.metaKey; + case 'ctrlKey': + return data.altKey || data.metaKey; + case 'metaKey': + return data.ctrlKey || data.altKey; + } + return false; + } + + public dispatchMouse(data: IMouseDispatchData): void { + if (data.startedOnLineNumbers) { + // If the dragging started on the gutter, then have operations work on the entire line + if (this._hasMulticursorModifier(data)) { + if (data.inSelectionMode) { + this.lastCursorLineSelect(data.position); + } else { + this.createCursor(data.position, true); + } + } else { + if (data.inSelectionMode) { + this.lineSelectDrag(data.position); + } else { + this.lineSelect(data.position); + } + } + } else if (data.mouseDownCount >= 4) { + this.selectAll(); + } else if (data.mouseDownCount === 3) { + if (this._hasMulticursorModifier(data)) { + if (data.inSelectionMode) { + this.lastCursorLineSelectDrag(data.position); + } else { + this.lastCursorLineSelect(data.position); + } + } else { + if (data.inSelectionMode) { + this.lineSelectDrag(data.position); + } else { + this.lineSelect(data.position); + } + } + } else if (data.mouseDownCount === 2) { + if (this._hasMulticursorModifier(data)) { + this.lastCursorWordSelect(data.position); + } else { + if (data.inSelectionMode) { + this.wordSelectDrag(data.position); + } else { + this.wordSelect(data.position); + } + } + } else { + if (this._hasMulticursorModifier(data)) { + if (!this._hasNonMulticursorModifier(data)) { + if (data.shiftKey) { + this.columnSelect(data.position, data.mouseColumn); + } else { + // Do multi-cursor operations only when purely alt is pressed + if (data.inSelectionMode) { + this.lastCursorMoveToSelect(data.position); + } else { + this.createCursor(data.position, false); + } + } + } + } else { + if (data.inSelectionMode) { + this.moveToSelect(data.position); + } else { + this.moveTo(data.position); + } + } + } + } + + private _usualArgs(viewPosition: Position) { + viewPosition = this._validateViewColumn(viewPosition); + return { + position: this.convertViewToModelPosition(viewPosition), + viewPosition: viewPosition + }; + } + + public moveTo(viewPosition: Position): void { + this._execMouseCommand(CoreNavigationCommands.MoveTo, this._usualArgs(viewPosition)); + } + + private moveToSelect(viewPosition: Position): void { + this._execMouseCommand(CoreNavigationCommands.MoveToSelect, this._usualArgs(viewPosition)); + } + + private columnSelect(viewPosition: Position, mouseColumn: number): void { + viewPosition = this._validateViewColumn(viewPosition); + this._execMouseCommand(CoreNavigationCommands.ColumnSelect, { + position: this.convertViewToModelPosition(viewPosition), + viewPosition: viewPosition, + mouseColumn: mouseColumn + }); + } + + private createCursor(viewPosition: Position, wholeLine: boolean): void { + viewPosition = this._validateViewColumn(viewPosition); + this._execMouseCommand(CoreNavigationCommands.CreateCursor, { + position: this.convertViewToModelPosition(viewPosition), + viewPosition: viewPosition, + wholeLine: wholeLine + }); + } + + private lastCursorMoveToSelect(viewPosition: Position): void { + this._execMouseCommand(CoreNavigationCommands.LastCursorMoveToSelect, this._usualArgs(viewPosition)); + } + + private wordSelect(viewPosition: Position): void { + this._execMouseCommand(CoreNavigationCommands.WordSelect, this._usualArgs(viewPosition)); + } + + private wordSelectDrag(viewPosition: Position): void { + this._execMouseCommand(CoreNavigationCommands.WordSelectDrag, this._usualArgs(viewPosition)); + } + + private lastCursorWordSelect(viewPosition: Position): void { + this._execMouseCommand(CoreNavigationCommands.LastCursorWordSelect, this._usualArgs(viewPosition)); + } + + private lineSelect(viewPosition: Position): void { + this._execMouseCommand(CoreNavigationCommands.LineSelect, this._usualArgs(viewPosition)); + } + + private lineSelectDrag(viewPosition: Position): void { + this._execMouseCommand(CoreNavigationCommands.LineSelectDrag, this._usualArgs(viewPosition)); + } + + private lastCursorLineSelect(viewPosition: Position): void { + this._execMouseCommand(CoreNavigationCommands.LastCursorLineSelect, this._usualArgs(viewPosition)); + } + + private lastCursorLineSelectDrag(viewPosition: Position): void { + this._execMouseCommand(CoreNavigationCommands.LastCursorLineSelectDrag, this._usualArgs(viewPosition)); + } + + private selectAll(): void { + this._execMouseCommand(CoreNavigationCommands.SelectAll, {}); + } + + // ---------------------- + + private convertViewToModelPosition(viewPosition: Position): Position { + return this.viewModel.coordinatesConverter.convertViewPositionToModelPosition(viewPosition); + } + + public emitKeyDown(e: IKeyboardEvent): void { + this.outgoingEvents.emitKeyDown(e); + } + + public emitKeyUp(e: IKeyboardEvent): void { + this.outgoingEvents.emitKeyUp(e); + } + + public emitContextMenu(e: IEditorMouseEvent): void { + this.outgoingEvents.emitContextMenu(e); + } + + public emitMouseMove(e: IEditorMouseEvent): void { + this.outgoingEvents.emitMouseMove(e); + } + + public emitMouseLeave(e: IEditorMouseEvent): void { + this.outgoingEvents.emitMouseLeave(e); + } + + public emitMouseUp(e: IEditorMouseEvent): void { + this.outgoingEvents.emitMouseUp(e); + } + + public emitMouseDown(e: IEditorMouseEvent): void { + this.outgoingEvents.emitMouseDown(e); + } + + public emitMouseDrag(e: IEditorMouseEvent): void { + this.outgoingEvents.emitMouseDrag(e); + } + + public emitMouseDrop(e: IEditorMouseEvent): void { + this.outgoingEvents.emitMouseDrop(e); + } +} diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts new file mode 100644 index 0000000000..383f7f83ba --- /dev/null +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -0,0 +1,597 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import * as dom from 'vs/base/browser/dom'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { Range } from 'vs/editor/common/core/range'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { Configuration } from 'vs/editor/browser/config/configuration'; +import { TextAreaHandler, ITextAreaHandlerHelper } from 'vs/editor/browser/controller/textAreaHandler'; +import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { ViewController, ExecCoreEditorCommandFunc } from 'vs/editor/browser/view/viewController'; +import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher'; +import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays'; +import { ViewContentWidgets } from 'vs/editor/browser/viewParts/contentWidgets/contentWidgets'; +import { CurrentLineHighlightOverlay } from 'vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight'; +import { CurrentLineMarginHighlightOverlay } from 'vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight'; +import { DecorationsOverlay } from 'vs/editor/browser/viewParts/decorations/decorations'; +import { GlyphMarginOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin'; +import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers'; +import { IndentGuidesOverlay } from 'vs/editor/browser/viewParts/indentGuides/indentGuides'; +import { ViewLines } from 'vs/editor/browser/viewParts/lines/viewLines'; +import { Margin } from 'vs/editor/browser/viewParts/margin/margin'; +import { LinesDecorationsOverlay } from 'vs/editor/browser/viewParts/linesDecorations/linesDecorations'; +import { MarginViewLineDecorationsOverlay } from 'vs/editor/browser/viewParts/marginDecorations/marginDecorations'; +import { ViewOverlayWidgets } from 'vs/editor/browser/viewParts/overlayWidgets/overlayWidgets'; +import { DecorationsOverviewRuler } from 'vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler'; +import { OverviewRuler } from 'vs/editor/browser/viewParts/overviewRuler/overviewRuler'; +import { Rulers } from 'vs/editor/browser/viewParts/rulers/rulers'; +import { ScrollDecorationViewPart } from 'vs/editor/browser/viewParts/scrollDecoration/scrollDecoration'; +import { SelectionsOverlay } from 'vs/editor/browser/viewParts/selections/selections'; +import { ViewCursors } from 'vs/editor/browser/viewParts/viewCursors/viewCursors'; +import { ViewZones } from 'vs/editor/browser/viewParts/viewZones/viewZones'; +import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; +import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar'; +import { Minimap } from 'vs/editor/browser/viewParts/minimap/minimap'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { IThemeService, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; +import { Cursor } from 'vs/editor/common/controller/cursor'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; + +export interface IContentWidgetData { + widget: editorBrowser.IContentWidget; + position: editorBrowser.IContentWidgetPosition; +} + +export interface IOverlayWidgetData { + widget: editorBrowser.IOverlayWidget; + position: editorBrowser.IOverlayWidgetPosition; +} + +export class View extends ViewEventHandler { + + private eventDispatcher: ViewEventDispatcher; + + private _scrollbar: EditorScrollbar; + private _context: ViewContext; + private _cursor: Cursor; + + // The view lines + private viewLines: ViewLines; + + // These are parts, but we must do some API related calls on them, so we keep a reference + private viewZones: ViewZones; + private contentWidgets: ViewContentWidgets; + private overlayWidgets: ViewOverlayWidgets; + private viewCursors: ViewCursors; + private viewParts: ViewPart[]; + + private readonly _textAreaHandler: TextAreaHandler; + private readonly pointerHandler: PointerHandler; + + private readonly outgoingEvents: ViewOutgoingEvents; + + // Dom nodes + private linesContent: FastDomNode; + public domNode: FastDomNode; + private overflowGuardContainer: FastDomNode; + + // Actual mutable state + private _isDisposed: boolean; + + private _renderAnimationFrame: IDisposable; + + constructor( + commandService: ICommandService, + configuration: Configuration, + themeService: IThemeService, + model: IViewModel, + cursor: Cursor, + execCoreEditorCommandFunc: ExecCoreEditorCommandFunc + ) { + super(); + this._isDisposed = false; + this._cursor = cursor; + this._renderAnimationFrame = null; + this.outgoingEvents = new ViewOutgoingEvents(model); + + let viewController = new ViewController(configuration, model, execCoreEditorCommandFunc, this.outgoingEvents, commandService); + + // The event dispatcher will always go through _renderOnce before dispatching any events + this.eventDispatcher = new ViewEventDispatcher((callback: () => void) => this._renderOnce(callback)); + + // Ensure the view is the first event handler in order to update the layout + this.eventDispatcher.addEventHandler(this); + + // The view context is passed on to most classes (basically to reduce param. counts in ctors) + this._context = new ViewContext(configuration, themeService.getTheme(), model, this.eventDispatcher); + + this._register(themeService.onThemeChange(theme => { + this._context.theme = theme; + this.eventDispatcher.emit(new viewEvents.ViewThemeChangedEvent()); + this.render(true, false); + })); + + this.viewParts = []; + + // Keyboard handler + this._textAreaHandler = new TextAreaHandler(this._context, viewController, this.createTextAreaHandlerHelper()); + this.viewParts.push(this._textAreaHandler); + + this.createViewParts(); + this._setLayout(); + + // Pointer handler + this.pointerHandler = new PointerHandler(this._context, viewController, this.createPointerHandlerHelper()); + + this._register(model.addEventListener((events: viewEvents.ViewEvent[]) => { + this.eventDispatcher.emitMany(events); + })); + + this._register(this._cursor.addEventListener((events: viewEvents.ViewEvent[]) => { + this.eventDispatcher.emitMany(events); + })); + } + + private createViewParts(): void { + // These two dom nodes must be constructed up front, since references are needed in the layout provider (scrolling & co.) + this.linesContent = createFastDomNode(document.createElement('div')); + this.linesContent.setClassName('lines-content' + ' monaco-editor-background'); + this.linesContent.setPosition('absolute'); + + this.domNode = createFastDomNode(document.createElement('div')); + this.domNode.setClassName(this.getEditorClassName()); + + this.overflowGuardContainer = createFastDomNode(document.createElement('div')); + PartFingerprints.write(this.overflowGuardContainer, PartFingerprint.OverflowGuard); + this.overflowGuardContainer.setClassName('overflow-guard'); + + this._scrollbar = new EditorScrollbar(this._context, this.linesContent, this.domNode, this.overflowGuardContainer); + this.viewParts.push(this._scrollbar); + + // View Lines + this.viewLines = new ViewLines(this._context, this.linesContent); + + // View Zones + this.viewZones = new ViewZones(this._context); + this.viewParts.push(this.viewZones); + + // Decorations overview ruler + let decorationsOverviewRuler = new DecorationsOverviewRuler(this._context); + this.viewParts.push(decorationsOverviewRuler); + + + let scrollDecoration = new ScrollDecorationViewPart(this._context); + this.viewParts.push(scrollDecoration); + + let contentViewOverlays = new ContentViewOverlays(this._context); + this.viewParts.push(contentViewOverlays); + contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context)); + contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context)); + contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context)); + contentViewOverlays.addDynamicOverlay(new IndentGuidesOverlay(this._context)); + + let marginViewOverlays = new MarginViewOverlays(this._context); + this.viewParts.push(marginViewOverlays); + marginViewOverlays.addDynamicOverlay(new CurrentLineMarginHighlightOverlay(this._context)); + marginViewOverlays.addDynamicOverlay(new GlyphMarginOverlay(this._context)); + marginViewOverlays.addDynamicOverlay(new MarginViewLineDecorationsOverlay(this._context)); + marginViewOverlays.addDynamicOverlay(new LinesDecorationsOverlay(this._context)); + marginViewOverlays.addDynamicOverlay(new LineNumbersOverlay(this._context)); + + let margin = new Margin(this._context); + margin.getDomNode().appendChild(this.viewZones.marginDomNode); + margin.getDomNode().appendChild(marginViewOverlays.getDomNode()); + this.viewParts.push(margin); + + // Content widgets + this.contentWidgets = new ViewContentWidgets(this._context, this.domNode); + this.viewParts.push(this.contentWidgets); + + this.viewCursors = new ViewCursors(this._context); + this.viewParts.push(this.viewCursors); + + // Overlay widgets + this.overlayWidgets = new ViewOverlayWidgets(this._context); + this.viewParts.push(this.overlayWidgets); + + let rulers = new Rulers(this._context); + this.viewParts.push(rulers); + + let minimap = new Minimap(this._context); + this.viewParts.push(minimap); + + // -------------- Wire dom nodes up + + if (decorationsOverviewRuler) { + let overviewRulerData = this._scrollbar.getOverviewRulerLayoutInfo(); + overviewRulerData.parent.insertBefore(decorationsOverviewRuler.getDomNode(), overviewRulerData.insertBefore); + } + + this.linesContent.appendChild(contentViewOverlays.getDomNode()); + this.linesContent.appendChild(rulers.domNode); + this.linesContent.appendChild(this.viewZones.domNode); + this.linesContent.appendChild(this.viewLines.getDomNode()); + this.linesContent.appendChild(this.contentWidgets.domNode); + this.linesContent.appendChild(this.viewCursors.getDomNode()); + this.overflowGuardContainer.appendChild(margin.getDomNode()); + this.overflowGuardContainer.appendChild(this._scrollbar.getDomNode()); + this.overflowGuardContainer.appendChild(scrollDecoration.getDomNode()); + this.overflowGuardContainer.appendChild(this._textAreaHandler.textArea); + this.overflowGuardContainer.appendChild(this._textAreaHandler.textAreaCover); + this.overflowGuardContainer.appendChild(this.overlayWidgets.getDomNode()); + this.overflowGuardContainer.appendChild(minimap.getDomNode()); + this.domNode.appendChild(this.overflowGuardContainer); + this.domNode.appendChild(this.contentWidgets.overflowingContentWidgetsDomNode); + } + + private _flushAccumulatedAndRenderNow(): void { + this._renderNow(); + } + + private createPointerHandlerHelper(): IPointerHandlerHelper { + return { + viewDomNode: this.domNode.domNode, + linesContentDomNode: this.linesContent.domNode, + + focusTextArea: () => { + this.focus(); + }, + + getLastViewCursorsRenderData: () => { + return this.viewCursors.getLastRenderData() || []; + }, + shouldSuppressMouseDownOnViewZone: (viewZoneId: number) => { + return this.viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId); + }, + shouldSuppressMouseDownOnWidget: (widgetId: string) => { + return this.contentWidgets.shouldSuppressMouseDownOnWidget(widgetId); + }, + getPositionFromDOMInfo: (spanNode: HTMLElement, offset: number) => { + this._flushAccumulatedAndRenderNow(); + return this.viewLines.getPositionFromDOMInfo(spanNode, offset); + }, + + visibleRangeForPosition2: (lineNumber: number, column: number) => { + this._flushAccumulatedAndRenderNow(); + let visibleRanges = this.viewLines.visibleRangesForRange2(new Range(lineNumber, column, lineNumber, column)); + if (!visibleRanges) { + return null; + } + return visibleRanges[0]; + }, + + getLineWidth: (lineNumber: number) => { + this._flushAccumulatedAndRenderNow(); + return this.viewLines.getLineWidth(lineNumber); + } + }; + } + + private createTextAreaHandlerHelper(): ITextAreaHandlerHelper { + return { + visibleRangeForPositionRelativeToEditor: (lineNumber: number, column: number) => { + this._flushAccumulatedAndRenderNow(); + let visibleRanges = this.viewLines.visibleRangesForRange2(new Range(lineNumber, column, lineNumber, column)); + if (!visibleRanges) { + return null; + } + return visibleRanges[0]; + } + }; + } + + private _setLayout(): void { + const layoutInfo = this._context.configuration.editor.layoutInfo; + this.domNode.setWidth(layoutInfo.width); + this.domNode.setHeight(layoutInfo.height); + + this.overflowGuardContainer.setWidth(layoutInfo.width); + this.overflowGuardContainer.setHeight(layoutInfo.height); + + this.linesContent.setWidth(1000000); + this.linesContent.setHeight(1000000); + + } + + private getEditorClassName() { + return this._context.configuration.editor.editorClassName + ' ' + getThemeTypeSelector(this._context.theme.type); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.editorClassName) { + this.domNode.setClassName(this.getEditorClassName()); + } + if (e.layoutInfo) { + this._setLayout(); + } + return false; + } + public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { + this.domNode.toggleClassName('focused', e.isFocused); + if (e.isFocused) { + this.outgoingEvents.emitViewFocusGained(); + } else { + this.outgoingEvents.emitViewFocusLost(); + } + return false; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + this.outgoingEvents.emitScrollChanged(e); + return false; + } + public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { + this.domNode.setClassName(this.getEditorClassName()); + return false; + } + + // --- end event handlers + + public dispose(): void { + this._isDisposed = true; + if (this._renderAnimationFrame !== null) { + this._renderAnimationFrame.dispose(); + this._renderAnimationFrame = null; + } + + this.eventDispatcher.removeEventHandler(this); + this.outgoingEvents.dispose(); + + this.pointerHandler.dispose(); + + this.viewLines.dispose(); + + // Destroy view parts + for (let i = 0, len = this.viewParts.length; i < len; i++) { + this.viewParts[i].dispose(); + } + this.viewParts = []; + + super.dispose(); + } + + private _renderOnce(callback: () => any): any { + let r = safeInvokeNoArg(callback); + this._scheduleRender(); + return r; + } + + private _scheduleRender(): void { + if (this._renderAnimationFrame === null) { + this._renderAnimationFrame = dom.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), 100); + } + } + + private _onRenderScheduled(): void { + this._renderAnimationFrame = null; + this._flushAccumulatedAndRenderNow(); + } + + private _renderNow(): void { + safeInvokeNoArg(() => this._actualRender()); + } + + private _getViewPartsToRender(): ViewPart[] { + let result: ViewPart[] = [], resultLen = 0; + for (let i = 0, len = this.viewParts.length; i < len; i++) { + let viewPart = this.viewParts[i]; + if (viewPart.shouldRender()) { + result[resultLen++] = viewPart; + } + } + return result; + } + + private _actualRender(): void { + if (!dom.isInDOM(this.domNode.domNode)) { + return; + } + + let viewPartsToRender = this._getViewPartsToRender(); + + if (!this.viewLines.shouldRender() && viewPartsToRender.length === 0) { + // Nothing to render + return; + } + + const partialViewportData = this._context.viewLayout.getLinesViewportData(); + this._context.model.setViewport(partialViewportData.startLineNumber, partialViewportData.endLineNumber, partialViewportData.centeredLineNumber); + + let viewportData = new ViewportData( + this._cursor.getViewSelections(), + partialViewportData, + this._context.viewLayout.getWhitespaceViewportData(), + this._context.model + ); + + if (this.viewLines.shouldRender()) { + this.viewLines.renderText(viewportData); + this.viewLines.onDidRender(); + + // Rendering of viewLines might cause scroll events to occur, so collect view parts to render again + viewPartsToRender = this._getViewPartsToRender(); + } + + let renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this.viewLines); + + // Render the rest of the parts + for (let i = 0, len = viewPartsToRender.length; i < len; i++) { + let viewPart = viewPartsToRender[i]; + viewPart.prepareRender(renderingContext); + } + + for (let i = 0, len = viewPartsToRender.length; i < len; i++) { + let viewPart = viewPartsToRender[i]; + viewPart.render(renderingContext); + viewPart.onDidRender(); + } + } + + // --- BEGIN CodeEditor helpers + + public delegateVerticalScrollbarMouseDown(browserEvent: IMouseEvent): void { + this._scrollbar.delegateVerticalScrollbarMouseDown(browserEvent); + } + + public getOffsetForColumn(modelLineNumber: number, modelColumn: number): number { + let modelPosition = this._context.model.validateModelPosition({ + lineNumber: modelLineNumber, + column: modelColumn + }); + let viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); + this._flushAccumulatedAndRenderNow(); + let visibleRanges = this.viewLines.visibleRangesForRange2(new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column)); + if (!visibleRanges) { + return -1; + } + return visibleRanges[0].left; + } + + public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget { + return this.pointerHandler.getTargetAtClientPoint(clientX, clientY); + } + + public getInternalEventBus(): ViewOutgoingEvents { + return this.outgoingEvents; + } + + public createOverviewRuler(cssClassName: string, minimumHeight: number, maximumHeight: number): OverviewRuler { + return new OverviewRuler(this._context, cssClassName, minimumHeight, maximumHeight); + } + + public change(callback: (changeAccessor: editorBrowser.IViewZoneChangeAccessor) => any): boolean { + let zonesHaveChanged = false; + + this._renderOnce(() => { + let changeAccessor: editorBrowser.IViewZoneChangeAccessor = { + addZone: (zone: editorBrowser.IViewZone): number => { + zonesHaveChanged = true; + return this.viewZones.addZone(zone); + }, + removeZone: (id: number): void => { + if (!id) { + return; + } + zonesHaveChanged = this.viewZones.removeZone(id) || zonesHaveChanged; + }, + layoutZone: (id: number): void => { + if (!id) { + return; + } + zonesHaveChanged = this.viewZones.layoutZone(id) || zonesHaveChanged; + } + }; + + safeInvoke1Arg(callback, changeAccessor); + + // Invalidate changeAccessor + changeAccessor.addZone = null; + changeAccessor.removeZone = null; + + if (zonesHaveChanged) { + this._context.viewLayout.onHeightMaybeChanged(); + this._context.privateViewEventBus.emit(new viewEvents.ViewZonesChangedEvent()); + } + }); + return zonesHaveChanged; + } + + public render(now: boolean, everything: boolean): void { + if (everything) { + // Force everything to render... + this.viewLines.forceShouldRender(); + for (let i = 0, len = this.viewParts.length; i < len; i++) { + let viewPart = this.viewParts[i]; + viewPart.forceShouldRender(); + } + } + if (now) { + this._flushAccumulatedAndRenderNow(); + } else { + this._scheduleRender(); + } + } + + public setAriaActiveDescendant(id: string): void { + this._textAreaHandler.setAriaActiveDescendant(id); + } + + public focus(): void { + this._textAreaHandler.focusTextArea(); + } + + public isFocused(): boolean { + return this._textAreaHandler.isFocused(); + } + + public addContentWidget(widgetData: IContentWidgetData): void { + this.contentWidgets.addWidget(widgetData.widget); + this.layoutContentWidget(widgetData); + this._scheduleRender(); + } + + public layoutContentWidget(widgetData: IContentWidgetData): void { + let newPosition = widgetData.position ? widgetData.position.position : null; + let newPreference = widgetData.position ? widgetData.position.preference : null; + this.contentWidgets.setWidgetPosition(widgetData.widget, newPosition, newPreference); + this._scheduleRender(); + } + + public removeContentWidget(widgetData: IContentWidgetData): void { + this.contentWidgets.removeWidget(widgetData.widget); + this._scheduleRender(); + } + + public addOverlayWidget(widgetData: IOverlayWidgetData): void { + this.overlayWidgets.addWidget(widgetData.widget); + this.layoutOverlayWidget(widgetData); + this._scheduleRender(); + } + + public layoutOverlayWidget(widgetData: IOverlayWidgetData): void { + let newPreference = widgetData.position ? widgetData.position.preference : null; + let shouldRender = this.overlayWidgets.setWidgetPosition(widgetData.widget, newPreference); + if (shouldRender) { + this._scheduleRender(); + } + } + + public removeOverlayWidget(widgetData: IOverlayWidgetData): void { + this.overlayWidgets.removeWidget(widgetData.widget); + this._scheduleRender(); + } + + // --- END CodeEditor helpers + +} + +function safeInvokeNoArg(func: Function): any { + try { + return func(); + } catch (e) { + onUnexpectedError(e); + } +} + +function safeInvoke1Arg(func: Function, arg1: any): any { + try { + return func(arg1); + } catch (e) { + onUnexpectedError(e); + } +} diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts new file mode 100644 index 0000000000..0009c6b8eb --- /dev/null +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -0,0 +1,610 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { createStringBuilder, IStringBuilder } from 'vs/editor/common/core/stringBuilder'; + +/** + * Represents a visible line + */ +export interface IVisibleLine { + getDomNode(): HTMLElement; + setDomNode(domNode: HTMLElement): void; + + onContentChanged(): void; + onTokensChanged(): void; + + /** + * Return null if the HTML should not be touched. + * Return the new HTML otherwise. + */ + renderLine(lineNumber: number, deltaTop: number, viewportData: ViewportData, sb: IStringBuilder): boolean; + + /** + * Layout the line. + */ + layoutLine(lineNumber: number, deltaTop: number): void; +} + +export interface ILine { + onContentChanged(): void; + onTokensChanged(): void; +} + +export class RenderedLinesCollection { + private readonly _createLine: () => T; + private _lines: T[]; + private _rendLineNumberStart: number; + + constructor(createLine: () => T) { + this._createLine = createLine; + this._set(1, []); + } + + public flush(): void { + this._set(1, []); + } + + _set(rendLineNumberStart: number, lines: T[]): void { + this._lines = lines; + this._rendLineNumberStart = rendLineNumberStart; + } + + _get(): { rendLineNumberStart: number; lines: T[]; } { + return { + rendLineNumberStart: this._rendLineNumberStart, + lines: this._lines + }; + } + + /** + * @returns Inclusive line number that is inside this collection + */ + public getStartLineNumber(): number { + return this._rendLineNumberStart; + } + + /** + * @returns Inclusive line number that is inside this collection + */ + public getEndLineNumber(): number { + return this._rendLineNumberStart + this._lines.length - 1; + } + + public getCount(): number { + return this._lines.length; + } + + public getLine(lineNumber: number): T { + let lineIndex = lineNumber - this._rendLineNumberStart; + if (lineIndex < 0 || lineIndex >= this._lines.length) { + throw new Error('Illegal value for lineNumber: ' + lineNumber); + } + return this._lines[lineIndex]; + } + + /** + * @returns Lines that were removed from this collection + */ + public onLinesDeleted(deleteFromLineNumber: number, deleteToLineNumber: number): T[] { + if (this.getCount() === 0) { + // no lines + return null; + } + + let startLineNumber = this.getStartLineNumber(); + let endLineNumber = this.getEndLineNumber(); + + if (deleteToLineNumber < startLineNumber) { + // deleting above the viewport + let deleteCnt = deleteToLineNumber - deleteFromLineNumber + 1; + this._rendLineNumberStart -= deleteCnt; + return null; + } + + if (deleteFromLineNumber > endLineNumber) { + // deleted below the viewport + return null; + } + + // Record what needs to be deleted + let deleteStartIndex = 0; + let deleteCount = 0; + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + let lineIndex = lineNumber - this._rendLineNumberStart; + + if (deleteFromLineNumber <= lineNumber && lineNumber <= deleteToLineNumber) { + // this is a line to be deleted + if (deleteCount === 0) { + // this is the first line to be deleted + deleteStartIndex = lineIndex; + deleteCount = 1; + } else { + deleteCount++; + } + } + } + + // Adjust this._rendLineNumberStart for lines deleted above + if (deleteFromLineNumber < startLineNumber) { + // Something was deleted above + let deleteAboveCount = 0; + + if (deleteToLineNumber < startLineNumber) { + // the entire deleted lines are above + deleteAboveCount = deleteToLineNumber - deleteFromLineNumber + 1; + } else { + deleteAboveCount = startLineNumber - deleteFromLineNumber; + } + + this._rendLineNumberStart -= deleteAboveCount; + } + + let deleted = this._lines.splice(deleteStartIndex, deleteCount); + return deleted; + } + + public onLinesChanged(changeFromLineNumber: number, changeToLineNumber: number): boolean { + if (this.getCount() === 0) { + // no lines + return false; + } + + let startLineNumber = this.getStartLineNumber(); + let endLineNumber = this.getEndLineNumber(); + + let someoneNotified = false; + + for (let changedLineNumber = changeFromLineNumber; changedLineNumber <= changeToLineNumber; changedLineNumber++) { + if (changedLineNumber >= startLineNumber && changedLineNumber <= endLineNumber) { + // Notify the line + this._lines[changedLineNumber - this._rendLineNumberStart].onContentChanged(); + someoneNotified = true; + } + } + + return someoneNotified; + } + + public onLinesInserted(insertFromLineNumber: number, insertToLineNumber: number): T[] { + if (this.getCount() === 0) { + // no lines + return null; + } + + let insertCnt = insertToLineNumber - insertFromLineNumber + 1; + let startLineNumber = this.getStartLineNumber(); + let endLineNumber = this.getEndLineNumber(); + + if (insertFromLineNumber <= startLineNumber) { + // inserting above the viewport + this._rendLineNumberStart += insertCnt; + return null; + } + + if (insertFromLineNumber > endLineNumber) { + // inserting below the viewport + return null; + } + + if (insertCnt + insertFromLineNumber > endLineNumber) { + // insert inside the viewport in such a way that all remaining lines are pushed outside + let deleted = this._lines.splice(insertFromLineNumber - this._rendLineNumberStart, endLineNumber - insertFromLineNumber + 1); + return deleted; + } + + // insert inside the viewport, push out some lines, but not all remaining lines + let newLines: T[] = []; + for (let i = 0; i < insertCnt; i++) { + newLines[i] = this._createLine(); + } + let insertIndex = insertFromLineNumber - this._rendLineNumberStart; + let beforeLines = this._lines.slice(0, insertIndex); + let afterLines = this._lines.slice(insertIndex, this._lines.length - insertCnt); + let deletedLines = this._lines.slice(this._lines.length - insertCnt, this._lines.length); + + this._lines = beforeLines.concat(newLines).concat(afterLines); + + return deletedLines; + } + + public onTokensChanged(ranges: { fromLineNumber: number; toLineNumber: number; }[]): boolean { + if (this.getCount() === 0) { + // no lines + return false; + } + + let startLineNumber = this.getStartLineNumber(); + let endLineNumber = this.getEndLineNumber(); + + let notifiedSomeone = false; + for (let i = 0, len = ranges.length; i < len; i++) { + let rng = ranges[i]; + + if (rng.toLineNumber < startLineNumber || rng.fromLineNumber > endLineNumber) { + // range outside viewport + continue; + } + + let from = Math.max(startLineNumber, rng.fromLineNumber); + let to = Math.min(endLineNumber, rng.toLineNumber); + + for (let lineNumber = from; lineNumber <= to; lineNumber++) { + let lineIndex = lineNumber - this._rendLineNumberStart; + this._lines[lineIndex].onTokensChanged(); + notifiedSomeone = true; + } + } + + return notifiedSomeone; + } +} + +export interface IVisibleLinesHost { + createVisibleLine(): T; +} + +export class VisibleLinesCollection { + + private readonly _host: IVisibleLinesHost; + public readonly domNode: FastDomNode; + private readonly _linesCollection: RenderedLinesCollection; + + constructor(host: IVisibleLinesHost) { + this._host = host; + this.domNode = this._createDomNode(); + this._linesCollection = new RenderedLinesCollection(() => this._host.createVisibleLine()); + } + + private _createDomNode(): FastDomNode { + let domNode = createFastDomNode(document.createElement('div')); + domNode.setClassName('view-layer'); + domNode.setPosition('absolute'); + domNode.domNode.setAttribute('role', 'presentation'); + domNode.domNode.setAttribute('aria-hidden', 'true'); + return domNode; + } + + // ---- begin view event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + return e.layoutInfo; + } + + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + this._linesCollection.flush(); + // No need to clear the dom node because a full .innerHTML will occur in ViewLayerRenderer._render + return true; + } + + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return this._linesCollection.onLinesChanged(e.fromLineNumber, e.toLineNumber); + } + + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + let deleted = this._linesCollection.onLinesDeleted(e.fromLineNumber, e.toLineNumber); + if (deleted) { + // Remove from DOM + for (let i = 0, len = deleted.length; i < len; i++) { + let lineDomNode = deleted[i].getDomNode(); + if (lineDomNode) { + this.domNode.domNode.removeChild(lineDomNode); + } + } + } + + return true; + } + + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + let deleted = this._linesCollection.onLinesInserted(e.fromLineNumber, e.toLineNumber); + if (deleted) { + // Remove from DOM + for (let i = 0, len = deleted.length; i < len; i++) { + let lineDomNode = deleted[i].getDomNode(); + if (lineDomNode) { + this.domNode.domNode.removeChild(lineDomNode); + } + } + } + + return true; + } + + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollTopChanged; + } + + public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + return this._linesCollection.onTokensChanged(e.ranges); + } + + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + // ---- end view event handlers + + public getStartLineNumber(): number { + return this._linesCollection.getStartLineNumber(); + } + + public getEndLineNumber(): number { + return this._linesCollection.getEndLineNumber(); + } + + public getVisibleLine(lineNumber: number): T { + return this._linesCollection.getLine(lineNumber); + } + + public renderLines(viewportData: ViewportData): void { + + let inp = this._linesCollection._get(); + + let renderer = new ViewLayerRenderer(this.domNode.domNode, this._host, viewportData); + + let ctx: IRendererContext = { + rendLineNumberStart: inp.rendLineNumberStart, + lines: inp.lines, + linesLength: inp.lines.length + }; + + // Decide if this render will do a single update (single large .innerHTML) or many updates (inserting/removing dom nodes) + let resCtx = renderer.render(ctx, viewportData.startLineNumber, viewportData.endLineNumber, viewportData.relativeVerticalOffset); + + this._linesCollection._set(resCtx.rendLineNumberStart, resCtx.lines); + } +} + +interface IRendererContext { + rendLineNumberStart: number; + lines: T[]; + linesLength: number; +} + +class ViewLayerRenderer { + + readonly domNode: HTMLElement; + readonly host: IVisibleLinesHost; + readonly viewportData: ViewportData; + + constructor(domNode: HTMLElement, host: IVisibleLinesHost, viewportData: ViewportData) { + this.domNode = domNode; + this.host = host; + this.viewportData = viewportData; + } + + public render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { + + let ctx: IRendererContext = { + rendLineNumberStart: inContext.rendLineNumberStart, + lines: inContext.lines.slice(0), + linesLength: inContext.linesLength + }; + + if ((ctx.rendLineNumberStart + ctx.linesLength - 1 < startLineNumber) || (stopLineNumber < ctx.rendLineNumberStart)) { + // There is no overlap whatsoever + ctx.rendLineNumberStart = startLineNumber; + ctx.linesLength = stopLineNumber - startLineNumber + 1; + ctx.lines = []; + for (let x = startLineNumber; x <= stopLineNumber; x++) { + ctx.lines[x - startLineNumber] = this.host.createVisibleLine(); + } + this._finishRendering(ctx, true, deltaTop); + return ctx; + } + + // Update lines which will remain untouched + this._renderUntouchedLines( + ctx, + Math.max(startLineNumber - ctx.rendLineNumberStart, 0), + Math.min(stopLineNumber - ctx.rendLineNumberStart, ctx.linesLength - 1), + deltaTop, + startLineNumber + ); + + if (ctx.rendLineNumberStart > startLineNumber) { + // Insert lines before + let fromLineNumber = startLineNumber; + let toLineNumber = Math.min(stopLineNumber, ctx.rendLineNumberStart - 1); + if (fromLineNumber <= toLineNumber) { + this._insertLinesBefore(ctx, fromLineNumber, toLineNumber, deltaTop, startLineNumber); + ctx.linesLength += toLineNumber - fromLineNumber + 1; + } + } else if (ctx.rendLineNumberStart < startLineNumber) { + // Remove lines before + let removeCnt = Math.min(ctx.linesLength, startLineNumber - ctx.rendLineNumberStart); + if (removeCnt > 0) { + this._removeLinesBefore(ctx, removeCnt); + ctx.linesLength -= removeCnt; + } + } + + ctx.rendLineNumberStart = startLineNumber; + + if (ctx.rendLineNumberStart + ctx.linesLength - 1 < stopLineNumber) { + // Insert lines after + let fromLineNumber = ctx.rendLineNumberStart + ctx.linesLength; + let toLineNumber = stopLineNumber; + + if (fromLineNumber <= toLineNumber) { + this._insertLinesAfter(ctx, fromLineNumber, toLineNumber, deltaTop, startLineNumber); + ctx.linesLength += toLineNumber - fromLineNumber + 1; + } + + } else if (ctx.rendLineNumberStart + ctx.linesLength - 1 > stopLineNumber) { + // Remove lines after + let fromLineNumber = Math.max(0, stopLineNumber - ctx.rendLineNumberStart + 1); + let toLineNumber = ctx.linesLength - 1; + let removeCnt = toLineNumber - fromLineNumber + 1; + + if (removeCnt > 0) { + this._removeLinesAfter(ctx, removeCnt); + ctx.linesLength -= removeCnt; + } + } + + this._finishRendering(ctx, false, deltaTop); + + return ctx; + } + + private _renderUntouchedLines(ctx: IRendererContext, startIndex: number, endIndex: number, deltaTop: number[], deltaLN: number): void { + const rendLineNumberStart = ctx.rendLineNumberStart; + const lines = ctx.lines; + + for (let i = startIndex; i <= endIndex; i++) { + let lineNumber = rendLineNumberStart + i; + lines[i].layoutLine(lineNumber, deltaTop[lineNumber - deltaLN]); + } + } + + private _insertLinesBefore(ctx: IRendererContext, fromLineNumber: number, toLineNumber: number, deltaTop: number[], deltaLN: number): void { + let newLines: T[] = []; + let newLinesLen = 0; + for (let lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) { + newLines[newLinesLen++] = this.host.createVisibleLine(); + } + ctx.lines = newLines.concat(ctx.lines); + } + + private _removeLinesBefore(ctx: IRendererContext, removeCount: number): void { + for (let i = 0; i < removeCount; i++) { + let lineDomNode = ctx.lines[i].getDomNode(); + if (lineDomNode) { + this.domNode.removeChild(lineDomNode); + } + } + ctx.lines.splice(0, removeCount); + } + + private _insertLinesAfter(ctx: IRendererContext, fromLineNumber: number, toLineNumber: number, deltaTop: number[], deltaLN: number): void { + let newLines: T[] = []; + let newLinesLen = 0; + for (let lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) { + newLines[newLinesLen++] = this.host.createVisibleLine(); + } + ctx.lines = ctx.lines.concat(newLines); + } + + private _removeLinesAfter(ctx: IRendererContext, removeCount: number): void { + let removeIndex = ctx.linesLength - removeCount; + + for (let i = 0; i < removeCount; i++) { + let lineDomNode = ctx.lines[removeIndex + i].getDomNode(); + if (lineDomNode) { + this.domNode.removeChild(lineDomNode); + } + } + ctx.lines.splice(removeIndex, removeCount); + } + + private _finishRenderingNewLines(ctx: IRendererContext, domNodeIsEmpty: boolean, newLinesHTML: string, wasNew: boolean[]): void { + let lastChild = this.domNode.lastChild; + if (domNodeIsEmpty || !lastChild) { + this.domNode.innerHTML = newLinesHTML; + } else { + lastChild.insertAdjacentHTML('afterend', newLinesHTML); + } + + let currChild = this.domNode.lastChild; + for (let i = ctx.linesLength - 1; i >= 0; i--) { + let line = ctx.lines[i]; + if (wasNew[i]) { + line.setDomNode(currChild); + currChild = currChild.previousSibling; + } + } + } + + private _finishRenderingInvalidLines(ctx: IRendererContext, invalidLinesHTML: string, wasInvalid: boolean[]): void { + let hugeDomNode = document.createElement('div'); + + hugeDomNode.innerHTML = invalidLinesHTML; + + for (let i = 0; i < ctx.linesLength; i++) { + let line = ctx.lines[i]; + if (wasInvalid[i]) { + let source = hugeDomNode.firstChild; + let lineDomNode = line.getDomNode(); + lineDomNode.parentNode.replaceChild(source, lineDomNode); + line.setDomNode(source); + } + } + } + + private static _sb = createStringBuilder(100000); + + private _finishRendering(ctx: IRendererContext, domNodeIsEmpty: boolean, deltaTop: number[]): void { + + const sb = ViewLayerRenderer._sb; + const linesLength = ctx.linesLength; + const lines = ctx.lines; + const rendLineNumberStart = ctx.rendLineNumberStart; + + let wasNew: boolean[] = []; + { + sb.reset(); + let hadNewLine = false; + + for (let i = 0; i < linesLength; i++) { + const line = lines[i]; + wasNew[i] = false; + + const lineDomNode = line.getDomNode(); + if (lineDomNode) { + // line is not new + continue; + } + + const renderResult = line.renderLine(i + rendLineNumberStart, deltaTop[i], this.viewportData, sb); + if (!renderResult) { + // line does not need rendering + continue; + } + + wasNew[i] = true; + hadNewLine = true; + } + + if (hadNewLine) { + this._finishRenderingNewLines(ctx, domNodeIsEmpty, sb.build(), wasNew); + } + } + + { + sb.reset(); + + let hadInvalidLine = false; + let wasInvalid: boolean[] = []; + + for (let i = 0; i < linesLength; i++) { + let line = lines[i]; + wasInvalid[i] = false; + + if (wasNew[i]) { + // line was new + continue; + } + + const renderResult = line.renderLine(i + rendLineNumberStart, deltaTop[i], this.viewportData, sb); + if (!renderResult) { + // line does not need rendering + continue; + } + + wasInvalid[i] = true; + hadInvalidLine = true; + } + + if (hadInvalidLine) { + this._finishRenderingInvalidLines(ctx, sb.build(), wasInvalid); + } + } + } +} diff --git a/src/vs/editor/browser/view/viewOutgoingEvents.ts b/src/vs/editor/browser/view/viewOutgoingEvents.ts new file mode 100644 index 0000000000..af7d660072 --- /dev/null +++ b/src/vs/editor/browser/view/viewOutgoingEvents.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { IScrollEvent } from 'vs/editor/common/editorCommon'; +import { IEditorMouseEvent, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { MouseTarget } from 'vs/editor/browser/controller/mouseTarget'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; + +export interface EventCallback { + (event: T): void; +} + +export class ViewOutgoingEvents extends Disposable { + + public onDidScroll: EventCallback = null; + public onDidGainFocus: EventCallback = null; + public onDidLoseFocus: EventCallback = null; + public onKeyDown: EventCallback = null; + public onKeyUp: EventCallback = null; + public onContextMenu: EventCallback = null; + public onMouseMove: EventCallback = null; + public onMouseLeave: EventCallback = null; + public onMouseUp: EventCallback = null; + public onMouseDown: EventCallback = null; + public onMouseDrag: EventCallback = null; + public onMouseDrop: EventCallback = null; + + private _viewModel: IViewModel; + + constructor(viewModel: IViewModel) { + super(); + this._viewModel = viewModel; + } + + public emitScrollChanged(e: viewEvents.ViewScrollChangedEvent): void { + if (this.onDidScroll) { + this.onDidScroll(e); + } + } + + public emitViewFocusGained(): void { + if (this.onDidGainFocus) { + this.onDidGainFocus(void 0); + } + } + + public emitViewFocusLost(): void { + if (this.onDidLoseFocus) { + this.onDidLoseFocus(void 0); + } + } + + public emitKeyDown(e: IKeyboardEvent): void { + if (this.onKeyDown) { + this.onKeyDown(e); + } + } + + public emitKeyUp(e: IKeyboardEvent): void { + if (this.onKeyUp) { + this.onKeyUp(e); + } + } + + public emitContextMenu(e: IEditorMouseEvent): void { + if (this.onContextMenu) { + this.onContextMenu(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseMove(e: IEditorMouseEvent): void { + if (this.onMouseMove) { + this.onMouseMove(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseLeave(e: IEditorMouseEvent): void { + if (this.onMouseLeave) { + this.onMouseLeave(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseUp(e: IEditorMouseEvent): void { + if (this.onMouseUp) { + this.onMouseUp(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseDown(e: IEditorMouseEvent): void { + if (this.onMouseDown) { + this.onMouseDown(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseDrag(e: IEditorMouseEvent): void { + if (this.onMouseDrag) { + this.onMouseDrag(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseDrop(e: IEditorMouseEvent): void { + if (this.onMouseDrop) { + this.onMouseDrop(this._convertViewToModelMouseEvent(e)); + } + } + + private _convertViewToModelMouseEvent(e: IEditorMouseEvent): IEditorMouseEvent { + if (e.target) { + return { + event: e.event, + target: this._convertViewToModelMouseTarget(e.target) + }; + } + return e; + } + + private _convertViewToModelMouseTarget(target: IMouseTarget): IMouseTarget { + return new ExternalMouseTarget( + target.element, + target.type, + target.mouseColumn, + target.position ? this._convertViewToModelPosition(target.position) : null, + target.range ? this._convertViewToModelRange(target.range) : null, + target.detail + ); + } + + private _convertViewToModelPosition(viewPosition: Position): Position { + return this._viewModel.coordinatesConverter.convertViewPositionToModelPosition(viewPosition); + } + + private _convertViewToModelRange(viewRange: Range): Range { + return this._viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange); + } +} + +class ExternalMouseTarget implements IMouseTarget { + + public readonly element: Element; + public readonly type: MouseTargetType; + public readonly mouseColumn: number; + public readonly position: Position; + public readonly range: Range; + public readonly detail: any; + + constructor(element: Element, type: MouseTargetType, mouseColumn: number, position: Position, range: Range, detail: any) { + this.element = element; + this.type = type; + this.mouseColumn = mouseColumn; + this.position = position; + this.range = range; + this.detail = detail; + } + + public toString(): string { + return MouseTarget.toString(this); + } +} diff --git a/src/vs/editor/browser/view/viewOverlays.ts b/src/vs/editor/browser/view/viewOverlays.ts new file mode 100644 index 0000000000..0c6d9aac40 --- /dev/null +++ b/src/vs/editor/browser/view/viewOverlays.ts @@ -0,0 +1,286 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { IConfiguration } from 'vs/editor/common/editorCommon'; +import { IVisibleLine, VisibleLinesCollection, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; +import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; +import { Configuration } from 'vs/editor/browser/config/configuration'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { IStringBuilder } from 'vs/editor/common/core/stringBuilder'; + +export class ViewOverlays extends ViewPart implements IVisibleLinesHost { + + private readonly _visibleLines: VisibleLinesCollection; + protected readonly domNode: FastDomNode; + private _dynamicOverlays: DynamicViewOverlay[]; + private _isFocused: boolean; + + constructor(context: ViewContext) { + super(context); + + this._visibleLines = new VisibleLinesCollection(this); + this.domNode = this._visibleLines.domNode; + + this._dynamicOverlays = []; + this._isFocused = false; + + this.domNode.setClassName('view-overlays'); + } + + public shouldRender(): boolean { + if (super.shouldRender()) { + return true; + } + + for (let i = 0, len = this._dynamicOverlays.length; i < len; i++) { + let dynamicOverlay = this._dynamicOverlays[i]; + if (dynamicOverlay.shouldRender()) { + return true; + } + } + + return false; + } + + public dispose(): void { + super.dispose(); + + for (let i = 0, len = this._dynamicOverlays.length; i < len; i++) { + let dynamicOverlay = this._dynamicOverlays[i]; + dynamicOverlay.dispose(); + } + this._dynamicOverlays = null; + } + + public getDomNode(): FastDomNode { + return this.domNode; + } + + // ---- begin IVisibleLinesHost + + public createVisibleLine(): ViewOverlayLine { + return new ViewOverlayLine(this._context.configuration, this._dynamicOverlays); + } + + // ---- end IVisibleLinesHost + + public addDynamicOverlay(overlay: DynamicViewOverlay): void { + this._dynamicOverlays.push(overlay); + } + + // ----- event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + this._visibleLines.onConfigurationChanged(e); + let startLineNumber = this._visibleLines.getStartLineNumber(); + let endLineNumber = this._visibleLines.getEndLineNumber(); + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + let line = this._visibleLines.getVisibleLine(lineNumber); + line.onConfigurationChanged(e); + } + return true; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return this._visibleLines.onFlushed(e); + } + public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { + this._isFocused = e.isFocused; + return true; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return this._visibleLines.onLinesChanged(e); + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return this._visibleLines.onLinesDeleted(e); + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return this._visibleLines.onLinesInserted(e); + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return this._visibleLines.onScrollChanged(e) || true; + } + public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + return this._visibleLines.onTokensChanged(e); + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return this._visibleLines.onZonesChanged(e); + } + + // ----- end event handlers + + public prepareRender(ctx: RenderingContext): void { + let toRender = this._dynamicOverlays.filter(overlay => overlay.shouldRender()); + + for (let i = 0, len = toRender.length; i < len; i++) { + let dynamicOverlay = toRender[i]; + dynamicOverlay.prepareRender(ctx); + dynamicOverlay.onDidRender(); + } + + return null; + } + + public render(ctx: RestrictedRenderingContext): void { + // Overwriting to bypass `shouldRender` flag + this._viewOverlaysRender(ctx); + + this.domNode.toggleClassName('focused', this._isFocused); + } + + _viewOverlaysRender(ctx: RestrictedRenderingContext): void { + this._visibleLines.renderLines(ctx.viewportData); + } +} + +export class ViewOverlayLine implements IVisibleLine { + + private _configuration: IConfiguration; + private _dynamicOverlays: DynamicViewOverlay[]; + private _domNode: FastDomNode; + private _renderedContent: string; + private _lineHeight: number; + + constructor(configuration: IConfiguration, dynamicOverlays: DynamicViewOverlay[]) { + this._configuration = configuration; + this._lineHeight = this._configuration.editor.lineHeight; + this._dynamicOverlays = dynamicOverlays; + + this._domNode = null; + this._renderedContent = null; + } + + public getDomNode(): HTMLElement { + if (!this._domNode) { + return null; + } + return this._domNode.domNode; + } + public setDomNode(domNode: HTMLElement): void { + this._domNode = createFastDomNode(domNode); + } + + public onContentChanged(): void { + // Nothing + } + public onTokensChanged(): void { + // Nothing + } + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): void { + if (e.lineHeight) { + this._lineHeight = this._configuration.editor.lineHeight; + } + } + + public renderLine(lineNumber: number, deltaTop: number, viewportData: ViewportData, sb: IStringBuilder): boolean { + let result = ''; + for (let i = 0, len = this._dynamicOverlays.length; i < len; i++) { + let dynamicOverlay = this._dynamicOverlays[i]; + result += dynamicOverlay.render(viewportData.startLineNumber, lineNumber); + } + + if (this._renderedContent === result) { + // No rendering needed + return false; + } + + this._renderedContent = result; + + sb.appendASCIIString('
'); + sb.appendASCIIString(result); + sb.appendASCIIString('
'); + + return true; + } + + public layoutLine(lineNumber: number, deltaTop: number): void { + if (this._domNode) { + this._domNode.setTop(deltaTop); + this._domNode.setHeight(this._lineHeight); + } + } +} + +export class ContentViewOverlays extends ViewOverlays { + + private _contentWidth: number; + + constructor(context: ViewContext) { + super(context); + + this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; + + this.domNode.setHeight(0); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.layoutInfo) { + this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; + } + return super.onConfigurationChanged(e); + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return super.onScrollChanged(e) || e.scrollWidthChanged; + } + + // --- end event handlers + + _viewOverlaysRender(ctx: RestrictedRenderingContext): void { + super._viewOverlaysRender(ctx); + + this.domNode.setWidth(Math.max(ctx.scrollWidth, this._contentWidth)); + } +} + +export class MarginViewOverlays extends ViewOverlays { + + private _contentLeft: number; + + constructor(context: ViewContext) { + super(context); + + this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; + + this.domNode.setClassName('margin-view-overlays'); + this.domNode.setWidth(1); + + Configuration.applyFontInfo(this.domNode, this._context.configuration.editor.fontInfo); + } + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + let shouldRender = false; + if (e.fontInfo) { + Configuration.applyFontInfo(this.domNode, this._context.configuration.editor.fontInfo); + shouldRender = true; + } + if (e.layoutInfo) { + this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; + shouldRender = true; + } + return super.onConfigurationChanged(e) || shouldRender; + } + + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return super.onScrollChanged(e) || e.scrollHeightChanged; + } + + _viewOverlaysRender(ctx: RestrictedRenderingContext): void { + super._viewOverlaysRender(ctx); + let height = Math.min(ctx.scrollHeight, 1000000); + this.domNode.setHeight(height); + this.domNode.setWidth(this._contentLeft); + } +} diff --git a/src/vs/editor/browser/view/viewPart.ts b/src/vs/editor/browser/view/viewPart.ts new file mode 100644 index 0000000000..a5c030d9a2 --- /dev/null +++ b/src/vs/editor/browser/view/viewPart.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { FastDomNode } from 'vs/base/browser/fastDomNode'; + +export abstract class ViewPart extends ViewEventHandler { + + _context: ViewContext; + + constructor(context: ViewContext) { + super(); + this._context = context; + this._context.addEventHandler(this); + } + + public dispose(): void { + this._context.removeEventHandler(this); + this._context = null; + super.dispose(); + } + + public abstract prepareRender(ctx: RenderingContext): void; + public abstract render(ctx: RestrictedRenderingContext): void; +} + +export const enum PartFingerprint { + None, + ContentWidgets, + OverflowingContentWidgets, + OverflowGuard, + OverlayWidgets, + ScrollableElement, + TextArea, + ViewLines, + Minimap +} + +export class PartFingerprints { + + public static write(target: Element | FastDomNode, partId: PartFingerprint) { + if (target instanceof FastDomNode) { + target.setAttribute('data-mprt', String(partId)); + } else { + target.setAttribute('data-mprt', String(partId)); + } + } + + public static read(target: Element): PartFingerprint { + let r = target.getAttribute('data-mprt'); + if (r === null) { + return PartFingerprint.None; + } + return parseInt(r, 10); + } + + public static collect(child: Element, stopAt: Element): Uint8Array { + let result: PartFingerprint[] = [], resultLen = 0; + + while (child && child !== document.body) { + if (child === stopAt) { + break; + } + if (child.nodeType === child.ELEMENT_NODE) { + result[resultLen++] = this.read(child); + } + child = child.parentElement; + } + + let r = new Uint8Array(resultLen); + for (let i = 0; i < resultLen; i++) { + r[i] = result[resultLen - i - 1]; + } + return r; + } +} diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts new file mode 100644 index 0000000000..c65bf08526 --- /dev/null +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -0,0 +1,431 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as dom from 'vs/base/browser/dom'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { ContentWidgetPositionPreference, IContentWidget } from 'vs/editor/browser/editorBrowser'; +import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; + +class Coordinate { + _coordinateBrand: void; + + public readonly top: number; + public readonly left: number; + + constructor(top: number, left: number) { + this.top = top; + this.left = left; + } +} + +export class ViewContentWidgets extends ViewPart { + + private _viewDomNode: FastDomNode; + private _widgets: { [key: string]: Widget; }; + + public domNode: FastDomNode; + public overflowingContentWidgetsDomNode: FastDomNode; + + constructor(context: ViewContext, viewDomNode: FastDomNode) { + super(context); + this._viewDomNode = viewDomNode; + this._widgets = {}; + + this.domNode = createFastDomNode(document.createElement('div')); + PartFingerprints.write(this.domNode, PartFingerprint.ContentWidgets); + this.domNode.setClassName('contentWidgets'); + this.domNode.setPosition('absolute'); + this.domNode.setTop(0); + + this.overflowingContentWidgetsDomNode = createFastDomNode(document.createElement('div')); + PartFingerprints.write(this.overflowingContentWidgetsDomNode, PartFingerprint.OverflowingContentWidgets); + this.overflowingContentWidgetsDomNode.setClassName('overflowingContentWidgets'); + } + + public dispose(): void { + super.dispose(); + this._widgets = null; + this.domNode = null; + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + let keys = Object.keys(this._widgets); + for (let i = 0, len = keys.length; i < len; i++) { + const widgetId = keys[i]; + this._widgets[widgetId].onConfigurationChanged(e); + } + return true; + } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + // true for inline decorations that can end up relayouting text + return true; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return true; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return true; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + // ---- end view event handlers + + public addWidget(_widget: IContentWidget): void { + const myWidget = new Widget(this._context, this._viewDomNode, _widget); + this._widgets[myWidget.id] = myWidget; + + if (myWidget.allowEditorOverflow) { + this.overflowingContentWidgetsDomNode.appendChild(myWidget.domNode); + } else { + this.domNode.appendChild(myWidget.domNode); + } + + this.setShouldRender(); + } + + public setWidgetPosition(widget: IContentWidget, position: IPosition, preference: ContentWidgetPositionPreference[]): void { + const myWidget = this._widgets[widget.getId()]; + myWidget.setPosition(position, preference); + + this.setShouldRender(); + } + + public removeWidget(widget: IContentWidget): void { + const widgetId = widget.getId(); + if (this._widgets.hasOwnProperty(widgetId)) { + const myWidget = this._widgets[widgetId]; + delete this._widgets[widgetId]; + + const domNode = myWidget.domNode.domNode; + domNode.parentNode.removeChild(domNode); + domNode.removeAttribute('monaco-visible-content-widget'); + + this.setShouldRender(); + } + } + + public shouldSuppressMouseDownOnWidget(widgetId: string): boolean { + if (this._widgets.hasOwnProperty(widgetId)) { + return this._widgets[widgetId].suppressMouseDown; + } + return false; + } + + public prepareRender(ctx: RenderingContext): void { + let keys = Object.keys(this._widgets); + for (let i = 0, len = keys.length; i < len; i++) { + const widgetId = keys[i]; + this._widgets[widgetId].prepareRender(ctx); + } + } + + public render(ctx: RestrictedRenderingContext): void { + let keys = Object.keys(this._widgets); + for (let i = 0, len = keys.length; i < len; i++) { + const widgetId = keys[i]; + this._widgets[widgetId].render(ctx); + } + } +} + +interface IBoxLayoutResult { + aboveTop: number; + fitsAbove: boolean; + belowTop: number; + fitsBelow: boolean; + left: number; +} + +class Widget { + private readonly _context: ViewContext; + private readonly _viewDomNode: FastDomNode; + private readonly _actual: IContentWidget; + + public readonly domNode: FastDomNode; + public readonly id: string; + public readonly allowEditorOverflow: boolean; + public readonly suppressMouseDown: boolean; + + private _fixedOverflowWidgets: boolean; + private _contentWidth: number; + private _contentLeft: number; + private _lineHeight: number; + + private _position: IPosition; + private _preference: ContentWidgetPositionPreference[]; + private _isVisible: boolean; + private _renderData: Coordinate; + + constructor(context: ViewContext, viewDomNode: FastDomNode, actual: IContentWidget) { + this._context = context; + this._viewDomNode = viewDomNode; + this._actual = actual; + this.domNode = createFastDomNode(this._actual.getDomNode()); + + this.id = this._actual.getId(); + this.allowEditorOverflow = this._actual.allowEditorOverflow || false; + this.suppressMouseDown = this._actual.suppressMouseDown || false; + + this._fixedOverflowWidgets = this._context.configuration.editor.viewInfo.fixedOverflowWidgets; + this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; + this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; + this._lineHeight = this._context.configuration.editor.lineHeight; + + this._position = null; + this._preference = null; + this._isVisible = false; + this._renderData = null; + + this.domNode.setPosition((this._fixedOverflowWidgets && this.allowEditorOverflow) ? 'fixed' : 'absolute'); + this._updateMaxWidth(); + this.domNode.setVisibility('hidden'); + this.domNode.setAttribute('widgetId', this.id); + } + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): void { + if (e.lineHeight) { + this._lineHeight = this._context.configuration.editor.lineHeight; + } + if (e.layoutInfo) { + this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; + this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; + + this._updateMaxWidth(); + } + } + + private _updateMaxWidth(): void { + const maxWidth = this.allowEditorOverflow + ? window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth + : this._contentWidth; + + this.domNode.setMaxWidth(maxWidth); + } + + public setPosition(position: IPosition, preference: ContentWidgetPositionPreference[]): void { + this._position = position; + this._preference = preference; + } + + private _layoutBoxInViewport(topLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult { + // Our visible box is split horizontally by the current line => 2 boxes + + // a) the box above the line + let aboveLineTop = topLeft.top; + let heightAboveLine = aboveLineTop; + + // b) the box under the line + let underLineTop = topLeft.top + this._lineHeight; + let heightUnderLine = ctx.viewportHeight - underLineTop; + + let aboveTop = aboveLineTop - height; + let fitsAbove = (heightAboveLine >= height); + let belowTop = underLineTop; + let fitsBelow = (heightUnderLine >= height); + + // And its left + let actualLeft = topLeft.left; + if (actualLeft + width > ctx.scrollLeft + ctx.viewportWidth) { + actualLeft = ctx.scrollLeft + ctx.viewportWidth - width; + } + if (actualLeft < ctx.scrollLeft) { + actualLeft = ctx.scrollLeft; + } + + return { + aboveTop: aboveTop, + fitsAbove: fitsAbove, + belowTop: belowTop, + fitsBelow: fitsBelow, + left: actualLeft + }; + } + + private _layoutBoxInPage(topLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult { + let left0 = topLeft.left - ctx.scrollLeft; + + if (left0 + width < 0 || left0 > this._contentWidth) { + return null; + } + + let aboveTop = topLeft.top - height; + let belowTop = topLeft.top + this._lineHeight; + let left = left0 + this._contentLeft; + + let domNodePosition = dom.getDomNodePagePosition(this._viewDomNode.domNode); + let absoluteAboveTop = domNodePosition.top + aboveTop - dom.StandardWindow.scrollY; + let absoluteBelowTop = domNodePosition.top + belowTop - dom.StandardWindow.scrollY; + let absoluteLeft = domNodePosition.left + left - dom.StandardWindow.scrollX; + + let INNER_WIDTH = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; + let INNER_HEIGHT = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; + + // Leave some clearance to the bottom + let TOP_PADDING = 22; + let BOTTOM_PADDING = 22; + + let fitsAbove = (absoluteAboveTop >= TOP_PADDING), + fitsBelow = (absoluteBelowTop + height <= INNER_HEIGHT - BOTTOM_PADDING); + + if (absoluteLeft + width + 20 > INNER_WIDTH) { + let delta = absoluteLeft - (INNER_WIDTH - width - 20); + absoluteLeft -= delta; + left -= delta; + } + if (absoluteLeft < 0) { + let delta = absoluteLeft; + absoluteLeft -= delta; + left -= delta; + } + + if (this._fixedOverflowWidgets) { + aboveTop = absoluteAboveTop; + belowTop = absoluteBelowTop; + left = absoluteLeft; + } + + return { aboveTop, fitsAbove, belowTop, fitsBelow, left }; + } + + private _prepareRenderWidgetAtExactPositionOverflowing(topLeft: Coordinate): Coordinate { + return new Coordinate(topLeft.top, topLeft.left + this._contentLeft); + } + + private _getTopLeft(ctx: RenderingContext, position: Position): Coordinate { + const visibleRange = ctx.visibleRangeForPosition(position); + if (!visibleRange) { + return null; + } + + const top = ctx.getVerticalOffsetForLineNumber(position.lineNumber) - ctx.scrollTop; + return new Coordinate(top, visibleRange.left); + } + + private _prepareRenderWidget(ctx: RenderingContext): Coordinate { + if (!this._position || !this._preference) { + return null; + } + + // Do not trust that widgets have a valid position + let validModelPosition = this._context.model.validateModelPosition(this._position); + + if (!this._context.model.coordinatesConverter.modelPositionIsVisible(validModelPosition)) { + // this position is hidden by the view model + return null; + } + + let position = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(validModelPosition); + + let placement: IBoxLayoutResult = null; + let fetchPlacement = (): void => { + if (placement) { + return; + } + + const topLeft = this._getTopLeft(ctx, position); + if (!topLeft) { + return; + } + + const domNode = this.domNode.domNode; + const width = domNode.clientWidth; + const height = domNode.clientHeight; + + if (this.allowEditorOverflow) { + placement = this._layoutBoxInPage(topLeft, width, height, ctx); + } else { + placement = this._layoutBoxInViewport(topLeft, width, height, ctx); + } + }; + + // Do two passes, first for perfect fit, second picks first option + for (let pass = 1; pass <= 2; pass++) { + for (let i = 0; i < this._preference.length; i++) { + let pref = this._preference[i]; + if (pref === ContentWidgetPositionPreference.ABOVE) { + fetchPlacement(); + if (!placement) { + // Widget outside of viewport + return null; + } + if (pass === 2 || placement.fitsAbove) { + return new Coordinate(placement.aboveTop, placement.left); + } + } else if (pref === ContentWidgetPositionPreference.BELOW) { + fetchPlacement(); + if (!placement) { + // Widget outside of viewport + return null; + } + if (pass === 2 || placement.fitsBelow) { + return new Coordinate(placement.belowTop, placement.left); + } + } else { + const topLeft = this._getTopLeft(ctx, position); + if (!topLeft) { + // Widget outside of viewport + return null; + } + if (this.allowEditorOverflow) { + return this._prepareRenderWidgetAtExactPositionOverflowing(topLeft); + } else { + return topLeft; + } + } + } + } + return null; + } + + public prepareRender(ctx: RenderingContext): void { + this._renderData = this._prepareRenderWidget(ctx); + } + + public render(ctx: RestrictedRenderingContext): void { + if (!this._renderData) { + // This widget should be invisible + if (this._isVisible) { + this.domNode.removeAttribute('monaco-visible-content-widget'); + this._isVisible = false; + this.domNode.setVisibility('hidden'); + } + return; + } + + // This widget should be visible + if (this.allowEditorOverflow) { + this.domNode.setTop(this._renderData.top); + this.domNode.setLeft(this._renderData.left); + } else { + this.domNode.setTop(this._renderData.top + ctx.scrollTop - ctx.bigNumbersDelta); + this.domNode.setLeft(this._renderData.left); + } + + if (!this._isVisible) { + this.domNode.setVisibility('inherit'); + this.domNode.setAttribute('monaco-visible-content-widget', 'true'); + this._isVisible = true; + } + } +} diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.css b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.css new file mode 100644 index 0000000000..25966f4e5a --- /dev/null +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.css @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .view-overlays .current-line { + display: block; + position: absolute; + left: 0; + top: 0; + box-sizing: border-box; +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts new file mode 100644 index 0000000000..bb4eb8ddf8 --- /dev/null +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./currentLineHighlight'; +import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry'; + +export class CurrentLineHighlightOverlay extends DynamicViewOverlay { + private _context: ViewContext; + private _lineHeight: number; + private _readOnly: boolean; + private _renderLineHighlight: 'none' | 'gutter' | 'line' | 'all'; + private _selectionIsEmpty: boolean; + private _primaryCursorIsInEditableRange: boolean; + private _primaryCursorLineNumber: number; + private _scrollWidth: number; + private _contentWidth: number; + + constructor(context: ViewContext) { + super(); + this._context = context; + this._lineHeight = this._context.configuration.editor.lineHeight; + this._readOnly = this._context.configuration.editor.readOnly; + this._renderLineHighlight = this._context.configuration.editor.viewInfo.renderLineHighlight; + + this._selectionIsEmpty = true; + this._primaryCursorIsInEditableRange = true; + this._primaryCursorLineNumber = 1; + this._scrollWidth = 0; + this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; + + this._context.addEventHandler(this); + } + + public dispose(): void { + this._context.removeEventHandler(this); + this._context = null; + super.dispose(); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.lineHeight) { + this._lineHeight = this._context.configuration.editor.lineHeight; + } + if (e.readOnly) { + this._readOnly = this._context.configuration.editor.readOnly; + } + if (e.viewInfo) { + this._renderLineHighlight = this._context.configuration.editor.viewInfo.renderLineHighlight; + } + if (e.layoutInfo) { + this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; + } + return true; + } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + let hasChanged = false; + + if (this._primaryCursorIsInEditableRange !== e.isInEditableRange) { + this._primaryCursorIsInEditableRange = e.isInEditableRange; + hasChanged = true; + } + + const primaryCursorLineNumber = e.selections[0].positionLineNumber; + if (this._primaryCursorLineNumber !== primaryCursorLineNumber) { + this._primaryCursorLineNumber = primaryCursorLineNumber; + hasChanged = true; + } + + const selectionIsEmpty = e.selections[0].isEmpty(); + if (this._selectionIsEmpty !== selectionIsEmpty) { + this._selectionIsEmpty = selectionIsEmpty; + hasChanged = true; + return true; + } + + return hasChanged; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollWidthChanged; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + // --- end event handlers + + public prepareRender(ctx: RenderingContext): void { + this._scrollWidth = ctx.scrollWidth; + } + + public render(startLineNumber: number, lineNumber: number): string { + if (lineNumber === this._primaryCursorLineNumber) { + if (this._shouldShowCurrentLine()) { + return ( + '
' + ); + } else { + return ''; + } + } + return ''; + } + + private _shouldShowCurrentLine(): boolean { + return (this._renderLineHighlight === 'line' || this._renderLineHighlight === 'all') && + this._selectionIsEmpty && + this._primaryCursorIsInEditableRange; + } +} + +registerThemingParticipant((theme, collector) => { + let lineHighlight = theme.getColor(editorLineHighlight); + if (lineHighlight) { + collector.addRule(`.monaco-editor .view-overlays .current-line { background-color: ${lineHighlight}; }`); + } + if (!lineHighlight || lineHighlight.isTransparent() || theme.defines(editorLineHighlightBorder)) { + let lineHighlightBorder = theme.getColor(editorLineHighlightBorder); + if (lineHighlightBorder) { + collector.addRule(`.monaco-editor .view-overlays .current-line { border: 2px solid ${lineHighlightBorder}; }`); + if (theme.type === 'hc') { + collector.addRule(`.monaco-editor .view-overlays .current-line { border-width: 1px; }`); + } + } + } +}); diff --git a/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.css b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.css new file mode 100644 index 0000000000..8bc8059487 --- /dev/null +++ b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.css @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .margin-view-overlays .current-line-margin { + display: block; + position: absolute; + left: 0; + top: 0; + box-sizing: border-box; +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts new file mode 100644 index 0000000000..2b536d3d0c --- /dev/null +++ b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./currentLineMarginHighlight'; +import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry'; + +export class CurrentLineMarginHighlightOverlay extends DynamicViewOverlay { + private _context: ViewContext; + private _lineHeight: number; + private _renderLineHighlight: 'none' | 'gutter' | 'line' | 'all'; + private _primaryCursorIsInEditableRange: boolean; + private _primaryCursorLineNumber: number; + private _contentLeft: number; + + constructor(context: ViewContext) { + super(); + this._context = context; + this._lineHeight = this._context.configuration.editor.lineHeight; + this._renderLineHighlight = this._context.configuration.editor.viewInfo.renderLineHighlight; + + this._primaryCursorIsInEditableRange = true; + this._primaryCursorLineNumber = 1; + this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; + + this._context.addEventHandler(this); + } + + public dispose(): void { + this._context.removeEventHandler(this); + this._context = null; + super.dispose(); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.lineHeight) { + this._lineHeight = this._context.configuration.editor.lineHeight; + } + if (e.viewInfo) { + this._renderLineHighlight = this._context.configuration.editor.viewInfo.renderLineHighlight; + } + if (e.layoutInfo) { + this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; + } + return true; + } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + let hasChanged = false; + + if (this._primaryCursorIsInEditableRange !== e.isInEditableRange) { + this._primaryCursorIsInEditableRange = e.isInEditableRange; + hasChanged = true; + } + + const primaryCursorLineNumber = e.selections[0].positionLineNumber; + if (this._primaryCursorLineNumber !== primaryCursorLineNumber) { + this._primaryCursorLineNumber = primaryCursorLineNumber; + hasChanged = true; + } + + return hasChanged; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + // --- end event handlers + + public prepareRender(ctx: RenderingContext): void { + } + + public render(startLineNumber: number, lineNumber: number): string { + if (lineNumber === this._primaryCursorLineNumber) { + if (this._shouldShowCurrentLine()) { + return ( + '
' + ); + } else { + return ''; + } + } + return ''; + } + + private _shouldShowCurrentLine(): boolean { + return (this._renderLineHighlight === 'gutter' || this._renderLineHighlight === 'all') && this._primaryCursorIsInEditableRange; + } +} + +registerThemingParticipant((theme, collector) => { + let lineHighlight = theme.getColor(editorLineHighlight); + if (lineHighlight) { + collector.addRule(`.monaco-editor .margin-view-overlays .current-line-margin { background-color: ${lineHighlight}; border: none; }`); + } else { + let lineHighlightBorder = theme.getColor(editorLineHighlightBorder); + if (lineHighlightBorder) { + collector.addRule(`.monaco-editor .margin-view-overlays .current-line-margin { border: 2px solid ${lineHighlightBorder}; }`); + } + if (theme.type === 'hc') { + collector.addRule(`.monaco-editor .margin-view-overlays .current-line-margin { border-width: 1px; }`); + } + } +}); diff --git a/src/vs/editor/browser/viewParts/decorations/decorations.css b/src/vs/editor/browser/viewParts/decorations/decorations.css new file mode 100644 index 0000000000..795df82686 --- /dev/null +++ b/src/vs/editor/browser/viewParts/decorations/decorations.css @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* + Keeping name short for faster parsing. + cdr = core decorations rendering (div) +*/ +.monaco-editor .lines-content .cdr { + position: absolute; +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/decorations/decorations.ts b/src/vs/editor/browser/viewParts/decorations/decorations.ts new file mode 100644 index 0000000000..d7d387b880 --- /dev/null +++ b/src/vs/editor/browser/viewParts/decorations/decorations.ts @@ -0,0 +1,209 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./decorations'; +import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; +import { Range } from 'vs/editor/common/core/range'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, HorizontalRange } from 'vs/editor/common/view/renderingContext'; +import { ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; + +export class DecorationsOverlay extends DynamicViewOverlay { + + private _context: ViewContext; + private _lineHeight: number; + private _typicalHalfwidthCharacterWidth: number; + private _renderResult: string[]; + + constructor(context: ViewContext) { + super(); + this._context = context; + this._lineHeight = this._context.configuration.editor.lineHeight; + this._typicalHalfwidthCharacterWidth = this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth; + this._renderResult = null; + + this._context.addEventHandler(this); + } + + public dispose(): void { + this._context.removeEventHandler(this); + this._context = null; + this._renderResult = null; + super.dispose(); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.lineHeight) { + this._lineHeight = this._context.configuration.editor.lineHeight; + } + if (e.fontInfo) { + this._typicalHalfwidthCharacterWidth = this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth; + } + return true; + } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + return true; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return true; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollTopChanged || e.scrollWidthChanged; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + // --- end event handlers + + public prepareRender(ctx: RenderingContext): void { + let _decorations = ctx.getDecorationsInViewport(); + + // Keep only decorations with `className` + let decorations: ViewModelDecoration[] = [], decorationsLen = 0; + for (let i = 0, len = _decorations.length; i < len; i++) { + let d = _decorations[i]; + if (d.source.options.className) { + decorations[decorationsLen++] = d; + } + } + + // Sort decorations for consistent render output + decorations = decorations.sort((a, b) => { + let aClassName = a.source.options.className; + let bClassName = b.source.options.className; + + if (aClassName < bClassName) { + return -1; + } + if (aClassName > bClassName) { + return 1; + } + + return Range.compareRangesUsingStarts(a.range, b.range); + }); + + let visibleStartLineNumber = ctx.visibleRange.startLineNumber; + let visibleEndLineNumber = ctx.visibleRange.endLineNumber; + let output: string[] = []; + for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { + let lineIndex = lineNumber - visibleStartLineNumber; + output[lineIndex] = ''; + } + + // Render first whole line decorations and then regular decorations + this._renderWholeLineDecorations(ctx, decorations, output); + this._renderNormalDecorations(ctx, decorations, output); + this._renderResult = output; + } + + private _renderWholeLineDecorations(ctx: RenderingContext, decorations: ViewModelDecoration[], output: string[]): void { + let lineHeight = String(this._lineHeight); + let visibleStartLineNumber = ctx.visibleRange.startLineNumber; + let visibleEndLineNumber = ctx.visibleRange.endLineNumber; + + for (let i = 0, lenI = decorations.length; i < lenI; i++) { + let d = decorations[i]; + + if (!d.source.options.isWholeLine) { + continue; + } + + let decorationOutput = ( + '
' + ); + + let startLineNumber = Math.max(d.range.startLineNumber, visibleStartLineNumber); + let endLineNumber = Math.min(d.range.endLineNumber, visibleEndLineNumber); + for (let j = startLineNumber; j <= endLineNumber; j++) { + let lineIndex = j - visibleStartLineNumber; + output[lineIndex] += decorationOutput; + } + } + } + + private _renderNormalDecorations(ctx: RenderingContext, decorations: ViewModelDecoration[], output: string[]): void { + let lineHeight = String(this._lineHeight); + let visibleStartLineNumber = ctx.visibleRange.startLineNumber; + + for (let i = 0, lenI = decorations.length; i < lenI; i++) { + const d = decorations[i]; + + if (d.source.options.isWholeLine) { + continue; + } + + const className = d.source.options.className; + const showIfCollapsed = d.source.options.showIfCollapsed; + + let range = d.range; + if (showIfCollapsed && range.endColumn === 1 && range.endLineNumber !== range.startLineNumber) { + range = new Range(range.startLineNumber, range.startColumn, range.endLineNumber - 1, this._context.model.getLineMaxColumn(range.endLineNumber - 1)); + } + + let linesVisibleRanges = ctx.linesVisibleRangesForRange(range, /*TODO@Alex*/className === 'findMatch'); + if (!linesVisibleRanges) { + continue; + } + + for (let j = 0, lenJ = linesVisibleRanges.length; j < lenJ; j++) { + let lineVisibleRanges = linesVisibleRanges[j]; + const lineIndex = lineVisibleRanges.lineNumber - visibleStartLineNumber; + + if (showIfCollapsed && lineVisibleRanges.ranges.length === 1) { + const singleVisibleRange = lineVisibleRanges.ranges[0]; + if (singleVisibleRange.width === 0) { + // collapsed range case => make the decoration visible by faking its width + lineVisibleRanges.ranges[0] = new HorizontalRange(singleVisibleRange.left, this._typicalHalfwidthCharacterWidth); + } + } + + for (let k = 0, lenK = lineVisibleRanges.ranges.length; k < lenK; k++) { + const visibleRange = lineVisibleRanges.ranges[k]; + const decorationOutput = ( + '
' + ); + output[lineIndex] += decorationOutput; + } + } + } + } + + public render(startLineNumber: number, lineNumber: number): string { + if (!this._renderResult) { + return ''; + } + let lineIndex = lineNumber - startLineNumber; + if (lineIndex < 0 || lineIndex >= this._renderResult.length) { + throw new Error('Unexpected render request'); + } + return this._renderResult[lineIndex]; + } +} diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts new file mode 100644 index 0000000000..5785975c0d --- /dev/null +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as dom from 'vs/base/browser/dom'; +import { ScrollableElementCreationOptions, ScrollableElementChangeOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; +import { IOverviewRulerLayoutInfo, SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { INewScrollPosition } from 'vs/editor/common/editorCommon'; +import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { ISimplifiedMouseEvent } from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; + +export class EditorScrollbar extends ViewPart { + + private scrollbar: SmoothScrollableElement; + private scrollbarDomNode: FastDomNode; + + constructor( + context: ViewContext, + linesContent: FastDomNode, + viewDomNode: FastDomNode, + overflowGuardDomNode: FastDomNode + ) { + super(context); + + const editor = this._context.configuration.editor; + const configScrollbarOpts = editor.viewInfo.scrollbar; + + let scrollbarOptions: ScrollableElementCreationOptions = { + listenOnDomNode: viewDomNode.domNode, + className: 'editor-scrollable' + ' ' + getThemeTypeSelector(context.theme.type), + useShadows: false, + lazyRender: true, + + vertical: configScrollbarOpts.vertical, + horizontal: configScrollbarOpts.horizontal, + verticalHasArrows: configScrollbarOpts.verticalHasArrows, + horizontalHasArrows: configScrollbarOpts.horizontalHasArrows, + verticalScrollbarSize: configScrollbarOpts.verticalScrollbarSize, + verticalSliderSize: configScrollbarOpts.verticalSliderSize, + horizontalScrollbarSize: configScrollbarOpts.horizontalScrollbarSize, + horizontalSliderSize: configScrollbarOpts.horizontalSliderSize, + handleMouseWheel: configScrollbarOpts.handleMouseWheel, + arrowSize: configScrollbarOpts.arrowSize, + mouseWheelScrollSensitivity: configScrollbarOpts.mouseWheelScrollSensitivity, + }; + + this.scrollbar = this._register(new SmoothScrollableElement(linesContent.domNode, scrollbarOptions, this._context.viewLayout.scrollable)); + PartFingerprints.write(this.scrollbar.getDomNode(), PartFingerprint.ScrollableElement); + + this.scrollbarDomNode = createFastDomNode(this.scrollbar.getDomNode()); + this.scrollbarDomNode.setPosition('absolute'); + this._setLayout(); + + // When having a zone widget that calls .focus() on one of its dom elements, + // the browser will try desperately to reveal that dom node, unexpectedly + // changing the .scrollTop of this.linesContent + + let onBrowserDesperateReveal = (domNode: HTMLElement, lookAtScrollTop: boolean, lookAtScrollLeft: boolean) => { + let newScrollPosition: INewScrollPosition = {}; + + if (lookAtScrollTop) { + let deltaTop = domNode.scrollTop; + if (deltaTop) { + newScrollPosition.scrollTop = this._context.viewLayout.getCurrentScrollTop() + deltaTop; + domNode.scrollTop = 0; + } + } + + if (lookAtScrollLeft) { + let deltaLeft = domNode.scrollLeft; + if (deltaLeft) { + newScrollPosition.scrollLeft = this._context.viewLayout.getCurrentScrollLeft() + deltaLeft; + domNode.scrollLeft = 0; + } + } + + this._context.viewLayout.setScrollPositionNow(newScrollPosition); + }; + + // I've seen this happen both on the view dom node & on the lines content dom node. + this._register(dom.addDisposableListener(viewDomNode.domNode, 'scroll', (e: Event) => onBrowserDesperateReveal(viewDomNode.domNode, true, true))); + this._register(dom.addDisposableListener(linesContent.domNode, 'scroll', (e: Event) => onBrowserDesperateReveal(linesContent.domNode, true, false))); + this._register(dom.addDisposableListener(overflowGuardDomNode.domNode, 'scroll', (e: Event) => onBrowserDesperateReveal(overflowGuardDomNode.domNode, true, false))); + } + + public dispose(): void { + super.dispose(); + } + + private _setLayout(): void { + const layoutInfo = this._context.configuration.editor.layoutInfo; + + this.scrollbarDomNode.setLeft(layoutInfo.contentLeft); + this.scrollbarDomNode.setWidth(layoutInfo.contentWidth + layoutInfo.minimapWidth); + this.scrollbarDomNode.setHeight(layoutInfo.contentHeight); + } + + public getOverviewRulerLayoutInfo(): IOverviewRulerLayoutInfo { + return this.scrollbar.getOverviewRulerLayoutInfo(); + } + + public getDomNode(): FastDomNode { + return this.scrollbarDomNode; + } + + public delegateVerticalScrollbarMouseDown(browserEvent: IMouseEvent): void { + this.scrollbar.delegateVerticalScrollbarMouseDown(browserEvent); + } + + public delegateSliderMouseDown(e: ISimplifiedMouseEvent, onDragFinished: () => void): void { + this.scrollbar.delegateSliderMouseDown(e, onDragFinished); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.viewInfo) { + const editor = this._context.configuration.editor; + let newOpts: ScrollableElementChangeOptions = { + handleMouseWheel: editor.viewInfo.scrollbar.handleMouseWheel, + mouseWheelScrollSensitivity: editor.viewInfo.scrollbar.mouseWheelScrollSensitivity + }; + this.scrollbar.updateOptions(newOpts); + } + if (e.layoutInfo) { + this._setLayout(); + } + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return true; + } + public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { + this.scrollbar.updateClassName('editor-scrollable' + ' ' + getThemeTypeSelector(this._context.theme.type)); + return true; + } + + // --- end event handlers + + public prepareRender(ctx: RenderingContext): void { + // Nothing to do + } + + public render(ctx: RestrictedRenderingContext): void { + this.scrollbar.renderNow(); + } +} diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css new file mode 100644 index 0000000000..ff9f04b123 --- /dev/null +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .glyph-margin { + position: absolute; + top: 0; +} + +/* + Keeping name short for faster parsing. + cgmr = core glyph margin rendering (div) +*/ +.monaco-editor .margin-view-overlays .cgmr { + position: absolute; +} diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts new file mode 100644 index 0000000000..1d1cbfdd6b --- /dev/null +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts @@ -0,0 +1,200 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./glyphMargin'; +import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; + +export class DecorationToRender { + _decorationToRenderBrand: void; + + public startLineNumber: number; + public endLineNumber: number; + public className: string; + + constructor(startLineNumber: number, endLineNumber: number, className: string) { + this.startLineNumber = +startLineNumber; + this.endLineNumber = +endLineNumber; + this.className = String(className); + } +} + +export abstract class DedupOverlay extends DynamicViewOverlay { + + protected _render(visibleStartLineNumber: number, visibleEndLineNumber: number, decorations: DecorationToRender[]): string[][] { + + let output: string[][] = []; + for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { + let lineIndex = lineNumber - visibleStartLineNumber; + output[lineIndex] = []; + } + + if (decorations.length === 0) { + return output; + } + + decorations.sort((a, b) => { + if (a.className === b.className) { + if (a.startLineNumber === b.startLineNumber) { + return a.endLineNumber - b.endLineNumber; + } + return a.startLineNumber - b.startLineNumber; + } + return (a.className < b.className ? -1 : 1); + }); + + let prevClassName: string = null; + let prevEndLineIndex = 0; + for (let i = 0, len = decorations.length; i < len; i++) { + let d = decorations[i]; + let className = d.className; + let startLineIndex = Math.max(d.startLineNumber, visibleStartLineNumber) - visibleStartLineNumber; + let endLineIndex = Math.min(d.endLineNumber, visibleEndLineNumber) - visibleStartLineNumber; + + if (prevClassName === className) { + startLineIndex = Math.max(prevEndLineIndex + 1, startLineIndex); + prevEndLineIndex = Math.max(prevEndLineIndex, endLineIndex); + } else { + prevClassName = className; + prevEndLineIndex = endLineIndex; + } + + for (let i = startLineIndex; i <= prevEndLineIndex; i++) { + output[i].push(prevClassName); + } + } + + return output; + } +} + +export class GlyphMarginOverlay extends DedupOverlay { + + private _context: ViewContext; + private _lineHeight: number; + private _glyphMargin: boolean; + private _glyphMarginLeft: number; + private _glyphMarginWidth: number; + private _renderResult: string[]; + + constructor(context: ViewContext) { + super(); + this._context = context; + this._lineHeight = this._context.configuration.editor.lineHeight; + this._glyphMargin = this._context.configuration.editor.viewInfo.glyphMargin; + this._glyphMarginLeft = this._context.configuration.editor.layoutInfo.glyphMarginLeft; + this._glyphMarginWidth = this._context.configuration.editor.layoutInfo.glyphMarginWidth; + this._renderResult = null; + this._context.addEventHandler(this); + } + + public dispose(): void { + this._context.removeEventHandler(this); + this._context = null; + this._renderResult = null; + super.dispose(); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.lineHeight) { + this._lineHeight = this._context.configuration.editor.lineHeight; + } + if (e.viewInfo) { + this._glyphMargin = this._context.configuration.editor.viewInfo.glyphMargin; + } + if (e.layoutInfo) { + this._glyphMarginLeft = this._context.configuration.editor.layoutInfo.glyphMarginLeft; + this._glyphMarginWidth = this._context.configuration.editor.layoutInfo.glyphMarginWidth; + } + return true; + } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + return true; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return true; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollTopChanged; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + // --- end event handlers + + protected _getDecorations(ctx: RenderingContext): DecorationToRender[] { + let decorations = ctx.getDecorationsInViewport(); + let r: DecorationToRender[] = [], rLen = 0; + for (let i = 0, len = decorations.length; i < len; i++) { + let d = decorations[i]; + let glyphMarginClassName = d.source.options.glyphMarginClassName; + if (glyphMarginClassName) { + r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, glyphMarginClassName); + } + } + return r; + } + + public prepareRender(ctx: RenderingContext): void { + if (!this._glyphMargin) { + this._renderResult = null; + return; + } + + let visibleStartLineNumber = ctx.visibleRange.startLineNumber; + let visibleEndLineNumber = ctx.visibleRange.endLineNumber; + let toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx)); + + let lineHeight = this._lineHeight.toString(); + let left = this._glyphMarginLeft.toString(); + let width = this._glyphMarginWidth.toString(); + let common = '" style="left:' + left + 'px;width:' + width + 'px' + ';height:' + lineHeight + 'px;">'; + + let output: string[] = []; + for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { + let lineIndex = lineNumber - visibleStartLineNumber; + let classNames = toRender[lineIndex]; + + if (classNames.length === 0) { + output[lineIndex] = ''; + } else { + output[lineIndex] = ( + '`; + left += tabWidth; + } + + output[lineIndex] = result; + } + this._renderResult = output; + } + + public render(startLineNumber: number, lineNumber: number): string { + if (!this._renderResult) { + return ''; + } + let lineIndex = lineNumber - startLineNumber; + if (lineIndex < 0 || lineIndex >= this._renderResult.length) { + throw new Error('Unexpected render request'); + } + return this._renderResult[lineIndex]; + } +} + +registerThemingParticipant((theme, collector) => { + let editorGuideColor = theme.getColor(editorIndentGuides); + if (editorGuideColor) { + collector.addRule(`.monaco-editor .lines-content .cigr { background-color: ${editorGuideColor}; }`); + } +}); diff --git a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg new file mode 100644 index 0000000000..094c6acfbe --- /dev/null +++ b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg @@ -0,0 +1 @@ + diff --git a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac-2x.svg b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac-2x.svg new file mode 100644 index 0000000000..4dee276170 --- /dev/null +++ b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac-2x.svg @@ -0,0 +1 @@ +flipped-cursor-mac-2x \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac.svg b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac.svg new file mode 100644 index 0000000000..706d2e2ba2 --- /dev/null +++ b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac.svg @@ -0,0 +1 @@ +flipped-cursor-mac \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor.svg b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor.svg new file mode 100644 index 0000000000..6753e02878 --- /dev/null +++ b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css new file mode 100644 index 0000000000..dfd92bf100 --- /dev/null +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .margin-view-overlays .line-numbers { + position: absolute; + text-align: right; + display: inline-block; + vertical-align: middle; + box-sizing: border-box; + cursor: default; + height: 100%; +} + +.monaco-editor .relative-current-line-number { + text-align: left; + display: inline-block; + width: 100%; +} + +.monaco-editor .margin-view-overlays .line-numbers { + cursor: -webkit-image-set( + url('flipped-cursor.svg') 1x, + url('flipped-cursor-2x.svg') 2x + ) 30 0, default; +} + +.monaco-editor.mac .margin-view-overlays .line-numbers { + cursor: -webkit-image-set( + url('flipped-cursor-mac.svg') 1x, + url('flipped-cursor-mac-2x.svg') 2x + ) 24 3, default; +} + +.monaco-editor .margin-view-overlays .line-numbers.lh-odd { + margin-top: 1px; +} diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts new file mode 100644 index 0000000000..3c0bc8f7b7 --- /dev/null +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./lineNumbers'; +import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import * as platform from 'vs/base/common/platform'; +import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { Position } from 'vs/editor/common/core/position'; + +export class LineNumbersOverlay extends DynamicViewOverlay { + + public static CLASS_NAME = 'line-numbers'; + + private _context: ViewContext; + + private _lineHeight: number; + private _renderLineNumbers: boolean; + private _renderCustomLineNumbers: (lineNumber: number) => string; + private _renderRelativeLineNumbers: boolean; + private _lineNumbersLeft: number; + private _lineNumbersWidth: number; + + private _lastCursorModelPosition: Position; + private _renderResult: string[]; + + constructor(context: ViewContext) { + super(); + this._context = context; + + this._readConfig(); + + this._lastCursorModelPosition = new Position(1, 1); + this._renderResult = null; + this._context.addEventHandler(this); + } + + private _readConfig(): void { + const config = this._context.configuration.editor; + this._lineHeight = config.lineHeight; + this._renderLineNumbers = config.viewInfo.renderLineNumbers; + this._renderCustomLineNumbers = config.viewInfo.renderCustomLineNumbers; + this._renderRelativeLineNumbers = config.viewInfo.renderRelativeLineNumbers; + this._lineNumbersLeft = config.layoutInfo.lineNumbersLeft; + this._lineNumbersWidth = config.layoutInfo.lineNumbersWidth; + } + + public dispose(): void { + this._context.removeEventHandler(this); + this._context = null; + this._renderResult = null; + super.dispose(); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + this._readConfig(); + return true; + } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + const primaryViewPosition = e.selections[0].getPosition(); + this._lastCursorModelPosition = this._context.model.coordinatesConverter.convertViewPositionToModelPosition(primaryViewPosition); + + if (this._renderRelativeLineNumbers) { + return true; + } + return false; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return true; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollTopChanged; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + // --- end event handlers + + private _getLineRenderLineNumber(viewLineNumber: number): string { + const modelPosition = this._context.model.coordinatesConverter.convertViewPositionToModelPosition(new Position(viewLineNumber, 1)); + if (modelPosition.column !== 1) { + return ''; + } + let modelLineNumber = modelPosition.lineNumber; + + if (this._renderCustomLineNumbers) { + return this._renderCustomLineNumbers(modelLineNumber); + } + + if (this._renderRelativeLineNumbers) { + let diff = Math.abs(this._lastCursorModelPosition.lineNumber - modelLineNumber); + if (diff === 0) { + return '' + modelLineNumber + ''; + } + return String(diff); + } + + return String(modelLineNumber); + } + + public prepareRender(ctx: RenderingContext): void { + if (!this._renderLineNumbers) { + this._renderResult = null; + return; + } + + let lineHeightClassName = (platform.isLinux ? (this._lineHeight % 2 === 0 ? ' lh-even' : ' lh-odd') : ''); + let visibleStartLineNumber = ctx.visibleRange.startLineNumber; + let visibleEndLineNumber = ctx.visibleRange.endLineNumber; + let common = '
'; + + let output: string[] = []; + for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { + let lineIndex = lineNumber - visibleStartLineNumber; + + let renderLineNumber = this._getLineRenderLineNumber(lineNumber); + if (renderLineNumber) { + output[lineIndex] = ( + common + + renderLineNumber + + '
' + ); + } else { + output[lineIndex] = ''; + } + } + + this._renderResult = output; + } + + public render(startLineNumber: number, lineNumber: number): string { + if (!this._renderResult) { + return ''; + } + let lineIndex = lineNumber - startLineNumber; + if (lineIndex < 0 || lineIndex >= this._renderResult.length) { + throw new Error('Unexpected render request'); + } + return this._renderResult[lineIndex]; + } +} + +// theming + +registerThemingParticipant((theme, collector) => { + let lineNumbers = theme.getColor(editorLineNumbers); + if (lineNumbers) { + collector.addRule(`.monaco-editor .line-numbers { color: ${lineNumbers}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts new file mode 100644 index 0000000000..0bf58e50aa --- /dev/null +++ b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; + +class FloatHorizontalRange { + _floatHorizontalRangeBrand: void; + + public readonly left: number; + public readonly width: number; + + constructor(left: number, width: number) { + this.left = left; + this.width = width; + } + + public toString(): string { + return `[${this.left},${this.width}]`; + } + + public static compare(a: FloatHorizontalRange, b: FloatHorizontalRange): number { + return a.left - b.left; + } +} + +export class RangeUtil { + + /** + * Reusing the same range here + * because IE is buggy and constantly freezes when using a large number + * of ranges and calling .detach on them + */ + private static _handyReadyRange: Range; + + private static _createRange(): Range { + if (!this._handyReadyRange) { + this._handyReadyRange = document.createRange(); + } + return this._handyReadyRange; + } + + private static _detachRange(range: Range, endNode: HTMLElement): void { + // Move range out of the span node, IE doesn't like having many ranges in + // the same spot and will act badly for lines containing dashes ('-') + range.selectNodeContents(endNode); + } + + private static _readClientRects(startElement: Node, startOffset: number, endElement: Node, endOffset: number, endNode: HTMLElement): ClientRectList { + let range = this._createRange(); + try { + range.setStart(startElement, startOffset); + range.setEnd(endElement, endOffset); + + return range.getClientRects(); + } catch (e) { + // This is life ... + return null; + } finally { + this._detachRange(range, endNode); + } + } + + private static _mergeAdjacentRanges(ranges: FloatHorizontalRange[]): HorizontalRange[] { + if (ranges.length === 1) { + // There is nothing to merge + return [new HorizontalRange(ranges[0].left, ranges[0].width)]; + } + + ranges.sort(FloatHorizontalRange.compare); + + let result: HorizontalRange[] = [], resultLen = 0; + let prevLeft = ranges[0].left; + let prevWidth = ranges[0].width; + + for (let i = 1, len = ranges.length; i < len; i++) { + const range = ranges[i]; + const myLeft = range.left; + const myWidth = range.width; + + if (prevLeft + prevWidth + 0.9 /* account for browser's rounding errors*/ >= myLeft) { + prevWidth = Math.max(prevWidth, myLeft + myWidth - prevLeft); + } else { + result[resultLen++] = new HorizontalRange(prevLeft, prevWidth); + prevLeft = myLeft; + prevWidth = myWidth; + } + } + + result[resultLen++] = new HorizontalRange(prevLeft, prevWidth); + + return result; + } + + private static _createHorizontalRangesFromClientRects(clientRects: ClientRectList, clientRectDeltaLeft: number): HorizontalRange[] { + if (!clientRects || clientRects.length === 0) { + return null; + } + + // We go through FloatHorizontalRange because it has been observed in bi-di text + // that the clientRects are not coming in sorted from the browser + + let result: FloatHorizontalRange[] = []; + for (let i = 0, len = clientRects.length; i < len; i++) { + const clientRect = clientRects[i]; + result[i] = new FloatHorizontalRange(Math.max(0, clientRect.left - clientRectDeltaLeft), clientRect.width); + } + + return this._mergeAdjacentRanges(result); + } + + public static readHorizontalRanges(domNode: HTMLElement, startChildIndex: number, startOffset: number, endChildIndex: number, endOffset: number, clientRectDeltaLeft: number, endNode: HTMLElement): HorizontalRange[] { + // Panic check + let min = 0; + let max = domNode.children.length - 1; + if (min > max) { + return null; + } + startChildIndex = Math.min(max, Math.max(min, startChildIndex)); + endChildIndex = Math.min(max, Math.max(min, endChildIndex)); + + // If crossing over to a span only to select offset 0, then use the previous span's maximum offset + // Chrome is buggy and doesn't handle 0 offsets well sometimes. + if (startChildIndex !== endChildIndex) { + if (endChildIndex > 0 && endOffset === 0) { + endChildIndex--; + endOffset = Number.MAX_VALUE; + } + } + + let startElement = domNode.children[startChildIndex].firstChild; + let endElement = domNode.children[endChildIndex].firstChild; + + if (!startElement || !endElement) { + return null; + } + + startOffset = Math.min(startElement.textContent.length, Math.max(0, startOffset)); + endOffset = Math.min(endElement.textContent.length, Math.max(0, endOffset)); + + let clientRects = this._readClientRects(startElement, startOffset, endElement, endOffset, endNode); + return this._createHorizontalRangesFromClientRects(clientRects, clientRectDeltaLeft); + } +} diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts new file mode 100644 index 0000000000..381d0ba712 --- /dev/null +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -0,0 +1,613 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as browser from 'vs/base/browser/browser'; +import * as platform from 'vs/base/common/platform'; +import * as strings from 'vs/base/common/strings'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { IConfiguration } from 'vs/editor/common/editorCommon'; +import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; +import { renderViewLine, RenderLineInput, CharacterMapping } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { IVisibleLine } from 'vs/editor/browser/view/viewLayer'; +import { RangeUtil } from 'vs/editor/browser/viewParts/lines/rangeUtil'; +import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { ThemeType, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { IStringBuilder } from 'vs/editor/common/core/stringBuilder'; + +const canUseFastRenderedViewLine = (function () { + if (platform.isNative) { + // In VSCode we know very well when the zoom level changes + return true; + } + + if (platform.isLinux || browser.isFirefox || browser.isSafari) { + // On Linux, it appears that zooming affects char widths (in pixels), which is unexpected. + // -- + // Even though we read character widths correctly, having read them at a specific zoom level + // does not mean they are the same at the current zoom level. + // -- + // This could be improved if we ever figure out how to get an event when browsers zoom, + // but until then we have to stick with reading client rects. + // -- + // The same has been observed with Firefox on Windows7 + // -- + // The same has been oversved with Safari + return false; + } + + return true; +})(); + +const alwaysRenderInlineSelection = (browser.isEdgeOrIE); + +export class DomReadingContext { + + private readonly _domNode: HTMLElement; + private _clientRectDeltaLeft: number; + private _clientRectDeltaLeftRead: boolean; + public get clientRectDeltaLeft(): number { + if (!this._clientRectDeltaLeftRead) { + this._clientRectDeltaLeftRead = true; + this._clientRectDeltaLeft = this._domNode.getBoundingClientRect().left; + } + return this._clientRectDeltaLeft; + } + + public readonly endNode: HTMLElement; + + constructor(domNode: HTMLElement, endNode: HTMLElement) { + this._domNode = domNode; + this._clientRectDeltaLeft = 0; + this._clientRectDeltaLeftRead = false; + this.endNode = endNode; + } + +} + +export class ViewLineOptions { + public readonly themeType: ThemeType; + public readonly renderWhitespace: 'none' | 'boundary' | 'all'; + public readonly renderControlCharacters: boolean; + public readonly spaceWidth: number; + public readonly useMonospaceOptimizations: boolean; + public readonly lineHeight: number; + public readonly stopRenderingLineAfter: number; + public readonly fontLigatures: boolean; + + constructor(config: IConfiguration, themeType: ThemeType) { + this.themeType = themeType; + this.renderWhitespace = config.editor.viewInfo.renderWhitespace; + this.renderControlCharacters = config.editor.viewInfo.renderControlCharacters; + this.spaceWidth = config.editor.fontInfo.spaceWidth; + this.useMonospaceOptimizations = ( + config.editor.fontInfo.isMonospace + && !config.editor.viewInfo.disableMonospaceOptimizations + ); + this.lineHeight = config.editor.lineHeight; + this.stopRenderingLineAfter = config.editor.viewInfo.stopRenderingLineAfter; + this.fontLigatures = config.editor.viewInfo.fontLigatures; + } + + public equals(other: ViewLineOptions): boolean { + return ( + this.themeType === other.themeType + && this.renderWhitespace === other.renderWhitespace + && this.renderControlCharacters === other.renderControlCharacters + && this.spaceWidth === other.spaceWidth + && this.useMonospaceOptimizations === other.useMonospaceOptimizations + && this.lineHeight === other.lineHeight + && this.stopRenderingLineAfter === other.stopRenderingLineAfter + && this.fontLigatures === other.fontLigatures + ); + } +} + +export class ViewLine implements IVisibleLine { + + public static CLASS_NAME = 'view-line'; + + private _options: ViewLineOptions; + private _isMaybeInvalid: boolean; + private _renderedViewLine: IRenderedViewLine; + + constructor(options: ViewLineOptions) { + this._options = options; + this._isMaybeInvalid = true; + this._renderedViewLine = null; + } + + // --- begin IVisibleLineData + + public getDomNode(): HTMLElement { + if (this._renderedViewLine && this._renderedViewLine.domNode) { + return this._renderedViewLine.domNode.domNode; + } + return null; + } + public setDomNode(domNode: HTMLElement): void { + if (this._renderedViewLine) { + this._renderedViewLine.domNode = createFastDomNode(domNode); + } else { + throw new Error('I have no rendered view line to set the dom node to...'); + } + } + + public onContentChanged(): void { + this._isMaybeInvalid = true; + } + public onTokensChanged(): void { + this._isMaybeInvalid = true; + } + public onDecorationsChanged(): void { + this._isMaybeInvalid = true; + } + public onOptionsChanged(newOptions: ViewLineOptions): void { + this._isMaybeInvalid = true; + this._options = newOptions; + } + public onSelectionChanged(): boolean { + if (alwaysRenderInlineSelection || this._options.themeType === HIGH_CONTRAST) { + this._isMaybeInvalid = true; + return true; + } + return false; + } + + public renderLine(lineNumber: number, deltaTop: number, viewportData: ViewportData, sb: IStringBuilder): boolean { + if (this._isMaybeInvalid === false) { + // it appears that nothing relevant has changed + return false; + } + + this._isMaybeInvalid = false; + + const lineData = viewportData.getViewLineRenderingData(lineNumber); + const options = this._options; + const actualInlineDecorations = LineDecoration.filter(lineData.inlineDecorations, lineNumber, lineData.minColumn, lineData.maxColumn); + + if (alwaysRenderInlineSelection || options.themeType === HIGH_CONTRAST) { + const selections = viewportData.selections; + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + + if (selection.endLineNumber < lineNumber || selection.startLineNumber > lineNumber) { + // Selection does not intersect line + continue; + } + + let startColumn = (selection.startLineNumber === lineNumber ? selection.startColumn : lineData.minColumn); + let endColumn = (selection.endLineNumber === lineNumber ? selection.endColumn : lineData.maxColumn); + + if (startColumn < endColumn) { + actualInlineDecorations.push(new LineDecoration(startColumn, endColumn, 'inline-selected-text', false)); + } + } + } + + let renderLineInput = new RenderLineInput( + options.useMonospaceOptimizations, + lineData.content, + lineData.mightContainRTL, + lineData.minColumn - 1, + lineData.tokens, + actualInlineDecorations, + lineData.tabSize, + options.spaceWidth, + options.stopRenderingLineAfter, + options.renderWhitespace, + options.renderControlCharacters, + options.fontLigatures + ); + + if (this._renderedViewLine && this._renderedViewLine.input.equals(renderLineInput)) { + // no need to do anything, we have the same render input + return false; + } + + sb.appendASCIIString('
'); + + const output = renderViewLine(renderLineInput, sb); + + sb.appendASCIIString('
'); + + let renderedViewLine: IRenderedViewLine = null; + if (canUseFastRenderedViewLine && options.useMonospaceOptimizations && !output.containsForeignElements) { + let isRegularASCII = true; + if (lineData.mightContainNonBasicASCII) { + isRegularASCII = strings.isBasicASCII(lineData.content); + } + + if (isRegularASCII && lineData.content.length < 1000) { + // Browser rounding errors have been observed in Chrome and IE, so using the fast + // view line only for short lines. Please test before removing the length check... + renderedViewLine = new FastRenderedViewLine( + this._renderedViewLine ? this._renderedViewLine.domNode : null, + renderLineInput, + output.characterMapping + ); + } + } + + if (!renderedViewLine) { + renderedViewLine = createRenderedLine( + this._renderedViewLine ? this._renderedViewLine.domNode : null, + renderLineInput, + output.characterMapping, + output.containsRTL, + output.containsForeignElements + ); + } + + this._renderedViewLine = renderedViewLine; + + return true; + } + + public layoutLine(lineNumber: number, deltaTop: number): void { + if (this._renderedViewLine && this._renderedViewLine.domNode) { + this._renderedViewLine.domNode.setTop(deltaTop); + this._renderedViewLine.domNode.setHeight(this._options.lineHeight); + } + } + + // --- end IVisibleLineData + + public getWidth(): number { + if (!this._renderedViewLine) { + return 0; + } + return this._renderedViewLine.getWidth(); + } + + public getWidthIsFast(): boolean { + if (!this._renderedViewLine) { + return true; + } + return this._renderedViewLine.getWidthIsFast(); + } + + public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] { + startColumn = Math.min(this._renderedViewLine.input.lineContent.length + 1, Math.max(1, startColumn)); + endColumn = Math.min(this._renderedViewLine.input.lineContent.length + 1, Math.max(1, endColumn)); + return this._renderedViewLine.getVisibleRangesForRange(startColumn, endColumn, context); + } + + public getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number { + return this._renderedViewLine.getColumnOfNodeOffset(lineNumber, spanNode, offset); + } +} + +interface IRenderedViewLine { + domNode: FastDomNode; + readonly input: RenderLineInput; + getWidth(): number; + getWidthIsFast(): boolean; + getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[]; + getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number; +} + +/** + * A rendered line which is guaranteed to contain only regular ASCII and is rendered with a monospace font. + */ +class FastRenderedViewLine implements IRenderedViewLine { + + public domNode: FastDomNode; + public readonly input: RenderLineInput; + + private readonly _characterMapping: CharacterMapping; + private readonly _charWidth: number; + + constructor(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping) { + this.domNode = domNode; + this.input = renderLineInput; + + this._characterMapping = characterMapping; + this._charWidth = renderLineInput.spaceWidth; + } + + public getWidth(): number { + return this._getCharPosition(this._characterMapping.length); + } + + public getWidthIsFast(): boolean { + return true; + } + + public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] { + startColumn = startColumn | 0; // @perf + endColumn = endColumn | 0; // @perf + const stopRenderingLineAfter = this.input.stopRenderingLineAfter | 0; // @perf + + if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter && endColumn > stopRenderingLineAfter) { + // This range is obviously not visible + return null; + } + + if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter) { + startColumn = stopRenderingLineAfter; + } + + if (stopRenderingLineAfter !== -1 && endColumn > stopRenderingLineAfter) { + endColumn = stopRenderingLineAfter; + } + + const startPosition = this._getCharPosition(startColumn); + const endPosition = this._getCharPosition(endColumn); + return [new HorizontalRange(startPosition, endPosition - startPosition)]; + } + + private _getCharPosition(column: number): number { + const charOffset = this._characterMapping.getAbsoluteOffsets(); + if (charOffset.length === 0) { + // No characters on this line + return 0; + } + return Math.round(this._charWidth * charOffset[column - 1]); + } + + public getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number { + let spanNodeTextContentLength = spanNode.textContent.length; + + let spanIndex = -1; + while (spanNode) { + spanNode = spanNode.previousSibling; + spanIndex++; + } + + let charOffset = this._characterMapping.partDataToCharOffset(spanIndex, spanNodeTextContentLength, offset); + return charOffset + 1; + } +} + +/** + * Every time we render a line, we save what we have rendered in an instance of this class. + */ +class RenderedViewLine implements IRenderedViewLine { + + public domNode: FastDomNode; + public readonly input: RenderLineInput; + + protected readonly _characterMapping: CharacterMapping; + private readonly _isWhitespaceOnly: boolean; + private readonly _containsForeignElements: boolean; + private _cachedWidth: number; + + /** + * This is a map that is used only when the line is guaranteed to have no RTL text. + */ + private _pixelOffsetCache: Int32Array; + + constructor(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: boolean) { + this.domNode = domNode; + this.input = renderLineInput; + this._characterMapping = characterMapping; + this._isWhitespaceOnly = /^\s*$/.test(renderLineInput.lineContent); + this._containsForeignElements = containsForeignElements; + this._cachedWidth = -1; + + this._pixelOffsetCache = null; + if (!containsRTL || this._characterMapping.length === 0 /* the line is empty */) { + this._pixelOffsetCache = new Int32Array(this._characterMapping.length + 1); + for (let column = 0, len = this._characterMapping.length; column <= len; column++) { + this._pixelOffsetCache[column] = -1; + } + } + } + + // --- Reading from the DOM methods + + protected _getReadingTarget(): HTMLElement { + return this.domNode.domNode.firstChild; + } + + /** + * Width of the line in pixels + */ + public getWidth(): number { + if (this._cachedWidth === -1) { + this._cachedWidth = this._getReadingTarget().offsetWidth; + } + return this._cachedWidth; + } + + public getWidthIsFast(): boolean { + if (this._cachedWidth === -1) { + return false; + } + return true; + } + + /** + * Visible ranges for a model range + */ + public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] { + startColumn = startColumn | 0; // @perf + endColumn = endColumn | 0; // @perf + const stopRenderingLineAfter = this.input.stopRenderingLineAfter | 0; // @perf + + if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter && endColumn > stopRenderingLineAfter) { + // This range is obviously not visible + return null; + } + + if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter) { + startColumn = stopRenderingLineAfter; + } + + if (stopRenderingLineAfter !== -1 && endColumn > stopRenderingLineAfter) { + endColumn = stopRenderingLineAfter; + } + + if (this._pixelOffsetCache !== null) { + // the text is LTR + let startOffset = this._readPixelOffset(startColumn, context); + if (startOffset === -1) { + return null; + } + + let endOffset = this._readPixelOffset(endColumn, context); + if (endOffset === -1) { + return null; + } + + return [new HorizontalRange(startOffset, endOffset - startOffset)]; + } + + return this._readVisibleRangesForRange(startColumn, endColumn, context); + } + + protected _readVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] { + if (startColumn === endColumn) { + let pixelOffset = this._readPixelOffset(startColumn, context); + if (pixelOffset === -1) { + return null; + } else { + return [new HorizontalRange(pixelOffset, 0)]; + } + } else { + return this._readRawVisibleRangesForRange(startColumn, endColumn, context); + } + } + + protected _readPixelOffset(column: number, context: DomReadingContext): number { + if (this._characterMapping.length === 0) { + // This line has no content + if (!this._containsForeignElements) { + // We can assume the line is really empty + return 0; + } + } + + if (this._pixelOffsetCache !== null) { + // the text is LTR + + let cachedPixelOffset = this._pixelOffsetCache[column]; + if (cachedPixelOffset !== -1) { + return cachedPixelOffset; + } + + let result = this._actualReadPixelOffset(column, context); + this._pixelOffsetCache[column] = result; + return result; + } + + return this._actualReadPixelOffset(column, context); + } + + private _actualReadPixelOffset(column: number, context: DomReadingContext): number { + if (this._characterMapping.length === 0) { + // This line has no content + let r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), 0, 0, 0, 0, context.clientRectDeltaLeft, context.endNode); + if (!r || r.length === 0) { + return -1; + } + return r[0].left; + } + + if (column === this._characterMapping.length && this._isWhitespaceOnly && !this._containsForeignElements) { + // This branch helps in the case of whitespace only lines which have a width set + return this.getWidth(); + } + + let partData = this._characterMapping.charOffsetToPartData(column - 1); + let partIndex = CharacterMapping.getPartIndex(partData); + let charOffsetInPart = CharacterMapping.getCharIndex(partData); + + let r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), partIndex, charOffsetInPart, partIndex, charOffsetInPart, context.clientRectDeltaLeft, context.endNode); + if (!r || r.length === 0) { + return -1; + } + return r[0].left; + } + + private _readRawVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] { + + if (startColumn === 1 && endColumn === this._characterMapping.length) { + // This branch helps IE with bidi text & gives a performance boost to other browsers when reading visible ranges for an entire line + + return [new HorizontalRange(0, this.getWidth())]; + } + + let startPartData = this._characterMapping.charOffsetToPartData(startColumn - 1); + let startPartIndex = CharacterMapping.getPartIndex(startPartData); + let startCharOffsetInPart = CharacterMapping.getCharIndex(startPartData); + + let endPartData = this._characterMapping.charOffsetToPartData(endColumn - 1); + let endPartIndex = CharacterMapping.getPartIndex(endPartData); + let endCharOffsetInPart = CharacterMapping.getCharIndex(endPartData); + + return RangeUtil.readHorizontalRanges(this._getReadingTarget(), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, context.clientRectDeltaLeft, context.endNode); + } + + /** + * Returns the column for the text found at a specific offset inside a rendered dom node + */ + public getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number { + let spanNodeTextContentLength = spanNode.textContent.length; + + let spanIndex = -1; + while (spanNode) { + spanNode = spanNode.previousSibling; + spanIndex++; + } + + let charOffset = this._characterMapping.partDataToCharOffset(spanIndex, spanNodeTextContentLength, offset); + return charOffset + 1; + } +} + +class WebKitRenderedViewLine extends RenderedViewLine { + protected _readVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] { + let output = super._readVisibleRangesForRange(startColumn, endColumn, context); + + if (!output || output.length === 0 || startColumn === endColumn || (startColumn === 1 && endColumn === this._characterMapping.length)) { + return output; + } + + // WebKit is buggy and returns an expanded range (to contain words in some cases) + // The last client rect is enlarged (I think) + + // This is an attempt to patch things up + // Find position of previous column + let beforeEndPixelOffset = this._readPixelOffset(endColumn - 1, context); + // Find position of last column + let endPixelOffset = this._readPixelOffset(endColumn, context); + + if (beforeEndPixelOffset !== -1 && endPixelOffset !== -1) { + let isLTR = (beforeEndPixelOffset <= endPixelOffset); + let lastRange = output[output.length - 1]; + + if (isLTR && lastRange.left < endPixelOffset) { + // Trim down the width of the last visible range to not go after the last column's position + lastRange.width = endPixelOffset - lastRange.left; + } + } + + return output; + } +} + +const createRenderedLine: (domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: boolean) => RenderedViewLine = (function () { + if (browser.isWebKit) { + return createWebKitRenderedLine; + } + return createNormalRenderedLine; +})(); + +function createWebKitRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: boolean): RenderedViewLine { + return new WebKitRenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements); +} + +function createNormalRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: boolean): RenderedViewLine { + return new RenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements); +} diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.css b/src/vs/editor/browser/viewParts/lines/viewLines.css new file mode 100644 index 0000000000..b97c5cc28f --- /dev/null +++ b/src/vs/editor/browser/viewParts/lines/viewLines.css @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Uncomment to see lines flashing when they're painted */ +/*.monaco-editor .view-lines > .view-line { + background-color: none; + animation-name: flash-background; + animation-duration: 800ms; +} +@keyframes flash-background { + 0% { background-color: lightgreen; } + 100% { background-color: none } +}*/ + +.monaco-editor .lines-content, +.monaco-editor .view-line, +.monaco-editor .view-lines { + -webkit-user-select: text; + -ms-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -o-user-select: text; + user-select: text; +} + +.monaco-editor.ie .lines-content, +.monaco-editor.ie .view-line, +.monaco-editor.ie .view-lines { + -ms-user-select: none; + user-select: none; +} + +.monaco-editor .view-lines { + cursor: text; + white-space: nowrap; +} + +.monaco-editor.vs-dark.mac .view-lines, +.monaco-editor.hc-black.mac .view-lines { + cursor: -webkit-image-set(url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=') 1x, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC') 2x) 5 8, text; +} + +.monaco-editor .view-line { + position: absolute; + width: 100%; +} + +/* TODO@tokenization bootstrap fix */ +/*.monaco-editor .view-line > span > span { + float: none; + min-height: inherit; + margin-left: inherit; +}*/ \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts new file mode 100644 index 0000000000..b3a5bff7a3 --- /dev/null +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -0,0 +1,689 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./viewLines'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { FastDomNode } from 'vs/base/browser/fastDomNode'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { VisibleLinesCollection, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; +import { ViewLineOptions, DomReadingContext, ViewLine } from 'vs/editor/browser/viewParts/lines/viewLine'; +import { Configuration } from 'vs/editor/browser/config/configuration'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { IViewLines, HorizontalRange, LineVisibleRanges } from 'vs/editor/common/view/renderingContext'; +import { Viewport } from 'vs/editor/common/viewModel/viewModel'; +import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { ScrollType } from 'vs/editor/common/editorCommon'; + +class LastRenderedData { + + private _currentVisibleRange: Range; + + constructor() { + this._currentVisibleRange = new Range(1, 1, 1, 1); + } + + public getCurrentVisibleRange(): Range { + return this._currentVisibleRange; + } + + public setCurrentVisibleRange(currentVisibleRange: Range): void { + this._currentVisibleRange = currentVisibleRange; + } +} + +class HorizontalRevealRequest { + + public readonly lineNumber: number; + public readonly startColumn: number; + public readonly endColumn: number; + public readonly startScrollTop: number; + public readonly stopScrollTop: number; + public readonly scrollType: ScrollType; + + constructor(lineNumber: number, startColumn: number, endColumn: number, startScrollTop: number, stopScrollTop: number, scrollType: ScrollType) { + this.lineNumber = lineNumber; + this.startColumn = startColumn; + this.endColumn = endColumn; + this.startScrollTop = startScrollTop; + this.stopScrollTop = stopScrollTop; + this.scrollType = scrollType; + } +} + +export class ViewLines extends ViewPart implements IVisibleLinesHost, IViewLines { + /** + * Adds this ammount of pixels to the right of lines (no-one wants to type near the edge of the viewport) + */ + private static HORIZONTAL_EXTRA_PX = 30; + + private readonly _linesContent: FastDomNode; + private readonly _textRangeRestingSpot: HTMLElement; + private readonly _visibleLines: VisibleLinesCollection; + private readonly domNode: FastDomNode; + + // --- config + private _lineHeight: number; + private _typicalHalfwidthCharacterWidth: number; + private _isViewportWrapping: boolean; + private _revealHorizontalRightPadding: number; + private _canUseLayerHinting: boolean; + private _viewLineOptions: ViewLineOptions; + + // --- width + private _maxLineWidth: number; + private _asyncUpdateLineWidths: RunOnceScheduler; + + private _horizontalRevealRequest: HorizontalRevealRequest; + private _lastRenderedData: LastRenderedData; + + constructor(context: ViewContext, linesContent: FastDomNode) { + super(context); + this._linesContent = linesContent; + this._textRangeRestingSpot = document.createElement('div'); + this._visibleLines = new VisibleLinesCollection(this); + this.domNode = this._visibleLines.domNode; + + const conf = this._context.configuration; + + this._lineHeight = conf.editor.lineHeight; + this._typicalHalfwidthCharacterWidth = conf.editor.fontInfo.typicalHalfwidthCharacterWidth; + this._isViewportWrapping = conf.editor.wrappingInfo.isViewportWrapping; + this._revealHorizontalRightPadding = conf.editor.viewInfo.revealHorizontalRightPadding; + this._canUseLayerHinting = conf.editor.canUseLayerHinting; + this._viewLineOptions = new ViewLineOptions(conf, this._context.theme.type); + + PartFingerprints.write(this.domNode, PartFingerprint.ViewLines); + this.domNode.setClassName('view-lines'); + Configuration.applyFontInfo(this.domNode, conf.editor.fontInfo); + + // --- width & height + this._maxLineWidth = 0; + this._asyncUpdateLineWidths = new RunOnceScheduler(() => { + this._updateLineWidthsSlow(); + }, 200); + + this._lastRenderedData = new LastRenderedData(); + + this._horizontalRevealRequest = null; + } + + public dispose(): void { + this._asyncUpdateLineWidths.dispose(); + super.dispose(); + } + + public getDomNode(): FastDomNode { + return this.domNode; + } + + // ---- begin IVisibleLinesHost + + public createVisibleLine(): ViewLine { + return new ViewLine(this._viewLineOptions); + } + + // ---- end IVisibleLinesHost + + // ---- begin view event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + this._visibleLines.onConfigurationChanged(e); + if (e.wrappingInfo) { + this._maxLineWidth = 0; + } + + const conf = this._context.configuration; + + if (e.lineHeight) { + this._lineHeight = conf.editor.lineHeight; + } + if (e.fontInfo) { + this._typicalHalfwidthCharacterWidth = conf.editor.fontInfo.typicalHalfwidthCharacterWidth; + } + if (e.wrappingInfo) { + this._isViewportWrapping = conf.editor.wrappingInfo.isViewportWrapping; + } + if (e.viewInfo) { + this._revealHorizontalRightPadding = conf.editor.viewInfo.revealHorizontalRightPadding; + } + if (e.canUseLayerHinting) { + this._canUseLayerHinting = conf.editor.canUseLayerHinting; + } + if (e.fontInfo) { + Configuration.applyFontInfo(this.domNode, conf.editor.fontInfo); + } + + this._onOptionsMaybeChanged(); + + if (e.layoutInfo) { + this._maxLineWidth = 0; + } + + return true; + } + private _onOptionsMaybeChanged(): boolean { + const conf = this._context.configuration; + + let newViewLineOptions = new ViewLineOptions(conf, this._context.theme.type); + if (!this._viewLineOptions.equals(newViewLineOptions)) { + this._viewLineOptions = newViewLineOptions; + + let startLineNumber = this._visibleLines.getStartLineNumber(); + let endLineNumber = this._visibleLines.getEndLineNumber(); + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + let line = this._visibleLines.getVisibleLine(lineNumber); + line.onOptionsChanged(this._viewLineOptions); + } + return true; + } + + return false; + } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + let rendStartLineNumber = this._visibleLines.getStartLineNumber(); + let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + let r = false; + for (let lineNumber = rendStartLineNumber; lineNumber <= rendEndLineNumber; lineNumber++) { + r = this._visibleLines.getVisibleLine(lineNumber).onSelectionChanged() || r; + } + return r; + } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + if (true/*e.inlineDecorationsChanged*/) { + let rendStartLineNumber = this._visibleLines.getStartLineNumber(); + let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + for (let lineNumber = rendStartLineNumber; lineNumber <= rendEndLineNumber; lineNumber++) { + this._visibleLines.getVisibleLine(lineNumber).onDecorationsChanged(); + } + } + return true; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + let shouldRender = this._visibleLines.onFlushed(e); + this._maxLineWidth = 0; + return shouldRender; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return this._visibleLines.onLinesChanged(e); + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return this._visibleLines.onLinesDeleted(e); + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return this._visibleLines.onLinesInserted(e); + } + public onRevealRangeRequest(e: viewEvents.ViewRevealRangeRequestEvent): boolean { + // Using the future viewport here in order to handle multiple + // incoming reveal range requests that might all desire to be animated + const desiredScrollTop = this._computeScrollTopToRevealRange(this._context.viewLayout.getFutureViewport(), e.range, e.verticalType); + + // validate the new desired scroll top + let newScrollPosition = this._context.viewLayout.validateScrollPosition({ scrollTop: desiredScrollTop }); + + if (e.revealHorizontal) { + if (e.range.startLineNumber !== e.range.endLineNumber) { + // Two or more lines? => scroll to base (That's how you see most of the two lines) + newScrollPosition = { + scrollTop: newScrollPosition.scrollTop, + scrollLeft: 0 + }; + } else { + // We don't necessarily know the horizontal offset of this range since the line might not be in the view... + this._horizontalRevealRequest = new HorizontalRevealRequest(e.range.startLineNumber, e.range.startColumn, e.range.endColumn, this._context.viewLayout.getCurrentScrollTop(), newScrollPosition.scrollTop, e.scrollType); + } + } else { + this._horizontalRevealRequest = null; + } + + const scrollTopDelta = Math.abs(this._context.viewLayout.getCurrentScrollTop() - newScrollPosition.scrollTop); + if (e.scrollType === ScrollType.Smooth && scrollTopDelta > this._lineHeight) { + this._context.viewLayout.setScrollPositionSmooth(newScrollPosition); + } else { + this._context.viewLayout.setScrollPositionNow(newScrollPosition); + } + + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + if (this._horizontalRevealRequest && e.scrollLeftChanged) { + // cancel any outstanding horizontal reveal request if someone else scrolls horizontally. + this._horizontalRevealRequest = null; + } + if (this._horizontalRevealRequest && e.scrollTopChanged) { + const min = Math.min(this._horizontalRevealRequest.startScrollTop, this._horizontalRevealRequest.stopScrollTop); + const max = Math.max(this._horizontalRevealRequest.startScrollTop, this._horizontalRevealRequest.stopScrollTop); + if (e.scrollTop < min || e.scrollTop > max) { + // cancel any outstanding horizontal reveal request if someone else scrolls vertically. + this._horizontalRevealRequest = null; + } + } + this.domNode.setWidth(e.scrollWidth); + return this._visibleLines.onScrollChanged(e) || true; + } + + public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + return this._visibleLines.onTokensChanged(e); + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return this._visibleLines.onZonesChanged(e); + } + public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { + return this._onOptionsMaybeChanged(); + } + + // ---- end view event handlers + + // ----------- HELPERS FOR OTHERS + + public getPositionFromDOMInfo(spanNode: HTMLElement, offset: number): Position { + let viewLineDomNode = this._getViewLineDomNode(spanNode); + if (viewLineDomNode === null) { + // Couldn't find view line node + return null; + } + let lineNumber = this._getLineNumberFor(viewLineDomNode); + + if (lineNumber === -1) { + // Couldn't find view line node + return null; + } + + if (lineNumber < 1 || lineNumber > this._context.model.getLineCount()) { + // lineNumber is outside range + return null; + } + + if (this._context.model.getLineMaxColumn(lineNumber) === 1) { + // Line is empty + return new Position(lineNumber, 1); + } + + let rendStartLineNumber = this._visibleLines.getStartLineNumber(); + let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + if (lineNumber < rendStartLineNumber || lineNumber > rendEndLineNumber) { + // Couldn't find line + return null; + } + + let column = this._visibleLines.getVisibleLine(lineNumber).getColumnOfNodeOffset(lineNumber, spanNode, offset); + let minColumn = this._context.model.getLineMinColumn(lineNumber); + if (column < minColumn) { + column = minColumn; + } + return new Position(lineNumber, column); + } + + private _getViewLineDomNode(node: HTMLElement): HTMLElement { + while (node && node.nodeType === 1) { + if (node.className === ViewLine.CLASS_NAME) { + return node; + } + node = node.parentElement; + } + return null; + } + + /** + * @returns the line number of this view line dom node. + */ + private _getLineNumberFor(domNode: HTMLElement): number { + let startLineNumber = this._visibleLines.getStartLineNumber(); + let endLineNumber = this._visibleLines.getEndLineNumber(); + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + let line = this._visibleLines.getVisibleLine(lineNumber); + if (domNode === line.getDomNode()) { + return lineNumber; + } + } + return -1; + } + + public getLineWidth(lineNumber: number): number { + let rendStartLineNumber = this._visibleLines.getStartLineNumber(); + let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + if (lineNumber < rendStartLineNumber || lineNumber > rendEndLineNumber) { + // Couldn't find line + return -1; + } + + return this._visibleLines.getVisibleLine(lineNumber).getWidth(); + } + + public linesVisibleRangesForRange(range: Range, includeNewLines: boolean): LineVisibleRanges[] { + if (this.shouldRender()) { + // Cannot read from the DOM because it is dirty + // i.e. the model & the dom are out of sync, so I'd be reading something stale + return null; + } + + let originalEndLineNumber = range.endLineNumber; + range = Range.intersectRanges(range, this._lastRenderedData.getCurrentVisibleRange()); + if (!range) { + return null; + } + + let visibleRanges: LineVisibleRanges[] = [], visibleRangesLen = 0; + let domReadingContext = new DomReadingContext(this.domNode.domNode, this._textRangeRestingSpot); + + let nextLineModelLineNumber: number; + if (includeNewLines) { + nextLineModelLineNumber = this._context.model.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber; + } + + let rendStartLineNumber = this._visibleLines.getStartLineNumber(); + let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + for (let lineNumber = range.startLineNumber; lineNumber <= range.endLineNumber; lineNumber++) { + + if (lineNumber < rendStartLineNumber || lineNumber > rendEndLineNumber) { + continue; + } + + let startColumn = lineNumber === range.startLineNumber ? range.startColumn : 1; + let endColumn = lineNumber === range.endLineNumber ? range.endColumn : this._context.model.getLineMaxColumn(lineNumber); + let visibleRangesForLine = this._visibleLines.getVisibleLine(lineNumber).getVisibleRangesForRange(startColumn, endColumn, domReadingContext); + + if (!visibleRangesForLine || visibleRangesForLine.length === 0) { + continue; + } + + if (includeNewLines && lineNumber < originalEndLineNumber) { + let currentLineModelLineNumber = nextLineModelLineNumber; + nextLineModelLineNumber = this._context.model.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber + 1, 1)).lineNumber; + + if (currentLineModelLineNumber !== nextLineModelLineNumber) { + visibleRangesForLine[visibleRangesForLine.length - 1].width += this._typicalHalfwidthCharacterWidth; + } + } + + visibleRanges[visibleRangesLen++] = new LineVisibleRanges(lineNumber, visibleRangesForLine); + } + + if (visibleRangesLen === 0) { + return null; + } + + return visibleRanges; + } + + public visibleRangesForRange2(range: Range): HorizontalRange[] { + + if (this.shouldRender()) { + // Cannot read from the DOM because it is dirty + // i.e. the model & the dom are out of sync, so I'd be reading something stale + return null; + } + + range = Range.intersectRanges(range, this._lastRenderedData.getCurrentVisibleRange()); + if (!range) { + return null; + } + + let result: HorizontalRange[] = []; + let domReadingContext = new DomReadingContext(this.domNode.domNode, this._textRangeRestingSpot); + + let rendStartLineNumber = this._visibleLines.getStartLineNumber(); + let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + for (let lineNumber = range.startLineNumber; lineNumber <= range.endLineNumber; lineNumber++) { + + if (lineNumber < rendStartLineNumber || lineNumber > rendEndLineNumber) { + continue; + } + + let startColumn = lineNumber === range.startLineNumber ? range.startColumn : 1; + let endColumn = lineNumber === range.endLineNumber ? range.endColumn : this._context.model.getLineMaxColumn(lineNumber); + let visibleRangesForLine = this._visibleLines.getVisibleLine(lineNumber).getVisibleRangesForRange(startColumn, endColumn, domReadingContext); + + if (!visibleRangesForLine || visibleRangesForLine.length === 0) { + continue; + } + + result = result.concat(visibleRangesForLine); + } + + if (result.length === 0) { + return null; + } + + return result; + } + + // --- implementation + + /** + * Updates the max line width if it is fast to compute. + * Returns true if all lines were taken into account. + * Returns false if some lines need to be reevaluated (in a slow fashion). + */ + private _updateLineWidthsFast(): boolean { + return this._updateLineWidths(true); + } + + private _updateLineWidthsSlow(): void { + this._updateLineWidths(false); + } + + private _updateLineWidths(fast: boolean): boolean { + const rendStartLineNumber = this._visibleLines.getStartLineNumber(); + const rendEndLineNumber = this._visibleLines.getEndLineNumber(); + + let localMaxLineWidth = 1; + let allWidthsComputed = true; + for (let lineNumber = rendStartLineNumber; lineNumber <= rendEndLineNumber; lineNumber++) { + const visibleLine = this._visibleLines.getVisibleLine(lineNumber); + + if (fast && !visibleLine.getWidthIsFast()) { + // Cannot compute width in a fast way for this line + allWidthsComputed = false; + continue; + } + + localMaxLineWidth = Math.max(localMaxLineWidth, visibleLine.getWidth()); + } + + if (allWidthsComputed && rendStartLineNumber === 1 && rendEndLineNumber === this._context.model.getLineCount()) { + // we know the max line width for all the lines + this._maxLineWidth = 0; + } + + this._ensureMaxLineWidth(localMaxLineWidth); + + return allWidthsComputed; + } + + public prepareRender(): void { + throw new Error('Not supported'); + } + + public render(): void { + throw new Error('Not supported'); + } + + public renderText(viewportData: ViewportData): void { + // (1) render lines - ensures lines are in the DOM + this._visibleLines.renderLines(viewportData); + this._lastRenderedData.setCurrentVisibleRange(viewportData.visibleRange); + this.domNode.setWidth(this._context.viewLayout.getScrollWidth()); + this.domNode.setHeight(Math.min(this._context.viewLayout.getScrollHeight(), 1000000)); + + // (2) compute horizontal scroll position: + // - this must happen after the lines are in the DOM since it might need a line that rendered just now + // - it might change `scrollWidth` and `scrollLeft` + if (this._horizontalRevealRequest) { + + const revealLineNumber = this._horizontalRevealRequest.lineNumber; + const revealStartColumn = this._horizontalRevealRequest.startColumn; + const revealEndColumn = this._horizontalRevealRequest.endColumn; + const scrollType = this._horizontalRevealRequest.scrollType; + + // Check that we have the line that contains the horizontal range in the viewport + if (viewportData.startLineNumber <= revealLineNumber && revealLineNumber <= viewportData.endLineNumber) { + + this._horizontalRevealRequest = null; + + // allow `visibleRangesForRange2` to work + this.onDidRender(); + + // compute new scroll position + let newScrollLeft = this._computeScrollLeftToRevealRange(revealLineNumber, revealStartColumn, revealEndColumn); + + let isViewportWrapping = this._isViewportWrapping; + if (!isViewportWrapping) { + // ensure `scrollWidth` is large enough + this._ensureMaxLineWidth(newScrollLeft.maxHorizontalOffset); + } + + // set `scrollLeft` + if (scrollType === ScrollType.Smooth) { + this._context.viewLayout.setScrollPositionSmooth({ + scrollLeft: newScrollLeft.scrollLeft + }); + } else { + this._context.viewLayout.setScrollPositionNow({ + scrollLeft: newScrollLeft.scrollLeft + }); + } + } + } + + // (3) handle scrolling + this._linesContent.setLayerHinting(this._canUseLayerHinting); + const adjustedScrollTop = this._context.viewLayout.getCurrentScrollTop() - viewportData.bigNumbersDelta; + this._linesContent.setTop(-adjustedScrollTop); + this._linesContent.setLeft(-this._context.viewLayout.getCurrentScrollLeft()); + + // Update max line width (not so important, it is just so the horizontal scrollbar doesn't get too small) + if (!this._updateLineWidthsFast()) { + // Computing the width of some lines would be slow => delay it + this._asyncUpdateLineWidths.schedule(); + } + } + + // --- width + + private _ensureMaxLineWidth(lineWidth: number): void { + let iLineWidth = Math.ceil(lineWidth); + if (this._maxLineWidth < iLineWidth) { + this._maxLineWidth = iLineWidth; + this._context.viewLayout.onMaxLineWidthChanged(this._maxLineWidth); + } + } + + private _computeScrollTopToRevealRange(viewport: Viewport, range: Range, verticalType: viewEvents.VerticalRevealType): number { + let viewportStartY = viewport.top; + let viewportHeight = viewport.height; + let viewportEndY = viewportStartY + viewportHeight; + let boxStartY: number; + let boxEndY: number; + + // Have a box that includes one extra line height (for the horizontal scrollbar) + boxStartY = this._context.viewLayout.getVerticalOffsetForLineNumber(range.startLineNumber); + boxEndY = this._context.viewLayout.getVerticalOffsetForLineNumber(range.endLineNumber) + this._lineHeight; + if (verticalType === viewEvents.VerticalRevealType.Simple || verticalType === viewEvents.VerticalRevealType.Bottom) { + // Reveal one line more when the last line would be covered by the scrollbar - arrow down case or revealing a line explicitly at bottom + boxEndY += this._lineHeight; + } + + let newScrollTop: number; + + if (verticalType === viewEvents.VerticalRevealType.Center || verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport) { + if (verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport && viewportStartY <= boxStartY && boxEndY <= viewportEndY) { + // Box is already in the viewport... do nothing + newScrollTop = viewportStartY; + } else { + // Box is outside the viewport... center it + let boxMiddleY = (boxStartY + boxEndY) / 2; + newScrollTop = Math.max(0, boxMiddleY - viewportHeight / 2); + } + } else { + newScrollTop = this._computeMinimumScrolling(viewportStartY, viewportEndY, boxStartY, boxEndY, verticalType === viewEvents.VerticalRevealType.Top, verticalType === viewEvents.VerticalRevealType.Bottom); + } + + return newScrollTop; + } + + private _computeScrollLeftToRevealRange(lineNumber: number, startColumn: number, endColumn: number): { scrollLeft: number; maxHorizontalOffset: number; } { + + let maxHorizontalOffset = 0; + + let viewport = this._context.viewLayout.getCurrentViewport(); + let viewportStartX = viewport.left; + let viewportEndX = viewportStartX + viewport.width; + + let visibleRanges = this.visibleRangesForRange2(new Range(lineNumber, startColumn, lineNumber, endColumn)); + let boxStartX = Number.MAX_VALUE; + let boxEndX = 0; + + if (!visibleRanges) { + // Unknown + return { + scrollLeft: viewportStartX, + maxHorizontalOffset: maxHorizontalOffset + }; + } + + for (let i = 0; i < visibleRanges.length; i++) { + let visibleRange = visibleRanges[i]; + if (visibleRange.left < boxStartX) { + boxStartX = visibleRange.left; + } + if (visibleRange.left + visibleRange.width > boxEndX) { + boxEndX = visibleRange.left + visibleRange.width; + } + } + + maxHorizontalOffset = boxEndX; + + boxStartX = Math.max(0, boxStartX - ViewLines.HORIZONTAL_EXTRA_PX); + boxEndX += this._revealHorizontalRightPadding; + + let newScrollLeft = this._computeMinimumScrolling(viewportStartX, viewportEndX, boxStartX, boxEndX); + return { + scrollLeft: newScrollLeft, + maxHorizontalOffset: maxHorizontalOffset + }; + } + + private _computeMinimumScrolling(viewportStart: number, viewportEnd: number, boxStart: number, boxEnd: number, revealAtStart?: boolean, revealAtEnd?: boolean): number { + viewportStart = viewportStart | 0; + viewportEnd = viewportEnd | 0; + boxStart = boxStart | 0; + boxEnd = boxEnd | 0; + revealAtStart = !!revealAtStart; + revealAtEnd = !!revealAtEnd; + + let viewportLength = viewportEnd - viewportStart; + let boxLength = boxEnd - boxStart; + + if (boxLength < viewportLength) { + // The box would fit in the viewport + + if (revealAtStart) { + return boxStart; + } + + if (revealAtEnd) { + return Math.max(0, boxEnd - viewportLength); + } + + if (boxStart < viewportStart) { + // The box is above the viewport + return boxStart; + } else if (boxEnd > viewportEnd) { + // The box is below the viewport + return Math.max(0, boxEnd - viewportLength); + } + } else { + // The box would not fit in the viewport + // Reveal the beginning of the box + return boxStart; + } + + return viewportStart; + } +} diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.css b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.css new file mode 100644 index 0000000000..ebd7e29087 --- /dev/null +++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.css @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +.monaco-editor .lines-decorations { + position: absolute; + top: 0; + background: white; +} + +/* + Keeping name short for faster parsing. + cldr = core lines decorations rendering (div) +*/ +.monaco-editor .margin-view-overlays .cldr { + position: absolute; + height: 100%; +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts new file mode 100644 index 0000000000..5a52429fa7 --- /dev/null +++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./linesDecorations'; +import { DecorationToRender, DedupOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; + +export class LinesDecorationsOverlay extends DedupOverlay { + + private _context: ViewContext; + + private _decorationsLeft: number; + private _decorationsWidth: number; + private _renderResult: string[]; + + constructor(context: ViewContext) { + super(); + this._context = context; + this._decorationsLeft = this._context.configuration.editor.layoutInfo.decorationsLeft; + this._decorationsWidth = this._context.configuration.editor.layoutInfo.decorationsWidth; + this._renderResult = null; + this._context.addEventHandler(this); + } + + public dispose(): void { + this._context.removeEventHandler(this); + this._context = null; + this._renderResult = null; + super.dispose(); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.layoutInfo) { + this._decorationsLeft = this._context.configuration.editor.layoutInfo.decorationsLeft; + this._decorationsWidth = this._context.configuration.editor.layoutInfo.decorationsWidth; + } + return true; + } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + return true; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return true; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollTopChanged; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + // --- end event handlers + + protected _getDecorations(ctx: RenderingContext): DecorationToRender[] { + let decorations = ctx.getDecorationsInViewport(); + let r: DecorationToRender[] = [], rLen = 0; + for (let i = 0, len = decorations.length; i < len; i++) { + let d = decorations[i]; + let linesDecorationsClassName = d.source.options.linesDecorationsClassName; + if (linesDecorationsClassName) { + r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, linesDecorationsClassName); + } + } + return r; + } + + public prepareRender(ctx: RenderingContext): void { + let visibleStartLineNumber = ctx.visibleRange.startLineNumber; + let visibleEndLineNumber = ctx.visibleRange.endLineNumber; + let toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx)); + + let left = this._decorationsLeft.toString(); + let width = this._decorationsWidth.toString(); + let common = '" style="left:' + left + 'px;width:' + width + 'px;">'; + + let output: string[] = []; + for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { + let lineIndex = lineNumber - visibleStartLineNumber; + let classNames = toRender[lineIndex]; + let lineOutput = ''; + for (let i = 0, len = classNames.length; i < len; i++) { + lineOutput += ''; + } + output[lineIndex] = lineOutput; + } + + this._renderResult = output; + } + + public render(startLineNumber: number, lineNumber: number): string { + if (!this._renderResult) { + return ''; + } + return this._renderResult[lineNumber - startLineNumber]; + } +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.css b/src/vs/editor/browser/viewParts/minimap/minimap.css new file mode 100644 index 0000000000..614543e790 --- /dev/null +++ b/src/vs/editor/browser/viewParts/minimap/minimap.css @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* START cover the case that slider is visible on mouseover */ +.monaco-editor .minimap.slider-mouseover .minimap-slider { + opacity: 0; + transition: opacity 100ms linear; +} +.monaco-editor .minimap.slider-mouseover:hover .minimap-slider { + opacity: 1; +} +.monaco-editor .minimap.slider-mouseover .minimap-slider.active { + opacity: 1; +} +/* END cover the case that slider is visible on mouseover */ + +.monaco-editor .minimap-shadow-hidden { + position: absolute; + width: 0; +} +.monaco-editor .minimap-shadow-visible { + position: absolute; + left: -6px; + width: 6px; +} diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts new file mode 100644 index 0000000000..a350797018 --- /dev/null +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -0,0 +1,911 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./minimap'; +import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; +import * as dom from 'vs/base/browser/dom'; +import { MinimapCharRenderer, MinimapTokensColorTracker, Constants } from 'vs/editor/common/view/minimapCharRenderer'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { CharCode } from 'vs/base/common/charCode'; +import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; +import { ColorId } from 'vs/editor/common/modes'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { RenderedLinesCollection, ILine } from 'vs/editor/browser/view/viewLayer'; +import { Range } from 'vs/editor/common/core/range'; +import { RGBA8 } from 'vs/editor/common/core/rgba'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; +import * as platform from 'vs/base/common/platform'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground, scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; + +const enum RenderMinimap { + None = 0, + Small = 1, + Large = 2, + SmallBlocks = 3, + LargeBlocks = 4, +} + +function getMinimapLineHeight(renderMinimap: RenderMinimap): number { + if (renderMinimap === RenderMinimap.Large) { + return Constants.x2_CHAR_HEIGHT; + } + if (renderMinimap === RenderMinimap.LargeBlocks) { + return Constants.x2_CHAR_HEIGHT + 2; + } + if (renderMinimap === RenderMinimap.Small) { + return Constants.x1_CHAR_HEIGHT; + } + // RenderMinimap.SmallBlocks + return Constants.x1_CHAR_HEIGHT + 1; +} + +function getMinimapCharWidth(renderMinimap: RenderMinimap): number { + if (renderMinimap === RenderMinimap.Large) { + return Constants.x2_CHAR_WIDTH; + } + if (renderMinimap === RenderMinimap.LargeBlocks) { + return Constants.x2_CHAR_WIDTH; + } + if (renderMinimap === RenderMinimap.Small) { + return Constants.x1_CHAR_WIDTH; + } + // RenderMinimap.SmallBlocks + return Constants.x1_CHAR_WIDTH; +} + +/** + * The orthogonal distance to the slider at which dragging "resets". This implements "snapping" + */ +const MOUSE_DRAG_RESET_DISTANCE = 140; + +class MinimapOptions { + + public readonly renderMinimap: RenderMinimap; + + public readonly scrollBeyondLastLine: boolean; + + public readonly showSlider: 'always' | 'mouseover'; + + public readonly pixelRatio: number; + + public readonly typicalHalfwidthCharacterWidth: number; + + public readonly lineHeight: number; + + /** + * container dom node width (in CSS px) + */ + public readonly minimapWidth: number; + /** + * container dom node height (in CSS px) + */ + public readonly minimapHeight: number; + + /** + * canvas backing store width (in device px) + */ + public readonly canvasInnerWidth: number; + /** + * canvas backing store height (in device px) + */ + public readonly canvasInnerHeight: number; + + /** + * canvas width (in CSS px) + */ + public readonly canvasOuterWidth: number; + /** + * canvas height (in CSS px) + */ + public readonly canvasOuterHeight: number; + + constructor(configuration: editorCommon.IConfiguration) { + const pixelRatio = configuration.editor.pixelRatio; + const layoutInfo = configuration.editor.layoutInfo; + const viewInfo = configuration.editor.viewInfo; + const fontInfo = configuration.editor.fontInfo; + + this.renderMinimap = layoutInfo.renderMinimap | 0; + this.scrollBeyondLastLine = viewInfo.scrollBeyondLastLine; + this.showSlider = viewInfo.minimap.showSlider; + this.pixelRatio = pixelRatio; + this.typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; + this.lineHeight = configuration.editor.lineHeight; + this.minimapWidth = layoutInfo.minimapWidth; + this.minimapHeight = layoutInfo.height; + + this.canvasInnerWidth = Math.max(1, Math.floor(pixelRatio * this.minimapWidth)); + this.canvasInnerHeight = Math.max(1, Math.floor(pixelRatio * this.minimapHeight)); + + this.canvasOuterWidth = this.canvasInnerWidth / pixelRatio; + this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio; + } + + public equals(other: MinimapOptions): boolean { + return (this.renderMinimap === other.renderMinimap + && this.scrollBeyondLastLine === other.scrollBeyondLastLine + && this.showSlider === other.showSlider + && this.pixelRatio === other.pixelRatio + && this.typicalHalfwidthCharacterWidth === other.typicalHalfwidthCharacterWidth + && this.lineHeight === other.lineHeight + && this.minimapWidth === other.minimapWidth + && this.minimapHeight === other.minimapHeight + && this.canvasInnerWidth === other.canvasInnerWidth + && this.canvasInnerHeight === other.canvasInnerHeight + && this.canvasOuterWidth === other.canvasOuterWidth + && this.canvasOuterHeight === other.canvasOuterHeight + ); + } +} + +class MinimapLayout { + + /** + * The given editor scrollTop (input). + */ + public readonly scrollTop: number; + + /** + * The given editor scrollHeight (input). + */ + public readonly scrollHeight: number; + + private readonly _computedSliderRatio: number; + + /** + * slider dom node top (in CSS px) + */ + public readonly sliderTop: number; + /** + * slider dom node height (in CSS px) + */ + public readonly sliderHeight: number; + + /** + * minimap render start line number. + */ + public readonly startLineNumber: number; + /** + * minimap render end line number. + */ + public readonly endLineNumber: number; + + constructor( + scrollTop: number, + scrollHeight: number, + computedSliderRatio: number, + sliderTop: number, + sliderHeight: number, + startLineNumber: number, + endLineNumber: number + ) { + this.scrollTop = scrollTop; + this.scrollHeight = scrollHeight; + this._computedSliderRatio = computedSliderRatio; + this.sliderTop = sliderTop; + this.sliderHeight = sliderHeight; + this.startLineNumber = startLineNumber; + this.endLineNumber = endLineNumber; + } + + /** + * Compute a desired `scrollPosition` such that the slider moves by `delta`. + */ + public getDesiredScrollTopFromDelta(delta: number): number { + let desiredSliderPosition = this.sliderTop + delta; + return Math.round(desiredSliderPosition / this._computedSliderRatio); + } + + public static create( + options: MinimapOptions, + viewportStartLineNumber: number, + viewportEndLineNumber: number, + viewportHeight: number, + viewportContainsWhitespaceGaps: boolean, + lineCount: number, + scrollTop: number, + scrollHeight: number, + previousLayout: MinimapLayout + ): MinimapLayout { + const pixelRatio = options.pixelRatio; + const minimapLineHeight = getMinimapLineHeight(options.renderMinimap); + const minimapLinesFitting = Math.floor(options.canvasInnerHeight / minimapLineHeight); + const lineHeight = options.lineHeight; + + // The visible line count in a viewport can change due to a number of reasons: + // a) with the same viewport width, different scroll positions can result in partial lines being visible: + // e.g. for a line height of 20, and a viewport height of 600 + // * scrollTop = 0 => visible lines are [1, 30] + // * scrollTop = 10 => visible lines are [1, 31] (with lines 1 and 31 partially visible) + // * scrollTop = 20 => visible lines are [2, 31] + // b) whitespace gaps might make their way in the viewport (which results in a decrease in the visible line count) + // c) we could be in the scroll beyond last line case (which also results in a decrease in the visible line count, down to possibly only one line being visible) + + // We must first establish a desirable slider height. + let sliderHeight: number; + if (viewportContainsWhitespaceGaps && viewportEndLineNumber !== lineCount) { + // case b) from above: there are whitespace gaps in the viewport. + // In this case, the height of the slider directly reflects the visible line count. + const viewportLineCount = viewportEndLineNumber - viewportStartLineNumber + 1; + sliderHeight = Math.floor(viewportLineCount * minimapLineHeight / pixelRatio); + } else { + // The slider has a stable height + const expectedViewportLineCount = viewportHeight / lineHeight; + sliderHeight = Math.floor(expectedViewportLineCount * minimapLineHeight / pixelRatio); + } + + let maxMinimapSliderTop: number; + if (options.scrollBeyondLastLine) { + // The minimap slider, when dragged all the way down, will contain the last line at its top + maxMinimapSliderTop = (lineCount - 1) * minimapLineHeight / pixelRatio; + } else { + // The minimap slider, when dragged all the way down, will contain the last line at its bottom + maxMinimapSliderTop = Math.max(0, lineCount * minimapLineHeight / pixelRatio - sliderHeight); + } + maxMinimapSliderTop = Math.min(options.minimapHeight - sliderHeight, maxMinimapSliderTop); + + // The slider can move from 0 to `maxMinimapSliderTop` + // in the same way `scrollTop` can move from 0 to `scrollHeight` - `viewportHeight`. + const computedSliderRatio = (maxMinimapSliderTop) / (scrollHeight - viewportHeight); + const sliderTop = (scrollTop * computedSliderRatio); + + if (minimapLinesFitting >= lineCount) { + // All lines fit in the minimap + const startLineNumber = 1; + const endLineNumber = lineCount; + + return new MinimapLayout(scrollTop, scrollHeight, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); + } else { + let startLineNumber = Math.max(1, Math.floor(viewportStartLineNumber - sliderTop * pixelRatio / minimapLineHeight)); + + // Avoid flickering caused by a partial viewport start line + // by being consistent w.r.t. the previous layout decision + if (previousLayout && previousLayout.scrollHeight === scrollHeight) { + if (previousLayout.scrollTop > scrollTop) { + // Scrolling up => never increase `startLineNumber` + startLineNumber = Math.min(startLineNumber, previousLayout.startLineNumber); + } + if (previousLayout.scrollTop < scrollTop) { + // Scrolling down => never decrease `startLineNumber` + startLineNumber = Math.max(startLineNumber, previousLayout.startLineNumber); + } + } + + const endLineNumber = Math.min(lineCount, startLineNumber + minimapLinesFitting - 1); + + return new MinimapLayout(scrollTop, scrollHeight, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); + } + } +} + +class MinimapLine implements ILine { + + public static INVALID = new MinimapLine(-1); + + dy: number; + + constructor(dy: number) { + this.dy = dy; + } + + public onContentChanged(): void { + this.dy = -1; + } + + public onTokensChanged(): void { + this.dy = -1; + } +} + +class RenderData { + /** + * last rendered layout. + */ + public readonly renderedLayout: MinimapLayout; + private readonly _imageData: ImageData; + private readonly _renderedLines: RenderedLinesCollection; + + constructor( + renderedLayout: MinimapLayout, + imageData: ImageData, + lines: MinimapLine[] + ) { + this.renderedLayout = renderedLayout; + this._imageData = imageData; + this._renderedLines = new RenderedLinesCollection( + () => MinimapLine.INVALID + ); + this._renderedLines._set(renderedLayout.startLineNumber, lines); + } + + /** + * Check if the current RenderData matches accurately the new desired layout and no painting is needed. + */ + public linesEquals(layout: MinimapLayout): boolean { + if (this.renderedLayout.startLineNumber !== layout.startLineNumber) { + return false; + } + if (this.renderedLayout.endLineNumber !== layout.endLineNumber) { + return false; + } + + const tmp = this._renderedLines._get(); + const lines = tmp.lines; + for (let i = 0, len = lines.length; i < len; i++) { + if (lines[i].dy === -1) { + // This line is invalid + return false; + } + } + + return true; + } + + _get(): { imageData: ImageData; rendLineNumberStart: number; lines: MinimapLine[]; } { + let tmp = this._renderedLines._get(); + return { + imageData: this._imageData, + rendLineNumberStart: tmp.rendLineNumberStart, + lines: tmp.lines + }; + } + + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return this._renderedLines.onLinesChanged(e.fromLineNumber, e.toLineNumber); + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): void { + this._renderedLines.onLinesDeleted(e.fromLineNumber, e.toLineNumber); + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): void { + this._renderedLines.onLinesInserted(e.fromLineNumber, e.toLineNumber); + } + public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + return this._renderedLines.onTokensChanged(e.ranges); + } +} + +/** + * Some sort of double buffering. + * + * Keeps two buffers around that will be rotated for painting. + * Always gives a buffer that is filled with the background color. + */ +class MinimapBuffers { + + private readonly _backgroundFillData: Uint8ClampedArray; + private readonly _buffers: [ImageData, ImageData]; + private _lastUsedBuffer: number; + + constructor(ctx: CanvasRenderingContext2D, WIDTH: number, HEIGHT: number, background: RGBA8) { + this._backgroundFillData = MinimapBuffers._createBackgroundFillData(WIDTH, HEIGHT, background); + this._buffers = [ + ctx.createImageData(WIDTH, HEIGHT), + ctx.createImageData(WIDTH, HEIGHT) + ]; + this._lastUsedBuffer = 0; + } + + public getBuffer(): ImageData { + // rotate buffers + this._lastUsedBuffer = 1 - this._lastUsedBuffer; + let result = this._buffers[this._lastUsedBuffer]; + + // fill with background color + result.data.set(this._backgroundFillData); + + return result; + } + + private static _createBackgroundFillData(WIDTH: number, HEIGHT: number, background: RGBA8): Uint8ClampedArray { + const backgroundR = background.r; + const backgroundG = background.g; + const backgroundB = background.b; + + let result = new Uint8ClampedArray(WIDTH * HEIGHT * 4); + let offset = 0; + for (let i = 0; i < HEIGHT; i++) { + for (let j = 0; j < WIDTH; j++) { + result[offset] = backgroundR; + result[offset + 1] = backgroundG; + result[offset + 2] = backgroundB; + result[offset + 3] = 255; + offset += 4; + } + } + + return result; + } +} + +export class Minimap extends ViewPart { + + private readonly _domNode: FastDomNode; + private readonly _shadow: FastDomNode; + private readonly _canvas: FastDomNode; + private readonly _slider: FastDomNode; + private readonly _sliderHorizontal: FastDomNode; + private readonly _tokensColorTracker: MinimapTokensColorTracker; + private readonly _mouseDownListener: IDisposable; + private readonly _sliderMouseMoveMonitor: GlobalMouseMoveMonitor; + private readonly _sliderMouseDownListener: IDisposable; + + private readonly _minimapCharRenderer: MinimapCharRenderer; + + private _options: MinimapOptions; + private _lastRenderData: RenderData; + private _buffers: MinimapBuffers; + + constructor(context: ViewContext) { + super(context); + + this._options = new MinimapOptions(this._context.configuration); + this._lastRenderData = null; + this._buffers = null; + + this._domNode = createFastDomNode(document.createElement('div')); + PartFingerprints.write(this._domNode, PartFingerprint.Minimap); + this._domNode.setClassName(this._getMinimapDomNodeClassName()); + this._domNode.setPosition('absolute'); + this._domNode.setAttribute('role', 'presentation'); + this._domNode.setAttribute('aria-hidden', 'true'); + this._domNode.setRight(this._context.configuration.editor.layoutInfo.verticalScrollbarWidth); + + this._shadow = createFastDomNode(document.createElement('div')); + this._shadow.setClassName('minimap-shadow-hidden'); + this._domNode.appendChild(this._shadow); + + this._canvas = createFastDomNode(document.createElement('canvas')); + this._canvas.setPosition('absolute'); + this._canvas.setLeft(0); + this._domNode.appendChild(this._canvas); + + this._slider = createFastDomNode(document.createElement('div')); + this._slider.setPosition('absolute'); + this._slider.setClassName('minimap-slider'); + this._slider.setLayerHinting(true); + this._domNode.appendChild(this._slider); + + this._sliderHorizontal = createFastDomNode(document.createElement('div')); + this._sliderHorizontal.setPosition('absolute'); + this._sliderHorizontal.setClassName('minimap-slider-horizontal'); + this._slider.appendChild(this._sliderHorizontal); + + this._tokensColorTracker = MinimapTokensColorTracker.getInstance(); + + this._minimapCharRenderer = getOrCreateMinimapCharRenderer(); + + this._applyLayout(); + + this._mouseDownListener = dom.addStandardDisposableListener(this._canvas.domNode, 'mousedown', (e) => { + e.preventDefault(); + + const renderMinimap = this._options.renderMinimap; + if (renderMinimap === RenderMinimap.None) { + return; + } + if (!this._lastRenderData) { + return; + } + const minimapLineHeight = getMinimapLineHeight(renderMinimap); + const internalOffsetY = this._options.pixelRatio * e.browserEvent.offsetY; + const lineIndex = Math.floor(internalOffsetY / minimapLineHeight); + + let lineNumber = lineIndex + this._lastRenderData.renderedLayout.startLineNumber; + lineNumber = Math.min(lineNumber, this._context.model.getLineCount()); + + this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( + new Range(lineNumber, 1, lineNumber, 1), + viewEvents.VerticalRevealType.Center, + false, + editorCommon.ScrollType.Smooth + )); + }); + + this._sliderMouseMoveMonitor = new GlobalMouseMoveMonitor(); + + this._sliderMouseDownListener = dom.addStandardDisposableListener(this._slider.domNode, 'mousedown', (e) => { + e.preventDefault(); + if (e.leftButton && this._lastRenderData) { + + const initialMousePosition = e.posy; + const initialMouseOrthogonalPosition = e.posx; + const initialSliderState = this._lastRenderData.renderedLayout; + this._slider.toggleClassName('active', true); + + this._sliderMouseMoveMonitor.startMonitoring( + standardMouseMoveMerger, + (mouseMoveData: IStandardMouseMoveEventData) => { + const mouseOrthogonalDelta = Math.abs(mouseMoveData.posx - initialMouseOrthogonalPosition); + + if (platform.isWindows && mouseOrthogonalDelta > MOUSE_DRAG_RESET_DISTANCE) { + // The mouse has wondered away from the scrollbar => reset dragging + this._context.viewLayout.setScrollPositionNow({ + scrollTop: initialSliderState.scrollTop + }); + return; + } + + const mouseDelta = mouseMoveData.posy - initialMousePosition; + this._context.viewLayout.setScrollPositionNow({ + scrollTop: initialSliderState.getDesiredScrollTopFromDelta(mouseDelta) + }); + }, + () => { + this._slider.toggleClassName('active', false); + } + ); + } + }); + } + + public dispose(): void { + this._mouseDownListener.dispose(); + this._sliderMouseMoveMonitor.dispose(); + this._sliderMouseDownListener.dispose(); + super.dispose(); + } + + private _getMinimapDomNodeClassName(): string { + if (this._options.showSlider === 'always') { + return 'minimap slider-always'; + } + return 'minimap slider-mouseover'; + } + + public getDomNode(): FastDomNode { + return this._domNode; + } + + private _applyLayout(): void { + this._domNode.setWidth(this._options.minimapWidth); + this._domNode.setHeight(this._options.minimapHeight); + this._shadow.setHeight(this._options.minimapHeight); + this._canvas.setWidth(this._options.canvasOuterWidth); + this._canvas.setHeight(this._options.canvasOuterHeight); + this._canvas.domNode.width = this._options.canvasInnerWidth; + this._canvas.domNode.height = this._options.canvasInnerHeight; + this._slider.setWidth(this._options.minimapWidth); + } + + private _getBuffer(): ImageData { + if (!this._buffers) { + this._buffers = new MinimapBuffers( + this._canvas.domNode.getContext('2d'), + this._options.canvasInnerWidth, + this._options.canvasInnerHeight, + this._tokensColorTracker.getColor(ColorId.DefaultBackground) + ); + } + return this._buffers.getBuffer(); + } + + private _onOptionsMaybeChanged(): boolean { + let opts = new MinimapOptions(this._context.configuration); + if (this._options.equals(opts)) { + return false; + } + this._options = opts; + this._lastRenderData = null; + this._buffers = null; + this._applyLayout(); + this._domNode.setClassName(this._getMinimapDomNodeClassName()); + return true; + } + + // ---- begin view event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + return this._onOptionsMaybeChanged(); + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + this._lastRenderData = null; + return true; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + if (this._lastRenderData) { + return this._lastRenderData.onLinesChanged(e); + } + return false; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + if (this._lastRenderData) { + this._lastRenderData.onLinesDeleted(e); + } + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + if (this._lastRenderData) { + this._lastRenderData.onLinesInserted(e); + } + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return true; + } + public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + if (this._lastRenderData) { + return this._lastRenderData.onTokensChanged(e); + } + return false; + } + public onTokensColorsChanged(e: viewEvents.ViewTokensColorsChangedEvent): boolean { + this._lastRenderData = null; + this._buffers = null; + return true; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + this._lastRenderData = null; + return true; + } + + // --- end event handlers + + public prepareRender(ctx: RenderingContext): void { + // Nothing to read + } + + public render(renderingCtx: RestrictedRenderingContext): void { + const renderMinimap = this._options.renderMinimap; + if (renderMinimap === RenderMinimap.None) { + this._shadow.setClassName('minimap-shadow-hidden'); + return; + } + if (renderingCtx.scrollLeft + renderingCtx.viewportWidth >= renderingCtx.scrollWidth) { + this._shadow.setClassName('minimap-shadow-hidden'); + } else { + this._shadow.setClassName('minimap-shadow-visible'); + } + + const layout = MinimapLayout.create( + this._options, + renderingCtx.visibleRange.startLineNumber, + renderingCtx.visibleRange.endLineNumber, + renderingCtx.viewportHeight, + (renderingCtx.viewportData.whitespaceViewportData.length > 0), + this._context.model.getLineCount(), + renderingCtx.scrollTop, + renderingCtx.scrollHeight, + this._lastRenderData ? this._lastRenderData.renderedLayout : null + ); + this._slider.setTop(layout.sliderTop); + this._slider.setHeight(layout.sliderHeight); + + // Compute horizontal slider coordinates + const scrollLeftChars = renderingCtx.scrollLeft / this._options.typicalHalfwidthCharacterWidth; + const horizontalSliderLeft = Math.min(this._options.minimapWidth, Math.round(scrollLeftChars * getMinimapCharWidth(this._options.renderMinimap) / this._options.pixelRatio)); + this._sliderHorizontal.setLeft(horizontalSliderLeft); + this._sliderHorizontal.setWidth(this._options.minimapWidth - horizontalSliderLeft); + this._sliderHorizontal.setTop(0); + this._sliderHorizontal.setHeight(layout.sliderHeight); + + this._lastRenderData = this.renderLines(layout); + } + + private renderLines(layout: MinimapLayout): RenderData { + const renderMinimap = this._options.renderMinimap; + const startLineNumber = layout.startLineNumber; + const endLineNumber = layout.endLineNumber; + const minimapLineHeight = getMinimapLineHeight(renderMinimap); + + // Check if nothing changed w.r.t. lines from last frame + if (this._lastRenderData && this._lastRenderData.linesEquals(layout)) { + const _lastData = this._lastRenderData._get(); + // Nice!! Nothing changed from last frame + return new RenderData(layout, _lastData.imageData, _lastData.lines); + } + + // Oh well!! We need to repaint some lines... + + const imageData = this._getBuffer(); + + // Render untouched lines by using last rendered data. + let needed = Minimap._renderUntouchedLines( + imageData, + startLineNumber, + endLineNumber, + minimapLineHeight, + this._lastRenderData + ); + + // Fetch rendering info from view model for rest of lines that need rendering. + const lineInfo = this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed); + const tabSize = lineInfo.tabSize; + const background = this._tokensColorTracker.getColor(ColorId.DefaultBackground); + const useLighterFont = this._tokensColorTracker.backgroundIsLight(); + + // Render the rest of lines + let dy = 0; + let renderedLines: MinimapLine[] = []; + for (let lineIndex = 0, lineCount = endLineNumber - startLineNumber + 1; lineIndex < lineCount; lineIndex++) { + if (needed[lineIndex]) { + Minimap._renderLine( + imageData, + background, + useLighterFont, + renderMinimap, + this._tokensColorTracker, + this._minimapCharRenderer, + dy, + tabSize, + lineInfo.data[lineIndex] + ); + } + renderedLines[lineIndex] = new MinimapLine(dy); + dy += minimapLineHeight; + } + + // Finally, paint to the canvas + const ctx = this._canvas.domNode.getContext('2d'); + ctx.putImageData(imageData, 0, 0); + + // Save rendered data for reuse on next frame if possible + return new RenderData( + layout, + imageData, + renderedLines + ); + } + + private static _renderUntouchedLines( + target: ImageData, + startLineNumber: number, + endLineNumber: number, + minimapLineHeight: number, + lastRenderData: RenderData, + ): boolean[] { + + let needed: boolean[] = []; + if (!lastRenderData) { + for (let i = 0, len = endLineNumber - startLineNumber + 1; i < len; i++) { + needed[i] = true; + } + return needed; + } + + const _lastData = lastRenderData._get(); + const lastTargetData = _lastData.imageData.data; + const lastStartLineNumber = _lastData.rendLineNumberStart; + const lastLines = _lastData.lines; + const lastLinesLength = lastLines.length; + const WIDTH = target.width; + const targetData = target.data; + + let copySourceStart = -1; + let copySourceEnd = -1; + let copyDestStart = -1; + let copyDestEnd = -1; + + let dest_dy = 0; + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + const lineIndex = lineNumber - startLineNumber; + const lastLineIndex = lineNumber - lastStartLineNumber; + const source_dy = (lastLineIndex >= 0 && lastLineIndex < lastLinesLength ? lastLines[lastLineIndex].dy : -1); + + if (source_dy === -1) { + needed[lineIndex] = true; + dest_dy += minimapLineHeight; + continue; + } + + let sourceStart = source_dy * WIDTH * 4; + let sourceEnd = (source_dy + minimapLineHeight) * WIDTH * 4; + let destStart = dest_dy * WIDTH * 4; + let destEnd = (dest_dy + minimapLineHeight) * WIDTH * 4; + + if (copySourceEnd === sourceStart && copyDestEnd === destStart) { + // contiguous zone => extend copy request + copySourceEnd = sourceEnd; + copyDestEnd = destEnd; + } else { + if (copySourceStart !== -1) { + // flush existing copy request + targetData.set(lastTargetData.subarray(copySourceStart, copySourceEnd), copyDestStart); + } + copySourceStart = sourceStart; + copySourceEnd = sourceEnd; + copyDestStart = destStart; + copyDestEnd = destEnd; + } + + needed[lineIndex] = false; + dest_dy += minimapLineHeight; + } + + if (copySourceStart !== -1) { + // flush existing copy request + targetData.set(lastTargetData.subarray(copySourceStart, copySourceEnd), copyDestStart); + } + + return needed; + } + + private static _renderLine( + target: ImageData, + backgroundColor: RGBA8, + useLighterFont: boolean, + renderMinimap: RenderMinimap, + colorTracker: MinimapTokensColorTracker, + minimapCharRenderer: MinimapCharRenderer, + dy: number, + tabSize: number, + lineData: ViewLineData + ): void { + const content = lineData.content; + const tokens = lineData.tokens; + const charWidth = getMinimapCharWidth(renderMinimap); + const maxDx = target.width - charWidth; + + let dx = 0; + let charIndex = 0; + let tabsCharDelta = 0; + + for (let tokenIndex = 0, tokensLen = tokens.length; tokenIndex < tokensLen; tokenIndex++) { + const token = tokens[tokenIndex]; + const tokenEndIndex = token.endIndex; + const tokenColorId = token.getForeground(); + const tokenColor = colorTracker.getColor(tokenColorId); + + for (; charIndex < tokenEndIndex; charIndex++) { + if (dx > maxDx) { + // hit edge of minimap + return; + } + const charCode = content.charCodeAt(charIndex); + + if (charCode === CharCode.Tab) { + let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; + tabsCharDelta += insertSpacesCount - 1; + // No need to render anything since tab is invisible + dx += insertSpacesCount * charWidth; + } else if (charCode === CharCode.Space) { + // No need to render anything since space is invisible + dx += charWidth; + } else { + if (renderMinimap === RenderMinimap.Large) { + minimapCharRenderer.x2RenderChar(target, dx, dy, charCode, tokenColor, backgroundColor, useLighterFont); + } else if (renderMinimap === RenderMinimap.Small) { + minimapCharRenderer.x1RenderChar(target, dx, dy, charCode, tokenColor, backgroundColor, useLighterFont); + } else if (renderMinimap === RenderMinimap.LargeBlocks) { + minimapCharRenderer.x2BlockRenderChar(target, dx, dy, tokenColor, backgroundColor, useLighterFont); + } else { + // RenderMinimap.SmallBlocks + minimapCharRenderer.x1BlockRenderChar(target, dx, dy, tokenColor, backgroundColor, useLighterFont); + } + dx += charWidth; + } + } + } + } +} + +registerThemingParticipant((theme, collector) => { + const sliderBackground = theme.getColor(scrollbarSliderBackground); + if (sliderBackground) { + const halfSliderBackground = sliderBackground.transparent(0.5); + collector.addRule(`.monaco-editor .minimap-slider, .monaco-editor .minimap-slider .minimap-slider-horizontal { background: ${halfSliderBackground}; }`); + } + const sliderHoverBackground = theme.getColor(scrollbarSliderHoverBackground); + if (sliderHoverBackground) { + const halfSliderHoverBackground = sliderHoverBackground.transparent(0.5); + collector.addRule(`.monaco-editor .minimap-slider:hover, .monaco-editor .minimap-slider:hover .minimap-slider-horizontal { background: ${halfSliderHoverBackground}; }`); + } + const sliderActiveBackground = theme.getColor(scrollbarSliderActiveBackground); + if (sliderActiveBackground) { + const halfSliderActiveBackground = sliderActiveBackground.transparent(0.5); + collector.addRule(`.monaco-editor .minimap-slider.active, .monaco-editor .minimap-slider.active .minimap-slider-horizontal { background: ${halfSliderActiveBackground}; }`); + } + const shadow = theme.getColor(scrollbarShadow); + if (shadow) { + collector.addRule(`.monaco-editor .minimap-shadow-visible { box-shadow: ${shadow} -6px 0 6px -6px inset; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.css b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.css new file mode 100644 index 0000000000..fee16d85b1 --- /dev/null +++ b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.css @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +.monaco-editor .overlayWidgets { + position: absolute; + top: 0; + left:0; +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts new file mode 100644 index 0000000000..443100b09f --- /dev/null +++ b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./overlayWidgets'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { IOverlayWidget, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; + +interface IWidgetData { + widget: IOverlayWidget; + preference: OverlayWidgetPositionPreference; + domNode: FastDomNode; +} + +interface IWidgetMap { + [key: string]: IWidgetData; +} + +export class ViewOverlayWidgets extends ViewPart { + + private _widgets: IWidgetMap; + private _domNode: FastDomNode; + + private _verticalScrollbarWidth: number; + private _minimapWidth: number; + private _horizontalScrollbarHeight: number; + private _editorHeight: number; + private _editorWidth: number; + + constructor(context: ViewContext) { + super(context); + + this._widgets = {}; + this._verticalScrollbarWidth = this._context.configuration.editor.layoutInfo.verticalScrollbarWidth; + this._minimapWidth = this._context.configuration.editor.layoutInfo.minimapWidth; + this._horizontalScrollbarHeight = this._context.configuration.editor.layoutInfo.horizontalScrollbarHeight; + this._editorHeight = this._context.configuration.editor.layoutInfo.height; + this._editorWidth = this._context.configuration.editor.layoutInfo.width; + + this._domNode = createFastDomNode(document.createElement('div')); + PartFingerprints.write(this._domNode, PartFingerprint.OverlayWidgets); + this._domNode.setClassName('overlayWidgets'); + } + + public dispose(): void { + super.dispose(); + this._widgets = null; + } + + public getDomNode(): FastDomNode { + return this._domNode; + } + + // ---- begin view event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.layoutInfo) { + this._verticalScrollbarWidth = this._context.configuration.editor.layoutInfo.verticalScrollbarWidth; + this._minimapWidth = this._context.configuration.editor.layoutInfo.minimapWidth; + this._horizontalScrollbarHeight = this._context.configuration.editor.layoutInfo.horizontalScrollbarHeight; + this._editorHeight = this._context.configuration.editor.layoutInfo.height; + this._editorWidth = this._context.configuration.editor.layoutInfo.width; + return true; + } + return false; + } + + // ---- end view event handlers + + public addWidget(widget: IOverlayWidget): void { + const domNode = createFastDomNode(widget.getDomNode()); + + this._widgets[widget.getId()] = { + widget: widget, + preference: null, + domNode: domNode + }; + + // This is sync because a widget wants to be in the dom + domNode.setPosition('absolute'); + domNode.setAttribute('widgetId', widget.getId()); + this._domNode.appendChild(domNode); + + this.setShouldRender(); + } + + public setWidgetPosition(widget: IOverlayWidget, preference: OverlayWidgetPositionPreference): boolean { + let widgetData = this._widgets[widget.getId()]; + if (widgetData.preference === preference) { + return false; + } + + widgetData.preference = preference; + this.setShouldRender(); + + return true; + } + + public removeWidget(widget: IOverlayWidget): void { + let widgetId = widget.getId(); + if (this._widgets.hasOwnProperty(widgetId)) { + const widgetData = this._widgets[widgetId]; + const domNode = widgetData.domNode.domNode; + delete this._widgets[widgetId]; + + domNode.parentNode.removeChild(domNode); + this.setShouldRender(); + } + } + + private _renderWidget(widgetData: IWidgetData): void { + const domNode = widgetData.domNode; + + if (widgetData.preference === null) { + domNode.unsetTop(); + return; + } + + if (widgetData.preference === OverlayWidgetPositionPreference.TOP_RIGHT_CORNER) { + domNode.setTop(0); + domNode.setRight((2 * this._verticalScrollbarWidth) + this._minimapWidth); + } else if (widgetData.preference === OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER) { + let widgetHeight = domNode.domNode.clientHeight; + domNode.setTop((this._editorHeight - widgetHeight - 2 * this._horizontalScrollbarHeight)); + domNode.setRight((2 * this._verticalScrollbarWidth) + this._minimapWidth); + } else if (widgetData.preference === OverlayWidgetPositionPreference.TOP_CENTER) { + domNode.setTop(0); + domNode.domNode.style.right = '50%'; + } + } + + public prepareRender(ctx: RenderingContext): void { + // Nothing to read + } + + public render(ctx: RestrictedRenderingContext): void { + this._domNode.setWidth(this._editorWidth); + + let keys = Object.keys(this._widgets); + for (let i = 0, len = keys.length; i < len; i++) { + let widgetId = keys[i]; + this._renderWidget(this._widgets[widgetId]); + } + } +} diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts new file mode 100644 index 0000000000..61cdd061e9 --- /dev/null +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -0,0 +1,267 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { OverviewRulerImpl } from 'vs/editor/browser/viewParts/overviewRuler/overviewRulerImpl'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { Position } from 'vs/editor/common/core/position'; +import { TokenizationRegistry } from 'vs/editor/common/modes'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; +import { editorOverviewRulerBorder, editorCursorForeground } from 'vs/editor/common/view/editorColorRegistry'; +import { Color } from 'vs/base/common/color'; +import { ThemeColor } from 'vs/platform/theme/common/themeService'; + +export class DecorationsOverviewRuler extends ViewPart { + + static MIN_DECORATION_HEIGHT = 6; + static MAX_DECORATION_HEIGHT = 60; + + private readonly _tokensColorTrackerListener: IDisposable; + + private _overviewRuler: OverviewRulerImpl; + + private _renderBorder: boolean; + private _borderColor: string; + private _cursorColor: string; + + private _shouldUpdateDecorations: boolean; + private _shouldUpdateCursorPosition: boolean; + + private _hideCursor: boolean; + private _cursorPositions: Position[]; + + private _zonesFromDecorations: OverviewRulerZone[]; + private _zonesFromCursors: OverviewRulerZone[]; + + constructor(context: ViewContext) { + super(context); + this._overviewRuler = new OverviewRulerImpl( + 1, + 'decorationsOverviewRuler', + this._context.viewLayout.getScrollHeight(), + this._context.configuration.editor.lineHeight, + this._context.configuration.editor.pixelRatio, + DecorationsOverviewRuler.MIN_DECORATION_HEIGHT, + DecorationsOverviewRuler.MAX_DECORATION_HEIGHT, + (lineNumber: number) => this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber) + ); + this._overviewRuler.setLanesCount(this._context.configuration.editor.viewInfo.overviewRulerLanes, false); + this._overviewRuler.setLayout(this._context.configuration.editor.layoutInfo.overviewRuler, false); + + this._renderBorder = this._context.configuration.editor.viewInfo.overviewRulerBorder; + + this._updateColors(); + + this._updateBackground(false); + this._tokensColorTrackerListener = TokenizationRegistry.onDidChange((e) => { + if (e.changedColorMap) { + this._updateBackground(true); + } + }); + + this._shouldUpdateDecorations = true; + this._zonesFromDecorations = []; + + this._shouldUpdateCursorPosition = true; + this._hideCursor = this._context.configuration.editor.viewInfo.hideCursorInOverviewRuler; + + this._zonesFromCursors = []; + this._cursorPositions = []; + } + + public dispose(): void { + super.dispose(); + this._overviewRuler.dispose(); + this._tokensColorTrackerListener.dispose(); + } + + private _updateBackground(render: boolean): void { + const minimapEnabled = this._context.configuration.editor.viewInfo.minimap.enabled; + this._overviewRuler.setUseBackground((minimapEnabled ? TokenizationRegistry.getDefaultBackground() : null), render); + } + + // ---- begin view event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + let prevLanesCount = this._overviewRuler.getLanesCount(); + let newLanesCount = this._context.configuration.editor.viewInfo.overviewRulerLanes; + + if (prevLanesCount !== newLanesCount) { + this._overviewRuler.setLanesCount(newLanesCount, false); + } + + if (e.lineHeight) { + this._overviewRuler.setLineHeight(this._context.configuration.editor.lineHeight, false); + } + + if (e.pixelRatio) { + this._overviewRuler.setPixelRatio(this._context.configuration.editor.pixelRatio, false); + } + + if (e.viewInfo) { + this._renderBorder = this._context.configuration.editor.viewInfo.overviewRulerBorder; + this._hideCursor = this._context.configuration.editor.viewInfo.hideCursorInOverviewRuler; + this._shouldUpdateCursorPosition = true; + this._updateBackground(false); + } + + if (e.layoutInfo) { + this._overviewRuler.setLayout(this._context.configuration.editor.layoutInfo.overviewRuler, false); + } + + return true; + } + + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + this._shouldUpdateCursorPosition = true; + this._cursorPositions = []; + for (let i = 0, len = e.selections.length; i < len; i++) { + this._cursorPositions[i] = e.selections[i].getPosition(); + } + return true; + } + + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + this._shouldUpdateDecorations = true; + return true; + } + + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + this._shouldUpdateCursorPosition = true; + this._shouldUpdateDecorations = true; + return true; + } + + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + this._overviewRuler.setScrollHeight(e.scrollHeight, false); + return super.onScrollChanged(e) || e.scrollHeightChanged; + } + + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { + this._updateColors(); + this._shouldUpdateDecorations = true; + this._shouldUpdateCursorPosition = true; + return true; + } + + // ---- end view event handlers + + public getDomNode(): HTMLElement { + return this._overviewRuler.getDomNode(); + } + + private _updateColors() { + let borderColor = this._context.theme.getColor(editorOverviewRulerBorder); + this._borderColor = borderColor ? borderColor.toString() : null; + + let cursorColor = this._context.theme.getColor(editorCursorForeground); + this._cursorColor = cursorColor ? cursorColor.transparent(0.7).toString() : null; + + this._overviewRuler.setThemeType(this._context.theme.type, false); + } + + private _createZonesFromDecorations(): OverviewRulerZone[] { + let decorations = this._context.model.getAllOverviewRulerDecorations(); + let zones: OverviewRulerZone[] = []; + + for (let i = 0, len = decorations.length; i < len; i++) { + let dec = decorations[i]; + let overviewRuler = dec.source.options.overviewRuler; + zones[i] = new OverviewRulerZone( + dec.range.startLineNumber, + dec.range.endLineNumber, + overviewRuler.position, + 0, + this.resolveRulerColor(overviewRuler.color), + this.resolveRulerColor(overviewRuler.darkColor), + this.resolveRulerColor(overviewRuler.hcColor) + ); + } + + return zones; + } + + private resolveRulerColor(color: string | ThemeColor): string { + if (editorCommon.isThemeColor(color)) { + let c = this._context.theme.getColor(color.id) || Color.transparent; + return c.toString(); + } + return color; + } + + private _createZonesFromCursors(): OverviewRulerZone[] { + let zones: OverviewRulerZone[] = []; + + for (let i = 0, len = this._cursorPositions.length; i < len; i++) { + let cursor = this._cursorPositions[i]; + + zones[i] = new OverviewRulerZone( + cursor.lineNumber, + cursor.lineNumber, + editorCommon.OverviewRulerLane.Full, + 2, + this._cursorColor, + this._cursorColor, + this._cursorColor + ); + } + + return zones; + } + + public prepareRender(ctx: RenderingContext): void { + // Nothing to read + } + + public render(ctx: RestrictedRenderingContext): void { + if (this._shouldUpdateDecorations || this._shouldUpdateCursorPosition) { + + if (this._shouldUpdateDecorations) { + this._shouldUpdateDecorations = false; + this._zonesFromDecorations = this._createZonesFromDecorations(); + } + + if (this._shouldUpdateCursorPosition) { + this._shouldUpdateCursorPosition = false; + if (this._hideCursor) { + this._zonesFromCursors = []; + } else { + this._zonesFromCursors = this._createZonesFromCursors(); + } + } + + let allZones: OverviewRulerZone[] = []; + allZones = allZones.concat(this._zonesFromCursors); + allZones = allZones.concat(this._zonesFromDecorations); + + this._overviewRuler.setZones(allZones, false); + } + + let hasRendered = this._overviewRuler.render(false); + + if (hasRendered && this._renderBorder && this._borderColor && this._overviewRuler.getLanesCount() > 0 && (this._zonesFromDecorations.length > 0 || this._zonesFromCursors.length > 0)) { + let ctx2 = this._overviewRuler.getDomNode().getContext('2d'); + ctx2.beginPath(); + ctx2.lineWidth = 1; + ctx2.strokeStyle = this._borderColor; + ctx2.moveTo(0, 0); + ctx2.lineTo(0, this._overviewRuler.getPixelHeight()); + ctx2.stroke(); + + ctx2.moveTo(0, 0); + ctx2.lineTo(this._overviewRuler.getPixelWidth(), 0); + ctx2.stroke(); + } + } +} diff --git a/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts new file mode 100644 index 0000000000..3a4ca1ab57 --- /dev/null +++ b/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { IOverviewRuler } from 'vs/editor/browser/editorBrowser'; +import { OverviewRulerImpl } from 'vs/editor/browser/viewParts/overviewRuler/overviewRulerImpl'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { OverviewRulerPosition } from 'vs/editor/common/config/editorOptions'; +import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; + +export class OverviewRuler extends ViewEventHandler implements IOverviewRuler { + + private _context: ViewContext; + private _overviewRuler: OverviewRulerImpl; + + constructor(context: ViewContext, cssClassName: string, minimumHeight: number, maximumHeight: number) { + super(); + this._context = context; + this._overviewRuler = new OverviewRulerImpl( + 0, + cssClassName, + this._context.viewLayout.getScrollHeight(), + this._context.configuration.editor.lineHeight, + this._context.configuration.editor.pixelRatio, + minimumHeight, + maximumHeight, + (lineNumber: number) => this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber) + ); + + this._context.addEventHandler(this); + } + + public dispose(): void { + this._context.removeEventHandler(this); + this._overviewRuler.dispose(); + super.dispose(); + } + + // ---- begin view event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.lineHeight) { + this._overviewRuler.setLineHeight(this._context.configuration.editor.lineHeight, true); + } + + if (e.pixelRatio) { + this._overviewRuler.setPixelRatio(this._context.configuration.editor.pixelRatio, true); + } + + return true; + } + + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + this._overviewRuler.setScrollHeight(e.scrollHeight, true); + return super.onScrollChanged(e) || e.scrollHeightChanged; + } + + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + // ---- end view event handlers + + public getDomNode(): HTMLElement { + return this._overviewRuler.getDomNode(); + } + + public setLayout(position: OverviewRulerPosition): void { + this._overviewRuler.setLayout(position, true); + } + + public setZones(zones: OverviewRulerZone[]): void { + this._overviewRuler.setZones(zones, true); + } +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/overviewRuler/overviewRulerImpl.ts b/src/vs/editor/browser/viewParts/overviewRuler/overviewRulerImpl.ts new file mode 100644 index 0000000000..77f35bb5c4 --- /dev/null +++ b/src/vs/editor/browser/viewParts/overviewRuler/overviewRulerImpl.ts @@ -0,0 +1,250 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { OverviewRulerLane } from 'vs/editor/common/editorCommon'; +import { OverviewZoneManager, ColorZone, OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; +import { Color } from 'vs/base/common/color'; +import { OverviewRulerPosition } from 'vs/editor/common/config/editorOptions'; +import { ThemeType, LIGHT } from 'vs/platform/theme/common/themeService'; + +export class OverviewRulerImpl { + + private _canvasLeftOffset: number; + private _domNode: FastDomNode; + private _lanesCount: number; + private _zoneManager: OverviewZoneManager; + private _background: Color; + + constructor( + canvasLeftOffset: number, cssClassName: string, scrollHeight: number, lineHeight: number, + pixelRatio: number, minimumHeight: number, maximumHeight: number, + getVerticalOffsetForLine: (lineNumber: number) => number + ) { + this._canvasLeftOffset = canvasLeftOffset; + + this._domNode = createFastDomNode(document.createElement('canvas')); + + this._domNode.setClassName(cssClassName); + this._domNode.setPosition('absolute'); + this._domNode.setLayerHinting(true); + + this._lanesCount = 3; + + this._background = null; + + this._zoneManager = new OverviewZoneManager(getVerticalOffsetForLine); + this._zoneManager.setMinimumHeight(minimumHeight); + this._zoneManager.setMaximumHeight(maximumHeight); + this._zoneManager.setThemeType(LIGHT); + this._zoneManager.setDOMWidth(0); + this._zoneManager.setDOMHeight(0); + this._zoneManager.setOuterHeight(scrollHeight); + this._zoneManager.setLineHeight(lineHeight); + + this._zoneManager.setPixelRatio(pixelRatio); + } + + public dispose(): void { + this._zoneManager = null; + } + + public setLayout(position: OverviewRulerPosition, render: boolean): void { + this._domNode.setTop(position.top); + this._domNode.setRight(position.right); + + let hasChanged = false; + hasChanged = this._zoneManager.setDOMWidth(position.width) || hasChanged; + hasChanged = this._zoneManager.setDOMHeight(position.height) || hasChanged; + + if (hasChanged) { + this._domNode.setWidth(this._zoneManager.getDOMWidth()); + this._domNode.setHeight(this._zoneManager.getDOMHeight()); + this._domNode.domNode.width = this._zoneManager.getCanvasWidth(); + this._domNode.domNode.height = this._zoneManager.getCanvasHeight(); + + if (render) { + this.render(true); + } + } + } + + public getLanesCount(): number { + return this._lanesCount; + } + + public setLanesCount(newLanesCount: number, render: boolean): void { + this._lanesCount = newLanesCount; + + if (render) { + this.render(true); + } + } + + public setThemeType(themeType: ThemeType, render: boolean): void { + this._zoneManager.setThemeType(themeType); + + if (render) { + this.render(true); + } + } + + public setUseBackground(background: Color, render: boolean): void { + this._background = background; + + if (render) { + this.render(true); + } + } + + public getDomNode(): HTMLCanvasElement { + return this._domNode.domNode; + } + + public getPixelWidth(): number { + return this._zoneManager.getCanvasWidth(); + } + + public getPixelHeight(): number { + return this._zoneManager.getCanvasHeight(); + } + + public setScrollHeight(scrollHeight: number, render: boolean): void { + this._zoneManager.setOuterHeight(scrollHeight); + if (render) { + this.render(true); + } + } + + public setLineHeight(lineHeight: number, render: boolean): void { + this._zoneManager.setLineHeight(lineHeight); + if (render) { + this.render(true); + } + } + + public setPixelRatio(pixelRatio: number, render: boolean): void { + this._zoneManager.setPixelRatio(pixelRatio); + this._domNode.setWidth(this._zoneManager.getDOMWidth()); + this._domNode.setHeight(this._zoneManager.getDOMHeight()); + this._domNode.domNode.width = this._zoneManager.getCanvasWidth(); + this._domNode.domNode.height = this._zoneManager.getCanvasHeight(); + if (render) { + this.render(true); + } + } + + public setZones(zones: OverviewRulerZone[], render: boolean): void { + this._zoneManager.setZones(zones); + if (render) { + this.render(false); + } + } + + public render(forceRender: boolean): boolean { + if (this._zoneManager.getOuterHeight() === 0) { + return false; + } + + const width = this._zoneManager.getCanvasWidth(); + const height = this._zoneManager.getCanvasHeight(); + + let colorZones = this._zoneManager.resolveColorZones(); + let id2Color = this._zoneManager.getId2Color(); + + let ctx = this._domNode.domNode.getContext('2d'); + if (this._background === null) { + ctx.clearRect(0, 0, width, height); + } else { + ctx.fillStyle = Color.Format.CSS.formatHex(this._background); + ctx.fillRect(0, 0, width, height); + } + + if (colorZones.length > 0) { + let remainingWidth = width - this._canvasLeftOffset; + + if (this._lanesCount >= 3) { + this._renderThreeLanes(ctx, colorZones, id2Color, remainingWidth); + } else if (this._lanesCount === 2) { + this._renderTwoLanes(ctx, colorZones, id2Color, remainingWidth); + } else if (this._lanesCount === 1) { + this._renderOneLane(ctx, colorZones, id2Color, remainingWidth); + } + } + + return true; + } + + private _renderOneLane(ctx: CanvasRenderingContext2D, colorZones: ColorZone[], id2Color: string[], w: number): void { + + this._renderVerticalPatch(ctx, colorZones, id2Color, OverviewRulerLane.Left | OverviewRulerLane.Center | OverviewRulerLane.Right, this._canvasLeftOffset, w); + + } + + private _renderTwoLanes(ctx: CanvasRenderingContext2D, colorZones: ColorZone[], id2Color: string[], w: number): void { + + let leftWidth = Math.floor(w / 2); + let rightWidth = w - leftWidth; + let leftOffset = this._canvasLeftOffset; + let rightOffset = this._canvasLeftOffset + leftWidth; + + this._renderVerticalPatch(ctx, colorZones, id2Color, OverviewRulerLane.Left | OverviewRulerLane.Center, leftOffset, leftWidth); + this._renderVerticalPatch(ctx, colorZones, id2Color, OverviewRulerLane.Right, rightOffset, rightWidth); + } + + private _renderThreeLanes(ctx: CanvasRenderingContext2D, colorZones: ColorZone[], id2Color: string[], w: number): void { + + let leftWidth = Math.floor(w / 3); + let rightWidth = Math.floor(w / 3); + let centerWidth = w - leftWidth - rightWidth; + let leftOffset = this._canvasLeftOffset; + let centerOffset = this._canvasLeftOffset + leftWidth; + let rightOffset = this._canvasLeftOffset + leftWidth + centerWidth; + + this._renderVerticalPatch(ctx, colorZones, id2Color, OverviewRulerLane.Left, leftOffset, leftWidth); + this._renderVerticalPatch(ctx, colorZones, id2Color, OverviewRulerLane.Center, centerOffset, centerWidth); + this._renderVerticalPatch(ctx, colorZones, id2Color, OverviewRulerLane.Right, rightOffset, rightWidth); + } + + private _renderVerticalPatch(ctx: CanvasRenderingContext2D, colorZones: ColorZone[], id2Color: string[], laneMask: number, xpos: number, width: number): void { + + let currentColorId = 0; + let currentFrom = 0; + let currentTo = 0; + + for (let i = 0, len = colorZones.length; i < len; i++) { + let zone = colorZones[i]; + + if (!(zone.position & laneMask)) { + continue; + } + + let zoneColorId = zone.colorId; + let zoneFrom = zone.from; + let zoneTo = zone.to; + + if (zoneColorId !== currentColorId) { + ctx.fillRect(xpos, currentFrom, width, currentTo - currentFrom); + + currentColorId = zoneColorId; + ctx.fillStyle = id2Color[currentColorId]; + currentFrom = zoneFrom; + currentTo = zoneTo; + } else { + if (currentTo >= zoneFrom) { + currentTo = Math.max(currentTo, zoneTo); + } else { + ctx.fillRect(xpos, currentFrom, width, currentTo - currentFrom); + currentFrom = zoneFrom; + currentTo = zoneTo; + } + } + } + + ctx.fillRect(xpos, currentFrom, width, currentTo - currentFrom); + + } +} diff --git a/src/vs/editor/browser/viewParts/rulers/rulers.css b/src/vs/editor/browser/viewParts/rulers/rulers.css new file mode 100644 index 0000000000..702f59dfa2 --- /dev/null +++ b/src/vs/editor/browser/viewParts/rulers/rulers.css @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .view-ruler { + position: absolute; + top: 0; +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/rulers/rulers.ts b/src/vs/editor/browser/viewParts/rulers/rulers.ts new file mode 100644 index 0000000000..2545b3c2f4 --- /dev/null +++ b/src/vs/editor/browser/viewParts/rulers/rulers.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./rulers'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorRuler } from 'vs/editor/common/view/editorColorRegistry'; +import * as dom from 'vs/base/browser/dom'; + +export class Rulers extends ViewPart { + + public domNode: FastDomNode; + private _renderedRulers: FastDomNode[]; + private _rulers: number[]; + private _height: number; + private _typicalHalfwidthCharacterWidth: number; + + constructor(context: ViewContext) { + super(context); + this.domNode = createFastDomNode(document.createElement('div')); + this.domNode.setAttribute('role', 'presentation'); + this.domNode.setAttribute('aria-hidden', 'true'); + this.domNode.setClassName('view-rulers'); + this._renderedRulers = []; + this._rulers = this._context.configuration.editor.viewInfo.rulers; + this._height = this._context.configuration.editor.layoutInfo.contentHeight; + this._typicalHalfwidthCharacterWidth = this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth; + } + + public dispose(): void { + super.dispose(); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.viewInfo || e.layoutInfo || e.fontInfo) { + this._rulers = this._context.configuration.editor.viewInfo.rulers; + this._height = this._context.configuration.editor.layoutInfo.contentHeight; + this._typicalHalfwidthCharacterWidth = this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth; + return true; + } + return false; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollHeightChanged; + } + + // --- end event handlers + + public prepareRender(ctx: RenderingContext): void { + // Nothing to read + } + + private _ensureRulersCount(): void { + const currentCount = this._renderedRulers.length; + const desiredCount = this._rulers.length; + + if (currentCount === desiredCount) { + // Nothing to do + return; + } + + if (currentCount < desiredCount) { + const rulerWidth = dom.computeScreenAwareSize(1); + let addCount = desiredCount - currentCount; + while (addCount > 0) { + let node = createFastDomNode(document.createElement('div')); + node.setClassName('view-ruler'); + node.setWidth(rulerWidth); + this.domNode.appendChild(node); + this._renderedRulers.push(node); + addCount--; + } + return; + } + + let removeCount = currentCount - desiredCount; + while (removeCount > 0) { + let node = this._renderedRulers.pop(); + this.domNode.removeChild(node); + removeCount--; + } + } + + public render(ctx: RestrictedRenderingContext): void { + + this._ensureRulersCount(); + + for (let i = 0, len = this._rulers.length; i < len; i++) { + let node = this._renderedRulers[i]; + + node.setHeight(Math.min(ctx.scrollHeight, 1000000)); + node.setLeft(this._rulers[i] * this._typicalHalfwidthCharacterWidth); + } + } +} + +registerThemingParticipant((theme, collector) => { + let rulerColor = theme.getColor(editorRuler); + if (rulerColor) { + collector.addRule(`.monaco-editor .view-ruler { background-color: ${rulerColor}; }`); + } +}); diff --git a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.css b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.css new file mode 100644 index 0000000000..26a5797e52 --- /dev/null +++ b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.css @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .scroll-decoration { + position: absolute; + top: 0; + left: 0; + height: 6px; +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts new file mode 100644 index 0000000000..8fe08aaeec --- /dev/null +++ b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./scrollDecoration'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; + +export class ScrollDecorationViewPart extends ViewPart { + + private _domNode: FastDomNode; + private _scrollTop: number; + private _width: number; + private _shouldShow: boolean; + private _useShadows: boolean; + + constructor(context: ViewContext) { + super(context); + + this._scrollTop = 0; + this._width = 0; + this._updateWidth(); + this._shouldShow = false; + this._useShadows = this._context.configuration.editor.viewInfo.scrollbar.useShadows; + this._domNode = createFastDomNode(document.createElement('div')); + this._domNode.setAttribute('role', 'presentation'); + this._domNode.setAttribute('aria-hidden', 'true'); + } + + public dispose(): void { + super.dispose(); + } + + private _updateShouldShow(): boolean { + let newShouldShow = (this._useShadows && this._scrollTop > 0); + if (this._shouldShow !== newShouldShow) { + this._shouldShow = newShouldShow; + return true; + } + return false; + } + + public getDomNode(): FastDomNode { + return this._domNode; + } + + private _updateWidth(): boolean { + const layoutInfo = this._context.configuration.editor.layoutInfo; + let newWidth = layoutInfo.width - layoutInfo.minimapWidth; + if (this._width !== newWidth) { + this._width = newWidth; + return true; + } + return false; + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + let shouldRender = false; + if (e.viewInfo) { + this._useShadows = this._context.configuration.editor.viewInfo.scrollbar.useShadows; + } + if (e.layoutInfo) { + shouldRender = this._updateWidth(); + } + return this._updateShouldShow() || shouldRender; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + this._scrollTop = e.scrollTop; + return this._updateShouldShow(); + } + + // --- end event handlers + + public prepareRender(ctx: RenderingContext): void { + // Nothing to read + } + + public render(ctx: RestrictedRenderingContext): void { + this._domNode.setWidth(this._width); + this._domNode.setClassName(this._shouldShow ? 'scroll-decoration' : ''); + } +} + +registerThemingParticipant((theme, collector) => { + let shadow = theme.getColor(scrollbarShadow); + if (shadow) { + collector.addRule(`.monaco-editor .scroll-decoration { box-shadow: ${shadow} 0 6px 6px -6px inset; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/selections/selections.css b/src/vs/editor/browser/viewParts/selections/selections.css new file mode 100644 index 0000000000..ad609c3245 --- /dev/null +++ b/src/vs/editor/browser/viewParts/selections/selections.css @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* + Keeping name short for faster parsing. + cslr = core selections layer rendering (div) +*/ +.monaco-editor .lines-content .cslr { + position: absolute; +} + +.monaco-editor .top-left-radius { border-top-left-radius: 3px; } +.monaco-editor .bottom-left-radius { border-bottom-left-radius: 3px; } +.monaco-editor .top-right-radius { border-top-right-radius: 3px; } +.monaco-editor .bottom-right-radius { border-bottom-right-radius: 3px; } + +.monaco-editor.hc-black .top-left-radius { border-top-left-radius: 0; } +.monaco-editor.hc-black .bottom-left-radius { border-bottom-left-radius: 0; } +.monaco-editor.hc-black .top-right-radius { border-top-right-radius: 0; } +.monaco-editor.hc-black .bottom-right-radius { border-bottom-right-radius: 0; } diff --git a/src/vs/editor/browser/viewParts/selections/selections.ts b/src/vs/editor/browser/viewParts/selections/selections.ts new file mode 100644 index 0000000000..c07026f1d9 --- /dev/null +++ b/src/vs/editor/browser/viewParts/selections/selections.ts @@ -0,0 +1,409 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./selections'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorSelectionBackground, editorInactiveSelection, editorSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { HorizontalRange, LineVisibleRanges, RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { Range } from 'vs/editor/common/core/range'; +import * as browser from 'vs/base/browser/browser'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; + +const enum CornerStyle { + EXTERN, + INTERN, + FLAT +} + +interface IVisibleRangeEndPointStyle { + top: CornerStyle; + bottom: CornerStyle; +} + +class HorizontalRangeWithStyle { + public left: number; + public width: number; + public startStyle: IVisibleRangeEndPointStyle; + public endStyle: IVisibleRangeEndPointStyle; + + constructor(other: HorizontalRange) { + this.left = other.left; + this.width = other.width; + this.startStyle = null; + this.endStyle = null; + } +} + +class LineVisibleRangesWithStyle { + public lineNumber: number; + public ranges: HorizontalRangeWithStyle[]; + + constructor(lineNumber: number, ranges: HorizontalRangeWithStyle[]) { + this.lineNumber = lineNumber; + this.ranges = ranges; + } +} + +function toStyledRange(item: HorizontalRange): HorizontalRangeWithStyle { + return new HorizontalRangeWithStyle(item); +} + +function toStyled(item: LineVisibleRanges): LineVisibleRangesWithStyle { + return new LineVisibleRangesWithStyle(item.lineNumber, item.ranges.map(toStyledRange)); +} + +// TODO@Alex: Remove this once IE11 fixes Bug #524217 +// The problem in IE11 is that it does some sort of auto-zooming to accomodate for displays with different pixel density. +// Unfortunately, this auto-zooming is buggy around dealing with rounded borders +const isIEWithZoomingIssuesNearRoundedBorders = browser.isEdgeOrIE; + + +export class SelectionsOverlay extends DynamicViewOverlay { + + private static SELECTION_CLASS_NAME = 'selected-text'; + private static SELECTION_TOP_LEFT = 'top-left-radius'; + private static SELECTION_BOTTOM_LEFT = 'bottom-left-radius'; + private static SELECTION_TOP_RIGHT = 'top-right-radius'; + private static SELECTION_BOTTOM_RIGHT = 'bottom-right-radius'; + private static EDITOR_BACKGROUND_CLASS_NAME = 'monaco-editor-background'; + + private static ROUNDED_PIECE_WIDTH = 10; + + private _context: ViewContext; + private _lineHeight: number; + private _roundedSelection: boolean; + private _selections: Range[]; + private _renderResult: string[]; + + constructor(context: ViewContext) { + super(); + this._context = context; + this._lineHeight = this._context.configuration.editor.lineHeight; + this._roundedSelection = this._context.configuration.editor.viewInfo.roundedSelection; + this._selections = []; + this._renderResult = null; + this._context.addEventHandler(this); + } + + public dispose(): void { + this._context.removeEventHandler(this); + this._context = null; + this._selections = null; + this._renderResult = null; + super.dispose(); + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.lineHeight) { + this._lineHeight = this._context.configuration.editor.lineHeight; + } + if (e.viewInfo) { + this._roundedSelection = this._context.configuration.editor.viewInfo.roundedSelection; + } + return true; + } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + this._selections = e.selections.slice(0); + return true; + } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + // true for inline decorations that can end up relayouting text + return true;//e.inlineDecorationsChanged; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return true; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollTopChanged; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + // --- end event handlers + + private _visibleRangesHaveGaps(linesVisibleRanges: LineVisibleRangesWithStyle[]): boolean { + + for (let i = 0, len = linesVisibleRanges.length; i < len; i++) { + let lineVisibleRanges = linesVisibleRanges[i]; + + if (lineVisibleRanges.ranges.length > 1) { + // There are two ranges on the same line + return true; + } + } + + return false; + } + + private _enrichVisibleRangesWithStyle(linesVisibleRanges: LineVisibleRangesWithStyle[], previousFrame: LineVisibleRangesWithStyle[]): void { + let previousFrameTop: HorizontalRangeWithStyle = null; + let previousFrameBottom: HorizontalRangeWithStyle = null; + + if (previousFrame && previousFrame.length > 0 && linesVisibleRanges.length > 0) { + + let topLineNumber = linesVisibleRanges[0].lineNumber; + for (let i = 0; !previousFrameTop && i < previousFrame.length; i++) { + if (previousFrame[i].lineNumber === topLineNumber) { + previousFrameTop = previousFrame[i].ranges[0]; + } + } + + let bottomLineNumber = linesVisibleRanges[linesVisibleRanges.length - 1].lineNumber; + for (let i = previousFrame.length - 1; !previousFrameBottom && i >= 0; i--) { + if (previousFrame[i].lineNumber === bottomLineNumber) { + previousFrameBottom = previousFrame[i].ranges[0]; + } + } + + if (previousFrameTop && !previousFrameTop.startStyle) { + previousFrameTop = null; + } + if (previousFrameBottom && !previousFrameBottom.startStyle) { + previousFrameBottom = null; + } + } + + for (let i = 0, len = linesVisibleRanges.length; i < len; i++) { + // We know for a fact that there is precisely one range on each line + let curLineRange = linesVisibleRanges[i].ranges[0]; + let curLeft = curLineRange.left; + let curRight = curLineRange.left + curLineRange.width; + + let startStyle = { + top: CornerStyle.EXTERN, + bottom: CornerStyle.EXTERN + }; + + let endStyle = { + top: CornerStyle.EXTERN, + bottom: CornerStyle.EXTERN + }; + + if (i > 0) { + // Look above + let prevLeft = linesVisibleRanges[i - 1].ranges[0].left; + let prevRight = linesVisibleRanges[i - 1].ranges[0].left + linesVisibleRanges[i - 1].ranges[0].width; + + if (curLeft === prevLeft) { + startStyle.top = CornerStyle.FLAT; + } else if (curLeft > prevLeft) { + startStyle.top = CornerStyle.INTERN; + } + + if (curRight === prevRight) { + endStyle.top = CornerStyle.FLAT; + } else if (prevLeft < curRight && curRight < prevRight) { + endStyle.top = CornerStyle.INTERN; + } + } else if (previousFrameTop) { + // Accept some hick-ups near the viewport edges to save on repaints + startStyle.top = previousFrameTop.startStyle.top; + endStyle.top = previousFrameTop.endStyle.top; + } + + if (i + 1 < len) { + // Look below + let nextLeft = linesVisibleRanges[i + 1].ranges[0].left; + let nextRight = linesVisibleRanges[i + 1].ranges[0].left + linesVisibleRanges[i + 1].ranges[0].width; + + if (curLeft === nextLeft) { + startStyle.bottom = CornerStyle.FLAT; + } else if (nextLeft < curLeft && curLeft < nextRight) { + startStyle.bottom = CornerStyle.INTERN; + } + + if (curRight === nextRight) { + endStyle.bottom = CornerStyle.FLAT; + } else if (curRight < nextRight) { + endStyle.bottom = CornerStyle.INTERN; + } + } else if (previousFrameBottom) { + // Accept some hick-ups near the viewport edges to save on repaints + startStyle.bottom = previousFrameBottom.startStyle.bottom; + endStyle.bottom = previousFrameBottom.endStyle.bottom; + } + + curLineRange.startStyle = startStyle; + curLineRange.endStyle = endStyle; + } + } + + private _getVisibleRangesWithStyle(selection: Range, ctx: RenderingContext, previousFrame: LineVisibleRangesWithStyle[]): LineVisibleRangesWithStyle[] { + let _linesVisibleRanges = ctx.linesVisibleRangesForRange(selection, true) || []; + let linesVisibleRanges = _linesVisibleRanges.map(toStyled); + let visibleRangesHaveGaps = this._visibleRangesHaveGaps(linesVisibleRanges); + + if (!isIEWithZoomingIssuesNearRoundedBorders && !visibleRangesHaveGaps && this._roundedSelection) { + this._enrichVisibleRangesWithStyle(linesVisibleRanges, previousFrame); + } + + // The visible ranges are sorted TOP-BOTTOM and LEFT-RIGHT + return linesVisibleRanges; + } + + private _createSelectionPiece(top: number, height: string, className: string, left: number, width: number): string { + return ( + '
' + ); + } + + private _actualRenderOneSelection(output2: string[], visibleStartLineNumber: number, hasMultipleSelections: boolean, visibleRanges: LineVisibleRangesWithStyle[]): void { + let visibleRangesHaveStyle = (visibleRanges.length > 0 && visibleRanges[0].ranges[0].startStyle); + let fullLineHeight = (this._lineHeight).toString(); + let reducedLineHeight = (this._lineHeight - 1).toString(); + + let firstLineNumber = (visibleRanges.length > 0 ? visibleRanges[0].lineNumber : 0); + let lastLineNumber = (visibleRanges.length > 0 ? visibleRanges[visibleRanges.length - 1].lineNumber : 0); + + for (let i = 0, len = visibleRanges.length; i < len; i++) { + let lineVisibleRanges = visibleRanges[i]; + let lineNumber = lineVisibleRanges.lineNumber; + let lineIndex = lineNumber - visibleStartLineNumber; + + let lineHeight = hasMultipleSelections ? (lineNumber === lastLineNumber || lineNumber === firstLineNumber ? reducedLineHeight : fullLineHeight) : fullLineHeight; + let top = hasMultipleSelections ? (lineNumber === firstLineNumber ? 1 : 0) : 0; + + let lineOutput = ''; + + for (let j = 0, lenJ = lineVisibleRanges.ranges.length; j < lenJ; j++) { + let visibleRange = lineVisibleRanges.ranges[j]; + + if (visibleRangesHaveStyle) { + if (visibleRange.startStyle.top === CornerStyle.INTERN || visibleRange.startStyle.bottom === CornerStyle.INTERN) { + // Reverse rounded corner to the left + + // First comes the selection (blue layer) + lineOutput += this._createSelectionPiece(top, lineHeight, SelectionsOverlay.SELECTION_CLASS_NAME, visibleRange.left - SelectionsOverlay.ROUNDED_PIECE_WIDTH, SelectionsOverlay.ROUNDED_PIECE_WIDTH); + + // Second comes the background (white layer) with inverse border radius + let className = SelectionsOverlay.EDITOR_BACKGROUND_CLASS_NAME; + if (visibleRange.startStyle.top === CornerStyle.INTERN) { + className += ' ' + SelectionsOverlay.SELECTION_TOP_RIGHT; + } + if (visibleRange.startStyle.bottom === CornerStyle.INTERN) { + className += ' ' + SelectionsOverlay.SELECTION_BOTTOM_RIGHT; + } + lineOutput += this._createSelectionPiece(top, lineHeight, className, visibleRange.left - SelectionsOverlay.ROUNDED_PIECE_WIDTH, SelectionsOverlay.ROUNDED_PIECE_WIDTH); + } + if (visibleRange.endStyle.top === CornerStyle.INTERN || visibleRange.endStyle.bottom === CornerStyle.INTERN) { + // Reverse rounded corner to the right + + // First comes the selection (blue layer) + lineOutput += this._createSelectionPiece(top, lineHeight, SelectionsOverlay.SELECTION_CLASS_NAME, visibleRange.left + visibleRange.width, SelectionsOverlay.ROUNDED_PIECE_WIDTH); + + // Second comes the background (white layer) with inverse border radius + let className = SelectionsOverlay.EDITOR_BACKGROUND_CLASS_NAME; + if (visibleRange.endStyle.top === CornerStyle.INTERN) { + className += ' ' + SelectionsOverlay.SELECTION_TOP_LEFT; + } + if (visibleRange.endStyle.bottom === CornerStyle.INTERN) { + className += ' ' + SelectionsOverlay.SELECTION_BOTTOM_LEFT; + } + lineOutput += this._createSelectionPiece(top, lineHeight, className, visibleRange.left + visibleRange.width, SelectionsOverlay.ROUNDED_PIECE_WIDTH); + } + } + + let className = SelectionsOverlay.SELECTION_CLASS_NAME; + if (visibleRangesHaveStyle) { + if (visibleRange.startStyle.top === CornerStyle.EXTERN) { + className += ' ' + SelectionsOverlay.SELECTION_TOP_LEFT; + } + if (visibleRange.startStyle.bottom === CornerStyle.EXTERN) { + className += ' ' + SelectionsOverlay.SELECTION_BOTTOM_LEFT; + } + if (visibleRange.endStyle.top === CornerStyle.EXTERN) { + className += ' ' + SelectionsOverlay.SELECTION_TOP_RIGHT; + } + if (visibleRange.endStyle.bottom === CornerStyle.EXTERN) { + className += ' ' + SelectionsOverlay.SELECTION_BOTTOM_RIGHT; + } + } + lineOutput += this._createSelectionPiece(top, lineHeight, className, visibleRange.left, visibleRange.width); + } + + output2[lineIndex] += lineOutput; + } + } + + private _previousFrameVisibleRangesWithStyle: LineVisibleRangesWithStyle[][] = []; + public prepareRender(ctx: RenderingContext): void { + + let output: string[] = []; + let visibleStartLineNumber = ctx.visibleRange.startLineNumber; + let visibleEndLineNumber = ctx.visibleRange.endLineNumber; + for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { + let lineIndex = lineNumber - visibleStartLineNumber; + output[lineIndex] = ''; + } + + let thisFrameVisibleRangesWithStyle: LineVisibleRangesWithStyle[][] = []; + for (let i = 0, len = this._selections.length; i < len; i++) { + let selection = this._selections[i]; + if (selection.isEmpty()) { + thisFrameVisibleRangesWithStyle[i] = null; + continue; + } + + let visibleRangesWithStyle = this._getVisibleRangesWithStyle(selection, ctx, this._previousFrameVisibleRangesWithStyle[i]); + thisFrameVisibleRangesWithStyle[i] = visibleRangesWithStyle; + this._actualRenderOneSelection(output, visibleStartLineNumber, this._selections.length > 1, visibleRangesWithStyle); + } + + this._previousFrameVisibleRangesWithStyle = thisFrameVisibleRangesWithStyle; + this._renderResult = output; + } + + public render(startLineNumber: number, lineNumber: number): string { + if (!this._renderResult) { + return ''; + } + let lineIndex = lineNumber - startLineNumber; + if (lineIndex < 0 || lineIndex >= this._renderResult.length) { + throw new Error('Unexpected render request'); + } + return this._renderResult[lineIndex]; + } +} + +registerThemingParticipant((theme, collector) => { + let editorSelectionColor = theme.getColor(editorSelectionBackground); + if (editorSelectionColor) { + collector.addRule(`.monaco-editor .focused .selected-text { background-color: ${editorSelectionColor}; }`); + } + let editorInactiveSelectionColor = theme.getColor(editorInactiveSelection); + if (editorInactiveSelectionColor) { + collector.addRule(`.monaco-editor .selected-text { background-color: ${editorInactiveSelectionColor}; }`); + } + let editorSelectionForegroundColor = theme.getColor(editorSelectionForeground); + if (editorSelectionForegroundColor) { + collector.addRule(`.monaco-editor .view-line span.inline-selected-text { color: ${editorSelectionForegroundColor}; }`); + } +}); diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts new file mode 100644 index 0000000000..68a38ef517 --- /dev/null +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts @@ -0,0 +1,203 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; +import { Configuration } from 'vs/editor/browser/config/configuration'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import * as dom from 'vs/base/browser/dom'; + +export interface IViewCursorRenderData { + domNode: HTMLElement; + position: Position; + contentLeft: number; + width: number; + height: number; +} + +class ViewCursorRenderData { + public readonly top: number; + public readonly left: number; + public readonly width: number; + public readonly textContent: string; + + constructor(top: number, left: number, width: number, textContent: string) { + this.top = top; + this.left = left; + this.width = width; + this.textContent = textContent; + } +} + +export class ViewCursor { + private readonly _context: ViewContext; + private readonly _isSecondary: boolean; + private readonly _domNode: FastDomNode; + + private _cursorStyle: TextEditorCursorStyle; + private _lineHeight: number; + private _typicalHalfwidthCharacterWidth: number; + + private _isVisible: boolean; + + private _position: Position; + private _isInEditableRange: boolean; + + private _lastRenderedContent: string; + private _renderData: ViewCursorRenderData; + + constructor(context: ViewContext, isSecondary: boolean) { + this._context = context; + this._isSecondary = isSecondary; + + this._cursorStyle = this._context.configuration.editor.viewInfo.cursorStyle; + this._lineHeight = this._context.configuration.editor.lineHeight; + this._typicalHalfwidthCharacterWidth = this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth; + + this._isVisible = true; + + // Create the dom node + this._domNode = createFastDomNode(document.createElement('div')); + if (this._isSecondary) { + this._domNode.setClassName('cursor secondary'); + } else { + this._domNode.setClassName('cursor'); + } + this._domNode.setHeight(this._lineHeight); + this._domNode.setTop(0); + this._domNode.setLeft(0); + Configuration.applyFontInfo(this._domNode, this._context.configuration.editor.fontInfo); + this._domNode.setDisplay('none'); + + this.updatePosition(new Position(1, 1)); + this._isInEditableRange = true; + + this._lastRenderedContent = ''; + this._renderData = null; + } + + public getDomNode(): FastDomNode { + return this._domNode; + } + + public getIsInEditableRange(): boolean { + return this._isInEditableRange; + } + + public getPosition(): Position { + return this._position; + } + + public show(): void { + if (!this._isVisible) { + this._domNode.setVisibility('inherit'); + this._isVisible = true; + } + } + + public hide(): void { + if (this._isVisible) { + this._domNode.setVisibility('hidden'); + this._isVisible = false; + } + } + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + if (e.lineHeight) { + this._lineHeight = this._context.configuration.editor.lineHeight; + } + if (e.viewInfo) { + this._cursorStyle = this._context.configuration.editor.viewInfo.cursorStyle; + } + if (e.fontInfo) { + Configuration.applyFontInfo(this._domNode, this._context.configuration.editor.fontInfo); + this._typicalHalfwidthCharacterWidth = this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth; + } + return true; + } + + public onCursorPositionChanged(position: Position, isInEditableRange: boolean): boolean { + this.updatePosition(position); + this._isInEditableRange = isInEditableRange; + return true; + } + + private _prepareRender(ctx: RenderingContext): ViewCursorRenderData { + if (this._cursorStyle === TextEditorCursorStyle.Line || this._cursorStyle === TextEditorCursorStyle.LineThin) { + const visibleRange = ctx.visibleRangeForPosition(this._position); + if (!visibleRange) { + // Outside viewport + return null; + } + let width: number; + if (this._cursorStyle === TextEditorCursorStyle.Line) { + width = dom.computeScreenAwareSize(2); + } else { + width = dom.computeScreenAwareSize(1); + } + const top = ctx.getVerticalOffsetForLineNumber(this._position.lineNumber) - ctx.bigNumbersDelta; + return new ViewCursorRenderData(top, visibleRange.left, width, ''); + } + + const visibleRangeForCharacter = ctx.linesVisibleRangesForRange(new Range(this._position.lineNumber, this._position.column, this._position.lineNumber, this._position.column + 1), false); + + if (!visibleRangeForCharacter || visibleRangeForCharacter.length === 0 || visibleRangeForCharacter[0].ranges.length === 0) { + // Outside viewport + return null; + } + + const range = visibleRangeForCharacter[0].ranges[0]; + const width = range.width < 1 ? this._typicalHalfwidthCharacterWidth : range.width; + + let textContent = ''; + if (this._cursorStyle === TextEditorCursorStyle.Block) { + const lineContent = this._context.model.getLineContent(this._position.lineNumber); + textContent = lineContent.charAt(this._position.column - 1); + } + + const top = ctx.getVerticalOffsetForLineNumber(this._position.lineNumber) - ctx.bigNumbersDelta; + return new ViewCursorRenderData(top, range.left, width, textContent); + } + + public prepareRender(ctx: RenderingContext): void { + this._renderData = this._prepareRender(ctx); + } + + public render(ctx: RestrictedRenderingContext): IViewCursorRenderData { + if (!this._renderData) { + this._domNode.setDisplay('none'); + return null; + } + + if (this._lastRenderedContent !== this._renderData.textContent) { + this._lastRenderedContent = this._renderData.textContent; + this._domNode.domNode.textContent = this._lastRenderedContent; + } + + this._domNode.setDisplay('block'); + this._domNode.setTop(this._renderData.top); + this._domNode.setLeft(this._renderData.left); + this._domNode.setWidth(this._renderData.width); + this._domNode.setLineHeight(this._lineHeight); + this._domNode.setHeight(this._lineHeight); + + return { + domNode: this._domNode.domNode, + position: this._position, + contentLeft: this._renderData.left, + height: this._lineHeight, + width: 2 + }; + } + + private updatePosition(newPosition: Position): void { + this._position = newPosition; + } +} diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css new file mode 100644 index 0000000000..6428a4b196 --- /dev/null +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +.monaco-editor .cursors-layer { + position: absolute; + top: 0; +} + +.monaco-editor .cursors-layer > .cursor { + position: absolute; + cursor: text; +} +.monaco-editor .cursors-layer > .cursor.secondary { + opacity: 0.6; +} + +/* -- block-outline-style -- */ +.monaco-editor .cursors-layer.cursor-block-outline-style > .cursor { + box-sizing: border-box; + background: transparent !important; + border-style: solid; + border-width: 1px; +} + +/* -- underline-style -- */ +.monaco-editor .cursors-layer.cursor-underline-style > .cursor { + border-bottom-width: 2px; + border-bottom-style: solid; + background: transparent !important; + box-sizing: border-box; +} + +/* -- underline-thin-style -- */ +.monaco-editor .cursors-layer.cursor-underline-thin-style > .cursor { + border-bottom-width: 1px; + border-bottom-style: solid; + background: transparent !important; + box-sizing: border-box; +} + +@keyframes monaco-cursor-smooth { + 0%, + 20% { + opacity: 1; + } + 60%, + 100% { + opacity: 0; + } +} + +@keyframes monaco-cursor-phase { + 0%, + 20% { + opacity: 1; + } + 90%, + 100% { + opacity: 0; + } +} + +@keyframes monaco-cursor-expand { + 0%, + 20% { + transform: scaleY(1); + } + 80%, + 100% { + transform: scaleY(0); + } +} + +.cursor-smooth { + animation: monaco-cursor-smooth 0.5s ease-in-out 0s 20 alternate; +} + +.cursor-phase { + animation: monaco-cursor-phase 0.5s ease-in-out 0s 20 alternate; +} + +.cursor-expand > .cursor { + animation: monaco-cursor-expand 0.5s ease-in-out 0s 20 alternate; +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts new file mode 100644 index 0000000000..a738de7f0a --- /dev/null +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -0,0 +1,368 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./viewCursors'; +import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { Position } from 'vs/editor/common/core/position'; +import { IViewCursorRenderData, ViewCursor } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { TimeoutTimer, IntervalTimer } from 'vs/base/common/async'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorCursorForeground, editorCursorBackground } from 'vs/editor/common/view/editorColorRegistry'; +import { TextEditorCursorBlinkingStyle, TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; + +export class ViewCursors extends ViewPart { + + static BLINK_INTERVAL = 500; + + private _readOnly: boolean; + private _cursorBlinking: TextEditorCursorBlinkingStyle; + private _cursorStyle: TextEditorCursorStyle; + private _selectionIsEmpty: boolean; + + private _isVisible: boolean; + + private _domNode: FastDomNode; + + private _startCursorBlinkAnimation: TimeoutTimer; + private _cursorFlatBlinkInterval: IntervalTimer; + private _blinkingEnabled: boolean; + + private _editorHasFocus: boolean; + + private _primaryCursor: ViewCursor; + private _secondaryCursors: ViewCursor[]; + private _renderData: IViewCursorRenderData[]; + + constructor(context: ViewContext) { + super(context); + + this._readOnly = this._context.configuration.editor.readOnly; + this._cursorBlinking = this._context.configuration.editor.viewInfo.cursorBlinking; + this._cursorStyle = this._context.configuration.editor.viewInfo.cursorStyle; + this._selectionIsEmpty = true; + + this._primaryCursor = new ViewCursor(this._context, false); + this._secondaryCursors = []; + this._renderData = []; + + this._domNode = createFastDomNode(document.createElement('div')); + this._domNode.setAttribute('role', 'presentation'); + this._domNode.setAttribute('aria-hidden', 'true'); + this._updateDomClassName(); + + this._domNode.appendChild(this._primaryCursor.getDomNode()); + + this._startCursorBlinkAnimation = new TimeoutTimer(); + this._cursorFlatBlinkInterval = new IntervalTimer(); + + this._blinkingEnabled = false; + + this._editorHasFocus = false; + this._updateBlinking(); + } + + public dispose(): void { + super.dispose(); + this._startCursorBlinkAnimation.dispose(); + this._cursorFlatBlinkInterval.dispose(); + } + + public getDomNode(): FastDomNode { + return this._domNode; + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + + if (e.readOnly) { + this._readOnly = this._context.configuration.editor.readOnly; + } + if (e.viewInfo) { + this._cursorBlinking = this._context.configuration.editor.viewInfo.cursorBlinking; + this._cursorStyle = this._context.configuration.editor.viewInfo.cursorStyle; + } + + this._primaryCursor.onConfigurationChanged(e); + this._updateBlinking(); + if (e.viewInfo) { + this._updateDomClassName(); + } + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { + this._secondaryCursors[i].onConfigurationChanged(e); + } + return true; + } + private _onCursorPositionChanged(position: Position, secondaryPositions: Position[], isInEditableRange: boolean): void { + this._primaryCursor.onCursorPositionChanged(position, isInEditableRange); + this._updateBlinking(); + + if (this._secondaryCursors.length < secondaryPositions.length) { + // Create new cursors + let addCnt = secondaryPositions.length - this._secondaryCursors.length; + for (let i = 0; i < addCnt; i++) { + let newCursor = new ViewCursor(this._context, true); + this._domNode.domNode.insertBefore(newCursor.getDomNode().domNode, this._primaryCursor.getDomNode().domNode.nextSibling); + this._secondaryCursors.push(newCursor); + } + } else if (this._secondaryCursors.length > secondaryPositions.length) { + // Remove some cursors + let removeCnt = this._secondaryCursors.length - secondaryPositions.length; + for (let i = 0; i < removeCnt; i++) { + this._domNode.removeChild(this._secondaryCursors[0].getDomNode()); + this._secondaryCursors.splice(0, 1); + } + } + + for (let i = 0; i < secondaryPositions.length; i++) { + this._secondaryCursors[i].onCursorPositionChanged(secondaryPositions[i], isInEditableRange); + } + + } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + let positions: Position[] = []; + for (let i = 0, len = e.selections.length; i < len; i++) { + positions[i] = e.selections[i].getPosition(); + } + this._onCursorPositionChanged(positions[0], positions.slice(1), e.isInEditableRange); + + const selectionIsEmpty = e.selections[0].isEmpty(); + if (this._selectionIsEmpty !== selectionIsEmpty) { + this._selectionIsEmpty = selectionIsEmpty; + this._updateDomClassName(); + } + + return true; + } + + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + // true for inline decorations that can end up relayouting text + return true; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { + this._editorHasFocus = e.isFocused; + this._updateBlinking(); + return false; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return true; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return true; + } + public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + let shouldRender = (position: Position) => { + for (let i = 0, len = e.ranges.length; i < len; i++) { + if (e.ranges[i].fromLineNumber <= position.lineNumber && position.lineNumber <= e.ranges[i].toLineNumber) { + return true; + } + } + return false; + }; + if (shouldRender(this._primaryCursor.getPosition())) { + return true; + } + for (let i = 0; i < this._secondaryCursors.length; i++) { + if (shouldRender(this._secondaryCursors[i].getPosition())) { + return true; + } + } + return false; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + // --- end event handlers + + public getPosition(): Position { + return this._primaryCursor.getPosition(); + } + + // ---- blinking logic + + private _getCursorBlinking(): TextEditorCursorBlinkingStyle { + if (!this._editorHasFocus) { + return TextEditorCursorBlinkingStyle.Hidden; + } + if (this._readOnly || !this._primaryCursor.getIsInEditableRange()) { + return TextEditorCursorBlinkingStyle.Solid; + } + return this._cursorBlinking; + } + + private _updateBlinking(): void { + this._startCursorBlinkAnimation.cancel(); + this._cursorFlatBlinkInterval.cancel(); + + let blinkingStyle = this._getCursorBlinking(); + + // hidden and solid are special as they involve no animations + let isHidden = (blinkingStyle === TextEditorCursorBlinkingStyle.Hidden); + let isSolid = (blinkingStyle === TextEditorCursorBlinkingStyle.Solid); + + if (isHidden) { + this._hide(); + } else { + this._show(); + } + + this._blinkingEnabled = false; + this._updateDomClassName(); + + if (!isHidden && !isSolid) { + if (blinkingStyle === TextEditorCursorBlinkingStyle.Blink) { + // flat blinking is handled by JavaScript to save battery life due to Chromium step timing issue https://bugs.chromium.org/p/chromium/issues/detail?id=361587 + this._cursorFlatBlinkInterval.cancelAndSet(() => { + if (this._isVisible) { + this._hide(); + } else { + this._show(); + } + }, ViewCursors.BLINK_INTERVAL); + } else { + this._startCursorBlinkAnimation.setIfNotSet(() => { + this._blinkingEnabled = true; + this._updateDomClassName(); + }, ViewCursors.BLINK_INTERVAL); + } + } + } + // --- end blinking logic + + private _updateDomClassName(): void { + this._domNode.setClassName(this._getClassName()); + } + + private _getClassName(): string { + let result = 'cursors-layer'; + if (!this._selectionIsEmpty) { + result += ' has-selection'; + } + switch (this._cursorStyle) { + case TextEditorCursorStyle.Line: + result += ' cursor-line-style'; + break; + case TextEditorCursorStyle.Block: + result += ' cursor-block-style'; + break; + case TextEditorCursorStyle.Underline: + result += ' cursor-underline-style'; + break; + case TextEditorCursorStyle.LineThin: + result += ' cursor-line-thin-style'; + break; + case TextEditorCursorStyle.BlockOutline: + result += ' cursor-block-outline-style'; + break; + case TextEditorCursorStyle.UnderlineThin: + result += ' cursor-underline-thin-style'; + break; + default: + result += ' cursor-line-style'; + } + if (this._blinkingEnabled) { + switch (this._getCursorBlinking()) { + case TextEditorCursorBlinkingStyle.Blink: + result += ' cursor-blink'; + break; + case TextEditorCursorBlinkingStyle.Smooth: + result += ' cursor-smooth'; + break; + case TextEditorCursorBlinkingStyle.Phase: + result += ' cursor-phase'; + break; + case TextEditorCursorBlinkingStyle.Expand: + result += ' cursor-expand'; + break; + case TextEditorCursorBlinkingStyle.Solid: + result += ' cursor-solid'; + break; + default: + result += ' cursor-solid'; + } + } else { + result += ' cursor-solid'; + } + return result; + } + + private _show(): void { + this._primaryCursor.show(); + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { + this._secondaryCursors[i].show(); + } + this._isVisible = true; + } + + private _hide(): void { + this._primaryCursor.hide(); + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { + this._secondaryCursors[i].hide(); + } + this._isVisible = false; + } + + // ---- IViewPart implementation + + public prepareRender(ctx: RenderingContext): void { + this._primaryCursor.prepareRender(ctx); + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { + this._secondaryCursors[i].prepareRender(ctx); + } + } + + public render(ctx: RestrictedRenderingContext): void { + let renderData: IViewCursorRenderData[] = [], renderDataLen = 0; + + const primaryRenderData = this._primaryCursor.render(ctx); + if (primaryRenderData) { + renderData[renderDataLen++] = primaryRenderData; + } + + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { + const secondaryRenderData = this._secondaryCursors[i].render(ctx); + if (secondaryRenderData) { + renderData[renderDataLen++] = secondaryRenderData; + } + } + + this._renderData = renderData; + } + + public getLastRenderData(): IViewCursorRenderData[] { + return this._renderData; + } +} + +registerThemingParticipant((theme, collector) => { + let caret = theme.getColor(editorCursorForeground); + if (caret) { + let caretBackground = theme.getColor(editorCursorBackground); + if (!caretBackground) { + caretBackground = caret.opposite(); + } + collector.addRule(`.monaco-editor .cursor { background-color: ${caret}; border-color: ${caret}; color: ${caretBackground}; }`); + if (theme.type === 'hc') { + collector.addRule(`.monaco-editor .cursors-layer.has-selection .cursor { border-left: 1px solid ${caretBackground}; border-right: 1px solid ${caretBackground}; }`); + } + } + +}); \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts new file mode 100644 index 0000000000..28a7d4bcc1 --- /dev/null +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -0,0 +1,353 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { IViewZone } from 'vs/editor/browser/editorBrowser'; +import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { Position } from 'vs/editor/common/core/position'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; + +export interface IMyViewZone { + whitespaceId: number; + delegate: IViewZone; + isVisible: boolean; + domNode: FastDomNode; + marginDomNode: FastDomNode; +} + +export interface IMyRenderData { + data: IViewWhitespaceViewportData[]; +} + +interface IComputedViewZoneProps { + afterViewLineNumber: number; + heightInPx: number; +} + +export class ViewZones extends ViewPart { + + private _zones: { [id: string]: IMyViewZone; }; + private _lineHeight: number; + private _contentWidth: number; + private _contentLeft: number; + + public domNode: FastDomNode; + + public marginDomNode: FastDomNode; + + constructor(context: ViewContext) { + super(context); + this._lineHeight = this._context.configuration.editor.lineHeight; + this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; + this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; + + this.domNode = createFastDomNode(document.createElement('div')); + this.domNode.setClassName('view-zones'); + this.domNode.setPosition('absolute'); + this.domNode.setAttribute('role', 'presentation'); + this.domNode.setAttribute('aria-hidden', 'true'); + + this.marginDomNode = createFastDomNode(document.createElement('div')); + this.marginDomNode.setClassName('margin-view-zones'); + this.marginDomNode.setPosition('absolute'); + this.marginDomNode.setAttribute('role', 'presentation'); + this.marginDomNode.setAttribute('aria-hidden', 'true'); + + this._zones = {}; + } + + public dispose(): void { + super.dispose(); + this._zones = {}; + } + + // ---- begin view event handlers + + private _recomputeWhitespacesProps(): boolean { + let hadAChange = false; + + let keys = Object.keys(this._zones); + for (let i = 0, len = keys.length; i < len; i++) { + let id = keys[i]; + let zone = this._zones[id]; + let props = this._computeWhitespaceProps(zone.delegate); + if (this._context.viewLayout.changeWhitespace(parseInt(id, 10), props.afterViewLineNumber, props.heightInPx)) { + this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); + hadAChange = true; + } + } + + return hadAChange; + } + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + + if (e.lineHeight) { + this._lineHeight = this._context.configuration.editor.lineHeight; + return this._recomputeWhitespacesProps(); + } + + if (e.layoutInfo) { + this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; + this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; + } + + return true; + } + + public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean { + return this._recomputeWhitespacesProps(); + } + + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollTopChanged || e.scrollWidthChanged; + } + + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + + // ---- end view event handlers + + private _getZoneOrdinal(zone: IViewZone): number { + + if (typeof zone.afterColumn !== 'undefined') { + return zone.afterColumn; + } + + return 10000; + } + + + private _computeWhitespaceProps(zone: IViewZone): IComputedViewZoneProps { + if (zone.afterLineNumber === 0) { + return { + afterViewLineNumber: 0, + heightInPx: this._heightInPixels(zone) + }; + } + + let zoneAfterModelPosition: Position; + if (typeof zone.afterColumn !== 'undefined') { + zoneAfterModelPosition = this._context.model.validateModelPosition({ + lineNumber: zone.afterLineNumber, + column: zone.afterColumn + }); + } else { + let validAfterLineNumber = this._context.model.validateModelPosition({ + lineNumber: zone.afterLineNumber, + column: 1 + }).lineNumber; + + zoneAfterModelPosition = new Position( + validAfterLineNumber, + this._context.model.getModelLineMaxColumn(validAfterLineNumber) + ); + } + + let zoneBeforeModelPosition: Position; + if (zoneAfterModelPosition.column === this._context.model.getModelLineMaxColumn(zoneAfterModelPosition.lineNumber)) { + zoneBeforeModelPosition = this._context.model.validateModelPosition({ + lineNumber: zoneAfterModelPosition.lineNumber + 1, + column: 1 + }); + } else { + zoneBeforeModelPosition = this._context.model.validateModelPosition({ + lineNumber: zoneAfterModelPosition.lineNumber, + column: zoneAfterModelPosition.column + 1 + }); + } + + let viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(zoneAfterModelPosition); + let isVisible = this._context.model.coordinatesConverter.modelPositionIsVisible(zoneBeforeModelPosition); + return { + afterViewLineNumber: viewPosition.lineNumber, + heightInPx: (isVisible ? this._heightInPixels(zone) : 0) + }; + } + + public addZone(zone: IViewZone): number { + let props = this._computeWhitespaceProps(zone); + let whitespaceId = this._context.viewLayout.addWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx); + + let myZone: IMyViewZone = { + whitespaceId: whitespaceId, + delegate: zone, + isVisible: false, + domNode: createFastDomNode(zone.domNode), + marginDomNode: zone.marginDomNode ? createFastDomNode(zone.marginDomNode) : null + }; + + this._safeCallOnComputedHeight(myZone.delegate, props.heightInPx); + + myZone.domNode.setPosition('absolute'); + myZone.domNode.domNode.style.width = '100%'; + myZone.domNode.setDisplay('none'); + myZone.domNode.setAttribute('monaco-view-zone', myZone.whitespaceId.toString()); + this.domNode.appendChild(myZone.domNode); + + if (myZone.marginDomNode) { + myZone.marginDomNode.setPosition('absolute'); + myZone.marginDomNode.domNode.style.width = '100%'; + myZone.marginDomNode.setDisplay('none'); + myZone.marginDomNode.setAttribute('monaco-view-zone', myZone.whitespaceId.toString()); + this.marginDomNode.appendChild(myZone.marginDomNode); + } + + this._zones[myZone.whitespaceId.toString()] = myZone; + + + this.setShouldRender(); + + return myZone.whitespaceId; + } + + public removeZone(id: number): boolean { + if (this._zones.hasOwnProperty(id.toString())) { + let zone = this._zones[id.toString()]; + delete this._zones[id.toString()]; + this._context.viewLayout.removeWhitespace(zone.whitespaceId); + + zone.domNode.removeAttribute('monaco-visible-view-zone'); + zone.domNode.removeAttribute('monaco-view-zone'); + zone.domNode.domNode.parentNode.removeChild(zone.domNode.domNode); + + if (zone.marginDomNode) { + zone.marginDomNode.removeAttribute('monaco-visible-view-zone'); + zone.marginDomNode.removeAttribute('monaco-view-zone'); + zone.marginDomNode.domNode.parentNode.removeChild(zone.marginDomNode.domNode); + } + + this.setShouldRender(); + + return true; + } + return false; + } + + public layoutZone(id: number): boolean { + let changed = false; + if (this._zones.hasOwnProperty(id.toString())) { + let zone = this._zones[id.toString()]; + let props = this._computeWhitespaceProps(zone.delegate); + // let newOrdinal = this._getZoneOrdinal(zone.delegate); + changed = this._context.viewLayout.changeWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx) || changed; + // TODO@Alex: change `newOrdinal` too + + if (changed) { + this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); + this.setShouldRender(); + } + } + return changed; + } + + public shouldSuppressMouseDownOnViewZone(id: number): boolean { + if (this._zones.hasOwnProperty(id.toString())) { + let zone = this._zones[id.toString()]; + return zone.delegate.suppressMouseDown; + } + return false; + } + + private _heightInPixels(zone: IViewZone): number { + if (typeof zone.heightInPx === 'number') { + return zone.heightInPx; + } + if (typeof zone.heightInLines === 'number') { + return this._lineHeight * zone.heightInLines; + } + return this._lineHeight; + } + + private _safeCallOnComputedHeight(zone: IViewZone, height: number): void { + if (typeof zone.onComputedHeight === 'function') { + try { + zone.onComputedHeight(height); + } catch (e) { + onUnexpectedError(e); + } + } + } + + private _safeCallOnDomNodeTop(zone: IViewZone, top: number): void { + if (typeof zone.onDomNodeTop === 'function') { + try { + zone.onDomNodeTop(top); + } catch (e) { + onUnexpectedError(e); + } + } + } + + public prepareRender(ctx: RenderingContext): void { + // Nothing to read + } + + public render(ctx: RestrictedRenderingContext): void { + const visibleWhitespaces = ctx.viewportData.whitespaceViewportData; + let visibleZones: { [id: string]: IViewWhitespaceViewportData; } = {}; + + let hasVisibleZone = false; + for (let i = 0, len = visibleWhitespaces.length; i < len; i++) { + visibleZones[visibleWhitespaces[i].id.toString()] = visibleWhitespaces[i]; + hasVisibleZone = true; + } + + let keys = Object.keys(this._zones); + for (let i = 0, len = keys.length; i < len; i++) { + let id = keys[i]; + let zone = this._zones[id]; + + let newTop = 0; + let newHeight = 0; + let newDisplay = 'none'; + if (visibleZones.hasOwnProperty(id)) { + newTop = visibleZones[id].verticalOffset - ctx.bigNumbersDelta; + newHeight = visibleZones[id].height; + newDisplay = 'block'; + // zone is visible + if (!zone.isVisible) { + zone.domNode.setAttribute('monaco-visible-view-zone', 'true'); + zone.isVisible = true; + } + this._safeCallOnDomNodeTop(zone.delegate, ctx.getScrolledTopFromAbsoluteTop(visibleZones[id].verticalOffset)); + } else { + if (zone.isVisible) { + zone.domNode.removeAttribute('monaco-visible-view-zone'); + zone.isVisible = false; + } + this._safeCallOnDomNodeTop(zone.delegate, ctx.getScrolledTopFromAbsoluteTop(-1000000)); + } + zone.domNode.setTop(newTop); + zone.domNode.setHeight(newHeight); + zone.domNode.setDisplay(newDisplay); + + if (zone.marginDomNode) { + zone.marginDomNode.setTop(newTop); + zone.marginDomNode.setHeight(newHeight); + zone.marginDomNode.setDisplay(newDisplay); + } + } + + if (hasVisibleZone) { + this.domNode.setWidth(Math.max(ctx.scrollWidth, this._contentWidth)); + this.marginDomNode.setWidth(this._contentLeft); + } + } +} diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts new file mode 100644 index 0000000000..5f16b5b15f --- /dev/null +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -0,0 +1,535 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./media/editor'; +import 'vs/css!./media/tokens'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as dom from 'vs/base/browser/dom'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { CommonCodeEditor } from 'vs/editor/common/commonCodeEditor'; +import { CommonEditorConfiguration } from 'vs/editor/common/config/commonEditorConfig'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { Configuration } from 'vs/editor/browser/config/configuration'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { View, IOverlayWidgetData, IContentWidgetData } from 'vs/editor/browser/view/viewImpl'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { InternalEditorAction } from 'vs/editor/common/editorAction'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IPosition } from 'vs/editor/common/core/position'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { CoreEditorCommand } from 'vs/editor/common/controller/coreCommands'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder } from 'vs/editor/common/view/editorColorRegistry'; +import { Color } from 'vs/base/common/color'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; + +export abstract class CodeEditorWidget extends CommonCodeEditor implements editorBrowser.ICodeEditor { + + private readonly _onMouseUp: Emitter = this._register(new Emitter()); + public readonly onMouseUp: Event = this._onMouseUp.event; + + private readonly _onMouseDown: Emitter = this._register(new Emitter()); + public readonly onMouseDown: Event = this._onMouseDown.event; + + private readonly _onMouseDrag: Emitter = this._register(new Emitter()); + public readonly onMouseDrag: Event = this._onMouseDrag.event; + + private readonly _onMouseDrop: Emitter = this._register(new Emitter()); + public readonly onMouseDrop: Event = this._onMouseDrop.event; + + private readonly _onContextMenu: Emitter = this._register(new Emitter()); + public readonly onContextMenu: Event = this._onContextMenu.event; + + private readonly _onMouseMove: Emitter = this._register(new Emitter()); + public readonly onMouseMove: Event = this._onMouseMove.event; + + private readonly _onMouseLeave: Emitter = this._register(new Emitter()); + public readonly onMouseLeave: Event = this._onMouseLeave.event; + + private readonly _onKeyUp: Emitter = this._register(new Emitter()); + public readonly onKeyUp: Event = this._onKeyUp.event; + + private readonly _onKeyDown: Emitter = this._register(new Emitter()); + public readonly onKeyDown: Event = this._onKeyDown.event; + + private readonly _onDidScrollChange: Emitter = this._register(new Emitter()); + public readonly onDidScrollChange: Event = this._onDidScrollChange.event; + + private readonly _onDidChangeViewZones: Emitter = this._register(new Emitter()); + public readonly onDidChangeViewZones: Event = this._onDidChangeViewZones.event; + + private _codeEditorService: ICodeEditorService; + private _commandService: ICommandService; + private _themeService: IThemeService; + + protected domElement: HTMLElement; + private _focusTracker: CodeEditorWidgetFocusTracker; + + _configuration: Configuration; + + private contentWidgets: { [key: string]: IContentWidgetData; }; + private overlayWidgets: { [key: string]: IOverlayWidgetData; }; + + _view: View; + + constructor( + domElement: HTMLElement, + options: IEditorOptions, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @ICommandService commandService: ICommandService, + @IContextKeyService contextKeyService: IContextKeyService, + @IThemeService themeService: IThemeService + ) { + super(domElement, options, instantiationService, contextKeyService); + this._codeEditorService = codeEditorService; + this._commandService = commandService; + this._themeService = themeService; + + this._focusTracker = new CodeEditorWidgetFocusTracker(domElement); + this._focusTracker.onChange(() => { + let hasFocus = this._focusTracker.hasFocus(); + + if (hasFocus) { + this._onDidFocusEditor.fire(); + } else { + this._onDidBlurEditor.fire(); + } + }); + + this.contentWidgets = {}; + this.overlayWidgets = {}; + + let contributions = this._getContributions(); + for (let i = 0, len = contributions.length; i < len; i++) { + let ctor = contributions[i]; + try { + let contribution = this._instantiationService.createInstance(ctor, this); + this._contributions[contribution.getId()] = contribution; + } catch (err) { + onUnexpectedError(err); + } + } + + this._getActions().forEach((action) => { + const internalAction = new InternalEditorAction( + action.id, + action.label, + action.alias, + action.precondition, + (): void | TPromise => { + return this._instantiationService.invokeFunction((accessor) => { + return action.runEditorCommand(accessor, this, null); + }); + }, + this._contextKeyService + ); + this._actions[internalAction.id] = internalAction; + }); + + this._codeEditorService.addCodeEditor(this); + } + + protected abstract _getContributions(): editorBrowser.IEditorContributionCtor[]; + protected abstract _getActions(): EditorAction[]; + + protected _createConfiguration(options: IEditorOptions): CommonEditorConfiguration { + return new Configuration(options, this.domElement); + } + + public dispose(): void { + this._codeEditorService.removeCodeEditor(this); + + this.contentWidgets = {}; + this.overlayWidgets = {}; + + this._focusTracker.dispose(); + super.dispose(); + } + + public createOverviewRuler(cssClassName: string, minimumHeight: number, maximumHeight: number): editorBrowser.IOverviewRuler { + return this._view.createOverviewRuler(cssClassName, minimumHeight, maximumHeight); + } + + public getDomNode(): HTMLElement { + if (!this.hasView) { + return null; + } + return this._view.domNode.domNode; + } + + public getCompletelyVisibleLinesRangeInViewport(): Range { + if (!this.hasView) { + return null; + } + const viewRange = this.viewModel.getCompletelyVisibleViewRange(); + return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange); + } + + public delegateVerticalScrollbarMouseDown(browserEvent: IMouseEvent): void { + if (!this.hasView) { + return; + } + this._view.delegateVerticalScrollbarMouseDown(browserEvent); + } + + public layout(dimension?: editorCommon.IDimension): void { + this._configuration.observeReferenceElement(dimension); + this.render(); + } + + public focus(): void { + if (!this.hasView) { + return; + } + this._view.focus(); + } + + public isFocused(): boolean { + return this.hasView && this._view.isFocused(); + } + + public hasWidgetFocus(): boolean { + return this._focusTracker && this._focusTracker.hasFocus(); + } + + public addContentWidget(widget: editorBrowser.IContentWidget): void { + let widgetData: IContentWidgetData = { + widget: widget, + position: widget.getPosition() + }; + + if (this.contentWidgets.hasOwnProperty(widget.getId())) { + console.warn('Overwriting a content widget with the same id.'); + } + + this.contentWidgets[widget.getId()] = widgetData; + + if (this.hasView) { + this._view.addContentWidget(widgetData); + } + } + + public layoutContentWidget(widget: editorBrowser.IContentWidget): void { + let widgetId = widget.getId(); + if (this.contentWidgets.hasOwnProperty(widgetId)) { + let widgetData = this.contentWidgets[widgetId]; + widgetData.position = widget.getPosition(); + if (this.hasView) { + this._view.layoutContentWidget(widgetData); + } + } + } + + public removeContentWidget(widget: editorBrowser.IContentWidget): void { + let widgetId = widget.getId(); + if (this.contentWidgets.hasOwnProperty(widgetId)) { + let widgetData = this.contentWidgets[widgetId]; + delete this.contentWidgets[widgetId]; + if (this.hasView) { + this._view.removeContentWidget(widgetData); + } + } + } + + public addOverlayWidget(widget: editorBrowser.IOverlayWidget): void { + let widgetData: IOverlayWidgetData = { + widget: widget, + position: widget.getPosition() + }; + + if (this.overlayWidgets.hasOwnProperty(widget.getId())) { + console.warn('Overwriting an overlay widget with the same id.'); + } + + this.overlayWidgets[widget.getId()] = widgetData; + + if (this.hasView) { + this._view.addOverlayWidget(widgetData); + } + } + + public layoutOverlayWidget(widget: editorBrowser.IOverlayWidget): void { + let widgetId = widget.getId(); + if (this.overlayWidgets.hasOwnProperty(widgetId)) { + let widgetData = this.overlayWidgets[widgetId]; + widgetData.position = widget.getPosition(); + if (this.hasView) { + this._view.layoutOverlayWidget(widgetData); + } + } + } + + public removeOverlayWidget(widget: editorBrowser.IOverlayWidget): void { + let widgetId = widget.getId(); + if (this.overlayWidgets.hasOwnProperty(widgetId)) { + let widgetData = this.overlayWidgets[widgetId]; + delete this.overlayWidgets[widgetId]; + if (this.hasView) { + this._view.removeOverlayWidget(widgetData); + } + } + } + + public changeViewZones(callback: (accessor: editorBrowser.IViewZoneChangeAccessor) => void): void { + if (!this.hasView) { + return; + } + let hasChanges = this._view.change(callback); + if (hasChanges) { + this._onDidChangeViewZones.fire(); + } + } + + public getWhitespaces(): IEditorWhitespace[] { + if (!this.hasView) { + return []; + } + return this.viewModel.viewLayout.getWhitespaces(); + } + + private _getVerticalOffsetForPosition(modelLineNumber: number, modelColumn: number): number { + let modelPosition = this.model.validatePosition({ + lineNumber: modelLineNumber, + column: modelColumn + }); + let viewPosition = this.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); + return this.viewModel.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber); + } + + public getTopForLineNumber(lineNumber: number): number { + if (!this.hasView) { + return -1; + } + return this._getVerticalOffsetForPosition(lineNumber, 1); + } + + public getTopForPosition(lineNumber: number, column: number): number { + if (!this.hasView) { + return -1; + } + return this._getVerticalOffsetForPosition(lineNumber, column); + } + + public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget { + if (!this.hasView) { + return null; + } + return this._view.getTargetAtClientPoint(clientX, clientY); + } + + public getScrolledVisiblePosition(rawPosition: IPosition): { top: number; left: number; height: number; } { + if (!this.hasView) { + return null; + } + + let position = this.model.validatePosition(rawPosition); + let layoutInfo = this._configuration.editor.layoutInfo; + + let top = this._getVerticalOffsetForPosition(position.lineNumber, position.column) - this.getScrollTop(); + let left = this._view.getOffsetForColumn(position.lineNumber, position.column) + layoutInfo.glyphMarginWidth + layoutInfo.lineNumbersWidth + layoutInfo.decorationsWidth - this.getScrollLeft(); + + return { + top: top, + left: left, + height: this._configuration.editor.lineHeight + }; + } + + public getOffsetForColumn(lineNumber: number, column: number): number { + if (!this.hasView) { + return -1; + } + return this._view.getOffsetForColumn(lineNumber, column); + } + + public render(): void { + if (!this.hasView) { + return; + } + this._view.render(true, false); + } + + public setHiddenAreas(ranges: IRange[]): void { + if (this.viewModel) { + this.viewModel.setHiddenAreas(ranges.map(r => Range.lift(r))); + } + } + + public setAriaActiveDescendant(id: string): void { + if (!this.hasView) { + return; + } + this._view.setAriaActiveDescendant(id); + } + + public applyFontInfo(target: HTMLElement): void { + Configuration.applyFontInfoSlow(target, this._configuration.editor.fontInfo); + } + + _attachModel(model: editorCommon.IModel): void { + this._view = null; + + super._attachModel(model); + + if (this._view) { + this.domElement.appendChild(this._view.domNode.domNode); + + let keys = Object.keys(this.contentWidgets); + for (let i = 0, len = keys.length; i < len; i++) { + let widgetId = keys[i]; + this._view.addContentWidget(this.contentWidgets[widgetId]); + } + + keys = Object.keys(this.overlayWidgets); + for (let i = 0, len = keys.length; i < len; i++) { + let widgetId = keys[i]; + this._view.addOverlayWidget(this.overlayWidgets[widgetId]); + } + + this._view.render(false, true); + this.hasView = true; + } + } + + protected _scheduleAtNextAnimationFrame(callback: () => void): IDisposable { + return dom.scheduleAtNextAnimationFrame(callback); + } + + protected _createView(): void { + this._view = new View( + this._commandService, + this._configuration, + this._themeService, + this.viewModel, + this.cursor, + (editorCommand: CoreEditorCommand, args: any) => { + if (!this.cursor) { + return; + } + editorCommand.runCoreEditorCommand(this.cursor, args); + } + ); + + const viewEventBus = this._view.getInternalEventBus(); + + viewEventBus.onDidGainFocus = () => { + this._onDidFocusEditorText.fire(); + // In IE, the focus is not synchronous, so we give it a little help + this._onDidFocusEditor.fire(); + }; + + viewEventBus.onDidScroll = (e) => this._onDidScrollChange.fire(e); + viewEventBus.onDidLoseFocus = () => this._onDidBlurEditorText.fire(); + viewEventBus.onContextMenu = (e) => this._onContextMenu.fire(e); + viewEventBus.onMouseDown = (e) => this._onMouseDown.fire(e); + viewEventBus.onMouseUp = (e) => this._onMouseUp.fire(e); + viewEventBus.onMouseDrag = (e) => this._onMouseDrag.fire(e); + viewEventBus.onMouseDrop = (e) => this._onMouseDrop.fire(e); + viewEventBus.onKeyUp = (e) => this._onKeyUp.fire(e); + viewEventBus.onMouseMove = (e) => this._onMouseMove.fire(e); + viewEventBus.onMouseLeave = (e) => this._onMouseLeave.fire(e); + viewEventBus.onKeyDown = (e) => this._onKeyDown.fire(e); + } + + protected _detachModel(): editorCommon.IModel { + let removeDomNode: HTMLElement = null; + + if (this._view) { + this._view.dispose(); + removeDomNode = this._view.domNode.domNode; + this._view = null; + } + + let result = super._detachModel(); + + if (removeDomNode) { + this.domElement.removeChild(removeDomNode); + } + + return result; + } + + // BEGIN decorations + + protected _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void { + this._codeEditorService.registerDecorationType(key, options, parentTypeKey); + } + + protected _removeDecorationType(key: string): void { + this._codeEditorService.removeDecorationType(key); + } + + protected _resolveDecorationOptions(typeKey: string, writable: boolean): editorCommon.IModelDecorationOptions { + return this._codeEditorService.resolveDecorationOptions(typeKey, writable); + } + + // END decorations +} + +class CodeEditorWidgetFocusTracker extends Disposable { + + private _hasFocus: boolean; + private _domFocusTracker: dom.IFocusTracker; + + private _onChange: Emitter = this._register(new Emitter()); + public onChange: Event = this._onChange.event; + + constructor(domElement: HTMLElement) { + super(); + + this._hasFocus = false; + this._domFocusTracker = this._register(dom.trackFocus(domElement)); + + this._domFocusTracker.addFocusListener(() => { + this._hasFocus = true; + this._onChange.fire(void 0); + }); + this._domFocusTracker.addBlurListener(() => { + this._hasFocus = false; + this._onChange.fire(void 0); + }); + } + + public hasFocus(): boolean { + return this._hasFocus; + } +} + +const squigglyStart = encodeURIComponent(``); + +function getSquigglySVGData(color: Color) { + return squigglyStart + encodeURIComponent(color.toString()) + squigglyEnd; +} + +registerThemingParticipant((theme, collector) => { + let errorBorderColor = theme.getColor(editorErrorBorder); + if (errorBorderColor) { + collector.addRule(`.monaco-editor .redsquiggly { border-bottom: 4px double ${errorBorderColor}; }`); + } + let errorForeground = theme.getColor(editorErrorForeground); + if (errorForeground) { + collector.addRule(`.monaco-editor .redsquiggly { background: url("data:image/svg+xml,${getSquigglySVGData(errorForeground)}") repeat-x bottom left; }`); + } + + let warningBorderColor = theme.getColor(editorWarningBorder); + if (warningBorderColor) { + collector.addRule(`.monaco-editor .greensquiggly { border-bottom: 4px double ${warningBorderColor}; }`); + } + let warningForeground = theme.getColor(editorWarningForeground); + if (warningForeground) { + collector.addRule(`.monaco-editor .greensquiggly { background: url("data:image/svg+xml;utf8,${getSquigglySVGData(warningForeground)}") repeat-x bottom left; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts new file mode 100644 index 0000000000..a8918db126 --- /dev/null +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -0,0 +1,2059 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/diffEditor'; +import * as nls from 'vs/nls'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as objects from 'vs/base/common/objects'; +import * as dom from 'vs/base/browser/dom'; +import Severity from 'vs/base/common/severity'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { ISashEvent, IVerticalSashLayoutProvider, Sash } from 'vs/base/browser/ui/sash/sash'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; +import { renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { CodeEditor } from 'vs/editor/browser/codeEditor'; +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; +import { Configuration } from 'vs/editor/browser/config/configuration'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { Selection, ISelection } from 'vs/editor/common/core/selection'; +import { InlineDecoration } from 'vs/editor/common/viewModel/viewModel'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ColorId, MetadataConsts, FontStyle } from 'vs/editor/common/modes'; +import Event, { Emitter } from 'vs/base/common/event'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { registerThemingParticipant, IThemeService, ITheme, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; +import { scrollbarShadow, diffInserted, diffRemoved, defaultInsertColor, defaultRemoveColor, diffInsertedOutline, diffRemovedOutline } from 'vs/platform/theme/common/colorRegistry'; +import { Color } from 'vs/base/common/color'; +import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { DiffReview } from 'vs/editor/browser/widget/diffReview'; +import URI from 'vs/base/common/uri'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder'; + +interface IEditorDiffDecorations { + decorations: editorCommon.IModelDeltaDecoration[]; + overviewZones: OverviewRulerZone[]; +} + +interface IEditorDiffDecorationsWithZones extends IEditorDiffDecorations { + zones: editorBrowser.IViewZone[]; +} + +interface IEditorsDiffDecorationsWithZones { + original: IEditorDiffDecorationsWithZones; + modified: IEditorDiffDecorationsWithZones; +} + +interface IEditorsZones { + original: editorBrowser.IViewZone[]; + modified: editorBrowser.IViewZone[]; +} + +interface IDiffEditorWidgetStyle { + getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones; + setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; + applyColors(theme: ITheme): boolean; + layout(): number; + dispose(): void; +} + +class VisualEditorState { + private _zones: number[]; + private _zonesMap: { [zoneId: string]: boolean; }; + private _decorations: string[]; + + constructor() { + this._zones = []; + this._zonesMap = {}; + this._decorations = []; + } + + public getForeignViewZones(allViewZones: IEditorWhitespace[]): IEditorWhitespace[] { + return allViewZones.filter((z) => !this._zonesMap[String(z.id)]); + } + + public clean(editor: CodeEditor): void { + // (1) View zones + if (this._zones.length > 0) { + editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => { + for (let i = 0, length = this._zones.length; i < length; i++) { + viewChangeAccessor.removeZone(this._zones[i]); + } + }); + } + this._zones = []; + this._zonesMap = {}; + + // (2) Model decorations + if (this._decorations.length > 0) { + editor.changeDecorations((changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => { + changeAccessor.deltaDecorations(this._decorations, []); + }); + } + this._decorations = []; + } + + public apply(editor: CodeEditor, overviewRuler: editorBrowser.IOverviewRuler, newDecorations: IEditorDiffDecorationsWithZones): void { + // view zones + editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => { + for (let i = 0, length = this._zones.length; i < length; i++) { + viewChangeAccessor.removeZone(this._zones[i]); + } + this._zones = []; + this._zonesMap = {}; + for (let i = 0, length = newDecorations.zones.length; i < length; i++) { + newDecorations.zones[i].suppressMouseDown = true; + let zoneId = viewChangeAccessor.addZone(newDecorations.zones[i]); + this._zones.push(zoneId); + this._zonesMap[String(zoneId)] = true; + } + }); + + // decorations + this._decorations = editor.deltaDecorations(this._decorations, newDecorations.decorations); + + // overview ruler + if (overviewRuler) { + overviewRuler.setZones(newDecorations.overviewZones); + } + } +} + +let DIFF_EDITOR_ID = 0; + +export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffEditor { + + private static ONE_OVERVIEW_WIDTH = 15; + public static ENTIRE_DIFF_OVERVIEW_WIDTH = 30; + private static UPDATE_DIFF_DECORATIONS_DELAY = 200; // ms + + private readonly _onDidDispose: Emitter = this._register(new Emitter()); + public readonly onDidDispose: Event = this._onDidDispose.event; + + private readonly _onDidUpdateDiff: Emitter = this._register(new Emitter()); + public readonly onDidUpdateDiff: Event = this._onDidUpdateDiff.event; + + private readonly id: number; + + private _domElement: HTMLElement; + protected readonly _containerDomElement: HTMLElement; + private readonly _overviewDomElement: HTMLElement; + private readonly _overviewViewportDomElement: FastDomNode; + + private _width: number; + private _height: number; + private _reviewHeight: number; + private readonly _measureDomElementToken: number; + + private originalEditor: CodeEditor; + private _originalDomNode: HTMLElement; + private _originalEditorState: VisualEditorState; + private _originalOverviewRuler: editorBrowser.IOverviewRuler; + + private modifiedEditor: CodeEditor; + private _modifiedDomNode: HTMLElement; + private _modifiedEditorState: VisualEditorState; + private _modifiedOverviewRuler: editorBrowser.IOverviewRuler; + + private _currentlyChangingViewZones: boolean; + private _beginUpdateDecorationsTimeout: number; + private _diffComputationToken: number; + private _lineChanges: editorCommon.ILineChange[]; + + private _isVisible: boolean; + private _isHandlingScrollEvent: boolean; + + private _ignoreTrimWhitespace: boolean; + private _originalIsEditable: boolean; + + private _renderSideBySide: boolean; + private _renderIndicators: boolean; + private _enableSplitViewResizing: boolean; + private _strategy: IDiffEditorWidgetStyle; + + private _updateDecorationsRunner: RunOnceScheduler; + + private _editorWorkerService: IEditorWorkerService; + protected _contextKeyService: IContextKeyService; + private _codeEditorService: ICodeEditorService; + private _themeService: IThemeService; + private readonly _messageService: IMessageService; + + private _reviewPane: DiffReview; + + constructor( + domElement: HTMLElement, + options: editorOptions.IDiffEditorOptions, + @IEditorWorkerService editorWorkerService: IEditorWorkerService, + @IContextKeyService contextKeyService: IContextKeyService, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @IThemeService themeService: IThemeService, + @IMessageService messageService: IMessageService + ) { + super(); + + this._editorWorkerService = editorWorkerService; + this._codeEditorService = codeEditorService; + this._contextKeyService = contextKeyService.createScoped(domElement); + this._contextKeyService.createKey('isInDiffEditor', true); + this._themeService = themeService; + this._messageService = messageService; + + this.id = (++DIFF_EDITOR_ID); + + this._domElement = domElement; + options = options || {}; + + // renderSideBySide + this._renderSideBySide = true; + if (typeof options.renderSideBySide !== 'undefined') { + this._renderSideBySide = options.renderSideBySide; + } + + // ignoreTrimWhitespace + this._ignoreTrimWhitespace = true; + if (typeof options.ignoreTrimWhitespace !== 'undefined') { + this._ignoreTrimWhitespace = options.ignoreTrimWhitespace; + } + + // renderIndicators + this._renderIndicators = true; + if (typeof options.renderIndicators !== 'undefined') { + this._renderIndicators = options.renderIndicators; + } + + this._originalIsEditable = false; + if (typeof options.originalEditable !== 'undefined') { + this._originalIsEditable = Boolean(options.originalEditable); + } + + this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0)); + + this._containerDomElement = document.createElement('div'); + this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide); + this._containerDomElement.style.position = 'relative'; + this._containerDomElement.style.height = '100%'; + this._domElement.appendChild(this._containerDomElement); + + this._overviewViewportDomElement = createFastDomNode(document.createElement('div')); + this._overviewViewportDomElement.setClassName('diffViewport'); + this._overviewViewportDomElement.setPosition('absolute'); + + this._overviewDomElement = document.createElement('div'); + this._overviewDomElement.className = 'diffOverview'; + this._overviewDomElement.style.position = 'absolute'; + + this._overviewDomElement.appendChild(this._overviewViewportDomElement.domNode); + + this._register(dom.addStandardDisposableListener(this._overviewDomElement, 'mousedown', (e) => { + this.modifiedEditor.delegateVerticalScrollbarMouseDown(e); + })); + this._containerDomElement.appendChild(this._overviewDomElement); + + this._createLeftHandSide(); + this._createRightHandSide(); + + this._beginUpdateDecorationsTimeout = -1; + this._currentlyChangingViewZones = false; + this._diffComputationToken = 0; + + this._originalEditorState = new VisualEditorState(); + this._modifiedEditorState = new VisualEditorState(); + + this._isVisible = true; + this._isHandlingScrollEvent = false; + + this._width = 0; + this._height = 0; + this._reviewHeight = 0; + + this._lineChanges = null; + + const services = new ServiceCollection(); + services.set(IContextKeyService, this._contextKeyService); + + const scopedInstantiationService = instantiationService.createChild(services); + + this._createLeftHandSideEditor(options, scopedInstantiationService); + this._createRightHandSideEditor(options, scopedInstantiationService); + + this._reviewPane = new DiffReview(this); + this._containerDomElement.appendChild(this._reviewPane.domNode.domNode); + this._containerDomElement.appendChild(this._reviewPane.shadow.domNode); + this._containerDomElement.appendChild(this._reviewPane.actionBarContainer.domNode); + + if (options.automaticLayout) { + this._measureDomElementToken = window.setInterval(() => this._measureDomElement(false), 100); + } + + // enableSplitViewResizing + this._enableSplitViewResizing = true; + if (typeof options.enableSplitViewResizing !== 'undefined') { + this._enableSplitViewResizing = options.enableSplitViewResizing; + } + + if (this._renderSideBySide) { + this._setStrategy(new DiffEdtorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing)); + } else { + this._setStrategy(new DiffEdtorWidgetInline(this._createDataSource(), this._enableSplitViewResizing)); + } + + this._codeEditorService.addDiffEditor(this); + + this._register(themeService.onThemeChange(t => { + if (this._strategy && this._strategy.applyColors(t)) { + this._updateDecorationsRunner.schedule(); + } + this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide); + })); + } + + public get ignoreTrimWhitespace(): boolean { + return this._ignoreTrimWhitespace; + } + + public get renderSideBySide(): boolean { + return this._renderSideBySide; + } + + public get renderIndicators(): boolean { + return this._renderIndicators; + } + + public hasWidgetFocus(): boolean { + return dom.isAncestor(document.activeElement, this._domElement); + } + + public diffReviewNext(): void { + this._reviewPane.next(); + } + + public diffReviewPrev(): void { + this._reviewPane.prev(); + } + + private static _getClassName(theme: ITheme, renderSideBySide: boolean): string { + let result = 'monaco-diff-editor monaco-editor-background '; + if (renderSideBySide) { + result += 'side-by-side '; + } + result += getThemeTypeSelector(theme.type); + return result; + } + + private _recreateOverviewRulers(): void { + if (this._originalOverviewRuler) { + this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); + this._originalOverviewRuler.dispose(); + } + this._originalOverviewRuler = this.originalEditor.createOverviewRuler('original diffOverviewRuler', 4, Number.MAX_VALUE); + this._overviewDomElement.appendChild(this._originalOverviewRuler.getDomNode()); + + if (this._modifiedOverviewRuler) { + this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); + this._modifiedOverviewRuler.dispose(); + } + this._modifiedOverviewRuler = this.modifiedEditor.createOverviewRuler('modified diffOverviewRuler', 4, Number.MAX_VALUE); + this._overviewDomElement.appendChild(this._modifiedOverviewRuler.getDomNode()); + + this._layoutOverviewRulers(); + } + + private _createLeftHandSide(): void { + this._originalDomNode = document.createElement('div'); + this._originalDomNode.className = 'editor original'; + this._originalDomNode.style.position = 'absolute'; + this._originalDomNode.style.height = '100%'; + this._containerDomElement.appendChild(this._originalDomNode); + } + + private _createRightHandSide(): void { + this._modifiedDomNode = document.createElement('div'); + this._modifiedDomNode.className = 'editor modified'; + this._modifiedDomNode.style.position = 'absolute'; + this._modifiedDomNode.style.height = '100%'; + this._containerDomElement.appendChild(this._modifiedDomNode); + } + + private _createLeftHandSideEditor(options: editorOptions.IDiffEditorOptions, instantiationService: IInstantiationService): void { + this.originalEditor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable)); + + this._register(this.originalEditor.onDidScrollChange((e) => { + if (this._isHandlingScrollEvent) { + return; + } + if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) { + return; + } + this._isHandlingScrollEvent = true; + this.modifiedEditor.setScrollPosition({ + scrollLeft: e.scrollLeft, + scrollTop: e.scrollTop + }); + this._isHandlingScrollEvent = false; + + this._layoutOverviewViewport(); + })); + + this._register(this.originalEditor.onDidChangeViewZones(() => { + this._onViewZonesChanged(); + })); + + this._register(this.originalEditor.onDidChangeModelContent(() => { + if (this._isVisible) { + this._beginUpdateDecorationsSoon(); + } + })); + } + + private _createRightHandSideEditor(options: editorOptions.IDiffEditorOptions, instantiationService: IInstantiationService): void { + this.modifiedEditor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options)); + + this._register(this.modifiedEditor.onDidScrollChange((e) => { + if (this._isHandlingScrollEvent) { + return; + } + if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) { + return; + } + this._isHandlingScrollEvent = true; + this.originalEditor.setScrollPosition({ + scrollLeft: e.scrollLeft, + scrollTop: e.scrollTop + }); + this._isHandlingScrollEvent = false; + + this._layoutOverviewViewport(); + })); + + this._register(this.modifiedEditor.onDidChangeViewZones(() => { + this._onViewZonesChanged(); + })); + + this._register(this.modifiedEditor.onDidChangeConfiguration((e) => { + if (e.fontInfo && this.modifiedEditor.getModel()) { + this._onViewZonesChanged(); + } + })); + + this._register(this.modifiedEditor.onDidChangeModelContent(() => { + if (this._isVisible) { + this._beginUpdateDecorationsSoon(); + } + })); + } + + protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: editorOptions.IEditorOptions): CodeEditor { + return instantiationService.createInstance(CodeEditor, container, options); + } + + public destroy(): void { + this.dispose(); + } + + public dispose(): void { + this._codeEditorService.removeDiffEditor(this); + + window.clearInterval(this._measureDomElementToken); + + this._cleanViewZonesAndDecorations(); + + this._originalOverviewRuler.dispose(); + this._modifiedOverviewRuler.dispose(); + + this.originalEditor.dispose(); + this.modifiedEditor.dispose(); + + this._strategy.dispose(); + + this._reviewPane.dispose(); + + this._onDidDispose.fire(); + + super.dispose(); + } + + //------------ begin IDiffEditor methods + + public getId(): string { + return this.getEditorType() + ':' + this.id; + } + + public getEditorType(): string { + return editorCommon.EditorType.IDiffEditor; + } + + public getLineChanges(): editorCommon.ILineChange[] { + return this._lineChanges; + } + + public getOriginalEditor(): editorBrowser.ICodeEditor { + return this.originalEditor; + } + + public getModifiedEditor(): editorBrowser.ICodeEditor { + return this.modifiedEditor; + } + + public updateOptions(newOptions: editorOptions.IDiffEditorOptions): void { + + // Handle side by side + let renderSideBySideChanged = false; + if (typeof newOptions.renderSideBySide !== 'undefined') { + if (this._renderSideBySide !== newOptions.renderSideBySide) { + this._renderSideBySide = newOptions.renderSideBySide; + renderSideBySideChanged = true; + } + } + + let beginUpdateDecorations = false; + + if (typeof newOptions.ignoreTrimWhitespace !== 'undefined') { + if (this._ignoreTrimWhitespace !== newOptions.ignoreTrimWhitespace) { + this._ignoreTrimWhitespace = newOptions.ignoreTrimWhitespace; + // Begin comparing + beginUpdateDecorations = true; + } + } + + if (typeof newOptions.renderIndicators !== 'undefined') { + if (this._renderIndicators !== newOptions.renderIndicators) { + this._renderIndicators = newOptions.renderIndicators; + beginUpdateDecorations = true; + } + } + + if (beginUpdateDecorations) { + this._beginUpdateDecorations(); + } + + if (typeof newOptions.originalEditable !== 'undefined') { + this._originalIsEditable = Boolean(newOptions.originalEditable); + } + + this.modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(newOptions)); + this.originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(newOptions, this._originalIsEditable)); + + // enableSplitViewResizing + if (typeof newOptions.enableSplitViewResizing !== 'undefined') { + this._enableSplitViewResizing = newOptions.enableSplitViewResizing; + } + this._strategy.setEnableSplitViewResizing(this._enableSplitViewResizing); + + // renderSideBySide + if (renderSideBySideChanged) { + if (this._renderSideBySide) { + this._setStrategy(new DiffEdtorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing, )); + } else { + this._setStrategy(new DiffEdtorWidgetInline(this._createDataSource(), this._enableSplitViewResizing)); + } + // Update class name + this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide); + } + } + + public getValue(options: { preserveBOM: boolean; lineEnding: string; } = null): string { + return this.modifiedEditor.getValue(options); + } + + public getModel(): editorCommon.IDiffEditorModel { + return { + original: this.originalEditor.getModel(), + modified: this.modifiedEditor.getModel() + }; + } + + public setModel(model: editorCommon.IDiffEditorModel): void { + // Guard us against partial null model + if (model && (!model.original || !model.modified)) { + throw new Error(!model.original ? 'DiffEditorWidget.setModel: Original model is null' : 'DiffEditorWidget.setModel: Modified model is null'); + } + + // Remove all view zones & decorations + this._cleanViewZonesAndDecorations(); + + // Update code editor models + this.originalEditor.setModel(model ? model.original : null); + this.modifiedEditor.setModel(model ? model.modified : null); + this._updateDecorationsRunner.cancel(); + + if (model) { + this.originalEditor.setScrollTop(0); + this.modifiedEditor.setScrollTop(0); + } + + // Disable any diff computations that will come in + this._lineChanges = null; + this._diffComputationToken++; + + if (model) { + this._recreateOverviewRulers(); + + // Begin comparing + this._beginUpdateDecorations(); + } else { + this._lineChanges = null; + } + + this._layoutOverviewViewport(); + } + + public getDomNode(): HTMLElement { + return this._domElement; + } + + public getVisibleColumnFromPosition(position: IPosition): number { + return this.modifiedEditor.getVisibleColumnFromPosition(position); + } + + public getPosition(): Position { + return this.modifiedEditor.getPosition(); + } + + public setPosition(position: IPosition): void { + this.modifiedEditor.setPosition(position); + } + + public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealLine(lineNumber, scrollType); + } + + public revealLineInCenter(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealLineInCenter(lineNumber, scrollType); + } + + public revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType); + } + + public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealPosition(position, scrollType); + } + + public revealPositionInCenter(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealPositionInCenter(position, scrollType); + } + + public revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType); + } + + public getSelection(): Selection { + return this.modifiedEditor.getSelection(); + } + + public getSelections(): Selection[] { + return this.modifiedEditor.getSelections(); + } + + public setSelection(range: IRange): void; + public setSelection(editorRange: Range): void; + public setSelection(selection: ISelection): void; + public setSelection(editorSelection: Selection): void; + public setSelection(something: any): void { + this.modifiedEditor.setSelection(something); + } + + public setSelections(ranges: ISelection[]): void { + this.modifiedEditor.setSelections(ranges); + } + + public revealLines(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealLines(startLineNumber, endLineNumber, scrollType); + } + + public revealLinesInCenter(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealLinesInCenter(startLineNumber, endLineNumber, scrollType); + } + + public revealLinesInCenterIfOutsideViewport(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType); + } + + public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void { + this.modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal); + } + + public revealRangeInCenter(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealRangeInCenter(range, scrollType); + } + + public revealRangeInCenterIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType); + } + + public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealRangeAtTop(range, scrollType); + } + + public getActions(): editorCommon.IEditorAction[] { + return this.modifiedEditor.getActions(); + } + + public getSupportedActions(): editorCommon.IEditorAction[] { + return this.modifiedEditor.getSupportedActions(); + } + + public getAction(id: string): editorCommon.IEditorAction { + return this.modifiedEditor.getAction(id); + } + + public saveViewState(): editorCommon.IDiffEditorViewState { + let originalViewState = this.originalEditor.saveViewState(); + let modifiedViewState = this.modifiedEditor.saveViewState(); + return { + original: originalViewState, + modified: modifiedViewState + }; + } + + public restoreViewState(s: editorCommon.IDiffEditorViewState): void { + if (s.original && s.original) { + let diffEditorState = s; + this.originalEditor.restoreViewState(diffEditorState.original); + this.modifiedEditor.restoreViewState(diffEditorState.modified); + } + } + + public layout(dimension?: editorCommon.IDimension): void { + this._measureDomElement(false, dimension); + } + + public focus(): void { + this.modifiedEditor.focus(); + } + + public isFocused(): boolean { + return this.originalEditor.isFocused() || this.modifiedEditor.isFocused(); + } + + public onVisible(): void { + this._isVisible = true; + this.originalEditor.onVisible(); + this.modifiedEditor.onVisible(); + // Begin comparing + this._beginUpdateDecorations(); + } + + public onHide(): void { + this._isVisible = false; + this.originalEditor.onHide(); + this.modifiedEditor.onHide(); + // Remove all view zones & decorations + this._cleanViewZonesAndDecorations(); + } + + public trigger(source: string, handlerId: string, payload: any): void { + this.modifiedEditor.trigger(source, handlerId, payload); + } + + public changeDecorations(callback: (changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => any): any { + return this.modifiedEditor.changeDecorations(callback); + } + + //------------ end IDiffEditor methods + + + + //------------ begin layouting methods + + private _measureDomElement(forceDoLayoutCall: boolean, dimensions?: editorCommon.IDimension): void { + dimensions = dimensions || { + width: this._containerDomElement.clientWidth, + height: this._containerDomElement.clientHeight + }; + + if (dimensions.width <= 0) { + this._width = 0; + this._height = 0; + this._reviewHeight = 0; + return; + } + + if (!forceDoLayoutCall && dimensions.width === this._width && dimensions.height === this._height) { + // Nothing has changed + return; + } + + this._width = dimensions.width; + this._height = dimensions.height; + this._reviewHeight = this._reviewPane.isVisible() ? this._height : 0; + + this._doLayout(); + } + + private _layoutOverviewRulers(): void { + let freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH; + let layoutInfo = this.modifiedEditor.getLayoutInfo(); + if (layoutInfo) { + this._originalOverviewRuler.setLayout({ + top: 0, + width: DiffEditorWidget.ONE_OVERVIEW_WIDTH, + right: freeSpace + DiffEditorWidget.ONE_OVERVIEW_WIDTH, + height: (this._height - this._reviewHeight) + }); + this._modifiedOverviewRuler.setLayout({ + top: 0, + right: 0, + width: DiffEditorWidget.ONE_OVERVIEW_WIDTH, + height: (this._height - this._reviewHeight) + }); + } + } + + //------------ end layouting methods + + private _onViewZonesChanged(): void { + if (this._currentlyChangingViewZones) { + return; + } + this._updateDecorationsRunner.schedule(); + } + + private _beginUpdateDecorationsSoon(): void { + // Clear previous timeout if necessary + if (this._beginUpdateDecorationsTimeout !== -1) { + window.clearTimeout(this._beginUpdateDecorationsTimeout); + this._beginUpdateDecorationsTimeout = -1; + } + this._beginUpdateDecorationsTimeout = window.setTimeout(() => this._beginUpdateDecorations(), DiffEditorWidget.UPDATE_DIFF_DECORATIONS_DELAY); + } + + private _lastOriginalWarning: URI = null; + private _lastModifiedWarning: URI = null; + + private static _equals(a: URI, b: URI): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return (a.toString() === b.toString()); + } + + private _beginUpdateDecorations(): void { + this._beginUpdateDecorationsTimeout = -1; + const currentOriginalModel = this.originalEditor.getModel(); + const currentModifiedModel = this.modifiedEditor.getModel(); + if (!currentOriginalModel || !currentModifiedModel) { + return; + } + + // Prevent old diff requests to come if a new request has been initiated + // The best method would be to call cancel on the Promise, but this is not + // yet supported, so using tokens for now. + this._diffComputationToken++; + let currentToken = this._diffComputationToken; + + if (!this._editorWorkerService.canComputeDiff(currentOriginalModel.uri, currentModifiedModel.uri)) { + if ( + !DiffEditorWidget._equals(currentOriginalModel.uri, this._lastOriginalWarning) + || !DiffEditorWidget._equals(currentModifiedModel.uri, this._lastModifiedWarning) + ) { + this._lastOriginalWarning = currentOriginalModel.uri; + this._lastModifiedWarning = currentModifiedModel.uri; + this._messageService.show(Severity.Warning, nls.localize("diff.tooLarge", "Cannot compare files because one file is too large.")); + } + return; + } + + this._editorWorkerService.computeDiff(currentOriginalModel.uri, currentModifiedModel.uri, this._ignoreTrimWhitespace).then((result) => { + if (currentToken === this._diffComputationToken + && currentOriginalModel === this.originalEditor.getModel() + && currentModifiedModel === this.modifiedEditor.getModel() + ) { + this._lineChanges = result; + this._updateDecorationsRunner.schedule(); + this._onDidUpdateDiff.fire(); + } + }, (error) => { + if (currentToken === this._diffComputationToken + && currentOriginalModel === this.originalEditor.getModel() + && currentModifiedModel === this.modifiedEditor.getModel() + ) { + this._lineChanges = null; + this._updateDecorationsRunner.schedule(); + } + }); + } + + private _cleanViewZonesAndDecorations(): void { + this._originalEditorState.clean(this.originalEditor); + this._modifiedEditorState.clean(this.modifiedEditor); + } + + private _updateDecorations(): void { + if (!this.originalEditor.getModel() || !this.modifiedEditor.getModel()) { + return; + } + let lineChanges = this._lineChanges || []; + + let foreignOriginal = this._originalEditorState.getForeignViewZones(this.originalEditor.getWhitespaces()); + let foreignModified = this._modifiedEditorState.getForeignViewZones(this.modifiedEditor.getWhitespaces()); + + let diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._ignoreTrimWhitespace, this._renderIndicators, foreignOriginal, foreignModified, this.originalEditor, this.modifiedEditor); + + try { + this._currentlyChangingViewZones = true; + this._originalEditorState.apply(this.originalEditor, this._originalOverviewRuler, diffDecorations.original); + this._modifiedEditorState.apply(this.modifiedEditor, this._modifiedOverviewRuler, diffDecorations.modified); + } finally { + this._currentlyChangingViewZones = false; + } + } + + private _adjustOptionsForSubEditor(options: editorOptions.IDiffEditorOptions): editorOptions.IDiffEditorOptions { + let clonedOptions: editorOptions.IDiffEditorOptions = objects.clone(options || {}); + clonedOptions.inDiffEditor = true; + clonedOptions.wordWrap = 'off'; + clonedOptions.wordWrapMinified = false; + clonedOptions.automaticLayout = false; + clonedOptions.scrollbar = clonedOptions.scrollbar || {}; + clonedOptions.scrollbar.vertical = 'visible'; + clonedOptions.folding = false; + clonedOptions.codeLens = false; + clonedOptions.fixedOverflowWidgets = true; + clonedOptions.lineDecorationsWidth = '2ch'; + if (!clonedOptions.minimap) { + clonedOptions.minimap = {}; + } + clonedOptions.minimap.enabled = false; + return clonedOptions; + } + + private _adjustOptionsForLeftHandSide(options: editorOptions.IDiffEditorOptions, isEditable: boolean): editorOptions.IEditorOptions { + let result = this._adjustOptionsForSubEditor(options); + result.readOnly = !isEditable; + result.overviewRulerLanes = 1; + result.extraEditorClassName = 'original-in-monaco-diff-editor'; + return result; + } + + private _adjustOptionsForRightHandSide(options: editorOptions.IDiffEditorOptions): editorOptions.IEditorOptions { + let result = this._adjustOptionsForSubEditor(options); + result.revealHorizontalRightPadding = editorOptions.EDITOR_DEFAULTS.viewInfo.revealHorizontalRightPadding + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; + result.scrollbar.verticalHasArrows = false; + result.extraEditorClassName = 'modified-in-monaco-diff-editor'; + return result; + } + + public doLayout(): void { + this._measureDomElement(true); + } + + private _doLayout(): void { + let splitPoint = this._strategy.layout(); + + this._originalDomNode.style.width = splitPoint + 'px'; + this._originalDomNode.style.left = '0px'; + + this._modifiedDomNode.style.width = (this._width - splitPoint) + 'px'; + this._modifiedDomNode.style.left = splitPoint + 'px'; + + this._overviewDomElement.style.top = '0px'; + this._overviewDomElement.style.height = (this._height - this._reviewHeight) + 'px'; + this._overviewDomElement.style.width = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH + 'px'; + this._overviewDomElement.style.left = (this._width - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px'; + this._overviewViewportDomElement.setWidth(DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH); + this._overviewViewportDomElement.setHeight(30); + + this.originalEditor.layout({ width: splitPoint, height: (this._height - this._reviewHeight) }); + this.modifiedEditor.layout({ width: this._width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (this._height - this._reviewHeight) }); + + if (this._originalOverviewRuler || this._modifiedOverviewRuler) { + this._layoutOverviewRulers(); + } + + this._reviewPane.layout(this._height - this._reviewHeight, this._width, this._reviewHeight); + + this._layoutOverviewViewport(); + } + + private _layoutOverviewViewport(): void { + let layout = this._computeOverviewViewport(); + if (!layout) { + this._overviewViewportDomElement.setTop(0); + this._overviewViewportDomElement.setHeight(0); + } else { + this._overviewViewportDomElement.setTop(layout.top); + this._overviewViewportDomElement.setHeight(layout.height); + } + } + + private _computeOverviewViewport(): { height: number; top: number; } { + let layoutInfo = this.modifiedEditor.getLayoutInfo(); + if (!layoutInfo) { + return null; + } + + let scrollTop = this.modifiedEditor.getScrollTop(); + let scrollHeight = this.modifiedEditor.getScrollHeight(); + + let computedAvailableSize = Math.max(0, layoutInfo.contentHeight); + let computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); + let computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; + + let computedSliderSize = Math.max(0, Math.floor(layoutInfo.contentHeight * computedRatio)); + let computedSliderPosition = Math.floor(scrollTop * computedRatio); + + return { + height: computedSliderSize, + top: computedSliderPosition + }; + } + + private _createDataSource(): IDataSource { + return { + getWidth: () => { + return this._width; + }, + + getHeight: () => { + return (this._height - this._reviewHeight); + }, + + getContainerDomNode: () => { + return this._containerDomElement; + }, + + relayoutEditors: () => { + this._doLayout(); + }, + + getOriginalEditor: () => { + return this.originalEditor; + }, + + getModifiedEditor: () => { + return this.modifiedEditor; + } + }; + } + + private _setStrategy(newStrategy: IDiffEditorWidgetStyle): void { + if (this._strategy) { + this._strategy.dispose(); + } + + this._strategy = newStrategy; + newStrategy.applyColors(this._themeService.getTheme()); + + if (this._lineChanges) { + this._updateDecorations(); + } + + // Just do a layout, the strategy might need it + this._measureDomElement(true); + } + + private _getLineChangeAtOrBeforeLineNumber(lineNumber: number, startLineNumberExtractor: (lineChange: editorCommon.ILineChange) => number): editorCommon.ILineChange { + if (this._lineChanges.length === 0 || lineNumber < startLineNumberExtractor(this._lineChanges[0])) { + // There are no changes or `lineNumber` is before the first change + return null; + } + + let min = 0, max = this._lineChanges.length - 1; + while (min < max) { + let mid = Math.floor((min + max) / 2); + let midStart = startLineNumberExtractor(this._lineChanges[mid]); + let midEnd = (mid + 1 <= max ? startLineNumberExtractor(this._lineChanges[mid + 1]) : Number.MAX_VALUE); + + if (lineNumber < midStart) { + max = mid - 1; + } else if (lineNumber >= midEnd) { + min = mid + 1; + } else { + // HIT! + min = mid; + max = mid; + } + } + return this._lineChanges[min]; + } + + private _getEquivalentLineForOriginalLineNumber(lineNumber: number): number { + let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber); + + if (!lineChange) { + return lineNumber; + } + + let originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); + let modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); + let lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); + let lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + + + let delta = lineNumber - originalEquivalentLineNumber; + + if (delta <= lineChangeOriginalLength) { + return modifiedEquivalentLineNumber + Math.min(delta, lineChangeModifiedLength); + } + + return modifiedEquivalentLineNumber + lineChangeModifiedLength - lineChangeOriginalLength + delta; + } + + private _getEquivalentLineForModifiedLineNumber(lineNumber: number): number { + let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber); + + if (!lineChange) { + return lineNumber; + } + + let originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); + let modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); + let lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); + let lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + + + let delta = lineNumber - modifiedEquivalentLineNumber; + + if (delta <= lineChangeModifiedLength) { + return originalEquivalentLineNumber + Math.min(delta, lineChangeOriginalLength); + } + + return originalEquivalentLineNumber + lineChangeOriginalLength - lineChangeModifiedLength + delta; + } + + public getDiffLineInformationForOriginal(lineNumber: number): editorCommon.IDiffLineInformation { + if (!this._lineChanges) { + // Cannot answer that which I don't know + return null; + } + return { + equivalentLineNumber: this._getEquivalentLineForOriginalLineNumber(lineNumber) + }; + } + + public getDiffLineInformationForModified(lineNumber: number): editorCommon.IDiffLineInformation { + if (!this._lineChanges) { + // Cannot answer that which I don't know + return null; + } + return { + equivalentLineNumber: this._getEquivalentLineForModifiedLineNumber(lineNumber) + }; + } +} + +interface IDataSource { + getWidth(): number; + getHeight(): number; + getContainerDomNode(): HTMLElement; + relayoutEditors(): void; + + getOriginalEditor(): editorBrowser.ICodeEditor; + getModifiedEditor(): editorBrowser.ICodeEditor; +} + +class DiffEditorWidgetStyle extends Disposable { + + _dataSource: IDataSource; + _insertColor: Color; + _removeColor: Color; + + constructor(dataSource: IDataSource) { + super(); + this._dataSource = dataSource; + } + + public applyColors(theme: ITheme): boolean { + let newInsertColor = (theme.getColor(diffInserted) || defaultInsertColor).transparent(2); + let newRemoveColor = (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2); + let hasChanges = !newInsertColor.equals(this._insertColor) || !newRemoveColor.equals(this._removeColor); + this._insertColor = newInsertColor; + this._removeColor = newRemoveColor; + return hasChanges; + } + + public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones { + // Get view zones + modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => { + return a.afterLineNumber - b.afterLineNumber; + }); + originalWhitespaces = originalWhitespaces.sort((a, b) => { + return a.afterLineNumber - b.afterLineNumber; + }); + let zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, originalEditor, modifiedEditor, renderIndicators); + + // Get decorations & overview ruler zones + let originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor); + let modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor); + + return { + original: { + decorations: originalDecorations.decorations, + overviewZones: originalDecorations.overviewZones, + zones: zones.original + }, + modified: { + decorations: modifiedDecorations.decorations, + overviewZones: modifiedDecorations.overviewZones, + zones: zones.modified + } + }; + } + + _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones { + return null; + } + + _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + return null; + } + + _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + return null; + } +} + +interface IMyViewZone extends editorBrowser.IViewZone { + shouldNotShrink?: boolean; +} + +class ForeignViewZonesIterator { + + private _index: number; + private _source: IEditorWhitespace[]; + public current: IEditorWhitespace; + + constructor(source: IEditorWhitespace[]) { + this._source = source; + this._index = -1; + this.advance(); + } + + public advance(): void { + this._index++; + if (this._index < this._source.length) { + this.current = this._source[this._index]; + } else { + this.current = null; + } + } +} + +abstract class ViewZonesComputer { + + private lineChanges: editorCommon.ILineChange[]; + private originalForeignVZ: IEditorWhitespace[]; + private modifiedForeignVZ: IEditorWhitespace[]; + + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) { + this.lineChanges = lineChanges; + this.originalForeignVZ = originalForeignVZ; + this.modifiedForeignVZ = modifiedForeignVZ; + } + + public getViewZones(): IEditorsZones { + let result: IEditorsZones = { + original: [], + modified: [] + }; + + let lineChangeModifiedLength: number = 0; + let lineChangeOriginalLength: number = 0; + let originalEquivalentLineNumber: number = 0; + let modifiedEquivalentLineNumber: number = 0; + let originalEndEquivalentLineNumber: number = 0; + let modifiedEndEquivalentLineNumber: number = 0; + + let sortMyViewZones = (a: IMyViewZone, b: IMyViewZone) => { + return a.afterLineNumber - b.afterLineNumber; + }; + + let addAndCombineIfPossible = (destination: editorBrowser.IViewZone[], item: IMyViewZone) => { + if (item.domNode === null && destination.length > 0) { + let lastItem = destination[destination.length - 1]; + if (lastItem.afterLineNumber === item.afterLineNumber && lastItem.domNode === null) { + lastItem.heightInLines += item.heightInLines; + return; + } + } + destination.push(item); + }; + + let modifiedForeignVZ = new ForeignViewZonesIterator(this.modifiedForeignVZ); + let originalForeignVZ = new ForeignViewZonesIterator(this.originalForeignVZ); + + // In order to include foreign view zones after the last line change, the for loop will iterate once more after the end of the `lineChanges` array + for (let i = 0, length = this.lineChanges.length; i <= length; i++) { + let lineChange = (i < length ? this.lineChanges[i] : null); + + if (lineChange !== null) { + originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); + modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); + lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); + lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + originalEndEquivalentLineNumber = Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber); + modifiedEndEquivalentLineNumber = Math.max(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber); + } else { + // Increase to very large value to get the producing tests of foreign view zones running + originalEquivalentLineNumber += 10000000 + lineChangeOriginalLength; + modifiedEquivalentLineNumber += 10000000 + lineChangeModifiedLength; + originalEndEquivalentLineNumber = originalEquivalentLineNumber; + modifiedEndEquivalentLineNumber = modifiedEquivalentLineNumber; + } + + // Each step produces view zones, and after producing them, we try to cancel them out, to avoid empty-empty view zone cases + let stepOriginal: IMyViewZone[] = []; + let stepModified: IMyViewZone[] = []; + + // ---------------------------- PRODUCE VIEW ZONES + + // [PRODUCE] View zone(s) in original-side due to foreign view zone(s) in modified-side + while (modifiedForeignVZ.current && modifiedForeignVZ.current.afterLineNumber <= modifiedEndEquivalentLineNumber) { + let viewZoneLineNumber: number; + if (modifiedForeignVZ.current.afterLineNumber <= modifiedEquivalentLineNumber) { + viewZoneLineNumber = originalEquivalentLineNumber - modifiedEquivalentLineNumber + modifiedForeignVZ.current.afterLineNumber; + } else { + viewZoneLineNumber = originalEndEquivalentLineNumber; + } + stepOriginal.push({ + afterLineNumber: viewZoneLineNumber, + heightInLines: modifiedForeignVZ.current.heightInLines, + domNode: null + }); + modifiedForeignVZ.advance(); + } + + // [PRODUCE] View zone(s) in modified-side due to foreign view zone(s) in original-side + while (originalForeignVZ.current && originalForeignVZ.current.afterLineNumber <= originalEndEquivalentLineNumber) { + let viewZoneLineNumber: number; + if (originalForeignVZ.current.afterLineNumber <= originalEquivalentLineNumber) { + viewZoneLineNumber = modifiedEquivalentLineNumber - originalEquivalentLineNumber + originalForeignVZ.current.afterLineNumber; + } else { + viewZoneLineNumber = modifiedEndEquivalentLineNumber; + } + stepModified.push({ + afterLineNumber: viewZoneLineNumber, + heightInLines: originalForeignVZ.current.heightInLines, + domNode: null + }); + originalForeignVZ.advance(); + } + + if (lineChange !== null && isChangeOrInsert(lineChange)) { + let r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); + if (r) { + stepOriginal.push(r); + } + } + + if (lineChange !== null && isChangeOrDelete(lineChange)) { + let r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); + if (r) { + stepModified.push(r); + } + } + + // ---------------------------- END PRODUCE VIEW ZONES + + + // ---------------------------- EMIT MINIMAL VIEW ZONES + + // [CANCEL & EMIT] Try to cancel view zones out + let stepOriginalIndex = 0; + let stepModifiedIndex = 0; + + stepOriginal = stepOriginal.sort(sortMyViewZones); + stepModified = stepModified.sort(sortMyViewZones); + + while (stepOriginalIndex < stepOriginal.length && stepModifiedIndex < stepModified.length) { + let original = stepOriginal[stepOriginalIndex]; + let modified = stepModified[stepModifiedIndex]; + + let originalDelta = original.afterLineNumber - originalEquivalentLineNumber; + let modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber; + + if (originalDelta < modifiedDelta) { + addAndCombineIfPossible(result.original, original); + stepOriginalIndex++; + } else if (modifiedDelta < originalDelta) { + addAndCombineIfPossible(result.modified, modified); + stepModifiedIndex++; + } else if (original.shouldNotShrink) { + addAndCombineIfPossible(result.original, original); + stepOriginalIndex++; + } else if (modified.shouldNotShrink) { + addAndCombineIfPossible(result.modified, modified); + stepModifiedIndex++; + } else { + if (original.heightInLines >= modified.heightInLines) { + // modified view zone gets removed + original.heightInLines -= modified.heightInLines; + stepModifiedIndex++; + } else { + // original view zone gets removed + modified.heightInLines -= original.heightInLines; + stepOriginalIndex++; + } + } + } + + // [EMIT] Remaining original view zones + while (stepOriginalIndex < stepOriginal.length) { + addAndCombineIfPossible(result.original, stepOriginal[stepOriginalIndex]); + stepOriginalIndex++; + } + + // [EMIT] Remaining modified view zones + while (stepModifiedIndex < stepModified.length) { + addAndCombineIfPossible(result.modified, stepModified[stepModifiedIndex]); + stepModifiedIndex++; + } + + // ---------------------------- END EMIT MINIMAL VIEW ZONES + } + + let ensureDomNode = (z: IMyViewZone) => { + if (!z.domNode) { + z.domNode = createFakeLinesDiv(); + } + }; + + result.original.forEach(ensureDomNode); + result.modified.forEach(ensureDomNode); + + return result; + } + + protected abstract _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone; + + protected abstract _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone; +} + +function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) { + return { + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + options: options + }; +} + +const DECORATIONS = { + + charDelete: ModelDecorationOptions.register({ + className: 'char-delete' + }), + charDeleteWholeLine: ModelDecorationOptions.register({ + className: 'char-delete', + isWholeLine: true + }), + + charInsert: ModelDecorationOptions.register({ + className: 'char-insert' + }), + charInsertWholeLine: ModelDecorationOptions.register({ + className: 'char-insert', + isWholeLine: true + }), + + lineInsert: ModelDecorationOptions.register({ + className: 'line-insert', + marginClassName: 'line-insert', + isWholeLine: true + }), + lineInsertWithSign: ModelDecorationOptions.register({ + className: 'line-insert', + linesDecorationsClassName: 'insert-sign', + marginClassName: 'line-insert', + isWholeLine: true + }), + + lineDelete: ModelDecorationOptions.register({ + className: 'line-delete', + marginClassName: 'line-delete', + isWholeLine: true + }), + lineDeleteWithSign: ModelDecorationOptions.register({ + className: 'line-delete', + linesDecorationsClassName: 'delete-sign', + marginClassName: 'line-delete', + isWholeLine: true + + }), + lineDeleteMargin: ModelDecorationOptions.register({ + marginClassName: 'line-delete', + }) + +}; + +class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider { + + static MINIMUM_EDITOR_WIDTH = 100; + + private _disableSash: boolean; + private _sash: Sash; + private _sashRatio: number; + private _sashPosition: number; + private _startSashPosition: number; + + constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) { + super(dataSource); + + this._disableSash = (enableSplitViewResizing === false); + this._sashRatio = null; + this._sashPosition = null; + this._sash = this._register(new Sash(this._dataSource.getContainerDomNode(), this)); + + if (this._disableSash) { + this._sash.disable(); + } + + this._sash.addListener('start', () => this.onSashDragStart()); + this._sash.addListener('change', (e: ISashEvent) => this.onSashDrag(e)); + this._sash.addListener('end', () => this.onSashDragEnd()); + this._sash.addListener('reset', () => this.onSashReset()); + } + + public dispose(): void { + super.dispose(); + } + + public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void { + let newDisableSash = (enableSplitViewResizing === false); + if (this._disableSash !== newDisableSash) { + this._disableSash = newDisableSash; + + if (this._disableSash) { + this._sash.disable(); + } else { + this._sash.enable(); + } + } + } + + public layout(sashRatio: number = this._sashRatio): number { + let w = this._dataSource.getWidth(); + let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; + + let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth); + let midPoint = Math.floor(0.5 * contentWidth); + + sashPosition = this._disableSash ? midPoint : sashPosition || midPoint; + + if (contentWidth > DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH * 2) { + if (sashPosition < DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) { + sashPosition = DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH; + } + + if (sashPosition > contentWidth - DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) { + sashPosition = contentWidth - DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH; + } + } else { + sashPosition = midPoint; + } + + if (this._sashPosition !== sashPosition) { + this._sashPosition = sashPosition; + this._sash.layout(); + } + + return this._sashPosition; + } + + private onSashDragStart(): void { + this._startSashPosition = this._sashPosition; + } + + private onSashDrag(e: ISashEvent): void { + let w = this._dataSource.getWidth(); + let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; + let sashPosition = this.layout((this._startSashPosition + (e.currentX - e.startX)) / contentWidth); + + this._sashRatio = sashPosition / contentWidth; + + this._dataSource.relayoutEditors(); + } + + private onSashDragEnd(): void { + this._sash.layout(); + } + + private onSashReset(): void { + this._sashRatio = 0.5; + this._dataSource.relayoutEditors(); + this._sash.layout(); + } + + public getVerticalSashTop(sash: Sash): number { + return 0; + } + + public getVerticalSashLeft(sash: Sash): number { + return this._sashPosition; + } + + public getVerticalSashHeight(sash: Sash): number { + return this._dataSource.getHeight(); + } + + _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones { + let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ); + return c.getViewZones(); + } + + _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + + let result: IEditorDiffDecorations = { + decorations: [], + overviewZones: [] + }; + + let originalModel = originalEditor.getModel(); + + for (let i = 0, length = lineChanges.length; i < length; i++) { + let lineChange = lineChanges[i]; + + if (isChangeOrDelete(lineChange)) { + result.decorations.push({ + range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE), + options: (renderIndicators ? DECORATIONS.lineDeleteWithSign : DECORATIONS.lineDelete) + }); + if (!isChangeOrInsert(lineChange) || !lineChange.charChanges) { + result.decorations.push(createDecoration(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE, DECORATIONS.charDeleteWholeLine)); + } + + let color = this._removeColor.toString(); + + result.overviewZones.push(new OverviewRulerZone( + lineChange.originalStartLineNumber, + lineChange.originalEndLineNumber, + editorCommon.OverviewRulerLane.Full, + 0, + color, + color, + color + )); + + if (lineChange.charChanges) { + for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { + let charChange = lineChange.charChanges[j]; + if (isChangeOrDelete(charChange)) { + if (ignoreTrimWhitespace) { + for (let lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) { + let startColumn: number; + let endColumn: number; + if (lineNumber === charChange.originalStartLineNumber) { + startColumn = charChange.originalStartColumn; + } else { + startColumn = originalModel.getLineFirstNonWhitespaceColumn(lineNumber); + } + if (lineNumber === charChange.originalEndLineNumber) { + endColumn = charChange.originalEndColumn; + } else { + endColumn = originalModel.getLineLastNonWhitespaceColumn(lineNumber); + } + result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charDelete)); + } + } else { + result.decorations.push(createDecoration(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn, DECORATIONS.charDelete)); + } + } + } + } + } + } + + return result; + } + + _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + + let result: IEditorDiffDecorations = { + decorations: [], + overviewZones: [] + }; + + let modifiedModel = modifiedEditor.getModel(); + + for (let i = 0, length = lineChanges.length; i < length; i++) { + let lineChange = lineChanges[i]; + + if (isChangeOrInsert(lineChange)) { + + result.decorations.push({ + range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE), + options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert) + }); + if (!isChangeOrDelete(lineChange) || !lineChange.charChanges) { + result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE, DECORATIONS.charInsertWholeLine)); + } + let color = this._insertColor.toString(); + result.overviewZones.push(new OverviewRulerZone( + lineChange.modifiedStartLineNumber, + lineChange.modifiedEndLineNumber, + editorCommon.OverviewRulerLane.Full, + 0, + color, + color, + color + )); + + if (lineChange.charChanges) { + for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { + let charChange = lineChange.charChanges[j]; + if (isChangeOrInsert(charChange)) { + if (ignoreTrimWhitespace) { + for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { + let startColumn: number; + let endColumn: number; + if (lineNumber === charChange.modifiedStartLineNumber) { + startColumn = charChange.modifiedStartColumn; + } else { + startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber); + } + if (lineNumber === charChange.modifiedEndLineNumber) { + endColumn = charChange.modifiedEndColumn; + } else { + endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber); + } + result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert)); + } + } else { + result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert)); + } + } + } + } + + } + } + return result; + } +} + +class SideBySideViewZonesComputer extends ViewZonesComputer { + + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) { + super(lineChanges, originalForeignVZ, modifiedForeignVZ); + } + + _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone { + if (lineChangeModifiedLength > lineChangeOriginalLength) { + return { + afterLineNumber: Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber), + heightInLines: (lineChangeModifiedLength - lineChangeOriginalLength), + domNode: null + }; + } + return null; + } + + _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone { + if (lineChangeOriginalLength > lineChangeModifiedLength) { + return { + afterLineNumber: Math.max(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber), + heightInLines: (lineChangeOriginalLength - lineChangeModifiedLength), + domNode: null + }; + } + return null; + } +} + +class DiffEdtorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle { + + private decorationsLeft: number; + + constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) { + super(dataSource); + + this.decorationsLeft = dataSource.getOriginalEditor().getLayoutInfo().decorationsLeft; + + this._register(dataSource.getOriginalEditor().onDidLayoutChange((layoutInfo: editorOptions.EditorLayoutInfo) => { + if (this.decorationsLeft !== layoutInfo.decorationsLeft) { + this.decorationsLeft = layoutInfo.decorationsLeft; + dataSource.relayoutEditors(); + } + })); + } + + public dispose(): void { + super.dispose(); + } + + public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void { + // Nothing to do.. + } + + _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones { + let computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators); + return computer.getViewZones(); + } + + _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + let result: IEditorDiffDecorations = { + decorations: [], + overviewZones: [] + }; + + for (let i = 0, length = lineChanges.length; i < length; i++) { + let lineChange = lineChanges[i]; + + // Add overview zones in the overview ruler + if (isChangeOrDelete(lineChange)) { + result.decorations.push({ + range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE), + options: DECORATIONS.lineDeleteMargin + }); + + let color = this._removeColor.toString(); + result.overviewZones.push(new OverviewRulerZone( + lineChange.originalStartLineNumber, + lineChange.originalEndLineNumber, + editorCommon.OverviewRulerLane.Full, + 0, + color, + color, + color + )); + } + } + + return result; + } + + _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + + let result: IEditorDiffDecorations = { + decorations: [], + overviewZones: [] + }; + + let modifiedModel = modifiedEditor.getModel(); + + for (let i = 0, length = lineChanges.length; i < length; i++) { + let lineChange = lineChanges[i]; + + // Add decorations & overview zones + if (isChangeOrInsert(lineChange)) { + result.decorations.push({ + range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE), + options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert) + }); + + let color = this._insertColor.toString(); + result.overviewZones.push(new OverviewRulerZone( + lineChange.modifiedStartLineNumber, + lineChange.modifiedEndLineNumber, + editorCommon.OverviewRulerLane.Full, + 0, + color, + color, + color + )); + + if (lineChange.charChanges) { + for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { + let charChange = lineChange.charChanges[j]; + if (isChangeOrInsert(charChange)) { + if (ignoreTrimWhitespace) { + for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { + let startColumn: number; + let endColumn: number; + if (lineNumber === charChange.modifiedStartLineNumber) { + startColumn = charChange.modifiedStartColumn; + } else { + startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber); + } + if (lineNumber === charChange.modifiedEndLineNumber) { + endColumn = charChange.modifiedEndColumn; + } else { + endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber); + } + result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert)); + } + } else { + result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert)); + } + } + } + } else { + result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE, DECORATIONS.charInsertWholeLine)); + } + } + } + + return result; + } + + public layout(): number { + // An editor should not be smaller than 5px + return Math.max(5, this.decorationsLeft); + } + +} + +class InlineViewZonesComputer extends ViewZonesComputer { + + private originalModel: editorCommon.IModel; + private modifiedEditorConfiguration: editorOptions.InternalEditorOptions; + private modifiedEditorTabSize: number; + private renderIndicators: boolean; + + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) { + super(lineChanges, originalForeignVZ, modifiedForeignVZ); + this.originalModel = originalEditor.getModel(); + this.modifiedEditorConfiguration = modifiedEditor.getConfiguration(); + this.modifiedEditorTabSize = modifiedEditor.getModel().getOptions().tabSize; + this.renderIndicators = renderIndicators; + } + + _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone { + let marginDomNode = document.createElement('div'); + marginDomNode.className = 'inline-added-margin-view-zone'; + Configuration.applyFontInfoSlow(marginDomNode, this.modifiedEditorConfiguration.fontInfo); + + return { + afterLineNumber: Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber), + heightInLines: lineChangeModifiedLength, + domNode: document.createElement('div'), + marginDomNode: marginDomNode + }; + } + + _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone { + let decorations: InlineDecoration[] = []; + if (lineChange.charChanges) { + for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { + let charChange = lineChange.charChanges[j]; + if (isChangeOrDelete(charChange)) { + decorations.push(new InlineDecoration( + new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), + 'char-delete', + false + )); + } + } + } + + let sb = createStringBuilder(10000); + let marginHTML: string[] = []; + let lineDecorationsWidth = this.modifiedEditorConfiguration.layoutInfo.decorationsWidth; + let lineHeight = this.modifiedEditorConfiguration.lineHeight; + for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { + this.renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorConfiguration, this.modifiedEditorTabSize, lineNumber, decorations, sb); + + if (this.renderIndicators) { + let index = lineNumber - lineChange.originalStartLineNumber; + marginHTML = marginHTML.concat([ + `
` + ]); + } + } + + let domNode = document.createElement('div'); + domNode.className = 'view-lines line-delete'; + domNode.innerHTML = sb.build(); + Configuration.applyFontInfoSlow(domNode, this.modifiedEditorConfiguration.fontInfo); + + let marginDomNode = document.createElement('div'); + marginDomNode.className = 'inline-deleted-margin-view-zone'; + marginDomNode.innerHTML = marginHTML.join(''); + Configuration.applyFontInfoSlow(marginDomNode, this.modifiedEditorConfiguration.fontInfo); + + return { + shouldNotShrink: true, + afterLineNumber: (lineChange.modifiedEndLineNumber === 0 ? lineChange.modifiedStartLineNumber : lineChange.modifiedStartLineNumber - 1), + heightInLines: lineChangeOriginalLength, + domNode: domNode, + marginDomNode: marginDomNode + }; + } + + private renderOriginalLine(count: number, originalModel: editorCommon.IModel, config: editorOptions.InternalEditorOptions, tabSize: number, lineNumber: number, decorations: InlineDecoration[], sb: IStringBuilder): void { + let lineContent = originalModel.getLineContent(lineNumber); + + let actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1); + + const defaultMetadata = ( + (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) + | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) + | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) + ) >>> 0; + + sb.appendASCIIString('
'); + + renderViewLine(new RenderLineInput( + (config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations), + lineContent, + originalModel.mightContainRTL(), + 0, + [new ViewLineToken(lineContent.length, defaultMetadata)], + actualDecorations, + tabSize, + config.fontInfo.spaceWidth, + config.viewInfo.stopRenderingLineAfter, + config.viewInfo.renderWhitespace, + config.viewInfo.renderControlCharacters, + config.viewInfo.fontLigatures + ), sb); + + sb.appendASCIIString('
'); + } +} + +function isChangeOrInsert(lineChange: editorCommon.IChange): boolean { + return lineChange.modifiedEndLineNumber > 0; +} + +function isChangeOrDelete(lineChange: editorCommon.IChange): boolean { + return lineChange.originalEndLineNumber > 0; +} + +function createFakeLinesDiv(): HTMLElement { + let r = document.createElement('div'); + r.className = 'diagonal-fill'; + return r; +} + +registerThemingParticipant((theme, collector) => { + let added = theme.getColor(diffInserted); + if (added) { + collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { background-color: ${added}; }`); + collector.addRule(`.monaco-diff-editor .line-insert, .monaco-diff-editor .char-insert { background-color: ${added}; }`); + collector.addRule(`.monaco-editor .inline-added-margin-view-zone { background-color: ${added}; }`); + } + let removed = theme.getColor(diffRemoved); + if (removed) { + collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { background-color: ${removed}; }`); + collector.addRule(`.monaco-diff-editor .line-delete, .monaco-diff-editor .char-delete { background-color: ${removed}; }`); + collector.addRule(`.monaco-editor .inline-deleted-margin-view-zone { background-color: ${removed}; }`); + } + let addedOutline = theme.getColor(diffInsertedOutline); + if (addedOutline) { + collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { border: 1px dashed ${addedOutline}; }`); + } + let removedOutline = theme.getColor(diffRemovedOutline); + if (removedOutline) { + collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { border: 1px dashed ${removedOutline}; }`); + } + let shadow = theme.getColor(scrollbarShadow); + if (shadow) { + collector.addRule(`.monaco-diff-editor.side-by-side .editor.modified { box-shadow: -6px 0 5px -5px ${shadow}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/browser/widget/diffNavigator.ts b/src/vs/editor/browser/widget/diffNavigator.ts new file mode 100644 index 0000000000..bcb08d3a0e --- /dev/null +++ b/src/vs/editor/browser/widget/diffNavigator.ts @@ -0,0 +1,226 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'vs/base/common/assert'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import * as objects from 'vs/base/common/objects'; +import { Range } from 'vs/editor/common/core/range'; +import { ICommonDiffEditor, ILineChange, ScrollType } from 'vs/editor/common/editorCommon'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; + +interface IDiffRange { + rhs: boolean; + range: Range; +} + +export interface Options { + followsCaret?: boolean; + ignoreCharChanges?: boolean; + alwaysRevealFirst?: boolean; +} + +var defaultOptions: Options = { + followsCaret: true, + ignoreCharChanges: true, + alwaysRevealFirst: true +}; + +/** + * Create a new diff navigator for the provided diff editor. + */ +export class DiffNavigator extends EventEmitter { + + public static Events = { + UPDATED: 'navigation.updated' + }; + + private editor: ICommonDiffEditor; + private options: Options; + private disposed: boolean; + private toUnbind: IDisposable[]; + + private nextIdx: number; + private ranges: IDiffRange[]; + private ignoreSelectionChange: boolean; + public revealFirst: boolean; + + constructor(editor: ICommonDiffEditor, options: Options = {}) { + super([ + DiffNavigator.Events.UPDATED + ]); + this.editor = editor; + this.options = objects.mixin(options, defaultOptions, false); + + this.disposed = false; + this.toUnbind = []; + + this.nextIdx = -1; + this.ranges = []; + this.ignoreSelectionChange = false; + this.revealFirst = this.options.alwaysRevealFirst; + + // hook up to diff editor for diff, disposal, and caret move + this.toUnbind.push(this.editor.onDidDispose(() => this.dispose())); + this.toUnbind.push(this.editor.onDidUpdateDiff(() => this.onDiffUpdated())); + + if (this.options.followsCaret) { + this.toUnbind.push(this.editor.getModifiedEditor().onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => { + if (this.ignoreSelectionChange) { + return; + } + this.nextIdx = -1; + })); + } + if (this.options.alwaysRevealFirst) { + this.toUnbind.push(this.editor.getModifiedEditor().onDidChangeModel((e) => { + this.revealFirst = true; + })); + } + + // init things + this.init(); + } + + private init(): void { + var changes = this.editor.getLineChanges(); + if (!changes) { + return; + } + } + + private onDiffUpdated(): void { + this.init(); + + this.compute(this.editor.getLineChanges()); + if (this.revealFirst) { + // Only reveal first on first non-null changes + if (this.editor.getLineChanges() !== null) { + this.revealFirst = false; + this.nextIdx = -1; + this.next(); + } + } + } + + private compute(lineChanges: ILineChange[]): void { + + // new ranges + this.ranges = []; + + if (lineChanges) { + // create ranges from changes + lineChanges.forEach((lineChange) => { + + if (!this.options.ignoreCharChanges && lineChange.charChanges) { + + lineChange.charChanges.forEach((charChange) => { + this.ranges.push({ + rhs: true, + range: new Range( + charChange.modifiedStartLineNumber, + charChange.modifiedStartColumn, + charChange.modifiedEndLineNumber, + charChange.modifiedEndColumn) + }); + }); + + } else { + this.ranges.push({ + rhs: true, + range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedStartLineNumber, 1) + }); + } + }); + } + + // sort + this.ranges.sort((left, right) => { + if (left.range.getStartPosition().isBeforeOrEqual(right.range.getStartPosition())) { + return -1; + } else if (right.range.getStartPosition().isBeforeOrEqual(left.range.getStartPosition())) { + return 1; + } else { + return 0; + } + }); + + this.emit(DiffNavigator.Events.UPDATED, {}); + } + + private initIdx(fwd: boolean): void { + var found = false; + var position = this.editor.getPosition(); + for (var i = 0, len = this.ranges.length; i < len && !found; i++) { + var range = this.ranges[i].range; + if (position.isBeforeOrEqual(range.getStartPosition())) { + this.nextIdx = i + (fwd ? 0 : -1); + found = true; + } + } + if (!found) { + // after the last change + this.nextIdx = fwd ? 0 : this.ranges.length - 1; + } + if (this.nextIdx < 0) { + this.nextIdx = this.ranges.length - 1; + } + } + + private move(fwd: boolean): void { + assert.ok(!this.disposed, 'Illegal State - diff navigator has been disposed'); + + if (!this.canNavigate()) { + return; + } + + if (this.nextIdx === -1) { + this.initIdx(fwd); + + } else if (fwd) { + this.nextIdx += 1; + if (this.nextIdx >= this.ranges.length) { + this.nextIdx = 0; + } + } else { + this.nextIdx -= 1; + if (this.nextIdx < 0) { + this.nextIdx = this.ranges.length - 1; + } + } + + var info = this.ranges[this.nextIdx]; + this.ignoreSelectionChange = true; + try { + var pos = info.range.getStartPosition(); + this.editor.setPosition(pos); + this.editor.revealPositionInCenter(pos, ScrollType.Smooth); + } finally { + this.ignoreSelectionChange = false; + } + } + + public canNavigate(): boolean { + return this.ranges && this.ranges.length > 0; + } + + public next(): void { + this.move(true); + } + + public previous(): void { + this.move(false); + } + + public dispose(): void { + this.toUnbind = dispose(this.toUnbind); + this.ranges = null; + this.disposed = true; + + super.dispose(); + } +} + diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts new file mode 100644 index 0000000000..50c70269af --- /dev/null +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -0,0 +1,822 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./media/diffReview'; +import * as nls from 'vs/nls'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as dom from 'vs/base/browser/dom'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { renderViewLine2 as renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; +import { Configuration } from 'vs/editor/browser/config/configuration'; +import { Position } from 'vs/editor/common/core/position'; +import { ColorId, MetadataConsts, FontStyle } from 'vs/editor/common/modes'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; + +const DIFF_LINES_PADDING = 3; + +const enum DiffEntryType { + Equal = 0, + Insert = 1, + Delete = 2 +} + +class DiffEntry { + readonly originalLineStart: number; + readonly originalLineEnd: number; + readonly modifiedLineStart: number; + readonly modifiedLineEnd: number; + + constructor(originalLineStart: number, originalLineEnd: number, modifiedLineStart: number, modifiedLineEnd: number) { + this.originalLineStart = originalLineStart; + this.originalLineEnd = originalLineEnd; + this.modifiedLineStart = modifiedLineStart; + this.modifiedLineEnd = modifiedLineEnd; + } + + public getType(): DiffEntryType { + if (this.originalLineStart === 0) { + return DiffEntryType.Insert; + } + if (this.modifiedLineStart === 0) { + return DiffEntryType.Delete; + } + return DiffEntryType.Equal; + } +} + +class Diff { + readonly entries: DiffEntry[]; + + constructor(entries: DiffEntry[]) { + this.entries = entries; + } +} + +export class DiffReview extends Disposable { + + private readonly _diffEditor: DiffEditorWidget; + private _isVisible: boolean; + public readonly shadow: FastDomNode; + private readonly _actionBar: ActionBar; + public readonly actionBarContainer: FastDomNode; + public readonly domNode: FastDomNode; + private readonly _content: FastDomNode; + private readonly scrollbar: DomScrollableElement; + private _diffs: Diff[]; + private _currentDiff: Diff; + + constructor(diffEditor: DiffEditorWidget) { + super(); + this._diffEditor = diffEditor; + this._isVisible = false; + + this.shadow = createFastDomNode(document.createElement('div')); + this.shadow.setClassName('diff-review-shadow'); + + this.actionBarContainer = createFastDomNode(document.createElement('div')); + this.actionBarContainer.setClassName('diff-review-actions'); + this._actionBar = this._register(new ActionBar( + this.actionBarContainer.domNode + )); + + this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review', true, () => { + this.hide(); + return null; + }), { label: false, icon: true }); + + this.domNode = createFastDomNode(document.createElement('div')); + this.domNode.setClassName('diff-review monaco-editor-background'); + + this._content = createFastDomNode(document.createElement('div')); + this._content.setClassName('diff-review-content'); + this.scrollbar = this._register(new DomScrollableElement(this._content.domNode, {})); + this.domNode.domNode.appendChild(this.scrollbar.getDomNode()); + + this._register(diffEditor.onDidUpdateDiff(() => { + if (!this._isVisible) { + return; + } + this._diffs = this._compute(); + this._render(); + })); + this._register(diffEditor.getModifiedEditor().onDidChangeCursorPosition(() => { + if (!this._isVisible) { + return; + } + this._render(); + })); + this._register(diffEditor.getOriginalEditor().onDidFocusEditor(() => { + if (this._isVisible) { + this.hide(); + } + })); + this._register(diffEditor.getModifiedEditor().onDidFocusEditor(() => { + if (this._isVisible) { + this.hide(); + } + })); + this._register(dom.addStandardDisposableListener(this.domNode.domNode, 'click', (e) => { + e.preventDefault(); + + let row = dom.findParentWithClass(e.target, 'diff-review-row'); + if (row) { + this._goToRow(row); + } + })); + this._register(dom.addStandardDisposableListener(this.domNode.domNode, 'keydown', (e) => { + if ( + e.equals(KeyCode.DownArrow) + || e.equals(KeyMod.CtrlCmd | KeyCode.DownArrow) + || e.equals(KeyMod.Alt | KeyCode.DownArrow) + ) { + e.preventDefault(); + this._goToRow(this._getNextRow()); + } + + if ( + e.equals(KeyCode.UpArrow) + || e.equals(KeyMod.CtrlCmd | KeyCode.UpArrow) + || e.equals(KeyMod.Alt | KeyCode.UpArrow) + ) { + e.preventDefault(); + this._goToRow(this._getPrevRow()); + } + + if ( + e.equals(KeyCode.Escape) + || e.equals(KeyMod.CtrlCmd | KeyCode.Escape) + || e.equals(KeyMod.Alt | KeyCode.Escape) + || e.equals(KeyMod.Shift | KeyCode.Escape) + ) { + e.preventDefault(); + this.hide(); + } + + if ( + e.equals(KeyCode.Space) + || e.equals(KeyCode.Enter) + ) { + e.preventDefault(); + this.accept(); + } + })); + this._diffs = []; + this._currentDiff = null; + } + + public prev(): void { + let index = 0; + + if (!this._isVisible) { + this._diffs = this._compute(); + } + + if (this._isVisible) { + let currentIndex = -1; + for (let i = 0, len = this._diffs.length; i < len; i++) { + if (this._diffs[i] === this._currentDiff) { + currentIndex = i; + break; + } + } + index = (this._diffs.length + currentIndex - 1); + } else { + index = this._findDiffIndex(this._diffEditor.getPosition()); + } + + if (this._diffs.length === 0) { + // Nothing to do + return; + } + + index = index % this._diffs.length; + this._diffEditor.setPosition(new Position(this._diffs[index].entries[0].modifiedLineStart, 1)); + this._isVisible = true; + this._diffEditor.doLayout(); + this._render(); + this._goToRow(this._getNextRow()); + } + + public next(): void { + let index = 0; + + if (!this._isVisible) { + this._diffs = this._compute(); + } + + if (this._isVisible) { + let currentIndex = -1; + for (let i = 0, len = this._diffs.length; i < len; i++) { + if (this._diffs[i] === this._currentDiff) { + currentIndex = i; + break; + } + } + index = (currentIndex + 1); + } else { + index = this._findDiffIndex(this._diffEditor.getPosition()); + } + + if (this._diffs.length === 0) { + // Nothing to do + return; + } + + index = index % this._diffs.length; + this._diffEditor.setPosition(new Position(this._diffs[index].entries[0].modifiedLineStart, 1)); + this._isVisible = true; + this._diffEditor.doLayout(); + this._render(); + this._goToRow(this._getNextRow()); + } + + private accept(): void { + let jumpToLineNumber = -1; + let current = this._getCurrentFocusedRow(); + if (current) { + let lineNumber = parseInt(current.getAttribute('data-line'), 10); + if (!isNaN(lineNumber)) { + jumpToLineNumber = lineNumber; + } + } + this.hide(); + + if (jumpToLineNumber !== -1) { + this._diffEditor.setPosition(new Position(jumpToLineNumber, 1)); + this._diffEditor.revealPosition(new Position(jumpToLineNumber, 1), editorCommon.ScrollType.Immediate); + } + } + + private hide(): void { + this._isVisible = false; + this._diffEditor.focus(); + this._diffEditor.doLayout(); + this._render(); + } + + private _getPrevRow(): HTMLElement { + let current = this._getCurrentFocusedRow(); + if (!current) { + return this._getFirstRow(); + } + if (current.previousElementSibling) { + return current.previousElementSibling; + } + return current; + } + + private _getNextRow(): HTMLElement { + let current = this._getCurrentFocusedRow(); + if (!current) { + return this._getFirstRow(); + } + if (current.nextElementSibling) { + return current.nextElementSibling; + } + return current; + } + + private _getFirstRow(): HTMLElement { + return this.domNode.domNode.querySelector('.diff-review-row'); + } + + private _getCurrentFocusedRow(): HTMLElement { + let result = document.activeElement; + if (result && /diff-review-row/.test(result.className)) { + return result; + } + return null; + } + + private _goToRow(row: HTMLElement): void { + let prev = this._getCurrentFocusedRow(); + row.tabIndex = 0; + row.focus(); + if (prev && prev !== row) { + prev.tabIndex = -1; + } + this.scrollbar.scanDomNode(); + } + + public isVisible(): boolean { + return this._isVisible; + } + + private _width: number = 0; + + public layout(top: number, width: number, height: number): void { + this._width = width; + this.shadow.setTop(top - 6); + this.shadow.setWidth(width); + this.shadow.setHeight(this._isVisible ? 6 : 0); + this.domNode.setTop(top); + this.domNode.setWidth(width); + this.domNode.setHeight(height); + this._content.setHeight(height); + this._content.setWidth(width); + + if (this._isVisible) { + this.actionBarContainer.setAttribute('aria-hidden', 'false'); + this.actionBarContainer.setDisplay('block'); + } else { + this.actionBarContainer.setAttribute('aria-hidden', 'true'); + this.actionBarContainer.setDisplay('none'); + } + } + + private _compute(): Diff[] { + const lineChanges = this._diffEditor.getLineChanges(); + if (!lineChanges || lineChanges.length === 0) { + return []; + } + const originalModel = this._diffEditor.getOriginalEditor().getModel(); + const modifiedModel = this._diffEditor.getModifiedEditor().getModel(); + + if (!originalModel || !modifiedModel) { + return []; + } + + return DiffReview._mergeAdjacent(lineChanges, originalModel.getLineCount(), modifiedModel.getLineCount()); + } + + private static _mergeAdjacent(lineChanges: editorCommon.ILineChange[], originalLineCount: number, modifiedLineCount: number): Diff[] { + if (!lineChanges || lineChanges.length === 0) { + return []; + } + + let diffs: Diff[] = [], diffsLength = 0; + + for (let i = 0, len = lineChanges.length; i < len; i++) { + const lineChange = lineChanges[i]; + + const originalStart = lineChange.originalStartLineNumber; + const originalEnd = lineChange.originalEndLineNumber; + const modifiedStart = lineChange.modifiedStartLineNumber; + const modifiedEnd = lineChange.modifiedEndLineNumber; + + let r: DiffEntry[] = [], rLength = 0; + + // Emit before anchors + { + const originalEqualAbove = (originalEnd === 0 ? originalStart : originalStart - 1); + const modifiedEqualAbove = (modifiedEnd === 0 ? modifiedStart : modifiedStart - 1); + + // Make sure we don't step into the previous diff + let minOriginal = 1; + let minModified = 1; + if (i > 0) { + const prevLineChange = lineChanges[i - 1]; + + if (prevLineChange.originalEndLineNumber === 0) { + minOriginal = prevLineChange.originalStartLineNumber + 1; + } else { + minOriginal = prevLineChange.originalEndLineNumber + 1; + } + + if (prevLineChange.modifiedEndLineNumber === 0) { + minModified = prevLineChange.modifiedStartLineNumber + 1; + } else { + minModified = prevLineChange.modifiedEndLineNumber + 1; + } + } + + let fromOriginal = originalEqualAbove - DIFF_LINES_PADDING + 1; + let fromModified = modifiedEqualAbove - DIFF_LINES_PADDING + 1; + if (fromOriginal < minOriginal) { + const delta = minOriginal - fromOriginal; + fromOriginal = fromOriginal + delta; + fromModified = fromModified + delta; + } + if (fromModified < minModified) { + const delta = minModified - fromModified; + fromOriginal = fromOriginal + delta; + fromModified = fromModified + delta; + } + + r[rLength++] = new DiffEntry( + fromOriginal, originalEqualAbove, + fromModified, modifiedEqualAbove + ); + } + + // Emit deleted lines + { + if (originalEnd !== 0) { + r[rLength++] = new DiffEntry(originalStart, originalEnd, 0, 0); + } + } + + // Emit inserted lines + { + if (modifiedEnd !== 0) { + r[rLength++] = new DiffEntry(0, 0, modifiedStart, modifiedEnd); + } + } + + // Emit after anchors + { + const originalEqualBelow = (originalEnd === 0 ? originalStart + 1 : originalEnd + 1); + const modifiedEqualBelow = (modifiedEnd === 0 ? modifiedStart + 1 : modifiedEnd + 1); + + // Make sure we don't step into the next diff + let maxOriginal = originalLineCount; + let maxModified = modifiedLineCount; + if (i + 1 < len) { + const nextLineChange = lineChanges[i + 1]; + + if (nextLineChange.originalEndLineNumber === 0) { + maxOriginal = nextLineChange.originalStartLineNumber; + } else { + maxOriginal = nextLineChange.originalStartLineNumber - 1; + } + + if (nextLineChange.modifiedEndLineNumber === 0) { + maxModified = nextLineChange.modifiedStartLineNumber; + } else { + maxModified = nextLineChange.modifiedStartLineNumber - 1; + } + } + + let toOriginal = originalEqualBelow + DIFF_LINES_PADDING - 1; + let toModified = modifiedEqualBelow + DIFF_LINES_PADDING - 1; + + if (toOriginal > maxOriginal) { + const delta = maxOriginal - toOriginal; + toOriginal = toOriginal + delta; + toModified = toModified + delta; + } + if (toModified > maxModified) { + const delta = maxModified - toModified; + toOriginal = toOriginal + delta; + toModified = toModified + delta; + } + + r[rLength++] = new DiffEntry( + originalEqualBelow, toOriginal, + modifiedEqualBelow, toModified, + ); + } + + diffs[diffsLength++] = new Diff(r); + } + + // Merge adjacent diffs + let curr: DiffEntry[] = diffs[0].entries; + let r: Diff[] = [], rLength = 0; + for (let i = 1, len = diffs.length; i < len; i++) { + const thisDiff = diffs[i].entries; + + const currLast = curr[curr.length - 1]; + const thisFirst = thisDiff[0]; + + if ( + currLast.getType() === DiffEntryType.Equal + && thisFirst.getType() === DiffEntryType.Equal + && thisFirst.originalLineStart <= currLast.originalLineEnd + ) { + // We are dealing with equal lines that overlap + + curr[curr.length - 1] = new DiffEntry( + currLast.originalLineStart, thisFirst.originalLineEnd, + currLast.modifiedLineStart, thisFirst.modifiedLineEnd + ); + curr = curr.concat(thisDiff.slice(1)); + continue; + } + + r[rLength++] = new Diff(curr); + curr = thisDiff; + } + r[rLength++] = new Diff(curr); + return r; + } + + private _findDiffIndex(pos: Position): number { + const lineNumber = pos.lineNumber; + for (let i = 0, len = this._diffs.length; i < len; i++) { + const diff = this._diffs[i].entries; + const lastModifiedLine = diff[diff.length - 1].modifiedLineEnd; + if (lineNumber <= lastModifiedLine) { + return i; + } + } + return 0; + } + + private _render(): void { + + const originalOpts = this._diffEditor.getOriginalEditor().getConfiguration(); + const modifiedOpts = this._diffEditor.getModifiedEditor().getConfiguration(); + + const originalModel = this._diffEditor.getOriginalEditor().getModel(); + const modifiedModel = this._diffEditor.getModifiedEditor().getModel(); + + const originalModelOpts = originalModel.getOptions(); + const modifiedModelOpts = modifiedModel.getOptions(); + + if (!this._isVisible || !originalModel || !modifiedModel) { + dom.clearNode(this._content.domNode); + this._currentDiff = null; + this.scrollbar.scanDomNode(); + return; + } + + const pos = this._diffEditor.getPosition(); + const diffIndex = this._findDiffIndex(pos); + + if (this._diffs[diffIndex] === this._currentDiff) { + return; + } + this._currentDiff = this._diffs[diffIndex]; + + const diffs = this._diffs[diffIndex].entries; + let container = document.createElement('div'); + container.className = 'diff-review-table'; + container.setAttribute('role', 'list'); + Configuration.applyFontInfoSlow(container, modifiedOpts.fontInfo); + + let minOriginalLine = 0; + let maxOriginalLine = 0; + let minModifiedLine = 0; + let maxModifiedLine = 0; + for (let i = 0, len = diffs.length; i < len; i++) { + const diffEntry = diffs[i]; + const originalLineStart = diffEntry.originalLineStart; + const originalLineEnd = diffEntry.originalLineEnd; + const modifiedLineStart = diffEntry.modifiedLineStart; + const modifiedLineEnd = diffEntry.modifiedLineEnd; + + if (originalLineStart !== 0 && ((minOriginalLine === 0 || originalLineStart < minOriginalLine))) { + minOriginalLine = originalLineStart; + } + if (originalLineEnd !== 0 && ((maxOriginalLine === 0 || originalLineEnd > maxOriginalLine))) { + maxOriginalLine = originalLineEnd; + } + if (modifiedLineStart !== 0 && ((minModifiedLine === 0 || modifiedLineStart < minModifiedLine))) { + minModifiedLine = modifiedLineStart; + } + if (modifiedLineEnd !== 0 && ((maxModifiedLine === 0 || modifiedLineEnd > maxModifiedLine))) { + maxModifiedLine = modifiedLineEnd; + } + } + + let header = document.createElement('div'); + header.className = 'diff-review-row'; + + let cell = document.createElement('div'); + cell.className = 'diff-review-cell diff-review-summary'; + cell.appendChild(document.createTextNode(`${diffIndex + 1}/${this._diffs.length}: @@ -${minOriginalLine},${maxOriginalLine - minOriginalLine + 1} +${minModifiedLine},${maxModifiedLine - minModifiedLine + 1} @@`)); + header.setAttribute('data-line', String(minModifiedLine)); + header.setAttribute('aria-label', nls.localize('header', "Difference {0} of {1}: original {2}, {3} lines, modified {4}, {5} lines", (diffIndex + 1), this._diffs.length, minOriginalLine, maxOriginalLine - minOriginalLine + 1, minModifiedLine, maxModifiedLine - minModifiedLine + 1)); + header.appendChild(cell); + + // @@ -504,7 +517,7 @@ + header.setAttribute('role', 'listitem'); + container.appendChild(header); + + let modLine = minModifiedLine; + for (let i = 0, len = diffs.length; i < len; i++) { + const diffEntry = diffs[i]; + DiffReview._renderSection(container, diffEntry, modLine, this._width, originalOpts, originalModel, originalModelOpts, modifiedOpts, modifiedModel, modifiedModelOpts); + if (diffEntry.modifiedLineStart !== 0) { + modLine = diffEntry.modifiedLineEnd; + } + } + + dom.clearNode(this._content.domNode); + this._content.domNode.appendChild(container); + this.scrollbar.scanDomNode(); + } + + private static _renderSection( + dest: HTMLElement, diffEntry: DiffEntry, modLine: number, width: number, + originalOpts: editorOptions.InternalEditorOptions, originalModel: editorCommon.IModel, originalModelOpts: editorCommon.TextModelResolvedOptions, + modifiedOpts: editorOptions.InternalEditorOptions, modifiedModel: editorCommon.IModel, modifiedModelOpts: editorCommon.TextModelResolvedOptions + ): void { + + const type = diffEntry.getType(); + + let rowClassName: string = 'diff-review-row'; + let lineNumbersExtraClassName: string = ''; + let spacerClassName: string = 'diff-review-spacer'; + switch (type) { + case DiffEntryType.Insert: + rowClassName = 'diff-review-row line-insert'; + lineNumbersExtraClassName = ' char-insert'; + spacerClassName = 'diff-review-spacer insert-sign'; + break; + case DiffEntryType.Delete: + rowClassName = 'diff-review-row line-delete'; + lineNumbersExtraClassName = ' char-delete'; + spacerClassName = 'diff-review-spacer delete-sign'; + break; + } + + const originalLineStart = diffEntry.originalLineStart; + const originalLineEnd = diffEntry.originalLineEnd; + const modifiedLineStart = diffEntry.modifiedLineStart; + const modifiedLineEnd = diffEntry.modifiedLineEnd; + + const cnt = Math.max( + modifiedLineEnd - modifiedLineStart, + originalLineEnd - originalLineStart + ); + + const originalLineNumbersWidth = originalOpts.layoutInfo.glyphMarginWidth + originalOpts.layoutInfo.lineNumbersWidth; + const modifiedLineNumbersWidth = 10 + modifiedOpts.layoutInfo.glyphMarginWidth + modifiedOpts.layoutInfo.lineNumbersWidth; + + for (let i = 0; i <= cnt; i++) { + const originalLine = (originalLineStart === 0 ? 0 : originalLineStart + i); + const modifiedLine = (modifiedLineStart === 0 ? 0 : modifiedLineStart + i); + + const row = document.createElement('div'); + row.style.minWidth = width + 'px'; + row.className = rowClassName; + row.setAttribute('role', 'listitem'); + if (modifiedLine !== 0) { + modLine = modifiedLine; + } + row.setAttribute('data-line', String(modLine)); + + let cell = document.createElement('div'); + cell.className = 'diff-review-cell'; + row.appendChild(cell); + + const originalLineNumber = document.createElement('span'); + originalLineNumber.style.width = (originalLineNumbersWidth + 'px'); + originalLineNumber.style.minWidth = (originalLineNumbersWidth + 'px'); + originalLineNumber.className = 'diff-review-line-number' + lineNumbersExtraClassName; + if (originalLine !== 0) { + originalLineNumber.appendChild(document.createTextNode(String(originalLine))); + } else { + originalLineNumber.innerHTML = ' '; + } + cell.appendChild(originalLineNumber); + + const modifiedLineNumber = document.createElement('span'); + modifiedLineNumber.style.width = (modifiedLineNumbersWidth + 'px'); + modifiedLineNumber.style.minWidth = (modifiedLineNumbersWidth + 'px'); + modifiedLineNumber.style.paddingRight = '10px'; + modifiedLineNumber.className = 'diff-review-line-number' + lineNumbersExtraClassName; + if (modifiedLine !== 0) { + modifiedLineNumber.appendChild(document.createTextNode(String(modifiedLine))); + } else { + modifiedLineNumber.innerHTML = ' '; + } + cell.appendChild(modifiedLineNumber); + + const spacer = document.createElement('span'); + spacer.className = spacerClassName; + spacer.innerHTML = '  '; + cell.appendChild(spacer); + + let lineContent: string; + if (modifiedLine !== 0) { + cell.insertAdjacentHTML('beforeend', + this._renderLine(modifiedModel, modifiedOpts, modifiedModelOpts.tabSize, modifiedLine) + ); + lineContent = modifiedModel.getLineContent(modifiedLine); + } else { + cell.insertAdjacentHTML('beforeend', + this._renderLine(originalModel, originalOpts, originalModelOpts.tabSize, originalLine) + ); + lineContent = originalModel.getLineContent(originalLine); + } + + if (lineContent.length === 0) { + lineContent = nls.localize('blankLine', "blank"); + } + + let ariaLabel: string; + switch (type) { + case DiffEntryType.Equal: + ariaLabel = nls.localize('equalLine', "original {0}, modified {1}: {2}", originalLine, modifiedLine, lineContent); + break; + case DiffEntryType.Insert: + ariaLabel = nls.localize('insertLine', "+ modified {0}: {1}", modifiedLine, lineContent); + break; + case DiffEntryType.Delete: + ariaLabel = nls.localize('deleteLine', "- original {0}: {1}", originalLine, lineContent); + break; + } + row.setAttribute('aria-label', ariaLabel); + + dest.appendChild(row); + } + } + + private static _renderLine(model: editorCommon.IModel, config: editorOptions.InternalEditorOptions, tabSize: number, lineNumber: number): string { + const lineContent = model.getLineContent(lineNumber); + + const defaultMetadata = ( + (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) + | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) + | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) + ) >>> 0; + + const r = renderViewLine(new RenderLineInput( + (config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations), + lineContent, + model.mightContainRTL(), + 0, + [new ViewLineToken(lineContent.length, defaultMetadata)], + [], + tabSize, + config.fontInfo.spaceWidth, + config.viewInfo.stopRenderingLineAfter, + config.viewInfo.renderWhitespace, + config.viewInfo.renderControlCharacters, + config.viewInfo.fontLigatures + )); + + return r.html; + } +} + +// theming + +registerThemingParticipant((theme, collector) => { + let lineNumbers = theme.getColor(editorLineNumbers); + if (lineNumbers) { + collector.addRule(`.monaco-diff-editor .diff-review-line-number { color: ${lineNumbers}; }`); + } + + const shadow = theme.getColor(scrollbarShadow); + if (shadow) { + collector.addRule(`.monaco-diff-editor .diff-review-shadow { box-shadow: ${shadow} 0 -6px 6px -6px inset; }`); + } +}); + +@editorAction +class DiffReviewNext extends EditorAction { + constructor() { + super({ + id: 'editor.action.diffReview.next', + label: nls.localize('editor.action.diffReview.next', "Go to Next Difference"), + alias: 'Go to Next Difference', + precondition: ContextKeyExpr.has('isInDiffEditor'), + kbOpts: { + kbExpr: null, + primary: KeyCode.F7 + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + const diffEditor = findFocusedDiffEditor(accessor); + if (diffEditor) { + diffEditor.diffReviewNext(); + } + } +} + +@editorAction +class DiffReviewPrev extends EditorAction { + constructor() { + super({ + id: 'editor.action.diffReview.prev', + label: nls.localize('editor.action.diffReview.prev', "Go to Previous Difference"), + alias: 'Go to Previous Difference', + precondition: ContextKeyExpr.has('isInDiffEditor'), + kbOpts: { + kbExpr: null, + primary: KeyMod.Shift | KeyCode.F7 + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + const diffEditor = findFocusedDiffEditor(accessor); + if (diffEditor) { + diffEditor.diffReviewPrev(); + } + } +} + +function findFocusedDiffEditor(accessor: ServicesAccessor): DiffEditorWidget { + const codeEditorService = accessor.get(ICodeEditorService); + const diffEditors = codeEditorService.listDiffEditors(); + for (let i = 0, len = diffEditors.length; i < len; i++) { + const diffEditor = diffEditors[i]; + if (diffEditor.hasWidgetFocus()) { + return diffEditor; + } + } + return null; +} diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts new file mode 100644 index 0000000000..bcc41eea59 --- /dev/null +++ b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as objects from 'vs/base/common/objects'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { CodeEditor } from 'vs/editor/browser/codeEditor'; +import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +export class EmbeddedCodeEditorWidget extends CodeEditor { + + private _parentEditor: ICodeEditor; + private _overwriteOptions: IEditorOptions; + + constructor( + domElement: HTMLElement, + options: IEditorOptions, + parentEditor: ICodeEditor, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @ICommandService commandService: ICommandService, + @IContextKeyService contextKeyService: IContextKeyService, + @IThemeService themeService: IThemeService + ) { + super(domElement, parentEditor.getRawConfiguration(), instantiationService, codeEditorService, commandService, contextKeyService, themeService); + + this._parentEditor = parentEditor; + this._overwriteOptions = options; + + // Overwrite parent's options + super.updateOptions(this._overwriteOptions); + + this._register(parentEditor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => this._onParentConfigurationChanged(e))); + } + + public getParentEditor(): ICodeEditor { + return this._parentEditor; + } + + private _onParentConfigurationChanged(e: IConfigurationChangedEvent): void { + super.updateOptions(this._parentEditor.getRawConfiguration()); + super.updateOptions(this._overwriteOptions); + } + + public updateOptions(newOptions: IEditorOptions): void { + objects.mixin(this._overwriteOptions, newOptions, true); + super.updateOptions(this._overwriteOptions); + } +} \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/addition-inverse.svg b/src/vs/editor/browser/widget/media/addition-inverse.svg new file mode 100644 index 0000000000..3475c1e196 --- /dev/null +++ b/src/vs/editor/browser/widget/media/addition-inverse.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/addition.svg b/src/vs/editor/browser/widget/media/addition.svg new file mode 100644 index 0000000000..bdecdb0e45 --- /dev/null +++ b/src/vs/editor/browser/widget/media/addition.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/close-inverse.svg b/src/vs/editor/browser/widget/media/close-inverse.svg new file mode 100644 index 0000000000..751e89b3b0 --- /dev/null +++ b/src/vs/editor/browser/widget/media/close-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/close.svg b/src/vs/editor/browser/widget/media/close.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/vs/editor/browser/widget/media/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/deletion-inverse.svg b/src/vs/editor/browser/widget/media/deletion-inverse.svg new file mode 100644 index 0000000000..2de46fcf5b --- /dev/null +++ b/src/vs/editor/browser/widget/media/deletion-inverse.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/deletion.svg b/src/vs/editor/browser/widget/media/deletion.svg new file mode 100644 index 0000000000..f5d128b2df --- /dev/null +++ b/src/vs/editor/browser/widget/media/deletion.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/diagonal-fill.png b/src/vs/editor/browser/widget/media/diagonal-fill.png new file mode 100644 index 0000000000000000000000000000000000000000..5cd481a8108c4ed683b1e4c6216342cd85d694e5 GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCf`#@Q5sCVBk9p!i>lBSEK+1rAk~QN`mv#O3D+9QW*jgGxJLH{9Hp6%8d0) z^$ZORz7#D4s?zXuaSYK2PPVB3SHsNA$1rJk!NWuKF`sx0SS@%Z<+vRFv9~fW+`z<; Xq7XbuI;-M4P!EHrtDnm{r-UW|w0ALU literal 0 HcmV?d00001 diff --git a/src/vs/editor/browser/widget/media/diffEditor.css b/src/vs/editor/browser/widget/media/diffEditor.css new file mode 100644 index 0000000000..274afd5f99 --- /dev/null +++ b/src/vs/editor/browser/widget/media/diffEditor.css @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +/* ---------- DiffEditor ---------- */ + +.monaco-diff-editor .diffOverview { + z-index: 9; +} + +/* colors not externalized: using transparancy on background */ +.monaco-diff-editor.vs .diffOverview { background: rgba(0, 0, 0, 0.03); } +.monaco-diff-editor.vs-dark .diffOverview { background: rgba(255, 255, 255, 0.01); } + +.monaco-diff-editor .diffViewport { + box-shadow: inset 0px 0px 1px 0px #B9B9B9; + background: rgba(0, 0, 0, 0.10); +} + +.monaco-diff-editor.vs-dark .diffViewport, +.monaco-diff-editor.hc-black .diffViewport { + background: rgba(255, 255, 255, 0.10); +} +.monaco-scrollable-element.modified-in-monaco-diff-editor.vs .scrollbar { background: rgba(0,0,0,0); } +.monaco-scrollable-element.modified-in-monaco-diff-editor.vs-dark .scrollbar { background: rgba(0,0,0,0); } +.monaco-scrollable-element.modified-in-monaco-diff-editor.hc-black .scrollbar { background: none; } + +.monaco-scrollable-element.modified-in-monaco-diff-editor .slider { + z-index: 10; +} +.modified-in-monaco-diff-editor .slider.active { background: rgba(171, 171, 171, .4); } +.modified-in-monaco-diff-editor.hc-black .slider.active { background: none; } + +/* ---------- Diff ---------- */ + +.monaco-editor .insert-sign, +.monaco-diff-editor .insert-sign, +.monaco-editor .delete-sign, +.monaco-diff-editor .delete-sign { + background-size: 60%; + opacity: 0.7; + background-repeat: no-repeat; + background-position: 50% 50%; +} +.monaco-editor.hc-black .insert-sign, +.monaco-diff-editor.hc-black .insert-sign, +.monaco-editor.hc-black .delete-sign, +.monaco-diff-editor.hc-black .delete-sign { + opacity: 1; +} +.monaco-editor .insert-sign, +.monaco-diff-editor .insert-sign { + background-image: url('addition.svg'); +} +.monaco-editor .delete-sign, +.monaco-diff-editor .delete-sign { + background-image: url('deletion.svg'); +} + +.monaco-editor.vs-dark .insert-sign, +.monaco-diff-editor.vs-dark .insert-sign, +.monaco-editor.hc-black .insert-sign, +.monaco-diff-editor.hc-black .insert-sign { + background-image: url('addition-inverse.svg'); +} +.monaco-editor.vs-dark .delete-sign, +.monaco-diff-editor.vs-dark .delete-sign, +.monaco-editor.hc-black .delete-sign, +.monaco-diff-editor.hc-black .delete-sign { + background-image: url('deletion-inverse.svg'); +} + +.monaco-editor .inline-deleted-margin-view-zone { + text-align: right; +} +.monaco-editor .inline-added-margin-view-zone { + text-align: right; +} + +.monaco-editor .diagonal-fill { + background: url('diagonal-fill.png'); +} +.monaco-editor.vs-dark .diagonal-fill { + opacity: 0.2; +} +.monaco-editor.hc-black .diagonal-fill { + background: none; +} + +/* ---------- Inline Diff ---------- */ + +.monaco-editor .view-zones .view-lines .view-line span { + display: inline-block; +} diff --git a/src/vs/editor/browser/widget/media/diffReview.css b/src/vs/editor/browser/widget/media/diffReview.css new file mode 100644 index 0000000000..b0eeb904da --- /dev/null +++ b/src/vs/editor/browser/widget/media/diffReview.css @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-diff-editor .diff-review-line-number { + text-align: right; + display: inline-block; +} + +.monaco-diff-editor .diff-review { + position: absolute; + -webkit-user-select: none; + -ms-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + user-select: none; +} + +.monaco-diff-editor .diff-review-summary { + padding-left: 10px; +} + +.monaco-diff-editor .diff-review-shadow { + position: absolute; +} + +.monaco-diff-editor .diff-review-row { + white-space: pre; +} + +.monaco-diff-editor .diff-review-table { + display: table; + min-width: 100%; +} + +.monaco-diff-editor .diff-review-row { + display: table-row; + width: 100%; +} + +.monaco-diff-editor .diff-review-cell { + display: table-cell; +} + +.monaco-diff-editor .diff-review-spacer { + display: inline-block; + width: 10px; +} + +.monaco-diff-editor .diff-review-actions { + display: inline-block; + position: absolute; + right: 10px; + top: 2px; +} + +.monaco-diff-editor .diff-review-actions .action-label { + width: 16px; + height: 16px; + margin: 2px 0; +} +.monaco-diff-editor .action-label.icon.close-diff-review { + background: url('close.svg') center center no-repeat; +} +.monaco-diff-editor.hc-black .action-label.icon.close-diff-review, +.monaco-diff-editor.vs-dark .action-label.icon.close-diff-review { + background: url('close-inverse.svg') center center no-repeat; +} \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/editor.css b/src/vs/editor/browser/widget/media/editor.css new file mode 100644 index 0000000000..a3eb23e495 --- /dev/null +++ b/src/vs/editor/browser/widget/media/editor.css @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* -------------------- IE10 remove auto clear button -------------------- */ + +::-ms-clear { + display: none; +} + +/* All widgets */ +/* I am not a big fan of this rule */ +.monaco-editor .editor-widget input { + color: inherit; +} + +/* -------------------- Editor -------------------- */ + +.monaco-editor { + position: relative; + overflow: visible; + -webkit-text-size-adjust: 100%; + -webkit-font-feature-settings: "liga" off, "calt" off; + font-feature-settings: "liga" off, "calt" off; +} +.monaco-editor.enable-ligatures { + -webkit-font-feature-settings: "liga" on, "calt" on; + font-feature-settings: "liga" on, "calt" on; +} + +/* -------------------- Misc -------------------- */ + +.monaco-editor .overflow-guard { + position: relative; + overflow: hidden; +} + +.monaco-editor .view-overlays { + position: absolute; + top: 0; +} \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/tokens.css b/src/vs/editor/browser/widget/media/tokens.css new file mode 100644 index 0000000000..23d80dd9be --- /dev/null +++ b/src/vs/editor/browser/widget/media/tokens.css @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .vs-whitespace { + display:inline-block; +} + diff --git a/src/vs/editor/common/commands/replaceCommand.ts b/src/vs/editor/common/commands/replaceCommand.ts new file mode 100644 index 0000000000..b99dd2413a --- /dev/null +++ b/src/vs/editor/common/commands/replaceCommand.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Range } from 'vs/editor/common/core/range'; + +export class ReplaceCommand implements editorCommon.ICommand { + + private readonly _range: Range; + private readonly _text: string; + public readonly insertsAutoWhitespace: boolean; + + constructor(range: Range, text: string, insertsAutoWhitespace: boolean = false) { + this._range = range; + this._text = text; + this.insertsAutoWhitespace = insertsAutoWhitespace; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + builder.addTrackedEditOperation(this._range, this._text); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + let inverseEditOperations = helper.getInverseEditOperations(); + let srcRange = inverseEditOperations[0].range; + return new Selection( + srcRange.endLineNumber, + srcRange.endColumn, + srcRange.endLineNumber, + srcRange.endColumn + ); + } +} + +export class ReplaceCommandWithoutChangingPosition implements editorCommon.ICommand { + + private readonly _range: Range; + private readonly _text: string; + public readonly insertsAutoWhitespace: boolean; + + constructor(range: Range, text: string, insertsAutoWhitespace: boolean = false) { + this._range = range; + this._text = text; + this.insertsAutoWhitespace = insertsAutoWhitespace; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + builder.addTrackedEditOperation(this._range, this._text); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + let inverseEditOperations = helper.getInverseEditOperations(); + let srcRange = inverseEditOperations[0].range; + return new Selection( + srcRange.startLineNumber, + srcRange.startColumn, + srcRange.startLineNumber, + srcRange.startColumn + ); + } +} + +export class ReplaceCommandWithOffsetCursorState implements editorCommon.ICommand { + + private readonly _range: Range; + private readonly _text: string; + private readonly _columnDeltaOffset: number; + private readonly _lineNumberDeltaOffset: number; + public readonly insertsAutoWhitespace: boolean; + + constructor(range: Range, text: string, lineNumberDeltaOffset: number, columnDeltaOffset: number, insertsAutoWhitespace: boolean = false) { + this._range = range; + this._text = text; + this._columnDeltaOffset = columnDeltaOffset; + this._lineNumberDeltaOffset = lineNumberDeltaOffset; + this.insertsAutoWhitespace = insertsAutoWhitespace; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + builder.addTrackedEditOperation(this._range, this._text); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + let inverseEditOperations = helper.getInverseEditOperations(); + let srcRange = inverseEditOperations[0].range; + return new Selection( + srcRange.endLineNumber + this._lineNumberDeltaOffset, + srcRange.endColumn + this._columnDeltaOffset, + srcRange.endLineNumber + this._lineNumberDeltaOffset, + srcRange.endColumn + this._columnDeltaOffset + ); + } +} + +export class ReplaceCommandThatPreservesSelection implements editorCommon.ICommand { + + private _range: Range; + private _text: string; + private _initialSelection: Selection; + private _selectionId: string; + + constructor(editRange: Range, text: string, initialSelection: Selection) { + this._range = editRange; + this._text = text; + this._initialSelection = initialSelection; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + builder.addEditOperation(this._range, this._text); + this._selectionId = builder.trackSelection(this._initialSelection); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + return helper.getTrackedSelection(this._selectionId); + } +} diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts new file mode 100644 index 0000000000..599abf9c45 --- /dev/null +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -0,0 +1,242 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { CharCode } from 'vs/base/common/charCode'; + +export interface IShiftCommandOpts { + isUnshift: boolean; + tabSize: number; + oneIndent: string; + useTabStops: boolean; +} + +export class ShiftCommand implements ICommand { + + public static unshiftIndentCount(line: string, column: number, tabSize: number): number { + // Determine the visible column where the content starts + let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(line, column, tabSize); + + let desiredTabStop = CursorColumns.prevTabStop(contentStartVisibleColumn, tabSize); + + // The `desiredTabStop` is a multiple of `tabSize` => determine the number of indents + return desiredTabStop / tabSize; + } + + public static shiftIndentCount(line: string, column: number, tabSize: number): number { + // Determine the visible column where the content starts + let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(line, column, tabSize); + + let desiredTabStop = CursorColumns.nextTabStop(contentStartVisibleColumn, tabSize); + + // The `desiredTabStop` is a multiple of `tabSize` => determine the number of indents + return desiredTabStop / tabSize; + } + + private _opts: IShiftCommandOpts; + private _selection: Selection; + private _selectionId: string; + private _useLastEditRangeForCursorEndPosition: boolean; + private _selectionStartColumnStaysPut: boolean; + + constructor(range: Selection, opts: IShiftCommandOpts) { + this._opts = opts; + this._selection = range; + this._useLastEditRangeForCursorEndPosition = false; + this._selectionStartColumnStaysPut = false; + } + + private _addEditOperation(builder: IEditOperationBuilder, range: Range, text: string) { + if (this._useLastEditRangeForCursorEndPosition) { + builder.addTrackedEditOperation(range, text); + } else { + builder.addEditOperation(range, text); + } + } + + public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void { + const startLine = this._selection.startLineNumber; + + let endLine = this._selection.endLineNumber; + if (this._selection.endColumn === 1 && startLine !== endLine) { + endLine = endLine - 1; + } + + const tabSize = this._opts.tabSize; + const oneIndent = this._opts.oneIndent; + const shouldIndentEmptyLines = (startLine === endLine); + + // if indenting or outdenting on a whitespace only line + if (this._selection.isEmpty()) { + if (/^\s*$/.test(model.getLineContent(startLine))) { + this._useLastEditRangeForCursorEndPosition = true; + } + } + + if (this._opts.useTabStops) { + // indents[i] represents i * oneIndent + let indents: string[] = ['', oneIndent]; + + // keep track of previous line's "miss-alignment" + let previousLineExtraSpaces = 0, extraSpaces = 0; + for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++ , previousLineExtraSpaces = extraSpaces) { + extraSpaces = 0; + let lineText = model.getLineContent(lineNumber); + let indentationEndIndex = strings.firstNonWhitespaceIndex(lineText); + + if (this._opts.isUnshift && (lineText.length === 0 || indentationEndIndex === 0)) { + // empty line or line with no leading whitespace => nothing to do + continue; + } + + if (!shouldIndentEmptyLines && !this._opts.isUnshift && lineText.length === 0) { + // do not indent empty lines => nothing to do + continue; + } + + if (indentationEndIndex === -1) { + // the entire line is whitespace + indentationEndIndex = lineText.length; + } + + if (lineNumber > 1) { + let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(lineText, indentationEndIndex + 1, tabSize); + if (contentStartVisibleColumn % tabSize !== 0) { + // The current line is "miss-aligned", so let's see if this is expected... + // This can only happen when it has trailing commas in the indent + if (model.isCheapToTokenize(lineNumber - 1)) { + let enterAction = LanguageConfigurationRegistry.getRawEnterActionAtPosition(model, lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); + if (enterAction) { + extraSpaces = previousLineExtraSpaces; + if (enterAction.appendText) { + for (let j = 0, lenJ = enterAction.appendText.length; j < lenJ && extraSpaces < tabSize; j++) { + if (enterAction.appendText.charCodeAt(j) === CharCode.Space) { + extraSpaces++; + } else { + break; + } + } + } + if (enterAction.removeText) { + extraSpaces = Math.max(0, extraSpaces - enterAction.removeText); + } + + // Act as if `prefixSpaces` is not part of the indentation + for (let j = 0; j < extraSpaces; j++) { + if (indentationEndIndex === 0 || lineText.charCodeAt(indentationEndIndex - 1) !== CharCode.Space) { + break; + } + indentationEndIndex--; + } + } + } + } + } + + + if (this._opts.isUnshift && indentationEndIndex === 0) { + // line with no leading whitespace => nothing to do + continue; + } + + let desiredIndentCount: number; + if (this._opts.isUnshift) { + desiredIndentCount = ShiftCommand.unshiftIndentCount(lineText, indentationEndIndex + 1, tabSize); + } else { + desiredIndentCount = ShiftCommand.shiftIndentCount(lineText, indentationEndIndex + 1, tabSize); + } + + // Fill `indents`, as needed + for (let j = indents.length; j <= desiredIndentCount; j++) { + indents[j] = indents[j - 1] + oneIndent; + } + + this._addEditOperation(builder, new Range(lineNumber, 1, lineNumber, indentationEndIndex + 1), indents[desiredIndentCount]); + if (lineNumber === startLine) { + // Force the startColumn to stay put because we're inserting after it + this._selectionStartColumnStaysPut = (this._selection.startColumn <= indentationEndIndex + 1); + } + } + } else { + + for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++) { + const lineText = model.getLineContent(lineNumber); + let indentationEndIndex = strings.firstNonWhitespaceIndex(lineText); + + if (this._opts.isUnshift && (lineText.length === 0 || indentationEndIndex === 0)) { + // empty line or line with no leading whitespace => nothing to do + continue; + } + + if (!shouldIndentEmptyLines && !this._opts.isUnshift && lineText.length === 0) { + // do not indent empty lines => nothing to do + continue; + } + + if (indentationEndIndex === -1) { + // the entire line is whitespace + indentationEndIndex = lineText.length; + } + + if (this._opts.isUnshift && indentationEndIndex === 0) { + // line with no leading whitespace => nothing to do + continue; + } + + if (this._opts.isUnshift) { + + indentationEndIndex = Math.min(indentationEndIndex, tabSize); + for (let i = 0; i < indentationEndIndex; i++) { + const chr = lineText.charCodeAt(i); + if (chr === CharCode.Tab) { + indentationEndIndex = i + 1; + break; + } + } + + this._addEditOperation(builder, new Range(lineNumber, 1, lineNumber, indentationEndIndex + 1), ''); + } else { + this._addEditOperation(builder, new Range(lineNumber, 1, lineNumber, 1), oneIndent); + if (lineNumber === startLine) { + // Force the startColumn to stay put because we're inserting after it + this._selectionStartColumnStaysPut = (this._selection.startColumn === 1); + } + } + } + } + + this._selectionId = builder.trackSelection(this._selection); + } + + public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { + if (this._useLastEditRangeForCursorEndPosition) { + let lastOp = helper.getInverseEditOperations()[0]; + return new Selection(lastOp.range.endLineNumber, lastOp.range.endColumn, lastOp.range.endLineNumber, lastOp.range.endColumn); + } + const result = helper.getTrackedSelection(this._selectionId); + + if (this._selectionStartColumnStaysPut) { + // The selection start should not move + let initialStartColumn = this._selection.startColumn; + let resultStartColumn = result.startColumn; + if (resultStartColumn <= initialStartColumn) { + return result; + } + + if (result.getDirection() === SelectionDirection.LTR) { + return new Selection(result.startLineNumber, initialStartColumn, result.endLineNumber, result.endColumn); + } + return new Selection(result.endLineNumber, result.endColumn, result.startLineNumber, initialStartColumn); + } + + return result; + } +} diff --git a/src/vs/editor/common/commands/surroundSelectionCommand.ts b/src/vs/editor/common/commands/surroundSelectionCommand.ts new file mode 100644 index 0000000000..6976027d2f --- /dev/null +++ b/src/vs/editor/common/commands/surroundSelectionCommand.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon'; + +export class SurroundSelectionCommand implements ICommand { + private _range: Selection; + private _charBeforeSelection: string; + private _charAfterSelection: string; + + constructor(range: Selection, charBeforeSelection: string, charAfterSelection: string) { + this._range = range; + this._charBeforeSelection = charBeforeSelection; + this._charAfterSelection = charAfterSelection; + } + + public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void { + builder.addTrackedEditOperation(new Range( + this._range.startLineNumber, + this._range.startColumn, + this._range.startLineNumber, + this._range.startColumn + ), this._charBeforeSelection); + + builder.addTrackedEditOperation(new Range( + this._range.endLineNumber, + this._range.endColumn, + this._range.endLineNumber, + this._range.endColumn + ), this._charAfterSelection); + } + + public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { + let inverseEditOperations = helper.getInverseEditOperations(); + let firstOperationRange = inverseEditOperations[0].range; + let secondOperationRange = inverseEditOperations[1].range; + + return new Selection( + firstOperationRange.endLineNumber, + firstOperationRange.endColumn, + secondOperationRange.endLineNumber, + secondOperationRange.endColumn - this._charAfterSelection.length + ); + } +} diff --git a/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts b/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts new file mode 100644 index 0000000000..06f4a0679e --- /dev/null +++ b/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Selection } from 'vs/editor/common/core/selection'; + +export class TrimTrailingWhitespaceCommand implements editorCommon.ICommand { + + private selection: Selection; + private selectionId: string; + + constructor(selection: Selection) { + this.selection = selection; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + let ops = trimTrailingWhitespace(model, []); + for (let i = 0, len = ops.length; i < len; i++) { + let op = ops[i]; + + builder.addEditOperation(op.range, op.text); + } + + this.selectionId = builder.trackSelection(this.selection); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + return helper.getTrackedSelection(this.selectionId); + } +} + +/** + * Generate commands for trimming trailing whitespace on a model and ignore lines on which cursors are sitting. + */ +export function trimTrailingWhitespace(model: editorCommon.ITextModel, cursors: Position[]): editorCommon.IIdentifiedSingleEditOperation[] { + // Sort cursors ascending + cursors.sort((a, b) => { + if (a.lineNumber === b.lineNumber) { + return a.column - b.column; + } + return a.lineNumber - b.lineNumber; + }); + + // Reduce multiple cursors on the same line and only keep the last one on the line + for (let i = cursors.length - 2; i >= 0; i--) { + if (cursors[i].lineNumber === cursors[i + 1].lineNumber) { + // Remove cursor at `i` + cursors.splice(i, 1); + } + } + + let r: editorCommon.IIdentifiedSingleEditOperation[] = []; + let rLen = 0; + let cursorIndex = 0; + let cursorLen = cursors.length; + + for (let lineNumber = 1, lineCount = model.getLineCount(); lineNumber <= lineCount; lineNumber++) { + let lineContent = model.getLineContent(lineNumber); + let maxLineColumn = lineContent.length + 1; + let minEditColumn = 0; + + if (cursorIndex < cursorLen && cursors[cursorIndex].lineNumber === lineNumber) { + minEditColumn = cursors[cursorIndex].column; + cursorIndex++; + if (minEditColumn === maxLineColumn) { + // The cursor is at the end of the line => no edits for sure on this line + continue; + } + } + + if (lineContent.length === 0) { + continue; + } + + let lastNonWhitespaceIndex = strings.lastNonWhitespaceIndex(lineContent); + + let fromColumn = 0; + if (lastNonWhitespaceIndex === -1) { + // Entire line is whitespace + fromColumn = 1; + } else if (lastNonWhitespaceIndex !== lineContent.length - 1) { + // There is trailing whitespace + fromColumn = lastNonWhitespaceIndex + 2; + } else { + // There is no trailing whitespace + continue; + } + + fromColumn = Math.max(minEditColumn, fromColumn); + r[rLen++] = EditOperation.delete(new Range( + lineNumber, fromColumn, + lineNumber, maxLineColumn + )); + } + + return r; +} diff --git a/src/vs/editor/common/commonCodeEditor.ts b/src/vs/editor/common/commonCodeEditor.ts new file mode 100644 index 0000000000..bc2c25cfc8 --- /dev/null +++ b/src/vs/editor/common/commonCodeEditor.ts @@ -0,0 +1,1063 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IContextKey, IContextKeyServiceTarget, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { CommonEditorConfiguration } from 'vs/editor/common/config/commonEditorConfig'; +import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor'; +import { CursorColumns, ICursors, CursorConfiguration } from 'vs/editor/common/controller/cursorCommon'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { Selection, ISelection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; +import { hash } from 'vs/base/common/hash'; +import { EditorModeContext } from 'vs/editor/common/modes/editorModeContext'; +import { + IModelContentChangedEvent, IModelDecorationsChangedEvent, + IModelLanguageChangedEvent, IModelOptionsChangedEvent, TextModelEventType +} from 'vs/editor/common/model/textModelEvents'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; + +let EDITOR_ID = 0; + +export abstract class CommonCodeEditor extends Disposable implements editorCommon.ICommonCodeEditor { + + private readonly _onDidDispose: Emitter = this._register(new Emitter()); + public readonly onDidDispose: Event = this._onDidDispose.event; + + private readonly _onDidChangeModelContent: Emitter = this._register(new Emitter()); + public readonly onDidChangeModelContent: Event = this._onDidChangeModelContent.event; + + private readonly _onDidChangeModelLanguage: Emitter = this._register(new Emitter()); + public readonly onDidChangeModelLanguage: Event = this._onDidChangeModelLanguage.event; + + private readonly _onDidChangeModelOptions: Emitter = this._register(new Emitter()); + public readonly onDidChangeModelOptions: Event = this._onDidChangeModelOptions.event; + + private readonly _onDidChangeModelDecorations: Emitter = this._register(new Emitter()); + public readonly onDidChangeModelDecorations: Event = this._onDidChangeModelDecorations.event; + + private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; + + protected readonly _onDidChangeModel: Emitter = this._register(new Emitter()); + public readonly onDidChangeModel: Event = this._onDidChangeModel.event; + + private readonly _onDidChangeCursorPosition: Emitter = this._register(new Emitter()); + public readonly onDidChangeCursorPosition: Event = this._onDidChangeCursorPosition.event; + + private readonly _onDidChangeCursorSelection: Emitter = this._register(new Emitter()); + public readonly onDidChangeCursorSelection: Event = this._onDidChangeCursorSelection.event; + + private readonly _onDidLayoutChange: Emitter = this._register(new Emitter()); + public readonly onDidLayoutChange: Event = this._onDidLayoutChange.event; + + protected readonly _onDidFocusEditorText: Emitter = this._register(new Emitter()); + public readonly onDidFocusEditorText: Event = this._onDidFocusEditorText.event; + + protected readonly _onDidBlurEditorText: Emitter = this._register(new Emitter()); + public readonly onDidBlurEditorText: Event = this._onDidBlurEditorText.event; + + protected readonly _onDidFocusEditor: Emitter = this._register(new Emitter()); + public readonly onDidFocusEditor: Event = this._onDidFocusEditor.event; + + protected readonly _onDidBlurEditor: Emitter = this._register(new Emitter()); + public readonly onDidBlurEditor: Event = this._onDidBlurEditor.event; + + private readonly _onWillType: Emitter = this._register(new Emitter()); + public readonly onWillType = this._onWillType.event; + + private readonly _onDidType: Emitter = this._register(new Emitter()); + public readonly onDidType = this._onDidType.event; + + private readonly _onDidPaste: Emitter = this._register(new Emitter()); + public readonly onDidPaste = this._onDidPaste.event; + + + protected readonly domElement: IContextKeyServiceTarget; + protected readonly id: number; + protected readonly _configuration: CommonEditorConfiguration; + + protected _contributions: { [key: string]: editorCommon.IEditorContribution; }; + protected _actions: { [key: string]: editorCommon.IEditorAction; }; + + // --- Members logically associated to a model + protected model: editorCommon.IModel; + protected listenersToRemove: IDisposable[]; + protected hasView: boolean; + + protected viewModel: ViewModel; + protected cursor: Cursor; + + protected readonly _instantiationService: IInstantiationService; + protected readonly _contextKeyService: IContextKeyService; + + /** + * map from "parent" decoration type to live decoration ids. + */ + private _decorationTypeKeysToIds: { [decorationTypeKey: string]: string[] }; + private _decorationTypeSubtypes: { [decorationTypeKey: string]: { [subtype: string]: boolean } }; + + + constructor( + domElement: IContextKeyServiceTarget, + options: editorOptions.IEditorOptions, + instantiationService: IInstantiationService, + contextKeyService: IContextKeyService + ) { + super(); + this.domElement = domElement; + this.id = (++EDITOR_ID); + this._decorationTypeKeysToIds = {}; + this._decorationTypeSubtypes = {}; + + options = options || {}; + this._configuration = this._register(this._createConfiguration(options)); + this._register(this._configuration.onDidChange((e) => { + this._onDidChangeConfiguration.fire(e); + + if (e.layoutInfo) { + this._onDidLayoutChange.fire(this._configuration.editor.layoutInfo); + } + })); + + this._contextKeyService = this._register(contextKeyService.createScoped(this.domElement)); + this._register(new EditorContextKeysManager(this, this._contextKeyService)); + this._register(new EditorModeContext(this, this._contextKeyService)); + + this._instantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, this._contextKeyService])); + + this._attachModel(null); + + this._contributions = {}; + this._actions = {}; + } + + protected abstract _createConfiguration(options: editorOptions.IEditorOptions): CommonEditorConfiguration; + + public getId(): string { + return this.getEditorType() + ':' + this.id; + } + + public getEditorType(): string { + return editorCommon.EditorType.ICodeEditor; + } + + public destroy(): void { + this.dispose(); + } + + public dispose(): void { + let keys = Object.keys(this._contributions); + for (let i = 0, len = keys.length; i < len; i++) { + let contributionId = keys[i]; + this._contributions[contributionId].dispose(); + } + this._contributions = {}; + + // editor actions don't need to be disposed + this._actions = {}; + + this._postDetachModelCleanup(this._detachModel()); + + this._onDidDispose.fire(); + + super.dispose(); + } + + public invokeWithinContext(fn: (accessor: ServicesAccessor) => T): T { + return this._instantiationService.invokeFunction(fn); + } + + public updateOptions(newOptions: editorOptions.IEditorOptions): void { + this._configuration.updateOptions(newOptions); + } + + public getConfiguration(): editorOptions.InternalEditorOptions { + return this._configuration.editor; + } + + public getRawConfiguration(): editorOptions.IEditorOptions { + return this._configuration.getRawOptions(); + } + + public getValue(options: { preserveBOM: boolean; lineEnding: string; } = null): string { + if (this.model) { + let preserveBOM: boolean = (options && options.preserveBOM) ? true : false; + let eolPreference = editorCommon.EndOfLinePreference.TextDefined; + if (options && options.lineEnding && options.lineEnding === '\n') { + eolPreference = editorCommon.EndOfLinePreference.LF; + } else if (options && options.lineEnding && options.lineEnding === '\r\n') { + eolPreference = editorCommon.EndOfLinePreference.CRLF; + } + return this.model.getValue(eolPreference, preserveBOM); + } + return ''; + } + + public setValue(newValue: string): void { + if (this.model) { + this.model.setValue(newValue); + } + } + + public getModel(): editorCommon.IModel { + return this.model; + } + + public setModel(model: editorCommon.IModel = null): void { + if (this.model === model) { + // Current model is the new model + return; + } + + let detachedModel = this._detachModel(); + this._attachModel(model); + + let e: editorCommon.IModelChangedEvent = { + oldModelUrl: detachedModel ? detachedModel.uri : null, + newModelUrl: model ? model.uri : null + }; + + this._onDidChangeModel.fire(e); + this._postDetachModelCleanup(detachedModel); + } + + public getCenteredRangeInViewport(): Range { + if (!this.hasView) { + return null; + } + return this.viewModel.getCenteredRangeInViewport(); + } + + public getVisibleColumnFromPosition(rawPosition: IPosition): number { + if (!this.model) { + return rawPosition.column; + } + + let position = this.model.validatePosition(rawPosition); + let tabSize = this.model.getOptions().tabSize; + + return CursorColumns.visibleColumnFromColumn(this.model.getLineContent(position.lineNumber), position.column, tabSize) + 1; + } + + public getPosition(): Position { + if (!this.cursor) { + return null; + } + return this.cursor.getPosition().clone(); + } + + public setPosition(position: IPosition): void { + if (!this.cursor) { + return; + } + if (!Position.isIPosition(position)) { + throw new Error('Invalid arguments'); + } + this.cursor.setSelections('api', [{ + selectionStartLineNumber: position.lineNumber, + selectionStartColumn: position.column, + positionLineNumber: position.lineNumber, + positionColumn: position.column + }]); + } + + private _sendRevealRange(modelRange: Range, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void { + if (!this.model || !this.cursor) { + return; + } + if (!Range.isIRange(modelRange)) { + throw new Error('Invalid arguments'); + } + const validatedModelRange = this.model.validateRange(modelRange); + const viewRange = this.viewModel.coordinatesConverter.convertModelRangeToViewRange(validatedModelRange); + + this.cursor.emitCursorRevealRange(viewRange, verticalType, revealHorizontal, scrollType); + } + + public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealLine(lineNumber, VerticalRevealType.Simple, scrollType); + } + + public revealLineInCenter(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealLine(lineNumber, VerticalRevealType.Center, scrollType); + } + + public revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealLine(lineNumber, VerticalRevealType.CenterIfOutsideViewport, scrollType); + } + + private _revealLine(lineNumber: number, revealType: VerticalRevealType, scrollType: editorCommon.ScrollType): void { + if (typeof lineNumber !== 'number') { + throw new Error('Invalid arguments'); + } + + this._sendRevealRange( + new Range(lineNumber, 1, lineNumber, 1), + revealType, + false, + scrollType + ); + } + + public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealPosition( + position, + VerticalRevealType.Simple, + true, + scrollType + ); + } + + public revealPositionInCenter(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealPosition( + position, + VerticalRevealType.Center, + true, + scrollType + ); + } + + public revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealPosition( + position, + VerticalRevealType.CenterIfOutsideViewport, + true, + scrollType + ); + } + + private _revealPosition(position: IPosition, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void { + if (!Position.isIPosition(position)) { + throw new Error('Invalid arguments'); + } + + this._sendRevealRange( + new Range(position.lineNumber, position.column, position.lineNumber, position.column), + verticalType, + revealHorizontal, + scrollType + ); + } + + public getSelection(): Selection { + if (!this.cursor) { + return null; + } + return this.cursor.getSelection().clone(); + } + + public getSelections(): Selection[] { + if (!this.cursor) { + return null; + } + let selections = this.cursor.getSelections(); + let result: Selection[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + result[i] = selections[i].clone(); + } + return result; + } + + public setSelection(range: IRange): void; + public setSelection(editorRange: Range): void; + public setSelection(selection: ISelection): void; + public setSelection(editorSelection: Selection): void; + public setSelection(something: any): void { + let isSelection = Selection.isISelection(something); + let isRange = Range.isIRange(something); + + if (!isSelection && !isRange) { + throw new Error('Invalid arguments'); + } + + if (isSelection) { + this._setSelectionImpl(something); + } else if (isRange) { + // act as if it was an IRange + let selection: ISelection = { + selectionStartLineNumber: something.startLineNumber, + selectionStartColumn: something.startColumn, + positionLineNumber: something.endLineNumber, + positionColumn: something.endColumn + }; + this._setSelectionImpl(selection); + } + } + + private _setSelectionImpl(sel: ISelection): void { + if (!this.cursor) { + return; + } + let selection = new Selection(sel.selectionStartLineNumber, sel.selectionStartColumn, sel.positionLineNumber, sel.positionColumn); + this.cursor.setSelections('api', [selection]); + } + + public revealLines(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealLines( + startLineNumber, + endLineNumber, + VerticalRevealType.Simple, + scrollType + ); + } + + public revealLinesInCenter(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealLines( + startLineNumber, + endLineNumber, + VerticalRevealType.Center, + scrollType + ); + } + + public revealLinesInCenterIfOutsideViewport(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealLines( + startLineNumber, + endLineNumber, + VerticalRevealType.CenterIfOutsideViewport, + scrollType + ); + } + + private _revealLines(startLineNumber: number, endLineNumber: number, verticalType: VerticalRevealType, scrollType: editorCommon.ScrollType): void { + if (typeof startLineNumber !== 'number' || typeof endLineNumber !== 'number') { + throw new Error('Invalid arguments'); + } + + this._sendRevealRange( + new Range(startLineNumber, 1, endLineNumber, 1), + verticalType, + false, + scrollType + ); + } + + public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void { + this._revealRange( + range, + false ? VerticalRevealType.Center : VerticalRevealType.Simple, + revealHorizontal, + scrollType + ); + } + + public revealRangeInCenter(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealRange( + range, + VerticalRevealType.Center, + true, + scrollType + ); + } + + public revealRangeInCenterIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealRange( + range, + VerticalRevealType.CenterIfOutsideViewport, + true, + scrollType + ); + } + + public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealRange( + range, + VerticalRevealType.Top, + true, + scrollType + ); + } + + private _revealRange(range: IRange, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void { + if (!Range.isIRange(range)) { + throw new Error('Invalid arguments'); + } + + this._sendRevealRange( + Range.lift(range), + verticalType, + revealHorizontal, + scrollType + ); + } + + public setSelections(ranges: ISelection[]): void { + if (!this.cursor) { + return; + } + if (!ranges || ranges.length === 0) { + throw new Error('Invalid arguments'); + } + for (let i = 0, len = ranges.length; i < len; i++) { + if (!Selection.isISelection(ranges[i])) { + throw new Error('Invalid arguments'); + } + } + this.cursor.setSelections('api', ranges); + } + + public getScrollWidth(): number { + if (!this.hasView) { + return -1; + } + return this.viewModel.viewLayout.getScrollWidth(); + } + public getScrollLeft(): number { + if (!this.hasView) { + return -1; + } + return this.viewModel.viewLayout.getCurrentScrollLeft(); + } + + public getScrollHeight(): number { + if (!this.hasView) { + return -1; + } + return this.viewModel.viewLayout.getScrollHeight(); + } + public getScrollTop(): number { + if (!this.hasView) { + return -1; + } + return this.viewModel.viewLayout.getCurrentScrollTop(); + } + + public setScrollLeft(newScrollLeft: number): void { + if (!this.hasView) { + return; + } + if (typeof newScrollLeft !== 'number') { + throw new Error('Invalid arguments'); + } + this.viewModel.viewLayout.setScrollPositionNow({ + scrollLeft: newScrollLeft + }); + } + public setScrollTop(newScrollTop: number): void { + if (!this.hasView) { + return; + } + if (typeof newScrollTop !== 'number') { + throw new Error('Invalid arguments'); + } + this.viewModel.viewLayout.setScrollPositionNow({ + scrollTop: newScrollTop + }); + } + public setScrollPosition(position: editorCommon.INewScrollPosition): void { + if (!this.hasView) { + return; + } + this.viewModel.viewLayout.setScrollPositionNow(position); + } + + public saveViewState(): editorCommon.ICodeEditorViewState { + if (!this.cursor || !this.hasView) { + return null; + } + let contributionsState: { [key: string]: any } = {}; + + let keys = Object.keys(this._contributions); + for (let i = 0, len = keys.length; i < len; i++) { + let id = keys[i]; + let contribution = this._contributions[id]; + if (typeof contribution.saveViewState === 'function') { + contributionsState[id] = contribution.saveViewState(); + } + } + + let cursorState = this.cursor.saveState(); + let viewState = this.viewModel.viewLayout.saveState(); + return { + cursorState: cursorState, + viewState: viewState, + contributionsState: contributionsState + }; + } + + public restoreViewState(s: editorCommon.ICodeEditorViewState): void { + if (!this.cursor || !this.hasView) { + return; + } + if (s && s.cursorState && s.viewState) { + let codeEditorState = s; + let cursorState = codeEditorState.cursorState; + if (Array.isArray(cursorState)) { + this.cursor.restoreState(cursorState); + } else { + // Backwards compatibility + this.cursor.restoreState([cursorState]); + } + this.viewModel.viewLayout.restoreState(codeEditorState.viewState); + + let contributionsState = s.contributionsState || {}; + let keys = Object.keys(this._contributions); + for (let i = 0, len = keys.length; i < len; i++) { + let id = keys[i]; + let contribution = this._contributions[id]; + if (typeof contribution.restoreViewState === 'function') { + contribution.restoreViewState(contributionsState[id]); + } + } + } + } + + public onVisible(): void { + } + + public onHide(): void { + } + + public abstract layout(dimension?: editorCommon.IDimension): void; + + public abstract focus(): void; + public abstract isFocused(): boolean; + public abstract hasWidgetFocus(): boolean; + + public getContribution(id: string): T { + return (this._contributions[id] || null); + } + + public getActions(): editorCommon.IEditorAction[] { + let result: editorCommon.IEditorAction[] = []; + + let keys = Object.keys(this._actions); + for (let i = 0, len = keys.length; i < len; i++) { + let id = keys[i]; + result.push(this._actions[id]); + } + + return result; + } + + public getSupportedActions(): editorCommon.IEditorAction[] { + let result = this.getActions(); + + result = result.filter(action => action.isSupported()); + + return result; + } + + public getAction(id: string): editorCommon.IEditorAction { + return this._actions[id] || null; + } + + public trigger(source: string, handlerId: string, payload: any): void { + payload = payload || {}; + + // Special case for typing + if (handlerId === editorCommon.Handler.Type) { + if (!this.cursor || typeof payload.text !== 'string' || payload.text.length === 0) { + // nothing to do + return; + } + if (source === 'keyboard') { + this._onWillType.fire(payload.text); + } + this.cursor.trigger(source, handlerId, payload); + if (source === 'keyboard') { + this._onDidType.fire(payload.text); + } + return; + } + + // Special case for pasting + if (handlerId === editorCommon.Handler.Paste) { + if (!this.cursor || typeof payload.text !== 'string' || payload.text.length === 0) { + // nothing to do + return; + } + const startPosition = this.cursor.getSelection().getStartPosition(); + this.cursor.trigger(source, handlerId, payload); + const endPosition = this.cursor.getSelection().getStartPosition(); + if (source === 'keyboard') { + this._onDidPaste.fire( + new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column) + ); + } + return; + } + + const action = this.getAction(handlerId); + if (action) { + TPromise.as(action.run()).done(null, onUnexpectedError); + return; + } + + if (!this.cursor) { + return; + } + + const command = CommonEditorRegistry.getEditorCommand(handlerId); + if (command) { + payload = payload || {}; + payload.source = source; + TPromise.as(command.runEditorCommand(null, this, payload)).done(null, onUnexpectedError); + return; + } + + this.cursor.trigger(source, handlerId, payload); + } + + public _getCursors(): ICursors { + return this.cursor; + } + + public _getCursorConfiguration(): CursorConfiguration { + return this.cursor.context.config; + } + + public pushUndoStop(): boolean { + if (!this.model) { + return false; + } + if (this._configuration.editor.readOnly) { + // read only editor => sorry! + return false; + } + this.model.pushStackElement(); + return true; + } + + public executeEdits(source: string, edits: editorCommon.IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean { + if (!this.cursor) { + // no view, no cursor + return false; + } + if (this._configuration.editor.readOnly) { + // read only editor => sorry! + return false; + } + + this.model.pushEditOperations(this.cursor.getSelections(), edits, () => { + return endCursorState ? endCursorState : this.cursor.getSelections(); + }); + + if (endCursorState) { + this.cursor.setSelections(source, endCursorState); + } + + return true; + } + + public executeCommand(source: string, command: editorCommon.ICommand): void { + if (!this.cursor) { + return; + } + this.cursor.trigger(source, editorCommon.Handler.ExecuteCommand, command); + } + + public executeCommands(source: string, commands: editorCommon.ICommand[]): void { + if (!this.cursor) { + return; + } + this.cursor.trigger(source, editorCommon.Handler.ExecuteCommands, commands); + } + + public changeDecorations(callback: (changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => any): any { + if (!this.model) { + // console.warn('Cannot change decorations on editor that is not attached to a model'); + // callback will not be called + return null; + } + return this.model.changeDecorations(callback, this.id); + } + + public getLineDecorations(lineNumber: number): editorCommon.IModelDecoration[] { + if (!this.model) { + return null; + } + return this.model.getLineDecorations(lineNumber, this.id, this._configuration.editor.readOnly); + } + + public deltaDecorations(oldDecorations: string[], newDecorations: editorCommon.IModelDeltaDecoration[]): string[] { + if (!this.model) { + return []; + } + + if (oldDecorations.length === 0 && newDecorations.length === 0) { + return oldDecorations; + } + + return this.model.deltaDecorations(oldDecorations, newDecorations, this.id); + } + + public setDecorations(decorationTypeKey: string, decorationOptions: editorCommon.IDecorationOptions[]): void { + + let newDecorationsSubTypes: { [key: string]: boolean } = {}; + let oldDecorationsSubTypes = this._decorationTypeSubtypes[decorationTypeKey] || {}; + this._decorationTypeSubtypes[decorationTypeKey] = newDecorationsSubTypes; + + let newModelDecorations: editorCommon.IModelDeltaDecoration[] = []; + + for (let decorationOption of decorationOptions) { + let typeKey = decorationTypeKey; + if (decorationOption.renderOptions) { + // identify custom reder options by a hash code over all keys and values + // For custom render options register a decoration type if necessary + let subType = hash(decorationOption.renderOptions).toString(16); + // The fact that `decorationTypeKey` appears in the typeKey has no influence + // it is just a mechanism to get predictable and unique keys (repeatable for the same options and unique across clients) + typeKey = decorationTypeKey + '-' + subType; + if (!oldDecorationsSubTypes[subType] && !newDecorationsSubTypes[subType]) { + // decoration type did not exist before, register new one + this._registerDecorationType(typeKey, decorationOption.renderOptions, decorationTypeKey); + } + newDecorationsSubTypes[subType] = true; + } + let opts = this._resolveDecorationOptions(typeKey, !!decorationOption.hoverMessage); + if (decorationOption.hoverMessage) { + opts.hoverMessage = decorationOption.hoverMessage; + } + newModelDecorations.push({ range: decorationOption.range, options: opts }); + } + + // remove decoration sub types that are no longer used, deregister decoration type if necessary + for (let subType in oldDecorationsSubTypes) { + if (!newDecorationsSubTypes[subType]) { + this._removeDecorationType(decorationTypeKey + '-' + subType); + } + } + + // update all decorations + let oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey] || []; + this._decorationTypeKeysToIds[decorationTypeKey] = this.deltaDecorations(oldDecorationsIds, newModelDecorations); + } + + public removeDecorations(decorationTypeKey: string): void { + // remove decorations for type and sub type + let oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey]; + if (oldDecorationsIds) { + this.deltaDecorations(oldDecorationsIds, []); + } + if (this._decorationTypeKeysToIds.hasOwnProperty(decorationTypeKey)) { + delete this._decorationTypeKeysToIds[decorationTypeKey]; + } + if (this._decorationTypeSubtypes.hasOwnProperty(decorationTypeKey)) { + delete this._decorationTypeSubtypes[decorationTypeKey]; + } + } + + public getLayoutInfo(): editorOptions.EditorLayoutInfo { + return this._configuration.editor.layoutInfo; + } + + protected _attachModel(model: editorCommon.IModel): void { + this.model = model ? model : null; + this.listenersToRemove = []; + this.viewModel = null; + this.cursor = null; + + if (this.model) { + this.domElement.setAttribute('data-mode-id', this.model.getLanguageIdentifier().language); + this._configuration.setIsDominatedByLongLines(this.model.isDominatedByLongLines()); + this._configuration.setMaxLineNumber(this.model.getLineCount()); + + this.model.onBeforeAttached(); + + this.viewModel = new ViewModel(this.id, this._configuration, this.model, (callback) => this._scheduleAtNextAnimationFrame(callback)); + + this.listenersToRemove.push(this.model.addBulkListener((events) => { + for (let i = 0, len = events.length; i < len; i++) { + let eventType = events[i].type; + let e = events[i].data; + + switch (eventType) { + case TextModelEventType.ModelDecorationsChanged: + this._onDidChangeModelDecorations.fire(e); + break; + + case TextModelEventType.ModelLanguageChanged: + this.domElement.setAttribute('data-mode-id', this.model.getLanguageIdentifier().language); + this._onDidChangeModelLanguage.fire(e); + break; + + case TextModelEventType.ModelContentChanged: + this._onDidChangeModelContent.fire(e); + break; + + case TextModelEventType.ModelOptionsChanged: + this._onDidChangeModelOptions.fire(e); + break; + + case TextModelEventType.ModelDispose: + // Someone might destroy the model from under the editor, so prevent any exceptions by setting a null model + this.setModel(null); + break; + + default: + // console.warn("Unhandled model event: ", e); + } + } + })); + + this.cursor = new Cursor( + this._configuration, + this.model, + this.viewModel + ); + + this._createView(); + + this.listenersToRemove.push(this.cursor.onDidChange((e: CursorStateChangedEvent) => { + + let positions: Position[] = []; + for (let i = 0, len = e.selections.length; i < len; i++) { + positions[i] = e.selections[i].getPosition(); + } + + const e1: ICursorPositionChangedEvent = { + position: positions[0], + secondaryPositions: positions.slice(1), + reason: e.reason, + source: e.source + }; + this._onDidChangeCursorPosition.fire(e1); + + const e2: ICursorSelectionChangedEvent = { + selection: e.selections[0], + secondarySelections: e.selections.slice(1), + source: e.source, + reason: e.reason + }; + this._onDidChangeCursorSelection.fire(e2); + })); + + } else { + this.hasView = false; + } + } + + protected abstract _scheduleAtNextAnimationFrame(callback: () => void): IDisposable; + protected abstract _createView(): void; + + protected _postDetachModelCleanup(detachedModel: editorCommon.IModel): void { + if (detachedModel) { + this._decorationTypeKeysToIds = {}; + if (this._decorationTypeSubtypes) { + for (let decorationType in this._decorationTypeSubtypes) { + let subTypes = this._decorationTypeSubtypes[decorationType]; + for (let subType in subTypes) { + this._removeDecorationType(decorationType + '-' + subType); + } + } + this._decorationTypeSubtypes = {}; + } + detachedModel.removeAllDecorationsWithOwnerId(this.id); + } + } + + protected _detachModel(): editorCommon.IModel { + if (this.model) { + this.model.onBeforeDetached(); + } + + this.hasView = false; + + this.listenersToRemove = dispose(this.listenersToRemove); + + if (this.cursor) { + this.cursor.dispose(); + this.cursor = null; + } + + if (this.viewModel) { + this.viewModel.dispose(); + this.viewModel = null; + } + + let result = this.model; + this.model = null; + + this.domElement.removeAttribute('data-mode-id'); + + return result; + } + + protected abstract _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void; + protected abstract _removeDecorationType(key: string): void; + protected abstract _resolveDecorationOptions(typeKey: string, writable: boolean): editorCommon.IModelDecorationOptions; + + public getTelemetryData(): { [key: string]: any; } { + return null; + } +} + +class EditorContextKeysManager extends Disposable { + + private _editor: CommonCodeEditor; + + private _editorId: IContextKey; + private _editorFocus: IContextKey; + private _editorTextFocus: IContextKey; + private _editorTabMovesFocus: IContextKey; + private _editorReadonly: IContextKey; + private _hasMultipleSelections: IContextKey; + private _hasNonEmptySelection: IContextKey; + + constructor( + editor: CommonCodeEditor, + contextKeyService: IContextKeyService + ) { + super(); + + this._editor = editor; + + this._editorId = contextKeyService.createKey('editorId', editor.getId()); + this._editorFocus = EditorContextKeys.focus.bindTo(contextKeyService); + this._editorTextFocus = EditorContextKeys.textFocus.bindTo(contextKeyService); + this._editorTabMovesFocus = EditorContextKeys.tabMovesFocus.bindTo(contextKeyService); + this._editorReadonly = EditorContextKeys.readOnly.bindTo(contextKeyService); + this._hasMultipleSelections = EditorContextKeys.hasMultipleSelections.bindTo(contextKeyService); + this._hasNonEmptySelection = EditorContextKeys.hasNonEmptySelection.bindTo(contextKeyService); + + this._register(this._editor.onDidChangeConfiguration(() => this._updateFromConfig())); + this._register(this._editor.onDidChangeCursorSelection(() => this._updateFromSelection())); + this._register(this._editor.onDidFocusEditor(() => this._updateFromFocus())); + this._register(this._editor.onDidBlurEditor(() => this._updateFromFocus())); + this._register(this._editor.onDidFocusEditorText(() => this._updateFromFocus())); + this._register(this._editor.onDidBlurEditorText(() => this._updateFromFocus())); + + this._updateFromConfig(); + this._updateFromSelection(); + this._updateFromFocus(); + } + + private _updateFromConfig(): void { + let config = this._editor.getConfiguration(); + + this._editorTabMovesFocus.set(config.tabFocusMode); + this._editorReadonly.set(config.readOnly); + } + + private _updateFromSelection(): void { + let selections = this._editor.getSelections(); + if (!selections) { + this._hasMultipleSelections.reset(); + this._hasNonEmptySelection.reset(); + } else { + this._hasMultipleSelections.set(selections.length > 1); + this._hasNonEmptySelection.set(selections.some(s => !s.isEmpty())); + } + } + + private _updateFromFocus(): void { + this._editorFocus.set(this._editor.hasWidgetFocus()); + this._editorTextFocus.set(this._editor.isFocused()); + } +} diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts new file mode 100644 index 0000000000..30aefa6d88 --- /dev/null +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -0,0 +1,627 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as objects from 'vs/base/common/objects'; +import * as platform from 'vs/base/common/platform'; +import { Extensions, IConfigurationRegistry, IConfigurationNode, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo'; +import { EditorZoom } from 'vs/editor/common/config/editorZoom'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import EDITOR_DEFAULTS = editorOptions.EDITOR_DEFAULTS; +import EDITOR_FONT_DEFAULTS = editorOptions.EDITOR_FONT_DEFAULTS; +import EDITOR_MODEL_DEFAULTS = editorOptions.EDITOR_MODEL_DEFAULTS; + +/** + * Control what pressing Tab does. + * If it is false, pressing Tab or Shift-Tab will be handled by the editor. + * If it is true, pressing Tab or Shift-Tab will move the browser focus. + * Defaults to false. + */ +export interface ITabFocus { + onDidChangeTabFocus: Event; + getTabFocusMode(): boolean; + setTabFocusMode(tabFocusMode: boolean): void; +} + +export const TabFocus: ITabFocus = new class { + private _tabFocus: boolean = false; + + private _onDidChangeTabFocus: Emitter = new Emitter(); + public onDidChangeTabFocus: Event = this._onDidChangeTabFocus.event; + + public getTabFocusMode(): boolean { + return this._tabFocus; + } + + public setTabFocusMode(tabFocusMode: boolean): void { + if (this._tabFocus === tabFocusMode) { + return; + } + + this._tabFocus = tabFocusMode; + this._onDidChangeTabFocus.fire(this._tabFocus); + } +}; + +export interface IEnvConfiguration { + extraEditorClassName: string; + outerWidth: number; + outerHeight: number; + emptySelectionClipboard: boolean; + pixelRatio: number; + zoomLevel: number; + accessibilitySupport: platform.AccessibilitySupport; +} + +export abstract class CommonEditorConfiguration extends Disposable implements editorCommon.IConfiguration { + + protected _rawOptions: editorOptions.IEditorOptions; + protected _validatedOptions: editorOptions.IValidatedEditorOptions; + public editor: editorOptions.InternalEditorOptions; + private _isDominatedByLongLines: boolean; + private _lineNumbersDigitCount: number; + + private _onDidChange = this._register(new Emitter()); + public onDidChange: Event = this._onDidChange.event; + + constructor(options: editorOptions.IEditorOptions) { + super(); + + // Do a "deep clone of sorts" on the incoming options + this._rawOptions = objects.mixin({}, options || {}); + this._rawOptions.scrollbar = objects.mixin({}, this._rawOptions.scrollbar || {}); + this._rawOptions.minimap = objects.mixin({}, this._rawOptions.minimap || {}); + this._rawOptions.find = objects.mixin({}, this._rawOptions.find || {}); + + this._validatedOptions = editorOptions.EditorOptionsValidator.validate(this._rawOptions, EDITOR_DEFAULTS); + this.editor = null; + this._isDominatedByLongLines = false; + this._lineNumbersDigitCount = 1; + + this._register(EditorZoom.onDidChangeZoomLevel(_ => this._recomputeOptions())); + this._register(TabFocus.onDidChangeTabFocus(_ => this._recomputeOptions())); + } + + public dispose(): void { + super.dispose(); + } + + protected _recomputeOptions(): void { + const oldOptions = this.editor; + const newOptions = this._computeInternalOptions(); + + if (oldOptions && oldOptions.equals(newOptions)) { + return; + } + + this.editor = newOptions; + + if (oldOptions) { + this._onDidChange.fire(oldOptions.createChangeEvent(newOptions)); + } + } + + public getRawOptions(): editorOptions.IEditorOptions { + return this._rawOptions; + } + + private _computeInternalOptions(): editorOptions.InternalEditorOptions { + const opts = this._validatedOptions; + const partialEnv = this._getEnvConfiguration(); + const bareFontInfo = BareFontInfo.createFromRawSettings(this._rawOptions, partialEnv.zoomLevel); + const env: editorOptions.IEnvironmentalOptions = { + outerWidth: partialEnv.outerWidth, + outerHeight: partialEnv.outerHeight, + fontInfo: this.readConfiguration(bareFontInfo), + extraEditorClassName: partialEnv.extraEditorClassName, + isDominatedByLongLines: this._isDominatedByLongLines, + lineNumbersDigitCount: this._lineNumbersDigitCount, + emptySelectionClipboard: partialEnv.emptySelectionClipboard, + pixelRatio: partialEnv.pixelRatio, + tabFocusMode: TabFocus.getTabFocusMode(), + accessibilitySupport: partialEnv.accessibilitySupport + }; + return editorOptions.InternalEditorOptionsFactory.createInternalEditorOptions(env, opts); + } + + public updateOptions(newOptions: editorOptions.IEditorOptions): void { + this._rawOptions = objects.mixin(this._rawOptions, newOptions || {}); + this._validatedOptions = editorOptions.EditorOptionsValidator.validate(this._rawOptions, EDITOR_DEFAULTS); + this._recomputeOptions(); + } + + public setIsDominatedByLongLines(isDominatedByLongLines: boolean): void { + this._isDominatedByLongLines = isDominatedByLongLines; + this._recomputeOptions(); + } + + public setMaxLineNumber(maxLineNumber: number): void { + let digitCount = CommonEditorConfiguration._digitCount(maxLineNumber); + if (this._lineNumbersDigitCount === digitCount) { + return; + } + this._lineNumbersDigitCount = digitCount; + this._recomputeOptions(); + } + + private static _digitCount(n: number): number { + var r = 0; + while (n) { + n = Math.floor(n / 10); + r++; + } + return r ? r : 1; + } + protected abstract _getEnvConfiguration(): IEnvConfiguration; + + protected abstract readConfiguration(styling: BareFontInfo): FontInfo; + +} + +const configurationRegistry = Registry.as(Extensions.Configuration); +const editorConfiguration: IConfigurationNode = { + 'id': 'editor', + 'order': 5, + 'type': 'object', + 'title': nls.localize('editorConfigurationTitle', "Editor"), + 'overridable': true, + 'scope': ConfigurationScope.RESOURCE, + 'properties': { + 'editor.fontFamily': { + 'type': 'string', + 'default': EDITOR_FONT_DEFAULTS.fontFamily, + 'description': nls.localize('fontFamily', "Controls the font family.") + }, + 'editor.fontWeight': { + 'type': 'string', + 'enum': ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], + 'default': EDITOR_FONT_DEFAULTS.fontWeight, + 'description': nls.localize('fontWeight', "Controls the font weight.") + }, + 'editor.fontSize': { + 'type': 'number', + 'default': EDITOR_FONT_DEFAULTS.fontSize, + 'description': nls.localize('fontSize', "Controls the font size in pixels.") + }, + 'editor.lineHeight': { + 'type': 'number', + 'default': EDITOR_FONT_DEFAULTS.lineHeight, + 'description': nls.localize('lineHeight', "Controls the line height. Use 0 to compute the lineHeight from the fontSize.") + }, + 'editor.letterSpacing': { + 'type': 'number', + 'default': EDITOR_FONT_DEFAULTS.letterSpacing, + 'description': nls.localize('letterSpacing', "Controls the letter spacing in pixels.") + }, + 'editor.lineNumbers': { + 'type': 'string', + 'enum': ['off', 'on', 'relative'], + 'default': 'on', + 'description': nls.localize('lineNumbers', "Controls the display of line numbers. Possible values are 'on', 'off', and 'relative'. 'relative' shows the line count from the current cursor position.") + }, + 'editor.rulers': { + 'type': 'array', + 'items': { + 'type': 'number' + }, + 'default': EDITOR_DEFAULTS.viewInfo.rulers, + 'description': nls.localize('rulers', "Columns at which to show vertical rulers") + }, + 'editor.wordSeparators': { + 'type': 'string', + 'default': EDITOR_DEFAULTS.wordSeparators, + 'description': nls.localize('wordSeparators', "Characters that will be used as word separators when doing word related navigations or operations") + }, + 'editor.tabSize': { + 'type': 'number', + 'default': EDITOR_MODEL_DEFAULTS.tabSize, + 'minimum': 1, + 'description': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overriden based on the file contents when `editor.detectIndentation` is on."), + 'errorMessage': nls.localize('tabSize.errorMessage', "Expected 'number'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.") + }, + 'editor.insertSpaces': { + 'type': 'boolean', + 'default': EDITOR_MODEL_DEFAULTS.insertSpaces, + 'description': nls.localize('insertSpaces', "Insert spaces when pressing Tab. This setting is overriden based on the file contents when `editor.detectIndentation` is on."), + 'errorMessage': nls.localize('insertSpaces.errorMessage', "Expected 'boolean'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.") + }, + 'editor.detectIndentation': { + 'type': 'boolean', + 'default': EDITOR_MODEL_DEFAULTS.detectIndentation, + 'description': nls.localize('detectIndentation', "When opening a file, `editor.tabSize` and `editor.insertSpaces` will be detected based on the file contents.") + }, + 'editor.roundedSelection': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.roundedSelection, + 'description': nls.localize('roundedSelection', "Controls if selections have rounded corners") + }, + 'editor.scrollBeyondLastLine': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.scrollBeyondLastLine, + 'description': nls.localize('scrollBeyondLastLine', "Controls if the editor will scroll beyond the last line") + }, + 'editor.smoothScrolling': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.smoothScrolling, + 'description': nls.localize('smoothScrolling', "Controls if the editor will scroll using an animation") + }, + 'editor.minimap.enabled': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.minimap.enabled, + 'description': nls.localize('minimap.enabled', "Controls if the minimap is shown") + }, + 'editor.minimap.showSlider': { + 'type': 'string', + 'enum': ['always', 'mouseover'], + 'default': EDITOR_DEFAULTS.viewInfo.minimap.showSlider, + 'description': nls.localize('minimap.showSlider', "Controls whether the minimap slider is automatically hidden. Possible values are \'always\' and \'mouseover\'") + }, + 'editor.minimap.renderCharacters': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.minimap.renderCharacters, + 'description': nls.localize('minimap.renderCharacters', "Render the actual characters on a line (as opposed to color blocks)") + }, + 'editor.minimap.maxColumn': { + 'type': 'number', + 'default': EDITOR_DEFAULTS.viewInfo.minimap.maxColumn, + 'description': nls.localize('minimap.maxColumn', "Limit the width of the minimap to render at most a certain number of columns") + }, + 'editor.find.seedSearchStringFromSelection': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.find.seedSearchStringFromSelection, + 'description': nls.localize('find.seedSearchStringFromSelection', "Controls if we seed the search string in Find Widget from editor selection") + }, + 'editor.find.autoFindInSelection': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.find.autoFindInSelection, + 'description': nls.localize('find.autoFindInSelection', "Controls if Find in Selection flag is turned on when multiple characters or lines of text are selected in the editor") + }, + 'editor.wordWrap': { + 'type': 'string', + 'enum': ['off', 'on', 'wordWrapColumn', 'bounded'], + 'enumDescriptions': [ + nls.localize('wordWrap.off', "Lines will never wrap."), + nls.localize('wordWrap.on', "Lines will wrap at the viewport width."), + nls.localize({ + key: 'wordWrap.wordWrapColumn', + comment: [ + '- `editor.wordWrapColumn` refers to a different setting and should not be localized.' + ] + }, "Lines will wrap at `editor.wordWrapColumn`."), + nls.localize({ + key: 'wordWrap.bounded', + comment: [ + '- viewport means the edge of the visible window size.', + '- `editor.wordWrapColumn` refers to a different setting and should not be localized.' + ] + }, "Lines will wrap at the minimum of viewport and `editor.wordWrapColumn`."), + ], + 'default': EDITOR_DEFAULTS.wordWrap, + 'description': nls.localize({ + key: 'wordWrap', + comment: [ + '- \'off\', \'on\', \'wordWrapColumn\' and \'bounded\' refer to values the setting can take and should not be localized.', + '- `editor.wordWrapColumn` refers to a different setting and should not be localized.' + ] + }, "Controls how lines should wrap. Can be:\n - 'off' (disable wrapping),\n - 'on' (viewport wrapping),\n - 'wordWrapColumn' (wrap at `editor.wordWrapColumn`) or\n - 'bounded' (wrap at minimum of viewport and `editor.wordWrapColumn`).") + }, + 'editor.wordWrapColumn': { + 'type': 'integer', + 'default': EDITOR_DEFAULTS.wordWrapColumn, + 'minimum': 1, + 'description': nls.localize({ + key: 'wordWrapColumn', + comment: [ + '- `editor.wordWrap` refers to a different setting and should not be localized.', + '- \'wordWrapColumn\' and \'bounded\' refer to values the different setting can take and should not be localized.' + ] + }, "Controls the wrapping column of the editor when `editor.wordWrap` is 'wordWrapColumn' or 'bounded'.") + }, + 'editor.wrappingIndent': { + 'type': 'string', + 'enum': ['none', 'same', 'indent'], + 'default': 'same', + 'description': nls.localize('wrappingIndent', "Controls the indentation of wrapped lines. Can be one of 'none', 'same' or 'indent'.") + }, + 'editor.mouseWheelScrollSensitivity': { + 'type': 'number', + 'default': EDITOR_DEFAULTS.viewInfo.scrollbar.mouseWheelScrollSensitivity, + 'description': nls.localize('mouseWheelScrollSensitivity', "A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events") + }, + 'editor.multiCursorModifier': { + 'type': 'string', + 'enum': ['ctrlCmd', 'alt'], + 'enumDescriptions': [ + nls.localize('multiCursorModifier.ctrlCmd', "Maps to `Control` on Windows and Linux and to `Command` on OSX."), + nls.localize('multiCursorModifier.alt', "Maps to `Alt` on Windows and Linux and to `Option` on OSX.") + ], + 'default': 'alt', + 'description': nls.localize({ + key: 'multiCursorModifier', + comment: [ + '- `ctrlCmd` refers to a value the setting can take and should not be localized.', + '- `Control` and `Command` refer to the modifier keys Ctrl or Cmd on the keyboard and can be localized.' + ] + }, "The modifier to be used to add multiple cursors with the mouse. `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on OSX. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier.") + }, + 'editor.quickSuggestions': { + 'anyOf': [ + { + type: 'boolean', + }, + { + type: 'object', + properties: { + strings: { + type: 'boolean', + default: false, + description: nls.localize('quickSuggestions.strings', "Enable quick suggestions inside strings.") + }, + comments: { + type: 'boolean', + default: false, + description: nls.localize('quickSuggestions.comments', "Enable quick suggestions inside comments.") + }, + other: { + type: 'boolean', + default: true, + description: nls.localize('quickSuggestions.other', "Enable quick suggestions outside of strings and comments.") + }, + } + } + ], + 'default': EDITOR_DEFAULTS.contribInfo.quickSuggestions, + 'description': nls.localize('quickSuggestions', "Controls if suggestions should automatically show up while typing") + }, + 'editor.quickSuggestionsDelay': { + 'type': 'integer', + 'default': EDITOR_DEFAULTS.contribInfo.quickSuggestionsDelay, + 'minimum': 0, + 'description': nls.localize('quickSuggestionsDelay', "Controls the delay in ms after which quick suggestions will show up") + }, + 'editor.parameterHints': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.parameterHints, + 'description': nls.localize('parameterHints', "Enables pop-up that shows parameter documentation and type information as you type") + }, + 'editor.autoClosingBrackets': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.autoClosingBrackets, + 'description': nls.localize('autoClosingBrackets', "Controls if the editor should automatically close brackets after opening them") + }, + 'editor.formatOnType': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.formatOnType, + 'description': nls.localize('formatOnType', "Controls if the editor should automatically format the line after typing") + }, + 'editor.formatOnPaste': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.formatOnPaste, + 'description': nls.localize('formatOnPaste', "Controls if the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document.") + }, + 'editor.autoIndent': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.autoIndent, + 'description': nls.localize('autoIndent', "Controls if the editor should automatically adjust the indentation when users type, paste or move lines. Indentation rules of the language must be available. ") + }, + 'editor.suggestOnTriggerCharacters': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.suggestOnTriggerCharacters, + 'description': nls.localize('suggestOnTriggerCharacters', "Controls if suggestions should automatically show up when typing trigger characters") + }, + 'editor.acceptSuggestionOnEnter': { + 'type': 'string', + 'enum': ['on', 'smart', 'off'], + 'default': EDITOR_DEFAULTS.contribInfo.acceptSuggestionOnEnter, + 'description': nls.localize('acceptSuggestionOnEnter', "Controls if suggestions should be accepted on 'Enter' - in addition to 'Tab'. Helps to avoid ambiguity between inserting new lines or accepting suggestions. The value 'smart' means only accept a suggestion with Enter when it makes a textual change") + }, + 'editor.acceptSuggestionOnCommitCharacter': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.acceptSuggestionOnCommitCharacter, + 'description': nls.localize('acceptSuggestionOnCommitCharacter', "Controls if suggestions should be accepted on commit characters. For instance in JavaScript the semi-colon (';') can be a commit character that accepts a suggestion and types that character.") + }, + 'editor.snippetSuggestions': { + 'type': 'string', + 'enum': ['top', 'bottom', 'inline', 'none'], + 'enumDescriptions': [ + nls.localize('snippetSuggestions.top', "Show snippet suggestions on top of other suggestions."), + nls.localize('snippetSuggestions.bottom', "Show snippet suggestions below other suggestions."), + nls.localize('snippetSuggestions.inline', "Show snippets suggestions with other suggestions."), + nls.localize('snippetSuggestions.none', "Do not show snippet suggestions."), + ], + 'default': EDITOR_DEFAULTS.contribInfo.snippetSuggestions, + 'description': nls.localize('snippetSuggestions', "Controls whether snippets are shown with other suggestions and how they are sorted.") + }, + 'editor.emptySelectionClipboard': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.emptySelectionClipboard, + 'description': nls.localize('emptySelectionClipboard', "Controls whether copying without a selection copies the current line.") + }, + 'editor.wordBasedSuggestions': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.wordBasedSuggestions, + 'description': nls.localize('wordBasedSuggestions', "Controls whether completions should be computed based on words in the document.") + }, + 'editor.suggestFontSize': { + 'type': 'integer', + 'default': 0, + 'minimum': 0, + 'description': nls.localize('suggestFontSize', "Font size for the suggest widget") + }, + 'editor.suggestLineHeight': { + 'type': 'integer', + 'default': 0, + 'minimum': 0, + 'description': nls.localize('suggestLineHeight', "Line height for the suggest widget") + }, + 'editor.selectionHighlight': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.selectionHighlight, + 'description': nls.localize('selectionHighlight', "Controls whether the editor should highlight similar matches to the selection") + }, + 'editor.occurrencesHighlight': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.occurrencesHighlight, + 'description': nls.localize('occurrencesHighlight', "Controls whether the editor should highlight semantic symbol occurrences") + }, + 'editor.overviewRulerLanes': { + 'type': 'integer', + 'default': 3, + 'description': nls.localize('overviewRulerLanes', "Controls the number of decorations that can show up at the same position in the overview ruler") + }, + 'editor.overviewRulerBorder': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.overviewRulerBorder, + 'description': nls.localize('overviewRulerBorder', "Controls if a border should be drawn around the overview ruler.") + }, + 'editor.cursorBlinking': { + 'type': 'string', + 'enum': ['blink', 'smooth', 'phase', 'expand', 'solid'], + 'default': editorOptions.blinkingStyleToString(EDITOR_DEFAULTS.viewInfo.cursorBlinking), + 'description': nls.localize('cursorBlinking', "Control the cursor animation style, possible values are 'blink', 'smooth', 'phase', 'expand' and 'solid'") + }, + 'editor.mouseWheelZoom': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.mouseWheelZoom, + 'description': nls.localize('mouseWheelZoom', "Zoom the font of the editor when using mouse wheel and holding Ctrl") + }, + 'editor.cursorStyle': { + 'type': 'string', + 'enum': ['block', 'block-outline', 'line', 'line-thin', 'underline', 'underline-thin'], + 'default': editorOptions.cursorStyleToString(EDITOR_DEFAULTS.viewInfo.cursorStyle), + 'description': nls.localize('cursorStyle', "Controls the cursor style, accepted values are 'block', 'block-outline', 'line', 'line-thin', 'underline' and 'underline-thin'") + }, + 'editor.fontLigatures': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.fontLigatures, + 'description': nls.localize('fontLigatures', "Enables font ligatures") + }, + 'editor.hideCursorInOverviewRuler': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.hideCursorInOverviewRuler, + 'description': nls.localize('hideCursorInOverviewRuler', "Controls if the cursor should be hidden in the overview ruler.") + }, + 'editor.renderWhitespace': { + 'type': 'string', + 'enum': ['none', 'boundary', 'all'], + default: EDITOR_DEFAULTS.viewInfo.renderWhitespace, + description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters, possibilities are 'none', 'boundary', and 'all'. The 'boundary' option does not render single spaces between words.") + }, + 'editor.renderControlCharacters': { + 'type': 'boolean', + default: EDITOR_DEFAULTS.viewInfo.renderControlCharacters, + description: nls.localize('renderControlCharacters', "Controls whether the editor should render control characters") + }, + 'editor.renderIndentGuides': { + 'type': 'boolean', + default: EDITOR_DEFAULTS.viewInfo.renderIndentGuides, + description: nls.localize('renderIndentGuides', "Controls whether the editor should render indent guides") + }, + 'editor.renderLineHighlight': { + 'type': 'string', + 'enum': ['none', 'gutter', 'line', 'all'], + default: EDITOR_DEFAULTS.viewInfo.renderLineHighlight, + description: nls.localize('renderLineHighlight', "Controls how the editor should render the current line highlight, possibilities are 'none', 'gutter', 'line', and 'all'.") + }, + 'editor.codeLens': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.codeLens, + 'description': nls.localize('codeLens', "Controls if the editor shows code lenses") + }, + 'editor.folding': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.folding, + 'description': nls.localize('folding', "Controls whether the editor has code folding enabled") + }, + 'editor.showFoldingControls': { + 'type': 'string', + 'enum': ['always', 'mouseover'], + 'default': EDITOR_DEFAULTS.contribInfo.showFoldingControls, + 'description': nls.localize('showFoldingControls', "Controls whether the fold controls on the gutter are automatically hidden.") + }, + 'editor.matchBrackets': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.matchBrackets, + 'description': nls.localize('matchBrackets', "Highlight matching brackets when one of them is selected.") + }, + 'editor.glyphMargin': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.glyphMargin, + 'description': nls.localize('glyphMargin', "Controls whether the editor should render the vertical glyph margin. Glyph margin is mostly used for debugging.") + }, + 'editor.useTabStops': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.useTabStops, + 'description': nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops") + }, + 'editor.trimAutoWhitespace': { + 'type': 'boolean', + 'default': EDITOR_MODEL_DEFAULTS.trimAutoWhitespace, + 'description': nls.localize('trimAutoWhitespace', "Remove trailing auto inserted whitespace") + }, + 'editor.stablePeek': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('stablePeek', "Keep peek editors open even when double clicking their content or when hitting Escape.") + }, + 'editor.dragAndDrop': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.dragAndDrop, + 'description': nls.localize('dragAndDrop', "Controls if the editor should allow to move selections via drag and drop.") + }, + 'editor.accessibilitySupport': { + 'type': 'string', + 'enum': ['auto', 'on', 'off'], + 'enumDescriptions': [ + nls.localize('accessibilitySupport.auto', "The editor will use platform APIs to detect when a Screen Reader is attached."), + nls.localize('accessibilitySupport.on', "The editor will be permanently optimized for usage with a Screen Reader."), + nls.localize('accessibilitySupport.off', "The editor will never be optimized for usage with a Screen Reader."), + ], + 'default': EDITOR_DEFAULTS.accessibilitySupport, + 'description': nls.localize('accessibilitySupport', "Controls whether the editor should run in a mode where it is optimized for screen readers.") + }, + 'editor.links': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.links, + 'description': nls.localize('links', "Controls whether the editor should detect links and make them clickable") + }, + 'editor.colorDecorators': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.colorDecorators, + 'description': nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") + }, + 'diffEditor.renderSideBySide': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('sideBySide', "Controls if the diff editor shows the diff side by side or inline") + }, + 'diffEditor.ignoreTrimWhitespace': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('ignoreTrimWhitespace', "Controls if the diff editor shows changes in leading or trailing whitespace as diffs") + }, + 'diffEditor.renderIndicators': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('renderIndicators', "Controls if the diff editor shows +/- indicators for added/removed changes") + } + } +}; + +if (platform.isLinux) { + editorConfiguration['properties']['editor.selectionClipboard'] = { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.selectionClipboard, + 'description': nls.localize('selectionClipboard', "Controls if the Linux primary clipboard should be supported.") + }; +} + +configurationRegistry.registerConfiguration(editorConfiguration); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts new file mode 100644 index 0000000000..619f51433e --- /dev/null +++ b/src/vs/editor/common/config/editorOptions.ts @@ -0,0 +1,2211 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import * as platform from 'vs/base/common/platform'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { Constants } from 'vs/editor/common/core/uint'; +import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; + +/** + * Configuration options for editor scrollbars + */ +export interface IEditorScrollbarOptions { + /** + * The size of arrows (if displayed). + * Defaults to 11. + */ + arrowSize?: number; + /** + * Render vertical scrollbar. + * Accepted values: 'auto', 'visible', 'hidden'. + * Defaults to 'auto'. + */ + vertical?: string; + /** + * Render horizontal scrollbar. + * Accepted values: 'auto', 'visible', 'hidden'. + * Defaults to 'auto'. + */ + horizontal?: string; + /** + * Cast horizontal and vertical shadows when the content is scrolled. + * Defaults to true. + */ + useShadows?: boolean; + /** + * Render arrows at the top and bottom of the vertical scrollbar. + * Defaults to false. + */ + verticalHasArrows?: boolean; + /** + * Render arrows at the left and right of the horizontal scrollbar. + * Defaults to false. + */ + horizontalHasArrows?: boolean; + /** + * Listen to mouse wheel events and react to them by scrolling. + * Defaults to true. + */ + handleMouseWheel?: boolean; + /** + * Height in pixels for the horizontal scrollbar. + * Defaults to 10 (px). + */ + horizontalScrollbarSize?: number; + /** + * Width in pixels for the vertical scrollbar. + * Defaults to 10 (px). + */ + verticalScrollbarSize?: number; + /** + * Width in pixels for the vertical slider. + * Defaults to `verticalScrollbarSize`. + */ + verticalSliderSize?: number; + /** + * Height in pixels for the horizontal slider. + * Defaults to `horizontalScrollbarSize`. + */ + horizontalSliderSize?: number; +} + +/** + * Configuration options for editor find widget + */ +export interface IEditorFindOptions { + /** + * Controls if we seed search string in the Find Widget with editor selection. + */ + seedSearchStringFromSelection?: boolean; + /** + * Controls if Find in Selection flag is turned on when multiple lines of text are selected in the editor. + */ + autoFindInSelection: boolean; +} + +/** + * Configuration options for editor minimap + */ +export interface IEditorMinimapOptions { + /** + * Enable the rendering of the minimap. + * Defaults to false. + */ + enabled?: boolean; + /** + * Control the rendering of the minimap slider. + * Defaults to 'mouseover'. + */ + showSlider?: 'always' | 'mouseover'; + /** + * Render the actual text on a line (as opposed to color blocks). + * Defaults to true. + */ + renderCharacters?: boolean; + /** + * Limit the width of the minimap to render at most a certain number of columns. + * Defaults to 120. + */ + maxColumn?: number; +} + +/** + * Configuration options for the editor. + */ +export interface IEditorOptions { + /** + * This editor is used inside a diff editor. + * @internal + */ + inDiffEditor?: boolean; + /** + * The aria label for the editor's textarea (when it is focused). + */ + ariaLabel?: string; + /** + * Render vertical lines at the specified columns. + * Defaults to empty array. + */ + rulers?: number[]; + /** + * A string containing the word separators used when doing word navigation. + * Defaults to `~!@#$%^&*()-=+[{]}\\|;:\'",.<>/? + */ + wordSeparators?: string; + /** + * Enable Linux primary clipboard. + * Defaults to true. + */ + selectionClipboard?: boolean; + /** + * Control the rendering of line numbers. + * If it is a function, it will be invoked when rendering a line number and the return value will be rendered. + * Otherwise, if it is a truey, line numbers will be rendered normally (equivalent of using an identity function). + * Otherwise, line numbers will not be rendered. + * Defaults to true. + */ + lineNumbers?: 'on' | 'off' | 'relative' | ((lineNumber: number) => string); + /** + * Should the corresponding line be selected when clicking on the line number? + * Defaults to true. + */ + selectOnLineNumbers?: boolean; + /** + * Control the width of line numbers, by reserving horizontal space for rendering at least an amount of digits. + * Defaults to 5. + */ + lineNumbersMinChars?: number; + /** + * Enable the rendering of the glyph margin. + * Defaults to true in vscode and to false in monaco-editor. + */ + glyphMargin?: boolean; + /** + * The width reserved for line decorations (in px). + * Line decorations are placed between line numbers and the editor content. + * You can pass in a string in the format floating point followed by "ch". e.g. 1.3ch. + * Defaults to 10. + */ + lineDecorationsWidth?: number | string; + /** + * When revealing the cursor, a virtual padding (px) is added to the cursor, turning it into a rectangle. + * This virtual padding ensures that the cursor gets revealed before hitting the edge of the viewport. + * Defaults to 30 (px). + */ + revealHorizontalRightPadding?: number; + /** + * Render the editor selection with rounded borders. + * Defaults to true. + */ + roundedSelection?: boolean; + /** + * Class name to be added to the editor. + */ + extraEditorClassName?: string; + /** + * Should the editor be read only. + * Defaults to false. + */ + readOnly?: boolean; + /** + * Control the behavior and rendering of the scrollbars. + */ + scrollbar?: IEditorScrollbarOptions; + /** + * Control the behavior and rendering of the minimap. + */ + minimap?: IEditorMinimapOptions; + /** + * Control the behavior of the find widget. + */ + find?: IEditorFindOptions; + /** + * Display overflow widgets as `fixed`. + * Defaults to `false`. + */ + fixedOverflowWidgets?: boolean; + /** + * The number of vertical lanes the overview ruler should render. + * Defaults to 2. + */ + overviewRulerLanes?: number; + /** + * Controls if a border should be drawn around the overview ruler. + * Defaults to `true`. + */ + overviewRulerBorder?: boolean; + /** + * Control the cursor animation style, possible values are 'blink', 'smooth', 'phase', 'expand' and 'solid'. + * Defaults to 'blink'. + */ + cursorBlinking?: string; + /** + * Zoom the font in the editor when using the mouse wheel in combination with holding Ctrl. + * Defaults to false. + */ + mouseWheelZoom?: boolean; + /** + * Control the mouse pointer style, either 'text' or 'default' or 'copy' + * Defaults to 'text' + * @internal + */ + mouseStyle?: 'text' | 'default' | 'copy'; + /** + * Control the cursor style, either 'block' or 'line'. + * Defaults to 'line'. + */ + cursorStyle?: string; + /** + * Enable font ligatures. + * Defaults to false. + */ + fontLigatures?: boolean; + /** + * Disable the use of `will-change` for the editor margin and lines layers. + * The usage of `will-change` acts as a hint for browsers to create an extra layer. + * Defaults to false. + */ + disableLayerHinting?: boolean; + /** + * Disable the optimizations for monospace fonts. + * Defaults to false. + */ + disableMonospaceOptimizations?: boolean; + /** + * Should the cursor be hidden in the overview ruler. + * Defaults to false. + */ + hideCursorInOverviewRuler?: boolean; + /** + * Enable that scrolling can go one screen size after the last line. + * Defaults to true. + */ + scrollBeyondLastLine?: boolean; + /** + * Enable that the editor animates scrolling to a position. + * Defaults to false. + */ + smoothScrolling?: boolean; + /** + * Enable that the editor will install an interval to check if its container dom node size has changed. + * Enabling this might have a severe performance impact. + * Defaults to false. + */ + automaticLayout?: boolean; + /** + * Control the wrapping of the editor. + * When `wordWrap` = "off", the lines will never wrap. + * When `wordWrap` = "on", the lines will wrap at the viewport width. + * When `wordWrap` = "wordWrapColumn", the lines will wrap at `wordWrapColumn`. + * When `wordWrap` = "bounded", the lines will wrap at min(viewport width, wordWrapColumn). + * Defaults to "off". + */ + wordWrap?: 'off' | 'on' | 'wordWrapColumn' | 'bounded'; + /** + * Control the wrapping of the editor. + * When `wordWrap` = "off", the lines will never wrap. + * When `wordWrap` = "on", the lines will wrap at the viewport width. + * When `wordWrap` = "wordWrapColumn", the lines will wrap at `wordWrapColumn`. + * When `wordWrap` = "bounded", the lines will wrap at min(viewport width, wordWrapColumn). + * Defaults to 80. + */ + wordWrapColumn?: number; + /** + * Force word wrapping when the text appears to be of a minified/generated file. + * Defaults to true. + */ + wordWrapMinified?: boolean; + /** + * Control indentation of wrapped lines. Can be: 'none', 'same' or 'indent'. + * Defaults to 'same' in vscode and to 'none' in monaco-editor. + */ + wrappingIndent?: string; + /** + * Configure word wrapping characters. A break will be introduced before these characters. + * Defaults to '{([+'. + */ + wordWrapBreakBeforeCharacters?: string; + /** + * Configure word wrapping characters. A break will be introduced after these characters. + * Defaults to ' \t})]?|&,;'. + */ + wordWrapBreakAfterCharacters?: string; + /** + * Configure word wrapping characters. A break will be introduced after these characters only if no `wordWrapBreakBeforeCharacters` or `wordWrapBreakAfterCharacters` were found. + * Defaults to '.'. + */ + wordWrapBreakObtrusiveCharacters?: string; + + /** + * Performance guard: Stop rendering a line after x characters. + * Defaults to 10000. + * Use -1 to never stop rendering + */ + stopRenderingLineAfter?: number; + /** + * Enable hover. + * Defaults to true. + */ + hover?: boolean; + /** + * Enable detecting links and making them clickable. + * Defaults to true. + */ + links?: boolean; + /** + * Enable inline color decorators and color picker rendering. + */ + colorDecorators?: boolean; + /** + * Enable custom contextmenu. + * Defaults to true. + */ + contextmenu?: boolean; + /** + * A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events. + * Defaults to 1. + */ + mouseWheelScrollSensitivity?: number; + /** + * The modifier to be used to add multiple cursors with the mouse. + * Defaults to 'alt' + */ + multiCursorModifier?: 'ctrlCmd' | 'alt'; + /** + * Configure the editor's accessibility support. + * Defaults to 'auto'. It is best to leave this to 'auto'. + */ + accessibilitySupport?: 'auto' | 'off' | 'on'; + /** + * Enable quick suggestions (shadow suggestions) + * Defaults to true. + */ + quickSuggestions?: boolean | { other: boolean, comments: boolean, strings: boolean }; + /** + * Quick suggestions show delay (in ms) + * Defaults to 500 (ms) + */ + quickSuggestionsDelay?: number; + /** + * Enables parameter hints + */ + parameterHints?: boolean; + /** + * Render icons in suggestions box. + * Defaults to true. + */ + iconsInSuggestions?: boolean; + /** + * Enable auto closing brackets. + * Defaults to true. + */ + autoClosingBrackets?: boolean; + /** + * Enable auto indentation adjustment. + * Defaults to false. + */ + autoIndent?: boolean; + /** + * Enable format on type. + * Defaults to false. + */ + formatOnType?: boolean; + /** + * Enable format on paste. + * Defaults to false. + */ + formatOnPaste?: boolean; + /** + * Controls if the editor should allow to move selections via drag and drop. + * Defaults to false. + */ + dragAndDrop?: boolean; + /** + * Enable the suggestion box to pop-up on trigger characters. + * Defaults to true. + */ + suggestOnTriggerCharacters?: boolean; + /** + * Accept suggestions on ENTER. + * Defaults to 'on'. + */ + acceptSuggestionOnEnter?: 'on' | 'smart' | 'off'; + /** + * Accept suggestions on provider defined characters. + * Defaults to true. + */ + acceptSuggestionOnCommitCharacter?: boolean; + /** + * Enable snippet suggestions. Default to 'true'. + */ + snippetSuggestions?: 'top' | 'bottom' | 'inline' | 'none'; + /** + * Copying without a selection copies the current line. + */ + emptySelectionClipboard?: boolean; + /** + * Enable word based suggestions. Defaults to 'true' + */ + wordBasedSuggestions?: boolean; + /** + * The font size for the suggest widget. + * Defaults to the editor font size. + */ + suggestFontSize?: number; + /** + * The line height for the suggest widget. + * Defaults to the editor line height. + */ + suggestLineHeight?: number; + /** + * Enable selection highlight. + * Defaults to true. + */ + selectionHighlight?: boolean; + /** + * Enable semantic occurrences highlight. + * Defaults to true. + */ + occurrencesHighlight?: boolean; + /** + * Show code lens + * Defaults to true. + */ + codeLens?: boolean; + /** + * @deprecated - use codeLens instead + * @internal + */ + referenceInfos?: boolean; + /** + * Enable code folding + * Defaults to true in vscode and to false in monaco-editor. + */ + folding?: boolean; + /** + * Controls whether the fold actions in the gutter stay always visible or hide unless the mouse is over the gutter. + * Defaults to 'mouseover'. + */ + showFoldingControls?: 'always' | 'mouseover'; + /** + * Enable highlighting of matching brackets. + * Defaults to true. + */ + matchBrackets?: boolean; + /** + * Enable rendering of whitespace. + * Defaults to none. + */ + renderWhitespace?: 'none' | 'boundary' | 'all'; + /** + * Enable rendering of control characters. + * Defaults to false. + */ + renderControlCharacters?: boolean; + /** + * Enable rendering of indent guides. + * Defaults to false. + */ + renderIndentGuides?: boolean; + /** + * Enable rendering of current line highlight. + * Defaults to all. + */ + renderLineHighlight?: 'none' | 'gutter' | 'line' | 'all'; + /** + * Inserting and deleting whitespace follows tab stops. + */ + useTabStops?: boolean; + /** + * The font family + */ + fontFamily?: string; + /** + * The font weight + */ + fontWeight?: 'normal' | 'bold' | 'bolder' | 'lighter' | 'initial' | 'inherit' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; + /** + * The font size + */ + fontSize?: number; + /** + * The line height + */ + lineHeight?: number; + /** + * The letter spacing + */ + letterSpacing?: number; +} + +/** + * Configuration options for the diff editor. + */ +export interface IDiffEditorOptions extends IEditorOptions { + /** + * Allow the user to resize the diff editor split view. + * Defaults to true. + */ + enableSplitViewResizing?: boolean; + /** + * Render the differences in two side-by-side editors. + * Defaults to true. + */ + renderSideBySide?: boolean; + /** + * Compute the diff by ignoring leading/trailing whitespace + * Defaults to true. + */ + ignoreTrimWhitespace?: boolean; + /** + * Render +/- indicators for added/deleted changes. + * Defaults to true. + */ + renderIndicators?: boolean; + /** + * Original model should be editable? + * Defaults to false. + */ + originalEditable?: boolean; +} + +export enum RenderMinimap { + None = 0, + Small = 1, + Large = 2, + SmallBlocks = 3, + LargeBlocks = 4, +} + +/** + * Describes how to indent wrapped lines. + */ +export enum WrappingIndent { + /** + * No indentation => wrapped lines begin at column 1. + */ + None = 0, + /** + * Same => wrapped lines get the same indentation as the parent. + */ + Same = 1, + /** + * Indent => wrapped lines get +1 indentation as the parent. + */ + Indent = 2 +} + +/** + * The kind of animation in which the editor's cursor should be rendered. + */ +export enum TextEditorCursorBlinkingStyle { + /** + * Hidden + */ + Hidden = 0, + /** + * Blinking + */ + Blink = 1, + /** + * Blinking with smooth fading + */ + Smooth = 2, + /** + * Blinking with prolonged filled state and smooth fading + */ + Phase = 3, + /** + * Expand collapse animation on the y axis + */ + Expand = 4, + /** + * No-Blinking + */ + Solid = 5 +} +/** + * @internal + */ +export function blinkingStyleToString(blinkingStyle: TextEditorCursorBlinkingStyle): string { + if (blinkingStyle === TextEditorCursorBlinkingStyle.Blink) { + return 'blink'; + } else if (blinkingStyle === TextEditorCursorBlinkingStyle.Expand) { + return 'expand'; + } else if (blinkingStyle === TextEditorCursorBlinkingStyle.Phase) { + return 'phase'; + } else if (blinkingStyle === TextEditorCursorBlinkingStyle.Smooth) { + return 'smooth'; + } else if (blinkingStyle === TextEditorCursorBlinkingStyle.Solid) { + return 'solid'; + } else { + throw new Error('blinkingStyleToString: Unknown blinkingStyle'); + } +} + +/** + * The style in which the editor's cursor should be rendered. + */ +export enum TextEditorCursorStyle { + /** + * As a vertical line (sitting between two characters). + */ + Line = 1, + /** + * As a block (sitting on top of a character). + */ + Block = 2, + /** + * As a horizontal line (sitting under a character). + */ + Underline = 3, + /** + * As a thin vertical line (sitting between two characters). + */ + LineThin = 4, + /** + * As an outlined block (sitting on top of a character). + */ + BlockOutline = 5, + /** + * As a thin horizontal line (sitting under a character). + */ + UnderlineThin = 6 +} + +/** + * @internal + */ +export function cursorStyleToString(cursorStyle: TextEditorCursorStyle): string { + if (cursorStyle === TextEditorCursorStyle.Line) { + return 'line'; + } else if (cursorStyle === TextEditorCursorStyle.Block) { + return 'block'; + } else if (cursorStyle === TextEditorCursorStyle.Underline) { + return 'underline'; + } else if (cursorStyle === TextEditorCursorStyle.LineThin) { + return 'line-thin'; + } else if (cursorStyle === TextEditorCursorStyle.BlockOutline) { + return 'block-outline'; + } else if (cursorStyle === TextEditorCursorStyle.UnderlineThin) { + return 'underline-thin'; + } else { + throw new Error('cursorStyleToString: Unknown cursorStyle'); + } +} + +function _cursorStyleFromString(cursorStyle: string, defaultValue: TextEditorCursorStyle): TextEditorCursorStyle { + if (typeof cursorStyle !== 'string') { + return defaultValue; + } + if (cursorStyle === 'line') { + return TextEditorCursorStyle.Line; + } else if (cursorStyle === 'block') { + return TextEditorCursorStyle.Block; + } else if (cursorStyle === 'underline') { + return TextEditorCursorStyle.Underline; + } else if (cursorStyle === 'line-thin') { + return TextEditorCursorStyle.LineThin; + } else if (cursorStyle === 'block-outline') { + return TextEditorCursorStyle.BlockOutline; + } else if (cursorStyle === 'underline-thin') { + return TextEditorCursorStyle.UnderlineThin; + } + return TextEditorCursorStyle.Line; +} + +export interface InternalEditorScrollbarOptions { + readonly arrowSize: number; + readonly vertical: ScrollbarVisibility; + readonly horizontal: ScrollbarVisibility; + readonly useShadows: boolean; + readonly verticalHasArrows: boolean; + readonly horizontalHasArrows: boolean; + readonly handleMouseWheel: boolean; + readonly horizontalScrollbarSize: number; + readonly horizontalSliderSize: number; + readonly verticalScrollbarSize: number; + readonly verticalSliderSize: number; + readonly mouseWheelScrollSensitivity: number; +} + +export interface InternalEditorMinimapOptions { + readonly enabled: boolean; + readonly showSlider: 'always' | 'mouseover'; + readonly renderCharacters: boolean; + readonly maxColumn: number; +} + +export interface InternalEditorFindOptions { + readonly seedSearchStringFromSelection: boolean; + readonly autoFindInSelection: boolean; +} + +export interface EditorWrappingInfo { + readonly inDiffEditor: boolean; + readonly isDominatedByLongLines: boolean; + readonly isWordWrapMinified: boolean; + readonly isViewportWrapping: boolean; + readonly wrappingColumn: number; + readonly wrappingIndent: WrappingIndent; + readonly wordWrapBreakBeforeCharacters: string; + readonly wordWrapBreakAfterCharacters: string; + readonly wordWrapBreakObtrusiveCharacters: string; +} + +export interface InternalEditorViewOptions { + readonly extraEditorClassName: string; + readonly disableMonospaceOptimizations: boolean; + readonly rulers: number[]; + readonly ariaLabel: string; + readonly renderLineNumbers: boolean; + readonly renderCustomLineNumbers: (lineNumber: number) => string; + readonly renderRelativeLineNumbers: boolean; + readonly selectOnLineNumbers: boolean; + readonly glyphMargin: boolean; + readonly revealHorizontalRightPadding: number; + readonly roundedSelection: boolean; + readonly overviewRulerLanes: number; + readonly overviewRulerBorder: boolean; + readonly cursorBlinking: TextEditorCursorBlinkingStyle; + readonly mouseWheelZoom: boolean; + readonly cursorStyle: TextEditorCursorStyle; + readonly hideCursorInOverviewRuler: boolean; + readonly scrollBeyondLastLine: boolean; + readonly smoothScrolling: boolean; + readonly stopRenderingLineAfter: number; + readonly renderWhitespace: 'none' | 'boundary' | 'all'; + readonly renderControlCharacters: boolean; + readonly fontLigatures: boolean; + readonly renderIndentGuides: boolean; + readonly renderLineHighlight: 'none' | 'gutter' | 'line' | 'all'; + readonly scrollbar: InternalEditorScrollbarOptions; + readonly minimap: InternalEditorMinimapOptions; + readonly fixedOverflowWidgets: boolean; +} + +export interface EditorContribOptions { + readonly selectionClipboard: boolean; + readonly hover: boolean; + readonly links: boolean; + readonly contextmenu: boolean; + readonly quickSuggestions: boolean | { other: boolean, comments: boolean, strings: boolean }; + readonly quickSuggestionsDelay: number; + readonly parameterHints: boolean; + readonly iconsInSuggestions: boolean; + readonly formatOnType: boolean; + readonly formatOnPaste: boolean; + readonly suggestOnTriggerCharacters: boolean; + readonly acceptSuggestionOnEnter: 'on' | 'smart' | 'off'; + readonly acceptSuggestionOnCommitCharacter: boolean; + readonly snippetSuggestions: 'top' | 'bottom' | 'inline' | 'none'; + readonly wordBasedSuggestions: boolean; + readonly suggestFontSize: number; + readonly suggestLineHeight: number; + readonly selectionHighlight: boolean; + readonly occurrencesHighlight: boolean; + readonly codeLens: boolean; + readonly folding: boolean; + readonly showFoldingControls: 'always' | 'mouseover'; + readonly matchBrackets: boolean; + readonly find: InternalEditorFindOptions; + readonly colorDecorators: boolean; +} + +/** + * Validated configuration options for the editor. + * This is a 1 to 1 validated/parsed version of IEditorOptions merged on top of the defaults. + * @internal + */ +export interface IValidatedEditorOptions { + readonly inDiffEditor: boolean; + readonly wordSeparators: string; + readonly lineNumbersMinChars: number; + readonly lineDecorationsWidth: number | string; + readonly readOnly: boolean; + readonly mouseStyle: 'text' | 'default' | 'copy'; + readonly disableLayerHinting: boolean; + readonly automaticLayout: boolean; + readonly wordWrap: 'off' | 'on' | 'wordWrapColumn' | 'bounded'; + readonly wordWrapColumn: number; + readonly wordWrapMinified: boolean; + readonly wrappingIndent: WrappingIndent; + readonly wordWrapBreakBeforeCharacters: string; + readonly wordWrapBreakAfterCharacters: string; + readonly wordWrapBreakObtrusiveCharacters: string; + readonly autoClosingBrackets: boolean; + readonly autoIndent: boolean; + readonly dragAndDrop: boolean; + readonly emptySelectionClipboard: boolean; + readonly useTabStops: boolean; + readonly multiCursorModifier: 'altKey' | 'ctrlKey' | 'metaKey'; + readonly accessibilitySupport: 'auto' | 'off' | 'on'; + + readonly viewInfo: InternalEditorViewOptions; + readonly contribInfo: EditorContribOptions; +} + +/** + * Internal configuration options (transformed or computed) for the editor. + */ +export class InternalEditorOptions { + readonly _internalEditorOptionsBrand: void; + + readonly canUseLayerHinting: boolean; + readonly pixelRatio: number; + readonly editorClassName: string; + readonly lineHeight: number; + readonly readOnly: boolean; + /** + * @internal + */ + readonly accessibilitySupport: platform.AccessibilitySupport; + readonly multiCursorModifier: 'altKey' | 'ctrlKey' | 'metaKey'; + + // ---- cursor options + readonly wordSeparators: string; + readonly autoClosingBrackets: boolean; + readonly autoIndent: boolean; + readonly useTabStops: boolean; + readonly tabFocusMode: boolean; + readonly dragAndDrop: boolean; + readonly emptySelectionClipboard: boolean; + + // ---- grouped options + readonly layoutInfo: EditorLayoutInfo; + readonly fontInfo: FontInfo; + readonly viewInfo: InternalEditorViewOptions; + readonly wrappingInfo: EditorWrappingInfo; + readonly contribInfo: EditorContribOptions; + + /** + * @internal + */ + constructor(source: { + canUseLayerHinting: boolean; + pixelRatio: number; + editorClassName: string; + lineHeight: number; + readOnly: boolean; + accessibilitySupport: platform.AccessibilitySupport; + multiCursorModifier: 'altKey' | 'ctrlKey' | 'metaKey'; + wordSeparators: string; + autoClosingBrackets: boolean; + autoIndent: boolean; + useTabStops: boolean; + tabFocusMode: boolean; + dragAndDrop: boolean; + emptySelectionClipboard: boolean; + layoutInfo: EditorLayoutInfo; + fontInfo: FontInfo; + viewInfo: InternalEditorViewOptions; + wrappingInfo: EditorWrappingInfo; + contribInfo: EditorContribOptions; + }) { + this.canUseLayerHinting = source.canUseLayerHinting; + this.pixelRatio = source.pixelRatio; + this.editorClassName = source.editorClassName; + this.lineHeight = source.lineHeight | 0; + this.readOnly = source.readOnly; + this.accessibilitySupport = source.accessibilitySupport; + this.multiCursorModifier = source.multiCursorModifier; + this.wordSeparators = source.wordSeparators; + this.autoClosingBrackets = source.autoClosingBrackets; + this.autoIndent = source.autoIndent; + this.useTabStops = source.useTabStops; + this.tabFocusMode = source.tabFocusMode; + this.dragAndDrop = source.dragAndDrop; + this.emptySelectionClipboard = source.emptySelectionClipboard; + this.layoutInfo = source.layoutInfo; + this.fontInfo = source.fontInfo; + this.viewInfo = source.viewInfo; + this.wrappingInfo = source.wrappingInfo; + this.contribInfo = source.contribInfo; + } + + /** + * @internal + */ + public equals(other: InternalEditorOptions): boolean { + return ( + this.canUseLayerHinting === other.canUseLayerHinting + && this.pixelRatio === other.pixelRatio + && this.editorClassName === other.editorClassName + && this.lineHeight === other.lineHeight + && this.readOnly === other.readOnly + && this.accessibilitySupport === other.accessibilitySupport + && this.multiCursorModifier === other.multiCursorModifier + && this.wordSeparators === other.wordSeparators + && this.autoClosingBrackets === other.autoClosingBrackets + && this.autoIndent === other.autoIndent + && this.useTabStops === other.useTabStops + && this.tabFocusMode === other.tabFocusMode + && this.dragAndDrop === other.dragAndDrop + && this.emptySelectionClipboard === other.emptySelectionClipboard + && InternalEditorOptions._equalsLayoutInfo(this.layoutInfo, other.layoutInfo) + && this.fontInfo.equals(other.fontInfo) + && InternalEditorOptions._equalsViewOptions(this.viewInfo, other.viewInfo) + && InternalEditorOptions._equalsWrappingInfo(this.wrappingInfo, other.wrappingInfo) + && InternalEditorOptions._equalsContribOptions(this.contribInfo, other.contribInfo) + ); + } + + /** + * @internal + */ + public createChangeEvent(newOpts: InternalEditorOptions): IConfigurationChangedEvent { + return { + canUseLayerHinting: (this.canUseLayerHinting !== newOpts.canUseLayerHinting), + pixelRatio: (this.pixelRatio !== newOpts.pixelRatio), + editorClassName: (this.editorClassName !== newOpts.editorClassName), + lineHeight: (this.lineHeight !== newOpts.lineHeight), + readOnly: (this.readOnly !== newOpts.readOnly), + accessibilitySupport: (this.accessibilitySupport !== newOpts.accessibilitySupport), + multiCursorModifier: (this.multiCursorModifier !== newOpts.multiCursorModifier), + wordSeparators: (this.wordSeparators !== newOpts.wordSeparators), + autoClosingBrackets: (this.autoClosingBrackets !== newOpts.autoClosingBrackets), + autoIndent: (this.autoIndent !== newOpts.autoIndent), + useTabStops: (this.useTabStops !== newOpts.useTabStops), + tabFocusMode: (this.tabFocusMode !== newOpts.tabFocusMode), + dragAndDrop: (this.dragAndDrop !== newOpts.dragAndDrop), + emptySelectionClipboard: (this.emptySelectionClipboard !== newOpts.emptySelectionClipboard), + layoutInfo: (!InternalEditorOptions._equalsLayoutInfo(this.layoutInfo, newOpts.layoutInfo)), + fontInfo: (!this.fontInfo.equals(newOpts.fontInfo)), + viewInfo: (!InternalEditorOptions._equalsViewOptions(this.viewInfo, newOpts.viewInfo)), + wrappingInfo: (!InternalEditorOptions._equalsWrappingInfo(this.wrappingInfo, newOpts.wrappingInfo)), + contribInfo: (!InternalEditorOptions._equalsContribOptions(this.contribInfo, newOpts.contribInfo)) + }; + } + + /** + * @internal + */ + private static _equalsLayoutInfo(a: EditorLayoutInfo, b: EditorLayoutInfo): boolean { + return ( + a.width === b.width + && a.height === b.height + && a.glyphMarginLeft === b.glyphMarginLeft + && a.glyphMarginWidth === b.glyphMarginWidth + && a.glyphMarginHeight === b.glyphMarginHeight + && a.lineNumbersLeft === b.lineNumbersLeft + && a.lineNumbersWidth === b.lineNumbersWidth + && a.lineNumbersHeight === b.lineNumbersHeight + && a.decorationsLeft === b.decorationsLeft + && a.decorationsWidth === b.decorationsWidth + && a.decorationsHeight === b.decorationsHeight + && a.contentLeft === b.contentLeft + && a.contentWidth === b.contentWidth + && a.contentHeight === b.contentHeight + && a.renderMinimap === b.renderMinimap + && a.minimapWidth === b.minimapWidth + && a.viewportColumn === b.viewportColumn + && a.verticalScrollbarWidth === b.verticalScrollbarWidth + && a.horizontalScrollbarHeight === b.horizontalScrollbarHeight + && this._equalsOverviewRuler(a.overviewRuler, b.overviewRuler) + ); + } + + /** + * @internal + */ + private static _equalsOverviewRuler(a: OverviewRulerPosition, b: OverviewRulerPosition): boolean { + return ( + a.width === b.width + && a.height === b.height + && a.top === b.top + && a.right === b.right + ); + } + + /** + * @internal + */ + private static _equalsViewOptions(a: InternalEditorViewOptions, b: InternalEditorViewOptions): boolean { + return ( + a.extraEditorClassName === b.extraEditorClassName + && a.disableMonospaceOptimizations === b.disableMonospaceOptimizations + && this._equalsNumberArrays(a.rulers, b.rulers) + && a.ariaLabel === b.ariaLabel + && a.renderLineNumbers === b.renderLineNumbers + && a.renderCustomLineNumbers === b.renderCustomLineNumbers + && a.renderRelativeLineNumbers === b.renderRelativeLineNumbers + && a.selectOnLineNumbers === b.selectOnLineNumbers + && a.glyphMargin === b.glyphMargin + && a.revealHorizontalRightPadding === b.revealHorizontalRightPadding + && a.roundedSelection === b.roundedSelection + && a.overviewRulerLanes === b.overviewRulerLanes + && a.overviewRulerBorder === b.overviewRulerBorder + && a.cursorBlinking === b.cursorBlinking + && a.mouseWheelZoom === b.mouseWheelZoom + && a.cursorStyle === b.cursorStyle + && a.hideCursorInOverviewRuler === b.hideCursorInOverviewRuler + && a.scrollBeyondLastLine === b.scrollBeyondLastLine + && a.smoothScrolling === b.smoothScrolling + && a.stopRenderingLineAfter === b.stopRenderingLineAfter + && a.renderWhitespace === b.renderWhitespace + && a.renderControlCharacters === b.renderControlCharacters + && a.fontLigatures === b.fontLigatures + && a.renderIndentGuides === b.renderIndentGuides + && a.renderLineHighlight === b.renderLineHighlight + && this._equalsScrollbarOptions(a.scrollbar, b.scrollbar) + && this._equalsMinimapOptions(a.minimap, b.minimap) + && a.fixedOverflowWidgets === b.fixedOverflowWidgets + ); + } + + /** + * @internal + */ + private static _equalsScrollbarOptions(a: InternalEditorScrollbarOptions, b: InternalEditorScrollbarOptions): boolean { + return ( + a.arrowSize === b.arrowSize + && a.vertical === b.vertical + && a.horizontal === b.horizontal + && a.useShadows === b.useShadows + && a.verticalHasArrows === b.verticalHasArrows + && a.horizontalHasArrows === b.horizontalHasArrows + && a.handleMouseWheel === b.handleMouseWheel + && a.horizontalScrollbarSize === b.horizontalScrollbarSize + && a.horizontalSliderSize === b.horizontalSliderSize + && a.verticalScrollbarSize === b.verticalScrollbarSize + && a.verticalSliderSize === b.verticalSliderSize + && a.mouseWheelScrollSensitivity === b.mouseWheelScrollSensitivity + ); + } + + /** + * @internal + */ + private static _equalsMinimapOptions(a: InternalEditorMinimapOptions, b: InternalEditorMinimapOptions): boolean { + return ( + a.enabled === b.enabled + && a.showSlider === b.showSlider + && a.renderCharacters === b.renderCharacters + && a.maxColumn === b.maxColumn + ); + } + + private static _equalsNumberArrays(a: number[], b: number[]): boolean { + if (a.length !== b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; + } + + /** + * @internal + */ + + private static _equalFindOptions(a: InternalEditorFindOptions, b: InternalEditorFindOptions): boolean { + return ( + a.seedSearchStringFromSelection === b.seedSearchStringFromSelection + && a.autoFindInSelection === b.autoFindInSelection + ); + } + + /** + * @internal + */ + private static _equalsWrappingInfo(a: EditorWrappingInfo, b: EditorWrappingInfo): boolean { + return ( + a.inDiffEditor === b.inDiffEditor + && a.isDominatedByLongLines === b.isDominatedByLongLines + && a.isWordWrapMinified === b.isWordWrapMinified + && a.isViewportWrapping === b.isViewportWrapping + && a.wrappingColumn === b.wrappingColumn + && a.wrappingIndent === b.wrappingIndent + && a.wordWrapBreakBeforeCharacters === b.wordWrapBreakBeforeCharacters + && a.wordWrapBreakAfterCharacters === b.wordWrapBreakAfterCharacters + && a.wordWrapBreakObtrusiveCharacters === b.wordWrapBreakObtrusiveCharacters + ); + } + + /** + * @internal + */ + private static _equalsContribOptions(a: EditorContribOptions, b: EditorContribOptions): boolean { + return ( + a.selectionClipboard === b.selectionClipboard + && a.hover === b.hover + && a.links === b.links + && a.contextmenu === b.contextmenu + && InternalEditorOptions._equalsQuickSuggestions(a.quickSuggestions, b.quickSuggestions) + && a.quickSuggestionsDelay === b.quickSuggestionsDelay + && a.parameterHints === b.parameterHints + && a.iconsInSuggestions === b.iconsInSuggestions + && a.formatOnType === b.formatOnType + && a.formatOnPaste === b.formatOnPaste + && a.suggestOnTriggerCharacters === b.suggestOnTriggerCharacters + && a.acceptSuggestionOnEnter === b.acceptSuggestionOnEnter + && a.acceptSuggestionOnCommitCharacter === b.acceptSuggestionOnCommitCharacter + && a.snippetSuggestions === b.snippetSuggestions + && a.wordBasedSuggestions === b.wordBasedSuggestions + && a.suggestFontSize === b.suggestFontSize + && a.suggestLineHeight === b.suggestLineHeight + && a.selectionHighlight === b.selectionHighlight + && a.occurrencesHighlight === b.occurrencesHighlight + && a.codeLens === b.codeLens + && a.folding === b.folding + && a.showFoldingControls === b.showFoldingControls + && a.matchBrackets === b.matchBrackets + && this._equalFindOptions(a.find, b.find) + && a.colorDecorators === b.colorDecorators + ); + } + + private static _equalsQuickSuggestions(a: boolean | { other: boolean, comments: boolean, strings: boolean }, b: boolean | { other: boolean, comments: boolean, strings: boolean }): boolean { + if (typeof a === 'boolean') { + if (typeof b !== 'boolean') { + return false; + } + return a === b; + } + if (typeof b === 'boolean') { + return false; + } + return ( + a.comments === b.comments + && a.other === b.other + && a.strings === b.strings + ); + } +} + +/** + * A description for the overview ruler position. + */ +export interface OverviewRulerPosition { + /** + * Width of the overview ruler + */ + readonly width: number; + /** + * Height of the overview ruler + */ + readonly height: number; + /** + * Top position for the overview ruler + */ + readonly top: number; + /** + * Right position for the overview ruler + */ + readonly right: number; +} + +/** + * The internal layout details of the editor. + */ +export interface EditorLayoutInfo { + + /** + * Full editor width. + */ + readonly width: number; + /** + * Full editor height. + */ + readonly height: number; + + /** + * Left position for the glyph margin. + */ + readonly glyphMarginLeft: number; + /** + * The width of the glyph margin. + */ + readonly glyphMarginWidth: number; + /** + * The height of the glyph margin. + */ + readonly glyphMarginHeight: number; + + /** + * Left position for the line numbers. + */ + readonly lineNumbersLeft: number; + /** + * The width of the line numbers. + */ + readonly lineNumbersWidth: number; + /** + * The height of the line numbers. + */ + readonly lineNumbersHeight: number; + + /** + * Left position for the line decorations. + */ + readonly decorationsLeft: number; + /** + * The width of the line decorations. + */ + readonly decorationsWidth: number; + /** + * The height of the line decorations. + */ + readonly decorationsHeight: number; + + /** + * Left position for the content (actual text) + */ + readonly contentLeft: number; + /** + * The width of the content (actual text) + */ + readonly contentWidth: number; + /** + * The height of the content (actual height) + */ + readonly contentHeight: number; + + /** + * The width of the minimap + */ + readonly minimapWidth: number; + + /** + * Minimap render type + */ + readonly renderMinimap: RenderMinimap; + + /** + * The number of columns (of typical characters) fitting on a viewport line. + */ + readonly viewportColumn: number; + + /** + * The width of the vertical scrollbar. + */ + readonly verticalScrollbarWidth: number; + /** + * The height of the horizontal scrollbar. + */ + readonly horizontalScrollbarHeight: number; + + /** + * The position of the overview ruler. + */ + readonly overviewRuler: OverviewRulerPosition; +} + +/** + * An event describing that the configuration of the editor has changed. + */ +export interface IConfigurationChangedEvent { + readonly canUseLayerHinting: boolean; + readonly pixelRatio: boolean; + readonly editorClassName: boolean; + readonly lineHeight: boolean; + readonly readOnly: boolean; + readonly accessibilitySupport: boolean; + readonly multiCursorModifier: boolean; + readonly wordSeparators: boolean; + readonly autoClosingBrackets: boolean; + readonly autoIndent: boolean; + readonly useTabStops: boolean; + readonly tabFocusMode: boolean; + readonly dragAndDrop: boolean; + readonly emptySelectionClipboard: boolean; + readonly layoutInfo: boolean; + readonly fontInfo: boolean; + readonly viewInfo: boolean; + readonly wrappingInfo: boolean; + readonly contribInfo: boolean; +} + +/** + * @internal + */ +export interface IEnvironmentalOptions { + readonly outerWidth: number; + readonly outerHeight: number; + readonly fontInfo: FontInfo; + readonly extraEditorClassName: string; + readonly isDominatedByLongLines: boolean; + readonly lineNumbersDigitCount: number; + readonly emptySelectionClipboard: boolean; + readonly pixelRatio: number; + readonly tabFocusMode: boolean; + readonly accessibilitySupport: platform.AccessibilitySupport; +} + +function _boolean(value: any, defaultValue: T): boolean | T { + if (typeof value === 'undefined') { + return defaultValue; + } + if (value === 'false') { + // treat the string 'false' as false + return false; + } + return Boolean(value); +} + +function _string(value: any, defaultValue: string): string { + if (typeof value !== 'string') { + return defaultValue; + } + return value; +} + +function _stringSet(value: any, defaultValue: T, allowedValues: string[]): T { + if (typeof value !== 'string') { + return defaultValue; + } + if (allowedValues.indexOf(value) === -1) { + return defaultValue; + } + return value; +} + +function _clampedInt(value: any, defaultValue: number, minimum: number, maximum: number): number { + let r: number; + if (typeof value === 'undefined') { + r = defaultValue; + } else { + r = parseInt(value, 10); + if (isNaN(r)) { + r = defaultValue; + } + } + r = Math.max(minimum, r); + r = Math.min(maximum, r); + return r | 0; +} + +function _float(value: any, defaultValue: number): number { + let r = parseFloat(value); + if (isNaN(r)) { + r = defaultValue; + } + return r; +} + +function _wrappingIndentFromString(wrappingIndent: string, defaultValue: WrappingIndent): WrappingIndent { + if (typeof wrappingIndent !== 'string') { + return defaultValue; + } + if (wrappingIndent === 'indent') { + return WrappingIndent.Indent; + } else if (wrappingIndent === 'same') { + return WrappingIndent.Same; + } else { + return WrappingIndent.None; + } +} + +function _cursorBlinkingStyleFromString(cursorBlinkingStyle: string, defaultValue: TextEditorCursorBlinkingStyle): TextEditorCursorBlinkingStyle { + if (typeof cursorBlinkingStyle !== 'string') { + return defaultValue; + } + switch (cursorBlinkingStyle) { + case 'blink': + return TextEditorCursorBlinkingStyle.Blink; + case 'smooth': + return TextEditorCursorBlinkingStyle.Smooth; + case 'phase': + return TextEditorCursorBlinkingStyle.Phase; + case 'expand': + return TextEditorCursorBlinkingStyle.Expand; + case 'visible': // maintain compatibility + case 'solid': + return TextEditorCursorBlinkingStyle.Solid; + } + return TextEditorCursorBlinkingStyle.Blink; +} + +function _scrollbarVisibilityFromString(visibility: string, defaultValue: ScrollbarVisibility): ScrollbarVisibility { + if (typeof visibility !== 'string') { + return defaultValue; + } + switch (visibility) { + case 'hidden': + return ScrollbarVisibility.Hidden; + case 'visible': + return ScrollbarVisibility.Visible; + default: + return ScrollbarVisibility.Auto; + } +} + +/** + * @internal + */ +export class EditorOptionsValidator { + + /** + * Validate raw editor options. + * i.e. since they can be defined by the user, they might be invalid. + */ + public static validate(opts: IEditorOptions, defaults: IValidatedEditorOptions): IValidatedEditorOptions { + let wordWrap = opts.wordWrap; + { + // Compatibility with old true or false values + if (wordWrap === true) { + wordWrap = 'on'; + } else if (wordWrap === false) { + wordWrap = 'off'; + } + + wordWrap = _stringSet<'off' | 'on' | 'wordWrapColumn' | 'bounded'>(wordWrap, defaults.wordWrap, ['off', 'on', 'wordWrapColumn', 'bounded']); + } + + const viewInfo = this._sanitizeViewInfo(opts, defaults.viewInfo); + const contribInfo = this._sanitizeContribInfo(opts, defaults.contribInfo); + + let configuredMulticursorModifier: 'altKey' | 'metaKey' | 'ctrlKey'; + if (typeof opts.multiCursorModifier === 'string') { + if (opts.multiCursorModifier === 'ctrlCmd') { + configuredMulticursorModifier = platform.isMacintosh ? 'metaKey' : 'ctrlKey'; + } else { + configuredMulticursorModifier = 'altKey'; + } + } + const multiCursorModifier = _stringSet<'altKey' | 'metaKey' | 'ctrlKey'>(configuredMulticursorModifier, defaults.multiCursorModifier, ['altKey', 'metaKey', 'ctrlKey']); + + return { + inDiffEditor: _boolean(opts.inDiffEditor, defaults.inDiffEditor), + wordSeparators: _string(opts.wordSeparators, defaults.wordSeparators), + lineNumbersMinChars: _clampedInt(opts.lineNumbersMinChars, defaults.lineNumbersMinChars, 1, 10), + lineDecorationsWidth: (typeof opts.lineDecorationsWidth === 'undefined' ? defaults.lineDecorationsWidth : opts.lineDecorationsWidth), + readOnly: _boolean(opts.readOnly, defaults.readOnly), + mouseStyle: _stringSet<'text' | 'default' | 'copy'>(opts.mouseStyle, defaults.mouseStyle, ['text', 'default', 'copy']), + disableLayerHinting: _boolean(opts.disableLayerHinting, defaults.disableLayerHinting), + automaticLayout: _boolean(opts.automaticLayout, defaults.automaticLayout), + wordWrap: wordWrap, + wordWrapColumn: _clampedInt(opts.wordWrapColumn, defaults.wordWrapColumn, 1, Constants.MAX_SAFE_SMALL_INTEGER), + wordWrapMinified: _boolean(opts.wordWrapMinified, defaults.wordWrapMinified), + wrappingIndent: _wrappingIndentFromString(opts.wrappingIndent, defaults.wrappingIndent), + wordWrapBreakBeforeCharacters: _string(opts.wordWrapBreakBeforeCharacters, defaults.wordWrapBreakBeforeCharacters), + wordWrapBreakAfterCharacters: _string(opts.wordWrapBreakAfterCharacters, defaults.wordWrapBreakAfterCharacters), + wordWrapBreakObtrusiveCharacters: _string(opts.wordWrapBreakObtrusiveCharacters, defaults.wordWrapBreakObtrusiveCharacters), + autoClosingBrackets: _boolean(opts.autoClosingBrackets, defaults.autoClosingBrackets), + autoIndent: _boolean(opts.autoIndent, defaults.autoIndent), + dragAndDrop: _boolean(opts.dragAndDrop, defaults.dragAndDrop), + emptySelectionClipboard: _boolean(opts.emptySelectionClipboard, defaults.emptySelectionClipboard), + useTabStops: _boolean(opts.useTabStops, defaults.useTabStops), + multiCursorModifier: multiCursorModifier, + accessibilitySupport: _stringSet<'auto' | 'on' | 'off'>(opts.accessibilitySupport, defaults.accessibilitySupport, ['auto', 'on', 'off']), + viewInfo: viewInfo, + contribInfo: contribInfo, + }; + } + + private static _sanitizeScrollbarOpts(opts: IEditorScrollbarOptions, defaults: InternalEditorScrollbarOptions, mouseWheelScrollSensitivity: number): InternalEditorScrollbarOptions { + if (typeof opts !== 'object') { + return defaults; + } + const horizontalScrollbarSize = _clampedInt(opts.horizontalScrollbarSize, defaults.horizontalScrollbarSize, 0, 1000); + const verticalScrollbarSize = _clampedInt(opts.verticalScrollbarSize, defaults.verticalScrollbarSize, 0, 1000); + return { + vertical: _scrollbarVisibilityFromString(opts.vertical, defaults.vertical), + horizontal: _scrollbarVisibilityFromString(opts.horizontal, defaults.horizontal), + + arrowSize: _clampedInt(opts.arrowSize, defaults.arrowSize, 0, 1000), + useShadows: _boolean(opts.useShadows, defaults.useShadows), + + verticalHasArrows: _boolean(opts.verticalHasArrows, defaults.verticalHasArrows), + horizontalHasArrows: _boolean(opts.horizontalHasArrows, defaults.horizontalHasArrows), + + horizontalScrollbarSize: horizontalScrollbarSize, + horizontalSliderSize: _clampedInt(opts.horizontalSliderSize, horizontalScrollbarSize, 0, 1000), + + verticalScrollbarSize: verticalScrollbarSize, + verticalSliderSize: _clampedInt(opts.verticalSliderSize, verticalScrollbarSize, 0, 1000), + + handleMouseWheel: _boolean(opts.handleMouseWheel, defaults.handleMouseWheel), + mouseWheelScrollSensitivity: mouseWheelScrollSensitivity + }; + } + + private static _sanitizeMinimapOpts(opts: IEditorMinimapOptions, defaults: InternalEditorMinimapOptions): InternalEditorMinimapOptions { + if (typeof opts !== 'object') { + return defaults; + } + return { + enabled: _boolean(opts.enabled, defaults.enabled), + showSlider: _stringSet<'always' | 'mouseover'>(opts.showSlider, defaults.showSlider, ['always', 'mouseover']), + renderCharacters: _boolean(opts.renderCharacters, defaults.renderCharacters), + maxColumn: _clampedInt(opts.maxColumn, defaults.maxColumn, 1, 10000), + }; + } + + private static _santizeFindOpts(opts: IEditorFindOptions, defaults: InternalEditorFindOptions): InternalEditorFindOptions { + if (typeof opts !== 'object') { + return defaults; + } + + return { + seedSearchStringFromSelection: _boolean(opts.seedSearchStringFromSelection, defaults.seedSearchStringFromSelection), + autoFindInSelection: _boolean(opts.autoFindInSelection, defaults.autoFindInSelection) + }; + } + + private static _sanitizeViewInfo(opts: IEditorOptions, defaults: InternalEditorViewOptions): InternalEditorViewOptions { + + let rulers: number[] = []; + if (Array.isArray(opts.rulers)) { + for (let i = 0, len = opts.rulers.length; i < len; i++) { + rulers.push(_clampedInt(opts.rulers[i], 0, 0, 10000)); + } + rulers.sort(); + } + + let renderLineNumbers: boolean = defaults.renderLineNumbers; + let renderCustomLineNumbers: (lineNumber: number) => string = defaults.renderCustomLineNumbers; + let renderRelativeLineNumbers: boolean = defaults.renderRelativeLineNumbers; + + if (typeof opts.lineNumbers !== 'undefined') { + let lineNumbers = opts.lineNumbers; + + // Compatibility with old true or false values + if (lineNumbers === true) { + lineNumbers = 'on'; + } else if (lineNumbers === false) { + lineNumbers = 'off'; + } + + if (typeof lineNumbers === 'function') { + renderLineNumbers = true; + renderCustomLineNumbers = lineNumbers; + renderRelativeLineNumbers = false; + } else if (lineNumbers === 'relative') { + renderLineNumbers = true; + renderCustomLineNumbers = null; + renderRelativeLineNumbers = true; + } else if (lineNumbers === 'on') { + renderLineNumbers = true; + renderCustomLineNumbers = null; + renderRelativeLineNumbers = false; + } else { + renderLineNumbers = false; + renderCustomLineNumbers = null; + renderRelativeLineNumbers = false; + } + } + + const fontLigatures = _boolean(opts.fontLigatures, defaults.fontLigatures); + const disableMonospaceOptimizations = _boolean(opts.disableMonospaceOptimizations, defaults.disableMonospaceOptimizations) || fontLigatures; + + let renderWhitespace = opts.renderWhitespace; + { + // Compatibility with old true or false values + if (renderWhitespace === true) { + renderWhitespace = 'boundary'; + } else if (renderWhitespace === false) { + renderWhitespace = 'none'; + } + renderWhitespace = _stringSet<'none' | 'boundary' | 'all'>(opts.renderWhitespace, defaults.renderWhitespace, ['none', 'boundary', 'all']); + } + + let renderLineHighlight = opts.renderLineHighlight; + { + // Compatibility with old true or false values + if (renderLineHighlight === true) { + renderLineHighlight = 'line'; + } else if (renderLineHighlight === false) { + renderLineHighlight = 'none'; + } + renderLineHighlight = _stringSet<'none' | 'gutter' | 'line' | 'all'>(opts.renderLineHighlight, defaults.renderLineHighlight, ['none', 'gutter', 'line', 'all']); + } + + const mouseWheelScrollSensitivity = _float(opts.mouseWheelScrollSensitivity, defaults.scrollbar.mouseWheelScrollSensitivity); + const scrollbar = this._sanitizeScrollbarOpts(opts.scrollbar, defaults.scrollbar, mouseWheelScrollSensitivity); + const minimap = this._sanitizeMinimapOpts(opts.minimap, defaults.minimap); + + return { + extraEditorClassName: _string(opts.extraEditorClassName, defaults.extraEditorClassName), + disableMonospaceOptimizations: disableMonospaceOptimizations, + rulers: rulers, + ariaLabel: _string(opts.ariaLabel, defaults.ariaLabel), + renderLineNumbers: renderLineNumbers, + renderCustomLineNumbers: renderCustomLineNumbers, + renderRelativeLineNumbers: renderRelativeLineNumbers, + selectOnLineNumbers: _boolean(opts.selectOnLineNumbers, defaults.selectOnLineNumbers), + glyphMargin: _boolean(opts.glyphMargin, defaults.glyphMargin), + revealHorizontalRightPadding: _clampedInt(opts.revealHorizontalRightPadding, defaults.revealHorizontalRightPadding, 0, 1000), + roundedSelection: _boolean(opts.roundedSelection, defaults.roundedSelection), + overviewRulerLanes: _clampedInt(opts.overviewRulerLanes, defaults.overviewRulerLanes, 0, 3), + overviewRulerBorder: _boolean(opts.overviewRulerBorder, defaults.overviewRulerBorder), + cursorBlinking: _cursorBlinkingStyleFromString(opts.cursorBlinking, defaults.cursorBlinking), + mouseWheelZoom: _boolean(opts.mouseWheelZoom, defaults.mouseWheelZoom), + cursorStyle: _cursorStyleFromString(opts.cursorStyle, defaults.cursorStyle), + hideCursorInOverviewRuler: _boolean(opts.hideCursorInOverviewRuler, defaults.hideCursorInOverviewRuler), + scrollBeyondLastLine: _boolean(opts.scrollBeyondLastLine, defaults.scrollBeyondLastLine), + smoothScrolling: _boolean(opts.smoothScrolling, defaults.smoothScrolling), + stopRenderingLineAfter: _clampedInt(opts.stopRenderingLineAfter, defaults.stopRenderingLineAfter, -1, Constants.MAX_SAFE_SMALL_INTEGER), + renderWhitespace: renderWhitespace, + renderControlCharacters: _boolean(opts.renderControlCharacters, defaults.renderControlCharacters), + fontLigatures: fontLigatures, + renderIndentGuides: _boolean(opts.renderIndentGuides, defaults.renderIndentGuides), + renderLineHighlight: renderLineHighlight, + scrollbar: scrollbar, + minimap: minimap, + fixedOverflowWidgets: _boolean(opts.fixedOverflowWidgets, defaults.fixedOverflowWidgets), + }; + } + + private static _sanitizeContribInfo(opts: IEditorOptions, defaults: EditorContribOptions): EditorContribOptions { + let quickSuggestions: boolean | { other: boolean, comments: boolean, strings: boolean }; + if (typeof opts.quickSuggestions === 'object') { + quickSuggestions = { other: true, ...opts.quickSuggestions }; + } else { + quickSuggestions = _boolean(opts.quickSuggestions, defaults.quickSuggestions); + } + const find = this._santizeFindOpts(opts.find, defaults.find); + return { + selectionClipboard: _boolean(opts.selectionClipboard, defaults.selectionClipboard), + hover: _boolean(opts.hover, defaults.hover), + links: _boolean(opts.links, defaults.links), + contextmenu: _boolean(opts.contextmenu, defaults.contextmenu), + quickSuggestions: quickSuggestions, + quickSuggestionsDelay: _clampedInt(opts.quickSuggestionsDelay, defaults.quickSuggestionsDelay, Constants.MIN_SAFE_SMALL_INTEGER, Constants.MAX_SAFE_SMALL_INTEGER), + parameterHints: _boolean(opts.parameterHints, defaults.parameterHints), + iconsInSuggestions: _boolean(opts.iconsInSuggestions, defaults.iconsInSuggestions), + formatOnType: _boolean(opts.formatOnType, defaults.formatOnType), + formatOnPaste: _boolean(opts.formatOnPaste, defaults.formatOnPaste), + suggestOnTriggerCharacters: _boolean(opts.suggestOnTriggerCharacters, defaults.suggestOnTriggerCharacters), + acceptSuggestionOnEnter: _stringSet<'on' | 'smart' | 'off'>(opts.acceptSuggestionOnEnter, defaults.acceptSuggestionOnEnter, ['on', 'smart', 'off']), + acceptSuggestionOnCommitCharacter: _boolean(opts.acceptSuggestionOnCommitCharacter, defaults.acceptSuggestionOnCommitCharacter), + snippetSuggestions: _stringSet<'top' | 'bottom' | 'inline' | 'none'>(opts.snippetSuggestions, defaults.snippetSuggestions, ['top', 'bottom', 'inline', 'none']), + wordBasedSuggestions: _boolean(opts.wordBasedSuggestions, defaults.wordBasedSuggestions), + suggestFontSize: _clampedInt(opts.suggestFontSize, defaults.suggestFontSize, 0, 1000), + suggestLineHeight: _clampedInt(opts.suggestLineHeight, defaults.suggestLineHeight, 0, 1000), + selectionHighlight: _boolean(opts.selectionHighlight, defaults.selectionHighlight), + occurrencesHighlight: _boolean(opts.occurrencesHighlight, defaults.occurrencesHighlight), + codeLens: _boolean(opts.codeLens, defaults.codeLens) && _boolean(opts.referenceInfos, true), + folding: _boolean(opts.folding, defaults.folding), + showFoldingControls: _stringSet<'always' | 'mouseover'>(opts.showFoldingControls, defaults.showFoldingControls, ['always', 'mouseover']), + matchBrackets: _boolean(opts.matchBrackets, defaults.matchBrackets), + find: find, + colorDecorators: _boolean(opts.colorDecorators, defaults.colorDecorators), + }; + } +} + +/** + * @internal + */ +export class InternalEditorOptionsFactory { + + private static _tweakValidatedOptions(opts: IValidatedEditorOptions, accessibilitySupport: platform.AccessibilitySupport): IValidatedEditorOptions { + const accessibilityIsOn = (accessibilitySupport === platform.AccessibilitySupport.Enabled); + const accessibilityIsOff = (accessibilitySupport === platform.AccessibilitySupport.Disabled); + return { + inDiffEditor: opts.inDiffEditor, + wordSeparators: opts.wordSeparators, + lineNumbersMinChars: opts.lineNumbersMinChars, + lineDecorationsWidth: opts.lineDecorationsWidth, + readOnly: opts.readOnly, + mouseStyle: opts.mouseStyle, + disableLayerHinting: opts.disableLayerHinting, + automaticLayout: opts.automaticLayout, + wordWrap: opts.wordWrap, + wordWrapColumn: opts.wordWrapColumn, + wordWrapMinified: opts.wordWrapMinified, + wrappingIndent: opts.wrappingIndent, + wordWrapBreakBeforeCharacters: opts.wordWrapBreakBeforeCharacters, + wordWrapBreakAfterCharacters: opts.wordWrapBreakAfterCharacters, + wordWrapBreakObtrusiveCharacters: opts.wordWrapBreakObtrusiveCharacters, + autoClosingBrackets: opts.autoClosingBrackets, + autoIndent: opts.autoIndent, + dragAndDrop: opts.dragAndDrop, + emptySelectionClipboard: opts.emptySelectionClipboard, + useTabStops: opts.useTabStops, + multiCursorModifier: opts.multiCursorModifier, + accessibilitySupport: opts.accessibilitySupport, + + viewInfo: { + extraEditorClassName: opts.viewInfo.extraEditorClassName, + disableMonospaceOptimizations: opts.viewInfo.disableMonospaceOptimizations, + rulers: opts.viewInfo.rulers, + ariaLabel: (accessibilityIsOff ? nls.localize('accessibilityOffAriaLabel', "The editor is not accessible at this time. Press Alt+F1 for options.") : opts.viewInfo.ariaLabel), + renderLineNumbers: opts.viewInfo.renderLineNumbers, + renderCustomLineNumbers: opts.viewInfo.renderCustomLineNumbers, + renderRelativeLineNumbers: opts.viewInfo.renderRelativeLineNumbers, + selectOnLineNumbers: opts.viewInfo.selectOnLineNumbers, + glyphMargin: opts.viewInfo.glyphMargin, + revealHorizontalRightPadding: opts.viewInfo.revealHorizontalRightPadding, + roundedSelection: (accessibilityIsOn ? false : opts.viewInfo.roundedSelection), // DISABLED WHEN SCREEN READER IS ATTACHED + overviewRulerLanes: opts.viewInfo.overviewRulerLanes, + overviewRulerBorder: opts.viewInfo.overviewRulerBorder, + cursorBlinking: opts.viewInfo.cursorBlinking, + mouseWheelZoom: opts.viewInfo.mouseWheelZoom, + cursorStyle: opts.viewInfo.cursorStyle, + hideCursorInOverviewRuler: opts.viewInfo.hideCursorInOverviewRuler, + scrollBeyondLastLine: opts.viewInfo.scrollBeyondLastLine, + smoothScrolling: opts.viewInfo.smoothScrolling, + stopRenderingLineAfter: opts.viewInfo.stopRenderingLineAfter, + renderWhitespace: (accessibilityIsOn ? 'none' : opts.viewInfo.renderWhitespace), // DISABLED WHEN SCREEN READER IS ATTACHED + renderControlCharacters: (accessibilityIsOn ? false : opts.viewInfo.renderControlCharacters), // DISABLED WHEN SCREEN READER IS ATTACHED + fontLigatures: (accessibilityIsOn ? false : opts.viewInfo.fontLigatures), // DISABLED WHEN SCREEN READER IS ATTACHED + renderIndentGuides: (accessibilityIsOn ? false : opts.viewInfo.renderIndentGuides), // DISABLED WHEN SCREEN READER IS ATTACHED + renderLineHighlight: (accessibilityIsOn ? 'none' : opts.viewInfo.renderLineHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED + scrollbar: opts.viewInfo.scrollbar, + minimap: { + enabled: (accessibilityIsOn ? false : opts.viewInfo.minimap.enabled), // DISABLED WHEN SCREEN READER IS ATTACHED + renderCharacters: opts.viewInfo.minimap.renderCharacters, + showSlider: opts.viewInfo.minimap.showSlider, + maxColumn: opts.viewInfo.minimap.maxColumn + }, + fixedOverflowWidgets: opts.viewInfo.fixedOverflowWidgets + }, + + contribInfo: { + selectionClipboard: opts.contribInfo.selectionClipboard, + hover: opts.contribInfo.hover, + links: (accessibilityIsOn ? false : opts.contribInfo.links), // DISABLED WHEN SCREEN READER IS ATTACHED + contextmenu: opts.contribInfo.contextmenu, + quickSuggestions: opts.contribInfo.quickSuggestions, + quickSuggestionsDelay: opts.contribInfo.quickSuggestionsDelay, + parameterHints: opts.contribInfo.parameterHints, + iconsInSuggestions: opts.contribInfo.iconsInSuggestions, + formatOnType: opts.contribInfo.formatOnType, + formatOnPaste: opts.contribInfo.formatOnPaste, + suggestOnTriggerCharacters: opts.contribInfo.suggestOnTriggerCharacters, + acceptSuggestionOnEnter: opts.contribInfo.acceptSuggestionOnEnter, + acceptSuggestionOnCommitCharacter: opts.contribInfo.acceptSuggestionOnCommitCharacter, + snippetSuggestions: opts.contribInfo.snippetSuggestions, + wordBasedSuggestions: opts.contribInfo.wordBasedSuggestions, + suggestFontSize: opts.contribInfo.suggestFontSize, + suggestLineHeight: opts.contribInfo.suggestLineHeight, + selectionHighlight: (accessibilityIsOn ? false : opts.contribInfo.selectionHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED + occurrencesHighlight: (accessibilityIsOn ? false : opts.contribInfo.occurrencesHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED + codeLens: (accessibilityIsOn ? false : opts.contribInfo.codeLens), // DISABLED WHEN SCREEN READER IS ATTACHED + folding: (accessibilityIsOn ? false : opts.contribInfo.folding), // DISABLED WHEN SCREEN READER IS ATTACHED + showFoldingControls: opts.contribInfo.showFoldingControls, + matchBrackets: (accessibilityIsOn ? false : opts.contribInfo.matchBrackets), // DISABLED WHEN SCREEN READER IS ATTACHED + find: opts.contribInfo.find, + colorDecorators: opts.contribInfo.colorDecorators + } + }; + } + + public static createInternalEditorOptions(env: IEnvironmentalOptions, _opts: IValidatedEditorOptions) { + + let accessibilitySupport: platform.AccessibilitySupport; + if (_opts.accessibilitySupport === 'auto') { + // The editor reads the `accessibilitySupport` from the environment + accessibilitySupport = env.accessibilitySupport; + } else if (_opts.accessibilitySupport === 'on') { + accessibilitySupport = platform.AccessibilitySupport.Enabled; + } else { + accessibilitySupport = platform.AccessibilitySupport.Disabled; + } + + // Disable some non critical features to get as best performance as possible + // See https://github.com/Microsoft/vscode/issues/26730 + const opts = this._tweakValidatedOptions(_opts, accessibilitySupport); + + let lineDecorationsWidth: number; + if (typeof opts.lineDecorationsWidth === 'string' && /^\d+(\.\d+)?ch$/.test(opts.lineDecorationsWidth)) { + const multiple = parseFloat(opts.lineDecorationsWidth.substr(0, opts.lineDecorationsWidth.length - 2)); + lineDecorationsWidth = multiple * env.fontInfo.typicalHalfwidthCharacterWidth; + } else { + lineDecorationsWidth = _clampedInt(opts.lineDecorationsWidth, 0, 0, 1000); + } + if (opts.contribInfo.folding) { + lineDecorationsWidth += 16; + } + + const layoutInfo = EditorLayoutProvider.compute({ + outerWidth: env.outerWidth, + outerHeight: env.outerHeight, + showGlyphMargin: opts.viewInfo.glyphMargin, + lineHeight: env.fontInfo.lineHeight, + showLineNumbers: opts.viewInfo.renderLineNumbers, + lineNumbersMinChars: opts.lineNumbersMinChars, + lineNumbersDigitCount: env.lineNumbersDigitCount, + lineDecorationsWidth: lineDecorationsWidth, + typicalHalfwidthCharacterWidth: env.fontInfo.typicalHalfwidthCharacterWidth, + maxDigitWidth: env.fontInfo.maxDigitWidth, + verticalScrollbarWidth: opts.viewInfo.scrollbar.verticalScrollbarSize, + horizontalScrollbarHeight: opts.viewInfo.scrollbar.horizontalScrollbarSize, + scrollbarArrowSize: opts.viewInfo.scrollbar.arrowSize, + verticalScrollbarHasArrows: opts.viewInfo.scrollbar.verticalHasArrows, + minimap: opts.viewInfo.minimap.enabled, + minimapRenderCharacters: opts.viewInfo.minimap.renderCharacters, + minimapMaxColumn: opts.viewInfo.minimap.maxColumn, + pixelRatio: env.pixelRatio + }); + + let bareWrappingInfo: { isWordWrapMinified: boolean; isViewportWrapping: boolean; wrappingColumn: number; } = null; + { + const wordWrap = opts.wordWrap; + const wordWrapColumn = opts.wordWrapColumn; + const wordWrapMinified = opts.wordWrapMinified; + + if (accessibilitySupport === platform.AccessibilitySupport.Enabled) { + // See https://github.com/Microsoft/vscode/issues/27766 + // Never enable wrapping when a screen reader is attached + // because arrow down etc. will not move the cursor in the way + // a screen reader expects. + bareWrappingInfo = { + isWordWrapMinified: false, + isViewportWrapping: false, + wrappingColumn: -1 + }; + } else if (wordWrapMinified && env.isDominatedByLongLines) { + // Force viewport width wrapping if model is dominated by long lines + bareWrappingInfo = { + isWordWrapMinified: true, + isViewportWrapping: true, + wrappingColumn: Math.max(1, layoutInfo.viewportColumn) + }; + } else if (wordWrap === 'on') { + bareWrappingInfo = { + isWordWrapMinified: false, + isViewportWrapping: true, + wrappingColumn: Math.max(1, layoutInfo.viewportColumn) + }; + } else if (wordWrap === 'bounded') { + bareWrappingInfo = { + isWordWrapMinified: false, + isViewportWrapping: true, + wrappingColumn: Math.min(Math.max(1, layoutInfo.viewportColumn), wordWrapColumn) + }; + } else if (wordWrap === 'wordWrapColumn') { + bareWrappingInfo = { + isWordWrapMinified: false, + isViewportWrapping: false, + wrappingColumn: wordWrapColumn + }; + } else { + bareWrappingInfo = { + isWordWrapMinified: false, + isViewportWrapping: false, + wrappingColumn: -1 + }; + } + } + + const wrappingInfo: EditorWrappingInfo = { + inDiffEditor: opts.inDiffEditor, + isDominatedByLongLines: env.isDominatedByLongLines, + isWordWrapMinified: bareWrappingInfo.isWordWrapMinified, + isViewportWrapping: bareWrappingInfo.isViewportWrapping, + wrappingColumn: bareWrappingInfo.wrappingColumn, + wrappingIndent: opts.wrappingIndent, + wordWrapBreakBeforeCharacters: opts.wordWrapBreakBeforeCharacters, + wordWrapBreakAfterCharacters: opts.wordWrapBreakAfterCharacters, + wordWrapBreakObtrusiveCharacters: opts.wordWrapBreakObtrusiveCharacters, + }; + + let className = 'monaco-editor'; + if (opts.viewInfo.extraEditorClassName) { + className += ' ' + opts.viewInfo.extraEditorClassName; + } + if (env.extraEditorClassName) { + className += ' ' + env.extraEditorClassName; + } + if (opts.viewInfo.fontLigatures) { + className += ' enable-ligatures'; + } + if (opts.mouseStyle === 'default') { + className += ' mouse-default'; + } else if (opts.mouseStyle === 'copy') { + className += ' mouse-copy'; + } + + return new InternalEditorOptions({ + canUseLayerHinting: opts.disableLayerHinting ? false : true, + pixelRatio: env.pixelRatio, + editorClassName: className, + lineHeight: env.fontInfo.lineHeight, + readOnly: opts.readOnly, + accessibilitySupport: accessibilitySupport, + multiCursorModifier: opts.multiCursorModifier, + wordSeparators: opts.wordSeparators, + autoClosingBrackets: opts.autoClosingBrackets, + autoIndent: opts.autoIndent, + useTabStops: opts.useTabStops, + tabFocusMode: opts.readOnly ? true : env.tabFocusMode, + dragAndDrop: opts.dragAndDrop, + emptySelectionClipboard: opts.emptySelectionClipboard && env.emptySelectionClipboard, + layoutInfo: layoutInfo, + fontInfo: env.fontInfo, + viewInfo: opts.viewInfo, + wrappingInfo: wrappingInfo, + contribInfo: opts.contribInfo + }); + } +} + +/** + * @internal + */ +export interface IEditorLayoutProviderOpts { + outerWidth: number; + outerHeight: number; + + showGlyphMargin: boolean; + lineHeight: number; + + showLineNumbers: boolean; + lineNumbersMinChars: number; + lineNumbersDigitCount: number; + + lineDecorationsWidth: number; + + typicalHalfwidthCharacterWidth: number; + maxDigitWidth: number; + + verticalScrollbarWidth: number; + verticalScrollbarHasArrows: boolean; + scrollbarArrowSize: number; + horizontalScrollbarHeight: number; + + minimap: boolean; + minimapRenderCharacters: boolean; + minimapMaxColumn: number; + pixelRatio: number; +} + +/** + * @internal + */ +export class EditorLayoutProvider { + public static compute(_opts: IEditorLayoutProviderOpts): EditorLayoutInfo { + const outerWidth = _opts.outerWidth | 0; + const outerHeight = _opts.outerHeight | 0; + const showGlyphMargin = _opts.showGlyphMargin; + const lineHeight = _opts.lineHeight | 0; + const showLineNumbers = _opts.showLineNumbers; + const lineNumbersMinChars = _opts.lineNumbersMinChars | 0; + const lineNumbersDigitCount = _opts.lineNumbersDigitCount | 0; + const lineDecorationsWidth = _opts.lineDecorationsWidth | 0; + const typicalHalfwidthCharacterWidth = _opts.typicalHalfwidthCharacterWidth; + const maxDigitWidth = _opts.maxDigitWidth; + const verticalScrollbarWidth = _opts.verticalScrollbarWidth | 0; + const verticalScrollbarHasArrows = _opts.verticalScrollbarHasArrows; + const scrollbarArrowSize = _opts.scrollbarArrowSize | 0; + const horizontalScrollbarHeight = _opts.horizontalScrollbarHeight | 0; + const minimap = _opts.minimap; + const minimapRenderCharacters = _opts.minimapRenderCharacters; + const minimapMaxColumn = _opts.minimapMaxColumn | 0; + const pixelRatio = _opts.pixelRatio; + + let lineNumbersWidth = 0; + if (showLineNumbers) { + const digitCount = Math.max(lineNumbersDigitCount, lineNumbersMinChars); + lineNumbersWidth = Math.round(digitCount * maxDigitWidth); + } + + let glyphMarginWidth = 0; + if (showGlyphMargin) { + glyphMarginWidth = lineHeight; + } + + const glyphMarginLeft = 0; + const lineNumbersLeft = glyphMarginLeft + glyphMarginWidth; + const decorationsLeft = lineNumbersLeft + lineNumbersWidth; + const contentLeft = decorationsLeft + lineDecorationsWidth; + + const remainingWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth; + + let renderMinimap: RenderMinimap; + let minimapWidth: number; + let contentWidth: number; + if (!minimap) { + minimapWidth = 0; + renderMinimap = RenderMinimap.None; + contentWidth = remainingWidth; + } else { + let minimapCharWidth: number; + if (pixelRatio >= 2) { + renderMinimap = minimapRenderCharacters ? RenderMinimap.Large : RenderMinimap.LargeBlocks; + minimapCharWidth = 2 / pixelRatio; + } else { + renderMinimap = minimapRenderCharacters ? RenderMinimap.Small : RenderMinimap.SmallBlocks; + minimapCharWidth = 1 / pixelRatio; + } + + // Given: + // viewportColumn = (contentWidth - verticalScrollbarWidth) / typicalHalfwidthCharacterWidth + // minimapWidth = viewportColumn * minimapCharWidth + // contentWidth = remainingWidth - minimapWidth + // What are good values for contentWidth and minimapWidth ? + + // minimapWidth = ((contentWidth - verticalScrollbarWidth) / typicalHalfwidthCharacterWidth) * minimapCharWidth + // typicalHalfwidthCharacterWidth * minimapWidth = (contentWidth - verticalScrollbarWidth) * minimapCharWidth + // typicalHalfwidthCharacterWidth * minimapWidth = (remainingWidth - minimapWidth - verticalScrollbarWidth) * minimapCharWidth + // (typicalHalfwidthCharacterWidth + minimapCharWidth) * minimapWidth = (remainingWidth - verticalScrollbarWidth) * minimapCharWidth + // minimapWidth = ((remainingWidth - verticalScrollbarWidth) * minimapCharWidth) / (typicalHalfwidthCharacterWidth + minimapCharWidth) + + minimapWidth = Math.max(0, Math.floor(((remainingWidth - verticalScrollbarWidth) * minimapCharWidth) / (typicalHalfwidthCharacterWidth + minimapCharWidth))); + let minimapColumns = minimapWidth / minimapCharWidth; + if (minimapColumns > minimapMaxColumn) { + minimapWidth = Math.floor(minimapMaxColumn * minimapCharWidth); + } + contentWidth = remainingWidth - minimapWidth; + } + + const viewportColumn = Math.max(1, Math.floor((contentWidth - verticalScrollbarWidth) / typicalHalfwidthCharacterWidth)); + + const verticalArrowSize = (verticalScrollbarHasArrows ? scrollbarArrowSize : 0); + + return { + width: outerWidth, + height: outerHeight, + + glyphMarginLeft: glyphMarginLeft, + glyphMarginWidth: glyphMarginWidth, + glyphMarginHeight: outerHeight, + + lineNumbersLeft: lineNumbersLeft, + lineNumbersWidth: lineNumbersWidth, + lineNumbersHeight: outerHeight, + + decorationsLeft: decorationsLeft, + decorationsWidth: lineDecorationsWidth, + decorationsHeight: outerHeight, + + contentLeft: contentLeft, + contentWidth: contentWidth, + contentHeight: outerHeight, + + renderMinimap: renderMinimap, + minimapWidth: minimapWidth, + + viewportColumn: viewportColumn, + + verticalScrollbarWidth: verticalScrollbarWidth, + horizontalScrollbarHeight: horizontalScrollbarHeight, + + overviewRuler: { + top: verticalArrowSize, + width: verticalScrollbarWidth, + height: (outerHeight - 2 * verticalArrowSize), + right: 0 + } + }; + } +} + +const DEFAULT_WINDOWS_FONT_FAMILY = 'Consolas, \'Courier New\', monospace'; +const DEFAULT_MAC_FONT_FAMILY = 'Menlo, Monaco, \'Courier New\', monospace'; +const DEFAULT_LINUX_FONT_FAMILY = '\'Droid Sans Mono\', \'Courier New\', monospace, \'Droid Sans Fallback\''; + +/** + * @internal + */ +export const EDITOR_FONT_DEFAULTS = { + fontFamily: ( + platform.isMacintosh ? DEFAULT_MAC_FONT_FAMILY : (platform.isLinux ? DEFAULT_LINUX_FONT_FAMILY : DEFAULT_WINDOWS_FONT_FAMILY) + ), + fontWeight: 'normal', + fontSize: ( + platform.isMacintosh ? 12 : 14 + ), + lineHeight: 0, + letterSpacing: 0, +}; + +/** + * @internal + */ +export const EDITOR_MODEL_DEFAULTS = { + tabSize: 4, + insertSpaces: true, + detectIndentation: true, + trimAutoWhitespace: true +}; + +/** + * @internal + */ +export const EDITOR_DEFAULTS: IValidatedEditorOptions = { + inDiffEditor: false, + wordSeparators: USUAL_WORD_SEPARATORS, + lineNumbersMinChars: 5, + lineDecorationsWidth: 10, + readOnly: false, + mouseStyle: 'text', + disableLayerHinting: false, + automaticLayout: false, + wordWrap: 'off', + wordWrapColumn: 80, + wordWrapMinified: true, + wrappingIndent: WrappingIndent.Same, + wordWrapBreakBeforeCharacters: '([{‘“〈《「『【〔([{「£¥$£¥++', + wordWrapBreakAfterCharacters: ' \t})]?|&,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」', + wordWrapBreakObtrusiveCharacters: '.', + autoClosingBrackets: true, + autoIndent: true, + dragAndDrop: true, + emptySelectionClipboard: true, + useTabStops: true, + multiCursorModifier: 'altKey', + accessibilitySupport: 'auto', + + viewInfo: { + extraEditorClassName: '', + disableMonospaceOptimizations: false, + rulers: [], + ariaLabel: nls.localize('editorViewAccessibleLabel', "Editor content"), + renderLineNumbers: true, + renderCustomLineNumbers: null, + renderRelativeLineNumbers: false, + selectOnLineNumbers: true, + glyphMargin: true, + revealHorizontalRightPadding: 30, + roundedSelection: true, + overviewRulerLanes: 2, + overviewRulerBorder: true, + cursorBlinking: TextEditorCursorBlinkingStyle.Blink, + mouseWheelZoom: false, + cursorStyle: TextEditorCursorStyle.Line, + hideCursorInOverviewRuler: false, + scrollBeyondLastLine: true, + smoothScrolling: false, + stopRenderingLineAfter: 10000, + renderWhitespace: 'none', + renderControlCharacters: false, + fontLigatures: false, + renderIndentGuides: true, + renderLineHighlight: 'line', + scrollbar: { + vertical: ScrollbarVisibility.Auto, + horizontal: ScrollbarVisibility.Auto, + arrowSize: 11, + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false, + horizontalScrollbarSize: 10, + horizontalSliderSize: 10, + verticalScrollbarSize: 14, + verticalSliderSize: 14, + handleMouseWheel: true, + mouseWheelScrollSensitivity: 1, + }, + minimap: { + // {{SQL CARBON EDIT}} + enabled: false, + showSlider: 'mouseover', + renderCharacters: true, + maxColumn: 120 + }, + fixedOverflowWidgets: false, + }, + + contribInfo: { + selectionClipboard: true, + hover: true, + links: true, + contextmenu: true, + quickSuggestions: { other: true, comments: false, strings: false }, + quickSuggestionsDelay: 10, + parameterHints: true, + iconsInSuggestions: true, + formatOnType: false, + formatOnPaste: false, + suggestOnTriggerCharacters: true, + acceptSuggestionOnEnter: 'on', + acceptSuggestionOnCommitCharacter: true, + snippetSuggestions: 'inline', + wordBasedSuggestions: true, + suggestFontSize: 0, + suggestLineHeight: 0, + selectionHighlight: true, + occurrencesHighlight: true, + codeLens: true, + folding: true, + showFoldingControls: 'mouseover', + matchBrackets: true, + find: { + seedSearchStringFromSelection: true, + autoFindInSelection: false + }, + colorDecorators: true + }, +}; diff --git a/src/vs/editor/common/config/editorZoom.ts b/src/vs/editor/common/config/editorZoom.ts new file mode 100644 index 0000000000..5b2319a575 --- /dev/null +++ b/src/vs/editor/common/config/editorZoom.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event, { Emitter } from 'vs/base/common/event'; + +export interface IEditorZoom { + onDidChangeZoomLevel: Event; + getZoomLevel(): number; + setZoomLevel(zoomLevel: number): void; +} + +export const EditorZoom: IEditorZoom = new class { + + private _zoomLevel: number = 0; + + private _onDidChangeZoomLevel: Emitter = new Emitter(); + public onDidChangeZoomLevel: Event = this._onDidChangeZoomLevel.event; + + public getZoomLevel(): number { + return this._zoomLevel; + } + + public setZoomLevel(zoomLevel: number): void { + zoomLevel = Math.min(Math.max(-9, zoomLevel), 9); + if (this._zoomLevel === zoomLevel) { + return; + } + + this._zoomLevel = zoomLevel; + this._onDidChangeZoomLevel.fire(this._zoomLevel); + } +}; diff --git a/src/vs/editor/common/config/fontInfo.ts b/src/vs/editor/common/config/fontInfo.ts new file mode 100644 index 0000000000..8c1c901063 --- /dev/null +++ b/src/vs/editor/common/config/fontInfo.ts @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as platform from 'vs/base/common/platform'; +import { EditorZoom } from 'vs/editor/common/config/editorZoom'; +import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; + +/** + * Determined from empirical observations. + * @internal + */ +const GOLDEN_LINE_HEIGHT_RATIO = platform.isMacintosh ? 1.5 : 1.35; + +function safeParseFloat(n: number | string, defaultValue: number): number { + if (typeof n === 'number') { + return n; + } + let r = parseFloat(n); + if (isNaN(r)) { + return defaultValue; + } + return r; +} + +function safeParseInt(n: number | string, defaultValue: number): number { + if (typeof n === 'number') { + return Math.round(n); + } + let r = parseInt(n); + if (isNaN(r)) { + return defaultValue; + } + return r; +} + +function clamp(n: number, min: number, max: number): number { + if (n < min) { + return min; + } + if (n > max) { + return max; + } + return n; +} + +function _string(value: any, defaultValue: string): string { + if (typeof value !== 'string') { + return defaultValue; + } + return value; +} + +export class BareFontInfo { + readonly _bareFontInfoBrand: void; + + /** + * @internal + */ + public static createFromRawSettings(opts: { + fontFamily?: string; + fontWeight?: string; + fontSize?: number | string; + lineHeight?: number | string; + letterSpacing?: number | string; + }, zoomLevel: number): BareFontInfo { + + let fontFamily = _string(opts.fontFamily, EDITOR_FONT_DEFAULTS.fontFamily); + let fontWeight = _string(opts.fontWeight, EDITOR_FONT_DEFAULTS.fontWeight); + + let fontSize = safeParseFloat(opts.fontSize, EDITOR_FONT_DEFAULTS.fontSize); + fontSize = clamp(fontSize, 0, 100); + if (fontSize === 0) { + fontSize = EDITOR_FONT_DEFAULTS.fontSize; + } else if (fontSize < 8) { + fontSize = 8; + } + + let lineHeight = safeParseInt(opts.lineHeight, 0); + lineHeight = clamp(lineHeight, 0, 150); + if (lineHeight === 0) { + lineHeight = Math.round(GOLDEN_LINE_HEIGHT_RATIO * fontSize); + } else if (lineHeight < 8) { + lineHeight = 8; + } + + let letterSpacing = safeParseFloat(opts.letterSpacing, 0); + letterSpacing = clamp(letterSpacing, -20, 20); + + let editorZoomLevelMultiplier = 1 + (EditorZoom.getZoomLevel() * 0.1); + fontSize *= editorZoomLevelMultiplier; + lineHeight *= editorZoomLevelMultiplier; + + return new BareFontInfo({ + zoomLevel: zoomLevel, + fontFamily: fontFamily, + fontWeight: fontWeight, + fontSize: fontSize, + lineHeight: lineHeight, + letterSpacing: letterSpacing + }); + } + + readonly zoomLevel: number; + readonly fontFamily: string; + readonly fontWeight: string; + readonly fontSize: number; + readonly lineHeight: number; + readonly letterSpacing: number; + + /** + * @internal + */ + protected constructor(opts: { + zoomLevel: number; + fontFamily: string; + fontWeight: string; + fontSize: number; + lineHeight: number; + letterSpacing: number; + }) { + this.zoomLevel = opts.zoomLevel; + this.fontFamily = String(opts.fontFamily); + this.fontWeight = String(opts.fontWeight); + this.fontSize = opts.fontSize; + this.lineHeight = opts.lineHeight | 0; + this.letterSpacing = opts.letterSpacing; + } + + /** + * @internal + */ + public getId(): string { + return this.zoomLevel + '-' + this.fontFamily + '-' + this.fontWeight + '-' + this.fontSize + '-' + this.lineHeight + '-' + this.letterSpacing; + } +} + +export class FontInfo extends BareFontInfo { + readonly _editorStylingBrand: void; + + readonly isTrusted: boolean; + readonly isMonospace: boolean; + readonly typicalHalfwidthCharacterWidth: number; + readonly typicalFullwidthCharacterWidth: number; + readonly spaceWidth: number; + readonly maxDigitWidth: number; + + /** + * @internal + */ + constructor(opts: { + zoomLevel: number; + fontFamily: string; + fontWeight: string; + fontSize: number; + lineHeight: number; + letterSpacing: number; + isMonospace: boolean; + typicalHalfwidthCharacterWidth: number; + typicalFullwidthCharacterWidth: number; + spaceWidth: number; + maxDigitWidth: number; + }, isTrusted: boolean) { + super(opts); + this.isTrusted = isTrusted; + this.isMonospace = opts.isMonospace; + this.typicalHalfwidthCharacterWidth = opts.typicalHalfwidthCharacterWidth; + this.typicalFullwidthCharacterWidth = opts.typicalFullwidthCharacterWidth; + this.spaceWidth = opts.spaceWidth; + this.maxDigitWidth = opts.maxDigitWidth; + } + + /** + * @internal + */ + public equals(other: FontInfo): boolean { + return ( + this.fontFamily === other.fontFamily + && this.fontWeight === other.fontWeight + && this.fontSize === other.fontSize + && this.lineHeight === other.lineHeight + && this.letterSpacing === other.letterSpacing + && this.typicalHalfwidthCharacterWidth === other.typicalHalfwidthCharacterWidth + && this.typicalFullwidthCharacterWidth === other.typicalFullwidthCharacterWidth + && this.spaceWidth === other.spaceWidth + && this.maxDigitWidth === other.maxDigitWidth + ); + } +} diff --git a/src/vs/editor/common/controller/coreCommands.ts b/src/vs/editor/common/controller/coreCommands.ts new file mode 100644 index 0000000000..e0b0738136 --- /dev/null +++ b/src/vs/editor/common/controller/coreCommands.ts @@ -0,0 +1,1792 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { CursorState, ICursors, RevealTarget, IColumnSelectData, CursorContext } from 'vs/editor/common/controller/cursorCommon'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { CursorMoveCommands, CursorMove as CursorMove_ } from 'vs/editor/common/controller/cursorMoveCommands'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { registerEditorCommand, ICommandOptions, EditorCommand, Command } from 'vs/editor/common/editorCommonExtensions'; +import { IColumnSelectResult, ColumnSelection } from 'vs/editor/common/controller/cursorColumnSelection'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import H = editorCommon.Handler; +import { ICodeEditorService, getCodeEditor } from 'vs/editor/common/services/codeEditorService'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import * as types from 'vs/base/common/types'; +import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; +import { IEditorService } from 'vs/platform/editor/common/editor'; +import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; +import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; +import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; + +const CORE_WEIGHT = KeybindingsRegistry.WEIGHT.editorCore(); + +export abstract class CoreEditorCommand extends EditorCommand { + public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void { + this.runCoreEditorCommand(editor._getCursors(), args || {}); + } + + public abstract runCoreEditorCommand(cursors: ICursors, args: any): void; +} + +export namespace EditorScroll_ { + + const isEditorScrollArgs = function (arg: any): boolean { + if (!types.isObject(arg)) { + return false; + } + + let scrollArg: RawArguments = arg; + + if (!types.isString(scrollArg.to)) { + return false; + } + + if (!types.isUndefined(scrollArg.by) && !types.isString(scrollArg.by)) { + return false; + } + + if (!types.isUndefined(scrollArg.value) && !types.isNumber(scrollArg.value)) { + return false; + } + + if (!types.isUndefined(scrollArg.revealCursor) && !types.isBoolean(scrollArg.revealCursor)) { + return false; + } + + return true; + }; + + export const description = { + description: 'Scroll editor in the given direction', + args: [ + { + name: 'Editor scroll argument object', + description: `Property-value pairs that can be passed through this argument: + * 'to': A mandatory direction value. + \`\`\` + 'up', 'down' + \`\`\` + * 'by': Unit to move. Default is computed based on 'to' value. + \`\`\` + 'line', 'wrappedLine', 'page', 'halfPage' + \`\`\` + * 'value': Number of units to move. Default is '1'. + * 'revealCursor': If 'true' reveals the cursor if it is outside view port. + `, + constraint: isEditorScrollArgs + } + ] + }; + + /** + * Directions in the view for editor scroll command. + */ + export const RawDirection = { + Up: 'up', + Down: 'down', + }; + + /** + * Units for editor scroll 'by' argument + */ + export const RawUnit = { + Line: 'line', + WrappedLine: 'wrappedLine', + Page: 'page', + HalfPage: 'halfPage' + }; + + /** + * Arguments for editor scroll command + */ + export interface RawArguments { + to: string; + by?: string; + value?: number; + revealCursor?: boolean; + select?: boolean; + }; + + export function parse(args: RawArguments): ParsedArguments { + let direction: Direction; + switch (args.to) { + case RawDirection.Up: + direction = Direction.Up; + break; + case RawDirection.Down: + direction = Direction.Down; + break; + default: + // Illegal arguments + return null; + } + + let unit: Unit; + switch (args.by) { + case RawUnit.Line: + unit = Unit.Line; + break; + case RawUnit.WrappedLine: + unit = Unit.WrappedLine; + break; + case RawUnit.Page: + unit = Unit.Page; + break; + case RawUnit.HalfPage: + unit = Unit.HalfPage; + break; + default: + unit = Unit.WrappedLine; + } + + const value = Math.floor(args.value || 1); + const revealCursor = !!args.revealCursor; + + return { + direction: direction, + unit: unit, + value: value, + revealCursor: revealCursor, + select: (!!args.select) + }; + } + + export interface ParsedArguments { + direction: Direction; + unit: Unit; + value: number; + revealCursor: boolean; + select: boolean; + } + + export const enum Direction { + Up = 1, + Down = 2 + } + + export const enum Unit { + Line = 1, + WrappedLine = 2, + Page = 3, + HalfPage = 4 + } +} + +export namespace RevealLine_ { + + const isRevealLineArgs = function (arg: any): boolean { + if (!types.isObject(arg)) { + return false; + } + + let reveaLineArg: RawArguments = arg; + + if (!types.isNumber(reveaLineArg.lineNumber)) { + return false; + } + + if (!types.isUndefined(reveaLineArg.at) && !types.isString(reveaLineArg.at)) { + return false; + } + + return true; + }; + + export const description = { + description: 'Reveal the given line at the given logical position', + args: [ + { + name: 'Reveal line argument object', + description: `Property-value pairs that can be passed through this argument: + * 'lineNumber': A mandatory line number value. + * 'at': Logical position at which line has to be revealed . + \`\`\` + 'top', 'center', 'bottom' + \`\`\` + `, + constraint: isRevealLineArgs + } + ] + }; + + /** + * Arguments for reveal line command + */ + export interface RawArguments { + lineNumber?: number; + at?: string; + }; + + /** + * Values for reveal line 'at' argument + */ + export const RawAtArgument = { + Top: 'top', + Center: 'center', + Bottom: 'bottom' + }; +} + +export namespace CoreNavigationCommands { + + class BaseMoveToCommand extends CoreEditorCommand { + + private readonly _inSelectionMode: boolean; + + constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + [ + CursorMoveCommands.moveTo(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position, args.viewPosition) + ] + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + } + + export const MoveTo: CoreEditorCommand = registerEditorCommand(new BaseMoveToCommand({ + id: '_moveTo', + inSelectionMode: false, + precondition: null + })); + + export const MoveToSelect: CoreEditorCommand = registerEditorCommand(new BaseMoveToCommand({ + id: '_moveToSelect', + inSelectionMode: true, + precondition: null + })); + + abstract class ColumnSelectCommand extends CoreEditorCommand { + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + const result = this._getColumnSelectResult(cursors.context, cursors.getPrimaryCursor(), cursors.getColumnSelectData(), args); + cursors.setStates(args.source, CursorChangeReason.Explicit, result.viewStates.map((viewState) => CursorState.fromViewState(viewState))); + cursors.setColumnSelectData({ + toViewLineNumber: result.toLineNumber, + toViewVisualColumn: result.toVisualColumn + }); + cursors.reveal(true, (result.reversed ? RevealTarget.TopMost : RevealTarget.BottomMost), editorCommon.ScrollType.Smooth); + } + + protected abstract _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult; + + } + + export const ColumnSelect: CoreEditorCommand = registerEditorCommand(new class extends ColumnSelectCommand { + constructor() { + super({ + id: 'columnSelect', + precondition: null + }); + } + + protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { + + // validate `args` + const validatedPosition = context.model.validatePosition(args.position); + + let validatedViewPosition: Position; + if (args.viewPosition) { + validatedViewPosition = context.validateViewPosition(new Position(args.viewPosition.lineNumber, args.viewPosition.column), validatedPosition); + } else { + validatedViewPosition = context.convertModelPositionToViewPosition(validatedPosition); + } + + return ColumnSelection.columnSelect(context.config, context.viewModel, primary.viewState.selection, validatedViewPosition.lineNumber, args.mouseColumn - 1); + } + }); + + export const CursorColumnSelectLeft: CoreEditorCommand = registerEditorCommand(new class extends ColumnSelectCommand { + constructor() { + super({ + id: 'cursorColumnSelectLeft', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.LeftArrow, + linux: { primary: 0 } + } + }); + } + + protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { + return ColumnSelection.columnSelectLeft(context.config, context.viewModel, primary.viewState, prevColumnSelectData.toViewLineNumber, prevColumnSelectData.toViewVisualColumn); + } + }); + + export const CursorColumnSelectRight: CoreEditorCommand = registerEditorCommand(new class extends ColumnSelectCommand { + constructor() { + super({ + id: 'cursorColumnSelectRight', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.RightArrow, + linux: { primary: 0 } + } + }); + } + + protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { + return ColumnSelection.columnSelectRight(context.config, context.viewModel, primary.viewState, prevColumnSelectData.toViewLineNumber, prevColumnSelectData.toViewVisualColumn); + } + }); + + class ColumnSelectUpCommand extends ColumnSelectCommand { + + private readonly _isPaged: boolean; + + constructor(opts: ICommandOptions & { isPaged: boolean; }) { + super(opts); + this._isPaged = opts.isPaged; + } + + protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { + return ColumnSelection.columnSelectUp(context.config, context.viewModel, primary.viewState, this._isPaged, prevColumnSelectData.toViewLineNumber, prevColumnSelectData.toViewVisualColumn); + } + } + + export const CursorColumnSelectUp: CoreEditorCommand = registerEditorCommand(new ColumnSelectUpCommand({ + isPaged: false, + id: 'cursorColumnSelectUp', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.UpArrow, + linux: { primary: 0 } + } + })); + + export const CursorColumnSelectPageUp: CoreEditorCommand = registerEditorCommand(new ColumnSelectUpCommand({ + isPaged: true, + id: 'cursorColumnSelectPageUp', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.PageUp, + linux: { primary: 0 } + } + })); + + class ColumnSelectDownCommand extends ColumnSelectCommand { + + private readonly _isPaged: boolean; + + constructor(opts: ICommandOptions & { isPaged: boolean; }) { + super(opts); + this._isPaged = opts.isPaged; + } + + protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { + return ColumnSelection.columnSelectDown(context.config, context.viewModel, primary.viewState, this._isPaged, prevColumnSelectData.toViewLineNumber, prevColumnSelectData.toViewVisualColumn); + } + } + + export const CursorColumnSelectDown: CoreEditorCommand = registerEditorCommand(new ColumnSelectDownCommand({ + isPaged: false, + id: 'cursorColumnSelectDown', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.DownArrow, + linux: { primary: 0 } + } + })); + + export const CursorColumnSelectPageDown: CoreEditorCommand = registerEditorCommand(new ColumnSelectDownCommand({ + isPaged: true, + id: 'cursorColumnSelectPageDown', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.PageDown, + linux: { primary: 0 } + } + })); + + export class CursorMoveImpl extends CoreEditorCommand { + constructor() { + super({ + id: 'cursorMove', + precondition: null, + description: CursorMove_.description + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + const parsed = CursorMove_.parse(args); + if (!parsed) { + // illegal arguments + return; + } + this._runCursorMove(cursors, args.source, parsed); + } + + _runCursorMove(cursors: ICursors, source: string, args: CursorMove_.ParsedArguments): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + source, + CursorChangeReason.Explicit, + CursorState.ensureInEditableRange( + cursors.context, + CursorMoveCommands.move(cursors.context, cursors.getAll(), args) + ) + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + } + + export const CursorMove: CursorMoveImpl = registerEditorCommand(new CursorMoveImpl()); + + const enum Constants { + PAGE_SIZE_MARKER = -1 + } + + class CursorMoveBasedCommand extends CoreEditorCommand { + + private readonly _staticArgs: CursorMove_.ParsedArguments; + + constructor(opts: ICommandOptions & { args: CursorMove_.ParsedArguments }) { + super(opts); + this._staticArgs = opts.args; + } + + public runCoreEditorCommand(cursors: ICursors, dynamicArgs: any): void { + let args = this._staticArgs; + if (this._staticArgs.value === Constants.PAGE_SIZE_MARKER) { + // -1 is a marker for page size + args = { + direction: this._staticArgs.direction, + unit: this._staticArgs.unit, + select: this._staticArgs.select, + value: cursors.context.config.pageSize + }; + } + CursorMove._runCursorMove(cursors, dynamicArgs.source, args); + } + } + + export const CursorLeft: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Left, + unit: CursorMove_.Unit.None, + select: false, + value: 1 + }, + id: 'cursorLeft', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.LeftArrow, + mac: { primary: KeyCode.LeftArrow, secondary: [KeyMod.WinCtrl | KeyCode.KEY_B] } + } + })); + + export const CursorLeftSelect: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Left, + unit: CursorMove_.Unit.None, + select: true, + value: 1 + }, + id: 'cursorLeftSelect', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.LeftArrow + } + })); + + export const CursorRight: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Right, + unit: CursorMove_.Unit.None, + select: false, + value: 1 + }, + id: 'cursorRight', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.RightArrow, + mac: { primary: KeyCode.RightArrow, secondary: [KeyMod.WinCtrl | KeyCode.KEY_F] } + } + })); + + export const CursorRightSelect: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Right, + unit: CursorMove_.Unit.None, + select: true, + value: 1 + }, + id: 'cursorRightSelect', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.RightArrow + } + })); + + export const CursorUp: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Up, + unit: CursorMove_.Unit.WrappedLine, + select: false, + value: 1 + }, + id: 'cursorUp', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.UpArrow, + mac: { primary: KeyCode.UpArrow, secondary: [KeyMod.WinCtrl | KeyCode.KEY_P] } + } + })); + + export const CursorUpSelect: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Up, + unit: CursorMove_.Unit.WrappedLine, + select: true, + value: 1 + }, + id: 'cursorUpSelect', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.UpArrow, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow], + mac: { primary: KeyMod.Shift | KeyCode.UpArrow }, + linux: { primary: KeyMod.Shift | KeyCode.UpArrow } + } + })); + + export const CursorPageUp: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Up, + unit: CursorMove_.Unit.WrappedLine, + select: false, + value: Constants.PAGE_SIZE_MARKER + }, + id: 'cursorPageUp', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.PageUp + } + })); + + export const CursorPageUpSelect: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Up, + unit: CursorMove_.Unit.WrappedLine, + select: true, + value: Constants.PAGE_SIZE_MARKER + }, + id: 'cursorPageUpSelect', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.PageUp + } + })); + + export const CursorDown: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Down, + unit: CursorMove_.Unit.WrappedLine, + select: false, + value: 1 + }, + id: 'cursorDown', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.DownArrow, + mac: { primary: KeyCode.DownArrow, secondary: [KeyMod.WinCtrl | KeyCode.KEY_N] } + } + })); + + export const CursorDownSelect: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Down, + unit: CursorMove_.Unit.WrappedLine, + select: true, + value: 1 + }, + id: 'cursorDownSelect', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.DownArrow, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow], + mac: { primary: KeyMod.Shift | KeyCode.DownArrow }, + linux: { primary: KeyMod.Shift | KeyCode.DownArrow } + } + })); + + export const CursorPageDown: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Down, + unit: CursorMove_.Unit.WrappedLine, + select: false, + value: Constants.PAGE_SIZE_MARKER + }, + id: 'cursorPageDown', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.PageDown + } + })); + + export const CursorPageDownSelect: CoreEditorCommand = registerEditorCommand(new CursorMoveBasedCommand({ + args: { + direction: CursorMove_.Direction.Down, + unit: CursorMove_.Unit.WrappedLine, + select: true, + value: Constants.PAGE_SIZE_MARKER + }, + id: 'cursorPageDownSelect', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.PageDown + } + })); + + export const CreateCursor: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'createCursor', + precondition: null + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + const context = cursors.context; + + if (context.config.readOnly || context.model.hasEditableRange()) { + return; + } + + let newState: CursorState; + if (args.wholeLine) { + newState = CursorMoveCommands.line(context, cursors.getPrimaryCursor(), false, args.position, args.viewPosition); + } else { + newState = CursorMoveCommands.moveTo(context, cursors.getPrimaryCursor(), false, args.position, args.viewPosition); + } + + const states = cursors.getAll(); + + // Check if we should remove a cursor (sort of like a toggle) + if (states.length > 1) { + const newModelPosition = (newState.modelState ? newState.modelState.position : null); + const newViewPosition = (newState.viewState ? newState.viewState.position : null); + + for (let i = 0, len = states.length; i < len; i++) { + const state = states[i]; + + if (newModelPosition && !state.modelState.selection.containsPosition(newModelPosition)) { + continue; + } + + if (newViewPosition && !state.viewState.selection.containsPosition(newViewPosition)) { + continue; + } + + // => Remove the cursor + states.splice(i, 1); + + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + states + ); + return; + } + } + + // => Add the new cursor + states.push(newState); + + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + states + ); + } + }); + + export const LastCursorMoveToSelect: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: '_lastCursorMoveToSelect', + precondition: null + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + const context = cursors.context; + + if (context.config.readOnly || context.model.hasEditableRange()) { + return; + } + + const lastAddedCursorIndex = cursors.getLastAddedCursorIndex(); + + let newStates = cursors.getAll().slice(0); + newStates[lastAddedCursorIndex] = CursorMoveCommands.moveTo(context, newStates[lastAddedCursorIndex], true, args.position, args.viewPosition); + + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + newStates + ); + } + }); + + class HomeCommand extends CoreEditorCommand { + + private readonly _inSelectionMode: boolean; + + constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + CursorState.ensureInEditableRange( + cursors.context, + CursorMoveCommands.moveToBeginningOfLine(cursors.context, cursors.getAll(), this._inSelectionMode) + ) + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + } + + export const CursorHome: CoreEditorCommand = registerEditorCommand(new HomeCommand({ + inSelectionMode: false, + id: 'cursorHome', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Home, + mac: { primary: KeyCode.Home, secondary: [KeyMod.CtrlCmd | KeyCode.LeftArrow] } + } + })); + + export const CursorHomeSelect: CoreEditorCommand = registerEditorCommand(new HomeCommand({ + inSelectionMode: true, + id: 'cursorHomeSelect', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.Home, + mac: { primary: KeyMod.Shift | KeyCode.Home, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow] } + } + })); + + export const CursorLineStart: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'cursorLineStart', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_A } + } + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + CursorState.ensureInEditableRange( + cursors.context, + this._exec(cursors.context, cursors.getAll()) + ) + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + + private _exec(context: CursorContext, cursors: CursorState[]): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + const lineNumber = cursor.modelState.position.lineNumber; + result[i] = CursorState.fromModelState(cursor.modelState.move(false, lineNumber, 1, 0)); + } + return result; + } + }); + + class EndCommand extends CoreEditorCommand { + + private readonly _inSelectionMode: boolean; + + constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + CursorState.ensureInEditableRange( + cursors.context, + CursorMoveCommands.moveToEndOfLine(cursors.context, cursors.getAll(), this._inSelectionMode) + ) + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + } + + export const CursorEnd: CoreEditorCommand = registerEditorCommand(new EndCommand({ + inSelectionMode: false, + id: 'cursorEnd', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.End, + mac: { primary: KeyCode.End, secondary: [KeyMod.CtrlCmd | KeyCode.RightArrow] } + } + })); + + export const CursorEndSelect: CoreEditorCommand = registerEditorCommand(new EndCommand({ + inSelectionMode: true, + id: 'cursorEndSelect', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.End, + mac: { primary: KeyMod.Shift | KeyCode.End, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow] } + } + })); + + export const CursorLineEnd: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'cursorLineEnd', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_E } + } + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + CursorState.ensureInEditableRange( + cursors.context, + this._exec(cursors.context, cursors.getAll()) + ) + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + + private _exec(context: CursorContext, cursors: CursorState[]): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + const lineNumber = cursor.modelState.position.lineNumber; + const maxColumn = context.model.getLineMaxColumn(lineNumber); + result[i] = CursorState.fromModelState(cursor.modelState.move(false, lineNumber, maxColumn, 0)); + } + return result; + } + }); + + class TopCommand extends CoreEditorCommand { + + private readonly _inSelectionMode: boolean; + + constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + CursorState.ensureInEditableRange( + cursors.context, + CursorMoveCommands.moveToBeginningOfBuffer(cursors.context, cursors.getAll(), this._inSelectionMode) + ) + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + } + + export const CursorTop: CoreEditorCommand = registerEditorCommand(new TopCommand({ + inSelectionMode: false, + id: 'cursorTop', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.Home, + mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow } + } + })); + + export const CursorTopSelect: CoreEditorCommand = registerEditorCommand(new TopCommand({ + inSelectionMode: true, + id: 'cursorTopSelect', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Home, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow } + } + })); + + class BottomCommand extends CoreEditorCommand { + + private readonly _inSelectionMode: boolean; + + constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + CursorState.ensureInEditableRange( + cursors.context, + CursorMoveCommands.moveToEndOfBuffer(cursors.context, cursors.getAll(), this._inSelectionMode) + ) + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + } + + export const CursorBottom: CoreEditorCommand = registerEditorCommand(new BottomCommand({ + inSelectionMode: false, + id: 'cursorBottom', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.End, + mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow } + } + })); + + export const CursorBottomSelect: CoreEditorCommand = registerEditorCommand(new BottomCommand({ + inSelectionMode: true, + id: 'cursorBottomSelect', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.End, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } + } + })); + + export class EditorScrollImpl extends CoreEditorCommand { + constructor() { + super({ + id: 'editorScroll', + precondition: null, + description: EditorScroll_.description + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + const parsed = EditorScroll_.parse(args); + if (!parsed) { + // illegal arguments + return; + } + this._runEditorScroll(cursors, args.source, parsed); + } + + _runEditorScroll(cursors: ICursors, source: string, args: EditorScroll_.ParsedArguments): void { + + const desiredScrollTop = this._computeDesiredScrollTop(cursors.context, args); + + if (args.revealCursor) { + // must ensure cursor is in new visible range + const desiredVisibleViewRange = cursors.context.getCompletelyVisibleViewRangeAtScrollTop(desiredScrollTop); + cursors.setStates( + source, + CursorChangeReason.Explicit, + [ + CursorMoveCommands.findPositionInViewportIfOutside(cursors.context, cursors.getPrimaryCursor(), desiredVisibleViewRange, args.select) + ] + ); + } + + cursors.scrollTo(desiredScrollTop); + } + + private _computeDesiredScrollTop(context: CursorContext, args: EditorScroll_.ParsedArguments): number { + + if (args.unit === EditorScroll_.Unit.Line) { + // scrolling by model lines + const visibleModelRange = context.getCompletelyVisibleModelRange(); + + let desiredTopModelLineNumber: number; + if (args.direction === EditorScroll_.Direction.Up) { + // must go x model lines up + desiredTopModelLineNumber = Math.max(1, visibleModelRange.startLineNumber - args.value); + } else { + // must go x model lines down + desiredTopModelLineNumber = Math.min(context.model.getLineCount(), visibleModelRange.startLineNumber + args.value); + } + + const desiredTopViewPosition = context.convertModelPositionToViewPosition(new Position(desiredTopModelLineNumber, 1)); + return context.getVerticalOffsetForViewLine(desiredTopViewPosition.lineNumber); + } + + let noOfLines: number; + if (args.unit === EditorScroll_.Unit.Page) { + noOfLines = context.config.pageSize * args.value; + } else if (args.unit === EditorScroll_.Unit.HalfPage) { + noOfLines = Math.round(context.config.pageSize / 2) * args.value; + } else { + noOfLines = args.value; + } + const deltaLines = (args.direction === EditorScroll_.Direction.Up ? -1 : 1) * noOfLines; + return context.getCurrentScrollTop() + deltaLines * context.config.lineHeight; + } + } + + export const EditorScroll: EditorScrollImpl = registerEditorCommand(new EditorScrollImpl()); + + export const ScrollLineUp: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'scrollLineUp', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.UpArrow, + mac: { primary: KeyMod.WinCtrl | KeyCode.PageUp } + } + }); + } + + runCoreEditorCommand(cursors: ICursors, args: any): void { + EditorScroll._runEditorScroll(cursors, args.source, { + direction: EditorScroll_.Direction.Up, + unit: EditorScroll_.Unit.WrappedLine, + value: 1, + revealCursor: false, + select: false + }); + } + }); + + export const ScrollPageUp: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'scrollPageUp', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.PageUp, + win: { primary: KeyMod.Alt | KeyCode.PageUp }, + linux: { primary: KeyMod.Alt | KeyCode.PageUp } + } + }); + } + + runCoreEditorCommand(cursors: ICursors, args: any): void { + EditorScroll._runEditorScroll(cursors, args.source, { + direction: EditorScroll_.Direction.Up, + unit: EditorScroll_.Unit.Page, + value: 1, + revealCursor: false, + select: false + }); + } + }); + + export const ScrollLineDown: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'scrollLineDown', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.DownArrow, + mac: { primary: KeyMod.WinCtrl | KeyCode.PageDown } + } + }); + } + + runCoreEditorCommand(cursors: ICursors, args: any): void { + EditorScroll._runEditorScroll(cursors, args.source, { + direction: EditorScroll_.Direction.Down, + unit: EditorScroll_.Unit.WrappedLine, + value: 1, + revealCursor: false, + select: false + }); + } + }); + + export const ScrollPageDown: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'scrollPageDown', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.PageDown, + win: { primary: KeyMod.Alt | KeyCode.PageDown }, + linux: { primary: KeyMod.Alt | KeyCode.PageDown } + } + }); + } + + runCoreEditorCommand(cursors: ICursors, args: any): void { + EditorScroll._runEditorScroll(cursors, args.source, { + direction: EditorScroll_.Direction.Down, + unit: EditorScroll_.Unit.Page, + value: 1, + revealCursor: false, + select: false + }); + } + }); + + class WordCommand extends CoreEditorCommand { + + private readonly _inSelectionMode: boolean; + + constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + [ + CursorMoveCommands.word(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position) + ] + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + } + + export const WordSelect: CoreEditorCommand = registerEditorCommand(new WordCommand({ + inSelectionMode: false, + id: '_wordSelect', + precondition: null + })); + + export const WordSelectDrag: CoreEditorCommand = registerEditorCommand(new WordCommand({ + inSelectionMode: true, + id: '_wordSelectDrag', + precondition: null + })); + + export const LastCursorWordSelect: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'lastCursorWordSelect', + precondition: null + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + const context = cursors.context; + if (context.config.readOnly || context.model.hasEditableRange()) { + return; + } + + const lastAddedCursorIndex = cursors.getLastAddedCursorIndex(); + + let newStates = cursors.getAll().slice(0); + newStates[lastAddedCursorIndex] = CursorMoveCommands.word(context, newStates[lastAddedCursorIndex], true, args.position); + + context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + newStates + ); + } + }); + + class LineCommand extends CoreEditorCommand { + private readonly _inSelectionMode: boolean; + + constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + [ + CursorMoveCommands.line(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position, args.viewPosition) + ] + ); + cursors.reveal(false, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + } + + export const LineSelect: CoreEditorCommand = registerEditorCommand(new LineCommand({ + inSelectionMode: false, + id: '_lineSelect', + precondition: null + })); + + export const LineSelectDrag: CoreEditorCommand = registerEditorCommand(new LineCommand({ + inSelectionMode: true, + id: '_lineSelectDrag', + precondition: null + })); + + class LastCursorLineCommand extends CoreEditorCommand { + private readonly _inSelectionMode: boolean; + + constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + const context = cursors.context; + + if (context.config.readOnly || context.model.hasEditableRange()) { + return; + } + + const lastAddedCursorIndex = cursors.getLastAddedCursorIndex(); + + let newStates = cursors.getAll().slice(0); + newStates[lastAddedCursorIndex] = CursorMoveCommands.line(cursors.context, newStates[lastAddedCursorIndex], this._inSelectionMode, args.position, args.viewPosition); + + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + newStates + ); + } + } + + export const LastCursorLineSelect: CoreEditorCommand = registerEditorCommand(new LastCursorLineCommand({ + inSelectionMode: false, + id: 'lastCursorLineSelect', + precondition: null + })); + + export const LastCursorLineSelectDrag: CoreEditorCommand = registerEditorCommand(new LastCursorLineCommand({ + inSelectionMode: true, + id: 'lastCursorLineSelectDrag', + precondition: null + })); + + export const ExpandLineSelection: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'expandLineSelection', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.KEY_I + } + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + CursorState.ensureInEditableRange( + cursors.context, + CursorMoveCommands.expandLineSelection(cursors.context, cursors.getAll()) + ) + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + + }); + + export const CancelSelection: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'cancelSelection', + precondition: EditorContextKeys.hasNonEmptySelection, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + [ + CursorMoveCommands.cancelSelection(cursors.context, cursors.getPrimaryCursor()) + ] + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + }); + + export const RemoveSecondaryCursors: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'removeSecondaryCursors', + precondition: EditorContextKeys.hasMultipleSelections, + kbOpts: { + weight: CORE_WEIGHT + 1, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + [ + cursors.getPrimaryCursor() + ] + ); + cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + } + }); + + export const RevealLine: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'revealLine', + precondition: null, + description: RevealLine_.description + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + const revealLineArg = args; + let lineNumber = revealLineArg.lineNumber + 1; + if (lineNumber < 1) { + lineNumber = 1; + } + const lineCount = cursors.context.model.getLineCount(); + if (lineNumber > lineCount) { + lineNumber = lineCount; + } + + const range = new Range( + lineNumber, 1, + lineNumber, cursors.context.model.getLineMaxColumn(lineNumber) + ); + + let revealAt = VerticalRevealType.Simple; + if (revealLineArg.at) { + switch (revealLineArg.at) { + case RevealLine_.RawAtArgument.Top: + revealAt = VerticalRevealType.Top; + break; + case RevealLine_.RawAtArgument.Center: + revealAt = VerticalRevealType.Center; + break; + case RevealLine_.RawAtArgument.Bottom: + revealAt = VerticalRevealType.Bottom; + break; + default: + break; + } + } + + const viewRange = cursors.context.convertModelRangeToViewRange(range); + + cursors.revealRange(false, viewRange, revealAt, editorCommon.ScrollType.Smooth); + } + }); + + export const SelectAll: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'selectAll', + precondition: null + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + [ + CursorMoveCommands.selectAll(cursors.context, cursors.getPrimaryCursor()) + ] + ); + } + }); + + export const SetSelection: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'setSelection', + precondition: null + }); + } + + public runCoreEditorCommand(cursors: ICursors, args: any): void { + cursors.context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + [ + CursorState.fromModelSelection(args.selection) + ] + ); + } + }); +} + +export namespace CoreEditingCommands { + + export const LineBreakInsert: EditorCommand = registerEditorCommand(new class extends EditorCommand { + constructor() { + super({ + id: 'lineBreakInsert', + precondition: EditorContextKeys.writable, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: null, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_O } + } + }); + } + + public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void { + editor.pushUndoStop(); + editor.executeCommands(this.id, TypeOperations.lineBreakInsert(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections())); + } + }); + + export const Outdent: EditorCommand = registerEditorCommand(new class extends EditorCommand { + constructor() { + super({ + id: 'outdent', + precondition: EditorContextKeys.writable, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: ContextKeyExpr.and( + EditorContextKeys.textFocus, + EditorContextKeys.tabDoesNotMoveFocus + ), + primary: KeyMod.Shift | KeyCode.Tab + } + }); + } + + public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void { + editor.pushUndoStop(); + editor.executeCommands(this.id, TypeOperations.outdent(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections())); + editor.pushUndoStop(); + } + }); + + export const Tab: EditorCommand = registerEditorCommand(new class extends EditorCommand { + constructor() { + super({ + id: 'tab', + precondition: EditorContextKeys.writable, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: ContextKeyExpr.and( + EditorContextKeys.textFocus, + EditorContextKeys.tabDoesNotMoveFocus + ), + primary: KeyCode.Tab + } + }); + } + + public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void { + editor.pushUndoStop(); + editor.executeCommands(this.id, TypeOperations.tab(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections())); + editor.pushUndoStop(); + } + }); + + export const DeleteLeft: EditorCommand = registerEditorCommand(new class extends EditorCommand { + constructor() { + super({ + id: 'deleteLeft', + precondition: EditorContextKeys.writable, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Backspace, + secondary: [KeyMod.Shift | KeyCode.Backspace], + mac: { primary: KeyCode.Backspace, secondary: [KeyMod.Shift | KeyCode.Backspace, KeyMod.WinCtrl | KeyCode.KEY_H, KeyMod.WinCtrl | KeyCode.Backspace] } + } + }); + } + + public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void { + const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections()); + if (shouldPushStackElementBefore) { + editor.pushUndoStop(); + } + editor.executeCommands(this.id, commands); + } + }); + + export const DeleteRight: EditorCommand = registerEditorCommand(new class extends EditorCommand { + constructor() { + super({ + id: 'deleteRight', + precondition: EditorContextKeys.writable, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Delete, + mac: { primary: KeyCode.Delete, secondary: [KeyMod.WinCtrl | KeyCode.KEY_D, KeyMod.WinCtrl | KeyCode.Delete] } + } + }); + } + + public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void { + const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteRight(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections()); + if (shouldPushStackElementBefore) { + editor.pushUndoStop(); + } + editor.executeCommands(this.id, commands); + } + }); + +} + +namespace Config { + + function findFocusedEditor(accessor: ServicesAccessor): editorCommon.ICommonCodeEditor { + return accessor.get(ICodeEditorService).getFocusedCodeEditor(); + } + + function getWorkbenchActiveEditor(accessor: ServicesAccessor): editorCommon.ICommonCodeEditor { + const editorService = accessor.get(IEditorService); + let activeEditor = (editorService).getActiveEditor && (editorService).getActiveEditor(); + return getCodeEditor(activeEditor); + } + + function registerCommand(command: Command) { + KeybindingsRegistry.registerCommandAndKeybindingRule(command.toCommandAndKeybindingRule(CORE_WEIGHT)); + } + + /** + * A command that will: + * 1. invoke a command on the focused editor. + * 2. otherwise, invoke a browser built-in command on the `activeElement`. + * 3. otherwise, invoke a command on the workbench active editor. + */ + class EditorOrNativeTextInputCommand extends Command { + + private readonly _editorHandler: string | EditorCommand; + private readonly _inputHandler: string; + + constructor(opts: ICommandOptions & { editorHandler: string | EditorCommand; inputHandler: string; }) { + super(opts); + this._editorHandler = opts.editorHandler; + this._inputHandler = opts.inputHandler; + } + + public runCommand(accessor: ServicesAccessor, args: any): void { + + let focusedEditor = findFocusedEditor(accessor); + // Only if editor text focus (i.e. not if editor has widget focus). + if (focusedEditor && focusedEditor.isFocused()) { + return this._runEditorHandler(focusedEditor, args); + } + + // Ignore this action when user is focused on an element that allows for entering text + let activeElement = document.activeElement; + if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) { + document.execCommand(this._inputHandler); + return; + } + + // Redirecting to last active editor + let activeEditor = getWorkbenchActiveEditor(accessor); + if (activeEditor) { + activeEditor.focus(); + return this._runEditorHandler(activeEditor, args); + } + } + + private _runEditorHandler(editor: editorCommon.ICommonCodeEditor, args: any): void { + let HANDLER = this._editorHandler; + if (typeof HANDLER === 'string') { + editor.trigger('keyboard', HANDLER, args); + } else { + args = args || {}; + args.source = 'keyboard'; + HANDLER.runEditorCommand(null, editor, args); + } + } + } + + registerCommand(new EditorOrNativeTextInputCommand({ + editorHandler: CoreNavigationCommands.SelectAll, + inputHandler: 'selectAll', + id: 'editor.action.selectAll', + precondition: null, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: null, + primary: KeyMod.CtrlCmd | KeyCode.KEY_A + } + })); + + registerCommand(new EditorOrNativeTextInputCommand({ + editorHandler: H.Undo, + inputHandler: 'undo', + id: H.Undo, + precondition: EditorContextKeys.writable, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.KEY_Z + } + })); + + registerCommand(new EditorOrNativeTextInputCommand({ + editorHandler: H.Redo, + inputHandler: 'redo', + id: H.Redo, + precondition: EditorContextKeys.writable, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z } + } + })); + + /** + * A command that will invoke a command on the focused editor. + */ + class EditorHandlerCommand extends Command { + + private readonly _handlerId: string; + + constructor(id: string, handlerId: string) { + super({ + id: id, + precondition: null + }); + this._handlerId = handlerId; + } + + public runCommand(accessor: ServicesAccessor, args: any): void { + const editor = findFocusedEditor(accessor); + if (!editor) { + return; + } + + editor.trigger('keyboard', this._handlerId, args); + } + } + + function registerOverwritableCommand(handlerId: string): void { + registerCommand(new EditorHandlerCommand('default:' + handlerId, handlerId)); + registerCommand(new EditorHandlerCommand(handlerId, handlerId)); + } + + registerOverwritableCommand(H.Type); + registerOverwritableCommand(H.ReplacePreviousChar); + registerOverwritableCommand(H.CompositionStart); + registerOverwritableCommand(H.CompositionEnd); + registerOverwritableCommand(H.Paste); + registerOverwritableCommand(H.Cut); + +} diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts new file mode 100644 index 0000000000..988d9163be --- /dev/null +++ b/src/vs/editor/common/controller/cursor.ts @@ -0,0 +1,861 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import * as strings from 'vs/base/common/strings'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { CursorCollection } from 'vs/editor/common/controller/cursorCollection'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection, SelectionDirection, ISelection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { CursorColumns, CursorConfiguration, EditOperationResult, CursorContext, CursorState, RevealTarget, IColumnSelectData, ICursors } from 'vs/editor/common/controller/cursorCommon'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; +import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; +import { TextModelEventType, ModelRawContentChangedEvent, RawContentChangedType } from 'vs/editor/common/model/textModelEvents'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import Event, { Emitter } from 'vs/base/common/event'; +// import { ScreenReaderMessageGenerator } from "vs/editor/common/controller/accGenerator"; + +function containsLineMappingChanged(events: viewEvents.ViewEvent[]): boolean { + for (let i = 0, len = events.length; i < len; i++) { + if (events[i].type === viewEvents.ViewEventType.ViewLineMappingChanged) { + return true; + } + } + return false; +} + +export class CursorStateChangedEvent { + /** + * The new selections. + * The primary selection is always at index 0. + */ + readonly selections: Selection[]; + /** + * Source of the call that caused the event. + */ + readonly source: string; + /** + * Reason. + */ + readonly reason: CursorChangeReason; + + constructor(selections: Selection[], source: string, reason: CursorChangeReason) { + this.selections = selections; + this.source = source; + this.reason = reason; + } +} + +/** + * A snapshot of the cursor and the model state + */ +export class CursorModelState { + + public readonly modelVersionId: number; + public readonly cursorState: CursorState[]; + + constructor(model: editorCommon.IModel, cursor: Cursor) { + this.modelVersionId = model.getVersionId(); + this.cursorState = cursor.getAll(); + } + + public equals(other: CursorModelState): boolean { + if (!other) { + return false; + } + if (this.modelVersionId !== other.modelVersionId) { + return false; + } + if (this.cursorState.length !== other.cursorState.length) { + return false; + } + for (let i = 0, len = this.cursorState.length; i < len; i++) { + if (!this.cursorState[i].equals(other.cursorState[i])) { + return false; + } + } + return true; + } +} + +export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { + + private readonly _onDidChange: Emitter = this._register(new Emitter()); + public readonly onDidChange: Event = this._onDidChange.event; + + private readonly _configuration: editorCommon.IConfiguration; + private readonly _model: editorCommon.IModel; + private readonly _viewModel: IViewModel; + public context: CursorContext; + private _cursors: CursorCollection; + + private _isHandling: boolean; + private _isDoingComposition: boolean; + private _columnSelectData: IColumnSelectData; + + constructor(configuration: editorCommon.IConfiguration, model: editorCommon.IModel, viewModel: IViewModel) { + super(); + this._configuration = configuration; + this._model = model; + this._viewModel = viewModel; + this.context = new CursorContext(this._configuration, this._model, this._viewModel); + this._cursors = new CursorCollection(this.context); + + this._isHandling = false; + this._isDoingComposition = false; + this._columnSelectData = null; + + this._register(this._model.addBulkListener((events) => { + if (this._isHandling) { + return; + } + + let hadContentChange = false; + let hadFlushEvent = false; + for (let i = 0, len = events.length; i < len; i++) { + const event = events[i]; + const eventType = event.type; + + if (eventType === TextModelEventType.ModelRawContentChanged2) { + hadContentChange = true; + const rawChangeEvent = event.data; + hadFlushEvent = hadFlushEvent || rawChangeEvent.containsEvent(RawContentChangedType.Flush); + } + } + + if (!hadContentChange) { + return; + } + + this._onModelContentChanged(hadFlushEvent); + })); + + this._register(viewModel.addEventListener((events: viewEvents.ViewEvent[]) => { + if (!containsLineMappingChanged(events)) { + return; + } + + // Ensure valid state + this.setStates('viewModel', CursorChangeReason.NotSet, this.getAll()); + })); + + const updateCursorContext = () => { + this.context = new CursorContext(this._configuration, this._model, this._viewModel); + this._cursors.updateContext(this.context); + }; + this._register(this._model.onDidChangeLanguage((e) => { + updateCursorContext(); + })); + this._register(LanguageConfigurationRegistry.onDidChange(() => { + // TODO@Alex: react only if certain supports changed? (and if my model's mode changed) + updateCursorContext(); + })); + this._register(model.onDidChangeOptions(() => { + updateCursorContext(); + })); + this._register(this._configuration.onDidChange((e) => { + if (CursorConfiguration.shouldRecreate(e)) { + updateCursorContext(); + } + })); + } + + public dispose(): void { + this._cursors.dispose(); + super.dispose(); + } + + // ------ some getters/setters + + public getPrimaryCursor(): CursorState { + return this._cursors.getPrimaryCursor(); + } + + public getLastAddedCursorIndex(): number { + return this._cursors.getLastAddedCursorIndex(); + } + + public getAll(): CursorState[] { + return this._cursors.getAll(); + } + + public setStates(source: string, reason: CursorChangeReason, states: CursorState[]): void { + const oldState = new CursorModelState(this._model, this); + + this._cursors.setStates(states); + this._cursors.normalize(); + this._columnSelectData = null; + + this._emitStateChangedIfNecessary(source, reason, oldState); + } + + public setColumnSelectData(columnSelectData: IColumnSelectData): void { + this._columnSelectData = columnSelectData; + } + + public reveal(horizontal: boolean, target: RevealTarget, scrollType: editorCommon.ScrollType): void { + this._revealRange(target, viewEvents.VerticalRevealType.Simple, horizontal, scrollType); + } + + public revealRange(revealHorizontal: boolean, viewRange: Range, verticalType: viewEvents.VerticalRevealType, scrollType: editorCommon.ScrollType) { + this.emitCursorRevealRange(viewRange, verticalType, revealHorizontal, scrollType); + } + + public scrollTo(desiredScrollTop: number): void { + this._viewModel.viewLayout.setScrollPositionSmooth({ + scrollTop: desiredScrollTop + }); + } + + public saveState(): editorCommon.ICursorState[] { + + let result: editorCommon.ICursorState[] = []; + + const selections = this._cursors.getSelections(); + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + + result.push({ + inSelectionMode: !selection.isEmpty(), + selectionStart: { + lineNumber: selection.selectionStartLineNumber, + column: selection.selectionStartColumn, + }, + position: { + lineNumber: selection.positionLineNumber, + column: selection.positionColumn, + } + }); + } + + return result; + } + + public restoreState(states: editorCommon.ICursorState[]): void { + + let desiredSelections: ISelection[] = []; + + for (let i = 0, len = states.length; i < len; i++) { + const state = states[i]; + + let positionLineNumber = 1; + let positionColumn = 1; + + // Avoid missing properties on the literal + if (state.position && state.position.lineNumber) { + positionLineNumber = state.position.lineNumber; + } + if (state.position && state.position.column) { + positionColumn = state.position.column; + } + + let selectionStartLineNumber = positionLineNumber; + let selectionStartColumn = positionColumn; + + // Avoid missing properties on the literal + if (state.selectionStart && state.selectionStart.lineNumber) { + selectionStartLineNumber = state.selectionStart.lineNumber; + } + if (state.selectionStart && state.selectionStart.column) { + selectionStartColumn = state.selectionStart.column; + } + + desiredSelections.push({ + selectionStartLineNumber: selectionStartLineNumber, + selectionStartColumn: selectionStartColumn, + positionLineNumber: positionLineNumber, + positionColumn: positionColumn + }); + } + + this.setStates('restoreState', CursorChangeReason.NotSet, CursorState.fromModelSelections(desiredSelections)); + this.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Immediate); + } + + private _onModelContentChanged(hadFlushEvent: boolean): void { + if (hadFlushEvent) { + // a model.setValue() was called + this._cursors.dispose(); + this._cursors = new CursorCollection(this.context); + + this._emitStateChangedIfNecessary('model', CursorChangeReason.ContentFlush, null); + } else { + const selectionsFromMarkers = this._cursors.readSelectionFromMarkers(); + this.setStates('modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers)); + } + } + + public getSelection(): Selection { + return this._cursors.getPrimaryCursor().modelState.selection; + } + + public getColumnSelectData(): IColumnSelectData { + if (this._columnSelectData) { + return this._columnSelectData; + } + const primaryCursor = this._cursors.getPrimaryCursor(); + const primaryPos = primaryCursor.viewState.position; + return { + toViewLineNumber: primaryPos.lineNumber, + toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, primaryPos) + }; + } + + public getSelections(): Selection[] { + return this._cursors.getSelections(); + } + + public getViewSelections(): Selection[] { + return this._cursors.getViewSelections(); + } + + public getPosition(): Position { + return this._cursors.getPrimaryCursor().modelState.position; + } + + public setSelections(source: string, selections: ISelection[]): void { + this.setStates(source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections)); + } + + // ------ auxiliary handling logic + + private _executeEditOperation(opResult: EditOperationResult): void { + + if (!opResult) { + // Nothing to execute + return; + } + + if (this._configuration.editor.readOnly) { + // Cannot execute when read only + return; + } + + if (opResult.shouldPushStackElementBefore) { + this._model.pushStackElement(); + } + + const result = CommandExecutor.executeCommands(this._model, this._cursors.getSelections(), opResult.commands); + if (result) { + // The commands were applied correctly + this._interpretCommandResult(result); + } + + if (opResult.shouldPushStackElementAfter) { + this._model.pushStackElement(); + } + } + + private _interpretCommandResult(cursorState: Selection[]): void { + if (!cursorState || cursorState.length === 0) { + return; + } + + this._columnSelectData = null; + this._cursors.setSelections(cursorState); + this._cursors.normalize(); + } + + // ----------------------------------------------------------------------------------------------------------- + // ----- emitting events + + private _emitStateChangedIfNecessary(source: string, reason: CursorChangeReason, oldState: CursorModelState): boolean { + const newState = new CursorModelState(this._model, this); + if (newState.equals(oldState)) { + return false; + } + + + let isInEditableRange: boolean = true; + if (this._model.hasEditableRange()) { + const editableRange = this._model.getEditableRange(); + if (!editableRange.containsPosition(newState.cursorState[0].modelState.position)) { + isInEditableRange = false; + } + } + + const selections = this._cursors.getSelections(); + const viewSelections = this._cursors.getViewSelections(); + + // Let the view get the event first. + this._emit([new viewEvents.ViewCursorStateChangedEvent(viewSelections, isInEditableRange)]); + + // Only after the view has been notified, let the rest of the world know... + if (!oldState + || oldState.cursorState.length !== newState.cursorState.length + || newState.cursorState.some((newCursorState, i) => !newCursorState.modelState.equals(oldState.cursorState[i].modelState)) + ) { + this._onDidChange.fire(new CursorStateChangedEvent(selections, source || 'keyboard', reason)); + } + + return true; + } + + private _revealRange(revealTarget: RevealTarget, verticalType: viewEvents.VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void { + const viewPositions = this._cursors.getViewPositions(); + + let viewPosition = viewPositions[0]; + + if (revealTarget === RevealTarget.TopMost) { + for (let i = 1; i < viewPositions.length; i++) { + if (viewPositions[i].isBefore(viewPosition)) { + viewPosition = viewPositions[i]; + } + } + } else if (revealTarget === RevealTarget.BottomMost) { + for (let i = 1; i < viewPositions.length; i++) { + if (viewPosition.isBeforeOrEqual(viewPositions[i])) { + viewPosition = viewPositions[i]; + } + } + } else { + if (viewPositions.length > 1) { + // no revealing! + return; + } + } + + const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column); + this.emitCursorRevealRange(viewRange, verticalType, revealHorizontal, scrollType); + } + + public emitCursorRevealRange(viewRange: Range, verticalType: viewEvents.VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType) { + this._emit([new viewEvents.ViewRevealRangeRequestEvent(viewRange, verticalType, revealHorizontal, scrollType)]); + } + + // ----------------------------------------------------------------------------------------------------------- + // ----- handlers beyond this point + + public trigger(source: string, handlerId: string, payload: any): void { + const H = editorCommon.Handler; + + if (handlerId === H.CompositionStart) { + this._isDoingComposition = true; + return; + } + + if (handlerId === H.CompositionEnd) { + this._isDoingComposition = false; + return; + } + + const oldState = new CursorModelState(this._model, this); + let cursorChangeReason = CursorChangeReason.NotSet; + + // ensure valid state on all cursors + this._cursors.ensureValidState(); + + this._isHandling = true; + + try { + switch (handlerId) { + case H.Type: + this._type(source, payload.text); + break; + + case H.ReplacePreviousChar: + this._replacePreviousChar(payload.text, payload.replaceCharCnt); + break; + + case H.Paste: + cursorChangeReason = CursorChangeReason.Paste; + this._paste(payload.text, payload.pasteOnNewLine); + break; + + case H.Cut: + this._cut(); + break; + + case H.Undo: + cursorChangeReason = CursorChangeReason.Undo; + this._interpretCommandResult(this._model.undo()); + break; + + case H.Redo: + cursorChangeReason = CursorChangeReason.Redo; + this._interpretCommandResult(this._model.redo()); + break; + + case H.ExecuteCommand: + this._externalExecuteCommand(payload); + break; + + case H.ExecuteCommands: + this._externalExecuteCommands(payload); + break; + } + } catch (err) { + onUnexpectedError(err); + } + + this._isHandling = false; + + if (this._emitStateChangedIfNecessary(source, cursorChangeReason, oldState)) { + this._revealRange(RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); + } + } + + private _type(source: string, text: string): void { + if (!this._isDoingComposition && source === 'keyboard') { + // If this event is coming straight from the keyboard, look for electric characters and enter + + for (let i = 0, len = text.length; i < len; i++) { + let charCode = text.charCodeAt(i); + let chr: string; + if (strings.isHighSurrogate(charCode) && i + 1 < len) { + chr = text.charAt(i) + text.charAt(i + 1); + i++; + } else { + chr = text.charAt(i); + } + + // Here we must interpret each typed character individually, that's why we create a new context + this._executeEditOperation(TypeOperations.typeWithInterceptors(this.context.config, this.context.model, this.getSelections(), chr)); + } + + } else { + this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this.context.config, this.context.model, this.getSelections(), text)); + } + } + + private _replacePreviousChar(text: string, replaceCharCnt: number): void { + this._executeEditOperation(TypeOperations.replacePreviousChar(this.context.config, this.context.model, this.getSelections(), text, replaceCharCnt)); + } + + private _paste(text: string, pasteOnNewLine: boolean): void { + this._executeEditOperation(TypeOperations.paste(this.context.config, this.context.model, this.getSelections(), pasteOnNewLine, text)); + } + + private _cut(): void { + this._executeEditOperation(DeleteOperations.cut(this.context.config, this.context.model, this.getSelections())); + } + + private _externalExecuteCommand(command: editorCommon.ICommand): void { + this._cursors.killSecondaryCursors(); + + this._executeEditOperation(new EditOperationResult([command], { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false + })); + } + + private _externalExecuteCommands(commands: editorCommon.ICommand[]): void { + this._executeEditOperation(new EditOperationResult(commands, { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false + })); + } +} + +interface IExecContext { + readonly model: editorCommon.IModel; + readonly selectionsBefore: Selection[]; + readonly selectionStartMarkers: string[]; + readonly positionMarkers: string[]; +} + +interface ICommandData { + operations: editorCommon.IIdentifiedSingleEditOperation[]; + hadTrackedEditOperation: boolean; +} + +interface ICommandsData { + operations: editorCommon.IIdentifiedSingleEditOperation[]; + hadTrackedEditOperation: boolean; +} + +class CommandExecutor { + + public static executeCommands(model: editorCommon.IModel, selectionsBefore: Selection[], commands: editorCommon.ICommand[]): Selection[] { + + const ctx: IExecContext = { + model: model, + selectionsBefore: selectionsBefore, + selectionStartMarkers: [], + positionMarkers: [] + }; + + const result = this._innerExecuteCommands(ctx, commands); + + for (let i = 0; i < ctx.selectionStartMarkers.length; i++) { + ctx.model._removeMarker(ctx.selectionStartMarkers[i]); + ctx.model._removeMarker(ctx.positionMarkers[i]); + } + + return result; + } + + private static _innerExecuteCommands(ctx: IExecContext, commands: editorCommon.ICommand[]): Selection[] { + + if (this._arrayIsEmpty(commands)) { + return null; + } + + const commandsData = this._getEditOperations(ctx, commands); + if (commandsData.operations.length === 0) { + return null; + } + + const rawOperations = commandsData.operations; + + const editableRange = ctx.model.getEditableRange(); + const editableRangeStart = editableRange.getStartPosition(); + const editableRangeEnd = editableRange.getEndPosition(); + for (let i = 0, len = rawOperations.length; i < len; i++) { + const operationRange = rawOperations[i].range; + if (!editableRangeStart.isBeforeOrEqual(operationRange.getStartPosition()) || !operationRange.getEndPosition().isBeforeOrEqual(editableRangeEnd)) { + // These commands are outside of the editable range + return null; + } + } + + const loserCursorsMap = this._getLoserCursorMap(rawOperations); + if (loserCursorsMap.hasOwnProperty('0')) { + // These commands are very messed up + console.warn('Ignoring commands'); + return null; + } + + // Remove operations belonging to losing cursors + let filteredOperations: editorCommon.IIdentifiedSingleEditOperation[] = []; + for (let i = 0, len = rawOperations.length; i < len; i++) { + if (!loserCursorsMap.hasOwnProperty(rawOperations[i].identifier.major.toString())) { + filteredOperations.push(rawOperations[i]); + } + } + + // TODO@Alex: find a better way to do this. + // give the hint that edit operations are tracked to the model + if (commandsData.hadTrackedEditOperation && filteredOperations.length > 0) { + filteredOperations[0]._isTracked = true; + } + const selectionsAfter = ctx.model.pushEditOperations(ctx.selectionsBefore, filteredOperations, (inverseEditOperations: editorCommon.IIdentifiedSingleEditOperation[]): Selection[] => { + let groupedInverseEditOperations: editorCommon.IIdentifiedSingleEditOperation[][] = []; + for (let i = 0; i < ctx.selectionsBefore.length; i++) { + groupedInverseEditOperations[i] = []; + } + for (let i = 0; i < inverseEditOperations.length; i++) { + const op = inverseEditOperations[i]; + if (!op.identifier) { + // perhaps auto whitespace trim edits + continue; + } + groupedInverseEditOperations[op.identifier.major].push(op); + } + const minorBasedSorter = (a: editorCommon.IIdentifiedSingleEditOperation, b: editorCommon.IIdentifiedSingleEditOperation) => { + return a.identifier.minor - b.identifier.minor; + }; + let cursorSelections: Selection[] = []; + for (let i = 0; i < ctx.selectionsBefore.length; i++) { + if (groupedInverseEditOperations[i].length > 0) { + groupedInverseEditOperations[i].sort(minorBasedSorter); + cursorSelections[i] = commands[i].computeCursorState(ctx.model, { + getInverseEditOperations: () => { + return groupedInverseEditOperations[i]; + }, + + getTrackedSelection: (id: string) => { + const idx = parseInt(id, 10); + const selectionStartMarker = ctx.model._getMarker(ctx.selectionStartMarkers[idx]); + const positionMarker = ctx.model._getMarker(ctx.positionMarkers[idx]); + return new Selection(selectionStartMarker.lineNumber, selectionStartMarker.column, positionMarker.lineNumber, positionMarker.column); + } + }); + } else { + cursorSelections[i] = ctx.selectionsBefore[i]; + } + } + return cursorSelections; + }); + + // Extract losing cursors + let losingCursors: number[] = []; + for (let losingCursorIndex in loserCursorsMap) { + if (loserCursorsMap.hasOwnProperty(losingCursorIndex)) { + losingCursors.push(parseInt(losingCursorIndex, 10)); + } + } + + // Sort losing cursors descending + losingCursors.sort((a: number, b: number): number => { + return b - a; + }); + + // Remove losing cursors + for (let i = 0; i < losingCursors.length; i++) { + selectionsAfter.splice(losingCursors[i], 1); + } + + return selectionsAfter; + } + + private static _arrayIsEmpty(commands: editorCommon.ICommand[]): boolean { + for (let i = 0, len = commands.length; i < len; i++) { + if (commands[i]) { + return false; + } + } + return true; + } + + private static _getEditOperations(ctx: IExecContext, commands: editorCommon.ICommand[]): ICommandsData { + let operations: editorCommon.IIdentifiedSingleEditOperation[] = []; + let hadTrackedEditOperation: boolean = false; + + for (let i = 0, len = commands.length; i < len; i++) { + if (commands[i]) { + const r = this._getEditOperationsFromCommand(ctx, i, commands[i]); + operations = operations.concat(r.operations); + hadTrackedEditOperation = hadTrackedEditOperation || r.hadTrackedEditOperation; + } + } + return { + operations: operations, + hadTrackedEditOperation: hadTrackedEditOperation + }; + } + + private static _getEditOperationsFromCommand(ctx: IExecContext, majorIdentifier: number, command: editorCommon.ICommand): ICommandData { + // This method acts as a transaction, if the command fails + // everything it has done is ignored + let operations: editorCommon.IIdentifiedSingleEditOperation[] = []; + let operationMinor = 0; + + const addEditOperation = (selection: Range, text: string) => { + if (selection.isEmpty() && text === '') { + // This command wants to add a no-op => no thank you + return; + } + operations.push({ + identifier: { + major: majorIdentifier, + minor: operationMinor++ + }, + range: selection, + text: text, + forceMoveMarkers: false, + isAutoWhitespaceEdit: command.insertsAutoWhitespace + }); + }; + + let hadTrackedEditOperation = false; + const addTrackedEditOperation = (selection: Range, text: string) => { + hadTrackedEditOperation = true; + addEditOperation(selection, text); + }; + + const trackSelection = (selection: Selection, trackPreviousOnEmpty?: boolean) => { + let selectionMarkerStickToPreviousCharacter: boolean; + let positionMarkerStickToPreviousCharacter: boolean; + + if (selection.isEmpty()) { + // Try to lock it with surrounding text + if (typeof trackPreviousOnEmpty === 'boolean') { + selectionMarkerStickToPreviousCharacter = trackPreviousOnEmpty; + positionMarkerStickToPreviousCharacter = trackPreviousOnEmpty; + } else { + const maxLineColumn = ctx.model.getLineMaxColumn(selection.startLineNumber); + if (selection.startColumn === maxLineColumn) { + selectionMarkerStickToPreviousCharacter = true; + positionMarkerStickToPreviousCharacter = true; + } else { + selectionMarkerStickToPreviousCharacter = false; + positionMarkerStickToPreviousCharacter = false; + } + } + } else { + if (selection.getDirection() === SelectionDirection.LTR) { + selectionMarkerStickToPreviousCharacter = false; + positionMarkerStickToPreviousCharacter = true; + } else { + selectionMarkerStickToPreviousCharacter = true; + positionMarkerStickToPreviousCharacter = false; + } + } + + const l = ctx.selectionStartMarkers.length; + ctx.selectionStartMarkers[l] = ctx.model._addMarker(0, selection.selectionStartLineNumber, selection.selectionStartColumn, selectionMarkerStickToPreviousCharacter); + ctx.positionMarkers[l] = ctx.model._addMarker(0, selection.positionLineNumber, selection.positionColumn, positionMarkerStickToPreviousCharacter); + return l.toString(); + }; + + const editOperationBuilder: editorCommon.IEditOperationBuilder = { + addEditOperation: addEditOperation, + addTrackedEditOperation: addTrackedEditOperation, + trackSelection: trackSelection + }; + + try { + command.getEditOperations(ctx.model, editOperationBuilder); + } catch (e) { + e.friendlyMessage = nls.localize('corrupt.commands', "Unexpected exception while executing command."); + onUnexpectedError(e); + return { + operations: [], + hadTrackedEditOperation: false + }; + } + + return { + operations: operations, + hadTrackedEditOperation: hadTrackedEditOperation + }; + } + + private static _getLoserCursorMap(operations: editorCommon.IIdentifiedSingleEditOperation[]): { [index: string]: boolean; } { + // This is destructive on the array + operations = operations.slice(0); + + // Sort operations with last one first + operations.sort((a: editorCommon.IIdentifiedSingleEditOperation, b: editorCommon.IIdentifiedSingleEditOperation): number => { + // Note the minus! + return -(Range.compareRangesUsingEnds(a.range, b.range)); + }); + + // Operations can not overlap! + let loserCursorsMap: { [index: string]: boolean; } = {}; + + for (let i = 1; i < operations.length; i++) { + const previousOp = operations[i - 1]; + const currentOp = operations[i]; + + if (previousOp.range.getStartPosition().isBefore(currentOp.range.getEndPosition())) { + + let loserMajor: number; + + if (previousOp.identifier.major > currentOp.identifier.major) { + // previousOp loses the battle + loserMajor = previousOp.identifier.major; + } else { + loserMajor = currentOp.identifier.major; + } + + loserCursorsMap[loserMajor.toString()] = true; + + for (let j = 0; j < operations.length; j++) { + if (operations[j].identifier.major === loserMajor) { + operations.splice(j, 1); + if (j < i) { + i--; + } + j--; + } + } + + if (i > 0) { + i--; + } + } + } + + return loserCursorsMap; + } +} diff --git a/src/vs/editor/common/controller/cursorCollection.ts b/src/vs/editor/common/controller/cursorCollection.ts new file mode 100644 index 0000000000..26d6ef4038 --- /dev/null +++ b/src/vs/editor/common/controller/cursorCollection.ts @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { OneCursor } from 'vs/editor/common/controller/oneCursor'; +import { Selection, ISelection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; +import { CursorState, CursorContext } from 'vs/editor/common/controller/cursorCommon'; + +export class CursorCollection { + + private context: CursorContext; + + private primaryCursor: OneCursor; + private secondaryCursors: OneCursor[]; + + // An index which identifies the last cursor that was added / moved (think Ctrl+drag) + private lastAddedCursorIndex: number; + + constructor(context: CursorContext) { + this.context = context; + this.primaryCursor = new OneCursor(context); + this.secondaryCursors = []; + this.lastAddedCursorIndex = 0; + } + + public dispose(): void { + this.primaryCursor.dispose(this.context); + this.killSecondaryCursors(); + } + + public updateContext(context: CursorContext): void { + this.context = context; + } + + public ensureValidState(): void { + this.primaryCursor.ensureValidState(this.context); + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + this.secondaryCursors[i].ensureValidState(this.context); + } + } + + public readSelectionFromMarkers(): Selection[] { + let result: Selection[] = []; + result[0] = this.primaryCursor.readSelectionFromMarkers(this.context); + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + result[i + 1] = this.secondaryCursors[i].readSelectionFromMarkers(this.context); + } + return result; + } + + public getAll(): CursorState[] { + let result: CursorState[] = []; + result[0] = this.primaryCursor.asCursorState(); + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + result[i + 1] = this.secondaryCursors[i].asCursorState(); + } + return result; + } + + public getViewPositions(): Position[] { + let result: Position[] = []; + result[0] = this.primaryCursor.viewState.position; + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + result[i + 1] = this.secondaryCursors[i].viewState.position; + } + return result; + } + + public getSelections(): Selection[] { + let result: Selection[] = []; + result[0] = this.primaryCursor.modelState.selection; + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + result[i + 1] = this.secondaryCursors[i].modelState.selection; + } + return result; + } + + public getViewSelections(): Selection[] { + let result: Selection[] = []; + result[0] = this.primaryCursor.viewState.selection; + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + result[i + 1] = this.secondaryCursors[i].viewState.selection; + } + return result; + } + + public setSelections(selections: ISelection[]): void { + this.setStates(CursorState.fromModelSelections(selections)); + } + + public getPrimaryCursor(): CursorState { + return this.primaryCursor.asCursorState(); + } + + public setStates(states: CursorState[]): void { + if (states === null) { + return; + } + this.primaryCursor.setState(this.context, states[0].modelState, states[0].viewState); + this._setSecondaryStates(states.slice(1)); + } + + /** + * Creates or disposes secondary cursors as necessary to match the number of `secondarySelections`. + */ + private _setSecondaryStates(secondaryStates: CursorState[]): void { + const secondaryCursorsLength = this.secondaryCursors.length; + const secondaryStatesLength = secondaryStates.length; + + if (secondaryCursorsLength < secondaryStatesLength) { + let createCnt = secondaryStatesLength - secondaryCursorsLength; + for (let i = 0; i < createCnt; i++) { + this._addSecondaryCursor(); + } + } else if (secondaryCursorsLength > secondaryStatesLength) { + let removeCnt = secondaryCursorsLength - secondaryStatesLength; + for (let i = 0; i < removeCnt; i++) { + this._removeSecondaryCursor(this.secondaryCursors.length - 1); + } + } + + for (let i = 0; i < secondaryStatesLength; i++) { + this.secondaryCursors[i].setState(this.context, secondaryStates[i].modelState, secondaryStates[i].viewState); + } + } + + public killSecondaryCursors(): void { + this._setSecondaryStates([]); + } + + private _addSecondaryCursor(): void { + this.secondaryCursors.push(new OneCursor(this.context)); + this.lastAddedCursorIndex = this.secondaryCursors.length; + } + + public getLastAddedCursorIndex(): number { + if (this.secondaryCursors.length === 0 || this.lastAddedCursorIndex === 0) { + return 0; + } + return this.lastAddedCursorIndex; + } + + private _removeSecondaryCursor(removeIndex: number): void { + if (this.lastAddedCursorIndex >= removeIndex + 1) { + this.lastAddedCursorIndex--; + } + this.secondaryCursors[removeIndex].dispose(this.context); + this.secondaryCursors.splice(removeIndex, 1); + } + + private _getAll(): OneCursor[] { + let result: OneCursor[] = []; + result[0] = this.primaryCursor; + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + result[i + 1] = this.secondaryCursors[i]; + } + return result; + } + + public normalize(): void { + if (this.secondaryCursors.length === 0) { + return; + } + let cursors = this._getAll(); + + interface SortedCursor { + index: number; + selection: Selection; + viewSelection: Selection; + } + let sortedCursors: SortedCursor[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + sortedCursors.push({ + index: i, + selection: cursors[i].modelState.selection, + viewSelection: cursors[i].viewState.selection + }); + } + sortedCursors.sort((a, b) => { + if (a.viewSelection.startLineNumber === b.viewSelection.startLineNumber) { + return a.viewSelection.startColumn - b.viewSelection.startColumn; + } + return a.viewSelection.startLineNumber - b.viewSelection.startLineNumber; + }); + + for (let sortedCursorIndex = 0; sortedCursorIndex < sortedCursors.length - 1; sortedCursorIndex++) { + const current = sortedCursors[sortedCursorIndex]; + const next = sortedCursors[sortedCursorIndex + 1]; + + const currentViewSelection = current.viewSelection; + const nextViewSelection = next.viewSelection; + + let shouldMergeCursors: boolean; + if (nextViewSelection.isEmpty() || currentViewSelection.isEmpty()) { + // Merge touching cursors if one of them is collapsed + shouldMergeCursors = nextViewSelection.getStartPosition().isBeforeOrEqual(currentViewSelection.getEndPosition()); + } else { + // Merge only overlapping cursors (i.e. allow touching ranges) + shouldMergeCursors = nextViewSelection.getStartPosition().isBefore(currentViewSelection.getEndPosition()); + } + + if (shouldMergeCursors) { + const winnerSortedCursorIndex = current.index < next.index ? sortedCursorIndex : sortedCursorIndex + 1; + const looserSortedCursorIndex = current.index < next.index ? sortedCursorIndex + 1 : sortedCursorIndex; + + const looserIndex = sortedCursors[looserSortedCursorIndex].index; + const winnerIndex = sortedCursors[winnerSortedCursorIndex].index; + + const looserSelection = sortedCursors[looserSortedCursorIndex].selection; + const winnerSelection = sortedCursors[winnerSortedCursorIndex].selection; + + if (!looserSelection.equalsSelection(winnerSelection)) { + const resultingRange = looserSelection.plusRange(winnerSelection); + const looserSelectionIsLTR = (looserSelection.selectionStartLineNumber === looserSelection.startLineNumber && looserSelection.selectionStartColumn === looserSelection.startColumn); + const winnerSelectionIsLTR = (winnerSelection.selectionStartLineNumber === winnerSelection.startLineNumber && winnerSelection.selectionStartColumn === winnerSelection.startColumn); + + // Give more importance to the last added cursor (think Ctrl-dragging + hitting another cursor) + let resultingSelectionIsLTR: boolean; + if (looserIndex === this.lastAddedCursorIndex) { + resultingSelectionIsLTR = looserSelectionIsLTR; + this.lastAddedCursorIndex = winnerIndex; + } else { + // Winner takes it all + resultingSelectionIsLTR = winnerSelectionIsLTR; + } + + let resultingSelection: Selection; + if (resultingSelectionIsLTR) { + resultingSelection = new Selection(resultingRange.startLineNumber, resultingRange.startColumn, resultingRange.endLineNumber, resultingRange.endColumn); + } else { + resultingSelection = new Selection(resultingRange.endLineNumber, resultingRange.endColumn, resultingRange.startLineNumber, resultingRange.startColumn); + } + + sortedCursors[winnerSortedCursorIndex].selection = resultingSelection; + const resultingState = CursorState.fromModelSelection(resultingSelection); + cursors[winnerIndex].setState(this.context, resultingState.modelState, resultingState.viewState); + } + + for (let j = 0; j < sortedCursors.length; j++) { + if (sortedCursors[j].index > looserIndex) { + sortedCursors[j].index--; + } + } + + cursors.splice(looserIndex, 1); + sortedCursors.splice(looserSortedCursorIndex, 1); + this._removeSecondaryCursor(looserIndex - 1); + + sortedCursorIndex--; + } + } + } +} diff --git a/src/vs/editor/common/controller/cursorColumnSelection.ts b/src/vs/editor/common/controller/cursorColumnSelection.ts new file mode 100644 index 0000000000..bd809a4da3 --- /dev/null +++ b/src/vs/editor/common/controller/cursorColumnSelection.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Selection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { SingleCursorState, CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; + +export interface IColumnSelectResult { + viewStates: SingleCursorState[]; + reversed: boolean; + toLineNumber: number; + toVisualColumn: number; +} + +export class ColumnSelection { + + private static _columnSelect(config: CursorConfiguration, model: ICursorSimpleModel, fromLineNumber: number, fromVisibleColumn: number, toLineNumber: number, toVisibleColumn: number): IColumnSelectResult { + let lineCount = Math.abs(toLineNumber - fromLineNumber) + 1; + let reversed = (fromLineNumber > toLineNumber); + let isRTL = (fromVisibleColumn > toVisibleColumn); + let isLTR = (fromVisibleColumn < toVisibleColumn); + + let result: SingleCursorState[] = []; + + // console.log(`fromVisibleColumn: ${fromVisibleColumn}, toVisibleColumn: ${toVisibleColumn}`); + + for (let i = 0; i < lineCount; i++) { + let lineNumber = fromLineNumber + (reversed ? -i : i); + + let startColumn = CursorColumns.columnFromVisibleColumn2(config, model, lineNumber, fromVisibleColumn); + let endColumn = CursorColumns.columnFromVisibleColumn2(config, model, lineNumber, toVisibleColumn); + let visibleStartColumn = CursorColumns.visibleColumnFromColumn2(config, model, new Position(lineNumber, startColumn)); + let visibleEndColumn = CursorColumns.visibleColumnFromColumn2(config, model, new Position(lineNumber, endColumn)); + + // console.log(`lineNumber: ${lineNumber}: visibleStartColumn: ${visibleStartColumn}, visibleEndColumn: ${visibleEndColumn}`); + + if (isLTR) { + if (visibleStartColumn > toVisibleColumn) { + continue; + } + if (visibleEndColumn < fromVisibleColumn) { + continue; + } + } + + if (isRTL) { + if (visibleEndColumn > fromVisibleColumn) { + continue; + } + if (visibleStartColumn < toVisibleColumn) { + continue; + } + } + + result.push(new SingleCursorState( + new Range(lineNumber, startColumn, lineNumber, startColumn), 0, + new Position(lineNumber, endColumn), 0 + )); + } + + return { + viewStates: result, + reversed: reversed, + toLineNumber: toLineNumber, + toVisualColumn: toVisibleColumn + }; + } + + public static columnSelect(config: CursorConfiguration, model: ICursorSimpleModel, fromViewSelection: Selection, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult { + const fromViewPosition = new Position(fromViewSelection.selectionStartLineNumber, fromViewSelection.selectionStartColumn); + const fromViewVisibleColumn = CursorColumns.visibleColumnFromColumn2(config, model, fromViewPosition); + return ColumnSelection._columnSelect(config, model, fromViewPosition.lineNumber, fromViewVisibleColumn, toViewLineNumber, toViewVisualColumn); + } + + public static columnSelectLeft(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult { + if (toViewVisualColumn > 1) { + toViewVisualColumn--; + } + + return this.columnSelect(config, model, cursor.selection, toViewLineNumber, toViewVisualColumn); + } + + public static columnSelectRight(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult { + let maxVisualViewColumn = 0; + let minViewLineNumber = Math.min(cursor.position.lineNumber, toViewLineNumber); + let maxViewLineNumber = Math.max(cursor.position.lineNumber, toViewLineNumber); + for (let lineNumber = minViewLineNumber; lineNumber <= maxViewLineNumber; lineNumber++) { + let lineMaxViewColumn = model.getLineMaxColumn(lineNumber); + let lineMaxVisualViewColumn = CursorColumns.visibleColumnFromColumn2(config, model, new Position(lineNumber, lineMaxViewColumn)); + maxVisualViewColumn = Math.max(maxVisualViewColumn, lineMaxVisualViewColumn); + } + + if (toViewVisualColumn < maxVisualViewColumn) { + toViewVisualColumn++; + } + + return this.columnSelect(config, model, cursor.selection, toViewLineNumber, toViewVisualColumn); + } + + public static columnSelectUp(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, isPaged: boolean, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult { + let linesCount = isPaged ? config.pageSize : 1; + + toViewLineNumber -= linesCount; + if (toViewLineNumber < 1) { + toViewLineNumber = 1; + } + + return this.columnSelect(config, model, cursor.selection, toViewLineNumber, toViewVisualColumn); + } + + public static columnSelectDown(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, isPaged: boolean, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult { + let linesCount = isPaged ? config.pageSize : 1; + + toViewLineNumber += linesCount; + if (toViewLineNumber > model.getLineCount()) { + toViewLineNumber = model.getLineCount(); + } + + return this.columnSelect(config, model, cursor.selection, toViewLineNumber, toViewVisualColumn); + } +} diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts new file mode 100644 index 0000000000..3e0a7a7754 --- /dev/null +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -0,0 +1,553 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Position } from 'vs/editor/common/core/position'; +import { CharCode } from 'vs/base/common/charCode'; +import * as strings from 'vs/base/common/strings'; +import { ICommand, TextModelResolvedOptions, IConfiguration, IModel, ScrollType } from 'vs/editor/common/editorCommon'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { Selection, ISelection } from 'vs/editor/common/core/selection'; +import { Range } from 'vs/editor/common/core/range'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; + +export interface IColumnSelectData { + toViewLineNumber: number; + toViewVisualColumn: number; +} + +export const enum RevealTarget { + Primary = 0, + TopMost = 1, + BottomMost = 2 +} + +export interface ICursors { + readonly context: CursorContext; + getPrimaryCursor(): CursorState; + getLastAddedCursorIndex(): number; + getAll(): CursorState[]; + + getColumnSelectData(): IColumnSelectData; + setColumnSelectData(columnSelectData: IColumnSelectData): void; + + setStates(source: string, reason: CursorChangeReason, states: CursorState[]): void; + reveal(horizontal: boolean, target: RevealTarget, scrollType: ScrollType): void; + revealRange(revealHorizontal: boolean, viewRange: Range, verticalType: VerticalRevealType, scrollType: ScrollType): void; + + scrollTo(desiredScrollTop: number): void; +} + +export interface CharacterMap { + [char: string]: string; +} + +export class CursorConfiguration { + _cursorMoveConfigurationBrand: void; + + public readonly readOnly: boolean; + public readonly tabSize: number; + public readonly insertSpaces: boolean; + public readonly oneIndent: string; + public readonly pageSize: number; + public readonly lineHeight: number; + public readonly useTabStops: boolean; + public readonly wordSeparators: string; + public readonly emptySelectionClipboard: boolean; + public readonly autoClosingBrackets: boolean; + public readonly autoIndent: boolean; + public readonly autoClosingPairsOpen: CharacterMap; + public readonly autoClosingPairsClose: CharacterMap; + public readonly surroundingPairs: CharacterMap; + public readonly electricChars: { [key: string]: boolean; }; + + public static shouldRecreate(e: IConfigurationChangedEvent): boolean { + return ( + e.layoutInfo + || e.wordSeparators + || e.emptySelectionClipboard + || e.autoClosingBrackets + || e.useTabStops + || e.lineHeight + || e.readOnly + ); + } + + constructor( + languageIdentifier: LanguageIdentifier, + oneIndent: string, + modelOptions: TextModelResolvedOptions, + configuration: IConfiguration + ) { + let c = configuration.editor; + + this.readOnly = c.readOnly; + this.tabSize = modelOptions.tabSize; + this.insertSpaces = modelOptions.insertSpaces; + this.oneIndent = oneIndent; + this.pageSize = Math.floor(c.layoutInfo.height / c.fontInfo.lineHeight) - 2; + this.lineHeight = c.lineHeight; + this.useTabStops = c.useTabStops; + this.wordSeparators = c.wordSeparators; + this.emptySelectionClipboard = c.emptySelectionClipboard; + this.autoClosingBrackets = c.autoClosingBrackets; + this.autoIndent = c.autoIndent; + + this.autoClosingPairsOpen = {}; + this.autoClosingPairsClose = {}; + this.surroundingPairs = {}; + this.electricChars = {}; + + let electricChars = CursorConfiguration._getElectricCharacters(languageIdentifier); + if (electricChars) { + for (let i = 0; i < electricChars.length; i++) { + this.electricChars[electricChars[i]] = true; + } + } + + let autoClosingPairs = CursorConfiguration._getAutoClosingPairs(languageIdentifier); + if (autoClosingPairs) { + for (let i = 0; i < autoClosingPairs.length; i++) { + this.autoClosingPairsOpen[autoClosingPairs[i].open] = autoClosingPairs[i].close; + this.autoClosingPairsClose[autoClosingPairs[i].close] = autoClosingPairs[i].open; + } + } + + let surroundingPairs = CursorConfiguration._getSurroundingPairs(languageIdentifier); + if (surroundingPairs) { + for (let i = 0; i < surroundingPairs.length; i++) { + this.surroundingPairs[surroundingPairs[i].open] = surroundingPairs[i].close; + } + } + } + + public normalizeIndentation(str: string): string { + return TextModel.normalizeIndentation(str, this.tabSize, this.insertSpaces); + } + + private static _getElectricCharacters(languageIdentifier: LanguageIdentifier): string[] { + try { + return LanguageConfigurationRegistry.getElectricCharacters(languageIdentifier.id); + } catch (e) { + onUnexpectedError(e); + return null; + } + } + + private static _getAutoClosingPairs(languageIdentifier: LanguageIdentifier): IAutoClosingPair[] { + try { + return LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id); + } catch (e) { + onUnexpectedError(e); + return null; + } + } + + private static _getSurroundingPairs(languageIdentifier: LanguageIdentifier): IAutoClosingPair[] { + try { + return LanguageConfigurationRegistry.getSurroundingPairs(languageIdentifier.id); + } catch (e) { + onUnexpectedError(e); + return null; + } + } +} + +/** + * Represents a simple model (either the model or the view model). + */ +export interface ICursorSimpleModel { + getLineCount(): number; + getLineContent(lineNumber: number): string; + getLineMinColumn(lineNumber: number): number; + getLineMaxColumn(lineNumber: number): number; + getLineFirstNonWhitespaceColumn(lineNumber: number): number; + getLineLastNonWhitespaceColumn(lineNumber: number): number; +} + +/** + * Represents the cursor state on either the model or on the view model. + */ +export class SingleCursorState { + _singleCursorStateBrand: void; + + // --- selection can start as a range (think double click and drag) + public readonly selectionStart: Range; + public readonly selectionStartLeftoverVisibleColumns: number; + public readonly position: Position; + public readonly leftoverVisibleColumns: number; + public readonly selection: Selection; + + constructor( + selectionStart: Range, + selectionStartLeftoverVisibleColumns: number, + position: Position, + leftoverVisibleColumns: number, + ) { + this.selectionStart = selectionStart; + this.selectionStartLeftoverVisibleColumns = selectionStartLeftoverVisibleColumns; + this.position = position; + this.leftoverVisibleColumns = leftoverVisibleColumns; + this.selection = SingleCursorState._computeSelection(this.selectionStart, this.position); + } + + public equals(other: SingleCursorState) { + return ( + this.selectionStartLeftoverVisibleColumns === other.selectionStartLeftoverVisibleColumns + && this.leftoverVisibleColumns === other.leftoverVisibleColumns + && this.position.equals(other.position) + && this.selectionStart.equalsRange(other.selectionStart) + ); + } + + public hasSelection(): boolean { + return (!this.selection.isEmpty() || !this.selectionStart.isEmpty()); + } + + public move(inSelectionMode: boolean, lineNumber: number, column: number, leftoverVisibleColumns: number): SingleCursorState { + if (inSelectionMode) { + // move just position + return new SingleCursorState( + this.selectionStart, + this.selectionStartLeftoverVisibleColumns, + new Position(lineNumber, column), + leftoverVisibleColumns + ); + } else { + // move everything + return new SingleCursorState( + new Range(lineNumber, column, lineNumber, column), + leftoverVisibleColumns, + new Position(lineNumber, column), + leftoverVisibleColumns + ); + } + } + + private static _computeSelection(selectionStart: Range, position: Position): Selection { + let startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number; + if (selectionStart.isEmpty()) { + startLineNumber = selectionStart.startLineNumber; + startColumn = selectionStart.startColumn; + endLineNumber = position.lineNumber; + endColumn = position.column; + } else { + if (position.isBeforeOrEqual(selectionStart.getStartPosition())) { + startLineNumber = selectionStart.endLineNumber; + startColumn = selectionStart.endColumn; + endLineNumber = position.lineNumber; + endColumn = position.column; + } else { + startLineNumber = selectionStart.startLineNumber; + startColumn = selectionStart.startColumn; + endLineNumber = position.lineNumber; + endColumn = position.column; + } + } + return new Selection( + startLineNumber, + startColumn, + endLineNumber, + endColumn + ); + } +} + +export class CursorContext { + _cursorContextBrand: void; + + public readonly model: IModel; + public readonly viewModel: IViewModel; + public readonly config: CursorConfiguration; + + constructor(configuration: IConfiguration, model: IModel, viewModel: IViewModel) { + this.model = model; + this.viewModel = viewModel; + this.config = new CursorConfiguration( + this.model.getLanguageIdentifier(), + this.model.getOneIndent(), + this.model.getOptions(), + configuration + ); + } + + public validateViewPosition(viewPosition: Position, modelPosition: Position): Position { + return this.viewModel.coordinatesConverter.validateViewPosition(viewPosition, modelPosition); + } + + public validateViewRange(viewRange: Range, expectedModelRange: Range): Range { + return this.viewModel.coordinatesConverter.validateViewRange(viewRange, expectedModelRange); + } + + public convertViewRangeToModelRange(viewRange: Range): Range { + return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange); + } + + public convertViewPositionToModelPosition(lineNumber: number, column: number): Position { + return this.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber, column)); + } + + public convertModelPositionToViewPosition(modelPosition: Position): Position { + return this.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); + } + + public convertModelRangeToViewRange(modelRange: Range): Range { + return this.viewModel.coordinatesConverter.convertModelRangeToViewRange(modelRange); + } + + public getCurrentScrollTop(): number { + return this.viewModel.viewLayout.getCurrentScrollTop(); + } + + public getCompletelyVisibleViewRange(): Range { + return this.viewModel.getCompletelyVisibleViewRange(); + } + + public getCompletelyVisibleModelRange(): Range { + const viewRange = this.viewModel.getCompletelyVisibleViewRange(); + return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange); + } + + public getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range { + return this.viewModel.getCompletelyVisibleViewRangeAtScrollTop(scrollTop); + } + + public getCompletelyVisibleModelRangeAtScrollTop(scrollTop: number): Range { + const viewRange = this.viewModel.getCompletelyVisibleViewRangeAtScrollTop(scrollTop); + return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange); + } + + public getVerticalOffsetForViewLine(viewLineNumber: number): number { + return this.viewModel.viewLayout.getVerticalOffsetForLineNumber(viewLineNumber); + } +} + +export class CursorState { + _cursorStateBrand: void; + + public static fromModelState(modelState: SingleCursorState): CursorState { + return new CursorState(modelState, null); + } + + public static fromViewState(viewState: SingleCursorState): CursorState { + return new CursorState(null, viewState); + } + + public static fromModelSelection(modelSelection: ISelection): CursorState { + const selectionStartLineNumber = modelSelection.selectionStartLineNumber; + const selectionStartColumn = modelSelection.selectionStartColumn; + const positionLineNumber = modelSelection.positionLineNumber; + const positionColumn = modelSelection.positionColumn; + const modelState = new SingleCursorState( + new Range(selectionStartLineNumber, selectionStartColumn, selectionStartLineNumber, selectionStartColumn), 0, + new Position(positionLineNumber, positionColumn), 0 + ); + return CursorState.fromModelState(modelState); + } + + public static fromModelSelections(modelSelections: ISelection[]): CursorState[] { + let states: CursorState[] = []; + for (let i = 0, len = modelSelections.length; i < len; i++) { + states[i] = this.fromModelSelection(modelSelections[i]); + } + return states; + } + + public static ensureInEditableRange(context: CursorContext, states: CursorState[]): CursorState[] { + const model = context.model; + if (!model.hasEditableRange()) { + return states; + } + + const modelEditableRange = model.getEditableRange(); + const viewEditableRange = context.convertModelRangeToViewRange(modelEditableRange); + + let result: CursorState[] = []; + for (let i = 0, len = states.length; i < len; i++) { + const state = states[i]; + + if (state.modelState) { + const newModelState = CursorState._ensureInEditableRange(state.modelState, modelEditableRange); + result[i] = newModelState ? CursorState.fromModelState(newModelState) : state; + } else { + const newViewState = CursorState._ensureInEditableRange(state.viewState, viewEditableRange); + result[i] = newViewState ? CursorState.fromViewState(newViewState) : state; + } + } + return result; + } + + private static _ensureInEditableRange(state: SingleCursorState, editableRange: Range): SingleCursorState { + const position = state.position; + + if (position.lineNumber < editableRange.startLineNumber || (position.lineNumber === editableRange.startLineNumber && position.column < editableRange.startColumn)) { + return new SingleCursorState( + state.selectionStart, state.selectionStartLeftoverVisibleColumns, + new Position(editableRange.startLineNumber, editableRange.startColumn), 0 + ); + } + + if (position.lineNumber > editableRange.endLineNumber || (position.lineNumber === editableRange.endLineNumber && position.column > editableRange.endColumn)) { + return new SingleCursorState( + state.selectionStart, state.selectionStartLeftoverVisibleColumns, + new Position(editableRange.endLineNumber, editableRange.endColumn), 0 + ); + } + + return null; + } + + readonly modelState: SingleCursorState; + readonly viewState: SingleCursorState; + + constructor(modelState: SingleCursorState, viewState: SingleCursorState) { + this.modelState = modelState; + this.viewState = viewState; + } + + public equals(other: CursorState): boolean { + return (this.viewState.equals(other.viewState) && this.modelState.equals(other.modelState)); + } +} + +export class EditOperationResult { + _editOperationResultBrand: void; + + readonly commands: ICommand[]; + readonly shouldPushStackElementBefore: boolean; + readonly shouldPushStackElementAfter: boolean; + + constructor( + commands: ICommand[], + opts: { + shouldPushStackElementBefore: boolean; + shouldPushStackElementAfter: boolean; + } + ) { + this.commands = commands; + this.shouldPushStackElementBefore = opts.shouldPushStackElementBefore; + this.shouldPushStackElementAfter = opts.shouldPushStackElementAfter; + } +} + +/** + * Common operations that work and make sense both on the model and on the view model. + */ +export class CursorColumns { + + public static isLowSurrogate(model: ICursorSimpleModel, lineNumber: number, charOffset: number): boolean { + let lineContent = model.getLineContent(lineNumber); + if (charOffset < 0 || charOffset >= lineContent.length) { + return false; + } + return strings.isLowSurrogate(lineContent.charCodeAt(charOffset)); + } + + public static isHighSurrogate(model: ICursorSimpleModel, lineNumber: number, charOffset: number): boolean { + let lineContent = model.getLineContent(lineNumber); + if (charOffset < 0 || charOffset >= lineContent.length) { + return false; + } + return strings.isHighSurrogate(lineContent.charCodeAt(charOffset)); + } + + public static isInsideSurrogatePair(model: ICursorSimpleModel, lineNumber: number, column: number): boolean { + return this.isHighSurrogate(model, lineNumber, column - 2); + } + + public static visibleColumnFromColumn(lineContent: string, column: number, tabSize: number): number { + let endOffset = lineContent.length; + if (endOffset > column - 1) { + endOffset = column - 1; + } + + let result = 0; + for (let i = 0; i < endOffset; i++) { + let charCode = lineContent.charCodeAt(i); + if (charCode === CharCode.Tab) { + result = this.nextTabStop(result, tabSize); + } else { + result = result + 1; + } + } + return result; + } + + public static visibleColumnFromColumn2(config: CursorConfiguration, model: ICursorSimpleModel, position: Position): number { + return this.visibleColumnFromColumn(model.getLineContent(position.lineNumber), position.column, config.tabSize); + } + + public static columnFromVisibleColumn(lineContent: string, visibleColumn: number, tabSize: number): number { + if (visibleColumn <= 0) { + return 1; + } + + const lineLength = lineContent.length; + + let beforeVisibleColumn = 0; + for (let i = 0; i < lineLength; i++) { + let charCode = lineContent.charCodeAt(i); + + let afterVisibleColumn: number; + if (charCode === CharCode.Tab) { + afterVisibleColumn = this.nextTabStop(beforeVisibleColumn, tabSize); + } else { + afterVisibleColumn = beforeVisibleColumn + 1; + } + + if (afterVisibleColumn >= visibleColumn) { + let prevDelta = visibleColumn - beforeVisibleColumn; + let afterDelta = afterVisibleColumn - visibleColumn; + if (afterDelta < prevDelta) { + return i + 2; + } else { + return i + 1; + } + } + + beforeVisibleColumn = afterVisibleColumn; + } + + // walked the entire string + return lineLength + 1; + } + + public static columnFromVisibleColumn2(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, visibleColumn: number): number { + let result = this.columnFromVisibleColumn(model.getLineContent(lineNumber), visibleColumn, config.tabSize); + + let minColumn = model.getLineMinColumn(lineNumber); + if (result < minColumn) { + return minColumn; + } + + let maxColumn = model.getLineMaxColumn(lineNumber); + if (result > maxColumn) { + return maxColumn; + } + + return result; + } + + /** + * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + */ + public static nextTabStop(visibleColumn: number, tabSize: number): number { + return visibleColumn + tabSize - visibleColumn % tabSize; + } + + /** + * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + */ + public static prevTabStop(column: number, tabSize: number): number { + return column - 1 - (column - 1) % tabSize; + } +} diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts new file mode 100644 index 0000000000..d1fa5a2da0 --- /dev/null +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -0,0 +1,218 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; +import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult } from 'vs/editor/common/controller/cursorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; +import * as strings from 'vs/base/common/strings'; +import { ICommand } from 'vs/editor/common/editorCommon'; + +export class DeleteOperations { + + public static deleteRight(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, ICommand[]] { + let commands: ICommand[] = []; + let shouldPushStackElementBefore = false; + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + + let deleteSelection: Range = selection; + + if (deleteSelection.isEmpty()) { + let position = selection.getPosition(); + let rightOfPosition = MoveOperations.right(config, model, position.lineNumber, position.column); + deleteSelection = new Range( + rightOfPosition.lineNumber, + rightOfPosition.column, + position.lineNumber, + position.column + ); + } + + if (deleteSelection.isEmpty()) { + // Probably at end of file => ignore + commands[i] = null; + continue; + } + + if (deleteSelection.startLineNumber !== deleteSelection.endLineNumber) { + shouldPushStackElementBefore = true; + } + + commands[i] = new ReplaceCommand(deleteSelection, ''); + } + return [shouldPushStackElementBefore, commands]; + } + + private static _isAutoClosingPairDelete(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): boolean { + if (!config.autoClosingBrackets) { + return false; + } + + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + const position = selection.getPosition(); + + if (!selection.isEmpty()) { + return false; + } + + const lineText = model.getLineContent(position.lineNumber); + const character = lineText[position.column - 2]; + + if (!config.autoClosingPairsOpen.hasOwnProperty(character)) { + return false; + } + + const afterCharacter = lineText[position.column - 1]; + const closeCharacter = config.autoClosingPairsOpen[character]; + + if (afterCharacter !== closeCharacter) { + return false; + } + } + + return true; + } + + private static _runAutoClosingPairDelete(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, ICommand[]] { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + const position = selections[i].getPosition(); + const deleteSelection = new Range( + position.lineNumber, + position.column - 1, + position.lineNumber, + position.column + 1 + ); + commands[i] = new ReplaceCommand(deleteSelection, ''); + } + return [true, commands]; + } + + public static deleteLeft(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, ICommand[]] { + + if (this._isAutoClosingPairDelete(config, model, selections)) { + return this._runAutoClosingPairDelete(config, model, selections); + } + + let commands: ICommand[] = []; + let shouldPushStackElementBefore = false; + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + + let deleteSelection: Range = selection; + + if (deleteSelection.isEmpty()) { + let position = selection.getPosition(); + + if (config.useTabStops && position.column > 1) { + let lineContent = model.getLineContent(position.lineNumber); + + let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent); + let lastIndentationColumn = ( + firstNonWhitespaceIndex === -1 + ? /* entire string is whitespace */lineContent.length + 1 + : firstNonWhitespaceIndex + 1 + ); + + if (position.column <= lastIndentationColumn) { + let fromVisibleColumn = CursorColumns.visibleColumnFromColumn2(config, model, position); + let toVisibleColumn = CursorColumns.prevTabStop(fromVisibleColumn, config.tabSize); + let toColumn = CursorColumns.columnFromVisibleColumn2(config, model, position.lineNumber, toVisibleColumn); + deleteSelection = new Range(position.lineNumber, toColumn, position.lineNumber, position.column); + } else { + deleteSelection = new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column); + } + } else { + let leftOfPosition = MoveOperations.left(config, model, position.lineNumber, position.column); + deleteSelection = new Range( + leftOfPosition.lineNumber, + leftOfPosition.column, + position.lineNumber, + position.column + ); + } + } + + if (deleteSelection.isEmpty()) { + // Probably at beginning of file => ignore + commands[i] = null; + continue; + } + + if (deleteSelection.startLineNumber !== deleteSelection.endLineNumber) { + shouldPushStackElementBefore = true; + } + + commands[i] = new ReplaceCommand(deleteSelection, ''); + } + return [shouldPushStackElementBefore, commands]; + } + + public static cut(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): EditOperationResult { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + + if (selection.isEmpty()) { + if (config.emptySelectionClipboard) { + // This is a full line cut + + let position = selection.getPosition(); + + let startLineNumber: number, + startColumn: number, + endLineNumber: number, + endColumn: number; + + if (position.lineNumber < model.getLineCount()) { + // Cutting a line in the middle of the model + startLineNumber = position.lineNumber; + startColumn = 1; + endLineNumber = position.lineNumber + 1; + endColumn = 1; + } else if (position.lineNumber > 1) { + // Cutting the last line & there are more than 1 lines in the model + startLineNumber = position.lineNumber - 1; + startColumn = model.getLineMaxColumn(position.lineNumber - 1); + endLineNumber = position.lineNumber; + endColumn = model.getLineMaxColumn(position.lineNumber); + } else { + // Cutting the single line that the model contains + startLineNumber = position.lineNumber; + startColumn = 1; + endLineNumber = position.lineNumber; + endColumn = model.getLineMaxColumn(position.lineNumber); + } + + let deleteSelection = new Range( + startLineNumber, + startColumn, + endLineNumber, + endColumn + ); + + if (!deleteSelection.isEmpty()) { + commands[i] = new ReplaceCommand(deleteSelection, ''); + } else { + commands[i] = null; + } + } else { + // Cannot cut empty selection + commands[i] = null; + } + } else { + commands[i] = new ReplaceCommand(selection, ''); + } + } + return new EditOperationResult(commands, { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: true + }); + } +} diff --git a/src/vs/editor/common/controller/cursorEvents.ts b/src/vs/editor/common/controller/cursorEvents.ts new file mode 100644 index 0000000000..ad295bfee1 --- /dev/null +++ b/src/vs/editor/common/controller/cursorEvents.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; + +/** + * Describes the reason the cursor has changed its position. + */ +export enum CursorChangeReason { + /** + * Unknown or not set. + */ + NotSet = 0, + /** + * A `model.setValue()` was called. + */ + ContentFlush = 1, + /** + * The `model` has been changed outside of this cursor and the cursor recovers its position from associated markers. + */ + RecoverFromMarkers = 2, + /** + * There was an explicit user gesture. + */ + Explicit = 3, + /** + * There was a Paste. + */ + Paste = 4, + /** + * There was an Undo. + */ + Undo = 5, + /** + * There was a Redo. + */ + Redo = 6, +} +/** + * An event describing that the cursor position has changed. + */ +export interface ICursorPositionChangedEvent { + /** + * Primary cursor's position. + */ + readonly position: Position; + /** + * Secondary cursors' position. + */ + readonly secondaryPositions: Position[]; + /** + * Reason. + */ + readonly reason: CursorChangeReason; + /** + * Source of the call that caused the event. + */ + readonly source: string; +} +/** + * An event describing that the cursor selection has changed. + */ +export interface ICursorSelectionChangedEvent { + /** + * The primary selection. + */ + readonly selection: Selection; + /** + * The secondary selections. + */ + readonly secondarySelections: Selection[]; + /** + * Source of the call that caused the event. + */ + readonly source: string; + /** + * Reason. + */ + readonly reason: CursorChangeReason; +} diff --git a/src/vs/editor/common/controller/cursorMoveCommands.ts b/src/vs/editor/common/controller/cursorMoveCommands.ts new file mode 100644 index 0000000000..621fe4294f --- /dev/null +++ b/src/vs/editor/common/controller/cursorMoveCommands.ts @@ -0,0 +1,762 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { SingleCursorState, ICursorSimpleModel, CursorState, CursorContext } from 'vs/editor/common/controller/cursorCommon'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; +import { WordOperations } from 'vs/editor/common/controller/cursorWordOperations'; +import * as types from 'vs/base/common/types'; +import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; + +export class CursorMoveCommands { + + public static addCursorDown(context: CursorContext, cursors: CursorState[]): CursorState[] { + let result: CursorState[] = [], resultLen = 0; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState); + result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(context.config, context.viewModel, cursor.viewState)); + } + return result; + } + + public static addCursorUp(context: CursorContext, cursors: CursorState[]): CursorState[] { + let result: CursorState[] = [], resultLen = 0; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState); + result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(context.config, context.viewModel, cursor.viewState)); + } + return result; + } + + public static moveToBeginningOfLine(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = this._moveToLineStart(context, cursor, inSelectionMode); + } + + return result; + } + + private static _moveToLineStart(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): CursorState { + const currentViewStateColumn = cursor.viewState.position.column; + const currentModelStateColumn = cursor.modelState.position.column; + const isFirstLineOfWrappedLine = currentViewStateColumn === currentModelStateColumn; + + const currentViewStatelineNumber = cursor.viewState.position.lineNumber; + const firstNonBlankColumn = context.viewModel.getLineFirstNonWhitespaceColumn(currentViewStatelineNumber); + const isBeginningOfViewLine = currentViewStateColumn === firstNonBlankColumn; + + if (!isFirstLineOfWrappedLine && !isBeginningOfViewLine) { + return this._moveToLineStartByView(context, cursor, inSelectionMode); + } else { + return this._moveToLineStartByModel(context, cursor, inSelectionMode); + } + } + + private static _moveToLineStartByView(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): CursorState { + return CursorState.fromViewState( + MoveOperations.moveToBeginningOfLine(context.config, context.viewModel, cursor.viewState, inSelectionMode) + ); + } + + private static _moveToLineStartByModel(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): CursorState { + return CursorState.fromModelState( + MoveOperations.moveToBeginningOfLine(context.config, context.model, cursor.modelState, inSelectionMode) + ); + } + + public static moveToEndOfLine(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = this._moveToLineEnd(context, cursor, inSelectionMode); + } + + return result; + } + + private static _moveToLineEnd(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): CursorState { + const viewStatePosition = cursor.viewState.position; + const viewModelMaxColumn = context.viewModel.getLineMaxColumn(viewStatePosition.lineNumber); + const isEndOfViewLine = viewStatePosition.column === viewModelMaxColumn; + + const modelStatePosition = cursor.modelState.position; + const modelMaxColumn = context.model.getLineMaxColumn(modelStatePosition.lineNumber); + const isEndLineOfWrappedLine = viewModelMaxColumn - viewStatePosition.column === modelMaxColumn - modelStatePosition.column; + + if (isEndOfViewLine || isEndLineOfWrappedLine) { + return this._moveToLineEndByModel(context, cursor, inSelectionMode); + } else { + return this._moveToLineEndByView(context, cursor, inSelectionMode); + } + } + + private static _moveToLineEndByView(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): CursorState { + return CursorState.fromViewState( + MoveOperations.moveToEndOfLine(context.config, context.viewModel, cursor.viewState, inSelectionMode) + ); + } + + private static _moveToLineEndByModel(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): CursorState { + return CursorState.fromModelState( + MoveOperations.moveToEndOfLine(context.config, context.model, cursor.modelState, inSelectionMode) + ); + } + + public static expandLineSelection(context: CursorContext, cursors: CursorState[]): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + + const viewSelection = cursor.viewState.selection; + const startLineNumber = viewSelection.startLineNumber; + const lineCount = context.viewModel.getLineCount(); + + let endLineNumber = viewSelection.endLineNumber; + let endColumn: number; + if (endLineNumber === lineCount) { + endColumn = context.viewModel.getLineMaxColumn(lineCount); + } else { + endLineNumber++; + endColumn = 1; + } + + result[i] = CursorState.fromViewState(new SingleCursorState( + new Range(startLineNumber, 1, startLineNumber, 1), 0, + new Position(endLineNumber, endColumn), 0 + )); + } + return result; + } + + public static moveToBeginningOfBuffer(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = CursorState.fromModelState(MoveOperations.moveToBeginningOfBuffer(context.config, context.model, cursor.modelState, inSelectionMode)); + } + return result; + } + + public static moveToEndOfBuffer(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = CursorState.fromModelState(MoveOperations.moveToEndOfBuffer(context.config, context.model, cursor.modelState, inSelectionMode)); + } + return result; + } + + public static selectAll(context: CursorContext, cursor: CursorState): CursorState { + + if (context.model.hasEditableRange()) { + // Toggle between selecting editable range and selecting the entire buffer + + const editableRange = context.model.getEditableRange(); + const selection = cursor.modelState.selection; + + if (!selection.equalsRange(editableRange)) { + // Selection is not editable range => select editable range + return CursorState.fromModelState(new SingleCursorState( + new Range(editableRange.startLineNumber, editableRange.startColumn, editableRange.startLineNumber, editableRange.startColumn), 0, + new Position(editableRange.endLineNumber, editableRange.endColumn), 0 + )); + } + } + + const lineCount = context.model.getLineCount(); + const maxColumn = context.model.getLineMaxColumn(lineCount); + + return CursorState.fromModelState(new SingleCursorState( + new Range(1, 1, 1, 1), 0, + new Position(lineCount, maxColumn), 0 + )); + } + + public static line(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): CursorState { + const position = context.model.validatePosition(_position); + const viewPosition = ( + _viewPosition + ? context.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position) + : context.convertModelPositionToViewPosition(position) + ); + + if (!inSelectionMode || !cursor.modelState.hasSelection()) { + // Entering line selection for the first time + const lineCount = context.model.getLineCount(); + + let selectToLineNumber = position.lineNumber + 1; + let selectToColumn = 1; + if (selectToLineNumber > lineCount) { + selectToLineNumber = lineCount; + selectToColumn = context.model.getLineMaxColumn(selectToLineNumber); + } + + return CursorState.fromModelState(new SingleCursorState( + new Range(position.lineNumber, 1, selectToLineNumber, selectToColumn), 0, + new Position(selectToLineNumber, selectToColumn), 0 + )); + } + + // Continuing line selection + const enteringLineNumber = cursor.modelState.selectionStart.getStartPosition().lineNumber; + + if (position.lineNumber < enteringLineNumber) { + + return CursorState.fromViewState(cursor.viewState.move( + cursor.modelState.hasSelection(), viewPosition.lineNumber, 1, 0 + )); + + } else if (position.lineNumber > enteringLineNumber) { + + const lineCount = context.viewModel.getLineCount(); + + let selectToViewLineNumber = viewPosition.lineNumber + 1; + let selectToViewColumn = 1; + if (selectToViewLineNumber > lineCount) { + selectToViewLineNumber = lineCount; + selectToViewColumn = context.viewModel.getLineMaxColumn(selectToViewLineNumber); + } + + return CursorState.fromViewState(cursor.viewState.move( + cursor.modelState.hasSelection(), selectToViewLineNumber, selectToViewColumn, 0 + )); + + } else { + + const endPositionOfSelectionStart = cursor.modelState.selectionStart.getEndPosition(); + return CursorState.fromModelState(cursor.modelState.move( + cursor.modelState.hasSelection(), endPositionOfSelectionStart.lineNumber, endPositionOfSelectionStart.column, 0 + )); + + } + } + + public static word(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition): CursorState { + const position = context.model.validatePosition(_position); + return CursorState.fromModelState(WordOperations.word(context.config, context.model, cursor.modelState, inSelectionMode, position)); + } + + public static cancelSelection(context: CursorContext, cursor: CursorState): CursorState { + if (!cursor.modelState.hasSelection()) { + return new CursorState(cursor.modelState, cursor.viewState); + } + + const lineNumber = cursor.viewState.position.lineNumber; + const column = cursor.viewState.position.column; + + return CursorState.fromViewState(new SingleCursorState( + new Range(lineNumber, column, lineNumber, column), 0, + new Position(lineNumber, column), 0 + )); + } + + public static moveTo(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): CursorState { + const position = context.model.validatePosition(_position); + const viewPosition = ( + _viewPosition + ? context.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position) + : context.convertModelPositionToViewPosition(position) + ); + return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, viewPosition.lineNumber, viewPosition.column, 0)); + } + + public static move(context: CursorContext, cursors: CursorState[], args: CursorMove.ParsedArguments): CursorState[] { + const inSelectionMode = args.select; + const value = args.value; + + switch (args.direction) { + case CursorMove.Direction.Left: { + if (args.unit === CursorMove.Unit.HalfLine) { + // Move left by half the current line length + return this._moveHalfLineLeft(context, cursors, inSelectionMode); + } else { + // Move left by `moveParams.value` columns + return this._moveLeft(context, cursors, inSelectionMode, value); + } + } + case CursorMove.Direction.Right: { + if (args.unit === CursorMove.Unit.HalfLine) { + // Move right by half the current line length + return this._moveHalfLineRight(context, cursors, inSelectionMode); + } else { + // Move right by `moveParams.value` columns + return this._moveRight(context, cursors, inSelectionMode, value); + } + } + case CursorMove.Direction.Up: { + if (args.unit === CursorMove.Unit.WrappedLine) { + // Move up by view lines + return this._moveUpByViewLines(context, cursors, inSelectionMode, value); + } else { + // Move up by model lines + return this._moveUpByModelLines(context, cursors, inSelectionMode, value); + } + } + case CursorMove.Direction.Down: { + if (args.unit === CursorMove.Unit.WrappedLine) { + // Move down by view lines + return this._moveDownByViewLines(context, cursors, inSelectionMode, value); + } else { + // Move down by model lines + return this._moveDownByModelLines(context, cursors, inSelectionMode, value); + } + } + case CursorMove.Direction.WrappedLineStart: { + // Move to the beginning of the current view line + return this._moveToViewMinColumn(context, cursors, inSelectionMode); + } + case CursorMove.Direction.WrappedLineFirstNonWhitespaceCharacter: { + // Move to the first non-whitespace column of the current view line + return this._moveToViewFirstNonWhitespaceColumn(context, cursors, inSelectionMode); + } + case CursorMove.Direction.WrappedLineColumnCenter: { + // Move to the "center" of the current view line + return this._moveToViewCenterColumn(context, cursors, inSelectionMode); + } + case CursorMove.Direction.WrappedLineEnd: { + // Move to the end of the current view line + return this._moveToViewMaxColumn(context, cursors, inSelectionMode); + } + case CursorMove.Direction.WrappedLineLastNonWhitespaceCharacter: { + // Move to the last non-whitespace column of the current view line + return this._moveToViewLastNonWhitespaceColumn(context, cursors, inSelectionMode); + } + case CursorMove.Direction.ViewPortTop: { + // Move to the nth line start in the viewport (from the top) + const cursor = cursors[0]; + const visibleModelRange = context.getCompletelyVisibleModelRange(); + const modelLineNumber = this._firstLineNumberInRange(context.model, visibleModelRange, value); + const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber); + return [this._moveToModelPosition(context, cursor, inSelectionMode, modelLineNumber, modelColumn)]; + } + case CursorMove.Direction.ViewPortBottom: { + // Move to the nth line start in the viewport (from the bottom) + const cursor = cursors[0]; + const visibleModelRange = context.getCompletelyVisibleModelRange(); + const modelLineNumber = this._lastLineNumberInRange(context.model, visibleModelRange, value); + const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber); + return [this._moveToModelPosition(context, cursor, inSelectionMode, modelLineNumber, modelColumn)]; + } + case CursorMove.Direction.ViewPortCenter: { + // Move to the line start in the viewport center + const cursor = cursors[0]; + const visibleModelRange = context.getCompletelyVisibleModelRange(); + const modelLineNumber = Math.round((visibleModelRange.startLineNumber + visibleModelRange.endLineNumber) / 2); + const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber); + return [this._moveToModelPosition(context, cursor, inSelectionMode, modelLineNumber, modelColumn)]; + } + case CursorMove.Direction.ViewPortIfOutside: { + // Move to a position inside the viewport + const visibleViewRange = context.getCompletelyVisibleViewRange(); + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = this.findPositionInViewportIfOutside(context, cursor, visibleViewRange, inSelectionMode); + } + return result; + } + } + + return null; + } + + + public static findPositionInViewportIfOutside(context: CursorContext, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): CursorState { + let viewLineNumber = cursor.viewState.position.lineNumber; + + if (visibleViewRange.startLineNumber <= viewLineNumber && viewLineNumber <= visibleViewRange.endLineNumber - 1) { + // Nothing to do, cursor is in viewport + return new CursorState(cursor.modelState, cursor.viewState); + + } else { + if (viewLineNumber > visibleViewRange.endLineNumber - 1) { + viewLineNumber = visibleViewRange.endLineNumber - 1; + } + if (viewLineNumber < visibleViewRange.startLineNumber) { + viewLineNumber = visibleViewRange.startLineNumber; + } + const viewColumn = context.viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber); + return this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + } + } + + /** + * Find the nth line start included in the range (from the start). + */ + private static _firstLineNumberInRange(model: ICursorSimpleModel, range: Range, count: number): number { + let startLineNumber = range.startLineNumber; + if (range.startColumn !== model.getLineMinColumn(startLineNumber)) { + // Move on to the second line if the first line start is not included in the range + startLineNumber++; + } + + return Math.min(range.endLineNumber, startLineNumber + count - 1); + } + + /** + * Find the nth line start included in the range (from the end). + */ + private static _lastLineNumberInRange(model: ICursorSimpleModel, range: Range, count: number): number { + let startLineNumber = range.startLineNumber; + if (range.startColumn !== model.getLineMinColumn(startLineNumber)) { + // Move on to the second line if the first line start is not included in the range + startLineNumber++; + } + + return Math.max(startLineNumber, range.endLineNumber - count + 1); + } + + private static _moveLeft(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = CursorState.fromViewState(MoveOperations.moveLeft(context.config, context.viewModel, cursor.viewState, inSelectionMode, noOfColumns)); + } + return result; + } + + private static _moveHalfLineLeft(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + const viewLineNumber = cursor.viewState.position.lineNumber; + const halfLine = Math.round(context.viewModel.getLineContent(viewLineNumber).length / 2); + result[i] = CursorState.fromViewState(MoveOperations.moveLeft(context.config, context.viewModel, cursor.viewState, inSelectionMode, halfLine)); + } + return result; + } + + private static _moveRight(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = CursorState.fromViewState(MoveOperations.moveRight(context.config, context.viewModel, cursor.viewState, inSelectionMode, noOfColumns)); + } + return result; + } + + private static _moveHalfLineRight(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + const viewLineNumber = cursor.viewState.position.lineNumber; + const halfLine = Math.round(context.viewModel.getLineContent(viewLineNumber).length / 2); + result[i] = CursorState.fromViewState(MoveOperations.moveRight(context.config, context.viewModel, cursor.viewState, inSelectionMode, halfLine)); + } + return result; + } + + private static _moveDownByViewLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = CursorState.fromViewState(MoveOperations.moveDown(context.config, context.viewModel, cursor.viewState, inSelectionMode, linesCount)); + } + return result; + } + + private static _moveDownByModelLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = CursorState.fromModelState(MoveOperations.moveDown(context.config, context.model, cursor.modelState, inSelectionMode, linesCount)); + } + return result; + } + + private static _moveUpByViewLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = CursorState.fromViewState(MoveOperations.moveUp(context.config, context.viewModel, cursor.viewState, inSelectionMode, linesCount)); + } + return result; + } + + private static _moveUpByModelLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + result[i] = CursorState.fromModelState(MoveOperations.moveUp(context.config, context.model, cursor.modelState, inSelectionMode, linesCount)); + } + return result; + } + + private static _moveToViewPosition(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, toViewLineNumber: number, toViewColumn: number): CursorState { + return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, toViewLineNumber, toViewColumn, 0)); + } + + private static _moveToModelPosition(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, toModelLineNumber: number, toModelColumn: number): CursorState { + return CursorState.fromModelState(cursor.modelState.move(inSelectionMode, toModelLineNumber, toModelColumn, 0)); + } + + private static _moveToViewMinColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + const viewLineNumber = cursor.viewState.position.lineNumber; + const viewColumn = context.viewModel.getLineMinColumn(viewLineNumber); + result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + } + return result; + } + + private static _moveToViewFirstNonWhitespaceColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + const viewLineNumber = cursor.viewState.position.lineNumber; + const viewColumn = context.viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber); + result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + } + return result; + } + + private static _moveToViewCenterColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + const viewLineNumber = cursor.viewState.position.lineNumber; + const viewColumn = Math.round((context.viewModel.getLineMaxColumn(viewLineNumber) + context.viewModel.getLineMinColumn(viewLineNumber)) / 2); + result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + } + return result; + } + + private static _moveToViewMaxColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + const viewLineNumber = cursor.viewState.position.lineNumber; + const viewColumn = context.viewModel.getLineMaxColumn(viewLineNumber); + result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + } + return result; + } + + private static _moveToViewLastNonWhitespaceColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): CursorState[] { + let result: CursorState[] = []; + for (let i = 0, len = cursors.length; i < len; i++) { + const cursor = cursors[i]; + const viewLineNumber = cursor.viewState.position.lineNumber; + const viewColumn = context.viewModel.getLineLastNonWhitespaceColumn(viewLineNumber); + result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + } + return result; + } +} + +export namespace CursorMove { + + const isCursorMoveArgs = function (arg: any): boolean { + if (!types.isObject(arg)) { + return false; + } + + let cursorMoveArg: RawArguments = arg; + + if (!types.isString(cursorMoveArg.to)) { + return false; + } + + if (!types.isUndefined(cursorMoveArg.select) && !types.isBoolean(cursorMoveArg.select)) { + return false; + } + + if (!types.isUndefined(cursorMoveArg.by) && !types.isString(cursorMoveArg.by)) { + return false; + } + + if (!types.isUndefined(cursorMoveArg.value) && !types.isNumber(cursorMoveArg.value)) { + return false; + } + + return true; + }; + + export const description = { + description: 'Move cursor to a logical position in the view', + args: [ + { + name: 'Cursor move argument object', + description: `Property-value pairs that can be passed through this argument: + * 'to': A mandatory logical position value providing where to move the cursor. + \`\`\` + 'left', 'right', 'up', 'down' + 'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter' + 'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter', + 'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside' + \`\`\` + * 'by': Unit to move. Default is computed based on 'to' value. + \`\`\` + 'line', 'wrappedLine', 'character', 'halfLine' + \`\`\` + * 'value': Number of units to move. Default is '1'. + * 'select': If 'true' makes the selection. Default is 'false'. + `, + constraint: isCursorMoveArgs + } + ] + }; + + /** + * Positions in the view for cursor move command. + */ + export const RawDirection = { + Left: 'left', + Right: 'right', + Up: 'up', + Down: 'down', + + WrappedLineStart: 'wrappedLineStart', + WrappedLineFirstNonWhitespaceCharacter: 'wrappedLineFirstNonWhitespaceCharacter', + WrappedLineColumnCenter: 'wrappedLineColumnCenter', + WrappedLineEnd: 'wrappedLineEnd', + WrappedLineLastNonWhitespaceCharacter: 'wrappedLineLastNonWhitespaceCharacter', + + ViewPortTop: 'viewPortTop', + ViewPortCenter: 'viewPortCenter', + ViewPortBottom: 'viewPortBottom', + + ViewPortIfOutside: 'viewPortIfOutside' + }; + + /** + * Units for Cursor move 'by' argument + */ + export const RawUnit = { + Line: 'line', + WrappedLine: 'wrappedLine', + Character: 'character', + HalfLine: 'halfLine' + }; + + /** + * Arguments for Cursor move command + */ + export interface RawArguments { + to: string; + select?: boolean; + by?: string; + value?: number; + }; + + export function parse(args: RawArguments): ParsedArguments { + if (!args.to) { + // illegal arguments + return null; + } + + let direction: Direction; + switch (args.to) { + case RawDirection.Left: + direction = Direction.Left; + break; + case RawDirection.Right: + direction = Direction.Right; + break; + case RawDirection.Up: + direction = Direction.Up; + break; + case RawDirection.Down: + direction = Direction.Down; + break; + case RawDirection.WrappedLineStart: + direction = Direction.WrappedLineStart; + break; + case RawDirection.WrappedLineFirstNonWhitespaceCharacter: + direction = Direction.WrappedLineFirstNonWhitespaceCharacter; + break; + case RawDirection.WrappedLineColumnCenter: + direction = Direction.WrappedLineColumnCenter; + break; + case RawDirection.WrappedLineEnd: + direction = Direction.WrappedLineEnd; + break; + case RawDirection.WrappedLineLastNonWhitespaceCharacter: + direction = Direction.WrappedLineLastNonWhitespaceCharacter; + break; + case RawDirection.ViewPortTop: + direction = Direction.ViewPortTop; + break; + case RawDirection.ViewPortBottom: + direction = Direction.ViewPortBottom; + break; + case RawDirection.ViewPortCenter: + direction = Direction.ViewPortCenter; + break; + case RawDirection.ViewPortIfOutside: + direction = Direction.ViewPortIfOutside; + break; + default: + // illegal arguments + return null; + } + + let unit = Unit.None; + switch (args.by) { + case RawUnit.Line: + unit = Unit.Line; + break; + case RawUnit.WrappedLine: + unit = Unit.WrappedLine; + break; + case RawUnit.Character: + unit = Unit.Character; + break; + case RawUnit.HalfLine: + unit = Unit.HalfLine; + break; + } + + return { + direction: direction, + unit: unit, + select: (!!args.select), + value: (args.value || 1) + }; + } + + export interface ParsedArguments { + direction: Direction; + unit: Unit; + select: boolean; + value: number; + }; + + export const enum Direction { + Left, + Right, + Up, + Down, + + WrappedLineStart, + WrappedLineFirstNonWhitespaceCharacter, + WrappedLineColumnCenter, + WrappedLineEnd, + WrappedLineLastNonWhitespaceCharacter, + + ViewPortTop, + ViewPortCenter, + ViewPortBottom, + + ViewPortIfOutside, + }; + + export const enum Unit { + None, + Line, + WrappedLine, + Character, + HalfLine, + }; + +} diff --git a/src/vs/editor/common/controller/cursorMoveOperations.ts b/src/vs/editor/common/controller/cursorMoveOperations.ts new file mode 100644 index 0000000000..dddbe9d8ce --- /dev/null +++ b/src/vs/editor/common/controller/cursorMoveOperations.ts @@ -0,0 +1,246 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { SingleCursorState, CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; + +export class CursorPosition { + _cursorPositionBrand: void; + + public readonly lineNumber: number; + public readonly column: number; + public readonly leftoverVisibleColumns: number; + + constructor(lineNumber: number, column: number, leftoverVisibleColumns: number) { + this.lineNumber = lineNumber; + this.column = column; + this.leftoverVisibleColumns = leftoverVisibleColumns; + } +} + +export class MoveOperations { + + public static left(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition { + + if (column > model.getLineMinColumn(lineNumber)) { + if (CursorColumns.isLowSurrogate(model, lineNumber, column - 2)) { + // character before column is a low surrogate + column = column - 2; + } else { + column = column - 1; + } + } else if (lineNumber > 1) { + lineNumber = lineNumber - 1; + column = model.getLineMaxColumn(lineNumber); + } + + return new CursorPosition(lineNumber, column, 0); + } + + public static moveLeft(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean, noOfColumns: number): SingleCursorState { + let lineNumber: number, + column: number; + + if (cursor.hasSelection() && !inSelectionMode) { + // If we are in selection mode, move left without selection cancels selection and puts cursor at the beginning of the selection + lineNumber = cursor.selection.startLineNumber; + column = cursor.selection.startColumn; + } else { + let r = MoveOperations.left(config, model, cursor.position.lineNumber, cursor.position.column - (noOfColumns - 1)); + lineNumber = r.lineNumber; + column = r.column; + } + + return cursor.move(inSelectionMode, lineNumber, column, 0); + } + + public static right(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition { + + if (column < model.getLineMaxColumn(lineNumber)) { + if (CursorColumns.isHighSurrogate(model, lineNumber, column - 1)) { + // character after column is a high surrogate + column = column + 2; + } else { + column = column + 1; + } + } else if (lineNumber < model.getLineCount()) { + lineNumber = lineNumber + 1; + column = model.getLineMinColumn(lineNumber); + } + + return new CursorPosition(lineNumber, column, 0); + } + + public static moveRight(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean, noOfColumns: number): SingleCursorState { + let lineNumber: number, + column: number; + + if (cursor.hasSelection() && !inSelectionMode) { + // If we are in selection mode, move right without selection cancels selection and puts cursor at the end of the selection + lineNumber = cursor.selection.endLineNumber; + column = cursor.selection.endColumn; + } else { + let r = MoveOperations.right(config, model, cursor.position.lineNumber, cursor.position.column + (noOfColumns - 1)); + lineNumber = r.lineNumber; + column = r.column; + } + + return cursor.move(inSelectionMode, lineNumber, column, 0); + } + + public static down(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnLastLine: boolean): CursorPosition { + const currentVisibleColumn = CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize) + leftoverVisibleColumns; + + lineNumber = lineNumber + count; + var lineCount = model.getLineCount(); + if (lineNumber > lineCount) { + lineNumber = lineCount; + if (allowMoveOnLastLine) { + column = model.getLineMaxColumn(lineNumber); + } else { + column = Math.min(model.getLineMaxColumn(lineNumber), column); + if (CursorColumns.isInsideSurrogatePair(model, lineNumber, column)) { + column = column - 1; + } + } + } else { + column = CursorColumns.columnFromVisibleColumn2(config, model, lineNumber, currentVisibleColumn); + if (CursorColumns.isInsideSurrogatePair(model, lineNumber, column)) { + column = column - 1; + } + } + + leftoverVisibleColumns = currentVisibleColumn - CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize); + + return new CursorPosition(lineNumber, column, leftoverVisibleColumns); + } + + public static moveDown(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean, linesCount: number): SingleCursorState { + let lineNumber: number, + column: number; + + if (cursor.hasSelection() && !inSelectionMode) { + // If we are in selection mode, move down acts relative to the end of selection + lineNumber = cursor.selection.endLineNumber; + column = cursor.selection.endColumn; + } else { + lineNumber = cursor.position.lineNumber; + column = cursor.position.column; + } + + let r = MoveOperations.down(config, model, lineNumber, column, cursor.leftoverVisibleColumns, linesCount, true); + + return cursor.move(inSelectionMode, r.lineNumber, r.column, r.leftoverVisibleColumns); + } + + public static translateDown(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState): SingleCursorState { + let selection = cursor.selection; + + let selectionStart = MoveOperations.down(config, model, selection.selectionStartLineNumber, selection.selectionStartColumn, cursor.selectionStartLeftoverVisibleColumns, 1, false); + let position = MoveOperations.down(config, model, selection.positionLineNumber, selection.positionColumn, cursor.leftoverVisibleColumns, 1, false); + + return new SingleCursorState( + new Range(selectionStart.lineNumber, selectionStart.column, selectionStart.lineNumber, selectionStart.column), + selectionStart.leftoverVisibleColumns, + new Position(position.lineNumber, position.column), + position.leftoverVisibleColumns + ); + } + + public static up(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnFirstLine: boolean): CursorPosition { + const currentVisibleColumn = CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize) + leftoverVisibleColumns; + + lineNumber = lineNumber - count; + if (lineNumber < 1) { + lineNumber = 1; + if (allowMoveOnFirstLine) { + column = model.getLineMinColumn(lineNumber); + } else { + column = Math.min(model.getLineMaxColumn(lineNumber), column); + if (CursorColumns.isInsideSurrogatePair(model, lineNumber, column)) { + column = column - 1; + } + } + } else { + column = CursorColumns.columnFromVisibleColumn2(config, model, lineNumber, currentVisibleColumn); + if (CursorColumns.isInsideSurrogatePair(model, lineNumber, column)) { + column = column - 1; + } + } + + leftoverVisibleColumns = currentVisibleColumn - CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize); + + return new CursorPosition(lineNumber, column, leftoverVisibleColumns); + } + + public static moveUp(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean, linesCount: number): SingleCursorState { + let lineNumber: number, + column: number; + + if (cursor.hasSelection() && !inSelectionMode) { + // If we are in selection mode, move up acts relative to the beginning of selection + lineNumber = cursor.selection.startLineNumber; + column = cursor.selection.startColumn; + } else { + lineNumber = cursor.position.lineNumber; + column = cursor.position.column; + } + + let r = MoveOperations.up(config, model, lineNumber, column, cursor.leftoverVisibleColumns, linesCount, true); + + return cursor.move(inSelectionMode, r.lineNumber, r.column, r.leftoverVisibleColumns); + } + + public static translateUp(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState): SingleCursorState { + + let selection = cursor.selection; + + let selectionStart = MoveOperations.up(config, model, selection.selectionStartLineNumber, selection.selectionStartColumn, cursor.selectionStartLeftoverVisibleColumns, 1, false); + let position = MoveOperations.up(config, model, selection.positionLineNumber, selection.positionColumn, cursor.leftoverVisibleColumns, 1, false); + + return new SingleCursorState( + new Range(selectionStart.lineNumber, selectionStart.column, selectionStart.lineNumber, selectionStart.column), + selectionStart.leftoverVisibleColumns, + new Position(position.lineNumber, position.column), + position.leftoverVisibleColumns + ); + } + + public static moveToBeginningOfLine(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState { + let lineNumber = cursor.position.lineNumber; + let minColumn = model.getLineMinColumn(lineNumber); + let firstNonBlankColumn = model.getLineFirstNonWhitespaceColumn(lineNumber) || minColumn; + + let column: number; + + let relevantColumnNumber = cursor.position.column; + if (relevantColumnNumber === firstNonBlankColumn) { + column = minColumn; + } else { + column = firstNonBlankColumn; + } + + return cursor.move(inSelectionMode, lineNumber, column, 0); + } + + public static moveToEndOfLine(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState { + let lineNumber = cursor.position.lineNumber; + let maxColumn = model.getLineMaxColumn(lineNumber); + return cursor.move(inSelectionMode, lineNumber, maxColumn, 0); + } + + public static moveToBeginningOfBuffer(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState { + return cursor.move(inSelectionMode, 1, 1, 0); + } + + public static moveToEndOfBuffer(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState { + let lastLineNumber = model.getLineCount(); + let lastColumn = model.getLineMaxColumn(lastLineNumber); + + return cursor.move(inSelectionMode, lastLineNumber, lastColumn, 0); + } +} diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts new file mode 100644 index 0000000000..e6118156ed --- /dev/null +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -0,0 +1,775 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { ReplaceCommand, ReplaceCommandWithoutChangingPosition, ReplaceCommandWithOffsetCursorState } from 'vs/editor/common/commands/replaceCommand'; +import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult } from 'vs/editor/common/controller/cursorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { ICommand, ITokenizedModel } from 'vs/editor/common/editorCommon'; +import * as strings from 'vs/base/common/strings'; +import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; +import { Selection } from 'vs/editor/common/core/selection'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { SurroundSelectionCommand } from 'vs/editor/common/commands/surroundSelectionCommand'; +import { IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; +import { getMapForWordSeparators, WordCharacterClass } from 'vs/editor/common/controller/wordCharacterClassifier'; + +export class TypeOperations { + + public static indent(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): ICommand[] { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + commands[i] = new ShiftCommand(selections[i], { + isUnshift: false, + tabSize: config.tabSize, + oneIndent: config.oneIndent, + useTabStops: config.useTabStops + }); + } + return commands; + } + + public static outdent(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): ICommand[] { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + commands[i] = new ShiftCommand(selections[i], { + isUnshift: true, + tabSize: config.tabSize, + oneIndent: config.oneIndent, + useTabStops: config.useTabStops + }); + } + return commands; + } + + public static shiftIndent(config: CursorConfiguration, indentation: string, count?: number): string { + count = count || 1; + let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + count, config.tabSize); + let newIndentation = ''; + for (let i = 0; i < desiredIndentCount; i++) { + newIndentation += '\t'; + } + + return newIndentation; + } + + public static unshiftIndent(config: CursorConfiguration, indentation: string, count?: number): string { + count = count || 1; + let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + count, config.tabSize); + let newIndentation = ''; + for (let i = 0; i < desiredIndentCount; i++) { + newIndentation += '\t'; + } + + return newIndentation; + } + + private static _distributedPaste(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], text: string[]): EditOperationResult { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + commands[i] = new ReplaceCommand(selections[i], text[i]); + } + return new EditOperationResult(commands, { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: true + }); + } + + private static _simplePaste(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], text: string, pasteOnNewLine: boolean): EditOperationResult { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + let position = selection.getPosition(); + + if (pasteOnNewLine && text.indexOf('\n') !== text.length - 1) { + pasteOnNewLine = false; + } + if (pasteOnNewLine && selection.startLineNumber !== selection.endLineNumber) { + pasteOnNewLine = false; + } + if (pasteOnNewLine && selection.startColumn === model.getLineMinColumn(selection.startLineNumber) && selection.endColumn === model.getLineMaxColumn(selection.startLineNumber)) { + pasteOnNewLine = false; + } + + if (pasteOnNewLine) { + // Paste entire line at the beginning of line + let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, 1); + commands[i] = new ReplaceCommand(typeSelection, text); + } else { + commands[i] = new ReplaceCommand(selection, text); + } + } + return new EditOperationResult(commands, { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: true + }); + } + + private static _distributePasteToCursors(selections: Selection[], pasteOnNewLine: boolean, text: string): string[] { + if (pasteOnNewLine) { + return null; + } + + if (selections.length === 1) { + return null; + } + + for (let i = 0; i < selections.length; i++) { + if (selections[i].startLineNumber !== selections[i].endLineNumber) { + return null; + } + } + + let pastePieces = text.split(/\r\n|\r|\n/); + if (pastePieces.length !== selections.length) { + return null; + } + + return pastePieces; + } + + public static paste(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], pasteOnNewLine: boolean, text: string): EditOperationResult { + const distributedPaste = this._distributePasteToCursors(selections, pasteOnNewLine, text); + + if (distributedPaste) { + selections = selections.sort(Range.compareRangesUsingStarts); + return this._distributedPaste(config, model, selections, distributedPaste); + } else { + return this._simplePaste(config, model, selections, text, pasteOnNewLine); + } + } + + private static _goodIndentForLine(config: CursorConfiguration, model: ITokenizedModel, lineNumber: number): string { + let action; + let indentation; + let expectedIndentAction = LanguageConfigurationRegistry.getInheritIndentForLine(model, lineNumber, false); + + if (expectedIndentAction) { + action = expectedIndentAction.action; + indentation = expectedIndentAction.indentation; + } else if (lineNumber > 1) { + let lastLineNumber = lineNumber - 1; + for (lastLineNumber = lineNumber - 1; lastLineNumber >= 1; lastLineNumber--) { + let lineText = model.getLineContent(lastLineNumber); + let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineText); + if (nonWhitespaceIdx >= 0) { + break; + } + } + + if (lastLineNumber < 1) { + // No previous line with content found + return null; + } + + let maxColumn = model.getLineMaxColumn(lastLineNumber); + let expectedEnterAction = LanguageConfigurationRegistry.getEnterAction(model, new Range(lastLineNumber, maxColumn, lastLineNumber, maxColumn)); + if (expectedEnterAction) { + indentation = expectedEnterAction.indentation; + action = expectedEnterAction.enterAction; + if (action) { + indentation += action.appendText; + } + } + } + + if (action) { + if (action === IndentAction.Indent) { + indentation = TypeOperations.shiftIndent(config, indentation); + } + + if (action === IndentAction.Outdent) { + indentation = TypeOperations.unshiftIndent(config, indentation); + } + + indentation = config.normalizeIndentation(indentation); + } + + if (!indentation) { + return null; + } + + return indentation; + } + + private static _replaceJumpToNextIndent(config: CursorConfiguration, model: ICursorSimpleModel, selection: Selection, insertsAutoWhitespace: boolean): ReplaceCommand { + let typeText = ''; + + let position = selection.getStartPosition(); + if (config.insertSpaces) { + let visibleColumnFromColumn = CursorColumns.visibleColumnFromColumn2(config, model, position); + let tabSize = config.tabSize; + let spacesCnt = tabSize - (visibleColumnFromColumn % tabSize); + for (let i = 0; i < spacesCnt; i++) { + typeText += ' '; + } + } else { + typeText = '\t'; + } + + return new ReplaceCommand(selection, typeText, insertsAutoWhitespace); + } + + public static tab(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[]): ICommand[] { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + + if (selection.isEmpty()) { + + let lineText = model.getLineContent(selection.startLineNumber); + + if (/^\s*$/.test(lineText) && model.isCheapToTokenize(selection.startLineNumber)) { + let goodIndent = this._goodIndentForLine(config, model, selection.startLineNumber); + goodIndent = goodIndent || '\t'; + let possibleTypeText = config.normalizeIndentation(goodIndent); + if (!strings.startsWith(lineText, possibleTypeText)) { + commands[i] = new ReplaceCommand(new Range(selection.startLineNumber, 1, selection.startLineNumber, lineText.length + 1), possibleTypeText, true); + continue; + } + } + + commands[i] = this._replaceJumpToNextIndent(config, model, selection, true); + } else { + if (selection.startLineNumber === selection.endLineNumber) { + let lineMaxColumn = model.getLineMaxColumn(selection.startLineNumber); + if (selection.startColumn !== 1 || selection.endColumn !== lineMaxColumn) { + // This is a single line selection that is not the entire line + commands[i] = this._replaceJumpToNextIndent(config, model, selection, false); + continue; + } + } + + commands[i] = new ShiftCommand(selection, { + isUnshift: false, + tabSize: config.tabSize, + oneIndent: config.oneIndent, + useTabStops: config.useTabStops + }); + } + } + return commands; + } + + public static replacePreviousChar(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], txt: string, replaceCharCnt: number): EditOperationResult { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + if (!selection.isEmpty()) { + // looks like https://github.com/Microsoft/vscode/issues/2773 + // where a cursor operation occurred before a canceled composition + // => ignore composition + commands[i] = null; + continue; + } + let pos = selection.getPosition(); + let startColumn = Math.max(1, pos.column - replaceCharCnt); + let range = new Range(pos.lineNumber, startColumn, pos.lineNumber, pos.column); + commands[i] = new ReplaceCommand(range, txt); + } + return new EditOperationResult(commands, { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false + }); + } + + private static _typeCommand(range: Range, text: string, keepPosition: boolean): ICommand { + if (keepPosition) { + return new ReplaceCommandWithoutChangingPosition(range, text, true); + } else { + return new ReplaceCommand(range, text, true); + } + } + + private static _enter(config: CursorConfiguration, model: ITokenizedModel, keepPosition: boolean, range: Range): ICommand { + if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) { + let lineText = model.getLineContent(range.startLineNumber); + let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1); + return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition); + } + + let r = LanguageConfigurationRegistry.getEnterAction(model, range); + if (r) { + let enterAction = r.enterAction; + let indentation = r.indentation; + + if (enterAction.indentAction === IndentAction.None) { + // Nothing special + return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation + enterAction.appendText), keepPosition); + + } else if (enterAction.indentAction === IndentAction.Indent) { + // Indent once + return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation + enterAction.appendText), keepPosition); + + } else if (enterAction.indentAction === IndentAction.IndentOutdent) { + // Ultra special + let normalIndent = config.normalizeIndentation(indentation); + let increasedIndent = config.normalizeIndentation(indentation + enterAction.appendText); + + let typeText = '\n' + increasedIndent + '\n' + normalIndent; + + if (keepPosition) { + return new ReplaceCommandWithoutChangingPosition(range, typeText, true); + } else { + return new ReplaceCommandWithOffsetCursorState(range, typeText, -1, increasedIndent.length - normalIndent.length, true); + } + } else if (enterAction.indentAction === IndentAction.Outdent) { + let actualIndentation = TypeOperations.unshiftIndent(config, indentation); + return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(actualIndentation + enterAction.appendText), keepPosition); + } + } + + // no enter rules applied, we should check indentation rules then. + let ir = LanguageConfigurationRegistry.getIndentForEnter(model, range, { + unshiftIndent: (indent) => { + return TypeOperations.unshiftIndent(config, indent); + }, + shiftIndent: (indent) => { + return TypeOperations.shiftIndent(config, indent); + }, + normalizeIndentation: (indent) => { + return config.normalizeIndentation(indent); + } + }, config.autoIndent); + + let lineText = model.getLineContent(range.startLineNumber); + let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1); + + if (ir) { + let oldEndViewColumn = CursorColumns.visibleColumnFromColumn2(config, model, range.getEndPosition()); + let oldEndColumn = range.endColumn; + + let beforeText = '\n'; + if (indentation !== config.normalizeIndentation(ir.beforeEnter)) { + beforeText = config.normalizeIndentation(ir.beforeEnter) + lineText.substring(indentation.length, range.startColumn - 1) + '\n'; + range = new Range(range.startLineNumber, 1, range.endLineNumber, range.endColumn); + } + + let newLineContent = model.getLineContent(range.endLineNumber); + let firstNonWhitespace = strings.firstNonWhitespaceIndex(newLineContent); + if (firstNonWhitespace >= 0) { + range = range.setEndPosition(range.endLineNumber, Math.max(range.endColumn, firstNonWhitespace + 1)); + } else { + range = range.setEndPosition(range.endLineNumber, model.getLineMaxColumn(range.endLineNumber)); + } + + if (keepPosition) { + return new ReplaceCommandWithoutChangingPosition(range, beforeText + config.normalizeIndentation(ir.afterEnter), true); + } else { + let offset = 0; + if (oldEndColumn <= firstNonWhitespace + 1) { + if (!config.insertSpaces) { + oldEndViewColumn = Math.ceil(oldEndViewColumn / config.tabSize); + } + offset = Math.min(oldEndViewColumn + 1 - config.normalizeIndentation(ir.afterEnter).length - 1, 0); + } + return new ReplaceCommandWithOffsetCursorState(range, beforeText + config.normalizeIndentation(ir.afterEnter), 0, offset, true); + } + + } else { + return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition); + } + } + + private static _isAutoIndentType(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[]): boolean { + if (!config.autoIndent) { + return false; + } + + for (let i = 0, len = selections.length; i < len; i++) { + if (!model.isCheapToTokenize(selections[i].getEndPosition().lineNumber)) { + return false; + } + } + + return true; + } + + private static _runAutoIndentType(config: CursorConfiguration, model: ITokenizedModel, range: Range, ch: string): ICommand { + let currentIndentation = LanguageConfigurationRegistry.getIndentationAtPosition(model, range.startLineNumber, range.startColumn); + let actualIndentation = LanguageConfigurationRegistry.getIndentActionForType(model, range, ch, { + shiftIndent: (indentation) => { + return TypeOperations.shiftIndent(config, indentation); + }, + unshiftIndent: (indentation) => { + return TypeOperations.unshiftIndent(config, indentation); + }, + }); + + if (actualIndentation === null) { + return null; + } + + if (actualIndentation !== config.normalizeIndentation(currentIndentation)) { + let firstNonWhitespace = model.getLineFirstNonWhitespaceColumn(range.startLineNumber); + if (firstNonWhitespace === 0) { + return TypeOperations._typeCommand( + new Range(range.startLineNumber, 0, range.endLineNumber, range.endColumn), + config.normalizeIndentation(actualIndentation) + ch, + false + ); + } else { + return TypeOperations._typeCommand( + new Range(range.startLineNumber, 0, range.endLineNumber, range.endColumn), + config.normalizeIndentation(actualIndentation) + + model.getLineContent(range.startLineNumber).substring(firstNonWhitespace - 1, range.startColumn - 1) + ch, + false + ); + } + } + + return null; + } + + private static _isAutoClosingCloseCharType(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): boolean { + if (!config.autoClosingBrackets || !config.autoClosingPairsClose.hasOwnProperty(ch)) { + return false; + } + + const isEqualPair = (ch === config.autoClosingPairsClose[ch]); + + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + + if (!selection.isEmpty()) { + return false; + } + + const position = selection.getPosition(); + const lineText = model.getLineContent(position.lineNumber); + const afterCharacter = lineText.charAt(position.column - 1); + + if (afterCharacter !== ch) { + return false; + } + + if (isEqualPair) { + const lineTextBeforeCursor = lineText.substr(0, position.column - 1); + const chCntBefore = this._countNeedlesInHaystack(lineTextBeforeCursor, ch); + if (chCntBefore % 2 === 0) { + return false; + } + } + } + + return true; + } + + private static _countNeedlesInHaystack(haystack: string, needle: string): number { + let cnt = 0; + let lastIndex = -1; + while ((lastIndex = haystack.indexOf(needle, lastIndex + 1)) !== -1) { + cnt++; + } + return cnt; + } + + private static _runAutoClosingCloseCharType(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + const position = selection.getPosition(); + const typeSelection = new Range(position.lineNumber, position.column, position.lineNumber, position.column + 1); + commands[i] = new ReplaceCommand(typeSelection, ch); + } + return new EditOperationResult(commands, { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false + }); + } + + private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): boolean { + if (!config.autoClosingBrackets || !config.autoClosingPairsOpen.hasOwnProperty(ch)) { + return false; + } + + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + if (!selection.isEmpty()) { + return false; + } + + const position = selection.getPosition(); + const lineText = model.getLineContent(position.lineNumber); + + // Do not auto-close ' or " after a word character + if ((ch === '\'' || ch === '"') && position.column > 1) { + const wordSeparators = getMapForWordSeparators(config.wordSeparators); + const characterBeforeCode = lineText.charCodeAt(position.column - 2); + const characterBeforeType = wordSeparators.get(characterBeforeCode); + if (characterBeforeType === WordCharacterClass.Regular) { + return false; + } + } + + // Only consider auto closing the pair if a space follows or if another autoclosed pair follows + const characterAfter = lineText.charAt(position.column - 1); + if (characterAfter) { + const thisBraceIsSymmetric = (config.autoClosingPairsOpen[ch] === ch); + + let isBeforeCloseBrace = false; + for (let otherCloseBrace in config.autoClosingPairsClose) { + const otherBraceIsSymmetric = (config.autoClosingPairsOpen[otherCloseBrace] === otherCloseBrace); + if (!thisBraceIsSymmetric && otherBraceIsSymmetric) { + continue; + } + if (characterAfter === otherCloseBrace) { + isBeforeCloseBrace = true; + break; + } + } + if (!isBeforeCloseBrace && !/\s/.test(characterAfter)) { + return false; + } + } + + if (!model.isCheapToTokenize(position.lineNumber)) { + // Do not force tokenization + return false; + } + + model.forceTokenization(position.lineNumber); + const lineTokens = model.getLineTokens(position.lineNumber); + + let shouldAutoClosePair = false; + try { + shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column); + } catch (e) { + onUnexpectedError(e); + } + + if (!shouldAutoClosePair) { + return false; + } + } + + return true; + } + + private static _runAutoClosingOpenCharType(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + const closeCharacter = config.autoClosingPairsOpen[ch]; + commands[i] = new ReplaceCommandWithOffsetCursorState(selection, ch + closeCharacter, 0, -closeCharacter.length); + } + return new EditOperationResult(commands, { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: false + }); + } + + private static _isSurroundSelectionType(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): boolean { + if (!config.autoClosingBrackets || !config.surroundingPairs.hasOwnProperty(ch)) { + return false; + } + + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + + if (selection.isEmpty()) { + return false; + } + + let selectionContainsOnlyWhitespace = true; + + for (let lineNumber = selection.startLineNumber; lineNumber <= selection.endLineNumber; lineNumber++) { + const lineText = model.getLineContent(lineNumber); + const startIndex = (lineNumber === selection.startLineNumber ? selection.startColumn - 1 : 0); + const endIndex = (lineNumber === selection.endLineNumber ? selection.endColumn - 1 : lineText.length); + const selectedText = lineText.substring(startIndex, endIndex); + if (/[^ \t]/.test(selectedText)) { + // this selected text contains something other than whitespace + selectionContainsOnlyWhitespace = false; + break; + } + } + + if (selectionContainsOnlyWhitespace) { + return false; + } + } + + return true; + } + + private static _runSurroundSelectionType(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + const selection = selections[i]; + const closeCharacter = config.surroundingPairs[ch]; + commands[i] = new SurroundSelectionCommand(selection, ch, closeCharacter); + } + return new EditOperationResult(commands, { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: true + }); + } + + private static _isTypeInterceptorElectricChar(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[]) { + if (selections.length === 1 && model.isCheapToTokenize(selections[0].getEndPosition().lineNumber)) { + return true; + } + return false; + } + + private static _typeInterceptorElectricChar(config: CursorConfiguration, model: ITokenizedModel, selection: Selection, ch: string): EditOperationResult { + if (!config.electricChars.hasOwnProperty(ch) || !selection.isEmpty()) { + return null; + } + + let position = selection.getPosition(); + model.forceTokenization(position.lineNumber); + let lineTokens = model.getLineTokens(position.lineNumber); + + let electricAction: IElectricAction; + try { + electricAction = LanguageConfigurationRegistry.onElectricCharacter(ch, lineTokens, position.column); + } catch (e) { + onUnexpectedError(e); + } + + if (!electricAction) { + return null; + } + + if (electricAction.appendText) { + const command = new ReplaceCommandWithOffsetCursorState(selection, ch + electricAction.appendText, 0, -electricAction.appendText.length); + return new EditOperationResult([command], { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: true + }); + } + + if (electricAction.matchOpenBracket) { + let endColumn = (lineTokens.getLineContent() + ch).lastIndexOf(electricAction.matchOpenBracket) + 1; + let match = model.findMatchingBracketUp(electricAction.matchOpenBracket, { + lineNumber: position.lineNumber, + column: endColumn + }); + + if (match) { + if (match.startLineNumber === position.lineNumber) { + // matched something on the same line => no change in indentation + return null; + } + let matchLine = model.getLineContent(match.startLineNumber); + let matchLineIndentation = strings.getLeadingWhitespace(matchLine); + let newIndentation = config.normalizeIndentation(matchLineIndentation); + + let lineText = model.getLineContent(position.lineNumber); + let lineFirstNonBlankColumn = model.getLineFirstNonWhitespaceColumn(position.lineNumber) || position.column; + + let prefix = lineText.substring(lineFirstNonBlankColumn - 1, position.column - 1); + let typeText = newIndentation + prefix + ch; + + let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, position.column); + + const command = new ReplaceCommand(typeSelection, typeText); + return new EditOperationResult([command], { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: true + }); + } + } + + return null; + } + + public static typeWithInterceptors(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { + + if (ch === '\n') { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + commands[i] = TypeOperations._enter(config, model, false, selections[i]); + } + return new EditOperationResult(commands, { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: false, + }); + } + + if (this._isAutoIndentType(config, model, selections)) { + let indentCommand = this._runAutoIndentType(config, model, selections[0], ch); + if (indentCommand) { + return new EditOperationResult([indentCommand], { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: false, + }); + } + } + + if (this._isAutoClosingCloseCharType(config, model, selections, ch)) { + return this._runAutoClosingCloseCharType(config, model, selections, ch); + } + + if (this._isAutoClosingOpenCharType(config, model, selections, ch)) { + return this._runAutoClosingOpenCharType(config, model, selections, ch); + } + + if (this._isSurroundSelectionType(config, model, selections, ch)) { + return this._runSurroundSelectionType(config, model, selections, ch); + } + + // Electric characters make sense only when dealing with a single cursor, + // as multiple cursors typing brackets for example would interfer with bracket matching + if (this._isTypeInterceptorElectricChar(config, model, selections)) { + const r = this._typeInterceptorElectricChar(config, model, selections[0], ch); + if (r) { + return r; + } + } + + return this.typeWithoutInterceptors(config, model, selections, ch); + } + + public static typeWithoutInterceptors(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], str: string): EditOperationResult { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + commands[i] = new ReplaceCommand(selections[i], str); + } + return new EditOperationResult(commands, { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false + }); + } + + public static lineInsertBefore(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[]): ICommand[] { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + let lineNumber = selections[i].positionLineNumber; + + if (lineNumber === 1) { + commands[i] = new ReplaceCommandWithoutChangingPosition(new Range(1, 1, 1, 1), '\n'); + } else { + lineNumber--; + let column = model.getLineMaxColumn(lineNumber); + + commands[i] = this._enter(config, model, false, new Range(lineNumber, column, lineNumber, column)); + } + } + return commands; + } + + public static lineInsertAfter(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[]): ICommand[] { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + const lineNumber = selections[i].positionLineNumber; + let column = model.getLineMaxColumn(lineNumber); + commands[i] = this._enter(config, model, false, new Range(lineNumber, column, lineNumber, column)); + } + return commands; + } + + public static lineBreakInsert(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[]): ICommand[] { + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + commands[i] = this._enter(config, model, true, selections[i]); + } + return commands; + } +} diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts new file mode 100644 index 0000000000..e89f3dd6dc --- /dev/null +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -0,0 +1,444 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { SingleCursorState, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; +import { Position } from 'vs/editor/common/core/position'; +import { WordCharacterClassifier, WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; +import * as strings from 'vs/base/common/strings'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; + +interface IFindWordResult { + /** + * The index where the word starts. + */ + start: number; + /** + * The index where the word ends. + */ + end: number; + /** + * The word type. + */ + wordType: WordType; +} + +const enum WordType { + None = 0, + Regular = 1, + Separator = 2 +} + +export const enum WordNavigationType { + WordStart = 0, + WordEnd = 1 +} + +export class WordOperations { + + private static _createWord(lineContent: string, wordType: WordType, start: number, end: number): IFindWordResult { + // console.log('WORD ==> ' + start + ' => ' + end + ':::: <<<' + lineContent.substring(start, end) + '>>>'); + return { start: start, end: end, wordType: wordType }; + } + + private static _findPreviousWordOnLine(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position): IFindWordResult { + let lineContent = model.getLineContent(position.lineNumber); + return this._doFindPreviousWordOnLine(lineContent, wordSeparators, position); + } + + private static _doFindPreviousWordOnLine(lineContent: string, wordSeparators: WordCharacterClassifier, position: Position): IFindWordResult { + let wordType = WordType.None; + for (let chIndex = position.column - 2; chIndex >= 0; chIndex--) { + let chCode = lineContent.charCodeAt(chIndex); + let chClass = wordSeparators.get(chCode); + + if (chClass === WordCharacterClass.Regular) { + if (wordType === WordType.Separator) { + return this._createWord(lineContent, wordType, chIndex + 1, this._findEndOfWord(lineContent, wordSeparators, wordType, chIndex + 1)); + } + wordType = WordType.Regular; + } else if (chClass === WordCharacterClass.WordSeparator) { + if (wordType === WordType.Regular) { + return this._createWord(lineContent, wordType, chIndex + 1, this._findEndOfWord(lineContent, wordSeparators, wordType, chIndex + 1)); + } + wordType = WordType.Separator; + } else if (chClass === WordCharacterClass.Whitespace) { + if (wordType !== WordType.None) { + return this._createWord(lineContent, wordType, chIndex + 1, this._findEndOfWord(lineContent, wordSeparators, wordType, chIndex + 1)); + } + } + } + + if (wordType !== WordType.None) { + return this._createWord(lineContent, wordType, 0, this._findEndOfWord(lineContent, wordSeparators, wordType, 0)); + } + + return null; + } + + private static _findEndOfWord(lineContent: string, wordSeparators: WordCharacterClassifier, wordType: WordType, startIndex: number): number { + let len = lineContent.length; + for (let chIndex = startIndex; chIndex < len; chIndex++) { + let chCode = lineContent.charCodeAt(chIndex); + let chClass = wordSeparators.get(chCode); + + if (chClass === WordCharacterClass.Whitespace) { + return chIndex; + } + if (wordType === WordType.Regular && chClass === WordCharacterClass.WordSeparator) { + return chIndex; + } + if (wordType === WordType.Separator && chClass === WordCharacterClass.Regular) { + return chIndex; + } + } + return len; + } + + private static _findNextWordOnLine(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position): IFindWordResult { + let lineContent = model.getLineContent(position.lineNumber); + return this._doFindNextWordOnLine(lineContent, wordSeparators, position); + } + + private static _doFindNextWordOnLine(lineContent: string, wordSeparators: WordCharacterClassifier, position: Position): IFindWordResult { + let wordType = WordType.None; + let len = lineContent.length; + + for (let chIndex = position.column - 1; chIndex < len; chIndex++) { + let chCode = lineContent.charCodeAt(chIndex); + let chClass = wordSeparators.get(chCode); + + if (chClass === WordCharacterClass.Regular) { + if (wordType === WordType.Separator) { + return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordSeparators, wordType, chIndex - 1), chIndex); + } + wordType = WordType.Regular; + } else if (chClass === WordCharacterClass.WordSeparator) { + if (wordType === WordType.Regular) { + return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordSeparators, wordType, chIndex - 1), chIndex); + } + wordType = WordType.Separator; + } else if (chClass === WordCharacterClass.Whitespace) { + if (wordType !== WordType.None) { + return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordSeparators, wordType, chIndex - 1), chIndex); + } + } + } + + if (wordType !== WordType.None) { + return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordSeparators, wordType, len - 1), len); + } + + return null; + } + + private static _findStartOfWord(lineContent: string, wordSeparators: WordCharacterClassifier, wordType: WordType, startIndex: number): number { + for (let chIndex = startIndex; chIndex >= 0; chIndex--) { + let chCode = lineContent.charCodeAt(chIndex); + let chClass = wordSeparators.get(chCode); + + if (chClass === WordCharacterClass.Whitespace) { + return chIndex + 1; + } + if (wordType === WordType.Regular && chClass === WordCharacterClass.WordSeparator) { + return chIndex + 1; + } + if (wordType === WordType.Separator && chClass === WordCharacterClass.Regular) { + return chIndex + 1; + } + } + return 0; + } + + public static moveWordLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { + let lineNumber = position.lineNumber; + let column = position.column; + + if (column === 1) { + if (lineNumber > 1) { + lineNumber = lineNumber - 1; + column = model.getLineMaxColumn(lineNumber); + } + } + + let prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, column)); + + if (wordNavigationType === WordNavigationType.WordStart) { + if (prevWordOnLine) { + column = prevWordOnLine.start + 1; + } else { + column = 1; + } + } else { + if (prevWordOnLine && column <= prevWordOnLine.end + 1) { + prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, prevWordOnLine.start + 1)); + } + if (prevWordOnLine) { + column = prevWordOnLine.end + 1; + } else { + column = 1; + } + } + + return new Position(lineNumber, column); + } + + public static moveWordRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { + let lineNumber = position.lineNumber; + let column = position.column; + + if (column === model.getLineMaxColumn(lineNumber)) { + if (lineNumber < model.getLineCount()) { + lineNumber = lineNumber + 1; + column = 1; + } + } + + let nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, column)); + + if (wordNavigationType === WordNavigationType.WordEnd) { + if (nextWordOnLine) { + column = nextWordOnLine.end + 1; + } else { + column = model.getLineMaxColumn(lineNumber); + } + } else { + if (nextWordOnLine && column >= nextWordOnLine.start + 1) { + nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1)); + } + if (nextWordOnLine) { + column = nextWordOnLine.start + 1; + } else { + column = model.getLineMaxColumn(lineNumber); + } + } + + return new Position(lineNumber, column); + } + + private static _deleteWordLeftWhitespace(model: ICursorSimpleModel, position: Position): Range { + const lineContent = model.getLineContent(position.lineNumber); + const startIndex = position.column - 2; + const lastNonWhitespace = strings.lastNonWhitespaceIndex(lineContent, startIndex); + if (lastNonWhitespace + 1 < startIndex) { + return new Range(position.lineNumber, lastNonWhitespace + 2, position.lineNumber, position.column); + } + return null; + } + + public static deleteWordLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + if (!selection.isEmpty()) { + return selection; + } + + const position = new Position(selection.positionLineNumber, selection.positionColumn); + + let lineNumber = position.lineNumber; + let column = position.column; + + if (lineNumber === 1 && column === 1) { + // Ignore deleting at beginning of file + return null; + } + + if (whitespaceHeuristics) { + let r = this._deleteWordLeftWhitespace(model, position); + if (r) { + return r; + } + } + + let prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, position); + + if (wordNavigationType === WordNavigationType.WordStart) { + if (prevWordOnLine) { + column = prevWordOnLine.start + 1; + } else { + if (column > 1) { + column = 1; + } else { + lineNumber--; + column = model.getLineMaxColumn(lineNumber); + } + } + } else { + if (prevWordOnLine && column <= prevWordOnLine.end + 1) { + prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, prevWordOnLine.start + 1)); + } + if (prevWordOnLine) { + column = prevWordOnLine.end + 1; + } else { + if (column > 1) { + column = 1; + } else { + lineNumber--; + column = model.getLineMaxColumn(lineNumber); + } + } + } + + return new Range(lineNumber, column, position.lineNumber, position.column); + } + + private static _findFirstNonWhitespaceChar(str: string, startIndex: number): number { + let len = str.length; + for (let chIndex = startIndex; chIndex < len; chIndex++) { + let ch = str.charAt(chIndex); + if (ch !== ' ' && ch !== '\t') { + return chIndex; + } + } + return len; + } + + private static _deleteWordRightWhitespace(model: ICursorSimpleModel, position: Position): Range { + const lineContent = model.getLineContent(position.lineNumber); + const startIndex = position.column - 1; + const firstNonWhitespace = this._findFirstNonWhitespaceChar(lineContent, startIndex); + if (startIndex + 1 < firstNonWhitespace) { + // bingo + return new Range(position.lineNumber, position.column, position.lineNumber, firstNonWhitespace + 1); + } + return null; + } + + public static deleteWordRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + if (!selection.isEmpty()) { + return selection; + } + + const position = new Position(selection.positionLineNumber, selection.positionColumn); + + let lineNumber = position.lineNumber; + let column = position.column; + + const lineCount = model.getLineCount(); + const maxColumn = model.getLineMaxColumn(lineNumber); + if (lineNumber === lineCount && column === maxColumn) { + // Ignore deleting at end of file + return null; + } + + if (whitespaceHeuristics) { + let r = this._deleteWordRightWhitespace(model, position); + if (r) { + return r; + } + } + + let nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, position); + + if (wordNavigationType === WordNavigationType.WordEnd) { + if (nextWordOnLine) { + column = nextWordOnLine.end + 1; + } else { + if (column < maxColumn || lineNumber === lineCount) { + column = maxColumn; + } else { + lineNumber++; + nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, 1)); + if (nextWordOnLine) { + column = nextWordOnLine.start + 1; + } else { + column = model.getLineMaxColumn(lineNumber); + } + } + } + } else { + if (nextWordOnLine && column >= nextWordOnLine.start + 1) { + nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1)); + } + if (nextWordOnLine) { + column = nextWordOnLine.start + 1; + } else { + if (column < maxColumn || lineNumber === lineCount) { + column = maxColumn; + } else { + lineNumber++; + nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, 1)); + if (nextWordOnLine) { + column = nextWordOnLine.start + 1; + } else { + column = model.getLineMaxColumn(lineNumber); + } + } + } + } + + return new Range(lineNumber, column, position.lineNumber, position.column); + } + + public static word(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean, position: Position): SingleCursorState { + const wordSeparators = getMapForWordSeparators(config.wordSeparators); + let prevWord = WordOperations._findPreviousWordOnLine(wordSeparators, model, position); + let isInPrevWord = (prevWord && prevWord.wordType === WordType.Regular && prevWord.start < position.column - 1 && position.column - 1 <= prevWord.end); + let nextWord = WordOperations._findNextWordOnLine(wordSeparators, model, position); + let isInNextWord = (nextWord && nextWord.wordType === WordType.Regular && nextWord.start < position.column - 1 && position.column - 1 <= nextWord.end); + + if (!inSelectionMode || !cursor.hasSelection()) { + // Entering word selection for the first time + + let startColumn: number; + let endColumn: number; + + if (isInPrevWord) { + startColumn = prevWord.start + 1; + endColumn = prevWord.end + 1; + } else if (isInNextWord) { + startColumn = nextWord.start + 1; + endColumn = nextWord.end + 1; + } else { + if (prevWord) { + startColumn = prevWord.end + 1; + } else { + startColumn = 1; + } + if (nextWord) { + endColumn = nextWord.start + 1; + } else { + endColumn = model.getLineMaxColumn(position.lineNumber); + } + } + + return new SingleCursorState( + new Range(position.lineNumber, startColumn, position.lineNumber, endColumn), 0, + new Position(position.lineNumber, endColumn), 0 + ); + } + + let startColumn: number; + let endColumn: number; + + if (isInPrevWord) { + startColumn = prevWord.start + 1; + endColumn = prevWord.end + 1; + } else if (isInNextWord) { + startColumn = nextWord.start + 1; + endColumn = nextWord.end + 1; + } else { + startColumn = position.column; + endColumn = position.column; + } + + let lineNumber = position.lineNumber; + let column: number; + if (position.isBeforeOrEqual(cursor.selectionStart.getStartPosition())) { + column = startColumn; + let possiblePosition = new Position(lineNumber, column); + if (cursor.selectionStart.containsPosition(possiblePosition)) { + column = cursor.selectionStart.endColumn; + } + } else { + column = endColumn; + let possiblePosition = new Position(lineNumber, column); + if (cursor.selectionStart.containsPosition(possiblePosition)) { + column = cursor.selectionStart.startColumn; + } + } + + return cursor.move(cursor.hasSelection(), lineNumber, column, 0); + } +} diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts new file mode 100644 index 0000000000..65c8cde310 --- /dev/null +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -0,0 +1,116 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { SingleCursorState, CursorContext, CursorState } from 'vs/editor/common/controller/cursorCommon'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; + +export class OneCursor { + + public modelState: SingleCursorState; + public viewState: SingleCursorState; + + private _selStartMarker: string; + private _selEndMarker: string; + + constructor(context: CursorContext) { + this._setState( + context, + new SingleCursorState(new Range(1, 1, 1, 1), 0, new Position(1, 1), 0), + new SingleCursorState(new Range(1, 1, 1, 1), 0, new Position(1, 1), 0) + ); + } + + public dispose(context: CursorContext): void { + context.model._removeMarker(this._selStartMarker); + context.model._removeMarker(this._selEndMarker); + } + + public asCursorState(): CursorState { + return new CursorState(this.modelState, this.viewState); + } + + public readSelectionFromMarkers(context: CursorContext): Selection { + const start = context.model._getMarker(this._selStartMarker); + const end = context.model._getMarker(this._selEndMarker); + + if (this.modelState.selection.getDirection() === SelectionDirection.LTR) { + return new Selection(start.lineNumber, start.column, end.lineNumber, end.column); + } + + return new Selection(end.lineNumber, end.column, start.lineNumber, start.column); + } + + public ensureValidState(context: CursorContext): void { + this._setState(context, this.modelState, this.viewState); + } + + public setState(context: CursorContext, modelState: SingleCursorState, viewState: SingleCursorState): void { + this._setState(context, modelState, viewState); + } + + private _setState(context: CursorContext, modelState: SingleCursorState, viewState: SingleCursorState): void { + if (!modelState) { + // We only have the view state => compute the model state + const selectionStart = context.model.validateRange( + context.convertViewRangeToModelRange(viewState.selectionStart) + ); + + const position = context.model.validatePosition( + context.convertViewPositionToModelPosition(viewState.position.lineNumber, viewState.position.column) + ); + + modelState = new SingleCursorState(selectionStart, viewState.selectionStartLeftoverVisibleColumns, position, viewState.leftoverVisibleColumns); + } else { + // Validate new model state + const selectionStart = context.model.validateRange(modelState.selectionStart); + const selectionStartLeftoverVisibleColumns = modelState.selectionStart.equalsRange(selectionStart) ? modelState.selectionStartLeftoverVisibleColumns : 0; + + const position = context.model.validatePosition( + modelState.position + ); + const leftoverVisibleColumns = modelState.position.equals(position) ? modelState.leftoverVisibleColumns : 0; + + modelState = new SingleCursorState(selectionStart, selectionStartLeftoverVisibleColumns, position, leftoverVisibleColumns); + } + + if (!viewState) { + // We only have the model state => compute the view state + const viewSelectionStart1 = context.convertModelPositionToViewPosition(new Position(modelState.selectionStart.startLineNumber, modelState.selectionStart.startColumn)); + const viewSelectionStart2 = context.convertModelPositionToViewPosition(new Position(modelState.selectionStart.endLineNumber, modelState.selectionStart.endColumn)); + const viewSelectionStart = new Range(viewSelectionStart1.lineNumber, viewSelectionStart1.column, viewSelectionStart2.lineNumber, viewSelectionStart2.column); + const viewPosition = context.convertModelPositionToViewPosition(modelState.position); + viewState = new SingleCursorState(viewSelectionStart, modelState.selectionStartLeftoverVisibleColumns, viewPosition, modelState.leftoverVisibleColumns); + } else { + // Validate new view state + const viewSelectionStart = context.validateViewRange(viewState.selectionStart, modelState.selectionStart); + const viewPosition = context.validateViewPosition(viewState.position, modelState.position); + viewState = new SingleCursorState(viewSelectionStart, modelState.selectionStartLeftoverVisibleColumns, viewPosition, modelState.leftoverVisibleColumns); + } + + if (this.modelState && this.viewState && this.modelState.equals(modelState) && this.viewState.equals(viewState)) { + // No-op, early return + return; + } + + this.modelState = modelState; + this.viewState = viewState; + + this._selStartMarker = this._ensureMarker(context, this._selStartMarker, this.modelState.selection.startLineNumber, this.modelState.selection.startColumn, true); + this._selEndMarker = this._ensureMarker(context, this._selEndMarker, this.modelState.selection.endLineNumber, this.modelState.selection.endColumn, false); + } + + private _ensureMarker(context: CursorContext, markerId: string, lineNumber: number, column: number, stickToPreviousCharacter: boolean): string { + if (!markerId) { + return context.model._addMarker(0, lineNumber, column, stickToPreviousCharacter); + } else { + context.model._changeMarker(markerId, lineNumber, column); + context.model._changeMarkerStickiness(markerId, stickToPreviousCharacter); + return markerId; + } + } +} diff --git a/src/vs/editor/common/controller/wordCharacterClassifier.ts b/src/vs/editor/common/controller/wordCharacterClassifier.ts new file mode 100644 index 0000000000..bba490cfbb --- /dev/null +++ b/src/vs/editor/common/controller/wordCharacterClassifier.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { CharCode } from 'vs/base/common/charCode'; +import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; + +export const enum WordCharacterClass { + Regular = 0, + Whitespace = 1, + WordSeparator = 2 +} + +export class WordCharacterClassifier extends CharacterClassifier { + + constructor(wordSeparators: string) { + super(WordCharacterClass.Regular); + + for (let i = 0, len = wordSeparators.length; i < len; i++) { + this.set(wordSeparators.charCodeAt(i), WordCharacterClass.WordSeparator); + } + + this.set(CharCode.Space, WordCharacterClass.Whitespace); + this.set(CharCode.Tab, WordCharacterClass.Whitespace); + } + +} + +function once(computeFn: (input: string) => R): (input: string) => R { + let cache: { [key: string]: R; } = {}; // TODO@Alex unbounded cache + return (input: string): R => { + if (!cache.hasOwnProperty(input)) { + cache[input] = computeFn(input); + } + return cache[input]; + }; +} + +export const getMapForWordSeparators = once( + (input) => new WordCharacterClassifier(input) +); diff --git a/src/vs/editor/common/core/characterClassifier.ts b/src/vs/editor/common/core/characterClassifier.ts new file mode 100644 index 0000000000..9808893caa --- /dev/null +++ b/src/vs/editor/common/core/characterClassifier.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { toUint8 } from 'vs/editor/common/core/uint'; + +/** + * A fast character classifier that uses a compact array for ASCII values. + */ +export class CharacterClassifier { + /** + * Maintain a compact (fully initialized ASCII map for quickly classifying ASCII characters - used more often in code). + */ + private _asciiMap: Uint8Array; + + /** + * The entire map (sparse array). + */ + private _map: Map; + + private _defaultValue: number; + + constructor(_defaultValue: T) { + let defaultValue = toUint8(_defaultValue); + + this._defaultValue = defaultValue; + this._asciiMap = CharacterClassifier._createAsciiMap(defaultValue); + this._map = new Map(); + } + + private static _createAsciiMap(defaultValue: number): Uint8Array { + let asciiMap: Uint8Array = new Uint8Array(256); + for (let i = 0; i < 256; i++) { + asciiMap[i] = defaultValue; + } + return asciiMap; + } + + public set(charCode: number, _value: T): void { + let value = toUint8(_value); + + if (charCode >= 0 && charCode < 256) { + this._asciiMap[charCode] = value; + } else { + this._map.set(charCode, value); + } + } + + public get(charCode: number): T { + if (charCode >= 0 && charCode < 256) { + return this._asciiMap[charCode]; + } else { + return (this._map.get(charCode) || this._defaultValue); + } + } +} + +const enum Boolean { + False = 0, + True = 1 +} + +export class CharacterSet { + + private _actual: CharacterClassifier; + + constructor() { + this._actual = new CharacterClassifier(Boolean.False); + } + + public add(charCode: number): void { + this._actual.set(charCode, Boolean.True); + } + + public has(charCode: number): boolean { + return (this._actual.get(charCode) === Boolean.True); + } +} diff --git a/src/vs/editor/common/core/editOperation.ts b/src/vs/editor/common/core/editOperation.ts new file mode 100644 index 0000000000..175a9f2c54 --- /dev/null +++ b/src/vs/editor/common/core/editOperation.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; + +export class EditOperation { + + public static insert(position: Position, text: string): IIdentifiedSingleEditOperation { + return { + identifier: null, + range: new Range(position.lineNumber, position.column, position.lineNumber, position.column), + text: text, + forceMoveMarkers: true + }; + } + + public static delete(range: Range): IIdentifiedSingleEditOperation { + return { + identifier: null, + range: range, + text: null, + forceMoveMarkers: true + }; + } + + public static replace(range: Range, text: string): IIdentifiedSingleEditOperation { + return { + identifier: null, + range: range, + text: text, + forceMoveMarkers: false + }; + } + + public static replaceMove(range: Range, text: string): IIdentifiedSingleEditOperation { + return { + identifier: null, + range: range, + text: text, + forceMoveMarkers: true + }; + } +} \ No newline at end of file diff --git a/src/vs/editor/common/core/editorState.ts b/src/vs/editor/common/core/editorState.ts new file mode 100644 index 0000000000..940fb131aa --- /dev/null +++ b/src/vs/editor/common/core/editorState.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; + +export const enum CodeEditorStateFlag { + Value = 1, + Selection = 2, + Position = 4, + Scroll = 8 +} + +export class EditorState { + + private readonly flags: number; + + private readonly position: Position; + private readonly selection: Range; + private readonly modelVersionId: string; + private readonly scrollLeft: number; + private readonly scrollTop: number; + + constructor(editor: ICommonCodeEditor, flags: number) { + this.flags = flags; + + if ((this.flags & CodeEditorStateFlag.Value) !== 0) { + var model = editor.getModel(); + this.modelVersionId = model ? strings.format('{0}#{1}', model.uri.toString(), model.getVersionId()) : null; + } + if ((this.flags & CodeEditorStateFlag.Position) !== 0) { + this.position = editor.getPosition(); + } + if ((this.flags & CodeEditorStateFlag.Selection) !== 0) { + this.selection = editor.getSelection(); + } + if ((this.flags & CodeEditorStateFlag.Scroll) !== 0) { + this.scrollLeft = editor.getScrollLeft(); + this.scrollTop = editor.getScrollTop(); + } + } + + private _equals(other: any): boolean { + + if (!(other instanceof EditorState)) { + return false; + } + var state = other; + + if (this.modelVersionId !== state.modelVersionId) { + return false; + } + if (this.scrollLeft !== state.scrollLeft || this.scrollTop !== state.scrollTop) { + return false; + } + if (!this.position && state.position || this.position && !state.position || this.position && state.position && !this.position.equals(state.position)) { + return false; + } + if (!this.selection && state.selection || this.selection && !state.selection || this.selection && state.selection && !this.selection.equalsRange(state.selection)) { + return false; + } + return true; + } + + public validate(editor: ICommonCodeEditor): boolean { + return this._equals(new EditorState(editor, this.flags)); + } +} diff --git a/src/vs/editor/common/core/lineTokens.ts b/src/vs/editor/common/core/lineTokens.ts new file mode 100644 index 0000000000..377587435d --- /dev/null +++ b/src/vs/editor/common/core/lineTokens.ts @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding'; +import { ViewLineTokenFactory, ViewLineToken } from 'vs/editor/common/core/viewLineToken'; +import { ColorId, FontStyle, StandardTokenType, LanguageId } from 'vs/editor/common/modes'; + +export class LineToken { + _lineTokenBrand: void; + + private readonly _source: LineTokens; + private readonly _tokenIndex: number; + private readonly _metadata: number; + + public readonly startOffset: number; + public readonly endOffset: number; + + public readonly hasPrev: boolean; + public readonly hasNext: boolean; + + public get languageId(): LanguageId { + return TokenMetadata.getLanguageId(this._metadata); + } + + public get tokenType(): StandardTokenType { + return TokenMetadata.getTokenType(this._metadata); + } + + public get fontStyle(): FontStyle { + return TokenMetadata.getFontStyle(this._metadata); + } + + public get foregroundId(): ColorId { + return TokenMetadata.getForeground(this._metadata); + } + + public get backgroundId(): ColorId { + return TokenMetadata.getBackground(this._metadata); + } + + constructor(source: LineTokens, tokenIndex: number, tokenCount: number, startOffset: number, endOffset: number, metadata: number) { + this._source = source; + this._tokenIndex = tokenIndex; + this._metadata = metadata; + + this.startOffset = startOffset; + this.endOffset = endOffset; + + this.hasPrev = (this._tokenIndex > 0); + this.hasNext = (this._tokenIndex + 1 < tokenCount); + } + + public prev(): LineToken { + if (!this.hasPrev) { + return null; + } + + return this._source.tokenAt(this._tokenIndex - 1); + } + + public next(): LineToken { + if (!this.hasNext) { + return null; + } + + return this._source.tokenAt(this._tokenIndex + 1); + } +} + +export class LineTokens { + _lineTokensBrand: void; + + private readonly _tokens: Uint32Array; + private readonly _tokensCount: number; + private readonly _text: string; + private readonly _textLength: number; + + constructor(tokens: Uint32Array, text: string) { + this._tokens = tokens; + this._tokensCount = (this._tokens.length >>> 1); + this._text = text; + this._textLength = this._text.length; + } + + public getTokenCount(): number { + return this._tokensCount; + } + + public getLineContent(): string { + return this._text; + } + + public getLineLength(): number { + return this._textLength; + } + + public getTokenStartOffset(tokenIndex: number): number { + return this._tokens[(tokenIndex << 1)]; + } + + public getLanguageId(tokenIndex: number): LanguageId { + let metadata = this._tokens[(tokenIndex << 1) + 1]; + return TokenMetadata.getLanguageId(metadata); + } + + public getStandardTokenType(tokenIndex: number): StandardTokenType { + let metadata = this._tokens[(tokenIndex << 1) + 1]; + return TokenMetadata.getTokenType(metadata); + } + + public getTokenEndOffset(tokenIndex: number): number { + if (tokenIndex + 1 < this._tokensCount) { + return this._tokens[(tokenIndex + 1) << 1]; + } + return this._textLength; + } + + /** + * Find the token containing offset `offset`. + * ``` + * For example, with the following tokens [0, 5), [5, 9), [9, infinity) + * Searching for 0, 1, 2, 3 or 4 will return 0. + * Searching for 5, 6, 7 or 8 will return 1. + * Searching for 9, 10, 11, ... will return 2. + * ``` + * @param offset The search offset + * @return The index of the token containing the offset. + */ + public findTokenIndexAtOffset(offset: number): number { + return ViewLineTokenFactory.findIndexInSegmentsArray(this._tokens, offset); + } + + public findTokenAtOffset(offset: number): LineToken { + let tokenIndex = this.findTokenIndexAtOffset(offset); + return this.tokenAt(tokenIndex); + } + + public tokenAt(tokenIndex: number): LineToken { + let startOffset = this._tokens[(tokenIndex << 1)]; + let endOffset: number; + if (tokenIndex + 1 < this._tokensCount) { + endOffset = this._tokens[(tokenIndex + 1) << 1]; + } else { + endOffset = this._textLength; + } + let metadata = this._tokens[(tokenIndex << 1) + 1]; + return new LineToken(this, tokenIndex, this._tokensCount, startOffset, endOffset, metadata); + } + + public firstToken(): LineToken { + if (this._textLength === 0) { + return null; + } + return this.tokenAt(0); + } + + public lastToken(): LineToken { + if (this._textLength === 0) { + return null; + } + return this.tokenAt(this._tokensCount - 1); + } + + public inflate(): ViewLineToken[] { + return ViewLineTokenFactory.inflateArr(this._tokens, this._textLength); + } + + public sliceAndInflate(startOffset: number, endOffset: number, deltaOffset: number): ViewLineToken[] { + return ViewLineTokenFactory.sliceAndInflate(this._tokens, startOffset, endOffset, deltaOffset, this._textLength); + } +} diff --git a/src/vs/editor/common/core/position.ts b/src/vs/editor/common/core/position.ts new file mode 100644 index 0000000000..c1ca146c57 --- /dev/null +++ b/src/vs/editor/common/core/position.ts @@ -0,0 +1,154 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +/** + * A position in the editor. This interface is suitable for serialization. + */ +export interface IPosition { + /** + * line number (starts at 1) + */ + readonly lineNumber: number; + /** + * column (the first character in a line is between column 1 and column 2) + */ + readonly column: number; +} + +/** + * A position in the editor. + */ +export class Position { + /** + * line number (starts at 1) + */ + public readonly lineNumber: number; + /** + * column (the first character in a line is between column 1 and column 2) + */ + public readonly column: number; + + constructor(lineNumber: number, column: number) { + this.lineNumber = lineNumber; + this.column = column; + } + + /** + * Test if this position equals other position + */ + public equals(other: IPosition): boolean { + return Position.equals(this, other); + } + + /** + * Test if position `a` equals position `b` + */ + public static equals(a: IPosition, b: IPosition): boolean { + if (!a && !b) { + return true; + } + return ( + !!a && + !!b && + a.lineNumber === b.lineNumber && + a.column === b.column + ); + } + + /** + * Test if this position is before other position. + * If the two positions are equal, the result will be false. + */ + public isBefore(other: IPosition): boolean { + return Position.isBefore(this, other); + } + + /** + * Test if position `a` is before position `b`. + * If the two positions are equal, the result will be false. + */ + public static isBefore(a: IPosition, b: IPosition): boolean { + if (a.lineNumber < b.lineNumber) { + return true; + } + if (b.lineNumber < a.lineNumber) { + return false; + } + return a.column < b.column; + } + + /** + * Test if this position is before other position. + * If the two positions are equal, the result will be true. + */ + public isBeforeOrEqual(other: IPosition): boolean { + return Position.isBeforeOrEqual(this, other); + } + + /** + * Test if position `a` is before position `b`. + * If the two positions are equal, the result will be true. + */ + public static isBeforeOrEqual(a: IPosition, b: IPosition): boolean { + if (a.lineNumber < b.lineNumber) { + return true; + } + if (b.lineNumber < a.lineNumber) { + return false; + } + return a.column <= b.column; + } + + /** + * A function that compares positions, useful for sorting + */ + public static compare(a: IPosition, b: IPosition): number { + let aLineNumber = a.lineNumber | 0; + let bLineNumber = b.lineNumber | 0; + + if (aLineNumber === bLineNumber) { + let aColumn = a.column | 0; + let bColumn = b.column | 0; + return aColumn - bColumn; + } + + return aLineNumber - bLineNumber; + } + + /** + * Clone this position. + */ + public clone(): Position { + return new Position(this.lineNumber, this.column); + } + + /** + * Convert to a human-readable representation. + */ + public toString(): string { + return '(' + this.lineNumber + ',' + this.column + ')'; + } + + // --- + + /** + * Create a `Position` from an `IPosition`. + */ + public static lift(pos: IPosition): Position { + return new Position(pos.lineNumber, pos.column); + } + + /** + * Test if `obj` is an `IPosition`. + */ + public static isIPosition(obj: any): obj is IPosition { + return ( + obj + && (typeof obj.lineNumber === 'number') + && (typeof obj.column === 'number') + ); + } +} diff --git a/src/vs/editor/common/core/range.ts b/src/vs/editor/common/core/range.ts new file mode 100644 index 0000000000..091f1f5ef6 --- /dev/null +++ b/src/vs/editor/common/core/range.ts @@ -0,0 +1,390 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Position, IPosition } from 'vs/editor/common/core/position'; + +/** + * A range in the editor. This interface is suitable for serialization. + */ +export interface IRange { + /** + * Line number on which the range starts (starts at 1). + */ + readonly startLineNumber: number; + /** + * Column on which the range starts in line `startLineNumber` (starts at 1). + */ + readonly startColumn: number; + /** + * Line number on which the range ends. + */ + readonly endLineNumber: number; + /** + * Column on which the range ends in line `endLineNumber`. + */ + readonly endColumn: number; +} + +/** + * A range in the editor. (startLineNumber,startColumn) is <= (endLineNumber,endColumn) + */ +export class Range { + + /** + * Line number on which the range starts (starts at 1). + */ + public readonly startLineNumber: number; + /** + * Column on which the range starts in line `startLineNumber` (starts at 1). + */ + public readonly startColumn: number; + /** + * Line number on which the range ends. + */ + public readonly endLineNumber: number; + /** + * Column on which the range ends in line `endLineNumber`. + */ + public readonly endColumn: number; + + constructor(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number) { + if ((startLineNumber > endLineNumber) || (startLineNumber === endLineNumber && startColumn > endColumn)) { + this.startLineNumber = endLineNumber; + this.startColumn = endColumn; + this.endLineNumber = startLineNumber; + this.endColumn = startColumn; + } else { + this.startLineNumber = startLineNumber; + this.startColumn = startColumn; + this.endLineNumber = endLineNumber; + this.endColumn = endColumn; + } + } + + /** + * Test if this range is empty. + */ + public isEmpty(): boolean { + return Range.isEmpty(this); + } + + /** + * Test if `range` is empty. + */ + public static isEmpty(range: IRange): boolean { + return (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn); + } + + /** + * Test if position is in this range. If the position is at the edges, will return true. + */ + public containsPosition(position: IPosition): boolean { + return Range.containsPosition(this, position); + } + + /** + * Test if `position` is in `range`. If the position is at the edges, will return true. + */ + public static containsPosition(range: IRange, position: IPosition): boolean { + if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) { + return false; + } + if (position.lineNumber === range.startLineNumber && position.column < range.startColumn) { + return false; + } + if (position.lineNumber === range.endLineNumber && position.column > range.endColumn) { + return false; + } + return true; + } + + /** + * Test if range is in this range. If the range is equal to this range, will return true. + */ + public containsRange(range: IRange): boolean { + return Range.containsRange(this, range); + } + + /** + * Test if `otherRange` is in `range`. If the ranges are equal, will return true. + */ + public static containsRange(range: IRange, otherRange: IRange): boolean { + if (otherRange.startLineNumber < range.startLineNumber || otherRange.endLineNumber < range.startLineNumber) { + return false; + } + if (otherRange.startLineNumber > range.endLineNumber || otherRange.endLineNumber > range.endLineNumber) { + return false; + } + if (otherRange.startLineNumber === range.startLineNumber && otherRange.startColumn < range.startColumn) { + return false; + } + if (otherRange.endLineNumber === range.endLineNumber && otherRange.endColumn > range.endColumn) { + return false; + } + return true; + } + + /** + * A reunion of the two ranges. + * The smallest position will be used as the start point, and the largest one as the end point. + */ + public plusRange(range: IRange): Range { + return Range.plusRange(this, range); + } + + /** + * A reunion of the two ranges. + * The smallest position will be used as the start point, and the largest one as the end point. + */ + public static plusRange(a: IRange, b: IRange): Range { + var startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number; + if (b.startLineNumber < a.startLineNumber) { + startLineNumber = b.startLineNumber; + startColumn = b.startColumn; + } else if (b.startLineNumber === a.startLineNumber) { + startLineNumber = b.startLineNumber; + startColumn = Math.min(b.startColumn, a.startColumn); + } else { + startLineNumber = a.startLineNumber; + startColumn = a.startColumn; + } + + if (b.endLineNumber > a.endLineNumber) { + endLineNumber = b.endLineNumber; + endColumn = b.endColumn; + } else if (b.endLineNumber === a.endLineNumber) { + endLineNumber = b.endLineNumber; + endColumn = Math.max(b.endColumn, a.endColumn); + } else { + endLineNumber = a.endLineNumber; + endColumn = a.endColumn; + } + + return new Range(startLineNumber, startColumn, endLineNumber, endColumn); + } + + /** + * A intersection of the two ranges. + */ + public intersectRanges(range: IRange): Range { + return Range.intersectRanges(this, range); + } + + /** + * A intersection of the two ranges. + */ + public static intersectRanges(a: IRange, b: IRange): Range { + var resultStartLineNumber = a.startLineNumber, + resultStartColumn = a.startColumn, + resultEndLineNumber = a.endLineNumber, + resultEndColumn = a.endColumn, + otherStartLineNumber = b.startLineNumber, + otherStartColumn = b.startColumn, + otherEndLineNumber = b.endLineNumber, + otherEndColumn = b.endColumn; + + if (resultStartLineNumber < otherStartLineNumber) { + resultStartLineNumber = otherStartLineNumber; + resultStartColumn = otherStartColumn; + } else if (resultStartLineNumber === otherStartLineNumber) { + resultStartColumn = Math.max(resultStartColumn, otherStartColumn); + } + + if (resultEndLineNumber > otherEndLineNumber) { + resultEndLineNumber = otherEndLineNumber; + resultEndColumn = otherEndColumn; + } else if (resultEndLineNumber === otherEndLineNumber) { + resultEndColumn = Math.min(resultEndColumn, otherEndColumn); + } + + // Check if selection is now empty + if (resultStartLineNumber > resultEndLineNumber) { + return null; + } + if (resultStartLineNumber === resultEndLineNumber && resultStartColumn > resultEndColumn) { + return null; + } + return new Range(resultStartLineNumber, resultStartColumn, resultEndLineNumber, resultEndColumn); + } + + /** + * Test if this range equals other. + */ + public equalsRange(other: IRange): boolean { + return Range.equalsRange(this, other); + } + + /** + * Test if range `a` equals `b`. + */ + public static equalsRange(a: IRange, b: IRange): boolean { + return ( + !!a && + !!b && + a.startLineNumber === b.startLineNumber && + a.startColumn === b.startColumn && + a.endLineNumber === b.endLineNumber && + a.endColumn === b.endColumn + ); + } + + /** + * Return the end position (which will be after or equal to the start position) + */ + public getEndPosition(): Position { + return new Position(this.endLineNumber, this.endColumn); + } + + /** + * Return the start position (which will be before or equal to the end position) + */ + public getStartPosition(): Position { + return new Position(this.startLineNumber, this.startColumn); + } + + /** + * Clone this range. + */ + public cloneRange(): Range { + return new Range(this.startLineNumber, this.startColumn, this.endLineNumber, this.endColumn); + } + + /** + * Transform to a user presentable string representation. + */ + public toString(): string { + return '[' + this.startLineNumber + ',' + this.startColumn + ' -> ' + this.endLineNumber + ',' + this.endColumn + ']'; + } + + /** + * Create a new range using this range's start position, and using endLineNumber and endColumn as the end position. + */ + public setEndPosition(endLineNumber: number, endColumn: number): Range { + return new Range(this.startLineNumber, this.startColumn, endLineNumber, endColumn); + } + + /** + * Create a new range using this range's end position, and using startLineNumber and startColumn as the start position. + */ + public setStartPosition(startLineNumber: number, startColumn: number): Range { + return new Range(startLineNumber, startColumn, this.endLineNumber, this.endColumn); + } + + /** + * Create a new empty range using this range's start position. + */ + public collapseToStart(): Range { + return Range.collapseToStart(this); + } + + /** + * Create a new empty range using this range's start position. + */ + public static collapseToStart(range: IRange): Range { + return new Range(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn); + } + + // --- + + public static fromPositions(start: IPosition, end: IPosition = start): Range { + return new Range(start.lineNumber, start.column, end.lineNumber, end.column); + } + + /** + * Create a `Range` from an `IRange`. + */ + public static lift(range: IRange): Range { + if (!range) { + return null; + } + return new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); + } + + /** + * Test if `obj` is an `IRange`. + */ + public static isIRange(obj: any): obj is IRange { + return ( + obj + && (typeof obj.startLineNumber === 'number') + && (typeof obj.startColumn === 'number') + && (typeof obj.endLineNumber === 'number') + && (typeof obj.endColumn === 'number') + ); + } + + /** + * Test if the two ranges are touching in any way. + */ + public static areIntersectingOrTouching(a: IRange, b: IRange): boolean { + // Check if `a` is before `b` + if (a.endLineNumber < b.startLineNumber || (a.endLineNumber === b.startLineNumber && a.endColumn < b.startColumn)) { + return false; + } + + // Check if `b` is before `a` + if (b.endLineNumber < a.startLineNumber || (b.endLineNumber === a.startLineNumber && b.endColumn < a.startColumn)) { + return false; + } + + // These ranges must intersect + return true; + } + + /** + * A function that compares ranges, useful for sorting ranges + * It will first compare ranges on the startPosition and then on the endPosition + */ + public static compareRangesUsingStarts(a: IRange, b: IRange): number { + let aStartLineNumber = a.startLineNumber | 0; + let bStartLineNumber = b.startLineNumber | 0; + + if (aStartLineNumber === bStartLineNumber) { + let aStartColumn = a.startColumn | 0; + let bStartColumn = b.startColumn | 0; + + if (aStartColumn === bStartColumn) { + let aEndLineNumber = a.endLineNumber | 0; + let bEndLineNumber = b.endLineNumber | 0; + + if (aEndLineNumber === bEndLineNumber) { + let aEndColumn = a.endColumn | 0; + let bEndColumn = b.endColumn | 0; + return aEndColumn - bEndColumn; + } + return aEndLineNumber - bEndLineNumber; + } + return aStartColumn - bStartColumn; + } + return aStartLineNumber - bStartLineNumber; + } + + /** + * A function that compares ranges, useful for sorting ranges + * It will first compare ranges on the endPosition and then on the startPosition + */ + public static compareRangesUsingEnds(a: IRange, b: IRange): number { + if (a.endLineNumber === b.endLineNumber) { + if (a.endColumn === b.endColumn) { + if (a.startLineNumber === b.startLineNumber) { + return a.startColumn - b.startColumn; + } + return a.startLineNumber - b.startLineNumber; + } + return a.endColumn - b.endColumn; + } + return a.endLineNumber - b.endLineNumber; + } + + /** + * Test if the range spans multiple lines. + */ + public static spansMultipleLines(range: IRange): boolean { + return range.endLineNumber > range.startLineNumber; + } +} + diff --git a/src/vs/editor/common/core/rgba.ts b/src/vs/editor/common/core/rgba.ts new file mode 100644 index 0000000000..7ef666dd53 --- /dev/null +++ b/src/vs/editor/common/core/rgba.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +/** + * A very VM friendly rgba datastructure. + * Please don't touch unless you take a look at the IR. + */ +export class RGBA8 { + _rgba8Brand: void; + + /** + * Red: integer in [0-255] + */ + public readonly r: number; + /** + * Green: integer in [0-255] + */ + public readonly g: number; + /** + * Blue: integer in [0-255] + */ + public readonly b: number; + /** + * Alpha: integer in [0-255] + */ + public readonly a: number; + + constructor(r: number, g: number, b: number, a: number) { + this.r = RGBA8._clampInt_0_255(r); + this.g = RGBA8._clampInt_0_255(g); + this.b = RGBA8._clampInt_0_255(b); + this.a = RGBA8._clampInt_0_255(a); + } + + public static equals(a: RGBA8, b: RGBA8): boolean { + return ( + a.r === b.r + && a.g === b.g + && a.b === b.b + && a.a === b.a + ); + } + + private static _clampInt_0_255(c: number): number { + if (c < 0) { + return 0; + } + if (c > 255) { + return 255; + } + return c | 0; + } +} diff --git a/src/vs/editor/common/core/selection.ts b/src/vs/editor/common/core/selection.ts new file mode 100644 index 0000000000..fb440243c3 --- /dev/null +++ b/src/vs/editor/common/core/selection.ts @@ -0,0 +1,210 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Range } from 'vs/editor/common/core/range'; +import { Position, IPosition } from 'vs/editor/common/core/position'; + +/** + * A selection in the editor. + * The selection is a range that has an orientation. + */ +export interface ISelection { + /** + * The line number on which the selection has started. + */ + readonly selectionStartLineNumber: number; + /** + * The column on `selectionStartLineNumber` where the selection has started. + */ + readonly selectionStartColumn: number; + /** + * The line number on which the selection has ended. + */ + readonly positionLineNumber: number; + /** + * The column on `positionLineNumber` where the selection has ended. + */ + readonly positionColumn: number; +} + +/** + * The direction of a selection. + */ +export enum SelectionDirection { + /** + * The selection starts above where it ends. + */ + LTR, + /** + * The selection starts below where it ends. + */ + RTL +} + +/** + * A selection in the editor. + * The selection is a range that has an orientation. + */ +export class Selection extends Range { + /** + * The line number on which the selection has started. + */ + public readonly selectionStartLineNumber: number; + /** + * The column on `selectionStartLineNumber` where the selection has started. + */ + public readonly selectionStartColumn: number; + /** + * The line number on which the selection has ended. + */ + public readonly positionLineNumber: number; + /** + * The column on `positionLineNumber` where the selection has ended. + */ + public readonly positionColumn: number; + + constructor(selectionStartLineNumber: number, selectionStartColumn: number, positionLineNumber: number, positionColumn: number) { + super(selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn); + this.selectionStartLineNumber = selectionStartLineNumber; + this.selectionStartColumn = selectionStartColumn; + this.positionLineNumber = positionLineNumber; + this.positionColumn = positionColumn; + } + + /** + * Clone this selection. + */ + public clone(): Selection { + return new Selection(this.selectionStartLineNumber, this.selectionStartColumn, this.positionLineNumber, this.positionColumn); + } + + /** + * Transform to a human-readable representation. + */ + public toString(): string { + return '[' + this.selectionStartLineNumber + ',' + this.selectionStartColumn + ' -> ' + this.positionLineNumber + ',' + this.positionColumn + ']'; + } + + /** + * Test if equals other selection. + */ + public equalsSelection(other: ISelection): boolean { + return ( + Selection.selectionsEqual(this, other) + ); + } + + /** + * Test if the two selections are equal. + */ + public static selectionsEqual(a: ISelection, b: ISelection): boolean { + return ( + a.selectionStartLineNumber === b.selectionStartLineNumber && + a.selectionStartColumn === b.selectionStartColumn && + a.positionLineNumber === b.positionLineNumber && + a.positionColumn === b.positionColumn + ); + } + + /** + * Get directions (LTR or RTL). + */ + public getDirection(): SelectionDirection { + if (this.selectionStartLineNumber === this.startLineNumber && this.selectionStartColumn === this.startColumn) { + return SelectionDirection.LTR; + } + return SelectionDirection.RTL; + } + + /** + * Create a new selection with a different `positionLineNumber` and `positionColumn`. + */ + public setEndPosition(endLineNumber: number, endColumn: number): Selection { + if (this.getDirection() === SelectionDirection.LTR) { + return new Selection(this.startLineNumber, this.startColumn, endLineNumber, endColumn); + } + return new Selection(endLineNumber, endColumn, this.startLineNumber, this.startColumn); + } + + /** + * Get the position at `positionLineNumber` and `positionColumn`. + */ + public getPosition(): Position { + return new Position(this.positionLineNumber, this.positionColumn); + } + + /** + * Create a new selection with a different `selectionStartLineNumber` and `selectionStartColumn`. + */ + public setStartPosition(startLineNumber: number, startColumn: number): Selection { + if (this.getDirection() === SelectionDirection.LTR) { + return new Selection(startLineNumber, startColumn, this.endLineNumber, this.endColumn); + } + return new Selection(this.endLineNumber, this.endColumn, startLineNumber, startColumn); + } + + // ---- + + /** + * Create a `Selection` from one or two positions + */ + public static fromPositions(start: IPosition, end: IPosition = start): Selection { + return new Selection(start.lineNumber, start.column, end.lineNumber, end.column); + } + + /** + * Create a `Selection` from an `ISelection`. + */ + public static liftSelection(sel: ISelection): Selection { + return new Selection(sel.selectionStartLineNumber, sel.selectionStartColumn, sel.positionLineNumber, sel.positionColumn); + } + + /** + * `a` equals `b`. + */ + public static selectionsArrEqual(a: ISelection[], b: ISelection[]): boolean { + if (a && !b || !a && b) { + return false; + } + if (!a && !b) { + return true; + } + if (a.length !== b.length) { + return false; + } + for (var i = 0, len = a.length; i < len; i++) { + if (!this.selectionsEqual(a[i], b[i])) { + return false; + } + } + return true; + } + + /** + * Test if `obj` is an `ISelection`. + */ + public static isISelection(obj: any): obj is ISelection { + return ( + obj + && (typeof obj.selectionStartLineNumber === 'number') + && (typeof obj.selectionStartColumn === 'number') + && (typeof obj.positionLineNumber === 'number') + && (typeof obj.positionColumn === 'number') + ); + } + + /** + * Create with a direction. + */ + public static createWithDirection(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, direction: SelectionDirection): Selection { + + if (direction === SelectionDirection.LTR) { + return new Selection(startLineNumber, startColumn, endLineNumber, endColumn); + } + + return new Selection(endLineNumber, endColumn, startLineNumber, startColumn); + } +} diff --git a/src/vs/editor/common/core/stringBuilder.ts b/src/vs/editor/common/core/stringBuilder.ts new file mode 100644 index 0000000000..4e3e2edbb4 --- /dev/null +++ b/src/vs/editor/common/core/stringBuilder.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; + +declare var TextDecoder: any; // TODO@TypeScript +interface TextDecoder { + decode(view: Uint16Array): string; +} + +export interface IStringBuilder { + build(): string; + reset(): void; + write1(charCode: number): void; + appendASCII(charCode: number): void; + appendASCIIString(str: string): void; +} + +export let createStringBuilder: (capacity: number) => IStringBuilder; + +if ((self).TextDecoder) { + createStringBuilder = (capacity) => new StringBuilder(capacity); +} else { + createStringBuilder = (capacity) => new CompatStringBuilder(); +} + +class StringBuilder implements IStringBuilder { + + private readonly _decoder: TextDecoder; + private readonly _capacity: number; + private readonly _buffer: Uint16Array; + + private _completedStrings: string[]; + private _bufferLength: number; + + constructor(capacity: number) { + this._decoder = new TextDecoder('UTF-16LE'); + this._capacity = capacity | 0; + this._buffer = new Uint16Array(this._capacity); + + this._completedStrings = null; + this._bufferLength = 0; + } + + public reset(): void { + this._completedStrings = null; + this._bufferLength = 0; + } + + public build(): string { + if (this._completedStrings !== null) { + this._flushBuffer(); + return this._completedStrings.join(''); + } + return this._buildBuffer(); + } + + private _buildBuffer(): string { + if (this._bufferLength === 0) { + return ''; + } + + const view = new Uint16Array(this._buffer.buffer, 0, this._bufferLength); + return this._decoder.decode(view); + } + + private _flushBuffer(): void { + const bufferString = this._buildBuffer(); + this._bufferLength = 0; + + if (this._completedStrings === null) { + this._completedStrings = [bufferString]; + } else { + this._completedStrings[this._completedStrings.length] = bufferString; + } + } + + public write1(charCode: number): void { + const remainingSpace = this._capacity - this._bufferLength; + + if (remainingSpace <= 1) { + if (remainingSpace === 0 || strings.isHighSurrogate(charCode)) { + this._flushBuffer(); + } + } + + this._buffer[this._bufferLength++] = charCode; + } + + public appendASCII(charCode: number): void { + if (this._bufferLength === this._capacity) { + // buffer is full + this._flushBuffer(); + } + this._buffer[this._bufferLength++] = charCode; + } + + public appendASCIIString(str: string): void { + const strLen = str.length; + + if (this._bufferLength + strLen >= this._capacity) { + // This string does not fit in the remaining buffer space + + this._flushBuffer(); + this._completedStrings[this._completedStrings.length] = str; + return; + } + + for (let i = 0; i < strLen; i++) { + this._buffer[this._bufferLength++] = str.charCodeAt(i); + } + } +} + +class CompatStringBuilder implements IStringBuilder { + + private _pieces: string[]; + private _piecesLen: number; + + constructor() { + this._pieces = []; + this._piecesLen = 0; + } + + public reset(): void { + this._pieces = []; + this._piecesLen = 0; + } + + public build(): string { + return this._pieces.join(''); + } + + public write1(charCode: number): void { + this._pieces[this._piecesLen++] = String.fromCharCode(charCode); + } + + public appendASCII(charCode: number): void { + this._pieces[this._piecesLen++] = String.fromCharCode(charCode); + } + + public appendASCIIString(str: string): void { + this._pieces[this._piecesLen++] = str; + } +} diff --git a/src/vs/editor/common/core/token.ts b/src/vs/editor/common/core/token.ts new file mode 100644 index 0000000000..170ace699f --- /dev/null +++ b/src/vs/editor/common/core/token.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IState } from 'vs/editor/common/modes'; + +export class Token { + _tokenBrand: void; + + public readonly offset: number; + public readonly type: string; + public readonly language: string; + + constructor(offset: number, type: string, language: string) { + this.offset = offset | 0;// @perf + this.type = type; + this.language = language; + } + + public toString(): string { + return '(' + this.offset + ', ' + this.type + ')'; + } +} + +export class TokenizationResult { + _tokenizationResultBrand: void; + + public readonly tokens: Token[]; + public readonly endState: IState; + + constructor(tokens: Token[], endState: IState) { + this.tokens = tokens; + this.endState = endState; + } +} + +export class TokenizationResult2 { + _tokenizationResult2Brand: void; + + /** + * The tokens in binary format. Each token occupies two array indices. For token i: + * - at offset 2*i => startIndex + * - at offset 2*i + 1 => metadata + * + */ + public readonly tokens: Uint32Array; + public readonly endState: IState; + + constructor(tokens: Uint32Array, endState: IState) { + this.tokens = tokens; + this.endState = endState; + } +} diff --git a/src/vs/editor/common/core/uint.ts b/src/vs/editor/common/core/uint.ts new file mode 100644 index 0000000000..e180922e3b --- /dev/null +++ b/src/vs/editor/common/core/uint.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +export class Uint8Matrix { + + private _data: Uint8Array; + private _rows: number; + private _cols: number; + + constructor(rows: number, cols: number, defaultValue: number) { + let data = new Uint8Array(rows * cols); + for (let i = 0, len = rows * cols; i < len; i++) { + data[i] = defaultValue; + } + + this._data = data; + this._rows = rows; + this._cols = cols; + } + + public get(row: number, col: number): number { + return this._data[row * this._cols + col]; + } + + public set(row: number, col: number, value: number): void { + this._data[row * this._cols + col] = value; + } +} + +export const enum Constants { + /** + * MAX SMI (SMall Integer) as defined in v8. + * one bit is lost for boxing/unboxing flag. + * one bit is lost for sign flag. + * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values + */ + MAX_SAFE_SMALL_INTEGER = 1 << 30, + + /** + * MIN SMI (SMall Integer) as defined in v8. + * one bit is lost for boxing/unboxing flag. + * one bit is lost for sign flag. + * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values + */ + MIN_SAFE_SMALL_INTEGER = -(1 << 30), + + /** + * Max unsigned integer that fits on 8 bits. + */ + MAX_UINT_8 = 255, // 2^8 - 1 + + /** + * Max unsigned integer that fits on 16 bits. + */ + MAX_UINT_16 = 65535, // 2^16 - 1 + + /** + * Max unsigned integer that fits on 32 bits. + */ + MAX_UINT_32 = 4294967295, // 2^32 - 1 + + +} + +export function toUint8(v: number): number { + if (v < 0) { + return 0; + } + if (v > Constants.MAX_UINT_8) { + return Constants.MAX_UINT_8; + } + return v | 0; +} + +export function toUint32(v: number): number { + if (v < 0) { + return 0; + } + if (v > Constants.MAX_UINT_32) { + return Constants.MAX_UINT_32; + } + return v | 0; +} + +export function toUint32Array(arr: number[]): Uint32Array { + let len = arr.length; + let r = new Uint32Array(len); + for (let i = 0; i < len; i++) { + r[i] = toUint32(arr[i]); + } + return r; +} diff --git a/src/vs/editor/common/core/viewLineToken.ts b/src/vs/editor/common/core/viewLineToken.ts new file mode 100644 index 0000000000..52a11b4be2 --- /dev/null +++ b/src/vs/editor/common/core/viewLineToken.ts @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ColorId } from 'vs/editor/common/modes'; +import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding'; + +/** + * A token on a line. + */ +export class ViewLineToken { + _viewLineTokenBrand: void; + + /** + * last char index of this token (not inclusive). + */ + public readonly endIndex: number; + private readonly _metadata: number; + + constructor(endIndex: number, metadata: number) { + this.endIndex = endIndex; + this._metadata = metadata; + } + + public getForeground(): ColorId { + return TokenMetadata.getForeground(this._metadata); + } + + public getType(): string { + return TokenMetadata.getClassNameFromMetadata(this._metadata); + } + + public getInlineStyle(colorMap: string[]): string { + return TokenMetadata.getInlineStyleFromMetadata(this._metadata, colorMap); + } + + private static _equals(a: ViewLineToken, b: ViewLineToken): boolean { + return ( + a.endIndex === b.endIndex + && a._metadata === b._metadata + ); + } + + public static equalsArr(a: ViewLineToken[], b: ViewLineToken[]): boolean { + const aLen = a.length; + const bLen = b.length; + if (aLen !== bLen) { + return false; + } + for (let i = 0; i < aLen; i++) { + if (!this._equals(a[i], b[i])) { + return false; + } + } + return true; + } +} + +export class ViewLineTokenFactory { + + public static inflateArr(tokens: Uint32Array, lineLength: number): ViewLineToken[] { + let result: ViewLineToken[] = []; + + for (let i = 0, len = (tokens.length >>> 1); i < len; i++) { + let endOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength); + let metadata = tokens[(i << 1) + 1]; + + result[i] = new ViewLineToken(endOffset, metadata); + } + + return result; + } + + public static sliceAndInflate(tokens: Uint32Array, startOffset: number, endOffset: number, deltaOffset: number, lineLength: number): ViewLineToken[] { + const tokenIndex = this.findIndexInSegmentsArray(tokens, startOffset); + const maxEndOffset = (endOffset - startOffset + deltaOffset); + let result: ViewLineToken[] = [], resultLen = 0; + + for (let i = tokenIndex, len = (tokens.length >>> 1); i < len; i++) { + let tokenStartOffset = tokens[(i << 1)]; + + if (tokenStartOffset >= endOffset) { + break; + } + + let tokenEndOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength); + let newEndOffset = Math.min(maxEndOffset, tokenEndOffset - startOffset + deltaOffset); + let metadata = tokens[(i << 1) + 1]; + + result[resultLen++] = new ViewLineToken(newEndOffset, metadata); + } + + return result; + } + + public static findIndexInSegmentsArray(tokens: Uint32Array, desiredIndex: number): number { + + let low = 0; + let high = (tokens.length >>> 1) - 1; + + while (low < high) { + + let mid = low + Math.ceil((high - low) / 2); + + let value = tokens[(mid << 1)]; + + if (value > desiredIndex) { + high = mid - 1; + } else { + low = mid; + } + } + + return low; + } +} diff --git a/src/vs/editor/common/diff/diffComputer.ts b/src/vs/editor/common/diff/diffComputer.ts new file mode 100644 index 0000000000..0a6481db38 --- /dev/null +++ b/src/vs/editor/common/diff/diffComputer.ts @@ -0,0 +1,554 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDiffChange, ISequence, LcsDiff } from 'vs/base/common/diff/diff'; +import * as strings from 'vs/base/common/strings'; +import { ICharChange, ILineChange } from 'vs/editor/common/editorCommon'; + +const MAXIMUM_RUN_TIME = 5000; // 5 seconds +const MINIMUM_MATCHING_CHARACTER_LENGTH = 3; + +interface IMarker { + lineNumber: number; + column: number; + offset: number; +} + +function computeDiff(originalSequence: ISequence, modifiedSequence: ISequence, continueProcessingPredicate: () => boolean, pretty: boolean): IDiffChange[] { + const diffAlgo = new LcsDiff(originalSequence, modifiedSequence, continueProcessingPredicate); + return diffAlgo.ComputeDiff(pretty); +} + +class MarkerSequence implements ISequence { + + public buffer: string; + public startMarkers: IMarker[]; + public endMarkers: IMarker[]; + + constructor(buffer: string, startMarkers: IMarker[], endMarkers: IMarker[]) { + this.buffer = buffer; + this.startMarkers = startMarkers; + this.endMarkers = endMarkers; + } + + public equals(other: any): boolean { + if (!(other instanceof MarkerSequence)) { + return false; + } + const otherMarkerSequence = other; + if (this.getLength() !== otherMarkerSequence.getLength()) { + return false; + } + for (let i = 0, len = this.getLength(); i < len; i++) { + const myElement = this.getElementHash(i); + const otherElement = otherMarkerSequence.getElementHash(i); + if (myElement !== otherElement) { + return false; + } + } + return true; + } + + public getLength(): number { + return this.startMarkers.length; + } + + public getElementHash(i: number): string { + return this.buffer.substring(this.startMarkers[i].offset, this.endMarkers[i].offset); + } + + public getStartLineNumber(i: number): number { + if (i === this.startMarkers.length) { + // This is the special case where a change happened after the last marker + return this.startMarkers[i - 1].lineNumber + 1; + } + return this.startMarkers[i].lineNumber; + } + + public getStartColumn(i: number): number { + return this.startMarkers[i].column; + } + + public getEndLineNumber(i: number): number { + return this.endMarkers[i].lineNumber; + } + + public getEndColumn(i: number): number { + return this.endMarkers[i].column; + } + +} + +class LineMarkerSequence extends MarkerSequence { + + constructor(lines: string[]) { + let buffer = ''; + let startMarkers: IMarker[] = []; + let endMarkers: IMarker[] = []; + + for (let pos = 0, i = 0, length = lines.length; i < length; i++) { + buffer += lines[i]; + const startColumn = LineMarkerSequence._getFirstNonBlankColumn(lines[i], 1); + const endColumn = LineMarkerSequence._getLastNonBlankColumn(lines[i], 1); + + startMarkers.push({ + offset: pos + startColumn - 1, + lineNumber: i + 1, + column: startColumn + }); + + endMarkers.push({ + offset: pos + endColumn - 1, + lineNumber: i + 1, + column: endColumn + }); + + pos += lines[i].length; + } + + super(buffer, startMarkers, endMarkers); + } + + public static _getFirstNonBlankColumn(txt: string, defaultValue: number): number { + const r = strings.firstNonWhitespaceIndex(txt); + if (r === -1) { + return defaultValue; + } + return r + 1; + } + + public static _getLastNonBlankColumn(txt: string, defaultValue: number): number { + const r = strings.lastNonWhitespaceIndex(txt); + if (r === -1) { + return defaultValue; + } + return r + 2; + } + + public getCharSequence(startIndex: number, endIndex: number): MarkerSequence { + let startMarkers: IMarker[] = []; + let endMarkers: IMarker[] = []; + for (let index = startIndex; index <= endIndex; index++) { + const startMarker = this.startMarkers[index]; + const endMarker = this.endMarkers[index]; + for (let i = startMarker.offset; i < endMarker.offset; i++) { + startMarkers.push({ + offset: i, + lineNumber: startMarker.lineNumber, + column: startMarker.column + (i - startMarker.offset) + }); + endMarkers.push({ + offset: i + 1, + lineNumber: startMarker.lineNumber, + column: startMarker.column + (i - startMarker.offset) + 1 + }); + } + } + return new MarkerSequence(this.buffer, startMarkers, endMarkers); + } +} + +class CharChange implements ICharChange { + + public originalStartLineNumber: number; + public originalStartColumn: number; + public originalEndLineNumber: number; + public originalEndColumn: number; + + public modifiedStartLineNumber: number; + public modifiedStartColumn: number; + public modifiedEndLineNumber: number; + public modifiedEndColumn: number; + + constructor( + originalStartLineNumber: number, + originalStartColumn: number, + originalEndLineNumber: number, + originalEndColumn: number, + modifiedStartLineNumber: number, + modifiedStartColumn: number, + modifiedEndLineNumber: number, + modifiedEndColumn: number + ) { + this.originalStartLineNumber = originalStartLineNumber; + this.originalStartColumn = originalStartColumn; + this.originalEndLineNumber = originalEndLineNumber; + this.originalEndColumn = originalEndColumn; + this.modifiedStartLineNumber = modifiedStartLineNumber; + this.modifiedStartColumn = modifiedStartColumn; + this.modifiedEndLineNumber = modifiedEndLineNumber; + this.modifiedEndColumn = modifiedEndColumn; + } + + public static createFromDiffChange(diffChange: IDiffChange, originalCharSequence: MarkerSequence, modifiedCharSequence: MarkerSequence): CharChange { + let originalStartLineNumber: number; + let originalStartColumn: number; + let originalEndLineNumber: number; + let originalEndColumn: number; + let modifiedStartLineNumber: number; + let modifiedStartColumn: number; + let modifiedEndLineNumber: number; + let modifiedEndColumn: number; + + if (diffChange.originalLength === 0) { + originalStartLineNumber = 0; + originalStartColumn = 0; + originalEndLineNumber = 0; + originalEndColumn = 0; + } else { + originalStartLineNumber = originalCharSequence.getStartLineNumber(diffChange.originalStart); + originalStartColumn = originalCharSequence.getStartColumn(diffChange.originalStart); + originalEndLineNumber = originalCharSequence.getEndLineNumber(diffChange.originalStart + diffChange.originalLength - 1); + originalEndColumn = originalCharSequence.getEndColumn(diffChange.originalStart + diffChange.originalLength - 1); + } + + if (diffChange.modifiedLength === 0) { + modifiedStartLineNumber = 0; + modifiedStartColumn = 0; + modifiedEndLineNumber = 0; + modifiedEndColumn = 0; + } else { + modifiedStartLineNumber = modifiedCharSequence.getStartLineNumber(diffChange.modifiedStart); + modifiedStartColumn = modifiedCharSequence.getStartColumn(diffChange.modifiedStart); + modifiedEndLineNumber = modifiedCharSequence.getEndLineNumber(diffChange.modifiedStart + diffChange.modifiedLength - 1); + modifiedEndColumn = modifiedCharSequence.getEndColumn(diffChange.modifiedStart + diffChange.modifiedLength - 1); + } + + return new CharChange( + originalStartLineNumber, originalStartColumn, originalEndLineNumber, originalEndColumn, + modifiedStartLineNumber, modifiedStartColumn, modifiedEndLineNumber, modifiedEndColumn, + ); + } +} + +function postProcessCharChanges(rawChanges: IDiffChange[]): IDiffChange[] { + if (rawChanges.length <= 1) { + return rawChanges; + } + + let result = [rawChanges[0]]; + let prevChange = result[0]; + + for (let i = 1, len = rawChanges.length; i < len; i++) { + const currChange = rawChanges[i]; + + const originalMatchingLength = currChange.originalStart - (prevChange.originalStart + prevChange.originalLength); + const modifiedMatchingLength = currChange.modifiedStart - (prevChange.modifiedStart + prevChange.modifiedLength); + // Both of the above should be equal, but the continueProcessingPredicate may prevent this from being true + const matchingLength = Math.min(originalMatchingLength, modifiedMatchingLength); + + if (matchingLength < MINIMUM_MATCHING_CHARACTER_LENGTH) { + // Merge the current change into the previous one + prevChange.originalLength = (currChange.originalStart + currChange.originalLength) - prevChange.originalStart; + prevChange.modifiedLength = (currChange.modifiedStart + currChange.modifiedLength) - prevChange.modifiedStart; + } else { + // Add the current change + result.push(currChange); + prevChange = currChange; + } + } + + return result; +} + +class LineChange implements ILineChange { + public originalStartLineNumber: number; + public originalEndLineNumber: number; + public modifiedStartLineNumber: number; + public modifiedEndLineNumber: number; + public charChanges: CharChange[]; + + constructor( + originalStartLineNumber: number, + originalEndLineNumber: number, + modifiedStartLineNumber: number, + modifiedEndLineNumber: number, + charChanges: CharChange[] + ) { + this.originalStartLineNumber = originalStartLineNumber; + this.originalEndLineNumber = originalEndLineNumber; + this.modifiedStartLineNumber = modifiedStartLineNumber; + this.modifiedEndLineNumber = modifiedEndLineNumber; + this.charChanges = charChanges; + } + + public static createFromDiffResult(diffChange: IDiffChange, originalLineSequence: LineMarkerSequence, modifiedLineSequence: LineMarkerSequence, continueProcessingPredicate: () => boolean, shouldPostProcessCharChanges: boolean): LineChange { + let originalStartLineNumber: number; + let originalEndLineNumber: number; + let modifiedStartLineNumber: number; + let modifiedEndLineNumber: number; + let charChanges: CharChange[]; + + if (diffChange.originalLength === 0) { + originalStartLineNumber = originalLineSequence.getStartLineNumber(diffChange.originalStart) - 1; + originalEndLineNumber = 0; + } else { + originalStartLineNumber = originalLineSequence.getStartLineNumber(diffChange.originalStart); + originalEndLineNumber = originalLineSequence.getEndLineNumber(diffChange.originalStart + diffChange.originalLength - 1); + } + + if (diffChange.modifiedLength === 0) { + modifiedStartLineNumber = modifiedLineSequence.getStartLineNumber(diffChange.modifiedStart) - 1; + modifiedEndLineNumber = 0; + } else { + modifiedStartLineNumber = modifiedLineSequence.getStartLineNumber(diffChange.modifiedStart); + modifiedEndLineNumber = modifiedLineSequence.getEndLineNumber(diffChange.modifiedStart + diffChange.modifiedLength - 1); + } + + if (diffChange.originalLength !== 0 && diffChange.modifiedLength !== 0 && continueProcessingPredicate()) { + const originalCharSequence = originalLineSequence.getCharSequence(diffChange.originalStart, diffChange.originalStart + diffChange.originalLength - 1); + const modifiedCharSequence = modifiedLineSequence.getCharSequence(diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength - 1); + + let rawChanges = computeDiff(originalCharSequence, modifiedCharSequence, continueProcessingPredicate, false); + + if (shouldPostProcessCharChanges) { + rawChanges = postProcessCharChanges(rawChanges); + } + + charChanges = []; + for (let i = 0, length = rawChanges.length; i < length; i++) { + charChanges.push(CharChange.createFromDiffChange(rawChanges[i], originalCharSequence, modifiedCharSequence)); + } + } + + return new LineChange(originalStartLineNumber, originalEndLineNumber, modifiedStartLineNumber, modifiedEndLineNumber, charChanges); + } +} + +export interface IDiffComputerOpts { + shouldPostProcessCharChanges: boolean; + shouldIgnoreTrimWhitespace: boolean; + shouldConsiderTrimWhitespaceInEmptyCase: boolean; + shouldMakePrettyDiff: boolean; +} + +export class DiffComputer { + + private readonly shouldPostProcessCharChanges: boolean; + private readonly shouldIgnoreTrimWhitespace: boolean; + private readonly shouldMakePrettyDiff: boolean; + private readonly maximumRunTimeMs: number; + private readonly originalLines: string[]; + private readonly modifiedLines: string[]; + private readonly original: LineMarkerSequence; + private readonly modified: LineMarkerSequence; + + private computationStartTime: number; + + constructor(originalLines: string[], modifiedLines: string[], opts: IDiffComputerOpts) { + this.shouldPostProcessCharChanges = opts.shouldPostProcessCharChanges; + this.shouldIgnoreTrimWhitespace = opts.shouldIgnoreTrimWhitespace; + this.shouldMakePrettyDiff = opts.shouldMakePrettyDiff; + this.maximumRunTimeMs = MAXIMUM_RUN_TIME; + this.originalLines = originalLines; + this.modifiedLines = modifiedLines; + this.original = new LineMarkerSequence(originalLines); + this.modified = new LineMarkerSequence(modifiedLines); + + if (opts.shouldConsiderTrimWhitespaceInEmptyCase && this.shouldIgnoreTrimWhitespace && this.original.equals(this.modified)) { + // Diff would be empty with `shouldIgnoreTrimWhitespace` + this.shouldIgnoreTrimWhitespace = false; + } + } + + public computeDiff(): ILineChange[] { + + if (this.original.getLength() === 1 && this.original.getElementHash(0).length === 0) { + // empty original => fast path + return [{ + originalStartLineNumber: 1, + originalEndLineNumber: 1, + modifiedStartLineNumber: 1, + modifiedEndLineNumber: this.modified.getLength(), + charChanges: [{ + modifiedEndColumn: 0, + modifiedEndLineNumber: 0, + modifiedStartColumn: 0, + modifiedStartLineNumber: 0, + originalEndColumn: 0, + originalEndLineNumber: 0, + originalStartColumn: 0, + originalStartLineNumber: 0 + }] + }]; + } + + if (this.modified.getLength() === 1 && this.modified.getElementHash(0).length === 0) { + // empty modified => fast path + return [{ + originalStartLineNumber: 1, + originalEndLineNumber: this.original.getLength(), + modifiedStartLineNumber: 1, + modifiedEndLineNumber: 1, + charChanges: [{ + modifiedEndColumn: 0, + modifiedEndLineNumber: 0, + modifiedStartColumn: 0, + modifiedStartLineNumber: 0, + originalEndColumn: 0, + originalEndLineNumber: 0, + originalStartColumn: 0, + originalStartLineNumber: 0 + }] + }]; + } + + this.computationStartTime = (new Date()).getTime(); + + let rawChanges = computeDiff(this.original, this.modified, this._continueProcessingPredicate.bind(this), this.shouldMakePrettyDiff); + + // The diff is always computed with ignoring trim whitespace + // This ensures we get the prettiest diff + + if (this.shouldIgnoreTrimWhitespace) { + let lineChanges: LineChange[] = []; + for (let i = 0, length = rawChanges.length; i < length; i++) { + lineChanges.push(LineChange.createFromDiffResult(rawChanges[i], this.original, this.modified, this._continueProcessingPredicate.bind(this), this.shouldPostProcessCharChanges)); + } + return lineChanges; + } + + // Need to post-process and introduce changes where the trim whitespace is different + // Note that we are looping starting at -1 to also cover the lines before the first change + let result: LineChange[] = []; + + let originalLineIndex = 0; + let modifiedLineIndex = 0; + for (let i = -1 /* !!!! */, len = rawChanges.length; i < len; i++) { + const nextChange = (i + 1 < len ? rawChanges[i + 1] : null); + const originalStop = (nextChange ? nextChange.originalStart : this.originalLines.length); + const modifiedStop = (nextChange ? nextChange.modifiedStart : this.modifiedLines.length); + + while (originalLineIndex < originalStop && modifiedLineIndex < modifiedStop) { + const originalLine = this.originalLines[originalLineIndex]; + const modifiedLine = this.modifiedLines[modifiedLineIndex]; + + if (originalLine !== modifiedLine) { + // These lines differ only in trim whitespace + + // Check the leading whitespace + { + let originalStartColumn = LineMarkerSequence._getFirstNonBlankColumn(originalLine, 1); + let modifiedStartColumn = LineMarkerSequence._getFirstNonBlankColumn(modifiedLine, 1); + while (originalStartColumn > 1 && modifiedStartColumn > 1) { + const originalChar = originalLine.charCodeAt(originalStartColumn - 2); + const modifiedChar = modifiedLine.charCodeAt(modifiedStartColumn - 2); + if (originalChar !== modifiedChar) { + break; + } + originalStartColumn--; + modifiedStartColumn--; + } + + if (originalStartColumn > 1 || modifiedStartColumn > 1) { + this._pushTrimWhitespaceCharChange(result, + originalLineIndex + 1, 1, originalStartColumn, + modifiedLineIndex + 1, 1, modifiedStartColumn + ); + } + } + + // Check the trailing whitespace + { + let originalEndColumn = LineMarkerSequence._getLastNonBlankColumn(originalLine, 1); + let modifiedEndColumn = LineMarkerSequence._getLastNonBlankColumn(modifiedLine, 1); + const originalMaxColumn = originalLine.length + 1; + const modifiedMaxColumn = modifiedLine.length + 1; + while (originalEndColumn < originalMaxColumn && modifiedEndColumn < modifiedMaxColumn) { + const originalChar = originalLine.charCodeAt(originalEndColumn - 1); + const modifiedChar = originalLine.charCodeAt(modifiedEndColumn - 1); + if (originalChar !== modifiedChar) { + break; + } + originalEndColumn++; + modifiedEndColumn++; + } + + if (originalEndColumn < originalMaxColumn || modifiedEndColumn < modifiedMaxColumn) { + this._pushTrimWhitespaceCharChange(result, + originalLineIndex + 1, originalEndColumn, originalMaxColumn, + modifiedLineIndex + 1, modifiedEndColumn, modifiedMaxColumn + ); + } + } + } + originalLineIndex++; + modifiedLineIndex++; + } + + if (nextChange) { + // Emit the actual change + result.push(LineChange.createFromDiffResult(nextChange, this.original, this.modified, this._continueProcessingPredicate.bind(this), this.shouldPostProcessCharChanges)); + + originalLineIndex += nextChange.originalLength; + modifiedLineIndex += nextChange.modifiedLength; + } + } + + return result; + } + + private _pushTrimWhitespaceCharChange( + result: LineChange[], + originalLineNumber: number, originalStartColumn: number, originalEndColumn: number, + modifiedLineNumber: number, modifiedStartColumn: number, modifiedEndColumn: number + ): void { + if (this._mergeTrimWhitespaceCharChange(result, originalLineNumber, originalStartColumn, originalEndColumn, modifiedLineNumber, modifiedStartColumn, modifiedEndColumn)) { + // Merged into previous + return; + } + + result.push(new LineChange( + originalLineNumber, originalLineNumber, + modifiedLineNumber, modifiedLineNumber, + [ + new CharChange( + originalLineNumber, originalStartColumn, originalLineNumber, originalEndColumn, + modifiedLineNumber, modifiedStartColumn, modifiedLineNumber, modifiedEndColumn + ) + ] + )); + } + + private _mergeTrimWhitespaceCharChange( + result: LineChange[], + originalLineNumber: number, originalStartColumn: number, originalEndColumn: number, + modifiedLineNumber: number, modifiedStartColumn: number, modifiedEndColumn: number + ): boolean { + const len = result.length; + if (len === 0) { + return false; + } + + const prevChange = result[len - 1]; + + if (prevChange.originalEndLineNumber === 0 || prevChange.modifiedEndLineNumber === 0) { + // Don't merge with inserts/deletes + return false; + } + + if (prevChange.originalEndLineNumber + 1 === originalLineNumber && prevChange.modifiedEndLineNumber + 1 === modifiedLineNumber) { + prevChange.originalEndLineNumber = originalLineNumber; + prevChange.modifiedEndLineNumber = modifiedLineNumber; + prevChange.charChanges.push(new CharChange( + originalLineNumber, originalStartColumn, originalLineNumber, originalEndColumn, + modifiedLineNumber, modifiedStartColumn, modifiedLineNumber, modifiedEndColumn + )); + return true; + } + + return false; + } + + private _continueProcessingPredicate(): boolean { + if (this.maximumRunTimeMs === 0) { + return true; + } + const now = (new Date()).getTime(); + return now - this.computationStartTime < this.maximumRunTimeMs; + } + +} diff --git a/src/vs/editor/common/diff/nullDiffComputer.ts b/src/vs/editor/common/diff/nullDiffComputer.ts new file mode 100644 index 0000000000..d89feeead1 --- /dev/null +++ b/src/vs/editor/common/diff/nullDiffComputer.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ILineChange } from 'vs/editor/common/editorCommon'; + +export class DiffComputer { + + constructor(originalLines: string[], modifiedLines: string[], shouldPostProcessCharChanges: boolean, shouldIgnoreTrimWhitespace: boolean) { + } + + public computeDiff(): ILineChange[] { + return []; + } +} \ No newline at end of file diff --git a/src/vs/editor/common/editorAction.ts b/src/vs/editor/common/editorAction.ts new file mode 100644 index 0000000000..d41c010bb0 --- /dev/null +++ b/src/vs/editor/common/editorAction.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IEditorAction } from 'vs/editor/common/editorCommon'; +import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; + +export class InternalEditorAction implements IEditorAction { + + public readonly id: string; + public readonly label: string; + public readonly alias: string; + + private readonly _precondition: ContextKeyExpr; + private readonly _run: () => void | TPromise; + private readonly _contextKeyService: IContextKeyService; + + constructor( + id: string, + label: string, + alias: string, + precondition: ContextKeyExpr, + run: () => void, + contextKeyService: IContextKeyService + ) { + this.id = id; + this.label = label; + this.alias = alias; + this._precondition = precondition; + this._run = run; + this._contextKeyService = contextKeyService; + } + + public isSupported(): boolean { + return this._contextKeyService.contextMatchesRules(this._precondition); + } + + public run(): TPromise { + if (!this.isSupported()) { + return TPromise.as(void 0); + } + + const r = this._run(); + return r ? r : TPromise.as(void 0); + } +} diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts new file mode 100644 index 0000000000..13f02ae0f9 --- /dev/null +++ b/src/vs/editor/common/editorCommon.ts @@ -0,0 +1,2105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { BulkListenerCallback } from 'vs/base/common/eventEmitter'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { Selection, ISelection } from 'vs/editor/common/core/selection'; +import { IndentRange } from 'vs/editor/common/model/indentRanges'; +import { ITextSource } from 'vs/editor/common/model/textSource'; +import { + ModelRawContentChangedEvent, IModelContentChangedEvent, IModelDecorationsChangedEvent, + IModelLanguageChangedEvent, IModelOptionsChangedEvent +} from 'vs/editor/common/model/textModelEvents'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { ICursors, CursorConfiguration } from 'vs/editor/common/controller/cursorCommon'; +import { ThemeColor } from 'vs/platform/theme/common/themeService'; + +/** + * Vertical Lane in the overview ruler of the editor. + */ +export enum OverviewRulerLane { + Left = 1, + Center = 2, + Right = 4, + Full = 7 +} + +/** + * Options for rendering a model decoration in the overview ruler. + */ +export interface IModelDecorationOverviewRulerOptions { + /** + * CSS color to render in the overview ruler. + * e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry + */ + color: string | ThemeColor; + /** + * CSS color to render in the overview ruler. + * e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry + */ + darkColor: string | ThemeColor; + /** + * CSS color to render in the overview ruler. + * e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry + */ + hcColor?: string | ThemeColor; + /** + * The position in the overview ruler. + */ + position: OverviewRulerLane; +} + +/** + * Options for a model decoration. + */ +export interface IModelDecorationOptions { + /** + * Customize the growing behavior of the decoration when typing at the edges of the decoration. + * Defaults to TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges + */ + stickiness?: TrackedRangeStickiness; + /** + * CSS class name describing the decoration. + */ + className?: string; + /** + * Message to be rendered when hovering over the glyph margin decoration. + */ + glyphMarginHoverMessage?: IMarkdownString | IMarkdownString[]; + /** + * Array of MarkdownString to render as the decoration message. + */ + hoverMessage?: IMarkdownString | IMarkdownString[]; + /** + * Should the decoration expand to encompass a whole line. + */ + isWholeLine?: boolean; + /** + * Always render the decoration (even when the range it encompasses is collapsed). + * @internal + */ + readonly showIfCollapsed?: boolean; + /** + * If set, render this decoration in the overview ruler. + */ + overviewRuler?: IModelDecorationOverviewRulerOptions; + /** + * If set, the decoration will be rendered in the glyph margin with this CSS class name. + */ + glyphMarginClassName?: string; + /** + * If set, the decoration will be rendered in the lines decorations with this CSS class name. + */ + linesDecorationsClassName?: string; + /** + * If set, the decoration will be rendered in the margin (covering its full width) with this CSS class name. + */ + marginClassName?: string; + /** + * If set, the decoration will be rendered inline with the text with this CSS class name. + * Please use this only for CSS rules that must impact the text. For example, use `className` + * to have a background color decoration. + */ + inlineClassName?: string; + /** + * If set, the decoration will be rendered before the text with this CSS class name. + */ + beforeContentClassName?: string; + /** + * If set, the decoration will be rendered after the text with this CSS class name. + */ + afterContentClassName?: string; +} + +/** + * New model decorations. + */ +export interface IModelDeltaDecoration { + /** + * Range that this decoration covers. + */ + range: IRange; + /** + * Options associated with this decoration. + */ + options: IModelDecorationOptions; +} + +/** + * A decoration in the model. + */ +export interface IModelDecoration { + /** + * Identifier for a decoration. + */ + readonly id: string; + /** + * Identifier for a decoration's owener. + */ + readonly ownerId: number; + /** + * Range that this decoration covers. + */ + readonly range: Range; + /** + * Options associated with this decoration. + */ + readonly options: IModelDecorationOptions; + /** + * A flag describing if this is a problem decoration (e.g. warning/error). + */ + readonly isForValidation: boolean; +} + +/** + * An accessor that can add, change or remove model decorations. + * @internal + */ +export interface IModelDecorationsChangeAccessor { + /** + * Add a new decoration. + * @param range Range that this decoration covers. + * @param options Options associated with this decoration. + * @return An unique identifier associated with this decoration. + */ + addDecoration(range: IRange, options: IModelDecorationOptions): string; + /** + * Change the range that an existing decoration covers. + * @param id The unique identifier associated with the decoration. + * @param newRange The new range that this decoration covers. + */ + changeDecoration(id: string, newRange: IRange): void; + /** + * Change the options associated with an existing decoration. + * @param id The unique identifier associated with the decoration. + * @param newOptions The new options associated with this decoration. + */ + changeDecorationOptions(id: string, newOptions: IModelDecorationOptions): void; + /** + * Remove an existing decoration. + * @param id The unique identifier associated with the decoration. + */ + removeDecoration(id: string): void; + /** + * Perform a minimum ammount of operations, in order to transform the decorations + * identified by `oldDecorations` to the decorations described by `newDecorations` + * and returns the new identifiers associated with the resulting decorations. + * + * @param oldDecorations Array containing previous decorations identifiers. + * @param newDecorations Array describing what decorations should result after the call. + * @return An array containing the new decorations identifiers. + */ + deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[]; +} + +/** + * Word inside a model. + */ +export interface IWordAtPosition { + /** + * The word. + */ + readonly word: string; + /** + * The column where the word starts. + */ + readonly startColumn: number; + /** + * The column where the word ends. + */ + readonly endColumn: number; +} + +/** + * End of line character preference. + */ +export enum EndOfLinePreference { + /** + * Use the end of line character identified in the text buffer. + */ + TextDefined = 0, + /** + * Use line feed (\n) as the end of line character. + */ + LF = 1, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 2 +} + +/** + * The default end of line to use when instantiating models. + */ +export enum DefaultEndOfLine { + /** + * Use line feed (\n) as the end of line character. + */ + LF = 1, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 2 +} + +/** + * End of line character preference. + */ +export enum EndOfLineSequence { + /** + * Use line feed (\n) as the end of line character. + */ + LF = 0, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 1 +} + +/** + * An identifier for a single edit operation. + */ +export interface ISingleEditOperationIdentifier { + /** + * Identifier major + */ + major: number; + /** + * Identifier minor + */ + minor: number; +} + +/** + * A builder and helper for edit operations for a command. + */ +export interface IEditOperationBuilder { + /** + * Add a new edit operation (a replace operation). + * @param range The range to replace (delete). May be empty to represent a simple insert. + * @param text The text to replace with. May be null to represent a simple delete. + */ + addEditOperation(range: Range, text: string): void; + + /** + * Add a new edit operation (a replace operation). + * The inverse edits will be accessible in `ICursorStateComputerData.getInverseEditOperations()` + * @param range The range to replace (delete). May be empty to represent a simple insert. + * @param text The text to replace with. May be null to represent a simple delete. + */ + addTrackedEditOperation(range: Range, text: string): void; + + /** + * Track `selection` when applying edit operations. + * A best effort will be made to not grow/expand the selection. + * An empty selection will clamp to a nearby character. + * @param selection The selection to track. + * @param trackPreviousOnEmpty If set, and the selection is empty, indicates whether the selection + * should clamp to the previous or the next character. + * @return A unique identifer. + */ + trackSelection(selection: Selection, trackPreviousOnEmpty?: boolean): string; +} + +/** + * A helper for computing cursor state after a command. + */ +export interface ICursorStateComputerData { + /** + * Get the inverse edit operations of the added edit operations. + */ + getInverseEditOperations(): IIdentifiedSingleEditOperation[]; + /** + * Get a previously tracked selection. + * @param id The unique identifier returned by `trackSelection`. + * @return The selection. + */ + getTrackedSelection(id: string): Selection; +} + +/** + * A command that modifies text / cursor state on a model. + */ +export interface ICommand { + + /** + * Signal that this command is inserting automatic whitespace that should be trimmed if possible. + * @internal + */ + readonly insertsAutoWhitespace?: boolean; + + /** + * Get the edit operations needed to execute this command. + * @param model The model the command will execute on. + * @param builder A helper to collect the needed edit operations and to track selections. + */ + getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void; + + /** + * Compute the cursor state after the edit operations were applied. + * @param model The model the commad has executed on. + * @param helper A helper to get inverse edit operations and to get previously tracked selections. + * @return The cursor state after the command executed. + */ + computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection; +} + +/** + * A single edit operation, that acts as a simple replace. + * i.e. Replace text at `range` with `text` in model. + */ +export interface ISingleEditOperation { + /** + * The range to replace. This can be empty to emulate a simple insert. + */ + range: IRange; + /** + * The text to replace with. This can be null to emulate a simple delete. + */ + text: string; + /** + * This indicates that this operation has "insert" semantics. + * i.e. forceMoveMarkers = true => if `range` is collapsed, all markers at the position will be moved. + */ + forceMoveMarkers?: boolean; +} + +/** + * A single edit operation, that has an identifier. + */ +export interface IIdentifiedSingleEditOperation { + /** + * An identifier associated with this single edit operation. + */ + identifier: ISingleEditOperationIdentifier; + /** + * The range to replace. This can be empty to emulate a simple insert. + */ + range: Range; + /** + * The text to replace with. This can be null to emulate a simple delete. + */ + text: string; + /** + * This indicates that this operation has "insert" semantics. + * i.e. forceMoveMarkers = true => if `range` is collapsed, all markers at the position will be moved. + */ + forceMoveMarkers: boolean; + /** + * This indicates that this operation is inserting automatic whitespace + * that can be removed on next model edit operation if `config.trimAutoWhitespace` is true. + */ + isAutoWhitespaceEdit?: boolean; + /** + * This indicates that this operation is in a set of operations that are tracked and should not be "simplified". + * @internal + */ + _isTracked?: boolean; +} + +/** + * A callback that can compute the cursor state after applying a series of edit operations. + */ +export interface ICursorStateComputer { + /** + * A callback that can compute the resulting cursors state after some edit operations have been executed. + */ + (inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[]; +} + +export class TextModelResolvedOptions { + _textModelResolvedOptionsBrand: void; + + readonly tabSize: number; + readonly insertSpaces: boolean; + readonly defaultEOL: DefaultEndOfLine; + readonly trimAutoWhitespace: boolean; + + /** + * @internal + */ + constructor(src: { + tabSize: number; + insertSpaces: boolean; + defaultEOL: DefaultEndOfLine; + trimAutoWhitespace: boolean; + }) { + this.tabSize = src.tabSize | 0; + this.insertSpaces = Boolean(src.insertSpaces); + this.defaultEOL = src.defaultEOL | 0; + this.trimAutoWhitespace = Boolean(src.trimAutoWhitespace); + } + + /** + * @internal + */ + public equals(other: TextModelResolvedOptions): boolean { + return ( + this.tabSize === other.tabSize + && this.insertSpaces === other.insertSpaces + && this.defaultEOL === other.defaultEOL + && this.trimAutoWhitespace === other.trimAutoWhitespace + ); + } + + /** + * @internal + */ + public createChangeEvent(newOpts: TextModelResolvedOptions): IModelOptionsChangedEvent { + return { + tabSize: this.tabSize !== newOpts.tabSize, + insertSpaces: this.insertSpaces !== newOpts.insertSpaces, + trimAutoWhitespace: this.trimAutoWhitespace !== newOpts.trimAutoWhitespace, + }; + } +} + +/** + * @internal + */ +export interface ITextModelCreationOptions { + tabSize: number; + insertSpaces: boolean; + detectIndentation: boolean; + trimAutoWhitespace: boolean; + defaultEOL: DefaultEndOfLine; +} + +export interface ITextModelUpdateOptions { + tabSize?: number; + insertSpaces?: boolean; + trimAutoWhitespace?: boolean; +} + +/** + * A textual read-only model. + */ +export interface ITextModel { + + /** + * If true, the text model might contain RTL. + * If false, the text model **contains only** contain LTR. + * @internal + */ + mightContainRTL(): boolean; + + /** + * If true, the text model might contain non basic ASCII. + * If false, the text model **contains only** basic ASCII. + * @internal + */ + mightContainNonBasicASCII(): boolean; + + /** + * Get the resolved options for this model. + */ + getOptions(): TextModelResolvedOptions; + + /** + * Get the current version id of the model. + * Anytime a change happens to the model (even undo/redo), + * the version id is incremented. + */ + getVersionId(): number; + + /** + * Get the alternative version id of the model. + * This alternative version id is not always incremented, + * it will return the same values in the case of undo-redo. + */ + getAlternativeVersionId(): number; + + /** + * Replace the entire text buffer value contained in this model. + */ + setValue(newValue: string): void; + + /** + * Replace the entire text buffer value contained in this model. + * @internal + */ + setValueFromTextSource(newValue: ITextSource): void; + + /** + * Get the text stored in this model. + * @param eol The end of line character preference. Defaults to `EndOfLinePreference.TextDefined`. + * @param preserverBOM Preserve a BOM character if it was detected when the model was constructed. + * @return The text. + */ + getValue(eol?: EndOfLinePreference, preserveBOM?: boolean): string; + + /** + * Get the length of the text stored in this model. + */ + getValueLength(eol?: EndOfLinePreference, preserveBOM?: boolean): number; + + /** + * Check if the raw text stored in this model equals another raw text. + * @internal + */ + equals(other: ITextSource): boolean; + + /** + * Get the text in a certain range. + * @param range The range describing what text to get. + * @param eol The end of line character preference. This will only be used for multiline ranges. Defaults to `EndOfLinePreference.TextDefined`. + * @return The text. + */ + getValueInRange(range: IRange, eol?: EndOfLinePreference): string; + + /** + * Get the length of text in a certain range. + * @param range The range describing what text length to get. + * @return The text length. + */ + getValueLengthInRange(range: IRange): number; + + /** + * Splits characters in two buckets. First bucket (A) is of characters that + * sit in lines with length < `LONG_LINE_BOUNDARY`. Second bucket (B) is of + * characters that sit in lines with length >= `LONG_LINE_BOUNDARY`. + * If count(B) > count(A) return true. Returns false otherwise. + * @internal + */ + isDominatedByLongLines(): boolean; + + /** + * Get the number of lines in the model. + */ + getLineCount(): number; + + /** + * Get the text for a certain line. + */ + getLineContent(lineNumber: number): string; + + /** + * @internal + */ + getIndentLevel(lineNumber: number): number; + + /** + * @internal + */ + getIndentRanges(): IndentRange[]; + + /** + * @internal + */ + getLineIndentGuide(lineNumber: number): number; + + /** + * Get the text for all lines. + */ + getLinesContent(): string[]; + + /** + * Get the end of line sequence predominantly used in the text buffer. + * @return EOL char sequence (e.g.: '\n' or '\r\n'). + */ + getEOL(): string; + + /** + * Change the end of line sequence used in the text buffer. + */ + setEOL(eol: EndOfLineSequence): void; + + /** + * Get the minimum legal column for line at `lineNumber` + */ + getLineMinColumn(lineNumber: number): number; + + /** + * Get the maximum legal column for line at `lineNumber` + */ + getLineMaxColumn(lineNumber: number): number; + + /** + * Returns the column before the first non whitespace character for line at `lineNumber`. + * Returns 0 if line is empty or contains only whitespace. + */ + getLineFirstNonWhitespaceColumn(lineNumber: number): number; + + /** + * Returns the column after the last non whitespace character for line at `lineNumber`. + * Returns 0 if line is empty or contains only whitespace. + */ + getLineLastNonWhitespaceColumn(lineNumber: number): number; + + /** + * Create a valid position, + */ + validatePosition(position: IPosition): Position; + + /** + * Advances the given position by the given offest (negative offsets are also accepted) + * and returns it as a new valid position. + * + * If the offset and position are such that their combination goes beyond the beginning or + * end of the model, throws an exception. + * + * If the ofsset is such that the new position would be in the middle of a multi-byte + * line terminator, throws an exception. + */ + modifyPosition(position: IPosition, offset: number): Position; + + /** + * Create a valid range. + */ + validateRange(range: IRange): Range; + + /** + * Converts the position to a zero-based offset. + * + * The position will be [adjusted](#TextDocument.validatePosition). + * + * @param position A position. + * @return A valid zero-based offset. + */ + getOffsetAt(position: IPosition): number; + + /** + * Converts a zero-based offset to a position. + * + * @param offset A zero-based offset. + * @return A valid [position](#Position). + */ + getPositionAt(offset: number): Position; + + /** + * Get a range covering the entire model + */ + getFullModelRange(): Range; + + /** + * Returns if the model was disposed or not. + */ + isDisposed(): boolean; + + /** + * Only basic mode supports allowed on this model because it is simply too large. + * (tokenization is allowed and other basic supports) + * @internal + */ + isTooLargeForHavingARichMode(): boolean; + + /** + * The file is so large, that even tokenization is disabled. + * @internal + */ + isTooLargeForTokenization(): boolean; + + /** + * Search the model. + * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. + * @param searchOnlyEditableRange Limit the searching to only search inside the editable range of the model. + * @param isRegex Used to indicate that `searchString` is a regular expression. + * @param matchCase Force the matching to match lower/upper case exactly. + * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. + * @param captureMatches The result will contain the captured groups. + * @param limitResultCount Limit the number of results + * @return The ranges where the matches are. It is empty if not matches have been found. + */ + findMatches(searchString: string, searchOnlyEditableRange: boolean, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean, limitResultCount?: number): FindMatch[]; + /** + * Search the model. + * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. + * @param searchScope Limit the searching to only search inside this range. + * @param isRegex Used to indicate that `searchString` is a regular expression. + * @param matchCase Force the matching to match lower/upper case exactly. + * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. + * @param captureMatches The result will contain the captured groups. + * @param limitResultCount Limit the number of results + * @return The ranges where the matches are. It is empty if no matches have been found. + */ + findMatches(searchString: string, searchScope: IRange, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean, limitResultCount?: number): FindMatch[]; + /** + * Search the model for the next match. Loops to the beginning of the model if needed. + * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. + * @param searchStart Start the searching at the specified position. + * @param isRegex Used to indicate that `searchString` is a regular expression. + * @param matchCase Force the matching to match lower/upper case exactly. + * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. + * @param captureMatches The result will contain the captured groups. + * @return The range where the next match is. It is null if no next match has been found. + */ + findNextMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): FindMatch; + /** + * Search the model for the previous match. Loops to the end of the model if needed. + * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. + * @param searchStart Start the searching at the specified position. + * @param isRegex Used to indicate that `searchString` is a regular expression. + * @param matchCase Force the matching to match lower/upper case exactly. + * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. + * @param captureMatches The result will contain the captured groups. + * @return The range where the previous match is. It is null if no previous match has been found. + */ + findPreviousMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): FindMatch; +} + +export class FindMatch { + _findMatchBrand: void; + + public readonly range: Range; + public readonly matches: string[]; + + /** + * @internal + */ + constructor(range: Range, matches: string[]) { + this.range = range; + this.matches = matches; + } +} + +export interface IReadOnlyModel extends ITextModel { + /** + * Gets the resource associated with this editor model. + */ + readonly uri: URI; + + /** + * Get the language associated with this model. + * @internal + */ + getLanguageIdentifier(): LanguageIdentifier; + + /** + * Get the language associated with this model. + */ + getModeId(): string; + + /** + * Get the word under or besides `position`. + * @param position The position to look for a word. + * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. + * @return The word under or besides `position`. Might be null. + */ + getWordAtPosition(position: IPosition): IWordAtPosition; + + /** + * Get the word under or besides `position` trimmed to `position`.column + * @param position The position to look for a word. + * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. + * @return The word under or besides `position`. Will never be null. + */ + getWordUntilPosition(position: IPosition): IWordAtPosition; +} + +/** + * @internal + */ +export interface IFoundBracket { + range: Range; + open: string; + close: string; + isOpen: boolean; +} + +/** + * A model that is tokenized. + */ +export interface ITokenizedModel extends ITextModel { + + /** + * Force tokenization information for `lineNumber` to be accurate. + * @internal + */ + forceTokenization(lineNumber: number): void; + + /** + * If it is cheap, force tokenization information for `lineNumber` to be accurate. + * This is based on a heuristic. + * @internal + */ + tokenizeIfCheap(lineNumber: number): void; + + /** + * Check if calling `forceTokenization` for this `lineNumber` will be cheap (time-wise). + * This is based on a heuristic. + * @internal + */ + isCheapToTokenize(lineNumber: number): boolean; + + /** + * Get the tokens for the line `lineNumber`. + * The tokens might be inaccurate. Use `forceTokenization` to ensure accurate tokens. + * @internal + */ + getLineTokens(lineNumber: number): LineTokens; + + /** + * Get the language associated with this model. + * @internal + */ + getLanguageIdentifier(): LanguageIdentifier; + + /** + * Get the language associated with this model. + */ + getModeId(): string; + + /** + * Set the current language mode associated with the model. + * @internal + */ + setMode(languageIdentifier: LanguageIdentifier): void; + + /** + * Returns the real (inner-most) language mode at a given position. + * The result might be inaccurate. Use `forceTokenization` to ensure accurate tokens. + * @internal + */ + getLanguageIdAtPosition(lineNumber: number, column: number): LanguageId; + + /** + * Get the word under or besides `position`. + * @param position The position to look for a word. + * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. + * @return The word under or besides `position`. Might be null. + */ + getWordAtPosition(position: IPosition): IWordAtPosition; + + /** + * Get the word under or besides `position` trimmed to `position`.column + * @param position The position to look for a word. + * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. + * @return The word under or besides `position`. Will never be null. + */ + getWordUntilPosition(position: IPosition): IWordAtPosition; + + /** + * Find the matching bracket of `request` up, counting brackets. + * @param request The bracket we're searching for + * @param position The position at which to start the search. + * @return The range of the matching bracket, or null if the bracket match was not found. + * @internal + */ + findMatchingBracketUp(bracket: string, position: IPosition): Range; + + /** + * Find the first bracket in the model before `position`. + * @param position The position at which to start the search. + * @return The info for the first bracket before `position`, or null if there are no more brackets before `positions`. + * @internal + */ + findPrevBracket(position: IPosition): IFoundBracket; + + /** + * Find the first bracket in the model after `position`. + * @param position The position at which to start the search. + * @return The info for the first bracket after `position`, or null if there are no more brackets after `positions`. + * @internal + */ + findNextBracket(position: IPosition): IFoundBracket; + + /** + * Given a `position`, if the position is on top or near a bracket, + * find the matching bracket of that bracket and return the ranges of both brackets. + * @param position The position at which to look for a bracket. + * @internal + */ + matchBracket(position: IPosition): [Range, Range]; +} + +/** + * A model that can track markers. + */ +export interface ITextModelWithMarkers extends ITextModel { + /** + * @internal + */ + _addMarker(internalDecorationId: number, lineNumber: number, column: number, stickToPreviousCharacter: boolean): string; + /** + * @internal + */ + _changeMarker(id: string, newLineNumber: number, newColumn: number): void; + /** + * @internal + */ + _changeMarkerStickiness(id: string, newStickToPreviousCharacter: boolean): void; + /** + * @internal + */ + _getMarker(id: string): Position; + /** + * @internal + */ + _removeMarker(id: string): void; +} + +/** + * Describes the behavior of decorations when typing/editing near their edges. + * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` + */ +export enum TrackedRangeStickiness { + AlwaysGrowsWhenTypingAtEdges = 0, + NeverGrowsWhenTypingAtEdges = 1, + GrowsOnlyWhenTypingBefore = 2, + GrowsOnlyWhenTypingAfter = 3, +} + +/** + * A model that can have decorations. + */ +export interface ITextModelWithDecorations { + /** + * Change the decorations. The callback will be called with a change accessor + * that becomes invalid as soon as the callback finishes executing. + * This allows for all events to be queued up until the change + * is completed. Returns whatever the callback returns. + * @param ownerId Identifies the editor id in which these decorations should appear. If no `ownerId` is provided, the decorations will appear in all editors that attach this model. + * @internal + */ + changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T, ownerId?: number): T; + + /** + * Perform a minimum ammount of operations, in order to transform the decorations + * identified by `oldDecorations` to the decorations described by `newDecorations` + * and returns the new identifiers associated with the resulting decorations. + * + * @param oldDecorations Array containing previous decorations identifiers. + * @param newDecorations Array describing what decorations should result after the call. + * @param ownerId Identifies the editor id in which these decorations should appear. If no `ownerId` is provided, the decorations will appear in all editors that attach this model. + * @return An array containing the new decorations identifiers. + */ + deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[], ownerId?: number): string[]; + + /** + * Remove all decorations that have been added with this specific ownerId. + * @param ownerId The owner id to search for. + * @internal + */ + removeAllDecorationsWithOwnerId(ownerId: number): void; + + /** + * Get the options associated with a decoration. + * @param id The decoration id. + * @return The decoration options or null if the decoration was not found. + */ + getDecorationOptions(id: string): IModelDecorationOptions; + + /** + * Get the range associated with a decoration. + * @param id The decoration id. + * @return The decoration range or null if the decoration was not found. + */ + getDecorationRange(id: string): Range; + + /** + * Gets all the decorations for the line `lineNumber` as an array. + * @param lineNumber The line number + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @return An array with the decorations + */ + getLineDecorations(lineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + + /** + * Gets all the decorations for the lines between `startLineNumber` and `endLineNumber` as an array. + * @param startLineNumber The start line number + * @param endLineNumber The end line number + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @return An array with the decorations + */ + getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + + /** + * Gets all the deocorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. + * So for now it returns all the decorations on the same line as `range`. + * @param range The range to search in + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @return An array with the decorations + */ + getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + + /** + * Gets all the decorations as an array. + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + */ + getAllDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; +} + +/** + * An editable text model. + */ +export interface IEditableTextModel extends ITextModelWithMarkers { + + /** + * Normalize a string containing whitespace according to indentation rules (converts to spaces or to tabs). + */ + normalizeIndentation(str: string): string; + + /** + * Get what is considered to be one indent (e.g. a tab character or 4 spaces, etc.). + */ + getOneIndent(): string; + + /** + * Change the options of this model. + */ + updateOptions(newOpts: ITextModelUpdateOptions): void; + + /** + * Detect the indentation options for this model from its content. + */ + detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void; + + /** + * Push a stack element onto the undo stack. This acts as an undo/redo point. + * The idea is to use `pushEditOperations` to edit the model and then to + * `pushStackElement` to create an undo/redo stop point. + */ + pushStackElement(): void; + + /** + * Push edit operations, basically editing the model. This is the preferred way + * of editing the model. The edit operations will land on the undo stack. + * @param beforeCursorState The cursor state before the edit operaions. This cursor state will be returned when `undo` or `redo` are invoked. + * @param editOperations The edit operations. + * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. + * @return The cursor state returned by the `cursorStateComputer`. + */ + pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[]; + + /** + * Edit the model without adding the edits to the undo stack. + * This can have dire consequences on the undo stack! See @pushEditOperations for the preferred way. + * @param operations The edit operations. + * @return The inverse edit operations, that, when applied, will bring the model back to the previous state. + */ + applyEdits(operations: IIdentifiedSingleEditOperation[]): IIdentifiedSingleEditOperation[]; + + /** + * Undo edit operations until the first previous stop point created by `pushStackElement`. + * The inverse edit operations will be pushed on the redo stack. + * @internal + */ + undo(): Selection[]; + + /** + * Redo edit operations until the next stop point created by `pushStackElement`. + * The inverse edit operations will be pushed on the undo stack. + * @internal + */ + redo(): Selection[]; + + /** + * Set an editable range on the model. + * @internal + */ + setEditableRange(range: IRange): void; + + /** + * Check if the model has an editable range. + * @internal + */ + hasEditableRange(): boolean; + + /** + * Get the editable range on the model. + * @internal + */ + getEditableRange(): Range; +} + +/** + * A model. + */ +export interface IModel extends IReadOnlyModel, IEditableTextModel, ITextModelWithMarkers, ITokenizedModel, ITextModelWithDecorations { + /** + * @deprecated Please use `onDidChangeContent` instead. + * An event emitted when the contents of the model have changed. + * @internal + * @event + */ + onDidChangeRawContent(listener: (e: ModelRawContentChangedEvent) => void): IDisposable; + /** + * An event emitted when the contents of the model have changed. + * @event + */ + onDidChangeContent(listener: (e: IModelContentChangedEvent) => void): IDisposable; + /** + * An event emitted when decorations of the model have changed. + * @event + */ + onDidChangeDecorations(listener: (e: IModelDecorationsChangedEvent) => void): IDisposable; + /** + * An event emitted when the model options have changed. + * @event + */ + onDidChangeOptions(listener: (e: IModelOptionsChangedEvent) => void): IDisposable; + /** + * An event emitted when the language associated with the model has changed. + * @event + */ + onDidChangeLanguage(listener: (e: IModelLanguageChangedEvent) => void): IDisposable; + /** + * An event emitted right before disposing the model. + * @event + */ + onWillDispose(listener: () => void): IDisposable; + + /** + * @internal + */ + addBulkListener(listener: BulkListenerCallback): IDisposable; + + /** + * A unique identifier associated with this model. + */ + readonly id: string; + + /** + * Destroy this model. This will unbind the model from the mode + * and make all necessary clean-up to release this object to the GC. + * @internal + */ + destroy(): void; + + /** + * Destroy this model. This will unbind the model from the mode + * and make all necessary clean-up to release this object to the GC. + */ + dispose(): void; + + /** + * @internal + */ + onBeforeAttached(): void; + + /** + * @internal + */ + onBeforeDetached(): void; + + /** + * Returns if this model is attached to an editor or not. + * @internal + */ + isAttachedToEditor(): boolean; +} + +/** + * A model for the diff editor. + */ +export interface IDiffEditorModel { + /** + * Original model. + */ + original: IModel; + /** + * Modified model. + */ + modified: IModel; +} + +/** + * An event describing that an editor has had its model reset (i.e. `editor.setModel()`). + */ +export interface IModelChangedEvent { + /** + * The `uri` of the previous model or null. + */ + readonly oldModelUrl: URI; + /** + * The `uri` of the new model or null. + */ + readonly newModelUrl: URI; +} + +export interface IDimension { + width: number; + height: number; +} + +/** + * A change + */ +export interface IChange { + readonly originalStartLineNumber: number; + readonly originalEndLineNumber: number; + readonly modifiedStartLineNumber: number; + readonly modifiedEndLineNumber: number; +} +/** + * A character level change. + */ +export interface ICharChange extends IChange { + readonly originalStartColumn: number; + readonly originalEndColumn: number; + readonly modifiedStartColumn: number; + readonly modifiedEndColumn: number; +} +/** + * A line change + */ +export interface ILineChange extends IChange { + readonly charChanges: ICharChange[]; +} +/** + * Information about a line in the diff editor + */ +export interface IDiffLineInformation { + readonly equivalentLineNumber: number; +} + +/** + * @internal + */ +export interface IConfiguration { + onDidChange(listener: (e: editorOptions.IConfigurationChangedEvent) => void): IDisposable; + + readonly editor: editorOptions.InternalEditorOptions; + + setMaxLineNumber(maxLineNumber: number): void; +} + +// --- view + +export interface IScrollEvent { + readonly scrollTop: number; + readonly scrollLeft: number; + readonly scrollWidth: number; + readonly scrollHeight: number; + + readonly scrollTopChanged: boolean; + readonly scrollLeftChanged: boolean; + readonly scrollWidthChanged: boolean; + readonly scrollHeightChanged: boolean; +} + +export interface INewScrollPosition { + scrollLeft?: number; + scrollTop?: number; +} + +/** + * Description of an action contribution + */ +export interface IActionDescriptor { + /** + * An unique identifier of the contributed action. + */ + id: string; + /** + * A label of the action that will be presented to the user. + */ + label: string; + /** + * Precondition rule. + */ + precondition?: string; + /** + * An array of keybindings for the action. + */ + keybindings?: number[]; + /** + * The keybinding rule (condition on top of precondition). + */ + keybindingContext?: string; + /** + * Control if the action should show up in the context menu and where. + * The context menu of the editor has these default: + * navigation - The navigation group comes first in all cases. + * 1_modification - This group comes next and contains commands that modify your code. + * 9_cutcopypaste - The last default group with the basic editing commands. + * You can also create your own group. + * Defaults to null (don't show in context menu). + */ + contextMenuGroupId?: string; + /** + * Control the order in the context menu group. + */ + contextMenuOrder?: number; + /** + * Method that will be executed when the action is triggered. + * @param editor The editor instance is passed in as a convinience + */ + run(editor: ICommonCodeEditor): void | TPromise; +} + +export interface IEditorAction { + readonly id: string; + readonly label: string; + readonly alias: string; + isSupported(): boolean; + run(): TPromise; +} + +export type IEditorModel = IModel | IDiffEditorModel; + +/** + * A (serializable) state of the cursors. + */ +export interface ICursorState { + inSelectionMode: boolean; + selectionStart: IPosition; + position: IPosition; +} +/** + * A (serializable) state of the view. + */ +export interface IViewState { + scrollTop: number; + scrollTopWithoutViewZones: number; + scrollLeft: number; +} +/** + * A (serializable) state of the code editor. + */ +export interface ICodeEditorViewState { + cursorState: ICursorState[]; + viewState: IViewState; + contributionsState: { [id: string]: any }; +} +/** + * (Serializable) View state for the diff editor. + */ +export interface IDiffEditorViewState { + original: ICodeEditorViewState; + modified: ICodeEditorViewState; +} +/** + * An editor view state. + */ +export type IEditorViewState = ICodeEditorViewState | IDiffEditorViewState; + +export const enum ScrollType { + Smooth = 0, + Immediate = 1, +} + +/** + * An editor. + */ +export interface IEditor { + /** + * An event emitted when the editor has been disposed. + * @event + */ + onDidDispose(listener: () => void): IDisposable; + + /** + * Dispose the editor. + */ + dispose(): void; + + /** + * Get a unique id for this editor instance. + */ + getId(): string; + + /** + * Get the editor type. Please see `EditorType`. + * This is to avoid an instanceof check + */ + getEditorType(): string; + + /** + * Destroy the editor. + * @internal + */ + destroy(): void; + + /** + * Update the editor's options after the editor has been created. + */ + updateOptions(newOptions: editorOptions.IEditorOptions): void; + + /** + * Indicates that the editor becomes visible. + * @internal + */ + onVisible(): void; + + /** + * Indicates that the editor becomes hidden. + * @internal + */ + onHide(): void; + + /** + * Instructs the editor to remeasure its container. This method should + * be called when the container of the editor gets resized. + */ + layout(dimension?: IDimension): void; + + /** + * Brings browser focus to the editor text + */ + focus(): void; + + /** + * Returns true if this editor has keyboard focus (e.g. cursor is blinking). + */ + isFocused(): boolean; + + /** + * Returns all actions associated with this editor. + */ + getActions(): IEditorAction[]; + + /** + * Returns all actions associated with this editor. + */ + getSupportedActions(): IEditorAction[]; + + /** + * Saves current view state of the editor in a serializable object. + */ + saveViewState(): IEditorViewState; + + /** + * Restores the view state of the editor from a serializable object generated by `saveViewState`. + */ + restoreViewState(state: IEditorViewState): void; + + /** + * Given a position, returns a column number that takes tab-widths into account. + */ + getVisibleColumnFromPosition(position: IPosition): number; + + /** + * Returns the primary position of the cursor. + */ + getPosition(): Position; + + /** + * Set the primary position of the cursor. This will remove any secondary cursors. + * @param position New primary cursor's position + */ + setPosition(position: IPosition): void; + + /** + * Scroll vertically as necessary and reveal a line. + */ + revealLine(lineNumber: number, scrollType?: ScrollType): void; + + /** + * Scroll vertically as necessary and reveal a line centered vertically. + */ + revealLineInCenter(lineNumber: number, scrollType?: ScrollType): void; + + /** + * Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. + */ + revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType?: ScrollType): void; + + /** + * Scroll vertically or horizontally as necessary and reveal a position. + */ + revealPosition(position: IPosition, scrollType?: ScrollType): void; + + /** + * Scroll vertically or horizontally as necessary and reveal a position centered vertically. + */ + revealPositionInCenter(position: IPosition, scrollType?: ScrollType): void; + + /** + * Scroll vertically or horizontally as necessary and reveal a position centered vertically only if it lies outside the viewport. + */ + revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType?: ScrollType): void; + + /** + * Returns the primary selection of the editor. + */ + getSelection(): Selection; + + /** + * Returns all the selections of the editor. + */ + getSelections(): Selection[]; + + /** + * Set the primary selection of the editor. This will remove any secondary cursors. + * @param selection The new selection + */ + setSelection(selection: IRange): void; + /** + * Set the primary selection of the editor. This will remove any secondary cursors. + * @param selection The new selection + */ + setSelection(selection: Range): void; + /** + * Set the primary selection of the editor. This will remove any secondary cursors. + * @param selection The new selection + */ + setSelection(selection: ISelection): void; + /** + * Set the primary selection of the editor. This will remove any secondary cursors. + * @param selection The new selection + */ + setSelection(selection: Selection): void; + + /** + * Set the selections for all the cursors of the editor. + * Cursors will be removed or added, as necessary. + */ + setSelections(selections: ISelection[]): void; + + /** + * Scroll vertically as necessary and reveal lines. + */ + revealLines(startLineNumber: number, endLineNumber: number, scrollType?: ScrollType): void; + + /** + * Scroll vertically as necessary and reveal lines centered vertically. + */ + revealLinesInCenter(lineNumber: number, endLineNumber: number, scrollType?: ScrollType): void; + + /** + * Scroll vertically as necessary and reveal lines centered vertically only if it lies outside the viewport. + */ + revealLinesInCenterIfOutsideViewport(lineNumber: number, endLineNumber: number, scrollType?: ScrollType): void; + + /** + * Scroll vertically or horizontally as necessary and reveal a range. + */ + revealRange(range: IRange, scrollType?: ScrollType): void; + + /** + * Scroll vertically or horizontally as necessary and reveal a range centered vertically. + */ + revealRangeInCenter(range: IRange, scrollType?: ScrollType): void; + + /** + * Scroll vertically or horizontally as necessary and reveal a range at the top of the viewport. + */ + revealRangeAtTop(range: IRange, scrollType?: ScrollType): void; + + /** + * Scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport. + */ + revealRangeInCenterIfOutsideViewport(range: IRange, scrollType?: ScrollType): void; + + /** + * Directly trigger a handler or an editor action. + * @param source The source of the call. + * @param handlerId The id of the handler or the id of a contribution. + * @param payload Extra data to be sent to the handler. + */ + trigger(source: string, handlerId: string, payload: any): void; + + /** + * Gets the current model attached to this editor. + */ + getModel(): IEditorModel; + + /** + * Sets the current model attached to this editor. + * If the previous model was created by the editor via the value key in the options + * literal object, it will be destroyed. Otherwise, if the previous model was set + * via setModel, or the model key in the options literal object, the previous model + * will not be destroyed. + * It is safe to call setModel(null) to simply detach the current model from the editor. + */ + setModel(model: IEditorModel): void; + + /** + * Change the decorations. All decorations added through this changeAccessor + * will get the ownerId of the editor (meaning they will not show up in other + * editors). + * @see IModel.changeDecorations + * @internal + */ + changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any; +} + +/** + * An editor contribution that gets created every time a new editor gets created and gets disposed when the editor gets disposed. + */ +export interface IEditorContribution { + /** + * Get a unique identifier for this contribution. + */ + getId(): string; + /** + * Dispose this contribution. + */ + dispose(): void; + /** + * Store view state. + */ + saveViewState?(): any; + /** + * Restore view state. + */ + restoreViewState?(state: any): void; +} + +/** + * @internal + */ +export function isThemeColor(o): o is ThemeColor { + return o && typeof o.id === 'string'; +} + +/** + * @internal + */ +export interface IThemeDecorationRenderOptions { + backgroundColor?: string | ThemeColor; + + outline?: string; + outlineColor?: string | ThemeColor; + outlineStyle?: string; + outlineWidth?: string; + + border?: string; + borderColor?: string | ThemeColor; + borderRadius?: string; + borderSpacing?: string; + borderStyle?: string; + borderWidth?: string; + + textDecoration?: string; + cursor?: string; + color?: string | ThemeColor; + letterSpacing?: string; + + gutterIconPath?: string | URI; + gutterIconSize?: string; + + overviewRulerColor?: string | ThemeColor; + + before?: IContentDecorationRenderOptions; + after?: IContentDecorationRenderOptions; +} + +/** + * @internal + */ +export interface IContentDecorationRenderOptions { + contentText?: string; + contentIconPath?: string | URI; + + border?: string; + borderColor?: string | ThemeColor; + textDecoration?: string; + color?: string | ThemeColor; + backgroundColor?: string | ThemeColor; + + margin?: string; + width?: string; + height?: string; +} + +/** + * @internal + */ +export interface IDecorationRenderOptions extends IThemeDecorationRenderOptions { + isWholeLine?: boolean; + rangeBehavior?: TrackedRangeStickiness; + overviewRulerLane?: OverviewRulerLane; + + light?: IThemeDecorationRenderOptions; + dark?: IThemeDecorationRenderOptions; +} + +/** + * @internal + */ +export interface IThemeDecorationInstanceRenderOptions { + before?: IContentDecorationRenderOptions; + after?: IContentDecorationRenderOptions; +} + +/** + * @internal + */ +export interface IDecorationInstanceRenderOptions extends IThemeDecorationInstanceRenderOptions { + light?: IThemeDecorationInstanceRenderOptions; + dark?: IThemeDecorationInstanceRenderOptions; +} + +/** + * @internal + */ +export interface IDecorationOptions { + range: IRange; + hoverMessage?: IMarkdownString | IMarkdownString[]; + renderOptions?: IDecorationInstanceRenderOptions; +} + +export interface ICommonCodeEditor extends IEditor { + /** + * An event emitted when the content of the current model has changed. + * @event + */ + onDidChangeModelContent(listener: (e: IModelContentChangedEvent) => void): IDisposable; + /** + * An event emitted when the language of the current model has changed. + * @event + */ + onDidChangeModelLanguage(listener: (e: IModelLanguageChangedEvent) => void): IDisposable; + /** + * An event emitted when the options of the current model has changed. + * @event + */ + onDidChangeModelOptions(listener: (e: IModelOptionsChangedEvent) => void): IDisposable; + /** + * An event emitted when the configuration of the editor has changed. (e.g. `editor.updateOptions()`) + * @event + */ + onDidChangeConfiguration(listener: (e: editorOptions.IConfigurationChangedEvent) => void): IDisposable; + /** + * An event emitted when the cursor position has changed. + * @event + */ + onDidChangeCursorPosition(listener: (e: ICursorPositionChangedEvent) => void): IDisposable; + /** + * An event emitted when the cursor selection has changed. + * @event + */ + onDidChangeCursorSelection(listener: (e: ICursorSelectionChangedEvent) => void): IDisposable; + /** + * An event emitted when the model of this editor has changed (e.g. `editor.setModel()`). + * @event + */ + onDidChangeModel(listener: (e: IModelChangedEvent) => void): IDisposable; + /** + * An event emitted when the decorations of the current model have changed. + * @event + */ + onDidChangeModelDecorations(listener: (e: IModelDecorationsChangedEvent) => void): IDisposable; + /** + * An event emitted when the text inside this editor gained focus (i.e. cursor blinking). + * @event + */ + onDidFocusEditorText(listener: () => void): IDisposable; + /** + * An event emitted when the text inside this editor lost focus. + * @event + */ + onDidBlurEditorText(listener: () => void): IDisposable; + /** + * An event emitted when the text inside this editor or an editor widget gained focus. + * @event + */ + onDidFocusEditor(listener: () => void): IDisposable; + /** + * An event emitted when the text inside this editor or an editor widget lost focus. + * @event + */ + onDidBlurEditor(listener: () => void): IDisposable; + /** + * An event emitted before interpreting typed characters (on the keyboard). + * @event + * @internal + */ + onWillType(listener: (text: string) => void): IDisposable; + /** + * An event emitted before interpreting typed characters (on the keyboard). + * @event + * @internal + */ + onDidType(listener: (text: string) => void): IDisposable; + /** + * An event emitted when users paste text in the editor. + * @event + * @internal + */ + onDidPaste(listener: (range: Range) => void): IDisposable; + + /** + * Saves current view state of the editor in a serializable object. + */ + saveViewState(): ICodeEditorViewState; + + /** + * Restores the view state of the editor from a serializable object generated by `saveViewState`. + */ + restoreViewState(state: ICodeEditorViewState): void; + + /** + * Returns true if this editor or one of its widgets has keyboard focus. + */ + hasWidgetFocus(): boolean; + + /** + * Get a contribution of this editor. + * @id Unique identifier of the contribution. + * @return The contribution or null if contribution not found. + */ + getContribution(id: string): T; + + /** + * Execute `fn` with the editor's services. + * @internal + */ + invokeWithinContext(fn: (accessor: ServicesAccessor) => T): T; + + /** + * Type the getModel() of IEditor. + */ + getModel(): IModel; + + /** + * Returns the current editor's configuration + */ + getConfiguration(): editorOptions.InternalEditorOptions; + + /** + * Returns the 'raw' editor's configuration (without any validation or defaults). + * @internal + */ + getRawConfiguration(): editorOptions.IEditorOptions; + + /** + * Get value of the current model attached to this editor. + * @see IModel.getValue + */ + getValue(options?: { preserveBOM: boolean; lineEnding: string; }): string; + + /** + * Set the value of the current model attached to this editor. + * @see IModel.setValue + */ + setValue(newValue: string): void; + + /** + * Get the scrollWidth of the editor's viewport. + */ + getScrollWidth(): number; + /** + * Get the scrollLeft of the editor's viewport. + */ + getScrollLeft(): number; + + /** + * Get the scrollHeight of the editor's viewport. + */ + getScrollHeight(): number; + /** + * Get the scrollTop of the editor's viewport. + */ + getScrollTop(): number; + + /** + * Change the scrollLeft of the editor's viewport. + */ + setScrollLeft(newScrollLeft: number): void; + /** + * Change the scrollTop of the editor's viewport. + */ + setScrollTop(newScrollTop: number): void; + /** + * Change the scroll position of the editor's viewport. + */ + setScrollPosition(position: INewScrollPosition): void; + + /** + * Get an action that is a contribution to this editor. + * @id Unique identifier of the contribution. + * @return The action or null if action not found. + */ + getAction(id: string): IEditorAction; + + /** + * Execute a command on the editor. + * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. + * @param source The source of the call. + * @param command The command to execute + */ + executeCommand(source: string, command: ICommand): void; + + /** + * Push an "undo stop" in the undo-redo stack. + */ + pushUndoStop(): boolean; + + /** + * Execute edits on the editor. + * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. + * @param source The source of the call. + * @param edits The edits to execute. + * @param endCursoState Cursor state after the edits were applied. + */ + executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursoState?: Selection[]): boolean; + + /** + * Execute multiple (concommitent) commands on the editor. + * @param source The source of the call. + * @param command The commands to execute + */ + executeCommands(source: string, commands: ICommand[]): void; + + /** + * @internal + */ + _getCursors(): ICursors; + + /** + * @internal + */ + _getCursorConfiguration(): CursorConfiguration; + + /** + * Get all the decorations on a line (filtering out decorations from other editors). + */ + getLineDecorations(lineNumber: number): IModelDecoration[]; + + /** + * All decorations added through this call will get the ownerId of this editor. + * @see IModel.deltaDecorations + */ + deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[]; + + /** + * @internal + */ + setDecorations(decorationTypeKey: string, ranges: IDecorationOptions[]): void; + + /** + * @internal + */ + removeDecorations(decorationTypeKey: string): void; + + /** + * Get the layout info for the editor. + */ + getLayoutInfo(): editorOptions.EditorLayoutInfo; + + /** + * @internal + */ + getTelemetryData(): { [key: string]: any; }; +} + +export interface ICommonDiffEditor extends IEditor { + /** + * An event emitted when the diff information computed by this diff editor has been updated. + * @event + */ + onDidUpdateDiff(listener: () => void): IDisposable; + + /** + * Saves current view state of the editor in a serializable object. + */ + saveViewState(): IDiffEditorViewState; + + /** + * Restores the view state of the editor from a serializable object generated by `saveViewState`. + */ + restoreViewState(state: IDiffEditorViewState): void; + + /** + * Type the getModel() of IEditor. + */ + getModel(): IDiffEditorModel; + + /** + * Get the `original` editor. + */ + getOriginalEditor(): ICommonCodeEditor; + + /** + * Get the `modified` editor. + */ + getModifiedEditor(): ICommonCodeEditor; + + /** + * Get the computed diff information. + */ + getLineChanges(): ILineChange[]; + + /** + * Get information based on computed diff about a line number from the original model. + * If the diff computation is not finished or the model is missing, will return null. + */ + getDiffLineInformationForOriginal(lineNumber: number): IDiffLineInformation; + + /** + * Get information based on computed diff about a line number from the modified model. + * If the diff computation is not finished or the model is missing, will return null. + */ + getDiffLineInformationForModified(lineNumber: number): IDiffLineInformation; + + /** + * @see ICodeEditor.getValue + */ + getValue(options?: { preserveBOM: boolean; lineEnding: string; }): string; + + /** + * Returns whether the diff editor is ignoring trim whitespace or not. + * @internal + */ + readonly ignoreTrimWhitespace: boolean; + + /** + * Returns whether the diff editor is rendering side by side or not. + * @internal + */ + readonly renderSideBySide: boolean; + /** + * Returns whether the diff editor is rendering +/- indicators or not. + * @internal + */ + readonly renderIndicators: boolean; +} + +/** + * The type of the `IEditor`. + */ +export var EditorType = { + ICodeEditor: 'vs.editor.ICodeEditor', + IDiffEditor: 'vs.editor.IDiffEditor' +}; + +/** + *@internal + */ +export function isCommonCodeEditor(thing: any): thing is ICommonCodeEditor { + if (thing && typeof (thing).getEditorType === 'function') { + return (thing).getEditorType() === EditorType.ICodeEditor; + } else { + return false; + } +} + +/** + *@internal + */ +export function isCommonDiffEditor(thing: any): thing is ICommonDiffEditor { + if (thing && typeof (thing).getEditorType === 'function') { + return (thing).getEditorType() === EditorType.IDiffEditor; + } else { + return false; + } +} + +/** + * Built-in commands. + * @internal + */ +export var Handler = { + ExecuteCommand: 'executeCommand', + ExecuteCommands: 'executeCommands', + + Type: 'type', + ReplacePreviousChar: 'replacePreviousChar', + CompositionStart: 'compositionStart', + CompositionEnd: 'compositionEnd', + Paste: 'paste', + + Cut: 'cut', + + Undo: 'undo', + Redo: 'redo', +}; diff --git a/src/vs/editor/common/editorCommonExtensions.ts b/src/vs/editor/common/editorCommonExtensions.ts new file mode 100644 index 0000000000..64d8adcece --- /dev/null +++ b/src/vs/editor/common/editorCommonExtensions.ts @@ -0,0 +1,332 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { illegalArgument } from 'vs/base/common/errors'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ServicesAccessor, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; +import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; +import { KeybindingsRegistry, ICommandAndKeybindingRule, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Position } from 'vs/editor/common/core/position'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { MenuId, MenuRegistry, IMenuItem } from 'vs/platform/actions/common/actions'; +import { IEditorService } from 'vs/platform/editor/common/editor'; +import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ICodeEditorService, getCodeEditor } from 'vs/editor/common/services/codeEditorService'; + +export type ServicesAccessor = ServicesAccessor; +export type ICommonEditorContributionCtor = IConstructorSignature1; + +// ----- Generic Command + +export interface ICommandKeybindingsOptions extends IKeybindings { + kbExpr?: ContextKeyExpr; + weight?: number; +} +export interface ICommandOptions { + id: string; + precondition: ContextKeyExpr; + kbOpts?: ICommandKeybindingsOptions; + description?: ICommandHandlerDescription; +} +export abstract class Command { + public readonly id: string; + public readonly precondition: ContextKeyExpr; + private readonly _kbOpts: ICommandKeybindingsOptions; + private readonly _description: ICommandHandlerDescription; + + constructor(opts: ICommandOptions) { + this.id = opts.id; + this.precondition = opts.precondition; + this._kbOpts = opts.kbOpts; + this._description = opts.description; + } + + public toCommandAndKeybindingRule(defaultWeight: number): ICommandAndKeybindingRule { + const kbOpts = this._kbOpts || { primary: 0 }; + + let kbWhen = kbOpts.kbExpr; + if (this.precondition) { + if (kbWhen) { + kbWhen = ContextKeyExpr.and(kbWhen, this.precondition); + } else { + kbWhen = this.precondition; + } + } + + const weight = (typeof kbOpts.weight === 'number' ? kbOpts.weight : defaultWeight); + + return { + id: this.id, + handler: (accessor, args) => this.runCommand(accessor, args), + weight: weight, + when: kbWhen, + primary: kbOpts.primary, + secondary: kbOpts.secondary, + win: kbOpts.win, + linux: kbOpts.linux, + mac: kbOpts.mac, + description: this._description + }; + } + + public abstract runCommand(accessor: ServicesAccessor, args: any): void | TPromise; +} + +// ----- Editor Command & Editor Contribution Command + +function findFocusedEditor(accessor: ServicesAccessor): editorCommon.ICommonCodeEditor { + return accessor.get(ICodeEditorService).getFocusedCodeEditor(); +} +function getWorkbenchActiveEditor(accessor: ServicesAccessor): editorCommon.ICommonCodeEditor { + const editorService = accessor.get(IEditorService); + let activeEditor = (editorService).getActiveEditor && (editorService).getActiveEditor(); + return getCodeEditor(activeEditor); +} + +export interface IContributionCommandOptions extends ICommandOptions { + handler: (controller: T) => void; +} +export interface EditorControllerCommand { + new(opts: IContributionCommandOptions): EditorCommand; +} +export abstract class EditorCommand extends Command { + + /** + * Create a command class that is bound to a certain editor contribution. + */ + public static bindToContribution(controllerGetter: (editor: editorCommon.ICommonCodeEditor) => T): EditorControllerCommand { + return class EditorControllerCommandImpl extends EditorCommand { + private _callback: (controller: T) => void; + + constructor(opts: IContributionCommandOptions) { + super(opts); + + this._callback = opts.handler; + } + + public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void { + let controller = controllerGetter(editor); + if (controller) { + this._callback(controllerGetter(editor)); + } + } + }; + } + + public runCommand(accessor: ServicesAccessor, args: any): void | TPromise { + // Find the editor with text focus + let editor = findFocusedEditor(accessor); + + if (!editor) { + // Fallback to use what the workbench considers the active editor + editor = getWorkbenchActiveEditor(accessor); + } + + if (!editor) { + // well, at least we tried... + return; + } + + return editor.invokeWithinContext((editorAccessor) => { + const kbService = editorAccessor.get(IContextKeyService); + if (!kbService.contextMatchesRules(this.precondition)) { + // precondition does not hold + return; + } + + return this.runEditorCommand(editorAccessor, editor, args); + }); + } + + public abstract runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void | TPromise; +} + +// ----- Editor Action + +export interface IEditorCommandMenuOptions { + group?: string; + order?: number; +} +export interface IActionOptions extends ICommandOptions { + label: string; + alias: string; + menuOpts?: IEditorCommandMenuOptions; +} +export abstract class EditorAction extends EditorCommand { + + public label: string; + public alias: string; + private menuOpts: IEditorCommandMenuOptions; + + constructor(opts: IActionOptions) { + super(opts); + this.label = opts.label; + this.alias = opts.alias; + this.menuOpts = opts.menuOpts; + } + + public toMenuItem(): IMenuItem { + if (!this.menuOpts) { + return null; + } + + return { + command: { + id: this.id, + title: this.label + }, + when: this.precondition, + group: this.menuOpts.group, + order: this.menuOpts.order + }; + } + + public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void | TPromise { + this.reportTelemetry(accessor, editor); + return this.run(accessor, editor, args || {}); + } + + protected reportTelemetry(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor) { + accessor.get(ITelemetryService).publicLog('editorActionInvoked', { name: this.label, id: this.id, ...editor.getTelemetryData() }); + } + + public abstract run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void | TPromise; +} + +// --- Registration of commands and actions + +export function editorAction(ctor: { new(): EditorAction; }): void { + CommonEditorRegistry.registerEditorAction(new ctor()); +} + +export function editorCommand(ctor: { new(): EditorCommand }): void { + registerEditorCommand(new ctor()); +} + +export function registerEditorCommand(editorCommand: T): T { + CommonEditorRegistry.registerEditorCommand(editorCommand); + return editorCommand; +} + +export function commonEditorContribution(ctor: ICommonEditorContributionCtor): void { + EditorContributionRegistry.INSTANCE.registerEditorContribution(ctor); +} + +export module CommonEditorRegistry { + + // --- Editor Actions + + export function registerEditorAction(editorAction: EditorAction) { + EditorContributionRegistry.INSTANCE.registerEditorAction(editorAction); + } + export function getEditorActions(): EditorAction[] { + return EditorContributionRegistry.INSTANCE.getEditorActions(); + } + export function getEditorCommand(commandId: string): EditorCommand { + return EditorContributionRegistry.INSTANCE.getEditorCommand(commandId); + } + + // --- Editor Contributions + + export function getEditorContributions(): ICommonEditorContributionCtor[] { + return EditorContributionRegistry.INSTANCE.getEditorContributions(); + } + + // --- Editor Commands + + export function commandWeight(importance: number = 0): number { + return KeybindingsRegistry.WEIGHT.editorContrib(importance); + } + + export function registerEditorCommand(editorCommand: EditorCommand): void { + EditorContributionRegistry.INSTANCE.registerEditorCommand(editorCommand); + } + + export function registerLanguageCommand(id: string, handler: (accessor: ServicesAccessor, args: { [n: string]: any }) => any) { + CommandsRegistry.registerCommand(id, (accessor, args) => handler(accessor, args || {})); + } + + export function registerDefaultLanguageCommand(id: string, handler: (model: editorCommon.IModel, position: Position, args: { [n: string]: any }) => any) { + registerLanguageCommand(id, function (accessor, args) { + + const { resource, position } = args; + if (!(resource instanceof URI)) { + throw illegalArgument('resource'); + } + if (!Position.isIPosition(position)) { + throw illegalArgument('position'); + } + + const model = accessor.get(IModelService).getModel(resource); + if (!model) { + throw illegalArgument('Can not find open model for ' + resource); + } + + const editorPosition = Position.lift(position); + + return handler(model, editorPosition, args); + }); + } +} + +// Editor extension points +const Extensions = { + EditorCommonContributions: 'editor.commonContributions' +}; + +class EditorContributionRegistry { + + public static INSTANCE = new EditorContributionRegistry(); + + private editorContributions: ICommonEditorContributionCtor[]; + private editorActions: EditorAction[]; + private editorCommands: { [commandId: string]: EditorCommand; }; + + constructor() { + this.editorContributions = []; + this.editorActions = []; + this.editorCommands = Object.create(null); + } + + public registerEditorContribution(ctor: ICommonEditorContributionCtor): void { + this.editorContributions.push(ctor); + } + + public registerEditorAction(action: EditorAction) { + + let menuItem = action.toMenuItem(); + if (menuItem) { + MenuRegistry.appendMenuItem(MenuId.EditorContext, menuItem); + } + + KeybindingsRegistry.registerCommandAndKeybindingRule(action.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + + this.editorActions.push(action); + } + + public getEditorContributions(): ICommonEditorContributionCtor[] { + return this.editorContributions.slice(0); + } + + public getEditorActions(): EditorAction[] { + return this.editorActions.slice(0); + } + + public registerEditorCommand(editorCommand: EditorCommand) { + KeybindingsRegistry.registerCommandAndKeybindingRule(editorCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + this.editorCommands[editorCommand.id] = editorCommand; + } + + public getEditorCommand(commandId: string): EditorCommand { + return (this.editorCommands[commandId] || null); + } + +} +Registry.add(Extensions.EditorCommonContributions, EditorContributionRegistry.INSTANCE); diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts new file mode 100644 index 0000000000..7705a61f08 --- /dev/null +++ b/src/vs/editor/common/editorContextKeys.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export namespace EditorContextKeys { + /** + * A context key that is set when the editor's text has focus (cursor is blinking). + */ + export const textFocus = new RawContextKey('editorTextFocus', false); + /** + * A context key that is set when the editor's text or an editor's widget has focus. + */ + export const focus = new RawContextKey('editorFocus', false); + + export const readOnly = new RawContextKey('editorReadonly', false); + export const writable: ContextKeyExpr = readOnly.toNegated(); + export const hasNonEmptySelection = new RawContextKey('editorHasSelection', false); + export const hasOnlyEmptySelection: ContextKeyExpr = hasNonEmptySelection.toNegated(); + export const hasMultipleSelections = new RawContextKey('editorHasMultipleSelections', false); + export const hasSingleSelection: ContextKeyExpr = hasMultipleSelections.toNegated(); + export const tabMovesFocus = new RawContextKey('editorTabMovesFocus', false); + export const tabDoesNotMoveFocus: ContextKeyExpr = tabMovesFocus.toNegated(); + export const isInEmbeddedEditor = new RawContextKey('isInEmbeddedEditor', undefined); + + // -- mode context keys + export const languageId = new RawContextKey('editorLangId', undefined); + export const hasCompletionItemProvider = new RawContextKey('editorHasCompletionItemProvider', undefined); + export const hasCodeActionsProvider = new RawContextKey('editorHasCodeActionsProvider', undefined); + export const hasCodeLensProvider = new RawContextKey('editorHasCodeLensProvider', undefined); + export const hasDefinitionProvider = new RawContextKey('editorHasDefinitionProvider', undefined); + export const hasImplementationProvider = new RawContextKey('editorHasImplementationProvider', undefined); + export const hasTypeDefinitionProvider = new RawContextKey('editorHasTypeDefinitionProvider', undefined); + export const hasHoverProvider = new RawContextKey('editorHasHoverProvider', undefined); + export const hasDocumentHighlightProvider = new RawContextKey('editorHasDocumentHighlightProvider', undefined); + export const hasDocumentSymbolProvider = new RawContextKey('editorHasDocumentSymbolProvider', undefined); + export const hasReferenceProvider = new RawContextKey('editorHasReferenceProvider', undefined); + export const hasRenameProvider = new RawContextKey('editorHasRenameProvider', undefined); + export const hasDocumentFormattingProvider = new RawContextKey('editorHasDocumentFormattingProvider', undefined); + export const hasDocumentSelectionFormattingProvider = new RawContextKey('editorHasDocumentSelectionFormattingProvider', undefined); + export const hasSignatureHelpProvider = new RawContextKey('editorHasSignatureHelpProvider', undefined); +}; diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts new file mode 100644 index 0000000000..2808614878 --- /dev/null +++ b/src/vs/editor/common/model/editStack.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { ICursorStateComputer, IEditableTextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; +import { Selection } from 'vs/editor/common/core/selection'; + +interface IEditOperation { + operations: IIdentifiedSingleEditOperation[]; +} + +interface IStackElement { + beforeVersionId: number; + beforeCursorState: Selection[]; + + editOperations: IEditOperation[]; + + afterCursorState: Selection[]; + afterVersionId: number; +} + +export interface IUndoRedoResult { + selections: Selection[]; + recordedVersionId: number; +} + +export class EditStack { + + private model: IEditableTextModel; + private currentOpenStackElement: IStackElement; + private past: IStackElement[]; + private future: IStackElement[]; + + constructor(model: IEditableTextModel) { + this.model = model; + this.currentOpenStackElement = null; + this.past = []; + this.future = []; + } + + public pushStackElement(): void { + if (this.currentOpenStackElement !== null) { + this.past.push(this.currentOpenStackElement); + this.currentOpenStackElement = null; + } + } + + public clear(): void { + this.currentOpenStackElement = null; + this.past = []; + this.future = []; + } + + public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] { + // No support for parallel universes :( + this.future = []; + + if (!this.currentOpenStackElement) { + this.currentOpenStackElement = { + beforeVersionId: this.model.getAlternativeVersionId(), + beforeCursorState: beforeCursorState, + editOperations: [], + afterCursorState: null, + afterVersionId: -1 + }; + } + + var inverseEditOperation: IEditOperation = { + operations: this.model.applyEdits(editOperations) + }; + + this.currentOpenStackElement.editOperations.push(inverseEditOperation); + try { + this.currentOpenStackElement.afterCursorState = cursorStateComputer ? cursorStateComputer(inverseEditOperation.operations) : null; + } catch (e) { + onUnexpectedError(e); + this.currentOpenStackElement.afterCursorState = null; + } + + this.currentOpenStackElement.afterVersionId = this.model.getVersionId(); + return this.currentOpenStackElement.afterCursorState; + } + + public undo(): IUndoRedoResult { + + this.pushStackElement(); + + if (this.past.length > 0) { + var pastStackElement = this.past.pop(); + + try { + // Apply all operations in reverse order + for (var i = pastStackElement.editOperations.length - 1; i >= 0; i--) { + pastStackElement.editOperations[i] = { + operations: this.model.applyEdits(pastStackElement.editOperations[i].operations) + }; + } + } catch (e) { + this.clear(); + return null; + } + + this.future.push(pastStackElement); + + return { + selections: pastStackElement.beforeCursorState, + recordedVersionId: pastStackElement.beforeVersionId + }; + } + + return null; + } + + public redo(): IUndoRedoResult { + + if (this.future.length > 0) { + if (this.currentOpenStackElement) { + throw new Error('How is this possible?'); + } + + var futureStackElement = this.future.pop(); + + try { + // Apply all operations + for (var i = 0; i < futureStackElement.editOperations.length; i++) { + futureStackElement.editOperations[i] = { + operations: this.model.applyEdits(futureStackElement.editOperations[i].operations) + }; + } + } catch (e) { + this.clear(); + return null; + } + + this.past.push(futureStackElement); + + return { + selections: futureStackElement.afterCursorState, + recordedVersionId: futureStackElement.afterVersionId + }; + } + + return null; + } +} diff --git a/src/vs/editor/common/model/editableTextModel.ts b/src/vs/editor/common/model/editableTextModel.ts new file mode 100644 index 0000000000..94b3467042 --- /dev/null +++ b/src/vs/editor/common/model/editableTextModel.ts @@ -0,0 +1,837 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Range, IRange } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditStack } from 'vs/editor/common/model/editStack'; +import { ILineEdit, LineMarker, MarkersTracker, IModelLine } from 'vs/editor/common/model/modelLine'; +import { TextModelWithDecorations, ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import * as strings from 'vs/base/common/strings'; +import * as arrays from 'vs/base/common/arrays'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { ITextSource, IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; + +export interface IValidatedEditOperation { + sortIndex: number; + identifier: editorCommon.ISingleEditOperationIdentifier; + range: Range; + rangeLength: number; + lines: string[]; + forceMoveMarkers: boolean; + isAutoWhitespaceEdit: boolean; +} + +interface IIdentifiedLineEdit extends ILineEdit { + lineNumber: number; +} + +export class EditableTextModel extends TextModelWithDecorations implements editorCommon.IEditableTextModel { + + public static createFromString(text: string, options: editorCommon.ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier = null): EditableTextModel { + return new EditableTextModel(RawTextSource.fromString(text), options, languageIdentifier); + } + + public onDidChangeRawContent(listener: (e: textModelEvents.ModelRawContentChangedEvent) => void): IDisposable { + return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelRawContentChanged2, listener); + } + public onDidChangeContent(listener: (e: textModelEvents.IModelContentChangedEvent) => void): IDisposable { + return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelContentChanged, listener); + } + + private _commandManager: EditStack; + + // for extra details about change events: + private _isUndoing: boolean; + private _isRedoing: boolean; + + // editable range + private _hasEditableRange: boolean; + private _editableRangeId: string; + + private _trimAutoWhitespaceLines: number[]; + + constructor(rawTextSource: IRawTextSource, creationOptions: editorCommon.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier) { + super(rawTextSource, creationOptions, languageIdentifier); + + this._commandManager = new EditStack(this); + + this._isUndoing = false; + this._isRedoing = false; + + this._hasEditableRange = false; + this._editableRangeId = null; + this._trimAutoWhitespaceLines = null; + } + + public dispose(): void { + this._commandManager = null; + super.dispose(); + } + + protected _resetValue(newValue: ITextSource): void { + super._resetValue(newValue); + + // Destroy my edit history and settings + this._commandManager = new EditStack(this); + this._hasEditableRange = false; + this._editableRangeId = null; + this._trimAutoWhitespaceLines = null; + } + + public pushStackElement(): void { + this._commandManager.pushStackElement(); + } + + public pushEditOperations(beforeCursorState: Selection[], editOperations: editorCommon.IIdentifiedSingleEditOperation[], cursorStateComputer: editorCommon.ICursorStateComputer): Selection[] { + try { + this._eventEmitter.beginDeferredEmit(); + return this._pushEditOperations(beforeCursorState, editOperations, cursorStateComputer); + } finally { + this._eventEmitter.endDeferredEmit(); + } + } + + private _pushEditOperations(beforeCursorState: Selection[], editOperations: editorCommon.IIdentifiedSingleEditOperation[], cursorStateComputer: editorCommon.ICursorStateComputer): Selection[] { + if (this._options.trimAutoWhitespace && this._trimAutoWhitespaceLines) { + // Go through each saved line number and insert a trim whitespace edit + // if it is safe to do so (no conflicts with other edits). + + let incomingEdits = editOperations.map((op) => { + return { + range: this.validateRange(op.range), + text: op.text + }; + }); + + // Sometimes, auto-formatters change ranges automatically which can cause undesired auto whitespace trimming near the cursor + // We'll use the following heuristic: if the edits occur near the cursor, then it's ok to trim auto whitespace + let editsAreNearCursors = true; + for (let i = 0, len = beforeCursorState.length; i < len; i++) { + let sel = beforeCursorState[i]; + let foundEditNearSel = false; + for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) { + let editRange = incomingEdits[j].range; + let selIsAbove = editRange.startLineNumber > sel.endLineNumber; + let selIsBelow = sel.startLineNumber > editRange.endLineNumber; + if (!selIsAbove && !selIsBelow) { + foundEditNearSel = true; + break; + } + } + if (!foundEditNearSel) { + editsAreNearCursors = false; + break; + } + } + + if (editsAreNearCursors) { + for (let i = 0, len = this._trimAutoWhitespaceLines.length; i < len; i++) { + let trimLineNumber = this._trimAutoWhitespaceLines[i]; + let maxLineColumn = this.getLineMaxColumn(trimLineNumber); + + let allowTrimLine = true; + for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) { + let editRange = incomingEdits[j].range; + let editText = incomingEdits[j].text; + + if (trimLineNumber < editRange.startLineNumber || trimLineNumber > editRange.endLineNumber) { + // `trimLine` is completely outside this edit + continue; + } + + // At this point: + // editRange.startLineNumber <= trimLine <= editRange.endLineNumber + + if ( + trimLineNumber === editRange.startLineNumber && editRange.startColumn === maxLineColumn + && editRange.isEmpty() && editText && editText.length > 0 && editText.charAt(0) === '\n' + ) { + // This edit inserts a new line (and maybe other text) after `trimLine` + continue; + } + + // Looks like we can't trim this line as it would interfere with an incoming edit + allowTrimLine = false; + break; + } + + if (allowTrimLine) { + editOperations.push({ + identifier: null, + range: new Range(trimLineNumber, 1, trimLineNumber, maxLineColumn), + text: null, + forceMoveMarkers: false, + isAutoWhitespaceEdit: false + }); + } + + } + } + + this._trimAutoWhitespaceLines = null; + } + return this._commandManager.pushEditOperation(beforeCursorState, editOperations, cursorStateComputer); + } + + /** + * Transform operations such that they represent the same logic edit, + * but that they also do not cause OOM crashes. + */ + private _reduceOperations(operations: IValidatedEditOperation[]): IValidatedEditOperation[] { + if (operations.length < 1000) { + // We know from empirical testing that a thousand edits work fine regardless of their shape. + return operations; + } + + // At one point, due to how events are emitted and how each operation is handled, + // some operations can trigger a high ammount of temporary string allocations, + // that will immediately get edited again. + // e.g. a formatter inserting ridiculous ammounts of \n on a model with a single line + // Therefore, the strategy is to collapse all the operations into a huge single edit operation + return [this._toSingleEditOperation(operations)]; + } + + _toSingleEditOperation(operations: IValidatedEditOperation[]): IValidatedEditOperation { + let forceMoveMarkers = false, + firstEditRange = operations[0].range, + lastEditRange = operations[operations.length - 1].range, + entireEditRange = new Range(firstEditRange.startLineNumber, firstEditRange.startColumn, lastEditRange.endLineNumber, lastEditRange.endColumn), + lastEndLineNumber = firstEditRange.startLineNumber, + lastEndColumn = firstEditRange.startColumn, + result: string[] = []; + + for (let i = 0, len = operations.length; i < len; i++) { + let operation = operations[i], + range = operation.range; + + forceMoveMarkers = forceMoveMarkers || operation.forceMoveMarkers; + + // (1) -- Push old text + for (let lineNumber = lastEndLineNumber; lineNumber < range.startLineNumber; lineNumber++) { + if (lineNumber === lastEndLineNumber) { + result.push(this._lines[lineNumber - 1].text.substring(lastEndColumn - 1)); + } else { + result.push('\n'); + result.push(this._lines[lineNumber - 1].text); + } + } + + if (range.startLineNumber === lastEndLineNumber) { + result.push(this._lines[range.startLineNumber - 1].text.substring(lastEndColumn - 1, range.startColumn - 1)); + } else { + result.push('\n'); + result.push(this._lines[range.startLineNumber - 1].text.substring(0, range.startColumn - 1)); + } + + // (2) -- Push new text + if (operation.lines) { + for (let j = 0, lenJ = operation.lines.length; j < lenJ; j++) { + if (j !== 0) { + result.push('\n'); + } + result.push(operation.lines[j]); + } + } + + lastEndLineNumber = operation.range.endLineNumber; + lastEndColumn = operation.range.endColumn; + } + + return { + sortIndex: 0, + identifier: operations[0].identifier, + range: entireEditRange, + rangeLength: this.getValueLengthInRange(entireEditRange), + lines: result.join('').split('\n'), + forceMoveMarkers: forceMoveMarkers, + isAutoWhitespaceEdit: false + }; + } + + private static _sortOpsAscending(a: IValidatedEditOperation, b: IValidatedEditOperation): number { + let r = Range.compareRangesUsingEnds(a.range, b.range); + if (r === 0) { + return a.sortIndex - b.sortIndex; + } + return r; + } + + private static _sortOpsDescending(a: IValidatedEditOperation, b: IValidatedEditOperation): number { + let r = Range.compareRangesUsingEnds(a.range, b.range); + if (r === 0) { + return b.sortIndex - a.sortIndex; + } + return -r; + } + + public applyEdits(rawOperations: editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] { + try { + this._eventEmitter.beginDeferredEmit(); + let markersTracker = this._acquireMarkersTracker(); + return this._applyEdits(markersTracker, rawOperations); + } finally { + this._releaseMarkersTracker(); + this._eventEmitter.endDeferredEmit(); + } + } + + private _applyEdits(markersTracker: MarkersTracker, rawOperations: editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] { + if (rawOperations.length === 0) { + return []; + } + + let mightContainRTL = this._mightContainRTL; + let mightContainNonBasicASCII = this._mightContainNonBasicASCII; + let canReduceOperations = true; + + let operations: IValidatedEditOperation[] = []; + for (let i = 0; i < rawOperations.length; i++) { + let op = rawOperations[i]; + if (canReduceOperations && op._isTracked) { + canReduceOperations = false; + } + let validatedRange = this.validateRange(op.range); + if (!mightContainRTL && op.text) { + // check if the new inserted text contains RTL + mightContainRTL = strings.containsRTL(op.text); + } + if (!mightContainNonBasicASCII && op.text) { + mightContainNonBasicASCII = !strings.isBasicASCII(op.text); + } + operations[i] = { + sortIndex: i, + identifier: op.identifier, + range: validatedRange, + rangeLength: this.getValueLengthInRange(validatedRange), + lines: op.text ? op.text.split(/\r\n|\r|\n/) : null, + forceMoveMarkers: op.forceMoveMarkers, + isAutoWhitespaceEdit: op.isAutoWhitespaceEdit || false + }; + } + + // Sort operations ascending + operations.sort(EditableTextModel._sortOpsAscending); + + for (let i = 0, count = operations.length - 1; i < count; i++) { + let rangeEnd = operations[i].range.getEndPosition(); + let nextRangeStart = operations[i + 1].range.getStartPosition(); + + if (nextRangeStart.isBefore(rangeEnd)) { + // overlapping ranges + throw new Error('Overlapping ranges are not allowed!'); + } + } + + if (canReduceOperations) { + operations = this._reduceOperations(operations); + } + + let editableRange = this.getEditableRange(); + let editableRangeStart = editableRange.getStartPosition(); + let editableRangeEnd = editableRange.getEndPosition(); + for (let i = 0; i < operations.length; i++) { + let operationRange = operations[i].range; + if (!editableRangeStart.isBeforeOrEqual(operationRange.getStartPosition()) || !operationRange.getEndPosition().isBeforeOrEqual(editableRangeEnd)) { + throw new Error('Editing outside of editable range not allowed!'); + } + } + + // Delta encode operations + let reverseRanges = EditableTextModel._getInverseEditRanges(operations); + let reverseOperations: editorCommon.IIdentifiedSingleEditOperation[] = []; + + let newTrimAutoWhitespaceCandidates: { lineNumber: number, oldContent: string }[] = []; + + for (let i = 0; i < operations.length; i++) { + let op = operations[i]; + let reverseRange = reverseRanges[i]; + + reverseOperations[i] = { + identifier: op.identifier, + range: reverseRange, + text: this.getValueInRange(op.range), + forceMoveMarkers: op.forceMoveMarkers + }; + + if (this._options.trimAutoWhitespace && op.isAutoWhitespaceEdit && op.range.isEmpty()) { + // Record already the future line numbers that might be auto whitespace removal candidates on next edit + for (let lineNumber = reverseRange.startLineNumber; lineNumber <= reverseRange.endLineNumber; lineNumber++) { + let currentLineContent = ''; + if (lineNumber === reverseRange.startLineNumber) { + currentLineContent = this.getLineContent(op.range.startLineNumber); + if (strings.firstNonWhitespaceIndex(currentLineContent) !== -1) { + continue; + } + } + newTrimAutoWhitespaceCandidates.push({ lineNumber: lineNumber, oldContent: currentLineContent }); + } + } + } + + this._mightContainRTL = mightContainRTL; + this._mightContainNonBasicASCII = mightContainNonBasicASCII; + this._doApplyEdits(markersTracker, operations); + + this._trimAutoWhitespaceLines = null; + if (this._options.trimAutoWhitespace && newTrimAutoWhitespaceCandidates.length > 0) { + // sort line numbers auto whitespace removal candidates for next edit descending + newTrimAutoWhitespaceCandidates.sort((a, b) => b.lineNumber - a.lineNumber); + + this._trimAutoWhitespaceLines = []; + for (let i = 0, len = newTrimAutoWhitespaceCandidates.length; i < len; i++) { + let lineNumber = newTrimAutoWhitespaceCandidates[i].lineNumber; + if (i > 0 && newTrimAutoWhitespaceCandidates[i - 1].lineNumber === lineNumber) { + // Do not have the same line number twice + continue; + } + + let prevContent = newTrimAutoWhitespaceCandidates[i].oldContent; + let lineContent = this.getLineContent(lineNumber); + + if (lineContent.length === 0 || lineContent === prevContent || strings.firstNonWhitespaceIndex(lineContent) !== -1) { + continue; + } + + this._trimAutoWhitespaceLines.push(lineNumber); + } + } + + return reverseOperations; + } + + /** + * Assumes `operations` are validated and sorted ascending + */ + public static _getInverseEditRanges(operations: IValidatedEditOperation[]): Range[] { + let result: Range[] = []; + + let prevOpEndLineNumber: number; + let prevOpEndColumn: number; + let prevOp: IValidatedEditOperation = null; + for (let i = 0, len = operations.length; i < len; i++) { + let op = operations[i]; + + let startLineNumber: number; + let startColumn: number; + + if (prevOp) { + if (prevOp.range.endLineNumber === op.range.startLineNumber) { + startLineNumber = prevOpEndLineNumber; + startColumn = prevOpEndColumn + (op.range.startColumn - prevOp.range.endColumn); + } else { + startLineNumber = prevOpEndLineNumber + (op.range.startLineNumber - prevOp.range.endLineNumber); + startColumn = op.range.startColumn; + } + } else { + startLineNumber = op.range.startLineNumber; + startColumn = op.range.startColumn; + } + + let resultRange: Range; + + if (op.lines && op.lines.length > 0) { + // the operation inserts something + let lineCount = op.lines.length; + let firstLine = op.lines[0]; + let lastLine = op.lines[lineCount - 1]; + + if (lineCount === 1) { + // single line insert + resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn + firstLine.length); + } else { + // multi line insert + resultRange = new Range(startLineNumber, startColumn, startLineNumber + lineCount - 1, lastLine.length + 1); + } + } else { + // There is nothing to insert + resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn); + } + + prevOpEndLineNumber = resultRange.endLineNumber; + prevOpEndColumn = resultRange.endColumn; + + result.push(resultRange); + prevOp = op; + } + + return result; + } + + private _doApplyEdits(markersTracker: MarkersTracker, operations: IValidatedEditOperation[]): void { + + const tabSize = this._options.tabSize; + + // Sort operations descending + operations.sort(EditableTextModel._sortOpsDescending); + + let rawContentChanges: textModelEvents.ModelRawChange[] = []; + let contentChanges: textModelEvents.IModelContentChange[] = []; + let lineEditsQueue: IIdentifiedLineEdit[] = []; + + const queueLineEdit = (lineEdit: IIdentifiedLineEdit) => { + if (lineEdit.startColumn === lineEdit.endColumn && lineEdit.text.length === 0) { + // empty edit => ignore it + return; + } + lineEditsQueue.push(lineEdit); + }; + + const flushLineEdits = () => { + if (lineEditsQueue.length === 0) { + return; + } + + lineEditsQueue.reverse(); + + // `lineEditsQueue` now contains edits from smaller (line number,column) to larger (line number,column) + let currentLineNumber = lineEditsQueue[0].lineNumber; + let currentLineNumberStart = 0; + + for (let i = 1, len = lineEditsQueue.length; i < len; i++) { + const lineNumber = lineEditsQueue[i].lineNumber; + + if (lineNumber === currentLineNumber) { + continue; + } + + this._invalidateLine(currentLineNumber - 1); + this._lines[currentLineNumber - 1].applyEdits(markersTracker, lineEditsQueue.slice(currentLineNumberStart, i), tabSize); + if (this._lineStarts) { + // update prefix sum + this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length); + } + rawContentChanges.push( + new textModelEvents.ModelRawLineChanged(currentLineNumber, this._lines[currentLineNumber - 1].text) + ); + + currentLineNumber = lineNumber; + currentLineNumberStart = i; + } + + this._invalidateLine(currentLineNumber - 1); + this._lines[currentLineNumber - 1].applyEdits(markersTracker, lineEditsQueue.slice(currentLineNumberStart, lineEditsQueue.length), tabSize); + if (this._lineStarts) { + // update prefix sum + this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length); + } + rawContentChanges.push( + new textModelEvents.ModelRawLineChanged(currentLineNumber, this._lines[currentLineNumber - 1].text) + ); + + lineEditsQueue = []; + }; + + let minTouchedLineNumber = operations[operations.length - 1].range.startLineNumber; + let maxTouchedLineNumber = operations[0].range.endLineNumber + 1; + let totalLinesCountDelta = 0; + + for (let i = 0, len = operations.length; i < len; i++) { + const op = operations[i]; + + // console.log(); + // console.log('-------------------'); + // console.log('OPERATION #' + (i)); + // console.log('op: ', op); + // console.log('<<<\n' + this._lines.map(l => l.text).join('\n') + '\n>>>'); + + const startLineNumber = op.range.startLineNumber; + const startColumn = op.range.startColumn; + const endLineNumber = op.range.endLineNumber; + const endColumn = op.range.endColumn; + + if (startLineNumber === endLineNumber && startColumn === endColumn && (!op.lines || op.lines.length === 0)) { + // no-op + continue; + } + + const deletingLinesCnt = endLineNumber - startLineNumber; + const insertingLinesCnt = (op.lines ? op.lines.length - 1 : 0); + const editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt); + + totalLinesCountDelta += (insertingLinesCnt - deletingLinesCnt); + + // Iterating descending to overlap with previous op + // in case there are common lines being edited in both + for (let j = editingLinesCnt; j >= 0; j--) { + const editLineNumber = startLineNumber + j; + + queueLineEdit({ + lineNumber: editLineNumber, + startColumn: (editLineNumber === startLineNumber ? startColumn : 1), + endColumn: (editLineNumber === endLineNumber ? endColumn : this.getLineMaxColumn(editLineNumber)), + text: (op.lines ? op.lines[j] : ''), + forceMoveMarkers: op.forceMoveMarkers + }); + } + + if (editingLinesCnt < deletingLinesCnt) { + // Must delete some lines + + // Flush any pending line edits + flushLineEdits(); + + const spliceStartLineNumber = startLineNumber + editingLinesCnt; + const spliceStartColumn = this.getLineMaxColumn(spliceStartLineNumber); + + const endLineRemains = this._lines[endLineNumber - 1].split(markersTracker, endColumn, false, tabSize); + this._invalidateLine(spliceStartLineNumber - 1); + + const spliceCnt = endLineNumber - spliceStartLineNumber; + + // Collect all these markers + let markersOnDeletedLines: LineMarker[] = []; + for (let j = 0; j < spliceCnt; j++) { + const deleteLineIndex = spliceStartLineNumber + j; + const deleteLineMarkers = this._lines[deleteLineIndex].getMarkers(); + if (deleteLineMarkers) { + markersOnDeletedLines = markersOnDeletedLines.concat(deleteLineMarkers); + } + } + + this._lines.splice(spliceStartLineNumber, spliceCnt); + if (this._lineStarts) { + // update prefix sum + this._lineStarts.removeValues(spliceStartLineNumber, spliceCnt); + } + + // Reconstruct first line + this._lines[spliceStartLineNumber - 1].append(markersTracker, spliceStartLineNumber, endLineRemains, tabSize); + if (this._lineStarts) { + // update prefix sum + this._lineStarts.changeValue(spliceStartLineNumber - 1, this._lines[spliceStartLineNumber - 1].text.length + this._EOL.length); + } + + // Update deleted markers + const deletedMarkersPosition = new Position(spliceStartLineNumber, spliceStartColumn); + for (let j = 0, lenJ = markersOnDeletedLines.length; j < lenJ; j++) { + markersOnDeletedLines[j].updatePosition(markersTracker, deletedMarkersPosition); + } + + this._lines[spliceStartLineNumber - 1].addMarkers(markersOnDeletedLines); + rawContentChanges.push( + new textModelEvents.ModelRawLineChanged(spliceStartLineNumber, this._lines[spliceStartLineNumber - 1].text) + ); + + rawContentChanges.push( + new textModelEvents.ModelRawLinesDeleted(spliceStartLineNumber + 1, spliceStartLineNumber + spliceCnt) + ); + } + + if (editingLinesCnt < insertingLinesCnt) { + // Must insert some lines + + // Flush any pending line edits + flushLineEdits(); + + const spliceLineNumber = startLineNumber + editingLinesCnt; + let spliceColumn = (spliceLineNumber === startLineNumber ? startColumn : 1); + if (op.lines) { + spliceColumn += op.lines[editingLinesCnt].length; + } + + // Split last line + let leftoverLine = this._lines[spliceLineNumber - 1].split(markersTracker, spliceColumn, op.forceMoveMarkers, tabSize); + if (this._lineStarts) { + // update prefix sum + this._lineStarts.changeValue(spliceLineNumber - 1, this._lines[spliceLineNumber - 1].text.length + this._EOL.length); + } + rawContentChanges.push( + new textModelEvents.ModelRawLineChanged(spliceLineNumber, this._lines[spliceLineNumber - 1].text) + ); + this._invalidateLine(spliceLineNumber - 1); + + // Lines in the middle + let newLines: IModelLine[] = []; + let newLinesContent: string[] = []; + let newLinesLengths = new Uint32Array(insertingLinesCnt - editingLinesCnt); + for (let j = editingLinesCnt + 1; j <= insertingLinesCnt; j++) { + newLines.push(this._createModelLine(op.lines[j], tabSize)); + newLinesContent.push(op.lines[j]); + newLinesLengths[j - editingLinesCnt - 1] = op.lines[j].length + this._EOL.length; + } + this._lines = arrays.arrayInsert(this._lines, startLineNumber + editingLinesCnt, newLines); + newLinesContent[newLinesContent.length - 1] += leftoverLine.text; + if (this._lineStarts) { + // update prefix sum + this._lineStarts.insertValues(startLineNumber + editingLinesCnt, newLinesLengths); + } + + // Last line + this._lines[startLineNumber + insertingLinesCnt - 1].append(markersTracker, startLineNumber + insertingLinesCnt, leftoverLine, tabSize); + if (this._lineStarts) { + // update prefix sum + this._lineStarts.changeValue(startLineNumber + insertingLinesCnt - 1, this._lines[startLineNumber + insertingLinesCnt - 1].text.length + this._EOL.length); + } + rawContentChanges.push( + new textModelEvents.ModelRawLinesInserted(spliceLineNumber + 1, startLineNumber + insertingLinesCnt, newLinesContent.join('\n')) + ); + } + + contentChanges.push({ + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + rangeLength: op.rangeLength, + text: op.lines ? op.lines.join(this.getEOL()) : '' + }); + + // console.log('AFTER:'); + // console.log('<<<\n' + this._lines.map(l => l.text).join('\n') + '\n>>>'); + } + + flushLineEdits(); + + maxTouchedLineNumber = Math.max(1, Math.min(this.getLineCount(), maxTouchedLineNumber + totalLinesCountDelta)); + if (totalLinesCountDelta !== 0) { + // must update line numbers all the way to the bottom + maxTouchedLineNumber = this.getLineCount(); + } + + for (let lineNumber = minTouchedLineNumber; lineNumber <= maxTouchedLineNumber; lineNumber++) { + this._lines[lineNumber - 1].updateLineNumber(markersTracker, lineNumber); + } + + if (rawContentChanges.length !== 0 || contentChanges.length !== 0) { + this._increaseVersionId(); + + this._emitModelRawContentChangedEvent(new textModelEvents.ModelRawContentChangedEvent( + rawContentChanges, + this.getVersionId(), + this._isUndoing, + this._isRedoing + )); + + const e: textModelEvents.IModelContentChangedEvent = { + changes: contentChanges, + eol: this._EOL, + versionId: this.getVersionId(), + isUndoing: this._isUndoing, + isRedoing: this._isRedoing, + isFlush: false + }; + this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelContentChanged, e); + } + + // this._assertLineNumbersOK(); + this._resetIndentRanges(); + } + + public _assertLineNumbersOK(): void { + let foundMarkersCnt = 0; + for (let i = 0, len = this._lines.length; i < len; i++) { + let line = this._lines[i]; + let lineNumber = i + 1; + + let markers = line.getMarkers(); + if (markers !== null) { + for (let j = 0, lenJ = markers.length; j < lenJ; j++) { + foundMarkersCnt++; + let markerId = markers[j].id; + let marker = this._markerIdToMarker[markerId]; + if (marker.position.lineNumber !== lineNumber) { + throw new Error('Misplaced marker with id ' + markerId); + } + } + } + } + + let totalMarkersCnt = Object.keys(this._markerIdToMarker).length; + if (totalMarkersCnt !== foundMarkersCnt) { + throw new Error('There are misplaced markers!'); + } + } + + private _undo(): Selection[] { + this._isUndoing = true; + let r = this._commandManager.undo(); + this._isUndoing = false; + + if (!r) { + return null; + } + + this._overwriteAlternativeVersionId(r.recordedVersionId); + + return r.selections; + } + + public undo(): Selection[] { + try { + this._eventEmitter.beginDeferredEmit(); + this._acquireMarkersTracker(); + return this._undo(); + } finally { + this._releaseMarkersTracker(); + this._eventEmitter.endDeferredEmit(); + } + } + + private _redo(): Selection[] { + this._isRedoing = true; + let r = this._commandManager.redo(); + this._isRedoing = false; + + if (!r) { + return null; + } + + this._overwriteAlternativeVersionId(r.recordedVersionId); + + return r.selections; + } + + public redo(): Selection[] { + try { + this._eventEmitter.beginDeferredEmit(); + this._acquireMarkersTracker(); + return this._redo(); + } finally { + this._releaseMarkersTracker(); + this._eventEmitter.endDeferredEmit(); + } + } + + public setEditableRange(range: IRange): void { + this._commandManager.clear(); + + if (!this._hasEditableRange && !range) { + // Nothing to do + return; + } + + this.changeDecorations((changeAccessor) => { + if (this._hasEditableRange) { + changeAccessor.removeDecoration(this._editableRangeId); + this._editableRangeId = null; + this._hasEditableRange = false; + } + + if (range) { + this._hasEditableRange = true; + this._editableRangeId = changeAccessor.addDecoration(range, EditableTextModel._DECORATION_OPTION); + } + }); + } + + private static _DECORATION_OPTION = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges + }); + + public hasEditableRange(): boolean { + return this._hasEditableRange; + } + + public getEditableRange(): Range { + if (this._hasEditableRange) { + return this.getDecorationRange(this._editableRangeId); + } else { + return this.getFullModelRange(); + } + } +} diff --git a/src/vs/editor/common/model/indentRanges.ts b/src/vs/editor/common/model/indentRanges.ts new file mode 100644 index 0000000000..0882851ea0 --- /dev/null +++ b/src/vs/editor/common/model/indentRanges.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ITextModel } from 'vs/editor/common/editorCommon'; + +export class IndentRange { + _indentRangeBrand: void; + startLineNumber: number; + endLineNumber: number; + indent: number; + + constructor(startLineNumber: number, endLineNumber: number, indent: number) { + this.startLineNumber = startLineNumber; + this.endLineNumber = endLineNumber; + this.indent = indent; + } + + public static deepCloneArr(indentRanges: IndentRange[]): IndentRange[] { + let result: IndentRange[] = []; + for (let i = 0, len = indentRanges.length; i < len; i++) { + let r = indentRanges[i]; + result[i] = new IndentRange(r.startLineNumber, r.endLineNumber, r.indent); + } + return result; + } +} + +export function computeRanges(model: ITextModel, minimumRangeSize: number = 1): IndentRange[] { + + let result: IndentRange[] = []; + + let previousRegions: { indent: number, line: number }[] = []; + previousRegions.push({ indent: -1, line: model.getLineCount() + 1 }); // sentinel, to make sure there's at least one entry + + for (let line = model.getLineCount(); line > 0; line--) { + let indent = model.getIndentLevel(line); + if (indent === -1) { + continue; // only whitespace + } + + let previous = previousRegions[previousRegions.length - 1]; + + if (previous.indent > indent) { + // discard all regions with larger indent + do { + previousRegions.pop(); + previous = previousRegions[previousRegions.length - 1]; + } while (previous.indent > indent); + + // new folding range + let endLineNumber = previous.line - 1; + if (endLineNumber - line >= minimumRangeSize) { + result.push(new IndentRange(line, endLineNumber, indent)); + } + } + if (previous.indent === indent) { + previous.line = line; + } else { // previous.indent < indent + // new region with a bigger indent + previousRegions.push({ indent, line }); + } + } + + return result.reverse(); +} diff --git a/src/vs/editor/common/model/indentationGuesser.ts b/src/vs/editor/common/model/indentationGuesser.ts new file mode 100644 index 0000000000..d69344d314 --- /dev/null +++ b/src/vs/editor/common/model/indentationGuesser.ts @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { CharCode } from 'vs/base/common/charCode'; + +/** + * Compute the diff in spaces between two line's indentation. + */ +function spacesDiff(a: string, aLength: number, b: string, bLength: number): number { + + // This can go both ways (e.g.): + // - a: "\t" + // - b: "\t " + // => This should count 1 tab and 4 spaces + + let i: number; + + for (i = 0; i < aLength && i < bLength; i++) { + let aCharCode = a.charCodeAt(i); + let bCharCode = b.charCodeAt(i); + + if (aCharCode !== bCharCode) { + break; + } + } + + let aSpacesCnt = 0, aTabsCount = 0; + for (let j = i; j < aLength; j++) { + let aCharCode = a.charCodeAt(j); + if (aCharCode === CharCode.Space) { + aSpacesCnt++; + } else { + aTabsCount++; + } + } + + let bSpacesCnt = 0, bTabsCount = 0; + for (let j = i; j < bLength; j++) { + let bCharCode = b.charCodeAt(j); + if (bCharCode === CharCode.Space) { + bSpacesCnt++; + } else { + bTabsCount++; + } + } + + if (aSpacesCnt > 0 && aTabsCount > 0) { + return 0; + } + if (bSpacesCnt > 0 && bTabsCount > 0) { + return 0; + } + + let tabsDiff = Math.abs(aTabsCount - bTabsCount); + let spacesDiff = Math.abs(aSpacesCnt - bSpacesCnt); + + if (tabsDiff === 0) { + return spacesDiff; + } + if (spacesDiff % tabsDiff === 0) { + return spacesDiff / tabsDiff; + } + return 0; +} + +/** + * Result for a guessIndentation + */ +export interface IGuessedIndentation { + /** + * If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent? + */ + tabSize: number; + /** + * Is indentation based on spaces? + */ + insertSpaces: boolean; +} + +export function guessIndentation(lines: string[], defaultTabSize: number, defaultInsertSpaces: boolean): IGuessedIndentation { + // Look at most at the first 10k lines + const linesLen = Math.min(lines.length, 10000); + + let linesIndentedWithTabsCount = 0; // number of lines that contain at least one tab in indentation + let linesIndentedWithSpacesCount = 0; // number of lines that contain only spaces in indentation + + let previousLineText = ''; // content of latest line that contained non-whitespace chars + let previousLineIndentation = 0; // index at which latest line contained the first non-whitespace char + + const ALLOWED_TAB_SIZE_GUESSES = [2, 4, 6, 8]; // limit guesses for `tabSize` to 2, 4, 6 or 8. + const MAX_ALLOWED_TAB_SIZE_GUESS = 8; // max(2,4,6,8) = 8 + + let spacesDiffCount = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // `tabSize` scores + + for (let i = 0; i < linesLen; i++) { + let currentLineText = lines[i]; + + let currentLineHasContent = false; // does `currentLineText` contain non-whitespace chars + let currentLineIndentation = 0; // index at which `currentLineText` contains the first non-whitespace char + let currentLineSpacesCount = 0; // count of spaces found in `currentLineText` indentation + let currentLineTabsCount = 0; // count of tabs found in `currentLineText` indentation + for (let j = 0, lenJ = currentLineText.length; j < lenJ; j++) { + let charCode = currentLineText.charCodeAt(j); + + if (charCode === CharCode.Tab) { + currentLineTabsCount++; + } else if (charCode === CharCode.Space) { + currentLineSpacesCount++; + } else { + // Hit non whitespace character on this line + currentLineHasContent = true; + currentLineIndentation = j; + break; + } + } + + // Ignore empty or only whitespace lines + if (!currentLineHasContent) { + continue; + } + + if (currentLineTabsCount > 0) { + linesIndentedWithTabsCount++; + } else if (currentLineSpacesCount > 1) { + linesIndentedWithSpacesCount++; + } + + let currentSpacesDiff = spacesDiff(previousLineText, previousLineIndentation, currentLineText, currentLineIndentation); + if (currentSpacesDiff <= MAX_ALLOWED_TAB_SIZE_GUESS) { + spacesDiffCount[currentSpacesDiff]++; + } + + previousLineText = currentLineText; + previousLineIndentation = currentLineIndentation; + } + + // Take into account the last line as well + let deltaSpacesCount = spacesDiff(previousLineText, previousLineIndentation, '', 0); + if (deltaSpacesCount <= MAX_ALLOWED_TAB_SIZE_GUESS) { + spacesDiffCount[deltaSpacesCount]++; + } + + let insertSpaces = defaultInsertSpaces; + if (linesIndentedWithTabsCount !== linesIndentedWithSpacesCount) { + insertSpaces = (linesIndentedWithTabsCount < linesIndentedWithSpacesCount); + } + + let tabSize = defaultTabSize; + let tabSizeScore = (insertSpaces ? 0 : 0.1 * linesLen); + + // console.log("score threshold: " + tabSizeScore); + + ALLOWED_TAB_SIZE_GUESSES.forEach((possibleTabSize) => { + let possibleTabSizeScore = spacesDiffCount[possibleTabSize]; + if (possibleTabSizeScore > tabSizeScore) { + tabSizeScore = possibleTabSizeScore; + tabSize = possibleTabSize; + } + }); + + + // console.log('--------------------------'); + // console.log('linesIndentedWithTabsCount: ' + linesIndentedWithTabsCount + ', linesIndentedWithSpacesCount: ' + linesIndentedWithSpacesCount); + // console.log('spacesDiffCount: ' + spacesDiffCount); + // console.log('tabSize: ' + tabSize + ', tabSizeScore: ' + tabSizeScore); + + return { + insertSpaces: insertSpaces, + tabSize: tabSize + }; +} diff --git a/src/vs/editor/common/model/mirrorModel.ts b/src/vs/editor/common/model/mirrorModel.ts new file mode 100644 index 0000000000..cc8d8ca4ac --- /dev/null +++ b/src/vs/editor/common/model/mirrorModel.ts @@ -0,0 +1,164 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { IRange } from 'vs/editor/common/core/range'; +import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; +import { IModelContentChange } from 'vs/editor/common/model/textModelEvents'; +import { IPosition } from 'vs/editor/common/core/position'; + +export interface IModelChangedEvent { + /** + * The actual changes. + */ + readonly changes: IModelContentChange[]; + /** + * The (new) end-of-line character. + */ + readonly eol: string; + /** + * The new version id the model has transitioned to. + */ + readonly versionId: number; +} + +export class MirrorModel { + + protected _uri: URI; + protected _lines: string[]; + protected _eol: string; + protected _versionId: number; + protected _lineStarts: PrefixSumComputer; + + constructor(uri: URI, lines: string[], eol: string, versionId: number) { + this._uri = uri; + this._lines = lines; + this._eol = eol; + this._versionId = versionId; + } + + dispose(): void { + this._lines.length = 0; + } + + get version(): number { + return this._versionId; + } + + getText(): string { + return this._lines.join(this._eol); + } + + onEvents(e: IModelChangedEvent): void { + if (e.eol && e.eol !== this._eol) { + this._eol = e.eol; + this._lineStarts = null; + } + + // Update my lines + const changes = e.changes; + for (let i = 0, len = changes.length; i < len; i++) { + const change = changes[i]; + this._acceptDeleteRange(change.range); + this._acceptInsertText({ + lineNumber: change.range.startLineNumber, + column: change.range.startColumn + }, change.text); + } + + this._versionId = e.versionId; + } + + protected _ensureLineStarts(): void { + if (!this._lineStarts) { + const eolLength = this._eol.length; + const linesLength = this._lines.length; + const lineStartValues = new Uint32Array(linesLength); + for (let i = 0; i < linesLength; i++) { + lineStartValues[i] = this._lines[i].length + eolLength; + } + this._lineStarts = new PrefixSumComputer(lineStartValues); + } + } + + /** + * All changes to a line's text go through this method + */ + private _setLineText(lineIndex: number, newValue: string): void { + this._lines[lineIndex] = newValue; + if (this._lineStarts) { + // update prefix sum + this._lineStarts.changeValue(lineIndex, this._lines[lineIndex].length + this._eol.length); + } + } + + private _acceptDeleteRange(range: IRange): void { + + if (range.startLineNumber === range.endLineNumber) { + if (range.startColumn === range.endColumn) { + // Nothing to delete + return; + } + // Delete text on the affected line + this._setLineText(range.startLineNumber - 1, + this._lines[range.startLineNumber - 1].substring(0, range.startColumn - 1) + + this._lines[range.startLineNumber - 1].substring(range.endColumn - 1) + ); + return; + } + + // Take remaining text on last line and append it to remaining text on first line + this._setLineText(range.startLineNumber - 1, + this._lines[range.startLineNumber - 1].substring(0, range.startColumn - 1) + + this._lines[range.endLineNumber - 1].substring(range.endColumn - 1) + ); + + // Delete middle lines + this._lines.splice(range.startLineNumber, range.endLineNumber - range.startLineNumber); + if (this._lineStarts) { + // update prefix sum + this._lineStarts.removeValues(range.startLineNumber, range.endLineNumber - range.startLineNumber); + } + } + + private _acceptInsertText(position: IPosition, insertText: string): void { + if (insertText.length === 0) { + // Nothing to insert + return; + } + let insertLines = insertText.split(/\r\n|\r|\n/); + if (insertLines.length === 1) { + // Inserting text on one line + this._setLineText(position.lineNumber - 1, + this._lines[position.lineNumber - 1].substring(0, position.column - 1) + + insertLines[0] + + this._lines[position.lineNumber - 1].substring(position.column - 1) + ); + return; + } + + // Append overflowing text from first line to the end of text to insert + insertLines[insertLines.length - 1] += this._lines[position.lineNumber - 1].substring(position.column - 1); + + // Delete overflowing text from first line and insert text on first line + this._setLineText(position.lineNumber - 1, + this._lines[position.lineNumber - 1].substring(0, position.column - 1) + + insertLines[0] + ); + + // Insert new lines & store lengths + let newLengths = new Uint32Array(insertLines.length - 1); + for (let i = 1; i < insertLines.length; i++) { + this._lines.splice(position.lineNumber + i - 1, 0, insertLines[i]); + newLengths[i - 1] = insertLines[i].length + this._eol.length; + } + + if (this._lineStarts) { + // update prefix sum + this._lineStarts.insertValues(position.lineNumber, newLengths); + } + } +} diff --git a/src/vs/editor/common/model/model.ts b/src/vs/editor/common/model/model.ts new file mode 100644 index 0000000000..72b5d36815 --- /dev/null +++ b/src/vs/editor/common/model/model.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { + IModel, ITextModelCreationOptions +} from 'vs/editor/common/editorCommon'; +import { EditableTextModel } from 'vs/editor/common/model/editableTextModel'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource'; +import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; + +// The hierarchy is: +// Model -> EditableTextModel -> TextModelWithDecorations -> TextModelWithTrackedRanges -> TextModelWithMarkers -> TextModelWithTokens -> TextModel + +var MODEL_ID = 0; + +export class Model extends EditableTextModel implements IModel { + + public onDidChangeDecorations(listener: (e: textModelEvents.IModelDecorationsChangedEvent) => void): IDisposable { + return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelDecorationsChanged, listener); + } + public onDidChangeOptions(listener: (e: textModelEvents.IModelOptionsChangedEvent) => void): IDisposable { + return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelOptionsChanged, listener); + } + public onWillDispose(listener: () => void): IDisposable { + return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelDispose, listener); + } + public onDidChangeLanguage(listener: (e: textModelEvents.IModelLanguageChangedEvent) => void): IDisposable { + return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelLanguageChanged, listener); + } + + public static createFromString(text: string, options: ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier = null, uri: URI = null): Model { + return new Model(RawTextSource.fromString(text), options, languageIdentifier, uri); + } + + public readonly id: string; + + private readonly _associatedResource: URI; + private _attachedEditorCount: number; + + constructor(rawTextSource: IRawTextSource, creationOptions: ITextModelCreationOptions, languageIdentifier: LanguageIdentifier, associatedResource: URI = null) { + super(rawTextSource, creationOptions, languageIdentifier); + + // Generate a new unique model id + MODEL_ID++; + this.id = '$model' + MODEL_ID; + + if (typeof associatedResource === 'undefined' || associatedResource === null) { + this._associatedResource = URI.parse('inmemory://model/' + MODEL_ID); + } else { + this._associatedResource = associatedResource; + } + + this._attachedEditorCount = 0; + } + + public destroy(): void { + this.dispose(); + } + + public dispose(): void { + this._isDisposing = true; + this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelDispose); + super.dispose(); + this._isDisposing = false; + } + + public onBeforeAttached(): void { + this._attachedEditorCount++; + // Warm up tokens for the editor + this._warmUpTokens(); + } + + public onBeforeDetached(): void { + this._attachedEditorCount--; + } + + protected _shouldAutoTokenize(): boolean { + return this.isAttachedToEditor(); + } + + public isAttachedToEditor(): boolean { + return this._attachedEditorCount > 0; + } + + public get uri(): URI { + return this._associatedResource; + } +} diff --git a/src/vs/editor/common/model/modelLine.ts b/src/vs/editor/common/model/modelLine.ts new file mode 100644 index 0000000000..b1bdf886fe --- /dev/null +++ b/src/vs/editor/common/model/modelLine.ts @@ -0,0 +1,893 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IState, FontStyle, StandardTokenType, MetadataConsts, ColorId, LanguageId } from 'vs/editor/common/modes'; +import { CharCode } from 'vs/base/common/charCode'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { Position } from 'vs/editor/common/core/position'; +import { Constants } from 'vs/editor/common/core/uint'; + +export interface ILineEdit { + startColumn: number; + endColumn: number; + text: string; + forceMoveMarkers: boolean; +} + +export class LineMarker { + _lineMarkerBrand: void; + + public readonly id: string; + public readonly internalDecorationId: number; + + public stickToPreviousCharacter: boolean; + public position: Position; + + constructor(id: string, internalDecorationId: number, position: Position, stickToPreviousCharacter: boolean) { + this.id = id; + this.internalDecorationId = internalDecorationId; + this.position = position; + this.stickToPreviousCharacter = stickToPreviousCharacter; + } + + public toString(): string { + return '{\'' + this.id + '\';' + this.position.toString() + ',' + this.stickToPreviousCharacter + '}'; + } + + public updateLineNumber(markersTracker: MarkersTracker, lineNumber: number): void { + if (this.position.lineNumber === lineNumber) { + return; + } + markersTracker.addChangedMarker(this); + this.position = new Position(lineNumber, this.position.column); + } + + public updateColumn(markersTracker: MarkersTracker, column: number): void { + if (this.position.column === column) { + return; + } + markersTracker.addChangedMarker(this); + this.position = new Position(this.position.lineNumber, column); + } + + public updatePosition(markersTracker: MarkersTracker, position: Position): void { + if (this.position.lineNumber === position.lineNumber && this.position.column === position.column) { + return; + } + markersTracker.addChangedMarker(this); + this.position = position; + } + + public setPosition(position: Position) { + this.position = position; + } + + + public static compareMarkers(a: LineMarker, b: LineMarker): number { + if (a.position.column === b.position.column) { + return (a.stickToPreviousCharacter ? 0 : 1) - (b.stickToPreviousCharacter ? 0 : 1); + } + return a.position.column - b.position.column; + } +} + +export class MarkersTracker { + _changedDecorationsBrand: void; + + private _changedDecorations: number[]; + private _changedDecorationsLen: number; + + constructor() { + this._changedDecorations = []; + this._changedDecorationsLen = 0; + } + + public addChangedMarker(marker: LineMarker): void { + let internalDecorationId = marker.internalDecorationId; + if (internalDecorationId !== 0) { + this._changedDecorations[this._changedDecorationsLen++] = internalDecorationId; + } + } + + public getDecorationIds(): number[] { + return this._changedDecorations; + } +} + +export interface ITokensAdjuster { + adjust(toColumn: number, delta: number, minimumAllowedColumn: number): void; + finish(delta: number, lineTextLength: number): void; +} + +interface IMarkersAdjuster { + adjustDelta(toColumn: number, delta: number, minimumAllowedColumn: number, moveSemantics: MarkerMoveSemantics): void; + adjustSet(toColumn: number, newColumn: number, moveSemantics: MarkerMoveSemantics): void; + finish(delta: number, lineTextLength: number): void; +} + +var NO_OP_TOKENS_ADJUSTER: ITokensAdjuster = { + adjust: () => { }, + finish: () => { } +}; +var NO_OP_MARKERS_ADJUSTER: IMarkersAdjuster = { + adjustDelta: () => { }, + adjustSet: () => { }, + finish: () => { } +}; + +const enum MarkerMoveSemantics { + MarkerDefined = 0, + ForceMove = 1, + ForceStay = 2 +} + +/** + * Returns: + * - 0 => the line consists of whitespace + * - otherwise => the indent level is returned value - 1 + */ +function computePlusOneIndentLevel(line: string, tabSize: number): number { + let indent = 0; + let i = 0; + let len = line.length; + + while (i < len) { + let chCode = line.charCodeAt(i); + if (chCode === CharCode.Space) { + indent++; + } else if (chCode === CharCode.Tab) { + indent = indent - indent % tabSize + tabSize; + } else { + break; + } + i++; + } + + if (i === len) { + return 0; // line only consists of whitespace + } + + return indent + 1; +} + +export interface IModelLine { + readonly text: string; + + // --- markers + addMarker(marker: LineMarker): void; + addMarkers(markers: LineMarker[]): void; + removeMarker(marker: LineMarker): void; + removeMarkers(deleteMarkers: { [markerId: string]: boolean; }): void; + getMarkers(): LineMarker[]; + + // --- tokenization + resetTokenizationState(): void; + isInvalid(): boolean; + setIsInvalid(isInvalid: boolean): void; + getState(): IState; + setState(state: IState): void; + getTokens(topLevelLanguageId: LanguageId): LineTokens; + setTokens(topLevelLanguageId: LanguageId, tokens: Uint32Array): void; + + // --- indentation + updateTabSize(tabSize: number): void; + getIndentLevel(): number; + + // --- editing + updateLineNumber(markersTracker: MarkersTracker, newLineNumber: number): void; + applyEdits(markersTracker: MarkersTracker, edits: ILineEdit[], tabSize: number): number; + append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void; + split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine; +} + +export abstract class AbstractModelLine { + + private _markers: LineMarker[]; + + constructor(initializeMarkers: boolean) { + if (initializeMarkers) { + this._markers = null; + } + } + + /// + + public abstract get text(): string; + protected abstract _setText(text: string, tabSize: number); + protected abstract _createTokensAdjuster(): ITokensAdjuster; + protected abstract _createModelLine(text: string, tabSize: number): IModelLine; + + /// + + // private _printMarkers(): string { + // if (!this._markers) { + // return '[]'; + // } + // if (this._markers.length === 0) { + // return '[]'; + // } + + // var markers = this._markers; + + // var printMarker = (m:LineMarker) => { + // if (m.stickToPreviousCharacter) { + // return '|' + m.position.column; + // } + // return m.position.column + '|'; + // }; + // return '[' + markers.map(printMarker).join(', ') + ']'; + // } + + private _createMarkersAdjuster(markersTracker: MarkersTracker): IMarkersAdjuster { + if (!this._markers) { + return NO_OP_MARKERS_ADJUSTER; + } + if (this._markers.length === 0) { + return NO_OP_MARKERS_ADJUSTER; + } + + this._markers.sort(LineMarker.compareMarkers); + + var markers = this._markers; + var markersLength = markers.length; + var markersIndex = 0; + var marker = markers[markersIndex]; + + // console.log('------------- INITIAL MARKERS: ' + this._printMarkers()); + + let adjustMarkerBeforeColumn = (toColumn: number, moveSemantics: MarkerMoveSemantics) => { + if (marker.position.column < toColumn) { + return true; + } + if (marker.position.column > toColumn) { + return false; + } + if (moveSemantics === MarkerMoveSemantics.ForceMove) { + return false; + } + if (moveSemantics === MarkerMoveSemantics.ForceStay) { + return true; + } + return marker.stickToPreviousCharacter; + }; + + let adjustDelta = (toColumn: number, delta: number, minimumAllowedColumn: number, moveSemantics: MarkerMoveSemantics) => { + // console.log('------------------------------'); + // console.log('adjustDelta called: toColumn: ' + toColumn + ', delta: ' + delta + ', minimumAllowedColumn: ' + minimumAllowedColumn + ', moveSemantics: ' + MarkerMoveSemantics[moveSemantics]); + // console.log('BEFORE::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers()); + + while (markersIndex < markersLength && adjustMarkerBeforeColumn(toColumn, moveSemantics)) { + if (delta !== 0) { + let newColumn = Math.max(minimumAllowedColumn, marker.position.column + delta); + marker.updateColumn(markersTracker, newColumn); + } + + markersIndex++; + if (markersIndex < markersLength) { + marker = markers[markersIndex]; + } + } + + // console.log('AFTER::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers()); + }; + + let adjustSet = (toColumn: number, newColumn: number, moveSemantics: MarkerMoveSemantics) => { + // console.log('------------------------------'); + // console.log('adjustSet called: toColumn: ' + toColumn + ', newColumn: ' + newColumn + ', moveSemantics: ' + MarkerMoveSemantics[moveSemantics]); + // console.log('BEFORE::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers()); + + while (markersIndex < markersLength && adjustMarkerBeforeColumn(toColumn, moveSemantics)) { + marker.updateColumn(markersTracker, newColumn); + + markersIndex++; + if (markersIndex < markersLength) { + marker = markers[markersIndex]; + } + } + + // console.log('AFTER::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers()); + }; + + let finish = (delta: number, lineTextLength: number) => { + adjustDelta(Constants.MAX_SAFE_SMALL_INTEGER, delta, 1, MarkerMoveSemantics.MarkerDefined); + + // console.log('------------- FINAL MARKERS: ' + this._printMarkers()); + }; + + return { + adjustDelta: adjustDelta, + adjustSet: adjustSet, + finish: finish + }; + } + + public applyEdits(markersTracker: MarkersTracker, edits: ILineEdit[], tabSize: number): number { + let deltaColumn = 0; + let resultText = this.text; + + let tokensAdjuster = this._createTokensAdjuster(); + let markersAdjuster = this._createMarkersAdjuster(markersTracker); + + for (let i = 0, len = edits.length; i < len; i++) { + let edit = edits[i]; + + // console.log(); + // console.log('============================='); + // console.log('EDIT #' + i + ' [ ' + edit.startColumn + ' -> ' + edit.endColumn + ' ] : <<<' + edit.text + '>>>, forceMoveMarkers: ' + edit.forceMoveMarkers); + // console.log('deltaColumn: ' + deltaColumn); + + let startColumn = deltaColumn + edit.startColumn; + let endColumn = deltaColumn + edit.endColumn; + let deletingCnt = endColumn - startColumn; + let insertingCnt = edit.text.length; + + // Adjust tokens & markers before this edit + // console.log('Adjust tokens & markers before this edit'); + tokensAdjuster.adjust(edit.startColumn - 1, deltaColumn, 1); + markersAdjuster.adjustDelta(edit.startColumn, deltaColumn, 1, edit.forceMoveMarkers ? MarkerMoveSemantics.ForceMove : (deletingCnt > 0 ? MarkerMoveSemantics.ForceStay : MarkerMoveSemantics.MarkerDefined)); + + // Adjust tokens & markers for the common part of this edit + let commonLength = Math.min(deletingCnt, insertingCnt); + if (commonLength > 0) { + // console.log('Adjust tokens & markers for the common part of this edit'); + tokensAdjuster.adjust(edit.startColumn - 1 + commonLength, deltaColumn, startColumn); + + if (!edit.forceMoveMarkers) { + markersAdjuster.adjustDelta(edit.startColumn + commonLength, deltaColumn, startColumn, edit.forceMoveMarkers ? MarkerMoveSemantics.ForceMove : (deletingCnt > insertingCnt ? MarkerMoveSemantics.ForceStay : MarkerMoveSemantics.MarkerDefined)); + } + } + + // Perform the edit & update `deltaColumn` + resultText = resultText.substring(0, startColumn - 1) + edit.text + resultText.substring(endColumn - 1); + deltaColumn += insertingCnt - deletingCnt; + + // Adjust tokens & markers inside this edit + // console.log('Adjust tokens & markers inside this edit'); + tokensAdjuster.adjust(edit.endColumn, deltaColumn, startColumn); + markersAdjuster.adjustSet(edit.endColumn, startColumn + insertingCnt, edit.forceMoveMarkers ? MarkerMoveSemantics.ForceMove : MarkerMoveSemantics.MarkerDefined); + } + + // Wrap up tokens & markers; adjust remaining if needed + tokensAdjuster.finish(deltaColumn, resultText.length); + markersAdjuster.finish(deltaColumn, resultText.length); + + // Save the resulting text + this._setText(resultText, tabSize); + + return deltaColumn; + } + + public split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine { + // console.log('--> split @ ' + splitColumn + '::: ' + this._printMarkers()); + var myText = this.text.substring(0, splitColumn - 1); + var otherText = this.text.substring(splitColumn - 1); + + var otherMarkers: LineMarker[] = null; + + if (this._markers) { + this._markers.sort(LineMarker.compareMarkers); + for (let i = 0, len = this._markers.length; i < len; i++) { + let marker = this._markers[i]; + + if ( + marker.position.column > splitColumn + || ( + marker.position.column === splitColumn + && ( + forceMoveMarkers + || !marker.stickToPreviousCharacter + ) + ) + ) { + let myMarkers = this._markers.slice(0, i); + otherMarkers = this._markers.slice(i); + this._markers = myMarkers; + break; + } + } + + if (otherMarkers) { + for (let i = 0, len = otherMarkers.length; i < len; i++) { + let marker = otherMarkers[i]; + + marker.updateColumn(markersTracker, marker.position.column - (splitColumn - 1)); + } + } + } + + this._setText(myText, tabSize); + + var otherLine = this._createModelLine(otherText, tabSize); + if (otherMarkers) { + otherLine.addMarkers(otherMarkers); + } + return otherLine; + } + + public append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void { + // console.log('--> append: THIS :: ' + this._printMarkers()); + // console.log('--> append: OTHER :: ' + this._printMarkers()); + let thisTextLength = this.text.length; + this._setText(this.text + other.text, tabSize); + + if (other instanceof AbstractModelLine) { + if (other._markers) { + // Other has markers + let otherMarkers = other._markers; + + // Adjust other markers + for (let i = 0, len = otherMarkers.length; i < len; i++) { + let marker = otherMarkers[i]; + + marker.updatePosition(markersTracker, new Position(myLineNumber, marker.position.column + thisTextLength)); + } + + this.addMarkers(otherMarkers); + } + } + } + + public addMarker(marker: LineMarker): void { + if (!this._markers) { + this._markers = [marker]; + } else { + this._markers.push(marker); + } + } + + public addMarkers(markers: LineMarker[]): void { + if (markers.length === 0) { + return; + } + + if (!this._markers) { + this._markers = markers.slice(0); + } else { + this._markers = this._markers.concat(markers); + } + } + + public removeMarker(marker: LineMarker): void { + if (!this._markers) { + return; + } + + let index = this._indexOfMarkerId(marker.id); + if (index < 0) { + return; + } + + if (this._markers.length === 1) { + // was last marker on line + this._markers = null; + } else { + this._markers.splice(index, 1); + } + } + + public removeMarkers(deleteMarkers: { [markerId: string]: boolean; }): void { + if (!this._markers) { + return; + } + for (let i = 0, len = this._markers.length; i < len; i++) { + let marker = this._markers[i]; + + if (deleteMarkers[marker.id]) { + this._markers.splice(i, 1); + len--; + i--; + } + } + if (this._markers.length === 0) { + this._markers = null; + } + } + + public getMarkers(): LineMarker[] { + if (!this._markers) { + return null; + } + return this._markers; + } + + public updateLineNumber(markersTracker: MarkersTracker, newLineNumber: number): void { + if (this._markers) { + let markers = this._markers; + for (let i = 0, len = markers.length; i < len; i++) { + let marker = markers[i]; + marker.updateLineNumber(markersTracker, newLineNumber); + } + } + } + + private _indexOfMarkerId(markerId: string): number { + let markers = this._markers; + for (let i = 0, len = markers.length; i < len; i++) { + if (markers[i].id === markerId) { + return i; + } + } + return undefined; + } +} + +export class ModelLine extends AbstractModelLine implements IModelLine { + + private _text: string; + public get text(): string { return this._text; } + + /** + * bits 31 - 1 => indentLevel + * bit 0 => isInvalid + */ + private _metadata: number; + + public isInvalid(): boolean { + return (this._metadata & 0x00000001) ? true : false; + } + + public setIsInvalid(isInvalid: boolean): void { + this._metadata = (this._metadata & 0xfffffffe) | (isInvalid ? 1 : 0); + } + + /** + * Returns: + * - -1 => the line consists of whitespace + * - otherwise => the indent level is returned value + */ + public getIndentLevel(): number { + return ((this._metadata & 0xfffffffe) >> 1) - 1; + } + + private _setPlusOneIndentLevel(value: number): void { + this._metadata = (this._metadata & 0x00000001) | ((value & 0xefffffff) << 1); + } + + public updateTabSize(tabSize: number): void { + if (tabSize === 0) { + // don't care mark + this._metadata = this._metadata & 0x00000001; + } else { + this._setPlusOneIndentLevel(computePlusOneIndentLevel(this._text, tabSize)); + } + } + + private _state: IState; + private _lineTokens: ArrayBuffer; + + constructor(text: string, tabSize: number) { + super(true); + this._metadata = 0; + this._setText(text, tabSize); + this._state = null; + this._lineTokens = null; + } + + protected _createModelLine(text: string, tabSize: number): IModelLine { + return new ModelLine(text, tabSize); + } + + public split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine { + let result = super.split(markersTracker, splitColumn, forceMoveMarkers, tabSize); + + // Mark overflowing tokens for deletion & delete marked tokens + this._deleteMarkedTokens(this._markOverflowingTokensForDeletion(0, this.text.length)); + + return result; + } + + public append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void { + let thisTextLength = this.text.length; + + super.append(markersTracker, myLineNumber, other, tabSize); + + if (other instanceof ModelLine) { + let otherRawTokens = other._lineTokens; + if (otherRawTokens) { + // Other has real tokens + + let otherTokens = new Uint32Array(otherRawTokens); + + // Adjust other tokens + if (thisTextLength > 0) { + for (let i = 0, len = (otherTokens.length >>> 1); i < len; i++) { + otherTokens[(i << 1)] = otherTokens[(i << 1)] + thisTextLength; + } + } + + // Append other tokens + let myRawTokens = this._lineTokens; + if (myRawTokens) { + // I have real tokens + let myTokens = new Uint32Array(myRawTokens); + let result = new Uint32Array(myTokens.length + otherTokens.length); + result.set(myTokens, 0); + result.set(otherTokens, myTokens.length); + this._lineTokens = result.buffer; + } else { + // I don't have real tokens + this._lineTokens = otherTokens.buffer; + } + } + } + } + + // --- BEGIN STATE + + public resetTokenizationState(): void { + this._state = null; + this._lineTokens = null; + } + + public setState(state: IState): void { + this._state = state; + } + + public getState(): IState { + return this._state || null; + } + + // --- END STATE + + // --- BEGIN TOKENS + + public setTokens(topLevelLanguageId: LanguageId, tokens: Uint32Array): void { + if (!tokens || tokens.length === 0) { + this._lineTokens = null; + return; + } + if (tokens.length === 2) { + // there is one token + if (tokens[0] === 0 && tokens[1] === getDefaultMetadata(topLevelLanguageId)) { + this._lineTokens = null; + return; + } + } + this._lineTokens = tokens.buffer; + } + + public getTokens(topLevelLanguageId: LanguageId): LineTokens { + let rawLineTokens = this._lineTokens; + if (rawLineTokens) { + return new LineTokens(new Uint32Array(rawLineTokens), this._text); + } + + let lineTokens = new Uint32Array(2); + lineTokens[0] = 0; + lineTokens[1] = getDefaultMetadata(topLevelLanguageId); + return new LineTokens(lineTokens, this._text); + } + + // --- END TOKENS + + protected _createTokensAdjuster(): ITokensAdjuster { + if (!this._lineTokens) { + // This line does not have real tokens, so there is nothing to adjust + return NO_OP_TOKENS_ADJUSTER; + } + + let lineTokens = new Uint32Array(this._lineTokens); + let tokensLength = (lineTokens.length >>> 1); + let tokenIndex = 0; + let tokenStartOffset = 0; + let removeTokensCount = 0; + + let adjust = (toColumn: number, delta: number, minimumAllowedColumn: number) => { + // console.log(`------------------------------------------------------------------`); + // console.log(`before call: tokenIndex: ${tokenIndex}: ${lineTokens}`); + // console.log(`adjustTokens: ${toColumn} with delta: ${delta} and [${minimumAllowedColumn}]`); + // console.log(`tokenStartOffset: ${tokenStartOffset}`); + let minimumAllowedIndex = minimumAllowedColumn - 1; + + while (tokenStartOffset < toColumn && tokenIndex < tokensLength) { + + if (tokenStartOffset > 0 && delta !== 0) { + // adjust token's `startIndex` by `delta` + let newTokenStartOffset = Math.max(minimumAllowedIndex, tokenStartOffset + delta); + lineTokens[(tokenIndex << 1)] = newTokenStartOffset; + + // console.log(` * adjusted token start offset for token at ${tokenIndex}: ${newTokenStartOffset}`); + + if (delta < 0) { + let tmpTokenIndex = tokenIndex; + while (tmpTokenIndex > 0) { + let prevTokenStartOffset = lineTokens[((tmpTokenIndex - 1) << 1)]; + if (prevTokenStartOffset >= newTokenStartOffset) { + if (prevTokenStartOffset !== Constants.MAX_UINT_32) { + // console.log(` * marking for deletion token at ${tmpTokenIndex - 1}`); + lineTokens[((tmpTokenIndex - 1) << 1)] = Constants.MAX_UINT_32; + removeTokensCount++; + } + tmpTokenIndex--; + } else { + break; + } + } + } + } + + tokenIndex++; + if (tokenIndex < tokensLength) { + tokenStartOffset = lineTokens[(tokenIndex << 1)]; + } + } + // console.log(`after call: tokenIndex: ${tokenIndex}: ${lineTokens}`); + }; + + let finish = (delta: number, lineTextLength: number) => { + adjust(Constants.MAX_SAFE_SMALL_INTEGER, delta, 1); + + // Mark overflowing tokens for deletion & delete marked tokens + this._deleteMarkedTokens(this._markOverflowingTokensForDeletion(removeTokensCount, lineTextLength)); + }; + + return { + adjust: adjust, + finish: finish + }; + } + + private _markOverflowingTokensForDeletion(removeTokensCount: number, lineTextLength: number): number { + if (!this._lineTokens) { + return removeTokensCount; + } + + let lineTokens = new Uint32Array(this._lineTokens); + let tokensLength = (lineTokens.length >>> 1); + + if (removeTokensCount + 1 === tokensLength) { + // no more removing, cannot end up without any tokens for mode transition reasons + return removeTokensCount; + } + + for (let tokenIndex = tokensLength - 1; tokenIndex > 0; tokenIndex--) { + let tokenStartOffset = lineTokens[(tokenIndex << 1)]; + if (tokenStartOffset < lineTextLength) { + // valid token => stop iterating + return removeTokensCount; + } + + // this token now overflows the text => mark it for removal + if (tokenStartOffset !== Constants.MAX_UINT_32) { + // console.log(` * marking for deletion token at ${tokenIndex}`); + lineTokens[(tokenIndex << 1)] = Constants.MAX_UINT_32; + removeTokensCount++; + + if (removeTokensCount + 1 === tokensLength) { + // no more removing, cannot end up without any tokens for mode transition reasons + return removeTokensCount; + } + } + } + + return removeTokensCount; + } + + private _deleteMarkedTokens(removeTokensCount: number): void { + if (removeTokensCount === 0) { + return; + } + + let lineTokens = new Uint32Array(this._lineTokens); + let tokensLength = (lineTokens.length >>> 1); + let newTokens = new Uint32Array(((tokensLength - removeTokensCount) << 1)), newTokenIdx = 0; + for (let i = 0; i < tokensLength; i++) { + let startOffset = lineTokens[(i << 1)]; + if (startOffset === Constants.MAX_UINT_32) { + // marked for deletion + continue; + } + let metadata = lineTokens[(i << 1) + 1]; + newTokens[newTokenIdx++] = startOffset; + newTokens[newTokenIdx++] = metadata; + } + this._lineTokens = newTokens.buffer; + } + + protected _setText(text: string, tabSize: number): void { + this._text = text; + if (tabSize === 0) { + // don't care mark + this._metadata = this._metadata & 0x00000001; + } else { + this._setPlusOneIndentLevel(computePlusOneIndentLevel(text, tabSize)); + } + } + +} + +/** + * A model line that cannot store any tokenization state, nor does it compute indentation levels. + * It has no fields except the text. + */ +export class MinimalModelLine extends AbstractModelLine implements IModelLine { + + private _text: string; + public get text(): string { return this._text; } + + public isInvalid(): boolean { + return false; + } + + public setIsInvalid(isInvalid: boolean): void { + } + + /** + * Returns: + * - -1 => the line consists of whitespace + * - otherwise => the indent level is returned value + */ + public getIndentLevel(): number { + return 0; + } + + public updateTabSize(tabSize: number): void { + } + + constructor(text: string, tabSize: number) { + super(false); + this._setText(text, tabSize); + } + + protected _createModelLine(text: string, tabSize: number): IModelLine { + return new MinimalModelLine(text, tabSize); + } + + public split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine { + return super.split(markersTracker, splitColumn, forceMoveMarkers, tabSize); + } + + public append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void { + super.append(markersTracker, myLineNumber, other, tabSize); + } + + // --- BEGIN STATE + + public resetTokenizationState(): void { + } + + public setState(state: IState): void { + } + + public getState(): IState { + return null; + } + + // --- END STATE + + // --- BEGIN TOKENS + + public setTokens(topLevelLanguageId: LanguageId, tokens: Uint32Array): void { + } + + public getTokens(topLevelLanguageId: LanguageId): LineTokens { + let lineTokens = new Uint32Array(2); + lineTokens[0] = 0; + lineTokens[1] = getDefaultMetadata(topLevelLanguageId); + return new LineTokens(lineTokens, this._text); + } + + // --- END TOKENS + + protected _createTokensAdjuster(): ITokensAdjuster { + // This line does not have real tokens, so there is nothing to adjust + return NO_OP_TOKENS_ADJUSTER; + } + + protected _setText(text: string, tabSize: number): void { + this._text = text; + } +} + +function getDefaultMetadata(topLevelLanguageId: LanguageId): number { + return ( + (topLevelLanguageId << MetadataConsts.LANGUAGEID_OFFSET) + | (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET) + | (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) + | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) + | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) + ) >>> 0; +} diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts new file mode 100644 index 0000000000..38926bd115 --- /dev/null +++ b/src/vs/editor/common/model/textModel.ts @@ -0,0 +1,827 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { OrderGuaranteeEventEmitter, BulkListenerCallback } from 'vs/base/common/eventEmitter'; +import * as strings from 'vs/base/common/strings'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ModelLine, IModelLine, MinimalModelLine } from 'vs/editor/common/model/modelLine'; +import { guessIndentation } from 'vs/editor/common/model/indentationGuesser'; +import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; +import { IndentRange, computeRanges } from 'vs/editor/common/model/indentRanges'; +import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelSearch'; +import { TextSource, ITextSource, IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; + +const USE_MIMINAL_MODEL_LINE = true; + +const LIMIT_FIND_COUNT = 999; +export const LONG_LINE_BOUNDARY = 10000; + +export interface ITextModelCreationData { + readonly text: ITextSource; + readonly options: editorCommon.TextModelResolvedOptions; +} + +export class TextModel implements editorCommon.ITextModel { + private static MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB + private static MODEL_TOKENIZATION_LIMIT = 20 * 1024 * 1024; // 20 MB + private static MANY_MANY_LINES = 300 * 1000; // 300K lines + + public static DEFAULT_CREATION_OPTIONS: editorCommon.ITextModelCreationOptions = { + tabSize: EDITOR_MODEL_DEFAULTS.tabSize, + insertSpaces: EDITOR_MODEL_DEFAULTS.insertSpaces, + detectIndentation: false, + defaultEOL: editorCommon.DefaultEndOfLine.LF, + trimAutoWhitespace: EDITOR_MODEL_DEFAULTS.trimAutoWhitespace, + }; + + public static createFromString(text: string, options: editorCommon.ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS): TextModel { + return new TextModel(RawTextSource.fromString(text), options); + } + + public static resolveCreationData(rawTextSource: IRawTextSource, options: editorCommon.ITextModelCreationOptions): ITextModelCreationData { + const textSource = TextSource.fromRawTextSource(rawTextSource, options.defaultEOL); + + let resolvedOpts: editorCommon.TextModelResolvedOptions; + if (options.detectIndentation) { + const guessedIndentation = guessIndentation(textSource.lines, options.tabSize, options.insertSpaces); + resolvedOpts = new editorCommon.TextModelResolvedOptions({ + tabSize: guessedIndentation.tabSize, + insertSpaces: guessedIndentation.insertSpaces, + trimAutoWhitespace: options.trimAutoWhitespace, + defaultEOL: options.defaultEOL + }); + } else { + resolvedOpts = new editorCommon.TextModelResolvedOptions({ + tabSize: options.tabSize, + insertSpaces: options.insertSpaces, + trimAutoWhitespace: options.trimAutoWhitespace, + defaultEOL: options.defaultEOL + }); + } + + return { + text: textSource, + options: resolvedOpts + }; + } + + public addBulkListener(listener: BulkListenerCallback): IDisposable { + return this._eventEmitter.addBulkListener(listener); + } + + protected readonly _eventEmitter: OrderGuaranteeEventEmitter; + + /*protected*/ _lines: IModelLine[]; + protected _EOL: string; + protected _isDisposed: boolean; + protected _isDisposing: boolean; + protected _options: editorCommon.TextModelResolvedOptions; + protected _lineStarts: PrefixSumComputer; + private _indentRanges: IndentRange[]; + + private _versionId: number; + /** + * Unlike, versionId, this can go down (via undo) or go to previous values (via redo) + */ + private _alternativeVersionId: number; + private _BOM: string; + protected _mightContainRTL: boolean; + protected _mightContainNonBasicASCII: boolean; + + private readonly _shouldSimplifyMode: boolean; + protected readonly _isTooLargeForTokenization: boolean; + + constructor(rawTextSource: IRawTextSource, creationOptions: editorCommon.ITextModelCreationOptions) { + this._eventEmitter = new OrderGuaranteeEventEmitter(); + + const textModelData = TextModel.resolveCreationData(rawTextSource, creationOptions); + + // !!! Make a decision in the ctor and permanently respect this decision !!! + // If a model is too large at construction time, it will never get tokenized, + // under no circumstances. + this._isTooLargeForTokenization = ( + (textModelData.text.length > TextModel.MODEL_TOKENIZATION_LIMIT) + || (textModelData.text.lines.length > TextModel.MANY_MANY_LINES) + ); + + this._shouldSimplifyMode = ( + this._isTooLargeForTokenization + || (textModelData.text.length > TextModel.MODEL_SYNC_LIMIT) + ); + + this._options = new editorCommon.TextModelResolvedOptions(textModelData.options); + this._constructLines(textModelData.text); + this._setVersionId(1); + this._isDisposed = false; + this._isDisposing = false; + } + + protected _createModelLine(text: string, tabSize: number): IModelLine { + if (USE_MIMINAL_MODEL_LINE && this._isTooLargeForTokenization) { + return new MinimalModelLine(text, tabSize); + } + return new ModelLine(text, tabSize); + } + + protected _assertNotDisposed(): void { + if (this._isDisposed) { + throw new Error('Model is disposed!'); + } + } + + public isTooLargeForHavingARichMode(): boolean { + return this._shouldSimplifyMode; + } + + public isTooLargeForTokenization(): boolean { + return this._isTooLargeForTokenization; + } + + public getOptions(): editorCommon.TextModelResolvedOptions { + this._assertNotDisposed(); + return this._options; + } + + public updateOptions(_newOpts: editorCommon.ITextModelUpdateOptions): void { + this._assertNotDisposed(); + let tabSize = (typeof _newOpts.tabSize !== 'undefined') ? _newOpts.tabSize : this._options.tabSize; + let insertSpaces = (typeof _newOpts.insertSpaces !== 'undefined') ? _newOpts.insertSpaces : this._options.insertSpaces; + let trimAutoWhitespace = (typeof _newOpts.trimAutoWhitespace !== 'undefined') ? _newOpts.trimAutoWhitespace : this._options.trimAutoWhitespace; + + let newOpts = new editorCommon.TextModelResolvedOptions({ + tabSize: tabSize, + insertSpaces: insertSpaces, + defaultEOL: this._options.defaultEOL, + trimAutoWhitespace: trimAutoWhitespace + }); + + if (this._options.equals(newOpts)) { + return; + } + + let e = this._options.createChangeEvent(newOpts); + this._options = newOpts; + + if (e.tabSize) { + let newTabSize = this._options.tabSize; + for (let i = 0, len = this._lines.length; i < len; i++) { + this._lines[i].updateTabSize(newTabSize); + } + } + + this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelOptionsChanged, e); + } + + public detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void { + this._assertNotDisposed(); + let lines = this._lines.map(line => line.text); + let guessedIndentation = guessIndentation(lines, defaultTabSize, defaultInsertSpaces); + this.updateOptions({ + insertSpaces: guessedIndentation.insertSpaces, + tabSize: guessedIndentation.tabSize + }); + } + + private static _normalizeIndentationFromWhitespace(str: string, tabSize: number, insertSpaces: boolean): string { + let spacesCnt = 0; + for (let i = 0; i < str.length; i++) { + if (str.charAt(i) === '\t') { + spacesCnt += tabSize; + } else { + spacesCnt++; + } + } + + let result = ''; + if (!insertSpaces) { + let tabsCnt = Math.floor(spacesCnt / tabSize); + spacesCnt = spacesCnt % tabSize; + for (let i = 0; i < tabsCnt; i++) { + result += '\t'; + } + } + + for (let i = 0; i < spacesCnt; i++) { + result += ' '; + } + + return result; + } + + public static normalizeIndentation(str: string, tabSize: number, insertSpaces: boolean): string { + let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(str); + if (firstNonWhitespaceIndex === -1) { + firstNonWhitespaceIndex = str.length; + } + return TextModel._normalizeIndentationFromWhitespace(str.substring(0, firstNonWhitespaceIndex), tabSize, insertSpaces) + str.substring(firstNonWhitespaceIndex); + } + + public normalizeIndentation(str: string): string { + this._assertNotDisposed(); + return TextModel.normalizeIndentation(str, this._options.tabSize, this._options.insertSpaces); + } + + public getOneIndent(): string { + this._assertNotDisposed(); + let tabSize = this._options.tabSize; + let insertSpaces = this._options.insertSpaces; + + if (insertSpaces) { + let result = ''; + for (let i = 0; i < tabSize; i++) { + result += ' '; + } + return result; + } else { + return '\t'; + } + } + + public getVersionId(): number { + this._assertNotDisposed(); + return this._versionId; + } + + public mightContainRTL(): boolean { + return this._mightContainRTL; + } + + public mightContainNonBasicASCII(): boolean { + return this._mightContainNonBasicASCII; + } + + public getAlternativeVersionId(): number { + this._assertNotDisposed(); + return this._alternativeVersionId; + } + + private _ensureLineStarts(): void { + if (!this._lineStarts) { + const eolLength = this._EOL.length; + const linesLength = this._lines.length; + const lineStartValues = new Uint32Array(linesLength); + for (let i = 0; i < linesLength; i++) { + lineStartValues[i] = this._lines[i].text.length + eolLength; + } + this._lineStarts = new PrefixSumComputer(lineStartValues); + } + } + + public getOffsetAt(rawPosition: IPosition): number { + this._assertNotDisposed(); + let position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, false); + this._ensureLineStarts(); + return this._lineStarts.getAccumulatedValue(position.lineNumber - 2) + position.column - 1; + } + + public getPositionAt(offset: number): Position { + this._assertNotDisposed(); + offset = Math.floor(offset); + offset = Math.max(0, offset); + + this._ensureLineStarts(); + let out = this._lineStarts.getIndexOf(offset); + + let lineLength = this._lines[out.index].text.length; + + // Ensure we return a valid position + return new Position(out.index + 1, Math.min(out.remainder + 1, lineLength + 1)); + } + + protected _increaseVersionId(): void { + this._setVersionId(this._versionId + 1); + } + + private _setVersionId(newVersionId: number): void { + this._versionId = newVersionId; + this._alternativeVersionId = this._versionId; + } + + protected _overwriteAlternativeVersionId(newAlternativeVersionId: number): void { + this._alternativeVersionId = newAlternativeVersionId; + } + + public isDisposed(): boolean { + return this._isDisposed; + } + + public dispose(): void { + this._isDisposed = true; + // Null out members, such that any use of a disposed model will throw exceptions sooner rather than later + this._lines = null; + this._EOL = null; + this._BOM = null; + + this._eventEmitter.dispose(); + } + + private _emitContentChanged2(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeLength: number, text: string, isUndoing: boolean, isRedoing: boolean, isFlush: boolean): void { + const e: textModelEvents.IModelContentChangedEvent = { + changes: [{ + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + rangeLength: rangeLength, + text: text, + }], + eol: this._EOL, + versionId: this.getVersionId(), + isUndoing: isUndoing, + isRedoing: isRedoing, + isFlush: isFlush + }; + if (!this._isDisposing) { + this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelContentChanged, e); + } + } + + protected _resetValue(newValue: ITextSource): void { + this._constructLines(newValue); + this._increaseVersionId(); + } + + public equals(other: ITextSource): boolean { + this._assertNotDisposed(); + if (this._BOM !== other.BOM) { + return false; + } + if (this._EOL !== other.EOL) { + return false; + } + if (this._lines.length !== other.lines.length) { + return false; + } + for (let i = 0, len = this._lines.length; i < len; i++) { + if (this._lines[i].text !== other.lines[i]) { + return false; + } + } + return true; + } + + public setValue(value: string): void { + this._assertNotDisposed(); + if (value === null) { + // There's nothing to do + return; + } + const textSource = TextSource.fromString(value, this._options.defaultEOL); + this.setValueFromTextSource(textSource); + } + + public setValueFromTextSource(newValue: ITextSource): void { + this._assertNotDisposed(); + if (newValue === null) { + // There's nothing to do + return; + } + var oldFullModelRange = this.getFullModelRange(); + var oldModelValueLength = this.getValueLengthInRange(oldFullModelRange); + var endLineNumber = this.getLineCount(); + var endColumn = this.getLineMaxColumn(endLineNumber); + + this._resetValue(newValue); + + this._emitModelRawContentChangedEvent( + new textModelEvents.ModelRawContentChangedEvent( + [ + new textModelEvents.ModelRawFlush() + ], + this._versionId, + false, + false + ) + ); + + this._emitContentChanged2(1, 1, endLineNumber, endColumn, oldModelValueLength, this.getValue(), false, false, true); + } + + public getValue(eol?: editorCommon.EndOfLinePreference, preserveBOM: boolean = false): string { + this._assertNotDisposed(); + var fullModelRange = this.getFullModelRange(); + var fullModelValue = this.getValueInRange(fullModelRange, eol); + + if (preserveBOM) { + return this._BOM + fullModelValue; + } + + return fullModelValue; + } + + public getValueLength(eol?: editorCommon.EndOfLinePreference, preserveBOM: boolean = false): number { + this._assertNotDisposed(); + var fullModelRange = this.getFullModelRange(); + var fullModelValue = this.getValueLengthInRange(fullModelRange, eol); + + if (preserveBOM) { + return this._BOM.length + fullModelValue; + } + + return fullModelValue; + } + + public getValueInRange(rawRange: IRange, eol: editorCommon.EndOfLinePreference = editorCommon.EndOfLinePreference.TextDefined): string { + this._assertNotDisposed(); + var range = this.validateRange(rawRange); + + if (range.isEmpty()) { + return ''; + } + + if (range.startLineNumber === range.endLineNumber) { + return this._lines[range.startLineNumber - 1].text.substring(range.startColumn - 1, range.endColumn - 1); + } + + var lineEnding = this._getEndOfLine(eol), + startLineIndex = range.startLineNumber - 1, + endLineIndex = range.endLineNumber - 1, + resultLines: string[] = []; + + resultLines.push(this._lines[startLineIndex].text.substring(range.startColumn - 1)); + for (var i = startLineIndex + 1; i < endLineIndex; i++) { + resultLines.push(this._lines[i].text); + } + resultLines.push(this._lines[endLineIndex].text.substring(0, range.endColumn - 1)); + + return resultLines.join(lineEnding); + } + + public getValueLengthInRange(rawRange: IRange, eol: editorCommon.EndOfLinePreference = editorCommon.EndOfLinePreference.TextDefined): number { + this._assertNotDisposed(); + var range = this.validateRange(rawRange); + + if (range.isEmpty()) { + return 0; + } + + if (range.startLineNumber === range.endLineNumber) { + return (range.endColumn - range.startColumn); + } + + let startOffset = this.getOffsetAt(new Position(range.startLineNumber, range.startColumn)); + let endOffset = this.getOffsetAt(new Position(range.endLineNumber, range.endColumn)); + return endOffset - startOffset; + } + + public isDominatedByLongLines(): boolean { + this._assertNotDisposed(); + var smallLineCharCount = 0, + longLineCharCount = 0, + i: number, + len: number, + lines = this._lines, + lineLength: number; + + for (i = 0, len = this._lines.length; i < len; i++) { + lineLength = lines[i].text.length; + if (lineLength >= LONG_LINE_BOUNDARY) { + longLineCharCount += lineLength; + } else { + smallLineCharCount += lineLength; + } + } + + return (longLineCharCount > smallLineCharCount); + } + + public getLineCount(): number { + this._assertNotDisposed(); + return this._lines.length; + } + + public getLineContent(lineNumber: number): string { + this._assertNotDisposed(); + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); + } + + return this._lines[lineNumber - 1].text; + } + + public getIndentLevel(lineNumber: number): number { + this._assertNotDisposed(); + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); + } + + return this._lines[lineNumber - 1].getIndentLevel(); + } + + protected _resetIndentRanges(): void { + this._indentRanges = null; + } + + private _getIndentRanges(): IndentRange[] { + if (!this._indentRanges) { + this._indentRanges = computeRanges(this); + } + return this._indentRanges; + } + + public getIndentRanges(): IndentRange[] { + this._assertNotDisposed(); + let indentRanges = this._getIndentRanges(); + return IndentRange.deepCloneArr(indentRanges); + } + + private _toValidLineIndentGuide(lineNumber: number, indentGuide: number): number { + let lineIndentLevel = this._lines[lineNumber - 1].getIndentLevel(); + if (lineIndentLevel === -1) { + return indentGuide; + } + let maxIndentGuide = Math.ceil(lineIndentLevel / this._options.tabSize); + return Math.min(maxIndentGuide, indentGuide); + } + + public getLineIndentGuide(lineNumber: number): number { + this._assertNotDisposed(); + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); + } + + let indentRanges = this._getIndentRanges(); + + for (let i = indentRanges.length - 1; i >= 0; i--) { + let rng = indentRanges[i]; + + if (rng.startLineNumber === lineNumber) { + return this._toValidLineIndentGuide(lineNumber, Math.ceil(rng.indent / this._options.tabSize)); + } + if (rng.startLineNumber < lineNumber && lineNumber <= rng.endLineNumber) { + return this._toValidLineIndentGuide(lineNumber, 1 + Math.floor(rng.indent / this._options.tabSize)); + } + if (rng.endLineNumber + 1 === lineNumber) { + let bestIndent = rng.indent; + while (i > 0) { + i--; + rng = indentRanges[i]; + if (rng.endLineNumber + 1 === lineNumber) { + bestIndent = rng.indent; + } + } + return this._toValidLineIndentGuide(lineNumber, Math.ceil(bestIndent / this._options.tabSize)); + } + } + + return 0; + } + + public getLinesContent(): string[] { + this._assertNotDisposed(); + var r: string[] = []; + for (var i = 0, len = this._lines.length; i < len; i++) { + r[i] = this._lines[i].text; + } + return r; + } + + public getEOL(): string { + this._assertNotDisposed(); + return this._EOL; + } + + public setEOL(eol: editorCommon.EndOfLineSequence): void { + this._assertNotDisposed(); + const newEOL = (eol === editorCommon.EndOfLineSequence.CRLF ? '\r\n' : '\n'); + if (this._EOL === newEOL) { + // Nothing to do + return; + } + + const oldFullModelRange = this.getFullModelRange(); + const oldModelValueLength = this.getValueLengthInRange(oldFullModelRange); + const endLineNumber = this.getLineCount(); + const endColumn = this.getLineMaxColumn(endLineNumber); + + this._EOL = newEOL; + this._lineStarts = null; + this._increaseVersionId(); + + this._emitModelRawContentChangedEvent( + new textModelEvents.ModelRawContentChangedEvent( + [ + new textModelEvents.ModelRawEOLChanged() + ], + this._versionId, + false, + false + ) + ); + + this._emitContentChanged2(1, 1, endLineNumber, endColumn, oldModelValueLength, this.getValue(), false, false, false); + } + + public getLineMinColumn(lineNumber: number): number { + this._assertNotDisposed(); + return 1; + } + + public getLineMaxColumn(lineNumber: number): number { + this._assertNotDisposed(); + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); + } + + return this._lines[lineNumber - 1].text.length + 1; + } + + public getLineFirstNonWhitespaceColumn(lineNumber: number): number { + this._assertNotDisposed(); + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); + } + + var result = strings.firstNonWhitespaceIndex(this._lines[lineNumber - 1].text); + if (result === -1) { + return 0; + } + return result + 1; + } + + public getLineLastNonWhitespaceColumn(lineNumber: number): number { + this._assertNotDisposed(); + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); + } + + var result = strings.lastNonWhitespaceIndex(this._lines[lineNumber - 1].text); + if (result === -1) { + return 0; + } + return result + 2; + } + + public validateLineNumber(lineNumber: number): number { + this._assertNotDisposed(); + if (lineNumber < 1) { + lineNumber = 1; + } + if (lineNumber > this._lines.length) { + lineNumber = this._lines.length; + } + return lineNumber; + } + + /** + * @param strict Do NOT allow a position inside a high-low surrogate pair + */ + private _validatePosition(_lineNumber: number, _column: number, strict: boolean): Position { + const lineNumber = Math.floor(typeof _lineNumber === 'number' ? _lineNumber : 1); + const column = Math.floor(typeof _column === 'number' ? _column : 1); + + if (lineNumber < 1) { + return new Position(1, 1); + } + + if (lineNumber > this._lines.length) { + return new Position(this._lines.length, this.getLineMaxColumn(this._lines.length)); + } + + if (column <= 1) { + return new Position(lineNumber, 1); + } + + const maxColumn = this.getLineMaxColumn(lineNumber); + if (column >= maxColumn) { + return new Position(lineNumber, maxColumn); + } + + if (strict) { + // If the position would end up in the middle of a high-low surrogate pair, + // we move it to before the pair + // !!At this point, column > 1 + const charCodeBefore = this._lines[lineNumber - 1].text.charCodeAt(column - 2); + if (strings.isHighSurrogate(charCodeBefore)) { + return new Position(lineNumber, column - 1); + } + } + + return new Position(lineNumber, column); + } + + public validatePosition(position: IPosition): Position { + this._assertNotDisposed(); + return this._validatePosition(position.lineNumber, position.column, true); + } + + public validateRange(_range: IRange): Range { + this._assertNotDisposed(); + const start = this._validatePosition(_range.startLineNumber, _range.startColumn, false); + const end = this._validatePosition(_range.endLineNumber, _range.endColumn, false); + + const startLineNumber = start.lineNumber; + const startColumn = start.column; + const endLineNumber = end.lineNumber; + const endColumn = end.column; + + const startLineText = this._lines[startLineNumber - 1].text; + const endLineText = this._lines[endLineNumber - 1].text; + + const charCodeBeforeStart = (startColumn > 1 ? startLineText.charCodeAt(startColumn - 2) : 0); + const charCodeBeforeEnd = (endColumn > 1 && endColumn <= endLineText.length ? endLineText.charCodeAt(endColumn - 2) : 0); + + const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart); + const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd); + + if (!startInsideSurrogatePair && !endInsideSurrogatePair) { + return new Range(startLineNumber, startColumn, endLineNumber, endColumn); + } + + if (startLineNumber === endLineNumber && startColumn === endColumn) { + // do not expand a collapsed range, simply move it to a valid location + return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn - 1); + } + + if (startInsideSurrogatePair && endInsideSurrogatePair) { + // expand range at both ends + return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn + 1); + } + + if (startInsideSurrogatePair) { + // only expand range at the start + return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn); + } + + // only expand range at the end + return new Range(startLineNumber, startColumn, endLineNumber, endColumn + 1); + } + + public modifyPosition(rawPosition: IPosition, offset: number): Position { + this._assertNotDisposed(); + return this.getPositionAt(this.getOffsetAt(rawPosition) + offset); + } + + public getFullModelRange(): Range { + this._assertNotDisposed(); + var lineCount = this.getLineCount(); + return new Range(1, 1, lineCount, this.getLineMaxColumn(lineCount)); + } + + protected _emitModelRawContentChangedEvent(e: textModelEvents.ModelRawContentChangedEvent): void { + if (this._isDisposing) { + // Do not confuse listeners by emitting any event after disposing + return; + } + this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelRawContentChanged2, e); + } + + private _constructLines(textSource: ITextSource): void { + const tabSize = this._options.tabSize; + let rawLines = textSource.lines; + let modelLines: IModelLine[] = new Array(rawLines.length); + + for (let i = 0, len = rawLines.length; i < len; i++) { + modelLines[i] = this._createModelLine(rawLines[i], tabSize); + } + this._BOM = textSource.BOM; + this._mightContainRTL = textSource.containsRTL; + this._mightContainNonBasicASCII = !textSource.isBasicASCII; + this._EOL = textSource.EOL; + this._lines = modelLines; + this._lineStarts = null; + this._resetIndentRanges(); + } + + private _getEndOfLine(eol: editorCommon.EndOfLinePreference): string { + switch (eol) { + case editorCommon.EndOfLinePreference.LF: + return '\n'; + case editorCommon.EndOfLinePreference.CRLF: + return '\r\n'; + case editorCommon.EndOfLinePreference.TextDefined: + return this.getEOL(); + } + throw new Error('Unknown EOL preference'); + } + + public findMatches(searchString: string, rawSearchScope: any, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean, limitResultCount: number = LIMIT_FIND_COUNT): editorCommon.FindMatch[] { + this._assertNotDisposed(); + + let searchRange: Range; + if (Range.isIRange(rawSearchScope)) { + searchRange = this.validateRange(rawSearchScope); + } else { + searchRange = this.getFullModelRange(); + } + + return TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount); + } + + public findNextMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): editorCommon.FindMatch { + this._assertNotDisposed(); + const searchStart = this.validatePosition(rawSearchStart); + return TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches); + } + + public findPreviousMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): editorCommon.FindMatch { + this._assertNotDisposed(); + const searchStart = this.validatePosition(rawSearchStart); + return TextModelSearch.findPreviousMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches); + } +} diff --git a/src/vs/editor/common/model/textModelEvents.ts b/src/vs/editor/common/model/textModelEvents.ts new file mode 100644 index 0000000000..d3bddcfbf2 --- /dev/null +++ b/src/vs/editor/common/model/textModelEvents.ts @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IRange } from 'vs/editor/common/core/range'; + +/** + * @internal + */ +export const TextModelEventType = { + ModelDispose: 'modelDispose', + ModelTokensChanged: 'modelTokensChanged', + ModelLanguageChanged: 'modelLanguageChanged', + ModelOptionsChanged: 'modelOptionsChanged', + ModelContentChanged: 'contentChanged', + ModelRawContentChanged2: 'rawContentChanged2', + ModelDecorationsChanged: 'decorationsChanged', +}; + +/** + * An event describing that the current mode associated with a model has changed. + */ +export interface IModelLanguageChangedEvent { + /** + * Previous language + */ + readonly oldLanguage: string; + /** + * New language + */ + readonly newLanguage: string; +} + +export interface IModelContentChange { + /** + * The range that got replaced. + */ + readonly range: IRange; + /** + * The length of the range that got replaced. + */ + readonly rangeLength: number; + /** + * The new text for the range. + */ + readonly text: string; +} + +/** + * An event describing a change in the text of a model. + */ +export interface IModelContentChangedEvent { + readonly changes: IModelContentChange[]; + /** + * The (new) end-of-line character. + */ + readonly eol: string; + /** + * The new version id the model has transitioned to. + */ + readonly versionId: number; + /** + * Flag that indicates that this event was generated while undoing. + */ + readonly isUndoing: boolean; + /** + * Flag that indicates that this event was generated while redoing. + */ + readonly isRedoing: boolean; + /** + * Flag that indicates that all decorations were lost with this edit. + * The model has been reset to a new value. + */ + readonly isFlush: boolean; +} + +/** + * An event describing that model decorations have changed. + */ +export interface IModelDecorationsChangedEvent { + /** + * Lists of ids for added decorations. + */ + readonly addedDecorations: string[]; + /** + * Lists of ids for changed decorations. + */ + readonly changedDecorations: string[]; + /** + * List of ids for removed decorations. + */ + readonly removedDecorations: string[]; +} + +/** + * An event describing that some ranges of lines have been tokenized (their tokens have changed). + */ +export interface IModelTokensChangedEvent { + readonly ranges: { + /** + * The start of the range (inclusive) + */ + readonly fromLineNumber: number; + /** + * The end of the range (inclusive) + */ + readonly toLineNumber: number; + }[]; +} + +export interface IModelOptionsChangedEvent { + readonly tabSize: boolean; + readonly insertSpaces: boolean; + readonly trimAutoWhitespace: boolean; +} + +/** + * @internal + */ +export const enum RawContentChangedType { + Flush = 1, + LineChanged = 2, + LinesDeleted = 3, + LinesInserted = 4, + EOLChanged = 5 +} + +/** + * An event describing that a model has been reset to a new value. + * @internal + */ +export class ModelRawFlush { + public readonly changeType = RawContentChangedType.Flush; +} + +/** + * An event describing that a line has changed in a model. + * @internal + */ +export class ModelRawLineChanged { + public readonly changeType = RawContentChangedType.LineChanged; + /** + * The line that has changed. + */ + public readonly lineNumber: number; + /** + * The new value of the line. + */ + public readonly detail: string; + + constructor(lineNumber: number, detail: string) { + this.lineNumber = lineNumber; + this.detail = detail; + } +} + +/** + * An event describing that line(s) have been deleted in a model. + * @internal + */ +export class ModelRawLinesDeleted { + public readonly changeType = RawContentChangedType.LinesDeleted; + /** + * At what line the deletion began (inclusive). + */ + public readonly fromLineNumber: number; + /** + * At what line the deletion stopped (inclusive). + */ + public readonly toLineNumber: number; + + constructor(fromLineNumber: number, toLineNumber: number) { + this.fromLineNumber = fromLineNumber; + this.toLineNumber = toLineNumber; + } +} + +/** + * An event describing that line(s) have been inserted in a model. + * @internal + */ +export class ModelRawLinesInserted { + public readonly changeType = RawContentChangedType.LinesInserted; + /** + * Before what line did the insertion begin + */ + public readonly fromLineNumber: number; + /** + * `toLineNumber` - `fromLineNumber` + 1 denotes the number of lines that were inserted + */ + public readonly toLineNumber: number; + /** + * The text that was inserted + */ + public readonly detail: string; + + constructor(fromLineNumber: number, toLineNumber: number, detail: string) { + this.fromLineNumber = fromLineNumber; + this.toLineNumber = toLineNumber; + this.detail = detail; + } +} + +/** + * An event describing that a model has had its EOL changed. + * @internal + */ +export class ModelRawEOLChanged { + public readonly changeType = RawContentChangedType.EOLChanged; +} + +/** + * @internal + */ +export type ModelRawChange = ModelRawFlush | ModelRawLineChanged | ModelRawLinesDeleted | ModelRawLinesInserted | ModelRawEOLChanged; + +/** + * An event describing a change in the text of a model. + * @internal + */ +export class ModelRawContentChangedEvent { + + public readonly changes: ModelRawChange[]; + /** + * The new version id the model has transitioned to. + */ + public readonly versionId: number; + /** + * Flag that indicates that this event was generated while undoing. + */ + public readonly isUndoing: boolean; + /** + * Flag that indicates that this event was generated while redoing. + */ + public readonly isRedoing: boolean; + + constructor(changes: ModelRawChange[], versionId: number, isUndoing: boolean, isRedoing: boolean) { + this.changes = changes; + this.versionId = versionId; + this.isUndoing = isUndoing; + this.isRedoing = isRedoing; + } + + public containsEvent(type: RawContentChangedType): boolean { + for (let i = 0, len = this.changes.length; i < len; i++) { + const change = this.changes[i]; + if (change.changeType === type) { + return true; + } + } + return false; + } +} diff --git a/src/vs/editor/common/model/textModelSearch.ts b/src/vs/editor/common/model/textModelSearch.ts new file mode 100644 index 0000000000..afb71b8f3e --- /dev/null +++ b/src/vs/editor/common/model/textModelSearch.ts @@ -0,0 +1,512 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { FindMatch, EndOfLinePreference } from 'vs/editor/common/editorCommon'; +import { CharCode } from 'vs/base/common/charCode'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { getMapForWordSeparators, WordCharacterClassifier, WordCharacterClass } from 'vs/editor/common/controller/wordCharacterClassifier'; + +const LIMIT_FIND_COUNT = 999; + +export class SearchParams { + public readonly searchString: string; + public readonly isRegex: boolean; + public readonly matchCase: boolean; + public readonly wordSeparators: string; + + constructor(searchString: string, isRegex: boolean, matchCase: boolean, wordSeparators: string) { + this.searchString = searchString; + this.isRegex = isRegex; + this.matchCase = matchCase; + this.wordSeparators = wordSeparators; + } + + private static _isMultilineRegexSource(searchString: string): boolean { + if (!searchString || searchString.length === 0) { + return false; + } + + for (let i = 0, len = searchString.length; i < len; i++) { + const chCode = searchString.charCodeAt(i); + + if (chCode === CharCode.Backslash) { + + // move to next char + i++; + + if (i >= len) { + // string ends with a \ + break; + } + + const nextChCode = searchString.charCodeAt(i); + if (nextChCode === CharCode.n || nextChCode === CharCode.r) { + return true; + } + } + } + + return false; + } + + public parseSearchRequest(): SearchData { + if (this.searchString === '') { + return null; + } + + // Try to create a RegExp out of the params + let multiline: boolean; + if (this.isRegex) { + multiline = SearchParams._isMultilineRegexSource(this.searchString); + } else { + multiline = (this.searchString.indexOf('\n') >= 0); + } + + let regex: RegExp = null; + try { + regex = strings.createRegExp(this.searchString, this.isRegex, { + matchCase: this.matchCase, + wholeWord: false, + multiline: multiline, + global: true + }); + } catch (err) { + return null; + } + + if (!regex) { + return null; + } + + let canUseSimpleSearch = (!this.isRegex && !multiline); + if (canUseSimpleSearch && this.searchString.toLowerCase() !== this.searchString.toUpperCase()) { + // casing might make a difference + canUseSimpleSearch = this.matchCase; + } + + return new SearchData(regex, this.wordSeparators ? getMapForWordSeparators(this.wordSeparators) : null, canUseSimpleSearch ? this.searchString : null); + } +} + +export class SearchData { + + /** + * The regex to search for. Always defined. + */ + public readonly regex: RegExp; + /** + * The word separator classifier. + */ + public readonly wordSeparators: WordCharacterClassifier; + /** + * The simple string to search for (if possible). + */ + public readonly simpleSearch: string; + + constructor(regex: RegExp, wordSeparators: WordCharacterClassifier, simpleSearch: string) { + this.regex = regex; + this.wordSeparators = wordSeparators; + this.simpleSearch = simpleSearch; + } +} + +function createFindMatch(range: Range, rawMatches: RegExpExecArray, captureMatches: boolean): FindMatch { + if (!captureMatches) { + return new FindMatch(range, null); + } + let matches: string[] = []; + for (let i = 0, len = rawMatches.length; i < len; i++) { + matches[i] = rawMatches[i]; + } + return new FindMatch(range, matches); +} + +export class TextModelSearch { + + public static findMatches(model: TextModel, searchParams: SearchParams, searchRange: Range, captureMatches: boolean, limitResultCount: number): FindMatch[] { + const searchData = searchParams.parseSearchRequest(); + if (!searchData) { + return []; + } + + if (searchData.regex.multiline) { + return this._doFindMatchesMultiline(model, searchRange, new Searcher(searchData.wordSeparators, searchData.regex), captureMatches, limitResultCount); + } + return this._doFindMatchesLineByLine(model, searchRange, searchData, captureMatches, limitResultCount); + } + + /** + * Multiline search always executes on the lines concatenated with \n. + * We must therefore compensate for the count of \n in case the model is CRLF + */ + private static _getMultilineMatchRange(model: TextModel, deltaOffset: number, text: string, matchIndex: number, match0: string): Range { + let startOffset: number; + if (model.getEOL() === '\r\n') { + let lineFeedCountBeforeMatch = 0; + for (let i = 0; i < matchIndex; i++) { + let chCode = text.charCodeAt(i); + if (chCode === CharCode.LineFeed) { + lineFeedCountBeforeMatch++; + } + } + startOffset = deltaOffset + matchIndex + lineFeedCountBeforeMatch /* add as many \r as there were \n */; + } else { + startOffset = deltaOffset + matchIndex; + } + + let endOffset: number; + if (model.getEOL() === '\r\n') { + let lineFeedCountInMatch = 0; + for (let i = 0, len = match0.length; i < len; i++) { + let chCode = text.charCodeAt(i + matchIndex); + if (chCode === CharCode.LineFeed) { + lineFeedCountInMatch++; + } + } + endOffset = startOffset + match0.length + lineFeedCountInMatch /* add as many \r as there were \n */; + } else { + endOffset = startOffset + match0.length; + } + + const startPosition = model.getPositionAt(startOffset); + const endPosition = model.getPositionAt(endOffset); + return new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column); + } + + private static _doFindMatchesMultiline(model: TextModel, searchRange: Range, searcher: Searcher, captureMatches: boolean, limitResultCount: number): FindMatch[] { + const deltaOffset = model.getOffsetAt(searchRange.getStartPosition()); + // We always execute multiline search over the lines joined with \n + // This makes it that \n will match the EOL for both CRLF and LF models + // We compensate for offset errors in `_getMultilineMatchRange` + const text = model.getValueInRange(searchRange, EndOfLinePreference.LF); + + const result: FindMatch[] = []; + let counter = 0; + + let m: RegExpExecArray; + searcher.reset(0); + while ((m = searcher.next(text))) { + result[counter++] = createFindMatch(this._getMultilineMatchRange(model, deltaOffset, text, m.index, m[0]), m, captureMatches); + if (counter >= limitResultCount) { + return result; + } + } + + return result; + } + + private static _doFindMatchesLineByLine(model: TextModel, searchRange: Range, searchData: SearchData, captureMatches: boolean, limitResultCount: number): FindMatch[] { + const result: FindMatch[] = []; + let resultLen = 0; + + // Early case for a search range that starts & stops on the same line number + if (searchRange.startLineNumber === searchRange.endLineNumber) { + const text = model.getLineContent(searchRange.startLineNumber).substring(searchRange.startColumn - 1, searchRange.endColumn - 1); + resultLen = this._findMatchesInLine(searchData, text, searchRange.startLineNumber, searchRange.startColumn - 1, resultLen, result, captureMatches, limitResultCount); + return result; + } + + // Collect results from first line + const text = model.getLineContent(searchRange.startLineNumber).substring(searchRange.startColumn - 1); + resultLen = this._findMatchesInLine(searchData, text, searchRange.startLineNumber, searchRange.startColumn - 1, resultLen, result, captureMatches, limitResultCount); + + // Collect results from middle lines + for (let lineNumber = searchRange.startLineNumber + 1; lineNumber < searchRange.endLineNumber && resultLen < limitResultCount; lineNumber++) { + resultLen = this._findMatchesInLine(searchData, model.getLineContent(lineNumber), lineNumber, 0, resultLen, result, captureMatches, limitResultCount); + } + + // Collect results from last line + if (resultLen < limitResultCount) { + const text = model.getLineContent(searchRange.endLineNumber).substring(0, searchRange.endColumn - 1); + resultLen = this._findMatchesInLine(searchData, text, searchRange.endLineNumber, 0, resultLen, result, captureMatches, limitResultCount); + } + + return result; + } + + private static _findMatchesInLine(searchData: SearchData, text: string, lineNumber: number, deltaOffset: number, resultLen: number, result: FindMatch[], captureMatches: boolean, limitResultCount: number): number { + const wordSeparators = searchData.wordSeparators; + if (!captureMatches && searchData.simpleSearch) { + const searchString = searchData.simpleSearch; + const searchStringLen = searchString.length; + const textLength = text.length; + + let lastMatchIndex = -searchStringLen; + while ((lastMatchIndex = text.indexOf(searchString, lastMatchIndex + searchStringLen)) !== -1) { + if (!wordSeparators || isValidMatch(wordSeparators, text, textLength, lastMatchIndex, searchStringLen)) { + result[resultLen++] = new FindMatch(new Range(lineNumber, lastMatchIndex + 1 + deltaOffset, lineNumber, lastMatchIndex + 1 + searchStringLen + deltaOffset), null); + if (resultLen >= limitResultCount) { + return resultLen; + } + } + } + return resultLen; + } + + const searcher = new Searcher(searchData.wordSeparators, searchData.regex); + let m: RegExpExecArray; + // Reset regex to search from the beginning + searcher.reset(0); + do { + m = searcher.next(text); + if (m) { + result[resultLen++] = createFindMatch(new Range(lineNumber, m.index + 1 + deltaOffset, lineNumber, m.index + 1 + m[0].length + deltaOffset), m, captureMatches); + if (resultLen >= limitResultCount) { + return resultLen; + } + } + } while (m); + return resultLen; + } + + public static findNextMatch(model: TextModel, searchParams: SearchParams, searchStart: Position, captureMatches: boolean): FindMatch { + const searchData = searchParams.parseSearchRequest(); + if (!searchData) { + return null; + } + + const searcher = new Searcher(searchData.wordSeparators, searchData.regex); + + if (searchData.regex.multiline) { + return this._doFindNextMatchMultiline(model, searchStart, searcher, captureMatches); + } + return this._doFindNextMatchLineByLine(model, searchStart, searcher, captureMatches); + } + + private static _doFindNextMatchMultiline(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch { + const searchTextStart = new Position(searchStart.lineNumber, 1); + const deltaOffset = model.getOffsetAt(searchTextStart); + const lineCount = model.getLineCount(); + // We always execute multiline search over the lines joined with \n + // This makes it that \n will match the EOL for both CRLF and LF models + // We compensate for offset errors in `_getMultilineMatchRange` + const text = model.getValueInRange(new Range(searchTextStart.lineNumber, searchTextStart.column, lineCount, model.getLineMaxColumn(lineCount)), EndOfLinePreference.LF); + searcher.reset(searchStart.column - 1); + let m = searcher.next(text); + if (m) { + return createFindMatch( + this._getMultilineMatchRange(model, deltaOffset, text, m.index, m[0]), + m, + captureMatches + ); + } + + if (searchStart.lineNumber !== 1 || searchStart.column !== 1) { + // Try again from the top + return this._doFindNextMatchMultiline(model, new Position(1, 1), searcher, captureMatches); + } + + return null; + } + + private static _doFindNextMatchLineByLine(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch { + const lineCount = model.getLineCount(); + const startLineNumber = searchStart.lineNumber; + + // Look in first line + const text = model.getLineContent(startLineNumber); + const r = this._findFirstMatchInLine(searcher, text, startLineNumber, searchStart.column, captureMatches); + if (r) { + return r; + } + + for (let i = 1; i <= lineCount; i++) { + const lineIndex = (startLineNumber + i - 1) % lineCount; + const text = model.getLineContent(lineIndex + 1); + const r = this._findFirstMatchInLine(searcher, text, lineIndex + 1, 1, captureMatches); + if (r) { + return r; + } + } + + return null; + } + + private static _findFirstMatchInLine(searcher: Searcher, text: string, lineNumber: number, fromColumn: number, captureMatches: boolean): FindMatch { + // Set regex to search from column + searcher.reset(fromColumn - 1); + const m: RegExpExecArray = searcher.next(text); + if (m) { + return createFindMatch( + new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length), + m, + captureMatches + ); + } + return null; + } + + public static findPreviousMatch(model: TextModel, searchParams: SearchParams, searchStart: Position, captureMatches: boolean): FindMatch { + const searchData = searchParams.parseSearchRequest(); + if (!searchData) { + return null; + } + + const searcher = new Searcher(searchData.wordSeparators, searchData.regex); + + if (searchData.regex.multiline) { + return this._doFindPreviousMatchMultiline(model, searchStart, searcher, captureMatches); + } + return this._doFindPreviousMatchLineByLine(model, searchStart, searcher, captureMatches); + } + + private static _doFindPreviousMatchMultiline(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch { + const matches = this._doFindMatchesMultiline(model, new Range(1, 1, searchStart.lineNumber, searchStart.column), searcher, captureMatches, 10 * LIMIT_FIND_COUNT); + if (matches.length > 0) { + return matches[matches.length - 1]; + } + + const lineCount = model.getLineCount(); + if (searchStart.lineNumber !== lineCount || searchStart.column !== model.getLineMaxColumn(lineCount)) { + // Try again with all content + return this._doFindPreviousMatchMultiline(model, new Position(lineCount, model.getLineMaxColumn(lineCount)), searcher, captureMatches); + } + + return null; + } + + private static _doFindPreviousMatchLineByLine(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch { + const lineCount = model.getLineCount(); + const startLineNumber = searchStart.lineNumber; + + // Look in first line + const text = model.getLineContent(startLineNumber).substring(0, searchStart.column - 1); + const r = this._findLastMatchInLine(searcher, text, startLineNumber, captureMatches); + if (r) { + return r; + } + + for (let i = 1; i <= lineCount; i++) { + const lineIndex = (lineCount + startLineNumber - i - 1) % lineCount; + const text = model.getLineContent(lineIndex + 1); + const r = this._findLastMatchInLine(searcher, text, lineIndex + 1, captureMatches); + if (r) { + return r; + } + } + + return null; + } + + private static _findLastMatchInLine(searcher: Searcher, text: string, lineNumber: number, captureMatches: boolean): FindMatch { + let bestResult: FindMatch = null; + let m: RegExpExecArray; + searcher.reset(0); + while ((m = searcher.next(text))) { + bestResult = createFindMatch(new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length), m, captureMatches); + } + return bestResult; + } +} + +function leftIsWordBounday(wordSeparators: WordCharacterClassifier, text: string, textLength: number, matchStartIndex: number, matchLength: number): boolean { + if (matchStartIndex === 0) { + // Match starts at start of string + return true; + } + + const charBefore = text.charCodeAt(matchStartIndex - 1); + if (wordSeparators.get(charBefore) !== WordCharacterClass.Regular) { + // The character before the match is a word separator + return true; + } + + if (matchLength > 0) { + const firstCharInMatch = text.charCodeAt(matchStartIndex); + if (wordSeparators.get(firstCharInMatch) !== WordCharacterClass.Regular) { + // The first character inside the match is a word separator + return true; + } + } + + return false; +} + +function rightIsWordBounday(wordSeparators: WordCharacterClassifier, text: string, textLength: number, matchStartIndex: number, matchLength: number): boolean { + if (matchStartIndex + matchLength === textLength) { + // Match ends at end of string + return true; + } + + const charAfter = text.charCodeAt(matchStartIndex + matchLength); + if (wordSeparators.get(charAfter) !== WordCharacterClass.Regular) { + // The character after the match is a word separator + return true; + } + + if (matchLength > 0) { + const lastCharInMatch = text.charCodeAt(matchStartIndex + matchLength - 1); + if (wordSeparators.get(lastCharInMatch) !== WordCharacterClass.Regular) { + // The last character in the match is a word separator + return true; + } + } + + return false; +} + +function isValidMatch(wordSeparators: WordCharacterClassifier, text: string, textLength: number, matchStartIndex: number, matchLength: number): boolean { + return ( + leftIsWordBounday(wordSeparators, text, textLength, matchStartIndex, matchLength) + && rightIsWordBounday(wordSeparators, text, textLength, matchStartIndex, matchLength) + ); +} + +class Searcher { + private _wordSeparators: WordCharacterClassifier; + private _searchRegex: RegExp; + private _prevMatchStartIndex: number; + private _prevMatchLength: number; + + constructor(wordSeparators: WordCharacterClassifier, searchRegex: RegExp, ) { + this._wordSeparators = wordSeparators; + this._searchRegex = searchRegex; + this._prevMatchStartIndex = -1; + this._prevMatchLength = 0; + } + + public reset(lastIndex: number): void { + this._searchRegex.lastIndex = lastIndex; + this._prevMatchStartIndex = -1; + this._prevMatchLength = 0; + } + + public next(text: string): RegExpExecArray { + const textLength = text.length; + + let m: RegExpExecArray; + do { + if (this._prevMatchStartIndex + this._prevMatchLength === textLength) { + // Reached the end of the line + return null; + } + + m = this._searchRegex.exec(text); + if (!m) { + return null; + } + + const matchStartIndex = m.index; + const matchLength = m[0].length; + if (matchStartIndex === this._prevMatchStartIndex && matchLength === this._prevMatchLength) { + // Exit early if the regex matches the same range twice + return null; + } + this._prevMatchStartIndex = matchStartIndex; + this._prevMatchLength = matchLength; + + if (!this._wordSeparators || isValidMatch(this._wordSeparators, text, textLength, matchStartIndex, matchLength)) { + return m; + } + + } while (m); + + return null; + } +} diff --git a/src/vs/editor/common/model/textModelWithDecorations.ts b/src/vs/editor/common/model/textModelWithDecorations.ts new file mode 100644 index 0000000000..1cbd19ca73 --- /dev/null +++ b/src/vs/editor/common/model/textModelWithDecorations.ts @@ -0,0 +1,969 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IMarkdownString, markedStringsEquals } from 'vs/base/common/htmlContent'; +import * as strings from 'vs/base/common/strings'; +import { CharCode } from 'vs/base/common/charCode'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { MarkersTracker, LineMarker } from 'vs/editor/common/model/modelLine'; +import { Position } from 'vs/editor/common/core/position'; +import { INewMarker, TextModelWithMarkers } from 'vs/editor/common/model/textModelWithMarkers'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { ITextSource, IRawTextSource } from 'vs/editor/common/model/textSource'; +import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; +import { ThemeColor } from 'vs/platform/theme/common/themeService'; + +export const ClassName = { + EditorWarningDecoration: 'greensquiggly', + EditorErrorDecoration: 'redsquiggly' +}; + +class DecorationsTracker { + + public addedDecorations: string[]; + public addedDecorationsLen: number; + public changedDecorations: string[]; + public changedDecorationsLen: number; + public removedDecorations: string[]; + public removedDecorationsLen: number; + + constructor() { + this.addedDecorations = []; + this.addedDecorationsLen = 0; + this.changedDecorations = []; + this.changedDecorationsLen = 0; + this.removedDecorations = []; + this.removedDecorationsLen = 0; + } + + // --- Build decoration events + + public addNewDecoration(id: string): void { + this.addedDecorations[this.addedDecorationsLen++] = id; + } + + public addRemovedDecoration(id: string): void { + this.removedDecorations[this.removedDecorationsLen++] = id; + } + + public addMovedDecoration(id: string): void { + this.changedDecorations[this.changedDecorationsLen++] = id; + } + + public addUpdatedDecoration(id: string): void { + this.changedDecorations[this.changedDecorationsLen++] = id; + } +} + +export class InternalDecoration implements editorCommon.IModelDecoration { + _internalDecorationBrand: void; + + public readonly id: string; + public readonly internalId: number; + public readonly ownerId: number; + public readonly startMarker: LineMarker; + public readonly endMarker: LineMarker; + public options: ModelDecorationOptions; + public isForValidation: boolean; + public range: Range; + + constructor(id: string, internalId: number, ownerId: number, range: Range, startMarker: LineMarker, endMarker: LineMarker, options: ModelDecorationOptions) { + this.id = id; + this.internalId = internalId; + this.ownerId = ownerId; + this.range = range; + this.startMarker = startMarker; + this.endMarker = endMarker; + this.setOptions(options); + } + + public setOptions(options: ModelDecorationOptions) { + this.options = options; + this.isForValidation = ( + this.options.className === ClassName.EditorErrorDecoration + || this.options.className === ClassName.EditorWarningDecoration + ); + } + + public setRange(multiLineDecorationsMap: { [key: string]: InternalDecoration; }, range: Range): void { + if (this.range.equalsRange(range)) { + return; + } + + let rangeWasMultiLine = (this.range.startLineNumber !== this.range.endLineNumber); + this.range = range; + let rangeIsMultiline = (this.range.startLineNumber !== this.range.endLineNumber); + + if (rangeWasMultiLine === rangeIsMultiline) { + return; + } + + if (rangeIsMultiline) { + multiLineDecorationsMap[this.id] = this; + } else { + delete multiLineDecorationsMap[this.id]; + } + } +} + +let _INSTANCE_COUNT = 0; +/** + * Produces 'a'-'z', followed by 'A'-'Z'... followed by 'a'-'z', etc. + */ +function nextInstanceId(): string { + const LETTERS_CNT = (CharCode.Z - CharCode.A + 1); + + let result = _INSTANCE_COUNT++; + result = result % (2 * LETTERS_CNT); + + if (result < LETTERS_CNT) { + return String.fromCharCode(CharCode.a + result); + } + + return String.fromCharCode(CharCode.A + result - LETTERS_CNT); +} + +export class TextModelWithDecorations extends TextModelWithMarkers implements editorCommon.ITextModelWithDecorations { + + /** + * Used to workaround broken clients that might attempt using a decoration id generated by a different model. + * It is not globally unique in order to limit it to one character. + */ + private readonly _instanceId: string; + private _lastDecorationId: number; + + private _currentDecorationsTracker: DecorationsTracker; + private _currentDecorationsTrackerCnt: number; + + private _currentMarkersTracker: MarkersTracker; + private _currentMarkersTrackerCnt: number; + + private _decorations: { [decorationId: string]: InternalDecoration; }; + private _internalDecorations: { [internalDecorationId: number]: InternalDecoration; }; + private _multiLineDecorationsMap: { [key: string]: InternalDecoration; }; + + constructor(rawTextSource: IRawTextSource, creationOptions: editorCommon.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier) { + super(rawTextSource, creationOptions, languageIdentifier); + + this._instanceId = nextInstanceId(); + this._lastDecorationId = 0; + + // Initialize decorations + this._currentDecorationsTracker = null; + this._currentDecorationsTrackerCnt = 0; + + this._currentMarkersTracker = null; + this._currentMarkersTrackerCnt = 0; + + this._decorations = Object.create(null); + this._internalDecorations = Object.create(null); + this._multiLineDecorationsMap = Object.create(null); + } + + public dispose(): void { + this._decorations = null; + this._internalDecorations = null; + this._multiLineDecorationsMap = null; + super.dispose(); + } + + protected _resetValue(newValue: ITextSource): void { + super._resetValue(newValue); + + // Destroy all my decorations + this._decorations = Object.create(null); + this._internalDecorations = Object.create(null); + this._multiLineDecorationsMap = Object.create(null); + } + + private static _shouldStartMarkerSticksToPreviousCharacter(stickiness: editorCommon.TrackedRangeStickiness): boolean { + if (stickiness === editorCommon.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges || stickiness === editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore) { + return true; + } + return false; + } + + private static _shouldEndMarkerSticksToPreviousCharacter(stickiness: editorCommon.TrackedRangeStickiness): boolean { + if (stickiness === editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges || stickiness === editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore) { + return true; + } + return false; + } + + _getTrackedRangesCount(): number { + return Object.keys(this._decorations).length; + } + + // --- END TrackedRanges + + public changeDecorations(callback: (changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => T, ownerId: number = 0): T { + this._assertNotDisposed(); + + try { + this._eventEmitter.beginDeferredEmit(); + let decorationsTracker = this._acquireDecorationsTracker(); + return this._changeDecorations(decorationsTracker, ownerId, callback); + } finally { + this._releaseDecorationsTracker(); + this._eventEmitter.endDeferredEmit(); + } + } + + private _changeDecorations(decorationsTracker: DecorationsTracker, ownerId: number, callback: (changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => T): T { + let changeAccessor: editorCommon.IModelDecorationsChangeAccessor = { + addDecoration: (range: IRange, options: editorCommon.IModelDecorationOptions): string => { + return this._addDecorationImpl(decorationsTracker, ownerId, this.validateRange(range), _normalizeOptions(options)); + }, + changeDecoration: (id: string, newRange: IRange): void => { + this._changeDecorationImpl(decorationsTracker, id, this.validateRange(newRange)); + }, + changeDecorationOptions: (id: string, options: editorCommon.IModelDecorationOptions) => { + this._changeDecorationOptionsImpl(decorationsTracker, id, _normalizeOptions(options)); + }, + removeDecoration: (id: string): void => { + this._removeDecorationImpl(decorationsTracker, id); + }, + deltaDecorations: (oldDecorations: string[], newDecorations: editorCommon.IModelDeltaDecoration[]): string[] => { + return this._deltaDecorationsImpl(decorationsTracker, ownerId, oldDecorations, this._normalizeDeltaDecorations(newDecorations)); + } + }; + let result: T = null; + try { + result = callback(changeAccessor); + } catch (e) { + onUnexpectedError(e); + } + // Invalidate change accessor + changeAccessor.addDecoration = null; + changeAccessor.changeDecoration = null; + changeAccessor.removeDecoration = null; + changeAccessor.deltaDecorations = null; + return result; + } + + public deltaDecorations(oldDecorations: string[], newDecorations: editorCommon.IModelDeltaDecoration[], ownerId: number = 0): string[] { + this._assertNotDisposed(); + if (!oldDecorations) { + oldDecorations = []; + } + return this.changeDecorations((changeAccessor) => { + return changeAccessor.deltaDecorations(oldDecorations, newDecorations); + }, ownerId); + } + + public removeAllDecorationsWithOwnerId(ownerId: number): void { + let toRemove: string[] = []; + + for (let decorationId in this._decorations) { + // No `hasOwnProperty` call due to using Object.create(null) + + let decoration = this._decorations[decorationId]; + + if (decoration.ownerId === ownerId) { + toRemove.push(decoration.id); + } + } + + this._removeDecorationsImpl(null, toRemove); + } + + public getDecorationOptions(decorationId: string): editorCommon.IModelDecorationOptions { + let decoration = this._decorations[decorationId]; + if (!decoration) { + return null; + } + return decoration.options; + } + + public getDecorationRange(decorationId: string): Range { + let decoration = this._decorations[decorationId]; + if (!decoration) { + return null; + } + return decoration.range; + } + + public getLineDecorations(lineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false): editorCommon.IModelDecoration[] { + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + return []; + } + + return this.getLinesDecorations(lineNumber, lineNumber, ownerId, filterOutValidation); + } + + /** + * Fetch only multi-line decorations that intersect with the given line number range + */ + private _getMultiLineDecorations(filterRange: Range, filterOwnerId: number, filterOutValidation: boolean): InternalDecoration[] { + const filterStartLineNumber = filterRange.startLineNumber; + const filterStartColumn = filterRange.startColumn; + const filterEndLineNumber = filterRange.endLineNumber; + const filterEndColumn = filterRange.endColumn; + + let result: InternalDecoration[] = [], resultLen = 0; + + for (let decorationId in this._multiLineDecorationsMap) { + // No `hasOwnProperty` call due to using Object.create(null) + let decoration = this._multiLineDecorationsMap[decorationId]; + + if (filterOwnerId && decoration.ownerId && decoration.ownerId !== filterOwnerId) { + continue; + } + + if (filterOutValidation && decoration.isForValidation) { + continue; + } + + let range = decoration.range; + + if (range.startLineNumber > filterEndLineNumber) { + continue; + } + if (range.startLineNumber === filterEndLineNumber && range.startColumn > filterEndColumn) { + continue; + } + if (range.endLineNumber < filterStartLineNumber) { + continue; + } + if (range.endLineNumber === filterStartLineNumber && range.endColumn < filterStartColumn) { + continue; + } + + result[resultLen++] = decoration; + } + + return result; + } + + private _getDecorationsInRange(filterRange: Range, filterOwnerId: number, filterOutValidation: boolean): InternalDecoration[] { + const filterStartLineNumber = filterRange.startLineNumber; + const filterStartColumn = filterRange.startColumn; + const filterEndLineNumber = filterRange.endLineNumber; + const filterEndColumn = filterRange.endColumn; + + let result = this._getMultiLineDecorations(filterRange, filterOwnerId, filterOutValidation); + let resultLen = result.length; + let resultMap: { [decorationId: string]: boolean; } = {}; + + for (let i = 0, len = resultLen; i < len; i++) { + resultMap[result[i].id] = true; + } + + for (let lineNumber = filterStartLineNumber; lineNumber <= filterEndLineNumber; lineNumber++) { + let lineMarkers = this._lines[lineNumber - 1].getMarkers(); + if (lineMarkers === null) { + continue; + } + for (let i = 0, len = lineMarkers.length; i < len; i++) { + let lineMarker = lineMarkers[i]; + let internalDecorationId = lineMarker.internalDecorationId; + + if (internalDecorationId === 0) { + // marker does not belong to any decoration + continue; + } + + let decoration = this._internalDecorations[internalDecorationId]; + + if (resultMap.hasOwnProperty(decoration.id)) { + // decoration already in result + continue; + } + + if (filterOwnerId && decoration.ownerId && decoration.ownerId !== filterOwnerId) { + continue; + } + + if (filterOutValidation && decoration.isForValidation) { + continue; + } + + let range = decoration.range; + + if (range.startLineNumber > filterEndLineNumber) { + continue; + } + if (range.startLineNumber === filterEndLineNumber && range.startColumn > filterEndColumn) { + continue; + } + if (range.endLineNumber < filterStartLineNumber) { + continue; + } + if (range.endLineNumber === filterStartLineNumber && range.endColumn < filterStartColumn) { + continue; + } + + result[resultLen++] = decoration; + resultMap[decoration.id] = true; + } + } + + return result; + } + + public getLinesDecorations(_startLineNumber: number, _endLineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false): editorCommon.IModelDecoration[] { + let lineCount = this.getLineCount(); + let startLineNumber = Math.min(lineCount, Math.max(1, _startLineNumber)); + let endLineNumber = Math.min(lineCount, Math.max(1, _endLineNumber)); + let endColumn = this.getLineMaxColumn(endLineNumber); + return this._getDecorationsInRange(new Range(startLineNumber, 1, endLineNumber, endColumn), ownerId, filterOutValidation); + } + + public getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean): editorCommon.IModelDecoration[] { + let validatedRange = this.validateRange(range); + return this._getDecorationsInRange(validatedRange, ownerId, filterOutValidation); + } + + public getAllDecorations(ownerId: number = 0, filterOutValidation: boolean = false): editorCommon.IModelDecoration[] { + let result: InternalDecoration[] = [], resultLen = 0; + + for (let decorationId in this._decorations) { + // No `hasOwnProperty` call due to using Object.create(null) + let decoration = this._decorations[decorationId]; + + if (ownerId && decoration.ownerId && decoration.ownerId !== ownerId) { + continue; + } + + if (filterOutValidation && decoration.isForValidation) { + continue; + } + + result[resultLen++] = decoration; + } + + return result; + } + + protected _acquireMarkersTracker(): MarkersTracker { + if (this._currentMarkersTrackerCnt === 0) { + this._currentMarkersTracker = new MarkersTracker(); + } + this._currentMarkersTrackerCnt++; + return this._currentMarkersTracker; + } + + protected _releaseMarkersTracker(): void { + this._currentMarkersTrackerCnt--; + if (this._currentMarkersTrackerCnt === 0) { + let markersTracker = this._currentMarkersTracker; + this._currentMarkersTracker = null; + this._handleTrackedMarkers(markersTracker); + } + } + + /** + * Handle changed markers (i.e. update decorations ranges and return the changed decorations, unique and sorted by id) + */ + private _handleTrackedMarkers(markersTracker: MarkersTracker): void { + let changedInternalDecorationIds = markersTracker.getDecorationIds(); + if (changedInternalDecorationIds.length === 0) { + return; + } + + changedInternalDecorationIds.sort(); + + let uniqueChangedDecorations: string[] = [], uniqueChangedDecorationsLen = 0; + let previousInternalDecorationId: number = 0; + for (let i = 0, len = changedInternalDecorationIds.length; i < len; i++) { + let internalDecorationId = changedInternalDecorationIds[i]; + if (internalDecorationId === previousInternalDecorationId) { + continue; + } + previousInternalDecorationId = internalDecorationId; + + let decoration = this._internalDecorations[internalDecorationId]; + if (!decoration) { + // perhaps the decoration was removed in the meantime + continue; + } + + let startMarker = decoration.startMarker.position; + let endMarker = decoration.endMarker.position; + let range = TextModelWithDecorations._createRangeFromMarkers(startMarker, endMarker); + decoration.setRange(this._multiLineDecorationsMap, range); + + uniqueChangedDecorations[uniqueChangedDecorationsLen++] = decoration.id; + } + + if (uniqueChangedDecorations.length > 0) { + let e: textModelEvents.IModelDecorationsChangedEvent = { + addedDecorations: [], + changedDecorations: uniqueChangedDecorations, + removedDecorations: [] + }; + this.emitModelDecorationsChangedEvent(e); + } + } + + private static _createRangeFromMarkers(startPosition: Position, endPosition: Position): Range { + if (endPosition.isBefore(startPosition)) { + // This tracked range has turned in on itself (end marker before start marker) + // This can happen in extreme editing conditions where lots of text is removed and lots is added + + // Treat it as a collapsed range + return new Range(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column); + } + return new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column); + } + + private _acquireDecorationsTracker(): DecorationsTracker { + if (this._currentDecorationsTrackerCnt === 0) { + this._currentDecorationsTracker = new DecorationsTracker(); + } + this._currentDecorationsTrackerCnt++; + return this._currentDecorationsTracker; + } + + private _releaseDecorationsTracker(): void { + this._currentDecorationsTrackerCnt--; + if (this._currentDecorationsTrackerCnt === 0) { + let decorationsTracker = this._currentDecorationsTracker; + this._currentDecorationsTracker = null; + this._handleTrackedDecorations(decorationsTracker); + } + } + + private _handleTrackedDecorations(decorationsTracker: DecorationsTracker): void { + if ( + decorationsTracker.addedDecorationsLen === 0 + && decorationsTracker.changedDecorationsLen === 0 + && decorationsTracker.removedDecorationsLen === 0 + ) { + return; + } + + let e: textModelEvents.IModelDecorationsChangedEvent = { + addedDecorations: decorationsTracker.addedDecorations, + changedDecorations: decorationsTracker.changedDecorations, + removedDecorations: decorationsTracker.removedDecorations + }; + this.emitModelDecorationsChangedEvent(e); + } + + private emitModelDecorationsChangedEvent(e: textModelEvents.IModelDecorationsChangedEvent): void { + if (!this._isDisposing) { + this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelDecorationsChanged, e); + } + } + + private _normalizeDeltaDecorations(deltaDecorations: editorCommon.IModelDeltaDecoration[]): ModelDeltaDecoration[] { + let result: ModelDeltaDecoration[] = []; + for (let i = 0, len = deltaDecorations.length; i < len; i++) { + let deltaDecoration = deltaDecorations[i]; + result.push(new ModelDeltaDecoration(i, this.validateRange(deltaDecoration.range), _normalizeOptions(deltaDecoration.options))); + } + return result; + } + + private _externalDecorationId(internalId: number): string { + return `${this._instanceId};${internalId}`; + } + + private _addDecorationImpl(decorationsTracker: DecorationsTracker, ownerId: number, _range: Range, options: ModelDecorationOptions): string { + let range = this.validateRange(_range); + + let internalDecorationId = (++this._lastDecorationId); + let decorationId = this._externalDecorationId(internalDecorationId); + + let markers = this._addMarkers([ + { + internalDecorationId: internalDecorationId, + position: new Position(range.startLineNumber, range.startColumn), + stickToPreviousCharacter: TextModelWithDecorations._shouldStartMarkerSticksToPreviousCharacter(options.stickiness) + }, + { + internalDecorationId: internalDecorationId, + position: new Position(range.endLineNumber, range.endColumn), + stickToPreviousCharacter: TextModelWithDecorations._shouldEndMarkerSticksToPreviousCharacter(options.stickiness) + } + ]); + + let decoration = new InternalDecoration(decorationId, internalDecorationId, ownerId, range, markers[0], markers[1], options); + this._decorations[decorationId] = decoration; + this._internalDecorations[internalDecorationId] = decoration; + if (range.startLineNumber !== range.endLineNumber) { + this._multiLineDecorationsMap[decorationId] = decoration; + } + + decorationsTracker.addNewDecoration(decorationId); + + return decorationId; + } + + private _addDecorationsImpl(decorationsTracker: DecorationsTracker, ownerId: number, newDecorations: ModelDeltaDecoration[]): string[] { + let internalDecorationIds: number[] = []; + let decorationIds: string[] = []; + let newMarkers: INewMarker[] = []; + + for (let i = 0, len = newDecorations.length; i < len; i++) { + let newDecoration = newDecorations[i]; + let range = newDecoration.range; + let stickiness = newDecoration.options.stickiness; + + let internalDecorationId = (++this._lastDecorationId); + let decorationId = this._externalDecorationId(internalDecorationId); + + internalDecorationIds[i] = internalDecorationId; + decorationIds[i] = decorationId; + + newMarkers[2 * i] = { + internalDecorationId: internalDecorationId, + position: new Position(range.startLineNumber, range.startColumn), + stickToPreviousCharacter: TextModelWithDecorations._shouldStartMarkerSticksToPreviousCharacter(stickiness) + }; + + newMarkers[2 * i + 1] = { + internalDecorationId: internalDecorationId, + position: new Position(range.endLineNumber, range.endColumn), + stickToPreviousCharacter: TextModelWithDecorations._shouldEndMarkerSticksToPreviousCharacter(stickiness) + }; + } + + let markerIds = this._addMarkers(newMarkers); + + for (let i = 0, len = newDecorations.length; i < len; i++) { + let newDecoration = newDecorations[i]; + let range = newDecoration.range; + let internalDecorationId = internalDecorationIds[i]; + let decorationId = decorationIds[i]; + let startMarker = markerIds[2 * i]; + let endMarker = markerIds[2 * i + 1]; + + let decoration = new InternalDecoration(decorationId, internalDecorationId, ownerId, range, startMarker, endMarker, newDecoration.options); + this._decorations[decorationId] = decoration; + this._internalDecorations[internalDecorationId] = decoration; + if (range.startLineNumber !== range.endLineNumber) { + this._multiLineDecorationsMap[decorationId] = decoration; + } + + decorationsTracker.addNewDecoration(decorationId); + } + + return decorationIds; + } + + private _changeDecorationImpl(decorationsTracker: DecorationsTracker, decorationId: string, newRange: Range): void { + let decoration = this._decorations[decorationId]; + if (!decoration) { + return; + } + + let startMarker = decoration.startMarker; + if (newRange.startLineNumber !== startMarker.position.lineNumber) { + // move marker between lines + this._lines[startMarker.position.lineNumber - 1].removeMarker(startMarker); + this._lines[newRange.startLineNumber - 1].addMarker(startMarker); + } + startMarker.setPosition(new Position(newRange.startLineNumber, newRange.startColumn)); + + let endMarker = decoration.endMarker; + if (newRange.endLineNumber !== endMarker.position.lineNumber) { + // move marker between lines + this._lines[endMarker.position.lineNumber - 1].removeMarker(endMarker); + this._lines[newRange.endLineNumber - 1].addMarker(endMarker); + } + endMarker.setPosition(new Position(newRange.endLineNumber, newRange.endColumn)); + + decoration.setRange(this._multiLineDecorationsMap, newRange); + + decorationsTracker.addMovedDecoration(decorationId); + } + + private _changeDecorationOptionsImpl(decorationsTracker: DecorationsTracker, decorationId: string, options: ModelDecorationOptions): void { + let decoration = this._decorations[decorationId]; + if (!decoration) { + return; + } + + if (decoration.options.stickiness !== options.stickiness) { + decoration.startMarker.stickToPreviousCharacter = TextModelWithDecorations._shouldStartMarkerSticksToPreviousCharacter(options.stickiness); + decoration.endMarker.stickToPreviousCharacter = TextModelWithDecorations._shouldEndMarkerSticksToPreviousCharacter(options.stickiness); + } + + decoration.setOptions(options); + + decorationsTracker.addUpdatedDecoration(decorationId); + } + + private _removeDecorationImpl(decorationsTracker: DecorationsTracker, decorationId: string): void { + let decoration = this._decorations[decorationId]; + if (!decoration) { + return; + } + + this._removeMarkers([decoration.startMarker, decoration.endMarker]); + + delete this._multiLineDecorationsMap[decorationId]; + delete this._decorations[decorationId]; + delete this._internalDecorations[decoration.internalId]; + + if (decorationsTracker) { + decorationsTracker.addRemovedDecoration(decorationId); + } + } + + private _removeDecorationsImpl(decorationsTracker: DecorationsTracker, decorationIds: string[]): void { + let removeMarkers: LineMarker[] = [], removeMarkersLen = 0; + + for (let i = 0, len = decorationIds.length; i < len; i++) { + let decorationId = decorationIds[i]; + let decoration = this._decorations[decorationId]; + if (!decoration) { + continue; + } + + if (decorationsTracker) { + decorationsTracker.addRemovedDecoration(decorationId); + } + + removeMarkers[removeMarkersLen++] = decoration.startMarker; + removeMarkers[removeMarkersLen++] = decoration.endMarker; + delete this._multiLineDecorationsMap[decorationId]; + delete this._decorations[decorationId]; + delete this._internalDecorations[decoration.internalId]; + } + + if (removeMarkers.length > 0) { + this._removeMarkers(removeMarkers); + } + } + + private _resolveOldDecorations(oldDecorations: string[]): InternalDecoration[] { + let result: InternalDecoration[] = []; + for (let i = 0, len = oldDecorations.length; i < len; i++) { + let id = oldDecorations[i]; + let decoration = this._decorations[id]; + if (!decoration) { + continue; + } + + result.push(decoration); + } + return result; + } + + private _deltaDecorationsImpl(decorationsTracker: DecorationsTracker, ownerId: number, oldDecorationsIds: string[], newDecorations: ModelDeltaDecoration[]): string[] { + + if (oldDecorationsIds.length === 0) { + // Nothing to remove + return this._addDecorationsImpl(decorationsTracker, ownerId, newDecorations); + } + + if (newDecorations.length === 0) { + // Nothing to add + this._removeDecorationsImpl(decorationsTracker, oldDecorationsIds); + return []; + } + + let oldDecorations = this._resolveOldDecorations(oldDecorationsIds); + + oldDecorations.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + newDecorations.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + + let result: string[] = [], + oldDecorationsIndex = 0, + oldDecorationsLength = oldDecorations.length, + newDecorationsIndex = 0, + newDecorationsLength = newDecorations.length, + decorationsToAdd: ModelDeltaDecoration[] = [], + decorationsToRemove: string[] = []; + + while (oldDecorationsIndex < oldDecorationsLength && newDecorationsIndex < newDecorationsLength) { + let oldDecoration = oldDecorations[oldDecorationsIndex]; + let newDecoration = newDecorations[newDecorationsIndex]; + let comparison = Range.compareRangesUsingStarts(oldDecoration.range, newDecoration.range); + + if (comparison < 0) { + // `oldDecoration` is before `newDecoration` => remove `oldDecoration` + decorationsToRemove.push(oldDecoration.id); + oldDecorationsIndex++; + continue; + } + + if (comparison > 0) { + // `newDecoration` is before `oldDecoration` => add `newDecoration` + decorationsToAdd.push(newDecoration); + newDecorationsIndex++; + continue; + } + + // The ranges of `oldDecoration` and `newDecoration` are equal + + if (!oldDecoration.options.equals(newDecoration.options)) { + // The options do not match => remove `oldDecoration` + decorationsToRemove.push(oldDecoration.id); + oldDecorationsIndex++; + continue; + } + + // Bingo! We can reuse `oldDecoration` for `newDecoration` + result[newDecoration.index] = oldDecoration.id; + oldDecorationsIndex++; + newDecorationsIndex++; + } + + while (oldDecorationsIndex < oldDecorationsLength) { + // No more new decorations => remove decoration at `oldDecorationsIndex` + decorationsToRemove.push(oldDecorations[oldDecorationsIndex].id); + oldDecorationsIndex++; + } + + while (newDecorationsIndex < newDecorationsLength) { + // No more old decorations => add decoration at `newDecorationsIndex` + decorationsToAdd.push(newDecorations[newDecorationsIndex]); + newDecorationsIndex++; + } + + // Remove `decorationsToRemove` + if (decorationsToRemove.length > 0) { + this._removeDecorationsImpl(decorationsTracker, decorationsToRemove); + } + + // Add `decorationsToAdd` + if (decorationsToAdd.length > 0) { + let newIds = this._addDecorationsImpl(decorationsTracker, ownerId, decorationsToAdd); + for (let i = 0, len = decorationsToAdd.length; i < len; i++) { + result[decorationsToAdd[i].index] = newIds[i]; + } + } + + return result; + } +} + +function cleanClassName(className: string): string { + return className.replace(/[^a-z0-9\-]/gi, ' '); +} + +export class ModelDecorationOverviewRulerOptions implements editorCommon.IModelDecorationOverviewRulerOptions { + readonly color: string | ThemeColor; + readonly darkColor: string | ThemeColor; + readonly hcColor: string | ThemeColor; + readonly position: editorCommon.OverviewRulerLane; + + constructor(options: editorCommon.IModelDecorationOverviewRulerOptions) { + this.color = strings.empty; + this.darkColor = strings.empty; + this.hcColor = strings.empty; + this.position = editorCommon.OverviewRulerLane.Center; + + if (options && options.color) { + this.color = options.color; + } + if (options && options.darkColor) { + this.darkColor = options.darkColor; + this.hcColor = options.darkColor; + } + if (options && options.hcColor) { + this.hcColor = options.hcColor; + } + if (options && options.hasOwnProperty('position')) { + this.position = options.position; + } + } + + public equals(other: ModelDecorationOverviewRulerOptions): boolean { + return ( + this.color === other.color + && this.darkColor === other.darkColor + && this.hcColor === other.hcColor + && this.position === other.position + ); + } +} + +let lastStaticId = 0; + +export class ModelDecorationOptions implements editorCommon.IModelDecorationOptions { + + public static EMPTY: ModelDecorationOptions; + + public static register(options: editorCommon.IModelDecorationOptions): ModelDecorationOptions { + return new ModelDecorationOptions(++lastStaticId, options); + } + + public static createDynamic(options: editorCommon.IModelDecorationOptions): ModelDecorationOptions { + return new ModelDecorationOptions(0, options); + } + + readonly staticId: number; + readonly stickiness: editorCommon.TrackedRangeStickiness; + readonly className: string; + readonly hoverMessage: IMarkdownString | IMarkdownString[]; + readonly glyphMarginHoverMessage: IMarkdownString | IMarkdownString[]; + readonly isWholeLine: boolean; + readonly showIfCollapsed: boolean; + readonly overviewRuler: ModelDecorationOverviewRulerOptions; + readonly glyphMarginClassName: string; + readonly linesDecorationsClassName: string; + readonly marginClassName: string; + readonly inlineClassName: string; + readonly beforeContentClassName: string; + readonly afterContentClassName: string; + + private constructor(staticId: number, options: editorCommon.IModelDecorationOptions) { + this.staticId = staticId; + this.stickiness = options.stickiness || editorCommon.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges; + this.className = options.className ? cleanClassName(options.className) : strings.empty; + this.hoverMessage = options.hoverMessage || []; + this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || []; + this.isWholeLine = options.isWholeLine || false; + this.showIfCollapsed = options.showIfCollapsed || false; + this.overviewRuler = new ModelDecorationOverviewRulerOptions(options.overviewRuler); + this.glyphMarginClassName = options.glyphMarginClassName ? cleanClassName(options.glyphMarginClassName) : strings.empty; + this.linesDecorationsClassName = options.linesDecorationsClassName ? cleanClassName(options.linesDecorationsClassName) : strings.empty; + this.marginClassName = options.marginClassName ? cleanClassName(options.marginClassName) : strings.empty; + this.inlineClassName = options.inlineClassName ? cleanClassName(options.inlineClassName) : strings.empty; + this.beforeContentClassName = options.beforeContentClassName ? cleanClassName(options.beforeContentClassName) : strings.empty; + this.afterContentClassName = options.afterContentClassName ? cleanClassName(options.afterContentClassName) : strings.empty; + } + + public equals(other: ModelDecorationOptions): boolean { + if (this.staticId > 0 || other.staticId > 0) { + return this.staticId === other.staticId; + } + + return ( + this.stickiness === other.stickiness + && this.className === other.className + && this.isWholeLine === other.isWholeLine + && this.showIfCollapsed === other.showIfCollapsed + && this.glyphMarginClassName === other.glyphMarginClassName + && this.linesDecorationsClassName === other.linesDecorationsClassName + && this.marginClassName === other.marginClassName + && this.inlineClassName === other.inlineClassName + && this.beforeContentClassName === other.beforeContentClassName + && this.afterContentClassName === other.afterContentClassName + && markedStringsEquals(this.hoverMessage, other.hoverMessage) + && markedStringsEquals(this.glyphMarginHoverMessage, other.glyphMarginHoverMessage) + && this.overviewRuler.equals(other.overviewRuler) + ); + } +} +ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({}); + +class ModelDeltaDecoration implements editorCommon.IModelDeltaDecoration { + + index: number; + range: Range; + options: ModelDecorationOptions; + + constructor(index: number, range: Range, options: ModelDecorationOptions) { + this.index = index; + this.range = range; + this.options = options; + } +} + +function _normalizeOptions(options: editorCommon.IModelDecorationOptions): ModelDecorationOptions { + if (options instanceof ModelDecorationOptions) { + return options; + } + return ModelDecorationOptions.createDynamic(options); +} diff --git a/src/vs/editor/common/model/textModelWithMarkers.ts b/src/vs/editor/common/model/textModelWithMarkers.ts new file mode 100644 index 0000000000..f3cb62635d --- /dev/null +++ b/src/vs/editor/common/model/textModelWithMarkers.ts @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IdGenerator } from 'vs/base/common/idGenerator'; +import { Position } from 'vs/editor/common/core/position'; +import { ITextModelWithMarkers, ITextModelCreationOptions } from 'vs/editor/common/editorCommon'; +import { LineMarker } from 'vs/editor/common/model/modelLine'; +import { TextModelWithTokens } from 'vs/editor/common/model/textModelWithTokens'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { ITextSource, IRawTextSource } from 'vs/editor/common/model/textSource'; + +export interface IMarkerIdToMarkerMap { + [key: string]: LineMarker; +} + +export interface INewMarker { + internalDecorationId: number; + position: Position; + stickToPreviousCharacter: boolean; +} + +var _INSTANCE_COUNT = 0; + +export class TextModelWithMarkers extends TextModelWithTokens implements ITextModelWithMarkers { + + private _markerIdGenerator: IdGenerator; + protected _markerIdToMarker: IMarkerIdToMarkerMap; + + constructor(rawTextSource: IRawTextSource, creationOptions: ITextModelCreationOptions, languageIdentifier: LanguageIdentifier) { + super(rawTextSource, creationOptions, languageIdentifier); + this._markerIdGenerator = new IdGenerator((++_INSTANCE_COUNT) + ';'); + this._markerIdToMarker = Object.create(null); + } + + public dispose(): void { + this._markerIdToMarker = null; + super.dispose(); + } + + protected _resetValue(newValue: ITextSource): void { + super._resetValue(newValue); + + // Destroy all my markers + this._markerIdToMarker = Object.create(null); + } + + _addMarker(internalDecorationId: number, lineNumber: number, column: number, stickToPreviousCharacter: boolean): string { + var pos = this.validatePosition(new Position(lineNumber, column)); + + var marker = new LineMarker(this._markerIdGenerator.nextId(), internalDecorationId, pos, stickToPreviousCharacter); + this._markerIdToMarker[marker.id] = marker; + + this._lines[pos.lineNumber - 1].addMarker(marker); + + return marker.id; + } + + protected _addMarkers(newMarkers: INewMarker[]): LineMarker[] { + if (newMarkers.length === 0) { + return []; + } + + let markers: LineMarker[] = []; + for (let i = 0, len = newMarkers.length; i < len; i++) { + let newMarker = newMarkers[i]; + + let marker = new LineMarker(this._markerIdGenerator.nextId(), newMarker.internalDecorationId, newMarker.position, newMarker.stickToPreviousCharacter); + this._markerIdToMarker[marker.id] = marker; + + markers[i] = marker; + } + + let sortedMarkers = markers.slice(0); + sortedMarkers.sort((a, b) => { + return a.position.lineNumber - b.position.lineNumber; + }); + + let currentLineNumber = 0; + let currentMarkers: LineMarker[] = [], currentMarkersLen = 0; + for (let i = 0, len = sortedMarkers.length; i < len; i++) { + let marker = sortedMarkers[i]; + + if (marker.position.lineNumber !== currentLineNumber) { + if (currentLineNumber !== 0) { + this._lines[currentLineNumber - 1].addMarkers(currentMarkers); + } + currentLineNumber = marker.position.lineNumber; + currentMarkers.length = 0; + currentMarkersLen = 0; + } + + currentMarkers[currentMarkersLen++] = marker; + } + this._lines[currentLineNumber - 1].addMarkers(currentMarkers); + + return markers; + } + + _changeMarker(id: string, lineNumber: number, column: number): void { + let marker = this._markerIdToMarker[id]; + if (!marker) { + return; + } + + let newPos = this.validatePosition(new Position(lineNumber, column)); + + if (newPos.lineNumber !== marker.position.lineNumber) { + // Move marker between lines + this._lines[marker.position.lineNumber - 1].removeMarker(marker); + this._lines[newPos.lineNumber - 1].addMarker(marker); + } + + marker.setPosition(newPos); + } + + _changeMarkerStickiness(id: string, newStickToPreviousCharacter: boolean): void { + let marker = this._markerIdToMarker[id]; + if (!marker) { + return; + } + + marker.stickToPreviousCharacter = newStickToPreviousCharacter; + } + + _getMarker(id: string): Position { + let marker = this._markerIdToMarker[id]; + if (!marker) { + return null; + } + + return marker.position; + } + + _getMarkersCount(): number { + return Object.keys(this._markerIdToMarker).length; + } + + _removeMarker(id: string): void { + let marker = this._markerIdToMarker[id]; + if (!marker) { + return; + } + + this._lines[marker.position.lineNumber - 1].removeMarker(marker); + delete this._markerIdToMarker[id]; + } + + protected _removeMarkers(markers: LineMarker[]): void { + markers.sort((a, b) => { + return a.position.lineNumber - b.position.lineNumber; + }); + + let currentLineNumber = 0; + let currentMarkers: { [markerId: string]: boolean; } = null; + for (let i = 0, len = markers.length; i < len; i++) { + let marker = markers[i]; + delete this._markerIdToMarker[marker.id]; + + if (marker.position.lineNumber !== currentLineNumber) { + if (currentLineNumber !== 0) { + this._lines[currentLineNumber - 1].removeMarkers(currentMarkers); + } + currentLineNumber = marker.position.lineNumber; + currentMarkers = Object.create(null); + } + + currentMarkers[marker.id] = true; + } + this._lines[currentLineNumber - 1].removeMarkers(currentMarkers); + } +} diff --git a/src/vs/editor/common/model/textModelWithTokens.ts b/src/vs/editor/common/model/textModelWithTokens.ts new file mode 100644 index 0000000000..f96f6c4142 --- /dev/null +++ b/src/vs/editor/common/model/textModelWithTokens.ts @@ -0,0 +1,817 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { ITokenizationSupport, IState, TokenizationRegistry, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; +import { NULL_LANGUAGE_IDENTIFIER, nullTokenize2 } from 'vs/editor/common/modes/nullMode'; +import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; +import { BracketsUtils, RichEditBrackets, RichEditBracket } from 'vs/editor/common/modes/supports/richEditBrackets'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { LineTokens, LineToken } from 'vs/editor/common/core/lineTokens'; +import { getWordAtText } from 'vs/editor/common/model/wordHelper'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { ITextSource, IRawTextSource } from 'vs/editor/common/model/textSource'; +import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; + +class ModelTokensChangedEventBuilder { + + private _ranges: { fromLineNumber: number; toLineNumber: number; }[]; + + constructor() { + this._ranges = []; + } + + public registerChangedTokens(lineNumber: number): void { + const ranges = this._ranges; + const rangesLength = ranges.length; + const previousRange = rangesLength > 0 ? ranges[rangesLength - 1] : null; + + if (previousRange && previousRange.toLineNumber === lineNumber - 1) { + // extend previous range + previousRange.toLineNumber++; + } else { + // insert new range + ranges[rangesLength] = { + fromLineNumber: lineNumber, + toLineNumber: lineNumber + }; + } + } + + public build(): textModelEvents.IModelTokensChangedEvent { + if (this._ranges.length === 0) { + return null; + } + return { + ranges: this._ranges + }; + } +} + +export class TextModelWithTokens extends TextModel implements editorCommon.ITokenizedModel { + + private static MODE_TOKENIZATION_FAILED_MSG = nls.localize('mode.tokenizationSupportFailed', "The mode has failed while tokenizing the input."); + + private _languageIdentifier: LanguageIdentifier; + private _tokenizationListener: IDisposable; + private _tokenizationSupport: ITokenizationSupport; + + private _invalidLineStartIndex: number; + private _lastState: IState; + + private _revalidateTokensTimeout: number; + + constructor(rawTextSource: IRawTextSource, creationOptions: editorCommon.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier) { + super(rawTextSource, creationOptions); + + this._languageIdentifier = languageIdentifier || NULL_LANGUAGE_IDENTIFIER; + this._tokenizationListener = TokenizationRegistry.onDidChange((e) => { + if (e.changedLanguages.indexOf(this._languageIdentifier.language) === -1) { + return; + } + + this._resetTokenizationState(); + this.emitModelTokensChangedEvent({ + ranges: [{ + fromLineNumber: 1, + toLineNumber: this.getLineCount() + }] + }); + + if (this._shouldAutoTokenize()) { + this._warmUpTokens(); + } + }); + + this._revalidateTokensTimeout = -1; + + this._resetTokenizationState(); + } + + public dispose(): void { + this._tokenizationListener.dispose(); + this._clearTimers(); + this._lastState = null; + + super.dispose(); + } + + protected _shouldAutoTokenize(): boolean { + return false; + } + + protected _resetValue(newValue: ITextSource): void { + super._resetValue(newValue); + // Cancel tokenization, clear all tokens and begin tokenizing + this._resetTokenizationState(); + } + + protected _resetTokenizationState(): void { + this._clearTimers(); + for (let i = 0; i < this._lines.length; i++) { + this._lines[i].resetTokenizationState(); + } + + this._tokenizationSupport = null; + if (!this._isTooLargeForTokenization) { + this._tokenizationSupport = TokenizationRegistry.get(this._languageIdentifier.language); + } + + if (this._tokenizationSupport) { + let initialState: IState = null; + try { + initialState = this._tokenizationSupport.getInitialState(); + } catch (e) { + e.friendlyMessage = TextModelWithTokens.MODE_TOKENIZATION_FAILED_MSG; + onUnexpectedError(e); + this._tokenizationSupport = null; + } + + if (initialState) { + this._lines[0].setState(initialState); + } + } + + this._lastState = null; + this._invalidLineStartIndex = 0; + this._beginBackgroundTokenization(); + } + + private _clearTimers(): void { + if (this._revalidateTokensTimeout !== -1) { + clearTimeout(this._revalidateTokensTimeout); + this._revalidateTokensTimeout = -1; + } + } + + private _withModelTokensChangedEventBuilder(callback: (eventBuilder: ModelTokensChangedEventBuilder) => T): T { + let eventBuilder = new ModelTokensChangedEventBuilder(); + + let result = callback(eventBuilder); + + if (!this._isDisposing) { + let e = eventBuilder.build(); + if (e) { + this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelTokensChanged, e); + } + } + + return result; + } + + public forceTokenization(lineNumber: number): void { + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); + } + + this._withModelTokensChangedEventBuilder((eventBuilder) => { + this._updateTokensUntilLine(eventBuilder, lineNumber); + }); + } + + public isCheapToTokenize(lineNumber: number): boolean { + const firstInvalidLineNumber = this._invalidLineStartIndex + 1; + return (firstInvalidLineNumber >= lineNumber); + } + + public tokenizeIfCheap(lineNumber: number): void { + if (this.isCheapToTokenize(lineNumber)) { + this.forceTokenization(lineNumber); + } + } + + public getLineTokens(lineNumber: number): LineTokens { + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); + } + + return this._getLineTokens(lineNumber); + } + + private _getLineTokens(lineNumber: number): LineTokens { + return this._lines[lineNumber - 1].getTokens(this._languageIdentifier.id); + } + + public getLanguageIdentifier(): LanguageIdentifier { + return this._languageIdentifier; + } + + public getModeId(): string { + return this._languageIdentifier.language; + } + + public setMode(languageIdentifier: LanguageIdentifier): void { + if (this._languageIdentifier.id === languageIdentifier.id) { + // There's nothing to do + return; + } + + let e: textModelEvents.IModelLanguageChangedEvent = { + oldLanguage: this._languageIdentifier.language, + newLanguage: languageIdentifier.language + }; + + this._languageIdentifier = languageIdentifier; + + // Cancel tokenization, clear all tokens and begin tokenizing + this._resetTokenizationState(); + + this.emitModelTokensChangedEvent({ + ranges: [{ + fromLineNumber: 1, + toLineNumber: this.getLineCount() + }] + }); + this._emitModelModeChangedEvent(e); + } + + public getLanguageIdAtPosition(_lineNumber: number, _column: number): LanguageId { + if (!this._tokenizationSupport) { + return this._languageIdentifier.id; + } + let { lineNumber, column } = this.validatePosition({ lineNumber: _lineNumber, column: _column }); + + let lineTokens = this._getLineTokens(lineNumber); + let token = lineTokens.findTokenAtOffset(column - 1); + return token.languageId; + } + + protected _invalidateLine(lineIndex: number): void { + this._lines[lineIndex].setIsInvalid(true); + if (lineIndex < this._invalidLineStartIndex) { + if (this._invalidLineStartIndex < this._lines.length) { + this._lines[this._invalidLineStartIndex].setIsInvalid(true); + } + this._invalidLineStartIndex = lineIndex; + this._beginBackgroundTokenization(); + } + } + + private _beginBackgroundTokenization(): void { + if (this._shouldAutoTokenize() && this._revalidateTokensTimeout === -1) { + this._revalidateTokensTimeout = setTimeout(() => { + this._revalidateTokensTimeout = -1; + this._revalidateTokensNow(); + }, 0); + } + } + + _warmUpTokens(): void { + // Warm up first 100 lines (if it takes less than 50ms) + var maxLineNumber = Math.min(100, this.getLineCount()); + this._revalidateTokensNow(maxLineNumber); + + if (this._invalidLineStartIndex < this._lines.length) { + this._beginBackgroundTokenization(); + } + } + + private _revalidateTokensNow(toLineNumber: number = this._invalidLineStartIndex + 1000000): void { + + this._withModelTokensChangedEventBuilder((eventBuilder) => { + + toLineNumber = Math.min(this._lines.length, toLineNumber); + + var MAX_ALLOWED_TIME = 20, + fromLineNumber = this._invalidLineStartIndex + 1, + tokenizedChars = 0, + currentCharsToTokenize = 0, + currentEstimatedTimeToTokenize = 0, + sw = StopWatch.create(false), + elapsedTime: number; + + // Tokenize at most 1000 lines. Estimate the tokenization speed per character and stop when: + // - MAX_ALLOWED_TIME is reached + // - tokenizing the next line would go above MAX_ALLOWED_TIME + + for (var lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) { + elapsedTime = sw.elapsed(); + if (elapsedTime > MAX_ALLOWED_TIME) { + // Stop if MAX_ALLOWED_TIME is reached + toLineNumber = lineNumber - 1; + break; + } + + // Compute how many characters will be tokenized for this line + currentCharsToTokenize = this._lines[lineNumber - 1].text.length; + + if (tokenizedChars > 0) { + // If we have enough history, estimate how long tokenizing this line would take + currentEstimatedTimeToTokenize = (elapsedTime / tokenizedChars) * currentCharsToTokenize; + if (elapsedTime + currentEstimatedTimeToTokenize > MAX_ALLOWED_TIME) { + // Tokenizing this line will go above MAX_ALLOWED_TIME + toLineNumber = lineNumber - 1; + break; + } + } + + this._updateTokensUntilLine(eventBuilder, lineNumber); + tokenizedChars += currentCharsToTokenize; + + // Skip the lines that got tokenized + lineNumber = Math.max(lineNumber, this._invalidLineStartIndex + 1); + } + + elapsedTime = sw.elapsed(); + + if (this._invalidLineStartIndex < this._lines.length) { + this._beginBackgroundTokenization(); + } + }); + } + + private _updateTokensUntilLine(eventBuilder: ModelTokensChangedEventBuilder, lineNumber: number): void { + if (!this._tokenizationSupport) { + this._invalidLineStartIndex = this._lines.length; + return; + } + + const linesLength = this._lines.length; + const endLineIndex = lineNumber - 1; + + // Validate all states up to and including endLineIndex + for (let lineIndex = this._invalidLineStartIndex; lineIndex <= endLineIndex; lineIndex++) { + const endStateIndex = lineIndex + 1; + let r: TokenizationResult2 = null; + const text = this._lines[lineIndex].text; + + try { + // Tokenize only the first X characters + let freshState = this._lines[lineIndex].getState().clone(); + r = this._tokenizationSupport.tokenize2(this._lines[lineIndex].text, freshState, 0); + } catch (e) { + e.friendlyMessage = TextModelWithTokens.MODE_TOKENIZATION_FAILED_MSG; + onUnexpectedError(e); + } + + if (!r) { + r = nullTokenize2(this._languageIdentifier.id, text, this._lines[lineIndex].getState(), 0); + } + this._lines[lineIndex].setTokens(this._languageIdentifier.id, r.tokens); + eventBuilder.registerChangedTokens(lineIndex + 1); + this._lines[lineIndex].setIsInvalid(false); + + if (endStateIndex < linesLength) { + if (this._lines[endStateIndex].getState() !== null && r.endState.equals(this._lines[endStateIndex].getState())) { + // The end state of this line remains the same + let nextInvalidLineIndex = lineIndex + 1; + while (nextInvalidLineIndex < linesLength) { + if (this._lines[nextInvalidLineIndex].isInvalid()) { + break; + } + if (nextInvalidLineIndex + 1 < linesLength) { + if (this._lines[nextInvalidLineIndex + 1].getState() === null) { + break; + } + } else { + if (this._lastState === null) { + break; + } + } + nextInvalidLineIndex++; + } + this._invalidLineStartIndex = Math.max(this._invalidLineStartIndex, nextInvalidLineIndex); + lineIndex = nextInvalidLineIndex - 1; // -1 because the outer loop increments it + } else { + this._lines[endStateIndex].setState(r.endState); + } + } else { + this._lastState = r.endState; + } + } + this._invalidLineStartIndex = Math.max(this._invalidLineStartIndex, endLineIndex + 1); + } + + private emitModelTokensChangedEvent(e: textModelEvents.IModelTokensChangedEvent): void { + if (!this._isDisposing) { + this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelTokensChanged, e); + } + } + + private _emitModelModeChangedEvent(e: textModelEvents.IModelLanguageChangedEvent): void { + if (!this._isDisposing) { + this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelLanguageChanged, e); + } + } + + // Having tokens allows implementing additional helper methods + + public getWordAtPosition(_position: IPosition): editorCommon.IWordAtPosition { + this._assertNotDisposed(); + let position = this.validatePosition(_position); + let lineContent = this.getLineContent(position.lineNumber); + + if (this._invalidLineStartIndex <= position.lineNumber) { + // this line is not tokenized + return getWordAtText( + position.column, + LanguageConfigurationRegistry.getWordDefinition(this._languageIdentifier.id), + lineContent, + 0 + ); + } + + let lineTokens = this._getLineTokens(position.lineNumber); + let offset = position.column - 1; + let token = lineTokens.findTokenAtOffset(offset); + + let result = getWordAtText( + position.column, + LanguageConfigurationRegistry.getWordDefinition(token.languageId), + lineContent.substring(token.startOffset, token.endOffset), + token.startOffset + ); + + if (!result && token.hasPrev && token.startOffset === offset) { + // The position is right at the beginning of `modeIndex`, so try looking at `modeIndex` - 1 too + + let prevToken = token.prev(); + result = getWordAtText( + position.column, + LanguageConfigurationRegistry.getWordDefinition(prevToken.languageId), + lineContent.substring(prevToken.startOffset, prevToken.endOffset), + prevToken.startOffset + ); + } + + return result; + } + + public getWordUntilPosition(position: IPosition): editorCommon.IWordAtPosition { + var wordAtPosition = this.getWordAtPosition(position); + if (!wordAtPosition) { + return { + word: '', + startColumn: position.column, + endColumn: position.column + }; + } + return { + word: wordAtPosition.word.substr(0, position.column - wordAtPosition.startColumn), + startColumn: wordAtPosition.startColumn, + endColumn: position.column + }; + } + + public findMatchingBracketUp(_bracket: string, _position: IPosition): Range { + let bracket = _bracket.toLowerCase(); + let position = this.validatePosition(_position); + + let lineTokens = this._getLineTokens(position.lineNumber); + let token = lineTokens.findTokenAtOffset(position.column - 1); + let bracketsSupport = LanguageConfigurationRegistry.getBracketsSupport(token.languageId); + + if (!bracketsSupport) { + return null; + } + + let data = bracketsSupport.textIsBracket[bracket]; + + if (!data) { + return null; + } + + return this._findMatchingBracketUp(data, position); + } + + public matchBracket(position: IPosition): [Range, Range] { + return this._matchBracket(this.validatePosition(position)); + } + + private _matchBracket(position: Position): [Range, Range] { + const lineNumber = position.lineNumber; + let lineTokens = this._getLineTokens(lineNumber); + const lineText = this._lines[lineNumber - 1].text; + + const currentToken = lineTokens.findTokenAtOffset(position.column - 1); + if (!currentToken) { + return null; + } + const currentModeBrackets = LanguageConfigurationRegistry.getBracketsSupport(currentToken.languageId); + + // check that the token is not to be ignored + if (currentModeBrackets && !ignoreBracketsInToken(currentToken.tokenType)) { + // limit search to not go before `maxBracketLength` + let searchStartOffset = Math.max(currentToken.startOffset, position.column - 1 - currentModeBrackets.maxBracketLength); + // limit search to not go after `maxBracketLength` + const searchEndOffset = Math.min(currentToken.endOffset, position.column - 1 + currentModeBrackets.maxBracketLength); + + // first, check if there is a bracket to the right of `position` + let foundBracket = BracketsUtils.findNextBracketInToken(currentModeBrackets.forwardRegex, lineNumber, lineText, position.column - 1, searchEndOffset); + if (foundBracket && foundBracket.startColumn === position.column) { + let foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1); + foundBracketText = foundBracketText.toLowerCase(); + + let r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText]); + + // check that we can actually match this bracket + if (r) { + return r; + } + } + + // it might still be the case that [currentTokenStart -> currentTokenEnd] contains multiple brackets + while (true) { + let foundBracket = BracketsUtils.findNextBracketInToken(currentModeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset); + if (!foundBracket) { + // there are no brackets in this text + break; + } + + // check that we didn't hit a bracket too far away from position + if (foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) { + let foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1); + foundBracketText = foundBracketText.toLowerCase(); + + let r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText]); + + // check that we can actually match this bracket + if (r) { + return r; + } + } + + searchStartOffset = foundBracket.endColumn - 1; + } + } + + // If position is in between two tokens, try also looking in the previous token + if (currentToken.hasPrev && currentToken.startOffset === position.column - 1) { + const prevToken = currentToken.prev(); + const prevModeBrackets = LanguageConfigurationRegistry.getBracketsSupport(prevToken.languageId); + + // check that previous token is not to be ignored + if (prevModeBrackets && !ignoreBracketsInToken(prevToken.tokenType)) { + // limit search in case previous token is very large, there's no need to go beyond `maxBracketLength` + const searchStartOffset = Math.max(prevToken.startOffset, position.column - 1 - prevModeBrackets.maxBracketLength); + const searchEndOffset = currentToken.startOffset; + const foundBracket = BracketsUtils.findPrevBracketInToken(prevModeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset); + + // check that we didn't hit a bracket too far away from position + if (foundBracket && foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) { + let foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1); + foundBracketText = foundBracketText.toLowerCase(); + + let r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText]); + + // check that we can actually match this bracket + if (r) { + return r; + } + } + } + } + + return null; + } + + private _matchFoundBracket(foundBracket: Range, data: RichEditBracket, isOpen: boolean): [Range, Range] { + if (isOpen) { + let matched = this._findMatchingBracketDown(data, foundBracket.getEndPosition()); + if (matched) { + return [foundBracket, matched]; + } + } else { + let matched = this._findMatchingBracketUp(data, foundBracket.getStartPosition()); + if (matched) { + return [foundBracket, matched]; + } + } + + return null; + } + + private _findMatchingBracketUp(bracket: RichEditBracket, position: Position): Range { + // console.log('_findMatchingBracketUp: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position)); + + const languageId = bracket.languageIdentifier.id; + const reversedBracketRegex = bracket.reversedRegex; + let count = -1; + + for (let lineNumber = position.lineNumber; lineNumber >= 1; lineNumber--) { + const lineTokens = this._getLineTokens(lineNumber); + const lineText = this._lines[lineNumber - 1].text; + + let currentToken: LineToken; + let searchStopOffset: number; + if (lineNumber === position.lineNumber) { + currentToken = lineTokens.findTokenAtOffset(position.column - 1); + searchStopOffset = position.column - 1; + } else { + currentToken = lineTokens.lastToken(); + if (currentToken) { + searchStopOffset = currentToken.endOffset; + } + } + + while (currentToken) { + if (currentToken.languageId === languageId && !ignoreBracketsInToken(currentToken.tokenType)) { + + while (true) { + let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, currentToken.startOffset, searchStopOffset); + if (!r) { + break; + } + + let hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1); + hitText = hitText.toLowerCase(); + + if (hitText === bracket.open) { + count++; + } else if (hitText === bracket.close) { + count--; + } + + if (count === 0) { + return r; + } + + searchStopOffset = r.startColumn - 1; + } + } + + currentToken = currentToken.prev(); + if (currentToken) { + searchStopOffset = currentToken.endOffset; + } + } + } + + return null; + } + + private _findMatchingBracketDown(bracket: RichEditBracket, position: Position): Range { + // console.log('_findMatchingBracketDown: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position)); + + const languageId = bracket.languageIdentifier.id; + const bracketRegex = bracket.forwardRegex; + let count = 1; + + for (let lineNumber = position.lineNumber, lineCount = this.getLineCount(); lineNumber <= lineCount; lineNumber++) { + const lineTokens = this._getLineTokens(lineNumber); + const lineText = this._lines[lineNumber - 1].text; + + let currentToken: LineToken; + let searchStartOffset: number; + if (lineNumber === position.lineNumber) { + currentToken = lineTokens.findTokenAtOffset(position.column - 1); + searchStartOffset = position.column - 1; + } else { + currentToken = lineTokens.firstToken(); + if (currentToken) { + searchStartOffset = currentToken.startOffset; + } + } + + while (currentToken) { + if (currentToken.languageId === languageId && !ignoreBracketsInToken(currentToken.tokenType)) { + while (true) { + let r = BracketsUtils.findNextBracketInToken(bracketRegex, lineNumber, lineText, searchStartOffset, currentToken.endOffset); + if (!r) { + break; + } + + let hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1); + hitText = hitText.toLowerCase(); + + if (hitText === bracket.open) { + count++; + } else if (hitText === bracket.close) { + count--; + } + + if (count === 0) { + return r; + } + + searchStartOffset = r.endColumn - 1; + } + } + + currentToken = currentToken.next(); + if (currentToken) { + searchStartOffset = currentToken.startOffset; + } + } + } + + return null; + } + + public findPrevBracket(_position: IPosition): editorCommon.IFoundBracket { + const position = this.validatePosition(_position); + + let languageId: LanguageId = -1; + let modeBrackets: RichEditBrackets = null; + for (let lineNumber = position.lineNumber; lineNumber >= 1; lineNumber--) { + const lineTokens = this._getLineTokens(lineNumber); + const lineText = this._lines[lineNumber - 1].text; + + let currentToken: LineToken; + let searchStopOffset: number; + if (lineNumber === position.lineNumber) { + currentToken = lineTokens.findTokenAtOffset(position.column - 1); + searchStopOffset = position.column - 1; + } else { + currentToken = lineTokens.lastToken(); + if (currentToken) { + searchStopOffset = currentToken.endOffset; + } + } + + while (currentToken) { + if (languageId !== currentToken.languageId) { + languageId = currentToken.languageId; + modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(languageId); + } + if (modeBrackets && !ignoreBracketsInToken(currentToken.tokenType)) { + let r = BracketsUtils.findPrevBracketInToken(modeBrackets.reversedRegex, lineNumber, lineText, currentToken.startOffset, searchStopOffset); + if (r) { + return this._toFoundBracket(modeBrackets, r); + } + } + + currentToken = currentToken.prev(); + if (currentToken) { + searchStopOffset = currentToken.endOffset; + } + } + } + + return null; + } + + public findNextBracket(_position: IPosition): editorCommon.IFoundBracket { + const position = this.validatePosition(_position); + + let languageId: LanguageId = -1; + let modeBrackets: RichEditBrackets = null; + for (let lineNumber = position.lineNumber, lineCount = this.getLineCount(); lineNumber <= lineCount; lineNumber++) { + const lineTokens = this._getLineTokens(lineNumber); + const lineText = this._lines[lineNumber - 1].text; + + let currentToken: LineToken; + let searchStartOffset: number; + if (lineNumber === position.lineNumber) { + currentToken = lineTokens.findTokenAtOffset(position.column - 1); + searchStartOffset = position.column - 1; + } else { + currentToken = lineTokens.firstToken(); + if (currentToken) { + searchStartOffset = currentToken.startOffset; + } + } + + while (currentToken) { + if (languageId !== currentToken.languageId) { + languageId = currentToken.languageId; + modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(languageId); + } + if (modeBrackets && !ignoreBracketsInToken(currentToken.tokenType)) { + let r = BracketsUtils.findNextBracketInToken(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, currentToken.endOffset); + if (r) { + return this._toFoundBracket(modeBrackets, r); + } + } + + currentToken = currentToken.next(); + if (currentToken) { + searchStartOffset = currentToken.startOffset; + } + } + } + + return null; + } + + private _toFoundBracket(modeBrackets: RichEditBrackets, r: Range): editorCommon.IFoundBracket { + if (!r) { + return null; + } + + let text = this.getValueInRange(r); + text = text.toLowerCase(); + + let data = modeBrackets.textIsBracket[text]; + if (!data) { + return null; + } + + return { + range: r, + open: data.open, + close: data.close, + isOpen: modeBrackets.textIsOpenBracket[text] + }; + } +} diff --git a/src/vs/editor/common/model/textSource.ts b/src/vs/editor/common/model/textSource.ts new file mode 100644 index 0000000000..3e5878710e --- /dev/null +++ b/src/vs/editor/common/model/textSource.ts @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { DefaultEndOfLine } from 'vs/editor/common/editorCommon'; + +/** + * A processed string ready to be turned into an editor model. + */ +export interface IRawTextSource { + /** + * The entire text length. + */ + readonly length: number; + /** + * The text split into lines. + */ + readonly lines: string[]; + /** + * The BOM (leading character sequence of the file). + */ + readonly BOM: string; + /** + * The number of lines ending with '\r\n' + */ + readonly totalCRCount: number; + /** + * The text contains Unicode characters classified as "R" or "AL". + */ + readonly containsRTL: boolean; + /** + * The text contains only characters inside the ASCII range 32-126 or \t \r \n + */ + readonly isBasicASCII: boolean; +} + +export class RawTextSource { + + public static fromString(rawText: string): IRawTextSource { + // Count the number of lines that end with \r\n + let carriageReturnCnt = 0; + let lastCarriageReturnIndex = -1; + while ((lastCarriageReturnIndex = rawText.indexOf('\r', lastCarriageReturnIndex + 1)) !== -1) { + carriageReturnCnt++; + } + + const containsRTL = strings.containsRTL(rawText); + const isBasicASCII = (containsRTL ? false : strings.isBasicASCII(rawText)); + + // Split the text into lines + const lines = rawText.split(/\r\n|\r|\n/); + + // Remove the BOM (if present) + let BOM = ''; + if (strings.startsWithUTF8BOM(lines[0])) { + BOM = strings.UTF8_BOM_CHARACTER; + lines[0] = lines[0].substr(1); + } + + return { + BOM: BOM, + lines: lines, + length: rawText.length, + containsRTL: containsRTL, + isBasicASCII: isBasicASCII, + totalCRCount: carriageReturnCnt + }; + } + +} + +/** + * A processed string with its EOL resolved ready to be turned into an editor model. + */ +export interface ITextSource { + /** + * The entire text length. + */ + readonly length: number; + /** + * The text split into lines. + */ + readonly lines: string[]; + /** + * The BOM (leading character sequence of the file). + */ + readonly BOM: string; + /** + * The end of line sequence. + */ + readonly EOL: string; + /** + * The text contains Unicode characters classified as "R" or "AL". + */ + readonly containsRTL: boolean; + /** + * The text contains only characters inside the ASCII range 32-126 or \t \r \n + */ + readonly isBasicASCII: boolean; +} + +export class TextSource { + + /** + * if text source is empty or with precisely one line, returns null. No end of line is detected. + * if text source contains more lines ending with '\r\n', returns '\r\n'. + * Otherwise returns '\n'. More lines end with '\n'. + */ + private static _getEOL(rawTextSource: IRawTextSource, defaultEOL: DefaultEndOfLine): '\r\n' | '\n' { + const lineFeedCnt = rawTextSource.lines.length - 1; + if (lineFeedCnt === 0) { + // This is an empty file or a file with precisely one line + return (defaultEOL === DefaultEndOfLine.LF ? '\n' : '\r\n'); + } + if (rawTextSource.totalCRCount > lineFeedCnt / 2) { + // More than half of the file contains \r\n ending lines + return '\r\n'; + } + // At least one line more ends in \n + return '\n'; + } + + public static fromRawTextSource(rawTextSource: IRawTextSource, defaultEOL: DefaultEndOfLine): ITextSource { + return { + length: rawTextSource.length, + lines: rawTextSource.lines, + BOM: rawTextSource.BOM, + EOL: this._getEOL(rawTextSource, defaultEOL), + containsRTL: rawTextSource.containsRTL, + isBasicASCII: rawTextSource.isBasicASCII, + }; + } + + public static fromString(text: string, defaultEOL: DefaultEndOfLine): ITextSource { + return this.fromRawTextSource(RawTextSource.fromString(text), defaultEOL); + } + + public static create(source: string | IRawTextSource, defaultEOL: DefaultEndOfLine): ITextSource { + if (typeof source === 'string') { + return this.fromString(source, defaultEOL); + } + + return this.fromRawTextSource(source, defaultEOL); + } + +} diff --git a/src/vs/editor/common/model/tokensBinaryEncoding.ts b/src/vs/editor/common/model/tokensBinaryEncoding.ts new file mode 100644 index 0000000000..a5bd587c3d --- /dev/null +++ b/src/vs/editor/common/model/tokensBinaryEncoding.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ColorId, FontStyle, StandardTokenType, MetadataConsts, LanguageId } from 'vs/editor/common/modes'; + +export class TokenMetadata { + + public static getLanguageId(metadata: number): LanguageId { + return (metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET; + } + + public static getTokenType(metadata: number): StandardTokenType { + return (metadata & MetadataConsts.TOKEN_TYPE_MASK) >>> MetadataConsts.TOKEN_TYPE_OFFSET; + } + + public static getFontStyle(metadata: number): FontStyle { + return (metadata & MetadataConsts.FONT_STYLE_MASK) >>> MetadataConsts.FONT_STYLE_OFFSET; + } + + public static getForeground(metadata: number): ColorId { + return (metadata & MetadataConsts.FOREGROUND_MASK) >>> MetadataConsts.FOREGROUND_OFFSET; + } + + public static getBackground(metadata: number): ColorId { + return (metadata & MetadataConsts.BACKGROUND_MASK) >>> MetadataConsts.BACKGROUND_OFFSET; + } + + public static getClassNameFromMetadata(metadata: number): string { + let foreground = this.getForeground(metadata); + let className = 'mtk' + foreground; + + let fontStyle = this.getFontStyle(metadata); + if (fontStyle & FontStyle.Italic) { + className += ' mtki'; + } + if (fontStyle & FontStyle.Bold) { + className += ' mtkb'; + } + if (fontStyle & FontStyle.Underline) { + className += ' mtku'; + } + + return className; + } + + public static getInlineStyleFromMetadata(metadata: number, colorMap: string[]): string { + const foreground = this.getForeground(metadata); + const fontStyle = this.getFontStyle(metadata); + + let result = `color: ${colorMap[foreground]};`; + if (fontStyle & FontStyle.Italic) { + result += 'font-style: italic;'; + } + if (fontStyle & FontStyle.Bold) { + result += 'font-weight: bold;'; + } + if (fontStyle & FontStyle.Underline) { + result += 'text-decoration: underline;'; + } + return result; + } +} diff --git a/src/vs/editor/common/model/wordHelper.ts b/src/vs/editor/common/model/wordHelper.ts new file mode 100644 index 0000000000..d514613614 --- /dev/null +++ b/src/vs/editor/common/model/wordHelper.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IWordAtPosition } from 'vs/editor/common/editorCommon'; + +export const USUAL_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?'; + +/** + * Create a word definition regular expression based on default word separators. + * Optionally provide allowed separators that should be included in words. + * + * The default would look like this: + * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + */ +function createWordRegExp(allowInWords: string = ''): RegExp { + var usualSeparators = USUAL_WORD_SEPARATORS; + var source = '(-?\\d*\\.\\d\\w*)|([^'; + for (var i = 0; i < usualSeparators.length; i++) { + if (allowInWords.indexOf(usualSeparators[i]) >= 0) { + continue; + } + source += '\\' + usualSeparators[i]; + } + source += '\\s]+)'; + return new RegExp(source, 'g'); +} + +// catches numbers (including floating numbers) in the first group, and alphanum in the second +export const DEFAULT_WORD_REGEXP = createWordRegExp(); + +export function ensureValidWordDefinition(wordDefinition?: RegExp): RegExp { + var result: RegExp = DEFAULT_WORD_REGEXP; + + if (wordDefinition && (wordDefinition instanceof RegExp)) { + if (!wordDefinition.global) { + var flags = 'g'; + if (wordDefinition.ignoreCase) { + flags += 'i'; + } + if (wordDefinition.multiline) { + flags += 'm'; + } + result = new RegExp(wordDefinition.source, flags); + } else { + result = wordDefinition; + } + } + + result.lastIndex = 0; + + return result; +} + +function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition { + // find whitespace enclosed text around column and match from there + + let pos = column - 1 - textOffset; + let start = text.lastIndexOf(' ', pos - 1) + 1; + let end = text.indexOf(' ', pos); + if (end === -1) { + end = text.length; + } + + wordDefinition.lastIndex = start; + let match: RegExpMatchArray; + while (match = wordDefinition.exec(text)) { + if (match.index <= pos && wordDefinition.lastIndex >= pos) { + return { + word: match[0], + startColumn: textOffset + 1 + match.index, + endColumn: textOffset + 1 + wordDefinition.lastIndex + }; + } + } + + return null; +} + + +function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition { + // matches all words starting at the beginning + // of the input until it finds a match that encloses + // the desired column. slow but correct + + let pos = column - 1 - textOffset; + wordDefinition.lastIndex = 0; + + let match: RegExpMatchArray; + while (match = wordDefinition.exec(text)) { + + if (match.index > pos) { + // |nW -> matched only after the pos + return null; + + } else if (wordDefinition.lastIndex >= pos) { + // W|W -> match encloses pos + return { + word: match[0], + startColumn: textOffset + 1 + match.index, + endColumn: textOffset + 1 + wordDefinition.lastIndex + }; + } + } + + return null; +} + +export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition { + + // if `words` can contain whitespace character we have to use the slow variant + // otherwise we use the fast variant of finding a word + wordDefinition.lastIndex = 0; + let match = wordDefinition.exec(text); + if (!match) { + return null; + } + // todo@joh the `match` could already be the (first) word + const ret = match[0].indexOf(' ') >= 0 + // did match a word which contains a space character -> use slow word find + ? getWordAtPosSlow(column, wordDefinition, text, textOffset) + // sane word definition -> use fast word find + : getWordAtPosFast(column, wordDefinition, text, textOffset); + + // both (getWordAtPosFast and getWordAtPosSlow) leave the wordDefinition-RegExp + // in an undefined state and to not confuse other users of the wordDefinition + // we reset the lastIndex + wordDefinition.lastIndex = 0; + + return ret; +} diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts new file mode 100644 index 0000000000..33d167d0ea --- /dev/null +++ b/src/vs/editor/common/modes.ts @@ -0,0 +1,888 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import URI from 'vs/base/common/uri'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; +import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Position } from 'vs/editor/common/core/position'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import Event from 'vs/base/common/event'; +import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry'; +import { Color } from 'vs/base/common/color'; + +/** + * Open ended enum at runtime + * @internal + */ +export const enum LanguageId { + Null = 0, + PlainText = 1 +} + +/** + * @internal + */ +export class LanguageIdentifier { + + /** + * A string identifier. Unique across languages. e.g. 'javascript'. + */ + public readonly language: string; + + /** + * A numeric identifier. Unique across languages. e.g. 5 + * Will vary at runtime based on registration order, etc. + */ + public readonly id: LanguageId; + + constructor(language: string, id: LanguageId) { + this.language = language; + this.id = id; + } +} + +/** + * A mode. Will soon be obsolete. + * @internal + */ +export interface IMode { + + getId(): string; + + getLanguageIdentifier(): LanguageIdentifier; + +} + +/** + * A font style. Values are 2^x such that a bit mask can be used. + * @internal + */ +export const enum FontStyle { + NotSet = -1, + None = 0, + Italic = 1, + Bold = 2, + Underline = 4 +} + +/** + * Open ended enum at runtime + * @internal + */ +export const enum ColorId { + None = 0, + DefaultForeground = 1, + DefaultBackground = 2 +} + +/** + * A standard token type. Values are 2^x such that a bit mask can be used. + * @internal + */ +export const enum StandardTokenType { + Other = 0, + Comment = 1, + String = 2, + RegEx = 4 +} + +/** + * Helpers to manage the "collapsed" metadata of an entire StackElement stack. + * The following assumptions have been made: + * - languageId < 256 => needs 8 bits + * - unique color count < 512 => needs 9 bits + * + * The binary format is: + * - ------------------------------------------- + * 3322 2222 2222 1111 1111 1100 0000 0000 + * 1098 7654 3210 9876 5432 1098 7654 3210 + * - ------------------------------------------- + * xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx + * bbbb bbbb bfff ffff ffFF FTTT LLLL LLLL + * - ------------------------------------------- + * - L = LanguageId (8 bits) + * - T = StandardTokenType (3 bits) + * - F = FontStyle (3 bits) + * - f = foreground color (9 bits) + * - b = background color (9 bits) + * + * @internal + */ +export const enum MetadataConsts { + LANGUAGEID_MASK = 0b00000000000000000000000011111111, + TOKEN_TYPE_MASK = 0b00000000000000000000011100000000, + FONT_STYLE_MASK = 0b00000000000000000011100000000000, + FOREGROUND_MASK = 0b00000000011111111100000000000000, + BACKGROUND_MASK = 0b11111111100000000000000000000000, + + LANGUAGEID_OFFSET = 0, + TOKEN_TYPE_OFFSET = 8, + FONT_STYLE_OFFSET = 11, + FOREGROUND_OFFSET = 14, + BACKGROUND_OFFSET = 23 +} + +/** + * @internal + */ +export interface ITokenizationSupport { + + getInitialState(): IState; + + // add offsetDelta to each of the returned indices + tokenize(line: string, state: IState, offsetDelta: number): TokenizationResult; + + tokenize2(line: string, state: IState, offsetDelta: number): TokenizationResult2; +} + +/** + * The state of the tokenizer between two lines. + * It is useful to store flags such as in multiline comment, etc. + * The model will clone the previous line's state and pass it in to tokenize the next line. + */ +export interface IState { + clone(): IState; + equals(other: IState): boolean; +} + +/** + * A hover represents additional information for a symbol or word. Hovers are + * rendered in a tooltip-like widget. + */ +export interface Hover { + /** + * The contents of this hover. + */ + contents: IMarkdownString[]; + + /** + * The range to which this hover applies. When missing, the + * editor will use the range at the current position or the + * current position itself. + */ + range: IRange; +} + +/** + * The hover provider interface defines the contract between extensions and + * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. + */ +export interface HoverProvider { + /** + * Provide a hover for the given position and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + */ + provideHover(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Hover | Thenable; +} + +/** + * @internal + */ +export type SuggestionType = 'method' + | 'function' + | 'constructor' + | 'field' + | 'variable' + | 'class' + | 'struct' + | 'interface' + | 'module' + | 'property' + | 'event' + | 'operator' + | 'unit' + | 'value' + | 'constant' + | 'enum' + | 'enum-member' + | 'keyword' + | 'snippet' + | 'text' + | 'color' + | 'file' + | 'reference' + | 'customcolor' + | 'folder' + | 'type-parameter'; + +/** + * @internal + */ +export type SnippetType = 'internal' | 'textmate'; + +/** + * @internal + */ +export interface ISuggestion { + label: string; + insertText: string; + type: SuggestionType; + detail?: string; + documentation?: string; + filterText?: string; + sortText?: string; + noAutoAccept?: boolean; + commitCharacters?: string[]; + overwriteBefore?: number; + overwriteAfter?: number; + additionalTextEdits?: editorCommon.ISingleEditOperation[]; + command?: Command; + snippetType?: SnippetType; +} + +/** + * @internal + */ +export interface ISuggestResult { + suggestions: ISuggestion[]; + incomplete?: boolean; +} + +/** + * @internal + */ +export interface ISuggestSupport { + + triggerCharacters?: string[]; + + provideCompletionItems(model: editorCommon.IModel, position: Position, token: CancellationToken): ISuggestResult | Thenable; + + resolveCompletionItem?(model: editorCommon.IModel, position: Position, item: ISuggestion, token: CancellationToken): ISuggestion | Thenable; +} + +/** + * The code action interface defines the contract between extensions and + * the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature. + * @internal + */ +export interface CodeActionProvider { + /** + * Provide commands for the given document and range. + */ + provideCodeActions(model: editorCommon.IReadOnlyModel, range: Range, token: CancellationToken): Command[] | Thenable; +} + +/** + * Represents a parameter of a callable-signature. A parameter can + * have a label and a doc-comment. + */ +export interface ParameterInformation { + /** + * The label of this signature. Will be shown in + * the UI. + */ + label: string; + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + documentation?: string; +} +/** + * Represents the signature of something callable. A signature + * can have a label, like a function-name, a doc-comment, and + * a set of parameters. + */ +export interface SignatureInformation { + /** + * The label of this signature. Will be shown in + * the UI. + */ + label: string; + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + documentation?: string; + /** + * The parameters of this signature. + */ + parameters: ParameterInformation[]; +} +/** + * Signature help represents the signature of something + * callable. There can be multiple signatures but only one + * active and only one active parameter. + */ +export interface SignatureHelp { + /** + * One or more signatures. + */ + signatures: SignatureInformation[]; + /** + * The active signature. + */ + activeSignature: number; + /** + * The active parameter of the active signature. + */ + activeParameter: number; +} +/** + * The signature help provider interface defines the contract between extensions and + * the [parameter hints](https://code.visualstudio.com/docs/editor/intellisense)-feature. + */ +export interface SignatureHelpProvider { + + signatureHelpTriggerCharacters: string[]; + + /** + * Provide help for the signature at the given position and document. + */ + provideSignatureHelp(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): SignatureHelp | Thenable; +} + +/** + * A document highlight kind. + */ +export enum DocumentHighlightKind { + /** + * A textual occurrence. + */ + Text, + /** + * Read-access of a symbol, like reading a variable. + */ + Read, + /** + * Write-access of a symbol, like writing to a variable. + */ + Write +} +/** + * A document highlight is a range inside a text document which deserves + * special attention. Usually a document highlight is visualized by changing + * the background color of its range. + */ +export interface DocumentHighlight { + /** + * The range this highlight applies to. + */ + range: IRange; + /** + * The highlight kind, default is [text](#DocumentHighlightKind.Text). + */ + kind: DocumentHighlightKind; +} +/** + * The document highlight provider interface defines the contract between extensions and + * the word-highlight-feature. + */ +export interface DocumentHighlightProvider { + /** + * Provide a set of document highlights, like all occurrences of a variable or + * all exit-points of a function. + */ + provideDocumentHighlights(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): DocumentHighlight[] | Thenable; +} + +/** + * Value-object that contains additional information when + * requesting references. + */ +export interface ReferenceContext { + /** + * Include the declaration of the current symbol. + */ + includeDeclaration: boolean; +} +/** + * The reference provider interface defines the contract between extensions and + * the [find references](https://code.visualstudio.com/docs/editor/editingevolved#_peek)-feature. + */ +export interface ReferenceProvider { + /** + * Provide a set of project-wide references for the given position and document. + */ + provideReferences(model: editorCommon.IReadOnlyModel, position: Position, context: ReferenceContext, token: CancellationToken): Location[] | Thenable; +} + +/** + * Represents a location inside a resource, such as a line + * inside a text file. + */ +export interface Location { + /** + * The resource identifier of this location. + */ + uri: URI; + /** + * The document range of this locations. + */ + range: IRange; +} +/** + * The definition of a symbol represented as one or many [locations](#Location). + * For most programming languages there is only one location at which a symbol is + * defined. + */ +export type Definition = Location | Location[]; + +/** + * The definition provider interface defines the contract between extensions and + * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) + * and peek definition features. + */ +export interface DefinitionProvider { + /** + * Provide the definition of the symbol at the given position and document. + */ + provideDefinition(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Definition | Thenable; +} + +/** + * The implementation provider interface defines the contract between extensions and + * the go to implementation feature. + */ +export interface ImplementationProvider { + /** + * Provide the implementation of the symbol at the given position and document. + */ + provideImplementation(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Definition | Thenable; +} + +/** + * The type definition provider interface defines the contract between extensions and + * the go to type definition feature. + */ +export interface TypeDefinitionProvider { + /** + * Provide the type definition of the symbol at the given position and document. + */ + provideTypeDefinition(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Definition | Thenable; +} + +/** + * A symbol kind. + */ +export enum SymbolKind { + File = 0, + Module = 1, + Namespace = 2, + Package = 3, + Class = 4, + Method = 5, + Property = 6, + Field = 7, + Constructor = 8, + Enum = 9, + Interface = 10, + Function = 11, + Variable = 12, + Constant = 13, + String = 14, + Number = 15, + Boolean = 16, + Array = 17, + Object = 18, + Key = 19, + Null = 20, + EnumMember = 21, + Struct = 22, + Event = 23, + Operator = 24, + TypeParameter = 25 +} + + +/** + * @internal + */ +export const symbolKindToCssClass = (function () { + + const _fromMapping: { [n: number]: string } = Object.create(null); + _fromMapping[SymbolKind.File] = 'file'; + _fromMapping[SymbolKind.Module] = 'module'; + _fromMapping[SymbolKind.Namespace] = 'namespace'; + _fromMapping[SymbolKind.Package] = 'package'; + _fromMapping[SymbolKind.Class] = 'class'; + _fromMapping[SymbolKind.Method] = 'method'; + _fromMapping[SymbolKind.Property] = 'property'; + _fromMapping[SymbolKind.Field] = 'field'; + _fromMapping[SymbolKind.Constructor] = 'constructor'; + _fromMapping[SymbolKind.Enum] = 'enum'; + _fromMapping[SymbolKind.Interface] = 'interface'; + _fromMapping[SymbolKind.Function] = 'function'; + _fromMapping[SymbolKind.Variable] = 'variable'; + _fromMapping[SymbolKind.Constant] = 'constant'; + _fromMapping[SymbolKind.String] = 'string'; + _fromMapping[SymbolKind.Number] = 'number'; + _fromMapping[SymbolKind.Boolean] = 'boolean'; + _fromMapping[SymbolKind.Array] = 'array'; + _fromMapping[SymbolKind.Object] = 'object'; + _fromMapping[SymbolKind.Key] = 'key'; + _fromMapping[SymbolKind.Null] = 'null'; + _fromMapping[SymbolKind.EnumMember] = 'enum-member'; + _fromMapping[SymbolKind.Struct] = 'struct'; + _fromMapping[SymbolKind.Event] = 'event'; + _fromMapping[SymbolKind.Operator] = 'operator'; + _fromMapping[SymbolKind.TypeParameter] = 'type-parameter'; + + return function toCssClassName(kind: SymbolKind): string { + return _fromMapping[kind] || 'property'; + }; +})(); + +/** + * @internal + */ +export interface IOutline { + entries: SymbolInformation[]; +} +/** + * Represents information about programming constructs like variables, classes, + * interfaces etc. + */ +export interface SymbolInformation { + /** + * The name of this symbol. + */ + name: string; + /** + * The name of the symbol containing this symbol. + */ + containerName?: string; + /** + * The kind of this symbol. + */ + kind: SymbolKind; + /** + * The location of this symbol. + */ + location: Location; +} +/** + * The document symbol provider interface defines the contract between extensions and + * the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_goto-symbol)-feature. + */ +export interface DocumentSymbolProvider { + /** + * Provide symbol information for the given document. + */ + provideDocumentSymbols(model: editorCommon.IReadOnlyModel, token: CancellationToken): SymbolInformation[] | Thenable; +} + +export interface TextEdit { + range: IRange; + text: string; + eol?: editorCommon.EndOfLineSequence; +} + +/** + * Interface used to format a model + */ +export interface FormattingOptions { + /** + * Size of a tab in spaces. + */ + tabSize: number; + /** + * Prefer spaces over tabs. + */ + insertSpaces: boolean; +} +/** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ +export interface DocumentFormattingEditProvider { + /** + * Provide formatting edits for a whole document. + */ + provideDocumentFormattingEdits(model: editorCommon.IReadOnlyModel, options: FormattingOptions, token: CancellationToken): TextEdit[] | Thenable; +} +/** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ +export interface DocumentRangeFormattingEditProvider { + /** + * Provide formatting edits for a range in a document. + * + * The given range is a hint and providers can decide to format a smaller + * or larger range. Often this is done by adjusting the start and end + * of the range to full syntax nodes. + */ + provideDocumentRangeFormattingEdits(model: editorCommon.IReadOnlyModel, range: Range, options: FormattingOptions, token: CancellationToken): TextEdit[] | Thenable; +} +/** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ +export interface OnTypeFormattingEditProvider { + autoFormatTriggerCharacters: string[]; + /** + * Provide formatting edits after a character has been typed. + * + * The given position and character should hint to the provider + * what range the position to expand to, like find the matching `{` + * when `}` has been entered. + */ + provideOnTypeFormattingEdits(model: editorCommon.IReadOnlyModel, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): TextEdit[] | Thenable; +} + +/** + * @internal + */ +export interface IInplaceReplaceSupportResult { + value: string; + range: IRange; +} + +/** + * A link inside the editor. + */ +export interface ILink { + range: IRange; + url: string; +} +/** + * A provider of links. + */ +export interface LinkProvider { + provideLinks(model: editorCommon.IReadOnlyModel, token: CancellationToken): ILink[] | Thenable; + resolveLink?: (link: ILink, token: CancellationToken) => ILink | Thenable; +} + +/** + * A color in RGBA format. + */ +export interface IColor { + + /** + * The red component in the range [0-1]. + */ + readonly red: number; + + /** + * The green component in the range [0-1]. + */ + readonly green: number; + + /** + * The blue component in the range [0-1]. + */ + readonly blue: number; + + /** + * The alpha component in the range [0-1]. + */ + readonly alpha: number; +} + +/** + * A color formatter. + */ +export interface IColorFormatter { + readonly supportsTransparency: boolean; + format(color: IColor): string; +} + +/** + * A color range is a range in a text model which represents a color. + */ +export interface IColorRange { + + /** + * The range within the model. + */ + range: IRange; + + /** + * The color represented in this range. + */ + color: IColor; + + /** + * The available formats for this specific color. + */ + formatters: IColorFormatter[]; +} + +/** + * A provider of colors for editor models. + */ +export interface DocumentColorProvider { + /** + * Provides the color ranges for a specific model. + */ + provideColorRanges(model: editorCommon.IReadOnlyModel, token: CancellationToken): IColorRange[] | Thenable; +} + +export interface IResourceEdit { + resource: URI; + range: IRange; + newText: string; +} +export interface WorkspaceEdit { + edits: IResourceEdit[]; + rejectReason?: string; +} +export interface RenameProvider { + provideRenameEdits(model: editorCommon.IReadOnlyModel, position: Position, newName: string, token: CancellationToken): WorkspaceEdit | Thenable; +} + + +export interface Command { + id: string; + title: string; + tooltip?: string; + arguments?: any[]; +} +export interface ICodeLensSymbol { + range: IRange; + id?: string; + command?: Command; +} +export interface CodeLensProvider { + onDidChange?: Event; + provideCodeLenses(model: editorCommon.IReadOnlyModel, token: CancellationToken): ICodeLensSymbol[] | Thenable; + resolveCodeLens?(model: editorCommon.IReadOnlyModel, codeLens: ICodeLensSymbol, token: CancellationToken): ICodeLensSymbol | Thenable; +} + +// --- feature registries ------ + +/** + * @internal + */ +export const ReferenceProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const RenameProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const SuggestRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const SignatureHelpProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const HoverProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const DocumentSymbolProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const DocumentHighlightProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const DefinitionProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const ImplementationProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const TypeDefinitionProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const CodeLensProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const CodeActionProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const DocumentFormattingEditProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const DocumentRangeFormattingEditProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const OnTypeFormattingEditProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const LinkProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const ColorProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export interface ITokenizationSupportChangedEvent { + changedLanguages: string[]; + changedColorMap: boolean; +} + +/** + * @internal + */ +export interface ITokenizationRegistry { + + /** + * An event triggered when: + * - a tokenization support is registered, unregistered or changed. + * - the color map is changed. + */ + onDidChange: Event; + + /** + * Fire a change event for a language. + * This is useful for languages that embed other languages. + */ + fire(languages: string[]): void; + + /** + * Register a tokenization support. + */ + register(language: string, support: ITokenizationSupport): IDisposable; + + /** + * Get the tokenization support for a language. + * Returns null if not found. + */ + get(language: string): ITokenizationSupport; + + /** + * Set the new color map that all tokens will use in their ColorId binary encoded bits for foreground and background. + */ + setColorMap(colorMap: Color[]): void; + + getColorMap(): Color[]; + getDefaultForeground(): Color; + getDefaultBackground(): Color; +} + +/** + * @internal + */ +export const TokenizationRegistry = new TokenizationRegistryImpl(); diff --git a/src/vs/editor/common/modes/abstractMode.ts b/src/vs/editor/common/modes/abstractMode.ts new file mode 100644 index 0000000000..c3a60bcc46 --- /dev/null +++ b/src/vs/editor/common/modes/abstractMode.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IMode, LanguageIdentifier } from 'vs/editor/common/modes'; + +export class FrankensteinMode implements IMode { + + private _languageIdentifier: LanguageIdentifier; + + constructor(languageIdentifier: LanguageIdentifier) { + this._languageIdentifier = languageIdentifier; + } + + public getId(): string { + return this._languageIdentifier.language; + } + + public getLanguageIdentifier(): LanguageIdentifier { + return this._languageIdentifier; + } +} diff --git a/src/vs/editor/common/modes/editorModeContext.ts b/src/vs/editor/common/modes/editorModeContext.ts new file mode 100644 index 0000000000..b39443c0bc --- /dev/null +++ b/src/vs/editor/common/modes/editorModeContext.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import * as modes from 'vs/editor/common/modes'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Schemas } from 'vs/base/common/network'; + +export class EditorModeContext extends Disposable { + + private _editor: ICommonCodeEditor; + + private _langId: IContextKey; + private _hasCompletionItemProvider: IContextKey; + private _hasCodeActionsProvider: IContextKey; + private _hasCodeLensProvider: IContextKey; + private _hasDefinitionProvider: IContextKey; + private _hasImplementationProvider: IContextKey; + private _hasTypeDefinitionProvider: IContextKey; + private _hasHoverProvider: IContextKey; + private _hasDocumentHighlightProvider: IContextKey; + private _hasDocumentSymbolProvider: IContextKey; + private _hasReferenceProvider: IContextKey; + private _hasRenameProvider: IContextKey; + private _hasDocumentFormattingProvider: IContextKey; + private _hasDocumentSelectionFormattingProvider: IContextKey; + private _hasSignatureHelpProvider: IContextKey; + private _isInWalkThrough: IContextKey; + + constructor( + editor: ICommonCodeEditor, + contextKeyService: IContextKeyService + ) { + super(); + this._editor = editor; + + this._langId = EditorContextKeys.languageId.bindTo(contextKeyService); + this._hasCompletionItemProvider = EditorContextKeys.hasCompletionItemProvider.bindTo(contextKeyService); + this._hasCodeActionsProvider = EditorContextKeys.hasCodeActionsProvider.bindTo(contextKeyService); + this._hasCodeLensProvider = EditorContextKeys.hasCodeLensProvider.bindTo(contextKeyService); + this._hasDefinitionProvider = EditorContextKeys.hasDefinitionProvider.bindTo(contextKeyService); + this._hasImplementationProvider = EditorContextKeys.hasImplementationProvider.bindTo(contextKeyService); + this._hasTypeDefinitionProvider = EditorContextKeys.hasTypeDefinitionProvider.bindTo(contextKeyService); + this._hasHoverProvider = EditorContextKeys.hasHoverProvider.bindTo(contextKeyService); + this._hasDocumentHighlightProvider = EditorContextKeys.hasDocumentHighlightProvider.bindTo(contextKeyService); + this._hasDocumentSymbolProvider = EditorContextKeys.hasDocumentSymbolProvider.bindTo(contextKeyService); + this._hasReferenceProvider = EditorContextKeys.hasReferenceProvider.bindTo(contextKeyService); + this._hasRenameProvider = EditorContextKeys.hasRenameProvider.bindTo(contextKeyService); + this._hasDocumentFormattingProvider = EditorContextKeys.hasDocumentFormattingProvider.bindTo(contextKeyService); + this._hasDocumentSelectionFormattingProvider = EditorContextKeys.hasDocumentSelectionFormattingProvider.bindTo(contextKeyService); + this._hasSignatureHelpProvider = EditorContextKeys.hasSignatureHelpProvider.bindTo(contextKeyService); + this._isInWalkThrough = EditorContextKeys.isInEmbeddedEditor.bindTo(contextKeyService); + + const update = () => this._update(); + + // update when model/mode changes + this._register(editor.onDidChangeModel(update)); + this._register(editor.onDidChangeModelLanguage(update)); + + // update when registries change + this._register(modes.SuggestRegistry.onDidChange(update)); + this._register(modes.CodeActionProviderRegistry.onDidChange(update)); + this._register(modes.CodeLensProviderRegistry.onDidChange(update)); + this._register(modes.DefinitionProviderRegistry.onDidChange(update)); + this._register(modes.ImplementationProviderRegistry.onDidChange(update)); + this._register(modes.TypeDefinitionProviderRegistry.onDidChange(update)); + this._register(modes.HoverProviderRegistry.onDidChange(update)); + this._register(modes.DocumentHighlightProviderRegistry.onDidChange(update)); + this._register(modes.DocumentSymbolProviderRegistry.onDidChange(update)); + this._register(modes.ReferenceProviderRegistry.onDidChange(update)); + this._register(modes.RenameProviderRegistry.onDidChange(update)); + this._register(modes.DocumentFormattingEditProviderRegistry.onDidChange(update)); + this._register(modes.DocumentRangeFormattingEditProviderRegistry.onDidChange(update)); + this._register(modes.SignatureHelpProviderRegistry.onDidChange(update)); + + update(); + } + + dispose() { + super.dispose(); + } + + reset() { + this._langId.reset(); + this._hasCompletionItemProvider.reset(); + this._hasCodeActionsProvider.reset(); + this._hasCodeLensProvider.reset(); + this._hasDefinitionProvider.reset(); + this._hasImplementationProvider.reset(); + this._hasTypeDefinitionProvider.reset(); + this._hasHoverProvider.reset(); + this._hasDocumentHighlightProvider.reset(); + this._hasDocumentSymbolProvider.reset(); + this._hasReferenceProvider.reset(); + this._hasRenameProvider.reset(); + this._hasDocumentFormattingProvider.reset(); + this._hasDocumentSelectionFormattingProvider.reset(); + this._hasSignatureHelpProvider.reset(); + this._isInWalkThrough.reset(); + } + + private _update() { + const model = this._editor.getModel(); + if (!model) { + this.reset(); + return; + } + this._langId.set(model.getLanguageIdentifier().language); + this._hasCompletionItemProvider.set(modes.SuggestRegistry.has(model)); + this._hasCodeActionsProvider.set(modes.CodeActionProviderRegistry.has(model)); + this._hasCodeLensProvider.set(modes.CodeLensProviderRegistry.has(model)); + this._hasDefinitionProvider.set(modes.DefinitionProviderRegistry.has(model)); + this._hasImplementationProvider.set(modes.ImplementationProviderRegistry.has(model)); + this._hasTypeDefinitionProvider.set(modes.TypeDefinitionProviderRegistry.has(model)); + this._hasHoverProvider.set(modes.HoverProviderRegistry.has(model)); + this._hasDocumentHighlightProvider.set(modes.DocumentHighlightProviderRegistry.has(model)); + this._hasDocumentSymbolProvider.set(modes.DocumentSymbolProviderRegistry.has(model)); + this._hasReferenceProvider.set(modes.ReferenceProviderRegistry.has(model)); + this._hasRenameProvider.set(modes.RenameProviderRegistry.has(model)); + this._hasSignatureHelpProvider.set(modes.SignatureHelpProviderRegistry.has(model)); + this._hasDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.has(model) || modes.DocumentRangeFormattingEditProviderRegistry.has(model)); + this._hasDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.has(model)); + this._isInWalkThrough.set(model.uri.scheme === Schemas.walkThroughSnippet); + } +} diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts new file mode 100644 index 0000000000..bd0472b128 --- /dev/null +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { StandardTokenType } from 'vs/editor/common/modes'; + +/** + * Describes how comments for a language work. + */ +export interface CommentRule { + /** + * The line comment token, like `// this is a comment` + */ + lineComment?: string; + /** + * The block comment character pair, like `/* block comment */` + */ + blockComment?: CharacterPair; +} + +/** + * The language configuration interface defines the contract between extensions and + * various editor features, like automatic bracket insertion, automatic indentation etc. + */ +export interface LanguageConfiguration { + /** + * The language's comment settings. + */ + comments?: CommentRule; + /** + * The language's brackets. + * This configuration implicitly affects pressing Enter around these brackets. + */ + brackets?: CharacterPair[]; + /** + * The language's word definition. + * If the language supports Unicode identifiers (e.g. JavaScript), it is preferable + * to provide a word definition that uses exclusion of known separators. + * e.g.: A regex that matches anything except known separators (and dot is allowed to occur in a floating point number): + * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + */ + wordPattern?: RegExp; + /** + * The language's indentation settings. + */ + indentationRules?: IndentationRule; + /** + * The language's rules to be evaluated when pressing Enter. + */ + onEnterRules?: OnEnterRule[]; + /** + * The language's auto closing pairs. The 'close' character is automatically inserted with the + * 'open' character is typed. If not set, the configured brackets will be used. + */ + autoClosingPairs?: IAutoClosingPairConditional[]; + /** + * The language's surrounding pairs. When the 'open' character is typed on a selection, the + * selected string is surrounded by the open and close characters. If not set, the autoclosing pairs + * settings will be used. + */ + surroundingPairs?: IAutoClosingPair[]; + /** + * **Deprecated** Do not use. + * + * @deprecated Will be replaced by a better API soon. + */ + __electricCharacterSupport?: IBracketElectricCharacterContribution; +} + +/** + * Describes indentation rules for a language. + */ +export interface IndentationRule { + /** + * If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches). + */ + decreaseIndentPattern: RegExp; + /** + * If a line matches this pattern, then all the lines after it should be indented once (until another rule matches). + */ + increaseIndentPattern: RegExp; + /** + * If a line matches this pattern, then **only the next line** after it should be indented once. + */ + indentNextLinePattern?: RegExp; + /** + * If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules. + */ + unIndentedLinePattern?: RegExp; +} + +/** + * Describes a rule to be evaluated when pressing Enter. + */ +export interface OnEnterRule { + /** + * This rule will only execute if the text before the cursor matches this regular expression. + */ + beforeText: RegExp; + /** + * This rule will only execute if the text after the cursor matches this regular expression. + */ + afterText?: RegExp; + /** + * The action to execute. + */ + action: EnterAction; +} + +export interface IBracketElectricCharacterContribution { + docComment?: IDocComment; +} + +/** + * Definition of documentation comments (e.g. Javadoc/JSdoc) + */ +export interface IDocComment { + /** + * The string that starts a doc comment (e.g. '/**') + */ + open: string; + /** + * The string that appears on the last line and closes the doc comment (e.g. ' * /'). + */ + close: string; +} + +/** + * A tuple of two characters, like a pair of + * opening and closing brackets. + */ +export type CharacterPair = [string, string]; + +export interface IAutoClosingPair { + open: string; + close: string; +} + +export interface IAutoClosingPairConditional extends IAutoClosingPair { + notIn?: string[]; +} + +/** + * Describes what to do with the indentation when pressing Enter. + */ +export enum IndentAction { + /** + * Insert new line and copy the previous line's indentation. + */ + None = 0, + /** + * Insert new line and indent once (relative to the previous line's indentation). + */ + Indent = 1, + /** + * Insert two new lines: + * - the first one indented which will hold the cursor + * - the second one at the same indentation level + */ + IndentOutdent = 2, + /** + * Insert new line and outdent once (relative to the previous line's indentation). + */ + Outdent = 3 +} + +/** + * Describes what to do when pressing Enter. + */ +export interface EnterAction { + /** + * Describe what to do with the indentation. + */ + indentAction: IndentAction; + /** + * Describe whether to outdent current line. + */ + outdentCurrentLine?: boolean; + /** + * Describes text to be appended after the new line and after the indentation. + */ + appendText?: string; + /** + * Describes the number of characters to remove from the new line's indentation. + */ + removeText?: number; +} + +/** + * @internal + */ +export class StandardAutoClosingPairConditional { + _standardAutoClosingPairConditionalBrand: void; + + readonly open: string; + readonly close: string; + private readonly _standardTokenMask: number; + + constructor(source: IAutoClosingPairConditional) { + this.open = source.open; + this.close = source.close; + + // initially allowed in all tokens + this._standardTokenMask = 0; + + if (Array.isArray(source.notIn)) { + for (let i = 0, len = source.notIn.length; i < len; i++) { + let notIn = source.notIn[i]; + switch (notIn) { + case 'string': + this._standardTokenMask |= StandardTokenType.String; + break; + case 'comment': + this._standardTokenMask |= StandardTokenType.Comment; + break; + case 'regex': + this._standardTokenMask |= StandardTokenType.RegEx; + break; + } + } + } + } + + public isOK(standardToken: StandardTokenType): boolean { + return (this._standardTokenMask & standardToken) === 0; + } +} diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts new file mode 100644 index 0000000000..8fda80bb23 --- /dev/null +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -0,0 +1,776 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; +import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; +import { IOnEnterSupportOptions, OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter'; +import { IndentRulesSupport, IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; +import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; +import Event, { Emitter } from 'vs/base/common/event'; +import { ITokenizedModel } from 'vs/editor/common/editorCommon'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import * as strings from 'vs/base/common/strings'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; +import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { Range } from 'vs/editor/common/core/range'; +import { IndentAction, EnterAction, IAutoClosingPair, LanguageConfiguration, IndentationRule } from 'vs/editor/common/modes/languageConfiguration'; +import { LanguageIdentifier, LanguageId } from 'vs/editor/common/modes'; + +/** + * Interface used to support insertion of mode specific comments. + */ +export interface ICommentsConfiguration { + lineCommentToken?: string; + blockCommentStartToken?: string; + blockCommentEndToken?: string; +} + +export interface IVirtualModel { + getLineTokens(lineNumber: number): LineTokens; + getLanguageIdentifier(): LanguageIdentifier; + getLanguageIdAtPosition(lineNumber: number, column: number): LanguageId; + getLineContent(lineNumber: number): string; +} + +export interface IIndentConverter { + shiftIndent?(indentation: string): string; + unshiftIndent?(indentation: string): string; + normalizeIndentation?(indentation: string): string; +} + +export class RichEditSupport { + + private readonly _conf: LanguageConfiguration; + + public readonly electricCharacter: BracketElectricCharacterSupport; + public readonly comments: ICommentsConfiguration; + public readonly characterPair: CharacterPairSupport; + public readonly wordDefinition: RegExp; + public readonly onEnter: OnEnterSupport; + public readonly indentRulesSupport: IndentRulesSupport; + public readonly brackets: RichEditBrackets; + public readonly indentationRules: IndentationRule; + + constructor(languageIdentifier: LanguageIdentifier, previous: RichEditSupport, rawConf: LanguageConfiguration) { + + let prev: LanguageConfiguration = null; + if (previous) { + prev = previous._conf; + } + + this._conf = RichEditSupport._mergeConf(prev, rawConf); + + if (this._conf.brackets) { + this.brackets = new RichEditBrackets(languageIdentifier, this._conf.brackets); + } + + this.onEnter = RichEditSupport._handleOnEnter(this._conf); + + this.comments = RichEditSupport._handleComments(this._conf); + + this.characterPair = new CharacterPairSupport(this._conf); + this.electricCharacter = new BracketElectricCharacterSupport(this.brackets, this.characterPair.getAutoClosingPairs(), this._conf.__electricCharacterSupport); + + this.wordDefinition = this._conf.wordPattern || DEFAULT_WORD_REGEXP; + + this.indentationRules = this._conf.indentationRules; + if (this._conf.indentationRules) { + this.indentRulesSupport = new IndentRulesSupport(this._conf.indentationRules); + } + } + + private static _mergeConf(prev: LanguageConfiguration, current: LanguageConfiguration): LanguageConfiguration { + return { + comments: (prev ? current.comments || prev.comments : current.comments), + brackets: (prev ? current.brackets || prev.brackets : current.brackets), + wordPattern: (prev ? current.wordPattern || prev.wordPattern : current.wordPattern), + indentationRules: (prev ? current.indentationRules || prev.indentationRules : current.indentationRules), + onEnterRules: (prev ? current.onEnterRules || prev.onEnterRules : current.onEnterRules), + autoClosingPairs: (prev ? current.autoClosingPairs || prev.autoClosingPairs : current.autoClosingPairs), + surroundingPairs: (prev ? current.surroundingPairs || prev.surroundingPairs : current.surroundingPairs), + __electricCharacterSupport: (prev ? current.__electricCharacterSupport || prev.__electricCharacterSupport : current.__electricCharacterSupport), + }; + } + + private static _handleOnEnter(conf: LanguageConfiguration): OnEnterSupport { + // on enter + let onEnter: IOnEnterSupportOptions = {}; + let empty = true; + + if (conf.brackets) { + empty = false; + onEnter.brackets = conf.brackets; + } + if (conf.indentationRules) { + empty = false; + onEnter.indentationRules = conf.indentationRules; + } + if (conf.onEnterRules) { + empty = false; + onEnter.regExpRules = conf.onEnterRules; + } + + if (!empty) { + return new OnEnterSupport(onEnter); + } + return null; + } + + private static _handleComments(conf: LanguageConfiguration): ICommentsConfiguration { + let commentRule = conf.comments; + if (!commentRule) { + return null; + } + + // comment configuration + let comments: ICommentsConfiguration = {}; + + if (commentRule.lineComment) { + comments.lineCommentToken = commentRule.lineComment; + } + if (commentRule.blockComment) { + let [blockStart, blockEnd] = commentRule.blockComment; + comments.blockCommentStartToken = blockStart; + comments.blockCommentEndToken = blockEnd; + } + + return comments; + } +} + +export class LanguageConfigurationRegistryImpl { + + private _entries: RichEditSupport[]; + + private _onDidChange: Emitter = new Emitter(); + public onDidChange: Event = this._onDidChange.event; + + constructor() { + this._entries = []; + } + + public register(languageIdentifier: LanguageIdentifier, configuration: LanguageConfiguration): IDisposable { + let previous = this._getRichEditSupport(languageIdentifier.id); + let current = new RichEditSupport(languageIdentifier, previous, configuration); + this._entries[languageIdentifier.id] = current; + this._onDidChange.fire(void 0); + return { + dispose: () => { + if (this._entries[languageIdentifier.id] === current) { + this._entries[languageIdentifier.id] = previous; + this._onDidChange.fire(void 0); + } + } + }; + } + + private _getRichEditSupport(languageId: LanguageId): RichEditSupport { + return this._entries[languageId] || null; + } + + public getIndentationRules(languageId: LanguageId) { + let value = this._entries[languageId]; + + if (!value) { + return null; + } + + return value.indentationRules || null; + } + + // begin electricCharacter + + private _getElectricCharacterSupport(languageId: LanguageId): BracketElectricCharacterSupport { + let value = this._getRichEditSupport(languageId); + if (!value) { + return null; + } + return value.electricCharacter || null; + } + + public getElectricCharacters(languageId: LanguageId): string[] { + let electricCharacterSupport = this._getElectricCharacterSupport(languageId); + if (!electricCharacterSupport) { + return []; + } + return electricCharacterSupport.getElectricCharacters(); + } + + /** + * Should return opening bracket type to match indentation with + */ + public onElectricCharacter(character: string, context: LineTokens, column: number): IElectricAction { + let scopedLineTokens = createScopedLineTokens(context, column - 1); + let electricCharacterSupport = this._getElectricCharacterSupport(scopedLineTokens.languageId); + if (!electricCharacterSupport) { + return null; + } + return electricCharacterSupport.onElectricCharacter(character, scopedLineTokens, column - scopedLineTokens.firstCharOffset); + } + + // end electricCharacter + + public getComments(languageId: LanguageId): ICommentsConfiguration { + let value = this._getRichEditSupport(languageId); + if (!value) { + return null; + } + return value.comments || null; + } + + // begin characterPair + + private _getCharacterPairSupport(languageId: LanguageId): CharacterPairSupport { + let value = this._getRichEditSupport(languageId); + if (!value) { + return null; + } + return value.characterPair || null; + } + + public getAutoClosingPairs(languageId: LanguageId): IAutoClosingPair[] { + let characterPairSupport = this._getCharacterPairSupport(languageId); + if (!characterPairSupport) { + return []; + } + return characterPairSupport.getAutoClosingPairs(); + } + + public getSurroundingPairs(languageId: LanguageId): IAutoClosingPair[] { + let characterPairSupport = this._getCharacterPairSupport(languageId); + if (!characterPairSupport) { + return []; + } + return characterPairSupport.getSurroundingPairs(); + } + + public shouldAutoClosePair(character: string, context: LineTokens, column: number): boolean { + let scopedLineTokens = createScopedLineTokens(context, column - 1); + let characterPairSupport = this._getCharacterPairSupport(scopedLineTokens.languageId); + if (!characterPairSupport) { + return false; + } + return characterPairSupport.shouldAutoClosePair(character, scopedLineTokens, column - scopedLineTokens.firstCharOffset); + } + + // end characterPair + + public getWordDefinition(languageId: LanguageId): RegExp { + let value = this._getRichEditSupport(languageId); + if (!value) { + return ensureValidWordDefinition(null); + } + return ensureValidWordDefinition(value.wordDefinition || null); + } + + + + // beigin Indent Rules + + public getIndentRulesSupport(languageId: LanguageId): IndentRulesSupport { + let value = this._getRichEditSupport(languageId); + if (!value) { + return null; + } + return value.indentRulesSupport || null; + } + + /** + * Get nearest preceiding line which doesn't match unIndentPattern or contains all whitespace. + * Result: + * -1: run into the boundary of embedded languages + * 0: every line above are invalid + * else: nearest preceding line of the same language + */ + private getPrecedingValidLine(model: IVirtualModel, lineNumber: number, indentRulesSupport: IndentRulesSupport) { + let languageID = model.getLanguageIdAtPosition(lineNumber, 0); + if (lineNumber > 1) { + let lastLineNumber = lineNumber - 1; + let resultLineNumber = -1; + + for (lastLineNumber = lineNumber - 1; lastLineNumber >= 1; lastLineNumber--) { + if (model.getLanguageIdAtPosition(lastLineNumber, 0) !== languageID) { + return resultLineNumber; + } + let text = model.getLineContent(lastLineNumber); + if (indentRulesSupport.shouldIgnore(text) || /^\s+$/.test(text) || text === '') { + resultLineNumber = lastLineNumber; + continue; + } + + return lastLineNumber; + } + } + + return -1; + } + + /** + * Get inherited indentation from above lines. + * 1. Find the nearest preceding line which doesn't match unIndentedLinePattern. + * 2. If this line matches indentNextLinePattern or increaseIndentPattern, it means that the indent level of `lineNumber` should be 1 greater than this line. + * 3. If this line doesn't match any indent rules + * a. check whether the line above it matches indentNextLinePattern + * b. If not, the indent level of this line is the result + * c. If so, it means the indent of this line is *temporary*, go upward utill we find a line whose indent is not temporary (the same workflow a -> b -> c). + * 4. Otherwise, we fail to get an inherited indent from aboves. Return null and we should not touch the indent of `lineNumber` + * + * This function only return the inherited indent based on above lines, it doesn't check whether current line should decrease or not. + */ + public getInheritIndentForLine(model: IVirtualModel, lineNumber: number, honorIntentialIndent: boolean = true): { indentation: string, action: IndentAction, line?: number } { + let indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id); + if (!indentRulesSupport) { + return null; + } + + if (lineNumber <= 1) { + return { + indentation: '', + action: null + }; + } + + let precedingUnIgnoredLine = this.getPrecedingValidLine(model, lineNumber, indentRulesSupport); + if (precedingUnIgnoredLine < 0) { + return null; + } else if (precedingUnIgnoredLine < 1) { + return { + indentation: '', + action: null + }; + } + + let precedingUnIgnoredLineContent = model.getLineContent(precedingUnIgnoredLine); + + if (indentRulesSupport.shouldIncrease(precedingUnIgnoredLineContent) || indentRulesSupport.shouldIndentNextLine(precedingUnIgnoredLineContent)) { + return { + indentation: strings.getLeadingWhitespace(precedingUnIgnoredLineContent), + action: IndentAction.Indent, + line: precedingUnIgnoredLine + }; + } else if (indentRulesSupport.shouldDecrease(precedingUnIgnoredLineContent)) { + return { + indentation: strings.getLeadingWhitespace(precedingUnIgnoredLineContent), + action: null, + line: precedingUnIgnoredLine + }; + } else { + // precedingUnIgnoredLine can not be ignored. + // it doesn't increase indent of following lines + // it doesn't increase just next line + // so current line is not affect by precedingUnIgnoredLine + // and then we should get a correct inheritted indentation from above lines + if (precedingUnIgnoredLine === 1) { + return { + indentation: strings.getLeadingWhitespace(model.getLineContent(precedingUnIgnoredLine)), + action: null, + line: precedingUnIgnoredLine + }; + } + + let previousLine = precedingUnIgnoredLine - 1; + + let previousLineIndentMetadata = indentRulesSupport.getIndentMetadata(model.getLineContent(previousLine)); + if (!(previousLineIndentMetadata & (IndentConsts.INCREASE_MASK | IndentConsts.DECREASE_MASK)) && + (previousLineIndentMetadata & IndentConsts.INDENT_NEXTLINE_MASK)) { + let stopLine = 0; + for (let i = previousLine - 1; i > 0; i--) { + if (indentRulesSupport.shouldIndentNextLine(model.getLineContent(i))) { + continue; + } + stopLine = i; + break; + } + + return { + indentation: strings.getLeadingWhitespace(model.getLineContent(stopLine + 1)), + action: null, + line: stopLine + 1 + }; + } + + if (honorIntentialIndent) { + return { + indentation: strings.getLeadingWhitespace(model.getLineContent(precedingUnIgnoredLine)), + action: null, + line: precedingUnIgnoredLine + }; + } else { + // search from precedingUnIgnoredLine until we find one whose indent is not temporary + for (let i = precedingUnIgnoredLine; i > 0; i--) { + let lineContent = model.getLineContent(i); + if (indentRulesSupport.shouldIncrease(lineContent)) { + return { + indentation: strings.getLeadingWhitespace(lineContent), + action: IndentAction.Indent, + line: i + }; + } else if (indentRulesSupport.shouldIndentNextLine(lineContent)) { + let stopLine = 0; + for (let j = i - 1; j > 0; j--) { + if (indentRulesSupport.shouldIndentNextLine(model.getLineContent(i))) { + continue; + } + stopLine = j; + break; + } + + return { + indentation: strings.getLeadingWhitespace(model.getLineContent(stopLine + 1)), + action: null, + line: stopLine + 1 + }; + } else if (indentRulesSupport.shouldDecrease(lineContent)) { + return { + indentation: strings.getLeadingWhitespace(lineContent), + action: null, + line: i + }; + } + } + + return { + indentation: strings.getLeadingWhitespace(model.getLineContent(1)), + action: null, + line: 1 + }; + } + } + } + + public getGoodIndentForLine(virtualModel: IVirtualModel, languageId: LanguageId, lineNumber: number, indentConverter: IIndentConverter): string { + let indentRulesSupport = this.getIndentRulesSupport(languageId); + if (!indentRulesSupport) { + return null; + } + + let indent = this.getInheritIndentForLine(virtualModel, lineNumber); + let lineContent = virtualModel.getLineContent(lineNumber); + + if (indent) { + let inheritLine = indent.line; + if (inheritLine !== undefined) { + let onEnterSupport = this._getOnEnterSupport(languageId); + let enterResult: EnterAction = null; + try { + enterResult = onEnterSupport.onEnter('', virtualModel.getLineContent(inheritLine), ''); + } catch (e) { + onUnexpectedError(e); + } + + if (enterResult) { + let indentation = strings.getLeadingWhitespace(virtualModel.getLineContent(inheritLine)); + + if (enterResult.removeText) { + indentation = indentation.substring(0, indentation.length - enterResult.removeText); + } + + if ( + (enterResult.indentAction === IndentAction.Indent) || + (enterResult.indentAction === IndentAction.IndentOutdent) + ) { + indentation = indentConverter.shiftIndent(indentation); + } else if (enterResult.indentAction === IndentAction.Outdent) { + indentation = indentConverter.unshiftIndent(indentation); + } + + if (indentRulesSupport.shouldDecrease(lineContent)) { + indentation = indentConverter.unshiftIndent(indentation); + } + + if (enterResult.appendText) { + indentation += enterResult.appendText; + } + + return strings.getLeadingWhitespace(indentation); + } + } + + if (indentRulesSupport.shouldDecrease(lineContent)) { + if (indent.action === IndentAction.Indent) { + return indent.indentation; + } else { + return indentConverter.unshiftIndent(indent.indentation); + } + } else { + if (indent.action === IndentAction.Indent) { + return indentConverter.shiftIndent(indent.indentation); + } else { + return indent.indentation; + } + } + } + return null; + } + + public getIndentForEnter(model: ITokenizedModel, range: Range, indentConverter: IIndentConverter, autoIndent: boolean): { beforeEnter: string, afterEnter: string } { + model.forceTokenization(range.startLineNumber); + let lineTokens = model.getLineTokens(range.startLineNumber); + + let beforeEnterText; + let afterEnterText; + let scopedLineTokens = createScopedLineTokens(lineTokens, range.startColumn - 1); + let scopedLineText = scopedLineTokens.getLineContent(); + + let embeddedLanguage = false; + if (scopedLineTokens.firstCharOffset > 0 && lineTokens.getLanguageId(0) !== scopedLineTokens.languageId) { + // we are in the embeded language content + embeddedLanguage = true; // if embeddedLanguage is true, then we don't touch the indentation of current line + beforeEnterText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset); + } else { + beforeEnterText = lineTokens.getLineContent().substring(0, range.startColumn - 1); + } + + if (range.isEmpty()) { + afterEnterText = scopedLineText.substr(range.startColumn - 1 - scopedLineTokens.firstCharOffset); + } else { + const endScopedLineTokens = this.getScopedLineTokens(model, range.endLineNumber, range.endColumn); + afterEnterText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset); + } + + let indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId); + + if (!indentRulesSupport) { + return null; + } + + let beforeEnterResult = beforeEnterText; + let beforeEnterIndent = strings.getLeadingWhitespace(beforeEnterText); + + if (!autoIndent && !embeddedLanguage) { + let beforeEnterIndentAction = this.getInheritIndentForLine(model, range.startLineNumber); + + if (indentRulesSupport.shouldDecrease(beforeEnterText)) { + if (beforeEnterIndentAction) { + beforeEnterIndent = beforeEnterIndentAction.indentation; + if (beforeEnterIndentAction.action !== IndentAction.Indent) { + beforeEnterIndent = indentConverter.unshiftIndent(beforeEnterIndent); + } + } + } + + beforeEnterResult = beforeEnterIndent + strings.ltrim(strings.ltrim(beforeEnterText, ' '), '\t'); + } + + let virtualModel: IVirtualModel = { + getLineTokens: (lineNumber: number) => { + return model.getLineTokens(lineNumber); + }, + getLanguageIdentifier: () => { + return model.getLanguageIdentifier(); + }, + getLanguageIdAtPosition: (lineNumber: number, column: number) => { + return model.getLanguageIdAtPosition(lineNumber, column); + }, + getLineContent: (lineNumber: number) => { + if (lineNumber === range.startLineNumber) { + return beforeEnterResult; + } else { + return model.getLineContent(lineNumber); + } + } + }; + + let currentLineIndent = strings.getLeadingWhitespace(lineTokens.getLineContent()); + let afterEnterAction = this.getInheritIndentForLine(virtualModel, range.startLineNumber + 1); + if (!afterEnterAction) { + let beforeEnter = embeddedLanguage ? currentLineIndent : beforeEnterIndent; + return { + beforeEnter: beforeEnter, + afterEnter: beforeEnter + }; + } + + let afterEnterIndent = embeddedLanguage ? currentLineIndent : afterEnterAction.indentation; + + if (afterEnterAction.action === IndentAction.Indent) { + afterEnterIndent = indentConverter.shiftIndent(afterEnterIndent); + } + + if (indentRulesSupport.shouldDecrease(afterEnterText)) { + afterEnterIndent = indentConverter.unshiftIndent(afterEnterIndent); + } + + return { + beforeEnter: embeddedLanguage ? currentLineIndent : beforeEnterIndent, + afterEnter: afterEnterIndent + }; + } + + /** + * We should always allow intentional indentation. It means, if users change the indentation of `lineNumber` and the content of + * this line doesn't match decreaseIndentPattern, we should not adjust the indentation. + */ + public getIndentActionForType(model: ITokenizedModel, range: Range, ch: string, indentConverter: IIndentConverter): string { + let scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn); + let indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId); + if (!indentRulesSupport) { + return null; + } + + let scopedLineText = scopedLineTokens.getLineContent(); + let beforeTypeText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset); + let afterTypeText; + + // selection support + if (range.isEmpty()) { + afterTypeText = scopedLineText.substr(range.startColumn - 1 - scopedLineTokens.firstCharOffset); + } else { + const endScopedLineTokens = this.getScopedLineTokens(model, range.endLineNumber, range.endColumn); + afterTypeText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset); + } + + // If previous content already matches decreaseIndentPattern, it means indentation of this line should already be adjusted + // Users might change the indentation by purpose and we should honor that instead of readjusting. + if (!indentRulesSupport.shouldDecrease(beforeTypeText + afterTypeText) && indentRulesSupport.shouldDecrease(beforeTypeText + ch + afterTypeText)) { + // after typing `ch`, the content matches decreaseIndentPattern, we should adjust the indent to a good manner. + // 1. Get inherited indent action + let r = this.getInheritIndentForLine(model, range.startLineNumber, false); + if (!r) { + return null; + } + + let indentation = r.indentation; + + if (r.action !== IndentAction.Indent) { + indentation = indentConverter.unshiftIndent(indentation); + } + + return indentation; + } + + return null; + } + + public getIndentMetadata(model: ITokenizedModel, lineNumber: number): number { + let indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id); + if (!indentRulesSupport) { + return null; + } + + if (lineNumber < 1 || lineNumber > model.getLineCount()) { + return null; + } + + return indentRulesSupport.getIndentMetadata(model.getLineContent(lineNumber)); + } + + // end Indent Rules + + // begin onEnter + + private _getOnEnterSupport(languageId: LanguageId): OnEnterSupport { + let value = this._getRichEditSupport(languageId); + if (!value) { + return null; + } + return value.onEnter || null; + } + + public getRawEnterActionAtPosition(model: ITokenizedModel, lineNumber: number, column: number): EnterAction { + let r = this.getEnterAction(model, new Range(lineNumber, column, lineNumber, column)); + + return r ? r.enterAction : null; + } + + public getEnterAction(model: ITokenizedModel, range: Range): { enterAction: EnterAction; indentation: string; } { + let indentation = this.getIndentationAtPosition(model, range.startLineNumber, range.startColumn); + + let scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn); + let onEnterSupport = this._getOnEnterSupport(scopedLineTokens.languageId); + if (!onEnterSupport) { + return null; + } + + let scopedLineText = scopedLineTokens.getLineContent(); + let beforeEnterText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset); + let afterEnterText; + + // selection support + if (range.isEmpty()) { + afterEnterText = scopedLineText.substr(range.startColumn - 1 - scopedLineTokens.firstCharOffset); + } else { + const endScopedLineTokens = this.getScopedLineTokens(model, range.endLineNumber, range.endColumn); + afterEnterText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset); + } + + let lineNumber = range.startLineNumber; + let oneLineAboveText = ''; + + if (lineNumber > 1 && scopedLineTokens.firstCharOffset === 0) { + // This is not the first line and the entire line belongs to this mode + let oneLineAboveScopedLineTokens = this.getScopedLineTokens(model, lineNumber - 1); + if (oneLineAboveScopedLineTokens.languageId === scopedLineTokens.languageId) { + // The line above ends with text belonging to the same mode + oneLineAboveText = oneLineAboveScopedLineTokens.getLineContent(); + } + } + + let enterResult: EnterAction = null; + try { + enterResult = onEnterSupport.onEnter(oneLineAboveText, beforeEnterText, afterEnterText); + } catch (e) { + onUnexpectedError(e); + } + + if (!enterResult) { + return null; + } else { + // Here we add `\t` to appendText first because enterAction is leveraging appendText and removeText to change indentation. + if (!enterResult.appendText) { + if ( + (enterResult.indentAction === IndentAction.Indent) || + (enterResult.indentAction === IndentAction.IndentOutdent) + ) { + enterResult.appendText = '\t'; + } else { + enterResult.appendText = ''; + } + } + } + + if (enterResult.removeText) { + indentation = indentation.substring(0, indentation.length - enterResult.removeText); + } + + return { + enterAction: enterResult, + indentation: indentation, + }; + } + + public getIndentationAtPosition(model: ITokenizedModel, lineNumber: number, column: number): string { + let lineText = model.getLineContent(lineNumber); + let indentation = strings.getLeadingWhitespace(lineText); + if (indentation.length > column - 1) { + indentation = indentation.substring(0, column - 1); + } + + return indentation; + } + + private getScopedLineTokens(model: ITokenizedModel, lineNumber: number, columnNumber?: number) { + model.forceTokenization(lineNumber); + let lineTokens = model.getLineTokens(lineNumber); + let column = isNaN(columnNumber) ? model.getLineMaxColumn(lineNumber) - 1 : columnNumber - 1; + let scopedLineTokens = createScopedLineTokens(lineTokens, column); + return scopedLineTokens; + } + + // end onEnter + + public getBracketsSupport(languageId: LanguageId): RichEditBrackets { + let value = this._getRichEditSupport(languageId); + if (!value) { + return null; + } + return value.brackets || null; + } +} + +export const LanguageConfigurationRegistry = new LanguageConfigurationRegistryImpl(); diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts new file mode 100644 index 0000000000..8ae93ac566 --- /dev/null +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import Event, { Emitter } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IReadOnlyModel } from 'vs/editor/common/editorCommon'; +import { LanguageSelector, score } from 'vs/editor/common/modes/languageSelector'; + +interface Entry { + selector: LanguageSelector; + provider: T; + _score: number; + _time: number; +} + +export default class LanguageFeatureRegistry { + + private _clock: number = 0; + private _entries: Entry[] = []; + private _onDidChange: Emitter = new Emitter(); + + constructor() { + } + + get onDidChange(): Event { + return this._onDidChange.event; + } + + register(selector: LanguageSelector, provider: T): IDisposable { + + let entry: Entry = { + selector, + provider, + _score: -1, + _time: this._clock++ + }; + + this._entries.push(entry); + this._lastCandidate = undefined; + this._onDidChange.fire(this._entries.length); + + return { + dispose: () => { + if (entry) { + let idx = this._entries.indexOf(entry); + if (idx >= 0) { + this._entries.splice(idx, 1); + this._lastCandidate = undefined; + this._onDidChange.fire(this._entries.length); + entry = undefined; + } + } + } + }; + } + + has(model: IReadOnlyModel): boolean { + return this.all(model).length > 0; + } + + all(model: IReadOnlyModel): T[] { + if (!model || model.isTooLargeForHavingARichMode()) { + return []; + } + + this._updateScores(model); + const result: T[] = []; + + // from registry + for (let entry of this._entries) { + if (entry._score > 0) { + result.push(entry.provider); + } + } + + return result; + } + + ordered(model: IReadOnlyModel): T[] { + const result: T[] = []; + this._orderedForEach(model, entry => result.push(entry.provider)); + return result; + } + + orderedGroups(model: IReadOnlyModel): T[][] { + const result: T[][] = []; + let lastBucket: T[]; + let lastBucketScore: number; + + this._orderedForEach(model, entry => { + if (lastBucket && lastBucketScore === entry._score) { + lastBucket.push(entry.provider); + } else { + lastBucketScore = entry._score; + lastBucket = [entry.provider]; + result.push(lastBucket); + } + }); + + return result; + } + + private _orderedForEach(model: IReadOnlyModel, callback: (provider: Entry) => any): void { + + if (!model || model.isTooLargeForHavingARichMode()) { + return; + } + + this._updateScores(model); + + for (let from = 0; from < this._entries.length; from++) { + let entry = this._entries[from]; + if (entry._score > 0) { + callback(entry); + } + } + } + + private _lastCandidate: { uri: string; language: string; }; + + private _updateScores(model: IReadOnlyModel): void { + + let candidate = { + uri: model.uri.toString(), + language: model.getLanguageIdentifier().language + }; + + if (this._lastCandidate + && this._lastCandidate.language === candidate.language + && this._lastCandidate.uri === candidate.uri) { + + // nothing has changed + return; + } + + this._lastCandidate = candidate; + + for (let entry of this._entries) { + entry._score = score(entry.selector, model.uri, model.getLanguageIdentifier().language); + } + + // needs sorting + this._entries.sort(LanguageFeatureRegistry._compareByScoreAndTime); + } + + private static _compareByScoreAndTime(a: Entry, b: Entry): number { + if (a._score < b._score) { + return 1; + } else if (a._score > b._score) { + return -1; + } else if (a._time < b._time) { + return 1; + } else if (a._time > b._time) { + return -1; + } else { + return 0; + } + } +} diff --git a/src/vs/editor/common/modes/languageSelector.ts b/src/vs/editor/common/modes/languageSelector.ts new file mode 100644 index 0000000000..d2ff114657 --- /dev/null +++ b/src/vs/editor/common/modes/languageSelector.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import URI from 'vs/base/common/uri'; +import { match as matchGlobPattern } from 'vs/base/common/glob'; // TODO@Alex + +export interface LanguageFilter { + language?: string; + scheme?: string; + pattern?: string; +} + +export type LanguageSelector = string | LanguageFilter | (string | LanguageFilter)[]; + +export default function matches(selection: LanguageSelector, uri: URI, language: string): boolean { + return score(selection, uri, language) > 0; +} + +export function score(selector: LanguageSelector, candidateUri: URI, candidateLanguage: string): number { + + if (Array.isArray(selector)) { + // array -> take max individual value + let ret = 0; + for (const filter of selector) { + const value = score(filter, candidateUri, candidateLanguage); + if (value === 10) { + return value; // already at the highest + } + if (value > ret) { + ret = value; + } + } + return ret; + + } else if (typeof selector === 'string') { + // short-hand notion, desugars to + // 'fooLang' -> [{ language: 'fooLang', scheme: 'file' }, { language: 'fooLang', scheme: 'untitled' }] + // '*' -> { language: '*', scheme: '*' } + if (selector === '*') { + return 5; + } else if (selector === candidateLanguage) { + return 10; + } else { + return 0; + } + + } else if (selector) { + // filter -> select accordingly, use defaults for scheme + const { language, pattern, scheme } = selector; + + let ret = 0; + + if (scheme) { + if (scheme === candidateUri.scheme) { + ret = 10; + } else if (scheme === '*') { + ret = 5; + } else { + return 0; + } + } + + if (language) { + if (language === candidateLanguage) { + ret = 10; + } else if (language === '*') { + ret = Math.max(ret, 5); + } else { + return 0; + } + } + + if (pattern) { + if (pattern === candidateUri.fsPath || matchGlobPattern(pattern, candidateUri.fsPath)) { + ret = 10; + } else { + return 0; + } + } + + return ret; + + } else { + return 0; + } +} diff --git a/src/vs/editor/common/modes/linkComputer.ts b/src/vs/editor/common/modes/linkComputer.ts new file mode 100644 index 0000000000..e8b20b7fc7 --- /dev/null +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -0,0 +1,286 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ILink } from 'vs/editor/common/modes'; +import { CharCode } from 'vs/base/common/charCode'; +import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; +import { Uint8Matrix } from 'vs/editor/common/core/uint'; + +export interface ILinkComputerTarget { + getLineCount(): number; + getLineContent(lineNumber: number): string; +} + +const enum State { + Invalid = 0, + Start = 1, + H = 2, + HT = 3, + HTT = 4, + HTTP = 5, + F = 6, + FI = 7, + FIL = 8, + BeforeColon = 9, + AfterColon = 10, + AlmostThere = 11, + End = 12, + Accept = 13 +} + +type Edge = [State, number, State]; + +class StateMachine { + + private _states: Uint8Matrix; + private _maxCharCode: number; + + constructor(edges: Edge[]) { + let maxCharCode = 0; + let maxState = State.Invalid; + for (let i = 0, len = edges.length; i < len; i++) { + let [from, chCode, to] = edges[i]; + if (chCode > maxCharCode) { + maxCharCode = chCode; + } + if (from > maxState) { + maxState = from; + } + if (to > maxState) { + maxState = to; + } + } + + maxCharCode++; + maxState++; + + let states = new Uint8Matrix(maxState, maxCharCode, State.Invalid); + for (let i = 0, len = edges.length; i < len; i++) { + let [from, chCode, to] = edges[i]; + states.set(from, chCode, to); + } + + this._states = states; + this._maxCharCode = maxCharCode; + } + + public nextState(currentState: State, chCode: number): State { + if (chCode < 0 || chCode >= this._maxCharCode) { + return State.Invalid; + } + return this._states.get(currentState, chCode); + } +} + +// State machine for http:// or https:// or file:// +let _stateMachine: StateMachine = null; +function getStateMachine(): StateMachine { + if (_stateMachine === null) { + _stateMachine = new StateMachine([ + [State.Start, CharCode.h, State.H], + [State.Start, CharCode.H, State.H], + [State.Start, CharCode.f, State.F], + [State.Start, CharCode.F, State.F], + + [State.H, CharCode.t, State.HT], + [State.H, CharCode.T, State.HT], + + [State.HT, CharCode.t, State.HTT], + [State.HT, CharCode.T, State.HTT], + + [State.HTT, CharCode.p, State.HTTP], + [State.HTT, CharCode.P, State.HTTP], + + [State.HTTP, CharCode.s, State.BeforeColon], + [State.HTTP, CharCode.S, State.BeforeColon], + [State.HTTP, CharCode.Colon, State.AfterColon], + + [State.F, CharCode.i, State.FI], + [State.F, CharCode.I, State.FI], + + [State.FI, CharCode.l, State.FIL], + [State.FI, CharCode.L, State.FIL], + + [State.FIL, CharCode.e, State.BeforeColon], + [State.FIL, CharCode.E, State.BeforeColon], + + [State.BeforeColon, CharCode.Colon, State.AfterColon], + + [State.AfterColon, CharCode.Slash, State.AlmostThere], + + [State.AlmostThere, CharCode.Slash, State.End], + ]); + } + return _stateMachine; +} + + +const enum CharacterClass { + None = 0, + ForceTermination = 1, + CannotEndIn = 2 +} + +let _classifier: CharacterClassifier = null; +function getClassifier(): CharacterClassifier { + if (_classifier === null) { + _classifier = new CharacterClassifier(CharacterClass.None); + + const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…'; + for (let i = 0; i < FORCE_TERMINATION_CHARACTERS.length; i++) { + _classifier.set(FORCE_TERMINATION_CHARACTERS.charCodeAt(i), CharacterClass.ForceTermination); + } + + const CANNOT_END_WITH_CHARACTERS = '.,;'; + for (let i = 0; i < CANNOT_END_WITH_CHARACTERS.length; i++) { + _classifier.set(CANNOT_END_WITH_CHARACTERS.charCodeAt(i), CharacterClass.CannotEndIn); + } + } + return _classifier; +} + +class LinkComputer { + + private static _createLink(classifier: CharacterClassifier, line: string, lineNumber: number, linkBeginIndex: number, linkEndIndex: number): ILink { + // Do not allow to end link in certain characters... + let lastIncludedCharIndex = linkEndIndex - 1; + do { + const chCode = line.charCodeAt(lastIncludedCharIndex); + const chClass = classifier.get(chCode); + if (chClass !== CharacterClass.CannotEndIn) { + break; + } + lastIncludedCharIndex--; + } while (lastIncludedCharIndex > linkBeginIndex); + + return { + range: { + startLineNumber: lineNumber, + startColumn: linkBeginIndex + 1, + endLineNumber: lineNumber, + endColumn: lastIncludedCharIndex + 2 + }, + url: line.substring(linkBeginIndex, lastIncludedCharIndex + 1) + }; + } + + public static computeLinks(model: ILinkComputerTarget): ILink[] { + const stateMachine = getStateMachine(); + const classifier = getClassifier(); + + let result: ILink[] = []; + for (let i = 1, lineCount = model.getLineCount(); i <= lineCount; i++) { + const line = model.getLineContent(i); + const len = line.length; + + let j = 0; + let linkBeginIndex = 0; + let linkBeginChCode = 0; + let state = State.Start; + let hasOpenParens = false; + let hasOpenSquareBracket = false; + let hasOpenCurlyBracket = false; + + while (j < len) { + + let resetStateMachine = false; + const chCode = line.charCodeAt(j); + + if (state === State.Accept) { + let chClass: CharacterClass; + switch (chCode) { + case CharCode.OpenParen: + hasOpenParens = true; + chClass = CharacterClass.None; + break; + case CharCode.CloseParen: + chClass = (hasOpenParens ? CharacterClass.None : CharacterClass.ForceTermination); + break; + case CharCode.OpenSquareBracket: + hasOpenSquareBracket = true; + chClass = CharacterClass.None; + break; + case CharCode.CloseSquareBracket: + chClass = (hasOpenSquareBracket ? CharacterClass.None : CharacterClass.ForceTermination); + break; + case CharCode.OpenCurlyBrace: + hasOpenCurlyBracket = true; + chClass = CharacterClass.None; + break; + case CharCode.CloseCurlyBrace: + chClass = (hasOpenCurlyBracket ? CharacterClass.None : CharacterClass.ForceTermination); + break; + /* The following three rules make it that ' or " or ` are allowed inside links if the link began with a different one */ + case CharCode.SingleQuote: + chClass = (linkBeginChCode === CharCode.DoubleQuote || linkBeginChCode === CharCode.BackTick) ? CharacterClass.None : CharacterClass.ForceTermination; + break; + case CharCode.DoubleQuote: + chClass = (linkBeginChCode === CharCode.SingleQuote || linkBeginChCode === CharCode.BackTick) ? CharacterClass.None : CharacterClass.ForceTermination; + break; + case CharCode.BackTick: + chClass = (linkBeginChCode === CharCode.SingleQuote || linkBeginChCode === CharCode.DoubleQuote) ? CharacterClass.None : CharacterClass.ForceTermination; + break; + default: + chClass = classifier.get(chCode); + } + + // Check if character terminates link + if (chClass === CharacterClass.ForceTermination) { + result.push(LinkComputer._createLink(classifier, line, i, linkBeginIndex, j)); + resetStateMachine = true; + } + } else if (state === State.End) { + const chClass = classifier.get(chCode); + + // Check if character terminates link + if (chClass === CharacterClass.ForceTermination) { + resetStateMachine = true; + } else { + state = State.Accept; + } + } else { + state = stateMachine.nextState(state, chCode); + if (state === State.Invalid) { + resetStateMachine = true; + } + } + + if (resetStateMachine) { + state = State.Start; + hasOpenParens = false; + hasOpenSquareBracket = false; + hasOpenCurlyBracket = false; + + // Record where the link started + linkBeginIndex = j + 1; + linkBeginChCode = chCode; + } + + j++; + } + + if (state === State.Accept) { + result.push(LinkComputer._createLink(classifier, line, i, linkBeginIndex, len)); + } + + } + + return result; + } +} + +/** + * Returns an array of all links contains in the provided + * document. *Note* that this operation is computational + * expensive and should not run in the UI thread. + */ +export function computeLinks(model: ILinkComputerTarget): ILink[] { + if (!model || typeof model.getLineCount !== 'function' || typeof model.getLineContent !== 'function') { + // Unknown caller! + return []; + } + return LinkComputer.computeLinks(model); +} diff --git a/src/vs/editor/common/modes/modesRegistry.ts b/src/vs/editor/common/modes/modesRegistry.ts new file mode 100644 index 0000000000..b606d576da --- /dev/null +++ b/src/vs/editor/common/modes/modesRegistry.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { LanguageIdentifier, LanguageId } from 'vs/editor/common/modes'; + +// Define extension point ids +export var Extensions = { + ModesRegistry: 'editor.modesRegistry' +}; + +export class EditorModesRegistry { + + private _languages: ILanguageExtensionPoint[]; + + private _onDidAddLanguages: Emitter = new Emitter(); + public onDidAddLanguages: Event = this._onDidAddLanguages.event; + + constructor() { + this._languages = []; + } + + // --- languages + + public registerLanguage(def: ILanguageExtensionPoint): void { + this._languages.push(def); + this._onDidAddLanguages.fire([def]); + } + public registerLanguages(def: ILanguageExtensionPoint[]): void { + this._languages = this._languages.concat(def); + this._onDidAddLanguages.fire(def); + } + public getLanguages(): ILanguageExtensionPoint[] { + return this._languages.slice(0); + } +} + +export var ModesRegistry = new EditorModesRegistry(); +Registry.add(Extensions.ModesRegistry, ModesRegistry); + +export const PLAINTEXT_MODE_ID = 'plaintext'; +export const PLAINTEXT_LANGUAGE_IDENTIFIER = new LanguageIdentifier(PLAINTEXT_MODE_ID, LanguageId.PlainText); + +ModesRegistry.registerLanguage({ + id: PLAINTEXT_MODE_ID, + extensions: ['.txt', '.gitignore'], + aliases: [nls.localize('plainText.alias', "Plain Text"), 'text'], + mimetypes: ['text/plain'] +}); +LanguageConfigurationRegistry.register(PLAINTEXT_LANGUAGE_IDENTIFIER, { + brackets: [ + ['(', ')'], + ['[', ']'], + ['{', '}'], + ] +}); diff --git a/src/vs/editor/common/modes/nullMode.ts b/src/vs/editor/common/modes/nullMode.ts new file mode 100644 index 0000000000..8f7033d754 --- /dev/null +++ b/src/vs/editor/common/modes/nullMode.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IState, ColorId, MetadataConsts, LanguageIdentifier, FontStyle, StandardTokenType, LanguageId } from 'vs/editor/common/modes'; +import { Token, TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; + +class NullStateImpl implements IState { + + public clone(): IState { + return this; + } + + public equals(other: IState): boolean { + return (this === other); + } +} + +export const NULL_STATE: IState = new NullStateImpl(); + +export const NULL_MODE_ID = 'vs.editor.nullMode'; + +export const NULL_LANGUAGE_IDENTIFIER = new LanguageIdentifier(NULL_MODE_ID, LanguageId.Null); + +export function nullTokenize(modeId: string, buffer: string, state: IState, deltaOffset: number): TokenizationResult { + return new TokenizationResult([new Token(deltaOffset, '', modeId)], state); +} + +export function nullTokenize2(languageId: LanguageId, buffer: string, state: IState, deltaOffset: number): TokenizationResult2 { + let tokens = new Uint32Array(2); + tokens[0] = deltaOffset; + tokens[1] = ( + (languageId << MetadataConsts.LANGUAGEID_OFFSET) + | (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET) + | (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) + | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) + | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) + ) >>> 0; + + return new TokenizationResult2(tokens, state); +} diff --git a/src/vs/editor/common/modes/supports.ts b/src/vs/editor/common/modes/supports.ts new file mode 100644 index 0000000000..74004d96e4 --- /dev/null +++ b/src/vs/editor/common/modes/supports.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as modes from 'vs/editor/common/modes'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; + +export function createScopedLineTokens(context: LineTokens, offset: number): ScopedLineTokens { + let tokenCount = context.getTokenCount(); + let tokenIndex = context.findTokenIndexAtOffset(offset); + let desiredLanguageId = context.getLanguageId(tokenIndex); + + let lastTokenIndex = tokenIndex; + while (lastTokenIndex + 1 < tokenCount && context.getLanguageId(lastTokenIndex + 1) === desiredLanguageId) { + lastTokenIndex++; + } + + let firstTokenIndex = tokenIndex; + while (firstTokenIndex > 0 && context.getLanguageId(firstTokenIndex - 1) === desiredLanguageId) { + firstTokenIndex--; + } + + return new ScopedLineTokens( + context, + desiredLanguageId, + firstTokenIndex, + lastTokenIndex + 1, + context.getTokenStartOffset(firstTokenIndex), + context.getTokenEndOffset(lastTokenIndex) + ); +} + +export class ScopedLineTokens { + _scopedLineTokensBrand: void; + + public readonly languageId: modes.LanguageId; + private readonly _actual: LineTokens; + private readonly _firstTokenIndex: number; + private readonly _lastTokenIndex: number; + public readonly firstCharOffset: number; + private readonly _lastCharOffset: number; + + constructor( + actual: LineTokens, + languageId: modes.LanguageId, + firstTokenIndex: number, + lastTokenIndex: number, + firstCharOffset: number, + lastCharOffset: number + ) { + this._actual = actual; + this.languageId = languageId; + this._firstTokenIndex = firstTokenIndex; + this._lastTokenIndex = lastTokenIndex; + this.firstCharOffset = firstCharOffset; + this._lastCharOffset = lastCharOffset; + } + + public getLineContent(): string { + var actualLineContent = this._actual.getLineContent(); + return actualLineContent.substring(this.firstCharOffset, this._lastCharOffset); + } + + public getTokenCount(): number { + return this._lastTokenIndex - this._firstTokenIndex; + } + + public findTokenIndexAtOffset(offset: number): number { + return this._actual.findTokenIndexAtOffset(offset + this.firstCharOffset) - this._firstTokenIndex; + } + + public getTokenStartOffset(tokenIndex: number): number { + return this._actual.getTokenStartOffset(tokenIndex + this._firstTokenIndex) - this.firstCharOffset; + } + + public getStandardTokenType(tokenIndex: number): modes.StandardTokenType { + return this._actual.getStandardTokenType(tokenIndex + this._firstTokenIndex); + } +} + +const enum IgnoreBracketsInTokens { + value = modes.StandardTokenType.Comment | modes.StandardTokenType.String | modes.StandardTokenType.RegEx +} + +export function ignoreBracketsInToken(standardTokenType: modes.StandardTokenType): boolean { + return (standardTokenType & IgnoreBracketsInTokens.value) !== 0; +} diff --git a/src/vs/editor/common/modes/supports/characterPair.ts b/src/vs/editor/common/modes/supports/characterPair.ts new file mode 100644 index 0000000000..9ac5d5949a --- /dev/null +++ b/src/vs/editor/common/modes/supports/characterPair.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ScopedLineTokens } from 'vs/editor/common/modes/supports'; +import { CharacterPair, IAutoClosingPair, IAutoClosingPairConditional, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; + +export class CharacterPairSupport { + + private readonly _autoClosingPairs: StandardAutoClosingPairConditional[]; + private readonly _surroundingPairs: IAutoClosingPair[]; + + constructor(config: { brackets?: CharacterPair[]; autoClosingPairs?: IAutoClosingPairConditional[], surroundingPairs?: IAutoClosingPair[] }) { + if (config.autoClosingPairs) { + this._autoClosingPairs = config.autoClosingPairs.map(el => new StandardAutoClosingPairConditional(el)); + } else if (config.brackets) { + this._autoClosingPairs = config.brackets.map(b => new StandardAutoClosingPairConditional({ open: b[0], close: b[1] })); + } else { + this._autoClosingPairs = []; + } + + this._surroundingPairs = config.surroundingPairs || this._autoClosingPairs; + } + + public getAutoClosingPairs(): IAutoClosingPair[] { + return this._autoClosingPairs; + } + + public shouldAutoClosePair(character: string, context: ScopedLineTokens, column: number): boolean { + // Always complete on empty line + if (context.getTokenCount() === 0) { + return true; + } + + let tokenIndex = context.findTokenIndexAtOffset(column - 2); + let standardTokenType = context.getStandardTokenType(tokenIndex); + + for (let i = 0; i < this._autoClosingPairs.length; ++i) { + let autoClosingPair = this._autoClosingPairs[i]; + + if (autoClosingPair.open === character) { + return autoClosingPair.isOK(standardTokenType); + } + } + + return true; + } + + public getSurroundingPairs(): IAutoClosingPair[] { + return this._surroundingPairs; + } +} diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts new file mode 100644 index 0000000000..0751f06653 --- /dev/null +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ScopedLineTokens, ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; +import { BracketsUtils, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; +import { IAutoClosingPairConditional, IBracketElectricCharacterContribution, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; + +/** + * Interface used to support electric characters + * @internal + */ +export interface IElectricAction { + // Only one of the following properties should be defined: + + // The line will be indented at the same level of the line + // which contains the matching given bracket type. + matchOpenBracket?: string; + + // The text will be appended after the electric character. + appendText?: string; +} + +export class BracketElectricCharacterSupport { + + private readonly _richEditBrackets: RichEditBrackets; + private readonly _complexAutoClosePairs: StandardAutoClosingPairConditional[]; + + constructor(richEditBrackets: RichEditBrackets, autoClosePairs: IAutoClosingPairConditional[], contribution: IBracketElectricCharacterContribution) { + contribution = contribution || {}; + this._richEditBrackets = richEditBrackets; + this._complexAutoClosePairs = autoClosePairs.filter(pair => pair.open.length > 1 && !!pair.close).map(el => new StandardAutoClosingPairConditional(el)); + if (contribution.docComment) { + // IDocComment is legacy, only partially supported + this._complexAutoClosePairs.push(new StandardAutoClosingPairConditional({ open: contribution.docComment.open, close: contribution.docComment.close })); + } + } + + public getElectricCharacters(): string[] { + var result: string[] = []; + + if (this._richEditBrackets) { + for (let i = 0, len = this._richEditBrackets.brackets.length; i < len; i++) { + let bracketPair = this._richEditBrackets.brackets[i]; + let lastChar = bracketPair.close.charAt(bracketPair.close.length - 1); + result.push(lastChar); + } + } + + // auto close + for (let pair of this._complexAutoClosePairs) { + result.push(pair.open.charAt(pair.open.length - 1)); + } + + // Filter duplicate entries + result = result.filter((item, pos, array) => { + return array.indexOf(item) === pos; + }); + + return result; + } + + public onElectricCharacter(character: string, context: ScopedLineTokens, column: number): IElectricAction { + return (this._onElectricAutoClose(character, context, column) || + this._onElectricAutoIndent(character, context, column)); + } + + private _onElectricAutoIndent(character: string, context: ScopedLineTokens, column: number): IElectricAction { + + if (!this._richEditBrackets || this._richEditBrackets.brackets.length === 0) { + return null; + } + + let tokenIndex = context.findTokenIndexAtOffset(column - 1); + if (ignoreBracketsInToken(context.getStandardTokenType(tokenIndex))) { + return null; + } + + let reversedBracketRegex = this._richEditBrackets.reversedRegex; + let text = context.getLineContent().substring(0, column - 1) + character; + + let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, 1, text, 0, text.length); + if (!r) { + return null; + } + + let bracketText = text.substring(r.startColumn - 1, r.endColumn - 1); + bracketText = bracketText.toLowerCase(); + + let isOpen = this._richEditBrackets.textIsOpenBracket[bracketText]; + if (isOpen) { + return null; + } + + let textBeforeBracket = text.substring(0, r.startColumn - 1); + if (!/^\s*$/.test(textBeforeBracket)) { + // There is other text on the line before the bracket + return null; + } + + return { + matchOpenBracket: bracketText + }; + } + + private _onElectricAutoClose(character: string, context: ScopedLineTokens, column: number): IElectricAction { + if (!this._complexAutoClosePairs.length) { + return null; + } + + let line = context.getLineContent(); + + for (let i = 0, len = this._complexAutoClosePairs.length; i < len; i++) { + let pair = this._complexAutoClosePairs[i]; + + // See if the right electric character was pressed + if (character !== pair.open.charAt(pair.open.length - 1)) { + continue; + } + + // check if the full open bracket matches + let actual = line.substring(line.length - pair.open.length + 1) + character; + if (actual !== pair.open) { + continue; + } + + let lastTokenIndex = context.findTokenIndexAtOffset(column - 1); + let lastTokenStandardType = context.getStandardTokenType(lastTokenIndex); + // If we're in a scope listed in 'notIn', do nothing + if (!pair.isOK(lastTokenStandardType)) { + continue; + } + + // If this line already contains the closing tag, do nothing. + if (line.indexOf(pair.close, column - 1) >= 0) { + continue; + } + + return { appendText: pair.close }; + } + + return null; + } +} diff --git a/src/vs/editor/common/modes/supports/indentRules.ts b/src/vs/editor/common/modes/supports/indentRules.ts new file mode 100644 index 0000000000..3870f8215d --- /dev/null +++ b/src/vs/editor/common/modes/supports/indentRules.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { IndentationRule, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; + +export const enum IndentConsts { + INCREASE_MASK = 0b00000001, + DECREASE_MASK = 0b00000010, + INDENT_NEXTLINE_MASK = 0b00000100, + UNINDENT_MASK = 0b00001000, +}; + +export class IndentRulesSupport { + + private readonly _indentationRules: IndentationRule; + + constructor(indentationRules: IndentationRule) { + this._indentationRules = indentationRules; + } + + public onType(text: string): IndentAction { + if (this._indentationRules) { + if (this._indentationRules.unIndentedLinePattern && this._indentationRules.unIndentedLinePattern.test(text)) { + return null; + } + + if (this._indentationRules.decreaseIndentPattern && this._indentationRules.decreaseIndentPattern.test(text)) { + return IndentAction.Outdent; + } + } + return null; + } + + public containNonWhitespace(text: string): boolean { + // the text doesn't contain any non-whitespace character. + let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(text); + + if (nonWhitespaceIdx >= 0) { + return true; + } + + return false; + } + + public shouldIncrease(text: string): boolean { + if (this._indentationRules) { + if (this._indentationRules.increaseIndentPattern && this._indentationRules.increaseIndentPattern.test(text)) { + return true; + } + // if (this._indentationRules.indentNextLinePattern && this._indentationRules.indentNextLinePattern.test(text)) { + // return true; + // } + } + return false; + } + + public shouldDecrease(text: string): boolean { + if (this._indentationRules && this._indentationRules.decreaseIndentPattern && this._indentationRules.decreaseIndentPattern.test(text)) { + return true; + } + return false; + } + + public shouldIndentNextLine(text: string): boolean { + if (this._indentationRules && this._indentationRules.indentNextLinePattern && this._indentationRules.indentNextLinePattern.test(text)) { + return true; + } + + return false; + } + + public shouldIgnore(text: string): boolean { + // the text matches `unIndentedLinePattern` + if (this._indentationRules && this._indentationRules.unIndentedLinePattern && this._indentationRules.unIndentedLinePattern.test(text)) { + return true; + } + + return false; + } + + public getIndentMetadata(text: string): number { + let ret = 0; + if (this.shouldIncrease(text)) { + ret += IndentConsts.INCREASE_MASK; + } + if (this.shouldDecrease(text)) { + ret += IndentConsts.DECREASE_MASK; + } + if (this.shouldIndentNextLine(text)) { + ret += IndentConsts.INDENT_NEXTLINE_MASK; + } + if (this.shouldIgnore(text)) { + ret += IndentConsts.UNINDENT_MASK; + } + return ret; + } +} + diff --git a/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts b/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts new file mode 100644 index 0000000000..b082c8f5e8 --- /dev/null +++ b/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IInplaceReplaceSupportResult } from 'vs/editor/common/modes'; +import { IRange } from 'vs/editor/common/core/range'; + +export class BasicInplaceReplace { + + public static INSTANCE = new BasicInplaceReplace(); + + public navigateValueSet(range1: IRange, text1: string, range2: IRange, text2: string, up: boolean): IInplaceReplaceSupportResult { + + if (range1 && text1) { + let result = this.doNavigateValueSet(text1, up); + if (result) { + return { + range: range1, + value: result + }; + } + } + + if (range2 && text2) { + let result = this.doNavigateValueSet(text2, up); + if (result) { + return { + range: range2, + value: result + }; + } + } + + return null; + } + + private doNavigateValueSet(text: string, up: boolean): string { + let numberResult = this.numberReplace(text, up); + if (numberResult !== null) { + return numberResult; + } + return this.textReplace(text, up); + } + + private numberReplace(value: string, up: boolean): string { + var precision = Math.pow(10, value.length - (value.lastIndexOf('.') + 1)), + n1 = Number(value), + n2 = parseFloat(value); + + if (!isNaN(n1) && !isNaN(n2) && n1 === n2) { + + if (n1 === 0 && !up) { + return null; // don't do negative + // } else if(n1 === 9 && up) { + // return null; // don't insert 10 into a number + } else { + n1 = Math.floor(n1 * precision); + n1 += up ? precision : -precision; + return String(n1 / precision); + } + } + + return null; + } + + private _defaultValueSet: string[][] = [ + ['true', 'false'], + ['True', 'False'], + ['Private', 'Public', 'Friend', 'ReadOnly', 'Partial', 'Protected', 'WriteOnly'], + ['public', 'protected', 'private'], + ]; + + private textReplace(value: string, up: boolean): string { + return this.valueSetsReplace(this._defaultValueSet, value, up); + } + + private valueSetsReplace(valueSets: string[][], value: string, up: boolean): string { + var result: string = null; + for (let i = 0, len = valueSets.length; result === null && i < len; i++) { + result = this.valueSetReplace(valueSets[i], value, up); + } + return result; + } + + private valueSetReplace(valueSet: string[], value: string, up: boolean): string { + var idx = valueSet.indexOf(value); + if (idx >= 0) { + idx += up ? +1 : -1; + if (idx < 0) { + idx = valueSet.length - 1; + } else { + idx %= valueSet.length; + } + return valueSet[idx]; + } + return null; + } +} diff --git a/src/vs/editor/common/modes/supports/onEnter.ts b/src/vs/editor/common/modes/supports/onEnter.ts new file mode 100644 index 0000000000..3a3f1d8779 --- /dev/null +++ b/src/vs/editor/common/modes/supports/onEnter.ts @@ -0,0 +1,116 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import * as strings from 'vs/base/common/strings'; +import { CharacterPair, IndentationRule, IndentAction, EnterAction, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; + +export interface IOnEnterSupportOptions { + brackets?: CharacterPair[]; + indentationRules?: IndentationRule; + regExpRules?: OnEnterRule[]; +} + +interface IProcessedBracketPair { + open: string; + close: string; + openRegExp: RegExp; + closeRegExp: RegExp; +} + +export class OnEnterSupport { + + private readonly _brackets: IProcessedBracketPair[]; + private readonly _indentationRules: IndentationRule; + private readonly _regExpRules: OnEnterRule[]; + + constructor(opts?: IOnEnterSupportOptions) { + opts = opts || {}; + opts.brackets = opts.brackets || [ + ['(', ')'], + ['{', '}'], + ['[', ']'] + ]; + + this._brackets = opts.brackets.map((bracket) => { + return { + open: bracket[0], + openRegExp: OnEnterSupport._createOpenBracketRegExp(bracket[0]), + close: bracket[1], + closeRegExp: OnEnterSupport._createCloseBracketRegExp(bracket[1]), + }; + }); + this._regExpRules = opts.regExpRules || []; + this._indentationRules = opts.indentationRules; + } + + public onEnter(oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction { + // (1): `regExpRules` + for (let i = 0, len = this._regExpRules.length; i < len; i++) { + let rule = this._regExpRules[i]; + if (rule.beforeText.test(beforeEnterText)) { + if (rule.afterText) { + if (rule.afterText.test(afterEnterText)) { + return rule.action; + } + } else { + return rule.action; + } + } + } + + // (2): Special indent-outdent + if (beforeEnterText.length > 0 && afterEnterText.length > 0) { + for (let i = 0, len = this._brackets.length; i < len; i++) { + let bracket = this._brackets[i]; + if (bracket.openRegExp.test(beforeEnterText) && bracket.closeRegExp.test(afterEnterText)) { + return { indentAction: IndentAction.IndentOutdent }; + } + } + } + + + // (4): Open bracket based logic + if (beforeEnterText.length > 0) { + for (let i = 0, len = this._brackets.length; i < len; i++) { + let bracket = this._brackets[i]; + if (bracket.openRegExp.test(beforeEnterText)) { + return { indentAction: IndentAction.Indent }; + } + } + } + + return null; + } + + private static _createOpenBracketRegExp(bracket: string): RegExp { + var str = strings.escapeRegExpCharacters(bracket); + if (!/\B/.test(str.charAt(0))) { + str = '\\b' + str; + } + str += '\\s*$'; + return OnEnterSupport._safeRegExp(str); + } + + private static _createCloseBracketRegExp(bracket: string): RegExp { + var str = strings.escapeRegExpCharacters(bracket); + if (!/\B/.test(str.charAt(str.length - 1))) { + str = str + '\\b'; + } + str = '^\\s*' + str; + return OnEnterSupport._safeRegExp(str); + } + + private static _safeRegExp(def: string): RegExp { + try { + return new RegExp(def); + } catch (err) { + onUnexpectedError(err); + return null; + } + } +} + diff --git a/src/vs/editor/common/modes/supports/richEditBrackets.ts b/src/vs/editor/common/modes/supports/richEditBrackets.ts new file mode 100644 index 0000000000..889ea6f871 --- /dev/null +++ b/src/vs/editor/common/modes/supports/richEditBrackets.ts @@ -0,0 +1,193 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { Range } from 'vs/editor/common/core/range'; +import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; + +interface ISimpleInternalBracket { + open: string; + close: string; +} + +export class RichEditBracket { + _richEditBracketBrand: void; + + readonly languageIdentifier: LanguageIdentifier; + readonly open: string; + readonly close: string; + readonly forwardRegex: RegExp; + readonly reversedRegex: RegExp; + + constructor(languageIdentifier: LanguageIdentifier, open: string, close: string, forwardRegex: RegExp, reversedRegex: RegExp) { + this.languageIdentifier = languageIdentifier; + this.open = open; + this.close = close; + this.forwardRegex = forwardRegex; + this.reversedRegex = reversedRegex; + } +} + +export class RichEditBrackets { + _richEditBracketsBrand: void; + + public readonly brackets: RichEditBracket[]; + public readonly forwardRegex: RegExp; + public readonly reversedRegex: RegExp; + public readonly maxBracketLength: number; + public readonly textIsBracket: { [text: string]: RichEditBracket; }; + public readonly textIsOpenBracket: { [text: string]: boolean; }; + + constructor(languageIdentifier: LanguageIdentifier, brackets: CharacterPair[]) { + this.brackets = brackets.map((b) => { + return new RichEditBracket( + languageIdentifier, + b[0], + b[1], + getRegexForBracketPair({ open: b[0], close: b[1] }), + getReversedRegexForBracketPair({ open: b[0], close: b[1] }) + ); + }); + this.forwardRegex = getRegexForBrackets(this.brackets); + this.reversedRegex = getReversedRegexForBrackets(this.brackets); + + this.textIsBracket = {}; + this.textIsOpenBracket = {}; + + let maxBracketLength = 0; + this.brackets.forEach((b) => { + this.textIsBracket[b.open.toLowerCase()] = b; + this.textIsBracket[b.close.toLowerCase()] = b; + this.textIsOpenBracket[b.open.toLowerCase()] = true; + this.textIsOpenBracket[b.close.toLowerCase()] = false; + maxBracketLength = Math.max(maxBracketLength, b.open.length); + maxBracketLength = Math.max(maxBracketLength, b.close.length); + }); + this.maxBracketLength = maxBracketLength; + } +} + +function once(keyFn: (input: T) => string, computeFn: (input: T) => R): (input: T) => R { + let cache: { [key: string]: R; } = {}; + return (input: T): R => { + let key = keyFn(input); + if (!cache.hasOwnProperty(key)) { + cache[key] = computeFn(input); + } + return cache[key]; + }; +} + +var getRegexForBracketPair = once( + (input) => `${input.open};${input.close}`, + (input) => { + return createOrRegex([input.open, input.close]); + } +); + +var getReversedRegexForBracketPair = once( + (input) => `${input.open};${input.close}`, + (input) => { + return createOrRegex([toReversedString(input.open), toReversedString(input.close)]); + } +); + +var getRegexForBrackets = once( + (input) => input.map(b => `${b.open};${b.close}`).join(';'), + (input) => { + let pieces: string[] = []; + input.forEach((b) => { + pieces.push(b.open); + pieces.push(b.close); + }); + return createOrRegex(pieces); + } +); + +var getReversedRegexForBrackets = once( + (input) => input.map(b => `${b.open};${b.close}`).join(';'), + (input) => { + let pieces: string[] = []; + input.forEach((b) => { + pieces.push(toReversedString(b.open)); + pieces.push(toReversedString(b.close)); + }); + return createOrRegex(pieces); + } +); + +function createOrRegex(pieces: string[]): RegExp { + let regexStr = `(${pieces.map(strings.escapeRegExpCharacters).join(')|(')})`; + return strings.createRegExp(regexStr, true); +} + +let toReversedString = (function () { + + function reverse(str: string): string { + let reversedStr = ''; + for (let i = str.length - 1; i >= 0; i--) { + reversedStr += str.charAt(i); + } + return reversedStr; + } + + let lastInput: string = null; + let lastOutput: string = null; + return function toReversedString(str: string): string { + if (lastInput !== str) { + lastInput = str; + lastOutput = reverse(lastInput); + } + return lastOutput; + }; +})(); + +export class BracketsUtils { + + private static _findPrevBracketInText(reversedBracketRegex: RegExp, lineNumber: number, reversedText: string, offset: number): Range { + let m = reversedText.match(reversedBracketRegex); + + if (!m) { + return null; + } + + let matchOffset = reversedText.length - m.index; + let matchLength = m[0].length; + let absoluteMatchOffset = offset + matchOffset; + + return new Range(lineNumber, absoluteMatchOffset - matchLength + 1, lineNumber, absoluteMatchOffset + 1); + } + + public static findPrevBracketInToken(reversedBracketRegex: RegExp, lineNumber: number, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range { + // Because JS does not support backwards regex search, we search forwards in a reversed string with a reversed regex ;) + let reversedLineText = toReversedString(lineText); + let reversedTokenText = reversedLineText.substring(lineText.length - currentTokenEnd, lineText.length - currentTokenStart); + + return this._findPrevBracketInText(reversedBracketRegex, lineNumber, reversedTokenText, currentTokenStart); + } + + public static findNextBracketInText(bracketRegex: RegExp, lineNumber: number, text: string, offset: number): Range { + let m = text.match(bracketRegex); + + if (!m) { + return null; + } + + let matchOffset = m.index; + let matchLength = m[0].length; + let absoluteMatchOffset = offset + matchOffset; + + return new Range(lineNumber, absoluteMatchOffset + 1, lineNumber, absoluteMatchOffset + 1 + matchLength); + } + + public static findNextBracketInToken(bracketRegex: RegExp, lineNumber: number, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range { + let currentTokenText = lineText.substring(currentTokenStart, currentTokenEnd); + + return this.findNextBracketInText(bracketRegex, lineNumber, currentTokenText, currentTokenStart); + } + +} diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts new file mode 100644 index 0000000000..8d7a68311a --- /dev/null +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -0,0 +1,404 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ColorId, FontStyle, MetadataConsts, LanguageId, StandardTokenType } from 'vs/editor/common/modes'; +import { Color } from 'vs/base/common/color'; + +export interface ITokenThemeRule { + token: string; + foreground?: string; + background?: string; + fontStyle?: string; +} + +export class ParsedTokenThemeRule { + _parsedThemeRuleBrand: void; + + readonly token: string; + readonly index: number; + + /** + * -1 if not set. An or mask of `FontStyle` otherwise. + */ + readonly fontStyle: FontStyle; + readonly foreground: string; + readonly background: string; + + constructor( + token: string, + index: number, + fontStyle: number, + foreground: string, + background: string, + ) { + this.token = token; + this.index = index; + this.fontStyle = fontStyle; + this.foreground = foreground; + this.background = background; + } +} + +/** + * Parse a raw theme into rules. + */ +export function parseTokenTheme(source: ITokenThemeRule[]): ParsedTokenThemeRule[] { + if (!source || !Array.isArray(source)) { + return []; + } + let result: ParsedTokenThemeRule[] = [], resultLen = 0; + for (let i = 0, len = source.length; i < len; i++) { + let entry = source[i]; + + let fontStyle: number = FontStyle.NotSet; + if (typeof entry.fontStyle === 'string') { + fontStyle = FontStyle.None; + + let segments = entry.fontStyle.split(' '); + for (let j = 0, lenJ = segments.length; j < lenJ; j++) { + let segment = segments[j]; + switch (segment) { + case 'italic': + fontStyle = fontStyle | FontStyle.Italic; + break; + case 'bold': + fontStyle = fontStyle | FontStyle.Bold; + break; + case 'underline': + fontStyle = fontStyle | FontStyle.Underline; + break; + } + } + } + + let foreground: string = null; + if (typeof entry.foreground === 'string') { + foreground = entry.foreground; + } + + let background: string = null; + if (typeof entry.background === 'string') { + background = entry.background; + } + + result[resultLen++] = new ParsedTokenThemeRule( + entry.token || '', + i, + fontStyle, + foreground, + background + ); + } + + return result; +} + +/** + * Resolve rules (i.e. inheritance). + */ +function resolveParsedTokenThemeRules(parsedThemeRules: ParsedTokenThemeRule[]): TokenTheme { + + // Sort rules lexicographically, and then by index if necessary + parsedThemeRules.sort((a, b) => { + let r = strcmp(a.token, b.token); + if (r !== 0) { + return r; + } + return a.index - b.index; + }); + + // Determine defaults + let defaultFontStyle = FontStyle.None; + let defaultForeground = '000000'; + let defaultBackground = 'ffffff'; + while (parsedThemeRules.length >= 1 && parsedThemeRules[0].token === '') { + let incomingDefaults = parsedThemeRules.shift(); + if (incomingDefaults.fontStyle !== FontStyle.NotSet) { + defaultFontStyle = incomingDefaults.fontStyle; + } + if (incomingDefaults.foreground !== null) { + defaultForeground = incomingDefaults.foreground; + } + if (incomingDefaults.background !== null) { + defaultBackground = incomingDefaults.background; + } + } + let colorMap = new ColorMap(); + // ensure default foreground gets id 1 and default background gets id 2 + let defaults = new ThemeTrieElementRule(defaultFontStyle, colorMap.getId(defaultForeground), colorMap.getId(defaultBackground)); + + let root = new ThemeTrieElement(defaults); + for (let i = 0, len = parsedThemeRules.length; i < len; i++) { + let rule = parsedThemeRules[i]; + root.insert(rule.token, rule.fontStyle, colorMap.getId(rule.foreground), colorMap.getId(rule.background)); + } + + return new TokenTheme(colorMap, root); +} + +export class ColorMap { + + private _lastColorId: number; + private _id2color: Color[]; + private _color2id: Map; + + constructor() { + this._lastColorId = 0; + this._id2color = []; + this._color2id = new Map(); + } + + public getId(color: string): ColorId { + if (color === null) { + return 0; + } + color = color.toUpperCase(); + if (!/^[0-9A-F]{6}$/.test(color)) { + throw new Error('Illegal color name: ' + color); + } + let value = this._color2id.get(color); + if (value) { + return value; + } + value = ++this._lastColorId; + this._color2id.set(color, value); + this._id2color[value] = Color.fromHex('#' + color); + return value; + } + + public getColorMap(): Color[] { + return this._id2color.slice(0); + } + +} + +export class TokenTheme { + + public static createFromRawTokenTheme(source: ITokenThemeRule[]): TokenTheme { + return this.createFromParsedTokenTheme(parseTokenTheme(source)); + } + + public static createFromParsedTokenTheme(source: ParsedTokenThemeRule[]): TokenTheme { + return resolveParsedTokenThemeRules(source); + } + + private readonly _colorMap: ColorMap; + private readonly _root: ThemeTrieElement; + private readonly _cache: Map; + + constructor(colorMap: ColorMap, root: ThemeTrieElement) { + this._colorMap = colorMap; + this._root = root; + this._cache = new Map(); + } + + public getColorMap(): Color[] { + return this._colorMap.getColorMap(); + } + + /** + * used for testing purposes + */ + public getThemeTrieElement(): ExternalThemeTrieElement { + return this._root.toExternalThemeTrieElement(); + } + + public _match(token: string): ThemeTrieElementRule { + return this._root.match(token); + } + + public match(languageId: LanguageId, token: string): number { + // The cache contains the metadata without the language bits set. + let result = this._cache.get(token); + if (typeof result === 'undefined') { + let rule = this._match(token); + let standardToken = toStandardTokenType(token); + result = ( + rule.metadata + | (standardToken << MetadataConsts.TOKEN_TYPE_OFFSET) + ) >>> 0; + this._cache.set(token, result); + } + + return ( + result + | (languageId << MetadataConsts.LANGUAGEID_OFFSET) + ) >>> 0; + } +} + +const STANDARD_TOKEN_TYPE_REGEXP = /\b(comment|string|regex)\b/; +export function toStandardTokenType(tokenType: string): StandardTokenType { + let m = tokenType.match(STANDARD_TOKEN_TYPE_REGEXP); + if (!m) { + return StandardTokenType.Other; + } + switch (m[1]) { + case 'comment': + return StandardTokenType.Comment; + case 'string': + return StandardTokenType.String; + case 'regex': + return StandardTokenType.RegEx; + } + throw new Error('Unexpected match for standard token type!'); +} + +export function strcmp(a: string, b: string): number { + if (a < b) { + return -1; + } + if (a > b) { + return 1; + } + return 0; +} + +export class ThemeTrieElementRule { + _themeTrieElementRuleBrand: void; + + private _fontStyle: FontStyle; + private _foreground: ColorId; + private _background: ColorId; + public metadata: number; + + constructor(fontStyle: FontStyle, foreground: ColorId, background: ColorId) { + this._fontStyle = fontStyle; + this._foreground = foreground; + this._background = background; + this.metadata = ( + (this._fontStyle << MetadataConsts.FONT_STYLE_OFFSET) + | (this._foreground << MetadataConsts.FOREGROUND_OFFSET) + | (this._background << MetadataConsts.BACKGROUND_OFFSET) + ) >>> 0; + } + + public clone(): ThemeTrieElementRule { + return new ThemeTrieElementRule(this._fontStyle, this._foreground, this._background); + } + + public static cloneArr(arr: ThemeTrieElementRule[]): ThemeTrieElementRule[] { + let r: ThemeTrieElementRule[] = []; + for (let i = 0, len = arr.length; i < len; i++) { + r[i] = arr[i].clone(); + } + return r; + } + + public acceptOverwrite(fontStyle: FontStyle, foreground: ColorId, background: ColorId): void { + if (fontStyle !== FontStyle.NotSet) { + this._fontStyle = fontStyle; + } + if (foreground !== ColorId.None) { + this._foreground = foreground; + } + if (background !== ColorId.None) { + this._background = background; + } + this.metadata = ( + (this._fontStyle << MetadataConsts.FONT_STYLE_OFFSET) + | (this._foreground << MetadataConsts.FOREGROUND_OFFSET) + | (this._background << MetadataConsts.BACKGROUND_OFFSET) + ) >>> 0; + } +} + +export class ExternalThemeTrieElement { + + public readonly mainRule: ThemeTrieElementRule; + public readonly children: { [segment: string]: ExternalThemeTrieElement }; + + constructor(mainRule: ThemeTrieElementRule, children?: { [segment: string]: ExternalThemeTrieElement }) { + this.mainRule = mainRule; + this.children = children || Object.create(null); + } +} + +export class ThemeTrieElement { + _themeTrieElementBrand: void; + + private readonly _mainRule: ThemeTrieElementRule; + private readonly _children: Map; + + constructor(mainRule: ThemeTrieElementRule) { + this._mainRule = mainRule; + this._children = new Map(); + } + + /** + * used for testing purposes + */ + public toExternalThemeTrieElement(): ExternalThemeTrieElement { + let children: { [segment: string]: ExternalThemeTrieElement } = Object.create(null); + this._children.forEach((element, index) => { + children[index] = element.toExternalThemeTrieElement(); + }); + return new ExternalThemeTrieElement(this._mainRule, children); + } + + public match(token: string): ThemeTrieElementRule { + if (token === '') { + return this._mainRule; + } + + let dotIndex = token.indexOf('.'); + let head: string; + let tail: string; + if (dotIndex === -1) { + head = token; + tail = ''; + } else { + head = token.substring(0, dotIndex); + tail = token.substring(dotIndex + 1); + } + + let child = this._children.get(head); + if (typeof child !== 'undefined') { + return child.match(tail); + } + + return this._mainRule; + } + + public insert(token: string, fontStyle: FontStyle, foreground: ColorId, background: ColorId): void { + if (token === '') { + // Merge into the main rule + this._mainRule.acceptOverwrite(fontStyle, foreground, background); + return; + } + + let dotIndex = token.indexOf('.'); + let head: string; + let tail: string; + if (dotIndex === -1) { + head = token; + tail = ''; + } else { + head = token.substring(0, dotIndex); + tail = token.substring(dotIndex + 1); + } + + let child = this._children.get(head); + if (typeof child === 'undefined') { + child = new ThemeTrieElement(this._mainRule.clone()); + this._children.set(head, child); + } + + child.insert(tail, fontStyle, foreground, background); + } +} + +export function generateTokensCSSForColorMap(colorMap: Color[]): string { + let rules: string[] = []; + for (let i = 1, len = colorMap.length; i < len; i++) { + let color = colorMap[i]; + rules[i] = `.mtk${i} { color: ${color}; }`; + } + rules.push('.mtki { font-style: italic; }'); + rules.push('.mtkb { font-weight: bold; }'); + rules.push('.mtku { text-decoration: underline; }'); + return rules.join('\n'); +} diff --git a/src/vs/editor/common/modes/textToHtmlTokenizer.ts b/src/vs/editor/common/modes/textToHtmlTokenizer.ts new file mode 100644 index 0000000000..b52f274dac --- /dev/null +++ b/src/vs/editor/common/modes/textToHtmlTokenizer.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { IState, ITokenizationSupport, TokenizationRegistry, LanguageId } from 'vs/editor/common/modes'; +import { NULL_STATE, nullTokenize2 } from 'vs/editor/common/modes/nullMode'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { CharCode } from 'vs/base/common/charCode'; +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; + +export function tokenizeToString(text: string, languageId: string): string { + return _tokenizeToString(text, _getSafeTokenizationSupport(languageId)); +} + +export function tokenizeLineToHTML(text: string, viewLineTokens: ViewLineToken[], colorMap: string[], startOffset: number, endOffset: number, tabSize: number): string { + let result = `
`; + let charIndex = startOffset; + let tabsCharDelta = 0; + + for (let tokenIndex = 0, lenJ = viewLineTokens.length; tokenIndex < lenJ; tokenIndex++) { + const token = viewLineTokens[tokenIndex]; + const tokenEndIndex = token.endIndex; + + if (token.endIndex <= startOffset) { + continue; + } + + let partContent = ''; + + for (; charIndex < tokenEndIndex && charIndex < endOffset; charIndex++) { + const charCode = text.charCodeAt(charIndex); + + switch (charCode) { + case CharCode.Tab: + let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; + tabsCharDelta += insertSpacesCount - 1; + while (insertSpacesCount > 0) { + partContent += ' '; + insertSpacesCount--; + } + break; + + case CharCode.LessThan: + partContent += '<'; + break; + + case CharCode.GreaterThan: + partContent += '>'; + break; + + case CharCode.Ampersand: + partContent += '&'; + break; + + case CharCode.Null: + partContent += '�'; + break; + + case CharCode.UTF8_BOM: + case CharCode.LINE_SEPARATOR_2028: + partContent += '\ufffd'; + break; + + case CharCode.CarriageReturn: + // zero width space, because carriage return would introduce a line break + partContent += '​'; + break; + + default: + partContent += String.fromCharCode(charCode); + } + } + + result += `${partContent}`; + + if (token.endIndex > endOffset || charIndex >= endOffset) { + break; + } + } + + result += `
`; + return result; +} + +function _getSafeTokenizationSupport(languageId: string): ITokenizationSupport { + let tokenizationSupport = TokenizationRegistry.get(languageId); + if (tokenizationSupport) { + return tokenizationSupport; + } + return { + getInitialState: () => NULL_STATE, + tokenize: undefined, + tokenize2: (buffer: string, state: IState, deltaOffset: number) => nullTokenize2(LanguageId.Null, buffer, state, deltaOffset) + }; +} + +function _tokenizeToString(text: string, tokenizationSupport: ITokenizationSupport): string { + let result = `
`; + let lines = text.split(/\r\n|\r|\n/); + let currentState = tokenizationSupport.getInitialState(); + for (let i = 0, len = lines.length; i < len; i++) { + let line = lines[i]; + + if (i > 0) { + result += `
`; + } + + let tokenizationResult = tokenizationSupport.tokenize2(line, currentState, 0); + let lineTokens = new LineTokens(tokenizationResult.tokens, line); + let viewLineTokens = lineTokens.inflate(); + + let startOffset = 0; + for (let j = 0, lenJ = viewLineTokens.length; j < lenJ; j++) { + const viewLineToken = viewLineTokens[j]; + result += `${strings.escape(line.substring(startOffset, viewLineToken.endIndex))}`; + startOffset = viewLineToken.endIndex; + } + + currentState = tokenizationResult.endState; + } + + result += `
`; + return result; +} diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts new file mode 100644 index 0000000000..3d19fab34a --- /dev/null +++ b/src/vs/editor/common/modes/tokenizationRegistry.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent } from 'vs/editor/common/modes'; +import { Color } from 'vs/base/common/color'; + +export class TokenizationRegistryImpl implements ITokenizationRegistry { + + private _map: { [language: string]: ITokenizationSupport }; + + private _onDidChange: Emitter = new Emitter(); + public onDidChange: Event = this._onDidChange.event; + + private _colorMap: Color[]; + + constructor() { + this._map = Object.create(null); + this._colorMap = null; + } + + public fire(languages: string[]): void { + this._onDidChange.fire({ + changedLanguages: languages, + changedColorMap: false + }); + } + + public register(language: string, support: ITokenizationSupport): IDisposable { + this._map[language] = support; + this.fire([language]); + return { + dispose: () => { + if (this._map[language] !== support) { + return; + } + delete this._map[language]; + this.fire([language]); + } + }; + } + + public get(language: string): ITokenizationSupport { + return (this._map[language] || null); + } + + public setColorMap(colorMap: Color[]): void { + this._colorMap = colorMap; + this._onDidChange.fire({ + changedLanguages: Object.keys(this._map), + changedColorMap: true + }); + } + + public getColorMap(): Color[] { + return this._colorMap; + } + + public getDefaultForeground(): Color { + return this._colorMap[ColorId.DefaultForeground]; + } + + public getDefaultBackground(): Color { + return this._colorMap[ColorId.DefaultBackground]; + } +} diff --git a/src/vs/editor/common/services/abstractCodeEditorService.ts b/src/vs/editor/common/services/abstractCodeEditorService.ts new file mode 100644 index 0000000000..21800e5661 --- /dev/null +++ b/src/vs/editor/common/services/abstractCodeEditorService.ts @@ -0,0 +1,158 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event, { Emitter } from 'vs/base/common/event'; +import { ICommonCodeEditor, ICommonDiffEditor, IDecorationRenderOptions, IModelDecorationOptions, IModel } from 'vs/editor/common/editorCommon'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; + +export abstract class AbstractCodeEditorService implements ICodeEditorService { + + _serviceBrand: any; + + private _onCodeEditorAdd: Emitter; + private _onCodeEditorRemove: Emitter; + private _codeEditors: { [editorId: string]: ICommonCodeEditor; }; + + private _onDiffEditorAdd: Emitter; + private _onDiffEditorRemove: Emitter; + private _diffEditors: { [editorId: string]: ICommonDiffEditor; }; + + constructor() { + this._codeEditors = Object.create(null); + this._diffEditors = Object.create(null); + this._onCodeEditorAdd = new Emitter(); + this._onCodeEditorRemove = new Emitter(); + this._onDiffEditorAdd = new Emitter(); + this._onDiffEditorRemove = new Emitter(); + } + + addCodeEditor(editor: ICommonCodeEditor): void { + this._codeEditors[editor.getId()] = editor; + this._onCodeEditorAdd.fire(editor); + } + + get onCodeEditorAdd(): Event { + return this._onCodeEditorAdd.event; + } + + removeCodeEditor(editor: ICommonCodeEditor): void { + if (delete this._codeEditors[editor.getId()]) { + this._onCodeEditorRemove.fire(editor); + } + } + + get onCodeEditorRemove(): Event { + return this._onCodeEditorRemove.event; + } + + getCodeEditor(editorId: string): ICommonCodeEditor { + return this._codeEditors[editorId] || null; + } + + listCodeEditors(): ICommonCodeEditor[] { + return Object.keys(this._codeEditors).map(id => this._codeEditors[id]); + } + + addDiffEditor(editor: ICommonDiffEditor): void { + this._diffEditors[editor.getId()] = editor; + this._onDiffEditorAdd.fire(editor); + } + + get onDiffEditorAdd(): Event { + return this._onDiffEditorAdd.event; + } + + removeDiffEditor(editor: ICommonDiffEditor): void { + if (delete this._diffEditors[editor.getId()]) { + this._onDiffEditorRemove.fire(editor); + } + } + + get onDiffEditorRemove(): Event { + return this._onDiffEditorRemove.event; + } + + getDiffEditor(editorId: string): ICommonDiffEditor { + return this._diffEditors[editorId] || null; + } + + listDiffEditors(): ICommonDiffEditor[] { + return Object.keys(this._diffEditors).map(id => this._diffEditors[id]); + } + + getFocusedCodeEditor(): ICommonCodeEditor { + let editorWithWidgetFocus: ICommonCodeEditor = null; + + let editors = this.listCodeEditors(); + for (let i = 0; i < editors.length; i++) { + let editor = editors[i]; + + if (editor.isFocused()) { + // bingo! + return editor; + } + + if (editor.hasWidgetFocus()) { + editorWithWidgetFocus = editor; + } + } + + return editorWithWidgetFocus; + } + + abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void; + abstract removeDecorationType(key: string): void; + abstract resolveDecorationOptions(decorationTypeKey: string, writable: boolean): IModelDecorationOptions; + + private _transientWatchers: { [uri: string]: ModelTransientSettingWatcher; } = {}; + + public setTransientModelProperty(model: IModel, key: string, value: any): void { + const uri = model.uri.toString(); + + let w: ModelTransientSettingWatcher; + if (this._transientWatchers.hasOwnProperty(uri)) { + w = this._transientWatchers[uri]; + } else { + w = new ModelTransientSettingWatcher(uri, model, this); + this._transientWatchers[uri] = w; + } + + w.set(key, value); + } + + public getTransientModelProperty(model: IModel, key: string): any { + const uri = model.uri.toString(); + + if (!this._transientWatchers.hasOwnProperty(uri)) { + return undefined; + } + + return this._transientWatchers[uri].get(key); + } + + _removeWatcher(w: ModelTransientSettingWatcher): void { + delete this._transientWatchers[w.uri]; + } +} + +export class ModelTransientSettingWatcher { + public readonly uri: string; + private readonly _values: { [key: string]: any; }; + + constructor(uri: string, model: IModel, owner: AbstractCodeEditorService) { + this.uri = uri; + this._values = {}; + model.onWillDispose(() => owner._removeWatcher(this)); + } + + public set(key: string, value: any): void { + this._values[key] = value; + } + + public get(key: string): any { + return this._values[key]; + } +} diff --git a/src/vs/editor/common/services/bulkEdit.ts b/src/vs/editor/common/services/bulkEdit.ts new file mode 100644 index 0000000000..2e710ab19f --- /dev/null +++ b/src/vs/editor/common/services/bulkEdit.ts @@ -0,0 +1,391 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { flatten } from 'vs/base/common/arrays'; +import { IStringDictionary, forEach, values, groupBy, size } from 'vs/base/common/collections'; +import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { IFileService, IFileChange } from 'vs/platform/files/common/files'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { Selection, ISelection } from 'vs/editor/common/core/selection'; +import { IIdentifiedSingleEditOperation, IModel, EndOfLineSequence, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { IProgressRunner } from 'vs/platform/progress/common/progress'; + +export interface IResourceEdit { + resource: URI; + range?: IRange; + newText: string; + newEol?: EndOfLineSequence; +} + +interface IRecording { + stop(): void; + hasChanged(resource: URI): boolean; + allChanges(): IFileChange[]; +} + +class ChangeRecorder { + + private _fileService: IFileService; + + constructor(fileService?: IFileService) { + this._fileService = fileService; + } + + public start(): IRecording { + + const changes: IStringDictionary = Object.create(null); + + let stop: IDisposable; + if (this._fileService) { + stop = this._fileService.onFileChanges((event) => { + event.changes.forEach(change => { + + const key = String(change.resource); + let array = changes[key]; + + if (!array) { + changes[key] = array = []; + } + + array.push(change); + }); + }); + } + + return { + stop: () => { return stop && stop.dispose(); }, + hasChanged: (resource: URI) => !!changes[resource.toString()], + allChanges: () => flatten(values(changes)) + }; + } +} + +class EditTask implements IDisposable { + + private _initialSelections: Selection[]; + private _endCursorSelection: Selection; + private get _model(): IModel { return this._modelReference.object.textEditorModel; } + private _modelReference: IReference; + private _edits: IIdentifiedSingleEditOperation[]; + private _newEol: EndOfLineSequence; + + constructor(modelReference: IReference) { + this._endCursorSelection = null; + this._modelReference = modelReference; + this._edits = []; + } + + public addEdit(edit: IResourceEdit): void { + + if (typeof edit.newEol === 'number') { + // honor eol-change + this._newEol = edit.newEol; + } + + if (edit.range || edit.newText) { + // create edit operation + let range: Range; + if (!edit.range) { + range = this._model.getFullModelRange(); + } else { + range = Range.lift(edit.range); + } + this._edits.push(EditOperation.replaceMove(range, edit.newText)); + } + } + + public apply(): void { + if (this._edits.length > 0) { + + this._edits = this._edits.map((value, index) => ({ value, index })).sort((a, b) => { + let ret = Range.compareRangesUsingStarts(a.value.range, b.value.range); + if (ret === 0) { + ret = a.index - b.index; + } + return ret; + }).map(element => element.value); + + this._initialSelections = this._getInitialSelections(); + this._model.pushStackElement(); + this._model.pushEditOperations(this._initialSelections, this._edits, (edits) => this._getEndCursorSelections(edits)); + this._model.pushStackElement(); + } + if (this._newEol !== undefined) { + this._model.pushStackElement(); + this._model.setEOL(this._newEol); + this._model.pushStackElement(); + } + } + + protected _getInitialSelections(): Selection[] { + const firstRange = this._edits[0].range; + const initialSelection = new Selection( + firstRange.startLineNumber, + firstRange.startColumn, + firstRange.endLineNumber, + firstRange.endColumn + ); + return [initialSelection]; + } + + private _getEndCursorSelections(inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] { + let relevantEditIndex = 0; + for (let i = 0; i < inverseEditOperations.length; i++) { + const editRange = inverseEditOperations[i].range; + for (let j = 0; j < this._initialSelections.length; j++) { + const selectionRange = this._initialSelections[j]; + if (Range.areIntersectingOrTouching(editRange, selectionRange)) { + relevantEditIndex = i; + break; + } + } + } + + const srcRange = inverseEditOperations[relevantEditIndex].range; + this._endCursorSelection = new Selection( + srcRange.endLineNumber, + srcRange.endColumn, + srcRange.endLineNumber, + srcRange.endColumn + ); + return [this._endCursorSelection]; + } + + public getEndCursorSelection(): Selection { + return this._endCursorSelection; + } + + dispose() { + if (this._model) { + this._modelReference.dispose(); + this._modelReference = null; + } + } +} + +class SourceModelEditTask extends EditTask { + + private _knownInitialSelections: Selection[]; + + constructor(modelReference: IReference, initialSelections: Selection[]) { + super(modelReference); + this._knownInitialSelections = initialSelections; + } + + protected _getInitialSelections(): Selection[] { + return this._knownInitialSelections; + } +} + +class BulkEditModel implements IDisposable { + + private _textModelResolverService: ITextModelService; + private _numberOfResourcesToModify: number = 0; + private _numberOfChanges: number = 0; + private _edits: IStringDictionary = Object.create(null); + private _tasks: EditTask[]; + private _sourceModel: URI; + private _sourceSelections: Selection[]; + private _sourceModelTask: SourceModelEditTask; + + constructor(textModelResolverService: ITextModelService, sourceModel: URI, sourceSelections: Selection[], edits: IResourceEdit[], private progress: IProgressRunner = null) { + this._textModelResolverService = textModelResolverService; + this._sourceModel = sourceModel; + this._sourceSelections = sourceSelections; + this._sourceModelTask = null; + + for (let edit of edits) { + this._addEdit(edit); + } + } + + public resourcesCount(): number { + return this._numberOfResourcesToModify; + } + + public changeCount(): number { + return this._numberOfChanges; + } + + private _addEdit(edit: IResourceEdit): void { + let array = this._edits[edit.resource.toString()]; + if (!array) { + this._edits[edit.resource.toString()] = array = []; + this._numberOfResourcesToModify += 1; + } + this._numberOfChanges += 1; + array.push(edit); + } + + public prepare(): TPromise { + + if (this._tasks) { + throw new Error('illegal state - already prepared'); + } + + this._tasks = []; + const promises: TPromise[] = []; + + if (this.progress) { + this.progress.total(this._numberOfResourcesToModify * 2); + } + + forEach(this._edits, entry => { + const promise = this._textModelResolverService.createModelReference(URI.parse(entry.key)).then(ref => { + const model = ref.object; + + if (!model || !model.textEditorModel) { + throw new Error(`Cannot load file ${entry.key}`); + } + + const textEditorModel = model.textEditorModel; + let task: EditTask; + + if (this._sourceModel && textEditorModel.uri.toString() === this._sourceModel.toString()) { + this._sourceModelTask = new SourceModelEditTask(ref, this._sourceSelections); + task = this._sourceModelTask; + } else { + task = new EditTask(ref); + } + + entry.value.forEach(edit => task.addEdit(edit)); + this._tasks.push(task); + if (this.progress) { + this.progress.worked(1); + } + }); + promises.push(promise); + }); + + + return TPromise.join(promises).then(_ => this); + } + + public apply(): Selection { + this._tasks.forEach(task => this.applyTask(task)); + let r: Selection = null; + if (this._sourceModelTask) { + r = this._sourceModelTask.getEndCursorSelection(); + } + return r; + } + + private applyTask(task: EditTask): void { + task.apply(); + if (this.progress) { + this.progress.worked(1); + } + } + + dispose(): void { + this._tasks = dispose(this._tasks); + } +} + +export interface BulkEdit { + progress(progress: IProgressRunner): void; + add(edit: IResourceEdit[]): void; + finish(): TPromise; + ariaMessage(): string; +} + +export function bulkEdit(textModelResolverService: ITextModelService, editor: ICommonCodeEditor, edits: IResourceEdit[], fileService?: IFileService, progress: IProgressRunner = null): TPromise { + let bulk = createBulkEdit(textModelResolverService, editor, fileService); + bulk.add(edits); + bulk.progress(progress); + return bulk.finish(); +} + +export function createBulkEdit(textModelResolverService: ITextModelService, editor?: ICommonCodeEditor, fileService?: IFileService): BulkEdit { + + let all: IResourceEdit[] = []; + let recording = new ChangeRecorder(fileService).start(); + let progressRunner: IProgressRunner; + + function progress(progress: IProgressRunner) { + progressRunner = progress; + } + + function add(edits: IResourceEdit[]): void { + all.push(...edits); + } + + function getConcurrentEdits() { + let names: string[]; + for (let edit of all) { + if (recording.hasChanged(edit.resource)) { + if (!names) { + names = []; + } + names.push(edit.resource.fsPath); + } + } + if (names) { + return nls.localize('conflict', "These files have changed in the meantime: {0}", names.join(', ')); + } + return undefined; + } + + function finish(): TPromise { + + if (all.length === 0) { + return TPromise.as(undefined); + } + + let concurrentEdits = getConcurrentEdits(); + if (concurrentEdits) { + return TPromise.wrapError(new Error(concurrentEdits)); + } + + let uri: URI; + let selections: Selection[]; + + if (editor && editor.getModel()) { + uri = editor.getModel().uri; + selections = editor.getSelections(); + } + + const model = new BulkEditModel(textModelResolverService, uri, selections, all, progressRunner); + + return model.prepare().then(_ => { + + let concurrentEdits = getConcurrentEdits(); + if (concurrentEdits) { + throw new Error(concurrentEdits); + } + + recording.stop(); + + const result = model.apply(); + model.dispose(); + return result; + }); + } + + function ariaMessage(): string { + let editCount = all.length; + let resourceCount = size(groupBy(all, edit => edit.resource.toString())); + if (editCount === 0) { + return nls.localize('summary.0', "Made no edits"); + } else if (editCount > 1 && resourceCount > 1) { + return nls.localize('summary.nm', "Made {0} text edits in {1} files", editCount, resourceCount); + } else { + return nls.localize('summary.n0', "Made {0} text edits in one file", editCount, resourceCount); + } + } + + return { + progress, + add, + finish, + ariaMessage + }; +} diff --git a/src/vs/editor/common/services/codeEditorService.ts b/src/vs/editor/common/services/codeEditorService.ts new file mode 100644 index 0000000000..85f077d0ff --- /dev/null +++ b/src/vs/editor/common/services/codeEditorService.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ICommonCodeEditor, ICommonDiffEditor, isCommonCodeEditor, isCommonDiffEditor, IDecorationRenderOptions, IModelDecorationOptions, IModel } from 'vs/editor/common/editorCommon'; +import { IEditor } from 'vs/platform/editor/common/editor'; + +export var ICodeEditorService = createDecorator('codeEditorService'); + +export interface ICodeEditorService { + _serviceBrand: any; + + onCodeEditorAdd: Event; + onCodeEditorRemove: Event; + + onDiffEditorAdd: Event; + onDiffEditorRemove: Event; + + addCodeEditor(editor: ICommonCodeEditor): void; + removeCodeEditor(editor: ICommonCodeEditor): void; + getCodeEditor(editorId: string): ICommonCodeEditor; + listCodeEditors(): ICommonCodeEditor[]; + + addDiffEditor(editor: ICommonDiffEditor): void; + removeDiffEditor(editor: ICommonDiffEditor): void; + getDiffEditor(editorId: string): ICommonDiffEditor; + listDiffEditors(): ICommonDiffEditor[]; + + /** + * Returns the current focused code editor (if the focus is in the editor or in an editor widget) or null. + */ + getFocusedCodeEditor(): ICommonCodeEditor; + + registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void; + removeDecorationType(key: string): void; + resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions; + + setTransientModelProperty(model: IModel, key: string, value: any): void; + getTransientModelProperty(model: IModel, key: string): any; +} + +/** + * Uses `editor.getControl()` and returns either a `codeEditor` or a `diffEditor` or nothing. + */ +export function getCodeOrDiffEditor(editor: IEditor): { codeEditor: ICommonCodeEditor; diffEditor: ICommonDiffEditor } { + if (editor) { + let control = editor.getControl(); + if (control) { + if (isCommonCodeEditor(control)) { + return { + codeEditor: control, + diffEditor: null + }; + } + if (isCommonDiffEditor(control)) { + return { + codeEditor: null, + diffEditor: control + }; + } + } + } + + return { + codeEditor: null, + diffEditor: null + }; +} + +/** + * Uses `editor.getControl()` and returns either the code editor, or the modified editor of a diff editor or nothing. + */ +export function getCodeEditor(editor: IEditor): ICommonCodeEditor { + let r = getCodeOrDiffEditor(editor); + return r.codeEditor || (r.diffEditor && r.diffEditor.getModifiedEditor()) || null; +} diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts new file mode 100644 index 0000000000..afbf70b525 --- /dev/null +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -0,0 +1,575 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; +import { stringDiff } from 'vs/base/common/diff/diff'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { MirrorModel as BaseMirrorModel, IModelChangedEvent } from 'vs/editor/common/model/mirrorModel'; +import { IInplaceReplaceSupportResult, ILink, ISuggestResult, ISuggestion, TextEdit } from 'vs/editor/common/modes'; +import { computeLinks } from 'vs/editor/common/modes/linkComputer'; +import { BasicInplaceReplace } from 'vs/editor/common/modes/supports/inplaceReplaceSupport'; +import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; +import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'; + +export interface IMirrorModel { + readonly uri: URI; + readonly version: number; + getValue(): string; +} + +export interface IWorkerContext { + /** + * Get all available mirror models in this worker. + */ + getMirrorModels(): IMirrorModel[]; +} + +/** + * @internal + */ +export interface IRawModelData { + url: string; + versionId: number; + lines: string[]; + EOL: string; +} + +/** + * @internal + */ +export interface ICommonModel { + uri: URI; + version: number; + eol: string; + getValue(): string; + + getLinesContent(): string[]; + getLineCount(): number; + getLineContent(lineNumber: number): string; + getWordUntilPosition(position: IPosition, wordDefinition: RegExp): editorCommon.IWordAtPosition; + getAllUniqueWords(wordDefinition: RegExp, skipWordOnce?: string): string[]; + getValueInRange(range: IRange): string; + getWordAtPosition(position: IPosition, wordDefinition: RegExp): Range; + offsetAt(position: IPosition): number; + positionAt(offset: number): IPosition; +} + +/** + * Range of a word inside a model. + * @internal + */ +interface IWordRange { + /** + * The index where the word starts. + */ + readonly start: number; + /** + * The index where the word ends. + */ + readonly end: number; +} + +/** + * @internal + */ +class MirrorModel extends BaseMirrorModel implements ICommonModel { + + public get uri(): URI { + return this._uri; + } + + public get version(): number { + return this._versionId; + } + + public get eol(): string { + return this._eol; + } + + public getValue(): string { + return this.getText(); + } + + public getLinesContent(): string[] { + return this._lines.slice(0); + } + + public getLineCount(): number { + return this._lines.length; + } + + public getLineContent(lineNumber: number): string { + return this._lines[lineNumber - 1]; + } + + public getWordAtPosition(position: IPosition, wordDefinition: RegExp): Range { + + let wordAtText = getWordAtText( + position.column, + ensureValidWordDefinition(wordDefinition), + this._lines[position.lineNumber - 1], + 0 + ); + + if (wordAtText) { + return new Range(position.lineNumber, wordAtText.startColumn, position.lineNumber, wordAtText.endColumn); + } + + return null; + } + + public getWordUntilPosition(position: IPosition, wordDefinition: RegExp): editorCommon.IWordAtPosition { + var wordAtPosition = this.getWordAtPosition(position, wordDefinition); + if (!wordAtPosition) { + return { + word: '', + startColumn: position.column, + endColumn: position.column + }; + } + return { + word: this._lines[position.lineNumber - 1].substring(wordAtPosition.startColumn - 1, position.column - 1), + startColumn: wordAtPosition.startColumn, + endColumn: position.column + }; + } + + private _getAllWords(wordDefinition: RegExp): string[] { + var result: string[] = []; + this._lines.forEach((line) => { + this._wordenize(line, wordDefinition).forEach((info) => { + result.push(line.substring(info.start, info.end)); + }); + }); + return result; + } + + public getAllUniqueWords(wordDefinition: RegExp, skipWordOnce?: string): string[] { + var foundSkipWord = false; + var uniqueWords = Object.create(null); + return this._getAllWords(wordDefinition).filter((word) => { + if (skipWordOnce && !foundSkipWord && skipWordOnce === word) { + foundSkipWord = true; + return false; + } else if (uniqueWords[word]) { + return false; + } else { + uniqueWords[word] = true; + return true; + } + }); + } + + private _wordenize(content: string, wordDefinition: RegExp): IWordRange[] { + const result: IWordRange[] = []; + let match: RegExpExecArray; + + wordDefinition.lastIndex = 0; // reset lastIndex just to be sure + + while (match = wordDefinition.exec(content)) { + if (match[0].length === 0) { + // it did match the empty string + break; + } + result.push({ start: match.index, end: match.index + match[0].length }); + } + return result; + } + + public getValueInRange(range: IRange): string { + range = this._validateRange(range); + + if (range.startLineNumber === range.endLineNumber) { + return this._lines[range.startLineNumber - 1].substring(range.startColumn - 1, range.endColumn - 1); + } + + var lineEnding = this._eol, + startLineIndex = range.startLineNumber - 1, + endLineIndex = range.endLineNumber - 1, + resultLines: string[] = []; + + resultLines.push(this._lines[startLineIndex].substring(range.startColumn - 1)); + for (var i = startLineIndex + 1; i < endLineIndex; i++) { + resultLines.push(this._lines[i]); + } + resultLines.push(this._lines[endLineIndex].substring(0, range.endColumn - 1)); + + return resultLines.join(lineEnding); + } + + public offsetAt(position: IPosition): number { + position = this._validatePosition(position); + this._ensureLineStarts(); + return this._lineStarts.getAccumulatedValue(position.lineNumber - 2) + (position.column - 1); + } + + public positionAt(offset: number): IPosition { + offset = Math.floor(offset); + offset = Math.max(0, offset); + + this._ensureLineStarts(); + let out = this._lineStarts.getIndexOf(offset); + let lineLength = this._lines[out.index].length; + + // Ensure we return a valid position + return { + lineNumber: 1 + out.index, + column: 1 + Math.min(out.remainder, lineLength) + }; + } + + private _validateRange(range: IRange): IRange { + + const start = this._validatePosition({ lineNumber: range.startLineNumber, column: range.startColumn }); + const end = this._validatePosition({ lineNumber: range.endLineNumber, column: range.endColumn }); + + if (start.lineNumber !== range.startLineNumber + || start.column !== range.startColumn + || end.lineNumber !== range.endLineNumber + || end.column !== range.endColumn) { + + return { + startLineNumber: start.lineNumber, + startColumn: start.column, + endLineNumber: end.lineNumber, + endColumn: end.column + }; + } + + return range; + } + + private _validatePosition(position: IPosition): IPosition { + if (!Position.isIPosition(position)) { + throw new Error('bad position'); + } + let { lineNumber, column } = position; + let hasChanged = false; + + if (lineNumber < 1) { + lineNumber = 1; + column = 1; + hasChanged = true; + + } else if (lineNumber > this._lines.length) { + lineNumber = this._lines.length; + column = this._lines[lineNumber - 1].length + 1; + hasChanged = true; + + } else { + let maxCharacter = this._lines[lineNumber - 1].length + 1; + if (column < 1) { + column = 1; + hasChanged = true; + } + else if (column > maxCharacter) { + column = maxCharacter; + hasChanged = true; + } + } + + if (!hasChanged) { + return position; + } else { + return { lineNumber, column }; + } + } +} + +/** + * @internal + */ +export abstract class BaseEditorSimpleWorker { + private _foreignModule: any; + + constructor() { + this._foreignModule = null; + } + + protected abstract _getModel(uri: string): ICommonModel; + protected abstract _getModels(): ICommonModel[]; + + // ---- BEGIN diff -------------------------------------------------------------------------- + + public computeDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): TPromise { + let original = this._getModel(originalUrl); + let modified = this._getModel(modifiedUrl); + if (!original || !modified) { + return null; + } + + let originalLines = original.getLinesContent(); + let modifiedLines = modified.getLinesContent(); + let diffComputer = new DiffComputer(originalLines, modifiedLines, { + shouldPostProcessCharChanges: true, + shouldIgnoreTrimWhitespace: ignoreTrimWhitespace, + shouldConsiderTrimWhitespaceInEmptyCase: true, + shouldMakePrettyDiff: true + }); + return TPromise.as(diffComputer.computeDiff()); + } + + public computeDirtyDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): TPromise { + let original = this._getModel(originalUrl); + let modified = this._getModel(modifiedUrl); + if (!original || !modified) { + return null; + } + + let originalLines = original.getLinesContent(); + let modifiedLines = modified.getLinesContent(); + let diffComputer = new DiffComputer(originalLines, modifiedLines, { + shouldPostProcessCharChanges: false, + shouldIgnoreTrimWhitespace: ignoreTrimWhitespace, + shouldConsiderTrimWhitespaceInEmptyCase: false, + shouldMakePrettyDiff: true + }); + return TPromise.as(diffComputer.computeDiff()); + } + + // ---- END diff -------------------------------------------------------------------------- + + + // ---- BEGIN minimal edits --------------------------------------------------------------- + + private static _diffLimit = 10000; + + public computeMoreMinimalEdits(modelUrl: string, edits: TextEdit[], ranges: IRange[]): TPromise { + const model = this._getModel(modelUrl); + if (!model) { + return TPromise.as(edits); + } + + const result: TextEdit[] = []; + let lastEol: editorCommon.EndOfLineSequence; + + for (let { range, text, eol } of edits) { + + if (typeof eol === 'number') { + lastEol = eol; + } + + if (!range) { + // eol-change only + continue; + } + + const original = model.getValueInRange(range); + text = text.replace(/\r\n|\n|\r/g, model.eol); + + if (original === text) { + // noop + continue; + } + + // make sure diff won't take too long + if (Math.max(text.length, original.length) > BaseEditorSimpleWorker._diffLimit) { + result.push({ range, text }); + continue; + } + + // compute diff between original and edit.text + const changes = stringDiff(original, text, false); + const editOffset = model.offsetAt(Range.lift(range).getStartPosition()); + + for (const change of changes) { + const start = model.positionAt(editOffset + change.originalStart); + const end = model.positionAt(editOffset + change.originalStart + change.originalLength); + const newEdit: TextEdit = { + text: text.substr(change.modifiedStart, change.modifiedLength), + range: { startLineNumber: start.lineNumber, startColumn: start.column, endLineNumber: end.lineNumber, endColumn: end.column } + }; + + if (model.getValueInRange(newEdit.range) !== newEdit.text) { + result.push(newEdit); + } + } + } + + if (typeof lastEol === 'number') { + result.push({ eol: lastEol, text: undefined, range: undefined }); + } + + return TPromise.as(result); + } + + // ---- END minimal edits --------------------------------------------------------------- + + public computeLinks(modelUrl: string): TPromise { + let model = this._getModel(modelUrl); + if (!model) { + return null; + } + + return TPromise.as(computeLinks(model)); + } + + // ---- BEGIN suggest -------------------------------------------------------------------------- + + public textualSuggest(modelUrl: string, position: IPosition, wordDef: string, wordDefFlags: string): TPromise { + const model = this._getModel(modelUrl); + if (model) { + const suggestions: ISuggestion[] = []; + const wordDefRegExp = new RegExp(wordDef, wordDefFlags); + const currentWord = model.getWordUntilPosition(position, wordDefRegExp).word; + + for (const word of model.getAllUniqueWords(wordDefRegExp)) { + if (word !== currentWord && isNaN(Number(word))) { + suggestions.push({ + type: 'text', + label: word, + insertText: word, + noAutoAccept: true, + overwriteBefore: currentWord.length + }); + } + } + return TPromise.as({ suggestions }); + } + return undefined; + } + + + // ---- END suggest -------------------------------------------------------------------------- + + public navigateValueSet(modelUrl: string, range: IRange, up: boolean, wordDef: string, wordDefFlags: string): TPromise { + let model = this._getModel(modelUrl); + if (!model) { + return null; + } + + let wordDefRegExp = new RegExp(wordDef, wordDefFlags); + + if (range.startColumn === range.endColumn) { + range = { + startLineNumber: range.startLineNumber, + startColumn: range.startColumn, + endLineNumber: range.endLineNumber, + endColumn: range.endColumn + 1 + }; + } + + let selectionText = model.getValueInRange(range); + + let wordRange = model.getWordAtPosition({ lineNumber: range.startLineNumber, column: range.startColumn }, wordDefRegExp); + let word: string = null; + if (wordRange !== null) { + word = model.getValueInRange(wordRange); + } + + let result = BasicInplaceReplace.INSTANCE.navigateValueSet(range, selectionText, wordRange, word, up); + return TPromise.as(result); + } + + // ---- BEGIN foreign module support -------------------------------------------------------------------------- + + public loadForeignModule(moduleId: string, createData: any): TPromise { + return new TPromise((c, e) => { + // Use the global require to be sure to get the global config + (self).require([moduleId], (foreignModule: { create: (ctx: IWorkerContext, createData: any) => any; }) => { + let ctx: IWorkerContext = { + getMirrorModels: (): IMirrorModel[] => { + return this._getModels(); + } + }; + this._foreignModule = foreignModule.create(ctx, createData); + + let methods: string[] = []; + for (let prop in this._foreignModule) { + if (typeof this._foreignModule[prop] === 'function') { + methods.push(prop); + } + } + + c(methods); + + }, e); + }); + } + + // foreign method request + public fmr(method: string, args: any[]): TPromise { + if (!this._foreignModule || typeof this._foreignModule[method] !== 'function') { + return TPromise.wrapError(new Error('Missing requestHandler or method: ' + method)); + } + + try { + return TPromise.as(this._foreignModule[method].apply(this._foreignModule, args)); + } catch (e) { + return TPromise.wrapError(e); + } + } + + // ---- END foreign module support -------------------------------------------------------------------------- +} + +/** + * @internal + */ +export class EditorSimpleWorkerImpl extends BaseEditorSimpleWorker implements IRequestHandler, IDisposable { + _requestHandlerTrait: any; + + private _models: { [uri: string]: MirrorModel; }; + + constructor() { + super(); + this._models = Object.create(null); + } + + public dispose(): void { + this._models = Object.create(null); + } + + protected _getModel(uri: string): ICommonModel { + return this._models[uri]; + } + + protected _getModels(): ICommonModel[] { + let all: MirrorModel[] = []; + Object.keys(this._models).forEach((key) => all.push(this._models[key])); + return all; + } + + public acceptNewModel(data: IRawModelData): void { + this._models[data.url] = new MirrorModel(URI.parse(data.url), data.lines, data.EOL, data.versionId); + } + + public acceptModelChanged(strURL: string, e: IModelChangedEvent): void { + if (!this._models[strURL]) { + return; + } + let model = this._models[strURL]; + model.onEvents(e); + } + + public acceptRemovedModel(strURL: string): void { + if (!this._models[strURL]) { + return; + } + delete this._models[strURL]; + } +} + +/** + * Called on the worker side + * @internal + */ +export function create(): IRequestHandler { + return new EditorSimpleWorkerImpl(); +} + +var global: any = self; +let isWebWorker = (typeof global.importScripts === 'function'); +if (isWebWorker) { + global.monaco = createMonacoBaseAPI(); +} diff --git a/src/vs/editor/common/services/editorWorkerService.ts b/src/vs/editor/common/services/editorWorkerService.ts new file mode 100644 index 0000000000..a007f249c5 --- /dev/null +++ b/src/vs/editor/common/services/editorWorkerService.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IChange, ILineChange } from 'vs/editor/common/editorCommon'; +import { IInplaceReplaceSupportResult, TextEdit } from 'vs/editor/common/modes'; +import { IRange } from 'vs/editor/common/core/range'; + +export var ID_EDITOR_WORKER_SERVICE = 'editorWorkerService'; +export var IEditorWorkerService = createDecorator(ID_EDITOR_WORKER_SERVICE); + +export interface IEditorWorkerService { + _serviceBrand: any; + + canComputeDiff(original: URI, modified: URI): boolean; + computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise; + + canComputeDirtyDiff(original: URI, modified: URI): boolean; + computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise; + + computeMoreMinimalEdits(resource: URI, edits: TextEdit[], ranges: IRange[]): TPromise; + + canNavigateValueSet(resource: URI): boolean; + navigateValueSet(resource: URI, range: IRange, up: boolean): TPromise; +} diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts new file mode 100644 index 0000000000..e07df1db72 --- /dev/null +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -0,0 +1,435 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IntervalTimer, ShallowCancelThenPromise, wireCancellationToken } from 'vs/base/common/async'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { SimpleWorkerClient, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker'; +import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import * as modes from 'vs/editor/common/modes'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IRange } from 'vs/editor/common/core/range'; +import { IModeService } from 'vs/editor/common/services/modeService'; + +/** + * Stop syncing a model to the worker if it was not needed for 1 min. + */ +const STOP_SYNC_MODEL_DELTA_TIME_MS = 60 * 1000; + +/** + * Stop the worker if it was not needed for 5 min. + */ +const STOP_WORKER_DELTA_TIME_MS = 5 * 60 * 1000; + +function canSyncModel(modelService: IModelService, resource: URI): boolean { + let model = modelService.getModel(resource); + if (!model) { + return false; + } + if (model.isTooLargeForTokenization()) { + return false; + } + return true; +} + +export class EditorWorkerServiceImpl extends Disposable implements IEditorWorkerService { + public _serviceBrand: any; + + private readonly _modelService: IModelService; + private readonly _workerManager: WorkerManager; + + constructor( + @IModelService modelService: IModelService, + @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, + @IModeService modeService: IModeService + ) { + super(); + this._modelService = modelService; + this._workerManager = this._register(new WorkerManager(this._modelService)); + + // todo@joh make sure this happens only once + this._register(modes.LinkProviderRegistry.register('*', { + provideLinks: (model, token) => { + if (!canSyncModel(this._modelService, model.uri)) { + return TPromise.as([]); // File too large + } + return wireCancellationToken(token, this._workerManager.withWorker().then(client => client.computeLinks(model.uri))); + } + })); + this._register(modes.SuggestRegistry.register('*', new WordBasedCompletionItemProvider(this._workerManager, configurationService, modeService, this._modelService))); + } + + public dispose(): void { + super.dispose(); + } + + public canComputeDiff(original: URI, modified: URI): boolean { + return (canSyncModel(this._modelService, original) && canSyncModel(this._modelService, modified)); + } + + public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise { + return this._workerManager.withWorker().then(client => client.computeDiff(original, modified, ignoreTrimWhitespace)); + } + + public canComputeDirtyDiff(original: URI, modified: URI): boolean { + return (canSyncModel(this._modelService, original) && canSyncModel(this._modelService, modified)); + } + + public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise { + return this._workerManager.withWorker().then(client => client.computeDirtyDiff(original, modified, ignoreTrimWhitespace)); + } + + public computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[], ranges: IRange[]): TPromise { + if (!Array.isArray(edits) || edits.length === 0) { + return TPromise.as(edits); + } else { + if (!canSyncModel(this._modelService, resource)) { + return TPromise.as(edits); // File too large + } + return this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits, ranges)); + } + } + + public canNavigateValueSet(resource: URI): boolean { + return (canSyncModel(this._modelService, resource)); + } + + public navigateValueSet(resource: URI, range: IRange, up: boolean): TPromise { + return this._workerManager.withWorker().then(client => client.navigateValueSet(resource, range, up)); + } +} + +class WordBasedCompletionItemProvider implements modes.ISuggestSupport { + + private readonly _workerManager: WorkerManager; + private readonly _configurationService: ITextResourceConfigurationService; + private readonly _modeService: IModeService; + private readonly _modelService: IModelService; + + constructor( + workerManager: WorkerManager, + configurationService: ITextResourceConfigurationService, + modeService: IModeService, + modelService: IModelService + ) { + this._workerManager = workerManager; + this._configurationService = configurationService; + this._modeService = modeService; + this._modelService = modelService; + } + + provideCompletionItems(model: editorCommon.IModel, position: Position): TPromise { + const { wordBasedSuggestions } = this._configurationService.getConfiguration(model.uri, position, 'editor'); + if (!wordBasedSuggestions) { + return undefined; + } + if (!canSyncModel(this._modelService, model.uri)) { + return undefined; // File too large + } + return this._workerManager.withWorker().then(client => client.textualSuggest(model.uri, position)); + } +} + +class WorkerManager extends Disposable { + + private _modelService: IModelService; + private _editorWorkerClient: EditorWorkerClient; + private _lastWorkerUsedTime: number; + + constructor(modelService: IModelService) { + super(); + this._modelService = modelService; + this._editorWorkerClient = null; + + let stopWorkerInterval = this._register(new IntervalTimer()); + stopWorkerInterval.cancelAndSet(() => this._checkStopIdleWorker(), Math.round(STOP_WORKER_DELTA_TIME_MS / 2)); + + this._register(this._modelService.onModelRemoved(_ => this._checkStopEmptyWorker())); + } + + public dispose(): void { + if (this._editorWorkerClient) { + this._editorWorkerClient.dispose(); + this._editorWorkerClient = null; + } + super.dispose(); + } + + /** + * Check if the model service has no more models and stop the worker if that is the case. + */ + private _checkStopEmptyWorker(): void { + if (!this._editorWorkerClient) { + return; + } + + let models = this._modelService.getModels(); + if (models.length === 0) { + // There are no more models => nothing possible for me to do + this._editorWorkerClient.dispose(); + this._editorWorkerClient = null; + } + } + + /** + * Check if the worker has been idle for a while and then stop it. + */ + private _checkStopIdleWorker(): void { + if (!this._editorWorkerClient) { + return; + } + + let timeSinceLastWorkerUsedTime = (new Date()).getTime() - this._lastWorkerUsedTime; + if (timeSinceLastWorkerUsedTime > STOP_WORKER_DELTA_TIME_MS) { + this._editorWorkerClient.dispose(); + this._editorWorkerClient = null; + } + } + + public withWorker(): TPromise { + this._lastWorkerUsedTime = (new Date()).getTime(); + if (!this._editorWorkerClient) { + this._editorWorkerClient = new EditorWorkerClient(this._modelService, 'editorWorkerService'); + } + return TPromise.as(this._editorWorkerClient); + } +} + +class EditorModelManager extends Disposable { + + private _proxy: EditorSimpleWorkerImpl; + private _modelService: IModelService; + private _syncedModels: { [modelUrl: string]: IDisposable[]; } = Object.create(null); + private _syncedModelsLastUsedTime: { [modelUrl: string]: number; } = Object.create(null); + + constructor(proxy: EditorSimpleWorkerImpl, modelService: IModelService, keepIdleModels: boolean) { + super(); + this._proxy = proxy; + this._modelService = modelService; + + if (!keepIdleModels) { + let timer = new IntervalTimer(); + timer.cancelAndSet(() => this._checkStopModelSync(), Math.round(STOP_SYNC_MODEL_DELTA_TIME_MS / 2)); + this._register(timer); + } + } + + public dispose(): void { + for (let modelUrl in this._syncedModels) { + dispose(this._syncedModels[modelUrl]); + } + this._syncedModels = Object.create(null); + this._syncedModelsLastUsedTime = Object.create(null); + super.dispose(); + } + + public esureSyncedResources(resources: URI[]): void { + for (let i = 0; i < resources.length; i++) { + let resource = resources[i]; + let resourceStr = resource.toString(); + + if (!this._syncedModels[resourceStr]) { + this._beginModelSync(resource); + } + if (this._syncedModels[resourceStr]) { + this._syncedModelsLastUsedTime[resourceStr] = (new Date()).getTime(); + } + } + } + + private _checkStopModelSync(): void { + let currentTime = (new Date()).getTime(); + + let toRemove: string[] = []; + for (let modelUrl in this._syncedModelsLastUsedTime) { + let elapsedTime = currentTime - this._syncedModelsLastUsedTime[modelUrl]; + if (elapsedTime > STOP_SYNC_MODEL_DELTA_TIME_MS) { + toRemove.push(modelUrl); + } + } + + for (let i = 0; i < toRemove.length; i++) { + this._stopModelSync(toRemove[i]); + } + } + + private _beginModelSync(resource: URI): void { + let model = this._modelService.getModel(resource); + if (!model) { + return; + } + if (model.isTooLargeForTokenization()) { + return; + } + + let modelUrl = resource.toString(); + + this._proxy.acceptNewModel({ + url: model.uri.toString(), + lines: model.getLinesContent(), + EOL: model.getEOL(), + versionId: model.getVersionId() + }); + + let toDispose: IDisposable[] = []; + toDispose.push(model.onDidChangeContent((e) => { + this._proxy.acceptModelChanged(modelUrl.toString(), e); + })); + toDispose.push(model.onWillDispose(() => { + this._stopModelSync(modelUrl); + })); + toDispose.push({ + dispose: () => { + this._proxy.acceptRemovedModel(modelUrl); + } + }); + + this._syncedModels[modelUrl] = toDispose; + } + + private _stopModelSync(modelUrl: string): void { + let toDispose = this._syncedModels[modelUrl]; + delete this._syncedModels[modelUrl]; + delete this._syncedModelsLastUsedTime[modelUrl]; + dispose(toDispose); + } +} + +interface IWorkerClient { + getProxyObject(): TPromise; + dispose(): void; +} + +class SynchronousWorkerClient implements IWorkerClient { + private _instance: T; + private _proxyObj: TPromise; + + constructor(instance: T) { + this._instance = instance; + this._proxyObj = TPromise.as(this._instance); + } + + public dispose(): void { + this._instance.dispose(); + this._instance = null; + this._proxyObj = null; + } + + public getProxyObject(): TPromise { + return new ShallowCancelThenPromise(this._proxyObj); + } +} + +export class EditorWorkerClient extends Disposable { + + private _modelService: IModelService; + private _worker: IWorkerClient; + private _workerFactory: DefaultWorkerFactory; + private _modelManager: EditorModelManager; + + constructor(modelService: IModelService, label: string) { + super(); + this._modelService = modelService; + this._workerFactory = new DefaultWorkerFactory(label); + this._worker = null; + this._modelManager = null; + } + + private _getOrCreateWorker(): IWorkerClient { + if (!this._worker) { + try { + this._worker = this._register(new SimpleWorkerClient( + this._workerFactory, + 'vs/editor/common/services/editorSimpleWorker' + )); + } catch (err) { + logOnceWebWorkerWarning(err); + this._worker = new SynchronousWorkerClient(new EditorSimpleWorkerImpl()); + } + } + return this._worker; + } + + protected _getProxy(): TPromise { + return new ShallowCancelThenPromise(this._getOrCreateWorker().getProxyObject().then(null, (err) => { + logOnceWebWorkerWarning(err); + this._worker = new SynchronousWorkerClient(new EditorSimpleWorkerImpl()); + return this._getOrCreateWorker().getProxyObject(); + })); + } + + private _getOrCreateModelManager(proxy: EditorSimpleWorkerImpl): EditorModelManager { + if (!this._modelManager) { + this._modelManager = this._register(new EditorModelManager(proxy, this._modelService, false)); + } + return this._modelManager; + } + + protected _withSyncedResources(resources: URI[]): TPromise { + return this._getProxy().then((proxy) => { + this._getOrCreateModelManager(proxy).esureSyncedResources(resources); + return proxy; + }); + } + + public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise { + return this._withSyncedResources([original, modified]).then(proxy => { + return proxy.computeDiff(original.toString(), modified.toString(), ignoreTrimWhitespace); + }); + } + + public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise { + return this._withSyncedResources([original, modified]).then(proxy => { + return proxy.computeDirtyDiff(original.toString(), modified.toString(), ignoreTrimWhitespace); + }); + } + + public computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[], ranges: IRange[]): TPromise { + return this._withSyncedResources([resource]).then(proxy => { + return proxy.computeMoreMinimalEdits(resource.toString(), edits, ranges); + }); + } + + public computeLinks(resource: URI): TPromise { + return this._withSyncedResources([resource]).then(proxy => { + return proxy.computeLinks(resource.toString()); + }); + } + + public textualSuggest(resource: URI, position: IPosition): TPromise { + return this._withSyncedResources([resource]).then(proxy => { + let model = this._modelService.getModel(resource); + if (!model) { + return null; + } + let wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); + let wordDef = wordDefRegExp.source; + let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : ''); + return proxy.textualSuggest(resource.toString(), position, wordDef, wordDefFlags); + }); + } + + public navigateValueSet(resource: URI, range: IRange, up: boolean): TPromise { + return this._withSyncedResources([resource]).then(proxy => { + let model = this._modelService.getModel(resource); + if (!model) { + return null; + } + let wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); + let wordDef = wordDefRegExp.source; + let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : ''); + return proxy.navigateValueSet(resource.toString(), range, up, wordDef, wordDefFlags); + }); + } +} diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts new file mode 100644 index 0000000000..6d4d76c4f7 --- /dev/null +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -0,0 +1,318 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import * as mime from 'vs/base/common/mime'; +import * as strings from 'vs/base/common/strings'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; +import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; +import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; +import { NULL_MODE_ID, NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode'; +import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +export interface IResolvedLanguage { + identifier: LanguageIdentifier; + name: string; + mimetypes: string[]; + aliases: string[]; + extensions: string[]; + filenames: string[]; + configurationFiles: string[]; +} + +export class LanguagesRegistry { + + private _nextLanguageId: number; + private _languages: { [id: string]: IResolvedLanguage; }; + private _languageIds: string[]; + + private _mimeTypesMap: { [mimeType: string]: LanguageIdentifier; }; + private _nameMap: { [name: string]: LanguageIdentifier; }; + private _lowercaseNameMap: { [name: string]: LanguageIdentifier; }; + + constructor(useModesRegistry = true) { + this._nextLanguageId = 1; + this._languages = {}; + this._mimeTypesMap = {}; + this._nameMap = {}; + this._lowercaseNameMap = {}; + this._languageIds = []; + + if (useModesRegistry) { + this._registerLanguages(ModesRegistry.getLanguages()); + ModesRegistry.onDidAddLanguages((m) => this._registerLanguages(m)); + } + } + + _registerLanguages(desc: ILanguageExtensionPoint[]): void { + if (desc.length === 0) { + return; + } + + for (let i = 0; i < desc.length; i++) { + this._registerLanguage(desc[i]); + } + + // Rebuild fast path maps + this._mimeTypesMap = {}; + this._nameMap = {}; + this._lowercaseNameMap = {}; + Object.keys(this._languages).forEach((langId) => { + let language = this._languages[langId]; + if (language.name) { + this._nameMap[language.name] = language.identifier; + } + language.aliases.forEach((alias) => { + this._lowercaseNameMap[alias.toLowerCase()] = language.identifier; + }); + language.mimetypes.forEach((mimetype) => { + this._mimeTypesMap[mimetype] = language.identifier; + }); + }); + + Registry.as(Extensions.Configuration).registerOverrideIdentifiers(ModesRegistry.getLanguages().map(language => language.id)); + } + + private _registerLanguage(lang: ILanguageExtensionPoint): void { + const langId = lang.id; + + let resolvedLanguage: IResolvedLanguage = null; + if (hasOwnProperty.call(this._languages, langId)) { + resolvedLanguage = this._languages[langId]; + } else { + let languageId = this._nextLanguageId++; + resolvedLanguage = { + identifier: new LanguageIdentifier(langId, languageId), + name: null, + mimetypes: [], + aliases: [], + extensions: [], + filenames: [], + configurationFiles: [] + }; + this._languageIds[languageId] = langId; + this._languages[langId] = resolvedLanguage; + } + + LanguagesRegistry._mergeLanguage(resolvedLanguage, lang); + } + + private static _mergeLanguage(resolvedLanguage: IResolvedLanguage, lang: ILanguageExtensionPoint): void { + const langId = lang.id; + + let primaryMime: string = null; + + if (typeof lang.mimetypes !== 'undefined' && Array.isArray(lang.mimetypes)) { + for (let i = 0; i < lang.mimetypes.length; i++) { + if (!primaryMime) { + primaryMime = lang.mimetypes[i]; + } + resolvedLanguage.mimetypes.push(lang.mimetypes[i]); + } + } + + if (!primaryMime) { + primaryMime = `text/x-${langId}`; + resolvedLanguage.mimetypes.push(primaryMime); + } + + if (Array.isArray(lang.extensions)) { + for (let extension of lang.extensions) { + mime.registerTextMime({ id: langId, mime: primaryMime, extension: extension }); + resolvedLanguage.extensions.push(extension); + } + } + + if (Array.isArray(lang.filenames)) { + for (let filename of lang.filenames) { + mime.registerTextMime({ id: langId, mime: primaryMime, filename: filename }); + resolvedLanguage.filenames.push(filename); + } + } + + if (Array.isArray(lang.filenamePatterns)) { + for (let filenamePattern of lang.filenamePatterns) { + mime.registerTextMime({ id: langId, mime: primaryMime, filepattern: filenamePattern }); + } + } + + if (typeof lang.firstLine === 'string' && lang.firstLine.length > 0) { + let firstLineRegexStr = lang.firstLine; + if (firstLineRegexStr.charAt(0) !== '^') { + firstLineRegexStr = '^' + firstLineRegexStr; + } + try { + let firstLineRegex = new RegExp(firstLineRegexStr); + if (!strings.regExpLeadsToEndlessLoop(firstLineRegex)) { + mime.registerTextMime({ id: langId, mime: primaryMime, firstline: firstLineRegex }); + } + } catch (err) { + // Most likely, the regex was bad + onUnexpectedError(err); + } + } + + resolvedLanguage.aliases.push(langId); + + let langAliases: string[] = null; + if (typeof lang.aliases !== 'undefined' && Array.isArray(lang.aliases)) { + if (lang.aliases.length === 0) { + // signal that this language should not get a name + langAliases = [null]; + } else { + langAliases = lang.aliases; + } + } + + if (langAliases !== null) { + for (let i = 0; i < langAliases.length; i++) { + if (!langAliases[i] || langAliases[i].length === 0) { + continue; + } + resolvedLanguage.aliases.push(langAliases[i]); + } + } + + let containsAliases = (langAliases !== null && langAliases.length > 0); + if (containsAliases && langAliases[0] === null) { + // signal that this language should not get a name + } else { + let bestName = (containsAliases ? langAliases[0] : null) || langId; + if (containsAliases || !resolvedLanguage.name) { + resolvedLanguage.name = bestName; + } + } + + if (typeof lang.configuration === 'string') { + resolvedLanguage.configurationFiles.push(lang.configuration); + } + } + + public isRegisteredMode(mimetypeOrModeId: string): boolean { + // Is this a known mime type ? + if (hasOwnProperty.call(this._mimeTypesMap, mimetypeOrModeId)) { + return true; + } + // Is this a known mode id ? + return hasOwnProperty.call(this._languages, mimetypeOrModeId); + } + + public getRegisteredModes(): string[] { + return Object.keys(this._languages); + } + + public getRegisteredLanguageNames(): string[] { + return Object.keys(this._nameMap); + } + + public getLanguageName(modeId: string): string { + if (!hasOwnProperty.call(this._languages, modeId)) { + return null; + } + return this._languages[modeId].name; + } + + public getModeIdForLanguageNameLowercase(languageNameLower: string): string { + if (!hasOwnProperty.call(this._lowercaseNameMap, languageNameLower)) { + return null; + } + return this._lowercaseNameMap[languageNameLower].language; + } + + public getConfigurationFiles(modeId: string): string[] { + if (!hasOwnProperty.call(this._languages, modeId)) { + return []; + } + return this._languages[modeId].configurationFiles || []; + } + + public getMimeForMode(modeId: string): string { + if (!hasOwnProperty.call(this._languages, modeId)) { + return null; + } + const language = this._languages[modeId]; + return (language.mimetypes[0] || null); + } + + public extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds: string): string[] { + if (!commaSeparatedMimetypesOrCommaSeparatedIds) { + return []; + } + + return ( + commaSeparatedMimetypesOrCommaSeparatedIds. + split(','). + map((mimeTypeOrId) => mimeTypeOrId.trim()). + map((mimeTypeOrId) => { + if (hasOwnProperty.call(this._mimeTypesMap, mimeTypeOrId)) { + return this._mimeTypesMap[mimeTypeOrId].language; + } + return mimeTypeOrId; + }). + filter((modeId) => { + return hasOwnProperty.call(this._languages, modeId); + }) + ); + } + + public getLanguageIdentifier(_modeId: string | LanguageId): LanguageIdentifier { + if (_modeId === NULL_MODE_ID || _modeId === LanguageId.Null) { + return NULL_LANGUAGE_IDENTIFIER; + } + + let modeId: string; + if (typeof _modeId === 'string') { + modeId = _modeId; + } else { + modeId = this._languageIds[_modeId]; + if (!modeId) { + return null; + } + } + + if (!hasOwnProperty.call(this._languages, modeId)) { + return null; + } + return this._languages[modeId].identifier; + } + + public getModeIdsFromLanguageName(languageName: string): string[] { + if (!languageName) { + return []; + } + if (hasOwnProperty.call(this._nameMap, languageName)) { + return [this._nameMap[languageName].language]; + } + return []; + } + + public getModeIdsFromFilenameOrFirstLine(filename: string, firstLine?: string): string[] { + if (!filename && !firstLine) { + return []; + } + var mimeTypes = mime.guessMimeTypes(filename, firstLine); + return this.extractModeIds(mimeTypes.join(',')); + } + + public getExtensions(languageName: string): string[] { + if (!hasOwnProperty.call(this._nameMap, languageName)) { + return []; + } + const languageId = this._nameMap[languageName]; + return this._languages[languageId.language].extensions; + } + + public getFilenames(languageName: string): string[] { + if (!hasOwnProperty.call(this._nameMap, languageName)) { + return []; + } + const languageId = this._nameMap[languageName]; + return this._languages[languageId.language].filenames; + } +} diff --git a/src/vs/editor/common/services/modeService.ts b/src/vs/editor/common/services/modeService.ts new file mode 100644 index 0000000000..7485089e0c --- /dev/null +++ b/src/vs/editor/common/services/modeService.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; + +export var IModeService = createDecorator('modeService'); + +export interface IModeLookupResult { + modeId: string; + isInstantiated: boolean; +} + +export interface ILanguageExtensionPoint { + id: string; + extensions?: string[]; + filenames?: string[]; + filenamePatterns?: string[]; + firstLine?: string; + aliases?: string[]; + mimetypes?: string[]; + configuration?: string; +} + +export interface IValidLanguageExtensionPoint { + id: string; + extensions: string[]; + filenames: string[]; + filenamePatterns: string[]; + firstLine: string; + aliases: string[]; + mimetypes: string[]; + configuration: string; +} + +export interface IModeService { + _serviceBrand: any; + + onDidCreateMode: Event; + + // --- reading + isRegisteredMode(mimetypeOrModeId: string): boolean; + getRegisteredModes(): string[]; + getRegisteredLanguageNames(): string[]; + getExtensions(alias: string): string[]; + getFilenames(alias: string): string[]; + getMimeForMode(modeId: string): string; + getLanguageName(modeId: string): string; + getModeIdForLanguageName(alias: string): string; + getModeIdByFilenameOrFirstLine(filename: string, firstLine?: string): string; + getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string; + getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier; + getConfigurationFiles(modeId: string): string[]; + + // --- instantiation + lookup(commaSeparatedMimetypesOrCommaSeparatedIds: string): IModeLookupResult[]; + getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): IMode; + getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): TPromise; + getOrCreateModeByLanguageName(languageName: string): TPromise; + getOrCreateModeByFilenameOrFirstLine(filename: string, firstLine?: string): TPromise; +} diff --git a/src/vs/editor/common/services/modeServiceImpl.ts b/src/vs/editor/common/services/modeServiceImpl.ts new file mode 100644 index 0000000000..f8aeeac5f9 --- /dev/null +++ b/src/vs/editor/common/services/modeServiceImpl.ts @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import Event, { Emitter } from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; +import { FrankensteinMode } from 'vs/editor/common/modes/abstractMode'; +import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry'; +import { IModeLookupResult, IModeService } from 'vs/editor/common/services/modeService'; + +export class ModeServiceImpl implements IModeService { + public _serviceBrand: any; + + private readonly _instantiatedModes: { [modeId: string]: IMode; }; + private readonly _registry: LanguagesRegistry; + + private readonly _onDidCreateMode: Emitter = new Emitter(); + public readonly onDidCreateMode: Event = this._onDidCreateMode.event; + + constructor() { + this._instantiatedModes = {}; + + this._registry = new LanguagesRegistry(); + } + + protected _onReady(): TPromise { + return TPromise.as(true); + } + + public isRegisteredMode(mimetypeOrModeId: string): boolean { + return this._registry.isRegisteredMode(mimetypeOrModeId); + } + + public getRegisteredModes(): string[] { + return this._registry.getRegisteredModes(); + } + + public getRegisteredLanguageNames(): string[] { + return this._registry.getRegisteredLanguageNames(); + } + + public getExtensions(alias: string): string[] { + return this._registry.getExtensions(alias); + } + + public getFilenames(alias: string): string[] { + return this._registry.getFilenames(alias); + } + + public getMimeForMode(modeId: string): string { + return this._registry.getMimeForMode(modeId); + } + + public getLanguageName(modeId: string): string { + return this._registry.getLanguageName(modeId); + } + + public getModeIdForLanguageName(alias: string): string { + return this._registry.getModeIdForLanguageNameLowercase(alias); + } + + public getModeIdByFilenameOrFirstLine(filename: string, firstLine?: string): string { + var modeIds = this._registry.getModeIdsFromFilenameOrFirstLine(filename, firstLine); + + if (modeIds.length > 0) { + return modeIds[0]; + } + + return null; + } + + public getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string { + var modeIds = this._registry.extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds); + + if (modeIds.length > 0) { + return modeIds[0]; + } + + return null; + } + + public getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier { + return this._registry.getLanguageIdentifier(modeId); + } + + public getConfigurationFiles(modeId: string): string[] { + return this._registry.getConfigurationFiles(modeId); + } + + // --- instantiation + + public lookup(commaSeparatedMimetypesOrCommaSeparatedIds: string): IModeLookupResult[] { + var r: IModeLookupResult[] = []; + var modeIds = this._registry.extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds); + + for (var i = 0; i < modeIds.length; i++) { + var modeId = modeIds[i]; + + r.push({ + modeId: modeId, + isInstantiated: this._instantiatedModes.hasOwnProperty(modeId) + }); + } + + return r; + } + + public getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): IMode { + var modeIds = this._registry.extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds); + + var isPlainText = false; + for (var i = 0; i < modeIds.length; i++) { + if (this._instantiatedModes.hasOwnProperty(modeIds[i])) { + return this._instantiatedModes[modeIds[i]]; + } + isPlainText = isPlainText || (modeIds[i] === 'plaintext'); + } + + if (isPlainText) { + // Try to do it synchronously + var r: IMode = null; + this.getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds).then((mode) => { + r = mode; + }).done(null, onUnexpectedError); + return r; + } + return null; + } + + public getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): TPromise { + return this._onReady().then(() => { + var modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds); + // Fall back to plain text if no mode was found + return this._getOrCreateMode(modeId || 'plaintext'); + }); + } + + public getOrCreateModeByLanguageName(languageName: string): TPromise { + return this._onReady().then(() => { + var modeId = this._getModeIdByLanguageName(languageName); + // Fall back to plain text if no mode was found + return this._getOrCreateMode(modeId || 'plaintext'); + }); + } + + private _getModeIdByLanguageName(languageName: string): string { + var modeIds = this._registry.getModeIdsFromLanguageName(languageName); + + if (modeIds.length > 0) { + return modeIds[0]; + } + + return null; + } + + public getOrCreateModeByFilenameOrFirstLine(filename: string, firstLine?: string): TPromise { + return this._onReady().then(() => { + var modeId = this.getModeIdByFilenameOrFirstLine(filename, firstLine); + // Fall back to plain text if no mode was found + return this._getOrCreateMode(modeId || 'plaintext'); + }); + } + + private _getOrCreateMode(modeId: string): IMode { + if (!this._instantiatedModes.hasOwnProperty(modeId)) { + let languageIdentifier = this.getLanguageIdentifier(modeId); + this._instantiatedModes[modeId] = new FrankensteinMode(languageIdentifier); + + this._onDidCreateMode.fire(this._instantiatedModes[modeId]); + } + return this._instantiatedModes[modeId]; + } +} diff --git a/src/vs/editor/common/services/modelService.ts b/src/vs/editor/common/services/modelService.ts new file mode 100644 index 0000000000..7527932bbc --- /dev/null +++ b/src/vs/editor/common/services/modelService.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event from 'vs/base/common/event'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IModel, ITextModelCreationOptions } from 'vs/editor/common/editorCommon'; +import { IMode } from 'vs/editor/common/modes'; +import { IRawTextSource } from 'vs/editor/common/model/textSource'; + +export var IModelService = createDecorator('modelService'); + +export interface IModelService { + _serviceBrand: any; + + createModel(value: string | IRawTextSource, modeOrPromise: TPromise | IMode, resource: URI): IModel; + + updateModel(model: IModel, value: string | IRawTextSource): void; + + setMode(model: IModel, modeOrPromise: TPromise | IMode): void; + + destroyModel(resource: URI): void; + + getModels(): IModel[]; + + getCreationOptions(language: string, resource: URI): ITextModelCreationOptions; + + getModel(resource: URI): IModel; + + onModelAdded: Event; + + onModelRemoved: Event; + + onModelModeChanged: Event<{ model: IModel; oldModeId: string; }>; +} + diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts new file mode 100644 index 0000000000..cdfe87c366 --- /dev/null +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -0,0 +1,595 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import network = require('vs/base/common/network'); +import Event, { Emitter } from 'vs/base/common/event'; +import { EmitterEvent } from 'vs/base/common/eventEmitter'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import Severity from 'vs/base/common/severity'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Model } from 'vs/editor/common/model/model'; +import { IMode, LanguageIdentifier } from 'vs/editor/common/modes'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import * as platform from 'vs/base/common/platform'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry'; +import { IRawTextSource, TextSource, RawTextSource, ITextSource } from 'vs/editor/common/model/textSource'; +import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; +import { ClassName } from 'vs/editor/common/model/textModelWithDecorations'; +import { ISequence, LcsDiff } from 'vs/base/common/diff/diff'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService'; +import { overviewRulerWarning, overviewRulerError } from 'vs/editor/common/view/editorColorRegistry'; + +function MODEL_ID(resource: URI): string { + return resource.toString(); +} + +class ModelData implements IDisposable { + model: editorCommon.IModel; + + private _markerDecorations: string[]; + private _modelEventsListener: IDisposable; + + constructor(model: editorCommon.IModel, eventsHandler: (modelData: ModelData, events: EmitterEvent[]) => void) { + this.model = model; + + this._markerDecorations = []; + this._modelEventsListener = model.addBulkListener((events) => eventsHandler(this, events)); + } + + public dispose(): void { + this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, []); + this._modelEventsListener.dispose(); + this._modelEventsListener = null; + this.model = null; + } + + public getModelId(): string { + return MODEL_ID(this.model.uri); + } + + public acceptMarkerDecorations(newDecorations: editorCommon.IModelDeltaDecoration[]): void { + this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, newDecorations); + } +} + +class ModelMarkerHandler { + + public static setMarkers(modelData: ModelData, markerService: IMarkerService): void { + + // Limit to the first 500 errors/warnings + const markers = markerService.read({ resource: modelData.model.uri, take: 500 }); + + let newModelDecorations: editorCommon.IModelDeltaDecoration[] = markers.map((marker) => { + return { + range: this._createDecorationRange(modelData.model, marker), + options: this._createDecorationOption(marker) + }; + }); + + modelData.acceptMarkerDecorations(newModelDecorations); + } + + private static _createDecorationRange(model: editorCommon.IModel, rawMarker: IMarker): Range { + let marker = model.validateRange(new Range(rawMarker.startLineNumber, rawMarker.startColumn, rawMarker.endLineNumber, rawMarker.endColumn)); + let ret: Range = new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn); + if (ret.isEmpty()) { + let word = model.getWordAtPosition(ret.getStartPosition()); + if (word) { + ret = new Range(ret.startLineNumber, word.startColumn, ret.endLineNumber, word.endColumn); + } else { + let maxColumn = model.getLineLastNonWhitespaceColumn(marker.startLineNumber) || + model.getLineMaxColumn(marker.startLineNumber); + + if (maxColumn === 1) { + // empty line + // console.warn('marker on empty line:', marker); + } else if (ret.endColumn >= maxColumn) { + // behind eol + ret = new Range(ret.startLineNumber, maxColumn - 1, ret.endLineNumber, maxColumn); + } else { + // extend marker to width = 1 + ret = new Range(ret.startLineNumber, ret.startColumn, ret.endLineNumber, ret.endColumn + 1); + } + } + } else if (rawMarker.endColumn === Number.MAX_VALUE && rawMarker.startColumn === 1 && ret.startLineNumber === ret.endLineNumber) { + let minColumn = model.getLineFirstNonWhitespaceColumn(rawMarker.startLineNumber); + if (minColumn < ret.endColumn) { + ret = new Range(ret.startLineNumber, minColumn, ret.endLineNumber, ret.endColumn); + rawMarker.startColumn = minColumn; + } + } + return ret; + } + + private static _createDecorationOption(marker: IMarker): editorCommon.IModelDecorationOptions { + + let className: string; + let color: ThemeColor; + let darkColor: ThemeColor; + + switch (marker.severity) { + case Severity.Ignore: + // do something + break; + case Severity.Warning: + case Severity.Info: + className = ClassName.EditorWarningDecoration; + color = themeColorFromId(overviewRulerWarning); + darkColor = themeColorFromId(overviewRulerWarning); + break; + case Severity.Error: + default: + className = ClassName.EditorErrorDecoration; + color = themeColorFromId(overviewRulerError); + darkColor = themeColorFromId(overviewRulerError); + break; + } + + let hoverMessage: MarkdownString = null; + let { message, source } = marker; + + if (typeof message === 'string') { + message = message.trim(); + + if (source) { + if (/\n/g.test(message)) { + message = nls.localize('diagAndSourceMultiline', "[{0}]\n{1}", source, message); + } else { + message = nls.localize('diagAndSource', "[{0}] {1}", source, message); + } + } + + hoverMessage = new MarkdownString().appendCodeblock('_', message); + } + + return { + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className, + hoverMessage, + showIfCollapsed: true, + overviewRuler: { + color, + darkColor, + position: editorCommon.OverviewRulerLane.Right + } + }; + } +} + +interface IRawConfig { + files?: { + eol?: any; + }; + editor?: { + tabSize?: any; + insertSpaces?: any; + detectIndentation?: any; + trimAutoWhitespace?: any; + }; +} + +const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? editorCommon.DefaultEndOfLine.LF : editorCommon.DefaultEndOfLine.CRLF; + +export class ModelServiceImpl implements IModelService { + public _serviceBrand: any; + + private _markerService: IMarkerService; + private _markerServiceSubscription: IDisposable; + private _configurationService: IConfigurationService; + private _configurationServiceSubscription: IDisposable; + + private _onModelAdded: Emitter; + private _onModelRemoved: Emitter; + private _onModelModeChanged: Emitter<{ model: editorCommon.IModel; oldModeId: string; }>; + + private _modelCreationOptionsByLanguageAndResource: { + [languageAndResource: string]: editorCommon.ITextModelCreationOptions; + }; + + /** + * All the models known in the system. + */ + private _models: { [modelId: string]: ModelData; }; + + constructor( + @IMarkerService markerService: IMarkerService, + @IConfigurationService configurationService: IConfigurationService, + ) { + this._markerService = markerService; + this._configurationService = configurationService; + this._models = {}; + this._modelCreationOptionsByLanguageAndResource = Object.create(null); + this._onModelAdded = new Emitter(); + this._onModelRemoved = new Emitter(); + this._onModelModeChanged = new Emitter<{ model: editorCommon.IModel; oldModeId: string; }>(); + + if (this._markerService) { + this._markerServiceSubscription = this._markerService.onMarkerChanged(this._handleMarkerChange, this); + } + + this._configurationServiceSubscription = this._configurationService.onDidUpdateConfiguration(e => this._updateModelOptions()); + this._updateModelOptions(); + } + + private static _readModelOptions(config: IRawConfig): editorCommon.ITextModelCreationOptions { + let tabSize = EDITOR_MODEL_DEFAULTS.tabSize; + if (config.editor && typeof config.editor.tabSize !== 'undefined') { + let parsedTabSize = parseInt(config.editor.tabSize, 10); + if (!isNaN(parsedTabSize)) { + tabSize = parsedTabSize; + } + } + + let insertSpaces = EDITOR_MODEL_DEFAULTS.insertSpaces; + if (config.editor && typeof config.editor.insertSpaces !== 'undefined') { + insertSpaces = (config.editor.insertSpaces === 'false' ? false : Boolean(config.editor.insertSpaces)); + } + + let newDefaultEOL = DEFAULT_EOL; + const eol = config.files && config.files.eol; + if (eol === '\r\n') { + newDefaultEOL = editorCommon.DefaultEndOfLine.CRLF; + } else if (eol === '\n') { + newDefaultEOL = editorCommon.DefaultEndOfLine.LF; + } + + let trimAutoWhitespace = EDITOR_MODEL_DEFAULTS.trimAutoWhitespace; + if (config.editor && typeof config.editor.trimAutoWhitespace !== 'undefined') { + trimAutoWhitespace = (config.editor.trimAutoWhitespace === 'false' ? false : Boolean(config.editor.trimAutoWhitespace)); + } + + let detectIndentation = EDITOR_MODEL_DEFAULTS.detectIndentation; + if (config.editor && typeof config.editor.detectIndentation !== 'undefined') { + detectIndentation = (config.editor.detectIndentation === 'false' ? false : Boolean(config.editor.detectIndentation)); + } + + return { + tabSize: tabSize, + insertSpaces: insertSpaces, + detectIndentation: detectIndentation, + defaultEOL: newDefaultEOL, + trimAutoWhitespace: trimAutoWhitespace + }; + } + + public getCreationOptions(language: string, resource: URI): editorCommon.ITextModelCreationOptions { + let creationOptions = this._modelCreationOptionsByLanguageAndResource[language + resource]; + if (!creationOptions) { + creationOptions = ModelServiceImpl._readModelOptions(this._configurationService.getConfiguration(null, { overrideIdentifier: language, resource })); + this._modelCreationOptionsByLanguageAndResource[language + resource] = creationOptions; + } + return creationOptions; + } + + private _updateModelOptions(): void { + let oldOptionsByLanguageAndResource = this._modelCreationOptionsByLanguageAndResource; + this._modelCreationOptionsByLanguageAndResource = Object.create(null); + + // Update options on all models + let keys = Object.keys(this._models); + for (let i = 0, len = keys.length; i < len; i++) { + let modelId = keys[i]; + let modelData = this._models[modelId]; + const language = modelData.model.getLanguageIdentifier().language; + const uri = modelData.model.uri; + const oldOptions = oldOptionsByLanguageAndResource[language + uri]; + const newOptions = this.getCreationOptions(language, uri); + ModelServiceImpl._setModelOptionsForModel(modelData.model, newOptions, oldOptions); + } + } + + private static _setModelOptionsForModel(model: editorCommon.IModel, newOptions: editorCommon.ITextModelCreationOptions, currentOptions: editorCommon.ITextModelCreationOptions): void { + if (currentOptions + && (currentOptions.detectIndentation === newOptions.detectIndentation) + && (currentOptions.insertSpaces === newOptions.insertSpaces) + && (currentOptions.tabSize === newOptions.tabSize) + && (currentOptions.trimAutoWhitespace === newOptions.trimAutoWhitespace) + ) { + // Same indent opts, no need to touch the model + return; + } + + if (newOptions.detectIndentation) { + model.detectIndentation(newOptions.insertSpaces, newOptions.tabSize); + model.updateOptions({ + trimAutoWhitespace: newOptions.trimAutoWhitespace + }); + } else { + model.updateOptions({ + insertSpaces: newOptions.insertSpaces, + tabSize: newOptions.tabSize, + trimAutoWhitespace: newOptions.trimAutoWhitespace + }); + } + } + + public dispose(): void { + if (this._markerServiceSubscription) { + this._markerServiceSubscription.dispose(); + } + this._configurationServiceSubscription.dispose(); + } + + private _handleMarkerChange(changedResources: URI[]): void { + changedResources.forEach((resource) => { + let modelId = MODEL_ID(resource); + let modelData = this._models[modelId]; + if (!modelData) { + return; + } + ModelMarkerHandler.setMarkers(modelData, this._markerService); + }); + } + + private _cleanUp(model: editorCommon.IModel): void { + // clean up markers for internal, transient models + if (model.uri.scheme === network.Schemas.inMemory + || model.uri.scheme === network.Schemas.internal + || model.uri.scheme === network.Schemas.vscode) { + if (this._markerService) { + this._markerService.read({ resource: model.uri }).map(marker => marker.owner).forEach(owner => this._markerService.remove(owner, [model.uri])); + } + } + + // clean up cache + delete this._modelCreationOptionsByLanguageAndResource[model.getLanguageIdentifier().language + model.uri]; + } + + // --- begin IModelService + + private _createModelData(value: string | IRawTextSource, languageIdentifier: LanguageIdentifier, resource: URI): ModelData { + // create & save the model + const options = this.getCreationOptions(languageIdentifier.language, resource); + const rawTextSource = (typeof value === 'string' ? RawTextSource.fromString(value) : value); + let model: Model = new Model(rawTextSource, options, languageIdentifier, resource); + let modelId = MODEL_ID(model.uri); + + if (this._models[modelId]) { + // There already exists a model with this id => this is a programmer error + throw new Error('ModelService: Cannot add model because it already exists!'); + } + + let modelData = new ModelData(model, (modelData, events) => this._onModelEvents(modelData, events)); + this._models[modelId] = modelData; + + return modelData; + } + + public updateModel(model: editorCommon.IModel, value: string | IRawTextSource): void { + let options = this.getCreationOptions(model.getLanguageIdentifier().language, model.uri); + const textSource = TextSource.create(value, options.defaultEOL); + + // Return early if the text is already set in that form + if (model.equals(textSource)) { + return; + } + + // Otherwise find a diff between the values and update model + model.setEOL(textSource.EOL === '\r\n' ? editorCommon.EndOfLineSequence.CRLF : editorCommon.EndOfLineSequence.LF); + model.pushEditOperations( + [new Selection(1, 1, 1, 1)], + ModelServiceImpl._computeEdits(model, textSource), + (inverseEditOperations: editorCommon.IIdentifiedSingleEditOperation[]) => [new Selection(1, 1, 1, 1)] + ); + } + + /** + * Compute edits to bring `model` to the state of `textSource`. + */ + public static _computeEdits(model: editorCommon.IModel, textSource: ITextSource): editorCommon.IIdentifiedSingleEditOperation[] { + const modelLineSequence = new class implements ISequence { + public getLength(): number { + return model.getLineCount(); + } + public getElementHash(index: number): string { + return model.getLineContent(index + 1); + } + }; + const textSourceLineSequence = new class implements ISequence { + public getLength(): number { + return textSource.lines.length; + } + public getElementHash(index: number): string { + return textSource.lines[index]; + } + }; + + const diffResult = new LcsDiff(modelLineSequence, textSourceLineSequence).ComputeDiff(false); + + let edits: editorCommon.IIdentifiedSingleEditOperation[] = [], editsLen = 0; + const modelLineCount = model.getLineCount(); + for (let i = 0, len = diffResult.length; i < len; i++) { + const diff = diffResult[i]; + const originalStart = diff.originalStart; + const originalLength = diff.originalLength; + const modifiedStart = diff.modifiedStart; + const modifiedLength = diff.modifiedLength; + + let lines: string[] = []; + for (let j = 0; j < modifiedLength; j++) { + lines[j] = textSource.lines[modifiedStart + j]; + } + let text = lines.join('\n'); + + let range: Range; + if (originalLength === 0) { + // insertion + + if (originalStart === modelLineCount) { + // insert at the end + const maxLineColumn = model.getLineMaxColumn(modelLineCount); + range = new Range( + modelLineCount, maxLineColumn, + modelLineCount, maxLineColumn + ); + text = '\n' + text; + } else { + // insert + range = new Range( + originalStart + 1, 1, + originalStart + 1, 1 + ); + text = text + '\n'; + } + + } else if (modifiedLength === 0) { + // deletion + + if (originalStart + originalLength >= modelLineCount) { + // delete at the end + range = new Range( + originalStart, model.getLineMaxColumn(originalStart), + originalStart + originalLength, model.getLineMaxColumn(originalStart + originalLength) + ); + } else { + // delete + range = new Range( + originalStart + 1, 1, + originalStart + originalLength + 1, 1 + ); + } + + } else { + // modification + range = new Range( + originalStart + 1, 1, + originalStart + originalLength, model.getLineMaxColumn(originalStart + originalLength) + ); + } + + edits[editsLen++] = EditOperation.replace(range, text); + } + + return edits; + } + + public createModel(value: string | IRawTextSource, modeOrPromise: TPromise | IMode, resource: URI): editorCommon.IModel { + let modelData: ModelData; + + if (!modeOrPromise || TPromise.is(modeOrPromise)) { + modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource); + this.setMode(modelData.model, modeOrPromise); + } else { + modelData = this._createModelData(value, modeOrPromise.getLanguageIdentifier(), resource); + } + + // handle markers (marker service => model) + if (this._markerService) { + ModelMarkerHandler.setMarkers(modelData, this._markerService); + } + + this._onModelAdded.fire(modelData.model); + + return modelData.model; + } + + public setMode(model: editorCommon.IModel, modeOrPromise: TPromise | IMode): void { + if (!modeOrPromise) { + return; + } + if (TPromise.is(modeOrPromise)) { + modeOrPromise.then((mode) => { + if (!model.isDisposed()) { + model.setMode(mode.getLanguageIdentifier()); + } + }); + } else { + model.setMode(modeOrPromise.getLanguageIdentifier()); + } + } + + public destroyModel(resource: URI): void { + // We need to support that not all models get disposed through this service (i.e. model.dispose() should work!) + let modelData = this._models[MODEL_ID(resource)]; + if (!modelData) { + return; + } + modelData.model.dispose(); + } + + public getModels(): editorCommon.IModel[] { + let ret: editorCommon.IModel[] = []; + + let keys = Object.keys(this._models); + for (let i = 0, len = keys.length; i < len; i++) { + let modelId = keys[i]; + ret.push(this._models[modelId].model); + } + + return ret; + } + + public getModel(resource: URI): editorCommon.IModel { + let modelId = MODEL_ID(resource); + let modelData = this._models[modelId]; + if (!modelData) { + return null; + } + return modelData.model; + } + + public get onModelAdded(): Event { + return this._onModelAdded ? this._onModelAdded.event : null; + } + + public get onModelRemoved(): Event { + return this._onModelRemoved ? this._onModelRemoved.event : null; + } + + public get onModelModeChanged(): Event<{ model: editorCommon.IModel; oldModeId: string; }> { + return this._onModelModeChanged ? this._onModelModeChanged.event : null; + } + + // --- end IModelService + + private _onModelDisposing(model: editorCommon.IModel): void { + let modelId = MODEL_ID(model.uri); + let modelData = this._models[modelId]; + + delete this._models[modelId]; + modelData.dispose(); + + this._cleanUp(model); + this._onModelRemoved.fire(model); + } + + private _onModelEvents(modelData: ModelData, events: EmitterEvent[]): void { + + // First look for dispose + for (let i = 0, len = events.length; i < len; i++) { + let e = events[i]; + if (e.type === textModelEvents.TextModelEventType.ModelDispose) { + this._onModelDisposing(modelData.model); + // no more processing since model got disposed + return; + } + } + + // Second, look for mode change + for (let i = 0, len = events.length; i < len; i++) { + let e = events[i]; + if (e.type === textModelEvents.TextModelEventType.ModelLanguageChanged) { + const model = modelData.model; + const oldModeId = (e.data).oldLanguage; + const newModeId = model.getLanguageIdentifier().language; + const oldOptions = this.getCreationOptions(oldModeId, model.uri); + const newOptions = this.getCreationOptions(newModeId, model.uri); + ModelServiceImpl._setModelOptionsForModel(model, newOptions, oldOptions); + this._onModelModeChanged.fire({ model, oldModeId }); + } + } + } +} diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/services/resolverService.ts new file mode 100644 index 0000000000..208ba3856f --- /dev/null +++ b/src/vs/editor/common/services/resolverService.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IEditorModel } from 'vs/platform/editor/common/editor'; +import { IDisposable, IReference } from 'vs/base/common/lifecycle'; + +export const ITextModelService = createDecorator('textModelService'); + +export interface ITextModelService { + _serviceBrand: any; + + /** + * Provided a resource URI, it will return a model reference + * which should be disposed once not needed anymore. + */ + createModelReference(resource: URI): TPromise>; + + /** + * Registers a specific `scheme` content provider. + */ + registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable; +} + +export interface ITextModelContentProvider { + + /** + * Given a resource, return the content of the resource as IModel. + */ + provideTextContent(resource: URI): TPromise; +} + +export interface ITextEditorModel extends IEditorModel { + + /** + * Provides access to the underlying IModel. + */ + textEditorModel: IModel; +} diff --git a/src/vs/editor/common/services/resourceConfiguration.ts b/src/vs/editor/common/services/resourceConfiguration.ts new file mode 100644 index 0000000000..3d9cd4804d --- /dev/null +++ b/src/vs/editor/common/services/resourceConfiguration.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Event from 'vs/base/common/event'; +import URI from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IPosition } from 'vs/editor/common/core/position'; + +export const ITextResourceConfigurationService = createDecorator('textResourceConfigurationService'); + +export interface ITextResourceConfigurationService { + + _serviceBrand: any; + + /** + * Event that fires when the configuration changes. + */ + onDidUpdateConfiguration: Event; + + /** + * Fetches the appropriate section of the for the given resource with appropriate overrides (e.g. language). + * This will be an object keyed off the section name. + * + * @param resource - Resource for which the configuration has to be fetched. Can be `null` or `undefined`. + * @param postion - Position in the resource for which configuration has to be fetched. Can be `null` or `undefined`. + * @param section - Section of the configuraion. Can be `null` or `undefined`. + * + */ + getConfiguration(resource: URI, section?: string): T; + getConfiguration(resource: URI, position?: IPosition, section?: string): T; + +} \ No newline at end of file diff --git a/src/vs/editor/common/services/resourceConfigurationImpl.ts b/src/vs/editor/common/services/resourceConfigurationImpl.ts new file mode 100644 index 0000000000..ad58d0e0d3 --- /dev/null +++ b/src/vs/editor/common/services/resourceConfigurationImpl.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Event, { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import URI from 'vs/base/common/uri'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModelService } from 'vs/editor/common/services/modelService'; + +export class TextResourceConfigurationService extends Disposable implements ITextResourceConfigurationService { + + public _serviceBrand: any; + + private readonly _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; + + constructor( + @IConfigurationService private configurationService: IConfigurationService, + @IModelService private modelService: IModelService, + @IModeService private modeService: IModeService, + ) { + super(); + this._register(this.configurationService.onDidUpdateConfiguration(() => this._onDidUpdateConfiguration.fire())); + } + + getConfiguration(resource: URI, section?: string): T + getConfiguration(resource: URI, at?: IPosition, section?: string): T + getConfiguration(resource: URI, arg2?: any, arg3?: any): T { + const position: IPosition = Position.isIPosition(arg2) ? arg2 : null; + const section: string = position ? (typeof arg3 === 'string' ? arg3 : void 0) : (typeof arg2 === 'string' ? arg2 : void 0); + const language = resource ? this.getLanguage(resource, position) : void 0; + return this.configurationService.getConfiguration(section, { resource, overrideIdentifier: language }); + } + + private getLanguage(resource: URI, position: IPosition): string { + const model = this.modelService.getModel(resource); + if (model) { + return position ? this.modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(position.lineNumber, position.column)).language : model.getLanguageIdentifier().language; + } + return this.modeService.getModeIdByFilenameOrFirstLine(resource.fsPath); + } +} \ No newline at end of file diff --git a/src/vs/editor/common/services/webWorker.ts b/src/vs/editor/common/services/webWorker.ts new file mode 100644 index 0000000000..79152dd348 --- /dev/null +++ b/src/vs/editor/common/services/webWorker.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ShallowCancelThenPromise } from 'vs/base/common/async'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { EditorWorkerClient } from 'vs/editor/common/services/editorWorkerServiceImpl'; + +/** + * Create a new web worker that has model syncing capabilities built in. + * Specify an AMD module to load that will `create` an object that will be proxied. + */ +export function createWebWorker(modelService: IModelService, opts: IWebWorkerOptions): MonacoWebWorker { + return new MonacoWebWorkerImpl(modelService, opts); +} + +/** + * A web worker that can provide a proxy to an arbitrary file. + */ +export interface MonacoWebWorker { + /** + * Terminate the web worker, thus invalidating the returned proxy. + */ + dispose(): void; + /** + * Get a proxy to the arbitrary loaded code. + */ + getProxy(): TPromise; + /** + * Synchronize (send) the models at `resources` to the web worker, + * making them available in the monaco.worker.getMirrorModels(). + */ + withSyncedResources(resources: URI[]): TPromise; +} + +export interface IWebWorkerOptions { + /** + * The AMD moduleId to load. + * It should export a function `create` that should return the exported proxy. + */ + moduleId: string; + /** + * The data to send over when calling create on the module. + */ + createData?: any; + /** + * A label to be used to identify the web worker for debugging purposes. + */ + label?: string; +} + +class MonacoWebWorkerImpl extends EditorWorkerClient implements MonacoWebWorker { + + private _foreignModuleId: string; + private _foreignModuleCreateData: any; + private _foreignProxy: TPromise; + + constructor(modelService: IModelService, opts: IWebWorkerOptions) { + super(modelService, opts.label); + this._foreignModuleId = opts.moduleId; + this._foreignModuleCreateData = opts.createData || null; + this._foreignProxy = null; + } + + private _getForeignProxy(): TPromise { + if (!this._foreignProxy) { + this._foreignProxy = new ShallowCancelThenPromise(this._getProxy().then((proxy) => { + return proxy.loadForeignModule(this._foreignModuleId, this._foreignModuleCreateData).then((foreignMethods) => { + this._foreignModuleId = null; + this._foreignModuleCreateData = null; + + let proxyMethodRequest = (method: string, args: any[]): TPromise => { + return proxy.fmr(method, args); + }; + + let createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => TPromise): Function => { + return function () { + let args = Array.prototype.slice.call(arguments, 0); + return proxyMethodRequest(method, args); + }; + }; + + let foreignProxy = {}; + for (let i = 0; i < foreignMethods.length; i++) { + foreignProxy[foreignMethods[i]] = createProxyMethod(foreignMethods[i], proxyMethodRequest); + } + + return foreignProxy; + }); + })); + } + return this._foreignProxy; + } + + public getProxy(): TPromise { + return this._getForeignProxy(); + } + + public withSyncedResources(resources: URI[]): TPromise { + return this._withSyncedResources(resources).then(_ => this.getProxy()); + } +} diff --git a/src/vs/editor/common/standalone/standaloneBase.ts b/src/vs/editor/common/standalone/standaloneBase.ts new file mode 100644 index 0000000000..3cf595724f --- /dev/null +++ b/src/vs/editor/common/standalone/standaloneBase.ts @@ -0,0 +1,245 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Emitter } from 'vs/base/common/event'; +import { KeyMod as ConstKeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Token } from 'vs/editor/common/core/token'; +import URI from 'vs/base/common/uri'; + +// -------------------------------------------- +// This is repeated here so it can be exported +// because TS inlines const enums +// -------------------------------------------- +export enum Severity { + Ignore = 0, + Info = 1, + Warning = 2, + Error = 3, +} + +// -------------------------------------------- +// This is repeated here so it can be exported +// because TS inlines const enums +// -------------------------------------------- +export class KeyMod { + public static readonly CtrlCmd: number = ConstKeyMod.CtrlCmd; + public static readonly Shift: number = ConstKeyMod.Shift; + public static readonly Alt: number = ConstKeyMod.Alt; + public static readonly WinCtrl: number = ConstKeyMod.WinCtrl; + + public static chord(firstPart: number, secondPart: number): number { + return KeyChord(firstPart, secondPart); + } +} + +// -------------------------------------------- +// This is repeated here so it can be exported +// because TS inlines const enums +// -------------------------------------------- +/** + * Virtual Key Codes, the value does not hold any inherent meaning. + * Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx + * But these are "more general", as they should work across browsers & OS`s. + */ +export enum KeyCode { + /** + * Placed first to cover the 0 value of the enum. + */ + Unknown = 0, + Backspace = 1, + Tab = 2, + Enter = 3, + Shift = 4, + Ctrl = 5, + Alt = 6, + PauseBreak = 7, + CapsLock = 8, + Escape = 9, + Space = 10, + PageUp = 11, + PageDown = 12, + End = 13, + Home = 14, + LeftArrow = 15, + UpArrow = 16, + RightArrow = 17, + DownArrow = 18, + Insert = 19, + Delete = 20, + KEY_0 = 21, + KEY_1 = 22, + KEY_2 = 23, + KEY_3 = 24, + KEY_4 = 25, + KEY_5 = 26, + KEY_6 = 27, + KEY_7 = 28, + KEY_8 = 29, + KEY_9 = 30, + KEY_A = 31, + KEY_B = 32, + KEY_C = 33, + KEY_D = 34, + KEY_E = 35, + KEY_F = 36, + KEY_G = 37, + KEY_H = 38, + KEY_I = 39, + KEY_J = 40, + KEY_K = 41, + KEY_L = 42, + KEY_M = 43, + KEY_N = 44, + KEY_O = 45, + KEY_P = 46, + KEY_Q = 47, + KEY_R = 48, + KEY_S = 49, + KEY_T = 50, + KEY_U = 51, + KEY_V = 52, + KEY_W = 53, + KEY_X = 54, + KEY_Y = 55, + KEY_Z = 56, + Meta = 57, + ContextMenu = 58, + F1 = 59, + F2 = 60, + F3 = 61, + F4 = 62, + F5 = 63, + F6 = 64, + F7 = 65, + F8 = 66, + F9 = 67, + F10 = 68, + F11 = 69, + F12 = 70, + F13 = 71, + F14 = 72, + F15 = 73, + F16 = 74, + F17 = 75, + F18 = 76, + F19 = 77, + NumLock = 78, + ScrollLock = 79, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the ';:' key + */ + US_SEMICOLON = 80, + /** + * For any country/region, the '+' key + * For the US standard keyboard, the '=+' key + */ + US_EQUAL = 81, + /** + * For any country/region, the ',' key + * For the US standard keyboard, the ',<' key + */ + US_COMMA = 82, + /** + * For any country/region, the '-' key + * For the US standard keyboard, the '-_' key + */ + US_MINUS = 83, + /** + * For any country/region, the '.' key + * For the US standard keyboard, the '.>' key + */ + US_DOT = 84, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the '/?' key + */ + US_SLASH = 85, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the '`~' key + */ + US_BACKTICK = 86, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the '[{' key + */ + US_OPEN_SQUARE_BRACKET = 87, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the '\|' key + */ + US_BACKSLASH = 88, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the ']}' key + */ + US_CLOSE_SQUARE_BRACKET = 89, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the ''"' key + */ + US_QUOTE = 90, + /** + * Used for miscellaneous characters; it can vary by keyboard. + */ + OEM_8 = 91, + /** + * Either the angle bracket key or the backslash key on the RT 102-key keyboard. + */ + OEM_102 = 92, + NUMPAD_0 = 93, + NUMPAD_1 = 94, + NUMPAD_2 = 95, + NUMPAD_3 = 96, + NUMPAD_4 = 97, + NUMPAD_5 = 98, + NUMPAD_6 = 99, + NUMPAD_7 = 100, + NUMPAD_8 = 101, + NUMPAD_9 = 102, + NUMPAD_MULTIPLY = 103, + NUMPAD_ADD = 104, + NUMPAD_SEPARATOR = 105, + NUMPAD_SUBTRACT = 106, + NUMPAD_DECIMAL = 107, + NUMPAD_DIVIDE = 108, + /** + * Cover all key codes when IME is processing input. + */ + KEY_IN_COMPOSITION = 109, + ABNT_C1 = 110, + ABNT_C2 = 111, + /** + * Placed last to cover the length of the enum. + * Please do not depend on this value! + */ + MAX_VALUE +} + +export function createMonacoBaseAPI(): typeof monaco { + return { + editor: undefined, + languages: undefined, + CancellationTokenSource: CancellationTokenSource, + Emitter: Emitter, + KeyCode: KeyCode, + KeyMod: KeyMod, + Position: Position, + Range: Range, + Selection: Selection, + SelectionDirection: SelectionDirection, + Severity: Severity, + Promise: TPromise, + Uri: URI, + Token: Token + }; +} diff --git a/src/vs/editor/common/view/editorColorRegistry.ts b/src/vs/editor/common/view/editorColorRegistry.ts new file mode 100644 index 0000000000..10ba88303e --- /dev/null +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import { registerColor, editorBackground, activeContrastBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { Color, RGBA } from 'vs/base/common/color'; + +/** + * Definition of the editor colors + */ +export const editorLineHighlight = registerColor('editor.lineHighlightBackground', { dark: null, light: null, hc: null }, nls.localize('lineHighlight', 'Background color for the highlight of line at the cursor position.')); +export const editorLineHighlightBorder = registerColor('editor.lineHighlightBorder', { dark: '#282828', light: '#eeeeee', hc: '#f38518' }, nls.localize('lineHighlightBorderBox', 'Background color for the border around the line at the cursor position.')); +export const editorRangeHighlight = registerColor('editor.rangeHighlightBackground', { dark: '#ffffff0b', light: '#fdff0033', hc: null }, nls.localize('rangeHighlight', 'Background color of highlighted ranges, like by quick open and find features.')); +export const editorCursorForeground = registerColor('editorCursor.foreground', { dark: '#AEAFAD', light: Color.black, hc: Color.white }, nls.localize('caret', 'Color of the editor cursor.')); +export const editorCursorBackground = registerColor('editorCursor.background', null, nls.localize('editorCursorBackground', 'The background color of the editor cursor. Allows customizing the color of a character overlapped by a block cursor.')); +export const editorWhitespaces = registerColor('editorWhitespace.foreground', { dark: '#e3e4e229', light: '#33333333', hc: '#e3e4e229' }, nls.localize('editorWhitespaces', 'Color of whitespace characters in the editor.')); +export const editorIndentGuides = registerColor('editorIndentGuide.background', { dark: editorWhitespaces, light: editorWhitespaces, hc: editorWhitespaces }, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.')); +export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#5A5A5A', light: '#2B91AF', hc: Color.white }, nls.localize('editorLineNumbers', 'Color of editor line numbers.')); +export const editorRuler = registerColor('editorRuler.foreground', { dark: '#5A5A5A', light: Color.lightgrey, hc: Color.white }, nls.localize('editorRuler', 'Color of the editor rulers.')); + +export const editorCodeLensForeground = registerColor('editorCodeLens.foreground', { dark: '#999999', light: '#999999', hc: '#999999' }, nls.localize('editorCodeLensForeground', 'Foreground color of editor code lenses')); + +export const editorBracketMatchBackground = registerColor('editorBracketMatch.background', { dark: '#0064001a', light: '#0064001a', hc: '#0064001a' }, nls.localize('editorBracketMatchBackground', 'Background color behind matching brackets')); +export const editorBracketMatchBorder = registerColor('editorBracketMatch.border', { dark: '#888', light: '#B9B9B9', hc: '#fff' }, nls.localize('editorBracketMatchBorder', 'Color for matching brackets boxes')); + +export const editorOverviewRulerBorder = registerColor('editorOverviewRuler.border', { dark: '#7f7f7f4d', light: '#7f7f7f4d', hc: '#7f7f7f4d' }, nls.localize('editorOverviewRulerBorder', 'Color of the overview ruler border.')); + +export const editorGutter = registerColor('editorGutter.background', { dark: editorBackground, light: editorBackground, hc: editorBackground }, nls.localize('editorGutter', 'Background color of the editor gutter. The gutter contains the glyph margins and the line numbers.')); + +export const editorErrorForeground = registerColor('editorError.foreground', { dark: '#FF0000', light: '#FF0000', hc: null }, nls.localize('errorForeground', 'Foreground color of error squigglies in the editor.')); +export const editorErrorBorder = registerColor('editorError.border', { dark: null, light: null, hc: Color.fromHex('#E47777').transparent(0.8) }, nls.localize('errorBorder', 'Border color of error squigglies in the editor.')); + +export const editorWarningForeground = registerColor('editorWarning.foreground', { dark: '#008000', light: '#008000', hc: null }, nls.localize('warningForeground', 'Foreground color of warning squigglies in the editor.')); +export const editorWarningBorder = registerColor('editorWarning.border', { dark: null, light: null, hc: Color.fromHex('#71B771').transparent(0.8) }, nls.localize('warningBorder', 'Border color of warning squigglies in the editor.')); + +const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6)); +export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hc: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights.')); +export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hc: new Color(new RGBA(255, 50, 50, 1)) }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.')); +export const overviewRulerWarning = registerColor('editorOverviewRuler.warningForeground', { dark: new Color(new RGBA(18, 136, 18, 0.7)), light: new Color(new RGBA(18, 136, 18, 0.7)), hc: new Color(new RGBA(50, 255, 50, 1)) }, nls.localize('overviewRuleWarning', 'Overview ruler marker color for warnings.')); +export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForeground', { dark: new Color(new RGBA(18, 18, 136, 0.7)), light: new Color(new RGBA(18, 18, 136, 0.7)), hc: new Color(new RGBA(50, 50, 255, 1)) }, nls.localize('overviewRuleInfo', 'Overview ruler marker color for infos.')); + + +// contains all color rules that used to defined in editor/browser/widget/editor.css +registerThemingParticipant((theme, collector) => { + let background = theme.getColor(editorBackground); + if (background) { + collector.addRule(`.monaco-editor, .monaco-editor-background, .monaco-editor .inputarea.ime-input { background-color: ${background}; }`); + } + let foreground = theme.getColor(editorForeground); + if (foreground) { + collector.addRule(`.monaco-editor, .monaco-editor .inputarea.ime-input { color: ${foreground}; }`); + } + + let gutter = theme.getColor(editorGutter); + if (gutter) { + collector.addRule(`.monaco-editor .margin { background-color: ${gutter}; }`); + } + + let rangeHighlight = theme.getColor(editorRangeHighlight); + if (rangeHighlight) { + collector.addRule(`.monaco-editor .rangeHighlight { background-color: ${rangeHighlight}; }`); + } + let outline = theme.getColor(activeContrastBorder); + if (outline) { + collector.addRule(`.monaco-editor .rangeHighlight { border: 1px dotted ${outline}; }`); + } + + let invisibles = theme.getColor(editorWhitespaces); + if (invisibles) { + collector.addRule(`.vs-whitespace { color: ${invisibles} !important; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/common/view/minimapCharRenderer.ts b/src/vs/editor/common/view/minimapCharRenderer.ts new file mode 100644 index 0000000000..0b27e50bff --- /dev/null +++ b/src/vs/editor/common/view/minimapCharRenderer.ts @@ -0,0 +1,350 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ColorId, TokenizationRegistry } from 'vs/editor/common/modes'; +import Event, { Emitter } from 'vs/base/common/event'; +import { RGBA8 } from 'vs/editor/common/core/rgba'; + +export class MinimapTokensColorTracker { + private static _INSTANCE: MinimapTokensColorTracker = null; + public static getInstance(): MinimapTokensColorTracker { + if (!this._INSTANCE) { + this._INSTANCE = new MinimapTokensColorTracker(); + } + return this._INSTANCE; + } + + private _colors: RGBA8[]; + private _backgroundIsLight: boolean; + + private _onDidChange = new Emitter(); + public onDidChange: Event = this._onDidChange.event; + + private constructor() { + this._updateColorMap(); + TokenizationRegistry.onDidChange((e) => { + if (e.changedColorMap) { + this._updateColorMap(); + } + }); + } + + private _updateColorMap(): void { + const colorMap = TokenizationRegistry.getColorMap(); + if (!colorMap) { + this._colors = [null]; + this._backgroundIsLight = true; + return; + } + this._colors = [null]; + for (let colorId = 1; colorId < colorMap.length; colorId++) { + const source = colorMap[colorId].rgba; + // Use a VM friendly data-type + this._colors[colorId] = new RGBA8(source.r, source.g, source.b, Math.round(source.a * 255)); + } + let backgroundLuminosity = colorMap[ColorId.DefaultBackground].getRelativeLuminance(); + this._backgroundIsLight = (backgroundLuminosity >= 0.5); + this._onDidChange.fire(void 0); + } + + public getColor(colorId: ColorId): RGBA8 { + if (colorId < 1 || colorId >= this._colors.length) { + // background color (basically invisible) + colorId = ColorId.DefaultBackground; + } + return this._colors[colorId]; + } + + public backgroundIsLight(): boolean { + return this._backgroundIsLight; + } +} + +export const enum Constants { + START_CH_CODE = 32, // Space + END_CH_CODE = 126, // Tilde (~) + CHAR_COUNT = END_CH_CODE - START_CH_CODE + 1, + + SAMPLED_CHAR_HEIGHT = 16, + SAMPLED_CHAR_WIDTH = 10, + SAMPLED_HALF_CHAR_WIDTH = SAMPLED_CHAR_WIDTH / 2, + + x2_CHAR_HEIGHT = 4, + x2_CHAR_WIDTH = 2, + + x1_CHAR_HEIGHT = 2, + x1_CHAR_WIDTH = 1, + + RGBA_CHANNELS_CNT = 4, +} + +export class MinimapCharRenderer { + + _minimapCharRendererBrand: void; + + public readonly x2charData: Uint8ClampedArray; + public readonly x1charData: Uint8ClampedArray; + + public readonly x2charDataLight: Uint8ClampedArray; + public readonly x1charDataLight: Uint8ClampedArray; + + constructor(x2CharData: Uint8ClampedArray, x1CharData: Uint8ClampedArray) { + const x2ExpectedLen = Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * Constants.CHAR_COUNT; + if (x2CharData.length !== x2ExpectedLen) { + throw new Error('Invalid x2CharData'); + } + const x1ExpectedLen = Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * Constants.CHAR_COUNT; + if (x1CharData.length !== x1ExpectedLen) { + throw new Error('Invalid x1CharData'); + } + this.x2charData = x2CharData; + this.x1charData = x1CharData; + + this.x2charDataLight = MinimapCharRenderer.soften(x2CharData, 12 / 15); + this.x1charDataLight = MinimapCharRenderer.soften(x1CharData, 50 / 60); + } + + private static soften(input: Uint8ClampedArray, ratio: number): Uint8ClampedArray { + let result = new Uint8ClampedArray(input.length); + for (let i = 0, len = input.length; i < len; i++) { + result[i] = input[i] * ratio; + } + return result; + } + + private static _getChIndex(chCode: number): number { + chCode -= Constants.START_CH_CODE; + if (chCode < 0) { + chCode += Constants.CHAR_COUNT; + } + return (chCode % Constants.CHAR_COUNT); + } + + public x2RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { + if (dx + Constants.x2_CHAR_WIDTH > target.width || dy + Constants.x2_CHAR_HEIGHT > target.height) { + console.warn('bad render request outside image data'); + return; + } + const x2CharData = useLighterFont ? this.x2charDataLight : this.x2charData; + const chIndex = MinimapCharRenderer._getChIndex(chCode); + + const outWidth = target.width * Constants.RGBA_CHANNELS_CNT; + + const backgroundR = backgroundColor.r; + const backgroundG = backgroundColor.g; + const backgroundB = backgroundColor.b; + + const deltaR = color.r - backgroundR; + const deltaG = color.g - backgroundG; + const deltaB = color.b - backgroundB; + + const dest = target.data; + const sourceOffset = chIndex * Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH; + let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT; + { + const c = x2CharData[sourceOffset] / 255; + dest[destOffset + 0] = backgroundR + deltaR * c; + dest[destOffset + 1] = backgroundG + deltaG * c; + dest[destOffset + 2] = backgroundB + deltaB * c; + } + { + const c = x2CharData[sourceOffset + 1] / 255; + dest[destOffset + 4] = backgroundR + deltaR * c; + dest[destOffset + 5] = backgroundG + deltaG * c; + dest[destOffset + 6] = backgroundB + deltaB * c; + } + + destOffset += outWidth; + { + const c = x2CharData[sourceOffset + 2] / 255; + dest[destOffset + 0] = backgroundR + deltaR * c; + dest[destOffset + 1] = backgroundG + deltaG * c; + dest[destOffset + 2] = backgroundB + deltaB * c; + } + { + const c = x2CharData[sourceOffset + 3] / 255; + dest[destOffset + 4] = backgroundR + deltaR * c; + dest[destOffset + 5] = backgroundG + deltaG * c; + dest[destOffset + 6] = backgroundB + deltaB * c; + } + + destOffset += outWidth; + { + const c = x2CharData[sourceOffset + 4] / 255; + dest[destOffset + 0] = backgroundR + deltaR * c; + dest[destOffset + 1] = backgroundG + deltaG * c; + dest[destOffset + 2] = backgroundB + deltaB * c; + } + { + const c = x2CharData[sourceOffset + 5] / 255; + dest[destOffset + 4] = backgroundR + deltaR * c; + dest[destOffset + 5] = backgroundG + deltaG * c; + dest[destOffset + 6] = backgroundB + deltaB * c; + } + + destOffset += outWidth; + { + const c = x2CharData[sourceOffset + 6] / 255; + dest[destOffset + 0] = backgroundR + deltaR * c; + dest[destOffset + 1] = backgroundG + deltaG * c; + dest[destOffset + 2] = backgroundB + deltaB * c; + } + { + const c = x2CharData[sourceOffset + 7] / 255; + dest[destOffset + 4] = backgroundR + deltaR * c; + dest[destOffset + 5] = backgroundG + deltaG * c; + dest[destOffset + 6] = backgroundB + deltaB * c; + } + } + + public x1RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { + if (dx + Constants.x1_CHAR_WIDTH > target.width || dy + Constants.x1_CHAR_HEIGHT > target.height) { + console.warn('bad render request outside image data'); + return; + } + const x1CharData = useLighterFont ? this.x1charDataLight : this.x1charData; + const chIndex = MinimapCharRenderer._getChIndex(chCode); + + const outWidth = target.width * Constants.RGBA_CHANNELS_CNT; + + const backgroundR = backgroundColor.r; + const backgroundG = backgroundColor.g; + const backgroundB = backgroundColor.b; + + const deltaR = color.r - backgroundR; + const deltaG = color.g - backgroundG; + const deltaB = color.b - backgroundB; + + const dest = target.data; + const sourceOffset = chIndex * Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH; + let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT; + { + const c = x1CharData[sourceOffset] / 255; + dest[destOffset + 0] = backgroundR + deltaR * c; + dest[destOffset + 1] = backgroundG + deltaG * c; + dest[destOffset + 2] = backgroundB + deltaB * c; + } + + destOffset += outWidth; + { + const c = x1CharData[sourceOffset + 1] / 255; + dest[destOffset + 0] = backgroundR + deltaR * c; + dest[destOffset + 1] = backgroundG + deltaG * c; + dest[destOffset + 2] = backgroundB + deltaB * c; + } + } + + public x2BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { + if (dx + Constants.x2_CHAR_WIDTH > target.width || dy + Constants.x2_CHAR_HEIGHT > target.height) { + console.warn('bad render request outside image data'); + return; + } + + const outWidth = target.width * Constants.RGBA_CHANNELS_CNT; + + const c = 0.5; + + const backgroundR = backgroundColor.r; + const backgroundG = backgroundColor.g; + const backgroundB = backgroundColor.b; + + const deltaR = color.r - backgroundR; + const deltaG = color.g - backgroundG; + const deltaB = color.b - backgroundB; + + const colorR = backgroundR + deltaR * c; + const colorG = backgroundG + deltaG * c; + const colorB = backgroundB + deltaB * c; + + const dest = target.data; + let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT; + { + dest[destOffset + 0] = colorR; + dest[destOffset + 1] = colorG; + dest[destOffset + 2] = colorB; + } + { + dest[destOffset + 4] = colorR; + dest[destOffset + 5] = colorG; + dest[destOffset + 6] = colorB; + } + + destOffset += outWidth; + { + dest[destOffset + 0] = colorR; + dest[destOffset + 1] = colorG; + dest[destOffset + 2] = colorB; + } + { + dest[destOffset + 4] = colorR; + dest[destOffset + 5] = colorG; + dest[destOffset + 6] = colorB; + } + + destOffset += outWidth; + { + dest[destOffset + 0] = colorR; + dest[destOffset + 1] = colorG; + dest[destOffset + 2] = colorB; + } + { + dest[destOffset + 4] = colorR; + dest[destOffset + 5] = colorG; + dest[destOffset + 6] = colorB; + } + + destOffset += outWidth; + { + dest[destOffset + 0] = colorR; + dest[destOffset + 1] = colorG; + dest[destOffset + 2] = colorB; + } + { + dest[destOffset + 4] = colorR; + dest[destOffset + 5] = colorG; + dest[destOffset + 6] = colorB; + } + } + + public x1BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { + if (dx + Constants.x1_CHAR_WIDTH > target.width || dy + Constants.x1_CHAR_HEIGHT > target.height) { + console.warn('bad render request outside image data'); + return; + } + + const outWidth = target.width * Constants.RGBA_CHANNELS_CNT; + + const c = 0.5; + + const backgroundR = backgroundColor.r; + const backgroundG = backgroundColor.g; + const backgroundB = backgroundColor.b; + + const deltaR = color.r - backgroundR; + const deltaG = color.g - backgroundG; + const deltaB = color.b - backgroundB; + + const colorR = backgroundR + deltaR * c; + const colorG = backgroundG + deltaG * c; + const colorB = backgroundB + deltaB * c; + + const dest = target.data; + + let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT; + { + dest[destOffset + 0] = colorR; + dest[destOffset + 1] = colorG; + dest[destOffset + 2] = colorB; + } + + destOffset += outWidth; + { + dest[destOffset + 0] = colorR; + dest[destOffset + 1] = colorG; + dest[destOffset + 2] = colorB; + } + } +} diff --git a/src/vs/editor/common/view/overviewZoneManager.ts b/src/vs/editor/common/view/overviewZoneManager.ts new file mode 100644 index 0000000000..fe7cb44dd9 --- /dev/null +++ b/src/vs/editor/common/view/overviewZoneManager.ts @@ -0,0 +1,394 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { OverviewRulerLane } from 'vs/editor/common/editorCommon'; +import { ThemeType, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService'; + +export class ColorZone { + _colorZoneBrand: void; + + from: number; + to: number; + colorId: number; + position: OverviewRulerLane; + + constructor(from: number, to: number, colorId: number, position: OverviewRulerLane) { + this.from = from | 0; + this.to = to | 0; + this.colorId = colorId | 0; + this.position = position | 0; + } +} + +/** + * A zone in the overview ruler + */ +export class OverviewRulerZone { + _overviewRulerZoneBrand: void; + + startLineNumber: number; + endLineNumber: number; + position: OverviewRulerLane; + forceHeight: number; + + private _color: string; + private _darkColor: string; + private _hcColor: string; + + private _colorZones: ColorZone[]; + + constructor( + startLineNumber: number, endLineNumber: number, + position: OverviewRulerLane, + forceHeight: number, + color: string, darkColor: string, hcColor: string + ) { + this.startLineNumber = startLineNumber; + this.endLineNumber = endLineNumber; + this.position = position; + this.forceHeight = forceHeight; + this._color = color; + this._darkColor = darkColor; + this._hcColor = hcColor; + this._colorZones = null; + } + + public getColor(themeType: ThemeType): string { + switch (themeType) { + case HIGH_CONTRAST: + return this._hcColor; + case DARK: + return this._darkColor; + } + return this._color; + } + + public equals(other: OverviewRulerZone): boolean { + return ( + this.startLineNumber === other.startLineNumber + && this.endLineNumber === other.endLineNumber + && this.position === other.position + && this.forceHeight === other.forceHeight + && this._color === other._color + && this._darkColor === other._darkColor + && this._hcColor === other._hcColor + ); + } + + public compareTo(other: OverviewRulerZone): number { + if (this.startLineNumber === other.startLineNumber) { + if (this.endLineNumber === other.endLineNumber) { + if (this.forceHeight === other.forceHeight) { + if (this.position === other.position) { + if (this._darkColor === other._darkColor) { + if (this._color === other._color) { + if (this._hcColor === other._hcColor) { + return 0; + } + return this._hcColor < other._hcColor ? -1 : 1; + } + return this._color < other._color ? -1 : 1; + } + return this._darkColor < other._darkColor ? -1 : 1; + } + return this.position - other.position; + } + return this.forceHeight - other.forceHeight; + } + return this.endLineNumber - other.endLineNumber; + } + return this.startLineNumber - other.startLineNumber; + } + + public setColorZones(colorZones: ColorZone[]): void { + this._colorZones = colorZones; + } + + public getColorZones(): ColorZone[] { + return this._colorZones; + } +} + +export class OverviewZoneManager { + + private _getVerticalOffsetForLine: (lineNumber: number) => number; + private _zones: OverviewRulerZone[]; + private _colorZonesInvalid: boolean; + private _lineHeight: number; + private _domWidth: number; + private _domHeight: number; + private _outerHeight: number; + private _maximumHeight: number; + private _minimumHeight: number; + private _themeType: ThemeType; + private _pixelRatio: number; + + private _lastAssignedId: number; + private _color2Id: { [color: string]: number; }; + private _id2Color: string[]; + + constructor(getVerticalOffsetForLine: (lineNumber: number) => number) { + this._getVerticalOffsetForLine = getVerticalOffsetForLine; + this._zones = []; + this._colorZonesInvalid = false; + this._lineHeight = 0; + this._domWidth = 0; + this._domHeight = 0; + this._outerHeight = 0; + this._maximumHeight = 0; + this._minimumHeight = 0; + this._themeType = LIGHT; + this._pixelRatio = 1; + + this._lastAssignedId = 0; + this._color2Id = Object.create(null); + this._id2Color = []; + } + + public getId2Color(): string[] { + return this._id2Color; + } + + public setZones(newZones: OverviewRulerZone[]): void { + newZones.sort((a, b) => a.compareTo(b)); + + let oldZones = this._zones; + let oldIndex = 0; + let oldLength = this._zones.length; + let newIndex = 0; + let newLength = newZones.length; + + let result: OverviewRulerZone[] = []; + while (newIndex < newLength) { + let newZone = newZones[newIndex]; + + if (oldIndex >= oldLength) { + result.push(newZone); + newIndex++; + } else { + let oldZone = oldZones[oldIndex]; + let cmp = oldZone.compareTo(newZone); + if (cmp < 0) { + oldIndex++; + } else if (cmp > 0) { + result.push(newZone); + newIndex++; + } else { + // cmp === 0 + result.push(oldZone); + oldIndex++; + newIndex++; + } + } + } + + this._zones = result; + } + + public setLineHeight(lineHeight: number): boolean { + if (this._lineHeight === lineHeight) { + return false; + } + this._lineHeight = lineHeight; + this._colorZonesInvalid = true; + return true; + } + + public setPixelRatio(pixelRatio: number): void { + this._pixelRatio = pixelRatio; + this._colorZonesInvalid = true; + } + + public getDOMWidth(): number { + return this._domWidth; + } + + public getCanvasWidth(): number { + return this._domWidth * this._pixelRatio; + } + + public setDOMWidth(width: number): boolean { + if (this._domWidth === width) { + return false; + } + this._domWidth = width; + this._colorZonesInvalid = true; + return true; + } + + public getDOMHeight(): number { + return this._domHeight; + } + + public getCanvasHeight(): number { + return this._domHeight * this._pixelRatio; + } + + public setDOMHeight(height: number): boolean { + if (this._domHeight === height) { + return false; + } + this._domHeight = height; + this._colorZonesInvalid = true; + return true; + } + + public getOuterHeight(): number { + return this._outerHeight; + } + + public setOuterHeight(outerHeight: number): boolean { + if (this._outerHeight === outerHeight) { + return false; + } + this._outerHeight = outerHeight; + this._colorZonesInvalid = true; + return true; + } + + public setMaximumHeight(maximumHeight: number): boolean { + if (this._maximumHeight === maximumHeight) { + return false; + } + this._maximumHeight = maximumHeight; + this._colorZonesInvalid = true; + return true; + } + + public setMinimumHeight(minimumHeight: number): boolean { + if (this._minimumHeight === minimumHeight) { + return false; + } + this._minimumHeight = minimumHeight; + this._colorZonesInvalid = true; + return true; + } + + public setThemeType(themeType: ThemeType): boolean { + if (this._themeType === themeType) { + return false; + } + this._themeType = themeType; + this._colorZonesInvalid = true; + return true; + } + + public resolveColorZones(): ColorZone[] { + const colorZonesInvalid = this._colorZonesInvalid; + const lineHeight = Math.floor(this._lineHeight); // @perf + const totalHeight = Math.floor(this.getCanvasHeight()); // @perf + const maximumHeight = Math.floor(this._maximumHeight * this._pixelRatio); // @perf + const minimumHeight = Math.floor(this._minimumHeight * this._pixelRatio); // @perf + const themeType = this._themeType; // @perf + const outerHeight = Math.floor(this._outerHeight); // @perf + const heightRatio = totalHeight / outerHeight; + + let allColorZones: ColorZone[] = []; + for (let i = 0, len = this._zones.length; i < len; i++) { + let zone = this._zones[i]; + + if (!colorZonesInvalid) { + let colorZones = zone.getColorZones(); + if (colorZones) { + for (let j = 0, lenJ = colorZones.length; j < lenJ; j++) { + allColorZones.push(colorZones[j]); + } + continue; + } + } + + let colorZones: ColorZone[] = []; + if (zone.forceHeight) { + let forcedHeight = Math.floor(zone.forceHeight * this._pixelRatio); + + let y1 = Math.floor(this._getVerticalOffsetForLine(zone.startLineNumber)); + y1 = Math.floor(y1 * heightRatio); + + let y2 = y1 + forcedHeight; + colorZones.push(this.createZone(totalHeight, y1, y2, forcedHeight, forcedHeight, zone.getColor(themeType), zone.position)); + } else { + let y1 = Math.floor(this._getVerticalOffsetForLine(zone.startLineNumber)); + let y2 = Math.floor(this._getVerticalOffsetForLine(zone.endLineNumber)) + lineHeight; + + y1 = Math.floor(y1 * heightRatio); + y2 = Math.floor(y2 * heightRatio); + + // Figure out if we can render this in one continuous zone + let zoneLineNumbers = zone.endLineNumber - zone.startLineNumber + 1; + let zoneMaximumHeight = zoneLineNumbers * maximumHeight; + + if (y2 - y1 > zoneMaximumHeight) { + // We need to draw one zone per line + for (let lineNumber = zone.startLineNumber; lineNumber <= zone.endLineNumber; lineNumber++) { + y1 = Math.floor(this._getVerticalOffsetForLine(lineNumber)); + y2 = y1 + lineHeight; + + y1 = Math.floor(y1 * heightRatio); + y2 = Math.floor(y2 * heightRatio); + + colorZones.push(this.createZone(totalHeight, y1, y2, minimumHeight, maximumHeight, zone.getColor(themeType), zone.position)); + } + } else { + colorZones.push(this.createZone(totalHeight, y1, y2, minimumHeight, zoneMaximumHeight, zone.getColor(themeType), zone.position)); + } + } + + zone.setColorZones(colorZones); + for (let j = 0, lenJ = colorZones.length; j < lenJ; j++) { + allColorZones.push(colorZones[j]); + } + } + + this._colorZonesInvalid = false; + + let sortFunc = (a: ColorZone, b: ColorZone) => { + if (a.colorId === b.colorId) { + if (a.from === b.from) { + return a.to - b.to; + } + return a.from - b.from; + } + return a.colorId - b.colorId; + }; + + allColorZones.sort(sortFunc); + return allColorZones; + } + + public createZone(totalHeight: number, y1: number, y2: number, minimumHeight: number, maximumHeight: number, color: string, position: OverviewRulerLane): ColorZone { + totalHeight = Math.floor(totalHeight); // @perf + y1 = Math.floor(y1); // @perf + y2 = Math.floor(y2); // @perf + minimumHeight = Math.floor(minimumHeight); // @perf + maximumHeight = Math.floor(maximumHeight); // @perf + + let ycenter = Math.floor((y1 + y2) / 2); + let halfHeight = (y2 - ycenter); + + + if (halfHeight > maximumHeight / 2) { + halfHeight = maximumHeight / 2; + } + if (halfHeight < minimumHeight / 2) { + halfHeight = minimumHeight / 2; + } + + if (ycenter - halfHeight < 0) { + ycenter = halfHeight; + } + if (ycenter + halfHeight > totalHeight) { + ycenter = totalHeight - halfHeight; + } + + let colorId = this._color2Id[color]; + if (!colorId) { + colorId = (++this._lastAssignedId); + this._color2Id[color] = colorId; + this._id2Color[colorId] = color; + } + return new ColorZone(ycenter - halfHeight, ycenter + halfHeight, colorId, position); + } +} diff --git a/src/vs/editor/common/view/renderingContext.ts b/src/vs/editor/common/view/renderingContext.ts new file mode 100644 index 0000000000..505da2adb9 --- /dev/null +++ b/src/vs/editor/common/view/renderingContext.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IViewLayout, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; + +export interface IViewLines { + linesVisibleRangesForRange(range: Range, includeNewLines: boolean): LineVisibleRanges[]; + visibleRangesForRange2(range: Range): HorizontalRange[]; +} + +export abstract class RestrictedRenderingContext { + _restrictedRenderingContextBrand: void; + + public readonly viewportData: ViewportData; + + public readonly scrollWidth: number; + public readonly scrollHeight: number; + + public readonly visibleRange: Range; + public readonly bigNumbersDelta: number; + + public readonly scrollTop: number; + public readonly scrollLeft: number; + + public readonly viewportWidth: number; + public readonly viewportHeight: number; + + private readonly _viewLayout: IViewLayout; + + constructor(viewLayout: IViewLayout, viewportData: ViewportData) { + this._viewLayout = viewLayout; + this.viewportData = viewportData; + + this.scrollWidth = this._viewLayout.getScrollWidth(); + this.scrollHeight = this._viewLayout.getScrollHeight(); + + this.visibleRange = this.viewportData.visibleRange; + this.bigNumbersDelta = this.viewportData.bigNumbersDelta; + + const vInfo = this._viewLayout.getCurrentViewport(); + this.scrollTop = vInfo.top; + this.scrollLeft = vInfo.left; + this.viewportWidth = vInfo.width; + this.viewportHeight = vInfo.height; + } + + public getScrolledTopFromAbsoluteTop(absoluteTop: number): number { + return absoluteTop - this.scrollTop; + } + + public getVerticalOffsetForLineNumber(lineNumber: number): number { + return this._viewLayout.getVerticalOffsetForLineNumber(lineNumber); + } + + public lineIsVisible(lineNumber: number): boolean { + return ( + this.visibleRange.startLineNumber <= lineNumber + && lineNumber <= this.visibleRange.endLineNumber + ); + } + + public getDecorationsInViewport(): ViewModelDecoration[] { + return this.viewportData.getDecorationsInViewport(); + } + +} + +export class RenderingContext extends RestrictedRenderingContext { + _renderingContextBrand: void; + + private readonly _viewLines: IViewLines; + + constructor(viewLayout: IViewLayout, viewportData: ViewportData, viewLines: IViewLines) { + super(viewLayout, viewportData); + this._viewLines = viewLines; + } + + public linesVisibleRangesForRange(range: Range, includeNewLines: boolean): LineVisibleRanges[] { + return this._viewLines.linesVisibleRangesForRange(range, includeNewLines); + } + + public visibleRangeForPosition(position: Position): HorizontalRange { + const visibleRanges = this._viewLines.visibleRangesForRange2( + new Range(position.lineNumber, position.column, position.lineNumber, position.column) + ); + if (!visibleRanges) { + return null; + } + return visibleRanges[0]; + } +} + +export class LineVisibleRanges { + _lineVisibleRangesBrand: void; + + public lineNumber: number; + public ranges: HorizontalRange[]; + + constructor(lineNumber: number, ranges: HorizontalRange[]) { + this.lineNumber = lineNumber; + this.ranges = ranges; + } +} + +export class HorizontalRange { + _horizontalRangeBrand: void; + + public left: number; + public width: number; + + constructor(left: number, width: number) { + this.left = Math.round(left); + this.width = Math.round(width); + } + + public toString(): string { + return `[${this.left},${this.width}]`; + } +} diff --git a/src/vs/editor/common/view/runtimeMinimapCharRenderer.ts b/src/vs/editor/common/view/runtimeMinimapCharRenderer.ts new file mode 100644 index 0000000000..fc6f79f66c --- /dev/null +++ b/src/vs/editor/common/view/runtimeMinimapCharRenderer.ts @@ -0,0 +1,986 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { MinimapCharRenderer } from 'vs/editor/common/view/minimapCharRenderer'; + +function toUint8ClampedArrat(arr: number[]): Uint8ClampedArray { + let r = new Uint8ClampedArray(arr.length); + for (let i = 0, len = arr.length; i < len; i++) { + r[i] = arr[i]; + } + return r; +} + +let minimapCharRenderer: MinimapCharRenderer = null; +export function getOrCreateMinimapCharRenderer(): MinimapCharRenderer { + if (!minimapCharRenderer) { + let _x1Data = toUint8ClampedArrat(x1Data); + x1Data = null; + + let _x2Data = toUint8ClampedArrat(x2Data); + x2Data = null; + minimapCharRenderer = new MinimapCharRenderer(_x2Data, _x1Data); + } + return minimapCharRenderer; +} + +var x2Data = [ + + // + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + // ! + 39, 14, + 39, 14, + 14, 5, + 29, 10, + + // " + 96, 96, + 29, 29, + 0, 0, + 0, 0, + + // # + 49, 113, + 195, 214, + 227, 166, + 135, 42, + + // $ + 40, 29, + 194, 38, + 75, 148, + 197, 187, + + // % + 145, 0, + 160, 61, + 75, 143, + 2, 183, + + // & + 138, 58, + 163, 6, + 177, 223, + 197, 227, + + // ' + 38, 13, + 11, 4, + 0, 0, + 0, 0, + + // ( + 10, 54, + 52, 8, + 62, 4, + 71, 122, + + // ) + 73, 2, + 19, 40, + 10, 50, + 155, 36, + + // * + 79, 70, + 145, 121, + 7, 5, + 0, 0, + + // + + 2, 1, + 36, 12, + 204, 166, + 16, 5, + + // , + 0, 0, + 0, 0, + 1, 0, + 154, 34, + + // - + 0, 0, + 0, 0, + 96, 83, + 0, 0, + + // . + 0, 0, + 0, 0, + 0, 0, + 46, 34, + + // / + 0, 82, + 2, 56, + 53, 3, + 146, 0, + + // 0 + 146, 119, + 152, 132, + 152, 131, + 145, 119, + + // 1 + 170, 42, + 15, 42, + 15, 42, + 172, 194, + + // 2 + 131, 132, + 0, 139, + 80, 28, + 227, 143, + + // 3 + 159, 135, + 15, 118, + 11, 126, + 171, 144, + + // 4 + 20, 124, + 88, 106, + 217, 196, + 0, 106, + + // 5 + 189, 92, + 168, 43, + 5, 130, + 164, 133, + + // 6 + 130, 115, + 183, 65, + 134, 120, + 141, 141, + + // 7 + 170, 196, + 2, 106, + 31, 32, + 105, 2, + + // 8 + 145, 130, + 116, 114, + 132, 135, + 138, 140, + + // 9 + 138, 113, + 147, 137, + 81, 183, + 129, 94, + + // : + 0, 0, + 21, 16, + 4, 3, + 46, 34, + + // ; + 0, 0, + 45, 34, + 1, 0, + 160, 49, + + // < + 0, 0, + 43, 143, + 203, 23, + 1, 76, + + // = + 0, 0, + 38, 28, + 131, 96, + 38, 28, + + // > + 0, 0, + 168, 31, + 29, 191, + 98, 0, + + // ? + 118, 139, + 5, 113, + 45, 13, + 37, 6, + + // @ + 97, 115, + 161, 179, + 204, 105, + 223, 224, + + // A + 83, 52, + 111, 100, + 184, 186, + 120, 132, + + // B + 212, 145, + 180, 139, + 174, 161, + 212, 182, + + // C + 104, 162, + 131, 0, + 131, 0, + 104, 161, + + // D + 219, 120, + 110, 116, + 110, 116, + 219, 120, + + // E + 207, 154, + 163, 40, + 147, 22, + 207, 154, + + // F + 202, 159, + 161, 47, + 145, 23, + 111, 0, + + // G + 139, 154, + 144, 30, + 144, 135, + 139, 187, + + // H + 110, 110, + 168, 161, + 150, 145, + 110, 110, + + // I + 185, 162, + 43, 16, + 43, 16, + 185, 162, + + // J + 73, 129, + 0, 110, + 0, 110, + 191, 87, + + // K + 149, 149, + 236, 48, + 195, 91, + 146, 149, + + // L + 146, 0, + 146, 0, + 146, 0, + 187, 173, + + // M + 200, 201, + 222, 215, + 172, 147, + 95, 95, + + // N + 193, 97, + 224, 129, + 159, 206, + 97, 192, + + // O + 155, 139, + 153, 115, + 153, 115, + 156, 140, + + // P + 189, 158, + 123, 136, + 190, 64, + 111, 0, + + // Q + 155, 139, + 153, 115, + 153, 114, + 156, 241, + + // R + 197, 148, + 150, 152, + 170, 116, + 110, 157, + + // S + 156, 128, + 169, 14, + 13, 159, + 158, 149, + + // T + 212, 189, + 43, 16, + 43, 16, + 43, 16, + + // U + 148, 110, + 148, 110, + 147, 109, + 182, 151, + + // V + 133, 121, + 106, 118, + 114, 103, + 89, 66, + + // W + 94, 94, + 211, 188, + 205, 207, + 139, 168, + + // X + 151, 152, + 87, 76, + 101, 79, + 151, 152, + + // Y + 130, 156, + 125, 116, + 47, 29, + 43, 16, + + // Z + 169, 228, + 11, 103, + 120, 6, + 230, 176, + + // [ + 55, 49, + 55, 6, + 55, 6, + 193, 102, + + // \ + 92, 0, + 71, 0, + 13, 30, + 0, 147, + + // ] + 63, 43, + 12, 43, + 12, 43, + 142, 152, + + // ^ + 71, 53, + 61, 61, + 0, 0, + 0, 0, + + // _ + 0, 0, + 0, 0, + 0, 0, + 158, 146, + + // ` + 25, 2, + 0, 0, + 0, 0, + 0, 0, + + // a + 0, 0, + 107, 130, + 170, 194, + 176, 188, + + // b + 109, 0, + 203, 159, + 113, 111, + 202, 158, + + // c + 0, 0, + 135, 135, + 114, 0, + 136, 135, + + // d + 0, 109, + 187, 190, + 148, 126, + 177, 187, + + // e + 0, 0, + 149, 130, + 218, 105, + 169, 135, + + // f + 37, 113, + 146, 113, + 49, 13, + 49, 13, + + // g + 0, 0, + 178, 195, + 147, 114, + 255, 255, + + // h + 109, 0, + 193, 149, + 110, 109, + 109, 109, + + // i + 12, 15, + 125, 41, + 33, 41, + 144, 188, + + // j + 1, 6, + 75, 53, + 10, 53, + 210, 161, + + // k + 110, 0, + 152, 148, + 210, 60, + 110, 156, + + // l + 213, 5, + 63, 5, + 63, 5, + 45, 111, + + // m + 0, 0, + 232, 172, + 190, 168, + 190, 169, + + // n + 0, 0, + 190, 144, + 109, 109, + 109, 109, + + // o + 0, 0, + 168, 140, + 148, 111, + 168, 140, + + // p + 0, 0, + 200, 151, + 113, 110, + 255, 158, + + // q + 0, 0, + 184, 188, + 147, 139, + 186, 255, + + // r + 0, 0, + 122, 130, + 111, 0, + 109, 0, + + // s + 0, 0, + 132, 69, + 109, 93, + 110, 136, + + // t + 51, 5, + 205, 103, + 61, 6, + 47, 106, + + // u + 0, 0, + 110, 109, + 110, 122, + 155, 179, + + // v + 0, 0, + 132, 120, + 113, 114, + 84, 63, + + // w + 0, 0, + 124, 108, + 202, 189, + 160, 174, + + // x + 0, 0, + 144, 142, + 79, 57, + 159, 146, + + // y + 0, 0, + 138, 138, + 119, 117, + 255, 69, + + // z + 0, 0, + 97, 198, + 47, 38, + 208, 84, + + // { + 23, 112, + 41, 14, + 157, 7, + 121, 192, + + // | + 35, 11, + 35, 11, + 35, 11, + 160, 61, + + // } + 129, 9, + 40, 19, + 20, 139, + 236, 44, + + // ~ + 0, 0, + 15, 3, + 97, 93, + 0, 0, + +]; + +var x1Data = [ + + // + 0, + 0, + + // ! + 23, + 12, + + // " + 53, + 0, + + // # + 130, + 127, + + // $ + 58, + 149, + + // % + 67, + 77, + + // & + 72, + 198, + + // ' + 13, + 0, + + // ( + 25, + 51, + + // ) + 25, + 49, + + // * + 94, + 2, + + // + + 8, + 64, + + // , + 0, + 24, + + // - + 0, + 21, + + // . + 0, + 9, + + // / + 19, + 27, + + // 0 + 126, + 126, + + // 1 + 51, + 80, + + // 2 + 72, + 105, + + // 3 + 87, + 98, + + // 4 + 73, + 93, + + // 5 + 106, + 85, + + // 6 + 111, + 123, + + // 7 + 87, + 30, + + // 8 + 116, + 126, + + // 9 + 123, + 110, + + // : + 4, + 16, + + // ; + 9, + 28, + + // < + 21, + 53, + + // = + 8, + 62, + + // > + 23, + 52, + + // ? + 73, + 21, + + // @ + 132, + 183, + + // A + 78, + 142, + + // B + 168, + 175, + + // C + 70, + 70, + + // D + 128, + 128, + + // E + 123, + 110, + + // F + 125, + 43, + + // G + 100, + 139, + + // H + 125, + 119, + + // I + 78, + 78, + + // J + 54, + 77, + + // K + 139, + 139, + + // L + 33, + 87, + + // M + 201, + 117, + + // N + 162, + 149, + + // O + 130, + 130, + + // P + 138, + 60, + + // Q + 130, + 172, + + // R + 149, + 127, + + // S + 95, + 98, + + // T + 95, + 25, + + // U + 118, + 135, + + // V + 110, + 85, + + // W + 147, + 175, + + // X + 105, + 110, + + // Y + 121, + 30, + + // Z + 101, + 113, + + // [ + 34, + 68, + + // \ + 20, + 26, + + // ] + 34, + 68, + + // ^ + 56, + 0, + + // _ + 0, + 44, + + // ` + 3, + 0, + + // a + 27, + 175, + + // b + 80, + 133, + + // c + 31, + 66, + + // d + 85, + 147, + + // e + 32, + 150, + + // f + 90, + 25, + + // g + 45, + 230, + + // h + 77, + 101, + + // i + 36, + 83, + + // j + 22, + 84, + + // k + 71, + 118, + + // l + 44, + 44, + + // m + 52, + 172, + + // n + 38, + 101, + + // o + 35, + 130, + + // p + 40, + 197, + + // q + 43, + 197, + + // r + 29, + 26, + + // s + 23, + 103, + + // t + 67, + 44, + + // u + 25, + 129, + + // v + 29, + 85, + + // w + 27, + 177, + + // x + 33, + 97, + + // y + 32, + 145, + + // z + 33, + 77, + + // { + 38, + 96, + + // | + 20, + 55, + + // } + 36, + 95, + + // ~ + 2, + 22, + +]; diff --git a/src/vs/editor/common/view/viewContext.ts b/src/vs/editor/common/view/viewContext.ts new file mode 100644 index 0000000000..536c503f3a --- /dev/null +++ b/src/vs/editor/common/view/viewContext.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IConfiguration } from 'vs/editor/common/editorCommon'; +import { IViewModel, IViewLayout } from 'vs/editor/common/viewModel/viewModel'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher'; +import { ITheme } from 'vs/platform/theme/common/themeService'; + +export class ViewContext { + + public readonly configuration: IConfiguration; + public readonly model: IViewModel; + public readonly viewLayout: IViewLayout; + public readonly privateViewEventBus: ViewEventDispatcher; + + public theme: ITheme; // will be updated + + constructor( + configuration: IConfiguration, + theme: ITheme, + model: IViewModel, + privateViewEventBus: ViewEventDispatcher + ) { + this.configuration = configuration; + this.theme = theme; + this.model = model; + this.viewLayout = model.viewLayout; + this.privateViewEventBus = privateViewEventBus; + } + + public addEventHandler(eventHandler: ViewEventHandler): void { + this.privateViewEventBus.addEventHandler(eventHandler); + } + + public removeEventHandler(eventHandler: ViewEventHandler): void { + this.privateViewEventBus.removeEventHandler(eventHandler); + } +} diff --git a/src/vs/editor/common/view/viewEventDispatcher.ts b/src/vs/editor/common/view/viewEventDispatcher.ts new file mode 100644 index 0000000000..fcb14eb1c5 --- /dev/null +++ b/src/vs/editor/common/view/viewEventDispatcher.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { ViewEvent } from 'vs/editor/common/view/viewEvents'; + +export class ViewEventDispatcher { + + private _eventHandlerGateKeeper: (callback: () => void) => void; + private _eventHandlers: ViewEventHandler[]; + private _eventQueue: ViewEvent[]; + private _isConsumingQueue: boolean; + + constructor(eventHandlerGateKeeper: (callback: () => void) => void) { + this._eventHandlerGateKeeper = eventHandlerGateKeeper; + this._eventHandlers = []; + this._eventQueue = null; + this._isConsumingQueue = false; + } + + public addEventHandler(eventHandler: ViewEventHandler): void { + for (let i = 0, len = this._eventHandlers.length; i < len; i++) { + if (this._eventHandlers[i] === eventHandler) { + console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler); + } + } + this._eventHandlers.push(eventHandler); + } + + public removeEventHandler(eventHandler: ViewEventHandler): void { + for (let i = 0; i < this._eventHandlers.length; i++) { + if (this._eventHandlers[i] === eventHandler) { + this._eventHandlers.splice(i, 1); + break; + } + } + } + + public emit(event: ViewEvent): void { + + if (this._eventQueue) { + this._eventQueue.push(event); + } else { + this._eventQueue = [event]; + } + + if (!this._isConsumingQueue) { + this.consumeQueue(); + } + } + + public emitMany(events: ViewEvent[]): void { + if (this._eventQueue) { + this._eventQueue = this._eventQueue.concat(events); + } else { + this._eventQueue = events; + } + + if (!this._isConsumingQueue) { + this.consumeQueue(); + } + } + + private consumeQueue(): void { + this._eventHandlerGateKeeper(() => { + try { + this._isConsumingQueue = true; + + this._doConsumeQueue(); + + } finally { + this._isConsumingQueue = false; + } + }); + } + + private _doConsumeQueue(): void { + while (this._eventQueue) { + // Empty event queue, as events might come in while sending these off + let events = this._eventQueue; + this._eventQueue = null; + + // Use a clone of the event handlers list, as they might remove themselves + let eventHandlers = this._eventHandlers.slice(0); + for (let i = 0, len = eventHandlers.length; i < len; i++) { + eventHandlers[i].handleEvents(events); + } + } + } +} diff --git a/src/vs/editor/common/view/viewEvents.ts b/src/vs/editor/common/view/viewEvents.ts new file mode 100644 index 0000000000..6017f0102d --- /dev/null +++ b/src/vs/editor/common/view/viewEvents.ts @@ -0,0 +1,349 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ScrollEvent } from 'vs/base/common/scrollable'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import * as errors from 'vs/base/common/errors'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { ScrollType } from 'vs/editor/common/editorCommon'; + +export const enum ViewEventType { + ViewConfigurationChanged = 1, + ViewCursorStateChanged = 2, + ViewDecorationsChanged = 3, + ViewFlushed = 4, + ViewFocusChanged = 5, + ViewLineMappingChanged = 6, + ViewLinesChanged = 7, + ViewLinesDeleted = 8, + ViewLinesInserted = 9, + ViewRevealRangeRequest = 10, + ViewScrollChanged = 11, + ViewTokensChanged = 12, + ViewTokensColorsChanged = 13, + ViewZonesChanged = 14, + ViewThemeChanged = 15 +} + +export class ViewConfigurationChangedEvent { + + public readonly type = ViewEventType.ViewConfigurationChanged; + + public readonly canUseLayerHinting: boolean; + public readonly pixelRatio: boolean; + public readonly editorClassName: boolean; + public readonly lineHeight: boolean; + public readonly readOnly: boolean; + public readonly accessibilitySupport: boolean; + public readonly emptySelectionClipboard: boolean; + public readonly layoutInfo: boolean; + public readonly fontInfo: boolean; + public readonly viewInfo: boolean; + public readonly wrappingInfo: boolean; + + constructor(source: IConfigurationChangedEvent) { + this.canUseLayerHinting = source.canUseLayerHinting; + this.pixelRatio = source.pixelRatio; + this.editorClassName = source.editorClassName; + this.lineHeight = source.lineHeight; + this.readOnly = source.readOnly; + this.accessibilitySupport = source.accessibilitySupport; + this.emptySelectionClipboard = source.emptySelectionClipboard; + this.layoutInfo = source.layoutInfo; + this.fontInfo = source.fontInfo; + this.viewInfo = source.viewInfo; + this.wrappingInfo = source.wrappingInfo; + } +} + +export class ViewCursorStateChangedEvent { + + public readonly type = ViewEventType.ViewCursorStateChanged; + + /** + * The primary selection is always at index 0. + */ + public readonly selections: Selection[]; + /** + * Is the primary cursor in the editable range? + */ + public readonly isInEditableRange: boolean; + + constructor(selections: Selection[], isInEditableRange: boolean) { + this.selections = selections; + this.isInEditableRange = isInEditableRange; + } +} + +export class ViewDecorationsChangedEvent { + + public readonly type = ViewEventType.ViewDecorationsChanged; + + constructor() { + // Nothing to do + } +} + +export class ViewFlushedEvent { + + public readonly type = ViewEventType.ViewFlushed; + + constructor() { + // Nothing to do + } +} + +export class ViewFocusChangedEvent { + + public readonly type = ViewEventType.ViewFocusChanged; + + public readonly isFocused: boolean; + + constructor(isFocused: boolean) { + this.isFocused = isFocused; + } +} + +export class ViewLineMappingChangedEvent { + + public readonly type = ViewEventType.ViewLineMappingChanged; + + constructor() { + // Nothing to do + } +} + +export class ViewLinesChangedEvent { + + public readonly type = ViewEventType.ViewLinesChanged; + + /** + * The first line that has changed. + */ + public readonly fromLineNumber: number; + /** + * The last line that has changed. + */ + public readonly toLineNumber: number; + + constructor(fromLineNumber: number, toLineNumber: number) { + this.fromLineNumber = fromLineNumber; + this.toLineNumber = toLineNumber; + } +} + +export class ViewLinesDeletedEvent { + + public readonly type = ViewEventType.ViewLinesDeleted; + + /** + * At what line the deletion began (inclusive). + */ + public readonly fromLineNumber: number; + /** + * At what line the deletion stopped (inclusive). + */ + public readonly toLineNumber: number; + + constructor(fromLineNumber: number, toLineNumber: number) { + this.fromLineNumber = fromLineNumber; + this.toLineNumber = toLineNumber; + } +} + +export class ViewLinesInsertedEvent { + + public readonly type = ViewEventType.ViewLinesInserted; + + /** + * Before what line did the insertion begin + */ + public readonly fromLineNumber: number; + /** + * `toLineNumber` - `fromLineNumber` + 1 denotes the number of lines that were inserted + */ + public readonly toLineNumber: number; + + constructor(fromLineNumber: number, toLineNumber: number) { + this.fromLineNumber = fromLineNumber; + this.toLineNumber = toLineNumber; + } +} + +export const enum VerticalRevealType { + Simple = 0, + Center = 1, + CenterIfOutsideViewport = 2, + Top = 3, + Bottom = 4 +} + +export class ViewRevealRangeRequestEvent { + + public readonly type = ViewEventType.ViewRevealRangeRequest; + + /** + * Range to be reavealed. + */ + public readonly range: Range; + + public readonly verticalType: VerticalRevealType; + /** + * If true: there should be a horizontal & vertical revealing + * If false: there should be just a vertical revealing + */ + public readonly revealHorizontal: boolean; + + public readonly scrollType: ScrollType; + + constructor(range: Range, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: ScrollType) { + this.range = range; + this.verticalType = verticalType; + this.revealHorizontal = revealHorizontal; + this.scrollType = scrollType; + } +} + +export class ViewScrollChangedEvent { + + public readonly type = ViewEventType.ViewScrollChanged; + + public readonly scrollWidth: number; + public readonly scrollLeft: number; + public readonly scrollHeight: number; + public readonly scrollTop: number; + + public readonly scrollWidthChanged: boolean; + public readonly scrollLeftChanged: boolean; + public readonly scrollHeightChanged: boolean; + public readonly scrollTopChanged: boolean; + + constructor(source: ScrollEvent) { + this.scrollWidth = source.scrollWidth; + this.scrollLeft = source.scrollLeft; + this.scrollHeight = source.scrollHeight; + this.scrollTop = source.scrollTop; + + this.scrollWidthChanged = source.scrollWidthChanged; + this.scrollLeftChanged = source.scrollLeftChanged; + this.scrollHeightChanged = source.scrollHeightChanged; + this.scrollTopChanged = source.scrollTopChanged; + } +} + +export class ViewTokensChangedEvent { + + public readonly type = ViewEventType.ViewTokensChanged; + + public readonly ranges: { + /** + * Start line number of range + */ + readonly fromLineNumber: number; + /** + * End line number of range + */ + readonly toLineNumber: number; + }[]; + + constructor(ranges: { fromLineNumber: number; toLineNumber: number; }[]) { + this.ranges = ranges; + } +} + +export class ViewThemeChangedEvent { + + public readonly type = ViewEventType.ViewThemeChanged; + + constructor() { + } +} + +export class ViewTokensColorsChangedEvent { + + public readonly type = ViewEventType.ViewTokensColorsChanged; + + constructor() { + // Nothing to do + } +} + +export class ViewZonesChangedEvent { + + public readonly type = ViewEventType.ViewZonesChanged; + + constructor() { + // Nothing to do + } +} + +export type ViewEvent = ( + ViewConfigurationChangedEvent + | ViewCursorStateChangedEvent + | ViewDecorationsChangedEvent + | ViewFlushedEvent + | ViewFocusChangedEvent + | ViewLinesChangedEvent + | ViewLineMappingChangedEvent + | ViewLinesDeletedEvent + | ViewLinesInsertedEvent + | ViewRevealRangeRequestEvent + | ViewScrollChangedEvent + | ViewTokensChangedEvent + | ViewTokensColorsChangedEvent + | ViewZonesChangedEvent + | ViewThemeChangedEvent +); + +export interface IViewEventListener { + (events: ViewEvent[]): void; +} + +export class ViewEventEmitter extends Disposable { + private _listeners: IViewEventListener[]; + + constructor() { + super(); + this._listeners = []; + } + + public dispose(): void { + this._listeners = []; + super.dispose(); + } + + protected _emit(events: ViewEvent[]): void { + const listeners = this._listeners.slice(0); + for (let i = 0, len = listeners.length; i < len; i++) { + safeInvokeListener(listeners[i], events); + } + } + + public addEventListener(listener: (events: ViewEvent[]) => void): IDisposable { + this._listeners.push(listener); + return { + dispose: () => { + let listeners = this._listeners; + for (let i = 0, len = listeners.length; i < len; i++) { + if (listeners[i] === listener) { + listeners.splice(i, 1); + break; + } + } + } + }; + } +} + +function safeInvokeListener(listener: IViewEventListener, events: ViewEvent[]): void { + try { + listener(events); + } catch (e) { + errors.onUnexpectedError(e); + } +} diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts new file mode 100644 index 0000000000..1f0618b81b --- /dev/null +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -0,0 +1,204 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { InlineDecoration } from 'vs/editor/common/viewModel/viewModel'; +import { Constants } from 'vs/editor/common/core/uint'; + +export class LineDecoration { + _lineDecorationBrand: void; + + public readonly startColumn: number; + public readonly endColumn: number; + public readonly className: string; + public readonly insertsBeforeOrAfter: boolean; + + constructor(startColumn: number, endColumn: number, className: string, insertsBeforeOrAfter: boolean) { + this.startColumn = startColumn; + this.endColumn = endColumn; + this.className = className; + this.insertsBeforeOrAfter = insertsBeforeOrAfter; + } + + private static _equals(a: LineDecoration, b: LineDecoration): boolean { + return ( + a.startColumn === b.startColumn + && a.endColumn === b.endColumn + && a.className === b.className + && a.insertsBeforeOrAfter === b.insertsBeforeOrAfter + ); + } + + public static equalsArr(a: LineDecoration[], b: LineDecoration[]): boolean { + let aLen = a.length; + let bLen = b.length; + if (aLen !== bLen) { + return false; + } + for (let i = 0; i < aLen; i++) { + if (!LineDecoration._equals(a[i], b[i])) { + return false; + } + } + return true; + } + + public static filter(lineDecorations: InlineDecoration[], lineNumber: number, minLineColumn: number, maxLineColumn: number): LineDecoration[] { + if (lineDecorations.length === 0) { + return []; + } + + let result: LineDecoration[] = [], resultLen = 0; + + for (let i = 0, len = lineDecorations.length; i < len; i++) { + let d = lineDecorations[i]; + let range = d.range; + + if (range.endLineNumber < lineNumber || range.startLineNumber > lineNumber) { + // Ignore decorations that sit outside this line + continue; + } + + if (range.isEmpty()) { + // Ignore empty range decorations + continue; + } + + let startColumn = (range.startLineNumber === lineNumber ? range.startColumn : minLineColumn); + let endColumn = (range.endLineNumber === lineNumber ? range.endColumn : maxLineColumn); + + if (endColumn <= 1) { + // An empty decoration (endColumn === 1) + continue; + } + + result[resultLen++] = new LineDecoration(startColumn, endColumn, d.inlineClassName, d.insertsBeforeOrAfter); + } + + return result; + } + + public static compare(a: LineDecoration, b: LineDecoration): number { + if (a.startColumn === b.startColumn) { + if (a.endColumn === b.endColumn) { + if (a.className < b.className) { + return -1; + } + if (a.className > b.className) { + return 1; + } + return 0; + } + return a.endColumn - b.endColumn; + } + return a.startColumn - b.startColumn; + } +} + +export class DecorationSegment { + startOffset: number; + endOffset: number; + className: string; + + constructor(startOffset: number, endOffset: number, className: string) { + this.startOffset = startOffset; + this.endOffset = endOffset; + this.className = className; + } +} + +class Stack { + public count: number; + private stopOffsets: number[]; + private classNames: string[]; + + constructor() { + this.stopOffsets = []; + this.classNames = []; + this.count = 0; + } + + public consumeLowerThan(maxStopOffset: number, nextStartOffset: number, result: DecorationSegment[]): number { + + while (this.count > 0 && this.stopOffsets[0] < maxStopOffset) { + var i = 0; + + // Take all equal stopping offsets + while (i + 1 < this.count && this.stopOffsets[i] === this.stopOffsets[i + 1]) { + i++; + } + + // Basically we are consuming the first i + 1 elements of the stack + result.push(new DecorationSegment(nextStartOffset, this.stopOffsets[i], this.classNames.join(' '))); + nextStartOffset = this.stopOffsets[i] + 1; + + // Consume them + this.stopOffsets.splice(0, i + 1); + this.classNames.splice(0, i + 1); + this.count -= (i + 1); + } + + if (this.count > 0 && nextStartOffset < maxStopOffset) { + result.push(new DecorationSegment(nextStartOffset, maxStopOffset - 1, this.classNames.join(' '))); + nextStartOffset = maxStopOffset; + } + + return nextStartOffset; + } + + public insert(stopOffset: number, className: string): void { + if (this.count === 0 || this.stopOffsets[this.count - 1] <= stopOffset) { + // Insert at the end + this.stopOffsets.push(stopOffset); + this.classNames.push(className); + } else { + // Find the insertion position for `stopOffset` + for (var i = 0; i < this.count; i++) { + if (this.stopOffsets[i] >= stopOffset) { + this.stopOffsets.splice(i, 0, stopOffset); + this.classNames.splice(i, 0, className); + break; + } + } + } + this.count++; + return; + } +} + +export class LineDecorationsNormalizer { + /** + * Normalize line decorations. Overlapping decorations will generate multiple segments + */ + public static normalize(lineDecorations: LineDecoration[]): DecorationSegment[] { + if (lineDecorations.length === 0) { + return []; + } + + let result: DecorationSegment[] = []; + + let stack = new Stack(); + let nextStartOffset = 0; + + for (let i = 0, len = lineDecorations.length; i < len; i++) { + let d = lineDecorations[i]; + + let currentStartOffset = d.startColumn - 1; + let currentEndOffset = d.endColumn - 2; + + nextStartOffset = stack.consumeLowerThan(currentStartOffset, nextStartOffset, result); + + if (stack.count === 0) { + nextStartOffset = currentStartOffset; + } + stack.insert(currentEndOffset, d.className); + } + + stack.consumeLowerThan(Constants.MAX_SAFE_SMALL_INTEGER, nextStartOffset, result); + + return result; + } + +} diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts new file mode 100644 index 0000000000..39b8651fb9 --- /dev/null +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -0,0 +1,475 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { WhitespaceComputer, IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; + +/** + * Layouting of objects that take vertical space (by having a height) and push down other objects. + * + * These objects are basically either text (lines) or spaces between those lines (whitespaces). + * This provides commodity operations for working with lines that contain whitespace that pushes lines lower (vertically). + * This is written with no knowledge of an editor in mind. + */ +export class LinesLayout { + + /** + * Keep track of the total number of lines. + * This is useful for doing binary searches or for doing hit-testing. + */ + private _lineCount: number; + + /** + * The height of a line in pixels. + */ + private _lineHeight: number; + + /** + * Contains whitespace information in pixels + */ + private _whitespaces: WhitespaceComputer; + + constructor(lineCount: number, lineHeight: number) { + this._lineCount = lineCount; + this._lineHeight = lineHeight; + this._whitespaces = new WhitespaceComputer(); + } + + /** + * Change the height of a line in pixels. + */ + public setLineHeight(lineHeight: number): void { + this._lineHeight = lineHeight; + } + + /** + * Set the number of lines. + * + * @param lineCount New number of lines. + */ + public onFlushed(lineCount: number): void { + this._lineCount = lineCount; + } + + /** + * Insert a new whitespace of a certain height after a line number. + * The whitespace has a "sticky" characteristic. + * Irrespective of edits above or below `afterLineNumber`, the whitespace will follow the initial line. + * + * @param afterLineNumber The conceptual position of this whitespace. The whitespace will follow this line as best as possible even when deleting/inserting lines above/below. + * @param heightInPx The height of the whitespace, in pixels. + * @return An id that can be used later to mutate or delete the whitespace + */ + public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number): number { + return this._whitespaces.insertWhitespace(afterLineNumber, ordinal, heightInPx); + } + + /** + * Change properties associated with a certain whitespace. + */ + public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean { + return this._whitespaces.changeWhitespace(id, newAfterLineNumber, newHeight); + } + + /** + * Remove an existing whitespace. + * + * @param id The whitespace to remove + * @return Returns true if the whitespace is found and it is removed. + */ + public removeWhitespace(id: number): boolean { + return this._whitespaces.removeWhitespace(id); + } + + /** + * Notify the layouter that lines have been deleted (a continuous zone of lines). + * + * @param fromLineNumber The line number at which the deletion started, inclusive + * @param toLineNumber The line number at which the deletion ended, inclusive + */ + public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void { + this._lineCount -= (toLineNumber - fromLineNumber + 1); + this._whitespaces.onLinesDeleted(fromLineNumber, toLineNumber); + } + + /** + * Notify the layouter that lines have been inserted (a continuous zone of lines). + * + * @param fromLineNumber The line number at which the insertion started, inclusive + * @param toLineNumber The line number at which the insertion ended, inclusive. + */ + public onLinesInserted(fromLineNumber: number, toLineNumber: number): void { + this._lineCount += (toLineNumber - fromLineNumber + 1); + this._whitespaces.onLinesInserted(fromLineNumber, toLineNumber); + } + + /** + * Get the sum of heights for all objects. + * + * @return The sum of heights for all objects. + */ + public getLinesTotalHeight(): number { + let linesHeight = this._lineHeight * this._lineCount; + let whitespacesHeight = this._whitespaces.getTotalHeight(); + return linesHeight + whitespacesHeight; + } + + /** + * Get the vertical offset (the sum of heights for all objects above) a certain line number. + * + * @param lineNumber The line number + * @return The sum of heights for all objects above `lineNumber`. + */ + public getVerticalOffsetForLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + let previousLinesHeight: number; + if (lineNumber > 1) { + previousLinesHeight = this._lineHeight * (lineNumber - 1); + } else { + previousLinesHeight = 0; + } + + let previousWhitespacesHeight = this._whitespaces.getAccumulatedHeightBeforeLineNumber(lineNumber); + + return previousLinesHeight + previousWhitespacesHeight; + } + + /** + * Returns the accumulated height of whitespaces before the given line number. + * + * @param lineNumber The line number + */ + public getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber: number): number { + return this._whitespaces.getAccumulatedHeightBeforeLineNumber(lineNumber); + } + + /** + * Returns if there is any whitespace in the document. + */ + public hasWhitespace(): boolean { + return this._whitespaces.getCount() > 0; + } + + /** + * Check if `verticalOffset` is below all lines. + */ + public isAfterLines(verticalOffset: number): boolean { + let totalHeight = this.getLinesTotalHeight(); + return verticalOffset > totalHeight; + } + + /** + * Find the first line number that is at or after vertical offset `verticalOffset`. + * i.e. if getVerticalOffsetForLine(line) is x and getVerticalOffsetForLine(line + 1) is y, then + * getLineNumberAtOrAfterVerticalOffset(i) = line, x <= i < y. + * + * @param verticalOffset The vertical offset to search at. + * @return The line number at or after vertical offset `verticalOffset`. + */ + public getLineNumberAtOrAfterVerticalOffset(verticalOffset: number): number { + verticalOffset = verticalOffset | 0; + + if (verticalOffset < 0) { + return 1; + } + + const linesCount = this._lineCount | 0; + const lineHeight = this._lineHeight; + let minLineNumber = 1; + let maxLineNumber = linesCount; + + while (minLineNumber < maxLineNumber) { + let midLineNumber = ((minLineNumber + maxLineNumber) / 2) | 0; + + let midLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(midLineNumber) | 0; + + if (verticalOffset >= midLineNumberVerticalOffset + lineHeight) { + // vertical offset is after mid line number + minLineNumber = midLineNumber + 1; + } else if (verticalOffset >= midLineNumberVerticalOffset) { + // Hit + return midLineNumber; + } else { + // vertical offset is before mid line number, but mid line number could still be what we're searching for + maxLineNumber = midLineNumber; + } + } + + if (minLineNumber > linesCount) { + return linesCount; + } + + return minLineNumber; + } + + /** + * Get all the lines and their relative vertical offsets that are positioned between `verticalOffset1` and `verticalOffset2`. + * + * @param verticalOffset1 The beginning of the viewport. + * @param verticalOffset2 The end of the viewport. + * @return A structure describing the lines positioned between `verticalOffset1` and `verticalOffset2`. + */ + public getLinesViewportData(verticalOffset1: number, verticalOffset2: number): IPartialViewLinesViewportData { + verticalOffset1 = verticalOffset1 | 0; + verticalOffset2 = verticalOffset2 | 0; + const lineHeight = this._lineHeight; + + // Find first line number + // We don't live in a perfect world, so the line number might start before or after verticalOffset1 + const startLineNumber = this.getLineNumberAtOrAfterVerticalOffset(verticalOffset1) | 0; + const startLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(startLineNumber) | 0; + + let endLineNumber = this._lineCount | 0; + + // Also keep track of what whitespace we've got + let whitespaceIndex = this._whitespaces.getFirstWhitespaceIndexAfterLineNumber(startLineNumber) | 0; + const whitespaceCount = this._whitespaces.getCount() | 0; + let currentWhitespaceHeight: number; + let currentWhitespaceAfterLineNumber: number; + + if (whitespaceIndex === -1) { + whitespaceIndex = whitespaceCount; + currentWhitespaceAfterLineNumber = endLineNumber + 1; + currentWhitespaceHeight = 0; + } else { + currentWhitespaceAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; + currentWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(whitespaceIndex) | 0; + } + + let currentVerticalOffset = startLineNumberVerticalOffset; + let currentLineRelativeOffset = currentVerticalOffset; + + // IE (all versions) cannot handle units above about 1,533,908 px, so every 500k pixels bring numbers down + const STEP_SIZE = 500000; + let bigNumbersDelta = 0; + if (startLineNumberVerticalOffset >= STEP_SIZE) { + // Compute a delta that guarantees that lines are positioned at `lineHeight` increments + bigNumbersDelta = Math.floor(startLineNumberVerticalOffset / STEP_SIZE) * STEP_SIZE; + bigNumbersDelta = Math.floor(bigNumbersDelta / lineHeight) * lineHeight; + + currentLineRelativeOffset -= bigNumbersDelta; + } + + let linesOffsets: number[] = []; + + const verticalCenter = verticalOffset1 + (verticalOffset2 - verticalOffset1) / 2; + let centeredLineNumber = -1; + + // Figure out how far the lines go + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + + if (centeredLineNumber === -1) { + let currentLineTop = currentVerticalOffset; + let currentLineBottom = currentVerticalOffset + lineHeight; + if ((currentLineTop <= verticalCenter && verticalCenter < currentLineBottom) || currentLineTop > verticalCenter) { + centeredLineNumber = lineNumber; + } + } + + // Count current line height in the vertical offsets + currentVerticalOffset += lineHeight; + linesOffsets[lineNumber - startLineNumber] = currentLineRelativeOffset; + + // Next line starts immediately after this one + currentLineRelativeOffset += lineHeight; + while (currentWhitespaceAfterLineNumber === lineNumber) { + // Push down next line with the height of the current whitespace + currentLineRelativeOffset += currentWhitespaceHeight; + + // Count current whitespace in the vertical offsets + currentVerticalOffset += currentWhitespaceHeight; + whitespaceIndex++; + + if (whitespaceIndex >= whitespaceCount) { + currentWhitespaceAfterLineNumber = endLineNumber + 1; + } else { + currentWhitespaceAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; + currentWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(whitespaceIndex) | 0; + } + } + + if (currentVerticalOffset >= verticalOffset2) { + // We have covered the entire viewport area, time to stop + endLineNumber = lineNumber; + break; + } + } + + if (centeredLineNumber === -1) { + centeredLineNumber = endLineNumber; + } + + const endLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(endLineNumber) | 0; + + let completelyVisibleStartLineNumber = startLineNumber; + let completelyVisibleEndLineNumber = endLineNumber; + + if (completelyVisibleStartLineNumber < completelyVisibleEndLineNumber) { + if (startLineNumberVerticalOffset < verticalOffset1) { + completelyVisibleStartLineNumber++; + } + } + if (completelyVisibleStartLineNumber < completelyVisibleEndLineNumber) { + if (endLineNumberVerticalOffset + lineHeight > verticalOffset2) { + completelyVisibleEndLineNumber--; + } + } + + return { + bigNumbersDelta: bigNumbersDelta, + startLineNumber: startLineNumber, + endLineNumber: endLineNumber, + relativeVerticalOffset: linesOffsets, + centeredLineNumber: centeredLineNumber, + completelyVisibleStartLineNumber: completelyVisibleStartLineNumber, + completelyVisibleEndLineNumber: completelyVisibleEndLineNumber + }; + } + + public getVerticalOffsetForWhitespaceIndex(whitespaceIndex: number): number { + whitespaceIndex = whitespaceIndex | 0; + + let afterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex); + + let previousLinesHeight: number; + if (afterLineNumber >= 1) { + previousLinesHeight = this._lineHeight * afterLineNumber; + } else { + previousLinesHeight = 0; + } + + let previousWhitespacesHeight: number; + if (whitespaceIndex > 0) { + previousWhitespacesHeight = this._whitespaces.getAccumulatedHeight(whitespaceIndex - 1); + } else { + previousWhitespacesHeight = 0; + } + return previousLinesHeight + previousWhitespacesHeight; + } + + public getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset: number): number { + verticalOffset = verticalOffset | 0; + + let midWhitespaceIndex: number, + minWhitespaceIndex = 0, + maxWhitespaceIndex = this._whitespaces.getCount() - 1, + midWhitespaceVerticalOffset: number, + midWhitespaceHeight: number; + + if (maxWhitespaceIndex < 0) { + return -1; + } + + // Special case: nothing to be found + let maxWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(maxWhitespaceIndex); + let maxWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(maxWhitespaceIndex); + if (verticalOffset >= maxWhitespaceVerticalOffset + maxWhitespaceHeight) { + return -1; + } + + while (minWhitespaceIndex < maxWhitespaceIndex) { + midWhitespaceIndex = Math.floor((minWhitespaceIndex + maxWhitespaceIndex) / 2); + + midWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(midWhitespaceIndex); + midWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(midWhitespaceIndex); + + if (verticalOffset >= midWhitespaceVerticalOffset + midWhitespaceHeight) { + // vertical offset is after whitespace + minWhitespaceIndex = midWhitespaceIndex + 1; + } else if (verticalOffset >= midWhitespaceVerticalOffset) { + // Hit + return midWhitespaceIndex; + } else { + // vertical offset is before whitespace, but midWhitespaceIndex might still be what we're searching for + maxWhitespaceIndex = midWhitespaceIndex; + } + } + return minWhitespaceIndex; + } + + /** + * Get exactly the whitespace that is layouted at `verticalOffset`. + * + * @param verticalOffset The vertical offset. + * @return Precisely the whitespace that is layouted at `verticaloffset` or null. + */ + public getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData { + verticalOffset = verticalOffset | 0; + + let candidateIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset); + + if (candidateIndex < 0) { + return null; + } + + if (candidateIndex >= this._whitespaces.getCount()) { + return null; + } + + let candidateTop = this.getVerticalOffsetForWhitespaceIndex(candidateIndex); + + if (candidateTop > verticalOffset) { + return null; + } + + let candidateHeight = this._whitespaces.getHeightForWhitespaceIndex(candidateIndex); + let candidateId = this._whitespaces.getIdForWhitespaceIndex(candidateIndex); + let candidateAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(candidateIndex); + + return { + id: candidateId, + afterLineNumber: candidateAfterLineNumber, + verticalOffset: candidateTop, + height: candidateHeight + }; + } + + /** + * Get a list of whitespaces that are positioned between `verticalOffset1` and `verticalOffset2`. + * + * @param verticalOffset1 The beginning of the viewport. + * @param verticalOffset2 The end of the viewport. + * @return An array with all the whitespaces in the viewport. If no whitespace is in viewport, the array is empty. + */ + public getWhitespaceViewportData(verticalOffset1: number, verticalOffset2: number): IViewWhitespaceViewportData[] { + verticalOffset1 = verticalOffset1 | 0; + verticalOffset2 = verticalOffset2 | 0; + + let startIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset1); + let endIndex = this._whitespaces.getCount() - 1; + + if (startIndex < 0) { + return []; + } + + let result: IViewWhitespaceViewportData[] = []; + for (let i = startIndex; i <= endIndex; i++) { + let top = this.getVerticalOffsetForWhitespaceIndex(i); + let height = this._whitespaces.getHeightForWhitespaceIndex(i); + if (top >= verticalOffset2) { + break; + } + + result.push({ + id: this._whitespaces.getIdForWhitespaceIndex(i), + afterLineNumber: this._whitespaces.getAfterLineNumberForWhitespaceIndex(i), + verticalOffset: top, + height: height + }); + } + + return result; + } + + /** + * Get all whitespaces. + */ + public getWhitespaces(): IEditorWhitespace[] { + return this._whitespaces.getWhitespaces(this._lineHeight); + } +} diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts new file mode 100644 index 0000000000..f5da50860e --- /dev/null +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -0,0 +1,280 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Scrollable, ScrollEvent, ScrollbarVisibility, IScrollDimensions, IScrollPosition } from 'vs/base/common/scrollable'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout'; +import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; +import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import Event from 'vs/base/common/event'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; + +const SMOOTH_SCROLLING_TIME = 125; + +export class ViewLayout extends Disposable implements IViewLayout { + + static LINES_HORIZONTAL_EXTRA_PX = 30; + + private readonly _configuration: editorCommon.IConfiguration; + private readonly _linesLayout: LinesLayout; + + public readonly scrollable: Scrollable; + public readonly onDidScroll: Event; + + constructor(configuration: editorCommon.IConfiguration, lineCount: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { + super(); + + this._configuration = configuration; + this._linesLayout = new LinesLayout(lineCount, this._configuration.editor.lineHeight); + + this.scrollable = this._register(new Scrollable(0, scheduleAtNextAnimationFrame)); + this._configureSmoothScrollDuration(); + + this.scrollable.setScrollDimensions({ + width: configuration.editor.layoutInfo.contentWidth, + height: configuration.editor.layoutInfo.contentHeight + }); + this.onDidScroll = this.scrollable.onScroll; + + this._updateHeight(); + } + + public dispose(): void { + super.dispose(); + } + + public getScrollable(): Scrollable { + return this.scrollable; + } + + public onHeightMaybeChanged(): void { + this._updateHeight(); + } + + private _configureSmoothScrollDuration(): void { + this.scrollable.setSmoothScrollDuration(this._configuration.editor.viewInfo.smoothScrolling ? SMOOTH_SCROLLING_TIME : 0); + } + + // ---- begin view event handlers + + public onConfigurationChanged(e: IConfigurationChangedEvent): void { + if (e.lineHeight) { + this._linesLayout.setLineHeight(this._configuration.editor.lineHeight); + } + if (e.layoutInfo) { + this.scrollable.setScrollDimensions({ + width: this._configuration.editor.layoutInfo.contentWidth, + height: this._configuration.editor.layoutInfo.contentHeight + }); + } + if (e.viewInfo) { + this._configureSmoothScrollDuration(); + } + this._updateHeight(); + } + public onFlushed(lineCount: number): void { + this._linesLayout.onFlushed(lineCount); + this._updateHeight(); + } + public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void { + this._linesLayout.onLinesDeleted(fromLineNumber, toLineNumber); + this._updateHeight(); + } + public onLinesInserted(fromLineNumber: number, toLineNumber: number): void { + this._linesLayout.onLinesInserted(fromLineNumber, toLineNumber); + this._updateHeight(); + } + + // ---- end view event handlers + + private _getHorizontalScrollbarHeight(scrollDimensions: IScrollDimensions): number { + if (this._configuration.editor.viewInfo.scrollbar.horizontal === ScrollbarVisibility.Hidden) { + // horizontal scrollbar not visible + return 0; + } + if (scrollDimensions.width >= scrollDimensions.scrollWidth) { + // horizontal scrollbar not visible + return 0; + } + return this._configuration.editor.viewInfo.scrollbar.horizontalScrollbarSize; + } + + private _getTotalHeight(): number { + const scrollDimensions = this.scrollable.getScrollDimensions(); + + let result = this._linesLayout.getLinesTotalHeight(); + if (this._configuration.editor.viewInfo.scrollBeyondLastLine) { + result += scrollDimensions.height - this._configuration.editor.lineHeight; + } else { + result += this._getHorizontalScrollbarHeight(scrollDimensions); + } + + return Math.max(scrollDimensions.height, result); + } + + private _updateHeight(): void { + this.scrollable.setScrollDimensions({ + scrollHeight: this._getTotalHeight() + }); + } + + // ---- Layouting logic + + public getCurrentViewport(): Viewport { + const scrollDimensions = this.scrollable.getScrollDimensions(); + const currentScrollPosition = this.scrollable.getCurrentScrollPosition(); + return new Viewport( + currentScrollPosition.scrollTop, + currentScrollPosition.scrollLeft, + scrollDimensions.width, + scrollDimensions.height + ); + } + + public getFutureViewport(): Viewport { + const scrollDimensions = this.scrollable.getScrollDimensions(); + const currentScrollPosition = this.scrollable.getFutureScrollPosition(); + return new Viewport( + currentScrollPosition.scrollTop, + currentScrollPosition.scrollLeft, + scrollDimensions.width, + scrollDimensions.height + ); + } + + private _computeScrollWidth(maxLineWidth: number, viewportWidth: number): number { + let isViewportWrapping = this._configuration.editor.wrappingInfo.isViewportWrapping; + if (!isViewportWrapping) { + return Math.max(maxLineWidth + ViewLayout.LINES_HORIZONTAL_EXTRA_PX, viewportWidth); + } + return Math.max(maxLineWidth, viewportWidth); + } + + public onMaxLineWidthChanged(maxLineWidth: number): void { + let newScrollWidth = this._computeScrollWidth(maxLineWidth, this.getCurrentViewport().width); + this.scrollable.setScrollDimensions({ + scrollWidth: newScrollWidth + }); + + // The height might depend on the fact that there is a horizontal scrollbar or not + this._updateHeight(); + } + + // ---- view state + + public saveState(): editorCommon.IViewState { + const currentScrollPosition = this.scrollable.getFutureScrollPosition(); + let scrollTop = currentScrollPosition.scrollTop; + let firstLineNumberInViewport = this._linesLayout.getLineNumberAtOrAfterVerticalOffset(scrollTop); + let whitespaceAboveFirstLine = this._linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(firstLineNumberInViewport); + return { + scrollTop: scrollTop, + scrollTopWithoutViewZones: scrollTop - whitespaceAboveFirstLine, + scrollLeft: currentScrollPosition.scrollLeft + }; + } + + public restoreState(state: editorCommon.IViewState): void { + let restoreScrollTop = state.scrollTop; + if (typeof state.scrollTopWithoutViewZones === 'number' && !this._linesLayout.hasWhitespace()) { + restoreScrollTop = state.scrollTopWithoutViewZones; + } + this.scrollable.setScrollPositionNow({ + scrollLeft: state.scrollLeft, + scrollTop: restoreScrollTop + }); + } + + // ---- IVerticalLayoutProvider + + public addWhitespace(afterLineNumber: number, ordinal: number, height: number): number { + return this._linesLayout.insertWhitespace(afterLineNumber, ordinal, height); + } + public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean { + return this._linesLayout.changeWhitespace(id, newAfterLineNumber, newHeight); + } + public removeWhitespace(id: number): boolean { + return this._linesLayout.removeWhitespace(id); + } + public getVerticalOffsetForLineNumber(lineNumber: number): number { + return this._linesLayout.getVerticalOffsetForLineNumber(lineNumber); + } + public isAfterLines(verticalOffset: number): boolean { + return this._linesLayout.isAfterLines(verticalOffset); + } + public getLineNumberAtVerticalOffset(verticalOffset: number): number { + return this._linesLayout.getLineNumberAtOrAfterVerticalOffset(verticalOffset); + } + + public getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData { + return this._linesLayout.getWhitespaceAtVerticalOffset(verticalOffset); + } + public getLinesViewportData(): IPartialViewLinesViewportData { + const visibleBox = this.getCurrentViewport(); + return this._linesLayout.getLinesViewportData(visibleBox.top, visibleBox.top + visibleBox.height); + } + public getLinesViewportDataAtScrollTop(scrollTop: number): IPartialViewLinesViewportData { + // do some minimal validations on scrollTop + const scrollDimensions = this.scrollable.getScrollDimensions(); + if (scrollTop + scrollDimensions.height > scrollDimensions.scrollHeight) { + scrollTop = scrollDimensions.scrollHeight - scrollDimensions.height; + } + if (scrollTop < 0) { + scrollTop = 0; + } + return this._linesLayout.getLinesViewportData(scrollTop, scrollTop + scrollDimensions.height); + } + public getWhitespaceViewportData(): IViewWhitespaceViewportData[] { + const visibleBox = this.getCurrentViewport(); + return this._linesLayout.getWhitespaceViewportData(visibleBox.top, visibleBox.top + visibleBox.height); + } + public getWhitespaces(): IEditorWhitespace[] { + return this._linesLayout.getWhitespaces(); + } + + // ---- IScrollingProvider + + + public getScrollWidth(): number { + const scrollDimensions = this.scrollable.getScrollDimensions(); + return scrollDimensions.scrollWidth; + } + public getScrollHeight(): number { + const scrollDimensions = this.scrollable.getScrollDimensions(); + return scrollDimensions.scrollHeight; + } + + public getCurrentScrollLeft(): number { + const currentScrollPosition = this.scrollable.getCurrentScrollPosition(); + return currentScrollPosition.scrollLeft; + } + public getCurrentScrollTop(): number { + const currentScrollPosition = this.scrollable.getCurrentScrollPosition(); + return currentScrollPosition.scrollTop; + } + + public validateScrollPosition(scrollPosition: editorCommon.INewScrollPosition): IScrollPosition { + return this.scrollable.validateScrollPosition(scrollPosition); + } + + public setScrollPositionNow(position: editorCommon.INewScrollPosition): void { + this.scrollable.setScrollPositionNow(position); + } + + public setScrollPositionSmooth(position: editorCommon.INewScrollPosition): void { + this.scrollable.setScrollPositionSmooth(position); + } + + public deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void { + const currentScrollPosition = this.scrollable.getCurrentScrollPosition(); + this.scrollable.setScrollPositionNow({ + scrollLeft: currentScrollPosition.scrollLeft + deltaScrollLeft, + scrollTop: currentScrollPosition.scrollTop + deltaScrollTop + }); + } +} diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts new file mode 100644 index 0000000000..bb88fc83d8 --- /dev/null +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -0,0 +1,759 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; +import { CharCode } from 'vs/base/common/charCode'; +import { LineDecoration, LineDecorationsNormalizer } from 'vs/editor/common/viewLayout/lineDecorations'; +import * as strings from 'vs/base/common/strings'; +import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder'; + +export const enum RenderWhitespace { + None = 0, + Boundary = 1, + All = 2 +} + +class LinePart { + _linePartBrand: void; + + /** + * last char index of this token (not inclusive). + */ + public readonly endIndex: number; + public readonly type: string; + + constructor(endIndex: number, type: string) { + this.endIndex = endIndex; + this.type = type; + } +} + +export class RenderLineInput { + + public readonly useMonospaceOptimizations: boolean; + public readonly lineContent: string; + public readonly mightContainRTL: boolean; + public readonly fauxIndentLength: number; + public readonly lineTokens: ViewLineToken[]; + public readonly lineDecorations: LineDecoration[]; + public readonly tabSize: number; + public readonly spaceWidth: number; + public readonly stopRenderingLineAfter: number; + public readonly renderWhitespace: RenderWhitespace; + public readonly renderControlCharacters: boolean; + public readonly fontLigatures: boolean; + + constructor( + useMonospaceOptimizations: boolean, + lineContent: string, + mightContainRTL: boolean, + fauxIndentLength: number, + lineTokens: ViewLineToken[], + lineDecorations: LineDecoration[], + tabSize: number, + spaceWidth: number, + stopRenderingLineAfter: number, + renderWhitespace: 'none' | 'boundary' | 'all', + renderControlCharacters: boolean, + fontLigatures: boolean + ) { + this.useMonospaceOptimizations = useMonospaceOptimizations; + this.lineContent = lineContent; + this.mightContainRTL = mightContainRTL; + this.fauxIndentLength = fauxIndentLength; + this.lineTokens = lineTokens; + this.lineDecorations = lineDecorations; + this.tabSize = tabSize; + this.spaceWidth = spaceWidth; + this.stopRenderingLineAfter = stopRenderingLineAfter; + this.renderWhitespace = ( + renderWhitespace === 'all' + ? RenderWhitespace.All + : renderWhitespace === 'boundary' + ? RenderWhitespace.Boundary + : RenderWhitespace.None + ); + this.renderControlCharacters = renderControlCharacters; + this.fontLigatures = fontLigatures; + } + + public equals(other: RenderLineInput): boolean { + return ( + this.useMonospaceOptimizations === other.useMonospaceOptimizations + && this.lineContent === other.lineContent + && this.mightContainRTL === other.mightContainRTL + && this.fauxIndentLength === other.fauxIndentLength + && this.tabSize === other.tabSize + && this.spaceWidth === other.spaceWidth + && this.stopRenderingLineAfter === other.stopRenderingLineAfter + && this.renderWhitespace === other.renderWhitespace + && this.renderControlCharacters === other.renderControlCharacters + && this.fontLigatures === other.fontLigatures + && LineDecoration.equalsArr(this.lineDecorations, other.lineDecorations) + && ViewLineToken.equalsArr(this.lineTokens, other.lineTokens) + ); + } +} + +export const enum CharacterMappingConstants { + PART_INDEX_MASK = 0b11111111111111110000000000000000, + CHAR_INDEX_MASK = 0b00000000000000001111111111111111, + + CHAR_INDEX_OFFSET = 0, + PART_INDEX_OFFSET = 16 +} + +/** + * Provides a both direction mapping between a line's character and its rendered position. + */ +export class CharacterMapping { + + public static getPartIndex(partData: number): number { + return (partData & CharacterMappingConstants.PART_INDEX_MASK) >>> CharacterMappingConstants.PART_INDEX_OFFSET; + } + + public static getCharIndex(partData: number): number { + return (partData & CharacterMappingConstants.CHAR_INDEX_MASK) >>> CharacterMappingConstants.CHAR_INDEX_OFFSET; + } + + public readonly length: number; + private readonly _data: Uint32Array; + private readonly _absoluteOffsets: Uint32Array; + + constructor(length: number, partCount: number) { + this.length = length; + this._data = new Uint32Array(this.length); + this._absoluteOffsets = new Uint32Array(this.length); + } + + public setPartData(charOffset: number, partIndex: number, charIndex: number, partAbsoluteOffset: number): void { + let partData = ( + (partIndex << CharacterMappingConstants.PART_INDEX_OFFSET) + | (charIndex << CharacterMappingConstants.CHAR_INDEX_OFFSET) + ) >>> 0; + this._data[charOffset] = partData; + this._absoluteOffsets[charOffset] = partAbsoluteOffset + charIndex; + } + + public getAbsoluteOffsets(): Uint32Array { + return this._absoluteOffsets; + } + + public charOffsetToPartData(charOffset: number): number { + if (this.length === 0) { + return 0; + } + if (charOffset < 0) { + return this._data[0]; + } + if (charOffset >= this.length) { + return this._data[this.length - 1]; + } + return this._data[charOffset]; + } + + public partDataToCharOffset(partIndex: number, partLength: number, charIndex: number): number { + if (this.length === 0) { + return 0; + } + + let searchEntry = ( + (partIndex << CharacterMappingConstants.PART_INDEX_OFFSET) + | (charIndex << CharacterMappingConstants.CHAR_INDEX_OFFSET) + ) >>> 0; + + let min = 0; + let max = this.length - 1; + while (min + 1 < max) { + let mid = ((min + max) >>> 1); + let midEntry = this._data[mid]; + if (midEntry === searchEntry) { + return mid; + } else if (midEntry > searchEntry) { + max = mid; + } else { + min = mid; + } + } + + if (min === max) { + return min; + } + + let minEntry = this._data[min]; + let maxEntry = this._data[max]; + + if (minEntry === searchEntry) { + return min; + } + if (maxEntry === searchEntry) { + return max; + } + + let minPartIndex = CharacterMapping.getPartIndex(minEntry); + let minCharIndex = CharacterMapping.getCharIndex(minEntry); + + let maxPartIndex = CharacterMapping.getPartIndex(maxEntry); + let maxCharIndex: number; + + if (minPartIndex !== maxPartIndex) { + // sitting between parts + maxCharIndex = partLength; + } else { + maxCharIndex = CharacterMapping.getCharIndex(maxEntry); + } + + let minEntryDistance = charIndex - minCharIndex; + let maxEntryDistance = maxCharIndex - charIndex; + + if (minEntryDistance <= maxEntryDistance) { + return min; + } + return max; + } +} + +export class RenderLineOutput { + _renderLineOutputBrand: void; + + readonly characterMapping: CharacterMapping; + readonly containsRTL: boolean; + readonly containsForeignElements: boolean; + + constructor(characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: boolean) { + this.characterMapping = characterMapping; + this.containsRTL = containsRTL; + this.containsForeignElements = containsForeignElements; + } +} + +export function renderViewLine(input: RenderLineInput, sb: IStringBuilder): RenderLineOutput { + if (input.lineContent.length === 0) { + + let containsForeignElements = false; + + // This is basically for IE's hit test to work + let content: string = '\u00a0'; + + if (input.lineDecorations.length > 0) { + // This line is empty, but it contains inline decorations + let classNames: string[] = []; + for (let i = 0, len = input.lineDecorations.length; i < len; i++) { + const lineDecoration = input.lineDecorations[i]; + if (lineDecoration.insertsBeforeOrAfter) { + classNames[i] = input.lineDecorations[i].className; + containsForeignElements = true; + } + } + + if (containsForeignElements) { + content = `\u00a0`; + } + } + + sb.appendASCIIString(content); + return new RenderLineOutput( + new CharacterMapping(0, 0), + false, + containsForeignElements + ); + } + + return _renderLine(resolveRenderLineInput(input), sb); +} + +export class RenderLineOutput2 { + constructor( + public readonly characterMapping: CharacterMapping, + public readonly html: string, + public readonly containsRTL: boolean, + public readonly containsForeignElements: boolean + ) { + } +} + +export function renderViewLine2(input: RenderLineInput): RenderLineOutput2 { + let sb = createStringBuilder(10000); + let out = renderViewLine(input, sb); + return new RenderLineOutput2(out.characterMapping, sb.build(), out.containsRTL, out.containsForeignElements); +} + +class ResolvedRenderLineInput { + constructor( + public readonly fontIsMonospace: boolean, + public readonly lineContent: string, + public readonly len: number, + public readonly isOverflowing: boolean, + public readonly parts: LinePart[], + public readonly containsForeignElements: boolean, + public readonly tabSize: number, + public readonly containsRTL: boolean, + public readonly spaceWidth: number, + public readonly renderWhitespace: RenderWhitespace, + public readonly renderControlCharacters: boolean, + ) { + // + } +} + +function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput { + const useMonospaceOptimizations = input.useMonospaceOptimizations; + const lineContent = input.lineContent; + + let isOverflowing: boolean; + let len: number; + + if (input.stopRenderingLineAfter !== -1 && input.stopRenderingLineAfter < lineContent.length) { + isOverflowing = true; + len = input.stopRenderingLineAfter; + } else { + isOverflowing = false; + len = lineContent.length; + } + + let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len); + if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary) { + tokens = _applyRenderWhitespace(lineContent, len, tokens, input.fauxIndentLength, input.tabSize, useMonospaceOptimizations, input.renderWhitespace === RenderWhitespace.Boundary); + } + let containsForeignElements = false; + if (input.lineDecorations.length > 0) { + for (let i = 0, len = input.lineDecorations.length; i < len; i++) { + const lineDecoration = input.lineDecorations[i]; + if (lineDecoration.insertsBeforeOrAfter) { + containsForeignElements = true; + break; + } + } + tokens = _applyInlineDecorations(lineContent, len, tokens, input.lineDecorations); + } + let containsRTL = false; + if (input.mightContainRTL) { + containsRTL = strings.containsRTL(lineContent); + } + if (!containsRTL && !input.fontLigatures) { + tokens = splitLargeTokens(lineContent, tokens); + } + + return new ResolvedRenderLineInput( + useMonospaceOptimizations, + lineContent, + len, + isOverflowing, + tokens, + containsForeignElements, + input.tabSize, + containsRTL, + input.spaceWidth, + input.renderWhitespace, + input.renderControlCharacters + ); +} + +/** + * In the rendering phase, characters are always looped until token.endIndex. + * Ensure that all tokens end before `len` and the last one ends precisely at `len`. + */ +function transformAndRemoveOverflowing(tokens: ViewLineToken[], fauxIndentLength: number, len: number): LinePart[] { + let result: LinePart[] = [], resultLen = 0; + + // The faux indent part of the line should have no token type + if (fauxIndentLength > 0) { + result[resultLen++] = new LinePart(fauxIndentLength, ''); + } + + for (let tokenIndex = 0, tokensLen = tokens.length; tokenIndex < tokensLen; tokenIndex++) { + const token = tokens[tokenIndex]; + const endIndex = token.endIndex; + if (endIndex <= fauxIndentLength) { + // The faux indent part of the line should have no token type + continue; + } + const type = token.getType(); + if (endIndex >= len) { + result[resultLen++] = new LinePart(len, type); + break; + } + result[resultLen++] = new LinePart(endIndex, type); + } + + return result; +} + +/** + * written as a const enum to get value inlining. + */ +const enum Constants { + LongToken = 50 +} + +/** + * See https://github.com/Microsoft/vscode/issues/6885. + * It appears that having very large spans causes very slow reading of character positions. + * So here we try to avoid that. + */ +function splitLargeTokens(lineContent: string, tokens: LinePart[]): LinePart[] { + let lastTokenEndIndex = 0; + let result: LinePart[] = [], resultLen = 0; + for (let i = 0, len = tokens.length; i < len; i++) { + const token = tokens[i]; + const tokenEndIndex = token.endIndex; + let diff = (tokenEndIndex - lastTokenEndIndex); + if (diff > Constants.LongToken) { + const tokenType = token.type; + const piecesCount = Math.ceil(diff / Constants.LongToken); + for (let j = 1; j < piecesCount; j++) { + let pieceEndIndex = lastTokenEndIndex + (j * Constants.LongToken); + let lastCharInPiece = lineContent.charCodeAt(pieceEndIndex - 1); + if (strings.isHighSurrogate(lastCharInPiece)) { + // Don't cut in the middle of a surrogate pair + pieceEndIndex--; + } + result[resultLen++] = new LinePart(pieceEndIndex, tokenType); + } + result[resultLen++] = new LinePart(tokenEndIndex, tokenType); + } else { + result[resultLen++] = token; + } + lastTokenEndIndex = tokenEndIndex; + } + + return result; +} + +/** + * Whitespace is rendered by "replacing" tokens with a special-purpose `vs-whitespace` type that is later recognized in the rendering phase. + * Moreover, a token is created for every visual indent because on some fonts the glyphs used for rendering whitespace (→ or ·) do not have the same width as  . + * The rendering phase will generate `style="width:..."` for these tokens. + */ +function _applyRenderWhitespace(lineContent: string, len: number, tokens: LinePart[], fauxIndentLength: number, tabSize: number, useMonospaceOptimizations: boolean, onlyBoundary: boolean): LinePart[] { + + let result: LinePart[] = [], resultLen = 0; + let tokenIndex = 0; + let tokenType = tokens[tokenIndex].type; + let tokenEndIndex = tokens[tokenIndex].endIndex; + + let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent); + let lastNonWhitespaceIndex: number; + if (firstNonWhitespaceIndex === -1) { + // The entire line is whitespace + firstNonWhitespaceIndex = len; + lastNonWhitespaceIndex = len; + } else { + lastNonWhitespaceIndex = strings.lastNonWhitespaceIndex(lineContent); + } + + let tmpIndent = 0; + for (let charIndex = 0; charIndex < fauxIndentLength; charIndex++) { + const chCode = lineContent.charCodeAt(charIndex); + if (chCode === CharCode.Tab) { + tmpIndent = tabSize; + } else { + tmpIndent++; + } + } + tmpIndent = tmpIndent % tabSize; + + let wasInWhitespace = false; + for (let charIndex = fauxIndentLength; charIndex < len; charIndex++) { + const chCode = lineContent.charCodeAt(charIndex); + + let isInWhitespace: boolean; + if (charIndex < firstNonWhitespaceIndex || charIndex > lastNonWhitespaceIndex) { + // in leading or trailing whitespace + isInWhitespace = true; + } else if (chCode === CharCode.Tab) { + // a tab character is rendered both in all and boundary cases + isInWhitespace = true; + } else if (chCode === CharCode.Space) { + // hit a space character + if (onlyBoundary) { + // rendering only boundary whitespace + if (wasInWhitespace) { + isInWhitespace = true; + } else { + const nextChCode = (charIndex + 1 < len ? lineContent.charCodeAt(charIndex + 1) : CharCode.Null); + isInWhitespace = (nextChCode === CharCode.Space || nextChCode === CharCode.Tab); + } + } else { + isInWhitespace = true; + } + } else { + isInWhitespace = false; + } + + if (wasInWhitespace) { + // was in whitespace token + if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) { + // leaving whitespace token or entering a new indent + result[resultLen++] = new LinePart(charIndex, 'vs-whitespace'); + tmpIndent = tmpIndent % tabSize; + } + } else { + // was in regular token + if (charIndex === tokenEndIndex || (isInWhitespace && charIndex > fauxIndentLength)) { + result[resultLen++] = new LinePart(charIndex, tokenType); + tmpIndent = tmpIndent % tabSize; + } + } + + if (chCode === CharCode.Tab) { + tmpIndent = tabSize; + } else { + tmpIndent++; + } + + wasInWhitespace = isInWhitespace; + + if (charIndex === tokenEndIndex) { + tokenIndex++; + tokenType = tokens[tokenIndex].type; + tokenEndIndex = tokens[tokenIndex].endIndex; + } + } + + if (wasInWhitespace) { + // was in whitespace token + result[resultLen++] = new LinePart(len, 'vs-whitespace'); + } else { + // was in regular token + result[resultLen++] = new LinePart(len, tokenType); + } + + return result; +} + +/** + * Inline decorations are "merged" on top of tokens. + * Special care must be taken when multiple inline decorations are at play and they overlap. + */ +function _applyInlineDecorations(lineContent: string, len: number, tokens: LinePart[], _lineDecorations: LineDecoration[]): LinePart[] { + _lineDecorations.sort(LineDecoration.compare); + const lineDecorations = LineDecorationsNormalizer.normalize(_lineDecorations); + const lineDecorationsLen = lineDecorations.length; + + let lineDecorationIndex = 0; + let result: LinePart[] = [], resultLen = 0, lastResultEndIndex = 0; + for (let tokenIndex = 0, len = tokens.length; tokenIndex < len; tokenIndex++) { + const token = tokens[tokenIndex]; + const tokenEndIndex = token.endIndex; + const tokenType = token.type; + + while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset < tokenEndIndex) { + const lineDecoration = lineDecorations[lineDecorationIndex]; + + if (lineDecoration.startOffset > lastResultEndIndex) { + lastResultEndIndex = lineDecoration.startOffset; + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType); + } + + if (lineDecoration.endOffset + 1 <= tokenEndIndex) { + // This line decoration ends before this token ends + lastResultEndIndex = lineDecoration.endOffset + 1; + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className); + lineDecorationIndex++; + } else { + // This line decoration continues on to the next token + lastResultEndIndex = tokenEndIndex; + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className); + break; + } + } + + if (tokenEndIndex > lastResultEndIndex) { + lastResultEndIndex = tokenEndIndex; + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType); + } + } + + return result; +} + +/** + * This function is on purpose not split up into multiple functions to allow runtime type inference (i.e. performance reasons). + * Notice how all the needed data is fully resolved and passed in (i.e. no other calls). + */ +function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): RenderLineOutput { + const fontIsMonospace = input.fontIsMonospace; + const containsForeignElements = input.containsForeignElements; + const lineContent = input.lineContent; + const len = input.len; + const isOverflowing = input.isOverflowing; + const parts = input.parts; + const tabSize = input.tabSize; + const containsRTL = input.containsRTL; + const spaceWidth = input.spaceWidth; + const renderWhitespace = input.renderWhitespace; + const renderControlCharacters = input.renderControlCharacters; + + const characterMapping = new CharacterMapping(len + 1, parts.length); + + let charIndex = 0; + let tabsCharDelta = 0; + let charOffsetInPart = 0; + + let prevPartContentCnt = 0; + let partAbsoluteOffset = 0; + + sb.appendASCIIString(''); + + for (let partIndex = 0, tokensLen = parts.length; partIndex < tokensLen; partIndex++) { + partAbsoluteOffset += prevPartContentCnt; + + const part = parts[partIndex]; + const partEndIndex = part.endIndex; + const partType = part.type; + const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && (partType.indexOf('vs-whitespace') >= 0)); + charOffsetInPart = 0; + + sb.appendASCIIString(' 0) { + sb.write1(0x2192); // → + insertSpacesCount--; + } + while (insertSpacesCount > 0) { + sb.write1(0xA0); //   + insertSpacesCount--; + } + } else { + // must be CharCode.Space + sb.write1(0xb7); // · + } + + charOffsetInPart++; + } + + prevPartContentCnt = partContentCnt; + + } else { + + let partContentCnt = 0; + + if (containsRTL) { + sb.appendASCIIString(' dir="ltr"'); + } + sb.appendASCII(CharCode.GreaterThan); + + for (; charIndex < partEndIndex; charIndex++) { + characterMapping.setPartData(charIndex, partIndex, charOffsetInPart, partAbsoluteOffset); + const charCode = lineContent.charCodeAt(charIndex); + + switch (charCode) { + case CharCode.Tab: + let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; + tabsCharDelta += insertSpacesCount - 1; + charOffsetInPart += insertSpacesCount - 1; + while (insertSpacesCount > 0) { + sb.write1(0xA0); //   + partContentCnt++; + insertSpacesCount--; + } + break; + + case CharCode.Space: + sb.write1(0xA0); //   + partContentCnt++; + break; + + case CharCode.LessThan: + sb.appendASCIIString('<'); + partContentCnt++; + break; + + case CharCode.GreaterThan: + sb.appendASCIIString('>'); + partContentCnt++; + break; + + case CharCode.Ampersand: + sb.appendASCIIString('&'); + partContentCnt++; + break; + + case CharCode.Null: + sb.appendASCIIString('�'); + partContentCnt++; + break; + + case CharCode.UTF8_BOM: + case CharCode.LINE_SEPARATOR_2028: + sb.write1(0xfffd); + partContentCnt++; + break; + + default: + if (renderControlCharacters && charCode < 32) { + sb.write1(9216 + charCode); + partContentCnt++; + } else { + sb.write1(charCode); + partContentCnt++; + } + } + + charOffsetInPart++; + } + + prevPartContentCnt = partContentCnt; + } + + sb.appendASCIIString(''); + + } + + // When getting client rects for the last character, we will position the + // text range at the end of the span, insteaf of at the beginning of next span + characterMapping.setPartData(len, parts.length - 1, charOffsetInPart, partAbsoluteOffset); + + if (isOverflowing) { + sb.appendASCIIString(''); + } + + sb.appendASCIIString(''); + + return new RenderLineOutput(characterMapping, containsRTL, containsForeignElements); +} diff --git a/src/vs/editor/common/viewLayout/viewLinesViewportData.ts b/src/vs/editor/common/viewLayout/viewLinesViewportData.ts new file mode 100644 index 0000000000..cec1597774 --- /dev/null +++ b/src/vs/editor/common/viewLayout/viewLinesViewportData.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ViewLineRenderingData, IViewModel, ViewModelDecoration, IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; + +export interface IPartialViewLinesViewportData { + /** + * Value to be substracted from `scrollTop` (in order to vertical offset numbers < 1MM) + */ + readonly bigNumbersDelta: number; + /** + * The first (partially) visible line number. + */ + readonly startLineNumber: number; + /** + * The last (partially) visible line number. + */ + readonly endLineNumber: number; + /** + * relativeVerticalOffset[i] is the `top` position for line at `i` + `startLineNumber`. + */ + readonly relativeVerticalOffset: number[]; + /** + * The centered line in the viewport. + */ + readonly centeredLineNumber: number; + /** + * The first completely visible line number. + */ + readonly completelyVisibleStartLineNumber: number; + /** + * The last completely visible line number. + */ + readonly completelyVisibleEndLineNumber: number; +} + +/** + * Contains all data needed to render at a specific viewport. + */ +export class ViewportData { + + public readonly selections: Selection[]; + + /** + * The line number at which to start rendering (inclusive). + */ + public readonly startLineNumber: number; + + /** + * The line number at which to end rendering (inclusive). + */ + public readonly endLineNumber: number; + + /** + * relativeVerticalOffset[i] is the `top` position for line at `i` + `startLineNumber`. + */ + public readonly relativeVerticalOffset: number[]; + + /** + * The viewport as a range (startLineNumber,1) -> (endLineNumber,maxColumn(endLineNumber)). + */ + public readonly visibleRange: Range; + + /** + * Value to be substracted from `scrollTop` (in order to vertical offset numbers < 1MM) + */ + public readonly bigNumbersDelta: number; + + /** + * Positioning information about gaps whitespace. + */ + public readonly whitespaceViewportData: IViewWhitespaceViewportData[]; + + private readonly _model: IViewModel; + + constructor( + selections: Selection[], + partialData: IPartialViewLinesViewportData, + whitespaceViewportData: IViewWhitespaceViewportData[], + model: IViewModel + ) { + this.selections = selections; + this.startLineNumber = partialData.startLineNumber | 0; + this.endLineNumber = partialData.endLineNumber | 0; + this.relativeVerticalOffset = partialData.relativeVerticalOffset; + this.bigNumbersDelta = partialData.bigNumbersDelta | 0; + this.whitespaceViewportData = whitespaceViewportData; + + this._model = model; + + this.visibleRange = new Range( + partialData.startLineNumber, + this._model.getLineMinColumn(partialData.startLineNumber), + partialData.endLineNumber, + this._model.getLineMaxColumn(partialData.endLineNumber) + ); + } + + public getViewLineRenderingData(lineNumber: number): ViewLineRenderingData { + return this._model.getViewLineRenderingData(this.visibleRange, lineNumber); + } + + public getDecorationsInViewport(): ViewModelDecoration[] { + return this._model.getDecorationsInViewport(this.visibleRange); + } +} diff --git a/src/vs/editor/common/viewLayout/whitespaceComputer.ts b/src/vs/editor/common/viewLayout/whitespaceComputer.ts new file mode 100644 index 0000000000..979e13a919 --- /dev/null +++ b/src/vs/editor/common/viewLayout/whitespaceComputer.ts @@ -0,0 +1,465 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +export interface IEditorWhitespace { + readonly id: number; + readonly afterLineNumber: number; + readonly heightInLines: number; +} + +/** + * Represent whitespaces in between lines and provide fast CRUD management methods. + * The whitespaces are sorted ascending by `afterLineNumber`. + */ +export class WhitespaceComputer { + + /** + * heights[i] is the height in pixels for whitespace at index i + */ + private _heights: number[]; + + /** + * afterLineNumbers[i] is the line number whitespace at index i is after + */ + private _afterLineNumbers: number[]; + + /** + * ordinals[i] is the orinal of the whitespace at index i + */ + private _ordinals: number[]; + + /** + * prefixSum[i] = SUM(heights[j]), 1 <= j <= i + */ + private _prefixSum: number[]; + + /** + * prefixSum[i], 1 <= i <= prefixSumValidIndex can be trusted + */ + private _prefixSumValidIndex: number; + + /** + * ids[i] is the whitespace id of whitespace at index i + */ + private _ids: number[]; + + /** + * index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members) + */ + private _whitespaceId2Index: { + [id: string]: number; + }; + + /** + * last whitespace id issued + */ + private _lastWhitespaceId: number; + + constructor() { + this._heights = []; + this._ids = []; + this._afterLineNumbers = []; + this._ordinals = []; + this._prefixSum = []; + this._prefixSumValidIndex = -1; + this._whitespaceId2Index = {}; + this._lastWhitespaceId = 0; + } + + /** + * Find the insertion index for a new value inside a sorted array of values. + * If the value is already present in the sorted array, the insertion index will be after the already existing value. + */ + public static findInsertionIndex(sortedArray: number[], value: number, ordinals: number[], valueOrdinal: number): number { + let low = 0; + let high = sortedArray.length; + + while (low < high) { + let mid = ((low + high) >>> 1); + + if (value === sortedArray[mid]) { + if (valueOrdinal < ordinals[mid]) { + high = mid; + } else { + low = mid + 1; + } + } else if (value < sortedArray[mid]) { + high = mid; + } else { + low = mid + 1; + } + } + + return low; + } + + /** + * Insert a new whitespace of a certain height after a line number. + * The whitespace has a "sticky" characteristic. + * Irrespective of edits above or below `afterLineNumber`, the whitespace will follow the initial line. + * + * @param afterLineNumber The conceptual position of this whitespace. The whitespace will follow this line as best as possible even when deleting/inserting lines above/below. + * @param heightInPx The height of the whitespace, in pixels. + * @return An id that can be used later to mutate or delete the whitespace + */ + public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number): number { + afterLineNumber = afterLineNumber | 0; + ordinal = ordinal | 0; + heightInPx = heightInPx | 0; + + let id = (++this._lastWhitespaceId); + let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, afterLineNumber, this._ordinals, ordinal); + this._insertWhitespaceAtIndex(id, insertionIndex, afterLineNumber, ordinal, heightInPx); + return id; + } + + private _insertWhitespaceAtIndex(id: number, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number): void { + id = id | 0; + insertIndex = insertIndex | 0; + afterLineNumber = afterLineNumber | 0; + ordinal = ordinal | 0; + heightInPx = heightInPx | 0; + + this._heights.splice(insertIndex, 0, heightInPx); + this._ids.splice(insertIndex, 0, id); + this._afterLineNumbers.splice(insertIndex, 0, afterLineNumber); + this._ordinals.splice(insertIndex, 0, ordinal); + this._prefixSum.splice(insertIndex, 0, 0); + + let keys = Object.keys(this._whitespaceId2Index); + for (let i = 0, len = keys.length; i < len; i++) { + let sid = keys[i]; + let oldIndex = this._whitespaceId2Index[sid]; + if (oldIndex >= insertIndex) { + this._whitespaceId2Index[sid] = oldIndex + 1; + } + } + + this._whitespaceId2Index[id.toString()] = insertIndex; + this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1); + } + + /** + * Change properties associated with a certain whitespace. + */ + public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean { + id = id | 0; + newAfterLineNumber = newAfterLineNumber | 0; + newHeight = newHeight | 0; + + let hasChanges = false; + hasChanges = this.changeWhitespaceHeight(id, newHeight) || hasChanges; + hasChanges = this.changeWhitespaceAfterLineNumber(id, newAfterLineNumber) || hasChanges; + return hasChanges; + } + + /** + * Change the height of an existing whitespace + * + * @param id The whitespace to change + * @param newHeightInPx The new height of the whitespace, in pixels + * @return Returns true if the whitespace is found and if the new height is different than the old height + */ + public changeWhitespaceHeight(id: number, newHeightInPx: number): boolean { + id = id | 0; + newHeightInPx = newHeightInPx | 0; + + let sid = id.toString(); + if (this._whitespaceId2Index.hasOwnProperty(sid)) { + let index = this._whitespaceId2Index[sid]; + if (this._heights[index] !== newHeightInPx) { + this._heights[index] = newHeightInPx; + this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1); + return true; + } + } + return false; + } + + /** + * Change the line number after which an existing whitespace flows. + * + * @param id The whitespace to change + * @param newAfterLineNumber The new line number the whitespace will follow + * @return Returns true if the whitespace is found and if the new line number is different than the old line number + */ + public changeWhitespaceAfterLineNumber(id: number, newAfterLineNumber: number): boolean { + id = id | 0; + newAfterLineNumber = newAfterLineNumber | 0; + + let sid = id.toString(); + if (this._whitespaceId2Index.hasOwnProperty(sid)) { + let index = this._whitespaceId2Index[sid]; + if (this._afterLineNumbers[index] !== newAfterLineNumber) { + // `afterLineNumber` changed for this whitespace + + // Record old ordinal + let ordinal = this._ordinals[index]; + + // Record old height + let heightInPx = this._heights[index]; + + // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace + this.removeWhitespace(id); + + // And add it again + let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, newAfterLineNumber, this._ordinals, ordinal); + this._insertWhitespaceAtIndex(id, insertionIndex, newAfterLineNumber, ordinal, heightInPx); + + return true; + } + } + return false; + } + + /** + * Remove an existing whitespace. + * + * @param id The whitespace to remove + * @return Returns true if the whitespace is found and it is removed. + */ + public removeWhitespace(id: number): boolean { + id = id | 0; + + let sid = id.toString(); + + if (this._whitespaceId2Index.hasOwnProperty(sid)) { + let index = this._whitespaceId2Index[sid]; + delete this._whitespaceId2Index[sid]; + this._removeWhitespaceAtIndex(index); + return true; + } + + return false; + } + + private _removeWhitespaceAtIndex(removeIndex: number): void { + removeIndex = removeIndex | 0; + + this._heights.splice(removeIndex, 1); + this._ids.splice(removeIndex, 1); + this._afterLineNumbers.splice(removeIndex, 1); + this._ordinals.splice(removeIndex, 1); + this._prefixSum.splice(removeIndex, 1); + this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1); + + let keys = Object.keys(this._whitespaceId2Index); + for (let i = 0, len = keys.length; i < len; i++) { + let sid = keys[i]; + let oldIndex = this._whitespaceId2Index[sid]; + if (oldIndex >= removeIndex) { + this._whitespaceId2Index[sid] = oldIndex - 1; + } + } + } + + /** + * Notify the computer that lines have been deleted (a continuous zone of lines). + * This gives it a chance to update `afterLineNumber` for whitespaces, giving the "sticky" characteristic. + * + * @param fromLineNumber The line number at which the deletion started, inclusive + * @param toLineNumber The line number at which the deletion ended, inclusive + */ + public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void { + fromLineNumber = fromLineNumber | 0; + toLineNumber = toLineNumber | 0; + + for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { + let afterLineNumber = this._afterLineNumbers[i]; + + if (fromLineNumber <= afterLineNumber && afterLineNumber <= toLineNumber) { + // The line this whitespace was after has been deleted + // => move whitespace to before first deleted line + this._afterLineNumbers[i] = fromLineNumber - 1; + } else if (afterLineNumber > toLineNumber) { + // The line this whitespace was after has been moved up + // => move whitespace up + this._afterLineNumbers[i] -= (toLineNumber - fromLineNumber + 1); + } + } + } + + /** + * Notify the computer that lines have been inserted (a continuous zone of lines). + * This gives it a chance to update `afterLineNumber` for whitespaces, giving the "sticky" characteristic. + * + * @param fromLineNumber The line number at which the insertion started, inclusive + * @param toLineNumber The line number at which the insertion ended, inclusive. + */ + public onLinesInserted(fromLineNumber: number, toLineNumber: number): void { + fromLineNumber = fromLineNumber | 0; + toLineNumber = toLineNumber | 0; + + for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { + let afterLineNumber = this._afterLineNumbers[i]; + + if (fromLineNumber <= afterLineNumber) { + this._afterLineNumbers[i] += (toLineNumber - fromLineNumber + 1); + } + } + } + + /** + * Get the sum of all the whitespaces. + */ + public getTotalHeight(): number { + if (this._heights.length === 0) { + return 0; + } + return this.getAccumulatedHeight(this._heights.length - 1); + } + + /** + * Return the sum of the heights of the whitespaces at [0..index]. + * This includes the whitespace at `index`. + * + * @param index The index of the whitespace. + * @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`. + */ + public getAccumulatedHeight(index: number): number { + index = index | 0; + + let startIndex = Math.max(0, this._prefixSumValidIndex + 1); + if (startIndex === 0) { + this._prefixSum[0] = this._heights[0]; + startIndex++; + } + + for (let i = startIndex; i <= index; i++) { + this._prefixSum[i] = this._prefixSum[i - 1] + this._heights[i]; + } + this._prefixSumValidIndex = Math.max(this._prefixSumValidIndex, index); + return this._prefixSum[index]; + } + + /** + * Find all whitespaces with `afterLineNumber` < `lineNumber` and return the sum of their heights. + * + * @param lineNumber The line number whitespaces should be before. + * @return The sum of the heights of the whitespaces before `lineNumber`. + */ + public getAccumulatedHeightBeforeLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); + + if (lastWhitespaceBeforeLineNumber === -1) { + return 0; + } + + return this.getAccumulatedHeight(lastWhitespaceBeforeLineNumber); + } + + private _findLastWhitespaceBeforeLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + // Find the whitespace before line number + let afterLineNumbers = this._afterLineNumbers; + let low = 0; + let high = afterLineNumbers.length - 1; + + while (low <= high) { + let delta = (high - low) | 0; + let halfDelta = (delta / 2) | 0; + let mid = (low + halfDelta) | 0; + + if (afterLineNumbers[mid] < lineNumber) { + if (mid + 1 >= afterLineNumbers.length || afterLineNumbers[mid + 1] >= lineNumber) { + return mid; + } else { + low = (mid + 1) | 0; + } + } else { + high = (mid - 1) | 0; + } + } + + return -1; + } + + private _findFirstWhitespaceAfterLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); + let firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1; + + if (firstWhitespaceAfterLineNumber < this._heights.length) { + return firstWhitespaceAfterLineNumber; + } + + return -1; + } + + /** + * Find the index of the first whitespace which has `afterLineNumber` >= `lineNumber`. + * @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found. + */ + public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + return this._findFirstWhitespaceAfterLineNumber(lineNumber); + } + + /** + * The number of whitespaces. + */ + public getCount(): number { + return this._heights.length; + } + + /** + * Get the `afterLineNumber` for whitespace at index `index`. + * + * @param index The index of the whitespace. + * @return `afterLineNumber` of whitespace at `index`. + */ + public getAfterLineNumberForWhitespaceIndex(index: number): number { + index = index | 0; + + return this._afterLineNumbers[index]; + } + + /** + * Get the `id` for whitespace at index `index`. + * + * @param index The index of the whitespace. + * @return `id` of whitespace at `index`. + */ + public getIdForWhitespaceIndex(index: number): number { + index = index | 0; + + return this._ids[index]; + } + + /** + * Get the `height` for whitespace at index `index`. + * + * @param index The index of the whitespace. + * @return `height` of whitespace at `index`. + */ + public getHeightForWhitespaceIndex(index: number): number { + index = index | 0; + + return this._heights[index]; + } + + /** + * Get all whitespaces. + */ + public getWhitespaces(deviceLineHeight: number): IEditorWhitespace[] { + deviceLineHeight = deviceLineHeight | 0; + + let result: IEditorWhitespace[] = []; + for (let i = 0; i < this._heights.length; i++) { + result.push({ + id: this._ids[i], + afterLineNumber: this._afterLineNumbers[i], + heightInLines: this._heights[i] / deviceLineHeight + }); + } + return result; + } +} diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts new file mode 100644 index 0000000000..f4940ac945 --- /dev/null +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -0,0 +1,277 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; +import { ILineMapperFactory, ILineMapping, OutputPosition } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { CharCode } from 'vs/base/common/charCode'; +import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; +import { toUint32Array } from 'vs/editor/common/core/uint'; +import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; + +const enum CharacterClass { + NONE = 0, + BREAK_BEFORE = 1, + BREAK_AFTER = 2, + BREAK_OBTRUSIVE = 3, + BREAK_IDEOGRAPHIC = 4 // for Han and Kana. +} + +class WrappingCharacterClassifier extends CharacterClassifier { + + constructor(BREAK_BEFORE: string, BREAK_AFTER: string, BREAK_OBTRUSIVE: string) { + super(CharacterClass.NONE); + + for (let i = 0; i < BREAK_BEFORE.length; i++) { + this.set(BREAK_BEFORE.charCodeAt(i), CharacterClass.BREAK_BEFORE); + } + + for (let i = 0; i < BREAK_AFTER.length; i++) { + this.set(BREAK_AFTER.charCodeAt(i), CharacterClass.BREAK_AFTER); + } + + for (let i = 0; i < BREAK_OBTRUSIVE.length; i++) { + this.set(BREAK_OBTRUSIVE.charCodeAt(i), CharacterClass.BREAK_OBTRUSIVE); + } + } + + public get(charCode: number): CharacterClass { + // Initialize CharacterClass.BREAK_IDEOGRAPHIC for these Unicode ranges: + // 1. CJK Unified Ideographs (0x4E00 -- 0x9FFF) + // 2. CJK Unified Ideographs Extension A (0x3400 -- 0x4DBF) + // 3. Hiragana and Katakana (0x3040 -- 0x30FF) + if ( + (charCode >= 0x3040 && charCode <= 0x30FF) + || (charCode >= 0x3400 && charCode <= 0x4DBF) + || (charCode >= 0x4E00 && charCode <= 0x9FFF) + ) { + return CharacterClass.BREAK_IDEOGRAPHIC; + } + + return super.get(charCode); + } +} + +export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactory { + + private classifier: WrappingCharacterClassifier; + + constructor(breakBeforeChars: string, breakAfterChars: string, breakObtrusiveChars: string) { + this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars, breakObtrusiveChars); + } + + // TODO@Alex -> duplicated in lineCommentCommand + private static nextVisibleColumn(currentVisibleColumn: number, tabSize: number, isTab: boolean, columnSize: number): number { + currentVisibleColumn = +currentVisibleColumn; //@perf + tabSize = +tabSize; //@perf + columnSize = +columnSize; //@perf + + if (isTab) { + return currentVisibleColumn + (tabSize - (currentVisibleColumn % tabSize)); + } + return currentVisibleColumn + columnSize; + } + + public createLineMapping(lineText: string, tabSize: number, breakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): ILineMapping { + if (breakingColumn === -1) { + return null; + } + + tabSize = +tabSize; //@perf + breakingColumn = +breakingColumn; //@perf + columnsForFullWidthChar = +columnsForFullWidthChar; //@perf + hardWrappingIndent = +hardWrappingIndent; //@perf + + let wrappedTextIndentVisibleColumn = 0; + let wrappedTextIndent = ''; + + let firstNonWhitespaceIndex = -1; + if (hardWrappingIndent !== WrappingIndent.None) { + firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineText); + if (firstNonWhitespaceIndex !== -1) { + wrappedTextIndent = lineText.substring(0, firstNonWhitespaceIndex); + for (let i = 0; i < firstNonWhitespaceIndex; i++) { + wrappedTextIndentVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(wrappedTextIndentVisibleColumn, tabSize, lineText.charCodeAt(i) === CharCode.Tab, 1); + } + if (hardWrappingIndent === WrappingIndent.Indent) { + wrappedTextIndent += '\t'; + wrappedTextIndentVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(wrappedTextIndentVisibleColumn, tabSize, true, 1); + } + // Force sticking to beginning of line if indentColumn > 66% breakingColumn + if (wrappedTextIndentVisibleColumn > 1 / 2 * breakingColumn) { + wrappedTextIndent = ''; + wrappedTextIndentVisibleColumn = 0; + } + } + } + + let classifier = this.classifier; + let lastBreakingOffset = 0; // Last 0-based offset in the lineText at which a break happened + let breakingLengths: number[] = []; // The length of each broken-up line text + let breakingLengthsIndex: number = 0; // The count of breaks already done + let visibleColumn = 0; // Visible column since the beginning of the current line + let niceBreakOffset = -1; // Last index of a character that indicates a break should happen before it (more desirable) + let niceBreakVisibleColumn = 0; // visible column if a break were to be later introduced before `niceBreakOffset` + let obtrusiveBreakOffset = -1; // Last index of a character that indicates a break should happen before it (less desirable) + let obtrusiveBreakVisibleColumn = 0; // visible column if a break were to be later introduced before `obtrusiveBreakOffset` + let len = lineText.length; + + for (let i = 0; i < len; i++) { + // At this point, there is a certainty that the character before `i` fits on the current line, + // but the character at `i` might not fit + + let charCode = lineText.charCodeAt(i); + let charCodeIsTab = (charCode === CharCode.Tab); + let charCodeClass = classifier.get(charCode); + + if (charCodeClass === CharacterClass.BREAK_BEFORE) { + // This is a character that indicates that a break should happen before it + // Since we are certain the character before `i` fits, there's no extra checking needed, + // just mark it as a nice breaking opportunity + niceBreakOffset = i; + niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; + } + + // CJK breaking : before break + if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i > 0) { + let prevCode = lineText.charCodeAt(i - 1); + let prevClass = classifier.get(prevCode); + if (prevClass !== CharacterClass.BREAK_BEFORE) { // Kinsoku Shori: Don't break after a leading character, like an open bracket + niceBreakOffset = i; + niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; + } + } + + let charColumnSize = 1; + if (strings.isFullWidthCharacter(charCode)) { + charColumnSize = columnsForFullWidthChar; + } + + // Advance visibleColumn with character at `i` + visibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(visibleColumn, tabSize, charCodeIsTab, charColumnSize); + + if (visibleColumn > breakingColumn && i !== 0) { + // We need to break at least before character at `i`: + // - break before niceBreakLastOffset if it exists (and re-establish a correct visibleColumn by using niceBreakVisibleColumn + charAt(i)) + // - otherwise, break before obtrusiveBreakLastOffset if it exists (and re-establish a correct visibleColumn by using obtrusiveBreakVisibleColumn + charAt(i)) + // - otherwise, break before i (and re-establish a correct visibleColumn by charAt(i)) + + let breakBeforeOffset: number; + let restoreVisibleColumnFrom: number; + + if (niceBreakOffset !== -1 && niceBreakVisibleColumn <= breakingColumn) { + + // We will break before `niceBreakLastOffset` + breakBeforeOffset = niceBreakOffset; + restoreVisibleColumnFrom = niceBreakVisibleColumn; + + } else if (obtrusiveBreakOffset !== -1 && obtrusiveBreakVisibleColumn <= breakingColumn) { + + // We will break before `obtrusiveBreakLastOffset` + breakBeforeOffset = obtrusiveBreakOffset; + restoreVisibleColumnFrom = obtrusiveBreakVisibleColumn; + + } else { + + // We will break before `i` + breakBeforeOffset = i; + restoreVisibleColumnFrom = wrappedTextIndentVisibleColumn; + + } + + // Break before character at `breakBeforeOffset` + breakingLengths[breakingLengthsIndex++] = breakBeforeOffset - lastBreakingOffset; + lastBreakingOffset = breakBeforeOffset; + + // Re-establish visibleColumn by taking character at `i` into account + visibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(restoreVisibleColumnFrom, tabSize, charCodeIsTab, charColumnSize); + + // Reset markers + niceBreakOffset = -1; + niceBreakVisibleColumn = 0; + obtrusiveBreakOffset = -1; + obtrusiveBreakVisibleColumn = 0; + } + + // At this point, there is a certainty that the character at `i` fits on the current line + + if (niceBreakOffset !== -1) { + // Advance niceBreakVisibleColumn + niceBreakVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(niceBreakVisibleColumn, tabSize, charCodeIsTab, charColumnSize); + } + if (obtrusiveBreakOffset !== -1) { + // Advance obtrusiveBreakVisibleColumn + obtrusiveBreakVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(obtrusiveBreakVisibleColumn, tabSize, charCodeIsTab, charColumnSize); + } + + if (charCodeClass === CharacterClass.BREAK_AFTER && (hardWrappingIndent === WrappingIndent.None || i >= firstNonWhitespaceIndex)) { + // This is a character that indicates that a break should happen after it + niceBreakOffset = i + 1; + niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; + } + + // CJK breaking : after break + if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i < len - 1) { + let nextCode = lineText.charCodeAt(i + 1); + let nextClass = classifier.get(nextCode); + if (nextClass !== CharacterClass.BREAK_AFTER) { // Kinsoku Shori: Don't break before a trailing character, like a period + niceBreakOffset = i + 1; + niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; + } + } + + if (charCodeClass === CharacterClass.BREAK_OBTRUSIVE) { + // This is an obtrusive character that indicates that a break should happen after it + obtrusiveBreakOffset = i + 1; + obtrusiveBreakVisibleColumn = wrappedTextIndentVisibleColumn; + } + } + + if (breakingLengthsIndex === 0) { + return null; + } + + // Add last segment + breakingLengths[breakingLengthsIndex++] = len - lastBreakingOffset; + + return new CharacterHardWrappingLineMapping( + new PrefixSumComputer(toUint32Array(breakingLengths)), + wrappedTextIndent + ); + } +} + +export class CharacterHardWrappingLineMapping implements ILineMapping { + + private _prefixSums: PrefixSumComputer; + private _wrappedLinesIndent: string; + + constructor(prefixSums: PrefixSumComputer, wrappedLinesIndent: string) { + this._prefixSums = prefixSums; + this._wrappedLinesIndent = wrappedLinesIndent; + } + + public getOutputLineCount(): number { + return this._prefixSums.getCount(); + } + + public getWrappedLinesIndent(): string { + return this._wrappedLinesIndent; + } + + public getInputOffsetOfOutputPosition(outputLineIndex: number, outputOffset: number): number { + if (outputLineIndex === 0) { + return outputOffset; + } else { + return this._prefixSums.getAccumulatedValue(outputLineIndex - 1) + outputOffset; + } + } + + public getOutputPositionOfInputOffset(inputOffset: number): OutputPosition { + let r = this._prefixSums.getIndexOf(inputOffset); + return new OutputPosition(r.index, r.remainder); + } +} diff --git a/src/vs/editor/common/viewModel/prefixSumComputer.ts b/src/vs/editor/common/viewModel/prefixSumComputer.ts new file mode 100644 index 0000000000..c89266dd43 --- /dev/null +++ b/src/vs/editor/common/viewModel/prefixSumComputer.ts @@ -0,0 +1,264 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { toUint32 } from 'vs/editor/common/core/uint'; + +export class PrefixSumIndexOfResult { + _prefixSumIndexOfResultBrand: void; + + index: number; + remainder: number; + + constructor(index: number, remainder: number) { + this.index = index; + this.remainder = remainder; + } +} + +export class PrefixSumComputer { + + /** + * values[i] is the value at index i + */ + private values: Uint32Array; + + /** + * prefixSum[i] = SUM(heights[j]), 0 <= j <= i + */ + private prefixSum: Uint32Array; + + /** + * prefixSum[i], 0 <= i <= prefixSumValidIndex can be trusted + */ + private prefixSumValidIndex: Int32Array; + + constructor(values: Uint32Array) { + this.values = values; + this.prefixSum = new Uint32Array(values.length); + this.prefixSumValidIndex = new Int32Array(1); + this.prefixSumValidIndex[0] = -1; + } + + public getCount(): number { + return this.values.length; + } + + public insertValues(insertIndex: number, insertValues: Uint32Array): boolean { + insertIndex = toUint32(insertIndex); + const oldValues = this.values; + const oldPrefixSum = this.prefixSum; + const insertValuesLen = insertValues.length; + + if (insertValuesLen === 0) { + return false; + } + + this.values = new Uint32Array(oldValues.length + insertValuesLen); + this.values.set(oldValues.subarray(0, insertIndex), 0); + this.values.set(oldValues.subarray(insertIndex), insertIndex + insertValuesLen); + this.values.set(insertValues, insertIndex); + + if (insertIndex - 1 < this.prefixSumValidIndex[0]) { + this.prefixSumValidIndex[0] = insertIndex - 1; + } + + this.prefixSum = new Uint32Array(this.values.length); + if (this.prefixSumValidIndex[0] >= 0) { + this.prefixSum.set(oldPrefixSum.subarray(0, this.prefixSumValidIndex[0] + 1)); + } + return true; + } + + public changeValue(index: number, value: number): boolean { + index = toUint32(index); + value = toUint32(value); + + if (this.values[index] === value) { + return false; + } + this.values[index] = value; + if (index - 1 < this.prefixSumValidIndex[0]) { + this.prefixSumValidIndex[0] = index - 1; + } + return true; + } + + public removeValues(startIndex: number, cnt: number): boolean { + startIndex = toUint32(startIndex); + cnt = toUint32(cnt); + + const oldValues = this.values; + const oldPrefixSum = this.prefixSum; + + if (startIndex >= oldValues.length) { + return false; + } + + let maxCnt = oldValues.length - startIndex; + if (cnt >= maxCnt) { + cnt = maxCnt; + } + + if (cnt === 0) { + return false; + } + + this.values = new Uint32Array(oldValues.length - cnt); + this.values.set(oldValues.subarray(0, startIndex), 0); + this.values.set(oldValues.subarray(startIndex + cnt), startIndex); + + this.prefixSum = new Uint32Array(this.values.length); + if (startIndex - 1 < this.prefixSumValidIndex[0]) { + this.prefixSumValidIndex[0] = startIndex - 1; + } + if (this.prefixSumValidIndex[0] >= 0) { + this.prefixSum.set(oldPrefixSum.subarray(0, this.prefixSumValidIndex[0] + 1)); + } + return true; + } + + public getTotalValue(): number { + if (this.values.length === 0) { + return 0; + } + return this._getAccumulatedValue(this.values.length - 1); + } + + public getAccumulatedValue(index: number): number { + if (index < 0) { + return 0; + } + + index = toUint32(index); + return this._getAccumulatedValue(index); + } + + private _getAccumulatedValue(index: number): number { + if (index <= this.prefixSumValidIndex[0]) { + return this.prefixSum[index]; + } + + let startIndex = this.prefixSumValidIndex[0] + 1; + if (startIndex === 0) { + this.prefixSum[0] = this.values[0]; + startIndex++; + } + + if (index >= this.values.length) { + index = this.values.length - 1; + } + + for (let i = startIndex; i <= index; i++) { + this.prefixSum[i] = this.prefixSum[i - 1] + this.values[i]; + } + this.prefixSumValidIndex[0] = Math.max(this.prefixSumValidIndex[0], index); + return this.prefixSum[index]; + } + + public getIndexOf(accumulatedValue: number): PrefixSumIndexOfResult { + accumulatedValue = Math.floor(accumulatedValue); //@perf + + // Compute all sums (to get a fully valid prefixSum) + this.getTotalValue(); + + let low = 0; + let high = this.values.length - 1; + let mid: number; + let midStop: number; + let midStart: number; + + while (low <= high) { + mid = low + ((high - low) / 2) | 0; + + midStop = this.prefixSum[mid]; + midStart = midStop - this.values[mid]; + + if (accumulatedValue < midStart) { + high = mid - 1; + } else if (accumulatedValue >= midStop) { + low = mid + 1; + } else { + break; + } + } + + return new PrefixSumIndexOfResult(mid, accumulatedValue - midStart); + } +} + +export class PrefixSumComputerWithCache { + + private readonly _actual: PrefixSumComputer; + private _cacheAccumulatedValueStart: number = 0; + private _cache: PrefixSumIndexOfResult[] = null; + + constructor(values: Uint32Array) { + this._actual = new PrefixSumComputer(values); + this._bustCache(); + } + + private _bustCache(): void { + this._cacheAccumulatedValueStart = 0; + this._cache = null; + } + + public getCount(): number { + return this._actual.getCount(); + } + + public insertValues(insertIndex: number, insertValues: Uint32Array): void { + if (this._actual.insertValues(insertIndex, insertValues)) { + this._bustCache(); + } + } + + public changeValue(index: number, value: number): void { + if (this._actual.changeValue(index, value)) { + this._bustCache(); + } + } + + public removeValues(startIndex: number, cnt: number): void { + if (this._actual.removeValues(startIndex, cnt)) { + this._bustCache(); + } + } + + public getTotalValue(): number { + return this._actual.getTotalValue(); + } + + public getAccumulatedValue(index: number): number { + return this._actual.getAccumulatedValue(index); + } + + public getIndexOf(accumulatedValue: number): PrefixSumIndexOfResult { + accumulatedValue = Math.floor(accumulatedValue); //@perf + + if (this._cache !== null) { + let cacheIndex = accumulatedValue - this._cacheAccumulatedValueStart; + if (cacheIndex >= 0 && cacheIndex < this._cache.length) { + // Cache hit! + return this._cache[cacheIndex]; + } + } + + // Cache miss! + return this._actual.getIndexOf(accumulatedValue); + } + + /** + * Gives a hint that a lot of requests are about to come in for these accumulated values. + */ + public warmUpCache(accumulatedValueStart: number, accumulatedValueEnd: number): void { + let newCache: PrefixSumIndexOfResult[] = []; + for (let accumulatedValue = accumulatedValueStart; accumulatedValue <= accumulatedValueEnd; accumulatedValue++) { + newCache[accumulatedValue - accumulatedValueStart] = this.getIndexOf(accumulatedValue); + } + this._cache = newCache; + this._cacheAccumulatedValueStart = accumulatedValueStart; + } +} diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts new file mode 100644 index 0000000000..fc1253ddc3 --- /dev/null +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -0,0 +1,1096 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { PrefixSumComputerWithCache } from 'vs/editor/common/viewModel/prefixSumComputer'; +import { ViewLineData, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; + +export class OutputPosition { + _outputPositionBrand: void; + outputLineIndex: number; + outputOffset: number; + + constructor(outputLineIndex: number, outputOffset: number) { + this.outputLineIndex = outputLineIndex; + this.outputOffset = outputOffset; + } +} + +export interface ILineMapping { + getOutputLineCount(): number; + getWrappedLinesIndent(): string; + getInputOffsetOfOutputPosition(outputLineIndex: number, outputOffset: number): number; + getOutputPositionOfInputOffset(inputOffset: number): OutputPosition; +} + +export interface ILineMapperFactory { + createLineMapping(lineText: string, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMapping; +} + +export interface IModel { + getLineTokens(lineNumber: number): LineTokens; + getLineContent(lineNumber: number): string; + getLineMinColumn(lineNumber: number): number; + getLineMaxColumn(lineNumber: number): number; +} + +export interface ISplitLine { + isVisible(): boolean; + setVisible(isVisible: boolean): ISplitLine; + + getViewLineCount(): number; + getViewLineContent(model: IModel, modelLineNumber: number, outputLineIndex: number): string; + getViewLineMinColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number; + getViewLineMaxColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number; + getViewLineData(model: IModel, modelLineNumber: number, outputLineIndex: number): ViewLineData; + getViewLinesData(model: IModel, modelLineNumber: number, fromOuputLineIndex: number, toOutputLineIndex: number, globalStartIndex: number, needed: boolean[], result: ViewLineData[]): void; + + getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number; + getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position; +} + +export interface IViewModelLinesCollection { + createCoordinatesConverter(): ICoordinatesConverter; + + dispose(): void; + + setWrappingSettings(wrappingIndent: WrappingIndent, wrappingColumn: number, columnsForFullWidthChar: number): boolean; + setTabSize(newTabSize: number): boolean; + setHiddenAreas(_ranges: Range[]): boolean; + + onModelFlushed(): void; + onModelLinesDeleted(versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent; + onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, text: string[]): viewEvents.ViewLinesInsertedEvent; + onModelLineChanged(versionId: number, lineNumber: number, newText: string): [boolean, viewEvents.ViewLinesChangedEvent, viewEvents.ViewLinesInsertedEvent, viewEvents.ViewLinesDeletedEvent]; + acceptVersionId(versionId: number): void; + + getViewLineCount(): number; + warmUpLookupCache(viewStartLineNumber: number, viewEndLineNumber: number): void; + getViewLineIndentGuide(viewLineNumber: number): number; + getViewLineContent(viewLineNumber: number): string; + getViewLineMinColumn(viewLineNumber: number): number; + getViewLineMaxColumn(viewLineNumber: number): number; + getViewLineData(viewLineNumber: number): ViewLineData; + getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): ViewLineData[]; +} + +export class CoordinatesConverter implements ICoordinatesConverter { + + private readonly _lines: SplitLinesCollection; + + constructor(lines: SplitLinesCollection) { + this._lines = lines; + } + + // View -> Model conversion and related methods + + public convertViewPositionToModelPosition(viewPosition: Position): Position { + return this._lines.convertViewPositionToModelPosition(viewPosition.lineNumber, viewPosition.column); + } + + public convertViewRangeToModelRange(viewRange: Range): Range { + let start = this._lines.convertViewPositionToModelPosition(viewRange.startLineNumber, viewRange.startColumn); + let end = this._lines.convertViewPositionToModelPosition(viewRange.endLineNumber, viewRange.endColumn); + return new Range(start.lineNumber, start.column, end.lineNumber, end.column); + } + + public convertViewSelectionToModelSelection(viewSelection: Selection): Selection { + let selectionStart = this._lines.convertViewPositionToModelPosition(viewSelection.selectionStartLineNumber, viewSelection.selectionStartColumn); + let position = this._lines.convertViewPositionToModelPosition(viewSelection.positionLineNumber, viewSelection.positionColumn); + return new Selection(selectionStart.lineNumber, selectionStart.column, position.lineNumber, position.column); + } + + public validateViewPosition(viewPosition: Position, expectedModelPosition: Position): Position { + return this._lines.validateViewPosition(viewPosition.lineNumber, viewPosition.column, expectedModelPosition); + } + + public validateViewRange(viewRange: Range, expectedModelRange: Range): Range { + var validViewStart = this._lines.validateViewPosition(viewRange.startLineNumber, viewRange.startColumn, expectedModelRange.getStartPosition()); + var validViewEnd = this._lines.validateViewPosition(viewRange.endLineNumber, viewRange.endColumn, expectedModelRange.getEndPosition()); + return new Range(validViewStart.lineNumber, validViewStart.column, validViewEnd.lineNumber, validViewEnd.column); + } + + // Model -> View conversion and related methods + + public convertModelPositionToViewPosition(modelPosition: Position): Position { + return this._lines.convertModelPositionToViewPosition(modelPosition.lineNumber, modelPosition.column); + } + + public convertModelRangeToViewRange(modelRange: Range): Range { + let start = this._lines.convertModelPositionToViewPosition(modelRange.startLineNumber, modelRange.startColumn); + let end = this._lines.convertModelPositionToViewPosition(modelRange.endLineNumber, modelRange.endColumn); + return new Range(start.lineNumber, start.column, end.lineNumber, end.column); + } + + public convertModelSelectionToViewSelection(modelSelection: Selection): Selection { + let selectionStart = this._lines.convertModelPositionToViewPosition(modelSelection.selectionStartLineNumber, modelSelection.selectionStartColumn); + let position = this._lines.convertModelPositionToViewPosition(modelSelection.positionLineNumber, modelSelection.positionColumn); + return new Selection(selectionStart.lineNumber, selectionStart.column, position.lineNumber, position.column); + } + + public modelPositionIsVisible(modelPosition: Position): boolean { + return this._lines.modelPositionIsVisible(modelPosition.lineNumber, modelPosition.column); + } + +} + +export class SplitLinesCollection implements IViewModelLinesCollection { + + private model: editorCommon.IModel; + private _validModelVersionId: number; + + private wrappingColumn: number; + private columnsForFullWidthChar: number; + private wrappingIndent: WrappingIndent; + private tabSize: number; + private lines: ISplitLine[]; + + private prefixSumComputer: PrefixSumComputerWithCache; + + private linePositionMapperFactory: ILineMapperFactory; + + private hiddenAreasIds: string[]; + + constructor(model: editorCommon.IModel, linePositionMapperFactory: ILineMapperFactory, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent) { + this.model = model; + this._validModelVersionId = -1; + this.tabSize = tabSize; + this.wrappingColumn = wrappingColumn; + this.columnsForFullWidthChar = columnsForFullWidthChar; + this.wrappingIndent = wrappingIndent; + this.linePositionMapperFactory = linePositionMapperFactory; + + this._constructLines(true); + } + + public dispose(): void { + this.hiddenAreasIds = this.model.deltaDecorations(this.hiddenAreasIds, []); + } + + public createCoordinatesConverter(): ICoordinatesConverter { + return new CoordinatesConverter(this); + } + + private _ensureValidState(): void { + let modelVersion = this.model.getVersionId(); + if (modelVersion !== this._validModelVersionId) { + throw new Error('SplitLinesCollection: attempt to access a \'newer\' model'); + } + } + + private _constructLines(resetHiddenAreas: boolean): void { + this.lines = []; + + if (resetHiddenAreas) { + this.hiddenAreasIds = []; + } + + let linesContent = this.model.getLinesContent(); + let lineCount = linesContent.length; + let values = new Uint32Array(lineCount); + + let hiddenAreas = this.hiddenAreasIds.map((areaId) => this.model.getDecorationRange(areaId)).sort(Range.compareRangesUsingStarts); + let hiddenAreaStart = 1, hiddenAreaEnd = 0; + let hiddenAreaIdx = -1; + let nextLineNumberToUpdateHiddenArea = (hiddenAreaIdx + 1 < hiddenAreas.length) ? hiddenAreaEnd + 1 : lineCount + 2; + + for (let i = 0; i < lineCount; i++) { + let lineNumber = i + 1; + + if (lineNumber === nextLineNumberToUpdateHiddenArea) { + hiddenAreaIdx++; + hiddenAreaStart = hiddenAreas[hiddenAreaIdx].startLineNumber; + hiddenAreaEnd = hiddenAreas[hiddenAreaIdx].endLineNumber; + nextLineNumberToUpdateHiddenArea = (hiddenAreaIdx + 1 < hiddenAreas.length) ? hiddenAreaEnd + 1 : lineCount + 2; + } + + let isInHiddenArea = (lineNumber >= hiddenAreaStart && lineNumber <= hiddenAreaEnd); + let line = createSplitLine(this.linePositionMapperFactory, linesContent[i], this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, !isInHiddenArea); + values[i] = line.getViewLineCount(); + this.lines[i] = line; + } + + this._validModelVersionId = this.model.getVersionId(); + + this.prefixSumComputer = new PrefixSumComputerWithCache(values); + } + + private getHiddenAreas(): Range[] { + return this.hiddenAreasIds.map((decId) => { + return this.model.getDecorationRange(decId); + }).sort(Range.compareRangesUsingStarts); + } + + private _reduceRanges(_ranges: Range[]): Range[] { + if (_ranges.length === 0) { + return []; + } + let ranges = _ranges.map(r => this.model.validateRange(r)).sort(Range.compareRangesUsingStarts); + + let result: Range[] = []; + let currentRangeStart = ranges[0].startLineNumber; + let currentRangeEnd = ranges[0].endLineNumber; + + for (let i = 1, len = ranges.length; i < len; i++) { + let range = ranges[i]; + + if (range.startLineNumber > currentRangeEnd + 1) { + result.push(new Range(currentRangeStart, 1, currentRangeEnd, 1)); + currentRangeStart = range.startLineNumber; + currentRangeEnd = range.endLineNumber; + } else if (range.endLineNumber > currentRangeEnd) { + currentRangeEnd = range.endLineNumber; + } + } + result.push(new Range(currentRangeStart, 1, currentRangeEnd, 1)); + return result; + } + + public setHiddenAreas(_ranges: Range[]): boolean { + + let newRanges = this._reduceRanges(_ranges); + + // BEGIN TODO@Martin: Please stop calling this method on each model change! + let oldRanges = this.hiddenAreasIds.map((areaId) => this.model.getDecorationRange(areaId)).sort(Range.compareRangesUsingStarts); + + if (newRanges.length === oldRanges.length) { + let hasDifference = false; + for (let i = 0; i < newRanges.length; i++) { + if (!newRanges[i].equalsRange(oldRanges[i])) { + hasDifference = true; + break; + } + } + if (!hasDifference) { + return false; + } + } + // END TODO@Martin: Please stop calling this method on each model change! + + let newDecorations: editorCommon.IModelDeltaDecoration[] = []; + for (let i = 0; i < newRanges.length; i++) { + newDecorations.push({ + range: newRanges[i], + options: ModelDecorationOptions.EMPTY + }); + } + + this.hiddenAreasIds = this.model.deltaDecorations(this.hiddenAreasIds, newDecorations); + + let hiddenAreas = newRanges; + let hiddenAreaStart = 1, hiddenAreaEnd = 0; + let hiddenAreaIdx = -1; + let nextLineNumberToUpdateHiddenArea = (hiddenAreaIdx + 1 < hiddenAreas.length) ? hiddenAreaEnd + 1 : this.lines.length + 2; + + for (let i = 0; i < this.lines.length; i++) { + let lineNumber = i + 1; + + if (lineNumber === nextLineNumberToUpdateHiddenArea) { + hiddenAreaIdx++; + hiddenAreaStart = hiddenAreas[hiddenAreaIdx].startLineNumber; + hiddenAreaEnd = hiddenAreas[hiddenAreaIdx].endLineNumber; + nextLineNumberToUpdateHiddenArea = (hiddenAreaIdx + 1 < hiddenAreas.length) ? hiddenAreaEnd + 1 : this.lines.length + 2; + } + + let lineChanged = false; + if (lineNumber >= hiddenAreaStart && lineNumber <= hiddenAreaEnd) { + // Line should be hidden + if (this.lines[i].isVisible()) { + this.lines[i] = this.lines[i].setVisible(false); + lineChanged = true; + } + } else { + // Line should be visible + if (!this.lines[i].isVisible()) { + this.lines[i] = this.lines[i].setVisible(true); + lineChanged = true; + } + } + if (lineChanged) { + let newOutputLineCount = this.lines[i].getViewLineCount(); + this.prefixSumComputer.changeValue(i, newOutputLineCount); + } + } + + return true; + } + + public modelPositionIsVisible(modelLineNumber: number, modelColumn: number): boolean { + if (modelLineNumber < 1 || modelLineNumber > this.lines.length) { + // invalid arguments + return false; + } + return this.lines[modelLineNumber - 1].isVisible(); + } + + public setTabSize(newTabSize: number): boolean { + if (this.tabSize === newTabSize) { + return false; + } + this.tabSize = newTabSize; + + this._constructLines(false); + + return true; + } + + public setWrappingSettings(wrappingIndent: WrappingIndent, wrappingColumn: number, columnsForFullWidthChar: number): boolean { + if (this.wrappingIndent === wrappingIndent && this.wrappingColumn === wrappingColumn && this.columnsForFullWidthChar === columnsForFullWidthChar) { + return false; + } + + this.wrappingIndent = wrappingIndent; + this.wrappingColumn = wrappingColumn; + this.columnsForFullWidthChar = columnsForFullWidthChar; + + this._constructLines(false); + + return true; + } + + public onModelFlushed(): void { + this._constructLines(true); + } + + public onModelLinesDeleted(versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent { + if (versionId <= this._validModelVersionId) { + // Here we check for versionId in case the lines were reconstructed in the meantime. + // We don't want to apply stale change events on top of a newer read model state. + return null; + } + + let outputFromLineNumber = (fromLineNumber === 1 ? 1 : this.prefixSumComputer.getAccumulatedValue(fromLineNumber - 2) + 1); + let outputToLineNumber = this.prefixSumComputer.getAccumulatedValue(toLineNumber - 1); + + this.lines.splice(fromLineNumber - 1, toLineNumber - fromLineNumber + 1); + this.prefixSumComputer.removeValues(fromLineNumber - 1, toLineNumber - fromLineNumber + 1); + + return new viewEvents.ViewLinesDeletedEvent(outputFromLineNumber, outputToLineNumber); + } + + public onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, text: string[]): viewEvents.ViewLinesInsertedEvent { + if (versionId <= this._validModelVersionId) { + // Here we check for versionId in case the lines were reconstructed in the meantime. + // We don't want to apply stale change events on top of a newer read model state. + return null; + } + + let hiddenAreas = this.getHiddenAreas(); + let isInHiddenArea = false; + let testPosition = new Position(fromLineNumber, 1); + for (let i = 0; i < hiddenAreas.length; i++) { + if (hiddenAreas[i].containsPosition(testPosition)) { + isInHiddenArea = true; + break; + } + } + + let outputFromLineNumber = (fromLineNumber === 1 ? 1 : this.prefixSumComputer.getAccumulatedValue(fromLineNumber - 2) + 1); + + let totalOutputLineCount = 0; + let insertLines: ISplitLine[] = []; + let insertPrefixSumValues = new Uint32Array(text.length); + + for (let i = 0, len = text.length; i < len; i++) { + let line = createSplitLine(this.linePositionMapperFactory, text[i], this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, !isInHiddenArea); + insertLines.push(line); + + let outputLineCount = line.getViewLineCount(); + totalOutputLineCount += outputLineCount; + insertPrefixSumValues[i] = outputLineCount; + } + + this.lines = this.lines.slice(0, fromLineNumber - 1).concat(insertLines).concat(this.lines.slice(fromLineNumber - 1)); + + this.prefixSumComputer.insertValues(fromLineNumber - 1, insertPrefixSumValues); + + return new viewEvents.ViewLinesInsertedEvent(outputFromLineNumber, outputFromLineNumber + totalOutputLineCount - 1); + } + + public onModelLineChanged(versionId: number, lineNumber: number, newText: string): [boolean, viewEvents.ViewLinesChangedEvent, viewEvents.ViewLinesInsertedEvent, viewEvents.ViewLinesDeletedEvent] { + if (versionId <= this._validModelVersionId) { + // Here we check for versionId in case the lines were reconstructed in the meantime. + // We don't want to apply stale change events on top of a newer read model state. + return [false, null, null, null]; + } + + let lineIndex = lineNumber - 1; + + let oldOutputLineCount = this.lines[lineIndex].getViewLineCount(); + let isVisible = this.lines[lineIndex].isVisible(); + let line = createSplitLine(this.linePositionMapperFactory, newText, this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, isVisible); + this.lines[lineIndex] = line; + let newOutputLineCount = this.lines[lineIndex].getViewLineCount(); + + let lineMappingChanged = false; + let changeFrom = 0; + let changeTo = -1; + let insertFrom = 0; + let insertTo = -1; + let deleteFrom = 0; + let deleteTo = -1; + + if (oldOutputLineCount > newOutputLineCount) { + changeFrom = (lineNumber === 1 ? 1 : this.prefixSumComputer.getAccumulatedValue(lineNumber - 2) + 1); + changeTo = changeFrom + newOutputLineCount - 1; + deleteFrom = changeTo + 1; + deleteTo = deleteFrom + (oldOutputLineCount - newOutputLineCount) - 1; + lineMappingChanged = true; + } else if (oldOutputLineCount < newOutputLineCount) { + changeFrom = (lineNumber === 1 ? 1 : this.prefixSumComputer.getAccumulatedValue(lineNumber - 2) + 1); + changeTo = changeFrom + oldOutputLineCount - 1; + insertFrom = changeTo + 1; + insertTo = insertFrom + (newOutputLineCount - oldOutputLineCount) - 1; + lineMappingChanged = true; + } else { + changeFrom = (lineNumber === 1 ? 1 : this.prefixSumComputer.getAccumulatedValue(lineNumber - 2) + 1); + changeTo = changeFrom + newOutputLineCount - 1; + } + + this.prefixSumComputer.changeValue(lineIndex, newOutputLineCount); + + const viewLinesChangedEvent = (changeFrom <= changeTo ? new viewEvents.ViewLinesChangedEvent(changeFrom, changeTo) : null); + const viewLinesInsertedEvent = (insertFrom <= insertTo ? new viewEvents.ViewLinesInsertedEvent(insertFrom, insertTo) : null); + const viewLinesDeletedEvent = (deleteFrom <= deleteTo ? new viewEvents.ViewLinesDeletedEvent(deleteFrom, deleteTo) : null); + + return [lineMappingChanged, viewLinesChangedEvent, viewLinesInsertedEvent, viewLinesDeletedEvent]; + } + + public acceptVersionId(versionId: number): void { + this._validModelVersionId = versionId; + } + + public getViewLineCount(): number { + this._ensureValidState(); + return this.prefixSumComputer.getTotalValue(); + } + + private _toValidViewLineNumber(viewLineNumber: number): number { + if (viewLineNumber < 1) { + return 1; + } + let viewLineCount = this.getViewLineCount(); + if (viewLineNumber > viewLineCount) { + return viewLineCount; + } + return viewLineNumber; + } + + /** + * Gives a hint that a lot of requests are about to come in for these line numbers. + */ + public warmUpLookupCache(viewStartLineNumber: number, viewEndLineNumber: number): void { + this.prefixSumComputer.warmUpCache(viewStartLineNumber - 1, viewEndLineNumber - 1); + } + + public getViewLineIndentGuide(viewLineNumber: number): number { + this._ensureValidState(); + viewLineNumber = this._toValidViewLineNumber(viewLineNumber); + let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); + return this.model.getLineIndentGuide(r.index + 1); + } + + public getViewLineContent(viewLineNumber: number): string { + this._ensureValidState(); + viewLineNumber = this._toValidViewLineNumber(viewLineNumber); + let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); + let lineIndex = r.index; + let remainder = r.remainder; + + return this.lines[lineIndex].getViewLineContent(this.model, lineIndex + 1, remainder); + } + + public getViewLineMinColumn(viewLineNumber: number): number { + this._ensureValidState(); + viewLineNumber = this._toValidViewLineNumber(viewLineNumber); + let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); + let lineIndex = r.index; + let remainder = r.remainder; + + return this.lines[lineIndex].getViewLineMinColumn(this.model, lineIndex + 1, remainder); + } + + public getViewLineMaxColumn(viewLineNumber: number): number { + this._ensureValidState(); + viewLineNumber = this._toValidViewLineNumber(viewLineNumber); + let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); + let lineIndex = r.index; + let remainder = r.remainder; + + return this.lines[lineIndex].getViewLineMaxColumn(this.model, lineIndex + 1, remainder); + } + + public getViewLineData(viewLineNumber: number): ViewLineData { + this._ensureValidState(); + viewLineNumber = this._toValidViewLineNumber(viewLineNumber); + let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); + let lineIndex = r.index; + let remainder = r.remainder; + + return this.lines[lineIndex].getViewLineData(this.model, lineIndex + 1, remainder); + } + + public getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): ViewLineData[] { + this._ensureValidState(); + + viewStartLineNumber = this._toValidViewLineNumber(viewStartLineNumber); + viewEndLineNumber = this._toValidViewLineNumber(viewEndLineNumber); + + let start = this.prefixSumComputer.getIndexOf(viewStartLineNumber - 1); + let viewLineNumber = viewStartLineNumber; + let startModelLineIndex = start.index; + let startRemainder = start.remainder; + + let result: ViewLineData[] = []; + for (let modelLineIndex = startModelLineIndex, len = this.model.getLineCount(); modelLineIndex < len; modelLineIndex++) { + let line = this.lines[modelLineIndex]; + if (!line.isVisible()) { + continue; + } + let fromViewLineIndex = (modelLineIndex === startModelLineIndex ? startRemainder : 0); + let remainingViewLineCount = line.getViewLineCount() - fromViewLineIndex; + + let lastLine = false; + if (viewLineNumber + remainingViewLineCount > viewEndLineNumber) { + lastLine = true; + remainingViewLineCount = viewEndLineNumber - viewLineNumber + 1; + } + let toViewLineIndex = fromViewLineIndex + remainingViewLineCount; + + line.getViewLinesData(this.model, modelLineIndex + 1, fromViewLineIndex, toViewLineIndex, viewLineNumber - viewStartLineNumber, needed, result); + + viewLineNumber += remainingViewLineCount; + + if (lastLine) { + break; + } + } + + return result; + } + + public validateViewPosition(viewLineNumber: number, viewColumn: number, expectedModelPosition: Position): Position { + this._ensureValidState(); + viewLineNumber = this._toValidViewLineNumber(viewLineNumber); + + let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); + let lineIndex = r.index; + let remainder = r.remainder; + + let line = this.lines[lineIndex]; + + let minColumn = line.getViewLineMinColumn(this.model, lineIndex + 1, remainder); + let maxColumn = line.getViewLineMaxColumn(this.model, lineIndex + 1, remainder); + if (viewColumn < minColumn) { + viewColumn = minColumn; + } + if (viewColumn > maxColumn) { + viewColumn = maxColumn; + } + + let computedModelColumn = line.getModelColumnOfViewPosition(remainder, viewColumn); + let computedModelPosition = this.model.validatePosition(new Position(lineIndex + 1, computedModelColumn)); + + if (computedModelPosition.equals(expectedModelPosition)) { + return new Position(viewLineNumber, viewColumn); + } + + return this.convertModelPositionToViewPosition(expectedModelPosition.lineNumber, expectedModelPosition.column); + } + + public convertViewPositionToModelPosition(viewLineNumber: number, viewColumn: number): Position { + this._ensureValidState(); + viewLineNumber = this._toValidViewLineNumber(viewLineNumber); + + let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); + let lineIndex = r.index; + let remainder = r.remainder; + + let inputColumn = this.lines[lineIndex].getModelColumnOfViewPosition(remainder, viewColumn); + // console.log('out -> in ' + viewLineNumber + ',' + viewColumn + ' ===> ' + (lineIndex+1) + ',' + inputColumn); + return this.model.validatePosition(new Position(lineIndex + 1, inputColumn)); + } + + public convertModelPositionToViewPosition(_modelLineNumber: number, _modelColumn: number): Position { + this._ensureValidState(); + + let validPosition = this.model.validatePosition(new Position(_modelLineNumber, _modelColumn)); + let inputLineNumber = validPosition.lineNumber; + let inputColumn = validPosition.column; + + let lineIndex = inputLineNumber - 1, lineIndexChanged = false; + while (lineIndex > 0 && !this.lines[lineIndex].isVisible()) { + lineIndex--; + lineIndexChanged = true; + } + if (lineIndex === 0 && !this.lines[lineIndex].isVisible()) { + // Could not reach a real line + // console.log('in -> out ' + inputLineNumber + ',' + inputColumn + ' ===> ' + 1 + ',' + 1); + return new Position(1, 1); + } + let deltaLineNumber = 1 + (lineIndex === 0 ? 0 : this.prefixSumComputer.getAccumulatedValue(lineIndex - 1)); + + let r: Position; + if (lineIndexChanged) { + r = this.lines[lineIndex].getViewPositionOfModelPosition(deltaLineNumber, this.model.getLineMaxColumn(lineIndex + 1)); + } else { + r = this.lines[inputLineNumber - 1].getViewPositionOfModelPosition(deltaLineNumber, inputColumn); + } + + // console.log('in -> out ' + inputLineNumber + ',' + inputColumn + ' ===> ' + r.lineNumber + ',' + r); + return r; + } +} + +class VisibleIdentitySplitLine implements ISplitLine { + + public static INSTANCE = new VisibleIdentitySplitLine(); + + private constructor() { } + + public isVisible(): boolean { + return true; + } + + public setVisible(isVisible: boolean): ISplitLine { + if (isVisible) { + return this; + } + return InvisibleIdentitySplitLine.INSTANCE; + } + + public getViewLineCount(): number { + return 1; + } + + public getViewLineContent(model: IModel, modelLineNumber: number, outputLineIndex: number): string { + return model.getLineContent(modelLineNumber); + } + + public getViewLineMinColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number { + return model.getLineMinColumn(modelLineNumber); + } + + public getViewLineMaxColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number { + return model.getLineMaxColumn(modelLineNumber); + } + + public getViewLineData(model: IModel, modelLineNumber: number, outputLineIndex: number): ViewLineData { + let lineTokens = model.getLineTokens(modelLineNumber); + let lineContent = lineTokens.getLineContent(); + return new ViewLineData( + lineContent, + 1, + lineContent.length + 1, + lineTokens.inflate() + ); + } + + public getViewLinesData(model: IModel, modelLineNumber: number, fromOuputLineIndex: number, toOutputLineIndex: number, globalStartIndex: number, needed: boolean[], result: ViewLineData[]): void { + if (!needed[globalStartIndex]) { + result[globalStartIndex] = null; + return; + } + result[globalStartIndex] = this.getViewLineData(model, modelLineNumber, 0); + } + + public getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number { + return outputColumn; + } + + public getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position { + return new Position(deltaLineNumber, inputColumn); + } +} + +class InvisibleIdentitySplitLine implements ISplitLine { + + public static INSTANCE = new InvisibleIdentitySplitLine(); + + private constructor() { } + + public isVisible(): boolean { + return false; + } + + public setVisible(isVisible: boolean): ISplitLine { + if (!isVisible) { + return this; + } + return VisibleIdentitySplitLine.INSTANCE; + } + + public getViewLineCount(): number { + return 0; + } + + public getViewLineContent(model: IModel, modelLineNumber: number, outputLineIndex: number): string { + throw new Error('Not supported'); + } + + public getViewLineMinColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number { + throw new Error('Not supported'); + } + + public getViewLineMaxColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number { + throw new Error('Not supported'); + } + + public getViewLineData(model: IModel, modelLineNumber: number, outputLineIndex: number): ViewLineData { + throw new Error('Not supported'); + } + + public getViewLinesData(model: IModel, modelLineNumber: number, fromOuputLineIndex: number, toOutputLineIndex: number, globalStartIndex: number, needed: boolean[], result: ViewLineData[]): void { + throw new Error('Not supported'); + } + + public getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number { + throw new Error('Not supported'); + } + + public getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position { + throw new Error('Not supported'); + } +} + +export class SplitLine implements ISplitLine { + + private positionMapper: ILineMapping; + private outputLineCount: number; + + private wrappedIndent: string; + private wrappedIndentLength: number; + private _isVisible: boolean; + + constructor(positionMapper: ILineMapping, isVisible: boolean) { + this.positionMapper = positionMapper; + this.wrappedIndent = this.positionMapper.getWrappedLinesIndent(); + this.wrappedIndentLength = this.wrappedIndent.length; + this.outputLineCount = this.positionMapper.getOutputLineCount(); + this._isVisible = isVisible; + } + + public isVisible(): boolean { + return this._isVisible; + } + + public setVisible(isVisible: boolean): ISplitLine { + this._isVisible = isVisible; + return this; + } + + public getViewLineCount(): number { + if (!this._isVisible) { + return 0; + } + return this.outputLineCount; + } + + private getInputStartOffsetOfOutputLineIndex(outputLineIndex: number): number { + return this.positionMapper.getInputOffsetOfOutputPosition(outputLineIndex, 0); + } + + private getInputEndOffsetOfOutputLineIndex(model: IModel, modelLineNumber: number, outputLineIndex: number): number { + if (outputLineIndex + 1 === this.outputLineCount) { + return model.getLineMaxColumn(modelLineNumber) - 1; + } + return this.positionMapper.getInputOffsetOfOutputPosition(outputLineIndex + 1, 0); + } + + public getViewLineContent(model: IModel, modelLineNumber: number, outputLineIndex: number): string { + if (!this._isVisible) { + throw new Error('Not supported'); + } + let startOffset = this.getInputStartOffsetOfOutputLineIndex(outputLineIndex); + let endOffset = this.getInputEndOffsetOfOutputLineIndex(model, modelLineNumber, outputLineIndex); + let r = model.getLineContent(modelLineNumber).substring(startOffset, endOffset); + + if (outputLineIndex > 0) { + r = this.wrappedIndent + r; + } + + return r; + } + + public getViewLineMinColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number { + if (!this._isVisible) { + throw new Error('Not supported'); + } + if (outputLineIndex > 0) { + return this.wrappedIndentLength + 1; + } + return 1; + } + + public getViewLineMaxColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number { + if (!this._isVisible) { + throw new Error('Not supported'); + } + return this.getViewLineContent(model, modelLineNumber, outputLineIndex).length + 1; + } + + public getViewLineData(model: IModel, modelLineNumber: number, outputLineIndex: number): ViewLineData { + if (!this._isVisible) { + throw new Error('Not supported'); + } + + let startOffset = this.getInputStartOffsetOfOutputLineIndex(outputLineIndex); + let endOffset = this.getInputEndOffsetOfOutputLineIndex(model, modelLineNumber, outputLineIndex); + + let lineContent = model.getLineContent(modelLineNumber).substring(startOffset, endOffset); + if (outputLineIndex > 0) { + lineContent = this.wrappedIndent + lineContent; + } + + let minColumn = (outputLineIndex > 0 ? this.wrappedIndentLength + 1 : 1); + let maxColumn = lineContent.length + 1; + + let deltaStartIndex = 0; + if (outputLineIndex > 0) { + deltaStartIndex = this.wrappedIndentLength; + } + let lineTokens = model.getLineTokens(modelLineNumber); + + return new ViewLineData( + lineContent, + minColumn, + maxColumn, + lineTokens.sliceAndInflate(startOffset, endOffset, deltaStartIndex) + ); + } + + public getViewLinesData(model: IModel, modelLineNumber: number, fromOuputLineIndex: number, toOutputLineIndex: number, globalStartIndex: number, needed: boolean[], result: ViewLineData[]): void { + if (!this._isVisible) { + throw new Error('Not supported'); + } + + for (let outputLineIndex = fromOuputLineIndex; outputLineIndex < toOutputLineIndex; outputLineIndex++) { + let globalIndex = globalStartIndex + outputLineIndex - fromOuputLineIndex; + if (!needed[globalIndex]) { + result[globalIndex] = null; + continue; + } + result[globalIndex] = this.getViewLineData(model, modelLineNumber, outputLineIndex); + } + } + + public getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number { + if (!this._isVisible) { + throw new Error('Not supported'); + } + let adjustedColumn = outputColumn - 1; + if (outputLineIndex > 0) { + if (adjustedColumn < this.wrappedIndentLength) { + adjustedColumn = 0; + } else { + adjustedColumn -= this.wrappedIndentLength; + } + } + return this.positionMapper.getInputOffsetOfOutputPosition(outputLineIndex, adjustedColumn) + 1; + } + + public getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position { + if (!this._isVisible) { + throw new Error('Not supported'); + } + let r = this.positionMapper.getOutputPositionOfInputOffset(inputColumn - 1); + let outputLineIndex = r.outputLineIndex; + let outputColumn = r.outputOffset + 1; + + if (outputLineIndex > 0) { + outputColumn += this.wrappedIndentLength; + } + + // console.log('in -> out ' + deltaLineNumber + ',' + inputColumn + ' ===> ' + (deltaLineNumber+outputLineIndex) + ',' + outputColumn); + return new Position(deltaLineNumber + outputLineIndex, outputColumn); + } +} + +function createSplitLine(linePositionMapperFactory: ILineMapperFactory, text: string, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, isVisible: boolean): ISplitLine { + let positionMapper = linePositionMapperFactory.createLineMapping(text, tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + if (positionMapper === null) { + // No mapping needed + if (isVisible) { + return VisibleIdentitySplitLine.INSTANCE; + } + return InvisibleIdentitySplitLine.INSTANCE; + } else { + return new SplitLine(positionMapper, isVisible); + } +} + +export class IdentityCoordinatesConverter implements ICoordinatesConverter { + + private readonly _lines: IdentityLinesCollection; + + constructor(lines: IdentityLinesCollection) { + this._lines = lines; + } + + private _validPosition(pos: Position): Position { + return this._lines.model.validatePosition(pos); + } + + private _validRange(range: Range): Range { + return this._lines.model.validateRange(range); + } + + private _validSelection(selection: Selection): Selection { + let selectionStart = this._validPosition(new Position(selection.selectionStartLineNumber, selection.selectionStartColumn)); + let position = this._validPosition(new Position(selection.positionLineNumber, selection.positionColumn)); + return new Selection(selectionStart.lineNumber, selectionStart.column, position.lineNumber, position.column); + } + + // View -> Model conversion and related methods + + public convertViewPositionToModelPosition(viewPosition: Position): Position { + return this._validPosition(viewPosition); + } + + public convertViewRangeToModelRange(viewRange: Range): Range { + return this._validRange(viewRange); + } + + public convertViewSelectionToModelSelection(viewSelection: Selection): Selection { + return this._validSelection(viewSelection); + } + + public validateViewPosition(viewPosition: Position, expectedModelPosition: Position): Position { + return this._validPosition(expectedModelPosition); + } + + public validateViewRange(viewRange: Range, expectedModelRange: Range): Range { + return this._validRange(expectedModelRange); + } + + // Model -> View conversion and related methods + + public convertModelPositionToViewPosition(modelPosition: Position): Position { + return this._validPosition(modelPosition); + } + + public convertModelRangeToViewRange(modelRange: Range): Range { + return this._validRange(modelRange); + } + + public convertModelSelectionToViewSelection(modelSelection: Selection): Selection { + return this._validSelection(modelSelection); + } + + public modelPositionIsVisible(modelPosition: Position): boolean { + const lineCount = this._lines.model.getLineCount(); + if (modelPosition.lineNumber < 1 || modelPosition.lineNumber > lineCount) { + // invalid arguments + return false; + } + return true; + } + +} + +export class IdentityLinesCollection implements IViewModelLinesCollection { + + public readonly model: editorCommon.IModel; + + constructor(model: editorCommon.IModel) { + this.model = model; + } + + public dispose(): void { + } + + public createCoordinatesConverter(): ICoordinatesConverter { + return new IdentityCoordinatesConverter(this); + } + + public setHiddenAreas(_ranges: Range[]): boolean { + return false; + } + + public setTabSize(newTabSize: number): boolean { + return false; + } + + public setWrappingSettings(wrappingIndent: WrappingIndent, wrappingColumn: number, columnsForFullWidthChar: number): boolean { + return false; + } + + public onModelFlushed(): void { + } + + public onModelLinesDeleted(versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent { + return new viewEvents.ViewLinesDeletedEvent(fromLineNumber, toLineNumber); + } + + public onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, text: string[]): viewEvents.ViewLinesInsertedEvent { + return new viewEvents.ViewLinesInsertedEvent(fromLineNumber, toLineNumber); + } + + public onModelLineChanged(versionId: number, lineNumber: number, newText: string): [boolean, viewEvents.ViewLinesChangedEvent, viewEvents.ViewLinesInsertedEvent, viewEvents.ViewLinesDeletedEvent] { + return [false, new viewEvents.ViewLinesChangedEvent(lineNumber, lineNumber), null, null]; + } + + public acceptVersionId(versionId: number): void { + } + + public getViewLineCount(): number { + return this.model.getLineCount(); + } + + public warmUpLookupCache(viewStartLineNumber: number, viewEndLineNumber: number): void { + } + + public getViewLineIndentGuide(viewLineNumber: number): number { + return 0; + } + + public getViewLineContent(viewLineNumber: number): string { + return this.model.getLineContent(viewLineNumber); + } + + public getViewLineMinColumn(viewLineNumber: number): number { + return this.model.getLineMinColumn(viewLineNumber); + } + + public getViewLineMaxColumn(viewLineNumber: number): number { + return this.model.getLineMaxColumn(viewLineNumber); + } + + public getViewLineData(viewLineNumber: number): ViewLineData { + let lineTokens = this.model.getLineTokens(viewLineNumber); + let lineContent = lineTokens.getLineContent(); + return new ViewLineData( + lineContent, + 1, + lineContent.length + 1, + lineTokens.inflate() + ); + } + + public getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): ViewLineData[] { + const lineCount = this.model.getLineCount(); + viewStartLineNumber = Math.min(Math.max(1, viewStartLineNumber), lineCount); + viewEndLineNumber = Math.min(Math.max(1, viewEndLineNumber), lineCount); + + let result: ViewLineData[] = []; + for (let lineNumber = viewStartLineNumber; lineNumber <= viewEndLineNumber; lineNumber++) { + let idx = lineNumber - viewStartLineNumber; + if (!needed[idx]) { + result[idx] = null; + } + result[idx] = this.getViewLineData(lineNumber); + } + + return result; + } +} diff --git a/src/vs/editor/common/viewModel/viewEventHandler.ts b/src/vs/editor/common/viewModel/viewEventHandler.ts new file mode 100644 index 0000000000..975bdbf7f8 --- /dev/null +++ b/src/vs/editor/common/viewModel/viewEventHandler.ts @@ -0,0 +1,195 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { Disposable } from 'vs/base/common/lifecycle'; + +export class ViewEventHandler extends Disposable { + + private _shouldRender: boolean; + + constructor() { + super(); + this._shouldRender = true; + } + + public shouldRender(): boolean { + return this._shouldRender; + } + + public forceShouldRender(): void { + this._shouldRender = true; + } + + protected setShouldRender(): void { + this._shouldRender = true; + } + + public onDidRender(): void { + this._shouldRender = false; + } + + // --- begin event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + return false; + } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + return false; + } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + return false; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return false; + } + public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { + return false; + } + public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean { + return false; + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return false; + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return false; + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return false; + } + public onRevealRangeRequest(e: viewEvents.ViewRevealRangeRequestEvent): boolean { + return false; + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return false; + } + public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + return false; + } + public onTokensColorsChanged(e: viewEvents.ViewTokensColorsChangedEvent): boolean { + return false; + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return false; + } + public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { + return false; + } + + // --- end event handlers + + public handleEvents(events: viewEvents.ViewEvent[]): void { + + let shouldRender = false; + + for (let i = 0, len = events.length; i < len; i++) { + let e = events[i]; + + switch (e.type) { + + case viewEvents.ViewEventType.ViewConfigurationChanged: + if (this.onConfigurationChanged(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewCursorStateChanged: + if (this.onCursorStateChanged(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewDecorationsChanged: + if (this.onDecorationsChanged(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewFlushed: + if (this.onFlushed(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewFocusChanged: + if (this.onFocusChanged(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewLineMappingChanged: + if (this.onLineMappingChanged(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewLinesChanged: + if (this.onLinesChanged(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewLinesDeleted: + if (this.onLinesDeleted(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewLinesInserted: + if (this.onLinesInserted(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewRevealRangeRequest: + if (this.onRevealRangeRequest(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewScrollChanged: + if (this.onScrollChanged(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewTokensChanged: + if (this.onTokensChanged(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewTokensColorsChanged: + if (this.onTokensColorsChanged(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewZonesChanged: + if (this.onZonesChanged(e)) { + shouldRender = true; + } + break; + + + case viewEvents.ViewEventType.ViewThemeChanged: + if (this.onThemeChanged(e)) { + shouldRender = true; + } + break; + + default: + console.info('View received unknown event: '); + console.info(e); + } + } + + if (shouldRender) { + this._shouldRender = true; + } + } +} diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts new file mode 100644 index 0000000000..1c2ddd7797 --- /dev/null +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -0,0 +1,299 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { INewScrollPosition, IModelDecoration, EndOfLinePreference, IViewState } from 'vs/editor/common/editorCommon'; +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ViewEvent, IViewEventListener } from 'vs/editor/common/view/viewEvents'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Scrollable, IScrollPosition } from 'vs/base/common/scrollable'; +import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; + +export interface IViewWhitespaceViewportData { + readonly id: number; + readonly afterLineNumber: number; + readonly verticalOffset: number; + readonly height: number; +} + +export class Viewport { + readonly _viewportBrand: void; + + readonly top: number; + readonly left: number; + readonly width: number; + readonly height: number; + + constructor(top: number, left: number, width: number, height: number) { + this.top = top | 0; + this.left = left | 0; + this.width = width | 0; + this.height = height | 0; + } +} + +export interface IViewLayout { + + readonly scrollable: Scrollable; + + onMaxLineWidthChanged(width: number): void; + + getScrollWidth(): number; + getScrollHeight(): number; + + getCurrentScrollLeft(): number; + getCurrentScrollTop(): number; + getCurrentViewport(): Viewport; + + getFutureViewport(): Viewport; + + validateScrollPosition(scrollPosition: INewScrollPosition): IScrollPosition; + setScrollPositionNow(position: INewScrollPosition): void; + setScrollPositionSmooth(position: INewScrollPosition): void; + deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void; + + getLinesViewportData(): IPartialViewLinesViewportData; + getLinesViewportDataAtScrollTop(scrollTop: number): IPartialViewLinesViewportData; + getWhitespaces(): IEditorWhitespace[]; + + saveState(): IViewState; + restoreState(state: IViewState): void; + + isAfterLines(verticalOffset: number): boolean; + getLineNumberAtVerticalOffset(verticalOffset: number): number; + getVerticalOffsetForLineNumber(lineNumber: number): number; + getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData; + + // --------------- Begin vertical whitespace management + + /** + * Reserve rendering space. + * @return an identifier that can be later used to remove or change the whitespace. + */ + addWhitespace(afterLineNumber: number, ordinal: number, height: number): number; + /** + * Change the properties of a whitespace. + */ + changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean; + /** + * Remove rendering space + */ + removeWhitespace(id: number): boolean; + /** + * Get the layout information for whitespaces currently in the viewport + */ + getWhitespaceViewportData(): IViewWhitespaceViewportData[]; + + // TODO@Alex whitespace management should work via a change accessor sort of thing + onHeightMaybeChanged(): void; + + // --------------- End vertical whitespace management +} + +export interface ICoordinatesConverter { + // View -> Model conversion and related methods + convertViewPositionToModelPosition(viewPosition: Position): Position; + convertViewRangeToModelRange(viewRange: Range): Range; + convertViewSelectionToModelSelection(viewSelection: Selection): Selection; + validateViewPosition(viewPosition: Position, expectedModelPosition: Position): Position; + validateViewRange(viewRange: Range, expectedModelRange: Range): Range; + + // Model -> View conversion and related methods + convertModelPositionToViewPosition(modelPosition: Position): Position; + convertModelRangeToViewRange(modelRange: Range): Range; + convertModelSelectionToViewSelection(modelSelection: Selection): Selection; + modelPositionIsVisible(modelPosition: Position): boolean; +} + +export interface IViewModel { + + addEventListener(listener: IViewEventListener): IDisposable; + + readonly coordinatesConverter: ICoordinatesConverter; + + readonly viewLayout: IViewLayout; + + /** + * Gives a hint that a lot of requests are about to come in for these line numbers. + */ + setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void; + + getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[]; + getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData; + getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData; + getCompletelyVisibleViewRange(): Range; + getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range; + + getTabSize(): number; + getLineCount(): number; + getLineContent(lineNumber: number): string; + getLineIndentGuide(lineNumber: number): number; + getLineMinColumn(lineNumber: number): number; + getLineMaxColumn(lineNumber: number): number; + getLineFirstNonWhitespaceColumn(lineNumber: number): number; + getLineLastNonWhitespaceColumn(lineNumber: number): number; + getAllOverviewRulerDecorations(): ViewModelDecoration[]; + getValueInRange(range: Range, eol: EndOfLinePreference): string; + + getModelLineMaxColumn(modelLineNumber: number): number; + validateModelPosition(modelPosition: IPosition): Position; + + deduceModelPositionRelativeToViewPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position; + getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean): string; + getHTMLToCopy(ranges: Range[], emptySelectionClipboard: boolean): string; +} + +export class MinimapLinesRenderingData { + public readonly tabSize: number; + public readonly data: ViewLineData[]; + + constructor( + tabSize: number, + data: ViewLineData[] + ) { + this.tabSize = tabSize; + this.data = data; + } +} + +export class ViewLineData { + _viewLineDataBrand: void; + + /** + * The content at this view line. + */ + public readonly content: string; + /** + * The minimum allowed column at this view line. + */ + public readonly minColumn: number; + /** + * The maximum allowed column at this view line. + */ + public readonly maxColumn: number; + /** + * The tokens at this view line. + */ + public readonly tokens: ViewLineToken[]; + + constructor( + content: string, + minColumn: number, + maxColumn: number, + tokens: ViewLineToken[] + ) { + this.content = content; + this.minColumn = minColumn; + this.maxColumn = maxColumn; + this.tokens = tokens; + } +} + +export class ViewLineRenderingData { + /** + * The minimum allowed column at this view line. + */ + public readonly minColumn: number; + /** + * The maximum allowed column at this view line. + */ + public readonly maxColumn: number; + /** + * The content at this view line. + */ + public readonly content: string; + /** + * If set to false, it is guaranteed that `content` contains only LTR chars. + */ + public readonly mightContainRTL: boolean; + /** + * If set to false, it is guaranteed that `content` contains only basic ASCII chars. + */ + public readonly mightContainNonBasicASCII: boolean; + /** + * The tokens at this view line. + */ + public readonly tokens: ViewLineToken[]; + /** + * Inline decorations at this view line. + */ + public readonly inlineDecorations: InlineDecoration[]; + /** + * The tab size for this view model. + */ + public readonly tabSize: number; + + constructor( + minColumn: number, + maxColumn: number, + content: string, + mightContainRTL: boolean, + mightContainNonBasicASCII: boolean, + tokens: ViewLineToken[], + inlineDecorations: InlineDecoration[], + tabSize: number + ) { + this.minColumn = minColumn; + this.maxColumn = maxColumn; + this.content = content; + this.mightContainRTL = mightContainRTL; + this.mightContainNonBasicASCII = mightContainNonBasicASCII; + this.tokens = tokens; + this.inlineDecorations = inlineDecorations; + this.tabSize = tabSize; + } +} + +export class InlineDecoration { + _inlineDecorationBrand: void; + + readonly range: Range; + readonly inlineClassName: string; + readonly insertsBeforeOrAfter: boolean; + + constructor(range: Range, inlineClassName: string, insertsBeforeOrAfter: boolean) { + this.range = range; + this.inlineClassName = inlineClassName; + this.insertsBeforeOrAfter = insertsBeforeOrAfter; + } +} + +export class ViewModelDecoration { + _viewModelDecorationBrand: void; + + public range: Range; + public readonly source: IModelDecoration; + + constructor(source: IModelDecoration) { + this.range = null; + this.source = source; + } +} + +export class ViewEventsCollector { + + private _events: ViewEvent[]; + private _eventsLen = 0; + + constructor() { + this._events = []; + this._eventsLen = 0; + } + + public emit(event: ViewEvent) { + this._events[this._eventsLen++] = event; + } + + public finalize(): ViewEvent[] { + let result = this._events; + this._events = null; + return result; + } + +} diff --git a/src/vs/editor/common/viewModel/viewModelDecorations.ts b/src/vs/editor/common/viewModel/viewModelDecorations.ts new file mode 100644 index 0000000000..824640c6af --- /dev/null +++ b/src/vs/editor/common/viewModel/viewModelDecorations.ts @@ -0,0 +1,195 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { InlineDecoration, ViewModelDecoration, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; +import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; + +export interface IDecorationsViewportData { + /** + * decorations in the viewport. + */ + readonly decorations: ViewModelDecoration[]; + /** + * inline decorations grouped by each line in the viewport. + */ + readonly inlineDecorations: InlineDecoration[][]; +} + +export class ViewModelDecorations implements IDisposable { + + private readonly editorId: number; + private readonly model: editorCommon.IModel; + private readonly configuration: editorCommon.IConfiguration; + private readonly _coordinatesConverter: ICoordinatesConverter; + + private _decorationsCache: { [decorationId: string]: ViewModelDecoration; }; + + private _cachedModelDecorationsResolver: IDecorationsViewportData; + private _cachedModelDecorationsResolverViewRange: Range; + + constructor(editorId: number, model: editorCommon.IModel, configuration: editorCommon.IConfiguration, coordinatesConverter: ICoordinatesConverter) { + this.editorId = editorId; + this.model = model; + this.configuration = configuration; + this._coordinatesConverter = coordinatesConverter; + this._decorationsCache = Object.create(null); + this._clearCachedModelDecorationsResolver(); + } + + private _clearCachedModelDecorationsResolver(): void { + this._cachedModelDecorationsResolver = null; + this._cachedModelDecorationsResolverViewRange = null; + } + + public dispose(): void { + this._decorationsCache = null; + this._clearCachedModelDecorationsResolver(); + } + + public reset(): void { + this._decorationsCache = Object.create(null); + this._clearCachedModelDecorationsResolver(); + } + + public onModelDecorationsChanged(e: IModelDecorationsChangedEvent): void { + let changedDecorations = e.changedDecorations; + for (let i = 0, len = changedDecorations.length; i < len; i++) { + let changedDecoration = changedDecorations[i]; + let myDecoration = this._decorationsCache[changedDecoration]; + if (!myDecoration) { + continue; + } + + myDecoration.range = null; + } + + let removedDecorations = e.removedDecorations; + if (this._decorationsCache !== null && this._decorationsCache !== undefined) { + for (let i = 0, len = removedDecorations.length; i < len; i++) { + let removedDecoration = removedDecorations[i]; + delete this._decorationsCache[removedDecoration]; + } + } + + this._clearCachedModelDecorationsResolver(); + } + + public onLineMappingChanged(): void { + this._decorationsCache = Object.create(null); + + this._clearCachedModelDecorationsResolver(); + } + + private _getOrCreateViewModelDecoration(modelDecoration: editorCommon.IModelDecoration): ViewModelDecoration { + let id = modelDecoration.id; + let r = this._decorationsCache[id]; + if (!r) { + r = new ViewModelDecoration(modelDecoration); + this._decorationsCache[id] = r; + } + if (r.range === null) { + const modelRange = modelDecoration.range; + if (modelDecoration.options.isWholeLine) { + let start = this._coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.startLineNumber, 1)); + let end = this._coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.endLineNumber, this.model.getLineMaxColumn(modelRange.endLineNumber))); + r.range = new Range(start.lineNumber, start.column, end.lineNumber, end.column); + } else { + r.range = this._coordinatesConverter.convertModelRangeToViewRange(modelRange); + } + } + return r; + } + + public getAllOverviewRulerDecorations(): ViewModelDecoration[] { + let modelDecorations = this.model.getAllDecorations(this.editorId, this.configuration.editor.readOnly); + let result: ViewModelDecoration[] = [], resultLen = 0; + for (let i = 0, len = modelDecorations.length; i < len; i++) { + let modelDecoration = modelDecorations[i]; + let decorationOptions = modelDecoration.options; + + if (!decorationOptions.overviewRuler.color) { + continue; + } + + let viewModelDecoration = this._getOrCreateViewModelDecoration(modelDecoration); + result[resultLen++] = viewModelDecoration; + } + return result; + } + + public getDecorationsViewportData(viewRange: Range): IDecorationsViewportData { + var cacheIsValid = true; + cacheIsValid = cacheIsValid && (this._cachedModelDecorationsResolver !== null); + cacheIsValid = cacheIsValid && (viewRange.equalsRange(this._cachedModelDecorationsResolverViewRange)); + if (!cacheIsValid) { + this._cachedModelDecorationsResolver = this._getDecorationsViewportData(viewRange); + this._cachedModelDecorationsResolverViewRange = viewRange; + } + return this._cachedModelDecorationsResolver; + } + + private _getDecorationsViewportData(viewportRange: Range): IDecorationsViewportData { + let viewportModelRange = this._coordinatesConverter.convertViewRangeToModelRange(viewportRange); + let startLineNumber = viewportRange.startLineNumber; + let endLineNumber = viewportRange.endLineNumber; + let modelDecorations = this.model.getDecorationsInRange(viewportModelRange, this.editorId, this.configuration.editor.readOnly); + + let decorationsInViewport: ViewModelDecoration[] = [], decorationsInViewportLen = 0; + let inlineDecorations: InlineDecoration[][] = []; + for (let j = startLineNumber; j <= endLineNumber; j++) { + inlineDecorations[j - startLineNumber] = []; + } + + for (let i = 0, len = modelDecorations.length; i < len; i++) { + let modelDecoration = modelDecorations[i]; + let decorationOptions = modelDecoration.options; + + let viewModelDecoration = this._getOrCreateViewModelDecoration(modelDecoration); + let viewRange = viewModelDecoration.range; + + decorationsInViewport[decorationsInViewportLen++] = viewModelDecoration; + + if (decorationOptions.inlineClassName) { + let inlineDecoration = new InlineDecoration(viewRange, decorationOptions.inlineClassName, false); + let intersectedStartLineNumber = Math.max(startLineNumber, viewRange.startLineNumber); + let intersectedEndLineNumber = Math.min(endLineNumber, viewRange.endLineNumber); + for (let j = intersectedStartLineNumber; j <= intersectedEndLineNumber; j++) { + inlineDecorations[j - startLineNumber].push(inlineDecoration); + } + } + if (decorationOptions.beforeContentClassName) { + if (startLineNumber <= viewRange.startLineNumber && viewRange.startLineNumber <= endLineNumber) { + // TODO: What happens if the startLineNumber and startColumn is at the end of a line? + let inlineDecoration = new InlineDecoration( + new Range(viewRange.startLineNumber, viewRange.startColumn, viewRange.startLineNumber, viewRange.startColumn + 1), + decorationOptions.beforeContentClassName, + true + ); + inlineDecorations[viewRange.startLineNumber - startLineNumber].push(inlineDecoration); + } + } + if (decorationOptions.afterContentClassName) { + if (startLineNumber <= viewRange.endLineNumber && viewRange.endLineNumber <= endLineNumber && viewRange.endColumn > 1) { + let inlineDecoration = new InlineDecoration( + new Range(viewRange.endLineNumber, viewRange.endColumn - 1, viewRange.endLineNumber, viewRange.endColumn), + decorationOptions.afterContentClassName, + true + ); + inlineDecorations[viewRange.endLineNumber - startLineNumber].push(inlineDecoration); + } + } + } + + return { + decorations: decorationsInViewport, + inlineDecorations: inlineDecorations + }; + } +} diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts new file mode 100644 index 0000000000..d0151a30c5 --- /dev/null +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -0,0 +1,559 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { EmitterEvent } from 'vs/base/common/eventEmitter'; +import * as strings from 'vs/base/common/strings'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { TokenizationRegistry, ColorId, LanguageId } from 'vs/editor/common/modes'; +import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; +import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; +import { MinimapLinesRenderingData, ViewLineRenderingData, ViewModelDecoration, IViewModel, ICoordinatesConverter, ViewEventsCollector } from 'vs/editor/common/viewModel/viewModel'; +import { SplitLinesCollection, IViewModelLinesCollection, IdentityLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { MinimapTokensColorTracker } from 'vs/editor/common/view/minimapCharRenderer'; +import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; +import { Color } from 'vs/base/common/color'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +const USE_IDENTITY_LINES_COLLECTION = true; + +export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel { + + private readonly editorId: number; + private readonly configuration: editorCommon.IConfiguration; + private readonly model: editorCommon.IModel; + private readonly lines: IViewModelLinesCollection; + public readonly coordinatesConverter: ICoordinatesConverter; + public readonly viewLayout: ViewLayout; + + private readonly decorations: ViewModelDecorations; + + private _isDisposing: boolean; + private _centeredViewLine: number; + + constructor(editorId: number, configuration: editorCommon.IConfiguration, model: editorCommon.IModel, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { + super(); + + this.editorId = editorId; + this.configuration = configuration; + this.model = model; + + if (USE_IDENTITY_LINES_COLLECTION && this.model.isTooLargeForTokenization()) { + + this.lines = new IdentityLinesCollection(this.model); + + } else { + const conf = this.configuration.editor; + + let hardWrappingLineMapperFactory = new CharacterHardWrappingLineMapperFactory( + conf.wrappingInfo.wordWrapBreakBeforeCharacters, + conf.wrappingInfo.wordWrapBreakAfterCharacters, + conf.wrappingInfo.wordWrapBreakObtrusiveCharacters + ); + + this.lines = new SplitLinesCollection( + this.model, + hardWrappingLineMapperFactory, + this.model.getOptions().tabSize, + conf.wrappingInfo.wrappingColumn, + conf.fontInfo.typicalFullwidthCharacterWidth / conf.fontInfo.typicalHalfwidthCharacterWidth, + conf.wrappingInfo.wrappingIndent + ); + } + + this.coordinatesConverter = this.lines.createCoordinatesConverter(); + + this.viewLayout = this._register(new ViewLayout(this.configuration, this.getLineCount(), scheduleAtNextAnimationFrame)); + + this._register(this.viewLayout.onDidScroll((e) => { + this._emit([new viewEvents.ViewScrollChangedEvent(e)]); + })); + + this._isDisposing = false; + this._centeredViewLine = -1; + + this.decorations = new ViewModelDecorations(this.editorId, this.model, this.configuration, this.coordinatesConverter); + + this._register(this.model.addBulkListener((events: EmitterEvent[]) => { + if (this._isDisposing) { + // Disposing the lines might end up sending model decoration changed events + // ...we no longer care about them... + return; + } + let eventsCollector = new ViewEventsCollector(); + this._onModelEvents(eventsCollector, events); + this._emit(eventsCollector.finalize()); + })); + + this._register(this.configuration.onDidChange((e) => { + const eventsCollector = new ViewEventsCollector(); + this._onConfigurationChanged(eventsCollector, e); + this._emit(eventsCollector.finalize()); + })); + + this._register(MinimapTokensColorTracker.getInstance().onDidChange(() => { + this._emit([new viewEvents.ViewTokensColorsChangedEvent()]); + })); + } + + public dispose(): void { + this._isDisposing = true; + this.decorations.dispose(); + this.lines.dispose(); + super.dispose(); + } + + private _onConfigurationChanged(eventsCollector: ViewEventsCollector, e: IConfigurationChangedEvent): void { + + // We might need to restore the current centered view range, so save it (if available) + const previousCenteredModelRange = this.getCenteredRangeInViewport(); + let revealPreviousCenteredModelRange = false; + + const conf = this.configuration.editor; + + if (this.lines.setWrappingSettings(conf.wrappingInfo.wrappingIndent, conf.wrappingInfo.wrappingColumn, conf.fontInfo.typicalFullwidthCharacterWidth / conf.fontInfo.typicalHalfwidthCharacterWidth)) { + eventsCollector.emit(new viewEvents.ViewFlushedEvent()); + eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + this.decorations.onLineMappingChanged(); + this.viewLayout.onFlushed(this.getLineCount()); + + if (this.viewLayout.getCurrentScrollTop() !== 0) { + // Never change the scroll position from 0 to something else... + revealPreviousCenteredModelRange = true; + } + } + + if (e.readOnly) { + // Must read again all decorations due to readOnly filtering + this.decorations.reset(); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + } + + eventsCollector.emit(new viewEvents.ViewConfigurationChangedEvent(e)); + this.viewLayout.onConfigurationChanged(e); + + if (revealPreviousCenteredModelRange && previousCenteredModelRange) { + // modelLine -> viewLine + const newCenteredViewRange = this.coordinatesConverter.convertModelRangeToViewRange(previousCenteredModelRange); + + // Send a reveal event to restore the centered content + eventsCollector.emit(new viewEvents.ViewRevealRangeRequestEvent( + newCenteredViewRange, + viewEvents.VerticalRevealType.Center, + false, + editorCommon.ScrollType.Immediate + )); + } + } + + private _onModelEvents(eventsCollector: ViewEventsCollector, events: EmitterEvent[]): void { + + // A quick check if there are model content change events incoming + // in order to update the configuration and reset the centered view line + for (let i = 0, len = events.length; i < len; i++) { + const eventType = events[i].type; + if (eventType === textModelEvents.TextModelEventType.ModelRawContentChanged2) { + // There is a content change event + this._centeredViewLine = -1; + this.configuration.setMaxLineNumber(this.model.getLineCount()); + + break; + } + } + + let hadOtherModelChange = false; + let hadModelLineChangeThatChangedLineMapping = false; + + for (let i = 0, len = events.length; i < len; i++) { + const _e = events[i]; + const type = _e.type; + const data = _e.data; + + switch (type) { + + case textModelEvents.TextModelEventType.ModelRawContentChanged2: { + const e = data; + const changes = e.changes; + const versionId = e.versionId; + + for (let j = 0, lenJ = changes.length; j < lenJ; j++) { + const change = changes[j]; + + switch (change.changeType) { + case textModelEvents.RawContentChangedType.Flush: { + this.lines.onModelFlushed(); + eventsCollector.emit(new viewEvents.ViewFlushedEvent()); + this.decorations.reset(); + this.viewLayout.onFlushed(this.getLineCount()); + hadOtherModelChange = true; + break; + } + case textModelEvents.RawContentChangedType.LinesDeleted: { + const linesDeletedEvent = this.lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber); + if (linesDeletedEvent !== null) { + eventsCollector.emit(linesDeletedEvent); + this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber); + } + hadOtherModelChange = true; + break; + } + case textModelEvents.RawContentChangedType.LinesInserted: { + const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, change.detail.split('\n')); + if (linesInsertedEvent !== null) { + eventsCollector.emit(linesInsertedEvent); + this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber); + } + hadOtherModelChange = true; + break; + } + case textModelEvents.RawContentChangedType.LineChanged: { + const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, change.detail); + hadModelLineChangeThatChangedLineMapping = lineMappingChanged; + if (linesChangedEvent) { + eventsCollector.emit(linesChangedEvent); + } + if (linesInsertedEvent) { + eventsCollector.emit(linesInsertedEvent); + this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber); + } + if (linesDeletedEvent) { + eventsCollector.emit(linesDeletedEvent); + this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber); + } + break; + } + case textModelEvents.RawContentChangedType.EOLChanged: { + // Nothing to do. The new version will be accepted below + break; + } + } + } + this.lines.acceptVersionId(versionId); + + break; + } + case textModelEvents.TextModelEventType.ModelTokensChanged: { + const e = data; + + let viewRanges: { fromLineNumber: number; toLineNumber: number; }[] = []; + for (let j = 0, lenJ = e.ranges.length; j < lenJ; j++) { + const modelRange = e.ranges[j]; + const viewStartLineNumber = this.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.fromLineNumber, 1)).lineNumber; + const viewEndLineNumber = this.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.toLineNumber, this.model.getLineMaxColumn(modelRange.toLineNumber))).lineNumber; + viewRanges[j] = { + fromLineNumber: viewStartLineNumber, + toLineNumber: viewEndLineNumber + }; + } + eventsCollector.emit(new viewEvents.ViewTokensChangedEvent(viewRanges)); + break; + } + case textModelEvents.TextModelEventType.ModelLanguageChanged: { + // That's ok, a model tokens changed event will follow shortly + break; + } + case textModelEvents.TextModelEventType.ModelContentChanged: { + // Ignore + break; + } + case textModelEvents.TextModelEventType.ModelOptionsChanged: { + // A tab size change causes a line mapping changed event => all view parts will repaint OK, no further event needed here + if (this.lines.setTabSize(this.model.getOptions().tabSize)) { + eventsCollector.emit(new viewEvents.ViewFlushedEvent()); + eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + this.decorations.onLineMappingChanged(); + this.viewLayout.onFlushed(this.getLineCount()); + } + + break; + } + case textModelEvents.TextModelEventType.ModelDecorationsChanged: { + const e = data; + this.decorations.onModelDecorationsChanged(e); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + break; + } + case textModelEvents.TextModelEventType.ModelDispose: { + // Ignore, since the editor will take care of this and destroy the view shortly + break; + } + default: + console.info('View received unknown event: '); + console.info(type, data); + } + } + + if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) { + eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + this.decorations.onLineMappingChanged(); + } + } + + public setHiddenAreas(ranges: Range[]): void { + let eventsCollector = new ViewEventsCollector(); + let lineMappingChanged = this.lines.setHiddenAreas(ranges); + if (lineMappingChanged) { + eventsCollector.emit(new viewEvents.ViewFlushedEvent()); + eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + this.decorations.onLineMappingChanged(); + this.viewLayout.onFlushed(this.getLineCount()); + } + this._emit(eventsCollector.finalize()); + } + + public getCenteredRangeInViewport(): Range { + if (this._centeredViewLine === -1) { + // Never got rendered or not rendered since last content change event + return null; + } + let viewLineNumber = this._centeredViewLine; + let currentCenteredViewRange = new Range(viewLineNumber, this.getLineMinColumn(viewLineNumber), viewLineNumber, this.getLineMaxColumn(viewLineNumber)); + return this.coordinatesConverter.convertViewRangeToModelRange(currentCenteredViewRange); + } + + public getCompletelyVisibleViewRange(): Range { + const partialData = this.viewLayout.getLinesViewportData(); + const startViewLineNumber = partialData.completelyVisibleStartLineNumber; + const endViewLineNumber = partialData.completelyVisibleEndLineNumber; + + return new Range( + startViewLineNumber, this.getLineMinColumn(startViewLineNumber), + endViewLineNumber, this.getLineMaxColumn(endViewLineNumber) + ); + } + + public getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range { + const partialData = this.viewLayout.getLinesViewportDataAtScrollTop(scrollTop); + const startViewLineNumber = partialData.completelyVisibleStartLineNumber; + const endViewLineNumber = partialData.completelyVisibleEndLineNumber; + + return new Range( + startViewLineNumber, this.getLineMinColumn(startViewLineNumber), + endViewLineNumber, this.getLineMaxColumn(endViewLineNumber) + ); + } + + public getTabSize(): number { + return this.model.getOptions().tabSize; + } + + public getLineCount(): number { + return this.lines.getViewLineCount(); + } + + /** + * Gives a hint that a lot of requests are about to come in for these line numbers. + */ + public setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void { + this._centeredViewLine = centeredLineNumber; + this.lines.warmUpLookupCache(startLineNumber, endLineNumber); + } + + public getLineIndentGuide(lineNumber: number): number { + return this.lines.getViewLineIndentGuide(lineNumber); + } + + public getLineContent(lineNumber: number): string { + return this.lines.getViewLineContent(lineNumber); + } + + public getLineMinColumn(lineNumber: number): number { + return this.lines.getViewLineMinColumn(lineNumber); + } + + public getLineMaxColumn(lineNumber: number): number { + return this.lines.getViewLineMaxColumn(lineNumber); + } + + public getLineFirstNonWhitespaceColumn(lineNumber: number): number { + var result = strings.firstNonWhitespaceIndex(this.getLineContent(lineNumber)); + if (result === -1) { + return 0; + } + return result + 1; + } + + public getLineLastNonWhitespaceColumn(lineNumber: number): number { + var result = strings.lastNonWhitespaceIndex(this.getLineContent(lineNumber)); + if (result === -1) { + return 0; + } + return result + 2; + } + + public getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[] { + return this.decorations.getDecorationsViewportData(visibleRange).decorations; + } + + public getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData { + let mightContainRTL = this.model.mightContainRTL(); + let mightContainNonBasicASCII = this.model.mightContainNonBasicASCII(); + let tabSize = this.getTabSize(); + let lineData = this.lines.getViewLineData(lineNumber); + let allInlineDecorations = this.decorations.getDecorationsViewportData(visibleRange).inlineDecorations; + let inlineDecorations = allInlineDecorations[lineNumber - visibleRange.startLineNumber]; + + return new ViewLineRenderingData( + lineData.minColumn, + lineData.maxColumn, + lineData.content, + mightContainRTL, + mightContainNonBasicASCII, + lineData.tokens, + inlineDecorations, + tabSize + ); + } + + public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData { + let result = this.lines.getViewLinesData(startLineNumber, endLineNumber, needed); + return new MinimapLinesRenderingData( + this.getTabSize(), + result + ); + } + + public getAllOverviewRulerDecorations(): ViewModelDecoration[] { + return this.decorations.getAllOverviewRulerDecorations(); + } + + public getValueInRange(range: Range, eol: editorCommon.EndOfLinePreference): string { + var modelRange = this.coordinatesConverter.convertViewRangeToModelRange(range); + return this.model.getValueInRange(modelRange, eol); + } + + public getModelLineMaxColumn(modelLineNumber: number): number { + return this.model.getLineMaxColumn(modelLineNumber); + } + + public validateModelPosition(position: IPosition): Position { + return this.model.validatePosition(position); + } + + public deduceModelPositionRelativeToViewPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position { + const modelAnchor = this.coordinatesConverter.convertViewPositionToModelPosition(viewAnchorPosition); + if (this.model.getEOL().length === 2) { + // This model uses CRLF, so the delta must take that into account + if (deltaOffset < 0) { + deltaOffset -= lineFeedCnt; + } else { + deltaOffset += lineFeedCnt; + } + } + + const modelAnchorOffset = this.model.getOffsetAt(modelAnchor); + const resultOffset = modelAnchorOffset + deltaOffset; + return this.model.getPositionAt(resultOffset); + } + + public getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean): string { + let newLineCharacter = this.model.getEOL(); + + if (ranges.length === 1) { + let range: Range = ranges[0]; + if (range.isEmpty()) { + if (emptySelectionClipboard) { + let modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber; + return this.model.getLineContent(modelLineNumber) + newLineCharacter; + } else { + return ''; + } + } + + return this.getValueInRange(range, editorCommon.EndOfLinePreference.TextDefined); + } else { + ranges = ranges.slice(0).sort(Range.compareRangesUsingStarts); + let result: string[] = []; + for (let i = 0; i < ranges.length; i++) { + result.push(this.getValueInRange(ranges[i], editorCommon.EndOfLinePreference.TextDefined)); + } + + return result.join(newLineCharacter); + } + } + + public getHTMLToCopy(viewRanges: Range[], emptySelectionClipboard: boolean): string { + if (this.model.getLanguageIdentifier().id === LanguageId.PlainText) { + return null; + } + + if (viewRanges.length !== 1) { + // no multiple selection support at this time + return null; + } + + let range = this.coordinatesConverter.convertViewRangeToModelRange(viewRanges[0]); + if (range.isEmpty()) { + if (!emptySelectionClipboard) { + // nothing to copy + return null; + } + let lineNumber = range.startLineNumber; + range = new Range(lineNumber, this.model.getLineMinColumn(lineNumber), lineNumber, this.model.getLineMaxColumn(lineNumber)); + } + + const fontInfo = this.configuration.editor.fontInfo; + const colorMap = this._getColorMap(); + + return ( + `
` + + this._getHTMLToCopy(range, colorMap) + + '
' + ); + } + + private _getHTMLToCopy(modelRange: Range, colorMap: string[]): string { + const startLineNumber = modelRange.startLineNumber; + const startColumn = modelRange.startColumn; + const endLineNumber = modelRange.endLineNumber; + const endColumn = modelRange.endColumn; + + const tabSize = this.getTabSize(); + + let result = ''; + + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + const lineTokens = this.model.getLineTokens(lineNumber); + const lineContent = lineTokens.getLineContent(); + const startOffset = (lineNumber === startLineNumber ? startColumn - 1 : 0); + const endOffset = (lineNumber === endLineNumber ? endColumn - 1 : lineContent.length); + + if (lineContent === '') { + result += '
'; + } else { + result += tokenizeLineToHTML(lineContent, lineTokens.inflate(), colorMap, startOffset, endOffset, tabSize); + } + } + + return result; + } + + private _getColorMap(): string[] { + let colorMap = TokenizationRegistry.getColorMap(); + let result: string[] = [null]; + for (let i = 1, len = colorMap.length; i < len; i++) { + result[i] = Color.Format.CSS.formatHex(colorMap[i]); + } + return result; + } +} diff --git a/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.css b/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.css new file mode 100644 index 0000000000..dd3671a784 --- /dev/null +++ b/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.css @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .bracket-match { + box-sizing: border-box; +} diff --git a/src/vs/editor/contrib/bracketMatching/common/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/common/bracketMatching.ts new file mode 100644 index 0000000000..93ce8b9482 --- /dev/null +++ b/src/vs/editor/contrib/bracketMatching/common/bracketMatching.ts @@ -0,0 +1,233 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { editorAction, commonEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorBracketMatchBackground, editorBracketMatchBorder } from 'vs/editor/common/view/editorColorRegistry'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; + +@editorAction +class SelectBracketAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.jumpToBracket', + label: nls.localize('smartSelect.jumpBracket', "Go to Bracket"), + alias: 'Go to Bracket', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let controller = BracketMatchingController.get(editor); + if (!controller) { + return; + } + controller.jumpToBracket(); + } +} + +type Brackets = [Range, Range]; + +class BracketsData { + public readonly position: Position; + public readonly brackets: Brackets; + + constructor(position: Position, brackets: Brackets) { + this.position = position; + this.brackets = brackets; + } +} + +@commonEditorContribution +export class BracketMatchingController extends Disposable implements editorCommon.IEditorContribution { + private static ID = 'editor.contrib.bracketMatchingController'; + + public static get(editor: editorCommon.ICommonCodeEditor): BracketMatchingController { + return editor.getContribution(BracketMatchingController.ID); + } + + private readonly _editor: editorCommon.ICommonCodeEditor; + + private _lastBracketsData: BracketsData[]; + private _lastVersionId: number; + private _decorations: string[]; + private _updateBracketsSoon: RunOnceScheduler; + private _matchBrackets: boolean; + + constructor( + editor: editorCommon.ICommonCodeEditor + ) { + super(); + this._editor = editor; + this._lastBracketsData = []; + this._lastVersionId = 0; + this._decorations = []; + this._updateBracketsSoon = this._register(new RunOnceScheduler(() => this._updateBrackets(), 50)); + this._matchBrackets = this._editor.getConfiguration().contribInfo.matchBrackets; + + this._updateBracketsSoon.schedule(); + this._register(editor.onDidChangeCursorPosition((e) => { + + if (!this._matchBrackets) { + // Early exit if nothing needs to be done! + // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) + return; + } + + this._updateBracketsSoon.schedule(); + })); + this._register(editor.onDidChangeModel((e) => { this._decorations = []; this._updateBracketsSoon.schedule(); })); + this._register(editor.onDidChangeConfiguration((e) => { + this._matchBrackets = this._editor.getConfiguration().contribInfo.matchBrackets; + if (!this._matchBrackets && this._decorations.length > 0) { + // Remove existing decorations if bracket matching is off + this._decorations = this._editor.deltaDecorations(this._decorations, []); + } + this._updateBracketsSoon.schedule(); + })); + } + + public getId(): string { + return BracketMatchingController.ID; + } + + public jumpToBracket(): void { + const model = this._editor.getModel(); + if (!model) { + return; + } + + let newSelections = this._editor.getSelections().map(selection => { + const position = selection.getStartPosition(); + + // find matching brackets if position is on a bracket + const brackets = model.matchBracket(position); + let newCursorPosition: Position = null; + if (brackets) { + if (brackets[0].containsPosition(position)) { + newCursorPosition = brackets[1].getStartPosition(); + } else if (brackets[1].containsPosition(position)) { + newCursorPosition = brackets[0].getStartPosition(); + } + } else { + // find the next bracket if the position isn't on a matching bracket + const nextBracket = model.findNextBracket(position); + if (nextBracket && nextBracket.range) { + newCursorPosition = nextBracket.range.getStartPosition(); + } + } + + if (newCursorPosition) { + return new Selection(newCursorPosition.lineNumber, newCursorPosition.column, newCursorPosition.lineNumber, newCursorPosition.column); + } + return new Selection(position.lineNumber, position.column, position.lineNumber, position.column); + }); + + this._editor.setSelections(newSelections); + } + + private static _DECORATION_OPTIONS = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'bracket-match' + }); + + private _updateBrackets(): void { + if (!this._matchBrackets) { + return; + } + this._recomputeBrackets(); + + let newDecorations: editorCommon.IModelDeltaDecoration[] = [], newDecorationsLen = 0; + for (let i = 0, len = this._lastBracketsData.length; i < len; i++) { + let brackets = this._lastBracketsData[i].brackets; + if (brackets) { + newDecorations[newDecorationsLen++] = { range: brackets[0], options: BracketMatchingController._DECORATION_OPTIONS }; + newDecorations[newDecorationsLen++] = { range: brackets[1], options: BracketMatchingController._DECORATION_OPTIONS }; + } + } + + this._decorations = this._editor.deltaDecorations(this._decorations, newDecorations); + } + + private _recomputeBrackets(): void { + const model = this._editor.getModel(); + if (!model) { + // no model => no brackets! + this._lastBracketsData = []; + this._lastVersionId = 0; + return; + } + + const versionId = model.getVersionId(); + let previousData: BracketsData[] = []; + if (this._lastVersionId === versionId) { + // use the previous data only if the model is at the same version id + previousData = this._lastBracketsData; + } + + const selections = this._editor.getSelections(); + + let positions: Position[] = [], positionsLen = 0; + for (let i = 0, len = selections.length; i < len; i++) { + let selection = selections[i]; + + if (selection.isEmpty()) { + // will bracket match a cursor only if the selection is collapsed + positions[positionsLen++] = selection.getStartPosition(); + } + } + + // sort positions for `previousData` cache hits + if (positions.length > 1) { + positions.sort(Position.compare); + } + + let newData: BracketsData[] = [], newDataLen = 0; + let previousIndex = 0, previousLen = previousData.length; + for (let i = 0, len = positions.length; i < len; i++) { + let position = positions[i]; + + while (previousIndex < previousLen && previousData[previousIndex].position.isBefore(position)) { + previousIndex++; + } + + if (previousIndex < previousLen && previousData[previousIndex].position.equals(position)) { + newData[newDataLen++] = previousData[previousIndex]; + } else { + let brackets = model.matchBracket(position); + newData[newDataLen++] = new BracketsData(position, brackets); + } + } + + this._lastBracketsData = newData; + this._lastVersionId = versionId; + } +} + +registerThemingParticipant((theme, collector) => { + let bracketMatchBackground = theme.getColor(editorBracketMatchBackground); + if (bracketMatchBackground) { + collector.addRule(`.monaco-editor .bracket-match { background-color: ${bracketMatchBackground}; }`); + } + let bracketMatchBorder = theme.getColor(editorBracketMatchBorder); + if (bracketMatchBorder) { + collector.addRule(`.monaco-editor .bracket-match { border: 1px solid ${bracketMatchBorder}; }`); + } +}); diff --git a/src/vs/editor/contrib/bracketMatching/test/common/bracketMatching.test.ts b/src/vs/editor/contrib/bracketMatching/test/common/bracketMatching.test.ts new file mode 100644 index 0000000000..3d643b8350 --- /dev/null +++ b/src/vs/editor/contrib/bracketMatching/test/common/bracketMatching.test.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { Position } from 'vs/editor/common/core/position'; +import { Model } from 'vs/editor/common/model/model'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { BracketMatchingController } from 'vs/editor/contrib/bracketMatching/common/bracketMatching'; + +suite('bracket matching', () => { + class BracketMode extends MockMode { + + private static _id = new LanguageIdentifier('bracketMode', 3); + + constructor() { + super(BracketMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'], + ] + })); + } + } + + test('issue #183: jump to matching bracket position', () => { + let mode = new BracketMode(); + let model = Model.createFromString('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier()); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController); + + // start on closing bracket + editor.setPosition(new Position(1, 20)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 9)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 19)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 9)); + + // start on opening bracket + editor.setPosition(new Position(1, 23)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 31)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 23)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 31)); + + bracketMatchingController.dispose(); + }); + + model.dispose(); + mode.dispose(); + }); + + test('Jump to next bracket', () => { + let mode = new BracketMode(); + let model = Model.createFromString('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController); + + // start position between brackets + editor.setPosition(new Position(1, 16)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 18)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 14)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 18)); + + // skip brackets in comments + editor.setPosition(new Position(1, 21)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 23)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 24)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 23)); + + // do not break if no brackets are available + editor.setPosition(new Position(1, 26)); + bracketMatchingController.jumpToBracket(); + assert.deepEqual(editor.getPosition(), new Position(1, 26)); + + bracketMatchingController.dispose(); + }); + + model.dispose(); + mode.dispose(); + }); +}); diff --git a/src/vs/editor/contrib/caretOperations/common/caretOperations.ts b/src/vs/editor/contrib/caretOperations/common/caretOperations.ts new file mode 100644 index 0000000000..7cc427e3e6 --- /dev/null +++ b/src/vs/editor/contrib/caretOperations/common/caretOperations.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { ICommand, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { IActionOptions, editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { MoveCaretCommand } from './moveCaretCommand'; + +class MoveCaretAction extends EditorAction { + + private left: boolean; + + constructor(left: boolean, opts: IActionOptions) { + super(opts); + + this.left = left; + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + + var commands: ICommand[] = []; + var selections = editor.getSelections(); + + for (var i = 0; i < selections.length; i++) { + commands.push(new MoveCaretCommand(selections[i], this.left)); + } + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } +} + +@editorAction +class MoveCaretLeftAction extends MoveCaretAction { + constructor() { + super(true, { + id: 'editor.action.moveCarretLeftAction', + label: nls.localize('caret.moveLeft', "Move Caret Left"), + alias: 'Move Caret Left', + precondition: EditorContextKeys.writable + }); + } +} + +@editorAction +class MoveCaretRightAction extends MoveCaretAction { + constructor() { + super(false, { + id: 'editor.action.moveCarretRightAction', + label: nls.localize('caret.moveRight', "Move Caret Right"), + alias: 'Move Caret Right', + precondition: EditorContextKeys.writable + }); + } +} diff --git a/src/vs/editor/contrib/caretOperations/common/moveCaretCommand.ts b/src/vs/editor/contrib/caretOperations/common/moveCaretCommand.ts new file mode 100644 index 0000000000..149e09051f --- /dev/null +++ b/src/vs/editor/contrib/caretOperations/common/moveCaretCommand.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon'; + +export class MoveCaretCommand implements ICommand { + + private _selection: Selection; + private _isMovingLeft: boolean; + + private _cutStartIndex: number; + private _cutEndIndex: number; + private _moved: boolean; + + private _selectionId: string; + + constructor(selection: Selection, isMovingLeft: boolean) { + this._selection = selection; + this._isMovingLeft = isMovingLeft; + } + + public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void { + var s = this._selection; + this._selectionId = builder.trackSelection(s); + if (s.startLineNumber !== s.endLineNumber) { + return; + } + if (this._isMovingLeft && s.startColumn === 0) { + return; + } else if (!this._isMovingLeft && s.endColumn === model.getLineMaxColumn(s.startLineNumber)) { + return; + } + + var lineNumber = s.selectionStartLineNumber; + var lineContent = model.getLineContent(lineNumber); + + var left; + var middle; + var right; + + if (this._isMovingLeft) { + left = lineContent.substring(0, s.startColumn - 2); + middle = lineContent.substring(s.startColumn - 1, s.endColumn - 1); + right = lineContent.substring(s.startColumn - 2, s.startColumn - 1) + lineContent.substring(s.endColumn - 1); + } else { + left = lineContent.substring(0, s.startColumn - 1) + lineContent.substring(s.endColumn - 1, s.endColumn); + middle = lineContent.substring(s.startColumn - 1, s.endColumn - 1); + right = lineContent.substring(s.endColumn); + } + + var newLineContent = left + middle + right; + + builder.addEditOperation(new Range(lineNumber, 1, lineNumber, model.getLineMaxColumn(lineNumber)), null); + builder.addEditOperation(new Range(lineNumber, 1, lineNumber, 1), newLineContent); + + this._cutStartIndex = s.startColumn + (this._isMovingLeft ? -1 : 1); + this._cutEndIndex = this._cutStartIndex + s.endColumn - s.startColumn; + this._moved = true; + } + + public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { + var result = helper.getTrackedSelection(this._selectionId); + if (this._moved) { + result = result.setStartPosition(result.startLineNumber, this._cutStartIndex); + result = result.setEndPosition(result.startLineNumber, this._cutEndIndex); + } + return result; + } +} diff --git a/src/vs/editor/contrib/caretOperations/common/transpose.ts b/src/vs/editor/contrib/caretOperations/common/transpose.ts new file mode 100644 index 0000000000..59a971a20a --- /dev/null +++ b/src/vs/editor/contrib/caretOperations/common/transpose.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Range } from 'vs/editor/common/core/range'; +import { ICommand, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; + +@editorAction +class TransposeLettersAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.transposeLetters', + label: nls.localize('transposeLetters.label', "Transpose Letters"), + alias: 'Transpose Letters', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: 0, + mac: { + primary: KeyMod.WinCtrl | KeyCode.KEY_T + } + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let model = editor.getModel(); + let commands: ICommand[] = []; + let selections = editor.getSelections(); + + for (let i = 0; i < selections.length; i++) { + let selection = selections[i]; + if (!selection.isEmpty()) { + continue; + } + let lineNumber = selection.startLineNumber; + let column = selection.startColumn; + if (column === 1) { + // at the beginning of line + continue; + } + let maxColumn = model.getLineMaxColumn(lineNumber); + if (column === maxColumn) { + // at the end of line + continue; + } + + let lineContent = model.getLineContent(lineNumber); + let charToTheLeft = lineContent.charAt(column - 2); + let charToTheRight = lineContent.charAt(column - 1); + + let replaceRange = new Range(lineNumber, column - 1, lineNumber, column + 1); + + commands.push(new ReplaceCommand(replaceRange, charToTheRight + charToTheLeft)); + } + + if (commands.length > 0) { + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } + } +} diff --git a/src/vs/editor/contrib/caretOperations/test/common/moveCarretCommand.test.ts b/src/vs/editor/contrib/caretOperations/test/common/moveCarretCommand.test.ts new file mode 100644 index 0000000000..8722e144b7 --- /dev/null +++ b/src/vs/editor/contrib/caretOperations/test/common/moveCarretCommand.test.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Selection } from 'vs/editor/common/core/selection'; +import { MoveCaretCommand } from 'vs/editor/contrib/caretOperations/common/moveCaretCommand'; +import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; + + +function testMoveCaretLeftCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new MoveCaretCommand(sel, true), expectedLines, expectedSelection); +} + +function testMoveCaretRightCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new MoveCaretCommand(sel, false), expectedLines, expectedSelection); +} + +suite('Editor Contrib - Move Caret Command', () => { + + test('move selection to left', function () { + testMoveCaretLeftCommand( + [ + '012345' + ], + new Selection(1, 3, 1, 5), + [ + '023145' + ], + new Selection(1, 2, 1, 4) + ); + }); + test('move selection to right', function () { + testMoveCaretRightCommand( + [ + '012345' + ], + new Selection(1, 3, 1, 5), + [ + '014235' + ], + new Selection(1, 4, 1, 6) + ); + }); + test('move selection to left - from first column - no change', function () { + testMoveCaretLeftCommand( + [ + '012345' + ], + new Selection(1, 1, 1, 1), + [ + '012345' + ], + new Selection(1, 1, 1, 1) + ); + }); + test('move selection to right - from last column - no change', function () { + testMoveCaretRightCommand( + [ + '012345' + ], + new Selection(1, 5, 1, 7), + [ + '012345' + ], + new Selection(1, 5, 1, 7) + ); + }); +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/clipboard/browser/clipboard.css b/src/vs/editor/contrib/clipboard/browser/clipboard.css new file mode 100644 index 0000000000..4c398245f1 --- /dev/null +++ b/src/vs/editor/contrib/clipboard/browser/clipboard.css @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-menu .monaco-action-bar.vertical .action-label.hover { + background-color: #EEE; +} \ No newline at end of file diff --git a/src/vs/editor/contrib/clipboard/browser/clipboard.ts b/src/vs/editor/contrib/clipboard/browser/clipboard.ts new file mode 100644 index 0000000000..6f4b033f2d --- /dev/null +++ b/src/vs/editor/contrib/clipboard/browser/clipboard.ts @@ -0,0 +1,199 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./clipboard'; +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import * as browser from 'vs/base/browser/browser'; +import * as platform from 'vs/base/common/platform'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { editorAction, IActionOptions, EditorAction, ICommandKeybindingsOptions } from 'vs/editor/common/editorCommonExtensions'; +import { CopyOptions } from 'vs/editor/browser/controller/textAreaInput'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; + +const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste'; + +const supportsCut = (platform.isNative || document.queryCommandSupported('cut')); +const supportsCopy = (platform.isNative || document.queryCommandSupported('copy')); +// IE and Edge have trouble with setting html content in clipboard +const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdgeOrIE); +// Chrome incorrectly returns true for document.queryCommandSupported('paste') +// when the paste feature is available but the calling script has insufficient +// privileges to actually perform the action +const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste'))); + +type ExecCommand = 'cut' | 'copy' | 'paste'; + +function conditionalEditorAction(condition: boolean) { + if (!condition) { + return () => { }; + } + return editorAction; +} + +abstract class ExecCommandAction extends EditorAction { + + private browserCommand: ExecCommand; + + constructor(browserCommand: ExecCommand, opts: IActionOptions) { + super(opts); + this.browserCommand = browserCommand; + } + + public runCommand(accessor: ServicesAccessor, args: any): void { + let focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); + // Only if editor text focus (i.e. not if editor has widget focus). + if (focusedEditor && focusedEditor.isFocused()) { + focusedEditor.trigger('keyboard', this.id, args); + return; + } + + document.execCommand(this.browserCommand); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + editor.focus(); + document.execCommand(this.browserCommand); + } +} + +@conditionalEditorAction(supportsCut) +class ExecCommandCutAction extends ExecCommandAction { + + constructor() { + let kbOpts: ICommandKeybindingsOptions = { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.KEY_X, + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] } + }; + // Do not bind cut keybindings in the browser, + // since browsers do that for us and it avoids security prompts + if (!platform.isNative) { + kbOpts = null; + } + super('cut', { + id: 'editor.action.clipboardCutAction', + label: nls.localize('actions.clipboard.cutLabel', "Cut"), + alias: 'Cut', + precondition: EditorContextKeys.writable, + kbOpts: kbOpts, + menuOpts: { + group: CLIPBOARD_CONTEXT_MENU_GROUP, + order: 1 + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + const emptySelectionClipboard = editor.getConfiguration().emptySelectionClipboard; + + if (!emptySelectionClipboard && editor.getSelection().isEmpty()) { + return; + } + + super.run(accessor, editor); + } +} + +@conditionalEditorAction(supportsCopy) +class ExecCommandCopyAction extends ExecCommandAction { + + constructor() { + let kbOpts: ICommandKeybindingsOptions = { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] } + }; + // Do not bind copy keybindings in the browser, + // since browsers do that for us and it avoids security prompts + if (!platform.isNative) { + kbOpts = null; + } + + super('copy', { + id: 'editor.action.clipboardCopyAction', + label: nls.localize('actions.clipboard.copyLabel', "Copy"), + alias: 'Copy', + precondition: null, + kbOpts: kbOpts, + menuOpts: { + group: CLIPBOARD_CONTEXT_MENU_GROUP, + order: 2 + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + const emptySelectionClipboard = editor.getConfiguration().emptySelectionClipboard; + + if (!emptySelectionClipboard && editor.getSelection().isEmpty()) { + return; + } + + super.run(accessor, editor); + } +} + +@conditionalEditorAction(supportsPaste) +class ExecCommandPasteAction extends ExecCommandAction { + + constructor() { + let kbOpts: ICommandKeybindingsOptions = { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] } + }; + // Do not bind paste keybindings in the browser, + // since browsers do that for us and it avoids security prompts + if (!platform.isNative) { + kbOpts = null; + } + + super('paste', { + id: 'editor.action.clipboardPasteAction', + label: nls.localize('actions.clipboard.pasteLabel', "Paste"), + alias: 'Paste', + precondition: EditorContextKeys.writable, + kbOpts: kbOpts, + menuOpts: { + group: CLIPBOARD_CONTEXT_MENU_GROUP, + order: 3 + } + }); + } +} + +@conditionalEditorAction(supportsCopyWithSyntaxHighlighting) +class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction { + + constructor() { + super('copy', { + id: 'editor.action.clipboardCopyWithSyntaxHighlightingAction', + label: nls.localize('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy With Syntax Highlighting"), + alias: 'Copy With Syntax Highlighting', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: null + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + const emptySelectionClipboard = editor.getConfiguration().emptySelectionClipboard; + + if (!emptySelectionClipboard && editor.getSelection().isEmpty()) { + return; + } + + CopyOptions.forceCopyWithSyntaxHighlighting = true; + super.run(accessor, editor); + CopyOptions.forceCopyWithSyntaxHighlighting = false; + } +} diff --git a/src/vs/editor/contrib/codelens/browser/codelens.ts b/src/vs/editor/contrib/codelens/browser/codelens.ts new file mode 100644 index 0000000000..6166526426 --- /dev/null +++ b/src/vs/editor/contrib/codelens/browser/codelens.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; +import { mergeSort } from 'vs/base/common/arrays'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { CodeLensProviderRegistry, CodeLensProvider, ICodeLensSymbol } from 'vs/editor/common/modes'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { asWinJsPromise } from 'vs/base/common/async'; + +export interface ICodeLensData { + symbol: ICodeLensSymbol; + provider: CodeLensProvider; +} + +export function getCodeLensData(model: IModel): TPromise { + + const symbols: ICodeLensData[] = []; + const provider = CodeLensProviderRegistry.ordered(model); + + const promises = provider.map(provider => asWinJsPromise(token => provider.provideCodeLenses(model, token)).then(result => { + if (Array.isArray(result)) { + for (let symbol of result) { + symbols.push({ symbol, provider }); + } + } + }, onUnexpectedExternalError)); + + return TPromise.join(promises).then(() => { + + return mergeSort(symbols, (a, b) => { + // sort by lineNumber, provider-rank, and column + if (a.symbol.range.startLineNumber < b.symbol.range.startLineNumber) { + return -1; + } else if (a.symbol.range.startLineNumber > b.symbol.range.startLineNumber) { + return 1; + } else if (provider.indexOf(a.provider) < provider.indexOf(b.provider)) { + return -1; + } else if (provider.indexOf(a.provider) > provider.indexOf(b.provider)) { + return 1; + } else if (a.symbol.range.startColumn < b.symbol.range.startColumn) { + return -1; + } else if (a.symbol.range.startColumn > b.symbol.range.startColumn) { + return 1; + } else { + return 0; + } + }); + }); +} + +CommonEditorRegistry.registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { + + const { resource } = args; + if (!(resource instanceof URI)) { + throw illegalArgument(); + } + + const model = accessor.get(IModelService).getModel(resource); + if (!model) { + throw illegalArgument(); + } + + return getCodeLensData(model).then(value => value.map(item => item.symbol)); +}); diff --git a/src/vs/editor/contrib/codelens/browser/codelensController.ts b/src/vs/editor/contrib/codelens/browser/codelensController.ts new file mode 100644 index 0000000000..52607b5049 --- /dev/null +++ b/src/vs/editor/contrib/codelens/browser/codelensController.ts @@ -0,0 +1,306 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { RunOnceScheduler, asWinJsPromise } from 'vs/base/common/async'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IMessageService } from 'vs/platform/message/common/message'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { ICodeLensData, getCodeLensData } from './codelens'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/browser/codelensWidget'; + +@editorContribution +export class CodeLensContribution implements editorCommon.IEditorContribution { + + private static ID: string = 'css.editor.codeLens'; + + private _isEnabled: boolean; + + private _globalToDispose: IDisposable[]; + private _localToDispose: IDisposable[]; + private _lenses: CodeLens[]; + private _currentFindCodeLensSymbolsPromise: TPromise; + private _modelChangeCounter: number; + private _currentFindOccPromise: TPromise; + private _detectVisibleLenses: RunOnceScheduler; + + constructor( + private _editor: editorBrowser.ICodeEditor, + @ICommandService private _commandService: ICommandService, + @IMessageService private _messageService: IMessageService + ) { + this._isEnabled = this._editor.getConfiguration().contribInfo.codeLens; + + this._globalToDispose = []; + this._localToDispose = []; + this._lenses = []; + this._currentFindCodeLensSymbolsPromise = null; + this._modelChangeCounter = 0; + + this._globalToDispose.push(this._editor.onDidChangeModel(() => this._onModelChange())); + this._globalToDispose.push(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); + this._globalToDispose.push(this._editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { + let prevIsEnabled = this._isEnabled; + this._isEnabled = this._editor.getConfiguration().contribInfo.codeLens; + if (prevIsEnabled !== this._isEnabled) { + this._onModelChange(); + } + })); + this._globalToDispose.push(CodeLensProviderRegistry.onDidChange(this._onModelChange, this)); + this._onModelChange(); + } + + dispose(): void { + this._localDispose(); + this._globalToDispose = dispose(this._globalToDispose); + } + + private _localDispose(): void { + if (this._currentFindCodeLensSymbolsPromise) { + this._currentFindCodeLensSymbolsPromise.cancel(); + this._currentFindCodeLensSymbolsPromise = null; + this._modelChangeCounter++; + } + if (this._currentFindOccPromise) { + this._currentFindOccPromise.cancel(); + this._currentFindOccPromise = null; + } + this._localToDispose = dispose(this._localToDispose); + } + + getId(): string { + return CodeLensContribution.ID; + } + + private _onModelChange(): void { + + this._localDispose(); + + const model = this._editor.getModel(); + if (!model) { + return; + } + + if (!this._isEnabled) { + return; + } + + if (!CodeLensProviderRegistry.has(model)) { + return; + } + + for (const provider of CodeLensProviderRegistry.all(model)) { + if (typeof provider.onDidChange === 'function') { + let registration = provider.onDidChange(() => scheduler.schedule()); + this._localToDispose.push(registration); + } + } + + this._detectVisibleLenses = new RunOnceScheduler(() => { + this._onViewportChanged(); + }, 500); + + const scheduler = new RunOnceScheduler(() => { + const counterValue = ++this._modelChangeCounter; + if (this._currentFindCodeLensSymbolsPromise) { + this._currentFindCodeLensSymbolsPromise.cancel(); + } + + this._currentFindCodeLensSymbolsPromise = getCodeLensData(model); + + this._currentFindCodeLensSymbolsPromise.then((result) => { + if (counterValue === this._modelChangeCounter) { // only the last one wins + this._renderCodeLensSymbols(result); + this._detectVisibleLenses.schedule(); + } + }, onUnexpectedError); + }, 250); + this._localToDispose.push(scheduler); + this._localToDispose.push(this._detectVisibleLenses); + this._localToDispose.push(this._editor.onDidChangeModelContent((e) => { + this._editor.changeDecorations((changeAccessor) => { + this._editor.changeViewZones((viewAccessor) => { + const toDispose: CodeLens[] = []; + this._lenses.forEach((lens) => { + if (lens.isValid()) { + lens.update(viewAccessor); + } else { + toDispose.push(lens); + } + }); + + let helper = new CodeLensHelper(); + toDispose.forEach((l) => { + l.dispose(helper, viewAccessor); + this._lenses.splice(this._lenses.indexOf(l), 1); + }); + helper.commit(changeAccessor); + }); + }); + + // Compute new `visible` code lenses + this._detectVisibleLenses.schedule(); + // Ask for all references again + scheduler.schedule(); + })); + this._localToDispose.push(this._editor.onDidScrollChange(e => { + if (e.scrollTopChanged && this._lenses.length > 0) { + this._detectVisibleLenses.schedule(); + } + })); + this._localToDispose.push(this._editor.onDidLayoutChange(e => { + this._detectVisibleLenses.schedule(); + })); + this._localToDispose.push({ + dispose: () => { + if (this._editor.getModel()) { + this._editor.changeDecorations((changeAccessor) => { + this._editor.changeViewZones((accessor) => { + this._disposeAllLenses(changeAccessor, accessor); + }); + }); + } else { + // No accessors available + this._disposeAllLenses(null, null); + } + } + }); + + scheduler.schedule(); + } + + private _disposeAllLenses(decChangeAccessor: editorCommon.IModelDecorationsChangeAccessor, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { + let helper = new CodeLensHelper(); + this._lenses.forEach((lens) => lens.dispose(helper, viewZoneChangeAccessor)); + if (decChangeAccessor) { + helper.commit(decChangeAccessor); + } + this._lenses = []; + } + + private _renderCodeLensSymbols(symbols: ICodeLensData[]): void { + if (!this._editor.getModel()) { + return; + } + + let maxLineNumber = this._editor.getModel().getLineCount(); + let groups: ICodeLensData[][] = []; + let lastGroup: ICodeLensData[]; + + for (let symbol of symbols) { + let line = symbol.symbol.range.startLineNumber; + if (line < 1 || line > maxLineNumber) { + // invalid code lens + continue; + } else if (lastGroup && lastGroup[lastGroup.length - 1].symbol.range.startLineNumber === line) { + // on same line as previous + lastGroup.push(symbol); + } else { + // on later line as previous + lastGroup = [symbol]; + groups.push(lastGroup); + } + } + + const centeredRange = this._editor.getCenteredRangeInViewport(); + const shouldRestoreCenteredRange = centeredRange && (groups.length !== this._lenses.length && this._editor.getScrollTop() !== 0); + this._editor.changeDecorations((changeAccessor) => { + this._editor.changeViewZones((accessor) => { + + let codeLensIndex = 0, groupsIndex = 0, helper = new CodeLensHelper(); + + while (groupsIndex < groups.length && codeLensIndex < this._lenses.length) { + + let symbolsLineNumber = groups[groupsIndex][0].symbol.range.startLineNumber; + let codeLensLineNumber = this._lenses[codeLensIndex].getLineNumber(); + + if (codeLensLineNumber < symbolsLineNumber) { + this._lenses[codeLensIndex].dispose(helper, accessor); + this._lenses.splice(codeLensIndex, 1); + } else if (codeLensLineNumber === symbolsLineNumber) { + this._lenses[codeLensIndex].updateCodeLensSymbols(groups[groupsIndex], helper); + groupsIndex++; + codeLensIndex++; + } else { + this._lenses.splice(codeLensIndex, 0, new CodeLens(groups[groupsIndex], this._editor, helper, accessor, this._commandService, this._messageService, () => this._detectVisibleLenses.schedule())); + codeLensIndex++; + groupsIndex++; + } + } + + // Delete extra code lenses + while (codeLensIndex < this._lenses.length) { + this._lenses[codeLensIndex].dispose(helper, accessor); + this._lenses.splice(codeLensIndex, 1); + } + + // Create extra symbols + while (groupsIndex < groups.length) { + this._lenses.push(new CodeLens(groups[groupsIndex], this._editor, helper, accessor, this._commandService, this._messageService, () => this._detectVisibleLenses.schedule())); + groupsIndex++; + } + + helper.commit(changeAccessor); + }); + }); + if (shouldRestoreCenteredRange) { + this._editor.revealRangeInCenter(centeredRange, editorCommon.ScrollType.Immediate); + } + } + + private _onViewportChanged(): void { + if (this._currentFindOccPromise) { + this._currentFindOccPromise.cancel(); + this._currentFindOccPromise = null; + } + + const model = this._editor.getModel(); + if (!model) { + return; + } + + const toResolve: ICodeLensData[][] = []; + const lenses: CodeLens[] = []; + this._lenses.forEach((lens) => { + const request = lens.computeIfNecessary(model); + if (request) { + toResolve.push(request); + lenses.push(lens); + } + }); + + if (toResolve.length === 0) { + return; + } + + const promises = toResolve.map((request, i) => { + + const resolvedSymbols = new Array(request.length); + const promises = request.map((request, i) => { + return asWinJsPromise((token) => { + return request.provider.resolveCodeLens(model, request.symbol, token); + }).then(symbol => { + resolvedSymbols[i] = symbol; + }); + }); + + return TPromise.join(promises).then(() => { + lenses[i].updateCommands(resolvedSymbols); + }); + }); + + this._currentFindOccPromise = TPromise.join(promises).then(() => { + this._currentFindOccPromise = null; + }); + } +} diff --git a/src/vs/editor/contrib/codelens/browser/codelensWidget.css b/src/vs/editor/contrib/codelens/browser/codelensWidget.css new file mode 100644 index 0000000000..16f2a7acee --- /dev/null +++ b/src/vs/editor/contrib/codelens/browser/codelensWidget.css @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .codelens-decoration { + overflow: hidden; + display: inline-block; + text-overflow: ellipsis; +} + +.monaco-editor .codelens-decoration > span, +.monaco-editor .codelens-decoration > a { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + white-space: nowrap; + vertical-align: sub; +} + +.monaco-editor .codelens-decoration > a { + text-decoration: none; +} + +.monaco-editor .codelens-decoration > a:hover { + text-decoration: underline; + cursor: pointer; +} + +.monaco-editor .codelens-decoration.invisible-cl { + opacity: 0; +} + +@keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } } +@-moz-keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } } +@-o-keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } } +@-webkit-keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } } + +.monaco-editor .codelens-decoration.fadein { + -webkit-animation: fadein 0.5s linear; + -moz-animation: fadein 0.5s linear; + -o-animation: fadein 0.5s linear; + animation: fadein 0.5s linear; +} diff --git a/src/vs/editor/contrib/codelens/browser/codelensWidget.ts b/src/vs/editor/contrib/codelens/browser/codelensWidget.ts new file mode 100644 index 0000000000..69a341345c --- /dev/null +++ b/src/vs/editor/contrib/codelens/browser/codelensWidget.ts @@ -0,0 +1,339 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./codelensWidget'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import Severity from 'vs/base/common/severity'; +import { format, escape } from 'vs/base/common/strings'; +import * as dom from 'vs/base/browser/dom'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICodeLensSymbol, Command } from 'vs/editor/common/modes'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { ICodeLensData } from './codelens'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { editorCodeLensForeground } from 'vs/editor/common/view/editorColorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; + +class CodeLensViewZone implements editorBrowser.IViewZone { + + readonly heightInLines: number; + readonly suppressMouseDown: boolean; + readonly domNode: HTMLElement; + + afterLineNumber: number; + + private _lastHeight: number; + private _onHeight: Function; + + constructor(afterLineNumber: number, onHeight: Function) { + this.afterLineNumber = afterLineNumber; + this._onHeight = onHeight; + + this.heightInLines = 1; + this.suppressMouseDown = true; + this.domNode = document.createElement('div'); + } + + onComputedHeight(height: number): void { + if (this._lastHeight === undefined) { + this._lastHeight = height; + } else if (this._lastHeight !== height) { + this._lastHeight = height; + this._onHeight(); + } + } +} + +class CodeLensContentWidget implements editorBrowser.IContentWidget { + + private static _idPool: number = 0; + + // Editor.IContentWidget.allowEditorOverflow + readonly allowEditorOverflow: boolean = false; + readonly suppressMouseDown: boolean = true; + + private readonly _id: string; + private readonly _domNode: HTMLElement; + private readonly _disposables: IDisposable[] = []; + private readonly _editor: editorBrowser.ICodeEditor; + + private _symbolRange: Range; + private _widgetPosition: editorBrowser.IContentWidgetPosition; + private _commands: { [id: string]: Command } = Object.create(null); + + constructor( + editor: editorBrowser.ICodeEditor, + symbolRange: Range, + commandService: ICommandService, + messageService: IMessageService + ) { + + this._id = 'codeLensWidget' + (++CodeLensContentWidget._idPool); + this._editor = editor; + + this.setSymbolRange(symbolRange); + + this._domNode = document.createElement('span'); + this._domNode.innerHTML = ' '; + dom.addClass(this._domNode, 'codelens-decoration'); + dom.addClass(this._domNode, 'invisible-cl'); + this._updateHeight(); + + this._disposables.push(this._editor.onDidChangeConfiguration(e => e.fontInfo && this._updateHeight())); + + this._disposables.push(dom.addDisposableListener(this._domNode, 'click', e => { + let element = e.target; + if (element.tagName === 'A' && element.id) { + let command = this._commands[element.id]; + if (command) { + editor.focus(); + commandService.executeCommand(command.id, ...command.arguments).done(undefined, err => { + messageService.show(Severity.Error, err); + }); + } + } + })); + + this.updateVisibility(); + } + + dispose(): void { + dispose(this._disposables); + this._symbolRange = null; + } + + private _updateHeight(): void { + const { fontInfo, lineHeight } = this._editor.getConfiguration(); + this._domNode.style.height = `${Math.round(lineHeight * 1.1)}px`; + this._domNode.style.lineHeight = `${lineHeight}px`; + this._domNode.style.fontSize = `${Math.round(fontInfo.fontSize * .9)}px`; + this._domNode.innerHTML = ' '; + } + + updateVisibility(): void { + if (this.isVisible()) { + dom.removeClass(this._domNode, 'invisible-cl'); + dom.addClass(this._domNode, 'fadein'); + } + } + + withCommands(symbols: ICodeLensSymbol[]): void { + this._commands = Object.create(null); + if (!symbols || !symbols.length) { + this._domNode.innerHTML = 'no commands'; + return; + } + + let html: string[] = []; + for (let i = 0; i < symbols.length; i++) { + let command = symbols[i].command; + let title = escape(command.title); + let part: string; + if (command.id) { + part = format('
{1}', i, title); + this._commands[i] = command; + } else { + part = format('{0}', title); + } + html.push(part); + } + + this._domNode.innerHTML = html.join(' | '); + this._editor.layoutContentWidget(this); + } + + getId(): string { + return this._id; + } + + getDomNode(): HTMLElement { + return this._domNode; + } + + setSymbolRange(range: Range): void { + this._symbolRange = range; + + const lineNumber = range.startLineNumber; + const column = this._editor.getModel().getLineFirstNonWhitespaceColumn(lineNumber); + this._widgetPosition = { + position: { lineNumber: lineNumber, column: column }, + preference: [editorBrowser.ContentWidgetPositionPreference.ABOVE] + }; + } + + getPosition(): editorBrowser.IContentWidgetPosition { + return this._widgetPosition; + } + + isVisible(): boolean { + return this._domNode.hasAttribute('monaco-visible-content-widget'); + } +} + +export interface IDecorationIdCallback { + (decorationId: string): void; +} + +export class CodeLensHelper { + + private _removeDecorations: string[]; + private _addDecorations: editorCommon.IModelDeltaDecoration[]; + private _addDecorationsCallbacks: IDecorationIdCallback[]; + + constructor() { + this._removeDecorations = []; + this._addDecorations = []; + this._addDecorationsCallbacks = []; + } + + addDecoration(decoration: editorCommon.IModelDeltaDecoration, callback: IDecorationIdCallback): void { + this._addDecorations.push(decoration); + this._addDecorationsCallbacks.push(callback); + } + + removeDecoration(decorationId: string): void { + this._removeDecorations.push(decorationId); + } + + commit(changeAccessor: editorCommon.IModelDecorationsChangeAccessor): void { + var resultingDecorations = changeAccessor.deltaDecorations(this._removeDecorations, this._addDecorations); + for (let i = 0, len = resultingDecorations.length; i < len; i++) { + this._addDecorationsCallbacks[i](resultingDecorations[i]); + } + } +} + +export class CodeLens { + + private readonly _editor: editorBrowser.ICodeEditor; + private readonly _viewZone: CodeLensViewZone; + private readonly _viewZoneId: number; + private readonly _contentWidget: CodeLensContentWidget; + private _decorationIds: string[]; + private _data: ICodeLensData[]; + + constructor( + data: ICodeLensData[], + editor: editorBrowser.ICodeEditor, + helper: CodeLensHelper, + viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor, + commandService: ICommandService, messageService: IMessageService, + updateCallabck: Function + ) { + this._editor = editor; + this._data = data; + this._decorationIds = new Array(this._data.length); + + let range: Range; + this._data.forEach((codeLensData, i) => { + + helper.addDecoration({ + range: codeLensData.symbol.range, + options: ModelDecorationOptions.EMPTY + }, id => this._decorationIds[i] = id); + + // the range contains all lenses on this line + if (!range) { + range = Range.lift(codeLensData.symbol.range); + } else { + range = Range.plusRange(range, codeLensData.symbol.range); + } + }); + + this._contentWidget = new CodeLensContentWidget(editor, range, commandService, messageService); + this._viewZone = new CodeLensViewZone(range.startLineNumber - 1, updateCallabck); + + this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone); + this._editor.addContentWidget(this._contentWidget); + } + + dispose(helper: CodeLensHelper, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { + while (this._decorationIds.length) { + helper.removeDecoration(this._decorationIds.pop()); + } + if (viewZoneChangeAccessor) { + viewZoneChangeAccessor.removeZone(this._viewZoneId); + } + this._editor.removeContentWidget(this._contentWidget); + + this._contentWidget.dispose(); + } + + isValid(): boolean { + return this._decorationIds.some((id, i) => { + const range = this._editor.getModel().getDecorationRange(id); + const symbol = this._data[i].symbol; + return range && Range.isEmpty(symbol.range) === range.isEmpty(); + }); + } + + updateCodeLensSymbols(data: ICodeLensData[], helper: CodeLensHelper): void { + while (this._decorationIds.length) { + helper.removeDecoration(this._decorationIds.pop()); + } + this._data = data; + this._decorationIds = new Array(this._data.length); + this._data.forEach((codeLensData, i) => { + helper.addDecoration({ + range: codeLensData.symbol.range, + options: ModelDecorationOptions.EMPTY + }, id => this._decorationIds[i] = id); + }); + } + + computeIfNecessary(model: editorCommon.IModel): ICodeLensData[] { + this._contentWidget.updateVisibility(); // trigger the fade in + if (!this._contentWidget.isVisible()) { + return null; + } + + // Read editor current state + for (let i = 0; i < this._decorationIds.length; i++) { + this._data[i].symbol.range = model.getDecorationRange(this._decorationIds[i]); + } + return this._data; + } + + updateCommands(symbols: ICodeLensSymbol[]): void { + this._contentWidget.withCommands(symbols); + } + + getLineNumber(): number { + const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); + if (range) { + return range.startLineNumber; + } + return -1; + } + + update(viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { + if (this.isValid()) { + const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); + + this._viewZone.afterLineNumber = range.startLineNumber - 1; + viewZoneChangeAccessor.layoutZone(this._viewZoneId); + + this._contentWidget.setSymbolRange(range); + this._editor.layoutContentWidget(this._contentWidget); + } + } +} + +registerThemingParticipant((theme, collector) => { + let codeLensForeground = theme.getColor(editorCodeLensForeground); + if (codeLensForeground) { + collector.addRule(`.monaco-editor .codelens-decoration { color: ${codeLensForeground}; }`); + } + let activeLinkForeground = theme.getColor(editorActiveLinkForeground); + if (activeLinkForeground) { + collector.addRule(`.monaco-editor .codelens-decoration > a:hover { color: ${activeLinkForeground} !important; }`); + } +}); diff --git a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts new file mode 100644 index 0000000000..f95ce5f10b --- /dev/null +++ b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts @@ -0,0 +1,242 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RGBA } from 'vs/base/common/color'; +import { hash } from 'vs/base/common/hash'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ICommonCodeEditor, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { ColorProviderRegistry, IColorRange } from 'vs/editor/common/modes'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { getColors } from 'vs/editor/contrib/colorPicker/common/color'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +const MAX_DECORATORS = 500; + +@editorContribution +export class ColorDetector implements IEditorContribution { + + private static ID: string = 'editor.contrib.colorDetector'; + + static RECOMPUTE_TIME = 1000; // ms + + private _globalToDispose: IDisposable[] = []; + private _localToDispose: IDisposable[] = []; + private _computePromise: TPromise; + private _timeoutPromise: TPromise; + + private _decorationsIds: string[] = []; + private _colorRanges = new Map(); + + private _colorDecoratorIds: string[] = []; + private _decorationsTypes: { [key: string]: boolean } = {}; + + private _isEnabled: boolean; + + constructor(private _editor: ICodeEditor, + @ICodeEditorService private _codeEditorService: ICodeEditorService, + @IConfigurationService private _configurationService: IConfigurationService + ) { + this._globalToDispose.push(_editor.onDidChangeModel((e) => { + this._isEnabled = this.isEnabled(); + this.onModelChanged(); + })); + this._globalToDispose.push(_editor.onDidChangeModelLanguage((e) => this.onModelChanged())); + this._globalToDispose.push(ColorProviderRegistry.onDidChange((e) => this.onModelChanged())); + this._globalToDispose.push(_editor.onDidChangeConfiguration((e) => { + let prevIsEnabled = this._isEnabled; + this._isEnabled = this.isEnabled(); + if (prevIsEnabled !== this._isEnabled) { + if (this._isEnabled) { + this.onModelChanged(); + } else { + this.removeAllDecorations(); + } + } + })); + + this._timeoutPromise = null; + this._computePromise = null; + this._isEnabled = this.isEnabled(); + this.onModelChanged(); + } + + isEnabled(): boolean { + const model = this._editor.getModel(); + if (!model) { + return false; + } + const languageId = model.getLanguageIdentifier(); + // handle deprecated settings. [languageId].colorDecorators.enable + let deprecatedConfig = this._configurationService.getConfiguration(languageId.language); + if (deprecatedConfig) { + let colorDecorators = deprecatedConfig['colorDecorators']; // deprecatedConfig.valueOf('.colorDecorators.enable'); + if (colorDecorators && colorDecorators['enable'] !== undefined && !colorDecorators['enable']) { + return colorDecorators['enable']; + } + } + + return this._editor.getConfiguration().contribInfo.colorDecorators; + } + + getId(): string { + return ColorDetector.ID; + } + + static get(editor: ICommonCodeEditor): ColorDetector { + return editor.getContribution(this.ID); + } + + dispose(): void { + this.stop(); + this.removeAllDecorations(); + this._globalToDispose = dispose(this._globalToDispose); + } + + private onModelChanged(): void { + this.stop(); + + if (!this._isEnabled) { + return; + } + const model = this._editor.getModel(); + // if (!model) { + // return; + // } + + if (!ColorProviderRegistry.has(model)) { + return; + } + + this._localToDispose.push(this._editor.onDidChangeModelContent((e) => { + if (!this._timeoutPromise) { + this._timeoutPromise = TPromise.timeout(ColorDetector.RECOMPUTE_TIME); + this._timeoutPromise.then(() => { + this._timeoutPromise = null; + this.beginCompute(); + }); + } + })); + this.beginCompute(); + } + + private beginCompute(): void { + this._computePromise = getColors(this._editor.getModel()).then(colorInfos => { + this.updateDecorations(colorInfos); + this.updateColorDecorators(colorInfos); + this._computePromise = null; + }); + } + + private stop(): void { + if (this._timeoutPromise) { + this._timeoutPromise.cancel(); + this._timeoutPromise = null; + } + if (this._computePromise) { + this._computePromise.cancel(); + this._computePromise = null; + } + this._localToDispose = dispose(this._localToDispose); + } + + private updateDecorations(colorInfos: IColorRange[]): void { + const decorations = colorInfos.map(c => ({ + range: { + startLineNumber: c.range.startLineNumber, + startColumn: c.range.startColumn, + endLineNumber: c.range.endLineNumber, + endColumn: c.range.endColumn + }, + options: {} + })); + + const colorRanges = colorInfos.map(c => ({ + range: c.range, + color: c.color, + formatters: c.formatters + })); + + this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, decorations); + + this._colorRanges = new Map(); + this._decorationsIds.forEach((id, i) => this._colorRanges.set(id, colorRanges[i])); + } + + private updateColorDecorators(colorInfos: IColorRange[]): void { + let decorations = []; + let newDecorationsTypes: { [key: string]: boolean } = {}; + + for (let i = 0; i < colorInfos.length && decorations.length < MAX_DECORATORS; i++) { + const { red, green, blue, alpha } = colorInfos[i].color; + const rgba = new RGBA(Math.round(red * 255), Math.round(green * 255), Math.round(blue * 255), alpha); + let subKey = hash(rgba).toString(16); + let color = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`; + let key = 'colorBox-' + subKey; + + if (!this._decorationsTypes[key] && !newDecorationsTypes[key]) { + this._codeEditorService.registerDecorationType(key, { + before: { + contentText: ' ', + border: 'solid 0.1em #000', + margin: '0.1em 0.2em 0 0.2em', + width: '0.8em', + height: '0.8em', + backgroundColor: color + }, + dark: { + before: { + border: 'solid 0.1em #eee' + } + } + }); + } + + newDecorationsTypes[key] = true; + decorations.push({ + range: { + startLineNumber: colorInfos[i].range.startLineNumber, + startColumn: colorInfos[i].range.startColumn, + endLineNumber: colorInfos[i].range.endLineNumber, + endColumn: colorInfos[i].range.endColumn + }, + options: this._codeEditorService.resolveDecorationOptions(key, true) + }); + } + + for (let subType in this._decorationsTypes) { + if (!newDecorationsTypes[subType]) { + this._codeEditorService.removeDecorationType(subType); + } + } + + this._colorDecoratorIds = this._editor.deltaDecorations(this._colorDecoratorIds, decorations); + } + + private removeAllDecorations(): void { + this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, []); + this._colorDecoratorIds = this._editor.deltaDecorations(this._colorDecoratorIds, []); + + for (let subType in this._decorationsTypes) { + this._codeEditorService.removeDecorationType(subType); + } + } + + getColorRange(position: Position): IColorRange | null { + const decorations = this._editor.getModel() + .getDecorationsInRange(Range.fromPositions(position, position)) + .filter(d => this._colorRanges.has(d.id)); + + if (decorations.length === 0) { + return null; + } + + return this._colorRanges.get(decorations[0].id); + } +} diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPicker.css b/src/vs/editor/contrib/colorPicker/browser/colorPicker.css new file mode 100644 index 0000000000..c81982d807 --- /dev/null +++ b/src/vs/editor/contrib/colorPicker/browser/colorPicker.css @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.colorpicker-widget { + height: 190px; + user-select: none; +} + +.monaco-shell .colorpicker-hover[tabindex="0"]:focus { + outline: none; +} + + +/* Header */ + +.colorpicker-header { + display: flex; + height: 24px; + position: relative; + background: url('images/opacity-background.png'); + background-size: 9px 9px; + image-rendering: pixelated; +} + +.colorpicker-header .picked-color { + width: 216px; + text-align: center; + line-height: 24px; + cursor: pointer; + color: white; + flex: 1; + text-align: center; +} + +.colorpicker-header .picked-color.light { + color: black; +} + +.colorpicker-header .original-color { + width: 74px; + z-index: inherit; + cursor: pointer; +} + + +/* Body */ + +.colorpicker-body { + display: flex; + padding: 8px; + position: relative; +} + +.colorpicker-body .saturation-wrap { + overflow: hidden; + height: 150px; + position: relative; + min-width: 220px; + flex: 1; +} + +.colorpicker-body .saturation-box { + height: 150px; + position: absolute; +} + +.colorpicker-body .saturation-selection { + width: 9px; + height: 9px; + margin: -5px 0 0 -5px; + border: 1px solid rgb(255, 255, 255); + border-radius: 100%; + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.8); + position: absolute; +} + +.colorpicker-body .strip { + width: 25px; + height: 150px; +} + +.colorpicker-body .hue-strip { + position: relative; + margin-left: 8px; + cursor: -webkit-grab; + background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); +} + +.colorpicker-body .opacity-strip { + position: relative; + margin-left: 8px; + cursor: -webkit-grab; + background: url('images/opacity-background.png'); + background-size: 9px 9px; + image-rendering: pixelated; +} + +.colorpicker-body .strip.grabbing { + cursor: -webkit-grabbing; +} + +.colorpicker-body .slider { + position: absolute; + top: 0; + left: -2px; + width: calc(100% + 4px); + height: 4px; + box-sizing: border-box; + border: 1px solid rgba(255, 255, 255, 0.71); + box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.85); +} + +.colorpicker-body .strip .overlay { + height: 150px; + pointer-events: none; +} \ No newline at end of file diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerModel.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerModel.ts new file mode 100644 index 0000000000..20e548c654 --- /dev/null +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerModel.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Event, { Emitter } from 'vs/base/common/event'; +import { Color } from 'vs/base/common/color'; +import { IColorFormatter } from 'vs/editor/common/modes'; + +function canFormat(formatter: IColorFormatter, color: Color): boolean { + return color.isOpaque() || formatter.supportsTransparency; +} + +export class ColorPickerModel { + + readonly originalColor: Color; + private _color: Color; + + get color(): Color { + return this._color; + } + + set color(color: Color) { + if (this._color.equals(color)) { + return; + } + + this._color = color; + this._checkFormat(); + this._onDidChangeColor.fire(color); + } + + get formatter(): IColorFormatter { return this.formatters[this.formatterIndex]; } + + readonly formatters: IColorFormatter[]; + + private _onColorFlushed = new Emitter(); + readonly onColorFlushed: Event = this._onColorFlushed.event; + + private _onDidChangeColor = new Emitter(); + readonly onDidChangeColor: Event = this._onDidChangeColor.event; + + private _onDidChangeFormatter = new Emitter(); + readonly onDidChangeFormatter: Event = this._onDidChangeFormatter.event; + + constructor(color: Color, availableFormatters: IColorFormatter[], private formatterIndex: number) { + if (availableFormatters.length === 0) { + throw new Error('Color picker needs formats'); + } + + if (formatterIndex < 0 || formatterIndex >= availableFormatters.length) { + throw new Error('Formatter index out of bounds'); + } + + this.originalColor = color; + this.formatters = availableFormatters; + this._color = color; + } + + selectNextColorFormat(): void { + const oldFomatterIndex = this.formatterIndex; + this._checkFormat((this.formatterIndex + 1) % this.formatters.length); + if (oldFomatterIndex !== this.formatterIndex) { + this.flushColor(); + } + } + + flushColor(): void { + this._onColorFlushed.fire(this._color); + } + + private _checkFormat(start = this.formatterIndex): void { + let isNewFormat = this.formatterIndex !== start; + this.formatterIndex = start; + + while (!canFormat(this.formatter, this._color)) { + this.formatterIndex = (this.formatterIndex + 1) % this.formatters.length; + + if (this.formatterIndex === start) { + return; + } + } + + if (isNewFormat) { + this._onDidChangeFormatter.fire(this.formatter); + } + } +} diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts new file mode 100644 index 0000000000..b403626a89 --- /dev/null +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts @@ -0,0 +1,361 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./colorPicker'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Widget } from 'vs/base/browser/ui/widget'; +import * as dom from 'vs/base/browser/dom'; +import { onDidChangeZoomLevel } from 'vs/base/browser/browser'; +import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/browser/colorPickerModel'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; +import { Color, RGBA, HSVA } from 'vs/base/common/color'; +import { editorHoverBackground } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; + +const $ = dom.$; + +export class ColorPickerHeader extends Disposable { + + private domNode: HTMLElement; + private pickedColorNode: HTMLElement; + private backgroundColor: Color; + + constructor(container: HTMLElement, private model: ColorPickerModel) { + super(); + + this.domNode = $('.colorpicker-header'); + dom.append(container, this.domNode); + + this.pickedColorNode = dom.append(this.domNode, $('.picked-color')); + + const colorBox = dom.append(this.domNode, $('.original-color')); + colorBox.style.backgroundColor = Color.Format.CSS.format(this.model.originalColor); + + this._register(registerThemingParticipant((theme, collector) => { + this.backgroundColor = theme.getColor(editorHoverBackground) || Color.white; + })); + + this._register(dom.addDisposableListener(this.pickedColorNode, dom.EventType.CLICK, () => this.model.selectNextColorFormat())); + this._register(dom.addDisposableListener(colorBox, dom.EventType.CLICK, () => { + this.model.color = this.model.originalColor; + this.model.flushColor(); + })); + this._register(model.onDidChangeColor(this.onDidChangeColor, this)); + this._register(model.onDidChangeFormatter(this.onDidChangeFormatter, this)); + this.onDidChangeColor(this.model.color); + } + + private onDidChangeColor(color: Color): void { + this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(color); + dom.toggleClass(this.pickedColorNode, 'light', color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : color.isLighter()); + this.onDidChangeFormatter(); + } + + private onDidChangeFormatter(): void { + this.pickedColorNode.textContent = this.model.formatter.format({ + red: this.model.color.rgba.r / 255, + green: this.model.color.rgba.g / 255, + blue: this.model.color.rgba.b / 255, + alpha: this.model.color.rgba.a + }); + } +} + +export class ColorPickerBody extends Disposable { + + private domNode: HTMLElement; + private saturationBox: SaturationBox; + private hueStrip: Strip; + private opacityStrip: Strip; + + constructor(private container: HTMLElement, private model: ColorPickerModel, private pixelRatio: number) { + super(); + + this.domNode = $('.colorpicker-body'); + dom.append(container, this.domNode); + + this.saturationBox = new SaturationBox(this.domNode, this.model, this.pixelRatio); + this._register(this.saturationBox); + this._register(this.saturationBox.onDidChange(this.onDidSaturationValueChange, this)); + this._register(this.saturationBox.onColorFlushed(this.flushColor, this)); + + this.opacityStrip = new OpacityStrip(this.domNode, this.model); + this._register(this.opacityStrip); + this._register(this.opacityStrip.onDidChange(this.onDidOpacityChange, this)); + this._register(this.opacityStrip.onColorFlushed(this.flushColor, this)); + + this.hueStrip = new HueStrip(this.domNode, this.model); + this._register(this.hueStrip); + this._register(this.hueStrip.onDidChange(this.onDidHueChange, this)); + this._register(this.hueStrip.onColorFlushed(this.flushColor, this)); + } + + private flushColor(): void { + this.model.flushColor(); + } + + private onDidSaturationValueChange({ s, v }: { s: number, v: number }): void { + const hsva = this.model.color.hsva; + this.model.color = new Color(new HSVA(hsva.h, s, v, hsva.a)); + } + + private onDidOpacityChange(a: number): void { + const hsva = this.model.color.hsva; + this.model.color = new Color(new HSVA(hsva.h, hsva.s, hsva.v, a)); + } + + private onDidHueChange(value: number): void { + const hsva = this.model.color.hsva; + const h = (1 - value) * 360; + + this.model.color = new Color(new HSVA(h === 360 ? 0 : h, hsva.s, hsva.v, hsva.a)); + } + + layout(): void { + this.saturationBox.layout(); + this.opacityStrip.layout(); + this.hueStrip.layout(); + } +} + +class SaturationBox extends Disposable { + + private domNode: HTMLElement; + private selection: HTMLElement; + private canvas: HTMLCanvasElement; + private width: number; + private height: number; + + private monitor: GlobalMouseMoveMonitor; + private _onDidChange = new Emitter<{ s: number, v: number }>(); + readonly onDidChange: Event<{ s: number, v: number }> = this._onDidChange.event; + + private _onColorFlushed = new Emitter(); + readonly onColorFlushed: Event = this._onColorFlushed.event; + + constructor(container: HTMLElement, private model: ColorPickerModel, private pixelRatio: number) { + super(); + + this.domNode = $('.saturation-wrap'); + dom.append(container, this.domNode); + + // Create canvas, draw selected color + this.canvas = document.createElement('canvas'); + this.canvas.className = 'saturation-box'; + dom.append(this.domNode, this.canvas); + + // Add selection circle + this.selection = $('.saturation-selection'); + dom.append(this.domNode, this.selection); + + this.layout(); + + this._register(dom.addDisposableListener(this.domNode, dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e))); + this._register(this.model.onDidChangeColor(this.onDidChangeColor, this)); + this.monitor = null; + } + + private onMouseDown(e: MouseEvent): void { + this.monitor = this._register(new GlobalMouseMoveMonitor()); + const origin = dom.getDomNodePagePosition(this.domNode); + + if (e.target !== this.selection) { + this.onDidChangePosition(e.offsetX, e.offsetY); + } + + this.monitor.startMonitoring(standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null); + + const mouseUpListener = dom.addDisposableListener(document, dom.EventType.MOUSE_UP, () => { + this._onColorFlushed.fire(); + mouseUpListener.dispose(); + this.monitor.stopMonitoring(true); + this.monitor = null; + }, true); + } + + private onDidChangePosition(left: number, top: number): void { + const s = Math.max(0, Math.min(1, left / this.width)); + const v = Math.max(0, Math.min(1, 1 - (top / this.height))); + + this.paintSelection(s, v); + this._onDidChange.fire({ s, v }); + } + + layout(): void { + this.width = this.domNode.offsetWidth; + this.height = this.domNode.offsetHeight; + this.canvas.width = this.width * this.pixelRatio; + this.canvas.height = this.height * this.pixelRatio; + this.paint(); + + const hsva = this.model.color.hsva; + this.paintSelection(hsva.s, hsva.v); + } + + private paint(): void { + const hsva = this.model.color.hsva; + const saturatedColor = new Color(new HSVA(hsva.h, 1, 1, 1)); + const ctx = this.canvas.getContext('2d'); + + const whiteGradient = ctx.createLinearGradient(0, 0, this.canvas.width, 0); + whiteGradient.addColorStop(0, 'rgba(255, 255, 255, 1)'); + whiteGradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.5)'); + whiteGradient.addColorStop(1, 'rgba(255, 255, 255, 0)'); + + const blackGradient = ctx.createLinearGradient(0, 0, 0, this.canvas.height); + blackGradient.addColorStop(0, 'rgba(0, 0, 0, 0)'); + blackGradient.addColorStop(1, 'rgba(0, 0, 0, 1)'); + + ctx.rect(0, 0, this.canvas.width, this.canvas.height); + ctx.fillStyle = Color.Format.CSS.format(saturatedColor); + ctx.fill(); + ctx.fillStyle = whiteGradient; + ctx.fill(); + ctx.fillStyle = blackGradient; + ctx.fill(); + } + + private paintSelection(s: number, v: number): void { + this.selection.style.left = `${s * this.width}px`; + this.selection.style.top = `${this.height - v * this.height}px`; + } + + private onDidChangeColor(): void { + if (this.monitor && this.monitor.isMonitoring()) { + return; + } + this.paint(); + } +} + +abstract class Strip extends Disposable { + + protected domNode: HTMLElement; + protected overlay: HTMLElement; + protected slider: HTMLElement; + private height: number; + + private _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; + + private _onColorFlushed = new Emitter(); + readonly onColorFlushed: Event = this._onColorFlushed.event; + + constructor(container: HTMLElement, protected model: ColorPickerModel) { + super(); + this.domNode = dom.append(container, $('.strip')); + this.overlay = dom.append(this.domNode, $('.overlay')); + this.slider = dom.append(this.domNode, $('.slider')); + this.slider.style.top = `0px`; + + this._register(dom.addDisposableListener(this.domNode, dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e))); + this.layout(); + } + + layout(): void { + this.height = this.domNode.offsetHeight - this.slider.offsetHeight; + + const value = this.getValue(this.model.color); + this.updateSliderPosition(value); + } + + private onMouseDown(e: MouseEvent): void { + const monitor = this._register(new GlobalMouseMoveMonitor()); + const origin = dom.getDomNodePagePosition(this.domNode); + dom.addClass(this.domNode, 'grabbing'); + + if (e.target !== this.slider) { + this.onDidChangeTop(e.offsetY); + } + + monitor.startMonitoring(standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null); + + const mouseUpListener = dom.addDisposableListener(document, dom.EventType.MOUSE_UP, () => { + this._onColorFlushed.fire(); + mouseUpListener.dispose(); + monitor.stopMonitoring(true); + dom.removeClass(this.domNode, 'grabbing'); + }, true); + } + + private onDidChangeTop(top: number): void { + const value = Math.max(0, Math.min(1, 1 - (top / this.height))); + + this.updateSliderPosition(value); + this._onDidChange.fire(value); + } + + private updateSliderPosition(value: number): void { + this.slider.style.top = `${(1 - value) * this.height}px`; + } + + protected abstract getValue(color: Color): number; +} + +class OpacityStrip extends Strip { + + constructor(container: HTMLElement, model: ColorPickerModel) { + super(container, model); + dom.addClass(this.domNode, 'opacity-strip'); + + this._register(model.onDidChangeColor(this.onDidChangeColor, this)); + this.onDidChangeColor(this.model.color); + } + + private onDidChangeColor(color: Color): void { + const { r, g, b } = color.rgba; + const opaque = new Color(new RGBA(r, g, b, 1)); + const transparent = new Color(new RGBA(r, g, b, 0)); + + this.overlay.style.background = `linear-gradient(to bottom, ${opaque} 0%, ${transparent} 100%)`; + } + + protected getValue(color: Color): number { + return color.hsva.a; + } +} + +class HueStrip extends Strip { + + constructor(container: HTMLElement, model: ColorPickerModel) { + super(container, model); + dom.addClass(this.domNode, 'hue-strip'); + } + + protected getValue(color: Color): number { + return 1 - (color.hsva.h / 360); + } +} + +export class ColorPickerWidget extends Widget { + + private static ID = 'editor.contrib.colorPickerWidget'; + + body: ColorPickerBody; + + constructor(container: Node, private model: ColorPickerModel, private pixelRatio: number) { + super(); + + this._register(onDidChangeZoomLevel(() => this.layout())); + + const element = $('.colorpicker-widget'); + container.appendChild(element); + + const header = new ColorPickerHeader(element, this.model); + this.body = new ColorPickerBody(element, this.model, this.pixelRatio); + + this._register(header); + this._register(this.body); + } + + getId(): string { + return ColorPickerWidget.ID; + } + + layout(): void { + this.body.layout(); + } +} \ No newline at end of file diff --git a/src/vs/editor/contrib/colorPicker/browser/images/opacity-background.png b/src/vs/editor/contrib/colorPicker/browser/images/opacity-background.png new file mode 100644 index 0000000000000000000000000000000000000000..0c18a715a1c03a019ced874f86a6496977035a45 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2{=7>k44ofy`glX(e}Nq6*hWMJ6X z&;2Kn70Bl-@Q5sCVBk9p!i>lBSEK+1B}-f*N`mv#O3D+9QW**oGxJLH@={9_O!N%& z49#3J*M9{nlJ#_P43P*=zOlD@y9&qd9XlEfmofA(PIeH!q07mT{a3Q`?<==6K!psR Lu6{1-oD!M { + const providers = ColorProviderRegistry.ordered(model).reverse(); + const promises = providers.map(p => asWinJsPromise(token => p.provideColorRanges(model, token))); + + return TPromise.join(promises) + .then(ranges => flatten(ranges.filter(r => Array.isArray(r)))); +} diff --git a/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts b/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts new file mode 100644 index 0000000000..c9213c150d --- /dev/null +++ b/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IColorFormatter, IColor } from 'vs/editor/common/modes'; +import { Color, RGBA } from 'vs/base/common/color'; + +function roundFloat(number: number, decimalPoints: number): number { + const decimal = Math.pow(10, decimalPoints); + return Math.round(number * decimal) / decimal; +} + +interface Node { + (color: Color): string; +} + +function createLiteralNode(value: string): Node { + return () => value; +} + +function normalize(value: number, min: number, max: number): number { + return value * (max - min) + min; +} + +function getPropertyValue(color: Color, variable: string): number | undefined { + switch (variable) { + case 'red': + return color.rgba.r / 255; + case 'green': + return color.rgba.g / 255; + case 'blue': + return color.rgba.b / 255; + case 'alpha': + return color.rgba.a; + case 'hue': + return color.hsla.h / 360; + case 'saturation': + return color.hsla.s; + case 'luminance': + return color.hsla.l; + default: + return undefined; + } +} + +function createPropertyNode(variable: string, fractionDigits: number, type: string, min: number | undefined, max: number | undefined): Node { + return color => { + let value = getPropertyValue(color, variable); + + if (value === undefined) { + return ''; + } + + if (type === 'd') { + min = typeof min === 'number' ? min : 0; + max = typeof max === 'number' ? max : 255; + + return (normalize(value, min, max).toFixed(0)).toString(); + } else if (type === 'x' || type === 'X') { + min = typeof min === 'number' ? min : 0; + max = typeof max === 'number' ? max : 255; + + let result = normalize(value, min, max).toString(16); + + if (type === 'X') { + result = result.toUpperCase(); + } + + return result.length < 2 ? `0${result}` : result; + } + + min = typeof min === 'number' ? min : 0; + max = typeof max === 'number' ? max : 1; + return roundFloat(normalize(value, min, max), 2).toString(); + }; +} + +export class ColorFormatter implements IColorFormatter { + + readonly supportsTransparency: boolean = false; + private tree: Node[] = []; + + // Group 0: variable + // Group 1: decimal digits + // Group 2: floating/integer/hex + // Group 3: range begin + // Group 4: range end + private static PATTERN = /{(\w+)(?::(\d*)(\w)+(?:\[(\d+)-(\d+)\])?)?}/g; + + constructor(format: string) { + let match = ColorFormatter.PATTERN.exec(format); + let startIndex = 0; + + // if no match -> erroor throw new Error(`${format} is not consistent with color format syntax.`); + while (match !== null) { + const index = match.index; + + if (startIndex < index) { + this.tree.push(createLiteralNode(format.substring(startIndex, index))); + } + + // add more parser catches + const variable = match[1]; + if (!variable) { + throw new Error(`${variable} is not defined.`); + } + + this.supportsTransparency = this.supportsTransparency || (variable === 'alpha'); + + const decimals = match[2] && parseInt(match[2]); + const type = match[3]; + const startRange = match[4] && parseInt(match[4]); + const endRange = match[5] && parseInt(match[5]); + + this.tree.push(createPropertyNode(variable, decimals, type, startRange, endRange)); + + startIndex = index + match[0].length; + match = ColorFormatter.PATTERN.exec(format); + } + + this.tree.push(createLiteralNode(format.substring(startIndex, format.length))); + } + + format(color: IColor): string { + const richColor = new Color(new RGBA(Math.round(color.red * 255), Math.round(color.green * 255), Math.round(color.blue * 255), color.alpha)); + return this.tree.map(node => node(richColor)).join(''); + } +} + +export class CombinedColorFormatter implements IColorFormatter { + + readonly supportsTransparency: boolean = true; + + constructor(private opaqueFormatter: IColorFormatter, private transparentFormatter: IColorFormatter) { + if (!transparentFormatter.supportsTransparency) { + throw new Error('Invalid transparent formatter'); + } + } + + format(color: IColor): string { + return color.alpha === 1 ? this.opaqueFormatter.format(color) : this.transparentFormatter.format(color); + } +} \ No newline at end of file diff --git a/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts b/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts new file mode 100644 index 0000000000..e365baffc6 --- /dev/null +++ b/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Color, RGBA, HSLA } from 'vs/base/common/color'; +import { IColor } from 'vs/editor/common/modes'; +import { ColorFormatter } from 'vs/editor/contrib/colorPicker/common/colorFormatter'; + +function convert2IColor(color: Color): IColor { + return { + red: color.rgba.r / 255, + green: color.rgba.g / 255, + blue: color.rgba.b / 255, + alpha: color.rgba.a + }; +} +suite('ColorFormatter', () => { + test('empty formatter', () => { + const formatter = new ColorFormatter(''); + assert.equal(formatter.supportsTransparency, false); + + assert.equal(formatter.format(convert2IColor(Color.white)), ''); + assert.equal(formatter.format(convert2IColor(Color.transparent)), ''); + }); + + test('no placeholder', () => { + const formatter = new ColorFormatter('hello'); + assert.equal(formatter.supportsTransparency, false); + + assert.equal(formatter.format(convert2IColor(Color.white)), 'hello'); + assert.equal(formatter.format(convert2IColor(Color.transparent)), 'hello'); + }); + + test('supportsTransparency', () => { + const formatter = new ColorFormatter('hello'); + assert.equal(formatter.supportsTransparency, false); + + const transparentFormatter = new ColorFormatter('{alpha}'); + assert.equal(transparentFormatter.supportsTransparency, true); + }); + + test('default number format is float', () => { + const formatter = new ColorFormatter('{red}'); + assert.equal(formatter.format(convert2IColor(Color.red)), '1'); + }); + + test('default decimal range is [0-255]', () => { + const formatter = new ColorFormatter('{red:d}'); + assert.equal(formatter.format(convert2IColor(Color.red)), '255'); + }); + + test('default hex range is [0-FF]', () => { + const formatter = new ColorFormatter('{red:X}'); + assert.equal(formatter.format(convert2IColor(Color.red)), 'FF'); + }); + + test('documentation', () => { + const color = new Color(new RGBA(255, 127, 0)); + + const rgb = new ColorFormatter('rgb({red:d[0-255]}, {green:d[0-255]}, {blue:d[0-255]})'); + assert.equal(rgb.format(convert2IColor(color)), 'rgb(255, 127, 0)'); + + const rgba = new ColorFormatter('rgba({red:d[0-255]}, {green:d[0-255]}, {blue:d[0-255]}, {alpha})'); + assert.equal(rgba.format(convert2IColor(color)), 'rgba(255, 127, 0, 1)'); + + const hex = new ColorFormatter('#{red:X}{green:X}{blue:X}'); + assert.equal(hex.format(convert2IColor(color)), '#FF7F00'); + + const hsla = new ColorFormatter('hsla({hue:d[0-360]}, {saturation:d[0-100]}%, {luminance:d[0-100]}%, {alpha})'); + assert.equal(hsla.format(convert2IColor(color)), 'hsla(30, 100%, 50%, 1)'); + }); + + test('bug#32323', () => { + const color = new Color(new HSLA(121, 0.45, 0.29, 0.61)); + const rgba = color.rgba; + const color2 = new Color(new RGBA(rgba.r, rgba.g, rgba.b, rgba.a)); + const hsla = new ColorFormatter('hsla({hue:d[0-360]}, {saturation:d[0-100]}%, {luminance:d[0-100]}%, {alpha})'); + assert.equal(hsla.format(convert2IColor(color2)), 'hsla(121, 45%, 29%, 0.61)'); + }); +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts new file mode 100644 index 0000000000..bf779ea670 --- /dev/null +++ b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommentsConfiguration, LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { CharCode } from 'vs/base/common/charCode'; + +export class BlockCommentCommand implements editorCommon.ICommand { + + private _selection: Selection; + private _usedEndToken: string; + + constructor(selection: Selection) { + this._selection = selection; + this._usedEndToken = null; + } + + public static _haystackHasNeedleAtOffset(haystack: string, needle: string, offset: number): boolean { + if (offset < 0) { + return false; + } + var needleLength = needle.length; + var haystackLength = haystack.length; + if (offset + needleLength > haystackLength) { + return false; + } + for (var i = 0; i < needleLength; i++) { + if (haystack.charCodeAt(offset + i) !== needle.charCodeAt(i)) { + return false; + } + } + return true; + } + + private _createOperationsForBlockComment(selection: Range, config: ICommentsConfiguration, model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + var startLineNumber = selection.startLineNumber; + var startColumn = selection.startColumn; + var endLineNumber = selection.endLineNumber; + var endColumn = selection.endColumn; + + var startToken = config.blockCommentStartToken; + var endToken = config.blockCommentEndToken; + + var startTokenIndex = model.getLineContent(startLineNumber).lastIndexOf(startToken, startColumn - 1 + startToken.length); + var endTokenIndex = model.getLineContent(endLineNumber).indexOf(endToken, endColumn - 1 - endToken.length); + + var ops: editorCommon.IIdentifiedSingleEditOperation[]; + + if (startTokenIndex !== -1 && endTokenIndex !== -1) { + var endTokenBeforeCursorIndex = model.getLineContent(startLineNumber).lastIndexOf(endToken, startColumn - 1 + endToken.length); + if (endTokenBeforeCursorIndex > startTokenIndex + startToken.length - 1) { + ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken); + this._usedEndToken = ops.length === 1 ? endToken : null; + } else { + // We have to adjust to possible inner white space + // For Space after startToken, add Space to startToken - range math will work out + if (model.getLineContent(startLineNumber).charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) { + startToken += ' '; + } + // For Space before endToken, add Space before endToken and shift index one left + if (model.getLineContent(endLineNumber).charCodeAt(endTokenIndex - 1) === CharCode.Space) { + endToken = ' ' + endToken; + endTokenIndex -= 1; + } + ops = BlockCommentCommand._createRemoveBlockCommentOperations( + new Range(startLineNumber, startTokenIndex + 1 + startToken.length, endLineNumber, endTokenIndex + 1), startToken, endToken + ); + } + } else { + ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken); + this._usedEndToken = ops.length === 1 ? endToken : null; + } + + for (var i = 0; i < ops.length; i++) { + builder.addTrackedEditOperation(ops[i].range, ops[i].text); + } + } + + public static _createRemoveBlockCommentOperations(r: Range, startToken: string, endToken: string): editorCommon.IIdentifiedSingleEditOperation[] { + var res: editorCommon.IIdentifiedSingleEditOperation[] = []; + + if (!Range.isEmpty(r)) { + // Remove block comment start + res.push(EditOperation.delete(new Range( + r.startLineNumber, r.startColumn - startToken.length, + r.startLineNumber, r.startColumn + ))); + + // Remove block comment end + res.push(EditOperation.delete(new Range( + r.endLineNumber, r.endColumn, + r.endLineNumber, r.endColumn + endToken.length + ))); + } else { + // Remove both continuously + res.push(EditOperation.delete(new Range( + r.startLineNumber, r.startColumn - startToken.length, + r.endLineNumber, r.endColumn + endToken.length + ))); + } + + return res; + } + + public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string): editorCommon.IIdentifiedSingleEditOperation[] { + var res: editorCommon.IIdentifiedSingleEditOperation[] = []; + + if (!Range.isEmpty(r)) { + // Insert block comment start + res.push(EditOperation.insert(new Position(r.startLineNumber, r.startColumn), startToken + ' ')); + + // Insert block comment end + res.push(EditOperation.insert(new Position(r.endLineNumber, r.endColumn), ' ' + endToken)); + } else { + // Insert both continuously + res.push(EditOperation.replace(new Range( + r.startLineNumber, r.startColumn, + r.endLineNumber, r.endColumn + ), startToken + ' ' + endToken)); + } + + return res; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + var startLineNumber = this._selection.startLineNumber; + var startColumn = this._selection.startColumn; + var endLineNumber = this._selection.endLineNumber; + var endColumn = this._selection.endColumn; + + model.tokenizeIfCheap(startLineNumber); + let languageId = model.getLanguageIdAtPosition(startLineNumber, startColumn); + let config = LanguageConfigurationRegistry.getComments(languageId); + if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) { + // Mode does not support block comments + return; + } + + this._createOperationsForBlockComment( + new Range(startLineNumber, startColumn, endLineNumber, endColumn), config, model, builder + ); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + var inverseEditOperations = helper.getInverseEditOperations(); + if (inverseEditOperations.length === 2) { + var startTokenEditOperation = inverseEditOperations[0]; + var endTokenEditOperation = inverseEditOperations[1]; + + return new Selection( + startTokenEditOperation.range.endLineNumber, + startTokenEditOperation.range.endColumn, + endTokenEditOperation.range.startLineNumber, + endTokenEditOperation.range.startColumn + ); + } else { + var srcRange = inverseEditOperations[0].range; + var deltaColumn = this._usedEndToken ? -this._usedEndToken.length - 1 : 0; // minus 1 space before endToken + return new Selection( + srcRange.endLineNumber, + srcRange.endColumn + deltaColumn, + srcRange.endLineNumber, + srcRange.endColumn + deltaColumn + ); + } + } +} diff --git a/src/vs/editor/contrib/comment/common/comment.ts b/src/vs/editor/contrib/comment/common/comment.ts new file mode 100644 index 0000000000..3d9e6ab8a1 --- /dev/null +++ b/src/vs/editor/contrib/comment/common/comment.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { ICommand, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, IActionOptions, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { BlockCommentCommand } from './blockCommentCommand'; +import { LineCommentCommand, Type } from './lineCommentCommand'; + +abstract class CommentLineAction extends EditorAction { + + private _type: Type; + + constructor(type: Type, opts: IActionOptions) { + super(opts); + this._type = type; + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let model = editor.getModel(); + if (!model) { + return; + } + + var commands: ICommand[] = []; + var selections = editor.getSelections(); + var opts = model.getOptions(); + + for (var i = 0; i < selections.length; i++) { + commands.push(new LineCommentCommand(selections[i], opts.tabSize, this._type)); + } + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } + +} + +@editorAction +class ToggleCommentLineAction extends CommentLineAction { + constructor() { + super(Type.Toggle, { + id: 'editor.action.commentLine', + label: nls.localize('comment.line', "Toggle Line Comment"), + alias: 'Toggle Line Comment', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.US_SLASH + } + }); + } +} + +@editorAction +class AddLineCommentAction extends CommentLineAction { + constructor() { + super(Type.ForceAdd, { + id: 'editor.action.addCommentLine', + label: nls.localize('comment.line.add', "Add Line Comment"), + alias: 'Add Line Comment', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C) + } + }); + } +} + +@editorAction +class RemoveLineCommentAction extends CommentLineAction { + constructor() { + super(Type.ForceRemove, { + id: 'editor.action.removeCommentLine', + label: nls.localize('comment.line.remove', "Remove Line Comment"), + alias: 'Remove Line Comment', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U) + } + }); + } +} + +@editorAction +class BlockCommentAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.blockComment', + label: nls.localize('comment.block', "Toggle Block Comment"), + alias: 'Toggle Block Comment', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_A, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_A } + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + var commands: ICommand[] = []; + var selections = editor.getSelections(); + + for (var i = 0; i < selections.length; i++) { + commands.push(new BlockCommentCommand(selections[i])); + } + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } +} diff --git a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts new file mode 100644 index 0000000000..f63b590a0f --- /dev/null +++ b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts @@ -0,0 +1,447 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { BlockCommentCommand } from './blockCommentCommand'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { CharCode } from 'vs/base/common/charCode'; + +export interface IInsertionPoint { + ignore: boolean; + commentStrOffset: number; +} + +export interface ILinePreflightData { + ignore: boolean; + commentStr: string; + commentStrOffset: number; + commentStrLength: number; +} + +export interface IPreflightData { + supported: boolean; + shouldRemoveComments: boolean; + lines: ILinePreflightData[]; +} + +export interface ISimpleModel { + getLineContent(lineNumber: number): string; +} + +export const enum Type { + Toggle = 0, + ForceAdd = 1, + ForceRemove = 2 +} + +export class LineCommentCommand implements editorCommon.ICommand { + + private _selection: Selection; + private _selectionId: string; + private _deltaColumn: number; + private _moveEndPositionDown: boolean; + private _tabSize: number; + private _type: Type; + + constructor(selection: Selection, tabSize: number, type: Type) { + this._selection = selection; + this._tabSize = tabSize; + this._type = type; + this._deltaColumn = 0; + } + + /** + * Do an initial pass over the lines and gather info about the line comment string. + * Returns null if any of the lines doesn't support a line comment string. + */ + public static _gatherPreflightCommentStrings(model: editorCommon.ITokenizedModel, startLineNumber: number, endLineNumber: number): ILinePreflightData[] { + + model.tokenizeIfCheap(startLineNumber); + const languageId = model.getLanguageIdAtPosition(startLineNumber, 1); + + const config = LanguageConfigurationRegistry.getComments(languageId); + const commentStr = (config ? config.lineCommentToken : null); + if (!commentStr) { + // Mode does not support line comments + return null; + } + + let lines: ILinePreflightData[] = []; + for (let i = 0, lineCount = endLineNumber - startLineNumber + 1; i < lineCount; i++) { + lines[i] = { + ignore: false, + commentStr: commentStr, + commentStrOffset: 0, + commentStrLength: commentStr.length + }; + } + + return lines; + } + + /** + * Analyze lines and decide which lines are relevant and what the toggle should do. + * Also, build up several offsets and lengths useful in the generation of editor operations. + */ + public static _analyzeLines(type: Type, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number): IPreflightData { + var lineData: ILinePreflightData, + lineContentStartOffset: number, + commentStrEndOffset: number, + i: number, + lineCount: number, + lineNumber: number, + shouldRemoveComments: boolean, + lineContent: string, + onlyWhitespaceLines = true; + + if (type === Type.Toggle) { + shouldRemoveComments = true; + } else if (type === Type.ForceAdd) { + shouldRemoveComments = false; + } else { + shouldRemoveComments = true; + } + + for (i = 0, lineCount = lines.length; i < lineCount; i++) { + lineData = lines[i]; + lineNumber = startLineNumber + i; + + lineContent = model.getLineContent(lineNumber); + lineContentStartOffset = strings.firstNonWhitespaceIndex(lineContent); + + if (lineContentStartOffset === -1) { + // Empty or whitespace only line + if (type === Type.Toggle) { + lineData.ignore = true; + } else if (type === Type.ForceAdd) { + lineData.ignore = true; + } else { + lineData.ignore = true; + } + lineData.commentStrOffset = lineContent.length; + continue; + } + + onlyWhitespaceLines = false; + lineData.ignore = false; + lineData.commentStrOffset = lineContentStartOffset; + + if (shouldRemoveComments && !BlockCommentCommand._haystackHasNeedleAtOffset(lineContent, lineData.commentStr, lineContentStartOffset)) { + if (type === Type.Toggle) { + // Every line so far has been a line comment, but this one is not + shouldRemoveComments = false; + } else if (type === Type.ForceAdd) { + // Will not happen + } else { + lineData.ignore = true; + } + } + + if (shouldRemoveComments) { + commentStrEndOffset = lineContentStartOffset + lineData.commentStrLength; + if (commentStrEndOffset < lineContent.length && lineContent.charCodeAt(commentStrEndOffset) === CharCode.Space) { + lineData.commentStrLength += 1; + } + } + } + + if (type === Type.Toggle && onlyWhitespaceLines) { + // For only whitespace lines, we insert comments + shouldRemoveComments = false; + + // Also, no longer ignore them + for (i = 0, lineCount = lines.length; i < lineCount; i++) { + lines[i].ignore = false; + } + } + + return { + supported: true, + shouldRemoveComments: shouldRemoveComments, + lines: lines + }; + } + + /** + * Analyze all lines and decide exactly what to do => not supported | insert line comments | remove line comments + */ + public static _gatherPreflightData(type: Type, model: editorCommon.ITokenizedModel, startLineNumber: number, endLineNumber: number): IPreflightData { + var lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber); + if (lines === null) { + return { + supported: false, + shouldRemoveComments: false, + lines: null + }; + } + + return LineCommentCommand._analyzeLines(type, model, lines, startLineNumber); + } + + /** + * Given a successful analysis, execute either insert line comments, either remove line comments + */ + private _executeLineComments(model: ISimpleModel, builder: editorCommon.IEditOperationBuilder, data: IPreflightData, s: Selection): void { + + var ops: editorCommon.IIdentifiedSingleEditOperation[]; + + if (data.shouldRemoveComments) { + ops = LineCommentCommand._createRemoveLineCommentsOperations(data.lines, s.startLineNumber); + } else { + LineCommentCommand._normalizeInsertionPoint(model, data.lines, s.startLineNumber, this._tabSize); + ops = LineCommentCommand._createAddLineCommentsOperations(data.lines, s.startLineNumber); + } + + var cursorPosition = new Position(s.positionLineNumber, s.positionColumn); + + for (var i = 0, len = ops.length; i < len; i++) { + builder.addEditOperation(ops[i].range, ops[i].text); + if (ops[i].range.isEmpty() && ops[i].range.getStartPosition().equals(cursorPosition)) { + this._deltaColumn = ops[i].text.length; + } + } + + this._selectionId = builder.trackSelection(s); + } + + private _attemptRemoveBlockComment(model: editorCommon.ITokenizedModel, s: Selection, startToken: string, endToken: string): editorCommon.IIdentifiedSingleEditOperation[] { + let startLineNumber = s.startLineNumber; + let endLineNumber = s.endLineNumber; + + let startTokenAllowedBeforeColumn = endToken.length + Math.max( + model.getLineFirstNonWhitespaceColumn(s.startLineNumber), + s.startColumn + ); + + let startTokenIndex = model.getLineContent(startLineNumber).lastIndexOf(startToken, startTokenAllowedBeforeColumn - 1); + let endTokenIndex = model.getLineContent(endLineNumber).indexOf(endToken, s.endColumn - 1 - startToken.length); + + if (startTokenIndex !== -1 && endTokenIndex === -1) { + endTokenIndex = model.getLineContent(startLineNumber).indexOf(endToken, startTokenIndex + startToken.length); + endLineNumber = startLineNumber; + } + + if (startTokenIndex === -1 && endTokenIndex !== -1) { + startTokenIndex = model.getLineContent(endLineNumber).lastIndexOf(startToken, endTokenIndex); + startLineNumber = endLineNumber; + } + + if (s.isEmpty() && (startTokenIndex === -1 || endTokenIndex === -1)) { + startTokenIndex = model.getLineContent(startLineNumber).indexOf(startToken); + if (startTokenIndex !== -1) { + endTokenIndex = model.getLineContent(startLineNumber).indexOf(endToken, startTokenIndex + startToken.length); + } + } + + // We have to adjust to possible inner white space. + // For Space after startToken, add Space to startToken - range math will work out. + if (startTokenIndex !== -1 && model.getLineContent(startLineNumber).charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) { + startToken += ' '; + } + + // For Space before endToken, add Space before endToken and shift index one left. + if (endTokenIndex !== -1 && model.getLineContent(endLineNumber).charCodeAt(endTokenIndex - 1) === CharCode.Space) { + endToken = ' ' + endToken; + endTokenIndex -= 1; + } + + if (startTokenIndex !== -1 && endTokenIndex !== -1) { + return BlockCommentCommand._createRemoveBlockCommentOperations( + new Range(startLineNumber, startTokenIndex + startToken.length + 1, endLineNumber, endTokenIndex + 1), startToken, endToken + ); + } + + return null; + } + + /** + * Given an unsuccessful analysis, delegate to the block comment command + */ + private _executeBlockComment(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder, s: Selection): void { + model.tokenizeIfCheap(s.startLineNumber); + let languageId = model.getLanguageIdAtPosition(s.startLineNumber, s.startColumn); + let config = LanguageConfigurationRegistry.getComments(languageId); + if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) { + // Mode does not support block comments + return; + } + + var startToken = config.blockCommentStartToken; + var endToken = config.blockCommentEndToken; + + var ops = this._attemptRemoveBlockComment(model, s, startToken, endToken); + if (!ops) { + if (s.isEmpty()) { + var lineContent = model.getLineContent(s.startLineNumber); + var firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent); + if (firstNonWhitespaceIndex === -1) { + // Line is empty or contains only whitespace + firstNonWhitespaceIndex = lineContent.length; + } + ops = BlockCommentCommand._createAddBlockCommentOperations( + new Range(s.startLineNumber, firstNonWhitespaceIndex + 1, s.startLineNumber, lineContent.length + 1), startToken, endToken + ); + } else { + ops = BlockCommentCommand._createAddBlockCommentOperations( + new Range(s.startLineNumber, model.getLineFirstNonWhitespaceColumn(s.startLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), startToken, endToken + ); + } + + if (ops.length === 1) { + // Leave cursor after token and Space + this._deltaColumn = startToken.length + 1; + } + } + this._selectionId = builder.trackSelection(s); + for (var i = 0; i < ops.length; i++) { + builder.addEditOperation(ops[i].range, ops[i].text); + } + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + + var s = this._selection; + this._moveEndPositionDown = false; + + if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) { + this._moveEndPositionDown = true; + s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1)); + } + + var data = LineCommentCommand._gatherPreflightData(this._type, model, s.startLineNumber, s.endLineNumber); + if (data.supported) { + return this._executeLineComments(model, builder, data, s); + } + + return this._executeBlockComment(model, builder, s); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + var result = helper.getTrackedSelection(this._selectionId); + + if (this._moveEndPositionDown) { + result = result.setEndPosition(result.endLineNumber + 1, 1); + } + + return new Selection( + result.startLineNumber, + result.startColumn + this._deltaColumn, + result.endLineNumber, + result.endColumn + this._deltaColumn + ); + } + + /** + * Generate edit operations in the remove line comment case + */ + public static _createRemoveLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): editorCommon.IIdentifiedSingleEditOperation[] { + var i: number, + len: number, + lineData: ILinePreflightData, + res: editorCommon.IIdentifiedSingleEditOperation[] = []; + + for (i = 0, len = lines.length; i < len; i++) { + lineData = lines[i]; + + if (lineData.ignore) { + continue; + } + + res.push(EditOperation.delete(new Range( + startLineNumber + i, lineData.commentStrOffset + 1, + startLineNumber + i, lineData.commentStrOffset + lineData.commentStrLength + 1 + ))); + } + + return res; + } + + /** + * Generate edit operations in the add line comment case + */ + public static _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): editorCommon.IIdentifiedSingleEditOperation[] { + var i: number, + len: number, + lineData: ILinePreflightData, + res: editorCommon.IIdentifiedSingleEditOperation[] = []; + + for (i = 0, len = lines.length; i < len; i++) { + lineData = lines[i]; + + if (lineData.ignore) { + continue; + } + + res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + ' ')); + } + + return res; + } + + // TODO@Alex -> duplicated in characterHardWrappingLineMapper + private static nextVisibleColumn(currentVisibleColumn: number, tabSize: number, isTab: boolean, columnSize: number): number { + if (isTab) { + return currentVisibleColumn + (tabSize - (currentVisibleColumn % tabSize)); + } + return currentVisibleColumn + columnSize; + } + + /** + * Adjust insertion points to have them vertically aligned in the add line comment case + */ + public static _normalizeInsertionPoint(model: ISimpleModel, lines: IInsertionPoint[], startLineNumber: number, tabSize: number): void { + var minVisibleColumn = Number.MAX_VALUE, + i: number, + len: number, + lineContent: string, + j: number, + lenJ: number, + currentVisibleColumn: number; + + for (i = 0, len = lines.length; i < len; i++) { + if (lines[i].ignore) { + continue; + } + + lineContent = model.getLineContent(startLineNumber + i); + + currentVisibleColumn = 0; + for (j = 0, lenJ = lines[i].commentStrOffset; currentVisibleColumn < minVisibleColumn && j < lenJ; j++) { + currentVisibleColumn = LineCommentCommand.nextVisibleColumn(currentVisibleColumn, tabSize, lineContent.charCodeAt(j) === CharCode.Tab, 1); + } + + if (currentVisibleColumn < minVisibleColumn) { + minVisibleColumn = currentVisibleColumn; + } + } + + minVisibleColumn = Math.floor(minVisibleColumn / tabSize) * tabSize; + + for (i = 0, len = lines.length; i < len; i++) { + if (lines[i].ignore) { + continue; + } + + lineContent = model.getLineContent(startLineNumber + i); + + currentVisibleColumn = 0; + for (j = 0, lenJ = lines[i].commentStrOffset; currentVisibleColumn < minVisibleColumn && j < lenJ; j++) { + currentVisibleColumn = LineCommentCommand.nextVisibleColumn(currentVisibleColumn, tabSize, lineContent.charCodeAt(j) === CharCode.Tab, 1); + } + + if (currentVisibleColumn > minVisibleColumn) { + lines[i].commentStrOffset = j - 1; + } else { + lines[i].commentStrOffset = j; + } + } + } +} diff --git a/src/vs/editor/contrib/comment/test/common/blockCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/common/blockCommentCommand.test.ts new file mode 100644 index 0000000000..40b6d08e54 --- /dev/null +++ b/src/vs/editor/contrib/comment/test/common/blockCommentCommand.test.ts @@ -0,0 +1,460 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Selection } from 'vs/editor/common/core/selection'; +import { BlockCommentCommand } from 'vs/editor/contrib/comment/common/blockCommentCommand'; +import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; +import { CommentMode } from 'vs/editor/test/common/commentMode'; + +function testBlockCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel), expectedLines, expectedSelection); + mode.dispose(); +} + +suite('Editor Contrib - Block Comment Command', () => { + + test('empty selection wraps itself', function () { + testBlockCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 1, 3), + [ + 'fi<0 0>rst', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 6, 1, 6) + ); + }); + + test('invisible selection ignored', function () { + testBlockCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 1, 1, 1), + [ + '<0 first', + ' 0>\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 4, 2, 1) + ); + }); + + test('bug9511', function () { + testBlockCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 6, 1, 1), + [ + '<0 first 0>', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 4, 1, 9) + ); + + testBlockCommentCommand( + [ + '<0first0>', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 8, 1, 3), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 6) + ); + }); + + test('one line selection', function () { + testBlockCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 6, 1, 3), + [ + 'fi<0 rst 0>', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 6, 1, 9) + ); + }); + + test('one line selection toggle', function () { + testBlockCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 6, 1, 3), + [ + 'fi<0 rst 0>', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 6, 1, 9) + ); + + testBlockCommentCommand( + [ + 'fi<0rst0>', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 8, 1, 5), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 1, 6) + ); + + testBlockCommentCommand( + [ + '<0 first 0>', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 10, 1, 1), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 6) + ); + + testBlockCommentCommand( + [ + '<0 first0>', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 9, 1, 1), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 6) + ); + + testBlockCommentCommand( + [ + '<0first 0>', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 9, 1, 1), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 6) + ); + + testBlockCommentCommand( + [ + 'fi<0rst0>', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 8, 1, 5), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 1, 6) + ); + }); + + test('multi line selection', function () { + testBlockCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 4, 1, 1), + [ + '<0 first', + '\tse 0>cond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 4, 2, 4) + ); + }); + + test('multi line selection toggle', function () { + testBlockCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 4, 1, 1), + [ + '<0 first', + '\tse 0>cond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 4, 2, 4) + ); + + testBlockCommentCommand( + [ + '<0first', + '\tse0>cond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 4, 1, 3), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 2, 4) + ); + + testBlockCommentCommand( + [ + '<0 first', + '\tse0>cond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 4, 1, 3), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 2, 4) + ); + + testBlockCommentCommand( + [ + '<0first', + '\tse 0>cond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 4, 1, 3), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 2, 4) + ); + + testBlockCommentCommand( + [ + '<0 first', + '\tse 0>cond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 4, 1, 3), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 2, 4) + ); + }); + + test('fuzzy removes', function () { + testBlockCommentCommand( + [ + 'asd <0 qwe', + 'asd 0> qwe' + ], + new Selection(2, 5, 1, 7), + [ + 'asd qwe', + 'asd qwe' + ], + new Selection(1, 5, 2, 4) + ); + + testBlockCommentCommand( + [ + 'asd <0 qwe', + 'asd 0> qwe' + ], + new Selection(2, 5, 1, 6), + [ + 'asd qwe', + 'asd qwe' + ], + new Selection(1, 5, 2, 4) + ); + + testBlockCommentCommand( + [ + 'asd <0 qwe', + 'asd 0> qwe' + ], + new Selection(2, 5, 1, 5), + [ + 'asd qwe', + 'asd qwe' + ], + new Selection(1, 5, 2, 4) + ); + + testBlockCommentCommand( + [ + 'asd <0 qwe', + 'asd 0> qwe' + ], + new Selection(2, 5, 1, 11), + [ + 'asd qwe', + 'asd qwe' + ], + new Selection(1, 5, 2, 4) + ); + + testBlockCommentCommand( + [ + 'asd <0 qwe', + 'asd 0> qwe' + ], + new Selection(2, 1, 1, 11), + [ + 'asd qwe', + 'asd qwe' + ], + new Selection(1, 5, 2, 4) + ); + + testBlockCommentCommand( + [ + 'asd <0 qwe', + 'asd 0> qwe' + ], + new Selection(2, 7, 1, 11), + [ + 'asd qwe', + 'asd qwe' + ], + new Selection(1, 5, 2, 4) + ); + }); + + test('bug #30358', function () { + testBlockCommentCommand( + [ + '<0 start 0> middle end', + ], + new Selection(1, 20, 1, 23), + [ + '<0 start 0> middle <0 end 0>' + ], + new Selection(1, 23, 1, 26) + ); + + testBlockCommentCommand( + [ + '<0 start 0> middle <0 end 0>' + ], + new Selection(1, 13, 1, 19), + [ + '<0 start 0> <0 middle 0> <0 end 0>' + ], + new Selection(1, 16, 1, 22) + ); + }); +}); diff --git a/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts new file mode 100644 index 0000000000..e877736f08 --- /dev/null +++ b/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts @@ -0,0 +1,940 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ILinePreflightData, IPreflightData, ISimpleModel, LineCommentCommand, Type } from 'vs/editor/contrib/comment/common/lineCommentCommand'; +import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; +import { CommentMode } from 'vs/editor/test/common/commentMode'; +import * as modes from 'vs/editor/common/modes'; +import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { CommentRule } from 'vs/editor/common/modes/languageConfiguration'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; + +suite('Editor Contrib - Line Comment Command', () => { + + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection); + mode.dispose(); + } + + function testAddLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd), expectedLines, expectedSelection); + mode.dispose(); + } + + test('comment single line', function () { + testLineCommentCommand( + [ + 'some text', + '\tsome more text' + ], + new Selection(1, 1, 1, 1), + [ + '!@# some text', + '\tsome more text' + ], + new Selection(1, 9, 1, 9) + ); + }); + + function createSimpleModel(lines: string[]): ISimpleModel { + return { + getLineContent: (lineNumber: number) => { + return lines[lineNumber - 1]; + } + }; + } + + function createBasicLinePreflightData(commentTokens: string[]): ILinePreflightData[] { + return commentTokens.map((commentString) => { + var r: ILinePreflightData = { + ignore: false, + commentStr: commentString, + commentStrOffset: 0, + commentStrLength: commentString.length + }; + return r; + }); + } + + test('_analyzeLines', function () { + var r: IPreflightData; + + r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([ + '\t\t', + ' ', + ' c', + '\t\td' + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1); + + assert.equal(r.shouldRemoveComments, false); + + // Does not change `commentStr` + assert.equal(r.lines[0].commentStr, '//'); + assert.equal(r.lines[1].commentStr, 'rem'); + assert.equal(r.lines[2].commentStr, '!@#'); + assert.equal(r.lines[3].commentStr, '!@#'); + + // Fills in `isWhitespace` + assert.equal(r.lines[0].ignore, true); + assert.equal(r.lines[1].ignore, true); + assert.equal(r.lines[2].ignore, false); + assert.equal(r.lines[3].ignore, false); + + // Fills in `commentStrOffset` + assert.equal(r.lines[0].commentStrOffset, 2); + assert.equal(r.lines[1].commentStrOffset, 4); + assert.equal(r.lines[2].commentStrOffset, 4); + assert.equal(r.lines[3].commentStrOffset, 2); + + + r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([ + '\t\t', + ' rem ', + ' !@# c', + '\t\t!@#d' + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1); + + assert.equal(r.shouldRemoveComments, true); + + // Does not change `commentStr` + assert.equal(r.lines[0].commentStr, '//'); + assert.equal(r.lines[1].commentStr, 'rem'); + assert.equal(r.lines[2].commentStr, '!@#'); + assert.equal(r.lines[3].commentStr, '!@#'); + + // Fills in `isWhitespace` + assert.equal(r.lines[0].ignore, true); + assert.equal(r.lines[1].ignore, false); + assert.equal(r.lines[2].ignore, false); + assert.equal(r.lines[3].ignore, false); + + // Fills in `commentStrOffset` + assert.equal(r.lines[0].commentStrOffset, 2); + assert.equal(r.lines[1].commentStrOffset, 4); + assert.equal(r.lines[2].commentStrOffset, 4); + assert.equal(r.lines[3].commentStrOffset, 2); + + // Fills in `commentStrLength` + assert.equal(r.lines[0].commentStrLength, 2); + assert.equal(r.lines[1].commentStrLength, 4); + assert.equal(r.lines[2].commentStrLength, 4); + assert.equal(r.lines[3].commentStrLength, 3); + }); + + test('_normalizeInsertionPoint', function () { + + var runTest = (mixedArr: any[], tabSize: number, expected: number[], testName: string) => { + var model = createSimpleModel(mixedArr.filter((item, idx) => idx % 2 === 0)); + var offsets = mixedArr.filter((item, idx) => idx % 2 === 1).map(offset => { + return { + commentStrOffset: offset, + ignore: false + }; + }); + LineCommentCommand._normalizeInsertionPoint(model, offsets, 1, tabSize); + var actual = offsets.map(item => item.commentStrOffset); + assert.deepEqual(actual, expected, testName); + }; + + // Bug 16696:[comment] comments not aligned in this case + runTest([ + ' XX', 2, + ' YY', 4 + ], 4, [0, 0], 'Bug 16696'); + + runTest([ + '\t\t\tXX', 3, + ' \tYY', 5, + ' ZZ', 8, + '\t\tTT', 2 + ], 4, [2, 5, 8, 2], 'Test1'); + + runTest([ + '\t\t\t XX', 6, + ' \t\t\t\tYY', 8, + ' ZZ', 8, + '\t\t TT', 6 + ], 4, [2, 5, 8, 2], 'Test2'); + + runTest([ + '\t\t', 2, + '\t\t\t', 3, + '\t\t\t\t', 4, + '\t\t\t', 3 + ], 4, [2, 2, 2, 2], 'Test3'); + + runTest([ + '\t\t', 2, + '\t\t\t', 3, + '\t\t\t\t', 4, + '\t\t\t', 3, + ' ', 4 + ], 2, [2, 2, 2, 2, 4], 'Test4'); + + runTest([ + '\t\t', 2, + '\t\t\t', 3, + '\t\t\t\t', 4, + '\t\t\t', 3, + ' ', 4 + ], 4, [1, 1, 1, 1, 4], 'Test5'); + + runTest([ + ' \t', 2, + ' \t', 3, + ' \t', 4, + ' ', 4, + '\t', 1 + ], 4, [2, 3, 4, 4, 1], 'Test6'); + + runTest([ + ' \t\t', 3, + ' \t\t', 4, + ' \t\t', 5, + ' \t', 5, + '\t', 1 + ], 4, [2, 3, 4, 4, 1], 'Test7'); + + runTest([ + '\t', 1, + ' ', 4 + ], 4, [1, 4], 'Test8:4'); + runTest([ + '\t', 1, + ' ', 3 + ], 4, [0, 0], 'Test8:3'); + runTest([ + '\t', 1, + ' ', 2 + ], 4, [0, 0], 'Test8:2'); + runTest([ + '\t', 1, + ' ', 1 + ], 4, [0, 0], 'Test8:1'); + runTest([ + '\t', 1, + '', 0 + ], 4, [0, 0], 'Test8:0'); + }); + + test('detects indentation', function () { + testLineCommentCommand( + [ + '\tsome text', + '\tsome more text' + ], + new Selection(2, 2, 1, 1), + [ + '\t!@# some text', + '\t!@# some more text' + ], + new Selection(1, 1, 2, 2) + ); + }); + + test('detects mixed indentation', function () { + testLineCommentCommand( + [ + '\tsome text', + ' some more text' + ], + new Selection(2, 2, 1, 1), + [ + '\t!@# some text', + ' !@# some more text' + ], + new Selection(1, 1, 2, 2) + ); + }); + + test('ignores whitespace lines', function () { + testLineCommentCommand( + [ + '\tsome text', + '\t ', + '', + '\tsome more text' + ], + new Selection(4, 2, 1, 1), + [ + '\t!@# some text', + '\t ', + '', + '\t!@# some more text' + ], + new Selection(1, 1, 4, 2) + ); + }); + + test('removes its own', function () { + testLineCommentCommand( + [ + '\t!@# some text', + '\t ', + '\t\t!@# some more text' + ], + new Selection(3, 2, 1, 1), + [ + '\tsome text', + '\t ', + '\t\tsome more text' + ], + new Selection(1, 1, 3, 2) + ); + }); + + test('works in only whitespace', function () { + testLineCommentCommand( + [ + '\t ', + '\t', + '\t\tsome more text' + ], + new Selection(3, 1, 1, 1), + [ + '\t!@# ', + '\t!@# ', + '\t\tsome more text' + ], + new Selection(1, 1, 3, 1) + ); + }); + + test('bug 9697 - whitespace before comment token', function () { + testLineCommentCommand( + [ + '\t !@#first', + '\tsecond line' + ], + new Selection(1, 1, 1, 1), + [ + '\t first', + '\tsecond line' + ], + new Selection(1, 1, 1, 1) + ); + }); + + test('bug 10162 - line comment before caret', function () { + testLineCommentCommand( + [ + 'first!@#', + '\tsecond line' + ], + new Selection(1, 1, 1, 1), + [ + '!@# first!@#', + '\tsecond line' + ], + new Selection(1, 9, 1, 9) + ); + }); + + test('comment single line - leading whitespace', function () { + testLineCommentCommand( + [ + 'first!@#', + '\tsecond line' + ], + new Selection(2, 3, 2, 1), + [ + 'first!@#', + '\t!@# second line' + ], + new Selection(2, 1, 2, 7) + ); + }); + + test('ignores invisible selection', function () { + testLineCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 1, 1, 1), + [ + '!@# first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 9, 2, 5) + ); + }); + + test('multiple lines', function () { + testLineCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 4, 1, 1), + [ + '!@# first', + '!@# \tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 9, 2, 12) + ); + }); + + test('multiple modes on multiple lines', function () { + testLineCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(4, 4, 3, 1), + [ + 'first', + '\tsecond line', + '!@# third line', + '!@# fourth line', + 'fifth' + ], + new Selection(3, 9, 4, 12) + ); + }); + + test('toggle single line', function () { + testLineCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 1), + [ + '!@# first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 9, 1, 9) + ); + + testLineCommentCommand( + [ + '!@# first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 4, 1, 4), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 1) + ); + }); + + test('toggle multiple lines', function () { + testLineCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 4, 1, 1), + [ + '!@# first', + '!@# \tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 9, 2, 12) + ); + + testLineCommentCommand( + [ + '!@# first', + '!@# \tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 7, 1, 4), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 2, 3) + ); + }); + + test('issue #2837 "Add Line Comment" fault when blank lines involved', function () { + testAddLineCommentCommand( + [ + ' if displayName == "":', + ' displayName = groupName', + ' description = getAttr(attributes, "description")', + ' mailAddress = getAttr(attributes, "mail")', + '', + ' print "||Group name|%s|" % displayName', + ' print "||Description|%s|" % description', + ' print "||Email address|[mailto:%s]|" % mailAddress`', + ], + new Selection(1, 1, 8, 56), + [ + ' !@# if displayName == "":', + ' !@# displayName = groupName', + ' !@# description = getAttr(attributes, "description")', + ' !@# mailAddress = getAttr(attributes, "mail")', + '', + ' !@# print "||Group name|%s|" % displayName', + ' !@# print "||Description|%s|" % description', + ' !@# print "||Email address|[mailto:%s]|" % mailAddress`', + ], + new Selection(1, 1, 8, 60) + ); + }); +}); + +suite('Editor Contrib - Line Comment As Block Comment', () => { + + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '', blockComment: ['(', ')'] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection); + mode.dispose(); + } + + test('fall back to block comment command', function () { + testLineCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 1), + [ + '( first )', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 1, 3) + ); + }); + + test('fall back to block comment command - toggle', function () { + testLineCommentCommand( + [ + '(first)', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 7, 1, 2), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 6) + ); + }); + + test('bug 9513 - expand single line to uncomment auto block', function () { + testLineCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 1), + [ + '( first )', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 1, 3) + ); + }); + + test('bug 9691 - always expand selection to line boundaries', function () { + testLineCommentCommand( + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(3, 2, 1, 3), + [ + '( first', + '\tsecond line', + 'third line )', + 'fourth line', + 'fifth' + ], + new Selection(1, 5, 3, 2) + ); + + testLineCommentCommand( + [ + '(first', + '\tsecond line', + 'third line)', + 'fourth line', + 'fifth' + ], + new Selection(3, 11, 1, 2), + [ + 'first', + '\tsecond line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 3, 11) + ); + }); +}); + +suite('Editor Contrib - Line Comment As Block Comment 2', () => { + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: null, blockComment: [''] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection); + mode.dispose(); + } + + test('no selection => uses indentation', function () { + testLineCommentCommand( + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + new Selection(1, 1, 1, 1), + [ + '\t\t', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + new Selection(1, 1, 1, 1) + ); + + testLineCommentCommand( + [ + '\t\t', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + new Selection(1, 1, 1, 1), + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + new Selection(1, 1, 1, 1) + ); + }); + + test('can remove', function () { + testLineCommentCommand( + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + new Selection(5, 1, 5, 1), + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\tfifth\t\t' + ], + new Selection(5, 1, 5, 1) + ); + + testLineCommentCommand( + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + new Selection(5, 3, 5, 3), + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\tfifth\t\t' + ], + new Selection(5, 3, 5, 3) + ); + + testLineCommentCommand( + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + new Selection(5, 4, 5, 4), + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\tfifth\t\t' + ], + new Selection(5, 3, 5, 3) + ); + + testLineCommentCommand( + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + new Selection(5, 16, 5, 3), + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\tfifth\t\t' + ], + new Selection(5, 3, 5, 8) + ); + + testLineCommentCommand( + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + new Selection(5, 12, 5, 7), + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\tfifth\t\t' + ], + new Selection(5, 3, 5, 8) + ); + + testLineCommentCommand( + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + new Selection(5, 18, 5, 18), + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\tfifth\t\t' + ], + new Selection(5, 10, 5, 10) + ); + }); + + test('issue #993: Remove comment does not work consistently in HTML', () => { + testLineCommentCommand( + [ + ' asd qwe', + ' asd qwe', + '' + ], + new Selection(1, 1, 3, 1), + [ + ' ', + '' + ], + new Selection(1, 1, 3, 1) + ); + + testLineCommentCommand( + [ + ' ', + '' + ], + new Selection(1, 1, 3, 1), + [ + ' asd qwe', + ' asd qwe', + '' + ], + new Selection(1, 1, 3, 1) + ); + }); +}); + +suite('Editor Contrib - Line Comment in mixed modes', () => { + + const OUTER_LANGUAGE_ID = new modes.LanguageIdentifier('outerMode', 3); + const INNER_LANGUAGE_ID = new modes.LanguageIdentifier('innerMode', 4); + + class OuterMode extends MockMode { + constructor(commentsConfig: CommentRule) { + super(OUTER_LANGUAGE_ID); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + comments: commentsConfig + })); + + this._register(modes.TokenizationRegistry.register(this.getLanguageIdentifier().language, { + getInitialState: (): modes.IState => NULL_STATE, + tokenize: undefined, + tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { + let languageId = (/^ /.test(line) ? INNER_LANGUAGE_ID : OUTER_LANGUAGE_ID); + + let tokens = new Uint32Array(1 << 1); + tokens[(0 << 1)] = 0; + tokens[(0 << 1) + 1] = ( + (modes.ColorId.DefaultForeground << modes.MetadataConsts.FOREGROUND_OFFSET) + | (languageId.id << modes.MetadataConsts.LANGUAGEID_OFFSET) + ); + return new TokenizationResult2(tokens, state); + } + })); + } + } + + class InnerMode extends MockMode { + constructor(commentsConfig: CommentRule) { + super(INNER_LANGUAGE_ID); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + comments: commentsConfig + })); + } + } + + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let outerMode = new OuterMode({ lineComment: '//', blockComment: ['/*', '*/'] }); + let innerMode = new InnerMode({ lineComment: null, blockComment: ['{/*', '*/}'] }); + testCommand( + lines, + outerMode.getLanguageIdentifier(), + selection, + (sel) => new LineCommentCommand(sel, 4, Type.Toggle), + expectedLines, + expectedSelection + ); + innerMode.dispose(); + outerMode.dispose(); + } + + test('issue #24047 (part 1): Commenting code in JSX files', () => { + testLineCommentCommand( + [ + 'import React from \'react\';', + 'const Loader = () => (', + '
', + ' Loading...', + '
', + ');', + 'export default Loader;' + ], + new Selection(1, 1, 7, 22), + [ + '// import React from \'react\';', + '// const Loader = () => (', + '//
', + '// Loading...', + '//
', + '// );', + '// export default Loader;' + ], + new Selection(1, 4, 7, 25), + ); + }); + + test('issue #24047 (part 2): Commenting code in JSX files', () => { + testLineCommentCommand( + [ + 'import React from \'react\';', + 'const Loader = () => (', + '
', + ' Loading...', + '
', + ');', + 'export default Loader;' + ], + new Selection(3, 4, 3, 4), + [ + 'import React from \'react\';', + 'const Loader = () => (', + ' {/*
*/}', + ' Loading...', + '
', + ');', + 'export default Loader;' + ], + new Selection(3, 8, 3, 8), + ); + }); + +}); diff --git a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts new file mode 100644 index 0000000000..7750aabddf --- /dev/null +++ b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts @@ -0,0 +1,240 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { IAction } from 'vs/base/common/actions'; +import { ResolvedKeybinding, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as dom from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { ICommonCodeEditor, IEditorContribution, IScrollEvent, ScrollType } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; + +export interface IPosition { + x: number; + y: number; +} + +@editorContribution +export class ContextMenuController implements IEditorContribution { + + private static ID = 'editor.contrib.contextmenu'; + + public static get(editor: ICommonCodeEditor): ContextMenuController { + return editor.getContribution(ContextMenuController.ID); + } + + private _toDispose: IDisposable[] = []; + private _contextMenuIsBeingShownCount: number = 0; + private _editor: ICodeEditor; + + constructor( + editor: ICodeEditor, + @IContextMenuService private _contextMenuService: IContextMenuService, + @IContextViewService private _contextViewService: IContextViewService, + @IContextKeyService private _contextKeyService: IContextKeyService, + @IKeybindingService private _keybindingService: IKeybindingService, + @IMenuService private _menuService: IMenuService + ) { + this._editor = editor; + + this._toDispose.push(this._editor.onContextMenu((e: IEditorMouseEvent) => this._onContextMenu(e))); + this._toDispose.push(this._editor.onDidScrollChange((e: IScrollEvent) => { + if (this._contextMenuIsBeingShownCount > 0) { + this._contextViewService.hideContextView(); + } + })); + this._toDispose.push(this._editor.onKeyDown((e: IKeyboardEvent) => { + if (e.keyCode === KeyCode.ContextMenu) { + // Chrome is funny like that + e.preventDefault(); + e.stopPropagation(); + this.showContextMenu(); + } + })); + } + + private _onContextMenu(e: IEditorMouseEvent): void { + if (!this._editor.getConfiguration().contribInfo.contextmenu) { + this._editor.focus(); + // Ensure the cursor is at the position of the mouse click + if (e.target.position && !this._editor.getSelection().containsPosition(e.target.position)) { + this._editor.setPosition(e.target.position); + } + return; // Context menu is turned off through configuration + } + + if (e.target.type === MouseTargetType.OVERLAY_WIDGET) { + return; // allow native menu on widgets to support right click on input field for example in find + } + + e.event.preventDefault(); + + if (e.target.type !== MouseTargetType.CONTENT_TEXT && e.target.type !== MouseTargetType.CONTENT_EMPTY && e.target.type !== MouseTargetType.TEXTAREA) { + return; // only support mouse click into text or native context menu key for now + } + + // Ensure the editor gets focus if it hasn't, so the right events are being sent to other contributions + this._editor.focus(); + + // Ensure the cursor is at the position of the mouse click + if (e.target.position && !this._editor.getSelection().containsPosition(e.target.position)) { + this._editor.setPosition(e.target.position); + } + + // Unless the user triggerd the context menu through Shift+F10, use the mouse position as menu position + var forcedPosition: IPosition; + if (e.target.type !== MouseTargetType.TEXTAREA) { + forcedPosition = { x: e.event.posx, y: e.event.posy + 1 }; + } + + // Show the context menu + this.showContextMenu(forcedPosition); + } + + public showContextMenu(forcedPosition?: IPosition): void { + if (!this._editor.getConfiguration().contribInfo.contextmenu) { + return; // Context menu is turned off through configuration + } + + if (!this._contextMenuService) { + this._editor.focus(); + return; // We need the context menu service to function + } + + // Find actions available for menu + var menuActions = this._getMenuActions(); + + // Show menu if we have actions to show + if (menuActions.length > 0) { + this._doShowContextMenu(menuActions, forcedPosition); + } + } + + private _getMenuActions(): IAction[] { + const result: IAction[] = []; + + let contextMenu = this._menuService.createMenu(MenuId.EditorContext, this._contextKeyService); + const groups = contextMenu.getActions({ arg: this._editor.getModel().uri }); + contextMenu.dispose(); + + for (let group of groups) { + const [, actions] = group; + result.push(...actions); + result.push(new Separator()); + } + result.pop(); // remove last separator + return result; + } + + private _doShowContextMenu(actions: IAction[], forcedPosition: IPosition = null): void { + + // Disable hover + var oldHoverSetting = this._editor.getConfiguration().contribInfo.hover; + this._editor.updateOptions({ + hover: false + }); + + var menuPosition = forcedPosition; + if (!menuPosition) { + // Ensure selection is visible + this._editor.revealPosition(this._editor.getPosition(), ScrollType.Immediate); + + this._editor.render(); + var cursorCoords = this._editor.getScrolledVisiblePosition(this._editor.getPosition()); + + // Translate to absolute editor position + var editorCoords = dom.getDomNodePagePosition(this._editor.getDomNode()); + var posx = editorCoords.left + cursorCoords.left; + var posy = editorCoords.top + cursorCoords.top + cursorCoords.height; + + menuPosition = { x: posx, y: posy }; + } + + // Show menu + this._contextMenuIsBeingShownCount++; + this._contextMenuService.showContextMenu({ + getAnchor: () => menuPosition, + + getActions: () => { + return TPromise.as(actions); + }, + + getActionItem: (action) => { + var keybinding = this._keybindingFor(action); + if (keybinding) { + return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel(), isMenu: true }); + } + + var customActionItem = action; + if (typeof customActionItem.getActionItem === 'function') { + return customActionItem.getActionItem(); + } + + return new ActionItem(action, action, { icon: true, label: true, isMenu: true }); + }, + + getKeyBinding: (action): ResolvedKeybinding => { + return this._keybindingFor(action); + }, + + onHide: (wasCancelled: boolean) => { + this._contextMenuIsBeingShownCount--; + this._editor.focus(); + this._editor.updateOptions({ + hover: oldHoverSetting + }); + } + }); + } + + private _keybindingFor(action: IAction): ResolvedKeybinding { + return this._keybindingService.lookupKeybinding(action.id); + } + + public getId(): string { + return ContextMenuController.ID; + } + + public dispose(): void { + if (this._contextMenuIsBeingShownCount > 0) { + this._contextViewService.hideContextView(); + } + + this._toDispose = dispose(this._toDispose); + } +} + +@editorAction +class ShowContextMenu extends EditorAction { + + constructor() { + super({ + id: 'editor.action.showContextMenu', + label: nls.localize('action.showContextMenu.label', "Show Editor Context Menu"), + alias: 'Show Editor Context Menu', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.F10 + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let contribution = ContextMenuController.get(editor); + contribution.showContextMenu(); + } +} diff --git a/src/vs/editor/contrib/cursorUndo/browser/cursorUndo.ts b/src/vs/editor/contrib/cursorUndo/browser/cursorUndo.ts new file mode 100644 index 0000000000..cd034d4adc --- /dev/null +++ b/src/vs/editor/contrib/cursorUndo/browser/cursorUndo.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Selection } from 'vs/editor/common/core/selection'; +import { editorCommand, ServicesAccessor, EditorCommand } from 'vs/editor/common/editorCommonExtensions'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ICommonCodeEditor, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; + +class CursorState { + readonly selections: Selection[]; + + constructor(selections: Selection[]) { + this.selections = selections; + } + + public equals(other: CursorState): boolean { + const thisLen = this.selections.length; + const otherLen = other.selections.length; + if (thisLen !== otherLen) { + return false; + } + for (let i = 0; i < thisLen; i++) { + if (!this.selections[i].equalsSelection(other.selections[i])) { + return false; + } + } + return true; + } +} + +@editorContribution +export class CursorUndoController extends Disposable implements IEditorContribution { + + private static ID = 'editor.contrib.cursorUndoController'; + + public static get(editor: ICommonCodeEditor): CursorUndoController { + return editor.getContribution(CursorUndoController.ID); + } + + private readonly _editor: ICodeEditor; + private _isCursorUndo: boolean; + + private _undoStack: CursorState[]; + private _prevState: CursorState; + + constructor(editor: ICodeEditor) { + super(); + this._editor = editor; + this._isCursorUndo = false; + + this._undoStack = []; + this._prevState = this._readState(); + + this._register(editor.onDidChangeModel((e) => { + this._undoStack = []; + this._prevState = null; + })); + this._register(editor.onDidChangeModelContent((e) => { + this._undoStack = []; + this._prevState = null; + })); + this._register(editor.onDidChangeCursorSelection((e) => { + + if (!this._isCursorUndo && this._prevState) { + this._undoStack.push(this._prevState); + if (this._undoStack.length > 50) { + // keep the cursor undo stack bounded + this._undoStack = this._undoStack.splice(0, this._undoStack.length - 50); + } + } + + this._prevState = this._readState(); + })); + } + + private _readState(): CursorState { + if (!this._editor.getModel()) { + // no model => no state + return null; + } + + return new CursorState(this._editor.getSelections()); + } + + public getId(): string { + return CursorUndoController.ID; + } + + public cursorUndo(): void { + const currState = new CursorState(this._editor.getSelections()); + + while (this._undoStack.length > 0) { + const prevState = this._undoStack.pop(); + + if (!prevState.equals(currState)) { + this._isCursorUndo = true; + this._editor.setSelections(prevState.selections); + this._isCursorUndo = false; + return; + } + } + } +} + +@editorCommand +export class CursorUndo extends EditorCommand { + constructor() { + super({ + id: 'cursorUndo', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.KEY_U + } + }); + } + + public runEditorCommand(accessor: ServicesAccessor, editor: ICommonCodeEditor, args: any): void { + CursorUndoController.get(editor).cursorUndo(); + } +} diff --git a/src/vs/editor/contrib/dnd/browser/dnd.css b/src/vs/editor/contrib/dnd/browser/dnd.css new file mode 100644 index 0000000000..8ec41c2bbc --- /dev/null +++ b/src/vs/editor/contrib/dnd/browser/dnd.css @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor.vs .dnd-target { + border-right: 2px dotted black; + color: white; /* opposite of black */ +} +.monaco-editor.vs-dark .dnd-target { + border-right: 2px dotted #AEAFAD; + color: #51504f; /* opposite of #AEAFAD */ +} +.monaco-editor.hc-black .dnd-target { + border-right: 2px dotted #fff; + color: #000; /* opposite of #fff */ +} + +.monaco-editor.mouse-default .view-lines, +.monaco-editor.vs-dark.mac.mouse-default .view-lines, +.monaco-editor.hc-black.mac.mouse-default .view-lines { + cursor: default; +} +.monaco-editor.mouse-copy .view-lines, +.monaco-editor.vs-dark.mac.mouse-copy .view-lines, +.monaco-editor.hc-black.mac.mouse-copy .view-lines { + cursor: copy; +} \ No newline at end of file diff --git a/src/vs/editor/contrib/dnd/browser/dnd.ts b/src/vs/editor/contrib/dnd/browser/dnd.ts new file mode 100644 index 0000000000..0168e14a97 --- /dev/null +++ b/src/vs/editor/contrib/dnd/browser/dnd.ts @@ -0,0 +1,210 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./dnd'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { isMacintosh } from 'vs/base/common/platform'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { ICodeEditor, IEditorMouseEvent, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { DragAndDropCommand } from '../common/dragAndDropCommand'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; + +@editorContribution +export class DragAndDropController implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.dragAndDrop'; + + private _editor: ICodeEditor; + private _toUnhook: IDisposable[]; + private _dragSelection: Selection; + private _dndDecorationIds: string[]; + private _mouseDown: boolean; + private _modiferPressed: boolean; + static TRIGGER_MODIFIER = isMacintosh ? 'altKey' : 'ctrlKey'; + static TRIGGER_KEY_VALUE = isMacintosh ? KeyCode.Alt : KeyCode.Ctrl; + + static get(editor: editorCommon.ICommonCodeEditor): DragAndDropController { + return editor.getContribution(DragAndDropController.ID); + } + + constructor(editor: ICodeEditor) { + this._editor = editor; + this._toUnhook = []; + this._toUnhook.push(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); + this._toUnhook.push(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); + this._toUnhook.push(this._editor.onMouseDrag((e: IEditorMouseEvent) => this._onEditorMouseDrag(e))); + this._toUnhook.push(this._editor.onMouseDrop((e: IEditorMouseEvent) => this._onEditorMouseDrop(e))); + this._toUnhook.push(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e))); + this._toUnhook.push(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e))); + this._dndDecorationIds = []; + this._mouseDown = false; + this._modiferPressed = false; + this._dragSelection = null; + } + + private onEditorKeyDown(e: IKeyboardEvent): void { + if (!this._editor.getConfiguration().dragAndDrop) { + return; + } + + if (e[DragAndDropController.TRIGGER_MODIFIER]) { + this._modiferPressed = true; + } + + if (this._mouseDown && e[DragAndDropController.TRIGGER_MODIFIER]) { + this._editor.updateOptions({ + mouseStyle: 'copy' + }); + } + } + + private onEditorKeyUp(e: IKeyboardEvent): void { + if (!this._editor.getConfiguration().dragAndDrop) { + return; + } + + if (e[DragAndDropController.TRIGGER_MODIFIER]) { + this._modiferPressed = false; + } + + if (this._mouseDown && e.keyCode === DragAndDropController.TRIGGER_KEY_VALUE) { + this._editor.updateOptions({ + mouseStyle: 'default' + }); + } + } + + private _onEditorMouseDown(mouseEvent: IEditorMouseEvent): void { + this._mouseDown = true; + } + + private _onEditorMouseUp(mouseEvent: IEditorMouseEvent): void { + this._mouseDown = false; + // Whenever users release the mouse, the drag and drop operation should finish and the cursor should revert to text. + this._editor.updateOptions({ + mouseStyle: 'text' + }); + } + + private _onEditorMouseDrag(mouseEvent: IEditorMouseEvent): void { + let target = mouseEvent.target; + + if (this._dragSelection === null) { + let possibleSelections = this._editor.getSelections().filter(selection => selection.containsPosition(target.position)); + if (possibleSelections.length === 1) { + this._dragSelection = possibleSelections[0]; + } else { + return; + } + } + + if (mouseEvent.event[DragAndDropController.TRIGGER_MODIFIER]) { + this._editor.updateOptions({ + mouseStyle: 'copy' + }); + } else { + this._editor.updateOptions({ + mouseStyle: 'default' + }); + } + + if (this._dragSelection.containsPosition(target.position)) { + this._removeDecoration(); + } else { + this.showAt(target.position); + } + } + + private _onEditorMouseDrop(mouseEvent: IEditorMouseEvent): void { + if (mouseEvent.target && (this._hitContent(mouseEvent.target) || this._hitMargin(mouseEvent.target)) && mouseEvent.target.position) { + let newCursorPosition = new Position(mouseEvent.target.position.lineNumber, mouseEvent.target.position.column); + + if (this._dragSelection === null) { + let newSelections = this._editor.getSelections().map(selection => { + if (selection.containsPosition(newCursorPosition)) { + return new Selection(newCursorPosition.lineNumber, newCursorPosition.column, newCursorPosition.lineNumber, newCursorPosition.column); + } else { + return selection; + } + }); + this._editor.setSelections(newSelections); + } else if (!this._dragSelection.containsPosition(newCursorPosition) || + ( + ( + mouseEvent.event[DragAndDropController.TRIGGER_MODIFIER] || + this._modiferPressed + ) && ( + this._dragSelection.getEndPosition().equals(newCursorPosition) || this._dragSelection.getStartPosition().equals(newCursorPosition) + ) // we allow users to paste content beside the selection + )) { + this._editor.pushUndoStop(); + this._editor.executeCommand(DragAndDropController.ID, new DragAndDropCommand(this._dragSelection, newCursorPosition, mouseEvent.event[DragAndDropController.TRIGGER_MODIFIER] || this._modiferPressed)); + this._editor.pushUndoStop(); + } + } + + this._editor.updateOptions({ + mouseStyle: 'text' + }); + + this._removeDecoration(); + this._dragSelection = null; + this._mouseDown = false; + } + + private static _DECORATION_OPTIONS = ModelDecorationOptions.register({ + className: 'dnd-target' + }); + + public showAt(position: Position): void { + this._editor.changeDecorations(changeAccessor => { + let newDecorations: editorCommon.IModelDeltaDecoration[] = []; + newDecorations.push({ + range: new Range(position.lineNumber, position.column, position.lineNumber, position.column), + options: DragAndDropController._DECORATION_OPTIONS + }); + + this._dndDecorationIds = changeAccessor.deltaDecorations(this._dndDecorationIds, newDecorations); + }); + this._editor.revealPosition(position, editorCommon.ScrollType.Immediate); + } + + private _removeDecoration(): void { + this._editor.changeDecorations(changeAccessor => { + changeAccessor.deltaDecorations(this._dndDecorationIds, []); + }); + } + + private _hitContent(target: IMouseTarget): boolean { + return target.type === MouseTargetType.CONTENT_TEXT || + target.type === MouseTargetType.CONTENT_EMPTY; + } + + private _hitMargin(target: IMouseTarget): boolean { + return target.type === MouseTargetType.GUTTER_GLYPH_MARGIN || + target.type === MouseTargetType.GUTTER_LINE_NUMBERS || + target.type === MouseTargetType.GUTTER_LINE_DECORATIONS; + } + + public getId(): string { + return DragAndDropController.ID; + } + + public dispose(): void { + this._removeDecoration(); + this._dragSelection = null; + this._mouseDown = false; + this._modiferPressed = false; + this._toUnhook = dispose(this._toUnhook); + } +} \ No newline at end of file diff --git a/src/vs/editor/contrib/dnd/common/dragAndDropCommand.ts b/src/vs/editor/contrib/dnd/common/dragAndDropCommand.ts new file mode 100644 index 0000000000..2deadd0b2f --- /dev/null +++ b/src/vs/editor/contrib/dnd/common/dragAndDropCommand.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; + + +export class DragAndDropCommand implements editorCommon.ICommand { + + private selection: Selection; + private targetPosition: Position; + private targetSelection: Selection; + private copy: boolean; + + constructor(selection: Selection, targetPosition: Position, copy: boolean) { + this.selection = selection; + this.targetPosition = targetPosition; + this.copy = copy; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + let text = model.getValueInRange(this.selection); + if (!this.copy) { + builder.addEditOperation(this.selection, null); + } + builder.addEditOperation(new Range(this.targetPosition.lineNumber, this.targetPosition.column, this.targetPosition.lineNumber, this.targetPosition.column), text); + + if (this.selection.containsPosition(this.targetPosition) && !( + this.copy && ( + this.selection.getEndPosition().equals(this.targetPosition) || this.selection.getStartPosition().equals(this.targetPosition) + ) // we allow users to paste content beside the selection + )) { + this.targetSelection = this.selection; + return; + } + + if (this.copy) { + this.targetSelection = new Selection( + this.targetPosition.lineNumber, + this.targetPosition.column, + this.selection.endLineNumber - this.selection.startLineNumber + this.targetPosition.lineNumber, + this.selection.startLineNumber === this.selection.endLineNumber ? + this.targetPosition.column + this.selection.endColumn - this.selection.startColumn : + this.selection.endColumn + ); + return; + } + + if (this.targetPosition.lineNumber > this.selection.endLineNumber) { + // Drag the selection downwards + this.targetSelection = new Selection( + this.targetPosition.lineNumber - this.selection.endLineNumber + this.selection.startLineNumber, + this.targetPosition.column, + this.targetPosition.lineNumber, + this.selection.startLineNumber === this.selection.endLineNumber ? + this.targetPosition.column + this.selection.endColumn - this.selection.startColumn : + this.selection.endColumn + ); + return; + } + + if (this.targetPosition.lineNumber < this.selection.endLineNumber) { + // Drag the selection upwards + this.targetSelection = new Selection( + this.targetPosition.lineNumber, + this.targetPosition.column, + this.targetPosition.lineNumber + this.selection.endLineNumber - this.selection.startLineNumber, + this.selection.startLineNumber === this.selection.endLineNumber ? + this.targetPosition.column + this.selection.endColumn - this.selection.startColumn : + this.selection.endColumn + ); + return; + } + + // The target position is at the same line as the selection's end position. + if (this.selection.endColumn <= this.targetPosition.column) { + // The target position is after the selection's end position + this.targetSelection = new Selection( + this.targetPosition.lineNumber - this.selection.endLineNumber + this.selection.startLineNumber, + this.selection.startLineNumber === this.selection.endLineNumber ? + this.targetPosition.column - this.selection.endColumn + this.selection.startColumn : + this.targetPosition.column - this.selection.endColumn + this.selection.startColumn, + this.targetPosition.lineNumber, + this.selection.startLineNumber === this.selection.endLineNumber ? + this.targetPosition.column : + this.selection.endColumn + ); + } else { + // The target position is before the selection's end postion. Since the selection doesn't contain the target position, the selection is one-line and target position is before this selection. + this.targetSelection = new Selection( + this.targetPosition.lineNumber - this.selection.endLineNumber + this.selection.startLineNumber, + this.targetPosition.column, + this.targetPosition.lineNumber, + this.targetPosition.column + this.selection.endColumn - this.selection.startColumn + ); + } + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + return this.targetSelection; + } +} \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/find.ts b/src/vs/editor/contrib/find/browser/find.ts new file mode 100644 index 0000000000..874d74327a --- /dev/null +++ b/src/vs/editor/contrib/find/browser/find.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { FindWidget, IFindController } from 'vs/editor/contrib/find/browser/findWidget'; +import { FindOptionsWidget } from 'vs/editor/contrib/find/browser/findOptionsWidget'; +import { CommonFindController, FindStartFocusAction, IFindStartOptions } from 'vs/editor/contrib/find/common/findController'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; + +@editorContribution +export class FindController extends CommonFindController implements IFindController { + + private _widget: FindWidget; + private _findOptionsWidget: FindOptionsWidget; + + constructor( + editor: ICodeEditor, + @IContextViewService contextViewService: IContextViewService, + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService keybindingService: IKeybindingService, + @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService + ) { + super(editor, contextKeyService, storageService); + + this._widget = this._register(new FindWidget(editor, this, this._state, contextViewService, keybindingService, contextKeyService, themeService)); + this._findOptionsWidget = this._register(new FindOptionsWidget(editor, this._state, keybindingService, themeService)); + } + + protected _start(opts: IFindStartOptions): void { + super._start(opts); + + if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) { + this._widget.focusReplaceInput(); + } else if (opts.shouldFocus === FindStartFocusAction.FocusFindInput) { + this._widget.focusFindInput(); + } + } + + public highlightFindOptions(): void { + if (this._state.isRevealed) { + this._widget.highlightFindOptions(); + } else { + this._findOptionsWidget.highlightFindOptions(); + } + } +} diff --git a/src/vs/editor/contrib/find/browser/findOptionsWidget.ts b/src/vs/editor/contrib/find/browser/findOptionsWidget.ts new file mode 100644 index 0000000000..214ce8b937 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/findOptionsWidget.ts @@ -0,0 +1,207 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as dom from 'vs/base/browser/dom'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { FIND_IDS } from 'vs/editor/contrib/find/common/findModel'; +import { FindReplaceState } from 'vs/editor/contrib/find/common/findState'; +import { CaseSensitiveCheckbox, WholeWordsCheckbox, RegexCheckbox } from 'vs/base/browser/ui/findinput/findInputCheckboxes'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { inputActiveOptionBorder, editorWidgetBackground, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; + +export class FindOptionsWidget extends Widget implements IOverlayWidget { + + private static ID = 'editor.contrib.findOptionsWidget'; + + private _editor: ICodeEditor; + private _state: FindReplaceState; + private _keybindingService: IKeybindingService; + + private _domNode: HTMLElement; + private regex: RegexCheckbox; + private wholeWords: WholeWordsCheckbox; + private caseSensitive: CaseSensitiveCheckbox; + + constructor( + editor: ICodeEditor, + state: FindReplaceState, + keybindingService: IKeybindingService, + themeService: IThemeService + ) { + super(); + + this._editor = editor; + this._state = state; + this._keybindingService = keybindingService; + + this._domNode = document.createElement('div'); + this._domNode.className = 'findOptionsWidget'; + this._domNode.style.display = 'none'; + this._domNode.style.top = '10px'; + this._domNode.setAttribute('role', 'presentation'); + this._domNode.setAttribute('aria-hidden', 'true'); + + let inputActiveOptionBorderColor = themeService.getTheme().getColor(inputActiveOptionBorder); + + this.caseSensitive = this._register(new CaseSensitiveCheckbox({ + appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand), + isChecked: this._state.matchCase, + onChange: (viaKeyboard) => { + this._state.change({ + matchCase: this.caseSensitive.checked + }, false); + }, + inputActiveOptionBorder: inputActiveOptionBorderColor + })); + this._domNode.appendChild(this.caseSensitive.domNode); + + this.wholeWords = this._register(new WholeWordsCheckbox({ + appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleWholeWordCommand), + isChecked: this._state.wholeWord, + onChange: (viaKeyboard) => { + this._state.change({ + wholeWord: this.wholeWords.checked + }, false); + }, + inputActiveOptionBorder: inputActiveOptionBorderColor + })); + this._domNode.appendChild(this.wholeWords.domNode); + + this.regex = this._register(new RegexCheckbox({ + appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleRegexCommand), + isChecked: this._state.isRegex, + onChange: (viaKeyboard) => { + this._state.change({ + isRegex: this.regex.checked + }, false); + }, + inputActiveOptionBorder: inputActiveOptionBorderColor + })); + this._domNode.appendChild(this.regex.domNode); + + this._editor.addOverlayWidget(this); + + this._register(this._state.addChangeListener((e) => { + let somethingChanged = false; + if (e.isRegex) { + this.regex.checked = this._state.isRegex; + somethingChanged = true; + } + if (e.wholeWord) { + this.wholeWords.checked = this._state.wholeWord; + somethingChanged = true; + } + if (e.matchCase) { + this.caseSensitive.checked = this._state.matchCase; + somethingChanged = true; + } + if (!this._state.isRevealed && somethingChanged) { + this._revealTemporarily(); + } + })); + + this._register(dom.addDisposableNonBubblingMouseOutListener(this._domNode, (e) => this._onMouseOut())); + this._register(dom.addDisposableListener(this._domNode, 'mouseover', (e) => this._onMouseOver())); + + this._applyTheme(themeService.getTheme()); + this._register(themeService.onThemeChange(this._applyTheme.bind(this))); + } + + private _keybindingLabelFor(actionId: string): string { + let kb = this._keybindingService.lookupKeybinding(actionId); + if (!kb) { + return ''; + } + return ` (${kb.getLabel()})`; + } + + public dispose(): void { + this._editor.removeOverlayWidget(this); + super.dispose(); + } + + // ----- IOverlayWidget API + + public getId(): string { + return FindOptionsWidget.ID; + } + + public getDomNode(): HTMLElement { + return this._domNode; + } + + public getPosition(): IOverlayWidgetPosition { + return { + preference: OverlayWidgetPositionPreference.TOP_RIGHT_CORNER + }; + } + + public highlightFindOptions(): void { + this._revealTemporarily(); + } + + private _hideSoon = this._register(new RunOnceScheduler(() => this._hide(), 1000)); + + private _revealTemporarily(): void { + this._show(); + this._hideSoon.schedule(); + } + + private _onMouseOut(): void { + this._hideSoon.schedule(); + } + + private _onMouseOver(): void { + this._hideSoon.cancel(); + } + + private _isVisible: boolean = false; + + private _show(): void { + if (this._isVisible) { + return; + } + this._isVisible = true; + this._domNode.style.display = 'block'; + } + + private _hide(): void { + if (!this._isVisible) { + return; + } + this._isVisible = false; + this._domNode.style.display = 'none'; + } + + private _applyTheme(theme: ITheme) { + let inputStyles = { inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder) }; + this.caseSensitive.style(inputStyles); + this.wholeWords.style(inputStyles); + this.regex.style(inputStyles); + } +} + + +registerThemingParticipant((theme, collector) => { + let widgetBackground = theme.getColor(editorWidgetBackground); + if (widgetBackground) { + collector.addRule(`.monaco-editor .findOptionsWidget { background-color: ${widgetBackground}; }`); + } + + let widgetShadowColor = theme.getColor(widgetShadow); + if (widgetShadowColor) { + collector.addRule(`.monaco-editor .findOptionsWidget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + } + + let hcBorder = theme.getColor(contrastBorder); + if (hcBorder) { + collector.addRule(`.monaco-editor .findOptionsWidget { border: 2px solid ${hcBorder}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/findWidget.css b/src/vs/editor/contrib/find/browser/findWidget.css new file mode 100644 index 0000000000..0e0271f89a --- /dev/null +++ b/src/vs/editor/contrib/find/browser/findWidget.css @@ -0,0 +1,356 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Checkbox */ + +.monaco-checkbox .label { + width: 12px; + height: 12px; + border: 1px solid black; + background-color: transparent; + display: inline-block; +} + +.monaco-checkbox .checkbox { + position: absolute; + overflow: hidden; + clip: rect(0 0 0 0); + height: 1px; + width: 1px; + margin: -1px; + padding: 0; + border: 0; +} + +.monaco-checkbox .checkbox:checked + .label { + background-color: black; +} + +/* Find widget */ +.monaco-editor .find-widget { + position: absolute; + z-index: 10; + top: -44px; /* find input height + shadow (10px) */ + height: 34px; /* find input height */ + overflow: hidden; + line-height: 19px; + + -webkit-transition: top 200ms linear; + -o-transition: top 200ms linear; + -moz-transition: top 200ms linear; + -ms-transition: top 200ms linear; + transition: top 200ms linear; + + padding: 0 4px; +} +/* Find widget when replace is toggled on */ +.monaco-editor .find-widget.replaceToggled { + top: -74px; /* find input height + replace input height + shadow (10px) */ + height: 64px; /* find input height + replace input height */ +} +.monaco-editor .find-widget.replaceToggled > .replace-part { + display: flex; + display: -webkit-flex; + align-items: center; +} + +.monaco-editor .find-widget.visible, +.monaco-editor .find-widget.replaceToggled.visible { + top: 0; +} + +.monaco-editor .find-widget .monaco-inputbox .input { + background-color: transparent; + /* Style to compensate for //winjs */ + min-height: 0; +} + +.monaco-editor .find-widget .replace-input .input { + font-size: 13px; +} + +.monaco-editor .find-widget.visible.noanimation { + -webkit-transition: none; + -o-transition: none; + -moz-transition: none; + -ms-transition: none; + transition: none; +} + +.monaco-editor .find-widget > .find-part, +.monaco-editor .find-widget > .replace-part { + margin: 4px 0 0 17px; + font-size: 12px; + display: flex; + display: -webkit-flex; + align-items: center; +} + +.monaco-editor .find-widget > .find-part .monaco-inputbox, +.monaco-editor .find-widget > .replace-part .monaco-inputbox { + height: 25px; +} + +.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input { + width: 100% !important; + padding-right: 66px; +} +.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input, +.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input { + padding-top: 2px; + padding-bottom: 2px; +} + +.monaco-editor .find-widget .monaco-findInput { + vertical-align: middle; + display: flex; + display: -webkit-flex; + flex:1; +} + +.monaco-editor .find-widget .matchesCount { + display: flex; + display: -webkit-flex; + flex: initial; + margin: 0 1px 0 3px; + padding: 2px 2px 0 2px; + height: 25px; + vertical-align: middle; + box-sizing: border-box; + text-align: center; + line-height: 23px; +} + +.monaco-editor .find-widget .button { + min-width: 20px; + width: 20px; + height: 20px; + display: flex; + display: -webkit-flex; + flex: initial; + margin-left: 3px; + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; +} + +.monaco-editor .find-widget .button:not(.disabled):hover { + background-color: rgba(0, 0, 0, 0.1); +} + +.monaco-editor .find-widget .button.left { + margin-left: 0; + margin-right: 3px; +} + +.monaco-editor .find-widget .button.wide { + width: auto; + padding: 1px 6px; + top: -1px; +} + +.monaco-editor .find-widget .button.toggle { + position: absolute; + top: 0; + left: 0; + width: 18px; + height: 100%; + -webkit-box-sizing: border-box; + -o-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; +} + +.monaco-editor .find-widget .button.toggle.disabled { + display: none; +} + +.monaco-editor .find-widget .previous { + background-image: url('images/previous.svg'); +} + +.monaco-editor .find-widget .next { + background-image: url('images/next.svg'); +} + +.monaco-editor .find-widget .disabled { + opacity: 0.3; + cursor: default; +} + +.monaco-editor .find-widget .monaco-checkbox { + width: 20px; + height: 20px; + display: inline-block; + vertical-align: middle; + margin-left: 3px; +} + +.monaco-editor .find-widget .monaco-checkbox .label { + content: ''; + display: inline-block; + background-repeat: no-repeat; + background-position: 0 0; + background-image: url('images/cancelSelectionFind.svg'); + width: 20px; + height: 20px; + border: none; +} + +.monaco-editor .find-widget .monaco-checkbox .checkbox:disabled + .label { + opacity: 0.3; + cursor: default; +} + +.monaco-editor .find-widget .monaco-checkbox .checkbox:not(:disabled) + .label { + cursor: pointer; +} + +.monaco-editor .find-widget .monaco-checkbox .checkbox:not(:disabled):hover:before + .label { + background-color: #DDD; +} + +.monaco-editor .find-widget .monaco-checkbox .checkbox:checked + .label { + background-color: rgba(100, 100, 100, 0.2); +} + +.monaco-editor .find-widget .close-fw { + background-image: url('images/close.svg'); +} + +.monaco-editor .find-widget .expand { + background-image: url('images/expando-expanded.svg'); +} + +.monaco-editor .find-widget .collapse { + background-image: url('images/expando-collapsed.svg'); +} + +.monaco-editor .find-widget .replace { + background-image: url('images/replace.svg'); +} + +.monaco-editor .find-widget .replace-all { + background-image: url('images/replace-all.svg'); +} + +.monaco-editor .find-widget > .replace-part { + display: none; +} + +.monaco-editor .find-widget > .replace-part > .replace-input { + display: flex; + display: -webkit-flex; + vertical-align: middle; + width: auto !important; +} + +/* REDUCED */ +.monaco-editor .find-widget.reduced-find-widget .matchesCount, +.monaco-editor .find-widget.reduced-find-widget .monaco-checkbox { + display:none; +} + +/* NARROW (SMALLER THAN REDUCED) */ +.monaco-editor .find-widget.narrow-find-widget { + max-width: 257px !important; +} + +/* COLLAPSED (SMALLER THAN NARROW) */ +.monaco-editor .find-widget.collapsed-find-widget { + max-width: 111px !important; +} + +.monaco-editor .find-widget.collapsed-find-widget .button.previous, +.monaco-editor .find-widget.collapsed-find-widget .button.next, +.monaco-editor .find-widget.collapsed-find-widget .button.replace, +.monaco-editor .find-widget.collapsed-find-widget .button.replace-all, +.monaco-editor .find-widget.collapsed-find-widget > .find-part .monaco-findInput .controls { + display:none; +} + +.monaco-editor .find-widget.collapsed-find-widget > .find-part .monaco-inputbox > .wrapper > .input { + padding-right: 0px; +} + +.monaco-editor .findMatch { + -webkit-animation-duration: 0; + -webkit-animation-name: inherit !important; + -moz-animation-duration: 0; + -moz-animation-name: inherit !important; + -ms-animation-duration: 0; + -ms-animation-name: inherit !important; + animation-duration: 0; + animation-name: inherit !important; +} + +.monaco-editor .find-widget .monaco-sash { + width: 2px !important; + margin-left: -4px; +} + +.monaco-editor.hc-black .find-widget .previous, +.monaco-editor.vs-dark .find-widget .previous { + background-image: url('images/previous-inverse.svg'); +} + +.monaco-editor.hc-black .find-widget .next, +.monaco-editor.vs-dark .find-widget .next { + background-image: url('images/next-inverse.svg'); +} + +.monaco-editor.hc-black .find-widget .monaco-checkbox .label, +.monaco-editor.vs-dark .find-widget .monaco-checkbox .label { + background-image: url('images/cancelSelectionFind-inverse.svg'); +} + +.monaco-editor.vs-dark .find-widget .monaco-checkbox .checkbox:not(:disabled):hover:before + .label { + background-color: rgba(255, 255, 255, 0.1); +} + +.monaco-editor.vs-dark .find-widget .monaco-checkbox .checkbox:checked + .label { + background-color: rgba(255, 255, 255, 0.1); +} + +.monaco-editor.hc-black .find-widget .close-fw, +.monaco-editor.vs-dark .find-widget .close-fw { + background-image: url('images/close-dark.svg'); +} + +.monaco-editor.hc-black .find-widget .replace, +.monaco-editor.vs-dark .find-widget .replace { + background-image: url('images/replace-inverse.svg'); +} + +.monaco-editor.hc-black .find-widget .replace-all, +.monaco-editor.vs-dark .find-widget .replace-all { + background-image: url('images/replace-all-inverse.svg'); +} + +.monaco-editor.hc-black .find-widget .expand, +.monaco-editor.vs-dark .find-widget .expand { + background-image: url('images/expando-expanded-dark.svg'); +} + +.monaco-editor.hc-black .find-widget .collapse, +.monaco-editor.vs-dark .find-widget .collapse { + background-image: url('images/expando-collapsed-dark.svg'); +} + +.monaco-editor.hc-black .find-widget .button:not(.disabled):hover, +.monaco-editor.vs-dark .find-widget .button:not(.disabled):hover { + background-color: rgba(255, 255, 255, 0.1); +} + +.monaco-editor.hc-black .find-widget .button:before { + position: relative; + top: 1px; + left: 2px; +} + +.monaco-editor.hc-black .find-widget .monaco-checkbox .checkbox:checked + .label { + background-color: rgba(255, 255, 255, 0.1); +} diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts new file mode 100644 index 0000000000..49845f50b1 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -0,0 +1,1079 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./findWidget'; +import * as nls from 'vs/nls'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import * as platform from 'vs/base/common/platform'; +import * as strings from 'vs/base/common/strings'; +import * as dom from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput'; +import { IMessage as InputBoxMessage, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { Sash, IHorizontalSashLayoutProvider, ISashEvent, Orientation } from 'vs/base/browser/ui/sash/sash'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { FIND_IDS, MATCHES_LIMIT } from 'vs/editor/contrib/find/common/findModel'; +import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/common/findState'; +import { Range } from 'vs/editor/common/core/range'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/common/findController'; +import { ITheme, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; +import { Color } from 'vs/base/common/color'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { editorFindRangeHighlight, editorFindMatch, editorFindMatchHighlight, activeContrastBorder, contrastBorder, inputBackground, editorWidgetBackground, inputActiveOptionBorder, widgetShadow, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, errorForeground, editorWidgetBorder } from 'vs/platform/theme/common/colorRegistry'; + + +export interface IFindController { + replace(): void; + replaceAll(): void; +} + +const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); +const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); +const NLS_PREVIOUS_MATCH_BTN_LABEL = nls.localize('label.previousMatchButton', "Previous match"); +const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next match"); +const NLS_TOGGLE_SELECTION_FIND_TITLE = nls.localize('label.toggleSelectionFind', "Find in selection"); +const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close"); +const NLS_REPLACE_INPUT_LABEL = nls.localize('label.replace', "Replace"); +const NLS_REPLACE_INPUT_PLACEHOLDER = nls.localize('placeholder.replace', "Replace"); +const NLS_REPLACE_BTN_LABEL = nls.localize('label.replaceButton', "Replace"); +const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All"); +const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode"); +const NLS_MATCHES_COUNT_LIMIT_TITLE = nls.localize('title.matchesCountLimit', "Only the first 999 results are highlighted, but all find operations work on the entire text."); +const NLS_MATCHES_LOCATION = nls.localize('label.matchesLocation', "{0} of {1}"); +const NLS_NO_RESULTS = nls.localize('label.noResults', "No Results"); + +const FIND_WIDGET_INITIAL_WIDTH = 411; +const PART_WIDTH = 275; +const FIND_INPUT_AREA_WIDTH = PART_WIDTH - 54; +const REPLACE_INPUT_AREA_WIDTH = FIND_INPUT_AREA_WIDTH; + +let MAX_MATCHES_COUNT_WIDTH = 69; +let FIND_ALL_CONTROLS_WIDTH = 17/** Find Input margin-left */ + (MAX_MATCHES_COUNT_WIDTH + 3 + 1) /** Match Results */ + 23 /** Button */ * 4 + 2/** sash */; + +const FIND_INPUT_AREA_HEIGHT = 34; // The height of Find Widget when Replace Input is not visible. +const FIND_REPLACE_AREA_HEIGHT = 64; // The height of Find Widget when Replace Input is visible. + + +export class FindWidgetViewZone implements IViewZone { + public afterLineNumber: number; + public heightInPx: number; + public suppressMouseDown: boolean; + public domNode: HTMLElement; + + constructor(afterLineNumber: number) { + this.afterLineNumber = afterLineNumber; + + this.heightInPx = FIND_INPUT_AREA_HEIGHT; + this.suppressMouseDown = false; + this.domNode = document.createElement('div'); + this.domNode.className = 'dock-find-viewzone'; + } +} + +export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider { + private static ID = 'editor.contrib.findWidget'; + private _codeEditor: ICodeEditor; + private _state: FindReplaceState; + private _controller: IFindController; + private _contextViewProvider: IContextViewProvider; + private _keybindingService: IKeybindingService; + + private _domNode: HTMLElement; + private _findInput: FindInput; + private _replaceInputBox: InputBox; + + private _toggleReplaceBtn: SimpleButton; + private _matchesCount: HTMLElement; + private _prevBtn: SimpleButton; + private _nextBtn: SimpleButton; + private _toggleSelectionFind: SimpleCheckbox; + private _closeBtn: SimpleButton; + private _replaceBtn: SimpleButton; + private _replaceAllBtn: SimpleButton; + + private _isVisible: boolean; + private _isReplaceVisible: boolean; + + private _focusTracker: dom.IFocusTracker; + private _findInputFocused: IContextKey; + private _viewZone: FindWidgetViewZone; + private _viewZoneId: number; + + private _resizeSash: Sash; + + constructor( + codeEditor: ICodeEditor, + controller: IFindController, + state: FindReplaceState, + contextViewProvider: IContextViewProvider, + keybindingService: IKeybindingService, + contextKeyService: IContextKeyService, + themeService: IThemeService + ) { + super(); + this._codeEditor = codeEditor; + this._controller = controller; + this._state = state; + this._contextViewProvider = contextViewProvider; + this._keybindingService = keybindingService; + + this._isVisible = false; + this._isReplaceVisible = false; + + this._register(this._state.addChangeListener((e) => this._onStateChanged(e))); + this._buildDomNode(); + this._updateButtons(); + + let checkEditorWidth = () => { + let editorWidth = this._codeEditor.getConfiguration().layoutInfo.width; + let minimapWidth = this._codeEditor.getConfiguration().layoutInfo.minimapWidth; + let collapsedFindWidget = false; + let reducedFindWidget = false; + let narrowFindWidget = false; + let widgetWidth = dom.getTotalWidth(this._domNode); + + if (widgetWidth > FIND_WIDGET_INITIAL_WIDTH) { + // as the widget is resized by users, we may need to change the max width of the widget as the editor width changes. + this._domNode.style.maxWidth = `${editorWidth - 28 - minimapWidth - 15}px`; + this._replaceInputBox.inputElement.style.width = `${dom.getTotalWidth(this._findInput.inputBox.inputElement)}px`; + return; + } + + if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth >= editorWidth) { + reducedFindWidget = true; + } + if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth - MAX_MATCHES_COUNT_WIDTH >= editorWidth) { + narrowFindWidget = true; + } + if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth - MAX_MATCHES_COUNT_WIDTH >= editorWidth + 50) { + collapsedFindWidget = true; + } + dom.toggleClass(this._domNode, 'collapsed-find-widget', collapsedFindWidget); + dom.toggleClass(this._domNode, 'narrow-find-widget', narrowFindWidget); + dom.toggleClass(this._domNode, 'reduced-find-widget', reducedFindWidget); + + if (!narrowFindWidget && !collapsedFindWidget) { + // the minimal left offset of findwidget is 15px. + this._domNode.style.maxWidth = `${editorWidth - 28 - minimapWidth - 15}px`; + } + + let findInputWidth = dom.getTotalWidth(this._findInput.inputBox.inputElement); + if (findInputWidth > 0) { + this._replaceInputBox.inputElement.style.width = `${findInputWidth}px`; + } + + }; + checkEditorWidth(); + + this._register(this._codeEditor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { + if (e.readOnly) { + if (this._codeEditor.getConfiguration().readOnly) { + // Hide replace part if editor becomes read only + this._state.change({ isReplaceRevealed: false }, false); + } + this._updateButtons(); + } + if (e.layoutInfo) { + checkEditorWidth(); + } + })); + this._register(this._codeEditor.onDidChangeCursorSelection(() => { + if (this._isVisible) { + this._updateToggleSelectionFindButton(); + } + })); + this._findInputFocused = CONTEXT_FIND_INPUT_FOCUSED.bindTo(contextKeyService); + this._focusTracker = this._register(dom.trackFocus(this._findInput.inputBox.inputElement)); + this._focusTracker.addFocusListener(() => { + this._findInputFocused.set(true); + + if (this._toggleSelectionFind.checked) { + let selection = this._codeEditor.getSelection(); + if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { + selection = selection.setEndPosition(selection.endLineNumber - 1, 1); + } + let currentMatch = this._state.currentMatch; + if (selection.startLineNumber !== selection.endLineNumber) { + if (!Range.equalsRange(selection, currentMatch)) { + // Reseed find scope + this._state.change({ searchScope: selection }, true); + } + } + } + }); + this._focusTracker.addBlurListener(() => { + this._findInputFocused.set(false); + }); + + this._codeEditor.addOverlayWidget(this); + this._viewZone = new FindWidgetViewZone(0); // Put it before the first line then users can scroll beyond the first line. + + this._applyTheme(themeService.getTheme()); + this._register(themeService.onThemeChange(this._applyTheme.bind(this))); + + this._register(this._codeEditor.onDidChangeModel((e) => { + if (!this._isVisible) { + return; + } + + if (this._viewZoneId === undefined) { + return; + } + + this._codeEditor.changeViewZones((accessor) => { + accessor.removeZone(this._viewZoneId); + this._viewZoneId = undefined; + }); + })); + + + this._register(this._codeEditor.onDidScrollChange((e) => { + if (e.scrollTopChanged) { + this._layoutViewZone(); + return; + } + + // for other scroll changes, layout the viewzone in next tick to avoid ruining current rendering. + setTimeout(() => { + this._layoutViewZone(); + }, 0); + })); + } + + // ----- IOverlayWidget API + + public getId(): string { + return FindWidget.ID; + } + + public getDomNode(): HTMLElement { + return this._domNode; + } + + public getPosition(): IOverlayWidgetPosition { + if (this._isVisible) { + return { + preference: OverlayWidgetPositionPreference.TOP_RIGHT_CORNER + }; + } + return null; + } + + // ----- React to state changes + + private _onStateChanged(e: FindReplaceStateChangedEvent): void { + if (e.searchString) { + this._findInput.setValue(this._state.searchString); + this._updateButtons(); + } + if (e.replaceString) { + this._replaceInputBox.value = this._state.replaceString; + } + if (e.isRevealed) { + if (this._state.isRevealed) { + this._reveal(true); + } else { + this._hide(true); + } + } + if (e.isReplaceRevealed) { + if (this._state.isReplaceRevealed) { + if (!this._codeEditor.getConfiguration().readOnly && !this._isReplaceVisible) { + this._isReplaceVisible = true; + this._updateButtons(); + } + } else { + if (this._isReplaceVisible) { + this._isReplaceVisible = false; + this._updateButtons(); + } + } + } + if (e.isRegex) { + this._findInput.setRegex(this._state.isRegex); + } + if (e.wholeWord) { + this._findInput.setWholeWords(this._state.wholeWord); + } + if (e.matchCase) { + this._findInput.setCaseSensitive(this._state.matchCase); + } + if (e.searchScope) { + if (this._state.searchScope) { + this._toggleSelectionFind.checked = true; + } else { + this._toggleSelectionFind.checked = false; + } + this._updateToggleSelectionFindButton(); + } + if (e.searchString || e.matchesCount || e.matchesPosition) { + let showRedOutline = (this._state.searchString.length > 0 && this._state.matchesCount === 0); + dom.toggleClass(this._domNode, 'no-results', showRedOutline); + + this._updateMatchesCount(); + } + if (e.searchString || e.currentMatch) { + this._layoutViewZone(); + } + } + + private _updateMatchesCount(): void { + this._matchesCount.style.minWidth = MAX_MATCHES_COUNT_WIDTH + 'px'; + if (this._state.matchesCount >= MATCHES_LIMIT) { + this._matchesCount.title = NLS_MATCHES_COUNT_LIMIT_TITLE; + } else { + this._matchesCount.title = ''; + } + + // remove previous content + if (this._matchesCount.firstChild) { + this._matchesCount.removeChild(this._matchesCount.firstChild); + } + + let label: string; + if (this._state.matchesCount > 0) { + let matchesCount: string = String(this._state.matchesCount); + if (this._state.matchesCount >= MATCHES_LIMIT) { + matchesCount += '+'; + } + let matchesPosition: string = String(this._state.matchesPosition); + if (matchesPosition === '0') { + matchesPosition = '?'; + } + label = strings.format(NLS_MATCHES_LOCATION, matchesPosition, matchesCount); + } else { + label = NLS_NO_RESULTS; + } + this._matchesCount.appendChild(document.createTextNode(label)); + + MAX_MATCHES_COUNT_WIDTH = Math.max(MAX_MATCHES_COUNT_WIDTH, this._matchesCount.clientWidth); + } + + // ----- actions + + /** + * If 'selection find' is ON we should not disable the button (its function is to cancel 'selection find'). + * If 'selection find' is OFF we enable the button only if there is a selection. + */ + private _updateToggleSelectionFindButton(): void { + let selection = this._codeEditor.getSelection(); + let isSelection = selection ? (selection.startLineNumber !== selection.endLineNumber || selection.startColumn !== selection.endColumn) : false; + let isChecked = this._toggleSelectionFind.checked; + + this._toggleSelectionFind.setEnabled(this._isVisible && (isChecked || isSelection)); + } + + private _updateButtons(): void { + this._findInput.setEnabled(this._isVisible); + this._replaceInputBox.setEnabled(this._isVisible && this._isReplaceVisible); + this._updateToggleSelectionFindButton(); + this._closeBtn.setEnabled(this._isVisible); + + let findInputIsNonEmpty = (this._state.searchString.length > 0); + this._prevBtn.setEnabled(this._isVisible && findInputIsNonEmpty); + this._nextBtn.setEnabled(this._isVisible && findInputIsNonEmpty); + this._replaceBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty); + this._replaceAllBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty); + + dom.toggleClass(this._domNode, 'replaceToggled', this._isReplaceVisible); + this._toggleReplaceBtn.toggleClass('collapse', !this._isReplaceVisible); + this._toggleReplaceBtn.toggleClass('expand', this._isReplaceVisible); + this._toggleReplaceBtn.setExpanded(this._isReplaceVisible); + + let canReplace = !this._codeEditor.getConfiguration().readOnly; + this._toggleReplaceBtn.setEnabled(this._isVisible && canReplace); + } + + private _reveal(animate: boolean): void { + if (!this._isVisible) { + this._isVisible = true; + + let selection = this._codeEditor.getSelection(); + let isSelection = selection ? (selection.startLineNumber !== selection.endLineNumber || selection.startColumn !== selection.endColumn) : false; + if (isSelection && this._codeEditor.getConfiguration().contribInfo.find.autoFindInSelection) { + this._toggleSelectionFind.checked = true; + } else { + this._toggleSelectionFind.checked = false; + } + this._updateButtons(); + + setTimeout(() => { + dom.addClass(this._domNode, 'visible'); + this._domNode.setAttribute('aria-hidden', 'false'); + if (!animate) { + dom.addClass(this._domNode, 'noanimation'); + setTimeout(() => { + dom.removeClass(this._domNode, 'noanimation'); + }, 200); + } + }, 0); + this._codeEditor.layoutOverlayWidget(this); + + let adjustEditorScrollTop = true; + if (this._codeEditor.getConfiguration().contribInfo.find.seedSearchStringFromSelection && selection) { + let editorCoords = dom.getDomNodePagePosition(this._codeEditor.getDomNode()); + let startCoords = this._codeEditor.getScrolledVisiblePosition(selection.getStartPosition()); + let startLeft = editorCoords.left + startCoords.left; + let startTop = startCoords.top; + + if (startTop < this._viewZone.heightInPx) { + if (selection.endLineNumber > selection.startLineNumber) { + adjustEditorScrollTop = false; + } + + let leftOfFindWidget = dom.getTopLeftOffset(this._domNode).left; + if (startLeft > leftOfFindWidget) { + adjustEditorScrollTop = false; + } + let endCoords = this._codeEditor.getScrolledVisiblePosition(selection.getEndPosition()); + let endLeft = editorCoords.left + endCoords.left; + if (endLeft > leftOfFindWidget) { + adjustEditorScrollTop = false; + } + } + } + this._showViewZone(adjustEditorScrollTop); + } + } + + private _hide(focusTheEditor: boolean): void { + if (this._isVisible) { + this._isVisible = false; + + this._updateButtons(); + + dom.removeClass(this._domNode, 'visible'); + this._domNode.setAttribute('aria-hidden', 'true'); + if (focusTheEditor) { + this._codeEditor.focus(); + } + this._codeEditor.layoutOverlayWidget(this); + this._codeEditor.changeViewZones((accessor) => { + if (this._viewZoneId !== undefined) { + accessor.removeZone(this._viewZoneId); + this._viewZoneId = undefined; + this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() - this._viewZone.heightInPx); + } + }); + } + } + + private _layoutViewZone() { + if (!this._isVisible) { + return; + } + + if (this._viewZoneId !== undefined) { + return; + } + + this._codeEditor.changeViewZones((accessor) => { + if (this._state.isReplaceRevealed) { + this._viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT; + } else { + this._viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT; + } + + this._viewZoneId = accessor.addZone(this._viewZone); + // scroll top adjust to make sure the editor doesn't scroll when adding viewzone at the beginning. + this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + this._viewZone.heightInPx); + }); + } + + private _showViewZone(adjustScroll: boolean = true) { + if (!this._isVisible) { + return; + } + + this._codeEditor.changeViewZones((accessor) => { + let scrollAdjustment = FIND_INPUT_AREA_HEIGHT; + + if (this._viewZoneId !== undefined) { + if (this._state.isReplaceRevealed) { + this._viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT; + scrollAdjustment = FIND_REPLACE_AREA_HEIGHT - FIND_INPUT_AREA_HEIGHT; + } else { + this._viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT; + scrollAdjustment = FIND_INPUT_AREA_HEIGHT - FIND_REPLACE_AREA_HEIGHT; + } + accessor.removeZone(this._viewZoneId); + } else { + this._viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT; + } + this._viewZoneId = accessor.addZone(this._viewZone); + + if (adjustScroll) { + this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment); + } + }); + } + + private _applyTheme(theme: ITheme) { + let inputStyles: IFindInputStyles = { + inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), + inputBackground: theme.getColor(inputBackground), + inputForeground: theme.getColor(inputForeground), + inputBorder: theme.getColor(inputBorder), + inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground), + inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder), + inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground), + inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder), + inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground), + inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder) + }; + this._findInput.style(inputStyles); + this._replaceInputBox.style(inputStyles); + } + + // ----- Public + + public focusFindInput(): void { + this._findInput.select(); + // Edge browser requires focus() in addition to select() + this._findInput.focus(); + } + + public focusReplaceInput(): void { + this._replaceInputBox.select(); + // Edge browser requires focus() in addition to select() + this._replaceInputBox.focus(); + } + + public highlightFindOptions(): void { + this._findInput.highlightFindOptions(); + } + + private _onFindInputMouseDown(e: IMouseEvent): void { + // on linux, middle key does pasting. + if (e.middleButton) { + e.stopPropagation(); + } + } + + private _onFindInputKeyDown(e: IKeyboardEvent): void { + + if (e.equals(KeyCode.Enter)) { + this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().done(null, onUnexpectedError); + e.preventDefault(); + return; + } + + if (e.equals(KeyMod.Shift | KeyCode.Enter)) { + this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().done(null, onUnexpectedError); + e.preventDefault(); + return; + } + + if (e.equals(KeyCode.Tab)) { + if (this._isReplaceVisible) { + this._replaceInputBox.focus(); + } else { + this._findInput.focusOnCaseSensitive(); + } + e.preventDefault(); + return; + } + + if (e.equals(KeyMod.CtrlCmd | KeyCode.DownArrow)) { + this._codeEditor.focus(); + e.preventDefault(); + return; + } + } + + private _onReplaceInputKeyDown(e: IKeyboardEvent): void { + + if (e.equals(KeyCode.Enter)) { + this._controller.replace(); + e.preventDefault(); + return; + } + + if (e.equals(KeyMod.CtrlCmd | KeyCode.Enter)) { + this._controller.replaceAll(); + e.preventDefault(); + return; + } + + if (e.equals(KeyCode.Tab)) { + this._findInput.focusOnCaseSensitive(); + e.preventDefault(); + return; + } + + if (e.equals(KeyMod.Shift | KeyCode.Tab)) { + this._findInput.focus(); + e.preventDefault(); + return; + } + + if (e.equals(KeyMod.CtrlCmd | KeyCode.DownArrow)) { + this._codeEditor.focus(); + e.preventDefault(); + return; + } + } + + // ----- sash + public getHorizontalSashTop(sash: Sash): number { + return 0; + } + public getHorizontalSashLeft?(sash: Sash): number { + return 0; + } + public getHorizontalSashWidth?(sash: Sash): number { + return 500; + } + + // ----- initialization + + private _keybindingLabelFor(actionId: string): string { + let kb = this._keybindingService.lookupKeybinding(actionId); + if (!kb) { + return ''; + } + return ` (${kb.getLabel()})`; + } + + private _buildFindPart(): HTMLElement { + // Find input + this._findInput = this._register(new FindInput(null, this._contextViewProvider, { + width: FIND_INPUT_AREA_WIDTH, + label: NLS_FIND_INPUT_LABEL, + placeholder: NLS_FIND_INPUT_PLACEHOLDER, + appendCaseSensitiveLabel: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand), + appendWholeWordsLabel: this._keybindingLabelFor(FIND_IDS.ToggleWholeWordCommand), + appendRegexLabel: this._keybindingLabelFor(FIND_IDS.ToggleRegexCommand), + validation: (value: string): InputBoxMessage => { + if (value.length === 0) { + return null; + } + if (!this._findInput.getRegex()) { + return null; + } + try { + /* tslint:disable:no-unused-expression */ + new RegExp(value); + /* tslint:enable:no-unused-expression */ + return null; + } catch (e) { + return { content: e.message }; + } + } + })); + this._findInput.setRegex(!!this._state.isRegex); + this._findInput.setCaseSensitive(!!this._state.matchCase); + this._findInput.setWholeWords(!!this._state.wholeWord); + this._register(this._findInput.onKeyDown((e) => this._onFindInputKeyDown(e))); + this._register(this._findInput.onInput(() => { + this._state.change({ searchString: this._findInput.getValue() }, true); + })); + this._register(this._findInput.onDidOptionChange(() => { + this._state.change({ + isRegex: this._findInput.getRegex(), + wholeWord: this._findInput.getWholeWords(), + matchCase: this._findInput.getCaseSensitive() + }, true); + })); + this._register(this._findInput.onCaseSensitiveKeyDown((e) => { + if (e.equals(KeyMod.Shift | KeyCode.Tab)) { + if (this._isReplaceVisible) { + this._replaceInputBox.focus(); + e.preventDefault(); + } + } + })); + if (platform.isLinux) { + this._register(this._findInput.onMouseDown((e) => this._onFindInputMouseDown(e))); + } + + this._matchesCount = document.createElement('div'); + this._matchesCount.className = 'matchesCount'; + this._updateMatchesCount(); + + // Previous button + this._prevBtn = this._register(new SimpleButton({ + label: NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.PreviousMatchFindAction), + className: 'previous', + onTrigger: () => { + this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().done(null, onUnexpectedError); + }, + onKeyDown: (e) => { } + })); + + // Next button + this._nextBtn = this._register(new SimpleButton({ + label: NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.NextMatchFindAction), + className: 'next', + onTrigger: () => { + this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().done(null, onUnexpectedError); + }, + onKeyDown: (e) => { } + })); + + let findPart = document.createElement('div'); + findPart.className = 'find-part'; + findPart.appendChild(this._findInput.domNode); + findPart.appendChild(this._matchesCount); + findPart.appendChild(this._prevBtn.domNode); + findPart.appendChild(this._nextBtn.domNode); + + // Toggle selection button + this._toggleSelectionFind = this._register(new SimpleCheckbox({ + parent: findPart, + title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand), + onChange: () => { + if (this._toggleSelectionFind.checked) { + let selection = this._codeEditor.getSelection(); + if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { + selection = selection.setEndPosition(selection.endLineNumber - 1, 1); + } + if (!selection.isEmpty()) { + this._state.change({ searchScope: selection }, true); + } + } else { + this._state.change({ searchScope: null }, true); + } + } + })); + + // Close button + this._closeBtn = this._register(new SimpleButton({ + label: NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.CloseFindWidgetCommand), + className: 'close-fw', + onTrigger: () => { + this._state.change({ isRevealed: false, searchScope: null }, false); + }, + onKeyDown: (e) => { + if (e.equals(KeyCode.Tab)) { + if (this._isReplaceVisible) { + if (this._replaceBtn.isEnabled()) { + this._replaceBtn.focus(); + } else { + this._codeEditor.focus(); + } + e.preventDefault(); + } + } + } + })); + + findPart.appendChild(this._closeBtn.domNode); + + return findPart; + } + + private _buildReplacePart(): HTMLElement { + // Replace input + let replaceInput = document.createElement('div'); + replaceInput.className = 'replace-input'; + replaceInput.style.width = REPLACE_INPUT_AREA_WIDTH + 'px'; + this._replaceInputBox = this._register(new InputBox(replaceInput, null, { + ariaLabel: NLS_REPLACE_INPUT_LABEL, + placeholder: NLS_REPLACE_INPUT_PLACEHOLDER + })); + + this._register(dom.addStandardDisposableListener(this._replaceInputBox.inputElement, 'keydown', (e) => this._onReplaceInputKeyDown(e))); + this._register(dom.addStandardDisposableListener(this._replaceInputBox.inputElement, 'input', (e) => { + this._state.change({ replaceString: this._replaceInputBox.value }, false); + })); + + // Replace one button + this._replaceBtn = this._register(new SimpleButton({ + label: NLS_REPLACE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceOneAction), + className: 'replace', + onTrigger: () => { + this._controller.replace(); + }, + onKeyDown: (e) => { + if (e.equals(KeyMod.Shift | KeyCode.Tab)) { + this._closeBtn.focus(); + e.preventDefault(); + } + } + })); + + // Replace all button + this._replaceAllBtn = this._register(new SimpleButton({ + label: NLS_REPLACE_ALL_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceAllAction), + className: 'replace-all', + onTrigger: () => { + this._controller.replaceAll(); + }, + onKeyDown: (e) => { } + })); + + let replacePart = document.createElement('div'); + replacePart.className = 'replace-part'; + replacePart.appendChild(replaceInput); + replacePart.appendChild(this._replaceBtn.domNode); + replacePart.appendChild(this._replaceAllBtn.domNode); + + return replacePart; + } + + private _buildDomNode(): void { + // Find part + let findPart = this._buildFindPart(); + + // Replace part + let replacePart = this._buildReplacePart(); + + // Toggle replace button + this._toggleReplaceBtn = this._register(new SimpleButton({ + label: NLS_TOGGLE_REPLACE_MODE_BTN_LABEL, + className: 'toggle left', + onTrigger: () => { + this._state.change({ isReplaceRevealed: !this._isReplaceVisible }, false); + if (this._isReplaceVisible) { + this._replaceInputBox.width = this._findInput.inputBox.width; + } + this._showViewZone(); + }, + onKeyDown: (e) => { } + })); + this._toggleReplaceBtn.toggleClass('expand', this._isReplaceVisible); + this._toggleReplaceBtn.toggleClass('collapse', !this._isReplaceVisible); + this._toggleReplaceBtn.setExpanded(this._isReplaceVisible); + + // Widget + this._domNode = document.createElement('div'); + this._domNode.className = 'editor-widget find-widget'; + this._domNode.setAttribute('aria-hidden', 'true'); + // We need to set this explicitly, otherwise on IE11, the width inheritence of flex doesn't work. + this._domNode.style.width = `${FIND_WIDGET_INITIAL_WIDTH}px`; + + this._domNode.appendChild(this._toggleReplaceBtn.domNode); + this._domNode.appendChild(findPart); + this._domNode.appendChild(replacePart); + + this._buildSash(); + } + + private _buildSash() { + this._resizeSash = new Sash(this._domNode, this, { orientation: Orientation.VERTICAL }); + let originalWidth = FIND_WIDGET_INITIAL_WIDTH; + + this._register(this._resizeSash.addListener('start', (e: ISashEvent) => { + originalWidth = dom.getTotalWidth(this._domNode); + })); + + this._register(this._resizeSash.addListener('change', (evt: ISashEvent) => { + let width = originalWidth + evt.startX - evt.currentX; + + if (width < FIND_WIDGET_INITIAL_WIDTH) { + // narrow down the find widget should be handled by CSS. + return; + } + + let inputBoxWidth = width - FIND_ALL_CONTROLS_WIDTH; + let maxWidth = parseFloat(dom.getComputedStyle(this._domNode).maxWidth) || 0; + if (width > maxWidth) { + return; + } + this._domNode.style.width = `${width}px`; + if (this._isReplaceVisible) { + this._replaceInputBox.width = inputBoxWidth; + } + })); + } +} + +interface ISimpleCheckboxOpts { + parent: HTMLElement; + title: string; + onChange: () => void; +} + +class SimpleCheckbox extends Widget { + + private static _COUNTER = 0; + + private _opts: ISimpleCheckboxOpts; + private _domNode: HTMLElement; + private _checkbox: HTMLInputElement; + private _label: HTMLLabelElement; + + constructor(opts: ISimpleCheckboxOpts) { + super(); + this._opts = opts; + + this._domNode = document.createElement('div'); + this._domNode.className = 'monaco-checkbox'; + this._domNode.title = this._opts.title; + this._domNode.tabIndex = 0; + + this._checkbox = document.createElement('input'); + this._checkbox.type = 'checkbox'; + this._checkbox.className = 'checkbox'; + this._checkbox.id = 'checkbox-' + SimpleCheckbox._COUNTER++; + this._checkbox.tabIndex = -1; + + this._label = document.createElement('label'); + this._label.className = 'label'; + // Connect the label and the checkbox. Checkbox will get checked when the label recieves a click. + this._label.htmlFor = this._checkbox.id; + this._label.tabIndex = -1; + + this._domNode.appendChild(this._checkbox); + this._domNode.appendChild(this._label); + + this._opts.parent.appendChild(this._domNode); + + this.onchange(this._checkbox, (e) => { + this._opts.onChange(); + }); + } + + public get domNode(): HTMLElement { + return this._domNode; + } + + public get checked(): boolean { + return this._checkbox.checked; + } + + public set checked(newValue: boolean) { + this._checkbox.checked = newValue; + } + + public focus(): void { + this._checkbox.focus(); + } + + private enable(): void { + this._checkbox.removeAttribute('disabled'); + } + + private disable(): void { + this._checkbox.disabled = true; + } + + public setEnabled(enabled: boolean): void { + if (enabled) { + this.enable(); + this.domNode.tabIndex = 0; + } else { + this.disable(); + this.domNode.tabIndex = -1; + } + } +} + +export interface ISimpleButtonOpts { + label: string; + className: string; + onTrigger: () => void; + onKeyDown: (e: IKeyboardEvent) => void; +} + +export class SimpleButton extends Widget { + + private _opts: ISimpleButtonOpts; + private _domNode: HTMLElement; + + constructor(opts: ISimpleButtonOpts) { + super(); + this._opts = opts; + + this._domNode = document.createElement('div'); + this._domNode.title = this._opts.label; + this._domNode.tabIndex = 0; + this._domNode.className = 'button ' + this._opts.className; + this._domNode.setAttribute('role', 'button'); + this._domNode.setAttribute('aria-label', this._opts.label); + + this.onclick(this._domNode, (e) => { + this._opts.onTrigger(); + e.preventDefault(); + }); + this.onkeydown(this._domNode, (e) => { + if (e.equals(KeyCode.Space) || e.equals(KeyCode.Enter)) { + this._opts.onTrigger(); + e.preventDefault(); + return; + } + this._opts.onKeyDown(e); + }); + } + + public get domNode(): HTMLElement { + return this._domNode; + } + + public isEnabled(): boolean { + return (this._domNode.tabIndex >= 0); + } + + public focus(): void { + this._domNode.focus(); + } + + public setEnabled(enabled: boolean): void { + dom.toggleClass(this._domNode, 'disabled', !enabled); + this._domNode.setAttribute('aria-disabled', String(!enabled)); + this._domNode.tabIndex = enabled ? 0 : -1; + } + + public setExpanded(expanded: boolean): void { + this._domNode.setAttribute('aria-expanded', String(!!expanded)); + } + + public toggleClass(className: string, shouldHaveIt: boolean): void { + dom.toggleClass(this._domNode, className, shouldHaveIt); + } +} + +// theming + +registerThemingParticipant((theme, collector) => { + function addBackgroundColorRule(selector: string, color: Color): void { + if (color) { + collector.addRule(`.monaco-editor ${selector} { background-color: ${color}; }`); + } + } + + addBackgroundColorRule('.findMatch', theme.getColor(editorFindMatchHighlight)); + addBackgroundColorRule('.currentFindMatch', theme.getColor(editorFindMatch)); + addBackgroundColorRule('.findScope', theme.getColor(editorFindRangeHighlight)); + + let widgetBackground = theme.getColor(editorWidgetBackground); + addBackgroundColorRule('.find-widget', widgetBackground); + + let widgetShadowColor = theme.getColor(widgetShadow); + if (widgetShadowColor) { + collector.addRule(`.monaco-editor .find-widget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + } + + let hcOutline = theme.getColor(activeContrastBorder); + if (hcOutline) { + collector.addRule(`.monaco-editor .findScope { border: 1px dashed ${hcOutline.transparent(0.4)}; }`); + collector.addRule(`.monaco-editor .currentFindMatch { border: 2px solid ${hcOutline}; padding: 1px; -moz-box-sizing: border-box; box-sizing: border-box; }`); + collector.addRule(`.monaco-editor .findMatch { border: 1px dotted ${hcOutline}; -moz-box-sizing: border-box; box-sizing: border-box; }`); + } + let hcBorder = theme.getColor(contrastBorder); + if (hcBorder) { + collector.addRule(`.monaco-editor .find-widget { border: 2px solid ${hcBorder}; }`); + } + + let error = theme.getColor(errorForeground); + if (error) { + collector.addRule(`.monaco-editor .find-widget.no-results .matchesCount { color: ${error}; }`); + } + + let border = theme.getColor(editorWidgetBorder); + if (border) { + collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${border}; width: 3px !important; margin-left: -4px;}`); + } +}); + diff --git a/src/vs/editor/contrib/find/browser/images/cancelSelectionFind-inverse.svg b/src/vs/editor/contrib/find/browser/images/cancelSelectionFind-inverse.svg new file mode 100644 index 0000000000..d776fcde98 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/cancelSelectionFind-inverse.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/images/cancelSelectionFind.svg b/src/vs/editor/contrib/find/browser/images/cancelSelectionFind.svg new file mode 100644 index 0000000000..cdff5731a8 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/cancelSelectionFind.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/images/close-dark.svg b/src/vs/editor/contrib/find/browser/images/close-dark.svg new file mode 100644 index 0000000000..751e89b3b0 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/close-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/images/close.svg b/src/vs/editor/contrib/find/browser/images/close.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/images/expando-collapsed-dark.svg b/src/vs/editor/contrib/find/browser/images/expando-collapsed-dark.svg new file mode 100644 index 0000000000..6f3abfce78 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/expando-collapsed-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/images/expando-collapsed.svg b/src/vs/editor/contrib/find/browser/images/expando-collapsed.svg new file mode 100644 index 0000000000..5dcb87c772 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/expando-collapsed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/images/expando-expanded-dark.svg b/src/vs/editor/contrib/find/browser/images/expando-expanded-dark.svg new file mode 100644 index 0000000000..22dfac04f1 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/expando-expanded-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/images/expando-expanded.svg b/src/vs/editor/contrib/find/browser/images/expando-expanded.svg new file mode 100644 index 0000000000..e55ccd923e --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/expando-expanded.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/images/next-inverse.svg b/src/vs/editor/contrib/find/browser/images/next-inverse.svg new file mode 100644 index 0000000000..50482917af --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/next-inverse.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/vs/editor/contrib/find/browser/images/next.svg b/src/vs/editor/contrib/find/browser/images/next.svg new file mode 100644 index 0000000000..a2a011453a --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/next.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/vs/editor/contrib/find/browser/images/previous-inverse.svg b/src/vs/editor/contrib/find/browser/images/previous-inverse.svg new file mode 100644 index 0000000000..8ff41da5da --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/previous-inverse.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/vs/editor/contrib/find/browser/images/previous.svg b/src/vs/editor/contrib/find/browser/images/previous.svg new file mode 100644 index 0000000000..3c8b367a93 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/previous.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/vs/editor/contrib/find/browser/images/replace-all-inverse.svg b/src/vs/editor/contrib/find/browser/images/replace-all-inverse.svg new file mode 100644 index 0000000000..45312b6089 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/replace-all-inverse.svg @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/vs/editor/contrib/find/browser/images/replace-all.svg b/src/vs/editor/contrib/find/browser/images/replace-all.svg new file mode 100644 index 0000000000..4254f7c6d1 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/replace-all.svg @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/vs/editor/contrib/find/browser/images/replace-inverse.svg b/src/vs/editor/contrib/find/browser/images/replace-inverse.svg new file mode 100644 index 0000000000..9a59e26378 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/replace-inverse.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/vs/editor/contrib/find/browser/images/replace.svg b/src/vs/editor/contrib/find/browser/images/replace.svg new file mode 100644 index 0000000000..8b1eb0de23 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/images/replace.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/vs/editor/contrib/find/browser/simpleFindWidget.css b/src/vs/editor/contrib/find/browser/simpleFindWidget.css new file mode 100644 index 0000000000..4d4d06a709 --- /dev/null +++ b/src/vs/editor/contrib/find/browser/simpleFindWidget.css @@ -0,0 +1,82 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .simple-find-part { + z-index: 10; + position: absolute; + top: -40px; + right: 28px; + display: flex; + padding: 4px; + align-items: center; + width: 220px; + max-width: calc(100% - 28px - 28px - 8px); + + -webkit-transition: top 200ms linear; + -o-transition: top 200ms linear; + -moz-transition: top 200ms linear; + -ms-transition: top 200ms linear; + transition: top 200ms linear; +} + +.monaco-workbench .simple-find-part.visible { + top: 0; +} + +.monaco-workbench .simple-find-part .monaco-findInput { + flex: 1; +} + +/* Temporarily we don't show match numbers */ +.monaco-workbench .simple-find-part .monaco-findInput .controls { + display: none; +} +.monaco-workbench .simple-find-part .monaco-findInput .monaco-inputbox .wrapper .input { + width: 100% !important; +} + +.monaco-workbench .simple-find-part .button { + min-width: 20px; + width: 20px; + height: 20px; + display: flex; + flex: initial; + margin-left: 3px; + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; +} + +.monaco-workbench .simple-find-part .button.previous { + background-image: url('images/previous.svg'); +} + +.monaco-workbench .simple-find-part .button.next { + background-image: url('images/next.svg'); +} + +.monaco-workbench .simple-find-part .button.close-fw { + background-image: url('images/close.svg'); +} + +.hc-black .monaco-workbench .simple-find-part .button.previous, +.vs-dark .monaco-workbench .simple-find-part .button.previous { + background-image: url('images/previous-inverse.svg'); +} + +.hc-black .monaco-workbench .simple-find-part .button.next, +.vs-dark .monaco-workbench .simple-find-part .button.next { + background-image: url('images/next-inverse.svg'); +} + +.hc-black .monaco-workbench .simple-find-part .button.close-fw, +.vs-dark .monaco-workbench .simple-find-part .button.close-fw { + background-image: url('images/close-dark.svg'); +} + +monaco-workbench .simple-find-part .button.disabled { + opacity: 0.3; + cursor: default; +} \ No newline at end of file diff --git a/src/vs/editor/contrib/find/browser/simpleFindWidget.ts b/src/vs/editor/contrib/find/browser/simpleFindWidget.ts new file mode 100644 index 0000000000..29af06026a --- /dev/null +++ b/src/vs/editor/contrib/find/browser/simpleFindWidget.ts @@ -0,0 +1,223 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./simpleFindWidget'; +import * as nls from 'vs/nls'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { Delayer } from 'vs/base/common/async'; +import { HistoryNavigator } from 'vs/base/common/history'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import * as dom from 'vs/base/browser/dom'; +import { FindInput } from 'vs/base/browser/ui/findinput/findInput'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService'; +import { inputBackground, inputActiveOptionBorder, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { SimpleButton } from './findWidget'; + +const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); +const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); +const NLS_PREVIOUS_MATCH_BTN_LABEL = nls.localize('label.previousMatchButton', "Previous match"); +const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next match"); +const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close"); + +export abstract class SimpleFindWidget extends Widget { + protected _findInput: FindInput; + protected _domNode: HTMLElement; + protected _isVisible: boolean; + protected _focusTracker: dom.IFocusTracker; + protected _findInputFocusTracker: dom.IFocusTracker; + protected _findHistory: HistoryNavigator; + protected _updateHistoryDelayer: Delayer; + + constructor( + @IContextViewService private _contextViewService: IContextViewService, + private animate: boolean = true + ) { + super(); + this._findInput = this._register(new FindInput(null, this._contextViewService, { + label: NLS_FIND_INPUT_LABEL, + placeholder: NLS_FIND_INPUT_PLACEHOLDER, + })); + + // Find History with update delayer + this._findHistory = new HistoryNavigator(); + this._updateHistoryDelayer = new Delayer(500); + + this.oninput(this._findInput.domNode, (e) => { + this.onInputChanged(); + this._delayedUpdateHistory(); + }); + + this._register(this._findInput.onKeyDown((e) => { + if (e.equals(KeyCode.Enter)) { + this.find(false); + e.preventDefault(); + return; + } + + if (e.equals(KeyMod.Shift | KeyCode.Enter)) { + this.find(true); + e.preventDefault(); + return; + } + })); + + let prevBtn = new SimpleButton({ + label: NLS_PREVIOUS_MATCH_BTN_LABEL, + className: 'previous', + onTrigger: () => { + this.find(true); + }, + onKeyDown: (e) => { } + }); + + let nextBtn = new SimpleButton({ + label: NLS_NEXT_MATCH_BTN_LABEL, + className: 'next', + onTrigger: () => { + this.find(false); + }, + onKeyDown: (e) => { } + }); + + let closeBtn = new SimpleButton({ + label: NLS_CLOSE_BTN_LABEL, + className: 'close-fw', + onTrigger: () => { + this.hide(); + }, + onKeyDown: (e) => { } + }); + + this._domNode = document.createElement('div'); + this._domNode.classList.add('simple-find-part'); + this._domNode.appendChild(this._findInput.domNode); + this._domNode.appendChild(prevBtn.domNode); + this._domNode.appendChild(nextBtn.domNode); + this._domNode.appendChild(closeBtn.domNode); + + this.onkeyup(this._domNode, e => { + if (e.equals(KeyCode.Escape)) { + this.hide(); + e.preventDefault(); + return; + } + }); + + this._focusTracker = this._register(dom.trackFocus(this._domNode)); + this._register(this._focusTracker.addFocusListener(this.onFocusTrackerFocus.bind(this))); + this._register(this._focusTracker.addBlurListener(this.onFocusTrackerBlur.bind(this))); + + this._findInputFocusTracker = this._register(dom.trackFocus(this._findInput.domNode)); + this._register(this._findInputFocusTracker.addFocusListener(this.onFindInputFocusTrackerFocus.bind(this))); + this._register(this._findInputFocusTracker.addBlurListener(this.onFindInputFocusTrackerBlur.bind(this))); + + this._register(dom.addDisposableListener(this._domNode, 'click', (event) => { + event.stopPropagation(); + })); + } + + protected abstract onInputChanged(); + protected abstract find(previous: boolean); + protected abstract onFocusTrackerFocus(); + protected abstract onFocusTrackerBlur(); + protected abstract onFindInputFocusTrackerFocus(); + protected abstract onFindInputFocusTrackerBlur(); + + protected get inputValue() { + return this._findInput.getValue(); + } + + public updateTheme(theme?: ITheme): void { + let inputStyles = { + inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), + inputBackground: theme.getColor(inputBackground), + inputForeground: theme.getColor(inputForeground), + inputBorder: theme.getColor(inputBorder), + inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground), + inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder), + inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground), + inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder), + inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground), + inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder) + }; + this._findInput.style(inputStyles); + } + + public getDomNode(): HTMLElement { + return this._domNode; + } + + public reveal(initialInput?: string): void { + if (initialInput) { + this._findInput.setValue(initialInput); + } + + if (this._isVisible) { + this._findInput.select(); + return; + } + + this._isVisible = true; + + setTimeout(() => { + dom.addClass(this._domNode, 'visible'); + this._domNode.setAttribute('aria-hidden', 'false'); + if (!this.animate) { + dom.addClass(this._domNode, 'noanimation'); + } + setTimeout(() => { + dom.removeClass(this._domNode, 'noanimation'); + this._findInput.select(); + }, 200); + }, 0); + } + + public hide(): void { + if (this._isVisible) { + this._isVisible = false; + + dom.removeClass(this._domNode, 'visible'); + this._domNode.setAttribute('aria-hidden', 'true'); + } + } + + protected _delayedUpdateHistory() { + this._updateHistoryDelayer.trigger(this._updateHistory.bind(this)); + } + + protected _updateHistory() { + if (this.inputValue) { + this._findHistory.add(this._findInput.getValue()); + } + } + + public showNextFindTerm() { + let next = this._findHistory.next(); + if (next) { + this._findInput.setValue(next); + } + } + + public showPreviousFindTerm() { + let previous = this._findHistory.previous(); + if (previous) { + this._findInput.setValue(previous); + } + } +} + +// theming +registerThemingParticipant((theme, collector) => { + const findWidgetBGColor = theme.getColor(editorWidgetBackground); + if (findWidgetBGColor) { + collector.addRule(`.monaco-workbench .simple-find-part { background-color: ${findWidgetBGColor} !important; }`); + } + + let widgetShadowColor = theme.getColor(widgetShadow); + if (widgetShadowColor) { + collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/find/common/find.ts b/src/vs/editor/contrib/find/common/find.ts new file mode 100644 index 0000000000..efa50d20b4 --- /dev/null +++ b/src/vs/editor/contrib/find/common/find.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as editorCommon from 'vs/editor/common/editorCommon'; + +export function getSelectionSearchString(editor: editorCommon.ICommonCodeEditor): string { + let selection = editor.getSelection(); + + // if selection spans multiple lines, default search string to empty + if (selection.startLineNumber === selection.endLineNumber) { + if (selection.isEmpty()) { + let wordAtPosition = editor.getModel().getWordAtPosition(selection.getStartPosition()); + if (wordAtPosition) { + return wordAtPosition.word; + } + } else { + return editor.getModel().getValueInRange(selection); + } + } + + return null; +} diff --git a/src/vs/editor/contrib/find/common/findController.ts b/src/vs/editor/contrib/find/common/findController.ts new file mode 100644 index 0000000000..e64977de9a --- /dev/null +++ b/src/vs/editor/contrib/find/common/findController.ts @@ -0,0 +1,1277 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { HistoryNavigator } from 'vs/base/common/history'; +import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ContextKeyExpr, RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as strings from 'vs/base/common/strings'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { editorAction, commonEditorContribution, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding, ToggleSearchScopeKeybinding, ShowPreviousFindTermKeybinding, ShowNextFindTermKeybinding } from 'vs/editor/contrib/find/common/findModel'; +import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/common/findState'; +import { getSelectionSearchString } from 'vs/editor/contrib/find/common/find'; +import { DocumentHighlightProviderRegistry } from 'vs/editor/common/modes'; +import { RunOnceScheduler, Delayer } from 'vs/base/common/async'; +import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; +import { themeColorFromId } from 'vs/platform/theme/common/themeService'; + +export const enum FindStartFocusAction { + NoFocusChange, + FocusFindInput, + FocusReplaceInput +} + +export interface IFindStartOptions { + forceRevealReplace: boolean; + seedSearchStringFromSelection: boolean; + shouldFocus: FindStartFocusAction; + shouldAnimate: boolean; +} + +export const CONTEXT_FIND_WIDGET_VISIBLE = new RawContextKey('findWidgetVisible', false); +export const CONTEXT_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = CONTEXT_FIND_WIDGET_VISIBLE.toNegated(); +// Keep ContextKey use of 'Focussed' to not break when clauses +export const CONTEXT_FIND_INPUT_FOCUSED = new RawContextKey('findInputFocussed', false); + +export class CommonFindController extends Disposable implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.findController'; + + private _editor: editorCommon.ICommonCodeEditor; + private _findWidgetVisible: IContextKey; + protected _state: FindReplaceState; + private _currentHistoryNavigator: HistoryNavigator; + protected _updateHistoryDelayer: Delayer; + private _model: FindModelBoundToEditorModel; + private _storageService: IStorageService; + + public static get(editor: editorCommon.ICommonCodeEditor): CommonFindController { + return editor.getContribution(CommonFindController.ID); + } + + constructor(editor: editorCommon.ICommonCodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @IStorageService storageService: IStorageService) { + super(); + this._editor = editor; + this._findWidgetVisible = CONTEXT_FIND_WIDGET_VISIBLE.bindTo(contextKeyService); + this._storageService = storageService; + + this._updateHistoryDelayer = new Delayer(500); + this._currentHistoryNavigator = new HistoryNavigator(); + this._state = this._register(new FindReplaceState()); + this.loadQueryState(); + this._register(this._state.addChangeListener((e) => this._onStateChanged(e))); + + this._model = null; + + this._register(this._editor.onDidChangeModel(() => { + let shouldRestartFind = (this._editor.getModel() && this._state.isRevealed); + + this.disposeModel(); + + this._state.change({ + searchScope: null, + matchCase: this._storageService.getBoolean('editor.matchCase', StorageScope.WORKSPACE, false), + wholeWord: this._storageService.getBoolean('editor.wholeWord', StorageScope.WORKSPACE, false), + isRegex: this._storageService.getBoolean('editor.isRegex', StorageScope.WORKSPACE, false) + }, false); + + if (shouldRestartFind) { + this._start({ + forceRevealReplace: false, + seedSearchStringFromSelection: false, + shouldFocus: FindStartFocusAction.NoFocusChange, + shouldAnimate: false, + }); + } + })); + } + + public dispose(): void { + this.disposeModel(); + super.dispose(); + } + + private disposeModel(): void { + if (this._model) { + this._model.dispose(); + this._model = null; + } + } + + public getId(): string { + return CommonFindController.ID; + } + + private _onStateChanged(e: FindReplaceStateChangedEvent): void { + this.saveQueryState(e); + + if (e.updateHistory && e.searchString) { + this._delayedUpdateHistory(); + } + if (e.isRevealed) { + if (this._state.isRevealed) { + this._findWidgetVisible.set(true); + } else { + this._findWidgetVisible.reset(); + this.disposeModel(); + } + } + } + + private saveQueryState(e: FindReplaceStateChangedEvent) { + if (e.isRegex && typeof this._state.isRegex !== 'undefined') { + this._storageService.store('editor.isRegex', this._state.isRegex, StorageScope.WORKSPACE); + } + if (e.wholeWord && typeof this._state.wholeWord !== 'undefined') { + this._storageService.store('editor.wholeWord', this._state.wholeWord, StorageScope.WORKSPACE); + } + if (e.matchCase && typeof this._state.matchCase !== 'undefined') { + this._storageService.store('editor.matchCase', this._state.matchCase, StorageScope.WORKSPACE); + } + } + + private loadQueryState() { + this._state.change({ + matchCase: this._storageService.getBoolean('editor.matchCase', StorageScope.WORKSPACE, this._state.matchCase), + wholeWord: this._storageService.getBoolean('editor.wholeWord', StorageScope.WORKSPACE, this._state.wholeWord), + isRegex: this._storageService.getBoolean('editor.isRegex', StorageScope.WORKSPACE, this._state.isRegex) + }, false); + } + + protected _delayedUpdateHistory() { + this._updateHistoryDelayer.trigger(this._updateHistory.bind(this)); + } + + protected _updateHistory() { + if (this._state.searchString) { + this._currentHistoryNavigator.add(this._state.searchString); + } + } + + public getState(): FindReplaceState { + return this._state; + } + + public getHistory(): HistoryNavigator { + return this._currentHistoryNavigator; + } + + public closeFindWidget(): void { + this._state.change({ + isRevealed: false, + searchScope: null + }, false); + this._editor.focus(); + } + + public toggleCaseSensitive(): void { + this._state.change({ matchCase: !this._state.matchCase }, false); + } + + public toggleWholeWords(): void { + this._state.change({ wholeWord: !this._state.wholeWord }, false); + } + + public toggleRegex(): void { + this._state.change({ isRegex: !this._state.isRegex }, false); + } + + public toggleSearchScope(): void { + if (this._state.searchScope) { + this._state.change({ searchScope: null }, true); + } else { + let selection = this._editor.getSelection(); + if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { + selection = selection.setEndPosition(selection.endLineNumber - 1, 1); + } + if (!selection.isEmpty()) { + this._state.change({ searchScope: selection }, true); + } + } + } + + public setSearchString(searchString: string): void { + this._state.change({ searchString: searchString }, false); + } + + public highlightFindOptions(): void { + // overwritten in subclass + } + + protected _start(opts: IFindStartOptions): void { + this.disposeModel(); + + if (!this._editor.getModel()) { + // cannot do anything with an editor that doesn't have a model... + return; + } + + let stateChanges: INewFindReplaceState = { + isRevealed: true + }; + + // Consider editor selection and overwrite the state with it + if (opts.seedSearchStringFromSelection && this._editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection) { + let selectionSearchString = getSelectionSearchString(this._editor); + if (selectionSearchString) { + if (this._state.isRegex) { + stateChanges.searchString = strings.escapeRegExpCharacters(selectionSearchString); + } else { + stateChanges.searchString = selectionSearchString; + } + } + } + + // Overwrite isReplaceRevealed + if (opts.forceRevealReplace) { + stateChanges.isReplaceRevealed = true; + } else if (!this._findWidgetVisible.get()) { + stateChanges.isReplaceRevealed = false; + } + + + this._state.change(stateChanges, false); + + if (!this._model) { + this._model = new FindModelBoundToEditorModel(this._editor, this._state); + } + } + + public start(opts: IFindStartOptions): void { + this._start(opts); + } + + public moveToNextMatch(): boolean { + if (this._model) { + this._model.moveToNextMatch(); + return true; + } + return false; + } + + public moveToPrevMatch(): boolean { + if (this._model) { + this._model.moveToPrevMatch(); + return true; + } + return false; + } + + public replace(): boolean { + if (this._model) { + this._model.replace(); + return true; + } + return false; + } + + public replaceAll(): boolean { + if (this._model) { + this._model.replaceAll(); + return true; + } + return false; + } + + public selectAllMatches(): boolean { + if (this._model) { + this._model.selectAllMatches(); + this._editor.focus(); + return true; + } + return false; + } + + public showPreviousFindTerm(): boolean { + let previousTerm = this._currentHistoryNavigator.previous(); + if (previousTerm) { + this._state.change({ searchString: previousTerm }, false, false); + } + return true; + } + + public showNextFindTerm(): boolean { + let nextTerm = this._currentHistoryNavigator.next(); + if (nextTerm) { + this._state.change({ searchString: nextTerm }, false, false); + } + return true; + } +} + +@editorAction +export class StartFindAction extends EditorAction { + + constructor() { + super({ + id: FIND_IDS.StartFindAction, + label: nls.localize('startFindAction', "Find"), + alias: 'Find', + precondition: null, + kbOpts: { + kbExpr: null, + primary: KeyMod.CtrlCmd | KeyCode.KEY_F, + mac: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F, + secondary: [KeyMod.CtrlCmd | KeyCode.KEY_E] + } + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let controller = CommonFindController.get(editor); + if (controller) { + controller.start({ + forceRevealReplace: false, + seedSearchStringFromSelection: true, + shouldFocus: FindStartFocusAction.FocusFindInput, + shouldAnimate: true + }); + } + } +} + +export abstract class MatchFindAction extends EditorAction { + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let controller = CommonFindController.get(editor); + if (controller && !this._run(controller)) { + controller.start({ + forceRevealReplace: false, + seedSearchStringFromSelection: (controller.getState().searchString.length === 0), + shouldFocus: FindStartFocusAction.NoFocusChange, + shouldAnimate: true + }); + this._run(controller); + } + } + + protected abstract _run(controller: CommonFindController): boolean; +} + +@editorAction +export class NextMatchFindAction extends MatchFindAction { + + constructor() { + super({ + id: FIND_IDS.NextMatchFindAction, + label: nls.localize('findNextMatchAction', "Find Next"), + alias: 'Find Next', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } + } + }); + } + + protected _run(controller: CommonFindController): boolean { + return controller.moveToNextMatch(); + } +} + +@editorAction +export class PreviousMatchFindAction extends MatchFindAction { + + constructor() { + super({ + id: FIND_IDS.PreviousMatchFindAction, + label: nls.localize('findPreviousMatchAction', "Find Previous"), + alias: 'Find Previous', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.Shift | KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] } + } + }); + } + + protected _run(controller: CommonFindController): boolean { + return controller.moveToPrevMatch(); + } +} + +export abstract class SelectionMatchFindAction extends EditorAction { + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let controller = CommonFindController.get(editor); + if (!controller) { + return; + } + let selectionSearchString = getSelectionSearchString(editor); + if (selectionSearchString) { + controller.setSearchString(selectionSearchString); + } + if (!this._run(controller)) { + controller.start({ + forceRevealReplace: false, + seedSearchStringFromSelection: false, + shouldFocus: FindStartFocusAction.NoFocusChange, + shouldAnimate: true + }); + this._run(controller); + } + } + + protected abstract _run(controller: CommonFindController): boolean; +} + +@editorAction +export class NextSelectionMatchFindAction extends SelectionMatchFindAction { + + constructor() { + super({ + id: FIND_IDS.NextSelectionMatchFindAction, + label: nls.localize('nextSelectionMatchFindAction', "Find Next Selection"), + alias: 'Find Next Selection', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyCode.F3 + } + }); + } + + protected _run(controller: CommonFindController): boolean { + return controller.moveToNextMatch(); + } +} + +@editorAction +export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction { + + constructor() { + super({ + id: FIND_IDS.PreviousSelectionMatchFindAction, + label: nls.localize('previousSelectionMatchFindAction', "Find Previous Selection"), + alias: 'Find Previous Selection', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F3 + } + }); + } + + protected _run(controller: CommonFindController): boolean { + return controller.moveToPrevMatch(); + } +} + +@editorAction +export class StartFindReplaceAction extends EditorAction { + + constructor() { + super({ + id: FIND_IDS.StartFindReplaceAction, + label: nls.localize('startReplace', "Replace"), + alias: 'Replace', + precondition: null, + kbOpts: { + kbExpr: null, + primary: KeyMod.CtrlCmd | KeyCode.KEY_H, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F } + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + if (editor.getConfiguration().readOnly) { + return; + } + + let controller = CommonFindController.get(editor); + let currentSelection = editor.getSelection(); + // we only seed search string from selection when the current selection is single line and not empty. + let seedSearchStringFromSelection = !currentSelection.isEmpty() && + currentSelection.startLineNumber === currentSelection.endLineNumber; + let oldSearchString = controller.getState().searchString; + // if the existing search string in find widget is empty and we don't seed search string from selection, it means the Find Input + // is still empty, so we should focus the Find Input instead of Replace Input. + let shouldFocus = (!!oldSearchString || seedSearchStringFromSelection) ? + FindStartFocusAction.FocusReplaceInput : FindStartFocusAction.FocusFindInput; + + if (controller) { + controller.start({ + forceRevealReplace: true, + seedSearchStringFromSelection: seedSearchStringFromSelection, + shouldFocus: shouldFocus, + shouldAnimate: true + }); + } + } +} + +export interface IMultiCursorFindInput { + changeFindSearchString: boolean; + allowMultiline: boolean; + highlightFindOptions: boolean; +} + +export interface IMultiCursorFindResult { + searchText: string; + matchCase: boolean; + wholeWord: boolean; + + currentMatch: Selection; +} + +function multiCursorFind(editor: editorCommon.ICommonCodeEditor, input: IMultiCursorFindInput): IMultiCursorFindResult { + let controller = CommonFindController.get(editor); + if (!controller) { + return null; + } + let state = controller.getState(); + let searchText: string; + let currentMatch: Selection; + + // In any case, if the find widget was ever opened, the options are taken from it + let wholeWord = state.wholeWord; + let matchCase = state.matchCase; + + // Find widget owns what we search for if: + // - focus is not in the editor (i.e. it is in the find widget) + // - and the search widget is visible + // - and the search string is non-empty + if (!editor.isFocused() && state.isRevealed && state.searchString.length > 0) { + // Find widget owns what is searched for + searchText = state.searchString; + } else { + // Selection owns what is searched for + let s = editor.getSelection(); + + if (s.startLineNumber !== s.endLineNumber && !input.allowMultiline) { + // multiline forbidden + return null; + } + + if (s.isEmpty()) { + // selection is empty => expand to current word + let word = editor.getModel().getWordAtPosition(s.getStartPosition()); + if (!word) { + return null; + } + searchText = word.word; + currentMatch = new Selection(s.startLineNumber, word.startColumn, s.startLineNumber, word.endColumn); + } else { + searchText = editor.getModel().getValueInRange(s).replace(/\r\n/g, '\n'); + } + if (input.changeFindSearchString) { + controller.setSearchString(searchText); + } + } + + if (input.highlightFindOptions) { + controller.highlightFindOptions(); + } + + return { + searchText: searchText, + matchCase: matchCase, + wholeWord: wholeWord, + currentMatch: currentMatch + }; +} + +export abstract class SelectNextFindMatchAction extends EditorAction { + protected _getNextMatch(editor: editorCommon.ICommonCodeEditor): Selection { + let r = multiCursorFind(editor, { + changeFindSearchString: true, + allowMultiline: true, + highlightFindOptions: true + }); + if (!r) { + return null; + } + if (r.currentMatch) { + return r.currentMatch; + } + + let allSelections = editor.getSelections(); + let lastAddedSelection = allSelections[allSelections.length - 1]; + + let nextMatch = editor.getModel().findNextMatch(r.searchText, lastAddedSelection.getEndPosition(), false, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null, false); + + if (!nextMatch) { + return null; + } + + return new Selection(nextMatch.range.startLineNumber, nextMatch.range.startColumn, nextMatch.range.endLineNumber, nextMatch.range.endColumn); + } +} + +export abstract class SelectPreviousFindMatchAction extends EditorAction { + protected _getPreviousMatch(editor: editorCommon.ICommonCodeEditor): Selection { + let r = multiCursorFind(editor, { + changeFindSearchString: true, + allowMultiline: true, + highlightFindOptions: true + }); + if (!r) { + return null; + } + if (r.currentMatch) { + return r.currentMatch; + } + + let allSelections = editor.getSelections(); + let lastAddedSelection = allSelections[allSelections.length - 1]; + + let previousMatch = editor.getModel().findPreviousMatch(r.searchText, lastAddedSelection.getStartPosition(), false, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null, false); + + if (!previousMatch) { + return null; + } + + return new Selection(previousMatch.range.startLineNumber, previousMatch.range.startColumn, previousMatch.range.endLineNumber, previousMatch.range.endColumn); + } +} + +@editorAction +export class AddSelectionToNextFindMatchAction extends SelectNextFindMatchAction { + + constructor() { + super({ + id: FIND_IDS.AddSelectionToNextFindMatchAction, + label: nls.localize('addSelectionToNextFindMatch', "Add Selection To Next Find Match"), + alias: 'Add Selection To Next Find Match', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyCode.KEY_D + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + const allSelections = editor.getSelections(); + + // If there are mulitple cursors, handle the case where they do not all select the same text. + if (allSelections.length > 1) { + const model = editor.getModel(); + const controller = CommonFindController.get(editor); + if (!controller) { + return; + } + const findState = controller.getState(); + const caseSensitive = findState.matchCase; + + let selectionsContainSameText = true; + + let selectedText = model.getValueInRange(allSelections[0]); + if (!caseSensitive) { + selectedText = selectedText.toLowerCase(); + } + for (let i = 1, len = allSelections.length; i < len; i++) { + let selection = allSelections[i]; + if (selection.isEmpty()) { + selectionsContainSameText = false; + break; + } + + let thisSelectedText = model.getValueInRange(selection); + if (!caseSensitive) { + thisSelectedText = thisSelectedText.toLowerCase(); + } + if (selectedText !== thisSelectedText) { + selectionsContainSameText = false; + break; + } + } + + if (!selectionsContainSameText) { + let resultingSelections: Selection[] = []; + for (let i = 0, len = allSelections.length; i < len; i++) { + let selection = allSelections[i]; + if (selection.isEmpty()) { + let word = editor.getModel().getWordAtPosition(selection.getStartPosition()); + if (word) { + resultingSelections[i] = new Selection(selection.startLineNumber, word.startColumn, selection.startLineNumber, word.endColumn); + continue; + } + } + resultingSelections[i] = selection; + } + editor.setSelections(resultingSelections); + return; + } + } + + let nextMatch = this._getNextMatch(editor); + + if (!nextMatch) { + return; + } + + editor.setSelections(allSelections.concat(nextMatch)); + editor.revealRangeInCenterIfOutsideViewport(nextMatch, editorCommon.ScrollType.Smooth); + } +} + +@editorAction +export class AddSelectionToPreviousFindMatchAction extends SelectPreviousFindMatchAction { + + constructor() { + super({ + id: FIND_IDS.AddSelectionToPreviousFindMatchAction, + label: nls.localize('addSelectionToPreviousFindMatch', "Add Selection To Previous Find Match"), + alias: 'Add Selection To Previous Find Match', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let previousMatch = this._getPreviousMatch(editor); + + if (!previousMatch) { + return; + } + + let allSelections = editor.getSelections(); + editor.setSelections(allSelections.concat(previousMatch)); + editor.revealRangeInCenterIfOutsideViewport(previousMatch, editorCommon.ScrollType.Smooth); + } +} + +@editorAction +export class MoveSelectionToNextFindMatchAction extends SelectNextFindMatchAction { + + constructor() { + super({ + id: FIND_IDS.MoveSelectionToNextFindMatchAction, + label: nls.localize('moveSelectionToNextFindMatch', "Move Last Selection To Next Find Match"), + alias: 'Move Last Selection To Next Find Match', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D) + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let nextMatch = this._getNextMatch(editor); + + if (!nextMatch) { + return; + } + + let allSelections = editor.getSelections(); + editor.setSelections(allSelections.slice(0, allSelections.length - 1).concat(nextMatch)); + editor.revealRangeInCenterIfOutsideViewport(nextMatch, editorCommon.ScrollType.Smooth); + } +} + +@editorAction +export class MoveSelectionToPreviousFindMatchAction extends SelectPreviousFindMatchAction { + + constructor() { + super({ + id: FIND_IDS.MoveSelectionToPreviousFindMatchAction, + label: nls.localize('moveSelectionToPreviousFindMatch', "Move Last Selection To Previous Find Match"), + alias: 'Move Last Selection To Previous Find Match', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let previousMatch = this._getPreviousMatch(editor); + + if (!previousMatch) { + return; + } + + let allSelections = editor.getSelections(); + editor.setSelections(allSelections.slice(0, allSelections.length - 1).concat(previousMatch)); + editor.revealRangeInCenterIfOutsideViewport(previousMatch, editorCommon.ScrollType.Smooth); + } +} + +export abstract class AbstractSelectHighlightsAction extends EditorAction { + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let controller = CommonFindController.get(editor); + if (!controller) { + return null; + } + + let matches: Range[] = null; + + const findState = controller.getState(); + if (findState.isRevealed && findState.isRegex && findState.searchString.length > 0) { + + matches = editor.getModel().findMatches(findState.searchString, true, findState.isRegex, findState.matchCase, findState.wholeWord ? editor.getConfiguration().wordSeparators : null, false).map(m => m.range); + + } else { + + let r = multiCursorFind(editor, { + changeFindSearchString: true, + allowMultiline: true, + highlightFindOptions: true + }); + if (!r) { + return; + } + + matches = editor.getModel().findMatches(r.searchText, true, false, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null, false).map(m => m.range); + } + + if (matches.length > 0) { + let editorSelection = editor.getSelection(); + for (let i = 0, len = matches.length; i < len; i++) { + let match = matches[i]; + let intersection = match.intersectRanges(editorSelection); + if (intersection) { + // bingo! + matches.splice(i, 1); + matches.unshift(match); + break; + } + } + editor.setSelections(matches.map(m => new Selection(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn))); + } + } +} + +@editorAction +export class SelectHighlightsAction extends AbstractSelectHighlightsAction { + constructor() { + super({ + id: 'editor.action.selectHighlights', + label: nls.localize('selectAllOccurrencesOfFindMatch', "Select All Occurrences of Find Match"), + alias: 'Select All Occurrences of Find Match', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L + } + }); + } +} + +@editorAction +export class CompatChangeAll extends AbstractSelectHighlightsAction { + constructor() { + super({ + id: 'editor.action.changeAll', + label: nls.localize('changeAll.label', "Change All Occurrences"), + alias: 'Change All Occurrences', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.F2 + }, + menuOpts: { + group: '1_modification', + order: 1.2 + } + }); + } +} + +class SelectionHighlighterState { + public readonly lastWordUnderCursor: Selection; + public readonly searchText: string; + public readonly matchCase: boolean; + public readonly wordSeparators: string; + + constructor(lastWordUnderCursor: Selection, searchText: string, matchCase: boolean, wordSeparators: string) { + this.searchText = searchText; + this.matchCase = matchCase; + this.wordSeparators = wordSeparators; + } + + /** + * Everything equals except for `lastWordUnderCursor` + */ + public static softEquals(a: SelectionHighlighterState, b: SelectionHighlighterState): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return ( + a.searchText === b.searchText + && a.matchCase === b.matchCase + && a.wordSeparators === b.wordSeparators + ); + } +} + +@commonEditorContribution +export class SelectionHighlighter extends Disposable implements editorCommon.IEditorContribution { + private static ID = 'editor.contrib.selectionHighlighter'; + + private editor: editorCommon.ICommonCodeEditor; + private _isEnabled: boolean; + private decorations: string[]; + private updateSoon: RunOnceScheduler; + private state: SelectionHighlighterState; + + constructor(editor: editorCommon.ICommonCodeEditor) { + super(); + this.editor = editor; + this._isEnabled = editor.getConfiguration().contribInfo.selectionHighlight; + this.decorations = []; + this.updateSoon = this._register(new RunOnceScheduler(() => this._update(), 300)); + this.state = null; + + this._register(editor.onDidChangeConfiguration((e) => { + this._isEnabled = editor.getConfiguration().contribInfo.selectionHighlight; + })); + this._register(editor.onDidChangeCursorSelection((e: ICursorSelectionChangedEvent) => { + + if (!this._isEnabled) { + // Early exit if nothing needs to be done! + // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) + return; + } + + if (e.selection.isEmpty()) { + if (e.reason === CursorChangeReason.Explicit) { + if (this.state && (!this.state.lastWordUnderCursor || !this.state.lastWordUnderCursor.containsPosition(e.selection.getStartPosition()))) { + // no longer valid + this._setState(null); + } + this.updateSoon.schedule(); + } else { + this._setState(null); + + } + } else { + this._update(); + } + })); + this._register(editor.onDidChangeModel((e) => { + this._setState(null); + })); + this._register(CommonFindController.get(editor).getState().addChangeListener((e) => { + this._update(); + })); + } + + public getId(): string { + return SelectionHighlighter.ID; + } + + private _update(): void { + this._setState(SelectionHighlighter._createState(this._isEnabled, this.editor)); + } + + private static _createState(isEnabled: boolean, editor: editorCommon.ICommonCodeEditor): SelectionHighlighterState { + const model = editor.getModel(); + if (!model) { + return null; + } + + const config = editor.getConfiguration(); + + let lastWordUnderCursor: Selection = null; + if (!isEnabled) { + return null; + } + + const r = multiCursorFind(editor, { + changeFindSearchString: false, + allowMultiline: false, + highlightFindOptions: false + }); + if (!r) { + return null; + } + + const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model); + if (r.currentMatch) { + // This is an empty selection + if (hasFindOccurrences) { + // Do not interfere with semantic word highlighting in the no selection case + return null; + } + + if (!config.contribInfo.occurrencesHighlight) { + return null; + } + + lastWordUnderCursor = r.currentMatch; + } + if (/^[ \t]+$/.test(r.searchText)) { + // whitespace only selection + return null; + } + if (r.searchText.length > 200) { + // very long selection + return null; + } + + const controller = CommonFindController.get(editor); + if (!controller) { + return null; + } + const findState = controller.getState(); + const caseSensitive = findState.matchCase; + + const selections = editor.getSelections(); + let firstSelectedText = model.getValueInRange(selections[0]); + if (!caseSensitive) { + firstSelectedText = firstSelectedText.toLowerCase(); + } + for (let i = 1; i < selections.length; i++) { + let selectedText = model.getValueInRange(selections[i]); + if (!caseSensitive) { + selectedText = selectedText.toLowerCase(); + } + if (firstSelectedText !== selectedText) { + // not all selections have the same text + return null; + } + } + + return new SelectionHighlighterState(lastWordUnderCursor, r.searchText, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null); + } + + + private _setState(state: SelectionHighlighterState): void { + if (SelectionHighlighterState.softEquals(this.state, state)) { + this.state = state; + return; + } + this.state = state; + + if (!this.state) { + if (this.decorations.length > 0) { + this.decorations = this.editor.deltaDecorations(this.decorations, []); + } + return; + } + + const model = this.editor.getModel(); + const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model); + + let allMatches = model.findMatches(this.state.searchText, true, false, this.state.matchCase, this.state.wordSeparators, false).map(m => m.range); + allMatches.sort(Range.compareRangesUsingStarts); + + let selections = this.editor.getSelections(); + selections.sort(Range.compareRangesUsingStarts); + + // do not overlap with selection (issue #64 and #512) + let matches: Range[] = []; + for (let i = 0, j = 0, len = allMatches.length, lenJ = selections.length; i < len;) { + const match = allMatches[i]; + + if (j >= lenJ) { + // finished all editor selections + matches.push(match); + i++; + } else { + const cmp = Range.compareRangesUsingStarts(match, selections[j]); + if (cmp < 0) { + // match is before sel + matches.push(match); + i++; + } else if (cmp > 0) { + // sel is before match + j++; + } else { + // sel is equal to match + i++; + j++; + } + } + } + + const decorations = matches.map(r => { + return { + range: r, + // Show in overviewRuler only if model has no semantic highlighting + options: (hasFindOccurrences ? SelectionHighlighter._SELECTION_HIGHLIGHT : SelectionHighlighter._SELECTION_HIGHLIGHT_OVERVIEW) + }; + }); + + this.decorations = this.editor.deltaDecorations(this.decorations, decorations); + } + + private static _SELECTION_HIGHLIGHT_OVERVIEW = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'selectionHighlight', + overviewRuler: { + color: themeColorFromId(overviewRulerSelectionHighlightForeground), + darkColor: themeColorFromId(overviewRulerSelectionHighlightForeground), + position: editorCommon.OverviewRulerLane.Center + } + }); + + private static _SELECTION_HIGHLIGHT = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'selectionHighlight', + }); + + public dispose(): void { + this._setState(null); + super.dispose(); + } +} + +@editorAction +export class ShowNextFindTermAction extends MatchFindAction { + + constructor() { + super({ + id: FIND_IDS.ShowNextFindTermAction, + label: nls.localize('showNextFindTermAction', "Show Next Find Term"), + alias: 'Show Next Find Term', + precondition: CONTEXT_FIND_WIDGET_VISIBLE, + kbOpts: { + weight: CommonEditorRegistry.commandWeight(5), + kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSED, EditorContextKeys.focus), + primary: ShowNextFindTermKeybinding.primary, + mac: ShowNextFindTermKeybinding.mac, + win: ShowNextFindTermKeybinding.win, + linux: ShowNextFindTermKeybinding.linux + } + }); + } + + protected _run(controller: CommonFindController): boolean { + return controller.showNextFindTerm(); + } +} + +@editorAction +export class ShpwPreviousFindTermAction extends MatchFindAction { + + constructor() { + super({ + id: FIND_IDS.ShowPreviousFindTermAction, + label: nls.localize('showPreviousFindTermAction', "Show Previous Find Term"), + alias: 'Find Show Previous Find Term', + precondition: CONTEXT_FIND_WIDGET_VISIBLE, + kbOpts: { + weight: CommonEditorRegistry.commandWeight(5), + kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSED, EditorContextKeys.focus), + primary: ShowPreviousFindTermKeybinding.primary, + mac: ShowPreviousFindTermKeybinding.mac, + win: ShowPreviousFindTermKeybinding.win, + linux: ShowPreviousFindTermKeybinding.linux + } + }); + } + + protected _run(controller: CommonFindController): boolean { + return controller.showPreviousFindTerm(); + } +} + +const FindCommand = EditorCommand.bindToContribution(CommonFindController.get); + +CommonEditorRegistry.registerEditorCommand(new FindCommand({ + id: FIND_IDS.CloseFindWidgetCommand, + precondition: CONTEXT_FIND_WIDGET_VISIBLE, + handler: x => x.closeFindWidget(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(5), + kbExpr: EditorContextKeys.focus, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } +})); + +CommonEditorRegistry.registerEditorCommand(new FindCommand({ + id: FIND_IDS.ToggleCaseSensitiveCommand, + precondition: null, + handler: x => x.toggleCaseSensitive(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(5), + kbExpr: EditorContextKeys.focus, + primary: ToggleCaseSensitiveKeybinding.primary, + mac: ToggleCaseSensitiveKeybinding.mac, + win: ToggleCaseSensitiveKeybinding.win, + linux: ToggleCaseSensitiveKeybinding.linux + } +})); + +CommonEditorRegistry.registerEditorCommand(new FindCommand({ + id: FIND_IDS.ToggleWholeWordCommand, + precondition: null, + handler: x => x.toggleWholeWords(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(5), + kbExpr: EditorContextKeys.focus, + primary: ToggleWholeWordKeybinding.primary, + mac: ToggleWholeWordKeybinding.mac, + win: ToggleWholeWordKeybinding.win, + linux: ToggleWholeWordKeybinding.linux + } +})); + +CommonEditorRegistry.registerEditorCommand(new FindCommand({ + id: FIND_IDS.ToggleRegexCommand, + precondition: null, + handler: x => x.toggleRegex(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(5), + kbExpr: EditorContextKeys.focus, + primary: ToggleRegexKeybinding.primary, + mac: ToggleRegexKeybinding.mac, + win: ToggleRegexKeybinding.win, + linux: ToggleRegexKeybinding.linux + } +})); + +CommonEditorRegistry.registerEditorCommand(new FindCommand({ + id: FIND_IDS.ToggleSearchScopeCommand, + precondition: null, + handler: x => x.toggleSearchScope(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(5), + kbExpr: EditorContextKeys.focus, + primary: ToggleSearchScopeKeybinding.primary, + mac: ToggleSearchScopeKeybinding.mac, + win: ToggleSearchScopeKeybinding.win, + linux: ToggleSearchScopeKeybinding.linux + } +})); + +CommonEditorRegistry.registerEditorCommand(new FindCommand({ + id: FIND_IDS.ReplaceOneAction, + precondition: CONTEXT_FIND_WIDGET_VISIBLE, + handler: x => x.replace(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(5), + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_1 + } +})); + +CommonEditorRegistry.registerEditorCommand(new FindCommand({ + id: FIND_IDS.ReplaceAllAction, + precondition: CONTEXT_FIND_WIDGET_VISIBLE, + handler: x => x.replaceAll(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(5), + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter + } +})); + +CommonEditorRegistry.registerEditorCommand(new FindCommand({ + id: FIND_IDS.SelectAllMatchesAction, + precondition: CONTEXT_FIND_WIDGET_VISIBLE, + handler: x => x.selectAllMatches(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(5), + kbExpr: EditorContextKeys.focus, + primary: KeyMod.Alt | KeyCode.Enter + } +})); \ No newline at end of file diff --git a/src/vs/editor/contrib/find/common/findDecorations.ts b/src/vs/editor/contrib/find/common/findDecorations.ts new file mode 100644 index 0000000000..9c46e4cb69 --- /dev/null +++ b/src/vs/editor/contrib/find/common/findDecorations.ts @@ -0,0 +1,192 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { editorFindMatchHighlight, editorFindMatch } from 'vs/platform/theme/common/colorRegistry'; +import { themeColorFromId } from 'vs/platform/theme/common/themeService'; + +export class FindDecorations implements IDisposable { + + private _editor: editorCommon.ICommonCodeEditor; + private _decorations: string[]; + private _findScopeDecorationId: string; + private _rangeHighlightDecorationId: string; + private _highlightedDecorationId: string; + private _startPosition: Position; + + constructor(editor: editorCommon.ICommonCodeEditor) { + this._editor = editor; + this._decorations = []; + this._findScopeDecorationId = null; + this._rangeHighlightDecorationId = null; + this._highlightedDecorationId = null; + this._startPosition = this._editor.getPosition(); + } + + public dispose(): void { + this._editor.deltaDecorations(this._allDecorations(), []); + + this._editor = null; + this._decorations = []; + this._findScopeDecorationId = null; + this._rangeHighlightDecorationId = null; + this._highlightedDecorationId = null; + this._startPosition = null; + } + + public reset(): void { + this._decorations = []; + this._findScopeDecorationId = null; + this._rangeHighlightDecorationId = null; + this._highlightedDecorationId = null; + } + + public getCount(): number { + return this._decorations.length; + } + + public getFindScope(): Range { + if (this._findScopeDecorationId) { + return this._editor.getModel().getDecorationRange(this._findScopeDecorationId); + } + return null; + } + + public getStartPosition(): Position { + return this._startPosition; + } + + public setStartPosition(newStartPosition: Position): void { + this._startPosition = newStartPosition; + this.setCurrentFindMatch(null); + } + + public getCurrentMatchesPosition(desiredRange: Range): number { + for (let i = 0, len = this._decorations.length; i < len; i++) { + let range = this._editor.getModel().getDecorationRange(this._decorations[i]); + if (desiredRange.equalsRange(range)) { + return (i + 1); + } + } + return 1; + } + + public setCurrentFindMatch(nextMatch: Range): number { + let newCurrentDecorationId: string = null; + let matchPosition = 0; + if (nextMatch) { + for (let i = 0, len = this._decorations.length; i < len; i++) { + let range = this._editor.getModel().getDecorationRange(this._decorations[i]); + if (nextMatch.equalsRange(range)) { + newCurrentDecorationId = this._decorations[i]; + matchPosition = (i + 1); + break; + } + } + } + + if (this._highlightedDecorationId !== null || newCurrentDecorationId !== null) { + this._editor.changeDecorations((changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => { + if (this._highlightedDecorationId !== null) { + changeAccessor.changeDecorationOptions(this._highlightedDecorationId, FindDecorations.createFindMatchDecorationOptions(false)); + this._highlightedDecorationId = null; + } + if (newCurrentDecorationId !== null) { + this._highlightedDecorationId = newCurrentDecorationId; + changeAccessor.changeDecorationOptions(this._highlightedDecorationId, FindDecorations.createFindMatchDecorationOptions(true)); + } + if (this._rangeHighlightDecorationId !== null) { + changeAccessor.removeDecoration(this._rangeHighlightDecorationId); + this._rangeHighlightDecorationId = null; + } + if (newCurrentDecorationId !== null) { + let rng = this._editor.getModel().getDecorationRange(newCurrentDecorationId); + this._rangeHighlightDecorationId = changeAccessor.addDecoration(rng, FindDecorations._RANGE_HIGHLIGHT_DECORATION); + } + }); + } + + return matchPosition; + } + + public set(matches: Range[], findScope: Range): void { + let newDecorations: editorCommon.IModelDeltaDecoration[] = matches.map((match) => { + return { + range: match, + options: FindDecorations.createFindMatchDecorationOptions(false) + }; + }); + if (findScope) { + newDecorations.unshift({ + range: findScope, + options: FindDecorations._FIND_SCOPE_DECORATION + }); + } + let tmpDecorations = this._editor.deltaDecorations(this._allDecorations(), newDecorations); + + if (findScope) { + this._findScopeDecorationId = tmpDecorations.shift(); + } else { + this._findScopeDecorationId = null; + } + this._decorations = tmpDecorations; + this._rangeHighlightDecorationId = null; + this._highlightedDecorationId = null; + } + + private _allDecorations(): string[] { + let result: string[] = []; + result = result.concat(this._decorations); + if (this._findScopeDecorationId) { + result.push(this._findScopeDecorationId); + } + if (this._rangeHighlightDecorationId) { + result.push(this._rangeHighlightDecorationId); + } + return result; + } + + private static createFindMatchDecorationOptions(isCurrent: boolean): ModelDecorationOptions { + return (isCurrent ? this._CURRENT_FIND_MATCH_DECORATION : this._FIND_MATCH_DECORATION); + } + + private static _CURRENT_FIND_MATCH_DECORATION = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'currentFindMatch', + showIfCollapsed: true, + overviewRuler: { + color: themeColorFromId(editorFindMatch), + darkColor: themeColorFromId(editorFindMatch), + position: editorCommon.OverviewRulerLane.Center + } + }); + + private static _FIND_MATCH_DECORATION = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'findMatch', + showIfCollapsed: true, + overviewRuler: { + color: themeColorFromId(editorFindMatchHighlight), + darkColor: themeColorFromId(editorFindMatchHighlight), + position: editorCommon.OverviewRulerLane.Center + } + }); + + private static _RANGE_HIGHLIGHT_DECORATION = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'rangeHighlight', + isWholeLine: true + }); + + private static _FIND_SCOPE_DECORATION = ModelDecorationOptions.register({ + className: 'findScope', + isWholeLine: true + }); +} diff --git a/src/vs/editor/contrib/find/common/findModel.ts b/src/vs/editor/contrib/find/common/findModel.ts new file mode 100644 index 0000000000..824d375dbc --- /dev/null +++ b/src/vs/editor/contrib/find/common/findModel.ts @@ -0,0 +1,478 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { RunOnceScheduler } from 'vs/base/common/async'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/common/replacePattern'; +import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { FindDecorations } from './findDecorations'; +import { FindReplaceState, FindReplaceStateChangedEvent } from './findState'; +import { ReplaceAllCommand } from './replaceAllCommand'; +import { Selection } from 'vs/editor/common/core/selection'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Constants } from 'vs/editor/common/core/uint'; +import { SearchParams } from 'vs/editor/common/model/textModelSearch'; +import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; + +export const ToggleCaseSensitiveKeybinding: IKeybindings = { + primary: KeyMod.Alt | KeyCode.KEY_C, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } +}; +export const ToggleWholeWordKeybinding: IKeybindings = { + primary: KeyMod.Alt | KeyCode.KEY_W, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } +}; +export const ToggleRegexKeybinding: IKeybindings = { + primary: KeyMod.Alt | KeyCode.KEY_R, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } +}; +export const ToggleSearchScopeKeybinding: IKeybindings = { + primary: KeyMod.Alt | KeyCode.KEY_L, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L } +}; +export const ShowPreviousFindTermKeybinding: IKeybindings = { + primary: KeyMod.Alt | KeyCode.UpArrow +}; +export const ShowNextFindTermKeybinding: IKeybindings = { + primary: KeyMod.Alt | KeyCode.DownArrow +}; + +export const FIND_IDS = { + StartFindAction: 'actions.find', + NextMatchFindAction: 'editor.action.nextMatchFindAction', + PreviousMatchFindAction: 'editor.action.previousMatchFindAction', + NextSelectionMatchFindAction: 'editor.action.nextSelectionMatchFindAction', + PreviousSelectionMatchFindAction: 'editor.action.previousSelectionMatchFindAction', + AddSelectionToNextFindMatchAction: 'editor.action.addSelectionToNextFindMatch', + AddSelectionToPreviousFindMatchAction: 'editor.action.addSelectionToPreviousFindMatch', + MoveSelectionToNextFindMatchAction: 'editor.action.moveSelectionToNextFindMatch', + MoveSelectionToPreviousFindMatchAction: 'editor.action.moveSelectionToPreviousFindMatch', + StartFindReplaceAction: 'editor.action.startFindReplaceAction', + CloseFindWidgetCommand: 'closeFindWidget', + ToggleCaseSensitiveCommand: 'toggleFindCaseSensitive', + ToggleWholeWordCommand: 'toggleFindWholeWord', + ToggleRegexCommand: 'toggleFindRegex', + ToggleSearchScopeCommand: 'toggleFindInSelection', + ReplaceOneAction: 'editor.action.replaceOne', + ReplaceAllAction: 'editor.action.replaceAll', + SelectAllMatchesAction: 'editor.action.selectAllMatches', + ShowPreviousFindTermAction: 'find.history.showPrevious', + ShowNextFindTermAction: 'find.history.showNext' +}; + +export const MATCHES_LIMIT = 999; + +export class FindModelBoundToEditorModel { + + private _editor: editorCommon.ICommonCodeEditor; + private _state: FindReplaceState; + private _toDispose: IDisposable[]; + private _decorations: FindDecorations; + private _ignoreModelContentChanged: boolean; + + private _updateDecorationsScheduler: RunOnceScheduler; + private _isDisposed: boolean; + + constructor(editor: editorCommon.ICommonCodeEditor, state: FindReplaceState) { + this._editor = editor; + this._state = state; + this._toDispose = []; + this._isDisposed = false; + + this._decorations = new FindDecorations(editor); + this._toDispose.push(this._decorations); + + this._updateDecorationsScheduler = new RunOnceScheduler(() => this.research(false), 100); + this._toDispose.push(this._updateDecorationsScheduler); + + this._toDispose.push(this._editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => { + if ( + e.reason === CursorChangeReason.Explicit + || e.reason === CursorChangeReason.Undo + || e.reason === CursorChangeReason.Redo + ) { + this._decorations.setStartPosition(this._editor.getPosition()); + } + })); + + this._ignoreModelContentChanged = false; + this._toDispose.push(this._editor.onDidChangeModelContent((e) => { + if (this._ignoreModelContentChanged) { + return; + } + if (e.isFlush) { + // a model.setValue() was called + this._decorations.reset(); + } + this._decorations.setStartPosition(this._editor.getPosition()); + this._updateDecorationsScheduler.schedule(); + })); + + this._toDispose.push(this._state.addChangeListener((e) => this._onStateChanged(e))); + + this.research(false, this._state.searchScope); + } + + public dispose(): void { + this._isDisposed = true; + this._toDispose = dispose(this._toDispose); + } + + private _onStateChanged(e: FindReplaceStateChangedEvent): void { + if (this._isDisposed) { + // The find model is disposed during a find state changed event + return; + } + if (e.searchString || e.isReplaceRevealed || e.isRegex || e.wholeWord || e.matchCase || e.searchScope) { + if (e.searchScope) { + this.research(e.moveCursor, this._state.searchScope); + } else { + this.research(e.moveCursor); + } + } + } + + private static _getSearchRange(model: editorCommon.IModel, searchOnlyEditableRange: boolean, findScope: Range): Range { + let searchRange: Range; + + if (searchOnlyEditableRange) { + searchRange = model.getEditableRange(); + } else { + searchRange = model.getFullModelRange(); + } + + // If we have set now or before a find scope, use it for computing the search range + if (findScope) { + searchRange = searchRange.intersectRanges(findScope); + } + + return searchRange; + } + + private research(moveCursor: boolean, newFindScope?: Range): void { + let findScope: Range = null; + if (typeof newFindScope !== 'undefined') { + findScope = newFindScope; + } else { + findScope = this._decorations.getFindScope(); + } + if (findScope !== null) { + if (findScope.startLineNumber !== findScope.endLineNumber) { + // multiline find scope => expand to line starts / ends + findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber)); + } + } + + let findMatches = this._findMatches(findScope, false, MATCHES_LIMIT); + this._decorations.set(findMatches.map(match => match.range), findScope); + + this._state.changeMatchInfo( + this._decorations.getCurrentMatchesPosition(this._editor.getSelection()), + this._decorations.getCount(), + undefined + ); + + if (moveCursor) { + this._moveToNextMatch(this._decorations.getStartPosition()); + } + } + + private _hasMatches(): boolean { + return (this._state.matchesCount > 0); + } + + private _cannotFind(): boolean { + if (!this._hasMatches()) { + let findScope = this._decorations.getFindScope(); + if (findScope) { + // Reveal the selection so user is reminded that 'selection find' is on. + this._editor.revealRangeInCenterIfOutsideViewport(findScope, editorCommon.ScrollType.Smooth); + } + return true; + } + return false; + } + + private _setCurrentFindMatch(match: Range): void { + let matchesPosition = this._decorations.setCurrentFindMatch(match); + this._state.changeMatchInfo( + matchesPosition, + this._decorations.getCount(), + match + ); + + this._editor.setSelection(match); + this._editor.revealRangeInCenterIfOutsideViewport(match, editorCommon.ScrollType.Smooth); + } + + private _moveToPrevMatch(before: Position, isRecursed: boolean = false): void { + if (this._cannotFind()) { + return; + } + + let findScope = this._decorations.getFindScope(); + let searchRange = FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), this._state.isReplaceRevealed, findScope); + + // ...(----)...|... + if (searchRange.getEndPosition().isBefore(before)) { + before = searchRange.getEndPosition(); + } + + // ...|...(----)... + if (before.isBefore(searchRange.getStartPosition())) { + before = searchRange.getEndPosition(); + } + + let { lineNumber, column } = before; + let model = this._editor.getModel(); + + let position = new Position(lineNumber, column); + + let prevMatch = model.findPreviousMatch(this._state.searchString, position, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false); + + if (prevMatch && prevMatch.range.isEmpty() && prevMatch.range.getStartPosition().equals(position)) { + // Looks like we're stuck at this position, unacceptable! + + let isUsingLineStops = this._state.isRegex && ( + this._state.searchString.indexOf('^') >= 0 + || this._state.searchString.indexOf('$') >= 0 + ); + + if (isUsingLineStops || column === 1) { + if (lineNumber === 1) { + lineNumber = model.getLineCount(); + } else { + lineNumber--; + } + column = model.getLineMaxColumn(lineNumber); + } else { + column--; + } + + position = new Position(lineNumber, column); + prevMatch = model.findPreviousMatch(this._state.searchString, position, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false); + } + + if (!prevMatch) { + // there is precisely one match and selection is on top of it + return null; + } + + if (!isRecursed && !searchRange.containsRange(prevMatch.range)) { + return this._moveToPrevMatch(prevMatch.range.getStartPosition(), true); + } + + this._setCurrentFindMatch(prevMatch.range); + } + + public moveToPrevMatch(): void { + this._moveToPrevMatch(this._editor.getSelection().getStartPosition()); + } + + private _moveToNextMatch(after: Position): void { + let nextMatch = this._getNextMatch(after, false, true); + if (nextMatch) { + this._setCurrentFindMatch(nextMatch.range); + } + } + + private _getNextMatch(after: Position, captureMatches: boolean, forceMove: boolean, isRecursed: boolean = false): editorCommon.FindMatch { + if (this._cannotFind()) { + return null; + } + + let findScope = this._decorations.getFindScope(); + let searchRange = FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), this._state.isReplaceRevealed, findScope); + + // ...(----)...|... + if (searchRange.getEndPosition().isBefore(after)) { + after = searchRange.getStartPosition(); + } + + // ...|...(----)... + if (after.isBefore(searchRange.getStartPosition())) { + after = searchRange.getStartPosition(); + } + + let { lineNumber, column } = after; + let model = this._editor.getModel(); + + let position = new Position(lineNumber, column); + + let nextMatch = model.findNextMatch(this._state.searchString, position, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getConfiguration().wordSeparators : null, captureMatches); + + if (forceMove && nextMatch && nextMatch.range.isEmpty() && nextMatch.range.getStartPosition().equals(position)) { + // Looks like we're stuck at this position, unacceptable! + + let isUsingLineStops = this._state.isRegex && ( + this._state.searchString.indexOf('^') >= 0 + || this._state.searchString.indexOf('$') >= 0 + ); + + if (isUsingLineStops || column === model.getLineMaxColumn(lineNumber)) { + if (lineNumber === model.getLineCount()) { + lineNumber = 1; + } else { + lineNumber++; + } + column = 1; + } else { + column++; + } + + position = new Position(lineNumber, column); + nextMatch = model.findNextMatch(this._state.searchString, position, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getConfiguration().wordSeparators : null, captureMatches); + } + + if (!nextMatch) { + // there is precisely one match and selection is on top of it + return null; + } + + if (!isRecursed && !searchRange.containsRange(nextMatch.range)) { + return this._getNextMatch(nextMatch.range.getEndPosition(), captureMatches, forceMove, true); + } + + return nextMatch; + } + + public moveToNextMatch(): void { + this._moveToNextMatch(this._editor.getSelection().getEndPosition()); + } + + private _getReplacePattern(): ReplacePattern { + if (this._state.isRegex) { + return parseReplaceString(this._state.replaceString); + } + return ReplacePattern.fromStaticValue(this._state.replaceString); + } + + public replace(): void { + if (!this._hasMatches()) { + return; + } + + let replacePattern = this._getReplacePattern(); + let selection = this._editor.getSelection(); + let nextMatch = this._getNextMatch(selection.getStartPosition(), replacePattern.hasReplacementPatterns, false); + if (nextMatch) { + if (selection.equalsRange(nextMatch.range)) { + // selection sits on a find match => replace it! + let replaceString = replacePattern.buildReplaceString(nextMatch.matches); + + let command = new ReplaceCommand(selection, replaceString); + + this._executeEditorCommand('replace', command); + + this._decorations.setStartPosition(new Position(selection.startLineNumber, selection.startColumn + replaceString.length)); + this.research(true); + } else { + this._decorations.setStartPosition(this._editor.getPosition()); + this._setCurrentFindMatch(nextMatch.range); + } + } + } + + private _findMatches(findScope: Range, captureMatches: boolean, limitResultCount: number): editorCommon.FindMatch[] { + let searchRange = FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), this._state.isReplaceRevealed, findScope); + return this._editor.getModel().findMatches(this._state.searchString, searchRange, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getConfiguration().wordSeparators : null, captureMatches, limitResultCount); + } + + public replaceAll(): void { + if (!this._hasMatches()) { + return; + } + + const findScope = this._decorations.getFindScope(); + + if (findScope === null && this._state.matchesCount >= MATCHES_LIMIT) { + // Doing a replace on the entire file that is over 1k matches + this._largeReplaceAll(); + } else { + this._regularReplaceAll(findScope); + } + + this.research(false); + } + + private _largeReplaceAll(): void { + const searchParams = new SearchParams(this._state.searchString, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getConfiguration().wordSeparators : null); + const searchData = searchParams.parseSearchRequest(); + if (!searchData) { + return; + } + + const model = this._editor.getModel(); + const modelText = model.getValue(editorCommon.EndOfLinePreference.LF); + const fullModelRange = model.getFullModelRange(); + + const replacePattern = this._getReplacePattern(); + let resultText: string; + if (replacePattern.hasReplacementPatterns) { + resultText = modelText.replace(searchData.regex, function () { + return replacePattern.buildReplaceString(arguments); + }); + } else { + resultText = modelText.replace(searchData.regex, replacePattern.buildReplaceString(null)); + } + + let command = new ReplaceCommandThatPreservesSelection(fullModelRange, resultText, this._editor.getSelection()); + this._executeEditorCommand('replaceAll', command); + } + + private _regularReplaceAll(findScope: Range): void { + const replacePattern = this._getReplacePattern(); + // Get all the ranges (even more than the highlighted ones) + let matches = this._findMatches(findScope, replacePattern.hasReplacementPatterns, Constants.MAX_SAFE_SMALL_INTEGER); + + let replaceStrings: string[] = []; + for (let i = 0, len = matches.length; i < len; i++) { + replaceStrings[i] = replacePattern.buildReplaceString(matches[i].matches); + } + + let command = new ReplaceAllCommand(this._editor.getSelection(), matches.map(m => m.range), replaceStrings); + this._executeEditorCommand('replaceAll', command); + } + + public selectAllMatches(): void { + if (!this._hasMatches()) { + return; + } + + let findScope = this._decorations.getFindScope(); + + // Get all the ranges (even more than the highlighted ones) + let matches = this._findMatches(findScope, false, Constants.MAX_SAFE_SMALL_INTEGER); + let selections = matches.map(m => new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn)); + + // If one of the ranges is the editor selection, then maintain it as primary + let editorSelection = this._editor.getSelection(); + for (let i = 0, len = selections.length; i < len; i++) { + let sel = selections[i]; + if (sel.equalsRange(editorSelection)) { + selections = [editorSelection].concat(selections.slice(0, i)).concat(selections.slice(i + 1)); + break; + } + } + + this._editor.setSelections(selections); + } + + private _executeEditorCommand(source: string, command: editorCommon.ICommand): void { + try { + this._ignoreModelContentChanged = true; + this._editor.pushUndoStop(); + this._editor.executeCommand(source, command); + this._editor.pushUndoStop(); + } finally { + this._ignoreModelContentChanged = false; + } + } +} \ No newline at end of file diff --git a/src/vs/editor/contrib/find/common/findState.ts b/src/vs/editor/contrib/find/common/findState.ts new file mode 100644 index 0000000000..30db916921 --- /dev/null +++ b/src/vs/editor/contrib/find/common/findState.ts @@ -0,0 +1,219 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Range } from 'vs/editor/common/core/range'; + +export interface FindReplaceStateChangedEvent { + moveCursor: boolean; + updateHistory: boolean; + + searchString: boolean; + replaceString: boolean; + isRevealed: boolean; + isReplaceRevealed: boolean; + isRegex: boolean; + wholeWord: boolean; + matchCase: boolean; + searchScope: boolean; + matchesPosition: boolean; + matchesCount: boolean; + currentMatch: boolean; +} + +export interface INewFindReplaceState { + searchString?: string; + replaceString?: string; + isRevealed?: boolean; + isReplaceRevealed?: boolean; + isRegex?: boolean; + wholeWord?: boolean; + matchCase?: boolean; + searchScope?: Range; +} + +export class FindReplaceState implements IDisposable { + + private static _CHANGED_EVENT = 'changed'; + + private _searchString: string; + private _replaceString: string; + private _isRevealed: boolean; + private _isReplaceRevealed: boolean; + private _isRegex: boolean; + private _wholeWord: boolean; + private _matchCase: boolean; + private _searchScope: Range; + private _matchesPosition: number; + private _matchesCount: number; + private _currentMatch: Range; + private _eventEmitter: EventEmitter; + + public get searchString(): string { return this._searchString; } + public get replaceString(): string { return this._replaceString; } + public get isRevealed(): boolean { return this._isRevealed; } + public get isReplaceRevealed(): boolean { return this._isReplaceRevealed; } + public get isRegex(): boolean { return this._isRegex; } + public get wholeWord(): boolean { return this._wholeWord; } + public get matchCase(): boolean { return this._matchCase; } + public get searchScope(): Range { return this._searchScope; } + public get matchesPosition(): number { return this._matchesPosition; } + public get matchesCount(): number { return this._matchesCount; } + public get currentMatch(): Range { return this._currentMatch; } + + constructor() { + this._searchString = ''; + this._replaceString = ''; + this._isRevealed = false; + this._isReplaceRevealed = false; + this._isRegex = false; + this._wholeWord = false; + this._matchCase = false; + this._searchScope = null; + this._matchesPosition = 0; + this._matchesCount = 0; + this._currentMatch = null; + this._eventEmitter = new EventEmitter(); + } + + public dispose(): void { + this._eventEmitter.dispose(); + } + + public addChangeListener(listener: (e: FindReplaceStateChangedEvent) => void): IDisposable { + return this._eventEmitter.addListener(FindReplaceState._CHANGED_EVENT, listener); + } + + public changeMatchInfo(matchesPosition: number, matchesCount: number, currentMatch: Range): void { + let changeEvent: FindReplaceStateChangedEvent = { + moveCursor: false, + updateHistory: false, + searchString: false, + replaceString: false, + isRevealed: false, + isReplaceRevealed: false, + isRegex: false, + wholeWord: false, + matchCase: false, + searchScope: false, + matchesPosition: false, + matchesCount: false, + currentMatch: false + }; + let somethingChanged = false; + + if (matchesCount === 0) { + matchesPosition = 0; + } + if (matchesPosition > matchesCount) { + matchesPosition = matchesCount; + } + + if (this._matchesPosition !== matchesPosition) { + this._matchesPosition = matchesPosition; + changeEvent.matchesPosition = true; + somethingChanged = true; + } + if (this._matchesCount !== matchesCount) { + this._matchesCount = matchesCount; + changeEvent.matchesCount = true; + somethingChanged = true; + } + + if (typeof currentMatch !== 'undefined') { + if (!Range.equalsRange(this._currentMatch, currentMatch)) { + this._currentMatch = currentMatch; + changeEvent.currentMatch = true; + somethingChanged = true; + } + } + + if (somethingChanged) { + this._eventEmitter.emit(FindReplaceState._CHANGED_EVENT, changeEvent); + } + } + + public change(newState: INewFindReplaceState, moveCursor: boolean, updateHistory: boolean = true): void { + let changeEvent: FindReplaceStateChangedEvent = { + moveCursor: moveCursor, + updateHistory: updateHistory, + searchString: false, + replaceString: false, + isRevealed: false, + isReplaceRevealed: false, + isRegex: false, + wholeWord: false, + matchCase: false, + searchScope: false, + matchesPosition: false, + matchesCount: false, + currentMatch: false + }; + let somethingChanged = false; + + if (typeof newState.searchString !== 'undefined') { + if (this._searchString !== newState.searchString) { + this._searchString = newState.searchString; + changeEvent.searchString = true; + somethingChanged = true; + } + } + if (typeof newState.replaceString !== 'undefined') { + if (this._replaceString !== newState.replaceString) { + this._replaceString = newState.replaceString; + changeEvent.replaceString = true; + somethingChanged = true; + } + } + if (typeof newState.isRevealed !== 'undefined') { + if (this._isRevealed !== newState.isRevealed) { + this._isRevealed = newState.isRevealed; + changeEvent.isRevealed = true; + somethingChanged = true; + } + } + if (typeof newState.isReplaceRevealed !== 'undefined') { + if (this._isReplaceRevealed !== newState.isReplaceRevealed) { + this._isReplaceRevealed = newState.isReplaceRevealed; + changeEvent.isReplaceRevealed = true; + somethingChanged = true; + } + } + if (typeof newState.isRegex !== 'undefined') { + if (this._isRegex !== newState.isRegex) { + this._isRegex = newState.isRegex; + changeEvent.isRegex = true; + somethingChanged = true; + } + } + if (typeof newState.wholeWord !== 'undefined') { + if (this._wholeWord !== newState.wholeWord) { + this._wholeWord = newState.wholeWord; + changeEvent.wholeWord = true; + somethingChanged = true; + } + } + if (typeof newState.matchCase !== 'undefined') { + if (this._matchCase !== newState.matchCase) { + this._matchCase = newState.matchCase; + changeEvent.matchCase = true; + somethingChanged = true; + } + } + if (typeof newState.searchScope !== 'undefined') { + if (!Range.equalsRange(this._searchScope, newState.searchScope)) { + this._searchScope = newState.searchScope; + changeEvent.searchScope = true; + somethingChanged = true; + } + } + + if (somethingChanged) { + this._eventEmitter.emit(FindReplaceState._CHANGED_EVENT, changeEvent); + } + } +} diff --git a/src/vs/editor/contrib/find/common/replaceAllCommand.ts b/src/vs/editor/contrib/find/common/replaceAllCommand.ts new file mode 100644 index 0000000000..0592616a7a --- /dev/null +++ b/src/vs/editor/contrib/find/common/replaceAllCommand.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; + +interface IEditOperation { + range: Range; + text: string; +} + +export class ReplaceAllCommand implements editorCommon.ICommand { + + private _editorSelection: Selection; + private _trackedEditorSelectionId: string; + private _ranges: Range[]; + private _replaceStrings: string[]; + + constructor(editorSelection: Selection, ranges: Range[], replaceStrings: string[]) { + this._editorSelection = editorSelection; + this._ranges = ranges; + this._replaceStrings = replaceStrings; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + if (this._ranges.length > 0) { + // Collect all edit operations + var ops: IEditOperation[] = []; + for (var i = 0; i < this._ranges.length; i++) { + ops.push({ + range: this._ranges[i], + text: this._replaceStrings[i] + }); + } + + // Sort them in ascending order by range starts + ops.sort((o1, o2) => { + return Range.compareRangesUsingStarts(o1.range, o2.range); + }); + + // Merge operations that touch each other + var resultOps: IEditOperation[] = []; + var previousOp = ops[0]; + for (var i = 1; i < ops.length; i++) { + if (previousOp.range.endLineNumber === ops[i].range.startLineNumber && previousOp.range.endColumn === ops[i].range.startColumn) { + // These operations are one after another and can be merged + previousOp.range = previousOp.range.plusRange(ops[i].range); + previousOp.text = previousOp.text + ops[i].text; + } else { + resultOps.push(previousOp); + previousOp = ops[i]; + } + } + resultOps.push(previousOp); + + for (var i = 0; i < resultOps.length; i++) { + builder.addEditOperation(resultOps[i].range, resultOps[i].text); + } + } + + this._trackedEditorSelectionId = builder.trackSelection(this._editorSelection); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + return helper.getTrackedSelection(this._trackedEditorSelectionId); + } +} diff --git a/src/vs/editor/contrib/find/common/replacePattern.ts b/src/vs/editor/contrib/find/common/replacePattern.ts new file mode 100644 index 0000000000..2a0469143b --- /dev/null +++ b/src/vs/editor/contrib/find/common/replacePattern.ts @@ -0,0 +1,266 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from 'vs/base/common/charCode'; + +export class ReplacePattern { + + public static fromStaticValue(value: string): ReplacePattern { + return new ReplacePattern([ReplacePiece.staticValue(value)]); + } + + /** + * Assigned when the replace pattern is entirely static. + */ + private readonly _staticValue: string; + + public get hasReplacementPatterns(): boolean { + return this._staticValue === null; + } + + /** + * Assigned when the replace pattern has replacemend patterns. + */ + private readonly _pieces: ReplacePiece[]; + + constructor(pieces: ReplacePiece[]) { + if (!pieces || pieces.length === 0) { + this._staticValue = ''; + this._pieces = null; + } else if (pieces.length === 1 && pieces[0].staticValue !== null) { + this._staticValue = pieces[0].staticValue; + this._pieces = null; + } else { + this._staticValue = null; + this._pieces = pieces; + } + } + + public buildReplaceString(matches: string[]): string { + if (this._staticValue !== null) { + return this._staticValue; + } + + let result = ''; + for (let i = 0, len = this._pieces.length; i < len; i++) { + let piece = this._pieces[i]; + if (piece.staticValue !== null) { + // static value ReplacePiece + result += piece.staticValue; + continue; + } + + // match index ReplacePiece + result += ReplacePattern._substitute(piece.matchIndex, matches); + } + + return result; + } + + private static _substitute(matchIndex: number, matches: string[]): string { + if (matchIndex === 0) { + return matches[0]; + } + + let remainder = ''; + while (matchIndex > 0) { + if (matchIndex < matches.length) { + // A match can be undefined + let match = (matches[matchIndex] || ''); + return match + remainder; + } + remainder = String(matchIndex % 10) + remainder; + matchIndex = Math.floor(matchIndex / 10); + } + return '$' + remainder; + } +} + +/** + * A replace piece can either be a static string or an index to a specific match. + */ +export class ReplacePiece { + + public static staticValue(value: string): ReplacePiece { + return new ReplacePiece(value, -1); + } + + public static matchIndex(index: number): ReplacePiece { + return new ReplacePiece(null, index); + } + + public readonly staticValue: string; + public readonly matchIndex: number; + + private constructor(staticValue: string, matchIndex: number) { + this.staticValue = staticValue; + this.matchIndex = matchIndex; + } +} + +class ReplacePieceBuilder { + + private readonly _source: string; + private _lastCharIndex: number; + private readonly _result: ReplacePiece[]; + private _resultLen: number; + private _currentStaticPiece: string; + + constructor(source: string) { + this._source = source; + this._lastCharIndex = 0; + this._result = []; + this._resultLen = 0; + this._currentStaticPiece = ''; + } + + public emitUnchanged(toCharIndex: number): void { + this._emitStatic(this._source.substring(this._lastCharIndex, toCharIndex)); + this._lastCharIndex = toCharIndex; + } + + public emitStatic(value: string, toCharIndex: number): void { + this._emitStatic(value); + this._lastCharIndex = toCharIndex; + } + + private _emitStatic(value: string): void { + if (value.length === 0) { + return; + } + this._currentStaticPiece += value; + } + + public emitMatchIndex(index: number, toCharIndex: number): void { + if (this._currentStaticPiece.length !== 0) { + this._result[this._resultLen++] = ReplacePiece.staticValue(this._currentStaticPiece); + this._currentStaticPiece = ''; + } + this._result[this._resultLen++] = ReplacePiece.matchIndex(index); + this._lastCharIndex = toCharIndex; + } + + + public finalize(): ReplacePattern { + this.emitUnchanged(this._source.length); + if (this._currentStaticPiece.length !== 0) { + this._result[this._resultLen++] = ReplacePiece.staticValue(this._currentStaticPiece); + this._currentStaticPiece = ''; + } + return new ReplacePattern(this._result); + } +} + +/** + * \n => inserts a LF + * \t => inserts a TAB + * \\ => inserts a "\". + * $$ => inserts a "$". + * $& and $0 => inserts the matched substring. + * $n => Where n is a non-negative integer lesser than 100, inserts the nth parenthesized submatch string + * everything else stays untouched + * + * Also see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter + */ +export function parseReplaceString(replaceString: string): ReplacePattern { + if (!replaceString || replaceString.length === 0) { + return new ReplacePattern(null); + } + + let result = new ReplacePieceBuilder(replaceString); + + for (let i = 0, len = replaceString.length; i < len; i++) { + let chCode = replaceString.charCodeAt(i); + + if (chCode === CharCode.Backslash) { + + // move to next char + i++; + + if (i >= len) { + // string ends with a \ + break; + } + + let nextChCode = replaceString.charCodeAt(i); + // let replaceWithCharacter: string = null; + + switch (nextChCode) { + case CharCode.Backslash: + // \\ => inserts a "\" + result.emitUnchanged(i - 1); + result.emitStatic('\\', i + 1); + break; + case CharCode.n: + // \n => inserts a LF + result.emitUnchanged(i - 1); + result.emitStatic('\n', i + 1); + break; + case CharCode.t: + // \t => inserts a TAB + result.emitUnchanged(i - 1); + result.emitStatic('\t', i + 1); + break; + } + + continue; + } + + if (chCode === CharCode.DollarSign) { + + // move to next char + i++; + + if (i >= len) { + // string ends with a $ + break; + } + + let nextChCode = replaceString.charCodeAt(i); + + if (nextChCode === CharCode.DollarSign) { + // $$ => inserts a "$" + result.emitUnchanged(i - 1); + result.emitStatic('$', i + 1); + continue; + } + + if (nextChCode === CharCode.Digit0 || nextChCode === CharCode.Ampersand) { + // $& and $0 => inserts the matched substring. + result.emitUnchanged(i - 1); + result.emitMatchIndex(0, i + 1); + continue; + } + + if (CharCode.Digit1 <= nextChCode && nextChCode <= CharCode.Digit9) { + // $n + + let matchIndex = nextChCode - CharCode.Digit0; + + // peek next char to probe for $nn + if (i + 1 < len) { + let nextNextChCode = replaceString.charCodeAt(i + 1); + if (CharCode.Digit0 <= nextNextChCode && nextNextChCode <= CharCode.Digit9) { + // $nn + + // move to next char + i++; + matchIndex = matchIndex * 10 + (nextNextChCode - CharCode.Digit0); + + result.emitUnchanged(i - 2); + result.emitMatchIndex(matchIndex, i + 1); + continue; + } + } + + result.emitUnchanged(i - 1); + result.emitMatchIndex(matchIndex, i + 1); + continue; + } + } + } + + return result.finalize(); +} diff --git a/src/vs/editor/contrib/find/test/common/find.test.ts b/src/vs/editor/contrib/find/test/common/find.test.ts new file mode 100644 index 0000000000..bbea42bb3a --- /dev/null +++ b/src/vs/editor/contrib/find/test/common/find.test.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { + getSelectionSearchString +} from 'vs/editor/contrib/find/common/find'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; + + +suite('Find', () => { + + test('search string at position', () => { + withMockCodeEditor([ + 'ABC DEF', + '0123 456' + ], {}, (editor, cursor) => { + + // The cursor is at the very top, of the file, at the first ABC + let searchStringAtTop = getSelectionSearchString(editor); + assert.equal(searchStringAtTop, 'ABC'); + + // Move cursor to the end of ABC + editor.setPosition(new Position(1, 3)); + let searchStringAfterABC = getSelectionSearchString(editor); + assert.equal(searchStringAfterABC, 'ABC'); + + // Move cursor to DEF + editor.setPosition(new Position(1, 5)); + let searchStringInsideDEF = getSelectionSearchString(editor); + assert.equal(searchStringInsideDEF, 'DEF'); + + }); + }); + + test('search string with selection', () => { + withMockCodeEditor([ + 'ABC DEF', + '0123 456' + ], {}, (editor, cursor) => { + + // Select A of ABC + editor.setSelection(new Range(1, 1, 1, 2)); + let searchStringSelectionA = getSelectionSearchString(editor); + assert.equal(searchStringSelectionA, 'A'); + + // Select BC of ABC + editor.setSelection(new Range(1, 2, 1, 4)); + let searchStringSelectionBC = getSelectionSearchString(editor); + assert.equal(searchStringSelectionBC, 'BC'); + + // Select BC DE + editor.setSelection(new Range(1, 2, 1, 7)); + let searchStringSelectionBCDE = getSelectionSearchString(editor); + assert.equal(searchStringSelectionBCDE, 'BC DE'); + + }); + }); + + test('search string with multiline selection', () => { + withMockCodeEditor([ + 'ABC DEF', + '0123 456' + ], {}, (editor, cursor) => { + + // Select first line and newline + editor.setSelection(new Range(1, 1, 2, 1)); + let searchStringSelectionWholeLine = getSelectionSearchString(editor); + assert.equal(searchStringSelectionWholeLine, null); + + // Select first line and chunk of second + editor.setSelection(new Range(1, 1, 2, 4)); + let searchStringSelectionTwoLines = getSelectionSearchString(editor); + assert.equal(searchStringSelectionTwoLines, null); + + // Select end of first line newline and and chunk of second + editor.setSelection(new Range(1, 7, 2, 4)); + let searchStringSelectionSpanLines = getSelectionSearchString(editor); + assert.equal(searchStringSelectionSpanLines, null); + + }); + }); + +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/find/test/common/findController.test.ts b/src/vs/editor/contrib/find/test/common/findController.test.ts new file mode 100644 index 0000000000..379cf9a27b --- /dev/null +++ b/src/vs/editor/contrib/find/test/common/findController.test.ts @@ -0,0 +1,871 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Emitter } from 'vs/base/common/event'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Range } from 'vs/editor/common/core/range'; +import { EndOfLineSequence, ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon'; +import { + CommonFindController, FindStartFocusAction, IFindStartOptions, + NextMatchFindAction, StartFindAction, SelectHighlightsAction, + AddSelectionToNextFindMatchAction +} from 'vs/editor/contrib/find/common/findController'; +import { MockCodeEditor, withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { HistoryNavigator } from 'vs/base/common/history'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { Delayer } from 'vs/base/common/async'; + +class TestFindController extends CommonFindController { + + public hasFocus: boolean; + public delayUpdateHistory: boolean = false; + public delayedUpdateHistoryPromise: TPromise; + + private _delayedUpdateHistoryEvent: Emitter = new Emitter(); + + constructor(editor: ICommonCodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @IStorageService storageService: IStorageService) { + super(editor, contextKeyService, storageService); + this._updateHistoryDelayer = new Delayer(50); + } + + protected _start(opts: IFindStartOptions): void { + super._start(opts); + + if (opts.shouldFocus !== FindStartFocusAction.NoFocusChange) { + this.hasFocus = true; + } + } + + protected _delayedUpdateHistory() { + if (!this.delayedUpdateHistoryPromise) { + this.delayedUpdateHistoryPromise = new TPromise((c, e) => { + const disposable = this._delayedUpdateHistoryEvent.event(() => { + disposable.dispose(); + this.delayedUpdateHistoryPromise = null; + c(null); + }); + }); + } + if (this.delayUpdateHistory) { + super._delayedUpdateHistory(); + } else { + this._updateHistory(); + } + } + + protected _updateHistory() { + super._updateHistory(); + this._delayedUpdateHistoryEvent.fire(); + } +} + +function fromRange(rng: Range): number[] { + return [rng.startLineNumber, rng.startColumn, rng.endLineNumber, rng.endColumn]; +} + +suite('FindController', () => { + let queryState: { [key: string]: any; } = {}; + let serviceCollection = new ServiceCollection(); + serviceCollection.set(IStorageService, { + get: (key: string) => queryState[key], + getBoolean: (key: string) => !!queryState[key], + store: (key: string, value: any) => { queryState[key] = value; } + }); + + test('issue #1857: F3, Find Next, acts like "Find Under Cursor"', () => { + withMockCodeEditor([ + 'ABC', + 'ABC', + 'XYZ', + 'ABC' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + // The cursor is at the very top, of the file, at the first ABC + let findController = editor.registerAndInstantiateContribution(TestFindController); + let findState = findController.getState(); + let startFindAction = new StartFindAction(); + let nextMatchFindAction = new NextMatchFindAction(); + + // I hit Ctrl+F to show the Find dialog + startFindAction.run(null, editor); + + // I type ABC. + findState.change({ searchString: 'A' }, true); + findState.change({ searchString: 'AB' }, true); + findState.change({ searchString: 'ABC' }, true); + + // The first ABC is highlighted. + assert.deepEqual(fromRange(editor.getSelection()), [1, 1, 1, 4]); + + // I hit Esc to exit the Find dialog. + findController.closeFindWidget(); + findController.hasFocus = false; + + // The cursor is now at end of the first line, with ABC on that line highlighted. + assert.deepEqual(fromRange(editor.getSelection()), [1, 1, 1, 4]); + + // I hit delete to remove it and change the text to XYZ. + editor.pushUndoStop(); + editor.executeEdits('test', [EditOperation.delete(new Range(1, 1, 1, 4))]); + editor.executeEdits('test', [EditOperation.insert(new Position(1, 1), 'XYZ')]); + editor.pushUndoStop(); + + // At this point the text editor looks like this: + // XYZ + // ABC + // XYZ + // ABC + assert.equal(editor.getModel().getLineContent(1), 'XYZ'); + + // The cursor is at end of the first line. + assert.deepEqual(fromRange(editor.getSelection()), [1, 4, 1, 4]); + + // I hit F3 to "Find Next" to find the next occurrence of ABC, but instead it searches for XYZ. + nextMatchFindAction.run(null, editor); + + assert.equal(findState.searchString, 'ABC'); + assert.equal(findController.hasFocus, false); + + findController.dispose(); + }); + }); + + test('issue #3090: F3 does not loop with two matches on a single line', () => { + withMockCodeEditor([ + 'import nls = require(\'vs/nls\');' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + let nextMatchFindAction = new NextMatchFindAction(); + + editor.setPosition({ + lineNumber: 1, + column: 9 + }); + + nextMatchFindAction.run(null, editor); + assert.deepEqual(fromRange(editor.getSelection()), [1, 26, 1, 29]); + + nextMatchFindAction.run(null, editor); + assert.deepEqual(fromRange(editor.getSelection()), [1, 8, 1, 11]); + + findController.dispose(); + }); + }); + + test('issue #6149: Auto-escape highlighted text for search and replace regex mode', () => { + withMockCodeEditor([ + 'var x = (3 * 5)', + 'var y = (3 * 5)', + 'var z = (3 * 5)', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + let startFindAction = new StartFindAction(); + let nextMatchFindAction = new NextMatchFindAction(); + + editor.setSelection(new Selection(1, 9, 1, 13)); + + findController.toggleRegex(); + startFindAction.run(null, editor); + + nextMatchFindAction.run(null, editor); + assert.deepEqual(fromRange(editor.getSelection()), [2, 9, 2, 13]); + + nextMatchFindAction.run(null, editor); + assert.deepEqual(fromRange(editor.getSelection()), [1, 9, 1, 13]); + + findController.dispose(); + }); + }); + + test('issue #8817: Cursor position changes when you cancel multicursor', () => { + withMockCodeEditor([ + 'var x = (3 * 5)', + 'var y = (3 * 5)', + 'var z = (3 * 5)', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + let selectHighlightsAction = new SelectHighlightsAction(); + + editor.setSelection(new Selection(2, 9, 2, 16)); + + selectHighlightsAction.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [2, 9, 2, 16], + [1, 9, 1, 16], + [3, 9, 3, 16], + ]); + + editor.trigger('test', 'removeSecondaryCursors', null); + + assert.deepEqual(fromRange(editor.getSelection()), [2, 9, 2, 16]); + + findController.dispose(); + }); + }); + + test('issue #5400: "Select All Occurrences of Find Match" does not select all if find uses regex', () => { + withMockCodeEditor([ + 'something', + 'someething', + 'someeething', + 'nothing' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + let selectHighlightsAction = new SelectHighlightsAction(); + + editor.setSelection(new Selection(1, 1, 1, 1)); + findController.getState().change({ searchString: 'some+thing', isRegex: true, isRevealed: true }, false); + + selectHighlightsAction.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [1, 1, 1, 10], + [2, 1, 2, 11], + [3, 1, 3, 12], + ]); + + assert.equal(findController.getState().searchString, 'some+thing'); + + findController.dispose(); + }); + }); + + test('issue #9043: Clear search scope when find widget is hidden', () => { + withMockCodeEditor([ + 'var x = (3 * 5)', + 'var y = (3 * 5)', + 'var z = (3 * 5)', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + findController.start({ + forceRevealReplace: false, + seedSearchStringFromSelection: false, + shouldFocus: FindStartFocusAction.NoFocusChange, + shouldAnimate: false + }); + + assert.equal(findController.getState().searchScope, null); + + findController.getState().change({ + searchScope: new Range(1, 1, 1, 5) + }, false); + + assert.deepEqual(findController.getState().searchScope, new Range(1, 1, 1, 5)); + + findController.closeFindWidget(); + assert.equal(findController.getState().searchScope, null); + }); + }); + + test('find term is added to history on state change', () => { + withMockCodeEditor([ + 'var x = (3 * 5)', + 'var y = (3 * 5)', + 'var z = (3 * 5)', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + findController.getState().change({ searchString: '1' }, false); + findController.getState().change({ searchString: '2' }, false); + findController.getState().change({ searchString: '3' }, false); + + assert.deepEqual(['1', '2', '3'], toArray(findController.getHistory())); + }); + }); + + test('find term is added with delay', (done) => { + withMockCodeEditor([ + 'var x = (3 * 5)', + 'var y = (3 * 5)', + 'var z = (3 * 5)', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + findController.delayUpdateHistory = true; + findController.getState().change({ searchString: '1' }, false); + findController.getState().change({ searchString: '2' }, false); + findController.getState().change({ searchString: '3' }, false); + + findController.delayedUpdateHistoryPromise.then(() => { + assert.deepEqual(['3'], toArray(findController.getHistory())); + done(); + }); + }); + }); + + test('show previous find term', () => { + withMockCodeEditor([ + 'var x = (3 * 5)', + 'var y = (3 * 5)', + 'var z = (3 * 5)', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + findController.getState().change({ searchString: '1' }, false); + findController.getState().change({ searchString: '2' }, false); + findController.getState().change({ searchString: '3' }, false); + + findController.showPreviousFindTerm(); + assert.deepEqual('2', findController.getState().searchString); + }); + }); + + test('show previous find term do not update history', () => { + withMockCodeEditor([ + 'var x = (3 * 5)', + 'var y = (3 * 5)', + 'var z = (3 * 5)', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + findController.getState().change({ searchString: '1' }, false); + findController.getState().change({ searchString: '2' }, false); + findController.getState().change({ searchString: '3' }, false); + + findController.showPreviousFindTerm(); + assert.deepEqual(['1', '2', '3'], toArray(findController.getHistory())); + }); + }); + + test('show next find term', () => { + withMockCodeEditor([ + 'var x = (3 * 5)', + 'var y = (3 * 5)', + 'var z = (3 * 5)', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + findController.getState().change({ searchString: '1' }, false); + findController.getState().change({ searchString: '2' }, false); + findController.getState().change({ searchString: '3' }, false); + findController.getState().change({ searchString: '4' }, false); + + findController.showPreviousFindTerm(); + findController.showPreviousFindTerm(); + findController.showNextFindTerm(); + assert.deepEqual('3', findController.getState().searchString); + }); + }); + + test('show next find term do not update history', () => { + withMockCodeEditor([ + 'var x = (3 * 5)', + 'var y = (3 * 5)', + 'var z = (3 * 5)', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + findController.getState().change({ searchString: '1' }, false); + findController.getState().change({ searchString: '2' }, false); + findController.getState().change({ searchString: '3' }, false); + findController.getState().change({ searchString: '4' }, false); + + findController.showPreviousFindTerm(); + findController.showPreviousFindTerm(); + findController.showNextFindTerm(); + assert.deepEqual(['1', '2', '3', '4'], toArray(findController.getHistory())); + }); + }); + + test('AddSelectionToNextFindMatchAction can work with multiline', () => { + withMockCodeEditor([ + '', + 'qwe', + 'rty', + '', + 'qwe', + '', + 'rty', + 'qwe', + 'rty' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); + + editor.setSelection(new Selection(2, 1, 3, 4)); + + addSelectionToNextFindMatch.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [2, 1, 3, 4], + [8, 1, 9, 4] + ]); + + editor.trigger('test', 'removeSecondaryCursors', null); + + assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 3, 4]); + + findController.dispose(); + }); + }); + + test('issue #6661: AddSelectionToNextFindMatchAction can work with touching ranges', () => { + withMockCodeEditor([ + 'abcabc', + 'abc', + 'abcabc', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); + + editor.setSelection(new Selection(1, 1, 1, 4)); + + addSelectionToNextFindMatch.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [1, 1, 1, 4], + [1, 4, 1, 7] + ]); + + addSelectionToNextFindMatch.run(null, editor); + addSelectionToNextFindMatch.run(null, editor); + addSelectionToNextFindMatch.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [1, 1, 1, 4], + [1, 4, 1, 7], + [2, 1, 2, 4], + [3, 1, 3, 4], + [3, 4, 3, 7] + ]); + + editor.trigger('test', Handler.Type, { text: 'z' }); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [1, 2, 1, 2], + [1, 3, 1, 3], + [2, 2, 2, 2], + [3, 2, 3, 2], + [3, 3, 3, 3] + ]); + assert.equal(editor.getValue(), [ + 'zz', + 'z', + 'zz', + ].join('\n')); + + findController.dispose(); + }); + }); + + test('issue #23541: Multiline Ctrl+D does not work in CRLF files', () => { + withMockCodeEditor([ + '', + 'qwe', + 'rty', + '', + 'qwe', + '', + 'rty', + 'qwe', + 'rty' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + editor.getModel().setEOL(EndOfLineSequence.CRLF); + + let findController = editor.registerAndInstantiateContribution(TestFindController); + let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); + + editor.setSelection(new Selection(2, 1, 3, 4)); + + addSelectionToNextFindMatch.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [2, 1, 3, 4], + [8, 1, 9, 4] + ]); + + editor.trigger('test', 'removeSecondaryCursors', null); + + assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 3, 4]); + + findController.dispose(); + }); + }); + + test('issue #18111: Regex replace with single space replaces with no space', () => { + withMockCodeEditor([ + 'HRESULT OnAmbientPropertyChange(DISPID dispid);' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + + let startFindAction = new StartFindAction(); + startFindAction.run(null, editor); + + findController.getState().change({ searchString: '\\b\\s{3}\\b', replaceString: ' ', isRegex: true }, false); + findController.moveToNextMatch(); + + assert.deepEqual(editor.getSelections().map(fromRange), [ + [1, 39, 1, 42] + ]); + + findController.replace(); + + assert.deepEqual(editor.getValue(), 'HRESULT OnAmbientPropertyChange(DISPID dispid);'); + + findController.dispose(); + }); + }); + + test('issue #24714: Regular expression with ^ in search & replace', () => { + withMockCodeEditor([ + '', + 'line2', + 'line3' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + + let startFindAction = new StartFindAction(); + startFindAction.run(null, editor); + + findController.getState().change({ searchString: '^', replaceString: 'x', isRegex: true }, false); + findController.moveToNextMatch(); + + assert.deepEqual(editor.getSelections().map(fromRange), [ + [2, 1, 2, 1] + ]); + + findController.replace(); + + assert.deepEqual(editor.getValue(), '\nxline2\nline3'); + + findController.dispose(); + }); + }); + + function toArray(historyNavigator: HistoryNavigator): string[] { + let result = []; + historyNavigator.first(); + if (historyNavigator.current()) { + do { + result.push(historyNavigator.current()); + } while (historyNavigator.next()); + } + return result; + } + + function testAddSelectionToNextFindMatchAction(text: string[], callback: (editor: MockCodeEditor, action: AddSelectionToNextFindMatchAction, findController: TestFindController) => void): void { + withMockCodeEditor(text, { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(TestFindController); + + let action = new AddSelectionToNextFindMatchAction(); + + callback(editor, action, findController); + + findController.dispose(); + }); + } + + test('AddSelectionToNextFindMatchAction starting with single collapsed selection', () => { + const text = [ + 'abc pizza', + 'abc house', + 'abc bar' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 2, 1, 2), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + }); + }); + + test('AddSelectionToNextFindMatchAction starting with two selections, one being collapsed 1)', () => { + const text = [ + 'abc pizza', + 'abc house', + 'abc bar' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 1, 1, 4), + new Selection(2, 2, 2, 2), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + }); + }); + + test('AddSelectionToNextFindMatchAction starting with two selections, one being collapsed 2)', () => { + const text = [ + 'abc pizza', + 'abc house', + 'abc bar' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 2, 1, 2), + new Selection(2, 1, 2, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + }); + }); + + test('AddSelectionToNextFindMatchAction starting with all collapsed selections', () => { + const text = [ + 'abc pizza', + 'abc house', + 'abc bar' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 2, 1, 2), + new Selection(2, 2, 2, 2), + new Selection(3, 1, 3, 1), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + }); + }); + + test('AddSelectionToNextFindMatchAction starting with all collapsed selections on different words', () => { + const text = [ + 'abc pizza', + 'abc house', + 'abc bar' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 6, 1, 6), + new Selection(2, 6, 2, 6), + new Selection(3, 6, 3, 6), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 5, 1, 10), + new Selection(2, 5, 2, 10), + new Selection(3, 5, 3, 8), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 5, 1, 10), + new Selection(2, 5, 2, 10), + new Selection(3, 5, 3, 8), + ]); + }); + }); + + test('issue #20651: AddSelectionToNextFindMatchAction case insensitive', () => { + const text = [ + 'test', + 'testte', + 'Test', + 'testte', + 'test' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 1, 1, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 5), + new Selection(2, 1, 2, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 5), + new Selection(2, 1, 2, 5), + new Selection(3, 1, 3, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 5), + new Selection(2, 1, 2, 5), + new Selection(3, 1, 3, 5), + new Selection(4, 1, 4, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 5), + new Selection(2, 1, 2, 5), + new Selection(3, 1, 3, 5), + new Selection(4, 1, 4, 5), + new Selection(5, 1, 5, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 5), + new Selection(2, 1, 2, 5), + new Selection(3, 1, 3, 5), + new Selection(4, 1, 4, 5), + new Selection(5, 1, 5, 5), + ]); + }); + }); +}); + +suite('FindController query options persistence', () => { + let queryState: { [key: string]: any; } = {}; + queryState['editor.isRegex'] = false; + queryState['editor.matchCase'] = false; + queryState['editor.wholeWord'] = false; + let serviceCollection = new ServiceCollection(); + serviceCollection.set(IStorageService, { + get: (key: string) => queryState[key], + getBoolean: (key: string) => !!queryState[key], + store: (key: string, value: any) => { queryState[key] = value; } + }); + + test('matchCase', () => { + withMockCodeEditor([ + 'abc', + 'ABC', + 'XYZ', + 'ABC' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + queryState = { 'editor.isRegex': false, 'editor.matchCase': true, 'editor.wholeWord': false }; + // The cursor is at the very top, of the file, at the first ABC + let findController = editor.registerAndInstantiateContribution(TestFindController); + let findState = findController.getState(); + let startFindAction = new StartFindAction(); + + // I hit Ctrl+F to show the Find dialog + startFindAction.run(null, editor); + + // I type ABC. + findState.change({ searchString: 'ABC' }, true); + // The second ABC is highlighted as matchCase is true. + assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 2, 4]); + + findController.dispose(); + }); + }); + + queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; + + test('wholeWord', () => { + withMockCodeEditor([ + 'ABC', + 'AB', + 'XYZ', + 'ABC' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; + // The cursor is at the very top, of the file, at the first ABC + let findController = editor.registerAndInstantiateContribution(TestFindController); + let findState = findController.getState(); + let startFindAction = new StartFindAction(); + + // I hit Ctrl+F to show the Find dialog + startFindAction.run(null, editor); + + // I type AB. + findState.change({ searchString: 'AB' }, true); + // The second AB is highlighted as wholeWord is true. + assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 2, 3]); + + findController.dispose(); + }); + }); + + test('toggling options is saved', () => { + withMockCodeEditor([ + 'ABC', + 'AB', + 'XYZ', + 'ABC' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; + // The cursor is at the very top, of the file, at the first ABC + let findController = editor.registerAndInstantiateContribution(TestFindController); + findController.toggleRegex(); + assert.equal(queryState['editor.isRegex'], true); + + findController.dispose(); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/find/test/common/findModel.test.ts b/src/vs/editor/contrib/find/test/common/findModel.test.ts new file mode 100644 index 0000000000..4c3503aaf2 --- /dev/null +++ b/src/vs/editor/contrib/find/test/common/findModel.test.ts @@ -0,0 +1,2037 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Cursor } from 'vs/editor/common/controller/cursor'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Range } from 'vs/editor/common/core/range'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { FindModelBoundToEditorModel } from 'vs/editor/contrib/find/common/findModel'; +import { FindReplaceState } from 'vs/editor/contrib/find/common/findState'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { CoreNavigationCommands } from 'vs/editor/common/controller/coreCommands'; + +suite('FindModel', () => { + + function findTest(testName: string, callback: (editor: ICommonCodeEditor, cursor: Cursor) => void): void { + test(testName, () => { + withMockCodeEditor([ + '// my cool header', + '#include "cool.h"', + '#include ', + '', + 'int main() {', + ' cout << "hello world, Hello!" << endl;', + ' cout << "hello world again" << endl;', + ' cout << "Hello world again" << endl;', + ' cout << "helloworld again" << endl;', + '}', + '// blablablaciao', + '' + ], {}, callback); + }); + } + + function fromRange(rng: Range): number[] { + return [rng.startLineNumber, rng.startColumn, rng.endLineNumber, rng.endColumn]; + } + + function _getFindState(editor: ICommonCodeEditor) { + let model = editor.getModel(); + let currentFindMatches: Range[] = []; + let allFindMatches: Range[] = []; + + for (let dec of model.getAllDecorations()) { + if (dec.options.className === 'currentFindMatch') { + currentFindMatches.push(dec.range); + allFindMatches.push(dec.range); + } else if (dec.options.className === 'findMatch') { + allFindMatches.push(dec.range); + } + } + + currentFindMatches.sort(Range.compareRangesUsingStarts); + allFindMatches.sort(Range.compareRangesUsingStarts); + + return { + highlighted: currentFindMatches.map(fromRange), + findDecorations: allFindMatches.map(fromRange) + }; + } + + function assertFindState(editor: ICommonCodeEditor, cursor: number[], highlighted: number[], findDecorations: number[][]): void { + assert.deepEqual(fromRange(editor.getSelection()), cursor, 'cursor'); + + let expectedState = { + highlighted: highlighted ? [highlighted] : [], + findDecorations: findDecorations + }; + assert.deepEqual(_getFindState(editor), expectedState, 'state'); + } + + findTest('incremental find from beginning of file', (editor, cursor) => { + editor.setPosition({ lineNumber: 1, column: 1 }); + let findState = new FindReplaceState(); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + // simulate typing the search string + findState.change({ searchString: 'H' }, true); + assertFindState( + editor, + [1, 12, 1, 13], + [1, 12, 1, 13], + [ + [1, 12, 1, 13], + [2, 16, 2, 17], + [6, 14, 6, 15], + [6, 27, 6, 28], + [7, 14, 7, 15], + [8, 14, 8, 15], + [9, 14, 9, 15] + ] + ); + + // simulate typing the search string + findState.change({ searchString: 'He' }, true); + assertFindState( + editor, + [1, 12, 1, 14], + [1, 12, 1, 14], + [ + [1, 12, 1, 14], + [6, 14, 6, 16], + [6, 27, 6, 29], + [7, 14, 7, 16], + [8, 14, 8, 16], + [9, 14, 9, 16] + ] + ); + + // simulate typing the search string + findState.change({ searchString: 'Hello' }, true); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19], + [9, 14, 9, 19] + ] + ); + + // simulate toggling on `matchCase` + findState.change({ matchCase: true }, true); + assertFindState( + editor, + [6, 27, 6, 32], + [6, 27, 6, 32], + [ + [6, 27, 6, 32], + [8, 14, 8, 19] + ] + ); + + // simulate typing the search string + findState.change({ searchString: 'hello' }, true); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [9, 14, 9, 19] + ] + ); + + // simulate toggling on `wholeWord` + findState.change({ wholeWord: true }, true); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19] + ] + ); + + // simulate toggling off `matchCase` + findState.change({ matchCase: false }, true); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + // simulate toggling off `wholeWord` + findState.change({ wholeWord: false }, true); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19], + [9, 14, 9, 19] + ] + ); + + // simulate adding a search scope + findState.change({ searchScope: new Range(8, 1, 10, 1) }, true); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [8, 14, 8, 19], + [9, 14, 9, 19] + ] + ); + + // simulate removing the search scope + findState.change({ searchScope: null }, true); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19], + [9, 14, 9, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find model removes its decorations', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello' }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assert.equal(findState.matchesCount, 5); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19], + [9, 14, 9, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + }); + + findTest('find model updates state matchesCount', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello' }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assert.equal(findState.matchesCount, 5); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19], + [9, 14, 9, 19] + ] + ); + + findState.change({ searchString: 'helloo' }, false); + assert.equal(findState.matchesCount, 0); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find model reacts to position change', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello' }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19], + [9, 14, 9, 19] + ] + ); + + editor.trigger('mouse', CoreNavigationCommands.MoveTo.id, { + position: new Position(6, 20) + }); + + assertFindState( + editor, + [6, 20, 6, 20], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19], + [9, 14, 9, 19] + ] + ); + + findState.change({ searchString: 'Hello' }, true); + assertFindState( + editor, + [6, 27, 6, 32], + [6, 27, 6, 32], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19], + [9, 14, 9, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find model next', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [6, 27, 6, 32], + [6, 27, 6, 32], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find model next stays in scope', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 9, 1) }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find model prev', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [6, 27, 6, 32], + [6, 27, 6, 32], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find model prev stays in scope', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 9, 1) }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find model next/prev with no matches', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'helloo', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find model next/prev respects cursor position', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + editor.trigger('mouse', CoreNavigationCommands.MoveTo.id, { + position: new Position(6, 20) + }); + assertFindState( + editor, + [6, 20, 6, 20], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [6, 27, 6, 32], + [6, 27, 6, 32], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find ^', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: '^', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [1, 1, 1, 1], + [2, 1, 2, 1], + [3, 1, 3, 1], + [4, 1, 4, 1], + [5, 1, 5, 1], + [6, 1, 6, 1], + [7, 1, 7, 1], + [8, 1, 8, 1], + [9, 1, 9, 1], + [10, 1, 10, 1], + [11, 1, 11, 1], + [12, 1, 12, 1], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [2, 1, 2, 1], + [2, 1, 2, 1], + [ + [1, 1, 1, 1], + [2, 1, 2, 1], + [3, 1, 3, 1], + [4, 1, 4, 1], + [5, 1, 5, 1], + [6, 1, 6, 1], + [7, 1, 7, 1], + [8, 1, 8, 1], + [9, 1, 9, 1], + [10, 1, 10, 1], + [11, 1, 11, 1], + [12, 1, 12, 1], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [3, 1, 3, 1], + [3, 1, 3, 1], + [ + [1, 1, 1, 1], + [2, 1, 2, 1], + [3, 1, 3, 1], + [4, 1, 4, 1], + [5, 1, 5, 1], + [6, 1, 6, 1], + [7, 1, 7, 1], + [8, 1, 8, 1], + [9, 1, 9, 1], + [10, 1, 10, 1], + [11, 1, 11, 1], + [12, 1, 12, 1], + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find $', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: '$', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [1, 18, 1, 18], + [2, 18, 2, 18], + [3, 20, 3, 20], + [4, 1, 4, 1], + [5, 13, 5, 13], + [6, 43, 6, 43], + [7, 41, 7, 41], + [8, 41, 8, 41], + [9, 40, 9, 40], + [10, 2, 10, 2], + [11, 17, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [1, 18, 1, 18], + [1, 18, 1, 18], + [ + [1, 18, 1, 18], + [2, 18, 2, 18], + [3, 20, 3, 20], + [4, 1, 4, 1], + [5, 13, 5, 13], + [6, 43, 6, 43], + [7, 41, 7, 41], + [8, 41, 8, 41], + [9, 40, 9, 40], + [10, 2, 10, 2], + [11, 17, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [2, 18, 2, 18], + [2, 18, 2, 18], + [ + [1, 18, 1, 18], + [2, 18, 2, 18], + [3, 20, 3, 20], + [4, 1, 4, 1], + [5, 13, 5, 13], + [6, 43, 6, 43], + [7, 41, 7, 41], + [8, 41, 8, 41], + [9, 40, 9, 40], + [10, 2, 10, 2], + [11, 17, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [3, 20, 3, 20], + [3, 20, 3, 20], + [ + [1, 18, 1, 18], + [2, 18, 2, 18], + [3, 20, 3, 20], + [4, 1, 4, 1], + [5, 13, 5, 13], + [6, 43, 6, 43], + [7, 41, 7, 41], + [8, 41, 8, 41], + [9, 40, 9, 40], + [10, 2, 10, 2], + [11, 17, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find next ^$', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: '^$', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [4, 1, 4, 1], + [12, 1, 12, 1], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [4, 1, 4, 1], + [4, 1, 4, 1], + [ + [4, 1, 4, 1], + [12, 1, 12, 1], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [12, 1, 12, 1], + [12, 1, 12, 1], + [ + [4, 1, 4, 1], + [12, 1, 12, 1], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [4, 1, 4, 1], + [4, 1, 4, 1], + [ + [4, 1, 4, 1], + [12, 1, 12, 1], + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find .*', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: '.*', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [1, 1, 1, 18], + [2, 1, 2, 18], + [3, 1, 3, 20], + [4, 1, 4, 1], + [5, 1, 5, 13], + [6, 1, 6, 43], + [7, 1, 7, 41], + [8, 1, 8, 41], + [9, 1, 9, 40], + [10, 1, 10, 2], + [11, 1, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find next ^.*$', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: '^.*$', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [1, 1, 1, 18], + [2, 1, 2, 18], + [3, 1, 3, 20], + [4, 1, 4, 1], + [5, 1, 5, 13], + [6, 1, 6, 43], + [7, 1, 7, 41], + [8, 1, 8, 41], + [9, 1, 9, 40], + [10, 1, 10, 2], + [11, 1, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [1, 1, 1, 18], + [1, 1, 1, 18], + [ + [1, 1, 1, 18], + [2, 1, 2, 18], + [3, 1, 3, 20], + [4, 1, 4, 1], + [5, 1, 5, 13], + [6, 1, 6, 43], + [7, 1, 7, 41], + [8, 1, 8, 41], + [9, 1, 9, 40], + [10, 1, 10, 2], + [11, 1, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [2, 1, 2, 18], + [2, 1, 2, 18], + [ + [1, 1, 1, 18], + [2, 1, 2, 18], + [3, 1, 3, 20], + [4, 1, 4, 1], + [5, 1, 5, 13], + [6, 1, 6, 43], + [7, 1, 7, 41], + [8, 1, 8, 41], + [9, 1, 9, 40], + [10, 1, 10, 2], + [11, 1, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find prev ^.*$', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: '^.*$', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [1, 1, 1, 18], + [2, 1, 2, 18], + [3, 1, 3, 20], + [4, 1, 4, 1], + [5, 1, 5, 13], + [6, 1, 6, 43], + [7, 1, 7, 41], + [8, 1, 8, 41], + [9, 1, 9, 40], + [10, 1, 10, 2], + [11, 1, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [12, 1, 12, 1], + [12, 1, 12, 1], + [ + [1, 1, 1, 18], + [2, 1, 2, 18], + [3, 1, 3, 20], + [4, 1, 4, 1], + [5, 1, 5, 13], + [6, 1, 6, 43], + [7, 1, 7, 41], + [8, 1, 8, 41], + [9, 1, 9, 40], + [10, 1, 10, 2], + [11, 1, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [11, 1, 11, 17], + [11, 1, 11, 17], + [ + [1, 1, 1, 18], + [2, 1, 2, 18], + [3, 1, 3, 20], + [4, 1, 4, 1], + [5, 1, 5, 13], + [6, 1, 6, 43], + [7, 1, 7, 41], + [8, 1, 8, 41], + [9, 1, 9, 40], + [10, 1, 10, 2], + [11, 1, 11, 17], + [12, 1, 12, 1], + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find prev ^$', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: '^$', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [4, 1, 4, 1], + [12, 1, 12, 1], + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [12, 1, 12, 1], + [12, 1, 12, 1], + [ + [4, 1, 4, 1], + [12, 1, 12, 1], + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [4, 1, 4, 1], + [4, 1, 4, 1], + [ + [4, 1, 4, 1], + [12, 1, 12, 1], + ] + ); + + findModel.moveToPrevMatch(); + assertFindState( + editor, + [12, 1, 12, 1], + [12, 1, 12, 1], + [ + [4, 1, 4, 1], + [12, 1, 12, 1], + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replace hello', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + editor.trigger('mouse', CoreNavigationCommands.MoveTo.id, { + position: new Position(6, 20) + }); + assertFindState( + editor, + [6, 20, 6, 20], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hello world, Hello!" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [6, 27, 6, 32], + [6, 27, 6, 32], + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hello world, Hello!" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hello world, hi!" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [6, 14, 6, 19], + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(7), ' cout << "hi world again" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(8), ' cout << "hi world again" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [6, 16, 6, 16], + null, + [] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hi world, hi!" << endl;'); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replace bla', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'bla', replaceString: 'ciao' }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [11, 4, 11, 7], + [11, 7, 11, 10], + [11, 10, 11, 13] + ] + ); + + findModel.replace(); + assertFindState( + editor, + [11, 4, 11, 7], + [11, 4, 11, 7], + [ + [11, 4, 11, 7], + [11, 7, 11, 10], + [11, 10, 11, 13] + ] + ); + assert.equal(editor.getModel().getLineContent(11), '// blablablaciao'); + + findModel.replace(); + assertFindState( + editor, + [11, 8, 11, 11], + [11, 8, 11, 11], + [ + [11, 8, 11, 11], + [11, 11, 11, 14] + ] + ); + assert.equal(editor.getModel().getLineContent(11), '// ciaoblablaciao'); + + findModel.replace(); + assertFindState( + editor, + [11, 12, 11, 15], + [11, 12, 11, 15], + [ + [11, 12, 11, 15] + ] + ); + assert.equal(editor.getModel().getLineContent(11), '// ciaociaoblaciao'); + + findModel.replace(); + assertFindState( + editor, + [11, 16, 11, 16], + null, + [] + ); + assert.equal(editor.getModel().getLineContent(11), '// ciaociaociaociao'); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replaceAll hello', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + editor.trigger('mouse', CoreNavigationCommands.MoveTo.id, { + position: new Position(6, 20) + }); + assertFindState( + editor, + [6, 20, 6, 20], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hello world, Hello!" << endl;'); + + findModel.replaceAll(); + assertFindState( + editor, + [6, 17, 6, 17], + null, + [] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hi world, hi!" << endl;'); + assert.equal(editor.getModel().getLineContent(7), ' cout << "hi world again" << endl;'); + assert.equal(editor.getModel().getLineContent(8), ' cout << "hi world again" << endl;'); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replaceAll two spaces with one space', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: ' ', replaceString: ' ' }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 1, 6, 3], + [6, 3, 6, 5], + [7, 1, 7, 3], + [7, 3, 7, 5], + [8, 1, 8, 3], + [8, 3, 8, 5], + [9, 1, 9, 3], + [9, 3, 9, 5] + ] + ); + + findModel.replaceAll(); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 1, 6, 3], + [7, 1, 7, 3], + [8, 1, 8, 3], + [9, 1, 9, 3] + ] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hello world, Hello!" << endl;'); + assert.equal(editor.getModel().getLineContent(7), ' cout << "hello world again" << endl;'); + assert.equal(editor.getModel().getLineContent(8), ' cout << "Hello world again" << endl;'); + assert.equal(editor.getModel().getLineContent(9), ' cout << "helloworld again" << endl;'); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replaceAll bla', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'bla', replaceString: 'ciao' }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [11, 4, 11, 7], + [11, 7, 11, 10], + [11, 10, 11, 13] + ] + ); + + findModel.replaceAll(); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + assert.equal(editor.getModel().getLineContent(11), '// ciaociaociaociao'); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replaceAll bla with \\t\\n', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'bla', replaceString: '<\\n\\t>', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [11, 4, 11, 7], + [11, 7, 11, 10], + [11, 10, 11, 13] + ] + ); + + findModel.replaceAll(); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + assert.equal(editor.getModel().getLineContent(11), '// <'); + assert.equal(editor.getModel().getLineContent(12), '\t><'); + assert.equal(editor.getModel().getLineContent(13), '\t><'); + assert.equal(editor.getModel().getLineContent(14), '\t>ciao'); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('issue #3516: "replace all" moves page/cursor/focus/scroll to the place of the last replacement', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'include', replaceString: 'bar' }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [2, 2, 2, 9], + [3, 2, 3, 9] + ] + ); + + findModel.replaceAll(); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + + assert.equal(editor.getModel().getLineContent(2), '#bar "cool.h"'); + assert.equal(editor.getModel().getLineContent(3), '#bar '); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('finds only in editable range if replace is shown', (editor, cursor) => { + editor.getModel().setEditableRange({ + startLineNumber: 6, + startColumn: 1, + endLineNumber: 8, + endColumn: 1 + }); + + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findState.change({ isReplaceRevealed: true }, false); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('listens to model content changes', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + editor.getModel().setValue('hello\nhi'); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('selectAllMatches', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.selectAllMatches(); + + assert.deepEqual(editor.getSelections().map(s => s.toString()), [ + new Selection(6, 14, 6, 19), + new Selection(6, 27, 6, 32), + new Selection(7, 14, 7, 19), + new Selection(8, 14, 8, 19) + ].map(s => s.toString())); + + assertFindState( + editor, + [6, 14, 6, 19], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('issue #14143 selectAllMatches should maintain primary cursor if feasible', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + editor.setSelection(new Range(7, 14, 7, 19)); + + findModel.selectAllMatches(); + + assert.deepEqual(editor.getSelections().map(s => s.toString()), [ + new Selection(7, 14, 7, 19), + new Selection(6, 14, 6, 19), + new Selection(6, 27, 6, 32), + new Selection(8, 14, 8, 19) + ].map(s => s.toString())); + + assert.deepEqual(editor.getSelection().toString(), new Selection(7, 14, 7, 19).toString()); + + assertFindState( + editor, + [7, 14, 7, 19], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('issue #1914: NPE when there is only one find match', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'cool.h' }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [2, 11, 2, 17] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [2, 11, 2, 17], + [2, 11, 2, 17], + [ + [2, 11, 2, 17] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [2, 11, 2, 17], + [2, 11, 2, 17], + [ + [2, 11, 2, 17] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replace when search string has look ahed regex', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello(?=\\sworld)', replaceString: 'hi', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.replace(); + + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hello world, Hello!" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hi world, Hello!" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(7), ' cout << "hi world again" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [8, 16, 8, 16], + null, + [] + ); + assert.equal(editor.getModel().getLineContent(8), ' cout << "hi world again" << endl;'); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replace when search string has look ahed regex and cursor is at the last find match', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello(?=\\sworld)', replaceString: 'hi', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + editor.trigger('mouse', CoreNavigationCommands.MoveTo.id, { + position: new Position(8, 14) + }); + + assertFindState( + editor, + [8, 14, 8, 14], + null, + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.replace(); + + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + assert.equal(editor.getModel().getLineContent(8), ' cout << "Hello world again" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + ] + ); + assert.equal(editor.getModel().getLineContent(8), ' cout << "hi world again" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [7, 14, 7, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hi world, Hello!" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [7, 16, 7, 16], + null, + [] + ); + assert.equal(editor.getModel().getLineContent(7), ' cout << "hi world again" << endl;'); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replaceAll when search string has look ahed regex', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello(?=\\sworld)', replaceString: 'hi', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.replaceAll(); + + assert.equal(editor.getModel().getLineContent(6), ' cout << "hi world, Hello!" << endl;'); + assert.equal(editor.getModel().getLineContent(7), ' cout << "hi world again" << endl;'); + assert.equal(editor.getModel().getLineContent(8), ' cout << "hi world again" << endl;'); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replace when search string has look ahed regex and replace string has capturing groups', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hel(lo)(?=\\sworld)', replaceString: 'hi$1', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.replace(); + + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hello world, Hello!" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hilo world, Hello!" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [8, 14, 8, 19] + ] + ); + assert.equal(editor.getModel().getLineContent(7), ' cout << "hilo world again" << endl;'); + + findModel.replace(); + assertFindState( + editor, + [8, 18, 8, 18], + null, + [] + ); + assert.equal(editor.getModel().getLineContent(8), ' cout << "hilo world again" << endl;'); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replaceAll when search string has look ahed regex and replace string has capturing groups', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'wo(rl)d(?=.*;$)', replaceString: 'gi$1', isRegex: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 20, 6, 25], + [7, 20, 7, 25], + [8, 20, 8, 25], + [9, 19, 9, 24] + ] + ); + + findModel.replaceAll(); + + assert.equal(editor.getModel().getLineContent(6), ' cout << "hello girl, Hello!" << endl;'); + assert.equal(editor.getModel().getLineContent(7), ' cout << "hello girl again" << endl;'); + assert.equal(editor.getModel().getLineContent(8), ' cout << "Hello girl again" << endl;'); + assert.equal(editor.getModel().getLineContent(9), ' cout << "hellogirl again" << endl;'); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('replaceAll when search string is multiline and has look ahed regex and replace string has capturing groups', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'wo(rl)d(.*;\\n)(?=.*hello)', replaceString: 'gi$1$2', isRegex: true, matchCase: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 20, 7, 1], + [8, 20, 9, 1] + ] + ); + + findModel.replaceAll(); + + assert.equal(editor.getModel().getLineContent(6), ' cout << "hello girl, Hello!" << endl;'); + assert.equal(editor.getModel().getLineContent(8), ' cout << "Hello girl again" << endl;'); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('issue #18711 replaceAll with empty string', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', replaceString: '', wholeWord: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [6, 27, 6, 32], + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.replaceAll(); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << " world, !" << endl;'); + assert.equal(editor.getModel().getLineContent(7), ' cout << " world again" << endl;'); + assert.equal(editor.getModel().getLineContent(8), ' cout << " world again" << endl;'); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('issue #19740 Find and replace capture group/backreference inserts `undefined` instead of empty string', (editor, cursor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello(z)?', replaceString: 'hi$1', isRegex: true, matchCase: true }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [9, 14, 9, 19] + ] + ); + + findModel.replaceAll(); + assertFindState( + editor, + [1, 1, 1, 1], + null, + [] + ); + assert.equal(editor.getModel().getLineContent(6), ' cout << "hi world, Hello!" << endl;'); + assert.equal(editor.getModel().getLineContent(7), ' cout << "hi world again" << endl;'); + assert.equal(editor.getModel().getLineContent(9), ' cout << "hiworld again" << endl;'); + + findModel.dispose(); + findState.dispose(); + }); +}); diff --git a/src/vs/editor/contrib/find/test/common/replacePattern.test.ts b/src/vs/editor/contrib/find/test/common/replacePattern.test.ts new file mode 100644 index 0000000000..284cad4f05 --- /dev/null +++ b/src/vs/editor/contrib/find/test/common/replacePattern.test.ts @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { parseReplaceString, ReplacePattern, ReplacePiece } from 'vs/editor/contrib/find/common/replacePattern'; + +suite('Replace Pattern test', () => { + + test('parse replace string', () => { + let testParse = (input: string, expectedPieces: ReplacePiece[]) => { + let actual = parseReplaceString(input); + let expected = new ReplacePattern(expectedPieces); + assert.deepEqual(actual, expected, 'Parsing ' + input); + }; + + // no backslash => no treatment + testParse('hello', [ReplacePiece.staticValue('hello')]); + + // \t => TAB + testParse('\\thello', [ReplacePiece.staticValue('\thello')]); + testParse('h\\tello', [ReplacePiece.staticValue('h\tello')]); + testParse('hello\\t', [ReplacePiece.staticValue('hello\t')]); + + // \n => LF + testParse('\\nhello', [ReplacePiece.staticValue('\nhello')]); + + // \\t => \t + testParse('\\\\thello', [ReplacePiece.staticValue('\\thello')]); + testParse('h\\\\tello', [ReplacePiece.staticValue('h\\tello')]); + testParse('hello\\\\t', [ReplacePiece.staticValue('hello\\t')]); + + // \\\t => \TAB + testParse('\\\\\\thello', [ReplacePiece.staticValue('\\\thello')]); + + // \\\\t => \\t + testParse('\\\\\\\\thello', [ReplacePiece.staticValue('\\\\thello')]); + + // \ at the end => no treatment + testParse('hello\\', [ReplacePiece.staticValue('hello\\')]); + + // \ with unknown char => no treatment + testParse('hello\\x', [ReplacePiece.staticValue('hello\\x')]); + + // \ with back reference => no treatment + testParse('hello\\0', [ReplacePiece.staticValue('hello\\0')]); + + testParse('hello$&', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(0)]); + testParse('hello$0', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(0)]); + testParse('hello$02', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(0), ReplacePiece.staticValue('2')]); + testParse('hello$1', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(1)]); + testParse('hello$2', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(2)]); + testParse('hello$9', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(9)]); + testParse('$9hello', [ReplacePiece.matchIndex(9), ReplacePiece.staticValue('hello')]); + + testParse('hello$12', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(12)]); + testParse('hello$99', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(99)]); + testParse('hello$99a', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(99), ReplacePiece.staticValue('a')]); + testParse('hello$1a', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(1), ReplacePiece.staticValue('a')]); + testParse('hello$100', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(10), ReplacePiece.staticValue('0')]); + testParse('hello$100a', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(10), ReplacePiece.staticValue('0a')]); + testParse('hello$10a0', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(10), ReplacePiece.staticValue('a0')]); + testParse('hello$$', [ReplacePiece.staticValue('hello$')]); + testParse('hello$$0', [ReplacePiece.staticValue('hello$0')]); + + testParse('hello$`', [ReplacePiece.staticValue('hello$`')]); + testParse('hello$\'', [ReplacePiece.staticValue('hello$\'')]); + }); + + test('replace has JavaScript semantics', () => { + let testJSReplaceSemantics = (target: string, search: RegExp, replaceString: string, expected: string) => { + let replacePattern = parseReplaceString(replaceString); + let m = search.exec(target); + let actual = replacePattern.buildReplaceString(m); + + assert.deepEqual(actual, expected, `${target}.replace(${search}, ${replaceString})`); + }; + + testJSReplaceSemantics('hi', /hi/, 'hello', 'hi'.replace(/hi/, 'hello')); + testJSReplaceSemantics('hi', /hi/, '\\t', 'hi'.replace(/hi/, '\t')); + testJSReplaceSemantics('hi', /hi/, '\\n', 'hi'.replace(/hi/, '\n')); + testJSReplaceSemantics('hi', /hi/, '\\\\t', 'hi'.replace(/hi/, '\\t')); + testJSReplaceSemantics('hi', /hi/, '\\\\n', 'hi'.replace(/hi/, '\\n')); + + // implicit capture group 0 + testJSReplaceSemantics('hi', /hi/, 'hello$&', 'hi'.replace(/hi/, 'hello$&')); + testJSReplaceSemantics('hi', /hi/, 'hello$0', 'hi'.replace(/hi/, 'hello$&')); + testJSReplaceSemantics('hi', /hi/, 'hello$&1', 'hi'.replace(/hi/, 'hello$&1')); + testJSReplaceSemantics('hi', /hi/, 'hello$01', 'hi'.replace(/hi/, 'hello$&1')); + + // capture groups have funny semantics in replace strings + // the replace string interprets $nn as a captured group only if it exists in the search regex + testJSReplaceSemantics('hi', /(hi)/, 'hello$10', 'hi'.replace(/(hi)/, 'hello$10')); + testJSReplaceSemantics('hi', /(hi)()()()()()()()()()/, 'hello$10', 'hi'.replace(/(hi)()()()()()()()()()/, 'hello$10')); + testJSReplaceSemantics('hi', /(hi)/, 'hello$100', 'hi'.replace(/(hi)/, 'hello$100')); + testJSReplaceSemantics('hi', /(hi)/, 'hello$20', 'hi'.replace(/(hi)/, 'hello$20')); + }); + + test('get replace string if given text is a complete match', () => { + function assertReplace(target: string, search: RegExp, replaceString: string, expected: string): void { + let replacePattern = parseReplaceString(replaceString); + let m = search.exec(target); + let actual = replacePattern.buildReplaceString(m); + + assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`); + } + + assertReplace('bla', /bla/, 'hello', 'hello'); + assertReplace('bla', /(bla)/, 'hello', 'hello'); + assertReplace('bla', /(bla)/, 'hello$0', 'hellobla'); + + let searchRegex = /let\s+(\w+)\s*=\s*require\s*\(\s*['"]([\w\.\-/]+)\s*['"]\s*\)\s*/; + assertReplace('let fs = require(\'fs\')', searchRegex, 'import * as $1 from \'$2\';', 'import * as fs from \'fs\';'); + assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $1 from \'$2\';', 'import * as something from \'fs\';'); + assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $1 from \'$1\';', 'import * as something from \'something\';'); + assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $2 from \'$1\';', 'import * as fs from \'something\';'); + assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $0 from \'$0\';', 'import * as let something = require(\'fs\') from \'let something = require(\'fs\')\';'); + assertReplace('let fs = require(\'fs\')', searchRegex, 'import * as $1 from \'$2\';', 'import * as fs from \'fs\';'); + assertReplace('for ()', /for(.*)/, 'cat$1', 'cat ()'); + + // issue #18111 + assertReplace('HRESULT OnAmbientPropertyChange(DISPID dispid);', /\b\s{3}\b/, ' ', ' '); + }); + + test('get replace string if match is sub-string of the text', () => { + function assertReplace(target: string, search: RegExp, replaceString: string, expected: string): void { + let replacePattern = parseReplaceString(replaceString); + let m = search.exec(target); + let actual = replacePattern.buildReplaceString(m); + + assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`); + } + assertReplace('this is a bla text', /bla/, 'hello', 'hello'); + assertReplace('this is a bla text', /this(?=.*bla)/, 'that', 'that'); + assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1at', 'that'); + assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1e', 'the'); + assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1ere', 'there'); + assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1', 'th'); + assertReplace('this is a bla text', /(th)is(?=.*bla)/, 'ma$1', 'math'); + assertReplace('this is a bla text', /(th)is(?=.*bla)/, 'ma$1s', 'maths'); + assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$0', 'this'); + assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$0$1', 'thisth'); + assertReplace('this is a bla text', /bla(?=\stext$)/, 'foo', 'foo'); + assertReplace('this is a bla text', /b(la)(?=\stext$)/, 'f$1', 'fla'); + assertReplace('this is a bla text', /b(la)(?=\stext$)/, 'f$0', 'fbla'); + assertReplace('this is a bla text', /b(la)(?=\stext$)/, '$0ah', 'blaah'); + }); + + test('issue #19740 Find and replace capture group/backreference inserts `undefined` instead of empty string', () => { + let replacePattern = parseReplaceString('a{$1}'); + let matches = /a(z)?/.exec('abcd'); + let actual = replacePattern.buildReplaceString(matches); + assert.equal(actual, 'a{}'); + }); +}); diff --git a/src/vs/editor/contrib/folding/browser/arrow-collapse-dark.svg b/src/vs/editor/contrib/folding/browser/arrow-collapse-dark.svg new file mode 100644 index 0000000000..1d7ce3b6bc --- /dev/null +++ b/src/vs/editor/contrib/folding/browser/arrow-collapse-dark.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/browser/arrow-collapse.svg b/src/vs/editor/contrib/folding/browser/arrow-collapse.svg new file mode 100644 index 0000000000..9e6896640c --- /dev/null +++ b/src/vs/editor/contrib/folding/browser/arrow-collapse.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/browser/arrow-expand-dark.svg b/src/vs/editor/contrib/folding/browser/arrow-expand-dark.svg new file mode 100644 index 0000000000..4d1a5ca84d --- /dev/null +++ b/src/vs/editor/contrib/folding/browser/arrow-expand-dark.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/browser/arrow-expand.svg b/src/vs/editor/contrib/folding/browser/arrow-expand.svg new file mode 100644 index 0000000000..f1472e2751 --- /dev/null +++ b/src/vs/editor/contrib/folding/browser/arrow-expand.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/browser/folding.css b/src/vs/editor/contrib/folding/browser/folding.css new file mode 100644 index 0000000000..2f9659faaa --- /dev/null +++ b/src/vs/editor/contrib/folding/browser/folding.css @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .margin-view-overlays .folding { + margin-left: 5px; + cursor: pointer; + background-repeat: no-repeat; + background-origin: border-box; + background-position: 3px center; + background-size: 15px; + opacity: 0; + transition: opacity 0.5s; +} + +.monaco-editor .margin-view-overlays .folding { + background-image: url('arrow-expand.svg'); +} + +.monaco-editor.hc-black .margin-view-overlays .folding, +.monaco-editor.vs-dark .margin-view-overlays .folding { + background-image: url('arrow-expand-dark.svg'); +} + +.monaco-editor .margin-view-overlays:hover .folding, +.monaco-editor.alwaysShowFoldIcons .margin-view-overlays .folding { + opacity: 1; +} + +.monaco-editor .margin-view-overlays .folding.collapsed { + background-image: url('arrow-collapse.svg'); + opacity: 1; +} + +.monaco-editor.hc-black .margin-view-overlays .folding.collapsed, +.monaco-editor.vs-dark .margin-view-overlays .folding.collapsed { + background-image: url('arrow-collapse-dark.svg'); +} + +.monaco-editor .inline-folded:after { + color: grey; + margin: 0.1em 0.2em 0 0.2em; + content: "⋯"; + display: inline; + line-height: 1em; + cursor: pointer; +} + diff --git a/src/vs/editor/contrib/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts new file mode 100644 index 0000000000..b9c811e908 --- /dev/null +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -0,0 +1,767 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +/// + +'use strict'; + +import * as nls from 'vs/nls'; +import * as types from 'vs/base/common/types'; +import * as dom from 'vs/base/browser/dom'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { editorAction, ServicesAccessor, EditorAction, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { CollapsibleRegion, getCollapsibleRegionsToFoldAtLine, getCollapsibleRegionsToUnfoldAtLine, doesLineBelongsToCollapsibleRegion, IFoldingRange } from 'vs/editor/contrib/folding/common/foldingModel'; +import { computeRanges, limitByIndent } from 'vs/editor/contrib/folding/common/indentFoldStrategy'; +import { IFoldingController, ID } from 'vs/editor/contrib/folding/common/folding'; +import { Selection } from 'vs/editor/common/core/selection'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; + +@editorContribution +export class FoldingController implements IFoldingController { + + static MAX_FOLDING_REGIONS = 5000; + + public static get(editor: editorCommon.ICommonCodeEditor): FoldingController { + return editor.getContribution(ID); + } + + private editor: ICodeEditor; + private _isEnabled: boolean; + private _showFoldingControls: 'always' | 'mouseover'; + private globalToDispose: IDisposable[]; + + private computeToken: number; + private cursorChangedScheduler: RunOnceScheduler; + private contentChangedScheduler: RunOnceScheduler; + private localToDispose: IDisposable[]; + + private decorations: CollapsibleRegion[]; + + constructor(editor: ICodeEditor) { + this.editor = editor; + this._isEnabled = this.editor.getConfiguration().contribInfo.folding; + this._showFoldingControls = this.editor.getConfiguration().contribInfo.showFoldingControls; + + this.globalToDispose = []; + this.localToDispose = []; + this.decorations = []; + this.computeToken = 0; + + this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged())); + this.globalToDispose.push(this.editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { + let oldIsEnabled = this._isEnabled; + this._isEnabled = this.editor.getConfiguration().contribInfo.folding; + if (oldIsEnabled !== this._isEnabled) { + this.onModelChanged(); + } + let oldShowFoldingControls = this._showFoldingControls; + this._showFoldingControls = this.editor.getConfiguration().contribInfo.showFoldingControls; + if (oldShowFoldingControls !== this._showFoldingControls) { + this.updateHideFoldIconClass(); + } + })); + + this.onModelChanged(); + } + + public getId(): string { + return ID; + } + + public dispose(): void { + this.cleanState(); + this.globalToDispose = dispose(this.globalToDispose); + } + + private updateHideFoldIconClass(): void { + let domNode = this.editor.getDomNode(); + if (domNode) { + dom.toggleClass(domNode, 'alwaysShowFoldIcons', this._showFoldingControls === 'always'); + } + } + + /** + * Store view state. + */ + public saveViewState(): any { + let model = this.editor.getModel(); + if (!model) { + return {}; + } + var collapsedRegions: IFoldingRange[] = []; + this.decorations.forEach(d => { + if (d.isCollapsed) { + var range = d.getDecorationRange(model); + if (range) { + collapsedRegions.push({ startLineNumber: range.startLineNumber, endLineNumber: range.endLineNumber, indent: d.indent, isCollapsed: true }); + } + } + }); + return { collapsedRegions: collapsedRegions, lineCount: model.getLineCount() }; + } + + /** + * Restore view state. + */ + public restoreViewState(state: any): void { + let model = this.editor.getModel(); + if (!model) { + return; + } + if (!this._isEnabled) { + return; + } + if (!state || !Array.isArray(state.collapsedRegions) || state.collapsedRegions.length === 0 || state.lineCount !== model.getLineCount()) { + return; + } + let newFolded = state.collapsedRegions; + + if (this.decorations.length > 0) { + let hasChanges = false; + let i = 0; + this.editor.changeDecorations(changeAccessor => { + this.decorations.forEach(d => { + if (i === newFolded.length || d.startLineNumber < newFolded[i].startLineNumber) { + if (d.isCollapsed) { + d.setCollapsed(false, changeAccessor); + hasChanges = true; + } + } else if (d.startLineNumber === newFolded[i].startLineNumber) { + if (!d.isCollapsed) { + d.setCollapsed(true, changeAccessor); + hasChanges = true; + } + i++; + } else { + return; // folding regions doesn't match, don't try to restore + } + }); + }); + if (hasChanges) { + this.updateHiddenAreas(void 0); + } + } + } + + private cleanState(): void { + this.localToDispose = dispose(this.localToDispose); + } + + private applyRegions(regions: IFoldingRange[]) { + let model = this.editor.getModel(); + if (!model) { + return; + } + let updateHiddenRegions = false; + regions = limitByIndent(regions, FoldingController.MAX_FOLDING_REGIONS).sort((r1, r2) => r1.startLineNumber - r2.startLineNumber); + + this.editor.changeDecorations(changeAccessor => { + + let newDecorations: CollapsibleRegion[] = []; + + let k = 0, i = 0; + while (i < this.decorations.length && k < regions.length) { + let dec = this.decorations[i]; + let decRange = dec.getDecorationRange(model); + if (!decRange) { + updateHiddenRegions = updateHiddenRegions || dec.isCollapsed; + dec.dispose(changeAccessor); + i++; + } else { + while (k < regions.length && decRange.startLineNumber > regions[k].startLineNumber) { + let region = regions[k]; + updateHiddenRegions = updateHiddenRegions || region.isCollapsed; + newDecorations.push(new CollapsibleRegion(region, model, changeAccessor)); + k++; + } + if (k < regions.length) { + let currRange = regions[k]; + if (decRange.startLineNumber < currRange.startLineNumber) { + updateHiddenRegions = updateHiddenRegions || dec.isCollapsed; + dec.dispose(changeAccessor); + i++; + } else if (decRange.startLineNumber === currRange.startLineNumber) { + if (dec.isCollapsed && (dec.startLineNumber !== currRange.startLineNumber || dec.endLineNumber !== currRange.endLineNumber)) { + updateHiddenRegions = true; + } + currRange.isCollapsed = dec.isCollapsed; // preserve collapse state + dec.update(currRange, model, changeAccessor); + newDecorations.push(dec); + i++; + k++; + } + } + } + } + while (i < this.decorations.length) { + let dec = this.decorations[i]; + updateHiddenRegions = updateHiddenRegions || dec.isCollapsed; + dec.dispose(changeAccessor); + i++; + } + while (k < regions.length) { + let region = regions[k]; + updateHiddenRegions = updateHiddenRegions || region.isCollapsed; + newDecorations.push(new CollapsibleRegion(region, model, changeAccessor)); + k++; + } + this.decorations = newDecorations; + }); + if (updateHiddenRegions) { + this.updateHiddenAreas(); + } + + } + + private onModelChanged(): void { + this.cleanState(); + this.updateHideFoldIconClass(); + + let model = this.editor.getModel(); + if (!this._isEnabled || !model) { + return; + } + + this.computeAndApplyCollapsibleRegions(); + this.contentChangedScheduler = new RunOnceScheduler(() => this.computeAndApplyCollapsibleRegions(), 200); + this.cursorChangedScheduler = new RunOnceScheduler(() => this.revealCursor(), 200); + this.localToDispose.push(this.contentChangedScheduler); + this.localToDispose.push(this.cursorChangedScheduler); + + this.localToDispose.push(this.editor.onDidChangeModelContent(e => this.contentChangedScheduler.schedule())); + this.localToDispose.push(this.editor.onDidChangeCursorPosition((e) => { + + if (!this._isEnabled) { + // Early exit if nothing needs to be done! + // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) + return; + } + + this.cursorChangedScheduler.schedule(); + })); + this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); + this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); + + this.localToDispose.push({ dispose: () => this.disposeDecorations() }); + } + + private computeAndApplyCollapsibleRegions(): void { + let model = this.editor.getModel(); + this.applyRegions(model ? computeRanges(model) : []); + } + + private disposeDecorations() { + this.editor.changeDecorations(changeAccessor => { + this.decorations.forEach(dec => dec.dispose(changeAccessor)); + }); + this.decorations = []; + this.editor.setHiddenAreas([]); + } + + private revealCursor() { + let model = this.editor.getModel(); + if (!model) { + return; + } + let hasChanges = false; + let selections = this.editor.getSelections(); + + this.editor.changeDecorations(changeAccessor => { + return this.decorations.forEach(dec => { + if (dec.isCollapsed) { + let decRange = dec.getDecorationRange(model); + if (decRange) { + for (let selection of selections) { + // reveal if cursor in in one of the collapsed line (not the first) + if (decRange.startLineNumber < selection.selectionStartLineNumber && selection.selectionStartLineNumber <= decRange.endLineNumber) { + dec.setCollapsed(false, changeAccessor); + hasChanges = true; + break; + } + } + } + } + }); + }); + if (hasChanges) { + this.updateHiddenAreas(this.editor.getPosition().lineNumber); + } + } + + private mouseDownInfo: { lineNumber: number, iconClicked: boolean }; + + private onEditorMouseDown(e: IEditorMouseEvent): void { + this.mouseDownInfo = null; + + if (this.decorations.length === 0) { + return; + } + let range = e.target.range; + if (!range) { + return; + } + if (!e.event.leftButton) { + return; + } + + let model = this.editor.getModel(); + + let iconClicked = false; + switch (e.target.type) { + case MouseTargetType.GUTTER_LINE_DECORATIONS: + iconClicked = true; + break; + case MouseTargetType.CONTENT_EMPTY: + case MouseTargetType.CONTENT_TEXT: + if (range.startColumn === model.getLineMaxColumn(range.startLineNumber)) { + break; + } + return; + default: + return; + } + + this.mouseDownInfo = { lineNumber: range.startLineNumber, iconClicked }; + } + + private onEditorMouseUp(e: IEditorMouseEvent): void { + if (!this.mouseDownInfo) { + return; + } + let lineNumber = this.mouseDownInfo.lineNumber; + let iconClicked = this.mouseDownInfo.iconClicked; + + let range = e.target.range; + if (!range || range.startLineNumber !== lineNumber) { + return; + } + + let model = this.editor.getModel(); + + if (iconClicked) { + if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) { + return; + } + } else { + if (range.startColumn !== model.getLineMaxColumn(lineNumber)) { + return; + } + } + + this.editor.changeDecorations(changeAccessor => { + for (let i = 0; i < this.decorations.length; i++) { + let dec = this.decorations[i]; + let decRange = dec.getDecorationRange(model); + if (decRange && decRange.startLineNumber === lineNumber) { + if (iconClicked || dec.isCollapsed) { + dec.setCollapsed(!dec.isCollapsed, changeAccessor); + this.updateHiddenAreas(lineNumber); + } + return; + } + } + }); + } + + private updateHiddenAreas(focusLine?: number): void { + let model = this.editor.getModel(); + var selections: Selection[] = this.editor.getSelections(); + var updateSelections = false; + let hiddenAreas: Range[] = []; + this.decorations.filter(dec => dec.isCollapsed).forEach(dec => { + let decRange = dec.getDecorationRange(model); + if (!decRange) { + return; + } + let isLineHidden = line => line > decRange.startLineNumber && line <= decRange.endLineNumber; + hiddenAreas.push(new Range(decRange.startLineNumber + 1, 1, decRange.endLineNumber, 1)); + selections.forEach((selection, i) => { + if (isLineHidden(selection.getStartPosition().lineNumber)) { + selections[i] = selection = selection.setStartPosition(decRange.startLineNumber, model.getLineMaxColumn(decRange.startLineNumber)); + updateSelections = true; + } + if (isLineHidden(selection.getEndPosition().lineNumber)) { + selections[i] = selection.setEndPosition(decRange.startLineNumber, model.getLineMaxColumn(decRange.startLineNumber)); + updateSelections = true; + } + }); + }); + if (updateSelections) { + this.editor.setSelections(selections); + } + this.editor.setHiddenAreas(hiddenAreas); + if (focusLine) { + this.editor.revealPositionInCenterIfOutsideViewport({ lineNumber: focusLine, column: 1 }, editorCommon.ScrollType.Smooth); + } + } + + public unfold(levels: number): void { + let model = this.editor.getModel(); + let hasChanges = false; + let selections = this.editor.getSelections(); + let selectionsHasChanged = false; + selections.forEach((selection, index) => { + let toUnfold: CollapsibleRegion[] = getCollapsibleRegionsToUnfoldAtLine(this.decorations, model, selection.startLineNumber, levels); + if (toUnfold.length > 0) { + toUnfold.forEach((collapsibleRegion, index) => { + this.editor.changeDecorations(changeAccessor => { + collapsibleRegion.setCollapsed(false, changeAccessor); + hasChanges = true; + }); + }); + if (!doesLineBelongsToCollapsibleRegion(toUnfold[0].foldingRange, selection.startLineNumber)) { + let lineNumber = toUnfold[0].startLineNumber, column = model.getLineMaxColumn(toUnfold[0].startLineNumber); + selections[index] = selection.setEndPosition(lineNumber, column).setStartPosition(lineNumber, column); + selectionsHasChanged = true; + } + } + }); + if (selectionsHasChanged) { + this.editor.setSelections(selections); + } + if (hasChanges) { + this.updateHiddenAreas(selections[0].startLineNumber); + } + } + + public fold(levels: number, up: boolean): void { + let hasChanges = false; + let selections = this.editor.getSelections(); + selections.forEach(selection => { + let lineNumber = selection.startLineNumber; + let toFold: CollapsibleRegion[] = getCollapsibleRegionsToFoldAtLine(this.decorations, this.editor.getModel(), lineNumber, levels, up); + toFold.forEach(collapsibleRegion => this.editor.changeDecorations(changeAccessor => { + collapsibleRegion.setCollapsed(true, changeAccessor); + hasChanges = true; + })); + }); + if (hasChanges) { + this.updateHiddenAreas(selections[0].startLineNumber); + } + } + + public foldUnfoldRecursively(isFold: boolean): void { + let hasChanges = false; + let model = this.editor.getModel(); + let selections = this.editor.getSelections(); + selections.forEach(selection => { + let lineNumber = selection.startLineNumber; + let endLineNumber: number; + let decToFoldUnfold: CollapsibleRegion[] = []; + for (let i = 0, len = this.decorations.length; i < len; i++) { + let dec = this.decorations[i]; + let decRange = dec.getDecorationRange(model); + if (!decRange) { + continue; + } + if (decRange.startLineNumber >= lineNumber && (decRange.endLineNumber <= endLineNumber || typeof endLineNumber === 'undefined')) { + //Protect against cursor not being in decoration and lower decoration folding/unfolding + if (decRange.startLineNumber !== lineNumber && typeof endLineNumber === 'undefined') { + return; + } + endLineNumber = endLineNumber || decRange.endLineNumber; + decToFoldUnfold.push(dec); + } + }; + if (decToFoldUnfold.length > 0) { + decToFoldUnfold.forEach(dec => { + this.editor.changeDecorations(changeAccessor => { + dec.setCollapsed(isFold, changeAccessor); + hasChanges = true; + }); + }); + } + }); + if (hasChanges) { + this.updateHiddenAreas(selections[0].startLineNumber); + } + } + + public foldAll(): void { + this.changeAll(true); + } + + public unfoldAll(): void { + this.changeAll(false); + } + + private changeAll(collapse: boolean): void { + if (this.decorations.length > 0) { + let hasChanges = true; + this.editor.changeDecorations(changeAccessor => { + this.decorations.forEach(d => { + if (collapse !== d.isCollapsed) { + d.setCollapsed(collapse, changeAccessor); + hasChanges = true; + } + }); + }); + if (hasChanges) { + this.updateHiddenAreas(this.editor.getPosition().lineNumber); + } + } + } + + public foldLevel(foldLevel: number, selectedLineNumbers: number[]): void { + let model = this.editor.getModel(); + let foldingRegionStack: Range[] = [model.getFullModelRange()]; // sentinel + + let hasChanges = false; + this.editor.changeDecorations(changeAccessor => { + this.decorations.forEach(dec => { + let decRange = dec.getDecorationRange(model); + if (decRange) { + while (!Range.containsRange(foldingRegionStack[foldingRegionStack.length - 1], decRange)) { + foldingRegionStack.pop(); + } + foldingRegionStack.push(decRange); + if (foldingRegionStack.length === foldLevel + 1 && !dec.isCollapsed && !selectedLineNumbers.some(lineNumber => decRange.startLineNumber < lineNumber && lineNumber <= decRange.endLineNumber)) { + dec.setCollapsed(true, changeAccessor); + hasChanges = true; + } + } + }); + }); + if (hasChanges) { + this.updateHiddenAreas(selectedLineNumbers[0]); + } + } +} + +abstract class FoldingAction extends EditorAction { + + abstract invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor, args: T): void; + + public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: T): void | TPromise { + let foldingController = FoldingController.get(editor); + if (!foldingController) { + return; + } + this.reportTelemetry(accessor, editor); + this.invoke(foldingController, editor, args); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + + } +} + +interface FoldingArguments { + levels?: number; + direction?: 'up' | 'down'; +} + +function foldingArgumentsConstraint(args: any) { + if (!types.isUndefined(args)) { + if (!types.isObject(args)) { + return false; + } + const foldingArgs: FoldingArguments = args; + if (!types.isUndefined(foldingArgs.levels) && !types.isNumber(foldingArgs.levels)) { + return false; + } + if (!types.isUndefined(foldingArgs.direction) && !types.isString(foldingArgs.direction)) { + return false; + } + } + return true; +} + +@editorAction +class UnfoldAction extends FoldingAction { + + constructor() { + super({ + id: 'editor.unfold', + label: nls.localize('unfoldAction.label', "Unfold"), + alias: 'Unfold', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_CLOSE_SQUARE_BRACKET + } + }, + description: { + description: 'Unfold the content in the editor', + args: [ + { + name: 'Unfold editor argument', + description: `Property-value pairs that can be passed through this argument: + * 'level': Number of levels to unfold + `, + constraint: foldingArgumentsConstraint + } + ] + } + }); + } + + invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor, args: FoldingArguments): void { + foldingController.unfold(args ? args.levels || 1 : 1); + } +} + +@editorAction +class UnFoldRecursivelyAction extends FoldingAction { + + constructor() { + super({ + id: 'editor.unfoldRecursively', + label: nls.localize('unFoldRecursivelyAction.label', "Unfold Recursively"), + alias: 'Unfold Recursively', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET) + } + }); + } + + invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor, args: any): void { + foldingController.foldUnfoldRecursively(false); + } +} + +@editorAction +class FoldAction extends FoldingAction { + + constructor() { + super({ + id: 'editor.fold', + label: nls.localize('foldAction.label', "Fold"), + alias: 'Fold', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_OPEN_SQUARE_BRACKET + } + }, + description: { + description: 'Fold the content in the editor', + args: [ + { + name: 'Fold editor argument', + description: `Property-value pairs that can be passed through this argument: + * 'levels': Number of levels to fold + * 'up': If 'true', folds given number of levels up otherwise folds down + `, + constraint: foldingArgumentsConstraint + } + ] + } + }); + } + + invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor, args: FoldingArguments): void { + args = args ? args : { levels: 1, direction: 'up' }; + foldingController.fold(args.levels || 1, args.direction === 'up'); + } +} + +@editorAction +class FoldRecursivelyAction extends FoldingAction { + + constructor() { + super({ + id: 'editor.foldRecursively', + label: nls.localize('foldRecursivelyAction.label', "Fold Recursively"), + alias: 'Fold Recursively', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_OPEN_SQUARE_BRACKET) + } + }); + } + + invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor): void { + foldingController.foldUnfoldRecursively(true); + } +} + +@editorAction +class FoldAllAction extends FoldingAction { + + constructor() { + super({ + id: 'editor.foldAll', + label: nls.localize('foldAllAction.label', "Fold All"), + alias: 'Fold All', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_0) + } + }); + } + + invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor): void { + foldingController.foldAll(); + } +} + +@editorAction +class UnfoldAllAction extends FoldingAction { + + constructor() { + super({ + id: 'editor.unfoldAll', + label: nls.localize('unfoldAllAction.label', "Unfold All"), + alias: 'Unfold All', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_J) + } + }); + } + + invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor): void { + foldingController.unfoldAll(); + } +} + +class FoldLevelAction extends FoldingAction { + private static ID_PREFIX = 'editor.foldLevel'; + public static ID = (level: number) => FoldLevelAction.ID_PREFIX + level; + + private getFoldingLevel() { + return parseInt(this.id.substr(FoldLevelAction.ID_PREFIX.length)); + } + + private getSelectedLines(editor: editorCommon.ICommonCodeEditor) { + return editor.getSelections().map(s => s.startLineNumber); + } + + invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor): void { + foldingController.foldLevel(this.getFoldingLevel(), this.getSelectedLines(editor)); + } +} + +for (let i = 1; i <= 9; i++) { + CommonEditorRegistry.registerEditorAction( + new FoldLevelAction({ + id: FoldLevelAction.ID(i), + label: nls.localize('foldLevelAction.label', "Fold Level {0}", i), + alias: `Fold Level ${i}`, + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | (KeyCode.KEY_0 + i)) + } + }) + ); +}; diff --git a/src/vs/editor/contrib/folding/common/folding.ts b/src/vs/editor/contrib/folding/common/folding.ts new file mode 100644 index 0000000000..8b9ad4b8d2 --- /dev/null +++ b/src/vs/editor/contrib/folding/common/folding.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEditorContribution } from 'vs/editor/common/editorCommon'; + +export const ID = 'editor.contrib.folding'; + +export interface IFoldingController extends IEditorContribution { + + foldAll(): void; + unfoldAll(): void; + +} \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/common/foldingModel.ts b/src/vs/editor/contrib/folding/common/foldingModel.ts new file mode 100644 index 0000000000..af75a60002 --- /dev/null +++ b/src/vs/editor/contrib/folding/common/foldingModel.ts @@ -0,0 +1,295 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; + +export interface IFoldingRange { + startLineNumber: number; + endLineNumber: number; + indent: number; + isCollapsed?: boolean; +} + +export function toString(range: IFoldingRange): string { + return (range ? range.startLineNumber + '/' + range.endLineNumber : 'null') + (range.isCollapsed ? ' (collapsed)' : '') + ' - ' + range.indent; +} + +export class CollapsibleRegion { + + private decorationIds: string[]; + private _isCollapsed: boolean; + private _indent: number; + + private _lastRange: IFoldingRange; + + public constructor(range: IFoldingRange, model: editorCommon.IModel, changeAccessor: editorCommon.IModelDecorationsChangeAccessor) { + this.decorationIds = []; + this.update(range, model, changeAccessor); + } + + public get isCollapsed(): boolean { + return this._isCollapsed; + } + + public get isExpanded(): boolean { + return !this._isCollapsed; + } + + public get indent(): number { + return this._indent; + } + + public get foldingRange(): IFoldingRange { + return this._lastRange; + } + + public get startLineNumber(): number { + return this._lastRange ? this._lastRange.startLineNumber : void 0; + } + + public get endLineNumber(): number { + return this._lastRange ? this._lastRange.endLineNumber : void 0; + } + + public setCollapsed(isCollaped: boolean, changeAccessor: editorCommon.IModelDecorationsChangeAccessor): void { + this._isCollapsed = isCollaped; + if (this.decorationIds.length > 0) { + changeAccessor.changeDecorationOptions(this.decorationIds[0], this.getVisualDecorationOptions()); + } + } + + public getDecorationRange(model: editorCommon.IModel): Range { + if (this.decorationIds.length > 0) { + return model.getDecorationRange(this.decorationIds[1]); + } + return null; + } + + private static _COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + afterContentClassName: 'inline-folded', + linesDecorationsClassName: 'folding collapsed' + }); + + private static _EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + linesDecorationsClassName: 'folding' + }); + + private getVisualDecorationOptions(): ModelDecorationOptions { + if (this._isCollapsed) { + return CollapsibleRegion._COLLAPSED_VISUAL_DECORATION; + } else { + return CollapsibleRegion._EXPANDED_VISUAL_DECORATION; + } + } + + private static _RANGE_DECORATION = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore + }); + + private getRangeDecorationOptions(): ModelDecorationOptions { + return CollapsibleRegion._RANGE_DECORATION; + } + + public update(newRange: IFoldingRange, model: editorCommon.IModel, changeAccessor: editorCommon.IModelDecorationsChangeAccessor): void { + this._lastRange = newRange; + this._isCollapsed = !!newRange.isCollapsed; + this._indent = newRange.indent; + + let newDecorations: editorCommon.IModelDeltaDecoration[] = []; + + let maxColumn = model.getLineMaxColumn(newRange.startLineNumber); + let visualRng = { + startLineNumber: newRange.startLineNumber, + startColumn: maxColumn, + endLineNumber: newRange.startLineNumber, + endColumn: maxColumn + }; + newDecorations.push({ range: visualRng, options: this.getVisualDecorationOptions() }); + + let colRng = { + startLineNumber: newRange.startLineNumber, + startColumn: 1, + endLineNumber: newRange.endLineNumber, + endColumn: model.getLineMaxColumn(newRange.endLineNumber) + }; + newDecorations.push({ range: colRng, options: this.getRangeDecorationOptions() }); + + this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, newDecorations); + } + + + public dispose(changeAccessor: editorCommon.IModelDecorationsChangeAccessor): void { + this._lastRange = null; + this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []); + } + + public toString(): string { + let str = this.isCollapsed ? 'collapsed ' : 'expanded '; + if (this._lastRange) { + str += (this._lastRange.startLineNumber + '/' + this._lastRange.endLineNumber); + } else { + str += 'no range'; + } + + return str; + } +} + +export function getCollapsibleRegionsToFoldAtLine(allRegions: CollapsibleRegion[], model: editorCommon.IModel, lineNumber: number, levels: number, up: boolean): CollapsibleRegion[] { + let surroundingRegion: CollapsibleRegion = getCollapsibleRegionAtLine(allRegions, model, lineNumber); + if (!surroundingRegion) { + return []; + } + if (levels === 1) { + return [surroundingRegion]; + } + let result = getCollapsibleRegionsFor(surroundingRegion, allRegions, model, levels, up); + return result.filter(collapsibleRegion => !collapsibleRegion.isCollapsed); +} + +export function getCollapsibleRegionsToUnfoldAtLine(allRegions: CollapsibleRegion[], model: editorCommon.IModel, lineNumber: number, levels: number): CollapsibleRegion[] { + let surroundingRegion: CollapsibleRegion = getCollapsibleRegionAtLine(allRegions, model, lineNumber); + if (!surroundingRegion) { + return []; + } + if (levels === 1) { + let regionToUnfold = surroundingRegion.isCollapsed ? surroundingRegion : getFoldedCollapsibleRegionAfterLine(allRegions, model, surroundingRegion, lineNumber); + return regionToUnfold ? [regionToUnfold] : []; + } + let result = getCollapsibleRegionsFor(surroundingRegion, allRegions, model, levels, false); + return result.filter(collapsibleRegion => collapsibleRegion.isCollapsed); +} + +function getCollapsibleRegionAtLine(allRegions: CollapsibleRegion[], model: editorCommon.IModel, lineNumber: number): CollapsibleRegion { + let collapsibleRegion: CollapsibleRegion = null; + for (let i = 0, len = allRegions.length; i < len; i++) { + let dec = allRegions[i]; + let decRange = dec.getDecorationRange(model); + if (decRange) { + if (doesLineBelongsToCollapsibleRegion(decRange, lineNumber)) { + collapsibleRegion = dec; + } + if (doesCollapsibleRegionIsAfterLine(decRange, lineNumber)) { + break; + } + } + } + return collapsibleRegion; +} + +function getFoldedCollapsibleRegionAfterLine(allRegions: CollapsibleRegion[], model: editorCommon.IModel, surroundingRegion: CollapsibleRegion, lineNumber: number): CollapsibleRegion { + let index = allRegions.indexOf(surroundingRegion); + for (let i = index + 1; i < allRegions.length; i++) { + let dec = allRegions[i]; + let decRange = dec.getDecorationRange(model); + if (decRange) { + if (doesCollapsibleRegionIsAfterLine(decRange, lineNumber)) { + if (!doesCollapsibleRegionContains(surroundingRegion.foldingRange, decRange)) { + return null; + } + if (dec.isCollapsed) { + return dec; + } + } + } + } + return null; +} + +export function doesLineBelongsToCollapsibleRegion(range: IFoldingRange | Range, lineNumber: number): boolean { + return lineNumber >= range.startLineNumber && lineNumber <= range.endLineNumber; +} + +function doesCollapsibleRegionIsAfterLine(range: IFoldingRange | Range, lineNumber: number): boolean { + return lineNumber < range.startLineNumber; +} +function doesCollapsibleRegionIsBeforeLine(range: IFoldingRange | Range, lineNumber: number): boolean { + return lineNumber > range.endLineNumber; +} + +function doesCollapsibleRegionContains(range1: IFoldingRange | Range, range2: IFoldingRange | Range): boolean { + if (range1 instanceof Range && range2 instanceof Range) { + return range1.containsRange(range2); + } + return range1.startLineNumber <= range2.startLineNumber && range1.endLineNumber >= range2.endLineNumber; +} + +function getCollapsibleRegionsFor(surroundingRegion: CollapsibleRegion, allRegions: CollapsibleRegion[], model: editorCommon.IModel, levels: number, up: boolean): CollapsibleRegion[] { + let collapsibleRegionsHierarchy: CollapsibleRegionsHierarchy = up ? new CollapsibleRegionsParentHierarchy(surroundingRegion, allRegions, model) : new CollapsibleRegionsChildrenHierarchy(surroundingRegion, allRegions, model); + return collapsibleRegionsHierarchy.getRegionsTill(levels); +} + +interface CollapsibleRegionsHierarchy { + getRegionsTill(level: number): CollapsibleRegion[]; +} + +class CollapsibleRegionsChildrenHierarchy implements CollapsibleRegionsHierarchy { + + children: CollapsibleRegionsChildrenHierarchy[] = []; + lastChildIndex: number; + + constructor(private region: CollapsibleRegion, allRegions: CollapsibleRegion[], model: editorCommon.IModel) { + for (let index = allRegions.indexOf(region) + 1; index < allRegions.length; index++) { + let dec = allRegions[index]; + let decRange = dec.getDecorationRange(model); + if (decRange) { + if (doesCollapsibleRegionContains(region.foldingRange, decRange)) { + index = this.processChildRegion(dec, allRegions, model, index); + } + if (doesCollapsibleRegionIsAfterLine(decRange, region.foldingRange.endLineNumber)) { + break; + } + } + } + } + + private processChildRegion(dec: CollapsibleRegion, allRegions: CollapsibleRegion[], model: editorCommon.IModel, index: number): number { + let childRegion = new CollapsibleRegionsChildrenHierarchy(dec, allRegions, model); + this.children.push(childRegion); + this.lastChildIndex = index; + return childRegion.children.length > 0 ? childRegion.lastChildIndex : index; + } + + public getRegionsTill(level: number): CollapsibleRegion[] { + let result = [this.region]; + if (level > 1) { + this.children.forEach(region => result = result.concat(region.getRegionsTill(level - 1))); + } + return result; + } +} +class CollapsibleRegionsParentHierarchy implements CollapsibleRegionsHierarchy { + + parent: CollapsibleRegionsParentHierarchy; + lastChildIndex: number; + + constructor(private region: CollapsibleRegion, allRegions: CollapsibleRegion[], model: editorCommon.IModel) { + for (let index = allRegions.indexOf(region) - 1; index >= 0; index--) { + let dec = allRegions[index]; + let decRange = dec.getDecorationRange(model); + if (decRange) { + if (doesCollapsibleRegionContains(decRange, region.foldingRange)) { + this.parent = new CollapsibleRegionsParentHierarchy(dec, allRegions, model); + break; + } + if (doesCollapsibleRegionIsBeforeLine(decRange, region.foldingRange.endLineNumber)) { + break; + } + } + } + } + + public getRegionsTill(level: number): CollapsibleRegion[] { + let result = [this.region]; + if (this.parent && level > 1) { + result = result.concat(this.parent.getRegionsTill(level - 1)); + } + return result; + } +} \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/common/indentFoldStrategy.ts b/src/vs/editor/contrib/folding/common/indentFoldStrategy.ts new file mode 100644 index 0000000000..66b897804f --- /dev/null +++ b/src/vs/editor/contrib/folding/common/indentFoldStrategy.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IModel } from 'vs/editor/common/editorCommon'; +import { IFoldingRange } from 'vs/editor/contrib/folding/common/foldingModel'; + +export function computeRanges(model: IModel): IFoldingRange[] { + // we get here a clone of the model's indent ranges + return model.getIndentRanges(); +} + +/** + * Limits the number of folding ranges by removing ranges with larger indent levels + */ +export function limitByIndent(ranges: IFoldingRange[], maxEntries: number): IFoldingRange[] { + if (ranges.length <= maxEntries) { + return ranges; + } + + let indentOccurrences: number[] = []; + ranges.forEach(r => { + if (r.indent < 1000) { + indentOccurrences[r.indent] = (indentOccurrences[r.indent] || 0) + 1; + } + }); + let maxIndent = indentOccurrences.length; + for (let i = 0; i < indentOccurrences.length; i++) { + if (indentOccurrences[i]) { + maxEntries -= indentOccurrences[i]; + if (maxEntries < 0) { + maxIndent = i; + break; + } + } + + } + return ranges.filter(r => r.indent < maxIndent); +} \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/test/indentFold.test.ts b/src/vs/editor/contrib/folding/test/indentFold.test.ts new file mode 100644 index 0000000000..84489bac1f --- /dev/null +++ b/src/vs/editor/contrib/folding/test/indentFold.test.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { IFoldingRange } from 'vs/editor/contrib/folding/common/foldingModel'; +import { limitByIndent } from 'vs/editor/contrib/folding/common/indentFoldStrategy'; + +suite('Indentation Folding', () => { + function r(startLineNumber: number, endLineNumber: number, indent: number): IFoldingRange { + return { startLineNumber, endLineNumber, indent }; + } + + test('Limit By indent', () => { + let ranges = [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0), r(10, 15, 10), r(11, 12, 2000), r(14, 15, 2000)]; + assert.deepEqual(limitByIndent(ranges, 8), [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0), r(10, 15, 10), r(11, 12, 2000), r(14, 15, 2000)]); + assert.deepEqual(limitByIndent(ranges, 7), [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0), r(10, 15, 10)]); + assert.deepEqual(limitByIndent(ranges, 6), [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0), r(10, 15, 10)]); + assert.deepEqual(limitByIndent(ranges, 5), [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0)]); + assert.deepEqual(limitByIndent(ranges, 4), [r(1, 4, 0), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0)]); + assert.deepEqual(limitByIndent(ranges, 3), [r(1, 4, 0), r(5, 8, 0), r(9, 15, 0)]); + assert.deepEqual(limitByIndent(ranges, 2), []); + assert.deepEqual(limitByIndent(ranges, 1), []); + assert.deepEqual(limitByIndent(ranges, 0), []); + }); + +}); diff --git a/src/vs/editor/contrib/format/browser/formatActions.ts b/src/vs/editor/contrib/format/browser/formatActions.ts new file mode 100644 index 0000000000..1fff24a53d --- /dev/null +++ b/src/vs/editor/contrib/format/browser/formatActions.ts @@ -0,0 +1,368 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { editorAction, ServicesAccessor, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions'; +import { OnTypeFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry } from 'vs/editor/common/modes'; +import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits } from '../common/format'; +import { EditOperationsCommand } from '../common/formatCommand'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; +import { Range } from 'vs/editor/common/core/range'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { EditorState, CodeEditorStateFlag } from 'vs/editor/common/core/editorState'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; + + +function alertFormattingEdits(edits: editorCommon.ISingleEditOperation[]): void { + + edits = edits.filter(edit => edit.range); + if (!edits.length) { + return; + } + + let { range } = edits[0]; + for (let i = 1; i < edits.length; i++) { + range = Range.plusRange(range, edits[i].range); + } + const { startLineNumber, endLineNumber } = range; + if (startLineNumber === endLineNumber) { + if (edits.length === 1) { + alert(nls.localize('hint11', "Made 1 formatting edit on line {0}", startLineNumber)); + } else { + alert(nls.localize('hintn1', "Made {0} formatting edits on line {1}", edits.length, startLineNumber)); + } + } else { + if (edits.length === 1) { + alert(nls.localize('hint1n', "Made 1 formatting edit between lines {0} and {1}", startLineNumber, endLineNumber)); + } else { + alert(nls.localize('hintnn', "Made {0} formatting edits between lines {1} and {2}", edits.length, startLineNumber, endLineNumber)); + } + } +} + +@commonEditorContribution +class FormatOnType implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.autoFormat'; + + private editor: editorCommon.ICommonCodeEditor; + private workerService: IEditorWorkerService; + private callOnDispose: IDisposable[]; + private callOnModel: IDisposable[]; + + constructor(editor: editorCommon.ICommonCodeEditor, @IEditorWorkerService workerService: IEditorWorkerService) { + this.editor = editor; + this.workerService = workerService; + this.callOnDispose = []; + this.callOnModel = []; + + this.callOnDispose.push(editor.onDidChangeConfiguration(() => this.update())); + this.callOnDispose.push(editor.onDidChangeModel(() => this.update())); + this.callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update())); + this.callOnDispose.push(OnTypeFormattingEditProviderRegistry.onDidChange(this.update, this)); + } + + private update(): void { + + // clean up + this.callOnModel = dispose(this.callOnModel); + + // we are disabled + if (!this.editor.getConfiguration().contribInfo.formatOnType) { + return; + } + + // no model + if (!this.editor.getModel()) { + return; + } + + var model = this.editor.getModel(); + + // no support + var [support] = OnTypeFormattingEditProviderRegistry.ordered(model); + if (!support || !support.autoFormatTriggerCharacters) { + return; + } + + // register typing listeners that will trigger the format + let triggerChars = new CharacterSet(); + for (let ch of support.autoFormatTriggerCharacters) { + triggerChars.add(ch.charCodeAt(0)); + } + this.callOnModel.push(this.editor.onDidType((text: string) => { + let lastCharCode = text.charCodeAt(text.length - 1); + if (triggerChars.has(lastCharCode)) { + this.trigger(String.fromCharCode(lastCharCode)); + } + })); + } + + private trigger(ch: string): void { + + if (this.editor.getSelections().length > 1) { + return; + } + + var model = this.editor.getModel(), + position = this.editor.getPosition(), + canceled = false; + + // install a listener that checks if edits happens before the + // position on which we format right now. If so, we won't + // apply the format edits + var unbind = this.editor.onDidChangeModelContent((e) => { + if (e.isFlush) { + // a model.setValue() was called + // cancel only once + canceled = true; + unbind.dispose(); + return; + } + + for (let i = 0, len = e.changes.length; i < len; i++) { + const change = e.changes[i]; + if (change.range.endLineNumber <= position.lineNumber) { + // cancel only once + canceled = true; + unbind.dispose(); + return; + } + } + + }); + + let modelOpts = model.getOptions(); + + getOnTypeFormattingEdits(model, position, ch, { + tabSize: modelOpts.tabSize, + insertSpaces: modelOpts.insertSpaces + }).then(edits => { + return this.workerService.computeMoreMinimalEdits(model.uri, edits, []); + }).then(edits => { + + unbind.dispose(); + + if (canceled || isFalsyOrEmpty(edits)) { + return; + } + + EditOperationsCommand.execute(this.editor, edits); + alertFormattingEdits(edits); + + }, (err) => { + unbind.dispose(); + throw err; + }); + } + + public getId(): string { + return FormatOnType.ID; + } + + public dispose(): void { + this.callOnDispose = dispose(this.callOnDispose); + this.callOnModel = dispose(this.callOnModel); + } +} + +@commonEditorContribution +class FormatOnPaste implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.formatOnPaste'; + + private editor: editorCommon.ICommonCodeEditor; + private workerService: IEditorWorkerService; + private callOnDispose: IDisposable[]; + private callOnModel: IDisposable[]; + + constructor(editor: editorCommon.ICommonCodeEditor, @IEditorWorkerService workerService: IEditorWorkerService) { + this.editor = editor; + this.workerService = workerService; + this.callOnDispose = []; + this.callOnModel = []; + + this.callOnDispose.push(editor.onDidChangeConfiguration(() => this.update())); + this.callOnDispose.push(editor.onDidChangeModel(() => this.update())); + this.callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update())); + this.callOnDispose.push(DocumentRangeFormattingEditProviderRegistry.onDidChange(this.update, this)); + } + + private update(): void { + + // clean up + this.callOnModel = dispose(this.callOnModel); + + // we are disabled + if (!this.editor.getConfiguration().contribInfo.formatOnPaste) { + return; + } + + // no model + if (!this.editor.getModel()) { + return; + } + + let model = this.editor.getModel(); + + // no support + let [support] = DocumentRangeFormattingEditProviderRegistry.ordered(model); + if (!support || !support.provideDocumentRangeFormattingEdits) { + return; + } + + this.callOnModel.push(this.editor.onDidPaste((range: Range) => { + this.trigger(range); + })); + } + + private trigger(range: Range): void { + if (this.editor.getSelections().length > 1) { + return; + } + + const model = this.editor.getModel(); + const { tabSize, insertSpaces } = model.getOptions(); + const state = new EditorState(this.editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); + + getDocumentRangeFormattingEdits(model, range, { tabSize, insertSpaces }).then(edits => { + return this.workerService.computeMoreMinimalEdits(model.uri, edits, []); + }).then(edits => { + if (!state.validate(this.editor) || isFalsyOrEmpty(edits)) { + return; + } + EditOperationsCommand.execute(this.editor, edits); + alertFormattingEdits(edits); + }); + } + + public getId(): string { + return FormatOnPaste.ID; + } + + public dispose(): void { + this.callOnDispose = dispose(this.callOnDispose); + this.callOnModel = dispose(this.callOnModel); + } +} + +export abstract class AbstractFormatAction extends EditorAction { + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): TPromise { + + const workerService = accessor.get(IEditorWorkerService); + + const formattingPromise = this._getFormattingEdits(editor); + if (!formattingPromise) { + return TPromise.as(void 0); + } + + // Capture the state of the editor + const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); + + // Receive formatted value from worker + return formattingPromise.then(edits => workerService.computeMoreMinimalEdits(editor.getModel().uri, edits, editor.getSelections())).then(edits => { + if (!state.validate(editor) || isFalsyOrEmpty(edits)) { + return; + } + + EditOperationsCommand.execute(editor, edits); + alertFormattingEdits(edits); + editor.focus(); + }); + } + + protected abstract _getFormattingEdits(editor: editorCommon.ICommonCodeEditor): TPromise; +} + + +@editorAction +export class FormatDocumentAction extends AbstractFormatAction { + + constructor() { + super({ + id: 'editor.action.formatDocument', + label: nls.localize('formatDocument.label', "Format Document"), + alias: 'Format Document', + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, + // secondary: [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D)], + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I } + }, + menuOpts: { + group: '1_modification', + order: 1.3 + } + }); + } + + protected _getFormattingEdits(editor: editorCommon.ICommonCodeEditor): TPromise { + const model = editor.getModel(); + const { tabSize, insertSpaces } = model.getOptions(); + return getDocumentFormattingEdits(model, { tabSize, insertSpaces }); + } +} + +@editorAction +export class FormatSelectionAction extends AbstractFormatAction { + + constructor() { + super({ + id: 'editor.action.formatSelection', + label: nls.localize('formatSelection.label', "Format Selection"), + alias: 'Format Code', + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasNonEmptySelection), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F) + }, + menuOpts: { + group: '1_modification', + order: 1.31 + } + }); + } + + protected _getFormattingEdits(editor: editorCommon.ICommonCodeEditor): TPromise { + const model = editor.getModel(); + const { tabSize, insertSpaces } = model.getOptions(); + return getDocumentRangeFormattingEdits(model, editor.getSelection(), { tabSize, insertSpaces }); + } +} + +// this is the old format action that does both (format document OR format selection) +// and we keep it here such that existing keybinding configurations etc will still work +CommandsRegistry.registerCommand('editor.action.format', accessor => { + const editor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); + if (editor) { + return new class extends AbstractFormatAction { + constructor() { + super({}); + } + _getFormattingEdits(editor: editorCommon.ICommonCodeEditor): TPromise { + const model = editor.getModel(); + const editorSelection = editor.getSelection(); + const { tabSize, insertSpaces } = model.getOptions(); + + return editorSelection.isEmpty() + ? getDocumentFormattingEdits(model, { tabSize, insertSpaces }) + : getDocumentRangeFormattingEdits(model, editorSelection, { tabSize, insertSpaces }); + } + }().run(accessor, editor); + } + return undefined; +}); diff --git a/src/vs/editor/contrib/format/common/format.ts b/src/vs/editor/contrib/format/common/format.ts new file mode 100644 index 0000000000..5f408a8e07 --- /dev/null +++ b/src/vs/editor/contrib/format/common/format.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; +import URI from 'vs/base/common/uri'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Range } from 'vs/editor/common/core/range'; +import { IReadOnlyModel } from 'vs/editor/common/editorCommon'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry, FormattingOptions, TextEdit } from 'vs/editor/common/modes'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { asWinJsPromise, sequence } from 'vs/base/common/async'; +import { Position } from 'vs/editor/common/core/position'; + +export function getDocumentRangeFormattingEdits(model: IReadOnlyModel, range: Range, options: FormattingOptions): TPromise { + + const providers = DocumentRangeFormattingEditProviderRegistry.ordered(model); + + if (providers.length === 0) { + return TPromise.as(undefined); + } + + let result: TextEdit[]; + return sequence(providers.map(provider => { + if (isFalsyOrEmpty(result)) { + return () => { + return asWinJsPromise(token => provider.provideDocumentRangeFormattingEdits(model, range, options, token)).then(value => { + result = value; + }, onUnexpectedExternalError); + }; + } + return undefined; + })).then(() => result); +} + +export function getDocumentFormattingEdits(model: IReadOnlyModel, options: FormattingOptions): TPromise { + const providers = DocumentFormattingEditProviderRegistry.ordered(model); + + // try range formatters when no document formatter is registered + if (providers.length === 0) { + return getDocumentRangeFormattingEdits(model, model.getFullModelRange(), options); + } + + let result: TextEdit[]; + return sequence(providers.map(provider => { + if (isFalsyOrEmpty(result)) { + return () => { + return asWinJsPromise(token => provider.provideDocumentFormattingEdits(model, options, token)).then(value => { + result = value; + }, onUnexpectedExternalError); + }; + } + return undefined; + })).then(() => result); +} + +export function getOnTypeFormattingEdits(model: IReadOnlyModel, position: Position, ch: string, options: FormattingOptions): TPromise { + const [support] = OnTypeFormattingEditProviderRegistry.ordered(model); + if (!support) { + return TPromise.as(undefined); + } + if (support.autoFormatTriggerCharacters.indexOf(ch) < 0) { + return TPromise.as(undefined); + } + + return asWinJsPromise((token) => { + return support.provideOnTypeFormattingEdits(model, position, ch, options, token); + }).then(r => r, onUnexpectedExternalError); +} + +CommonEditorRegistry.registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) { + const { resource, range, options } = args; + if (!(resource instanceof URI) || !Range.isIRange(range)) { + throw illegalArgument(); + } + const model = accessor.get(IModelService).getModel(resource); + if (!model) { + throw illegalArgument('resource'); + } + return getDocumentRangeFormattingEdits(model, Range.lift(range), options); +}); + +CommonEditorRegistry.registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) { + const { resource, options } = args; + if (!(resource instanceof URI)) { + throw illegalArgument('resource'); + } + const model = accessor.get(IModelService).getModel(resource); + if (!model) { + throw illegalArgument('resource'); + } + + return getDocumentFormattingEdits(model, options); +}); + +CommonEditorRegistry.registerDefaultLanguageCommand('_executeFormatOnTypeProvider', function (model, position, args) { + const { ch, options } = args; + if (typeof ch !== 'string') { + throw illegalArgument('ch'); + } + return getOnTypeFormattingEdits(model, position, ch, options); +}); diff --git a/src/vs/editor/contrib/format/common/formatCommand.ts b/src/vs/editor/contrib/format/common/formatCommand.ts new file mode 100644 index 0000000000..c4c3a9014b --- /dev/null +++ b/src/vs/editor/contrib/format/common/formatCommand.ts @@ -0,0 +1,134 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { Range } from 'vs/editor/common/core/range'; +import { TextEdit } from 'vs/editor/common/modes'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Selection } from 'vs/editor/common/core/selection'; + +export class EditOperationsCommand implements editorCommon.ICommand { + + static execute(editor: editorCommon.ICommonCodeEditor, edits: TextEdit[]) { + const cmd = new EditOperationsCommand(edits, editor.getSelection()); + if (typeof cmd._newEol === 'number') { + editor.getModel().setEOL(cmd._newEol); + } + editor.pushUndoStop(); + editor.executeCommand('formatEditsCommand', cmd); + editor.pushUndoStop(); + } + + private _edits: TextEdit[]; + private _newEol: editorCommon.EndOfLineSequence; + + private _initialSelection: Selection; + private _selectionId: string; + + constructor(edits: TextEdit[], initialSelection: Selection) { + this._initialSelection = initialSelection; + this._edits = []; + this._newEol = undefined; + + for (let edit of edits) { + if (typeof edit.eol === 'number') { + this._newEol = edit.eol; + } + if (edit.range && typeof edit.text === 'string') { + this._edits.push(edit); + } + } + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + + for (let edit of this._edits) { + // We know that this edit.range comes from the mirror model, so it should only contain \n and no \r's + let trimEdit = EditOperationsCommand.trimEdit(edit, model); + if (trimEdit !== null) { // produced above in case the edit.text is identical to the existing text + builder.addEditOperation(Range.lift(edit.range), edit.text); + } + } + + var selectionIsSet = false; + if (Array.isArray(this._edits) && this._edits.length === 1 && this._initialSelection.isEmpty()) { + if (this._edits[0].range.startColumn === this._initialSelection.endColumn && + this._edits[0].range.startLineNumber === this._initialSelection.endLineNumber) { + selectionIsSet = true; + this._selectionId = builder.trackSelection(this._initialSelection, true); + } else if (this._edits[0].range.endColumn === this._initialSelection.startColumn && + this._edits[0].range.endLineNumber === this._initialSelection.startLineNumber) { + selectionIsSet = true; + this._selectionId = builder.trackSelection(this._initialSelection, false); + } + } + + if (!selectionIsSet) { + this._selectionId = builder.trackSelection(this._initialSelection); + } + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + return helper.getTrackedSelection(this._selectionId); + } + + static fixLineTerminators(edit: editorCommon.ISingleEditOperation, model: editorCommon.ITokenizedModel): void { + edit.text = edit.text.replace(/\r\n|\r|\n/g, model.getEOL()); + } + + /** + * This is used to minimize the edits by removing changes that appear on the edges of the range which are identical + * to the current text. + * + * The reason this was introduced is to allow better selection tracking of the current cursor and solve + * bug #15108. There the cursor was jumping since the tracked selection was in the middle of the range edit + * and was lost. + */ + static trimEdit(edit: editorCommon.ISingleEditOperation, model: editorCommon.ITokenizedModel): editorCommon.ISingleEditOperation { + + this.fixLineTerminators(edit, model); + + return this._trimEdit(model.validateRange(edit.range), edit.text, edit.forceMoveMarkers, model); + } + + static _trimEdit(editRange: Range, editText: string, editForceMoveMarkers: boolean, model: editorCommon.ITokenizedModel): editorCommon.ISingleEditOperation { + + let currentText = model.getValueInRange(editRange); + + // Find the equal characters in the front + let commonPrefixLength = strings.commonPrefixLength(editText, currentText); + + // If the two strings are identical, return no edit (no-op) + if (commonPrefixLength === currentText.length && commonPrefixLength === editText.length) { + return null; + } + + if (commonPrefixLength > 0) { + // Apply front trimming + let newStartPosition = model.modifyPosition(editRange.getStartPosition(), commonPrefixLength); + editRange = new Range(newStartPosition.lineNumber, newStartPosition.column, editRange.endLineNumber, editRange.endColumn); + editText = editText.substring(commonPrefixLength); + currentText = currentText.substr(commonPrefixLength); + } + + // Find the equal characters in the rear + let commonSuffixLength = strings.commonSuffixLength(editText, currentText); + + if (commonSuffixLength > 0) { + // Apply rear trimming + let newEndPosition = model.modifyPosition(editRange.getEndPosition(), -commonSuffixLength); + editRange = new Range(editRange.startLineNumber, editRange.startColumn, newEndPosition.lineNumber, newEndPosition.column); + editText = editText.substring(0, editText.length - commonSuffixLength); + currentText = currentText.substring(0, currentText.length - commonSuffixLength); + } + + return { + text: editText, + range: editRange, + forceMoveMarkers: editForceMoveMarkers + }; + } +} diff --git a/src/vs/editor/contrib/format/test/common/formatCommand.test.ts b/src/vs/editor/contrib/format/test/common/formatCommand.test.ts new file mode 100644 index 0000000000..f9d311f63e --- /dev/null +++ b/src/vs/editor/contrib/format/test/common/formatCommand.test.ts @@ -0,0 +1,313 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ISingleEditOperation } from 'vs/editor/common/editorCommon'; +import { Model } from 'vs/editor/common/model/model'; +import { EditOperationsCommand } from 'vs/editor/contrib/format/common/formatCommand'; +import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; + +function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): ISingleEditOperation { + return { + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + text: text.join('\n'), + forceMoveMarkers: false + }; +} + +suite('FormatCommand.trimEdit', () => { + function testTrimEdit(lines: string[], edit: ISingleEditOperation, expected: ISingleEditOperation): void { + let model = Model.createFromString(lines.join('\n')); + let actual = EditOperationsCommand.trimEdit(edit, model); + assert.deepEqual(actual, expected); + model.dispose(); + } + + test('single-line no-op', () => { + testTrimEdit( + [ + 'some text', + 'some other text' + ], + editOp(1, 1, 1, 10, [ + 'some text' + ]), + null + ); + }); + + test('multi-line no-op 1', () => { + testTrimEdit( + [ + 'some text', + 'some other text' + ], + editOp(1, 1, 2, 16, [ + 'some text', + 'some other text' + ]), + null + ); + }); + + test('multi-line no-op 2', () => { + testTrimEdit( + [ + 'some text', + 'some other text' + ], + editOp(1, 1, 2, 1, [ + 'some text', + '' + ]), + null + ); + }); + + test('simple prefix, no suffix', () => { + testTrimEdit( + [ + 'some text', + 'some other text' + ], + editOp(1, 1, 1, 10, [ + 'some interesting thing' + ]), + editOp(1, 6, 1, 10, [ + 'interesting thing' + ]) + ); + }); + + test('whole line prefix, no suffix', () => { + testTrimEdit( + [ + 'some text', + 'some other text' + ], + editOp(1, 1, 1, 10, [ + 'some text', + 'interesting thing' + ]), + editOp(1, 10, 1, 10, [ + '', + 'interesting thing' + ]) + ); + }); + + test('multi-line prefix, no suffix', () => { + testTrimEdit( + [ + 'some text', + 'some other text' + ], + editOp(1, 1, 2, 16, [ + 'some text', + 'some other interesting thing' + ]), + editOp(2, 12, 2, 16, [ + 'interesting thing' + ]) + ); + }); + + test('no prefix, simple suffix', () => { + testTrimEdit( + [ + 'some text', + 'some other text' + ], + editOp(1, 1, 1, 10, [ + 'interesting text' + ]), + editOp(1, 1, 1, 5, [ + 'interesting' + ]) + ); + }); + + test('no prefix, whole line suffix', () => { + testTrimEdit( + [ + 'some text', + 'some other text' + ], + editOp(1, 1, 1, 10, [ + 'interesting thing', + 'some text' + ]), + editOp(1, 1, 1, 1, [ + 'interesting thing', + '' + ]) + ); + }); + + test('no prefix, multi-line suffix', () => { + testTrimEdit( + [ + 'some text', + 'some other text' + ], + editOp(1, 1, 2, 16, [ + 'interesting thing text', + 'some other text' + ]), + editOp(1, 1, 1, 5, [ + 'interesting thing' + ]) + ); + }); + + test('no overlapping prefix & suffix', () => { + testTrimEdit( + [ + 'some cool text' + ], + editOp(1, 1, 1, 15, [ + 'some interesting text' + ]), + editOp(1, 6, 1, 10, [ + 'interesting' + ]) + ); + }); + + test('overlapping prefix & suffix 1', () => { + testTrimEdit( + [ + 'some cool text' + ], + editOp(1, 1, 1, 15, [ + 'some cool cool text' + ]), + editOp(1, 11, 1, 11, [ + 'cool ' + ]) + ); + }); + + test('overlapping prefix & suffix 2', () => { + testTrimEdit( + [ + 'some cool cool text' + ], + editOp(1, 1, 1, 29, [ + 'some cool text' + ]), + editOp(1, 11, 1, 16, [ + '' + ]) + ); + }); +}); + +suite('FormatCommand', () => { + function testFormatCommand(lines: string[], selection: Selection, edits: ISingleEditOperation[], expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new EditOperationsCommand(edits, sel), expectedLines, expectedSelection); + } + + test('no-op', () => { + testFormatCommand( + [ + 'some text', + 'some other text' + ], + new Selection(2, 1, 2, 5), + [ + editOp(1, 1, 2, 16, [ + 'some text', + 'some other text' + ]) + ], + [ + 'some text', + 'some other text' + ], + new Selection(2, 1, 2, 5) + ); + }); + + test('trim beginning', () => { + testFormatCommand( + [ + 'some text', + 'some other text' + ], + new Selection(2, 1, 2, 5), + [ + editOp(1, 1, 2, 16, [ + 'some text', + 'some new other text' + ]) + ], + [ + 'some text', + 'some new other text' + ], + new Selection(2, 1, 2, 5) + ); + }); + + test('issue #144', () => { + testFormatCommand( + [ + 'package caddy', + '', + 'func main() {', + '\tfmt.Println("Hello World! :)")', + '}', + '' + ], + new Selection(1, 1, 1, 1), + [ + editOp(1, 1, 6, 1, [ + 'package caddy', + '', + 'import "fmt"', + '', + 'func main() {', + '\tfmt.Println("Hello World! :)")', + '}', + '' + ]) + ], + [ + 'package caddy', + '', + 'import "fmt"', + '', + 'func main() {', + '\tfmt.Println("Hello World! :)")', + '}', + '' + ], + new Selection(1, 1, 1, 1) + ); + }); + + test('issue #23765', () => { + testFormatCommand( + [ + ' let a;' + ], + new Selection(1, 1, 1, 1), + [ + editOp(1, 1, 1, 2, [ + '' + ]) + ], + [ + 'let a;' + ], + new Selection(1, 1, 1, 1) + ); + }); + +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/goToDeclaration/browser/clickLinkGesture.ts b/src/vs/editor/contrib/goToDeclaration/browser/clickLinkGesture.ts new file mode 100644 index 0000000000..425da16131 --- /dev/null +++ b/src/vs/editor/contrib/goToDeclaration/browser/clickLinkGesture.ts @@ -0,0 +1,205 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./goToDeclarationMouse'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import * as browser from 'vs/base/browser/browser'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { ICodeEditor, IEditorMouseEvent, IMouseTarget } from 'vs/editor/browser/editorBrowser'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import Event, { Emitter } from 'vs/base/common/event'; +import * as platform from 'vs/base/common/platform'; + +function hasModifier(e: { ctrlKey: boolean; shiftKey: boolean; altKey: boolean; metaKey: boolean }, modifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'): boolean { + return !!e[modifier]; +} + +/** + * An event that encapsulates the various trigger modifiers logic needed for go to definition. + */ +export class ClickLinkMouseEvent { + + public readonly target: IMouseTarget; + public readonly hasTriggerModifier: boolean; + public readonly hasSideBySideModifier: boolean; + public readonly isNoneOrSingleMouseDown: boolean; + + constructor(source: IEditorMouseEvent, opts: ClickLinkOptions) { + this.target = source.target; + this.hasTriggerModifier = hasModifier(source.event, opts.triggerModifier); + this.hasSideBySideModifier = hasModifier(source.event, opts.triggerSideBySideModifier); + this.isNoneOrSingleMouseDown = (browser.isIE || source.event.detail <= 1); // IE does not support event.detail properly + } +} + +/** + * An event that encapsulates the various trigger modifiers logic needed for go to definition. + */ +export class ClickLinkKeyboardEvent { + + public readonly keyCodeIsTriggerKey: boolean; + public readonly keyCodeIsSideBySideKey: boolean; + public readonly hasTriggerModifier: boolean; + + constructor(source: IKeyboardEvent, opts: ClickLinkOptions) { + this.keyCodeIsTriggerKey = (source.keyCode === opts.triggerKey); + this.keyCodeIsSideBySideKey = (source.keyCode === opts.triggerSideBySideKey); + this.hasTriggerModifier = hasModifier(source, opts.triggerModifier); + } +} + +export class ClickLinkOptions { + + public readonly triggerKey: KeyCode; + public readonly triggerModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'; + public readonly triggerSideBySideKey: KeyCode; + public readonly triggerSideBySideModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'; + + constructor( + triggerKey: KeyCode, + triggerModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey', + triggerSideBySideKey: KeyCode, + triggerSideBySideModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey' + ) { + this.triggerKey = triggerKey; + this.triggerModifier = triggerModifier; + this.triggerSideBySideKey = triggerSideBySideKey; + this.triggerSideBySideModifier = triggerSideBySideModifier; + } + + public equals(other: ClickLinkOptions): boolean { + return ( + this.triggerKey === other.triggerKey + && this.triggerModifier === other.triggerModifier + && this.triggerSideBySideKey === other.triggerSideBySideKey + && this.triggerSideBySideModifier === other.triggerSideBySideModifier + ); + } +} + +function createOptions(multiCursorModifier: 'altKey' | 'ctrlKey' | 'metaKey'): ClickLinkOptions { + if (multiCursorModifier === 'altKey') { + if (platform.isMacintosh) { + return new ClickLinkOptions(KeyCode.Meta, 'metaKey', KeyCode.Alt, 'altKey'); + } + return new ClickLinkOptions(KeyCode.Ctrl, 'ctrlKey', KeyCode.Alt, 'altKey'); + } + + if (platform.isMacintosh) { + return new ClickLinkOptions(KeyCode.Alt, 'altKey', KeyCode.Meta, 'metaKey'); + } + return new ClickLinkOptions(KeyCode.Alt, 'altKey', KeyCode.Ctrl, 'ctrlKey'); +} + +export class ClickLinkGesture extends Disposable { + + private readonly _onMouseMoveOrRelevantKeyDown: Emitter<[ClickLinkMouseEvent, ClickLinkKeyboardEvent]> = this._register(new Emitter<[ClickLinkMouseEvent, ClickLinkKeyboardEvent]>()); + public readonly onMouseMoveOrRelevantKeyDown: Event<[ClickLinkMouseEvent, ClickLinkKeyboardEvent]> = this._onMouseMoveOrRelevantKeyDown.event; + + private readonly _onExecute: Emitter = this._register(new Emitter()); + public readonly onExecute: Event = this._onExecute.event; + + private readonly _onCancel: Emitter = this._register(new Emitter()); + public readonly onCancel: Event = this._onCancel.event; + + private readonly _editor: ICodeEditor; + private _opts: ClickLinkOptions; + + private lastMouseMoveEvent: ClickLinkMouseEvent; + private hasTriggerKeyOnMouseDown: boolean; + + constructor(editor: ICodeEditor) { + super(); + + this._editor = editor; + this._opts = createOptions(this._editor.getConfiguration().multiCursorModifier); + + this.lastMouseMoveEvent = null; + this.hasTriggerKeyOnMouseDown = false; + + this._register(this._editor.onDidChangeConfiguration((e) => { + if (e.multiCursorModifier) { + const newOpts = createOptions(this._editor.getConfiguration().multiCursorModifier); + if (this._opts.equals(newOpts)) { + return; + } + this._opts = newOpts; + this.lastMouseMoveEvent = null; + this.hasTriggerKeyOnMouseDown = false; + this._onCancel.fire(); + } + })); + this._register(this._editor.onMouseMove((e: IEditorMouseEvent) => this.onEditorMouseMove(new ClickLinkMouseEvent(e, this._opts)))); + this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(new ClickLinkMouseEvent(e, this._opts)))); + this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this.onEditorMouseUp(new ClickLinkMouseEvent(e, this._opts)))); + this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(new ClickLinkKeyboardEvent(e, this._opts)))); + this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(new ClickLinkKeyboardEvent(e, this._opts)))); + this._register(this._editor.onMouseDrag(() => this.resetHandler())); + + this._register(this._editor.onDidChangeCursorSelection((e) => this.onDidChangeCursorSelection(e))); + this._register(this._editor.onDidChangeModel((e) => this.resetHandler())); + this._register(this._editor.onDidChangeModelContent(() => this.resetHandler())); + this._register(this._editor.onDidScrollChange((e) => { + if (e.scrollTopChanged || e.scrollLeftChanged) { + this.resetHandler(); + } + })); + } + + private onDidChangeCursorSelection(e: ICursorSelectionChangedEvent): void { + if (e.selection && e.selection.startColumn !== e.selection.endColumn) { + this.resetHandler(); // immediately stop this feature if the user starts to select (https://github.com/Microsoft/vscode/issues/7827) + } + } + + private onEditorMouseMove(mouseEvent: ClickLinkMouseEvent): void { + this.lastMouseMoveEvent = mouseEvent; + + this._onMouseMoveOrRelevantKeyDown.fire([mouseEvent, null]); + } + + private onEditorMouseDown(mouseEvent: ClickLinkMouseEvent): void { + // We need to record if we had the trigger key on mouse down because someone might select something in the editor + // holding the mouse down and then while mouse is down start to press Ctrl/Cmd to start a copy operation and then + // release the mouse button without wanting to do the navigation. + // With this flag we prevent goto definition if the mouse was down before the trigger key was pressed. + this.hasTriggerKeyOnMouseDown = mouseEvent.hasTriggerModifier; + } + + private onEditorMouseUp(mouseEvent: ClickLinkMouseEvent): void { + if (this.hasTriggerKeyOnMouseDown) { + this._onExecute.fire(mouseEvent); + } + } + + private onEditorKeyDown(e: ClickLinkKeyboardEvent): void { + if ( + this.lastMouseMoveEvent + && ( + e.keyCodeIsTriggerKey // User just pressed Ctrl/Cmd (normal goto definition) + || (e.keyCodeIsSideBySideKey && e.hasTriggerModifier) // User pressed Ctrl/Cmd+Alt (goto definition to the side) + ) + ) { + this._onMouseMoveOrRelevantKeyDown.fire([this.lastMouseMoveEvent, e]); + } else if (e.hasTriggerModifier) { + this._onCancel.fire(); // remove decorations if user holds another key with ctrl/cmd to prevent accident goto declaration + } + } + + private onEditorKeyUp(e: ClickLinkKeyboardEvent): void { + if (e.keyCodeIsTriggerKey) { + this._onCancel.fire(); + } + } + + private resetHandler(): void { + this.lastMouseMoveEvent = null; + this.hasTriggerKeyOnMouseDown = false; + this._onCancel.fire(); + } +} diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts new file mode 100644 index 0000000000..135687d3e3 --- /dev/null +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IReadOnlyModel } from 'vs/editor/common/editorCommon'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; +import { DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, Location } from 'vs/editor/common/modes'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { asWinJsPromise } from 'vs/base/common/async'; +import { Position } from 'vs/editor/common/core/position'; + +function outputResults(promises: TPromise[]) { + return TPromise.join(promises).then(allReferences => { + let result: Location[] = []; + for (let references of allReferences) { + if (Array.isArray(references)) { + result.push(...references); + } else if (references) { + result.push(references); + } + } + return result; + }); +} + +function getDefinitions( + model: IReadOnlyModel, + position: Position, + registry: LanguageFeatureRegistry, + provide: (provider: T, model: IReadOnlyModel, position: Position, token: CancellationToken) => Location | Location[] | Thenable +): TPromise { + const provider = registry.ordered(model); + + // get results + const promises = provider.map((provider, idx) => { + return asWinJsPromise((token) => { + return provide(provider, model, position, token); + }).then(undefined, err => { + onUnexpectedExternalError(err); + return null; + }); + }); + return outputResults(promises); +} + + +export function getDefinitionsAtPosition(model: IReadOnlyModel, position: Position): TPromise { + return getDefinitions(model, position, DefinitionProviderRegistry, (provider, model, position, token) => { + return provider.provideDefinition(model, position, token); + }); +} + +export function getImplementationsAtPosition(model: IReadOnlyModel, position: Position): TPromise { + return getDefinitions(model, position, ImplementationProviderRegistry, (provider, model, position, token) => { + return provider.provideImplementation(model, position, token); + }); +} + +export function getTypeDefinitionsAtPosition(model: IReadOnlyModel, position: Position): TPromise { + return getDefinitions(model, position, TypeDefinitionProviderRegistry, (provider, model, position, token) => { + return provider.provideTypeDefinition(model, position, token); + }); +} + +CommonEditorRegistry.registerDefaultLanguageCommand('_executeDefinitionProvider', getDefinitionsAtPosition); +CommonEditorRegistry.registerDefaultLanguageCommand('_executeImplementationProvider', getImplementationsAtPosition); +CommonEditorRegistry.registerDefaultLanguageCommand('_executeTypeDefinitionProvider', getTypeDefinitionsAtPosition); \ No newline at end of file diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts new file mode 100644 index 0000000000..9e460a2f29 --- /dev/null +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts @@ -0,0 +1,371 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import * as platform from 'vs/base/common/platform'; +import Severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IEditorService } from 'vs/platform/editor/common/editor'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { editorAction, IActionOptions, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { Location } from 'vs/editor/common/modes'; +import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from './goToDeclaration'; +import { ReferencesController } from 'vs/editor/contrib/referenceSearch/browser/referencesController'; +import { ReferencesModel } from 'vs/editor/contrib/referenceSearch/browser/referencesModel'; +import { PeekContext } from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { MessageController } from './messageController'; +import * as corePosition from 'vs/editor/common/core/position'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { IProgressService } from 'vs/platform/progress/common/progress'; + +export class DefinitionActionConfig { + + constructor( + public readonly openToSide = false, + public readonly openInPeek = false, + public readonly filterCurrent = true, + public readonly showMessage = true, + ) { + // + } +} + +export class DefinitionAction extends EditorAction { + + private _configuration: DefinitionActionConfig; + + constructor(configuration: DefinitionActionConfig, opts: IActionOptions) { + super(opts); + this._configuration = configuration; + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): TPromise { + const messageService = accessor.get(IMessageService); + const editorService = accessor.get(IEditorService); + const progressService = accessor.get(IProgressService); + + const model = editor.getModel(); + const pos = editor.getPosition(); + + const definitionPromise = this._getDeclarationsAtPosition(model, pos).then(references => { + + if (model.isDisposed() || editor.getModel() !== model) { + // new model, no more model + return; + } + + // * remove falsy references + // * find reference at the current pos + let idxOfCurrent = -1; + let result: Location[] = []; + for (let i = 0; i < references.length; i++) { + let reference = references[i]; + if (!reference || !reference.range) { + continue; + } + let { uri, range } = reference; + let newLen = result.push({ + uri, + range + }); + if (this._configuration.filterCurrent + && uri.toString() === model.uri.toString() + && Range.containsPosition(range, pos) + && idxOfCurrent === -1 + ) { + idxOfCurrent = newLen - 1; + } + } + + if (result.length === 0) { + // no result -> show message + if (this._configuration.showMessage) { + const info = model.getWordAtPosition(pos); + MessageController.get(editor).showMessage(this._getNoResultFoundMessage(info), pos); + } + } else if (result.length === 1 && idxOfCurrent !== -1) { + // only the position at which we are -> adjust selection + let [current] = result; + this._openReference(editorService, current, false); + + } else { + // handle multile results + this._onResult(editorService, editor, new ReferencesModel(result)); + } + + }, (err) => { + // report an error + messageService.show(Severity.Error, err); + }); + + progressService.showWhile(definitionPromise, 250); + return definitionPromise; + } + + protected _getDeclarationsAtPosition(model: editorCommon.IModel, position: corePosition.Position): TPromise { + return getDefinitionsAtPosition(model, position); + } + + protected _getNoResultFoundMessage(info?: editorCommon.IWordAtPosition): string { + return info && info.word + ? nls.localize('noResultWord', "No definition found for '{0}'", info.word) + : nls.localize('generic.noResults', "No definition found"); + } + + protected _getMetaTitle(model: ReferencesModel): string { + return model.references.length > 1 && nls.localize('meta.title', " – {0} definitions", model.references.length); + } + + private _onResult(editorService: IEditorService, editor: editorCommon.ICommonCodeEditor, model: ReferencesModel) { + + const msg = model.getAriaMessage(); + alert(msg); + + if (this._configuration.openInPeek) { + this._openInPeek(editorService, editor, model); + } else { + let next = model.nearestReference(editor.getModel().uri, editor.getPosition()); + this._openReference(editorService, next, this._configuration.openToSide).then(editor => { + if (editor && model.references.length > 1) { + this._openInPeek(editorService, editor, model); + } else { + model.dispose(); + } + }); + } + } + + private _openReference(editorService: IEditorService, reference: Location, sideBySide: boolean): TPromise { + let { uri, range } = reference; + return editorService.openEditor({ + resource: uri, + options: { + selection: Range.collapseToStart(range), + revealIfVisible: !sideBySide + } + }, sideBySide).then(editor => { + return editor && editor.getControl(); + }); + } + + private _openInPeek(editorService: IEditorService, target: editorCommon.ICommonCodeEditor, model: ReferencesModel) { + let controller = ReferencesController.get(target); + if (controller) { + controller.toggleWidget(target.getSelection(), TPromise.as(model), { + getMetaTitle: (model) => { + return this._getMetaTitle(model); + }, + onGoto: (reference) => { + controller.closeWidget(); + return this._openReference(editorService, reference, false); + } + }); + } else { + model.dispose(); + } + } +} + +const goToDeclarationKb = platform.isWeb + ? KeyMod.CtrlCmd | KeyCode.F12 + : KeyCode.F12; + +@editorAction +export class GoToDefinitionAction extends DefinitionAction { + + public static ID = 'editor.action.goToDeclaration'; + + constructor() { + super(new DefinitionActionConfig(), { + id: GoToDefinitionAction.ID, + label: nls.localize('actions.goToDecl.label', "Go to Definition"), + alias: 'Go to Definition', + precondition: ContextKeyExpr.and( + EditorContextKeys.hasDefinitionProvider, + EditorContextKeys.isInEmbeddedEditor.toNegated()), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: goToDeclarationKb + }, + menuOpts: { + group: 'navigation', + order: 1.1 + } + }); + } +} + +@editorAction +export class OpenDefinitionToSideAction extends DefinitionAction { + + public static ID = 'editor.action.openDeclarationToTheSide'; + + constructor() { + super(new DefinitionActionConfig(true), { + id: OpenDefinitionToSideAction.ID, + label: nls.localize('actions.goToDeclToSide.label', "Open Definition to the Side"), + alias: 'Open Definition to the Side', + precondition: ContextKeyExpr.and( + EditorContextKeys.hasDefinitionProvider, + EditorContextKeys.isInEmbeddedEditor.toNegated()), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, goToDeclarationKb) + } + }); + } +} + +@editorAction +export class PeekDefinitionAction extends DefinitionAction { + constructor() { + super(new DefinitionActionConfig(void 0, true, false), { + id: 'editor.action.previewDeclaration', + label: nls.localize('actions.previewDecl.label', "Peek Definition"), + alias: 'Peek Definition', + precondition: ContextKeyExpr.and( + EditorContextKeys.hasDefinitionProvider, + PeekContext.notInPeekEditor, + EditorContextKeys.isInEmbeddedEditor.toNegated()), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Alt | KeyCode.F12, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 } + }, + menuOpts: { + group: 'navigation', + order: 1.2 + } + }); + } +} + +export class ImplementationAction extends DefinitionAction { + protected _getDeclarationsAtPosition(model: editorCommon.IModel, position: corePosition.Position): TPromise { + return getImplementationsAtPosition(model, position); + } + + protected _getNoResultFoundMessage(info?: editorCommon.IWordAtPosition): string { + return info && info.word + ? nls.localize('goToImplementation.noResultWord', "No implementation found for '{0}'", info.word) + : nls.localize('goToImplementation.generic.noResults', "No implementation found"); + } + + protected _getMetaTitle(model: ReferencesModel): string { + return model.references.length > 1 && nls.localize('meta.implementations.title', " – {0} implementations", model.references.length); + } +} + +@editorAction +export class GoToImplementationAction extends ImplementationAction { + + public static ID = 'editor.action.goToImplementation'; + + constructor() { + super(new DefinitionActionConfig(), { + id: GoToImplementationAction.ID, + label: nls.localize('actions.goToImplementation.label', "Go to Implementation"), + alias: 'Go to Implementation', + precondition: ContextKeyExpr.and( + EditorContextKeys.hasImplementationProvider, + EditorContextKeys.isInEmbeddedEditor.toNegated()), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.F12 + } + }); + } +} + +@editorAction +export class PeekImplementationAction extends ImplementationAction { + + public static ID = 'editor.action.peekImplementation'; + + constructor() { + super(new DefinitionActionConfig(false, true, false), { + id: PeekImplementationAction.ID, + label: nls.localize('actions.peekImplementation.label', "Peek Implementation"), + alias: 'Peek Implementation', + precondition: ContextKeyExpr.and( + EditorContextKeys.hasImplementationProvider, + EditorContextKeys.isInEmbeddedEditor.toNegated()), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F12 + } + }); + } +} + +export class TypeDefinitionAction extends DefinitionAction { + protected _getDeclarationsAtPosition(model: editorCommon.IModel, position: corePosition.Position): TPromise { + return getTypeDefinitionsAtPosition(model, position); + } + + protected _getNoResultFoundMessage(info?: editorCommon.IWordAtPosition): string { + return info && info.word + ? nls.localize('goToTypeDefinition.noResultWord', "No type definition found for '{0}'", info.word) + : nls.localize('goToTypeDefinition.generic.noResults', "No type definition found"); + } + + protected _getMetaTitle(model: ReferencesModel): string { + return model.references.length > 1 && nls.localize('meta.typeDefinitions.title', " – {0} type definitions", model.references.length); + } +} + +@editorAction +export class GoToTypeDefintionAction extends TypeDefinitionAction { + + public static ID = 'editor.action.goToTypeDefinition'; + + constructor() { + super(new DefinitionActionConfig(), { + id: GoToTypeDefintionAction.ID, + label: nls.localize('actions.goToTypeDefinition.label', "Go to Type Definition"), + alias: 'Go to Type Definition', + precondition: ContextKeyExpr.and( + EditorContextKeys.hasTypeDefinitionProvider, + EditorContextKeys.isInEmbeddedEditor.toNegated()), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: 0 + }, + menuOpts: { + group: 'navigation', + order: 1.4 + } + }); + } +} + +@editorAction +export class PeekTypeDefinitionAction extends TypeDefinitionAction { + + public static ID = 'editor.action.peekTypeDefinition'; + + constructor() { + super(new DefinitionActionConfig(false, true, false), { + id: PeekTypeDefinitionAction.ID, + label: nls.localize('actions.peekTypeDefinition.label', "Peek Type Definition"), + alias: 'Peek Type Definition', + precondition: ContextKeyExpr.and( + EditorContextKeys.hasTypeDefinitionProvider, + EditorContextKeys.isInEmbeddedEditor.toNegated()), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: 0 + } + }); + } +} + diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.css b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.css new file mode 100644 index 0000000000..bb1fc6f543 --- /dev/null +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.css @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .goto-definition-link { + text-decoration: underline; + cursor: pointer; +} \ No newline at end of file diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.ts new file mode 100644 index 0000000000..fab70ee1e2 --- /dev/null +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.ts @@ -0,0 +1,226 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./goToDeclarationMouse'; +import * as nls from 'vs/nls'; +import { Throttler } from 'vs/base/common/async'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Location, DefinitionProviderRegistry } from 'vs/editor/common/modes'; +import { ICodeEditor, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { getDefinitionsAtPosition } from './goToDeclaration'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { EditorState, CodeEditorStateFlag } from 'vs/editor/common/core/editorState'; +import { DefinitionAction, DefinitionActionConfig } from './goToDeclarationCommands'; +import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDeclaration/browser/clickLinkGesture'; + +@editorContribution +class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.gotodefinitionwithmouse'; + static MAX_SOURCE_PREVIEW_LINES = 8; + + private editor: ICodeEditor; + private toUnhook: IDisposable[]; + private decorations: string[]; + private currentWordUnderMouse: editorCommon.IWordAtPosition; + private throttler: Throttler; + + constructor( + editor: ICodeEditor, + @ITextModelService private textModelResolverService: ITextModelService, + @IModeService private modeService: IModeService + ) { + this.toUnhook = []; + this.decorations = []; + this.editor = editor; + this.throttler = new Throttler(); + + let linkGesture = new ClickLinkGesture(editor); + this.toUnhook.push(linkGesture); + + this.toUnhook.push(linkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => { + this.startFindDefinition(mouseEvent, keyboardEvent); + })); + + this.toUnhook.push(linkGesture.onExecute((mouseEvent: ClickLinkMouseEvent) => { + if (this.isEnabled(mouseEvent)) { + this.gotoDefinition(mouseEvent.target, mouseEvent.hasSideBySideModifier).done(() => { + this.removeDecorations(); + }, (error: Error) => { + this.removeDecorations(); + onUnexpectedError(error); + }); + } + })); + + this.toUnhook.push(linkGesture.onCancel(() => { + this.removeDecorations(); + this.currentWordUnderMouse = null; + })); + + } + + private startFindDefinition(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): void { + if (!this.isEnabled(mouseEvent, withKey)) { + this.currentWordUnderMouse = null; + this.removeDecorations(); + return; + } + + // Find word at mouse position + let position = mouseEvent.target.position; + let word = position ? this.editor.getModel().getWordAtPosition(position) : null; + if (!word) { + this.currentWordUnderMouse = null; + this.removeDecorations(); + return; + } + + // Return early if word at position is still the same + if (this.currentWordUnderMouse && this.currentWordUnderMouse.startColumn === word.startColumn && this.currentWordUnderMouse.endColumn === word.endColumn && this.currentWordUnderMouse.word === word.word) { + return; + } + + this.currentWordUnderMouse = word; + + // Find definition and decorate word if found + let state = new EditorState(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection | CodeEditorStateFlag.Scroll); + + this.throttler.queue(() => { + return state.validate(this.editor) + ? this.findDefinition(mouseEvent.target) + : TPromise.as(null); + + }).then(results => { + if (!results || !results.length || !state.validate(this.editor)) { + this.removeDecorations(); + return; + } + + // Multiple results + if (results.length > 1) { + this.addDecoration( + new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn), + new MarkdownString().appendText(nls.localize('multipleResults', "Click to show {0} definitions.", results.length)) + ); + } + + // Single result + else { + let result = results[0]; + + if (!result.uri) { + return; + } + + this.textModelResolverService.createModelReference(result.uri).then(ref => { + + if (!ref.object || !ref.object.textEditorModel) { + ref.dispose(); + return; + } + + const { object: { textEditorModel } } = ref; + const { startLineNumber } = result.range; + + if (textEditorModel.getLineMaxColumn(startLineNumber) === 0) { + ref.dispose(); + return; + } + + const startIndent = textEditorModel.getLineFirstNonWhitespaceColumn(startLineNumber); + const maxLineNumber = Math.min(textEditorModel.getLineCount(), startLineNumber + GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES); + let endLineNumber = startLineNumber + 1; + let minIndent = startIndent; + + for (; endLineNumber < maxLineNumber; endLineNumber++) { + let endIndent = textEditorModel.getLineFirstNonWhitespaceColumn(endLineNumber); + minIndent = Math.min(minIndent, endIndent); + if (startIndent === endIndent) { + break; + } + } + + const previewRange = new Range(startLineNumber, 1, endLineNumber + 1, 1); + const value = textEditorModel.getValueInRange(previewRange).replace(new RegExp(`^\\s{${minIndent - 1}}`, 'gm'), '').trim(); + + this.addDecoration( + new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn), + new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilenameOrFirstLine(textEditorModel.uri.fsPath), value) + ); + ref.dispose(); + }); + } + }).done(undefined, onUnexpectedError); + } + + private addDecoration(range: Range, hoverMessage: MarkdownString): void { + + const newDecorations: editorCommon.IModelDeltaDecoration = { + range: range, + options: { + inlineClassName: 'goto-definition-link', + hoverMessage + } + }; + + this.decorations = this.editor.deltaDecorations(this.decorations, [newDecorations]); + } + + private removeDecorations(): void { + if (this.decorations.length > 0) { + this.decorations = this.editor.deltaDecorations(this.decorations, []); + } + } + + private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): boolean { + return this.editor.getModel() && + mouseEvent.isNoneOrSingleMouseDown && + mouseEvent.target.type === MouseTargetType.CONTENT_TEXT && + (mouseEvent.hasTriggerModifier || (withKey && withKey.keyCodeIsTriggerKey)) && + DefinitionProviderRegistry.has(this.editor.getModel()); + } + + private findDefinition(target: IMouseTarget): TPromise { + let model = this.editor.getModel(); + if (!model) { + return TPromise.as(null); + } + + return getDefinitionsAtPosition(this.editor.getModel(), target.position); + } + + private gotoDefinition(target: IMouseTarget, sideBySide: boolean): TPromise { + this.editor.setPosition(target.position); + const action = new DefinitionAction(new DefinitionActionConfig(sideBySide, false, true, false), { alias: undefined, label: undefined, id: undefined, precondition: undefined }); + return this.editor.invokeWithinContext(accessor => action.run(accessor, this.editor)); + } + + public getId(): string { + return GotoDefinitionWithMouseEditorContribution.ID; + } + + public dispose(): void { + this.toUnhook = dispose(this.toUnhook); + } +} + +registerThemingParticipant((theme, collector) => { + let activeLinkForeground = theme.getColor(editorActiveLinkForeground); + if (activeLinkForeground) { + collector.addRule(`.monaco-editor .goto-definition-link { color: ${activeLinkForeground} !important; }`); + } +}); diff --git a/src/vs/editor/contrib/goToDeclaration/browser/messageController.css b/src/vs/editor/contrib/goToDeclaration/browser/messageController.css new file mode 100644 index 0000000000..9775ef0cb5 --- /dev/null +++ b/src/vs/editor/contrib/goToDeclaration/browser/messageController.css @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .monaco-editor-overlaymessage { + padding-bottom: 8px; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} +.monaco-editor .monaco-editor-overlaymessage.fadeIn { + animation: fadeIn 150ms ease-out; +} + +@keyframes fadeOut { + from { opacity: 1; } + to { opacity: 0; } +} +.monaco-editor .monaco-editor-overlaymessage.fadeOut { + animation: fadeOut 100ms ease-out; +} + +.monaco-editor .monaco-editor-overlaymessage .message { + padding: 1px 4px; +} + +.monaco-editor .monaco-editor-overlaymessage .anchor { + width: 0 !important; + height: 0 !important; + border-color: transparent; + border-style: solid; + z-index: 1000; + border-width: 8px; + position: absolute; +} diff --git a/src/vs/editor/contrib/goToDeclaration/browser/messageController.ts b/src/vs/editor/contrib/goToDeclaration/browser/messageController.ts new file mode 100644 index 0000000000..6754b6d8a7 --- /dev/null +++ b/src/vs/editor/contrib/goToDeclaration/browser/messageController.ts @@ -0,0 +1,184 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./messageController'; +import { setDisposableTimeout } from 'vs/base/common/async'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { commonEditorContribution, CommonEditorRegistry, EditorCommand } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor, IContentWidget, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IPosition } from 'vs/editor/common/core/position'; +import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { inputValidationInfoBorder, inputValidationInfoBackground } from 'vs/platform/theme/common/colorRegistry'; + +@commonEditorContribution +export class MessageController { + + private static _id = 'editor.contrib.messageController'; + + static CONTEXT_SNIPPET_MODE = new RawContextKey('messageVisible', false); + + static get(editor: editorCommon.ICommonCodeEditor): MessageController { + return editor.getContribution(MessageController._id); + } + + getId(): string { + return MessageController._id; + } + + private _editor: ICodeEditor; + private _visible: IContextKey; + private _messageWidget: MessageWidget; + private _messageListeners: IDisposable[] = []; + + constructor( + editor: ICodeEditor, + @IContextKeyService contextKeyService: IContextKeyService + ) { + this._editor = editor; + this._visible = MessageController.CONTEXT_SNIPPET_MODE.bindTo(contextKeyService); + } + + dispose() { + this._visible.reset(); + } + + showMessage(message: string, position: IPosition): void { + + alert(message); + + this._visible.set(true); + dispose(this._messageWidget); + this._messageListeners = dispose(this._messageListeners); + this._messageWidget = new MessageWidget(this._editor, position, message); + + // close on blur, cursor, model change, dispose + this._messageListeners.push(this._editor.onDidBlurEditorText(() => this.closeMessage())); + this._messageListeners.push(this._editor.onDidChangeCursorPosition(() => this.closeMessage())); + this._messageListeners.push(this._editor.onDidDispose(() => this.closeMessage())); + this._messageListeners.push(this._editor.onDidChangeModel(() => this.closeMessage())); + + // close after 3s + this._messageListeners.push(setDisposableTimeout(() => this.closeMessage(), 3000)); + + // close on mouse move + let bounds: Range; + this._messageListeners.push(this._editor.onMouseMove(e => { + // outside the text area + if (!e.target.position) { + return; + } + + if (!bounds) { + // define bounding box around position and first mouse occurance + bounds = new Range(position.lineNumber - 3, 1, e.target.position.lineNumber + 3, 1); + } else if (!bounds.containsPosition(e.target.position)) { + // check if position is still in bounds + this.closeMessage(); + } + })); + } + + closeMessage(): void { + this._visible.reset(); + this._messageListeners = dispose(this._messageListeners); + this._messageListeners.push(MessageWidget.fadeOut(this._messageWidget)); + } +} + +const MessageCommand = EditorCommand.bindToContribution(MessageController.get); + + +CommonEditorRegistry.registerEditorCommand(new MessageCommand({ + id: 'leaveEditorMessage', + precondition: MessageController.CONTEXT_SNIPPET_MODE, + handler: c => c.closeMessage(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(30), + primary: KeyCode.Escape + } +})); + +class MessageWidget implements IContentWidget { + + // Editor.IContentWidget.allowEditorOverflow + readonly allowEditorOverflow = true; + readonly suppressMouseDown = false; + + private _editor: ICodeEditor; + private _position: IPosition; + private _domNode: HTMLDivElement; + + static fadeOut(messageWidget: MessageWidget): IDisposable { + let handle: number; + const dispose = () => { + messageWidget.dispose(); + clearTimeout(handle); + messageWidget.getDomNode().removeEventListener('animationend', dispose); + }; + handle = setTimeout(dispose, 110); + messageWidget.getDomNode().addEventListener('animationend', dispose); + messageWidget.getDomNode().classList.add('fadeOut'); + return { dispose }; + } + + constructor(editor: ICodeEditor, { lineNumber, column }: IPosition, text: string) { + + this._editor = editor; + this._editor.revealLinesInCenterIfOutsideViewport(lineNumber, lineNumber, editorCommon.ScrollType.Smooth); + this._position = { lineNumber, column: 1 }; + + this._domNode = document.createElement('div'); + this._domNode.style.paddingLeft = `${editor.getOffsetForColumn(lineNumber, column) - 6}px`; + this._domNode.classList.add('monaco-editor-overlaymessage'); + + const message = document.createElement('div'); + message.classList.add('message'); + message.textContent = text; + this._domNode.appendChild(message); + + const anchor = document.createElement('div'); + anchor.classList.add('anchor'); + this._domNode.appendChild(anchor); + + this._editor.addContentWidget(this); + this._domNode.classList.add('fadeIn'); + } + + dispose() { + this._editor.removeContentWidget(this); + } + + getId(): string { + return 'messageoverlay'; + } + + getDomNode(): HTMLElement { + return this._domNode; + } + + getPosition(): IContentWidgetPosition { + return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE] }; + } +} + +registerThemingParticipant((theme, collector) => { + let border = theme.getColor(inputValidationInfoBorder); + if (border) { + let borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1; + collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor { border-top-color: ${border}; }`); + collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .message { border: ${borderWidth}px solid ${border}; }`); + } + let background = theme.getColor(inputValidationInfoBackground); + if (background) { + collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .message { background-color: ${background}; }`); + } +}); diff --git a/src/vs/editor/contrib/gotoError/browser/gotoError.css b/src/vs/editor/contrib/gotoError/browser/gotoError.css new file mode 100644 index 0000000000..d4c423c886 --- /dev/null +++ b/src/vs/editor/contrib/gotoError/browser/gotoError.css @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* marker zone */ + +.monaco-editor .marker-widget { + padding-left: 2px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.monaco-editor .marker-widget > .stale { + opacity: 0.6; + font-style: italic; +} + +.monaco-editor .marker-widget div.block { + display: inline-block; + vertical-align: top; +} + +.monaco-editor .marker-widget .title { + display: inline-block; + padding-right: 5px; +} + +.monaco-editor .marker-widget .descriptioncontainer { + white-space: pre; + -webkit-user-select: text; + user-select: text; +} diff --git a/src/vs/editor/contrib/gotoError/browser/gotoError.ts b/src/vs/editor/contrib/gotoError/browser/gotoError.ts new file mode 100644 index 0000000000..0691d98c80 --- /dev/null +++ b/src/vs/editor/contrib/gotoError/browser/gotoError.ts @@ -0,0 +1,496 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./gotoError'; +import * as nls from 'vs/nls'; +import { Emitter } from 'vs/base/common/event'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import Severity from 'vs/base/common/severity'; +import URI from 'vs/base/common/uri'; +import * as dom from 'vs/base/browser/dom'; +import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, IActionOptions, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget'; +import { registerColor, oneOf } from 'vs/platform/theme/common/colorRegistry'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; +import { Color } from 'vs/base/common/color'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { AccessibilitySupport } from 'vs/base/common/platform'; +import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder } from 'vs/editor/common/view/editorColorRegistry'; + +class MarkerModel { + + private _editor: ICodeEditor; + private _markers: IMarker[]; + private _nextIdx: number; + private _toUnbind: IDisposable[]; + private _ignoreSelectionChange: boolean; + private _onCurrentMarkerChanged: Emitter; + private _onMarkerSetChanged: Emitter; + + constructor(editor: ICodeEditor, markers: IMarker[]) { + this._editor = editor; + this._markers = null; + this._nextIdx = -1; + this._toUnbind = []; + this._ignoreSelectionChange = false; + this._onCurrentMarkerChanged = new Emitter(); + this._onMarkerSetChanged = new Emitter(); + this.setMarkers(markers); + + // listen on editor + this._toUnbind.push(this._editor.onDidDispose(() => this.dispose())); + this._toUnbind.push(this._editor.onDidChangeCursorPosition(() => { + if (!this._ignoreSelectionChange) { + this._nextIdx = -1; + } + })); + } + + public get onCurrentMarkerChanged() { + return this._onCurrentMarkerChanged.event; + } + + public get onMarkerSetChanged() { + return this._onMarkerSetChanged.event; + } + + public setMarkers(markers: IMarker[]): void { + // assign + this._markers = markers || []; + + // sort markers + this._markers.sort((left, right) => Severity.compare(left.severity, right.severity) || Range.compareRangesUsingStarts(left, right)); + + this._nextIdx = -1; + this._onMarkerSetChanged.fire(this); + } + + public withoutWatchingEditorPosition(callback: () => void): void { + this._ignoreSelectionChange = true; + try { + callback(); + } finally { + this._ignoreSelectionChange = false; + } + } + + private _initIdx(fwd: boolean): void { + let found = false; + const position = this._editor.getPosition(); + for (let i = 0; i < this._markers.length; i++) { + let range = Range.lift(this._markers[i]); + + if (range.isEmpty()) { + const word = this._editor.getModel().getWordAtPosition(range.getStartPosition()); + if (word) { + range = new Range(range.startLineNumber, word.startColumn, range.startLineNumber, word.endColumn); + } + } + + if (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition())) { + this._nextIdx = i + (fwd ? 0 : -1); + found = true; + break; + } + } + if (!found) { + // after the last change + this._nextIdx = fwd ? 0 : this._markers.length - 1; + } + if (this._nextIdx < 0) { + this._nextIdx = this._markers.length - 1; + } + } + + private move(fwd: boolean): void { + if (!this.canNavigate()) { + this._onCurrentMarkerChanged.fire(undefined); + return; + } + + if (this._nextIdx === -1) { + this._initIdx(fwd); + + } else if (fwd) { + this._nextIdx += 1; + if (this._nextIdx >= this._markers.length) { + this._nextIdx = 0; + } + } else { + this._nextIdx -= 1; + if (this._nextIdx < 0) { + this._nextIdx = this._markers.length - 1; + } + } + const marker = this._markers[this._nextIdx]; + this._onCurrentMarkerChanged.fire(marker); + } + + public canNavigate(): boolean { + return this._markers.length > 0; + } + + public next(): void { + this.move(true); + } + + public previous(): void { + this.move(false); + } + + public findMarkerAtPosition(pos: Position): IMarker { + for (const marker of this._markers) { + if (Range.containsPosition(marker, pos)) { + return marker; + } + } + return undefined; + } + + public get total() { + return this._markers.length; + } + + public indexOf(marker: IMarker): number { + return 1 + this._markers.indexOf(marker); + } + + public reveal(): void { + + if (this._nextIdx === -1) { + return; + } + + this.withoutWatchingEditorPosition(() => { + const pos = new Position(this._markers[this._nextIdx].startLineNumber, this._markers[this._nextIdx].startColumn); + this._editor.setPosition(pos); + this._editor.revealPositionInCenter(pos, editorCommon.ScrollType.Smooth); + }); + } + + public dispose(): void { + this._toUnbind = dispose(this._toUnbind); + } +} + +class MessageWidget { + + domNode: HTMLDivElement; + lines: number = 0; + + constructor(container: HTMLElement) { + this.domNode = document.createElement('div'); + this.domNode.className = 'block descriptioncontainer'; + this.domNode.setAttribute('aria-live', 'assertive'); + this.domNode.setAttribute('role', 'alert'); + container.appendChild(this.domNode); + } + + update({ source, message }: IMarker): void { + this.lines = 1; + if (source) { + const indent = new Array(source.length + 3 + 1).join(' '); + message = `[${source}] ` + message.replace(/\r\n|\r|\n/g, () => { + this.lines += 1; + return '\n' + indent; + }); + } + this.domNode.innerText = message; + } +} + +class MarkerNavigationWidget extends ZoneWidget { + + private _parentContainer: HTMLElement; + private _container: HTMLElement; + private _title: HTMLElement; + private _message: MessageWidget; + private _callOnDispose: IDisposable[] = []; + private _severity: Severity; + private _backgroundColor: Color; + + constructor(editor: ICodeEditor, private _model: MarkerModel, private _themeService: IThemeService) { + super(editor, { showArrow: true, showFrame: true, isAccessible: true }); + this._severity = Severity.Warning; + this._backgroundColor = Color.white; + + this._applyTheme(_themeService.getTheme()); + this._callOnDispose.push(_themeService.onThemeChange(this._applyTheme.bind(this))); + + this.create(); + this._wireModelAndView(); + } + + private _applyTheme(theme: ITheme) { + this._backgroundColor = theme.getColor(editorMarkerNavigationBackground); + let frameColor = theme.getColor(this._severity === Severity.Error ? editorMarkerNavigationError : editorMarkerNavigationWarning); + this.style({ + arrowColor: frameColor, + frameColor: frameColor + }); // style() will trigger _applyStyles + } + + protected _applyStyles(): void { + if (this._parentContainer) { + this._parentContainer.style.backgroundColor = this._backgroundColor.toString(); + } + super._applyStyles(); + } + + dispose(): void { + this._callOnDispose = dispose(this._callOnDispose); + super.dispose(); + } + + focus(): void { + this._parentContainer.focus(); + } + + protected _fillContainer(container: HTMLElement): void { + this._parentContainer = container; + dom.addClass(container, 'marker-widget'); + this._parentContainer.tabIndex = 0; + this._parentContainer.setAttribute('role', 'tooltip'); + + this._container = document.createElement('div'); + container.appendChild(this._container); + + this._title = document.createElement('div'); + this._title.className = 'block title'; + this._container.appendChild(this._title); + + this._message = new MessageWidget(this._container); + this.editor.applyFontInfo(this._message.domNode); + } + + public show(where: Position, heightInLines: number): void { + super.show(where, heightInLines); + if (this.editor.getConfiguration().accessibilitySupport !== AccessibilitySupport.Disabled) { + this.focus(); + } + } + + private _wireModelAndView(): void { + // listen to events + this._model.onCurrentMarkerChanged(this.showAtMarker, this, this._callOnDispose); + this._model.onMarkerSetChanged(this._onMarkersChanged, this, this._callOnDispose); + } + + public showAtMarker(marker: IMarker): void { + + if (!marker) { + return; + } + + // update: + // * title + // * message + this._container.classList.remove('stale'); + this._title.innerHTML = nls.localize('title.wo_source', "({0}/{1})", this._model.indexOf(marker), this._model.total); + this._message.update(marker); + + this._model.withoutWatchingEditorPosition(() => { + // update frame color (only applied on 'show') + this._severity = marker.severity; + this._applyTheme(this._themeService.getTheme()); + + this.show(new Position(marker.startLineNumber, marker.startColumn), this.computeRequiredHeight()); + }); + } + + private _onMarkersChanged(): void { + const marker = this._model.findMarkerAtPosition(this.position); + if (marker) { + this._container.classList.remove('stale'); + this._message.update(marker); + } else { + this._container.classList.add('stale'); + } + this._relayout(); + } + + protected _relayout(): void { + super._relayout(this.computeRequiredHeight()); + } + + private computeRequiredHeight() { + return 1 + this._message.lines; + } +} + +class MarkerNavigationAction extends EditorAction { + + private _isNext: boolean; + + constructor(next: boolean, opts: IActionOptions) { + super(opts); + this._isNext = next; + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + const telemetryService = accessor.get(ITelemetryService); + + const controller = MarkerController.get(editor); + if (!controller) { + return; + } + + let model = controller.getOrCreateModel(); + telemetryService.publicLog('zoneWidgetShown', { mode: 'go to error', ...editor.getTelemetryData() }); + if (model) { + if (this._isNext) { + model.next(); + } else { + model.previous(); + } + model.reveal(); + } + } +} + +@editorContribution +class MarkerController implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.markerController'; + + public static get(editor: editorCommon.ICommonCodeEditor): MarkerController { + return editor.getContribution(MarkerController.ID); + } + + private _editor: ICodeEditor; + private _model: MarkerModel; + private _zone: MarkerNavigationWidget; + private _callOnClose: IDisposable[] = []; + private _markersNavigationVisible: IContextKey; + + constructor( + editor: ICodeEditor, + @IMarkerService private _markerService: IMarkerService, + @IContextKeyService private _contextKeyService: IContextKeyService, + @IThemeService private _themeService: IThemeService + ) { + this._editor = editor; + this._markersNavigationVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService); + } + + public getId(): string { + return MarkerController.ID; + } + + public dispose(): void { + this._cleanUp(); + } + + private _cleanUp(): void { + this._markersNavigationVisible.reset(); + this._callOnClose = dispose(this._callOnClose); + this._zone = null; + this._model = null; + } + + public getOrCreateModel(): MarkerModel { + + if (this._model) { + return this._model; + } + + const markers = this._getMarkers(); + this._model = new MarkerModel(this._editor, markers); + this._zone = new MarkerNavigationWidget(this._editor, this._model, this._themeService); + this._markersNavigationVisible.set(true); + + this._callOnClose.push(this._model); + this._callOnClose.push(this._zone); + + this._callOnClose.push(this._editor.onDidChangeModel(() => this._cleanUp())); + this._model.onCurrentMarkerChanged(marker => !marker && this._cleanUp(), undefined, this._callOnClose); + this._markerService.onMarkerChanged(this._onMarkerChanged, this, this._callOnClose); + return this._model; + } + + public closeMarkersNavigation(): void { + this._cleanUp(); + this._editor.focus(); + } + + private _onMarkerChanged(changedResources: URI[]): void { + if (!changedResources.some(r => this._editor.getModel().uri.toString() === r.toString())) { + return; + } + this._model.setMarkers(this._getMarkers()); + } + + private _getMarkers(): IMarker[] { + return this._markerService.read({ resource: this._editor.getModel().uri }); + } +} + +@editorAction +class NextMarkerAction extends MarkerNavigationAction { + constructor() { + super(true, { + id: 'editor.action.marker.next', + label: nls.localize('markerAction.next.label', "Go to Next Error or Warning"), + alias: 'Go to Next Error or Warning', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyCode.F8 + } + }); + } +} + +@editorAction +class PrevMarkerAction extends MarkerNavigationAction { + constructor() { + super(false, { + id: 'editor.action.marker.prev', + label: nls.localize('markerAction.previous.label', "Go to Previous Error or Warning"), + alias: 'Go to Previous Error or Warning', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.Shift | KeyCode.F8 + } + }); + } +} + +const CONTEXT_MARKERS_NAVIGATION_VISIBLE = new RawContextKey('markersNavigationVisible', false); + +const MarkerCommand = EditorCommand.bindToContribution(MarkerController.get); + +CommonEditorRegistry.registerEditorCommand(new MarkerCommand({ + id: 'closeMarkersNavigation', + precondition: CONTEXT_MARKERS_NAVIGATION_VISIBLE, + handler: x => x.closeMarkersNavigation(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(50), + kbExpr: EditorContextKeys.focus, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } +})); + +// theming + +let errorDefault = oneOf(editorErrorForeground, editorErrorBorder); +let warningDefault = oneOf(editorWarningForeground, editorWarningBorder); + +export const editorMarkerNavigationError = registerColor('editorMarkerNavigationError.background', { dark: errorDefault, light: errorDefault, hc: errorDefault }, nls.localize('editorMarkerNavigationError', 'Editor marker navigation widget error color.')); +export const editorMarkerNavigationWarning = registerColor('editorMarkerNavigationWarning.background', { dark: warningDefault, light: warningDefault, hc: warningDefault }, nls.localize('editorMarkerNavigationWarning', 'Editor marker navigation widget warning color.')); +export const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: '#2D2D30', light: Color.white, hc: '#0C141F' }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.')); diff --git a/src/vs/editor/contrib/hover/browser/hover.css b/src/vs/editor/contrib/hover/browser/hover.css new file mode 100644 index 0000000000..a380ddbced --- /dev/null +++ b/src/vs/editor/contrib/hover/browser/hover.css @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor-hover { + cursor: default; + position: absolute; + overflow: hidden; + z-index: 50; + -webkit-user-select: text; + -ms-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -o-user-select: text; + user-select: text; + box-sizing: initial; + animation: fadein 100ms linear; + line-height: 1.5em; +} + +.monaco-editor-hover.hidden { + display: none; +} + +.monaco-editor-hover .monaco-editor-hover-content { + max-width: 500px; +} + +/* + * https://github.com/Microsoft/monaco-editor/issues/417 + * Safari 10.1, fails inherit correct visibility from parent when we change the visibility of parent element from hidden to inherit, in this particular case. + */ +.monaco-editor-hover .monaco-scrollable-element { + visibility: visible; +} + +.monaco-editor-hover .hover-row { + padding: 4px 5px; +} + +.monaco-editor-hover p, +.monaco-editor-hover ul { + margin: 8px 0; +} + +.monaco-editor-hover p:first-child, +.monaco-editor-hover ul:first-child { + margin-top: 0; +} + +.monaco-editor-hover p:last-child, +.monaco-editor-hover ul:last-child { + margin-bottom: 0; +} + +.monaco-editor-hover ul { + padding-left: 20px; +} + +.monaco-editor-hover li > p { + margin-bottom: 0; +} + +.monaco-editor-hover li > ul { + margin-top: 0; +} + +.monaco-editor-hover code { + border-radius: 3px; + padding: 0 0.4em; +} + +.monaco-editor-hover .monaco-tokenized-source { + white-space: pre-wrap; + word-break: break-all; +} \ No newline at end of file diff --git a/src/vs/editor/contrib/hover/browser/hover.ts b/src/vs/editor/contrib/hover/browser/hover.ts new file mode 100644 index 0000000000..04f5741eec --- /dev/null +++ b/src/vs/editor/contrib/hover/browser/hover.ts @@ -0,0 +1,222 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./hover'; +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import * as platform from 'vs/base/common/platform'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { ModesContentHoverWidget } from './modesContentHover'; +import { ModesGlyphHoverWidget } from './modesGlyphHover'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorHoverHighlight, editorHoverBackground, editorHoverBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; + +@editorContribution +export class ModesHoverController implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.hover'; + + private _editor: ICodeEditor; + private _toUnhook: IDisposable[]; + + private _contentWidget: ModesContentHoverWidget; + private _glyphWidget: ModesGlyphHoverWidget; + + private _isMouseDown: boolean; + private _hoverClicked: boolean; + + static get(editor: editorCommon.ICommonCodeEditor): ModesHoverController { + return editor.getContribution(ModesHoverController.ID); + } + + constructor(editor: ICodeEditor, + @IOpenerService openerService: IOpenerService, + @IModeService modeService: IModeService + ) { + this._editor = editor; + + this._toUnhook = []; + this._isMouseDown = false; + + if (editor.getConfiguration().contribInfo.hover) { + this._toUnhook.push(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); + this._toUnhook.push(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); + this._toUnhook.push(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(e))); + this._toUnhook.push(this._editor.onMouseLeave((e: IEditorMouseEvent) => this._hideWidgets())); + this._toUnhook.push(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e))); + this._toUnhook.push(this._editor.onDidChangeModel(() => this._hideWidgets())); + this._toUnhook.push(this._editor.onDidChangeModelDecorations(() => this._onModelDecorationsChanged())); + this._toUnhook.push(this._editor.onDidScrollChange((e) => { + if (e.scrollTopChanged || e.scrollLeftChanged) { + this._hideWidgets(); + } + })); + + this._contentWidget = new ModesContentHoverWidget(editor, openerService, modeService); + this._glyphWidget = new ModesGlyphHoverWidget(editor, openerService, modeService); + } + } + + private _onModelDecorationsChanged(): void { + this._contentWidget.onModelDecorationsChanged(); + this._glyphWidget.onModelDecorationsChanged(); + } + + private _onEditorMouseDown(mouseEvent: IEditorMouseEvent): void { + this._isMouseDown = true; + + var targetType = mouseEvent.target.type; + + if (targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === ModesContentHoverWidget.ID) { + this._hoverClicked = true; + // mouse down on top of content hover widget + return; + } + + if (targetType === MouseTargetType.OVERLAY_WIDGET && mouseEvent.target.detail === ModesGlyphHoverWidget.ID) { + // mouse down on top of overlay hover widget + return; + } + + if (targetType !== MouseTargetType.OVERLAY_WIDGET && mouseEvent.target.detail !== ModesGlyphHoverWidget.ID) { + this._hoverClicked = false; + } + + this._hideWidgets(); + } + + private _onEditorMouseUp(mouseEvent: IEditorMouseEvent): void { + this._isMouseDown = false; + } + + private _onEditorMouseMove(mouseEvent: IEditorMouseEvent): void { + var targetType = mouseEvent.target.type; + var stopKey = platform.isMacintosh ? 'metaKey' : 'ctrlKey'; + + if (this._isMouseDown && this._hoverClicked && this._contentWidget.isColorPickerVisible()) { + return; + } + + if (targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === ModesContentHoverWidget.ID && !mouseEvent.event[stopKey]) { + // mouse moved on top of content hover widget + return; + } + + if (targetType === MouseTargetType.OVERLAY_WIDGET && mouseEvent.target.detail === ModesGlyphHoverWidget.ID && !mouseEvent.event[stopKey]) { + // mouse moved on top of overlay hover widget + return; + } + + if (this._editor.getConfiguration().contribInfo.hover && targetType === MouseTargetType.CONTENT_TEXT) { + this._glyphWidget.hide(); + this._contentWidget.startShowingAt(mouseEvent.target.range, false); + } else if (targetType === MouseTargetType.GUTTER_GLYPH_MARGIN) { + this._contentWidget.hide(); + this._glyphWidget.startShowingAt(mouseEvent.target.position.lineNumber); + } else { + this._hideWidgets(); + } + } + + private _onKeyDown(e: IKeyboardEvent): void { + if (e.keyCode !== KeyCode.Ctrl && e.keyCode !== KeyCode.Alt && e.keyCode !== KeyCode.Meta) { + // Do not hide hover when Ctrl/Meta is pressed + this._hideWidgets(); + } + } + + private _hideWidgets(): void { + if (this._isMouseDown && this._hoverClicked && this._contentWidget.isColorPickerVisible()) { + return; + } + + this._glyphWidget.hide(); + this._contentWidget.hide(); + } + + public showContentHover(range: Range, focus: boolean): void { + this._contentWidget.startShowingAt(range, focus); + } + + public getId(): string { + return ModesHoverController.ID; + } + + public dispose(): void { + this._toUnhook = dispose(this._toUnhook); + if (this._glyphWidget) { + this._glyphWidget.dispose(); + this._glyphWidget = null; + } + if (this._contentWidget) { + this._contentWidget.dispose(); + this._contentWidget = null; + } + } +} + +@editorAction +class ShowHoverAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.showHover', + label: nls.localize('showHover', "Show Hover"), + alias: 'Show Hover', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_I) + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let controller = ModesHoverController.get(editor); + if (!controller) { + return; + } + const position = editor.getPosition(); + const range = new Range(position.lineNumber, position.column, position.lineNumber, position.column); + controller.showContentHover(range, true); + } +} + +// theming +registerThemingParticipant((theme, collector) => { + let editorHoverHighlightColor = theme.getColor(editorHoverHighlight); + if (editorHoverHighlightColor) { + collector.addRule(`.monaco-editor .hoverHighlight { background-color: ${editorHoverHighlightColor}; }`); + } + let hoverBackground = theme.getColor(editorHoverBackground); + if (hoverBackground) { + collector.addRule(`.monaco-editor .monaco-editor-hover { background-color: ${hoverBackground}; }`); + } + let hoverBorder = theme.getColor(editorHoverBorder); + if (hoverBorder) { + collector.addRule(`.monaco-editor .monaco-editor-hover { border: 1px solid ${hoverBorder}; }`); + collector.addRule(`.monaco-editor .monaco-editor-hover .hover-row:not(:first-child):not(:empty) { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`); + } + let link = theme.getColor(textLinkForeground); + if (link) { + collector.addRule(`.monaco-editor .monaco-editor-hover a { color: ${link}; }`); + } + let codeBackground = theme.getColor(textCodeBlockBackground); + if (codeBackground) { + collector.addRule(`.monaco-editor .monaco-editor-hover code { background-color: ${codeBackground}; }`); + } + +}); diff --git a/src/vs/editor/contrib/hover/browser/hoverOperation.ts b/src/vs/editor/contrib/hover/browser/hoverOperation.ts new file mode 100644 index 0000000000..f3ce6e1780 --- /dev/null +++ b/src/vs/editor/contrib/hover/browser/hoverOperation.ts @@ -0,0 +1,189 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { RunOnceScheduler } from 'vs/base/common/async'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; + +export interface IHoverComputer { + + /** + * Overwrite the default hover time + */ + getHoverTimeMillis?: () => number; + + /** + * This is called after half the hover time + */ + computeAsync?: () => TPromise; + + /** + * This is called after all the hover time + */ + computeSync?: () => Result; + + /** + * This is called whenever one of the compute* methods returns a truey value + */ + onResult: (result: Result, isFromSynchronousComputation: boolean) => void; + + /** + * This is what will be sent as progress/complete to the computation promise + */ + getResult: () => Result; + + getResultWithLoadingMessage: () => Result; + +} + +const enum ComputeHoverOperationState { + IDLE = 0, + FIRST_WAIT = 1, + SECOND_WAIT = 2, + WAITING_FOR_ASYNC_COMPUTATION = 3 +} + +export class HoverOperation { + + static HOVER_TIME = 300; + + private _computer: IHoverComputer; + private _state: ComputeHoverOperationState; + + private _firstWaitScheduler: RunOnceScheduler; + private _secondWaitScheduler: RunOnceScheduler; + private _loadingMessageScheduler: RunOnceScheduler; + private _asyncComputationPromise: TPromise; + private _asyncComputationPromiseDone: boolean; + + private _completeCallback: (r: Result) => void; + private _errorCallback: (err: any) => void; + private _progressCallback: (progress: any) => void; + + constructor(computer: IHoverComputer, success: (r: Result) => void, error: (err: any) => void, progress: (progress: any) => void) { + this._computer = computer; + this._state = ComputeHoverOperationState.IDLE; + + this._firstWaitScheduler = new RunOnceScheduler(() => this._triggerAsyncComputation(), this._getHoverTimeMillis() / 2); + this._secondWaitScheduler = new RunOnceScheduler(() => this._triggerSyncComputation(), this._getHoverTimeMillis() / 2); + this._loadingMessageScheduler = new RunOnceScheduler(() => this._showLoadingMessage(), 3 * this._getHoverTimeMillis()); + + this._asyncComputationPromise = null; + this._asyncComputationPromiseDone = false; + + this._completeCallback = success; + this._errorCallback = error; + this._progressCallback = progress; + } + + public getComputer(): IHoverComputer { + return this._computer; + } + + private _getHoverTimeMillis(): number { + if (this._computer.getHoverTimeMillis) { + return this._computer.getHoverTimeMillis(); + } + return HoverOperation.HOVER_TIME; + } + + private _triggerAsyncComputation(): void { + this._state = ComputeHoverOperationState.SECOND_WAIT; + this._secondWaitScheduler.schedule(); + + if (this._computer.computeAsync) { + this._asyncComputationPromiseDone = false; + this._asyncComputationPromise = this._computer.computeAsync().then((asyncResult: Result) => { + this._asyncComputationPromiseDone = true; + this._withAsyncResult(asyncResult); + }, (e) => this._onError(e)); + } else { + this._asyncComputationPromiseDone = true; + } + } + + private _triggerSyncComputation(): void { + if (this._computer.computeSync) { + this._computer.onResult(this._computer.computeSync(), true); + } + + if (this._asyncComputationPromiseDone) { + this._state = ComputeHoverOperationState.IDLE; + this._onComplete(this._computer.getResult()); + } else { + this._state = ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION; + this._onProgress(this._computer.getResult()); + } + } + + private _showLoadingMessage(): void { + if (this._state === ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION) { + this._onProgress(this._computer.getResultWithLoadingMessage()); + } + } + + private _withAsyncResult(asyncResult: Result): void { + if (asyncResult) { + this._computer.onResult(asyncResult, false); + } + + if (this._state === ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION) { + this._state = ComputeHoverOperationState.IDLE; + this._onComplete(this._computer.getResult()); + } + } + + private _onComplete(value: Result): void { + if (this._completeCallback) { + this._completeCallback(value); + } + } + + private _onError(error: any): void { + if (this._errorCallback) { + this._errorCallback(error); + } else { + onUnexpectedError(error); + } + } + + private _onProgress(value: Result): void { + if (this._progressCallback) { + this._progressCallback(value); + } + } + + public start(): void { + if (this._state === ComputeHoverOperationState.IDLE) { + this._state = ComputeHoverOperationState.FIRST_WAIT; + this._firstWaitScheduler.schedule(); + this._loadingMessageScheduler.schedule(); + } + } + + public cancel(): void { + this._loadingMessageScheduler.cancel(); + if (this._state === ComputeHoverOperationState.FIRST_WAIT) { + this._firstWaitScheduler.cancel(); + } + if (this._state === ComputeHoverOperationState.SECOND_WAIT) { + this._secondWaitScheduler.cancel(); + if (this._asyncComputationPromise) { + this._asyncComputationPromise.cancel(); + this._asyncComputationPromise = null; + } + } + if (this._state === ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION) { + if (this._asyncComputationPromise) { + this._asyncComputationPromise.cancel(); + this._asyncComputationPromise = null; + } + } + this._state = ComputeHoverOperationState.IDLE; + } + +} + diff --git a/src/vs/editor/contrib/hover/browser/hoverWidgets.ts b/src/vs/editor/contrib/hover/browser/hoverWidgets.ts new file mode 100644 index 0000000000..d3fd09e74a --- /dev/null +++ b/src/vs/editor/contrib/hover/browser/hoverWidgets.ts @@ -0,0 +1,251 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { toggleClass } from 'vs/base/browser/dom'; +import { Position } from 'vs/editor/common/core/position'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; + +export class ContentHoverWidget extends Widget implements editorBrowser.IContentWidget { + + private _id: string; + protected _editor: editorBrowser.ICodeEditor; + private _isVisible: boolean; + private _containerDomNode: HTMLElement; + private _domNode: HTMLElement; + protected _showAtPosition: Position; + private _stoleFocus: boolean; + private scrollbar: DomScrollableElement; + private disposables: IDisposable[] = []; + + // Editor.IContentWidget.allowEditorOverflow + public allowEditorOverflow = true; + + protected get isVisible(): boolean { + return this._isVisible; + } + + protected set isVisible(value: boolean) { + this._isVisible = value; + toggleClass(this._containerDomNode, 'hidden', !this._isVisible); + } + + constructor(id: string, editor: editorBrowser.ICodeEditor) { + super(); + this._id = id; + this._editor = editor; + this._isVisible = false; + + this._containerDomNode = document.createElement('div'); + this._containerDomNode.className = 'monaco-editor-hover hidden'; + this._containerDomNode.tabIndex = 0; + + this._domNode = document.createElement('div'); + this._domNode.className = 'monaco-editor-hover-content'; + + this.scrollbar = new DomScrollableElement(this._domNode, {}); + this.disposables.push(this.scrollbar); + this._containerDomNode.appendChild(this.scrollbar.getDomNode()); + + this.onkeydown(this._containerDomNode, (e: IKeyboardEvent) => { + if (e.equals(KeyCode.Escape)) { + this.hide(); + } + }); + + this._register(this._editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { + if (e.fontInfo) { + this.updateFont(); + } + })); + + this._editor.onDidLayoutChange(e => this.updateMaxHeight()); + + this.updateMaxHeight(); + this._editor.addContentWidget(this); + this._showAtPosition = null; + } + + public getId(): string { + return this._id; + } + + public getDomNode(): HTMLElement { + return this._containerDomNode; + } + + public showAt(position: Position, focus: boolean): void { + // Position has changed + this._showAtPosition = new Position(position.lineNumber, position.column); + this.isVisible = true; + + this._editor.layoutContentWidget(this); + // Simply force a synchronous render on the editor + // such that the widget does not really render with left = '0px' + this._editor.render(); + this._stoleFocus = focus; + if (focus) { + this._containerDomNode.focus(); + } + } + + public hide(): void { + if (!this.isVisible) { + return; + } + + this.isVisible = false; + + this._editor.layoutContentWidget(this); + if (this._stoleFocus) { + this._editor.focus(); + } + } + + public getPosition(): editorBrowser.IContentWidgetPosition { + if (this.isVisible) { + return { + position: this._showAtPosition, + preference: [ + editorBrowser.ContentWidgetPositionPreference.ABOVE, + editorBrowser.ContentWidgetPositionPreference.BELOW + ] + }; + } + return null; + } + + public dispose(): void { + this._editor.removeContentWidget(this); + this.disposables = dispose(this.disposables); + super.dispose(); + } + + private updateFont(): void { + const codeTags: HTMLElement[] = Array.prototype.slice.call(this._domNode.getElementsByTagName('code')); + const codeClasses: HTMLElement[] = Array.prototype.slice.call(this._domNode.getElementsByClassName('code')); + + [...codeTags, ...codeClasses].forEach(node => this._editor.applyFontInfo(node)); + } + + protected updateContents(node: Node): void { + this._domNode.textContent = ''; + this._domNode.appendChild(node); + this.updateFont(); + + this._editor.layoutContentWidget(this); + this.scrollbar.scanDomNode(); + } + + private updateMaxHeight(): void { + const height = Math.max(this._editor.getLayoutInfo().height / 4, 250); + const { fontSize, lineHeight } = this._editor.getConfiguration().fontInfo; + + this._domNode.style.fontSize = `${fontSize}px`; + this._domNode.style.lineHeight = `${lineHeight}px`; + this._domNode.style.maxHeight = `${height}px`; + } +} + +export class GlyphHoverWidget extends Widget implements editorBrowser.IOverlayWidget { + + private _id: string; + protected _editor: editorBrowser.ICodeEditor; + private _isVisible: boolean; + private _domNode: HTMLElement; + protected _showAtLineNumber: number; + + constructor(id: string, editor: editorBrowser.ICodeEditor) { + super(); + this._id = id; + this._editor = editor; + this._isVisible = false; + + this._domNode = document.createElement('div'); + this._domNode.className = 'monaco-editor-hover hidden'; + this._domNode.setAttribute('aria-hidden', 'true'); + this._domNode.setAttribute('role', 'presentation'); + + this._showAtLineNumber = -1; + + this._register(this._editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { + if (e.fontInfo) { + this.updateFont(); + } + })); + + this._editor.addOverlayWidget(this); + } + + protected get isVisible(): boolean { + return this._isVisible; + } + + protected set isVisible(value: boolean) { + this._isVisible = value; + toggleClass(this._domNode, 'hidden', !this._isVisible); + } + + public getId(): string { + return this._id; + } + + public getDomNode(): HTMLElement { + return this._domNode; + } + + public showAt(lineNumber: number): void { + this._showAtLineNumber = lineNumber; + + if (!this.isVisible) { + this.isVisible = true; + } + + const editorLayout = this._editor.getLayoutInfo(); + const topForLineNumber = this._editor.getTopForLineNumber(this._showAtLineNumber); + const editorScrollTop = this._editor.getScrollTop(); + const lineHeight = this._editor.getConfiguration().lineHeight; + const nodeHeight = this._domNode.clientHeight; + const top = topForLineNumber - editorScrollTop - ((nodeHeight - lineHeight) / 2); + + this._domNode.style.left = `${editorLayout.glyphMarginLeft + editorLayout.glyphMarginWidth}px`; + this._domNode.style.top = `${Math.max(Math.round(top), 0)}px`; + } + + public hide(): void { + if (!this.isVisible) { + return; + } + this.isVisible = false; + } + + public getPosition(): editorBrowser.IOverlayWidgetPosition { + return null; + } + + public dispose(): void { + this._editor.removeOverlayWidget(this); + super.dispose(); + } + + private updateFont(): void { + const codeTags: HTMLElement[] = Array.prototype.slice.call(this._domNode.getElementsByTagName('code')); + const codeClasses: HTMLElement[] = Array.prototype.slice.call(this._domNode.getElementsByClassName('code')); + + [...codeTags, ...codeClasses].forEach(node => this._editor.applyFontInfo(node)); + } + + protected updateContents(node: Node): void { + this._domNode.textContent = ''; + this._domNode.appendChild(node); + this.updateFont(); + } +} diff --git a/src/vs/editor/contrib/hover/browser/modesContentHover.ts b/src/vs/editor/contrib/hover/browser/modesContentHover.ts new file mode 100644 index 0000000000..238a209278 --- /dev/null +++ b/src/vs/editor/contrib/hover/browser/modesContentHover.ts @@ -0,0 +1,397 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import URI from 'vs/base/common/uri'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import * as dom from 'vs/base/browser/dom'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { HoverProviderRegistry, Hover, IColor, IColorFormatter } from 'vs/editor/common/modes'; +import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { getHover } from '../common/hover'; +import { HoverOperation, IHoverComputer } from './hoverOperation'; +import { ContentHoverWidget } from './hoverWidgets'; +import { IMarkdownString, MarkdownString, isEmptyMarkdownString } from 'vs/base/common/htmlContent'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/browser/colorPickerModel'; +import { ColorPickerWidget } from 'vs/editor/contrib/colorPicker/browser/colorPickerWidget'; +import { ColorDetector } from 'vs/editor/contrib/colorPicker/browser/colorDetector'; +import { Color, RGBA } from 'vs/base/common/color'; +import { IDisposable, empty as EmptyDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; +const $ = dom.$; + +class ColorHover { + + constructor( + public readonly range: IRange, + public readonly color: IColor, + public readonly formatters: IColorFormatter[] + ) { } +} + +type HoverPart = Hover | ColorHover; + +class ModesContentComputer implements IHoverComputer { + + private _editor: ICodeEditor; + private _result: HoverPart[]; + private _range: Range; + + constructor(editor: ICodeEditor) { + this._editor = editor; + this._range = null; + } + + setRange(range: Range): void { + this._range = range; + this._result = []; + } + + clearResult(): void { + this._result = []; + } + + computeAsync(): TPromise { + const model = this._editor.getModel(); + + if (!HoverProviderRegistry.has(model)) { + return TPromise.as(null); + } + + return getHover(model, new Position( + this._range.startLineNumber, + this._range.startColumn + )); + } + + computeSync(): HoverPart[] { + const lineNumber = this._range.startLineNumber; + + if (lineNumber > this._editor.getModel().getLineCount()) { + // Illegal line number => no results + return []; + } + + const colorDetector = ColorDetector.get(this._editor); + const maxColumn = this._editor.getModel().getLineMaxColumn(lineNumber); + const lineDecorations = this._editor.getLineDecorations(lineNumber); + let didFindColor = false; + + const result = lineDecorations.map(d => { + const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1; + const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn; + + if (startColumn > this._range.startColumn || this._range.endColumn > endColumn) { + return null; + } + + const range = new Range(this._range.startLineNumber, startColumn, this._range.startLineNumber, endColumn); + const colorRange = colorDetector.getColorRange(d.range.getStartPosition()); + + if (!didFindColor && colorRange) { + didFindColor = true; + + const { color, formatters } = colorRange; + return new ColorHover(d.range, color, formatters); + } else { + if (isEmptyMarkdownString(d.options.hoverMessage)) { + return null; + } + + let contents: IMarkdownString[]; + + if (d.options.hoverMessage) { + if (Array.isArray(d.options.hoverMessage)) { + contents = [...d.options.hoverMessage]; + } else { + contents = [d.options.hoverMessage]; + } + } + + return { contents, range }; + } + }); + + return result.filter(d => !!d); + } + + onResult(result: HoverPart[], isFromSynchronousComputation: boolean): void { + // Always put synchronous messages before asynchronous ones + if (isFromSynchronousComputation) { + this._result = result.concat(this._result.sort((a, b) => { + if (a instanceof ColorHover) { // sort picker messages at to the top + return -1; + } else if (b instanceof ColorHover) { + return 1; + } + return 0; + })); + } else { + this._result = this._result.concat(result); + } + } + + getResult(): HoverPart[] { + return this._result.slice(0); + } + + getResultWithLoadingMessage(): HoverPart[] { + return this._result.slice(0).concat([this._getLoadingMessage()]); + } + + private _getLoadingMessage(): HoverPart { + return { + range: this._range, + contents: [new MarkdownString().appendText(nls.localize('modesContentHover.loading', "Loading..."))] + }; + } +} + +export class ModesContentHoverWidget extends ContentHoverWidget { + + static ID = 'editor.contrib.modesContentHoverWidget'; + + private _messages: HoverPart[]; + private _lastRange: Range; + private _computer: ModesContentComputer; + private _hoverOperation: HoverOperation; + private _highlightDecorations: string[]; + private _isChangingDecorations: boolean; + private _openerService: IOpenerService; + private _modeService: IModeService; + private _shouldFocus: boolean; + private _colorPicker: ColorPickerWidget; + + private renderDisposable: IDisposable = EmptyDisposable; + private toDispose: IDisposable[]; + + constructor(editor: ICodeEditor, openerService: IOpenerService, modeService: IModeService) { + super(ModesContentHoverWidget.ID, editor); + + this._computer = new ModesContentComputer(this._editor); + this._highlightDecorations = []; + this._isChangingDecorations = false; + this._openerService = openerService || NullOpenerService; + this._modeService = modeService; + + this._hoverOperation = new HoverOperation( + this._computer, + result => this._withResult(result, true), + null, + result => this._withResult(result, false) + ); + + this.toDispose = []; + this.toDispose.push(dom.addStandardDisposableListener(this.getDomNode(), dom.EventType.FOCUS, () => { + if (this._colorPicker) { + dom.addClass(this.getDomNode(), 'colorpicker-hover'); + } + })); + this.toDispose.push(dom.addStandardDisposableListener(this.getDomNode(), dom.EventType.BLUR, () => { + dom.removeClass(this.getDomNode(), 'colorpicker-hover'); + })); + } + + dispose(): void { + this.renderDisposable.dispose(); + this.renderDisposable = EmptyDisposable; + this._hoverOperation.cancel(); + this.toDispose = dispose(this.toDispose); + super.dispose(); + } + + onModelDecorationsChanged(): void { + if (this._isChangingDecorations) { + return; + } + if (this.isVisible) { + // The decorations have changed and the hover is visible, + // we need to recompute the displayed text + this._hoverOperation.cancel(); + this._computer.clearResult(); + + if (!this._colorPicker) { // TODO@Michel ensure that displayed text for other decorations is computed even if color picker is in place + this._hoverOperation.start(); + } + } + } + + startShowingAt(range: Range, focus: boolean): void { + if (this._lastRange && this._lastRange.equalsRange(range)) { + // We have to show the widget at the exact same range as before, so no work is needed + return; + } + + this._hoverOperation.cancel(); + + if (this.isVisible) { + // The range might have changed, but the hover is visible + // Instead of hiding it completely, filter out messages that are still in the new range and + // kick off a new computation + if (this._showAtPosition.lineNumber !== range.startLineNumber) { + this.hide(); + } else { + var filteredMessages: HoverPart[] = []; + for (var i = 0, len = this._messages.length; i < len; i++) { + var msg = this._messages[i]; + var rng = msg.range; + if (rng.startColumn <= range.startColumn && rng.endColumn >= range.endColumn) { + filteredMessages.push(msg); + } + } + if (filteredMessages.length > 0) { + this._renderMessages(range, filteredMessages); + } else { + this.hide(); + } + } + } + + this._lastRange = range; + this._computer.setRange(range); + this._shouldFocus = focus; + this._hoverOperation.start(); + } + + hide(): void { + this._lastRange = null; + this._hoverOperation.cancel(); + super.hide(); + this._isChangingDecorations = true; + this._highlightDecorations = this._editor.deltaDecorations(this._highlightDecorations, []); + this._isChangingDecorations = false; + this.renderDisposable.dispose(); + this.renderDisposable = EmptyDisposable; + this._colorPicker = null; + } + + isColorPickerVisible(): boolean { + if (this._colorPicker) { + return true; + } + return false; + } + + private _withResult(result: HoverPart[], complete: boolean): void { + this._messages = result; + + if (this._lastRange && this._messages.length > 0) { + this._renderMessages(this._lastRange, this._messages); + } else if (complete) { + this.hide(); + } + } + + private _renderMessages(renderRange: Range, messages: HoverPart[]): void { + this.renderDisposable.dispose(); + this._colorPicker = null; + + // update column from which to show + var renderColumn = Number.MAX_VALUE, + highlightRange = messages[0].range, + fragment = document.createDocumentFragment(); + + messages.forEach((msg) => { + if (!msg.range) { + return; + } + + renderColumn = Math.min(renderColumn, msg.range.startColumn); + highlightRange = Range.plusRange(highlightRange, msg.range); + + if (!(msg instanceof ColorHover)) { + msg.contents + .filter(contents => !isEmptyMarkdownString(contents)) + .forEach(contents => { + const renderedContents = renderMarkdown(contents, { + actionCallback: (content) => { + this._openerService.open(URI.parse(content)).then(void 0, onUnexpectedError); + }, + codeBlockRenderer: (languageAlias, value): string | TPromise => { + // In markdown, + // it is possible that we stumble upon language aliases (e.g.js instead of javascript) + // it is possible no alias is given in which case we fall back to the current editor lang + const modeId = languageAlias + ? this._modeService.getModeIdForLanguageName(languageAlias) + : this._editor.getModel().getLanguageIdentifier().language; + + return this._modeService.getOrCreateMode(modeId).then(_ => { + return tokenizeToString(value, modeId); + }); + } + }); + + fragment.appendChild($('div.hover-row', null, renderedContents)); + }); + } else { + const { red, green, blue, alpha } = msg.color; + const rgba = new RGBA(red * 255, green * 255, blue * 255, alpha); + const color = new Color(rgba); + + const formatters = [...msg.formatters]; + const text = this._editor.getModel().getValueInRange(msg.range); + + let formatterIndex = 0; + + for (let i = 0; i < formatters.length; i++) { + if (text === formatters[i].format(msg.color)) { + formatterIndex = i; + break; + } + } + + const model = new ColorPickerModel(color, formatters, formatterIndex); + const widget = new ColorPickerWidget(fragment, model, this._editor.getConfiguration().pixelRatio); + + const editorModel = this._editor.getModel(); + let range = new Range(msg.range.startLineNumber, msg.range.startColumn, msg.range.endLineNumber, msg.range.endColumn); + + const updateEditorModel = () => { + const text = model.formatter.format({ + red: model.color.rgba.r / 255, + green: model.color.rgba.g / 255, + blue: model.color.rgba.b / 255, + alpha: model.color.rgba.a + }); + editorModel.pushEditOperations([], [{ identifier: null, range, text, forceMoveMarkers: false }], () => []); + this._editor.pushUndoStop(); + range = range.setEndPosition(range.endLineNumber, range.startColumn + text.length); + }; + + const colorListener = model.onColorFlushed(updateEditorModel); + + this._colorPicker = widget; + this.renderDisposable = combinedDisposable([colorListener, widget]); + } + }); + + // show + this.showAt(new Position(renderRange.startLineNumber, renderColumn), this._shouldFocus); + + this.updateContents(fragment); + + if (this._colorPicker) { + this._colorPicker.layout(); + } + + this._isChangingDecorations = true; + this._highlightDecorations = this._editor.deltaDecorations(this._highlightDecorations, [{ + range: highlightRange, + options: ModesContentHoverWidget._DECORATION_OPTIONS + }]); + this._isChangingDecorations = false; + } + + private static _DECORATION_OPTIONS = ModelDecorationOptions.register({ + className: 'hoverHighlight' + }); +} diff --git a/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts b/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts new file mode 100644 index 0000000000..41ad4ed5ca --- /dev/null +++ b/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts @@ -0,0 +1,186 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { HoverOperation, IHoverComputer } from './hoverOperation'; +import { GlyphHoverWidget } from './hoverWidgets'; +import { $ } from 'vs/base/browser/dom'; +import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; +import URI from 'vs/base/common/uri'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; +import { IMarkdownString, isEmptyMarkdownString } from 'vs/base/common/htmlContent'; + +export interface IHoverMessage { + value: IMarkdownString; +} + +class MarginComputer implements IHoverComputer { + + private _editor: ICodeEditor; + private _lineNumber: number; + private _result: IHoverMessage[]; + + constructor(editor: ICodeEditor) { + this._editor = editor; + this._lineNumber = -1; + } + + public setLineNumber(lineNumber: number): void { + this._lineNumber = lineNumber; + this._result = []; + } + + public clearResult(): void { + this._result = []; + } + + public computeSync(): IHoverMessage[] { + + const toHoverMessage = (contents: IMarkdownString): IHoverMessage => { + return { + value: contents + }; + }; + + let lineDecorations = this._editor.getLineDecorations(this._lineNumber); + + let result: IHoverMessage[] = []; + for (let i = 0, len = lineDecorations.length; i < len; i++) { + let d = lineDecorations[i]; + + if (!d.options.glyphMarginClassName) { + continue; + } + + let hoverMessage = d.options.glyphMarginHoverMessage; + + if (isEmptyMarkdownString(hoverMessage)) { + continue; + } + + if (Array.isArray(hoverMessage)) { + result = result.concat(hoverMessage.map(toHoverMessage)); + } else { + result.push(toHoverMessage(hoverMessage)); + } + } + + return result; + } + + public onResult(result: IHoverMessage[], isFromSynchronousComputation: boolean): void { + this._result = this._result.concat(result); + } + + public getResult(): IHoverMessage[] { + return this._result; + } + + public getResultWithLoadingMessage(): IHoverMessage[] { + return this.getResult(); + } +} + +export class ModesGlyphHoverWidget extends GlyphHoverWidget { + + public static ID = 'editor.contrib.modesGlyphHoverWidget'; + private _messages: IHoverMessage[]; + private _lastLineNumber: number; + + private _computer: MarginComputer; + private _hoverOperation: HoverOperation; + + constructor(editor: ICodeEditor, private openerService: IOpenerService, private modeService: IModeService) { + super(ModesGlyphHoverWidget.ID, editor); + + this.openerService = openerService || NullOpenerService; + + this._lastLineNumber = -1; + + this._computer = new MarginComputer(this._editor); + + this._hoverOperation = new HoverOperation( + this._computer, + (result: IHoverMessage[]) => this._withResult(result), + null, + (result: any) => this._withResult(result) + ); + + } + + public dispose(): void { + this._hoverOperation.cancel(); + super.dispose(); + } + + public onModelDecorationsChanged(): void { + if (this.isVisible) { + // The decorations have changed and the hover is visible, + // we need to recompute the displayed text + this._hoverOperation.cancel(); + this._computer.clearResult(); + this._hoverOperation.start(); + } + } + + public startShowingAt(lineNumber: number): void { + if (this._lastLineNumber === lineNumber) { + // We have to show the widget at the exact same line number as before, so no work is needed + return; + } + + this._hoverOperation.cancel(); + + this.hide(); + + this._lastLineNumber = lineNumber; + this._computer.setLineNumber(lineNumber); + this._hoverOperation.start(); + } + + public hide(): void { + this._lastLineNumber = -1; + this._hoverOperation.cancel(); + super.hide(); + } + + public _withResult(result: IHoverMessage[]): void { + this._messages = result; + + if (this._messages.length > 0) { + this._renderMessages(this._lastLineNumber, this._messages); + } else { + this.hide(); + } + } + + private _renderMessages(lineNumber: number, messages: IHoverMessage[]): void { + + const fragment = document.createDocumentFragment(); + + messages.forEach((msg) => { + const renderedContents = renderMarkdown(msg.value, { + actionCallback: content => this.openerService.open(URI.parse(content)).then(undefined, onUnexpectedError), + codeBlockRenderer: (languageAlias, value): string | TPromise => { + // In markdown, it is possible that we stumble upon language aliases (e.g. js instead of javascript) + const modeId = this.modeService.getModeIdForLanguageName(languageAlias); + return this.modeService.getOrCreateMode(modeId).then(_ => { + return tokenizeToString(value, modeId); + }); + } + }); + + fragment.appendChild($('div.hover-row', null, renderedContents)); + }); + + this.updateContents(fragment); + this.showAt(lineNumber); + } +} diff --git a/src/vs/editor/contrib/hover/common/hover.ts b/src/vs/editor/contrib/hover/common/hover.ts new file mode 100644 index 0000000000..140dd2d39a --- /dev/null +++ b/src/vs/editor/contrib/hover/common/hover.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { coalesce } from 'vs/base/common/arrays'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IReadOnlyModel } from 'vs/editor/common/editorCommon'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { Hover, HoverProviderRegistry } from 'vs/editor/common/modes'; +import { asWinJsPromise } from 'vs/base/common/async'; +import { Position } from 'vs/editor/common/core/position'; + +export function getHover(model: IReadOnlyModel, position: Position): TPromise { + + const supports = HoverProviderRegistry.ordered(model); + const values: Hover[] = []; + + const promises = supports.map((support, idx) => { + return asWinJsPromise((token) => { + return support.provideHover(model, position, token); + }).then((result) => { + if (result) { + let hasRange = (typeof result.range !== 'undefined'); + let hasHtmlContent = typeof result.contents !== 'undefined' && result.contents && result.contents.length > 0; + if (hasRange && hasHtmlContent) { + values[idx] = result; + } + } + }, err => { + onUnexpectedExternalError(err); + }); + }); + + return TPromise.join(promises).then(() => coalesce(values)); +} + +CommonEditorRegistry.registerDefaultLanguageCommand('_executeHoverProvider', getHover); \ No newline at end of file diff --git a/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.ts b/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.ts new file mode 100644 index 0000000000..5692310139 --- /dev/null +++ b/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { IEditorContribution, ICommonCodeEditor, IModelDecorationsChangeAccessor } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, ServicesAccessor, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions'; +import { IInplaceReplaceSupportResult } from 'vs/editor/common/modes'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { InPlaceReplaceCommand } from './inPlaceReplaceCommand'; +import { EditorState, CodeEditorStateFlag } from 'vs/editor/common/core/editorState'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorBracketMatchBorder } from 'vs/editor/common/view/editorColorRegistry'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; + +@commonEditorContribution +class InPlaceReplaceController implements IEditorContribution { + + private static ID = 'editor.contrib.inPlaceReplaceController'; + + static get(editor: ICommonCodeEditor): InPlaceReplaceController { + return editor.getContribution(InPlaceReplaceController.ID); + } + + private static DECORATION = ModelDecorationOptions.register({ + className: 'valueSetReplacement' + }); + + private editor: ICommonCodeEditor; + private requestIdPool: number; + private currentRequest: TPromise; + private decorationRemover: TPromise; + private decorationIds: string[]; + private editorWorkerService: IEditorWorkerService; + + constructor( + editor: ICommonCodeEditor, + @IEditorWorkerService editorWorkerService: IEditorWorkerService + ) { + this.editor = editor; + this.editorWorkerService = editorWorkerService; + this.requestIdPool = 0; + this.currentRequest = TPromise.as(null); + this.decorationRemover = TPromise.as(null); + this.decorationIds = []; + } + + public dispose(): void { + } + + public getId(): string { + return InPlaceReplaceController.ID; + } + + public run(source: string, up: boolean): TPromise { + + // cancel any pending request + this.currentRequest.cancel(); + + var selection = this.editor.getSelection(), + model = this.editor.getModel(), + modelURI = model.uri; + + if (selection.startLineNumber !== selection.endLineNumber) { + // Can't accept multiline selection + return null; + } + + var state = new EditorState(this.editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); + + if (!this.editorWorkerService.canNavigateValueSet(modelURI)) { + this.currentRequest = TPromise.as(null); + } else { + this.currentRequest = this.editorWorkerService.navigateValueSet(modelURI, selection, up); + this.currentRequest = this.currentRequest.then((basicResult) => { + if (basicResult && basicResult.range && basicResult.value) { + return basicResult; + } + return null; + }); + } + + return this.currentRequest.then((result: IInplaceReplaceSupportResult) => { + + if (!result || !result.range || !result.value) { + // No proper result + return; + } + + if (!state.validate(this.editor)) { + // state has changed + return; + } + + // Selection + var editRange = Range.lift(result.range), + highlightRange = result.range, + diff = result.value.length - (selection.endColumn - selection.startColumn); + + // highlight + highlightRange = { + startLineNumber: highlightRange.startLineNumber, + startColumn: highlightRange.startColumn, + endLineNumber: highlightRange.endLineNumber, + endColumn: highlightRange.startColumn + result.value.length + }; + if (diff > 1) { + selection = new Selection(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn + diff - 1); + } + + // Insert new text + var command = new InPlaceReplaceCommand(editRange, selection, result.value); + + this.editor.pushUndoStop(); + this.editor.executeCommand(source, command); + this.editor.pushUndoStop(); + + // add decoration + this.decorationIds = this.editor.deltaDecorations(this.decorationIds, [{ + range: highlightRange, + options: InPlaceReplaceController.DECORATION + }]); + + // remove decoration after delay + this.decorationRemover.cancel(); + this.decorationRemover = TPromise.timeout(350); + this.decorationRemover.then(() => { + this.editor.changeDecorations((accessor: IModelDecorationsChangeAccessor) => { + this.decorationIds = accessor.deltaDecorations(this.decorationIds, []); + }); + }); + }); + } +} + +@editorAction +class InPlaceReplaceUp extends EditorAction { + + constructor() { + super({ + id: 'editor.action.inPlaceReplace.up', + label: nls.localize('InPlaceReplaceAction.previous.label', "Replace with Previous Value"), + alias: 'Replace with Previous Value', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_COMMA + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + let controller = InPlaceReplaceController.get(editor); + if (!controller) { + return undefined; + } + return controller.run(this.id, true); + } +} + +@editorAction +class InPlaceReplaceDown extends EditorAction { + + constructor() { + super({ + id: 'editor.action.inPlaceReplace.down', + label: nls.localize('InPlaceReplaceAction.next.label', "Replace with Next Value"), + alias: 'Replace with Next Value', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + let controller = InPlaceReplaceController.get(editor); + if (!controller) { + return undefined; + } + return controller.run(this.id, false); + } +} + +registerThemingParticipant((theme, collector) => { + let border = theme.getColor(editorBracketMatchBorder); + if (border) { + collector.addRule(`.monaco-editor.vs .valueSetReplacement { outline: solid 2px ${border}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplaceCommand.ts b/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplaceCommand.ts new file mode 100644 index 0000000000..90aaa0e9c1 --- /dev/null +++ b/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplaceCommand.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Range } from 'vs/editor/common/core/range'; + +export class InPlaceReplaceCommand implements editorCommon.ICommand { + + private _editRange: Range; + private _originalSelection: Selection; + private _text: string; + + constructor(editRange: Range, originalSelection: Selection, text: string) { + this._editRange = editRange; + this._originalSelection = originalSelection; + this._text = text; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + builder.addTrackedEditOperation(this._editRange, this._text); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + var inverseEditOperations = helper.getInverseEditOperations(); + var srcRange = inverseEditOperations[0].range; + + if (!this._originalSelection.isEmpty()) { + // Preserve selection and extends to typed text + return new Selection( + srcRange.endLineNumber, + srcRange.endColumn - this._text.length, + srcRange.endLineNumber, + srcRange.endColumn + ); + } + + return new Selection( + srcRange.endLineNumber, + Math.min(this._originalSelection.positionColumn, srcRange.endColumn), + srcRange.endLineNumber, + Math.min(this._originalSelection.positionColumn, srcRange.endColumn) + ); + } +} diff --git a/src/vs/editor/contrib/indentation/common/indentUtils.ts b/src/vs/editor/contrib/indentation/common/indentUtils.ts new file mode 100644 index 0000000000..df033e93b3 --- /dev/null +++ b/src/vs/editor/contrib/indentation/common/indentUtils.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function getSpaceCnt(str, tabSize) { + let spacesCnt = 0; + + for (let i = 0; i < str.length; i++) { + if (str.charAt(i) === '\t') { + spacesCnt += tabSize; + } else { + spacesCnt++; + } + } + + return spacesCnt; +} + +export function generateIndent(spacesCnt: number, tabSize, insertSpaces) { + spacesCnt = spacesCnt < 0 ? 0 : spacesCnt; + + let result = ''; + if (!insertSpaces) { + let tabsCnt = Math.floor(spacesCnt / tabSize); + spacesCnt = spacesCnt % tabSize; + for (let i = 0; i < tabsCnt; i++) { + result += '\t'; + } + } + + for (let i = 0; i < spacesCnt; i++) { + result += ' '; + } + + return result; +} \ No newline at end of file diff --git a/src/vs/editor/contrib/indentation/common/indentation.ts b/src/vs/editor/contrib/indentation/common/indentation.ts new file mode 100644 index 0000000000..16d251b0ad --- /dev/null +++ b/src/vs/editor/contrib/indentation/common/indentation.ts @@ -0,0 +1,620 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as strings from 'vs/base/common/strings'; +import { ICommonCodeEditor, IEditorContribution, IIdentifiedSingleEditOperation, ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel, EndOfLineSequence } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, ServicesAccessor, IActionOptions, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; +import { TextEdit, StandardTokenType } from 'vs/editor/common/modes'; +import * as IndentUtil from './indentUtils'; + +export function shiftIndent(tabSize: number, indentation: string, count?: number): string { + count = count || 1; + let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + count, tabSize); + let newIndentation = ''; + for (let i = 0; i < desiredIndentCount; i++) { + newIndentation += '\t'; + } + + return newIndentation; +} + +export function unshiftIndent(tabSize: number, indentation: string, count?: number): string { + count = count || 1; + let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + count, tabSize); + let newIndentation = ''; + for (let i = 0; i < desiredIndentCount; i++) { + newIndentation += '\t'; + } + + return newIndentation; +} + +export function getReindentEditOperations(model: ITokenizedModel, startLineNumber: number, endLineNumber: number, inheritedIndent?: string): IIdentifiedSingleEditOperation[] { + if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) { + // Model is empty + return undefined; + } + + let indentationRules = LanguageConfigurationRegistry.getIndentationRules(model.getLanguageIdentifier().id); + if (!indentationRules) { + return undefined; + } + + endLineNumber = Math.min(endLineNumber, model.getLineCount()); + + // Skip `unIndentedLinePattern` lines + while (startLineNumber <= endLineNumber) { + if (!indentationRules.unIndentedLinePattern) { + break; + } + + let text = model.getLineContent(startLineNumber); + if (!indentationRules.unIndentedLinePattern.test(text)) { + break; + } + + startLineNumber++; + } + + if (startLineNumber > endLineNumber - 1) { + return undefined; + } + + let { tabSize, insertSpaces } = model.getOptions(); + let indentEdits = []; + + // indentation being passed to lines below + let globalIndent: string; + + // Calculate indentation for the first line + // If there is no passed-in indentation, we use the indentation of the first line as base. + let currentLineText = model.getLineContent(startLineNumber); + let adjustedLineContent = currentLineText; + if (inheritedIndent !== undefined && inheritedIndent !== null) { + globalIndent = inheritedIndent; + let oldIndentation = strings.getLeadingWhitespace(currentLineText); + + adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length); + if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) { + globalIndent = unshiftIndent(tabSize, globalIndent); + adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length); + + } + if (currentLineText !== adjustedLineContent) { + indentEdits.push(EditOperation.replace(new Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(globalIndent, tabSize, insertSpaces))); + } + } else { + globalIndent = strings.getLeadingWhitespace(currentLineText); + } + + // idealIndentForNextLine doesn't equal globalIndent when there is a line matching `indentNextLinePattern`. + let idealIndentForNextLine: string = globalIndent; + + if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) { + idealIndentForNextLine = shiftIndent(tabSize, idealIndentForNextLine); + globalIndent = shiftIndent(tabSize, globalIndent); + } + else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) { + idealIndentForNextLine = shiftIndent(tabSize, idealIndentForNextLine); + } + + startLineNumber++; + + // Calculate indentation adjustment for all following lines + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + let text = model.getLineContent(lineNumber); + let oldIndentation = strings.getLeadingWhitespace(text); + let adjustedLineContent = idealIndentForNextLine + text.substring(oldIndentation.length); + + if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) { + idealIndentForNextLine = unshiftIndent(tabSize, idealIndentForNextLine); + globalIndent = unshiftIndent(tabSize, globalIndent); + } + + if (oldIndentation !== idealIndentForNextLine) { + indentEdits.push(EditOperation.replace(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(idealIndentForNextLine, tabSize, insertSpaces))); + } + + // calculate idealIndentForNextLine + if (indentationRules.unIndentedLinePattern && indentationRules.unIndentedLinePattern.test(text)) { + // In reindent phase, if the line matches `unIndentedLinePattern` we inherit indentation from above lines + // but don't change globalIndent and idealIndentForNextLine. + continue; + } else if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) { + globalIndent = shiftIndent(tabSize, globalIndent); + idealIndentForNextLine = globalIndent; + } else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) { + idealIndentForNextLine = shiftIndent(tabSize, idealIndentForNextLine); + } else { + idealIndentForNextLine = globalIndent; + } + } + + return indentEdits; +} + +@editorAction +export class IndentationToSpacesAction extends EditorAction { + public static ID = 'editor.action.indentationToSpaces'; + + constructor() { + super({ + id: IndentationToSpacesAction.ID, + label: nls.localize('indentationToSpaces', "Convert Indentation to Spaces"), + alias: 'Convert Indentation to Spaces', + precondition: EditorContextKeys.writable + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let model = editor.getModel(); + if (!model) { + return; + } + let modelOpts = model.getOptions(); + const command = new IndentationToSpacesCommand(editor.getSelection(), modelOpts.tabSize); + + editor.pushUndoStop(); + editor.executeCommands(this.id, [command]); + editor.pushUndoStop(); + + model.updateOptions({ + insertSpaces: true + }); + } +} + +@editorAction +export class IndentationToTabsAction extends EditorAction { + public static ID = 'editor.action.indentationToTabs'; + + constructor() { + super({ + id: IndentationToTabsAction.ID, + label: nls.localize('indentationToTabs', "Convert Indentation to Tabs"), + alias: 'Convert Indentation to Tabs', + precondition: EditorContextKeys.writable + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let model = editor.getModel(); + if (!model) { + return; + } + let modelOpts = model.getOptions(); + const command = new IndentationToTabsCommand(editor.getSelection(), modelOpts.tabSize); + + editor.pushUndoStop(); + editor.executeCommands(this.id, [command]); + editor.pushUndoStop(); + + model.updateOptions({ + insertSpaces: false + }); + } +} + +export class ChangeIndentationSizeAction extends EditorAction { + + constructor(private insertSpaces: boolean, opts: IActionOptions) { + super(opts); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + const quickOpenService = accessor.get(IQuickOpenService); + const modelService = accessor.get(IModelService); + + let model = editor.getModel(); + if (!model) { + return undefined; + } + + let creationOpts = modelService.getCreationOptions(model.getLanguageIdentifier().language, model.uri); + const picks = [1, 2, 3, 4, 5, 6, 7, 8].map(n => ({ + id: n.toString(), + label: n.toString(), + // add description for tabSize value set in the configuration + description: n === creationOpts.tabSize ? nls.localize('configuredTabSize', "Configured Tab Size") : null + })); + + // auto focus the tabSize set for the current editor + const autoFocusIndex = Math.min(model.getOptions().tabSize - 1, 7); + + return TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).then(() => + quickOpenService.pick(picks, { placeHolder: nls.localize({ key: 'selectTabWidth', comment: ['Tab corresponds to the tab key'] }, "Select Tab Size for Current File"), autoFocus: { autoFocusIndex } }).then(pick => { + if (pick) { + model.updateOptions({ + tabSize: parseInt(pick.label, 10), + insertSpaces: this.insertSpaces + }); + } + }) + ); + } +} + +@editorAction +export class IndentUsingTabs extends ChangeIndentationSizeAction { + + public static ID = 'editor.action.indentUsingTabs'; + + constructor() { + super(false, { + id: IndentUsingTabs.ID, + label: nls.localize('indentUsingTabs', "Indent Using Tabs"), + alias: 'Indent Using Tabs', + precondition: null + }); + } +} + +@editorAction +export class IndentUsingSpaces extends ChangeIndentationSizeAction { + + public static ID = 'editor.action.indentUsingSpaces'; + + constructor() { + super(true, { + id: IndentUsingSpaces.ID, + label: nls.localize('indentUsingSpaces', "Indent Using Spaces"), + alias: 'Indent Using Spaces', + precondition: null + }); + } +} + +@editorAction +export class DetectIndentation extends EditorAction { + + public static ID = 'editor.action.detectIndentation'; + + constructor() { + super({ + id: DetectIndentation.ID, + label: nls.localize('detectIndentation', "Detect Indentation from Content"), + alias: 'Detect Indentation from Content', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const modelService = accessor.get(IModelService); + + let model = editor.getModel(); + if (!model) { + return; + } + + let creationOpts = modelService.getCreationOptions(model.getLanguageIdentifier().language, model.uri); + model.detectIndentation(creationOpts.insertSpaces, creationOpts.tabSize); + } +} + +@editorAction +export class ReindentLinesAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.reindentlines', + label: nls.localize('editor.reindentlines', "Reindent Lines"), + alias: 'Reindent Lines', + precondition: EditorContextKeys.writable + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let model = editor.getModel(); + if (!model) { + return; + } + let edits = getReindentEditOperations(model, 1, model.getLineCount()); + if (edits) { + editor.executeEdits(this.id, edits); + } + } +} + +export class AutoIndentOnPasteCommand implements ICommand { + + private _edits: TextEdit[]; + private _newEol: EndOfLineSequence; + + private _initialSelection: Selection; + private _selectionId: string; + + constructor(edits: TextEdit[], initialSelection: Selection) { + this._initialSelection = initialSelection; + this._edits = []; + this._newEol = undefined; + + for (let edit of edits) { + if (typeof edit.eol === 'number') { + this._newEol = edit.eol; + } + if (edit.range && typeof edit.text === 'string') { + this._edits.push(edit); + } + } + } + + public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void { + for (let edit of this._edits) { + builder.addEditOperation(Range.lift(edit.range), edit.text); + } + + var selectionIsSet = false; + if (Array.isArray(this._edits) && this._edits.length === 1 && this._initialSelection.isEmpty()) { + if (this._edits[0].range.startColumn === this._initialSelection.endColumn && + this._edits[0].range.startLineNumber === this._initialSelection.endLineNumber) { + selectionIsSet = true; + this._selectionId = builder.trackSelection(this._initialSelection, true); + } else if (this._edits[0].range.endColumn === this._initialSelection.startColumn && + this._edits[0].range.endLineNumber === this._initialSelection.startLineNumber) { + selectionIsSet = true; + this._selectionId = builder.trackSelection(this._initialSelection, false); + } + } + + if (!selectionIsSet) { + this._selectionId = builder.trackSelection(this._initialSelection); + } + } + + public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { + return helper.getTrackedSelection(this._selectionId); + } +} + +@commonEditorContribution +export class AutoIndentOnPaste implements IEditorContribution { + private static ID = 'editor.contrib.autoIndentOnPaste'; + + private editor: ICommonCodeEditor; + private callOnDispose: IDisposable[]; + private callOnModel: IDisposable[]; + + constructor(editor: ICommonCodeEditor) { + this.editor = editor; + this.callOnDispose = []; + this.callOnModel = []; + + this.callOnDispose.push(editor.onDidChangeConfiguration(() => this.update())); + this.callOnDispose.push(editor.onDidChangeModel(() => this.update())); + this.callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update())); + } + + private update(): void { + + // clean up + this.callOnModel = dispose(this.callOnModel); + + // we are disabled + if (!this.editor.getConfiguration().autoIndent || this.editor.getConfiguration().contribInfo.formatOnPaste) { + return; + } + + // no model + if (!this.editor.getModel()) { + return; + } + + this.callOnModel.push(this.editor.onDidPaste((range: Range) => { + this.trigger(range); + })); + } + + private trigger(range: Range): void { + if (this.editor.getSelections().length > 1) { + return; + } + + const model = this.editor.getModel(); + if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) { + return; + } + const { tabSize, insertSpaces } = model.getOptions(); + this.editor.pushUndoStop(); + let textEdits: TextEdit[] = []; + + let indentConverter = { + shiftIndent: (indentation) => { + let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + 1, tabSize); + let newIndentation = ''; + for (let i = 0; i < desiredIndentCount; i++) { + newIndentation += '\t'; + } + + return newIndentation; + }, + unshiftIndent: (indentation) => { + let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + 1, tabSize); + let newIndentation = ''; + for (let i = 0; i < desiredIndentCount; i++) { + newIndentation += '\t'; + } + + return newIndentation; + } + }; + + let startLineNumber = range.startLineNumber; + + while (startLineNumber <= range.endLineNumber) { + if (this.shouldIgnoreLine(model, startLineNumber)) { + startLineNumber++; + continue; + } + break; + } + + if (startLineNumber > range.endLineNumber) { + return; + } + + let firstLineText = model.getLineContent(startLineNumber); + if (!/\S/.test(firstLineText.substring(0, range.startColumn - 1))) { + let indentOfFirstLine = LanguageConfigurationRegistry.getGoodIndentForLine(model, model.getLanguageIdentifier().id, startLineNumber, indentConverter); + + if (indentOfFirstLine !== null) { + let oldIndentation = strings.getLeadingWhitespace(firstLineText); + let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfFirstLine, tabSize); + let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize); + + if (newSpaceCnt !== oldSpaceCnt) { + let newIndent = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces); + textEdits.push({ + range: new Range(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), + text: newIndent + }); + firstLineText = newIndent + firstLineText.substr(oldIndentation.length); + } + } + } + + if (startLineNumber !== range.endLineNumber) { + let virtualModel = { + getLineTokens: (lineNumber: number) => { + return model.getLineTokens(lineNumber); + }, + getLanguageIdentifier: () => { + return model.getLanguageIdentifier(); + }, + getLanguageIdAtPosition: (lineNumber: number, column: number) => { + return model.getLanguageIdAtPosition(lineNumber, column); + }, + getLineContent: (lineNumber) => { + if (lineNumber === startLineNumber) { + return firstLineText; + } else { + return model.getLineContent(lineNumber); + } + } + }; + let indentOfSecondLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdentifier().id, startLineNumber + 1, indentConverter); + if (indentOfSecondLine !== null) { + let newSpaceCntOfSecondLine = IndentUtil.getSpaceCnt(indentOfSecondLine, tabSize); + let oldSpaceCntOfSecondLine = IndentUtil.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(startLineNumber + 1)), tabSize); + + if (newSpaceCntOfSecondLine !== oldSpaceCntOfSecondLine) { + let spaceCntOffset = newSpaceCntOfSecondLine - oldSpaceCntOfSecondLine; + for (let i = startLineNumber + 1; i <= range.endLineNumber; i++) { + let lineContent = model.getLineContent(i); + let originalIndent = strings.getLeadingWhitespace(lineContent); + let originalSpacesCnt = IndentUtil.getSpaceCnt(originalIndent, tabSize); + let newSpacesCnt = originalSpacesCnt + spaceCntOffset; + let newIndent = IndentUtil.generateIndent(newSpacesCnt, tabSize, insertSpaces); + + if (newIndent !== originalIndent) { + textEdits.push({ + range: new Range(i, 1, i, originalIndent.length + 1), + text: newIndent + }); + } + } + } + } + } + + let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection()); + this.editor.executeCommand('autoIndentOnPaste', cmd); + this.editor.pushUndoStop(); + } + + private shouldIgnoreLine(model: ITokenizedModel, lineNumber: number): boolean { + model.forceTokenization(lineNumber); + let nonWhiteSpaceColumn = model.getLineFirstNonWhitespaceColumn(lineNumber); + if (nonWhiteSpaceColumn === 0) { + return true; + } + let tokens = model.getLineTokens(lineNumber); + if (tokens.getTokenCount() > 0) { + let firstNonWhiteSpaceToken = tokens.findTokenAtOffset(nonWhiteSpaceColumn); + if (firstNonWhiteSpaceToken && firstNonWhiteSpaceToken.tokenType === StandardTokenType.Comment) { + return true; + } + } + + return false; + } + + public getId(): string { + return AutoIndentOnPaste.ID; + } + + public dispose(): void { + this.callOnDispose = dispose(this.callOnDispose); + this.callOnModel = dispose(this.callOnModel); + } +} + +function getIndentationEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder, tabSize: number, tabsToSpaces: boolean): void { + if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) { + // Model is empty + return; + } + + let spaces = ''; + for (let i = 0; i < tabSize; i++) { + spaces += ' '; + } + + const content = model.getLinesContent(); + for (let i = 0; i < content.length; i++) { + let lastIndentationColumn = model.getLineFirstNonWhitespaceColumn(i + 1); + if (lastIndentationColumn === 0) { + lastIndentationColumn = model.getLineMaxColumn(i + 1); + } + + const text = (tabsToSpaces ? content[i].substr(0, lastIndentationColumn).replace(/\t/ig, spaces) : + content[i].substr(0, lastIndentationColumn).replace(new RegExp(spaces, 'gi'), '\t')) + + content[i].substr(lastIndentationColumn); + + builder.addEditOperation(new Range(i + 1, 1, i + 1, model.getLineMaxColumn(i + 1)), text); + } +} + +export class IndentationToSpacesCommand implements ICommand { + + private selectionId: string; + + constructor(private selection: Selection, private tabSize: number) { } + + public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void { + this.selectionId = builder.trackSelection(this.selection); + getIndentationEditOperations(model, builder, this.tabSize, true); + } + + public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { + return helper.getTrackedSelection(this.selectionId); + } +} + +export class IndentationToTabsCommand implements ICommand { + + private selectionId: string; + + constructor(private selection: Selection, private tabSize: number) { } + + public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void { + this.selectionId = builder.trackSelection(this.selection); + getIndentationEditOperations(model, builder, this.tabSize, false); + } + + public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { + return helper.getTrackedSelection(this.selectionId); + } +} diff --git a/src/vs/editor/contrib/indentation/test/indentation.test.ts b/src/vs/editor/contrib/indentation/test/indentation.test.ts new file mode 100644 index 0000000000..6b436adac1 --- /dev/null +++ b/src/vs/editor/contrib/indentation/test/indentation.test.ts @@ -0,0 +1,172 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Selection } from 'vs/editor/common/core/selection'; +import { IndentationToSpacesCommand, IndentationToTabsCommand } from 'vs/editor/contrib/indentation/common/indentation'; +import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; + +function testIndentationToSpacesCommand(lines: string[], selection: Selection, tabSize: number, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new IndentationToSpacesCommand(sel, tabSize), expectedLines, expectedSelection); +} + +function testIndentationToTabsCommand(lines: string[], selection: Selection, tabSize: number, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new IndentationToTabsCommand(sel, tabSize), expectedLines, expectedSelection); +} + +suite('Editor Contrib - Indentation to Spaces', () => { + + test('single tabs only at start of line', function () { + testIndentationToSpacesCommand( + [ + 'first', + 'second line', + 'third line', + '\tfourth line', + '\tfifth' + ], + new Selection(2, 3, 2, 3), + 4, + [ + 'first', + 'second line', + 'third line', + ' fourth line', + ' fifth' + ], + new Selection(2, 3, 2, 3) + ); + }); + + test('multiple tabs at start of line', function () { + testIndentationToSpacesCommand( + [ + '\t\tfirst', + '\tsecond line', + '\t\t\t third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 5, 1, 5), + 3, + [ + ' first', + ' second line', + ' third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 5, 1, 5) + ); + }); + + test('multiple tabs', function () { + testIndentationToSpacesCommand( + [ + '\t\tfirst\t', + '\tsecond \t line \t', + '\t\t\t third line', + ' \tfourth line', + 'fifth' + ], + new Selection(1, 5, 1, 5), + 2, + [ + ' first\t', + ' second \t line \t', + ' third line', + ' fourth line', + 'fifth' + ], + new Selection(1, 5, 1, 5) + ); + }); + + test('empty lines', function () { + testIndentationToSpacesCommand( + [ + '\t\t\t', + '\t', + '\t\t' + ], + new Selection(1, 4, 1, 4), + 2, + [ + ' ', + ' ', + ' ' + ], + new Selection(1, 4, 1, 4) + ); + }); +}); + +suite('Editor Contrib - Indentation to Tabs', () => { + + test('spaces only at start of line', function () { + testIndentationToTabsCommand( + [ + ' first', + 'second line', + ' third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 3, 2, 3), + 4, + [ + '\tfirst', + 'second line', + '\tthird line', + 'fourth line', + 'fifth' + ], + new Selection(2, 3, 2, 3) + ); + }); + + test('multiple spaces at start of line', function () { + testIndentationToTabsCommand( + [ + 'first', + ' second line', + ' third line', + 'fourth line', + ' fifth' + ], + new Selection(1, 5, 1, 5), + 3, + [ + 'first', + '\tsecond line', + '\t\t\t third line', + 'fourth line', + '\t fifth' + ], + new Selection(1, 5, 1, 5) + ); + }); + + test('multiple spaces', function () { + testIndentationToTabsCommand( + [ + ' first ', + ' second line \t', + ' third line', + ' fourth line', + 'fifth' + ], + new Selection(1, 5, 1, 5), + 2, + [ + '\t\t\tfirst ', + '\tsecond line \t', + '\t\t\t third line', + '\t fourth line', + 'fifth' + ], + new Selection(1, 5, 1, 5) + ); + }); +}); diff --git a/src/vs/editor/contrib/linesOperations/common/copyLinesCommand.ts b/src/vs/editor/contrib/linesOperations/common/copyLinesCommand.ts new file mode 100644 index 0000000000..7715deda6a --- /dev/null +++ b/src/vs/editor/contrib/linesOperations/common/copyLinesCommand.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Range } from 'vs/editor/common/core/range'; +import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; + +export class CopyLinesCommand implements editorCommon.ICommand { + + private _selection: Selection; + private _isCopyingDown: boolean; + + private _selectionDirection: SelectionDirection; + private _selectionId: string; + private _startLineNumberDelta: number; + private _endLineNumberDelta: number; + + constructor(selection: Selection, isCopyingDown: boolean) { + this._selection = selection; + this._isCopyingDown = isCopyingDown; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + var s = this._selection; + + this._startLineNumberDelta = 0; + this._endLineNumberDelta = 0; + if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) { + this._endLineNumberDelta = 1; + s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1)); + } + + var sourceLines: string[] = []; + for (var i = s.startLineNumber; i <= s.endLineNumber; i++) { + sourceLines.push(model.getLineContent(i)); + } + var sourceText = sourceLines.join('\n'); + + if (sourceText === '') { + // Duplicating empty line + if (this._isCopyingDown) { + this._startLineNumberDelta++; + this._endLineNumberDelta++; + } + } + + if (!this._isCopyingDown) { + builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + sourceText); + } else { + builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), sourceText + '\n'); + } + + this._selectionId = builder.trackSelection(s); + this._selectionDirection = this._selection.getDirection(); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + var result = helper.getTrackedSelection(this._selectionId); + + if (this._startLineNumberDelta !== 0 || this._endLineNumberDelta !== 0) { + var startLineNumber = result.startLineNumber, + startColumn = result.startColumn, + endLineNumber = result.endLineNumber, + endColumn = result.endColumn; + + if (this._startLineNumberDelta !== 0) { + startLineNumber = startLineNumber + this._startLineNumberDelta; + startColumn = 1; + } + + if (this._endLineNumberDelta !== 0) { + endLineNumber = endLineNumber + this._endLineNumberDelta; + endColumn = 1; + } + + result = Selection.createWithDirection(startLineNumber, startColumn, endLineNumber, endColumn, this._selectionDirection); + } + + return result; + } +} diff --git a/src/vs/editor/contrib/linesOperations/common/deleteLinesCommand.ts b/src/vs/editor/contrib/linesOperations/common/deleteLinesCommand.ts new file mode 100644 index 0000000000..c712b1ffbe --- /dev/null +++ b/src/vs/editor/contrib/linesOperations/common/deleteLinesCommand.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon'; + + +export class DeleteLinesCommand implements ICommand { + + public static createFromSelection(selection: Selection): DeleteLinesCommand { + var endLineNumber = selection.endLineNumber; + if (selection.startLineNumber < selection.endLineNumber && selection.endColumn === 1) { + endLineNumber -= 1; + } + return new DeleteLinesCommand(selection.startLineNumber, endLineNumber, selection.positionColumn); + } + + private startLineNumber: number; + private endLineNumber: number; + private restoreCursorToColumn: number; + + constructor(startLineNumber: number, endLineNumber: number, restoreCursorToColumn: number) { + this.startLineNumber = startLineNumber; + this.endLineNumber = endLineNumber; + this.restoreCursorToColumn = restoreCursorToColumn; + } + + public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void { + if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) { + // Model is empty + return; + } + + var startLineNumber = this.startLineNumber; + var endLineNumber = this.endLineNumber; + + var startColumn = 1; + var endColumn = model.getLineMaxColumn(endLineNumber); + if (endLineNumber < model.getLineCount()) { + endLineNumber += 1; + endColumn = 1; + } else if (startLineNumber > 1) { + startLineNumber -= 1; + startColumn = model.getLineMaxColumn(startLineNumber); + } + + builder.addTrackedEditOperation(new Range(startLineNumber, startColumn, endLineNumber, endColumn), null); + } + + public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { + var inverseEditOperations = helper.getInverseEditOperations(); + var srcRange = inverseEditOperations[0].range; + return new Selection( + srcRange.endLineNumber, + this.restoreCursorToColumn, + srcRange.endLineNumber, + this.restoreCursorToColumn + ); + } +} diff --git a/src/vs/editor/contrib/linesOperations/common/linesOperations.ts b/src/vs/editor/contrib/linesOperations/common/linesOperations.ts new file mode 100644 index 0000000000..3c4fbaef38 --- /dev/null +++ b/src/vs/editor/contrib/linesOperations/common/linesOperations.ts @@ -0,0 +1,797 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { SortLinesCommand } from 'vs/editor/contrib/linesOperations/common/sortLinesCommand'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { TrimTrailingWhitespaceCommand } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand'; +import { ICommand, ICommonCodeEditor, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { editorAction, ServicesAccessor, IActionOptions, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { CopyLinesCommand } from './copyLinesCommand'; +import { DeleteLinesCommand } from './deleteLinesCommand'; +import { MoveLinesCommand } from './moveLinesCommand'; +import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; +import { CoreEditingCommands } from 'vs/editor/common/controller/coreCommands'; + +// copy lines + +abstract class AbstractCopyLinesAction extends EditorAction { + + private down: boolean; + + constructor(down: boolean, opts: IActionOptions) { + super(opts); + this.down = down; + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + + var commands: ICommand[] = []; + var selections = editor.getSelections(); + + for (var i = 0; i < selections.length; i++) { + commands.push(new CopyLinesCommand(selections[i], this.down)); + } + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } +} + +@editorAction +class CopyLinesUpAction extends AbstractCopyLinesAction { + constructor() { + super(false, { + id: 'editor.action.copyLinesUpAction', + label: nls.localize('lines.copyUp', "Copy Line Up"), + alias: 'Copy Line Up', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Alt | KeyMod.Shift | KeyCode.UpArrow, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.UpArrow } + } + }); + } +} + +@editorAction +class CopyLinesDownAction extends AbstractCopyLinesAction { + constructor() { + super(true, { + id: 'editor.action.copyLinesDownAction', + label: nls.localize('lines.copyDown', "Copy Line Down"), + alias: 'Copy Line Down', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Alt | KeyMod.Shift | KeyCode.DownArrow, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.DownArrow } + } + }); + } +} + +// move lines + +abstract class AbstractMoveLinesAction extends EditorAction { + + private down: boolean; + + constructor(down: boolean, opts: IActionOptions) { + super(opts); + this.down = down; + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + + var commands: ICommand[] = []; + var selections = editor.getSelections(); + var autoIndent = editor.getConfiguration().autoIndent; + + for (var i = 0; i < selections.length; i++) { + commands.push(new MoveLinesCommand(selections[i], this.down, autoIndent)); + } + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } +} + +@editorAction +class MoveLinesUpAction extends AbstractMoveLinesAction { + constructor() { + super(false, { + id: 'editor.action.moveLinesUpAction', + label: nls.localize('lines.moveUp', "Move Line Up"), + alias: 'Move Line Up', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Alt | KeyCode.UpArrow, + linux: { primary: KeyMod.Alt | KeyCode.UpArrow } + } + }); + } +} + +@editorAction +class MoveLinesDownAction extends AbstractMoveLinesAction { + constructor() { + super(true, { + id: 'editor.action.moveLinesDownAction', + label: nls.localize('lines.moveDown', "Move Line Down"), + alias: 'Move Line Down', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Alt | KeyCode.DownArrow, + linux: { primary: KeyMod.Alt | KeyCode.DownArrow } + } + }); + } +} + +abstract class AbstractSortLinesAction extends EditorAction { + private descending: boolean; + + constructor(descending: boolean, opts: IActionOptions) { + super(opts); + this.descending = descending; + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + + if (!SortLinesCommand.canRun(editor.getModel(), editor.getSelection(), this.descending)) { + return; + } + + var command = new SortLinesCommand(editor.getSelection(), this.descending); + + editor.pushUndoStop(); + editor.executeCommands(this.id, [command]); + editor.pushUndoStop(); + } +} + +@editorAction +class SortLinesAscendingAction extends AbstractSortLinesAction { + constructor() { + super(false, { + id: 'editor.action.sortLinesAscending', + label: nls.localize('lines.sortAscending', "Sort Lines Ascending"), + alias: 'Sort Lines Ascending', + precondition: EditorContextKeys.writable + }); + } +} + +@editorAction +class SortLinesDescendingAction extends AbstractSortLinesAction { + constructor() { + super(true, { + id: 'editor.action.sortLinesDescending', + label: nls.localize('lines.sortDescending', "Sort Lines Descending"), + alias: 'Sort Lines Descending', + precondition: EditorContextKeys.writable + }); + } +} + +@editorAction +export class TrimTrailingWhitespaceAction extends EditorAction { + + public static ID = 'editor.action.trimTrailingWhitespace'; + + constructor() { + super({ + id: TrimTrailingWhitespaceAction.ID, + label: nls.localize('lines.trimTrailingWhitespace', "Trim Trailing Whitespace"), + alias: 'Trim Trailing Whitespace', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_X) + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + + var command = new TrimTrailingWhitespaceCommand(editor.getSelection()); + + editor.pushUndoStop(); + editor.executeCommands(this.id, [command]); + editor.pushUndoStop(); + } +} + +// delete lines + +interface IDeleteLinesOperation { + startLineNumber: number; + endLineNumber: number; + positionColumn: number; +} + +abstract class AbstractRemoveLinesAction extends EditorAction { + _getLinesToRemove(editor: ICommonCodeEditor): IDeleteLinesOperation[] { + // Construct delete operations + var operations: IDeleteLinesOperation[] = editor.getSelections().map((s) => { + + var endLineNumber = s.endLineNumber; + if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) { + endLineNumber -= 1; + } + + return { + startLineNumber: s.startLineNumber, + endLineNumber: endLineNumber, + positionColumn: s.positionColumn + }; + }); + + // Sort delete operations + operations.sort((a, b) => { + return a.startLineNumber - b.startLineNumber; + }); + + // Merge delete operations on consecutive lines + var mergedOperations: IDeleteLinesOperation[] = []; + var previousOperation = operations[0]; + for (var i = 1; i < operations.length; i++) { + if (previousOperation.endLineNumber + 1 === operations[i].startLineNumber) { + // Merge current operations into the previous one + previousOperation.endLineNumber = operations[i].endLineNumber; + } else { + // Push previous operation + mergedOperations.push(previousOperation); + previousOperation = operations[i]; + } + } + // Push the last operation + mergedOperations.push(previousOperation); + + return mergedOperations; + } +} + +@editorAction +class DeleteLinesAction extends AbstractRemoveLinesAction { + + constructor() { + super({ + id: 'editor.action.deleteLines', + label: nls.localize('lines.delete', "Delete Line"), + alias: 'Delete Line', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_K + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + + var ops = this._getLinesToRemove(editor); + + // Finally, construct the delete lines commands + var commands: ICommand[] = ops.map((op) => { + return new DeleteLinesCommand(op.startLineNumber, op.endLineNumber, op.positionColumn); + }); + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } +} + +@editorAction +export class IndentLinesAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.indentLines', + label: nls.localize('lines.indent', "Indent Line"), + alias: 'Indent Line', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + editor.pushUndoStop(); + editor.executeCommands(this.id, TypeOperations.indent(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections())); + editor.pushUndoStop(); + } +} + +@editorAction +class OutdentLinesAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.outdentLines', + label: nls.localize('lines.outdent', "Outdent Line"), + alias: 'Outdent Line', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.US_OPEN_SQUARE_BRACKET + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + CoreEditingCommands.Outdent.runEditorCommand(null, editor, null); + } +} + +@editorAction +export class InsertLineBeforeAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.insertLineBefore', + label: nls.localize('lines.insertBefore', "Insert Line Above"), + alias: 'Insert Line Above', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + editor.pushUndoStop(); + editor.executeCommands(this.id, TypeOperations.lineInsertBefore(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections())); + } +} + +@editorAction +export class InsertLineAfterAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.insertLineAfter', + label: nls.localize('lines.insertAfter', "Insert Line Below"), + alias: 'Insert Line Below', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.Enter + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + editor.pushUndoStop(); + editor.executeCommands(this.id, TypeOperations.lineInsertAfter(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections())); + } +} + +export abstract class AbstractDeleteAllToBoundaryAction extends EditorAction { + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const primaryCursor = editor.getSelection(); + let rangesToDelete = this._getRangesToDelete(editor); + // merge overlapping selections + let effectiveRanges: Range[] = []; + + for (let i = 0, count = rangesToDelete.length - 1; i < count; i++) { + let range = rangesToDelete[i]; + let nextRange = rangesToDelete[i + 1]; + + if (Range.intersectRanges(range, nextRange) === null) { + effectiveRanges.push(range); + } else { + rangesToDelete[i + 1] = Range.plusRange(range, nextRange); + } + } + + effectiveRanges.push(rangesToDelete[rangesToDelete.length - 1]); + + let endCursorState = this._getEndCursorState(primaryCursor, effectiveRanges); + let edits: IIdentifiedSingleEditOperation[] = effectiveRanges.map(range => { + endCursorState.push(new Selection(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn)); + return EditOperation.replace(range, ''); + }); + + editor.executeEdits(this.id, edits, endCursorState); + } + + /** + * Compute the cursor state after the edit operations were applied. + */ + protected abstract _getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[]; + + protected abstract _getRangesToDelete(editor: ICommonCodeEditor): Range[]; +} + +@editorAction +export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction { + constructor() { + super({ + id: 'deleteAllLeft', + label: nls.localize('lines.deleteAllLeft', "Delete All Left"), + alias: 'Delete All Left', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: null, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } + } + }); + } + + _getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[] { + let endPrimaryCursor: Selection; + let endCursorState: Selection[] = []; + + for (let i = 0, len = rangesToDelete.length; i < len; i++) { + let range = rangesToDelete[i]; + let endCursor = new Selection(rangesToDelete[i].startLineNumber, rangesToDelete[i].startColumn, rangesToDelete[i].startLineNumber, rangesToDelete[i].startColumn); + + if (range.intersectRanges(primaryCursor)) { + endPrimaryCursor = endCursor; + } else { + endCursorState.push(endCursor); + } + } + + if (endPrimaryCursor) { + endCursorState.unshift(endPrimaryCursor); + } + + return endCursorState; + } + + _getRangesToDelete(editor: ICommonCodeEditor): Range[] { + let rangesToDelete: Range[] = editor.getSelections(); + + rangesToDelete.sort(Range.compareRangesUsingStarts); + rangesToDelete = rangesToDelete.map(selection => { + if (selection.isEmpty()) { + return new Range(selection.startLineNumber, 1, selection.startLineNumber, selection.startColumn); + } else { + return selection; + } + }); + + return rangesToDelete; + } +} + +@editorAction +export class DeleteAllRightAction extends AbstractDeleteAllToBoundaryAction { + constructor() { + super({ + id: 'deleteAllRight', + label: nls.localize('lines.deleteAllRight', "Delete All Right"), + alias: 'Delete All Right', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: null, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_K, secondary: [KeyMod.CtrlCmd | KeyCode.Delete] } + } + }); + } + + _getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[] { + let endPrimaryCursor: Selection; + let endCursorState: Selection[] = []; + for (let i = 0, len = rangesToDelete.length, offset = 0; i < len; i++) { + let range = rangesToDelete[i]; + let endCursor = new Selection(range.startLineNumber - offset, range.startColumn, range.startLineNumber - offset, range.startColumn); + + if (range.intersectRanges(primaryCursor)) { + endPrimaryCursor = endCursor; + } else { + endCursorState.push(endCursor); + } + } + + if (endPrimaryCursor) { + endCursorState.unshift(endPrimaryCursor); + } + + return endCursorState; + } + + _getRangesToDelete(editor: ICommonCodeEditor): Range[] { + let model = editor.getModel(); + + let rangesToDelete: Range[] = editor.getSelections().map((sel) => { + if (sel.isEmpty()) { + const maxColumn = model.getLineMaxColumn(sel.startLineNumber); + + if (sel.startColumn === maxColumn) { + return new Range(sel.startLineNumber, sel.startColumn, sel.startLineNumber + 1, 1); + } else { + return new Range(sel.startLineNumber, sel.startColumn, sel.startLineNumber, maxColumn); + } + } + return sel; + }); + + rangesToDelete.sort(Range.compareRangesUsingStarts); + return rangesToDelete; + } +} + +@editorAction +export class JoinLinesAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.joinLines', + label: nls.localize('lines.joinLines', "Join Lines"), + alias: 'Join Lines', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_J } + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let selections = editor.getSelections(); + let primaryCursor = editor.getSelection(); + + selections.sort(Range.compareRangesUsingStarts); + let reducedSelections: Selection[] = []; + + let lastSelection = selections.reduce((previousValue, currentValue) => { + if (previousValue.isEmpty()) { + if (previousValue.endLineNumber === currentValue.startLineNumber) { + if (primaryCursor.equalsSelection(previousValue)) { + primaryCursor = currentValue; + } + return currentValue; + } + + if (currentValue.startLineNumber > previousValue.endLineNumber + 1) { + reducedSelections.push(previousValue); + return currentValue; + } else { + return new Selection(previousValue.startLineNumber, previousValue.startColumn, currentValue.endLineNumber, currentValue.endColumn); + } + } else { + if (currentValue.startLineNumber > previousValue.endLineNumber) { + reducedSelections.push(previousValue); + return currentValue; + } else { + return new Selection(previousValue.startLineNumber, previousValue.startColumn, currentValue.endLineNumber, currentValue.endColumn); + } + } + }); + + reducedSelections.push(lastSelection); + + let model = editor.getModel(); + let edits = []; + let endCursorState = []; + let endPrimaryCursor = primaryCursor; + let lineOffset = 0; + + for (let i = 0, len = reducedSelections.length; i < len; i++) { + let selection = reducedSelections[i]; + let startLineNumber = selection.startLineNumber; + let startColumn = 1; + let endLineNumber: number, + endColumn: number, + columnDeltaOffset: number; + + let selectionEndPositionOffset = model.getLineContent(selection.endLineNumber).length - selection.endColumn; + + if (selection.isEmpty() || selection.startLineNumber === selection.endLineNumber) { + let position = selection.getStartPosition(); + if (position.lineNumber < model.getLineCount()) { + endLineNumber = startLineNumber + 1; + endColumn = model.getLineMaxColumn(endLineNumber); + } else { + endLineNumber = position.lineNumber; + endColumn = model.getLineMaxColumn(position.lineNumber); + } + } else { + endLineNumber = selection.endLineNumber; + endColumn = model.getLineMaxColumn(endLineNumber); + } + + let trimmedLinesContent = model.getLineContent(startLineNumber); + + for (let i = startLineNumber + 1; i <= endLineNumber; i++) { + let lineText = model.getLineContent(i); + let firstNonWhitespaceIdx = model.getLineFirstNonWhitespaceColumn(i); + + if (firstNonWhitespaceIdx >= 1) { + let insertSpace = true; + if (trimmedLinesContent === '') { + insertSpace = false; + } + + if (insertSpace && (trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === ' ' || + trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === '\t')) { + insertSpace = false; + trimmedLinesContent = trimmedLinesContent.replace(/[\s\uFEFF\xA0]+$/g, ' '); + } + + let lineTextWithoutIndent = lineText.substr(firstNonWhitespaceIdx - 1); + + trimmedLinesContent += (insertSpace ? ' ' : '') + lineTextWithoutIndent; + + if (insertSpace) { + columnDeltaOffset = lineTextWithoutIndent.length + 1; + } else { + columnDeltaOffset = lineTextWithoutIndent.length; + } + } else { + columnDeltaOffset = 0; + } + } + + let deleteSelection = new Range(startLineNumber, startColumn, endLineNumber, endColumn); + + if (!deleteSelection.isEmpty()) { + let resultSelection: Selection; + + if (selection.isEmpty()) { + edits.push(EditOperation.replace(deleteSelection, trimmedLinesContent)); + resultSelection = new Selection(deleteSelection.startLineNumber - lineOffset, trimmedLinesContent.length - columnDeltaOffset + 1, startLineNumber - lineOffset, trimmedLinesContent.length - columnDeltaOffset + 1); + } else { + if (selection.startLineNumber === selection.endLineNumber) { + edits.push(EditOperation.replace(deleteSelection, trimmedLinesContent)); + resultSelection = new Selection(selection.startLineNumber - lineOffset, selection.startColumn, + selection.endLineNumber - lineOffset, selection.endColumn); + } else { + edits.push(EditOperation.replace(deleteSelection, trimmedLinesContent)); + resultSelection = new Selection(selection.startLineNumber - lineOffset, selection.startColumn, + selection.startLineNumber - lineOffset, trimmedLinesContent.length - selectionEndPositionOffset); + } + } + + if (Range.intersectRanges(deleteSelection, primaryCursor) !== null) { + endPrimaryCursor = resultSelection; + } else { + endCursorState.push(resultSelection); + } + } + + lineOffset += deleteSelection.endLineNumber - deleteSelection.startLineNumber; + } + + endCursorState.unshift(endPrimaryCursor); + editor.executeEdits(this.id, edits, endCursorState); + + } +} + +@editorAction +export class TransposeAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.transpose', + label: nls.localize('editor.transpose', "Transpose characters around the cursor"), + alias: 'Transpose characters around the cursor', + precondition: EditorContextKeys.writable + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let selections = editor.getSelections(); + let model = editor.getModel(); + let commands: ICommand[] = []; + + for (let i = 0, len = selections.length; i < len; i++) { + let selection = selections[i]; + + if (!selection.isEmpty()) { + continue; + } + + let cursor = selection.getStartPosition(); + let maxColumn = model.getLineMaxColumn(cursor.lineNumber); + + if (cursor.column >= maxColumn) { + if (cursor.lineNumber === model.getLineCount()) { + continue; + } + + // The cursor is at the end of current line and current line is not empty + // then we transpose the character before the cursor and the line break if there is any following line. + let deleteSelection = new Range(cursor.lineNumber, Math.max(1, cursor.column - 1), cursor.lineNumber + 1, 1); + let chars = model.getValueInRange(deleteSelection).split('').reverse().join(''); + + commands.push(new ReplaceCommand(new Selection(cursor.lineNumber, Math.max(1, cursor.column - 1), cursor.lineNumber + 1, 1), chars)); + } else { + let deleteSelection = new Range(cursor.lineNumber, Math.max(1, cursor.column - 1), cursor.lineNumber, cursor.column + 1); + let chars = model.getValueInRange(deleteSelection).split('').reverse().join(''); + commands.push(new ReplaceCommandThatPreservesSelection(deleteSelection, chars, + new Selection(cursor.lineNumber, cursor.column + 1, cursor.lineNumber, cursor.column + 1))); + } + } + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } +} + +export abstract class AbstractCaseAction extends EditorAction { + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let selections = editor.getSelections(); + let model = editor.getModel(); + let commands: ICommand[] = []; + + for (let i = 0, len = selections.length; i < len; i++) { + let selection = selections[i]; + if (selection.isEmpty()) { + let cursor = selection.getStartPosition(); + let word = model.getWordAtPosition(cursor); + + if (!word) { + continue; + } + + let wordRange = new Range(cursor.lineNumber, word.startColumn, cursor.lineNumber, word.endColumn); + let text = model.getValueInRange(wordRange); + commands.push(new ReplaceCommandThatPreservesSelection(wordRange, this._modifyText(text), + new Selection(cursor.lineNumber, cursor.column, cursor.lineNumber, cursor.column))); + + } else { + let text = model.getValueInRange(selection); + commands.push(new ReplaceCommandThatPreservesSelection(selection, this._modifyText(text), selection)); + } + } + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } + + protected abstract _modifyText(text: string): string; +} + +@editorAction +export class UpperCaseAction extends AbstractCaseAction { + constructor() { + super({ + id: 'editor.action.transformToUppercase', + label: nls.localize('editor.transformToUppercase', "Transform to Uppercase"), + alias: 'Transform to Uppercase', + precondition: EditorContextKeys.writable + }); + } + + protected _modifyText(text: string): string { + return text.toLocaleUpperCase(); + } +} + +@editorAction +export class LowerCaseAction extends AbstractCaseAction { + constructor() { + super({ + id: 'editor.action.transformToLowercase', + label: nls.localize('editor.transformToLowercase', "Transform to Lowercase"), + alias: 'Transform to Lowercase', + precondition: EditorContextKeys.writable + }); + } + + protected _modifyText(text: string): string { + return text.toLocaleLowerCase(); + } +} diff --git a/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts new file mode 100644 index 0000000000..2b226d9405 --- /dev/null +++ b/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts @@ -0,0 +1,356 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon'; +import { LanguageConfigurationRegistry, IIndentConverter } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; +import * as IndentUtil from 'vs/editor/contrib/indentation/common/indentUtils'; +import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; + +export class MoveLinesCommand implements ICommand { + + private _selection: Selection; + private _isMovingDown: boolean; + private _autoIndent: boolean; + + private _selectionId: string; + private _moveEndPositionDown: boolean; + private _moveEndLineSelectionShrink: boolean; + + constructor(selection: Selection, isMovingDown: boolean, autoIndent: boolean) { + this._selection = selection; + this._isMovingDown = isMovingDown; + this._autoIndent = autoIndent; + this._moveEndLineSelectionShrink = false; + } + + public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void { + + var modelLineCount = model.getLineCount(); + + if (this._isMovingDown && this._selection.endLineNumber === modelLineCount) { + return; + } + if (!this._isMovingDown && this._selection.startLineNumber === 1) { + return; + } + + this._moveEndPositionDown = false; + var s = this._selection; + + if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) { + this._moveEndPositionDown = true; + s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1)); + } + + let tabSize = model.getOptions().tabSize; + let insertSpaces = model.getOptions().insertSpaces; + let indentConverter = this.buildIndentConverter(tabSize); + let virtualModel = { + getLineTokens: (lineNumber: number) => { + return model.getLineTokens(lineNumber); + }, + getLanguageIdentifier: () => { + return model.getLanguageIdentifier(); + }, + getLanguageIdAtPosition: (lineNumber: number, column: number) => { + return model.getLanguageIdAtPosition(lineNumber, column); + }, + getLineContent: null + }; + + if (s.startLineNumber === s.endLineNumber && model.getLineMaxColumn(s.startLineNumber) === 1) { + // Current line is empty + var lineNumber = s.startLineNumber; + var otherLineNumber = (this._isMovingDown ? lineNumber + 1 : lineNumber - 1); + + if (model.getLineMaxColumn(otherLineNumber) === 1) { + // Other line number is empty too, so no editing is needed + // Add a no-op to force running by the model + builder.addEditOperation(new Range(1, 1, 1, 1), null); + } else { + // Type content from other line number on line number + builder.addEditOperation(new Range(lineNumber, 1, lineNumber, 1), model.getLineContent(otherLineNumber)); + + // Remove content from other line number + builder.addEditOperation(new Range(otherLineNumber, 1, otherLineNumber, model.getLineMaxColumn(otherLineNumber)), null); + } + // Track selection at the other line number + s = new Selection(otherLineNumber, 1, otherLineNumber, 1); + + } else { + + var movingLineNumber: number, + movingLineText: string; + + if (this._isMovingDown) { + movingLineNumber = s.endLineNumber + 1; + movingLineText = model.getLineContent(movingLineNumber); + // Delete line that needs to be moved + builder.addEditOperation(new Range(movingLineNumber - 1, model.getLineMaxColumn(movingLineNumber - 1), movingLineNumber, model.getLineMaxColumn(movingLineNumber)), null); + + let insertingText = movingLineText; + + if (this.shouldAutoIndent(model, s)) { + let movingLineMatchResult = this.matchEnterRule(model, indentConverter, tabSize, movingLineNumber, s.startLineNumber - 1); + // if s.startLineNumber - 1 matches onEnter rule, we still honor that. + if (movingLineMatchResult !== null) { + let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber)); + let newSpaceCnt = movingLineMatchResult + IndentUtil.getSpaceCnt(oldIndentation, tabSize); + let newIndentation = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces); + insertingText = newIndentation + this.trimLeft(movingLineText); + } else { + // no enter rule matches, let's check indentatin rules then. + virtualModel.getLineContent = (lineNumber) => { + if (lineNumber === s.startLineNumber) { + return model.getLineContent(movingLineNumber); + } else { + return model.getLineContent(lineNumber); + } + }; + let indentOfMovingLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition( + movingLineNumber, 1), s.startLineNumber, indentConverter); + if (indentOfMovingLine !== null) { + let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber)); + let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfMovingLine, tabSize); + let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize); + if (newSpaceCnt !== oldSpaceCnt) { + let newIndentation = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces); + insertingText = newIndentation + this.trimLeft(movingLineText); + } + } + } + + // add edit operations for moving line first to make sure it's executed after we make indentation change + // to s.startLineNumber + builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), insertingText + '\n'); + + let ret = this.matchEnterRule(model, indentConverter, tabSize, s.startLineNumber, s.startLineNumber, insertingText); + // check if the line being moved before matches onEnter rules, if so let's adjust the indentation by onEnter rules. + if (ret !== null) { + if (ret !== 0) { + this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, ret); + } + } else { + // it doesn't match onEnter rules, let's check indentation rules then. + virtualModel.getLineContent = (lineNumber) => { + if (lineNumber === s.startLineNumber) { + return insertingText; + } else if (lineNumber >= s.startLineNumber + 1 && lineNumber <= s.endLineNumber + 1) { + return model.getLineContent(lineNumber - 1); + } else { + return model.getLineContent(lineNumber); + } + }; + + let newIndentatOfMovingBlock = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition( + movingLineNumber, 1), s.startLineNumber + 1, indentConverter); + + if (newIndentatOfMovingBlock !== null) { + const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber)); + const newSpaceCnt = IndentUtil.getSpaceCnt(newIndentatOfMovingBlock, tabSize); + const oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize); + if (newSpaceCnt !== oldSpaceCnt) { + const spaceCntOffset = newSpaceCnt - oldSpaceCnt; + + this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, spaceCntOffset); + } + } + } + } else { + // Insert line that needs to be moved before + builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), insertingText + '\n'); + } + } else { + movingLineNumber = s.startLineNumber - 1; + movingLineText = model.getLineContent(movingLineNumber); + + // Delete line that needs to be moved + builder.addEditOperation(new Range(movingLineNumber, 1, movingLineNumber + 1, 1), null); + + // Insert line that needs to be moved after + builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + movingLineText); + + if (this.shouldAutoIndent(model, s)) { + virtualModel.getLineContent = (lineNumber: number) => { + if (lineNumber === movingLineNumber) { + return model.getLineContent(s.startLineNumber); + } else { + return model.getLineContent(lineNumber); + } + }; + + let ret = this.matchEnterRule(model, indentConverter, tabSize, s.startLineNumber, s.startLineNumber - 2); + // check if s.startLineNumber - 2 matches onEnter rules, if so adjust the moving block by onEnter rules. + if (ret !== null) { + if (ret !== 0) { + this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, ret); + } + } else { + // it doesn't match any onEnter rule, let's check indentation rules then. + let indentOfFirstLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(s.startLineNumber, 1), movingLineNumber, indentConverter); + if (indentOfFirstLine !== null) { + // adjust the indentation of the moving block + let oldIndent = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber)); + let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfFirstLine, tabSize); + let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndent, tabSize); + if (newSpaceCnt !== oldSpaceCnt) { + let spaceCntOffset = newSpaceCnt - oldSpaceCnt; + + this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, spaceCntOffset); + } + } + } + } + } + } + + this._selectionId = builder.trackSelection(s); + } + + private buildIndentConverter(tabSize: number): IIndentConverter { + return { + shiftIndent: (indentation) => { + let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + 1, tabSize); + let newIndentation = ''; + for (let i = 0; i < desiredIndentCount; i++) { + newIndentation += '\t'; + } + + return newIndentation; + }, + unshiftIndent: (indentation) => { + let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + 1, tabSize); + let newIndentation = ''; + for (let i = 0; i < desiredIndentCount; i++) { + newIndentation += '\t'; + } + + return newIndentation; + } + }; + } + + private matchEnterRule(model: ITokenizedModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, oneLineAboveText?: string) { + let validPrecedingLine = oneLineAbove; + while (validPrecedingLine >= 1) { + // ship empty lines as empty lines just inherit indentation + let lineContent; + if (validPrecedingLine === oneLineAbove && oneLineAboveText !== undefined) { + lineContent = oneLineAboveText; + } else { + lineContent = model.getLineContent(validPrecedingLine); + } + + let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineContent); + if (nonWhitespaceIdx >= 0) { + break; + } + validPrecedingLine--; + } + + if (validPrecedingLine < 1 || line > model.getLineCount()) { + return null; + } + + let maxColumn = model.getLineMaxColumn(validPrecedingLine); + let enter = LanguageConfigurationRegistry.getEnterAction(model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); + + if (enter) { + let enterPrefix = enter.indentation; + let enterAction = enter.enterAction; + + if (enterAction.indentAction === IndentAction.None) { + enterPrefix = enter.indentation + enterAction.appendText; + } else if (enterAction.indentAction === IndentAction.Indent) { + enterPrefix = enter.indentation + enterAction.appendText; + } else if (enterAction.indentAction === IndentAction.IndentOutdent) { + enterPrefix = enter.indentation; + } else if (enterAction.indentAction === IndentAction.Outdent) { + enterPrefix = indentConverter.unshiftIndent(enter.indentation) + enterAction.appendText; + } + let movingLineText = model.getLineContent(line); + if (this.trimLeft(movingLineText).indexOf(this.trimLeft(enterPrefix)) >= 0) { + let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(line)); + let newIndentation = strings.getLeadingWhitespace(enterPrefix); + let indentMetadataOfMovelingLine = LanguageConfigurationRegistry.getIndentMetadata(model, line); + if (indentMetadataOfMovelingLine & IndentConsts.DECREASE_MASK) { + newIndentation = indentConverter.unshiftIndent(newIndentation); + } + let newSpaceCnt = IndentUtil.getSpaceCnt(newIndentation, tabSize); + let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize); + return newSpaceCnt - oldSpaceCnt; + } + } + + return null; + } + + private trimLeft(str: string) { + return str.replace(/^\s+/, ''); + } + + private shouldAutoIndent(model: ITokenizedModel, selection: Selection) { + if (!this._autoIndent) { + return false; + } + // if it's not easy to tokenize, we stop auto indent. + if (!model.isCheapToTokenize(selection.startLineNumber)) { + return false; + } + let languageAtSelectionStart = model.getLanguageIdAtPosition(selection.startLineNumber, 1); + let languageAtSelectionEnd = model.getLanguageIdAtPosition(selection.endLineNumber, 1); + + if (languageAtSelectionStart !== languageAtSelectionEnd) { + return false; + } + + if (LanguageConfigurationRegistry.getIndentRulesSupport(languageAtSelectionStart) === null) { + return false; + } + + return true; + } + + private getIndentEditsOfMovingBlock(model: ITokenizedModel, builder: IEditOperationBuilder, s: Selection, tabSize: number, insertSpaces: boolean, offset: number) { + for (let i = s.startLineNumber; i <= s.endLineNumber; i++) { + let lineContent = model.getLineContent(i); + let originalIndent = strings.getLeadingWhitespace(lineContent); + let originalSpacesCnt = IndentUtil.getSpaceCnt(originalIndent, tabSize); + let newSpacesCnt = originalSpacesCnt + offset; + let newIndent = IndentUtil.generateIndent(newSpacesCnt, tabSize, insertSpaces); + + if (newIndent !== originalIndent) { + builder.addEditOperation(new Range(i, 1, i, originalIndent.length + 1), newIndent); + + if (i === s.endLineNumber && s.endColumn <= originalIndent.length + 1 && newIndent === '') { + // as users select part of the original indent white spaces + // when we adjust the indentation of endLine, we should adjust the cursor position as well. + this._moveEndLineSelectionShrink = true; + } + } + + } + } + + public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { + var result = helper.getTrackedSelection(this._selectionId); + + if (this._moveEndPositionDown) { + result = result.setEndPosition(result.endLineNumber + 1, 1); + } + + if (this._moveEndLineSelectionShrink && result.startLineNumber < result.endLineNumber) { + result = result.setEndPosition(result.endLineNumber, 2); + } + + return result; + } +} diff --git a/src/vs/editor/contrib/linesOperations/common/sortLinesCommand.ts b/src/vs/editor/contrib/linesOperations/common/sortLinesCommand.ts new file mode 100644 index 0000000000..9efcab5349 --- /dev/null +++ b/src/vs/editor/contrib/linesOperations/common/sortLinesCommand.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; + +export class SortLinesCommand implements editorCommon.ICommand { + + private selection: Selection; + private selectionId: string; + private descending: boolean; + + constructor(selection: Selection, descending: boolean) { + this.selection = selection; + this.descending = descending; + } + + public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { + let op = sortLines(model, this.selection, this.descending); + if (op) { + builder.addEditOperation(op.range, op.text); + } + + this.selectionId = builder.trackSelection(this.selection); + } + + public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { + return helper.getTrackedSelection(this.selectionId); + } + + public static canRun(model: editorCommon.ITextModel, selection: Selection, descending: boolean): boolean { + let data = getSortData(model, selection, descending); + + if (!data) { + return false; + } + + for (let i = 0, len = data.before.length; i < len; i++) { + if (data.before[i] !== data.after[i]) { + return true; + } + } + + return false; + } +} + +function getSortData(model: editorCommon.ITextModel, selection: Selection, descending: boolean) { + let startLineNumber = selection.startLineNumber; + let endLineNumber = selection.endLineNumber; + + if (selection.endColumn === 1) { + endLineNumber--; + } + + // Nothing to sort if user didn't select anything. + if (startLineNumber >= endLineNumber) { + return null; + } + + let linesToSort = []; + + // Get the contents of the selection to be sorted. + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + linesToSort.push(model.getLineContent(lineNumber)); + } + + let sorted = linesToSort.slice(0); + sorted.sort((a, b) => { + return a.toLowerCase().localeCompare(b.toLowerCase()); + }); + + // If descending, reverse the order. + if (descending === true) { + sorted = sorted.reverse(); + } + + return { + startLineNumber: startLineNumber, + endLineNumber: endLineNumber, + before: linesToSort, + after: sorted + }; +} + +/** + * Generate commands for sorting lines on a model. + */ +function sortLines(model: editorCommon.ITextModel, selection: Selection, descending: boolean): editorCommon.IIdentifiedSingleEditOperation { + let data = getSortData(model, selection, descending); + + if (!data) { + return null; + } + + return EditOperation.replace( + new Range(data.startLineNumber, 1, data.endLineNumber, model.getLineMaxColumn(data.endLineNumber)), + data.after.join('\n') + ); +} diff --git a/src/vs/editor/contrib/linesOperations/test/common/copyLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/common/copyLinesCommand.test.ts new file mode 100644 index 0000000000..bf63bc51ba --- /dev/null +++ b/src/vs/editor/contrib/linesOperations/test/common/copyLinesCommand.test.ts @@ -0,0 +1,200 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Selection } from 'vs/editor/common/core/selection'; +import { CopyLinesCommand } from 'vs/editor/contrib/linesOperations/common/copyLinesCommand'; +import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; + +function testCopyLinesDownCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new CopyLinesCommand(sel, true), expectedLines, expectedSelection); +} + +function testCopyLinesUpCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new CopyLinesCommand(sel, false), expectedLines, expectedSelection); +} + +suite('Editor Contrib - Copy Lines Command', () => { + + test('copy first line down', function () { + testCopyLinesDownCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 1, 1), + [ + 'first', + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 3, 2, 1) + ); + }); + + test('copy first line up', function () { + testCopyLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 1, 1), + [ + 'first', + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 1, 1) + ); + }); + + test('copy last line down', function () { + testCopyLinesDownCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(5, 3, 5, 1), + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth', + 'fifth' + ], + new Selection(6, 3, 6, 1) + ); + }); + + test('copy last line up', function () { + testCopyLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(5, 3, 5, 1), + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth', + 'fifth' + ], + new Selection(5, 3, 5, 1) + ); + }); + + test('issue #1322: copy line up', function () { + testCopyLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(3, 11, 3, 11), + [ + 'first', + 'second line', + 'third line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(3, 11, 3, 11) + ); + }); + + test('issue #1322: copy last line up', function () { + testCopyLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(5, 6, 5, 6), + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth', + 'fifth' + ], + new Selection(5, 6, 5, 6) + ); + }); + + test('copy many lines up', function () { + testCopyLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(4, 3, 2, 1), + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(4, 3, 2, 1) + ); + }); + + test('ignore empty selection', function () { + testCopyLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 1, 1, 1), + [ + 'first', + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 1, 1, 1) + ); + }); +}); + + diff --git a/src/vs/editor/contrib/linesOperations/test/common/deleteLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/common/deleteLinesCommand.test.ts new file mode 100644 index 0000000000..e92802f387 --- /dev/null +++ b/src/vs/editor/contrib/linesOperations/test/common/deleteLinesCommand.test.ts @@ -0,0 +1,194 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Selection } from 'vs/editor/common/core/selection'; +import { DeleteLinesCommand } from 'vs/editor/contrib/linesOperations/common/deleteLinesCommand'; +import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; + +function testDeleteLinesCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => DeleteLinesCommand.createFromSelection(sel), expectedLines, expectedSelection); +} + +suite('Editor Contrib - Delete Lines Command', () => { + + test('empty selection in middle of lines', function () { + testDeleteLinesCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 3, 2, 3), + [ + 'first', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 3, 2, 3) + ); + }); + + test('empty selection at top of lines', function () { + testDeleteLinesCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 5, 1, 5), + [ + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 5, 1, 5) + ); + }); + + test('empty selection at end of lines', function () { + testDeleteLinesCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(5, 2, 5, 2), + [ + 'first', + 'second line', + 'third line', + 'fourth line' + ], + new Selection(4, 2, 4, 2) + ); + }); + + test('with selection in middle of lines', function () { + testDeleteLinesCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(3, 3, 2, 2), + [ + 'first', + 'fourth line', + 'fifth' + ], + new Selection(2, 2, 2, 2) + ); + }); + + test('with selection at top of lines', function () { + testDeleteLinesCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 4, 1, 5), + [ + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 5, 1, 5) + ); + }); + + test('with selection at end of lines', function () { + testDeleteLinesCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(5, 1, 5, 2), + [ + 'first', + 'second line', + 'third line', + 'fourth line' + ], + new Selection(4, 2, 4, 2) + ); + }); + + test('with full line selection in middle of lines', function () { + testDeleteLinesCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(4, 1, 2, 1), + [ + 'first', + 'fourth line', + 'fifth' + ], + new Selection(2, 1, 2, 1) + ); + }); + + test('with full line selection at top of lines', function () { + testDeleteLinesCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 1, 1, 5), + [ + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 5, 1, 5) + ); + }); + + test('with full line selection at end of lines', function () { + testDeleteLinesCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(4, 1, 5, 2), + [ + 'first', + 'second line', + 'third line' + ], + new Selection(3, 2, 3, 2) + ); + }); +}); + diff --git a/src/vs/editor/contrib/linesOperations/test/common/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/common/linesOperations.test.ts new file mode 100644 index 0000000000..63f65d693c --- /dev/null +++ b/src/vs/editor/contrib/linesOperations/test/common/linesOperations.test.ts @@ -0,0 +1,607 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; +import { Handler, IModel, DefaultEndOfLine } from 'vs/editor/common/editorCommon'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { DeleteAllLeftAction, JoinLinesAction, TransposeAction, UpperCaseAction, LowerCaseAction, DeleteAllRightAction, InsertLineBeforeAction, InsertLineAfterAction, IndentLinesAction } from 'vs/editor/contrib/linesOperations/common/linesOperations'; +import { Cursor } from 'vs/editor/common/controller/cursor'; +import { Model } from 'vs/editor/common/model/model'; +import { CoreEditingCommands } from 'vs/editor/common/controller/coreCommands'; + +suite('Editor Contrib - Line Operations', () => { + suite('DeleteAllLeftAction', () => { + test('should delete to the left of the cursor', function () { + withMockCodeEditor( + [ + 'one', + 'two', + 'three' + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let deleteAllLeftAction = new DeleteAllLeftAction(); + + editor.setSelection(new Selection(1, 2, 1, 2)); + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLineContent(1), 'ne', '001'); + + editor.setSelections([new Selection(2, 2, 2, 2), new Selection(3, 2, 3, 2)]); + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLineContent(2), 'wo', '002'); + assert.equal(model.getLineContent(3), 'hree', '003'); + }); + }); + + test('should work in multi cursor mode', function () { + withMockCodeEditor( + [ + 'hello', + 'world', + 'hello world', + 'hello', + 'bonjour', + 'hola', + 'world', + 'hello world', + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let deleteAllLeftAction = new DeleteAllLeftAction(); + + editor.setSelections([new Selection(1, 2, 1, 2), new Selection(1, 4, 1, 4)]); + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLineContent(1), 'lo', '001'); + + editor.setSelections([new Selection(2, 2, 2, 2), new Selection(2, 4, 2, 5)]); + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLineContent(2), 'ord', '002'); + + editor.setSelections([new Selection(3, 2, 3, 5), new Selection(3, 7, 3, 7)]); + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLineContent(3), 'world', '003'); + + editor.setSelections([new Selection(4, 3, 4, 3), new Selection(4, 5, 5, 4)]); + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLineContent(4), 'lljour', '004'); + + editor.setSelections([new Selection(5, 3, 6, 3), new Selection(6, 5, 7, 5), new Selection(7, 7, 7, 7)]); + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLineContent(5), 'horlworld', '005'); + }); + }); + }); + + suite('JoinLinesAction', () => { + test('should join lines and insert space if necessary', function () { + withMockCodeEditor( + [ + 'hello', + 'world', + 'hello ', + 'world', + 'hello ', + ' world', + 'hello ', + ' world', + '', + '', + 'hello world' + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let joinLinesAction = new JoinLinesAction(); + + editor.setSelection(new Selection(1, 2, 1, 2)); + joinLinesAction.run(null, editor); + assert.equal(model.getLineContent(1), 'hello world', '001'); + assert.deepEqual(editor.getSelection().toString(), new Selection(1, 6, 1, 6).toString(), '002'); + + editor.setSelection(new Selection(2, 2, 2, 2)); + joinLinesAction.run(null, editor); + assert.equal(model.getLineContent(2), 'hello world', '003'); + assert.deepEqual(editor.getSelection().toString(), new Selection(2, 7, 2, 7).toString(), '004'); + + editor.setSelection(new Selection(3, 2, 3, 2)); + joinLinesAction.run(null, editor); + assert.equal(model.getLineContent(3), 'hello world', '005'); + assert.deepEqual(editor.getSelection().toString(), new Selection(3, 7, 3, 7).toString(), '006'); + + editor.setSelection(new Selection(4, 2, 5, 3)); + joinLinesAction.run(null, editor); + assert.equal(model.getLineContent(4), 'hello world', '007'); + assert.deepEqual(editor.getSelection().toString(), new Selection(4, 2, 4, 8).toString(), '008'); + + editor.setSelection(new Selection(5, 1, 7, 3)); + joinLinesAction.run(null, editor); + assert.equal(model.getLineContent(5), 'hello world', '009'); + assert.deepEqual(editor.getSelection().toString(), new Selection(5, 1, 5, 3).toString(), '010'); + }); + }); + + test('should work in multi cursor mode', function () { + withMockCodeEditor( + [ + 'hello', + 'world', + 'hello ', + 'world', + 'hello ', + ' world', + 'hello ', + ' world', + '', + '', + 'hello world' + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let joinLinesAction = new JoinLinesAction(); + + editor.setSelections([ + /** primary cursor */ + new Selection(5, 2, 5, 2), + new Selection(1, 2, 1, 2), + new Selection(3, 2, 4, 2), + new Selection(5, 4, 6, 3), + new Selection(7, 5, 8, 4), + new Selection(10, 1, 10, 1) + ]); + + joinLinesAction.run(null, editor); + assert.equal(model.getLinesContent().join('\n'), 'hello world\nhello world\nhello world\nhello world\n\nhello world', '001'); + assert.deepEqual(editor.getSelections().toString(), [ + /** primary cursor */ + new Selection(3, 4, 3, 8), + new Selection(1, 6, 1, 6), + new Selection(2, 2, 2, 8), + new Selection(4, 5, 4, 9), + new Selection(6, 1, 6, 1) + ].toString(), '002'); + + /** primary cursor */ + assert.deepEqual(editor.getSelection().toString(), new Selection(3, 4, 3, 8).toString(), '003'); + }); + }); + }); + + test('transpose', function () { + withMockCodeEditor( + [ + 'hello world', + '', + '', + ' ', + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let transposeAction = new TransposeAction(); + + editor.setSelection(new Selection(1, 1, 1, 1)); + transposeAction.run(null, editor); + assert.equal(model.getLineContent(1), 'hello world', '001'); + assert.deepEqual(editor.getSelection().toString(), new Selection(1, 2, 1, 2).toString(), '002'); + + editor.setSelection(new Selection(1, 6, 1, 6)); + transposeAction.run(null, editor); + assert.equal(model.getLineContent(1), 'hell oworld', '003'); + assert.deepEqual(editor.getSelection().toString(), new Selection(1, 7, 1, 7).toString(), '004'); + + editor.setSelection(new Selection(1, 12, 1, 12)); + transposeAction.run(null, editor); + assert.equal(model.getLineContent(1), 'hell oworl', '005'); + assert.deepEqual(editor.getSelection().toString(), new Selection(2, 2, 2, 2).toString(), '006'); + + editor.setSelection(new Selection(3, 1, 3, 1)); + transposeAction.run(null, editor); + assert.equal(model.getLineContent(3), '', '007'); + assert.deepEqual(editor.getSelection().toString(), new Selection(4, 1, 4, 1).toString(), '008'); + + editor.setSelection(new Selection(4, 2, 4, 2)); + transposeAction.run(null, editor); + assert.equal(model.getLineContent(4), ' ', '009'); + assert.deepEqual(editor.getSelection().toString(), new Selection(4, 3, 4, 3).toString(), '010'); + } + ); + + // fix #16633 + withMockCodeEditor( + [ + '', + '', + 'hello', + 'world', + '', + 'hello world', + '', + 'hello world' + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let transposeAction = new TransposeAction(); + + editor.setSelection(new Selection(1, 1, 1, 1)); + transposeAction.run(null, editor); + assert.equal(model.getLineContent(2), '', '011'); + assert.deepEqual(editor.getSelection().toString(), new Selection(2, 1, 2, 1).toString(), '012'); + + editor.setSelection(new Selection(3, 6, 3, 6)); + transposeAction.run(null, editor); + assert.equal(model.getLineContent(4), 'oworld', '013'); + assert.deepEqual(editor.getSelection().toString(), new Selection(4, 2, 4, 2).toString(), '014'); + + editor.setSelection(new Selection(6, 12, 6, 12)); + transposeAction.run(null, editor); + assert.equal(model.getLineContent(7), 'd', '015'); + assert.deepEqual(editor.getSelection().toString(), new Selection(7, 2, 7, 2).toString(), '016'); + + editor.setSelection(new Selection(8, 12, 8, 12)); + transposeAction.run(null, editor); + assert.equal(model.getLineContent(8), 'hello world', '019'); + assert.deepEqual(editor.getSelection().toString(), new Selection(8, 12, 8, 12).toString(), '020'); + } + ); + }); + + test('toggle case', function () { + withMockCodeEditor( + [ + 'hello world', + 'öçşğü' + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let uppercaseAction = new UpperCaseAction(); + let lowercaseAction = new LowerCaseAction(); + + editor.setSelection(new Selection(1, 1, 1, 12)); + uppercaseAction.run(null, editor); + assert.equal(model.getLineContent(1), 'HELLO WORLD', '001'); + assert.deepEqual(editor.getSelection().toString(), new Selection(1, 1, 1, 12).toString(), '002'); + + editor.setSelection(new Selection(1, 1, 1, 12)); + lowercaseAction.run(null, editor); + assert.equal(model.getLineContent(1), 'hello world', '003'); + assert.deepEqual(editor.getSelection().toString(), new Selection(1, 1, 1, 12).toString(), '004'); + + editor.setSelection(new Selection(1, 3, 1, 3)); + uppercaseAction.run(null, editor); + assert.equal(model.getLineContent(1), 'HELLO world', '005'); + assert.deepEqual(editor.getSelection().toString(), new Selection(1, 3, 1, 3).toString(), '006'); + + editor.setSelection(new Selection(1, 4, 1, 4)); + lowercaseAction.run(null, editor); + assert.equal(model.getLineContent(1), 'hello world', '007'); + assert.deepEqual(editor.getSelection().toString(), new Selection(1, 4, 1, 4).toString(), '008'); + + editor.setSelection(new Selection(2, 1, 2, 6)); + uppercaseAction.run(null, editor); + assert.equal(model.getLineContent(2), 'ÖÇŞĞÜ', '009'); + assert.deepEqual(editor.getSelection().toString(), new Selection(2, 1, 2, 6).toString(), '010'); + + editor.setSelection(new Selection(2, 1, 2, 6)); + lowercaseAction.run(null, editor); + assert.equal(model.getLineContent(2), 'öçşğü', '011'); + assert.deepEqual(editor.getSelection().toString(), new Selection(2, 1, 2, 6).toString(), '012'); + } + ); + + withMockCodeEditor( + [ + '', + ' ' + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let uppercaseAction = new UpperCaseAction(); + let lowercaseAction = new LowerCaseAction(); + + editor.setSelection(new Selection(1, 1, 1, 1)); + uppercaseAction.run(null, editor); + assert.equal(model.getLineContent(1), '', '013'); + assert.deepEqual(editor.getSelection().toString(), new Selection(1, 1, 1, 1).toString(), '014'); + + editor.setSelection(new Selection(1, 1, 1, 1)); + lowercaseAction.run(null, editor); + assert.equal(model.getLineContent(1), '', '015'); + assert.deepEqual(editor.getSelection().toString(), new Selection(1, 1, 1, 1).toString(), '016'); + + editor.setSelection(new Selection(2, 2, 2, 2)); + uppercaseAction.run(null, editor); + assert.equal(model.getLineContent(2), ' ', '017'); + assert.deepEqual(editor.getSelection().toString(), new Selection(2, 2, 2, 2).toString(), '018'); + + editor.setSelection(new Selection(2, 2, 2, 2)); + lowercaseAction.run(null, editor); + assert.equal(model.getLineContent(2), ' ', '019'); + assert.deepEqual(editor.getSelection().toString(), new Selection(2, 2, 2, 2).toString(), '020'); + } + ); + }); + + suite('DeleteAllRightAction', () => { + test('should be noop on empty', () => { + withMockCodeEditor([''], {}, (editor, cursor) => { + const model = editor.getModel(); + const action = new DeleteAllRightAction(); + + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['']); + assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); + + editor.setSelection(new Selection(1, 1, 1, 1)); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['']); + assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); + + editor.setSelections([new Selection(1, 1, 1, 1), new Selection(1, 1, 1, 1), new Selection(1, 1, 1, 1)]); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['']); + assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); + }); + }); + + test('should delete selected range', () => { + withMockCodeEditor([ + 'hello', + 'world' + ], {}, (editor, cursor) => { + const model = editor.getModel(); + const action = new DeleteAllRightAction(); + + editor.setSelection(new Selection(1, 2, 1, 5)); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['ho', 'world']); + assert.deepEqual(editor.getSelections(), [new Selection(1, 2, 1, 2)]); + + editor.setSelection(new Selection(1, 1, 2, 4)); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['ld']); + assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); + + editor.setSelection(new Selection(1, 1, 1, 3)); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['']); + assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); + }); + }); + + test('should delete to the right of the cursor', () => { + withMockCodeEditor([ + 'hello', + 'world' + ], {}, (editor, cursor) => { + const model = editor.getModel(); + const action = new DeleteAllRightAction(); + + editor.setSelection(new Selection(1, 3, 1, 3)); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['he', 'world']); + assert.deepEqual(editor.getSelections(), [new Selection(1, 3, 1, 3)]); + + editor.setSelection(new Selection(2, 1, 2, 1)); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['he', '']); + assert.deepEqual(editor.getSelections(), [new Selection(2, 1, 2, 1)]); + }); + }); + + test('should join two lines, if at the end of the line', () => { + withMockCodeEditor([ + 'hello', + 'world' + ], {}, (editor, cursor) => { + const model = editor.getModel(); + const action = new DeleteAllRightAction(); + + editor.setSelection(new Selection(1, 6, 1, 6)); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['helloworld']); + assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]); + + editor.setSelection(new Selection(1, 6, 1, 6)); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['hello']); + assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]); + + editor.setSelection(new Selection(1, 6, 1, 6)); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['hello']); + assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]); + }); + }); + + test('should work with multiple cursors', () => { + withMockCodeEditor([ + 'hello', + 'there', + 'world' + ], {}, (editor, cursor) => { + const model = editor.getModel(); + const action = new DeleteAllRightAction(); + + editor.setSelections([ + new Selection(1, 3, 1, 3), + new Selection(1, 6, 1, 6), + new Selection(3, 4, 3, 4), + ]); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['hethere', 'wor']); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 3, 1, 3), + new Selection(2, 4, 2, 4) + ]); + + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['he', 'wor']); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 3, 1, 3), + new Selection(2, 4, 2, 4) + ]); + + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['hewor']); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 3, 1, 3), + new Selection(1, 6, 1, 6) + ]); + + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['he']); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 3, 1, 3) + ]); + + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['he']); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 3, 1, 3) + ]); + }); + }); + + test('should work with undo/redo', () => { + withMockCodeEditor([ + 'hello', + 'there', + 'world' + ], {}, (editor, cursor) => { + const model = editor.getModel(); + const action = new DeleteAllRightAction(); + + editor.setSelections([ + new Selection(1, 3, 1, 3), + new Selection(1, 6, 1, 6), + new Selection(3, 4, 3, 4), + ]); + action.run(null, editor); + assert.deepEqual(model.getLinesContent(), ['hethere', 'wor']); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 3, 1, 3), + new Selection(2, 4, 2, 4) + ]); + + editor.trigger('tests', Handler.Undo, {}); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 3, 1, 3), + new Selection(1, 6, 1, 6), + new Selection(3, 4, 3, 4) + ]); + editor.trigger('tests', Handler.Redo, {}); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 3, 1, 3), + new Selection(2, 4, 2, 4) + ]); + }); + }); + }); + + test('InsertLineBeforeAction', function () { + function testInsertLineBefore(lineNumber: number, column: number, callback: (model: IModel, cursor: Cursor) => void): void { + const TEXT = [ + 'First line', + 'Second line', + 'Third line' + ]; + withMockCodeEditor(TEXT, {}, (editor, cursor) => { + editor.setPosition(new Position(lineNumber, column)); + let insertLineBeforeAction = new InsertLineBeforeAction(); + + insertLineBeforeAction.run(null, editor); + callback(editor.getModel(), cursor); + }); + } + + testInsertLineBefore(1, 3, (model, cursor) => { + assert.deepEqual(cursor.getSelection(), new Selection(1, 1, 1, 1)); + assert.equal(model.getLineContent(1), ''); + assert.equal(model.getLineContent(2), 'First line'); + assert.equal(model.getLineContent(3), 'Second line'); + assert.equal(model.getLineContent(4), 'Third line'); + }); + + testInsertLineBefore(2, 3, (model, cursor) => { + assert.deepEqual(cursor.getSelection(), new Selection(2, 1, 2, 1)); + assert.equal(model.getLineContent(1), 'First line'); + assert.equal(model.getLineContent(2), ''); + assert.equal(model.getLineContent(3), 'Second line'); + assert.equal(model.getLineContent(4), 'Third line'); + }); + + testInsertLineBefore(3, 3, (model, cursor) => { + assert.deepEqual(cursor.getSelection(), new Selection(3, 1, 3, 1)); + assert.equal(model.getLineContent(1), 'First line'); + assert.equal(model.getLineContent(2), 'Second line'); + assert.equal(model.getLineContent(3), ''); + assert.equal(model.getLineContent(4), 'Third line'); + }); + }); + + test('InsertLineAfterAction', () => { + function testInsertLineAfter(lineNumber: number, column: number, callback: (model: IModel, cursor: Cursor) => void): void { + const TEXT = [ + 'First line', + 'Second line', + 'Third line' + ]; + withMockCodeEditor(TEXT, {}, (editor, cursor) => { + editor.setPosition(new Position(lineNumber, column)); + let insertLineAfterAction = new InsertLineAfterAction(); + + insertLineAfterAction.run(null, editor); + callback(editor.getModel(), cursor); + }); + } + + testInsertLineAfter(1, 3, (model, cursor) => { + assert.deepEqual(cursor.getSelection(), new Selection(2, 1, 2, 1)); + assert.equal(model.getLineContent(1), 'First line'); + assert.equal(model.getLineContent(2), ''); + assert.equal(model.getLineContent(3), 'Second line'); + assert.equal(model.getLineContent(4), 'Third line'); + }); + + testInsertLineAfter(2, 3, (model, cursor) => { + assert.deepEqual(cursor.getSelection(), new Selection(3, 1, 3, 1)); + assert.equal(model.getLineContent(1), 'First line'); + assert.equal(model.getLineContent(2), 'Second line'); + assert.equal(model.getLineContent(3), ''); + assert.equal(model.getLineContent(4), 'Third line'); + }); + + testInsertLineAfter(3, 3, (model, cursor) => { + assert.deepEqual(cursor.getSelection(), new Selection(4, 1, 4, 1)); + assert.equal(model.getLineContent(1), 'First line'); + assert.equal(model.getLineContent(2), 'Second line'); + assert.equal(model.getLineContent(3), 'Third line'); + assert.equal(model.getLineContent(4), ''); + }); + }); + + test('Bug 18276:[editor] Indentation broken when selection is empty', () => { + + let model = Model.createFromString( + [ + 'function baz() {' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + let indentLinesAction = new IndentLinesAction(); + editor.setPosition(new Position(1, 2)); + + indentLinesAction.run(null, editor); + assert.equal(model.getLineContent(1), '\tfunction baz() {'); + assert.deepEqual(editor.getSelection(), new Selection(1, 3, 1, 3)); + + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), '\tf\tunction baz() {'); + }); + + model.dispose(); + }); +}); diff --git a/src/vs/editor/contrib/linesOperations/test/common/moveLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/common/moveLinesCommand.test.ts new file mode 100644 index 0000000000..d30a97a00d --- /dev/null +++ b/src/vs/editor/contrib/linesOperations/test/common/moveLinesCommand.test.ts @@ -0,0 +1,353 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +import { Selection } from 'vs/editor/common/core/selection'; +import { MoveLinesCommand } from 'vs/editor/contrib/linesOperations/common/moveLinesCommand'; +import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { IndentationRule } from 'vs/editor/common/modes/languageConfiguration'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; + +function testMoveLinesDownCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new MoveLinesCommand(sel, true, false), expectedLines, expectedSelection); +} + +function testMoveLinesUpCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new MoveLinesCommand(sel, false, false), expectedLines, expectedSelection); +} + +function testMoveLinesDownWithIndentCommand(languageId: LanguageIdentifier, lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, languageId, selection, (sel) => new MoveLinesCommand(sel, true, true), expectedLines, expectedSelection); +} + +function testMoveLinesUpWithIndentCommand(languageId: LanguageIdentifier, lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, languageId, selection, (sel) => new MoveLinesCommand(sel, false, true), expectedLines, expectedSelection); +} + +suite('Editor Contrib - Move Lines Command', () => { + + test('move first up / last down disabled', function () { + testMoveLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 1), + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 1) + ); + + testMoveLinesDownCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(5, 1, 5, 1), + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(5, 1, 5, 1) + ); + }); + + test('move first line down', function () { + testMoveLinesDownCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 4, 1, 1), + [ + 'second line', + 'first', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 4, 2, 1) + ); + }); + + test('move 2nd line up', function () { + testMoveLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 1, 2, 1), + [ + 'second line', + 'first', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 1, 1) + ); + }); + + test('issue #1322a: move 2nd line up', function () { + testMoveLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 12, 2, 12), + [ + 'second line', + 'first', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 12, 1, 12) + ); + }); + + test('issue #1322b: move last line up', function () { + testMoveLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(5, 6, 5, 6), + [ + 'first', + 'second line', + 'third line', + 'fifth', + 'fourth line' + ], + new Selection(4, 6, 4, 6) + ); + }); + + test('issue #1322c: move last line selected up', function () { + testMoveLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(5, 6, 5, 1), + [ + 'first', + 'second line', + 'third line', + 'fifth', + 'fourth line' + ], + new Selection(4, 6, 4, 1) + ); + }); + + test('move last line up', function () { + testMoveLinesUpCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(5, 1, 5, 1), + [ + 'first', + 'second line', + 'third line', + 'fifth', + 'fourth line' + ], + new Selection(4, 1, 4, 1) + ); + }); + + test('move 4th line down', function () { + testMoveLinesDownCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(4, 1, 4, 1), + [ + 'first', + 'second line', + 'third line', + 'fifth', + 'fourth line' + ], + new Selection(5, 1, 5, 1) + ); + }); + + test('move multiple lines down', function () { + testMoveLinesDownCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(4, 4, 2, 2), + [ + 'first', + 'fifth', + 'second line', + 'third line', + 'fourth line' + ], + new Selection(5, 4, 3, 2) + ); + }); + + test('invisible selection is ignored', function () { + testMoveLinesDownCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(2, 1, 1, 1), + [ + 'second line', + 'first', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(3, 1, 2, 1) + ); + }); +}); + +class IndentRulesMode extends MockMode { + private static _id = new LanguageIdentifier('moveLinesIndentMode', 7); + constructor(indentationRules: IndentationRule) { + super(IndentRulesMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + indentationRules: indentationRules + })); + } +} + +suite('Editor contrib - Move Lines Command honors Indentation Rules', () => { + let indentRules = { + decreaseIndentPattern: /^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$/, + increaseIndentPattern: /(\{[^}"'`]*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$/, + indentNextLinePattern: /^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$)/, + unIndentedLinePattern: /^(?!.*([;{}]|\S:)\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!.*(\{[^}"']*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$))/ + }; + + // https://github.com/Microsoft/vscode/issues/28552#issuecomment-307862797 + test('first line indentation adjust to 0', () => { + let mode = new IndentRulesMode(indentRules); + + testMoveLinesUpWithIndentCommand( + mode.getLanguageIdentifier(), + [ + 'class X {', + '\tz = 2', + '}' + ], + new Selection(2, 1, 2, 1), + [ + 'z = 2', + 'class X {', + '}' + ], + new Selection(1, 1, 1, 1) + ); + + mode.dispose(); + }); + + // https://github.com/Microsoft/vscode/issues/28552#issuecomment-307867717 + test('move lines across block', () => { + let mode = new IndentRulesMode(indentRules); + + testMoveLinesDownWithIndentCommand( + mode.getLanguageIdentifier(), + [ + 'const value = 2;', + 'const standardLanguageDescriptions = [', + ' {', + ' diagnosticSource: \'js\',', + ' }', + '];' + ], + new Selection(1, 1, 1, 1), + [ + 'const standardLanguageDescriptions = [', + ' const value = 2;', + ' {', + ' diagnosticSource: \'js\',', + ' }', + '];' + ], + new Selection(2, 5, 2, 5) + ); + + mode.dispose(); + }); + + test('move line should still work as before if there is no indentation rules', () => { + testMoveLinesUpWithIndentCommand( + null, + [ + 'if (true) {', + ' var task = new Task(() => {', + ' var work = 1234;', + ' });', + '}' + ], + new Selection(3, 1, 3, 1), + [ + 'if (true) {', + ' var work = 1234;', + ' var task = new Task(() => {', + ' });', + '}' + ], + new Selection(2, 1, 2, 1) + ); + }); +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/linesOperations/test/common/sortLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/common/sortLinesCommand.test.ts new file mode 100644 index 0000000000..28b44a3658 --- /dev/null +++ b/src/vs/editor/contrib/linesOperations/test/common/sortLinesCommand.test.ts @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Selection } from 'vs/editor/common/core/selection'; +import { SortLinesCommand } from 'vs/editor/contrib/linesOperations/common/sortLinesCommand'; +import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; + +function testSortLinesAscendingCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new SortLinesCommand(sel, false), expectedLines, expectedSelection); +} + +function testSortLinesDescendingCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, null, selection, (sel) => new SortLinesCommand(sel, true), expectedLines, expectedSelection); +} + +suite('Editor Contrib - Sort Lines Command', () => { + + test('no op unless at least two lines selected 1', function () { + testSortLinesAscendingCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 1, 1), + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 1, 1) + ); + }); + + test('no op unless at least two lines selected 2', function () { + testSortLinesAscendingCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 2, 1), + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 3, 2, 1) + ); + }); + + test('sorting two lines ascending', function () { + testSortLinesAscendingCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(3, 3, 4, 2), + [ + 'first', + 'second line', + 'fourth line', + 'third line', + 'fifth' + ], + new Selection(3, 3, 4, 2) + ); + }); + + test('sorting first 4 lines ascending', function () { + testSortLinesAscendingCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 5, 1), + [ + 'first', + 'fourth line', + 'second line', + 'third line', + 'fifth' + ], + new Selection(1, 1, 5, 1) + ); + }); + + test('sorting all lines ascending', function () { + testSortLinesAscendingCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 5, 6), + [ + 'fifth', + 'first', + 'fourth line', + 'second line', + 'third line', + ], + new Selection(1, 1, 5, 6) + ); + }); + + test('sorting first 4 lines desscending', function () { + testSortLinesDescendingCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 5, 1), + [ + 'third line', + 'second line', + 'fourth line', + 'first', + 'fifth' + ], + new Selection(1, 1, 5, 1) + ); + }); + + test('sorting all lines descending', function () { + testSortLinesDescendingCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth line', + 'fifth' + ], + new Selection(1, 1, 5, 6), + [ + 'third line', + 'second line', + 'fourth line', + 'first', + 'fifth', + ], + new Selection(1, 1, 5, 6) + ); + }); +}); diff --git a/src/vs/editor/contrib/links/browser/links.css b/src/vs/editor/contrib/links/browser/links.css new file mode 100644 index 0000000000..2c407bd200 --- /dev/null +++ b/src/vs/editor/contrib/links/browser/links.css @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +.monaco-editor .detected-link, +.monaco-editor .detected-link-active { + text-decoration: underline; +} + +.monaco-editor .detected-link-active { + cursor: pointer; +} diff --git a/src/vs/editor/contrib/links/browser/links.ts b/src/vs/editor/contrib/links/browser/links.ts new file mode 100644 index 0000000000..28cea2d8ed --- /dev/null +++ b/src/vs/editor/contrib/links/browser/links.ts @@ -0,0 +1,421 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./links'; +import * as nls from 'vs/nls'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import * as platform from 'vs/base/common/platform'; +import Severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { LinkProviderRegistry } from 'vs/editor/common/modes'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { getLinks, Link } from 'vs/editor/contrib/links/common/links'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { Position } from 'vs/editor/common/core/position'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDeclaration/browser/clickLinkGesture'; +import { MarkdownString } from 'vs/base/common/htmlContent'; + +const HOVER_MESSAGE_GENERAL_META = new MarkdownString().appendText( + platform.isMacintosh + ? nls.localize('links.navigate.mac', "Cmd + click to follow link") + : nls.localize('links.navigate', "Ctrl + click to follow link") +); + +const HOVER_MESSAGE_COMMAND_META = new MarkdownString().appendText( + platform.isMacintosh + ? nls.localize('links.command.mac', "Cmd + click to execute command") + : nls.localize('links.command', "Ctrl + click to execute command") +); + +const HOVER_MESSAGE_GENERAL_ALT = new MarkdownString().appendText(nls.localize('links.navigate.al', "Alt + click to follow link")); +const HOVER_MESSAGE_COMMAND_ALT = new MarkdownString().appendText(nls.localize('links.command.al', "Alt + click to execute command")); + +const decoration = { + meta: ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'detected-link', + hoverMessage: HOVER_MESSAGE_GENERAL_META + }), + metaActive: ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'detected-link-active', + hoverMessage: HOVER_MESSAGE_GENERAL_META + }), + alt: ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'detected-link', + hoverMessage: HOVER_MESSAGE_GENERAL_ALT + }), + altActive: ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'detected-link-active', + hoverMessage: HOVER_MESSAGE_GENERAL_ALT + }), + altCommand: ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'detected-link', + hoverMessage: HOVER_MESSAGE_COMMAND_ALT + }), + altCommandActive: ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'detected-link-active', + hoverMessage: HOVER_MESSAGE_COMMAND_ALT + }), + metaCommand: ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'detected-link', + hoverMessage: HOVER_MESSAGE_COMMAND_META + }), + metaCommandActive: ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'detected-link-active', + hoverMessage: HOVER_MESSAGE_COMMAND_META + }), +}; + + +class LinkOccurrence { + + public static decoration(link: Link, useMetaKey: boolean): editorCommon.IModelDeltaDecoration { + return { + range: link.range, + options: LinkOccurrence._getOptions(link, useMetaKey, false) + }; + } + + private static _getOptions(link: Link, useMetaKey: boolean, isActive: boolean): ModelDecorationOptions { + if (/^command:/i.test(link.url)) { + if (useMetaKey) { + return (isActive ? decoration.metaCommandActive : decoration.metaCommand); + } else { + return (isActive ? decoration.altCommandActive : decoration.altCommand); + } + } else { + if (useMetaKey) { + return (isActive ? decoration.metaActive : decoration.meta); + } else { + return (isActive ? decoration.altActive : decoration.alt); + } + } + } + + public decorationId: string; + public link: Link; + + constructor(link: Link, decorationId: string) { + this.link = link; + this.decorationId = decorationId; + } + + public activate(changeAccessor: editorCommon.IModelDecorationsChangeAccessor, useMetaKey: boolean): void { + changeAccessor.changeDecorationOptions(this.decorationId, LinkOccurrence._getOptions(this.link, useMetaKey, true)); + } + + public deactivate(changeAccessor: editorCommon.IModelDecorationsChangeAccessor, useMetaKey: boolean): void { + changeAccessor.changeDecorationOptions(this.decorationId, LinkOccurrence._getOptions(this.link, useMetaKey, false)); + } +} + +@editorContribution +class LinkDetector implements editorCommon.IEditorContribution { + + private static ID: string = 'editor.linkDetector'; + + public static get(editor: editorCommon.ICommonCodeEditor): LinkDetector { + return editor.getContribution(LinkDetector.ID); + } + + static RECOMPUTE_TIME = 1000; // ms + + private editor: ICodeEditor; + private enabled: boolean; + private listenersToRemove: IDisposable[]; + private timeoutPromise: TPromise; + private computePromise: TPromise; + private activeLinkDecorationId: string; + private openerService: IOpenerService; + private messageService: IMessageService; + private editorWorkerService: IEditorWorkerService; + private currentOccurrences: { [decorationId: string]: LinkOccurrence; }; + + constructor( + editor: ICodeEditor, + @IOpenerService openerService: IOpenerService, + @IMessageService messageService: IMessageService, + @IEditorWorkerService editorWorkerService: IEditorWorkerService + ) { + this.editor = editor; + this.openerService = openerService; + this.messageService = messageService; + this.editorWorkerService = editorWorkerService; + this.listenersToRemove = []; + + let clickLinkGesture = new ClickLinkGesture(editor); + this.listenersToRemove.push(clickLinkGesture); + this.listenersToRemove.push(clickLinkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => { + this._onEditorMouseMove(mouseEvent, keyboardEvent); + })); + this.listenersToRemove.push(clickLinkGesture.onExecute((e) => { + this.onEditorMouseUp(e); + })); + this.listenersToRemove.push(clickLinkGesture.onCancel((e) => { + this.cleanUpActiveLinkDecoration(); + })); + + this.enabled = editor.getConfiguration().contribInfo.links; + this.listenersToRemove.push(editor.onDidChangeConfiguration((e) => { + let enabled = editor.getConfiguration().contribInfo.links; + if (this.enabled === enabled) { + // No change in our configuration option + return; + } + this.enabled = enabled; + + // Remove any links (for the getting disabled case) + this.updateDecorations([]); + + // Stop any computation (for the getting disabled case) + this.stop(); + + // Start computing (for the getting enabled case) + this.beginCompute(); + })); + this.listenersToRemove.push(editor.onDidChangeModelContent((e) => this.onChange())); + this.listenersToRemove.push(editor.onDidChangeModel((e) => this.onModelChanged())); + this.listenersToRemove.push(editor.onDidChangeModelLanguage((e) => this.onModelModeChanged())); + this.listenersToRemove.push(LinkProviderRegistry.onDidChange((e) => this.onModelModeChanged())); + + this.timeoutPromise = null; + this.computePromise = null; + this.currentOccurrences = {}; + this.activeLinkDecorationId = null; + this.beginCompute(); + } + + public getId(): string { + return LinkDetector.ID; + } + + public isComputing(): boolean { + return TPromise.is(this.computePromise); + } + + private onModelChanged(): void { + this.currentOccurrences = {}; + this.activeLinkDecorationId = null; + this.stop(); + this.beginCompute(); + } + + private onModelModeChanged(): void { + this.stop(); + this.beginCompute(); + } + + private onChange(): void { + if (!this.timeoutPromise) { + this.timeoutPromise = TPromise.timeout(LinkDetector.RECOMPUTE_TIME); + this.timeoutPromise.then(() => { + this.timeoutPromise = null; + this.beginCompute(); + }); + } + } + + private beginCompute(): void { + if (!this.editor.getModel() || !this.enabled) { + return; + } + + if (!LinkProviderRegistry.has(this.editor.getModel())) { + return; + } + + this.computePromise = getLinks(this.editor.getModel()).then(links => { + this.updateDecorations(links); + this.computePromise = null; + }); + } + + private updateDecorations(links: Link[]): void { + const useMetaKey = (this.editor.getConfiguration().multiCursorModifier === 'altKey'); + this.editor.changeDecorations((changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => { + var oldDecorations: string[] = []; + let keys = Object.keys(this.currentOccurrences); + for (let i = 0, len = keys.length; i < len; i++) { + let decorationId = keys[i]; + let occurance = this.currentOccurrences[decorationId]; + oldDecorations.push(occurance.decorationId); + } + + var newDecorations: editorCommon.IModelDeltaDecoration[] = []; + if (links) { + // Not sure why this is sometimes null + for (var i = 0; i < links.length; i++) { + newDecorations.push(LinkOccurrence.decoration(links[i], useMetaKey)); + } + } + + var decorations = changeAccessor.deltaDecorations(oldDecorations, newDecorations); + + this.currentOccurrences = {}; + this.activeLinkDecorationId = null; + for (let i = 0, len = decorations.length; i < len; i++) { + var occurance = new LinkOccurrence(links[i], decorations[i]); + this.currentOccurrences[occurance.decorationId] = occurance; + } + }); + } + + private _onEditorMouseMove(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): void { + const useMetaKey = (this.editor.getConfiguration().multiCursorModifier === 'altKey'); + if (this.isEnabled(mouseEvent, withKey)) { + this.cleanUpActiveLinkDecoration(); // always remove previous link decoration as their can only be one + var occurrence = this.getLinkOccurrence(mouseEvent.target.position); + if (occurrence) { + this.editor.changeDecorations((changeAccessor) => { + occurrence.activate(changeAccessor, useMetaKey); + this.activeLinkDecorationId = occurrence.decorationId; + }); + } + } else { + this.cleanUpActiveLinkDecoration(); + } + } + + private cleanUpActiveLinkDecoration(): void { + const useMetaKey = (this.editor.getConfiguration().multiCursorModifier === 'altKey'); + if (this.activeLinkDecorationId) { + var occurrence = this.currentOccurrences[this.activeLinkDecorationId]; + if (occurrence) { + this.editor.changeDecorations((changeAccessor) => { + occurrence.deactivate(changeAccessor, useMetaKey); + }); + } + + this.activeLinkDecorationId = null; + } + } + + private onEditorMouseUp(mouseEvent: ClickLinkMouseEvent): void { + if (!this.isEnabled(mouseEvent)) { + return; + } + var occurrence = this.getLinkOccurrence(mouseEvent.target.position); + if (!occurrence) { + return; + } + this.openLinkOccurrence(occurrence, mouseEvent.hasSideBySideModifier); + } + + public openLinkOccurrence(occurrence: LinkOccurrence, openToSide: boolean): void { + + if (!this.openerService) { + return; + } + + const { link } = occurrence; + + link.resolve().then(uri => { + // open the uri + return this.openerService.open(uri, { openToSide }); + + }, err => { + // different error cases + if (err === 'invalid') { + this.messageService.show(Severity.Warning, nls.localize('invalid.url', 'Sorry, failed to open this link because it is not well-formed: {0}', link.url)); + } else if (err === 'missing') { + this.messageService.show(Severity.Warning, nls.localize('missing.url', 'Sorry, failed to open this link because its target is missing.')); + } else { + onUnexpectedError(err); + } + }).done(null, onUnexpectedError); + } + + public getLinkOccurrence(position: Position): LinkOccurrence { + var decorations = this.editor.getModel().getDecorationsInRange({ + startLineNumber: position.lineNumber, + startColumn: position.column, + endLineNumber: position.lineNumber, + endColumn: position.column + }, 0, true); + + for (var i = 0; i < decorations.length; i++) { + var decoration = decorations[i]; + var currentOccurrence = this.currentOccurrences[decoration.id]; + if (currentOccurrence) { + return currentOccurrence; + } + } + + return null; + } + + private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): boolean { + return ( + mouseEvent.target.type === MouseTargetType.CONTENT_TEXT + && (mouseEvent.hasTriggerModifier || (withKey && withKey.keyCodeIsTriggerKey)) + ); + } + + private stop(): void { + if (this.timeoutPromise) { + this.timeoutPromise.cancel(); + this.timeoutPromise = null; + } + if (this.computePromise) { + this.computePromise.cancel(); + this.computePromise = null; + } + } + + public dispose(): void { + this.listenersToRemove = dispose(this.listenersToRemove); + this.stop(); + } +} + +@editorAction +class OpenLinkAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.openLink', + label: nls.localize('label', "Open Link"), + alias: 'Open Link', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let linkDetector = LinkDetector.get(editor); + if (!linkDetector) { + return; + } + + let link = linkDetector.getLinkOccurrence(editor.getPosition()); + if (link) { + linkDetector.openLinkOccurrence(link, false); + } + } +} + +registerThemingParticipant((theme, collector) => { + let activeLinkForeground = theme.getColor(editorActiveLinkForeground); + if (activeLinkForeground) { + collector.addRule(`.monaco-editor .detected-link-active { color: ${activeLinkForeground} !important; }`); + } +}); diff --git a/src/vs/editor/contrib/links/common/links.ts b/src/vs/editor/contrib/links/common/links.ts new file mode 100644 index 0000000000..7e8b78f229 --- /dev/null +++ b/src/vs/editor/contrib/links/common/links.ts @@ -0,0 +1,137 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { IReadOnlyModel } from 'vs/editor/common/editorCommon'; +import { ILink, LinkProvider, LinkProviderRegistry } from 'vs/editor/common/modes'; +import { asWinJsPromise } from 'vs/base/common/async'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IModelService } from 'vs/editor/common/services/modelService'; + +export class Link implements ILink { + + private _link: ILink; + private _provider: LinkProvider; + + constructor(link: ILink, provider: LinkProvider) { + this._link = link; + this._provider = provider; + } + + get range(): IRange { + return this._link.range; + } + + get url(): string { + return this._link.url; + } + + resolve(): TPromise { + if (this._link.url) { + try { + return TPromise.as(URI.parse(this._link.url)); + } catch (e) { + return TPromise.wrapError(new Error('invalid')); + } + } + + if (typeof this._provider.resolveLink === 'function') { + return asWinJsPromise(token => this._provider.resolveLink(this._link, token)).then(value => { + this._link = value || this._link; + if (this._link.url) { + // recurse + return this.resolve(); + } + + return TPromise.wrapError(new Error('missing')); + }); + } + + return TPromise.wrapError(new Error('missing')); + } +} + +export function getLinks(model: IReadOnlyModel): TPromise { + + let links: Link[] = []; + + // ask all providers for links in parallel + const promises = LinkProviderRegistry.ordered(model).reverse().map(provider => { + return asWinJsPromise(token => provider.provideLinks(model, token)).then(result => { + if (Array.isArray(result)) { + const newLinks = result.map(link => new Link(link, provider)); + links = union(links, newLinks); + } + }, onUnexpectedExternalError); + }); + + return TPromise.join(promises).then(() => { + return links; + }); +} + +function union(oldLinks: Link[], newLinks: Link[]): Link[] { + // reunite oldLinks with newLinks and remove duplicates + var result: Link[] = [], + oldIndex: number, + oldLen: number, + newIndex: number, + newLen: number, + oldLink: Link, + newLink: Link, + comparisonResult: number; + + for (oldIndex = 0, newIndex = 0, oldLen = oldLinks.length, newLen = newLinks.length; oldIndex < oldLen && newIndex < newLen;) { + oldLink = oldLinks[oldIndex]; + newLink = newLinks[newIndex]; + + if (Range.areIntersectingOrTouching(oldLink.range, newLink.range)) { + // Remove the oldLink + oldIndex++; + continue; + } + + comparisonResult = Range.compareRangesUsingStarts(oldLink.range, newLink.range); + + if (comparisonResult < 0) { + // oldLink is before + result.push(oldLink); + oldIndex++; + } else { + // newLink is before + result.push(newLink); + newIndex++; + } + } + + for (; oldIndex < oldLen; oldIndex++) { + result.push(oldLinks[oldIndex]); + } + for (; newIndex < newLen; newIndex++) { + result.push(newLinks[newIndex]); + } + + return result; +} + +CommandsRegistry.registerCommand('_executeLinkProvider', (accessor, ...args) => { + + const [uri] = args; + if (!(uri instanceof URI)) { + return undefined; + } + + const model = accessor.get(IModelService).getModel(uri); + if (!model) { + return undefined; + } + + return getLinks(model); +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/multicursor/common/multicursor.ts b/src/vs/editor/contrib/multicursor/common/multicursor.ts new file mode 100644 index 0000000000..1bd2f1c710 --- /dev/null +++ b/src/vs/editor/contrib/multicursor/common/multicursor.ts @@ -0,0 +1,141 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICommonCodeEditor, ScrollType } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { Selection } from 'vs/editor/common/core/selection'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands'; +import { CursorState, RevealTarget } from 'vs/editor/common/controller/cursorCommon'; + +@editorAction +export class InsertCursorAbove extends EditorAction { + constructor() { + super({ + id: 'editor.action.insertCursorAbove', + label: nls.localize('mutlicursor.insertAbove', "Add Cursor Above"), + alias: 'Add Cursor Above', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.UpArrow, + linux: { + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.UpArrow, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow] + } + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor, args: any): void { + const cursors = editor._getCursors(); + const context = cursors.context; + + if (context.config.readOnly) { + return; + } + + context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + CursorState.ensureInEditableRange( + context, + CursorMoveCommands.addCursorUp(context, cursors.getAll()) + ) + ); + cursors.reveal(true, RevealTarget.TopMost, ScrollType.Smooth); + } +} + +@editorAction +export class InsertCursorBelow extends EditorAction { + constructor() { + super({ + id: 'editor.action.insertCursorBelow', + label: nls.localize('mutlicursor.insertBelow', "Add Cursor Below"), + alias: 'Add Cursor Below', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.DownArrow, + linux: { + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.DownArrow, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow] + } + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor, args: any): void { + const cursors = editor._getCursors(); + const context = cursors.context; + + if (context.config.readOnly) { + return; + } + + context.model.pushStackElement(); + cursors.setStates( + args.source, + CursorChangeReason.Explicit, + CursorState.ensureInEditableRange( + context, + CursorMoveCommands.addCursorDown(context, cursors.getAll()) + ) + ); + cursors.reveal(true, RevealTarget.BottomMost, ScrollType.Smooth); + } +} + +@editorAction +class InsertCursorAtEndOfEachLineSelected extends EditorAction { + + constructor() { + super({ + id: 'editor.action.insertCursorAtEndOfEachLineSelected', + label: nls.localize('mutlicursor.insertAtEndOfEachLineSelected', "Add Cursors to Line Ends"), + alias: 'Add Cursors to Line Ends', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_I + } + }); + } + + private getCursorsForSelection(selection: Selection, editor: ICommonCodeEditor): Selection[] { + if (selection.isEmpty()) { + return []; + } + + let model = editor.getModel(); + let newSelections: Selection[] = []; + for (let i = selection.startLineNumber; i < selection.endLineNumber; i++) { + let currentLineMaxColumn = model.getLineMaxColumn(i); + newSelections.push(new Selection(i, currentLineMaxColumn, i, currentLineMaxColumn)); + } + if (selection.endColumn > 1) { + newSelections.push(new Selection(selection.endLineNumber, selection.endColumn, selection.endLineNumber, selection.endColumn)); + } + + return newSelections; + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let selections = editor.getSelections(); + let newSelections = selections + .map((selection) => this.getCursorsForSelection(selection, editor)) + .reduce((prev, curr) => { return prev.concat(curr); }); + + if (newSelections.length > 0) { + editor.setSelections(newSelections); + } + } +} diff --git a/src/vs/editor/contrib/multicursor/test/common/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/common/multicursor.test.ts new file mode 100644 index 0000000000..89752b34a0 --- /dev/null +++ b/src/vs/editor/contrib/multicursor/test/common/multicursor.test.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { Selection } from 'vs/editor/common/core/selection'; +import { InsertCursorAbove, InsertCursorBelow } from 'vs/editor/contrib/multicursor/common/multicursor'; +import { Handler } from 'vs/editor/common/editorCommon'; + + +suite('Multicursor', () => { + + test('issue #2205: Multi-cursor pastes in reverse order', () => { + withMockCodeEditor([ + 'abc', + 'def' + ], {}, (editor, cursor) => { + let addCursorUpAction = new InsertCursorAbove(); + + editor.setSelection(new Selection(2, 1, 2, 1)); + addCursorUpAction.run(null, editor, {}); + assert.equal(cursor.getSelections().length, 2); + + editor.trigger('test', Handler.Paste, { text: '1\n2' }); + // cursorCommand(cursor, H.Paste, { text: '1\n2' }); + assert.equal(editor.getModel().getLineContent(1), '1abc'); + assert.equal(editor.getModel().getLineContent(2), '2def'); + }); + }); + + test('issue #1336: Insert cursor below on last line adds a cursor to the end of the current line', () => { + withMockCodeEditor([ + 'abc' + ], {}, (editor, cursor) => { + let addCursorDownAction = new InsertCursorBelow(); + addCursorDownAction.run(null, editor, {}); + assert.equal(cursor.getSelections().length, 1); + }); + }); + +}); diff --git a/src/vs/editor/contrib/parameterHints/browser/arrow-down-dark.svg b/src/vs/editor/contrib/parameterHints/browser/arrow-down-dark.svg new file mode 100644 index 0000000000..b1a76d789e --- /dev/null +++ b/src/vs/editor/contrib/parameterHints/browser/arrow-down-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/browser/arrow-down.svg b/src/vs/editor/contrib/parameterHints/browser/arrow-down.svg new file mode 100644 index 0000000000..d643403d75 --- /dev/null +++ b/src/vs/editor/contrib/parameterHints/browser/arrow-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/browser/arrow-up-dark.svg b/src/vs/editor/contrib/parameterHints/browser/arrow-up-dark.svg new file mode 100644 index 0000000000..43f31a0952 --- /dev/null +++ b/src/vs/editor/contrib/parameterHints/browser/arrow-up-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/browser/arrow-up.svg b/src/vs/editor/contrib/parameterHints/browser/arrow-up.svg new file mode 100644 index 0000000000..b8072bb31b --- /dev/null +++ b/src/vs/editor/contrib/parameterHints/browser/arrow-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHints.css b/src/vs/editor/contrib/parameterHints/browser/parameterHints.css new file mode 100644 index 0000000000..345a7a6516 --- /dev/null +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHints.css @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .parameter-hints-widget { + z-index: 10; + display: flex; + flex-direction: column; + line-height: 1.5em; +} + +.monaco-editor .parameter-hints-widget > .wrapper { + max-width: 440px; + display: flex; + flex-direction: column; +} + +.monaco-editor .parameter-hints-widget.multiple { + min-height: 3.3em; + padding: 0 0 0 1.9em; +} + +.monaco-editor .parameter-hints-widget.visible { + -webkit-transition: left .05s ease-in-out; + -moz-transition: left .05s ease-in-out; + -o-transition: left .05s ease-in-out; + transition: left .05s ease-in-out; +} + +.monaco-editor .parameter-hints-widget p, +.monaco-editor .parameter-hints-widget ul { + margin: 8px 0; +} + +.monaco-editor .parameter-hints-widget .monaco-scrollable-element, +.monaco-editor .parameter-hints-widget .body { + display: flex; + flex-direction: column; +} + +.monaco-editor .parameter-hints-widget .signature { + padding: 4px 5px; +} + +.monaco-editor .parameter-hints-widget .docs { + padding: 0 10px 0 5px; + white-space: pre-wrap; +} + +.monaco-editor .parameter-hints-widget .buttons { + position: absolute; + display: none; + bottom: 0; + left: 0; +} + +.monaco-editor .parameter-hints-widget.multiple .buttons { + display: block; +} + +.monaco-editor .parameter-hints-widget.multiple .button { + position: absolute; + left: 2px; + width: 16px; + height: 16px; + background-repeat: no-repeat; + cursor: pointer; +} + +.monaco-editor .parameter-hints-widget .button.previous { + bottom: 24px; + background-image: url('arrow-up.svg'); +} + +.monaco-editor .parameter-hints-widget .button.next { + bottom: 0; + background-image: url('arrow-down.svg'); +} + +.monaco-editor .parameter-hints-widget .overloads { + position: absolute; + display: none; + text-align: center; + bottom: 14px; + left: 0; + width: 22px; + height: 12px; + line-height: 12px; + opacity: 0.5; +} + +.monaco-editor .parameter-hints-widget.multiple .overloads { + display: block; +} + +.monaco-editor .parameter-hints-widget .signature .parameter { + display: inline-block; +} + +.monaco-editor .parameter-hints-widget .signature .parameter.active { + font-weight: bold; + text-decoration: underline; +} + +.monaco-editor .parameter-hints-widget .documentation-parameter > .parameter { + font-weight: bold; + margin-right: 0.5em; +} + +/*** VS Dark & High Contrast*/ + +.monaco-editor.hc-black .parameter-hints-widget .button.previous, +.monaco-editor.vs-dark .parameter-hints-widget .button.previous { + background-image: url('arrow-up-dark.svg'); +} + +.monaco-editor.hc-black .parameter-hints-widget .button.next, +.monaco-editor.vs-dark .parameter-hints-widget .button.next { + background-image: url('arrow-down-dark.svg'); +} \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHints.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHints.ts new file mode 100644 index 0000000000..92d1c96bdb --- /dev/null +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHints.ts @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { dispose } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICommonCodeEditor, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { ParameterHintsWidget } from './parameterHintsWidget'; +import { Context } from '../common/parameterHints'; + +@editorContribution +class ParameterHintsController implements IEditorContribution { + + private static ID = 'editor.controller.parameterHints'; + + public static get(editor: ICommonCodeEditor): ParameterHintsController { + return editor.getContribution(ParameterHintsController.ID); + } + + private editor: ICodeEditor; + private widget: ParameterHintsWidget; + + constructor(editor: ICodeEditor, @IInstantiationService instantiationService: IInstantiationService) { + this.editor = editor; + this.widget = instantiationService.createInstance(ParameterHintsWidget, this.editor); + } + + getId(): string { + return ParameterHintsController.ID; + } + + cancel(): void { + this.widget.cancel(); + } + + previous(): void { + this.widget.previous(); + } + + next(): void { + this.widget.next(); + } + + trigger(): void { + this.widget.trigger(); + } + + dispose(): void { + this.widget = dispose(this.widget); + } +} + +@editorAction +export class TriggerParameterHintsAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.triggerParameterHints', + label: nls.localize('parameterHints.trigger.label', "Trigger Parameter Hints"), + alias: 'Trigger Parameter Hints', + precondition: EditorContextKeys.hasSignatureHelpProvider, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Space + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let controller = ParameterHintsController.get(editor); + if (controller) { + controller.trigger(); + } + } +} + +const weight = CommonEditorRegistry.commandWeight(75); + +const ParameterHintsCommand = EditorCommand.bindToContribution(ParameterHintsController.get); + +CommonEditorRegistry.registerEditorCommand(new ParameterHintsCommand({ + id: 'closeParameterHints', + precondition: Context.Visible, + handler: x => x.cancel(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } +})); +CommonEditorRegistry.registerEditorCommand(new ParameterHintsCommand({ + id: 'showPrevParameterHint', + precondition: ContextKeyExpr.and(Context.Visible, Context.MultipleSignatures), + handler: x => x.previous(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.UpArrow, + secondary: [KeyMod.Alt | KeyCode.UpArrow], + mac: { primary: KeyCode.UpArrow, secondary: [KeyMod.Alt | KeyCode.UpArrow, KeyMod.WinCtrl | KeyCode.KEY_P] } + } +})); +CommonEditorRegistry.registerEditorCommand(new ParameterHintsCommand({ + id: 'showNextParameterHint', + precondition: ContextKeyExpr.and(Context.Visible, Context.MultipleSignatures), + handler: x => x.next(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.DownArrow, + secondary: [KeyMod.Alt | KeyCode.DownArrow], + mac: { primary: KeyCode.DownArrow, secondary: [KeyMod.Alt | KeyCode.DownArrow, KeyMod.WinCtrl | KeyCode.KEY_N] } + } +})); diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts new file mode 100644 index 0000000000..3064b11c07 --- /dev/null +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -0,0 +1,494 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./parameterHints'; +import nls = require('vs/nls'); +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as dom from 'vs/base/browser/dom'; +import aria = require('vs/base/browser/ui/aria/aria'); +import { SignatureHelp, SignatureInformation, SignatureHelpProviderRegistry } from 'vs/editor/common/modes'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import Event, { Emitter, chain } from 'vs/base/common/event'; +import { domEvent, stop } from 'vs/base/browser/event'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { Context, provideSignatureHelp } from '../common/parameterHints'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; + +const $ = dom.$; + +export interface IHintEvent { + hints: SignatureHelp; +} + +export class ParameterHintsModel extends Disposable { + + static DELAY = 120; // ms + + private _onHint = this._register(new Emitter()); + onHint: Event = this._onHint.event; + + private _onCancel = this._register(new Emitter()); + onCancel: Event = this._onCancel.event; + + private editor: ICommonCodeEditor; + private enabled: boolean; + private triggerCharactersListeners: IDisposable[]; + private active: boolean; + private throttledDelayer: RunOnceScheduler; + + constructor(editor: ICommonCodeEditor) { + super(); + + this.editor = editor; + this.enabled = false; + this.triggerCharactersListeners = []; + + this.throttledDelayer = new RunOnceScheduler(() => this.doTrigger(), ParameterHintsModel.DELAY); + + this.active = false; + + this._register(this.editor.onDidChangeConfiguration(() => this.onEditorConfigurationChange())); + this._register(this.editor.onDidChangeModel(e => this.onModelChanged())); + this._register(this.editor.onDidChangeModelLanguage(_ => this.onModelChanged())); + this._register(this.editor.onDidChangeCursorSelection(e => this.onCursorChange(e))); + this._register(SignatureHelpProviderRegistry.onDidChange(this.onModelChanged, this)); + + this.onEditorConfigurationChange(); + this.onModelChanged(); + } + + cancel(silent: boolean = false): void { + this.active = false; + + this.throttledDelayer.cancel(); + + if (!silent) { + this._onCancel.fire(void 0); + } + } + + trigger(delay = ParameterHintsModel.DELAY): void { + if (!SignatureHelpProviderRegistry.has(this.editor.getModel())) { + return; + } + + this.cancel(true); + return this.throttledDelayer.schedule(delay); + } + + private doTrigger(): void { + provideSignatureHelp(this.editor.getModel(), this.editor.getPosition()) + .then(null, onUnexpectedError) + .then(result => { + if (!result || !result.signatures || result.signatures.length === 0) { + this.cancel(); + this._onCancel.fire(void 0); + return false; + } + + this.active = true; + + const event: IHintEvent = { hints: result }; + this._onHint.fire(event); + return true; + }); + } + + isTriggered(): boolean { + return this.active || this.throttledDelayer.isScheduled(); + } + + private onModelChanged(): void { + if (this.active) { + this.cancel(); + } + this.triggerCharactersListeners = dispose(this.triggerCharactersListeners); + + const model = this.editor.getModel(); + if (!model) { + return; + } + + const triggerChars = new CharacterSet(); + for (const support of SignatureHelpProviderRegistry.ordered(model)) { + if (Array.isArray(support.signatureHelpTriggerCharacters)) { + for (const ch of support.signatureHelpTriggerCharacters) { + triggerChars.add(ch.charCodeAt(0)); + } + } + } + + this.triggerCharactersListeners.push(this.editor.onDidType((text: string) => { + if (!this.enabled) { + return; + } + + if (triggerChars.has(text.charCodeAt(text.length - 1))) { + this.trigger(); + } + })); + } + + private onCursorChange(e: ICursorSelectionChangedEvent): void { + if (e.source === 'mouse') { + this.cancel(); + } else if (this.isTriggered()) { + this.trigger(); + } + } + + private onEditorConfigurationChange(): void { + this.enabled = this.editor.getConfiguration().contribInfo.parameterHints; + + if (!this.enabled) { + this.cancel(); + } + } + + dispose(): void { + this.cancel(true); + this.triggerCharactersListeners = dispose(this.triggerCharactersListeners); + + super.dispose(); + } +} + +export class ParameterHintsWidget implements IContentWidget, IDisposable { + + private static ID = 'editor.widget.parameterHintsWidget'; + + private model: ParameterHintsModel; + private keyVisible: IContextKey; + private keyMultipleSignatures: IContextKey; + private element: HTMLElement; + private signature: HTMLElement; + private docs: HTMLElement; + private overloads: HTMLElement; + private currentSignature: number; + private visible: boolean; + private hints: SignatureHelp; + private announcedLabel: string; + private scrollbar: DomScrollableElement; + private disposables: IDisposable[]; + + // Editor.IContentWidget.allowEditorOverflow + allowEditorOverflow = true; + + constructor(private editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService) { + this.model = new ParameterHintsModel(editor); + this.keyVisible = Context.Visible.bindTo(contextKeyService); + this.keyMultipleSignatures = Context.MultipleSignatures.bindTo(contextKeyService); + this.visible = false; + this.disposables = []; + + this.disposables.push(this.model.onHint(e => { + this.show(); + this.hints = e.hints; + this.currentSignature = e.hints.activeSignature; + this.render(); + })); + + this.disposables.push(this.model.onCancel(() => { + this.hide(); + })); + + this.element = $('.editor-widget.parameter-hints-widget'); + const wrapper = dom.append(this.element, $('.wrapper')); + + const onClick = stop(domEvent(this.element, 'click')); + onClick(this.next, this, this.disposables); + + const buttons = dom.append(wrapper, $('.buttons')); + const previous = dom.append(buttons, $('.button.previous')); + const next = dom.append(buttons, $('.button.next')); + + const onPreviousClick = stop(domEvent(previous, 'click')); + onPreviousClick(this.previous, this, this.disposables); + + const onNextClick = stop(domEvent(next, 'click')); + onNextClick(this.next, this, this.disposables); + + this.overloads = dom.append(wrapper, $('.overloads')); + + const body = $('.body'); + this.scrollbar = new DomScrollableElement(body, {}); + this.disposables.push(this.scrollbar); + wrapper.appendChild(this.scrollbar.getDomNode()); + + this.signature = dom.append(body, $('.signature')); + + this.docs = dom.append(body, $('.docs')); + + this.currentSignature = 0; + + this.editor.addContentWidget(this); + this.hide(); + + this.disposables.push(this.editor.onDidChangeCursorSelection(e => { + if (this.visible) { + this.editor.layoutContentWidget(this); + } + })); + + const updateFont = () => { + const fontInfo = this.editor.getConfiguration().fontInfo; + this.element.style.fontSize = `${fontInfo.fontSize}px`; + }; + + updateFont(); + + chain(this.editor.onDidChangeConfiguration.bind(this.editor)) + .filter(e => e.fontInfo) + .on(updateFont, null, this.disposables); + + this.disposables.push(this.editor.onDidLayoutChange(e => this.updateMaxHeight())); + this.updateMaxHeight(); + } + + private show(): void { + if (!this.model || this.visible) { + return; + } + + this.keyVisible.set(true); + this.visible = true; + TPromise.timeout(100).done(() => dom.addClass(this.element, 'visible')); + this.editor.layoutContentWidget(this); + } + + private hide(): void { + if (!this.model || !this.visible) { + return; + } + + this.keyVisible.reset(); + this.visible = false; + this.hints = null; + this.announcedLabel = null; + dom.removeClass(this.element, 'visible'); + this.editor.layoutContentWidget(this); + } + + getPosition(): IContentWidgetPosition { + if (this.visible) { + return { + position: this.editor.getPosition(), + preference: [ContentWidgetPositionPreference.ABOVE, ContentWidgetPositionPreference.BELOW] + }; + } + return null; + } + + private render(): void { + const multiple = this.hints.signatures.length > 1; + dom.toggleClass(this.element, 'multiple', multiple); + this.keyMultipleSignatures.set(multiple); + + this.signature.innerHTML = ''; + this.docs.innerHTML = ''; + + const signature = this.hints.signatures[this.currentSignature]; + + if (!signature) { + return; + } + + const code = dom.append(this.signature, $('.code')); + const hasParameters = signature.parameters.length > 0; + + const fontInfo = this.editor.getConfiguration().fontInfo; + code.style.fontSize = `${fontInfo.fontSize}px`; + code.style.fontFamily = fontInfo.fontFamily; + + if (!hasParameters) { + const label = dom.append(code, $('span')); + label.textContent = signature.label; + + } else { + this.renderParameters(code, signature, this.hints.activeParameter); + } + + const activeParameter = signature.parameters[this.hints.activeParameter]; + + if (activeParameter && activeParameter.documentation) { + const documentation = $('span.documentation'); + documentation.textContent = activeParameter.documentation; + dom.append(this.docs, $('p', null, documentation)); + } + + dom.toggleClass(this.signature, 'has-docs', !!signature.documentation); + + if (signature.documentation) { + dom.append(this.docs, $('p', null, signature.documentation)); + } + + let currentOverload = String(this.currentSignature + 1); + + if (this.hints.signatures.length < 10) { + currentOverload += `/${this.hints.signatures.length}`; + } + + this.overloads.textContent = currentOverload; + + if (activeParameter) { + const labelToAnnounce = activeParameter.label; + // Select method gets called on every user type while parameter hints are visible. + // We do not want to spam the user with same announcements, so we only announce if the current parameter changed. + + if (this.announcedLabel !== labelToAnnounce) { + aria.alert(nls.localize('hint', "{0}, hint", labelToAnnounce)); + this.announcedLabel = labelToAnnounce; + } + } + + this.editor.layoutContentWidget(this); + this.scrollbar.scanDomNode(); + } + + private renderParameters(parent: HTMLElement, signature: SignatureInformation, currentParameter: number): void { + let end = signature.label.length; + let idx = 0; + let element: HTMLSpanElement; + + for (let i = signature.parameters.length - 1; i >= 0; i--) { + const parameter = signature.parameters[i]; + idx = signature.label.lastIndexOf(parameter.label, end - 1); + + let signatureLabelOffset = 0; + let signatureLabelEnd = 0; + + if (idx >= 0) { + signatureLabelOffset = idx; + signatureLabelEnd = idx + parameter.label.length; + } + + // non parameter part + element = document.createElement('span'); + element.textContent = signature.label.substring(signatureLabelEnd, end); + dom.prepend(parent, element); + + // parameter part + element = document.createElement('span'); + element.className = `parameter ${i === currentParameter ? 'active' : ''}`; + element.textContent = signature.label.substring(signatureLabelOffset, signatureLabelEnd); + dom.prepend(parent, element); + + end = signatureLabelOffset; + } + // non parameter part + element = document.createElement('span'); + element.textContent = signature.label.substring(0, end); + dom.prepend(parent, element); + } + + // private select(position: number): void { + // const signature = this.signatureViews[position]; + + // if (!signature) { + // return; + // } + + // this.signatures.style.height = `${ signature.height }px`; + // this.signatures.scrollTop = signature.top; + + // let overloads = '' + (position + 1); + + // if (this.signatureViews.length < 10) { + // overloads += '/' + this.signatureViews.length; + // } + + // this.overloads.textContent = overloads; + + // if (this.hints && this.hints.signatures[position].parameters[this.hints.activeParameter]) { + // const labelToAnnounce = this.hints.signatures[position].parameters[this.hints.activeParameter].label; + // // Select method gets called on every user type while parameter hints are visible. + // // We do not want to spam the user with same announcements, so we only announce if the current parameter changed. + // if (this.announcedLabel !== labelToAnnounce) { + // aria.alert(nls.localize('hint', "{0}, hint", labelToAnnounce)); + // this.announcedLabel = labelToAnnounce; + // } + // } + + // this.editor.layoutContentWidget(this); + // } + + next(): boolean { + const length = this.hints.signatures.length; + + if (length < 2) { + this.cancel(); + return false; + } + + this.currentSignature = (this.currentSignature + 1) % length; + this.render(); + return true; + } + + previous(): boolean { + const length = this.hints.signatures.length; + + if (length < 2) { + this.cancel(); + return false; + } + + this.currentSignature = (this.currentSignature - 1 + length) % length; + this.render(); + return true; + } + + cancel(): void { + this.model.cancel(); + } + + getDomNode(): HTMLElement { + return this.element; + } + + getId(): string { + return ParameterHintsWidget.ID; + } + + trigger(): void { + this.model.trigger(0); + } + + private updateMaxHeight(): void { + const height = Math.max(this.editor.getLayoutInfo().height / 4, 250); + this.element.style.maxHeight = `${height}px`; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + this.model = null; + } +} + +registerThemingParticipant((theme, collector) => { + let border = theme.getColor(editorHoverBorder); + if (border) { + let borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1; + collector.addRule(`.monaco-editor .parameter-hints-widget { border: ${borderWidth}px solid ${border}; }`); + collector.addRule(`.monaco-editor .parameter-hints-widget.multiple .body { border-left: 1px solid ${border.transparent(0.5)}; }`); + collector.addRule(`.monaco-editor .parameter-hints-widget .signature.has-docs { border-bottom: 1px solid ${border.transparent(0.5)}; }`); + + } + let background = theme.getColor(editorHoverBackground); + if (background) { + collector.addRule(`.monaco-editor .parameter-hints-widget { background-color: ${background}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/common/parameterHints.ts b/src/vs/editor/contrib/parameterHints/common/parameterHints.ts new file mode 100644 index 0000000000..763f61baa1 --- /dev/null +++ b/src/vs/editor/contrib/parameterHints/common/parameterHints.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { IReadOnlyModel } from 'vs/editor/common/editorCommon'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { SignatureHelp, SignatureHelpProviderRegistry } from 'vs/editor/common/modes'; +import { asWinJsPromise, sequence } from 'vs/base/common/async'; +import { Position } from 'vs/editor/common/core/position'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const Context = { + Visible: new RawContextKey('parameterHintsVisible', false), + MultipleSignatures: new RawContextKey('parameterHintsMultipleSignatures', false), +}; + +export function provideSignatureHelp(model: IReadOnlyModel, position: Position): TPromise { + + const supports = SignatureHelpProviderRegistry.ordered(model); + let result: SignatureHelp; + + return sequence(supports.map(support => () => { + + if (result) { + // stop when there is a result + return undefined; + } + + return asWinJsPromise(token => support.provideSignatureHelp(model, position, token)).then(thisResult => { + result = thisResult; + }, onUnexpectedExternalError); + + })).then(() => result); +} + +CommonEditorRegistry.registerDefaultLanguageCommand('_executeSignatureHelpProvider', provideSignatureHelp); \ No newline at end of file diff --git a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css new file mode 100644 index 0000000000..b2a3d0f10d --- /dev/null +++ b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .lightbulb-glyph { + display: flex; + align-items: center; + justify-content: center; + height: 16px; + width: 16px; +} + +.monaco-editor .lightbulb-glyph.hidden { + display: none; + visibility: hidden; +} + +.monaco-editor .lightbulb-glyph:hover { + cursor: pointer; +} + +.monaco-editor.vs .lightbulb-glyph { + background: url('lightbulb.svg') center center no-repeat; +} + +.monaco-editor.vs-dark .lightbulb-glyph, +.monaco-editor.hc-black .lightbulb-glyph { + background: url('lightbulb-dark.svg') center center no-repeat; +} diff --git a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts new file mode 100644 index 0000000000..20ca9f8741 --- /dev/null +++ b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./lightBulbWidget'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import * as dom from 'vs/base/browser/dom'; +import { TrackedRangeStickiness } from 'vs/editor/common/editorCommon'; +import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { QuickFixComputeEvent } from './quickFixModel'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; + +export class LightBulbWidget implements IDisposable { + + private readonly _options = { + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + glyphMarginClassName: 'lightbulb-glyph', + glyphMarginHoverMessage: undefined + }; + + private readonly _editor: ICodeEditor; + private readonly _onClick = new Emitter<{ x: number, y: number }>(); + private readonly _mouseDownSubscription: IDisposable; + + private _decorationIds: string[] = []; + private _currentLine: number; + private _model: QuickFixComputeEvent; + private _futureFixes = new CancellationTokenSource(); + + constructor(editor: ICodeEditor) { + this._editor = editor; + this._mouseDownSubscription = this._editor.onMouseDown(e => { + + // not on glyh margin or not on 💡 + if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN + || this._currentLine === undefined + || this._currentLine !== e.target.position.lineNumber + ) { + return; + } + + // a bit of extra work to make sure the menu + // doesn't cover the line-text + const { top, height } = dom.getDomNodePagePosition(e.target.element); + const { lineHeight } = this._editor.getConfiguration(); + this._onClick.fire({ + x: e.event.posx, + y: top + height + Math.floor(lineHeight / 3) + }); + }); + } + + dispose(): void { + this._mouseDownSubscription.dispose(); + this.hide(); + } + + get onClick(): Event<{ x: number, y: number }> { + return this._onClick.event; + } + + set model(e: QuickFixComputeEvent) { + this._model = e; + this.hide(); + this._futureFixes = new CancellationTokenSource(); + const { token } = this._futureFixes; + + e.fixes.done(fixes => { + if (!token.isCancellationRequested && fixes && fixes.length > 0) { + this.show(e); + } else { + this.hide(); + } + }, err => { + this.hide(); + }); + } + + get model(): QuickFixComputeEvent { + return this._model; + } + + set title(value: string) { + // TODO(joh,alex) this isn't working well because the hover hover + // message sticks around after clicking the light bulb + // this._options.glyphMarginHoverMessage = value; + } + + get title() { + return this._options.glyphMarginHoverMessage && this._options.glyphMarginHoverMessage.value; + } + + show(e: QuickFixComputeEvent): void { + this._currentLine = e.range.startLineNumber; + this._decorationIds = this._editor.deltaDecorations(this._decorationIds, [{ + options: this._options, + range: { ...e.range, endLineNumber: e.range.startLineNumber } + }]); + } + + hide(): void { + this._decorationIds = this._editor.deltaDecorations(this._decorationIds, []); + this._futureFixes.cancel(); + this._currentLine = undefined; + } +} diff --git a/src/vs/editor/contrib/quickFix/browser/lightbulb-dark.svg b/src/vs/editor/contrib/quickFix/browser/lightbulb-dark.svg new file mode 100644 index 0000000000..520f78f3e5 --- /dev/null +++ b/src/vs/editor/contrib/quickFix/browser/lightbulb-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/quickFix/browser/lightbulb.svg b/src/vs/editor/contrib/quickFix/browser/lightbulb.svg new file mode 100644 index 0000000000..b359604661 --- /dev/null +++ b/src/vs/editor/contrib/quickFix/browser/lightbulb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/quickFix/browser/quickFix.ts b/src/vs/editor/contrib/quickFix/browser/quickFix.ts new file mode 100644 index 0000000000..b39fbaf967 --- /dev/null +++ b/src/vs/editor/contrib/quickFix/browser/quickFix.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { IReadOnlyModel } from 'vs/editor/common/editorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { Command, CodeActionProviderRegistry } from 'vs/editor/common/modes'; +import { asWinJsPromise } from 'vs/base/common/async'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { onUnexpectedExternalError, illegalArgument } from 'vs/base/common/errors'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; + +export function getCodeActions(model: IReadOnlyModel, range: Range): TPromise { + + const allResults: Command[] = []; + const promises = CodeActionProviderRegistry.all(model).map(support => { + return asWinJsPromise(token => support.provideCodeActions(model, range, token)).then(result => { + if (Array.isArray(result)) { + for (const quickFix of result) { + if (quickFix) { + allResults.push(quickFix); + } + } + } + }, err => { + onUnexpectedExternalError(err); + }); + }); + + return TPromise.join(promises).then(() => allResults); +} + +CommonEditorRegistry.registerLanguageCommand('_executeCodeActionProvider', function (accessor, args) { + + const { resource, range } = args; + if (!(resource instanceof URI) || !Range.isIRange(range)) { + throw illegalArgument(); + } + + const model = accessor.get(IModelService).getModel(resource); + if (!model) { + throw illegalArgument(); + } + + return getCodeActions(model, model.validateRange(range)); +}); + diff --git a/src/vs/editor/contrib/quickFix/browser/quickFixCommands.ts b/src/vs/editor/contrib/quickFix/browser/quickFixCommands.ts new file mode 100644 index 0000000000..a55a980fed --- /dev/null +++ b/src/vs/editor/contrib/quickFix/browser/quickFixCommands.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { ICommonCodeEditor, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { QuickFixContextMenu } from './quickFixWidget'; +import { LightBulbWidget } from './lightBulbWidget'; +import { QuickFixModel, QuickFixComputeEvent } from './quickFixModel'; + +@editorContribution +export class QuickFixController implements IEditorContribution { + + private static ID = 'editor.contrib.quickFixController'; + + public static get(editor: ICommonCodeEditor): QuickFixController { + return editor.getContribution(QuickFixController.ID); + } + + private _editor: ICodeEditor; + private _model: QuickFixModel; + private _quickFixContextMenu: QuickFixContextMenu; + private _lightBulbWidget: LightBulbWidget; + private _disposables: IDisposable[] = []; + + constructor(editor: ICodeEditor, + @IMarkerService markerService: IMarkerService, + @IContextKeyService contextKeyService: IContextKeyService, + @ICommandService commandService: ICommandService, + @IContextMenuService contextMenuService: IContextMenuService, + @IKeybindingService private _keybindingService: IKeybindingService + ) { + this._editor = editor; + this._model = new QuickFixModel(this._editor, markerService); + this._quickFixContextMenu = new QuickFixContextMenu(editor, contextMenuService, commandService); + this._lightBulbWidget = new LightBulbWidget(editor); + + this._updateLightBulbTitle(); + + this._disposables.push( + this._quickFixContextMenu.onDidExecuteCodeAction(_ => this._model.trigger('auto')), + this._lightBulbWidget.onClick(this._handleLightBulbSelect, this), + this._model.onDidChangeFixes(e => this._onQuickFixEvent(e)), + this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this) + ); + } + + public dispose(): void { + this._model.dispose(); + dispose(this._disposables); + } + + private _onQuickFixEvent(e: QuickFixComputeEvent): void { + if (e && e.type === 'manual') { + this._quickFixContextMenu.show(e.fixes, e.position); + + } else if (e && e.fixes) { + // auto magically triggered + // * update an existing list of code actions + // * manage light bulb + if (this._quickFixContextMenu.isVisible) { + this._quickFixContextMenu.show(e.fixes, e.position); + } else { + this._lightBulbWidget.model = e; + } + } else { + this._lightBulbWidget.hide(); + } + } + + public getId(): string { + return QuickFixController.ID; + } + + private _handleLightBulbSelect(coords: { x: number, y: number }): void { + this._quickFixContextMenu.show(this._lightBulbWidget.model.fixes, coords); + } + + public triggerFromEditorSelection(): void { + this._model.trigger('manual'); + } + + private _updateLightBulbTitle(): void { + const kb = this._keybindingService.lookupKeybinding(QuickFixAction.Id); + let title: string; + if (kb) { + title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel()); + } else { + title = nls.localize('quickFix', "Show Fixes"); + } + this._lightBulbWidget.title = title; + } +} + +@editorAction +export class QuickFixAction extends EditorAction { + + static Id = 'editor.action.quickFix'; + + constructor() { + super({ + id: QuickFixAction.Id, + label: nls.localize('quickfix.trigger.label', "Quick Fix"), + alias: 'Quick Fix', + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.US_DOT + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let controller = QuickFixController.get(editor); + if (controller) { + controller.triggerFromEditorSelection(); + } + } +} diff --git a/src/vs/editor/contrib/quickFix/browser/quickFixModel.ts b/src/vs/editor/contrib/quickFix/browser/quickFixModel.ts new file mode 100644 index 0000000000..26c7c04451 --- /dev/null +++ b/src/vs/editor/contrib/quickFix/browser/quickFixModel.ts @@ -0,0 +1,176 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event, { Emitter, debounceEvent } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { CodeActionProviderRegistry, Command } from 'vs/editor/common/modes'; +import { getCodeActions } from './quickFix'; +import { Position } from 'vs/editor/common/core/position'; + + +export class QuickFixOracle { + + private _disposables: IDisposable[] = []; + private _currentRange: Range; + + constructor( + private _editor: ICommonCodeEditor, + private _markerService: IMarkerService, + private _signalChange: (e: QuickFixComputeEvent) => any, + delay: number = 250 + ) { + this._disposables.push( + debounceEvent(this._markerService.onMarkerChanged, (last, cur) => last ? last.concat(cur) : cur, delay / 2)(e => this._onMarkerChanges(e)), + debounceEvent(this._editor.onDidChangeCursorPosition, last => last, delay)(e => this._onCursorChange()) + ); + } + + dispose(): void { + this._disposables = dispose(this._disposables); + } + + trigger(type: 'manual' | 'auto'): void { + + // get selection from marker or current word + // unless the selection is non-empty and manually + // requesting code actions + const range = (type === 'manual' && this._getRangeOfNonEmptySelection()) + || this._getRangeOfMarker() + || this._getRangeOfWord() + || this._editor.getSelection(); + + this._createEventAndSignalChange(type, range); + } + + private _onMarkerChanges(resources: URI[]): void { + const { uri } = this._editor.getModel(); + for (const resource of resources) { + if (resource.toString() === uri.toString()) { + this._currentRange = undefined; + this._onCursorChange(); + return; + } + } + } + + private _onCursorChange(): void { + const rangeOrSelection = this._getRangeOfMarker() || this._getRangeOfNonEmptySelection(); + if (!Range.equalsRange(this._currentRange, rangeOrSelection)) { + this._currentRange = rangeOrSelection; + this._createEventAndSignalChange('auto', rangeOrSelection); + } + } + + private _getRangeOfMarker(): Range { + const selection = this._editor.getSelection(); + const model = this._editor.getModel(); + for (const marker of this._markerService.read({ resource: model.uri })) { + if (Range.intersectRanges(marker, selection)) { + return Range.lift(marker); + } + } + return undefined; + } + + private _getRangeOfWord(): Range { + const pos = this._editor.getPosition(); + const info = this._editor.getModel().getWordAtPosition(pos); + return info ? new Range(pos.lineNumber, info.startColumn, pos.lineNumber, info.endColumn) : undefined; + } + + private _getRangeOfNonEmptySelection(): Selection { + const selection = this._editor.getSelection(); + return !selection.isEmpty() ? selection : undefined; + } + + private _createEventAndSignalChange(type: 'auto' | 'manual', rangeOrSelection: Range | Selection): void { + if (!rangeOrSelection) { + // cancel + this._signalChange({ + type, + range: undefined, + position: undefined, + fixes: undefined + }); + } else { + // actual + const model = this._editor.getModel(); + const range = model.validateRange(rangeOrSelection); + const position = rangeOrSelection instanceof Selection ? rangeOrSelection.getPosition() : rangeOrSelection.getStartPosition(); + this._signalChange({ + type, + range, + position, + fixes: getCodeActions(model, range) + }); + } + } +} + +export interface QuickFixComputeEvent { + type: 'auto' | 'manual'; + range: Range; + position: Position; + fixes: TPromise; +} + +export class QuickFixModel { + + private _editor: ICommonCodeEditor; + private _markerService: IMarkerService; + private _quickFixOracle: QuickFixOracle; + private _onDidChangeFixes = new Emitter(); + private _disposables: IDisposable[] = []; + + constructor(editor: ICommonCodeEditor, markerService: IMarkerService) { + this._editor = editor; + this._markerService = markerService; + + this._disposables.push(this._editor.onDidChangeModel(() => this._update())); + this._disposables.push(this._editor.onDidChangeModelLanguage(() => this._update())); + this._disposables.push(CodeActionProviderRegistry.onDidChange(this._update, this)); + + this._update(); + } + + dispose(): void { + this._disposables = dispose(this._disposables); + dispose(this._quickFixOracle); + } + + get onDidChangeFixes(): Event { + return this._onDidChangeFixes.event; + } + + private _update(): void { + + if (this._quickFixOracle) { + this._quickFixOracle.dispose(); + this._quickFixOracle = undefined; + this._onDidChangeFixes.fire(undefined); + } + + if (this._editor.getModel() + && CodeActionProviderRegistry.has(this._editor.getModel()) + && !this._editor.getConfiguration().readOnly) { + + this._quickFixOracle = new QuickFixOracle(this._editor, this._markerService, p => this._onDidChangeFixes.fire(p)); + this._quickFixOracle.trigger('auto'); + } + } + + trigger(type: 'auto' | 'manual'): void { + if (this._quickFixOracle) { + this._quickFixOracle.trigger(type); + } + } +} diff --git a/src/vs/editor/contrib/quickFix/browser/quickFixWidget.ts b/src/vs/editor/contrib/quickFix/browser/quickFixWidget.ts new file mode 100644 index 0000000000..4c0270b4dc --- /dev/null +++ b/src/vs/editor/contrib/quickFix/browser/quickFixWidget.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { always } from 'vs/base/common/async'; +import { getDomNodePagePosition } from 'vs/base/browser/dom'; +import { Position } from 'vs/editor/common/core/position'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Command } from 'vs/editor/common/modes'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { Action } from 'vs/base/common/actions'; +import Event, { Emitter } from 'vs/base/common/event'; +import { ScrollType } from 'vs/editor/common/editorCommon'; + +export class QuickFixContextMenu { + + private _editor: ICodeEditor; + private _contextMenuService: IContextMenuService; + private _commandService: ICommandService; + private _visible: boolean; + private _onDidExecuteCodeAction = new Emitter(); + + readonly onDidExecuteCodeAction: Event = this._onDidExecuteCodeAction.event; + + constructor(editor: ICodeEditor, contextMenuService: IContextMenuService, commandService: ICommandService) { + this._editor = editor; + this._contextMenuService = contextMenuService; + this._commandService = commandService; + } + + show(fixes: TPromise, at: { x: number; y: number } | Position) { + + const actions = fixes.then(value => { + return value.map(command => { + return new Action(command.id, command.title, undefined, true, () => { + return always( + this._commandService.executeCommand(command.id, ...command.arguments), + () => this._onDidExecuteCodeAction.fire(undefined) + ); + }); + }); + }); + + this._contextMenuService.showContextMenu({ + getAnchor: () => { + if (Position.isIPosition(at)) { + at = this._toCoords(at); + } + return at; + }, + getActions: () => actions, + onHide: () => { this._visible = false; } + }); + } + + get isVisible(): boolean { + return this._visible; + } + + private _toCoords(position: Position): { x: number, y: number } { + + this._editor.revealPosition(position, ScrollType.Immediate); + this._editor.render(); + + // Translate to absolute editor position + const cursorCoords = this._editor.getScrolledVisiblePosition(this._editor.getPosition()); + const editorCoords = getDomNodePagePosition(this._editor.getDomNode()); + const x = editorCoords.left + cursorCoords.left; + const y = editorCoords.top + cursorCoords.top + cursorCoords.height; + + return { x, y }; + } +} diff --git a/src/vs/editor/contrib/quickFix/test/browser/quickFixModel.test.ts b/src/vs/editor/contrib/quickFix/test/browser/quickFixModel.test.ts new file mode 100644 index 0000000000..5944fb5306 --- /dev/null +++ b/src/vs/editor/contrib/quickFix/test/browser/quickFixModel.test.ts @@ -0,0 +1,253 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Model } from 'vs/editor/common/model/model'; +import { mockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { QuickFixOracle } from 'vs/editor/contrib/quickFix/browser/quickFixModel'; +import { CodeActionProviderRegistry, LanguageIdentifier } from 'vs/editor/common/modes'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import Event from 'vs/base/common/event'; +import { Range } from 'vs/editor/common/core/range'; + +function promiseOnce(event: Event): TPromise { + return new TPromise(resolve => { + let reg = event(e => { + reg.dispose(); + resolve(e); + }); + }); +} + +suite('QuickFix', () => { + + const languageIdentifier = new LanguageIdentifier('foo-lang', 3); + let uri = URI.parse('untitled:path'); + let model: Model; + let markerService: MarkerService; + let editor: ICommonCodeEditor; + let reg: IDisposable; + + setup(() => { + reg = CodeActionProviderRegistry.register(languageIdentifier.language, { + provideCodeActions() { + return [{ id: 'test-command', title: 'test', arguments: [] }]; + } + }); + markerService = new MarkerService(); + model = Model.createFromString('foobar foo bar\nfarboo far boo', undefined, languageIdentifier, uri); + editor = mockCodeEditor([], { model }); + editor.setPosition({ lineNumber: 1, column: 1 }); + }); + + teardown(() => { + reg.dispose(); + editor.dispose(); + model.dispose(); + markerService.dispose(); + }); + + test('Orcale -> marker added', done => { + + const oracle = new QuickFixOracle(editor, markerService, e => { + assert.equal(e.type, 'auto'); + assert.ok(e.fixes); + + e.fixes.then(fixes => { + oracle.dispose(); + assert.equal(fixes.length, 1); + done(); + }, done); + }); + + // start here + markerService.changeOne('fake', uri, [{ + startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, + message: 'error', + severity: 1, + code: '', + source: '' + }]); + + }); + + test('Orcale -> position changed', done => { + + markerService.changeOne('fake', uri, [{ + startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, + message: 'error', + severity: 1, + code: '', + source: '' + }]); + + editor.setPosition({ lineNumber: 2, column: 1 }); + + const oracle = new QuickFixOracle(editor, markerService, e => { + assert.equal(e.type, 'auto'); + assert.ok(e.fixes); + + e.fixes.then(fixes => { + oracle.dispose(); + assert.equal(fixes.length, 1); + done(); + }, done); + }); + + // start here + editor.setPosition({ lineNumber: 1, column: 1 }); + + }); + + test('Oracle -> ask once per marker/word', async () => { + + const start = promiseOnce(markerService.onMarkerChanged); + + markerService.changeOne('fake', uri, [{ + startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, + message: 'error', + severity: 1, + code: '', + source: '' + }]); + + await start; + + let counter = 0; + let reg = CodeActionProviderRegistry.register(languageIdentifier.language, { + provideCodeActions() { + counter += 1; + return []; + } + }); + + let fixes: TPromise[] = []; + let oracle = new QuickFixOracle(editor, markerService, e => { + fixes.push(e.fixes); + }, 10); + + editor.setPosition({ lineNumber: 1, column: 3 }); // marker + editor.setPosition({ lineNumber: 1, column: 6 }); // (same) marker + + await TPromise.join([TPromise.timeout(20)].concat(fixes)); + assert.equal(counter, 1); + + // no auto trigger when empty selection + editor.setPosition({ lineNumber: 1, column: 8 }); // whitespace + editor.setPosition({ lineNumber: 2, column: 2 }); // word + editor.setPosition({ lineNumber: 2, column: 6 }); // (same) word + await TPromise.join([TPromise.timeout(20)].concat(fixes)); + assert.equal(counter, 1); + + // auto trigger on non-empty selection + editor.setSelection({ startLineNumber: 2, startColumn: 2, endLineNumber: 2, endColumn: 6 }); + await TPromise.join([TPromise.timeout(20)].concat(fixes)); + assert.equal(counter, 2); + + reg.dispose(); + oracle.dispose(); + }); + + test('Oracle -> selection wins over marker', () => { + + let range: Range; + let reg = CodeActionProviderRegistry.register(languageIdentifier.language, { + provideCodeActions(doc, _range) { + range = _range; + return []; + } + }); + + markerService.changeOne('fake', uri, [{ + startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, + message: 'error', + severity: 1, + code: '', + source: '' + }]); + + let fixes: TPromise[] = []; + let oracle = new QuickFixOracle(editor, markerService, e => { + fixes.push(e.fixes); + }, 10); + + editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 13 }); + + return TPromise.join([TPromise.timeout(20)].concat(fixes)).then(_ => { + + // -> marker wins + assert.deepEqual(range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6 }); + + // 'auto' triggered, non-empty selection BUT within a marker + editor.setSelection({ startLineNumber: 1, startColumn: 2, endLineNumber: 1, endColumn: 4 }); + + return TPromise.join([TPromise.timeout(20)].concat(fixes)).then(_ => { + reg.dispose(); + oracle.dispose(); + + // assert marker + assert.deepEqual(range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6 }); + }); + }); + }); + + test('Lightbulb is in the wrong place, #29933', async function () { + let reg = CodeActionProviderRegistry.register(languageIdentifier.language, { + provideCodeActions(doc, _range) { + return []; + } + }); + + editor.getModel().setValue('// @ts-check\n2\ncon\n'); + + markerService.changeOne('fake', uri, [{ + startLineNumber: 3, startColumn: 1, endLineNumber: 3, endColumn: 4, + message: 'error', + severity: 1, + code: '', + source: '' + }]); + + // case 1 - drag selection over multiple lines -> range of enclosed marker, position or marker + await new TPromise(resolve => { + + let oracle = new QuickFixOracle(editor, markerService, e => { + assert.equal(e.type, 'auto'); + assert.deepEqual(e.range, { startLineNumber: 3, startColumn: 1, endLineNumber: 3, endColumn: 4 }); + assert.deepEqual(e.position, { lineNumber: 3, column: 1 }); + + oracle.dispose(); + resolve(null); + }, 5); + + editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 }); + }); + + // case 2 - selection over multiple lines & manual trigger -> lightbulb + await new TPromise(resolve => { + + editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 }); + + let oracle = new QuickFixOracle(editor, markerService, e => { + assert.equal(e.type, 'manual'); + assert.ok(e.range.equalsRange({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 })); + + oracle.dispose(); + resolve(null); + }, 5); + + oracle.trigger('manual'); + }); + + + reg.dispose(); + }); + +}); diff --git a/src/vs/editor/contrib/quickOpen/common/quickOpen.ts b/src/vs/editor/contrib/quickOpen/common/quickOpen.ts new file mode 100644 index 0000000000..ec5126d1de --- /dev/null +++ b/src/vs/editor/contrib/quickOpen/common/quickOpen.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Range } from 'vs/editor/common/core/range'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { SymbolInformation, DocumentSymbolProviderRegistry, IOutline } from 'vs/editor/common/modes'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { asWinJsPromise } from 'vs/base/common/async'; + +export function getDocumentSymbols(model: IModel): TPromise { + + let entries: SymbolInformation[] = []; + + let promises = DocumentSymbolProviderRegistry.all(model).map(support => { + + return asWinJsPromise((token) => { + return support.provideDocumentSymbols(model, token); + }).then(result => { + if (Array.isArray(result)) { + entries.push(...result); + } + }, err => { + onUnexpectedExternalError(err); + }); + }); + + return TPromise.join(promises).then(() => { + let flatEntries: SymbolInformation[] = []; + flatten(flatEntries, entries, ''); + flatEntries.sort(compareEntriesUsingStart); + + return { + entries: flatEntries, + }; + }); +} + +function compareEntriesUsingStart(a: SymbolInformation, b: SymbolInformation): number { + return Range.compareRangesUsingStarts(Range.lift(a.location.range), Range.lift(b.location.range)); +} + +function flatten(bucket: SymbolInformation[], entries: SymbolInformation[], overrideContainerLabel: string): void { + for (let entry of entries) { + bucket.push({ + kind: entry.kind, + location: entry.location, + name: entry.name, + containerName: entry.containerName || overrideContainerLabel + }); + } +} + + +CommonEditorRegistry.registerLanguageCommand('_executeDocumentSymbolProvider', function (accessor, args) { + const { resource } = args; + if (!(resource instanceof URI)) { + throw illegalArgument('resource'); + } + const model = accessor.get(IModelService).getModel(resource); + if (!model) { + throw illegalArgument('resource'); + } + return getDocumentSymbols(model); +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.ts b/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.ts new file mode 100644 index 0000000000..85a262bd08 --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.ts @@ -0,0 +1,221 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IEditorService } from 'vs/platform/editor/common/editor'; +import { optional } from 'vs/platform/instantiation/common/instantiation'; +import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; +import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, EditorAction, CommonEditorRegistry, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions'; +import { Location, ReferenceProviderRegistry } from 'vs/editor/common/modes'; +import { IPeekViewService, PeekContext, getOuterEditor } from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget'; +import { ReferencesController, RequestOptions, ctxReferenceSearchVisible } from './referencesController'; +import { ReferencesModel } from './referencesModel'; +import { asWinJsPromise } from 'vs/base/common/async'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; + +const defaultReferenceSearchOptions: RequestOptions = { + getMetaTitle(model) { + return model.references.length > 1 && nls.localize('meta.titleReference', " – {0} references", model.references.length); + } +}; + +@commonEditorContribution +export class ReferenceController implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.referenceController'; + + constructor( + editor: editorCommon.ICommonCodeEditor, + @IContextKeyService contextKeyService: IContextKeyService, + @optional(IPeekViewService) peekViewService: IPeekViewService + ) { + if (peekViewService) { + PeekContext.inPeekEditor.bindTo(contextKeyService); + } + } + + public dispose(): void { + } + + public getId(): string { + return ReferenceController.ID; + } +} + +@editorAction +export class ReferenceAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.referenceSearch.trigger', + label: nls.localize('references.action.label', "Find All References"), + alias: 'Find All References', + precondition: ContextKeyExpr.and( + EditorContextKeys.hasReferenceProvider, + PeekContext.notInPeekEditor, + EditorContextKeys.isInEmbeddedEditor.toNegated()), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.F12 + }, + menuOpts: { + group: 'navigation', + order: 1.5 + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + let controller = ReferencesController.get(editor); + if (!controller) { + return; + } + let range = editor.getSelection(); + let model = editor.getModel(); + let references = provideReferences(model, range.getStartPosition()).then(references => new ReferencesModel(references)); + controller.toggleWidget(range, references, defaultReferenceSearchOptions); + } +} + +let findReferencesCommand: ICommandHandler = (accessor: ServicesAccessor, resource: URI, position: IPosition) => { + + if (!(resource instanceof URI)) { + throw new Error('illegal argument, uri'); + } + if (!position) { + throw new Error('illegal argument, position'); + } + + return accessor.get(IEditorService).openEditor({ resource }).then(editor => { + + let control = editor.getControl(); + if (!editorCommon.isCommonCodeEditor(control)) { + return undefined; + } + + let controller = ReferencesController.get(control); + if (!controller) { + return undefined; + } + + let references = provideReferences(control.getModel(), Position.lift(position)).then(references => new ReferencesModel(references)); + let range = new Range(position.lineNumber, position.column, position.lineNumber, position.column); + return TPromise.as(controller.toggleWidget(range, references, defaultReferenceSearchOptions)); + }); +}; + +let showReferencesCommand: ICommandHandler = (accessor: ServicesAccessor, resource: URI, position: IPosition, references: Location[]) => { + if (!(resource instanceof URI)) { + throw new Error('illegal argument, uri expected'); + } + + return accessor.get(IEditorService).openEditor({ resource: resource }).then(editor => { + + let control = editor.getControl(); + if (!editorCommon.isCommonCodeEditor(control)) { + return undefined; + } + + let controller = ReferencesController.get(control); + if (!controller) { + return undefined; + } + + return TPromise.as(controller.toggleWidget( + new Range(position.lineNumber, position.column, position.lineNumber, position.column), + TPromise.as(new ReferencesModel(references)), + defaultReferenceSearchOptions)).then(() => true); + }); +}; + + + +// register commands + +CommandsRegistry.registerCommand('editor.action.findReferences', findReferencesCommand); + +CommandsRegistry.registerCommand('editor.action.showReferences', { + handler: showReferencesCommand, + description: { + description: 'Show references at a position in a file', + args: [ + { name: 'uri', description: 'The text document in which to show references', constraint: URI }, + { name: 'position', description: 'The position at which to show', constraint: Position.isIPosition }, + { name: 'locations', description: 'An array of locations.', constraint: Array }, + ] + } +}); + +function closeActiveReferenceSearch(accessor: ServicesAccessor, args: any) { + var outerEditor = getOuterEditor(accessor); + if (!outerEditor) { + return; + } + + let controller = ReferencesController.get(outerEditor); + if (!controller) { + return; + } + + controller.closeWidget(); +} + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'closeReferenceSearch', + weight: CommonEditorRegistry.commandWeight(50), + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape], + when: ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek')), + handler: closeActiveReferenceSearch +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'closeReferenceSearchEditor', + weight: CommonEditorRegistry.commandWeight(-101), + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape], + when: ContextKeyExpr.and(PeekContext.inPeekEditor, ContextKeyExpr.not('config.editor.stablePeek')), + handler: closeActiveReferenceSearch +}); + + +export function provideReferences(model: editorCommon.IReadOnlyModel, position: Position): TPromise { + + // collect references from all providers + const promises = ReferenceProviderRegistry.ordered(model).map(provider => { + return asWinJsPromise((token) => { + return provider.provideReferences(model, position, { includeDeclaration: true }, token); + }).then(result => { + if (Array.isArray(result)) { + return result; + } + return undefined; + }, err => { + onUnexpectedExternalError(err); + }); + }); + + return TPromise.join(promises).then(references => { + let result: Location[] = []; + for (let ref of references) { + if (ref) { + result.push(...ref); + } + } + return result; + }); +} + +CommonEditorRegistry.registerDefaultLanguageCommand('_executeReferenceProvider', provideReferences); diff --git a/src/vs/editor/contrib/referenceSearch/browser/referencesController.ts b/src/vs/editor/contrib/referenceSearch/browser/referencesController.ts new file mode 100644 index 0000000000..afe06ce494 --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/browser/referencesController.ts @@ -0,0 +1,262 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import Severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IEditorService } from 'vs/platform/editor/common/editor'; +import { fromPromise, stopwatch } from 'vs/base/common/event'; +import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { IPeekViewService } from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget'; +import { ReferencesModel, OneReference } from './referencesModel'; +import { ReferenceWidget, LayoutData } from './referencesWidget'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { Position } from 'vs/editor/common/core/position'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + +export const ctxReferenceSearchVisible = new RawContextKey('referenceSearchVisible', false); + +export interface RequestOptions { + getMetaTitle(model: ReferencesModel): string; + onGoto?: (reference: OneReference) => TPromise; +} + +@editorContribution +export class ReferencesController implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.referencesController'; + + private _editor: ICodeEditor; + private _widget: ReferenceWidget; + private _model: ReferencesModel; + private _requestIdPool = 0; + private _disposables: IDisposable[] = []; + private _ignoreModelChangeEvent = false; + + private _referenceSearchVisible: IContextKey; + + public static get(editor: editorCommon.ICommonCodeEditor): ReferencesController { + return editor.getContribution(ReferencesController.ID); + } + + public constructor( + editor: ICodeEditor, + @IContextKeyService contextKeyService: IContextKeyService, + @IEditorService private _editorService: IEditorService, + @ITextModelService private _textModelResolverService: ITextModelService, + @ITelemetryService private _telemetryService: ITelemetryService, + @IMessageService private _messageService: IMessageService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IWorkspaceContextService private _contextService: IWorkspaceContextService, + @IStorageService private _storageService: IStorageService, + @IThemeService private _themeService: IThemeService, + @IConfigurationService private _configurationService: IConfigurationService, + @optional(IPeekViewService) private _peekViewService: IPeekViewService, + @optional(IEnvironmentService) private _environmentService: IEnvironmentService + ) { + this._editor = editor; + this._referenceSearchVisible = ctxReferenceSearchVisible.bindTo(contextKeyService); + } + + public getId(): string { + return ReferencesController.ID; + } + + public dispose(): void { + if (this._widget) { + this._widget.dispose(); + this._widget = null; + } + this._editor = null; + } + + public toggleWidget(range: Range, modelPromise: TPromise, options: RequestOptions): void { + + // close current widget and return early is position didn't change + let widgetPosition: Position; + if (this._widget) { + widgetPosition = this._widget.position; + } + this.closeWidget(); + if (!!widgetPosition && range.containsPosition(widgetPosition)) { + return null; + } + + this._referenceSearchVisible.set(true); + + // close the widget on model/mode changes + this._disposables.push(this._editor.onDidChangeModelLanguage(() => { this.closeWidget(); })); + this._disposables.push(this._editor.onDidChangeModel(() => { + if (!this._ignoreModelChangeEvent) { + this.closeWidget(); + } + })); + const storageKey = 'peekViewLayout'; + const data = JSON.parse(this._storageService.get(storageKey, undefined, '{}')); + this._widget = new ReferenceWidget(this._editor, data, this._textModelResolverService, this._contextService, this._themeService, this._instantiationService, this._environmentService); + this._widget.setTitle(nls.localize('labelLoading', "Loading...")); + this._widget.show(range); + this._disposables.push(this._widget.onDidClose(() => { + modelPromise.cancel(); + + this._storageService.store(storageKey, JSON.stringify(this._widget.layoutData)); + this._widget = null; + this.closeWidget(); + })); + + this._disposables.push(this._widget.onDidSelectReference(event => { + let { element, kind } = event; + switch (kind) { + case 'open': + if (event.source === 'editor' + && this._configurationService.lookup('editor.stablePeek').value) { + + // when stable peek is configured we don't close + // the peek window on selecting the editor + break; + } + case 'side': + this._openReference(element, kind === 'side'); + break; + case 'goto': + if (options.onGoto) { + options.onGoto(element); + } else { + this._gotoReference(element); + } + break; + } + })); + + const requestId = ++this._requestIdPool; + + const promise = modelPromise.then(model => { + + // still current request? widget still open? + if (requestId !== this._requestIdPool || !this._widget) { + return undefined; + } + + if (this._model) { + this._model.dispose(); + } + + this._model = model; + + // measure time it stays open + const startTime = Date.now(); + this._disposables.push({ + dispose: () => { + this._telemetryService.publicLog('zoneWidgetShown', { + mode: 'reference search', + elapsedTime: Date.now() - startTime + }); + } + }); + + // show widget + return this._widget.setModel(this._model).then(() => { + + // set title + this._widget.setMetaTitle(options.getMetaTitle(this._model)); + + // set 'best' selection + let uri = this._editor.getModel().uri; + let pos = new Position(range.startLineNumber, range.startColumn); + let selection = this._model.nearestReference(uri, pos); + if (selection) { + return this._widget.setSelection(selection); + } + return undefined; + }); + + }, error => { + this._messageService.show(Severity.Error, error); + }); + + const onDone = stopwatch(fromPromise(promise)); + const mode = this._editor.getModel().getLanguageIdentifier().language; + + onDone(duration => this._telemetryService.publicLog('findReferences', { + duration, + mode + })); + } + + public closeWidget(): void { + if (this._widget) { + this._widget.dispose(); + this._widget = null; + } + this._referenceSearchVisible.reset(); + this._disposables = dispose(this._disposables); + if (this._model) { + this._model.dispose(); + this._model = null; + } + this._editor.focus(); + this._requestIdPool += 1; // Cancel pending requests + } + + private _gotoReference(ref: OneReference): void { + this._widget.hide(); + + this._ignoreModelChangeEvent = true; + const { uri, range } = ref; + + this._editorService.openEditor({ + resource: uri, + options: { selection: range } + }).done(openedEditor => { + this._ignoreModelChangeEvent = false; + + if (!openedEditor || openedEditor.getControl() !== this._editor) { + // TODO@Alex TODO@Joh + // when opening the current reference we might end up + // in a different editor instance. that means we also have + // a different instance of this reference search controller + // and cannot hold onto the widget (which likely doesn't + // exist). Instead of bailing out we should find the + // 'sister' action and pass our current model on to it. + this.closeWidget(); + return; + } + + this._widget.show(range); + this._widget.focus(); + + }, (err) => { + this._ignoreModelChangeEvent = false; + onUnexpectedError(err); + }); + } + + private _openReference(ref: OneReference, sideBySide: boolean): void { + const { uri, range } = ref; + this._editorService.openEditor({ + resource: uri, + options: { selection: range } + }, sideBySide); + + // clear stage + if (!sideBySide) { + this.closeWidget(); + } + } +} diff --git a/src/vs/editor/contrib/referenceSearch/browser/referencesModel.ts b/src/vs/editor/contrib/referenceSearch/browser/referencesModel.ts new file mode 100644 index 0000000000..9f13f88f30 --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/browser/referencesModel.ts @@ -0,0 +1,313 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { localize } from 'vs/nls'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import Event, { fromEventEmitter } from 'vs/base/common/event'; +import { basename, dirname } from 'vs/base/common/paths'; +import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; +import * as strings from 'vs/base/common/strings'; +import URI from 'vs/base/common/uri'; +import { defaultGenerator } from 'vs/base/common/idGenerator'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { Location } from 'vs/editor/common/modes'; +import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { Position } from 'vs/editor/common/core/position'; + +export class OneReference { + + private _id: string; + + constructor( + private _parent: FileReferences, + private _range: IRange, + private _eventBus: EventEmitter + ) { + this._id = defaultGenerator.nextId(); + } + + public get id(): string { + return this._id; + } + + public get model(): FileReferences { + return this._parent; + } + + public get parent(): FileReferences { + return this._parent; + } + + public get uri(): URI { + return this._parent.uri; + } + + public get name(): string { + return this._parent.name; + } + + public get directory(): string { + return this._parent.directory; + } + + public get range(): IRange { + return this._range; + } + + public set range(value: IRange) { + this._range = value; + this._eventBus.emit('ref/changed', this); + } + + public getAriaMessage(): string { + return localize( + 'aria.oneReference', "symbol in {0} on line {1} at column {2}", + basename(this.uri.fsPath), this.range.startLineNumber, this.range.startColumn + ); + } +} + +export class FilePreview implements IDisposable { + + constructor(private _modelReference: IReference) { + + } + + private get _model() { return this._modelReference.object.textEditorModel; } + + public preview(range: IRange, n: number = 8): { before: string; inside: string; after: string } { + const model = this._model; + + if (!model) { + return undefined; + } + + const { startLineNumber, startColumn, endLineNumber, endColumn } = range; + const word = model.getWordUntilPosition({ lineNumber: startLineNumber, column: startColumn - n }); + const beforeRange = new Range(startLineNumber, word.startColumn, startLineNumber, startColumn); + const afterRange = new Range(endLineNumber, endColumn, endLineNumber, Number.MAX_VALUE); + + const ret = { + before: model.getValueInRange(beforeRange).replace(/^\s+/, strings.empty), + inside: model.getValueInRange(range), + after: model.getValueInRange(afterRange).replace(/\s+$/, strings.empty) + }; + + return ret; + } + + dispose(): void { + if (this._modelReference) { + this._modelReference.dispose(); + this._modelReference = null; + } + } +} + +export class FileReferences implements IDisposable { + + private _children: OneReference[]; + private _preview: FilePreview; + private _resolved: boolean; + private _loadFailure: any; + + constructor(private _parent: ReferencesModel, private _uri: URI) { + this._children = []; + } + + public get id(): string { + return this._uri.toString(); + } + + public get parent(): ReferencesModel { + return this._parent; + } + + public get children(): OneReference[] { + return this._children; + } + + public get uri(): URI { + return this._uri; + } + + public get name(): string { + return basename(this.uri.fsPath); + } + + public get directory(): string { + return dirname(this.uri.fsPath); + } + + public get preview(): FilePreview { + return this._preview; + } + + public get failure(): any { + return this._loadFailure; + } + + getAriaMessage(): string { + const len = this.children.length; + if (len === 1) { + return localize('aria.fileReferences.1', "1 symbol in {0}, full path {1}", basename(this.uri.fsPath), this.uri.fsPath); + } else { + return localize('aria.fileReferences.N', "{0} symbols in {1}, full path {2}", len, basename(this.uri.fsPath), this.uri.fsPath); + } + } + + public resolve(textModelResolverService: ITextModelService): TPromise { + + if (this._resolved) { + return TPromise.as(this); + } + + return textModelResolverService.createModelReference(this._uri).then(modelReference => { + const model = modelReference.object; + + if (!model) { + modelReference.dispose(); + throw new Error(); + } + + this._preview = new FilePreview(modelReference); + this._resolved = true; + return this; + + }, err => { + // something wrong here + this._children = []; + this._resolved = true; + this._loadFailure = err; + return this; + }); + } + + dispose(): void { + if (this._preview) { + this._preview.dispose(); + this._preview = null; + } + } +} + +export class ReferencesModel implements IDisposable { + + private _groups: FileReferences[] = []; + private _references: OneReference[] = []; + private _eventBus = new EventEmitter(); + + onDidChangeReferenceRange: Event = fromEventEmitter(this._eventBus, 'ref/changed'); + + constructor(references: Location[]) { + + // grouping and sorting + references.sort(ReferencesModel._compareReferences); + + let current: FileReferences; + for (let ref of references) { + if (!current || current.uri.toString() !== ref.uri.toString()) { + // new group + current = new FileReferences(this, ref.uri); + this.groups.push(current); + } + + // append, check for equality first! + if (current.children.length === 0 + || !Range.equalsRange(ref.range, current.children[current.children.length - 1].range)) { + + let oneRef = new OneReference(current, ref.range, this._eventBus); + this._references.push(oneRef); + current.children.push(oneRef); + } + } + } + + public get empty(): boolean { + return this._groups.length === 0; + } + + public get references(): OneReference[] { + return this._references; + } + + public get groups(): FileReferences[] { + return this._groups; + } + + getAriaMessage(): string { + if (this.empty) { + return localize('aria.result.0', "No results found"); + } else if (this.references.length === 1) { + return localize('aria.result.1', "Found 1 symbol in {0}", this.references[0].uri.fsPath); + } else if (this.groups.length === 1) { + return localize('aria.result.n1', "Found {0} symbols in {1}", this.references.length, this.groups[0].uri.fsPath); + } else { + return localize('aria.result.nm', "Found {0} symbols in {1} files", this.references.length, this.groups.length); + } + } + + public nextReference(reference: OneReference): OneReference { + + var idx = reference.parent.children.indexOf(reference), + len = reference.parent.children.length, + totalLength = reference.parent.parent.groups.length; + + if (idx + 1 < len || totalLength === 1) { + return reference.parent.children[(idx + 1) % len]; + } + + idx = reference.parent.parent.groups.indexOf(reference.parent); + idx = (idx + 1) % totalLength; + + return reference.parent.parent.groups[idx].children[0]; + } + + public nearestReference(resource: URI, position: Position): OneReference { + + const nearest = this._references.map((ref, idx) => { + return { + idx, + prefixLen: strings.commonPrefixLength(ref.uri.toString(), resource.toString()), + offsetDist: Math.abs(ref.range.startLineNumber - position.lineNumber) * 100 + Math.abs(ref.range.startColumn - position.column) + }; + }).sort((a, b) => { + if (a.prefixLen > b.prefixLen) { + return -1; + } else if (a.prefixLen < b.prefixLen) { + return 1; + } else if (a.offsetDist < b.offsetDist) { + return -1; + } else if (a.offsetDist > b.offsetDist) { + return 1; + } else { + return 0; + } + })[0]; + + if (nearest) { + return this._references[nearest.idx]; + } + return undefined; + } + + dispose(): void { + this._groups = dispose(this._groups); + } + + private static _compareReferences(a: Location, b: Location): number { + const auri = a.uri.toString(); + const buri = b.uri.toString(); + if (auri < buri) { + return -1; + } else if (auri > buri) { + return 1; + } else { + return Range.compareRangesUsingStarts(a.range, b.range); + } + } +} diff --git a/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.css b/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.css new file mode 100644 index 0000000000..0751381728 --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.css @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* -- zone widget */ +.monaco-editor .zone-widget .zone-widget-container.reference-zone-widget { + border-top-width: 1px; + border-bottom-width: 1px; +} + +.monaco-editor .zone-widget .zone-widget-container.reference-zone-widget.results-loaded { + -webkit-transition: height 100ms ease-in; + transition: height 100ms ease-in; +} + +.monaco-editor .reference-zone-widget .inline { + display: inline-block; + vertical-align: top; +} + +.monaco-editor .reference-zone-widget .messages { + height: 100%; + width: 100%; + text-align: center; + padding: 3em 0; +} + +.monaco-editor .reference-zone-widget .ref-tree { + line-height: 22px; + font-size: 13px; +} + +.monaco-editor .reference-zone-widget .ref-tree .reference { + text-overflow: ellipsis; + overflow: hidden; +} + +.monaco-editor .reference-zone-widget .ref-tree .reference-file { + display: flex; + justify-content: space-between; + align-items: center; +} + +.monaco-editor .reference-zone-widget .monaco-count-badge { + margin-right: .5em; + height: 15px; + padding: 0 .5em .5em .5em +} + +/* High Contrast Theming */ + +.monaco-editor.hc-black .reference-zone-widget .ref-tree .reference-file { + font-weight: bold; + display: flex; + justify-content: space-between; +} diff --git a/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts new file mode 100644 index 0000000000..5b7e04a4c9 --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts @@ -0,0 +1,923 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./referencesWidget'; +import * as nls from 'vs/nls'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { getPathLabel } from 'vs/base/common/labels'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import * as strings from 'vs/base/common/strings'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Color } from 'vs/base/common/color'; +import { $, Builder } from 'vs/base/browser/builder'; +import * as dom from 'vs/base/browser/dom'; +import { Sash, ISashEvent, IVerticalSashLayoutProvider } from 'vs/base/browser/ui/sash/sash'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { GestureEvent } from 'vs/base/browser/touch'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { FileLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import * as tree from 'vs/base/parts/tree/browser/tree'; +import { DefaultController } from 'vs/base/parts/tree/browser/treeDefaults'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Model } from 'vs/editor/common/model/model'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { PeekViewWidget, IPeekViewService } from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget'; +import { FileReferences, OneReference, ReferencesModel } from './referencesModel'; +import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { registerColor, activeContrastBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant, ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachListStyler, attachBadgeStyler } from 'vs/platform/theme/common/styler'; +import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import URI from 'vs/base/common/uri'; + +class DecorationsManager implements IDisposable { + + private static DecorationOptions = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'reference-decoration' + }); + + private _decorations = new Map(); + private _decorationIgnoreSet = new Set(); + private _callOnDispose: IDisposable[] = []; + private _callOnModelChange: IDisposable[] = []; + + constructor(private _editor: ICodeEditor, private _model: ReferencesModel) { + this._callOnDispose.push(this._editor.onDidChangeModel(() => this._onModelChanged())); + this._onModelChanged(); + } + + public dispose(): void { + this._callOnModelChange = dispose(this._callOnModelChange); + this._callOnDispose = dispose(this._callOnDispose); + this.removeDecorations(); + } + + private _onModelChanged(): void { + this._callOnModelChange = dispose(this._callOnModelChange); + const model = this._editor.getModel(); + if (model) { + for (const ref of this._model.groups) { + if (ref.uri.toString() === model.uri.toString()) { + this._addDecorations(ref); + return; + } + } + } + } + + private _addDecorations(reference: FileReferences): void { + this._callOnModelChange.push(this._editor.getModel().onDidChangeDecorations((event) => this._onDecorationChanged(event))); + + this._editor.changeDecorations(accessor => { + + const newDecorations: editorCommon.IModelDeltaDecoration[] = []; + const newDecorationsActualIndex: number[] = []; + + for (let i = 0, len = reference.children.length; i < len; i++) { + let oneReference = reference.children[i]; + if (this._decorationIgnoreSet.has(oneReference.id)) { + continue; + } + newDecorations.push({ + range: oneReference.range, + options: DecorationsManager.DecorationOptions + }); + newDecorationsActualIndex.push(i); + } + + const decorations = accessor.deltaDecorations([], newDecorations); + for (let i = 0; i < decorations.length; i++) { + this._decorations.set(decorations[i], reference.children[newDecorationsActualIndex[i]]); + } + }); + } + + private _onDecorationChanged(event: IModelDecorationsChangedEvent): void { + const changedDecorations = event.changedDecorations, + toRemove: string[] = []; + + for (let i = 0, len = changedDecorations.length; i < len; i++) { + let reference = this._decorations.get(changedDecorations[i]); + if (!reference) { + continue; + } + + const newRange = this._editor.getModel().getDecorationRange(changedDecorations[i]); + let ignore = false; + + if (Range.equalsRange(newRange, reference.range)) { + continue; + + } else if (Range.spansMultipleLines(newRange)) { + ignore = true; + + } else { + const lineLength = reference.range.endColumn - reference.range.startColumn; + const newLineLength = newRange.endColumn - newRange.startColumn; + + if (lineLength !== newLineLength) { + ignore = true; + } + } + + if (ignore) { + this._decorationIgnoreSet.add(reference.id); + toRemove.push(changedDecorations[i]); + } else { + reference.range = newRange; + } + } + + this._editor.changeDecorations((accessor) => { + for (let i = 0, len = toRemove.length; i < len; i++) { + this._decorations.delete(toRemove[i]); + } + accessor.deltaDecorations(toRemove, []); + }); + } + + public removeDecorations(): void { + this._editor.changeDecorations(accessor => { + this._decorations.forEach((value, key) => { + accessor.removeDecoration(key); + }); + this._decorations.clear(); + }); + } +} + +class DataSource implements tree.IDataSource { + + constructor( + @ITextModelService private _textModelResolverService: ITextModelService + ) { + // + } + + public getId(tree: tree.ITree, element: any): string { + if (element instanceof ReferencesModel) { + return 'root'; + } else if (element instanceof FileReferences) { + return (element).id; + } else if (element instanceof OneReference) { + return (element).id; + } + return undefined; + } + + public hasChildren(tree: tree.ITree, element: any): boolean { + if (element instanceof ReferencesModel) { + return true; + } + if (element instanceof FileReferences && !(element).failure) { + return true; + } + return false; + } + + public getChildren(tree: tree.ITree, element: ReferencesModel | FileReferences): TPromise { + if (element instanceof ReferencesModel) { + return TPromise.as(element.groups); + } else if (element instanceof FileReferences) { + return element.resolve(this._textModelResolverService).then(val => { + if (element.failure) { + // refresh the element on failure so that + // we can update its rendering + return tree.refresh(element).then(() => val.children); + } + return val.children; + }); + } else { + return TPromise.as([]); + } + } + + public getParent(tree: tree.ITree, element: any): TPromise { + var result: any = null; + if (element instanceof FileReferences) { + result = (element).parent; + } else if (element instanceof OneReference) { + result = (element).parent; + } + return TPromise.as(result); + } +} + +class Controller extends DefaultController { + + static Events = { + FOCUSED: 'events/custom/focused', + SELECTED: 'events/custom/selected', + OPEN_TO_SIDE: 'events/custom/opentoside' + }; + + public onTap(tree: tree.ITree, element: any, event: GestureEvent): boolean { + if (element instanceof FileReferences) { + event.preventDefault(); + event.stopPropagation(); + return this._expandCollapse(tree, element); + } + + var result = super.onTap(tree, element, event); + tree.emit(Controller.Events.FOCUSED, element); + return result; + } + + public onMouseDown(tree: tree.ITree, element: any, event: IMouseEvent): boolean { + if (event.leftButton) { + if (element instanceof FileReferences) { + event.preventDefault(); + event.stopPropagation(); + return this._expandCollapse(tree, element); + } + + var result = super.onClick(tree, element, event); + if (event.ctrlKey || event.metaKey) { + tree.emit(Controller.Events.OPEN_TO_SIDE, element); + } else if (event.detail === 2) { + tree.emit(Controller.Events.SELECTED, element); + } else { + tree.emit(Controller.Events.FOCUSED, element); + } + return result; + } + + return false; + } + + public onClick(tree: tree.ITree, element: any, event: IMouseEvent): boolean { + if (event.leftButton) { + return false; // Already handled by onMouseDown + } + + return super.onClick(tree, element, event); + } + + private _expandCollapse(tree: tree.ITree, element: any): boolean { + + if (tree.isExpanded(element)) { + tree.collapse(element).done(null, onUnexpectedError); + } else { + tree.expand(element).done(null, onUnexpectedError); + } + return true; + } + + public onEscape(tree: tree.ITree, event: IKeyboardEvent): boolean { + return false; + } + + public onEnter(tree: tree.ITree, event: IKeyboardEvent): boolean { + var element = tree.getFocus(); + if (element instanceof FileReferences) { + return this._expandCollapse(tree, element); + } + + var result = super.onEnter(tree, event); + if (event.ctrlKey || event.metaKey) { + tree.emit(Controller.Events.OPEN_TO_SIDE, element); + } else { + tree.emit(Controller.Events.SELECTED, element); + } + return result; + } + + public onUp(tree: tree.ITree, event: IKeyboardEvent): boolean { + super.onUp(tree, event); + this._fakeFocus(tree, event); + return true; + } + + public onPageUp(tree: tree.ITree, event: IKeyboardEvent): boolean { + super.onPageUp(tree, event); + this._fakeFocus(tree, event); + return true; + } + + public onLeft(tree: tree.ITree, event: IKeyboardEvent): boolean { + super.onLeft(tree, event); + this._fakeFocus(tree, event); + return true; + } + + public onDown(tree: tree.ITree, event: IKeyboardEvent): boolean { + super.onDown(tree, event); + this._fakeFocus(tree, event); + return true; + } + + public onPageDown(tree: tree.ITree, event: IKeyboardEvent): boolean { + super.onPageDown(tree, event); + this._fakeFocus(tree, event); + return true; + } + + public onRight(tree: tree.ITree, event: IKeyboardEvent): boolean { + super.onRight(tree, event); + this._fakeFocus(tree, event); + return true; + } + + private _fakeFocus(tree: tree.ITree, event: IKeyboardEvent): void { + // focus next item + var focus = tree.getFocus(); + tree.setSelection([focus]); + // send out event + tree.emit(Controller.Events.FOCUSED, focus); + } +} + +class FileReferencesTemplate { + + readonly file: FileLabel; + readonly badge: CountBadge; + readonly dispose: () => void; + + constructor( + container: HTMLElement, + @IWorkspaceContextService private _contextService: IWorkspaceContextService, + @optional(IEnvironmentService) private _environmentService: IEnvironmentService, + @IThemeService themeService: IThemeService, + ) { + const parent = document.createElement('div'); + dom.addClass(parent, 'reference-file'); + container.appendChild(parent); + + this.file = new FileLabel(parent, URI.parse('no:file'), this._contextService, this._environmentService); + this.badge = new CountBadge(parent); + const styler = attachBadgeStyler(this.badge, themeService); + this.dispose = () => styler.dispose(); + } + + set(element: FileReferences) { + this.file.setFile(element.uri, this._contextService, this._environmentService); + const len = element.children.length; + this.badge.setCount(len); + if (element.failure) { + this.badge.setTitleFormat(nls.localize('referencesFailre', "Failed to resolve file.")); + } else if (len > 1) { + this.badge.setTitleFormat(nls.localize('referencesCount', "{0} references", len)); + } else { + this.badge.setTitleFormat(nls.localize('referenceCount', "{0} reference", len)); + } + } +} + +class OneReferenceTemplate { + + readonly before: HTMLSpanElement; + readonly inside: HTMLSpanElement; + readonly after: HTMLSpanElement; + + constructor(container: HTMLElement) { + const parent = document.createElement('div'); + this.before = document.createElement('span'); + this.inside = document.createElement('span'); + this.after = document.createElement('span'); + dom.addClass(this.inside, 'referenceMatch'); + dom.addClass(parent, 'reference'); + parent.appendChild(this.before); + parent.appendChild(this.inside); + parent.appendChild(this.after); + container.appendChild(parent); + } + + set(element: OneReference): void { + const { before, inside, after } = element.parent.preview.preview(element.range); + this.before.innerHTML = strings.escape(before); + this.inside.innerHTML = strings.escape(inside); + this.after.innerHTML = strings.escape(after); + } +} + +class Renderer implements tree.IRenderer { + + private static _ids = { + FileReferences: 'FileReferences', + OneReference: 'OneReference' + }; + + constructor( + @IWorkspaceContextService private _contextService: IWorkspaceContextService, + @IThemeService private _themeService: IThemeService, + @optional(IEnvironmentService) private _environmentService: IEnvironmentService, + ) { + // + } + + getHeight(tree: tree.ITree, element: FileReferences | OneReference): number { + return 22; + } + + getTemplateId(tree: tree.ITree, element: FileReferences | OneReference): string { + if (element instanceof FileReferences) { + return Renderer._ids.FileReferences; + } else if (element instanceof OneReference) { + return Renderer._ids.OneReference; + } + throw element; + } + + renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement) { + if (templateId === Renderer._ids.FileReferences) { + return new FileReferencesTemplate(container, this._contextService, this._environmentService, this._themeService); + } else if (templateId === Renderer._ids.OneReference) { + return new OneReferenceTemplate(container); + } + throw templateId; + } + + renderElement(tree: tree.ITree, element: FileReferences | OneReference, templateId: string, templateData: any): void { + if (element instanceof FileReferences) { + (templateData).set(element); + } else if (element instanceof OneReference) { + (templateData).set(element); + } else { + throw templateId; + } + } + + disposeTemplate(tree: tree.ITree, templateId: string, templateData: FileReferencesTemplate | OneReferenceTemplate): void { + if (templateData instanceof FileReferencesTemplate) { + templateData.dispose(); + } + } +} + +class AriaProvider implements tree.IAccessibilityProvider { + + getAriaLabel(tree: tree.ITree, element: FileReferences | OneReference): string { + if (element instanceof FileReferences) { + return element.getAriaMessage(); + } else if (element instanceof OneReference) { + return element.getAriaMessage(); + } else { + return undefined; + } + } +} + +class VSash { + + private _disposables: IDisposable[] = []; + private _sash: Sash; + private _ratio: number; + private _height: number; + private _width: number; + private _onDidChangePercentages = new Emitter(); + + constructor(container: HTMLElement, ratio: number) { + this._ratio = ratio; + this._sash = new Sash(container, { + getVerticalSashLeft: () => this._width * this._ratio, + getVerticalSashHeight: () => this._height + }); + + // compute the current widget clientX postion since + // the sash works with clientX when dragging + let clientX: number; + this._disposables.push(this._sash.addListener('start', (e: ISashEvent) => { + clientX = e.startX - (this._width * this.ratio); + })); + + this._disposables.push(this._sash.addListener('change', (e: ISashEvent) => { + // compute the new position of the sash and from that + // compute the new ratio that we are using + let newLeft = e.currentX - clientX; + if (newLeft > 20 && newLeft + 20 < this._width) { + this._ratio = newLeft / this._width; + this._sash.layout(); + this._onDidChangePercentages.fire(this); + } + })); + } + + dispose() { + this._sash.dispose(); + this._onDidChangePercentages.dispose(); + dispose(this._disposables); + } + + get onDidChangePercentages() { + return this._onDidChangePercentages.event; + } + + set width(value: number) { + this._width = value; + this._sash.layout(); + } + + set height(value: number) { + this._height = value; + this._sash.layout(); + } + + get percentages() { + let left = 100 * this._ratio; + let right = 100 - left; + return [`${left}%`, `${right}%`]; + } + + get ratio() { + return this._ratio; + } +} + +export interface LayoutData { + ratio: number; + heightInLines: number; +} + +export interface SelectionEvent { + kind: 'goto' | 'show' | 'side' | 'open'; + source: 'editor' | 'tree' | 'title'; + element: OneReference; +} + +/** + * ZoneWidget that is shown inside the editor + */ +export class ReferenceWidget extends PeekViewWidget { + + private _model: ReferencesModel; + private _decorationsManager: DecorationsManager; + + private _disposeOnNewModel: IDisposable[] = []; + private _callOnDispose: IDisposable[] = []; + private _onDidSelectReference = new Emitter(); + + private _tree: Tree; + private _treeContainer: Builder; + private _sash: VSash; + private _preview: ICodeEditor; + private _previewModelReference: IReference; + private _previewNotAvailableMessage: Model; + private _previewContainer: Builder; + private _messageContainer: Builder; + + constructor( + editor: ICodeEditor, + public layoutData: LayoutData, + private _textModelResolverService: ITextModelService, + private _contextService: IWorkspaceContextService, + private _themeService: IThemeService, + private _instantiationService: IInstantiationService, + private _environmentService: IEnvironmentService + ) { + super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true }); + + this._applyTheme(_themeService.getTheme()); + this._callOnDispose.push(_themeService.onThemeChange(this._applyTheme.bind(this))); + + this._instantiationService = this._instantiationService.createChild(new ServiceCollection([IPeekViewService, this])); + this.create(); + } + + private _applyTheme(theme: ITheme) { + let borderColor = theme.getColor(peekViewBorder) || Color.transparent; + this.style({ + arrowColor: borderColor, + frameColor: borderColor, + headerBackgroundColor: theme.getColor(peekViewTitleBackground) || Color.transparent, + primaryHeadingColor: theme.getColor(peekViewTitleForeground), + secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) + }); + } + + public dispose(): void { + this.setModel(null); + this._callOnDispose = dispose(this._callOnDispose); + dispose(this._preview, this._previewNotAvailableMessage, this._tree, this._sash, this._previewModelReference); + super.dispose(); + } + + get onDidSelectReference(): Event { + return this._onDidSelectReference.event; + } + + show(where: IRange) { + this.editor.revealRangeInCenterIfOutsideViewport(where, editorCommon.ScrollType.Smooth); + super.show(where, this.layoutData.heightInLines || 18); + } + + focus(): void { + this._tree.DOMFocus(); + } + + protected _onTitleClick(e: MouseEvent): void { + if (this._preview && this._preview.getModel()) { + this._onDidSelectReference.fire({ + element: this._getFocusedReference(), + kind: e.ctrlKey || e.metaKey ? 'side' : 'open', + source: 'title' + }); + } + } + + protected _fillBody(containerElement: HTMLElement): void { + var container = $(containerElement); + + this.setCssClass('reference-zone-widget'); + + // message pane + container.div({ 'class': 'messages' }, div => { + this._messageContainer = div.hide(); + }); + + // editor + container.div({ 'class': 'preview inline' }, (div: Builder) => { + + var options: IEditorOptions = { + scrollBeyondLastLine: false, + scrollbar: { + verticalScrollbarSize: 14, + horizontal: 'auto', + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false + }, + overviewRulerLanes: 2, + fixedOverflowWidgets: true, + minimap: { + enabled: false + } + }; + + this._preview = this._instantiationService.createInstance(EmbeddedCodeEditorWidget, div.getHTMLElement(), options, this.editor); + this._previewContainer = div.hide(); + this._previewNotAvailableMessage = Model.createFromString(nls.localize('missingPreviewMessage', "no preview available")); + }); + + // sash + this._sash = new VSash(containerElement, this.layoutData.ratio || .8); + this._sash.onDidChangePercentages(() => { + let [left, right] = this._sash.percentages; + this._previewContainer.style({ width: left }); + this._treeContainer.style({ width: right }); + this._preview.layout(); + this._tree.layout(); + this.layoutData.ratio = this._sash.ratio; + }); + + // tree + container.div({ 'class': 'ref-tree inline' }, (div: Builder) => { + var config = { + dataSource: this._instantiationService.createInstance(DataSource), + renderer: this._instantiationService.createInstance(Renderer), + controller: new Controller(), + accessibilityProvider: new AriaProvider() + }; + + var options = { + allowHorizontalScroll: false, + twistiePixels: 20, + ariaLabel: nls.localize('treeAriaLabel', "References") + }; + this._tree = new Tree(div.getHTMLElement(), config, options); + this._callOnDispose.push(attachListStyler(this._tree, this._themeService)); + + this._treeContainer = div.hide(); + }); + } + + protected _doLayoutBody(heightInPixel: number, widthInPixel: number): void { + super._doLayoutBody(heightInPixel, widthInPixel); + + const height = heightInPixel + 'px'; + this._sash.height = heightInPixel; + this._sash.width = widthInPixel; + + // set height/width + const [left, right] = this._sash.percentages; + this._previewContainer.style({ height, width: left }); + this._treeContainer.style({ height, width: right }); + + // forward + this._tree.layout(heightInPixel); + this._preview.layout(); + + // store layout data + this.layoutData = { + heightInLines: this._viewZone.heightInLines, + ratio: this._sash.ratio + }; + } + + public _onWidth(widthInPixel: number): void { + this._sash.width = widthInPixel; + this._preview.layout(); + } + + public setSelection(selection: OneReference): TPromise { + return this._revealReference(selection); + } + + public setModel(newModel: ReferencesModel): TPromise { + // clean up + this._disposeOnNewModel = dispose(this._disposeOnNewModel); + this._model = newModel; + if (this._model) { + return this._onNewModel(); + } + return undefined; + } + + private _onNewModel(): TPromise { + + if (this._model.empty) { + this.setTitle(''); + this._messageContainer.innerHtml(nls.localize('noResults', "No results")).show(); + return TPromise.as(void 0); + } + + this._messageContainer.hide(); + this._decorationsManager = new DecorationsManager(this._preview, this._model); + this._disposeOnNewModel.push(this._decorationsManager); + + // listen on model changes + this._disposeOnNewModel.push(this._model.onDidChangeReferenceRange(reference => this._tree.refresh(reference))); + + // listen on selection and focus + this._disposeOnNewModel.push(this._tree.addListener(Controller.Events.FOCUSED, (element) => { + if (element instanceof OneReference) { + this._revealReference(element); + this._onDidSelectReference.fire({ element, kind: 'show', source: 'tree' }); + } + })); + + this._disposeOnNewModel.push(this._tree.addListener(Controller.Events.SELECTED, (element: any) => { + if (element instanceof OneReference) { + this._onDidSelectReference.fire({ element, kind: 'goto', source: 'tree' }); + } + })); + this._disposeOnNewModel.push(this._tree.addListener(Controller.Events.OPEN_TO_SIDE, (element: any) => { + if (element instanceof OneReference) { + this._onDidSelectReference.fire({ element, kind: 'side', source: 'tree' }); + } + })); + + // listen on editor + this._disposeOnNewModel.push(this._preview.onMouseDown((e) => { + if (e.event.detail === 2) { + this._onDidSelectReference.fire({ + element: this._getFocusedReference(), + kind: (e.event.ctrlKey || e.event.metaKey) ? 'side' : 'open', + source: 'editor' + }); + } + })); + + // make sure things are rendered + dom.addClass(this.container, 'results-loaded'); + this._treeContainer.show(); + this._previewContainer.show(); + this._preview.layout(); + this._tree.layout(); + this.focus(); + + // pick input and a reference to begin with + const input = this._model.groups.length === 1 ? this._model.groups[0] : this._model; + return this._tree.setInput(input); + } + + private _getFocusedReference(): OneReference { + const element = this._tree.getFocus(); + if (element instanceof OneReference) { + return element; + } else if (element instanceof FileReferences) { + if (element.children.length > 0) { + return element.children[0]; + } + } + return undefined; + } + + private _revealReference(reference: OneReference) { + + // Update widget header + if (reference.uri.scheme !== Schemas.inMemory) { + this.setTitle(reference.name, getPathLabel(reference.directory, this._contextService, this._environmentService)); + } else { + this.setTitle(nls.localize('peekView.alternateTitle', "References")); + } + + const promise = this._textModelResolverService.createModelReference(reference.uri); + + return TPromise.join([promise, this._tree.reveal(reference)]).then(values => { + const ref = values[0]; + + if (!this._model) { + ref.dispose(); + // disposed + return; + } + + dispose(this._previewModelReference); + + // show in editor + const model = ref.object; + if (model) { + this._previewModelReference = ref; + let isSameModel = (this._preview.getModel() === model.textEditorModel); + this._preview.setModel(model.textEditorModel); + var sel = Range.lift(reference.range).collapseToStart(); + this._preview.setSelection(sel); + this._preview.revealRangeInCenter(sel, isSameModel ? editorCommon.ScrollType.Smooth : editorCommon.ScrollType.Immediate); + } else { + this._preview.setModel(this._previewNotAvailableMessage); + ref.dispose(); + } + + // show in tree + this._tree.setSelection([reference]); + this._tree.setFocus(reference); + + }, onUnexpectedError); + } +} + +// theming + +export const peekViewTitleBackground = registerColor('peekViewTitle.background', { dark: '#1E1E1E', light: '#FFFFFF', hc: '#0C141F' }, nls.localize('peekViewTitleBackground', 'Background color of the peek view title area.')); +export const peekViewTitleForeground = registerColor('peekViewTitleLabel.foreground', { dark: '#FFFFFF', light: '#333333', hc: '#FFFFFF' }, nls.localize('peekViewTitleForeground', 'Color of the peek view title.')); +export const peekViewTitleInfoForeground = registerColor('peekViewTitleDescription.foreground', { dark: '#ccccccb3', light: '#6c6c6cb3', hc: '#FFFFFF99' }, nls.localize('peekViewTitleInfoForeground', 'Color of the peek view title info.')); +export const peekViewBorder = registerColor('peekView.border', { dark: '#007acc', light: '#007acc', hc: contrastBorder }, nls.localize('peekViewBorder', 'Color of the peek view borders and arrow.')); + +export const peekViewResultsBackground = registerColor('peekViewResult.background', { dark: '#252526', light: '#F3F3F3', hc: Color.black }, nls.localize('peekViewResultsBackground', 'Background color of the peek view result list.')); +export const peekViewResultsMatchForeground = registerColor('peekViewResult.lineForeground', { dark: '#bbbbbb', light: '#646465', hc: Color.white }, nls.localize('peekViewResultsMatchForeground', 'Foreground color for line nodes in the peek view result list.')); +export const peekViewResultsFileForeground = registerColor('peekViewResult.fileForeground', { dark: Color.white, light: '#1E1E1E', hc: Color.white }, nls.localize('peekViewResultsFileForeground', 'Foreground color for file nodes in the peek view result list.')); +export const peekViewResultsSelectionBackground = registerColor('peekViewResult.selectionBackground', { dark: '#3399ff33', light: '#3399ff33', hc: null }, nls.localize('peekViewResultsSelectionBackground', 'Background color of the selected entry in the peek view result list.')); +export const peekViewResultsSelectionForeground = registerColor('peekViewResult.selectionForeground', { dark: Color.white, light: '#6C6C6C', hc: Color.white }, nls.localize('peekViewResultsSelectionForeground', 'Foreground color of the selected entry in the peek view result list.')); +export const peekViewEditorBackground = registerColor('peekViewEditor.background', { dark: '#001F33', light: '#F2F8FC', hc: Color.black }, nls.localize('peekViewEditorBackground', 'Background color of the peek view editor.')); +export const peekViewEditorGutterBackground = registerColor('peekViewEditorGutter.background', { dark: peekViewEditorBackground, light: peekViewEditorBackground, hc: peekViewEditorBackground }, nls.localize('peekViewEditorGutterBackground', 'Background color of the gutter in the peek view editor.')); + +export const peekViewResultsMatchHighlight = registerColor('peekViewResult.matchHighlightBackground', { dark: '#ea5c004d', light: '#ea5c004d', hc: null }, nls.localize('peekViewResultsMatchHighlight', 'Match highlight color in the peek view result list.')); +export const peekViewEditorMatchHighlight = registerColor('peekViewEditor.matchHighlightBackground', { dark: '#ff8f0099', light: '#f5d802de', hc: null }, nls.localize('peekViewEditorMatchHighlight', 'Match highlight color in the peek view editor.')); + + +registerThemingParticipant((theme, collector) => { + let findMatchHighlightColor = theme.getColor(peekViewResultsMatchHighlight); + if (findMatchHighlightColor) { + collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree .referenceMatch { background-color: ${findMatchHighlightColor}; }`); + } + let referenceHighlightColor = theme.getColor(peekViewEditorMatchHighlight); + if (referenceHighlightColor) { + collector.addRule(`.monaco-editor .reference-zone-widget .preview .reference-decoration { background-color: ${referenceHighlightColor}; }`); + } + let hcOutline = theme.getColor(activeContrastBorder); + if (hcOutline) { + collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree .referenceMatch { border: 1px dotted ${hcOutline}; box-sizing: border-box; }`); + collector.addRule(`.monaco-editor .reference-zone-widget .preview .reference-decoration { border: 2px solid ${hcOutline}; box-sizing: border-box; }`); + } + let resultsBackground = theme.getColor(peekViewResultsBackground); + if (resultsBackground) { + collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree { background-color: ${resultsBackground}; }`); + } + let resultsMatchForeground = theme.getColor(peekViewResultsMatchForeground); + if (resultsMatchForeground) { + collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree { color: ${resultsMatchForeground}; }`); + } + let resultsFileForeground = theme.getColor(peekViewResultsFileForeground); + if (resultsFileForeground) { + collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree .reference-file { color: ${resultsFileForeground}; }`); + } + let resultsSelectedBackground = theme.getColor(peekViewResultsSelectionBackground); + if (resultsSelectedBackground) { + collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: ${resultsSelectedBackground}; }`); + } + let resultsSelectedForeground = theme.getColor(peekViewResultsSelectionForeground); + if (resultsSelectedForeground) { + collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: ${resultsSelectedForeground} !important; }`); + } + let editorBackground = theme.getColor(peekViewEditorBackground); + if (editorBackground) { + collector.addRule( + `.monaco-editor .reference-zone-widget .preview .monaco-editor .monaco-editor-background,` + + `.monaco-editor .reference-zone-widget .preview .monaco-editor .inputarea.ime-input {` + + ` background-color: ${editorBackground};` + + `}`); + } + let editorGutterBackground = theme.getColor(peekViewEditorGutterBackground); + if (editorGutterBackground) { + collector.addRule( + `.monaco-editor .reference-zone-widget .preview .monaco-editor .margin {` + + ` background-color: ${editorGutterBackground};` + + `}`); + } +}); diff --git a/src/vs/editor/contrib/referenceSearch/test/browser/referencesModel.test.ts b/src/vs/editor/contrib/referenceSearch/test/browser/referencesModel.test.ts new file mode 100644 index 0000000000..c8a9f2ed61 --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/test/browser/referencesModel.test.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { ReferencesModel } from 'vs/editor/contrib/referenceSearch/browser/referencesModel'; + +suite('references', function () { + + test('nearestReference', function () { + const model = new ReferencesModel([{ + uri: URI.file('/out/obj/can'), + range: new Range(1, 1, 1, 1) + }, { + uri: URI.file('/out/obj/can2'), + range: new Range(1, 1, 1, 1) + }, { + uri: URI.file('/src/can'), + range: new Range(1, 1, 1, 1) + }]); + + let ref = model.nearestReference(URI.file('/src/can'), new Position(1, 1)); + assert.equal(ref.uri.path, '/src/can'); + + ref = model.nearestReference(URI.file('/src/someOtherFileInSrc'), new Position(1, 1)); + assert.equal(ref.uri.path, '/src/can'); + + ref = model.nearestReference(URI.file('/out/someOtherFile'), new Position(1, 1)); + assert.equal(ref.uri.path, '/out/obj/can'); + + ref = model.nearestReference(URI.file('/out/obj/can2222'), new Position(1, 1)); + assert.equal(ref.uri.path, '/out/obj/can2'); + }); + +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/rename/browser/rename.ts b/src/vs/editor/contrib/rename/browser/rename.ts new file mode 100644 index 0000000000..726bc8cd74 --- /dev/null +++ b/src/vs/editor/contrib/rename/browser/rename.ts @@ -0,0 +1,273 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import { isPromiseCanceledError, onUnexpectedExternalError, illegalArgument } from 'vs/base/common/errors'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import Severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IFileService } from 'vs/platform/files/common/files'; +import { RawContextKey, IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { ICommonCodeEditor, IEditorContribution, IReadOnlyModel } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { BulkEdit, createBulkEdit } from 'vs/editor/common/services/bulkEdit'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import RenameInputField from './renameInputField'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { optional } from 'vs/platform/instantiation/common/instantiation'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { sequence, asWinJsPromise } from 'vs/base/common/async'; +import { WorkspaceEdit, RenameProviderRegistry } from 'vs/editor/common/modes'; +import { Position } from 'vs/editor/common/core/position'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { Range } from 'vs/editor/common/core/range'; + + +export function rename(model: IReadOnlyModel, position: Position, newName: string): TPromise { + + const supports = RenameProviderRegistry.ordered(model); + const rejects: string[] = []; + let hasResult = false; + + const factory = supports.map(support => { + return (): TPromise => { + if (!hasResult) { + return asWinJsPromise((token) => { + return support.provideRenameEdits(model, position, newName, token); + }).then(result => { + if (!result) { + // ignore + } else if (!result.rejectReason) { + hasResult = true; + return result; + } else { + rejects.push(result.rejectReason); + } + return undefined; + }, err => { + onUnexpectedExternalError(err); + return TPromise.wrapError(new Error('provider failed')); + }); + } + return undefined; + }; + }); + + return sequence(factory).then((values): WorkspaceEdit => { + let result = values[0]; + if (rejects.length > 0) { + return { + edits: undefined, + rejectReason: rejects.join('\n') + }; + } else if (!result) { + return { + edits: undefined, + rejectReason: nls.localize('no result', "No result.") + }; + } else { + return result; + } + }); +} + + +// --- register actions and commands + +const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey('renameInputVisible', false); + +@editorContribution +class RenameController implements IEditorContribution { + + private static ID = 'editor.contrib.renameController'; + + public static get(editor: ICommonCodeEditor): RenameController { + return editor.getContribution(RenameController.ID); + } + + private _renameInputField: RenameInputField; + private _renameInputVisible: IContextKey; + + constructor( + private editor: ICodeEditor, + @IMessageService private _messageService: IMessageService, + @ITextModelService private _textModelResolverService: ITextModelService, + @IProgressService private _progressService: IProgressService, + @IContextKeyService contextKeyService: IContextKeyService, + @IThemeService themeService: IThemeService, + @optional(IFileService) private _fileService: IFileService + ) { + this._renameInputField = new RenameInputField(editor, themeService); + this._renameInputVisible = CONTEXT_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); + } + + public dispose(): void { + this._renameInputField.dispose(); + } + + public getId(): string { + return RenameController.ID; + } + + public run(): TPromise { + + const selection = this.editor.getSelection(), + word = this.editor.getModel().getWordAtPosition(selection.getStartPosition()); + + if (!word) { + return undefined; + } + + let lineNumber = selection.startLineNumber, + selectionStart = 0, + selectionEnd = word.word.length, + wordRange: Range; + + wordRange = new Range( + lineNumber, + word.startColumn, + lineNumber, + word.endColumn + ); + + if (!selection.isEmpty() && selection.startLineNumber === selection.endLineNumber) { + selectionStart = Math.max(0, selection.startColumn - word.startColumn); + selectionEnd = Math.min(word.endColumn, selection.endColumn) - word.startColumn; + } + + this._renameInputVisible.set(true); + return this._renameInputField.getInput(wordRange, word.word, selectionStart, selectionEnd).then(newName => { + this._renameInputVisible.reset(); + this.editor.focus(); + + const renameOperation = this._prepareRename(newName).then(edit => { + + return edit.finish().then(selection => { + if (selection) { + this.editor.setSelection(selection); + } + // alert + alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", word.word, newName, edit.ariaMessage())); + }); + + }, err => { + if (typeof err === 'string') { + this._messageService.show(Severity.Info, err); + return undefined; + } else { + this._messageService.show(Severity.Error, nls.localize('rename.failed', "Sorry, rename failed to execute.")); + return TPromise.wrapError(err); + } + }); + + this._progressService.showWhile(renameOperation, 250); + return renameOperation; + + }, err => { + this._renameInputVisible.reset(); + this.editor.focus(); + + if (!isPromiseCanceledError(err)) { + return TPromise.wrapError(err); + } + return undefined; + }); + } + + public acceptRenameInput(): void { + this._renameInputField.acceptInput(); + } + + public cancelRenameInput(): void { + this._renameInputField.cancelInput(); + } + + private _prepareRename(newName: string): TPromise { + + // start recording of file changes so that we can figure out if a file that + // is to be renamed conflicts with another (concurrent) modification + let edit = createBulkEdit(this._textModelResolverService, this.editor, this._fileService); + + return rename(this.editor.getModel(), this.editor.getPosition(), newName).then(result => { + if (result.rejectReason) { + return TPromise.wrapError(new Error(result.rejectReason)); + } + edit.add(result.edits); + return edit; + }); + } +} + +// ---- action implementation + +@editorAction +export class RenameAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.rename', + label: nls.localize('rename.label', "Rename Symbol"), + alias: 'Rename Symbol', + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasRenameProvider), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.F2 + }, + menuOpts: { + group: '1_modification', + order: 1.1 + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + let controller = RenameController.get(editor); + if (controller) { + return controller.run(); + } + return undefined; + } +} + +const RenameCommand = EditorCommand.bindToContribution(RenameController.get); + +CommonEditorRegistry.registerEditorCommand(new RenameCommand({ + id: 'acceptRenameInput', + precondition: CONTEXT_RENAME_INPUT_VISIBLE, + handler: x => x.acceptRenameInput(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(99), + kbExpr: EditorContextKeys.focus, + primary: KeyCode.Enter + } +})); + +CommonEditorRegistry.registerEditorCommand(new RenameCommand({ + id: 'cancelRenameInput', + precondition: CONTEXT_RENAME_INPUT_VISIBLE, + handler: x => x.cancelRenameInput(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(99), + kbExpr: EditorContextKeys.focus, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } +})); + +// ---- api bridge command + +CommonEditorRegistry.registerDefaultLanguageCommand('_executeDocumentRenameProvider', function (model, position, args) { + let { newName } = args; + if (typeof newName !== 'string') { + throw illegalArgument('newName'); + } + return rename(model, position, newName); +}); diff --git a/src/vs/editor/contrib/rename/browser/renameInputField.css b/src/vs/editor/contrib/rename/browser/renameInputField.css new file mode 100644 index 0000000000..49959fc391 --- /dev/null +++ b/src/vs/editor/contrib/rename/browser/renameInputField.css @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .rename-box { + z-index: 100; + color: inherit; +} + +.monaco-editor .rename-box .rename-input { + padding: 4px; +} diff --git a/src/vs/editor/contrib/rename/browser/renameInputField.ts b/src/vs/editor/contrib/rename/browser/renameInputField.ts new file mode 100644 index 0000000000..195a3f5eb4 --- /dev/null +++ b/src/vs/editor/contrib/rename/browser/renameInputField.ts @@ -0,0 +1,201 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./renameInputField'; +import { localize } from 'vs/nls'; +import { canceled } from 'vs/base/common/errors'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Range } from 'vs/editor/common/core/range'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; +import { inputBackground, inputBorder, inputForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { Position } from 'vs/editor/common/core/position'; +import { ScrollType } from 'vs/editor/common/editorCommon'; + +export default class RenameInputField implements IContentWidget, IDisposable { + + private _editor: ICodeEditor; + private _position: Position; + private _domNode: HTMLElement; + private _inputField: HTMLInputElement; + private _visible: boolean; + private _disposables: IDisposable[] = []; + + // Editor.IContentWidget.allowEditorOverflow + public allowEditorOverflow: boolean = true; + + constructor(editor: ICodeEditor, @IThemeService private themeService: IThemeService) { + this._editor = editor; + this._editor.addContentWidget(this); + + this._disposables.push(editor.onDidChangeConfiguration(e => { + if (e.fontInfo) { + this.updateFont(); + } + })); + + this._disposables.push(themeService.onThemeChange(theme => this.onThemeChange(theme))); + } + + private onThemeChange(theme: ITheme): void { + this.updateStyles(theme); + } + + public dispose(): void { + this._disposables = dispose(this._disposables); + this._editor.removeContentWidget(this); + } + + public getId(): string { + return '__renameInputWidget'; + } + + public getDomNode(): HTMLElement { + if (!this._domNode) { + this._inputField = document.createElement('input'); + this._inputField.className = 'rename-input'; + this._inputField.type = 'text'; + this._inputField.setAttribute('aria-label', localize('renameAriaLabel', "Rename input. Type new name and press Enter to commit.")); + this._domNode = document.createElement('div'); + this._domNode.style.height = `${this._editor.getConfiguration().lineHeight}px`; + this._domNode.className = 'monaco-editor rename-box'; + this._domNode.appendChild(this._inputField); + + this.updateFont(); + this.updateStyles(this.themeService.getTheme()); + } + return this._domNode; + } + + private updateStyles(theme: ITheme): void { + if (!this._inputField) { + return; + } + + const background = theme.getColor(inputBackground); + const foreground = theme.getColor(inputForeground); + const widgetShadowColor = theme.getColor(widgetShadow); + const border = theme.getColor(inputBorder); + + this._inputField.style.backgroundColor = background ? background.toString() : null; + this._inputField.style.color = foreground ? foreground.toString() : null; + + this._inputField.style.borderWidth = border ? '1px' : '0px'; + this._inputField.style.borderStyle = border ? 'solid' : 'none'; + this._inputField.style.borderColor = border ? border.toString() : 'none'; + + this._domNode.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : null; + } + + private updateFont(): void { + if (!this._inputField) { + return; + } + + const fontInfo = this._editor.getConfiguration().fontInfo; + this._inputField.style.fontFamily = fontInfo.fontFamily; + this._inputField.style.fontWeight = fontInfo.fontWeight; + this._inputField.style.fontSize = `${fontInfo.fontSize}px`; + } + + public getPosition(): IContentWidgetPosition { + return this._visible + ? { position: this._position, preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] } + : null; + } + + private _currentAcceptInput: () => void = null; + private _currentCancelInput: () => void = null; + + public acceptInput(): void { + if (this._currentAcceptInput) { + this._currentAcceptInput(); + } + } + + public cancelInput(): void { + if (this._currentCancelInput) { + this._currentCancelInput(); + } + } + + public getInput(where: Range, value: string, selectionStart: number, selectionEnd: number): TPromise { + + this._position = new Position(where.startLineNumber, where.startColumn); + this._inputField.value = value; + this._inputField.setAttribute('selectionStart', selectionStart.toString()); + this._inputField.setAttribute('selectionEnd', selectionEnd.toString()); + this._inputField.size = Math.max((where.endColumn - where.startColumn) * 1.1, 20); + + let disposeOnDone: IDisposable[] = [], + always: Function; + + always = () => { + dispose(disposeOnDone); + this._hide(); + }; + + return new TPromise((c, e) => { + + this._currentCancelInput = () => { + this._currentAcceptInput = null; + this._currentCancelInput = null; + e(canceled()); + return true; + }; + + this._currentAcceptInput = () => { + if (this._inputField.value.trim().length === 0 || this._inputField.value === value) { + // empty or whitespace only or not changed + this.cancelInput(); + return; + } + + this._currentAcceptInput = null; + this._currentCancelInput = null; + c(this._inputField.value); + }; + + let onCursorChanged = () => { + if (!Range.containsPosition(where, this._editor.getPosition())) { + this.cancelInput(); + } + }; + + disposeOnDone.push(this._editor.onDidChangeCursorSelection(onCursorChanged)); + disposeOnDone.push(this._editor.onDidBlurEditor(() => this.cancelInput())); + + this._show(); + + }, this._currentCancelInput).then(newValue => { + always(); + return newValue; + }, err => { + always(); + return TPromise.wrapError(err); + }); + } + + private _show(): void { + this._editor.revealLineInCenterIfOutsideViewport(this._position.lineNumber, ScrollType.Smooth); + this._visible = true; + this._editor.layoutContentWidget(this); + + setTimeout(() => { + this._inputField.focus(); + this._inputField.setSelectionRange( + parseInt(this._inputField.getAttribute('selectionStart')), + parseInt(this._inputField.getAttribute('selectionEnd'))); + }, 25); + } + + private _hide(): void { + this._visible = false; + this._editor.layoutContentWidget(this); + } +} diff --git a/src/vs/editor/contrib/smartSelect/common/smartSelect.ts b/src/vs/editor/contrib/smartSelect/common/smartSelect.ts new file mode 100644 index 0000000000..2d191edc18 --- /dev/null +++ b/src/vs/editor/contrib/smartSelect/common/smartSelect.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import * as arrays from 'vs/base/common/arrays'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Range } from 'vs/editor/common/core/range'; +import { ICommonCodeEditor, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, ServicesAccessor, IActionOptions, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions'; +import { TokenSelectionSupport, ILogicalSelectionEntry } from './tokenSelectionSupport'; +import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; + +// --- selection state machine + +class State { + + public editor: ICommonCodeEditor; + public next: State; + public previous: State; + public selection: Range; + + constructor(editor: ICommonCodeEditor) { + this.editor = editor; + this.next = null; + this.previous = null; + this.selection = editor.getSelection(); + } +} + +// --- shared state between grow and shrink actions +var state: State = null; +var ignoreSelection = false; + +// -- action implementation + +@commonEditorContribution +class SmartSelectController implements IEditorContribution { + + private static ID = 'editor.contrib.smartSelectController'; + + public static get(editor: ICommonCodeEditor): SmartSelectController { + return editor.getContribution(SmartSelectController.ID); + } + + private _tokenSelectionSupport: TokenSelectionSupport; + + constructor( + private editor: ICommonCodeEditor, + @IInstantiationService instantiationService: IInstantiationService + ) { + this._tokenSelectionSupport = instantiationService.createInstance(TokenSelectionSupport); + } + + public dispose(): void { + } + + public getId(): string { + return SmartSelectController.ID; + } + + public run(forward: boolean): TPromise { + + var selection = this.editor.getSelection(); + var model = this.editor.getModel(); + + // forget about current state + if (state) { + if (state.editor !== this.editor) { + state = null; + } + } + + var promise: TPromise = TPromise.as(null); + if (!state) { + promise = this._tokenSelectionSupport.getRangesToPosition(model.uri, selection.getStartPosition()).then((elements: ILogicalSelectionEntry[]) => { + + if (arrays.isFalsyOrEmpty(elements)) { + return; + } + + var lastState: State; + elements.filter((element) => { + // filter ranges inside the selection + var selection = this.editor.getSelection(); + var range = new Range(element.range.startLineNumber, element.range.startColumn, element.range.endLineNumber, element.range.endColumn); + return range.containsPosition(selection.getStartPosition()) && range.containsPosition(selection.getEndPosition()); + + }).forEach((element) => { + // create ranges + var range = element.range; + var state = new State(this.editor); + state.selection = new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); + if (lastState) { + state.next = lastState; + lastState.previous = state; + } + lastState = state; + }); + + // insert current selection + var editorState = new State(this.editor); + editorState.next = lastState; + if (lastState) { + lastState.previous = editorState; + } + state = editorState; + + // listen to caret move and forget about state + var unhook = this.editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => { + if (ignoreSelection) { + return; + } + state = null; + unhook.dispose(); + }); + }); + } + + return promise.then(() => { + + if (!state) { + return; + } + + state = forward ? state.next : state.previous; + if (!state) { + return; + } + + ignoreSelection = true; + try { + this.editor.setSelection(state.selection); + } finally { + ignoreSelection = false; + } + + return; + }); + } +} + +abstract class AbstractSmartSelect extends EditorAction { + + private _forward: boolean; + + constructor(forward: boolean, opts: IActionOptions) { + super(opts); + this._forward = forward; + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + let controller = SmartSelectController.get(editor); + if (controller) { + return controller.run(this._forward); + } + return undefined; + } +} + +@editorAction +class GrowSelectionAction extends AbstractSmartSelect { + constructor() { + super(true, { + id: 'editor.action.smartSelect.grow', + label: nls.localize('smartSelect.grow', "Expand Select"), + alias: 'Expand Select', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.RightArrow, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyMod.Shift | KeyCode.RightArrow } + } + }); + } +} + +@editorAction +class ShrinkSelectionAction extends AbstractSmartSelect { + constructor() { + super(false, { + id: 'editor.action.smartSelect.shrink', + label: nls.localize('smartSelect.shrink', "Shrink Select"), + alias: 'Shrink Select', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.LeftArrow, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyMod.Shift | KeyCode.LeftArrow } + } + }); + } +} diff --git a/src/vs/editor/contrib/smartSelect/common/tokenSelectionSupport.ts b/src/vs/editor/contrib/smartSelect/common/tokenSelectionSupport.ts new file mode 100644 index 0000000000..2f5f86b53f --- /dev/null +++ b/src/vs/editor/contrib/smartSelect/common/tokenSelectionSupport.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Range } from 'vs/editor/common/core/range'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { Node, build, find } from './tokenTree'; +import { Position } from 'vs/editor/common/core/position'; + +/** + * Interface used to compute a hierachry of logical ranges. + */ +export interface ILogicalSelectionEntry { + type: string; + range: Range; +} + +export class TokenSelectionSupport { + + private _modelService: IModelService; + + constructor( @IModelService modelService: IModelService) { + this._modelService = modelService; + } + + public getRangesToPosition(resource: URI, position: Position): TPromise { + return TPromise.as(this.getRangesToPositionSync(resource, position)); + } + + public getRangesToPositionSync(resource: URI, position: Position): ILogicalSelectionEntry[] { + var model = this._modelService.getModel(resource), + entries: ILogicalSelectionEntry[] = []; + + if (model) { + this._doGetRangesToPosition(model, position).forEach(range => { + entries.push({ + type: void 0, + range + }); + }); + } + + return entries; + } + + private _doGetRangesToPosition(model: IModel, position: Position): Range[] { + + var tree = build(model), + node: Node, + lastRange: Range; + + node = find(tree, position); + var ranges: Range[] = []; + while (node) { + if (!lastRange || !Range.equalsRange(lastRange, node.range)) { + ranges.push(node.range); + } + lastRange = node.range; + node = node.parent; + } + ranges = ranges.reverse(); + return ranges; + } + +} diff --git a/src/vs/editor/contrib/smartSelect/common/tokenTree.ts b/src/vs/editor/contrib/smartSelect/common/tokenTree.ts new file mode 100644 index 0000000000..45df5e9ec4 --- /dev/null +++ b/src/vs/editor/contrib/smartSelect/common/tokenTree.ts @@ -0,0 +1,425 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { LineToken } from 'vs/editor/common/core/lineTokens'; +import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; +import { BracketsUtils, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { LanguageId, StandardTokenType } from 'vs/editor/common/modes'; + +export const enum TokenTreeBracket { + None = 0, + Open = 1, + Close = -1 +} + +export class Node { + + start: Position; + + end: Position; + + get range(): Range { + return new Range( + this.start.lineNumber, + this.start.column, + this.end.lineNumber, + this.end.column + ); + } + + parent: Node; +} + +export class NodeList extends Node { + + children: Node[]; + + get start(): Position { + return this.hasChildren + ? this.children[0].start + : this.parent.start; + } + + get end(): Position { + return this.hasChildren + ? this.children[this.children.length - 1].end + : this.parent.end; + } + + get hasChildren() { + return this.children && this.children.length > 0; + } + + get isEmpty() { + return !this.hasChildren && !this.parent; + } + + public append(node: Node): boolean { + if (!node) { + return false; + } + node.parent = this; + if (!this.children) { + this.children = []; + } + if (node instanceof NodeList) { + if (node.children) { + this.children.push.apply(this.children, node.children); + } + } else { + this.children.push(node); + } + return true; + } +} + +export class Block extends Node { + + open: Node; + close: Node; + elements: NodeList; + + get start(): Position { + return this.open.start; + } + + get end(): Position { + return this.close.end; + } + + constructor() { + super(); + this.elements = new NodeList(); + this.elements.parent = this; + } +} + +class Token { + _tokenBrand: void; + + readonly range: Range; + readonly bracket: TokenTreeBracket; + readonly bracketType: string; + + constructor(range: Range, bracket: TokenTreeBracket, bracketType: string) { + this.range = range; + this.bracket = bracket; + this.bracketType = bracketType; + } +} + +function newNode(token: Token): Node { + var node = new Node(); + node.start = token.range.getStartPosition(); + node.end = token.range.getEndPosition(); + return node; +} + +class RawToken { + _basicTokenBrand: void; + + public lineNumber: number; + public lineText: string; + public startOffset: number; + public endOffset: number; + public type: StandardTokenType; + public languageId: LanguageId; + + constructor(source: LineToken, lineNumber: number, lineText: string) { + this.lineNumber = lineNumber; + this.lineText = lineText; + this.startOffset = source.startOffset; + this.endOffset = source.endOffset; + this.type = source.tokenType; + this.languageId = source.languageId; + } +} + +class ModelRawTokenScanner { + + private _model: IModel; + private _lineCount: number; + private _versionId: number; + private _lineNumber: number; + private _lineText: string; + private _next: LineToken; + + constructor(model: IModel) { + this._model = model; + this._lineCount = this._model.getLineCount(); + this._versionId = this._model.getVersionId(); + this._lineNumber = 0; + this._lineText = null; + this._advance(); + } + + private _advance(): void { + this._next = (this._next ? this._next.next() : null); + while (!this._next && this._lineNumber < this._lineCount) { + this._lineNumber++; + this._lineText = this._model.getLineContent(this._lineNumber); + this._model.forceTokenization(this._lineNumber); + let currentLineTokens = this._model.getLineTokens(this._lineNumber); + this._next = currentLineTokens.firstToken(); + } + } + + public next(): RawToken { + if (!this._next) { + return null; + } + if (this._model.getVersionId() !== this._versionId) { + return null; + } + + let result = new RawToken(this._next, this._lineNumber, this._lineText); + this._advance(); + return result; + } +} + +class TokenScanner { + + private _rawTokenScanner: ModelRawTokenScanner; + private _nextBuff: Token[]; + + private _cachedLanguageBrackets: RichEditBrackets; + private _cachedLanguageId: LanguageId; + + constructor(model: IModel) { + this._rawTokenScanner = new ModelRawTokenScanner(model); + this._nextBuff = []; + this._cachedLanguageBrackets = null; + this._cachedLanguageId = -1; + } + + next(): Token { + if (this._nextBuff.length > 0) { + return this._nextBuff.shift(); + } + + const token = this._rawTokenScanner.next(); + if (!token) { + return null; + } + const lineNumber = token.lineNumber; + const lineText = token.lineText; + const tokenType = token.type; + let startOffset = token.startOffset; + const endOffset = token.endOffset; + + if (this._cachedLanguageId !== token.languageId) { + this._cachedLanguageId = token.languageId; + this._cachedLanguageBrackets = LanguageConfigurationRegistry.getBracketsSupport(this._cachedLanguageId); + } + const modeBrackets = this._cachedLanguageBrackets; + + if (!modeBrackets || ignoreBracketsInToken(tokenType)) { + return new Token( + new Range(lineNumber, startOffset + 1, lineNumber, endOffset + 1), + TokenTreeBracket.None, + null + ); + } + + let foundBracket: Range; + do { + foundBracket = BracketsUtils.findNextBracketInToken(modeBrackets.forwardRegex, lineNumber, lineText, startOffset, endOffset); + if (foundBracket) { + const foundBracketStartOffset = foundBracket.startColumn - 1; + const foundBracketEndOffset = foundBracket.endColumn - 1; + + if (startOffset < foundBracketStartOffset) { + // there is some text before this bracket in this token + this._nextBuff.push(new Token( + new Range(lineNumber, startOffset + 1, lineNumber, foundBracketStartOffset + 1), + TokenTreeBracket.None, + null + )); + } + + let bracketText = lineText.substring(foundBracketStartOffset, foundBracketEndOffset); + bracketText = bracketText.toLowerCase(); + + const bracketData = modeBrackets.textIsBracket[bracketText]; + const bracketIsOpen = modeBrackets.textIsOpenBracket[bracketText]; + + this._nextBuff.push(new Token( + new Range(lineNumber, foundBracketStartOffset + 1, lineNumber, foundBracketEndOffset + 1), + bracketIsOpen ? TokenTreeBracket.Open : TokenTreeBracket.Close, + `${bracketData.languageIdentifier.language};${bracketData.open};${bracketData.close}` + )); + + startOffset = foundBracketEndOffset; + } + } while (foundBracket); + + if (startOffset < endOffset) { + // there is some remaining none-bracket text in this token + this._nextBuff.push(new Token( + new Range(lineNumber, startOffset + 1, lineNumber, endOffset + 1), + TokenTreeBracket.None, + null + )); + } + + return this._nextBuff.shift(); + } +} + +class TokenTreeBuilder { + + private _scanner: TokenScanner; + private _stack: Token[] = []; + private _currentToken: Token; + + constructor(model: IModel) { + this._scanner = new TokenScanner(model); + } + + public build(): Node { + var node = new NodeList(); + while (node.append(this._line() || this._any())) { + // accept all + } + return node; + } + + private _accept(condt: (info: Token) => boolean): boolean { + var token = this._stack.pop() || this._scanner.next(); + if (!token) { + return false; + } + var accepted = condt(token); + if (!accepted) { + this._stack.push(token); + this._currentToken = null; + } else { + this._currentToken = token; + // console.log('accepted: ' + token.__debugContent); + } + return accepted; + } + + private _peek(condt: (info: Token) => boolean): boolean { + var ret = false; + this._accept(info => { + ret = condt(info); + return false; + }); + return ret; + } + + private _line(): Node { + var node = new NodeList(), + lineNumber: number; + + // capture current linenumber + this._peek(info => { + lineNumber = info.range.startLineNumber; + return false; + }); + + while (this._peek(info => info.range.startLineNumber === lineNumber) + && node.append(this._token() || this._block())) { + + // all children that started on this line + } + + if (!node.children || node.children.length === 0) { + return null; + } else if (node.children.length === 1) { + return node.children[0]; + } else { + return node; + } + } + + private _token(): Node { + if (!this._accept(token => token.bracket === TokenTreeBracket.None)) { + return null; + } + return newNode(this._currentToken); + } + + private _block(): Node { + + var bracketType: string, + accepted: boolean; + + accepted = this._accept(token => { + bracketType = token.bracketType; + return token.bracket === TokenTreeBracket.Open; + }); + if (!accepted) { + return null; + } + + var bracket = new Block(); + bracket.open = newNode(this._currentToken); + while (bracket.elements.append(this._line())) { + // inside brackets + } + + if (!this._accept(token => token.bracket === TokenTreeBracket.Close && token.bracketType === bracketType)) { + // missing closing bracket -> return just a node list + var nodelist = new NodeList(); + nodelist.append(bracket.open); + nodelist.append(bracket.elements); + return nodelist; + } + + bracket.close = newNode(this._currentToken); + return bracket; + } + + private _any(): Node { + if (!this._accept(_ => true)) { + return null; + } + return newNode(this._currentToken); + } +} + +/** + * Parses this grammar: + * grammer = { line } + * line = { block | "token" } + * block = "open_bracket" { line } "close_bracket" + */ +export function build(model: IModel): Node { + var node = new TokenTreeBuilder(model).build(); + return node; +} + +export function find(node: Node, position: Position): Node { + if (node instanceof NodeList && node.isEmpty) { + return null; + } + + if (!Range.containsPosition(node.range, position)) { + return null; + } + + var result: Node; + + if (node instanceof NodeList) { + if (node.hasChildren) { + for (var i = 0, len = node.children.length; i < len && !result; i++) { + result = find(node.children[i], position); + } + } + + } else if (node instanceof Block) { + result = find(node.open, position) || find(node.elements, position) || find(node.close, position); + } + + return result || node; +} diff --git a/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts b/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts new file mode 100644 index 0000000000..645cf8a2c9 --- /dev/null +++ b/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { TokenSelectionSupport } from 'vs/editor/contrib/smartSelect/common/tokenSelectionSupport'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; + +class MockJSMode extends MockMode { + + private static _id = new LanguageIdentifier('mockJSMode', 3); + + constructor() { + super(MockJSMode._id); + + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + brackets: [ + ['(', ')'], + ['{', '}'], + ['[', ']'] + ], + + onEnterRules: [ + { + // e.g. /** | */ + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + afterText: /^\s*\*\/$/, + action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' } + }, + { + // e.g. /** ...| + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + action: { indentAction: IndentAction.None, appendText: ' * ' } + }, + { + // e.g. * ...| + beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/, + action: { indentAction: IndentAction.None, appendText: '* ' } + }, + { + // e.g. */| + beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/, + action: { indentAction: IndentAction.None, removeText: 1 } + }, + { + // e.g. *-----*/| + beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/, + action: { indentAction: IndentAction.None, removeText: 1 } + } + ] + })); + } +} + +suite('TokenSelectionSupport', () => { + + let modelService: ModelServiceImpl = null; + let tokenSelectionSupport: TokenSelectionSupport; + let mode: MockJSMode = null; + + setup(() => { + modelService = new ModelServiceImpl(null, new TestConfigurationService()); + tokenSelectionSupport = new TokenSelectionSupport(modelService); + mode = new MockJSMode(); + }); + + teardown(() => { + modelService.dispose(); + mode.dispose(); + }); + + function assertGetRangesToPosition(text: string[], lineNumber: number, column: number, ranges: Range[]): void { + let uri = URI.file('test.js'); + modelService.createModel(text.join('\n'), mode, uri); + + let actual = tokenSelectionSupport.getRangesToPositionSync(uri, new Position(lineNumber, column)); + + let actualStr = actual.map(r => new Range(r.range.startLineNumber, r.range.startColumn, r.range.endLineNumber, r.range.endColumn).toString()); + let desiredStr = ranges.map(r => String(r)); + + assert.deepEqual(actualStr, desiredStr); + + modelService.destroyModel(uri); + } + + test('getRangesToPosition #1', () => { + + assertGetRangesToPosition([ + 'function a(bar, foo){', + '\tif (bar) {', + '\t\treturn (bar + (2 * foo))', + '\t}', + '}' + ], 3, 20, [ + new Range(1, 1, 5, 2), + new Range(1, 21, 5, 2), + new Range(2, 1, 4, 3), + new Range(2, 11, 4, 3), + new Range(3, 1, 4, 2), + new Range(3, 1, 3, 27), + new Range(3, 10, 3, 27), + new Range(3, 11, 3, 26), + new Range(3, 17, 3, 26), + new Range(3, 18, 3, 25), + // new Range(3, 19, 3, 20) + ]); + }); +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/snippet/browser/snippet.md b/src/vs/editor/contrib/snippet/browser/snippet.md new file mode 100644 index 0000000000..f370f5cd33 --- /dev/null +++ b/src/vs/editor/contrib/snippet/browser/snippet.md @@ -0,0 +1,46 @@ + +Tabstops +-- + +With tabstops you can make the editor cursor move inside a snippet. Use `$1`, `$2` to specify cursor locations. The number is the order in which tabstops will be visited, whereas `$0` denotes the final cursor position. Multiple tabstops are linked and updated in sync. + +Placeholders +-- + +Placeholders are tabstops with values, like `${1:foo}`. The placeholder text will be inserted and selected such that it can be easily changed. Placeholders can nested, like `${1:another ${2:placeholder}}`. + +Choice +-- + +Placeholders can have choices as values. The syntax is a comma-separated enumeration of values, enclosed with the pipe-character, e.g. `${1|one,two,three|}`. When inserted and selected choices will prompt the user to pick one of the values. + +Variables +-- + +With `$name` or `${name:default}` you can insert the value of a variable. When a variable isn’t set its *default* or the empty string is inserted. When a varibale is unknown (that is, its name isn’t defined) the name of the variable is inserted and it is transformed into a placeholder. The following variables can be used: + +* `TM_SELECTED_TEXT` The currently selected text or the empty string +* `TM_CURRENT_LINE` The contents of the current line +* `TM_CURRENT_WORD` The contents of the word under cursor or the empty string +* `TM_LINE_INDEX` The zero-index based line number +* `TM_LINE_NUMBER` The one-index based line number +* `TM_FILENAME` The filename of the current document +* `TM_DIRECTORY` The direcorty of the current document +* `TM_FILEPATH` The full file path of the current document + + +Grammar +-- + +Below is the EBNF for snippets. With `\` (backslash) you can escape `$`, `}` and `\`, within choice elements the backslash also escapes comma and pipe characters. + +``` +any ::= tabstop | placeholder | choice | variable | text +tabstop ::= '$' int | '${' int '}' +placeholder ::= '${' int ':' any '}' +choice ::= '${' int '|' text (',' text)* '|}' +variable ::= '$' var | '${' var }' | '${' var ':' any '}' +var ::= [_a-zA-Z] [_a-zA-Z0-9]* +int ::= [0-9]+ +text ::= .* +``` diff --git a/src/vs/editor/contrib/snippet/browser/snippetController2.ts b/src/vs/editor/contrib/snippet/browser/snippetController2.ts new file mode 100644 index 0000000000..4cf332962f --- /dev/null +++ b/src/vs/editor/contrib/snippet/browser/snippetController2.ts @@ -0,0 +1,227 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { RawContextKey, IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { commonEditorContribution, CommonEditorRegistry, EditorCommand } from 'vs/editor/common/editorCommonExtensions'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { SnippetSession } from './snippetSession'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { showSimpleSuggestions } from 'vs/editor/contrib/suggest/browser/suggest'; +import { ISuggestion } from 'vs/editor/common/modes'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Choice } from 'vs/editor/contrib/snippet/browser/snippetParser'; + +@commonEditorContribution +export class SnippetController2 { + + static get(editor: ICommonCodeEditor): SnippetController2 { + return editor.getContribution('snippetController2'); + } + + static InSnippetMode = new RawContextKey('inSnippetMode', false); + static HasNextTabstop = new RawContextKey('hasNextTabstop', false); + static HasPrevTabstop = new RawContextKey('hasPrevTabstop', false); + + private readonly _inSnippet: IContextKey; + private readonly _hasNextTabstop: IContextKey; + private readonly _hasPrevTabstop: IContextKey; + + private _session: SnippetSession; + private _snippetListener: IDisposable[] = []; + private _modelVersionId: number; + private _currentChoice: Choice; + + constructor( + private readonly _editor: ICommonCodeEditor, + @IContextKeyService contextKeyService: IContextKeyService + ) { + this._inSnippet = SnippetController2.InSnippetMode.bindTo(contextKeyService); + this._hasNextTabstop = SnippetController2.HasNextTabstop.bindTo(contextKeyService); + this._hasPrevTabstop = SnippetController2.HasPrevTabstop.bindTo(contextKeyService); + } + + dispose(): void { + this._inSnippet.reset(); + this._hasPrevTabstop.reset(); + this._hasNextTabstop.reset(); + dispose(this._session); + } + + getId(): string { + return 'snippetController2'; + } + + insert( + template: string, + overwriteBefore: number = 0, overwriteAfter: number = 0, + undoStopBefore: boolean = true, undoStopAfter: boolean = true + ): void { + + // don't listen while inserting the snippet + // as that is the inflight state causing cancelation + this._snippetListener = dispose(this._snippetListener); + + if (undoStopBefore) { + this._editor.getModel().pushStackElement(); + } + + if (!this._session) { + this._modelVersionId = this._editor.getModel().getAlternativeVersionId(); + this._session = new SnippetSession(this._editor, template, overwriteBefore, overwriteAfter); + this._session.insert(); + } else { + this._session.merge(template, overwriteBefore, overwriteAfter); + } + + if (undoStopAfter) { + this._editor.getModel().pushStackElement(); + } + + this._updateState(); + + this._snippetListener = [ + this._editor.onDidChangeModel(() => this.cancel()), + this._editor.onDidChangeCursorSelection(() => this._updateState()) + ]; + } + + private _updateState(): void { + if (!this._session) { + // canceled in the meanwhile + return; + } + + if (this._modelVersionId === this._editor.getModel().getAlternativeVersionId()) { + // undo until the 'before' state happened + // and makes use cancel snippet mode + return this.cancel(); + } + + if (!this._session.hasPlaceholder) { + // don't listen for selection changes and don't + // update context keys when the snippet is plain text + return this.cancel(); + } + + if (this._session.isAtLastPlaceholder || !this._session.isSelectionWithinPlaceholders()) { + return this.cancel(); + } + + this._inSnippet.set(true); + this._hasPrevTabstop.set(!this._session.isAtFirstPlaceholder); + this._hasNextTabstop.set(!this._session.isAtLastPlaceholder); + + this._handleChoice(); + } + + private _handleChoice(): void { + const { choice } = this._session; + if (!choice) { + this._currentChoice = undefined; + return; + } + if (this._currentChoice !== choice) { + this._currentChoice = choice; + + this._editor.setSelections(this._editor.getSelections() + .map(s => Selection.fromPositions(s.getStartPosition())) + ); + + const [first] = choice.options; + + showSimpleSuggestions(this._editor, choice.options.map((option, i) => { + + // let before = choice.options.slice(0, i); + // let after = choice.options.slice(i); + + return { + type: 'value', + label: option.value, + insertText: option.value, + // insertText: `\${1|${after.concat(before).join(',')}|}$0`, + // snippetType: 'textmate', + sortText: String(i), + overwriteAfter: first.value.length + }; + })); + } + } + + finish(): void { + while (this._inSnippet.get()) { + this.next(); + } + } + + cancel(): void { + this._inSnippet.reset(); + this._hasPrevTabstop.reset(); + this._hasNextTabstop.reset(); + dispose(this._snippetListener); + dispose(this._session); + this._session = undefined; + this._modelVersionId = -1; + } + + prev(): void { + this._session.prev(); + this._updateState(); + } + + next(): void { + this._session.next(); + this._updateState(); + } +} + + +const CommandCtor = EditorCommand.bindToContribution(SnippetController2.get); + +CommonEditorRegistry.registerEditorCommand(new CommandCtor({ + id: 'jumpToNextSnippetPlaceholder', + precondition: ContextKeyExpr.and(SnippetController2.InSnippetMode, SnippetController2.HasNextTabstop), + handler: ctrl => ctrl.next(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(30), + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Tab + } +})); +CommonEditorRegistry.registerEditorCommand(new CommandCtor({ + id: 'jumpToPrevSnippetPlaceholder', + precondition: ContextKeyExpr.and(SnippetController2.InSnippetMode, SnippetController2.HasPrevTabstop), + handler: ctrl => ctrl.prev(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(30), + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.Tab + } +})); +CommonEditorRegistry.registerEditorCommand(new CommandCtor({ + id: 'leaveSnippet', + precondition: SnippetController2.InSnippetMode, + handler: ctrl => ctrl.cancel(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(30), + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } +})); + +CommonEditorRegistry.registerEditorCommand(new CommandCtor({ + id: 'acceptSnippet', + precondition: SnippetController2.InSnippetMode, + handler: ctrl => ctrl.finish(), + // kbOpts: { + // weight: CommonEditorRegistry.commandWeight(30), + // kbExpr: EditorContextKeys.textFocus, + // primary: KeyCode.Enter, + // } +})); diff --git a/src/vs/editor/contrib/snippet/browser/snippetParser.ts b/src/vs/editor/contrib/snippet/browser/snippetParser.ts new file mode 100644 index 0000000000..c61443f17f --- /dev/null +++ b/src/vs/editor/contrib/snippet/browser/snippetParser.ts @@ -0,0 +1,714 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { CharCode } from 'vs/base/common/charCode'; + +export enum TokenType { + Dollar, + Colon, + Comma, + CurlyOpen, + CurlyClose, + Backslash, + Forwardslash, + Pipe, + Int, + VariableName, + Format, + EOF +} + +export interface Token { + type: TokenType; + pos: number; + len: number; +} + + +export class Scanner { + + private static _table: { [ch: number]: TokenType } = { + [CharCode.DollarSign]: TokenType.Dollar, + [CharCode.Colon]: TokenType.Colon, + [CharCode.Comma]: TokenType.Comma, + [CharCode.OpenCurlyBrace]: TokenType.CurlyOpen, + [CharCode.CloseCurlyBrace]: TokenType.CurlyClose, + [CharCode.Backslash]: TokenType.Backslash, + [CharCode.Slash]: TokenType.Forwardslash, + [CharCode.Pipe]: TokenType.Pipe, + }; + + static isDigitCharacter(ch: number): boolean { + return ch >= CharCode.Digit0 && ch <= CharCode.Digit9; + } + + static isVariableCharacter(ch: number): boolean { + return ch === CharCode.Underline + || (ch >= CharCode.a && ch <= CharCode.z) + || (ch >= CharCode.A && ch <= CharCode.Z); + } + + value: string; + pos: number; + + constructor() { + this.text(''); + } + + text(value: string) { + this.value = value; + this.pos = 0; + } + + tokenText(token: Token): string { + return this.value.substr(token.pos, token.len); + } + + next(): Token { + + if (this.pos >= this.value.length) { + return { type: TokenType.EOF, pos: this.pos, len: 0 }; + } + + let pos = this.pos; + let len = 0; + let ch = this.value.charCodeAt(pos); + let type: TokenType; + + // static types + type = Scanner._table[ch]; + if (typeof type === 'number') { + this.pos += 1; + return { type, pos, len: 1 }; + } + + // number + if (Scanner.isDigitCharacter(ch)) { + type = TokenType.Int; + do { + len += 1; + ch = this.value.charCodeAt(pos + len); + } while (Scanner.isDigitCharacter(ch)); + + this.pos += len; + return { type, pos, len }; + } + + // variable name + if (Scanner.isVariableCharacter(ch)) { + type = TokenType.VariableName; + do { + ch = this.value.charCodeAt(pos + (++len)); + } while (Scanner.isVariableCharacter(ch) || Scanner.isDigitCharacter(ch)); + + this.pos += len; + return { type, pos, len }; + } + + + // format + type = TokenType.Format; + do { + len += 1; + ch = this.value.charCodeAt(pos + len); + } while ( + !isNaN(ch) + && typeof Scanner._table[ch] === 'undefined' // not static token + && !Scanner.isDigitCharacter(ch) // not number + && !Scanner.isVariableCharacter(ch) // not variable + ); + + this.pos += len; + return { type, pos, len }; + } +} + +export abstract class Marker { + + readonly _markerBrand: any; + + public parent: Marker; + protected _children: Marker[] = []; + + appendChild(child: Marker): this { + if (child instanceof Text && this._children[this._children.length - 1] instanceof Text) { + // this and previous child are text -> merge them + (this._children[this._children.length - 1]).value += child.value; + } else { + // normal adoption of child + child.parent = this; + this._children.push(child); + } + return this; + } + + replace(child: Marker, others: Marker[]): void { + const { parent } = child; + const idx = parent.children.indexOf(child); + const newChildren = parent.children.slice(0); + newChildren.splice(idx, 1, ...others); + parent._children = newChildren; + others.forEach(node => node.parent = parent); + } + + get children(): Marker[] { + return this._children; + } + + get snippet(): TextmateSnippet { + let candidate: Marker = this; + while (true) { + if (!candidate) { + return undefined; + } + if (candidate instanceof TextmateSnippet) { + return candidate; + } + candidate = candidate.parent; + } + } + + toString() { + return this.children.reduce((prev, cur) => prev + cur.toString(), ''); + } + + abstract toTextmateString(): string; + + len(): number { + return 0; + } + + abstract clone(): Marker; +} + +export class Text extends Marker { + constructor(public value: string) { + super(); + } + toString() { + return this.value; + } + toTextmateString(): string { + return this.value.replace(/\$|}|\\/g, '\\$&'); + } + len(): number { + return this.value.length; + } + clone(): Text { + return new Text(this.value); + } +} + +export class Placeholder extends Marker { + + static compareByIndex(a: Placeholder, b: Placeholder): number { + if (a.index === b.index) { + return 0; + } else if (a.isFinalTabstop) { + return 1; + } else if (b.isFinalTabstop) { + return -1; + } else if (a.index < b.index) { + return -1; + } else if (a.index > b.index) { + return 1; + } else { + return 0; + } + } + + constructor(public index: number) { + super(); + } + + get isFinalTabstop() { + return this.index === 0; + } + + get choice(): Choice { + return this._children.length === 1 && this._children[0] instanceof Choice + ? this._children[0] as Choice + : undefined; + } + + toTextmateString(): string { + if (this.children.length === 0) { + return `\$${this.index}`; + } else if (this.choice) { + return `\${${this.index}|${this.choice.toTextmateString()}|}`; + } else { + return `\${${this.index}:${this.children.map(child => child.toTextmateString()).join('')}}`; + } + } + + clone(): Placeholder { + let ret = new Placeholder(this.index); + ret._children = this.children.map(child => child.clone()); + return ret; + } +} + +export class Choice extends Marker { + + readonly options: Text[] = []; + + appendChild(marker: Marker): this { + if (marker instanceof Text) { + marker.parent = this; + this.options.push(marker); + } + return this; + } + + toString() { + return this.options[0].value; + } + + toTextmateString(): string { + return this.options + .map(option => option.value.replace(/\||,/g, '\\$&')) + .join(','); + } + + len(): number { + return this.options[0].len(); + } + + clone(): Choice { + let ret = new Choice(); + this.options.forEach(ret.appendChild, ret); + return ret; + } +} + +export class Variable extends Marker { + + constructor(public name: string) { + super(); + } + + resolve(resolver: VariableResolver): boolean { + const value = resolver.resolve(this); + if (value !== undefined) { + this._children = [new Text(value)]; + return true; + } + return false; + } + + toTextmateString(): string { + if (this.children.length === 0) { + return `\${${this.name}}`; + } else { + return `\${${this.name}:${this.children.map(child => child.toTextmateString()).join('')}}`; + } + } + + clone(): Variable { + const ret = new Variable(this.name); + ret._children = this.children.map(child => child.clone()); + return ret; + } +} + +export interface VariableResolver { + resolve(variable: Variable): string | undefined; +} + +function walk(marker: Marker[], visitor: (marker: Marker) => boolean): void { + const stack = [...marker]; + while (stack.length > 0) { + const marker = stack.shift(); + const recurse = visitor(marker); + if (!recurse) { + break; + } + stack.unshift(...marker.children); + } +} + +export class TextmateSnippet extends Marker { + + private _placeholders: { all: Placeholder[], last: Placeholder }; + + get placeholderInfo() { + if (!this._placeholders) { + // fill in placeholders + let all: Placeholder[] = []; + let last: Placeholder; + this.walk(function (candidate) { + if (candidate instanceof Placeholder) { + all.push(candidate); + last = !last || last.index < candidate.index ? candidate : last; + } + return true; + }); + this._placeholders = { all, last }; + } + return this._placeholders; + } + + get placeholders(): Placeholder[] { + const { all } = this.placeholderInfo; + return all; + } + + offset(marker: Marker): number { + let pos = 0; + let found = false; + this.walk(candidate => { + if (candidate === marker) { + found = true; + return false; + } + pos += candidate.len(); + return true; + }); + + if (!found) { + return -1; + } + return pos; + } + + fullLen(marker: Marker): number { + let ret = 0; + walk([marker], marker => { + ret += marker.len(); + return true; + }); + return ret; + } + + enclosingPlaceholders(placeholder: Placeholder): Placeholder[] { + let ret: Placeholder[] = []; + let { parent } = placeholder; + while (parent) { + if (parent instanceof Placeholder) { + ret.push(parent); + } + parent = parent.parent; + } + return ret; + } + + resolveVariables(resolver: VariableResolver): this { + this.walk(candidate => { + if (candidate instanceof Variable) { + if (candidate.resolve(resolver)) { + this._placeholders = undefined; + } + } + return true; + }); + return this; + } + + appendChild(child: Marker) { + this._placeholders = undefined; + return super.appendChild(child); + } + + replace(child: Marker, others: Marker[]): void { + this._placeholders = undefined; + return super.replace(child, others); + } + + toTextmateString(): string { + return this.children.reduce((prev, cur) => prev + cur.toTextmateString(), ''); + } + + clone(): TextmateSnippet { + let ret = new TextmateSnippet(); + this._children = this.children.map(child => child.clone()); + return ret; + } + + walk(visitor: (marker: Marker) => boolean): void { + walk(this.children, visitor); + } +} + +export class SnippetParser { + + static escape(value: string): string { + return value.replace(/\$|}|\\/g, '\\$&'); + } + + private _scanner = new Scanner(); + private _token: Token; + + text(value: string): string { + return this.parse(value).toString(); + } + + parse(value: string, insertFinalTabstop?: boolean, enforceFinalTabstop?: boolean): TextmateSnippet { + + this._scanner.text(value); + this._token = this._scanner.next(); + + const snippet = new TextmateSnippet(); + while (this._parse(snippet)) { + // nothing + } + + // fill in values for placeholders. the first placeholder of an index + // that has a value defines the value for all placeholders with that index + const placeholderDefaultValues = new Map(); + const incompletePlaceholders: Placeholder[] = []; + let placeholderCount = 0; + snippet.walk(marker => { + if (marker instanceof Placeholder) { + placeholderCount += 1; + if (marker.isFinalTabstop) { + placeholderDefaultValues.set(0); + } else if (!placeholderDefaultValues.has(marker.index) && marker.children.length > 0) { + placeholderDefaultValues.set(marker.index, marker.children); + } else { + incompletePlaceholders.push(marker); + } + } + return true; + }); + for (const placeholder of incompletePlaceholders) { + if (placeholderDefaultValues.has(placeholder.index)) { + const clone = new Placeholder(placeholder.index); + for (const child of placeholderDefaultValues.get(placeholder.index)) { + clone.appendChild(child.clone()); + } + snippet.replace(placeholder, [clone]); + } + } + + if (!enforceFinalTabstop) { + enforceFinalTabstop = placeholderCount > 0 && insertFinalTabstop; + } + + if (!placeholderDefaultValues.has(0) && enforceFinalTabstop) { + // the snippet uses placeholders but has no + // final tabstop defined -> insert at the end + snippet.appendChild(new Placeholder(0)); + } + + return snippet; + } + + private _accept(type: TokenType): boolean; + private _accept(type: TokenType, value: true): string; + private _accept(type: TokenType, value?: boolean): boolean | string { + if (type === undefined || this._token.type === type) { + let ret = !value ? true : this._scanner.tokenText(this._token); + this._token = this._scanner.next(); + return ret; + } + return false; + } + + private _backTo(token: Token): false { + this._scanner.pos = token.pos + token.len; + this._token = token; + return false; + } + + private _parse(marker: Marker): boolean { + return this._parseEscaped(marker) + || this._parseTabstopOrVariableName(marker) + || this._parseComplexPlaceholder(marker) + || this._parseComplexVariable(marker) + || this._parseAnything(marker); + } + + // \$, \\, \} -> just text + private _parseEscaped(marker: Marker): boolean { + let value: string; + if (value = this._accept(TokenType.Backslash, true)) { + // saw a backslash, append escaped token or that backslash + value = this._accept(TokenType.Dollar, true) + || this._accept(TokenType.CurlyClose, true) + || this._accept(TokenType.Backslash, true) + || value; + + marker.appendChild(new Text(value)); + return true; + } + return false; + } + + // $foo -> variable, $1 -> tabstop + private _parseTabstopOrVariableName(parent: Marker): boolean { + let value: string; + const token = this._token; + const match = this._accept(TokenType.Dollar) + && (value = this._accept(TokenType.VariableName, true) || this._accept(TokenType.Int, true)); + + if (!match) { + return this._backTo(token); + } + + parent.appendChild(/^\d+$/.test(value) + ? new Placeholder(Number(value)) + : new Variable(value) + ); + return true; + } + + // ${1:}, ${1} -> placeholder + private _parseComplexPlaceholder(parent: Marker): boolean { + let index: string; + const token = this._token; + const match = this._accept(TokenType.Dollar) + && this._accept(TokenType.CurlyOpen) + && (index = this._accept(TokenType.Int, true)); + + if (!match) { + return this._backTo(token); + } + + const placeholder = new Placeholder(Number(index)); + + if (this._accept(TokenType.Colon)) { + // ${1:} + while (true) { + + // ...} -> done + if (this._accept(TokenType.CurlyClose)) { + parent.appendChild(placeholder); + return true; + } + + if (this._parse(placeholder)) { + continue; + } + + // fallback + parent.appendChild(new Text('${' + index + ':')); + placeholder.children.forEach(parent.appendChild, parent); + return true; + } + } else if (placeholder.index > 0 && this._accept(TokenType.Pipe)) { + // ${1|one,two,three|} + const choice = new Choice(); + + while (true) { + if (this._parseChoiceElement(choice)) { + + if (this._accept(TokenType.Comma)) { + // opt, -> more + continue; + } + + if (this._accept(TokenType.Pipe) && this._accept(TokenType.CurlyClose)) { + // ..|} -> done + placeholder.appendChild(choice); + parent.appendChild(placeholder); + return true; + } + } + + this._backTo(token); + return false; + } + + } else if (this._accept(TokenType.CurlyClose)) { + // ${1} + parent.appendChild(placeholder); + return true; + + } else { + // ${1 <- missing curly or colon + return this._backTo(token); + } + } + + private _parseChoiceElement(parent: Choice): boolean { + const token = this._token; + const values: string[] = []; + + while (true) { + if (this._token.type === TokenType.Comma || this._token.type === TokenType.Pipe) { + break; + } + let value: string; + if (value = this._accept(TokenType.Backslash, true)) { + // \, or \| + value = this._accept(TokenType.Comma, true) + || this._accept(TokenType.Pipe, true) + || value; + } else { + value = this._accept(undefined, true); + } + if (!value) { + // EOF + this._backTo(token); + return false; + } + values.push(value); + } + + if (values.length === 0) { + this._backTo(token); + return false; + } + + parent.appendChild(new Text(values.join(''))); + return true; + } + + // ${foo:}, ${foo} -> variable + private _parseComplexVariable(parent: Marker): boolean { + let name: string; + const token = this._token; + const match = this._accept(TokenType.Dollar) + && this._accept(TokenType.CurlyOpen) + && (name = this._accept(TokenType.VariableName, true)); + + if (!match) { + return this._backTo(token); + } + + const variable = new Variable(name); + + if (this._accept(TokenType.Colon)) { + // ${foo:} + while (true) { + + // ...} -> done + if (this._accept(TokenType.CurlyClose)) { + parent.appendChild(variable); + return true; + } + + if (this._parse(variable)) { + continue; + } + + // fallback + parent.appendChild(new Text('${' + name + ':')); + variable.children.forEach(parent.appendChild, parent); + return true; + } + + } else if (this._accept(TokenType.CurlyClose)) { + // ${foo} + parent.appendChild(variable); + return true; + + } else { + // ${foo <- missing curly or colon + return this._backTo(token); + } + } + + private _parseAnything(marker: Marker): boolean { + if (this._token.type !== TokenType.EOF) { + marker.appendChild(new Text(this._scanner.tokenText(this._token))); + this._accept(undefined); + return true; + } + return false; + } +} diff --git a/src/vs/editor/contrib/snippet/browser/snippetSession.css b/src/vs/editor/contrib/snippet/browser/snippetSession.css new file mode 100644 index 0000000000..5116c5db78 --- /dev/null +++ b/src/vs/editor/contrib/snippet/browser/snippetSession.css @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor.vs .snippet-placeholder { background-color: rgba(10, 50, 100, 0.1); } +.monaco-editor.vs-dark .snippet-placeholder { background-color: rgba(124, 124, 124, 0.1); } +.monaco-editor.hc-black .snippet-placeholder { background-color: rgba(124, 124, 124, 0.1); } + +.monaco-editor.vs .finish-snippet-placeholder { outline: rgba(10, 50, 100, 0.5) solid 1px; } +.monaco-editor.vs-dark .finish-snippet-placeholder { outline: #525252 solid 1px; } +.monaco-editor.hc-black .finish-snippet-placeholder { outline: #525252 solid 1px; } diff --git a/src/vs/editor/contrib/snippet/browser/snippetSession.ts b/src/vs/editor/contrib/snippet/browser/snippetSession.ts new file mode 100644 index 0000000000..69f86f1147 --- /dev/null +++ b/src/vs/editor/contrib/snippet/browser/snippetSession.ts @@ -0,0 +1,442 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./snippetSession'; +import { getLeadingWhitespace } from 'vs/base/common/strings'; +import { ICommonCodeEditor, IModel, TrackedRangeStickiness, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { TextmateSnippet, Placeholder, Choice, SnippetParser } from './snippetParser'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Range } from 'vs/editor/common/core/range'; +import { IPosition } from 'vs/editor/common/core/position'; +import { groupBy } from 'vs/base/common/arrays'; +import { dispose } from 'vs/base/common/lifecycle'; +import { EditorSnippetVariableResolver } from './snippetVariables'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; + +export class OneSnippet { + + private readonly _editor: ICommonCodeEditor; + private readonly _snippet: TextmateSnippet; + private readonly _offset: number; + + private _placeholderDecorations: Map; + private _placeholderGroups: Placeholder[][]; + private _placeholderGroupsIdx: number; + private _nestingLevel: number = 1; + + private static readonly _decor = { + active: ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges, className: 'snippet-placeholder' }), + inactive: ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'snippet-placeholder' }), + activeFinal: ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'finish-snippet-placeholder' }), + inactiveFinal: ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'finish-snippet-placeholder' }), + }; + + constructor(editor: ICommonCodeEditor, snippet: TextmateSnippet, offset: number) { + this._editor = editor; + this._snippet = snippet; + this._offset = offset; + + this._placeholderGroups = groupBy(snippet.placeholders, Placeholder.compareByIndex); + this._placeholderGroupsIdx = -1; + } + + dispose(): void { + if (this._placeholderDecorations) { + this._editor.changeDecorations(accessor => this._placeholderDecorations.forEach(handle => accessor.removeDecoration(handle))); + } + this._placeholderGroups.length = 0; + } + + private _initDecorations(): void { + + if (this._placeholderDecorations) { + // already initialized + return; + } + + this._placeholderDecorations = new Map(); + const model = this._editor.getModel(); + + this._editor.changeDecorations(accessor => { + // create a decoration for each placeholder + for (const placeholder of this._snippet.placeholders) { + const placeholderOffset = this._snippet.offset(placeholder); + const placeholderLen = this._snippet.fullLen(placeholder); + const range = Range.fromPositions( + model.getPositionAt(this._offset + placeholderOffset), + model.getPositionAt(this._offset + placeholderOffset + placeholderLen) + ); + const options = placeholder.isFinalTabstop ? OneSnippet._decor.inactiveFinal : OneSnippet._decor.inactive; + const handle = accessor.addDecoration(range, options); + this._placeholderDecorations.set(placeholder, handle); + } + }); + } + + move(fwd: boolean | undefined): Selection[] { + + this._initDecorations(); + + if (fwd === true && this._placeholderGroupsIdx < this._placeholderGroups.length - 1) { + this._placeholderGroupsIdx += 1; + + } else if (fwd === false && this._placeholderGroupsIdx > 0) { + this._placeholderGroupsIdx -= 1; + + } else { + // the selection of the current placeholder might + // not acurate any more -> simply restore it + } + + return this._editor.getModel().changeDecorations(accessor => { + + const activePlaceholders = new Set(); + + // change stickiness to always grow when typing at its edges + // because these decorations represent the currently active + // tabstop. + // Special case #1: reaching the final tabstop + // Special case #2: placeholders enclosing active placeholders + const selections: Selection[] = []; + for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) { + const id = this._placeholderDecorations.get(placeholder); + const range = this._editor.getModel().getDecorationRange(id); + selections.push(new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)); + + accessor.changeDecorationOptions(id, placeholder.isFinalTabstop ? OneSnippet._decor.activeFinal : OneSnippet._decor.active); + activePlaceholders.add(placeholder); + + for (const enclosingPlaceholder of this._snippet.enclosingPlaceholders(placeholder)) { + const id = this._placeholderDecorations.get(enclosingPlaceholder); + accessor.changeDecorationOptions(id, enclosingPlaceholder.isFinalTabstop ? OneSnippet._decor.activeFinal : OneSnippet._decor.active); + activePlaceholders.add(enclosingPlaceholder); + } + } + + // change stickness to never grow when typing at its edges + // so that in-active tabstops never grow + this._placeholderDecorations.forEach((id, placeholder) => { + if (!activePlaceholders.has(placeholder)) { + accessor.changeDecorationOptions(id, placeholder.isFinalTabstop ? OneSnippet._decor.inactiveFinal : OneSnippet._decor.inactive); + } + }); + + return selections; + }); + } + + get isAtFirstPlaceholder() { + return this._placeholderGroupsIdx <= 0 || this._placeholderGroups.length === 0; + } + + get isAtLastPlaceholder() { + return this._placeholderGroupsIdx === this._placeholderGroups.length - 1; + } + + get hasPlaceholder() { + return this._snippet.placeholders.length > 0; + } + + get placeholderRanges() { + const ret: Range[] = []; + this._placeholderDecorations.forEach((id, placeholder) => { + if (!placeholder.isFinalTabstop) { + const range = this._editor.getModel().getDecorationRange(id); + if (range) { + ret.push(range); + } + } + }); + return ret; + } + + get choice(): Choice { + return this._placeholderGroups[this._placeholderGroupsIdx][0].choice; + } + + merge(others: OneSnippet[]): void { + + const model = this._editor.getModel(); + this._nestingLevel *= 10; + + this._editor.changeDecorations(accessor => { + + // For each active placeholder take one snippet and merge it + // in that the placeholder (can be many for `$1foo$1foo`). Because + // everything is sorted by editor selection we can simply remove + // elements from the beginning of the array + for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) { + const nested = others.shift(); + console.assert(!nested._placeholderDecorations); + + // Massage placeholder-indicies of the nested snippet to be + // sorted right after the insertion point. This ensures we move + // through the placeholders in the correct order + for (const nestedPlaceholder of nested._snippet.placeholderInfo.all) { + if (nestedPlaceholder.isFinalTabstop) { + nestedPlaceholder.index = placeholder.index + ((nested._snippet.placeholderInfo.last.index + 1) / this._nestingLevel); + } else { + nestedPlaceholder.index = placeholder.index + (nestedPlaceholder.index / this._nestingLevel); + } + } + this._snippet.replace(placeholder, nested._snippet.children); + + // Remove the placeholder at which position are inserting + // the snippet and also remove its decoration. + const id = this._placeholderDecorations.get(placeholder); + accessor.removeDecoration(id); + this._placeholderDecorations.delete(placeholder); + + // For each *new* placeholder we create decoration to monitor + // how and if it grows/shrinks. + for (const placeholder of nested._snippet.placeholders) { + const placeholderOffset = nested._snippet.offset(placeholder); + const placeholderLen = nested._snippet.fullLen(placeholder); + const range = Range.fromPositions( + model.getPositionAt(nested._offset + placeholderOffset), + model.getPositionAt(nested._offset + placeholderOffset + placeholderLen) + ); + const handle = accessor.addDecoration(range, OneSnippet._decor.inactive); + this._placeholderDecorations.set(placeholder, handle); + } + } + + // Last, re-create the placeholder groups by sorting placeholders by their index. + this._placeholderGroups = groupBy(this._snippet.placeholders, Placeholder.compareByIndex); + }); + } +} + +export class SnippetSession { + + static adjustWhitespace(model: IModel, position: IPosition, template: string): string { + + const line = model.getLineContent(position.lineNumber); + const lineLeadingWhitespace = getLeadingWhitespace(line, 0, position.column - 1); + const templateLines = template.split(/\r\n|\r|\n/); + + for (let i = 1; i < templateLines.length; i++) { + let templateLeadingWhitespace = getLeadingWhitespace(templateLines[i]); + templateLines[i] = model.normalizeIndentation(lineLeadingWhitespace + templateLeadingWhitespace) + templateLines[i].substr(templateLeadingWhitespace.length); + } + return templateLines.join(model.getEOL()); + } + + static adjustSelection(model: IModel, selection: Selection, overwriteBefore: number, overwriteAfter: number): Selection { + if (overwriteBefore !== 0 || overwriteAfter !== 0) { + let { startLineNumber, startColumn, endLineNumber, endColumn } = selection; + startColumn -= overwriteBefore; + endColumn += overwriteAfter; + + const range = model.validateRange(Range.plusRange(selection, { + startLineNumber, + startColumn, + endLineNumber, + endColumn, + })); + + selection = Selection.createWithDirection( + range.startLineNumber, range.startColumn, + range.endLineNumber, range.endColumn, + selection.getDirection() + ); + } + return selection; + } + + static createEditsAndSnippets(editor: ICommonCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { + + const model = editor.getModel(); + const edits: IIdentifiedSingleEditOperation[] = []; + const snippets: OneSnippet[] = []; + + let delta = 0; + + // know what text the overwrite[Before|After] extensions + // of the primary curser have selected because only when + // secondary selections extend to the same text we can grow them + let firstBeforeText = model.getValueInRange(SnippetSession.adjustSelection(model, editor.getSelection(), overwriteBefore, 0)); + let firstAfterText = model.getValueInRange(SnippetSession.adjustSelection(model, editor.getSelection(), 0, overwriteAfter)); + + // sort selections by their start position but remeber + // the original index. that allows you to create correct + // offset-based selection logic without changing the + // primary selection + const indexedSelection = editor.getSelections() + .map((selection, idx) => ({ selection, idx })) + .sort((a, b) => Range.compareRangesUsingStarts(a.selection, b.selection)); + + for (const { selection, idx } of indexedSelection) { + + // extend selection with the `overwriteBefore` and `overwriteAfter` and then + // compare if this matches the extensions of the primary selection + let extensionBefore = SnippetSession.adjustSelection(model, selection, overwriteBefore, 0); + let extensionAfter = SnippetSession.adjustSelection(model, selection, 0, overwriteAfter); + if (firstBeforeText !== model.getValueInRange(extensionBefore)) { + extensionBefore = selection; + } + if (firstAfterText !== model.getValueInRange(extensionAfter)) { + extensionAfter = selection; + } + + // merge the before and after selection into one + const snippetSelection = selection + .setStartPosition(extensionBefore.startLineNumber, extensionBefore.startColumn) + .setEndPosition(extensionAfter.endLineNumber, extensionAfter.endColumn); + + // adjust the template string to match the indentation and + // whitespace rules of this insert location (can be different for each cursor) + const start = snippetSelection.getStartPosition(); + const adjustedTemplate = SnippetSession.adjustWhitespace(model, start, template); + + const snippet = new SnippetParser() + .parse(adjustedTemplate, true, enforceFinalTabstop) + .resolveVariables(new EditorSnippetVariableResolver(model, selection)); + + const offset = model.getOffsetAt(start) + delta; + delta += snippet.toString().length - model.getValueLengthInRange(snippetSelection); + + // store snippets with the index of their originating selection. + // that ensures the primiary cursor stays primary despite not being + // the one with lowest start position + edits[idx] = EditOperation.replace(snippetSelection, snippet.toString()); + snippets[idx] = new OneSnippet(editor, snippet, offset); + } + + return { edits, snippets }; + } + + private readonly _editor: ICommonCodeEditor; + private readonly _template: string; + private readonly _overwriteBefore: number; + private readonly _overwriteAfter: number; + private _snippets: OneSnippet[] = []; + + constructor(editor: ICommonCodeEditor, template: string, overwriteBefore: number = 0, overwriteAfter: number = 0) { + this._editor = editor; + this._template = template; + this._overwriteBefore = overwriteBefore; + this._overwriteAfter = overwriteAfter; + } + + dispose(): void { + dispose(this._snippets); + } + + insert(): void { + + const model = this._editor.getModel(); + + // make insert edit and start with first selections + const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._overwriteBefore, this._overwriteAfter, false); + this._snippets = snippets; + + this._editor.setSelections(model.pushEditOperations(this._editor.getSelections(), edits, undoEdits => { + if (this._snippets[0].hasPlaceholder) { + return this._move(true); + } else { + return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition())); + } + })); + } + + merge(template: string, overwriteBefore: number = 0, overwriteAfter: number = 0): void { + const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, overwriteBefore, overwriteAfter, true); + + this._editor.setSelections(this._editor.getModel().pushEditOperations(this._editor.getSelections(), edits, undoEdits => { + + for (const snippet of this._snippets) { + snippet.merge(snippets); + } + console.assert(snippets.length === 0); + + if (this._snippets[0].hasPlaceholder) { + return this._move(undefined); + } else { + return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition())); + } + })); + } + + next(): void { + const newSelections = this._move(true); + this._editor.setSelections(newSelections); + } + + prev(): void { + const newSelections = this._move(false); + this._editor.setSelections(newSelections); + } + + private _move(fwd: boolean | undefined): Selection[] { + const selections: Selection[] = []; + for (const snippet of this._snippets) { + const oneSelection = snippet.move(fwd); + selections.push(...oneSelection); + } + return selections; + } + + get isAtFirstPlaceholder() { + return this._snippets[0].isAtFirstPlaceholder; + } + + get isAtLastPlaceholder() { + return this._snippets[0].isAtLastPlaceholder; + } + + get hasPlaceholder() { + return this._snippets[0].hasPlaceholder; + } + + get choice(): Choice { + return this._snippets[0].choice; + } + + isSelectionWithinPlaceholders(): boolean { + + if (!this.hasPlaceholder) { + return false; + } + + const selections = this._editor.getSelections(); + if (selections.length < this._snippets.length) { + // this means we started snippet mode with N + // selections and have M (N > M) selections. + // So one snippet is without selection -> cancel + return false; + } + + const ranges: Range[] = []; + for (const snippet of this._snippets) { + ranges.push(...snippet.placeholderRanges); + } + + if (selections.length > ranges.length) { + return false; + } + + // sort selections and ranges by their start position + // and then make sure each selection is contained by + // a placeholder range + selections.sort(Range.compareRangesUsingStarts); + ranges.sort(Range.compareRangesUsingStarts); + + outer: for (const selection of selections) { + let range: Range; + while (range = ranges.shift()) { + if (range.containsRange(selection)) { + continue outer; + } + } + return false; + } + + return true; + } +} diff --git a/src/vs/editor/contrib/snippet/browser/snippetVariables.ts b/src/vs/editor/contrib/snippet/browser/snippetVariables.ts new file mode 100644 index 0000000000..5ae642a9ab --- /dev/null +++ b/src/vs/editor/contrib/snippet/browser/snippetVariables.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { basename, dirname } from 'vs/base/common/paths'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { Selection } from 'vs/editor/common/core/selection'; +import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { getLeadingWhitespace, commonPrefixLength } from 'vs/base/common/strings'; + +export class EditorSnippetVariableResolver implements VariableResolver { + + static readonly VariableNames = Object.freeze({ + 'SELECTION': true, + 'TM_SELECTED_TEXT': true, + 'TM_CURRENT_LINE': true, + 'TM_CURRENT_WORD': true, + 'TM_LINE_INDEX': true, + 'TM_LINE_NUMBER': true, + 'TM_FILENAME': true, + 'TM_FILENAME_BASE': true, + 'TM_DIRECTORY': true, + 'TM_FILEPATH': true, + }); + + constructor( + private readonly _model: IModel, + private readonly _selection: Selection + ) { + // + } + + resolve(variable: Variable): string { + + const { name } = variable; + + if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') { + let value = this._model.getValueInRange(this._selection) || undefined; + if (value && this._selection.startLineNumber !== this._selection.endLineNumber) { + // Selection is a multiline string which we indentation we now + // need to adjust. We compare the indentation of this variable + // with the indentation at the editor position and add potential + // extra indentation to the value + + const line = this._model.getLineContent(this._selection.startLineNumber); + const lineLeadingWhitespace = getLeadingWhitespace(line, 0, this._selection.startColumn - 1); + + let varLeadingWhitespace = lineLeadingWhitespace; + variable.snippet.walk(marker => { + if (marker === variable) { + return false; + } + if (marker instanceof Text) { + varLeadingWhitespace = getLeadingWhitespace(marker.value.split(/\r\n|\r|\n/).pop()); + } + return true; + }); + const whitespaceCommonLength = commonPrefixLength(varLeadingWhitespace, lineLeadingWhitespace); + + value = value.replace( + /(\r\n|\r|\n)(.*)/g, + (m, newline, rest) => `${newline}${varLeadingWhitespace.substr(whitespaceCommonLength)}${rest}` + ); + } + return value; + + } else if (name === 'TM_CURRENT_LINE') { + return this._model.getLineContent(this._selection.positionLineNumber); + + } else if (name === 'TM_CURRENT_WORD') { + const info = this._model.getWordAtPosition({ + lineNumber: this._selection.positionLineNumber, + column: this._selection.positionColumn + }); + return info && info.word || undefined; + + } else if (name === 'TM_LINE_INDEX') { + return String(this._selection.positionLineNumber - 1); + + } else if (name === 'TM_LINE_NUMBER') { + return String(this._selection.positionLineNumber); + + } else if (name === 'TM_FILENAME') { + return basename(this._model.uri.fsPath); + + } else if (name === 'TM_FILENAME_BASE') { + const name = basename(this._model.uri.fsPath); + const idx = name.lastIndexOf('.'); + if (idx <= 0) { + return name; + } else { + return name.slice(0, idx); + } + + } else if (name === 'TM_DIRECTORY') { + const dir = dirname(this._model.uri.fsPath); + return dir !== '.' ? dir : ''; + + } else if (name === 'TM_FILEPATH') { + return this._model.uri.fsPath; + + } else { + return undefined; + } + } +} diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts new file mode 100644 index 0000000000..271cf8d8fc --- /dev/null +++ b/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts @@ -0,0 +1,588 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; +import { MockCodeEditor, withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { Cursor } from 'vs/editor/common/controller/cursor'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; + +class TestSnippetController extends SnippetController2 { + + constructor( + editor: ICommonCodeEditor, + @IContextKeyService private _contextKeyService: IContextKeyService + ) { + super(editor, _contextKeyService); + } + + isInSnippetMode(): boolean { + return SnippetController2.InSnippetMode.getValue(this._contextKeyService); + } + +} + +suite('SnippetController', () => { + + function snippetTest(cb: (editor: MockCodeEditor, cursor: Cursor, template: string, snippetController: TestSnippetController) => void, lines?: string[]): void { + + if (!lines) { + lines = [ + 'function test() {', + '\tvar x = 3;', + '\tvar arr = [];', + '\t', + '}' + ]; + }; + + withMockCodeEditor(lines, {}, (editor, cursor) => { + editor.getModel().updateOptions({ + insertSpaces: false + }); + let snippetController = editor.registerAndInstantiateContribution(TestSnippetController); + let template = [ + 'for (var ${1:index}; $1 < ${2:array}.length; $1++) {', + '\tvar element = $2[$1];', + '\t$0', + '}' + ].join('\n'); + + cb(editor, cursor, template, snippetController); + snippetController.dispose(); + }); + } + + test('Simple accepted', () => { + snippetTest((editor, cursor, template, snippetController) => { + editor.setPosition({ lineNumber: 4, column: 2 }); + + snippetController.insert(template, 0, 0); + assert.equal(editor.getModel().getLineContent(4), '\tfor (var index; index < array.length; index++) {'); + assert.equal(editor.getModel().getLineContent(5), '\t\tvar element = array[index];'); + assert.equal(editor.getModel().getLineContent(6), '\t\t'); + assert.equal(editor.getModel().getLineContent(7), '\t}'); + + editor.trigger('test', 'type', { text: 'i' }); + assert.equal(editor.getModel().getLineContent(4), '\tfor (var i; i < array.length; i++) {'); + assert.equal(editor.getModel().getLineContent(5), '\t\tvar element = array[i];'); + assert.equal(editor.getModel().getLineContent(6), '\t\t'); + assert.equal(editor.getModel().getLineContent(7), '\t}'); + + snippetController.next(); + editor.trigger('test', 'type', { text: 'arr' }); + assert.equal(editor.getModel().getLineContent(4), '\tfor (var i; i < arr.length; i++) {'); + assert.equal(editor.getModel().getLineContent(5), '\t\tvar element = arr[i];'); + assert.equal(editor.getModel().getLineContent(6), '\t\t'); + assert.equal(editor.getModel().getLineContent(7), '\t}'); + + snippetController.prev(); + editor.trigger('test', 'type', { text: 'j' }); + assert.equal(editor.getModel().getLineContent(4), '\tfor (var j; j < arr.length; j++) {'); + assert.equal(editor.getModel().getLineContent(5), '\t\tvar element = arr[j];'); + assert.equal(editor.getModel().getLineContent(6), '\t\t'); + assert.equal(editor.getModel().getLineContent(7), '\t}'); + + snippetController.next(); + snippetController.next(); + assert.deepEqual(editor.getPosition(), new Position(6, 3)); + }); + }); + + test('Simple canceled', () => { + snippetTest((editor, cursor, template, snippetController) => { + editor.setPosition({ lineNumber: 4, column: 2 }); + + snippetController.insert(template, 0, 0); + assert.equal(editor.getModel().getLineContent(4), '\tfor (var index; index < array.length; index++) {'); + assert.equal(editor.getModel().getLineContent(5), '\t\tvar element = array[index];'); + assert.equal(editor.getModel().getLineContent(6), '\t\t'); + assert.equal(editor.getModel().getLineContent(7), '\t}'); + + snippetController.cancel(); + assert.deepEqual(editor.getPosition(), new Position(4, 16)); + }); + }); + + // test('Stops when deleting lines above', () => { + // snippetTest((editor, cursor, codeSnippet, snippetController) => { + // editor.setPosition({ lineNumber: 4, column: 2 }); + // snippetController.insert(codeSnippet, 0, 0); + + // editor.getModel().applyEdits([{ + // forceMoveMarkers: false, + // identifier: null, + // isAutoWhitespaceEdit: false, + // range: new Range(1, 1, 3, 1), + // text: null + // }]); + + // assert.equal(snippetController.isInSnippetMode(), false); + // }); + // }); + + // test('Stops when deleting lines below', () => { + // snippetTest((editor, cursor, codeSnippet, snippetController) => { + // editor.setPosition({ lineNumber: 4, column: 2 }); + // snippetController.run(codeSnippet, 0, 0); + + // editor.getModel().applyEdits([{ + // forceMoveMarkers: false, + // identifier: null, + // isAutoWhitespaceEdit: false, + // range: new Range(8, 1, 8, 100), + // text: null + // }]); + + // assert.equal(snippetController.isInSnippetMode(), false); + // }); + // }); + + // test('Stops when inserting lines above', () => { + // snippetTest((editor, cursor, codeSnippet, snippetController) => { + // editor.setPosition({ lineNumber: 4, column: 2 }); + // snippetController.run(codeSnippet, 0, 0); + + // editor.getModel().applyEdits([{ + // forceMoveMarkers: false, + // identifier: null, + // isAutoWhitespaceEdit: false, + // range: new Range(1, 100, 1, 100), + // text: '\nHello' + // }]); + + // assert.equal(snippetController.isInSnippetMode(), false); + // }); + // }); + + // test('Stops when inserting lines below', () => { + // snippetTest((editor, cursor, codeSnippet, snippetController) => { + // editor.setPosition({ lineNumber: 4, column: 2 }); + // snippetController.run(codeSnippet, 0, 0); + + // editor.getModel().applyEdits([{ + // forceMoveMarkers: false, + // identifier: null, + // isAutoWhitespaceEdit: false, + // range: new Range(8, 100, 8, 100), + // text: '\nHello' + // }]); + + // assert.equal(snippetController.isInSnippetMode(), false); + // }); + // }); + + test('Stops when calling model.setValue()', () => { + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setPosition({ lineNumber: 4, column: 2 }); + snippetController.insert(codeSnippet, 0, 0); + + editor.getModel().setValue('goodbye'); + + assert.equal(snippetController.isInSnippetMode(), false); + }); + }); + + test('Stops when undoing', () => { + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setPosition({ lineNumber: 4, column: 2 }); + snippetController.insert(codeSnippet, 0, 0); + + editor.getModel().undo(); + + assert.equal(snippetController.isInSnippetMode(), false); + }); + }); + + test('Stops when moving cursor outside', () => { + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setPosition({ lineNumber: 4, column: 2 }); + snippetController.insert(codeSnippet, 0, 0); + + editor.setPosition({ lineNumber: 1, column: 1 }); + + assert.equal(snippetController.isInSnippetMode(), false); + }); + }); + + test('Stops when disconnecting editor model', () => { + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setPosition({ lineNumber: 4, column: 2 }); + snippetController.insert(codeSnippet, 0, 0); + + editor.setModel(null); + + assert.equal(snippetController.isInSnippetMode(), false); + }); + }); + + test('Stops when disposing editor', () => { + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setPosition({ lineNumber: 4, column: 2 }); + snippetController.insert(codeSnippet, 0, 0); + + snippetController.dispose(); + + assert.equal(snippetController.isInSnippetMode(), false); + }); + }); + + test('Final tabstop with multiple selections', () => { + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setSelections([ + new Selection(1, 1, 1, 1), + new Selection(2, 1, 2, 1), + ]); + + codeSnippet = 'foo$0'; + snippetController.insert(codeSnippet, 0, 0); + + assert.equal(editor.getSelections().length, 2); + const [first, second] = editor.getSelections(); + assert.ok(first.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), first.toString()); + assert.ok(second.equalsRange({ startLineNumber: 2, startColumn: 4, endLineNumber: 2, endColumn: 4 }), second.toString()); + }); + + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setSelections([ + new Selection(1, 1, 1, 1), + new Selection(2, 1, 2, 1), + ]); + + codeSnippet = 'foo$0bar'; + snippetController.insert(codeSnippet, 0, 0); + + assert.equal(editor.getSelections().length, 2); + const [first, second] = editor.getSelections(); + assert.ok(first.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), first.toString()); + assert.ok(second.equalsRange({ startLineNumber: 2, startColumn: 4, endLineNumber: 2, endColumn: 4 }), second.toString()); + }); + + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setSelections([ + new Selection(1, 1, 1, 1), + new Selection(1, 5, 1, 5), + ]); + + codeSnippet = 'foo$0bar'; + snippetController.insert(codeSnippet, 0, 0); + + assert.equal(editor.getSelections().length, 2); + const [first, second] = editor.getSelections(); + assert.ok(first.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), first.toString()); + assert.ok(second.equalsRange({ startLineNumber: 1, startColumn: 14, endLineNumber: 1, endColumn: 14 }), second.toString()); + }); + + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setSelections([ + new Selection(1, 1, 1, 1), + new Selection(1, 5, 1, 5), + ]); + + codeSnippet = 'foo\n$0\nbar'; + snippetController.insert(codeSnippet, 0, 0); + + assert.equal(editor.getSelections().length, 2); + const [first, second] = editor.getSelections(); + assert.ok(first.equalsRange({ startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 }), first.toString()); + assert.ok(second.equalsRange({ startLineNumber: 4, startColumn: 1, endLineNumber: 4, endColumn: 1 }), second.toString()); + }); + + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setSelections([ + new Selection(1, 1, 1, 1), + new Selection(1, 5, 1, 5), + ]); + + codeSnippet = 'foo\n$0\nbar'; + snippetController.insert(codeSnippet, 0, 0); + + assert.equal(editor.getSelections().length, 2); + const [first, second] = editor.getSelections(); + assert.ok(first.equalsRange({ startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 }), first.toString()); + assert.ok(second.equalsRange({ startLineNumber: 4, startColumn: 1, endLineNumber: 4, endColumn: 1 }), second.toString()); + }); + + snippetTest((editor, cursor, codeSnippet, snippetController) => { + editor.setSelections([ + new Selection(2, 7, 2, 7), + ]); + + codeSnippet = 'xo$0r'; + snippetController.insert(codeSnippet, 1, 0); + + assert.equal(editor.getSelections().length, 1); + assert.ok(editor.getSelection().equalsRange({ startLineNumber: 2, startColumn: 8, endColumn: 8, endLineNumber: 2 })); + }); + }); + + test('Final tabstop, #11742 simple', () => { + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelection(new Selection(1, 19, 1, 19)); + + codeSnippet = '{{% url_**$1** %}}'; + controller.insert(codeSnippet, 2, 0); + + assert.equal(editor.getSelections().length, 1); + assert.ok(editor.getSelection().equalsRange({ startLineNumber: 1, startColumn: 27, endLineNumber: 1, endColumn: 27 })); + assert.equal(editor.getModel().getValue(), 'example example {{% url_**** %}}'); + + }, ['example example sc']); + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelection(new Selection(1, 3, 1, 3)); + + codeSnippet = [ + 'afterEach((done) => {', + '\t${1}test', + '});' + ].join('\n'); + + controller.insert(codeSnippet, 2, 0); + + assert.equal(editor.getSelections().length, 1); + assert.ok(editor.getSelection().equalsRange({ startLineNumber: 2, startColumn: 2, endLineNumber: 2, endColumn: 2 }), editor.getSelection().toString()); + assert.equal(editor.getModel().getValue(), 'afterEach((done) => {\n\ttest\n});'); + + }, ['af']); + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelection(new Selection(1, 3, 1, 3)); + + codeSnippet = [ + 'afterEach((done) => {', + '${1}\ttest', + '});' + ].join('\n'); + + controller.insert(codeSnippet, 2, 0); + + assert.equal(editor.getSelections().length, 1); + assert.ok(editor.getSelection().equalsRange({ startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 }), editor.getSelection().toString()); + assert.equal(editor.getModel().getValue(), 'afterEach((done) => {\n\ttest\n});'); + + }, ['af']); + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelection(new Selection(1, 9, 1, 9)); + + codeSnippet = [ + 'aft${1}er' + ].join('\n'); + + controller.insert(codeSnippet, 8, 0); + + assert.equal(editor.getModel().getValue(), 'after'); + assert.equal(editor.getSelections().length, 1); + assert.ok(editor.getSelection().equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), editor.getSelection().toString()); + + }, ['afterone']); + }); + + test('Final tabstop, #11742 different indents', () => { + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(2, 4, 2, 4), + new Selection(1, 3, 1, 3) + ]); + + codeSnippet = [ + 'afterEach((done) => {', + '\t${0}test', + '});' + ].join('\n'); + + controller.insert(codeSnippet, 2, 0); + + assert.equal(editor.getSelections().length, 2); + const [first, second] = editor.getSelections(); + + assert.ok(first.equalsRange({ startLineNumber: 5, startColumn: 3, endLineNumber: 5, endColumn: 3 }), first.toString()); + assert.ok(second.equalsRange({ startLineNumber: 2, startColumn: 2, endLineNumber: 2, endColumn: 2 }), second.toString()); + + }, ['af', '\taf']); + }); + + test('Final tabstop, #11890 stay at the beginning', () => { + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(1, 5, 1, 5) + ]); + + codeSnippet = [ + 'afterEach((done) => {', + '${1}\ttest', + '});' + ].join('\n'); + + controller.insert(codeSnippet, 2, 0); + + assert.equal(editor.getSelections().length, 1); + const [first] = editor.getSelections(); + + assert.ok(first.equalsRange({ startLineNumber: 2, startColumn: 3, endLineNumber: 2, endColumn: 3 }), first.toString()); + + }, [' af']); + }); + + test('Final tabstop, no tabstop', () => { + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(1, 3, 1, 3) + ]); + + codeSnippet = 'afterEach'; + + controller.insert(codeSnippet, 2, 0); + + assert.ok(editor.getSelection().equalsRange({ startLineNumber: 1, startColumn: 10, endLineNumber: 1, endColumn: 10 })); + + }, ['af', '\taf']); + }); + + test('Multiple cursor and overwriteBefore/After, issue #11060', () => { + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(1, 7, 1, 7), + new Selection(2, 4, 2, 4) + ]); + + codeSnippet = '_foo'; + controller.insert(codeSnippet, 1, 0); + assert.equal(editor.getModel().getValue(), 'this._foo\nabc_foo'); + + }, ['this._', 'abc']); + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(1, 7, 1, 7), + new Selection(2, 4, 2, 4) + ]); + + codeSnippet = 'XX'; + controller.insert(codeSnippet, 1, 0); + assert.equal(editor.getModel().getValue(), 'this.XX\nabcXX'); + + }, ['this._', 'abc']); + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(1, 7, 1, 7), + new Selection(2, 4, 2, 4), + new Selection(3, 5, 3, 5) + ]); + + codeSnippet = '_foo'; + controller.insert(codeSnippet, 1, 0); + assert.equal(editor.getModel().getValue(), 'this._foo\nabc_foo\ndef_foo'); + + }, ['this._', 'abc', 'def_']); + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(1, 7, 1, 7), // primary at `this._` + new Selection(2, 4, 2, 4), + new Selection(3, 6, 3, 6) + ]); + + codeSnippet = '._foo'; + controller.insert(codeSnippet, 2, 0); + assert.equal(editor.getModel().getValue(), 'this._foo\nabc._foo\ndef._foo'); + + }, ['this._', 'abc', 'def._']); + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(3, 6, 3, 6), // primary at `def._` + new Selection(1, 7, 1, 7), + new Selection(2, 4, 2, 4), + ]); + + codeSnippet = '._foo'; + controller.insert(codeSnippet, 2, 0); + assert.equal(editor.getModel().getValue(), 'this._foo\nabc._foo\ndef._foo'); + + }, ['this._', 'abc', 'def._']); + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(2, 4, 2, 4), // primary at `abc` + new Selection(3, 6, 3, 6), + new Selection(1, 7, 1, 7), + ]); + + codeSnippet = '._foo'; + controller.insert(codeSnippet, 2, 0); + assert.equal(editor.getModel().getValue(), 'this._._foo\na._foo\ndef._._foo'); + + }, ['this._', 'abc', 'def._']); + + }); + + test('Multiple cursor and overwriteBefore/After, #16277', () => { + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(1, 5, 1, 5), + new Selection(2, 5, 2, 5), + ]); + + codeSnippet = 'document'; + controller.insert(codeSnippet, 3, 0); + assert.equal(editor.getModel().getValue(), '{document}\n{document && true}'); + + }, ['{foo}', '{foo && true}']); + }); + + test('Insert snippet twice, #19449', () => { + + snippetTest((editor, cursor, codeSnippet, controller) => { + + editor.setSelections([ + new Selection(1, 1, 1, 1) + ]); + + codeSnippet = 'for (var ${1:i}=0; ${1:i} { + + editor.setSelections([ + new Selection(1, 1, 1, 1) + ]); + + codeSnippet = 'for (let ${1:i}=0; ${1:i} expected=${actual.toString()}`); + } + assert.equal(s.length, 0); + } + + function assertContextKeys(service: MockContextKeyService, inSnippet: boolean, hasPrev: boolean, hasNext: boolean): void { + assert.equal(SnippetController2.InSnippetMode.getValue(service), inSnippet, `inSnippetMode`); + assert.equal(SnippetController2.HasPrevTabstop.getValue(service), hasPrev, `HasPrevTabstop`); + assert.equal(SnippetController2.HasNextTabstop.getValue(service), hasNext, `HasNextTabstop`); + } + + let editor: ICommonCodeEditor; + let model: Model; + let contextKeys: MockContextKeyService; + + setup(function () { + contextKeys = new MockContextKeyService(); + model = Model.createFromString('if\n $state\nfi'); + editor = mockCodeEditor([], { model }); + editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]); + assert.equal(model.getEOL(), '\n'); + }); + + teardown(function () { + model.dispose(); + }); + + test('creation', function () { + const ctrl = new SnippetController2(editor, contextKeys); + assertContextKeys(contextKeys, false, false, false); + ctrl.dispose(); + }); + + test('insert, insert -> abort', function () { + const ctrl = new SnippetController2(editor, contextKeys); + + ctrl.insert('foo${1:bar}foo$0'); + assertContextKeys(contextKeys, true, false, true); + assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11)); + + ctrl.cancel(); + assertContextKeys(contextKeys, false, false, false); + assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11)); + }); + + test('insert, insert -> tab, tab, done', function () { + const ctrl = new SnippetController2(editor, contextKeys); + + ctrl.insert('${1:one}${2:two}$0'); + assertContextKeys(contextKeys, true, false, true); + + ctrl.next(); + assertContextKeys(contextKeys, true, true, true); + + ctrl.next(); + assertContextKeys(contextKeys, false, false, false); + + editor.trigger('test', 'type', { text: '\t' }); + assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), false); + assert.equal(SnippetController2.HasNextTabstop.getValue(contextKeys), false); + assert.equal(SnippetController2.HasPrevTabstop.getValue(contextKeys), false); + }); + + test('insert, insert -> cursor moves out (left/right)', function () { + const ctrl = new SnippetController2(editor, contextKeys); + + ctrl.insert('foo${1:bar}foo$0'); + assertContextKeys(contextKeys, true, false, true); + assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11)); + + // bad selection change + editor.setSelections([new Selection(1, 12, 1, 12), new Selection(2, 16, 2, 16)]); + assertContextKeys(contextKeys, false, false, false); + }); + + test('insert, insert -> cursor moves out (up/down)', function () { + const ctrl = new SnippetController2(editor, contextKeys); + + ctrl.insert('foo${1:bar}foo$0'); + assertContextKeys(contextKeys, true, false, true); + assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11)); + + // bad selection change + editor.setSelections([new Selection(2, 4, 2, 7), new Selection(3, 8, 3, 11)]); + assertContextKeys(contextKeys, false, false, false); + }); + + test('insert, insert -> cursors collapse', function () { + const ctrl = new SnippetController2(editor, contextKeys); + + ctrl.insert('foo${1:bar}foo$0'); + assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true); + assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11)); + + // bad selection change + editor.setSelections([new Selection(1, 4, 1, 7)]); + assertContextKeys(contextKeys, false, false, false); + }); + + test('insert, insert plain text -> no snippet mode', function () { + const ctrl = new SnippetController2(editor, contextKeys); + + ctrl.insert('foobar'); + assertContextKeys(contextKeys, false, false, false); + assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + }); + + test('insert, delete snippet text', function () { + const ctrl = new SnippetController2(editor, contextKeys); + + ctrl.insert('${1:foobar}$0'); + assertContextKeys(contextKeys, true, false, true); + assertSelections(editor, new Selection(1, 1, 1, 7), new Selection(2, 5, 2, 11)); + + editor.trigger('test', 'cut', {}); + assertContextKeys(contextKeys, true, false, true); + assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)); + + editor.trigger('test', 'type', { text: 'abc' }); + assertContextKeys(contextKeys, true, false, true); + + ctrl.next(); + assertContextKeys(contextKeys, false, false, false); + + editor.trigger('test', 'tab', {}); + assertContextKeys(contextKeys, false, false, false); + + // editor.trigger('test', 'type', { text: 'abc' }); + // assertContextKeys(contextKeys, false, false, false); + }); + + test('insert, nested snippet', function () { + const ctrl = new SnippetController2(editor, contextKeys); + ctrl.insert('${1:foobar}$0'); + assertContextKeys(contextKeys, true, false, true); + assertSelections(editor, new Selection(1, 1, 1, 7), new Selection(2, 5, 2, 11)); + + ctrl.insert('far$1boo$0'); + assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8)); + assertContextKeys(contextKeys, true, false, true); + + ctrl.next(); + assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + assertContextKeys(contextKeys, true, true, true); + + ctrl.next(); + assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + assertContextKeys(contextKeys, false, false, false); + }); + + test('insert, nested plain text', function () { + const ctrl = new SnippetController2(editor, contextKeys); + ctrl.insert('${1:foobar}$0'); + assertContextKeys(contextKeys, true, false, true); + assertSelections(editor, new Selection(1, 1, 1, 7), new Selection(2, 5, 2, 11)); + + ctrl.insert('farboo'); + assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + assertContextKeys(contextKeys, true, false, true); + + ctrl.next(); + assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + assertContextKeys(contextKeys, false, false, false); + }); + + test('Nested snippets without final placeholder jumps to next outer placeholder, #27898', function () { + const ctrl = new SnippetController2(editor, contextKeys); + + ctrl.insert('for(const ${1:element} of ${2:array}) {$0}'); + assertContextKeys(contextKeys, true, false, true); + assertSelections(editor, new Selection(1, 11, 1, 18), new Selection(2, 15, 2, 22)); + + ctrl.next(); + assertContextKeys(contextKeys, true, true, true); + assertSelections(editor, new Selection(1, 22, 1, 27), new Selection(2, 26, 2, 31)); + + ctrl.insert('document'); + assertContextKeys(contextKeys, true, true, true); + assertSelections(editor, new Selection(1, 30, 1, 30), new Selection(2, 34, 2, 34)); + + ctrl.next(); + assertContextKeys(contextKeys, false, false, false); + }); + + test('Inconsistent tab stop behaviour with recursive snippets and tab / shift tab, #27543', function () { + const ctrl = new SnippetController2(editor, contextKeys); + ctrl.insert('1_calize(${1:nl}, \'${2:value}\')$0'); + + assertContextKeys(contextKeys, true, false, true); + assertSelections(editor, new Selection(1, 10, 1, 12), new Selection(2, 14, 2, 16)); + + ctrl.insert('2_calize(${1:nl}, \'${2:value}\')$0'); + + assertSelections(editor, new Selection(1, 19, 1, 21), new Selection(2, 23, 2, 25)); + + ctrl.next(); // inner `value` + assertSelections(editor, new Selection(1, 24, 1, 29), new Selection(2, 28, 2, 33)); + + ctrl.next(); // inner `$0` + assertSelections(editor, new Selection(1, 31, 1, 31), new Selection(2, 35, 2, 35)); + + ctrl.next(); // outer `value` + assertSelections(editor, new Selection(1, 34, 1, 39), new Selection(2, 38, 2, 43)); + + ctrl.prev(); // inner `$0` + assertSelections(editor, new Selection(1, 31, 1, 31), new Selection(2, 35, 2, 35)); + }); + + test('Snippet tabstop selecting content of previously entered variable only works when separated by space, #23728', function () { + const ctrl = new SnippetController2(editor, contextKeys); + + model.setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + + ctrl.insert('import ${2:${1:module}} from \'${1:module}\'$0'); + + assertContextKeys(contextKeys, true, false, true); + assertSelections(editor, new Selection(1, 8, 1, 14), new Selection(1, 21, 1, 27)); + + ctrl.insert('foo'); + assertSelections(editor, new Selection(1, 11, 1, 11), new Selection(1, 21, 1, 21)); + + ctrl.next(); // ${2:...} + assertSelections(editor, new Selection(1, 8, 1, 11)); + }); + + test('HTML Snippets Combine, #32211', function () { + const ctrl = new SnippetController2(editor, contextKeys); + + model.setValue(''); + model.updateOptions({ insertSpaces: false, tabSize: 4, trimAutoWhitespace: false }); + editor.setSelection(new Selection(1, 1, 1, 1)); + + ctrl.insert(` + + + + + + + \${7:Document} + + + \${8} + + + `); + ctrl.next(); + ctrl.next(); + ctrl.next(); + ctrl.next(); + assertSelections(editor, new Selection(11, 5, 11, 5)); + + ctrl.insert(''); + assertSelections(editor, new Selection(11, 18, 11, 22)); + }); + +}); diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts new file mode 100644 index 0000000000..1249329456 --- /dev/null +++ b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts @@ -0,0 +1,547 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Scanner, TokenType, SnippetParser, Text, Placeholder, Variable, Marker, TextmateSnippet, Choice } from 'vs/editor/contrib/snippet/browser/snippetParser'; + + +suite('SnippetParser', () => { + + test('Scanner', () => { + + const scanner = new Scanner(); + assert.equal(scanner.next().type, TokenType.EOF); + + scanner.text('abc'); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.EOF); + + scanner.text('{{abc}}'); + assert.equal(scanner.next().type, TokenType.CurlyOpen); + assert.equal(scanner.next().type, TokenType.CurlyOpen); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.CurlyClose); + assert.equal(scanner.next().type, TokenType.CurlyClose); + assert.equal(scanner.next().type, TokenType.EOF); + + scanner.text('abc() '); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.Format); + assert.equal(scanner.next().type, TokenType.EOF); + + scanner.text('abc 123'); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.Format); + assert.equal(scanner.next().type, TokenType.Int); + assert.equal(scanner.next().type, TokenType.EOF); + + scanner.text('$foo'); + assert.equal(scanner.next().type, TokenType.Dollar); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.EOF); + + scanner.text('$foo_bar'); + assert.equal(scanner.next().type, TokenType.Dollar); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.EOF); + + scanner.text('$foo-bar'); + assert.equal(scanner.next().type, TokenType.Dollar); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.Format); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.EOF); + + scanner.text('${foo}'); + assert.equal(scanner.next().type, TokenType.Dollar); + assert.equal(scanner.next().type, TokenType.CurlyOpen); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.CurlyClose); + assert.equal(scanner.next().type, TokenType.EOF); + + scanner.text('${1223:foo}'); + assert.equal(scanner.next().type, TokenType.Dollar); + assert.equal(scanner.next().type, TokenType.CurlyOpen); + assert.equal(scanner.next().type, TokenType.Int); + assert.equal(scanner.next().type, TokenType.Colon); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.CurlyClose); + assert.equal(scanner.next().type, TokenType.EOF); + + scanner.text('\\${}'); + assert.equal(scanner.next().type, TokenType.Backslash); + assert.equal(scanner.next().type, TokenType.Dollar); + assert.equal(scanner.next().type, TokenType.CurlyOpen); + assert.equal(scanner.next().type, TokenType.CurlyClose); + + scanner.text('${foo/regex/format/option}'); + assert.equal(scanner.next().type, TokenType.Dollar); + assert.equal(scanner.next().type, TokenType.CurlyOpen); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.Forwardslash); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.Forwardslash); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.Forwardslash); + assert.equal(scanner.next().type, TokenType.VariableName); + assert.equal(scanner.next().type, TokenType.CurlyClose); + assert.equal(scanner.next().type, TokenType.EOF); + }); + + function assertText(value: string, expected: string) { + const p = new SnippetParser(); + const actual = p.text(value); + assert.equal(actual, expected); + } + + function assertMarker(input: TextmateSnippet | Marker[] | string, ...ctors: Function[]) { + let marker: Marker[]; + if (input instanceof TextmateSnippet) { + marker = input.children; + } else if (typeof input === 'string') { + const p = new SnippetParser(); + marker = p.parse(input).children; + } else { + marker = input; + } + while (marker.length > 0) { + let m = marker.pop(); + let ctor = ctors.pop(); + assert.ok(m instanceof ctor); + } + assert.equal(marker.length, ctors.length); + assert.equal(marker.length, 0); + } + + function assertTextAndMarker(value: string, escaped: string, ...ctors: Function[]) { + assertText(value, escaped); + assertMarker(value, ...ctors); + } + + function assertEscaped(value: string, expected: string) { + const actual = SnippetParser.escape(value); + assert.equal(actual, expected); + } + + test('Parser, escaped', function () { + assertEscaped('foo$0', 'foo\\$0'); + assertEscaped('foo\\$0', 'foo\\\\\\$0'); + assertEscaped('f$1oo$0', 'f\\$1oo\\$0'); + assertEscaped('${1:foo}$0', '\\${1:foo\\}\\$0'); + assertEscaped('$', '\\$'); + }); + + test('Parser, text', () => { + assertText('$', '$'); + assertText('\\\\$', '\\$'); + assertText('{', '{'); + assertText('\\}', '}'); + assertText('\\abc', '\\abc'); + assertText('foo${f:\\}}bar', 'foo}bar'); + assertText('\\{', '\\{'); + assertText('I need \\\\\\$', 'I need \\$'); + assertText('\\', '\\'); + assertText('\\{{', '\\{{'); + assertText('{{', '{{'); + assertText('{{dd', '{{dd'); + assertText('}}', '}}'); + assertText('ff}}', 'ff}}'); + + assertText('farboo', 'farboo'); + assertText('far{{}}boo', 'far{{}}boo'); + assertText('far{{123}}boo', 'far{{123}}boo'); + assertText('far\\{{123}}boo', 'far\\{{123}}boo'); + assertText('far{{id:bern}}boo', 'far{{id:bern}}boo'); + assertText('far{{id:bern {{basel}}}}boo', 'far{{id:bern {{basel}}}}boo'); + assertText('far{{id:bern {{id:basel}}}}boo', 'far{{id:bern {{id:basel}}}}boo'); + assertText('far{{id:bern {{id2:basel}}}}boo', 'far{{id:bern {{id2:basel}}}}boo'); + }); + + + test('Parser, TM text', () => { + assertTextAndMarker('foo${1:bar}}', 'foobar}', Text, Placeholder, Text); + assertTextAndMarker('foo${1:bar}${2:foo}}', 'foobarfoo}', Text, Placeholder, Placeholder, Text); + + assertTextAndMarker('foo${1:bar\\}${2:foo}}', 'foobar}foo', Text, Placeholder); + + let [, placeholder] = new SnippetParser().parse('foo${1:bar\\}${2:foo}}').children; + let { children } = (placeholder); + + assert.equal((placeholder).index, '1'); + assert.ok(children[0] instanceof Text); + assert.equal(children[0].toString(), 'bar}'); + assert.ok(children[1] instanceof Placeholder); + assert.equal(children[1].toString(), 'foo'); + }); + + test('Parser, placeholder', () => { + assertTextAndMarker('farboo', 'farboo', Text); + assertTextAndMarker('far{{}}boo', 'far{{}}boo', Text); + assertTextAndMarker('far{{123}}boo', 'far{{123}}boo', Text); + assertTextAndMarker('far\\{{123}}boo', 'far\\{{123}}boo', Text); + }); + + test('Parser, literal code', () => { + assertTextAndMarker('far`123`boo', 'far`123`boo', Text); + assertTextAndMarker('far\\`123\\`boo', 'far\\`123\\`boo', Text); + }); + + test('Parser, variables/tabstop', () => { + assertTextAndMarker('$far-boo', '-boo', Variable, Text); + assertTextAndMarker('\\$far-boo', '$far-boo', Text); + assertTextAndMarker('far$farboo', 'far', Text, Variable); + assertTextAndMarker('far${farboo}', 'far', Text, Variable); + assertTextAndMarker('$123', '', Placeholder); + assertTextAndMarker('$farboo', '', Variable); + assertTextAndMarker('$far12boo', '', Variable); + assertTextAndMarker('000_${far}_000', '000__000', Text, Variable, Text); + assertTextAndMarker('FFF_${TM_SELECTED_TEXT}_FFF$0', 'FFF__FFF', Text, Variable, Text, Placeholder); + }); + + test('Parser, variables/placeholder with defaults', () => { + assertTextAndMarker('${name:value}', 'value', Variable); + assertTextAndMarker('${1:value}', 'value', Placeholder); + assertTextAndMarker('${1:bar${2:foo}bar}', 'barfoobar', Placeholder); + + assertTextAndMarker('${name:value', '${name:value', Text); + assertTextAndMarker('${1:bar${2:foobar}', '${1:barfoobar', Text, Placeholder); + }); + + test('Parser, placeholder with choice', () => { + + assertTextAndMarker('${1|one,two,three|}', 'one', Placeholder); + assertTextAndMarker('${1|one|}', 'one', Placeholder); + assertTextAndMarker('${1|one1,two2|}', 'one1', Placeholder); + assertTextAndMarker('${1|one1\\,two2|}', 'one1,two2', Placeholder); + assertTextAndMarker('${1|one1\\|two2|}', 'one1|two2', Placeholder); + assertTextAndMarker('${1|one1\\atwo2|}', 'one1\\atwo2', Placeholder); + assertTextAndMarker('${1|one,two,three,|}', '${1|one,two,three,|}', Text); + assertTextAndMarker('${1|one,', '${1|one,', Text); + + const p = new SnippetParser(); + const snippet = p.parse('${1|one,two,three|}'); + assertMarker(snippet, Placeholder); + const expected = [Placeholder, Text, Text, Text]; + snippet.walk(marker => { + assert.equal(marker, expected.shift()); + return true; + }); + }); + + test('Snippet choices: unable to escape comma and pipe, #31521', function () { + assertTextAndMarker('console.log(${1|not\\, not, five, 5, 1 23|});', 'console.log(not, not);', Text, Placeholder, Text); + }); + + test('Marker, toTextmateString()', function () { + + function assertTextsnippetString(input: string, expected: string): void { + const snippet = new SnippetParser().parse(input); + const actual = snippet.toTextmateString(); + assert.equal(actual, expected); + } + + assertTextsnippetString('$1', '$1'); + assertTextsnippetString('\\$1', '\\$1'); + assertTextsnippetString('console.log(${1|not\\, not, five, 5, 1 23|});', 'console.log(${1|not\\, not, five, 5, 1 23|});'); + assertTextsnippetString('console.log(${1|not\\, not, \\| five, 5, 1 23|});', 'console.log(${1|not\\, not, \\| five, 5, 1 23|});'); + assertTextsnippetString('this is text', 'this is text'); + assertTextsnippetString('this ${1:is ${2:nested with $var}}', 'this ${1:is ${2:nested with ${var}}}'); + assertTextsnippetString('this ${1:is ${2:nested with $var}}}', 'this ${1:is ${2:nested with ${var}}}\\}'); + }); + + test('Marker, toTextmateString() <-> identity', function () { + + function assertIdent(input: string): void { + // full loop: (1) parse input, (2) generate textmate string, (3) parse, (4) ensure both trees are equal + const snippet = new SnippetParser().parse(input); + const input2 = snippet.toTextmateString(); + const snippet2 = new SnippetParser().parse(input2); + + function checkCheckChildren(marker1: Marker, marker2: Marker) { + assert.ok(marker1 instanceof Object.getPrototypeOf(marker2).constructor); + assert.ok(marker2 instanceof Object.getPrototypeOf(marker1).constructor); + + assert.equal(marker1.children.length, marker2.children.length); + assert.equal(marker1.toString(), marker2.toString()); + + for (let i = 0; i < marker1.children.length; i++) { + checkCheckChildren(marker1.children[i], marker2.children[i]); + } + } + + checkCheckChildren(snippet, snippet2); + } + + assertIdent('$1'); + assertIdent('\\$1'); + assertIdent('console.log(${1|not\\, not, five, 5, 1 23|});'); + assertIdent('console.log(${1|not\\, not, \\| five, 5, 1 23|});'); + assertIdent('this is text'); + assertIdent('this ${1:is ${2:nested with $var}}'); + assertIdent('this ${1:is ${2:nested with $var}}}'); + assertIdent('this ${1:is ${2:nested with $var}} and repeating $1'); + }); + + test('Parser, choise marker', () => { + const { placeholders } = new SnippetParser().parse('${1|one,two,three|}'); + + assert.equal(placeholders.length, 1); + assert.ok(placeholders[0].choice instanceof Choice); + assert.ok(placeholders[0].children[0] instanceof Choice); + assert.equal((placeholders[0].children[0]).options.length, 3); + + assertText('${1|one,two,three|}', 'one'); + assertText('\\${1|one,two,three|}', '${1|one,two,three|}'); + assertText('${1\\|one,two,three|}', '${1\\|one,two,three|}'); + assertText('${1||}', '${1||}'); + }); + + + test('Parser, only textmate', () => { + const p = new SnippetParser(); + assertMarker(p.parse('far{{}}boo'), Text); + assertMarker(p.parse('far{{123}}boo'), Text); + assertMarker(p.parse('far\\{{123}}boo'), Text); + + assertMarker(p.parse('far$0boo'), Text, Placeholder, Text); + assertMarker(p.parse('far${123}boo'), Text, Placeholder, Text); + assertMarker(p.parse('far\\${123}boo'), Text); + }); + + test('Parser, real world', () => { + let marker = new SnippetParser().parse('console.warn(${1: $TM_SELECTED_TEXT })').children; + + assert.equal(marker[0].toString(), 'console.warn('); + assert.ok(marker[1] instanceof Placeholder); + assert.equal(marker[2].toString(), ')'); + + const placeholder = marker[1]; + assert.equal(placeholder, false); + assert.equal(placeholder.index, '1'); + assert.equal(placeholder.children.length, 3); + assert.ok(placeholder.children[0] instanceof Text); + assert.ok(placeholder.children[1] instanceof Variable); + assert.ok(placeholder.children[2] instanceof Text); + assert.equal(placeholder.children[0].toString(), ' '); + assert.equal(placeholder.children[1].toString(), ''); + assert.equal(placeholder.children[2].toString(), ' '); + + const nestedVariable = placeholder.children[1]; + assert.equal(nestedVariable.name, 'TM_SELECTED_TEXT'); + assert.equal(nestedVariable.children.length, 0); + + marker = new SnippetParser().parse('$TM_SELECTED_TEXT').children; + assert.equal(marker.length, 1); + assert.ok(marker[0] instanceof Variable); + }); + + test('Parser, default placeholder values', () => { + + assertMarker('errorContext: `${1:err}`, error: $1', Text, Placeholder, Text, Placeholder); + + const [, p1, , p2] = new SnippetParser().parse('errorContext: `${1:err}`, error:$1').children; + + assert.equal((p1).index, '1'); + assert.equal((p1).children.length, '1'); + assert.equal(((p1).children[0]), 'err'); + + assert.equal((p2).index, '1'); + assert.equal((p2).children.length, '1'); + assert.equal(((p2).children[0]), 'err'); + }); + + test('Repeated snippet placeholder should always inherit, #31040', function () { + assertText('${1:foo}-abc-$1', 'foo-abc-foo'); + assertText('${1:foo}-abc-${1}', 'foo-abc-foo'); + assertText('${1:foo}-abc-${1:bar}', 'foo-abc-foo'); + assertText('${1}-abc-${1:foo}', 'foo-abc-foo'); + }); + + test('backspace esapce in TM only, #16212', () => { + const actual = new SnippetParser().text('Foo \\\\${abc}bar'); + assert.equal(actual, 'Foo \\bar'); + }); + + test('colon as variable/placeholder value, #16717', () => { + let actual = new SnippetParser().text('${TM_SELECTED_TEXT:foo:bar}'); + assert.equal(actual, 'foo:bar'); + + actual = new SnippetParser().text('${1:foo:bar}'); + assert.equal(actual, 'foo:bar'); + }); + + test('incomplete placeholder', () => { + assertTextAndMarker('${1:}', '', Placeholder); + }); + + test('marker#len', () => { + + function assertLen(template: string, ...lengths: number[]): void { + const snippet = new SnippetParser().parse(template, true); + snippet.walk(m => { + const expected = lengths.shift(); + assert.equal(m.len(), expected); + return true; + }); + assert.equal(lengths.length, 0); + } + + assertLen('text$0', 4, 0); + assertLen('$1text$0', 0, 4, 0); + assertLen('te$1xt$0', 2, 0, 2, 0); + assertLen('errorContext: `${1:err}`, error: $0', 15, 0, 3, 10, 0); + assertLen('errorContext: `${1:err}`, error: $1$0', 15, 0, 3, 10, 0, 3, 0); + assertLen('$TM_SELECTED_TEXT$0', 0, 0); + assertLen('${TM_SELECTED_TEXT:def}$0', 0, 3, 0); + }); + + test('parser, parent node', function () { + let snippet = new SnippetParser().parse('This ${1:is ${2:nested}}$0', true); + + assert.equal(snippet.placeholders.length, 3); + let [first, second] = snippet.placeholders; + assert.equal(first.index, '1'); + assert.equal(second.index, '2'); + assert.ok(second.parent === first); + assert.ok(first.parent === snippet); + + snippet = new SnippetParser().parse('${VAR:default${1:value}}$0', true); + assert.equal(snippet.placeholders.length, 2); + [first] = snippet.placeholders; + assert.equal(first.index, '1'); + + assert.ok(snippet.children[0] instanceof Variable); + assert.ok(first.parent === snippet.children[0]); + }); + + test('TextmateSnippet#enclosingPlaceholders', function () { + let snippet = new SnippetParser().parse('This ${1:is ${2:nested}}$0', true); + let [first, second] = snippet.placeholders; + + assert.deepEqual(snippet.enclosingPlaceholders(first), []); + assert.deepEqual(snippet.enclosingPlaceholders(second), [first]); + }); + + test('TextmateSnippet#offset', () => { + let snippet = new SnippetParser().parse('te$1xt', true); + assert.equal(snippet.offset(snippet.children[0]), 0); + assert.equal(snippet.offset(snippet.children[1]), 2); + assert.equal(snippet.offset(snippet.children[2]), 2); + + snippet = new SnippetParser().parse('${TM_SELECTED_TEXT:def}', true); + assert.equal(snippet.offset(snippet.children[0]), 0); + assert.equal(snippet.offset((snippet.children[0]).children[0]), 0); + + // forgein marker + assert.equal(snippet.offset(new Text('foo')), -1); + }); + + test('TextmateSnippet#placeholder', () => { + let snippet = new SnippetParser().parse('te$1xt$0', true); + let placeholders = snippet.placeholders; + assert.equal(placeholders.length, 2); + + snippet = new SnippetParser().parse('te$1xt$1$0', true); + placeholders = snippet.placeholders; + assert.equal(placeholders.length, 3); + + + snippet = new SnippetParser().parse('te$1xt$2$0', true); + placeholders = snippet.placeholders; + assert.equal(placeholders.length, 3); + + snippet = new SnippetParser().parse('${1:bar${2:foo}bar}$0', true); + placeholders = snippet.placeholders; + assert.equal(placeholders.length, 3); + }); + + test('TextmateSnippet#replace 1/2', function () { + let snippet = new SnippetParser().parse('aaa${1:bbb${2:ccc}}$0', true); + + assert.equal(snippet.placeholders.length, 3); + const [, second] = snippet.placeholders; + assert.equal(second.index, '2'); + + const enclosing = snippet.enclosingPlaceholders(second); + assert.equal(enclosing.length, 1); + assert.equal(enclosing[0].index, '1'); + + let nested = new SnippetParser().parse('ddd$1eee$0', true); + snippet.replace(second, nested.children); + + assert.equal(snippet.toString(), 'aaabbbdddeee'); + assert.equal(snippet.placeholders.length, 4); + assert.equal(snippet.placeholders[0].index, '1'); + assert.equal(snippet.placeholders[1].index, '1'); + assert.equal(snippet.placeholders[2].index, '0'); + assert.equal(snippet.placeholders[3].index, '0'); + + const newEnclosing = snippet.enclosingPlaceholders(snippet.placeholders[1]); + assert.ok(newEnclosing[0] === snippet.placeholders[0]); + assert.equal(newEnclosing.length, 1); + assert.equal(newEnclosing[0].index, '1'); + }); + + test('TextmateSnippet#replace 2/2', function () { + let snippet = new SnippetParser().parse('aaa${1:bbb${2:ccc}}$0', true); + + assert.equal(snippet.placeholders.length, 3); + const [, second] = snippet.placeholders; + assert.equal(second.index, '2'); + + let nested = new SnippetParser().parse('dddeee$0', true); + snippet.replace(second, nested.children); + + assert.equal(snippet.toString(), 'aaabbbdddeee'); + assert.equal(snippet.placeholders.length, 3); + }); + + test('Snippet order for placeholders, #28185', function () { + + const _10 = new Placeholder(10); + const _2 = new Placeholder(2); + + assert.equal(Placeholder.compareByIndex(_10, _2), 1); + }); + + test('Maximum call stack size exceeded, #28983', function () { + new SnippetParser().parse('${1:${foo:${1}}}'); + }); + + test('Snippet can freeze the editor, #30407', function () { + + const seen = new Set(); + + seen.clear(); + new SnippetParser().parse('class ${1:${TM_FILENAME/(?:\\A|_)([A-Za-z0-9]+)(?:\\.rb)?/(?2::\\u$1)/g}} < ${2:Application}Controller\n $3\nend').walk(marker => { + assert.ok(!seen.has(marker)); + seen.add(marker); + return true; + }); + + seen.clear(); + new SnippetParser().parse('${1:${FOO:abc$1def}}').walk(marker => { + assert.ok(!seen.has(marker)); + seen.add(marker); + return true; + }); + }); + + test('Snippets: make parser ignore `${0|choice|}`, #31599', function () { + assertTextAndMarker('${0|foo,bar|}', '${0|foo,bar|}', Text); + assertTextAndMarker('${1|foo,bar|}', 'foo', Placeholder); + }); + + test('[BUG] HTML attribute suggestions: Snippet session does not have end-position set, #33147', function () { + + const { placeholders } = new SnippetParser().parse('src="$1"', true); + const [first, second] = placeholders; + + assert.equal(placeholders.length, 2); + assert.equal(first.index, 1); + assert.equal(second.index, 0); + }); +}); diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts new file mode 100644 index 0000000000..9cc8d9c323 --- /dev/null +++ b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts @@ -0,0 +1,514 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Range } from 'vs/editor/common/core/range'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { SnippetSession } from 'vs/editor/contrib/snippet/browser/snippetSession'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { mockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { Model } from 'vs/editor/common/model/model'; + +suite('SnippetSession', function () { + + let editor: ICommonCodeEditor; + let model: Model; + + function assertSelections(editor: ICommonCodeEditor, ...s: Selection[]) { + for (const selection of editor.getSelections()) { + const actual = s.shift(); + assert.ok(selection.equalsSelection(actual), `actual=${selection.toString()} <> expected=${actual.toString()}`); + } + assert.equal(s.length, 0); + } + + setup(function () { + model = Model.createFromString('function foo() {\n console.log(a);\n}'); + editor = mockCodeEditor([], { model }); + editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]); + assert.equal(model.getEOL(), '\n'); + }); + + teardown(function () { + model.dispose(); + editor.dispose(); + }); + + test('normalize whitespace', function () { + + function assertNormalized(position: IPosition, input: string, expected: string): void { + const actual = SnippetSession.adjustWhitespace(model, position, input); + assert.equal(actual, expected); + } + + assertNormalized(new Position(1, 1), 'foo', 'foo'); + assertNormalized(new Position(1, 1), 'foo\rbar', 'foo\nbar'); + assertNormalized(new Position(1, 1), 'foo\rbar', 'foo\nbar'); + assertNormalized(new Position(2, 5), 'foo\r\tbar', 'foo\n bar'); + assertNormalized(new Position(2, 3), 'foo\r\tbar', 'foo\n bar'); + assertNormalized(new Position(2, 5), 'foo\r\tbar\nfoo', 'foo\n bar\n foo'); + }); + + test('adjust selection (overwrite[Before|After])', function () { + + let range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 1, 0); + assert.ok(range.equalsRange(new Range(1, 1, 1, 2))); + range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 1111, 0); + assert.ok(range.equalsRange(new Range(1, 1, 1, 2))); + range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 0, 10); + assert.ok(range.equalsRange(new Range(1, 2, 1, 12))); + range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 0, 10111); + assert.ok(range.equalsRange(new Range(1, 2, 1, 17))); + + }); + + test('text edits & selection', function () { + const session = new SnippetSession(editor, 'foo${1:bar}foo$0'); + session.insert(); + assert.equal(editor.getModel().getValue(), 'foobarfoofunction foo() {\n foobarfooconsole.log(a);\n}'); + + assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11)); + session.next(); + assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14)); + }); + + test('text edit with reversed selection', function () { + + const session = new SnippetSession(editor, '${1:bar}$0'); + editor.setSelections([new Selection(2, 5, 2, 5), new Selection(1, 1, 1, 1)]); + + session.insert(); + assert.equal(model.getValue(), 'barfunction foo() {\n barconsole.log(a);\n}'); + assertSelections(editor, new Selection(2, 5, 2, 8), new Selection(1, 1, 1, 4)); + }); + + test('snippets, repeated tabstops', function () { + const session = new SnippetSession(editor, '${1:abc}foo${1:abc}$0'); + session.insert(); + assertSelections(editor, + new Selection(1, 1, 1, 4), new Selection(1, 7, 1, 10), + new Selection(2, 5, 2, 8), new Selection(2, 11, 2, 14), + ); + session.next(); + assertSelections(editor, + new Selection(1, 10, 1, 10), + new Selection(2, 14, 2, 14), + ); + }); + + test('snippets, just text', function () { + const session = new SnippetSession(editor, 'foobar'); + session.insert(); + assert.equal(model.getValue(), 'foobarfunction foo() {\n foobarconsole.log(a);\n}'); + assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + }); + + test('snippets, selections and new text with newlines', () => { + + const session = new SnippetSession(editor, 'foo\n\t${1:bar}\n$0'); + session.insert(); + + assert.equal(editor.getModel().getValue(), 'foo\n bar\nfunction foo() {\n foo\n bar\n console.log(a);\n}'); + + assertSelections(editor, new Selection(2, 5, 2, 8), new Selection(5, 9, 5, 12)); + + session.next(); + assertSelections(editor, new Selection(3, 1, 3, 1), new Selection(6, 5, 6, 5)); + }); + + test('snippets, selections -> next/prev', () => { + + const session = new SnippetSession(editor, 'f$1oo${2:bar}foo$0'); + session.insert(); + + // @ $2 + assertSelections(editor, new Selection(1, 2, 1, 2), new Selection(2, 6, 2, 6)); + // @ $1 + session.next(); + assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11)); + // @ $2 + session.prev(); + assertSelections(editor, new Selection(1, 2, 1, 2), new Selection(2, 6, 2, 6)); + // @ $1 + session.next(); + assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11)); + // @ $0 + session.next(); + assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14)); + }); + + test('snippets, selections & typing', function () { + const session = new SnippetSession(editor, 'f${1:oo}_$2_$0'); + session.insert(); + + editor.trigger('test', 'type', { text: 'X' }); + session.next(); + editor.trigger('test', 'type', { text: 'bar' }); + + // go back to ${2:oo} which is now just 'X' + session.prev(); + assertSelections(editor, new Selection(1, 2, 1, 3), new Selection(2, 6, 2, 7)); + + // go forward to $1 which is now 'bar' + session.next(); + assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11)); + + // go to final tabstop + session.next(); + assert.equal(model.getValue(), 'fX_bar_function foo() {\n fX_bar_console.log(a);\n}'); + assertSelections(editor, new Selection(1, 8, 1, 8), new Selection(2, 12, 2, 12)); + }); + + test('snippets, insert shorter snippet into non-empty selection', function () { + model.setValue('foo_bar_foo'); + editor.setSelections([new Selection(1, 1, 1, 4), new Selection(1, 9, 1, 12)]); + + new SnippetSession(editor, 'x$0').insert(); + assert.equal(model.getValue(), 'x_bar_x'); + assertSelections(editor, new Selection(1, 2, 1, 2), new Selection(1, 8, 1, 8)); + }); + + test('snippets, insert longer snippet into non-empty selection', function () { + model.setValue('foo_bar_foo'); + editor.setSelections([new Selection(1, 1, 1, 4), new Selection(1, 9, 1, 12)]); + + new SnippetSession(editor, 'LONGER$0').insert(); + assert.equal(model.getValue(), 'LONGER_bar_LONGER'); + assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(1, 18, 1, 18)); + }); + + test('snippets, don\'t grow final tabstop', function () { + model.setValue('foo_zzz_foo'); + editor.setSelection(new Selection(1, 5, 1, 8)); + const session = new SnippetSession(editor, '$1bar$0'); + session.insert(); + + assertSelections(editor, new Selection(1, 5, 1, 5)); + editor.trigger('test', 'type', { text: 'foo-' }); + + session.next(); + assert.equal(model.getValue(), 'foo_foo-bar_foo'); + assertSelections(editor, new Selection(1, 12, 1, 12)); + + editor.trigger('test', 'type', { text: 'XXX' }); + assert.equal(model.getValue(), 'foo_foo-barXXX_foo'); + session.prev(); + assertSelections(editor, new Selection(1, 5, 1, 9)); + session.next(); + assertSelections(editor, new Selection(1, 15, 1, 15)); + }); + + test('snippets, don\'t merge touching tabstops 1/2', function () { + + const session = new SnippetSession(editor, '$1$2$3$0'); + session.insert(); + assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)); + + session.next(); + assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)); + + session.next(); + assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)); + + session.next(); + assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)); + + session.prev(); + session.prev(); + session.prev(); + assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)); + editor.trigger('test', 'type', { text: '111' }); + + session.next(); + editor.trigger('test', 'type', { text: '222' }); + + session.next(); + editor.trigger('test', 'type', { text: '333' }); + + session.next(); + assert.equal(model.getValue(), '111222333function foo() {\n 111222333console.log(a);\n}'); + assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14)); + + session.prev(); + assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + session.prev(); + assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8)); + session.prev(); + assertSelections(editor, new Selection(1, 1, 1, 4), new Selection(2, 5, 2, 8)); + }); + test('snippets, don\'t merge touching tabstops 2/2', function () { + + const session = new SnippetSession(editor, '$1$2$3$0'); + session.insert(); + assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)); + + editor.trigger('test', 'type', { text: '111' }); + + session.next(); + assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8)); + editor.trigger('test', 'type', { text: '222' }); + + session.next(); + assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + editor.trigger('test', 'type', { text: '333' }); + + session.next(); + assert.equal(session.isAtLastPlaceholder, true); + }); + + test('snippets, gracefully move over final tabstop', function () { + const session = new SnippetSession(editor, '${1}bar$0'); + session.insert(); + + assert.equal(session.isAtLastPlaceholder, false); + assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)); + + session.next(); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8)); + + session.next(); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8)); + }); + + test('snippets, overwriting nested placeholder', function () { + const session = new SnippetSession(editor, 'log(${1:"$2"});$0'); + session.insert(); + assertSelections(editor, new Selection(1, 5, 1, 7), new Selection(2, 9, 2, 11)); + + editor.trigger('test', 'type', { text: 'XXX' }); + assert.equal(model.getValue(), 'log(XXX);function foo() {\n log(XXX);console.log(a);\n}'); + + session.next(); + assert.equal(session.isAtLastPlaceholder, false); + // assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + + session.next(); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14)); + }); + + test('snippets, selections and snippet ranges', function () { + const session = new SnippetSession(editor, '${1:foo}farboo${2:bar}$0'); + session.insert(); + assert.equal(model.getValue(), 'foofarboobarfunction foo() {\n foofarboobarconsole.log(a);\n}'); + assertSelections(editor, new Selection(1, 1, 1, 4), new Selection(2, 5, 2, 8)); + + assert.equal(session.isSelectionWithinPlaceholders(), true); + + editor.setSelections([new Selection(1, 1, 1, 1)]); + assert.equal(session.isSelectionWithinPlaceholders(), false); + + editor.setSelections([new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10)]); + assert.equal(session.isSelectionWithinPlaceholders(), false); // in snippet, outside placeholder + + editor.setSelections([new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10), new Selection(1, 1, 1, 1)]); + assert.equal(session.isSelectionWithinPlaceholders(), false); // in snippet, outside placeholder + + editor.setSelections([new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10), new Selection(2, 20, 2, 21)]); + assert.equal(session.isSelectionWithinPlaceholders(), false); + + // reset selection to placeholder + session.next(); + assert.equal(session.isSelectionWithinPlaceholders(), true); + assertSelections(editor, new Selection(1, 10, 1, 13), new Selection(2, 14, 2, 17)); + + // reset selection to placeholder + session.next(); + assert.equal(session.isSelectionWithinPlaceholders(), true); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 13, 1, 13), new Selection(2, 17, 2, 17)); + }); + + test('snippets, nested sessions', function () { + + model.setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + + const first = new SnippetSession(editor, 'foo${2:bar}foo$0'); + first.insert(); + assert.equal(model.getValue(), 'foobarfoo'); + assertSelections(editor, new Selection(1, 4, 1, 7)); + + const second = new SnippetSession(editor, 'ba${1:zzzz}$0'); + second.insert(); + assert.equal(model.getValue(), 'foobazzzzfoo'); + assertSelections(editor, new Selection(1, 6, 1, 10)); + + second.next(); + assert.equal(second.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 10, 1, 10)); + + first.next(); + assert.equal(first.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 13, 1, 13)); + }); + + test('snippets, typing at final tabstop', function () { + + const session = new SnippetSession(editor, 'farboo$0'); + session.insert(); + assert.equal(session.isAtLastPlaceholder, true); + assert.equal(session.isSelectionWithinPlaceholders(), false); + + editor.trigger('test', 'type', { text: 'XXX' }); + assert.equal(session.isSelectionWithinPlaceholders(), false); + }); + + test('snippets, typing at beginning', function () { + + editor.setSelection(new Selection(1, 2, 1, 2)); + const session = new SnippetSession(editor, 'farboo$0'); + session.insert(); + + editor.setSelection(new Selection(1, 2, 1, 2)); + assert.equal(session.isSelectionWithinPlaceholders(), false); + assert.equal(session.isAtLastPlaceholder, true); + + editor.trigger('test', 'type', { text: 'XXX' }); + assert.equal(model.getLineContent(1), 'fXXXfarboounction foo() {'); + assert.equal(session.isSelectionWithinPlaceholders(), false); + + session.next(); + assertSelections(editor, new Selection(1, 11, 1, 11)); + }); + + test('snippets, typing with nested placeholder', function () { + + editor.setSelection(new Selection(1, 1, 1, 1)); + const session = new SnippetSession(editor, 'This ${1:is ${2:nested}}.$0'); + session.insert(); + assertSelections(editor, new Selection(1, 6, 1, 15)); + + session.next(); + assertSelections(editor, new Selection(1, 9, 1, 15)); + + editor.trigger('test', 'cut', {}); + assertSelections(editor, new Selection(1, 9, 1, 9)); + + editor.trigger('test', 'type', { text: 'XXX' }); + session.prev(); + assertSelections(editor, new Selection(1, 6, 1, 12)); + }); + + test('snippets, snippet with variables', function () { + const session = new SnippetSession(editor, '@line=$TM_LINE_NUMBER$0'); + session.insert(); + + assert.equal(model.getValue(), '@line=1function foo() {\n @line=2console.log(a);\n}'); + assertSelections(editor, new Selection(1, 8, 1, 8), new Selection(2, 12, 2, 12)); + }); + + test('snippets, merge', function () { + editor.setSelection(new Selection(1, 1, 1, 1)); + const session = new SnippetSession(editor, 'This ${1:is ${2:nested}}.$0'); + session.insert(); + session.next(); + assertSelections(editor, new Selection(1, 9, 1, 15)); + + session.merge('really ${1:nested}$0'); + assertSelections(editor, new Selection(1, 16, 1, 22)); + + session.next(); + assertSelections(editor, new Selection(1, 22, 1, 22)); + assert.equal(session.isAtLastPlaceholder, false); + + session.next(); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 23, 1, 23)); + + session.prev(); + editor.trigger('test', 'type', { text: 'AAA' }); + + // back to `really ${1:nested}` + session.prev(); + assertSelections(editor, new Selection(1, 16, 1, 22)); + + // back to `${1:is ...}` which now grew + session.prev(); + assertSelections(editor, new Selection(1, 6, 1, 25)); + }); + + test('Snippet placeholder index incorrect after using 2+ snippets in a row that each end with a placeholder, #30769', function () { + editor.getModel().setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + const session = new SnippetSession(editor, 'test ${1:replaceme}'); + session.insert(); + + editor.trigger('test', 'type', { text: '1' }); + editor.trigger('test', 'type', { text: '\n' }); + assert.equal(editor.getModel().getValue(), 'test 1\n'); + + session.merge('test ${1:replaceme}'); + editor.trigger('test', 'type', { text: '2' }); + editor.trigger('test', 'type', { text: '\n' }); + + assert.equal(editor.getModel().getValue(), 'test 1\ntest 2\n'); + + session.merge('test ${1:replaceme}'); + editor.trigger('test', 'type', { text: '3' }); + editor.trigger('test', 'type', { text: '\n' }); + + assert.equal(editor.getModel().getValue(), 'test 1\ntest 2\ntest 3\n'); + + session.merge('test ${1:replaceme}'); + editor.trigger('test', 'type', { text: '4' }); + editor.trigger('test', 'type', { text: '\n' }); + + assert.equal(editor.getModel().getValue(), 'test 1\ntest 2\ntest 3\ntest 4\n'); + }); + + test('Snippet variable text isn\'t whitespace normalised, #31124', function () { + editor.getModel().setValue([ + 'start', + '\t\t-one', + '\t\t-two', + 'end' + ].join('\n')); + + editor.getModel().updateOptions({ insertSpaces: false }); + editor.setSelection(new Selection(2, 2, 3, 7)); + + new SnippetSession(editor, '
\n\t$TM_SELECTED_TEXT\n
$0').insert(); + + let expected = [ + 'start', + '\t
', + '\t\t\t-one', + '\t\t\t-two', + '\t
', + 'end' + ].join('\n'); + + assert.equal(editor.getModel().getValue(), expected); + + editor.getModel().setValue([ + 'start', + '\t\t-one', + '\t-two', + 'end' + ].join('\n')); + + editor.getModel().updateOptions({ insertSpaces: false }); + editor.setSelection(new Selection(2, 2, 3, 7)); + + new SnippetSession(editor, '
\n\t$TM_SELECTED_TEXT\n
$0').insert(); + + expected = [ + 'start', + '\t
', + '\t\t\t-one', + '\t\t-two', + '\t
', + 'end' + ].join('\n'); + + assert.equal(editor.getModel().getValue(), expected); + }); +}); + diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts new file mode 100644 index 0000000000..b27eb1de88 --- /dev/null +++ b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts @@ -0,0 +1,138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { isWindows } from 'vs/base/common/platform'; +import URI from 'vs/base/common/uri'; +import { Selection } from 'vs/editor/common/core/selection'; +import { EditorSnippetVariableResolver } from 'vs/editor/contrib/snippet/browser/snippetVariables'; +import { SnippetParser, Variable } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { Model } from 'vs/editor/common/model/model'; + +suite('Snippet Variables Resolver', function () { + + let model: Model; + let resolver: EditorSnippetVariableResolver; + + setup(function () { + model = Model.createFromString([ + 'this is line one', + 'this is line two', + ' this is line three' + ].join('\n'), undefined, undefined, URI.parse('file:///foo/files/text.txt')); + + resolver = new EditorSnippetVariableResolver(model, new Selection(1, 1, 1, 1)); + }); + + teardown(function () { + model.dispose(); + }); + + function assertVariableResolve(resolver: EditorSnippetVariableResolver, varName: string, expected: string) { + const snippet = new SnippetParser().parse(`$${varName}`); + const variable = snippet.children[0]; + variable.resolve(resolver); + if (variable.children.length === 0) { + assert.equal(undefined, expected); + } else { + assert.equal(variable.toString(), expected); + } + } + + test('editor variables, basics', function () { + assertVariableResolve(resolver, 'TM_FILENAME', 'text.txt'); + assertVariableResolve(resolver, 'something', undefined); + }); + + test('editor variables, file/dir', function () { + + assertVariableResolve(resolver, 'TM_FILENAME', 'text.txt'); + if (!isWindows) { + assertVariableResolve(resolver, 'TM_DIRECTORY', '/foo/files'); + assertVariableResolve(resolver, 'TM_FILEPATH', '/foo/files/text.txt'); + } + + resolver = new EditorSnippetVariableResolver( + Model.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')), + new Selection(1, 1, 1, 1) + ); + assertVariableResolve(resolver, 'TM_FILENAME', 'ghi'); + if (!isWindows) { + assertVariableResolve(resolver, 'TM_DIRECTORY', '/abc/def'); + assertVariableResolve(resolver, 'TM_FILEPATH', '/abc/def/ghi'); + } + + resolver = new EditorSnippetVariableResolver( + Model.createFromString('', undefined, undefined, URI.parse('mem:fff.ts')), + new Selection(1, 1, 1, 1) + ); + assertVariableResolve(resolver, 'TM_DIRECTORY', ''); + assertVariableResolve(resolver, 'TM_FILEPATH', 'fff.ts'); + + }); + + test('editor variables, selection', function () { + + resolver = new EditorSnippetVariableResolver(model, new Selection(1, 2, 2, 3)); + assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); + assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line two'); + assertVariableResolve(resolver, 'TM_LINE_INDEX', '1'); + assertVariableResolve(resolver, 'TM_LINE_NUMBER', '2'); + + resolver = new EditorSnippetVariableResolver(model, new Selection(2, 3, 1, 2)); + assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); + assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line one'); + assertVariableResolve(resolver, 'TM_LINE_INDEX', '0'); + assertVariableResolve(resolver, 'TM_LINE_NUMBER', '1'); + + resolver = new EditorSnippetVariableResolver(model, new Selection(1, 2, 1, 2)); + assertVariableResolve(resolver, 'TM_SELECTED_TEXT', undefined); + + assertVariableResolve(resolver, 'TM_CURRENT_WORD', 'this'); + + resolver = new EditorSnippetVariableResolver(model, new Selection(3, 1, 3, 1)); + assertVariableResolve(resolver, 'TM_CURRENT_WORD', undefined); + + }); + + test('TextmateSnippet, resolve variable', function () { + const snippet = new SnippetParser().parse('"$TM_CURRENT_WORD"', true); + assert.equal(snippet.toString(), '""'); + snippet.resolveVariables(resolver); + assert.equal(snippet.toString(), '"this"'); + + }); + + test('TextmateSnippet, resolve variable with default', function () { + const snippet = new SnippetParser().parse('"${TM_CURRENT_WORD:foo}"', true); + assert.equal(snippet.toString(), '"foo"'); + snippet.resolveVariables(resolver); + assert.equal(snippet.toString(), '"this"'); + }); + + test('More useful environment variables for snippets, #32737', function () { + + assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'text'); + + resolver = new EditorSnippetVariableResolver( + Model.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')), + new Selection(1, 1, 1, 1) + ); + assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'ghi'); + + resolver = new EditorSnippetVariableResolver( + Model.createFromString('', undefined, undefined, URI.parse('mem:.git')), + new Selection(1, 1, 1, 1) + ); + assertVariableResolve(resolver, 'TM_FILENAME_BASE', '.git'); + + resolver = new EditorSnippetVariableResolver( + Model.createFromString('', undefined, undefined, URI.parse('mem:foo.')), + new Selection(1, 1, 1, 1) + ); + assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'foo'); + }); +}); diff --git a/src/vs/editor/contrib/suggest/browser/completionModel.ts b/src/vs/editor/contrib/suggest/browser/completionModel.ts new file mode 100644 index 0000000000..c0f4b5ff44 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/completionModel.ts @@ -0,0 +1,211 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { fuzzyScore } from 'vs/base/common/filters'; +import { ISuggestSupport } from 'vs/editor/common/modes'; +import { ISuggestionItem, SnippetConfig } from './suggest'; + +export interface ICompletionItem extends ISuggestionItem { + matches?: number[]; + score?: number; + idx?: number; +} + +export interface ICompletionStats { + suggestionCount: number; + snippetCount: number; + textCount: number; + [name: string]: any; +} + +export class LineContext { + leadingLineContent: string; + characterCountDelta: number; +} + +export class CompletionModel { + + private readonly _column: number; + private readonly _items: ISuggestionItem[]; + private readonly _snippetCompareFn = CompletionModel._compareCompletionItems; + + private _lineContext: LineContext; + private _filteredItems: ICompletionItem[]; + private _isIncomplete: boolean; + private _stats: ICompletionStats; + + constructor(items: ISuggestionItem[], column: number, lineContext: LineContext, snippetConfig?: SnippetConfig) { + this._items = items; + this._column = column; + this._lineContext = lineContext; + + if (snippetConfig === 'top') { + this._snippetCompareFn = CompletionModel._compareCompletionItemsSnippetsUp; + } else if (snippetConfig === 'bottom') { + this._snippetCompareFn = CompletionModel._compareCompletionItemsSnippetsDown; + } + } + + get lineContext(): LineContext { + return this._lineContext; + } + + set lineContext(value: LineContext) { + if (this._lineContext.leadingLineContent !== value.leadingLineContent + || this._lineContext.characterCountDelta !== value.characterCountDelta) { + + this._lineContext = value; + this._filteredItems = undefined; + } + } + + get items(): ICompletionItem[] { + this._ensureCachedState(); + return this._filteredItems; + } + + get incomplete(): boolean { + this._ensureCachedState(); + return this._isIncomplete; + } + + resolveIncompleteInfo(): { incomplete: ISuggestSupport[], complete: ISuggestionItem[] } { + const incomplete: ISuggestSupport[] = []; + const complete: ISuggestionItem[] = []; + + for (const item of this._items) { + if (!item.container.incomplete) { + complete.push(item); + } else if (incomplete.indexOf(item.support) < 0) { + incomplete.push(item.support); + } + } + + return { incomplete, complete }; + } + + get stats(): ICompletionStats { + this._ensureCachedState(); + return this._stats; + } + + private _ensureCachedState(): void { + if (!this._filteredItems) { + this._createCachedState(); + } + } + + private _createCachedState(): void { + this._filteredItems = []; + this._isIncomplete = false; + this._stats = { suggestionCount: 0, snippetCount: 0, textCount: 0 }; + + const { leadingLineContent, characterCountDelta } = this._lineContext; + let word = ''; + + for (let i = 0; i < this._items.length; i++) { + + const item = this._items[i]; + const { suggestion, container } = item; + + // collect those supports that signaled having + // an incomplete result + this._isIncomplete = this._isIncomplete || container.incomplete; + + // 'word' is that remainder of the current line that we + // filter and score against. In theory each suggestion uses a + // differnet word, but in practice not - that's why we cache + const wordLen = suggestion.overwriteBefore + characterCountDelta - (item.position.column - this._column); + if (word.length !== wordLen) { + word = wordLen === 0 ? '' : leadingLineContent.slice(-wordLen); + } + + if (wordLen === 0) { + // when there is nothing to score against, don't + // event try to do. Use a const rank and rely on + // the fallback-sort using the initial sort order. + // use a score of `-100` because that is out of the + // bound of values `fuzzyScore` will return + item.score = -100; + + } else if (typeof suggestion.filterText === 'string') { + // when there is a `filterText` it must match the `word`. + // if it matches we check with the label to compute highlights + // and if that doesn't yield a result we have no highlights, + // despite having the match + let match = fuzzyScore(word, suggestion.filterText, suggestion.overwriteBefore); + if (!match) { + continue; + } + item.score = match[0]; + item.matches = []; + match = fuzzyScore(word, suggestion.label, suggestion.overwriteBefore); + if (match) { + item.matches = match[1]; + } + } else { + // by default match `word` against the `label` + let match = fuzzyScore(word, suggestion.label, suggestion.overwriteBefore); + if (match) { + item.score = match[0]; + item.matches = match[1]; + } else { + continue; + } + } + + item.idx = i; + + this._filteredItems.push(item); + + // update stats + this._stats.suggestionCount++; + switch (suggestion.type) { + case 'snippet': this._stats.snippetCount++; break; + case 'text': this._stats.textCount++; break; + } + } + + this._filteredItems.sort(this._snippetCompareFn); + } + + private static _compareCompletionItems(a: ICompletionItem, b: ICompletionItem): number { + if (a.score > b.score) { + return -1; + } else if (a.score < b.score) { + return 1; + } else if (a.idx < b.idx) { + return -1; + } else if (a.idx > b.idx) { + return 1; + } else { + return 0; + } + } + + private static _compareCompletionItemsSnippetsDown(a: ICompletionItem, b: ICompletionItem): number { + if (a.suggestion.type !== b.suggestion.type) { + if (a.suggestion.type === 'snippet') { + return 1; + } else if (b.suggestion.type === 'snippet') { + return -1; + } + } + return CompletionModel._compareCompletionItems(a, b); + } + + private static _compareCompletionItemsSnippetsUp(a: ICompletionItem, b: ICompletionItem): number { + if (a.suggestion.type !== b.suggestion.type) { + if (a.suggestion.type === 'snippet') { + return -1; + } else if (b.suggestion.type === 'snippet') { + return 1; + } + } + return CompletionModel._compareCompletionItems(a, b); + } +} diff --git a/src/vs/editor/contrib/suggest/browser/media/Class_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Class_16x.svg new file mode 100644 index 0000000000..5ef1c6f80b --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Class_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Class_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Class_inverse_16x.svg new file mode 100644 index 0000000000..c43aad29ef --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Class_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/ColorPalette_16x.svg b/src/vs/editor/contrib/suggest/browser/media/ColorPalette_16x.svg new file mode 100644 index 0000000000..2af5cc6fae --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/ColorPalette_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/ColorPalette_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/ColorPalette_inverse_16x.svg new file mode 100644 index 0000000000..7afb32b895 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/ColorPalette_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Constant_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Constant_16x.svg new file mode 100644 index 0000000000..ed2a175100 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Constant_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Constant_16x_inverse.svg b/src/vs/editor/contrib/suggest/browser/media/Constant_16x_inverse.svg new file mode 100644 index 0000000000..173e427f96 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Constant_16x_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Document_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Document_16x.svg new file mode 100644 index 0000000000..13ded2953e --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Document_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Document_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Document_inverse_16x.svg new file mode 100644 index 0000000000..949a376216 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Document_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/EnumItem_16x.svg b/src/vs/editor/contrib/suggest/browser/media/EnumItem_16x.svg new file mode 100644 index 0000000000..aa901ec193 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/EnumItem_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/EnumItem_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/EnumItem_inverse_16x.svg new file mode 100644 index 0000000000..791759092f --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/EnumItem_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Enumerator_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Enumerator_16x.svg new file mode 100644 index 0000000000..e4a9551fd5 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Enumerator_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Enumerator_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Enumerator_inverse_16x.svg new file mode 100644 index 0000000000..d8e9f4f107 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Enumerator_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Event_16x_vscode.svg b/src/vs/editor/contrib/suggest/browser/media/Event_16x_vscode.svg new file mode 100644 index 0000000000..0e202ec10b --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Event_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Event_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/browser/media/Event_16x_vscode_inverse.svg new file mode 100644 index 0000000000..a508edcd3d --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Event_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Field_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Field_16x.svg new file mode 100644 index 0000000000..c6cb5362b3 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Field_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Field_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Field_inverse_16x.svg new file mode 100644 index 0000000000..5fc48ceff0 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Field_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Folder_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Folder_16x.svg new file mode 100644 index 0000000000..3d64ae71db --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Folder_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Folder_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Folder_inverse_16x.svg new file mode 100644 index 0000000000..13b18d1801 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Folder_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode.svg b/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode.svg new file mode 100644 index 0000000000..5511fc9e23 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode_inverse.svg new file mode 100644 index 0000000000..604d994cbd --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_16x.svg b/src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_16x.svg new file mode 100644 index 0000000000..4a69c4a038 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_inverse_16x.svg new file mode 100644 index 0000000000..decbf2c403 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Interface_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Interface_16x.svg new file mode 100644 index 0000000000..958a792742 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Interface_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Interface_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Interface_inverse_16x.svg new file mode 100644 index 0000000000..f7c2934a55 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Interface_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/LocalVariable_16x_vscode.svg b/src/vs/editor/contrib/suggest/browser/media/LocalVariable_16x_vscode.svg new file mode 100644 index 0000000000..e78894b6c6 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/LocalVariable_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/LocalVariable_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/browser/media/LocalVariable_16x_vscode_inverse.svg new file mode 100644 index 0000000000..44a44b489d --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/LocalVariable_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Method_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Method_16x.svg new file mode 100644 index 0000000000..2be9daa5f5 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Method_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Method_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Method_inverse_16x.svg new file mode 100644 index 0000000000..d3c2c571d9 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Method_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Misc_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Misc_16x.svg new file mode 100644 index 0000000000..13ff00b234 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Misc_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Misc_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Misc_inverse_16x.svg new file mode 100644 index 0000000000..50a038657b --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Misc_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Namespace_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Namespace_16x.svg new file mode 100644 index 0000000000..dab07dd5ad --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Namespace_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Namespace_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Namespace_inverse_16x.svg new file mode 100644 index 0000000000..9b9a44c52d --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Namespace_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Operator_16x_vscode.svg b/src/vs/editor/contrib/suggest/browser/media/Operator_16x_vscode.svg new file mode 100644 index 0000000000..ba2f2d091c --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Operator_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Operator_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/browser/media/Operator_16x_vscode_inverse.svg new file mode 100644 index 0000000000..21e1e814b2 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Operator_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Property_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Property_16x.svg new file mode 100644 index 0000000000..fb1c74cf77 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Property_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Property_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Property_inverse_16x.svg new file mode 100644 index 0000000000..f90781897a --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Property_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Ruler_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Ruler_16x.svg new file mode 100644 index 0000000000..2e8e88fef0 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Ruler_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Ruler_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Ruler_inverse_16x.svg new file mode 100644 index 0000000000..373ab812f9 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Ruler_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Snippet_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Snippet_16x.svg new file mode 100644 index 0000000000..8bf3b9f67d --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Snippet_16x.svg @@ -0,0 +1,47 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/vs/editor/contrib/suggest/browser/media/Snippet_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Snippet_inverse_16x.svg new file mode 100644 index 0000000000..501ff9c617 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Snippet_inverse_16x.svg @@ -0,0 +1,43 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/vs/editor/contrib/suggest/browser/media/String_16x.svg b/src/vs/editor/contrib/suggest/browser/media/String_16x.svg new file mode 100644 index 0000000000..35e744ce90 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/String_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/String_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/String_inverse_16x.svg new file mode 100644 index 0000000000..1ac0cf99ac --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/String_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Structure_16x_vscode.svg b/src/vs/editor/contrib/suggest/browser/media/Structure_16x_vscode.svg new file mode 100644 index 0000000000..e776cbc565 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Structure_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Structure_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/browser/media/Structure_16x_vscode_inverse.svg new file mode 100644 index 0000000000..1b76b62be9 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Structure_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Template_16x_vscode.svg b/src/vs/editor/contrib/suggest/browser/media/Template_16x_vscode.svg new file mode 100644 index 0000000000..788cc8d645 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Template_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/Template_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/browser/media/Template_16x_vscode_inverse.svg new file mode 100644 index 0000000000..6cec71cb03 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/Template_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/close-dark.svg b/src/vs/editor/contrib/suggest/browser/media/close-dark.svg new file mode 100644 index 0000000000..751e89b3b0 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/close-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/close.svg b/src/vs/editor/contrib/suggest/browser/media/close.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/info.svg b/src/vs/editor/contrib/suggest/browser/media/info.svg new file mode 100644 index 0000000000..6578b81ea3 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/suggest.css b/src/vs/editor/contrib/suggest/browser/media/suggest.css new file mode 100644 index 0000000000..55120eae34 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/suggest.css @@ -0,0 +1,322 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Suggest widget*/ +.monaco-editor .suggest-widget { + z-index: 40; +} + +/** Initial widths **/ + +.monaco-editor .suggest-widget { + width: 430px; +} + +.monaco-editor .suggest-widget > .message, +.monaco-editor .suggest-widget > .tree, +.monaco-editor .suggest-widget > .details { + width: 100%; + border-style: solid; + border-width: 1px; + box-sizing: border-box; +} + +.monaco-editor.hc-black .suggest-widget > .message, +.monaco-editor.hc-black .suggest-widget > .tree, +.monaco-editor.hc-black .suggest-widget > .details { + border-width: 2px; +} + +/** Adjust width when docs are expanded to the side **/ +.monaco-editor .suggest-widget.docs-side { + width: 660px; +} + +.monaco-editor .suggest-widget.docs-side > .tree, +.monaco-editor .suggest-widget.docs-side > .details { + width: 50%; + float: left; +} + +.monaco-editor .suggest-widget.docs-side.list-right > .tree, +.monaco-editor .suggest-widget.docs-side.list-right > .details { + float: right; +} + + +/* Styles for Message element for when widget is loading or is empty */ +.monaco-editor .suggest-widget > .message { + padding-left: 22px; +} + +/** Styles for the list element **/ +.monaco-editor .suggest-widget > .tree { + height: 100%; +} + + + +/** Styles for each row in the list element **/ + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row { + display: flex; + -mox-box-sizing: border-box; + box-sizing: border-box; + padding-right: 10px; + background-repeat: no-repeat; + background-position: 2px 2px; + white-space: nowrap; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents { + flex: 1; + height: 100%; + overflow: hidden; + padding-left: 2px; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main { + display: flex; + overflow: hidden; + text-overflow: ellipsis; + white-space: pre; +} + +.monaco-editor .suggest-widget:not(.frozen) .monaco-highlighted-label .highlight { + font-weight: bold; +} + +/** Icon styles **/ + +.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore { + opacity: 0.6; + background-position: center center; + background-repeat: no-repeat; + background-size: 70%; + cursor: pointer; +} + +.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close { + background-image: url('./close.svg'); + float: right; + margin-right: 5px; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore { + background-image: url('./info.svg'); +} + +.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close:hover, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore:hover { + opacity: 1; +} + +/** Type Info and icon next to the label in the focused completion item **/ + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label { + margin-left: 0.8em; + flex: 1; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + opacity: 0.7; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label > .monaco-tokenized-source { + display: inline; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label, +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .type-label, +.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { + display: none; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .type-label { + display: inline; +} + +/** Styles for each row in the list **/ + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon { + display: block; + height: 16px; + width: 16px; + background-repeat: no-repeat; + background-size: 80%; + background-position: center; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon { background-image: url('Misc_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.method, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.function, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.constructor { background-image: url('Method_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.field { background-image: url('Field_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.event { background-image: url('Event_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.operator { background-image: url('Operator_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.variable { background-image: url('LocalVariable_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.class { background-image: url('Class_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.interface { background-image: url('Interface_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.struct { background-image: url('Structure_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.type-parameter { background-image: url('Template_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.module { background-image: url('Namespace_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.property { background-image: url('Property_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.unit { background-image: url('Ruler_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.constant { background-image: url('Constant_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.value, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.enum { background-image: url('Enumerator_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.enum-member { background-image: url('EnumItem_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.keyword { background-image: url('IntelliSenseKeyword_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.text { background-image: url('String_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.color { background-image: url('ColorPalette_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.file { background-image: url('Document_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.reference { background-image: url('ImportFile_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.snippet { background-image: url('Snippet_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.customcolor { background-image: none; } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.folder { background-image: url('Folder_16x.svg'); } + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.customcolor .colorspan { + margin: 0 0 0 0.3em; + border: 0.1em solid #000; + width: 0.7em; + height: 0.7em; + display: inline-block; +} + +/** Styles for the docs of the completion item in focus **/ +.monaco-editor .suggest-widget .details { + display: flex; + flex-direction: column; + cursor: default; +} + +.monaco-editor .suggest-widget .details.no-docs { + display: none; +} + +.monaco-editor .suggest-widget.docs-below .details { + border-top-width: 0px; +} + +.monaco-editor .suggest-widget .details > .monaco-scrollable-element { + flex: 1; +} + +.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body { + position: absolute; + box-sizing: border-box; + height: 100%; + width: 100%; + white-space: pre-wrap; +} + +.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .type { + flex: 2; + overflow: hidden; + text-overflow: ellipsis; + opacity: 0.7; + word-break: break-all; + margin: 0; + padding: 4px 0 4px 5px; +} + +.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs { + margin: 0; + padding: 4px 5px; +} + +.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > p:empty { + display: none; +} + + +/* High Contrast and Dark Theming */ + +.monaco-editor.vs-dark .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close, +.monaco-editor.hc-black .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close { + background-image: url('./close-dark.svg'); +} + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon { background-image: url('Misc_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.method, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.method, +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.function, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.function, +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.constructor, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.constructor { background-image: url('Method_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.field, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.field { background-image: url('Field_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.event, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.event { background-image: url('Event_16x_vscode_inverse.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.operator, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.operator { background-image: url('Operator_16x_vscode_inverse.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.variable, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.variable { background-image: url('LocalVariable_16x_vscode_inverse.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.class, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.class { background-image: url('Class_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.interface, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.interface { background-image: url('Interface_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.struct, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.struct { background-image: url('Structure_16x_vscode_inverse.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.type-parameter, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.type-parameter { background-image: url('Template_16x_vscode_inverse.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.module, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.module { background-image: url('Namespace_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.property, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.property { background-image: url('Property_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.unit, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.unit { background-image: url('Ruler_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.constant, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.constant { background-image: url('Constant_16x_inverse.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.value, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.value, +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.enum, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.enum { background-image: url('Enumerator_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.enum-member, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.enum-member { background-image: url('EnumItem_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.keyword, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.keyword { background-image: url('IntelliSenseKeyword_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.text, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.text { background-image: url('String_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.color, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.color { background-image: url('ColorPalette_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.file, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.file { background-image: url('Document_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.reference, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.reference { background-image: url('ImportFile_16x_vscode_inverse.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.snippet, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.snippet { background-image: url('Snippet_inverse_16x.svg'); } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.customcolor, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.customcolor { background-image: none; } + +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.folder, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.folder { background-image: url('Folder_inverse_16x.svg'); } diff --git a/src/vs/editor/contrib/suggest/browser/suggest.ts b/src/vs/editor/contrib/suggest/browser/suggest.ts new file mode 100644 index 0000000000..c8214b3d3b --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/suggest.ts @@ -0,0 +1,245 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { sequence, asWinJsPromise } from 'vs/base/common/async'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { compareIgnoreCase } from 'vs/base/common/strings'; +import { assign } from 'vs/base/common/objects'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModel, IEditorContribution, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { ISuggestResult, ISuggestSupport, ISuggestion, SuggestRegistry } from 'vs/editor/common/modes'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const Context = { + Visible: new RawContextKey('suggestWidgetVisible', false), + MultipleSuggestions: new RawContextKey('suggestWidgetMultipleSuggestions', false), + MakesTextEdit: new RawContextKey('suggestionMakesTextEdit', true), + AcceptOnKey: new RawContextKey('suggestionSupportsAcceptOnKey', true), + AcceptSuggestionsOnEnter: new RawContextKey('acceptSuggestionOnEnter', true) +}; + +export interface ISuggestionItem { + position: IPosition; + suggestion: ISuggestion; + container: ISuggestResult; + support: ISuggestSupport; + resolve(): TPromise; +} + +export type SnippetConfig = 'top' | 'bottom' | 'inline' | 'none'; + +let _snippetSuggestSupport: ISuggestSupport; + +export function setSnippetSuggestSupport(support: ISuggestSupport): ISuggestSupport { + const old = _snippetSuggestSupport; + _snippetSuggestSupport = support; + return old; +} + +export function provideSuggestionItems(model: IModel, position: Position, snippetConfig: SnippetConfig = 'bottom', onlyFrom?: ISuggestSupport[]): TPromise { + + const allSuggestions: ISuggestionItem[] = []; + const acceptSuggestion = createSuggesionFilter(snippetConfig); + + position = position.clone(); + + // get provider groups, always add snippet suggestion provider + const supports = SuggestRegistry.orderedGroups(model); + + // add snippets provider unless turned off + if (snippetConfig !== 'none' && _snippetSuggestSupport) { + supports.unshift([_snippetSuggestSupport]); + } + + // add suggestions from contributed providers - providers are ordered in groups of + // equal score and once a group produces a result the process stops + let hasResult = false; + const factory = supports.map(supports => { + return () => { + // stop when we have a result + if (hasResult) { + return undefined; + } + // for each support in the group ask for suggestions + return TPromise.join(supports.map(support => { + + if (!isFalsyOrEmpty(onlyFrom) && onlyFrom.indexOf(support) < 0) { + return undefined; + } + + return asWinJsPromise(token => support.provideCompletionItems(model, position, token)).then(container => { + + const len = allSuggestions.length; + + if (container && !isFalsyOrEmpty(container.suggestions)) { + for (let suggestion of container.suggestions) { + if (acceptSuggestion(suggestion)) { + + fixOverwriteBeforeAfter(suggestion, container); + + allSuggestions.push({ + position, + container, + suggestion, + support, + resolve: createSuggestionResolver(support, suggestion, model, position) + }); + } + } + } + + if (len !== allSuggestions.length && support !== _snippetSuggestSupport) { + hasResult = true; + } + + }, onUnexpectedExternalError); + })); + }; + }); + + const result = sequence(factory).then(() => allSuggestions.sort(getSuggestionComparator(snippetConfig))); + + // result.then(items => { + // console.log(model.getWordUntilPosition(position), items.map(item => `${item.suggestion.label}, type=${item.suggestion.type}, incomplete?${item.container.incomplete}, overwriteBefore=${item.suggestion.overwriteBefore}`)); + // return items; + // }, err => { + // console.warn(model.getWordUntilPosition(position), err); + // }); + + return result; +} + +function fixOverwriteBeforeAfter(suggestion: ISuggestion, container: ISuggestResult): void { + if (typeof suggestion.overwriteBefore !== 'number') { + suggestion.overwriteBefore = 0; + } + if (typeof suggestion.overwriteAfter !== 'number' || suggestion.overwriteAfter < 0) { + suggestion.overwriteAfter = 0; + } +} + +function createSuggestionResolver(provider: ISuggestSupport, suggestion: ISuggestion, model: IModel, position: Position): () => TPromise { + return () => { + if (typeof provider.resolveCompletionItem === 'function') { + return asWinJsPromise(token => provider.resolveCompletionItem(model, position, suggestion, token)) + .then(value => { assign(suggestion, value); }); + } + return TPromise.as(void 0); + }; +} + +function createSuggesionFilter(snippetConfig: SnippetConfig): (candidate: ISuggestion) => boolean { + if (snippetConfig === 'none') { + return suggestion => suggestion.type !== 'snippet'; + } else { + return () => true; + } +} +function defaultComparator(a: ISuggestionItem, b: ISuggestionItem): number { + + let ret = 0; + + // check with 'sortText' + if (typeof a.suggestion.sortText === 'string' && typeof b.suggestion.sortText === 'string') { + ret = compareIgnoreCase(a.suggestion.sortText, b.suggestion.sortText); + } + + // check with 'label' + if (ret === 0) { + ret = compareIgnoreCase(a.suggestion.label, b.suggestion.label); + } + + // check with 'type' and lower snippets + if (ret === 0 && a.suggestion.type !== b.suggestion.type) { + if (a.suggestion.type === 'snippet') { + ret = 1; + } else if (b.suggestion.type === 'snippet') { + ret = -1; + } + } + + return ret; +} + +function snippetUpComparator(a: ISuggestionItem, b: ISuggestionItem): number { + if (a.suggestion.type !== b.suggestion.type) { + if (a.suggestion.type === 'snippet') { + return -1; + } else if (b.suggestion.type === 'snippet') { + return 1; + } + } + return defaultComparator(a, b); +} + +function snippetDownComparator(a: ISuggestionItem, b: ISuggestionItem): number { + if (a.suggestion.type !== b.suggestion.type) { + if (a.suggestion.type === 'snippet') { + return 1; + } else if (b.suggestion.type === 'snippet') { + return -1; + } + } + return defaultComparator(a, b); +} + +export function getSuggestionComparator(snippetConfig: SnippetConfig): (a: ISuggestionItem, b: ISuggestionItem) => number { + if (snippetConfig === 'top') { + return snippetUpComparator; + } else if (snippetConfig === 'bottom') { + return snippetDownComparator; + } else { + return defaultComparator; + } +} + +CommonEditorRegistry.registerDefaultLanguageCommand('_executeCompletionItemProvider', (model, position, args) => { + + const result: ISuggestResult = { + incomplete: false, + suggestions: [] + }; + + return provideSuggestionItems(model, position).then(items => { + + for (const { container, suggestion } of items) { + result.incomplete = result.incomplete || container.incomplete; + result.suggestions.push(suggestion); + } + + return result; + }); +}); + +interface SuggestController extends IEditorContribution { + triggerSuggest(onlyFrom?: ISuggestSupport[]): void; +} + +let _suggestions: ISuggestion[]; +let _provider = new class implements ISuggestSupport { + provideCompletionItems(): ISuggestResult { + return _suggestions && { suggestions: _suggestions }; + } +}; + +SuggestRegistry.register('*', _provider); + +/** + * + * @param editor + * @param suggestions + */ +export function showSimpleSuggestions(editor: ICommonCodeEditor, suggestions: ISuggestion[]) { + setTimeout(() => { + _suggestions = suggestions; + editor.getContribution('editor.contrib.suggestController').triggerSuggest([_provider]); + _suggestions = undefined; + }, 0); +} + diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts new file mode 100644 index 0000000000..55f948bcfb --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -0,0 +1,434 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ICommonCodeEditor, IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Range } from 'vs/editor/common/core/range'; +import { ISuggestSupport } from 'vs/editor/common/modes'; +import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; +import { Context as SuggestContext } from './suggest'; +import { SuggestModel, State } from './suggestModel'; +import { ICompletionItem } from './completionModel'; +import { SuggestWidget } from './suggestWidget'; + +class AcceptOnCharacterOracle { + + private _disposables: IDisposable[] = []; + + private _activeAcceptCharacters = new Set(); + private _activeItem: ICompletionItem; + + constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (item: ICompletionItem) => any) { + + this._disposables.push(widget.onDidShow(() => this._onItem(widget.getFocusedItem()))); + this._disposables.push(widget.onDidFocus(this._onItem, this)); + this._disposables.push(widget.onDidHide(this.reset, this)); + + this._disposables.push(editor.onWillType(text => { + if (this._activeItem) { + const ch = text[text.length - 1]; + if (this._activeAcceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) { + accept(this._activeItem); + } + } + })); + } + + private _onItem(item: ICompletionItem): void { + if (!item || isFalsyOrEmpty(item.suggestion.commitCharacters)) { + this.reset(); + return; + } + this._activeItem = item; + this._activeAcceptCharacters.clear(); + for (const ch of item.suggestion.commitCharacters) { + if (ch.length > 0) { + this._activeAcceptCharacters.add(ch[0]); + } + } + } + + reset(): void { + this._activeItem = undefined; + } + + dispose() { + dispose(this._disposables); + } +} + +@editorContribution +export class SuggestController implements IEditorContribution { + + private static ID: string = 'editor.contrib.suggestController'; + + public static get(editor: ICommonCodeEditor): SuggestController { + return editor.getContribution(SuggestController.ID); + } + + private _model: SuggestModel; + private _widget: SuggestWidget; + private _toDispose: IDisposable[] = []; + + constructor( + private _editor: ICodeEditor, + @ICommandService private _commandService: ICommandService, + @ITelemetryService private _telemetryService: ITelemetryService, + @IContextKeyService _contextKeyService: IContextKeyService, + @IInstantiationService _instantiationService: IInstantiationService + ) { + this._model = new SuggestModel(this._editor); + this._toDispose.push(this._model.onDidTrigger(e => this._widget.showTriggered(e.auto))); + this._toDispose.push(this._model.onDidSuggest(e => this._widget.showSuggestions(e.completionModel, e.isFrozen, e.auto))); + this._toDispose.push(this._model.onDidCancel(e => !e.retrigger && this._widget.hideWidget())); + + // Manage the acceptSuggestionsOnEnter context key + let acceptSuggestionsOnEnter = SuggestContext.AcceptSuggestionsOnEnter.bindTo(_contextKeyService); + let updateFromConfig = () => { + const { acceptSuggestionOnEnter } = this._editor.getConfiguration().contribInfo; + acceptSuggestionsOnEnter.set( + acceptSuggestionOnEnter === 'on' || acceptSuggestionOnEnter === 'smart' + || (acceptSuggestionOnEnter) === true + ); + }; + this._toDispose.push(this._editor.onDidChangeConfiguration((e) => updateFromConfig())); + updateFromConfig(); + + this._widget = _instantiationService.createInstance(SuggestWidget, this._editor); + this._toDispose.push(this._widget.onDidSelect(this._onDidSelectItem, this)); + + // Wire up logic to accept a suggestion on certain characters + const autoAcceptOracle = new AcceptOnCharacterOracle(_editor, this._widget, item => this._onDidSelectItem(item)); + this._toDispose.push( + autoAcceptOracle, + this._model.onDidSuggest(e => { + if (e.completionModel.items.length === 0) { + autoAcceptOracle.reset(); + } + }) + ); + + let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(_contextKeyService); + this._toDispose.push(this._widget.onDidFocus(item => { + + const position = this._editor.getPosition(); + const startColumn = item.position.column - item.suggestion.overwriteBefore; + const endColumn = position.column; + let value = true; + if ( + this._editor.getConfiguration().contribInfo.acceptSuggestionOnEnter === 'smart' + && this._model.state === State.Auto + && !item.suggestion.command + && !item.suggestion.additionalTextEdits + && item.suggestion.snippetType !== 'textmate' + && endColumn - startColumn === item.suggestion.insertText.length + ) { + const oldText = this._editor.getModel().getValueInRange({ + startLineNumber: position.lineNumber, + startColumn, + endLineNumber: position.lineNumber, + endColumn + }); + value = oldText !== item.suggestion.insertText; + } + makesTextEdit.set(value); + })); + this._toDispose.push({ + dispose() { makesTextEdit.reset(); } + }); + } + + getId(): string { + return SuggestController.ID; + } + + dispose(): void { + this._toDispose = dispose(this._toDispose); + if (this._widget) { + this._widget.dispose(); + this._widget = null; + } + if (this._model) { + this._model.dispose(); + this._model = null; + } + } + + private _onDidSelectItem(item: ICompletionItem): void { + if (item) { + const { suggestion, position } = item; + const columnDelta = this._editor.getPosition().column - position.column; + + if (Array.isArray(suggestion.additionalTextEdits)) { + this._editor.pushUndoStop(); + this._editor.executeEdits('suggestController.additionalTextEdits', suggestion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text))); + this._editor.pushUndoStop(); + } + + let { insertText } = suggestion; + if (suggestion.snippetType !== 'textmate') { + insertText = SnippetParser.escape(insertText); + } + + SnippetController2.get(this._editor).insert( + insertText, + suggestion.overwriteBefore + columnDelta, + suggestion.overwriteAfter + ); + + + if (suggestion.command) { + this._commandService.executeCommand(suggestion.command.id, ...suggestion.command.arguments).done(undefined, onUnexpectedError); + } + + this._alertCompletionItem(item); + this._telemetryService.publicLog('suggestSnippetInsert', { ...this._editor.getTelemetryData(), suggestionType: suggestion.type }); + } + + this._model.cancel(); + } + + private _alertCompletionItem({ suggestion }: ICompletionItem): void { + let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' did insert the following text: {1}", suggestion.label, suggestion.insertText); + alert(msg); + } + + triggerSuggest(onlyFrom?: ISuggestSupport[]): void { + this._model.trigger(false, false, onlyFrom); + this._editor.revealLine(this._editor.getPosition().lineNumber, ScrollType.Smooth); + this._editor.focus(); + } + + acceptSelectedSuggestion(): void { + if (this._widget) { + const item = this._widget.getFocusedItem(); + this._onDidSelectItem(item); + } + } + + cancelSuggestWidget(): void { + if (this._widget) { + this._model.cancel(); + this._widget.hideWidget(); + } + } + + selectNextSuggestion(): void { + if (this._widget) { + this._widget.selectNext(); + } + } + + selectNextPageSuggestion(): void { + if (this._widget) { + this._widget.selectNextPage(); + } + } + + selectLastSuggestion(): void { + if (this._widget) { + this._widget.selectLast(); + } + } + + selectPrevSuggestion(): void { + if (this._widget) { + this._widget.selectPrevious(); + } + } + + selectPrevPageSuggestion(): void { + if (this._widget) { + this._widget.selectPreviousPage(); + } + } + + selectFirstSuggestion(): void { + if (this._widget) { + this._widget.selectFirst(); + } + } + + toggleSuggestionDetails(): void { + if (this._widget) { + this._widget.toggleDetails(); + } + } + + toggleSuggestionFocus(): void { + if (this._widget) { + this._widget.toggleDetailsFocus(); + } + } +} + +@editorAction +export class TriggerSuggestAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.triggerSuggest', + label: nls.localize('suggest.trigger.label', "Trigger Suggest"), + alias: 'Trigger Suggest', + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCompletionItemProvider), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.Space, + mac: { primary: KeyMod.WinCtrl | KeyCode.Space } + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const controller = SuggestController.get(editor); + + if (!controller) { + return; + } + + controller.triggerSuggest(); + } +} + +const weight = CommonEditorRegistry.commandWeight(90); + +const SuggestCommand = EditorCommand.bindToContribution(SuggestController.get); + + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'acceptSelectedSuggestion', + precondition: SuggestContext.Visible, + handler: x => x.acceptSelectedSuggestion(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Tab + } +})); + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'acceptSelectedSuggestionOnEnter', + precondition: SuggestContext.Visible, + handler: x => x.acceptSelectedSuggestion(), + kbOpts: { + weight: weight, + kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit), + primary: KeyCode.Enter + } +})); + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'hideSuggestWidget', + precondition: SuggestContext.Visible, + handler: x => x.cancelSuggestWidget(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } +})); + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'selectNextSuggestion', + precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions), + handler: c => c.selectNextSuggestion(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.DownArrow, + secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow], + mac: { primary: KeyCode.DownArrow, secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow, KeyMod.WinCtrl | KeyCode.KEY_N] } + } +})); + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'selectNextPageSuggestion', + precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions), + handler: c => c.selectNextPageSuggestion(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.PageDown, + secondary: [KeyMod.CtrlCmd | KeyCode.PageDown] + } +})); + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'selectLastSuggestion', + precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions), + handler: c => c.selectLastSuggestion() +})); + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'selectPrevSuggestion', + precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions), + handler: c => c.selectPrevSuggestion(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.UpArrow, + secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow], + mac: { primary: KeyCode.UpArrow, secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow, KeyMod.WinCtrl | KeyCode.KEY_P] } + } +})); + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'selectPrevPageSuggestion', + precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions), + handler: c => c.selectPrevPageSuggestion(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.PageUp, + secondary: [KeyMod.CtrlCmd | KeyCode.PageUp] + } +})); + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'selectFirstSuggestion', + precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions), + handler: c => c.selectFirstSuggestion() +})); + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'toggleSuggestionDetails', + precondition: SuggestContext.Visible, + handler: x => x.toggleSuggestionDetails(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.Space, + mac: { primary: KeyMod.WinCtrl | KeyCode.Space } + } +})); + +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'toggleSuggestionFocus', + precondition: SuggestContext.Visible, + handler: x => x.toggleSuggestionFocus(), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Space, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Space } + } +})); diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts new file mode 100644 index 0000000000..777cf59826 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -0,0 +1,455 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { TimeoutTimer } from 'vs/base/common/async'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ICommonCodeEditor, IModel, IWordAtPosition } from 'vs/editor/common/editorCommon'; +import { ISuggestSupport, SuggestRegistry, StandardTokenType } from 'vs/editor/common/modes'; +import { Position } from 'vs/editor/common/core/position'; +import { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest'; +import { CompletionModel } from './completionModel'; +import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; + +export interface ICancelEvent { + retrigger: boolean; +} + +export interface ITriggerEvent { + auto: boolean; +} + +export interface ISuggestEvent { + completionModel: CompletionModel; + isFrozen: boolean; + auto: boolean; +} + +export class LineContext { + + static shouldAutoTrigger(editor: ICommonCodeEditor): boolean { + const model = editor.getModel(); + if (!model) { + return false; + } + const pos = editor.getPosition(); + model.tokenizeIfCheap(pos.lineNumber); + const word = model.getWordAtPosition(pos); + if (!word) { + return false; + } + if (word.endColumn !== pos.column) { + return false; + } + if (!isNaN(Number(word.word))) { + return false; + } + return true; + } + + static isInEditableRange(editor: ICommonCodeEditor): boolean { + const model = editor.getModel(); + const position = editor.getPosition(); + if (model.hasEditableRange()) { + const editableRange = model.getEditableRange(); + if (!editableRange.containsPosition(position)) { + return false; + } + } + return true; + } + + readonly lineNumber: number; + readonly column: number; + readonly leadingLineContent: string; + readonly leadingWord: IWordAtPosition; + readonly auto: boolean; + + constructor(model: IModel, position: Position, auto: boolean) { + this.leadingLineContent = model.getLineContent(position.lineNumber).substr(0, position.column - 1); + this.leadingWord = model.getWordUntilPosition(position); + this.lineNumber = position.lineNumber; + this.column = position.column; + this.auto = auto; + } +} + +export const enum State { + Idle = 0, + Manual = 1, + Auto = 2 +} + +export class SuggestModel implements IDisposable { + + private toDispose: IDisposable[] = []; + private quickSuggestDelay: number; + private triggerCharacterListener: IDisposable; + private triggerAutoSuggestPromise: TPromise; + private triggerRefilter = new TimeoutTimer(); + private _state: State; + + private requestPromise: TPromise; + private context: LineContext; + private currentPosition: Position; + + private completionModel: CompletionModel; + + private _onDidCancel: Emitter = new Emitter(); + get onDidCancel(): Event { return this._onDidCancel.event; } + + private _onDidTrigger: Emitter = new Emitter(); + get onDidTrigger(): Event { return this._onDidTrigger.event; } + + private _onDidSuggest: Emitter = new Emitter(); + get onDidSuggest(): Event { return this._onDidSuggest.event; } + + constructor(private editor: ICommonCodeEditor) { + this._state = State.Idle; + this.triggerAutoSuggestPromise = null; + this.requestPromise = null; + this.completionModel = null; + this.context = null; + this.currentPosition = editor.getPosition() || new Position(1, 1); + + // wire up various listeners + this.toDispose.push(this.editor.onDidChangeModel(() => { + this.updateTriggerCharacters(); + this.cancel(); + })); + this.toDispose.push(editor.onDidChangeModelLanguage(() => { + this.updateTriggerCharacters(); + this.cancel(); + })); + this.toDispose.push(this.editor.onDidChangeConfiguration(() => { + this.updateTriggerCharacters(); + this.updateQuickSuggest(); + })); + this.toDispose.push(SuggestRegistry.onDidChange(() => { + this.updateTriggerCharacters(); + this.updateActiveSuggestSession(); + })); + this.toDispose.push(this.editor.onDidChangeCursorSelection(e => { + this.onCursorChange(e); + })); + + this.updateTriggerCharacters(); + this.updateQuickSuggest(); + } + + dispose(): void { + dispose([this._onDidCancel, this._onDidSuggest, this._onDidTrigger, this.triggerCharacterListener, this.triggerRefilter]); + this.toDispose = dispose(this.toDispose); + this.cancel(); + } + + // --- handle configuration & precondition changes + + private updateQuickSuggest(): void { + this.quickSuggestDelay = this.editor.getConfiguration().contribInfo.quickSuggestionsDelay; + + if (isNaN(this.quickSuggestDelay) || (!this.quickSuggestDelay && this.quickSuggestDelay !== 0) || this.quickSuggestDelay < 0) { + this.quickSuggestDelay = 10; + } + } + + private updateTriggerCharacters(): void { + + dispose(this.triggerCharacterListener); + + if (this.editor.getConfiguration().readOnly + || !this.editor.getModel() + || !this.editor.getConfiguration().contribInfo.suggestOnTriggerCharacters) { + + return; + } + + const supportsByTriggerCharacter: { [ch: string]: ISuggestSupport[] } = Object.create(null); + for (const support of SuggestRegistry.all(this.editor.getModel())) { + if (isFalsyOrEmpty(support.triggerCharacters)) { + continue; + } + for (const ch of support.triggerCharacters) { + const array = supportsByTriggerCharacter[ch]; + if (!array) { + supportsByTriggerCharacter[ch] = [support]; + } else { + array.push(support); + } + } + } + + this.triggerCharacterListener = this.editor.onDidType(text => { + const lastChar = text.charAt(text.length - 1); + const supports = supportsByTriggerCharacter[lastChar]; + + if (supports) { + // keep existing items that where not computed by the + // supports/providers that want to trigger now + const items: ISuggestionItem[] = []; + if (this.completionModel) { + for (const item of this.completionModel.items) { + if (supports.indexOf(item.support) < 0) { + items.push(item); + } + } + } + this.trigger(true, Boolean(this.completionModel), supports, items); + } + }); + } + + // --- trigger/retrigger/cancel suggest + + get state(): State { + return this._state; + } + + cancel(retrigger: boolean = false): void { + + if (this.triggerAutoSuggestPromise) { + this.triggerAutoSuggestPromise.cancel(); + this.triggerAutoSuggestPromise = null; + } + + if (this.requestPromise) { + this.requestPromise.cancel(); + this.requestPromise = null; + } + + this._state = State.Idle; + this.completionModel = null; + this.context = null; + + this._onDidCancel.fire({ retrigger }); + } + + private updateActiveSuggestSession(): void { + if (this._state !== State.Idle) { + if (!SuggestRegistry.has(this.editor.getModel())) { + this.cancel(); + } else { + this.trigger(this._state === State.Auto, true); + } + } + } + + private onCursorChange(e: ICursorSelectionChangedEvent): void { + + const prevPosition = this.currentPosition; + this.currentPosition = this.editor.getPosition(); + + if (!e.selection.isEmpty() + || e.source !== 'keyboard' + || e.reason !== CursorChangeReason.NotSet) { + + if (this._state === State.Idle) { + // Early exit if nothing needs to be done! + // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) + return; + } + + this.cancel(); + return; + } + + if (!SuggestRegistry.has(this.editor.getModel())) { + return; + } + + const model = this.editor.getModel(); + if (!model) { + return; + } + + if (this._state === State.Idle) { + + // trigger 24x7 IntelliSense when idle, enabled, when cursor + // moved RIGHT, and when at a good position + if (this.editor.getConfiguration().contribInfo.quickSuggestions !== false + && prevPosition.isBefore(this.currentPosition) + ) { + + this.cancel(); + + if (LineContext.shouldAutoTrigger(this.editor)) { + this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay); + this.triggerAutoSuggestPromise.then(() => { + const model = this.editor.getModel(); + const pos = this.editor.getPosition(); + + if (!model) { + return; + } + // validate enabled now + const { quickSuggestions } = this.editor.getConfiguration().contribInfo; + if (quickSuggestions === false) { + return; + } else if (quickSuggestions === true) { + // all good + } else { + model.tokenizeIfCheap(pos.lineNumber); + const { tokenType } = model + .getLineTokens(pos.lineNumber) + .findTokenAtOffset(pos.column - 1); + + const inValidScope = quickSuggestions.other && tokenType === StandardTokenType.Other + || quickSuggestions.comments && tokenType === StandardTokenType.Comment + || quickSuggestions.strings && tokenType === StandardTokenType.String; + + if (!inValidScope) { + return; + } + } + + this.triggerAutoSuggestPromise = null; + this.trigger(true); + }); + } + } + + } else { + // refine active suggestion + this.triggerRefilter.cancelAndSet(() => { + const position = this.editor.getPosition(); + const ctx = new LineContext(model, position, this._state === State.Auto); + this.onNewContext(ctx); + }, 25); + } + } + + public trigger(auto: boolean, retrigger: boolean = false, onlyFrom?: ISuggestSupport[], existingItems?: ISuggestionItem[]): void { + + const model = this.editor.getModel(); + + if (!model) { + return; + } + + const ctx = new LineContext(model, this.editor.getPosition(), auto); + + if (!LineContext.isInEditableRange(this.editor)) { + return; + } + + // Cancel previous requests, change state & update UI + this.cancel(retrigger); + this._state = auto ? State.Auto : State.Manual; + this._onDidTrigger.fire({ auto }); + + // Capture context when request was sent + this.context = ctx; + + this.requestPromise = provideSuggestionItems(model, this.editor.getPosition(), + this.editor.getConfiguration().contribInfo.snippetSuggestions, + onlyFrom + ).then(items => { + + this.requestPromise = null; + if (this._state === State.Idle) { + return; + } + const model = this.editor.getModel(); + if (!model) { + return; + } + + if (!isFalsyOrEmpty(existingItems)) { + const cmpFn = getSuggestionComparator(this.editor.getConfiguration().contribInfo.snippetSuggestions); + items = items.concat(existingItems).sort(cmpFn); + } + + const ctx = new LineContext(model, this.editor.getPosition(), auto); + this.completionModel = new CompletionModel(items, this.context.column, { + leadingLineContent: ctx.leadingLineContent, + characterCountDelta: this.context ? ctx.column - this.context.column : 0 + }, this.editor.getConfiguration().contribInfo.snippetSuggestions); + this.onNewContext(ctx); + + }).then(null, onUnexpectedError); + } + + private onNewContext(ctx: LineContext): void { + + if (!this.context) { + // happens when 24x7 IntelliSense is enabled and still in its delay + return; + } + + if (ctx.lineNumber !== this.context.lineNumber) { + // e.g. happens when pressing Enter while IntelliSense is computed + this.cancel(); + return; + } + + if (ctx.column < this.context.column) { + // typed -> moved cursor LEFT -> retrigger if still on a word + if (ctx.leadingWord.word) { + this.trigger(this.context.auto, true); + } else { + this.cancel(); + } + return; + } + + if (!this.completionModel) { + // happens when IntelliSense is not yet computed + return; + } + + if (ctx.column > this.context.column && this.completionModel.incomplete && ctx.leadingWord.word.length !== 0) { + // typed -> moved cursor RIGHT & incomple model & still on a word -> retrigger + const { complete, incomplete } = this.completionModel.resolveIncompleteInfo(); + this.trigger(this._state === State.Auto, true, incomplete, complete); + + } else { + // typed -> moved cursor RIGHT -> update UI + let oldLineContext = this.completionModel.lineContext; + let isFrozen = false; + + this.completionModel.lineContext = { + leadingLineContent: ctx.leadingLineContent, + characterCountDelta: ctx.column - this.context.column + }; + + if (this.completionModel.items.length === 0) { + + if (LineContext.shouldAutoTrigger(this.editor) && this.context.leadingWord.endColumn < ctx.leadingWord.startColumn) { + // retrigger when heading into a new word + this.trigger(this.context.auto, true); + return; + } + + if (!this.context.auto) { + // freeze when IntelliSense was manually requested + this.completionModel.lineContext = oldLineContext; + isFrozen = this.completionModel.items.length > 0; + + if (isFrozen && ctx.leadingWord.word.length === 0) { + // there were results before but now there aren't + // and also we are not on a word anymore -> cancel + this.cancel(); + return; + } + + } else { + // nothing left + this.cancel(); + return; + } + } + + this._onDidSuggest.fire({ + completionModel: this.completionModel, + auto: this.context.auto, + isFrozen, + }); + } + } +} diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts new file mode 100644 index 0000000000..117b6bc303 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -0,0 +1,1071 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/suggest'; +import * as nls from 'vs/nls'; +import { createMatches } from 'vs/base/common/filters'; +import * as strings from 'vs/base/common/strings'; +import Event, { Emitter, chain } from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass } from 'vs/base/browser/dom'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { IDelegate, IListEvent, IRenderer } from 'vs/base/browser/ui/list/list'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { Context as SuggestContext } from './suggest'; +import { ICompletionItem, CompletionModel } from './completionModel'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; + +const sticky = false; // for development purposes +const expandSuggestionDocsByDefault = false; +const maxSuggestionsToShow = 12; + +interface ISuggestionTemplateData { + root: HTMLElement; + icon: HTMLElement; + colorspan: HTMLElement; + highlightedLabel: HighlightedLabel; + typeLabel: HTMLElement; + readMore: HTMLElement; + disposables: IDisposable[]; +} + +/** + * Suggest widget colors + */ +export const editorSuggestWidgetBackground = registerColor('editorSuggestWidget.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('editorSuggestWidgetBackground', 'Background color of the suggest widget.')); +export const editorSuggestWidgetBorder = registerColor('editorSuggestWidget.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, nls.localize('editorSuggestWidgetBorder', 'Border color of the suggest widget.')); +export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.foreground', { dark: editorForeground, light: editorForeground, hc: editorForeground }, nls.localize('editorSuggestWidgetForeground', 'Foreground color of the suggest widget.')); +export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.')); +export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.')); + + +const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i; +function matchesColor(text: string) { + return text && text.match(colorRegExp) ? text : null; +} + +function canExpandCompletionItem(item: ICompletionItem) { + if (!item) { + return false; + } + const suggestion = item.suggestion; + if (suggestion.documentation) { + return true; + } + return (suggestion.detail && suggestion.detail !== suggestion.label); +} + +class Renderer implements IRenderer { + + constructor( + private widget: SuggestWidget, + private editor: ICodeEditor, + private triggerKeybindingLabel: string + ) { + + } + + get templateId(): string { + return 'suggestion'; + } + + renderTemplate(container: HTMLElement): ISuggestionTemplateData { + const data = Object.create(null); + data.disposables = []; + data.root = container; + + data.icon = append(container, $('.icon')); + data.colorspan = append(data.icon, $('span.colorspan')); + + const text = append(container, $('.contents')); + const main = append(text, $('.main')); + data.highlightedLabel = new HighlightedLabel(main); + data.disposables.push(data.highlightedLabel); + data.typeLabel = append(main, $('span.type-label')); + + data.readMore = append(main, $('span.readMore')); + data.readMore.title = nls.localize('readMore', "Read More...{0}", this.triggerKeybindingLabel); + + const configureFont = () => { + const configuration = this.editor.getConfiguration(); + const fontFamily = configuration.fontInfo.fontFamily; + const fontSize = configuration.contribInfo.suggestFontSize || configuration.fontInfo.fontSize; + const lineHeight = configuration.contribInfo.suggestLineHeight || configuration.fontInfo.lineHeight; + const fontSizePx = `${fontSize}px`; + const lineHeightPx = `${lineHeight}px`; + + data.root.style.fontSize = fontSizePx; + main.style.fontFamily = fontFamily; + main.style.lineHeight = lineHeightPx; + data.icon.style.height = lineHeightPx; + data.icon.style.width = lineHeightPx; + data.readMore.style.height = lineHeightPx; + data.readMore.style.width = lineHeightPx; + }; + + configureFont(); + + chain(this.editor.onDidChangeConfiguration.bind(this.editor)) + .filter(e => e.fontInfo || e.contribInfo) + .on(configureFont, null, data.disposables); + + return data; + } + + renderElement(element: ICompletionItem, index: number, templateData: ISuggestionTemplateData): void { + const data = templateData; + const suggestion = (element).suggestion; + + if (canExpandCompletionItem(element)) { + data.root.setAttribute('aria-label', nls.localize('suggestionWithDetailsAriaLabel', "{0}, suggestion, has details", suggestion.label)); + } else { + data.root.setAttribute('aria-label', nls.localize('suggestionAriaLabel', "{0}, suggestion", suggestion.label)); + } + + data.icon.className = 'icon ' + suggestion.type; + data.colorspan.style.backgroundColor = ''; + + if (suggestion.type === 'color') { + let color = matchesColor(suggestion.label) || matchesColor(suggestion.documentation); + if (color) { + data.icon.className = 'icon customcolor'; + data.colorspan.style.backgroundColor = color; + } + } + + data.highlightedLabel.set(suggestion.label, createMatches(element.matches)); + data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); + + if (canExpandCompletionItem(element)) { + show(data.readMore); + data.readMore.onmousedown = e => { + e.stopPropagation(); + e.preventDefault(); + }; + data.readMore.onclick = e => { + e.stopPropagation(); + e.preventDefault(); + this.widget.toggleDetails(); + }; + } else { + hide(data.readMore); + data.readMore.onmousedown = null; + data.readMore.onclick = null; + } + + } + + disposeTemplate(templateData: ISuggestionTemplateData): void { + templateData.highlightedLabel.dispose(); + templateData.disposables = dispose(templateData.disposables); + } +} + +const enum State { + Hidden, + Loading, + Empty, + Open, + Frozen, + Details +} + +class SuggestionDetails { + + private el: HTMLElement; + private close: HTMLElement; + private scrollbar: DomScrollableElement; + private body: HTMLElement; + private header: HTMLElement; + private type: HTMLElement; + private docs: HTMLElement; + private ariaLabel: string; + private disposables: IDisposable[]; + private borderWidth: number = 1; + + constructor( + container: HTMLElement, + private widget: SuggestWidget, + private editor: ICodeEditor, + private triggerKeybindingLabel: string + ) { + this.disposables = []; + + this.el = append(container, $('.details')); + this.disposables.push(toDisposable(() => container.removeChild(this.el))); + + this.body = $('.body'); + + this.scrollbar = new DomScrollableElement(this.body, {}); + append(this.el, this.scrollbar.getDomNode()); + this.disposables.push(this.scrollbar); + + this.header = append(this.body, $('.header')); + this.close = append(this.header, $('span.close')); + this.close.title = nls.localize('readLess', "Read less...{0}", triggerKeybindingLabel); + this.type = append(this.header, $('p.type')); + + this.docs = append(this.body, $('p.docs')); + this.ariaLabel = null; + + this.configureFont(); + + chain(this.editor.onDidChangeConfiguration.bind(this.editor)) + .filter(e => e.fontInfo) + .on(this.configureFont, this, this.disposables); + } + + get element() { + return this.el; + } + + render(item: ICompletionItem): void { + if (!item || !canExpandCompletionItem(item)) { + this.type.textContent = ''; + this.docs.textContent = ''; + addClass(this.el, 'no-docs'); + this.ariaLabel = null; + return; + } + removeClass(this.el, 'no-docs'); + this.docs.textContent = item.suggestion.documentation; + + if (item.suggestion.detail) { + this.type.innerText = item.suggestion.detail; + show(this.type); + } else { + this.type.innerText = ''; + hide(this.type); + } + + this.el.style.height = this.header.offsetHeight + this.docs.offsetHeight + (this.borderWidth * 2) + 'px'; + + this.close.onmousedown = e => { + e.preventDefault(); + e.stopPropagation(); + }; + this.close.onclick = e => { + e.preventDefault(); + e.stopPropagation(); + this.widget.toggleDetails(); + }; + + this.body.scrollTop = 0; + this.scrollbar.scanDomNode(); + + this.ariaLabel = strings.format('{0}\n{1}\n{2}', item.suggestion.label || '', item.suggestion.detail || '', item.suggestion.documentation || ''); + } + + getAriaLabel(): string { + return this.ariaLabel; + } + + scrollDown(much = 8): void { + this.body.scrollTop += much; + } + + scrollUp(much = 8): void { + this.body.scrollTop -= much; + } + + scrollTop(): void { + this.body.scrollTop = 0; + } + + scrollBottom(): void { + this.body.scrollTop = this.body.scrollHeight; + } + + pageDown(): void { + this.scrollDown(80); + } + + pageUp(): void { + this.scrollUp(80); + } + + setBorderWidth(width: number): void { + this.borderWidth = width; + } + + private configureFont() { + const configuration = this.editor.getConfiguration(); + const fontFamily = configuration.fontInfo.fontFamily; + const fontSize = configuration.contribInfo.suggestFontSize || configuration.fontInfo.fontSize; + const lineHeight = configuration.contribInfo.suggestLineHeight || configuration.fontInfo.lineHeight; + const fontSizePx = `${fontSize}px`; + const lineHeightPx = `${lineHeight}px`; + + this.el.style.fontSize = fontSizePx; + this.type.style.fontFamily = fontFamily; + this.close.style.height = lineHeightPx; + this.close.style.width = lineHeightPx; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + +export class SuggestWidget implements IContentWidget, IDelegate, IDisposable { + + private static ID: string = 'editor.widget.suggestWidget'; + + static LOADING_MESSAGE: string = nls.localize('suggestWidget.loading', "Loading..."); + static NO_SUGGESTIONS_MESSAGE: string = nls.localize('suggestWidget.noSuggestions', "No suggestions."); + + // Editor.IContentWidget.allowEditorOverflow + readonly allowEditorOverflow = true; + + private state: State; + private isAuto: boolean; + private loadingTimeout: number; + private currentSuggestionDetails: TPromise; + private focusedItemIndex: number; + private focusedItem: ICompletionItem; + private ignoreFocusEvents = false; + private completionModel: CompletionModel; + + private element: HTMLElement; + private messageElement: HTMLElement; + private listElement: HTMLElement; + private details: SuggestionDetails; + private list: List; + + private suggestWidgetVisible: IContextKey; + private suggestWidgetMultipleSuggestions: IContextKey; + private suggestionSupportsAutoAccept: IContextKey; + + private editorBlurTimeout: TPromise; + private showTimeout: TPromise; + private toDispose: IDisposable[]; + + private onDidSelectEmitter = new Emitter(); + private onDidFocusEmitter = new Emitter(); + private onDidHideEmitter = new Emitter(); + private onDidShowEmitter = new Emitter(); + + + readonly onDidSelect: Event = this.onDidSelectEmitter.event; + readonly onDidFocus: Event = this.onDidFocusEmitter.event; + readonly onDidHide: Event = this.onDidHideEmitter.event; + readonly onDidShow: Event = this.onDidShowEmitter.event; + + private readonly maxWidgetWidth = 660; + private readonly listWidth = 330; + private storageService: IStorageService; + private detailsFocusBorderColor: string; + private detailsBorderColor: string; + + private storageServiceAvailable: boolean = true; + private expandSuggestionDocs: boolean = false; + + constructor( + private editor: ICodeEditor, + @ITelemetryService private telemetryService: ITelemetryService, + @IContextKeyService contextKeyService: IContextKeyService, + @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService, + @IKeybindingService keybindingService: IKeybindingService + ) { + const kb = keybindingService.lookupKeybinding('editor.action.triggerSuggest'); + const triggerKeybindingLabel = !kb ? '' : ` (${kb.getLabel()})`; + + this.isAuto = false; + this.focusedItem = null; + this.storageService = storageService; + + if (this.expandDocsSettingFromStorage() === undefined) { + this.storageService.store('expandSuggestionDocs', expandSuggestionDocsByDefault, StorageScope.GLOBAL); + if (this.expandDocsSettingFromStorage() === undefined) { + this.storageServiceAvailable = false; + } + } + + this.element = $('.editor-widget.suggest-widget'); + if (!this.editor.getConfiguration().contribInfo.iconsInSuggestions) { + addClass(this.element, 'no-icons'); + } + + this.messageElement = append(this.element, $('.message')); + this.listElement = append(this.element, $('.tree')); + this.details = new SuggestionDetails(this.element, this, this.editor, triggerKeybindingLabel); + + let renderer = new Renderer(this, this.editor, triggerKeybindingLabel); + + this.list = new List(this.listElement, this, [renderer], { + useShadows: false, + selectOnMouseDown: true + }); + + this.toDispose = [ + attachListStyler(this.list, themeService, { + listInactiveFocusBackground: editorSuggestWidgetSelectedBackground, + listInactiveFocusOutline: activeContrastBorder + }), + themeService.onThemeChange(t => this.onThemeChange(t)), + editor.onDidBlurEditorText(() => this.onEditorBlur()), + editor.onDidLayoutChange(() => this.onEditorLayoutChange()), + this.list.onSelectionChange(e => this.onListSelection(e)), + this.list.onFocusChange(e => this.onListFocus(e)), + this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged()) + ]; + + this.suggestWidgetVisible = SuggestContext.Visible.bindTo(contextKeyService); + this.suggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(contextKeyService); + this.suggestionSupportsAutoAccept = SuggestContext.AcceptOnKey.bindTo(contextKeyService); + + this.editor.addContentWidget(this); + this.setState(State.Hidden); + + this.onThemeChange(themeService.getTheme()); + + // TODO@Alex: this is useful, but spammy + // var isVisible = false; + // this.onDidVisibilityChange((newIsVisible) => { + // if (isVisible === newIsVisible) { + // return; + // } + // isVisible = newIsVisible; + // if (isVisible) { + // alert(nls.localize('suggestWidgetAriaVisible', "Suggestions opened")); + // } else { + // alert(nls.localize('suggestWidgetAriaInvisible', "Suggestions closed")); + // } + // }); + } + + private onCursorSelectionChanged(): void { + if (this.state === State.Hidden) { + return; + } + + this.editor.layoutContentWidget(this); + } + + private onEditorBlur(): void { + if (sticky) { + return; + } + + this.editorBlurTimeout = TPromise.timeout(150).then(() => { + if (!this.editor.isFocused()) { + this.setState(State.Hidden); + } + }); + } + + private onEditorLayoutChange(): void { + if ((this.state === State.Open || this.state === State.Details) && this.expandDocsSettingFromStorage()) { + this.expandSideOrBelow(); + } + } + + private onListSelection(e: IListEvent): void { + if (!e.elements.length) { + return; + } + + const item = e.elements[0]; + this.onDidSelectEmitter.fire(item); + + alert(nls.localize('suggestionAriaAccepted', "{0}, accepted", item.suggestion.label)); + + this.editor.focus(); + } + + private _getSuggestionAriaAlertLabel(item: ICompletionItem): string { + if (canExpandCompletionItem(item)) { + return nls.localize('ariaCurrentSuggestionWithDetails', "{0}, suggestion, has details", item.suggestion.label); + } else { + return nls.localize('ariaCurrentSuggestion', "{0}, suggestion", item.suggestion.label); + } + } + + private _lastAriaAlertLabel: string; + private _ariaAlert(newAriaAlertLabel: string): void { + if (this._lastAriaAlertLabel === newAriaAlertLabel) { + return; + } + this._lastAriaAlertLabel = newAriaAlertLabel; + if (this._lastAriaAlertLabel) { + alert(this._lastAriaAlertLabel); + } + } + + private onThemeChange(theme: ITheme) { + let backgroundColor = theme.getColor(editorSuggestWidgetBackground); + if (backgroundColor) { + this.listElement.style.backgroundColor = backgroundColor.toString(); + this.details.element.style.backgroundColor = backgroundColor.toString(); + this.messageElement.style.backgroundColor = backgroundColor.toString(); + } + let borderColor = theme.getColor(editorSuggestWidgetBorder); + if (borderColor) { + this.listElement.style.borderColor = borderColor.toString(); + this.details.element.style.borderColor = borderColor.toString(); + this.messageElement.style.borderColor = borderColor.toString(); + this.detailsBorderColor = borderColor.toString(); + } + let focusBorderColor = theme.getColor(focusBorder); + if (focusBorderColor) { + this.detailsFocusBorderColor = focusBorderColor.toString(); + } + this.details.setBorderWidth(theme.type === 'hc' ? 2 : 1); + } + + private onListFocus(e: IListEvent): void { + if (this.ignoreFocusEvents) { + return; + } + + if (!e.elements.length) { + if (this.currentSuggestionDetails) { + this.currentSuggestionDetails.cancel(); + this.currentSuggestionDetails = null; + this.focusedItem = null; + } + + this._ariaAlert(null); + // TODO@Alex: Chromium bug + // this.editor.setAriaActiveDescendant(null); + + return; + } + + const item = e.elements[0]; + this._ariaAlert(this._getSuggestionAriaAlertLabel(item)); + + // TODO@Alex: Chromium bug + // // TODO@Alex: the list is not done rendering... + // setTimeout(() => { + // this.editor.setAriaActiveDescendant(this.list.getElementId(e.indexes[0])); + // }, 100); + + if (item === this.focusedItem) { + return; + } + + if (this.currentSuggestionDetails) { + this.currentSuggestionDetails.cancel(); + this.currentSuggestionDetails = null; + } + + const index = e.indexes[0]; + + this.suggestionSupportsAutoAccept.set(!item.suggestion.noAutoAccept); + + const oldFocus = this.focusedItem; + const oldFocusIndex = this.focusedItemIndex; + this.focusedItemIndex = index; + this.focusedItem = item; + + if (oldFocus) { + this.ignoreFocusEvents = true; + this.list.splice(oldFocusIndex, 1, [oldFocus]); + this.ignoreFocusEvents = false; + } + + this.updateListHeight(); + this.list.reveal(index); + + this.currentSuggestionDetails = item.resolve() + .then(() => { + this.ignoreFocusEvents = true; + this.list.splice(index, 1, [item]); + this.ignoreFocusEvents = false; + + this.list.setFocus([index]); + this.list.reveal(index); + + if (this.expandDocsSettingFromStorage()) { + this.showDetails(); + } else { + removeClass(this.element, 'docs-side'); + } + }) + .then(null, err => !isPromiseCanceledError(err) && onUnexpectedError(err)) + .then(() => this.currentSuggestionDetails = null); + + // emit an event + this.onDidFocusEmitter.fire(item); + } + + private setState(state: State): void { + if (!this.element) { + return; + } + + const stateChanged = this.state !== state; + this.state = state; + + toggleClass(this.element, 'frozen', state === State.Frozen); + + switch (state) { + case State.Hidden: + hide(this.messageElement, this.details.element); + show(this.listElement); + this.hide(); + if (stateChanged) { + this.list.splice(0, this.list.length); + } + break; + case State.Loading: + this.messageElement.textContent = SuggestWidget.LOADING_MESSAGE; + hide(this.listElement, this.details.element); + show(this.messageElement); + removeClass(this.element, 'docs-side'); + this.show(); + break; + case State.Empty: + this.messageElement.textContent = SuggestWidget.NO_SUGGESTIONS_MESSAGE; + hide(this.listElement, this.details.element); + show(this.messageElement); + removeClass(this.element, 'docs-side'); + this.show(); + break; + case State.Open: + hide(this.messageElement); + show(this.listElement); + if (this.expandDocsSettingFromStorage() + && canExpandCompletionItem(this.list.getFocusedElements()[0])) { + show(this.details.element); + this.expandSideOrBelow(); + } else { + hide(this.details.element); + } + this.show(); + break; + case State.Frozen: + hide(this.messageElement, this.details.element); + show(this.listElement); + this.show(); + break; + case State.Details: + hide(this.messageElement); + show(this.details.element, this.listElement); + this.show(); + this._ariaAlert(this.details.getAriaLabel()); + break; + } + + if (stateChanged) { + this.editor.layoutContentWidget(this); + } + } + + showTriggered(auto: boolean) { + if (this.state !== State.Hidden) { + return; + } + + this.isAuto = !!auto; + + if (!this.isAuto) { + this.loadingTimeout = setTimeout(() => { + this.loadingTimeout = null; + this.setState(State.Loading); + }, 50); + } + } + + showSuggestions(completionModel: CompletionModel, isFrozen: boolean, isAuto: boolean): void { + if (this.loadingTimeout) { + clearTimeout(this.loadingTimeout); + this.loadingTimeout = null; + } + + this.completionModel = completionModel; + + if (isFrozen && this.state !== State.Empty && this.state !== State.Hidden) { + this.setState(State.Frozen); + return; + } + + let visibleCount = this.completionModel.items.length; + + const isEmpty = visibleCount === 0; + this.suggestWidgetMultipleSuggestions.set(visibleCount > 1); + + if (isEmpty) { + if (isAuto) { + this.setState(State.Hidden); + } else { + this.setState(State.Empty); + } + + this.completionModel = null; + + } else { + const { stats } = this.completionModel; + stats['wasAutomaticallyTriggered'] = !!isAuto; + this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() }); + + this.focusedItem = null; + this.focusedItemIndex = null; + this.list.splice(0, this.list.length, this.completionModel.items); + this.list.setFocus([0]); + this.list.reveal(0, 0); + + if (isFrozen) { + this.setState(State.Frozen); + } else { + this.setState(State.Open); + } + + // Reset focus border + if (this.detailsBorderColor) { + this.details.element.style.borderColor = this.detailsBorderColor; + } + } + } + + selectNextPage(): boolean { + switch (this.state) { + case State.Hidden: + return false; + case State.Details: + this.details.pageDown(); + return true; + case State.Loading: + return !this.isAuto; + default: + this.list.focusNextPage(); + return true; + } + } + + selectNext(): boolean { + switch (this.state) { + case State.Hidden: + return false; + case State.Loading: + return !this.isAuto; + default: + this.list.focusNext(1, true); + return true; + } + } + + selectLast(): boolean { + switch (this.state) { + case State.Hidden: + return false; + case State.Details: + this.details.scrollBottom(); + return true; + case State.Loading: + return !this.isAuto; + default: + this.list.focusLast(); + return true; + } + } + + selectPreviousPage(): boolean { + switch (this.state) { + case State.Hidden: + return false; + case State.Details: + this.details.pageUp(); + return true; + case State.Loading: + return !this.isAuto; + default: + this.list.focusPreviousPage(); + return true; + } + } + + selectPrevious(): boolean { + switch (this.state) { + case State.Hidden: + return false; + case State.Loading: + return !this.isAuto; + default: + this.list.focusPrevious(1, true); + return false; + } + } + + selectFirst(): boolean { + switch (this.state) { + case State.Hidden: + return false; + case State.Details: + this.details.scrollTop(); + return true; + case State.Loading: + return !this.isAuto; + default: + this.list.focusFirst(); + return true; + } + } + + getFocusedItem(): ICompletionItem { + if (this.state !== State.Hidden + && this.state !== State.Empty + && this.state !== State.Loading) { + + return this.list.getFocusedElements()[0]; + } + return undefined; + } + + toggleDetailsFocus(): void { + if (this.state === State.Details) { + this.setState(State.Open); + if (this.detailsBorderColor) { + this.details.element.style.borderColor = this.detailsBorderColor; + } + } else if (this.state === State.Open && this.expandDocsSettingFromStorage()) { + this.setState(State.Details); + if (this.detailsFocusBorderColor) { + this.details.element.style.borderColor = this.detailsFocusBorderColor; + } + } + this.telemetryService.publicLog('suggestWidget:toggleDetailsFocus', this.editor.getTelemetryData()); + } + + toggleDetails(): void { + if (!canExpandCompletionItem(this.list.getFocusedElements()[0])) { + return; + } + + if (this.expandDocsSettingFromStorage()) { + this.updateExpandDocsSetting(false); + hide(this.details.element); + removeClass(this.element, 'docs-side'); + removeClass(this.element, 'docs-below'); + this.editor.layoutContentWidget(this); + this.telemetryService.publicLog('suggestWidget:collapseDetails', this.editor.getTelemetryData()); + } else { + if (this.state !== State.Open && this.state !== State.Details) { + return; + } + + this.updateExpandDocsSetting(true); + this.showDetails(); + this.telemetryService.publicLog('suggestWidget:expandDetails', this.editor.getTelemetryData()); + } + + } + + showDetails(): void { + this.expandSideOrBelow(); + + show(this.details.element); + this.details.render(this.list.getFocusedElements()[0]); + this.details.element.style.maxHeight = this.maxWidgetHeight + 'px'; + + // Reset margin-top that was set as Fix for #26416 + this.listElement.style.marginTop = '0px'; + + // with docs showing up widget width/height may change, so reposition the widget + this.editor.layoutContentWidget(this); + + this.adjustDocsPosition(); + + this.editor.focus(); + + this._ariaAlert(this.details.getAriaLabel()); + } + + private show(): void { + this.updateListHeight(); + this.suggestWidgetVisible.set(true); + + this.showTimeout = TPromise.timeout(100).then(() => { + addClass(this.element, 'visible'); + this.onDidShowEmitter.fire(this); + }); + } + + private hide(): void { + this.suggestWidgetVisible.reset(); + this.suggestWidgetMultipleSuggestions.reset(); + removeClass(this.element, 'visible'); + } + + hideWidget(): void { + clearTimeout(this.loadingTimeout); + this.setState(State.Hidden); + this.onDidHideEmitter.fire(this); + } + + getPosition(): IContentWidgetPosition { + if (this.state === State.Hidden) { + return null; + } + + return { + position: this.editor.getPosition(), + preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] + }; + } + + getDomNode(): HTMLElement { + return this.element; + } + + getId(): string { + return SuggestWidget.ID; + } + + private updateListHeight(): number { + let height = 0; + + if (this.state === State.Empty || this.state === State.Loading) { + height = this.unfocusedHeight; + } else { + const suggestionCount = this.list.contentHeight / this.unfocusedHeight; + height = Math.min(suggestionCount, maxSuggestionsToShow) * this.unfocusedHeight; + } + + this.element.style.lineHeight = `${this.unfocusedHeight}px`; + this.listElement.style.height = `${height}px`; + this.list.layout(height); + + this.editor.layoutContentWidget(this); + + return height; + } + + private adjustDocsPosition() { + const lineHeight = this.editor.getConfiguration().fontInfo.lineHeight; + const cursorCoords = this.editor.getScrolledVisiblePosition(this.editor.getPosition()); + const editorCoords = getDomNodePagePosition(this.editor.getDomNode()); + const cursorX = editorCoords.left + cursorCoords.left; + const cursorY = editorCoords.top + cursorCoords.top + cursorCoords.height; + const widgetCoords = getDomNodePagePosition(this.element); + const widgetX = widgetCoords.left; + const widgetY = widgetCoords.top; + + if (widgetX < cursorX - this.listWidth) { + // Widget is too far to the left of cursor, swap list and docs + addClass(this.element, 'list-right'); + } else { + removeClass(this.element, 'list-right'); + } + + // Compare top of the cursor (cursorY - lineheight) with widgetTop to determine if + // margin-top needs to be applied on list to make it appear right above the cursor + // Cannot compare cursorY directly as it may be a few decimals off due to zoooming + if (hasClass(this.element, 'docs-side') + && cursorY - lineHeight > widgetY + && this.details.element.offsetHeight > this.listElement.offsetHeight) { + + // Fix for #26416 + // Docs is bigger than list and widget is above cursor, apply margin-top so that list appears right above cursor + this.listElement.style.marginTop = `${this.details.element.offsetHeight - this.listElement.offsetHeight}px`; + } + } + + private expandSideOrBelow() { + let matches = this.element.style.maxWidth.match(/(\d+)px/); + if (!matches || Number(matches[1]) < this.maxWidgetWidth) { + addClass(this.element, 'docs-below'); + removeClass(this.element, 'docs-side'); + } else { + addClass(this.element, 'docs-side'); + removeClass(this.element, 'docs-below'); + } + } + + // Heights + + private get maxWidgetHeight(): number { + return this.unfocusedHeight * maxSuggestionsToShow; + } + + private get unfocusedHeight(): number { + const configuration = this.editor.getConfiguration(); + return configuration.contribInfo.suggestLineHeight || configuration.fontInfo.lineHeight; + } + + // IDelegate + + getHeight(element: ICompletionItem): number { + return this.unfocusedHeight; + } + + getTemplateId(element: ICompletionItem): string { + return 'suggestion'; + } + + // Monaco Editor does not have a storage service + private expandDocsSettingFromStorage(): boolean { + if (this.storageServiceAvailable) { + return this.storageService.getBoolean('expandSuggestionDocs', StorageScope.GLOBAL); + } else { + return this.expandSuggestionDocs; + } + } + + // Monaco Editor does not have a storage service + private updateExpandDocsSetting(value: boolean) { + if (this.storageServiceAvailable) { + this.storageService.store('expandSuggestionDocs', value, StorageScope.GLOBAL); + } else { + this.expandSuggestionDocs = value; + } + } + + dispose(): void { + this.state = null; + this.suggestionSupportsAutoAccept = null; + this.currentSuggestionDetails = null; + this.focusedItem = null; + this.element = null; + this.messageElement = null; + this.listElement = null; + this.details.dispose(); + this.details = null; + this.list.dispose(); + this.list = null; + this.toDispose = dispose(this.toDispose); + if (this.loadingTimeout) { + clearTimeout(this.loadingTimeout); + this.loadingTimeout = null; + } + + if (this.editorBlurTimeout) { + this.editorBlurTimeout.cancel(); + this.editorBlurTimeout = null; + } + + if (this.showTimeout) { + this.showTimeout.cancel(); + this.showTimeout = null; + } + } +} + +registerThemingParticipant((theme, collector) => { + let matchHighlight = theme.getColor(editorSuggestWidgetHighlightForeground); + if (matchHighlight) { + collector.addRule(`.monaco-editor .suggest-widget:not(.frozen) .monaco-highlighted-label .highlight { color: ${matchHighlight}; }`); + } + let foreground = theme.getColor(editorSuggestWidgetForeground); + if (foreground) { + collector.addRule(`.monaco-editor .suggest-widget { color: ${foreground}; }`); + } +}); diff --git a/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts b/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts new file mode 100644 index 0000000000..10d0140fb4 --- /dev/null +++ b/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts @@ -0,0 +1,231 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { ISuggestion, ISuggestResult, ISuggestSupport, SuggestionType } from 'vs/editor/common/modes'; +import { ISuggestionItem, getSuggestionComparator } from 'vs/editor/contrib/suggest/browser/suggest'; +import { CompletionModel } from 'vs/editor/contrib/suggest/browser/completionModel'; +import { IPosition } from 'vs/editor/common/core/position'; +import { TPromise } from 'vs/base/common/winjs.base'; + +suite('CompletionModel', function () { + + function createSuggestItem(label: string, overwriteBefore: number, type: SuggestionType = 'property', incomplete: boolean = false, position: IPosition = { lineNumber: 1, column: 1 }): ISuggestionItem { + + return new class implements ISuggestionItem { + + position = position; + + suggestion: ISuggestion = { + label, + overwriteBefore, + insertText: label, + type + }; + + container: ISuggestResult = { + incomplete, + suggestions: [this.suggestion] + }; + + support: ISuggestSupport = { + provideCompletionItems(): any { + return; + } + }; + + resolve(): TPromise { + return null; + } + }; + } + + let model: CompletionModel; + + setup(function () { + + model = new CompletionModel([ + createSuggestItem('foo', 3), + createSuggestItem('Foo', 3), + createSuggestItem('foo', 2), + ], 1, { + leadingLineContent: 'foo', + characterCountDelta: 0 + }); + }); + + test('filtering - cached', function () { + + const itemsNow = model.items; + let itemsThen = model.items; + assert.ok(itemsNow === itemsThen); + + // still the same context + model.lineContext = { leadingLineContent: 'foo', characterCountDelta: 0 }; + itemsThen = model.items; + assert.ok(itemsNow === itemsThen); + + // different context, refilter + model.lineContext = { leadingLineContent: 'foo1', characterCountDelta: 1 }; + itemsThen = model.items; + assert.ok(itemsNow !== itemsThen); + }); + + + test('complete/incomplete', function () { + + assert.equal(model.incomplete, false); + + let incompleteModel = new CompletionModel([ + createSuggestItem('foo', 3, undefined, true), + createSuggestItem('foo', 2), + ], 1, { + leadingLineContent: 'foo', + characterCountDelta: 0 + }); + assert.equal(incompleteModel.incomplete, true); + }); + + test('replaceIncomplete', function () { + + const completeItem = createSuggestItem('foobar', 1, undefined, false, { lineNumber: 1, column: 2 }); + const incompleteItem = createSuggestItem('foofoo', 1, undefined, true, { lineNumber: 1, column: 2 }); + + const model = new CompletionModel([completeItem, incompleteItem], 2, { leadingLineContent: 'f', characterCountDelta: 0 }); + assert.equal(model.incomplete, true); + assert.equal(model.items.length, 2); + + const { complete, incomplete } = model.resolveIncompleteInfo(); + + assert.equal(incomplete.length, 1); + assert.ok(incomplete[0] === incompleteItem.support); + assert.equal(complete.length, 1); + assert.ok(complete[0] === completeItem); + }); + + test('proper current word when length=0, #16380', function () { + + model = new CompletionModel([ + createSuggestItem(' b.score); // snippet really demoted + }); + + test('filterText seems ignored in autocompletion, #26874', function () { + + const item1 = createSuggestItem('Map - java.util', 1, 'property'); + item1.suggestion.filterText = 'Map'; + const item2 = createSuggestItem('Map - java.util', 1, 'property'); + + model = new CompletionModel([item1, item2], 1, { + leadingLineContent: 'M', + characterCountDelta: 0 + }); + + assert.equal(model.items.length, 2); + + model.lineContext = { + leadingLineContent: 'Map ', + characterCountDelta: 3 + }; + assert.equal(model.items.length, 1); + }); + + test('Vscode 1.12 no longer obeys \'sortText\' in completion items (from language server), #26096', function () { + + const item1 = createSuggestItem('<- groups', 2, 'property', false, { lineNumber: 1, column: 3 }); + item1.suggestion.filterText = ' groups'; + item1.suggestion.sortText = '00002'; + + const item2 = createSuggestItem('source', 0, 'property', false, { lineNumber: 1, column: 3 }); + item2.suggestion.filterText = 'source'; + item2.suggestion.sortText = '00001'; + + const items = [item1, item2].sort(getSuggestionComparator('inline')); + + model = new CompletionModel(items, 3, { + leadingLineContent: ' ', + characterCountDelta: 0 + }); + + assert.equal(model.items.length, 2); + + const [first, second] = model.items; + assert.equal(first.suggestion.label, 'source'); + assert.equal(second.suggestion.label, '<- groups'); + + }); + +}); diff --git a/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts new file mode 100644 index 0000000000..c97ba1e93f --- /dev/null +++ b/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { SuggestRegistry } from 'vs/editor/common/modes'; +import { provideSuggestionItems } from 'vs/editor/contrib/suggest/browser/suggest'; +import { Position } from 'vs/editor/common/core/position'; +import { Model } from 'vs/editor/common/model/model'; + + +suite('Suggest', function () { + + let model: Model; + let registration: IDisposable; + + setup(function () { + + model = Model.createFromString('FOO\nbar\BAR\nfoo', undefined, undefined, URI.parse('foo:bar/path')); + registration = SuggestRegistry.register({ pattern: 'bar/path', scheme: 'foo' }, { + provideCompletionItems() { + return { + incomplete: false, + suggestions: [{ + label: 'aaa', + type: 'snippet', + insertText: 'aaa' + }, { + label: 'zzz', + type: 'snippet', + insertText: 'zzz' + }, { + label: 'fff', + type: 'property', + insertText: 'fff' + }] + }; + } + }); + }); + + teardown(() => { + registration.dispose(); + model.dispose(); + }); + + test('sort - snippet inline', function () { + return provideSuggestionItems(model, new Position(1, 1), 'inline').then(items => { + assert.equal(items.length, 3); + assert.equal(items[0].suggestion.label, 'aaa'); + assert.equal(items[1].suggestion.label, 'fff'); + assert.equal(items[2].suggestion.label, 'zzz'); + }); + }); + + test('sort - snippet top', function () { + return provideSuggestionItems(model, new Position(1, 1), 'top').then(items => { + assert.equal(items.length, 3); + assert.equal(items[0].suggestion.label, 'aaa'); + assert.equal(items[1].suggestion.label, 'zzz'); + assert.equal(items[2].suggestion.label, 'fff'); + }); + }); + + test('sort - snippet bottom', function () { + return provideSuggestionItems(model, new Position(1, 1), 'bottom').then(items => { + assert.equal(items.length, 3); + assert.equal(items[0].suggestion.label, 'fff'); + assert.equal(items[1].suggestion.label, 'aaa'); + assert.equal(items[2].suggestion.label, 'zzz'); + }); + }); + + test('sort - snippet none', function () { + return provideSuggestionItems(model, new Position(1, 1), 'none').then(items => { + assert.equal(items.length, 1); + assert.equal(items[0].suggestion.label, 'fff'); + }); + }); + + test('only from', function () { + + const foo: any = { + triggerCharacters: [], + provideCompletionItems() { + return { + currentWord: '', + incomplete: false, + suggestions: [{ + label: 'jjj', + type: 'property', + insertText: 'jjj' + }] + }; + } + }; + const registration = SuggestRegistry.register({ pattern: 'bar/path', scheme: 'foo' }, foo); + + provideSuggestionItems(model, new Position(1, 1), undefined, [foo]).then(items => { + registration.dispose(); + + assert.equal(items.length, 1); + assert.ok(items[0].support === foo); + }); + }); +}); diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts new file mode 100644 index 0000000000..ef7dbda9a9 --- /dev/null +++ b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts @@ -0,0 +1,460 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import Event from 'vs/base/common/event'; +import URI from 'vs/base/common/uri'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Model } from 'vs/editor/common/model/model'; +import { ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon'; +import { ISuggestSupport, ISuggestResult, SuggestRegistry } from 'vs/editor/common/modes'; +import { SuggestModel, LineContext } from 'vs/editor/contrib/suggest/browser/suggestModel'; +import { MockCodeEditor, MockScopeLocation } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; + +function createMockEditor(model: Model): MockCodeEditor { + const contextKeyService = new MockContextKeyService(); + const telemetryService = NullTelemetryService; + const instantiationService = new InstantiationService(new ServiceCollection( + [IContextKeyService, contextKeyService], + [ITelemetryService, telemetryService] + )); + + const editor = new MockCodeEditor(new MockScopeLocation(), {}, instantiationService, contextKeyService); + editor.setModel(model); + return editor; +} + +suite('SuggestModel - Context', function () { + + let model: Model; + + setup(function () { + model = Model.createFromString('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?'); + }); + + teardown(function () { + model.dispose(); + }); + + test('Context - shouldAutoTrigger', function () { + + function assertAutoTrigger(offset: number, expected: boolean): void { + const pos = model.getPositionAt(offset); + const editor = createMockEditor(model); + editor.setPosition(pos); + assert.equal(LineContext.shouldAutoTrigger(editor), expected); + editor.dispose(); + } + + assertAutoTrigger(3, true); // end of word, Das| + assertAutoTrigger(4, false); // no word Das | + assertAutoTrigger(1, false); // middle of word D|as + assertAutoTrigger(55, false); // number, 1861| + }); + +}); + +suite('SuggestModel - TriggerAndCancelOracle', function () { + + + const alwaysEmptySupport: ISuggestSupport = { + provideCompletionItems(doc, pos) { + return { + incomplete: false, + suggestions: [] + }; + } + }; + + const alwaysSomethingSupport: ISuggestSupport = { + provideCompletionItems(doc, pos) { + return { + incomplete: false, + suggestions: [{ + label: doc.getWordUntilPosition(pos).word, + type: 'property', + insertText: 'foofoo' + }] + }; + } + }; + + let disposables: IDisposable[] = []; + let model: Model; + + setup(function () { + disposables = dispose(disposables); + model = Model.createFromString('abc def', undefined, undefined, URI.parse('test:somefile.ttt')); + disposables.push(model); + }); + + function withOracle(callback: (model: SuggestModel, editor: ICommonCodeEditor) => any): TPromise { + + return new TPromise((resolve, reject) => { + const editor = createMockEditor(model); + const oracle = new SuggestModel(editor); + disposables.push(oracle, editor); + + try { + resolve(callback(oracle, editor)); + } catch (err) { + reject(err); + } + }); + } + + function assertEvent(event: Event, action: () => any, assert: (e: E) => any) { + return new TPromise((resolve, reject) => { + const sub = event(e => { + sub.dispose(); + try { + resolve(assert(e)); + } catch (err) { + reject(err); + } + }); + try { + action(); + } catch (err) { + reject(err); + } + }); + } + + test('events - cancel/trigger', function () { + return withOracle(model => { + + return TPromise.join([ + assertEvent(model.onDidCancel, function () { + model.cancel(); + }, function (event) { + assert.equal(event.retrigger, false); + }), + + assertEvent(model.onDidCancel, function () { + model.cancel(true); + }, function (event) { + assert.equal(event.retrigger, true); + }), + + // cancel on trigger + assertEvent(model.onDidCancel, function () { + model.trigger(false); + }, function (event) { + assert.equal(event.retrigger, false); + }), + + assertEvent(model.onDidCancel, function () { + model.trigger(false, true); + }, function (event) { + assert.equal(event.retrigger, true); + }), + + assertEvent(model.onDidTrigger, function () { + model.trigger(true); + }, function (event) { + assert.equal(event.auto, true); + }), + + assertEvent(model.onDidTrigger, function () { + model.trigger(false); + }, function (event) { + assert.equal(event.auto, false); + }) + ]); + }); + }); + + + test('events - suggest/empty', function () { + + disposables.push(SuggestRegistry.register({ scheme: 'test' }, alwaysEmptySupport)); + + return withOracle(model => { + return TPromise.join([ + assertEvent(model.onDidCancel, function () { + model.trigger(true); + }, function (event) { + assert.equal(event.retrigger, false); + }), + assertEvent(model.onDidSuggest, function () { + model.trigger(false); + }, function (event) { + assert.equal(event.auto, false); + assert.equal(event.isFrozen, false); + assert.equal(event.completionModel.items.length, 0); + }) + ]); + }); + }); + + test('trigger - on type', function () { + + disposables.push(SuggestRegistry.register({ scheme: 'test' }, alwaysSomethingSupport)); + + return withOracle((model, editor) => { + return assertEvent(model.onDidSuggest, () => { + editor.setPosition({ lineNumber: 1, column: 4 }); + editor.trigger('keyboard', Handler.Type, { text: 'd' }); + + }, event => { + assert.equal(event.auto, true); + assert.equal(event.completionModel.items.length, 1); + const [first] = event.completionModel.items; + + assert.equal(first.support, alwaysSomethingSupport); + }); + }); + }); + + test('#17400: Keep filtering suggestModel.ts after space', function () { + + disposables.push(SuggestRegistry.register({ scheme: 'test' }, { + provideCompletionItems(doc, pos) { + return { + currentWord: '', + incomplete: false, + suggestions: [{ + label: 'My Table', + type: 'property', + insertText: 'My Table' + }] + }; + } + })); + + model.setValue(''); + + return withOracle((model, editor) => { + + return assertEvent(model.onDidSuggest, () => { + // make sure completionModel starts here! + model.trigger(true); + }, event => { + + return assertEvent(model.onDidSuggest, () => { + editor.setPosition({ lineNumber: 1, column: 1 }); + editor.trigger('keyboard', Handler.Type, { text: 'My' }); + + }, event => { + assert.equal(event.auto, true); + assert.equal(event.completionModel.items.length, 1); + const [first] = event.completionModel.items; + assert.equal(first.suggestion.label, 'My Table'); + + return assertEvent(model.onDidSuggest, () => { + editor.setPosition({ lineNumber: 1, column: 3 }); + editor.trigger('keyboard', Handler.Type, { text: ' ' }); + + }, event => { + assert.equal(event.auto, true); + assert.equal(event.completionModel.items.length, 1); + const [first] = event.completionModel.items; + assert.equal(first.suggestion.label, 'My Table'); + }); + }); + }); + }); + }); + + test('#21484: Trigger character always force a new completion session', function () { + + disposables.push(SuggestRegistry.register({ scheme: 'test' }, { + provideCompletionItems(doc, pos) { + return { + currentWord: '', + incomplete: false, + suggestions: [{ + label: 'foo.bar', + type: 'property', + insertText: 'foo.bar', + overwriteBefore: pos.column - 1 + }] + }; + } + })); + + disposables.push(SuggestRegistry.register({ scheme: 'test' }, { + triggerCharacters: ['.'], + provideCompletionItems(doc, pos) { + return { + currentWord: '', + incomplete: false, + suggestions: [{ + label: 'boom', + type: 'property', + insertText: 'boom', + overwriteBefore: doc.getLineContent(pos.lineNumber)[pos.column - 2] === '.' ? 0 : pos.column - 1 + }] + }; + } + })); + + model.setValue(''); + + return withOracle((model, editor) => { + + return assertEvent(model.onDidSuggest, () => { + editor.setPosition({ lineNumber: 1, column: 1 }); + editor.trigger('keyboard', Handler.Type, { text: 'foo' }); + + }, event => { + assert.equal(event.auto, true); + assert.equal(event.completionModel.items.length, 1); + const [first] = event.completionModel.items; + assert.equal(first.suggestion.label, 'foo.bar'); + + return assertEvent(model.onDidSuggest, () => { + editor.trigger('keyboard', Handler.Type, { text: '.' }); + + }, event => { + assert.equal(event.auto, true); + assert.equal(event.completionModel.items.length, 2); + const [first, second] = event.completionModel.items; + assert.equal(first.suggestion.label, 'foo.bar'); + assert.equal(second.suggestion.label, 'boom'); + }); + }); + }); + }); + + test('Intellisense Completion doesn\'t respect space after equal sign (.html file), #29353 [1/2]', function () { + + disposables.push(SuggestRegistry.register({ scheme: 'test' }, alwaysSomethingSupport)); + + return withOracle((model, editor) => { + + editor.getModel().setValue('fo'); + editor.setPosition({ lineNumber: 1, column: 3 }); + + return assertEvent(model.onDidSuggest, () => { + model.trigger(false); + }, event => { + assert.equal(event.auto, false); + assert.equal(event.isFrozen, false); + assert.equal(event.completionModel.items.length, 1); + + return assertEvent(model.onDidCancel, () => { + editor.trigger('keyboard', Handler.Type, { text: '+' }); + }, event => { + assert.equal(event.retrigger, false); + }); + }); + }); + }); + + test('Intellisense Completion doesn\'t respect space after equal sign (.html file), #29353 [2/2]', function () { + + disposables.push(SuggestRegistry.register({ scheme: 'test' }, alwaysSomethingSupport)); + + return withOracle((model, editor) => { + + editor.getModel().setValue('fo'); + editor.setPosition({ lineNumber: 1, column: 3 }); + + return assertEvent(model.onDidSuggest, () => { + model.trigger(false); + }, event => { + assert.equal(event.auto, false); + assert.equal(event.isFrozen, false); + assert.equal(event.completionModel.items.length, 1); + + return assertEvent(model.onDidCancel, () => { + editor.trigger('keyboard', Handler.Type, { text: ' ' }); + }, event => { + assert.equal(event.retrigger, false); + }); + }); + }); + }); + + test('Incomplete suggestion results cause re-triggering when typing w/o further context, #28400 (1/2)', function () { + + disposables.push(SuggestRegistry.register({ scheme: 'test' }, { + provideCompletionItems(doc, pos) { + return { + incomplete: true, + suggestions: [{ + label: 'foo', + type: 'property', + insertText: 'foo', + overwriteBefore: pos.column - 1 + }] + }; + } + })); + + return withOracle((model, editor) => { + + editor.getModel().setValue('foo'); + editor.setPosition({ lineNumber: 1, column: 4 }); + + return assertEvent(model.onDidSuggest, () => { + model.trigger(false); + }, event => { + assert.equal(event.auto, false); + assert.equal(event.completionModel.incomplete, true); + assert.equal(event.completionModel.items.length, 1); + + return assertEvent(model.onDidCancel, () => { + editor.trigger('keyboard', Handler.Type, { text: ';' }); + }, event => { + assert.equal(event.retrigger, false); + }); + }); + }); + }); + + test('Incomplete suggestion results cause re-triggering when typing w/o further context, #28400 (2/2)', function () { + + disposables.push(SuggestRegistry.register({ scheme: 'test' }, { + provideCompletionItems(doc, pos) { + return { + incomplete: true, + suggestions: [{ + label: 'foo;', + type: 'property', + insertText: 'foo', + overwriteBefore: pos.column - 1 + }] + }; + } + })); + + return withOracle((model, editor) => { + + editor.getModel().setValue('foo'); + editor.setPosition({ lineNumber: 1, column: 4 }); + + return assertEvent(model.onDidSuggest, () => { + model.trigger(false); + }, event => { + assert.equal(event.auto, false); + assert.equal(event.completionModel.incomplete, true); + assert.equal(event.completionModel.items.length, 1); + + return assertEvent(model.onDidSuggest, () => { + // while we cancel incrementally enriching the set of + // completions we still filter against those that we have + // until now + editor.trigger('keyboard', Handler.Type, { text: ';' }); + }, event => { + assert.equal(event.auto, false); + assert.equal(event.completionModel.incomplete, true); + assert.equal(event.completionModel.items.length, 1); + + }); + }); + }); + }); +}); diff --git a/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.ts b/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.ts new file mode 100644 index 0000000000..26258398b6 --- /dev/null +++ b/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; + +@editorAction +export class ToggleTabFocusModeAction extends EditorAction { + + public static ID = 'editor.action.toggleTabFocusMode'; + + constructor() { + super({ + id: ToggleTabFocusModeAction.ID, + label: nls.localize({ key: 'toggle.tabMovesFocus', comment: ['Turn on/off use of tab key for moving focus around VS Code'] }, "Toggle Tab Key Moves Focus"), + alias: 'Toggle Tab Key Moves Focus', + precondition: null, + kbOpts: { + kbExpr: null, + primary: KeyMod.CtrlCmd | KeyCode.KEY_M, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_M } + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let oldValue = TabFocus.getTabFocusMode(); + TabFocus.setTabFocusMode(!oldValue); + } +} diff --git a/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts new file mode 100644 index 0000000000..7c1543d13b --- /dev/null +++ b/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts @@ -0,0 +1,373 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); + +import { sequence, asWinJsPromise } from 'vs/base/common/async'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { CommonEditorRegistry, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions'; +import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProviderRegistry } from 'vs/editor/common/modes'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Position } from 'vs/editor/common/core/position'; +import { registerColor, editorSelectionHighlight, activeContrastBorder, overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; + +export const editorWordHighlight = registerColor('editor.wordHighlightBackground', { dark: '#575757B8', light: '#57575740', hc: null }, nls.localize('wordHighlight', 'Background color of a symbol during read-access, like reading a variable.')); +export const editorWordHighlightStrong = registerColor('editor.wordHighlightStrongBackground', { dark: '#004972B8', light: '#0e639c40', hc: null }, nls.localize('wordHighlightStrong', 'Background color of a symbol during write-access, like writing to a variable.')); + +export const overviewRulerWordHighlightForeground = registerColor('editorOverviewRuler.wordHighlightForeground', { dark: '#A0A0A0', light: '#A0A0A0', hc: '#A0A0A0' }, nls.localize('overviewRulerWordHighlightForeground', 'Overview ruler marker color for symbol highlights.')); +export const overviewRulerWordHighlightStrongForeground = registerColor('editorOverviewRuler.wordHighlightStrongForeground', { dark: '#C0A0C0', light: '#C0A0C0', hc: '#C0A0C0' }, nls.localize('overviewRulerWordHighlightStrongForeground', 'Overview ruler marker color for write-access symbol highlights.')); + +export function getOccurrencesAtPosition(model: editorCommon.IReadOnlyModel, position: Position): TPromise { + + const orderedByScore = DocumentHighlightProviderRegistry.ordered(model); + let foundResult = false; + + // in order of score ask the occurrences provider + // until someone response with a good result + // (good = none empty array) + return sequence(orderedByScore.map(provider => { + return (): TPromise => { + if (!foundResult) { + return asWinJsPromise((token) => { + return provider.provideDocumentHighlights(model, position, token); + }).then(data => { + if (Array.isArray(data) && data.length > 0) { + foundResult = true; + return data; + } + return undefined; + }, err => { + onUnexpectedExternalError(err); + return undefined; + }); + } + return undefined; + }; + })).then(values => { + return values[0]; + }); +} + +CommonEditorRegistry.registerDefaultLanguageCommand('_executeDocumentHighlights', getOccurrencesAtPosition); + +class WordHighlighter { + + private editor: editorCommon.ICommonCodeEditor; + private occurrencesHighlight: boolean; + private model: editorCommon.IModel; + private _lastWordRange: Range; + private _decorationIds: string[]; + private toUnhook: IDisposable[]; + + private workerRequestTokenId: number = 0; + private workerRequest: TPromise = null; + private workerRequestCompleted: boolean = false; + private workerRequestValue: DocumentHighlight[] = []; + + private lastCursorPositionChangeTime: number = 0; + private renderDecorationsTimer: number = -1; + + constructor(editor: editorCommon.ICommonCodeEditor) { + this.editor = editor; + this.occurrencesHighlight = this.editor.getConfiguration().contribInfo.occurrencesHighlight; + this.model = this.editor.getModel(); + this.toUnhook = []; + this.toUnhook.push(editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => { + + if (!this.occurrencesHighlight) { + // Early exit if nothing needs to be done! + // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) + return; + } + + this._onPositionChanged(e); + })); + this.toUnhook.push(editor.onDidChangeModel((e) => { + this._stopAll(); + this.model = this.editor.getModel(); + })); + this.toUnhook.push(editor.onDidChangeModelContent((e) => { + this._stopAll(); + })); + this.toUnhook.push(editor.onDidChangeConfiguration((e) => { + let newValue = this.editor.getConfiguration().contribInfo.occurrencesHighlight; + if (this.occurrencesHighlight !== newValue) { + this.occurrencesHighlight = newValue; + this._stopAll(); + } + })); + + this._lastWordRange = null; + this._decorationIds = []; + this.workerRequestTokenId = 0; + this.workerRequest = null; + this.workerRequestCompleted = false; + + this.lastCursorPositionChangeTime = 0; + this.renderDecorationsTimer = -1; + } + + private _removeDecorations(): void { + if (this._decorationIds.length > 0) { + // remove decorations + this._decorationIds = this.editor.deltaDecorations(this._decorationIds, []); + } + } + + private _stopAll(): void { + this._lastWordRange = null; + + // Remove any existing decorations + this._removeDecorations(); + + // Cancel any renderDecorationsTimer + if (this.renderDecorationsTimer !== -1) { + clearTimeout(this.renderDecorationsTimer); + this.renderDecorationsTimer = -1; + } + + // Cancel any worker request + if (this.workerRequest !== null) { + this.workerRequest.cancel(); + this.workerRequest = null; + } + + // Invalidate any worker request callback + if (!this.workerRequestCompleted) { + this.workerRequestTokenId++; + this.workerRequestCompleted = true; + } + } + + private _onPositionChanged(e: ICursorPositionChangedEvent): void { + + // disabled + if (!this.occurrencesHighlight) { + this._stopAll(); + return; + } + + // ignore typing & other + if (e.reason !== CursorChangeReason.Explicit) { + this._stopAll(); + return; + } + + // no providers for this model + if (!DocumentHighlightProviderRegistry.has(this.model)) { + this._stopAll(); + return; + } + + var editorSelection = this.editor.getSelection(); + + // ignore multiline selection + if (editorSelection.startLineNumber !== editorSelection.endLineNumber) { + this._stopAll(); + return; + } + + var lineNumber = editorSelection.startLineNumber; + var startColumn = editorSelection.startColumn; + var endColumn = editorSelection.endColumn; + + var word = this.model.getWordAtPosition({ + lineNumber: lineNumber, + column: startColumn + }); + + // The selection must be inside a word or surround one word at most + if (!word || word.startColumn > startColumn || word.endColumn < endColumn) { + this._stopAll(); + return; + } + + // All the effort below is trying to achieve this: + // - when cursor is moved to a word, trigger immediately a findOccurrences request + // - 250ms later after the last cursor move event, render the occurrences + // - no flickering! + + var currentWordRange = new Range(lineNumber, word.startColumn, lineNumber, word.endColumn); + + var workerRequestIsValid = this._lastWordRange && this._lastWordRange.equalsRange(currentWordRange); + + // Even if we are on a different word, if that word is in the decorations ranges, the request is still valid + // (Same symbol) + for (var i = 0, len = this._decorationIds.length; !workerRequestIsValid && i < len; i++) { + var range = this.model.getDecorationRange(this._decorationIds[i]); + if (range && range.startLineNumber === lineNumber) { + if (range.startColumn <= startColumn && range.endColumn >= endColumn) { + workerRequestIsValid = true; + } + } + } + + + // There are 4 cases: + // a) old workerRequest is valid & completed, renderDecorationsTimer fired + // b) old workerRequest is valid & completed, renderDecorationsTimer not fired + // c) old workerRequest is valid, but not completed + // d) old workerRequest is not valid + + // For a) no action is needed + // For c), member 'lastCursorPositionChangeTime' will be used when installing the timer so no action is needed + + this.lastCursorPositionChangeTime = (new Date()).getTime(); + + if (workerRequestIsValid) { + if (this.workerRequestCompleted && this.renderDecorationsTimer !== -1) { + // case b) + // Delay the firing of renderDecorationsTimer by an extra 250 ms + clearTimeout(this.renderDecorationsTimer); + this.renderDecorationsTimer = -1; + this._beginRenderDecorations(); + } + } else { + // case d) + // Stop all previous actions and start fresh + this._stopAll(); + + var myRequestId = ++this.workerRequestTokenId; + this.workerRequestCompleted = false; + + this.workerRequest = getOccurrencesAtPosition(this.model, this.editor.getPosition()); + + this.workerRequest.then(data => { + if (myRequestId === this.workerRequestTokenId) { + this.workerRequestCompleted = true; + this.workerRequestValue = data || []; + this._beginRenderDecorations(); + } + }).done(); + } + + this._lastWordRange = currentWordRange; + } + + private _beginRenderDecorations(): void { + var currentTime = (new Date()).getTime(); + var minimumRenderTime = this.lastCursorPositionChangeTime + 250; + + if (currentTime >= minimumRenderTime) { + // Synchronous + this.renderDecorationsTimer = -1; + this.renderDecorations(); + } else { + // Asyncrhonous + this.renderDecorationsTimer = setTimeout(() => { + this.renderDecorations(); + }, (minimumRenderTime - currentTime)); + } + } + + private renderDecorations(): void { + this.renderDecorationsTimer = -1; + var decorations: editorCommon.IModelDeltaDecoration[] = []; + for (var i = 0, len = this.workerRequestValue.length; i < len; i++) { + var info = this.workerRequestValue[i]; + decorations.push({ + range: info.range, + options: WordHighlighter._getDecorationOptions(info.kind) + }); + } + + this._decorationIds = this.editor.deltaDecorations(this._decorationIds, decorations); + } + + private static _getDecorationOptions(kind: DocumentHighlightKind): ModelDecorationOptions { + if (kind === DocumentHighlightKind.Write) { + return this._WRITE_OPTIONS; + } else if (kind === DocumentHighlightKind.Text) { + return this._TEXT_OPTIONS; + } else { + return this._REGULAR_OPTIONS; + } + } + + private static _WRITE_OPTIONS = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'wordHighlightStrong', + overviewRuler: { + color: themeColorFromId(overviewRulerWordHighlightStrongForeground), + darkColor: themeColorFromId(overviewRulerWordHighlightStrongForeground), + position: editorCommon.OverviewRulerLane.Center + } + }); + + private static _TEXT_OPTIONS = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'selectionHighlight', + overviewRuler: { + color: themeColorFromId(overviewRulerSelectionHighlightForeground), + darkColor: themeColorFromId(overviewRulerSelectionHighlightForeground), + position: editorCommon.OverviewRulerLane.Center + } + }); + + private static _REGULAR_OPTIONS = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'wordHighlight', + overviewRuler: { + color: themeColorFromId(overviewRulerWordHighlightForeground), + darkColor: themeColorFromId(overviewRulerWordHighlightForeground), + position: editorCommon.OverviewRulerLane.Center + } + }); + + public dispose(): void { + this._stopAll(); + this.toUnhook = dispose(this.toUnhook); + } +} + +@commonEditorContribution +class WordHighlighterContribution implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.wordHighlighter'; + + private wordHighligher: WordHighlighter; + + constructor(editor: editorCommon.ICommonCodeEditor) { + this.wordHighligher = new WordHighlighter(editor); + } + + public getId(): string { + return WordHighlighterContribution.ID; + } + + public dispose(): void { + this.wordHighligher.dispose(); + } +} + +registerThemingParticipant((theme, collector) => { + let selectionHighlight = theme.getColor(editorSelectionHighlight); + if (selectionHighlight) { + collector.addRule(`.monaco-editor .focused .selectionHighlight { background-color: ${selectionHighlight}; }`); + collector.addRule(`.monaco-editor .selectionHighlight { background-color: ${selectionHighlight.transparent(0.5)}; }`); + } + let wordHighlight = theme.getColor(editorWordHighlight); + if (wordHighlight) { + collector.addRule(`.monaco-editor .wordHighlight { background-color: ${wordHighlight}; }`); + } + let wordHighlightStrong = theme.getColor(editorWordHighlightStrong); + if (wordHighlightStrong) { + collector.addRule(`.monaco-editor .wordHighlightStrong { background-color: ${wordHighlightStrong}; }`); + } + let hcOutline = theme.getColor(activeContrastBorder); + if (hcOutline) { + collector.addRule(`.monaco-editor .selectionHighlight { border: 1px dotted ${hcOutline}; box-sizing: border-box; }`); + collector.addRule(`.monaco-editor .wordHighlight { border: 1px dashed ${hcOutline}; box-sizing: border-box; }`); + collector.addRule(`.monaco-editor .wordHighlightStrong { border: 1px dashed ${hcOutline}; box-sizing: border-box; }`); + } + +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/wordOperations/common/wordOperations.ts b/src/vs/editor/contrib/wordOperations/common/wordOperations.ts new file mode 100644 index 0000000000..19aee7b008 --- /dev/null +++ b/src/vs/editor/contrib/wordOperations/common/wordOperations.ts @@ -0,0 +1,391 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICommonCodeEditor, IModel, ScrollType } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Selection } from 'vs/editor/common/core/selection'; +import { editorCommand, ServicesAccessor, EditorCommand, ICommandOptions } from 'vs/editor/common/editorCommonExtensions'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { WordNavigationType, WordOperations } from 'vs/editor/common/controller/cursorWordOperations'; +import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; +import { getMapForWordSeparators, WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { CursorState } from 'vs/editor/common/controller/cursorCommon'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; + +export interface MoveWordOptions extends ICommandOptions { + inSelectionMode: boolean; + wordNavigationType: WordNavigationType; +} + +export abstract class MoveWordCommand extends EditorCommand { + + private readonly _inSelectionMode: boolean; + private readonly _wordNavigationType: WordNavigationType; + + constructor(opts: MoveWordOptions) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; + this._wordNavigationType = opts.wordNavigationType; + } + + public runEditorCommand(accessor: ServicesAccessor, editor: ICommonCodeEditor, args: any): void { + const config = editor.getConfiguration(); + const wordSeparators = getMapForWordSeparators(config.wordSeparators); + const model = editor.getModel(); + const selections = editor.getSelections(); + + const result = selections.map((sel) => { + const inPosition = new Position(sel.positionLineNumber, sel.positionColumn); + const outPosition = this._move(wordSeparators, model, inPosition, this._wordNavigationType); + return this._moveTo(sel, outPosition, this._inSelectionMode); + }); + + editor._getCursors().setStates('moveWordCommand', CursorChangeReason.NotSet, result.map(r => CursorState.fromModelSelection(r))); + if (result.length === 1) { + const pos = new Position(result[0].positionLineNumber, result[0].positionColumn); + editor.revealPosition(pos, ScrollType.Smooth); + } + } + + private _moveTo(from: Selection, to: Position, inSelectionMode: boolean): Selection { + if (inSelectionMode) { + // move just position + return new Selection( + from.selectionStartLineNumber, + from.selectionStartColumn, + to.lineNumber, + to.column + ); + } else { + // move everything + return new Selection( + to.lineNumber, + to.column, + to.lineNumber, + to.column + ); + } + } + + protected abstract _move(wordSeparators: WordCharacterClassifier, model: IModel, position: Position, wordNavigationType: WordNavigationType): Position; +} + +export class WordLeftCommand extends MoveWordCommand { + protected _move(wordSeparators: WordCharacterClassifier, model: IModel, position: Position, wordNavigationType: WordNavigationType): Position { + return WordOperations.moveWordLeft(wordSeparators, model, position, wordNavigationType); + } +} + +export class WordRightCommand extends MoveWordCommand { + protected _move(wordSeparators: WordCharacterClassifier, model: IModel, position: Position, wordNavigationType: WordNavigationType): Position { + return WordOperations.moveWordRight(wordSeparators, model, position, wordNavigationType); + } +} + +@editorCommand +export class CursorWordStartLeft extends WordLeftCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordStart, + id: 'cursorWordStartLeft', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.LeftArrow, + mac: { primary: KeyMod.Alt | KeyCode.LeftArrow } + } + }); + } +} + +@editorCommand +export class CursorWordEndLeft extends WordLeftCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordEnd, + id: 'cursorWordEndLeft', + precondition: null + }); + } +} + +@editorCommand +export class CursorWordLeft extends WordLeftCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordStart, + id: 'cursorWordLeft', + precondition: null + }); + } +} + +@editorCommand +export class CursorWordStartLeftSelect extends WordLeftCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordStart, + id: 'cursorWordStartLeftSelect', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow, + mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow } + } + }); + } +} + +@editorCommand +export class CursorWordEndLeftSelect extends WordLeftCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordEnd, + id: 'cursorWordEndLeftSelect', + precondition: null + }); + } +} + +@editorCommand +export class CursorWordLeftSelect extends WordLeftCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordStart, + id: 'cursorWordLeftSelect', + precondition: null + }); + } +} + +@editorCommand +export class CursorWordStartRight extends WordRightCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordStart, + id: 'cursorWordStartRight', + precondition: null + }); + } +} + +@editorCommand +export class CursorWordEndRight extends WordRightCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordEnd, + id: 'cursorWordEndRight', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.RightArrow, + mac: { primary: KeyMod.Alt | KeyCode.RightArrow } + } + }); + } +} + +@editorCommand +export class CursorWordRight extends WordRightCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordEnd, + id: 'cursorWordRight', + precondition: null + }); + } +} + +@editorCommand +export class CursorWordStartRightSelect extends WordRightCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordStart, + id: 'cursorWordStartRightSelect', + precondition: null + }); + } +} + +@editorCommand +export class CursorWordEndRightSelect extends WordRightCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordEnd, + id: 'cursorWordEndRightSelect', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow, + mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow } + } + }); + } +} + +@editorCommand +export class CursorWordRightSelect extends WordRightCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordEnd, + id: 'cursorWordRightSelect', + precondition: null + }); + } +} + +export interface DeleteWordOptions extends ICommandOptions { + whitespaceHeuristics: boolean; + wordNavigationType: WordNavigationType; +} + +export abstract class DeleteWordCommand extends EditorCommand { + private readonly _whitespaceHeuristics: boolean; + private readonly _wordNavigationType: WordNavigationType; + + constructor(opts: DeleteWordOptions) { + super(opts); + this._whitespaceHeuristics = opts.whitespaceHeuristics; + this._wordNavigationType = opts.wordNavigationType; + } + + public runEditorCommand(accessor: ServicesAccessor, editor: ICommonCodeEditor, args: any): void { + const config = editor.getConfiguration(); + const wordSeparators = getMapForWordSeparators(config.wordSeparators); + const model = editor.getModel(); + const selections = editor.getSelections(); + + const commands = selections.map((sel) => { + const deleteRange = this._delete(wordSeparators, model, sel, this._whitespaceHeuristics, this._wordNavigationType); + return new ReplaceCommand(deleteRange, ''); + }); + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } + + protected abstract _delete(wordSeparators: WordCharacterClassifier, model: IModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range; +} + +export class DeleteWordLeftCommand extends DeleteWordCommand { + protected _delete(wordSeparators: WordCharacterClassifier, model: IModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + let r = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + if (r) { + return r; + } + return new Range(1, 1, 1, 1); + } +} + +export class DeleteWordRightCommand extends DeleteWordCommand { + protected _delete(wordSeparators: WordCharacterClassifier, model: IModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + let r = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + if (r) { + return r; + } + const lineCount = model.getLineCount(); + const maxColumn = model.getLineMaxColumn(lineCount); + return new Range(lineCount, maxColumn, lineCount, maxColumn); + } +} + +@editorCommand +export class DeleteWordStartLeft extends DeleteWordLeftCommand { + constructor() { + super({ + whitespaceHeuristics: false, + wordNavigationType: WordNavigationType.WordStart, + id: 'deleteWordStartLeft', + precondition: EditorContextKeys.writable + }); + } +} + +@editorCommand +export class DeleteWordEndLeft extends DeleteWordLeftCommand { + constructor() { + super({ + whitespaceHeuristics: false, + wordNavigationType: WordNavigationType.WordEnd, + id: 'deleteWordEndLeft', + precondition: EditorContextKeys.writable + }); + } +} + +@editorCommand +export class DeleteWordLeft extends DeleteWordLeftCommand { + constructor() { + super({ + whitespaceHeuristics: true, + wordNavigationType: WordNavigationType.WordStart, + id: 'deleteWordLeft', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.Backspace, + mac: { primary: KeyMod.Alt | KeyCode.Backspace } + } + }); + } +} + +@editorCommand +export class DeleteWordStartRight extends DeleteWordRightCommand { + constructor() { + super({ + whitespaceHeuristics: false, + wordNavigationType: WordNavigationType.WordStart, + id: 'deleteWordStartRight', + precondition: EditorContextKeys.writable + }); + } +} + +@editorCommand +export class DeleteWordEndRight extends DeleteWordRightCommand { + constructor() { + super({ + whitespaceHeuristics: false, + wordNavigationType: WordNavigationType.WordEnd, + id: 'deleteWordEndRight', + precondition: EditorContextKeys.writable + }); + } +} + +@editorCommand +export class DeleteWordRight extends DeleteWordRightCommand { + constructor() { + super({ + whitespaceHeuristics: true, + wordNavigationType: WordNavigationType.WordEnd, + id: 'deleteWordRight', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.Delete, + mac: { primary: KeyMod.Alt | KeyCode.Delete } + } + }); + } +} diff --git a/src/vs/editor/contrib/wordOperations/test/common/wordOperations.test.ts b/src/vs/editor/contrib/wordOperations/test/common/wordOperations.test.ts new file mode 100644 index 0000000000..d1d364196c --- /dev/null +++ b/src/vs/editor/contrib/wordOperations/test/common/wordOperations.test.ts @@ -0,0 +1,704 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { + CursorWordLeft, CursorWordLeftSelect, CursorWordStartLeft, + CursorWordEndLeft, CursorWordStartLeftSelect, CursorWordEndLeftSelect, + CursorWordStartRight, CursorWordEndRight, CursorWordRight, + CursorWordStartRightSelect, CursorWordEndRightSelect, CursorWordRightSelect, + DeleteWordLeft, DeleteWordStartLeft, DeleteWordEndLeft, + DeleteWordRight, DeleteWordStartRight, DeleteWordEndRight +} from 'vs/editor/contrib/wordOperations/common/wordOperations'; +import { EditorCommand } from 'vs/editor/common/editorCommonExtensions'; + +suite('WordOperations', () => { + + const _cursorWordStartLeft = new CursorWordStartLeft(); + const _cursorWordEndLeft = new CursorWordEndLeft(); + const _cursorWordLeft = new CursorWordLeft(); + const _cursorWordStartLeftSelect = new CursorWordStartLeftSelect(); + const _cursorWordEndLeftSelect = new CursorWordEndLeftSelect(); + const _cursorWordLeftSelect = new CursorWordLeftSelect(); + const _cursorWordStartRight = new CursorWordStartRight(); + const _cursorWordEndRight = new CursorWordEndRight(); + const _cursorWordRight = new CursorWordRight(); + const _cursorWordStartRightSelect = new CursorWordStartRightSelect(); + const _cursorWordEndRightSelect = new CursorWordEndRightSelect(); + const _cursorWordRightSelect = new CursorWordRightSelect(); + const _deleteWordLeft = new DeleteWordLeft(); + const _deleteWordStartLeft = new DeleteWordStartLeft(); + const _deleteWordEndLeft = new DeleteWordEndLeft(); + const _deleteWordRight = new DeleteWordRight(); + const _deleteWordStartRight = new DeleteWordStartRight(); + const _deleteWordEndRight = new DeleteWordEndRight(); + + function runEditorCommand(editor: ICommonCodeEditor, command: EditorCommand): void { + command.runEditorCommand(null, editor, null); + } + function moveWordLeft(editor: ICommonCodeEditor, inSelectionMode: boolean = false): void { + runEditorCommand(editor, inSelectionMode ? _cursorWordLeftSelect : _cursorWordLeft); + } + function moveWordStartLeft(editor: ICommonCodeEditor, inSelectionMode: boolean = false): void { + runEditorCommand(editor, inSelectionMode ? _cursorWordStartLeftSelect : _cursorWordStartLeft); + } + function moveWordEndLeft(editor: ICommonCodeEditor, inSelectionMode: boolean = false): void { + runEditorCommand(editor, inSelectionMode ? _cursorWordEndLeftSelect : _cursorWordEndLeft); + } + function moveWordRight(editor: ICommonCodeEditor, inSelectionMode: boolean = false): void { + runEditorCommand(editor, inSelectionMode ? _cursorWordRightSelect : _cursorWordRight); + } + function moveWordEndRight(editor: ICommonCodeEditor, inSelectionMode: boolean = false): void { + runEditorCommand(editor, inSelectionMode ? _cursorWordEndRightSelect : _cursorWordEndRight); + } + function moveWordStartRight(editor: ICommonCodeEditor, inSelectionMode: boolean = false): void { + runEditorCommand(editor, inSelectionMode ? _cursorWordStartRightSelect : _cursorWordStartRight); + } + function deleteWordLeft(editor: ICommonCodeEditor): void { + runEditorCommand(editor, _deleteWordLeft); + } + function deleteWordStartLeft(editor: ICommonCodeEditor): void { + runEditorCommand(editor, _deleteWordStartLeft); + } + function deleteWordEndLeft(editor: ICommonCodeEditor): void { + runEditorCommand(editor, _deleteWordEndLeft); + } + function deleteWordRight(editor: ICommonCodeEditor): void { + runEditorCommand(editor, _deleteWordRight); + } + function deleteWordStartRight(editor: ICommonCodeEditor): void { + runEditorCommand(editor, _deleteWordStartRight); + } + function deleteWordEndRight(editor: ICommonCodeEditor): void { + runEditorCommand(editor, _deleteWordEndRight); + } + + test('move word left', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + editor.setPosition(new Position(5, 2)); + const expectedStops = [ + [5, 1], + [4, 1], + [3, 11], + [3, 5], + [3, 1], + [2, 12], + [2, 5], + [2, 2], + [2, 1], + [1, 15], + [1, 9], + [1, 6], + [1, 1], + [1, 1], + ]; + + let actualStops: number[][] = []; + for (let i = 0; i < expectedStops.length; i++) { + moveWordLeft(editor); + const pos = editor.getPosition(); + actualStops.push([pos.lineNumber, pos.column]); + } + + assert.deepEqual(actualStops, expectedStops); + }); + }); + + test('move word left selection', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + editor.setPosition(new Position(5, 2)); + moveWordLeft(editor, true); + assert.deepEqual(editor.getSelection(), new Selection(5, 2, 5, 1)); + }); + }); + + test('issue #832: moveWordLeft', () => { + withMockCodeEditor([ + ' /* Just some more text a+= 3 +5-3 + 7 */ ' + ], {}, (editor, _) => { + editor.setPosition(new Position(1, 50)); + + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 '.length + 1, '001'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + '.length + 1, '002'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 '.length + 1, '003'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-'.length + 1, '004'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5'.length + 1, '005'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +'.length + 1, '006'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 '.length + 1, '007'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= '.length + 1, '008'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a'.length + 1, '009'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text '.length + 1, '010'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more '.length + 1, '011'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some '.length + 1, '012'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* Just '.length + 1, '013'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' /* '.length + 1, '014'); + moveWordLeft(editor); assert.equal(editor.getPosition().column, ' '.length + 1, '015'); + }); + }); + + test('moveWordStartLeft', () => { + withMockCodeEditor([ + ' /* Just some more text a+= 3 +5-3 + 7 */ ' + ], {}, (editor, _) => { + editor.setPosition(new Position(1, 50)); + + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 '.length + 1, '001'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + '.length + 1, '002'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 '.length + 1, '003'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-'.length + 1, '004'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5'.length + 1, '005'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +'.length + 1, '006'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 '.length + 1, '007'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= '.length + 1, '008'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a'.length + 1, '009'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text '.length + 1, '010'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more '.length + 1, '011'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some '.length + 1, '012'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* Just '.length + 1, '013'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' /* '.length + 1, '014'); + moveWordStartLeft(editor); assert.equal(editor.getPosition().column, ' '.length + 1, '015'); + }); + }); + + test('moveWordEndLeft', () => { + withMockCodeEditor([ + ' /* Just some more text a+= 3 +5-3 + 7 */ ' + ], {}, (editor, _) => { + editor.setPosition(new Position(1, 50)); + + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 */'.length + 1, '001'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7'.length + 1, '002'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 +'.length + 1, '003'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3'.length + 1, '004'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-'.length + 1, '005'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5'.length + 1, '006'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +'.length + 1, '007'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3'.length + 1, '008'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+='.length + 1, '009'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a'.length + 1, '010'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more text'.length + 1, '011'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some more'.length + 1, '012'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just some'.length + 1, '013'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /* Just'.length + 1, '014'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ' /*'.length + 1, '015'); + moveWordEndLeft(editor); assert.equal(editor.getPosition().column, ''.length + 1, '016'); + }); + }); + + test('move word right', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + editor.setPosition(new Position(1, 1)); + let expectedStops = [ + [1, 8], + [1, 14], + [1, 19], + [1, 21], + [2, 4], + [2, 11], + [2, 16], + [3, 10], + [3, 17], + [4, 1], + [5, 2], + [5, 2], + ]; + + let actualStops: number[][] = []; + for (let i = 0; i < expectedStops.length; i++) { + moveWordRight(editor); + let pos = editor.getPosition(); + actualStops.push([pos.lineNumber, pos.column]); + } + + assert.deepEqual(actualStops, expectedStops); + }); + }); + + test('move word right selection', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + editor.setPosition(new Position(1, 1)); + moveWordRight(editor, true); + assert.deepEqual(editor.getSelection(), new Selection(1, 1, 1, 8)); + }); + }); + + test('issue #832: moveWordRight', () => { + withMockCodeEditor([ + ' /* Just some more text a+= 3 +5-3 + 7 */ ' + ], {}, (editor, _) => { + editor.setPosition(new Position(1, 1)); + + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /*'.length + 1, '001'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just'.length + 1, '003'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some'.length + 1, '004'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more'.length + 1, '005'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text'.length + 1, '006'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a'.length + 1, '007'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+='.length + 1, '008'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3'.length + 1, '009'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +'.length + 1, '010'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5'.length + 1, '011'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-'.length + 1, '012'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3'.length + 1, '013'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 +'.length + 1, '014'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7'.length + 1, '015'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 */'.length + 1, '016'); + moveWordRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1, '016'); + + }); + }); + + test('moveWordEndRight', () => { + withMockCodeEditor([ + ' /* Just some more text a+= 3 +5-3 + 7 */ ' + ], {}, (editor, _) => { + editor.setPosition(new Position(1, 1)); + + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /*'.length + 1, '001'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just'.length + 1, '003'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some'.length + 1, '004'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more'.length + 1, '005'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text'.length + 1, '006'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a'.length + 1, '007'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+='.length + 1, '008'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3'.length + 1, '009'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +'.length + 1, '010'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5'.length + 1, '011'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-'.length + 1, '012'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3'.length + 1, '013'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 +'.length + 1, '014'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7'.length + 1, '015'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 */'.length + 1, '016'); + moveWordEndRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1, '016'); + + }); + }); + + test('moveWordStartRight', () => { + withMockCodeEditor([ + ' /* Just some more text a+= 3 +5-3 + 7 */ ' + ], {}, (editor, _) => { + editor.setPosition(new Position(1, 1)); + + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' '.length + 1, '001'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* '.length + 1, '002'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just '.length + 1, '003'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some '.length + 1, '004'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more '.length + 1, '005'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text '.length + 1, '006'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a'.length + 1, '007'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= '.length + 1, '008'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 '.length + 1, '009'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +'.length + 1, '010'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5'.length + 1, '011'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-'.length + 1, '012'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 '.length + 1, '013'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + '.length + 1, '014'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 '.length + 1, '015'); + moveWordStartRight(editor); assert.equal(editor.getPosition().column, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1, '016'); + }); + }); + + test('delete word left for non-empty selection', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setSelection(new Selection(3, 7, 3, 9)); + deleteWordLeft(editor); + assert.equal(model.getLineContent(3), ' Thd Line🐶'); + assert.deepEqual(editor.getPosition(), new Position(3, 7)); + }); + }); + + test('delete word left for caret at beginning of document', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 1)); + deleteWordLeft(editor); + assert.equal(model.getLineContent(1), ' \tMy First Line\t '); + assert.deepEqual(editor.getPosition(), new Position(1, 1)); + }); + }); + + test('delete word left for caret at end of whitespace', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(3, 11)); + deleteWordLeft(editor); + assert.equal(model.getLineContent(3), ' Line🐶'); + assert.deepEqual(editor.getPosition(), new Position(3, 5)); + }); + }); + + test('delete word left for caret just behind a word', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(2, 11)); + deleteWordLeft(editor); + assert.equal(model.getLineContent(2), '\tMy Line'); + assert.deepEqual(editor.getPosition(), new Position(2, 5)); + }); + }); + + test('delete word left for caret inside of a word', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 12)); + deleteWordLeft(editor); + assert.equal(model.getLineContent(1), ' \tMy st Line\t '); + assert.deepEqual(editor.getPosition(), new Position(1, 9)); + }); + }); + + test('delete word right for non-empty selection', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setSelection(new Selection(3, 7, 3, 9)); + deleteWordRight(editor); + assert.equal(model.getLineContent(3), ' Thd Line🐶'); + assert.deepEqual(editor.getPosition(), new Position(3, 7)); + }); + }); + + test('delete word right for caret at end of document', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(5, 3)); + deleteWordRight(editor); + assert.equal(model.getLineContent(5), '1'); + assert.deepEqual(editor.getPosition(), new Position(5, 2)); + }); + }); + + test('delete word right for caret at beggining of whitespace', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(3, 1)); + deleteWordRight(editor); + assert.equal(model.getLineContent(3), 'Third Line🐶'); + assert.deepEqual(editor.getPosition(), new Position(3, 1)); + }); + }); + + test('delete word right for caret just before a word', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(2, 5)); + deleteWordRight(editor); + assert.equal(model.getLineContent(2), '\tMy Line'); + assert.deepEqual(editor.getPosition(), new Position(2, 5)); + }); + }); + + test('delete word right for caret inside of a word', () => { + withMockCodeEditor([ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1', + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 11)); + deleteWordRight(editor); + assert.equal(model.getLineContent(1), ' \tMy Fi Line\t '); + assert.deepEqual(editor.getPosition(), new Position(1, 11)); + }); + }); + + test('issue #832: deleteWordLeft', () => { + withMockCodeEditor([ + ' /* Just some text a+= 3 +5 */ ' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 37)); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5 */', '001'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5 ', '002'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +', '003'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 ', '004'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= ', '005'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a', '006'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text ', '007'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some ', '008'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' /* Just ', '009'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' /* ', '010'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), ' ', '011'); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), '', '012'); + }); + }); + + test('deleteWordStartLeft', () => { + withMockCodeEditor([ + ' /* Just some text a+= 3 +5 */ ' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 37)); + + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5 ', '001'); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +', '002'); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 ', '003'); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= ', '004'); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a', '005'); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text ', '006'); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some ', '007'); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just ', '008'); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), ' /* ', '009'); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), ' ', '010'); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), '', '011'); + }); + }); + + test('deleteWordEndLeft', () => { + withMockCodeEditor([ + ' /* Just some text a+= 3 +5 */ ' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 37)); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5 */', '001'); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5', '002'); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +', '003'); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3', '004'); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+=', '005'); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a', '006'); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text', '007'); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some', '008'); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), ' /* Just', '009'); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), ' /*', '010'); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), '', '011'); + }); + }); + + test('issue #24947', () => { + withMockCodeEditor([ + '{', + '}' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(2, 1)); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), '{}'); + }); + + withMockCodeEditor([ + '{', + '}' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(2, 1)); + deleteWordStartLeft(editor); assert.equal(model.getLineContent(1), '{}'); + }); + + withMockCodeEditor([ + '{', + '}' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(2, 1)); + deleteWordEndLeft(editor); assert.equal(model.getLineContent(1), '{}'); + }); + }); + + test('issue #832: deleteWordRight', () => { + withMockCodeEditor([ + ' /* Just some text a+= 3 +5-3 */ ' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 1)); + deleteWordRight(editor); assert.equal(model.getLineContent(1), '/* Just some text a+= 3 +5-3 */ ', '001'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), ' Just some text a+= 3 +5-3 */ ', '002'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), ' some text a+= 3 +5-3 */ ', '003'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), ' text a+= 3 +5-3 */ ', '004'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), ' a+= 3 +5-3 */ ', '005'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), '+= 3 +5-3 */ ', '006'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), ' 3 +5-3 */ ', '007'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), ' +5-3 */ ', '008'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), '5-3 */ ', '009'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), '-3 */ ', '010'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), '3 */ ', '011'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), ' */ ', '012'); + deleteWordRight(editor); assert.equal(model.getLineContent(1), ' ', '013'); + }); + }); + + test('issue #3882: deleteWordRight', () => { + withMockCodeEditor([ + 'public void Add( int x,', + ' int y )' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 24)); + deleteWordRight(editor); assert.equal(model.getLineContent(1), 'public void Add( int x,int y )', '001'); + }); + }); + + test('issue #3882: deleteWordStartRight', () => { + withMockCodeEditor([ + 'public void Add( int x,', + ' int y )' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 24)); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), 'public void Add( int x,int y )', '001'); + }); + }); + + test('issue #3882: deleteWordEndRight', () => { + withMockCodeEditor([ + 'public void Add( int x,', + ' int y )' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 24)); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), 'public void Add( int x,int y )', '001'); + }); + }); + + test('deleteWordStartRight', () => { + withMockCodeEditor([ + ' /* Just some text a+= 3 +5-3 */ ' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 1)); + + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), '/* Just some text a+= 3 +5-3 */ ', '001'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), 'Just some text a+= 3 +5-3 */ ', '002'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), 'some text a+= 3 +5-3 */ ', '003'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), 'text a+= 3 +5-3 */ ', '004'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), 'a+= 3 +5-3 */ ', '005'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), '+= 3 +5-3 */ ', '006'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), '3 +5-3 */ ', '007'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), '+5-3 */ ', '008'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), '5-3 */ ', '009'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), '-3 */ ', '010'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), '3 */ ', '011'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), '*/ ', '012'); + deleteWordStartRight(editor); assert.equal(model.getLineContent(1), '', '013'); + }); + }); + + test('deleteWordEndRight', () => { + withMockCodeEditor([ + ' /* Just some text a+= 3 +5-3 */ ' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 1)); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), ' Just some text a+= 3 +5-3 */ ', '001'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), ' some text a+= 3 +5-3 */ ', '002'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), ' text a+= 3 +5-3 */ ', '003'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), ' a+= 3 +5-3 */ ', '004'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), '+= 3 +5-3 */ ', '005'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), ' 3 +5-3 */ ', '006'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), ' +5-3 */ ', '007'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), '5-3 */ ', '008'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), '-3 */ ', '009'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), '3 */ ', '010'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), ' */ ', '011'); + deleteWordEndRight(editor); assert.equal(model.getLineContent(1), ' ', '012'); + }); + }); + + test('issue #3882 (1): Ctrl+Delete removing entire line when used at the end of line', () => { + withMockCodeEditor([ + 'A line with text.', + ' And another one' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 18)); + deleteWordRight(editor); assert.equal(model.getLineContent(1), 'A line with text.And another one', '001'); + }); + }); + + test('issue #3882 (2): Ctrl+Delete removing entire line when used at the end of line', () => { + withMockCodeEditor([ + 'A line with text.', + ' And another one' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(2, 1)); + deleteWordLeft(editor); assert.equal(model.getLineContent(1), 'A line with text. And another one', '001'); + }); + }); +}); diff --git a/src/vs/editor/contrib/zoneWidget/browser/media/close-inverse.svg b/src/vs/editor/contrib/zoneWidget/browser/media/close-inverse.svg new file mode 100644 index 0000000000..751e89b3b0 --- /dev/null +++ b/src/vs/editor/contrib/zoneWidget/browser/media/close-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/zoneWidget/browser/media/close.svg b/src/vs/editor/contrib/zoneWidget/browser/media/close.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/vs/editor/contrib/zoneWidget/browser/media/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.css b/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.css new file mode 100644 index 0000000000..f21a549310 --- /dev/null +++ b/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.css @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .peekview-widget .head { + -webkit-box-sizing: border-box; + -o-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; +} + +.monaco-editor .peekview-widget .head .peekview-title { + display: inline-block; + font-size: 13px; + margin-left: 20px; + cursor: pointer; +} + +.monaco-editor .peekview-widget .head .peekview-title .dirname:not(:empty) { + font-size: 0.9em; + margin-left: 0.5em; +} + +.monaco-editor .peekview-widget .head .peekview-actions { + display: inline-block; + position: absolute; + right: 2px; + top: 2px; +} + +.monaco-editor .peekview-widget .head .peekview-actions .action-label { + width: 16px; + height: 16px; + margin: 2px 0; +} + +.monaco-editor .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action { + background: url('media/close.svg') center center no-repeat; +} + +.monaco-editor .peekview-widget > .body { + border-top: 1px solid; + position: relative; +} + +/* Dark Theme */ +/* High Contrast Theme */ + +.monaco-editor.hc-black .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action, +.monaco-editor.vs-dark .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action { + background: url('media/close-inverse.svg') center center no-repeat; +} + diff --git a/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.ts new file mode 100644 index 0000000000..1b733eb475 --- /dev/null +++ b/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.ts @@ -0,0 +1,216 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./peekViewWidget'; +import * as nls from 'vs/nls'; +import { Action } from 'vs/base/common/actions'; +import * as strings from 'vs/base/common/strings'; +import * as objects from 'vs/base/common/objects'; +import { $ } from 'vs/base/browser/builder'; +import Event, { Emitter } from 'vs/base/common/event'; +import * as dom from 'vs/base/browser/dom'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IOptions, ZoneWidget, IStyles } from './zoneWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { Color } from 'vs/base/common/color'; + +export var IPeekViewService = createDecorator('peekViewService'); + +export namespace PeekContext { + export const inPeekEditor = new RawContextKey('inReferenceSearchEditor', true); + export const notInPeekEditor: ContextKeyExpr = inPeekEditor.toNegated(); +} + +export const NOT_INNER_EDITOR_CONTEXT_KEY = new RawContextKey('inReferenceSearchEditor', true); + +export interface IPeekViewService { + _serviceBrand: any; + isActive: boolean; +} + +export function getOuterEditor(accessor: ServicesAccessor): ICommonCodeEditor { + let editor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); + if (editor instanceof EmbeddedCodeEditorWidget) { + return editor.getParentEditor(); + } + return editor; +} + +export interface IPeekViewStyles extends IStyles { + headerBackgroundColor?: Color; + primaryHeadingColor?: Color; + secondaryHeadingColor?: Color; +} + +export interface IPeekViewOptions extends IOptions, IPeekViewStyles { +} + +const defaultOptions: IPeekViewOptions = { + headerBackgroundColor: Color.white, + primaryHeadingColor: Color.fromHex('#333333'), + secondaryHeadingColor: Color.fromHex('#6c6c6cb3') +}; + +export abstract class PeekViewWidget extends ZoneWidget implements IPeekViewService { + + public _serviceBrand: any; + + private _onDidClose = new Emitter(); + private _isActive = false; + + protected _headElement: HTMLDivElement; + protected _primaryHeading: HTMLElement; + protected _secondaryHeading: HTMLElement; + protected _metaHeading: HTMLElement; + protected _actionbarWidget: ActionBar; + protected _bodyElement: HTMLDivElement; + + constructor(editor: ICodeEditor, options: IPeekViewOptions = {}) { + super(editor, options); + objects.mixin(this.options, defaultOptions, false); + } + + public dispose(): void { + this._isActive = false; + super.dispose(); + this._onDidClose.fire(this); + } + + public get onDidClose(): Event { + return this._onDidClose.event; + } + + public get isActive(): boolean { + return this._isActive; + } + + public show(where: any, heightInLines: number): void { + this._isActive = true; + super.show(where, heightInLines); + } + + public style(styles: IPeekViewStyles): void { + let options = this.options; + if (styles.headerBackgroundColor) { + options.headerBackgroundColor = styles.headerBackgroundColor; + } + if (styles.primaryHeadingColor) { + options.primaryHeadingColor = styles.primaryHeadingColor; + } + if (styles.secondaryHeadingColor) { + options.secondaryHeadingColor = styles.secondaryHeadingColor; + } + super.style(styles); + } + + protected _applyStyles(): void { + super._applyStyles(); + let options = this.options; + if (this._headElement) { + this._headElement.style.backgroundColor = options.headerBackgroundColor.toString(); + } + if (this._primaryHeading) { + this._primaryHeading.style.color = options.primaryHeadingColor.toString(); + } + if (this._secondaryHeading) { + this._secondaryHeading.style.color = options.secondaryHeadingColor.toString(); + } + if (this._bodyElement) { + this._bodyElement.style.borderColor = options.frameColor.toString(); + } + } + + protected _fillContainer(container: HTMLElement): void { + this.setCssClass('peekview-widget'); + + this._headElement = $('.head').getHTMLElement(); + this._bodyElement = $('.body').getHTMLElement(); + + this._fillHead(this._headElement); + this._fillBody(this._bodyElement); + + container.appendChild(this._headElement); + container.appendChild(this._bodyElement); + } + + protected _fillHead(container: HTMLElement): void { + var titleElement = $('.peekview-title'). + on(dom.EventType.CLICK, e => this._onTitleClick(e)). + appendTo(this._headElement). + getHTMLElement(); + + this._primaryHeading = $('span.filename').appendTo(titleElement).getHTMLElement(); + this._secondaryHeading = $('span.dirname').appendTo(titleElement).getHTMLElement(); + this._metaHeading = $('span.meta').appendTo(titleElement).getHTMLElement(); + + this._actionbarWidget = new ActionBar( + $('.peekview-actions'). + appendTo(this._headElement) + ); + + this._actionbarWidget.push(new Action('peekview.close', nls.localize('label.close', "Close"), 'close-peekview-action', true, () => { + this.dispose(); + return null; + }), { label: false, icon: true }); + } + + protected _onTitleClick(event: MouseEvent): void { + // implement me + } + + public setTitle(primaryHeading: string, secondaryHeading?: string): void { + $(this._primaryHeading).safeInnerHtml(primaryHeading); + this._primaryHeading.setAttribute('aria-label', primaryHeading); + if (secondaryHeading) { + $(this._secondaryHeading).safeInnerHtml(secondaryHeading); + } else { + dom.clearNode(this._secondaryHeading); + } + } + + public setMetaTitle(value: string): void { + if (value) { + $(this._metaHeading).safeInnerHtml(value); + } else { + dom.clearNode(this._metaHeading); + } + } + + protected _fillBody(container: HTMLElement): void { + // implement me + } + + public _doLayout(heightInPixel: number, widthInPixel: number): void { + + if (!this._isShowing && heightInPixel < 0) { + // Looks like the view zone got folded away! + this.dispose(); + this._onDidClose.fire(this); + return; + } + + var headHeight = Math.ceil(this.editor.getConfiguration().lineHeight * 1.2), + bodyHeight = heightInPixel - (headHeight + 2 /* the border-top/bottom width*/); + + this._doLayoutHead(headHeight, widthInPixel); + this._doLayoutBody(bodyHeight, widthInPixel); + } + + protected _doLayoutHead(heightInPixel: number, widthInPixel: number): void { + this._headElement.style.height = strings.format('{0}px', heightInPixel); + this._headElement.style.lineHeight = this._headElement.style.height; + } + + protected _doLayoutBody(heightInPixel: number, widthInPixel: number): void { + this._bodyElement.style.height = strings.format('{0}px', heightInPixel); + } +} diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.css b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.css new file mode 100644 index 0000000000..94a505f5db --- /dev/null +++ b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.css @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +.monaco-editor .zone-widget { + position: absolute; + z-index: 10; +} + + +.monaco-editor .zone-widget .zone-widget-container { + border-top-style: solid; + border-bottom-style: solid; + border-top-width: 0; + border-bottom-width: 0; + position: relative; +} diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts new file mode 100644 index 0000000000..7c69cae455 --- /dev/null +++ b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts @@ -0,0 +1,480 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./zoneWidget'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Widget } from 'vs/base/browser/ui/widget'; +import * as objects from 'vs/base/common/objects'; +import * as dom from 'vs/base/browser/dom'; +import { Sash, Orientation, IHorizontalSashLayoutProvider, ISashEvent } from 'vs/base/browser/ui/sash/sash'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; +import { Color, RGBA } from 'vs/base/common/color'; +import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { IdGenerator } from 'vs/base/common/idGenerator'; +import { ScrollType } from 'vs/editor/common/editorCommon'; + +export interface IOptions { + showFrame?: boolean; + showArrow?: boolean; + frameWidth?: number; + className?: string; + isAccessible?: boolean; + isResizeable?: boolean; + frameColor?: Color; + arrowColor?: Color; +} + +export interface IStyles { + frameColor?: Color; + arrowColor?: Color; +} + +const defaultColor = new Color(new RGBA(0, 122, 204)); + +const defaultOptions: IOptions = { + showArrow: true, + showFrame: true, + className: '', + frameColor: defaultColor, + arrowColor: defaultColor +}; + +const WIDGET_ID = 'vs.editor.contrib.zoneWidget'; + +export class ViewZoneDelegate implements IViewZone { + + public domNode: HTMLElement; + public id: number; + public afterLineNumber: number; + public afterColumn: number; + public heightInLines: number; + + private _onDomNodeTop: (top: number) => void; + private _onComputedHeight: (height: number) => void; + + constructor(domNode: HTMLElement, afterLineNumber: number, afterColumn: number, heightInLines: number, + onDomNodeTop: (top: number) => void, + onComputedHeight: (height: number) => void + ) { + this.domNode = domNode; + this.afterLineNumber = afterLineNumber; + this.afterColumn = afterColumn; + this.heightInLines = heightInLines; + this._onDomNodeTop = onDomNodeTop; + this._onComputedHeight = onComputedHeight; + } + + public onDomNodeTop(top: number): void { + this._onDomNodeTop(top); + } + + public onComputedHeight(height: number): void { + this._onComputedHeight(height); + } +} + +export class OverlayWidgetDelegate implements IOverlayWidget { + + private _id: string; + private _domNode: HTMLElement; + + constructor(id: string, domNode: HTMLElement) { + this._id = id; + this._domNode = domNode; + } + + public getId(): string { + return this._id; + } + + public getDomNode(): HTMLElement { + return this._domNode; + } + + public getPosition(): IOverlayWidgetPosition { + return null; + } +} + +class Arrow { + + private static _IdGenerator = new IdGenerator('.arrow-decoration-'); + + private readonly _ruleName = Arrow._IdGenerator.nextId(); + private _decorations: string[] = []; + private _color: string; + private _height: number; + + constructor( + private readonly _editor: ICodeEditor + ) { + // + } + + dispose(): void { + this.hide(); + dom.removeCSSRulesContainingSelector(this._ruleName); + } + + set color(value: string) { + if (this._color !== value) { + this._color = value; + this._updateStyle(); + } + } + + set height(value: number) { + if (this._height !== value) { + this._height = value; + this._updateStyle(); + } + } + + private _updateStyle(): void { + dom.removeCSSRulesContainingSelector(this._ruleName); + dom.createCSSRule( + `.monaco-editor ${this._ruleName}`, + `border-style: solid; border-color: transparent; border-bottom-color: ${this._color}; border-width: ${this._height}px; bottom: -${this._height}px; margin-left: -${this._height}px; ` + ); + } + + show(where: IPosition): void { + this._decorations = this._editor.deltaDecorations( + this._decorations, + [{ range: Range.fromPositions(where), options: { className: this._ruleName } }] + ); + } + + hide(): void { + this._editor.deltaDecorations(this._decorations, []); + } +} + +export abstract class ZoneWidget extends Widget implements IHorizontalSashLayoutProvider { + + private _arrow: Arrow; + private _overlayWidget: OverlayWidgetDelegate; + private _resizeSash: Sash; + private _positionMarkerId: string[] = []; + + protected _viewZone: ViewZoneDelegate; + protected _disposables: IDisposable[] = []; + + public container: HTMLElement; + public domNode: HTMLElement; + public editor: ICodeEditor; + public options: IOptions; + + + constructor(editor: ICodeEditor, options: IOptions = {}) { + super(); + this.editor = editor; + this.options = objects.clone(options); + objects.mixin(this.options, defaultOptions, false); + this.domNode = document.createElement('div'); + if (!this.options.isAccessible) { + this.domNode.setAttribute('aria-hidden', 'true'); + this.domNode.setAttribute('role', 'presentation'); + } + + this._disposables.push(this.editor.onDidLayoutChange((info: EditorLayoutInfo) => { + const width = this._getWidth(info); + this.domNode.style.width = width + 'px'; + this._onWidth(width); + })); + } + + public dispose(): void { + + dispose(this._disposables); + + if (this._overlayWidget) { + this.editor.removeOverlayWidget(this._overlayWidget); + this._overlayWidget = null; + } + + if (this._viewZone) { + this.editor.changeViewZones(accessor => { + accessor.removeZone(this._viewZone.id); + this._viewZone = null; + }); + } + + this.editor.deltaDecorations(this._positionMarkerId, []); + } + + public create(): void { + + dom.addClass(this.domNode, 'zone-widget'); + dom.addClass(this.domNode, this.options.className); + + this.container = document.createElement('div'); + dom.addClass(this.container, 'zone-widget-container'); + this.domNode.appendChild(this.container); + if (this.options.showArrow) { + this._arrow = new Arrow(this.editor); + this._disposables.push(this._arrow); + } + this._fillContainer(this.container); + this._initSash(); + this._applyStyles(); + } + + public style(styles: IStyles): void { + if (styles.frameColor) { + this.options.frameColor = styles.frameColor; + } + if (styles.arrowColor) { + this.options.arrowColor = styles.arrowColor; + } + this._applyStyles(); + } + + protected _applyStyles(): void { + if (this.container) { + let frameColor = this.options.frameColor.toString(); + this.container.style.borderTopColor = frameColor; + this.container.style.borderBottomColor = frameColor; + } + if (this._arrow) { + let arrowColor = this.options.arrowColor.toString(); + this._arrow.color = arrowColor; + } + } + + private _getWidth(info: EditorLayoutInfo = this.editor.getLayoutInfo()): number { + return info.width - info.minimapWidth - info.verticalScrollbarWidth; + } + + private _onViewZoneTop(top: number): void { + this.domNode.style.top = top + 'px'; + } + + private _onViewZoneHeight(height: number): void { + this.domNode.style.height = `${height}px`; + + let containerHeight = height - this._decoratingElementsHeight(); + this.container.style.height = `${containerHeight}px`; + this._doLayout(containerHeight, this._getWidth()); + + this._resizeSash.layout(); + } + + public get position(): Position { + const [id] = this._positionMarkerId; + if (id) { + return this.editor.getModel().getDecorationRange(id).getStartPosition(); + } + return undefined; + } + + protected _isShowing: boolean = false; + + public show(rangeOrPos: IRange | IPosition, heightInLines: number): void { + const range = Range.isIRange(rangeOrPos) + ? rangeOrPos + : new Range(rangeOrPos.lineNumber, rangeOrPos.column, rangeOrPos.lineNumber, rangeOrPos.column); + + this._isShowing = true; + this._showImpl(range, heightInLines); + this._isShowing = false; + this._positionMarkerId = this.editor.deltaDecorations(this._positionMarkerId, [{ range, options: ModelDecorationOptions.EMPTY }]); + } + + public hide(): void { + if (this._viewZone) { + this.editor.changeViewZones(accessor => { + accessor.removeZone(this._viewZone.id); + }); + this._viewZone = null; + } + if (this._overlayWidget) { + this.editor.removeOverlayWidget(this._overlayWidget); + this._overlayWidget = null; + } + if (this._arrow) { + this._arrow.hide(); + } + } + + private _decoratingElementsHeight(): number { + let lineHeight = this.editor.getConfiguration().lineHeight; + let result = 0; + + if (this.options.showArrow) { + let arrowHeight = Math.round(lineHeight / 3); + result += 2 * arrowHeight; + } + + if (this.options.showFrame) { + let frameThickness = Math.round(lineHeight / 9); + result += 2 * frameThickness; + } + + return result; + } + + private _showImpl(where: IRange, heightInLines: number): void { + const position = { + lineNumber: where.startLineNumber, + column: where.startColumn + }; + + const width = this._getWidth(); + this.domNode.style.width = `${width}px`; + + // Render the widget as zone (rendering) and widget (lifecycle) + const viewZoneDomNode = document.createElement('div'); + viewZoneDomNode.style.overflow = 'hidden'; + const lineHeight = this.editor.getConfiguration().lineHeight; + + // adjust heightInLines to viewport + const maxHeightInLines = (this.editor.getLayoutInfo().height / lineHeight) * .8; + if (heightInLines >= maxHeightInLines) { + heightInLines = maxHeightInLines; + } + + let arrowHeight = 0; + let frameThickness = 0; + + // Render the arrow one 1/3 of an editor line height + if (this.options.showArrow) { + arrowHeight = Math.round(lineHeight / 3); + this._arrow.height = arrowHeight; + this._arrow.show(position); + } + + // Render the frame as 1/9 of an editor line height + if (this.options.showFrame) { + frameThickness = Math.round(lineHeight / 9); + } + + // insert zone widget + this.editor.changeViewZones((accessor: IViewZoneChangeAccessor) => { + if (this._viewZone) { + accessor.removeZone(this._viewZone.id); + } + if (this._overlayWidget) { + this.editor.removeOverlayWidget(this._overlayWidget); + this._overlayWidget = null; + } + this.domNode.style.top = '-1000px'; + this._viewZone = new ViewZoneDelegate( + viewZoneDomNode, + position.lineNumber, + position.column, + heightInLines, + (top: number) => this._onViewZoneTop(top), + (height: number) => this._onViewZoneHeight(height) + ); + this._viewZone.id = accessor.addZone(this._viewZone); + this._overlayWidget = new OverlayWidgetDelegate(WIDGET_ID + this._viewZone.id, this.domNode); + this.editor.addOverlayWidget(this._overlayWidget); + }); + + if (this.options.showFrame) { + const width = this.options.frameWidth ? this.options.frameWidth : frameThickness; + this.container.style.borderTopWidth = width + 'px'; + this.container.style.borderBottomWidth = width + 'px'; + } + + let containerHeight = heightInLines * lineHeight - this._decoratingElementsHeight(); + this.container.style.top = arrowHeight + 'px'; + this.container.style.height = containerHeight + 'px'; + this.container.style.overflow = 'hidden'; + + + this._doLayout(containerHeight, width); + + this.editor.setSelection(where); + + // Reveal the line above or below the zone widget, to get the zone widget in the viewport + const revealLineNumber = Math.min(this.editor.getModel().getLineCount(), Math.max(1, where.endLineNumber + 1)); + this.editor.revealLine(revealLineNumber, ScrollType.Smooth); + } + + protected setCssClass(className: string, classToReplace?: string): void { + if (classToReplace) { + this.container.classList.remove(classToReplace); + } + + dom.addClass(this.container, className); + + } + + protected abstract _fillContainer(container: HTMLElement): void; + + protected _onWidth(widthInPixel: number): void { + // implement in subclass + } + + protected _doLayout(heightInPixel: number, widthInPixel: number): void { + // implement in subclass + } + + protected _relayout(newHeightInLines: number): void { + if (this._viewZone.heightInLines !== newHeightInLines) { + this.editor.changeViewZones(accessor => { + this._viewZone.heightInLines = newHeightInLines; + accessor.layoutZone(this._viewZone.id); + }); + } + } + + // --- sash + + private _initSash(): void { + this._resizeSash = new Sash(this.domNode, this, { orientation: Orientation.HORIZONTAL }); + + if (!this.options.isResizeable) { + this._resizeSash.hide(); + this._resizeSash.disable(); + } + + let data: { startY: number; heightInLines: number; }; + this._disposables.push(this._resizeSash.addListener('start', (e: ISashEvent) => { + if (this._viewZone) { + data = { + startY: e.startY, + heightInLines: this._viewZone.heightInLines, + }; + } + })); + + this._disposables.push(this._resizeSash.addListener('end', () => { + data = undefined; + })); + + this._disposables.push(this._resizeSash.addListener('change', (evt: ISashEvent) => { + if (data) { + let lineDelta = (evt.currentY - data.startY) / this.editor.getConfiguration().lineHeight; + let roundedLineDelta = lineDelta < 0 ? Math.ceil(lineDelta) : Math.floor(lineDelta); + let newHeightInLines = data.heightInLines + roundedLineDelta; + + if (newHeightInLines > 5 && newHeightInLines < 35) { + this._relayout(newHeightInLines); + } + } + })); + } + + getHorizontalSashLeft() { + return 0; + } + + getHorizontalSashTop() { + return parseInt(this.domNode.style.height) - (this._decoratingElementsHeight() / 2); + } + + getHorizontalSashWidth() { + const layoutInfo = this.editor.getLayoutInfo(); + return layoutInfo.width - layoutInfo.minimapWidth; + } +} diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts new file mode 100644 index 0000000000..d253655670 --- /dev/null +++ b/src/vs/editor/editor.all.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/editor/common/controller/coreCommands'; +import 'vs/editor/browser/widget/codeEditorWidget'; +import 'vs/editor/browser/widget/diffEditorWidget'; +import 'vs/editor/browser/widget/diffNavigator'; + +import 'vs/editor/contrib/bracketMatching/common/bracketMatching'; +import 'vs/css!vs/editor/contrib/bracketMatching/browser/bracketMatching'; +import 'vs/editor/contrib/caretOperations/common/caretOperations'; +import 'vs/editor/contrib/caretOperations/common/transpose'; +import 'vs/editor/contrib/clipboard/browser/clipboard'; +import 'vs/editor/contrib/codelens/browser/codelensController'; +import 'vs/editor/contrib/comment/common/comment'; +import 'vs/editor/contrib/contextmenu/browser/contextmenu'; +import 'vs/editor/contrib/cursorUndo/browser/cursorUndo'; +import 'vs/editor/contrib/dnd/browser/dnd'; +import 'vs/editor/contrib/find/browser/find'; +import 'vs/editor/contrib/folding/browser/folding'; +import 'vs/editor/contrib/format/browser/formatActions'; +import 'vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands'; +import 'vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse'; +import 'vs/editor/contrib/gotoError/browser/gotoError'; +import 'vs/editor/contrib/hover/browser/hover'; +import 'vs/editor/contrib/inPlaceReplace/common/inPlaceReplace'; +import 'vs/editor/contrib/linesOperations/common/linesOperations'; +import 'vs/editor/contrib/links/browser/links'; +import 'vs/editor/contrib/multicursor/common/multicursor'; +import 'vs/editor/contrib/parameterHints/browser/parameterHints'; +import 'vs/editor/contrib/quickFix/browser/quickFixCommands'; +import 'vs/editor/contrib/referenceSearch/browser/referenceSearch'; +import 'vs/editor/contrib/rename/browser/rename'; +import 'vs/editor/contrib/smartSelect/common/smartSelect'; +import 'vs/editor/contrib/snippet/browser/snippetController2'; +import 'vs/editor/contrib/suggest/browser/suggestController'; +import 'vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode'; +import 'vs/editor/contrib/wordHighlighter/common/wordHighlighter'; +import 'vs/editor/contrib/wordOperations/common/wordOperations'; +import 'vs/editor/contrib/colorPicker/browser/colorDetector'; \ No newline at end of file diff --git a/src/vs/editor/editor.main.ts b/src/vs/editor/editor.main.ts new file mode 100644 index 0000000000..6d2dfd6497 --- /dev/null +++ b/src/vs/editor/editor.main.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/editor/editor.all'; +import 'vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp'; +import 'vs/editor/standalone/browser/inspectTokens/inspectTokens'; +import 'vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard'; +import 'vs/editor/standalone/browser/quickOpen/quickOutline'; +import 'vs/editor/standalone/browser/quickOpen/gotoLine'; +import 'vs/editor/standalone/browser/quickOpen/quickCommand'; +import 'vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast'; + +import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'; +import { createMonacoEditorAPI } from 'vs/editor/standalone/browser/standaloneEditor'; +import { createMonacoLanguagesAPI } from 'vs/editor/standalone/browser/standaloneLanguages'; +import { EDITOR_DEFAULTS, WrappingIndent } from 'vs/editor/common/config/editorOptions'; + +// Set defaults for standalone editor +(EDITOR_DEFAULTS).wrappingIndent = WrappingIndent.None; +(EDITOR_DEFAULTS.contribInfo).folding = false; +(EDITOR_DEFAULTS.viewInfo).glyphMargin = false; +(EDITOR_DEFAULTS).autoIndent = false; + +let base = createMonacoBaseAPI(); +for (let prop in base) { + if (base.hasOwnProperty(prop)) { + exports[prop] = base[prop]; + } +} +exports.editor = createMonacoEditorAPI(); +exports.languages = createMonacoLanguagesAPI(); + +var global: any = self; +global.monaco = exports; + +if (typeof global.require !== 'undefined' && typeof global.require.config === 'function') { + global.require.config({ + ignoreDuplicateModules: [ + 'vscode-languageserver-types', + 'vscode-languageserver-types/main', + 'vscode-nls', + 'vscode-nls/vscode-nls', + 'jsonc-parser', + 'jsonc-parser/main', + 'vscode-uri', + 'vscode-uri/index' + ] + }); +} diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.css b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.css new file mode 100644 index 0000000000..05d1056f6e --- /dev/null +++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.css @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .accessibilityHelpWidget { + padding: 10px; + vertical-align: middle; + overflow: scroll; +} \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts new file mode 100644 index 0000000000..b1c3f359e9 --- /dev/null +++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts @@ -0,0 +1,391 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./accessibilityHelp'; +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as strings from 'vs/base/common/strings'; +import * as dom from 'vs/base/browser/dom'; +import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICommonCodeEditor, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, CommonEditorRegistry, EditorAction, EditorCommand } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorWidgetBackground, widgetShadow, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import * as platform from 'vs/base/common/platform'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import URI from 'vs/base/common/uri'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as browser from 'vs/base/browser/browser'; +import { IEditorConstructionOptions } from 'vs/editor/standalone/browser/standaloneCodeEditor'; + +const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey('accessibilityHelpWidgetVisible', false); + +@editorContribution +class AccessibilityHelpController extends Disposable + implements IEditorContribution { + private static ID = 'editor.contrib.accessibilityHelpController'; + + public static get(editor: ICommonCodeEditor): AccessibilityHelpController { + return editor.getContribution( + AccessibilityHelpController.ID + ); + } + + private _editor: ICodeEditor; + private _widget: AccessibilityHelpWidget; + + constructor( + editor: ICodeEditor, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(); + + this._editor = editor; + this._widget = this._register( + instantiationService.createInstance(AccessibilityHelpWidget, this._editor) + ); + } + + public getId(): string { + return AccessibilityHelpController.ID; + } + + public show(): void { + this._widget.show(); + } + + public hide(): void { + this._widget.hide(); + } +} + +const nlsNoSelection = nls.localize("noSelection", "No selection"); +const nlsSingleSelectionRange = nls.localize("singleSelectionRange", "Line {0}, Column {1} ({2} selected)"); +const nlsSingleSelection = nls.localize("singleSelection", "Line {0}, Column {1}"); +const nlsMultiSelectionRange = nls.localize("multiSelectionRange", "{0} selections ({1} characters selected)"); +const nlsMultiSelection = nls.localize("multiSelection", "{0} selections"); + +function getSelectionLabel(selections: Selection[], charactersSelected: number): string { + if (!selections || selections.length === 0) { + return nlsNoSelection; + } + + if (selections.length === 1) { + if (charactersSelected) { + return strings.format(nlsSingleSelectionRange, selections[0].positionLineNumber, selections[0].positionColumn, charactersSelected); + } + + return strings.format(nlsSingleSelection, selections[0].positionLineNumber, selections[0].positionColumn); + } + + if (charactersSelected) { + return strings.format(nlsMultiSelectionRange, selections.length, charactersSelected); + } + + if (selections.length > 0) { + return strings.format(nlsMultiSelection, selections.length); + } + + return null; +} + +class AccessibilityHelpWidget extends Widget implements IOverlayWidget { + private static ID = 'editor.contrib.accessibilityHelpWidget'; + private static WIDTH = 500; + private static HEIGHT = 300; + + private _editor: ICodeEditor; + private _domNode: FastDomNode; + private _contentDomNode: FastDomNode; + private _isVisible: boolean; + private _isVisibleKey: IContextKey; + + constructor( + editor: ICodeEditor, + @IContextKeyService private _contextKeyService: IContextKeyService, + @IKeybindingService private _keybindingService: IKeybindingService, + @IOpenerService private _openerService: IOpenerService + ) { + super(); + + this._editor = editor; + this._isVisibleKey = CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE.bindTo( + this._contextKeyService + ); + + this._domNode = createFastDomNode(document.createElement('div')); + this._domNode.setClassName('accessibilityHelpWidget'); + this._domNode.setDisplay('none'); + this._domNode.setAttribute('role', 'dialog'); + this._domNode.setAttribute('aria-hidden', 'true'); + + this._contentDomNode = createFastDomNode(document.createElement('div')); + this._contentDomNode.setAttribute('role', 'document'); + this._domNode.appendChild(this._contentDomNode); + + this._isVisible = false; + + this._register(this._editor.onDidLayoutChange(() => { + if (this._isVisible) { + this._layout(); + } + })); + + // Intentionally not configurable! + this._register(dom.addStandardDisposableListener(this._contentDomNode.domNode, 'keydown', (e) => { + if (!this._isVisible) { + return; + } + + if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_E)) { + alert(nls.localize("emergencyConfOn", "Now changing the setting `accessibilitySupport` to 'on'.")); + + this._editor.updateOptions({ + accessibilitySupport: 'on' + }); + + dom.clearNode(this._contentDomNode.domNode); + this._buildContent(); + this._contentDomNode.domNode.focus(); + + e.preventDefault(); + e.stopPropagation(); + } + + if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_H)) { + alert(nls.localize("openingDocs", "Now opening the Editor Accessibility documentation page.")); + + let url = (this._editor.getRawConfiguration()).accessibilityHelpUrl; + if (typeof url === 'undefined') { + url = 'https://go.microsoft.com/fwlink/?linkid=852450'; + } + this._openerService.open(URI.parse(url)); + + e.preventDefault(); + e.stopPropagation(); + } + })); + + this.onblur(this._contentDomNode.domNode, () => { + this.hide(); + }); + + this._editor.addOverlayWidget(this); + } + + public dispose(): void { + this._editor.removeOverlayWidget(this); + super.dispose(); + } + + public getId(): string { + return AccessibilityHelpWidget.ID; + } + + public getDomNode(): HTMLElement { + return this._domNode.domNode; + } + + public getPosition(): IOverlayWidgetPosition { + return { + preference: null + }; + } + + public show(): void { + if (this._isVisible) { + return; + } + this._isVisible = true; + this._isVisibleKey.set(true); + this._layout(); + this._domNode.setDisplay('block'); + this._domNode.setAttribute('aria-hidden', 'false'); + this._contentDomNode.domNode.tabIndex = 0; + this._buildContent(); + this._contentDomNode.domNode.focus(); + } + + private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string { + let kb = this._keybindingService.lookupKeybinding(commandId); + if (kb) { + return strings.format(msg, kb.getAriaLabel()); + } + return strings.format(noKbMsg, commandId); + } + + private _buildContent() { + let opts = this._editor.getConfiguration(); + + const selections = this._editor.getSelections(); + let charactersSelected = 0; + + if (selections) { + const model = this._editor.getModel(); + if (model) { + selections.forEach((selection) => { + charactersSelected += model.getValueLengthInRange(selection); + }); + } + } + + let text = getSelectionLabel(selections, charactersSelected); + + if (opts.wrappingInfo.inDiffEditor) { + if (opts.readOnly) { + text += nls.localize("readonlyDiffEditor", " in a read-only pane of a diff editor."); + } else { + text += nls.localize("editableDiffEditor", " in a pane of a diff editor."); + } + } else { + if (opts.readOnly) { + text += nls.localize("readonlyEditor", " in a read-only code editor"); + } else { + text += nls.localize("editableEditor", " in a code editor"); + } + } + + switch (opts.accessibilitySupport) { + case platform.AccessibilitySupport.Unknown: + const turnOnMessage = ( + platform.isMacintosh + ? nls.localize("changeConfigToOnMac", "To configure the editor to be optimized for usage with a Screen Reader press Command+E now.") + : nls.localize("changeConfigToOnWinLinux", "To configure the editor to be optimized for usage with a Screen Reader press Control+E now.") + ); + text += '\n\n - ' + turnOnMessage; + break; + case platform.AccessibilitySupport.Enabled: + text += '\n\n - ' + nls.localize("auto_on", "The editor is configured to be optimized for usage with a Screen Reader."); + break; + case platform.AccessibilitySupport.Disabled: + text += '\n\n - ' + nls.localize("auto_off", "The editor is configured to never be optimized for usage with a Screen Reader, which is not the case at this time."); + text += ' ' + turnOnMessage; + break; + } + + const NLS_TAB_FOCUS_MODE_ON = nls.localize("tabFocusModeOnMsg", "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}."); + const NLS_TAB_FOCUS_MODE_ON_NO_KB = nls.localize("tabFocusModeOnMsgNoKb", "Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding."); + const NLS_TAB_FOCUS_MODE_OFF = nls.localize("tabFocusModeOffMsg", "Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}."); + const NLS_TAB_FOCUS_MODE_OFF_NO_KB = nls.localize("tabFocusModeOffMsgNoKb", "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding."); + + if (opts.tabFocusMode) { + text += '\n\n - ' + this._descriptionForCommand(ToggleTabFocusModeAction.ID, NLS_TAB_FOCUS_MODE_ON, NLS_TAB_FOCUS_MODE_ON_NO_KB); + } else { + text += '\n\n - ' + this._descriptionForCommand(ToggleTabFocusModeAction.ID, NLS_TAB_FOCUS_MODE_OFF, NLS_TAB_FOCUS_MODE_OFF_NO_KB); + } + + const openDocMessage = ( + platform.isMacintosh + ? nls.localize("openDocMac", "Press Command+H now to open a browser window with more information related to editor accessibility.") + : nls.localize("openDocWinLinux", "Press Control+H now to open a browser window with more information related to editor accessibility.") + ); + + text += '\n\n - ' + openDocMessage; + + text += '\n\n' + nls.localize("outroMsg", "You can dismiss this tooltip and return to the editor by pressing Escape or Shift+Escape."); + + this._contentDomNode.domNode.appendChild(renderFormattedText(text)); + // Per https://www.w3.org/TR/wai-aria/roles#document, Authors SHOULD provide a title or label for documents + this._contentDomNode.domNode.setAttribute('aria-label', text); + } + + public hide(): void { + if (!this._isVisible) { + return; + } + this._isVisible = false; + this._isVisibleKey.reset(); + this._domNode.setDisplay('none'); + this._domNode.setAttribute('aria-hidden', 'true'); + this._contentDomNode.domNode.tabIndex = -1; + dom.clearNode(this._contentDomNode.domNode); + + this._editor.focus(); + } + + private _layout(): void { + let editorLayout = this._editor.getLayoutInfo(); + + let w = Math.max(5, Math.min(AccessibilityHelpWidget.WIDTH, editorLayout.width - 40)); + let h = Math.max(5, Math.min(AccessibilityHelpWidget.HEIGHT, editorLayout.height - 40)); + + this._domNode.setWidth(w); + this._domNode.setHeight(h); + + let top = Math.round((editorLayout.height - h) / 2); + this._domNode.setTop(top); + + let left = Math.round((editorLayout.width - w) / 2); + this._domNode.setLeft(left); + } +} + +@editorAction +class ShowAccessibilityHelpAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.showAccessibilityHelp', + label: nls.localize("ShowAccessibilityHelpAction", "Show Accessibility Help"), + alias: 'Show Accessibility Help', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: (browser.isIE ? KeyMod.CtrlCmd | KeyCode.F1 : KeyMod.Alt | KeyCode.F1) + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let controller = AccessibilityHelpController.get(editor); + if (controller) { + controller.show(); + } + } +} + +const AccessibilityHelpCommand = EditorCommand.bindToContribution(AccessibilityHelpController.get); + +CommonEditorRegistry.registerEditorCommand( + new AccessibilityHelpCommand({ + id: 'closeAccessibilityHelp', + precondition: CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE, + handler: x => x.hide(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(100), + kbExpr: EditorContextKeys.focus, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } + }) +); + +registerThemingParticipant((theme, collector) => { + let widgetBackground = theme.getColor(editorWidgetBackground); + if (widgetBackground) { + collector.addRule(`.monaco-editor .accessibilityHelpWidget { background-color: ${widgetBackground}; }`); + } + + let widgetShadowColor = theme.getColor(widgetShadow); + if (widgetShadowColor) { + collector.addRule(`.monaco-editor .accessibilityHelpWidget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + } + + let hcBorder = theme.getColor(contrastBorder); + if (hcBorder) { + collector.addRule(`.monaco-editor .accessibilityHelpWidget { border: 2px solid ${hcBorder}; }`); + } +}); diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts new file mode 100644 index 0000000000..bc9b8eb7a6 --- /dev/null +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -0,0 +1,192 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { ColorId, MetadataConsts, FontStyle, TokenizationRegistry, ITokenizationSupport } from 'vs/editor/common/modes'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { renderViewLine2 as renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import * as strings from 'vs/base/common/strings'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; + +export interface IColorizerOptions { + tabSize?: number; +} + +export interface IColorizerElementOptions extends IColorizerOptions { + theme?: string; + mimeType?: string; +} + +export class Colorizer { + + public static colorizeElement(themeService: IStandaloneThemeService, modeService: IModeService, domNode: HTMLElement, options: IColorizerElementOptions): TPromise { + options = options || {}; + let theme = options.theme || 'vs'; + let mimeType = options.mimeType || domNode.getAttribute('lang') || domNode.getAttribute('data-lang'); + if (!mimeType) { + console.error('Mode not detected'); + return undefined; + } + + themeService.setTheme(theme); + + let text = domNode.firstChild.nodeValue; + domNode.className += 'monaco-editor ' + theme; + let render = (str: string) => { + domNode.innerHTML = str; + }; + return this.colorize(modeService, text, mimeType, options).then(render, (err) => console.error(err), render); + } + + private static _tokenizationSupportChangedPromise(language: string): TPromise { + let listener: IDisposable = null; + let stopListening = () => { + if (listener) { + listener.dispose(); + listener = null; + } + }; + + return new TPromise((c, e, p) => { + listener = TokenizationRegistry.onDidChange((e) => { + if (e.changedLanguages.indexOf(language) >= 0) { + stopListening(); + c(void 0); + } + }); + }, stopListening); + } + + public static colorize(modeService: IModeService, text: string, mimeType: string, options: IColorizerOptions): TPromise { + if (strings.startsWithUTF8BOM(text)) { + text = text.substr(1); + } + let lines = text.split(/\r\n|\r|\n/); + let language = modeService.getModeId(mimeType); + + options = options || {}; + if (typeof options.tabSize === 'undefined') { + options.tabSize = 4; + } + + // Send out the event to create the mode + modeService.getOrCreateMode(language); + + let tokenizationSupport = TokenizationRegistry.get(language); + if (tokenizationSupport) { + return TPromise.as(_colorize(lines, options.tabSize, tokenizationSupport)); + } + + // wait 500ms for mode to load, then give up + return TPromise.any([this._tokenizationSupportChangedPromise(language), TPromise.timeout(500)]).then(_ => { + let tokenizationSupport = TokenizationRegistry.get(language); + if (tokenizationSupport) { + return _colorize(lines, options.tabSize, tokenizationSupport); + } + return _fakeColorize(lines, options.tabSize); + }); + } + + public static colorizeLine(line: string, mightContainRTL: boolean, tokens: ViewLineToken[], tabSize: number = 4): string { + let renderResult = renderViewLine(new RenderLineInput( + false, + line, + mightContainRTL, + 0, + tokens, + [], + tabSize, + 0, + -1, + 'none', + false, + false + )); + return renderResult.html; + } + + public static colorizeModelLine(model: IModel, lineNumber: number, tabSize: number = 4): string { + let content = model.getLineContent(lineNumber); + model.forceTokenization(lineNumber); + let tokens = model.getLineTokens(lineNumber); + let inflatedTokens = tokens.inflate(); + return this.colorizeLine(content, model.mightContainRTL(), inflatedTokens, tabSize); + } +} + +function _colorize(lines: string[], tabSize: number, tokenizationSupport: ITokenizationSupport): string { + return _actualColorize(lines, tabSize, tokenizationSupport); +} + +function _fakeColorize(lines: string[], tabSize: number): string { + let html: string[] = []; + + const defaultMetadata = ( + (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) + | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) + | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) + ) >>> 0; + + for (let i = 0, length = lines.length; i < length; i++) { + let line = lines[i]; + + let renderResult = renderViewLine(new RenderLineInput( + false, + line, + false, + 0, + [new ViewLineToken(line.length, defaultMetadata)], + [], + tabSize, + 0, + -1, + 'none', + false, + false + )); + + html = html.concat(renderResult.html); + html.push('
'); + } + + return html.join(''); +} + +function _actualColorize(lines: string[], tabSize: number, tokenizationSupport: ITokenizationSupport): string { + let html: string[] = []; + let state = tokenizationSupport.getInitialState(); + + for (let i = 0, length = lines.length; i < length; i++) { + let line = lines[i]; + let tokenizeResult = tokenizationSupport.tokenize2(line, state, 0); + let lineTokens = new LineTokens(tokenizeResult.tokens, line); + let renderResult = renderViewLine(new RenderLineInput( + false, + line, + true/* check for RTL */, + 0, + lineTokens.inflate(), + [], + tabSize, + 0, + -1, + 'none', + false, + false + )); + + html = html.concat(renderResult.html); + html.push('
'); + + state = tokenizeResult.endState; + } + + return html.join(''); +} diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.css b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.css new file mode 100644 index 0000000000..2c47d247c8 --- /dev/null +++ b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.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. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .iPadShowKeyboard { + width: 58px; + min-width: 0; + height: 36px; + min-height: 0; + margin: 0; + padding: 0; + position: absolute; + resize: none; + overflow: hidden; + background: url('keyboard.svg') center center no-repeat; + border: 4px solid #F6F6F6; + border-radius: 4px; +} + +.monaco-editor.vs-dark .iPadShowKeyboard { + background: url('keyboard-inverse.svg') center center no-repeat; + border: 4px solid #252526; +} \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts new file mode 100644 index 0000000000..01531fd2d9 --- /dev/null +++ b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./iPadShowKeyboard'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as browser from 'vs/base/browser/browser'; +import * as dom from 'vs/base/browser/dom'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; + +@editorContribution +export class IPadShowKeyboard implements IEditorContribution { + + private static ID = 'editor.contrib.iPadShowKeyboard'; + + private editor: ICodeEditor; + private widget: ShowKeyboardWidget; + private toDispose: IDisposable[]; + + constructor(editor: ICodeEditor) { + this.editor = editor; + this.toDispose = []; + if (browser.isIPad) { + this.toDispose.push(editor.onDidChangeConfiguration(() => this.update())); + this.update(); + } + } + + private update(): void { + var hasWidget = (!!this.widget); + var shouldHaveWidget = (!this.editor.getConfiguration().readOnly); + + if (!hasWidget && shouldHaveWidget) { + + this.widget = new ShowKeyboardWidget(this.editor); + + } else if (hasWidget && !shouldHaveWidget) { + + this.widget.dispose(); + this.widget = null; + + } + } + + public getId(): string { + return IPadShowKeyboard.ID; + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + if (this.widget) { + this.widget.dispose(); + this.widget = null; + } + } +} + +class ShowKeyboardWidget implements IOverlayWidget { + + private static ID = 'editor.contrib.ShowKeyboardWidget'; + + private editor: ICodeEditor; + + private _domNode: HTMLElement; + private _toDispose: IDisposable[]; + + constructor(editor: ICodeEditor) { + this.editor = editor; + this._domNode = document.createElement('textarea'); + this._domNode.className = 'iPadShowKeyboard'; + + this._toDispose = []; + this._toDispose.push(dom.addDisposableListener(this._domNode, 'touchstart', (e) => { + this.editor.focus(); + })); + this._toDispose.push(dom.addDisposableListener(this._domNode, 'focus', (e) => { + this.editor.focus(); + })); + + this.editor.addOverlayWidget(this); + } + + public dispose(): void { + this.editor.removeOverlayWidget(this); + this._toDispose = dispose(this._toDispose); + } + + // ----- IOverlayWidget API + + public getId(): string { + return ShowKeyboardWidget.ID; + } + + public getDomNode(): HTMLElement { + return this._domNode; + } + + public getPosition(): IOverlayWidgetPosition { + return { + preference: OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER + }; + } +} diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-inverse.svg b/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-inverse.svg new file mode 100644 index 0000000000..40bfc47424 --- /dev/null +++ b/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard.svg b/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard.svg new file mode 100644 index 0000000000..bd13224272 --- /dev/null +++ b/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.css b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.css new file mode 100644 index 0000000000..a7bc05744d --- /dev/null +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.css @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .tokens-inspect-widget { + z-index: 50; + -webkit-user-select: text; + -ms-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -o-user-select: text; + user-select: text; + padding: 10px; +} + +.tokens-inspect-separator { + height: 1px; + border: 0; +} + +.monaco-editor .tokens-inspect-widget .tm-token { + font-family: monospace; +} + +.monaco-editor .tokens-inspect-widget .tm-token-length { + font-weight: normal; + font-size: 60%; + float: right; +} + +.monaco-editor .tokens-inspect-widget .tm-metadata-table { + width: 100%; +} + +.monaco-editor .tokens-inspect-widget .tm-metadata-value { + font-family: monospace; + text-align: right; +} + +.monaco-editor .tokens-inspect-widget .tm-token-type { + font-family: monospace; +} diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts new file mode 100644 index 0000000000..09c4565e45 --- /dev/null +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -0,0 +1,348 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./inspectTokens'; +import * as nls from 'vs/nls'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { escape } from 'vs/base/common/strings'; +import { Position } from 'vs/editor/common/core/position'; +import { ICommonCodeEditor, IEditorContribution, IModel } from 'vs/editor/common/editorCommon'; +import { editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor, ContentWidgetPositionPreference, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding'; +import { TokenizationRegistry, LanguageIdentifier, FontStyle, StandardTokenType, ITokenizationSupport, IState } from 'vs/editor/common/modes'; +import { CharCode } from 'vs/base/common/charCode'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { NULL_STATE, nullTokenize, nullTokenize2 } from 'vs/editor/common/modes/nullMode'; +import { Token } from 'vs/editor/common/core/token'; +import { Color } from 'vs/base/common/color'; +import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; + + +@editorContribution +class InspectTokensController extends Disposable implements IEditorContribution { + + private static ID = 'editor.contrib.inspectTokens'; + + public static get(editor: ICommonCodeEditor): InspectTokensController { + return editor.getContribution(InspectTokensController.ID); + } + + private _editor: ICodeEditor; + private _standaloneThemeService: IStandaloneThemeService; + private _modeService: IModeService; + private _widget: InspectTokensWidget; + + constructor( + editor: ICodeEditor, + @IStandaloneThemeService standaloneColorService: IStandaloneThemeService, + @IModeService modeService: IModeService + ) { + super(); + this._editor = editor; + this._standaloneThemeService = standaloneColorService; + this._modeService = modeService; + this._widget = null; + + this._register(this._editor.onDidChangeModel((e) => this.stop())); + this._register(this._editor.onDidChangeModelLanguage((e) => this.stop())); + this._register(TokenizationRegistry.onDidChange((e) => this.stop())); + } + + public getId(): string { + return InspectTokensController.ID; + } + + public dispose(): void { + this.stop(); + super.dispose(); + } + + public launch(): void { + if (this._widget) { + return; + } + if (!this._editor.getModel()) { + return; + } + this._widget = new InspectTokensWidget(this._editor, this._standaloneThemeService, this._modeService); + } + + public stop(): void { + if (this._widget) { + this._widget.dispose(); + this._widget = null; + } + } +} + +@editorAction +class InspectTokens extends EditorAction { + + constructor() { + super({ + id: 'editor.action.inspectTokens', + label: nls.localize('inspectTokens', "Developer: Inspect Tokens"), + alias: 'Developer: Inspect Tokens', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let controller = InspectTokensController.get(editor); + if (controller) { + controller.launch(); + } + } +} + +interface ICompleteLineTokenization { + startState: IState; + tokens1: Token[]; + tokens2: Uint32Array; + endState: IState; +} + +interface IDecodedMetadata { + languageIdentifier: LanguageIdentifier; + tokenType: StandardTokenType; + fontStyle: FontStyle; + foreground: Color; + background: Color; +} + +function renderTokenText(tokenText: string): string { + let result: string = ''; + for (let charIndex = 0, len = tokenText.length; charIndex < len; charIndex++) { + let charCode = tokenText.charCodeAt(charIndex); + switch (charCode) { + case CharCode.Tab: + result += '→'; + break; + + case CharCode.Space: + result += '·'; + break; + + case CharCode.LessThan: + result += '<'; + break; + + case CharCode.GreaterThan: + result += '>'; + break; + + case CharCode.Ampersand: + result += '&'; + break; + + default: + result += String.fromCharCode(charCode); + } + } + return result; +} + +function getSafeTokenizationSupport(languageIdentifier: LanguageIdentifier): ITokenizationSupport { + let tokenizationSupport = TokenizationRegistry.get(languageIdentifier.language); + if (tokenizationSupport) { + return tokenizationSupport; + } + return { + getInitialState: () => NULL_STATE, + tokenize: (line: string, state: IState, deltaOffset: number) => nullTokenize(languageIdentifier.language, line, state, deltaOffset), + tokenize2: (line: string, state: IState, deltaOffset: number) => nullTokenize2(languageIdentifier.id, line, state, deltaOffset) + }; +} + +class InspectTokensWidget extends Disposable implements IContentWidget { + + private static _ID = 'editor.contrib.inspectTokensWidget'; + + // Editor.IContentWidget.allowEditorOverflow + public allowEditorOverflow = true; + + private _editor: ICodeEditor; + private _standaloneThemeService: IStandaloneThemeService; + private _modeService: IModeService; + private _tokenizationSupport: ITokenizationSupport; + private _model: IModel; + private _domNode: HTMLElement; + + constructor( + editor: ICodeEditor, + standaloneThemeService: IStandaloneThemeService, + modeService: IModeService + ) { + super(); + this._editor = editor; + this._standaloneThemeService = standaloneThemeService; + this._modeService = modeService; + this._model = this._editor.getModel(); + this._domNode = document.createElement('div'); + this._domNode.className = 'tokens-inspect-widget'; + this._tokenizationSupport = getSafeTokenizationSupport(this._model.getLanguageIdentifier()); + this._compute(this._editor.getPosition()); + this._register(this._editor.onDidChangeCursorPosition((e) => this._compute(this._editor.getPosition()))); + this._editor.addContentWidget(this); + } + + public dispose(): void { + this._editor.removeContentWidget(this); + super.dispose(); + } + + public getId(): string { + return InspectTokensWidget._ID; + } + + private _compute(position: Position): void { + let data = this._getTokensAtLine(position.lineNumber); + + let token1Index = 0; + for (let i = data.tokens1.length - 1; i >= 0; i--) { + let t = data.tokens1[i]; + if (position.column - 1 >= t.offset) { + token1Index = i; + break; + } + } + + let token2Index = 0; + for (let i = (data.tokens2.length >>> 1); i >= 0; i--) { + if (position.column - 1 >= data.tokens2[(i << 1)]) { + token2Index = i; + break; + } + } + + let result = ''; + + let lineContent = this._model.getLineContent(position.lineNumber); + let tokenText = ''; + if (token1Index < data.tokens1.length) { + let tokenStartIndex = data.tokens1[token1Index].offset; + let tokenEndIndex = token1Index + 1 < data.tokens1.length ? data.tokens1[token1Index + 1].offset : lineContent.length; + tokenText = lineContent.substring(tokenStartIndex, tokenEndIndex); + } + result += `

${renderTokenText(tokenText)}(${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'})

`; + + result += `
`; + + let metadata = this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]); + result += ``; + result += ``; + result += ``; + result += ``; + result += ``; + result += ``; + result += ``; + + result += `
`; + + if (token1Index < data.tokens1.length) { + result += `${escape(data.tokens1[token1Index].type)}`; + } + + this._domNode.innerHTML = result; + this._editor.layoutContentWidget(this); + } + + private _decodeMetadata(metadata: number): IDecodedMetadata { + let colorMap = TokenizationRegistry.getColorMap(); + let languageId = TokenMetadata.getLanguageId(metadata); + let tokenType = TokenMetadata.getTokenType(metadata); + let fontStyle = TokenMetadata.getFontStyle(metadata); + let foreground = TokenMetadata.getForeground(metadata); + let background = TokenMetadata.getBackground(metadata); + return { + languageIdentifier: this._modeService.getLanguageIdentifier(languageId), + tokenType: tokenType, + fontStyle: fontStyle, + foreground: colorMap[foreground], + background: colorMap[background] + }; + } + + private _tokenTypeToString(tokenType: StandardTokenType): string { + switch (tokenType) { + case StandardTokenType.Other: return 'Other'; + case StandardTokenType.Comment: return 'Comment'; + case StandardTokenType.String: return 'String'; + case StandardTokenType.RegEx: return 'RegEx'; + } + return '??'; + } + + private _fontStyleToString(fontStyle: FontStyle): string { + let r = ''; + if (fontStyle & FontStyle.Italic) { + r += 'italic '; + } + if (fontStyle & FontStyle.Bold) { + r += 'bold '; + } + if (fontStyle & FontStyle.Underline) { + r += 'underline '; + } + if (r.length === 0) { + r = '---'; + } + return r; + } + + private _getTokensAtLine(lineNumber: number): ICompleteLineTokenization { + let stateBeforeLine = this._getStateBeforeLine(lineNumber); + + let tokenizationResult1 = this._tokenizationSupport.tokenize(this._model.getLineContent(lineNumber), stateBeforeLine, 0); + let tokenizationResult2 = this._tokenizationSupport.tokenize2(this._model.getLineContent(lineNumber), stateBeforeLine, 0); + + return { + startState: stateBeforeLine, + tokens1: tokenizationResult1.tokens, + tokens2: tokenizationResult2.tokens, + endState: tokenizationResult1.endState + }; + } + + private _getStateBeforeLine(lineNumber: number): IState { + let state: IState = this._tokenizationSupport.getInitialState(); + + for (let i = 1; i < lineNumber; i++) { + let tokenizationResult = this._tokenizationSupport.tokenize(this._model.getLineContent(i), state, 0); + state = tokenizationResult.endState; + } + + return state; + } + + public getDomNode(): HTMLElement { + return this._domNode; + } + + public getPosition(): IContentWidgetPosition { + return { + position: this._editor.getPosition(), + preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] + }; + } +} + +registerThemingParticipant((theme, collector) => { + let border = theme.getColor(editorHoverBorder); + if (border) { + let borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1; + collector.addRule(`.monaco-editor .tokens-inspect-widget { border: ${borderWidth}px solid ${border}; }`); + collector.addRule(`.monaco-editor .tokens-inspect-widget .tokens-inspect-separator { background-color: ${border}; }`); + } + let background = theme.getColor(editorHoverBackground); + if (background) { + collector.addRule(`.monaco-editor .tokens-inspect-widget { background-color: ${background}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts new file mode 100644 index 0000000000..2e33f54085 --- /dev/null +++ b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts @@ -0,0 +1,173 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { QuickOpenEditorWidget } from './quickOpenEditorWidget'; +import { Selection } from 'vs/editor/common/core/selection'; +import { IActionOptions, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { Range } from 'vs/editor/common/core/range'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; + +export interface IQuickOpenControllerOpts { + inputAriaLabel: string; + getModel(value: string): QuickOpenModel; + getAutoFocus(searchValue: string): IAutoFocus; +} + +@editorContribution +export class QuickOpenController implements editorCommon.IEditorContribution { + + private static ID = 'editor.controller.quickOpenController'; + + public static get(editor: editorCommon.ICommonCodeEditor): QuickOpenController { + return editor.getContribution(QuickOpenController.ID); + } + + private editor: ICodeEditor; + private widget: QuickOpenEditorWidget; + private rangeHighlightDecorationId: string; + private lastKnownEditorSelection: Selection; + + constructor(editor: ICodeEditor, @IThemeService private themeService: IThemeService) { + this.editor = editor; + } + + public getId(): string { + return QuickOpenController.ID; + } + + public dispose(): void { + // Dispose widget + if (this.widget) { + this.widget.destroy(); + this.widget = null; + } + } + + public run(opts: IQuickOpenControllerOpts): void { + if (this.widget) { + this.widget.destroy(); + this.widget = null; + } + + // Create goto line widget + let onClose = (canceled: boolean) => { + // Clear Highlight Decorations if present + this.clearDecorations(); + + // Restore selection if canceled + if (canceled && this.lastKnownEditorSelection) { + this.editor.setSelection(this.lastKnownEditorSelection); + this.editor.revealRangeInCenterIfOutsideViewport(this.lastKnownEditorSelection, editorCommon.ScrollType.Smooth); + } + + this.lastKnownEditorSelection = null; + this.editor.focus(); + }; + + this.widget = new QuickOpenEditorWidget( + this.editor, + () => onClose(false), + () => onClose(true), + (value: string) => { + this.widget.setInput(opts.getModel(value), opts.getAutoFocus(value)); + }, + { + inputAriaLabel: opts.inputAriaLabel + }, + this.themeService + ); + + // Remember selection to be able to restore on cancel + if (!this.lastKnownEditorSelection) { + this.lastKnownEditorSelection = this.editor.getSelection(); + } + + // Show + this.widget.show(''); + } + + private static _RANGE_HIGHLIGHT_DECORATION = ModelDecorationOptions.register({ + className: 'rangeHighlight', + isWholeLine: true + }); + + public decorateLine(range: Range, editor: ICodeEditor): void { + editor.changeDecorations((changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => { + var oldDecorations: string[] = []; + if (this.rangeHighlightDecorationId) { + oldDecorations.push(this.rangeHighlightDecorationId); + this.rangeHighlightDecorationId = null; + } + + var newDecorations: editorCommon.IModelDeltaDecoration[] = [ + { + range: range, + options: QuickOpenController._RANGE_HIGHLIGHT_DECORATION + } + ]; + + var decorations = changeAccessor.deltaDecorations(oldDecorations, newDecorations); + this.rangeHighlightDecorationId = decorations[0]; + }); + } + + public clearDecorations(): void { + if (this.rangeHighlightDecorationId) { + this.editor.changeDecorations((changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => { + changeAccessor.deltaDecorations([this.rangeHighlightDecorationId], []); + this.rangeHighlightDecorationId = null; + }); + } + } +} + +export interface IQuickOpenOpts { + /** + * provide the quick open model for the given search value. + */ + getModel(value: string): QuickOpenModel; + + /** + * provide the quick open auto focus mode for the given search value. + */ + getAutoFocus(searchValue: string): IAutoFocus; +} + +/** + * Base class for providing quick open in the editor. + */ +export abstract class BaseEditorQuickOpenAction extends EditorAction { + + private _inputAriaLabel: string; + + constructor(inputAriaLabel: string, opts: IActionOptions) { + super(opts); + this._inputAriaLabel = inputAriaLabel; + } + + protected getController(editor: editorCommon.ICommonCodeEditor): QuickOpenController { + return QuickOpenController.get(editor); + } + + protected _show(controller: QuickOpenController, opts: IQuickOpenOpts): void { + controller.run({ + inputAriaLabel: this._inputAriaLabel, + getModel: (value: string): QuickOpenModel => opts.getModel(value), + getAutoFocus: (searchValue: string): IAutoFocus => opts.getAutoFocus(searchValue) + }); + } +} + +export interface IDecorator { + decorateLine(range: Range, editor: editorCommon.IEditor): void; + clearDecorations(): void; +} diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.css b/src/vs/editor/standalone/browser/quickOpen/gotoLine.css new file mode 100644 index 0000000000..824f1b47a4 --- /dev/null +++ b/src/vs/editor/standalone/browser/quickOpen/gotoLine.css @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.quick-open-widget { + font-size: 13px; +} \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts new file mode 100644 index 0000000000..f5f24f5297 --- /dev/null +++ b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./gotoLine'; +import * as nls from 'vs/nls'; +import { IContext, QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IAutoFocus, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { BaseEditorQuickOpenAction, IDecorator } from './editorQuickOpen'; +import { editorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; + +interface ParseResult { + position: Position; + isValid: boolean; + label: string; +} + +export class GotoLineEntry extends QuickOpenEntry { + + private _parseResult: ParseResult; + private decorator: IDecorator; + private editor: editorCommon.IEditor; + + constructor(line: string, editor: editorCommon.IEditor, decorator: IDecorator) { + super(); + + this.editor = editor; + this.decorator = decorator; + this._parseResult = this._parseInput(line); + } + + + private _parseInput(line: string): ParseResult { + + let numbers = line.split(',').map(part => parseInt(part, 10)).filter(part => !isNaN(part)), + position: Position; + + if (numbers.length === 0) { + position = new Position(-1, -1); + } else if (numbers.length === 1) { + position = new Position(numbers[0], 1); + } else { + position = new Position(numbers[0], numbers[1]); + } + + let model: editorCommon.IModel; + if (editorCommon.isCommonCodeEditor(this.editor)) { + model = this.editor.getModel(); + } else { + model = (this.editor).getModel().modified; + } + + let isValid = model.validatePosition(position).equals(position), + label: string; + + if (isValid) { + if (position.column && position.column > 1) { + label = nls.localize('gotoLineLabelValidLineAndColumn', "Go to line {0} and character {1}", position.lineNumber, position.column); + } else { + label = nls.localize('gotoLineLabelValidLine', "Go to line {0}", position.lineNumber, position.column); + } + } else if (position.lineNumber < 1 || position.lineNumber > model.getLineCount()) { + label = nls.localize('gotoLineLabelEmptyWithLineLimit', "Type a line number between 1 and {0} to navigate to", model.getLineCount()); + } else { + label = nls.localize('gotoLineLabelEmptyWithLineAndColumnLimit', "Type a character between 1 and {0} to navigate to", model.getLineMaxColumn(position.lineNumber)); + } + + return { + position: position, + isValid: isValid, + label: label + }; + } + + public getLabel(): string { + return this._parseResult.label; + } + + public getAriaLabel(): string { + return nls.localize('gotoLineAriaLabel', "Go to line {0}", this._parseResult.label); + } + + public run(mode: Mode, context: IContext): boolean { + if (mode === Mode.OPEN) { + return this.runOpen(); + } + + return this.runPreview(); + } + + public runOpen(): boolean { + + // No-op if range is not valid + if (!this._parseResult.isValid) { + return false; + } + + // Apply selection and focus + let range = this.toSelection(); + (this.editor).setSelection(range); + (this.editor).revealRangeInCenter(range, editorCommon.ScrollType.Smooth); + this.editor.focus(); + + return true; + } + + public runPreview(): boolean { + + // No-op if range is not valid + if (!this._parseResult.isValid) { + this.decorator.clearDecorations(); + return false; + } + + // Select Line Position + let range = this.toSelection(); + this.editor.revealRangeInCenter(range, editorCommon.ScrollType.Smooth); + + // Decorate if possible + this.decorator.decorateLine(range, this.editor); + + return false; + } + + private toSelection(): Range { + return new Range( + this._parseResult.position.lineNumber, + this._parseResult.position.column, + this._parseResult.position.lineNumber, + this._parseResult.position.column + ); + } +} + +@editorAction +export class GotoLineAction extends BaseEditorQuickOpenAction { + + constructor() { + super(nls.localize('gotoLineActionInput', "Type a line number, followed by an optional colon and a character number to navigate to"), { + id: 'editor.action.gotoLine', + label: nls.localize('GotoLineAction.label', "Go to Line..."), + alias: 'Go to Line...', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyCode.KEY_G, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G } + } + }); + } + + public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + this._show(this.getController(editor), { + getModel: (value: string): QuickOpenModel => { + return new QuickOpenModel([new GotoLineEntry(value, editor, this.getController(editor))]); + }, + + getAutoFocus: (searchValue: string): IAutoFocus => { + return { + autoFocusFirstEntry: searchValue.length > 0 + }; + } + }); + } +} diff --git a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts new file mode 100644 index 0000000000..baa9060a89 --- /dev/null +++ b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts @@ -0,0 +1,135 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { matchesFuzzy } from 'vs/base/common/filters'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IContext, IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IAutoFocus, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IEditorAction, ICommonCodeEditor, IEditor } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { BaseEditorQuickOpenAction } from './editorQuickOpen'; +import { editorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import * as browser from 'vs/base/browser/browser'; + +export class EditorActionCommandEntry extends QuickOpenEntryGroup { + private key: string; + private action: IEditorAction; + private editor: IEditor; + + constructor(key: string, highlights: IHighlight[], action: IEditorAction, editor: IEditor) { + super(); + + this.key = key; + this.setHighlights(highlights); + this.action = action; + this.editor = editor; + } + + public getLabel(): string { + return this.action.label; + } + + public getAriaLabel(): string { + return nls.localize('ariaLabelEntry', "{0}, commands", this.getLabel()); + } + + public getGroupLabel(): string { + return this.key; + } + + public run(mode: Mode, context: IContext): boolean { + if (mode === Mode.OPEN) { + + // Use a timeout to give the quick open widget a chance to close itself first + TPromise.timeout(50).done(() => { + + // Some actions are enabled only when editor has focus + this.editor.focus(); + + try { + let promise = this.action.run() || TPromise.as(null); + promise.done(null, onUnexpectedError); + } catch (error) { + onUnexpectedError(error); + } + }, onUnexpectedError); + + return true; + } + + return false; + } +} + +@editorAction +export class QuickCommandAction extends BaseEditorQuickOpenAction { + + constructor() { + super(nls.localize('quickCommandActionInput', "Type the name of an action you want to execute"), { + id: 'editor.action.quickCommand', + label: nls.localize('QuickCommandAction.label', "Command Palette"), + alias: 'Command Palette', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: (browser.isIE ? KeyMod.Alt | KeyCode.F1 : KeyCode.F1) + }, + menuOpts: { + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const keybindingService = accessor.get(IKeybindingService); + + this._show(this.getController(editor), { + getModel: (value: string): QuickOpenModel => { + return new QuickOpenModel(this._editorActionsToEntries(keybindingService, editor, value)); + }, + + getAutoFocus: (searchValue: string): IAutoFocus => { + return { + autoFocusFirstEntry: true, + autoFocusPrefixMatch: searchValue + }; + } + }); + } + + private _sort(elementA: QuickOpenEntryGroup, elementB: QuickOpenEntryGroup): number { + let elementAName = elementA.getLabel().toLowerCase(); + let elementBName = elementB.getLabel().toLowerCase(); + + return elementAName.localeCompare(elementBName); + } + + private _editorActionsToEntries(keybindingService: IKeybindingService, editor: ICommonCodeEditor, searchValue: string): EditorActionCommandEntry[] { + let actions: IEditorAction[] = editor.getSupportedActions(); + let entries: EditorActionCommandEntry[] = []; + + for (let i = 0; i < actions.length; i++) { + let action = actions[i]; + + let keybind = keybindingService.lookupKeybinding(action.id); + + if (action.label) { + let highlights = matchesFuzzy(searchValue, action.label); + if (highlights) { + entries.push(new EditorActionCommandEntry(keybind ? keybind.getLabel() : '', highlights, action, editor)); + } + } + } + + // Sort by name + entries = entries.sort(this._sort); + + return entries; + } +} \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts new file mode 100644 index 0000000000..1edd8a1e1e --- /dev/null +++ b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { $, Dimension } from 'vs/base/browser/builder'; +import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenWidget } from 'vs/base/parts/quickopen/browser/quickOpenWidget'; +import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { attachQuickOpenStyler } from 'vs/platform/theme/common/styler'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +export interface IQuickOpenEditorWidgetOptions { + inputAriaLabel: string; +} + +export class QuickOpenEditorWidget implements IOverlayWidget { + + private static ID = 'editor.contrib.quickOpenEditorWidget'; + + private codeEditor: ICodeEditor; + private themeService: IThemeService; + private visible: boolean; + private quickOpenWidget: QuickOpenWidget; + private domNode: HTMLElement; + private styler: IDisposable; + + constructor(codeEditor: ICodeEditor, onOk: () => void, onCancel: () => void, onType: (value: string) => void, configuration: IQuickOpenEditorWidgetOptions, themeService: IThemeService) { + this.codeEditor = codeEditor; + this.themeService = themeService; + + this.create(onOk, onCancel, onType, configuration); + } + + private create(onOk: () => void, onCancel: () => void, onType: (value: string) => void, configuration: IQuickOpenEditorWidgetOptions): void { + this.domNode = $().div().getHTMLElement(); + + this.quickOpenWidget = new QuickOpenWidget( + this.domNode, + { + onOk: onOk, + onCancel: onCancel, + onType: onType + }, { + inputPlaceHolder: null, + inputAriaLabel: configuration.inputAriaLabel, + keyboardSupport: true + }, + null + ); + this.styler = attachQuickOpenStyler(this.quickOpenWidget, this.themeService); + + this.quickOpenWidget.create(); + this.codeEditor.addOverlayWidget(this); + } + + public setInput(model: QuickOpenModel, focus: IAutoFocus): void { + this.quickOpenWidget.setInput(model, focus); + } + + public getId(): string { + return QuickOpenEditorWidget.ID; + } + + public getDomNode(): HTMLElement { + return this.domNode; + } + + public destroy(): void { + this.codeEditor.removeOverlayWidget(this); + this.quickOpenWidget.dispose(); + this.styler.dispose(); + } + + public isVisible(): boolean { + return this.visible; + } + + public show(value: string): void { + this.visible = true; + + var editorLayout = this.codeEditor.getLayoutInfo(); + if (editorLayout) { + this.quickOpenWidget.layout(new Dimension(editorLayout.width, editorLayout.height)); + } + + this.quickOpenWidget.show(value); + this.codeEditor.layoutOverlayWidget(this); + } + + public hide(): void { + this.visible = false; + this.quickOpenWidget.hide(); + this.codeEditor.layoutOverlayWidget(this); + } + + public getPosition(): IOverlayWidgetPosition { + if (this.visible) { + return { + preference: OverlayWidgetPositionPreference.TOP_CENTER + }; + } + + return null; + } +} \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.css b/src/vs/editor/standalone/browser/quickOpen/quickOutline.css new file mode 100644 index 0000000000..6cb5f67578 --- /dev/null +++ b/src/vs/editor/standalone/browser/quickOpen/quickOutline.css @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.quick-open-widget { + font-size: 13px; +} + +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon, +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon { + background-image: url('symbol-sprite.svg'); + background-repeat: no-repeat; +} + +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method, +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function, +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor { background-position: 0 -4px; } +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field, +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable { background-position: -22px -4px; } +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class { background-position: -43px -3px; } +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface { background-position: -63px -4px; } +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module { background-position: -82px -4px; } +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property { background-position: -102px -3px; } +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum { background-position: -122px -3px; } +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule { background-position: -242px -4px; } +.quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file { background-position: -262px -4px; } + +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method, +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function, +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor { background-position: 0 -24px; } +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field, +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable { background-position: -22px -24px; } +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class { background-position: -43px -23px; } +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface { background-position: -63px -24px; } +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module { background-position: -82px -24px; } +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property { background-position: -102px -23px; } +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum { background-position: -122px -23px; } +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule { background-position: -242px -24px; } +.vs-dark .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file { background-position: -262px -24px; } + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon { + background: none; + display: inline; +} + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon:before { + height: 16px; + width: 16px; + display: inline-block; +} + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method:before, +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function:before, +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor:before { + content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZmlsbD0iI0IxODBENyIgZD0iTTUuNSAzbC00LjUgMi44NTd2NC4yODVsNC41IDIuODU4IDQuNS0yLjg1N3YtNC4yODZsLTQuNS0yLjg1N3ptLS41IDguNDk4bC0zLTEuOTA1di0yLjgxNmwzIDEuOTA1djIuODE2em0tMi4zNTgtNS40OThsMi44NTgtMS44MTUgMi44NTggMS44MTUtMi44NTggMS44MTUtMi44NTgtMS44MTV6bTYuMzU4IDMuNTkzbC0zIDEuOTA1di0yLjgxNWwzLTEuOTA1djIuODE1eiIvPjwvc3ZnPg=="); + margin-left: 2px; +} + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field:before, +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable:before { + content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZmlsbD0iIzc1QkVGRiIgZD0iTTEgNnY0bDQgMiA2LTN2LTRsLTQtMi02IDN6bTQgMWwtMi0xIDQtMiAyIDEtNCAyeiIvPjwvc3ZnPg=="); + margin-left: 2px; +} + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class:before { + content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBvbHlnb24gZmlsbD0iI0U4QUI1MyIgcG9pbnRzPSIxMS45OTgsMTEuMDAyIDksMTEgOSw3IDExLDcgMTAsOCAxMiwxMCAxNSw3IDEzLDUgMTIsNiA3LDYgOSw0IDYsMSAxLDYgNCw5IDYsNyA4LDcgOCwxMiAxMSwxMiAxMCwxMyAxMiwxNSAxNSwxMiAxMywxMCIvPjwvc3ZnPg=="); +} + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface:before { + content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZmlsbD0iIzc1QkVGRiIgZD0iTTExLjUgNGMtMS43NTkgMC0zLjIwNCAxLjMwOC0zLjQ0OSAzaC0zLjEyMmMtLjIyMy0uODYxLS45OTgtMS41LTEuOTI5LTEuNS0xLjEwNCAwLTIgLjg5NS0yIDIgMCAxLjEwNC44OTYgMiAyIDIgLjkzMSAwIDEuNzA2LS42MzkgMS45MjktMS41aDMuMTIyYy4yNDUgMS42OTEgMS42OSAzIDMuNDQ5IDMgMS45MyAwIDMuNS0xLjU3IDMuNS0zLjUgMC0xLjkzMS0xLjU3LTMuNS0zLjUtMy41em0wIDVjLS44MjcgMC0xLjUtLjY3NC0xLjUtMS41IDAtLjgyOC42NzMtMS41IDEuNS0xLjVzMS41LjY3MiAxLjUgMS41YzAgLjgyNi0uNjczIDEuNS0xLjUgMS41eiIvPjwvc3ZnPg=="); +} + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module:before { + content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZmlsbD0iI0M1QzVDNSIgZD0iTTkgMTF2LTFjMC0uODM0LjQ5Ni0xLjczOCAxLTItLjUwNC0uMjctMS0xLjE2OC0xLTJ2LTFjMC0uODQtLjU4NC0xLTEtMXYtMWMyLjA4MyAwIDIgMS4xNjYgMiAydjFjMCAuOTY5LjcwMy45OCAxIDF2MmMtLjMyMi4wMi0xIC4wNTMtMSAxdjFjMCAuODM0LjA4MyAyLTIgMnYtMWMuODMzIDAgMS0xIDEtMXptLTYgMHYtMWMwLS44MzQtLjQ5Ni0xLjczOC0xLTIgLjUwNC0uMjcgMS0xLjE2OCAxLTJ2LTFjMC0uODQuNTg0LTEgMS0xdi0xYy0yLjA4MyAwLTIgMS4xNjYtMiAydjFjMCAuOTY5LS43MDMuOTgtMSAxdjJjLjMyMi4wMiAxIC4wNTMgMSAxdjFjMCAuODM0LS4wODMgMiAyIDJ2LTFjLS44MzMgMC0xLTEtMS0xeiIvPjwvc3ZnPg=="); + margin-left: 2px; +} + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property:before { + content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZmlsbD0iI0M1QzVDNSIgZD0iTTEyLjA5IDQuMzU5bC0yLjY0MSAyLjY0MS0yLTIgMi42NDEtMi42NDFjLS41MDItLjIyNi0xLjA1NS0uMzU5LTEuNjQxLS4zNTktMi4yMDkgMC00IDEuNzkxLTQgNCAwIC41ODYuMTMzIDEuMTM5LjM1OSAxLjY0bC0zLjM1OSAzLjM2cy0xIDEgMCAyaDJsMy4zNTktMy4zNmMuNTAzLjIyNiAxLjA1NS4zNiAxLjY0MS4zNiAyLjIwOSAwIDQtMS43OTEgNC00IDAtLjU4Ni0uMTMzLTEuMTM5LS4zNTktMS42NDF6Ii8+PC9zdmc+"); + margin-left: 1px; +} + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.value:before, +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum:before { + content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PGcgZmlsbD0iIzc1QkVGRiI+PHBhdGggZD0iTTEyIDNoLTRsLTEgMXYyaDV2MWgtMnYxaDJsMS0xdi0zbC0xLTF6bTAgMmgtNHYtMWg0djF6TTMgMTJoNnYtNWgtNnY1em0xLTNoNHYxaC00di0xeiIvPjwvZz48L3N2Zz4="); +} + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule:before { + content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiI+PHBhdGggZmlsbD0iI0M1QzVDNSIgZD0iTTEwIDVoLTh2LTJoOHYyem0wIDFoLTZ2MWg2di0xem0wIDJoLTZ2MWg2di0xeiIvPjwvc3ZnPg=="); +} + +.hc-black .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file:before { + content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZmlsbD0iI0M1QzVDNSIgZD0iTTkuNjc2IDJoLTYuNjc2djEyaDEwdi05bC0zLjMyNC0zem0yLjMyNCAxMWgtOHYtMTBoNXYzaDN2N3oiLz48L3N2Zz4="); +} \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts new file mode 100644 index 0000000000..fd087fa1e3 --- /dev/null +++ b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts @@ -0,0 +1,316 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +'use strict'; + +import 'vs/css!./quickOutline'; +import * as nls from 'vs/nls'; +import { matchesFuzzy } from 'vs/base/common/filters'; +import * as strings from 'vs/base/common/strings'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IContext, IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IAutoFocus, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; +import { ICommonCodeEditor, ScrollType } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { SymbolInformation, DocumentSymbolProviderRegistry, symbolKindToCssClass, IOutline } from 'vs/editor/common/modes'; +import { BaseEditorQuickOpenAction, IDecorator } from './editorQuickOpen'; +import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/common/quickOpen'; +import { editorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Range } from 'vs/editor/common/core/range'; + +let SCOPE_PREFIX = ':'; + +class SymbolEntry extends QuickOpenEntryGroup { + private name: string; + private type: string; + private description: string; + private range: Range; + private editor: ICommonCodeEditor; + private decorator: IDecorator; + + constructor(name: string, type: string, description: string, range: Range, highlights: IHighlight[], editor: ICommonCodeEditor, decorator: IDecorator) { + super(); + + this.name = name; + this.type = type; + this.description = description; + this.range = range; + this.setHighlights(highlights); + this.editor = editor; + this.decorator = decorator; + } + + public getLabel(): string { + return this.name; + } + + public getAriaLabel(): string { + return nls.localize('entryAriaLabel', "{0}, symbols", this.name); + } + + public getIcon(): string { + return this.type; + } + + public getDescription(): string { + return this.description; + } + + public getType(): string { + return this.type; + } + + public getRange(): Range { + return this.range; + } + + public run(mode: Mode, context: IContext): boolean { + if (mode === Mode.OPEN) { + return this.runOpen(context); + } + + return this.runPreview(); + } + + private runOpen(context: IContext): boolean { + + // Apply selection and focus + let range = this.toSelection(); + this.editor.setSelection(range); + this.editor.revealRangeInCenter(range, ScrollType.Smooth); + this.editor.focus(); + + return true; + } + + private runPreview(): boolean { + + // Select Outline Position + let range = this.toSelection(); + this.editor.revealRangeInCenter(range, ScrollType.Smooth); + + // Decorate if possible + this.decorator.decorateLine(this.range, this.editor); + + return false; + } + + private toSelection(): Range { + return new Range( + this.range.startLineNumber, + this.range.startColumn || 1, + this.range.startLineNumber, + this.range.startColumn || 1 + ); + } +} + +@editorAction +export class QuickOutlineAction extends BaseEditorQuickOpenAction { + + constructor() { + super(nls.localize('quickOutlineActionInput', "Type the name of an identifier you wish to navigate to"), { + id: 'editor.action.quickOutline', + label: nls.localize('QuickOutlineAction.label', "Go to Symbol..."), + alias: 'Go to Symbol...', + precondition: EditorContextKeys.hasDocumentSymbolProvider, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O + }, + menuOpts: { + group: 'navigation', + order: 3 + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + + let model = editor.getModel(); + + if (!DocumentSymbolProviderRegistry.has(model)) { + return null; + } + + // Resolve outline + return getDocumentSymbols(model).then((result: IOutline) => { + if (result.entries.length === 0) { + return; + } + + this._run(editor, result.entries); + }); + } + + private _run(editor: ICommonCodeEditor, result: SymbolInformation[]): void { + this._show(this.getController(editor), { + getModel: (value: string): QuickOpenModel => { + return new QuickOpenModel(this.toQuickOpenEntries(editor, result, value)); + }, + + getAutoFocus: (searchValue: string): IAutoFocus => { + // Remove any type pattern (:) from search value as needed + if (searchValue.indexOf(SCOPE_PREFIX) === 0) { + searchValue = searchValue.substr(SCOPE_PREFIX.length); + } + + return { + autoFocusPrefixMatch: searchValue, + autoFocusFirstEntry: !!searchValue + }; + } + }); + } + + private toQuickOpenEntries(editor: ICommonCodeEditor, flattened: SymbolInformation[], searchValue: string): SymbolEntry[] { + const controller = this.getController(editor); + + let results: SymbolEntry[] = []; + + // Convert to Entries + let normalizedSearchValue = searchValue; + if (searchValue.indexOf(SCOPE_PREFIX) === 0) { + normalizedSearchValue = normalizedSearchValue.substr(SCOPE_PREFIX.length); + } + + for (let i = 0; i < flattened.length; i++) { + let element = flattened[i]; + let label = strings.trim(element.name); + + // Check for meatch + let highlights = matchesFuzzy(normalizedSearchValue, label); + if (highlights) { + + // Show parent scope as description + let description: string = null; + if (element.containerName) { + description = element.containerName; + } + + // Add + results.push(new SymbolEntry(label, symbolKindToCssClass(element.kind), description, Range.lift(element.location.range), highlights, editor, controller)); + } + } + + // Sort properly if actually searching + if (searchValue) { + if (searchValue.indexOf(SCOPE_PREFIX) === 0) { + results = results.sort(this.sortScoped.bind(this, searchValue.toLowerCase())); + } else { + results = results.sort(this.sortNormal.bind(this, searchValue.toLowerCase())); + } + } + + // Mark all type groups + if (results.length > 0 && searchValue.indexOf(SCOPE_PREFIX) === 0) { + let currentType: string = null; + let currentResult: SymbolEntry = null; + let typeCounter = 0; + + for (let i = 0; i < results.length; i++) { + let result = results[i]; + + // Found new type + if (currentType !== result.getType()) { + + // Update previous result with count + if (currentResult) { + currentResult.setGroupLabel(this.typeToLabel(currentType, typeCounter)); + } + + currentType = result.getType(); + currentResult = result; + typeCounter = 1; + + result.setShowBorder(i > 0); + } + + // Existing type, keep counting + else { + typeCounter++; + } + } + + // Update previous result with count + if (currentResult) { + currentResult.setGroupLabel(this.typeToLabel(currentType, typeCounter)); + } + } + + // Mark first entry as outline + else if (results.length > 0) { + results[0].setGroupLabel(nls.localize('symbols', "symbols ({0})", results.length)); + } + + return results; + } + + private typeToLabel(type: string, count: number): string { + switch (type) { + case 'module': return nls.localize('modules', "modules ({0})", count); + case 'class': return nls.localize('class', "classes ({0})", count); + case 'interface': return nls.localize('interface', "interfaces ({0})", count); + case 'method': return nls.localize('method', "methods ({0})", count); + case 'function': return nls.localize('function', "functions ({0})", count); + case 'property': return nls.localize('property', "properties ({0})", count); + case 'variable': return nls.localize('variable', "variables ({0})", count); + case 'var': return nls.localize('variable2', "variables ({0})", count); + case 'constructor': return nls.localize('_constructor', "constructors ({0})", count); + case 'call': return nls.localize('call', "calls ({0})", count); + } + + return type; + } + + private sortNormal(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number { + let elementAName = elementA.getLabel().toLowerCase(); + let elementBName = elementB.getLabel().toLowerCase(); + + // Compare by name + let r = elementAName.localeCompare(elementBName); + if (r !== 0) { + return r; + } + + // If name identical sort by range instead + let elementARange = elementA.getRange(); + let elementBRange = elementB.getRange(); + return elementARange.startLineNumber - elementBRange.startLineNumber; + } + + private sortScoped(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number { + + // Remove scope char + searchValue = searchValue.substr(SCOPE_PREFIX.length); + + // Sort by type first if scoped search + let elementAType = elementA.getType(); + let elementBType = elementB.getType(); + let r = elementAType.localeCompare(elementBType); + if (r !== 0) { + return r; + } + + // Special sort when searching in scoped mode + if (searchValue) { + let elementAName = elementA.getLabel().toLowerCase(); + let elementBName = elementB.getLabel().toLowerCase(); + + // Compare by name + let r = elementAName.localeCompare(elementBName); + if (r !== 0) { + return r; + } + } + + // Default to sort by range + let elementARange = elementA.getRange(); + let elementBRange = elementB.getRange(); + return elementARange.startLineNumber - elementBRange.startLineNumber; + } +} diff --git a/src/vs/editor/standalone/browser/quickOpen/symbol-sprite.svg b/src/vs/editor/standalone/browser/quickOpen/symbol-sprite.svg new file mode 100644 index 0000000000..d2b2038b1f --- /dev/null +++ b/src/vs/editor/standalone/browser/quickOpen/symbol-sprite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts new file mode 100644 index 0000000000..0dbe788c00 --- /dev/null +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -0,0 +1,569 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Schemas } from 'vs/base/common/network'; +import Severity from 'vs/base/common/severity'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IConfigurationService, IConfigurationServiceEvent, IConfigurationValue, IConfigurationKeys, IConfigurationValues, Configuration, IConfigurationData, ConfigurationModel, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; +import { IEditor, IEditorInput, IEditorOptions, IEditorService, IResourceInput, Position } from 'vs/platform/editor/common/editor'; +import { ICommandService, ICommand, ICommandEvent, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; +import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; +import { IKeybindingEvent, KeybindingSource, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IConfirmation, IMessageService } from 'vs/platform/message/common/message'; +import { IWorkspaceContextService, ILegacyWorkspace, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { Selection } from 'vs/editor/common/core/selection'; +import Event, { Emitter } from 'vs/base/common/event'; +import { DefaultConfigurationModel } from 'vs/platform/configuration/common/model'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { ITextModelService, ITextModelContentProvider, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { IDisposable, IReference, ImmortalReference, combinedDisposable } from 'vs/base/common/lifecycle'; +import * as dom from 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeybindingsRegistry, IKeybindingItem } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions'; +import { Menu } from 'vs/platform/actions/common/menu'; +import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; +import { ResolvedKeybinding, Keybinding, createKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { OS } from 'vs/base/common/platform'; +import { IRange } from 'vs/editor/common/core/range'; + +export class SimpleEditor implements IEditor { + + public input: IEditorInput; + public options: IEditorOptions; + public position: Position; + + public _widget: editorCommon.IEditor; + + constructor(editor: editorCommon.IEditor) { + this._widget = editor; + } + + public getId(): string { return 'editor'; } + public getControl(): editorCommon.IEditor { return this._widget; } + public getSelection(): Selection { return this._widget.getSelection(); } + public focus(): void { this._widget.focus(); } + public isVisible(): boolean { return true; } + + public withTypedEditor(codeEditorCallback: (editor: ICodeEditor) => T, diffEditorCallback: (editor: IDiffEditor) => T): T { + if (editorCommon.isCommonCodeEditor(this._widget)) { + // Single Editor + return codeEditorCallback(this._widget); + } else { + // Diff Editor + return diffEditorCallback(this._widget); + } + } +} + +export class SimpleModel implements ITextEditorModel { + + private model: editorCommon.IModel; + private _onDispose: Emitter; + + constructor(model: editorCommon.IModel) { + this.model = model; + this._onDispose = new Emitter(); + } + + public get onDispose(): Event { + return this._onDispose.event; + } + + public load(): TPromise { + return TPromise.as(this); + } + + public get textEditorModel(): editorCommon.IModel { + return this.model; + } + + public dispose(): void { + this._onDispose.fire(); + } +} + +export interface IOpenEditorDelegate { + (url: string): boolean; +} + +export class SimpleEditorService implements IEditorService { + public _serviceBrand: any; + + private editor: SimpleEditor; + private openEditorDelegate: IOpenEditorDelegate; + + constructor() { + this.openEditorDelegate = null; + } + + public setEditor(editor: editorCommon.IEditor): void { + this.editor = new SimpleEditor(editor); + } + + public setOpenEditorDelegate(openEditorDelegate: IOpenEditorDelegate): void { + this.openEditorDelegate = openEditorDelegate; + } + + public openEditor(typedData: IResourceInput, sideBySide?: boolean): TPromise { + return TPromise.as(this.editor.withTypedEditor( + (editor) => this.doOpenEditor(editor, typedData), + (diffEditor) => ( + this.doOpenEditor(diffEditor.getOriginalEditor(), typedData) || + this.doOpenEditor(diffEditor.getModifiedEditor(), typedData) + ) + )); + } + + private doOpenEditor(editor: editorCommon.ICommonCodeEditor, data: IResourceInput): IEditor { + let model = this.findModel(editor, data); + if (!model) { + if (data.resource) { + if (this.openEditorDelegate) { + this.openEditorDelegate(data.resource.toString()); + return null; + } else { + let schema = data.resource.scheme; + if (schema === Schemas.http || schema === Schemas.https) { + // This is a fully qualified http or https URL + window.open(data.resource.toString()); + return this.editor; + } + } + } + return null; + } + + let selection = data.options.selection; + if (selection) { + if (typeof selection.endLineNumber === 'number' && typeof selection.endColumn === 'number') { + editor.setSelection(selection); + editor.revealRangeInCenter(selection, editorCommon.ScrollType.Immediate); + } else { + let pos = { + lineNumber: selection.startLineNumber, + column: selection.startColumn + }; + editor.setPosition(pos); + editor.revealPositionInCenter(pos, editorCommon.ScrollType.Immediate); + } + } + + return this.editor; + } + + private findModel(editor: editorCommon.ICommonCodeEditor, data: IResourceInput): editorCommon.IModel { + let model = editor.getModel(); + if (model.uri.toString() !== data.resource.toString()) { + return null; + } + + return model; + } +} + +export class SimpleEditorModelResolverService implements ITextModelService { + public _serviceBrand: any; + + private editor: SimpleEditor; + + public setEditor(editor: editorCommon.IEditor): void { + this.editor = new SimpleEditor(editor); + } + + public createModelReference(resource: URI): TPromise> { + let model: editorCommon.IModel; + + model = this.editor.withTypedEditor( + (editor) => this.findModel(editor, resource), + (diffEditor) => this.findModel(diffEditor.getOriginalEditor(), resource) || this.findModel(diffEditor.getModifiedEditor(), resource) + ); + + if (!model) { + return TPromise.as(new ImmortalReference(null)); + } + + return TPromise.as(new ImmortalReference(new SimpleModel(model))); + } + + public registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable { + return { + dispose: function () { /* no op */ } + }; + } + + private findModel(editor: editorCommon.ICommonCodeEditor, resource: URI): editorCommon.IModel { + let model = editor.getModel(); + if (model.uri.toString() !== resource.toString()) { + return null; + } + + return model; + } +} + +export class SimpleProgressService implements IProgressService { + _serviceBrand: any; + + private static NULL_PROGRESS_RUNNER: IProgressRunner = { + done: () => { }, + total: () => { }, + worked: () => { } + }; + + show(infinite: boolean, delay?: number): IProgressRunner; + show(total: number, delay?: number): IProgressRunner; + show(): IProgressRunner { + return SimpleProgressService.NULL_PROGRESS_RUNNER; + } + + showWhile(promise: TPromise, delay?: number): TPromise { + return null; + } +} + +export class SimpleMessageService implements IMessageService { + public _serviceBrand: any; + + private static Empty = function () { /* nothing */ }; + + public show(sev: Severity, message: any): () => void { + + switch (sev) { + case Severity.Error: + console.error(message); + break; + case Severity.Warning: + console.warn(message); + break; + default: + console.log(message); + break; + } + + return SimpleMessageService.Empty; + } + + public hideAll(): void { + // No-op + } + + public confirm(confirmation: IConfirmation): boolean { + let messageText = confirmation.message; + if (confirmation.detail) { + messageText = messageText + '\n\n' + confirmation.detail; + } + + return window.confirm(messageText); + } +} + +export class StandaloneCommandService implements ICommandService { + _serviceBrand: any; + + private readonly _instantiationService: IInstantiationService; + private _dynamicCommands: { [id: string]: ICommand; }; + + private _onWillExecuteCommand: Emitter = new Emitter(); + public readonly onWillExecuteCommand: Event = this._onWillExecuteCommand.event; + + constructor(instantiationService: IInstantiationService) { + this._instantiationService = instantiationService; + this._dynamicCommands = Object.create(null); + } + + public addCommand(id: string, command: ICommand): IDisposable { + this._dynamicCommands[id] = command; + return { + dispose: () => { + delete this._dynamicCommands[id]; + } + }; + } + + public executeCommand(id: string, ...args: any[]): TPromise { + const command = (CommandsRegistry.getCommand(id) || this._dynamicCommands[id]); + if (!command) { + return TPromise.wrapError(new Error(`command '${id}' not found`)); + } + + try { + this._onWillExecuteCommand.fire({ commandId: id }); + const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler].concat(args)); + return TPromise.as(result); + } catch (err) { + return TPromise.wrapError(err); + } + } +} + +export class StandaloneKeybindingService extends AbstractKeybindingService { + private _cachedResolver: KeybindingResolver; + private _dynamicKeybindings: IKeybindingItem[]; + + constructor( + contextKeyService: IContextKeyService, + commandService: ICommandService, + messageService: IMessageService, + domNode: HTMLElement + ) { + super(contextKeyService, commandService, messageService); + + this._cachedResolver = null; + this._dynamicKeybindings = []; + + this.toDispose.push(dom.addDisposableListener(domNode, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + let keyEvent = new StandardKeyboardEvent(e); + let shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target); + if (shouldPreventDefault) { + keyEvent.preventDefault(); + } + })); + } + + public addDynamicKeybinding(commandId: string, keybinding: number, handler: ICommandHandler, when: ContextKeyExpr): IDisposable { + let toDispose: IDisposable[] = []; + + this._dynamicKeybindings.push({ + keybinding: createKeybinding(keybinding, OS), + command: commandId, + when: when, + weight1: 1000, + weight2: 0 + }); + + toDispose.push({ + dispose: () => { + for (let i = 0; i < this._dynamicKeybindings.length; i++) { + let kb = this._dynamicKeybindings[i]; + if (kb.command === commandId) { + this._dynamicKeybindings.splice(i, 1); + this.updateResolver({ source: KeybindingSource.Default }); + return; + } + } + } + }); + + let commandService = this._commandService; + if (commandService instanceof StandaloneCommandService) { + toDispose.push(commandService.addCommand(commandId, { + handler: handler + })); + } else { + throw new Error('Unknown command service!'); + } + this.updateResolver({ source: KeybindingSource.Default }); + + return combinedDisposable(toDispose); + } + + private updateResolver(event: IKeybindingEvent): void { + this._cachedResolver = null; + this._onDidUpdateKeybindings.fire(event); + } + + protected _getResolver(): KeybindingResolver { + if (!this._cachedResolver) { + const defaults = this._toNormalizedKeybindingItems(KeybindingsRegistry.getDefaultKeybindings(), true); + const overrides = this._toNormalizedKeybindingItems(this._dynamicKeybindings, false); + this._cachedResolver = new KeybindingResolver(defaults, overrides); + } + return this._cachedResolver; + } + + private _toNormalizedKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { + let result: ResolvedKeybindingItem[] = [], resultLen = 0; + for (let i = 0, len = items.length; i < len; i++) { + const item = items[i]; + const when = (item.when ? item.when.normalize() : null); + const keybinding = item.keybinding; + + if (!keybinding) { + // This might be a removal keybinding item in user settings => accept it + result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault); + } else { + const resolvedKeybindings = this.resolveKeybinding(keybinding); + for (let j = 0; j < resolvedKeybindings.length; j++) { + result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybindings[j], item.command, item.commandArgs, when, isDefault); + } + } + } + + return result; + } + + public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] { + return [new USLayoutResolvedKeybinding(keybinding, OS)]; + } + + public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding { + let keybinding = new SimpleKeybinding( + keyboardEvent.ctrlKey, + keyboardEvent.shiftKey, + keyboardEvent.altKey, + keyboardEvent.metaKey, + keyboardEvent.keyCode + ); + return new USLayoutResolvedKeybinding(keybinding, OS); + } + + public resolveUserBinding(userBinding: string): ResolvedKeybinding[] { + return []; + } +} + +export class SimpleConfigurationService implements IConfigurationService { + + _serviceBrand: any; + + private _onDidUpdateConfiguration = new Emitter(); + public onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; + + private _configuration: Configuration; + + constructor() { + this._configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel()); + } + + private configuration(): Configuration { + return this._configuration; + } + + public reloadConfiguration(section?: string): TPromise { + return TPromise.as(this.getConfiguration(section)); + } + + public getConfiguration(section?: string, options?: IConfigurationOverrides): C { + return this.configuration().getValue(section, options); + } + + public lookup(key: string, options?: IConfigurationOverrides): IConfigurationValue { + return this.configuration().lookup(key, options); + } + + public keys(): IConfigurationKeys { + return this.configuration().keys(); + } + + public values(): IConfigurationValues { + return this._configuration.values(); + } + + public getConfigurationData(): IConfigurationData { + return this.configuration().toData(); + } +} + +export class SimpleResourceConfigurationService implements ITextResourceConfigurationService { + + _serviceBrand: any; + + public readonly onDidUpdateConfiguration: Event; + private readonly _onDidUpdateConfigurationEmitter = new Emitter(); + + constructor(private configurationService: SimpleConfigurationService) { + this.configurationService.onDidUpdateConfiguration(() => { + this._onDidUpdateConfigurationEmitter.fire(); + }); + } + + public getConfiguration(): T { + return this.configurationService.getConfiguration(); + } + +} + +export class SimpleMenuService implements IMenuService { + + _serviceBrand: any; + + private readonly _commandService: ICommandService; + + constructor(commandService: ICommandService) { + this._commandService = commandService; + } + + public createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu { + return new Menu(id, TPromise.as(true), this._commandService, contextKeyService); + } +} + +export class StandaloneTelemetryService implements ITelemetryService { + _serviceBrand: void; + + public isOptedIn = false; + + public publicLog(eventName: string, data?: any): TPromise { + return TPromise.as(null); + } + + public getTelemetryInfo(): TPromise { + return null; + } +} + +export class SimpleWorkspaceContextService implements IWorkspaceContextService { + + public _serviceBrand: any; + + private static SCHEME: 'inmemory'; + + private readonly _onDidChangeWorkspaceName: Emitter = new Emitter(); + public readonly onDidChangeWorkspaceName: Event = this._onDidChangeWorkspaceName.event; + + private readonly _onDidChangeWorkspaceRoots: Emitter = new Emitter(); + public readonly onDidChangeWorkspaceRoots: Event = this._onDidChangeWorkspaceRoots.event; + + private readonly legacyWorkspace: ILegacyWorkspace; + private readonly workspace: IWorkspace; + + constructor() { + this.legacyWorkspace = { resource: URI.from({ scheme: SimpleWorkspaceContextService.SCHEME, authority: 'model', path: '/' }) }; + this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', roots: [this.legacyWorkspace.resource], name: this.legacyWorkspace.resource.fsPath }; + } + + public getLegacyWorkspace(): ILegacyWorkspace { + return this.legacyWorkspace; + } + + public getWorkspace(): IWorkspace { + return this.workspace; + } + + public getRoot(resource: URI): URI { + return resource && resource.scheme === SimpleWorkspaceContextService.SCHEME ? this.workspace.roots[0] : void 0; + } + + public hasWorkspace(): boolean { + return true; + } + + public hasFolderWorkspace(): boolean { + return true; + } + + public hasMultiFolderWorkspace(): boolean { + return false; + } + + public isInsideWorkspace(resource: URI): boolean { + return resource && resource.scheme === SimpleWorkspaceContextService.SCHEME; + } + + public toResource(workspaceRelativePath: string): URI { + return URI.file(workspaceRelativePath); + } +} diff --git a/src/vs/editor/standalone/browser/standalone-tokens.css b/src/vs/editor/standalone/browser/standalone-tokens.css new file mode 100644 index 0000000000..67e4391864 --- /dev/null +++ b/src/vs/editor/standalone/browser/standalone-tokens.css @@ -0,0 +1,251 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +/* Default standalone editor font */ +.monaco-editor { + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; +} + +.monaco-menu .monaco-action-bar.vertical .action-item .action-label:focus { + color: #0059AC; + stroke-width: 1.2px; + text-shadow: 0px 0px 0.15px #0059AC; +} + +.monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-item .action-label:focus, +.monaco-editor.hc-black .monaco-menu .monaco-action-bar.vertical .action-item .action-label:focus { + color: #ACDDFF; + stroke-width: 1.2px; + text-shadow: 0px 0px 0.15px #ACDDFF; +} + +.monaco-editor-hover p { + margin: 0; +} + +/* The hc-black theme is already high contrast optimized */ +.monaco-editor.hc-black { + -ms-high-contrast-adjust: none; +} +/* In case the browser goes into high contrast mode and the editor is not configured with the hc-black theme */ +@media screen and (-ms-high-contrast:active) { + + /* current line highlight */ + .monaco-editor.vs .view-overlays .current-line, + .monaco-editor.vs-dark .view-overlays .current-line { + border-color: windowtext !important; + border-left: 0; + border-right: 0; + } + + /* view cursors */ + .monaco-editor.vs .cursor, + .monaco-editor.vs-dark .cursor { + background-color: windowtext !important; + } + /* dnd target */ + .monaco-editor.vs .dnd-target, + .monaco-editor.vs-dark .dnd-target { + border-color: windowtext !important; + } + + /* selected text background */ + .monaco-editor.vs .selected-text, + .monaco-editor.vs-dark .selected-text { + background-color: highlight !important; + } + + /* allow the text to have a transparent background. */ + .monaco-editor.vs .view-line, + .monaco-editor.vs-dark .view-line { + -ms-high-contrast-adjust: none; + } + + /* text color */ + .monaco-editor.vs .view-line span, + .monaco-editor.vs-dark .view-line span { + color: windowtext !important; + } + /* selected text color */ + .monaco-editor.vs .view-line span.inline-selected-text, + .monaco-editor.vs-dark .view-line span.inline-selected-text { + color: highlighttext !important; + } + + /* allow decorations */ + .monaco-editor.vs .view-overlays, + .monaco-editor.vs-dark .view-overlays { + -ms-high-contrast-adjust: none; + } + + /* various decorations */ + .monaco-editor.vs .selectionHighlight, + .monaco-editor.vs-dark .selectionHighlight, + .monaco-editor.vs .wordHighlight, + .monaco-editor.vs-dark .wordHighlight, + .monaco-editor.vs .wordHighlightStrong, + .monaco-editor.vs-dark .wordHighlightStrong, + .monaco-editor.vs .reference-decoration, + .monaco-editor.vs-dark .reference-decoration { + border: 2px dotted highlight !important; + background: transparent !important; + box-sizing: border-box; + } + .monaco-editor.vs .rangeHighlight, + .monaco-editor.vs-dark .rangeHighlight { + background: transparent !important; + border: 1px dotted activeborder !important; + box-sizing: border-box; + } + .monaco-editor.vs .bracket-match, + .monaco-editor.vs-dark .bracket-match { + border-color: windowtext !important; + background: transparent !important; + } + + /* find widget */ + .monaco-editor.vs .findMatch, + .monaco-editor.vs-dark .findMatch, + .monaco-editor.vs .currentFindMatch, + .monaco-editor.vs-dark .currentFindMatch { + border: 2px dotted activeborder !important; + background: transparent !important; + box-sizing: border-box; + } + .monaco-editor.vs .find-widget, + .monaco-editor.vs-dark .find-widget { + border: 1px solid windowtext; + } + + /* list - used by suggest widget */ + .monaco-editor.vs .monaco-list .monaco-list-row, + .monaco-editor.vs-dark .monaco-list .monaco-list-row { + -ms-high-contrast-adjust: none; + color: windowtext !important; + } + .monaco-editor.vs .monaco-list .monaco-list-row.focused, + .monaco-editor.vs-dark .monaco-list .monaco-list-row.focused { + color: highlighttext !important; + background-color: highlight !important; + } + .monaco-editor.vs .monaco-list .monaco-list-row:hover, + .monaco-editor.vs-dark .monaco-list .monaco-list-row:hover { + background: transparent !important; + border: 1px solid highlight; + box-sizing: border-box; + } + + /* tree */ + .monaco-editor.vs .monaco-tree .monaco-tree-row, + .monaco-editor.vs-dark .monaco-tree .monaco-tree-row { + -ms-high-contrast-adjust: none; + color: windowtext !important; + } + .monaco-editor.vs .monaco-tree .monaco-tree-row.selected, + .monaco-editor.vs-dark .monaco-tree .monaco-tree-row.selected, + .monaco-editor.vs .monaco-tree .monaco-tree-row.focused, + .monaco-editor.vs-dark .monaco-tree .monaco-tree-row.focused { + color: highlighttext !important; + background-color: highlight !important; + } + .monaco-editor.vs .monaco-tree .monaco-tree-row:hover, + .monaco-editor.vs-dark .monaco-tree .monaco-tree-row:hover { + background: transparent !important; + border: 1px solid highlight; + box-sizing: border-box; + } + + /* scrollbars */ + .monaco-editor.vs .monaco-scrollable-element > .scrollbar, + .monaco-editor.vs-dark .monaco-scrollable-element > .scrollbar { + -ms-high-contrast-adjust: none; + background: background !important; + border: 1px solid windowtext; + box-sizing: border-box; + } + .monaco-editor.vs .monaco-scrollable-element > .scrollbar > .slider, + .monaco-editor.vs-dark .monaco-scrollable-element > .scrollbar > .slider { + background: windowtext !important; + } + .monaco-editor.vs .monaco-scrollable-element > .scrollbar > .slider:hover, + .monaco-editor.vs-dark .monaco-scrollable-element > .scrollbar > .slider:hover { + background: highlight !important; + } + .monaco-editor.vs .monaco-scrollable-element > .scrollbar > .slider.active, + .monaco-editor.vs-dark .monaco-scrollable-element > .scrollbar > .slider.active { + background: highlight !important; + } + + /* overview ruler */ + .monaco-editor.vs .decorationsOverviewRuler, + .monaco-editor.vs-dark .decorationsOverviewRuler { + opacity: 0; + } + + /* minimap */ + .monaco-editor.vs .minimap, + .monaco-editor.vs-dark .minimap { + display: none; + } + + /* squiggles */ + .monaco-editor.vs .redsquiggly, + .monaco-editor.vs-dark .redsquiggly { + background: transparent !important; + border-bottom: 4px double #E47777; + } + .monaco-editor.vs .greensquiggly, + .monaco-editor.vs-dark .greensquiggly { + border-bottom: 4px double #71B771; + } + + /* contextmenu */ + .monaco-editor.vs .monaco-menu .monaco-action-bar.vertical .action-item .action-label:focus, + .monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-item .action-label:focus { + -ms-high-contrast-adjust: none; + color: highlighttext !important; + background-color: highlight !important; + } + .monaco-editor.vs .monaco-menu .monaco-action-bar.vertical .action-item .action-label:hover, + .monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-item .action-label:hover { + -ms-high-contrast-adjust: none; + background: transparent !important; + border: 1px solid highlight; + box-sizing: border-box; + } + + /* diff editor */ + .monaco-diff-editor.vs .diffOverviewRuler, + .monaco-diff-editor.vs-dark .diffOverviewRuler { + display: none; + } + .monaco-editor.vs .line-insert, + .monaco-editor.vs-dark .line-insert, + .monaco-editor.vs .line-delete, + .monaco-editor.vs-dark .line-delete { + background: transparent !important; + border: 1px solid highlight !important; + box-sizing: border-box; + } + .monaco-editor.vs .char-insert, + .monaco-editor.vs-dark .char-insert, + .monaco-editor.vs .char-delete, + .monaco-editor.vs-dark .char-delete { + background: transparent !important; + } +} + +/*.monaco-editor.vs [tabindex="0"]:focus { + outline: 1px solid rgba(0, 122, 204, 0.4); + outline-offset: -1px; + opacity: 1 !important; +} + +.monaco-editor.vs-dark [tabindex="0"]:focus { + outline: 1px solid rgba(14, 99, 156, 0.6); + outline-offset: -1px; + opacity: 1 !important; +}*/ diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts new file mode 100644 index 0000000000..dd7792f91f --- /dev/null +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -0,0 +1,373 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { empty as emptyDisposable, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IActionDescriptor, IModel, IModelChangedEvent } from 'vs/editor/common/editorCommon'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { StandaloneKeybindingService } from 'vs/editor/standalone/browser/simpleServices'; +import { IEditorContextViewService } from 'vs/editor/standalone/browser/standaloneServices'; +import { CodeEditor } from 'vs/editor/browser/codeEditor'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { InternalEditorAction } from 'vs/editor/common/editorAction'; +import { MenuId, MenuRegistry, IMenuItem } from 'vs/platform/actions/common/actions'; +import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import * as aria from 'vs/base/browser/ui/aria/aria'; +import { IMessageService } from 'vs/platform/message/common/message'; +import * as nls from 'vs/nls'; +import * as browser from 'vs/base/browser/browser'; + +/** + * The options to create an editor. + */ +export interface IEditorConstructionOptions extends IEditorOptions { + /** + * The initial model associated with this code editor. + */ + model?: IModel; + /** + * The initial value of the auto created model in the editor. + * To not create automatically a model, use `model: null`. + */ + value?: string; + /** + * The initial language of the auto created model in the editor. + * To not create automatically a model, use `model: null`. + */ + language?: string; + /** + * Initial theme to be used for rendering. + * The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'. + * You can create custom themes via `monaco.editor.defineTheme`. + * To switch a theme, use `monaco.editor.setTheme` + */ + theme?: string; + /** + * An URL to open when Ctrl+H (Windows and Linux) or Cmd+H (OSX) is pressed in + * the accessibility help dialog in the editor. + * + * Defaults to "https://go.microsoft.com/fwlink/?linkid=852450" + */ + accessibilityHelpUrl?: string; +} + +/** + * The options to create a diff editor. + */ +export interface IDiffEditorConstructionOptions extends IDiffEditorOptions { + /** + * Initial theme to be used for rendering. + * The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'. + * You can create custom themes via `monaco.editor.defineTheme`. + * To switch a theme, use `monaco.editor.setTheme` + */ + theme?: string; +} + +export interface IStandaloneCodeEditor extends ICodeEditor { + addCommand(keybinding: number, handler: ICommandHandler, context: string): string; + createContextKey(key: string, defaultValue: T): IContextKey; + addAction(descriptor: IActionDescriptor): IDisposable; +} + +export interface IStandaloneDiffEditor extends IDiffEditor { + addCommand(keybinding: number, handler: ICommandHandler, context: string): string; + createContextKey(key: string, defaultValue: T): IContextKey; + addAction(descriptor: IActionDescriptor): IDisposable; + + getOriginalEditor(): IStandaloneCodeEditor; + getModifiedEditor(): IStandaloneCodeEditor; +} + +let LAST_GENERATED_COMMAND_ID = 0; + +let ariaDomNodeCreated = false; +function createAriaDomNode() { + if (ariaDomNodeCreated) { + return; + } + ariaDomNodeCreated = true; + aria.setARIAContainer(document.body); +} + +/** + * A code editor to be used both by the standalone editor and the standalone diff editor. + */ +export class StandaloneCodeEditor extends CodeEditor implements IStandaloneCodeEditor { + + private _standaloneKeybindingService: StandaloneKeybindingService; + + constructor( + domElement: HTMLElement, + options: IEditorConstructionOptions, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @ICommandService commandService: ICommandService, + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService keybindingService: IKeybindingService, + @IThemeService themeService: IThemeService + ) { + options = options || {}; + options.ariaLabel = options.ariaLabel || nls.localize('editorViewAccessibleLabel', "Editor content"); + options.ariaLabel = options.ariaLabel + ';' + ( + browser.isIE + ? nls.localize('accessibilityHelpMessageIE', "Press Ctrl+F1 for Accessibility Options.") + : nls.localize('accessibilityHelpMessage', "Press Alt+F1 for Accessibility Options.") + ); + super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService, themeService); + + if (keybindingService instanceof StandaloneKeybindingService) { + this._standaloneKeybindingService = keybindingService; + } + + // Create the ARIA dom node as soon as the first editor is instantiated + createAriaDomNode(); + } + + public addCommand(keybinding: number, handler: ICommandHandler, context: string): string { + if (!this._standaloneKeybindingService) { + console.warn('Cannot add command because the editor is configured with an unrecognized KeybindingService'); + return null; + } + let commandId = 'DYNAMIC_' + (++LAST_GENERATED_COMMAND_ID); + let whenExpression = ContextKeyExpr.deserialize(context); + this._standaloneKeybindingService.addDynamicKeybinding(commandId, keybinding, handler, whenExpression); + return commandId; + } + + public createContextKey(key: string, defaultValue: T): IContextKey { + return this._contextKeyService.createKey(key, defaultValue); + } + + public addAction(_descriptor: IActionDescriptor): IDisposable { + if ((typeof _descriptor.id !== 'string') || (typeof _descriptor.label !== 'string') || (typeof _descriptor.run !== 'function')) { + throw new Error('Invalid action descriptor, `id`, `label` and `run` are required properties!'); + } + if (!this._standaloneKeybindingService) { + console.warn('Cannot add keybinding because the editor is configured with an unrecognized KeybindingService'); + return emptyDisposable; + } + + // Read descriptor options + const id = _descriptor.id; + const label = _descriptor.label; + const precondition = ContextKeyExpr.and( + ContextKeyExpr.equals('editorId', this.getId()), + ContextKeyExpr.deserialize(_descriptor.precondition) + ); + const keybindings = _descriptor.keybindings; + const keybindingsWhen = ContextKeyExpr.and( + precondition, + ContextKeyExpr.deserialize(_descriptor.keybindingContext) + ); + const contextMenuGroupId = _descriptor.contextMenuGroupId || null; + const contextMenuOrder = _descriptor.contextMenuOrder || 0; + const run = (): TPromise => { + const r = _descriptor.run(this); + return r ? r : TPromise.as(void 0); + }; + + + let toDispose: IDisposable[] = []; + + // Generate a unique id to allow the same descriptor.id across multiple editor instances + const uniqueId = this.getId() + ':' + id; + + // Register the command + toDispose.push(CommandsRegistry.registerCommand(uniqueId, run)); + + // Register the context menu item + if (contextMenuGroupId) { + let menuItem: IMenuItem = { + command: { + id: uniqueId, + title: label + }, + when: precondition, + group: contextMenuGroupId, + order: contextMenuOrder + }; + toDispose.push(MenuRegistry.appendMenuItem(MenuId.EditorContext, menuItem)); + } + + // Register the keybindings + if (Array.isArray(keybindings)) { + toDispose = toDispose.concat( + keybindings.map((kb) => { + return this._standaloneKeybindingService.addDynamicKeybinding(uniqueId, kb, run, keybindingsWhen); + }) + ); + } + + // Finally, register an internal editor action + let internalAction = new InternalEditorAction( + uniqueId, + label, + label, + precondition, + run, + this._contextKeyService + ); + + // Store it under the original id, such that trigger with the original id will work + this._actions[id] = internalAction; + toDispose.push({ + dispose: () => { + delete this._actions[id]; + } + }); + + return combinedDisposable(toDispose); + } +} + +export class StandaloneEditor extends StandaloneCodeEditor implements IStandaloneCodeEditor { + + private _contextViewService: IEditorContextViewService; + private _ownsModel: boolean; + + constructor( + domElement: HTMLElement, + options: IEditorConstructionOptions, + toDispose: IDisposable, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @ICommandService commandService: ICommandService, + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService keybindingService: IKeybindingService, + @IContextViewService contextViewService: IContextViewService, + @IStandaloneThemeService themeService: IStandaloneThemeService + ) { + options = options || {}; + if (typeof options.theme === 'string') { + themeService.setTheme(options.theme); + } + let model: IModel = options.model; + delete options.model; + super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService, keybindingService, themeService); + + this._contextViewService = contextViewService; + this._register(toDispose); + + if (typeof model === 'undefined') { + model = (self).monaco.editor.createModel(options.value || '', options.language || 'text/plain'); + this._ownsModel = true; + } else { + this._ownsModel = false; + } + + this._attachModel(model); + if (model) { + let e: IModelChangedEvent = { + oldModelUrl: null, + newModelUrl: model.uri + }; + this._onDidChangeModel.fire(e); + } + } + + public dispose(): void { + super.dispose(); + } + + public destroy(): void { + this.dispose(); + } + + _attachModel(model: IModel): void { + super._attachModel(model); + if (this._view) { + this._contextViewService.setContainer(this._view.domNode.domNode); + } + } + + _postDetachModelCleanup(detachedModel: IModel): void { + super._postDetachModelCleanup(detachedModel); + if (detachedModel && this._ownsModel) { + detachedModel.dispose(); + this._ownsModel = false; + } + } +} + +export class StandaloneDiffEditor extends DiffEditorWidget implements IStandaloneDiffEditor { + + private _contextViewService: IEditorContextViewService; + private _standaloneKeybindingService: StandaloneKeybindingService; + + constructor( + domElement: HTMLElement, + options: IDiffEditorConstructionOptions, + toDispose: IDisposable, + @IInstantiationService instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService keybindingService: IKeybindingService, + @IContextViewService contextViewService: IContextViewService, + @IEditorWorkerService editorWorkerService: IEditorWorkerService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @IStandaloneThemeService themeService: IStandaloneThemeService, + @IMessageService messageService: IMessageService + ) { + options = options || {}; + if (typeof options.theme === 'string') { + options.theme = themeService.setTheme(options.theme); + } + + super(domElement, options, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, messageService); + + if (keybindingService instanceof StandaloneKeybindingService) { + this._standaloneKeybindingService = keybindingService; + } + + this._contextViewService = contextViewService; + + this._register(toDispose); + + this._contextViewService.setContainer(this._containerDomElement); + } + + public dispose(): void { + super.dispose(); + } + + public destroy(): void { + this.dispose(); + } + + protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: IEditorOptions): CodeEditor { + return instantiationService.createInstance(StandaloneCodeEditor, container, options); + } + + public getOriginalEditor(): IStandaloneCodeEditor { + return super.getOriginalEditor(); + } + + public getModifiedEditor(): IStandaloneCodeEditor { + return super.getModifiedEditor(); + } + + public addCommand(keybinding: number, handler: ICommandHandler, context: string): string { + return this.getModifiedEditor().addCommand(keybinding, handler, context); + } + + public createContextKey(key: string, defaultValue: T): IContextKey { + return this.getModifiedEditor().createContextKey(key, defaultValue); + } + + public addAction(descriptor: IActionDescriptor): IDisposable { + return this.getModifiedEditor().addAction(descriptor); + } +} diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts new file mode 100644 index 0000000000..fc584bfb8a --- /dev/null +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -0,0 +1,398 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./standalone-tokens'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICodeEditor, ContentWidgetPositionPreference, OverlayWidgetPositionPreference, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { StandaloneEditor, IStandaloneCodeEditor, StandaloneDiffEditor, IStandaloneDiffEditor, IEditorConstructionOptions, IDiffEditorConstructionOptions } from 'vs/editor/standalone/browser/standaloneCodeEditor'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { IEditorOverrideServices, DynamicStandaloneServices, StaticServices } from 'vs/editor/standalone/browser/standaloneServices'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { OpenerService } from 'vs/platform/opener/browser/openerService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { Colorizer, IColorizerElementOptions, IColorizerOptions } from 'vs/editor/standalone/browser/colorizer'; +import { SimpleEditorService, SimpleEditorModelResolverService } from 'vs/editor/standalone/browser/simpleServices'; +import * as modes from 'vs/editor/common/modes'; +import { IWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from 'vs/editor/common/services/webWorker'; +import { IMarkerData, IMarker } from 'vs/platform/markers/common/markers'; +import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; +import { IEditorService } from 'vs/platform/editor/common/editor'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { NULL_STATE, nullTokenize } from 'vs/editor/common/modes/nullMode'; +import { IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { Token } from 'vs/editor/common/core/token'; +import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { IMessageService } from 'vs/platform/message/common/message'; + +/** + * @internal + */ +export function setupServices(overrides: IEditorOverrideServices): any { + return StaticServices.init(overrides); +} + +function withAllStandaloneServices(domElement: HTMLElement, override: IEditorOverrideServices, callback: (services: DynamicStandaloneServices) => T): T { + let services = new DynamicStandaloneServices(domElement, override); + + // The editorService is a lovely beast. It needs to point back to the code editor instance... + let simpleEditorService: SimpleEditorService = null; + if (!services.has(IEditorService)) { + simpleEditorService = new SimpleEditorService(); + services.set(IEditorService, simpleEditorService); + } + + let simpleEditorModelResolverService: SimpleEditorModelResolverService = null; + if (!services.has(ITextModelService)) { + simpleEditorModelResolverService = new SimpleEditorModelResolverService(); + services.set(ITextModelService, simpleEditorModelResolverService); + } + + if (!services.has(IOpenerService)) { + services.set(IOpenerService, new OpenerService(services.get(IEditorService), services.get(ICommandService))); + } + + let result = callback(services); + + if (simpleEditorService) { + simpleEditorService.setEditor(result); + } + + if (simpleEditorModelResolverService) { + simpleEditorModelResolverService.setEditor(result); + } + + return result; +} + +/** + * Create a new editor under `domElement`. + * `domElement` should be empty (not contain other dom nodes). + * The editor will read the size of `domElement`. + */ +export function create(domElement: HTMLElement, options?: IEditorConstructionOptions, override?: IEditorOverrideServices): IStandaloneCodeEditor { + return withAllStandaloneServices(domElement, override, (services) => { + return new StandaloneEditor( + domElement, + options, + services, + services.get(IInstantiationService), + services.get(ICodeEditorService), + services.get(ICommandService), + services.get(IContextKeyService), + services.get(IKeybindingService), + services.get(IContextViewService), + services.get(IStandaloneThemeService) + ); + }); +} + +/** + * Emitted when an editor is created. + * Creating a diff editor might cause this listener to be invoked with the two editors. + * @event + */ +export function onDidCreateEditor(listener: (codeEditor: ICodeEditor) => void): IDisposable { + return StaticServices.codeEditorService.get().onCodeEditorAdd((editor) => { + listener(editor); + }); +} + +/** + * Create a new diff editor under `domElement`. + * `domElement` should be empty (not contain other dom nodes). + * The editor will read the size of `domElement`. + */ +export function createDiffEditor(domElement: HTMLElement, options?: IDiffEditorConstructionOptions, override?: IEditorOverrideServices): IStandaloneDiffEditor { + return withAllStandaloneServices(domElement, override, (services) => { + return new StandaloneDiffEditor( + domElement, + options, + services, + services.get(IInstantiationService), + services.get(IContextKeyService), + services.get(IKeybindingService), + services.get(IContextViewService), + services.get(IEditorWorkerService), + services.get(ICodeEditorService), + services.get(IStandaloneThemeService), + services.get(IMessageService) + ); + }); +} + +export interface IDiffNavigator { + revealFirst: boolean; + + canNavigate(): boolean; + next(): void; + previous(): void; + dispose(): void; +} + +export interface IDiffNavigatorOptions { + readonly followsCaret?: boolean; + readonly ignoreCharChanges?: boolean; + readonly alwaysRevealFirst?: boolean; +} + +export function createDiffNavigator(diffEditor: IStandaloneDiffEditor, opts?: IDiffNavigatorOptions): IDiffNavigator { + return new DiffNavigator(diffEditor, opts); +} + +function doCreateModel(value: string, mode: TPromise, uri?: URI): editorCommon.IModel { + return StaticServices.modelService.get().createModel(value, mode, uri); +} + +/** + * Create a new editor model. + * You can specify the language that should be set for this model or let the language be inferred from the `uri`. + */ +export function createModel(value: string, language?: string, uri?: URI): editorCommon.IModel { + value = value || ''; + + if (!language) { + let path = uri ? uri.path : null; + + let firstLF = value.indexOf('\n'); + let firstLine = value; + if (firstLF !== -1) { + firstLine = value.substring(0, firstLF); + } + + return doCreateModel(value, StaticServices.modeService.get().getOrCreateModeByFilenameOrFirstLine(path, firstLine), uri); + } + return doCreateModel(value, StaticServices.modeService.get().getOrCreateMode(language), uri); +} + +/** + * Change the language for a model. + */ +export function setModelLanguage(model: editorCommon.IModel, language: string): void { + StaticServices.modelService.get().setMode(model, StaticServices.modeService.get().getOrCreateMode(language)); +} + +/** + * Set the markers for a model. + */ +export function setModelMarkers(model: editorCommon.IModel, owner: string, markers: IMarkerData[]): void { + if (model) { + StaticServices.markerService.get().changeOne(owner, model.uri, markers); + } +} + +/** + * Get markers for owner and/or resource + * @returns {IMarker[]} list of markers + * @param filter + */ +export function getModelMarkers(filter: { owner?: string, resource?: URI, take?: number }): IMarker[] { + return StaticServices.markerService.get().read(filter); +} + +/** + * Get the model that has `uri` if it exists. + */ +export function getModel(uri: URI): editorCommon.IModel { + return StaticServices.modelService.get().getModel(uri); +} + +/** + * Get all the created models. + */ +export function getModels(): editorCommon.IModel[] { + return StaticServices.modelService.get().getModels(); +} + +/** + * Emitted when a model is created. + * @event + */ +export function onDidCreateModel(listener: (model: editorCommon.IModel) => void): IDisposable { + return StaticServices.modelService.get().onModelAdded(listener); +} + +/** + * Emitted right before a model is disposed. + * @event + */ +export function onWillDisposeModel(listener: (model: editorCommon.IModel) => void): IDisposable { + return StaticServices.modelService.get().onModelRemoved(listener); +} + +/** + * Emitted when a different language is set to a model. + * @event + */ +export function onDidChangeModelLanguage(listener: (e: { readonly model: editorCommon.IModel; readonly oldLanguage: string; }) => void): IDisposable { + return StaticServices.modelService.get().onModelModeChanged((e) => { + listener({ + model: e.model, + oldLanguage: e.oldModeId + }); + }); +} + +/** + * Create a new web worker that has model syncing capabilities built in. + * Specify an AMD module to load that will `create` an object that will be proxied. + */ +export function createWebWorker(opts: IWebWorkerOptions): MonacoWebWorker { + return actualCreateWebWorker(StaticServices.modelService.get(), opts); +} + +/** + * Colorize the contents of `domNode` using attribute `data-lang`. + */ +export function colorizeElement(domNode: HTMLElement, options: IColorizerElementOptions): TPromise { + return Colorizer.colorizeElement(StaticServices.standaloneThemeService.get(), StaticServices.modeService.get(), domNode, options); +} + +/** + * Colorize `text` using language `languageId`. + */ +export function colorize(text: string, languageId: string, options: IColorizerOptions): TPromise { + return Colorizer.colorize(StaticServices.modeService.get(), text, languageId, options); +} + +/** + * Colorize a line in a model. + */ +export function colorizeModelLine(model: editorCommon.IModel, lineNumber: number, tabSize: number = 4): string { + return Colorizer.colorizeModelLine(model, lineNumber, tabSize); +} + +/** + * @internal + */ +function getSafeTokenizationSupport(languageId: string): modes.ITokenizationSupport { + let tokenizationSupport = modes.TokenizationRegistry.get(languageId); + if (tokenizationSupport) { + return tokenizationSupport; + } + return { + getInitialState: () => NULL_STATE, + tokenize: (line: string, state: modes.IState, deltaOffset: number) => nullTokenize(languageId, line, state, deltaOffset), + tokenize2: undefined, + }; +} + +/** + * Tokenize `text` using language `languageId` + */ +export function tokenize(text: string, languageId: string): Token[][] { + let modeService = StaticServices.modeService.get(); + // Needed in order to get the mode registered for subsequent look-ups + modeService.getOrCreateMode(languageId); + + let tokenizationSupport = getSafeTokenizationSupport(languageId); + let lines = text.split(/\r\n|\r|\n/); + let result: Token[][] = []; + let state = tokenizationSupport.getInitialState(); + for (let i = 0, len = lines.length; i < len; i++) { + let line = lines[i]; + let tokenizationResult = tokenizationSupport.tokenize(line, state, 0); + + result[i] = tokenizationResult.tokens; + state = tokenizationResult.endState; + } + return result; +} + +/** + * Define a new theme. + */ +export function defineTheme(themeName: string, themeData: IStandaloneThemeData): void { + StaticServices.standaloneThemeService.get().defineTheme(themeName, themeData); +} + +/** + * Switches to a theme. + */ +export function setTheme(themeName: string): void { + StaticServices.standaloneThemeService.get().setTheme(themeName); +} + +/** + * @internal + * -------------------------------------------- + * This is repeated here so it can be exported + * because TS inlines const enums + * -------------------------------------------- + */ +enum ScrollType { + Smooth = 0, + Immediate = 1, +} + +/** + * @internal + */ +export function createMonacoEditorAPI(): typeof monaco.editor { + return { + // methods + create: create, + onDidCreateEditor: onDidCreateEditor, + createDiffEditor: createDiffEditor, + createDiffNavigator: createDiffNavigator, + + createModel: createModel, + setModelLanguage: setModelLanguage, + setModelMarkers: setModelMarkers, + getModelMarkers: getModelMarkers, + getModels: getModels, + getModel: getModel, + onDidCreateModel: onDidCreateModel, + onWillDisposeModel: onWillDisposeModel, + onDidChangeModelLanguage: onDidChangeModelLanguage, + + + createWebWorker: createWebWorker, + colorizeElement: colorizeElement, + colorize: colorize, + colorizeModelLine: colorizeModelLine, + tokenize: tokenize, + defineTheme: defineTheme, + setTheme: setTheme, + + // enums + ScrollbarVisibility: ScrollbarVisibility, + WrappingIndent: editorOptions.WrappingIndent, + OverviewRulerLane: editorCommon.OverviewRulerLane, + EndOfLinePreference: editorCommon.EndOfLinePreference, + DefaultEndOfLine: editorCommon.DefaultEndOfLine, + EndOfLineSequence: editorCommon.EndOfLineSequence, + TrackedRangeStickiness: editorCommon.TrackedRangeStickiness, + CursorChangeReason: CursorChangeReason, + MouseTargetType: MouseTargetType, + TextEditorCursorStyle: editorOptions.TextEditorCursorStyle, + TextEditorCursorBlinkingStyle: editorOptions.TextEditorCursorBlinkingStyle, + ContentWidgetPositionPreference: ContentWidgetPositionPreference, + OverlayWidgetPositionPreference: OverlayWidgetPositionPreference, + RenderMinimap: editorOptions.RenderMinimap, + ScrollType: ScrollType, + + // classes + InternalEditorOptions: editorOptions.InternalEditorOptions, + BareFontInfo: BareFontInfo, + FontInfo: FontInfo, + TextModelResolvedOptions: editorCommon.TextModelResolvedOptions, + FindMatch: editorCommon.FindMatch, + + // vars + EditorType: editorCommon.EditorType + }; +} diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts new file mode 100644 index 0000000000..a57e4d0cdb --- /dev/null +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -0,0 +1,743 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; +import { IMonarchLanguage } from 'vs/editor/standalone/common/monarch/monarchTypes'; +import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; +import { StaticServices } from 'vs/editor/standalone/browser/standaloneServices'; +import * as modes from 'vs/editor/common/modes'; +import { LanguageConfiguration, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { toThenable } from 'vs/base/common/async'; +import { compile } from 'vs/editor/standalone/common/monarch/monarchCompile'; +import { createTokenizationSupport } from 'vs/editor/standalone/common/monarch/monarchLexer'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { IMarkerData } from 'vs/platform/markers/common/markers'; +import { Token, TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; + +/** + * Register information about a new language. + */ +export function register(language: ILanguageExtensionPoint): void { + ModesRegistry.registerLanguage(language); +} + +/** + * Get the information of all the registered languages. + */ +export function getLanguages(): ILanguageExtensionPoint[] { + let result: ILanguageExtensionPoint[] = []; + result = result.concat(ModesRegistry.getLanguages()); + return result; +} + +/** + * An event emitted when a language is first time needed (e.g. a model has it set). + * @event + */ +export function onLanguage(languageId: string, callback: () => void): IDisposable { + let disposable = StaticServices.modeService.get().onDidCreateMode((mode) => { + if (mode.getId() === languageId) { + // stop listening + disposable.dispose(); + // invoke actual listener + callback(); + } + }); + return disposable; +} + +/** + * Set the editing configuration for a language. + */ +export function setLanguageConfiguration(languageId: string, configuration: LanguageConfiguration): IDisposable { + let languageIdentifier = StaticServices.modeService.get().getLanguageIdentifier(languageId); + if (!languageIdentifier) { + throw new Error(`Cannot set configuration for unknown language ${languageId}`); + } + return LanguageConfigurationRegistry.register(languageIdentifier, configuration); +} + +/** + * @internal + */ +export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { + + private readonly _standaloneThemeService: IStandaloneThemeService; + private readonly _languageIdentifier: modes.LanguageIdentifier; + private readonly _actual: TokensProvider; + + constructor(standaloneThemeService: IStandaloneThemeService, languageIdentifier: modes.LanguageIdentifier, actual: TokensProvider) { + this._standaloneThemeService = standaloneThemeService; + this._languageIdentifier = languageIdentifier; + this._actual = actual; + } + + public getInitialState(): modes.IState { + return this._actual.getInitialState(); + } + + private _toClassicTokens(tokens: IToken[], language: string, offsetDelta: number): Token[] { + let result: Token[] = []; + let previousStartIndex: number = 0; + for (let i = 0, len = tokens.length; i < len; i++) { + const t = tokens[i]; + let startIndex = t.startIndex; + + // Prevent issues stemming from a buggy external tokenizer. + if (i === 0) { + // Force first token to start at first index! + startIndex = 0; + } else if (startIndex < previousStartIndex) { + // Force tokens to be after one another! + startIndex = previousStartIndex; + } + + result[i] = new Token(startIndex + offsetDelta, t.scopes, language); + + previousStartIndex = startIndex; + } + return result; + } + + public tokenize(line: string, state: modes.IState, offsetDelta: number): TokenizationResult { + let actualResult = this._actual.tokenize(line, state); + let tokens = this._toClassicTokens(actualResult.tokens, this._languageIdentifier.language, offsetDelta); + + let endState: modes.IState; + // try to save an object if possible + if (actualResult.endState.equals(state)) { + endState = state; + } else { + endState = actualResult.endState; + } + + return new TokenizationResult(tokens, endState); + } + + private _toBinaryTokens(tokens: IToken[], offsetDelta: number): Uint32Array { + const languageId = this._languageIdentifier.id; + const tokenTheme = this._standaloneThemeService.getTheme().tokenTheme; + + let result: number[] = [], resultLen = 0; + let previousStartIndex: number = 0; + for (let i = 0, len = tokens.length; i < len; i++) { + const t = tokens[i]; + const metadata = tokenTheme.match(languageId, t.scopes); + if (resultLen > 0 && result[resultLen - 1] === metadata) { + // same metadata + continue; + } + + let startIndex = t.startIndex; + + // Prevent issues stemming from a buggy external tokenizer. + if (i === 0) { + // Force first token to start at first index! + startIndex = 0; + } else if (startIndex < previousStartIndex) { + // Force tokens to be after one another! + startIndex = previousStartIndex; + } + + result[resultLen++] = startIndex + offsetDelta; + result[resultLen++] = metadata; + + previousStartIndex = startIndex; + } + + let actualResult = new Uint32Array(resultLen); + for (let i = 0; i < resultLen; i++) { + actualResult[i] = result[i]; + } + return actualResult; + } + + public tokenize2(line: string, state: modes.IState, offsetDelta: number): TokenizationResult2 { + let actualResult = this._actual.tokenize(line, state); + let tokens = this._toBinaryTokens(actualResult.tokens, offsetDelta); + + let endState: modes.IState; + // try to save an object if possible + if (actualResult.endState.equals(state)) { + endState = state; + } else { + endState = actualResult.endState; + } + + return new TokenizationResult2(tokens, endState); + } +} + +/** + * A token. + */ +export interface IToken { + startIndex: number; + scopes: string; +} + +/** + * The result of a line tokenization. + */ +export interface ILineTokens { + /** + * The list of tokens on the line. + */ + tokens: IToken[]; + /** + * The tokenization end state. + * A pointer will be held to this and the object should not be modified by the tokenizer after the pointer is returned. + */ + endState: modes.IState; +} + +/** + * A "manual" provider of tokens. + */ +export interface TokensProvider { + /** + * The initial state of a language. Will be the state passed in to tokenize the first line. + */ + getInitialState(): modes.IState; + /** + * Tokenize a line given the state at the beginning of the line. + */ + tokenize(line: string, state: modes.IState): ILineTokens; +} + +/** + * Set the tokens provider for a language (manual implementation). + */ +export function setTokensProvider(languageId: string, provider: TokensProvider): IDisposable { + let languageIdentifier = StaticServices.modeService.get().getLanguageIdentifier(languageId); + if (!languageIdentifier) { + throw new Error(`Cannot set tokens provider for unknown language ${languageId}`); + } + let adapter = new TokenizationSupport2Adapter(StaticServices.standaloneThemeService.get(), languageIdentifier, provider); + return modes.TokenizationRegistry.register(languageId, adapter); +} + +/** + * Set the tokens provider for a language (monarch implementation). + */ +export function setMonarchTokensProvider(languageId: string, languageDef: IMonarchLanguage): IDisposable { + let lexer = compile(languageId, languageDef); + let adapter = createTokenizationSupport(StaticServices.modeService.get(), StaticServices.standaloneThemeService.get(), languageId, lexer); + return modes.TokenizationRegistry.register(languageId, adapter); +} + +/** + * Register a reference provider (used by e.g. reference search). + */ +export function registerReferenceProvider(languageId: string, provider: modes.ReferenceProvider): IDisposable { + return modes.ReferenceProviderRegistry.register(languageId, provider); +} + +/** + * Register a rename provider (used by e.g. rename symbol). + */ +export function registerRenameProvider(languageId: string, provider: modes.RenameProvider): IDisposable { + return modes.RenameProviderRegistry.register(languageId, provider); +} + +/** + * Register a signature help provider (used by e.g. paremeter hints). + */ +export function registerSignatureHelpProvider(languageId: string, provider: modes.SignatureHelpProvider): IDisposable { + return modes.SignatureHelpProviderRegistry.register(languageId, provider); +} + +/** + * Register a hover provider (used by e.g. editor hover). + */ +export function registerHoverProvider(languageId: string, provider: modes.HoverProvider): IDisposable { + return modes.HoverProviderRegistry.register(languageId, { + provideHover: (model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Thenable => { + let word = model.getWordAtPosition(position); + + return toThenable(provider.provideHover(model, position, token)).then((value) => { + if (!value) { + return undefined; + } + if (!value.range && word) { + value.range = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn); + } + if (!value.range) { + value.range = new Range(position.lineNumber, position.column, position.lineNumber, position.column); + } + return value; + }); + } + }); +} + +/** + * Register a document symbol provider (used by e.g. outline). + */ +export function registerDocumentSymbolProvider(languageId: string, provider: modes.DocumentSymbolProvider): IDisposable { + return modes.DocumentSymbolProviderRegistry.register(languageId, provider); +} + +/** + * Register a document highlight provider (used by e.g. highlight occurrences). + */ +export function registerDocumentHighlightProvider(languageId: string, provider: modes.DocumentHighlightProvider): IDisposable { + return modes.DocumentHighlightProviderRegistry.register(languageId, provider); +} + +/** + * Register a definition provider (used by e.g. go to definition). + */ +export function registerDefinitionProvider(languageId: string, provider: modes.DefinitionProvider): IDisposable { + return modes.DefinitionProviderRegistry.register(languageId, provider); +} + +/** + * Register a implementation provider (used by e.g. go to implementation). + */ +export function registerImplementationProvider(languageId: string, provider: modes.ImplementationProvider): IDisposable { + return modes.ImplementationProviderRegistry.register(languageId, provider); +} + +/** + * Register a type definition provider (used by e.g. go to type definition). + */ +export function registerTypeDefinitionProvider(languageId: string, provider: modes.TypeDefinitionProvider): IDisposable { + return modes.TypeDefinitionProviderRegistry.register(languageId, provider); +} + +/** + * Register a code lens provider (used by e.g. inline code lenses). + */ +export function registerCodeLensProvider(languageId: string, provider: modes.CodeLensProvider): IDisposable { + return modes.CodeLensProviderRegistry.register(languageId, provider); +} + +/** + * Register a code action provider (used by e.g. quick fix). + */ +export function registerCodeActionProvider(languageId: string, provider: CodeActionProvider): IDisposable { + return modes.CodeActionProviderRegistry.register(languageId, { + provideCodeActions: (model: editorCommon.IReadOnlyModel, range: Range, token: CancellationToken): modes.Command[] | Thenable => { + let markers = StaticServices.markerService.get().read({ resource: model.uri }).filter(m => { + return Range.areIntersectingOrTouching(m, range); + }); + return provider.provideCodeActions(model, range, { markers }, token); + } + }); +} + +/** + * Register a formatter that can handle only entire models. + */ +export function registerDocumentFormattingEditProvider(languageId: string, provider: modes.DocumentFormattingEditProvider): IDisposable { + return modes.DocumentFormattingEditProviderRegistry.register(languageId, provider); +} + +/** + * Register a formatter that can handle a range inside a model. + */ +export function registerDocumentRangeFormattingEditProvider(languageId: string, provider: modes.DocumentRangeFormattingEditProvider): IDisposable { + return modes.DocumentRangeFormattingEditProviderRegistry.register(languageId, provider); +} + +/** + * Register a formatter than can do formatting as the user types. + */ +export function registerOnTypeFormattingEditProvider(languageId: string, provider: modes.OnTypeFormattingEditProvider): IDisposable { + return modes.OnTypeFormattingEditProviderRegistry.register(languageId, provider); +} + +/** + * Register a link provider that can find links in text. + */ +export function registerLinkProvider(languageId: string, provider: modes.LinkProvider): IDisposable { + return modes.LinkProviderRegistry.register(languageId, provider); +} + +/** + * Register a completion item provider (use by e.g. suggestions). + */ +export function registerCompletionItemProvider(languageId: string, provider: CompletionItemProvider): IDisposable { + let adapter = new SuggestAdapter(provider); + return modes.SuggestRegistry.register(languageId, { + triggerCharacters: provider.triggerCharacters, + provideCompletionItems: (model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Thenable => { + return adapter.provideCompletionItems(model, position, token); + }, + resolveCompletionItem: (model: editorCommon.IReadOnlyModel, position: Position, suggestion: modes.ISuggestion, token: CancellationToken): Thenable => { + return adapter.resolveCompletionItem(model, position, suggestion, token); + } + }); +} + +/** + * Register a document color provider (used by Color Picker, Color Decorator). + */ +export function registerColorProvider(languageId: string, provider: modes.DocumentColorProvider): IDisposable { + return modes.ColorProviderRegistry.register(languageId, provider); +} + +/** + * Contains additional diagnostic information about the context in which + * a [code action](#CodeActionProvider.provideCodeActions) is run. + */ +export interface CodeActionContext { + + /** + * An array of diagnostics. + * + * @readonly + */ + readonly markers: IMarkerData[]; +} + +/** + * The code action interface defines the contract between extensions and + * the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature. + */ +export interface CodeActionProvider { + /** + * Provide commands for the given document and range. + */ + provideCodeActions(model: editorCommon.IReadOnlyModel, range: Range, context: CodeActionContext, token: CancellationToken): modes.Command[] | Thenable; +} + +/** + * Completion item kinds. + */ +export enum CompletionItemKind { + Text, + Method, + Function, + Constructor, + Field, + Variable, + Class, + Interface, + Module, + Property, + Unit, + Value, + Enum, + Keyword, + Snippet, + Color, + File, + Reference, + Folder +} + +/** + * A snippet string is a template which allows to insert text + * and to control the editor cursor when insertion happens. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Variables are defined with `$name` and + * `${name:default value}`. The full snippet syntax is documented + * [here](http://code.visualstudio.com/docs/editor/userdefinedsnippets#_creating-your-own-snippets). + */ +export interface SnippetString { + + /** + * The snippet string. + */ + value: string; +} + +/** + * A completion item represents a text snippet that is + * proposed to complete text that is being typed. + */ +export interface CompletionItem { + /** + * The label of this completion item. By default + * this is also the text that is inserted when selecting + * this completion. + */ + label: string; + /** + * The kind of this completion item. Based on the kind + * an icon is chosen by the editor. + */ + kind: CompletionItemKind; + /** + * A human-readable string with additional information + * about this item, like type or symbol information. + */ + detail?: string; + /** + * A human-readable string that represents a doc-comment. + */ + documentation?: string; + /** + * A string that should be used when comparing this item + * with other items. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + sortText?: string; + /** + * A string that should be used when filtering a set of + * completion items. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + filterText?: string; + /** + * A string or snippet that should be inserted in a document when selecting + * this completion. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + insertText?: string | SnippetString; + /** + * A range of text that should be replaced by this completion item. + * + * Defaults to a range from the start of the [current word](#TextDocument.getWordRangeAtPosition) to the + * current position. + * + * *Note:* The range must be a [single line](#Range.isSingleLine) and it must + * [contain](#Range.contains) the position at which completion has been [requested](#CompletionItemProvider.provideCompletionItems). + */ + range?: Range; + /** + * @deprecated **Deprecated** in favor of `CompletionItem.insertText` and `CompletionItem.range`. + * + * ~~An [edit](#TextEdit) which is applied to a document when selecting + * this completion. When an edit is provided the value of + * [insertText](#CompletionItem.insertText) is ignored.~~ + * + * ~~The [range](#Range) of the edit must be single-line and on the same + * line completions were [requested](#CompletionItemProvider.provideCompletionItems) at.~~ + */ + textEdit?: editorCommon.ISingleEditOperation; +} +/** + * Represents a collection of [completion items](#CompletionItem) to be presented + * in the editor. + */ +export interface CompletionList { + /** + * This list it not complete. Further typing should result in recomputing + * this list. + */ + isIncomplete?: boolean; + /** + * The completion items. + */ + items: CompletionItem[]; +} +/** + * The completion item provider interface defines the contract between extensions and + * the [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). + * + * When computing *complete* completion items is expensive, providers can optionally implement + * the `resolveCompletionItem`-function. In that case it is enough to return completion + * items with a [label](#CompletionItem.label) from the + * [provideCompletionItems](#CompletionItemProvider.provideCompletionItems)-function. Subsequently, + * when a completion item is shown in the UI and gains focus this provider is asked to resolve + * the item, like adding [doc-comment](#CompletionItem.documentation) or [details](#CompletionItem.detail). + */ +export interface CompletionItemProvider { + triggerCharacters?: string[]; + /** + * Provide completion items for the given position and document. + */ + provideCompletionItems(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): CompletionItem[] | Thenable | CompletionList | Thenable; + /** + * Given a completion item fill in more data, like [doc-comment](#CompletionItem.documentation) + * or [details](#CompletionItem.detail). + * + * The editor will only resolve a completion item once. + */ + resolveCompletionItem?(item: CompletionItem, token: CancellationToken): CompletionItem | Thenable; +} + +interface ISuggestion2 extends modes.ISuggestion { + _actual: CompletionItem; +} +function convertKind(kind: CompletionItemKind): modes.SuggestionType { + switch (kind) { + case CompletionItemKind.Method: return 'method'; + case CompletionItemKind.Function: return 'function'; + case CompletionItemKind.Constructor: return 'constructor'; + case CompletionItemKind.Field: return 'field'; + case CompletionItemKind.Variable: return 'variable'; + case CompletionItemKind.Class: return 'class'; + case CompletionItemKind.Interface: return 'interface'; + case CompletionItemKind.Module: return 'module'; + case CompletionItemKind.Property: return 'property'; + case CompletionItemKind.Unit: return 'unit'; + case CompletionItemKind.Value: return 'value'; + case CompletionItemKind.Enum: return 'enum'; + case CompletionItemKind.Keyword: return 'keyword'; + case CompletionItemKind.Snippet: return 'snippet'; + case CompletionItemKind.Text: return 'text'; + case CompletionItemKind.Color: return 'color'; + case CompletionItemKind.File: return 'file'; + case CompletionItemKind.Reference: return 'reference'; + case CompletionItemKind.Folder: return 'folder'; + } + return 'property'; +} +class SuggestAdapter { + + private _provider: CompletionItemProvider; + + constructor(provider: CompletionItemProvider) { + this._provider = provider; + } + + private static from(item: CompletionItem, position: Position, wordStartPos: Position): ISuggestion2 { + let suggestion: ISuggestion2 = { + _actual: item, + label: item.label, + insertText: item.label, + type: convertKind(item.kind), + detail: item.detail, + documentation: item.documentation, + sortText: item.sortText, + filterText: item.filterText, + snippetType: 'internal' + }; + let editRange = item.textEdit ? item.textEdit.range : item.range; + if (editRange) { + let isSingleLine = (editRange.startLineNumber === editRange.endLineNumber); + + // invalid text edit + if (!isSingleLine || editRange.startLineNumber !== position.lineNumber) { + console.warn('INVALID range, must be single line and on the same line'); + return null; + } + + // insert the text of the edit and create a dedicated + // suggestion-container with overwrite[Before|After] + suggestion.overwriteBefore = position.column - editRange.startColumn; + suggestion.overwriteAfter = editRange.endColumn - position.column; + } else { + suggestion.overwriteBefore = position.column - wordStartPos.column; + suggestion.overwriteAfter = 0; + } + if (item.textEdit) { + suggestion.insertText = item.textEdit.text; + } else if (typeof item.insertText === 'object' && typeof item.insertText.value === 'string') { + suggestion.insertText = item.insertText.value; + suggestion.snippetType = 'textmate'; + } else if (typeof item.insertText === 'string') { + suggestion.insertText = item.insertText; + } + return suggestion; + } + + provideCompletionItems(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Thenable { + + return toThenable(this._provider.provideCompletionItems(model, position, token)).then(value => { + const result: modes.ISuggestResult = { + suggestions: [] + }; + + // default text edit start + let wordStartPos = position; + const word = model.getWordUntilPosition(position); + if (word) { + wordStartPos = new Position(wordStartPos.lineNumber, word.startColumn); + } + + let list: CompletionList; + if (Array.isArray(value)) { + list = { + items: value, + isIncomplete: false + }; + } else if (typeof value === 'object' && Array.isArray(value.items)) { + list = value; + result.incomplete = list.isIncomplete; + } else if (!value) { + // undefined and null are valid results + return undefined; + } else { + // warn about everything else + console.warn('INVALID result from completion provider. expected CompletionItem-array or CompletionList but got:', value); + } + + for (let i = 0; i < list.items.length; i++) { + const item = list.items[i]; + const suggestion = SuggestAdapter.from(item, position, wordStartPos); + if (suggestion) { + result.suggestions.push(suggestion); + } + } + + return result; + }); + } + + resolveCompletionItem(model: editorCommon.IReadOnlyModel, position: Position, suggestion: modes.ISuggestion, token: CancellationToken): Thenable { + if (typeof this._provider.resolveCompletionItem !== 'function') { + return TPromise.as(suggestion); + } + + let item = (suggestion)._actual; + if (!item) { + return TPromise.as(suggestion); + } + + return toThenable(this._provider.resolveCompletionItem(item, token)).then(resolvedItem => { + let wordStartPos = position; + const word = model.getWordUntilPosition(position); + if (word) { + wordStartPos = new Position(wordStartPos.lineNumber, word.startColumn); + } + return SuggestAdapter.from(resolvedItem, position, wordStartPos); + }); + } +} + +/** + * @internal + */ +export function createMonacoLanguagesAPI(): typeof monaco.languages { + return { + register: register, + getLanguages: getLanguages, + onLanguage: onLanguage, + + // provider methods + setLanguageConfiguration: setLanguageConfiguration, + setTokensProvider: setTokensProvider, + setMonarchTokensProvider: setMonarchTokensProvider, + registerReferenceProvider: registerReferenceProvider, + registerRenameProvider: registerRenameProvider, + registerCompletionItemProvider: registerCompletionItemProvider, + registerSignatureHelpProvider: registerSignatureHelpProvider, + registerHoverProvider: registerHoverProvider, + registerDocumentSymbolProvider: registerDocumentSymbolProvider, + registerDocumentHighlightProvider: registerDocumentHighlightProvider, + registerDefinitionProvider: registerDefinitionProvider, + registerImplementationProvider: registerImplementationProvider, + registerTypeDefinitionProvider: registerTypeDefinitionProvider, + registerCodeLensProvider: registerCodeLensProvider, + registerCodeActionProvider: registerCodeActionProvider, + registerDocumentFormattingEditProvider: registerDocumentFormattingEditProvider, + registerDocumentRangeFormattingEditProvider: registerDocumentRangeFormattingEditProvider, + registerOnTypeFormattingEditProvider: registerOnTypeFormattingEditProvider, + registerLinkProvider: registerLinkProvider, + registerColorProvider: registerColorProvider, + + // enums + DocumentHighlightKind: modes.DocumentHighlightKind, + CompletionItemKind: CompletionItemKind, + SymbolKind: modes.SymbolKind, + IndentAction: IndentAction, + }; +} diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts new file mode 100644 index 0000000000..aed0504e72 --- /dev/null +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -0,0 +1,203 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; +import { createDecorator, IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IStorageService, NullStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; +import { + SimpleConfigurationService, SimpleResourceConfigurationService, SimpleMenuService, SimpleMessageService, + SimpleProgressService, StandaloneCommandService, StandaloneKeybindingService, + StandaloneTelemetryService, SimpleWorkspaceContextService +} from 'vs/editor/standalone/browser/simpleServices'; +import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; +import { IMenuService } from 'vs/platform/actions/common/actions'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl'; + +export interface IEditorContextViewService extends IContextViewService { + dispose(): void; + setContainer(domNode: HTMLElement): void; +} + +export interface IEditorOverrideServices { + [index: string]: any; +} + +export module StaticServices { + + const _serviceCollection = new ServiceCollection(); + + export class LazyStaticService { + private _serviceId: ServiceIdentifier; + private _factory: (overrides: IEditorOverrideServices) => T; + private _value: T; + + public get id() { return this._serviceId; } + + constructor(serviceId: ServiceIdentifier, factory: (overrides: IEditorOverrideServices) => T) { + this._serviceId = serviceId; + this._factory = factory; + this._value = null; + } + + public get(overrides?: IEditorOverrideServices): T { + if (!this._value) { + if (overrides) { + this._value = overrides[this._serviceId.toString()]; + } + if (!this._value) { + this._value = this._factory(overrides); + } + if (!this._value) { + throw new Error('Service ' + this._serviceId + ' is missing!'); + } + _serviceCollection.set(this._serviceId, this._value); + } + return this._value; + } + } + + let _all: LazyStaticService[] = []; + + function define(serviceId: ServiceIdentifier, factory: (overrides: IEditorOverrideServices) => T): LazyStaticService { + let r = new LazyStaticService(serviceId, factory); + _all.push(r); + return r; + } + + export function init(overrides: IEditorOverrideServices): [ServiceCollection, IInstantiationService] { + // Create a fresh service collection + let result = new ServiceCollection(); + + // Initialize the service collection with the overrides + for (let serviceId in overrides) { + if (overrides.hasOwnProperty(serviceId)) { + result.set(createDecorator(serviceId), overrides[serviceId]); + } + } + + // Make sure the same static services are present in all service collections + _all.forEach(service => result.set(service.id, service.get(overrides))); + + // Ensure the collection gets the correct instantiation service + let instantiationService = new InstantiationService(result, true); + result.set(IInstantiationService, instantiationService); + + return [result, instantiationService]; + } + + export const instantiationService = define(IInstantiationService, () => new InstantiationService(_serviceCollection, true)); + + const configurationServiceImpl = new SimpleConfigurationService(); + export const configurationService = define(IConfigurationService, () => configurationServiceImpl); + + export const resourceConfigurationService = define(ITextResourceConfigurationService, () => new SimpleResourceConfigurationService(configurationServiceImpl)); + + export const contextService = define(IWorkspaceContextService, () => new SimpleWorkspaceContextService()); + + export const telemetryService = define(ITelemetryService, () => new StandaloneTelemetryService()); + + export const messageService = define(IMessageService, () => new SimpleMessageService()); + + export const markerService = define(IMarkerService, () => new MarkerService()); + + export const modeService = define(IModeService, (o) => new ModeServiceImpl()); + + export const modelService = define(IModelService, (o) => new ModelServiceImpl(markerService.get(o), configurationService.get(o))); + + export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), resourceConfigurationService.get(o), modeService.get(o))); + + export const standaloneThemeService = define(IStandaloneThemeService, () => new StandaloneThemeServiceImpl()); + + export const codeEditorService = define(ICodeEditorService, (o) => new CodeEditorServiceImpl(standaloneThemeService.get(o))); + + export const progressService = define(IProgressService, () => new SimpleProgressService()); + + export const storageService = define(IStorageService, () => NullStorageService); + +} + +export class DynamicStandaloneServices extends Disposable { + + private _serviceCollection: ServiceCollection; + private _instantiationService: IInstantiationService; + + constructor(domElement: HTMLElement, overrides: IEditorOverrideServices) { + super(); + + const [_serviceCollection, _instantiationService] = StaticServices.init(overrides); + this._serviceCollection = _serviceCollection; + this._instantiationService = _instantiationService; + + const configurationService = this.get(IConfigurationService); + const messageService = this.get(IMessageService); + const telemetryService = this.get(ITelemetryService); + + let ensure = (serviceId: ServiceIdentifier, factory: () => T): T => { + let value: T = null; + if (overrides) { + value = overrides[serviceId.toString()]; + } + if (!value) { + value = factory(); + } + this._serviceCollection.set(serviceId, value); + return value; + }; + + let contextKeyService = ensure(IContextKeyService, () => this._register(new ContextKeyService(configurationService))); + + let commandService = ensure(ICommandService, () => new StandaloneCommandService(this._instantiationService)); + + ensure(IKeybindingService, () => this._register(new StandaloneKeybindingService(contextKeyService, commandService, messageService, domElement))); + + let contextViewService = ensure(IContextViewService, () => this._register(new ContextViewService(domElement, telemetryService, messageService))); + + ensure(IContextMenuService, () => this._register(new ContextMenuService(domElement, telemetryService, messageService, contextViewService))); + + ensure(IMenuService, () => new SimpleMenuService(commandService)); + } + + public get(serviceId: ServiceIdentifier): T { + let r = this._serviceCollection.get(serviceId); + if (!r) { + throw new Error('Missing service ' + serviceId); + } + return r; + } + + public set(serviceId: ServiceIdentifier, instance: T): void { + this._serviceCollection.set(serviceId, instance); + } + + public has(serviceId: ServiceIdentifier): boolean { + return this._serviceCollection.has(serviceId); + } +} diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts new file mode 100644 index 0000000000..d42b3ecb5c --- /dev/null +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -0,0 +1,201 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TokenTheme, ITokenThemeRule, generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; +import { IStandaloneThemeService, BuiltinTheme, IStandaloneThemeData, IStandaloneTheme, IColors } from 'vs/editor/standalone/common/standaloneThemeService'; +import { vs, vs_dark, hc_black } from 'vs/editor/standalone/common/themes'; +import * as dom from 'vs/base/browser/dom'; +import { TokenizationRegistry } from 'vs/editor/common/modes'; +import { Color } from 'vs/base/common/color'; +import { Extensions, IColorRegistry, ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; +import { Extensions as ThemingExtensions, IThemingRegistry, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import Event, { Emitter } from 'vs/base/common/event'; + +const VS_THEME_NAME = 'vs'; +const VS_DARK_THEME_NAME = 'vs-dark'; +const HC_BLACK_THEME_NAME = 'hc-black'; + +const colorRegistry = Registry.as(Extensions.ColorContribution); +const themingRegistry = Registry.as(ThemingExtensions.ThemingContribution); + +class StandaloneTheme implements IStandaloneTheme { + public readonly id: string; + public readonly themeName: string; + private rules: ITokenThemeRule[]; + public readonly base: string; + private colors: { [colorId: string]: Color }; + private defaultColors: { [colorId: string]: Color }; + private _tokenTheme: TokenTheme; + + constructor(base: string, name: string, colors: IColors, rules: ITokenThemeRule[]) { + if (name.length > 0) { + this.id = base + ' ' + name; + this.themeName = name; + } else { + this.id = base; + this.themeName = base; + } + this.base = base; + this.rules = rules; + this.colors = {}; + for (let id in colors) { + this.colors[id] = Color.fromHex(colors[id]); + } + this.defaultColors = {}; + } + + public getColor(colorId: ColorIdentifier, useDefault?: boolean): Color { + if (this.colors.hasOwnProperty(colorId)) { + return this.colors[colorId]; + } + if (useDefault !== false) { + return this.getDefault(colorId); + } + return null; + } + + private getDefault(colorId: ColorIdentifier): Color { + if (this.defaultColors.hasOwnProperty(colorId)) { + return this.defaultColors[colorId]; + } + let color = colorRegistry.resolveDefaultColor(colorId, this); + this.defaultColors[colorId] = color; + return color; + } + + public defines(colorId: ColorIdentifier): boolean { + return this.colors.hasOwnProperty(colorId); + } + + public get type() { + switch (this.base) { + case VS_THEME_NAME: return 'light'; + case HC_BLACK_THEME_NAME: return 'hc'; + default: return 'dark'; + } + } + + public get tokenTheme(): TokenTheme { + if (!this._tokenTheme) { + this._tokenTheme = TokenTheme.createFromRawTokenTheme(this.rules); + } + return this._tokenTheme; + } +} + +function isBuiltinTheme(themeName: string): themeName is BuiltinTheme { + return ( + themeName === VS_THEME_NAME + || themeName === VS_DARK_THEME_NAME + || themeName === HC_BLACK_THEME_NAME + ); +} + +function getBuiltinRules(builtinTheme: BuiltinTheme): IStandaloneThemeData { + switch (builtinTheme) { + case VS_THEME_NAME: + return vs; + case VS_DARK_THEME_NAME: + return vs_dark; + case HC_BLACK_THEME_NAME: + return hc_black; + } +} + +function newBuiltInTheme(builtinTheme: BuiltinTheme): StandaloneTheme { + let themeData = getBuiltinRules(builtinTheme); + return new StandaloneTheme(builtinTheme, '', themeData.colors, themeData.rules); +} + +export class StandaloneThemeServiceImpl implements IStandaloneThemeService { + + _serviceBrand: any; + + private _knownThemes: Map; + private _styleElement: HTMLStyleElement; + private _theme: IStandaloneTheme; + private _onThemeChange: Emitter; + + + constructor() { + this._onThemeChange = new Emitter(); + + this._knownThemes = new Map(); + this._knownThemes.set(VS_THEME_NAME, newBuiltInTheme(VS_THEME_NAME)); + this._knownThemes.set(VS_DARK_THEME_NAME, newBuiltInTheme(VS_DARK_THEME_NAME)); + this._knownThemes.set(HC_BLACK_THEME_NAME, newBuiltInTheme(HC_BLACK_THEME_NAME)); + this._styleElement = dom.createStyleSheet(); + this._styleElement.className = 'monaco-colors'; + this.setTheme(VS_THEME_NAME); + } + + public get onThemeChange(): Event { + return this._onThemeChange.event; + } + + public defineTheme(themeName: string, themeData: IStandaloneThemeData): void { + if (!/^[a-z0-9\-]+$/i.test(themeName) || isBuiltinTheme(themeName)) { + throw new Error('Illegal theme name!'); + } + if (!isBuiltinTheme(themeData.base)) { + throw new Error('Illegal theme base!'); + } + + let rules: ITokenThemeRule[] = []; + let colors: IColors = {}; + if (themeData.inherit) { + let baseData = getBuiltinRules(themeData.base); + rules = rules.concat(baseData.rules); + for (let id in baseData.colors) { + colors[id] = baseData.colors[id]; + } + } + rules = rules.concat(themeData.rules); + for (let id in themeData.colors) { + colors[id] = themeData.colors[id]; + } + + this._knownThemes.set(themeName, new StandaloneTheme(themeData.base, themeName, colors, rules)); + } + + public getTheme(): IStandaloneTheme { + return this._theme; + } + + public setTheme(themeName: string): string { + let theme: StandaloneTheme; + if (this._knownThemes.has(themeName)) { + theme = this._knownThemes.get(themeName); + } else { + theme = this._knownThemes.get(VS_THEME_NAME); + } + this._theme = theme; + + let cssRules: string[] = []; + let hasRule: { [rule: string]: boolean; } = {}; + let ruleCollector: ICssStyleCollector = { + addRule: (rule: string) => { + if (!hasRule[rule]) { + cssRules.push(rule); + hasRule[rule] = true; + } + } + }; + themingRegistry.getThemingParticipants().forEach(p => p(theme, ruleCollector)); + + let tokenTheme = theme.tokenTheme; + let colorMap = tokenTheme.getColorMap(); + ruleCollector.addRule(generateTokensCSSForColorMap(colorMap)); + + this._styleElement.innerHTML = cssRules.join('\n'); + + TokenizationRegistry.setColorMap(colorMap); + this._onThemeChange.fire(theme); + + return theme.id; + } +} diff --git a/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts b/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts new file mode 100644 index 0000000000..1cd3b98ca4 --- /dev/null +++ b/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; + +@editorAction +class ToggleHighContrast extends EditorAction { + + private _originalThemeName: string; + + constructor() { + super({ + id: 'editor.action.toggleHighContrast', + label: nls.localize('toggleHighContrast', "Toggle High Contrast Theme"), + alias: 'Toggle High Contrast Theme', + precondition: null + }); + this._originalThemeName = null; + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const standaloneThemeService = accessor.get(IStandaloneThemeService); + if (this._originalThemeName) { + // We must toggle back to the integrator's theme + standaloneThemeService.setTheme(this._originalThemeName); + this._originalThemeName = null; + } else { + this._originalThemeName = standaloneThemeService.getTheme().themeName; + standaloneThemeService.setTheme('hc-black'); + } + } +} diff --git a/src/vs/editor/standalone/common/monarch/monarchCommon.ts b/src/vs/editor/standalone/common/monarch/monarchCommon.ts new file mode 100644 index 0000000000..98a89fdfca --- /dev/null +++ b/src/vs/editor/standalone/common/monarch/monarchCommon.ts @@ -0,0 +1,218 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +/* + * This module exports common types and functionality shared between + * the Monarch compiler that compiles JSON to ILexer, and the Monarch + * Tokenizer (that highlights at runtime) + */ + +/* + * Type definitions to be used internally to Monarch. + * Inside monarch we use fully typed definitions and compiled versions of the more abstract JSON descriptions. + */ + +export const enum MonarchBracket { + None = 0, + Open = 1, + Close = -1 +} + +export interface ILexerMin { + languageId: string; + noThrow: boolean; + ignoreCase: boolean; + usesEmbedded: boolean; + defaultToken: string; + stateNames: Object; +} + +export interface ILexer extends ILexerMin { + maxStack: number; + start: string; + ignoreCase: boolean; + tokenPostfix: string; + + tokenizer: IRule[][]; + brackets: IBracket[]; +} + +export interface IBracket { + token: string; + open: string; + close: string; +} + +export type FuzzyAction = IAction | string; + +export function isFuzzyActionArr(what: FuzzyAction | FuzzyAction[]): what is FuzzyAction[] { + return (Array.isArray(what)); +} + +export function isFuzzyAction(what: FuzzyAction | FuzzyAction[]): what is FuzzyAction { + return !isFuzzyActionArr(what); +} + +export function isString(what: FuzzyAction): what is string { + return (typeof what === 'string'); +} + +export function isIAction(what: FuzzyAction): what is IAction { + return !isString(what); +} + +export interface IRule { + regex: RegExp; + action: FuzzyAction; + matchOnlyAtLineStart: boolean; + name: string; +} + +export interface IAction { + // an action is either a group of actions + group?: FuzzyAction[]; + + // or a function that returns a fresh action + test?: (id: string, matches: string[], state: string, eos: boolean) => FuzzyAction; + + // or it is a declarative action with a token value and various other attributes + token?: string; + tokenSubst?: boolean; + next?: string; + nextEmbedded?: string; + bracket?: MonarchBracket; + log?: string; + switchTo?: string; + goBack?: number; + transform?: (states: string[]) => string[]; +} + +export interface IBranch { + name: string; + value: FuzzyAction; + test: (id: string, matches: string[], state: string, eos: boolean) => boolean; +} + +// Small helper functions + +/** + * Is a string null, undefined, or empty? + */ +export function empty(s: string): boolean { + return (s ? false : true); +} + +/** + * Puts a string to lower case if 'ignoreCase' is set. + */ +export function fixCase(lexer: ILexerMin, str: string): string { + return (lexer.ignoreCase && str ? str.toLowerCase() : str); +} + +/** + * Ensures there are no bad characters in a CSS token class. + */ +export function sanitize(s: string) { + return s.replace(/[&<>'"_]/g, '-'); // used on all output token CSS classes +} + +// Logging + +/** + * Logs a message. + */ +export function log(lexer: ILexerMin, msg: string) { + console.log(`${lexer.languageId}: ${msg}`); +} + +// Throwing errors + +/** + * Throws error. May actually just log the error and continue. + */ +export function throwError(lexer: ILexerMin, msg: string) { + throw new Error(`${lexer.languageId}: ${msg}`); +} + +// Helper functions for rule finding and substitution + +/** + * substituteMatches is used on lexer strings and can substitutes predefined patterns: + * $$ => $ + * $# => id + * $n => matched entry n + * @attr => contents of lexer[attr] + * + * See documentation for more info + */ +export function substituteMatches(lexer: ILexerMin, str: string, id: string, matches: string[], state: string) { + var re = /\$((\$)|(#)|(\d\d?)|[sS](\d\d?)|@(\w+))/g; + var stateMatches: string[] = null; + return str.replace(re, function (full, sub?, dollar?, hash?, n?, s?, attr?, ofs?, total?) { + if (!empty(dollar)) { + return '$'; // $$ + } + if (!empty(hash)) { + return fixCase(lexer, id); // default $# + } + if (!empty(n) && n < matches.length) { + return fixCase(lexer, matches[n]); // $n + } + if (!empty(attr) && lexer && typeof (lexer[attr]) === 'string') { + return lexer[attr]; //@attribute + } + if (stateMatches === null) { // split state on demand + stateMatches = state.split('.'); + stateMatches.unshift(state); + } + if (!empty(s) && s < stateMatches.length) { + return fixCase(lexer, stateMatches[s]); //$Sn + } + return ''; + }); +} + +/** + * Find the tokenizer rules for a specific state (i.e. next action) + */ +export function findRules(lexer: ILexer, state: string): IRule[] { + while (state && state.length > 0) { + var rules = lexer.tokenizer[state]; + if (rules) { + return rules; + } + + var idx = state.lastIndexOf('.'); + if (idx < 0) { + state = null; // no further parent + } else { + state = state.substr(0, idx); + } + } + return null; +} + +/** + * Is a certain state defined? In contrast to 'findRules' this works on a ILexerMin. + * This is used during compilation where we may know the defined states + * but not yet whether the corresponding rules are correct. + */ +export function stateExists(lexer: ILexerMin, state: string): boolean { + while (state && state.length > 0) { + var exist = lexer.stateNames[state]; + if (exist) { + return true; + } + + var idx = state.lastIndexOf('.'); + if (idx < 0) { + state = null; // no further parent + } else { + state = state.substr(0, idx); + } + } + return false; +} diff --git a/src/vs/editor/standalone/common/monarch/monarchCompile.ts b/src/vs/editor/standalone/common/monarch/monarchCompile.ts new file mode 100644 index 0000000000..f136a4844a --- /dev/null +++ b/src/vs/editor/standalone/common/monarch/monarchCompile.ts @@ -0,0 +1,543 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +/* + * This module only exports 'compile' which compiles a JSON language definition + * into a typed and checked ILexer definition. + */ + +import * as objects from 'vs/base/common/objects'; +import * as monarchCommon from 'vs/editor/standalone/common/monarch/monarchCommon'; +import { IMonarchLanguage, IMonarchLanguageBracket } from 'vs/editor/standalone/common/monarch/monarchTypes'; + +/* + * Type helpers + * + * Note: this is just for sanity checks on the JSON description which is + * helpful for the programmer. No checks are done anymore once the lexer is + * already 'compiled and checked'. + * + */ + +function isArrayOf(elemType: (x: any) => boolean, obj: any): boolean { + if (!obj) { + return false; + } + if (!(Array.isArray(obj))) { + return false; + } + var idx: any; + for (idx in obj) { + if (obj.hasOwnProperty(idx)) { + if (!(elemType(obj[idx]))) { + return false; + } + } + } + return true; +} + +function bool(prop: any, def?: boolean, onerr?: () => void): boolean { + if (typeof (prop) === 'boolean') { + return prop; + } + if (onerr && (prop || def === undefined)) { + onerr(); // type is wrong, or there is no default + } + return (def === undefined ? null : def); +} + +function string(prop: any, def?: string, onerr?: () => void): string { + if (typeof (prop) === 'string') { + return prop; + } + if (onerr && (prop || def === undefined)) { + onerr(); // type is wrong, or there is no default + } + return (def === undefined ? null : def); + +} + +// Lexer helpers + +/** + * Compiles a regular expression string, adding the 'i' flag if 'ignoreCase' is set. + * Also replaces @\w+ or sequences with the content of the specified attribute + */ +function compileRegExp(lexer: monarchCommon.ILexerMin, str: string): RegExp { + if (typeof (str) !== 'string') { + return null; + } + + var n = 0; + while (str.indexOf('@') >= 0 && n < 5) { // at most 5 expansions + n++; + str = str.replace(/@(\w+)/g, function (s, attr?) { + var sub = ''; + if (typeof (lexer[attr]) === 'string') { + sub = lexer[attr]; + } else if (lexer[attr] && lexer[attr] instanceof RegExp) { + sub = lexer[attr].source; + } else { + if (lexer[attr] === undefined) { + monarchCommon.throwError(lexer, 'language definition does not contain attribute \'' + attr + '\', used at: ' + str); + } else { + monarchCommon.throwError(lexer, 'attribute reference \'' + attr + '\' must be a string, used at: ' + str); + } + } + return (monarchCommon.empty(sub) ? '' : '(?:' + sub + ')'); + }); + } + + return new RegExp(str, (lexer.ignoreCase ? 'i' : '')); +} + +/** + * Compiles guard functions for case matches. + * This compiles 'cases' attributes into efficient match functions. + * + */ +function selectScrutinee(id: string, matches: string[], state: string, num: number): string { + if (num < 0) { + return id; + } + if (num < matches.length) { + return matches[num]; + } + if (num >= 100) { + num = num - 100; + var parts = state.split('.'); + parts.unshift(state); + if (num < parts.length) { + return parts[num]; + } + } + return null; +} + +function createGuard(lexer: monarchCommon.ILexerMin, ruleName: string, tkey: string, val: monarchCommon.FuzzyAction): monarchCommon.IBranch { + // get the scrutinee and pattern + var scrut = -1; // -1: $!, 0-99: $n, 100+n: $Sn + var oppat = tkey; + var matches: string[] = tkey.match(/^\$(([sS]?)(\d\d?)|#)(.*)$/); + if (matches) { + if (matches[3]) { // if digits + scrut = parseInt(matches[3]); + if (matches[2]) { + scrut = scrut + 100; // if [sS] present + } + } + oppat = matches[4]; + } + // get operator + var op = '~'; + var pat = oppat; + if (!oppat || oppat.length === 0) { + op = '!='; + pat = ''; + } + else if (/^\w*$/.test(pat)) { // just a word + op = '=='; + } + else { + matches = oppat.match(/^(@|!@|~|!~|==|!=)(.*)$/); + if (matches) { + op = matches[1]; + pat = matches[2]; + } + } + + // set the tester function + var tester: (s: string, id: string, matches: string[], state: string, eos: boolean) => boolean; + + // special case a regexp that matches just words + if ((op === '~' || op === '!~') && /^(\w|\|)*$/.test(pat)) { + var inWords = objects.createKeywordMatcher(pat.split('|'), lexer.ignoreCase); + tester = function (s) { return (op === '~' ? inWords(s) : !inWords(s)); }; + } + else if (op === '@' || op === '!@') { + var words = lexer[pat]; + if (!words) { + monarchCommon.throwError(lexer, 'the @ match target \'' + pat + '\' is not defined, in rule: ' + ruleName); + } + if (!(isArrayOf(function (elem) { return (typeof (elem) === 'string'); }, words))) { + monarchCommon.throwError(lexer, 'the @ match target \'' + pat + '\' must be an array of strings, in rule: ' + ruleName); + } + var inWords = objects.createKeywordMatcher(words, lexer.ignoreCase); + tester = function (s) { return (op === '@' ? inWords(s) : !inWords(s)); }; + } + else if (op === '~' || op === '!~') { + if (pat.indexOf('$') < 0) { + // precompile regular expression + var re = compileRegExp(lexer, '^' + pat + '$'); + tester = function (s) { return (op === '~' ? re.test(s) : !re.test(s)); }; + } + else { + tester = function (s, id, matches, state) { + var re = compileRegExp(lexer, '^' + monarchCommon.substituteMatches(lexer, pat, id, matches, state) + '$'); + return re.test(s); + }; + } + } + else { // if (op==='==' || op==='!=') { + if (pat.indexOf('$') < 0) { + var patx = monarchCommon.fixCase(lexer, pat); + tester = function (s) { return (op === '==' ? s === patx : s !== patx); }; + } + else { + var patx = monarchCommon.fixCase(lexer, pat); + tester = function (s, id, matches, state, eos) { + var patexp = monarchCommon.substituteMatches(lexer, patx, id, matches, state); + return (op === '==' ? s === patexp : s !== patexp); + }; + } + } + + // return the branch object + if (scrut === -1) { + return { + name: tkey, value: val, test: function (id, matches, state, eos) { + return tester(id, id, matches, state, eos); + } + }; + } + else { + return { + name: tkey, value: val, test: function (id, matches, state, eos) { + var scrutinee = selectScrutinee(id, matches, state, scrut); + return tester(!scrutinee ? '' : scrutinee, id, matches, state, eos); + } + }; + } +} + +/** + * Compiles an action: i.e. optimize regular expressions and case matches + * and do many sanity checks. + * + * This is called only during compilation but if the lexer definition + * contains user functions as actions (which is usually not allowed), then this + * may be called during lexing. It is important therefore to compile common cases efficiently + */ +function compileAction(lexer: monarchCommon.ILexerMin, ruleName: string, action: any): monarchCommon.FuzzyAction { + if (!action) { + return { token: '' }; + } + else if (typeof (action) === 'string') { + return action; // { token: action }; + } + else if (action.token || action.token === '') { + if (typeof (action.token) !== 'string') { + monarchCommon.throwError(lexer, 'a \'token\' attribute must be of type string, in rule: ' + ruleName); + return { token: '' }; + } + else { + // only copy specific typed fields (only happens once during compile Lexer) + var newAction: monarchCommon.IAction = { token: action.token }; + if (action.token.indexOf('$') >= 0) { + newAction.tokenSubst = true; + } + if (typeof (action.bracket) === 'string') { + if (action.bracket === '@open') { + newAction.bracket = monarchCommon.MonarchBracket.Open; + } else if (action.bracket === '@close') { + newAction.bracket = monarchCommon.MonarchBracket.Close; + } else { + monarchCommon.throwError(lexer, 'a \'bracket\' attribute must be either \'@open\' or \'@close\', in rule: ' + ruleName); + } + } + if (action.next) { + if (typeof (action.next) !== 'string') { + monarchCommon.throwError(lexer, 'the next state must be a string value in rule: ' + ruleName); + } + else { + var next: string = action.next; + if (!/^(@pop|@push|@popall)$/.test(next)) { + if (next[0] === '@') { + next = next.substr(1); // peel off starting @ sign + } + if (next.indexOf('$') < 0) { // no dollar substitution, we can check if the state exists + if (!monarchCommon.stateExists(lexer, monarchCommon.substituteMatches(lexer, next, '', [], ''))) { + monarchCommon.throwError(lexer, 'the next state \'' + action.next + '\' is not defined in rule: ' + ruleName); + } + } + } + newAction.next = next; + } + } + if (typeof (action.goBack) === 'number') { + newAction.goBack = action.goBack; + } + if (typeof (action.switchTo) === 'string') { + newAction.switchTo = action.switchTo; + } + if (typeof (action.log) === 'string') { + newAction.log = action.log; + } + if (typeof (action.nextEmbedded) === 'string') { + newAction.nextEmbedded = action.nextEmbedded; + lexer.usesEmbedded = true; + } + return newAction; + } + } + else if (Array.isArray(action)) { + var results: monarchCommon.FuzzyAction[] = []; + var idx: string; + for (idx in action) { + if (action.hasOwnProperty(idx)) { + results[idx] = compileAction(lexer, ruleName, action[idx]); + } + } + return { group: results }; + } + else if (action.cases) { + // build an array of test cases + var cases: monarchCommon.IBranch[] = []; + + // for each case, push a test function and result value + var tkey: string; + for (tkey in action.cases) { + if (action.cases.hasOwnProperty(tkey)) { + var val = compileAction(lexer, ruleName, action.cases[tkey]); + + // what kind of case + if (tkey === '@default' || tkey === '@' || tkey === '') { + cases.push({ test: null, value: val, name: tkey }); + } + else if (tkey === '@eos') { + cases.push({ test: function (id, matches, state, eos) { return eos; }, value: val, name: tkey }); + } + else { + cases.push(createGuard(lexer, ruleName, tkey, val)); // call separate function to avoid local variable capture + } + } + } + + // create a matching function + var def = lexer.defaultToken; + return { + test: function (id, matches, state, eos) { + var idx: string; + for (idx in cases) { + if (cases.hasOwnProperty(idx)) { + var didmatch = (!cases[idx].test || cases[idx].test(id, matches, state, eos)); + if (didmatch) { + return cases[idx].value; + } + } + } + return def; + } + }; + } + else { + monarchCommon.throwError(lexer, 'an action must be a string, an object with a \'token\' or \'cases\' attribute, or an array of actions; in rule: ' + ruleName); + return ''; + } +} + +/** + * Helper class for creating matching rules + */ +class Rule implements monarchCommon.IRule { + public regex: RegExp = new RegExp(''); + public action: monarchCommon.FuzzyAction = { token: '' }; + public matchOnlyAtLineStart: boolean = false; + public name: string = ''; + + constructor(name: string) { + this.name = name; + } + + public setRegex(lexer: monarchCommon.ILexerMin, re: string); + public setRegex(lexer: monarchCommon.ILexerMin, re: RegExp); + public setRegex(lexer: monarchCommon.ILexerMin, re: any) { + var sregex: string; + if (typeof (re) === 'string') { + sregex = re; + } + else if (re instanceof RegExp) { + sregex = (re).source; + } + else { + monarchCommon.throwError(lexer, 'rules must start with a match string or regular expression: ' + this.name); + } + + this.matchOnlyAtLineStart = (sregex.length > 0 && sregex[0] === '^'); + this.name = this.name + ': ' + sregex; + this.regex = compileRegExp(lexer, '^(?:' + (this.matchOnlyAtLineStart ? sregex.substr(1) : sregex) + ')'); + } + + public setAction(lexer: monarchCommon.ILexerMin, act: monarchCommon.IAction) { + this.action = compileAction(lexer, this.name, act); + } +} + +/** + * Compiles a json description function into json where all regular expressions, + * case matches etc, are compiled and all include rules are expanded. + * We also compile the bracket definitions, supply defaults, and do many sanity checks. + * If the 'jsonStrict' parameter is 'false', we allow at certain locations + * regular expression objects and functions that get called during lexing. + * (Currently we have no samples that need this so perhaps we should always have + * jsonStrict to true). + */ +export function compile(languageId: string, json: IMonarchLanguage): monarchCommon.ILexer { + if (!json || typeof (json) !== 'object') { + throw new Error('Monarch: expecting a language definition object'); + } + + // Create our lexer + var lexer: monarchCommon.ILexer = {}; + lexer.languageId = languageId; + lexer.noThrow = false; // raise exceptions during compilation + lexer.maxStack = 100; + + // Set standard fields: be defensive about types + lexer.start = string(json.start); + lexer.ignoreCase = bool(json.ignoreCase, false); + + lexer.tokenPostfix = string(json.tokenPostfix, '.' + lexer.languageId); + lexer.defaultToken = string(json.defaultToken, 'source', function () { monarchCommon.throwError(lexer, 'the \'defaultToken\' must be a string'); }); + + lexer.usesEmbedded = false; // becomes true if we find a nextEmbedded action + + // For calling compileAction later on + var lexerMin: monarchCommon.ILexerMin = json; + lexerMin.languageId = languageId; + lexerMin.ignoreCase = lexer.ignoreCase; + lexerMin.noThrow = lexer.noThrow; + lexerMin.usesEmbedded = lexer.usesEmbedded; + lexerMin.stateNames = json.tokenizer; + lexerMin.defaultToken = lexer.defaultToken; + + + // Compile an array of rules into newrules where RegExp objects are created. + function addRules(state: string, newrules: monarchCommon.IRule[], rules: any[]) { + var idx: string; + for (idx in rules) { + if (rules.hasOwnProperty(idx)) { + var rule = rules[idx]; + var include = rule.include; + if (include) { + if (typeof (include) !== 'string') { + monarchCommon.throwError(lexer, 'an \'include\' attribute must be a string at: ' + state); + } + if (include[0] === '@') { + include = include.substr(1); // peel off starting @ + } + if (!json.tokenizer[include]) { + monarchCommon.throwError(lexer, 'include target \'' + include + '\' is not defined at: ' + state); + } + addRules(state + '.' + include, newrules, json.tokenizer[include]); + } + else { + var newrule = new Rule(state); + + + // Set up new rule attributes + if (Array.isArray(rule) && rule.length >= 1 && rule.length <= 3) { + newrule.setRegex(lexerMin, rule[0]); + if (rule.length >= 3) { + if (typeof (rule[1]) === 'string') { + newrule.setAction(lexerMin, { token: rule[1], next: rule[2] }); + } + else if (typeof (rule[1]) === 'object') { + var rule1 = rule[1]; + rule1.next = rule[2]; + newrule.setAction(lexerMin, rule1); + } + else { + monarchCommon.throwError(lexer, 'a next state as the last element of a rule can only be given if the action is either an object or a string, at: ' + state); + } + } + else { + newrule.setAction(lexerMin, rule[1]); + } + } + else { + if (!rule.regex) { + monarchCommon.throwError(lexer, 'a rule must either be an array, or an object with a \'regex\' or \'include\' field at: ' + state); + } + if (rule.name) { + newrule.name = string(rule.name); + } + if (rule.matchOnlyAtStart) { + newrule.matchOnlyAtLineStart = bool(rule.matchOnlyAtLineStart); + } + newrule.setRegex(lexerMin, rule.regex); + newrule.setAction(lexerMin, rule.action); + } + + newrules.push(newrule); + } + } + } + } + + // compile the tokenizer rules + if (!json.tokenizer || typeof (json.tokenizer) !== 'object') { + monarchCommon.throwError(lexer, 'a language definition must define the \'tokenizer\' attribute as an object'); + } + + lexer.tokenizer = []; + var key: string; + for (key in json.tokenizer) { + if (json.tokenizer.hasOwnProperty(key)) { + if (!lexer.start) { + lexer.start = key; + } + + var rules = json.tokenizer[key]; + lexer.tokenizer[key] = new Array(); + addRules('tokenizer.' + key, lexer.tokenizer[key], rules); + } + } + lexer.usesEmbedded = lexerMin.usesEmbedded; // can be set during compileAction + + // Set simple brackets + if (json.brackets) { + if (!(Array.isArray(json.brackets))) { + monarchCommon.throwError(lexer, 'the \'brackets\' attribute must be defined as an array'); + } + } + else { + json.brackets = [ + { open: '{', close: '}', token: 'delimiter.curly' }, + { open: '[', close: ']', token: 'delimiter.square' }, + { open: '(', close: ')', token: 'delimiter.parenthesis' }, + { open: '<', close: '>', token: 'delimiter.angle' }]; + } + var brackets: IMonarchLanguageBracket[] = []; + for (var bracketIdx in json.brackets) { + if (json.brackets.hasOwnProperty(bracketIdx)) { + var desc = json.brackets[bracketIdx]; + if (desc && Array.isArray(desc) && desc.length === 3) { + desc = { token: desc[2], open: desc[0], close: desc[1] }; + } + if (desc.open === desc.close) { + monarchCommon.throwError(lexer, 'open and close brackets in a \'brackets\' attribute must be different: ' + desc.open + + '\n hint: use the \'bracket\' attribute if matching on equal brackets is required.'); + } + if (typeof (desc.open) === 'string' && typeof (desc.token) === 'string') { + brackets.push({ + token: string(desc.token) + lexer.tokenPostfix + , open: monarchCommon.fixCase(lexer, string(desc.open)) + , close: monarchCommon.fixCase(lexer, string(desc.close)) + }); + } + else { + monarchCommon.throwError(lexer, 'every element in the \'brackets\' array must be a \'{open,close,token}\' object or array'); + } + } + } + lexer.brackets = brackets; + + // Disable throw so the syntax highlighter goes, no matter what + lexer.noThrow = true; + return lexer; +} diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts new file mode 100644 index 0000000000..ffad51db27 --- /dev/null +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -0,0 +1,842 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +/** + * Create a syntax highighter with a fully declarative JSON style lexer description + * using regular expressions. + */ + +import { IDisposable } from 'vs/base/common/lifecycle'; +import * as modes from 'vs/editor/common/modes'; +import * as monarchCommon from 'vs/editor/standalone/common/monarch/monarchCommon'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { Token, TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; +import { NULL_STATE, NULL_MODE_ID } from 'vs/editor/common/modes/nullMode'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { TokenTheme } from 'vs/editor/common/modes/supports/tokenization'; + +const CACHE_STACK_DEPTH = 5; + +/** + * Reuse the same stack elements up to a certain depth. + */ +class MonarchStackElementFactory { + + private static _INSTANCE = new MonarchStackElementFactory(CACHE_STACK_DEPTH); + public static create(parent: MonarchStackElement, state: string): MonarchStackElement { + return this._INSTANCE.create(parent, state); + } + + private readonly _maxCacheDepth: number; + private readonly _entries: { [stackElementId: string]: MonarchStackElement; }; + + constructor(maxCacheDepth: number) { + this._maxCacheDepth = maxCacheDepth; + this._entries = Object.create(null); + } + + public create(parent: MonarchStackElement, state: string): MonarchStackElement { + if (parent !== null && parent.depth >= this._maxCacheDepth) { + // no caching above a certain depth + return new MonarchStackElement(parent, state); + } + let stackElementId = MonarchStackElement.getStackElementId(parent); + if (stackElementId.length > 0) { + stackElementId += '|'; + } + stackElementId += state; + + let result = this._entries[stackElementId]; + if (result) { + return result; + } + result = new MonarchStackElement(parent, state); + this._entries[stackElementId] = result; + return result; + } +} + +class MonarchStackElement { + + public readonly parent: MonarchStackElement; + public readonly state: string; + public readonly depth: number; + + constructor(parent: MonarchStackElement, state: string) { + this.parent = parent; + this.state = state; + this.depth = (this.parent ? this.parent.depth : 0) + 1; + } + + public static getStackElementId(element: MonarchStackElement): string { + let result = ''; + while (element !== null) { + if (result.length > 0) { + result += '|'; + } + result += element.state; + element = element.parent; + } + return result; + } + + private static _equals(a: MonarchStackElement, b: MonarchStackElement): boolean { + while (a !== null && b !== null) { + if (a === b) { + return true; + } + if (a.state !== b.state) { + return false; + } + a = a.parent; + b = b.parent; + } + if (a === null && b === null) { + return true; + } + return false; + } + + public equals(other: MonarchStackElement): boolean { + return MonarchStackElement._equals(this, other); + } + + public push(state: string): MonarchStackElement { + return MonarchStackElementFactory.create(this, state); + } + + public pop(): MonarchStackElement { + return this.parent; + } + + public popall(): MonarchStackElement { + let result: MonarchStackElement = this; + while (result.parent) { + result = result.parent; + } + return result; + } + + public switchTo(state: string): MonarchStackElement { + return MonarchStackElementFactory.create(this.parent, state); + } +} + +class EmbeddedModeData { + public readonly modeId: string; + public readonly state: modes.IState; + + constructor(modeId: string, state: modes.IState) { + this.modeId = modeId; + this.state = state; + } + + public equals(other: EmbeddedModeData): boolean { + return ( + this.modeId === other.modeId + && this.state.equals(other.state) + ); + } + + public clone(): EmbeddedModeData { + let stateClone = this.state.clone(); + // save an object + if (stateClone === this.state) { + return this; + } + return new EmbeddedModeData(this.modeId, this.state); + } +} + +/** + * Reuse the same line states up to a certain depth. + */ +class MonarchLineStateFactory { + + private static _INSTANCE = new MonarchLineStateFactory(CACHE_STACK_DEPTH); + public static create(stack: MonarchStackElement, embeddedModeData: EmbeddedModeData): MonarchLineState { + return this._INSTANCE.create(stack, embeddedModeData); + } + + private readonly _maxCacheDepth: number; + private readonly _entries: { [stackElementId: string]: MonarchLineState; }; + + constructor(maxCacheDepth: number) { + this._maxCacheDepth = maxCacheDepth; + this._entries = Object.create(null); + } + + public create(stack: MonarchStackElement, embeddedModeData: EmbeddedModeData): MonarchLineState { + if (embeddedModeData !== null) { + // no caching when embedding + return new MonarchLineState(stack, embeddedModeData); + } + if (stack !== null && stack.depth >= this._maxCacheDepth) { + // no caching above a certain depth + return new MonarchLineState(stack, embeddedModeData); + } + let stackElementId = MonarchStackElement.getStackElementId(stack); + + let result = this._entries[stackElementId]; + if (result) { + return result; + } + result = new MonarchLineState(stack, null); + this._entries[stackElementId] = result; + return result; + } +} + +class MonarchLineState implements modes.IState { + + public readonly stack: MonarchStackElement; + public readonly embeddedModeData: EmbeddedModeData; + + constructor( + stack: MonarchStackElement, + embeddedModeData: EmbeddedModeData + ) { + this.stack = stack; + this.embeddedModeData = embeddedModeData; + } + + public clone(): modes.IState { + let embeddedModeDataClone = this.embeddedModeData ? this.embeddedModeData.clone() : null; + // save an object + if (embeddedModeDataClone === this.embeddedModeData) { + return this; + } + return MonarchLineStateFactory.create(this.stack, this.embeddedModeData); + } + + public equals(other: modes.IState): boolean { + if (!(other instanceof MonarchLineState)) { + return false; + } + if (!this.stack.equals(other.stack)) { + return false; + } + if (this.embeddedModeData === null && other.embeddedModeData === null) { + return true; + } + if (this.embeddedModeData === null || other.embeddedModeData === null) { + return false; + } + return this.embeddedModeData.equals(other.embeddedModeData); + } +} + +const hasOwnProperty = Object.hasOwnProperty; + +interface IMonarchTokensCollector { + enterMode(startOffset: number, modeId: string): void; + emit(startOffset: number, type: string): void; + nestedModeTokenize(embeddedModeLine: string, embeddedModeData: EmbeddedModeData, offsetDelta: number): modes.IState; +} + +class MonarchClassicTokensCollector implements IMonarchTokensCollector { + + private _tokens: Token[]; + private _language: string; + private _lastTokenType: string; + private _lastTokenLanguage: string; + + constructor() { + this._tokens = []; + this._language = null; + this._lastTokenType = null; + this._lastTokenLanguage = null; + } + + public enterMode(startOffset: number, modeId: string): void { + this._language = modeId; + } + + public emit(startOffset: number, type: string): void { + if (this._lastTokenType === type && this._lastTokenLanguage === this._language) { + return; + } + this._lastTokenType = type; + this._lastTokenLanguage = this._language; + this._tokens.push(new Token(startOffset, type, this._language)); + } + + public nestedModeTokenize(embeddedModeLine: string, embeddedModeData: EmbeddedModeData, offsetDelta: number): modes.IState { + const nestedModeId = embeddedModeData.modeId; + const embeddedModeState = embeddedModeData.state; + + const nestedModeTokenizationSupport = modes.TokenizationRegistry.get(nestedModeId); + if (!nestedModeTokenizationSupport) { + this.enterMode(offsetDelta, nestedModeId); + this.emit(offsetDelta, ''); + return embeddedModeState; + } + + let nestedResult = nestedModeTokenizationSupport.tokenize(embeddedModeLine, embeddedModeState, offsetDelta); + this._tokens = this._tokens.concat(nestedResult.tokens); + this._lastTokenType = null; + this._lastTokenLanguage = null; + this._language = null; + return nestedResult.endState; + } + + public finalize(endState: MonarchLineState): TokenizationResult { + return new TokenizationResult(this._tokens, endState); + } +} + +class MonarchModernTokensCollector implements IMonarchTokensCollector { + + private _modeService: IModeService; + private _theme: TokenTheme; + private _prependTokens: Uint32Array; + private _tokens: number[]; + private _currentLanguageId: modes.LanguageId; + private _lastTokenMetadata: number; + + constructor(modeService: IModeService, theme: TokenTheme) { + this._modeService = modeService; + this._theme = theme; + this._prependTokens = null; + this._tokens = []; + this._currentLanguageId = modes.LanguageId.Null; + this._lastTokenMetadata = 0; + } + + public enterMode(startOffset: number, modeId: string): void { + this._currentLanguageId = this._modeService.getLanguageIdentifier(modeId).id; + } + + public emit(startOffset: number, type: string): void { + let metadata = this._theme.match(this._currentLanguageId, type); + if (this._lastTokenMetadata === metadata) { + return; + } + this._lastTokenMetadata = metadata; + this._tokens.push(startOffset); + this._tokens.push(metadata); + } + + private static _merge(a: Uint32Array, b: number[], c: Uint32Array): Uint32Array { + let aLen = (a !== null ? a.length : 0); + let bLen = b.length; + let cLen = (c !== null ? c.length : 0); + + if (aLen === 0 && bLen === 0 && cLen === 0) { + return new Uint32Array(0); + } + if (aLen === 0 && bLen === 0) { + return c; + } + if (bLen === 0 && cLen === 0) { + return a; + } + + let result = new Uint32Array(aLen + bLen + cLen); + if (a !== null) { + result.set(a); + } + for (let i = 0; i < bLen; i++) { + result[aLen + i] = b[i]; + } + if (c !== null) { + result.set(c, aLen + bLen); + } + return result; + } + + public nestedModeTokenize(embeddedModeLine: string, embeddedModeData: EmbeddedModeData, offsetDelta: number): modes.IState { + const nestedModeId = embeddedModeData.modeId; + const embeddedModeState = embeddedModeData.state; + + const nestedModeTokenizationSupport = modes.TokenizationRegistry.get(nestedModeId); + if (!nestedModeTokenizationSupport) { + this.enterMode(offsetDelta, nestedModeId); + this.emit(offsetDelta, ''); + return embeddedModeState; + } + + let nestedResult = nestedModeTokenizationSupport.tokenize2(embeddedModeLine, embeddedModeState, offsetDelta); + this._prependTokens = MonarchModernTokensCollector._merge(this._prependTokens, this._tokens, nestedResult.tokens); + this._tokens = []; + this._currentLanguageId = 0; + this._lastTokenMetadata = 0; + return nestedResult.endState; + } + + public finalize(endState: MonarchLineState): TokenizationResult2 { + return new TokenizationResult2( + MonarchModernTokensCollector._merge(this._prependTokens, this._tokens, null), + endState + ); + } +} + +class MonarchTokenizer implements modes.ITokenizationSupport { + + private readonly _modeService: IModeService; + private readonly _standaloneThemeService: IStandaloneThemeService; + private readonly _modeId: string; + private readonly _lexer: monarchCommon.ILexer; + private _embeddedModes: { [modeId: string]: boolean; }; + private _tokenizationRegistryListener: IDisposable; + + constructor(modeService: IModeService, standaloneThemeService: IStandaloneThemeService, modeId: string, lexer: monarchCommon.ILexer) { + this._modeService = modeService; + this._standaloneThemeService = standaloneThemeService; + this._modeId = modeId; + this._lexer = lexer; + this._embeddedModes = Object.create(null); + + // Set up listening for embedded modes + let emitting = false; + this._tokenizationRegistryListener = modes.TokenizationRegistry.onDidChange((e) => { + if (emitting) { + return; + } + let isOneOfMyEmbeddedModes = false; + for (let i = 0, len = e.changedLanguages.length; i < len; i++) { + let language = e.changedLanguages[i]; + if (this._embeddedModes[language]) { + isOneOfMyEmbeddedModes = true; + break; + } + } + if (isOneOfMyEmbeddedModes) { + emitting = true; + modes.TokenizationRegistry.fire([this._modeId]); + emitting = false; + } + }); + } + + public dispose(): void { + this._tokenizationRegistryListener.dispose(); + } + + public getInitialState(): modes.IState { + let rootState = MonarchStackElementFactory.create(null, this._lexer.start); + return MonarchLineStateFactory.create(rootState, null); + } + + public tokenize(line: string, lineState: modes.IState, offsetDelta: number): TokenizationResult { + let tokensCollector = new MonarchClassicTokensCollector(); + let endLineState = this._tokenize(line, lineState, offsetDelta, tokensCollector); + return tokensCollector.finalize(endLineState); + } + + public tokenize2(line: string, lineState: modes.IState, offsetDelta: number): TokenizationResult2 { + let tokensCollector = new MonarchModernTokensCollector(this._modeService, this._standaloneThemeService.getTheme().tokenTheme); + let endLineState = this._tokenize(line, lineState, offsetDelta, tokensCollector); + return tokensCollector.finalize(endLineState); + } + + private _tokenize(line: string, lineState: MonarchLineState, offsetDelta: number, collector: IMonarchTokensCollector): MonarchLineState { + if (lineState.embeddedModeData) { + return this._nestedTokenize(line, lineState, offsetDelta, collector); + } else { + return this._myTokenize(line, lineState, offsetDelta, collector); + } + } + + private _findLeavingNestedModeOffset(line: string, state: MonarchLineState): number { + let rules = this._lexer.tokenizer[state.stack.state]; + if (!rules) { + rules = monarchCommon.findRules(this._lexer, state.stack.state); // do parent matching + if (!rules) { + monarchCommon.throwError(this._lexer, 'tokenizer state is not defined: ' + state.stack.state); + } + } + + let popOffset = -1; + let hasEmbeddedPopRule = false; + + for (let idx in rules) { + if (!hasOwnProperty.call(rules, idx)) { + continue; + } + let rule: monarchCommon.IRule = rules[idx]; + if (monarchCommon.isIAction(rule.action) && rule.action.nextEmbedded !== '@pop') { + continue; + } + hasEmbeddedPopRule = true; + + let regex = rule.regex; + let regexSource = rule.regex.source; + if (regexSource.substr(0, 4) === '^(?:' && regexSource.substr(regexSource.length - 1, 1) === ')') { + regex = new RegExp(regexSource.substr(4, regexSource.length - 5), regex.ignoreCase ? 'i' : ''); + } + + let result = line.search(regex); + if (result === -1) { + continue; + } + + if (popOffset === -1 || result < popOffset) { + popOffset = result; + } + } + + if (!hasEmbeddedPopRule) { + monarchCommon.throwError(this._lexer, 'no rule containing nextEmbedded: "@pop" in tokenizer embedded state: ' + state.stack.state); + } + + return popOffset; + } + + private _nestedTokenize(line: string, lineState: MonarchLineState, offsetDelta: number, tokensCollector: IMonarchTokensCollector): MonarchLineState { + + let popOffset = this._findLeavingNestedModeOffset(line, lineState); + + if (popOffset === -1) { + // tokenization will not leave nested mode + let nestedEndState = tokensCollector.nestedModeTokenize(line, lineState.embeddedModeData, offsetDelta); + return MonarchLineStateFactory.create(lineState.stack, new EmbeddedModeData(lineState.embeddedModeData.modeId, nestedEndState)); + } + + let nestedModeLine = line.substring(0, popOffset); + if (nestedModeLine.length > 0) { + // tokenize with the nested mode + tokensCollector.nestedModeTokenize(nestedModeLine, lineState.embeddedModeData, offsetDelta); + } + + let restOfTheLine = line.substring(popOffset); + return this._myTokenize(restOfTheLine, lineState, offsetDelta + popOffset, tokensCollector); + } + + private _myTokenize(line: string, lineState: MonarchLineState, offsetDelta: number, tokensCollector: IMonarchTokensCollector): MonarchLineState { + tokensCollector.enterMode(offsetDelta, this._modeId); + + const lineLength = line.length; + + let embeddedModeData = lineState.embeddedModeData; + let stack = lineState.stack; + let pos = 0; + + // regular expression group matching + // these never need cloning or equality since they are only used within a line match + let groupActions: monarchCommon.FuzzyAction[] = null; + let groupMatches: string[] = null; + let groupMatched: string[] = null; + let groupRule: monarchCommon.IRule = null; + + while (pos < lineLength) { + const pos0 = pos; + const stackLen0 = stack.depth; + const groupLen0 = groupActions ? groupActions.length : 0; + const state = stack.state; + + let matches: string[] = null; + let matched: string = null; + let action: monarchCommon.FuzzyAction | monarchCommon.FuzzyAction[] = null; + let rule: monarchCommon.IRule = null; + + let enteringEmbeddedMode: string = null; + + // check if we need to process group matches first + if (groupActions) { + matches = groupMatches; + matched = groupMatched.shift(); + action = groupActions.shift(); + rule = groupRule; + + // cleanup if necessary + if (groupActions.length === 0) { + groupActions = null; + groupMatches = null; + groupMatched = null; + groupRule = null; + } + } else { + // otherwise we match on the token stream + + if (pos >= lineLength) { + // nothing to do + break; + } + + // get the rules for this state + let rules = this._lexer.tokenizer[state]; + if (!rules) { + rules = monarchCommon.findRules(this._lexer, state); // do parent matching + if (!rules) { + monarchCommon.throwError(this._lexer, 'tokenizer state is not defined: ' + state); + } + } + + // try each rule until we match + let restOfLine = line.substr(pos); + for (let idx in rules) { + if (hasOwnProperty.call(rules, idx)) { + let rule: monarchCommon.IRule = rules[idx]; + if (pos === 0 || !rule.matchOnlyAtLineStart) { + matches = restOfLine.match(rule.regex); + if (matches) { + matched = matches[0]; + action = rule.action; + break; + } + } + } + } + } + + // We matched 'rule' with 'matches' and 'action' + if (!matches) { + matches = ['']; + matched = ''; + } + + if (!action) { + // bad: we didn't match anything, and there is no action to take + // we need to advance the stream or we get progress trouble + if (pos < lineLength) { + matches = [line.charAt(pos)]; + matched = matches[0]; + } + action = this._lexer.defaultToken; + } + + // advance stream + pos += matched.length; + + // maybe call action function (used for 'cases') + while (monarchCommon.isFuzzyAction(action) && monarchCommon.isIAction(action) && action.test) { + action = action.test(matched, matches, state, pos === lineLength); + } + + let result: monarchCommon.FuzzyAction | monarchCommon.FuzzyAction[] = null; + // set the result: either a string or an array of actions + if (typeof action === 'string' || Array.isArray(action)) { + result = action; + } else if (action.group) { + result = action.group; + } else if (action.token !== null && action.token !== undefined) { + + // do $n replacements? + if (action.tokenSubst) { + result = monarchCommon.substituteMatches(this._lexer, action.token, matched, matches, state); + } else { + result = action.token; + } + + // enter embedded mode? + if (action.nextEmbedded) { + if (action.nextEmbedded === '@pop') { + if (!embeddedModeData) { + monarchCommon.throwError(this._lexer, 'cannot pop embedded mode if not inside one'); + } + embeddedModeData = null; + } else if (embeddedModeData) { + monarchCommon.throwError(this._lexer, 'cannot enter embedded mode from within an embedded mode'); + } else { + enteringEmbeddedMode = monarchCommon.substituteMatches(this._lexer, action.nextEmbedded, matched, matches, state); + } + } + + // state transformations + if (action.goBack) { // back up the stream.. + pos = Math.max(0, pos - action.goBack); + } + + if (action.switchTo && typeof action.switchTo === 'string') { + let nextState = monarchCommon.substituteMatches(this._lexer, action.switchTo, matched, matches, state); // switch state without a push... + if (nextState[0] === '@') { + nextState = nextState.substr(1); // peel off starting '@' + } + if (!monarchCommon.findRules(this._lexer, nextState)) { + monarchCommon.throwError(this._lexer, 'trying to switch to a state \'' + nextState + '\' that is undefined in rule: ' + rule.name); + } else { + stack = stack.switchTo(nextState); + } + } else if (action.transform && typeof action.transform === 'function') { + monarchCommon.throwError(this._lexer, 'action.transform not supported'); + } else if (action.next) { + if (action.next === '@push') { + if (stack.depth >= this._lexer.maxStack) { + monarchCommon.throwError(this._lexer, 'maximum tokenizer stack size reached: [' + + stack.state + ',' + stack.parent.state + ',...]'); + } else { + stack = stack.push(state); + } + } else if (action.next === '@pop') { + if (stack.depth <= 1) { + monarchCommon.throwError(this._lexer, 'trying to pop an empty stack in rule: ' + rule.name); + } else { + stack = stack.pop(); + } + } else if (action.next === '@popall') { + stack = stack.popall(); + } else { + let nextState = monarchCommon.substituteMatches(this._lexer, action.next, matched, matches, state); + if (nextState[0] === '@') { + nextState = nextState.substr(1); // peel off starting '@' + } + + if (!monarchCommon.findRules(this._lexer, nextState)) { + monarchCommon.throwError(this._lexer, 'trying to set a next state \'' + nextState + '\' that is undefined in rule: ' + rule.name); + } else { + stack = stack.push(nextState); + } + } + } + + if (action.log && typeof (action.log) === 'string') { + monarchCommon.log(this._lexer, this._lexer.languageId + ': ' + monarchCommon.substituteMatches(this._lexer, action.log, matched, matches, state)); + } + } + + // check result + if (result === null) { + monarchCommon.throwError(this._lexer, 'lexer rule has no well-defined action in rule: ' + rule.name); + } + + // is the result a group match? + if (Array.isArray(result)) { + if (groupActions && groupActions.length > 0) { + monarchCommon.throwError(this._lexer, 'groups cannot be nested: ' + rule.name); + } + if (matches.length !== result.length + 1) { + monarchCommon.throwError(this._lexer, 'matched number of groups does not match the number of actions in rule: ' + rule.name); + } + let totalLen = 0; + for (let i = 1; i < matches.length; i++) { + totalLen += matches[i].length; + } + if (totalLen !== matched.length) { + monarchCommon.throwError(this._lexer, 'with groups, all characters should be matched in consecutive groups in rule: ' + rule.name); + } + groupMatches = matches; + groupMatched = matches.slice(1); + groupActions = result.slice(0); + groupRule = rule; + pos -= matched.length; + // call recursively to initiate first result match + continue; + } else { + // regular result + + // check for '@rematch' + if (result === '@rematch') { + pos -= matched.length; + matched = ''; // better set the next state too.. + matches = null; + result = ''; + } + + // check progress + if (matched.length === 0) { + if (stackLen0 !== stack.depth || state !== stack.state || (!groupActions ? 0 : groupActions.length) !== groupLen0) { + continue; + } else { + monarchCommon.throwError(this._lexer, 'no progress in tokenizer in rule: ' + rule.name); + pos = lineLength; // must make progress or editor loops + } + } + + // return the result (and check for brace matching) + // todo: for efficiency we could pre-sanitize tokenPostfix and substitutions + let tokenType: string = null; + if (monarchCommon.isString(result) && result.indexOf('@brackets') === 0) { + let rest = result.substr('@brackets'.length); + let bracket = findBracket(this._lexer, matched); + if (!bracket) { + monarchCommon.throwError(this._lexer, '@brackets token returned but no bracket defined as: ' + matched); + bracket = { token: '', bracketType: monarchCommon.MonarchBracket.None }; + } + tokenType = monarchCommon.sanitize(bracket.token + rest); + } else { + let token = (result === '' ? '' : result + this._lexer.tokenPostfix); + tokenType = monarchCommon.sanitize(token); + } + + tokensCollector.emit(pos0 + offsetDelta, tokenType); + } + + if (enteringEmbeddedMode !== null) { + // substitute language alias to known modes to support syntax highlighting + let enteringEmbeddedModeId = this._modeService.getModeIdForLanguageName(enteringEmbeddedMode); + if (enteringEmbeddedModeId) { + enteringEmbeddedMode = enteringEmbeddedModeId; + } + + let embeddedModeData = this._getNestedEmbeddedModeData(enteringEmbeddedMode); + + if (pos < lineLength) { + // there is content from the embedded mode on this line + let restOfLine = line.substr(pos); + return this._nestedTokenize(restOfLine, MonarchLineStateFactory.create(stack, embeddedModeData), offsetDelta + pos, tokensCollector); + } else { + return MonarchLineStateFactory.create(stack, embeddedModeData); + } + } + } + + return MonarchLineStateFactory.create(stack, embeddedModeData); + } + + private _getNestedEmbeddedModeData(mimetypeOrModeId: string): EmbeddedModeData { + let nestedMode = this._locateMode(mimetypeOrModeId); + if (nestedMode) { + let tokenizationSupport = modes.TokenizationRegistry.get(nestedMode.getId()); + if (tokenizationSupport) { + return new EmbeddedModeData(nestedMode.getId(), tokenizationSupport.getInitialState()); + } + } + + let nestedModeId = nestedMode ? nestedMode.getId() : NULL_MODE_ID; + return new EmbeddedModeData(nestedModeId, NULL_STATE); + } + + private _locateMode(mimetypeOrModeId: string): modes.IMode { + if (!mimetypeOrModeId || !this._modeService.isRegisteredMode(mimetypeOrModeId)) { + return null; + } + + let modeId = this._modeService.getModeId(mimetypeOrModeId); + + // Fire mode loading event + this._modeService.getOrCreateMode(modeId); + + let mode = this._modeService.getMode(modeId); + if (mode) { + // Re-emit tokenizationSupport change events from all modes that I ever embedded + this._embeddedModes[modeId] = true; + return mode; + } + + this._embeddedModes[modeId] = true; + + return null; + } + +} + +/** + * Searches for a bracket in the 'brackets' attribute that matches the input. + */ +function findBracket(lexer: monarchCommon.ILexer, matched: string) { + if (!matched) { + return null; + } + matched = monarchCommon.fixCase(lexer, matched); + + var brackets = lexer.brackets; + for (var i = 0; i < brackets.length; i++) { + var bracket = brackets[i]; + if (bracket.open === matched) { + return { token: bracket.token, bracketType: monarchCommon.MonarchBracket.Open }; + } + else if (bracket.close === matched) { + return { token: bracket.token, bracketType: monarchCommon.MonarchBracket.Close }; + } + } + return null; +} + +export function createTokenizationSupport(modeService: IModeService, standaloneThemeService: IStandaloneThemeService, modeId: string, lexer: monarchCommon.ILexer): modes.ITokenizationSupport { + return new MonarchTokenizer(modeService, standaloneThemeService, modeId, lexer); +} diff --git a/src/vs/editor/standalone/common/monarch/monarchTypes.ts b/src/vs/editor/standalone/common/monarch/monarchTypes.ts new file mode 100644 index 0000000000..2380199716 --- /dev/null +++ b/src/vs/editor/standalone/common/monarch/monarchTypes.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +/* + * Interface types for Monarch language definitions + * These descriptions are really supposed to be JSON values but if using typescript + * to describe them, these type definitions can help check the validity. + */ + +/** + * A Monarch language definition + */ +export interface IMonarchLanguage { + /** + * map from string to ILanguageRule[] + */ + tokenizer: { [name: string]: IMonarchLanguageRule[] }; + /** + * is the language case insensitive? + */ + ignoreCase?: boolean; + /** + * if no match in the tokenizer assign this token class (default 'source') + */ + defaultToken?: string; + /** + * for example [['{','}','delimiter.curly']] + */ + brackets?: IMonarchLanguageBracket[]; + /** + * start symbol in the tokenizer (by default the first entry is used) + */ + start?: string; + /** + * attach this to every token class (by default '.' + name) + */ + tokenPostfix: string; +} + +/** + * A rule is either a regular expression and an action + * shorthands: [reg,act] == { regex: reg, action: act} + * and : [reg,act,nxt] == { regex: reg, action: act{ next: nxt }} + */ +export interface IMonarchLanguageRule { + /** + * match tokens + */ + regex?: string | RegExp; + /** + * action to take on match + */ + action?: IMonarchLanguageAction; + + /** + * or an include rule. include all rules from the included state + */ + include?: string; +} + +/** + * An action is either an array of actions... + * ... or a case statement with guards... + * ... or a basic action with a token value. + */ +export interface IMonarchLanguageAction { + /** + * array of actions for each parenthesized match group + */ + group?: IMonarchLanguageAction[]; + + /** + * map from string to ILanguageAction + */ + cases?: Object; + + /** + * token class (ie. css class) (or "@brackets" or "@rematch") + */ + token?: string; + /** + * the next state to push, or "@push", "@pop", "@popall" + */ + next?: string; + /** + * switch to this state + */ + switchTo?: string; + /** + * go back n characters in the stream + */ + goBack?: number; + /** + * @open or @close + */ + bracket?: string; + /** + * switch to embedded language (useing the mimetype) or get out using "@pop" + */ + nextEmbedded?: string; + /** + * log a message to the browser console window + */ + log?: string; +} + +/** + * This interface can be shortened as an array, ie. ['{','}','delimiter.curly'] + */ +export interface IMonarchLanguageBracket { + /** + * open bracket + */ + open: string; + /** + * closeing bracket + */ + close: string; + /** + * token class + */ + token: string; +} diff --git a/src/vs/editor/standalone/common/standaloneThemeService.ts b/src/vs/editor/standalone/common/standaloneThemeService.ts new file mode 100644 index 0000000000..0388cce4d8 --- /dev/null +++ b/src/vs/editor/standalone/common/standaloneThemeService.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { TokenTheme, ITokenThemeRule } from 'vs/editor/common/modes/supports/tokenization'; +import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; + +export var IStandaloneThemeService = createDecorator('themeService'); + +export type BuiltinTheme = 'vs' | 'vs-dark' | 'hc-black'; +export type IColors = { [colorId: string]: string; }; + +export interface IStandaloneThemeData { + base: BuiltinTheme; + inherit: boolean; + rules: ITokenThemeRule[]; + colors: IColors; +} + +export interface IStandaloneTheme extends ITheme { + tokenTheme: TokenTheme; + themeName: string; +} + +export interface IStandaloneThemeService extends IThemeService { + _serviceBrand: any; + + setTheme(themeName: string): string; + + defineTheme(themeName: string, themeData: IStandaloneThemeData): void; + + getTheme(): IStandaloneTheme; +} diff --git a/src/vs/editor/standalone/common/themes.ts b/src/vs/editor/standalone/common/themes.ts new file mode 100644 index 0000000000..449dd91ef1 --- /dev/null +++ b/src/vs/editor/standalone/common/themes.ts @@ -0,0 +1,210 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IStandaloneThemeData } from 'vs/editor/standalone/common/standaloneThemeService'; +import { editorBackground, editorForeground, editorSelectionHighlight, editorInactiveSelection } from 'vs/platform/theme/common/colorRegistry'; +import { editorIndentGuides } from 'vs/editor/common/view/editorColorRegistry'; + +/* -------------------------------- Begin vs theme -------------------------------- */ +export const vs: IStandaloneThemeData = { + base: 'vs', + inherit: false, + rules: [ + { token: '', foreground: '000000', background: 'fffffe' }, + { token: 'invalid', foreground: 'cd3131' }, + { token: 'emphasis', fontStyle: 'italic' }, + { token: 'strong', fontStyle: 'bold' }, + + { token: 'variable', foreground: '001188' }, + { token: 'variable.predefined', foreground: '4864AA' }, + { token: 'constant', foreground: 'dd0000' }, + { token: 'comment', foreground: '008000' }, + { token: 'number', foreground: '09885A' }, + { token: 'number.hex', foreground: '3030c0' }, + { token: 'regexp', foreground: '800000' }, + { token: 'annotation', foreground: '808080' }, + { token: 'type', foreground: '008080' }, + + { token: 'delimiter', foreground: '000000' }, + { token: 'delimiter.html', foreground: '383838' }, + { token: 'delimiter.xml', foreground: '0000FF' }, + + { token: 'tag', foreground: '800000' }, + { token: 'tag.id.jade', foreground: '4F76AC' }, + { token: 'tag.class.jade', foreground: '4F76AC' }, + { token: 'meta.scss', foreground: '800000' }, + { token: 'metatag', foreground: 'e00000' }, + { token: 'metatag.content.html', foreground: 'FF0000' }, + { token: 'metatag.html', foreground: '808080' }, + { token: 'metatag.xml', foreground: '808080' }, + { token: 'metatag.php', fontStyle: 'bold' }, + + { token: 'key', foreground: '863B00' }, + { token: 'string.key.json', foreground: 'A31515' }, + { token: 'string.value.json', foreground: '0451A5' }, + + { token: 'attribute.name', foreground: 'FF0000' }, + { token: 'attribute.value', foreground: '0451A5' }, + { token: 'attribute.value.number', foreground: '09885A' }, + { token: 'attribute.value.unit', foreground: '09885A' }, + { token: 'attribute.value.html', foreground: '0000FF' }, + { token: 'attribute.value.xml', foreground: '0000FF' }, + + { token: 'string', foreground: 'A31515' }, + { token: 'string.html', foreground: '0000FF' }, + { token: 'string.sql', foreground: 'FF0000' }, + { token: 'string.yaml', foreground: '0451A5' }, + + { token: 'keyword', foreground: '0000FF' }, + { token: 'keyword.json', foreground: '0451A5' }, + { token: 'keyword.flow', foreground: 'AF00DB' }, + { token: 'keyword.flow.scss', foreground: '0000FF' }, + + { token: 'operator.scss', foreground: '666666' }, + { token: 'operator.sql', foreground: '778899' }, + { token: 'operator.swift', foreground: '666666' }, + { token: 'predefined.sql', foreground: 'FF00FF' }, + ], + colors: { + [editorBackground]: '#FFFFFE', + [editorForeground]: '#000000', + [editorInactiveSelection]: '#E5EBF1', + [editorIndentGuides]: '#D3D3D3', + [editorSelectionHighlight]: '#ADD6FF4D' + } +}; +/* -------------------------------- End vs theme -------------------------------- */ + + +/* -------------------------------- Begin vs-dark theme -------------------------------- */ +export const vs_dark: IStandaloneThemeData = { + base: 'vs-dark', + inherit: false, + rules: [ + { token: '', foreground: 'D4D4D4', background: '1E1E1E' }, + { token: 'invalid', foreground: 'f44747' }, + { token: 'emphasis', fontStyle: 'italic' }, + { token: 'strong', fontStyle: 'bold' }, + + { token: 'variable', foreground: '74B0DF' }, + { token: 'variable.predefined', foreground: '4864AA' }, + { token: 'variable.parameter', foreground: '9CDCFE' }, + { token: 'constant', foreground: '569CD6' }, + { token: 'comment', foreground: '608B4E' }, + { token: 'number', foreground: 'B5CEA8' }, + { token: 'number.hex', foreground: '5BB498' }, + { token: 'regexp', foreground: 'B46695' }, + { token: 'annotation', foreground: 'cc6666' }, + { token: 'type', foreground: '3DC9B0' }, + + { token: 'delimiter', foreground: 'DCDCDC' }, + { token: 'delimiter.html', foreground: '808080' }, + { token: 'delimiter.xml', foreground: '808080' }, + + { token: 'tag', foreground: '569CD6' }, + { token: 'tag.id.jade', foreground: '4F76AC' }, + { token: 'tag.class.jade', foreground: '4F76AC' }, + { token: 'meta.scss', foreground: 'A79873' }, + { token: 'meta.tag', foreground: 'CE9178' }, + { token: 'metatag', foreground: 'DD6A6F' }, + { token: 'metatag.content.html', foreground: '9CDCFE' }, + { token: 'metatag.html', foreground: '569CD6' }, + { token: 'metatag.xml', foreground: '569CD6' }, + { token: 'metatag.php', fontStyle: 'bold' }, + + { token: 'key', foreground: '9CDCFE' }, + { token: 'string.key.json', foreground: '9CDCFE' }, + { token: 'string.value.json', foreground: 'CE9178' }, + + { token: 'attribute.name', foreground: '9CDCFE' }, + { token: 'attribute.value', foreground: 'CE9178' }, + { token: 'attribute.value.number.css', foreground: 'B5CEA8' }, + { token: 'attribute.value.unit.css', foreground: 'B5CEA8' }, + { token: 'attribute.value.hex.css', foreground: 'D4D4D4' }, + + { token: 'string', foreground: 'CE9178' }, + { token: 'string.sql', foreground: 'FF0000' }, + + { token: 'keyword', foreground: '569CD6' }, + { token: 'keyword.flow', foreground: 'C586C0' }, + { token: 'keyword.json', foreground: 'CE9178' }, + { token: 'keyword.flow.scss', foreground: '569CD6' }, + + { token: 'operator.scss', foreground: '909090' }, + { token: 'operator.sql', foreground: '778899' }, + { token: 'operator.swift', foreground: '909090' }, + { token: 'predefined.sql', foreground: 'FF00FF' }, + ], + colors: { + [editorBackground]: '#1E1E1E', + [editorForeground]: '#D4D4D4', + [editorInactiveSelection]: '#3A3D41', + [editorIndentGuides]: '#404040', + [editorSelectionHighlight]: '#ADD6FF26' + } +}; +/* -------------------------------- End vs-dark theme -------------------------------- */ + + + +/* -------------------------------- Begin hc-black theme -------------------------------- */ +export const hc_black: IStandaloneThemeData = { + base: 'hc-black', + inherit: false, + rules: [ + { token: '', foreground: 'FFFFFF', background: '000000' }, + { token: 'invalid', foreground: 'f44747' }, + { token: 'emphasis', fontStyle: 'italic' }, + { token: 'strong', fontStyle: 'bold' }, + + { token: 'variable', foreground: '1AEBFF' }, + { token: 'variable.parameter', foreground: '9CDCFE' }, + { token: 'constant', foreground: '569CD6' }, + { token: 'comment', foreground: '608B4E' }, + { token: 'number', foreground: 'FFFFFF' }, + { token: 'regexp', foreground: 'C0C0C0' }, + { token: 'annotation', foreground: '569CD6' }, + { token: 'type', foreground: '3DC9B0' }, + + { token: 'delimiter', foreground: 'FFFF00' }, + { token: 'delimiter.html', foreground: 'FFFF00' }, + + { token: 'tag', foreground: '569CD6' }, + { token: 'tag.id.jade', foreground: '4F76AC' }, + { token: 'tag.class.jade', foreground: '4F76AC' }, + { token: 'meta', foreground: 'D4D4D4' }, + { token: 'meta.tag', foreground: 'CE9178' }, + { token: 'metatag', foreground: '569CD6' }, + { token: 'metatag.content.html', foreground: '1AEBFF' }, + { token: 'metatag.html', foreground: '569CD6' }, + { token: 'metatag.xml', foreground: '569CD6' }, + { token: 'metatag.php', fontStyle: 'bold' }, + + { token: 'key', foreground: '9CDCFE' }, + { token: 'string.key', foreground: '9CDCFE' }, + { token: 'string.value', foreground: 'CE9178' }, + + { token: 'attribute.name', foreground: '569CD6' }, + { token: 'attribute.value', foreground: '3FF23F' }, + + { token: 'string', foreground: 'CE9178' }, + { token: 'string.sql', foreground: 'FF0000' }, + + { token: 'keyword', foreground: '569CD6' }, + { token: 'keyword.flow', foreground: 'C586C0' }, + + { token: 'operator.sql', foreground: '778899' }, + { token: 'operator.swift', foreground: '909090' }, + { token: 'predefined.sql', foreground: 'FF00FF' }, + ], + colors: { + [editorBackground]: '#000000', + [editorForeground]: '#FFFFFF', + [editorIndentGuides]: '#FFFFFF', + } +}; +/* -------------------------------- End hc-black theme -------------------------------- */ diff --git a/src/vs/editor/standalone/test/browser/simpleServices.test.ts b/src/vs/editor/standalone/test/browser/simpleServices.test.ts new file mode 100644 index 0000000000..eed2036402 --- /dev/null +++ b/src/vs/editor/standalone/test/browser/simpleServices.test.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; +import { SimpleConfigurationService, SimpleMessageService, StandaloneKeybindingService, StandaloneCommandService } from 'vs/editor/standalone/browser/simpleServices'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; + +suite('StandaloneKeybindingService', () => { + + class TestStandaloneKeybindingService extends StandaloneKeybindingService { + public testDispatch(e: IKeyboardEvent): void { + super._dispatch(e, null); + } + } + + test('issue Microsoft/monaco-editor#167', () => { + + let serviceCollection = new ServiceCollection(); + const instantiationService = new InstantiationService(serviceCollection, true); + + let configurationService = new SimpleConfigurationService(); + + let contextKeyService = new ContextKeyService(configurationService); + + let commandService = new StandaloneCommandService(instantiationService); + + let messageService = new SimpleMessageService(); + + let domElement = document.createElement('div'); + + let keybindingService = new TestStandaloneKeybindingService(contextKeyService, commandService, messageService, domElement); + + let commandInvoked = false; + keybindingService.addDynamicKeybinding('testCommand', KeyCode.F9, () => { + commandInvoked = true; + }, null); + + keybindingService.testDispatch({ + ctrlKey: false, + shiftKey: false, + altKey: false, + metaKey: false, + keyCode: KeyCode.F9, + code: null + }); + + assert.ok(commandInvoked, 'command invoked'); + }); +}); diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts new file mode 100644 index 0000000000..187e645e4d --- /dev/null +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -0,0 +1,180 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { TokenizationSupport2Adapter, TokensProvider, ILineTokens, IToken } from 'vs/editor/standalone/browser/standaloneLanguages'; +import { IStandaloneThemeService, IStandaloneThemeData, IStandaloneTheme } from 'vs/editor/standalone/common/standaloneThemeService'; +import Event from 'vs/base/common/event'; +import { ITheme, LIGHT } from 'vs/platform/theme/common/themeService'; +import { LanguageIdentifier, LanguageId, IState, MetadataConsts } from 'vs/editor/common/modes'; +import { Token } from 'vs/editor/common/core/token'; +import { TokenTheme } from 'vs/editor/common/modes/supports/tokenization'; +import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; +import { Color } from 'vs/base/common/color'; + +suite('TokenizationSupport2Adapter', () => { + + const languageIdentifier = new LanguageIdentifier('tttt', LanguageId.PlainText); + const tokenMetadata = (languageIdentifier.id << MetadataConsts.LANGUAGEID_OFFSET); + + class MockTokenTheme extends TokenTheme { + private counter = 0; + constructor() { + super(null, null); + } + public match(languageId: LanguageId, token: string): number { + return ( + ((this.counter++) << MetadataConsts.FOREGROUND_OFFSET) + | (languageId << MetadataConsts.LANGUAGEID_OFFSET) + ) >>> 0; + } + } + + class MockThemeService implements IStandaloneThemeService { + _serviceBrand = null; + public setTheme(themeName: string): string { + throw new Error('Not implemented'); + } + public defineTheme(themeName: string, themeData: IStandaloneThemeData): void { + throw new Error('Not implemented'); + } + public getTheme(): IStandaloneTheme { + return { + tokenTheme: new MockTokenTheme(), + + themeName: LIGHT, + + type: LIGHT, + + getColor: (color: ColorIdentifier, useDefault?: boolean): Color => { + throw new Error('Not implemented'); + }, + + defines: (color: ColorIdentifier): boolean => { + throw new Error('Not implemented'); + } + }; + } + public onThemeChange: Event = null; + } + + class MockState implements IState { + public static INSTANCE = new MockState(); + private constructor() { } + public clone(): IState { + return this; + } + public equals(other: IState): boolean { + return this === other; + } + } + + function testBadTokensProvider(providerTokens: IToken[], offsetDelta: number, expectedClassicTokens: Token[], expectedModernTokens: number[]): void { + + class BadTokensProvider implements TokensProvider { + public getInitialState(): IState { + return MockState.INSTANCE; + } + public tokenize(line: string, state: IState): ILineTokens { + return { + tokens: providerTokens, + endState: MockState.INSTANCE + }; + } + } + + const adapter = new TokenizationSupport2Adapter(new MockThemeService(), languageIdentifier, new BadTokensProvider()); + + const actualClassicTokens = adapter.tokenize('whatever', MockState.INSTANCE, offsetDelta); + assert.deepEqual(actualClassicTokens.tokens, expectedClassicTokens); + + const actualModernTokens = adapter.tokenize2('whatever', MockState.INSTANCE, offsetDelta); + const modernTokens: number[] = []; + for (let i = 0; i < actualModernTokens.tokens.length; i++) { + modernTokens[i] = actualModernTokens.tokens[i]; + } + assert.deepEqual(modernTokens, expectedModernTokens); + } + + test('tokens always start at index 0 (no offset delta)', () => { + testBadTokensProvider( + [ + { startIndex: 7, scopes: 'foo' }, + { startIndex: 0, scopes: 'bar' } + ], + 0, + [ + new Token(0, 'foo', languageIdentifier.language), + new Token(0, 'bar', languageIdentifier.language), + ], + [ + 0, tokenMetadata | (0 << MetadataConsts.FOREGROUND_OFFSET), + 0, tokenMetadata | (1 << MetadataConsts.FOREGROUND_OFFSET) + ] + ); + }); + + test('tokens always start after each other (no offset delta)', () => { + testBadTokensProvider( + [ + { startIndex: 0, scopes: 'foo' }, + { startIndex: 5, scopes: 'bar' }, + { startIndex: 3, scopes: 'foo' }, + ], + 0, + [ + new Token(0, 'foo', languageIdentifier.language), + new Token(5, 'bar', languageIdentifier.language), + new Token(5, 'foo', languageIdentifier.language), + ], + [ + 0, tokenMetadata | (0 << MetadataConsts.FOREGROUND_OFFSET), + 5, tokenMetadata | (1 << MetadataConsts.FOREGROUND_OFFSET), + 5, tokenMetadata | (2 << MetadataConsts.FOREGROUND_OFFSET) + ] + ); + }); + + test('tokens always start at index 0 (with offset delta)', () => { + testBadTokensProvider( + [ + { startIndex: 7, scopes: 'foo' }, + { startIndex: 0, scopes: 'bar' } + ], + 7, + [ + new Token(7, 'foo', languageIdentifier.language), + new Token(7, 'bar', languageIdentifier.language), + ], + [ + 7, tokenMetadata | (0 << MetadataConsts.FOREGROUND_OFFSET), + 7, tokenMetadata | (1 << MetadataConsts.FOREGROUND_OFFSET) + ] + ); + }); + + test('tokens always start after each other (with offset delta)', () => { + testBadTokensProvider( + [ + { startIndex: 0, scopes: 'foo' }, + { startIndex: 5, scopes: 'bar' }, + { startIndex: 3, scopes: 'foo' }, + ], + 7, + [ + new Token(7, 'foo', languageIdentifier.language), + new Token(12, 'bar', languageIdentifier.language), + new Token(12, 'foo', languageIdentifier.language), + ], + [ + 7, tokenMetadata | (0 << MetadataConsts.FOREGROUND_OFFSET), + 12, tokenMetadata | (1 << MetadataConsts.FOREGROUND_OFFSET), + 12, tokenMetadata | (2 << MetadataConsts.FOREGROUND_OFFSET) + ] + ); + }); + +}); \ No newline at end of file diff --git a/src/vs/editor/test/browser/controller/imeTester.html b/src/vs/editor/test/browser/controller/imeTester.html new file mode 100644 index 0000000000..efdf094d88 --- /dev/null +++ b/src/vs/editor/test/browser/controller/imeTester.html @@ -0,0 +1,57 @@ + + + + + + + + +

Detailed setup steps at https://github.com/Microsoft/vscode/wiki/IME-Test

+ + + + \ No newline at end of file diff --git a/src/vs/editor/test/browser/controller/imeTester.ts b/src/vs/editor/test/browser/controller/imeTester.ts new file mode 100644 index 0000000000..db98b8fdfe --- /dev/null +++ b/src/vs/editor/test/browser/controller/imeTester.ts @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TextAreaInput, ITextAreaInputHost } from 'vs/editor/browser/controller/textAreaInput'; +import { ISimpleModel, TextAreaState, PagedScreenReaderStrategy } from 'vs/editor/browser/controller/textAreaState'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { createFastDomNode } from 'vs/base/browser/fastDomNode'; +import * as browser from 'vs/base/browser/browser'; + +// To run this test, open imeTester.html + +class SingleLineTestModel implements ISimpleModel { + + private _line: string; + private _eol: string; + + constructor(line: string) { + this._line = line; + this._eol = '\n'; + } + + _setText(text: string) { + this._line = text; + } + + getLineMaxColumn(lineNumber: number): number { + return this._line.length + 1; + } + + getValueInRange(range: IRange, eol: editorCommon.EndOfLinePreference): string { + return this._line.substring(range.startColumn - 1, range.endColumn - 1); + } + + getModelLineContent(lineNumber: number): string { + return this._line; + } + + getLineCount(): number { + return 1; + } +} + +class TestView { + + private _model: SingleLineTestModel; + + constructor(model: SingleLineTestModel) { + this._model = model; + } + + public paint(output: HTMLElement) { + let r = ''; + for (let i = 1; i <= this._model.getLineCount(); i++) { + let content = this._model.getModelLineContent(i); + r += content + '
'; + } + output.innerHTML = r; + } +} + +function doCreateTest(description: string, inputStr: string, expectedStr: string): HTMLElement { + let cursorOffset: number = 0; + let cursorLength: number = 0; + + let container = document.createElement('div'); + container.className = 'container'; + + let title = document.createElement('div'); + title.className = 'title'; + + title.innerHTML = description + '. Type ' + inputStr + ''; + container.appendChild(title); + + let startBtn = document.createElement('button'); + startBtn.innerHTML = 'Start'; + container.appendChild(startBtn); + + + let input = document.createElement('textarea'); + input.setAttribute('rows', '10'); + input.setAttribute('cols', '40'); + container.appendChild(input); + + let model = new SingleLineTestModel('some text'); + + const textAreaInputHost: ITextAreaInputHost = { + getPlainTextToCopy: (): string => '', + getHTMLToCopy: (): string => '', + getScreenReaderContent: (currentState: TextAreaState): TextAreaState => { + + if (browser.isIPad) { + // Do not place anything in the textarea for the iPad + return TextAreaState.EMPTY; + } + + const selection = new Range(1, 1 + cursorOffset, 1, 1 + cursorOffset + cursorLength); + + return PagedScreenReaderStrategy.fromEditorSelection(currentState, model, selection); + }, + deduceModelPosition: (viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position => { + return null; + } + }; + + let handler = new TextAreaInput(textAreaInputHost, createFastDomNode(input)); + + let output = document.createElement('pre'); + output.className = 'output'; + container.appendChild(output); + + let check = document.createElement('pre'); + check.className = 'check'; + container.appendChild(check); + + let br = document.createElement('br'); + br.style.clear = 'both'; + container.appendChild(br); + + let view = new TestView(model); + + let updatePosition = (off: number, len: number) => { + cursorOffset = off; + cursorLength = len; + handler.writeScreenReaderContent('selection changed'); + handler.focusTextArea(); + }; + + let updateModelAndPosition = (text: string, off: number, len: number) => { + model._setText(text); + updatePosition(off, len); + view.paint(output); + + let expected = 'some ' + expectedStr + ' text'; + if (text === expected) { + check.innerHTML = '[GOOD]'; + check.className = 'check good'; + } else { + check.innerHTML = '[BAD]'; + check.className = 'check bad'; + } + check.innerHTML += expected; + }; + + handler.onType((e) => { + console.log('type text: ' + e.text + ', replaceCharCnt: ' + e.replaceCharCnt); + let text = model.getModelLineContent(1); + let preText = text.substring(0, cursorOffset - e.replaceCharCnt); + let postText = text.substring(cursorOffset + cursorLength); + let midText = e.text; + + updateModelAndPosition(preText + midText + postText, (preText + midText).length, 0); + }); + + view.paint(output); + + startBtn.onclick = function () { + updateModelAndPosition('some text', 5, 0); + input.focus(); + }; + + return container; +} + +const TESTS = [ + { description: 'Japanese IME 1', in: 'sennsei [Enter]', out: 'せんせい' }, + { description: 'Japanese IME 2', in: 'konnichiha [Enter]', out: 'こんいちは' }, + { description: 'Japanese IME 3', in: 'mikann [Enter]', out: 'みかん' }, + { description: 'Korean IME 1', in: 'gksrmf [Space]', out: '한글 ' }, + { description: 'Chinese IME 1', in: '.,', out: '。,' }, + { description: 'Chinese IME 2', in: 'ni [Space] hao [Space]', out: '你好' }, + { description: 'Chinese IME 3', in: 'hazni [Space]', out: '哈祝你' }, + { description: 'Mac dead key 1', in: '`.', out: '`.' }, + { description: 'Mac hold key 1', in: 'e long press and 1', out: 'é' } +]; + +TESTS.forEach((t) => { + document.body.appendChild(doCreateTest(t.description, t.in, t.out)); +}); diff --git a/src/vs/editor/test/browser/controller/inputRecorder.html b/src/vs/editor/test/browser/controller/inputRecorder.html new file mode 100644 index 0000000000..3f538b8a04 --- /dev/null +++ b/src/vs/editor/test/browser/controller/inputRecorder.html @@ -0,0 +1,115 @@ + + + + + + + + +

Input Recorder

+

Records input events and logs them in the console

+ + +

+ + + + + \ No newline at end of file diff --git a/src/vs/editor/test/browser/controller/textAreaState.test.ts b/src/vs/editor/test/browser/controller/textAreaState.test.ts new file mode 100644 index 0000000000..8136866b87 --- /dev/null +++ b/src/vs/editor/test/browser/controller/textAreaState.test.ts @@ -0,0 +1,643 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { ISimpleModel, TextAreaState, ITextAreaWrapper, PagedScreenReaderStrategy } from 'vs/editor/browser/controller/textAreaState'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { EndOfLinePreference } from 'vs/editor/common/editorCommon'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Model } from 'vs/editor/common/model/model'; +import { Selection } from 'vs/editor/common/core/selection'; + +export class MockTextAreaWrapper extends Disposable implements ITextAreaWrapper { + + public _value: string; + public _selectionStart: number; + public _selectionEnd: number; + + constructor() { + super(); + this._value = ''; + this._selectionStart = 0; + this._selectionEnd = 0; + } + + public getValue(): string { + return this._value; + } + + public setValue(reason: string, value: string): void { + this._value = value; + this._selectionStart = this._value.length; + this._selectionEnd = this._value.length; + } + + public getSelectionStart(): number { + return this._selectionStart; + } + + public getSelectionEnd(): number { + return this._selectionEnd; + } + + public setSelectionRange(reason: string, selectionStart: number, selectionEnd: number): void { + if (selectionStart < 0) { + selectionStart = 0; + } + if (selectionStart > this._value.length) { + selectionStart = this._value.length; + } + if (selectionEnd < 0) { + selectionEnd = 0; + } + if (selectionEnd > this._value.length) { + selectionEnd = this._value.length; + } + this._selectionStart = selectionStart; + this._selectionEnd = selectionEnd; + } +} + +suite('TextAreaState', () => { + + function assertTextAreaState(actual: TextAreaState, value: string, selectionStart: number, selectionEnd: number): void { + let desired = new TextAreaState(value, selectionStart, selectionEnd, null, null); + assert.ok(desired.equals(actual), desired.toString() + ' == ' + actual.toString()); + } + + test('fromTextArea', () => { + let textArea = new MockTextAreaWrapper(); + textArea._value = 'Hello world!'; + textArea._selectionStart = 1; + textArea._selectionEnd = 12; + let actual = TextAreaState.EMPTY.readFromTextArea(textArea); + + assertTextAreaState(actual, 'Hello world!', 1, 12); + assert.equal(actual.value, 'Hello world!'); + assert.equal(actual.selectionStart, 1); + + actual = actual.collapseSelection(); + assertTextAreaState(actual, 'Hello world!', 12, 12); + + textArea.dispose(); + }); + + test('applyToTextArea', () => { + let textArea = new MockTextAreaWrapper(); + textArea._value = 'Hello world!'; + textArea._selectionStart = 1; + textArea._selectionEnd = 12; + + let state = new TextAreaState('Hi world!', 2, 2, null, null); + state.writeToTextArea('test', textArea, false); + + assert.equal(textArea._value, 'Hi world!'); + assert.equal(textArea._selectionStart, 9); + assert.equal(textArea._selectionEnd, 9); + + state = new TextAreaState('Hi world!', 3, 3, null, null); + state.writeToTextArea('test', textArea, false); + + assert.equal(textArea._value, 'Hi world!'); + assert.equal(textArea._selectionStart, 9); + assert.equal(textArea._selectionEnd, 9); + + state = new TextAreaState('Hi world!', 0, 2, null, null); + state.writeToTextArea('test', textArea, true); + + assert.equal(textArea._value, 'Hi world!'); + assert.equal(textArea._selectionStart, 0); + assert.equal(textArea._selectionEnd, 2); + + textArea.dispose(); + }); + + function testDeduceInput(prevState: TextAreaState, value: string, selectionStart: number, selectionEnd: number, expected: string, expectedCharReplaceCnt: number): void { + prevState = prevState || TextAreaState.EMPTY; + + let textArea = new MockTextAreaWrapper(); + textArea._value = value; + textArea._selectionStart = selectionStart; + textArea._selectionEnd = selectionEnd; + + let newState = prevState.readFromTextArea(textArea); + let actual = TextAreaState.deduceInput(prevState, newState, true); + + assert.equal(actual.text, expected); + assert.equal(actual.replaceCharCnt, expectedCharReplaceCnt); + + textArea.dispose(); + } + + test('deduceInput - Japanese typing sennsei and accepting', () => { + // manual test: + // - choose keyboard layout: Japanese -> Hiragama + // - type sennsei + // - accept with Enter + // - expected: せんせい + + // s + // PREVIOUS STATE: [ <>, selectionStart: 0, selectionEnd: 0, selectionToken: 0] + // CURRENT STATE: [ <s>, selectionStart: 0, selectionEnd: 1, selectionToken: 0] + testDeduceInput( + TextAreaState.EMPTY, + 's', + 0, 1, + 's', 0 + ); + + // e + // PREVIOUS STATE: [ <s>, selectionStart: 0, selectionEnd: 1, selectionToken: 0] + // CURRENT STATE: [ <せ>, selectionStart: 0, selectionEnd: 1, selectionToken: 0] + testDeduceInput( + new TextAreaState('s', 0, 1, null, null), + 'せ', + 0, 1, + 'せ', 1 + ); + + // n + // PREVIOUS STATE: [ <せ>, selectionStart: 0, selectionEnd: 1, selectionToken: 0] + // CURRENT STATE: [ <せn>, selectionStart: 0, selectionEnd: 2, selectionToken: 0] + testDeduceInput( + new TextAreaState('せ', 0, 1, null, null), + 'せn', + 0, 2, + 'せn', 1 + ); + + // n + // PREVIOUS STATE: [ <せn>, selectionStart: 0, selectionEnd: 2, selectionToken: 0] + // CURRENT STATE: [ <せん>, selectionStart: 0, selectionEnd: 2, selectionToken: 0] + testDeduceInput( + new TextAreaState('せn', 0, 2, null, null), + 'せん', + 0, 2, + 'せん', 2 + ); + + // s + // PREVIOUS STATE: [ <せん>, selectionStart: 0, selectionEnd: 2, selectionToken: 0] + // CURRENT STATE: [ <せんs>, selectionStart: 0, selectionEnd: 3, selectionToken: 0] + testDeduceInput( + new TextAreaState('せん', 0, 2, null, null), + 'せんs', + 0, 3, + 'せんs', 2 + ); + + // e + // PREVIOUS STATE: [ <せんs>, selectionStart: 0, selectionEnd: 3, selectionToken: 0] + // CURRENT STATE: [ <せんせ>, selectionStart: 0, selectionEnd: 3, selectionToken: 0] + testDeduceInput( + new TextAreaState('せんs', 0, 3, null, null), + 'せんせ', + 0, 3, + 'せんせ', 3 + ); + + // no-op? [was recorded] + // PREVIOUS STATE: [ <せんせ>, selectionStart: 0, selectionEnd: 3, selectionToken: 0] + // CURRENT STATE: [ <せんせ>, selectionStart: 0, selectionEnd: 3, selectionToken: 0] + testDeduceInput( + new TextAreaState('せんせ', 0, 3, null, null), + 'せんせ', + 0, 3, + 'せんせ', 3 + ); + + // i + // PREVIOUS STATE: [ <せんせ>, selectionStart: 0, selectionEnd: 3, selectionToken: 0] + // CURRENT STATE: [ <せんせい>, selectionStart: 0, selectionEnd: 4, selectionToken: 0] + testDeduceInput( + new TextAreaState('せんせ', 0, 3, null, null), + 'せんせい', + 0, 4, + 'せんせい', 3 + ); + + // ENTER (accept) + // PREVIOUS STATE: [ <せんせい>, selectionStart: 0, selectionEnd: 4, selectionToken: 0] + // CURRENT STATE: [ <せんせい>, selectionStart: 4, selectionEnd: 4, selectionToken: 0] + testDeduceInput( + new TextAreaState('せんせい', 0, 4, null, null), + 'せんせい', + 4, 4, + '', 0 + ); + }); + + test('deduceInput - Japanese typing sennsei and choosing different suggestion', () => { + // manual test: + // - choose keyboard layout: Japanese -> Hiragama + // - type sennsei + // - arrow down (choose next suggestion) + // - accept with Enter + // - expected: せんせい + + // sennsei + // PREVIOUS STATE: [ <せんせい>, selectionStart: 0, selectionEnd: 4, selectionToken: 0] + // CURRENT STATE: [ <せんせい>, selectionStart: 0, selectionEnd: 4, selectionToken: 0] + testDeduceInput( + new TextAreaState('せんせい', 0, 4, null, null), + 'せんせい', + 0, 4, + 'せんせい', 4 + ); + + // arrow down + // CURRENT STATE: [ <先生>, selectionStart: 0, selectionEnd: 2, selectionToken: 0] + // PREVIOUS STATE: [ <せんせい>, selectionStart: 0, selectionEnd: 4, selectionToken: 0] + testDeduceInput( + new TextAreaState('せんせい', 0, 4, null, null), + '先生', + 0, 2, + '先生', 4 + ); + + // ENTER (accept) + // PREVIOUS STATE: [ <先生>, selectionStart: 0, selectionEnd: 2, selectionToken: 0] + // CURRENT STATE: [ <先生>, selectionStart: 2, selectionEnd: 2, selectionToken: 0] + testDeduceInput( + new TextAreaState('先生', 0, 2, null, null), + '先生', + 2, 2, + '', 0 + ); + }); + + test('extractNewText - no previous state with selection', () => { + testDeduceInput( + null, + 'a', + 0, 1, + 'a', 0 + ); + }); + + test('issue #2586: Replacing selected end-of-line with newline locks up the document', () => { + testDeduceInput( + new TextAreaState(']\n', 1, 2, null, null), + ']\n', + 2, 2, + '\n', 0 + ); + }); + + test('extractNewText - no previous state without selection', () => { + testDeduceInput( + null, + 'a', + 1, 1, + 'a', 0 + ); + }); + + test('extractNewText - typing does not cause a selection', () => { + testDeduceInput( + TextAreaState.EMPTY, + 'a', + 0, 1, + 'a', 0 + ); + }); + + test('extractNewText - had the textarea empty', () => { + testDeduceInput( + TextAreaState.EMPTY, + 'a', + 1, 1, + 'a', 0 + ); + }); + + test('extractNewText - had the entire line selected', () => { + testDeduceInput( + new TextAreaState('Hello world!', 0, 12, null, null), + 'H', + 1, 1, + 'H', 0 + ); + }); + + test('extractNewText - had previous text 1', () => { + testDeduceInput( + new TextAreaState('Hello world!', 12, 12, null, null), + 'Hello world!a', + 13, 13, + 'a', 0 + ); + }); + + test('extractNewText - had previous text 2', () => { + testDeduceInput( + new TextAreaState('Hello world!', 0, 0, null, null), + 'aHello world!', + 1, 1, + 'a', 0 + ); + }); + + test('extractNewText - had previous text 3', () => { + testDeduceInput( + new TextAreaState('Hello world!', 6, 11, null, null), + 'Hello other!', + 11, 11, + 'other', 0 + ); + }); + + test('extractNewText - IME', () => { + testDeduceInput( + TextAreaState.EMPTY, + 'これは', + 3, 3, + 'これは', 0 + ); + }); + + test('extractNewText - isInOverwriteMode', () => { + testDeduceInput( + new TextAreaState('Hello world!', 0, 0, null, null), + 'Aello world!', + 1, 1, + 'A', 0 + ); + }); + + test('extractMacReplacedText - does nothing if there is selection', () => { + testDeduceInput( + new TextAreaState('Hello world!', 5, 5, null, null), + 'Hellö world!', + 4, 5, + 'ö', 0 + ); + }); + + test('extractMacReplacedText - does nothing if there is more than one extra char', () => { + testDeduceInput( + new TextAreaState('Hello world!', 5, 5, null, null), + 'Hellöö world!', + 5, 5, + 'öö', 1 + ); + }); + + test('extractMacReplacedText - does nothing if there is more than one changed char', () => { + testDeduceInput( + new TextAreaState('Hello world!', 5, 5, null, null), + 'Helöö world!', + 5, 5, + 'öö', 2 + ); + }); + + test('extractMacReplacedText', () => { + testDeduceInput( + new TextAreaState('Hello world!', 5, 5, null, null), + 'Hellö world!', + 5, 5, + 'ö', 1 + ); + }); + + test('issue #25101 - First key press ignored', () => { + testDeduceInput( + new TextAreaState('a', 0, 1, null, null), + 'a', + 1, 1, + 'a', 0 + ); + }); + + test('issue #16520 - Cmd-d of single character followed by typing same character as has no effect', () => { + testDeduceInput( + new TextAreaState('x x', 0, 1, null, null), + 'x x', + 1, 1, + 'x', 0 + ); + }); + + test('issue #4271 (example 1) - When inserting an emoji on OSX, it is placed two spaces left of the cursor', () => { + // The OSX emoji inserter inserts emojis at random positions in the text, unrelated to where the cursor is. + testDeduceInput( + new TextAreaState( + [ + 'some1 text', + 'some2 text', + 'some3 text', + 'some4 text', // cursor is here in the middle of the two spaces + 'some5 text', + 'some6 text', + 'some7 text' + ].join('\n'), + 42, 42, + null, null + ), + [ + 'so📅me1 text', + 'some2 text', + 'some3 text', + 'some4 text', + 'some5 text', + 'some6 text', + 'some7 text' + ].join('\n'), + 4, 4, + '📅', 0 + ); + }); + + test('issue #4271 (example 2) - When inserting an emoji on OSX, it is placed two spaces left of the cursor', () => { + // The OSX emoji inserter inserts emojis at random positions in the text, unrelated to where the cursor is. + testDeduceInput( + new TextAreaState( + 'some1 text', + 6, 6, + null, null + ), + 'some💊1 text', + 6, 6, + '💊', 0 + ); + }); + + test('issue #4271 (example 3) - When inserting an emoji on OSX, it is placed two spaces left of the cursor', () => { + // The OSX emoji inserter inserts emojis at random positions in the text, unrelated to where the cursor is. + testDeduceInput( + new TextAreaState( + 'qwertyu\nasdfghj\nzxcvbnm', + 12, 12, + null, null + ), + 'qwertyu\nasdfghj\nzxcvbnm🎈', + 25, 25, + '🎈', 0 + ); + }); + + // an example of an emoji missed by the regex but which has the FE0F variant 16 hint + test('issue #4271 (example 4) - When inserting an emoji on OSX, it is placed two spaces left of the cursor', () => { + // The OSX emoji inserter inserts emojis at random positions in the text, unrelated to where the cursor is. + testDeduceInput( + new TextAreaState( + 'some1 text', + 6, 6, + null, null + ), + 'some⌨️1 text', + 6, 6, + '⌨️', 0 + ); + }); + + suite('PagedScreenReaderStrategy', () => { + + function testPagedScreenReaderStrategy(lines: string[], selection: Selection, expected: TextAreaState): void { + const model = Model.createFromString(lines.join('\n')); + const actual = PagedScreenReaderStrategy.fromEditorSelection(TextAreaState.EMPTY, model, selection); + assert.ok(actual.equals(expected)); + model.dispose(); + } + + test('simple', () => { + testPagedScreenReaderStrategy( + [ + 'Hello world!' + ], + new Selection(1, 13, 1, 13), + new TextAreaState('Hello world!', 12, 12, new Position(1, 13), new Position(1, 13)) + ); + + testPagedScreenReaderStrategy( + [ + 'Hello world!' + ], + new Selection(1, 1, 1, 1), + new TextAreaState('Hello world!', 0, 0, new Position(1, 1), new Position(1, 1)) + ); + + testPagedScreenReaderStrategy( + [ + 'Hello world!' + ], + new Selection(1, 1, 1, 6), + new TextAreaState('Hello world!', 0, 5, new Position(1, 1), new Position(1, 6)) + ); + }); + + test('multiline', () => { + testPagedScreenReaderStrategy( + [ + 'Hello world!', + 'How are you?' + ], + new Selection(1, 1, 1, 1), + new TextAreaState('Hello world!\nHow are you?', 0, 0, new Position(1, 1), new Position(1, 1)) + ); + + testPagedScreenReaderStrategy( + [ + 'Hello world!', + 'How are you?' + ], + new Selection(2, 1, 2, 1), + new TextAreaState('Hello world!\nHow are you?', 13, 13, new Position(2, 1), new Position(2, 1)) + ); + }); + + test('page', () => { + testPagedScreenReaderStrategy( + [ + 'L1\nL2\nL3\nL4\nL5\nL6\nL7\nL8\nL9\nL10\nL11\nL12\nL13\nL14\nL15\nL16\nL17\nL18\nL19\nL20\nL21' + ], + new Selection(1, 1, 1, 1), + new TextAreaState('L1\nL2\nL3\nL4\nL5\nL6\nL7\nL8\nL9\nL10\n', 0, 0, new Position(1, 1), new Position(1, 1)) + ); + + testPagedScreenReaderStrategy( + [ + 'L1\nL2\nL3\nL4\nL5\nL6\nL7\nL8\nL9\nL10\nL11\nL12\nL13\nL14\nL15\nL16\nL17\nL18\nL19\nL20\nL21' + ], + new Selection(11, 1, 11, 1), + new TextAreaState('L11\nL12\nL13\nL14\nL15\nL16\nL17\nL18\nL19\nL20\n', 0, 0, new Position(11, 1), new Position(11, 1)) + ); + + testPagedScreenReaderStrategy( + [ + 'L1\nL2\nL3\nL4\nL5\nL6\nL7\nL8\nL9\nL10\nL11\nL12\nL13\nL14\nL15\nL16\nL17\nL18\nL19\nL20\nL21' + ], + new Selection(12, 1, 12, 1), + new TextAreaState('L11\nL12\nL13\nL14\nL15\nL16\nL17\nL18\nL19\nL20\n', 4, 4, new Position(12, 1), new Position(12, 1)) + ); + + testPagedScreenReaderStrategy( + [ + 'L1\nL2\nL3\nL4\nL5\nL6\nL7\nL8\nL9\nL10\nL11\nL12\nL13\nL14\nL15\nL16\nL17\nL18\nL19\nL20\nL21' + ], + new Selection(21, 1, 21, 1), + new TextAreaState('L21', 0, 0, new Position(21, 1), new Position(21, 1)) + ); + }); + + }); +}); + +class SimpleModel implements ISimpleModel { + + private _lines: string[]; + private _eol: string; + + constructor(lines: string[], eol: string) { + this._lines = lines; + this._eol = eol; + } + + public getLineMaxColumn(lineNumber: number): number { + return this._lines[lineNumber - 1].length + 1; + } + + private _getEndOfLine(eol: EndOfLinePreference): string { + switch (eol) { + case EndOfLinePreference.LF: + return '\n'; + case EndOfLinePreference.CRLF: + return '\r\n'; + case EndOfLinePreference.TextDefined: + return this._eol; + } + throw new Error('Unknown EOL preference'); + } + + public getValueInRange(range: Range, eol: EndOfLinePreference): string { + if (Range.isEmpty(range)) { + return ''; + } + + if (range.startLineNumber === range.endLineNumber) { + return this._lines[range.startLineNumber - 1].substring(range.startColumn - 1, range.endColumn - 1); + } + + var lineEnding = this._getEndOfLine(eol), + startLineIndex = range.startLineNumber - 1, + endLineIndex = range.endLineNumber - 1, + resultLines: string[] = []; + + resultLines.push(this._lines[startLineIndex].substring(range.startColumn - 1)); + for (var i = startLineIndex + 1; i < endLineIndex; i++) { + resultLines.push(this._lines[i]); + } + resultLines.push(this._lines[endLineIndex].substring(0, range.endColumn - 1)); + + return resultLines.join(lineEnding); + } + + public getLineCount(): number { + return this._lines.length; + } +} diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts new file mode 100644 index 0000000000..9b77149c28 --- /dev/null +++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import * as dom from 'vs/base/browser/dom'; +import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; +import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; +import { TestTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; + +const themeServiceMock = new TestThemeService(); + +suite('Decoration Render Options', () => { + test('register and resolve decoration type', () => { + assert.equal(1, 1); + }); +}); diff --git a/src/vs/editor/test/browser/view/minimapFontCreator.html b/src/vs/editor/test/browser/view/minimapFontCreator.html new file mode 100644 index 0000000000..9ddd334797 --- /dev/null +++ b/src/vs/editor/test/browser/view/minimapFontCreator.html @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/vs/editor/test/browser/view/minimapFontCreator.ts b/src/vs/editor/test/browser/view/minimapFontCreator.ts new file mode 100644 index 0000000000..8abe76242e --- /dev/null +++ b/src/vs/editor/test/browser/view/minimapFontCreator.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Constants, MinimapCharRenderer } from 'vs/editor/common/view/minimapCharRenderer'; +import { MinimapCharRendererFactory } from 'vs/editor/test/common/view/minimapCharRendererFactory'; +import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; +import { RGBA8 } from 'vs/editor/common/core/rgba'; + +let canvas = document.getElementById('my-canvas'); +let ctx = canvas.getContext('2d'); + +canvas.style.height = 100 + 'px'; +canvas.height = 100; + +canvas.width = Constants.CHAR_COUNT * Constants.SAMPLED_CHAR_WIDTH; +canvas.style.width = (Constants.CHAR_COUNT * Constants.SAMPLED_CHAR_WIDTH) + 'px'; + +ctx.fillStyle = '#ffffff'; +ctx.font = 'bold 16px monospace'; +for (let chCode = Constants.START_CH_CODE; chCode <= Constants.END_CH_CODE; chCode++) { + ctx.fillText(String.fromCharCode(chCode), (chCode - Constants.START_CH_CODE) * Constants.SAMPLED_CHAR_WIDTH, Constants.SAMPLED_CHAR_HEIGHT); +} + +let sampleData = ctx.getImageData(0, 4, Constants.SAMPLED_CHAR_WIDTH * Constants.CHAR_COUNT, Constants.SAMPLED_CHAR_HEIGHT); +let minimapCharRenderer = MinimapCharRendererFactory.create(sampleData.data); + +renderImageData(sampleData, 10, 100); +renderMinimapCharRenderer(minimapCharRenderer, 400); +renderMinimapCharRenderer(getOrCreateMinimapCharRenderer(), 600); + +function createFakeImageData(width: number, height: number): ImageData { + return { + width: width, + height: height, + data: new Uint8ClampedArray(width * height * Constants.RGBA_CHANNELS_CNT) + }; +} + +function renderMinimapCharRenderer(minimapCharRenderer: MinimapCharRenderer, y: number): void { + + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); + + { + let x2 = createFakeImageData(Constants.x2_CHAR_WIDTH * Constants.CHAR_COUNT, Constants.x2_CHAR_HEIGHT); + // set the background color + for (let i = 0, len = x2.data.length / 4; i < len; i++) { + x2.data[4 * i + 0] = background.r; + x2.data[4 * i + 1] = background.g; + x2.data[4 * i + 2] = background.b; + x2.data[4 * i + 3] = 255; + } + let dx = 0; + for (let chCode = Constants.START_CH_CODE; chCode <= Constants.END_CH_CODE; chCode++) { + minimapCharRenderer.x2RenderChar(x2, dx, 0, chCode, color, background, false); + dx += Constants.x2_CHAR_WIDTH; + } + renderImageData(x2, 10, y); + } + { + let x1 = createFakeImageData(Constants.x1_CHAR_WIDTH * Constants.CHAR_COUNT, Constants.x1_CHAR_HEIGHT); + // set the background color + for (let i = 0, len = x1.data.length / 4; i < len; i++) { + x1.data[4 * i + 0] = background.r; + x1.data[4 * i + 1] = background.g; + x1.data[4 * i + 2] = background.b; + x1.data[4 * i + 3] = 255; + } + let dx = 0; + for (let chCode = Constants.START_CH_CODE; chCode <= Constants.END_CH_CODE; chCode++) { + minimapCharRenderer.x1RenderChar(x1, dx, 0, chCode, color, background, false); + dx += Constants.x1_CHAR_WIDTH; + } + renderImageData(x1, 10, y + 100); + } +} + +(function () { + let r = 'let x2Data = [', offset = 0; + for (let charIndex = 0; charIndex < Constants.CHAR_COUNT; charIndex++) { + let charCode = charIndex + Constants.START_CH_CODE; + r += '\n\n// ' + String.fromCharCode(charCode); + + for (let i = 0; i < Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH; i++) { + if (i % 2 === 0) { + r += '\n'; + } + r += minimapCharRenderer.x2charData[offset] + ','; + offset++; + } + + } + r += '\n\n]'; + console.log(r); +})(); + +(function () { + let r = 'let x1Data = [', offset = 0; + for (let charIndex = 0; charIndex < Constants.CHAR_COUNT; charIndex++) { + let charCode = charIndex + Constants.START_CH_CODE; + r += '\n\n// ' + String.fromCharCode(charCode); + + for (let i = 0; i < Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH; i++) { + r += '\n'; + r += minimapCharRenderer.x1charData[offset] + ','; + offset++; + } + + } + r += '\n\n]'; + console.log(r); +})(); + + + +function renderImageData(imageData: ImageData, left: number, top: number): void { + let output = ''; + var offset = 0; + var PX_SIZE = 15; + for (var i = 0; i < imageData.height; i++) { + for (var j = 0; j < imageData.width; j++) { + var R = imageData.data[offset]; + var G = imageData.data[offset + 1]; + var B = imageData.data[offset + 2]; + var A = imageData.data[offset + 3]; + offset += 4; + + output += `
`; + } + } + + var domNode = document.createElement('div'); + domNode.style.position = 'absolute'; + domNode.style.top = top + 'px'; + domNode.style.left = left + 'px'; + domNode.style.width = (imageData.width * PX_SIZE) + 'px'; + domNode.style.height = (imageData.height * PX_SIZE) + 'px'; + domNode.style.border = '1px solid #ccc'; + domNode.style.background = '#000000'; + domNode.innerHTML = output; + document.body.appendChild(domNode); +} diff --git a/src/vs/editor/test/browser/view/viewLayer.test.ts b/src/vs/editor/test/browser/view/viewLayer.test.ts new file mode 100644 index 0000000000..14298ffd1e --- /dev/null +++ b/src/vs/editor/test/browser/view/viewLayer.test.ts @@ -0,0 +1,753 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { RenderedLinesCollection, ILine } from 'vs/editor/browser/view/viewLayer'; + +class TestLine implements ILine { + + _pinged = false; + constructor(public id: string) { + } + + onContentChanged(): void { + this._pinged = true; + } + onTokensChanged(): void { + this._pinged = true; + } +} + +interface ILinesCollectionState { + startLineNumber: number; + lines: string[]; + pinged: boolean[]; +} + +function assertState(col: RenderedLinesCollection, state: ILinesCollectionState): void { + let actualState: ILinesCollectionState = { + startLineNumber: col.getStartLineNumber(), + lines: [], + pinged: [] + }; + for (let lineNumber = col.getStartLineNumber(); lineNumber <= col.getEndLineNumber(); lineNumber++) { + actualState.lines.push(col.getLine(lineNumber).id); + actualState.pinged.push(col.getLine(lineNumber)._pinged); + } + assert.deepEqual(actualState, state); +} + +suite('RenderedLinesCollection onLinesDeleted', () => { + + function testOnModelLinesDeleted(deleteFromLineNumber: number, deleteToLineNumber: number, expectedDeleted: string[], expectedState: ILinesCollectionState): void { + let col = new RenderedLinesCollection(() => new TestLine('new')); + col._set(6, [ + new TestLine('old6'), + new TestLine('old7'), + new TestLine('old8'), + new TestLine('old9') + ]); + let actualDeleted1 = col.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); + let actualDeleted: string[] = []; + if (actualDeleted1) { + actualDeleted = actualDeleted1.map(line => line.id); + } + assert.deepEqual(actualDeleted, expectedDeleted); + assertState(col, expectedState); + } + + test('A1', () => { + testOnModelLinesDeleted(3, 3, [], { + startLineNumber: 5, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('A2', () => { + testOnModelLinesDeleted(3, 4, [], { + startLineNumber: 4, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('A3', () => { + testOnModelLinesDeleted(3, 5, [], { + startLineNumber: 3, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('A4', () => { + testOnModelLinesDeleted(3, 6, ['old6'], { + startLineNumber: 3, + lines: ['old7', 'old8', 'old9'], + pinged: [false, false, false] + }); + }); + + test('A5', () => { + testOnModelLinesDeleted(3, 7, ['old6', 'old7'], { + startLineNumber: 3, + lines: ['old8', 'old9'], + pinged: [false, false] + }); + }); + + test('A6', () => { + testOnModelLinesDeleted(3, 8, ['old6', 'old7', 'old8'], { + startLineNumber: 3, + lines: ['old9'], + pinged: [false] + }); + }); + + test('A7', () => { + testOnModelLinesDeleted(3, 9, ['old6', 'old7', 'old8', 'old9'], { + startLineNumber: 3, + lines: [], + pinged: [] + }); + }); + + test('A8', () => { + testOnModelLinesDeleted(3, 10, ['old6', 'old7', 'old8', 'old9'], { + startLineNumber: 3, + lines: [], + pinged: [] + }); + }); + + + test('B1', () => { + testOnModelLinesDeleted(5, 5, [], { + startLineNumber: 5, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('B2', () => { + testOnModelLinesDeleted(5, 6, ['old6'], { + startLineNumber: 5, + lines: ['old7', 'old8', 'old9'], + pinged: [false, false, false] + }); + }); + + test('B3', () => { + testOnModelLinesDeleted(5, 7, ['old6', 'old7'], { + startLineNumber: 5, + lines: ['old8', 'old9'], + pinged: [false, false] + }); + }); + + test('B4', () => { + testOnModelLinesDeleted(5, 8, ['old6', 'old7', 'old8'], { + startLineNumber: 5, + lines: ['old9'], + pinged: [false] + }); + }); + + test('B5', () => { + testOnModelLinesDeleted(5, 9, ['old6', 'old7', 'old8', 'old9'], { + startLineNumber: 5, + lines: [], + pinged: [] + }); + }); + + test('B6', () => { + testOnModelLinesDeleted(5, 10, ['old6', 'old7', 'old8', 'old9'], { + startLineNumber: 5, + lines: [], + pinged: [] + }); + }); + + + test('C1', () => { + testOnModelLinesDeleted(6, 6, ['old6'], { + startLineNumber: 6, + lines: ['old7', 'old8', 'old9'], + pinged: [false, false, false] + }); + }); + + test('C2', () => { + testOnModelLinesDeleted(6, 7, ['old6', 'old7'], { + startLineNumber: 6, + lines: ['old8', 'old9'], + pinged: [false, false] + }); + }); + + test('C3', () => { + testOnModelLinesDeleted(6, 8, ['old6', 'old7', 'old8'], { + startLineNumber: 6, + lines: ['old9'], + pinged: [false] + }); + }); + + test('C4', () => { + testOnModelLinesDeleted(6, 9, ['old6', 'old7', 'old8', 'old9'], { + startLineNumber: 6, + lines: [], + pinged: [] + }); + }); + + test('C5', () => { + testOnModelLinesDeleted(6, 10, ['old6', 'old7', 'old8', 'old9'], { + startLineNumber: 6, + lines: [], + pinged: [] + }); + }); + + + test('D1', () => { + testOnModelLinesDeleted(7, 7, ['old7'], { + startLineNumber: 6, + lines: ['old6', 'old8', 'old9'], + pinged: [false, false, false] + }); + }); + + test('D2', () => { + testOnModelLinesDeleted(7, 8, ['old7', 'old8'], { + startLineNumber: 6, + lines: ['old6', 'old9'], + pinged: [false, false] + }); + }); + + test('D3', () => { + testOnModelLinesDeleted(7, 9, ['old7', 'old8', 'old9'], { + startLineNumber: 6, + lines: ['old6'], + pinged: [false] + }); + }); + + test('D4', () => { + testOnModelLinesDeleted(7, 10, ['old7', 'old8', 'old9'], { + startLineNumber: 6, + lines: ['old6'], + pinged: [false] + }); + }); + + + test('E1', () => { + testOnModelLinesDeleted(8, 8, ['old8'], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old9'], + pinged: [false, false, false] + }); + }); + + test('E2', () => { + testOnModelLinesDeleted(8, 9, ['old8', 'old9'], { + startLineNumber: 6, + lines: ['old6', 'old7'], + pinged: [false, false] + }); + }); + + test('E3', () => { + testOnModelLinesDeleted(8, 10, ['old8', 'old9'], { + startLineNumber: 6, + lines: ['old6', 'old7'], + pinged: [false, false] + }); + }); + + + test('F1', () => { + testOnModelLinesDeleted(9, 9, ['old9'], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8'], + pinged: [false, false, false] + }); + }); + + test('F2', () => { + testOnModelLinesDeleted(9, 10, ['old9'], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8'], + pinged: [false, false, false] + }); + }); + + + test('G1', () => { + testOnModelLinesDeleted(10, 10, [], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('G2', () => { + testOnModelLinesDeleted(10, 11, [], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + + test('H1', () => { + testOnModelLinesDeleted(11, 13, [], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); +}); + +suite('RenderedLinesCollection onLineChanged', () => { + + function testOnModelLineChanged(changedLineNumber: number, expectedPinged: boolean, expectedState: ILinesCollectionState): void { + let col = new RenderedLinesCollection(() => new TestLine('new')); + col._set(6, [ + new TestLine('old6'), + new TestLine('old7'), + new TestLine('old8'), + new TestLine('old9') + ]); + let actualPinged = col.onLinesChanged(changedLineNumber, changedLineNumber); + assert.deepEqual(actualPinged, expectedPinged); + assertState(col, expectedState); + } + + test('3', () => { + testOnModelLineChanged(3, false, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + test('4', () => { + testOnModelLineChanged(4, false, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + test('5', () => { + testOnModelLineChanged(5, false, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + test('6', () => { + testOnModelLineChanged(6, true, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [true, false, false, false] + }); + }); + test('7', () => { + testOnModelLineChanged(7, true, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, true, false, false] + }); + }); + test('8', () => { + testOnModelLineChanged(8, true, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, true, false] + }); + }); + test('9', () => { + testOnModelLineChanged(9, true, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, true] + }); + }); + test('10', () => { + testOnModelLineChanged(10, false, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + test('11', () => { + testOnModelLineChanged(11, false, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + +}); + +suite('RenderedLinesCollection onLinesInserted', () => { + + function testOnModelLinesInserted(insertFromLineNumber: number, insertToLineNumber: number, expectedDeleted: string[], expectedState: ILinesCollectionState): void { + let col = new RenderedLinesCollection(() => new TestLine('new')); + col._set(6, [ + new TestLine('old6'), + new TestLine('old7'), + new TestLine('old8'), + new TestLine('old9') + ]); + let actualDeleted1 = col.onLinesInserted(insertFromLineNumber, insertToLineNumber); + let actualDeleted: string[] = []; + if (actualDeleted1) { + actualDeleted = actualDeleted1.map(line => line.id); + } + assert.deepEqual(actualDeleted, expectedDeleted); + assertState(col, expectedState); + } + + test('A1', () => { + testOnModelLinesInserted(3, 3, [], { + startLineNumber: 7, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('A2', () => { + testOnModelLinesInserted(3, 4, [], { + startLineNumber: 8, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('A3', () => { + testOnModelLinesInserted(3, 5, [], { + startLineNumber: 9, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('A4', () => { + testOnModelLinesInserted(3, 6, [], { + startLineNumber: 10, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('A5', () => { + testOnModelLinesInserted(3, 7, [], { + startLineNumber: 11, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('A6', () => { + testOnModelLinesInserted(3, 8, [], { + startLineNumber: 12, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('A7', () => { + testOnModelLinesInserted(3, 9, [], { + startLineNumber: 13, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('A8', () => { + testOnModelLinesInserted(3, 10, [], { + startLineNumber: 14, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + + test('B1', () => { + testOnModelLinesInserted(5, 5, [], { + startLineNumber: 7, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('B2', () => { + testOnModelLinesInserted(5, 6, [], { + startLineNumber: 8, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('B3', () => { + testOnModelLinesInserted(5, 7, [], { + startLineNumber: 9, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('B4', () => { + testOnModelLinesInserted(5, 8, [], { + startLineNumber: 10, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('B5', () => { + testOnModelLinesInserted(5, 9, [], { + startLineNumber: 11, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('B6', () => { + testOnModelLinesInserted(5, 10, [], { + startLineNumber: 12, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + + test('C1', () => { + testOnModelLinesInserted(6, 6, [], { + startLineNumber: 7, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('C2', () => { + testOnModelLinesInserted(6, 7, [], { + startLineNumber: 8, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('C3', () => { + testOnModelLinesInserted(6, 8, [], { + startLineNumber: 9, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('C4', () => { + testOnModelLinesInserted(6, 9, [], { + startLineNumber: 10, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('C5', () => { + testOnModelLinesInserted(6, 10, [], { + startLineNumber: 11, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + + test('D1', () => { + testOnModelLinesInserted(7, 7, ['old9'], { + startLineNumber: 6, + lines: ['old6', 'new', 'old7', 'old8'], + pinged: [false, false, false, false] + }); + }); + + test('D2', () => { + testOnModelLinesInserted(7, 8, ['old8', 'old9'], { + startLineNumber: 6, + lines: ['old6', 'new', 'new', 'old7'], + pinged: [false, false, false, false] + }); + }); + + test('D3', () => { + testOnModelLinesInserted(7, 9, ['old7', 'old8', 'old9'], { + startLineNumber: 6, + lines: ['old6'], + pinged: [false] + }); + }); + + test('D4', () => { + testOnModelLinesInserted(7, 10, ['old7', 'old8', 'old9'], { + startLineNumber: 6, + lines: ['old6'], + pinged: [false] + }); + }); + + + test('E1', () => { + testOnModelLinesInserted(8, 8, ['old9'], { + startLineNumber: 6, + lines: ['old6', 'old7', 'new', 'old8'], + pinged: [false, false, false, false] + }); + }); + + test('E2', () => { + testOnModelLinesInserted(8, 9, ['old8', 'old9'], { + startLineNumber: 6, + lines: ['old6', 'old7'], + pinged: [false, false] + }); + }); + + test('E3', () => { + testOnModelLinesInserted(8, 10, ['old8', 'old9'], { + startLineNumber: 6, + lines: ['old6', 'old7'], + pinged: [false, false] + }); + }); + + + test('F1', () => { + testOnModelLinesInserted(9, 9, ['old9'], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8'], + pinged: [false, false, false] + }); + }); + + test('F2', () => { + testOnModelLinesInserted(9, 10, ['old9'], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8'], + pinged: [false, false, false] + }); + }); + + + test('G1', () => { + testOnModelLinesInserted(10, 10, [], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + test('G2', () => { + testOnModelLinesInserted(10, 11, [], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + + + test('H1', () => { + testOnModelLinesInserted(11, 13, [], { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); +}); + + +suite('RenderedLinesCollection onTokensChanged', () => { + + function testOnModelTokensChanged(changedFromLineNumber: number, changedToLineNumber: number, expectedPinged: boolean, expectedState: ILinesCollectionState): void { + let col = new RenderedLinesCollection(() => new TestLine('new')); + col._set(6, [ + new TestLine('old6'), + new TestLine('old7'), + new TestLine('old8'), + new TestLine('old9') + ]); + let actualPinged = col.onTokensChanged([{ fromLineNumber: changedFromLineNumber, toLineNumber: changedToLineNumber }]); + assert.deepEqual(actualPinged, expectedPinged); + assertState(col, expectedState); + } + + test('A', () => { + testOnModelTokensChanged(3, 3, false, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + test('B', () => { + testOnModelTokensChanged(3, 5, false, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + test('C', () => { + testOnModelTokensChanged(3, 6, true, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [true, false, false, false] + }); + }); + test('D', () => { + testOnModelTokensChanged(6, 6, true, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [true, false, false, false] + }); + }); + test('E', () => { + testOnModelTokensChanged(5, 10, true, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [true, true, true, true] + }); + }); + test('F', () => { + testOnModelTokensChanged(8, 9, true, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, true, true] + }); + }); + test('G', () => { + testOnModelTokensChanged(8, 11, true, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, true, true] + }); + }); + test('H', () => { + testOnModelTokensChanged(10, 10, false, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); + test('I', () => { + testOnModelTokensChanged(10, 11, false, { + startLineNumber: 6, + lines: ['old6', 'old7', 'old8', 'old9'], + pinged: [false, false, false, false] + }); + }); +}); diff --git a/src/vs/editor/test/common/commands/commandTestUtils.ts b/src/vs/editor/test/common/commands/commandTestUtils.ts new file mode 100644 index 0000000000..f640ccf035 --- /dev/null +++ b/src/vs/editor/test/common/commands/commandTestUtils.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Model } from 'vs/editor/common/model/model'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; + +export function testCommand( + lines: string[], + languageIdentifier: LanguageIdentifier, + selection: Selection, + commandFactory: (selection: Selection) => editorCommon.ICommand, + expectedLines: string[], + expectedSelection: Selection +): void { + let model = Model.createFromString(lines.join('\n'), undefined, languageIdentifier); + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + + cursor.setSelections('tests', [selection]); + + cursor.trigger('tests', editorCommon.Handler.ExecuteCommand, commandFactory(cursor.getSelection())); + + assert.deepEqual(model.getLinesContent(), expectedLines); + + let actualSelection = cursor.getSelection(); + assert.deepEqual(actualSelection.toString(), expectedSelection.toString()); + + }); + model.dispose(); +} + +/** + * Extract edit operations if command `command` were to execute on model `model` + */ +export function getEditOperation(model: editorCommon.IModel, command: editorCommon.ICommand): editorCommon.IIdentifiedSingleEditOperation[] { + var operations: editorCommon.IIdentifiedSingleEditOperation[] = []; + var editOperationBuilder: editorCommon.IEditOperationBuilder = { + addEditOperation: (range: Range, text: string) => { + operations.push({ + identifier: null, + range: range, + text: text, + forceMoveMarkers: false + }); + }, + + addTrackedEditOperation: (range: Range, text: string) => { + operations.push({ + identifier: null, + range: range, + text: text, + forceMoveMarkers: false + }); + }, + + + trackSelection: (selection: Selection) => { + return null; + } + }; + command.getEditOperations(model, editOperationBuilder); + return operations; +} + +/** + * Create single edit operation + */ +export function createSingleEditOp(text: string, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): editorCommon.IIdentifiedSingleEditOperation { + return { + identifier: null, + range: new Range(selectionLineNumber, selectionColumn, positionLineNumber, positionColumn), + text: text, + forceMoveMarkers: false + }; +} + +/** + * Create single edit operation + */ +export function createInsertDeleteSingleEditOp(text: string, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): editorCommon.IIdentifiedSingleEditOperation { + return { + identifier: null, + range: new Range(selectionLineNumber, selectionColumn, positionLineNumber, positionColumn), + text: text, + forceMoveMarkers: true + }; +} diff --git a/src/vs/editor/test/common/commands/shiftCommand.test.ts b/src/vs/editor/test/common/commands/shiftCommand.test.ts new file mode 100644 index 0000000000..c8be349548 --- /dev/null +++ b/src/vs/editor/test/common/commands/shiftCommand.test.ts @@ -0,0 +1,987 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; +import { Selection } from 'vs/editor/common/core/selection'; +import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; +import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { createSingleEditOp, getEditOperation, testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; +import { withEditorModel } from 'vs/editor/test/common/editorTestUtils'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; + +class DocBlockCommentMode extends MockMode { + + private static _id = new LanguageIdentifier('commentMode', 3); + + constructor() { + super(DocBlockCommentMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + brackets: [ + ['(', ')'], + ['{', '}'], + ['[', ']'] + ], + + onEnterRules: [ + { + // e.g. /** | */ + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + afterText: /^\s*\*\/$/, + action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' } + }, + { + // e.g. /** ...| + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + action: { indentAction: IndentAction.None, appendText: ' * ' } + }, + { + // e.g. * ...| + beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/, + action: { indentAction: IndentAction.None, appendText: '* ' } + }, + { + // e.g. */| + beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/, + action: { indentAction: IndentAction.None, removeText: 1 } + }, + { + // e.g. *-----*/| + beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/, + action: { indentAction: IndentAction.None, removeText: 1 } + } + ] + })); + } +} + +function testShiftCommand(lines: string[], languageIdentifier: LanguageIdentifier, useTabStops: boolean, selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, languageIdentifier, selection, (sel) => new ShiftCommand(sel, { + isUnshift: false, + tabSize: 4, + oneIndent: '\t', + useTabStops: useTabStops, + }), expectedLines, expectedSelection); +} + +function testUnshiftCommand(lines: string[], languageIdentifier: LanguageIdentifier, useTabStops: boolean, selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + testCommand(lines, languageIdentifier, selection, (sel) => new ShiftCommand(sel, { + isUnshift: true, + tabSize: 4, + oneIndent: '\t', + useTabStops: useTabStops, + }), expectedLines, expectedSelection); +} + +function withDockBlockCommentMode(callback: (mode: DocBlockCommentMode) => void): void { + let mode = new DocBlockCommentMode(); + callback(mode); + mode.dispose(); +} + +suite('Editor Commands - ShiftCommand', () => { + + // --------- shift + + test('Bug 9503: Shifting without any selection', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 1, 1, 1), + [ + '\tMy First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(1, 1, 1, 2) + ); + }); + + test('shift on single line selection 1', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 3, 1, 1), + [ + '\tMy First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(1, 4, 1, 1) + ); + }); + + test('shift on single line selection 2', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 1, 1, 3), + [ + '\tMy First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(1, 1, 1, 4) + ); + }); + + test('simple shift', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 1, 2, 1), + [ + '\tMy First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(1, 1, 2, 1) + ); + }); + + test('shifting on two separate lines', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 1, 2, 1), + [ + '\tMy First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(1, 1, 2, 1) + ); + + testShiftCommand( + [ + '\tMy First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(2, 1, 3, 1), + [ + '\tMy First Line', + '\t\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(2, 1, 3, 1) + ); + }); + + test('shifting on two lines', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 2, 2, 2), + [ + '\tMy First Line', + '\t\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(1, 3, 2, 2) + ); + }); + + test('shifting on two lines again', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(2, 2, 1, 2), + [ + '\tMy First Line', + '\t\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(2, 2, 1, 3) + ); + }); + + test('shifting at end of file', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(4, 1, 5, 2), + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '\t123' + ], + new Selection(4, 1, 5, 3) + ); + }); + + test('issue #1120 TAB should not indent empty lines in a multi-line selection', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 1, 5, 2), + [ + '\tMy First Line', + '\t\t\tMy Second Line', + '\t\tThird Line', + '', + '\t123' + ], + new Selection(1, 1, 5, 3) + ); + + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(4, 1, 5, 1), + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '\t', + '123' + ], + new Selection(4, 1, 5, 1) + ); + }); + + // --------- unshift + + test('unshift on single line selection 1', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(2, 3, 2, 1), + [ + 'My First Line', + '\t\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(2, 3, 2, 1) + ); + }); + + test('unshift on single line selection 2', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(2, 1, 2, 3), + [ + 'My First Line', + '\t\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(2, 1, 2, 3) + ); + }); + + test('simple unshift', () => { + testUnshiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 1, 2, 1), + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(1, 1, 2, 1) + ); + }); + + test('unshifting on two lines 1', () => { + testUnshiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 2, 2, 2), + [ + 'My First Line', + '\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(1, 2, 2, 2) + ); + }); + + test('unshifting on two lines 2', () => { + testUnshiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(2, 3, 2, 1), + [ + 'My First Line', + '\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(2, 2, 2, 1) + ); + }); + + test('unshifting at the end of the file', () => { + testUnshiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(4, 1, 5, 2), + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(4, 1, 5, 2) + ); + }); + + test('unshift many times + shift', () => { + testUnshiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 1, 5, 4), + [ + 'My First Line', + '\tMy Second Line', + 'Third Line', + '', + '123' + ], + new Selection(1, 1, 5, 4) + ); + + testUnshiftCommand( + [ + 'My First Line', + '\tMy Second Line', + 'Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 1, 5, 4), + [ + 'My First Line', + 'My Second Line', + 'Third Line', + '', + '123' + ], + new Selection(1, 1, 5, 4) + ); + + testShiftCommand( + [ + 'My First Line', + 'My Second Line', + 'Third Line', + '', + '123' + ], + null, + true, + new Selection(1, 1, 5, 4), + [ + '\tMy First Line', + '\tMy Second Line', + '\tThird Line', + '', + '\t123' + ], + new Selection(1, 1, 5, 5) + ); + }); + + test('Bug 9119: Unshift from first column doesn\'t work', () => { + testUnshiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + null, + true, + new Selection(2, 1, 2, 1), + [ + 'My First Line', + '\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(2, 1, 2, 1) + ); + }); + + test('issue #348: indenting around doc block comments', () => { + withDockBlockCommentMode((mode) => { + + testShiftCommand( + [ + '', + '/**', + ' * a doc comment', + ' */', + 'function hello() {}' + ], + mode.getLanguageIdentifier(), + true, + new Selection(1, 1, 5, 20), + [ + '', + '\t/**', + '\t * a doc comment', + '\t */', + '\tfunction hello() {}' + ], + new Selection(1, 1, 5, 21) + ); + + testUnshiftCommand( + [ + '', + '/**', + ' * a doc comment', + ' */', + 'function hello() {}' + ], + mode.getLanguageIdentifier(), + true, + new Selection(1, 1, 5, 20), + [ + '', + '/**', + ' * a doc comment', + ' */', + 'function hello() {}' + ], + new Selection(1, 1, 5, 20) + ); + + testUnshiftCommand( + [ + '\t', + '\t/**', + '\t * a doc comment', + '\t */', + '\tfunction hello() {}' + ], + mode.getLanguageIdentifier(), + true, + new Selection(1, 1, 5, 21), + [ + '', + '/**', + ' * a doc comment', + ' */', + 'function hello() {}' + ], + new Selection(1, 1, 5, 20) + ); + + }); + }); + + test('issue #1609: Wrong indentation of block comments', () => { + withDockBlockCommentMode((mode) => { + testShiftCommand( + [ + '', + '/**', + ' * test', + ' *', + ' * @type {number}', + ' */', + 'var foo = 0;' + ], + mode.getLanguageIdentifier(), + true, + new Selection(1, 1, 7, 13), + [ + '', + '\t/**', + '\t * test', + '\t *', + '\t * @type {number}', + '\t */', + '\tvar foo = 0;' + ], + new Selection(1, 1, 7, 14) + ); + }); + }); + + test('issue #1620: a) Line indent doesn\'t handle leading whitespace properly', () => { + testCommand( + [ + ' Written | Numeric', + ' one | 1', + ' two | 2', + ' three | 3', + ' four | 4', + ' five | 5', + ' six | 6', + ' seven | 7', + ' eight | 8', + ' nine | 9', + ' ten | 10', + ' eleven | 11', + '', + ], + null, + new Selection(1, 1, 13, 1), + (sel) => new ShiftCommand(sel, { + isUnshift: false, + tabSize: 4, + oneIndent: ' ', + useTabStops: false + }), + [ + ' Written | Numeric', + ' one | 1', + ' two | 2', + ' three | 3', + ' four | 4', + ' five | 5', + ' six | 6', + ' seven | 7', + ' eight | 8', + ' nine | 9', + ' ten | 10', + ' eleven | 11', + '', + ], + new Selection(1, 1, 13, 1) + ); + }); + + test('issue #1620: b) Line indent doesn\'t handle leading whitespace properly', () => { + testCommand( + [ + ' Written | Numeric', + ' one | 1', + ' two | 2', + ' three | 3', + ' four | 4', + ' five | 5', + ' six | 6', + ' seven | 7', + ' eight | 8', + ' nine | 9', + ' ten | 10', + ' eleven | 11', + '', + ], + null, + new Selection(1, 1, 13, 1), + (sel) => new ShiftCommand(sel, { + isUnshift: true, + tabSize: 4, + oneIndent: ' ', + useTabStops: false + }), + [ + ' Written | Numeric', + ' one | 1', + ' two | 2', + ' three | 3', + ' four | 4', + ' five | 5', + ' six | 6', + ' seven | 7', + ' eight | 8', + ' nine | 9', + ' ten | 10', + ' eleven | 11', + '', + ], + new Selection(1, 1, 13, 1) + ); + }); + + test('issue #1620: c) Line indent doesn\'t handle leading whitespace properly', () => { + testCommand( + [ + ' Written | Numeric', + ' one | 1', + ' two | 2', + ' three | 3', + ' four | 4', + ' five | 5', + ' six | 6', + ' seven | 7', + ' eight | 8', + ' nine | 9', + ' ten | 10', + ' eleven | 11', + '', + ], + null, + new Selection(1, 1, 13, 1), + (sel) => new ShiftCommand(sel, { + isUnshift: true, + tabSize: 4, + oneIndent: '\t', + useTabStops: false + }), + [ + ' Written | Numeric', + ' one | 1', + ' two | 2', + ' three | 3', + ' four | 4', + ' five | 5', + ' six | 6', + ' seven | 7', + ' eight | 8', + ' nine | 9', + ' ten | 10', + ' eleven | 11', + '', + ], + new Selection(1, 1, 13, 1) + ); + }); + + test('issue #1620: d) Line indent doesn\'t handle leading whitespace properly', () => { + testCommand( + [ + '\t Written | Numeric', + '\t one | 1', + '\t two | 2', + '\t three | 3', + '\t four | 4', + '\t five | 5', + '\t six | 6', + '\t seven | 7', + '\t eight | 8', + '\t nine | 9', + '\t ten | 10', + '\t eleven | 11', + '', + ], + null, + new Selection(1, 1, 13, 1), + (sel) => new ShiftCommand(sel, { + isUnshift: true, + tabSize: 4, + oneIndent: ' ', + useTabStops: false + }), + [ + ' Written | Numeric', + ' one | 1', + ' two | 2', + ' three | 3', + ' four | 4', + ' five | 5', + ' six | 6', + ' seven | 7', + ' eight | 8', + ' nine | 9', + ' ten | 10', + ' eleven | 11', + '', + ], + new Selection(1, 1, 13, 1) + ); + }); + + test('issue Microsoft/monaco-editor#443: Indentation of a single row deletes selected text in some cases', () => { + testCommand( + [ + 'Hello world!', + 'another line' + ], + null, + new Selection(1, 1, 1, 13), + (sel) => new ShiftCommand(sel, { + isUnshift: false, + tabSize: 4, + oneIndent: '\t', + useTabStops: true + }), + [ + '\tHello world!', + 'another line' + ], + new Selection(1, 1, 1, 14) + ); + }); + + test('bug #16815:Shift+Tab doesn\'t go back to tabstop', () => { + + var repeatStr = (str: string, cnt: number): string => { + var r = ''; + for (var i = 0; i < cnt; i++) { + r += str; + } + return r; + }; + + var testOutdent = (tabSize: number, oneIndent: string, lineText: string, expectedIndents: number) => { + var expectedIndent = repeatStr(oneIndent, expectedIndents); + if (lineText.length > 0) { + _assertUnshiftCommand(tabSize, oneIndent, [lineText + 'aaa'], [createSingleEditOp(expectedIndent, 1, 1, 1, lineText.length + 1)]); + } else { + _assertUnshiftCommand(tabSize, oneIndent, [lineText + 'aaa'], []); + } + }; + + var testIndent = (tabSize: number, oneIndent: string, lineText: string, expectedIndents: number) => { + var expectedIndent = repeatStr(oneIndent, expectedIndents); + _assertShiftCommand(tabSize, oneIndent, [lineText + 'aaa'], [createSingleEditOp(expectedIndent, 1, 1, 1, lineText.length + 1)]); + }; + + var testIndentation = (tabSize: number, lineText: string, expectedOnOutdent: number, expectedOnIndent: number) => { + var spaceIndent = ''; + for (var i = 0; i < tabSize; i++) { + spaceIndent += ' '; + } + + testOutdent(tabSize, spaceIndent, lineText, expectedOnOutdent); + testOutdent(tabSize, '\t', lineText, expectedOnOutdent); + + testIndent(tabSize, spaceIndent, lineText, expectedOnIndent); + testIndent(tabSize, '\t', lineText, expectedOnIndent); + }; + + // insertSpaces: true + // 0 => 0 + testIndentation(4, '', 0, 1); + + // 1 => 0 + testIndentation(4, '\t', 0, 2); + testIndentation(4, ' ', 0, 1); + testIndentation(4, ' \t', 0, 2); + testIndentation(4, ' ', 0, 1); + testIndentation(4, ' \t', 0, 2); + testIndentation(4, ' ', 0, 1); + testIndentation(4, ' \t', 0, 2); + testIndentation(4, ' ', 0, 2); + + // 2 => 1 + testIndentation(4, '\t\t', 1, 3); + testIndentation(4, '\t ', 1, 2); + testIndentation(4, '\t \t', 1, 3); + testIndentation(4, '\t ', 1, 2); + testIndentation(4, '\t \t', 1, 3); + testIndentation(4, '\t ', 1, 2); + testIndentation(4, '\t \t', 1, 3); + testIndentation(4, '\t ', 1, 3); + testIndentation(4, ' \t\t', 1, 3); + testIndentation(4, ' \t ', 1, 2); + testIndentation(4, ' \t \t', 1, 3); + testIndentation(4, ' \t ', 1, 2); + testIndentation(4, ' \t \t', 1, 3); + testIndentation(4, ' \t ', 1, 2); + testIndentation(4, ' \t \t', 1, 3); + testIndentation(4, ' \t ', 1, 3); + testIndentation(4, ' \t\t', 1, 3); + testIndentation(4, ' \t ', 1, 2); + testIndentation(4, ' \t \t', 1, 3); + testIndentation(4, ' \t ', 1, 2); + testIndentation(4, ' \t \t', 1, 3); + testIndentation(4, ' \t ', 1, 2); + testIndentation(4, ' \t \t', 1, 3); + testIndentation(4, ' \t ', 1, 3); + testIndentation(4, ' \t\t', 1, 3); + testIndentation(4, ' \t ', 1, 2); + testIndentation(4, ' \t \t', 1, 3); + testIndentation(4, ' \t ', 1, 2); + testIndentation(4, ' \t \t', 1, 3); + testIndentation(4, ' \t ', 1, 2); + testIndentation(4, ' \t \t', 1, 3); + testIndentation(4, ' \t ', 1, 3); + testIndentation(4, ' \t', 1, 3); + testIndentation(4, ' ', 1, 2); + testIndentation(4, ' \t', 1, 3); + testIndentation(4, ' ', 1, 2); + testIndentation(4, ' \t', 1, 3); + testIndentation(4, ' ', 1, 2); + testIndentation(4, ' \t', 1, 3); + testIndentation(4, ' ', 1, 3); + + // 3 => 2 + testIndentation(4, ' ', 2, 3); + + function _assertUnshiftCommand(tabSize: number, oneIndent: string, text: string[], expected: IIdentifiedSingleEditOperation[]): void { + return withEditorModel(text, (model) => { + var op = new ShiftCommand(new Selection(1, 1, text.length + 1, 1), { + isUnshift: true, + tabSize: tabSize, + oneIndent: oneIndent, + useTabStops: true + }); + var actual = getEditOperation(model, op); + assert.deepEqual(actual, expected); + }); + } + + function _assertShiftCommand(tabSize: number, oneIndent: string, text: string[], expected: IIdentifiedSingleEditOperation[]): void { + return withEditorModel(text, (model) => { + var op = new ShiftCommand(new Selection(1, 1, text.length + 1, 1), { + isUnshift: false, + tabSize: tabSize, + oneIndent: oneIndent, + useTabStops: true + }); + var actual = getEditOperation(model, op); + assert.deepEqual(actual, expected); + }); + } + }); + +}); diff --git a/src/vs/editor/test/common/commands/sideEditing.test.ts b/src/vs/editor/test/common/commands/sideEditing.test.ts new file mode 100644 index 0000000000..cdc74995e9 --- /dev/null +++ b/src/vs/editor/test/common/commands/sideEditing.test.ts @@ -0,0 +1,208 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; +import { ILineEdit, ModelLine, LineMarker, MarkersTracker } from 'vs/editor/common/model/modelLine'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; + +const NO_TAB_SIZE = 0; + +function testCommand(lines: string[], selections: Selection[], edits: IIdentifiedSingleEditOperation[], expectedLines: string[], expectedSelections: Selection[]): void { + withMockCodeEditor(lines, {}, (editor, cursor) => { + const model = editor.getModel(); + + cursor.setSelections('tests', selections); + + model.applyEdits(edits); + + assert.deepEqual(model.getLinesContent(), expectedLines); + + let actualSelections = cursor.getSelections(); + assert.deepEqual(actualSelections.map(s => s.toString()), expectedSelections.map(s => s.toString())); + + }); +} + +function testLineEditMarker(text: string, column: number, stickToPreviousCharacter: boolean, edit: ILineEdit, expectedColumn: number): void { + var line = new ModelLine(text, NO_TAB_SIZE); + line.addMarker(new LineMarker('1', 0, new Position(0, column), stickToPreviousCharacter)); + + line.applyEdits(new MarkersTracker(), [edit], NO_TAB_SIZE); + + assert.equal(line.getMarkers()[0].position.column, expectedColumn); +} + +suite('Editor Side Editing - collapsed selection', () => { + + test('replace at selection', () => { + testCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth' + ], + [new Selection(1, 1, 1, 1)], + [ + EditOperation.replace(new Selection(1, 1, 1, 1), 'something ') + ], + [ + 'something first', + 'second line', + 'third line', + 'fourth' + ], + [new Selection(1, 1, 1, 11)] + ); + }); + + test('replace at selection 2', () => { + testCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth' + ], + [new Selection(1, 1, 1, 6)], + [ + EditOperation.replace(new Selection(1, 1, 1, 6), 'something') + ], + [ + 'something', + 'second line', + 'third line', + 'fourth' + ], + [new Selection(1, 1, 1, 10)] + ); + }); + + test('ModelLine.applyEdits uses `isReplace`', () => { + testLineEditMarker('something', 1, true, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: false }, 1); + testLineEditMarker('something', 1, true, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: true }, 4); + + testLineEditMarker('something', 1, false, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: false }, 4); + testLineEditMarker('something', 1, false, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: true }, 4); + }); + + test('insert at selection', () => { + testCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth' + ], + [new Selection(1, 1, 1, 1)], + [ + EditOperation.insert(new Position(1, 1), 'something ') + ], + [ + 'something first', + 'second line', + 'third line', + 'fourth' + ], + [new Selection(1, 11, 1, 11)] + ); + }); + + test('insert at selection sitting on max column', () => { + testCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth' + ], + [new Selection(1, 6, 1, 6)], + [ + EditOperation.insert(new Position(1, 6), ' something\nnew ') + ], + [ + 'first something', + 'new ', + 'second line', + 'third line', + 'fourth' + ], + [new Selection(2, 5, 2, 5)] + ); + }); + + test('issue #3994: replace on top of selection', () => { + testCommand( + [ + '$obj = New-Object "system.col"' + ], + [new Selection(1, 30, 1, 30)], + [ + EditOperation.replaceMove(new Range(1, 19, 1, 31), '"System.Collections"') + ], + [ + '$obj = New-Object "System.Collections"' + ], + [new Selection(1, 39, 1, 39)] + ); + }); + + test('issue #15267: Suggestion that adds a line - cursor goes to the wrong line ', () => { + testCommand( + [ + 'package main', + '', + 'import (', + ' "fmt"', + ')', + '', + 'func main(', + ' fmt.Println(strings.Con)', + '}' + ], + [new Selection(8, 25, 8, 25)], + [ + EditOperation.replaceMove(new Range(5, 1, 5, 1), '\t\"strings\"\n') + ], + [ + 'package main', + '', + 'import (', + ' "fmt"', + ' "strings"', + ')', + '', + 'func main(', + ' fmt.Println(strings.Con)', + '}' + ], + [new Selection(9, 25, 9, 25)] + ); + }); + + test('issue #15236: Selections broke after deleting text using vscode.TextEditor.edit ', () => { + testCommand( + [ + 'foofoofoo, foofoofoo, bar' + ], + [new Selection(1, 1, 1, 10), new Selection(1, 12, 1, 21)], + [ + EditOperation.replace(new Range(1, 1, 1, 10), ''), + EditOperation.replace(new Range(1, 12, 1, 21), ''), + ], + [ + ', , bar' + ], + [new Selection(1, 1, 1, 1), new Selection(1, 3, 1, 3)] + ); + }); + +}); \ No newline at end of file diff --git a/src/vs/editor/test/common/commands/trimTrailingWhitespaceCommand.test.ts b/src/vs/editor/test/common/commands/trimTrailingWhitespaceCommand.test.ts new file mode 100644 index 0000000000..c1d2829abd --- /dev/null +++ b/src/vs/editor/test/common/commands/trimTrailingWhitespaceCommand.test.ts @@ -0,0 +1,82 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { TrimTrailingWhitespaceCommand, trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; +import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; +import { createInsertDeleteSingleEditOp, createSingleEditOp, getEditOperation } from 'vs/editor/test/common/commands/commandTestUtils'; +import { withEditorModel } from 'vs/editor/test/common/editorTestUtils'; + +function assertTrimTrailingWhitespaceCommand(text: string[], expected: IIdentifiedSingleEditOperation[]): void { + return withEditorModel(text, (model) => { + var op = new TrimTrailingWhitespaceCommand(new Selection(1, 1, 1, 1)); + var actual = getEditOperation(model, op); + assert.deepEqual(actual, expected); + }); +} + +function assertTrimTrailingWhitespace(text: string[], cursors: Position[], expected: IIdentifiedSingleEditOperation[]): void { + return withEditorModel(text, (model) => { + var actual = trimTrailingWhitespace(model, cursors); + assert.deepEqual(actual, expected); + }); +} + +suite('Editor Commands - Trim Trailing Whitespace Command', () => { + + test('remove trailing whitespace', function () { + assertTrimTrailingWhitespaceCommand([''], []); + assertTrimTrailingWhitespaceCommand(['text'], []); + assertTrimTrailingWhitespaceCommand(['text '], [createSingleEditOp(null, 1, 5, 1, 8)]); + assertTrimTrailingWhitespaceCommand(['text\t '], [createSingleEditOp(null, 1, 5, 1, 9)]); + assertTrimTrailingWhitespaceCommand(['\t '], [createSingleEditOp(null, 1, 1, 1, 5)]); + assertTrimTrailingWhitespaceCommand(['text\t'], [createSingleEditOp(null, 1, 5, 1, 6)]); + assertTrimTrailingWhitespaceCommand([ + 'some text\t', + 'some more text', + '\t ', + 'even more text ', + 'and some mixed\t \t' + ], [ + createSingleEditOp(null, 1, 10, 1, 11), + createSingleEditOp(null, 3, 1, 3, 4), + createSingleEditOp(null, 4, 15, 4, 17), + createSingleEditOp(null, 5, 15, 5, 20) + ]); + + + assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 2), new Position(1, 3)], [createInsertDeleteSingleEditOp(null, 1, 5, 1, 8)]); + assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 5)], [createInsertDeleteSingleEditOp(null, 1, 5, 1, 8)]); + assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 5), new Position(1, 6)], [createInsertDeleteSingleEditOp(null, 1, 6, 1, 8)]); + assertTrimTrailingWhitespace([ + 'some text\t', + 'some more text', + '\t ', + 'even more text ', + 'and some mixed\t \t' + ], [], [ + createInsertDeleteSingleEditOp(null, 1, 10, 1, 11), + createInsertDeleteSingleEditOp(null, 3, 1, 3, 4), + createInsertDeleteSingleEditOp(null, 4, 15, 4, 17), + createInsertDeleteSingleEditOp(null, 5, 15, 5, 20) + ]); + assertTrimTrailingWhitespace([ + 'some text\t', + 'some more text', + '\t ', + 'even more text ', + 'and some mixed\t \t' + ], [new Position(1, 11), new Position(3, 2), new Position(5, 1), new Position(4, 1), new Position(5, 10)], [ + createInsertDeleteSingleEditOp(null, 3, 2, 3, 4), + createInsertDeleteSingleEditOp(null, 4, 15, 4, 17), + createInsertDeleteSingleEditOp(null, 5, 15, 5, 20) + ]); + }); + +}); + diff --git a/src/vs/editor/test/common/commentMode.ts b/src/vs/editor/test/common/commentMode.ts new file mode 100644 index 0000000000..041308933f --- /dev/null +++ b/src/vs/editor/test/common/commentMode.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { CommentRule } from 'vs/editor/common/modes/languageConfiguration'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; + +export class CommentMode extends MockMode { + private static _id = new LanguageIdentifier('commentMode', 3); + + constructor(commentsConfig: CommentRule) { + super(CommentMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + comments: commentsConfig + })); + } +} diff --git a/src/vs/editor/test/common/config/commonEditorConfig.test.ts b/src/vs/editor/test/common/config/commonEditorConfig.test.ts new file mode 100644 index 0000000000..642e5dd254 --- /dev/null +++ b/src/vs/editor/test/common/config/commonEditorConfig.test.ts @@ -0,0 +1,182 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { EditorZoom } from 'vs/editor/common/config/editorZoom'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import { IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; +import { AccessibilitySupport } from 'vs/base/common/platform'; + +suite('Common Editor Config', () => { + test('Zoom Level', () => { + + //Zoom levels are defined to go between -9, 9 inclusive + var zoom = EditorZoom; + + zoom.setZoomLevel(0); + assert.equal(zoom.getZoomLevel(), 0); + + zoom.setZoomLevel(-0); + assert.equal(zoom.getZoomLevel(), 0); + + zoom.setZoomLevel(5); + assert.equal(zoom.getZoomLevel(), 5); + + zoom.setZoomLevel(-1); + assert.equal(zoom.getZoomLevel(), -1); + + zoom.setZoomLevel(9); + assert.equal(zoom.getZoomLevel(), 9); + + zoom.setZoomLevel(-9); + assert.equal(zoom.getZoomLevel(), -9); + + zoom.setZoomLevel(10); + assert.equal(zoom.getZoomLevel(), 9); + + zoom.setZoomLevel(-10); + assert.equal(zoom.getZoomLevel(), -9); + + zoom.setZoomLevel(9.1); + assert.equal(zoom.getZoomLevel(), 9); + + zoom.setZoomLevel(-9.1); + assert.equal(zoom.getZoomLevel(), -9); + + zoom.setZoomLevel(Infinity); + assert.equal(zoom.getZoomLevel(), 9); + + zoom.setZoomLevel(Number.NEGATIVE_INFINITY); + assert.equal(zoom.getZoomLevel(), -9); + }); + + class TestWrappingConfiguration extends TestConfiguration { + protected _getEnvConfiguration(): IEnvConfiguration { + return { + extraEditorClassName: '', + outerWidth: 1000, + outerHeight: 100, + emptySelectionClipboard: true, + pixelRatio: 1, + zoomLevel: 0, + accessibilitySupport: AccessibilitySupport.Unknown + }; + } + } + + function assertWrapping(config: TestConfiguration, isViewportWrapping: boolean, wrappingColumn: number): void { + assert.equal(config.editor.wrappingInfo.isViewportWrapping, isViewportWrapping); + assert.equal(config.editor.wrappingInfo.wrappingColumn, wrappingColumn); + } + + test('wordWrap default', () => { + let config = new TestWrappingConfiguration({}); + assertWrapping(config, false, -1); + }); + + test('wordWrap compat false', () => { + let config = new TestWrappingConfiguration({ + wordWrap: false + }); + assertWrapping(config, false, -1); + }); + + test('wordWrap compat true', () => { + let config = new TestWrappingConfiguration({ + wordWrap: true + }); + // {{SQL CARBON EDIT}} + assertWrapping(config, true, 89); + }); + + test('wordWrap on', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'on' + }); + // {{SQL CARBON EDIT}} + assertWrapping(config, true, 89); + }); + + test('wordWrap on without minimap', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'on', + minimap: { + enabled: false + } + }); + assertWrapping(config, true, 89); + }); + + test('wordWrap on does not use wordWrapColumn', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'on', + wordWrapColumn: 10 + }); + // {{SQL CARBON EDIT}} + assertWrapping(config, true, 89); + }); + + test('wordWrap off', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'off' + }); + assertWrapping(config, false, -1); + }); + + test('wordWrap off does not use wordWrapColumn', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'off', + wordWrapColumn: 10 + }); + assertWrapping(config, false, -1); + }); + + test('wordWrap wordWrapColumn uses default wordWrapColumn', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'wordWrapColumn' + }); + assertWrapping(config, false, 80); + }); + + test('wordWrap wordWrapColumn uses wordWrapColumn', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'wordWrapColumn', + wordWrapColumn: 100 + }); + assertWrapping(config, false, 100); + }); + + test('wordWrap wordWrapColumn validates wordWrapColumn', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'wordWrapColumn', + wordWrapColumn: -1 + }); + assertWrapping(config, false, 1); + }); + + test('wordWrap bounded uses default wordWrapColumn', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'bounded' + }); + assertWrapping(config, true, 80); + }); + + test('wordWrap bounded uses wordWrapColumn', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'bounded', + wordWrapColumn: 40 + }); + assertWrapping(config, true, 40); + }); + + test('wordWrap bounded validates wordWrapColumn', () => { + let config = new TestWrappingConfiguration({ + wordWrap: 'bounded', + wordWrapColumn: -1 + }); + assertWrapping(config, true, 1); + }); +}); diff --git a/src/vs/editor/test/common/controller/cursor.test.ts b/src/vs/editor/test/common/controller/cursor.test.ts new file mode 100644 index 0000000000..342b4bb2b8 --- /dev/null +++ b/src/vs/editor/test/common/controller/cursor.test.ts @@ -0,0 +1,3702 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { + EndOfLinePreference, Handler, + DefaultEndOfLine, ITextModelCreationOptions, ICommand, + ITokenizedModel, IEditOperationBuilder, ICursorStateComputerData, EndOfLineSequence +} from 'vs/editor/common/editorCommon'; +import { Model } from 'vs/editor/common/model/model'; +import { IndentAction, IndentationRule } from 'vs/editor/common/modes/languageConfiguration'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { CoreNavigationCommands, CoreEditingCommands } from 'vs/editor/common/controller/coreCommands'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; +let H = Handler; + +// --------- utils + +function cursorCommand(cursor: Cursor, command: string, extraData?: any, overwriteSource?: string) { + cursor.trigger(overwriteSource || 'tests', command, extraData); +} + +function cursorCommandAndTokenize(model: Model, cursor: Cursor, command: string, extraData?: any, overwriteSource?: string) { + cursor.trigger(overwriteSource || 'tests', command, extraData); + model.forceTokenization(model.getLineCount()); +} + +function moveTo(cursor: Cursor, lineNumber: number, column: number, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(cursor, { + position: new Position(lineNumber, column) + }); + } else { + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { + position: new Position(lineNumber, column) + }); + } +} + +function moveLeft(cursor: Cursor, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.CursorLeftSelect.runCoreEditorCommand(cursor, {}); + } else { + CoreNavigationCommands.CursorLeft.runCoreEditorCommand(cursor, {}); + } +} + +function moveRight(cursor: Cursor, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.CursorRightSelect.runCoreEditorCommand(cursor, {}); + } else { + CoreNavigationCommands.CursorRight.runCoreEditorCommand(cursor, {}); + } +} + +function moveDown(cursor: Cursor, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.CursorDownSelect.runCoreEditorCommand(cursor, {}); + } else { + CoreNavigationCommands.CursorDown.runCoreEditorCommand(cursor, {}); + } +} + +function moveUp(cursor: Cursor, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.CursorUpSelect.runCoreEditorCommand(cursor, {}); + } else { + CoreNavigationCommands.CursorUp.runCoreEditorCommand(cursor, {}); + } +} + +function moveToBeginningOfLine(cursor: Cursor, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.CursorHomeSelect.runCoreEditorCommand(cursor, {}); + } else { + CoreNavigationCommands.CursorHome.runCoreEditorCommand(cursor, {}); + } +} + +function moveToEndOfLine(cursor: Cursor, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(cursor, {}); + } else { + CoreNavigationCommands.CursorEnd.runCoreEditorCommand(cursor, {}); + } +} + +function moveToBeginningOfBuffer(cursor: Cursor, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.CursorTopSelect.runCoreEditorCommand(cursor, {}); + } else { + CoreNavigationCommands.CursorTop.runCoreEditorCommand(cursor, {}); + } +} + +function moveToEndOfBuffer(cursor: Cursor, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.CursorBottomSelect.runCoreEditorCommand(cursor, {}); + } else { + CoreNavigationCommands.CursorBottom.runCoreEditorCommand(cursor, {}); + } +} + +function assertCursor(cursor: Cursor, what: Position | Selection | Selection[]): void { + let selections: Selection[]; + if (what instanceof Position) { + selections = [new Selection(what.lineNumber, what.column, what.lineNumber, what.column)]; + } else if (what instanceof Selection) { + selections = [what]; + } else { + selections = what; + } + let actual = cursor.getSelections().map(s => s.toString()); + let expected = selections.map(s => s.toString()); + + assert.deepEqual(actual, expected); +} + +suite('Editor Controller - Cursor', () => { + const LINE1 = ' \tMy First Line\t '; + const LINE2 = '\tMy Second Line'; + const LINE3 = ' Third Line🐶'; + const LINE4 = ''; + const LINE5 = '1'; + + let thisModel: Model; + let thisConfiguration: TestConfiguration; + let thisViewModel: ViewModel; + let thisCursor: Cursor; + + setup(() => { + let text = + LINE1 + '\r\n' + + LINE2 + '\n' + + LINE3 + '\n' + + LINE4 + '\r\n' + + LINE5; + + thisModel = Model.createFromString(text); + thisConfiguration = new TestConfiguration(null); + thisViewModel = new ViewModel(0, thisConfiguration, thisModel, null); + + thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel); + }); + + teardown(() => { + thisCursor.dispose(); + thisViewModel.dispose(); + thisModel.dispose(); + thisConfiguration.dispose(); + }); + + test('cursor initialized', () => { + assertCursor(thisCursor, new Position(1, 1)); + }); + + // --------- absolute move + + test('no move', () => { + moveTo(thisCursor, 1, 1); + assertCursor(thisCursor, new Position(1, 1)); + }); + + test('move', () => { + moveTo(thisCursor, 1, 2); + assertCursor(thisCursor, new Position(1, 2)); + }); + + test('move in selection mode', () => { + moveTo(thisCursor, 1, 2, true); + assertCursor(thisCursor, new Selection(1, 1, 1, 2)); + }); + + test('move beyond line end', () => { + moveTo(thisCursor, 1, 25); + assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + }); + + test('move empty line', () => { + moveTo(thisCursor, 4, 20); + assertCursor(thisCursor, new Position(4, 1)); + }); + + test('move one char line', () => { + moveTo(thisCursor, 5, 20); + assertCursor(thisCursor, new Position(5, 2)); + }); + + test('selection down', () => { + moveTo(thisCursor, 2, 1, true); + assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + }); + + test('move and then select', () => { + moveTo(thisCursor, 2, 3); + assertCursor(thisCursor, new Position(2, 3)); + + moveTo(thisCursor, 2, 15, true); + assertCursor(thisCursor, new Selection(2, 3, 2, 15)); + + moveTo(thisCursor, 1, 2, true); + assertCursor(thisCursor, new Selection(2, 3, 1, 2)); + }); + + // --------- move left + + test('move left on top left position', () => { + moveLeft(thisCursor); + assertCursor(thisCursor, new Position(1, 1)); + }); + + test('move left', () => { + moveTo(thisCursor, 1, 3); + assertCursor(thisCursor, new Position(1, 3)); + moveLeft(thisCursor); + assertCursor(thisCursor, new Position(1, 2)); + }); + + test('move left with surrogate pair', () => { + moveTo(thisCursor, 3, 17); + assertCursor(thisCursor, new Position(3, 17)); + moveLeft(thisCursor); + assertCursor(thisCursor, new Position(3, 15)); + }); + + test('move left goes to previous row', () => { + moveTo(thisCursor, 2, 1); + assertCursor(thisCursor, new Position(2, 1)); + moveLeft(thisCursor); + assertCursor(thisCursor, new Position(1, 21)); + }); + + test('move left selection', () => { + moveTo(thisCursor, 2, 1); + assertCursor(thisCursor, new Position(2, 1)); + moveLeft(thisCursor, true); + assertCursor(thisCursor, new Selection(2, 1, 1, 21)); + }); + + // --------- move right + + test('move right on bottom right position', () => { + moveTo(thisCursor, 5, 2); + assertCursor(thisCursor, new Position(5, 2)); + moveRight(thisCursor); + assertCursor(thisCursor, new Position(5, 2)); + }); + + test('move right', () => { + moveTo(thisCursor, 1, 3); + assertCursor(thisCursor, new Position(1, 3)); + moveRight(thisCursor); + assertCursor(thisCursor, new Position(1, 4)); + }); + + test('move right with surrogate pair', () => { + moveTo(thisCursor, 3, 15); + assertCursor(thisCursor, new Position(3, 15)); + moveRight(thisCursor); + assertCursor(thisCursor, new Position(3, 17)); + }); + + test('move right goes to next row', () => { + moveTo(thisCursor, 1, 21); + assertCursor(thisCursor, new Position(1, 21)); + moveRight(thisCursor); + assertCursor(thisCursor, new Position(2, 1)); + }); + + test('move right selection', () => { + moveTo(thisCursor, 1, 21); + assertCursor(thisCursor, new Position(1, 21)); + moveRight(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 21, 2, 1)); + }); + + // --------- move down + + test('move down', () => { + moveDown(thisCursor); + assertCursor(thisCursor, new Position(2, 1)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(3, 1)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(4, 1)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(5, 1)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(5, 2)); + }); + + test('move down with selection', () => { + moveDown(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + moveDown(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 1, 3, 1)); + moveDown(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 1, 4, 1)); + moveDown(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 1, 5, 1)); + moveDown(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 1, 5, 2)); + }); + + test('move down with tabs', () => { + moveTo(thisCursor, 1, 5); + assertCursor(thisCursor, new Position(1, 5)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(2, 2)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(3, 5)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(4, 1)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(5, 2)); + }); + + // --------- move up + + test('move up', () => { + moveTo(thisCursor, 3, 5); + assertCursor(thisCursor, new Position(3, 5)); + + moveUp(thisCursor); + assertCursor(thisCursor, new Position(2, 2)); + + moveUp(thisCursor); + assertCursor(thisCursor, new Position(1, 5)); + }); + + test('move up with selection', () => { + moveTo(thisCursor, 3, 5); + assertCursor(thisCursor, new Position(3, 5)); + + moveUp(thisCursor, true); + assertCursor(thisCursor, new Selection(3, 5, 2, 2)); + + moveUp(thisCursor, true); + assertCursor(thisCursor, new Selection(3, 5, 1, 5)); + }); + + test('move up and down with tabs', () => { + moveTo(thisCursor, 1, 5); + assertCursor(thisCursor, new Position(1, 5)); + moveDown(thisCursor); + moveDown(thisCursor); + moveDown(thisCursor); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(5, 2)); + moveUp(thisCursor); + assertCursor(thisCursor, new Position(4, 1)); + moveUp(thisCursor); + assertCursor(thisCursor, new Position(3, 5)); + moveUp(thisCursor); + assertCursor(thisCursor, new Position(2, 2)); + moveUp(thisCursor); + assertCursor(thisCursor, new Position(1, 5)); + }); + + test('move up and down with end of lines starting from a long one', () => { + moveToEndOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + moveToEndOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(2, LINE2.length + 1)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(3, LINE3.length + 1)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(4, LINE4.length + 1)); + moveDown(thisCursor); + assertCursor(thisCursor, new Position(5, LINE5.length + 1)); + moveUp(thisCursor); + moveUp(thisCursor); + moveUp(thisCursor); + moveUp(thisCursor); + assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + }); + + // --------- move to beginning of line + + test('move to beginning of line', () => { + moveToBeginningOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, 6)); + moveToBeginningOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, 1)); + }); + + test('move to beginning of line from within line', () => { + moveTo(thisCursor, 1, 8); + moveToBeginningOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, 6)); + moveToBeginningOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, 1)); + }); + + test('move to beginning of line from whitespace at beginning of line', () => { + moveTo(thisCursor, 1, 2); + moveToBeginningOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, 6)); + moveToBeginningOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, 1)); + }); + + test('move to beginning of line from within line selection', () => { + moveTo(thisCursor, 1, 8); + moveToBeginningOfLine(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 8, 1, 6)); + moveToBeginningOfLine(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 8, 1, 1)); + }); + + test('move to beginning of line with selection multiline forward', () => { + moveTo(thisCursor, 1, 8); + moveTo(thisCursor, 3, 9, true); + moveToBeginningOfLine(thisCursor, false); + assertCursor(thisCursor, new Selection(3, 5, 3, 5)); + }); + + test('move to beginning of line with selection multiline backward', () => { + moveTo(thisCursor, 3, 9); + moveTo(thisCursor, 1, 8, true); + moveToBeginningOfLine(thisCursor, false); + assertCursor(thisCursor, new Selection(1, 6, 1, 6)); + }); + + test('move to beginning of line with selection single line forward', () => { + moveTo(thisCursor, 3, 2); + moveTo(thisCursor, 3, 9, true); + moveToBeginningOfLine(thisCursor, false); + assertCursor(thisCursor, new Selection(3, 5, 3, 5)); + }); + + test('move to beginning of line with selection single line backward', () => { + moveTo(thisCursor, 3, 9); + moveTo(thisCursor, 3, 2, true); + moveToBeginningOfLine(thisCursor, false); + assertCursor(thisCursor, new Selection(3, 5, 3, 5)); + }); + + test('issue #15401: "End" key is behaving weird when text is selected part 1', () => { + moveTo(thisCursor, 1, 8); + moveTo(thisCursor, 3, 9, true); + moveToBeginningOfLine(thisCursor, false); + assertCursor(thisCursor, new Selection(3, 5, 3, 5)); + }); + + test('issue #17011: Shift+home/end now go to the end of the selection start\'s line, not the selection\'s end', () => { + moveTo(thisCursor, 1, 8); + moveTo(thisCursor, 3, 9, true); + moveToBeginningOfLine(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 8, 3, 5)); + }); + + // --------- move to end of line + + test('move to end of line', () => { + moveToEndOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + moveToEndOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + }); + + test('move to end of line from within line', () => { + moveTo(thisCursor, 1, 6); + moveToEndOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + moveToEndOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + }); + + test('move to end of line from whitespace at end of line', () => { + moveTo(thisCursor, 1, 20); + moveToEndOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + moveToEndOfLine(thisCursor); + assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + }); + + test('move to end of line from within line selection', () => { + moveTo(thisCursor, 1, 6); + moveToEndOfLine(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 6, 1, LINE1.length + 1)); + moveToEndOfLine(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 6, 1, LINE1.length + 1)); + }); + + test('move to end of line with selection multiline forward', () => { + moveTo(thisCursor, 1, 1); + moveTo(thisCursor, 3, 9, true); + moveToEndOfLine(thisCursor, false); + assertCursor(thisCursor, new Selection(3, 17, 3, 17)); + }); + + test('move to end of line with selection multiline backward', () => { + moveTo(thisCursor, 3, 9); + moveTo(thisCursor, 1, 1, true); + moveToEndOfLine(thisCursor, false); + assertCursor(thisCursor, new Selection(1, 21, 1, 21)); + }); + + test('move to end of line with selection single line forward', () => { + moveTo(thisCursor, 3, 1); + moveTo(thisCursor, 3, 9, true); + moveToEndOfLine(thisCursor, false); + assertCursor(thisCursor, new Selection(3, 17, 3, 17)); + }); + + test('move to end of line with selection single line backward', () => { + moveTo(thisCursor, 3, 9); + moveTo(thisCursor, 3, 1, true); + moveToEndOfLine(thisCursor, false); + assertCursor(thisCursor, new Selection(3, 17, 3, 17)); + }); + + test('issue #15401: "End" key is behaving weird when text is selected part 2', () => { + moveTo(thisCursor, 1, 1); + moveTo(thisCursor, 3, 9, true); + moveToEndOfLine(thisCursor, false); + assertCursor(thisCursor, new Selection(3, 17, 3, 17)); + }); + + // --------- move to beginning of buffer + + test('move to beginning of buffer', () => { + moveToBeginningOfBuffer(thisCursor); + assertCursor(thisCursor, new Position(1, 1)); + }); + + test('move to beginning of buffer from within first line', () => { + moveTo(thisCursor, 1, 3); + moveToBeginningOfBuffer(thisCursor); + assertCursor(thisCursor, new Position(1, 1)); + }); + + test('move to beginning of buffer from within another line', () => { + moveTo(thisCursor, 3, 3); + moveToBeginningOfBuffer(thisCursor); + assertCursor(thisCursor, new Position(1, 1)); + }); + + test('move to beginning of buffer from within first line selection', () => { + moveTo(thisCursor, 1, 3); + moveToBeginningOfBuffer(thisCursor, true); + assertCursor(thisCursor, new Selection(1, 3, 1, 1)); + }); + + test('move to beginning of buffer from within another line selection', () => { + moveTo(thisCursor, 3, 3); + moveToBeginningOfBuffer(thisCursor, true); + assertCursor(thisCursor, new Selection(3, 3, 1, 1)); + }); + + // --------- move to end of buffer + + test('move to end of buffer', () => { + moveToEndOfBuffer(thisCursor); + assertCursor(thisCursor, new Position(5, LINE5.length + 1)); + }); + + test('move to end of buffer from within last line', () => { + moveTo(thisCursor, 5, 1); + moveToEndOfBuffer(thisCursor); + assertCursor(thisCursor, new Position(5, LINE5.length + 1)); + }); + + test('move to end of buffer from within another line', () => { + moveTo(thisCursor, 3, 3); + moveToEndOfBuffer(thisCursor); + assertCursor(thisCursor, new Position(5, LINE5.length + 1)); + }); + + test('move to end of buffer from within last line selection', () => { + moveTo(thisCursor, 5, 1); + moveToEndOfBuffer(thisCursor, true); + assertCursor(thisCursor, new Selection(5, 1, 5, LINE5.length + 1)); + }); + + test('move to end of buffer from within another line selection', () => { + moveTo(thisCursor, 3, 3); + moveToEndOfBuffer(thisCursor, true); + assertCursor(thisCursor, new Selection(3, 3, 5, LINE5.length + 1)); + }); + + // --------- misc + + test('select all', () => { + CoreNavigationCommands.SelectAll.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 5, LINE5.length + 1)); + }); + + test('expandLineSelection', () => { + // 0 1 2 + // 01234 56789012345678 0 + // let LINE1 = ' \tMy First Line\t '; + moveTo(thisCursor, 1, 1); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + + moveTo(thisCursor, 1, 2); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + + moveTo(thisCursor, 1, 5); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + + moveTo(thisCursor, 1, 19); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + + moveTo(thisCursor, 1, 20); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + + moveTo(thisCursor, 1, 21); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 3, 1)); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 4, 1)); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 5, 1)); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 5, LINE5.length + 1)); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); + assertCursor(thisCursor, new Selection(1, 1, 5, LINE5.length + 1)); + }); + + // --------- eventing + + test('no move doesn\'t trigger event', () => { + thisCursor.onDidChange((e) => { + assert.ok(false, 'was not expecting event'); + }); + moveTo(thisCursor, 1, 1); + }); + + test('move eventing', () => { + let events = 0; + thisCursor.onDidChange((e: CursorStateChangedEvent) => { + events++; + assert.deepEqual(e.selections, [new Selection(1, 2, 1, 2)]); + }); + moveTo(thisCursor, 1, 2); + assert.equal(events, 1, 'receives 1 event'); + }); + + test('move in selection mode eventing', () => { + let events = 0; + thisCursor.onDidChange((e: CursorStateChangedEvent) => { + events++; + assert.deepEqual(e.selections, [new Selection(1, 1, 1, 2)]); + }); + moveTo(thisCursor, 1, 2, true); + assert.equal(events, 1, 'receives 1 event'); + }); + + // --------- state save & restore + + test('saveState & restoreState', () => { + moveTo(thisCursor, 2, 1, true); + assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + + let savedState = JSON.stringify(thisCursor.saveState()); + + moveTo(thisCursor, 1, 1, false); + assertCursor(thisCursor, new Position(1, 1)); + + thisCursor.restoreState(JSON.parse(savedState)); + assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + }); + + // --------- updating cursor + + test('Independent model edit 1', () => { + moveTo(thisCursor, 2, 16, true); + + thisModel.applyEdits([EditOperation.delete(new Range(2, 1, 2, 2))]); + assertCursor(thisCursor, new Selection(1, 1, 2, 15)); + }); + + test('column select 1', () => { + withMockCodeEditor([ + '\tprivate compute(a:number): boolean {', + '\t\tif (a + 3 === 0 || a + 5 === 0) {', + '\t\t\treturn false;', + '\t\t}', + '\t}' + ], {}, (editor, cursor) => { + + moveTo(cursor, 1, 7, false); + assertCursor(cursor, new Position(1, 7)); + + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursor, { + position: new Position(4, 4), + viewPosition: new Position(4, 4), + mouseColumn: 15 + }); + + let expectedSelections = [ + new Selection(1, 7, 1, 12), + new Selection(2, 4, 2, 9), + new Selection(3, 3, 3, 6), + new Selection(4, 4, 4, 4), + ]; + + assertCursor(cursor, expectedSelections); + + }); + }); + + test('issue #4905 - column select is biased to the right', () => { + const model = Model.createFromString([ + 'var gulp = require("gulp");', + 'var path = require("path");', + 'var rimraf = require("rimraf");', + 'var isarray = require("isarray");', + 'var merge = require("merge-stream");', + 'var concat = require("gulp-concat");', + 'var newer = require("gulp-newer");', + ].join('\n')); + const config = new TestConfiguration(null); + const viewModel = new ViewModel(0, config, model, null); + const cursor = new Cursor(config, model, viewModel); + + moveTo(cursor, 1, 4, false); + assertCursor(cursor, new Position(1, 4)); + + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursor, { + position: new Position(4, 1), + viewPosition: new Position(4, 1), + mouseColumn: 1 + }); + + assertCursor(cursor, [ + new Selection(1, 4, 1, 1), + new Selection(2, 4, 2, 1), + new Selection(3, 4, 3, 1), + new Selection(4, 4, 4, 1), + ]); + + cursor.dispose(); + viewModel.dispose(); + config.dispose(); + model.dispose(); + }); + + test('issue #20087: column select with mouse', () => { + const model = Model.createFromString([ + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + ].join('\n')); + const config = new TestConfiguration(null); + const viewModel = new ViewModel(0, config, model, null); + const cursor = new Cursor(config, model, viewModel); + + moveTo(cursor, 10, 10, false); + assertCursor(cursor, new Position(10, 10)); + + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursor, { + position: new Position(1, 1), + viewPosition: new Position(1, 1), + mouseColumn: 1 + }); + assertCursor(cursor, [ + new Selection(10, 10, 10, 1), + new Selection(9, 10, 9, 1), + new Selection(8, 10, 8, 1), + new Selection(7, 10, 7, 1), + new Selection(6, 10, 6, 1), + new Selection(5, 10, 5, 1), + new Selection(4, 10, 4, 1), + new Selection(3, 10, 3, 1), + new Selection(2, 10, 2, 1), + new Selection(1, 10, 1, 1), + ]); + + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursor, { + position: new Position(1, 1), + viewPosition: new Position(1, 1), + mouseColumn: 1 + }); + assertCursor(cursor, [ + new Selection(10, 10, 10, 1), + new Selection(9, 10, 9, 1), + new Selection(8, 10, 8, 1), + new Selection(7, 10, 7, 1), + new Selection(6, 10, 6, 1), + new Selection(5, 10, 5, 1), + new Selection(4, 10, 4, 1), + new Selection(3, 10, 3, 1), + new Selection(2, 10, 2, 1), + new Selection(1, 10, 1, 1), + ]); + + cursor.dispose(); + viewModel.dispose(); + config.dispose(); + model.dispose(); + }); + + test('issue #20087: column select with keyboard', () => { + const model = Model.createFromString([ + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + ].join('\n')); + const config = new TestConfiguration(null); + const viewModel = new ViewModel(0, config, model, null); + const cursor = new Cursor(config, model, viewModel); + + moveTo(cursor, 10, 10, false); + assertCursor(cursor, new Position(10, 10)); + + CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(10, 10, 10, 9) + ]); + + CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(10, 10, 10, 8) + ]); + + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(10, 10, 10, 9) + ]); + + CoreNavigationCommands.CursorColumnSelectUp.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(10, 10, 10, 9), + new Selection(9, 10, 9, 9), + ]); + + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(10, 10, 10, 9) + ]); + + cursor.dispose(); + viewModel.dispose(); + config.dispose(); + model.dispose(); + }); + + test('column select with keyboard', () => { + const model = Model.createFromString([ + 'var gulp = require("gulp");', + 'var path = require("path");', + 'var rimraf = require("rimraf");', + 'var isarray = require("isarray");', + 'var merge = require("merge-stream");', + 'var concat = require("gulp-concat");', + 'var newer = require("gulp-newer");', + ].join('\n')); + const config = new TestConfiguration(null); + const viewModel = new ViewModel(0, config, model, null); + const cursor = new Cursor(config, model, viewModel); + + moveTo(cursor, 1, 4, false); + assertCursor(cursor, new Position(1, 4)); + + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 5) + ]); + + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 5), + new Selection(2, 4, 2, 5) + ]); + + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 5), + new Selection(2, 4, 2, 5), + new Selection(3, 4, 3, 5), + ]); + + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 5), + new Selection(2, 4, 2, 5), + new Selection(3, 4, 3, 5), + new Selection(4, 4, 4, 5), + new Selection(5, 4, 5, 5), + new Selection(6, 4, 6, 5), + new Selection(7, 4, 7, 5), + ]); + + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 6), + new Selection(2, 4, 2, 6), + new Selection(3, 4, 3, 6), + new Selection(4, 4, 4, 6), + new Selection(5, 4, 5, 6), + new Selection(6, 4, 6, 6), + new Selection(7, 4, 7, 6), + ]); + + // 10 times + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 16), + new Selection(2, 4, 2, 16), + new Selection(3, 4, 3, 16), + new Selection(4, 4, 4, 16), + new Selection(5, 4, 5, 16), + new Selection(6, 4, 6, 16), + new Selection(7, 4, 7, 16), + ]); + + // 10 times + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 26), + new Selection(2, 4, 2, 26), + new Selection(3, 4, 3, 26), + new Selection(4, 4, 4, 26), + new Selection(5, 4, 5, 26), + new Selection(6, 4, 6, 26), + new Selection(7, 4, 7, 26), + ]); + + // 2 times => reaching the ending of lines 1 and 2 + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 28), + new Selection(4, 4, 4, 28), + new Selection(5, 4, 5, 28), + new Selection(6, 4, 6, 28), + new Selection(7, 4, 7, 28), + ]); + + // 4 times => reaching the ending of line 3 + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 32), + new Selection(5, 4, 5, 32), + new Selection(6, 4, 6, 32), + new Selection(7, 4, 7, 32), + ]); + + // 2 times => reaching the ending of line 4 + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 34), + new Selection(6, 4, 6, 34), + new Selection(7, 4, 7, 34), + ]); + + // 1 time => reaching the ending of line 7 + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 35), + new Selection(6, 4, 6, 35), + new Selection(7, 4, 7, 35), + ]); + + // 3 times => reaching the ending of lines 5 & 6 + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 37), + new Selection(6, 4, 6, 37), + new Selection(7, 4, 7, 35), + ]); + + // cannot go anywhere anymore + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 37), + new Selection(6, 4, 6, 37), + new Selection(7, 4, 7, 35), + ]); + + // cannot go anywhere anymore even if we insist + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 37), + new Selection(6, 4, 6, 37), + new Selection(7, 4, 7, 35), + ]); + + // can easily go back + CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(cursor, {}); + assertCursor(cursor, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 36), + new Selection(6, 4, 6, 36), + new Selection(7, 4, 7, 35), + ]); + + cursor.dispose(); + viewModel.dispose(); + config.dispose(); + model.dispose(); + }); +}); + +class SurroundingMode extends MockMode { + + private static _id = new LanguageIdentifier('surroundingMode', 3); + + constructor() { + super(SurroundingMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + autoClosingPairs: [{ open: '(', close: ')' }] + })); + } +} + +class OnEnterMode extends MockMode { + private static _id = new LanguageIdentifier('onEnterMode', 3); + + constructor(indentAction: IndentAction, outdentCurrentLine?: boolean) { + super(OnEnterMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + onEnterRules: [{ + beforeText: /.*/, + action: { + indentAction: indentAction, + outdentCurrentLine: outdentCurrentLine + } + }] + })); + } +} + +class IndentRulesMode extends MockMode { + private static _id = new LanguageIdentifier('indentRulesMode', 4); + constructor(indentationRules: IndentationRule) { + super(IndentRulesMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + indentationRules: indentationRules + })); + } +} + +suite('Editor Controller - Regression tests', () => { + + test('issue Microsoft/monaco-editor#443: Indentation of a single row deletes selected text in some cases', () => { + let model = Model.createFromString( + [ + 'Hello world!', + 'another line' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: false + }, + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursor.setSelections('test', [new Selection(1, 1, 1, 13)]); + + // Check that indenting maintains the selection start at column 1 + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.deepEqual(cursor.getSelection(), new Selection(1, 1, 1, 14)); + }); + + model.dispose(); + }); + + test('Bug 9121: Auto indent + undo + redo is funky', () => { + let model = Model.createFromString( + [ + '' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: false + }, + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n', 'assert1'); + + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2'); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\t', 'assert3'); + + cursorCommand(cursor, H.Type, { text: 'x' }); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert4'); + + CoreNavigationCommands.CursorLeft.runCoreEditorCommand(cursor, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert5'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert6'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\tx', 'assert7'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), '\nx', 'assert8'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), 'x', 'assert9'); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\nx', 'assert10'); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert11'); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert12'); + + cursorCommand(cursor, H.Redo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert13'); + + cursorCommand(cursor, H.Redo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\nx', 'assert14'); + + cursorCommand(cursor, H.Redo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), 'x', 'assert15'); + }); + + model.dispose(); + }); + + test('bug #16815:Shift+Tab doesn\'t go back to tabstop', () => { + let mode = new OnEnterMode(IndentAction.IndentOutdent); + let model = Model.createFromString( + [ + ' function baz() {' + ].join('\n'), + { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + }, + mode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 1, 6, false); + assertCursor(cursor, new Selection(1, 6, 1, 6)); + + CoreEditingCommands.Outdent.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), ' function baz() {'); + assertCursor(cursor, new Selection(1, 5, 1, 5)); + }); + + model.dispose(); + mode.dispose(); + }); + + test('Bug #18293:[regression][editor] Can\'t outdent whitespace line', () => { + let model = Model.createFromString( + [ + ' ' + ].join('\n'), + { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 1, 7, false); + assertCursor(cursor, new Selection(1, 7, 1, 7)); + + CoreEditingCommands.Outdent.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), ' '); + assertCursor(cursor, new Selection(1, 5, 1, 5)); + }); + + model.dispose(); + }); + + test('Bug #16657: [editor] Tab on empty line of zero indentation moves cursor to position (1,1)', () => { + let model = Model.createFromString( + [ + 'function baz() {', + '\tfunction hello() { // something here', + '\t', + '', + '\t}', + '}', + '' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 7, 1, false); + assertCursor(cursor, new Selection(7, 1, 7, 1)); + + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(7), '\t'); + assertCursor(cursor, new Selection(7, 2, 7, 2)); + }); + + model.dispose(); + }); + + test('bug #16740: [editor] Cut line doesn\'t quite cut the last line', () => { + + // Part 1 => there is text on the last line + withMockCodeEditor([ + 'asdasd', + 'qwerty' + ], {}, (editor, cursor) => { + const model = editor.getModel(); + + moveTo(cursor, 2, 1, false); + assertCursor(cursor, new Selection(2, 1, 2, 1)); + + cursorCommand(cursor, H.Cut, null, 'keyboard'); + assert.equal(model.getLineCount(), 1); + assert.equal(model.getLineContent(1), 'asdasd'); + + }); + + // Part 2 => there is no text on the last line + withMockCodeEditor([ + 'asdasd', + '' + ], {}, (editor, cursor) => { + const model = editor.getModel(); + + moveTo(cursor, 2, 1, false); + assertCursor(cursor, new Selection(2, 1, 2, 1)); + + cursorCommand(cursor, H.Cut, null, 'keyboard'); + assert.equal(model.getLineCount(), 1); + assert.equal(model.getLineContent(1), 'asdasd'); + + cursorCommand(cursor, H.Cut, null, 'keyboard'); + assert.equal(model.getLineCount(), 1); + assert.equal(model.getLineContent(1), ''); + }); + }); + + test('Bug #11476: Double bracket surrounding + undo is broken', () => { + let mode = new SurroundingMode(); + usingCursor({ + text: [ + 'hello' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { tabSize: 4, insertSpaces: true, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 1, 3, false); + moveTo(cursor, 1, 5, true); + assertCursor(cursor, new Selection(1, 3, 1, 5)); + + cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + assertCursor(cursor, new Selection(1, 4, 1, 6)); + + cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + assertCursor(cursor, new Selection(1, 5, 1, 7)); + }); + mode.dispose(); + }); + + test('issue #1140: Backspace stops prematurely', () => { + let mode = new SurroundingMode(); + let model = Model.createFromString( + [ + 'function baz() {', + ' return 1;', + '};' + ].join('\n'), + { + tabSize: 4, + insertSpaces: true, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + }, + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 3, 2, false); + moveTo(cursor, 1, 14, true); + assertCursor(cursor, new Selection(3, 2, 1, 14)); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assertCursor(cursor, new Selection(1, 14, 1, 14)); + assert.equal(model.getLineCount(), 1); + assert.equal(model.getLineContent(1), 'function baz(;'); + }); + + model.dispose(); + mode.dispose(); + }); + + test('issue #10212: Pasting entire line does not replace selection', () => { + usingCursor({ + text: [ + 'line1', + 'line2' + ], + }, (model, cursor) => { + moveTo(cursor, 2, 1, false); + moveTo(cursor, 2, 6, true); + + cursorCommand(cursor, H.Paste, { text: 'line1\n', pasteOnNewLine: true }); + + assert.equal(model.getLineContent(1), 'line1'); + assert.equal(model.getLineContent(2), 'line1'); + assert.equal(model.getLineContent(3), ''); + }); + }); + + test('issue #3071: Investigate why undo stack gets corrupted', () => { + let model = Model.createFromString( + [ + 'some lines', + 'and more lines', + 'just some text', + ].join('\n'), + { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 1, 1, false); + moveTo(cursor, 3, 4, true); + + let isFirst = true; + model.onDidChangeContent(() => { + if (isFirst) { + isFirst = false; + cursorCommand(cursor, H.Type, { text: '\t' }, 'keyboard'); + } + }); + + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getValue(), [ + '\t just some text' + ].join('\n'), '001'); + + cursorCommand(cursor, H.Undo); + assert.equal(model.getValue(), [ + 'some lines', + 'and more lines', + 'just some text', + ].join('\n'), '002'); + + cursorCommand(cursor, H.Undo); + assert.equal(model.getValue(), [ + 'some lines', + 'and more lines', + 'just some text', + ].join('\n'), '003'); + }); + + model.dispose(); + }); + + test('issue #12950: Cannot Double Click To Insert Emoji Using OSX Emoji Panel', () => { + usingCursor({ + text: [ + 'some lines', + 'and more lines', + 'just some text', + ], + languageIdentifier: null, + modelOpts: { insertSpaces: true, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 3, 1, false); + + cursorCommand(cursor, H.Type, { text: '😍' }, 'keyboard'); + + assert.equal(model.getValue(), [ + 'some lines', + 'and more lines', + '😍just some text', + ].join('\n')); + }); + }); + + test('issue #3463: pressing tab adds spaces, but not as many as for a tab', () => { + let model = Model.createFromString( + [ + 'function a() {', + '\tvar a = {', + '\t\tx: 3', + '\t};', + '}', + ].join('\n'), + { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 3, 2, false); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(3), '\t \tx: 3'); + }); + + model.dispose(); + }); + + test('issue #4312: trying to type a tab character over a sequence of spaces results in unexpected behaviour', () => { + let model = Model.createFromString( + [ + 'var foo = 123; // this is a comment', + 'var bar = 4; // another comment' + ].join('\n'), + { + insertSpaces: false, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 1, 15, false); + moveTo(cursor, 1, 22, true); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), 'var foo = 123;\t// this is a comment'); + }); + + model.dispose(); + }); + + test('issue #832: word right', () => { + + usingCursor({ + text: [ + ' /* Just some more text a+= 3 +5-3 + 7 */ ' + ], + }, (model, cursor) => { + moveTo(cursor, 1, 1, false); + + function assertWordRight(col: number, expectedCol: number) { + let args = { + position: { + lineNumber: 1, + column: col + } + }; + if (col === 1) { + CoreNavigationCommands.WordSelect.runCoreEditorCommand(cursor, args); + } else { + CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(cursor, args); + } + + assert.equal(cursor.getSelection().startColumn, 1, 'TEST FOR ' + col); + assert.equal(cursor.getSelection().endColumn, expectedCol, 'TEST FOR ' + col); + } + + assertWordRight(1, ' '.length + 1); + assertWordRight(2, ' '.length + 1); + assertWordRight(3, ' '.length + 1); + assertWordRight(4, ' '.length + 1); + assertWordRight(5, ' /'.length + 1); + assertWordRight(6, ' /*'.length + 1); + assertWordRight(7, ' /* '.length + 1); + assertWordRight(8, ' /* Just'.length + 1); + assertWordRight(9, ' /* Just'.length + 1); + assertWordRight(10, ' /* Just'.length + 1); + assertWordRight(11, ' /* Just'.length + 1); + assertWordRight(12, ' /* Just '.length + 1); + assertWordRight(13, ' /* Just some'.length + 1); + assertWordRight(14, ' /* Just some'.length + 1); + assertWordRight(15, ' /* Just some'.length + 1); + assertWordRight(16, ' /* Just some'.length + 1); + assertWordRight(17, ' /* Just some '.length + 1); + assertWordRight(18, ' /* Just some '.length + 1); + assertWordRight(19, ' /* Just some '.length + 1); + assertWordRight(20, ' /* Just some more'.length + 1); + assertWordRight(21, ' /* Just some more'.length + 1); + assertWordRight(22, ' /* Just some more'.length + 1); + assertWordRight(23, ' /* Just some more'.length + 1); + assertWordRight(24, ' /* Just some more '.length + 1); + assertWordRight(25, ' /* Just some more '.length + 1); + assertWordRight(26, ' /* Just some more '.length + 1); + assertWordRight(27, ' /* Just some more text'.length + 1); + assertWordRight(28, ' /* Just some more text'.length + 1); + assertWordRight(29, ' /* Just some more text'.length + 1); + assertWordRight(30, ' /* Just some more text'.length + 1); + assertWordRight(31, ' /* Just some more text '.length + 1); + assertWordRight(32, ' /* Just some more text a'.length + 1); + assertWordRight(33, ' /* Just some more text a+'.length + 1); + assertWordRight(34, ' /* Just some more text a+='.length + 1); + assertWordRight(35, ' /* Just some more text a+= '.length + 1); + assertWordRight(36, ' /* Just some more text a+= 3'.length + 1); + assertWordRight(37, ' /* Just some more text a+= 3 '.length + 1); + assertWordRight(38, ' /* Just some more text a+= 3 +'.length + 1); + assertWordRight(39, ' /* Just some more text a+= 3 +5'.length + 1); + assertWordRight(40, ' /* Just some more text a+= 3 +5-'.length + 1); + assertWordRight(41, ' /* Just some more text a+= 3 +5-3'.length + 1); + assertWordRight(42, ' /* Just some more text a+= 3 +5-3 '.length + 1); + assertWordRight(43, ' /* Just some more text a+= 3 +5-3 +'.length + 1); + assertWordRight(44, ' /* Just some more text a+= 3 +5-3 + '.length + 1); + assertWordRight(45, ' /* Just some more text a+= 3 +5-3 + 7'.length + 1); + assertWordRight(46, ' /* Just some more text a+= 3 +5-3 + 7 '.length + 1); + assertWordRight(47, ' /* Just some more text a+= 3 +5-3 + 7 *'.length + 1); + assertWordRight(48, ' /* Just some more text a+= 3 +5-3 + 7 */'.length + 1); + assertWordRight(49, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1); + assertWordRight(50, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1); + }); + }); + + test('issue #9675: Undo/Redo adds a stop in between CHN Characters', () => { + usingCursor({ + text: [ + ] + }, (model, cursor) => { + assertCursor(cursor, new Position(1, 1)); + + // Typing sennsei in Japanese - Hiragana + cursorCommand(cursor, H.Type, { text: 's' }, 'keyboard'); + cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せ', replaceCharCnt: 1 }); + cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せn', replaceCharCnt: 1 }); + cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せん', replaceCharCnt: 2 }); + cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんs', replaceCharCnt: 2 }); + cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせ', replaceCharCnt: 3 }); + cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせ', replaceCharCnt: 3 }); + cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせい', replaceCharCnt: 3 }); + cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせい', replaceCharCnt: 4 }); + cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせい', replaceCharCnt: 4 }); + cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせい', replaceCharCnt: 4 }); + + assert.equal(model.getLineContent(1), 'せんせい'); + assertCursor(cursor, new Position(1, 5)); + + cursorCommand(cursor, H.Undo); + assert.equal(model.getLineContent(1), ''); + assertCursor(cursor, new Position(1, 1)); + }); + }); + + test('issue #23913: Greater than 1000+ multi cursor typing replacement text appears inverted, lines begin to drop off selection', () => { + const LINE_CNT = 2000; + + let text = []; + for (let i = 0; i < LINE_CNT; i++) { + text[i] = 'asd'; + } + usingCursor({ + text: text + }, (model, cursor) => { + + let selections: Selection[] = []; + for (let i = 0; i < LINE_CNT; i++) { + selections[i] = new Selection(i + 1, 1, i + 1, 1); + } + cursor.setSelections('test', selections); + + cursorCommand(cursor, H.Type, { text: 'n' }, 'keyboard'); + cursorCommand(cursor, H.Type, { text: 'n' }, 'keyboard'); + + for (let i = 0; i < LINE_CNT; i++) { + assert.equal(model.getLineContent(i + 1), 'nnasd', 'line #' + (i + 1)); + } + + assert.equal(cursor.getSelections().length, LINE_CNT); + assert.equal(cursor.getSelections()[LINE_CNT - 1].startLineNumber, LINE_CNT); + }); + }); + + test('issue #23983: Calling model.setEOL does not reset cursor position', () => { + usingCursor({ + text: [ + 'first line', + 'second line' + ] + }, (model, cursor) => { + model.setEOL(EndOfLineSequence.CRLF); + + cursor.setSelections('test', [new Selection(2, 2, 2, 2)]); + model.setEOL(EndOfLineSequence.LF); + + assertCursor(cursor, new Selection(2, 2, 2, 2)); + }); + }); + + test('issue #23983: Calling model.setValue() resets cursor position', () => { + usingCursor({ + text: [ + 'first line', + 'second line' + ] + }, (model, cursor) => { + model.setEOL(EndOfLineSequence.CRLF); + + cursor.setSelections('test', [new Selection(2, 2, 2, 2)]); + model.setValue([ + 'different first line', + 'different second line', + 'new third line' + ].join('\n')); + + assertCursor(cursor, new Selection(1, 1, 1, 1)); + }); + }); +}); + +suite('Editor Controller - Cursor Configuration', () => { + + test('Cursor honors insertSpaces configuration on new line', () => { + usingCursor({ + text: [ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line', + '', + '1' + ], + modelOpts: { insertSpaces: true, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(1, 21), source: 'keyboard' }); + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' \tMy First Line\t '); + assert.equal(model.getLineContent(2), ' '); + }); + }); + + test('Cursor honors insertSpaces configuration on tab', () => { + let model = Model.createFromString( + [ + ' \tMy First Line\t ', + 'My Second Line123', + ' Third Line', + '', + '1' + ].join('\n'), + { + insertSpaces: true, + tabSize: 13, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + // Tab on column 1 + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 1) }); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), ' My Second Line123'); + cursorCommand(cursor, H.Undo, null, 'keyboard'); + + // Tab on column 2 + assert.equal(model.getLineContent(2), 'My Second Line123'); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 2) }); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'M y Second Line123'); + cursorCommand(cursor, H.Undo, null, 'keyboard'); + + // Tab on column 3 + assert.equal(model.getLineContent(2), 'My Second Line123'); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 3) }); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'My Second Line123'); + cursorCommand(cursor, H.Undo, null, 'keyboard'); + + // Tab on column 4 + assert.equal(model.getLineContent(2), 'My Second Line123'); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 4) }); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'My Second Line123'); + cursorCommand(cursor, H.Undo, null, 'keyboard'); + + // Tab on column 5 + assert.equal(model.getLineContent(2), 'My Second Line123'); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 5) }); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'My S econd Line123'); + cursorCommand(cursor, H.Undo, null, 'keyboard'); + + // Tab on column 5 + assert.equal(model.getLineContent(2), 'My Second Line123'); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 5) }); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'My S econd Line123'); + cursorCommand(cursor, H.Undo, null, 'keyboard'); + + // Tab on column 13 + assert.equal(model.getLineContent(2), 'My Second Line123'); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 13) }); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'My Second Li ne123'); + cursorCommand(cursor, H.Undo, null, 'keyboard'); + + // Tab on column 14 + assert.equal(model.getLineContent(2), 'My Second Line123'); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 14) }); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'My Second Lin e123'); + }); + + model.dispose(); + }); + + test('Enter auto-indents with insertSpaces setting 1', () => { + let mode = new OnEnterMode(IndentAction.Indent); + usingCursor({ + text: [ + '\thello' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: true, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 1, 7, false); + assertCursor(cursor, new Selection(1, 7, 1, 7)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n '); + }); + mode.dispose(); + }); + + test('Enter auto-indents with insertSpaces setting 2', () => { + let mode = new OnEnterMode(IndentAction.None); + usingCursor({ + text: [ + '\thello' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: true, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 1, 7, false); + assertCursor(cursor, new Selection(1, 7, 1, 7)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n '); + }); + mode.dispose(); + }); + + test('Enter auto-indents with insertSpaces setting 3', () => { + let mode = new OnEnterMode(IndentAction.IndentOutdent); + usingCursor({ + text: [ + '\thell()' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: true, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 1, 7, false); + assertCursor(cursor, new Selection(1, 7, 1, 7)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getValue(EndOfLinePreference.CRLF), '\thell(\r\n \r\n )'); + }); + mode.dispose(); + }); + + test('removeAutoWhitespace off', () => { + usingCursor({ + text: [ + ' some line abc ' + ], + modelOpts: { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: false + } + }, (model, cursor) => { + + // Move cursor to the end, verify that we do not trim whitespaces if line has values + moveTo(cursor, 1, model.getLineContent(1).length + 1); + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' some line abc '); + assert.equal(model.getLineContent(2), ' '); + + // Try to enter again, we should trimmed previous line + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' some line abc '); + assert.equal(model.getLineContent(2), ' '); + assert.equal(model.getLineContent(3), ' '); + }); + }); + + test('removeAutoWhitespace on: removes only whitespace the cursor added 1', () => { + usingCursor({ + text: [ + ' ' + ], + modelOpts: { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + }, (model, cursor) => { + moveTo(cursor, 1, model.getLineContent(1).length + 1); + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' '); + assert.equal(model.getLineContent(2), ' '); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' '); + assert.equal(model.getLineContent(2), ''); + assert.equal(model.getLineContent(3), ' '); + }); + }); + + test('issue #6862: Editor removes auto inserted indentation when formatting on type', () => { + let mode = new OnEnterMode(IndentAction.IndentOutdent); + usingCursor({ + text: [ + 'function foo (params: string) {}' + ], + modelOpts: { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + }, + languageIdentifier: mode.getLanguageIdentifier(), + }, (model, cursor) => { + + moveTo(cursor, 1, 32); + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), 'function foo (params: string) {'); + assert.equal(model.getLineContent(2), ' '); + assert.equal(model.getLineContent(3), '}'); + + class TestCommand implements ICommand { + + private _selectionId: string = null; + + public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void { + builder.addEditOperation(new Range(1, 13, 1, 14), ''); + this._selectionId = builder.trackSelection(cursor.getSelection()); + } + + public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { + return helper.getTrackedSelection(this._selectionId); + } + + } + + cursor.trigger('autoFormat', Handler.ExecuteCommand, new TestCommand()); + assert.equal(model.getLineContent(1), 'function foo(params: string) {'); + assert.equal(model.getLineContent(2), ' '); + assert.equal(model.getLineContent(3), '}'); + }); + mode.dispose(); + }); + + test('removeAutoWhitespace on: removes only whitespace the cursor added 2', () => { + let model = Model.createFromString( + [ + ' if (a) {', + ' ', + '', + '', + ' }' + ].join('\n'), + { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + + moveTo(cursor, 3, 1); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), ' if (a) {'); + assert.equal(model.getLineContent(2), ' '); + assert.equal(model.getLineContent(3), ' '); + assert.equal(model.getLineContent(4), ''); + assert.equal(model.getLineContent(5), ' }'); + + moveTo(cursor, 4, 1); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), ' if (a) {'); + assert.equal(model.getLineContent(2), ' '); + assert.equal(model.getLineContent(3), ''); + assert.equal(model.getLineContent(4), ' '); + assert.equal(model.getLineContent(5), ' }'); + + moveTo(cursor, 5, model.getLineMaxColumn(5)); + cursorCommand(cursor, H.Type, { text: 'something' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' if (a) {'); + assert.equal(model.getLineContent(2), ' '); + assert.equal(model.getLineContent(3), ''); + assert.equal(model.getLineContent(4), ''); + assert.equal(model.getLineContent(5), ' }something'); + }); + + model.dispose(); + }); + + test('removeAutoWhitespace on: test 1', () => { + let model = Model.createFromString( + [ + ' some line abc ' + ].join('\n'), + { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + + // Move cursor to the end, verify that we do not trim whitespaces if line has values + moveTo(cursor, 1, model.getLineContent(1).length + 1); + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' some line abc '); + assert.equal(model.getLineContent(2), ' '); + + // Try to enter again, we should trimmed previous line + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' some line abc '); + assert.equal(model.getLineContent(2), ''); + assert.equal(model.getLineContent(3), ' '); + + // More whitespaces + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), ' some line abc '); + assert.equal(model.getLineContent(2), ''); + assert.equal(model.getLineContent(3), ' '); + + // Enter and verify that trimmed again + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' some line abc '); + assert.equal(model.getLineContent(2), ''); + assert.equal(model.getLineContent(3), ''); + assert.equal(model.getLineContent(4), ' '); + + // Trimmed if we will keep only text + moveTo(cursor, 1, 5); + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' '); + assert.equal(model.getLineContent(2), ' some line abc '); + assert.equal(model.getLineContent(3), ''); + assert.equal(model.getLineContent(4), ''); + assert.equal(model.getLineContent(5), ''); + + // Trimmed if we will keep only text by selection + moveTo(cursor, 2, 5); + moveTo(cursor, 3, 1, true); + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(1), ' '); + assert.equal(model.getLineContent(2), ' '); + assert.equal(model.getLineContent(3), ' '); + assert.equal(model.getLineContent(4), ''); + assert.equal(model.getLineContent(5), ''); + }); + + model.dispose(); + }); + + test('UseTabStops is off', () => { + let model = Model.createFromString( + [ + ' x', + ' a ', + ' ' + ].join('\n'), + { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model, useTabStops: false }, (editor, cursor) => { + // DeleteLeft removes just one whitespace + moveTo(cursor, 2, 9); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), ' a '); + }); + + model.dispose(); + }); + + test('Backspace removes whitespaces with tab size', () => { + let model = Model.createFromString( + [ + ' \t \t x', + ' a ', + ' ' + ].join('\n'), + { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model, useTabStops: true }, (editor, cursor) => { + // DeleteLeft does not remove tab size, because some text exists before + moveTo(cursor, 2, model.getLineContent(2).length + 1); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), ' a '); + + // DeleteLeft removes tab size = 4 + moveTo(cursor, 2, 9); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), ' a '); + + // DeleteLeft removes tab size = 4 + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'a '); + + // Undo DeleteLeft - get us back to original indentation + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(2), ' a '); + + // Nothing is broken when cursor is in (1,1) + moveTo(cursor, 1, 1); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), ' \t \t x'); + + // DeleteLeft stops at tab stops even in mixed whitespace case + moveTo(cursor, 1, 10); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), ' \t \t x'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), ' \t \tx'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), ' \tx'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), 'x'); + + // DeleteLeft on last line + moveTo(cursor, 3, model.getLineContent(3).length + 1); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(3), ''); + + // DeleteLeft with removing new line symbol + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), 'x\n a '); + + // In case of selection DeleteLeft only deletes selected text + moveTo(cursor, 2, 3); + moveTo(cursor, 2, 4, true); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), ' a '); + }); + + model.dispose(); + }); + + test('PR #5423: Auto indent + undo + redo is funky', () => { + let model = Model.createFromString( + [ + '' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + } + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n', 'assert1'); + + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2'); + + cursorCommand(cursor, H.Type, { text: 'y' }, 'keyboard'); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty', 'assert2'); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty\n\t', 'assert3'); + + cursorCommand(cursor, H.Type, { text: 'x' }); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert4'); + + CoreNavigationCommands.CursorLeft.runCoreEditorCommand(cursor, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert5'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert6'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\tyx', 'assert7'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\tx', 'assert8'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), '\nx', 'assert9'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), 'x', 'assert10'); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\nx', 'assert11'); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert12'); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert13'); + + cursorCommand(cursor, H.Redo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert14'); + + cursorCommand(cursor, H.Redo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), '\nx', 'assert15'); + + cursorCommand(cursor, H.Redo, {}); + assert.equal(model.getValue(EndOfLinePreference.LF), 'x', 'assert16'); + }); + + model.dispose(); + }); +}); + +suite('Editor Controller - Indentation Rules', () => { + let mode = new IndentRulesMode({ + decreaseIndentPattern: /^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$/, + increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$/, + indentNextLinePattern: /^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$)/, + unIndentedLinePattern: /^(?!.*([;{}]|\S:)\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!.*(\{[^}"']*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$))/ + }); + + test('Enter honors increaseIndentPattern', () => { + usingCursor({ + text: [ + 'if (true) {', + '\tif (true) {' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true }, + editorOpts: { autoIndent: true } + }, (model, cursor) => { + moveTo(cursor, 1, 12, false); + assertCursor(cursor, new Selection(1, 12, 1, 12)); + + cursorCommandAndTokenize(model, cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(2, 2, 2, 2)); + + moveTo(cursor, 3, 13, false); + assertCursor(cursor, new Selection(3, 13, 3, 13)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 3, 4, 3)); + }); + }); + + test('Type honors decreaseIndentPattern', () => { + usingCursor({ + text: [ + 'if (true) {', + '\t' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true }, + editorOpts: { autoIndent: true } + }, (model, cursor) => { + moveTo(cursor, 2, 2, false); + assertCursor(cursor, new Selection(2, 2, 2, 2)); + + cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + assertCursor(cursor, new Selection(2, 2, 2, 2)); + assert.equal(model.getLineContent(2), '}', '001'); + }); + }); + + test('Enter honors unIndentedLinePattern', () => { + usingCursor({ + text: [ + 'if (true) {', + '\t\t\treturn true' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true }, + editorOpts: { autoIndent: true } + }, (model, cursor) => { + moveTo(cursor, 2, 15, false); + assertCursor(cursor, new Selection(2, 15, 2, 15)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(3, 2, 3, 2)); + }); + }); + + test('Enter honors indentNextLinePattern', () => { + usingCursor({ + text: [ + 'if (true)', + '\treturn true;', + 'if (true)', + '\t\t\t\treturn true' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true }, + editorOpts: { autoIndent: true } + }, (model, cursor) => { + moveTo(cursor, 2, 14, false); + assertCursor(cursor, new Selection(2, 14, 2, 14)); + + cursorCommandAndTokenize(model, cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(3, 1, 3, 1)); + + moveTo(cursor, 5, 16, false); + assertCursor(cursor, new Selection(5, 16, 5, 16)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(6, 2, 6, 2)); + }); + }); + + test('Enter honors indentNextLinePattern 2', () => { + let model = Model.createFromString( + [ + 'if (true)', + '\tif (true)' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + mode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model, autoIndent: true }, (editor, cursor) => { + moveTo(cursor, 2, 11, false); + assertCursor(cursor, new Selection(2, 11, 2, 11)); + + cursorCommandAndTokenize(model, cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(3, 3, 3, 3)); + + cursorCommand(cursor, H.Type, { text: 'console.log();' }, 'keyboard'); + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 1, 4, 1)); + }); + + model.dispose(); + }); + + test('Enter honors intential indent', () => { + usingCursor({ + text: [ + 'if (true) {', + '\tif (true) {', + 'return true;', + '}}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true }, + editorOpts: { autoIndent: true } + }, (model, cursor) => { + moveTo(cursor, 3, 13, false); + assertCursor(cursor, new Selection(3, 13, 3, 13)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 1, 4, 1)); + assert.equal(model.getLineContent(3), 'return true;', '001'); + }); + }); + + test('Enter supports selection 1', () => { + usingCursor({ + text: [ + 'if (true) {', + '\tif (true) {', + '\t\treturn true;', + '\t}a}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 4, 3, false); + moveTo(cursor, 4, 4, true); + assertCursor(cursor, new Selection(4, 3, 4, 4)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(5, 1, 5, 1)); + assert.equal(model.getLineContent(4), '\t}', '001'); + }); + }); + + test('Enter supports selection 2', () => { + usingCursor({ + text: [ + 'if (true) {', + '\tif (true) {' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 2, 12, false); + moveTo(cursor, 2, 13, true); + assertCursor(cursor, new Selection(2, 12, 2, 13)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(3, 3, 3, 3)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 3, 4, 3)); + }); + }); + + test('Enter honors tabSize and insertSpaces 1', () => { + usingCursor({ + text: [ + 'if (true) {', + '\tif (true) {' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: true, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 1, 12, false); + assertCursor(cursor, new Selection(1, 12, 1, 12)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(2, 5, 2, 5)); + + model.forceTokenization(model.getLineCount()); + + moveTo(cursor, 3, 13, false); + assertCursor(cursor, new Selection(3, 13, 3, 13)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 9, 4, 9)); + }); + }); + + test('Enter honors tabSize and insertSpaces 2', () => { + usingCursor({ + text: [ + 'if (true) {', + ' if (true) {' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: true, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 1, 12, false); + assertCursor(cursor, new Selection(1, 12, 1, 12)); + + cursorCommandAndTokenize(model, cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(2, 5, 2, 5)); + + moveTo(cursor, 3, 16, false); + assertCursor(cursor, new Selection(3, 16, 3, 16)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(3), ' if (true) {'); + assertCursor(cursor, new Selection(4, 9, 4, 9)); + }); + }); + + test('Enter honors tabSize and insertSpaces 3', () => { + usingCursor({ + text: [ + 'if (true) {', + ' if (true) {' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 1, 12, false); + assertCursor(cursor, new Selection(1, 12, 1, 12)); + + cursorCommandAndTokenize(model, cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(2, 2, 2, 2)); + + moveTo(cursor, 3, 16, false); + assertCursor(cursor, new Selection(3, 16, 3, 16)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(3), ' if (true) {'); + assertCursor(cursor, new Selection(4, 3, 4, 3)); + }); + }); + + test('Enter supports intentional indentation', () => { + usingCursor({ + text: [ + '\tif (true) {', + '\t\tswitch(true) {', + '\t\t\tcase true:', + '\t\t\t\tbreak;', + '\t\t}', + '\t}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true }, + editorOpts: { autoIndent: true } + }, (model, cursor) => { + moveTo(cursor, 5, 4, false); + assertCursor(cursor, new Selection(5, 4, 5, 4)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(5), '\t\t}'); + assertCursor(cursor, new Selection(6, 3, 6, 3)); + }); + }); + + test('Enter should not adjust cursor position when press enter in the middle of a line 1', () => { + usingCursor({ + text: [ + 'if (true) {', + '\tif (true) {', + '\t\treturn true;', + '\t}a}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 3, 9, false); + assertCursor(cursor, new Selection(3, 9, 3, 9)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 3, 4, 3)); + assert.equal(model.getLineContent(4), '\t\t true;', '001'); + }); + }); + + test('Enter should not adjust cursor position when press enter in the middle of a line 2', () => { + usingCursor({ + text: [ + 'if (true) {', + '\tif (true) {', + '\t\treturn true;', + '\t}a}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 3, 3, false); + assertCursor(cursor, new Selection(3, 3, 3, 3)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 3, 4, 3)); + assert.equal(model.getLineContent(4), '\t\treturn true;', '001'); + }); + }); + + test('Enter should not adjust cursor position when press enter in the middle of a line 3', () => { + usingCursor({ + text: [ + 'if (true) {', + ' if (true) {', + ' return true;', + ' }a}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: true, tabSize: 2, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 3, 11, false); + assertCursor(cursor, new Selection(3, 11, 3, 11)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 5, 4, 5)); + assert.equal(model.getLineContent(4), ' true;', '001'); + }); + }); + + test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 1', () => { + usingCursor({ + text: [ + 'if (true) {', + '\tif (true) {', + '\t\treturn true;', + '\t}a}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 3, 2, false); + assertCursor(cursor, new Selection(3, 2, 3, 2)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 2, 4, 2)); + assert.equal(model.getLineContent(4), '\t\treturn true;', '001'); + + moveTo(cursor, 4, 1, false); + assertCursor(cursor, new Selection(4, 1, 4, 1)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(5, 1, 5, 1)); + assert.equal(model.getLineContent(5), '\t\treturn true;', '002'); + }); + }); + + test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 2', () => { + usingCursor({ + text: [ + '\tif (true) {', + '\t\tif (true) {', + '\t \treturn true;', + '\t\t}a}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 3, 4, false); + assertCursor(cursor, new Selection(3, 4, 3, 4)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 3, 4, 3)); + assert.equal(model.getLineContent(4), '\t\t\treturn true;', '001'); + + moveTo(cursor, 4, 1, false); + assertCursor(cursor, new Selection(4, 1, 4, 1)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(5, 1, 5, 1)); + assert.equal(model.getLineContent(5), '\t\t\treturn true;', '002'); + }); + }); + + test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 3', () => { + usingCursor({ + text: [ + 'if (true) {', + ' if (true) {', + ' return true;', + '}a}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: true, tabSize: 2, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 3, 2, false); + assertCursor(cursor, new Selection(3, 2, 3, 2)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 2, 4, 2)); + assert.equal(model.getLineContent(4), ' return true;', '001'); + + moveTo(cursor, 4, 3, false); + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(5, 3, 5, 3)); + assert.equal(model.getLineContent(5), ' return true;', '002'); + }); + }); + + test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 4', () => { + usingCursor({ + text: [ + 'if (true) {', + ' if (true) {', + '\t return true;', + '}a}', + '', + 'if (true) {', + ' if (true) {', + '\t return true;', + '}a}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: true, tabSize: 2, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 3, 3, false); + assertCursor(cursor, new Selection(3, 3, 3, 3)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 4, 4, 4)); + assert.equal(model.getLineContent(4), ' return true;', '001'); + + moveTo(cursor, 9, 4, false); + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(10, 5, 10, 5)); + assert.equal(model.getLineContent(10), ' return true;', '001'); + }); + }); + + test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 5', () => { + usingCursor({ + text: [ + 'if (true) {', + ' if (true) {', + ' return true;', + ' return true;', + '' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: true, tabSize: 2, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 3, 5, false); + moveTo(cursor, 4, 3, true); + assertCursor(cursor, new Selection(3, 5, 4, 3)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 3, 4, 3)); + assert.equal(model.getLineContent(4), ' return true;', '001'); + }); + }); + + test('issue Microsoft/monaco-editor#108 part 1/2: Auto indentation on Enter with selection is half broken', () => { + usingCursor({ + text: [ + 'function baz() {', + '\tvar x = 1;', + '\t\t\t\t\t\t\treturn x;', + '}' + ], + modelOpts: { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + languageIdentifier: mode.getLanguageIdentifier(), + }, (model, cursor) => { + moveTo(cursor, 3, 8, false); + moveTo(cursor, 2, 12, true); + assertCursor(cursor, new Selection(3, 8, 2, 12)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(3), '\treturn x;'); + assertCursor(cursor, new Position(3, 2)); + }); + }); + + test('issue Microsoft/monaco-editor#108 part 2/2: Auto indentation on Enter with selection is half broken', () => { + usingCursor({ + text: [ + 'function baz() {', + '\tvar x = 1;', + '\t\t\t\t\t\t\treturn x;', + '}' + ], + modelOpts: { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + languageIdentifier: mode.getLanguageIdentifier(), + }, (model, cursor) => { + moveTo(cursor, 2, 12, false); + moveTo(cursor, 3, 8, true); + assertCursor(cursor, new Selection(2, 12, 3, 8)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(3), '\treturn x;'); + assertCursor(cursor, new Position(3, 2)); + }); + }); + + test('onEnter works if there are no indentation rules', () => { + usingCursor({ + text: [ + '' + ], + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 5, 3, false); + assertCursor(cursor, new Selection(5, 3, 5, 3)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getLineContent(6), '\t'); + assertCursor(cursor, new Selection(6, 2, 6, 2)); + assert.equal(model.getLineContent(5), '\t}'); + }); + }); + + test('onEnter works if there are no indentation rules 2', () => { + usingCursor({ + text: [ + ' if (5)', + ' return 5;', + ' ' + ], + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 3, 2, false); + assertCursor(cursor, new Selection(3, 2, 3, 2)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assertCursor(cursor, new Selection(4, 2, 4, 2)); + assert.equal(model.getLineContent(4), '\t'); + }); + }); + + test('bug #16543: Tab should indent to correct indentation spot immediately', () => { + let model = Model.createFromString( + [ + 'function baz() {', + '\tfunction hello() { // something here', + '\t', + '', + '\t}', + '}' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + mode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 4, 1, false); + assertCursor(cursor, new Selection(4, 1, 4, 1)); + + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(4), '\t\t'); + }); + + model.dispose(); + }); + + + test('bug #2938 (1): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => { + let model = Model.createFromString( + [ + '\tfunction baz() {', + '\t\tfunction hello() { // something here', + '\t\t', + '\t', + '\t\t}', + '\t}' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + mode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 4, 2, false); + assertCursor(cursor, new Selection(4, 2, 4, 2)); + + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(4), '\t\t\t'); + }); + + model.dispose(); + }); + + + test('bug #2938 (2): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => { + let model = Model.createFromString( + [ + '\tfunction baz() {', + '\t\tfunction hello() { // something here', + '\t\t', + ' ', + '\t\t}', + '\t}' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + mode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 4, 1, false); + assertCursor(cursor, new Selection(4, 1, 4, 1)); + + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(4), '\t\t\t'); + }); + + model.dispose(); + }); + + test('bug #2938 (3): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => { + let model = Model.createFromString( + [ + '\tfunction baz() {', + '\t\tfunction hello() { // something here', + '\t\t', + '\t\t\t', + '\t\t}', + '\t}' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + mode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 4, 3, false); + assertCursor(cursor, new Selection(4, 3, 4, 3)); + + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(4), '\t\t\t\t'); + }); + + model.dispose(); + }); + + test('bug #2938 (4): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => { + let model = Model.createFromString( + [ + '\tfunction baz() {', + '\t\tfunction hello() { // something here', + '\t\t', + '\t\t\t\t', + '\t\t}', + '\t}' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + mode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + moveTo(cursor, 4, 4, false); + assertCursor(cursor, new Selection(4, 4, 4, 4)); + + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(4), '\t\t\t\t\t'); + }); + + model.dispose(); + }); + + test('bug #31015: When pressing Tab on lines and Enter rules are avail, indent straight to the right spotTab', () => { + let mode = new OnEnterMode(IndentAction.Indent); + let model = Model.createFromString( + [ + ' if (a) {', + ' ', + '', + '', + ' }' + ].join('\n'), + { + insertSpaces: true, + tabSize: 4, + detectIndentation: false, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + }, + mode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + + moveTo(cursor, 3, 1); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), ' if (a) {'); + assert.equal(model.getLineContent(2), ' '); + assert.equal(model.getLineContent(3), ' '); + assert.equal(model.getLineContent(4), ''); + assert.equal(model.getLineContent(5), ' }'); + }); + + model.dispose(); + }); + + test('type honors indentation rules: ruby keywords', () => { + let rubyMode = new IndentRulesMode({ + increaseIndentPattern: /^\s*((begin|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while)|(.*\sdo\b))\b[^\{;]*$/, + decreaseIndentPattern: /^\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif|when)\b)/ + }); + let model = Model.createFromString( + [ + 'class Greeter', + ' def initialize(name)', + ' @name = name', + ' en' + ].join('\n'), + { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: true, + tabSize: 2, + trimAutoWhitespace: true + }, + rubyMode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model, autoIndent: true }, (editor, cursor) => { + moveTo(cursor, 4, 7, false); + assertCursor(cursor, new Selection(4, 7, 4, 7)); + + cursorCommand(cursor, H.Type, { text: 'd' }, 'keyboard'); + assert.equal(model.getLineContent(4), ' end'); + }); + + rubyMode.dispose(); + model.dispose(); + }); + + test('Auto indent on type: increaseIndentPattern has higher priority than decreaseIndent when inheriting', () => { + usingCursor({ + text: [ + '\tif (true) {', + '\t\tconsole.log();', + '\t} else if {', + '\t\tconsole.log()', + '\t}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 5, 3, false); + assertCursor(cursor, new Selection(5, 3, 5, 3)); + + cursorCommand(cursor, H.Type, { text: 'e' }, 'keyboard'); + assertCursor(cursor, new Selection(5, 4, 5, 4)); + assert.equal(model.getLineContent(5), '\t}e', 'This line should not decrease indent'); + }); + }); + + test('type honors users indentation adjustment', () => { + usingCursor({ + text: [ + '\tif (true ||', + '\t ) {', + '\t}', + 'if (true ||', + ') {', + '}' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true } + }, (model, cursor) => { + moveTo(cursor, 2, 3, false); + assertCursor(cursor, new Selection(2, 3, 2, 3)); + + cursorCommand(cursor, H.Type, { text: ' ' }, 'keyboard'); + assertCursor(cursor, new Selection(2, 4, 2, 4)); + assert.equal(model.getLineContent(2), '\t ) {', 'This line should not decrease indent'); + }); + }); + + test('bug 29972: if a line is line comment, open bracket should not indent next line', () => { + usingCursor({ + text: [ + 'if (true) {', + '\t// {', + '\t\t' + ], + languageIdentifier: mode.getLanguageIdentifier(), + modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true }, + editorOpts: { autoIndent: true } + }, (model, cursor) => { + moveTo(cursor, 3, 3, false); + assertCursor(cursor, new Selection(3, 3, 3, 3)); + + cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + assertCursor(cursor, new Selection(3, 2, 3, 2)); + assert.equal(model.getLineContent(3), '}'); + }); + }); +}); + +interface ICursorOpts { + text: string[]; + languageIdentifier?: LanguageIdentifier; + modelOpts?: ITextModelCreationOptions; + editorOpts?: IEditorOptions; +} + +function usingCursor(opts: ICursorOpts, callback: (model: Model, cursor: Cursor) => void): void { + let model = Model.createFromString(opts.text.join('\n'), opts.modelOpts, opts.languageIdentifier); + model.forceTokenization(model.getLineCount()); + let config = new TestConfiguration(opts.editorOpts); + let viewModel = new ViewModel(0, config, model, null); + let cursor = new Cursor(config, model, viewModel); + + callback(model, cursor); + + cursor.dispose(); + viewModel.dispose(); + config.dispose(); + model.dispose(); +} + +class ElectricCharMode extends MockMode { + + private static _id = new LanguageIdentifier('electricCharMode', 3); + + constructor() { + super(ElectricCharMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + __electricCharacterSupport: { + docComment: { open: '/**', close: ' */' } + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'] + ] + })); + } +} + +suite('ElectricCharacter', () => { + test('does nothing if no electric char', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + ' if (a) {', + '' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 2, 1); + cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + assert.deepEqual(model.getLineContent(2), '*'); + }); + mode.dispose(); + }); + + test('indents in order to match bracket', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + ' if (a) {', + '' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 2, 1); + cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + assert.deepEqual(model.getLineContent(2), ' }'); + }); + mode.dispose(); + }); + + test('unindents in order to match bracket', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + ' if (a) {', + ' ' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 2, 5); + cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + assert.deepEqual(model.getLineContent(2), ' }'); + }); + mode.dispose(); + }); + + test('matches with correct bracket', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + ' if (a) {', + ' if (b) {', + ' }', + ' ' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 4, 1); + cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + assert.deepEqual(model.getLineContent(4), ' } '); + }); + mode.dispose(); + }); + + test('does nothing if bracket does not match', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + ' if (a) {', + ' if (b) {', + ' }', + ' } ' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 4, 6); + cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + assert.deepEqual(model.getLineContent(4), ' } }'); + }); + mode.dispose(); + }); + + test('matches bracket even in line with content', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + ' if (a) {', + '// hello' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 2, 1); + cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + assert.deepEqual(model.getLineContent(2), ' }// hello'); + }); + mode.dispose(); + }); + + test('is no-op if bracket is lined up', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + ' if (a) {', + ' ' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 2, 3); + cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + assert.deepEqual(model.getLineContent(2), ' }'); + }); + mode.dispose(); + }); + + test('is no-op if there is non-whitespace text before', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + ' if (a) {', + 'a' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 2, 2); + cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + assert.deepEqual(model.getLineContent(2), 'a}'); + }); + mode.dispose(); + }); + + test('is no-op if pairs are all matched before', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + 'foo(() => {', + ' ( 1 + 2 ) ', + '})' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 2, 13); + cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + assert.deepEqual(model.getLineContent(2), ' ( 1 + 2 ) *'); + }); + mode.dispose(); + }); + + test('is no-op if matching bracket is on the same line', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + '(div', + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 1, 5); + let changeText: string = null; + model.onDidChangeContent(e => { + changeText = e.changes[0].text; + }); + cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + assert.deepEqual(model.getLineContent(1), '(div)'); + assert.deepEqual(changeText, ')'); + }); + mode.dispose(); + }); + + test('is no-op if the line has other content', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + 'Math.max(', + '\t2', + '\t3' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 3, 3); + cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + assert.deepEqual(model.getLineContent(3), '\t3)'); + }); + mode.dispose(); + }); + + test('appends text', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + ' if (a) {', + '/*' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 2, 3); + cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + assert.deepEqual(model.getLineContent(2), '/** */'); + }); + mode.dispose(); + }); + + test('appends text 2', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + ' if (a) {', + ' /*' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 2, 5); + cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + assert.deepEqual(model.getLineContent(2), ' /** */'); + }); + mode.dispose(); + }); + + test('issue #23711: Replacing selected text with )]} fails to delete old text with backwards-dragged selection', () => { + let mode = new ElectricCharMode(); + usingCursor({ + text: [ + '{', + 'word' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + moveTo(cursor, 2, 5); + moveTo(cursor, 2, 1, true); + cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + assert.deepEqual(model.getLineContent(2), '}'); + }); + mode.dispose(); + }); +}); + +suite('autoClosingPairs', () => { + + class AutoClosingMode extends MockMode { + + private static _id = new LanguageIdentifier('autoClosingMode', 5); + + constructor() { + super(AutoClosingMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '\'', close: '\'', notIn: ['string', 'comment'] }, + { open: '\"', close: '\"', notIn: ['string'] }, + { open: '`', close: '`', notIn: ['string', 'comment'] }, + { open: '/**', close: ' */', notIn: ['string'] } + ], + })); + } + } + + const enum ColumnType { + Normal = 0, + Special1 = 1, + Special2 = 2 + } + + function extractSpecialColumns(maxColumn: number, annotatedLine: string): ColumnType[] { + let result: ColumnType[] = []; + for (let j = 1; j <= maxColumn; j++) { + result[j] = ColumnType.Normal; + } + let column = 1; + for (let j = 0; j < annotatedLine.length; j++) { + if (annotatedLine.charAt(j) === '|') { + result[column] = ColumnType.Special1; + } else if (annotatedLine.charAt(j) === '!') { + result[column] = ColumnType.Special2; + } else { + column++; + } + } + return result; + } + + function assertType(model: Model, cursor: Cursor, lineNumber: number, column: number, chr: string, expectedInsert: string, message: string): void { + let lineContent = model.getLineContent(lineNumber); + let expected = lineContent.substr(0, column - 1) + expectedInsert + lineContent.substr(column - 1); + moveTo(cursor, lineNumber, column); + cursorCommand(cursor, H.Type, { text: chr }, 'keyboard'); + assert.deepEqual(model.getLineContent(lineNumber), expected, message); + cursorCommand(cursor, H.Undo); + } + + test('open parens', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var a = [];', + 'var b = `asd`;', + 'var c = \'asd\';', + 'var d = "asd";', + 'var e = /*3*/ 3;', + 'var f = /** 3 */3;', + 'var g = (3+5);', + 'var h = { a: \'value\' };', + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + + let autoClosePositions = [ + 'var| a| =| [|];|', + 'var| b| =| `asd`;|', + 'var| c| =| \'asd\';|', + 'var| d| =| "asd";|', + 'var| e| =| /*3*/| 3;|', + 'var| f| =| /**| 3| */3;|', + 'var| g| =| (3+5|);|', + 'var| h| =| {| a:| \'value\'| |};|', + ]; + for (let i = 0, len = autoClosePositions.length; i < len; i++) { + const lineNumber = i + 1; + const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); + + for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); + if (autoCloseColumns[column] === ColumnType.Special1) { + assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + } else { + assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + } + } + } + }); + mode.dispose(); + }); + + test('quote', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var a = [];', + 'var b = `asd`;', + 'var c = \'asd\';', + 'var d = "asd";', + 'var e = /*3*/ 3;', + 'var f = /** 3 */3;', + 'var g = (3+5);', + 'var h = { a: \'value\' };', + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + + let autoClosePositions = [ + 'var a =| [|];|', + 'var b =| |`asd`;|', + 'var c =| |\'asd!\';|', + 'var d =| |"asd";|', + 'var e =| /*3*/| 3;|', + 'var f =| /**| 3 */3;|', + 'var g =| (3+5);|', + 'var h =| {| a:| |\'value!\'| |};|', + ]; + for (let i = 0, len = autoClosePositions.length; i < len; i++) { + const lineNumber = i + 1; + const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); + + for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); + if (autoCloseColumns[column] === ColumnType.Special1) { + assertType(model, cursor, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`); + } else if (autoCloseColumns[column] === ColumnType.Special2) { + assertType(model, cursor, lineNumber, column, '\'', '', `over types @ (${lineNumber}, ${column})`); + } else { + assertType(model, cursor, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`); + } + } + } + }); + mode.dispose(); + }); + + test('issue #27937: Trying to add an item to the front of a list is cumbersome', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var arr = ["b", "c"];' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + assertType(model, cursor, 1, 12, '"', '""', `does not over type and will auto close`); + }); + mode.dispose(); + }); + + test('issue #25658 - Do not auto-close single/double quotes after word characters', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + '', + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + + function typeCharacters(cursor: Cursor, chars: string): void { + for (let i = 0, len = chars.length; i < len; i++) { + cursorCommand(cursor, H.Type, { text: chars[i] }, 'keyboard'); + } + } + + // First gif + model.forceTokenization(model.getLineCount()); + typeCharacters(cursor, 'teste1 = teste\' ok'); + assert.equal(model.getLineContent(1), 'teste1 = teste\' ok'); + + cursor.setSelections('test', [new Selection(1, 1000, 1, 1000)]); + typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); + typeCharacters(cursor, 'teste2 = teste \'ok'); + assert.equal(model.getLineContent(2), 'teste2 = teste \'ok\''); + + cursor.setSelections('test', [new Selection(2, 1000, 2, 1000)]); + typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); + typeCharacters(cursor, 'teste3 = teste" ok'); + assert.equal(model.getLineContent(3), 'teste3 = teste" ok'); + + cursor.setSelections('test', [new Selection(3, 1000, 3, 1000)]); + typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); + typeCharacters(cursor, 'teste4 = teste "ok'); + assert.equal(model.getLineContent(4), 'teste4 = teste "ok"'); + + // Second gif + cursor.setSelections('test', [new Selection(4, 1000, 4, 1000)]); + typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); + typeCharacters(cursor, 'teste \''); + assert.equal(model.getLineContent(5), 'teste \'\''); + + cursor.setSelections('test', [new Selection(5, 1000, 5, 1000)]); + typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); + typeCharacters(cursor, 'teste "'); + assert.equal(model.getLineContent(6), 'teste ""'); + + cursor.setSelections('test', [new Selection(6, 1000, 6, 1000)]); + typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); + typeCharacters(cursor, 'teste\''); + assert.equal(model.getLineContent(7), 'teste\''); + + cursor.setSelections('test', [new Selection(7, 1000, 7, 1000)]); + typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); + typeCharacters(cursor, 'teste"'); + assert.equal(model.getLineContent(8), 'teste"'); + }); + mode.dispose(); + }); + + test('issue #15825: accents on mac US intl keyboard', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + assertCursor(cursor, new Position(1, 1)); + + // Typing ` + e on the mac US intl kb layout + cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); + cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: 'è' }, 'keyboard'); + cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + + assert.equal(model.getValue(), 'è'); + }); + mode.dispose(); + }); + + test('issue #2773: Accents (´`¨^, others?) are inserted in the wrong position (Mac)', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'hello', + 'world' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + assertCursor(cursor, new Position(1, 1)); + + // Typing ` and pressing shift+down on the mac US intl kb layout + // Here we're just replaying what the cursor gets + cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); + cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + moveDown(cursor, true); + cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '`' }, 'keyboard'); + cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '`' }, 'keyboard'); + cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + + assert.equal(model.getValue(), '`hello\nworld'); + assertCursor(cursor, new Selection(1, 2, 2, 2)); + }); + mode.dispose(); + }); + + test('issue #20891: All cursors should do the same thing', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var a = asd' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + + cursor.setSelections('test', [ + new Selection(1, 9, 1, 9), + new Selection(1, 12, 1, 12), + ]); + + // type a ` + cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + + assert.equal(model.getValue(), 'var a = `asd`'); + }); + mode.dispose(); + }); + + test('All cursors should do the same thing when deleting left', () => { + let mode = new AutoClosingMode(); + let model = Model.createFromString( + [ + 'var a = ()' + ].join('\n'), + TextModel.DEFAULT_CREATION_OPTIONS, + mode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursor.setSelections('test', [ + new Selection(1, 4, 1, 4), + new Selection(1, 10, 1, 10), + ]); + + // delete left + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + + assert.equal(model.getValue(), 'va a = )'); + }); + model.dispose(); + mode.dispose(); + }); +}); diff --git a/src/vs/editor/test/common/controller/cursorMoveCommand.test.ts b/src/vs/editor/test/common/controller/cursorMoveCommand.test.ts new file mode 100644 index 0000000000..3a672aaa86 --- /dev/null +++ b/src/vs/editor/test/common/controller/cursorMoveCommand.test.ts @@ -0,0 +1,512 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Cursor } from 'vs/editor/common/controller/cursor'; +import { Position } from 'vs/editor/common/core/position'; +import { Model } from 'vs/editor/common/model/model'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import { CursorMove } from 'vs/editor/common/controller/cursorMoveCommands'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { CoreNavigationCommands } from 'vs/editor/common/controller/coreCommands'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; + +suite('Cursor move command test', () => { + + let thisModel: Model; + let thisConfiguration: TestConfiguration; + let thisViewModel: ViewModel; + let thisCursor: Cursor; + + setup(() => { + let text = [ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third Line🐶', + '', + '1' + ].join('\n'); + + thisModel = Model.createFromString(text); + thisConfiguration = new TestConfiguration(null); + thisViewModel = new ViewModel(0, thisConfiguration, thisModel, null); + thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel); + }); + + teardown(() => { + thisCursor.dispose(); + thisViewModel.dispose(); + thisModel.dispose(); + thisConfiguration.dispose(); + }); + + test('move left should move to left character', () => { + moveTo(thisCursor, 1, 8); + + moveLeft(thisCursor); + + cursorEqual(thisCursor, 1, 7); + }); + + test('move left should move to left by n characters', () => { + moveTo(thisCursor, 1, 8); + + moveLeft(thisCursor, 3); + + cursorEqual(thisCursor, 1, 5); + }); + + test('move left should move to left by half line', () => { + moveTo(thisCursor, 1, 8); + + moveLeft(thisCursor, 1, CursorMove.RawUnit.HalfLine); + + cursorEqual(thisCursor, 1, 1); + }); + + test('move left moves to previous line', () => { + moveTo(thisCursor, 2, 3); + + moveLeft(thisCursor, 10); + + cursorEqual(thisCursor, 1, 21); + }); + + test('move right should move to right character', () => { + moveTo(thisCursor, 1, 5); + + moveRight(thisCursor); + + cursorEqual(thisCursor, 1, 6); + }); + + test('move right should move to right by n characters', () => { + moveTo(thisCursor, 1, 2); + + moveRight(thisCursor, 6); + + cursorEqual(thisCursor, 1, 8); + }); + + test('move right should move to right by half line', () => { + moveTo(thisCursor, 1, 4); + + moveRight(thisCursor, 1, CursorMove.RawUnit.HalfLine); + + cursorEqual(thisCursor, 1, 14); + }); + + test('move right moves to next line', () => { + moveTo(thisCursor, 1, 8); + + moveRight(thisCursor, 100); + + cursorEqual(thisCursor, 2, 1); + }); + + test('move to first character of line from middle', () => { + moveTo(thisCursor, 1, 8); + moveToLineStart(thisCursor); + cursorEqual(thisCursor, 1, 1); + }); + + test('move to first character of line from first non white space character', () => { + moveTo(thisCursor, 1, 6); + + moveToLineStart(thisCursor); + + cursorEqual(thisCursor, 1, 1); + }); + + test('move to first character of line from first character', () => { + moveTo(thisCursor, 1, 1); + + moveToLineStart(thisCursor); + + cursorEqual(thisCursor, 1, 1); + }); + + test('move to first non white space character of line from middle', () => { + moveTo(thisCursor, 1, 8); + + moveToLineFirstNonWhiteSpaceCharacter(thisCursor); + + cursorEqual(thisCursor, 1, 6); + }); + + test('move to first non white space character of line from first non white space character', () => { + moveTo(thisCursor, 1, 6); + + moveToLineFirstNonWhiteSpaceCharacter(thisCursor); + + cursorEqual(thisCursor, 1, 6); + }); + + test('move to first non white space character of line from first character', () => { + moveTo(thisCursor, 1, 1); + + moveToLineFirstNonWhiteSpaceCharacter(thisCursor); + + cursorEqual(thisCursor, 1, 6); + }); + + test('move to end of line from middle', () => { + moveTo(thisCursor, 1, 8); + + moveToLineEnd(thisCursor); + + cursorEqual(thisCursor, 1, 21); + }); + + test('move to end of line from last non white space character', () => { + moveTo(thisCursor, 1, 19); + + moveToLineEnd(thisCursor); + + cursorEqual(thisCursor, 1, 21); + }); + + test('move to end of line from line end', () => { + moveTo(thisCursor, 1, 21); + + moveToLineEnd(thisCursor); + + cursorEqual(thisCursor, 1, 21); + }); + + test('move to last non white space character from middle', () => { + moveTo(thisCursor, 1, 8); + + moveToLineLastNonWhiteSpaceCharacter(thisCursor); + + cursorEqual(thisCursor, 1, 19); + }); + + test('move to last non white space character from last non white space character', () => { + moveTo(thisCursor, 1, 19); + + moveToLineLastNonWhiteSpaceCharacter(thisCursor); + + cursorEqual(thisCursor, 1, 19); + }); + + test('move to last non white space character from line end', () => { + moveTo(thisCursor, 1, 21); + + moveToLineLastNonWhiteSpaceCharacter(thisCursor); + + cursorEqual(thisCursor, 1, 19); + }); + + test('move to center of line not from center', () => { + moveTo(thisCursor, 1, 8); + + moveToLineCenter(thisCursor); + + cursorEqual(thisCursor, 1, 11); + }); + + test('move to center of line from center', () => { + moveTo(thisCursor, 1, 11); + + moveToLineCenter(thisCursor); + + cursorEqual(thisCursor, 1, 11); + }); + + test('move to center of line from start', () => { + moveToLineStart(thisCursor); + + moveToLineCenter(thisCursor); + + cursorEqual(thisCursor, 1, 11); + }); + + test('move to center of line from end', () => { + moveToLineEnd(thisCursor); + + moveToLineCenter(thisCursor); + + cursorEqual(thisCursor, 1, 11); + }); + + test('move up by cursor move command', () => { + + moveTo(thisCursor, 3, 5); + cursorEqual(thisCursor, 3, 5); + + moveUp(thisCursor, 2); + cursorEqual(thisCursor, 1, 5); + + moveUp(thisCursor, 1); + cursorEqual(thisCursor, 1, 1); + }); + + test('move up by model line cursor move command', () => { + + moveTo(thisCursor, 3, 5); + cursorEqual(thisCursor, 3, 5); + + moveUpByModelLine(thisCursor, 2); + cursorEqual(thisCursor, 1, 5); + + moveUpByModelLine(thisCursor, 1); + cursorEqual(thisCursor, 1, 1); + }); + + test('move down by model line cursor move command', () => { + + moveTo(thisCursor, 3, 5); + cursorEqual(thisCursor, 3, 5); + + moveDownByModelLine(thisCursor, 2); + cursorEqual(thisCursor, 5, 2); + + moveDownByModelLine(thisCursor, 1); + cursorEqual(thisCursor, 5, 2); + }); + + test('move up with selection by cursor move command', () => { + + moveTo(thisCursor, 3, 5); + cursorEqual(thisCursor, 3, 5); + + moveUp(thisCursor, 1, true); + cursorEqual(thisCursor, 2, 2, 3, 5); + + moveUp(thisCursor, 1, true); + cursorEqual(thisCursor, 1, 5, 3, 5); + }); + + test('move up and down with tabs by cursor move command', () => { + + moveTo(thisCursor, 1, 5); + cursorEqual(thisCursor, 1, 5); + + moveDown(thisCursor, 4); + cursorEqual(thisCursor, 5, 2); + + moveUp(thisCursor, 1); + cursorEqual(thisCursor, 4, 1); + + moveUp(thisCursor, 1); + cursorEqual(thisCursor, 3, 5); + + moveUp(thisCursor, 1); + cursorEqual(thisCursor, 2, 2); + + moveUp(thisCursor, 1); + cursorEqual(thisCursor, 1, 5); + }); + + test('move up and down with end of lines starting from a long one by cursor move command', () => { + + moveToEndOfLine(thisCursor); + cursorEqual(thisCursor, 1, 21); + + moveToEndOfLine(thisCursor); + cursorEqual(thisCursor, 1, 21); + + moveDown(thisCursor, 2); + cursorEqual(thisCursor, 3, 17); + + moveDown(thisCursor, 1); + cursorEqual(thisCursor, 4, 1); + + moveDown(thisCursor, 1); + cursorEqual(thisCursor, 5, 2); + + moveUp(thisCursor, 4); + cursorEqual(thisCursor, 1, 21); + }); + + test('move to view top line moves to first visible line if it is first line', () => { + thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1); + + moveTo(thisCursor, 2, 2); + moveToTop(thisCursor); + + cursorEqual(thisCursor, 1, 6); + }); + + test('move to view top line moves to top visible line when first line is not visible', () => { + thisViewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 10, 1); + + moveTo(thisCursor, 4, 1); + moveToTop(thisCursor); + + cursorEqual(thisCursor, 2, 2); + }); + + test('move to view top line moves to nth line from top', () => { + thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1); + + moveTo(thisCursor, 4, 1); + moveToTop(thisCursor, 3); + + cursorEqual(thisCursor, 3, 5); + }); + + test('move to view top line moves to last line if n is greater than last visible line number', () => { + thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 3, 1); + + moveTo(thisCursor, 2, 2); + moveToTop(thisCursor, 4); + + cursorEqual(thisCursor, 3, 5); + }); + + test('move to view center line moves to the center line', () => { + thisViewModel.getCompletelyVisibleViewRange = () => new Range(3, 1, 3, 1); + + moveTo(thisCursor, 2, 2); + moveToCenter(thisCursor); + + cursorEqual(thisCursor, 3, 5); + }); + + test('move to view bottom line moves to last visible line if it is last line', () => { + thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1); + + moveTo(thisCursor, 2, 2); + moveToBottom(thisCursor); + + cursorEqual(thisCursor, 5, 1); + }); + + test('move to view bottom line moves to last visible line when last line is not visible', () => { + thisViewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 3, 1); + + moveTo(thisCursor, 2, 2); + moveToBottom(thisCursor); + + cursorEqual(thisCursor, 3, 5); + }); + + test('move to view bottom line moves to nth line from bottom', () => { + thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1); + + moveTo(thisCursor, 4, 1); + moveToBottom(thisCursor, 3); + + cursorEqual(thisCursor, 3, 5); + }); + + test('move to view bottom line moves to first line if n is lesser than first visible line number', () => { + thisViewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 5, 1); + + moveTo(thisCursor, 4, 1); + moveToBottom(thisCursor, 5); + + cursorEqual(thisCursor, 2, 2); + }); +}); + +// Move command + +function move(cursor: Cursor, args: any) { + CoreNavigationCommands.CursorMove.runCoreEditorCommand(cursor, args); +} + +function moveToLineStart(cursor: Cursor) { + move(cursor, { to: CursorMove.RawDirection.WrappedLineStart }); +} + +function moveToLineFirstNonWhiteSpaceCharacter(cursor: Cursor) { + move(cursor, { to: CursorMove.RawDirection.WrappedLineFirstNonWhitespaceCharacter }); +} + +function moveToLineCenter(cursor: Cursor) { + move(cursor, { to: CursorMove.RawDirection.WrappedLineColumnCenter }); +} + +function moveToLineEnd(cursor: Cursor) { + move(cursor, { to: CursorMove.RawDirection.WrappedLineEnd }); +} + +function moveToLineLastNonWhiteSpaceCharacter(cursor: Cursor) { + move(cursor, { to: CursorMove.RawDirection.WrappedLineLastNonWhitespaceCharacter }); +} + +function moveLeft(cursor: Cursor, value?: number, by?: string, select?: boolean) { + move(cursor, { to: CursorMove.RawDirection.Left, by: by, value: value, select: select }); +} + +function moveRight(cursor: Cursor, value?: number, by?: string, select?: boolean) { + move(cursor, { to: CursorMove.RawDirection.Right, by: by, value: value, select: select }); +} + +function moveUp(cursor: Cursor, noOfLines: number = 1, select?: boolean) { + move(cursor, { to: CursorMove.RawDirection.Up, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select }); +} + +function moveUpByModelLine(cursor: Cursor, noOfLines: number = 1, select?: boolean) { + move(cursor, { to: CursorMove.RawDirection.Up, value: noOfLines, select: select }); +} + +function moveDown(cursor: Cursor, noOfLines: number = 1, select?: boolean) { + move(cursor, { to: CursorMove.RawDirection.Down, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select }); +} + +function moveDownByModelLine(cursor: Cursor, noOfLines: number = 1, select?: boolean) { + move(cursor, { to: CursorMove.RawDirection.Down, value: noOfLines, select: select }); +} + +function moveToTop(cursor: Cursor, noOfLines: number = 1, select?: boolean) { + move(cursor, { to: CursorMove.RawDirection.ViewPortTop, value: noOfLines, select: select }); +} + +function moveToCenter(cursor: Cursor, select?: boolean) { + move(cursor, { to: CursorMove.RawDirection.ViewPortCenter, select: select }); +} + +function moveToBottom(cursor: Cursor, noOfLines: number = 1, select?: boolean) { + move(cursor, { to: CursorMove.RawDirection.ViewPortBottom, value: noOfLines, select: select }); +} + +function cursorEqual(cursor: Cursor, posLineNumber: number, posColumn: number, selLineNumber: number = posLineNumber, selColumn: number = posColumn) { + positionEqual(cursor.getPosition(), posLineNumber, posColumn); + selectionEqual(cursor.getSelection(), posLineNumber, posColumn, selLineNumber, selColumn); +} + +function positionEqual(position: Position, lineNumber: number, column: number) { + assert.deepEqual(position, new Position(lineNumber, column), 'position equal'); +} + +function selectionEqual(selection: Selection, posLineNumber: number, posColumn: number, selLineNumber: number, selColumn: number) { + assert.deepEqual({ + selectionStartLineNumber: selection.selectionStartLineNumber, + selectionStartColumn: selection.selectionStartColumn, + positionLineNumber: selection.positionLineNumber, + positionColumn: selection.positionColumn + }, { + selectionStartLineNumber: selLineNumber, + selectionStartColumn: selColumn, + positionLineNumber: posLineNumber, + positionColumn: posColumn + }, 'selection equal'); +} + +function moveTo(cursor: Cursor, lineNumber: number, column: number, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(cursor, { + position: new Position(lineNumber, column) + }); + } else { + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { + position: new Position(lineNumber, column) + }); + } +} + +function moveToEndOfLine(cursor: Cursor, inSelectionMode: boolean = false) { + if (inSelectionMode) { + CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(cursor, {}); + } else { + CoreNavigationCommands.CursorEnd.runCoreEditorCommand(cursor, {}); + } +} diff --git a/src/vs/editor/test/common/controller/cursorMoveHelper.test.ts b/src/vs/editor/test/common/controller/cursorMoveHelper.test.ts new file mode 100644 index 0000000000..86467d78d3 --- /dev/null +++ b/src/vs/editor/test/common/controller/cursorMoveHelper.test.ts @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; + +suite('CursorMove', () => { + + test('nextTabStop', () => { + assert.equal(CursorColumns.nextTabStop(0, 4), 4); + assert.equal(CursorColumns.nextTabStop(1, 4), 4); + assert.equal(CursorColumns.nextTabStop(2, 4), 4); + assert.equal(CursorColumns.nextTabStop(3, 4), 4); + assert.equal(CursorColumns.nextTabStop(4, 4), 8); + assert.equal(CursorColumns.nextTabStop(5, 4), 8); + assert.equal(CursorColumns.nextTabStop(6, 4), 8); + assert.equal(CursorColumns.nextTabStop(7, 4), 8); + assert.equal(CursorColumns.nextTabStop(8, 4), 12); + + assert.equal(CursorColumns.nextTabStop(0, 2), 2); + assert.equal(CursorColumns.nextTabStop(1, 2), 2); + assert.equal(CursorColumns.nextTabStop(2, 2), 4); + assert.equal(CursorColumns.nextTabStop(3, 2), 4); + assert.equal(CursorColumns.nextTabStop(4, 2), 6); + assert.equal(CursorColumns.nextTabStop(5, 2), 6); + assert.equal(CursorColumns.nextTabStop(6, 2), 8); + assert.equal(CursorColumns.nextTabStop(7, 2), 8); + assert.equal(CursorColumns.nextTabStop(8, 2), 10); + + assert.equal(CursorColumns.nextTabStop(0, 1), 1); + assert.equal(CursorColumns.nextTabStop(1, 1), 2); + assert.equal(CursorColumns.nextTabStop(2, 1), 3); + assert.equal(CursorColumns.nextTabStop(3, 1), 4); + assert.equal(CursorColumns.nextTabStop(4, 1), 5); + assert.equal(CursorColumns.nextTabStop(5, 1), 6); + assert.equal(CursorColumns.nextTabStop(6, 1), 7); + assert.equal(CursorColumns.nextTabStop(7, 1), 8); + assert.equal(CursorColumns.nextTabStop(8, 1), 9); + }); + + test('visibleColumnFromColumn', () => { + + function testVisibleColumnFromColumn(text: string, tabSize: number, column: number, expected: number): void { + assert.equal(CursorColumns.visibleColumnFromColumn(text, column, tabSize), expected); + } + + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 1, 0); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 2, 4); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 3, 8); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 4, 9); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 5, 10); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 6, 11); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 7, 12); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 8, 13); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 9, 14); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 10, 15); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 11, 16); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 12, 17); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 13, 18); + + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 1, 0); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 2, 4); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 3, 5); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 4, 8); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 5, 9); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 6, 10); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 7, 11); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 8, 12); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 9, 13); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 10, 14); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 11, 15); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 12, 16); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 13, 17); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 14, 18); + + testVisibleColumnFromColumn('\t \tx\t', 4, -1, 0); + testVisibleColumnFromColumn('\t \tx\t', 4, 0, 0); + testVisibleColumnFromColumn('\t \tx\t', 4, 1, 0); + testVisibleColumnFromColumn('\t \tx\t', 4, 2, 4); + testVisibleColumnFromColumn('\t \tx\t', 4, 3, 5); + testVisibleColumnFromColumn('\t \tx\t', 4, 4, 6); + testVisibleColumnFromColumn('\t \tx\t', 4, 5, 8); + testVisibleColumnFromColumn('\t \tx\t', 4, 6, 9); + testVisibleColumnFromColumn('\t \tx\t', 4, 7, 12); + testVisibleColumnFromColumn('\t \tx\t', 4, 8, 12); + testVisibleColumnFromColumn('\t \tx\t', 4, 9, 12); + + testVisibleColumnFromColumn('baz', 4, 1, 0); + testVisibleColumnFromColumn('baz', 4, 2, 1); + testVisibleColumnFromColumn('baz', 4, 3, 2); + testVisibleColumnFromColumn('baz', 4, 4, 3); + + testVisibleColumnFromColumn('📚az', 4, 1, 0); + testVisibleColumnFromColumn('📚az', 4, 2, 1); + testVisibleColumnFromColumn('📚az', 4, 3, 2); + testVisibleColumnFromColumn('📚az', 4, 4, 3); + testVisibleColumnFromColumn('📚az', 4, 5, 4); + }); + + test('columnFromVisibleColumn', () => { + + function testColumnFromVisibleColumn(text: string, tabSize: number, visibleColumn: number, expected: number): void { + assert.equal(CursorColumns.columnFromVisibleColumn(text, visibleColumn, tabSize), expected); + } + + // testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 0, 1); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 1, 1); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 2, 1); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 3, 2); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 4, 2); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 5, 2); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 6, 2); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 7, 3); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 8, 3); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 9, 4); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 10, 5); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 11, 6); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 12, 7); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 13, 8); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 14, 9); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 15, 10); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 16, 11); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 17, 12); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 18, 13); + + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 0, 1); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 1, 1); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 2, 1); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 3, 2); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 4, 2); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 5, 3); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 6, 3); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 7, 4); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 8, 4); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 9, 5); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 10, 6); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 11, 7); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 12, 8); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 13, 9); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 14, 10); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 15, 11); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 16, 12); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 17, 13); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 18, 14); + + testColumnFromVisibleColumn('\t \tx\t', 4, -2, 1); + testColumnFromVisibleColumn('\t \tx\t', 4, -1, 1); + testColumnFromVisibleColumn('\t \tx\t', 4, 0, 1); + testColumnFromVisibleColumn('\t \tx\t', 4, 1, 1); + testColumnFromVisibleColumn('\t \tx\t', 4, 2, 1); + testColumnFromVisibleColumn('\t \tx\t', 4, 3, 2); + testColumnFromVisibleColumn('\t \tx\t', 4, 4, 2); + testColumnFromVisibleColumn('\t \tx\t', 4, 5, 3); + testColumnFromVisibleColumn('\t \tx\t', 4, 6, 4); + testColumnFromVisibleColumn('\t \tx\t', 4, 7, 4); + testColumnFromVisibleColumn('\t \tx\t', 4, 8, 5); + testColumnFromVisibleColumn('\t \tx\t', 4, 9, 6); + testColumnFromVisibleColumn('\t \tx\t', 4, 10, 6); + testColumnFromVisibleColumn('\t \tx\t', 4, 11, 7); + testColumnFromVisibleColumn('\t \tx\t', 4, 12, 7); + testColumnFromVisibleColumn('\t \tx\t', 4, 13, 7); + testColumnFromVisibleColumn('\t \tx\t', 4, 14, 7); + + testColumnFromVisibleColumn('baz', 4, 0, 1); + testColumnFromVisibleColumn('baz', 4, 1, 2); + testColumnFromVisibleColumn('baz', 4, 2, 3); + testColumnFromVisibleColumn('baz', 4, 3, 4); + + testColumnFromVisibleColumn('📚az', 4, 0, 1); + testColumnFromVisibleColumn('📚az', 4, 1, 2); + testColumnFromVisibleColumn('📚az', 4, 2, 3); + testColumnFromVisibleColumn('📚az', 4, 3, 4); + testColumnFromVisibleColumn('📚az', 4, 4, 5); + }); +}); \ No newline at end of file diff --git a/src/vs/editor/test/common/core/characterClassifier.test.ts b/src/vs/editor/test/common/core/characterClassifier.test.ts new file mode 100644 index 0000000000..3c8e10723c --- /dev/null +++ b/src/vs/editor/test/common/core/characterClassifier.test.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; +import { CharCode } from 'vs/base/common/charCode'; + +suite('CharacterClassifier', () => { + + test('works', () => { + let classifier = new CharacterClassifier(0); + + assert.equal(classifier.get(-1), 0); + assert.equal(classifier.get(0), 0); + assert.equal(classifier.get(CharCode.a), 0); + assert.equal(classifier.get(CharCode.b), 0); + assert.equal(classifier.get(CharCode.z), 0); + assert.equal(classifier.get(255), 0); + assert.equal(classifier.get(1000), 0); + assert.equal(classifier.get(2000), 0); + + classifier.set(CharCode.a, 1); + classifier.set(CharCode.z, 2); + classifier.set(1000, 3); + + assert.equal(classifier.get(-1), 0); + assert.equal(classifier.get(0), 0); + assert.equal(classifier.get(CharCode.a), 1); + assert.equal(classifier.get(CharCode.b), 0); + assert.equal(classifier.get(CharCode.z), 2); + assert.equal(classifier.get(255), 0); + assert.equal(classifier.get(1000), 3); + assert.equal(classifier.get(2000), 0); + }); + +}); \ No newline at end of file diff --git a/src/vs/editor/test/common/core/editorState.test.ts b/src/vs/editor/test/common/core/editorState.test.ts new file mode 100644 index 0000000000..ccdba5d696 --- /dev/null +++ b/src/vs/editor/test/common/core/editorState.test.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { ICommonCodeEditor, IModel } from 'vs/editor/common/editorCommon'; +import { EditorState, CodeEditorStateFlag } from 'vs/editor/common/core/editorState'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; + +interface IStubEditorState { + model?: { uri?: URI, version?: number }; + position?: Position; + selection?: Selection; + scroll?: { left?: number, top?: number }; +} + +suite('Editor Core - Editor State', () => { + + const allFlags = ( + CodeEditorStateFlag.Value + | CodeEditorStateFlag.Selection + | CodeEditorStateFlag.Position + | CodeEditorStateFlag.Scroll + ); + + test('empty editor state should be valid', () => { + let result = validate({}, {}); + assert.equal(result, true); + }); + + test('different model URIs should be invalid', () => { + let result = validate( + { model: { uri: URI.parse('http://test1') } }, + { model: { uri: URI.parse('http://test2') } } + ); + + assert.equal(result, false); + }); + + test('different model versions should be invalid', () => { + let result = validate( + { model: { version: 1 } }, + { model: { version: 2 } } + ); + + assert.equal(result, false); + }); + + test('different positions should be invalid', () => { + let result = validate( + { position: new Position(1, 2) }, + { position: new Position(2, 3) } + ); + + assert.equal(result, false); + }); + + test('different selections should be invalid', () => { + let result = validate( + { selection: new Selection(1, 2, 3, 4) }, + { selection: new Selection(5, 2, 3, 4) } + ); + + assert.equal(result, false); + }); + + test('different scroll positions should be invalid', () => { + let result = validate( + { scroll: { left: 1, top: 2 } }, + { scroll: { left: 3, top: 2 } } + ); + + assert.equal(result, false); + }); + + + function validate(source: IStubEditorState, target: IStubEditorState) { + let sourceEditor = createEditor(source), + targetEditor = createEditor(target); + + let result = new EditorState(sourceEditor, allFlags).validate(targetEditor); + + return result; + } + + function createEditor({ model, position, selection, scroll }: IStubEditorState = {}): ICommonCodeEditor { + let mappedModel = model ? { uri: model.uri ? model.uri : URI.parse('http://dummy.org'), getVersionId: () => model.version } : null; + + return { + getModel: (): IModel => mappedModel, + getPosition: (): Position => position, + getSelection: (): Selection => selection, + getScrollLeft: (): number => scroll && scroll.left, + getScrollTop: (): number => scroll && scroll.top + }; + } + +}); + diff --git a/src/vs/editor/test/common/core/lineTokens.test.ts b/src/vs/editor/test/common/core/lineTokens.test.ts new file mode 100644 index 0000000000..1e773c1076 --- /dev/null +++ b/src/vs/editor/test/common/core/lineTokens.test.ts @@ -0,0 +1,292 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { MetadataConsts } from 'vs/editor/common/modes'; +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; + +suite('LineTokens', () => { + + interface ILineToken { + startIndex: number; + foreground: number; + } + + function createLineTokens(text: string, tokens: ILineToken[]): LineTokens { + let binTokens = new Uint32Array(tokens.length << 1); + + for (let i = 0, len = tokens.length; i < len; i++) { + let token = tokens[i]; + binTokens[(i << 1)] = token.startIndex; + binTokens[(i << 1) + 1] = ( + token.foreground << MetadataConsts.FOREGROUND_OFFSET + ) >>> 0; + } + + return new LineTokens(binTokens, text); + } + + function createTestLineTokens(): LineTokens { + return createLineTokens( + 'Hello world, this is a lovely day', + [ + { startIndex: 0, foreground: 1 }, // Hello_ + { startIndex: 6, foreground: 2 }, // world,_ + { startIndex: 13, foreground: 3 }, // this_ + { startIndex: 18, foreground: 4 }, // is_ + { startIndex: 21, foreground: 5 }, // a_ + { startIndex: 23, foreground: 6 }, // lovely_ + { startIndex: 30, foreground: 7 }, // day + ] + ); + } + + test('basics', () => { + const lineTokens = createTestLineTokens(); + + assert.equal(lineTokens.getLineContent(), 'Hello world, this is a lovely day'); + assert.equal(lineTokens.getLineLength(), 33); + assert.equal(lineTokens.getTokenCount(), 7); + + assert.equal(lineTokens.getTokenStartOffset(0), 0); + assert.equal(lineTokens.getTokenEndOffset(0), 6); + assert.equal(lineTokens.getTokenStartOffset(1), 6); + assert.equal(lineTokens.getTokenEndOffset(1), 13); + assert.equal(lineTokens.getTokenStartOffset(2), 13); + assert.equal(lineTokens.getTokenEndOffset(2), 18); + assert.equal(lineTokens.getTokenStartOffset(3), 18); + assert.equal(lineTokens.getTokenEndOffset(3), 21); + assert.equal(lineTokens.getTokenStartOffset(4), 21); + assert.equal(lineTokens.getTokenEndOffset(4), 23); + assert.equal(lineTokens.getTokenStartOffset(5), 23); + assert.equal(lineTokens.getTokenEndOffset(5), 30); + assert.equal(lineTokens.getTokenStartOffset(6), 30); + assert.equal(lineTokens.getTokenEndOffset(6), 33); + }); + + test('findToken', () => { + const lineTokens = createTestLineTokens(); + + assert.equal(lineTokens.findTokenIndexAtOffset(0), 0); + assert.equal(lineTokens.findTokenIndexAtOffset(1), 0); + assert.equal(lineTokens.findTokenIndexAtOffset(2), 0); + assert.equal(lineTokens.findTokenIndexAtOffset(3), 0); + assert.equal(lineTokens.findTokenIndexAtOffset(4), 0); + assert.equal(lineTokens.findTokenIndexAtOffset(5), 0); + assert.equal(lineTokens.findTokenIndexAtOffset(6), 1); + assert.equal(lineTokens.findTokenIndexAtOffset(7), 1); + assert.equal(lineTokens.findTokenIndexAtOffset(8), 1); + assert.equal(lineTokens.findTokenIndexAtOffset(9), 1); + assert.equal(lineTokens.findTokenIndexAtOffset(10), 1); + assert.equal(lineTokens.findTokenIndexAtOffset(11), 1); + assert.equal(lineTokens.findTokenIndexAtOffset(12), 1); + assert.equal(lineTokens.findTokenIndexAtOffset(13), 2); + assert.equal(lineTokens.findTokenIndexAtOffset(14), 2); + assert.equal(lineTokens.findTokenIndexAtOffset(15), 2); + assert.equal(lineTokens.findTokenIndexAtOffset(16), 2); + assert.equal(lineTokens.findTokenIndexAtOffset(17), 2); + assert.equal(lineTokens.findTokenIndexAtOffset(18), 3); + assert.equal(lineTokens.findTokenIndexAtOffset(19), 3); + assert.equal(lineTokens.findTokenIndexAtOffset(20), 3); + assert.equal(lineTokens.findTokenIndexAtOffset(21), 4); + assert.equal(lineTokens.findTokenIndexAtOffset(22), 4); + assert.equal(lineTokens.findTokenIndexAtOffset(23), 5); + assert.equal(lineTokens.findTokenIndexAtOffset(24), 5); + assert.equal(lineTokens.findTokenIndexAtOffset(25), 5); + assert.equal(lineTokens.findTokenIndexAtOffset(26), 5); + assert.equal(lineTokens.findTokenIndexAtOffset(27), 5); + assert.equal(lineTokens.findTokenIndexAtOffset(28), 5); + assert.equal(lineTokens.findTokenIndexAtOffset(29), 5); + assert.equal(lineTokens.findTokenIndexAtOffset(30), 6); + assert.equal(lineTokens.findTokenIndexAtOffset(31), 6); + assert.equal(lineTokens.findTokenIndexAtOffset(32), 6); + assert.equal(lineTokens.findTokenIndexAtOffset(33), 6); + assert.equal(lineTokens.findTokenIndexAtOffset(34), 6); + + assert.equal(lineTokens.findTokenAtOffset(7).startOffset, 6); + assert.equal(lineTokens.findTokenAtOffset(7).endOffset, 13); + assert.equal(lineTokens.findTokenAtOffset(7).foregroundId, 2); + + assert.equal(lineTokens.findTokenAtOffset(30).startOffset, 30); + assert.equal(lineTokens.findTokenAtOffset(30).endOffset, 33); + assert.equal(lineTokens.findTokenAtOffset(30).foregroundId, 7); + }); + + test('iterate forward', () => { + const lineTokens = createTestLineTokens(); + + let token = lineTokens.firstToken(); + assert.equal(token.startOffset, 0); + assert.equal(token.endOffset, 6); + assert.equal(token.foregroundId, 1); + + token = token.next(); + assert.equal(token.startOffset, 6); + assert.equal(token.endOffset, 13); + assert.equal(token.foregroundId, 2); + + token = token.next(); + assert.equal(token.startOffset, 13); + assert.equal(token.endOffset, 18); + assert.equal(token.foregroundId, 3); + + token = token.next(); + assert.equal(token.startOffset, 18); + assert.equal(token.endOffset, 21); + assert.equal(token.foregroundId, 4); + + token = token.next(); + assert.equal(token.startOffset, 21); + assert.equal(token.endOffset, 23); + assert.equal(token.foregroundId, 5); + + token = token.next(); + assert.equal(token.startOffset, 23); + assert.equal(token.endOffset, 30); + assert.equal(token.foregroundId, 6); + + token = token.next(); + assert.equal(token.startOffset, 30); + assert.equal(token.endOffset, 33); + assert.equal(token.foregroundId, 7); + + token = token.next(); + assert.equal(token, null); + }); + + test('iterate backward', () => { + const lineTokens = createTestLineTokens(); + + let token = lineTokens.lastToken(); + assert.equal(token.startOffset, 30); + assert.equal(token.endOffset, 33); + assert.equal(token.foregroundId, 7); + + token = token.prev(); + assert.equal(token.startOffset, 23); + assert.equal(token.endOffset, 30); + assert.equal(token.foregroundId, 6); + + token = token.prev(); + assert.equal(token.startOffset, 21); + assert.equal(token.endOffset, 23); + assert.equal(token.foregroundId, 5); + + token = token.prev(); + assert.equal(token.startOffset, 18); + assert.equal(token.endOffset, 21); + assert.equal(token.foregroundId, 4); + + token = token.prev(); + assert.equal(token.startOffset, 13); + assert.equal(token.endOffset, 18); + assert.equal(token.foregroundId, 3); + + token = token.prev(); + assert.equal(token.startOffset, 6); + assert.equal(token.endOffset, 13); + assert.equal(token.foregroundId, 2); + + token = token.prev(); + assert.equal(token.startOffset, 0); + assert.equal(token.endOffset, 6); + assert.equal(token.foregroundId, 1); + + token = token.prev(); + assert.equal(token, null); + }); + + interface ITestViewLineToken { + endIndex: number; + foreground: number; + } + + function assertViewLineTokens(actual: ViewLineToken[], expected: ITestViewLineToken[]): void { + assert.deepEqual(actual.map(token => { + return { + endIndex: token.endIndex, + foreground: token.getForeground() + }; + }), expected); + } + + test('inflate', () => { + const lineTokens = createTestLineTokens(); + assertViewLineTokens(lineTokens.inflate(), [ + { endIndex: 6, foreground: 1 }, + { endIndex: 13, foreground: 2 }, + { endIndex: 18, foreground: 3 }, + { endIndex: 21, foreground: 4 }, + { endIndex: 23, foreground: 5 }, + { endIndex: 30, foreground: 6 }, + { endIndex: 33, foreground: 7 }, + ]); + }); + + test('sliceAndInflate', () => { + const lineTokens = createTestLineTokens(); + assertViewLineTokens(lineTokens.sliceAndInflate(0, 33, 0), [ + { endIndex: 6, foreground: 1 }, + { endIndex: 13, foreground: 2 }, + { endIndex: 18, foreground: 3 }, + { endIndex: 21, foreground: 4 }, + { endIndex: 23, foreground: 5 }, + { endIndex: 30, foreground: 6 }, + { endIndex: 33, foreground: 7 }, + ]); + + assertViewLineTokens(lineTokens.sliceAndInflate(0, 32, 0), [ + { endIndex: 6, foreground: 1 }, + { endIndex: 13, foreground: 2 }, + { endIndex: 18, foreground: 3 }, + { endIndex: 21, foreground: 4 }, + { endIndex: 23, foreground: 5 }, + { endIndex: 30, foreground: 6 }, + { endIndex: 32, foreground: 7 }, + ]); + + assertViewLineTokens(lineTokens.sliceAndInflate(0, 30, 0), [ + { endIndex: 6, foreground: 1 }, + { endIndex: 13, foreground: 2 }, + { endIndex: 18, foreground: 3 }, + { endIndex: 21, foreground: 4 }, + { endIndex: 23, foreground: 5 }, + { endIndex: 30, foreground: 6 } + ]); + + assertViewLineTokens(lineTokens.sliceAndInflate(0, 30, 1), [ + { endIndex: 7, foreground: 1 }, + { endIndex: 14, foreground: 2 }, + { endIndex: 19, foreground: 3 }, + { endIndex: 22, foreground: 4 }, + { endIndex: 24, foreground: 5 }, + { endIndex: 31, foreground: 6 } + ]); + + assertViewLineTokens(lineTokens.sliceAndInflate(6, 18, 0), [ + { endIndex: 7, foreground: 2 }, + { endIndex: 12, foreground: 3 } + ]); + + assertViewLineTokens(lineTokens.sliceAndInflate(7, 18, 0), [ + { endIndex: 6, foreground: 2 }, + { endIndex: 11, foreground: 3 } + ]); + + assertViewLineTokens(lineTokens.sliceAndInflate(6, 17, 0), [ + { endIndex: 7, foreground: 2 }, + { endIndex: 11, foreground: 3 } + ]); + + assertViewLineTokens(lineTokens.sliceAndInflate(6, 19, 0), [ + { endIndex: 7, foreground: 2 }, + { endIndex: 12, foreground: 3 }, + { endIndex: 13, foreground: 4 }, + ]); + }); +}); diff --git a/src/vs/editor/test/common/core/range.test.ts b/src/vs/editor/test/common/core/range.test.ts new file mode 100644 index 0000000000..d697b1c78a --- /dev/null +++ b/src/vs/editor/test/common/core/range.test.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; + +suite('Editor Core - Range', () => { + test('empty range', () => { + var s = new Range(1, 1, 1, 1); + assert.equal(s.startLineNumber, 1); + assert.equal(s.startColumn, 1); + assert.equal(s.endLineNumber, 1); + assert.equal(s.endColumn, 1); + assert.equal(s.isEmpty(), true); + }); + + test('swap start and stop same line', () => { + var s = new Range(1, 2, 1, 1); + assert.equal(s.startLineNumber, 1); + assert.equal(s.startColumn, 1); + assert.equal(s.endLineNumber, 1); + assert.equal(s.endColumn, 2); + assert.equal(s.isEmpty(), false); + }); + + test('swap start and stop', () => { + var s = new Range(2, 1, 1, 2); + assert.equal(s.startLineNumber, 1); + assert.equal(s.startColumn, 2); + assert.equal(s.endLineNumber, 2); + assert.equal(s.endColumn, 1); + assert.equal(s.isEmpty(), false); + }); + + test('no swap same line', () => { + var s = new Range(1, 1, 1, 2); + assert.equal(s.startLineNumber, 1); + assert.equal(s.startColumn, 1); + assert.equal(s.endLineNumber, 1); + assert.equal(s.endColumn, 2); + assert.equal(s.isEmpty(), false); + }); + + test('no swap', () => { + var s = new Range(1, 1, 2, 1); + assert.equal(s.startLineNumber, 1); + assert.equal(s.startColumn, 1); + assert.equal(s.endLineNumber, 2); + assert.equal(s.endColumn, 1); + assert.equal(s.isEmpty(), false); + }); + + test('compareRangesUsingEnds', () => { + var a, b; + + a = new Range(1, 1, 1, 3); + b = new Range(1, 2, 1, 4); + assert.ok(Range.compareRangesUsingEnds(a, b) < 0, 'a.start < b.start, a.end < b.end'); + + a = new Range(1, 1, 1, 3); + b = new Range(1, 1, 1, 4); + assert.ok(Range.compareRangesUsingEnds(a, b) < 0, 'a.start = b.start, a.end < b.end'); + + a = new Range(1, 2, 1, 3); + b = new Range(1, 1, 1, 4); + assert.ok(Range.compareRangesUsingEnds(a, b) < 0, 'a.start > b.start, a.end < b.end'); + + a = new Range(1, 1, 1, 4); + b = new Range(1, 2, 1, 4); + assert.ok(Range.compareRangesUsingEnds(a, b) < 0, 'a.start < b.start, a.end = b.end'); + + a = new Range(1, 1, 1, 4); + b = new Range(1, 1, 1, 4); + assert.ok(Range.compareRangesUsingEnds(a, b) === 0, 'a.start = b.start, a.end = b.end'); + + a = new Range(1, 2, 1, 4); + b = new Range(1, 1, 1, 4); + assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start > b.start, a.end = b.end'); + + a = new Range(1, 1, 1, 5); + b = new Range(1, 2, 1, 4); + assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start < b.start, a.end > b.end'); + + a = new Range(1, 1, 2, 4); + b = new Range(1, 1, 1, 4); + assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start = b.start, a.end > b.end'); + + a = new Range(1, 1, 5, 1); + b = new Range(1, 1, 1, 4); + assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start = b.start, a.end > b.end'); + }); + + test('containsPosition', () => { + assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(1, 3)), false); + assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(2, 1)), false); + assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(2, 2)), true); + assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(2, 3)), true); + assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(3, 1)), true); + assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(5, 9)), true); + assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(5, 10)), true); + assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(5, 11)), false); + assert.equal(new Range(2, 2, 5, 10).containsPosition(new Position(6, 1)), false); + }); + + test('containsRange', () => { + assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(1, 3, 2, 2)), false); + assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(2, 1, 2, 2)), false); + assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(2, 2, 5, 11)), false); + assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(2, 2, 6, 1)), false); + assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(5, 9, 6, 1)), false); + assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(5, 10, 6, 1)), false); + assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(2, 2, 5, 10)), true); + assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(2, 3, 5, 9)), true); + assert.equal(new Range(2, 2, 5, 10).containsRange(new Range(3, 100, 4, 100)), true); + }); +}); diff --git a/src/vs/editor/test/common/diff/diffComputer.test.ts b/src/vs/editor/test/common/diff/diffComputer.test.ts new file mode 100644 index 0000000000..1873577d45 --- /dev/null +++ b/src/vs/editor/test/common/diff/diffComputer.test.ts @@ -0,0 +1,677 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; +import { IChange, ICharChange, ILineChange } from 'vs/editor/common/editorCommon'; + +function extractCharChangeRepresentation(change: ICharChange, expectedChange: ICharChange): ICharChange { + var hasOriginal = expectedChange && expectedChange.originalStartLineNumber > 0; + var hasModified = expectedChange && expectedChange.modifiedStartLineNumber > 0; + return { + originalStartLineNumber: hasOriginal ? change.originalStartLineNumber : 0, + originalStartColumn: hasOriginal ? change.originalStartColumn : 0, + originalEndLineNumber: hasOriginal ? change.originalEndLineNumber : 0, + originalEndColumn: hasOriginal ? change.originalEndColumn : 0, + + modifiedStartLineNumber: hasModified ? change.modifiedStartLineNumber : 0, + modifiedStartColumn: hasModified ? change.modifiedStartColumn : 0, + modifiedEndLineNumber: hasModified ? change.modifiedEndLineNumber : 0, + modifiedEndColumn: hasModified ? change.modifiedEndColumn : 0, + }; +} + +function extractLineChangeRepresentation(change: ILineChange, expectedChange: ILineChange): IChange | ILineChange { + if (change.charChanges) { + let charChanges: ICharChange[] = []; + for (let i = 0; i < change.charChanges.length; i++) { + charChanges.push( + extractCharChangeRepresentation( + change.charChanges[i], + expectedChange && expectedChange.charChanges && i < expectedChange.charChanges.length ? expectedChange.charChanges[i] : null + ) + ); + } + return { + originalStartLineNumber: change.originalStartLineNumber, + originalEndLineNumber: change.originalEndLineNumber, + modifiedStartLineNumber: change.modifiedStartLineNumber, + modifiedEndLineNumber: change.modifiedEndLineNumber, + charChanges: charChanges + }; + } + return { + originalStartLineNumber: change.originalStartLineNumber, + originalEndLineNumber: change.originalEndLineNumber, + modifiedStartLineNumber: change.modifiedStartLineNumber, + modifiedEndLineNumber: change.modifiedEndLineNumber + }; +} + +function assertDiff(originalLines: string[], modifiedLines: string[], expectedChanges: IChange[], shouldPostProcessCharChanges: boolean = false, shouldIgnoreTrimWhitespace: boolean = false) { + var diffComputer = new DiffComputer(originalLines, modifiedLines, { + shouldPostProcessCharChanges: shouldPostProcessCharChanges || false, + shouldIgnoreTrimWhitespace: shouldIgnoreTrimWhitespace || false, + shouldConsiderTrimWhitespaceInEmptyCase: true, + shouldMakePrettyDiff: true + }); + var changes = diffComputer.computeDiff(); + + var extracted = []; + for (var i = 0; i < changes.length; i++) { + extracted.push(extractLineChangeRepresentation(changes[i], (i < expectedChanges.length ? expectedChanges[i] : null))); + } + assert.deepEqual(extracted, expectedChanges); +} + +function createLineDeletion(startLineNumber: number, endLineNumber: number, modifiedLineNumber: number): IChange { + return { + originalStartLineNumber: startLineNumber, + originalEndLineNumber: endLineNumber, + modifiedStartLineNumber: modifiedLineNumber, + modifiedEndLineNumber: 0 + }; +} + +function createLineInsertion(startLineNumber: number, endLineNumber: number, originalLineNumber: number): IChange { + return { + originalStartLineNumber: originalLineNumber, + originalEndLineNumber: 0, + modifiedStartLineNumber: startLineNumber, + modifiedEndLineNumber: endLineNumber + }; +} + +function createLineChange(originalStartLineNumber: number, originalEndLineNumber: number, modifiedStartLineNumber: number, modifiedEndLineNumber: number, charChanges: ICharChange[]): ILineChange { + return { + originalStartLineNumber: originalStartLineNumber, + originalEndLineNumber: originalEndLineNumber, + modifiedStartLineNumber: modifiedStartLineNumber, + modifiedEndLineNumber: modifiedEndLineNumber, + charChanges: charChanges + }; +} + +function createCharInsertion(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number) { + return { + originalStartLineNumber: 0, + originalStartColumn: 0, + originalEndLineNumber: 0, + originalEndColumn: 0, + modifiedStartLineNumber: startLineNumber, + modifiedStartColumn: startColumn, + modifiedEndLineNumber: endLineNumber, + modifiedEndColumn: endColumn + }; +} + +function createCharDeletion(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number) { + return { + originalStartLineNumber: startLineNumber, + originalStartColumn: startColumn, + originalEndLineNumber: endLineNumber, + originalEndColumn: endColumn, + modifiedStartLineNumber: 0, + modifiedStartColumn: 0, + modifiedEndLineNumber: 0, + modifiedEndColumn: 0 + }; +} + +function createCharChange( + originalStartLineNumber: number, originalStartColumn: number, originalEndLineNumber: number, originalEndColumn: number, + modifiedStartLineNumber: number, modifiedStartColumn: number, modifiedEndLineNumber: number, modifiedEndColumn: number +) { + return { + originalStartLineNumber: originalStartLineNumber, + originalStartColumn: originalStartColumn, + originalEndLineNumber: originalEndLineNumber, + originalEndColumn: originalEndColumn, + modifiedStartLineNumber: modifiedStartLineNumber, + modifiedStartColumn: modifiedStartColumn, + modifiedEndLineNumber: modifiedEndLineNumber, + modifiedEndColumn: modifiedEndColumn + }; +} + +suite('Editor Diff - DiffComputer', () => { + + // ---- insertions + + test('one inserted line below', () => { + var original = ['line']; + var modified = ['line', 'new line']; + var expected = [createLineInsertion(2, 2, 1)]; + assertDiff(original, modified, expected); + }); + + test('two inserted lines below', () => { + var original = ['line']; + var modified = ['line', 'new line', 'another new line']; + var expected = [createLineInsertion(2, 3, 1)]; + assertDiff(original, modified, expected); + }); + + test('one inserted line above', () => { + var original = ['line']; + var modified = ['new line', 'line']; + var expected = [createLineInsertion(1, 1, 0)]; + assertDiff(original, modified, expected); + }); + + test('two inserted lines above', () => { + var original = ['line']; + var modified = ['new line', 'another new line', 'line']; + var expected = [createLineInsertion(1, 2, 0)]; + assertDiff(original, modified, expected); + }); + + test('one inserted line in middle', () => { + var original = ['line1', 'line2', 'line3', 'line4']; + var modified = ['line1', 'line2', 'new line', 'line3', 'line4']; + var expected = [createLineInsertion(3, 3, 2)]; + assertDiff(original, modified, expected); + }); + + test('two inserted lines in middle', () => { + var original = ['line1', 'line2', 'line3', 'line4']; + var modified = ['line1', 'line2', 'new line', 'another new line', 'line3', 'line4']; + var expected = [createLineInsertion(3, 4, 2)]; + assertDiff(original, modified, expected); + }); + + test('two inserted lines in middle interrupted', () => { + var original = ['line1', 'line2', 'line3', 'line4']; + var modified = ['line1', 'line2', 'new line', 'line3', 'another new line', 'line4']; + var expected = [createLineInsertion(3, 3, 2), createLineInsertion(5, 5, 3)]; + assertDiff(original, modified, expected); + }); + + // ---- deletions + + test('one deleted line below', () => { + var original = ['line', 'new line']; + var modified = ['line']; + var expected = [createLineDeletion(2, 2, 1)]; + assertDiff(original, modified, expected); + }); + + test('two deleted lines below', () => { + var original = ['line', 'new line', 'another new line']; + var modified = ['line']; + var expected = [createLineDeletion(2, 3, 1)]; + assertDiff(original, modified, expected); + }); + + test('one deleted lines above', () => { + var original = ['new line', 'line']; + var modified = ['line']; + var expected = [createLineDeletion(1, 1, 0)]; + assertDiff(original, modified, expected); + }); + + test('two deleted lines above', () => { + var original = ['new line', 'another new line', 'line']; + var modified = ['line']; + var expected = [createLineDeletion(1, 2, 0)]; + assertDiff(original, modified, expected); + }); + + test('one deleted line in middle', () => { + var original = ['line1', 'line2', 'new line', 'line3', 'line4']; + var modified = ['line1', 'line2', 'line3', 'line4']; + var expected = [createLineDeletion(3, 3, 2)]; + assertDiff(original, modified, expected); + }); + + test('two deleted lines in middle', () => { + var original = ['line1', 'line2', 'new line', 'another new line', 'line3', 'line4']; + var modified = ['line1', 'line2', 'line3', 'line4']; + var expected = [createLineDeletion(3, 4, 2)]; + assertDiff(original, modified, expected); + }); + + test('two deleted lines in middle interrupted', () => { + var original = ['line1', 'line2', 'new line', 'line3', 'another new line', 'line4']; + var modified = ['line1', 'line2', 'line3', 'line4']; + var expected = [createLineDeletion(3, 3, 2), createLineDeletion(5, 5, 3)]; + assertDiff(original, modified, expected); + }); + + // ---- changes + + test('one line changed: chars inserted at the end', () => { + var original = ['line']; + var modified = ['line changed']; + var expected = [ + createLineChange(1, 1, 1, 1, [ + createCharInsertion(1, 5, 1, 13) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('one line changed: chars inserted at the beginning', () => { + var original = ['line']; + var modified = ['my line']; + var expected = [ + createLineChange(1, 1, 1, 1, [ + createCharInsertion(1, 1, 1, 4) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('one line changed: chars inserted in the middle', () => { + var original = ['abba']; + var modified = ['abzzba']; + var expected = [ + createLineChange(1, 1, 1, 1, [ + createCharInsertion(1, 3, 1, 5) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('one line changed: chars inserted in the middle (two spots)', () => { + var original = ['abba']; + var modified = ['abzzbzza']; + var expected = [ + createLineChange(1, 1, 1, 1, [ + createCharInsertion(1, 3, 1, 5), + createCharInsertion(1, 6, 1, 8) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('one line changed: chars deleted 1', () => { + var original = ['abcdefg']; + var modified = ['abcfg']; + var expected = [ + createLineChange(1, 1, 1, 1, [ + createCharDeletion(1, 4, 1, 6) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('one line changed: chars deleted 2', () => { + var original = ['abcdefg']; + var modified = ['acfg']; + var expected = [ + createLineChange(1, 1, 1, 1, [ + createCharDeletion(1, 2, 1, 3), + createCharDeletion(1, 4, 1, 6) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('two lines changed 1', () => { + var original = ['abcd', 'efgh']; + var modified = ['abcz']; + var expected = [ + createLineChange(1, 2, 1, 1, [ + createCharChange(1, 4, 2, 5, 1, 4, 1, 5) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('two lines changed 2', () => { + var original = ['foo', 'abcd', 'efgh', 'BAR']; + var modified = ['foo', 'abcz', 'BAR']; + var expected = [ + createLineChange(2, 3, 2, 2, [ + createCharChange(2, 4, 3, 5, 2, 4, 2, 5) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('two lines changed 3', () => { + var original = ['foo', 'abcd', 'efgh', 'BAR']; + var modified = ['foo', 'abcz', 'zzzzefgh', 'BAR']; + var expected = [ + createLineChange(2, 3, 2, 3, [ + createCharChange(2, 4, 2, 5, 2, 4, 3, 5) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('three lines changed', () => { + var original = ['foo', 'abcd', 'efgh', 'BAR']; + var modified = ['foo', 'zzzefgh', 'xxx', 'BAR']; + var expected = [ + createLineChange(2, 3, 2, 3, [ + createCharChange(2, 1, 2, 5, 2, 1, 2, 4), + createCharInsertion(3, 1, 3, 4) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('big change part 1', () => { + var original = ['foo', 'abcd', 'efgh', 'BAR']; + var modified = ['hello', 'foo', 'zzzefgh', 'xxx', 'BAR']; + var expected = [ + createLineInsertion(1, 1, 0), + createLineChange(2, 3, 3, 4, [ + createCharChange(2, 1, 2, 5, 3, 1, 3, 4), + createCharInsertion(4, 1, 4, 4) + ]) + ]; + assertDiff(original, modified, expected); + }); + + test('big change part 2', () => { + var original = ['foo', 'abcd', 'efgh', 'BAR', 'RAB']; + var modified = ['hello', 'foo', 'zzzefgh', 'xxx', 'BAR']; + var expected = [ + createLineInsertion(1, 1, 0), + createLineChange(2, 3, 3, 4, [ + createCharChange(2, 1, 2, 5, 3, 1, 3, 4), + createCharInsertion(4, 1, 4, 4) + ]), + createLineDeletion(5, 5, 5) + ]; + assertDiff(original, modified, expected); + }); + + test('char change postprocessing merges', () => { + var original = ['abba']; + var modified = ['azzzbzzzbzzza']; + var expected = [ + createLineChange(1, 1, 1, 1, [ + createCharChange(1, 2, 1, 4, 1, 2, 1, 13) + ]) + ]; + assertDiff(original, modified, expected, true); + }); + + test('ignore trim whitespace', () => { + var original = ['\t\t foo ', 'abcd', 'efgh', '\t\t BAR\t\t']; + var modified = [' hello\t', '\t foo \t', 'zzzefgh', 'xxx', ' BAR \t']; + var expected = [ + createLineInsertion(1, 1, 0), + createLineChange(2, 3, 3, 4, [ + createCharChange(2, 1, 2, 5, 3, 1, 3, 4), + createCharInsertion(4, 1, 4, 4) + ]) + ]; + assertDiff(original, modified, expected, false, true); + }); + + test('issue #12122 r.hasOwnProperty is not a function', () => { + var original = ['hasOwnProperty']; + var modified = ['hasOwnProperty', 'and another line']; + var expected = [ + createLineInsertion(2, 2, 1) + ]; + assertDiff(original, modified, expected); + }); + + test('empty diff 1', () => { + var original = ['']; + var modified = ['something']; + var expected = [ + createLineChange(1, 1, 1, 1, [ + createCharChange(0, 0, 0, 0, 0, 0, 0, 0) + ]) + ]; + assertDiff(original, modified, expected, false, true); + }); + + test('empty diff 2', () => { + var original = ['']; + var modified = ['something', 'something else']; + var expected = [ + createLineChange(1, 1, 1, 2, [ + createCharChange(0, 0, 0, 0, 0, 0, 0, 0) + ]) + ]; + assertDiff(original, modified, expected, false, true); + }); + + test('empty diff 3', () => { + var original = ['something', 'something else']; + var modified = ['']; + var expected = [ + createLineChange(1, 2, 1, 1, [ + createCharChange(0, 0, 0, 0, 0, 0, 0, 0) + ]) + ]; + assertDiff(original, modified, expected, false, true); + }); + + test('empty diff 4', () => { + var original = ['something']; + var modified = ['']; + var expected = [ + createLineChange(1, 1, 1, 1, [ + createCharChange(0, 0, 0, 0, 0, 0, 0, 0) + ]) + ]; + assertDiff(original, modified, expected, false, true); + }); + + test('pretty diff 1', () => { + var original = [ + 'suite(function () {', + ' test1() {', + ' assert.ok(true);', + ' }', + '', + ' test2() {', + ' assert.ok(true);', + ' }', + '});', + '', + ]; + var modified = [ + '// An insertion', + 'suite(function () {', + ' test1() {', + ' assert.ok(true);', + ' }', + '', + ' test2() {', + ' assert.ok(true);', + ' }', + '', + ' test3() {', + ' assert.ok(true);', + ' }', + '});', + '', + ]; + var expected = [ + createLineInsertion(1, 1, 0), + createLineInsertion(10, 13, 8) + ]; + assertDiff(original, modified, expected, false, true); + }); + + test('pretty diff 2', () => { + var original = [ + '// Just a comment', + '', + 'function compute(a, b, c, d) {', + ' if (a) {', + ' if (b) {', + ' if (c) {', + ' return 5;', + ' }', + ' }', + ' // These next lines will be deleted', + ' if (d) {', + ' return -1;', + ' }', + ' return 0;', + ' }', + '}', + ]; + var modified = [ + '// Here is an inserted line', + '// and another inserted line', + '// and another one', + '// Just a comment', + '', + 'function compute(a, b, c, d) {', + ' if (a) {', + ' if (b) {', + ' if (c) {', + ' return 5;', + ' }', + ' }', + ' return 0;', + ' }', + '}', + ]; + var expected = [ + createLineInsertion(1, 3, 0), + createLineDeletion(10, 13, 12), + ]; + assertDiff(original, modified, expected, false, true); + }); + + test('pretty diff 3', () => { + var original = [ + 'class A {', + ' /**', + ' * m1', + ' */', + ' method1() {}', + '', + ' /**', + ' * m3', + ' */', + ' method3() {}', + '}', + ]; + var modified = [ + 'class A {', + ' /**', + ' * m1', + ' */', + ' method1() {}', + '', + ' /**', + ' * m2', + ' */', + ' method2() {}', + '', + ' /**', + ' * m3', + ' */', + ' method3() {}', + '}', + ]; + var expected = [ + createLineInsertion(7, 11, 6) + ]; + assertDiff(original, modified, expected, false, true); + }); + + test('issue #23636', () => { + let original = [ + 'if(!TextDrawLoad[playerid])', + '{', + '', + ' TextDrawHideForPlayer(playerid,TD_AppleJob[3]);', + ' TextDrawHideForPlayer(playerid,TD_AppleJob[4]);', + ' if(!AppleJobTreesType[AppleJobTreesPlayerNum[playerid]])', + ' {', + ' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[5+i]);', + ' }', + ' else', + ' {', + ' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[15+i]);', + ' }', + '}', + 'else', + '{', + ' TextDrawHideForPlayer(playerid,TD_AppleJob[3]);', + ' TextDrawHideForPlayer(playerid,TD_AppleJob[27]);', + ' if(!AppleJobTreesType[AppleJobTreesPlayerNum[playerid]])', + ' {', + ' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[28+i]);', + ' }', + ' else', + ' {', + ' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[38+i]);', + ' }', + '}', + ]; + let modified = [ + ' if(!TextDrawLoad[playerid])', + ' {', + ' ', + ' TextDrawHideForPlayer(playerid,TD_AppleJob[3]);', + ' TextDrawHideForPlayer(playerid,TD_AppleJob[4]);', + ' if(!AppleJobTreesType[AppleJobTreesPlayerNum[playerid]])', + ' {', + ' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[5+i]);', + ' }', + ' else', + ' {', + ' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[15+i]);', + ' }', + ' }', + ' else', + ' {', + ' TextDrawHideForPlayer(playerid,TD_AppleJob[3]);', + ' TextDrawHideForPlayer(playerid,TD_AppleJob[27]);', + ' if(!AppleJobTreesType[AppleJobTreesPlayerNum[playerid]])', + ' {', + ' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[28+i]);', + ' }', + ' else', + ' {', + ' for(new i=0;i<10;i++) if(StatusTD_AppleJobApples[playerid][i]) TextDrawHideForPlayer(playerid,TD_AppleJob[38+i]);', + ' }', + ' }', + ]; + var expected = [ + createLineChange( + 1, 27, 1, 27, + [ + createCharChange(1, 1, 1, 1, 1, 1, 1, 2), + createCharChange(2, 1, 2, 1, 2, 1, 2, 2), + createCharChange(3, 1, 3, 1, 3, 1, 3, 2), + createCharChange(4, 1, 4, 1, 4, 1, 4, 2), + createCharChange(5, 1, 5, 1, 5, 1, 5, 2), + createCharChange(6, 1, 6, 1, 6, 1, 6, 2), + createCharChange(7, 1, 7, 1, 7, 1, 7, 2), + createCharChange(8, 1, 8, 1, 8, 1, 8, 2), + createCharChange(9, 1, 9, 1, 9, 1, 9, 2), + createCharChange(10, 1, 10, 1, 10, 1, 10, 2), + createCharChange(11, 1, 11, 1, 11, 1, 11, 2), + createCharChange(12, 1, 12, 1, 12, 1, 12, 2), + createCharChange(13, 1, 13, 1, 13, 1, 13, 2), + createCharChange(14, 1, 14, 1, 14, 1, 14, 2), + createCharChange(15, 1, 15, 1, 15, 1, 15, 2), + createCharChange(16, 1, 16, 1, 16, 1, 16, 2), + createCharChange(17, 1, 17, 1, 17, 1, 17, 2), + createCharChange(18, 1, 18, 1, 18, 1, 18, 2), + createCharChange(19, 1, 19, 1, 19, 1, 19, 2), + createCharChange(20, 1, 20, 1, 20, 1, 20, 2), + createCharChange(21, 1, 21, 1, 21, 1, 21, 2), + createCharChange(22, 1, 22, 1, 22, 1, 22, 2), + createCharChange(23, 1, 23, 1, 23, 1, 23, 2), + createCharChange(24, 1, 24, 1, 24, 1, 24, 2), + createCharChange(25, 1, 25, 1, 25, 1, 25, 2), + createCharChange(26, 1, 26, 1, 26, 1, 26, 2), + createCharChange(27, 1, 27, 1, 27, 1, 27, 2), + ] + ) + // createLineInsertion(7, 11, 6) + ]; + assertDiff(original, modified, expected, true, false); + }); +}); diff --git a/src/vs/editor/test/common/editorTestUtils.ts b/src/vs/editor/test/common/editorTestUtils.ts new file mode 100644 index 0000000000..8663ccee88 --- /dev/null +++ b/src/vs/editor/test/common/editorTestUtils.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Model } from 'vs/editor/common/model/model'; + +export function withEditorModel(text: string[], callback: (model: Model) => void): void { + var model = Model.createFromString(text.join('\n')); + callback(model); + model.dispose(); +} diff --git a/src/vs/editor/test/common/mocks/mockCodeEditor.ts b/src/vs/editor/test/common/mocks/mockCodeEditor.ts new file mode 100644 index 0000000000..7ee50139f6 --- /dev/null +++ b/src/vs/editor/test/common/mocks/mockCodeEditor.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { CommonCodeEditor } from 'vs/editor/common/commonCodeEditor'; +import { CommonEditorConfiguration } from 'vs/editor/common/config/commonEditorConfig'; +import { Cursor } from 'vs/editor/common/controller/cursor'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Model } from 'vs/editor/common/model/model'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export class MockCodeEditor extends CommonCodeEditor { + protected _createConfiguration(options: editorOptions.IEditorOptions): CommonEditorConfiguration { + return new TestConfiguration(options); + } + + public layout(dimension?: editorCommon.IDimension): void { } + + public focus(): void { } + public isFocused(): boolean { return true; } + public hasWidgetFocus(): boolean { return true; }; + + protected _enableEmptySelectionClipboard(): boolean { return false; } + protected _scheduleAtNextAnimationFrame(callback: () => void): IDisposable { throw new Error('Notimplemented'); } + protected _createView(): void { } + + protected _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void { throw new Error('NotImplemented'); } + protected _removeDecorationType(key: string): void { throw new Error('NotImplemented'); } + protected _resolveDecorationOptions(typeKey: string, writable: boolean): editorCommon.IModelDecorationOptions { throw new Error('NotImplemented'); } + + // --- test utils + getCursor(): Cursor { + return this.cursor; + } + + public registerAndInstantiateContribution(ctor: any): T { + let r = this._instantiationService.createInstance(ctor, this); + this._contributions[r.getId()] = r; + return r; + } + + public dispose() { + super.dispose(); + if (this.model) { + this.model.dispose(); + } + this._contextKeyService.dispose(); + } +} + +export class MockScopeLocation implements IContextKeyServiceTarget { + parentElement: IContextKeyServiceTarget = null; + setAttribute(attr: string, value: string): void { } + removeAttribute(attr: string): void { } + hasAttribute(attr: string): boolean { return false; } + getAttribute(attr: string): string { return undefined; } +} + +export interface MockCodeEditorCreationOptions extends editorOptions.IEditorOptions { + /** + * The initial model associated with this code editor. + */ + model?: editorCommon.IModel; + serviceCollection?: ServiceCollection; +} + +export function withMockCodeEditor(text: string[], options: MockCodeEditorCreationOptions, callback: (editor: MockCodeEditor, cursor: Cursor) => void): void { + // create a model if necessary and remember it in order to dispose it. + let modelToDispose: Model = null; + if (!options.model) { + modelToDispose = Model.createFromString(text.join('\n')); + options.model = modelToDispose; + } + + let editor = _mockCodeEditor(options); + callback(editor, editor.getCursor()); + + if (modelToDispose) { + modelToDispose.dispose(); + } + editor.dispose(); +} + +export function mockCodeEditor(text: string[], options: MockCodeEditorCreationOptions): CommonCodeEditor { + // TODO: who owns this model now? + if (!options.model) { + options.model = Model.createFromString(text.join('\n')); + } + return _mockCodeEditor(options); +} + +function _mockCodeEditor(options: MockCodeEditorCreationOptions): CommonCodeEditor { + + let contextKeyService = new MockContextKeyService(); + + let services = options.serviceCollection || new ServiceCollection(); + services.set(IContextKeyService, contextKeyService); + let instantiationService = new InstantiationService(services); + + let editor = new MockCodeEditor(new MockScopeLocation(), options, instantiationService, contextKeyService); + editor.setModel(options.model); + return editor; +} diff --git a/src/vs/editor/test/common/mocks/mockCodeEditorService.ts b/src/vs/editor/test/common/mocks/mockCodeEditorService.ts new file mode 100644 index 0000000000..4732d70753 --- /dev/null +++ b/src/vs/editor/test/common/mocks/mockCodeEditorService.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDecorationRenderOptions, IModelDecorationOptions } from 'vs/editor/common/editorCommon'; +import { AbstractCodeEditorService } from 'vs/editor/common/services/abstractCodeEditorService'; + +export class MockCodeEditorService extends AbstractCodeEditorService { + public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void { } + public removeDecorationType(key: string): void { } + public resolveDecorationOptions(decorationTypeKey: string, writable: boolean): IModelDecorationOptions { return null; } +} diff --git a/src/vs/editor/test/common/mocks/mockMode.ts b/src/vs/editor/test/common/mocks/mockMode.ts new file mode 100644 index 0000000000..f11f5c98b6 --- /dev/null +++ b/src/vs/editor/test/common/mocks/mockMode.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IMode, LanguageIdentifier } from 'vs/editor/common/modes'; + +export class MockMode extends Disposable implements IMode { + private _languageIdentifier: LanguageIdentifier; + + constructor(languageIdentifier: LanguageIdentifier) { + super(); + this._languageIdentifier = languageIdentifier; + } + + public getId(): string { + return this._languageIdentifier.language; + } + + public getLanguageIdentifier(): LanguageIdentifier { + return this._languageIdentifier; + } +} diff --git a/src/vs/editor/test/common/mocks/testConfiguration.ts b/src/vs/editor/test/common/mocks/testConfiguration.ts new file mode 100644 index 0000000000..1e6e617ae0 --- /dev/null +++ b/src/vs/editor/test/common/mocks/testConfiguration.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo'; +import { AccessibilitySupport } from 'vs/base/common/platform'; + +export class TestConfiguration extends CommonEditorConfiguration { + + constructor(opts: IEditorOptions) { + super(opts); + this._recomputeOptions(); + } + + protected _getEnvConfiguration(): IEnvConfiguration { + return { + extraEditorClassName: '', + outerWidth: 100, + outerHeight: 100, + emptySelectionClipboard: true, + pixelRatio: 1, + zoomLevel: 0, + accessibilitySupport: AccessibilitySupport.Unknown + }; + } + + protected readConfiguration(styling: BareFontInfo): FontInfo { + return new FontInfo({ + zoomLevel: 0, + fontFamily: 'mockFont', + fontWeight: 'normal', + fontSize: 14, + lineHeight: 19, + letterSpacing: 1.5, + isMonospace: true, + typicalHalfwidthCharacterWidth: 10, + typicalFullwidthCharacterWidth: 20, + spaceWidth: 10, + maxDigitWidth: 10, + }, true); + } +} diff --git a/src/vs/editor/test/common/model/editableTextModel.test.ts b/src/vs/editor/test/common/model/editableTextModel.test.ts new file mode 100644 index 0000000000..f4ac0f6904 --- /dev/null +++ b/src/vs/editor/test/common/model/editableTextModel.test.ts @@ -0,0 +1,1835 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { Range } from 'vs/editor/common/core/range'; +import { EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; +import { EditableTextModel, IValidatedEditOperation } from 'vs/editor/common/model/editableTextModel'; +import { MirrorModel } from 'vs/editor/common/model/mirrorModel'; +import { assertSyncedModels, testApplyEditsWithSyncedModels } from 'vs/editor/test/common/model/editableTextModelTestUtils'; +import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; + +suite('EditorModel - EditableTextModel._getInverseEdits', () => { + + function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeLength: number, text: string[]): IValidatedEditOperation { + return { + sortIndex: 0, + identifier: null, + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + rangeLength: rangeLength, + lines: text, + forceMoveMarkers: false, + isAutoWhitespaceEdit: false + }; + } + + function inverseEditOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number): Range { + return new Range(startLineNumber, startColumn, endLineNumber, endColumn); + } + + function assertInverseEdits(ops: IValidatedEditOperation[], expected: Range[]): void { + var actual = EditableTextModel._getInverseEditRanges(ops); + assert.deepEqual(actual, expected); + } + + test('single insert', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 1, 0, ['hello']) + ], + [ + inverseEditOp(1, 1, 1, 6) + ] + ); + }); + + test('Bug 19872: Undo is funky', () => { + assertInverseEdits( + [ + editOp(2, 1, 2, 2, 0, ['']), + editOp(3, 1, 4, 2, 0, ['']) + ], + [ + inverseEditOp(2, 1, 2, 1), + inverseEditOp(3, 1, 3, 1) + ] + ); + }); + + test('two single unrelated inserts', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 1, 0, ['hello']), + editOp(2, 1, 2, 1, 0, ['world']) + ], + [ + inverseEditOp(1, 1, 1, 6), + inverseEditOp(2, 1, 2, 6) + ] + ); + }); + + test('two single inserts 1', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 1, 0, ['hello']), + editOp(1, 2, 1, 2, 0, ['world']) + ], + [ + inverseEditOp(1, 1, 1, 6), + inverseEditOp(1, 7, 1, 12) + ] + ); + }); + + test('two single inserts 2', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 1, 0, ['hello']), + editOp(1, 4, 1, 4, 0, ['world']) + ], + [ + inverseEditOp(1, 1, 1, 6), + inverseEditOp(1, 9, 1, 14) + ] + ); + }); + + test('multiline insert', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 1, 0, ['hello', 'world']) + ], + [ + inverseEditOp(1, 1, 2, 6) + ] + ); + }); + + test('two unrelated multiline inserts', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 1, 0, ['hello', 'world']), + editOp(2, 1, 2, 1, 0, ['how', 'are', 'you?']), + ], + [ + inverseEditOp(1, 1, 2, 6), + inverseEditOp(3, 1, 5, 5), + ] + ); + }); + + test('two multiline inserts 1', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 1, 0, ['hello', 'world']), + editOp(1, 2, 1, 2, 0, ['how', 'are', 'you?']), + ], + [ + inverseEditOp(1, 1, 2, 6), + inverseEditOp(2, 7, 4, 5), + ] + ); + }); + + test('single delete', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 6, 0, null) + ], + [ + inverseEditOp(1, 1, 1, 1) + ] + ); + }); + + test('two single unrelated deletes', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 6, 0, null), + editOp(2, 1, 2, 6, 0, null) + ], + [ + inverseEditOp(1, 1, 1, 1), + inverseEditOp(2, 1, 2, 1) + ] + ); + }); + + test('two single deletes 1', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 6, 0, null), + editOp(1, 7, 1, 12, 0, null) + ], + [ + inverseEditOp(1, 1, 1, 1), + inverseEditOp(1, 2, 1, 2) + ] + ); + }); + + test('two single deletes 2', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 6, 0, null), + editOp(1, 9, 1, 14, 0, null) + ], + [ + inverseEditOp(1, 1, 1, 1), + inverseEditOp(1, 4, 1, 4) + ] + ); + }); + + test('multiline delete', () => { + assertInverseEdits( + [ + editOp(1, 1, 2, 6, 0, null) + ], + [ + inverseEditOp(1, 1, 1, 1) + ] + ); + }); + + test('two unrelated multiline deletes', () => { + assertInverseEdits( + [ + editOp(1, 1, 2, 6, 0, null), + editOp(3, 1, 5, 5, 0, null), + ], + [ + inverseEditOp(1, 1, 1, 1), + inverseEditOp(2, 1, 2, 1), + ] + ); + }); + + test('two multiline deletes 1', () => { + assertInverseEdits( + [ + editOp(1, 1, 2, 6, 0, null), + editOp(2, 7, 4, 5, 0, null), + ], + [ + inverseEditOp(1, 1, 1, 1), + inverseEditOp(1, 2, 1, 2), + ] + ); + }); + + test('single replace', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 6, 0, ['Hello world']) + ], + [ + inverseEditOp(1, 1, 1, 12) + ] + ); + }); + + test('two replaces', () => { + assertInverseEdits( + [ + editOp(1, 1, 1, 6, 0, ['Hello world']), + editOp(1, 7, 1, 8, 0, ['How are you?']), + ], + [ + inverseEditOp(1, 1, 1, 12), + inverseEditOp(1, 13, 1, 25) + ] + ); + }); + + test('many edits', () => { + assertInverseEdits( + [ + editOp(1, 2, 1, 2, 0, ['', ' ']), + editOp(1, 5, 1, 6, 0, ['']), + editOp(1, 9, 1, 9, 0, ['', '']) + ], + [ + inverseEditOp(1, 2, 2, 3), + inverseEditOp(2, 6, 2, 6), + inverseEditOp(2, 9, 3, 1) + ] + ); + }); +}); + +suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { + + function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeLength: number, text: string[]): IValidatedEditOperation { + return { + sortIndex: 0, + identifier: null, + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + rangeLength: rangeLength, + lines: text, + forceMoveMarkers: false, + isAutoWhitespaceEdit: false + }; + } + + function testSimpleApplyEdits(original: string[], edits: IValidatedEditOperation[], expected: IValidatedEditOperation): void { + let model = EditableTextModel.createFromString(original.join('\n')); + model.setEOL(EndOfLineSequence.LF); + + let actual = model._toSingleEditOperation(edits); + assert.deepEqual(actual, expected); + + model.dispose(); + } + + test('one edit op is unchanged', () => { + testSimpleApplyEdits( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 3, 1, 3, 0, [' new line', 'No longer']) + ], + editOp(1, 3, 1, 3, 0, [' new line', 'No longer']) + ); + }); + + test('two edits on one line', () => { + testSimpleApplyEdits([ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], [ + editOp(1, 1, 1, 3, 0, ['Your']), + editOp(1, 4, 1, 4, 0, ['Interesting ']), + editOp(2, 3, 2, 6, 0, null) + ], + editOp(1, 1, 2, 6, 19, [ + 'Your Interesting First Line', + '\t\t' + ])); + }); + + test('insert multiple newlines', () => { + testSimpleApplyEdits( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 3, 1, 3, 0, ['', '', '', '', '']), + editOp(3, 15, 3, 15, 0, ['a', 'b']) + ], + editOp(1, 3, 3, 15, 43, [ + '', + '', + '', + '', + ' First Line', + '\t\tMy Second Line', + ' Third Linea', + 'b' + ]) + ); + }); + + test('delete empty text', () => { + testSimpleApplyEdits( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 1, 1, 1, 0, ['']) + ], + editOp(1, 1, 1, 1, 0, ['']) + ); + }); + + test('two unrelated edits', () => { + testSimpleApplyEdits( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + [ + editOp(2, 1, 2, 3, 0, ['\t']), + editOp(3, 1, 3, 5, 0, ['']) + ], + editOp(2, 1, 3, 5, 21, ['\tMy Second Line', '']) + ); + }); + + test('many edits', () => { + testSimpleApplyEdits( + [ + '{"x" : 1}' + ], + [ + editOp(1, 2, 1, 2, 0, ['\n ']), + editOp(1, 5, 1, 6, 0, ['']), + editOp(1, 9, 1, 9, 0, ['\n']) + ], + editOp(1, 2, 1, 9, 7, [ + '', + ' "x": 1', + '' + ]) + ); + }); + + test('many edits reversed', () => { + testSimpleApplyEdits( + [ + '{', + ' "x": 1', + '}' + ], + [ + editOp(1, 2, 2, 3, 0, ['']), + editOp(2, 6, 2, 6, 0, [' ']), + editOp(2, 9, 3, 1, 0, ['']) + ], + editOp(1, 2, 3, 1, 10, ['"x" : 1']) + ); + }); + + test('replacing newlines 1', () => { + testSimpleApplyEdits( + [ + '{', + '"a": true,', + '', + '"b": true', + '}' + ], + [ + editOp(1, 2, 2, 1, 0, ['', '\t']), + editOp(2, 11, 4, 1, 0, ['', '\t']) + ], + editOp(1, 2, 4, 1, 13, [ + '', + '\t"a": true,', + '\t' + ]) + ); + }); + + test('replacing newlines 2', () => { + testSimpleApplyEdits( + [ + 'some text', + 'some more text', + 'now comes an empty line', + '', + 'after empty line', + 'and the last line' + ], + [ + editOp(1, 5, 3, 1, 0, [' text', 'some more text', 'some more text']), + editOp(3, 2, 4, 1, 0, ['o more lines', 'asd', 'asd', 'asd']), + editOp(5, 1, 5, 6, 0, ['zzzzzzzz']), + editOp(5, 11, 6, 16, 0, ['1', '2', '3', '4']) + ], + editOp(1, 5, 6, 16, 78, [ + ' text', + 'some more text', + 'some more textno more lines', + 'asd', + 'asd', + 'asd', + 'zzzzzzzz empt1', + '2', + '3', + '4' + ]) + ); + }); + + test('advanced', () => { + testSimpleApplyEdits( + [ + ' { "d": [', + ' null', + ' ] /*comment*/', + ' ,"e": /*comment*/ [null] }', + ], + [ + editOp(1, 1, 1, 2, 0, ['']), + editOp(1, 3, 1, 10, 0, ['', ' ']), + editOp(1, 16, 2, 14, 0, ['', ' ']), + editOp(2, 18, 3, 9, 0, ['', ' ']), + editOp(3, 22, 4, 9, 0, ['']), + editOp(4, 10, 4, 10, 0, ['', ' ']), + editOp(4, 28, 4, 28, 0, ['', ' ']), + editOp(4, 32, 4, 32, 0, ['', ' ']), + editOp(4, 33, 4, 34, 0, ['', '']) + ], + editOp(1, 1, 4, 34, 89, [ + '{', + ' "d": [', + ' null', + ' ] /*comment*/,', + ' "e": /*comment*/ [', + ' null', + ' ]', + '' + ]) + ); + }); + + test('advanced simplified', () => { + testSimpleApplyEdits( + [ + ' abc', + ' ,def' + ], + [ + editOp(1, 1, 1, 4, 0, ['']), + editOp(1, 7, 2, 2, 0, ['']), + editOp(2, 3, 2, 3, 0, ['', '']) + ], + editOp(1, 1, 2, 3, 9, [ + 'abc,', + '' + ]) + ); + }); +}); + +suite('EditorModel - EditableTextModel.applyEdits updates mightContainRTL', () => { + + function testApplyEdits(original: string[], edits: IIdentifiedSingleEditOperation[], before: boolean, after: boolean): void { + let model = EditableTextModel.createFromString(original.join('\n')); + model.setEOL(EndOfLineSequence.LF); + + assert.equal(model.mightContainRTL(), before); + + model.applyEdits(edits); + assert.equal(model.mightContainRTL(), after); + model.dispose(); + } + + function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): IIdentifiedSingleEditOperation { + return { + identifier: null, + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + text: text.join('\n'), + forceMoveMarkers: false + }; + } + + test('start with RTL, insert LTR', () => { + testApplyEdits(['Hello,\nזוהי עובדה מבוססת שדעתו'], [editOp(1, 1, 1, 1, ['hello'])], true, true); + }); + + test('start with RTL, delete RTL', () => { + testApplyEdits(['Hello,\nזוהי עובדה מבוססת שדעתו'], [editOp(1, 1, 10, 10, [''])], true, true); + }); + + test('start with RTL, insert RTL', () => { + testApplyEdits(['Hello,\nזוהי עובדה מבוססת שדעתו'], [editOp(1, 1, 1, 1, ['هناك حقيقة مثبتة منذ زمن طويل'])], true, true); + }); + + test('start with LTR, insert LTR', () => { + testApplyEdits(['Hello,\nworld!'], [editOp(1, 1, 1, 1, ['hello'])], false, false); + }); + + test('start with LTR, insert RTL 1', () => { + testApplyEdits(['Hello,\nworld!'], [editOp(1, 1, 1, 1, ['هناك حقيقة مثبتة منذ زمن طويل'])], false, true); + }); + + test('start with LTR, insert RTL 2', () => { + testApplyEdits(['Hello,\nworld!'], [editOp(1, 1, 1, 1, ['זוהי עובדה מבוססת שדעתו'])], false, true); + }); +}); + + +suite('EditorModel - EditableTextModel.applyEdits updates mightContainNonBasicASCII', () => { + + function testApplyEdits(original: string[], edits: IIdentifiedSingleEditOperation[], before: boolean, after: boolean): void { + let model = EditableTextModel.createFromString(original.join('\n')); + model.setEOL(EndOfLineSequence.LF); + + assert.equal(model.mightContainNonBasicASCII(), before); + + model.applyEdits(edits); + assert.equal(model.mightContainNonBasicASCII(), after); + model.dispose(); + } + + function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): IIdentifiedSingleEditOperation { + return { + identifier: null, + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + text: text.join('\n'), + forceMoveMarkers: false + }; + } + + test('start with NON-ASCII, insert ASCII', () => { + testApplyEdits(['Hello,\nZürich'], [editOp(1, 1, 1, 1, ['hello', 'second line'])], true, true); + }); + + test('start with NON-ASCII, delete NON-ASCII', () => { + testApplyEdits(['Hello,\nZürich'], [editOp(1, 1, 10, 10, [''])], true, true); + }); + + test('start with NON-ASCII, insert NON-ASCII', () => { + testApplyEdits(['Hello,\nZürich'], [editOp(1, 1, 1, 1, ['Zürich'])], true, true); + }); + + test('start with ASCII, insert ASCII', () => { + testApplyEdits(['Hello,\nworld!'], [editOp(1, 1, 1, 1, ['hello', 'second line'])], false, false); + }); + + test('start with ASCII, insert NON-ASCII', () => { + testApplyEdits(['Hello,\nworld!'], [editOp(1, 1, 1, 1, ['Zürich', 'Zürich'])], false, true); + }); + +}); + +suite('EditorModel - EditableTextModel.applyEdits', () => { + + function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): IIdentifiedSingleEditOperation { + return { + identifier: null, + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + text: text.join('\n'), + forceMoveMarkers: false + }; + } + + test('high-low surrogates 1', () => { + testApplyEditsWithSyncedModels( + [ + '📚some', + 'very nice', + 'text' + ], + [ + editOp(1, 2, 1, 2, ['a']) + ], + [ + 'a📚some', + 'very nice', + 'text' + ], +/*inputEditsAreInvalid*/true + ); + }); + test('high-low surrogates 2', () => { + testApplyEditsWithSyncedModels( + [ + '📚some', + 'very nice', + 'text' + ], + [ + editOp(1, 2, 1, 3, ['a']) + ], + [ + 'asome', + 'very nice', + 'text' + ], +/*inputEditsAreInvalid*/true + ); + }); + test('high-low surrogates 3', () => { + testApplyEditsWithSyncedModels( + [ + '📚some', + 'very nice', + 'text' + ], + [ + editOp(1, 1, 1, 2, ['a']) + ], + [ + 'asome', + 'very nice', + 'text' + ], +/*inputEditsAreInvalid*/true + ); + }); + test('high-low surrogates 4', () => { + testApplyEditsWithSyncedModels( + [ + '📚some', + 'very nice', + 'text' + ], + [ + editOp(1, 1, 1, 3, ['a']) + ], + [ + 'asome', + 'very nice', + 'text' + ], +/*inputEditsAreInvalid*/true + ); + }); + + test('Bug 19872: Undo is funky', () => { + testApplyEditsWithSyncedModels( + [ + 'something', + ' A', + '', + ' B', + 'something else' + ], + [ + editOp(2, 1, 2, 2, ['']), + editOp(3, 1, 4, 2, ['']) + ], + [ + 'something', + 'A', + 'B', + 'something else' + ] + ); + }); + + test('Bug 19872: Undo is funky', () => { + testApplyEditsWithSyncedModels( + [ + 'something', + 'A', + 'B', + 'something else' + ], + [ + editOp(2, 1, 2, 1, [' ']), + editOp(3, 1, 3, 1, ['', ' ']) + ], + [ + 'something', + ' A', + '', + ' B', + 'something else' + ] + ); + }); + + test('insert empty text', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 1, 1, 1, ['']) + ], + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('last op is no-op', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 1, 1, 2, ['']), + editOp(4, 1, 4, 1, ['']) + ], + [ + 'y First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('insert text without newline 1', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 1, 1, 1, ['foo ']) + ], + [ + 'foo My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('insert text without newline 2', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 3, 1, 3, [' foo']) + ], + [ + 'My foo First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('insert one newline', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 4, 1, 4, ['', '']) + ], + [ + 'My ', + 'First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('insert text with one newline', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 3, 1, 3, [' new line', 'No longer']) + ], + [ + 'My new line', + 'No longer First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('insert text with two newlines', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 3, 1, 3, [' new line', 'One more line in the middle', 'No longer']) + ], + [ + 'My new line', + 'One more line in the middle', + 'No longer First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('insert text with many newlines', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 3, 1, 3, ['', '', '', '', '']) + ], + [ + 'My', + '', + '', + '', + ' First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('insert multiple newlines', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 3, 1, 3, ['', '', '', '', '']), + editOp(3, 15, 3, 15, ['a', 'b']) + ], + [ + 'My', + '', + '', + '', + ' First Line', + '\t\tMy Second Line', + ' Third Linea', + 'b', + '', + '1' + ] + ); + }); + + test('delete empty text', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 1, 1, 1, ['']) + ], + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('delete text from one line', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 1, 1, 2, ['']) + ], + [ + 'y First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('delete text from one line 2', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 1, 1, 3, ['a']) + ], + [ + 'a First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('delete all text from a line', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 1, 1, 14, ['']) + ], + [ + '', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('delete text from two lines', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 4, 2, 6, ['']) + ], + [ + 'My Second Line', + ' Third Line', + '', + '1' + ] + ); + }); + + test('delete text from many lines', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 4, 3, 5, ['']) + ], + [ + 'My Third Line', + '', + '1' + ] + ); + }); + + test('delete everything', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '1' + ], + [ + editOp(1, 1, 5, 2, ['']) + ], + [ + '' + ] + ); + }); + + test('two unrelated edits', () => { + testApplyEditsWithSyncedModels( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + [ + editOp(2, 1, 2, 3, ['\t']), + editOp(3, 1, 3, 5, ['']) + ], + [ + 'My First Line', + '\tMy Second Line', + 'Third Line', + '', + '123' + ] + ); + }); + + test('two edits on one line', () => { + testApplyEditsWithSyncedModels( + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\t\t\t' + ], + [ + editOp(5, 3, 5, 7, ['']), + editOp(5, 12, 5, 16, ['']) + ], + [ + '\t\tfirst\t ', + '\t\tsecond line', + '\tthird line', + 'fourth line', + '\t\tfifth\t\t' + ] + ); + }); + + test('many edits', () => { + testApplyEditsWithSyncedModels( + [ + '{"x" : 1}' + ], + [ + editOp(1, 2, 1, 2, ['\n ']), + editOp(1, 5, 1, 6, ['']), + editOp(1, 9, 1, 9, ['\n']) + ], + [ + '{', + ' "x": 1', + '}' + ] + ); + }); + + test('many edits reversed', () => { + testApplyEditsWithSyncedModels( + [ + '{', + ' "x": 1', + '}' + ], + [ + editOp(1, 2, 2, 3, ['']), + editOp(2, 6, 2, 6, [' ']), + editOp(2, 9, 3, 1, ['']) + ], + [ + '{"x" : 1}' + ] + ); + }); + + test('replacing newlines 1', () => { + testApplyEditsWithSyncedModels( + [ + '{', + '"a": true,', + '', + '"b": true', + '}' + ], + [ + editOp(1, 2, 2, 1, ['', '\t']), + editOp(2, 11, 4, 1, ['', '\t']) + ], + [ + '{', + '\t"a": true,', + '\t"b": true', + '}' + ] + ); + }); + + test('replacing newlines 2', () => { + testApplyEditsWithSyncedModels( + [ + 'some text', + 'some more text', + 'now comes an empty line', + '', + 'after empty line', + 'and the last line' + ], + [ + editOp(1, 5, 3, 1, [' text', 'some more text', 'some more text']), + editOp(3, 2, 4, 1, ['o more lines', 'asd', 'asd', 'asd']), + editOp(5, 1, 5, 6, ['zzzzzzzz']), + editOp(5, 11, 6, 16, ['1', '2', '3', '4']) + ], + [ + 'some text', + 'some more text', + 'some more textno more lines', + 'asd', + 'asd', + 'asd', + 'zzzzzzzz empt1', + '2', + '3', + '4ne' + ] + ); + }); + + test('advanced 1', () => { + testApplyEditsWithSyncedModels( + [ + ' { "d": [', + ' null', + ' ] /*comment*/', + ' ,"e": /*comment*/ [null] }', + ], + [ + editOp(1, 1, 1, 2, ['']), + editOp(1, 3, 1, 10, ['', ' ']), + editOp(1, 16, 2, 14, ['', ' ']), + editOp(2, 18, 3, 9, ['', ' ']), + editOp(3, 22, 4, 9, ['']), + editOp(4, 10, 4, 10, ['', ' ']), + editOp(4, 28, 4, 28, ['', ' ']), + editOp(4, 32, 4, 32, ['', ' ']), + editOp(4, 33, 4, 34, ['', '']) + ], + [ + '{', + ' "d": [', + ' null', + ' ] /*comment*/,', + ' "e": /*comment*/ [', + ' null', + ' ]', + '}', + ] + ); + }); + + test('advanced simplified', () => { + testApplyEditsWithSyncedModels( + [ + ' abc', + ' ,def' + ], + [ + editOp(1, 1, 1, 4, ['']), + editOp(1, 7, 2, 2, ['']), + editOp(2, 3, 2, 3, ['', '']) + ], + [ + 'abc,', + 'def' + ] + ); + }); + + test('issue #144', () => { + testApplyEditsWithSyncedModels( + [ + 'package caddy', + '', + 'func main() {', + '\tfmt.Println("Hello World! :)")', + '}', + '' + ], + [ + editOp(1, 1, 6, 1, [ + 'package caddy', + '', + 'import "fmt"', + '', + 'func main() {', + '\tfmt.Println("Hello World! :)")', + '}', + '' + ]) + ], + [ + 'package caddy', + '', + 'import "fmt"', + '', + 'func main() {', + '\tfmt.Println("Hello World! :)")', + '}', + '' + ] + ); + }); + + test('issue #2586 Replacing selected end-of-line with newline locks up the document', () => { + testApplyEditsWithSyncedModels( + [ + 'something', + 'interesting' + ], + [ + editOp(1, 10, 2, 1, ['', '']) + ], + [ + 'something', + 'interesting' + ] + ); + }); + + test('issue #3980', () => { + testApplyEditsWithSyncedModels( + [ + 'class A {', + ' someProperty = false;', + ' someMethod() {', + ' this.someMethod();', + ' }', + '}', + ], + [ + editOp(1, 8, 1, 9, ['', '']), + editOp(3, 17, 3, 18, ['', '']), + editOp(3, 18, 3, 18, [' ']), + editOp(4, 5, 4, 5, [' ']), + ], + [ + 'class A', + '{', + ' someProperty = false;', + ' someMethod()', + ' {', + ' this.someMethod();', + ' }', + '}', + ] + ); + }); + + function testApplyEditsFails(original: string[], edits: IIdentifiedSingleEditOperation[]): void { + let model = EditableTextModel.createFromString(original.join('\n')); + + let hasThrown = false; + try { + model.applyEdits(edits); + } catch (err) { + hasThrown = true; + } + assert.ok(hasThrown, 'expected model.applyEdits to fail.'); + + model.dispose(); + } + + test('touching edits: two inserts at the same position', () => { + testApplyEditsWithSyncedModels( + [ + 'hello world' + ], + [ + editOp(1, 1, 1, 1, ['a']), + editOp(1, 1, 1, 1, ['b']), + ], + [ + 'abhello world' + ] + ); + }); + + test('touching edits: insert and replace touching', () => { + testApplyEditsWithSyncedModels( + [ + 'hello world' + ], + [ + editOp(1, 1, 1, 1, ['b']), + editOp(1, 1, 1, 3, ['ab']), + ], + [ + 'babllo world' + ] + ); + }); + + test('overlapping edits: two overlapping replaces', () => { + testApplyEditsFails( + [ + 'hello world' + ], + [ + editOp(1, 1, 1, 2, ['b']), + editOp(1, 1, 1, 3, ['ab']), + ] + ); + }); + + test('overlapping edits: two overlapping deletes', () => { + testApplyEditsFails( + [ + 'hello world' + ], + [ + editOp(1, 1, 1, 2, ['']), + editOp(1, 1, 1, 3, ['']), + ] + ); + }); + + test('touching edits: two touching replaces', () => { + testApplyEditsWithSyncedModels( + [ + 'hello world' + ], + [ + editOp(1, 1, 1, 2, ['H']), + editOp(1, 2, 1, 3, ['E']), + ], + [ + 'HEllo world' + ] + ); + }); + + test('touching edits: two touching deletes', () => { + testApplyEditsWithSyncedModels( + [ + 'hello world' + ], + [ + editOp(1, 1, 1, 2, ['']), + editOp(1, 2, 1, 3, ['']), + ], + [ + 'llo world' + ] + ); + }); + + test('touching edits: insert and replace', () => { + testApplyEditsWithSyncedModels( + [ + 'hello world' + ], + [ + editOp(1, 1, 1, 1, ['H']), + editOp(1, 1, 1, 3, ['e']), + ], + [ + 'Hello world' + ] + ); + }); + + test('touching edits: replace and insert', () => { + testApplyEditsWithSyncedModels( + [ + 'hello world' + ], + [ + editOp(1, 1, 1, 3, ['H']), + editOp(1, 3, 1, 3, ['e']), + ], + [ + 'Hello world' + ] + ); + }); + + test('change while emitting events 1', () => { + + assertSyncedModels('Hello', (model, assertMirrorModels) => { + model.applyEdits([{ + identifier: null, + range: new Range(1, 6, 1, 6), + text: ' world!', + forceMoveMarkers: false + }]); + + assertMirrorModels(); + + }, (model) => { + var isFirstTime = true; + model.addBulkListener((events) => { + if (!isFirstTime) { + return; + } + isFirstTime = false; + + model.applyEdits([{ + identifier: null, + range: new Range(1, 13, 1, 13), + text: ' How are you?', + forceMoveMarkers: false + }]); + }); + }); + }); + + test('change while emitting events 2', () => { + + assertSyncedModels('Hello', (model, assertMirrorModels) => { + model.applyEdits([{ + identifier: null, + range: new Range(1, 6, 1, 6), + text: ' world!', + forceMoveMarkers: false + }]); + + assertMirrorModels(); + + }, (model) => { + var isFirstTime = true; + model.onDidChangeContent((e: IModelContentChangedEvent) => { + if (!isFirstTime) { + return; + } + isFirstTime = false; + + model.applyEdits([{ + identifier: null, + range: new Range(1, 13, 1, 13), + text: ' How are you?', + forceMoveMarkers: false + }]); + }); + }); + }); + + test('issue #1580: Changes in line endings are not correctly reflected in the extension host, leading to invalid offsets sent to external refactoring tools', () => { + let model = EditableTextModel.createFromString('Hello\nWorld!'); + assert.equal(model.getEOL(), '\n'); + + let mirrorModel2 = new MirrorModel(null, model.getLinesContent(), model.getEOL(), model.getVersionId()); + let mirrorModel2PrevVersionId = model.getVersionId(); + + model.onDidChangeContent((e: IModelContentChangedEvent) => { + let versionId = e.versionId; + if (versionId < mirrorModel2PrevVersionId) { + console.warn('Model version id did not advance between edits (2)'); + } + mirrorModel2PrevVersionId = versionId; + mirrorModel2.onEvents(e); + }); + + let assertMirrorModels = () => { + model._assertLineNumbersOK(); + assert.equal(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK'); + assert.equal(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK'); + }; + + model.setEOL(EndOfLineSequence.CRLF); + assertMirrorModels(); + + model.dispose(); + mirrorModel2.dispose(); + }); +}); + +interface ILightWeightMarker { + id: string; + lineNumber: number; + column: number; + stickToPreviousCharacter: boolean; +} + +suite('EditorModel - EditableTextModel.applyEdits & markers', () => { + + function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): IIdentifiedSingleEditOperation { + return { + identifier: null, + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + text: text.join('\n'), + forceMoveMarkers: false + }; + } + + function marker(id: string, lineNumber: number, column: number, stickToPreviousCharacter: boolean): ILightWeightMarker { + return { + id: id, + lineNumber: lineNumber, + column: column, + stickToPreviousCharacter: stickToPreviousCharacter + }; + } + + function toMarkersMap(markers: ILightWeightMarker[]): { [markerId: string]: ILightWeightMarker } { + var result: { [markerId: string]: ILightWeightMarker } = {}; + markers.forEach(m => { + result[m.id] = m; + }); + return result; + } + + function testApplyEditsAndMarkers(text: string[], markers: ILightWeightMarker[], edits: IIdentifiedSingleEditOperation[], changedMarkers: string[], expectedText: string[], expectedMarkers: ILightWeightMarker[]): void { + var textStr = text.join('\n'); + var expectedTextStr = expectedText.join('\n'); + var markersMap = toMarkersMap(markers); + // var expectedMarkersMap = toMarkersMap(expectedMarkers); + var markerId2ModelMarkerId = Object.create(null); + + var model = EditableTextModel.createFromString(textStr); + model.setEOL(EndOfLineSequence.LF); + + // Add markers + markers.forEach((m) => { + let modelMarkerId = model._addMarker(0, m.lineNumber, m.column, m.stickToPreviousCharacter); + markerId2ModelMarkerId[m.id] = modelMarkerId; + }); + + // Apply edits & collect inverse edits + model.applyEdits(edits); + model._assertLineNumbersOK(); + + // Assert edits produced expected result + assert.deepEqual(model.getValue(EndOfLinePreference.LF), expectedTextStr); + + let actualChangedMarkers: string[] = []; + for (let i = 0, len = expectedMarkers.length; i < len; i++) { + let expectedMarker = expectedMarkers[i]; + let initialMarker = markersMap[expectedMarker.id]; + let expectedMarkerModelMarkerId = markerId2ModelMarkerId[expectedMarker.id]; + let actualMarker = model._getMarker(expectedMarkerModelMarkerId); + + if (actualMarker.lineNumber !== initialMarker.lineNumber || actualMarker.column !== initialMarker.column) { + actualChangedMarkers.push(initialMarker.id); + } + + assert.equal(actualMarker.lineNumber, expectedMarker.lineNumber, 'marker lineNumber of marker ' + expectedMarker.id); + assert.equal(actualMarker.column, expectedMarker.column, 'marker column of marker ' + expectedMarker.id); + } + + changedMarkers.sort(); + actualChangedMarkers.sort(); + assert.deepEqual(actualChangedMarkers, changedMarkers, 'changed markers'); + + model.dispose(); + } + + test('no markers changed', () => { + testApplyEditsAndMarkers( + [ + 'Hello world,', + 'this is a short text', + 'that is used in testing' + ], + [ + marker('a', 1, 1, true), + marker('b', 1, 1, false), + marker('c', 1, 7, false), + marker('d', 1, 12, true), + marker('e', 2, 1, false), + marker('f', 2, 16, true), + marker('g', 2, 21, true), + marker('h', 3, 24, false) + ], + [ + editOp(1, 13, 1, 13, [' how are you?']) + ], + [], + [ + 'Hello world, how are you?', + 'this is a short text', + 'that is used in testing' + ], + [ + marker('a', 1, 1, true), + marker('b', 1, 1, false), + marker('c', 1, 7, false), + marker('d', 1, 12, true), + marker('e', 2, 1, false), + marker('f', 2, 16, true), + marker('g', 2, 21, true), + marker('h', 3, 24, false) + ] + ); + }); + + test('first line changes', () => { + testApplyEditsAndMarkers( + [ + 'Hello world,', + 'this is a short text', + 'that is used in testing' + ], + [ + marker('a', 1, 1, true), + marker('b', 1, 1, false), + marker('c', 1, 7, false), + marker('d', 1, 12, true), + marker('e', 2, 1, false), + marker('f', 2, 16, true), + marker('g', 2, 21, true), + marker('h', 3, 24, false) + ], + [ + editOp(1, 7, 1, 12, ['friends']) + ], + [], + [ + 'Hello friends,', + 'this is a short text', + 'that is used in testing' + ], + [ + marker('a', 1, 1, true), + marker('b', 1, 1, false), + marker('c', 1, 7, false), + marker('d', 1, 12, true), + marker('e', 2, 1, false), + marker('f', 2, 16, true), + marker('g', 2, 21, true), + marker('h', 3, 24, false) + ] + ); + }); + + test('inserting lines', () => { + testApplyEditsAndMarkers( + [ + 'Hello world,', + 'this is a short text', + 'that is used in testing' + ], + [ + marker('a', 1, 1, true), + marker('b', 1, 1, false), + marker('c', 1, 7, false), + marker('d', 1, 12, true), + marker('e', 2, 1, false), + marker('f', 2, 16, true), + marker('g', 2, 21, true), + marker('h', 3, 24, false) + ], + [ + editOp(1, 7, 1, 12, ['friends']), + editOp(1, 13, 1, 13, ['', 'this is an inserted line', 'and another one. By the way,']) + ], + ['e', 'f', 'g', 'h'], + [ + 'Hello friends,', + 'this is an inserted line', + 'and another one. By the way,', + 'this is a short text', + 'that is used in testing' + ], + [ + marker('a', 1, 1, true), + marker('b', 1, 1, false), + marker('c', 1, 7, false), + marker('d', 1, 12, true), + marker('e', 4, 1, false), + marker('f', 4, 16, true), + marker('g', 4, 21, true), + marker('h', 5, 24, false) + ] + ); + }); + + test('replacing a lot', () => { + testApplyEditsAndMarkers( + [ + 'Hello world,', + 'this is a short text', + 'that is used in testing', + 'more lines...', + 'more lines...', + 'more lines...', + 'more lines...' + ], + [ + marker('a', 1, 1, true), + marker('b', 1, 1, false), + marker('c', 1, 7, false), + marker('d', 1, 12, true), + marker('e', 2, 1, false), + marker('f', 2, 16, true), + marker('g', 2, 21, true), + marker('h', 3, 24, false), + marker('i', 5, 1, false), + marker('j', 6, 1, false), + marker('k', 7, 14, false), + ], + [ + editOp(1, 7, 1, 12, ['friends']), + editOp(1, 13, 1, 13, ['', 'this is an inserted line', 'and another one. By the way,', 'This is another line']), + editOp(2, 1, 7, 14, ['Some new text here']) + ], + ['e', 'f', 'g', 'h', 'i', 'j', 'k'], + [ + 'Hello friends,', + 'this is an inserted line', + 'and another one. By the way,', + 'This is another line', + 'Some new text here' + ], + [ + marker('a', 1, 1, true), + marker('b', 1, 1, false), + marker('c', 1, 7, false), + marker('d', 1, 12, true), + marker('e', 5, 1, false), + marker('f', 5, 16, true), + marker('g', 5, 19, true), + marker('h', 5, 19, false), + marker('i', 5, 19, false), + marker('j', 5, 19, false), + marker('k', 5, 19, false), + ] + ); + }); +}); diff --git a/src/vs/editor/test/common/model/editableTextModelAuto.test.ts b/src/vs/editor/test/common/model/editableTextModelAuto.test.ts new file mode 100644 index 0000000000..51e7e8e66b --- /dev/null +++ b/src/vs/editor/test/common/model/editableTextModelAuto.test.ts @@ -0,0 +1,300 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; +import { testApplyEditsWithSyncedModels } from 'vs/editor/test/common/model/editableTextModelTestUtils'; +import { CharCode } from 'vs/base/common/charCode'; + +const GENERATE_TESTS = false; + +suite('EditorModel Auto Tests', () => { + function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): IIdentifiedSingleEditOperation { + return { + identifier: null, + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + text: text.join('\n'), + forceMoveMarkers: false + }; + } + + test('auto1', () => { + testApplyEditsWithSyncedModels( + [ + 'ioe', + '', + 'yjct', + '', + '', + ], + [ + editOp(1, 2, 1, 2, ['b', 'r', 'fq']), + editOp(1, 4, 2, 1, ['', '']), + ], + [ + 'ib', + 'r', + 'fqoe', + '', + 'yjct', + '', + '', + ] + ); + }); + + test('auto2', () => { + testApplyEditsWithSyncedModels( + [ + 'f', + 'littnhskrq', + 'utxvsizqnk', + 'lslqz', + 'jxn', + 'gmm', + ], + [ + editOp(1, 2, 1, 2, ['', 'o']), + editOp(2, 4, 2, 4, ['zaq', 'avb']), + editOp(2, 5, 6, 2, ['jlr', 'zl', 'j']), + ], + [ + 'f', + 'o', + 'litzaq', + 'avbtjlr', + 'zl', + 'jmm', + ] + ); + }); + + test('auto3', () => { + testApplyEditsWithSyncedModels( + [ + 'ofw', + 'qsxmziuvzw', + 'rp', + 'qsnymek', + 'elth', + 'wmgzbwudxz', + 'iwsdkndh', + 'bujlbwb', + 'asuouxfv', + 'xuccnb', + ], + [ + editOp(4, 3, 4, 3, ['']), + ], + [ + 'ofw', + 'qsxmziuvzw', + 'rp', + 'qsnymek', + 'elth', + 'wmgzbwudxz', + 'iwsdkndh', + 'bujlbwb', + 'asuouxfv', + 'xuccnb', + ] + ); + }); + + test('auto4', () => { + testApplyEditsWithSyncedModels( + [ + 'fefymj', + 'qum', + 'vmiwxxaiqq', + 'dz', + 'lnqdgorosf', + ], + [ + editOp(1, 3, 1, 5, ['hp']), + editOp(1, 7, 2, 1, ['kcg', '', 'mpx']), + editOp(2, 2, 2, 2, ['', 'aw', '']), + editOp(2, 2, 2, 2, ['vqr', 'mo']), + editOp(4, 2, 5, 3, ['xyc']), + ], + [ + 'fehpmjkcg', + '', + 'mpxq', + 'aw', + 'vqr', + 'moum', + 'vmiwxxaiqq', + 'dxycqdgorosf', + ] + ); + }); +}); + +function getRandomInt(min: number, max: number): number { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function getRandomString(minLength: number, maxLength: number): string { + let length = getRandomInt(minLength, maxLength); + let r = ''; + for (let i = 0; i < length; i++) { + r += String.fromCharCode(getRandomInt(CharCode.a, CharCode.z)); + } + return r; +} + +function generateFile(small: boolean): string { + let lineCount = getRandomInt(1, small ? 3 : 10); + let lines: string[] = []; + for (let i = 0; i < lineCount; i++) { + lines.push(getRandomString(0, small ? 3 : 10)); + } + return lines.join('\n'); +} + +function generateEdits(content: string): ITestModelEdit[] { + + let result: ITestModelEdit[] = []; + let cnt = getRandomInt(1, 5); + + let maxOffset = content.length; + + while (cnt > 0 && maxOffset > 0) { + + let offset = getRandomInt(0, maxOffset); + let length = getRandomInt(0, maxOffset - offset); + let text = generateFile(true); + + result.push({ + offset: offset, + length: length, + text: text + }); + + maxOffset = offset; + cnt--; + } + + result.reverse(); + + return result; +} + +interface ITestModelEdit { + offset: number; + length: number; + text: string; +} + +class TestModel { + + public initialContent: string; + public resultingContent: string; + public edits: IIdentifiedSingleEditOperation[]; + + private static _generateOffsetToPosition(content: string): Position[] { + let result: Position[] = []; + let lineNumber = 1; + let column = 1; + + for (let offset = 0, len = content.length; offset <= len; offset++) { + let ch = content.charAt(offset); + + result[offset] = new Position(lineNumber, column); + + if (ch === '\n') { + lineNumber++; + column = 1; + } else { + column++; + } + } + + return result; + } + + constructor() { + this.initialContent = generateFile(false); + + let edits = generateEdits(this.initialContent); + + let offsetToPosition = TestModel._generateOffsetToPosition(this.initialContent); + this.edits = []; + for (let i = 0; i < edits.length; i++) { + let startPosition = offsetToPosition[edits[i].offset]; + let endPosition = offsetToPosition[edits[i].offset + edits[i].length]; + this.edits.push({ + identifier: null, + range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column), + text: edits[i].text, + forceMoveMarkers: false + }); + } + + this.resultingContent = this.initialContent; + for (let i = edits.length - 1; i >= 0; i--) { + this.resultingContent = ( + this.resultingContent.substring(0, edits[i].offset) + + edits[i].text + + this.resultingContent.substring(edits[i].offset + edits[i].length) + ); + } + } + + public print(): string { + let r: string[] = []; + r.push('testApplyEditsWithSyncedModels('); + r.push('\t['); + let initialLines = this.initialContent.split('\n'); + r = r.concat(initialLines.map((i) => `\t\t'${i}',`)); + r.push('\t],'); + r.push('\t['); + r = r.concat(this.edits.map((i) => { + let text = `['` + i.text.split('\n').join(`', '`) + `']`; + return `\t\teditOp(${i.range.startLineNumber}, ${i.range.startColumn}, ${i.range.endLineNumber}, ${i.range.endColumn}, ${text}),`; + })); + r.push('\t],'); + r.push('\t['); + let resultLines = this.resultingContent.split('\n'); + r = r.concat(resultLines.map((i) => `\t\t'${i}',`)); + r.push('\t]'); + r.push(');'); + + return r.join('\n'); + } +} + +if (GENERATE_TESTS) { + let number = 1; + while (true) { + + console.log('------BEGIN NEW TEST: ' + number); + + let testModel = new TestModel(); + + // console.log(testModel.print()); + + console.log('------END NEW TEST: ' + (number++)); + + try { + testApplyEditsWithSyncedModels( + testModel.initialContent.split('\n'), + testModel.edits, + testModel.resultingContent.split('\n') + ); + // throw new Error('a'); + } catch (err) { + console.log(err); + console.log(testModel.print()); + break; + } + + // break; + } + +} diff --git a/src/vs/editor/test/common/model/editableTextModelTestUtils.ts b/src/vs/editor/test/common/model/editableTextModelTestUtils.ts new file mode 100644 index 0000000000..600b248b49 --- /dev/null +++ b/src/vs/editor/test/common/model/editableTextModelTestUtils.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditableTextModel } from 'vs/editor/common/model/editableTextModel'; +import { MirrorModel } from 'vs/editor/common/model/mirrorModel'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { Position } from 'vs/editor/common/core/position'; +import { RawTextSource } from 'vs/editor/common/model/textSource'; +import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; + +export function testApplyEditsWithSyncedModels(original: string[], edits: editorCommon.IIdentifiedSingleEditOperation[], expected: string[], inputEditsAreInvalid: boolean = false): void { + var originalStr = original.join('\n'); + var expectedStr = expected.join('\n'); + + assertSyncedModels(originalStr, (model, assertMirrorModels) => { + // Apply edits & collect inverse edits + var inverseEdits = model.applyEdits(edits); + + // Assert edits produced expected result + assert.deepEqual(model.getValue(editorCommon.EndOfLinePreference.LF), expectedStr); + + assertMirrorModels(); + + // Apply the inverse edits + var inverseInverseEdits = model.applyEdits(inverseEdits); + + // Assert the inverse edits brought back model to original state + assert.deepEqual(model.getValue(editorCommon.EndOfLinePreference.LF), originalStr); + + if (!inputEditsAreInvalid) { + // Assert the inverse of the inverse edits are the original edits + assert.deepEqual(inverseInverseEdits, edits); + } + + assertMirrorModels(); + }); +} + +const enum AssertDocumentLineMappingDirection { + OffsetToPosition, + PositionToOffset +} + +function assertOneDirectionLineMapping(model: TextModel, direction: AssertDocumentLineMappingDirection, msg: string): void { + let allText = model.getValue(); + + let line = 1, column = 1, previousIsCarriageReturn = false; + for (let offset = 0; offset <= allText.length; offset++) { + // The position coordinate system cannot express the position between \r and \n + let position = new Position(line, column + (previousIsCarriageReturn ? -1 : 0)); + + if (direction === AssertDocumentLineMappingDirection.OffsetToPosition) { + let actualPosition = model.getPositionAt(offset); + assert.equal(actualPosition.toString(), position.toString(), msg + ' - getPositionAt mismatch for offset ' + offset); + } else { + // The position coordinate system cannot express the position between \r and \n + let expectedOffset = offset + (previousIsCarriageReturn ? -1 : 0); + let actualOffset = model.getOffsetAt(position); + assert.equal(actualOffset, expectedOffset, msg + ' - getOffsetAt mismatch for position ' + position.toString()); + } + + if (allText.charAt(offset) === '\n') { + line++; + column = 1; + } else { + column++; + } + + previousIsCarriageReturn = (allText.charAt(offset) === '\r'); + } +} + +function assertLineMapping(model: TextModel, msg: string): void { + assertOneDirectionLineMapping(model, AssertDocumentLineMappingDirection.PositionToOffset, msg); + assertOneDirectionLineMapping(model, AssertDocumentLineMappingDirection.OffsetToPosition, msg); +} + + +export function assertSyncedModels(text: string, callback: (model: EditableTextModel, assertMirrorModels: () => void) => void, setup: (model: EditableTextModel) => void = null): void { + var model = new EditableTextModel(RawTextSource.fromString(text), TextModel.DEFAULT_CREATION_OPTIONS, null); + model.setEOL(editorCommon.EndOfLineSequence.LF); + assertLineMapping(model, 'model'); + + if (setup) { + setup(model); + assertLineMapping(model, 'model'); + } + + var mirrorModel2 = new MirrorModel(null, model.getLinesContent(), model.getEOL(), model.getVersionId()); + var mirrorModel2PrevVersionId = model.getVersionId(); + + model.onDidChangeContent((e: IModelContentChangedEvent) => { + let versionId = e.versionId; + if (versionId < mirrorModel2PrevVersionId) { + console.warn('Model version id did not advance between edits (2)'); + } + mirrorModel2PrevVersionId = versionId; + mirrorModel2.onEvents(e); + }); + + var assertMirrorModels = () => { + assertLineMapping(model, 'model'); + model._assertLineNumbersOK(); + assert.equal(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK'); + assert.equal(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK'); + }; + + callback(model, assertMirrorModels); + + model.dispose(); + mirrorModel2.dispose(); +} diff --git a/src/vs/editor/test/common/model/indentRanges.test.ts b/src/vs/editor/test/common/model/indentRanges.test.ts new file mode 100644 index 0000000000..22561b1051 --- /dev/null +++ b/src/vs/editor/test/common/model/indentRanges.test.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { Model } from 'vs/editor/common/model/model'; +import { computeRanges } from 'vs/editor/common/model/indentRanges'; + +export interface IndentRange { + startLineNumber: number; + endLineNumber: number; + indent: number; +} + +suite('Indentation Folding', () => { + function assertRanges(lines: string[], expected: IndentRange[]): void { + let model = Model.createFromString(lines.join('\n')); + let actual = computeRanges(model); + actual.sort((r1, r2) => r1.startLineNumber - r2.startLineNumber); + assert.deepEqual(actual, expected); + model.dispose(); + } + + function r(startLineNumber: number, endLineNumber: number, indent: number): IndentRange { + return { startLineNumber, endLineNumber, indent }; + } + + test('Fold one level', () => { + assertRanges([ + 'A', + ' A', + ' A', + ' A' + ], [r(1, 4, 0)]); + }); + + test('Fold two levels', () => { + assertRanges([ + 'A', + ' A', + ' A', + ' A', + ' A' + ], [r(1, 5, 0), r(3, 5, 2)]); + }); + + test('Fold three levels', () => { + assertRanges([ + 'A', + ' A', + ' A', + ' A', + 'A' + ], [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)]); + }); + + test('Fold decreasing indent', () => { + assertRanges([ + ' A', + ' A', + 'A' + ], []); + }); + + test('Fold Java', () => { + assertRanges([ + /* 1*/ 'class A {', + /* 2*/ ' void foo() {', + /* 3*/ ' console.log();', + /* 4*/ ' console.log();', + /* 5*/ ' }', + /* 6*/ '', + /* 7*/ ' void bar() {', + /* 8*/ ' console.log();', + /* 9*/ ' }', + /*10*/ '}', + /*11*/ 'interface B {', + /*12*/ ' void bar();', + /*13*/ '}', + ], [r(1, 9, 0), r(2, 4, 2), r(7, 8, 2), r(11, 12, 0)]); + }); + + test('Fold Javadoc', () => { + assertRanges([ + /* 1*/ '/**', + /* 2*/ ' * Comment', + /* 3*/ ' */', + /* 4*/ 'class A {', + /* 5*/ ' void foo() {', + /* 6*/ ' }', + /* 7*/ '}', + ], [r(1, 3, 0), r(4, 6, 0)]); + }); + test('Fold Whitespace', () => { + assertRanges([ + /* 1*/ 'class A {', + /* 2*/ '', + /* 3*/ ' void foo() {', + /* 4*/ ' ', + /* 5*/ ' return 0;', + /* 6*/ ' }', + /* 7*/ ' ', + /* 8*/ '}', + ], [r(1, 7, 0), r(3, 5, 2)]); + }); + + test('Fold Tabs', () => { + assertRanges([ + /* 1*/ 'class A {', + /* 2*/ '\t\t', + /* 3*/ '\tvoid foo() {', + /* 4*/ '\t \t//hello', + /* 5*/ '\t return 0;', + /* 6*/ ' \t}', + /* 7*/ ' ', + /* 8*/ '}', + ], [r(1, 7, 0), r(3, 5, 4)]); + }); +}); diff --git a/src/vs/editor/test/common/model/model.line.test.ts b/src/vs/editor/test/common/model/model.line.test.ts new file mode 100644 index 0000000000..434a030eee --- /dev/null +++ b/src/vs/editor/test/common/model/model.line.test.ts @@ -0,0 +1,2331 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { ModelLine, ILineEdit, LineMarker, MarkersTracker } from 'vs/editor/common/model/modelLine'; +import { MetadataConsts } from 'vs/editor/common/modes'; +import { Position } from 'vs/editor/common/core/position'; +import { ViewLineToken, ViewLineTokenFactory } from 'vs/editor/common/core/viewLineToken'; + +function assertLineTokens(_actual: LineTokens, _expected: TestToken[]): void { + let expected = ViewLineTokenFactory.inflateArr(TestToken.toTokens(_expected), _actual.getLineLength()); + let actual = _actual.inflate(); + let decode = (token: ViewLineToken) => { + return { + endIndex: token.endIndex, + type: token.getType() + }; + }; + assert.deepEqual(actual.map(decode), expected.map(decode)); +} + +const NO_TAB_SIZE = 0; + +suite('ModelLine - getIndentLevel', () => { + function assertIndentLevel(text: string, expected: number, tabSize: number = 4): void { + let modelLine = new ModelLine(text, tabSize); + let actual = modelLine.getIndentLevel(); + assert.equal(actual, expected, text); + } + + test('getIndentLevel', () => { + assertIndentLevel('', -1); + assertIndentLevel(' ', -1); + assertIndentLevel(' \t', -1); + assertIndentLevel('Hello', 0); + assertIndentLevel(' Hello', 1); + assertIndentLevel(' Hello', 3); + assertIndentLevel('\tHello', 4); + assertIndentLevel(' \tHello', 4); + assertIndentLevel(' \tHello', 4); + assertIndentLevel(' \tHello', 4); + assertIndentLevel(' \tHello', 8); + assertIndentLevel(' \tHello', 8); + assertIndentLevel('\t Hello', 5); + assertIndentLevel('\t \tHello', 8); + }); +}); + +suite('Editor Model - modelLine.applyEdits text', () => { + + function testEdits(initial: string, edits: ILineEdit[], expected: string): void { + var line = new ModelLine(initial, NO_TAB_SIZE); + line.applyEdits(new MarkersTracker(), edits, NO_TAB_SIZE); + assert.equal(line.text, expected); + } + + function editOp(startColumn: number, endColumn: number, text: string): ILineEdit { + return { + startColumn: startColumn, + endColumn: endColumn, + text: text, + forceMoveMarkers: false + }; + } + + test('single insert 1', () => { + testEdits( + '', + [ + editOp(1, 1, 'Hello world') + ], + 'Hello world' + ); + }); + + test('single insert 2', () => { + testEdits( + 'Hworld', + [ + editOp(2, 2, 'ello ') + ], + 'Hello world' + ); + }); + + test('multiple inserts 1', () => { + testEdits( + 'Hw', + [ + editOp(2, 2, 'ello '), + editOp(3, 3, 'orld') + ], + 'Hello world' + ); + }); + + test('multiple inserts 2', () => { + testEdits( + 'Hw,', + [ + editOp(2, 2, 'ello '), + editOp(3, 3, 'orld'), + editOp(4, 4, ' this is H.A.L.') + ], + 'Hello world, this is H.A.L.' + ); + }); + + test('single delete 1', () => { + testEdits( + 'Hello world', + [ + editOp(1, 12, '') + ], + '' + ); + }); + + test('single delete 2', () => { + testEdits( + 'Hello world', + [ + editOp(2, 7, '') + ], + 'Hworld' + ); + }); + + test('multiple deletes 1', () => { + testEdits( + 'Hello world', + [ + editOp(2, 7, ''), + editOp(8, 12, '') + ], + 'Hw' + ); + }); + + test('multiple deletes 2', () => { + testEdits( + 'Hello world, this is H.A.L.', + [ + editOp(2, 7, ''), + editOp(8, 12, ''), + editOp(13, 28, '') + ], + 'Hw,' + ); + }); + + test('single replace 1', () => { + testEdits( + '', + [ + editOp(1, 1, 'Hello world') + ], + 'Hello world' + ); + }); + + test('single replace 2', () => { + testEdits( + 'H1234world', + [ + editOp(2, 6, 'ello ') + ], + 'Hello world' + ); + }); + + test('multiple replace 1', () => { + testEdits( + 'H123w321', + [ + editOp(2, 5, 'ello '), + editOp(6, 9, 'orld') + ], + 'Hello world' + ); + }); + + test('multiple replace 2', () => { + testEdits( + 'H1w12,123', + [ + editOp(2, 3, 'ello '), + editOp(4, 6, 'orld'), + editOp(7, 10, ' this is H.A.L.') + ], + 'Hello world, this is H.A.L.' + ); + }); +}); + +suite('Editor Model - modelLine.split text', () => { + + function testLineSplit(initial: string, splitColumn: number, expected1: string, expected2: string): void { + var line = new ModelLine(initial, NO_TAB_SIZE); + var newLine = line.split(new MarkersTracker(), splitColumn, false, NO_TAB_SIZE); + assert.equal(line.text, expected1); + assert.equal(newLine.text, expected2); + } + + test('split at the beginning', () => { + testLineSplit( + 'qwerty', + 1, + '', + 'qwerty' + ); + }); + + test('split at the end', () => { + testLineSplit( + 'qwerty', + 7, + 'qwerty', + '' + ); + }); + + test('split in the middle', () => { + testLineSplit( + 'qwerty', + 3, + 'qw', + 'erty' + ); + }); +}); + +suite('Editor Model - modelLine.append text', () => { + + function testLineAppend(a: string, b: string, expected: string): void { + var line1 = new ModelLine(a, NO_TAB_SIZE); + var line2 = new ModelLine(b, NO_TAB_SIZE); + line1.append(new MarkersTracker(), 1, line2, NO_TAB_SIZE); + assert.equal(line1.text, expected); + } + + test('append at the beginning', () => { + testLineAppend( + '', + 'qwerty', + 'qwerty' + ); + }); + + test('append at the end', () => { + testLineAppend( + 'qwerty', + '', + 'qwerty' + ); + }); + + test('append in the middle', () => { + testLineAppend( + 'qw', + 'erty', + 'qwerty' + ); + }); +}); + +class TestToken { + public readonly startOffset: number; + public readonly color: number; + + constructor(startOffset: number, color: number) { + this.startOffset = startOffset; + this.color = color; + } + + public static toTokens(tokens: TestToken[]): Uint32Array { + if (tokens === null) { + return null; + } + let tokensLen = tokens.length; + let result = new Uint32Array((tokensLen << 1)); + for (let i = 0; i < tokensLen; i++) { + let token = tokens[i]; + result[(i << 1)] = token.startOffset; + result[(i << 1) + 1] = ( + token.color << MetadataConsts.FOREGROUND_OFFSET + ) >>> 0; + } + return result; + } +} + +suite('Editor Model - modelLine.applyEdits text & tokens', () => { + + + function testLineEditTokens(initialText: string, initialTokens: TestToken[], edits: ILineEdit[], expectedText: string, expectedTokens: TestToken[]): void { + let line = new ModelLine(initialText, NO_TAB_SIZE); + line.setTokens(0, TestToken.toTokens(initialTokens)); + + line.applyEdits(new MarkersTracker(), edits, NO_TAB_SIZE); + + assert.equal(line.text, expectedText); + assertLineTokens(line.getTokens(0), expectedTokens); + } + + test('insertion on empty line', () => { + let line = new ModelLine('some text', NO_TAB_SIZE); + line.setTokens(0, TestToken.toTokens([new TestToken(0, 1)])); + + line.applyEdits(new MarkersTracker(), [{ startColumn: 1, endColumn: 10, text: '', forceMoveMarkers: false }], NO_TAB_SIZE); + line.setTokens(0, new Uint32Array(0)); + + line.applyEdits(new MarkersTracker(), [{ startColumn: 1, endColumn: 1, text: 'a', forceMoveMarkers: false }], NO_TAB_SIZE); + assertLineTokens(line.getTokens(0), [new TestToken(0, 1)]); + }); + + test('updates tokens on insertion 1', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 1, + endColumn: 1, + text: 'a', + forceMoveMarkers: false + }], + 'aabcd efgh', + [ + new TestToken(0, 1), + new TestToken(5, 2), + new TestToken(6, 3) + ] + ); + }); + + test('updates tokens on insertion 2', () => { + testLineEditTokens( + 'aabcd efgh', + [ + new TestToken(0, 1), + new TestToken(5, 2), + new TestToken(6, 3) + ], + [{ + startColumn: 2, + endColumn: 2, + text: 'x', + forceMoveMarkers: false + }], + 'axabcd efgh', + [ + new TestToken(0, 1), + new TestToken(6, 2), + new TestToken(7, 3) + ] + ); + }); + + test('updates tokens on insertion 3', () => { + testLineEditTokens( + 'axabcd efgh', + [ + new TestToken(0, 1), + new TestToken(6, 2), + new TestToken(7, 3) + ], + [{ + startColumn: 3, + endColumn: 3, + text: 'stu', + forceMoveMarkers: false + }], + 'axstuabcd efgh', + [ + new TestToken(0, 1), + new TestToken(9, 2), + new TestToken(10, 3) + ] + ); + }); + + test('updates tokens on insertion 4', () => { + testLineEditTokens( + 'axstuabcd efgh', + [ + new TestToken(0, 1), + new TestToken(9, 2), + new TestToken(10, 3) + ], + [{ + startColumn: 10, + endColumn: 10, + text: '\t', + forceMoveMarkers: false + }], + 'axstuabcd\t efgh', + [ + new TestToken(0, 1), + new TestToken(10, 2), + new TestToken(11, 3) + ] + ); + }); + + test('updates tokens on insertion 5', () => { + testLineEditTokens( + 'axstuabcd\t efgh', + [ + new TestToken(0, 1), + new TestToken(10, 2), + new TestToken(11, 3) + ], + [{ + startColumn: 12, + endColumn: 12, + text: 'dd', + forceMoveMarkers: false + }], + 'axstuabcd\t ddefgh', + [ + new TestToken(0, 1), + new TestToken(10, 2), + new TestToken(13, 3) + ] + ); + }); + + test('updates tokens on insertion 6', () => { + testLineEditTokens( + 'axstuabcd\t ddefgh', + [ + new TestToken(0, 1), + new TestToken(10, 2), + new TestToken(13, 3) + ], + [{ + startColumn: 18, + endColumn: 18, + text: 'xyz', + forceMoveMarkers: false + }], + 'axstuabcd\t ddefghxyz', + [ + new TestToken(0, 1), + new TestToken(10, 2), + new TestToken(13, 3) + ] + ); + }); + + test('updates tokens on insertion 7', () => { + testLineEditTokens( + 'axstuabcd\t ddefghxyz', + [ + new TestToken(0, 1), + new TestToken(10, 2), + new TestToken(13, 3) + ], + [{ + startColumn: 1, + endColumn: 1, + text: 'x', + forceMoveMarkers: false + }], + 'xaxstuabcd\t ddefghxyz', + [ + new TestToken(0, 1), + new TestToken(11, 2), + new TestToken(14, 3) + ] + ); + }); + + test('updates tokens on insertion 8', () => { + testLineEditTokens( + 'xaxstuabcd\t ddefghxyz', + [ + new TestToken(0, 1), + new TestToken(11, 2), + new TestToken(14, 3) + ], + [{ + startColumn: 22, + endColumn: 22, + text: 'x', + forceMoveMarkers: false + }], + 'xaxstuabcd\t ddefghxyzx', + [ + new TestToken(0, 1), + new TestToken(11, 2), + new TestToken(14, 3) + ] + ); + }); + + test('updates tokens on insertion 9', () => { + testLineEditTokens( + 'xaxstuabcd\t ddefghxyzx', + [ + new TestToken(0, 1), + new TestToken(11, 2), + new TestToken(14, 3) + ], + [{ + startColumn: 2, + endColumn: 2, + text: '', + forceMoveMarkers: false + }], + 'xaxstuabcd\t ddefghxyzx', + [ + new TestToken(0, 1), + new TestToken(11, 2), + new TestToken(14, 3) + ] + ); + }); + + test('updates tokens on insertion 10', () => { + testLineEditTokens( + '', + null, + [{ + startColumn: 1, + endColumn: 1, + text: 'a', + forceMoveMarkers: false + }], + 'a', + [ + new TestToken(0, 1) + ] + ); + }); + + test('delete second token 2', () => { + testLineEditTokens( + 'abcdefghij', + [ + new TestToken(0, 1), + new TestToken(3, 2), + new TestToken(6, 3) + ], + [{ + startColumn: 4, + endColumn: 7, + text: '', + forceMoveMarkers: false + }], + 'abcghij', + [ + new TestToken(0, 1), + new TestToken(3, 3) + ] + ); + }); + + test('insert right before second token', () => { + testLineEditTokens( + 'abcdefghij', + [ + new TestToken(0, 1), + new TestToken(3, 2), + new TestToken(6, 3) + ], + [{ + startColumn: 4, + endColumn: 4, + text: 'hello', + forceMoveMarkers: false + }], + 'abchellodefghij', + [ + new TestToken(0, 1), + new TestToken(8, 2), + new TestToken(11, 3) + ] + ); + }); + + test('delete first char', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 1, + endColumn: 2, + text: '', + forceMoveMarkers: false + }], + 'bcd efgh', + [ + new TestToken(0, 1), + new TestToken(3, 2), + new TestToken(4, 3) + ] + ); + }); + + test('delete 2nd and 3rd chars', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 2, + endColumn: 4, + text: '', + forceMoveMarkers: false + }], + 'ad efgh', + [ + new TestToken(0, 1), + new TestToken(2, 2), + new TestToken(3, 3) + ] + ); + }); + + test('delete first token', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 1, + endColumn: 5, + text: '', + forceMoveMarkers: false + }], + ' efgh', + [ + new TestToken(0, 2), + new TestToken(1, 3) + ] + ); + }); + + test('delete second token', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 5, + endColumn: 6, + text: '', + forceMoveMarkers: false + }], + 'abcdefgh', + [ + new TestToken(0, 1), + new TestToken(4, 3) + ] + ); + }); + + test('delete second token + a bit of the third one', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 5, + endColumn: 7, + text: '', + forceMoveMarkers: false + }], + 'abcdfgh', + [ + new TestToken(0, 1), + new TestToken(4, 3) + ] + ); + }); + + test('delete second and third token', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 5, + endColumn: 10, + text: '', + forceMoveMarkers: false + }], + 'abcd', + [ + new TestToken(0, 1) + ] + ); + }); + + test('delete everything', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 1, + endColumn: 10, + text: '', + forceMoveMarkers: false + }], + '', + [ + new TestToken(0, 3) + ] + ); + }); + + test('noop', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 1, + endColumn: 1, + text: '', + forceMoveMarkers: false + }], + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ] + ); + }); + + test('equivalent to deleting first two chars', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 1, + endColumn: 3, + text: '', + forceMoveMarkers: false + }], + 'cd efgh', + [ + new TestToken(0, 1), + new TestToken(2, 2), + new TestToken(3, 3) + ] + ); + }); + + test('equivalent to deleting from 5 to the end', () => { + testLineEditTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + [{ + startColumn: 5, + endColumn: 10, + text: '', + forceMoveMarkers: false + }], + 'abcd', + [ + new TestToken(0, 1) + ] + ); + }); + + test('updates tokens on replace 1', () => { + testLineEditTokens( + 'Hello world, ciao', + [ + new TestToken(0, 1), + new TestToken(5, 0), + new TestToken(6, 2), + new TestToken(11, 0), + new TestToken(13, 0) + ], + [{ + startColumn: 1, + endColumn: 6, + text: 'Hi', + forceMoveMarkers: false + }], + 'Hi world, ciao', + [ + new TestToken(0, 1), + new TestToken(2, 0), + new TestToken(3, 2), + new TestToken(8, 0), + new TestToken(10, 0), + ] + ); + }); + + test('updates tokens on replace 2', () => { + testLineEditTokens( + 'Hello world, ciao', + [ + new TestToken(0, 1), + new TestToken(5, 0), + new TestToken(6, 2), + new TestToken(11, 0), + new TestToken(13, 0), + ], + [{ + startColumn: 1, + endColumn: 6, + text: 'Hi', + forceMoveMarkers: false + }, { + startColumn: 8, + endColumn: 12, + text: 'my friends', + forceMoveMarkers: false + }], + 'Hi wmy friends, ciao', + [ + new TestToken(0, 1), + new TestToken(2, 0), + new TestToken(3, 2), + new TestToken(14, 0), + new TestToken(16, 0), + ] + ); + }); +}); + +suite('Editor Model - modelLine.split text & tokens', () => { + function testLineSplitTokens(initialText: string, initialTokens: TestToken[], splitColumn: number, expectedText1: string, expectedText2: string, expectedTokens: TestToken[]): void { + let line = new ModelLine(initialText, NO_TAB_SIZE); + line.setTokens(0, TestToken.toTokens(initialTokens)); + + let other = line.split(new MarkersTracker(), splitColumn, false, NO_TAB_SIZE); + + assert.equal(line.text, expectedText1); + assert.equal(other.text, expectedText2); + assertLineTokens(line.getTokens(0), expectedTokens); + } + + test('split at the beginning', () => { + testLineSplitTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + 1, + '', + 'abcd efgh', + [ + new TestToken(0, 1), + ] + ); + }); + + test('split at the end', () => { + testLineSplitTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + 10, + 'abcd efgh', + '', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ] + ); + }); + + test('split inthe middle 1', () => { + testLineSplitTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + 5, + 'abcd', + ' efgh', + [ + new TestToken(0, 1) + ] + ); + }); + + test('split inthe middle 2', () => { + testLineSplitTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + 6, + 'abcd ', + 'efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2) + ] + ); + }); +}); + +suite('Editor Model - modelLine.append text & tokens', () => { + function testLineAppendTokens(aText: string, aTokens: TestToken[], bText: string, bTokens: TestToken[], expectedText: string, expectedTokens: TestToken[]): void { + let a = new ModelLine(aText, NO_TAB_SIZE); + a.setTokens(0, TestToken.toTokens(aTokens)); + + let b = new ModelLine(bText, NO_TAB_SIZE); + b.setTokens(0, TestToken.toTokens(bTokens)); + + a.append(new MarkersTracker(), 1, b, NO_TAB_SIZE); + + assert.equal(a.text, expectedText); + assertLineTokens(a.getTokens(0), expectedTokens); + } + + test('append empty 1', () => { + testLineAppendTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + '', + [], + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ] + ); + }); + + test('append empty 2', () => { + testLineAppendTokens( + '', + [], + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ] + ); + }); + + test('append 1', () => { + testLineAppendTokens( + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ], + 'abcd efgh', + [ + new TestToken(0, 4), + new TestToken(4, 5), + new TestToken(5, 6) + ], + 'abcd efghabcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3), + new TestToken(9, 4), + new TestToken(13, 5), + new TestToken(14, 6) + ] + ); + }); + + test('append 2', () => { + testLineAppendTokens( + 'abcd ', + [ + new TestToken(0, 1), + new TestToken(4, 2) + ], + 'efgh', + [ + new TestToken(0, 3) + ], + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ] + ); + }); + + test('append 3', () => { + testLineAppendTokens( + 'abcd', + [ + new TestToken(0, 1), + ], + ' efgh', + [ + new TestToken(0, 2), + new TestToken(1, 3) + ], + 'abcd efgh', + [ + new TestToken(0, 1), + new TestToken(4, 2), + new TestToken(5, 3) + ] + ); + }); +}); + +interface ILightWeightMarker { + id: string; + lineNumber: number; + column: number; + stickToPreviousCharacter: boolean; +} + +suite('Editor Model - modelLine.applyEdits text & markers', () => { + + function marker(id: number, column: number, stickToPreviousCharacter: boolean): LineMarker { + return new LineMarker(String(id), id, new Position(0, column), stickToPreviousCharacter); + } + + function toLightWeightMarker(marker: LineMarker): ILightWeightMarker { + return { + id: marker.id, + lineNumber: marker.position.lineNumber, + column: marker.position.column, + stickToPreviousCharacter: marker.stickToPreviousCharacter + }; + } + + function testLineEditMarkers(initialText: string, initialMarkers: LineMarker[], edits: ILineEdit[], expectedText: string, expectedChangedMarkers: number[], _expectedMarkers: LineMarker[]): void { + let line = new ModelLine(initialText, NO_TAB_SIZE); + line.addMarkers(initialMarkers); + + let changedMarkers = new MarkersTracker(); + line.applyEdits(changedMarkers, edits, NO_TAB_SIZE); + + assert.equal(line.text, expectedText, 'text'); + + let actualMarkers = line.getMarkers().map(toLightWeightMarker); + let expectedMarkers = _expectedMarkers.map(toLightWeightMarker); + assert.deepEqual(actualMarkers, expectedMarkers, 'markers'); + + let actualChangedMarkers = changedMarkers.getDecorationIds(); + actualChangedMarkers.sort(); + assert.deepEqual(actualChangedMarkers, expectedChangedMarkers, 'changed markers'); + } + + test('insertion: updates markers 1', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 1, + endColumn: 1, + text: 'abc', + forceMoveMarkers: false + }], + 'abcabcd efgh', + [2, 3, 4, 5, 6, 7, 8], + [ + marker(1, 1, true), + marker(2, 4, false), + marker(3, 5, true), + marker(4, 5, false), + marker(5, 8, true), + marker(6, 8, false), + marker(7, 13, true), + marker(8, 13, false) + ] + ); + }); + + test('insertion: updates markers 2', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 2, + endColumn: 2, + text: 'abc', + forceMoveMarkers: false + }], + 'aabcbcd efgh', + [4, 5, 6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 5, false), + marker(5, 8, true), + marker(6, 8, false), + marker(7, 13, true), + marker(8, 13, false) + ] + ); + }); + + test('insertion: updates markers 3', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 3, + endColumn: 3, + text: 'abc', + forceMoveMarkers: false + }], + 'ababccd efgh', + [5, 6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 8, true), + marker(6, 8, false), + marker(7, 13, true), + marker(8, 13, false) + ] + ); + }); + + test('insertion: updates markers 4', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 5, + endColumn: 5, + text: 'abc', + forceMoveMarkers: false + }], + 'abcdabc efgh', + [6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 8, false), + marker(7, 13, true), + marker(8, 13, false) + ] + ); + }); + + test('insertion: updates markers 5', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 10, + endColumn: 10, + text: 'abc', + forceMoveMarkers: false + }], + 'abcd efghabc', + [8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 13, false) + ] + ); + }); + + test('insertion bis: updates markers 1', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 1, + endColumn: 1, + text: 'a', + forceMoveMarkers: false + }], + 'aabcd efgh', + [2, 3, 4, 5, 6, 7, 8], + [ + marker(1, 1, true), + marker(2, 2, false), + marker(3, 3, true), + marker(4, 3, false), + marker(5, 6, true), + marker(6, 6, false), + marker(7, 11, true), + marker(8, 11, false) + ] + ); + }); + + test('insertion bis: updates markers 2', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 2, + endColumn: 2, + text: 'a', + forceMoveMarkers: false + }], + 'aabcd efgh', + [4, 5, 6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 3, false), + marker(5, 6, true), + marker(6, 6, false), + marker(7, 11, true), + marker(8, 11, false) + ] + ); + }); + + test('insertion bis: updates markers 3', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 3, + endColumn: 3, + text: 'a', + forceMoveMarkers: false + }], + 'abacd efgh', + [5, 6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 6, true), + marker(6, 6, false), + marker(7, 11, true), + marker(8, 11, false) + ] + ); + }); + + test('insertion bis: updates markers 4', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 5, + endColumn: 5, + text: 'a', + forceMoveMarkers: false + }], + 'abcda efgh', + [6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 6, false), + marker(7, 11, true), + marker(8, 11, false) + ] + ); + }); + + test('insertion bis: updates markers 5', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 10, + endColumn: 10, + text: 'a', + forceMoveMarkers: false + }], + 'abcd efgha', + [8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 11, false) + ] + ); + }); + + test('insertion: does not move marker at column 1', () => { + testLineEditMarkers( + 'abcd efgh', + [marker(1, 1, true)], + [{ + startColumn: 1, + endColumn: 1, + text: 'a', + forceMoveMarkers: false + }], + 'aabcd efgh', + [], + [marker(1, 1, true)] + ); + }); + + test('insertion: does move marker at column 1', () => { + testLineEditMarkers( + 'abcd efgh', + [marker(1, 1, false)], + [{ + startColumn: 1, + endColumn: 1, + text: 'a', + forceMoveMarkers: false + }], + 'aabcd efgh', + [1], + [marker(1, 2, false)] + ); + }); + + test('insertion: two markers at column 1', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + ], + [{ + startColumn: 1, + endColumn: 1, + text: 'a', + forceMoveMarkers: false + }], + 'aabcd efgh', + [2], + [ + marker(1, 1, true), + marker(2, 2, false) + ] + ); + }); + + test('insertion: two markers at column 1 unsorted', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(2, 1, false), + marker(1, 1, true), + ], + [{ + startColumn: 1, + endColumn: 1, + text: 'a', + forceMoveMarkers: false + }], + 'aabcd efgh', + [2], + [ + marker(1, 1, true), + marker(2, 2, false) + ] + ); + }); + + test('deletion: updates markers 1', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 1, + endColumn: 2, + text: '', + forceMoveMarkers: false + }], + 'bcd efgh', + [3, 4, 5, 6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 1, true), + marker(4, 1, false), + marker(5, 4, true), + marker(6, 4, false), + marker(7, 9, true), + marker(8, 9, false) + ] + ); + }); + + test('deletion: updates markers 2', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 1, + endColumn: 4, + text: '', + forceMoveMarkers: false + }], + 'd efgh', + [3, 4, 5, 6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 1, true), + marker(4, 1, false), + marker(5, 2, true), + marker(6, 2, false), + marker(7, 7, true), + marker(8, 7, false) + ] + ); + }); + + test('deletion: updates markers 3', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 5, + endColumn: 6, + text: '', + forceMoveMarkers: false + }], + 'abcdefgh', + [7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 9, true), + marker(8, 9, false) + ] + ); + }); + + test('replace: updates markers 1', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + [{ + startColumn: 1, + endColumn: 1, + text: 'a', + forceMoveMarkers: false + }, { + startColumn: 2, + endColumn: 3, + text: '', + forceMoveMarkers: false + }], + 'aacd efgh', + [2, 3, 4], + [ + marker(1, 1, true), + marker(2, 2, false), + marker(3, 3, true), + marker(4, 3, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ] + ); + }); + + test('delete near markers', () => { + testLineEditMarkers( + 'abcd', + [ + marker(1, 3, true), + marker(2, 3, false) + ], + [{ + startColumn: 3, + endColumn: 4, + text: '', + forceMoveMarkers: false + }], + 'abd', + [], + [ + marker(1, 3, true), + marker(2, 3, false) + ] + ); + }); + + test('replace: updates markers 2', () => { + testLineEditMarkers( + 'Hello world, how are you', + [ + marker(1, 1, false), + marker(2, 6, true), + marker(3, 14, false), + marker(4, 21, true) + ], + [{ + startColumn: 1, + endColumn: 1, + text: ' - ', + forceMoveMarkers: false + }, { + startColumn: 6, + endColumn: 12, + text: '', + forceMoveMarkers: false + }, { + startColumn: 22, + endColumn: 25, + text: 'things', + forceMoveMarkers: false + }], + ' - Hello, how are things', + [1, 2, 3, 4], + [ + marker(1, 4, false), + marker(2, 9, true), + marker(3, 11, false), + marker(4, 18, true) + ] + ); + }); + + test('sorts markers', () => { + testLineEditMarkers( + 'Hello world, how are you', + [ + marker(4, 21, true), + marker(2, 6, true), + marker(1, 1, false), + marker(3, 14, false) + ], + [{ + startColumn: 1, + endColumn: 1, + text: ' - ', + forceMoveMarkers: false + }, { + startColumn: 6, + endColumn: 12, + text: '', + forceMoveMarkers: false + }, { + startColumn: 22, + endColumn: 25, + text: 'things', + forceMoveMarkers: false + }], + ' - Hello, how are things', + [1, 2, 3, 4], + [ + marker(1, 4, false), + marker(2, 9, true), + marker(3, 11, false), + marker(4, 18, true) + ] + ); + }); + + test('change text inside markers', () => { + testLineEditMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 6, false), + marker(4, 10, true) + ], + [{ + startColumn: 6, + endColumn: 10, + text: '1234567', + forceMoveMarkers: false + }], + 'abcd 1234567', + [], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 6, false), + marker(4, 10, true) + ] + ); + }); + + test('inserting is different than replacing for markers part 1', () => { + testLineEditMarkers( + 'abcd', + [ + marker(1, 2, false) + ], + [{ + startColumn: 2, + endColumn: 2, + text: 'INSERT', + forceMoveMarkers: false + }], + 'aINSERTbcd', + [1], + [ + marker(1, 8, false) + ] + ); + }); + + test('inserting is different than replacing for markers part 2', () => { + testLineEditMarkers( + 'abcd', + [ + marker(1, 2, false) + ], + [{ + startColumn: 2, + endColumn: 3, + text: 'REPLACED', + forceMoveMarkers: false + }], + 'aREPLACEDcd', + [], + [ + marker(1, 2, false) + ] + ); + }); + + test('replacing the entire line with more text', () => { + testLineEditMarkers( + 'this is a short text', + [ + marker(1, 1, false), + marker(2, 16, true), + ], + [{ + startColumn: 1, + endColumn: 21, + text: 'Some new text here', + forceMoveMarkers: false + }], + 'Some new text here', + [], + [ + marker(1, 1, false), + marker(2, 16, true), + ] + ); + }); + + test('replacing the entire line with less text', () => { + testLineEditMarkers( + 'this is a short text', + [ + marker(1, 1, false), + marker(2, 16, true), + ], + [{ + startColumn: 1, + endColumn: 21, + text: 'ttt', + forceMoveMarkers: false + }], + 'ttt', + [2], + [ + marker(1, 1, false), + marker(2, 4, true), + ] + ); + }); + + test('replace selection', () => { + testLineEditMarkers( + 'first', + [ + marker(1, 1, true), + marker(2, 6, false), + ], + [{ + startColumn: 1, + endColumn: 6, + text: 'something', + forceMoveMarkers: false + }], + 'something', + [2], + [ + marker(1, 1, true), + marker(2, 10, false), + ] + ); + }); +}); + +suite('Editor Model - modelLine.split text & markers', () => { + + function marker(id: number, column: number, stickToPreviousCharacter: boolean): LineMarker { + return new LineMarker(String(id), id, new Position(0, column), stickToPreviousCharacter); + } + + function toLightWeightMarker(marker: LineMarker): ILightWeightMarker { + return { + id: marker.id, + lineNumber: marker.position.lineNumber, + column: marker.position.column, + stickToPreviousCharacter: marker.stickToPreviousCharacter + }; + } + + function testLineSplitMarkers(initialText: string, initialMarkers: LineMarker[], splitColumn: number, forceMoveMarkers: boolean, expectedText1: string, expectedText2: string, expectedChangedMarkers: number[], _expectedMarkers1: LineMarker[], _expectedMarkers2: LineMarker[]): void { + let line = new ModelLine(initialText, NO_TAB_SIZE); + line.addMarkers(initialMarkers); + + let changedMarkers = new MarkersTracker(); + let otherLine = line.split(changedMarkers, splitColumn, forceMoveMarkers, NO_TAB_SIZE); + + assert.equal(line.text, expectedText1, 'text'); + assert.equal(otherLine.text, expectedText2, 'text'); + + let actualMarkers1 = line.getMarkers().map(toLightWeightMarker); + let expectedMarkers1 = _expectedMarkers1.map(toLightWeightMarker); + assert.deepEqual(actualMarkers1, expectedMarkers1, 'markers'); + + let actualMarkers2 = otherLine.getMarkers().map(toLightWeightMarker); + let expectedMarkers2 = _expectedMarkers2.map(toLightWeightMarker); + assert.deepEqual(actualMarkers2, expectedMarkers2, 'markers'); + + let actualChangedMarkers = changedMarkers.getDecorationIds(); + actualChangedMarkers.sort(); + assert.deepEqual(actualChangedMarkers, expectedChangedMarkers, 'changed markers'); + } + + test('split at the beginning', () => { + testLineSplitMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + 1, + false, + '', + 'abcd efgh', + [], + [ + marker(1, 1, true) + ], + [ + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ] + ); + }); + + test('split at the beginning 2', () => { + testLineSplitMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + 1, + true, + '', + 'abcd efgh', + [], + [], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ] + ); + }); + + test('split at the end', () => { + testLineSplitMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + 10, + false, + 'abcd efgh', + '', + [8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + ], + [ + marker(8, 1, false) + ] + ); + }); + + test('split it the middle 1', () => { + testLineSplitMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + 2, + false, + 'a', + 'bcd efgh', + [4, 5, 6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + ], + [ + marker(4, 1, false), + marker(5, 4, true), + marker(6, 4, false), + marker(7, 9, true), + marker(8, 9, false) + ] + ); + }); + + test('split it the middle 2', () => { + testLineSplitMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + 3, + false, + 'ab', + 'cd efgh', + [5, 6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + ], + [ + marker(5, 3, true), + marker(6, 3, false), + marker(7, 8, true), + marker(8, 8, false) + ] + ); + }); + + test('split it the middle 3', () => { + testLineSplitMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + 5, + false, + 'abcd', + ' efgh', + [6, 7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + ], + [ + marker(6, 1, false), + marker(7, 6, true), + marker(8, 6, false) + ] + ); + }); + + test('split it the middle 4', () => { + testLineSplitMarkers( + 'abcd efgh', + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + marker(7, 10, true), + marker(8, 10, false) + ], + 6, + false, + 'abcd ', + 'efgh', + [7, 8], + [ + marker(1, 1, true), + marker(2, 1, false), + marker(3, 2, true), + marker(4, 2, false), + marker(5, 5, true), + marker(6, 5, false), + ], + [ + marker(7, 5, true), + marker(8, 5, false) + ] + ); + }); +}); + +suite('Editor Model - modelLine.append text & markers', () => { + + function markerOnFirstLine(id: number, column: number, stickToPreviousCharacter: boolean): LineMarker { + return new LineMarker(String(id), id, new Position(1, column), stickToPreviousCharacter); + } + + function markerOnSecondLine(id: number, column: number, stickToPreviousCharacter: boolean): LineMarker { + return new LineMarker(String(id), id, new Position(2, column), stickToPreviousCharacter); + } + + function toLightWeightMarker(marker: LineMarker): ILightWeightMarker { + return { + id: marker.id, + lineNumber: marker.position.lineNumber, + column: marker.position.column, + stickToPreviousCharacter: marker.stickToPreviousCharacter + }; + } + + function testLinePrependMarkers(aText: string, aMarkers: LineMarker[], bText: string, bMarkers: LineMarker[], expectedText: string, expectedChangedMarkers: number[], _expectedMarkers: LineMarker[]): void { + let a = new ModelLine(aText, NO_TAB_SIZE); + a.addMarkers(aMarkers); + + let b = new ModelLine(bText, NO_TAB_SIZE); + b.addMarkers(bMarkers); + + let changedMarkers = new MarkersTracker(); + a.append(changedMarkers, 1, b, NO_TAB_SIZE); + + assert.equal(a.text, expectedText, 'text'); + + let actualMarkers = a.getMarkers().map(toLightWeightMarker); + let expectedMarkers = _expectedMarkers.map(toLightWeightMarker); + assert.deepEqual(actualMarkers, expectedMarkers, 'markers'); + + let actualChangedMarkers = changedMarkers.getDecorationIds(); + actualChangedMarkers.sort(); + assert.deepEqual(actualChangedMarkers, expectedChangedMarkers, 'changed markers'); + } + + test('append to an empty', () => { + testLinePrependMarkers( + 'abcd efgh', + [ + markerOnFirstLine(1, 1, true), + markerOnFirstLine(2, 1, false), + markerOnFirstLine(3, 2, true), + markerOnFirstLine(4, 2, false), + markerOnFirstLine(5, 5, true), + markerOnFirstLine(6, 5, false), + markerOnFirstLine(7, 10, true), + markerOnFirstLine(8, 10, false), + ], + '', + [ + ], + 'abcd efgh', + [], + [ + markerOnFirstLine(1, 1, true), + markerOnFirstLine(2, 1, false), + markerOnFirstLine(3, 2, true), + markerOnFirstLine(4, 2, false), + markerOnFirstLine(5, 5, true), + markerOnFirstLine(6, 5, false), + markerOnFirstLine(7, 10, true), + markerOnFirstLine(8, 10, false) + ] + ); + }); + + test('append an empty', () => { + testLinePrependMarkers( + '', + [ + ], + 'abcd efgh', + [ + markerOnSecondLine(1, 1, true), + markerOnSecondLine(2, 1, false), + markerOnSecondLine(3, 2, true), + markerOnSecondLine(4, 2, false), + markerOnSecondLine(5, 5, true), + markerOnSecondLine(6, 5, false), + markerOnSecondLine(7, 10, true), + markerOnSecondLine(8, 10, false), + ], + 'abcd efgh', + [1, 2, 3, 4, 5, 6, 7, 8], + [ + markerOnFirstLine(1, 1, true), + markerOnFirstLine(2, 1, false), + markerOnFirstLine(3, 2, true), + markerOnFirstLine(4, 2, false), + markerOnFirstLine(5, 5, true), + markerOnFirstLine(6, 5, false), + markerOnFirstLine(7, 10, true), + markerOnFirstLine(8, 10, false) + ] + ); + }); + + test('append 1', () => { + testLinePrependMarkers( + 'abcd', + [ + markerOnFirstLine(1, 1, true), + markerOnFirstLine(2, 1, false), + markerOnFirstLine(3, 2, true), + markerOnFirstLine(4, 2, false) + ], + ' efgh', + [ + markerOnSecondLine(5, 1, true), + markerOnSecondLine(6, 1, false), + markerOnSecondLine(7, 6, true), + markerOnSecondLine(8, 6, false), + ], + 'abcd efgh', + [5, 6, 7, 8], + [ + markerOnFirstLine(1, 1, true), + markerOnFirstLine(2, 1, false), + markerOnFirstLine(3, 2, true), + markerOnFirstLine(4, 2, false), + markerOnFirstLine(5, 5, true), + markerOnFirstLine(6, 5, false), + markerOnFirstLine(7, 10, true), + markerOnFirstLine(8, 10, false) + ] + ); + }); + + test('append 2', () => { + testLinePrependMarkers( + 'abcd e', + [ + markerOnFirstLine(1, 1, true), + markerOnFirstLine(2, 1, false), + markerOnFirstLine(3, 2, true), + markerOnFirstLine(4, 2, false), + markerOnFirstLine(5, 5, true), + markerOnFirstLine(6, 5, false) + ], + 'fgh', + [ + markerOnSecondLine(7, 4, true), + markerOnSecondLine(8, 4, false), + ], + 'abcd efgh', + [7, 8], + [ + markerOnFirstLine(1, 1, true), + markerOnFirstLine(2, 1, false), + markerOnFirstLine(3, 2, true), + markerOnFirstLine(4, 2, false), + markerOnFirstLine(5, 5, true), + markerOnFirstLine(6, 5, false), + markerOnFirstLine(7, 10, true), + markerOnFirstLine(8, 10, false) + ] + ); + }); +}); + diff --git a/src/vs/editor/test/common/model/model.modes.test.ts b/src/vs/editor/test/common/model/model.modes.test.ts new file mode 100644 index 0000000000..3fd4ff9b48 --- /dev/null +++ b/src/vs/editor/test/common/model/model.modes.test.ts @@ -0,0 +1,290 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Model } from 'vs/editor/common/model/model'; +import * as modes from 'vs/editor/common/modes'; +import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; + +// --------- utils + +suite('Editor Model - Model Modes 1', () => { + + let calledFor: string[] = []; + + function checkAndClear(arr: string[]) { + assert.deepEqual(calledFor, arr); + calledFor = []; + } + + const tokenizationSupport: modes.ITokenizationSupport = { + getInitialState: () => NULL_STATE, + tokenize: undefined, + tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { + calledFor.push(line.charAt(0)); + return new TokenizationResult2(null, state); + } + }; + + let thisModel: Model = null; + let languageRegistration: IDisposable = null; + + setup(() => { + const TEXT = + '1\r\n' + + '2\n' + + '3\n' + + '4\r\n' + + '5'; + const LANGUAGE_ID = 'modelModeTest1'; + calledFor = []; + languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport); + thisModel = Model.createFromString(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); + }); + + teardown(() => { + thisModel.dispose(); + thisModel = null; + languageRegistration.dispose(); + languageRegistration = null; + calledFor = []; + }); + + test('model calls syntax highlighter 1', () => { + thisModel.forceTokenization(1); + checkAndClear(['1']); + }); + + test('model calls syntax highlighter 2', () => { + thisModel.forceTokenization(2); + checkAndClear(['1', '2']); + + thisModel.forceTokenization(2); + checkAndClear([]); + }); + + test('model caches states', () => { + thisModel.forceTokenization(1); + checkAndClear(['1']); + + thisModel.forceTokenization(2); + checkAndClear(['2']); + + thisModel.forceTokenization(3); + checkAndClear(['3']); + + thisModel.forceTokenization(4); + checkAndClear(['4']); + + thisModel.forceTokenization(5); + checkAndClear(['5']); + + thisModel.forceTokenization(5); + checkAndClear([]); + }); + + test('model invalidates states for one line insert', () => { + thisModel.forceTokenization(5); + checkAndClear(['1', '2', '3', '4', '5']); + + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), '-')]); + thisModel.forceTokenization(5); + checkAndClear(['-']); + + thisModel.forceTokenization(5); + checkAndClear([]); + }); + + test('model invalidates states for many lines insert', () => { + thisModel.forceTokenization(5); + checkAndClear(['1', '2', '3', '4', '5']); + + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), '0\n-\n+')]); + assert.equal(thisModel.getLineCount(), 7); + thisModel.forceTokenization(7); + checkAndClear(['0', '-', '+']); + + thisModel.forceTokenization(7); + checkAndClear([]); + }); + + test('model invalidates states for one new line', () => { + thisModel.forceTokenization(5); + checkAndClear(['1', '2', '3', '4', '5']); + + thisModel.applyEdits([EditOperation.insert(new Position(1, 2), '\n')]); + thisModel.applyEdits([EditOperation.insert(new Position(2, 1), 'a')]); + thisModel.forceTokenization(6); + checkAndClear(['1', 'a']); + }); + + test('model invalidates states for one line delete', () => { + thisModel.forceTokenization(5); + checkAndClear(['1', '2', '3', '4', '5']); + + thisModel.applyEdits([EditOperation.insert(new Position(1, 2), '-')]); + thisModel.forceTokenization(5); + checkAndClear(['1']); + + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 2))]); + thisModel.forceTokenization(5); + checkAndClear(['-']); + + thisModel.forceTokenization(5); + checkAndClear([]); + }); + + test('model invalidates states for many lines delete', () => { + thisModel.forceTokenization(5); + checkAndClear(['1', '2', '3', '4', '5']); + + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 3, 1))]); + thisModel.forceTokenization(3); + checkAndClear(['3']); + + thisModel.forceTokenization(3); + checkAndClear([]); + }); +}); + +suite('Editor Model - Model Modes 2', () => { + + class ModelState2 implements modes.IState { + prevLineContent: string; + + constructor(prevLineContent: string) { + this.prevLineContent = prevLineContent; + } + + clone(): modes.IState { + return new ModelState2(this.prevLineContent); + } + + equals(other: modes.IState): boolean { + return (other instanceof ModelState2) && other.prevLineContent === this.prevLineContent; + } + } + + const tokenizationSupport: modes.ITokenizationSupport = { + getInitialState: () => new ModelState2(''), + tokenize: undefined, + tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { + (state).prevLineContent = line; + return new TokenizationResult2(null, state); + } + }; + + function invalidEqual(model: Model, expected: number[]): void { + let actual: number[] = []; + for (let i = 0, len = model.getLineCount(); i < len; i++) { + if (model._lines[i].isInvalid()) { + actual.push(i); + } + } + assert.deepEqual(actual, expected); + } + + function stateEqual(state: modes.IState, content: string): void { + assert.equal((state).prevLineContent, content); + } + + function statesEqual(model: Model, states: string[]): void { + var i, len = states.length - 1; + for (i = 0; i < len; i++) { + stateEqual(model._lines[i].getState(), states[i]); + } + stateEqual((model)._lastState, states[len]); + } + + let thisModel: Model = null; + let languageRegistration: IDisposable = null; + + setup(() => { + const TEXT = + 'Line1' + '\r\n' + + 'Line2' + '\n' + + 'Line3' + '\n' + + 'Line4' + '\r\n' + + 'Line5'; + const LANGUAGE_ID = 'modelModeTest2'; + languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport); + thisModel = Model.createFromString(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); + }); + + teardown(() => { + thisModel.dispose(); + thisModel = null; + languageRegistration.dispose(); + languageRegistration = null; + }); + + test('getTokensForInvalidLines one text insert', () => { + thisModel.forceTokenization(5); + statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + thisModel.applyEdits([EditOperation.insert(new Position(1, 6), '-')]); + invalidEqual(thisModel, [0]); + statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + thisModel.forceTokenization(5); + statesEqual(thisModel, ['', 'Line1-', 'Line2', 'Line3', 'Line4', 'Line5']); + }); + + test('getTokensForInvalidLines two text insert', () => { + thisModel.forceTokenization(5); + statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + thisModel.applyEdits([ + EditOperation.insert(new Position(1, 6), '-'), + EditOperation.insert(new Position(3, 6), '-') + ]); + + invalidEqual(thisModel, [0, 2]); + thisModel.forceTokenization(5); + statesEqual(thisModel, ['', 'Line1-', 'Line2', 'Line3-', 'Line4', 'Line5']); + }); + + test('getTokensForInvalidLines one multi-line text insert, one small text insert', () => { + thisModel.forceTokenization(5); + statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + thisModel.applyEdits([EditOperation.insert(new Position(1, 6), '\nNew line\nAnother new line')]); + thisModel.applyEdits([EditOperation.insert(new Position(5, 6), '-')]); + invalidEqual(thisModel, [0, 4]); + thisModel.forceTokenization(7); + statesEqual(thisModel, ['', 'Line1', 'New line', 'Another new line', 'Line2', 'Line3-', 'Line4', 'Line5']); + }); + + test('getTokensForInvalidLines one delete text', () => { + thisModel.forceTokenization(5); + statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 5))]); + invalidEqual(thisModel, [0]); + thisModel.forceTokenization(5); + statesEqual(thisModel, ['', '1', 'Line2', 'Line3', 'Line4', 'Line5']); + }); + + test('getTokensForInvalidLines one line delete text', () => { + thisModel.forceTokenization(5); + statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 2, 1))]); + invalidEqual(thisModel, [0]); + statesEqual(thisModel, ['', 'Line2', 'Line3', 'Line4', 'Line5']); + thisModel.forceTokenization(4); + statesEqual(thisModel, ['', 'Line2', 'Line3', 'Line4', 'Line5']); + }); + + test('getTokensForInvalidLines multiple lines delete text', () => { + thisModel.forceTokenization(5); + statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 3, 3))]); + invalidEqual(thisModel, [0]); + statesEqual(thisModel, ['', 'Line3', 'Line4', 'Line5']); + thisModel.forceTokenization(3); + statesEqual(thisModel, ['', 'ne3', 'Line4', 'Line5']); + }); +}); diff --git a/src/vs/editor/test/common/model/model.test.ts b/src/vs/editor/test/common/model/model.test.ts new file mode 100644 index 0000000000..d32002c221 --- /dev/null +++ b/src/vs/editor/test/common/model/model.test.ts @@ -0,0 +1,395 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { + ModelRawContentChangedEvent, ModelRawFlush, ModelRawLineChanged, + ModelRawLinesDeleted, ModelRawLinesInserted +} from 'vs/editor/common/model/textModelEvents'; +import { Model } from 'vs/editor/common/model/model'; + +// --------- utils + +var LINE1 = 'My First Line'; +var LINE2 = '\t\tMy Second Line'; +var LINE3 = ' Third Line'; +var LINE4 = ''; +var LINE5 = '1'; + +suite('Editor Model - Model', () => { + + var thisModel: Model; + + setup(() => { + var text = + LINE1 + '\r\n' + + LINE2 + '\n' + + LINE3 + '\n' + + LINE4 + '\r\n' + + LINE5; + thisModel = Model.createFromString(text); + }); + + teardown(() => { + thisModel.dispose(); + }); + + // --------- insert text + + test('model getValue', () => { + assert.equal(thisModel.getValue(), 'My First Line\n\t\tMy Second Line\n Third Line\n\n1'); + }); + + test('model insert empty text', () => { + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), '')]); + assert.equal(thisModel.getLineCount(), 5); + assert.equal(thisModel.getLineContent(1), 'My First Line'); + }); + + test('model insert text without newline 1', () => { + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'foo ')]); + assert.equal(thisModel.getLineCount(), 5); + assert.equal(thisModel.getLineContent(1), 'foo My First Line'); + }); + + test('model insert text without newline 2', () => { + thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' foo')]); + assert.equal(thisModel.getLineCount(), 5); + assert.equal(thisModel.getLineContent(1), 'My foo First Line'); + }); + + test('model insert text with one newline', () => { + thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' new line\nNo longer')]); + assert.equal(thisModel.getLineCount(), 6); + assert.equal(thisModel.getLineContent(1), 'My new line'); + assert.equal(thisModel.getLineContent(2), 'No longer First Line'); + }); + + test('model insert text with two newlines', () => { + thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' new line\nOne more line in the middle\nNo longer')]); + assert.equal(thisModel.getLineCount(), 7); + assert.equal(thisModel.getLineContent(1), 'My new line'); + assert.equal(thisModel.getLineContent(2), 'One more line in the middle'); + assert.equal(thisModel.getLineContent(3), 'No longer First Line'); + }); + + test('model insert text with many newlines', () => { + thisModel.applyEdits([EditOperation.insert(new Position(1, 3), '\n\n\n\n')]); + assert.equal(thisModel.getLineCount(), 9); + assert.equal(thisModel.getLineContent(1), 'My'); + assert.equal(thisModel.getLineContent(2), ''); + assert.equal(thisModel.getLineContent(3), ''); + assert.equal(thisModel.getLineContent(4), ''); + assert.equal(thisModel.getLineContent(5), ' First Line'); + }); + + + // --------- insert text eventing + + test('model insert empty text does not trigger eventing', () => { + thisModel.onDidChangeRawContent((e) => { + assert.ok(false, 'was not expecting event'); + }); + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), '')]); + }); + + test('model insert text without newline eventing', () => { + let e: ModelRawContentChangedEvent = null; + thisModel.onDidChangeRawContent((_e) => { + if (e !== null) { + assert.fail(); + } + e = _e; + }); + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'foo ')]); + assert.deepEqual(e, new ModelRawContentChangedEvent( + [ + new ModelRawLineChanged(1, 'foo My First Line') + ], + 2, + false, + false + )); + }); + + test('model insert text with one newline eventing', () => { + let e: ModelRawContentChangedEvent = null; + thisModel.onDidChangeRawContent((_e) => { + if (e !== null) { + assert.fail(); + } + e = _e; + }); + thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' new line\nNo longer')]); + assert.deepEqual(e, new ModelRawContentChangedEvent( + [ + new ModelRawLineChanged(1, 'My new line First Line'), + new ModelRawLineChanged(1, 'My new line'), + new ModelRawLinesInserted(2, 2, 'No longer First Line'), + ], + 2, + false, + false + )); + }); + + + // --------- delete text + + test('model delete empty text', () => { + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 1))]); + assert.equal(thisModel.getLineCount(), 5); + assert.equal(thisModel.getLineContent(1), 'My First Line'); + }); + + test('model delete text from one line', () => { + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 2))]); + assert.equal(thisModel.getLineCount(), 5); + assert.equal(thisModel.getLineContent(1), 'y First Line'); + }); + + test('model delete text from one line 2', () => { + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'a')]); + assert.equal(thisModel.getLineContent(1), 'aMy First Line'); + + thisModel.applyEdits([EditOperation.delete(new Range(1, 2, 1, 4))]); + assert.equal(thisModel.getLineCount(), 5); + assert.equal(thisModel.getLineContent(1), 'a First Line'); + }); + + test('model delete all text from a line', () => { + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 14))]); + assert.equal(thisModel.getLineCount(), 5); + assert.equal(thisModel.getLineContent(1), ''); + }); + + test('model delete text from two lines', () => { + thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 2, 6))]); + assert.equal(thisModel.getLineCount(), 4); + assert.equal(thisModel.getLineContent(1), 'My Second Line'); + }); + + test('model delete text from many lines', () => { + thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 3, 5))]); + assert.equal(thisModel.getLineCount(), 3); + assert.equal(thisModel.getLineContent(1), 'My Third Line'); + }); + + test('model delete everything', () => { + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 5, 2))]); + assert.equal(thisModel.getLineCount(), 1); + assert.equal(thisModel.getLineContent(1), ''); + }); + + // --------- delete text eventing + + test('model delete empty text does not trigger eventing', () => { + thisModel.onDidChangeRawContent((e) => { + assert.ok(false, 'was not expecting event'); + }); + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 1))]); + }); + + test('model delete text from one line eventing', () => { + let e: ModelRawContentChangedEvent = null; + thisModel.onDidChangeRawContent((_e) => { + if (e !== null) { + assert.fail(); + } + e = _e; + }); + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 2))]); + assert.deepEqual(e, new ModelRawContentChangedEvent( + [ + new ModelRawLineChanged(1, 'y First Line'), + ], + 2, + false, + false + )); + }); + + test('model delete all text from a line eventing', () => { + let e: ModelRawContentChangedEvent = null; + thisModel.onDidChangeRawContent((_e) => { + if (e !== null) { + assert.fail(); + } + e = _e; + }); + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 14))]); + assert.deepEqual(e, new ModelRawContentChangedEvent( + [ + new ModelRawLineChanged(1, ''), + ], + 2, + false, + false + )); + }); + + test('model delete text from two lines eventing', () => { + let e: ModelRawContentChangedEvent = null; + thisModel.onDidChangeRawContent((_e) => { + if (e !== null) { + assert.fail(); + } + e = _e; + }); + thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 2, 6))]); + assert.deepEqual(e, new ModelRawContentChangedEvent( + [ + new ModelRawLineChanged(1, 'My '), + new ModelRawLineChanged(1, 'My Second Line'), + new ModelRawLinesDeleted(2, 2), + ], + 2, + false, + false + )); + }); + + test('model delete text from many lines eventing', () => { + let e: ModelRawContentChangedEvent = null; + thisModel.onDidChangeRawContent((_e) => { + if (e !== null) { + assert.fail(); + } + e = _e; + }); + thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 3, 5))]); + assert.deepEqual(e, new ModelRawContentChangedEvent( + [ + new ModelRawLineChanged(1, 'My '), + new ModelRawLineChanged(1, 'My Third Line'), + new ModelRawLinesDeleted(2, 3), + ], + 2, + false, + false + )); + }); + + // --------- getValueInRange + + test('getValueInRange', () => { + assert.equal(thisModel.getValueInRange(new Range(1, 1, 1, 1)), ''); + assert.equal(thisModel.getValueInRange(new Range(1, 1, 1, 2)), 'M'); + assert.equal(thisModel.getValueInRange(new Range(1, 2, 1, 3)), 'y'); + assert.equal(thisModel.getValueInRange(new Range(1, 1, 1, 14)), 'My First Line'); + assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 1)), 'My First Line\n'); + assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 2)), 'My First Line\n\t'); + assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 3)), 'My First Line\n\t\t'); + assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 17)), 'My First Line\n\t\tMy Second Line'); + assert.equal(thisModel.getValueInRange(new Range(1, 1, 3, 1)), 'My First Line\n\t\tMy Second Line\n'); + assert.equal(thisModel.getValueInRange(new Range(1, 1, 4, 1)), 'My First Line\n\t\tMy Second Line\n Third Line\n'); + }); + + // --------- getValueLengthInRange + + test('getValueLengthInRange', () => { + assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length); + assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length); + assert.equal(thisModel.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length); + assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 1, 14)), 'My First Line'.length); + assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 1)), 'My First Line\n'.length); + assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 2)), 'My First Line\n\t'.length); + assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 3)), 'My First Line\n\t\t'.length); + assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 17)), 'My First Line\n\t\tMy Second Line'.length); + assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 3, 1)), 'My First Line\n\t\tMy Second Line\n'.length); + assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 4, 1)), 'My First Line\n\t\tMy Second Line\n Third Line\n'.length); + }); + + // --------- setValue + test('setValue eventing', () => { + let e: ModelRawContentChangedEvent = null; + thisModel.onDidChangeRawContent((_e) => { + if (e !== null) { + assert.fail(); + } + e = _e; + }); + thisModel.setValue('new value'); + assert.deepEqual(e, new ModelRawContentChangedEvent( + [ + new ModelRawFlush() + ], + 2, + false, + false + )); + }); +}); + + +// --------- Special Unicode LINE SEPARATOR character +suite('Editor Model - Model Line Separators', () => { + + var thisModel: Model; + + setup(() => { + var text = + LINE1 + '\u2028' + + LINE2 + '\n' + + LINE3 + '\u2028' + + LINE4 + '\r\n' + + LINE5; + thisModel = Model.createFromString(text); + }); + + teardown(() => { + thisModel.dispose(); + }); + + test('model getValue', () => { + assert.equal(thisModel.getValue(), 'My First Line\u2028\t\tMy Second Line\n Third Line\u2028\n1'); + }); + + test('model lines', () => { + assert.equal(thisModel.getLineCount(), 3); + }); + + test('Bug 13333:Model should line break on lonely CR too', () => { + var model = Model.createFromString('Hello\rWorld!\r\nAnother line'); + assert.equal(model.getLineCount(), 3); + assert.equal(model.getValue(), 'Hello\r\nWorld!\r\nAnother line'); + model.dispose(); + }); +}); + + +// --------- Words + +suite('Editor Model - Words', () => { + + var thisModel: Model; + + setup(() => { + var text = ['This text has some words. ']; + thisModel = Model.createFromString(text.join('\n')); + }); + + teardown(() => { + thisModel.dispose(); + }); + + test('Get word at position', () => { + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 1)), { word: 'This', startColumn: 1, endColumn: 5 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 2)), { word: 'This', startColumn: 1, endColumn: 5 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 4)), { word: 'This', startColumn: 1, endColumn: 5 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 5)), { word: 'This', startColumn: 1, endColumn: 5 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 6)), { word: 'text', startColumn: 6, endColumn: 10 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 19)), { word: 'some', startColumn: 15, endColumn: 19 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 20)), null); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 21)), { word: 'words', startColumn: 21, endColumn: 26 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 26)), { word: 'words', startColumn: 21, endColumn: 26 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 27)), null); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 28)), null); + }); +}); diff --git a/src/vs/editor/test/common/model/modelDecorations.test.ts b/src/vs/editor/test/common/model/modelDecorations.test.ts new file mode 100644 index 0000000000..ab25d0384f --- /dev/null +++ b/src/vs/editor/test/common/model/modelDecorations.test.ts @@ -0,0 +1,638 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/editorCommon'; +import { Model } from 'vs/editor/common/model/model'; + +// --------- utils + +interface ILightWeightDecoration2 { + range: Range; + className: string; +} + +function modelHasDecorations(model: Model, decorations: ILightWeightDecoration2[]) { + let modelDecorations: ILightWeightDecoration2[] = []; + let actualDecorations = model.getAllDecorations(); + for (let i = 0, len = actualDecorations.length; i < len; i++) { + modelDecorations.push({ + range: actualDecorations[i].range, + className: actualDecorations[i].options.className + }); + } + assert.deepEqual(modelDecorations, decorations, 'Model decorations'); +} + +function modelHasDecoration(model: Model, startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, className: string) { + modelHasDecorations(model, [{ + range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + className: className + }]); +} + +function modelHasNoDecorations(model: Model) { + assert.equal(model.getAllDecorations().length, 0, 'Model has no decoration'); +} + +function addDecoration(model: Model, startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, className: string): string { + return model.changeDecorations((changeAccessor) => { + return changeAccessor.addDecoration(new Range(startLineNumber, startColumn, endLineNumber, endColumn), { + className: className + }); + }); +} + +function lineHasDecorations(model: Model, lineNumber: number, decorations: { start: number; end: number; className: string; }[]) { + var lineDecorations = []; + var decs = model.getLineDecorations(lineNumber); + for (var i = 0, len = decs.length; i < len; i++) { + lineDecorations.push({ + start: decs[i].range.startColumn, + end: decs[i].range.endColumn, + className: decs[i].options.className + }); + } + assert.deepEqual(lineDecorations, decorations, 'Line decorations'); +} + +function lineHasNoDecorations(model: Model, lineNumber: number) { + lineHasDecorations(model, lineNumber, []); +} + +function lineHasDecoration(model: Model, lineNumber: number, start: number, end: number, className: string) { + lineHasDecorations(model, lineNumber, [{ + start: start, + end: end, + className: className + }]); +} + +suite('Editor Model - Model Decorations', () => { + var LINE1 = 'My First Line'; + var LINE2 = '\t\tMy Second Line'; + var LINE3 = ' Third Line'; + var LINE4 = ''; + var LINE5 = '1'; + + // --------- Model Decorations + + var thisModel: Model; + + setup(() => { + var text = + LINE1 + '\r\n' + + LINE2 + '\n' + + LINE3 + '\n' + + LINE4 + '\r\n' + + LINE5; + thisModel = Model.createFromString(text); + }); + + teardown(() => { + thisModel.dispose(); + thisModel = null; + }); + + test('single character decoration', () => { + addDecoration(thisModel, 1, 1, 1, 2, 'myType'); + lineHasDecoration(thisModel, 1, 1, 2, 'myType'); + lineHasNoDecorations(thisModel, 2); + lineHasNoDecorations(thisModel, 3); + lineHasNoDecorations(thisModel, 4); + lineHasNoDecorations(thisModel, 5); + }); + + test('line decoration', () => { + addDecoration(thisModel, 1, 1, 1, 14, 'myType'); + lineHasDecoration(thisModel, 1, 1, 14, 'myType'); + lineHasNoDecorations(thisModel, 2); + lineHasNoDecorations(thisModel, 3); + lineHasNoDecorations(thisModel, 4); + lineHasNoDecorations(thisModel, 5); + }); + + test('full line decoration', () => { + addDecoration(thisModel, 1, 1, 2, 1, 'myType'); + + var line1Decorations = thisModel.getLineDecorations(1); + assert.equal(line1Decorations.length, 1); + assert.equal(line1Decorations[0].options.className, 'myType'); + + var line2Decorations = thisModel.getLineDecorations(1); + assert.equal(line2Decorations.length, 1); + assert.equal(line2Decorations[0].options.className, 'myType'); + + lineHasNoDecorations(thisModel, 3); + lineHasNoDecorations(thisModel, 4); + lineHasNoDecorations(thisModel, 5); + }); + + test('multiple line decoration', () => { + addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + + var line1Decorations = thisModel.getLineDecorations(1); + assert.equal(line1Decorations.length, 1); + assert.equal(line1Decorations[0].options.className, 'myType'); + + var line2Decorations = thisModel.getLineDecorations(1); + assert.equal(line2Decorations.length, 1); + assert.equal(line2Decorations[0].options.className, 'myType'); + + var line3Decorations = thisModel.getLineDecorations(1); + assert.equal(line3Decorations.length, 1); + assert.equal(line3Decorations[0].options.className, 'myType'); + + lineHasNoDecorations(thisModel, 4); + lineHasNoDecorations(thisModel, 5); + }); + + // --------- removing, changing decorations + + test('decoration gets removed', () => { + var decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.changeDecorations((changeAccessor) => { + changeAccessor.removeDecoration(decId); + }); + modelHasNoDecorations(thisModel); + }); + + test('decorations get removed', () => { + var decId1 = addDecoration(thisModel, 1, 2, 3, 2, 'myType1'); + var decId2 = addDecoration(thisModel, 1, 2, 3, 1, 'myType2'); + modelHasDecorations(thisModel, [ + { + range: new Range(1, 2, 3, 2), + className: 'myType1' + }, + { + range: new Range(1, 2, 3, 1), + className: 'myType2' + } + ]); + thisModel.changeDecorations((changeAccessor) => { + changeAccessor.removeDecoration(decId1); + }); + modelHasDecorations(thisModel, [ + { + range: new Range(1, 2, 3, 1), + className: 'myType2' + } + ]); + thisModel.changeDecorations((changeAccessor) => { + changeAccessor.removeDecoration(decId2); + }); + modelHasNoDecorations(thisModel); + }); + + test('decoration range can be changed', () => { + var decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.changeDecorations((changeAccessor) => { + changeAccessor.changeDecoration(decId, new Range(1, 1, 1, 2)); + }); + modelHasDecoration(thisModel, 1, 1, 1, 2, 'myType'); + }); + + // --------- eventing + + test('decorations emit event on add', () => { + let listenerCalled = 0; + thisModel.onDidChangeDecorations((e) => { + listenerCalled++; + assert.equal(e.addedDecorations.length, 1); + assert.equal(e.changedDecorations.length, 0); + assert.equal(e.removedDecorations.length, 0); + }); + addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + assert.equal(listenerCalled, 1, 'listener called'); + }); + + test('decorations emit event on change', () => { + let listenerCalled = 0; + let decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.onDidChangeDecorations((e) => { + listenerCalled++; + assert.equal(e.addedDecorations.length, 0); + assert.equal(e.changedDecorations.length, 1); + assert.equal(e.changedDecorations[0], decId); + assert.equal(e.removedDecorations.length, 0); + }); + thisModel.changeDecorations((changeAccessor) => { + changeAccessor.changeDecoration(decId, new Range(1, 1, 1, 2)); + }); + assert.equal(listenerCalled, 1, 'listener called'); + }); + + test('decorations emit event on remove', () => { + let listenerCalled = 0; + let decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.onDidChangeDecorations((e) => { + listenerCalled++; + assert.equal(e.addedDecorations.length, 0); + assert.equal(e.changedDecorations.length, 0); + assert.equal(e.removedDecorations.length, 1); + assert.equal(e.removedDecorations[0], decId); + }); + thisModel.changeDecorations((changeAccessor) => { + changeAccessor.removeDecoration(decId); + }); + assert.equal(listenerCalled, 1, 'listener called'); + }); + + test('decorations emit event when inserting one line text before it', () => { + let listenerCalled = 0; + let decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + + thisModel.onDidChangeDecorations((e) => { + listenerCalled++; + assert.equal(e.addedDecorations.length, 0); + assert.equal(e.changedDecorations.length, 1); + assert.equal(e.changedDecorations[0], decId); + assert.equal(e.removedDecorations.length, 0); + }); + + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'Hallo ')]); + assert.equal(listenerCalled, 1, 'listener called'); + }); + + // --------- editing text & effects on decorations + + test('decorations are updated when inserting one line text before it', () => { + addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'Hallo ')]); + modelHasDecoration(thisModel, 1, 8, 3, 2, 'myType'); + }); + + test('decorations are updated when inserting one line text before it 2', () => { + addDecoration(thisModel, 1, 1, 3, 2, 'myType'); + modelHasDecoration(thisModel, 1, 1, 3, 2, 'myType'); + thisModel.applyEdits([EditOperation.replace(new Range(1, 1, 1, 1), 'Hallo ')]); + modelHasDecoration(thisModel, 1, 1, 3, 2, 'myType'); + }); + + test('decorations are updated when inserting multiple lines text before it', () => { + addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'Hallo\nI\'m inserting multiple\nlines')]); + modelHasDecoration(thisModel, 3, 7, 5, 2, 'myType'); + }); + + test('decorations change when inserting text after them', () => { + addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.applyEdits([EditOperation.insert(new Position(3, 2), 'Hallo')]); + modelHasDecoration(thisModel, 1, 2, 3, 7, 'myType'); + }); + + test('decorations are updated when inserting text inside', () => { + addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.applyEdits([EditOperation.insert(new Position(1, 3), 'Hallo ')]); + modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType'); + }); + + test('decorations are updated when inserting text inside 2', () => { + addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.applyEdits([EditOperation.insert(new Position(3, 1), 'Hallo ')]); + modelHasDecoration(thisModel, 1, 2, 3, 8, 'myType'); + }); + + test('decorations are updated when inserting text inside 3', () => { + addDecoration(thisModel, 1, 1, 2, 16, 'myType'); + modelHasDecoration(thisModel, 1, 1, 2, 16, 'myType'); + thisModel.applyEdits([EditOperation.insert(new Position(2, 2), '\n')]); + modelHasDecoration(thisModel, 1, 1, 3, 15, 'myType'); + }); + + test('decorations are updated when inserting multiple lines text inside', () => { + addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.applyEdits([EditOperation.insert(new Position(1, 3), 'Hallo\nI\'m inserting multiple\nlines')]); + modelHasDecoration(thisModel, 1, 2, 5, 2, 'myType'); + }); + + test('decorations are updated when deleting one line text before it', () => { + addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + modelHasDecoration(thisModel, 1, 2, 3, 2, 'myType'); + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 2))]); + modelHasDecoration(thisModel, 1, 1, 3, 2, 'myType'); + }); + + test('decorations are updated when deleting multiple lines text before it', () => { + addDecoration(thisModel, 2, 2, 3, 2, 'myType'); + modelHasDecoration(thisModel, 2, 2, 3, 2, 'myType'); + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 2, 1))]); + modelHasDecoration(thisModel, 1, 2, 2, 2, 'myType'); + }); + + test('decorations are updated when deleting multiple lines text before it 2', () => { + addDecoration(thisModel, 2, 3, 3, 2, 'myType'); + modelHasDecoration(thisModel, 2, 3, 3, 2, 'myType'); + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 2, 2))]); + modelHasDecoration(thisModel, 1, 2, 2, 2, 'myType'); + }); + + test('decorations are updated when deleting text inside', () => { + addDecoration(thisModel, 1, 2, 4, 1, 'myType'); + modelHasDecoration(thisModel, 1, 2, 4, 1, 'myType'); + thisModel.applyEdits([EditOperation.delete(new Range(1, 3, 2, 1))]); + modelHasDecoration(thisModel, 1, 2, 3, 1, 'myType'); + }); + + test('decorations are updated when deleting text inside 2', () => { + addDecoration(thisModel, 1, 2, 4, 1, 'myType'); + modelHasDecoration(thisModel, 1, 2, 4, 1, 'myType'); + thisModel.applyEdits([ + EditOperation.delete(new Range(1, 1, 1, 2)), + EditOperation.delete(new Range(4, 1, 4, 1)) + ]); + modelHasDecoration(thisModel, 1, 1, 4, 1, 'myType'); + }); + + test('decorations are updated when deleting multiple lines text', () => { + addDecoration(thisModel, 1, 2, 4, 1, 'myType'); + modelHasDecoration(thisModel, 1, 2, 4, 1, 'myType'); + thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 3, 1))]); + modelHasDecoration(thisModel, 1, 1, 2, 1, 'myType'); + }); +}); + +interface ILightWeightDecoration { + id: string; + range: Range; +} + +suite('deltaDecorations', () => { + + function decoration(id: string, startLineNumber: number, startColumn: number, endLineNumber: number, endColum: number): ILightWeightDecoration { + return { + id: id, + range: new Range(startLineNumber, startColumn, endLineNumber, endColum) + }; + } + + function toModelDeltaDecoration(dec: ILightWeightDecoration): IModelDeltaDecoration { + return { + range: dec.range, + options: { + className: dec.id + } + }; + } + + function strcmp(a: string, b: string): number { + if (a === b) { + return 0; + } + if (a < b) { + return -1; + } + return 1; + } + + function readModelDecorations(model: Model, ids: string[]): ILightWeightDecoration[] { + return ids.map((id) => { + return { + range: model.getDecorationRange(id), + id: model.getDecorationOptions(id).className + }; + }); + } + + function testDeltaDecorations(text: string[], decorations: ILightWeightDecoration[], newDecorations: ILightWeightDecoration[]): void { + + var model = Model.createFromString(text.join('\n')); + + // Add initial decorations & assert they are added + var initialIds = model.deltaDecorations([], decorations.map(toModelDeltaDecoration)); + var actualDecorations = readModelDecorations(model, initialIds); + + assert.equal(initialIds.length, decorations.length, 'returns expected cnt of ids'); + assert.equal(initialIds.length, model.getAllDecorations().length, 'does not leak decorations'); + assert.equal(initialIds.length, model._getTrackedRangesCount(), 'does not leak tracked ranges'); + assert.equal(2 * initialIds.length, model._getMarkersCount(), 'does not leak markers'); + actualDecorations.sort((a, b) => strcmp(a.id, b.id)); + decorations.sort((a, b) => strcmp(a.id, b.id)); + assert.deepEqual(actualDecorations, decorations); + + var newIds = model.deltaDecorations(initialIds, newDecorations.map(toModelDeltaDecoration)); + var actualNewDecorations = readModelDecorations(model, newIds); + + assert.equal(newIds.length, newDecorations.length, 'returns expected cnt of ids'); + assert.equal(newIds.length, model.getAllDecorations().length, 'does not leak decorations'); + assert.equal(newIds.length, model._getTrackedRangesCount(), 'does not leak tracked ranges'); + assert.equal(2 * newIds.length, model._getMarkersCount(), 'does not leak markers'); + actualNewDecorations.sort((a, b) => strcmp(a.id, b.id)); + newDecorations.sort((a, b) => strcmp(a.id, b.id)); + assert.deepEqual(actualDecorations, decorations); + + model.dispose(); + } + + function range(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number): Range { + return new Range(startLineNumber, startColumn, endLineNumber, endColumn); + } + + test('result respects input', () => { + var model = Model.createFromString([ + 'Hello world,', + 'How are you?' + ].join('\n')); + + var ids = model.deltaDecorations([], [ + toModelDeltaDecoration(decoration('a', 1, 1, 1, 12)), + toModelDeltaDecoration(decoration('b', 2, 1, 2, 13)) + ]); + + assert.deepEqual(model.getDecorationRange(ids[0]), range(1, 1, 1, 12)); + assert.deepEqual(model.getDecorationRange(ids[1]), range(2, 1, 2, 13)); + + model.dispose(); + }); + + test('deltaDecorations 1', () => { + testDeltaDecorations( + [ + 'This is a text', + 'That has multiple lines', + 'And is very friendly', + 'Towards testing' + ], + [ + decoration('a', 1, 1, 1, 2), + decoration('b', 1, 1, 1, 15), + decoration('c', 1, 1, 2, 1), + decoration('d', 1, 1, 2, 24), + decoration('e', 2, 1, 2, 24), + decoration('f', 2, 1, 4, 16) + ], + [ + decoration('x', 1, 1, 1, 2), + decoration('b', 1, 1, 1, 15), + decoration('c', 1, 1, 2, 1), + decoration('d', 1, 1, 2, 24), + decoration('e', 2, 1, 2, 21), + decoration('f', 2, 17, 4, 16) + ] + ); + }); + + test('deltaDecorations 2', () => { + testDeltaDecorations( + [ + 'This is a text', + 'That has multiple lines', + 'And is very friendly', + 'Towards testing' + ], + [ + decoration('a', 1, 1, 1, 2), + decoration('b', 1, 2, 1, 3), + decoration('c', 1, 3, 1, 4), + decoration('d', 1, 4, 1, 5), + decoration('e', 1, 5, 1, 6) + ], + [ + decoration('a', 1, 2, 1, 3), + decoration('b', 1, 3, 1, 4), + decoration('c', 1, 4, 1, 5), + decoration('d', 1, 5, 1, 6) + ] + ); + }); + + test('deltaDecorations 3', () => { + testDeltaDecorations( + [ + 'This is a text', + 'That has multiple lines', + 'And is very friendly', + 'Towards testing' + ], + [ + decoration('a', 1, 1, 1, 2), + decoration('b', 1, 2, 1, 3), + decoration('c', 1, 3, 1, 4), + decoration('d', 1, 4, 1, 5), + decoration('e', 1, 5, 1, 6) + ], + [] + ); + }); + + test('issue #4317: editor.setDecorations doesn\'t update the hover message', () => { + + let model = Model.createFromString('Hello world!'); + + let ids = model.deltaDecorations([], [{ + range: { + startLineNumber: 1, + startColumn: 1, + endLineNumber: 100, + endColumn: 1 + }, + options: { + hoverMessage: { value: 'hello1' } + } + }]); + + ids = model.deltaDecorations(ids, [{ + range: { + startLineNumber: 1, + startColumn: 1, + endLineNumber: 100, + endColumn: 1 + }, + options: { + hoverMessage: { value: 'hello2' } + } + }]); + + let actualDecoration = model.getDecorationOptions(ids[0]); + + assert.deepEqual(actualDecoration.hoverMessage, { value: 'hello2' }); + + model.dispose(); + }); + + test('model doesn\'t get confused with individual tracked ranges', () => { + var model = Model.createFromString([ + 'Hello world,', + 'How are you?' + ].join('\n')); + + var trackedRangeId = model.changeDecorations((changeAcessor) => { + return changeAcessor.addDecoration( + { + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 1 + }, { + stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges + } + ); + }); + model.changeDecorations((changeAccessor) => { + changeAccessor.removeDecoration(trackedRangeId); + }); + + var ids = model.deltaDecorations([], [ + toModelDeltaDecoration(decoration('a', 1, 1, 1, 12)), + toModelDeltaDecoration(decoration('b', 2, 1, 2, 13)) + ]); + + assert.deepEqual(model.getDecorationRange(ids[0]), range(1, 1, 1, 12)); + assert.deepEqual(model.getDecorationRange(ids[1]), range(2, 1, 2, 13)); + + ids = model.deltaDecorations(ids, [ + toModelDeltaDecoration(decoration('a', 1, 1, 1, 12)), + toModelDeltaDecoration(decoration('b', 2, 1, 2, 13)) + ]); + + assert.deepEqual(model.getDecorationRange(ids[0]), range(1, 1, 1, 12)); + assert.deepEqual(model.getDecorationRange(ids[1]), range(2, 1, 2, 13)); + + model.dispose(); + }); + + test('issue #16922: Clicking on link doesn\'t seem to do anything', () => { + var model = Model.createFromString([ + 'Hello world,', + 'How are you?', + 'Fine.', + 'Good.', + ].join('\n')); + + model.deltaDecorations([], [ + { range: new Range(1, 1, 1, 1), options: { className: '1' } }, + { range: new Range(1, 13, 1, 13), options: { className: '2' } }, + { range: new Range(2, 1, 2, 1), options: { className: '3' } }, + { range: new Range(2, 1, 2, 4), options: { className: '4' } }, + { range: new Range(2, 8, 2, 13), options: { className: '5' } }, + { range: new Range(3, 1, 4, 6), options: { className: '6' } }, + { range: new Range(1, 1, 3, 6), options: { className: 'x1' } }, + { range: new Range(2, 5, 2, 8), options: { className: 'x2' } }, + { range: new Range(1, 1, 2, 8), options: { className: 'x3' } }, + { range: new Range(2, 5, 3, 1), options: { className: 'x4' } }, + ]); + + let inRange = model.getDecorationsInRange(new Range(2, 6, 2, 6)); + + let inRangeClassNames = inRange.map(d => d.options.className); + inRangeClassNames.sort(); + assert.deepEqual(inRangeClassNames, ['x1', 'x2', 'x3', 'x4']); + + model.dispose(); + }); +}); diff --git a/src/vs/editor/test/common/model/modelEditOperation.test.ts b/src/vs/editor/test/common/model/modelEditOperation.test.ts new file mode 100644 index 0000000000..14c819478a --- /dev/null +++ b/src/vs/editor/test/common/model/modelEditOperation.test.ts @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Range } from 'vs/editor/common/core/range'; +import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; +import { Model } from 'vs/editor/common/model/model'; + +suite('Editor Model - Model Edit Operation', () => { + var LINE1 = 'My First Line'; + var LINE2 = '\t\tMy Second Line'; + var LINE3 = ' Third Line'; + var LINE4 = ''; + var LINE5 = '1'; + + var model: Model; + + setup(() => { + var text = + LINE1 + '\r\n' + + LINE2 + '\n' + + LINE3 + '\n' + + LINE4 + '\r\n' + + LINE5; + model = Model.createFromString(text); + }); + + teardown(() => { + model.dispose(); + model = null; + }); + + function createSingleEditOp(text: string, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): IIdentifiedSingleEditOperation { + var range = new Range( + selectionLineNumber, + selectionColumn, + positionLineNumber, + positionColumn + ); + + return { + identifier: { + major: 0, + minor: 0 + }, + range: range, + text: text, + forceMoveMarkers: false + }; + } + + function assertSingleEditOp(singleEditOp: IIdentifiedSingleEditOperation, editedLines: string[]) { + var editOp = [singleEditOp]; + + var inverseEditOp = model.applyEdits(editOp); + + assert.equal(model.getLineCount(), editedLines.length); + for (var i = 0; i < editedLines.length; i++) { + assert.equal(model.getLineContent(i + 1), editedLines[i]); + } + + var originalOp = model.applyEdits(inverseEditOp); + + assert.equal(model.getLineCount(), 5); + assert.equal(model.getLineContent(1), LINE1); + assert.equal(model.getLineContent(2), LINE2); + assert.equal(model.getLineContent(3), LINE3); + assert.equal(model.getLineContent(4), LINE4); + assert.equal(model.getLineContent(5), LINE5); + + assert.deepEqual(originalOp, editOp); + } + + test('Insert inline', () => { + assertSingleEditOp( + createSingleEditOp('a', 1, 1), + [ + 'aMy First Line', + LINE2, + LINE3, + LINE4, + LINE5 + ] + ); + }); + + test('Replace inline/inline 1', () => { + assertSingleEditOp( + createSingleEditOp(' incredibly awesome', 1, 3), + [ + 'My incredibly awesome First Line', + LINE2, + LINE3, + LINE4, + LINE5 + ] + ); + }); + + test('Replace inline/inline 2', () => { + assertSingleEditOp( + createSingleEditOp(' with text at the end.', 1, 14), + [ + 'My First Line with text at the end.', + LINE2, + LINE3, + LINE4, + LINE5 + ] + ); + }); + + test('Replace inline/inline 3', () => { + assertSingleEditOp( + createSingleEditOp('My new First Line.', 1, 1, 1, 14), + [ + 'My new First Line.', + LINE2, + LINE3, + LINE4, + LINE5 + ] + ); + }); + + test('Replace inline/multi line 1', () => { + assertSingleEditOp( + createSingleEditOp('My new First Line.', 1, 1, 3, 15), + [ + 'My new First Line.', + LINE4, + LINE5 + ] + ); + }); + + test('Replace inline/multi line 2', () => { + assertSingleEditOp( + createSingleEditOp('My new First Line.', 1, 2, 3, 15), + [ + 'MMy new First Line.', + LINE4, + LINE5 + ] + ); + }); + + test('Replace inline/multi line 3', () => { + assertSingleEditOp( + createSingleEditOp('My new First Line.', 1, 2, 3, 2), + [ + 'MMy new First Line. Third Line', + LINE4, + LINE5 + ] + ); + }); + + test('Replace muli line/multi line', () => { + assertSingleEditOp( + createSingleEditOp('1\n2\n3\n4\n', 1, 1), + [ + '1', + '2', + '3', + '4', + LINE1, + LINE2, + LINE3, + LINE4, + LINE5 + ] + ); + }); +}); diff --git a/src/vs/editor/test/common/model/textModel.test.ts b/src/vs/editor/test/common/model/textModel.test.ts new file mode 100644 index 0000000000..cc2225428e --- /dev/null +++ b/src/vs/editor/test/common/model/textModel.test.ts @@ -0,0 +1,997 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { TextModel, ITextModelCreationData } from 'vs/editor/common/model/textModel'; +import { DefaultEndOfLine, TextModelResolvedOptions } from 'vs/editor/common/editorCommon'; +import { RawTextSource } from 'vs/editor/common/model/textSource'; + +function testGuessIndentation(defaultInsertSpaces: boolean, defaultTabSize: number, expectedInsertSpaces: boolean, expectedTabSize: number, text: string[], msg?: string): void { + var m = TextModel.createFromString( + text.join('\n'), + { + tabSize: defaultTabSize, + insertSpaces: defaultInsertSpaces, + detectIndentation: true, + defaultEOL: DefaultEndOfLine.LF, + trimAutoWhitespace: true + } + ); + var r = m.getOptions(); + m.dispose(); + + assert.equal(r.insertSpaces, expectedInsertSpaces, msg); + assert.equal(r.tabSize, expectedTabSize, msg); +} + +function assertGuess(expectedInsertSpaces: boolean, expectedTabSize: number, text: string[], msg?: string): void { + if (typeof expectedInsertSpaces === 'undefined') { + // cannot guess insertSpaces + if (typeof expectedTabSize === 'undefined') { + // cannot guess tabSize + testGuessIndentation(true, 13370, true, 13370, text, msg); + testGuessIndentation(false, 13371, false, 13371, text, msg); + } else { + // can guess tabSize + testGuessIndentation(true, 13370, true, expectedTabSize, text, msg); + testGuessIndentation(false, 13371, false, expectedTabSize, text, msg); + } + } else { + // can guess insertSpaces + if (typeof expectedTabSize === 'undefined') { + // cannot guess tabSize + testGuessIndentation(true, 13370, expectedInsertSpaces, 13370, text, msg); + testGuessIndentation(false, 13371, expectedInsertSpaces, 13371, text, msg); + } else { + // can guess tabSize + testGuessIndentation(true, 13370, expectedInsertSpaces, expectedTabSize, text, msg); + testGuessIndentation(false, 13371, expectedInsertSpaces, expectedTabSize, text, msg); + } + } +} + +suite('TextModelData.fromString', () => { + + function testTextModelDataFromString(text: string, expected: ITextModelCreationData): void { + const rawTextSource = RawTextSource.fromString(text); + const actual = TextModel.resolveCreationData(rawTextSource, TextModel.DEFAULT_CREATION_OPTIONS); + assert.deepEqual(actual, expected); + } + + test('one line text', () => { + testTextModelDataFromString('Hello world!', { + text: { + BOM: '', + EOL: '\n', + length: 12, + 'lines': [ + 'Hello world!' + ], + containsRTL: false, + isBasicASCII: true + }, + options: new TextModelResolvedOptions({ + defaultEOL: DefaultEndOfLine.LF, + insertSpaces: true, + tabSize: 4, + trimAutoWhitespace: true, + }) + }); + }); + + test('multiline text', () => { + testTextModelDataFromString('Hello,\r\ndear friend\nHow\rare\r\nyou?', { + text: { + BOM: '', + EOL: '\r\n', + length: 33, + 'lines': [ + 'Hello,', + 'dear friend', + 'How', + 'are', + 'you?' + ], + containsRTL: false, + isBasicASCII: true + }, + options: new TextModelResolvedOptions({ + defaultEOL: DefaultEndOfLine.LF, + insertSpaces: true, + tabSize: 4, + trimAutoWhitespace: true, + }) + }); + }); + + test('Non Basic ASCII 1', () => { + testTextModelDataFromString('Hello,\nZürich', { + text: { + BOM: '', + EOL: '\n', + length: 13, + 'lines': [ + 'Hello,', + 'Zürich' + ], + containsRTL: false, + isBasicASCII: false + }, + options: new TextModelResolvedOptions({ + defaultEOL: DefaultEndOfLine.LF, + insertSpaces: true, + tabSize: 4, + trimAutoWhitespace: true, + }) + }); + }); + + test('containsRTL 1', () => { + testTextModelDataFromString('Hello,\nזוהי עובדה מבוססת שדעתו', { + text: { + BOM: '', + EOL: '\n', + length: 30, + 'lines': [ + 'Hello,', + 'זוהי עובדה מבוססת שדעתו' + ], + containsRTL: true, + isBasicASCII: false + }, + options: new TextModelResolvedOptions({ + defaultEOL: DefaultEndOfLine.LF, + insertSpaces: true, + tabSize: 4, + trimAutoWhitespace: true, + }) + }); + }); + + test('containsRTL 2', () => { + testTextModelDataFromString('Hello,\nهناك حقيقة مثبتة منذ زمن طويل', { + text: { + BOM: '', + EOL: '\n', + length: 36, + 'lines': [ + 'Hello,', + 'هناك حقيقة مثبتة منذ زمن طويل' + ], + containsRTL: true, + isBasicASCII: false + }, + options: new TextModelResolvedOptions({ + defaultEOL: DefaultEndOfLine.LF, + insertSpaces: true, + tabSize: 4, + trimAutoWhitespace: true, + }) + }); + }); + +}); + +suite('Editor Model - TextModel', () => { + + test('getValueLengthInRange', () => { + + var m = TextModel.createFromString('My First Line\r\nMy Second Line\r\nMy Third Line'); + assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length); + assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 14)), 'My First Line'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 1, 2, 1)), 'My First Line\r\n'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 1)), 'y First Line\r\n'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 2)), 'y First Line\r\nM'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 1000)), 'y First Line\r\nMy Second Line'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 3, 1)), 'y First Line\r\nMy Second Line\r\n'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 3, 1000)), 'y First Line\r\nMy Second Line\r\nMy Third Line'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 1, 1000, 1000)), 'My First Line\r\nMy Second Line\r\nMy Third Line'.length); + + m = TextModel.createFromString('My First Line\nMy Second Line\nMy Third Line'); + assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length); + assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 14)), 'My First Line'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 1, 2, 1)), 'My First Line\n'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 1)), 'y First Line\n'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 2)), 'y First Line\nM'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 2, 1000)), 'y First Line\nMy Second Line'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 3, 1)), 'y First Line\nMy Second Line\n'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 2, 3, 1000)), 'y First Line\nMy Second Line\nMy Third Line'.length); + assert.equal(m.getValueLengthInRange(new Range(1, 1, 1000, 1000)), 'My First Line\nMy Second Line\nMy Third Line'.length); + }); + + test('guess indentation 1', () => { + + assertGuess(undefined, undefined, [ + 'x', + 'x', + 'x', + 'x', + 'x', + 'x', + 'x' + ], 'no clues'); + + assertGuess(false, undefined, [ + '\tx', + 'x', + 'x', + 'x', + 'x', + 'x', + 'x' + ], 'no spaces, 1xTAB'); + + assertGuess(true, 2, [ + ' x', + 'x', + 'x', + 'x', + 'x', + 'x', + 'x' + ], '1x2'); + + assertGuess(false, undefined, [ + '\tx', + '\tx', + '\tx', + '\tx', + '\tx', + '\tx', + '\tx' + ], '7xTAB'); + + assertGuess(undefined, 2, [ + '\tx', + ' x', + '\tx', + ' x', + '\tx', + ' x', + '\tx', + ' x', + ], '4x2, 4xTAB'); + assertGuess(false, undefined, [ + '\tx', + ' x', + '\tx', + ' x', + '\tx', + ' x', + '\tx', + ' x' + ], '4x1, 4xTAB'); + assertGuess(false, 2, [ + '\tx', + '\tx', + ' x', + '\tx', + ' x', + '\tx', + ' x', + '\tx', + ' x', + ], '4x2, 5xTAB'); + assertGuess(false, 2, [ + '\tx', + '\tx', + 'x', + '\tx', + 'x', + '\tx', + 'x', + '\tx', + ' x', + ], '1x2, 5xTAB'); + assertGuess(false, 4, [ + '\tx', + '\tx', + 'x', + '\tx', + 'x', + '\tx', + 'x', + '\tx', + ' x', + ], '1x4, 5xTAB'); + assertGuess(false, 2, [ + '\tx', + '\tx', + 'x', + '\tx', + 'x', + '\tx', + ' x', + '\tx', + ' x', + ], '1x2, 1x4, 5xTAB'); + + assertGuess(undefined, undefined, [ + 'x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x' + ], '7x1 - 1 space is never guessed as an indentation'); + assertGuess(true, undefined, [ + 'x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x' + ], '1x10, 6x1'); + assertGuess(undefined, undefined, [ + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ], 'whitespace lines don\'t count'); + assertGuess(true, 4, [ + 'x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + ], 'odd number is not allowed: 6x3, 3x4'); + assertGuess(true, 4, [ + 'x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + ], 'odd number is not allowed: 6x5, 3x4'); + assertGuess(true, 4, [ + 'x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + ], 'odd number is not allowed: 6x7, 3x4'); + assertGuess(true, 2, [ + 'x', + ' x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + ' x', + ], '8x2'); + + assertGuess(true, 2, [ + 'x', + ' x', + ' x', + 'x', + ' x', + ' x', + 'x', + ' x', + ' x', + 'x', + ' x', + ' x', + ], '8x2'); + assertGuess(true, 2, [ + 'x', + ' x', + ' x', + 'x', + ' x', + ' x', + 'x', + ' x', + ' x', + 'x', + ' x', + ' x', + ], '4x2, 4x4'); + assertGuess(true, 2, [ + 'x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + ], '6x2, 3x4'); + assertGuess(true, 2, [ + 'x', + ' x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + ' x', + ], '4x2, 4x4'); + assertGuess(true, 2, [ + 'x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + ], '2x2, 4x4'); + assertGuess(true, 4, [ + 'x', + ' x', + ' x', + 'x', + ' x', + ' x', + 'x', + ' x', + ' x', + 'x', + ' x', + ' x', + ], '8x4'); + assertGuess(true, 2, [ + 'x', + ' x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + ' x', + ], '2x2, 4x4, 2x6'); + assertGuess(true, 2, [ + 'x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x', + ], '1x2, 2x4, 2x6, 1x8'); + assertGuess(true, 4, [ + 'x', + ' x', + ' x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + ' x', + ' x', + ], '6x4, 2x5, 2x8'); + assertGuess(true, 4, [ + 'x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x', + ], '3x4, 1x5, 2x8'); + assertGuess(true, 4, [ + 'x', + 'x', + ' x', + ' x', + ' x', + ' x', + ' x', + 'x', + 'x', + ' x', + ' x', + ' x', + ' x', + ' x', + ], '6x4, 2x5, 4x8'); + assertGuess(true, 4, [ + 'x', + ' x', + ' x', + ' x', + ' x', + ' x', + 'x', + ' x', + ' x', + ' x', + ], '5x1, 2x0, 1x3, 2x4'); + assertGuess(false, undefined, [ + '\t x', + ' \t x', + '\tx' + ], 'mixed whitespace 1'); + assertGuess(false, 4, [ + '\tx', + '\t x' + ], 'mixed whitespace 2'); + }); + + test('validatePosition', () => { + + let m = TextModel.createFromString('line one\nline two'); + + assert.deepEqual(m.validatePosition(new Position(0, 0)), new Position(1, 1)); + assert.deepEqual(m.validatePosition(new Position(0, 1)), new Position(1, 1)); + + assert.deepEqual(m.validatePosition(new Position(1, 1)), new Position(1, 1)); + assert.deepEqual(m.validatePosition(new Position(1, 2)), new Position(1, 2)); + assert.deepEqual(m.validatePosition(new Position(1, 30)), new Position(1, 9)); + + assert.deepEqual(m.validatePosition(new Position(2, 0)), new Position(2, 1)); + assert.deepEqual(m.validatePosition(new Position(2, 1)), new Position(2, 1)); + assert.deepEqual(m.validatePosition(new Position(2, 2)), new Position(2, 2)); + assert.deepEqual(m.validatePosition(new Position(2, 30)), new Position(2, 9)); + + assert.deepEqual(m.validatePosition(new Position(3, 0)), new Position(2, 9)); + assert.deepEqual(m.validatePosition(new Position(3, 1)), new Position(2, 9)); + assert.deepEqual(m.validatePosition(new Position(3, 30)), new Position(2, 9)); + + assert.deepEqual(m.validatePosition(new Position(30, 30)), new Position(2, 9)); + + assert.deepEqual(m.validatePosition(new Position(-123.123, -0.5)), new Position(1, 1)); + assert.deepEqual(m.validatePosition(new Position(Number.MIN_VALUE, Number.MIN_VALUE)), new Position(1, 1)); + + assert.deepEqual(m.validatePosition(new Position(Number.MAX_VALUE, Number.MAX_VALUE)), new Position(2, 9)); + assert.deepEqual(m.validatePosition(new Position(123.23, 47.5)), new Position(2, 9)); + }); + + test('validatePosition around high-low surrogate pairs 1', () => { + + let m = TextModel.createFromString('a📚b'); + + assert.deepEqual(m.validatePosition(new Position(0, 0)), new Position(1, 1)); + assert.deepEqual(m.validatePosition(new Position(0, 1)), new Position(1, 1)); + assert.deepEqual(m.validatePosition(new Position(0, 7)), new Position(1, 1)); + + assert.deepEqual(m.validatePosition(new Position(1, 1)), new Position(1, 1)); + assert.deepEqual(m.validatePosition(new Position(1, 2)), new Position(1, 2)); + assert.deepEqual(m.validatePosition(new Position(1, 3)), new Position(1, 2)); + assert.deepEqual(m.validatePosition(new Position(1, 4)), new Position(1, 4)); + assert.deepEqual(m.validatePosition(new Position(1, 5)), new Position(1, 5)); + assert.deepEqual(m.validatePosition(new Position(1, 30)), new Position(1, 5)); + + assert.deepEqual(m.validatePosition(new Position(2, 0)), new Position(1, 5)); + assert.deepEqual(m.validatePosition(new Position(2, 1)), new Position(1, 5)); + assert.deepEqual(m.validatePosition(new Position(2, 2)), new Position(1, 5)); + assert.deepEqual(m.validatePosition(new Position(2, 30)), new Position(1, 5)); + + assert.deepEqual(m.validatePosition(new Position(-123.123, -0.5)), new Position(1, 1)); + assert.deepEqual(m.validatePosition(new Position(Number.MIN_VALUE, Number.MIN_VALUE)), new Position(1, 1)); + + assert.deepEqual(m.validatePosition(new Position(Number.MAX_VALUE, Number.MAX_VALUE)), new Position(1, 5)); + assert.deepEqual(m.validatePosition(new Position(123.23, 47.5)), new Position(1, 5)); + }); + + test('validatePosition around high-low surrogate pairs 2', () => { + + let m = TextModel.createFromString('a📚📚b'); + + assert.deepEqual(m.validatePosition(new Position(1, 1)), new Position(1, 1)); + assert.deepEqual(m.validatePosition(new Position(1, 2)), new Position(1, 2)); + assert.deepEqual(m.validatePosition(new Position(1, 3)), new Position(1, 2)); + assert.deepEqual(m.validatePosition(new Position(1, 4)), new Position(1, 4)); + assert.deepEqual(m.validatePosition(new Position(1, 5)), new Position(1, 4)); + assert.deepEqual(m.validatePosition(new Position(1, 6)), new Position(1, 6)); + assert.deepEqual(m.validatePosition(new Position(1, 7)), new Position(1, 7)); + + }); + + test('validateRange around high-low surrogate pairs 1', () => { + + let m = TextModel.createFromString('a📚b'); + + assert.deepEqual(m.validateRange(new Range(0, 0, 0, 1)), new Range(1, 1, 1, 1)); + assert.deepEqual(m.validateRange(new Range(0, 0, 0, 7)), new Range(1, 1, 1, 1)); + + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 1)), new Range(1, 1, 1, 1)); + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 2)), new Range(1, 1, 1, 2)); + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 3)), new Range(1, 1, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 4)), new Range(1, 1, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 5)), new Range(1, 1, 1, 5)); + + assert.deepEqual(m.validateRange(new Range(1, 2, 1, 2)), new Range(1, 2, 1, 2)); + assert.deepEqual(m.validateRange(new Range(1, 2, 1, 3)), new Range(1, 2, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 2, 1, 4)), new Range(1, 2, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 2, 1, 5)), new Range(1, 2, 1, 5)); + + assert.deepEqual(m.validateRange(new Range(1, 3, 1, 3)), new Range(1, 2, 1, 2)); + assert.deepEqual(m.validateRange(new Range(1, 3, 1, 4)), new Range(1, 2, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 3, 1, 5)), new Range(1, 2, 1, 5)); + + assert.deepEqual(m.validateRange(new Range(1, 4, 1, 4)), new Range(1, 4, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 4, 1, 5)), new Range(1, 4, 1, 5)); + + assert.deepEqual(m.validateRange(new Range(1, 5, 1, 5)), new Range(1, 5, 1, 5)); + }); + + test('validateRange around high-low surrogate pairs 2', () => { + + let m = TextModel.createFromString('a📚📚b'); + + assert.deepEqual(m.validateRange(new Range(0, 0, 0, 1)), new Range(1, 1, 1, 1)); + assert.deepEqual(m.validateRange(new Range(0, 0, 0, 7)), new Range(1, 1, 1, 1)); + + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 1)), new Range(1, 1, 1, 1)); + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 2)), new Range(1, 1, 1, 2)); + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 3)), new Range(1, 1, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 4)), new Range(1, 1, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 5)), new Range(1, 1, 1, 6)); + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 6)), new Range(1, 1, 1, 6)); + assert.deepEqual(m.validateRange(new Range(1, 1, 1, 7)), new Range(1, 1, 1, 7)); + + assert.deepEqual(m.validateRange(new Range(1, 2, 1, 2)), new Range(1, 2, 1, 2)); + assert.deepEqual(m.validateRange(new Range(1, 2, 1, 3)), new Range(1, 2, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 2, 1, 4)), new Range(1, 2, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 2, 1, 5)), new Range(1, 2, 1, 6)); + assert.deepEqual(m.validateRange(new Range(1, 2, 1, 6)), new Range(1, 2, 1, 6)); + assert.deepEqual(m.validateRange(new Range(1, 2, 1, 7)), new Range(1, 2, 1, 7)); + + assert.deepEqual(m.validateRange(new Range(1, 3, 1, 3)), new Range(1, 2, 1, 2)); + assert.deepEqual(m.validateRange(new Range(1, 3, 1, 4)), new Range(1, 2, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 3, 1, 5)), new Range(1, 2, 1, 6)); + assert.deepEqual(m.validateRange(new Range(1, 3, 1, 6)), new Range(1, 2, 1, 6)); + assert.deepEqual(m.validateRange(new Range(1, 3, 1, 7)), new Range(1, 2, 1, 7)); + + assert.deepEqual(m.validateRange(new Range(1, 4, 1, 4)), new Range(1, 4, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 4, 1, 5)), new Range(1, 4, 1, 6)); + assert.deepEqual(m.validateRange(new Range(1, 4, 1, 6)), new Range(1, 4, 1, 6)); + assert.deepEqual(m.validateRange(new Range(1, 4, 1, 7)), new Range(1, 4, 1, 7)); + + assert.deepEqual(m.validateRange(new Range(1, 5, 1, 5)), new Range(1, 4, 1, 4)); + assert.deepEqual(m.validateRange(new Range(1, 5, 1, 6)), new Range(1, 4, 1, 6)); + assert.deepEqual(m.validateRange(new Range(1, 5, 1, 7)), new Range(1, 4, 1, 7)); + + assert.deepEqual(m.validateRange(new Range(1, 6, 1, 6)), new Range(1, 6, 1, 6)); + assert.deepEqual(m.validateRange(new Range(1, 6, 1, 7)), new Range(1, 6, 1, 7)); + + assert.deepEqual(m.validateRange(new Range(1, 7, 1, 7)), new Range(1, 7, 1, 7)); + }); + + test('modifyPosition', () => { + + var m = TextModel.createFromString('line one\nline two'); + assert.deepEqual(m.modifyPosition(new Position(1, 1), 0), new Position(1, 1)); + assert.deepEqual(m.modifyPosition(new Position(0, 0), 0), new Position(1, 1)); + assert.deepEqual(m.modifyPosition(new Position(30, 1), 0), new Position(2, 9)); + + assert.deepEqual(m.modifyPosition(new Position(1, 1), 17), new Position(2, 9)); + assert.deepEqual(m.modifyPosition(new Position(1, 1), 1), new Position(1, 2)); + assert.deepEqual(m.modifyPosition(new Position(1, 1), 3), new Position(1, 4)); + assert.deepEqual(m.modifyPosition(new Position(1, 2), 10), new Position(2, 3)); + assert.deepEqual(m.modifyPosition(new Position(1, 5), 13), new Position(2, 9)); + assert.deepEqual(m.modifyPosition(new Position(1, 2), 16), new Position(2, 9)); + + assert.deepEqual(m.modifyPosition(new Position(2, 9), -17), new Position(1, 1)); + assert.deepEqual(m.modifyPosition(new Position(1, 2), -1), new Position(1, 1)); + assert.deepEqual(m.modifyPosition(new Position(1, 4), -3), new Position(1, 1)); + assert.deepEqual(m.modifyPosition(new Position(2, 3), -10), new Position(1, 2)); + assert.deepEqual(m.modifyPosition(new Position(2, 9), -13), new Position(1, 5)); + assert.deepEqual(m.modifyPosition(new Position(2, 9), -16), new Position(1, 2)); + + assert.deepEqual(m.modifyPosition(new Position(1, 2), 17), new Position(2, 9)); + assert.deepEqual(m.modifyPosition(new Position(1, 2), 100), new Position(2, 9)); + + assert.deepEqual(m.modifyPosition(new Position(1, 2), -2), new Position(1, 1)); + assert.deepEqual(m.modifyPosition(new Position(1, 2), -100), new Position(1, 1)); + assert.deepEqual(m.modifyPosition(new Position(2, 2), -100), new Position(1, 1)); + assert.deepEqual(m.modifyPosition(new Position(2, 9), -18), new Position(1, 1)); + }); + + test('normalizeIndentation 1', () => { + let model = TextModel.createFromString('', + { + detectIndentation: false, + tabSize: 4, + insertSpaces: false, + trimAutoWhitespace: true, + defaultEOL: DefaultEndOfLine.LF + } + ); + + assert.equal(model.normalizeIndentation('\t'), '\t'); + assert.equal(model.normalizeIndentation(' '), '\t'); + assert.equal(model.normalizeIndentation(' '), ' '); + assert.equal(model.normalizeIndentation(' '), ' '); + assert.equal(model.normalizeIndentation(' '), ' '); + assert.equal(model.normalizeIndentation(''), ''); + assert.equal(model.normalizeIndentation(' \t '), '\t\t'); + assert.equal(model.normalizeIndentation(' \t '), '\t '); + assert.equal(model.normalizeIndentation(' \t '), '\t '); + assert.equal(model.normalizeIndentation(' \t'), '\t '); + + assert.equal(model.normalizeIndentation('\ta'), '\ta'); + assert.equal(model.normalizeIndentation(' a'), '\ta'); + assert.equal(model.normalizeIndentation(' a'), ' a'); + assert.equal(model.normalizeIndentation(' a'), ' a'); + assert.equal(model.normalizeIndentation(' a'), ' a'); + assert.equal(model.normalizeIndentation('a'), 'a'); + assert.equal(model.normalizeIndentation(' \t a'), '\t\ta'); + assert.equal(model.normalizeIndentation(' \t a'), '\t a'); + assert.equal(model.normalizeIndentation(' \t a'), '\t a'); + assert.equal(model.normalizeIndentation(' \ta'), '\t a'); + + model.dispose(); + }); + + test('normalizeIndentation 2', () => { + let model = TextModel.createFromString('', + { + detectIndentation: false, + tabSize: 4, + insertSpaces: true, + trimAutoWhitespace: true, + defaultEOL: DefaultEndOfLine.LF + } + ); + + assert.equal(model.normalizeIndentation('\ta'), ' a'); + assert.equal(model.normalizeIndentation(' a'), ' a'); + assert.equal(model.normalizeIndentation(' a'), ' a'); + assert.equal(model.normalizeIndentation(' a'), ' a'); + assert.equal(model.normalizeIndentation(' a'), ' a'); + assert.equal(model.normalizeIndentation('a'), 'a'); + assert.equal(model.normalizeIndentation(' \t a'), ' a'); + assert.equal(model.normalizeIndentation(' \t a'), ' a'); + assert.equal(model.normalizeIndentation(' \t a'), ' a'); + assert.equal(model.normalizeIndentation(' \ta'), ' a'); + + model.dispose(); + }); +}); + +suite('TextModel.mightContainRTL', () => { + + test('nope', () => { + let model = TextModel.createFromString('hello world!'); + assert.equal(model.mightContainRTL(), false); + }); + + test('yes', () => { + let model = TextModel.createFromString('Hello,\nזוהי עובדה מבוססת שדעתו'); + assert.equal(model.mightContainRTL(), true); + }); + + test('setValue resets 1', () => { + let model = TextModel.createFromString('hello world!'); + assert.equal(model.mightContainRTL(), false); + model.setValue('Hello,\nזוהי עובדה מבוססת שדעתו'); + assert.equal(model.mightContainRTL(), true); + }); + + test('setValue resets 2', () => { + let model = TextModel.createFromString('Hello,\nهناك حقيقة مثبتة منذ زمن طويل'); + assert.equal(model.mightContainRTL(), true); + model.setValue('hello world!'); + assert.equal(model.mightContainRTL(), false); + }); + +}); + +suite('TextModel.getLineIndentGuide', () => { + function assertIndentGuides(lines: [number, string][]): void { + let text = lines.map(l => l[1]).join('\n'); + let model = TextModel.createFromString(text); + + let actual: [number, string][] = []; + for (let line = 1; line <= model.getLineCount(); line++) { + actual[line - 1] = [model.getLineIndentGuide(line), model.getLineContent(line)]; + } + + // let expected = lines.map(l => l[0]); + + assert.deepEqual(actual, lines); + + model.dispose(); + } + + test('getLineIndentGuide one level', () => { + assertIndentGuides([ + [0, 'A'], + [1, ' A'], + [1, ' A'], + [1, ' A'], + ]); + }); + + test('getLineIndentGuide two levels', () => { + assertIndentGuides([ + [0, 'A'], + [1, ' A'], + [1, ' A'], + [1, ' A'], + [1, ' A'], + ]); + }); + + test('getLineIndentGuide three levels', () => { + assertIndentGuides([ + [0, 'A'], + [1, ' A'], + [1, ' A'], + [2, ' A'], + [0, 'A'], + ]); + }); + + test('getLineIndentGuide decreasing indent', () => { + assertIndentGuides([ + [0, ' A'], + [0, ' A'], + [0, 'A'], + ]); + }); + + test('getLineIndentGuide Java', () => { + assertIndentGuides([ + /* 1*/[0, 'class A {'], + /* 2*/[1, ' void foo() {'], + /* 3*/[1, ' console.log(1);'], + /* 4*/[1, ' console.log(2);'], + /* 5*/[1, ' }'], + /* 6*/[1, ''], + /* 7*/[1, ' void bar() {'], + /* 8*/[1, ' console.log(3);'], + /* 9*/[1, ' }'], + /*10*/[0, '}'], + /*11*/[0, 'interface B {'], + /*12*/[1, ' void bar();'], + /*13*/[0, '}'], + ]); + }); + + test('getLineIndentGuide Javadoc', () => { + assertIndentGuides([ + [0, '/**'], + [1, ' * Comment'], + [1, ' */'], + [0, 'class A {'], + [1, ' void foo() {'], + [1, ' }'], + [0, '}'], + ]); + }); + + test('getLineIndentGuide Whitespace', () => { + assertIndentGuides([ + [0, 'class A {'], + [1, ''], + [1, ' void foo() {'], + [1, ' '], + [1, ' return 1;'], + [1, ' }'], + [1, ' '], + [0, '}'], + ]); + }); + + test('getLineIndentGuide Tabs', () => { + assertIndentGuides([ + [0, 'class A {'], + [1, '\t\t'], + [1, '\tvoid foo() {'], + [2, '\t \t//hello'], + [2, '\t return 2;'], + [1, ' \t}'], + [1, ' '], + [0, '}'], + ]); + }); + + test('getLineIndentGuide checker.ts', () => { + assertIndentGuides([ + /* 1*/[0, '/// '], + /* 2*/[0, ''], + /* 3*/[0, '/* @internal */'], + /* 4*/[0, 'namespace ts {'], + /* 5*/[1, ' let nextSymbolId = 1;'], + /* 6*/[1, ' let nextNodeId = 1;'], + /* 7*/[1, ' let nextMergeId = 1;'], + /* 8*/[1, ' let nextFlowId = 1;'], + /* 9*/[1, ''], + /*10*/[1, ' export function getNodeId(node: Node): number {'], + /*11*/[2, ' if (!node.id) {'], + /*12*/[3, ' node.id = nextNodeId;'], + /*13*/[3, ' nextNodeId++;'], + /*14*/[2, ' }'], + /*15*/[2, ' return node.id;'], + /*16*/[1, ' }'], + /*17*/[0, '}'], + ]); + }); + + test('issue #8425 - Missing indentation lines for first level indentation', () => { + assertIndentGuides([ + [1, '\tindent1'], + [2, '\t\tindent2'], + [2, '\t\tindent2'], + [1, '\tindent1'], + ]); + }); + + test('issue #8952 - Indentation guide lines going through text on .yml file', () => { + assertIndentGuides([ + [0, 'properties:'], + [1, ' emailAddress:'], + [2, ' - bla'], + [2, ' - length:'], + [3, ' max: 255'], + [0, 'getters:'], + ]); + }); + + test('issue #11892 - Indent guides look funny', () => { + assertIndentGuides([ + [0, 'function test(base) {'], + [1, '\tswitch (base) {'], + [2, '\t\tcase 1:'], + [3, '\t\t\treturn 1;'], + [2, '\t\tcase 2:'], + [3, '\t\t\treturn 2;'], + [1, '\t}'], + [0, '}'], + ]); + }); + + test('issue #12398 - Problem in indent guidelines', () => { + assertIndentGuides([ + [2, '\t\t.bla'], + [3, '\t\t\tlabel(for)'], + [0, 'include script'], + ]); + }); +}); diff --git a/src/vs/editor/test/common/model/textModelSearch.test.ts b/src/vs/editor/test/common/model/textModelSearch.test.ts new file mode 100644 index 0000000000..ea28443e94 --- /dev/null +++ b/src/vs/editor/test/common/model/textModelSearch.test.ts @@ -0,0 +1,636 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Position } from 'vs/editor/common/core/position'; +import { FindMatch, EndOfLineSequence } from 'vs/editor/common/editorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { TextModelSearch, SearchParams, SearchData } from 'vs/editor/common/model/textModelSearch'; +import { getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; + +// --------- Find +suite('TextModelSearch', () => { + + const usualWordSeparators = getMapForWordSeparators(USUAL_WORD_SEPARATORS); + + function assertFindMatch(actual: FindMatch, expectedRange: Range, expectedMatches: string[] = null): void { + assert.deepEqual(actual, new FindMatch(expectedRange, expectedMatches)); + } + + function _assertFindMatches(model: TextModel, searchParams: SearchParams, expectedMatches: FindMatch[]): void { + let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), false, 1000); + assert.deepEqual(actual, expectedMatches, 'findMatches OK'); + + // test `findNextMatch` + let startPos = new Position(1, 1); + let match = TextModelSearch.findNextMatch(model, searchParams, startPos, false); + assert.deepEqual(match, expectedMatches[0], `findNextMatch ${startPos}`); + for (let i = 0; i < expectedMatches.length; i++) { + startPos = expectedMatches[i].range.getStartPosition(); + match = TextModelSearch.findNextMatch(model, searchParams, startPos, false); + assert.deepEqual(match, expectedMatches[i], `findNextMatch ${startPos}`); + } + + // test `findPrevMatch` + startPos = new Position(model.getLineCount(), model.getLineMaxColumn(model.getLineCount())); + match = TextModelSearch.findPreviousMatch(model, searchParams, startPos, false); + assert.deepEqual(match, expectedMatches[expectedMatches.length - 1], `findPrevMatch ${startPos}`); + for (let i = 0; i < expectedMatches.length; i++) { + startPos = expectedMatches[i].range.getEndPosition(); + match = TextModelSearch.findPreviousMatch(model, searchParams, startPos, false); + assert.deepEqual(match, expectedMatches[i], `findPrevMatch ${startPos}`); + } + } + + function assertFindMatches(text: string, searchString: string, isRegex: boolean, matchCase: boolean, wordSeparators: string, _expected: [number, number, number, number][]): void { + let expectedRanges = _expected.map(entry => new Range(entry[0], entry[1], entry[2], entry[3])); + let expectedMatches = expectedRanges.map(entry => new FindMatch(entry, null)); + let searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators); + + let model = TextModel.createFromString(text); + _assertFindMatches(model, searchParams, expectedMatches); + model.dispose(); + + + let model2 = TextModel.createFromString(text); + model2.setEOL(EndOfLineSequence.CRLF); + _assertFindMatches(model2, searchParams, expectedMatches); + model2.dispose(); + } + + let regularText = [ + 'This is some foo - bar text which contains foo and bar - as in Barcelona.', + 'Now it begins a word fooBar and now it is caps Foo-isn\'t this great?', + 'And here\'s a dull line with nothing interesting in it', + 'It is also interesting if it\'s part of a word like amazingFooBar', + 'Again nothing interesting here' + ]; + + test('Simple find', () => { + assertFindMatches( + regularText.join('\n'), + 'foo', false, false, null, + [ + [1, 14, 1, 17], + [1, 44, 1, 47], + [2, 22, 2, 25], + [2, 48, 2, 51], + [4, 59, 4, 62] + ] + ); + }); + + test('Case sensitive find', () => { + assertFindMatches( + regularText.join('\n'), + 'foo', false, true, null, + [ + [1, 14, 1, 17], + [1, 44, 1, 47], + [2, 22, 2, 25] + ] + ); + }); + + test('Whole words find', () => { + assertFindMatches( + regularText.join('\n'), + 'foo', false, false, USUAL_WORD_SEPARATORS, + [ + [1, 14, 1, 17], + [1, 44, 1, 47], + [2, 48, 2, 51] + ] + ); + }); + + test('/^/ find', () => { + assertFindMatches( + regularText.join('\n'), + '^', true, false, null, + [ + [1, 1, 1, 1], + [2, 1, 2, 1], + [3, 1, 3, 1], + [4, 1, 4, 1], + [5, 1, 5, 1] + ] + ); + }); + + test('/$/ find', () => { + assertFindMatches( + regularText.join('\n'), + '$', true, false, null, + [ + [1, 74, 1, 74], + [2, 69, 2, 69], + [3, 54, 3, 54], + [4, 65, 4, 65], + [5, 31, 5, 31] + ] + ); + }); + + test('/.*/ find', () => { + assertFindMatches( + regularText.join('\n'), + '.*', true, false, null, + [ + [1, 1, 1, 74], + [2, 1, 2, 69], + [3, 1, 3, 54], + [4, 1, 4, 65], + [5, 1, 5, 31] + ] + ); + }); + + test('/^$/ find', () => { + assertFindMatches( + [ + 'This is some foo - bar text which contains foo and bar - as in Barcelona.', + '', + 'And here\'s a dull line with nothing interesting in it', + '', + 'Again nothing interesting here' + ].join('\n'), + '^$', true, false, null, + [ + [2, 1, 2, 1], + [4, 1, 4, 1] + ] + ); + }); + + test('multiline find 1', () => { + assertFindMatches( + [ + 'Just some text text', + 'Just some text text', + 'some text again', + 'again some text' + ].join('\n'), + 'text\\n', true, false, null, + [ + [1, 16, 2, 1], + [2, 16, 3, 1], + ] + ); + }); + + test('multiline find 2', () => { + assertFindMatches( + [ + 'Just some text text', + 'Just some text text', + 'some text again', + 'again some text' + ].join('\n'), + 'text\\nJust', true, false, null, + [ + [1, 16, 2, 5] + ] + ); + }); + + test('multiline find 3', () => { + assertFindMatches( + [ + 'Just some text text', + 'Just some text text', + 'some text again', + 'again some text' + ].join('\n'), + '\\nagain', true, false, null, + [ + [3, 16, 4, 6] + ] + ); + }); + + test('multiline find 4', () => { + assertFindMatches( + [ + 'Just some text text', + 'Just some text text', + 'some text again', + 'again some text' + ].join('\n'), + '.*\\nJust.*\\n', true, false, null, + [ + [1, 1, 3, 1] + ] + ); + }); + + test('multiline find with line beginning regex', () => { + assertFindMatches( + [ + 'if', + 'else', + '', + 'if', + 'else' + ].join('\n'), + '^if\\nelse', true, false, null, + [ + [1, 1, 2, 5], + [4, 1, 5, 5] + ] + ); + }); + + test('matching empty lines using boundary expression', () => { + assertFindMatches( + [ + 'if', + '', + 'else', + ' ', + 'if', + ' ', + 'else' + ].join('\n'), + '^\\s*$\\n', true, false, null, + [ + [2, 1, 3, 1], + [4, 1, 5, 1], + [6, 1, 7, 1] + ] + ); + }); + + test('matching lines starting with A and ending with B', () => { + assertFindMatches( + [ + 'a if b', + 'a', + 'ab', + 'eb' + ].join('\n'), + '^a.*b$', true, false, null, + [ + [1, 1, 1, 7], + [3, 1, 3, 3] + ] + ); + }); + + test('multiline find with line ending regex', () => { + assertFindMatches( + [ + 'if', + 'else', + '', + 'if', + 'elseif', + 'else' + ].join('\n'), + 'if\\nelse$', true, false, null, + [ + [1, 1, 2, 5], + [5, 5, 6, 5] + ] + ); + }); + + test('issue #4836 - ^.*$', () => { + assertFindMatches( + [ + 'Just some text text', + '', + 'some text again', + '', + 'again some text' + ].join('\n'), + '^.*$', true, false, null, + [ + [1, 1, 1, 20], + [2, 1, 2, 1], + [3, 1, 3, 16], + [4, 1, 4, 1], + [5, 1, 5, 16], + ] + ); + }); + + test('multiline find for non-regex string', () => { + assertFindMatches( + [ + 'Just some text text', + 'some text text', + 'some text again', + 'again some text', + 'but not some' + ].join('\n'), + 'text\nsome', false, false, null, + [ + [1, 16, 2, 5], + [2, 11, 3, 5], + ] + ); + }); + + test('issue #3623: Match whole word does not work for not latin characters', () => { + assertFindMatches( + [ + 'я', + 'компилятор', + 'обфускация', + ':я-я' + ].join('\n'), + 'я', false, false, USUAL_WORD_SEPARATORS, + [ + [1, 1, 1, 2], + [4, 2, 4, 3], + [4, 4, 4, 5], + ] + ); + }); + + test('issue #27459: Match whole words regression', () => { + assertFindMatches( + [ + 'this._register(this._textAreaInput.onKeyDown((e: IKeyboardEvent) => {', + ' this._viewController.emitKeyDown(e);', + '}));', + ].join('\n'), + '((e: ', false, false, USUAL_WORD_SEPARATORS, + [ + [1, 45, 1, 50] + ] + ); + }); + + test('issue #27594: Search results disappear', () => { + assertFindMatches( + [ + 'this.server.listen(0);', + ].join('\n'), + 'listen(', false, false, USUAL_WORD_SEPARATORS, + [ + [1, 13, 1, 20] + ] + ); + }); + + test('findNextMatch without regex', () => { + let model = TextModel.createFromString('line line one\nline two\nthree'); + + let searchParams = new SearchParams('line', false, false, null); + + let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), false); + assertFindMatch(actual, new Range(1, 1, 1, 5)); + + actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false); + assertFindMatch(actual, new Range(1, 6, 1, 10)); + + actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 3), false); + assertFindMatch(actual, new Range(1, 6, 1, 10)); + + actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false); + assertFindMatch(actual, new Range(2, 1, 2, 5)); + + actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false); + assertFindMatch(actual, new Range(1, 1, 1, 5)); + + model.dispose(); + }); + + test('findNextMatch with beginning boundary regex', () => { + let model = TextModel.createFromString('line one\nline two\nthree'); + + let searchParams = new SearchParams('^line', true, false, null); + + let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), false); + assertFindMatch(actual, new Range(1, 1, 1, 5)); + + actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false); + assertFindMatch(actual, new Range(2, 1, 2, 5)); + + actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 3), false); + assertFindMatch(actual, new Range(2, 1, 2, 5)); + + actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false); + assertFindMatch(actual, new Range(1, 1, 1, 5)); + + model.dispose(); + }); + + test('findNextMatch with beginning boundary regex and line has repetitive beginnings', () => { + let model = TextModel.createFromString('line line one\nline two\nthree'); + + let searchParams = new SearchParams('^line', true, false, null); + + let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), false); + assertFindMatch(actual, new Range(1, 1, 1, 5)); + + actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false); + assertFindMatch(actual, new Range(2, 1, 2, 5)); + + actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 3), false); + assertFindMatch(actual, new Range(2, 1, 2, 5)); + + actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false); + assertFindMatch(actual, new Range(1, 1, 1, 5)); + + model.dispose(); + }); + + test('findNextMatch with beginning boundary multiline regex and line has repetitive beginnings', () => { + let model = TextModel.createFromString('line line one\nline two\nline three\nline four'); + + let searchParams = new SearchParams('^line.*\\nline', true, false, null); + + let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), false); + assertFindMatch(actual, new Range(1, 1, 2, 5)); + + actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false); + assertFindMatch(actual, new Range(3, 1, 4, 5)); + + actual = TextModelSearch.findNextMatch(model, searchParams, new Position(2, 1), false); + assertFindMatch(actual, new Range(2, 1, 3, 5)); + + model.dispose(); + }); + + test('findNextMatch with ending boundary regex', () => { + let model = TextModel.createFromString('one line line\ntwo line\nthree'); + + let searchParams = new SearchParams('line$', true, false, null); + + let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), false); + assertFindMatch(actual, new Range(1, 10, 1, 14)); + + actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 4), false); + assertFindMatch(actual, new Range(1, 10, 1, 14)); + + actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false); + assertFindMatch(actual, new Range(2, 5, 2, 9)); + + actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false); + assertFindMatch(actual, new Range(1, 10, 1, 14)); + + model.dispose(); + }); + + test('findMatches with capturing matches', () => { + let model = TextModel.createFromString('one line line\ntwo line\nthree'); + + let searchParams = new SearchParams('(l(in)e)', true, false, null); + + let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 100); + assert.deepEqual(actual, [ + new FindMatch(new Range(1, 5, 1, 9), ['line', 'line', 'in']), + new FindMatch(new Range(1, 10, 1, 14), ['line', 'line', 'in']), + new FindMatch(new Range(2, 5, 2, 9), ['line', 'line', 'in']), + ]); + + model.dispose(); + }); + + test('findMatches multiline with capturing matches', () => { + let model = TextModel.createFromString('one line line\ntwo line\nthree'); + + let searchParams = new SearchParams('(l(in)e)\\n', true, false, null); + + let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 100); + assert.deepEqual(actual, [ + new FindMatch(new Range(1, 10, 2, 1), ['line\n', 'line', 'in']), + new FindMatch(new Range(2, 5, 3, 1), ['line\n', 'line', 'in']), + ]); + + model.dispose(); + }); + + test('findNextMatch with capturing matches', () => { + let model = TextModel.createFromString('one line line\ntwo line\nthree'); + + let searchParams = new SearchParams('(l(in)e)', true, false, null); + + let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true); + assertFindMatch(actual, new Range(1, 5, 1, 9), ['line', 'line', 'in']); + + model.dispose(); + }); + + test('findNextMatch multiline with capturing matches', () => { + let model = TextModel.createFromString('one line line\ntwo line\nthree'); + + let searchParams = new SearchParams('(l(in)e)\\n', true, false, null); + + let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true); + assertFindMatch(actual, new Range(1, 10, 2, 1), ['line\n', 'line', 'in']); + + model.dispose(); + }); + + test('findPreviousMatch with capturing matches', () => { + let model = TextModel.createFromString('one line line\ntwo line\nthree'); + + let searchParams = new SearchParams('(l(in)e)', true, false, null); + + let actual = TextModelSearch.findPreviousMatch(model, searchParams, new Position(1, 1), true); + assertFindMatch(actual, new Range(2, 5, 2, 9), ['line', 'line', 'in']); + + model.dispose(); + }); + + test('findPreviousMatch multiline with capturing matches', () => { + let model = TextModel.createFromString('one line line\ntwo line\nthree'); + + let searchParams = new SearchParams('(l(in)e)\\n', true, false, null); + + let actual = TextModelSearch.findPreviousMatch(model, searchParams, new Position(1, 1), true); + assertFindMatch(actual, new Range(2, 5, 3, 1), ['line\n', 'line', 'in']); + + model.dispose(); + }); + + test('\\n matches \\r\\n', () => { + let model = TextModel.createFromString('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni'); + + assert.equal(model.getEOL(), '\r\n'); + + let searchParams = new SearchParams('h\\n', true, false, null); + let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true); + actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 1000)[0]; + assertFindMatch(actual, new Range(8, 1, 9, 1), ['h\n']); + + searchParams = new SearchParams('g\\nh\\n', true, false, null); + actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true); + actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 1000)[0]; + assertFindMatch(actual, new Range(7, 1, 9, 1), ['g\nh\n']); + + searchParams = new SearchParams('\\ni', true, false, null); + actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true); + actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 1000)[0]; + assertFindMatch(actual, new Range(8, 2, 9, 2), ['\ni']); + + model.dispose(); + }); + + test('\\r can never be found', () => { + let model = TextModel.createFromString('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni'); + + assert.equal(model.getEOL(), '\r\n'); + + let searchParams = new SearchParams('\\r\\n', true, false, null); + let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true); + assert.equal(actual, null); + assert.deepEqual(TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 1000), []); + + model.dispose(); + }); + + function assertParseSearchResult(searchString: string, isRegex: boolean, matchCase: boolean, wordSeparators: string, expected: SearchData): void { + let searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators); + let actual = searchParams.parseSearchRequest(); + + if (expected === null) { + assert.ok(actual === null); + } else { + assert.deepEqual(actual.regex, expected.regex); + assert.deepEqual(actual.simpleSearch, expected.simpleSearch); + if (wordSeparators) { + assert.ok(actual.wordSeparators !== null); + } else { + assert.ok(actual.wordSeparators === null); + } + } + } + + test('parseSearchRequest invalid', () => { + assertParseSearchResult('', true, true, USUAL_WORD_SEPARATORS, null); + assertParseSearchResult(null, true, true, USUAL_WORD_SEPARATORS, null); + assertParseSearchResult('(', true, false, null, null); + }); + + test('parseSearchRequest non regex', () => { + assertParseSearchResult('foo', false, false, null, new SearchData(/foo/gi, null, null)); + assertParseSearchResult('foo', false, false, USUAL_WORD_SEPARATORS, new SearchData(/foo/gi, usualWordSeparators, null)); + assertParseSearchResult('foo', false, true, null, new SearchData(/foo/g, null, 'foo')); + assertParseSearchResult('foo', false, true, USUAL_WORD_SEPARATORS, new SearchData(/foo/g, usualWordSeparators, 'foo')); + assertParseSearchResult('foo\\n', false, false, null, new SearchData(/foo\\n/gi, null, null)); + assertParseSearchResult('foo\\\\n', false, false, null, new SearchData(/foo\\\\n/gi, null, null)); + assertParseSearchResult('foo\\r', false, false, null, new SearchData(/foo\\r/gi, null, null)); + assertParseSearchResult('foo\\\\r', false, false, null, new SearchData(/foo\\\\r/gi, null, null)); + }); + + test('parseSearchRequest regex', () => { + assertParseSearchResult('foo', true, false, null, new SearchData(/foo/gi, null, null)); + assertParseSearchResult('foo', true, false, USUAL_WORD_SEPARATORS, new SearchData(/foo/gi, usualWordSeparators, null)); + assertParseSearchResult('foo', true, true, null, new SearchData(/foo/g, null, null)); + assertParseSearchResult('foo', true, true, USUAL_WORD_SEPARATORS, new SearchData(/foo/g, usualWordSeparators, null)); + assertParseSearchResult('foo\\n', true, false, null, new SearchData(/foo\n/gim, null, null)); + assertParseSearchResult('foo\\\\n', true, false, null, new SearchData(/foo\\n/gi, null, null)); + assertParseSearchResult('foo\\r', true, false, null, new SearchData(/foo\r/gim, null, null)); + assertParseSearchResult('foo\\\\r', true, false, null, new SearchData(/foo\\r/gi, null, null)); + }); +}); diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts new file mode 100644 index 0000000000..162511a27a --- /dev/null +++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts @@ -0,0 +1,361 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Model } from 'vs/editor/common/model/model'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; +import { ITokenizationSupport, TokenizationRegistry, LanguageId, LanguageIdentifier, MetadataConsts } from 'vs/editor/common/modes'; +import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { IFoundBracket } from 'vs/editor/common/editorCommon'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { TextModelWithTokens } from 'vs/editor/common/model/textModelWithTokens'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { RawTextSource } from 'vs/editor/common/model/textSource'; + +suite('TextModelWithTokens', () => { + + function testBrackets(contents: string[], brackets: CharacterPair[]): void { + function toRelaxedFoundBracket(a: IFoundBracket) { + if (!a) { + return null; + } + return { + range: a.range.toString(), + open: a.open, + close: a.close, + isOpen: a.isOpen + }; + } + + let charIsBracket: { [char: string]: boolean } = {}; + let charIsOpenBracket: { [char: string]: boolean } = {}; + let openForChar: { [char: string]: string } = {}; + let closeForChar: { [char: string]: string } = {}; + brackets.forEach((b) => { + charIsBracket[b[0]] = true; + charIsBracket[b[1]] = true; + + charIsOpenBracket[b[0]] = true; + charIsOpenBracket[b[1]] = false; + + openForChar[b[0]] = b[0]; + closeForChar[b[0]] = b[1]; + + openForChar[b[1]] = b[0]; + closeForChar[b[1]] = b[1]; + }); + + let expectedBrackets: IFoundBracket[] = []; + for (let lineIndex = 0; lineIndex < contents.length; lineIndex++) { + let lineText = contents[lineIndex]; + + for (let charIndex = 0; charIndex < lineText.length; charIndex++) { + let ch = lineText.charAt(charIndex); + if (charIsBracket[ch]) { + expectedBrackets.push({ + open: openForChar[ch], + close: closeForChar[ch], + isOpen: charIsOpenBracket[ch], + range: new Range(lineIndex + 1, charIndex + 1, lineIndex + 1, charIndex + 2) + }); + } + } + } + + const languageIdentifier = new LanguageIdentifier('testMode', LanguageId.PlainText); + + let registration = LanguageConfigurationRegistry.register(languageIdentifier, { + brackets: brackets + }); + + let model = new TextModelWithTokens( + RawTextSource.fromString(contents.join('\n')), + TextModel.DEFAULT_CREATION_OPTIONS, + languageIdentifier + ); + + // findPrevBracket + { + let expectedBracketIndex = expectedBrackets.length - 1; + let currentExpectedBracket = expectedBracketIndex >= 0 ? expectedBrackets[expectedBracketIndex] : null; + for (let lineNumber = contents.length; lineNumber >= 1; lineNumber--) { + let lineText = contents[lineNumber - 1]; + + for (let column = lineText.length + 1; column >= 1; column--) { + + if (currentExpectedBracket) { + if (lineNumber === currentExpectedBracket.range.startLineNumber && column < currentExpectedBracket.range.endColumn) { + expectedBracketIndex--; + currentExpectedBracket = expectedBracketIndex >= 0 ? expectedBrackets[expectedBracketIndex] : null; + } + } + + let actual = model.findPrevBracket({ + lineNumber: lineNumber, + column: column + }); + + assert.deepEqual(toRelaxedFoundBracket(actual), toRelaxedFoundBracket(currentExpectedBracket), 'findPrevBracket of ' + lineNumber + ', ' + column); + } + } + } + + // findNextBracket + { + let expectedBracketIndex = 0; + let currentExpectedBracket = expectedBracketIndex < expectedBrackets.length ? expectedBrackets[expectedBracketIndex] : null; + for (let lineNumber = 1; lineNumber <= contents.length; lineNumber++) { + let lineText = contents[lineNumber - 1]; + + for (let column = 1; column <= lineText.length + 1; column++) { + + if (currentExpectedBracket) { + if (lineNumber === currentExpectedBracket.range.startLineNumber && column > currentExpectedBracket.range.startColumn) { + expectedBracketIndex++; + currentExpectedBracket = expectedBracketIndex < expectedBrackets.length ? expectedBrackets[expectedBracketIndex] : null; + } + } + + let actual = model.findNextBracket({ + lineNumber: lineNumber, + column: column + }); + + assert.deepEqual(toRelaxedFoundBracket(actual), toRelaxedFoundBracket(currentExpectedBracket), 'findNextBracket of ' + lineNumber + ', ' + column); + } + } + } + + model.dispose(); + registration.dispose(); + } + + test('brackets', () => { + testBrackets([ + 'if (a == 3) { return (7 * (a + 5)); }' + ], [ + ['{', '}'], + ['[', ']'], + ['(', ')'] + ]); + }); +}); + +suite('TextModelWithTokens - bracket matching', () => { + + function isNotABracket(model: Model, lineNumber: number, column: number) { + let match = model.matchBracket(new Position(lineNumber, column)); + assert.equal(match, null, 'is not matching brackets at ' + lineNumber + ', ' + column); + } + + function isBracket2(model: Model, testPosition: Position, expected: [Range, Range]): void { + let actual = model.matchBracket(testPosition); + assert.deepEqual(actual, expected, 'matches brackets at ' + testPosition); + } + + const languageIdentifier = new LanguageIdentifier('bracketMode1', LanguageId.PlainText); + let registration: IDisposable = null; + + setup(() => { + registration = LanguageConfigurationRegistry.register(languageIdentifier, { + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'], + ] + }); + }); + + teardown(() => { + registration.dispose(); + registration = null; + }); + + test('bracket matching 1', () => { + let text = + ')]}{[(' + '\n' + + ')]}{[('; + let model = Model.createFromString(text, undefined, languageIdentifier); + + isNotABracket(model, 1, 1); + isNotABracket(model, 1, 2); + isNotABracket(model, 1, 3); + isBracket2(model, new Position(1, 4), [new Range(1, 4, 1, 5), new Range(2, 3, 2, 4)]); + isBracket2(model, new Position(1, 5), [new Range(1, 5, 1, 6), new Range(2, 2, 2, 3)]); + isBracket2(model, new Position(1, 6), [new Range(1, 6, 1, 7), new Range(2, 1, 2, 2)]); + isBracket2(model, new Position(1, 7), [new Range(1, 6, 1, 7), new Range(2, 1, 2, 2)]); + + isBracket2(model, new Position(2, 1), [new Range(2, 1, 2, 2), new Range(1, 6, 1, 7)]); + isBracket2(model, new Position(2, 2), [new Range(2, 2, 2, 3), new Range(1, 5, 1, 6)]); + isBracket2(model, new Position(2, 3), [new Range(2, 3, 2, 4), new Range(1, 4, 1, 5)]); + isBracket2(model, new Position(2, 4), [new Range(2, 3, 2, 4), new Range(1, 4, 1, 5)]); + isNotABracket(model, 2, 5); + isNotABracket(model, 2, 6); + isNotABracket(model, 2, 7); + + model.dispose(); + }); + + test('bracket matching 2', () => { + let text = + 'var bar = {' + '\n' + + 'foo: {' + '\n' + + '}, bar: {hallo: [{' + '\n' + + '}, {' + '\n' + + '}]}}'; + let model = Model.createFromString(text, undefined, languageIdentifier); + + let brackets: [Position, Range, Range][] = [ + [new Position(1, 11), new Range(1, 11, 1, 12), new Range(5, 4, 5, 5)], + [new Position(1, 12), new Range(1, 11, 1, 12), new Range(5, 4, 5, 5)], + + [new Position(2, 6), new Range(2, 6, 2, 7), new Range(3, 1, 3, 2)], + [new Position(2, 7), new Range(2, 6, 2, 7), new Range(3, 1, 3, 2)], + + [new Position(3, 1), new Range(3, 1, 3, 2), new Range(2, 6, 2, 7)], + [new Position(3, 2), new Range(3, 1, 3, 2), new Range(2, 6, 2, 7)], + [new Position(3, 9), new Range(3, 9, 3, 10), new Range(5, 3, 5, 4)], + [new Position(3, 10), new Range(3, 9, 3, 10), new Range(5, 3, 5, 4)], + [new Position(3, 17), new Range(3, 17, 3, 18), new Range(5, 2, 5, 3)], + [new Position(3, 18), new Range(3, 18, 3, 19), new Range(4, 1, 4, 2)], + [new Position(3, 19), new Range(3, 18, 3, 19), new Range(4, 1, 4, 2)], + + [new Position(4, 1), new Range(4, 1, 4, 2), new Range(3, 18, 3, 19)], + [new Position(4, 2), new Range(4, 1, 4, 2), new Range(3, 18, 3, 19)], + [new Position(4, 4), new Range(4, 4, 4, 5), new Range(5, 1, 5, 2)], + [new Position(4, 5), new Range(4, 4, 4, 5), new Range(5, 1, 5, 2)], + + [new Position(5, 1), new Range(5, 1, 5, 2), new Range(4, 4, 4, 5)], + [new Position(5, 2), new Range(5, 2, 5, 3), new Range(3, 17, 3, 18)], + [new Position(5, 3), new Range(5, 3, 5, 4), new Range(3, 9, 3, 10)], + [new Position(5, 4), new Range(5, 4, 5, 5), new Range(1, 11, 1, 12)], + [new Position(5, 5), new Range(5, 4, 5, 5), new Range(1, 11, 1, 12)], + ]; + + let isABracket: { [lineNumber: number]: { [col: number]: boolean; }; } = { 1: {}, 2: {}, 3: {}, 4: {}, 5: {} }; + for (let i = 0, len = brackets.length; i < len; i++) { + let [testPos, b1, b2] = brackets[i]; + isBracket2(model, testPos, [b1, b2]); + isABracket[testPos.lineNumber][testPos.column] = true; + } + + for (let i = 1, len = model.getLineCount(); i <= len; i++) { + let line = model.getLineContent(i); + for (let j = 1, lenJ = line.length + 1; j <= lenJ; j++) { + if (!isABracket[i].hasOwnProperty(j)) { + isNotABracket(model, i, j); + } + } + } + + model.dispose(); + }); +}); + + +suite('TextModelWithTokens regression tests', () => { + + test('Microsoft/monaco-editor#122: Unhandled Exception: TypeError: Unable to get property \'replace\' of undefined or null reference', () => { + function assertViewLineTokens(model: Model, lineNumber: number, forceTokenization: boolean, expected: ViewLineToken[]): void { + if (forceTokenization) { + model.forceTokenization(lineNumber); + } + let actual = model.getLineTokens(lineNumber).inflate(); + let decode = (token: ViewLineToken) => { + return { + endIndex: token.endIndex, + foreground: token.getForeground() + }; + }; + assert.deepEqual(actual.map(decode), expected.map(decode)); + } + + let _tokenId = 10; + const LANG_ID1 = 'indicisiveMode1'; + const LANG_ID2 = 'indicisiveMode2'; + const languageIdentifier1 = new LanguageIdentifier(LANG_ID1, 3); + const languageIdentifier2 = new LanguageIdentifier(LANG_ID2, 4); + + const tokenizationSupport: ITokenizationSupport = { + getInitialState: () => NULL_STATE, + tokenize: undefined, + tokenize2: (line, state) => { + let myId = ++_tokenId; + let tokens = new Uint32Array(2); + tokens[0] = 0; + tokens[1] = ( + myId << MetadataConsts.FOREGROUND_OFFSET + ) >>> 0; + return new TokenizationResult2(tokens, state); + } + }; + + let registration1 = TokenizationRegistry.register(LANG_ID1, tokenizationSupport); + let registration2 = TokenizationRegistry.register(LANG_ID2, tokenizationSupport); + + let model = Model.createFromString('A model with\ntwo lines'); + + assertViewLineTokens(model, 1, true, [createViewLineToken(12, 1)]); + assertViewLineTokens(model, 2, true, [createViewLineToken(9, 1)]); + + model.setMode(languageIdentifier1); + + assertViewLineTokens(model, 1, true, [createViewLineToken(12, 11)]); + assertViewLineTokens(model, 2, true, [createViewLineToken(9, 12)]); + + model.setMode(languageIdentifier2); + + assertViewLineTokens(model, 1, false, [createViewLineToken(12, 1)]); + assertViewLineTokens(model, 2, false, [createViewLineToken(9, 1)]); + + model.dispose(); + registration1.dispose(); + registration2.dispose(); + + function createViewLineToken(endIndex: number, foreground: number): ViewLineToken { + let metadata = ( + (foreground << MetadataConsts.FOREGROUND_OFFSET) + ) >>> 0; + return new ViewLineToken(endIndex, metadata); + } + }); + + + test('Microsoft/monaco-editor#133: Error: Cannot read property \'modeId\' of undefined', () => { + + const languageIdentifier = new LanguageIdentifier('testMode', LanguageId.PlainText); + + let registration = LanguageConfigurationRegistry.register(languageIdentifier, { + brackets: [ + ['module', 'end module'], + ['sub', 'end sub'] + ] + }); + + let model = Model.createFromString([ + 'Imports System', + 'Imports System.Collections.Generic', + '', + 'Module m1', + '', + '\tSub Main()', + '\tEnd Sub', + '', + 'End Module', + ].join('\n'), undefined, languageIdentifier); + + let actual = model.matchBracket(new Position(4, 1)); + assert.deepEqual(actual, [new Range(4, 1, 4, 7), new Range(9, 1, 9, 11)]); + + model.dispose(); + registration.dispose(); + }); +}); diff --git a/src/vs/editor/test/common/modes/languageConfiguration.test.ts b/src/vs/editor/test/common/modes/languageConfiguration.test.ts new file mode 100644 index 0000000000..937aee46c5 --- /dev/null +++ b/src/vs/editor/test/common/modes/languageConfiguration.test.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; +import { StandardTokenType } from 'vs/editor/common/modes'; + +suite('StandardAutoClosingPairConditional', () => { + + test('Missing notIn', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}' }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('Empty notIn', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('Invalid notIn', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['bla'] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('notIn in strings', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['string'] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), false); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('notIn in comments', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['comment'] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), false); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('notIn in regex', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['regex'] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), false); + }); + + test('notIn in strings nor comments', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['string', 'comment'] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), false); + assert.equal(v.isOK(StandardTokenType.String), false); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('notIn in strings nor regex', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['string', 'regex'] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), false); + assert.equal(v.isOK(StandardTokenType.RegEx), false); + }); + + test('notIn in comments nor regex', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['comment', 'regex'] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), false); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), false); + }); + + test('notIn in strings, comments nor regex', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: ['string', 'comment', 'regex'] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), false); + assert.equal(v.isOK(StandardTokenType.String), false); + assert.equal(v.isOK(StandardTokenType.RegEx), false); + }); +}); diff --git a/src/vs/editor/test/common/modes/languageSelector.test.ts b/src/vs/editor/test/common/modes/languageSelector.test.ts new file mode 100644 index 0000000000..de5294c8fd --- /dev/null +++ b/src/vs/editor/test/common/modes/languageSelector.test.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { score } from 'vs/editor/common/modes/languageSelector'; + +suite('LanguageSelector', function () { + + let model = { + language: 'farboo', + uri: URI.parse('file:///testbed/file.fb') + }; + + test('score, invalid selector', function () { + assert.equal(score({}, model.uri, model.language), 0); + assert.equal(score(undefined, model.uri, model.language), 0); + assert.equal(score(null, model.uri, model.language), 0); + assert.equal(score('', model.uri, model.language), 0); + }); + + test('score, any language', function () { + assert.equal(score({ language: '*' }, model.uri, model.language), 5); + assert.equal(score('*', model.uri, model.language), 5); + + assert.equal(score('*', URI.parse('foo:bar'), model.language), 5); + assert.equal(score('farboo', URI.parse('foo:bar'), model.language), 10); + }); + + test('score, default schemes', function () { + + const uri = URI.parse('git:foo/file.txt'); + const language = 'farboo'; + + assert.equal(score('*', uri, language), 5); + assert.equal(score('farboo', uri, language), 10); + assert.equal(score({ language: 'farboo', scheme: '' }, uri, language), 10); + assert.equal(score({ language: 'farboo', scheme: 'git' }, uri, language), 10); + assert.equal(score({ language: 'farboo', scheme: '*' }, uri, language), 10); + assert.equal(score({ language: 'farboo' }, uri, language), 10); + assert.equal(score({ language: '*' }, uri, language), 5); + + assert.equal(score({ scheme: '*' }, uri, language), 5); + assert.equal(score({ scheme: 'git' }, uri, language), 10); + }); + + test('score, filter', function () { + assert.equal(score('farboo', model.uri, model.language), 10); + assert.equal(score({ language: 'farboo' }, model.uri, model.language), 10); + assert.equal(score({ language: 'farboo', scheme: 'file' }, model.uri, model.language), 10); + assert.equal(score({ language: 'farboo', scheme: 'http' }, model.uri, model.language), 0); + + assert.equal(score({ pattern: '**/*.fb' }, model.uri, model.language), 10); + assert.equal(score({ pattern: '**/*.fb', scheme: 'file' }, model.uri, model.language), 10); + assert.equal(score({ pattern: '**/*.fb' }, URI.parse('foo:bar'), model.language), 0); + assert.equal(score({ pattern: '**/*.fb', scheme: 'foo' }, URI.parse('foo:bar'), model.language), 0); + + let doc = { + uri: URI.parse('git:/my/file.js'), + langId: 'javascript' + }; + assert.equal(score('javascript', doc.uri, doc.langId), 10); // 0; + assert.equal(score({ language: 'javascript', scheme: 'git' }, doc.uri, doc.langId), 10); // 10; + assert.equal(score('*', doc.uri, doc.langId), 5); // 5 + assert.equal(score('fooLang', doc.uri, doc.langId), 0); // 0 + assert.equal(score(['fooLang', '*'], doc.uri, doc.langId), 5); // 5 + }); + + test('score, max(filters)', function () { + let match = { language: 'farboo', scheme: 'file' }; + let fail = { language: 'farboo', scheme: 'http' }; + + assert.equal(score(match, model.uri, model.language), 10); + assert.equal(score(fail, model.uri, model.language), 0); + assert.equal(score([match, fail], model.uri, model.language), 10); + assert.equal(score([fail, fail], model.uri, model.language), 0); + assert.equal(score(['farboo', '*'], model.uri, model.language), 10); + assert.equal(score(['*', 'farboo'], model.uri, model.language), 10); + }); +}); diff --git a/src/vs/editor/test/common/modes/linkComputer.test.ts b/src/vs/editor/test/common/modes/linkComputer.test.ts new file mode 100644 index 0000000000..9d817438a3 --- /dev/null +++ b/src/vs/editor/test/common/modes/linkComputer.test.ts @@ -0,0 +1,193 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { ILink } from 'vs/editor/common/modes'; +import { ILinkComputerTarget, computeLinks } from 'vs/editor/common/modes/linkComputer'; + +class SimpleLinkComputerTarget implements ILinkComputerTarget { + + constructor(private _lines: string[]) { + // Intentional Empty + } + + public getLineCount(): number { + return this._lines.length; + } + + public getLineContent(lineNumber: number): string { + return this._lines[lineNumber - 1]; + } +} + +function myComputeLinks(lines: string[]): ILink[] { + var target = new SimpleLinkComputerTarget(lines); + return computeLinks(target); +} + +function assertLink(text: string, extractedLink: string): void { + var startColumn = 0, + endColumn = 0, + chr: string, + i = 0; + + for (i = 0; i < extractedLink.length; i++) { + chr = extractedLink.charAt(i); + if (chr !== ' ' && chr !== '\t') { + startColumn = i + 1; + break; + } + } + + for (i = extractedLink.length - 1; i >= 0; i--) { + chr = extractedLink.charAt(i); + if (chr !== ' ' && chr !== '\t') { + endColumn = i + 2; + break; + } + } + + var r = myComputeLinks([text]); + assert.deepEqual(r, [{ + range: { + startLineNumber: 1, + startColumn: startColumn, + endLineNumber: 1, + endColumn: endColumn + }, + url: extractedLink.substring(startColumn - 1, endColumn - 1) + }]); +} + +suite('Editor Modes - Link Computer', () => { + + test('Null model', () => { + var r = computeLinks(null); + assert.deepEqual(r, []); + }); + + test('Parsing', () => { + + assertLink( + 'x = "http://foo.bar";', + ' http://foo.bar ' + ); + + assertLink( + 'x = (http://foo.bar);', + ' http://foo.bar ' + ); + + assertLink( + 'x = [http://foo.bar];', + ' http://foo.bar ' + ); + + assertLink( + 'x = \'http://foo.bar\';', + ' http://foo.bar ' + ); + + assertLink( + 'x = http://foo.bar ;', + ' http://foo.bar ' + ); + + assertLink( + 'x = ;', + ' http://foo.bar ' + ); + + assertLink( + 'x = {http://foo.bar};', + ' http://foo.bar ' + ); + + assertLink( + '(see http://foo.bar)', + ' http://foo.bar ' + ); + assertLink( + '[see http://foo.bar]', + ' http://foo.bar ' + ); + assertLink( + '{see http://foo.bar}', + ' http://foo.bar ' + ); + assertLink( + '', + ' http://foo.bar ' + ); + assertLink( + 'http://mylink.com', + ' http://mylink.com ' + ); + assertLink( + '// Click here to learn more. https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409', + ' https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409' + ); + assertLink( + '// Click here to learn more. https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx', + ' https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx' + ); + assertLink( + '// https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js', + ' https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js' + ); + assertLink( + '', + ' https://go.microsoft.com/fwlink/?LinkId=166007 ' + ); + assertLink( + 'For instructions, see https://go.microsoft.com/fwlink/?LinkId=166007.', + ' https://go.microsoft.com/fwlink/?LinkId=166007 ' + ); + assertLink( + 'For instructions, see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx.', + ' https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx ' + ); + assertLink( + 'x = "https://en.wikipedia.org/wiki/Zürich";', + ' https://en.wikipedia.org/wiki/Zürich ' + ); + assertLink( + '請參閱 http://go.microsoft.com/fwlink/?LinkId=761051。', + ' http://go.microsoft.com/fwlink/?LinkId=761051 ' + ); + assertLink( + '(請參閱 http://go.microsoft.com/fwlink/?LinkId=761051)', + ' http://go.microsoft.com/fwlink/?LinkId=761051 ' + ); + + assertLink( + 'x = "file:///foo.bar";', + ' file:///foo.bar ' + ); + assertLink( + 'x = "file://c:/foo.bar";', + ' file://c:/foo.bar ' + ); + + assertLink( + 'x = "file://shares/foo.bar";', + ' file://shares/foo.bar ' + ); + + assertLink( + 'x = "file://shäres/foo.bar";', + ' file://shäres/foo.bar ' + ); + assertLink( + 'Some text, then http://www.bing.com.', + ' http://www.bing.com ' + ); + assertLink( + 'let url = `http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items`;', + ' http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items ' + ); + }); +}); diff --git a/src/vs/editor/test/common/modes/supports/characterPair.test.ts b/src/vs/editor/test/common/modes/supports/characterPair.test.ts new file mode 100644 index 0000000000..5b73e9effd --- /dev/null +++ b/src/vs/editor/test/common/modes/supports/characterPair.test.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; +import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils'; +import { StandardTokenType } from 'vs/editor/common/modes'; + +suite('CharacterPairSupport', () => { + + test('only autoClosingPairs', () => { + let characaterPairSupport = new CharacterPairSupport({ autoClosingPairs: [{ open: 'a', close: 'b' }] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]); + }); + + test('only empty autoClosingPairs', () => { + let characaterPairSupport = new CharacterPairSupport({ autoClosingPairs: [] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); + }); + + test('only brackets', () => { + let characaterPairSupport = new CharacterPairSupport({ brackets: [['a', 'b']] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]); + }); + + test('only empty brackets', () => { + let characaterPairSupport = new CharacterPairSupport({ brackets: [] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); + }); + + test('only surroundingPairs', () => { + let characaterPairSupport = new CharacterPairSupport({ surroundingPairs: [{ open: 'a', close: 'b' }] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), [{ open: 'a', close: 'b' }]); + }); + + test('only empty surroundingPairs', () => { + let characaterPairSupport = new CharacterPairSupport({ surroundingPairs: [] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); + }); + + test('brackets is ignored when having autoClosingPairs', () => { + let characaterPairSupport = new CharacterPairSupport({ autoClosingPairs: [], brackets: [['a', 'b']] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); + }); + + function testShouldAutoClose(characterPairSupport: CharacterPairSupport, line: TokenText[], character: string, column: number): boolean { + return characterPairSupport.shouldAutoClosePair(character, createFakeScopedLineTokens(line), column); + } + + test('shouldAutoClosePair in empty line', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + assert.equal(testShouldAutoClose(sup, [], 'a', 1), true); + assert.equal(testShouldAutoClose(sup, [], '{', 1), true); + }); + + test('shouldAutoClosePair in not interesting line 1', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.Other }], '{', 3), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.Other }], 'a', 3), true); + }); + + test('shouldAutoClosePair in not interesting line 2', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}' }] }); + assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.String }], '{', 3), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.String }], 'a', 3), true); + }); + + test('shouldAutoClosePair in interesting line 1', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 1), false); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 1), true); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 2), false); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 3), false); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 3), true); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 4), false); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 4), true); + }); + + test('shouldAutoClosePair in interesting line 2', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 1), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 1), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 3), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 3), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 4), false); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 4), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 5), false); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 5), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 6), false); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 6), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 7), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 7), true); + }); + + test('shouldAutoClosePair in interesting line 3', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 1), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 1), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 3), false); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 3), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 4), false); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 4), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 5), false); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 5), true); + }); + +}); diff --git a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts new file mode 100644 index 0000000000..ff2690d9a3 --- /dev/null +++ b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; +import { createFakeScopedLineTokens, TokenText } from 'vs/editor/test/common/modesTestUtils'; +import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; +import { LanguageIdentifier, StandardTokenType } from 'vs/editor/common/modes'; + +const fakeLanguageIdentifier = new LanguageIdentifier('test', 3); + +suite('Editor Modes - Auto Indentation', () => { + function _testOnElectricCharacter(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number): IElectricAction { + return electricCharacterSupport.onElectricCharacter(character, createFakeScopedLineTokens(line), offset); + } + + function testDoesNothing(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number): void { + let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset); + assert.deepEqual(actual, null); + } + + function testAppends(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number, appendText: string): void { + let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset); + assert.deepEqual(actual, { appendText: appendText }); + } + + function testMatchBracket(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number, matchOpenBracket: string): void { + let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset); + assert.deepEqual(actual, { matchOpenBracket: matchOpenBracket }); + } + + test('Doc comments', () => { + var brackets = new BracketElectricCharacterSupport(null, [{ open: '/**', close: ' */' }], null); + + testAppends(brackets, [ + { text: '/*', type: StandardTokenType.Other }, + ], '*', 3, ' */'); + + testDoesNothing(brackets, [ + { text: '/*', type: StandardTokenType.Other }, + { text: ' ', type: StandardTokenType.Other }, + { text: '*/', type: StandardTokenType.Other }, + ], '*', 3); + }); + + test('getElectricCharacters uses all sources and dedups', () => { + var sup = new BracketElectricCharacterSupport( + new RichEditBrackets(fakeLanguageIdentifier, [ + ['{', '}'], + ['(', ')'] + ]), [ + { open: '{', close: '}', notIn: ['string', 'comment'] }, + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: 'begin', close: 'end', notIn: ['string'] } + ], + { docComment: { open: '/**', close: ' */' } } + ); + + assert.deepEqual(sup.getElectricCharacters(), ['}', ')', 'n', '*']); + }); + + test('auto-close', () => { + var sup = new BracketElectricCharacterSupport( + new RichEditBrackets(fakeLanguageIdentifier, [ + ['{', '}'], + ['(', ')'] + ]), [ + { open: '{', close: '}', notIn: ['string', 'comment'] }, + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: 'begin', close: 'end', notIn: ['string'] } + ], + { docComment: { open: '/**', close: ' */' } } + ); + + testDoesNothing(sup, [], 'a', 0); + + testDoesNothing(sup, [{ text: 'egi', type: StandardTokenType.Other }], 'b', 1); + testDoesNothing(sup, [{ text: 'bgi', type: StandardTokenType.Other }], 'e', 2); + testDoesNothing(sup, [{ text: 'bei', type: StandardTokenType.Other }], 'g', 3); + testDoesNothing(sup, [{ text: 'beg', type: StandardTokenType.Other }], 'i', 4); + + testDoesNothing(sup, [{ text: 'egin', type: StandardTokenType.Other }], 'b', 1); + testDoesNothing(sup, [{ text: 'bgin', type: StandardTokenType.Other }], 'e', 2); + testDoesNothing(sup, [{ text: 'bein', type: StandardTokenType.Other }], 'g', 3); + testDoesNothing(sup, [{ text: 'begn', type: StandardTokenType.Other }], 'i', 4); + testAppends(sup, [{ text: 'begi', type: StandardTokenType.Other }], 'n', 5, 'end'); + + testDoesNothing(sup, [{ text: '3gin', type: StandardTokenType.Other }], 'b', 1); + testDoesNothing(sup, [{ text: 'bgin', type: StandardTokenType.Other }], '3', 2); + testDoesNothing(sup, [{ text: 'b3in', type: StandardTokenType.Other }], 'g', 3); + testDoesNothing(sup, [{ text: 'b3gn', type: StandardTokenType.Other }], 'i', 4); + testDoesNothing(sup, [{ text: 'b3gi', type: StandardTokenType.Other }], 'n', 5); + + testDoesNothing(sup, [{ text: 'begi', type: StandardTokenType.String }], 'n', 5); + + testAppends(sup, [{ text: '"', type: StandardTokenType.String }, { text: 'begi', type: StandardTokenType.Other }], 'n', 6, 'end'); + testDoesNothing(sup, [{ text: '"', type: StandardTokenType.String }, { text: 'begi', type: StandardTokenType.String }], 'n', 6); + + testAppends(sup, [{ text: '/*', type: StandardTokenType.String }], '*', 3, ' */'); + + testDoesNothing(sup, [{ text: 'begi', type: StandardTokenType.Other }, { text: 'end', type: StandardTokenType.Other }], 'n', 5); + }); + + test('matchOpenBracket', () => { + var sup = new BracketElectricCharacterSupport( + new RichEditBrackets(fakeLanguageIdentifier, [ + ['{', '}'], + ['(', ')'] + ]), [ + { open: '{', close: '}', notIn: ['string', 'comment'] }, + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: 'begin', close: 'end', notIn: ['string'] } + ], + { docComment: { open: '/**', close: ' */' } } + ); + + testDoesNothing(sup, [{ text: '\t{', type: StandardTokenType.Other }], '\t', 1); + testDoesNothing(sup, [{ text: '\t{', type: StandardTokenType.Other }], '\t', 2); + testDoesNothing(sup, [{ text: '\t\t', type: StandardTokenType.Other }], '{', 3); + + testDoesNothing(sup, [{ text: '\t}', type: StandardTokenType.Other }], '\t', 1); + testDoesNothing(sup, [{ text: '\t}', type: StandardTokenType.Other }], '\t', 2); + testMatchBracket(sup, [{ text: '\t\t', type: StandardTokenType.Other }], '}', 3, '}'); + }); +}); diff --git a/src/vs/editor/test/common/modes/supports/onEnter.test.ts b/src/vs/editor/test/common/modes/supports/onEnter.test.ts new file mode 100644 index 0000000000..5033b58957 --- /dev/null +++ b/src/vs/editor/test/common/modes/supports/onEnter.test.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { CharacterPair, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter'; + +suite('OnEnter', () => { + + test('uses brackets', () => { + var brackets: CharacterPair[] = [ + ['(', ')'], + ['begin', 'end'] + ]; + var support = new OnEnterSupport({ + brackets: brackets + }); + var testIndentAction = (beforeText: string, afterText: string, expected: IndentAction) => { + var actual = support.onEnter('', beforeText, afterText); + if (expected === IndentAction.None) { + assert.equal(actual, null); + } else { + assert.equal(actual.indentAction, expected); + } + }; + + testIndentAction('a', '', IndentAction.None); + testIndentAction('', 'b', IndentAction.None); + testIndentAction('(', 'b', IndentAction.Indent); + testIndentAction('a', ')', IndentAction.None); + testIndentAction('begin', 'ending', IndentAction.Indent); + testIndentAction('abegin', 'end', IndentAction.None); + testIndentAction('begin', ')', IndentAction.Indent); + testIndentAction('begin', 'end', IndentAction.IndentOutdent); + testIndentAction('begin ', ' end', IndentAction.IndentOutdent); + testIndentAction(' begin', 'end//as', IndentAction.IndentOutdent); + testIndentAction('(', ')', IndentAction.IndentOutdent); + testIndentAction('( ', ')', IndentAction.IndentOutdent); + testIndentAction('a(', ')b', IndentAction.IndentOutdent); + + testIndentAction('(', '', IndentAction.Indent); + testIndentAction('(', 'foo', IndentAction.Indent); + testIndentAction('begin', 'foo', IndentAction.Indent); + testIndentAction('begin', '', IndentAction.Indent); + }); + + test('uses regExpRules', () => { + var support = new OnEnterSupport({ + regExpRules: [ + { + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + afterText: /^\s*\*\/$/, + action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' } + }, + { + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + action: { indentAction: IndentAction.None, appendText: ' * ' } + }, + { + beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/, + action: { indentAction: IndentAction.None, appendText: '* ' } + }, + { + beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/, + action: { indentAction: IndentAction.None, removeText: 1 } + }, + { + beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/, + action: { indentAction: IndentAction.None, removeText: 1 } + } + ] + }); + var testIndentAction = (beforeText: string, afterText: string, expectedIndentAction: IndentAction, expectedAppendText: string, removeText: number = 0) => { + var actual = support.onEnter('', beforeText, afterText); + if (expectedIndentAction === null) { + assert.equal(actual, null, 'isNull:' + beforeText); + } else { + assert.equal(actual !== null, true, 'isNotNull:' + beforeText); + assert.equal(actual.indentAction, expectedIndentAction, 'indentAction:' + beforeText); + if (expectedAppendText !== null) { + assert.equal(actual.appendText, expectedAppendText, 'appendText:' + beforeText); + } + if (removeText !== 0) { + assert.equal(actual.removeText, removeText, 'removeText:' + beforeText); + } + } + }; + + testIndentAction('\t/**', ' */', IndentAction.IndentOutdent, ' * '); + testIndentAction('\t/**', '', IndentAction.None, ' * '); + testIndentAction('\t/** * / * / * /', '', IndentAction.None, ' * '); + testIndentAction('\t/** /*', '', IndentAction.None, ' * '); + testIndentAction('/**', '', IndentAction.None, ' * '); + testIndentAction('\t/**/', '', null, null); + testIndentAction('\t/***/', '', null, null); + testIndentAction('\t/*******/', '', null, null); + testIndentAction('\t/** * * * * */', '', null, null); + testIndentAction('\t/** */', '', null, null); + testIndentAction('\t/** asdfg */', '', null, null); + testIndentAction('\t/* asdfg */', '', null, null); + testIndentAction('\t/* asdfg */', '', null, null); + testIndentAction('\t/** asdfg */', '', null, null); + testIndentAction('*/', '', null, null); + testIndentAction('\t/*', '', null, null); + testIndentAction('\t*', '', null, null); + testIndentAction('\t *', '', IndentAction.None, '* '); + testIndentAction('\t */', '', IndentAction.None, null, 1); + testIndentAction('\t * */', '', IndentAction.None, null, 1); + testIndentAction('\t * * / * / * / */', '', null, null); + testIndentAction('\t * ', '', IndentAction.None, '* '); + testIndentAction(' * ', '', IndentAction.None, '* '); + testIndentAction(' * asdfsfagadfg', '', IndentAction.None, '* '); + testIndentAction(' * asdfsfagadfg * * * ', '', IndentAction.None, '* '); + testIndentAction(' * /*', '', IndentAction.None, '* '); + testIndentAction(' * asdfsfagadfg * / * / * /', '', IndentAction.None, '* '); + testIndentAction(' * asdfsfagadfg * / * / * /*', '', IndentAction.None, '* '); + testIndentAction(' */', '', IndentAction.None, null, 1); + testIndentAction('\t */', '', IndentAction.None, null, 1); + testIndentAction('\t\t */', '', IndentAction.None, null, 1); + testIndentAction(' */', '', IndentAction.None, null, 1); + testIndentAction(' */', '', IndentAction.None, null, 1); + testIndentAction('\t */', '', IndentAction.None, null, 1); + testIndentAction(' *--------------------------------------------------------------------------------------------*/', '', IndentAction.None, null, 1); + }); +}); \ No newline at end of file diff --git a/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts b/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts new file mode 100644 index 0000000000..959c898c09 --- /dev/null +++ b/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { BracketsUtils } from 'vs/editor/common/modes/supports/richEditBrackets'; +import { Range } from 'vs/editor/common/core/range'; + +suite('richEditBrackets', () => { + + function findPrevBracketInToken(reversedBracketRegex: RegExp, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range { + return BracketsUtils.findPrevBracketInToken(reversedBracketRegex, 1, lineText, currentTokenStart, currentTokenEnd); + } + + function findNextBracketInToken(forwardBracketRegex: RegExp, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range { + return BracketsUtils.findNextBracketInToken(forwardBracketRegex, 1, lineText, currentTokenStart, currentTokenEnd); + } + + test('findPrevBracketInToken one char 1', () => { + let result = findPrevBracketInToken(/(\{)|(\})/i, '{', 0, 1); + assert.equal(result.startColumn, 1); + assert.equal(result.endColumn, 2); + }); + + test('findPrevBracketInToken one char 2', () => { + let result = findPrevBracketInToken(/(\{)|(\})/i, '{{', 0, 1); + assert.equal(result.startColumn, 1); + assert.equal(result.endColumn, 2); + }); + + test('findPrevBracketInToken one char 3', () => { + let result = findPrevBracketInToken(/(\{)|(\})/i, '{hello world!', 0, 13); + assert.equal(result.startColumn, 1); + assert.equal(result.endColumn, 2); + }); + + test('findPrevBracketInToken more chars 1', () => { + let result = findPrevBracketInToken(/(olleh)/i, 'hello world!', 0, 12); + assert.equal(result.startColumn, 1); + assert.equal(result.endColumn, 6); + }); + + test('findPrevBracketInToken more chars 2', () => { + let result = findPrevBracketInToken(/(olleh)/i, 'hello world!', 0, 5); + assert.equal(result.startColumn, 1); + assert.equal(result.endColumn, 6); + }); + + test('findPrevBracketInToken more chars 3', () => { + let result = findPrevBracketInToken(/(olleh)/i, ' hello world!', 0, 6); + assert.equal(result.startColumn, 2); + assert.equal(result.endColumn, 7); + }); + + test('findNextBracketInToken one char', () => { + let result = findNextBracketInToken(/(\{)|(\})/i, '{', 0, 1); + assert.equal(result.startColumn, 1); + assert.equal(result.endColumn, 2); + }); + + test('findNextBracketInToken more chars', () => { + let result = findNextBracketInToken(/(world)/i, 'hello world!', 0, 12); + assert.equal(result.startColumn, 7); + assert.equal(result.endColumn, 12); + }); + + test('issue #3894: [Handlebars] Curly braces edit issues', () => { + let result = findPrevBracketInToken(/(\-\-!<)|(>\-\-)|(\{\{)|(\}\})/i, '{{asd}}', 0, 2); + assert.equal(result.startColumn, 1); + assert.equal(result.endColumn, 3); + }); + +}); \ No newline at end of file diff --git a/src/vs/editor/test/common/modes/supports/tokenization.test.ts b/src/vs/editor/test/common/modes/supports/tokenization.test.ts new file mode 100644 index 0000000000..8593c1f9a7 --- /dev/null +++ b/src/vs/editor/test/common/modes/supports/tokenization.test.ts @@ -0,0 +1,333 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { strcmp, parseTokenTheme, TokenTheme, ParsedTokenThemeRule, ColorMap, ExternalThemeTrieElement, ThemeTrieElementRule } from 'vs/editor/common/modes/supports/tokenization'; +import { FontStyle } from 'vs/editor/common/modes'; + +suite('Token theme matching', () => { + + test('gives higher priority to deeper matches', () => { + let theme = TokenTheme.createFromRawTokenTheme([ + { token: '', foreground: '100000', background: '200000' }, + { token: 'punctuation.definition.string.begin.html', foreground: '300000' }, + { token: 'punctuation.definition.string', foreground: '400000' }, + ]); + + let colorMap = new ColorMap(); + colorMap.getId('100000'); + const _B = colorMap.getId('200000'); + colorMap.getId('400000'); + const _D = colorMap.getId('300000'); + + let actual = theme._match('punctuation.definition.string.begin.html'); + + assert.deepEqual(actual, new ThemeTrieElementRule(FontStyle.None, _D, _B)); + }); + + test('can match', () => { + let theme = TokenTheme.createFromRawTokenTheme([ + { token: '', foreground: 'F8F8F2', background: '272822' }, + { token: 'source', background: '100000' }, + { token: 'something', background: '100000' }, + { token: 'bar', background: '200000' }, + { token: 'baz', background: '200000' }, + { token: 'bar', fontStyle: 'bold' }, + { token: 'constant', fontStyle: 'italic', foreground: '300000' }, + { token: 'constant.numeric', foreground: '400000' }, + { token: 'constant.numeric.hex', fontStyle: 'bold' }, + { token: 'constant.numeric.oct', fontStyle: 'bold italic underline' }, + { token: 'constant.numeric.dec', fontStyle: '', foreground: '500000' }, + { token: 'storage.object.bar', fontStyle: '', foreground: '600000' }, + ]); + + let colorMap = new ColorMap(); + const _A = colorMap.getId('F8F8F2'); + const _B = colorMap.getId('272822'); + const _C = colorMap.getId('200000'); + const _D = colorMap.getId('300000'); + const _E = colorMap.getId('400000'); + const _F = colorMap.getId('500000'); + const _G = colorMap.getId('100000'); + const _H = colorMap.getId('600000'); + + function assertMatch(scopeName: string, expected: ThemeTrieElementRule): void { + let actual = theme._match(scopeName); + assert.deepEqual(actual, expected, 'when matching <<' + scopeName + '>>'); + } + + function assertSimpleMatch(scopeName: string, fontStyle: FontStyle, foreground: number, background: number): void { + assertMatch(scopeName, new ThemeTrieElementRule(fontStyle, foreground, background)); + } + + function assertNoMatch(scopeName: string): void { + assertMatch(scopeName, new ThemeTrieElementRule(FontStyle.None, _A, _B)); + } + + // matches defaults + assertNoMatch(''); + assertNoMatch('bazz'); + assertNoMatch('asdfg'); + + // matches source + assertSimpleMatch('source', FontStyle.None, _A, _G); + assertSimpleMatch('source.ts', FontStyle.None, _A, _G); + assertSimpleMatch('source.tss', FontStyle.None, _A, _G); + + // matches something + assertSimpleMatch('something', FontStyle.None, _A, _G); + assertSimpleMatch('something.ts', FontStyle.None, _A, _G); + assertSimpleMatch('something.tss', FontStyle.None, _A, _G); + + // matches baz + assertSimpleMatch('baz', FontStyle.None, _A, _C); + assertSimpleMatch('baz.ts', FontStyle.None, _A, _C); + assertSimpleMatch('baz.tss', FontStyle.None, _A, _C); + + // matches constant + assertSimpleMatch('constant', FontStyle.Italic, _D, _B); + assertSimpleMatch('constant.string', FontStyle.Italic, _D, _B); + assertSimpleMatch('constant.hex', FontStyle.Italic, _D, _B); + + // matches constant.numeric + assertSimpleMatch('constant.numeric', FontStyle.Italic, _E, _B); + assertSimpleMatch('constant.numeric.baz', FontStyle.Italic, _E, _B); + + // matches constant.numeric.hex + assertSimpleMatch('constant.numeric.hex', FontStyle.Bold, _E, _B); + assertSimpleMatch('constant.numeric.hex.baz', FontStyle.Bold, _E, _B); + + // matches constant.numeric.oct + assertSimpleMatch('constant.numeric.oct', FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, _E, _B); + assertSimpleMatch('constant.numeric.oct.baz', FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, _E, _B); + + // matches constant.numeric.dec + assertSimpleMatch('constant.numeric.dec', FontStyle.None, _F, _B); + assertSimpleMatch('constant.numeric.dec.baz', FontStyle.None, _F, _B); + + // matches storage.object.bar + assertSimpleMatch('storage.object.bar', FontStyle.None, _H, _B); + assertSimpleMatch('storage.object.bar.baz', FontStyle.None, _H, _B); + + // does not match storage.object.bar + assertSimpleMatch('storage.object.bart', FontStyle.None, _A, _B); + assertSimpleMatch('storage.object', FontStyle.None, _A, _B); + assertSimpleMatch('storage', FontStyle.None, _A, _B); + + assertSimpleMatch('bar', FontStyle.Bold, _A, _C); + }); +}); + +suite('Token theme parsing', () => { + + test('can parse', () => { + + let actual = parseTokenTheme([ + { token: '', foreground: 'F8F8F2', background: '272822' }, + { token: 'source', background: '100000' }, + { token: 'something', background: '100000' }, + { token: 'bar', background: '010000' }, + { token: 'baz', background: '010000' }, + { token: 'bar', fontStyle: 'bold' }, + { token: 'constant', fontStyle: 'italic', foreground: 'ff0000' }, + { token: 'constant.numeric', foreground: '00ff00' }, + { token: 'constant.numeric.hex', fontStyle: 'bold' }, + { token: 'constant.numeric.oct', fontStyle: 'bold italic underline' }, + { token: 'constant.numeric.dec', fontStyle: '', foreground: '0000ff' }, + ]); + + let expected = [ + new ParsedTokenThemeRule('', 0, FontStyle.NotSet, 'F8F8F2', '272822'), + new ParsedTokenThemeRule('source', 1, FontStyle.NotSet, null, '100000'), + new ParsedTokenThemeRule('something', 2, FontStyle.NotSet, null, '100000'), + new ParsedTokenThemeRule('bar', 3, FontStyle.NotSet, null, '010000'), + new ParsedTokenThemeRule('baz', 4, FontStyle.NotSet, null, '010000'), + new ParsedTokenThemeRule('bar', 5, FontStyle.Bold, null, null), + new ParsedTokenThemeRule('constant', 6, FontStyle.Italic, 'ff0000', null), + new ParsedTokenThemeRule('constant.numeric', 7, FontStyle.NotSet, '00ff00', null), + new ParsedTokenThemeRule('constant.numeric.hex', 8, FontStyle.Bold, null, null), + new ParsedTokenThemeRule('constant.numeric.oct', 9, FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, null, null), + new ParsedTokenThemeRule('constant.numeric.dec', 10, FontStyle.None, '0000ff', null), + ]; + + assert.deepEqual(actual, expected); + }); +}); + +suite('Token theme resolving', () => { + + test('strcmp works', () => { + let actual = ['bar', 'z', 'zu', 'a', 'ab', ''].sort(strcmp); + + let expected = ['', 'a', 'ab', 'bar', 'z', 'zu']; + assert.deepEqual(actual, expected); + }); + + test('always has defaults', () => { + let actual = TokenTheme.createFromParsedTokenTheme([]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('000000'); + const _B = colorMap.getId('ffffff'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B))); + }); + + test('respects incoming defaults 1', () => { + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, null, null) + ]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('000000'); + const _B = colorMap.getId('ffffff'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B))); + }); + + test('respects incoming defaults 2', () => { + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.None, null, null) + ]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('000000'); + const _B = colorMap.getId('ffffff'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B))); + }); + + test('respects incoming defaults 3', () => { + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.Bold, null, null) + ]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('000000'); + const _B = colorMap.getId('ffffff'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _A, _B))); + }); + + test('respects incoming defaults 4', () => { + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'ff0000', null) + ]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('ff0000'); + const _B = colorMap.getId('ffffff'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B))); + }); + + test('respects incoming defaults 5', () => { + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, null, 'ff0000') + ]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('000000'); + const _B = colorMap.getId('ff0000'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B))); + }); + + test('can merge incoming defaults', () => { + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, null, 'ff0000'), + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, '00ff00', null), + new ParsedTokenThemeRule('', -1, FontStyle.Bold, null, null), + ]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('00ff00'); + const _B = colorMap.getId('ff0000'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + assert.deepEqual(actual.getThemeTrieElement(), new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _A, _B))); + }); + + test('defaults are inherited', () => { + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), + new ParsedTokenThemeRule('var', -1, FontStyle.NotSet, 'ff0000', null) + ]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('F8F8F2'); + const _B = colorMap.getId('272822'); + const _C = colorMap.getId('ff0000'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + let root = new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B), { + 'var': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _C, _B)) + }); + assert.deepEqual(actual.getThemeTrieElement(), root); + }); + + test('same rules get merged', () => { + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), + new ParsedTokenThemeRule('var', 1, FontStyle.Bold, null, null), + new ParsedTokenThemeRule('var', 0, FontStyle.NotSet, 'ff0000', null), + ]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('F8F8F2'); + const _B = colorMap.getId('272822'); + const _C = colorMap.getId('ff0000'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + let root = new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B), { + 'var': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _C, _B)) + }); + assert.deepEqual(actual.getThemeTrieElement(), root); + }); + + test('rules are inherited 1', () => { + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), + new ParsedTokenThemeRule('var', -1, FontStyle.Bold, 'ff0000', null), + new ParsedTokenThemeRule('var.identifier', -1, FontStyle.NotSet, '00ff00', null), + ]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('F8F8F2'); + const _B = colorMap.getId('272822'); + const _C = colorMap.getId('ff0000'); + const _D = colorMap.getId('00ff00'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + let root = new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B), { + 'var': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _C, _B), { + 'identifier': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _D, _B)) + }) + }); + assert.deepEqual(actual.getThemeTrieElement(), root); + }); + + test('rules are inherited 2', () => { + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), + new ParsedTokenThemeRule('var', -1, FontStyle.Bold, 'ff0000', null), + new ParsedTokenThemeRule('var.identifier', -1, FontStyle.NotSet, '00ff00', null), + new ParsedTokenThemeRule('constant', 4, FontStyle.Italic, '100000', null), + new ParsedTokenThemeRule('constant.numeric', 5, FontStyle.NotSet, '200000', null), + new ParsedTokenThemeRule('constant.numeric.hex', 6, FontStyle.Bold, null, null), + new ParsedTokenThemeRule('constant.numeric.oct', 7, FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, null, null), + new ParsedTokenThemeRule('constant.numeric.dec', 8, FontStyle.None, '300000', null), + ]); + let colorMap = new ColorMap(); + const _A = colorMap.getId('F8F8F2'); + const _B = colorMap.getId('272822'); + const _C = colorMap.getId('100000'); + const _D = colorMap.getId('200000'); + const _E = colorMap.getId('300000'); + const _F = colorMap.getId('ff0000'); + const _G = colorMap.getId('00ff00'); + assert.deepEqual(actual.getColorMap(), colorMap.getColorMap()); + let root = new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _A, _B), { + 'var': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _F, _B), { + 'identifier': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _G, _B)) + }), + 'constant': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Italic, _C, _B), { + 'numeric': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Italic, _D, _B), { + 'hex': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold, _D, _B)), + 'oct': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, _D, _B)), + 'dec': new ExternalThemeTrieElement(new ThemeTrieElementRule(FontStyle.None, _E, _B)), + }) + }) + }); + assert.deepEqual(actual.getThemeTrieElement(), root); + }); +}); diff --git a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts new file mode 100644 index 0000000000..cb53da88b5 --- /dev/null +++ b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { TokenizationRegistry, IState, LanguageIdentifier, ColorId, FontStyle, MetadataConsts } from 'vs/editor/common/modes'; +import { tokenizeToString, tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; + +suite('Editor Modes - textToHtmlTokenizer', () => { + function toStr(pieces: { className: string; text: string }[]): string { + let resultArr = pieces.map((t) => `${t.text}`); + return resultArr.join(''); + } + + test('TextToHtmlTokenizer 1', () => { + let mode = new Mode(); + + let actual = tokenizeToString('.abc..def...gh', mode.getId()); + let expected = [ + { className: 'mtk7', text: '.' }, + { className: 'mtk9', text: 'abc' }, + { className: 'mtk7', text: '..' }, + { className: 'mtk9', text: 'def' }, + { className: 'mtk7', text: '...' }, + { className: 'mtk9', text: 'gh' }, + ]; + let expectedStr = `
${toStr(expected)}
`; + + assert.equal(actual, expectedStr); + + mode.dispose(); + }); + + test('TextToHtmlTokenizer 2', () => { + let mode = new Mode(); + + let actual = tokenizeToString('.abc..def...gh\n.abc..def...gh', mode.getId()); + let expected1 = [ + { className: 'mtk7', text: '.' }, + { className: 'mtk9', text: 'abc' }, + { className: 'mtk7', text: '..' }, + { className: 'mtk9', text: 'def' }, + { className: 'mtk7', text: '...' }, + { className: 'mtk9', text: 'gh' }, + ]; + let expected2 = [ + { className: 'mtk7', text: '.' }, + { className: 'mtk9', text: 'abc' }, + { className: 'mtk7', text: '..' }, + { className: 'mtk9', text: 'def' }, + { className: 'mtk7', text: '...' }, + { className: 'mtk9', text: 'gh' }, + ]; + let expectedStr1 = toStr(expected1); + let expectedStr2 = toStr(expected2); + let expectedStr = `
${expectedStr1}
${expectedStr2}
`; + + assert.equal(actual, expectedStr); + + mode.dispose(); + }); + + test('tokenizeLineToHTML', () => { + const text = 'Ciao hello world!'; + const lineTokens = [ + new ViewLineToken( + 4, + ( + (3 << MetadataConsts.FOREGROUND_OFFSET) + | ((FontStyle.Bold | FontStyle.Italic) << MetadataConsts.FONT_STYLE_OFFSET) + ) >>> 0 + ), + new ViewLineToken( + 5, + ( + (1 << MetadataConsts.FOREGROUND_OFFSET) + ) >>> 0 + ), + new ViewLineToken( + 10, + ( + (4 << MetadataConsts.FOREGROUND_OFFSET) + ) >>> 0 + ), + new ViewLineToken( + 11, + ( + (1 << MetadataConsts.FOREGROUND_OFFSET) + ) >>> 0 + ), + new ViewLineToken( + 17, + ( + (5 << MetadataConsts.FOREGROUND_OFFSET) + | ((FontStyle.Underline) << MetadataConsts.FONT_STYLE_OFFSET) + ) >>> 0 + ) + ]; + const colorMap = [null, '#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff']; + + assert.equal( + tokenizeLineToHTML(text, lineTokens, colorMap, 0, 17, 4), + [ + '
', + 'Ciao', + ' ', + 'hello', + ' ', + 'world!', + '
' + ].join('') + ); + + assert.equal( + tokenizeLineToHTML(text, lineTokens, colorMap, 0, 12, 4), + [ + '
', + 'Ciao', + ' ', + 'hello', + ' ', + 'w', + '
' + ].join('') + ); + + assert.equal( + tokenizeLineToHTML(text, lineTokens, colorMap, 0, 11, 4), + [ + '
', + 'Ciao', + ' ', + 'hello', + ' ', + '
' + ].join('') + ); + + assert.equal( + tokenizeLineToHTML(text, lineTokens, colorMap, 1, 11, 4), + [ + '
', + 'iao', + ' ', + 'hello', + ' ', + '
' + ].join('') + ); + + assert.equal( + tokenizeLineToHTML(text, lineTokens, colorMap, 4, 11, 4), + [ + '
', + ' ', + 'hello', + ' ', + '
' + ].join('') + ); + + assert.equal( + tokenizeLineToHTML(text, lineTokens, colorMap, 5, 11, 4), + [ + '
', + 'hello', + ' ', + '
' + ].join('') + ); + + assert.equal( + tokenizeLineToHTML(text, lineTokens, colorMap, 5, 10, 4), + [ + '
', + 'hello', + '
' + ].join('') + ); + + assert.equal( + tokenizeLineToHTML(text, lineTokens, colorMap, 6, 9, 4), + [ + '
', + 'ell', + '
' + ].join('') + ); + }); + +}); + +class Mode extends MockMode { + + private static _id = new LanguageIdentifier('textToHtmlTokenizerMode', 3); + + constructor() { + super(Mode._id); + this._register(TokenizationRegistry.register(this.getId(), { + getInitialState: (): IState => null, + tokenize: undefined, + tokenize2: (line: string, state: IState): TokenizationResult2 => { + let tokensArr: number[] = []; + let prevColor: ColorId = -1; + for (let i = 0; i < line.length; i++) { + let colorId = line.charAt(i) === '.' ? 7 : 9; + if (prevColor !== colorId) { + tokensArr.push(i); + tokensArr.push(( + colorId << MetadataConsts.FOREGROUND_OFFSET + ) >>> 0); + } + prevColor = colorId; + } + + let tokens = new Uint32Array(tokensArr.length); + for (let i = 0; i < tokens.length; i++) { + tokens[i] = tokensArr[i]; + } + return new TokenizationResult2(tokens, null); + } + })); + } +} diff --git a/src/vs/editor/test/common/modesTestUtils.ts b/src/vs/editor/test/common/modesTestUtils.ts new file mode 100644 index 0000000000..122d849948 --- /dev/null +++ b/src/vs/editor/test/common/modesTestUtils.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/modes/supports'; +import { StandardTokenType, MetadataConsts } from 'vs/editor/common/modes'; + +export interface TokenText { + text: string; + type: StandardTokenType; +} + +export function createFakeScopedLineTokens(rawTokens: TokenText[]): ScopedLineTokens { + let tokens = new Uint32Array(rawTokens.length << 1); + let line = ''; + + for (let i = 0, len = rawTokens.length; i < len; i++) { + let rawToken = rawTokens[i]; + + let startOffset = line.length; + let metadata = ( + (rawToken.type << MetadataConsts.TOKEN_TYPE_OFFSET) + ) >>> 0; + + tokens[(i << 1)] = startOffset; + tokens[(i << 1) + 1] = metadata; + line += rawToken.text; + } + + return createScopedLineTokens(new LineTokens(tokens, line), 0); +} diff --git a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts new file mode 100644 index 0000000000..ae93cc0df5 --- /dev/null +++ b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts @@ -0,0 +1,171 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { EditorSimpleWorkerImpl, ICommonModel } from 'vs/editor/common/services/editorSimpleWorker'; +import { Range } from 'vs/editor/common/core/range'; + +suite('EditorSimpleWorker', () => { + + class WorkerWithModels extends EditorSimpleWorkerImpl { + + getModel(uri: string) { + return this._getModel(uri); + } + + addModel(lines: string[], eol: string = '\n') { + const uri = 'test:file#' + Date.now(); + this.acceptNewModel({ + url: uri, + versionId: 1, + lines: lines, + EOL: eol + }); + return this._getModel(uri); + } + } + + let worker: WorkerWithModels; + let model: ICommonModel; + + setup(() => { + worker = new WorkerWithModels(); + model = worker.addModel([ + 'This is line one', //16 + 'and this is line number two', //27 + 'it is followed by #3', //20 + 'and finished with the fourth.', //29 + ]); + }); + + function assertPositionAt(offset: number, line: number, column: number) { + let position = model.positionAt(offset); + assert.equal(position.lineNumber, line); + assert.equal(position.column, column); + } + + function assertOffsetAt(lineNumber: number, column: number, offset: number) { + let actual = model.offsetAt({ lineNumber, column }); + assert.equal(actual, offset); + } + + test('ICommonModel#offsetAt', function () { + assertOffsetAt(1, 1, 0); + assertOffsetAt(1, 2, 1); + assertOffsetAt(1, 17, 16); + assertOffsetAt(2, 1, 17); + assertOffsetAt(2, 4, 20); + assertOffsetAt(3, 1, 45); + assertOffsetAt(5, 30, 95); + assertOffsetAt(5, 31, 95); + assertOffsetAt(5, Number.MAX_VALUE, 95); + assertOffsetAt(6, 30, 95); + assertOffsetAt(Number.MAX_VALUE, 30, 95); + assertOffsetAt(Number.MAX_VALUE, Number.MAX_VALUE, 95); + }); + + test('ICommonModel#positionAt', function () { + assertPositionAt(0, 1, 1); + assertPositionAt(Number.MIN_VALUE, 1, 1); + assertPositionAt(1, 1, 2); + assertPositionAt(16, 1, 17); + assertPositionAt(17, 2, 1); + assertPositionAt(20, 2, 4); + assertPositionAt(45, 3, 1); + assertPositionAt(95, 4, 30); + assertPositionAt(96, 4, 30); + assertPositionAt(99, 4, 30); + assertPositionAt(Number.MAX_VALUE, 4, 30); + }); + + test('ICommonModel#validatePosition, issue #15882', function () { + let model = worker.addModel(['{"id": "0001","type": "donut","name": "Cake","image":{"url": "images/0001.jpg","width": 200,"height": 200},"thumbnail":{"url": "images/thumbnails/0001.jpg","width": 32,"height": 32}}']); + assert.equal(model.offsetAt({ lineNumber: 1, column: 2 }), 1); + }); + + test('MoreMinimal', function () { + + return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: 'This is line One', range: new Range(1, 1, 1, 17) }], []).then(edits => { + assert.equal(edits.length, 1); + const [first] = edits; + assert.equal(first.text, 'O'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 14, endLineNumber: 1, endColumn: 15 }); + }); + }); + + test('MoreMinimal, issue #15385 newline changes only', function () { + + let model = worker.addModel([ + '{', + '\t"a":1', + '}' + ], '\n'); + + return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: '{\r\n\t"a":1\r\n}', range: new Range(1, 1, 3, 2) }], []).then(edits => { + assert.equal(edits.length, 0); + }); + }); + + test('MoreMinimal, issue #15385 newline changes and other', function () { + + let model = worker.addModel([ + '{', + '\t"a":1', + '}' + ], '\n'); + + return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: '{\r\n\t"b":1\r\n}', range: new Range(1, 1, 3, 2) }], []).then(edits => { + assert.equal(edits.length, 1); + const [first] = edits; + assert.equal(first.text, 'b'); + assert.deepEqual(first.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 2, endColumn: 4 }); + }); + }); + + test('MoreMinimal, issue #15385 newline changes and other', function () { + + let model = worker.addModel([ + 'package main', // 1 + 'func foo() {', // 2 + '}' // 3 + ]); + + return worker.computeMoreMinimalEdits(model.uri.toString(), [{ text: '\n', range: new Range(3, 2, 4, 1000) }], []).then(edits => { + assert.equal(edits.length, 1); + const [first] = edits; + assert.equal(first.text, '\n'); + assert.deepEqual(first.range, { startLineNumber: 3, startColumn: 2, endLineNumber: 3, endColumn: 2 }); + }); + }); + + + test('ICommonModel#getValueInRange, issue #17424', function () { + + let model = worker.addModel([ + 'package main', // 1 + 'func foo() {', // 2 + '}' // 3 + ]); + + const value = model.getValueInRange({ startLineNumber: 3, startColumn: 1, endLineNumber: 4, endColumn: 1 }); + assert.equal(value, '}'); + }); + + + test('textualSuggest, issue #17785', function () { + + let model = worker.addModel([ + 'foobar', // 1 + 'f f' // 2 + ]); + + return worker.textualSuggest(model.uri.toString(), { lineNumber: 2, column: 2 }, '[a-z]+', 'img').then((result) => { + const { suggestions } = result; + assert.equal(suggestions.length, 1); + assert.equal(suggestions[0].label, 'foobar'); + }); + }); +}); diff --git a/src/vs/editor/test/common/services/languagesRegistry.test.ts b/src/vs/editor/test/common/services/languagesRegistry.test.ts new file mode 100644 index 0000000000..7df0fe4139 --- /dev/null +++ b/src/vs/editor/test/common/services/languagesRegistry.test.ts @@ -0,0 +1,269 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry'; + +suite('LanguagesRegistry', () => { + + test('output mode does not have a name', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'outputModeId', + extensions: [], + aliases: [null], + mimetypes: ['outputModeMimeType'], + }]); + + assert.deepEqual(registry.getRegisteredLanguageNames(), []); + }); + + test('mode with alias does have a name', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'modeId', + extensions: [], + aliases: ['ModeName'], + mimetypes: ['bla'], + }]); + + assert.deepEqual(registry.getRegisteredLanguageNames(), ['ModeName']); + assert.deepEqual(registry.getLanguageName('modeId'), 'ModeName'); + }); + + test('mode without alias gets a name', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'modeId', + extensions: [], + mimetypes: ['bla'], + }]); + + assert.deepEqual(registry.getRegisteredLanguageNames(), ['modeId']); + assert.deepEqual(registry.getLanguageName('modeId'), 'modeId'); + }); + + test('bug #4360: f# not shown in status bar', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'modeId', + extensions: ['.ext1'], + aliases: ['ModeName'], + mimetypes: ['bla'], + }]); + + registry._registerLanguages([{ + id: 'modeId', + extensions: ['.ext2'], + aliases: [], + mimetypes: ['bla'], + }]); + + assert.deepEqual(registry.getRegisteredLanguageNames(), ['ModeName']); + assert.deepEqual(registry.getLanguageName('modeId'), 'ModeName'); + }); + + test('issue #5278: Extension cannot override language name anymore', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'modeId', + extensions: ['.ext1'], + aliases: ['ModeName'], + mimetypes: ['bla'], + }]); + + registry._registerLanguages([{ + id: 'modeId', + extensions: ['.ext2'], + aliases: ['BetterModeName'], + mimetypes: ['bla'], + }]); + + assert.deepEqual(registry.getRegisteredLanguageNames(), ['BetterModeName']); + assert.deepEqual(registry.getLanguageName('modeId'), 'BetterModeName'); + }); + + test('mimetypes are generated if necessary', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'modeId' + }]); + + assert.deepEqual(registry.getMimeForMode('modeId'), 'text/x-modeId'); + }); + + test('first mimetype wins', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'modeId', + mimetypes: ['text/modeId', 'text/modeId2'] + }]); + + assert.deepEqual(registry.getMimeForMode('modeId'), 'text/modeId'); + }); + + test('first mimetype wins 2', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'modeId' + }]); + + registry._registerLanguages([{ + id: 'modeId', + mimetypes: ['text/modeId'] + }]); + + assert.deepEqual(registry.getMimeForMode('modeId'), 'text/x-modeId'); + }); + + test('aliases', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'a' + }]); + + assert.deepEqual(registry.getRegisteredLanguageNames(), ['a']); + assert.deepEqual(registry.getModeIdsFromLanguageName('a'), ['a']); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a'), 'a'); + assert.deepEqual(registry.getLanguageName('a'), 'a'); + + registry._registerLanguages([{ + id: 'a', + aliases: ['A1', 'A2'] + }]); + + assert.deepEqual(registry.getRegisteredLanguageNames(), ['A1']); + assert.deepEqual(registry.getModeIdsFromLanguageName('a'), []); + assert.deepEqual(registry.getModeIdsFromLanguageName('A1'), ['a']); + assert.deepEqual(registry.getModeIdsFromLanguageName('A2'), []); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a'), 'a'); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a1'), 'a'); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a2'), 'a'); + assert.deepEqual(registry.getLanguageName('a'), 'A1'); + + registry._registerLanguages([{ + id: 'a', + aliases: ['A3', 'A4'] + }]); + + assert.deepEqual(registry.getRegisteredLanguageNames(), ['A3']); + assert.deepEqual(registry.getModeIdsFromLanguageName('a'), []); + assert.deepEqual(registry.getModeIdsFromLanguageName('A1'), []); + assert.deepEqual(registry.getModeIdsFromLanguageName('A2'), []); + assert.deepEqual(registry.getModeIdsFromLanguageName('A3'), ['a']); + assert.deepEqual(registry.getModeIdsFromLanguageName('A4'), []); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a'), 'a'); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a1'), 'a'); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a2'), 'a'); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a3'), 'a'); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a4'), 'a'); + assert.deepEqual(registry.getLanguageName('a'), 'A3'); + }); + + test('empty aliases array means no alias', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'a' + }]); + + assert.deepEqual(registry.getRegisteredLanguageNames(), ['a']); + assert.deepEqual(registry.getModeIdsFromLanguageName('a'), ['a']); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a'), 'a'); + assert.deepEqual(registry.getLanguageName('a'), 'a'); + + registry._registerLanguages([{ + id: 'b', + aliases: [] + }]); + + assert.deepEqual(registry.getRegisteredLanguageNames(), ['a']); + assert.deepEqual(registry.getModeIdsFromLanguageName('a'), ['a']); + assert.deepEqual(registry.getModeIdsFromLanguageName('b'), []); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('a'), 'a'); + assert.deepEqual(registry.getModeIdForLanguageNameLowercase('b'), 'b'); + assert.deepEqual(registry.getLanguageName('a'), 'a'); + assert.deepEqual(registry.getLanguageName('b'), null); + }); + + test('extensions', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'a', + aliases: ['aName'], + extensions: ['aExt'] + }]); + + assert.deepEqual(registry.getExtensions('a'), []); + assert.deepEqual(registry.getExtensions('aname'), []); + assert.deepEqual(registry.getExtensions('aName'), ['aExt']); + + registry._registerLanguages([{ + id: 'a', + extensions: ['aExt2'] + }]); + + assert.deepEqual(registry.getExtensions('a'), []); + assert.deepEqual(registry.getExtensions('aname'), []); + assert.deepEqual(registry.getExtensions('aName'), ['aExt', 'aExt2']); + }); + + test('filenames', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'a', + aliases: ['aName'], + filenames: ['aFilename'] + }]); + + assert.deepEqual(registry.getFilenames('a'), []); + assert.deepEqual(registry.getFilenames('aname'), []); + assert.deepEqual(registry.getFilenames('aName'), ['aFilename']); + + registry._registerLanguages([{ + id: 'a', + filenames: ['aFilename2'] + }]); + + assert.deepEqual(registry.getFilenames('a'), []); + assert.deepEqual(registry.getFilenames('aname'), []); + assert.deepEqual(registry.getFilenames('aName'), ['aFilename', 'aFilename2']); + }); + + test('configuration', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'a', + aliases: ['aName'], + configuration: 'aFilename' + }]); + + assert.deepEqual(registry.getConfigurationFiles('a'), ['aFilename']); + assert.deepEqual(registry.getConfigurationFiles('aname'), []); + assert.deepEqual(registry.getConfigurationFiles('aName'), []); + + registry._registerLanguages([{ + id: 'a', + configuration: 'aFilename2' + }]); + + assert.deepEqual(registry.getConfigurationFiles('a'), ['aFilename', 'aFilename2']); + assert.deepEqual(registry.getConfigurationFiles('aname'), []); + assert.deepEqual(registry.getConfigurationFiles('aName'), []); + }); +}); diff --git a/src/vs/editor/test/common/services/modelService.test.ts b/src/vs/editor/test/common/services/modelService.test.ts new file mode 100644 index 0000000000..1d801a8b33 --- /dev/null +++ b/src/vs/editor/test/common/services/modelService.test.ts @@ -0,0 +1,333 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import URI from 'vs/base/common/uri'; +import * as platform from 'vs/base/common/platform'; +import { DefaultEndOfLine } from 'vs/editor/common/editorCommon'; +import { Model } from 'vs/editor/common/model/model'; +import { TextSource } from 'vs/editor/common/model/textSource'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Range } from 'vs/editor/common/core/range'; +import { CharCode } from 'vs/base/common/charCode'; +import { createStringBuilder } from 'vs/editor/common/core/stringBuilder'; + +const GENERATE_TESTS = false; + +suite('ModelService', () => { + let modelService: ModelServiceImpl; + + setup(() => { + const configService = new TestConfigurationService(); + configService.setUserConfiguration('files', { 'eol': '\n' }); + configService.setUserConfiguration('files', { 'eol': '\r\n' }, URI.file(platform.isWindows ? 'c:\\myroot' : '/myroot')); + + modelService = new ModelServiceImpl(null, configService); + }); + + teardown(() => { + modelService.dispose(); + }); + + test('EOL setting respected depending on root', () => { + const model1 = modelService.createModel('farboo', null, null); + const model2 = modelService.createModel('farboo', null, URI.file(platform.isWindows ? 'c:\\myroot\\myfile.txt' : '/myroot/myfile.txt')); + const model3 = modelService.createModel('farboo', null, URI.file(platform.isWindows ? 'c:\\other\\myfile.txt' : '/other/myfile.txt')); + + assert.equal(model1.getOptions().defaultEOL, DefaultEndOfLine.LF); + assert.equal(model2.getOptions().defaultEOL, DefaultEndOfLine.CRLF); + assert.equal(model3.getOptions().defaultEOL, DefaultEndOfLine.LF); + }); + + test('_computeEdits first line changed', function () { + + const model = Model.createFromString( + [ + 'This is line one', //16 + 'and this is line number two', //27 + 'it is followed by #3', //20 + 'and finished with the fourth.', //29 + ].join('\n') + ); + + const textSource = TextSource.fromString( + [ + 'This is line One', //16 + 'and this is line number two', //27 + 'it is followed by #3', //20 + 'and finished with the fourth.', //29 + ].join('\n'), + DefaultEndOfLine.LF + ); + + const actual = ModelServiceImpl._computeEdits(model, textSource); + + assert.deepEqual(actual, [ + EditOperation.replace(new Range(1, 1, 1, 17), 'This is line One') + ]); + }); + + test('_computeEdits EOL changed', function () { + + const model = Model.createFromString( + [ + 'This is line one', //16 + 'and this is line number two', //27 + 'it is followed by #3', //20 + 'and finished with the fourth.', //29 + ].join('\n') + ); + + const textSource = TextSource.fromString( + [ + 'This is line one', //16 + 'and this is line number two', //27 + 'it is followed by #3', //20 + 'and finished with the fourth.', //29 + ].join('\r\n'), + DefaultEndOfLine.LF + ); + + const actual = ModelServiceImpl._computeEdits(model, textSource); + + assert.deepEqual(actual, []); + }); + + test('_computeEdits EOL and other change 1', function () { + + const model = Model.createFromString( + [ + 'This is line one', //16 + 'and this is line number two', //27 + 'it is followed by #3', //20 + 'and finished with the fourth.', //29 + ].join('\n') + ); + + const textSource = TextSource.fromString( + [ + 'This is line One', //16 + 'and this is line number two', //27 + 'It is followed by #3', //20 + 'and finished with the fourth.', //29 + ].join('\r\n'), + DefaultEndOfLine.LF + ); + + const actual = ModelServiceImpl._computeEdits(model, textSource); + + assert.deepEqual(actual, [ + EditOperation.replace(new Range(1, 1, 1, 17), 'This is line One'), + EditOperation.replace(new Range(3, 1, 3, 21), 'It is followed by #3') + ]); + }); + + test('_computeEdits EOL and other change 2', function () { + + const model = Model.createFromString( + [ + 'package main', // 1 + 'func foo() {', // 2 + '}' // 3 + ].join('\n') + ); + + const textSource = TextSource.fromString( + [ + 'package main', // 1 + 'func foo() {', // 2 + '}', // 3 + '' + ].join('\r\n'), + DefaultEndOfLine.LF + ); + + const actual = ModelServiceImpl._computeEdits(model, textSource); + + assert.deepEqual(actual, [ + EditOperation.replace(new Range(3, 2, 3, 2), '\n') + ]); + }); + + test('generated1', () => { + const file1 = ['pram', 'okctibad', 'pjuwtemued', 'knnnm', 'u', '']; + const file2 = ['tcnr', 'rxwlicro', 'vnzy', '', '', 'pjzcogzur', 'ptmxyp', 'dfyshia', 'pee', 'ygg']; + assertComputeEdits(file1, file2); + }); + + test('generated2', () => { + const file1 = ['', 'itls', 'hrilyhesv', '']; + const file2 = ['vdl', '', 'tchgz', 'bhx', 'nyl']; + assertComputeEdits(file1, file2); + }); + + test('generated3', () => { + const file1 = ['ubrbrcv', 'wv', 'xodspybszt', 's', 'wednjxm', 'fklajt', 'fyfc', 'lvejgge', 'rtpjlodmmk', 'arivtgmjdm']; + const file2 = ['s', 'qj', 'tu', 'ur', 'qerhjjhyvx', 't']; + assertComputeEdits(file1, file2); + }); + + test('generated4', () => { + const file1 = ['ig', 'kh', 'hxegci', 'smvker', 'pkdmjjdqnv', 'vgkkqqx', '', 'jrzeb']; + const file2 = ['yk', '']; + assertComputeEdits(file1, file2); + }); + + test('does insertions in the middle of the document', () => { + const file1 = [ + 'line 1', + 'line 2', + 'line 3' + ]; + const file2 = [ + 'line 1', + 'line 2', + 'line 5', + 'line 3' + ]; + assertComputeEdits(file1, file2); + }); + + test('does insertions at the end of the document', () => { + const file1 = [ + 'line 1', + 'line 2', + 'line 3' + ]; + const file2 = [ + 'line 1', + 'line 2', + 'line 3', + 'line 4' + ]; + assertComputeEdits(file1, file2); + }); + + test('does insertions at the beginning of the document', () => { + const file1 = [ + 'line 1', + 'line 2', + 'line 3' + ]; + const file2 = [ + 'line 0', + 'line 1', + 'line 2', + 'line 3' + ]; + assertComputeEdits(file1, file2); + }); + + test('does replacements', () => { + const file1 = [ + 'line 1', + 'line 2', + 'line 3' + ]; + const file2 = [ + 'line 1', + 'line 7', + 'line 3' + ]; + assertComputeEdits(file1, file2); + }); + + test('does deletions', () => { + const file1 = [ + 'line 1', + 'line 2', + 'line 3' + ]; + const file2 = [ + 'line 1', + 'line 3' + ]; + assertComputeEdits(file1, file2); + }); + + test('does insert, replace, and delete', () => { + const file1 = [ + 'line 1', + 'line 2', + 'line 3', + 'line 4', + 'line 5', + ]; + const file2 = [ + 'line 0', // insert line 0 + 'line 1', + 'replace line 2', // replace line 2 + 'line 3', + // delete line 4 + 'line 5', + ]; + assertComputeEdits(file1, file2); + }); +}); + +function assertComputeEdits(lines1: string[], lines2: string[]): void { + const model = Model.createFromString(lines1.join('\n')); + const textSource = TextSource.fromString(lines2.join('\n'), DefaultEndOfLine.LF); + + // compute required edits + // let start = Date.now(); + const edits = ModelServiceImpl._computeEdits(model, textSource); + // console.log(`took ${Date.now() - start} ms.`); + + // apply edits + model.pushEditOperations(null, edits, null); + + assert.equal(model.getValue(), lines2.join('\n')); +} + +function getRandomInt(min: number, max: number): number { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function getRandomString(minLength: number, maxLength: number): string { + let length = getRandomInt(minLength, maxLength); + let t = createStringBuilder(length); + for (let i = 0; i < length; i++) { + t.appendASCII(getRandomInt(CharCode.a, CharCode.z)); + } + return t.build(); +} + +function generateFile(small: boolean): string[] { + let lineCount = getRandomInt(1, small ? 3 : 10000); + let lines: string[] = []; + for (let i = 0; i < lineCount; i++) { + lines.push(getRandomString(0, small ? 3 : 10000)); + } + return lines; +} + +if (GENERATE_TESTS) { + let number = 1; + while (true) { + + console.log('------TEST: ' + number++); + + const file1 = generateFile(true); + const file2 = generateFile(true); + + console.log('------TEST GENERATED'); + + try { + assertComputeEdits(file1, file2); + } catch (err) { + console.log(err); + console.log(` +const file1 = ${JSON.stringify(file1).replace(/"/g, '\'')}; +const file2 = ${JSON.stringify(file2).replace(/"/g, '\'')}; +assertComputeEdits(file1, file2); +`); + break; + } + } +} \ No newline at end of file diff --git a/src/vs/editor/test/common/standalone/standaloneBase.test.ts b/src/vs/editor/test/common/standalone/standaloneBase.test.ts new file mode 100644 index 0000000000..b7a52b8e68 --- /dev/null +++ b/src/vs/editor/test/common/standalone/standaloneBase.test.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { KeyCode as StandaloneKeyCode, Severity as StandaloneSeverity } from 'vs/editor/common/standalone/standaloneBase'; +import { KeyCode as RuntimeKeyCode } from 'vs/base/common/keyCodes'; +import RuntimeSeverity from 'vs/base/common/severity'; + +suite('StandaloneBase', () => { + test('exports enums correctly', () => { + assert.equal(StandaloneSeverity.Ignore, RuntimeSeverity.Ignore); + assert.equal(StandaloneSeverity.Info, RuntimeSeverity.Info); + assert.equal(StandaloneSeverity.Warning, RuntimeSeverity.Warning); + assert.equal(StandaloneSeverity.Error, RuntimeSeverity.Error); + }); +}); + +suite('KeyCode', () => { + test('is exported correctly in standalone editor', () => { + + function assertKeyCode(standalone: StandaloneKeyCode, runtime: RuntimeKeyCode): void { + assert.equal(standalone, runtime); + } + + assertKeyCode(StandaloneKeyCode.Unknown, RuntimeKeyCode.Unknown); + assertKeyCode(StandaloneKeyCode.Backspace, RuntimeKeyCode.Backspace); + assertKeyCode(StandaloneKeyCode.Tab, RuntimeKeyCode.Tab); + assertKeyCode(StandaloneKeyCode.Enter, RuntimeKeyCode.Enter); + assertKeyCode(StandaloneKeyCode.Shift, RuntimeKeyCode.Shift); + assertKeyCode(StandaloneKeyCode.Ctrl, RuntimeKeyCode.Ctrl); + assertKeyCode(StandaloneKeyCode.Alt, RuntimeKeyCode.Alt); + assertKeyCode(StandaloneKeyCode.PauseBreak, RuntimeKeyCode.PauseBreak); + assertKeyCode(StandaloneKeyCode.CapsLock, RuntimeKeyCode.CapsLock); + assertKeyCode(StandaloneKeyCode.Escape, RuntimeKeyCode.Escape); + assertKeyCode(StandaloneKeyCode.Space, RuntimeKeyCode.Space); + assertKeyCode(StandaloneKeyCode.PageUp, RuntimeKeyCode.PageUp); + assertKeyCode(StandaloneKeyCode.PageDown, RuntimeKeyCode.PageDown); + assertKeyCode(StandaloneKeyCode.End, RuntimeKeyCode.End); + assertKeyCode(StandaloneKeyCode.Home, RuntimeKeyCode.Home); + assertKeyCode(StandaloneKeyCode.LeftArrow, RuntimeKeyCode.LeftArrow); + assertKeyCode(StandaloneKeyCode.UpArrow, RuntimeKeyCode.UpArrow); + assertKeyCode(StandaloneKeyCode.RightArrow, RuntimeKeyCode.RightArrow); + assertKeyCode(StandaloneKeyCode.DownArrow, RuntimeKeyCode.DownArrow); + assertKeyCode(StandaloneKeyCode.Insert, RuntimeKeyCode.Insert); + assertKeyCode(StandaloneKeyCode.Delete, RuntimeKeyCode.Delete); + assertKeyCode(StandaloneKeyCode.KEY_0, RuntimeKeyCode.KEY_0); + assertKeyCode(StandaloneKeyCode.KEY_1, RuntimeKeyCode.KEY_1); + assertKeyCode(StandaloneKeyCode.KEY_2, RuntimeKeyCode.KEY_2); + assertKeyCode(StandaloneKeyCode.KEY_3, RuntimeKeyCode.KEY_3); + assertKeyCode(StandaloneKeyCode.KEY_4, RuntimeKeyCode.KEY_4); + assertKeyCode(StandaloneKeyCode.KEY_5, RuntimeKeyCode.KEY_5); + assertKeyCode(StandaloneKeyCode.KEY_6, RuntimeKeyCode.KEY_6); + assertKeyCode(StandaloneKeyCode.KEY_7, RuntimeKeyCode.KEY_7); + assertKeyCode(StandaloneKeyCode.KEY_8, RuntimeKeyCode.KEY_8); + assertKeyCode(StandaloneKeyCode.KEY_9, RuntimeKeyCode.KEY_9); + assertKeyCode(StandaloneKeyCode.KEY_A, RuntimeKeyCode.KEY_A); + assertKeyCode(StandaloneKeyCode.KEY_B, RuntimeKeyCode.KEY_B); + assertKeyCode(StandaloneKeyCode.KEY_C, RuntimeKeyCode.KEY_C); + assertKeyCode(StandaloneKeyCode.KEY_D, RuntimeKeyCode.KEY_D); + assertKeyCode(StandaloneKeyCode.KEY_E, RuntimeKeyCode.KEY_E); + assertKeyCode(StandaloneKeyCode.KEY_F, RuntimeKeyCode.KEY_F); + assertKeyCode(StandaloneKeyCode.KEY_G, RuntimeKeyCode.KEY_G); + assertKeyCode(StandaloneKeyCode.KEY_H, RuntimeKeyCode.KEY_H); + assertKeyCode(StandaloneKeyCode.KEY_I, RuntimeKeyCode.KEY_I); + assertKeyCode(StandaloneKeyCode.KEY_J, RuntimeKeyCode.KEY_J); + assertKeyCode(StandaloneKeyCode.KEY_K, RuntimeKeyCode.KEY_K); + assertKeyCode(StandaloneKeyCode.KEY_L, RuntimeKeyCode.KEY_L); + assertKeyCode(StandaloneKeyCode.KEY_M, RuntimeKeyCode.KEY_M); + assertKeyCode(StandaloneKeyCode.KEY_N, RuntimeKeyCode.KEY_N); + assertKeyCode(StandaloneKeyCode.KEY_O, RuntimeKeyCode.KEY_O); + assertKeyCode(StandaloneKeyCode.KEY_P, RuntimeKeyCode.KEY_P); + assertKeyCode(StandaloneKeyCode.KEY_Q, RuntimeKeyCode.KEY_Q); + assertKeyCode(StandaloneKeyCode.KEY_R, RuntimeKeyCode.KEY_R); + assertKeyCode(StandaloneKeyCode.KEY_S, RuntimeKeyCode.KEY_S); + assertKeyCode(StandaloneKeyCode.KEY_T, RuntimeKeyCode.KEY_T); + assertKeyCode(StandaloneKeyCode.KEY_U, RuntimeKeyCode.KEY_U); + assertKeyCode(StandaloneKeyCode.KEY_V, RuntimeKeyCode.KEY_V); + assertKeyCode(StandaloneKeyCode.KEY_W, RuntimeKeyCode.KEY_W); + assertKeyCode(StandaloneKeyCode.KEY_X, RuntimeKeyCode.KEY_X); + assertKeyCode(StandaloneKeyCode.KEY_Y, RuntimeKeyCode.KEY_Y); + assertKeyCode(StandaloneKeyCode.KEY_Z, RuntimeKeyCode.KEY_Z); + assertKeyCode(StandaloneKeyCode.Meta, RuntimeKeyCode.Meta); + assertKeyCode(StandaloneKeyCode.ContextMenu, RuntimeKeyCode.ContextMenu); + assertKeyCode(StandaloneKeyCode.F1, RuntimeKeyCode.F1); + assertKeyCode(StandaloneKeyCode.F2, RuntimeKeyCode.F2); + assertKeyCode(StandaloneKeyCode.F3, RuntimeKeyCode.F3); + assertKeyCode(StandaloneKeyCode.F4, RuntimeKeyCode.F4); + assertKeyCode(StandaloneKeyCode.F5, RuntimeKeyCode.F5); + assertKeyCode(StandaloneKeyCode.F6, RuntimeKeyCode.F6); + assertKeyCode(StandaloneKeyCode.F7, RuntimeKeyCode.F7); + assertKeyCode(StandaloneKeyCode.F8, RuntimeKeyCode.F8); + assertKeyCode(StandaloneKeyCode.F9, RuntimeKeyCode.F9); + assertKeyCode(StandaloneKeyCode.F10, RuntimeKeyCode.F10); + assertKeyCode(StandaloneKeyCode.F11, RuntimeKeyCode.F11); + assertKeyCode(StandaloneKeyCode.F12, RuntimeKeyCode.F12); + assertKeyCode(StandaloneKeyCode.F13, RuntimeKeyCode.F13); + assertKeyCode(StandaloneKeyCode.F14, RuntimeKeyCode.F14); + assertKeyCode(StandaloneKeyCode.F15, RuntimeKeyCode.F15); + assertKeyCode(StandaloneKeyCode.F16, RuntimeKeyCode.F16); + assertKeyCode(StandaloneKeyCode.F17, RuntimeKeyCode.F17); + assertKeyCode(StandaloneKeyCode.F18, RuntimeKeyCode.F18); + assertKeyCode(StandaloneKeyCode.F19, RuntimeKeyCode.F19); + assertKeyCode(StandaloneKeyCode.NumLock, RuntimeKeyCode.NumLock); + assertKeyCode(StandaloneKeyCode.ScrollLock, RuntimeKeyCode.ScrollLock); + assertKeyCode(StandaloneKeyCode.US_SEMICOLON, RuntimeKeyCode.US_SEMICOLON); + assertKeyCode(StandaloneKeyCode.US_EQUAL, RuntimeKeyCode.US_EQUAL); + assertKeyCode(StandaloneKeyCode.US_COMMA, RuntimeKeyCode.US_COMMA); + assertKeyCode(StandaloneKeyCode.US_MINUS, RuntimeKeyCode.US_MINUS); + assertKeyCode(StandaloneKeyCode.US_DOT, RuntimeKeyCode.US_DOT); + assertKeyCode(StandaloneKeyCode.US_SLASH, RuntimeKeyCode.US_SLASH); + assertKeyCode(StandaloneKeyCode.US_BACKTICK, RuntimeKeyCode.US_BACKTICK); + assertKeyCode(StandaloneKeyCode.US_OPEN_SQUARE_BRACKET, RuntimeKeyCode.US_OPEN_SQUARE_BRACKET); + assertKeyCode(StandaloneKeyCode.US_BACKSLASH, RuntimeKeyCode.US_BACKSLASH); + assertKeyCode(StandaloneKeyCode.US_CLOSE_SQUARE_BRACKET, RuntimeKeyCode.US_CLOSE_SQUARE_BRACKET); + assertKeyCode(StandaloneKeyCode.US_QUOTE, RuntimeKeyCode.US_QUOTE); + assertKeyCode(StandaloneKeyCode.OEM_8, RuntimeKeyCode.OEM_8); + assertKeyCode(StandaloneKeyCode.OEM_102, RuntimeKeyCode.OEM_102); + assertKeyCode(StandaloneKeyCode.NUMPAD_0, RuntimeKeyCode.NUMPAD_0); + assertKeyCode(StandaloneKeyCode.NUMPAD_1, RuntimeKeyCode.NUMPAD_1); + assertKeyCode(StandaloneKeyCode.NUMPAD_2, RuntimeKeyCode.NUMPAD_2); + assertKeyCode(StandaloneKeyCode.NUMPAD_3, RuntimeKeyCode.NUMPAD_3); + assertKeyCode(StandaloneKeyCode.NUMPAD_4, RuntimeKeyCode.NUMPAD_4); + assertKeyCode(StandaloneKeyCode.NUMPAD_5, RuntimeKeyCode.NUMPAD_5); + assertKeyCode(StandaloneKeyCode.NUMPAD_6, RuntimeKeyCode.NUMPAD_6); + assertKeyCode(StandaloneKeyCode.NUMPAD_7, RuntimeKeyCode.NUMPAD_7); + assertKeyCode(StandaloneKeyCode.NUMPAD_8, RuntimeKeyCode.NUMPAD_8); + assertKeyCode(StandaloneKeyCode.NUMPAD_9, RuntimeKeyCode.NUMPAD_9); + assertKeyCode(StandaloneKeyCode.NUMPAD_MULTIPLY, RuntimeKeyCode.NUMPAD_MULTIPLY); + assertKeyCode(StandaloneKeyCode.NUMPAD_ADD, RuntimeKeyCode.NUMPAD_ADD); + assertKeyCode(StandaloneKeyCode.NUMPAD_SEPARATOR, RuntimeKeyCode.NUMPAD_SEPARATOR); + assertKeyCode(StandaloneKeyCode.NUMPAD_SUBTRACT, RuntimeKeyCode.NUMPAD_SUBTRACT); + assertKeyCode(StandaloneKeyCode.NUMPAD_DECIMAL, RuntimeKeyCode.NUMPAD_DECIMAL); + assertKeyCode(StandaloneKeyCode.NUMPAD_DIVIDE, RuntimeKeyCode.NUMPAD_DIVIDE); + assertKeyCode(StandaloneKeyCode.KEY_IN_COMPOSITION, RuntimeKeyCode.KEY_IN_COMPOSITION); + assertKeyCode(StandaloneKeyCode.ABNT_C1, RuntimeKeyCode.ABNT_C1); + assertKeyCode(StandaloneKeyCode.ABNT_C2, RuntimeKeyCode.ABNT_C2); + assertKeyCode(StandaloneKeyCode.MAX_VALUE, RuntimeKeyCode.MAX_VALUE); + }); +}); diff --git a/src/vs/editor/test/common/view/minimapCharRenderer.test.ts b/src/vs/editor/test/common/view/minimapCharRenderer.test.ts new file mode 100644 index 0000000000..403f31ea4a --- /dev/null +++ b/src/vs/editor/test/common/view/minimapCharRenderer.test.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Constants } from 'vs/editor/common/view/minimapCharRenderer'; +import { MinimapCharRendererFactory } from 'vs/editor/test/common/view/minimapCharRendererFactory'; +import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; +import { RGBA8 } from 'vs/editor/common/core/rgba'; + +suite('MinimapCharRenderer', () => { + + let sampleData: Uint8ClampedArray = null; + + suiteSetup(() => { + sampleData = new Uint8ClampedArray(Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT); + }); + + suiteTeardown(() => { + sampleData = null; + }); + + setup(() => { + for (let i = 0; i < sampleData.length; i++) { + sampleData[i] = 0; + } + + }); + + const sampleD = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0d, 0xff, 0xff, 0xff, 0xa3, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xe5, 0xff, 0xff, 0xff, 0x5e, 0xff, 0xff, 0xff, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xa4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x10, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x02, 0xff, 0xff, 0xff, 0x6a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x47, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x31, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x0e, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x69, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xb9, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0e, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xff, 0xff, 0xf5, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xff, 0xff, 0x71, 0xff, 0xff, 0xff, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + function setSampleData(charCode: number, data: number[]) { + const rowWidth = Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT; + let chIndex = charCode - Constants.START_CH_CODE; + + let globalOutputOffset = chIndex * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT; + let inputOffset = 0; + for (let i = 0; i < Constants.SAMPLED_CHAR_HEIGHT; i++) { + let outputOffset = globalOutputOffset; + for (let j = 0; j < Constants.SAMPLED_CHAR_WIDTH; j++) { + for (let channel = 0; channel < Constants.RGBA_CHANNELS_CNT; channel++) { + sampleData[outputOffset] = data[inputOffset]; + inputOffset++; + outputOffset++; + } + } + globalOutputOffset += rowWidth; + } + } + + function createFakeImageData(width: number, height: number): ImageData { + return { + width: width, + height: height, + data: new Uint8ClampedArray(width * height * Constants.RGBA_CHANNELS_CNT) + }; + } + + test('letter d @ 2x', () => { + setSampleData('d'.charCodeAt(0), sampleD); + let renderer = MinimapCharRendererFactory.create(sampleData); + + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); + let imageData = createFakeImageData(Constants.x2_CHAR_WIDTH, Constants.x2_CHAR_HEIGHT); + // set the background color + for (let i = 0, len = imageData.data.length / 4; i < len; i++) { + imageData.data[4 * i + 0] = background.r; + imageData.data[4 * i + 1] = background.g; + imageData.data[4 * i + 2] = background.b; + imageData.data[4 * i + 3] = 255; + } + renderer.x2RenderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, false); + + let actual: number[] = []; + for (let i = 0; i < imageData.data.length; i++) { + actual[i] = imageData.data[i]; + } + assert.deepEqual(actual, [ + 0x00, 0x00, 0x00, 0xff, 0x6d, 0x6d, 0x6d, 0xff, + 0xbb, 0xbb, 0xbb, 0xff, 0xbe, 0xbe, 0xbe, 0xff, + 0x94, 0x94, 0x94, 0xff, 0x7e, 0x7e, 0x7e, 0xff, + 0xb1, 0xb1, 0xb1, 0xff, 0xbb, 0xbb, 0xbb, 0xff, + ]); + }); + + test('letter d @ 2x at runtime', () => { + let renderer = getOrCreateMinimapCharRenderer(); + + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); + let imageData = createFakeImageData(Constants.x2_CHAR_WIDTH, Constants.x2_CHAR_HEIGHT); + // set the background color + for (let i = 0, len = imageData.data.length / 4; i < len; i++) { + imageData.data[4 * i + 0] = background.r; + imageData.data[4 * i + 1] = background.g; + imageData.data[4 * i + 2] = background.b; + imageData.data[4 * i + 3] = 255; + } + + renderer.x2RenderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, false); + + let actual: number[] = []; + for (let i = 0; i < imageData.data.length; i++) { + actual[i] = imageData.data[i]; + } + assert.deepEqual(actual, [ + 0x00, 0x00, 0x00, 0xff, 0x6d, 0x6d, 0x6d, 0xff, + 0xbb, 0xbb, 0xbb, 0xff, 0xbe, 0xbe, 0xbe, 0xff, + 0x94, 0x94, 0x94, 0xff, 0x7e, 0x7e, 0x7e, 0xff, + 0xb1, 0xb1, 0xb1, 0xff, 0xbb, 0xbb, 0xbb, 0xff, + ]); + }); + + test('letter d @ 1x', () => { + setSampleData('d'.charCodeAt(0), sampleD); + let renderer = MinimapCharRendererFactory.create(sampleData); + + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); + let imageData = createFakeImageData(Constants.x1_CHAR_WIDTH, Constants.x1_CHAR_HEIGHT); + // set the background color + for (let i = 0, len = imageData.data.length / 4; i < len; i++) { + imageData.data[4 * i + 0] = background.r; + imageData.data[4 * i + 1] = background.g; + imageData.data[4 * i + 2] = background.b; + imageData.data[4 * i + 3] = 255; + } + + renderer.x1RenderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, false); + + let actual: number[] = []; + for (let i = 0; i < imageData.data.length; i++) { + actual[i] = imageData.data[i]; + } + assert.deepEqual(actual, [ + 0x55, 0x55, 0x55, 0xff, + 0x93, 0x93, 0x93, 0xff, + ]); + }); + + test('letter d @ 1x at runtime', () => { + let renderer = getOrCreateMinimapCharRenderer(); + + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); + let imageData = createFakeImageData(Constants.x1_CHAR_WIDTH, Constants.x1_CHAR_HEIGHT); + // set the background color + for (let i = 0, len = imageData.data.length / 4; i < len; i++) { + imageData.data[4 * i + 0] = background.r; + imageData.data[4 * i + 1] = background.g; + imageData.data[4 * i + 2] = background.b; + imageData.data[4 * i + 3] = 255; + } + + renderer.x1RenderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, false); + + let actual: number[] = []; + for (let i = 0; i < imageData.data.length; i++) { + actual[i] = imageData.data[i]; + } + assert.deepEqual(actual, [ + 0x55, 0x55, 0x55, 0xff, + 0x93, 0x93, 0x93, 0xff, + ]); + }); + +}); \ No newline at end of file diff --git a/src/vs/editor/test/common/view/minimapCharRendererFactory.ts b/src/vs/editor/test/common/view/minimapCharRendererFactory.ts new file mode 100644 index 0000000000..5153a5b4c5 --- /dev/null +++ b/src/vs/editor/test/common/view/minimapCharRendererFactory.ts @@ -0,0 +1,173 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Constants, MinimapCharRenderer } from 'vs/editor/common/view/minimapCharRenderer'; + +const enum InternalConstants { + CA_CHANNELS_CNT = 2, +} + +export class MinimapCharRendererFactory { + + public static create(source: Uint8ClampedArray): MinimapCharRenderer { + const expectedLength = (Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT); + if (source.length !== expectedLength) { + throw new Error('Unexpected source in MinimapCharRenderer'); + } + + let x2CharData = this.toGrayscale(MinimapCharRendererFactory._downsample2x(source)); + let x1CharData = this.toGrayscale(MinimapCharRendererFactory._downsample1x(source)); + return new MinimapCharRenderer(x2CharData, x1CharData); + } + + private static toGrayscale(charData: Uint8ClampedArray): Uint8ClampedArray { + let newLength = charData.length / 2; + let result = new Uint8ClampedArray(newLength); + let sourceOffset = 0; + for (var i = 0; i < newLength; i++) { + let color = charData[sourceOffset]; + let alpha = charData[sourceOffset + 1]; + let newColor = Math.round((color * alpha) / 255); + result[i] = newColor; + sourceOffset += 2; + } + return result; + } + + private static _extractSampledChar(source: Uint8ClampedArray, charIndex: number, dest: Uint8ClampedArray) { + let destOffset = 0; + for (let i = 0; i < Constants.SAMPLED_CHAR_HEIGHT; i++) { + let sourceOffset = ( + Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT * i + + Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * charIndex + ); + for (let j = 0; j < Constants.SAMPLED_CHAR_WIDTH; j++) { + for (let c = 0; c < Constants.RGBA_CHANNELS_CNT; c++) { + dest[destOffset] = source[sourceOffset]; + sourceOffset++; + destOffset++; + } + } + } + } + + private static _downsample2xChar(source: Uint8ClampedArray, dest: Uint8ClampedArray): void { + // chars are 2 x 4px (width x height) + const resultLen = Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT; + const result = new Uint16Array(resultLen); + for (let i = 0; i < resultLen; i++) { + result[i] = 0; + } + + let inputOffset = 0, globalOutputOffset = 0; + for (let i = 0; i < Constants.SAMPLED_CHAR_HEIGHT; i++) { + + let outputOffset = globalOutputOffset; + + let color = 0; + let alpha = 0; + for (let j = 0; j < Constants.SAMPLED_HALF_CHAR_WIDTH; j++) { + color += source[inputOffset]; // R + alpha += source[inputOffset + 3]; // A + inputOffset += Constants.RGBA_CHANNELS_CNT; + } + result[outputOffset] += color; + result[outputOffset + 1] += alpha; + outputOffset += InternalConstants.CA_CHANNELS_CNT; + + color = 0; + alpha = 0; + for (let j = 0; j < Constants.SAMPLED_HALF_CHAR_WIDTH; j++) { + color += source[inputOffset]; // R + alpha += source[inputOffset + 3]; // A + inputOffset += Constants.RGBA_CHANNELS_CNT; + } + result[outputOffset] += color; + result[outputOffset + 1] += alpha; + outputOffset += InternalConstants.CA_CHANNELS_CNT; + + if (i === 2 || i === 5 || i === 8) { + globalOutputOffset = outputOffset; + } + } + + for (let i = 0; i < resultLen; i++) { + dest[i] = result[i] / 12; // 15 it should be + } + } + + private static _downsample2x(data: Uint8ClampedArray): Uint8ClampedArray { + const resultLen = Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT * Constants.CHAR_COUNT; + const result = new Uint8ClampedArray(resultLen); + + const sampledChar = new Uint8ClampedArray(Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT); + const downsampledChar = new Uint8ClampedArray(Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT); + + for (let charIndex = 0; charIndex < Constants.CHAR_COUNT; charIndex++) { + this._extractSampledChar(data, charIndex, sampledChar); + this._downsample2xChar(sampledChar, downsampledChar); + let resultOffset = (Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT * charIndex); + for (let i = 0; i < downsampledChar.length; i++) { + result[resultOffset + i] = downsampledChar[i]; + } + } + + return result; + } + + private static _downsample1xChar(source: Uint8ClampedArray, dest: Uint8ClampedArray): void { + // chars are 1 x 2px (width x height) + const resultLen = Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT; + const result = new Uint16Array(resultLen); + for (let i = 0; i < resultLen; i++) { + result[i] = 0; + } + + let inputOffset = 0, globalOutputOffset = 0; + for (let i = 0; i < Constants.SAMPLED_CHAR_HEIGHT; i++) { + + let outputOffset = globalOutputOffset; + + let color = 0; + let alpha = 0; + for (let j = 0; j < Constants.SAMPLED_CHAR_WIDTH; j++) { + color += source[inputOffset]; // R + alpha += source[inputOffset + 3]; // A + inputOffset += Constants.RGBA_CHANNELS_CNT; + } + result[outputOffset] += color; + result[outputOffset + 1] += alpha; + outputOffset += InternalConstants.CA_CHANNELS_CNT; + + if (i === 5) { + globalOutputOffset = outputOffset; + } + } + + for (let i = 0; i < resultLen; i++) { + dest[i] = result[i] / 50; // 60 it should be + } + } + + private static _downsample1x(data: Uint8ClampedArray): Uint8ClampedArray { + const resultLen = Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT * Constants.CHAR_COUNT; + const result = new Uint8ClampedArray(resultLen); + + const sampledChar = new Uint8ClampedArray(Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT); + const downsampledChar = new Uint8ClampedArray(Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT); + + for (let charIndex = 0; charIndex < Constants.CHAR_COUNT; charIndex++) { + this._extractSampledChar(data, charIndex, sampledChar); + this._downsample1xChar(sampledChar, downsampledChar); + let resultOffset = (Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * InternalConstants.CA_CHANNELS_CNT * charIndex); + for (let i = 0; i < downsampledChar.length; i++) { + result[resultOffset + i] = downsampledChar[i]; + } + } + + return result; + } +} diff --git a/src/vs/editor/test/common/view/overviewZoneManager.test.ts b/src/vs/editor/test/common/view/overviewZoneManager.test.ts new file mode 100644 index 0000000000..b09fe5abf8 --- /dev/null +++ b/src/vs/editor/test/common/view/overviewZoneManager.test.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { OverviewRulerLane } from 'vs/editor/common/editorCommon'; +import { OverviewZoneManager, ColorZone, OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; +import { LIGHT } from 'vs/platform/theme/common/themeService'; + +suite('Editor View - OverviewZoneManager', () => { + + test('pixel ratio 1, dom height 600', () => { + const LINE_COUNT = 50; + const LINE_HEIGHT = 20; + let manager = new OverviewZoneManager((lineNumber) => LINE_HEIGHT * lineNumber); + manager.setMinimumHeight(6); + manager.setMaximumHeight(6); + manager.setThemeType(LIGHT); + manager.setDOMWidth(30); + manager.setDOMHeight(600); + manager.setOuterHeight(LINE_COUNT * LINE_HEIGHT); + manager.setLineHeight(LINE_HEIGHT); + manager.setPixelRatio(1); + + manager.setZones([ + new OverviewRulerZone(1, 1, OverviewRulerLane.Full, 10, '1', '1', '1'), + new OverviewRulerZone(10, 10, OverviewRulerLane.Full, 0, '2', '2', '2'), + new OverviewRulerZone(30, 31, OverviewRulerLane.Full, 0, '3', '3', '3'), + new OverviewRulerZone(50, 50, OverviewRulerLane.Full, 0, '4', '4', '4'), + ]); + + // one line = 12, but cap is at 6 + assert.deepEqual(manager.resolveColorZones(), [ + new ColorZone(12, 22, 1, OverviewRulerLane.Full), // forced height of 10 + new ColorZone(123, 129, 2, OverviewRulerLane.Full), // 120 -> 132 + new ColorZone(363, 369, 3, OverviewRulerLane.Full), // 360 -> 372 [360 -> 384] + new ColorZone(375, 381, 3, OverviewRulerLane.Full), // 372 -> 384 [360 -> 384] + new ColorZone(594, 600, 4, OverviewRulerLane.Full), // 588 -> 600 + ]); + }); + + test('pixel ratio 1, dom height 300', () => { + const LINE_COUNT = 50; + const LINE_HEIGHT = 20; + let manager = new OverviewZoneManager((lineNumber) => LINE_HEIGHT * lineNumber); + manager.setMinimumHeight(6); + manager.setMaximumHeight(6); + manager.setThemeType(LIGHT); + manager.setDOMWidth(30); + manager.setDOMHeight(300); + manager.setOuterHeight(LINE_COUNT * LINE_HEIGHT); + manager.setLineHeight(LINE_HEIGHT); + manager.setPixelRatio(1); + + manager.setZones([ + new OverviewRulerZone(1, 1, OverviewRulerLane.Full, 10, '1', '1', '1'), + new OverviewRulerZone(10, 10, OverviewRulerLane.Full, 0, '2', '2', '2'), + new OverviewRulerZone(30, 31, OverviewRulerLane.Full, 0, '3', '3', '3'), + new OverviewRulerZone(50, 50, OverviewRulerLane.Full, 0, '4', '4', '4'), + ]); + + // one line = 6, cap is at 6 + assert.deepEqual(manager.resolveColorZones(), [ + new ColorZone(6, 16, 1, OverviewRulerLane.Full), // forced height of 10 + new ColorZone(60, 66, 2, OverviewRulerLane.Full), // 60 -> 66 + new ColorZone(180, 192, 3, OverviewRulerLane.Full), // 180 -> 192 + new ColorZone(294, 300, 4, OverviewRulerLane.Full), // 294 -> 300 + ]); + }); + + test('pixel ratio 2, dom height 300', () => { + const LINE_COUNT = 50; + const LINE_HEIGHT = 20; + let manager = new OverviewZoneManager((lineNumber) => LINE_HEIGHT * lineNumber); + manager.setMinimumHeight(6); + manager.setMaximumHeight(6); + manager.setThemeType(LIGHT); + manager.setDOMWidth(30); + manager.setDOMHeight(300); + manager.setOuterHeight(LINE_COUNT * LINE_HEIGHT); + manager.setLineHeight(LINE_HEIGHT); + manager.setPixelRatio(2); + + manager.setZones([ + new OverviewRulerZone(1, 1, OverviewRulerLane.Full, 10, '1', '1', '1'), + new OverviewRulerZone(10, 10, OverviewRulerLane.Full, 0, '2', '2', '2'), + new OverviewRulerZone(30, 31, OverviewRulerLane.Full, 0, '3', '3', '3'), + new OverviewRulerZone(50, 50, OverviewRulerLane.Full, 0, '4', '4', '4'), + ]); + + // one line = 6, cap is at 12 + assert.deepEqual(manager.resolveColorZones(), [ + new ColorZone(12, 32, 1, OverviewRulerLane.Full), // forced height of 10 => forced height of 20 + new ColorZone(120, 132, 2, OverviewRulerLane.Full), // 120 -> 132 + new ColorZone(360, 384, 3, OverviewRulerLane.Full), // 360 -> 384 + new ColorZone(588, 600, 4, OverviewRulerLane.Full), // 588 -> 600 + ]); + }); +}); diff --git a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts new file mode 100644 index 0000000000..f2d336ab4c --- /dev/null +++ b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts @@ -0,0 +1,744 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { RenderMinimap, EditorLayoutInfo, EditorLayoutProvider, IEditorLayoutProviderOpts } from 'vs/editor/common/config/editorOptions'; + +suite('Editor ViewLayout - EditorLayoutProvider', () => { + + function doTest(input: IEditorLayoutProviderOpts, expected: EditorLayoutInfo): void { + let actual = EditorLayoutProvider.compute(input); + assert.deepEqual(actual, expected); + } + + test('EditorLayoutProvider 1', () => { + doTest({ + outerWidth: 1000, + outerHeight: 800, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 1, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: false, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 1000, + height: 800, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 800, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + lineNumbersHeight: 800, + + decorationsLeft: 0, + decorationsWidth: 10, + decorationsHeight: 800, + + contentLeft: 10, + contentWidth: 990, + contentHeight: 800, + + renderMinimap: RenderMinimap.None, + minimapWidth: 0, + viewportColumn: 99, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 800, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 1.1', () => { + doTest({ + outerWidth: 1000, + outerHeight: 800, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 1, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 11, + horizontalScrollbarHeight: 12, + scrollbarArrowSize: 13, + verticalScrollbarHasArrows: true, + minimap: false, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 1000, + height: 800, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 800, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + lineNumbersHeight: 800, + + decorationsLeft: 0, + decorationsWidth: 10, + decorationsHeight: 800, + + contentLeft: 10, + contentWidth: 990, + contentHeight: 800, + + renderMinimap: RenderMinimap.None, + minimapWidth: 0, + viewportColumn: 97, + + verticalScrollbarWidth: 11, + horizontalScrollbarHeight: 12, + + overviewRuler: { + top: 13, + width: 11, + height: (800 - 2 * 13), + right: 0 + } + }); + }); + + test('EditorLayoutProvider 2', () => { + doTest({ + outerWidth: 900, + outerHeight: 800, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 1, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: false, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 900, + height: 800, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 800, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + lineNumbersHeight: 800, + + decorationsLeft: 0, + decorationsWidth: 10, + decorationsHeight: 800, + + contentLeft: 10, + contentWidth: 890, + contentHeight: 800, + + renderMinimap: RenderMinimap.None, + minimapWidth: 0, + viewportColumn: 89, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 800, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 3', () => { + doTest({ + outerWidth: 900, + outerHeight: 900, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 1, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: false, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 900, + height: 900, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 900, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + lineNumbersHeight: 900, + + decorationsLeft: 0, + decorationsWidth: 10, + decorationsHeight: 900, + + contentLeft: 10, + contentWidth: 890, + contentHeight: 900, + + renderMinimap: RenderMinimap.None, + minimapWidth: 0, + viewportColumn: 89, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 900, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 4', () => { + doTest({ + outerWidth: 900, + outerHeight: 900, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 5, + lineNumbersDigitCount: 1, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: false, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 900, + height: 900, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 900, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + lineNumbersHeight: 900, + + decorationsLeft: 0, + decorationsWidth: 10, + decorationsHeight: 900, + + contentLeft: 10, + contentWidth: 890, + contentHeight: 900, + + renderMinimap: RenderMinimap.None, + minimapWidth: 0, + viewportColumn: 89, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 900, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 5', () => { + doTest({ + outerWidth: 900, + outerHeight: 900, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: true, + lineNumbersMinChars: 5, + lineNumbersDigitCount: 1, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: false, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 900, + height: 900, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 900, + + lineNumbersLeft: 0, + lineNumbersWidth: 50, + lineNumbersHeight: 900, + + decorationsLeft: 50, + decorationsWidth: 10, + decorationsHeight: 900, + + contentLeft: 60, + contentWidth: 840, + contentHeight: 900, + + renderMinimap: RenderMinimap.None, + minimapWidth: 0, + viewportColumn: 84, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 900, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 6', () => { + doTest({ + outerWidth: 900, + outerHeight: 900, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: true, + lineNumbersMinChars: 5, + lineNumbersDigitCount: 5, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: false, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 900, + height: 900, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 900, + + lineNumbersLeft: 0, + lineNumbersWidth: 50, + lineNumbersHeight: 900, + + decorationsLeft: 50, + decorationsWidth: 10, + decorationsHeight: 900, + + contentLeft: 60, + contentWidth: 840, + contentHeight: 900, + + renderMinimap: RenderMinimap.None, + minimapWidth: 0, + viewportColumn: 84, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 900, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 7', () => { + doTest({ + outerWidth: 900, + outerHeight: 900, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: true, + lineNumbersMinChars: 5, + lineNumbersDigitCount: 6, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: false, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 900, + height: 900, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 900, + + lineNumbersLeft: 0, + lineNumbersWidth: 60, + lineNumbersHeight: 900, + + decorationsLeft: 60, + decorationsWidth: 10, + decorationsHeight: 900, + + contentLeft: 70, + contentWidth: 830, + contentHeight: 900, + + renderMinimap: RenderMinimap.None, + minimapWidth: 0, + viewportColumn: 83, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 900, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 8', () => { + doTest({ + outerWidth: 900, + outerHeight: 900, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: true, + lineNumbersMinChars: 5, + lineNumbersDigitCount: 6, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 5, + maxDigitWidth: 5, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: false, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 900, + height: 900, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 900, + + lineNumbersLeft: 0, + lineNumbersWidth: 30, + lineNumbersHeight: 900, + + decorationsLeft: 30, + decorationsWidth: 10, + decorationsHeight: 900, + + contentLeft: 40, + contentWidth: 860, + contentHeight: 900, + + renderMinimap: RenderMinimap.None, + minimapWidth: 0, + viewportColumn: 172, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 900, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 8 - rounds floats', () => { + doTest({ + outerWidth: 900, + outerHeight: 900, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: true, + lineNumbersMinChars: 5, + lineNumbersDigitCount: 6, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 5.05, + maxDigitWidth: 5.05, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: false, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 900, + height: 900, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 900, + + lineNumbersLeft: 0, + lineNumbersWidth: 30, + lineNumbersHeight: 900, + + decorationsLeft: 30, + decorationsWidth: 10, + decorationsHeight: 900, + + contentLeft: 40, + contentWidth: 860, + contentHeight: 900, + + renderMinimap: RenderMinimap.None, + minimapWidth: 0, + viewportColumn: 170, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 900, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 9 - render minimap', () => { + doTest({ + outerWidth: 1000, + outerHeight: 800, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 1, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: true, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 1, + }, { + width: 1000, + height: 800, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 800, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + lineNumbersHeight: 800, + + decorationsLeft: 0, + decorationsWidth: 10, + decorationsHeight: 800, + + contentLeft: 10, + contentWidth: 900, + contentHeight: 800, + + renderMinimap: RenderMinimap.Small, + minimapWidth: 90, + viewportColumn: 90, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 800, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 9 - render minimap with pixelRatio = 2', () => { + doTest({ + outerWidth: 1000, + outerHeight: 800, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 1, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: true, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 2, + }, { + width: 1000, + height: 800, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 800, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + lineNumbersHeight: 800, + + decorationsLeft: 0, + decorationsWidth: 10, + decorationsHeight: 800, + + contentLeft: 10, + contentWidth: 900, + contentHeight: 800, + + renderMinimap: RenderMinimap.Large, + minimapWidth: 90, + viewportColumn: 90, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 800, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 9 - render minimap with pixelRatio = 4', () => { + doTest({ + outerWidth: 1000, + outerHeight: 800, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 1, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: true, + minimapRenderCharacters: true, + minimapMaxColumn: 150, + pixelRatio: 4, + }, { + width: 1000, + height: 800, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + glyphMarginHeight: 800, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + lineNumbersHeight: 800, + + decorationsLeft: 0, + decorationsWidth: 10, + decorationsHeight: 800, + + contentLeft: 10, + contentWidth: 943, + contentHeight: 800, + + renderMinimap: RenderMinimap.Large, + minimapWidth: 47, + viewportColumn: 94, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 800, + right: 0 + } + }); + }); +}); diff --git a/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts b/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts new file mode 100644 index 0000000000..f0d494e3a5 --- /dev/null +++ b/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { DecorationSegment, LineDecorationsNormalizer, LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; +import { Range } from 'vs/editor/common/core/range'; +import { InlineDecoration } from 'vs/editor/common/viewModel/viewModel'; + +suite('Editor ViewLayout - ViewLineParts', () => { + + function newDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, inlineClassName: string): InlineDecoration { + return new InlineDecoration(new Range(startLineNumber, startColumn, endLineNumber, endColumn), inlineClassName, false); + } + + test('Bug 9827:Overlapping inline decorations can cause wrong inline class to be applied', () => { + + var result = LineDecorationsNormalizer.normalize([ + new LineDecoration(1, 11, 'c1', false), + new LineDecoration(3, 4, 'c2', false) + ]); + + assert.deepEqual(result, [ + new DecorationSegment(0, 1, 'c1'), + new DecorationSegment(2, 2, 'c2 c1'), + new DecorationSegment(3, 9, 'c1'), + ]); + }); + + test('issue #3462: no whitespace shown at the end of a decorated line', () => { + + var result = LineDecorationsNormalizer.normalize([ + new LineDecoration(15, 21, 'vs-whitespace', false), + new LineDecoration(20, 21, 'inline-folded', false), + ]); + + assert.deepEqual(result, [ + new DecorationSegment(14, 18, 'vs-whitespace'), + new DecorationSegment(19, 19, 'vs-whitespace inline-folded') + ]); + }); + + test('issue #3661: Link decoration bleeds to next line when wrapping', () => { + + let result = LineDecoration.filter([ + newDecoration(2, 12, 3, 30, 'detected-link') + ], 3, 12, 500); + + assert.deepEqual(result, [ + new LineDecoration(12, 30, 'detected-link', false), + ]); + }); + + test('ViewLineParts', () => { + + assert.deepEqual(LineDecorationsNormalizer.normalize([ + new LineDecoration(1, 2, 'c1', false), + new LineDecoration(3, 4, 'c2', false) + ]), [ + new DecorationSegment(0, 0, 'c1'), + new DecorationSegment(2, 2, 'c2') + ]); + + assert.deepEqual(LineDecorationsNormalizer.normalize([ + new LineDecoration(1, 3, 'c1', false), + new LineDecoration(3, 4, 'c2', false) + ]), [ + new DecorationSegment(0, 1, 'c1'), + new DecorationSegment(2, 2, 'c2') + ]); + + assert.deepEqual(LineDecorationsNormalizer.normalize([ + new LineDecoration(1, 4, 'c1', false), + new LineDecoration(3, 4, 'c2', false) + ]), [ + new DecorationSegment(0, 1, 'c1'), + new DecorationSegment(2, 2, 'c1 c2') + ]); + + assert.deepEqual(LineDecorationsNormalizer.normalize([ + new LineDecoration(1, 4, 'c1', false), + new LineDecoration(1, 4, 'c1*', false), + new LineDecoration(3, 4, 'c2', false) + ]), [ + new DecorationSegment(0, 1, 'c1 c1*'), + new DecorationSegment(2, 2, 'c1 c1* c2') + ]); + + assert.deepEqual(LineDecorationsNormalizer.normalize([ + new LineDecoration(1, 4, 'c1', false), + new LineDecoration(1, 4, 'c1*', false), + new LineDecoration(1, 4, 'c1**', false), + new LineDecoration(3, 4, 'c2', false) + ]), [ + new DecorationSegment(0, 1, 'c1 c1* c1**'), + new DecorationSegment(2, 2, 'c1 c1* c1** c2') + ]); + + assert.deepEqual(LineDecorationsNormalizer.normalize([ + new LineDecoration(1, 4, 'c1', false), + new LineDecoration(1, 4, 'c1*', false), + new LineDecoration(1, 4, 'c1**', false), + new LineDecoration(3, 4, 'c2', false), + new LineDecoration(3, 4, 'c2*', false) + ]), [ + new DecorationSegment(0, 1, 'c1 c1* c1**'), + new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*') + ]); + + assert.deepEqual(LineDecorationsNormalizer.normalize([ + new LineDecoration(1, 4, 'c1', false), + new LineDecoration(1, 4, 'c1*', false), + new LineDecoration(1, 4, 'c1**', false), + new LineDecoration(3, 4, 'c2', false), + new LineDecoration(3, 5, 'c2*', false) + ]), [ + new DecorationSegment(0, 1, 'c1 c1* c1**'), + new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*'), + new DecorationSegment(3, 3, 'c2*') + ]); + }); +}); diff --git a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts b/src/vs/editor/test/common/viewLayout/linesLayout.test.ts new file mode 100644 index 0000000000..223550ad62 --- /dev/null +++ b/src/vs/editor/test/common/viewLayout/linesLayout.test.ts @@ -0,0 +1,597 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout'; + +suite('Editor ViewLayout - LinesLayout', () => { + + test('LinesLayout 1', () => { + + // Start off with 10 lines + var linesLayout = new LinesLayout(10, 10); + + // lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + // whitespace: - + assert.equal(linesLayout.getLinesTotalHeight(), 100); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 10); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 20); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 30); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 40); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 50); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 60); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 70); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 80); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 90); + + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(1), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(5), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(9), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 2); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(11), 2); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(15), 2); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(19), 2); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(20), 3); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(21), 3); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(29), 3); + + // Add whitespace of height 5px after 2nd line + linesLayout.insertWhitespace(2, 0, 5); + // lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + // whitespace: a(2,5) + assert.equal(linesLayout.getLinesTotalHeight(), 105); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 10); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 25); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 35); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 45); + + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(1), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(9), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 2); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(20), 3); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(21), 3); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(24), 3); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(25), 3); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(35), 4); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(45), 5); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(104), 10); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(105), 10); + + // Add two more whitespaces of height 5px + linesLayout.insertWhitespace(3, 0, 5); + linesLayout.insertWhitespace(4, 0, 5); + // lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + // whitespace: a(2,5), b(3, 5), c(4, 5) + assert.equal(linesLayout.getLinesTotalHeight(), 115); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 10); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 25); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 40); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 55); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 65); + + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(1), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(9), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 2); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(19), 2); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(20), 3); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(34), 3); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(35), 4); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(49), 4); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(50), 5); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(64), 5); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(65), 6); + + assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(0), 20); // 20 -> 25 + assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(1), 35); // 35 -> 40 + assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(2), 50); + + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(0), 0); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(19), 0); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(20), 0); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(21), 0); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(22), 0); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(23), 0); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(24), 0); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(25), 1); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(26), 1); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(34), 1); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(35), 1); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(36), 1); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(39), 1); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(40), 2); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(41), 2); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(49), 2); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(50), 2); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(51), 2); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(54), 2); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(55), -1); + assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(1000), -1); + + }); + + test('LinesLayout 2', () => { + + // Start off with 10 lines and one whitespace after line 2, of height 5 + var linesLayout = new LinesLayout(10, 1); + var a = linesLayout.insertWhitespace(2, 0, 5); + + // 10 lines + // whitespace: - a(2,5) + assert.equal(linesLayout.getLinesTotalHeight(), 15); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 7); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 8); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 9); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 10); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 11); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 12); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 13); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 14); + + // Change whitespace height + // 10 lines + // whitespace: - a(2,10) + linesLayout.changeWhitespace(a, 2, 10); + assert.equal(linesLayout.getLinesTotalHeight(), 20); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 12); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 13); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 14); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 15); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 18); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 19); + + // Change whitespace position + // 10 lines + // whitespace: - a(5,10) + linesLayout.changeWhitespace(a, 5, 10); + assert.equal(linesLayout.getLinesTotalHeight(), 20); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 4); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 15); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 18); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 19); + + // Pretend that lines 5 and 6 were deleted + // 8 lines + // whitespace: - a(4,10) + linesLayout.onLinesDeleted(5, 6); + assert.equal(linesLayout.getLinesTotalHeight(), 18); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 14); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 15); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17); + + // Insert two lines at the beginning + // 10 lines + // whitespace: - a(6,10) + linesLayout.onLinesInserted(1, 2); + assert.equal(linesLayout.getLinesTotalHeight(), 20); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 4); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 5); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 18); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 19); + + // Remove whitespace + // 10 lines + linesLayout.removeWhitespace(a); + assert.equal(linesLayout.getLinesTotalHeight(), 10); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 4); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 5); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 6); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 7); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 8); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 9); + }); + + test('LinesLayout getLineNumberAtOrAfterVerticalOffset', () => { + var linesLayout = new LinesLayout(10, 1); + linesLayout.insertWhitespace(6, 0, 10); + + // 10 lines + // whitespace: - a(6,10) + assert.equal(linesLayout.getLinesTotalHeight(), 20); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 4); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 5); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 18); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 19); + + // Do some hit testing + // line [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + // vertical: [0, 1, 2, 3, 4, 5, 16, 17, 18, 19] + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(-100), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(-1), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(1), 2); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(2), 3); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(3), 4); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(4), 5); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(5), 6); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(6), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(7), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(8), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(9), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(11), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(12), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(13), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(14), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(15), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(16), 7); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(17), 8); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(18), 9); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(19), 10); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(20), 10); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(21), 10); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(22), 10); + assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(23), 10); + }); + + test('LinesLayout getCenteredLineInViewport', () => { + var linesLayout = new LinesLayout(10, 1); + linesLayout.insertWhitespace(6, 0, 10); + + // 10 lines + // whitespace: - a(6,10) + assert.equal(linesLayout.getLinesTotalHeight(), 20); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 2); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 3); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 4); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 5); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 16); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 17); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 18); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 19); + + // Find centered line in viewport 1 + // line [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + // vertical: [0, 1, 2, 3, 4, 5, 16, 17, 18, 19] + assert.equal(linesLayout.getLinesViewportData(0, 1).centeredLineNumber, 1); + assert.equal(linesLayout.getLinesViewportData(0, 2).centeredLineNumber, 2); + assert.equal(linesLayout.getLinesViewportData(0, 3).centeredLineNumber, 2); + assert.equal(linesLayout.getLinesViewportData(0, 4).centeredLineNumber, 3); + assert.equal(linesLayout.getLinesViewportData(0, 5).centeredLineNumber, 3); + assert.equal(linesLayout.getLinesViewportData(0, 6).centeredLineNumber, 4); + assert.equal(linesLayout.getLinesViewportData(0, 7).centeredLineNumber, 4); + assert.equal(linesLayout.getLinesViewportData(0, 8).centeredLineNumber, 5); + assert.equal(linesLayout.getLinesViewportData(0, 9).centeredLineNumber, 5); + assert.equal(linesLayout.getLinesViewportData(0, 10).centeredLineNumber, 6); + assert.equal(linesLayout.getLinesViewportData(0, 11).centeredLineNumber, 6); + assert.equal(linesLayout.getLinesViewportData(0, 12).centeredLineNumber, 6); + assert.equal(linesLayout.getLinesViewportData(0, 13).centeredLineNumber, 6); + assert.equal(linesLayout.getLinesViewportData(0, 14).centeredLineNumber, 6); + assert.equal(linesLayout.getLinesViewportData(0, 15).centeredLineNumber, 6); + assert.equal(linesLayout.getLinesViewportData(0, 16).centeredLineNumber, 6); + assert.equal(linesLayout.getLinesViewportData(0, 17).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 18).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 19).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 21).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 22).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 23).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 24).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 25).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 26).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 27).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 28).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 29).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 30).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 31).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 32).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(0, 33).centeredLineNumber, 7); + + // Find centered line in viewport 2 + // line [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + // vertical: [0, 1, 2, 3, 4, 5, 16, 17, 18, 19] + assert.equal(linesLayout.getLinesViewportData(0, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(1, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(2, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(3, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(4, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(5, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(6, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(7, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(8, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(9, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(10, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(11, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(12, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(13, 20).centeredLineNumber, 7); + assert.equal(linesLayout.getLinesViewportData(14, 20).centeredLineNumber, 8); + assert.equal(linesLayout.getLinesViewportData(15, 20).centeredLineNumber, 8); + assert.equal(linesLayout.getLinesViewportData(16, 20).centeredLineNumber, 9); + assert.equal(linesLayout.getLinesViewportData(17, 20).centeredLineNumber, 9); + assert.equal(linesLayout.getLinesViewportData(18, 20).centeredLineNumber, 10); + assert.equal(linesLayout.getLinesViewportData(19, 20).centeredLineNumber, 10); + assert.equal(linesLayout.getLinesViewportData(20, 23).centeredLineNumber, 10); + assert.equal(linesLayout.getLinesViewportData(21, 23).centeredLineNumber, 10); + assert.equal(linesLayout.getLinesViewportData(22, 23).centeredLineNumber, 10); + }); + + test('LinesLayout getLinesViewportData 1', () => { + var linesLayout = new LinesLayout(10, 10); + linesLayout.insertWhitespace(6, 0, 100); + + // 10 lines + // whitespace: - a(6,100) + assert.equal(linesLayout.getLinesTotalHeight(), 200); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 10); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 20); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 30); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 40); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 50); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 160); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 170); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 180); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 190); + + // viewport 0->50 + var viewportData = linesLayout.getLinesViewportData(0, 50); + assert.equal(viewportData.startLineNumber, 1); + assert.equal(viewportData.endLineNumber, 5); + assert.equal(viewportData.completelyVisibleStartLineNumber, 1); + assert.equal(viewportData.completelyVisibleEndLineNumber, 5); + assert.deepEqual(viewportData.relativeVerticalOffset, [0, 10, 20, 30, 40]); + + // viewport 1->51 + viewportData = linesLayout.getLinesViewportData(1, 51); + assert.equal(viewportData.startLineNumber, 1); + assert.equal(viewportData.endLineNumber, 6); + assert.equal(viewportData.completelyVisibleStartLineNumber, 2); + assert.equal(viewportData.completelyVisibleEndLineNumber, 5); + assert.deepEqual(viewportData.relativeVerticalOffset, [0, 10, 20, 30, 40, 50]); + + // viewport 5->55 + viewportData = linesLayout.getLinesViewportData(5, 55); + assert.equal(viewportData.startLineNumber, 1); + assert.equal(viewportData.endLineNumber, 6); + assert.equal(viewportData.completelyVisibleStartLineNumber, 2); + assert.equal(viewportData.completelyVisibleEndLineNumber, 5); + assert.deepEqual(viewportData.relativeVerticalOffset, [0, 10, 20, 30, 40, 50]); + + // viewport 10->60 + viewportData = linesLayout.getLinesViewportData(10, 60); + assert.equal(viewportData.startLineNumber, 2); + assert.equal(viewportData.endLineNumber, 6); + assert.equal(viewportData.completelyVisibleStartLineNumber, 2); + assert.equal(viewportData.completelyVisibleEndLineNumber, 6); + assert.deepEqual(viewportData.relativeVerticalOffset, [10, 20, 30, 40, 50]); + + // viewport 50->100 + viewportData = linesLayout.getLinesViewportData(50, 100); + assert.equal(viewportData.startLineNumber, 6); + assert.equal(viewportData.endLineNumber, 6); + assert.equal(viewportData.completelyVisibleStartLineNumber, 6); + assert.equal(viewportData.completelyVisibleEndLineNumber, 6); + assert.deepEqual(viewportData.relativeVerticalOffset, [50]); + + // viewport 60->110 + viewportData = linesLayout.getLinesViewportData(60, 110); + assert.equal(viewportData.startLineNumber, 7); + assert.equal(viewportData.endLineNumber, 7); + assert.equal(viewportData.completelyVisibleStartLineNumber, 7); + assert.equal(viewportData.completelyVisibleEndLineNumber, 7); + assert.deepEqual(viewportData.relativeVerticalOffset, [160]); + + // viewport 65->115 + viewportData = linesLayout.getLinesViewportData(65, 115); + assert.equal(viewportData.startLineNumber, 7); + assert.equal(viewportData.endLineNumber, 7); + assert.equal(viewportData.completelyVisibleStartLineNumber, 7); + assert.equal(viewportData.completelyVisibleEndLineNumber, 7); + assert.deepEqual(viewportData.relativeVerticalOffset, [160]); + + // viewport 50->159 + viewportData = linesLayout.getLinesViewportData(50, 159); + assert.equal(viewportData.startLineNumber, 6); + assert.equal(viewportData.endLineNumber, 6); + assert.equal(viewportData.completelyVisibleStartLineNumber, 6); + assert.equal(viewportData.completelyVisibleEndLineNumber, 6); + assert.deepEqual(viewportData.relativeVerticalOffset, [50]); + + // viewport 50->160 + viewportData = linesLayout.getLinesViewportData(50, 160); + assert.equal(viewportData.startLineNumber, 6); + assert.equal(viewportData.endLineNumber, 6); + assert.equal(viewportData.completelyVisibleStartLineNumber, 6); + assert.equal(viewportData.completelyVisibleEndLineNumber, 6); + assert.deepEqual(viewportData.relativeVerticalOffset, [50]); + + // viewport 51->161 + viewportData = linesLayout.getLinesViewportData(51, 161); + assert.equal(viewportData.startLineNumber, 6); + assert.equal(viewportData.endLineNumber, 7); + assert.equal(viewportData.completelyVisibleStartLineNumber, 7); + assert.equal(viewportData.completelyVisibleEndLineNumber, 7); + assert.deepEqual(viewportData.relativeVerticalOffset, [50, 160]); + + + // viewport 150->169 + viewportData = linesLayout.getLinesViewportData(150, 169); + assert.equal(viewportData.startLineNumber, 7); + assert.equal(viewportData.endLineNumber, 7); + assert.equal(viewportData.completelyVisibleStartLineNumber, 7); + assert.equal(viewportData.completelyVisibleEndLineNumber, 7); + assert.deepEqual(viewportData.relativeVerticalOffset, [160]); + + // viewport 159->169 + viewportData = linesLayout.getLinesViewportData(159, 169); + assert.equal(viewportData.startLineNumber, 7); + assert.equal(viewportData.endLineNumber, 7); + assert.equal(viewportData.completelyVisibleStartLineNumber, 7); + assert.equal(viewportData.completelyVisibleEndLineNumber, 7); + assert.deepEqual(viewportData.relativeVerticalOffset, [160]); + + // viewport 160->169 + viewportData = linesLayout.getLinesViewportData(160, 169); + assert.equal(viewportData.startLineNumber, 7); + assert.equal(viewportData.endLineNumber, 7); + assert.equal(viewportData.completelyVisibleStartLineNumber, 7); + assert.equal(viewportData.completelyVisibleEndLineNumber, 7); + assert.deepEqual(viewportData.relativeVerticalOffset, [160]); + + + // viewport 160->1000 + viewportData = linesLayout.getLinesViewportData(160, 1000); + assert.equal(viewportData.startLineNumber, 7); + assert.equal(viewportData.endLineNumber, 10); + assert.equal(viewportData.completelyVisibleStartLineNumber, 7); + assert.equal(viewportData.completelyVisibleEndLineNumber, 10); + assert.deepEqual(viewportData.relativeVerticalOffset, [160, 170, 180, 190]); + }); + + + test('LinesLayout getLinesViewportData 2 & getWhitespaceViewportData', () => { + var linesLayout = new LinesLayout(10, 10); + var a = linesLayout.insertWhitespace(6, 0, 100); + var b = linesLayout.insertWhitespace(7, 0, 50); + + // 10 lines + // whitespace: - a(6,100), b(7, 50) + assert.equal(linesLayout.getLinesTotalHeight(), 250); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 10); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 20); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 30); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 40); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 50); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 160); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 220); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 230); + assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 240); + + // viewport 50->160 + var viewportData = linesLayout.getLinesViewportData(50, 160); + assert.equal(viewportData.startLineNumber, 6); + assert.equal(viewportData.endLineNumber, 6); + assert.equal(viewportData.completelyVisibleStartLineNumber, 6); + assert.equal(viewportData.completelyVisibleEndLineNumber, 6); + assert.deepEqual(viewportData.relativeVerticalOffset, [50]); + var whitespaceData = linesLayout.getWhitespaceViewportData(50, 160); + assert.deepEqual(whitespaceData, [{ + id: a, + afterLineNumber: 6, + verticalOffset: 60, + height: 100 + }]); + + // viewport 50->219 + viewportData = linesLayout.getLinesViewportData(50, 219); + assert.equal(viewportData.startLineNumber, 6); + assert.equal(viewportData.endLineNumber, 7); + assert.equal(viewportData.completelyVisibleStartLineNumber, 6); + assert.equal(viewportData.completelyVisibleEndLineNumber, 7); + assert.deepEqual(viewportData.relativeVerticalOffset, [50, 160]); + whitespaceData = linesLayout.getWhitespaceViewportData(50, 219); + assert.deepEqual(whitespaceData, [{ + id: a, + afterLineNumber: 6, + verticalOffset: 60, + height: 100 + }, { + id: b, + afterLineNumber: 7, + verticalOffset: 170, + height: 50 + }]); + + // viewport 50->220 + viewportData = linesLayout.getLinesViewportData(50, 220); + assert.equal(viewportData.startLineNumber, 6); + assert.equal(viewportData.endLineNumber, 7); + assert.equal(viewportData.completelyVisibleStartLineNumber, 6); + assert.equal(viewportData.completelyVisibleEndLineNumber, 7); + assert.deepEqual(viewportData.relativeVerticalOffset, [50, 160]); + + // viewport 50->250 + viewportData = linesLayout.getLinesViewportData(50, 250); + assert.equal(viewportData.startLineNumber, 6); + assert.equal(viewportData.endLineNumber, 10); + assert.equal(viewportData.completelyVisibleStartLineNumber, 6); + assert.equal(viewportData.completelyVisibleEndLineNumber, 10); + assert.deepEqual(viewportData.relativeVerticalOffset, [50, 160, 220, 230, 240]); + }); + + test('LinesLayout getWhitespaceAtVerticalOffset', () => { + var linesLayout = new LinesLayout(10, 10); + var a = linesLayout.insertWhitespace(6, 0, 100); + var b = linesLayout.insertWhitespace(7, 0, 50); + + var whitespace = linesLayout.getWhitespaceAtVerticalOffset(0); + assert.equal(whitespace, null); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(59); + assert.equal(whitespace, null); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(60); + assert.equal(whitespace.id, a); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(61); + assert.equal(whitespace.id, a); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(159); + assert.equal(whitespace.id, a); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(160); + assert.equal(whitespace, null); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(161); + assert.equal(whitespace, null); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(169); + assert.equal(whitespace, null); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(170); + assert.equal(whitespace.id, b); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(171); + assert.equal(whitespace.id, b); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(219); + assert.equal(whitespace.id, b); + + whitespace = linesLayout.getWhitespaceAtVerticalOffset(220); + assert.equal(whitespace, null); + }); +}); diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts new file mode 100644 index 0000000000..cf31167898 --- /dev/null +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -0,0 +1,1234 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { renderViewLine2 as renderViewLine, RenderLineInput, CharacterMapping } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; +import { CharCode } from 'vs/base/common/charCode'; +import { MetadataConsts } from 'vs/editor/common/modes'; +import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; + +suite('viewLineRenderer.renderLine', () => { + + function createPart(endIndex: number, foreground: number): ViewLineToken { + return new ViewLineToken(endIndex, ( + foreground << MetadataConsts.FOREGROUND_OFFSET + ) >>> 0); + } + + function assertCharacterReplacement(lineContent: string, tabSize: number, expected: string, expectedCharOffsetInPart: number[][], expectedPartLengts: number[]): void { + let _actual = renderViewLine(new RenderLineInput( + false, + lineContent, + false, + 0, + [new ViewLineToken(lineContent.length, 0)], + [], + tabSize, + 0, + -1, + 'none', + false, + false + )); + + assert.equal(_actual.html, '' + expected + ''); + assertCharacterMapping(_actual.characterMapping, expectedCharOffsetInPart, expectedPartLengts); + } + + test('replaces spaces', () => { + assertCharacterReplacement(' ', 4, '\u00a0', [[0, 1]], [1]); + assertCharacterReplacement(' ', 4, '\u00a0\u00a0', [[0, 1, 2]], [2]); + assertCharacterReplacement('a b', 4, 'a\u00a0\u00a0b', [[0, 1, 2, 3, 4]], [4]); + }); + + test('escapes HTML markup', () => { + assertCharacterReplacement('ab', 4, 'a>b', [[0, 1, 2, 3]], [3]); + assertCharacterReplacement('a&b', 4, 'a&b', [[0, 1, 2, 3]], [3]); + }); + + test('replaces some bad characters', () => { + assertCharacterReplacement('a\0b', 4, 'a�b', [[0, 1, 2, 3]], [3]); + assertCharacterReplacement('a' + String.fromCharCode(CharCode.UTF8_BOM) + 'b', 4, 'a\ufffdb', [[0, 1, 2, 3]], [3]); + assertCharacterReplacement('a\u2028b', 4, 'a\ufffdb', [[0, 1, 2, 3]], [3]); + }); + + test('handles tabs', () => { + assertCharacterReplacement('\t', 4, '\u00a0\u00a0\u00a0\u00a0', [[0, 4]], [4]); + assertCharacterReplacement('x\t', 4, 'x\u00a0\u00a0\u00a0', [[0, 1, 4]], [4]); + assertCharacterReplacement('xx\t', 4, 'xx\u00a0\u00a0', [[0, 1, 2, 4]], [4]); + assertCharacterReplacement('xxx\t', 4, 'xxx\u00a0', [[0, 1, 2, 3, 4]], [4]); + assertCharacterReplacement('xxxx\t', 4, 'xxxx\u00a0\u00a0\u00a0\u00a0', [[0, 1, 2, 3, 4, 8]], [8]); + }); + + function assertParts(lineContent: string, tabSize: number, parts: ViewLineToken[], expected: string, expectedCharOffsetInPart: number[][], expectedPartLengts: number[]): void { + let _actual = renderViewLine(new RenderLineInput( + false, + lineContent, + false, + 0, + parts, + [], + tabSize, + 0, + -1, + 'none', + false, + false + )); + + assert.equal(_actual.html, '' + expected + ''); + assertCharacterMapping(_actual.characterMapping, expectedCharOffsetInPart, expectedPartLengts); + } + + test('empty line', () => { + assertParts('', 4, [], '\u00a0', [], []); + }); + + test('uses part type', () => { + assertParts('x', 4, [createPart(1, 10)], 'x', [[0, 1]], [1]); + assertParts('x', 4, [createPart(1, 20)], 'x', [[0, 1]], [1]); + assertParts('x', 4, [createPart(1, 30)], 'x', [[0, 1]], [1]); + }); + + test('two parts', () => { + assertParts('xy', 4, [createPart(1, 1), createPart(2, 2)], 'xy', [[0], [0, 1]], [1, 1]); + assertParts('xyz', 4, [createPart(1, 1), createPart(3, 2)], 'xyz', [[0], [0, 1, 2]], [1, 2]); + assertParts('xyz', 4, [createPart(2, 1), createPart(3, 2)], 'xyz', [[0, 1], [0, 1]], [2, 1]); + }); + + test('overflow', () => { + let _actual = renderViewLine(new RenderLineInput( + false, + 'Hello world!', + false, + 0, + [ + createPart(1, 0), + createPart(2, 1), + createPart(3, 2), + createPart(4, 3), + createPart(5, 4), + createPart(6, 5), + createPart(7, 6), + createPart(8, 7), + createPart(9, 8), + createPart(10, 9), + createPart(11, 10), + createPart(12, 11), + ], + [], + 4, + 10, + 6, + 'boundary', + false, + false + )); + + let expectedOutput = [ + 'H', + 'e', + 'l', + 'l', + 'o', + '\u00a0', + '' + ].join(''); + + assert.equal(_actual.html, '' + expectedOutput + ''); + assertCharacterMapping(_actual.characterMapping, + [ + [0], + [0], + [0], + [0], + [0], + [0, 1], + ], + [1, 1, 1, 1, 1, 1] + ); + }); + + test('typical line', () => { + let lineText = '\t export class Game { // http://test.com '; + let lineParts = [ + createPart(5, 1), + createPart(11, 2), + createPart(12, 3), + createPart(17, 4), + createPart(18, 5), + createPart(22, 6), + createPart(23, 7), + createPart(24, 8), + createPart(25, 9), + createPart(28, 10), + createPart(43, 11), + createPart(48, 12), + ]; + let expectedOutput = [ + '\u2192\u00a0\u00a0\u00a0', + '\u00b7\u00b7\u00b7\u00b7', + 'export', + '\u00a0', + 'class', + '\u00a0', + 'Game', + '\u00a0', + '{', + '\u00a0', + '//\u00a0', + 'http://test.com', + '\u00b7\u00b7', + '\u00b7\u00b7\u00b7' + ].join(''); + let expectedOffsetsArr = [ + [0], + [0, 1, 2, 3], + [0, 1, 2, 3, 4, 5], + [0], + [0, 1, 2, 3, 4], + [0], + [0, 1, 2, 3], + [0], + [0], + [0], + [0, 1, 2], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [0, 1], + [0, 1, 2, 3], + ]; + + let _actual = renderViewLine(new RenderLineInput( + false, + lineText, + false, + 0, + lineParts, + [], + 4, + 10, + -1, + 'boundary', + false, + false + )); + + assert.equal(_actual.html, '' + expectedOutput + ''); + assertCharacterMapping(_actual.characterMapping, expectedOffsetsArr, [4, 4, 6, 1, 5, 1, 4, 1, 1, 1, 3, 15, 2, 3]); + }); + + test('issue #2255: Weird line rendering part 1', () => { + let lineText = '\t\t\tcursorStyle:\t\t\t\t\t\t(prevOpts.cursorStyle !== newOpts.cursorStyle),'; + + let lineParts = [ + createPart(3, 1), // 3 chars + createPart(15, 2), // 12 chars + createPart(21, 3), // 6 chars + createPart(22, 4), // 1 char + createPart(43, 5), // 21 chars + createPart(45, 6), // 2 chars + createPart(46, 7), // 1 char + createPart(66, 8), // 20 chars + createPart(67, 9), // 1 char + createPart(68, 10), // 2 chars + ]; + let expectedOutput = [ + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + 'cursorStyle:', + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + '(', + 'prevOpts.cursorStyle\u00a0', + '!=', + '=', + '\u00a0newOpts.cursorStyle', + ')', + ',', + ].join(''); + let expectedOffsetsArr = [ + [0, 4, 8], // 3 chars + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], // 12 chars + [0, 4, 8, 12, 16, 20], // 6 chars + [0], // 1 char + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], // 21 chars + [0, 1], // 2 chars + [0], // 1 char + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], // 20 chars + [0], // 1 char + [0, 1] // 2 chars + ]; + + let _actual = renderViewLine(new RenderLineInput( + false, + lineText, + false, + 0, + lineParts, + [], + 4, + 10, + -1, + 'none', + false, + false + )); + + assert.equal(_actual.html, '' + expectedOutput + ''); + assertCharacterMapping(_actual.characterMapping, expectedOffsetsArr, [12, 12, 24, 1, 21, 2, 1, 20, 1, 1]); + }); + + test('issue #2255: Weird line rendering part 2', () => { + let lineText = ' \t\t\tcursorStyle:\t\t\t\t\t\t(prevOpts.cursorStyle !== newOpts.cursorStyle),'; + + let lineParts = [ + createPart(4, 1), // 4 chars + createPart(16, 2), // 12 chars + createPart(22, 3), // 6 chars + createPart(23, 4), // 1 char + createPart(44, 5), // 21 chars + createPart(46, 6), // 2 chars + createPart(47, 7), // 1 char + createPart(67, 8), // 20 chars + createPart(68, 9), // 1 char + createPart(69, 10), // 2 chars + ]; + let expectedOutput = [ + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + 'cursorStyle:', + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + '(', + 'prevOpts.cursorStyle\u00a0', + '!=', + '=', + '\u00a0newOpts.cursorStyle', + ')', + ',', + ].join(''); + let expectedOffsetsArr = [ + [0, 1, 4, 8], // 4 chars + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], // 12 chars + [0, 4, 8, 12, 16, 20], // 6 chars + [0], // 1 char + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], // 21 chars + [0, 1], // 2 chars + [0], // 1 char + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], // 20 chars + [0], // 1 char + [0, 1] // 2 chars + ]; + + let _actual = renderViewLine(new RenderLineInput( + false, + lineText, + false, + 0, + lineParts, + [], + 4, + 10, + -1, + 'none', + false, + false + )); + + assert.equal(_actual.html, '' + expectedOutput + ''); + assertCharacterMapping(_actual.characterMapping, expectedOffsetsArr, [12, 12, 24, 1, 21, 2, 1, 20, 1, 1]); + }); + + test('issue Microsoft/monaco-editor#280: Improved source code rendering for RTL languages', () => { + let lineText = 'var קודמות = \"מיותר קודמות צ\'ט של, אם לשון העברית שינויים ויש, אם\";'; + + let lineParts = [ + createPart(3, 6), + createPart(13, 1), + createPart(66, 20), + createPart(67, 1), + ]; + + let expectedOutput = [ + 'var', + '\u00a0קודמות\u00a0=\u00a0', + '"מיותר\u00a0קודמות\u00a0צ\'ט\u00a0של,\u00a0אם\u00a0לשון\u00a0העברית\u00a0שינויים\u00a0ויש,\u00a0אם"', + ';' + ].join(''); + + let _actual = renderViewLine(new RenderLineInput( + false, + lineText, + true, + 0, + lineParts, + [], + 4, + 10, + -1, + 'none', + false, + false + )); + + assert.equal(_actual.html, '' + expectedOutput + ''); + assert.equal(_actual.containsRTL, true); + }); + + test('issue #6885: Splits large tokens', () => { + // 1 1 1 + // 1 2 3 4 5 6 7 8 9 0 1 2 + // 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 + let _lineText = 'This is just a long line that contains very interesting text. This is just a long line that contains very interesting text.'; + + function assertSplitsTokens(message: string, lineText: string, expectedOutput: string[]): void { + let lineParts = [createPart(lineText.length, 1)]; + let actual = renderViewLine(new RenderLineInput( + false, + lineText, + false, + 0, + lineParts, + [], + 4, + 10, + -1, + 'none', + false, + false + )); + assert.equal(actual.html, '' + expectedOutput.join('') + '', message); + } + + // A token with 49 chars + { + assertSplitsTokens( + '49 chars', + _lineText.substr(0, 49), + [ + 'This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0inter', + ] + ); + } + + // A token with 50 chars + { + assertSplitsTokens( + '50 chars', + _lineText.substr(0, 50), + [ + 'This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0intere', + ] + ); + } + + // A token with 51 chars + { + assertSplitsTokens( + '51 chars', + _lineText.substr(0, 51), + [ + 'This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0intere', + 's', + ] + ); + } + + // A token with 99 chars + { + assertSplitsTokens( + '99 chars', + _lineText.substr(0, 99), + [ + 'This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0intere', + 'sting\u00a0text.\u00a0This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contain', + ] + ); + } + + // A token with 100 chars + { + assertSplitsTokens( + '100 chars', + _lineText.substr(0, 100), + [ + 'This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0intere', + 'sting\u00a0text.\u00a0This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains', + ] + ); + } + + // A token with 101 chars + { + assertSplitsTokens( + '101 chars', + _lineText.substr(0, 101), + [ + 'This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0intere', + 'sting\u00a0text.\u00a0This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains', + '\u00a0', + ] + ); + } + }); + + test('issue #21476: Does not split large tokens when ligatures are on', () => { + // 1 1 1 + // 1 2 3 4 5 6 7 8 9 0 1 2 + // 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 + let _lineText = 'This is just a long line that contains very interesting text. This is just a long line that contains very interesting text.'; + + function assertSplitsTokens(message: string, lineText: string, expectedOutput: string[]): void { + let lineParts = [createPart(lineText.length, 1)]; + let actual = renderViewLine(new RenderLineInput( + false, + lineText, + false, + 0, + lineParts, + [], + 4, + 10, + -1, + 'none', + false, + true + )); + assert.equal(actual.html, '' + expectedOutput.join('') + '', message); + } + + // A token with 101 chars + { + assertSplitsTokens( + '101 chars', + _lineText.substr(0, 101), + [ + 'This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0very\u00a0interesting\u00a0text.\u00a0This\u00a0is\u00a0just\u00a0a\u00a0long\u00a0line\u00a0that\u00a0contains\u00a0', + ] + ); + } + }); + + test('issue #20624: Unaligned surrogate pairs are corrupted at multiples of 50 columns', () => { + let lineText = 'a𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷'; + + let lineParts = [createPart(lineText.length, 1)]; + let actual = renderViewLine(new RenderLineInput( + false, + lineText, + false, + 0, + lineParts, + [], + 4, + 10, + -1, + 'none', + false, + false + )); + let expectedOutput = [ + 'a𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷', + '𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷', + '𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷', + '𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷', + '𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷', + ]; + assert.equal(actual.html, '' + expectedOutput.join('') + ''); + }); + + test('issue #6885: Does not split large tokens in RTL text', () => { + let lineText = 'את גרמנית בהתייחסות שמו, שנתי המשפט אל חפש, אם כתב אחרים ולחבר. של התוכן אודות בויקיפדיה כלל, של עזרה כימיה היא. על עמוד יוצרים מיתולוגיה סדר, אם שכל שתפו לעברית שינויים, אם שאלות אנגלית עזה. שמות בקלות מה סדר.'; + let lineParts = [createPart(lineText.length, 1)]; + let expectedOutput = [ + 'את\u00a0גרמנית\u00a0בהתייחסות\u00a0שמו,\u00a0שנתי\u00a0המשפט\u00a0אל\u00a0חפש,\u00a0אם\u00a0כתב\u00a0אחרים\u00a0ולחבר.\u00a0של\u00a0התוכן\u00a0אודות\u00a0בויקיפדיה\u00a0כלל,\u00a0של\u00a0עזרה\u00a0כימיה\u00a0היא.\u00a0על\u00a0עמוד\u00a0יוצרים\u00a0מיתולוגיה\u00a0סדר,\u00a0אם\u00a0שכל\u00a0שתפו\u00a0לעברית\u00a0שינויים,\u00a0אם\u00a0שאלות\u00a0אנגלית\u00a0עזה.\u00a0שמות\u00a0בקלות\u00a0מה\u00a0סדר.' + ]; + let actual = renderViewLine(new RenderLineInput( + false, + lineText, + true, + 0, + lineParts, + [], + 4, + 10, + -1, + 'none', + false, + false + )); + assert.equal(actual.html, '' + expectedOutput.join('') + ''); + assert.equal(actual.containsRTL, true); + }); + + test('issue #19673: Monokai Theme bad-highlighting in line wrap', () => { + let lineText = ' MongoCallback): void {'; + + let lineParts = [ + createPart(17, 1), + createPart(18, 2), + createPart(24, 3), + createPart(26, 4), + createPart(27, 5), + createPart(28, 6), + createPart(32, 7), + createPart(34, 8), + ]; + let expectedOutput = [ + '\u00a0\u00a0\u00a0\u00a0', + 'MongoCallback', + '<', + 'string', + '>)', + ':', + '\u00a0', + 'void', + '\u00a0{' + ].join(''); + + let _actual = renderViewLine(new RenderLineInput( + true, + lineText, + false, + 4, + lineParts, + [], + 4, + 10, + -1, + 'none', + false, + false + )); + + assert.equal(_actual.html, '' + expectedOutput + ''); + }); + + function assertCharacterMapping(actual: CharacterMapping, expectedCharPartOffsets: number[][], expectedPartLengths: number[]): void { + + assertCharPartOffsets(actual, expectedCharPartOffsets); + + let expectedCharAbsoluteOffset: number[] = [], currentPartAbsoluteOffset = 0; + for (let partIndex = 0; partIndex < expectedCharPartOffsets.length; partIndex++) { + const part = expectedCharPartOffsets[partIndex]; + + for (let i = 0; i < part.length; i++) { + const charIndex = part[i]; + expectedCharAbsoluteOffset.push(currentPartAbsoluteOffset + charIndex); + } + + currentPartAbsoluteOffset += expectedPartLengths[partIndex]; + } + + let actualCharOffset: number[] = []; + let tmp = actual.getAbsoluteOffsets(); + for (let i = 0; i < tmp.length; i++) { + actualCharOffset[i] = tmp[i]; + } + assert.deepEqual(actualCharOffset, expectedCharAbsoluteOffset); + } + + function assertCharPartOffsets(actual: CharacterMapping, expected: number[][]): void { + + let charOffset = 0; + for (let partIndex = 0; partIndex < expected.length; partIndex++) { + let part = expected[partIndex]; + for (let i = 0; i < part.length; i++) { + let charIndex = part[i]; + // here + let _actualPartData = actual.charOffsetToPartData(charOffset); + let actualPartIndex = CharacterMapping.getPartIndex(_actualPartData); + let actualCharIndex = CharacterMapping.getCharIndex(_actualPartData); + + assert.deepEqual( + { partIndex: actualPartIndex, charIndex: actualCharIndex }, + { partIndex: partIndex, charIndex: charIndex }, + `character mapping for offset ${charOffset}` + ); + + // here + let actualOffset = actual.partDataToCharOffset(partIndex, part[part.length - 1] + 1, charIndex); + + assert.equal( + actualOffset, + charOffset, + `character mapping for part ${partIndex}, ${charIndex}` + ); + + charOffset++; + } + } + + assert.equal(actual.length, charOffset); + } +}); + +suite('viewLineRenderer.renderLine 2', () => { + function createPart(endIndex: number, foreground: number): ViewLineToken { + return new ViewLineToken(endIndex, ( + foreground << MetadataConsts.FOREGROUND_OFFSET + ) >>> 0); + } + + function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'all', expected: string): void { + let actual = renderViewLine(new RenderLineInput( + fontIsMonospace, + lineContent, + false, + fauxIndentLength, + tokens, + [], + 4, + 10, + -1, + renderWhitespace, + false, + false + )); + + assert.deepEqual(actual.html, expected); + } + + test('issue #18616: Inline decorations ending at the text length are no longer rendered', () => { + + let lineContent = 'https://microsoft.com'; + + let actual = renderViewLine(new RenderLineInput( + false, + lineContent, + false, + 0, + [createPart(21, 3)], + [new LineDecoration(1, 22, 'link', false)], + 4, + 10, + -1, + 'none', + false, + false + )); + + let expected = [ + '', + 'https://microsoft.com', + '' + ].join(''); + + assert.deepEqual(actual.html, expected); + }); + + test('issue #19207: Link in Monokai is not rendered correctly', () => { + + let lineContent = '\'let url = `http://***/_api/web/lists/GetByTitle(\\\'Teambuildingaanvragen\\\')/items`;\''; + + let actual = renderViewLine(new RenderLineInput( + true, + lineContent, + false, + 0, + [ + createPart(49, 6), + createPart(51, 4), + createPart(72, 6), + createPart(74, 4), + createPart(84, 6), + ], + [ + new LineDecoration(13, 51, 'detected-link', false) + ], + 4, + 10, + -1, + 'none', + false, + false + )); + + let expected = [ + '', + '\'let\u00a0url\u00a0=\u00a0`', + 'http://***/_api/web/lists/GetByTitle(', + '\\', + '\'', + 'Teambuildingaanvragen', + '\\\'', + ')/items`;\'', + '' + ].join(''); + + assert.deepEqual(actual.html, expected); + }); + + test('createLineParts simple', () => { + testCreateLineParts( + false, + 'Hello world!', + [ + createPart(12, 1) + ], + 0, + 'none', + [ + '', + 'Hello\u00a0world!', + '', + ].join('') + ); + }); + test('createLineParts simple two tokens', () => { + testCreateLineParts( + false, + 'Hello world!', + [ + createPart(6, 1), + createPart(12, 2) + ], + 0, + 'none', + [ + '', + 'Hello\u00a0', + 'world!', + '', + ].join('') + ); + }); + test('createLineParts render whitespace - 4 leading spaces', () => { + testCreateLineParts( + false, + ' Hello world! ', + [ + createPart(4, 1), + createPart(6, 2), + createPart(20, 3) + ], + 0, + 'boundary', + [ + '', + '\u00b7\u00b7\u00b7\u00b7', + 'He', + 'llo\u00a0world!', + '\u00b7\u00b7\u00b7\u00b7', + '', + ].join('') + ); + }); + test('createLineParts render whitespace - 8 leading spaces', () => { + testCreateLineParts( + false, + ' Hello world! ', + [ + createPart(8, 1), + createPart(10, 2), + createPart(28, 3) + ], + 0, + 'boundary', + [ + '', + '\u00b7\u00b7\u00b7\u00b7', + '\u00b7\u00b7\u00b7\u00b7', + 'He', + 'llo\u00a0world!', + '\u00b7\u00b7\u00b7\u00b7', + '\u00b7\u00b7\u00b7\u00b7', + '', + ].join('') + ); + }); + test('createLineParts render whitespace - 2 leading tabs', () => { + testCreateLineParts( + false, + '\t\tHello world!\t', + [ + createPart(2, 1), + createPart(4, 2), + createPart(15, 3) + ], + 0, + 'boundary', + [ + '', + '\u2192\u00a0\u00a0\u00a0', + '\u2192\u00a0\u00a0\u00a0', + 'He', + 'llo\u00a0world!', + '\u2192\u00a0\u00a0\u00a0', + '', + ].join('') + ); + }); + test('createLineParts render whitespace - mixed leading spaces and tabs', () => { + testCreateLineParts( + false, + ' \t\t Hello world! \t \t \t ', + [ + createPart(6, 1), + createPart(8, 2), + createPart(31, 3) + ], + 0, + 'boundary', + [ + '', + '\u00b7\u00b7\u2192\u00a0', + '\u2192\u00a0\u00a0\u00a0', + '\u00b7\u00b7', + 'He', + 'llo\u00a0world!', + '\u00b7\u2192', + '\u00b7\u00b7\u2192\u00a0', + '\u00b7\u00b7\u00b7\u2192', + '\u00b7\u00b7\u00b7\u00b7', + '', + ].join('') + ); + }); + + test('createLineParts render whitespace skips faux indent', () => { + testCreateLineParts( + false, + '\t\t Hello world! \t \t \t ', + [ + createPart(4, 1), + createPart(6, 2), + createPart(29, 3) + ], + 2, + 'boundary', + [ + '', + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + '\u00b7\u00b7', + 'He', + 'llo\u00a0world!', + '\u00b7\u2192', + '\u00b7\u00b7\u2192\u00a0', + '\u00b7\u00b7\u00b7\u2192', + '\u00b7\u00b7\u00b7\u00b7', + '', + ].join('') + ); + }); + + test('createLineParts does not emit width for monospace fonts', () => { + testCreateLineParts( + true, + '\t\t Hello world! \t \t \t ', + [ + createPart(4, 1), + createPart(6, 2), + createPart(29, 3) + ], + 2, + 'boundary', + [ + '', + '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0', + '\u00b7\u00b7', + 'He', + 'llo\u00a0world!', + '\u00b7\u2192\u00b7\u00b7\u2192\u00a0\u00b7\u00b7\u00b7\u2192\u00b7\u00b7\u00b7\u00b7', + '', + ].join('') + ); + }); + + test('createLineParts render whitespace in middle but not for one space', () => { + testCreateLineParts( + false, + 'it it it it', + [ + createPart(6, 1), + createPart(7, 2), + createPart(13, 3) + ], + 0, + 'boundary', + [ + '', + 'it', + '\u00b7\u00b7', + 'it', + '\u00a0', + 'it', + '\u00b7\u00b7', + 'it', + '', + ].join('') + ); + }); + + test('createLineParts render whitespace for all in middle', () => { + testCreateLineParts( + false, + ' Hello world!\t', + [ + createPart(4, 0), + createPart(6, 1), + createPart(14, 2) + ], + 0, + 'all', + [ + '', + '\u00b7', + 'Hel', + 'lo', + '\u00b7', + 'world!', + '\u2192\u00a0\u00a0', + '', + ].join('') + ); + }); + + test('createLineParts can handle unsorted inline decorations', () => { + let actual = renderViewLine(new RenderLineInput( + false, + 'Hello world', + false, + 0, + [createPart(11, 0)], + [ + new LineDecoration(5, 7, 'a', false), + new LineDecoration(1, 3, 'b', false), + new LineDecoration(2, 8, 'c', false), + ], + 4, + 10, + -1, + 'none', + false, + false + )); + + // 01234567890 + // Hello world + // ----aa----- + // bb--------- + // -cccccc---- + + assert.deepEqual(actual.html, [ + '', + 'H', + 'e', + 'll', + 'o\u00a0', + 'w', + 'orld', + '', + ].join('')); + }); + + test('issue #11485: Visible whitespace conflicts with before decorator attachment', () => { + + let lineContent = '\tbla'; + + let actual = renderViewLine(new RenderLineInput( + false, + lineContent, + false, + 0, + [createPart(4, 3)], + [new LineDecoration(1, 2, 'before', true)], + 4, + 10, + -1, + 'all', + false, + true + )); + + let expected = [ + '', + '\u2192\u00a0\u00a0\u00a0', + 'bla', + '' + ].join(''); + + assert.deepEqual(actual.html, expected); + }); + + test('issue #30133: Empty lines don\'t render inline decorations', () => { + + let lineContent = ''; + + let actual = renderViewLine(new RenderLineInput( + false, + lineContent, + false, + 0, + [createPart(0, 3)], + [new LineDecoration(1, 2, 'before', true)], + 4, + 10, + -1, + 'all', + false, + true + )); + + let expected = [ + '', + '\u00a0', + '' + ].join(''); + + assert.deepEqual(actual.html, expected); + }); + + function createTestGetColumnOfLinePartOffset(lineContent: string, tabSize: number, parts: ViewLineToken[], expectedPartLengths: number[]): (partIndex: number, partLength: number, offset: number, expected: number) => void { + let renderLineOutput = renderViewLine(new RenderLineInput( + false, + lineContent, + false, + 0, + parts, + [], + tabSize, + 10, + -1, + 'none', + false, + false + )); + + return (partIndex: number, partLength: number, offset: number, expected: number) => { + let charOffset = renderLineOutput.characterMapping.partDataToCharOffset(partIndex, partLength, offset); + let actual = charOffset + 1; + assert.equal(actual, expected, 'getColumnOfLinePartOffset for ' + partIndex + ' @ ' + offset); + }; + } + + test('getColumnOfLinePartOffset 1 - simple text', () => { + let testGetColumnOfLinePartOffset = createTestGetColumnOfLinePartOffset( + 'hello world', + 4, + [ + createPart(11, 1) + ], + [11] + ); + testGetColumnOfLinePartOffset(0, 11, 0, 1); + testGetColumnOfLinePartOffset(0, 11, 1, 2); + testGetColumnOfLinePartOffset(0, 11, 2, 3); + testGetColumnOfLinePartOffset(0, 11, 3, 4); + testGetColumnOfLinePartOffset(0, 11, 4, 5); + testGetColumnOfLinePartOffset(0, 11, 5, 6); + testGetColumnOfLinePartOffset(0, 11, 6, 7); + testGetColumnOfLinePartOffset(0, 11, 7, 8); + testGetColumnOfLinePartOffset(0, 11, 8, 9); + testGetColumnOfLinePartOffset(0, 11, 9, 10); + testGetColumnOfLinePartOffset(0, 11, 10, 11); + testGetColumnOfLinePartOffset(0, 11, 11, 12); + }); + + test('getColumnOfLinePartOffset 2 - regular JS', () => { + let testGetColumnOfLinePartOffset = createTestGetColumnOfLinePartOffset( + 'var x = 3;', + 4, + [ + createPart(3, 1), + createPart(4, 2), + createPart(5, 3), + createPart(8, 4), + createPart(9, 5), + createPart(10, 6), + ], + [3, 1, 1, 3, 1, 1] + ); + testGetColumnOfLinePartOffset(0, 3, 0, 1); + testGetColumnOfLinePartOffset(0, 3, 1, 2); + testGetColumnOfLinePartOffset(0, 3, 2, 3); + testGetColumnOfLinePartOffset(0, 3, 3, 4); + testGetColumnOfLinePartOffset(1, 1, 0, 4); + testGetColumnOfLinePartOffset(1, 1, 1, 5); + testGetColumnOfLinePartOffset(2, 1, 0, 5); + testGetColumnOfLinePartOffset(2, 1, 1, 6); + testGetColumnOfLinePartOffset(3, 3, 0, 6); + testGetColumnOfLinePartOffset(3, 3, 1, 7); + testGetColumnOfLinePartOffset(3, 3, 2, 8); + testGetColumnOfLinePartOffset(3, 3, 3, 9); + testGetColumnOfLinePartOffset(4, 1, 0, 9); + testGetColumnOfLinePartOffset(4, 1, 1, 10); + testGetColumnOfLinePartOffset(5, 1, 0, 10); + testGetColumnOfLinePartOffset(5, 1, 1, 11); + }); + + test('getColumnOfLinePartOffset 3 - tab with tab size 6', () => { + let testGetColumnOfLinePartOffset = createTestGetColumnOfLinePartOffset( + '\t', + 6, + [ + createPart(1, 1) + ], + [6] + ); + testGetColumnOfLinePartOffset(0, 6, 0, 1); + testGetColumnOfLinePartOffset(0, 6, 1, 1); + testGetColumnOfLinePartOffset(0, 6, 2, 1); + testGetColumnOfLinePartOffset(0, 6, 3, 1); + testGetColumnOfLinePartOffset(0, 6, 4, 2); + testGetColumnOfLinePartOffset(0, 6, 5, 2); + testGetColumnOfLinePartOffset(0, 6, 6, 2); + }); + + test('getColumnOfLinePartOffset 4 - once indented line, tab size 4', () => { + let testGetColumnOfLinePartOffset = createTestGetColumnOfLinePartOffset( + '\tfunction', + 4, + [ + createPart(1, 1), + createPart(9, 2), + ], + [4, 8] + ); + testGetColumnOfLinePartOffset(0, 4, 0, 1); + testGetColumnOfLinePartOffset(0, 4, 1, 1); + testGetColumnOfLinePartOffset(0, 4, 2, 1); + testGetColumnOfLinePartOffset(0, 4, 3, 2); + testGetColumnOfLinePartOffset(0, 4, 4, 2); + testGetColumnOfLinePartOffset(1, 8, 0, 2); + testGetColumnOfLinePartOffset(1, 8, 1, 3); + testGetColumnOfLinePartOffset(1, 8, 2, 4); + testGetColumnOfLinePartOffset(1, 8, 3, 5); + testGetColumnOfLinePartOffset(1, 8, 4, 6); + testGetColumnOfLinePartOffset(1, 8, 5, 7); + testGetColumnOfLinePartOffset(1, 8, 6, 8); + testGetColumnOfLinePartOffset(1, 8, 7, 9); + testGetColumnOfLinePartOffset(1, 8, 8, 10); + }); + + test('getColumnOfLinePartOffset 5 - twice indented line, tab size 4', () => { + let testGetColumnOfLinePartOffset = createTestGetColumnOfLinePartOffset( + '\t\tfunction', + 4, + [ + createPart(2, 1), + createPart(10, 2), + ], + [8, 8] + ); + testGetColumnOfLinePartOffset(0, 8, 0, 1); + testGetColumnOfLinePartOffset(0, 8, 1, 1); + testGetColumnOfLinePartOffset(0, 8, 2, 1); + testGetColumnOfLinePartOffset(0, 8, 3, 2); + testGetColumnOfLinePartOffset(0, 8, 4, 2); + testGetColumnOfLinePartOffset(0, 8, 5, 2); + testGetColumnOfLinePartOffset(0, 8, 6, 2); + testGetColumnOfLinePartOffset(0, 8, 7, 3); + testGetColumnOfLinePartOffset(0, 8, 8, 3); + testGetColumnOfLinePartOffset(1, 8, 0, 3); + testGetColumnOfLinePartOffset(1, 8, 1, 4); + testGetColumnOfLinePartOffset(1, 8, 2, 5); + testGetColumnOfLinePartOffset(1, 8, 3, 6); + testGetColumnOfLinePartOffset(1, 8, 4, 7); + testGetColumnOfLinePartOffset(1, 8, 5, 8); + testGetColumnOfLinePartOffset(1, 8, 6, 9); + testGetColumnOfLinePartOffset(1, 8, 7, 10); + testGetColumnOfLinePartOffset(1, 8, 8, 11); + }); +}); diff --git a/src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts b/src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts new file mode 100644 index 0000000000..22f5f9a884 --- /dev/null +++ b/src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts @@ -0,0 +1,560 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { WhitespaceComputer } from 'vs/editor/common/viewLayout/whitespaceComputer'; + +suite('Editor ViewLayout - WhitespaceComputer', () => { + + test('WhitespaceComputer', () => { + + var whitespaceComputer = new WhitespaceComputer(); + + // Insert a whitespace after line number 2, of height 10 + var a = whitespaceComputer.insertWhitespace(2, 0, 10); + // whitespaces: a(2, 10) + assert.equal(whitespaceComputer.getCount(), 1); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10); + assert.equal(whitespaceComputer.getTotalHeight(), 10); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 10); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 10); + + // Insert a whitespace again after line number 2, of height 20 + var b = whitespaceComputer.insertWhitespace(2, 0, 20); + // whitespaces: a(2, 10), b(2, 20) + assert.equal(whitespaceComputer.getCount(), 2); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 30); + assert.equal(whitespaceComputer.getTotalHeight(), 30); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 30); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30); + + // Change last inserted whitespace height to 30 + whitespaceComputer.changeWhitespaceHeight(b, 30); + // whitespaces: a(2, 10), b(2, 30) + assert.equal(whitespaceComputer.getCount(), 2); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 30); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 40); + assert.equal(whitespaceComputer.getTotalHeight(), 40); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 40); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 40); + + // Remove last inserted whitespace + whitespaceComputer.removeWhitespace(b); + // whitespaces: a(2, 10) + assert.equal(whitespaceComputer.getCount(), 1); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10); + assert.equal(whitespaceComputer.getTotalHeight(), 10); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 10); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 10); + + // Add a whitespace before the first line of height 50 + b = whitespaceComputer.insertWhitespace(0, 0, 50); + // whitespaces: b(0, 50), a(2, 10) + assert.equal(whitespaceComputer.getCount(), 2); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60); + assert.equal(whitespaceComputer.getTotalHeight(), 60); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 60); + + // Add a whitespace after line 4 of height 20 + whitespaceComputer.insertWhitespace(4, 0, 20); + // whitespaces: b(0, 50), a(2, 10), c(4, 20) + assert.equal(whitespaceComputer.getCount(), 3); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 4); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 20); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60); + assert.equal(whitespaceComputer.getAccumulatedHeight(2), 80); + assert.equal(whitespaceComputer.getTotalHeight(), 80); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 60); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 80); + + // Add a whitespace after line 3 of height 30 + whitespaceComputer.insertWhitespace(3, 0, 30); + // whitespaces: b(0, 50), a(2, 10), d(3, 30), c(4, 20) + assert.equal(whitespaceComputer.getCount(), 4); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 3); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 30); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(3), 4); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(3), 20); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60); + assert.equal(whitespaceComputer.getAccumulatedHeight(2), 90); + assert.equal(whitespaceComputer.getAccumulatedHeight(3), 110); + assert.equal(whitespaceComputer.getTotalHeight(), 110); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 90); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 110); + + // Change whitespace after line 2 to height of 100 + whitespaceComputer.changeWhitespaceHeight(a, 100); + // whitespaces: b(0, 50), a(2, 100), d(3, 30), c(4, 20) + assert.equal(whitespaceComputer.getCount(), 4); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 100); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 3); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 30); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(3), 4); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(3), 20); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 150); + assert.equal(whitespaceComputer.getAccumulatedHeight(2), 180); + assert.equal(whitespaceComputer.getAccumulatedHeight(3), 200); + assert.equal(whitespaceComputer.getTotalHeight(), 200); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 150); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 180); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 200); + + // Remove whitespace after line 2 + whitespaceComputer.removeWhitespace(a); + // whitespaces: b(0, 50), d(3, 30), c(4, 20) + assert.equal(whitespaceComputer.getCount(), 3); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 30); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 4); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 20); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 80); + assert.equal(whitespaceComputer.getAccumulatedHeight(2), 100); + assert.equal(whitespaceComputer.getTotalHeight(), 100); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 80); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 100); + + // Remove whitespace before line 1 + whitespaceComputer.removeWhitespace(b); + // whitespaces: d(3, 30), c(4, 20) + assert.equal(whitespaceComputer.getCount(), 2); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 4); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50); + assert.equal(whitespaceComputer.getTotalHeight(), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50); + + // Delete line 1 + whitespaceComputer.onLinesDeleted(1, 1); + // whitespaces: d(2, 30), c(3, 20) + assert.equal(whitespaceComputer.getCount(), 2); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50); + assert.equal(whitespaceComputer.getTotalHeight(), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 30); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50); + + // Insert a line before line 1 + whitespaceComputer.onLinesInserted(1, 1); + // whitespaces: d(3, 30), c(4, 20) + assert.equal(whitespaceComputer.getCount(), 2); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 4); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50); + assert.equal(whitespaceComputer.getTotalHeight(), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50); + + // Delete line 4 + whitespaceComputer.onLinesDeleted(4, 4); + // whitespaces: d(3, 30), c(3, 20) + assert.equal(whitespaceComputer.getCount(), 2); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30); + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20); + assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30); + assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50); + assert.equal(whitespaceComputer.getTotalHeight(), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 50); + assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50); + }); + + test('WhitespaceComputer findInsertionIndex', () => { + + var makeArray = (size: number, fillValue: number) => { + var r: number[] = []; + for (var i = 0; i < size; i++) { + r[i] = fillValue; + } + return r; + }; + + var arr: number[]; + var ordinals: number[]; + + arr = []; + ordinals = makeArray(arr.length, 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 0); + + arr = [1]; + ordinals = makeArray(arr.length, 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); + + arr = [1, 3]; + ordinals = makeArray(arr.length, 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); + + arr = [1, 3, 5]; + ordinals = makeArray(arr.length, 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); + + arr = [1, 3, 5]; + ordinals = makeArray(arr.length, 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); + + arr = [1, 3, 5, 7]; + ordinals = makeArray(arr.length, 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4); + + arr = [1, 3, 5, 7, 9]; + ordinals = makeArray(arr.length, 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5); + + arr = [1, 3, 5, 7, 9, 11]; + ordinals = makeArray(arr.length, 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6); + + arr = [1, 3, 5, 7, 9, 11, 13]; + ordinals = makeArray(arr.length, 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 13, ordinals, 0), 7); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 14, ordinals, 0), 7); + + arr = [1, 3, 5, 7, 9, 11, 13, 15]; + ordinals = makeArray(arr.length, 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 13, ordinals, 0), 7); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 14, ordinals, 0), 7); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 15, ordinals, 0), 8); + assert.equal(WhitespaceComputer.findInsertionIndex(arr, 16, ordinals, 0), 8); + }); + + test('WhitespaceComputer changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => { + var whitespaceComputer = new WhitespaceComputer(); + + var a = whitespaceComputer.insertWhitespace(0, 0, 1); + var b = whitespaceComputer.insertWhitespace(7, 0, 1); + var c = whitespaceComputer.insertWhitespace(3, 0, 1); + + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 1); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + + // Do not really move a + whitespaceComputer.changeWhitespaceAfterLineNumber(a, 1); + + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 1 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 1); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + + + // Do not really move a + whitespaceComputer.changeWhitespaceAfterLineNumber(a, 2); + + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 2 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + + + // Change a to conflict with c => a gets placed after c + whitespaceComputer.changeWhitespaceAfterLineNumber(a, 3); + + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), c); // 3 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), a); // 3 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + + + // Make a no-op + whitespaceComputer.changeWhitespaceAfterLineNumber(c, 3); + + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), c); // 3 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), a); // 3 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + + + + // Conflict c with b => c gets placed after b + whitespaceComputer.changeWhitespaceAfterLineNumber(c, 7); + + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 3 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), b); // 7 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 7); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 7 + assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // a + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 1); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 1); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 1); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 1); // b + assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + }); + + + test('WhitespaceComputer Bug', () => { + var whitespaceComputer = new WhitespaceComputer(); + + var a = whitespaceComputer.insertWhitespace(0, 0, 1); + var b = whitespaceComputer.insertWhitespace(7, 0, 1); + + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), b); // 7 + + var c = whitespaceComputer.insertWhitespace(3, 0, 1); + + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 + + var d = whitespaceComputer.insertWhitespace(2, 0, 1); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7 + + var e = whitespaceComputer.insertWhitespace(8, 0, 1); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8 + + var f = whitespaceComputer.insertWhitespace(11, 0, 1); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), f); // 11 + + var g = whitespaceComputer.insertWhitespace(10, 0, 1); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), g); // 10 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(6), f); // 11 + + var h = whitespaceComputer.insertWhitespace(0, 0, 1); + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), h); // 0 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), d); // 2 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), c); // 3 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), b); // 7 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), e); // 8 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(6), g); // 10 + assert.equal(whitespaceComputer.getIdForWhitespaceIndex(7), f); // 11 + }); +}); + diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts new file mode 100644 index 0000000000..21924c1486 --- /dev/null +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; +import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { ILineMapperFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; + +function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None) { + + let rawText = ''; + let currentLineIndex = 0; + let lineIndices: number[] = []; + for (let i = 0, len = annotatedText.length; i < len; i++) { + if (annotatedText.charAt(i) === '|') { + currentLineIndex++; + } else { + rawText += annotatedText.charAt(i); + lineIndices[rawText.length - 1] = currentLineIndex; + } + } + + let mapper = factory.createLineMapping(rawText, tabSize, breakAfter, 2, wrappingIndent); + + let actualAnnotatedText = ''; + if (mapper) { + let previousLineIndex = 0; + for (let i = 0, len = rawText.length; i < len; i++) { + let r = mapper.getOutputPositionOfInputOffset(i); + if (previousLineIndex !== r.outputLineIndex) { + previousLineIndex = r.outputLineIndex; + actualAnnotatedText += '|'; + } + actualAnnotatedText += rawText.charAt(i); + } + } else { + // No wrapping + actualAnnotatedText = rawText; + } + + assert.equal(actualAnnotatedText, annotatedText); +} + +suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { + test('CharacterHardWrappingLineMapper', () => { + + let factory = new CharacterHardWrappingLineMapperFactory('(', ')', '.'); + + // Empty string + assertLineMapping(factory, 4, 5, ''); + + // No wrapping if not necessary + assertLineMapping(factory, 4, 5, 'aaa'); + assertLineMapping(factory, 4, 5, 'aaaaa'); + assertLineMapping(factory, 4, -1, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); + + // Acts like hard wrapping if no char found + assertLineMapping(factory, 4, 5, 'aaaaa|a'); + + // Honors obtrusive wrapping character + assertLineMapping(factory, 4, 5, 'aaaaa|.'); + assertLineMapping(factory, 4, 5, 'aaaaa|a.|aaa.|aa'); + assertLineMapping(factory, 4, 5, 'aaaaa|a..|aaa.|aa'); + assertLineMapping(factory, 4, 5, 'aaaaa|a...|aaa.|aa'); + assertLineMapping(factory, 4, 5, 'aaaaa|a....|aaa.|aa'); + + // Honors tabs when computing wrapping position + assertLineMapping(factory, 4, 5, '\t'); + assertLineMapping(factory, 4, 5, '\ta|aa'); + assertLineMapping(factory, 4, 5, '\ta|\ta|a'); + assertLineMapping(factory, 4, 5, 'aa\ta'); + assertLineMapping(factory, 4, 5, 'aa\ta|a'); + + // Honors wrapping before characters (& gives it priority) + assertLineMapping(factory, 4, 5, 'aaa.|aa'); + assertLineMapping(factory, 4, 5, 'aaa|(.aa'); + + // Honors wrapping after characters (& gives it priority) + assertLineMapping(factory, 4, 5, 'aaa))|).aaa'); + assertLineMapping(factory, 4, 5, 'aaa))|)|.aaaa'); + assertLineMapping(factory, 4, 5, 'aaa)|()|.aaa'); + assertLineMapping(factory, 4, 5, 'aaa(|()|.aaa'); + assertLineMapping(factory, 4, 5, 'aa.(|()|.aaa'); + assertLineMapping(factory, 4, 5, 'aa.|(.)|.aaa'); + }); + + test('CharacterHardWrappingLineMapper - CJK and Kinsoku Shori', () => { + let factory = new CharacterHardWrappingLineMapperFactory('(', ')', '.'); + assertLineMapping(factory, 4, 5, 'aa \u5b89|\u5b89'); + assertLineMapping(factory, 4, 5, '\u3042 \u5b89|\u5b89'); + assertLineMapping(factory, 4, 5, '\u3042\u3042|\u5b89\u5b89'); + assertLineMapping(factory, 4, 5, 'aa |\u5b89)\u5b89|\u5b89'); + assertLineMapping(factory, 4, 5, 'aa \u3042|\u5b89\u3042)|\u5b89'); + assertLineMapping(factory, 4, 5, 'aa |(\u5b89aa|\u5b89'); + }); + + test('CharacterHardWrappingLineMapper - WrappingIndent.Same', () => { + let factory = new CharacterHardWrappingLineMapperFactory('', ' ', ''); + assertLineMapping(factory, 4, 38, ' *123456789012345678901234567890123456|7890', WrappingIndent.Same); + }); + + test('issue #16332: Scroll bar overlaying on top of text', () => { + let factory = new CharacterHardWrappingLineMapperFactory('', ' ', ''); + assertLineMapping(factory, 4, 24, 'a/ very/long/line/of/tex|t/that/expands/beyon|d/your/typical/line/|of/code/', WrappingIndent.Indent); + }); +}); diff --git a/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts b/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts new file mode 100644 index 0000000000..35473abd66 --- /dev/null +++ b/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts @@ -0,0 +1,171 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { PrefixSumComputer, PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer'; +import { toUint32Array } from 'vs/editor/common/core/uint'; + +suite('Editor ViewModel - PrefixSumComputer', () => { + + test('PrefixSumComputer', () => { + let indexOfResult: PrefixSumIndexOfResult; + + var psc = new PrefixSumComputer(toUint32Array([1, 1, 2, 1, 3])); + assert.equal(psc.getTotalValue(), 8); + assert.equal(psc.getAccumulatedValue(-1), 0); + assert.equal(psc.getAccumulatedValue(0), 1); + assert.equal(psc.getAccumulatedValue(1), 2); + assert.equal(psc.getAccumulatedValue(2), 4); + assert.equal(psc.getAccumulatedValue(3), 5); + assert.equal(psc.getAccumulatedValue(4), 8); + indexOfResult = psc.getIndexOf(0); + assert.equal(indexOfResult.index, 0); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(1); + assert.equal(indexOfResult.index, 1); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(2); + assert.equal(indexOfResult.index, 2); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(3); + assert.equal(indexOfResult.index, 2); + assert.equal(indexOfResult.remainder, 1); + indexOfResult = psc.getIndexOf(4); + assert.equal(indexOfResult.index, 3); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(5); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(6); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 1); + indexOfResult = psc.getIndexOf(7); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 2); + indexOfResult = psc.getIndexOf(8); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 3); + + // [1, 2, 2, 1, 3] + psc.changeValue(1, 2); + assert.equal(psc.getTotalValue(), 9); + assert.equal(psc.getAccumulatedValue(0), 1); + assert.equal(psc.getAccumulatedValue(1), 3); + assert.equal(psc.getAccumulatedValue(2), 5); + assert.equal(psc.getAccumulatedValue(3), 6); + assert.equal(psc.getAccumulatedValue(4), 9); + + // [1, 0, 2, 1, 3] + psc.changeValue(1, 0); + assert.equal(psc.getTotalValue(), 7); + assert.equal(psc.getAccumulatedValue(0), 1); + assert.equal(psc.getAccumulatedValue(1), 1); + assert.equal(psc.getAccumulatedValue(2), 3); + assert.equal(psc.getAccumulatedValue(3), 4); + assert.equal(psc.getAccumulatedValue(4), 7); + indexOfResult = psc.getIndexOf(0); + assert.equal(indexOfResult.index, 0); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(1); + assert.equal(indexOfResult.index, 2); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(2); + assert.equal(indexOfResult.index, 2); + assert.equal(indexOfResult.remainder, 1); + indexOfResult = psc.getIndexOf(3); + assert.equal(indexOfResult.index, 3); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(4); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(5); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 1); + indexOfResult = psc.getIndexOf(6); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 2); + indexOfResult = psc.getIndexOf(7); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 3); + + // [1, 0, 0, 1, 3] + psc.changeValue(2, 0); + assert.equal(psc.getTotalValue(), 5); + assert.equal(psc.getAccumulatedValue(0), 1); + assert.equal(psc.getAccumulatedValue(1), 1); + assert.equal(psc.getAccumulatedValue(2), 1); + assert.equal(psc.getAccumulatedValue(3), 2); + assert.equal(psc.getAccumulatedValue(4), 5); + indexOfResult = psc.getIndexOf(0); + assert.equal(indexOfResult.index, 0); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(1); + assert.equal(indexOfResult.index, 3); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(2); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(3); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 1); + indexOfResult = psc.getIndexOf(4); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 2); + indexOfResult = psc.getIndexOf(5); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 3); + + // [1, 0, 0, 0, 3] + psc.changeValue(3, 0); + assert.equal(psc.getTotalValue(), 4); + assert.equal(psc.getAccumulatedValue(0), 1); + assert.equal(psc.getAccumulatedValue(1), 1); + assert.equal(psc.getAccumulatedValue(2), 1); + assert.equal(psc.getAccumulatedValue(3), 1); + assert.equal(psc.getAccumulatedValue(4), 4); + indexOfResult = psc.getIndexOf(0); + assert.equal(indexOfResult.index, 0); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(1); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(2); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 1); + indexOfResult = psc.getIndexOf(3); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 2); + indexOfResult = psc.getIndexOf(4); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 3); + + // [1, 1, 0, 1, 1] + psc.changeValue(1, 1); + psc.changeValue(3, 1); + psc.changeValue(4, 1); + assert.equal(psc.getTotalValue(), 4); + assert.equal(psc.getAccumulatedValue(0), 1); + assert.equal(psc.getAccumulatedValue(1), 2); + assert.equal(psc.getAccumulatedValue(2), 2); + assert.equal(psc.getAccumulatedValue(3), 3); + assert.equal(psc.getAccumulatedValue(4), 4); + indexOfResult = psc.getIndexOf(0); + assert.equal(indexOfResult.index, 0); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(1); + assert.equal(indexOfResult.index, 1); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(2); + assert.equal(indexOfResult.index, 3); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(3); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 0); + indexOfResult = psc.getIndexOf(4); + assert.equal(indexOfResult.index, 4); + assert.equal(indexOfResult.remainder, 1); + }); +}); diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts new file mode 100644 index 0000000000..7df30397b9 --- /dev/null +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -0,0 +1,786 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Position } from 'vs/editor/common/core/position'; +import { CharacterHardWrappingLineMapping, CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; +import { ILineMapping, IModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import { Model } from 'vs/editor/common/model/model'; +import { toUint32Array } from 'vs/editor/common/core/uint'; +import * as modes from 'vs/editor/common/modes'; +import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; +import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; +import { Range } from 'vs/editor/common/core/range'; + +suite('Editor ViewModel - SplitLinesCollection', () => { + test('SplitLine', () => { + var model1 = createModel('My First LineMy Second LineAnd another one'); + var line1 = createSplitLine([13, 14, 15], ''); + + assert.equal(line1.getViewLineCount(), 3); + assert.equal(line1.getViewLineContent(model1, 1, 0), 'My First Line'); + assert.equal(line1.getViewLineContent(model1, 1, 1), 'My Second Line'); + assert.equal(line1.getViewLineContent(model1, 1, 2), 'And another one'); + assert.equal(line1.getViewLineMaxColumn(model1, 1, 0), 14); + assert.equal(line1.getViewLineMaxColumn(model1, 1, 1), 15); + assert.equal(line1.getViewLineMaxColumn(model1, 1, 2), 16); + for (var col = 1; col <= 14; col++) { + assert.equal(line1.getModelColumnOfViewPosition(0, col), col, 'getInputColumnOfOutputPosition(0, ' + col + ')'); + } + for (var col = 1; col <= 15; col++) { + assert.equal(line1.getModelColumnOfViewPosition(1, col), 13 + col, 'getInputColumnOfOutputPosition(1, ' + col + ')'); + } + for (var col = 1; col <= 16; col++) { + assert.equal(line1.getModelColumnOfViewPosition(2, col), 13 + 14 + col, 'getInputColumnOfOutputPosition(2, ' + col + ')'); + } + for (var col = 1; col <= 13; col++) { + assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(0, col), 'getOutputPositionOfInputPosition(' + col + ')'); + } + for (var col = 1 + 13; col <= 14 + 13; col++) { + assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(1, col - 13), 'getOutputPositionOfInputPosition(' + col + ')'); + } + for (var col = 1 + 13 + 14; col <= 15 + 14 + 13; col++) { + assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(2, col - 13 - 14), 'getOutputPositionOfInputPosition(' + col + ')'); + } + + model1 = createModel('My First LineMy Second LineAnd another one'); + line1 = createSplitLine([13, 14, 15], '\t'); + + assert.equal(line1.getViewLineCount(), 3); + assert.equal(line1.getViewLineContent(model1, 1, 0), 'My First Line'); + assert.equal(line1.getViewLineContent(model1, 1, 1), '\tMy Second Line'); + assert.equal(line1.getViewLineContent(model1, 1, 2), '\tAnd another one'); + assert.equal(line1.getViewLineMaxColumn(model1, 1, 0), 14); + assert.equal(line1.getViewLineMaxColumn(model1, 1, 1), 16); + assert.equal(line1.getViewLineMaxColumn(model1, 1, 2), 17); + for (var col = 1; col <= 14; col++) { + assert.equal(line1.getModelColumnOfViewPosition(0, col), col, 'getInputColumnOfOutputPosition(0, ' + col + ')'); + } + for (var col = 1; col <= 1; col++) { + assert.equal(line1.getModelColumnOfViewPosition(1, 1), 13 + col, 'getInputColumnOfOutputPosition(1, ' + col + ')'); + } + for (var col = 2; col <= 16; col++) { + assert.equal(line1.getModelColumnOfViewPosition(1, col), 13 + col - 1, 'getInputColumnOfOutputPosition(1, ' + col + ')'); + } + for (var col = 1; col <= 1; col++) { + assert.equal(line1.getModelColumnOfViewPosition(2, col), 13 + 14 + col, 'getInputColumnOfOutputPosition(2, ' + col + ')'); + } + for (var col = 2; col <= 17; col++) { + assert.equal(line1.getModelColumnOfViewPosition(2, col), 13 + 14 + col - 1, 'getInputColumnOfOutputPosition(2, ' + col + ')'); + } + for (var col = 1; col <= 13; col++) { + assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(0, col), 'getOutputPositionOfInputPosition(' + col + ')'); + } + for (var col = 1 + 13; col <= 14 + 13; col++) { + assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(1, 1 + col - 13), 'getOutputPositionOfInputPosition(' + col + ')'); + } + for (var col = 1 + 13 + 14; col <= 15 + 14 + 13; col++) { + assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(2, 1 + col - 13 - 14), 'getOutputPositionOfInputPosition(' + col + ')'); + } + }); + + function withSplitLinesCollection(text: string, callback: (model: Model, linesCollection: SplitLinesCollection) => void): void { + let config = new TestConfiguration({}); + + let hardWrappingLineMapperFactory = new CharacterHardWrappingLineMapperFactory( + config.editor.wrappingInfo.wordWrapBreakBeforeCharacters, + config.editor.wrappingInfo.wordWrapBreakAfterCharacters, + config.editor.wrappingInfo.wordWrapBreakObtrusiveCharacters + ); + + let model = Model.createFromString([ + 'int main() {', + '\tprintf("Hello world!");', + '}', + 'int main() {', + '\tprintf("Hello world!");', + '}', + ].join('\n')); + + let linesCollection = new SplitLinesCollection( + model, + hardWrappingLineMapperFactory, + model.getOptions().tabSize, + config.editor.wrappingInfo.wrappingColumn, + config.editor.fontInfo.typicalFullwidthCharacterWidth / config.editor.fontInfo.typicalHalfwidthCharacterWidth, + config.editor.wrappingInfo.wrappingIndent + ); + + callback(model, linesCollection); + + linesCollection.dispose(); + model.dispose(); + config.dispose(); + } + + test('Invalid line numbers', () => { + + const text = [ + 'int main() {', + '\tprintf("Hello world!");', + '}', + 'int main() {', + '\tprintf("Hello world!");', + '}', + ].join('\n'); + + withSplitLinesCollection(text, (model, linesCollection) => { + assert.equal(linesCollection.getViewLineCount(), 6); + + // getOutputIndentGuide + assert.equal(linesCollection.getViewLineIndentGuide(-1), 0); + assert.equal(linesCollection.getViewLineIndentGuide(0), 0); + assert.equal(linesCollection.getViewLineIndentGuide(1), 0); + assert.equal(linesCollection.getViewLineIndentGuide(2), 1); + assert.equal(linesCollection.getViewLineIndentGuide(3), 0); + assert.equal(linesCollection.getViewLineIndentGuide(4), 0); + assert.equal(linesCollection.getViewLineIndentGuide(5), 1); + assert.equal(linesCollection.getViewLineIndentGuide(6), 0); + assert.equal(linesCollection.getViewLineIndentGuide(7), 0); + + // getOutputLineContent + assert.equal(linesCollection.getViewLineContent(-1), 'int main() {'); + assert.equal(linesCollection.getViewLineContent(0), 'int main() {'); + assert.equal(linesCollection.getViewLineContent(1), 'int main() {'); + assert.equal(linesCollection.getViewLineContent(2), '\tprintf("Hello world!");'); + assert.equal(linesCollection.getViewLineContent(3), '}'); + assert.equal(linesCollection.getViewLineContent(4), 'int main() {'); + assert.equal(linesCollection.getViewLineContent(5), '\tprintf("Hello world!");'); + assert.equal(linesCollection.getViewLineContent(6), '}'); + assert.equal(linesCollection.getViewLineContent(7), '}'); + + // getOutputLineMinColumn + assert.equal(linesCollection.getViewLineMinColumn(-1), 1); + assert.equal(linesCollection.getViewLineMinColumn(0), 1); + assert.equal(linesCollection.getViewLineMinColumn(1), 1); + assert.equal(linesCollection.getViewLineMinColumn(2), 1); + assert.equal(linesCollection.getViewLineMinColumn(3), 1); + assert.equal(linesCollection.getViewLineMinColumn(4), 1); + assert.equal(linesCollection.getViewLineMinColumn(5), 1); + assert.equal(linesCollection.getViewLineMinColumn(6), 1); + assert.equal(linesCollection.getViewLineMinColumn(7), 1); + + // getOutputLineMaxColumn + assert.equal(linesCollection.getViewLineMaxColumn(-1), 13); + assert.equal(linesCollection.getViewLineMaxColumn(0), 13); + assert.equal(linesCollection.getViewLineMaxColumn(1), 13); + assert.equal(linesCollection.getViewLineMaxColumn(2), 25); + assert.equal(linesCollection.getViewLineMaxColumn(3), 2); + assert.equal(linesCollection.getViewLineMaxColumn(4), 13); + assert.equal(linesCollection.getViewLineMaxColumn(5), 25); + assert.equal(linesCollection.getViewLineMaxColumn(6), 2); + assert.equal(linesCollection.getViewLineMaxColumn(7), 2); + + // convertOutputPositionToInputPosition + assert.deepEqual(linesCollection.convertViewPositionToModelPosition(-1, 1), new Position(1, 1)); + assert.deepEqual(linesCollection.convertViewPositionToModelPosition(0, 1), new Position(1, 1)); + assert.deepEqual(linesCollection.convertViewPositionToModelPosition(1, 1), new Position(1, 1)); + assert.deepEqual(linesCollection.convertViewPositionToModelPosition(2, 1), new Position(2, 1)); + assert.deepEqual(linesCollection.convertViewPositionToModelPosition(3, 1), new Position(3, 1)); + assert.deepEqual(linesCollection.convertViewPositionToModelPosition(4, 1), new Position(4, 1)); + assert.deepEqual(linesCollection.convertViewPositionToModelPosition(5, 1), new Position(5, 1)); + assert.deepEqual(linesCollection.convertViewPositionToModelPosition(6, 1), new Position(6, 1)); + assert.deepEqual(linesCollection.convertViewPositionToModelPosition(7, 1), new Position(6, 1)); + assert.deepEqual(linesCollection.convertViewPositionToModelPosition(8, 1), new Position(6, 1)); + }); + }); + + test('issue #3662', () => { + + const text = [ + 'int main() {', + '\tprintf("Hello world!");', + '}', + 'int main() {', + '\tprintf("Hello world!");', + '}', + ].join('\n'); + + withSplitLinesCollection(text, (model, linesCollection) => { + linesCollection.setHiddenAreas([ + new Range(1, 1, 3, 1), + new Range(5, 1, 6, 1) + ]); + + let viewLineCount = linesCollection.getViewLineCount(); + assert.equal(viewLineCount, 1, 'getOutputLineCount()'); + + let modelLineCount = model.getLineCount(); + for (let lineNumber = 0; lineNumber <= modelLineCount + 1; lineNumber++) { + let lineMinColumn = (lineNumber >= 1 && lineNumber <= modelLineCount) ? model.getLineMinColumn(lineNumber) : 1; + let lineMaxColumn = (lineNumber >= 1 && lineNumber <= modelLineCount) ? model.getLineMaxColumn(lineNumber) : 1; + for (let column = lineMinColumn - 1; column <= lineMaxColumn + 1; column++) { + let viewPosition = linesCollection.convertModelPositionToViewPosition(lineNumber, column); + + // validate view position + let viewLineNumber = viewPosition.lineNumber; + let viewColumn = viewPosition.column; + if (viewLineNumber < 1) { + viewLineNumber = 1; + } + var lineCount = linesCollection.getViewLineCount(); + if (viewLineNumber > lineCount) { + viewLineNumber = lineCount; + } + var viewMinColumn = linesCollection.getViewLineMinColumn(viewLineNumber); + var viewMaxColumn = linesCollection.getViewLineMaxColumn(viewLineNumber); + if (viewColumn < viewMinColumn) { + viewColumn = viewMinColumn; + } + if (viewColumn > viewMaxColumn) { + viewColumn = viewMaxColumn; + } + let validViewPosition = new Position(viewLineNumber, viewColumn); + assert.equal(viewPosition.toString(), validViewPosition.toString(), 'model->view for ' + lineNumber + ', ' + column); + } + } + + for (let lineNumber = 0; lineNumber <= viewLineCount + 1; lineNumber++) { + let lineMinColumn = linesCollection.getViewLineMinColumn(lineNumber); + let lineMaxColumn = linesCollection.getViewLineMaxColumn(lineNumber); + for (let column = lineMinColumn - 1; column <= lineMaxColumn + 1; column++) { + let modelPosition = linesCollection.convertViewPositionToModelPosition(lineNumber, column); + let validModelPosition = model.validatePosition(modelPosition); + assert.equal(modelPosition.toString(), validModelPosition.toString(), 'view->model for ' + lineNumber + ', ' + column); + } + } + }); + }); + +}); + +suite('SplitLinesCollection', () => { + + const _text = [ + 'class Nice {', + ' function hi() {', + ' console.log("Hello world");', + ' }', + ' function hello() {', + ' console.log("Hello world, this is a somewhat longer line");', + ' }', + '}', + ]; + + const _tokens = [ + [ + { startIndex: 0, value: 1 }, + { startIndex: 5, value: 2 }, + { startIndex: 6, value: 3 }, + { startIndex: 10, value: 4 }, + ], + [ + { startIndex: 0, value: 5 }, + { startIndex: 1, value: 6 }, + { startIndex: 9, value: 7 }, + { startIndex: 10, value: 8 }, + { startIndex: 12, value: 9 }, + ], + [ + { startIndex: 0, value: 10 }, + { startIndex: 2, value: 11 }, + { startIndex: 9, value: 12 }, + { startIndex: 10, value: 13 }, + { startIndex: 13, value: 14 }, + { startIndex: 14, value: 15 }, + { startIndex: 27, value: 16 }, + ], + [ + { startIndex: 0, value: 17 }, + ], + [ + { startIndex: 0, value: 18 }, + { startIndex: 1, value: 19 }, + { startIndex: 9, value: 20 }, + { startIndex: 10, value: 21 }, + { startIndex: 15, value: 22 }, + ], + [ + { startIndex: 0, value: 23 }, + { startIndex: 2, value: 24 }, + { startIndex: 9, value: 25 }, + { startIndex: 10, value: 26 }, + { startIndex: 13, value: 27 }, + { startIndex: 14, value: 28 }, + { startIndex: 59, value: 29 }, + ], + [ + { startIndex: 0, value: 30 }, + ], + [ + { startIndex: 0, value: 31 }, + ] + ]; + + let model: Model = null; + let languageRegistration: IDisposable = null; + + setup(() => { + let _lineIndex = 0; + const tokenizationSupport: modes.ITokenizationSupport = { + getInitialState: () => NULL_STATE, + tokenize: undefined, + tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { + let tokens = _tokens[_lineIndex++]; + + let result = new Uint32Array(2 * tokens.length); + for (let i = 0; i < tokens.length; i++) { + result[2 * i] = tokens[i].startIndex; + result[2 * i + 1] = ( + tokens[i].value << modes.MetadataConsts.FOREGROUND_OFFSET + ); + } + return new TokenizationResult2(result, state); + } + }; + const LANGUAGE_ID = 'modelModeTest1'; + languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport); + model = Model.createFromString(_text.join('\n'), undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); + // force tokenization + model.forceTokenization(model.getLineCount()); + }); + + teardown(() => { + model.dispose(); + model = null; + languageRegistration.dispose(); + languageRegistration = null; + }); + + + interface ITestViewLineToken { + endIndex: number; + value: number; + } + + function assertViewLineTokens(actual: ViewLineToken[], expected: ITestViewLineToken[]): void { + let _actual = actual.map((token) => { + return { + endIndex: token.endIndex, + value: token.getForeground() + }; + }); + assert.deepEqual(_actual, expected); + } + + interface ITestMinimapLineRenderingData { + content: string; + minColumn: number; + maxColumn: number; + tokens: ITestViewLineToken[]; + } + + function assertMinimapLineRenderingData(actual: ViewLineData, expected: ITestMinimapLineRenderingData): void { + if (actual === null && expected === null) { + assert.ok(true); + return; + } + assert.equal(actual.content, expected.content); + assert.equal(actual.minColumn, expected.minColumn); + assert.equal(actual.maxColumn, expected.maxColumn); + assertViewLineTokens(actual.tokens, expected.tokens); + } + + function assertMinimapLinesRenderingData(actual: ViewLineData[], expected: ITestMinimapLineRenderingData[]): void { + assert.equal(actual.length, expected.length); + for (let i = 0; i < expected.length; i++) { + assertMinimapLineRenderingData(actual[i], expected[i]); + } + } + + function assertAllMinimapLinesRenderingData(splitLinesCollection: SplitLinesCollection, all: ITestMinimapLineRenderingData[]): void { + let lineCount = all.length; + for (let start = 1; start <= lineCount; start++) { + for (let end = start; end <= lineCount; end++) { + let count = end - start + 1; + for (let desired = Math.pow(2, count) - 1; desired >= 0; desired--) { + let needed: boolean[] = []; + let expected: ITestMinimapLineRenderingData[] = []; + for (let i = 0; i < count; i++) { + needed[i] = (desired & (1 << i)) ? true : false; + expected[i] = (needed[i] ? all[start - 1 + i] : null); + } + let actual = splitLinesCollection.getViewLinesData(start, end, needed); + assertMinimapLinesRenderingData(actual, expected); + // Comment out next line to test all possible combinations + break; + } + } + } + } + + test('getViewLinesData - no wrapping', () => { + withSplitLinesCollection(model, 'off', 0, (splitLinesCollection) => { + assert.equal(splitLinesCollection.getViewLineCount(), 8); + assert.equal(splitLinesCollection.modelPositionIsVisible(1, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(2, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(3, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(4, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(5, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(6, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(7, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(8, 1), true); + + let _expected: ITestMinimapLineRenderingData[] = [ + { + content: 'class Nice {', + minColumn: 1, + maxColumn: 13, + tokens: [ + { endIndex: 5, value: 1 }, + { endIndex: 6, value: 2 }, + { endIndex: 10, value: 3 }, + { endIndex: 12, value: 4 }, + ] + }, + { + content: ' function hi() {', + minColumn: 1, + maxColumn: 17, + tokens: [ + { endIndex: 1, value: 5 }, + { endIndex: 9, value: 6 }, + { endIndex: 10, value: 7 }, + { endIndex: 12, value: 8 }, + { endIndex: 16, value: 9 }, + ] + }, + { + content: ' console.log("Hello world");', + minColumn: 1, + maxColumn: 30, + tokens: [ + { endIndex: 2, value: 10 }, + { endIndex: 9, value: 11 }, + { endIndex: 10, value: 12 }, + { endIndex: 13, value: 13 }, + { endIndex: 14, value: 14 }, + { endIndex: 27, value: 15 }, + { endIndex: 29, value: 16 }, + ] + }, + { + content: ' }', + minColumn: 1, + maxColumn: 3, + tokens: [ + { endIndex: 2, value: 17 }, + ] + }, + { + content: ' function hello() {', + minColumn: 1, + maxColumn: 20, + tokens: [ + { endIndex: 1, value: 18 }, + { endIndex: 9, value: 19 }, + { endIndex: 10, value: 20 }, + { endIndex: 15, value: 21 }, + { endIndex: 19, value: 22 }, + ] + }, + { + content: ' console.log("Hello world, this is a somewhat longer line");', + minColumn: 1, + maxColumn: 62, + tokens: [ + { endIndex: 2, value: 23 }, + { endIndex: 9, value: 24 }, + { endIndex: 10, value: 25 }, + { endIndex: 13, value: 26 }, + { endIndex: 14, value: 27 }, + { endIndex: 59, value: 28 }, + { endIndex: 61, value: 29 }, + ] + }, + { + minColumn: 1, + maxColumn: 3, + content: ' }', + tokens: [ + { endIndex: 2, value: 30 }, + ] + }, + { + minColumn: 1, + maxColumn: 2, + content: '}', + tokens: [ + { endIndex: 1, value: 31 }, + ] + } + ]; + + assertAllMinimapLinesRenderingData(splitLinesCollection, [ + _expected[0], + _expected[1], + _expected[2], + _expected[3], + _expected[4], + _expected[5], + _expected[6], + _expected[7], + ]); + + splitLinesCollection.setHiddenAreas([new Range(2, 1, 4, 1)]); + assert.equal(splitLinesCollection.getViewLineCount(), 5); + assert.equal(splitLinesCollection.modelPositionIsVisible(1, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(2, 1), false); + assert.equal(splitLinesCollection.modelPositionIsVisible(3, 1), false); + assert.equal(splitLinesCollection.modelPositionIsVisible(4, 1), false); + assert.equal(splitLinesCollection.modelPositionIsVisible(5, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(6, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(7, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(8, 1), true); + + assertAllMinimapLinesRenderingData(splitLinesCollection, [ + _expected[0], + _expected[4], + _expected[5], + _expected[6], + _expected[7], + ]); + }); + }); + + test('getViewLinesData - with wrapping', () => { + withSplitLinesCollection(model, 'wordWrapColumn', 30, (splitLinesCollection) => { + assert.equal(splitLinesCollection.getViewLineCount(), 12); + assert.equal(splitLinesCollection.modelPositionIsVisible(1, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(2, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(3, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(4, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(5, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(6, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(7, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(8, 1), true); + + let _expected: ITestMinimapLineRenderingData[] = [ + { + content: 'class Nice {', + minColumn: 1, + maxColumn: 13, + tokens: [ + { endIndex: 5, value: 1 }, + { endIndex: 6, value: 2 }, + { endIndex: 10, value: 3 }, + { endIndex: 12, value: 4 }, + ] + }, + { + content: ' function hi() {', + minColumn: 1, + maxColumn: 17, + tokens: [ + { endIndex: 1, value: 5 }, + { endIndex: 9, value: 6 }, + { endIndex: 10, value: 7 }, + { endIndex: 12, value: 8 }, + { endIndex: 16, value: 9 }, + ] + }, + { + content: ' console.log("Hello ', + minColumn: 1, + maxColumn: 22, + tokens: [ + { endIndex: 2, value: 10 }, + { endIndex: 9, value: 11 }, + { endIndex: 10, value: 12 }, + { endIndex: 13, value: 13 }, + { endIndex: 14, value: 14 }, + { endIndex: 21, value: 15 }, + ] + }, + { + content: ' world");', + minColumn: 4, + maxColumn: 12, + tokens: [ + { endIndex: 9, value: 15 }, + { endIndex: 11, value: 16 }, + ] + }, + { + content: ' }', + minColumn: 1, + maxColumn: 3, + tokens: [ + { endIndex: 2, value: 17 }, + ] + }, + { + content: ' function hello() {', + minColumn: 1, + maxColumn: 20, + tokens: [ + { endIndex: 1, value: 18 }, + { endIndex: 9, value: 19 }, + { endIndex: 10, value: 20 }, + { endIndex: 15, value: 21 }, + { endIndex: 19, value: 22 }, + ] + }, + { + content: ' console.log("Hello ', + minColumn: 1, + maxColumn: 22, + tokens: [ + { endIndex: 2, value: 23 }, + { endIndex: 9, value: 24 }, + { endIndex: 10, value: 25 }, + { endIndex: 13, value: 26 }, + { endIndex: 14, value: 27 }, + { endIndex: 21, value: 28 }, + ] + }, + { + content: ' world, this is a ', + minColumn: 4, + maxColumn: 21, + tokens: [ + { endIndex: 20, value: 28 }, + ] + }, + { + content: ' somewhat longer ', + minColumn: 4, + maxColumn: 20, + tokens: [ + { endIndex: 19, value: 28 }, + ] + }, + { + content: ' line");', + minColumn: 4, + maxColumn: 11, + tokens: [ + { endIndex: 8, value: 28 }, + { endIndex: 10, value: 29 }, + ] + }, + { + content: ' }', + minColumn: 1, + maxColumn: 3, + tokens: [ + { endIndex: 2, value: 30 }, + ] + }, + { + content: '}', + minColumn: 1, + maxColumn: 2, + tokens: [ + { endIndex: 1, value: 31 }, + ] + } + ]; + + assertAllMinimapLinesRenderingData(splitLinesCollection, [ + _expected[0], + _expected[1], + _expected[2], + _expected[3], + _expected[4], + _expected[5], + _expected[6], + _expected[7], + _expected[8], + _expected[9], + _expected[10], + _expected[11], + ]); + + splitLinesCollection.setHiddenAreas([new Range(2, 1, 4, 1)]); + assert.equal(splitLinesCollection.getViewLineCount(), 8); + assert.equal(splitLinesCollection.modelPositionIsVisible(1, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(2, 1), false); + assert.equal(splitLinesCollection.modelPositionIsVisible(3, 1), false); + assert.equal(splitLinesCollection.modelPositionIsVisible(4, 1), false); + assert.equal(splitLinesCollection.modelPositionIsVisible(5, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(6, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(7, 1), true); + assert.equal(splitLinesCollection.modelPositionIsVisible(8, 1), true); + + assertAllMinimapLinesRenderingData(splitLinesCollection, [ + _expected[0], + _expected[5], + _expected[6], + _expected[7], + _expected[8], + _expected[9], + _expected[10], + _expected[11], + ]); + }); + }); + + function withSplitLinesCollection(model: Model, wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded', wordWrapColumn: number, callback: (splitLinesCollection: SplitLinesCollection) => void): void { + let configuration = new TestConfiguration({ + wordWrap: wordWrap, + wordWrapColumn: wordWrapColumn, + wrappingIndent: 'indent' + }); + + let factory = new CharacterHardWrappingLineMapperFactory( + configuration.editor.wrappingInfo.wordWrapBreakBeforeCharacters, + configuration.editor.wrappingInfo.wordWrapBreakAfterCharacters, + configuration.editor.wrappingInfo.wordWrapBreakObtrusiveCharacters + ); + + let linesCollection = new SplitLinesCollection( + model, + factory, + model.getOptions().tabSize, + configuration.editor.wrappingInfo.wrappingColumn, + configuration.editor.fontInfo.typicalFullwidthCharacterWidth / configuration.editor.fontInfo.typicalHalfwidthCharacterWidth, + configuration.editor.wrappingInfo.wrappingIndent + ); + + callback(linesCollection); + + configuration.dispose(); + } +}); + + +function pos(lineNumber: number, column: number): Position { + return new Position(lineNumber, column); +} + +function createSplitLine(splitLengths: number[], wrappedLinesPrefix: string, isVisible: boolean = true): SplitLine { + return new SplitLine(createLineMapping(splitLengths, wrappedLinesPrefix), isVisible); +} + +function createLineMapping(breakingLengths: number[], wrappedLinesPrefix: string): ILineMapping { + return new CharacterHardWrappingLineMapping( + new PrefixSumComputer(toUint32Array(breakingLengths)), + wrappedLinesPrefix + ); +} + +function createModel(text: string): IModel { + return { + getLineTokens: (lineNumber: number) => { + return null; + }, + getLineContent: (lineNumber: number) => { + return text; + }, + getLineMinColumn: (lineNumber: number) => { + return 1; + }, + getLineMaxColumn: (lineNumber: number) => { + return text.length + 1; + } + }; +} \ No newline at end of file diff --git a/src/vs/editor/test/common/viewModel/testViewModel.ts b/src/vs/editor/test/common/viewModel/testViewModel.ts new file mode 100644 index 0000000000..2533ad9618 --- /dev/null +++ b/src/vs/editor/test/common/viewModel/testViewModel.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Model } from 'vs/editor/common/model/model'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; +import { MockCodeEditorCreationOptions } from 'vs/editor/test/common/mocks/mockCodeEditor'; + +export function testViewModel(text: string[], options: MockCodeEditorCreationOptions, callback: (viewModel: ViewModel, model: Model) => void): void { + const EDITOR_ID = 1; + + let configuration = new TestConfiguration(options); + + let model = Model.createFromString(text.join('\n')); + + let viewModel = new ViewModel(EDITOR_ID, configuration, model, null); + + callback(viewModel, model); + + viewModel.dispose(); + model.dispose(); + configuration.dispose(); +} diff --git a/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts b/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts new file mode 100644 index 0000000000..10644a37dd --- /dev/null +++ b/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts @@ -0,0 +1,311 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Range } from 'vs/editor/common/core/range'; +import { testViewModel } from 'vs/editor/test/common/viewModel/testViewModel'; +import { MockCodeEditorCreationOptions } from 'vs/editor/test/common/mocks/mockCodeEditor'; + +suite('ViewModelDecorations', () => { + test('getDecorationsViewportData', () => { + const text = [ + 'hello world, this is a buffer that will be wrapped' + ]; + const opts: MockCodeEditorCreationOptions = { + wordWrap: 'wordWrapColumn', + wordWrapColumn: 13 + }; + testViewModel(text, opts, (viewModel, model) => { + assert.equal(viewModel.getLineContent(1), 'hello world, '); + assert.equal(viewModel.getLineContent(2), 'this is a '); + assert.equal(viewModel.getLineContent(3), 'buffer that '); + assert.equal(viewModel.getLineContent(4), 'will be '); + assert.equal(viewModel.getLineContent(5), 'wrapped'); + + let dec1: string; + let dec2: string; + let dec3: string; + let dec4: string; + let dec5: string; + let dec6: string; + let dec7: string; + let dec8: string; + let dec9: string; + let dec10: string; + let dec11: string; + let dec12: string; + let dec13: string; + let dec14: string; + let dec15: string; + model.changeDecorations((accessor) => { + let createOpts = (id: string) => { + return { + className: id, + inlineClassName: 'i-' + id, + beforeContentClassName: 'b-' + id, + afterContentClassName: 'a-' + id + }; + }; + + // VIEWPORT will be (1,14) -> (1,36) + + // completely before viewport + dec1 = accessor.addDecoration(new Range(1, 2, 1, 3), createOpts('dec1')); + // starts before viewport, ends at viewport start + dec2 = accessor.addDecoration(new Range(1, 2, 1, 14), createOpts('dec2')); + // starts before viewport, ends inside viewport + dec3 = accessor.addDecoration(new Range(1, 2, 1, 15), createOpts('dec3')); + // starts before viewport, ends at viewport end + dec4 = accessor.addDecoration(new Range(1, 2, 1, 36), createOpts('dec4')); + // starts before viewport, ends after viewport + dec5 = accessor.addDecoration(new Range(1, 2, 1, 51), createOpts('dec5')); + + // starts at viewport start, ends at viewport start + dec6 = accessor.addDecoration(new Range(1, 14, 1, 14), createOpts('dec6')); + // starts at viewport start, ends inside viewport + dec7 = accessor.addDecoration(new Range(1, 14, 1, 16), createOpts('dec7')); + // starts at viewport start, ends at viewport end + dec8 = accessor.addDecoration(new Range(1, 14, 1, 36), createOpts('dec8')); + // starts at viewport start, ends after viewport + dec9 = accessor.addDecoration(new Range(1, 14, 1, 51), createOpts('dec9')); + + // starts inside viewport, ends inside viewport + dec10 = accessor.addDecoration(new Range(1, 16, 1, 18), createOpts('dec10')); + // starts inside viewport, ends at viewport end + dec11 = accessor.addDecoration(new Range(1, 16, 1, 36), createOpts('dec11')); + // starts inside viewport, ends after viewport + dec12 = accessor.addDecoration(new Range(1, 16, 1, 51), createOpts('dec12')); + + // starts at viewport end, ends at viewport end + dec13 = accessor.addDecoration(new Range(1, 36, 1, 36), createOpts('dec13')); + // starts at viewport end, ends after viewport + dec14 = accessor.addDecoration(new Range(1, 36, 1, 51), createOpts('dec14')); + + // starts after viewport, ends after viewport + dec15 = accessor.addDecoration(new Range(1, 40, 1, 51), createOpts('dec15')); + }); + + let actualDecorations = viewModel.getDecorationsInViewport( + new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)) + ).map((dec) => { + return dec.source.id; + }); + + assert.deepEqual(actualDecorations, [ + dec2, + dec3, + dec4, + dec5, + dec6, + dec7, + dec8, + dec9, + dec10, + dec11, + dec12, + dec13, + dec14, + ]); + + let inlineDecorations1 = viewModel.getViewLineRenderingData( + new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)), + 2 + ).inlineDecorations; + + // view line 2: (1,14 -> 1,24) + assert.deepEqual(inlineDecorations1, [ + { + range: new Range(1, 2, 2, 1), + inlineClassName: 'i-dec2', + insertsBeforeOrAfter: false + }, + { + range: new Range(1, 2, 2, 2), + inlineClassName: 'i-dec3', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 1, 2, 2), + inlineClassName: 'a-dec3', + insertsBeforeOrAfter: true + }, + { + range: new Range(1, 2, 4, 1), + inlineClassName: 'i-dec4', + insertsBeforeOrAfter: false + }, + { + range: new Range(1, 2, 5, 8), + inlineClassName: 'i-dec5', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 1, 2, 1), + inlineClassName: 'i-dec6', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 1, 2, 2), + inlineClassName: 'b-dec6', + insertsBeforeOrAfter: true + }, + { + range: new Range(2, 1, 2, 3), + inlineClassName: 'i-dec7', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 1, 2, 2), + inlineClassName: 'b-dec7', + insertsBeforeOrAfter: true + }, + { + range: new Range(2, 2, 2, 3), + inlineClassName: 'a-dec7', + insertsBeforeOrAfter: true + }, + { + range: new Range(2, 1, 4, 1), + inlineClassName: 'i-dec8', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 1, 2, 2), + inlineClassName: 'b-dec8', + insertsBeforeOrAfter: true + }, + { + range: new Range(2, 1, 5, 8), + inlineClassName: 'i-dec9', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 1, 2, 2), + inlineClassName: 'b-dec9', + insertsBeforeOrAfter: true + }, + { + range: new Range(2, 3, 2, 5), + inlineClassName: 'i-dec10', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 3, 2, 4), + inlineClassName: 'b-dec10', + insertsBeforeOrAfter: true + }, + { + range: new Range(2, 4, 2, 5), + inlineClassName: 'a-dec10', + insertsBeforeOrAfter: true + }, + { + range: new Range(2, 3, 4, 1), + inlineClassName: 'i-dec11', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 3, 2, 4), + inlineClassName: 'b-dec11', + insertsBeforeOrAfter: true + }, + { + range: new Range(2, 3, 5, 8), + inlineClassName: 'i-dec12', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 3, 2, 4), + inlineClassName: 'b-dec12', + insertsBeforeOrAfter: true + }, + ]); + + let inlineDecorations2 = viewModel.getViewLineRenderingData( + new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)), + 3 + ).inlineDecorations; + + // view line 3 (24 -> 36) + assert.deepEqual(inlineDecorations2, [ + { + range: new Range(1, 2, 4, 1), + inlineClassName: 'i-dec4', + insertsBeforeOrAfter: false + }, + { + range: new Range(1, 2, 5, 8), + inlineClassName: 'i-dec5', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 1, 4, 1), + inlineClassName: 'i-dec8', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 1, 5, 8), + inlineClassName: 'i-dec9', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 3, 4, 1), + inlineClassName: 'i-dec11', + insertsBeforeOrAfter: false + }, + { + range: new Range(2, 3, 5, 8), + inlineClassName: 'i-dec12', + insertsBeforeOrAfter: false + }, + ]); + }); + }); + + test('issue #17208: Problem scrolling in 1.8.0', () => { + const text = [ + 'hello world, this is a buffer that will be wrapped' + ]; + const opts: MockCodeEditorCreationOptions = { + wordWrap: 'wordWrapColumn', + wordWrapColumn: 13 + }; + testViewModel(text, opts, (viewModel, model) => { + assert.equal(viewModel.getLineContent(1), 'hello world, '); + assert.equal(viewModel.getLineContent(2), 'this is a '); + assert.equal(viewModel.getLineContent(3), 'buffer that '); + assert.equal(viewModel.getLineContent(4), 'will be '); + assert.equal(viewModel.getLineContent(5), 'wrapped'); + + let dec1: string; + model.changeDecorations((accessor) => { + dec1 = accessor.addDecoration( + new Range(1, 50, 1, 51), + { + beforeContentClassName: 'dec1' + } + ); + }); + + let decorations = viewModel.getDecorationsInViewport( + new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)) + ); + assert.deepEqual(decorations, []); + + let inlineDecorations1 = viewModel.getViewLineRenderingData( + new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)), + 2 + ).inlineDecorations; + assert.deepEqual(inlineDecorations1, []); + + let inlineDecorations2 = viewModel.getViewLineRenderingData( + new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)), + 3 + ).inlineDecorations; + assert.deepEqual(inlineDecorations2, []); + }); + }); +}); diff --git a/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts b/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts new file mode 100644 index 0000000000..6cb041e505 --- /dev/null +++ b/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Range } from 'vs/editor/common/core/range'; +import { testViewModel } from 'vs/editor/test/common/viewModel/testViewModel'; + +suite('ViewModel', () => { + + test('issue #21073: SplitLinesCollection: attempt to access a \'newer\' model', () => { + const text = ['']; + const opts = { + lineNumbersMinChars: 1 + }; + testViewModel(text, opts, (viewModel, model) => { + assert.equal(viewModel.getLineCount(), 1); + + viewModel.setViewport(1, 1, 1); + + model.applyEdits([{ + identifier: null, + range: new Range(1, 1, 1, 1), + text: [ + 'line01', + 'line02', + 'line03', + 'line04', + 'line05', + 'line06', + 'line07', + 'line08', + 'line09', + 'line10', + ].join('\n'), + forceMoveMarkers: false + }]); + + assert.equal(viewModel.getLineCount(), 10); + }); + }); +}); diff --git a/src/vs/loader.js b/src/vs/loader.js new file mode 100644 index 0000000000..51a2ab24f7 --- /dev/null +++ b/src/vs/loader.js @@ -0,0 +1,1681 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + * Please make sure to make edits in the .ts file at https://github.com/Microsoft/vscode-loader/ + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------- + *--------------------------------------------------------------------------------------------*/ +var _amdLoaderGlobal = this; +var AMDLoader; +(function (AMDLoader) { + AMDLoader.global = _amdLoaderGlobal; + var Environment = (function () { + function Environment(opts) { + this.isWindows = opts.isWindows; + this.isNode = opts.isNode; + this.isElectronRenderer = opts.isElectronRenderer; + this.isWebWorker = opts.isWebWorker; + } + Environment.detect = function () { + return new Environment({ + isWindows: this._isWindows(), + isNode: (typeof module !== 'undefined' && !!module.exports), + isElectronRenderer: (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' && process.type === 'renderer'), + isWebWorker: (typeof AMDLoader.global.importScripts === 'function') + }); + }; + Environment._isWindows = function () { + if (typeof navigator !== 'undefined') { + if (navigator.userAgent && navigator.userAgent.indexOf('Windows') >= 0) { + return true; + } + } + if (typeof process !== 'undefined') { + return (process.platform === 'win32'); + } + return false; + }; + return Environment; + }()); + AMDLoader.Environment = Environment; +})(AMDLoader || (AMDLoader = {})); +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var AMDLoader; +(function (AMDLoader) { + var LoaderEventType; + (function (LoaderEventType) { + LoaderEventType[LoaderEventType["LoaderAvailable"] = 1] = "LoaderAvailable"; + LoaderEventType[LoaderEventType["BeginLoadingScript"] = 10] = "BeginLoadingScript"; + LoaderEventType[LoaderEventType["EndLoadingScriptOK"] = 11] = "EndLoadingScriptOK"; + LoaderEventType[LoaderEventType["EndLoadingScriptError"] = 12] = "EndLoadingScriptError"; + LoaderEventType[LoaderEventType["BeginInvokeFactory"] = 21] = "BeginInvokeFactory"; + LoaderEventType[LoaderEventType["EndInvokeFactory"] = 22] = "EndInvokeFactory"; + LoaderEventType[LoaderEventType["NodeBeginEvaluatingScript"] = 31] = "NodeBeginEvaluatingScript"; + LoaderEventType[LoaderEventType["NodeEndEvaluatingScript"] = 32] = "NodeEndEvaluatingScript"; + LoaderEventType[LoaderEventType["NodeBeginNativeRequire"] = 33] = "NodeBeginNativeRequire"; + LoaderEventType[LoaderEventType["NodeEndNativeRequire"] = 34] = "NodeEndNativeRequire"; + })(LoaderEventType = AMDLoader.LoaderEventType || (AMDLoader.LoaderEventType = {})); + var LoaderEvent = (function () { + function LoaderEvent(type, detail, timestamp) { + this.type = type; + this.detail = detail; + this.timestamp = timestamp; + } + return LoaderEvent; + }()); + AMDLoader.LoaderEvent = LoaderEvent; + var LoaderEventRecorder = (function () { + function LoaderEventRecorder(loaderAvailableTimestamp) { + this._events = [new LoaderEvent(LoaderEventType.LoaderAvailable, '', loaderAvailableTimestamp)]; + } + LoaderEventRecorder.prototype.record = function (type, detail) { + this._events.push(new LoaderEvent(type, detail, AMDLoader.Utilities.getHighPerformanceTimestamp())); + }; + LoaderEventRecorder.prototype.getEvents = function () { + return this._events; + }; + return LoaderEventRecorder; + }()); + AMDLoader.LoaderEventRecorder = LoaderEventRecorder; + var NullLoaderEventRecorder = (function () { + function NullLoaderEventRecorder() { + } + NullLoaderEventRecorder.prototype.record = function (type, detail) { + // Nothing to do + }; + NullLoaderEventRecorder.prototype.getEvents = function () { + return []; + }; + return NullLoaderEventRecorder; + }()); + NullLoaderEventRecorder.INSTANCE = new NullLoaderEventRecorder(); + AMDLoader.NullLoaderEventRecorder = NullLoaderEventRecorder; +})(AMDLoader || (AMDLoader = {})); +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var AMDLoader; +(function (AMDLoader) { + var Utilities = (function () { + function Utilities() { + } + /** + * This method does not take care of / vs \ + */ + Utilities.fileUriToFilePath = function (isWindows, uri) { + uri = decodeURI(uri); + if (isWindows) { + if (/^file:\/\/\//.test(uri)) { + // This is a URI without a hostname => return only the path segment + return uri.substr(8); + } + if (/^file:\/\//.test(uri)) { + return uri.substr(5); + } + } + else { + if (/^file:\/\//.test(uri)) { + return uri.substr(7); + } + } + // Not sure... + return uri; + }; + Utilities.startsWith = function (haystack, needle) { + return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; + }; + Utilities.endsWith = function (haystack, needle) { + return haystack.length >= needle.length && haystack.substr(haystack.length - needle.length) === needle; + }; + // only check for "?" before "#" to ensure that there is a real Query-String + Utilities.containsQueryString = function (url) { + return /^[^\#]*\?/gi.test(url); + }; + /** + * Does `url` start with http:// or https:// or file:// or / ? + */ + Utilities.isAbsolutePath = function (url) { + return /^((http:\/\/)|(https:\/\/)|(file:\/\/)|(\/))/.test(url); + }; + Utilities.forEachProperty = function (obj, callback) { + if (obj) { + var key = void 0; + for (key in obj) { + if (obj.hasOwnProperty(key)) { + callback(key, obj[key]); + } + } + } + }; + Utilities.isEmpty = function (obj) { + var isEmpty = true; + Utilities.forEachProperty(obj, function () { + isEmpty = false; + }); + return isEmpty; + }; + Utilities.recursiveClone = function (obj) { + if (!obj || typeof obj !== 'object') { + return obj; + } + var result = Array.isArray(obj) ? [] : {}; + Utilities.forEachProperty(obj, function (key, value) { + if (value && typeof value === 'object') { + result[key] = Utilities.recursiveClone(value); + } + else { + result[key] = value; + } + }); + return result; + }; + Utilities.generateAnonymousModule = function () { + return '===anonymous' + (Utilities.NEXT_ANONYMOUS_ID++) + '==='; + }; + Utilities.isAnonymousModule = function (id) { + return /^===anonymous/.test(id); + }; + Utilities.getHighPerformanceTimestamp = function () { + if (!this.PERFORMANCE_NOW_PROBED) { + this.PERFORMANCE_NOW_PROBED = true; + this.HAS_PERFORMANCE_NOW = (AMDLoader.global.performance && typeof AMDLoader.global.performance.now === 'function'); + } + return (this.HAS_PERFORMANCE_NOW ? AMDLoader.global.performance.now() : Date.now()); + }; + return Utilities; + }()); + Utilities.NEXT_ANONYMOUS_ID = 1; + Utilities.PERFORMANCE_NOW_PROBED = false; + Utilities.HAS_PERFORMANCE_NOW = false; + AMDLoader.Utilities = Utilities; +})(AMDLoader || (AMDLoader = {})); +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var AMDLoader; +(function (AMDLoader) { + var ConfigurationOptionsUtil = (function () { + function ConfigurationOptionsUtil() { + } + /** + * Ensure configuration options make sense + */ + ConfigurationOptionsUtil.validateConfigurationOptions = function (isWebWorker, options) { + function defaultOnError(err) { + if (err.errorCode === 'load') { + console.error('Loading "' + err.moduleId + '" failed'); + console.error('Detail: ', err.detail); + if (err.detail && err.detail.stack) { + console.error(err.detail.stack); + } + console.error('Here are the modules that depend on it:'); + console.error(err.neededBy); + return; + } + if (err.errorCode === 'factory') { + console.error('The factory method of "' + err.moduleId + '" has thrown an exception'); + console.error(err.detail); + if (err.detail && err.detail.stack) { + console.error(err.detail.stack); + } + return; + } + } + options = options || {}; + if (typeof options.baseUrl !== 'string') { + options.baseUrl = ''; + } + if (typeof options.isBuild !== 'boolean') { + options.isBuild = false; + } + if (typeof options.paths !== 'object') { + options.paths = {}; + } + if (typeof options.config !== 'object') { + options.config = {}; + } + if (typeof options.catchError === 'undefined') { + // Catch errors by default in web workers, do not catch errors by default in other contexts + options.catchError = isWebWorker; + } + if (typeof options.urlArgs !== 'string') { + options.urlArgs = ''; + } + if (typeof options.onError !== 'function') { + options.onError = defaultOnError; + } + if (typeof options.ignoreDuplicateModules !== 'object' || !Array.isArray(options.ignoreDuplicateModules)) { + options.ignoreDuplicateModules = []; + } + if (options.baseUrl.length > 0) { + if (!AMDLoader.Utilities.endsWith(options.baseUrl, '/')) { + options.baseUrl += '/'; + } + } + if (!Array.isArray(options.nodeModules)) { + options.nodeModules = []; + } + if (typeof options.nodeCachedDataWriteDelay !== 'number' || options.nodeCachedDataWriteDelay < 0) { + options.nodeCachedDataWriteDelay = 1000 * 7; + } + if (typeof options.onNodeCachedData !== 'function') { + options.onNodeCachedData = function (err, data) { + if (!err) { + // ignore + } + else if (err.errorCode === 'cachedDataRejected') { + console.warn('Rejected cached data from file: ' + err.path); + } + else if (err.errorCode === 'unlink' || err.errorCode === 'writeFile') { + console.error('Problems writing cached data file: ' + err.path); + console.error(err.detail); + } + else { + console.error(err); + } + }; + } + return options; + }; + ConfigurationOptionsUtil.mergeConfigurationOptions = function (isWebWorker, overwrite, base) { + if (overwrite === void 0) { overwrite = null; } + if (base === void 0) { base = null; } + var result = AMDLoader.Utilities.recursiveClone(base || {}); + // Merge known properties and overwrite the unknown ones + AMDLoader.Utilities.forEachProperty(overwrite, function (key, value) { + if (key === 'ignoreDuplicateModules' && typeof result.ignoreDuplicateModules !== 'undefined') { + result.ignoreDuplicateModules = result.ignoreDuplicateModules.concat(value); + } + else if (key === 'paths' && typeof result.paths !== 'undefined') { + AMDLoader.Utilities.forEachProperty(value, function (key2, value2) { return result.paths[key2] = value2; }); + } + else if (key === 'config' && typeof result.config !== 'undefined') { + AMDLoader.Utilities.forEachProperty(value, function (key2, value2) { return result.config[key2] = value2; }); + } + else { + result[key] = AMDLoader.Utilities.recursiveClone(value); + } + }); + return ConfigurationOptionsUtil.validateConfigurationOptions(isWebWorker, result); + }; + return ConfigurationOptionsUtil; + }()); + AMDLoader.ConfigurationOptionsUtil = ConfigurationOptionsUtil; + var Configuration = (function () { + function Configuration(env, options) { + this._env = env; + this.options = ConfigurationOptionsUtil.mergeConfigurationOptions(this._env.isWebWorker, options); + this._createIgnoreDuplicateModulesMap(); + this._createNodeModulesMap(); + this._createSortedPathsRules(); + if (this.options.baseUrl === '') { + if (this._env.isNode && this.options.nodeRequire && this.options.nodeRequire.main && this.options.nodeRequire.main.filename) { + var nodeMain = this.options.nodeRequire.main.filename; + var dirnameIndex = Math.max(nodeMain.lastIndexOf('/'), nodeMain.lastIndexOf('\\')); + this.options.baseUrl = nodeMain.substring(0, dirnameIndex + 1); + } + if (this._env.isNode && this.options.nodeMain) { + var nodeMain = this.options.nodeMain; + var dirnameIndex = Math.max(nodeMain.lastIndexOf('/'), nodeMain.lastIndexOf('\\')); + this.options.baseUrl = nodeMain.substring(0, dirnameIndex + 1); + } + } + } + Configuration.prototype._createIgnoreDuplicateModulesMap = function () { + // Build a map out of the ignoreDuplicateModules array + this.ignoreDuplicateModulesMap = {}; + for (var i = 0; i < this.options.ignoreDuplicateModules.length; i++) { + this.ignoreDuplicateModulesMap[this.options.ignoreDuplicateModules[i]] = true; + } + }; + Configuration.prototype._createNodeModulesMap = function () { + // Build a map out of nodeModules array + this.nodeModulesMap = Object.create(null); + for (var _i = 0, _a = this.options.nodeModules; _i < _a.length; _i++) { + var nodeModule = _a[_i]; + this.nodeModulesMap[nodeModule] = true; + } + }; + Configuration.prototype._createSortedPathsRules = function () { + var _this = this; + // Create an array our of the paths rules, sorted descending by length to + // result in a more specific -> less specific order + this.sortedPathsRules = []; + AMDLoader.Utilities.forEachProperty(this.options.paths, function (from, to) { + if (!Array.isArray(to)) { + _this.sortedPathsRules.push({ + from: from, + to: [to] + }); + } + else { + _this.sortedPathsRules.push({ + from: from, + to: to + }); + } + }); + this.sortedPathsRules.sort(function (a, b) { + return b.from.length - a.from.length; + }); + }; + /** + * Clone current configuration and overwrite options selectively. + * @param options The selective options to overwrite with. + * @result A new configuration + */ + Configuration.prototype.cloneAndMerge = function (options) { + return new Configuration(this._env, ConfigurationOptionsUtil.mergeConfigurationOptions(this._env.isWebWorker, options, this.options)); + }; + /** + * Get current options bag. Useful for passing it forward to plugins. + */ + Configuration.prototype.getOptionsLiteral = function () { + return this.options; + }; + Configuration.prototype._applyPaths = function (moduleId) { + var pathRule; + for (var i = 0, len = this.sortedPathsRules.length; i < len; i++) { + pathRule = this.sortedPathsRules[i]; + if (AMDLoader.Utilities.startsWith(moduleId, pathRule.from)) { + var result = []; + for (var j = 0, lenJ = pathRule.to.length; j < lenJ; j++) { + result.push(pathRule.to[j] + moduleId.substr(pathRule.from.length)); + } + return result; + } + } + return [moduleId]; + }; + Configuration.prototype._addUrlArgsToUrl = function (url) { + if (AMDLoader.Utilities.containsQueryString(url)) { + return url + '&' + this.options.urlArgs; + } + else { + return url + '?' + this.options.urlArgs; + } + }; + Configuration.prototype._addUrlArgsIfNecessaryToUrl = function (url) { + if (this.options.urlArgs) { + return this._addUrlArgsToUrl(url); + } + return url; + }; + Configuration.prototype._addUrlArgsIfNecessaryToUrls = function (urls) { + if (this.options.urlArgs) { + for (var i = 0, len = urls.length; i < len; i++) { + urls[i] = this._addUrlArgsToUrl(urls[i]); + } + } + return urls; + }; + /** + * Transform a module id to a location. Appends .js to module ids + */ + Configuration.prototype.moduleIdToPaths = function (moduleId) { + if (this.nodeModulesMap[moduleId] === true) { + // This is a node module... + if (this.isBuild()) { + // ...and we are at build time, drop it + return ['empty:']; + } + else { + // ...and at runtime we create a `shortcut`-path + return ['node|' + moduleId]; + } + } + var result = moduleId; + var results; + if (!AMDLoader.Utilities.endsWith(result, '.js') && !AMDLoader.Utilities.isAbsolutePath(result)) { + results = this._applyPaths(result); + for (var i = 0, len = results.length; i < len; i++) { + if (this.isBuild() && results[i] === 'empty:') { + continue; + } + if (!AMDLoader.Utilities.isAbsolutePath(results[i])) { + results[i] = this.options.baseUrl + results[i]; + } + if (!AMDLoader.Utilities.endsWith(results[i], '.js') && !AMDLoader.Utilities.containsQueryString(results[i])) { + results[i] = results[i] + '.js'; + } + } + } + else { + if (!AMDLoader.Utilities.endsWith(result, '.js') && !AMDLoader.Utilities.containsQueryString(result)) { + result = result + '.js'; + } + results = [result]; + } + return this._addUrlArgsIfNecessaryToUrls(results); + }; + /** + * Transform a module id or url to a location. + */ + Configuration.prototype.requireToUrl = function (url) { + var result = url; + if (!AMDLoader.Utilities.isAbsolutePath(result)) { + result = this._applyPaths(result)[0]; + if (!AMDLoader.Utilities.isAbsolutePath(result)) { + result = this.options.baseUrl + result; + } + } + return this._addUrlArgsIfNecessaryToUrl(result); + }; + /** + * Flag to indicate if current execution is as part of a build. + */ + Configuration.prototype.isBuild = function () { + return this.options.isBuild; + }; + /** + * Test if module `moduleId` is expected to be defined multiple times + */ + Configuration.prototype.isDuplicateMessageIgnoredFor = function (moduleId) { + return this.ignoreDuplicateModulesMap.hasOwnProperty(moduleId); + }; + /** + * Get the configuration settings for the provided module id + */ + Configuration.prototype.getConfigForModule = function (moduleId) { + if (this.options.config) { + return this.options.config[moduleId]; + } + }; + /** + * Should errors be caught when executing module factories? + */ + Configuration.prototype.shouldCatchError = function () { + return this.options.catchError; + }; + /** + * Should statistics be recorded? + */ + Configuration.prototype.shouldRecordStats = function () { + return this.options.recordStats; + }; + /** + * Forward an error to the error handler. + */ + Configuration.prototype.onError = function (err) { + this.options.onError(err); + }; + return Configuration; + }()); + AMDLoader.Configuration = Configuration; +})(AMDLoader || (AMDLoader = {})); +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var AMDLoader; +(function (AMDLoader) { + /** + * Load `scriptSrc` only once (avoid multiple + + + + + + + + + \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/bootstrap/index.js b/src/vs/workbench/electron-browser/bootstrap/index.js new file mode 100644 index 0000000000..9094ebeded --- /dev/null +++ b/src/vs/workbench/electron-browser/bootstrap/index.js @@ -0,0 +1,268 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Warning: Do not use the `let` declarator in this file, it breaks our minification + +'use strict'; + +if (window.location.search.indexOf('prof-startup') >= 0) { + var profiler = require('v8-profiler'); + profiler.startProfiling('renderer', true); +} + +/*global window,document,define,Monaco_Loader_Init*/ + +const startTimer = require('../../../base/node/startupTimers').startTimer; +const path = require('path'); +const electron = require('electron'); +const remote = electron.remote; +const ipc = electron.ipcRenderer; + +process.lazyEnv = new Promise(function (resolve) { + const handle = setTimeout(function () { + resolve(); + console.warn('renderer did not receive lazyEnv in time'); + }, 10000); + ipc.once('vscode:acceptShellEnv', function (event, shellEnv) { + clearTimeout(handle); + assign(process.env, shellEnv); + resolve(process.env); + }); + ipc.send('vscode:fetchShellEnv', remote.getCurrentWindow().id); +}); + +function onError(error, enableDeveloperTools) { + if (enableDeveloperTools) { + remote.getCurrentWebContents().openDevTools(); + } + + console.error('[uncaught exception]: ' + error); + + if (error.stack) { + console.error(error.stack); + } +} + +function assign(destination, source) { + return Object.keys(source) + .reduce(function (r, key) { r[key] = source[key]; return r; }, destination); +} + +function parseURLQueryArgs() { + const search = window.location.search || ''; + + return search.split(/[?&]/) + .filter(function (param) { return !!param; }) + .map(function (param) { return param.split('='); }) + .filter(function (param) { return param.length === 2; }) + .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); +} + +function createScript(src, onload) { + const script = document.createElement('script'); + script.src = src; + script.addEventListener('load', onload); + + const head = document.getElementsByTagName('head')[0]; + head.insertBefore(script, head.lastChild); +} + +function uriFromPath(_path) { + var pathName = path.resolve(_path).replace(/\\/g, '/'); + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = '/' + pathName; + } + + return encodeURI('file://' + pathName); +} + +function registerListeners(enableDeveloperTools) { + + // Devtools & reload support + var listener; + if (enableDeveloperTools) { + const extractKey = function (e) { + return [ + e.ctrlKey ? 'ctrl-' : '', + e.metaKey ? 'meta-' : '', + e.altKey ? 'alt-' : '', + e.shiftKey ? 'shift-' : '', + e.keyCode + ].join(''); + }; + + const TOGGLE_DEV_TOOLS_KB = (process.platform === 'darwin' ? 'meta-alt-73' : 'ctrl-shift-73'); // mac: Cmd-Alt-I, rest: Ctrl-Shift-I + const RELOAD_KB = (process.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R + + listener = function (e) { + const key = extractKey(e); + if (key === TOGGLE_DEV_TOOLS_KB) { + remote.getCurrentWebContents().toggleDevTools(); + } else if (key === RELOAD_KB) { + remote.getCurrentWindow().reload(); + } + }; + window.addEventListener('keydown', listener); + } + + process.on('uncaughtException', function (error) { onError(error, enableDeveloperTools); }); + + return function () { + if (listener) { + window.removeEventListener('keydown', listener); + listener = void 0; + } + }; +} + +// {{SQL CARBON EDIT}} + +/* eslint-disable */ + +// SQL global imports +require('slickgrid/slick.core'); +const Slick = window.Slick; +require('slickgrid/slick.grid'); +require('slickgrid/slick.editors');; +require('reflect-metadata'); +require('zone.js'); + +const _ = require('underscore')._; + +/* eslint-enable */ +function main() { + const webFrame = require('electron').webFrame; + const args = parseURLQueryArgs(); + const configuration = JSON.parse(args['config'] || '{}') || {}; + + // Correctly inherit the parent's environment + assign(process.env, configuration.userEnv); + + // Get the nls configuration into the process.env as early as possible. + var nlsConfig = { availableLanguages: {} }; + const config = process.env['VSCODE_NLS_CONFIG']; + if (config) { + process.env['VSCODE_NLS_CONFIG'] = config; + try { + nlsConfig = JSON.parse(config); + } catch (e) { /*noop*/ } + } + + var locale = nlsConfig.availableLanguages['*'] || 'en'; + if (locale === 'zh-tw') { + locale = 'zh-Hant'; + } else if (locale === 'zh-cn') { + locale = 'zh-Hans'; + } + + window.document.documentElement.setAttribute('lang', locale); + + const enableDeveloperTools = (process.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; + const unbind = registerListeners(enableDeveloperTools); + + // disable pinch zoom & apply zoom level early to avoid glitches + const zoomLevel = configuration.zoomLevel; + webFrame.setVisualZoomLevelLimits(1, 1); + if (typeof zoomLevel === 'number' && zoomLevel !== 0) { + webFrame.setZoomLevel(zoomLevel); + } + + // {{SQL CARBON EDIT}} + // Load the loader and start loading the workbench + const appRoot = uriFromPath(configuration.appRoot); + const rootUrl = appRoot + '/out'; + + createScript(appRoot + '/node_modules/chart.js/dist/Chart.js', undefined); + + + function onLoader() { + window.nodeRequire = require.__$__nodeRequire; + + define('fs', ['original-fs'], function (originalFS) { return originalFS; }); // replace the patched electron fs with the original node fs for all AMD code + loaderTimer.stop(); + + window.MonacoEnvironment = {}; + + const onNodeCachedData = window.MonacoEnvironment.onNodeCachedData = []; + + // {{SQL CARBON EDIT}} + require.config({ + baseUrl: rootUrl, + 'vs/nls': nlsConfig, + recordStats: !!configuration.performance, + nodeCachedDataDir: configuration.nodeCachedDataDir, + onNodeCachedData: function () { onNodeCachedData.push(arguments); }, + nodeModules: [ + '@angular/common', + '@angular/core', + '@angular/forms', + '@angular/platform-browser', + '@angular/platform-browser-dynamic', + '@angular/router', + 'angular2-grid', + 'pretty-data', + 'html-query-plan', + 'ng2-charts/ng2-charts', + 'rangy', + 'rangy/lib/rangy-textrange', + 'rxjs/Observable', + 'rxjs/Subject', + 'rxjs/Observer' + ] + }); + + + if (nlsConfig.pseudo) { + require(['vs/nls'], function (nlsPlugin) { + nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); + }); + } + + // Perf Counters + const timers = window.MonacoEnvironment.timers = { + isInitialStartup: !!configuration.isInitialStartup, + hasAccessibilitySupport: !!configuration.accessibilitySupport, + start: configuration.perfStartTime, + appReady: configuration.perfAppReady, + windowLoad: configuration.perfWindowLoadTime, + beforeLoadWorkbenchMain: Date.now() + }; + + const workbenchMainTimer = startTimer('load:workbench.main'); + require([ + 'vs/workbench/workbench.main', + 'vs/nls!vs/workbench/workbench.main', + 'vs/css!vs/workbench/workbench.main' + ], function () { + workbenchMainTimer.stop(); + timers.afterLoadWorkbenchMain = Date.now(); + + process.lazyEnv.then(function () { + require('vs/workbench/electron-browser/main') + .startup(configuration) + .done(function () { + unbind(); // since the workbench is running, unbind our developer related listeners and let the workbench handle them + }, function (error) { + onError(error, enableDeveloperTools); + }); + }); + }); + } + + // In the bundled version the nls plugin is packaged with the loader so the NLS Plugins + // loads as soon as the loader loads. To be able to have pseudo translation + const loaderTimer = startTimer('load:loader'); + if (typeof Monaco_Loader_Init === 'function') { + const loader = Monaco_Loader_Init(); + //eslint-disable-next-line no-global-assign + define = loader.define; require = loader.require; + onLoader(); + + } else { + createScript(rootUrl + '/vs/loader.js', onLoader); + } +} + +main(); diff --git a/src/vs/workbench/electron-browser/bootstrap/preload.js b/src/vs/workbench/electron-browser/bootstrap/preload.js new file mode 100644 index 0000000000..a873b68809 --- /dev/null +++ b/src/vs/workbench/electron-browser/bootstrap/preload.js @@ -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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +(function() { + function getConfig() { + const queryParams = window.location.search.substring(1).split('&'); + for (var i = 0; i < queryParams.length; i++) { + var kv = queryParams[i].split('='); + if (kv[0] === 'config' && kv[1]) { + return JSON.parse(decodeURIComponent(kv[1])); + } + } + return {}; + } + try { + const config = getConfig(); + const document = window.document; + + // sets the base theme class ('vs', 'vs-dark', 'hc-black') + const baseTheme = config.baseTheme || 'vs'; + document.body.className = 'monaco-shell ' + baseTheme; + + // adds a stylesheet with the backgrdound color + var backgroundColor = config.backgroundColor; + if (!backgroundColor) { + backgroundColor = baseTheme === 'hc-black' ? '#000000' : (baseTheme === 'vs' ? '#FFFFFF' : '#1E1E1E'); + } + const foregroundColor = baseTheme === 'hc-black' ? '#FFFFFF' : (baseTheme === 'vs' ? '#6C6C6C' : '#CCCCCC'); + const style = document.createElement('style'); + style.innerHTML = '.monaco-shell { background-color:' + backgroundColor + '; color:' + foregroundColor + '; }'; + document.head.appendChild(style); + + } catch (error) { + console.error(error); + } +})(); \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts new file mode 100644 index 0000000000..b4f31e24aa --- /dev/null +++ b/src/vs/workbench/electron-browser/commands.ts @@ -0,0 +1,423 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { NoEditorsVisibleContext, InZenModeContext } from 'vs/workbench/electron-browser/workbench'; +import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { IListService, ListFocusContext } from 'vs/platform/list/browser/listService'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import errors = require('vs/base/common/errors'); +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import URI from 'vs/base/common/uri'; +import { IEditorOptions, Position as EditorPosition } from 'vs/platform/editor/common/editor'; + +// --- List Commands + +export function registerCommands(): void { + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusDown', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.DownArrow, + mac: { + primary: KeyCode.DownArrow, + secondary: [KeyMod.WinCtrl | KeyCode.KEY_N] + }, + handler: (accessor, arg2) => { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + const count = typeof arg2 === 'number' ? arg2 : 1; + + // List + if (focused instanceof List) { + const list = focused; + + list.focusNext(count); + list.reveal(list.getFocus()[0]); + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusNext(count, { origin: 'keyboard' }); + tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError); + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusUp', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.UpArrow, + mac: { + primary: KeyCode.UpArrow, + secondary: [KeyMod.WinCtrl | KeyCode.KEY_P] + }, + handler: (accessor, arg2) => { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + const count = typeof arg2 === 'number' ? arg2 : 1; + + // List + if (focused instanceof List) { + const list = focused; + + list.focusPrevious(count); + list.reveal(list.getFocus()[0]); + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusPrevious(count, { origin: 'keyboard' }); + tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError); + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.collapse', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.LeftArrow, + mac: { + primary: KeyCode.LeftArrow, + secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow] + }, + handler: (accessor) => { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + + // Tree only + if (focused && !(focused instanceof List)) { + const tree = focused; + const focus = tree.getFocus(); + + tree.collapse(focus).then(didCollapse => { + if (focus && !didCollapse) { + tree.focusParent({ origin: 'keyboard' }); + + return tree.reveal(tree.getFocus()); + } + + return void 0; + }).done(null, errors.onUnexpectedError); + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.expand', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.RightArrow, + handler: (accessor) => { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + + // Tree only + if (focused && !(focused instanceof List)) { + const tree = focused; + const focus = tree.getFocus(); + + tree.expand(focus).then(didExpand => { + if (focus && !didExpand) { + tree.focusFirstChild({ origin: 'keyboard' }); + + return tree.reveal(tree.getFocus()); + } + + return void 0; + }).done(null, errors.onUnexpectedError); + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusPageUp', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.PageUp, + handler: (accessor) => { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + + // List + if (focused instanceof List) { + const list = focused; + + list.focusPreviousPage(); + list.reveal(list.getFocus()[0]); + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusPreviousPage({ origin: 'keyboard' }); + tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError); + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusPageDown', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.PageDown, + handler: (accessor) => { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + + // List + if (focused instanceof List) { + const list = focused; + + list.focusNextPage(); + list.reveal(list.getFocus()[0]); + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusNextPage({ origin: 'keyboard' }); + tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError); + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusFirst', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.Home, + handler: accessor => listFocusFirst(accessor) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusFirstChild', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: null, + handler: accessor => listFocusFirst(accessor, { fromFocused: true }) + }); + + function listFocusFirst(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + + // List + if (focused instanceof List) { + const list = focused; + + list.setFocus([0]); + list.reveal(0); + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusFirst({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : void 0); + tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError); + } + } + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusLast', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.End, + handler: accessor => listFocusLast(accessor) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusLastChild', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: null, + handler: accessor => listFocusLast(accessor, { fromFocused: true }) + }); + + function listFocusLast(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + + // List + if (focused instanceof List) { + const list = focused; + + list.setFocus([list.length - 1]); + list.reveal(list.length - 1); + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusLast({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : void 0); + tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError); + } + } + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.select', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.Enter, + secondary: [KeyMod.CtrlCmd | KeyCode.Enter], + mac: { + primary: KeyCode.Enter, + secondary: [KeyMod.CtrlCmd | KeyCode.Enter, KeyMod.CtrlCmd | KeyCode.DownArrow] + }, + handler: (accessor) => { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + + // List + if (focused instanceof List) { + const list = focused; + list.setSelection(list.getFocus()); + list.open(list.getFocus()); + } + + // Tree + else if (focused) { + const tree = focused; + const focus = tree.getFocus(); + + if (focus) { + tree.setSelection([focus], { origin: 'keyboard' }); + } + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.toggleExpand', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.Space, + handler: (accessor) => { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + + // Tree only + if (focused && !(focused instanceof List)) { + const tree = focused; + const focus = tree.getFocus(); + + if (focus) { + tree.toggleExpansion(focus); + } + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.clear', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ListFocusContext, + primary: KeyCode.Escape, + handler: (accessor) => { + const listService = accessor.get(IListService); + const focused = listService.getFocused(); + + // Tree only + if (focused && !(focused instanceof List)) { + const tree = focused; + + if (tree.getSelection().length) { + tree.clearSelection({ origin: 'keyboard' }); + + return void 0; + } + + if (tree.getFocus()) { + tree.clearFocus({ origin: 'keyboard' }); + + return void 0; + } + } + } + }); + + // --- commands + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.action.closeWindow', // close the window when the last editor is closed by reusing the same keybinding + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: NoEditorsVisibleContext, + primary: KeyMod.CtrlCmd | KeyCode.KEY_W, + handler: accessor => { + const windowService = accessor.get(IWindowService); + windowService.closeWindow(); + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.action.exitZenMode', + weight: CommonEditorRegistry.commandWeight(-1000), + handler(accessor: ServicesAccessor, configurationOrName: any) { + const partService = accessor.get(IPartService); + partService.toggleZenMode(); + }, + when: InZenModeContext, + primary: KeyChord(KeyCode.Escape, KeyCode.Escape) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.action.quit', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + handler(accessor: ServicesAccessor) { + const windowsService = accessor.get(IWindowsService); + windowsService.quit(); + }, + when: void 0, + primary: KeyMod.CtrlCmd | KeyCode.KEY_Q, + win: { primary: void 0 } + }); + + CommandsRegistry.registerCommand('_workbench.diff', function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorPosition]) { + const editorService = accessor.get(IWorkbenchEditorService); + let [leftResource, rightResource, label, description, options, position] = args; + + if (!options || typeof options !== 'object') { + options = { + preserveFocus: false + }; + } + + if (!label) { + label = nls.localize('diffLeftRightLabel', "{0} ⟷ {1}", leftResource.toString(true), rightResource.toString(true)); + } + + return editorService.openEditor({ leftResource, rightResource, label, description, options }, position).then(() => { + return void 0; + }); + }); + + CommandsRegistry.registerCommand('_workbench.open', function (accessor: ServicesAccessor, args: [URI, number]) { + const editorService = accessor.get(IWorkbenchEditorService); + const [resource, column] = args; + + return editorService.openEditor({ resource }, column).then(() => { + return void 0; + }); + }); +} diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts new file mode 100644 index 0000000000..eb4b36c0a1 --- /dev/null +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -0,0 +1,398 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Registry } from 'vs/platform/registry/common/platform'; +import nls = require('vs/nls'); +import product from 'vs/platform/node/product'; +import * as os from 'os'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; +import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; +import { CloseEditorAction, KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, ReportIssueAction, ReportPerformanceIssueAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, CloseMessagesAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowStartupPerformance, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction } from 'vs/workbench/electron-browser/actions'; +import { MessagesVisibleContext } from 'vs/workbench/electron-browser/workbench'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { registerCommands } from 'vs/workbench/electron-browser/commands'; +import { AddRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction } from 'vs/workbench/browser/actions/workspaceActions'; + +// Contribute Commands +registerCommands(); + +// Contribute Global Actions +const viewCategory = nls.localize('view', "View"); +const helpCategory = nls.localize('help', "Help"); +const fileCategory = nls.localize('file', "File"); +const workbenchActionsRegistry = Registry.as(Extensions.WorkbenchActions); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NewWindowAction, NewWindowAction.ID, NewWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_N }), 'New Window'); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseCurrentWindowAction, CloseCurrentWindowAction.ID, CloseCurrentWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W }), 'Close Window'); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SwitchWindow, SwitchWindow.ID, SwitchWindow.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_W } }), 'Switch Window...'); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(QuickSwitchWindow, QuickSwitchWindow.ID, QuickSwitchWindow.LABEL), 'Quick Switch Window...'); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory); +if (!!product.reportIssueUrl) { + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReportIssueAction, ReportIssueAction.ID, ReportIssueAction.LABEL), 'Help: Report Issues', helpCategory); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReportPerformanceIssueAction, ReportPerformanceIssueAction.ID, ReportPerformanceIssueAction.LABEL), 'Help: Report Performance Issue', helpCategory); +} +if (KeybindingsReferenceAction.AVAILABLE) { + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(KeybindingsReferenceAction, KeybindingsReferenceAction.ID, KeybindingsReferenceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_R) }), 'Help: Keyboard Shortcuts Reference', helpCategory); +} +if (OpenDocumentationUrlAction.AVAILABLE) { + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenDocumentationUrlAction, OpenDocumentationUrlAction.ID, OpenDocumentationUrlAction.LABEL), 'Help: Documentation', helpCategory); +} +if (OpenIntroductoryVideosUrlAction.AVAILABLE) { + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenIntroductoryVideosUrlAction, OpenIntroductoryVideosUrlAction.ID, OpenIntroductoryVideosUrlAction.LABEL), 'Help: Introductory Videos', helpCategory); +} +if (OpenTipsAndTricksUrlAction.AVAILABLE) { + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); +} +workbenchActionsRegistry.registerWorkbenchAction( + new SyncActionDescriptor(ZoomInAction, ZoomInAction.ID, ZoomInAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.US_EQUAL, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_EQUAL, KeyMod.CtrlCmd | KeyCode.NUMPAD_ADD] + }), 'View: Zoom In', viewCategory); +workbenchActionsRegistry.registerWorkbenchAction( + new SyncActionDescriptor(ZoomOutAction, ZoomOutAction.ID, ZoomOutAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS, KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT], + linux: { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT] } + }), 'View: Zoom Out', viewCategory +); +workbenchActionsRegistry.registerWorkbenchAction( + new SyncActionDescriptor(ZoomResetAction, ZoomResetAction.ID, ZoomResetAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.NUMPAD_0 + }), 'View: Reset Zoom', viewCategory +); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseMessagesAction, CloseMessagesAction.ID, CloseMessagesAction.LABEL, { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] }, MessagesVisibleContext), 'Close Notification Messages'); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorAction, CloseEditorAction.ID, CloseEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_W, win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] } }), 'View: Close Editor', viewCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory); +if (isWindows || isLinux) { + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMenuBarAction, ToggleMenuBarAction.ID, ToggleMenuBarAction.LABEL), 'View: Toggle Menu Bar', viewCategory); +} +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateUpAction, NavigateUpAction.ID, NavigateUpAction.LABEL, null), 'View: Move to the View Above', viewCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateDownAction, NavigateDownAction.ID, NavigateDownAction.LABEL, null), 'View: Move to the View Below', viewCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLeftAction, NavigateLeftAction.ID, NavigateLeftAction.LABEL, null), 'View: Move to the View on the Left', viewCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateRightAction, NavigateRightAction.ID, NavigateRightAction.LABEL, null), 'View: Move to the View on the Right', viewCategory); + +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(IncreaseViewSizeAction, IncreaseViewSizeAction.ID, IncreaseViewSizeAction.LABEL, null), 'View: Increase Current View Size', viewCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(DecreaseViewSizeAction, DecreaseViewSizeAction.ID, DecreaseViewSizeAction.LABEL, null), 'View: Decrease Current View Size', viewCategory); + +// TODO@Ben multi root +if (product.quality !== 'stable') { + const workspacesCategory = nls.localize('workspaces', "Workspaces"); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SaveWorkspaceAsAction, SaveWorkspaceAsAction.ID, SaveWorkspaceAsAction.LABEL), 'Workspaces: Save Workspace...', workspacesCategory); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceConfigFileAction, OpenWorkspaceConfigFileAction.ID, OpenWorkspaceConfigFileAction.LABEL), 'Workspaces: Open Workspace Configuration File', workspacesCategory); +} + +// Developer related actions +const developerCategory = nls.localize('developer', "Developer"); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowStartupPerformance, ShowStartupPerformance.ID, ShowStartupPerformance.LABEL), 'Developer: Startup Performance', developerCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); + +// Configuration: Workbench +const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + +let workbenchProperties: { [path: string]: IJSONSchema; } = { + 'workbench.editor.showTabs': { + 'type': 'boolean', + 'description': nls.localize('showEditorTabs', "Controls if opened editors should show in tabs or not."), + 'default': true + }, + 'workbench.editor.tabCloseButton': { + 'type': 'string', + 'enum': ['left', 'right', 'off'], + 'default': 'right', + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons or disables them when set to 'off'.") + }, + 'workbench.editor.showIcons': { + 'type': 'boolean', + 'description': nls.localize('showIcons', "Controls if opened editors should show with an icon or not. This requires an icon theme to be enabled as well."), + 'default': true + }, + 'workbench.editor.enablePreview': { + 'type': 'boolean', + 'description': nls.localize('enablePreview', "Controls if opened editors show as preview. Preview editors are reused until they are kept (e.g. via double click or editing)."), + 'default': true + }, + 'workbench.editor.enablePreviewFromQuickOpen': { + 'type': 'boolean', + 'description': nls.localize('enablePreviewFromQuickOpen', "Controls if opened editors from Quick Open show as preview. Preview editors are reused until they are kept (e.g. via double click or editing)."), + 'default': true + }, + 'workbench.editor.openPositioning': { + 'type': 'string', + 'enum': ['left', 'right', 'first', 'last'], + 'default': 'right', + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select 'left' or 'right' to open editors to the left or right of the current active one. Select 'first' or 'last' to open editors independently from the currently active one.") + }, + 'workbench.editor.revealIfOpen': { + 'type': 'boolean', + 'description': nls.localize('revealIfOpen', "Controls if an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, e.g. when forcing an editor to open in a specific group or to the side of the currently active group."), + 'default': false + }, + 'workbench.commandPalette.history': { + 'type': 'number', + 'description': nls.localize('commandHistory', "Controls if the number of recently used commands to keep in history for the command palette. Set to 0 to disable command history."), + 'default': 50 + }, + 'workbench.commandPalette.preserveInput': { + 'type': 'boolean', + 'description': nls.localize('preserveInput', "Controls if the last typed input to the command palette should be restored when opening it the next time."), + 'default': false + }, + 'workbench.quickOpen.closeOnFocusLost': { + 'type': 'boolean', + 'description': nls.localize('closeOnFocusLost', "Controls if Quick Open should close automatically once it loses focus."), + 'default': true + }, + 'workbench.settings.openDefaultSettings': { + 'type': 'boolean', + 'description': nls.localize('openDefaultSettings', "Controls if opening settings also opens an editor showing all default settings."), + 'default': true + }, + 'workbench.sideBar.location': { + 'type': 'string', + 'enum': ['left', 'right'], + 'default': 'left', + 'description': nls.localize('sideBarLocation', "Controls the location of the sidebar. It can either show on the left or right of the workbench.") + }, + 'workbench.statusBar.visible': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('statusBarVisibility', "Controls the visibility of the status bar at the bottom of the workbench.") + }, + 'workbench.activityBar.visible': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('activityBarVisibility', "Controls the visibility of the activity bar in the workbench.") + }, + 'workbench.editor.closeOnFileDelete': { + 'type': 'boolean', + 'description': nls.localize('closeOnFileDelete', "Controls if editors showing a file should close automatically when the file is deleted or renamed by some other process. Disabling this will keep the editor open as dirty on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."), + 'default': true + } +}; + +if (isMacintosh) { + workbenchProperties['workbench.fontAliasing'] = { + 'type': 'string', + 'enum': ['default', 'antialiased', 'none'], + 'default': 'default', + 'description': + nls.localize('fontAliasing', + `Controls font aliasing method in the workbench. +- default: Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text +- antialiased: Smooth the font on the level of the pixel, as opposed to the subpixel. Can make the font appear lighter overall +- none: Disables font smoothing. Text will show with jagged sharp edges`), + 'enumDescriptions': [ + nls.localize('workbench.fontAliasing.default', "Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text."), + nls.localize('workbench.fontAliasing.antialiased', "Smooth the font on the level of the pixel, as opposed to the subpixel. Can make the font appear lighter overall."), + nls.localize('workbench.fontAliasing.none', "Disables font smoothing. Text will show with jagged sharp edges.") + ], + }; + + workbenchProperties['workbench.editor.swipeToNavigate'] = { + 'type': 'boolean', + 'description': nls.localize('swipeToNavigate', "Navigate between open files using three-finger swipe horizontally."), + 'default': false + }; +} + + +configurationRegistry.registerConfiguration({ + 'id': 'workbench', + 'order': 7, + 'title': nls.localize('workbenchConfigurationTitle', "Workbench"), + 'type': 'object', + 'properties': workbenchProperties +}); + + +// Configuration: Window +let properties: { [path: string]: IJSONSchema; } = { + 'window.openFilesInNewWindow': { + 'type': 'string', + 'enum': ['on', 'off', 'default'], + 'enumDescriptions': [ + nls.localize('window.openFilesInNewWindow.on', "Files will open in a new window"), + nls.localize('window.openFilesInNewWindow.off', "Files will open in the window with the files' folder open or the last active window"), + nls.localize('window.openFilesInNewWindow.default', "Files will open in the window with the files' folder open or the last active window unless opened via the dock or from finder (macOS only)") + ], + 'default': 'off', + 'description': + nls.localize('openFilesInNewWindow', + `Controls if files should open in a new window. +- default: files will open in the window with the files' folder open or the last active window unless opened via the dock or from finder (macOS only) +- on: files will open in a new window +- off: files will open in the window with the files' folder open or the last active window +Note that there can still be cases where this setting is ignored (e.g. when using the -new-window or -reuse-window command line option).` + ) + }, + 'window.openFoldersInNewWindow': { + 'type': 'string', + 'enum': ['on', 'off', 'default'], + 'enumDescriptions': [ + nls.localize('window.openFoldersInNewWindow.on', "Folders will open in a new window"), + nls.localize('window.openFoldersInNewWindow.off', "Folders will replace the last active window"), + nls.localize('window.openFoldersInNewWindow.default', "Folders will open in a new window unless a folder is picked from within the application (e.g. via the File menu)") + ], + 'default': 'default', + 'description': nls.localize('openFoldersInNewWindow', + `Controls if folders should open in a new window or replace the last active window. +- default: folders will open in a new window unless a folder is picked from within the application (e.g. via the File menu) +- on: folders will open in a new window +- off: folders will replace the last active window +Note that there can still be cases where this setting is ignored (e.g. when using the -new-window or -reuse-window command line option).` + ) + }, + 'window.restoreWindows': { + 'type': 'string', + 'enum': ['all', 'folders', 'one', 'none'], + 'enumDescriptions': [ + nls.localize('window.reopenFolders.all', "Reopen all windows."), + nls.localize('window.reopenFolders.folders', "Reopen all folders. Empty workspaces will not be restored."), + nls.localize('window.reopenFolders.one', "Reopen the last active window."), + nls.localize('window.reopenFolders.none', "Never reopen a window. Always start with an empty one.") + ], + 'default': 'one', + 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after a restart. Select 'none' to always start with an empty workspace, 'one' to reopen the last window you worked on, 'folders' to reopen all windows that had folders opened or 'all' to reopen all windows of your last session.") + }, + 'window.restoreFullscreen': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('restoreFullscreen', "Controls if a window should restore to full screen mode if it was exited in full screen mode.") + }, + 'window.zoomLevel': { + 'type': 'number', + 'default': 0, + 'description': nls.localize('zoomLevel', "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.") + }, + 'window.title': { + 'type': 'string', + 'default': isMacintosh ? '${activeEditorShort}${separator}${rootName}' : '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}', + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'title' }, + `Controls the window title based on the active editor. Variables are substituted based on the context: +\${activeEditorShort}: e.g. myFile.txt +\${activeEditorMedium}: e.g. myFolder/myFile.txt +\${activeEditorLong}: e.g. /Users/Development/myProject/myFolder/myFile.txt +\${folderName}: e.g. myFolder +\${folderPath}: e.g. /Users/Development/myFolder +\${rootName}: e.g. myFolder1, myFolder2, myFolder3 +\${rootPath}: e.g. /Users/Development/myWorkspace +\${appName}: e.g. VS Code +\${dirty}: a dirty indicator if the active editor is dirty +\${separator}: a conditional separator (" - ") that only shows when surrounded by variables with values`) + }, + 'window.newWindowDimensions': { + 'type': 'string', + 'enum': ['default', 'inherit', 'maximized', 'fullscreen'], + 'enumDescriptions': [ + nls.localize('window.newWindowDimensions.default', "Open new windows in the center of the screen."), + nls.localize('window.newWindowDimensions.inherit', "Open new windows with same dimension as last active one."), + nls.localize('window.newWindowDimensions.maximized', "Open new windows maximized."), + nls.localize('window.newWindowDimensions.fullscreen', "Open new windows in full screen mode.") + ], + 'default': 'default', + 'description': nls.localize('newWindowDimensions', "Controls the dimensions of opening a new window when at least one window is already opened. By default, a new window will open in the center of the screen with small dimensions. When set to 'inherit', the window will get the same dimensions as the last window that was active. When set to 'maximized', the window will open maximized and fullscreen if configured to 'fullscreen'. Note that this setting does not have an impact on the first window that is opened. The first window will always restore the size and location as you left it before closing.") + }, + 'window.closeWhenEmpty': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('closeWhenEmpty', "Controls if closing the last editor should also close the window. This setting only applies for windows that do not show folders.") + } +}; + +if (isWindows || isLinux) { + properties['window.menuBarVisibility'] = { + 'type': 'string', + 'enum': ['default', 'visible', 'toggle', 'hidden'], + 'enumDescriptions': [ + nls.localize('window.menuBarVisibility.default', "Menu is only hidden in full screen mode."), + nls.localize('window.menuBarVisibility.visible', "Menu is always visible even in full screen mode."), + nls.localize('window.menuBarVisibility.toggle', "Menu is hidden but can be displayed via Alt key."), + nls.localize('window.menuBarVisibility.hidden', "Menu is always hidden.") + ], + 'default': 'default', + 'description': nls.localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and a single press of the Alt key will show it. By default, the menu bar will be visible, unless the window is full screen.") + }; + properties['window.enableMenuBarMnemonics'] = { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('enableMenuBarMnemonics', "If enabled, the main menus can be opened via Alt-key shortcuts. Disabling mnemonics allows to bind these Alt-key shortcuts to editor commands instead.") + }; +} + +if (isWindows) { + properties['window.autoDetectHighContrast'] = { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('autoDetectHighContrast', "If enabled, will automatically change to high contrast theme if Windows is using a high contrast theme, and to dark theme when switching away from a Windows high contrast theme."), + }; +} + +if (isMacintosh) { + properties['window.titleBarStyle'] = { + 'type': 'string', + 'enum': ['native', 'custom'], + 'default': 'custom', + 'description': nls.localize('titleBarStyle', "Adjust the appearance of the window title bar. Changes require a full restart to apply.") + }; + + // macOS Sierra (10.12.x = darwin 16.x) and electron > 1.4.6 only + if (os.release().indexOf('16.') === 0 && process.versions.electron !== '1.4.6') { + properties['window.nativeTabs'] = { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('window.nativeTabs', "Enables macOS Sierra window tabs. Note that changes require a full restart to apply and that native tabs will disable a custom title bar style if configured.") + }; + } +} + +configurationRegistry.registerConfiguration({ + 'id': 'window', + 'order': 8, + 'title': nls.localize('windowConfigurationTitle', "Window"), + 'type': 'object', + 'properties': properties +}); + +// Configuration: Zen Mode +configurationRegistry.registerConfiguration({ + 'id': 'zenMode', + 'order': 9, + 'title': nls.localize('zenModeConfigurationTitle', "Zen Mode"), + 'type': 'object', + 'properties': { + 'zenMode.fullScreen': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.fullScreen', "Controls if turning on Zen Mode also puts the workbench into full screen mode.") + }, + 'zenMode.hideTabs': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.hideTabs', "Controls if turning on Zen Mode also hides workbench tabs.") + }, + 'zenMode.hideStatusBar': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.hideStatusBar', "Controls if turning on Zen Mode also hides the status bar at the bottom of the workbench.") + }, + 'zenMode.hideActivityBar': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.hideActivityBar', "Controls if turning on Zen Mode also hides the activity bar at the left of the workbench.") + }, + 'zenMode.restore': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('zenMode.restore', "Controls if a window should restore to zen mode if it was exited in zen mode.") + } + } +}); \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts new file mode 100644 index 0000000000..6924e1e969 --- /dev/null +++ b/src/vs/workbench/electron-browser/main.ts @@ -0,0 +1,238 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { WorkbenchShell } from 'vs/workbench/electron-browser/shell'; +import * as browser from 'vs/base/browser/browser'; +import { domContentLoaded } from 'vs/base/browser/dom'; +import errors = require('vs/base/common/errors'); +import comparer = require('vs/base/common/comparers'); +import platform = require('vs/base/common/platform'); +import paths = require('vs/base/common/paths'); +import uri from 'vs/base/common/uri'; +import strings = require('vs/base/common/strings'); +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { EmptyWorkspaceServiceImpl, WorkspaceServiceImpl, WorkspaceService } from 'vs/workbench/services/configuration/node/configuration'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { realpath, readFile, writeFile } from 'vs/base/node/pfs'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import path = require('path'); +import gracefulFs = require('graceful-fs'); +import { IInitData } from 'vs/workbench/services/timer/common/timerService'; +import { TimerService } from 'vs/workbench/services/timer/node/timerService'; +import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/common/windows'; +import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { StorageService, inMemoryLocalStorageInstance } from 'vs/platform/storage/common/storageService'; +import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser'; +import { webFrame, remote } from 'electron'; +import { UpdateChannelClient } from 'vs/platform/update/common/updateIpc'; +import { IUpdateService } from 'vs/platform/update/common/update'; +import { URLChannelClient } from 'vs/platform/url/common/urlIpc'; +import { IURLService } from 'vs/platform/url/common/url'; +import { WorkspacesChannelClient } from 'vs/platform/workspaces/common/workspacesIpc'; +import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { CredentialsChannelClient } from 'vs/platform/credentials/node/credentialsIpc'; +import { migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/migration'; + +import fs = require('fs'); +gracefulFs.gracefulify(fs); // enable gracefulFs + +const currentWindowId = remote.getCurrentWindow().id; + +export function startup(configuration: IWindowConfiguration): TPromise { + + // Ensure others can listen to zoom level changes + browser.setZoomFactor(webFrame.getZoomFactor()); + + // See https://github.com/Microsoft/vscode/issues/26151 + // Can be trusted because we are not setting it ourselves. + browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */); + + browser.setFullscreen(!!configuration.fullscreen); + + KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(configuration.isISOKeyboard); + + browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled); + + // Setup Intl + comparer.setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })); + + // Open workbench + return openWorkbench(configuration); +} + +function openWorkbench(configuration: IWindowConfiguration): TPromise { + const mainProcessClient = new ElectronIPCClient(String(`window${currentWindowId}`)); + const mainServices = createMainProcessServices(mainProcessClient); + + const environmentService = new EnvironmentService(configuration, configuration.execPath); + + // Since the configuration service is one of the core services that is used in so many places, we initialize it + // right before startup of the workbench shell to have its data ready for consumers + return createAndInitializeWorkspaceService(configuration, environmentService, mainServices.get(IWorkspacesService)).then(workspaceService => { + const timerService = new TimerService((window).MonacoEnvironment.timers as IInitData, !workspaceService.hasWorkspace()); + const storageService = createStorageService(configuration, workspaceService, environmentService); + + timerService.beforeDOMContentLoaded = Date.now(); + + return domContentLoaded().then(() => { + timerService.afterDOMContentLoaded = Date.now(); + + // Open Shell + timerService.beforeWorkbenchOpen = Date.now(); + const shell = new WorkbenchShell(document.body, { + contextService: workspaceService, + configurationService: workspaceService, + environmentService, + timerService, + storageService + }, mainServices, configuration); + shell.open(); + + // Inform user about loading issues from the loader + (self).require.config({ + onError: (err: any) => { + if (err.errorCode === 'load') { + shell.onUnexpectedError(loaderError(err)); + } + } + }); + }); + }); +} + +function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService, workspacesService: IWorkspacesService): TPromise { + return migrateWorkspaceId(configuration).then(() => { + return validateWorkspacePath(configuration).then(() => { + let workspaceService: WorkspaceServiceImpl | EmptyWorkspaceServiceImpl; + if (configuration.workspace || configuration.folderPath) { + workspaceService = new WorkspaceServiceImpl(configuration.workspace || configuration.folderPath, environmentService, workspacesService); + } else { + workspaceService = new EmptyWorkspaceServiceImpl(environmentService); + } + + return workspaceService.initialize().then(() => workspaceService, error => new EmptyWorkspaceServiceImpl(environmentService)); + }); + }); +} + +// TODO@Ben migration +function migrateWorkspaceId(configuration: IWindowConfiguration): TPromise { + if (!configuration.workspace || !configuration.workspace.configPath) { + return TPromise.as(null); + } + + return readFile(configuration.workspace.configPath).then(data => { + try { + const raw = JSON.parse(data.toString()); + if (raw.id) { + const previousWorkspaceId = raw.id; + delete raw.id; + + migrateStorageToMultiRootWorkspace(uri.from({ path: previousWorkspaceId, scheme: 'root' }).toString(), configuration.workspace, window.localStorage); + + return writeFile(configuration.workspace.configPath, JSON.stringify(raw, null, '\t')); + } + } catch (error) { }; + + return void 0; + }).then(() => void 0, () => void 0); +} + +function validateWorkspacePath(configuration: IWindowConfiguration): TPromise { + if (!configuration.folderPath) { + return TPromise.as(null); + } + + return realpath(configuration.folderPath).then(realFolderPath => { + + // for some weird reason, node adds a trailing slash to UNC paths + // we never ever want trailing slashes as our workspace path unless + // someone opens root ("/"). + // See also https://github.com/nodejs/io.js/issues/1765 + if (paths.isUNC(realFolderPath) && strings.endsWith(realFolderPath, paths.nativeSep)) { + realFolderPath = strings.rtrim(realFolderPath, paths.nativeSep); + } + + // update config + configuration.folderPath = realFolderPath; + }, error => { + errors.onUnexpectedError(error); + + return null; // treat invalid paths as empty workspace + }); +} + +function createStorageService(configuration: IWindowConfiguration, workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageService { + const workspace = workspaceService.getWorkspace(); + + let workspaceId: string; + let secondaryWorkspaceId: number; + + // in multi root workspace mode we use the provided ID as key for workspace storage + if (workspaceService.hasMultiFolderWorkspace()) { + workspaceId = uri.from({ path: workspace.id, scheme: 'root' }).toString(); + } + + // in single folder mode we use the path of the opened folder as key for workspace storage + // the ctime is used as secondary workspace id to clean up stale UI state if necessary + else if (workspaceService.hasFolderWorkspace()) { + const legacyWorkspace = workspaceService.getLegacyWorkspace(); + workspaceId = legacyWorkspace.resource.toString(); + secondaryWorkspaceId = legacyWorkspace.ctime; + } + + // finaly, if we do not have a workspace open, we need to find another identifier for the window to store + // workspace UI state. if we have a backup path in the configuration we can use that because this + // will be a unique identifier per window that is stable between restarts as long as there are + // dirty files in the workspace. + // We use basename() to produce a short identifier, we do not need the full path. We use a custom + // scheme so that we can later distinguish these identifiers from the workspace one. + else if (configuration.backupPath) { + workspaceId = uri.from({ path: path.basename(configuration.backupPath), scheme: 'empty' }).toString(); + } + + const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests! + const storage = disableStorage ? inMemoryLocalStorageInstance : window.localStorage; + + return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId); +} + +function createMainProcessServices(mainProcessClient: ElectronIPCClient): ServiceCollection { + const serviceCollection = new ServiceCollection(); + + const windowsChannel = mainProcessClient.getChannel('windows'); + serviceCollection.set(IWindowsService, new WindowsChannelClient(windowsChannel)); + + const updateChannel = mainProcessClient.getChannel('update'); + serviceCollection.set(IUpdateService, new SyncDescriptor(UpdateChannelClient, updateChannel)); + + const urlChannel = mainProcessClient.getChannel('url'); + serviceCollection.set(IURLService, new SyncDescriptor(URLChannelClient, urlChannel, currentWindowId)); + + const workspacesChannel = mainProcessClient.getChannel('workspaces'); + serviceCollection.set(IWorkspacesService, new WorkspacesChannelClient(workspacesChannel)); + + const credentialsChannel = mainProcessClient.getChannel('credentials'); + serviceCollection.set(ICredentialsService, new CredentialsChannelClient(credentialsChannel)); + + return serviceCollection; +} + +function loaderError(err: Error): Error { + if (platform.isWeb) { + return new Error(nls.localize('loaderError', "Failed to load a required file. Either you are no longer connected to the internet or the server you are connected to is offline. Please refresh the browser to try again.")); + } + + return new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err))); +} diff --git a/src/vs/workbench/electron-browser/media/actions.css b/src/vs/workbench/electron-browser/media/actions.css new file mode 100644 index 0000000000..76334008ff --- /dev/null +++ b/src/vs/workbench/electron-browser/media/actions.css @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.vs .action-remove-from-recently-opened { + background: url("remove.svg") center center no-repeat; +} + +.vs-dark .action-remove-from-recently-opened, +.hc-black .action-remove-from-recently-opened { + background: url("remove-dark.svg") center center no-repeat; +} \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/media/clear.svg b/src/vs/workbench/electron-browser/media/clear.svg new file mode 100644 index 0000000000..c2f3e02710 --- /dev/null +++ b/src/vs/workbench/electron-browser/media/clear.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/media/remove-dark.svg b/src/vs/workbench/electron-browser/media/remove-dark.svg new file mode 100644 index 0000000000..751e89b3b0 --- /dev/null +++ b/src/vs/workbench/electron-browser/media/remove-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/media/remove.svg b/src/vs/workbench/electron-browser/media/remove.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/vs/workbench/electron-browser/media/remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/media/shell.css b/src/vs/workbench/electron-browser/media/shell.css new file mode 100644 index 0000000000..1c55f1c5ce --- /dev/null +++ b/src/vs/workbench/electron-browser/media/shell.css @@ -0,0 +1,173 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-shell { + height: 100%; + width: 100%; + margin: 0; + padding: 0; + overflow: hidden; + font-size: 11px; + -webkit-user-select: none; +} + +/* Font Families (with CJK support) */ + +.monaco-shell { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; } +.monaco-shell:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; } +.monaco-shell:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif; } +.monaco-shell:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Meiryo", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif; } +.monaco-shell:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; } + +@-webkit-keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } +@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } + +.monaco-shell img { + border: 0; +} + +.monaco-shell label { + cursor: pointer; +} + +.monaco-shell a { + text-decoration: none; +} + +.monaco-shell a:active { + color: inherit; + background-color: inherit; +} + +.monaco-shell a.plain { + color: inherit; + text-decoration: none; +} + +.monaco-shell a.plain:hover, +.monaco-shell a.plain.hover { + color: inherit; + text-decoration: none; +} + +.monaco-shell input { + color: inherit; + font-family: inherit; + font-size: 100%; +} + +.monaco-shell select { + font-family: inherit; +} + +.monaco-shell .pointer { + cursor: pointer; +} + +.monaco-shell input[type="search"]::-webkit-search-decoration, +.monaco-shell input[type="search"]::-webkit-search-results-button, +.monaco-shell input[type="search"]::-webkit-search-results-decoration { + display: none; +} + +.monaco-shell input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; + height: 18px; + width: 18px; + background-image: url('clear.svg'); + background-repeat: no-repeat; + background-position: center center; +} + +/* START Keyboard Focus Indication Styles */ + +.monaco-shell [tabindex="0"]:focus, +.monaco-shell .synthetic-focus, +.monaco-shell select:focus, +.monaco-shell input[type="button"]:focus, +.monaco-shell input[type="submit"]:focus, +.monaco-shell input[type="search"]:focus, +.monaco-shell input[type="text"]:focus, +.monaco-shell textarea:focus, +.monaco-shell input[type="checkbox"]:focus { + outline-width: 1px; + outline-style: solid; + outline-offset: -1px; + opacity: 1 !important; +} + +.monaco-shell .mac select:focus { + border: none; /* outline is a square, but border has a radius, so we avoid this glitch when focused (https://github.com/Microsoft/vscode/issues/26045) */ +} + +.monaco-shell.hc-black [tabindex="0"]:focus, +.monaco-shell.hc-black .synthetic-focus, +.monaco-shell.hc-black select:focus, +.monaco-shell.hc-black input[type="button"]:focus, +.monaco-shell.hc-black input[type="text"]:focus, +.monaco-shell.hc-black textarea:focus, +.monaco-shell.hc-black input[type="search"]:focus, +.monaco-shell.hc-black input[type="checkbox"]:focus { + outline-style: solid; + outline-width: 1px; +} + +.monaco-shell.hc-black .synthetic-focus input { + background: transparent; /* Search input focus fix when in high contrast */ +} + +.monaco-shell.vs .monaco-tree.focused .monaco-tree-row.focused [tabindex="0"]:focus, +.monaco-shell.vs-dark .monaco-tree.focused .monaco-tree-row.focused [tabindex="0"]:focus { + outline-width: 1px; /* higher contrast color for focusable elements in a row that shows focus feedback */ + outline-style: solid; +} + +.monaco-shell .monaco-tree.focused.no-focused-item:focus:before { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 5; /* make sure we are on top of the tree items */ + content: ""; + pointer-events: none; /* enable click through */ + outline: 1px solid; /* we still need to handle the empty tree or no focus item case */ + outline-width: 1px; + outline-style: solid; + outline-offset: -1px; +} + +.monaco-shell.hc-black .monaco-tree.focused.no-focused-item:focus:before { + outline-width: 1px; + outline-offset: -2px; +} + +.monaco-shell .synthetic-focus :focus { + outline: 0 !important; /* elements within widgets that draw synthetic-focus should never show focus */ +} + +.monaco-shell .monaco-inputbox.info.synthetic-focus, +.monaco-shell .monaco-inputbox.warning.synthetic-focus, +.monaco-shell .monaco-inputbox.error.synthetic-focus, +.monaco-shell .monaco-inputbox.info input[type="text"]:focus, +.monaco-shell .monaco-inputbox.warning input[type="text"]:focus, +.monaco-shell .monaco-inputbox.error input[type="text"]:focus { + outline: 0 !important; /* outline is not going well with decoration */ +} + +.monaco-shell .monaco-tree.focused:focus { + outline: 0 !important; /* tree indicates focus not via outline but through the focused item */ +} + +.monaco-shell [tabindex="0"]:active, +.monaco-shell select:active, +.monaco-shell input[type="button"]:active, +.monaco-shell input[type="submit"]:active, +.monaco-shell input[type="checkbox"]:active, +.monaco-shell .monaco-tree .monaco-tree-row +.monaco-action-bar .action-item [tabindex="0"]:hover, +.monaco-shell .monaco-tree.focused.no-focused-item:active:before { + outline: 0 !important; /* fixes some flashing outlines from showing up when clicking */ +} \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/media/workbench.css b/src/vs/workbench/electron-browser/media/workbench.css new file mode 100644 index 0000000000..5f2482409c --- /dev/null +++ b/src/vs/workbench/electron-browser/media/workbench.css @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench-container { + position: absolute; +} + +.monaco-workbench { + font-size: 13px; + line-height: 1.4em; + position: relative; + z-index: 1; + overflow: hidden; +} + +.monaco-workbench > .part { + position: absolute; + box-sizing: border-box; +} + +.monaco-workbench .monaco-action-bar .select-box { + margin-top: 8px; /* Center the select box */ +} + +.monaco-workbench.windows .monaco-action-bar .select-box { + margin-top: 7px; /* Center the select box */ +} \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/nodeCachedDataManager.ts b/src/vs/workbench/electron-browser/nodeCachedDataManager.ts new file mode 100644 index 0000000000..93f34981ab --- /dev/null +++ b/src/vs/workbench/electron-browser/nodeCachedDataManager.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { join, basename } from 'path'; +import { readdir, rimraf, stat } from 'vs/base/node/pfs'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import product from 'vs/platform/node/product'; + +declare type OnNodeCachedDataArgs = [{ errorCode: string, path: string, detail?: string }, { path: string, length: number }]; +declare const MonacoEnvironment: { onNodeCachedData: OnNodeCachedDataArgs[] }; + +export class NodeCachedDataManager { + + private static _DataMaxAge = product.nameLong.indexOf('Insiders') >= 0 + ? 1000 * 60 * 60 * 24 * 7 // roughly 1 week + : 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months + + private _telemetryService: ITelemetryService; + private _environmentService: IEnvironmentService; + private _disposables: IDisposable[] = []; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IEnvironmentService environmentService: IEnvironmentService + ) { + this._telemetryService = telemetryService; + this._environmentService = environmentService; + + this._handleCachedDataInfo(); + this._manageCachedDataSoon(); + } + + dispose(): void { + this._disposables = dispose(this._disposables); + } + + private _handleCachedDataInfo(): void { + + let didRejectCachedData = false; + let didProduceCachedData = false; + for (const [err, data] of MonacoEnvironment.onNodeCachedData) { + // build summary + didRejectCachedData = didRejectCachedData || Boolean(err); + didProduceCachedData = didProduceCachedData || Boolean(data); + + // log each failure separately + if (err) { + this._telemetryService.publicLog('cachedDataError', { + errorCode: err.errorCode, + path: basename(err.path) + }); + } + } + + // log summary + this._telemetryService.publicLog('cachedDataInfo', { + didRequestCachedData: Boolean(global.require.getConfig().nodeCachedDataDir), + didRejectCachedData, + didProduceCachedData + }); + + global.require.config({ onNodeCachedData: undefined }); + delete MonacoEnvironment.onNodeCachedData; + } + + private _manageCachedDataSoon(): void { + // Cached data is stored as user data and we run a cleanup task everytime + // the editor starts. The strategy is to delete all files that are older than + // 3 months + + const { nodeCachedDataDir } = this._environmentService; + if (!nodeCachedDataDir) { + return; + } + + let handle = setTimeout(() => { + handle = undefined; + + readdir(nodeCachedDataDir).then(entries => { + + const now = Date.now(); + const deletes = entries.map(entry => { + const path = join(nodeCachedDataDir, entry); + return stat(path).then(stats => { + const diff = now - stats.mtime.getTime(); + if (diff > NodeCachedDataManager._DataMaxAge) { + return rimraf(path); + } + return undefined; + }); + }); + + return TPromise.join(deletes); + + }).done(undefined, onUnexpectedError); + + }, 30 * 1000); + + this._disposables.push({ + dispose() { clearTimeout(handle); } + }); + } +} diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts new file mode 100644 index 0000000000..c34c093480 --- /dev/null +++ b/src/vs/workbench/electron-browser/shell.ts @@ -0,0 +1,567 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/shell'; + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as platform from 'vs/base/common/platform'; +import { Dimension, Builder, $ } from 'vs/base/browser/builder'; +import dom = require('vs/base/browser/dom'); +import aria = require('vs/base/browser/ui/aria/aria'); +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import errors = require('vs/base/common/errors'); +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import product from 'vs/platform/node/product'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import pkg from 'vs/platform/node/package'; +import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; +import { Workbench, IWorkbenchStartedInfo } from 'vs/workbench/electron-browser/workbench'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService, configurationTelemetry, lifecycleTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; +import { IExperimentService, ExperimentService } from 'vs/platform/telemetry/common/experiments'; +import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; +import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; +import { IdleMonitor, UserStatus } from 'vs/platform/telemetry/browser/idleMonitor'; +import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; +import { ElectronWindow } from 'vs/workbench/electron-browser/window'; +import { resolveWorkbenchCommonProperties, getOrCreateMachineId } from 'vs/platform/telemetry/node/workbenchCommonProperties'; +import { machineIdIpcChannel } from 'vs/platform/telemetry/node/commonProperties'; +import { WorkspaceStats } from 'vs/workbench/services/telemetry/common/workspaceStats'; +import { IWindowsService, IWindowService, IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { WindowService } from 'vs/platform/windows/electron-browser/windowService'; +import { MessageService } from 'vs/workbench/services/message/electron-browser/messageService'; +import { IRequestService } from 'vs/platform/request/node/request'; +import { RequestService } from 'vs/platform/request/electron-browser/requestService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { SearchService } from 'vs/workbench/services/search/node/searchService'; +import { LifecycleService } from 'vs/workbench/services/lifecycle/electron-browser/lifecycleService'; +import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { IntegrityServiceImpl } from 'vs/platform/integrity/node/integrityServiceImpl'; +import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; +import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IMessageService, IChoiceService, Severity } from 'vs/platform/message/common/message'; +import { ChoiceChannel } from 'vs/platform/message/common/messageIpc'; +import { ISearchService } from 'vs/platform/search/common/search'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { CommandService } from 'vs/platform/commands/common/commandService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { WorkbenchModeServiceImpl } from 'vs/workbench/services/mode/common/workbenchModeService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { ICrashReporterService, NullCrashReporterService } from 'vs/workbench/services/crashReporter/common/crashReporterService'; +import { CrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService'; +import { NodeCachedDataManager } from 'vs/workbench/electron-browser/nodeCachedDataManager'; +import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; +import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; +import { IExtensionManagementChannel, ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { IExtensionManagementService, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; +import { ITimerService } from 'vs/workbench/services/timer/common/timerService'; +import { remote, ipcRenderer as ipc } from 'electron'; +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; +import { restoreFontInfo, readFontInfo, saveFontInfo } from 'vs/editor/browser/config/configuration'; +import * as browser from 'vs/base/browser/browser'; +import 'vs/platform/opener/browser/opener.contribution'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { WorkbenchThemeService } from 'vs/workbench/services/themes/electron-browser/workbenchThemeService'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { TextResourceConfigurationService } from 'vs/editor/common/services/resourceConfigurationImpl'; +import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; +import { TextMateService } from 'vs/workbench/services/textMate/electron-browser/TMSyntax'; +import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; +import { IBroadcastService, BroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService'; + +/** + * Services that we require for the Shell + */ +export interface ICoreServices { + contextService: IWorkspaceContextService; + configurationService: IConfigurationService; + environmentService: IEnvironmentService; + timerService: ITimerService; + storageService: IStorageService; +} + +const currentWindow = remote.getCurrentWindow(); + +/** + * The workbench shell contains the workbench with a rich header containing navigation and the activity bar. + * With the Shell being the top level element in the page, it is also responsible for driving the layouting. + */ +export class WorkbenchShell { + private storageService: IStorageService; + private messageService: MessageService; + private environmentService: IEnvironmentService; + private contextViewService: ContextViewService; + private configurationService: IConfigurationService; + private contextService: IWorkspaceContextService; + private telemetryService: ITelemetryService; + private experimentService: IExperimentService; + private extensionService: ExtensionService; + private broadcastService: IBroadcastService; + private timerService: ITimerService; + private themeService: WorkbenchThemeService; + private lifecycleService: LifecycleService; + private mainProcessServices: ServiceCollection; + + private container: HTMLElement; + private toUnbind: IDisposable[]; + private previousErrorValue: string; + private previousErrorTime: number; + private content: HTMLElement; + private contentsContainer: Builder; + + private configuration: IWindowConfiguration; + private workbench: Workbench; + + constructor(container: HTMLElement, coreServices: ICoreServices, mainProcessServices: ServiceCollection, configuration: IWindowConfiguration) { + this.container = container; + + this.configuration = configuration; + + this.contextService = coreServices.contextService; + this.configurationService = coreServices.configurationService; + this.environmentService = coreServices.environmentService; + this.timerService = coreServices.timerService; + this.storageService = coreServices.storageService; + + this.mainProcessServices = mainProcessServices; + + this.toUnbind = []; + this.previousErrorTime = 0; + } + + private createContents(parent: Builder): Builder { + + // ARIA + aria.setARIAContainer(document.body); + + // Workbench Container + const workbenchContainer = $(parent).div(); + + // Instantiation service with services + const [instantiationService, serviceCollection] = this.initServiceCollection(parent.getHTMLElement()); + + // Workbench + this.workbench = instantiationService.createInstance(Workbench, parent.getHTMLElement(), workbenchContainer.getHTMLElement(), this.configuration, serviceCollection); + this.workbench.startup({ + onWorkbenchStarted: (info: IWorkbenchStartedInfo) => { + + // run workbench started logic + this.onWorkbenchStarted(info); + + // start cached data manager + instantiationService.createInstance(NodeCachedDataManager); + + // Set lifecycle phase to `Runnning` so that other contributions + // can now do something + this.lifecycleService.phase = LifecyclePhase.Running; + } + }); + + // Window + this.workbench.getInstantiationService().createInstance(ElectronWindow, this.container); + + // Handle case where workbench is not starting up properly + const timeoutHandle = setTimeout(() => { + console.warn('Workbench did not finish loading in 10 seconds, that might be a problem that should be reported.'); + }, 10000); + + this.workbench.joinCreation().then(() => { + clearTimeout(timeoutHandle); + }); + + return workbenchContainer; + } + + private onWorkbenchStarted(info: IWorkbenchStartedInfo): void { + + // Telemetry: workspace info + const { filesToOpen, filesToCreate, filesToDiff } = this.configuration; + this.telemetryService.publicLog('workspaceLoad', { + userAgent: navigator.userAgent, + windowSize: { innerHeight: window.innerHeight, innerWidth: window.innerWidth, outerHeight: window.outerHeight, outerWidth: window.outerWidth }, + emptyWorkbench: !this.contextService.hasWorkspace(), + 'workbench.filesToOpen': filesToOpen && filesToOpen.length || void 0, + 'workbench.filesToCreate': filesToCreate && filesToCreate.length || void 0, + 'workbench.filesToDiff': filesToDiff && filesToDiff.length || void 0, + customKeybindingsCount: info.customKeybindingsCount, + theme: this.themeService.getColorTheme().id, + language: platform.language, + experiments: this.experimentService.getExperiments(), + pinnedViewlets: info.pinnedViewlets, + restoredViewlet: info.restoredViewlet, + restoredEditors: info.restoredEditors.length, + startupKind: this.lifecycleService.startupKind + }); + + // Telemetry: startup metrics + this.timerService.workbenchStarted = Date.now(); + this.timerService.restoreEditorsDuration = info.restoreEditorsDuration; + this.timerService.restoreViewletDuration = info.restoreViewletDuration; + this.extensionService.onReady().done(() => { + this.telemetryService.publicLog('startupTime', this.timerService.startupMetrics); + }); + + // Telemetry: workspace tags + const workspaceStats: WorkspaceStats = this.workbench.getInstantiationService().createInstance(WorkspaceStats); + workspaceStats.reportWorkspaceTags(this.configuration); + workspaceStats.reportCloudStats(); + + if ((platform.isLinux || platform.isMacintosh) && process.getuid() === 0) { + this.messageService.show(Severity.Warning, nls.localize('runningAsRoot', "It is recommended not to run Code as 'root'.")); + } + } + + private initServiceCollection(container: HTMLElement): [IInstantiationService, ServiceCollection] { + const disposables: IDisposable[] = []; + + const serviceCollection = new ServiceCollection(); + serviceCollection.set(IWorkspaceContextService, this.contextService); + serviceCollection.set(IConfigurationService, this.configurationService); + serviceCollection.set(IEnvironmentService, this.environmentService); + serviceCollection.set(ITimerService, this.timerService); + serviceCollection.set(IStorageService, this.storageService); + this.mainProcessServices.forEach((serviceIdentifier, serviceInstance) => { + serviceCollection.set(serviceIdentifier, serviceInstance); + }); + + const instantiationService: IInstantiationService = new InstantiationService(serviceCollection, true); + + this.broadcastService = new BroadcastService(currentWindow.id); + serviceCollection.set(IBroadcastService, this.broadcastService); + + serviceCollection.set(IWindowService, new SyncDescriptor(WindowService, currentWindow.id)); + + const sharedProcess = (serviceCollection.get(IWindowsService)).whenSharedProcessReady() + .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${currentWindow.id}`)); + + sharedProcess + .done(client => client.registerChannel('choice', instantiationService.createInstance(ChoiceChannel))); + + // Warm up font cache information before building up too many dom elements + restoreFontInfo(this.storageService); + readFontInfo(BareFontInfo.createFromRawSettings(this.configurationService.getConfiguration('editor'), browser.getZoomLevel())); + + // Experiments + this.experimentService = instantiationService.createInstance(ExperimentService); + serviceCollection.set(IExperimentService, this.experimentService); + + // Telemetry + this.sendMachineIdToMain(this.storageService); + if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !!product.enableTelemetry) { + const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); + const commit = product.commit; + const version = pkg.version; + + const config: ITelemetryServiceConfig = { + appender: new TelemetryAppenderClient(channel), + commonProperties: resolveWorkbenchCommonProperties(this.storageService, commit, version), + piiPaths: [this.environmentService.appRoot, this.environmentService.extensionsPath] + }; + + const telemetryService = instantiationService.createInstance(TelemetryService, config); + this.telemetryService = telemetryService; + + const errorTelemetry = new ErrorTelemetry(telemetryService); + const idleMonitor = new IdleMonitor(2 * 60 * 1000); // 2 minutes + + const listener = idleMonitor.onStatusChange(status => + this.telemetryService.publicLog(status === UserStatus.Active + ? TelemetryService.IDLE_STOP_EVENT_NAME + : TelemetryService.IDLE_START_EVENT_NAME + )); + + disposables.push(telemetryService, errorTelemetry, listener, idleMonitor); + } else { + this.telemetryService = NullTelemetryService; + } + + serviceCollection.set(ITelemetryService, this.telemetryService); + disposables.push(configurationTelemetry(this.telemetryService, this.configurationService)); + + let crashReporterService = NullCrashReporterService; + if (product.crashReporter && product.hockeyApp) { + crashReporterService = instantiationService.createInstance(CrashReporterService); + } + serviceCollection.set(ICrashReporterService, crashReporterService); + + this.messageService = instantiationService.createInstance(MessageService, container); + serviceCollection.set(IMessageService, this.messageService); + serviceCollection.set(IChoiceService, this.messageService); + + const lifecycleService = instantiationService.createInstance(LifecycleService); + this.toUnbind.push(lifecycleService.onShutdown(reason => dispose(disposables))); + this.toUnbind.push(lifecycleService.onShutdown(reason => saveFontInfo(this.storageService))); + serviceCollection.set(ILifecycleService, lifecycleService); + disposables.push(lifecycleTelemetry(this.telemetryService, lifecycleService)); + this.lifecycleService = lifecycleService; + + const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions'))); + serviceCollection.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementChannelClient, extensionManagementChannel)); + + const extensionEnablementService = instantiationService.createInstance(ExtensionEnablementService); + serviceCollection.set(IExtensionEnablementService, extensionEnablementService); + disposables.push(extensionEnablementService); + + this.extensionService = instantiationService.createInstance(ExtensionService); + serviceCollection.set(IExtensionService, this.extensionService); + + this.timerService.beforeExtensionLoad = Date.now(); + this.extensionService.onReady().done(() => { + this.timerService.afterExtensionLoad = Date.now(); + }); + + this.themeService = instantiationService.createInstance(WorkbenchThemeService, document.body); + serviceCollection.set(IWorkbenchThemeService, this.themeService); + + serviceCollection.set(ICommandService, new SyncDescriptor(CommandService)); + + this.contextViewService = instantiationService.createInstance(ContextViewService, this.container); + serviceCollection.set(IContextViewService, this.contextViewService); + + serviceCollection.set(IRequestService, new SyncDescriptor(RequestService)); + + serviceCollection.set(IMarkerService, new SyncDescriptor(MarkerService)); + + serviceCollection.set(IModeService, new SyncDescriptor(WorkbenchModeServiceImpl)); + + serviceCollection.set(IModelService, new SyncDescriptor(ModelServiceImpl)); + + serviceCollection.set(ITextResourceConfigurationService, new SyncDescriptor(TextResourceConfigurationService)); + + serviceCollection.set(IEditorWorkerService, new SyncDescriptor(EditorWorkerServiceImpl)); + + serviceCollection.set(IUntitledEditorService, new SyncDescriptor(UntitledEditorService)); + + serviceCollection.set(ITextMateService, new SyncDescriptor(TextMateService)); + + serviceCollection.set(ISearchService, new SyncDescriptor(SearchService)); + + serviceCollection.set(ICodeEditorService, new SyncDescriptor(CodeEditorServiceImpl)); + + serviceCollection.set(IIntegrityService, new SyncDescriptor(IntegrityServiceImpl)); + + return [instantiationService, serviceCollection]; + } + + private sendMachineIdToMain(storageService: IStorageService) { + getOrCreateMachineId(storageService).then(machineId => { + ipc.send(machineIdIpcChannel, machineId); + }).then(null, errors.onUnexpectedError); + } + + public open(): void { + + // Listen on unexpected errors + errors.setUnexpectedErrorHandler((error: any) => { + this.onUnexpectedError(error); + }); + + // Shell Class for CSS Scoping + $(this.container).addClass('monaco-shell'); + + // Controls + this.content = $('.monaco-shell-content').appendTo(this.container).getHTMLElement(); + + // Create Contents + this.contentsContainer = this.createContents($(this.content)); + + // Layout + this.layout(); + + // Listeners + this.registerListeners(); + } + + private registerListeners(): void { + + // Resize + $(window).on(dom.EventType.RESIZE, () => this.layout(), this.toUnbind); + } + + public onUnexpectedError(error: any): void { + const errorMsg = toErrorMessage(error, true); + if (!errorMsg) { + return; + } + + const now = Date.now(); + if (errorMsg === this.previousErrorValue && now - this.previousErrorTime <= 1000) { + return; // Return if error message identical to previous and shorter than 1 second + } + + this.previousErrorTime = now; + this.previousErrorValue = errorMsg; + + // Log to console + console.error(errorMsg); + + // Show to user if friendly message provided + if (error && error.friendlyMessage && this.messageService) { + this.messageService.show(Severity.Error, error.friendlyMessage); + } + } + + private layout(): void { + const clArea = $(this.container).getClientArea(); + + const contentsSize = new Dimension(clArea.width, clArea.height); + this.contentsContainer.size(contentsSize.width, contentsSize.height); + + this.contextViewService.layout(); + this.workbench.layout(); + } + + public joinCreation(): TPromise { + return this.workbench.joinCreation(); + } + + public dispose(): void { + + // Workbench + if (this.workbench) { + this.workbench.dispose(); + } + + this.contextViewService.dispose(); + + // Listeners + this.toUnbind = dispose(this.toUnbind); + + // Container + $(this.container).empty(); + } +} + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + + // Foreground + const windowForeground = theme.getColor(foreground); + if (windowForeground) { + collector.addRule(`.monaco-shell { color: ${windowForeground}; }`); + } + + // Selection + const windowSelectionBackground = theme.getColor(selectionBackground); + if (windowSelectionBackground) { + collector.addRule(`.monaco-shell ::selection { background-color: ${windowSelectionBackground}; }`); + } + + // Input placeholder + const placeholderForeground = theme.getColor(inputPlaceholderForeground); + if (placeholderForeground) { + collector.addRule(`.monaco-shell input::-webkit-input-placeholder { color: ${placeholderForeground}; }`); + collector.addRule(`.monaco-shell textarea::-webkit-input-placeholder { color: ${placeholderForeground}; }`); + } + + // List highlight + const listHighlightForegroundColor = theme.getColor(listHighlightForeground); + if (listHighlightForegroundColor) { + collector.addRule(` + .monaco-shell .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight, + .monaco-shell .monaco-list .monaco-list-row .monaco-highlighted-label .highlight { + color: ${listHighlightForegroundColor}; + } + `); + } + + // We need to set the workbench background color so that on Windows we get subpixel-antialiasing. + let workbenchBackground: string; + switch (theme.type) { + case 'dark': + workbenchBackground = '#252526'; + break; + case 'light': + workbenchBackground = '#F3F3F3'; + break; + default: + workbenchBackground = '#000000'; + } + collector.addRule(`.monaco-workbench { background-color: ${workbenchBackground}; }`); + + // Scrollbars + const scrollbarShadowColor = theme.getColor(scrollbarShadow); + if (scrollbarShadowColor) { + collector.addRule(` + .monaco-shell .monaco-scrollable-element > .shadow.top { + box-shadow: ${scrollbarShadowColor} 0 6px 6px -6px inset; + } + + .monaco-shell .monaco-scrollable-element > .shadow.left { + box-shadow: ${scrollbarShadowColor} 6px 0 6px -6px inset; + } + + .monaco-shell .monaco-scrollable-element > .shadow.top.left { + box-shadow: ${scrollbarShadowColor} 6px 6px 6px -6px inset; + } + `); + } + + const scrollbarSliderBackgroundColor = theme.getColor(scrollbarSliderBackground); + if (scrollbarSliderBackgroundColor) { + collector.addRule(` + .monaco-shell .monaco-scrollable-element > .scrollbar > .slider { + background: ${scrollbarSliderBackgroundColor}; + } + `); + } + + const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground); + if (scrollbarSliderHoverBackgroundColor) { + collector.addRule(` + .monaco-shell .monaco-scrollable-element > .scrollbar > .slider:hover { + background: ${scrollbarSliderHoverBackgroundColor}; + } + `); + } + + const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground); + if (scrollbarSliderActiveBackgroundColor) { + collector.addRule(` + .monaco-shell .monaco-scrollable-element > .scrollbar > .slider.active { + background: ${scrollbarSliderActiveBackgroundColor}; + } + `); + } + + // Focus outline + const focusOutline = theme.getColor(focusBorder); + if (focusOutline) { + collector.addRule(` + .monaco-shell [tabindex="0"]:focus, + .monaco-shell .synthetic-focus, + .monaco-shell select:focus, + .monaco-shell .monaco-tree.focused.no-focused-item:focus:before, + .monaco-shell input[type="button"]:focus, + .monaco-shell input[type="text"]:focus, + .monaco-shell button:focus, + .monaco-shell textarea:focus, + .monaco-shell input[type="search"]:focus, + .monaco-shell input[type="checkbox"]:focus { + outline-color: ${focusOutline}; + } + `); + } +}); diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts new file mode 100644 index 0000000000..6fa4f33483 --- /dev/null +++ b/src/vs/workbench/electron-browser/window.ts @@ -0,0 +1,394 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import platform = require('vs/base/common/platform'); +import URI from 'vs/base/common/uri'; +import errors = require('vs/base/common/errors'); +import types = require('vs/base/common/types'); +import { TPromise } from 'vs/base/common/winjs.base'; +import arrays = require('vs/base/common/arrays'); +import DOM = require('vs/base/browser/dom'); +import Severity from 'vs/base/common/severity'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, Action } from 'vs/base/common/actions'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { AutoSaveConfiguration } from 'vs/platform/files/common/files'; +import { toResource } from 'vs/workbench/common/editor'; +import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IWindowsService, IWindowService, IWindowSettings, IPath, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest } from 'vs/platform/windows/common/windows'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { ITitleService } from 'vs/workbench/services/title/common/titleService'; +import { IWorkbenchThemeService, VS_HC_THEME, VS_DARK_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import * as browser from 'vs/base/browser/browser'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { Position, IResourceInput, IUntitledResourceInput, IEditor } from 'vs/platform/editor/common/editor'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import { Themable } from 'vs/workbench/common/theme'; +import { ipcRenderer as ipc, webFrame } from 'electron'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; + +const TextInputActions: IAction[] = [ + new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && TPromise.as(true)), + new Action('redo', nls.localize('redo', "Redo"), null, true, () => document.execCommand('redo') && TPromise.as(true)), + new Separator(), + new Action('editor.action.clipboardCutAction', nls.localize('cut', "Cut"), null, true, () => document.execCommand('cut') && TPromise.as(true)), + new Action('editor.action.clipboardCopyAction', nls.localize('copy', "Copy"), null, true, () => document.execCommand('copy') && TPromise.as(true)), + new Action('editor.action.clipboardPasteAction', nls.localize('paste', "Paste"), null, true, () => document.execCommand('paste') && TPromise.as(true)), + new Separator(), + new Action('editor.action.selectAll', nls.localize('selectAll', "Select All"), null, true, () => document.execCommand('selectAll') && TPromise.as(true)) +]; + +export class ElectronWindow extends Themable { + + private static AUTO_SAVE_SETTING = 'files.autoSave'; + + constructor( + shellContainer: HTMLElement, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IPartService private partService: IPartService, + @IWindowsService private windowsService: IWindowsService, + @IWindowService private windowService: IWindowService, + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, + @ITitleService private titleService: ITitleService, + @IWorkbenchThemeService protected themeService: IWorkbenchThemeService, + @IMessageService private messageService: IMessageService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, + @ICommandService private commandService: ICommandService, + @IExtensionService private extensionService: IExtensionService, + @IViewletService private viewletService: IViewletService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IKeybindingService private keybindingService: IKeybindingService, + @IEnvironmentService private environmentService: IEnvironmentService, + @ITelemetryService private telemetryService: ITelemetryService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService + ) { + super(themeService); + + this.registerListeners(); + this.setup(); + } + + private registerListeners(): void { + + // React to editor input changes + this.editorGroupService.onEditorsChanged(() => { + const file = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); + + this.titleService.setRepresentedFilename(file ? file.fsPath : ''); + }); + + // prevent opening a real URL inside the shell + [DOM.EventType.DRAG_OVER, DOM.EventType.DROP].forEach(event => { + window.document.body.addEventListener(event, (e: DragEvent) => { + DOM.EventHelper.stop(e); + }); + }); + + // Handle window.open() calls + const $this = this; + (window).open = function (url: string, target: string, features: string, replace: boolean) { + $this.windowsService.openExternal(url); + + return null; + }; + } + + private setup(): void { + + // Support runAction event + ipc.on('vscode:runAction', (event, actionId: string) => { + this.commandService.executeCommand(actionId, { from: 'menu' }).done(_ => { + this.telemetryService.publicLog('commandExecuted', { id: actionId, from: 'menu' }); + }, err => { + this.messageService.show(Severity.Error, err); + }); + }); + + // Support resolve keybindings event + ipc.on('vscode:resolveKeybindings', (event, rawActionIds: string) => { + let actionIds: string[] = []; + try { + actionIds = JSON.parse(rawActionIds); + } catch (error) { + // should not happen + } + + // Resolve keys using the keybinding service and send back to browser process + this.resolveKeybindings(actionIds).done(keybindings => { + if (keybindings.length) { + ipc.send('vscode:keybindingsResolved', JSON.stringify(keybindings)); + } + }, () => errors.onUnexpectedError); + }); + + // Send over all extension viewlets when extensions are ready + this.extensionService.onReady().then(() => { + ipc.send('vscode:extensionViewlets', JSON.stringify(this.viewletService.getViewlets().filter(v => !!v.extensionId).map(v => { return { id: v.id, label: v.name }; }))); + }); + + ipc.on('vscode:reportError', (event, error) => { + if (error) { + const errorParsed = JSON.parse(error); + errorParsed.mainProcess = true; + errors.onUnexpectedError(errorParsed); + } + }); + + // Support openFiles event for existing and new files + ipc.on('vscode:openFiles', (event, request: IOpenFileRequest) => this.onOpenFiles(request)); + + // Support addFolders event if we have a workspace opened + ipc.on('vscode:addFolders', (event, request: IAddFoldersRequest) => this.onAddFolders(request)); + + // Emit event when vscode has loaded + this.partService.joinCreation().then(() => { + ipc.send('vscode:workbenchLoaded', this.windowService.getCurrentWindowId()); + }); + + // Message support + ipc.on('vscode:showInfoMessage', (event, message: string) => { + this.messageService.show(Severity.Info, message); + }); + + // Support toggling auto save + ipc.on('vscode.toggleAutoSave', event => { + this.toggleAutoSave(); + }); + + // Fullscreen Events + ipc.on('vscode:enterFullScreen', event => { + this.partService.joinCreation().then(() => { + browser.setFullscreen(true); + }); + }); + + ipc.on('vscode:leaveFullScreen', event => { + this.partService.joinCreation().then(() => { + browser.setFullscreen(false); + }); + }); + + // High Contrast Events + ipc.on('vscode:enterHighContrast', event => { + const windowConfig = this.configurationService.getConfiguration('window'); + if (windowConfig && windowConfig.autoDetectHighContrast) { + this.partService.joinCreation().then(() => { + this.themeService.setColorTheme(VS_HC_THEME, null); + }); + } + }); + + ipc.on('vscode:leaveHighContrast', event => { + const windowConfig = this.configurationService.getConfiguration('window'); + if (windowConfig && windowConfig.autoDetectHighContrast) { + this.partService.joinCreation().then(() => { + this.themeService.setColorTheme(VS_DARK_THEME, null); + }); + } + }); + + // keyboard layout changed event + ipc.on('vscode:keyboardLayoutChanged', (event, isISOKeyboard: boolean) => { + KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(isISOKeyboard); + }); + + // keyboard layout changed event + ipc.on('vscode:accessibilitySupportChanged', (event, accessibilitySupportEnabled: boolean) => { + browser.setAccessibilitySupport(accessibilitySupportEnabled ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled); + }); + + // Configuration changes + let previousConfiguredZoomLevel: number; + this.configurationService.onDidUpdateConfiguration(e => { + const windowConfig: IWindowsConfiguration = this.configurationService.getConfiguration(); + + let newZoomLevel = 0; + if (windowConfig.window && typeof windowConfig.window.zoomLevel === 'number') { + newZoomLevel = windowConfig.window.zoomLevel; + + // Leave early if the configured zoom level did not change (https://github.com/Microsoft/vscode/issues/1536) + if (previousConfiguredZoomLevel === newZoomLevel) { + return; + } + + previousConfiguredZoomLevel = newZoomLevel; + } + + if (webFrame.getZoomLevel() !== newZoomLevel) { + webFrame.setZoomLevel(newZoomLevel); + browser.setZoomFactor(webFrame.getZoomFactor()); + // See https://github.com/Microsoft/vscode/issues/26151 + // Cannot be trusted because the webFrame might take some time + // until it really applies the new zoom level + browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); + } + }); + + // Context menu support in input/textarea + window.document.addEventListener('contextmenu', e => { + if (e.target instanceof HTMLElement) { + const target = e.target; + if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) { + e.preventDefault(); + e.stopPropagation(); + + this.contextMenuService.showContextMenu({ + getAnchor: () => e, + getActions: () => TPromise.as(TextInputActions) + }); + } + } + }); + } + + private resolveKeybindings(actionIds: string[]): TPromise<{ id: string; label: string, isNative: boolean; }[]> { + return TPromise.join([this.partService.joinCreation(), this.extensionService.onReady()]).then(() => { + return arrays.coalesce(actionIds.map(id => { + const binding = this.keybindingService.lookupKeybinding(id); + if (!binding) { + return null; + } + + // first try to resolve a native accelerator + const electronAccelerator = binding.getElectronAccelerator(); + if (electronAccelerator) { + return { id, label: electronAccelerator, isNative: true }; + } + + // we need this fallback to support keybindings that cannot show in electron menus (e.g. chords) + const acceleratorLabel = binding.getLabel(); + if (acceleratorLabel) { + return { id, label: acceleratorLabel, isNative: false }; + } + + return null; + })); + }); + } + + private onAddFolders(request: IAddFoldersRequest): void { + const foldersToAdd = request.foldersToAdd.map(folderToAdd => URI.file(folderToAdd.filePath)); + + // Workspace: just add to workspace config + if (this.contextService.hasMultiFolderWorkspace()) { + this.workspaceEditingService.addRoots(foldersToAdd).done(null, errors.onUnexpectedError); + } + + // Single folder or no workspace: create workspace and open + else { + let workspaceFolders: URI[] = []; + + // Folder of workspace is the first of multi root workspace, so add it + if (this.contextService.hasFolderWorkspace()) { + workspaceFolders.push(...this.contextService.getWorkspace().roots); + } + + // Fill in remaining ones from request + workspaceFolders.push(...request.foldersToAdd.map(folderToAdd => URI.file(folderToAdd.filePath))); + + // Create workspace and open (ensure no duplicates) + this.windowService.createAndOpenWorkspace(arrays.distinct(workspaceFolders.map(folder => folder.fsPath), folder => platform.isLinux ? folder : folder.toLowerCase())); + } + } + + private onOpenFiles(request: IOpenFileRequest): void { + let inputs: IResourceInputType[] = []; + let diffMode = (request.filesToDiff.length === 2); + + if (!diffMode && request.filesToOpen) { + inputs.push(...this.toInputs(request.filesToOpen, false)); + } + + if (!diffMode && request.filesToCreate) { + inputs.push(...this.toInputs(request.filesToCreate, true)); + } + + if (diffMode) { + inputs.push(...this.toInputs(request.filesToDiff, false)); + } + + if (inputs.length) { + this.openResources(inputs, diffMode).done(null, errors.onUnexpectedError); + } + } + + private openResources(resources: (IResourceInput | IUntitledResourceInput)[], diffMode: boolean): TPromise { + return this.partService.joinCreation().then((): TPromise => { + + + // In diffMode we open 2 resources as diff + if (diffMode && resources.length === 2) { + return this.editorService.openEditor({ leftResource: resources[0].resource, rightResource: resources[1].resource, options: { pinned: true } }); + } + + // For one file, just put it into the current active editor + if (resources.length === 1) { + return this.editorService.openEditor(resources[0]); + } + + // Otherwise open all + const activeEditor = this.editorService.getActiveEditor(); + return this.editorService.openEditors(resources.map((r, index) => { + return { + input: r, + position: activeEditor ? activeEditor.position : Position.ONE + }; + })); + }); + } + + private toInputs(paths: IPath[], isNew: boolean): IResourceInputType[] { + return paths.map(p => { + const resource = URI.file(p.filePath); + let input: IResourceInput | IUntitledResourceInput; + if (isNew) { + input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput; + } else { + input = { resource, options: { pinned: true } } as IResourceInput; + } + + if (!isNew && p.lineNumber) { + input.options.selection = { + startLineNumber: p.lineNumber, + startColumn: p.columnNumber + }; + } + + return input; + }); + } + + private toggleAutoSave(): void { + const setting = this.configurationService.lookup(ElectronWindow.AUTO_SAVE_SETTING); + let userAutoSaveConfig = setting.user; + if (types.isUndefinedOrNull(userAutoSaveConfig)) { + userAutoSaveConfig = setting.default; // use default if setting not defined + } + + let newAutoSaveValue: string; + if ([AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE].some(s => s === userAutoSaveConfig)) { + newAutoSaveValue = AutoSaveConfiguration.OFF; + } else { + newAutoSaveValue = AutoSaveConfiguration.AFTER_DELAY; + } + + this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ElectronWindow.AUTO_SAVE_SETTING, value: newAutoSaveValue }); + } +} diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts new file mode 100644 index 0000000000..2c0621c046 --- /dev/null +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -0,0 +1,1440 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/workbench'; + +import { localize } from 'vs/nls'; +import { TPromise, ValueCallback } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import Event, { Emitter, chain } from 'vs/base/common/event'; +import DOM = require('vs/base/browser/dom'); +import { Builder, $ } from 'vs/base/browser/builder'; +import { Delayer, RunOnceScheduler } from 'vs/base/common/async'; +import * as browser from 'vs/base/browser/browser'; +import assert = require('vs/base/common/assert'); +import { StopWatch } from 'vs/base/common/stopwatch'; +import { startTimer } from 'vs/base/node/startupTimers'; +import errors = require('vs/base/common/errors'); +import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; +import { Position as EditorPosition, IResourceDiffInput, IUntitledResourceInput, IEditor, IResourceInput } from 'vs/platform/editor/common/editor'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { HistoryService } from 'vs/workbench/services/history/browser/history'; +import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; +import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; +import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; +import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; +import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart'; +import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; +import { WorkbenchLayout } from 'vs/workbench/browser/layout'; +import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; +import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; +import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController'; +import { getServices } from 'vs/platform/instantiation/common/extensions'; +import { WorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; +import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService'; +import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { WorkspaceService } from 'vs/workbench/services/configuration/node/configuration'; +import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; +import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; +import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IKeybindingEditingService, KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; +import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IActivityBarService } from 'vs/workbench/services/activity/common/activityBarService'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { ViewletService } from 'vs/workbench/services/viewlet/browser/viewletService'; +// import { FileService } from 'vs/workbench/services/files/electron-browser/fileService'; +import { RemoteFileService } from 'vs/workbench/services/files/electron-browser/remoteFileService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IListService, ListService } from 'vs/platform/list/browser/listService'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/node/configurationResolverService'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { ITitleService } from 'vs/workbench/services/title/common/titleService'; +import { WorkbenchMessageService } from 'vs/workbench/services/message/browser/messageService'; +import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ClipboardService } from 'vs/platform/clipboard/electron-browser/clipboardService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { TextFileService } from 'vs/workbench/services/textfile/electron-browser/textFileService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ISCMService } from 'vs/workbench/services/scm/common/scm'; +import { SCMService } from 'vs/workbench/services/scm/common/scmService'; +import { IProgressService2 } from 'vs/platform/progress/common/progress'; +import { ProgressService2 } from 'vs/workbench/services/progress/browser/progressService2'; +import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ILifecycleService, ShutdownReason, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWindowService, IWindowConfiguration as IWindowSettings, IWindowConfiguration, IPath } from 'vs/platform/windows/common/windows'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import { IMenuService, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { MenuService } from 'vs/platform/actions/common/menuService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; +import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, inRecentFilesPickerContextKey } from 'vs/workbench/electron-browser/actions'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { getQuickNavigateHandler, inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen'; +import { IWorkspaceEditingService, IWorkspaceMigrationService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService'; +import URI from 'vs/base/common/uri'; +import { isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { WorkspaceMigrationService } from 'vs/workbench/services/workspace/node/workspaceMigrationService'; + +// {{SQL CARBON EDIT}} +import { IConnectionManagementService, IConnectionDialogService, IErrorMessageService, IServerGroupController } from 'sql/parts/connection/common/connectionManagement'; +import { ConnectionManagementService } from 'sql/parts/connection/common/connectionManagementService'; +import { ConnectionDialogService } from 'sql/parts/connection/connectionDialog/connectionDialogService'; +import { ErrorMessageService } from 'sql/workbench/errorMessageDialog/errorMessageService'; +import { ServerGroupController } from 'sql/parts/registeredServer/serverGroupDialog/serverGroupController'; + +import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import { IAngularEventingService, AngularEventingService } from 'sql/services/angularEventing/angularEventingService'; +import { BootstrapService } from 'sql/services/bootstrap/bootstrapServiceImpl'; +import { ICapabilitiesService, CapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { ICredentialsService, CredentialsService } from 'sql/services/credentials/credentialsService'; +import { ISerializationService, SerializationService } from 'sql/services/serialization/serializationService'; +import { IMetadataService, MetadataService } from 'sql/services/metadata/metadataService'; +import { IObjectExplorerService, ObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService'; +import { ITaskService, TaskService } from 'sql/parts/taskHistory/common/taskService'; +import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; +import { QueryModelService } from 'sql/parts/query/execution/queryModelService'; +import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; +import { QueryEditorService } from 'sql/parts/query/services/queryEditorService'; +import { IQueryManagementService, QueryManagementService } from 'sql/parts/query/common/queryManagement'; +import { IEditorDescriptorService, EditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService'; +import { IScriptingService, ScriptingService } from 'sql/services/scripting/scriptingService'; +import { IAdminService, AdminService } from 'sql/parts/admin/common/adminService'; +import { DisasterRecoveryService } from 'sql/parts/disasterRecovery/common/disasterRecoveryService'; +import { DisasterRecoveryUiService } from 'sql/parts/disasterRecovery/common/disasterRecoveryUiService'; +import { RestoreDialogController } from 'sql/parts/disasterRecovery/restore/restoreDialogController'; +import { IDisasterRecoveryService, IDisasterRecoveryUiService, IRestoreDialogController } from 'sql/parts/disasterRecovery/common/interfaces'; +import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces'; +import { FileBrowserService } from 'sql/parts/fileBrowser/common/fileBrowserService'; +import { FileBrowserDialogController } from 'sql/parts/fileBrowser/fileBrowserDialogController'; +import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces'; +import { InsightsDialogService } from 'sql/parts/insights/insightsDialogService'; +import { IAccountManagementService } from 'sql/services/accountManagement/interfaces'; +import { AccountManagementService } from 'sql/services/accountManagement/accountManagementService'; +import { IProfilerService } from 'sql/parts/profiler/service/interfaces'; +import { ProfilerService } from 'sql/parts/profiler/service/profilerService'; +import { ISqlOAuthService } from 'sql/common/sqlOAuthService'; +import { SqlOAuthService } from 'sql/common/browser/sqlOAuthServiceImpl'; +import { IClipboardService as sqlIClipboardService } from 'sql/platform/clipboard/common/clipboardService'; +import { ClipboardService as sqlClipboardService } from 'sql/platform/clipboard/electron-browser/clipboardService'; +import { IResourceProviderService, IAccountPickerService } from 'sql/parts/accountManagement/common/interfaces'; +import { ResourceProviderService } from 'sql/parts/accountManagement/common/resourceProviderService'; +import { AccountPickerService } from 'sql/parts/accountManagement/accountPicker/accountPickerService'; + +export const MessagesVisibleContext = new RawContextKey('globalMessageVisible', false); +export const EditorsVisibleContext = new RawContextKey('editorIsOpen', false); +export const InZenModeContext = new RawContextKey('inZenMode', false); +export const NoEditorsVisibleContext: ContextKeyExpr = EditorsVisibleContext.toNegated(); + +interface WorkbenchParams { + configuration: IWindowConfiguration; + serviceCollection: ServiceCollection; +} + +interface IZenModeSettings { + fullScreen: boolean; + hideTabs: boolean; + hideActivityBar: boolean; + hideStatusBar: boolean; + restore: boolean; +} + +export interface IWorkbenchStartedInfo { + customKeybindingsCount: number; + restoreViewletDuration: number; + restoreEditorsDuration: number; + pinnedViewlets: string[]; + restoredViewlet: string; + restoredEditors: string[]; +} + +export interface IWorkbenchCallbacks { + onServicesCreated?: () => void; + onWorkbenchStarted?: (info: IWorkbenchStartedInfo) => void; +} + +const Identifiers = { + WORKBENCH_CONTAINER: 'workbench.main.container', + TITLEBAR_PART: 'workbench.parts.titlebar', + ACTIVITYBAR_PART: 'workbench.parts.activitybar', + SIDEBAR_PART: 'workbench.parts.sidebar', + PANEL_PART: 'workbench.parts.panel', + EDITOR_PART: 'workbench.parts.editor', + STATUSBAR_PART: 'workbench.parts.statusbar' +}; + +/** + * The workbench creates and lays out all parts that make up the workbench. + */ +export class Workbench implements IPartService { + + private static sidebarHiddenSettingKey = 'workbench.sidebar.hidden'; + private static sidebarRestoreSettingKey = 'workbench.sidebar.restore'; + private static panelHiddenSettingKey = 'workbench.panel.hidden'; + private static zenModeActiveSettingKey = 'workbench.zenmode.active'; + + private static sidebarPositionConfigurationKey = 'workbench.sideBar.location'; + private static statusbarVisibleConfigurationKey = 'workbench.statusBar.visible'; + private static activityBarVisibleConfigurationKey = 'workbench.activityBar.visible'; + + private static closeWhenEmptyConfigurationKey = 'window.closeWhenEmpty'; + + private static fontAliasingConfigurationKey = 'workbench.fontAliasing'; + + private _onTitleBarVisibilityChange: Emitter; + + public _serviceBrand: any; + + private parent: HTMLElement; + private container: HTMLElement; + private workbenchParams: WorkbenchParams; + private workbenchContainer: Builder; + private workbench: Builder; + private workbenchStarted: boolean; + private workbenchCreated: boolean; + private workbenchShutdown: boolean; + private editorService: WorkbenchEditorService; + private viewletService: IViewletService; + private contextKeyService: IContextKeyService; + private keybindingService: IKeybindingService; + private backupFileService: IBackupFileService; + private configurationEditingService: IConfigurationEditingService; + private workspaceMigrationService: WorkspaceMigrationService; + private titlebarPart: TitlebarPart; + private activitybarPart: ActivitybarPart; + private sidebarPart: SidebarPart; + private panelPart: PanelPart; + private editorPart: EditorPart; + private statusbarPart: StatusbarPart; + private quickOpen: QuickOpenController; + private workbenchLayout: WorkbenchLayout; + private toDispose: IDisposable[]; + private toShutdown: { shutdown: () => void; }[]; + private callbacks: IWorkbenchCallbacks; + private creationPromise: TPromise; + private creationPromiseComplete: ValueCallback; + private sideBarHidden: boolean; + private statusBarHidden: boolean; + private activityBarHidden: boolean; + private sideBarPosition: Position; + private panelHidden: boolean; + private editorBackgroundDelayer: Delayer; + private closeEmptyWindowScheduler: RunOnceScheduler; + private messagesVisibleContext: IContextKey; + private editorsVisibleContext: IContextKey; + private inZenMode: IContextKey; + private hasFilesToCreateOpenOrDiff: boolean; + private fontAliasing: string; + private zenMode: { + active: boolean; + transitionedToFullScreen: boolean; + wasSideBarVisible: boolean; + wasPanelVisible: boolean; + }; + + constructor( + parent: HTMLElement, + container: HTMLElement, + configuration: IWindowConfiguration, + serviceCollection: ServiceCollection, + @IInstantiationService private instantiationService: IInstantiationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IStorageService private storageService: IStorageService, + @ILifecycleService private lifecycleService: ILifecycleService, + @IMessageService private messageService: IMessageService, + @IConfigurationService private configurationService: WorkspaceService, + @ITelemetryService private telemetryService: ITelemetryService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IWindowService private windowService: IWindowService + ) { + this.parent = parent; + this.container = container; + + this.workbenchParams = { + configuration, + serviceCollection + }; + + this.hasFilesToCreateOpenOrDiff = + (configuration.filesToCreate && configuration.filesToCreate.length > 0) || + (configuration.filesToOpen && configuration.filesToOpen.length > 0) || + (configuration.filesToDiff && configuration.filesToDiff.length > 0); + + this.toDispose = []; + this.toShutdown = []; + + this.editorBackgroundDelayer = new Delayer(50); + this.closeEmptyWindowScheduler = new RunOnceScheduler(() => this.onAllEditorsClosed(), 50); + + this._onTitleBarVisibilityChange = new Emitter(); + + this.creationPromise = new TPromise(c => { + this.creationPromiseComplete = c; + }); + } + + public get onTitleBarVisibilityChange(): Event { + return this._onTitleBarVisibilityChange.event; + } + + public get onEditorLayout(): Event { + return chain(this.editorPart.onLayout) + .map(() => void 0) + .event; + } + + /** + * Starts the workbench and creates the HTML elements on the container. A workbench can only be started + * once. Use the shutdown function to free up resources created by the workbench on startup. + */ + public startup(callbacks?: IWorkbenchCallbacks): void { + assert.ok(!this.workbenchStarted, 'Can not start a workbench that was already started'); + assert.ok(!this.workbenchShutdown, 'Can not start a workbench that was shutdown'); + + try { + this.workbenchStarted = true; + this.callbacks = callbacks; + + // Create Workbench + this.createWorkbench(); + + // Install some global actions + this.createGlobalActions(); + + // Services + this.initServices(); + if (this.callbacks && this.callbacks.onServicesCreated) { + this.callbacks.onServicesCreated(); + } + + // Contexts + this.messagesVisibleContext = MessagesVisibleContext.bindTo(this.contextKeyService); + this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService); + this.inZenMode = InZenModeContext.bindTo(this.contextKeyService); + + // Register Listeners + this.registerListeners(); + + // Settings + this.initSettings(); + + // Create Workbench and Parts + this.renderWorkbench(); + + // Workbench Layout + this.createWorkbenchLayout(); + + // Load composites and editors in parallel + const compositeAndEditorPromises: TPromise[] = []; + + // Restore last opened viewlet + let viewletRestoreStopWatch: StopWatch; + let viewletIdToRestore: string; + if (!this.sideBarHidden) { + + if (this.shouldRestoreLastOpenedViewlet()) { + viewletIdToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE); + } + + if (!viewletIdToRestore) { + viewletIdToRestore = this.viewletService.getDefaultViewletId(); + } + + viewletRestoreStopWatch = StopWatch.create(); + const viewletTimer = startTimer('restore:viewlet'); + compositeAndEditorPromises.push(viewletTimer.while(this.viewletService.openViewlet(viewletIdToRestore)).then(() => { + viewletRestoreStopWatch.stop(); + })); + } + + // Load Panel + const panelRegistry = Registry.as(PanelExtensions.Panels); + const panelId = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); + if (!this.panelHidden && !!panelId) { + compositeAndEditorPromises.push(this.panelPart.openPanel(panelId, false)); + } + + // Load Editors + const editorRestoreStopWatch = StopWatch.create(); + const restoredEditors: string[] = []; + const editorsTimer = startTimer('restore:editors'); + compositeAndEditorPromises.push(editorsTimer.while(this.resolveEditorsToOpen().then(inputs => { + let editorOpenPromise: TPromise; + if (inputs.length) { + editorOpenPromise = this.editorService.openEditors(inputs.map(input => { return { input, position: EditorPosition.ONE }; })); + } else { + editorOpenPromise = this.editorPart.restoreEditors(); + } + + return editorOpenPromise.then(editors => { + this.handleEditorBackground(); // make sure we show the proper background in the editor area + editorRestoreStopWatch.stop(); + + for (const editor of editors) { + if (editor) { + if (editor.input) { + restoredEditors.push(editor.input.getName()); + } else { + restoredEditors.push(`other:${editor.getId()}`); + } + } + } + }); + }))); + + if (this.storageService.getBoolean(Workbench.zenModeActiveSettingKey, StorageScope.WORKSPACE, false)) { + this.toggleZenMode(true); + } + + // Flag workbench as created once done + const workbenchDone = (error?: Error) => { + this.workbenchCreated = true; + this.creationPromiseComplete(true); + + if (this.callbacks && this.callbacks.onWorkbenchStarted) { + this.callbacks.onWorkbenchStarted({ + customKeybindingsCount: this.keybindingService.customKeybindingsCount(), + restoreViewletDuration: viewletRestoreStopWatch ? Math.round(viewletRestoreStopWatch.elapsed()) : 0, + restoreEditorsDuration: Math.round(editorRestoreStopWatch.elapsed()), + pinnedViewlets: this.activitybarPart.getPinned(), + restoredViewlet: viewletIdToRestore, + restoredEditors + }); + } + + if (error) { + errors.onUnexpectedError(error); + } + }; + + // Join viewlet, panel and editor promises + TPromise.join(compositeAndEditorPromises).then(() => workbenchDone(), error => workbenchDone(error)); + } catch (error) { + + // Print out error + console.error(toErrorMessage(error, true)); + + // Rethrow + throw error; + } + } + + private createGlobalActions(): void { + const isDeveloping = !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment; + + // Actions registered here to adjust for developing vs built workbench + const workbenchActionsRegistry = Registry.as(Extensions.WorkbenchActions); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL, isDeveloping ? { primary: KeyMod.CtrlCmd | KeyCode.KEY_R } : void 0), 'Reload Window'); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleDevToolsAction, ToggleDevToolsAction.ID, ToggleDevToolsAction.LABEL, isDeveloping ? { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_I } } : void 0), 'Developer: Toggle Developer Tools', localize('developer', "Developer")); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: isDeveloping ? null : KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', localize('file', "File")); + + const recentFilesPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(inRecentFilesPickerContextKey)); + + const quickOpenNavigateNextInRecentFilesPickerId = 'workbench.action.quickOpenNavigateNextInRecentFilesPicker'; + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: quickOpenNavigateNextInRecentFilesPickerId, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + handler: getQuickNavigateHandler(quickOpenNavigateNextInRecentFilesPickerId, true), + when: recentFilesPickerContext, + primary: KeyMod.CtrlCmd | KeyCode.KEY_R, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } + }); + + const quickOpenNavigatePreviousInRecentFilesPicker = 'workbench.action.quickOpenNavigatePreviousInRecentFilesPicker'; + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: quickOpenNavigatePreviousInRecentFilesPicker, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + handler: getQuickNavigateHandler(quickOpenNavigatePreviousInRecentFilesPicker, false), + when: recentFilesPickerContext, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R } + }); + } + + private resolveEditorsToOpen(): TPromise { + + // Files to open, diff or create + if (this.hasFilesToCreateOpenOrDiff) { + const filesToCreate = this.toInputs(this.workbenchParams.configuration.filesToCreate); + const filesToOpen = this.toInputs(this.workbenchParams.configuration.filesToOpen); + const filesToDiff = this.toInputs(this.workbenchParams.configuration.filesToDiff); + + // Files to diff is exclusive + if (filesToDiff && filesToDiff.length === 2) { + return TPromise.as([{ + leftResource: filesToDiff[0].resource, + rightResource: filesToDiff[1].resource, + options: { pinned: true } + }]); + } + + // Otherwise: Open/Create files + else { + const filesToCreateInputs: IUntitledResourceInput[] = filesToCreate.map(resourceInput => { + return { + filePath: resourceInput.resource.fsPath, + options: { pinned: true } + }; + }); + + return TPromise.as([].concat(filesToOpen).concat(filesToCreateInputs)); + } + } + + // Empty workbench + else if (!this.contextService.hasWorkspace() && this.openUntitledFile()) { + if (this.editorPart.hasEditorsToRestore()) { + return TPromise.as([]); // do not open any empty untitled file if we have editors to restore + } + + return this.backupFileService.hasBackups().then(hasBackups => { + if (hasBackups) { + return TPromise.as([]); // do not open any empty untitled file if we have backups to restore + } + + return TPromise.as([{}]); + }); + } + + return TPromise.as([]); + } + + private toInputs(paths?: IPath[]): IResourceInput[] { + if (!paths || !paths.length) { + return []; + } + + return paths.map(p => { + const input = {}; + input.resource = URI.file(p.filePath); + + input.options = { + pinned: true // opening on startup is always pinned and not preview + }; + + if (p.lineNumber) { + input.options.selection = { + startLineNumber: p.lineNumber, + startColumn: p.columnNumber + }; + } + + return input; + }); + } + + private openUntitledFile() { + const startupEditor = this.configurationService.lookup('workbench.startupEditor'); + + // Fallback to previous workbench.welcome.enabled setting in case startupEditor is not defined + if (!startupEditor.user && !startupEditor.workspace) { + const welcomeEnabled = this.configurationService.lookup('workbench.welcome.enabled'); + if (typeof welcomeEnabled.value === 'boolean') { + return !welcomeEnabled.value; + } + } + + return startupEditor.value === 'newUntitledFile'; + } + + private initServices(): void { + const { serviceCollection } = this.workbenchParams; + + this.toDispose.push(this.lifecycleService.onWillShutdown(event => this.onWillShutdown(event))); + this.toDispose.push(this.lifecycleService.onShutdown(this.shutdownComponents, this)); + + // Services we contribute + serviceCollection.set(IPartService, this); + + // Clipboard + serviceCollection.set(IClipboardService, new ClipboardService()); + + // Status bar + this.statusbarPart = this.instantiationService.createInstance(StatusbarPart, Identifiers.STATUSBAR_PART); + this.toDispose.push(this.statusbarPart); + this.toShutdown.push(this.statusbarPart); + serviceCollection.set(IStatusbarService, this.statusbarPart); + + // Progress 2 + serviceCollection.set(IProgressService2, new SyncDescriptor(ProgressService2)); + + // Keybindings + this.contextKeyService = this.instantiationService.createInstance(ContextKeyService); + serviceCollection.set(IContextKeyService, this.contextKeyService); + + this.keybindingService = this.instantiationService.createInstance(WorkbenchKeybindingService, window); + serviceCollection.set(IKeybindingService, this.keybindingService); + + // List + serviceCollection.set(IListService, this.instantiationService.createInstance(ListService)); + + // Context Menu + serviceCollection.set(IContextMenuService, new SyncDescriptor(ContextMenuService)); + + // Menus/Actions + serviceCollection.set(IMenuService, new SyncDescriptor(MenuService)); + + // Sidebar part + this.sidebarPart = this.instantiationService.createInstance(SidebarPart, Identifiers.SIDEBAR_PART); + this.toDispose.push(this.sidebarPart); + this.toShutdown.push(this.sidebarPart); + + // Viewlet service + this.viewletService = this.instantiationService.createInstance(ViewletService, this.sidebarPart); + serviceCollection.set(IViewletService, this.viewletService); + + // Panel service (panel part) + this.panelPart = this.instantiationService.createInstance(PanelPart, Identifiers.PANEL_PART); + this.toDispose.push(this.panelPart); + this.toShutdown.push(this.panelPart); + serviceCollection.set(IPanelService, this.panelPart); + + // Activity service (activitybar part) + this.activitybarPart = this.instantiationService.createInstance(ActivitybarPart, Identifiers.ACTIVITYBAR_PART); + this.toDispose.push(this.activitybarPart); + this.toShutdown.push(this.activitybarPart); + serviceCollection.set(IActivityBarService, this.activitybarPart); + + // Editor service (editor part) + this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, !this.hasFilesToCreateOpenOrDiff); + this.toDispose.push(this.editorPart); + this.toShutdown.push(this.editorPart); + this.editorService = this.instantiationService.createInstance(WorkbenchEditorService, this.editorPart); + serviceCollection.set(IWorkbenchEditorService, this.editorService); + serviceCollection.set(IEditorGroupService, this.editorPart); + + // Title bar + this.titlebarPart = this.instantiationService.createInstance(TitlebarPart, Identifiers.TITLEBAR_PART); + this.toDispose.push(this.titlebarPart); + this.toShutdown.push(this.titlebarPart); + serviceCollection.set(ITitleService, this.titlebarPart); + + // File Service + const fileService = this.instantiationService.createInstance(RemoteFileService); + serviceCollection.set(IFileService, fileService); + this.toDispose.push(fileService.onFileChanges(e => this.configurationService.handleWorkspaceFileEvents(e))); + + // History + serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService)); + + // Backup File Service + this.backupFileService = this.instantiationService.createInstance(BackupFileService, this.workbenchParams.configuration.backupPath); + serviceCollection.set(IBackupFileService, this.backupFileService); + + // Text File Service + serviceCollection.set(ITextFileService, new SyncDescriptor(TextFileService)); + + // SCM Service + serviceCollection.set(ISCMService, new SyncDescriptor(SCMService)); + + // Text Model Resolver Service + serviceCollection.set(ITextModelService, new SyncDescriptor(TextModelResolverService)); + + // JSON Editing + const jsonEditingService = this.instantiationService.createInstance(JSONEditingService); + serviceCollection.set(IJSONEditingService, jsonEditingService); + + // Configuration Editing + this.configurationEditingService = this.instantiationService.createInstance(ConfigurationEditingService); + serviceCollection.set(IConfigurationEditingService, this.configurationEditingService); + + // Workspace Editing + serviceCollection.set(IWorkspaceEditingService, new SyncDescriptor(WorkspaceEditingService)); + + // Keybinding Editing + serviceCollection.set(IKeybindingEditingService, this.instantiationService.createInstance(KeybindingsEditingService)); + + // Configuration Resolver + serviceCollection.set(IConfigurationResolverService, new SyncDescriptor(ConfigurationResolverService, process.env)); + + // Workspace Migrating + this.workspaceMigrationService = this.instantiationService.createInstance(WorkspaceMigrationService); + serviceCollection.set(IWorkspaceMigrationService, this.workspaceMigrationService); + + // Quick open service (quick open controller) + this.quickOpen = this.instantiationService.createInstance(QuickOpenController); + this.toDispose.push(this.quickOpen); + this.toShutdown.push(this.quickOpen); + serviceCollection.set(IQuickOpenService, this.quickOpen); + + // {{SQL CARBON EDIT}} + // SQL Tools services + serviceCollection.set(IAngularEventingService, this.instantiationService.createInstance(AngularEventingService)); + serviceCollection.set(ISqlOAuthService, this.instantiationService.createInstance(SqlOAuthService)); + serviceCollection.set(sqlIClipboardService, this.instantiationService.createInstance(sqlClipboardService)); + serviceCollection.set(ICapabilitiesService, this.instantiationService.createInstance(CapabilitiesService)); + serviceCollection.set(IErrorMessageService, this.instantiationService.createInstance(ErrorMessageService)); + serviceCollection.set(IConnectionDialogService, this.instantiationService.createInstance(ConnectionDialogService)); + serviceCollection.set(IServerGroupController, this.instantiationService.createInstance(ServerGroupController)); + serviceCollection.set(ICredentialsService, this.instantiationService.createInstance(CredentialsService)); + serviceCollection.set(IResourceProviderService, this.instantiationService.createInstance(ResourceProviderService)); + let connectionManagementService = this.instantiationService.createInstance(ConnectionManagementService, undefined, undefined); + serviceCollection.set(IConnectionManagementService, connectionManagementService); + serviceCollection.set(ISerializationService, this.instantiationService.createInstance(SerializationService)); + serviceCollection.set(IQueryManagementService, this.instantiationService.createInstance(QueryManagementService)); + serviceCollection.set(IQueryModelService, this.instantiationService.createInstance(QueryModelService)); + serviceCollection.set(IQueryEditorService, this.instantiationService.createInstance(QueryEditorService)); + serviceCollection.set(IEditorDescriptorService, this.instantiationService.createInstance(EditorDescriptorService)); + serviceCollection.set(ITaskService, this.instantiationService.createInstance(TaskService)); + serviceCollection.set(IMetadataService, this.instantiationService.createInstance(MetadataService)); + serviceCollection.set(IObjectExplorerService, this.instantiationService.createInstance(ObjectExplorerService)); + serviceCollection.set(IScriptingService, this.instantiationService.createInstance(ScriptingService)); + serviceCollection.set(IAdminService, this.instantiationService.createInstance(AdminService)); + serviceCollection.set(IDisasterRecoveryService, this.instantiationService.createInstance(DisasterRecoveryService)); + serviceCollection.set(IDisasterRecoveryUiService, this.instantiationService.createInstance(DisasterRecoveryUiService)); + serviceCollection.set(IRestoreDialogController, this.instantiationService.createInstance(RestoreDialogController)); + serviceCollection.set(IFileBrowserService, this.instantiationService.createInstance(FileBrowserService)); + serviceCollection.set(IFileBrowserDialogController, this.instantiationService.createInstance(FileBrowserDialogController)); + serviceCollection.set(IInsightsDialogService, this.instantiationService.createInstance(InsightsDialogService)); + let accountManagementService = this.instantiationService.createInstance(AccountManagementService, undefined); + serviceCollection.set(IAccountManagementService, accountManagementService); + serviceCollection.set(IAccountPickerService, this.instantiationService.createInstance(AccountPickerService)); + serviceCollection.set(IBootstrapService, this.instantiationService.createInstance(BootstrapService)); + serviceCollection.set(IProfilerService, this.instantiationService.createInstance(ProfilerService)); + + this.toDispose.push(connectionManagementService); + this.toShutdown.push(connectionManagementService); + this.toShutdown.push(accountManagementService); + + // Contributed services + const contributedServices = getServices(); + for (let contributedService of contributedServices) { + serviceCollection.set(contributedService.id, contributedService.descriptor); + } + + // Set the some services to registries that have been created eagerly + Registry.as(ActionBarExtensions.Actionbar).setInstantiationService(this.instantiationService); + Registry.as(WorkbenchExtensions.Workbench).setInstantiationService(this.instantiationService); + Registry.as(EditorExtensions.Editors).setInstantiationService(this.instantiationService); + } + + private initSettings(): void { + + // Sidebar visibility + this.sideBarHidden = this.storageService.getBoolean(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE, !this.contextService.hasWorkspace()); + + // Panel part visibility + const panelRegistry = Registry.as(PanelExtensions.Panels); + this.panelHidden = this.storageService.getBoolean(Workbench.panelHiddenSettingKey, StorageScope.WORKSPACE, true); + if (!panelRegistry.getDefaultPanelId()) { + this.panelHidden = true; // we hide panel part if there is no default panel + } + + // Sidebar position + const sideBarPosition = this.configurationService.lookup(Workbench.sidebarPositionConfigurationKey).value; + this.sideBarPosition = (sideBarPosition === 'right') ? Position.RIGHT : Position.LEFT; + + // Statusbar visibility + const statusBarVisible = this.configurationService.lookup(Workbench.statusbarVisibleConfigurationKey).value; + this.statusBarHidden = !statusBarVisible; + + // Activity bar visibility + const activityBarVisible = this.configurationService.lookup(Workbench.activityBarVisibleConfigurationKey).value; + this.activityBarHidden = !activityBarVisible; + + // Font aliasing + this.fontAliasing = this.configurationService.lookup(Workbench.fontAliasingConfigurationKey).value; + + // Zen mode + this.zenMode = { + active: false, + transitionedToFullScreen: false, + wasSideBarVisible: false, + wasPanelVisible: false + }; + } + + /** + * Returns whether the workbench has been started. + */ + public isStarted(): boolean { + return this.workbenchStarted && !this.workbenchShutdown; + } + + /** + * Returns whether the workbench has been fully created. + */ + public isCreated(): boolean { + return this.workbenchCreated && this.workbenchStarted; + } + + public joinCreation(): TPromise { + return this.creationPromise; + } + + public hasFocus(part: Parts): boolean { + const activeElement = document.activeElement; + if (!activeElement) { + return false; + } + + const container = this.getContainer(part); + return DOM.isAncestor(activeElement, container); + } + + public getContainer(part: Parts): HTMLElement { + let container: Builder = null; + switch (part) { + case Parts.TITLEBAR_PART: + container = this.titlebarPart.getContainer(); + break; + case Parts.ACTIVITYBAR_PART: + container = this.activitybarPart.getContainer(); + break; + case Parts.SIDEBAR_PART: + container = this.sidebarPart.getContainer(); + break; + case Parts.PANEL_PART: + container = this.panelPart.getContainer(); + break; + case Parts.EDITOR_PART: + container = this.editorPart.getContainer(); + break; + case Parts.STATUSBAR_PART: + container = this.statusbarPart.getContainer(); + break; + } + return container && container.getHTMLElement(); + } + + public isVisible(part: Parts): boolean { + switch (part) { + case Parts.TITLEBAR_PART: + return this.getCustomTitleBarStyle() && !browser.isFullscreen(); + case Parts.SIDEBAR_PART: + return !this.sideBarHidden; + case Parts.PANEL_PART: + return !this.panelHidden; + case Parts.STATUSBAR_PART: + return !this.statusBarHidden; + case Parts.ACTIVITYBAR_PART: + return !this.activityBarHidden; + } + + return true; // any other part cannot be hidden + } + + public getTitleBarOffset(): number { + let offset = 0; + if (this.isVisible(Parts.TITLEBAR_PART)) { + offset = 22 / browser.getZoomFactor(); // adjust the position based on title bar size and zoom factor + } + + return offset; + } + + private getCustomTitleBarStyle(): 'custom' { + // {{SQL CARBON EDIT}} + // turn-off custom menus to avoid bug calculating size of SQL editor + /* + if (!isMacintosh) { + return null; // custom title bar is only supported on Mac currently + } + + const isDev = !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment; + if (isDev) { + return null; // not enabled when developing due to https://github.com/electron/electron/issues/3647 + } + + const windowConfig = this.configurationService.getConfiguration(); + if (windowConfig && windowConfig.window) { + const useNativeTabs = windowConfig.window.nativeTabs; + if (useNativeTabs) { + return null; // native tabs on sierra do not work with custom title style + } + + const style = windowConfig.window.titleBarStyle; + if (style === 'custom') { + return style; + } + } + */ + + return null; + } + + private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void { + this.statusBarHidden = hidden; + + + // Layout + if (!skipLayout) { + this.workbenchLayout.layout(); + } + } + + public setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void { + this.activityBarHidden = hidden; + + + // Layout + if (!skipLayout) { + this.workbenchLayout.layout(); + } + } + + public setSideBarHidden(hidden: boolean, skipLayout?: boolean): TPromise { + this.sideBarHidden = hidden; + + // Adjust CSS + if (hidden) { + this.workbench.addClass('nosidebar'); + } else { + this.workbench.removeClass('nosidebar'); + } + + // If sidebar becomes hidden, also hide the current active Viewlet if any + let promise = TPromise.as(null); + if (hidden && this.sidebarPart.getActiveViewlet()) { + promise = this.sidebarPart.hideActiveViewlet().then(() => { + const activeEditor = this.editorPart.getActiveEditor(); + const activePanel = this.panelPart.getActivePanel(); + + // Pass Focus to Editor or Panel if Sidebar is now hidden + if (this.hasFocus(Parts.PANEL_PART) && activePanel) { + activePanel.focus(); + } else if (activeEditor) { + activeEditor.focus(); + } + }); + } + + // If sidebar becomes visible, show last active Viewlet or default viewlet + else if (!hidden && !this.sidebarPart.getActiveViewlet()) { + const viewletToOpen = this.sidebarPart.getLastActiveViewletId(); + if (viewletToOpen) { + promise = this.sidebarPart.openViewlet(viewletToOpen, true); + } + } + + return promise.then(() => { + + // Remember in settings + const defaultHidden = !this.contextService.hasWorkspace(); + if (hidden !== defaultHidden) { + this.storageService.store(Workbench.sidebarHiddenSettingKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE); + } + + // Layout + if (!skipLayout) { + this.workbenchLayout.layout(); + } + }); + } + + public setPanelHidden(hidden: boolean, skipLayout?: boolean): TPromise { + this.panelHidden = hidden; + + // Adjust CSS + if (hidden) { + this.workbench.addClass('nopanel'); + } else { + this.workbench.removeClass('nopanel'); + } + + // If panel part becomes hidden, also hide the current active panel if any + let promise = TPromise.as(null); + if (hidden && this.panelPart.getActivePanel()) { + promise = this.panelPart.hideActivePanel().then(() => { + // Pass Focus to Editor if Panel part is now hidden + const editor = this.editorPart.getActiveEditor(); + if (editor) { + editor.focus(); + } + }); + } + + // If panel part becomes visible, show last active panel or default panel + else if (!hidden && !this.panelPart.getActivePanel()) { + const panelToOpen = this.panelPart.getLastActivePanelId(); + if (panelToOpen) { + promise = this.panelPart.openPanel(panelToOpen, true); + } + } + + return promise.then(() => { + + // Remember in settings + if (!hidden) { + this.storageService.store(Workbench.panelHiddenSettingKey, 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(Workbench.panelHiddenSettingKey, StorageScope.WORKSPACE); + } + + // Layout + if (!skipLayout) { + this.workbenchLayout.layout(); + } + }); + } + + public toggleMaximizedPanel(): void { + this.workbenchLayout.layout({ toggleMaximizedPanel: true }); + } + + public isPanelMaximized(): boolean { + return this.workbenchLayout.isPanelMaximized(); + } + + public getSideBarPosition(): Position { + return this.sideBarPosition; + } + + private setSideBarPosition(position: Position): void { + if (this.sideBarHidden) { + this.setSideBarHidden(false, true /* Skip Layout */).done(undefined, errors.onUnexpectedError); + } + + const newPositionValue = (position === Position.LEFT) ? 'left' : 'right'; + const oldPositionValue = (this.sideBarPosition === Position.LEFT) ? 'left' : 'right'; + this.sideBarPosition = position; + + // Adjust CSS + this.activitybarPart.getContainer().removeClass(oldPositionValue); + this.sidebarPart.getContainer().removeClass(oldPositionValue); + this.activitybarPart.getContainer().addClass(newPositionValue); + this.sidebarPart.getContainer().addClass(newPositionValue); + + // Update Styles + this.activitybarPart.updateStyles(); + this.sidebarPart.updateStyles(); + + // Layout + this.workbenchLayout.layout(); + } + + private setFontAliasing(aliasing: string) { + this.fontAliasing = aliasing; + + document.body.style['-webkit-font-smoothing'] = (aliasing === 'default' ? '' : aliasing); + } + + public dispose(): void { + if (this.isStarted()) { + this.shutdownComponents(); + this.workbenchShutdown = true; + } + + this.toDispose = dispose(this.toDispose); + } + + /** + * Asks the workbench and all its UI components inside to lay out according to + * the containers dimension the workbench is living in. + */ + public layout(options?: ILayoutOptions): void { + if (this.isStarted()) { + this.workbenchLayout.layout(options); + } + } + + private onWillShutdown(event: ShutdownEvent): void { + + if (event.reason === ShutdownReason.RELOAD) { + const workspace = event.payload; + + // We are transitioning into a workspace from an empty workspace or workspace, and + // as such we want to migrate UI state from the current workspace to the new one. + if (isWorkspaceIdentifier(workspace)) { + event.veto(this.instantiationService.createInstance(WorkspaceMigrationService).migrate(workspace).then(() => false, () => false)); + } + } + } + + private shutdownComponents(reason = ShutdownReason.QUIT): void { + + // Restore sidebar if we are being shutdown as a matter of a reload + if (reason === ShutdownReason.RELOAD) { + this.storageService.store(Workbench.sidebarRestoreSettingKey, 'true', StorageScope.WORKSPACE); + } + + // Preserve zen mode only on reload. Real quit gets out of zen mode so novice users do not get stuck in zen mode. + const zenConfig = this.configurationService.getConfiguration('zenMode'); + const zenModeActive = (zenConfig.restore || reason === ShutdownReason.RELOAD) && this.zenMode.active; + if (zenModeActive) { + this.storageService.store(Workbench.zenModeActiveSettingKey, true, StorageScope.WORKSPACE); + } else { + this.storageService.remove(Workbench.zenModeActiveSettingKey, StorageScope.WORKSPACE); + } + + // Pass shutdown on to each participant + this.toShutdown.forEach(s => s.shutdown()); + } + + private registerListeners(): void { + + // Listen to editor changes + this.toDispose.push(this.editorPart.onEditorsChanged(() => this.onEditorsChanged())); + + // Handle message service and quick open events + this.toDispose.push((this.messageService).onMessagesShowing(() => this.messagesVisibleContext.set(true))); + this.toDispose.push((this.messageService).onMessagesCleared(() => this.messagesVisibleContext.reset())); + + this.toDispose.push(this.quickOpen.onShow(() => (this.messageService).suspend())); // when quick open is open, don't show messages behind + this.toDispose.push(this.quickOpen.onHide(() => (this.messageService).resume())); // resume messages once quick open is closed again + + // Configuration changes + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(() => this.onDidUpdateConfiguration())); + + // Fullscreen changes + this.toDispose.push(browser.onDidChangeFullscreen(() => this.onFullscreenChanged())); + } + + private onFullscreenChanged(): void { + if (!this.isCreated) { + return; // we need to be ready + } + + // Apply as CSS class + const isFullscreen = browser.isFullscreen(); + if (isFullscreen) { + this.addClass('fullscreen'); + } else { + this.removeClass('fullscreen'); + if (this.zenMode.transitionedToFullScreen && this.zenMode.active) { + this.toggleZenMode(); + } + } + + // Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update + const hasCustomTitle = this.getCustomTitleBarStyle() === 'custom'; + if (hasCustomTitle) { + this._onTitleBarVisibilityChange.fire(); + this.layout(); // handle title bar when fullscreen changes + } + } + + private onEditorsChanged(): void { + const visibleEditors = this.editorService.getVisibleEditors().length; + + // Close when empty: check if we should close the window based on the setting + // Overruled by: window has a workspace opened or this window is for extension development + // or setting is disabled. Also enabled when running with --wait from the command line. + if (visibleEditors === 0 && !this.contextService.hasWorkspace() && !this.environmentService.isExtensionDevelopment) { + const closeWhenEmpty = this.configurationService.lookup(Workbench.closeWhenEmptyConfigurationKey).value; + if (closeWhenEmpty || this.environmentService.args.wait) { + this.closeEmptyWindowScheduler.schedule(); + } + } + + // We update the editorpart class to indicate if an editor is opened or not + // through a delay to accomodate for fast editor switching + this.handleEditorBackground(); + } + + private handleEditorBackground(): void { + const visibleEditors = this.editorService.getVisibleEditors().length; + + const editorContainer = this.editorPart.getContainer(); + if (visibleEditors === 0) { + this.editorsVisibleContext.reset(); + this.editorBackgroundDelayer.trigger(() => editorContainer.addClass('empty')); + } else { + this.editorsVisibleContext.set(true); + this.editorBackgroundDelayer.trigger(() => editorContainer.removeClass('empty')); + } + } + + private onAllEditorsClosed(): void { + const visibleEditors = this.editorService.getVisibleEditors().length; + if (visibleEditors === 0) { + this.windowService.closeWindow(); + } + } + + private onDidUpdateConfiguration(skipLayout?: boolean): void { + const newSidebarPositionValue = this.configurationService.lookup(Workbench.sidebarPositionConfigurationKey).value; + const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT; + if (newSidebarPosition !== this.getSideBarPosition()) { + this.setSideBarPosition(newSidebarPosition); + } + + const fontAliasing = this.configurationService.lookup(Workbench.fontAliasingConfigurationKey).value; + if (fontAliasing !== this.fontAliasing) { + this.setFontAliasing(fontAliasing); + } + + if (!this.zenMode.active) { + const newStatusbarHiddenValue = !this.configurationService.lookup(Workbench.statusbarVisibleConfigurationKey).value; + if (newStatusbarHiddenValue !== this.statusBarHidden) { + this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout); + } + + const newActivityBarHiddenValue = !this.configurationService.lookup(Workbench.activityBarVisibleConfigurationKey).value; + if (newActivityBarHiddenValue !== this.activityBarHidden) { + this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout); + } + } + } + + private createWorkbenchLayout(): void { + this.workbenchLayout = this.instantiationService.createInstance(WorkbenchLayout, + $(this.container), // Parent + this.workbench, // Workbench Container + { + titlebar: this.titlebarPart, // Title Bar + activitybar: this.activitybarPart, // Activity Bar + editor: this.editorPart, // Editor + sidebar: this.sidebarPart, // Sidebar + panel: this.panelPart, // Panel Part + statusbar: this.statusbarPart, // Statusbar + }, + this.quickOpen // Quickopen + ); + + this.toDispose.push(this.workbenchLayout); + } + + private createWorkbench(): void { + + // Create Workbench DIV Off-DOM + this.workbenchContainer = $('.monaco-workbench-container'); + this.workbench = $().div({ 'class': 'monaco-workbench ' + (isWindows ? 'windows' : isLinux ? 'linux' : 'mac'), id: Identifiers.WORKBENCH_CONTAINER }).appendTo(this.workbenchContainer); + } + + private renderWorkbench(): void { + + // Apply sidebar state as CSS class + if (this.sideBarHidden) { + this.workbench.addClass('nosidebar'); + } + if (this.panelHidden) { + this.workbench.addClass('nopanel'); + } + + // Apply font aliasing + this.setFontAliasing(this.fontAliasing); + + // Apply title style if shown + const titleStyle = this.getCustomTitleBarStyle(); + if (titleStyle) { + DOM.addClass(this.parent, `titlebar-style-${titleStyle}`); + } + + // Apply fullscreen state + if (browser.isFullscreen()) { + this.workbench.addClass('fullscreen'); + } + + // Create Parts + this.createTitlebarPart(); + this.createActivityBarPart(); + this.createSidebarPart(); + this.createEditorPart(); + this.createPanelPart(); + this.createStatusbarPart(); + + // Add Workbench to DOM + this.workbenchContainer.build(this.container); + } + + private createTitlebarPart(): void { + const titlebarContainer = $(this.workbench).div({ + 'class': ['part', 'titlebar'], + id: Identifiers.TITLEBAR_PART, + role: 'contentinfo' + }); + + this.titlebarPart.create(titlebarContainer); + } + + private createActivityBarPart(): void { + const activitybarPartContainer = $(this.workbench) + .div({ + 'class': ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'], + id: Identifiers.ACTIVITYBAR_PART, + role: 'navigation' + }); + + this.activitybarPart.create(activitybarPartContainer); + } + + private createSidebarPart(): void { + const sidebarPartContainer = $(this.workbench) + .div({ + 'class': ['part', 'sidebar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'], + id: Identifiers.SIDEBAR_PART, + role: 'complementary' + }); + + this.sidebarPart.create(sidebarPartContainer); + } + + private createPanelPart(): void { + const panelPartContainer = $(this.workbench) + .div({ + 'class': ['part', 'panel'], + id: Identifiers.PANEL_PART, + role: 'complementary' + }); + + this.panelPart.create(panelPartContainer); + } + + private createEditorPart(): void { + const editorContainer = $(this.workbench) + .div({ + 'class': ['part', 'editor', 'empty'], + id: Identifiers.EDITOR_PART, + role: 'main' + }); + + this.editorPart.create(editorContainer); + } + + private createStatusbarPart(): void { + const statusbarContainer = $(this.workbench).div({ + 'class': ['part', 'statusbar'], + id: Identifiers.STATUSBAR_PART, + role: 'contentinfo' + }); + + this.statusbarPart.create(statusbarContainer); + } + + public getEditorPart(): EditorPart { + assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); + + return this.editorPart; + } + + public getSidebarPart(): SidebarPart { + assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); + + return this.sidebarPart; + } + + public getPanelPart(): PanelPart { + assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); + + return this.panelPart; + } + + public getInstantiationService(): IInstantiationService { + assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); + + return this.instantiationService; + } + + public addClass(clazz: string): void { + if (this.workbench) { + this.workbench.addClass(clazz); + } + } + + public removeClass(clazz: string): void { + if (this.workbench) { + this.workbench.removeClass(clazz); + } + } + + public getWorkbenchElementId(): string { + return Identifiers.WORKBENCH_CONTAINER; + } + + public toggleZenMode(skipLayout?: boolean): void { + this.zenMode.active = !this.zenMode.active; + // Check if zen mode transitioned to full screen and if now we are out of zen mode -> we need to go out of full screen + let toggleFullScreen = false; + if (this.zenMode.active) { + const config = this.configurationService.getConfiguration('zenMode'); + toggleFullScreen = !browser.isFullscreen() && config.fullScreen; + this.zenMode.transitionedToFullScreen = toggleFullScreen; + this.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART); + this.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART); + this.setPanelHidden(true, true).done(undefined, errors.onUnexpectedError); + this.setSideBarHidden(true, true).done(undefined, errors.onUnexpectedError); + + if (config.hideActivityBar) { + this.setActivityBarHidden(true, true); + } + if (config.hideStatusBar) { + this.setStatusBarHidden(true, true); + } + if (config.hideTabs) { + this.editorPart.hideTabs(true); + } + } else { + if (this.zenMode.wasPanelVisible) { + this.setPanelHidden(false, true).done(undefined, errors.onUnexpectedError); + } + if (this.zenMode.wasSideBarVisible) { + this.setSideBarHidden(false, true).done(undefined, errors.onUnexpectedError); + } + // Status bar and activity bar visibility come from settings -> update their visibility. + this.onDidUpdateConfiguration(true); + this.editorPart.hideTabs(false); + const activeEditor = this.editorPart.getActiveEditor(); + if (activeEditor) { + activeEditor.focus(); + } + toggleFullScreen = this.zenMode.transitionedToFullScreen && browser.isFullscreen(); + } + this.inZenMode.set(this.zenMode.active); + + if (!skipLayout) { + this.layout(); + } + if (toggleFullScreen) { + this.windowService.toggleFullScreen().done(undefined, errors.onUnexpectedError); + } + } + + // Resize requested part along the main axis + // layout will do all the math for us and adjusts the other Parts + public resizePart(part: Parts, sizeChange: number): void { + switch (part) { + case Parts.SIDEBAR_PART: + case Parts.PANEL_PART: + case Parts.EDITOR_PART: + this.workbenchLayout.resizePart(part, sizeChange); + break; + default: + return; // Cannot resize other parts + } + } + + + private shouldRestoreLastOpenedViewlet(): boolean { + if (!this.environmentService.isBuilt) { + return true; // always restore sidebar when we are in development mode + } + + const restore = this.storageService.getBoolean(Workbench.sidebarRestoreSettingKey, StorageScope.WORKSPACE); + if (restore) { + this.storageService.remove(Workbench.sidebarRestoreSettingKey, StorageScope.WORKSPACE); // only support once + } + + return restore; + } +} diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts new file mode 100644 index 0000000000..d721976e7b --- /dev/null +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -0,0 +1,255 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import pfs = require('vs/base/node/pfs'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { join } from 'path'; +import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; +import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; +import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { QueryType, ISearchQuery } from 'vs/platform/search/common/search'; +import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; +import { IInitData, IEnvironment, IWorkspaceData, MainContext } from 'vs/workbench/api/node/extHost.protocol'; +import * as errors from 'vs/base/common/errors'; +import * as watchdog from 'native-watchdog'; + +// const nativeExit = process.exit.bind(process); +process.exit = function () { + const err = new Error('An extension called process.exit() and this was prevented.'); + console.warn(err.stack); +}; +export function exit(code?: number) { + //nativeExit(code); + + // TODO@electron + // See https://github.com/Microsoft/vscode/issues/32990 + // calling process.exit() does not exit the process when the process is being debugged + // It waits for the debugger to disconnect, but in our version, the debugger does not + // receive an event that the process desires to exit such that it can disconnect. + + // Do exactly what node.js would have done, minus the wait for the debugger part + + if (code || code === 0) { + process.exitCode = code; + } + + if (!(process)._exiting) { + (process)._exiting = true; + process.emit('exit', process.exitCode || 0); + } + watchdog.exit(process.exitCode || 0); +} + +interface ITestRunner { + run(testsRoot: string, clb: (error: Error, failures?: number) => void): void; +} + +export class ExtensionHostMain { + + private _isTerminating: boolean = false; + private _diskSearch: DiskSearch; + private _workspace: IWorkspaceData; + private _environment: IEnvironment; + private _extensionService: ExtHostExtensionService; + + constructor(rpcProtocol: RPCProtocol, initData: IInitData) { + this._environment = initData.environment; + this._workspace = initData.workspace; + + // services + const threadService = new ExtHostThreadService(rpcProtocol); + this._extensionService = new ExtHostExtensionService(initData, threadService); + + // error forwarding and stack trace scanning + const extensionErrors = new WeakMap(); + this._extensionService.getExtensionPathIndex().then(map => { + (Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => { + let stackTraceMessage = ''; + let extension: IExtensionDescription; + let fileName: string; + for (const call of stackTrace) { + stackTraceMessage += `\n\tat ${call.toString()}`; + fileName = call.getFileName(); + if (!extension && fileName) { + extension = map.findSubstr(fileName); + } + + } + extensionErrors.set(error, extension); + return `${error.name || 'Error'}: ${error.message || ''}${stackTraceMessage}`; + }; + }); + const mainThreadErrors = threadService.get(MainContext.MainThreadErrors); + errors.setUnexpectedErrorHandler(err => { + const data = errors.transformErrorForSerialization(err); + const extension = extensionErrors.get(err); + mainThreadErrors.$onUnexpectedError(data, extension && extension.id); + }); + + // Configure the watchdog to kill our process if the JS event loop is unresponsive for more than 10s + // if (!initData.environment.isExtensionDevelopmentDebug) { + // watchdog.start(10000); + // } + } + + public start(): TPromise { + return this._extensionService.onExtensionAPIReady() + .then(() => this.handleEagerExtensions()) + .then(() => this.handleExtensionTests()); + } + + public terminate(): void { + if (this._isTerminating) { + // we are already shutting down... + return; + } + this._isTerminating = true; + + errors.setUnexpectedErrorHandler((err) => { + // TODO: write to log once we have one + }); + + let allPromises: TPromise[] = []; + try { + let allExtensions = this._extensionService.getAllExtensionDescriptions(); + let allExtensionsIds = allExtensions.map(ext => ext.id); + let activatedExtensions = allExtensionsIds.filter(id => this._extensionService.isActivated(id)); + + allPromises = activatedExtensions.map((extensionId) => { + return this._extensionService.deactivate(extensionId); + }); + } catch (err) { + // TODO: write to log once we have one + } + + let extensionsDeactivated = TPromise.join(allPromises).then(() => void 0); + + // Give extensions 1 second to wrap up any async dispose, then exit + setTimeout(() => { + TPromise.any([TPromise.timeout(4000), extensionsDeactivated]).then(() => exit(), () => exit()); + }, 1000); + } + + // Handle "eager" activation extensions + private handleEagerExtensions(): TPromise { + this._extensionService.activateByEvent('*', true).then(null, (err) => { + console.error(err); + }); + return this.handleWorkspaceContainsEagerExtensions(); + } + + private handleWorkspaceContainsEagerExtensions(): TPromise { + if (!this._workspace || this._workspace.roots.length === 0) { + return TPromise.as(null); + } + + const desiredFilesMap: { + [filename: string]: boolean; + } = {}; + + this._extensionService.getAllExtensionDescriptions().forEach((desc) => { + let activationEvents = desc.activationEvents; + if (!activationEvents) { + return; + } + + for (let i = 0; i < activationEvents.length; i++) { + if (/^workspaceContains:/.test(activationEvents[i])) { + let fileName = activationEvents[i].substr('workspaceContains:'.length); + desiredFilesMap[fileName] = true; + } + } + }); + + const matchingPatterns = Object.keys(desiredFilesMap).map(p => { + // TODO: This is a bit hacky -- maybe this should be implemented by using something like + // `workspaceGlob` or something along those lines? + if (p.indexOf('*') > -1 || p.indexOf('?') > -1) { + if (!this._diskSearch) { + // Shut down this search process after 1s + this._diskSearch = new DiskSearch(false, 1000); + } + + const query: ISearchQuery = { + folderQueries: this._workspace.roots.map(root => ({ folder: root })), + type: QueryType.File, + maxResults: 1, + includePattern: { [p]: true } + }; + + return this._diskSearch.search(query).then(result => result.results.length ? p : undefined); + } else { + // find exact path + return (async resolve => { + for (const { fsPath } of this._workspace.roots) { + if (await pfs.exists(join(fsPath, p))) { + return p; + } + } + return undefined; + })(); + } + }); + + return TPromise.join(matchingPatterns).then(patterns => { + patterns + .filter(p => p !== undefined) + .forEach(p => { + const activationEvent = `workspaceContains:${p}`; + + this._extensionService.activateByEvent(activationEvent, true) + .done(null, err => console.error(err)); + }); + }); + } + + private handleExtensionTests(): TPromise { + if (!this._environment.extensionTestsPath || !this._environment.extensionDevelopmentPath) { + return TPromise.as(null); + } + + // Require the test runner via node require from the provided path + let testRunner: ITestRunner; + let requireError: Error; + try { + testRunner = require.__$__nodeRequire(this._environment.extensionTestsPath); + } catch (error) { + requireError = error; + } + + // Execute the runner if it follows our spec + if (testRunner && typeof testRunner.run === 'function') { + return new TPromise((c, e) => { + testRunner.run(this._environment.extensionTestsPath, (error, failures) => { + if (error) { + e(error.toString()); + } else { + c(null); + } + + // after tests have run, we shutdown the host + this.gracefulExit(failures && failures > 0 ? 1 /* ERROR */ : 0 /* OK */); + }); + }); + } + + // Otherwise make sure to shutdown anyway even in case of an error + else { + this.gracefulExit(1 /* ERROR */); + } + + return TPromise.wrapError(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", this._environment.extensionTestsPath))); + } + + private gracefulExit(code: number): void { + // to give the PH process a chance to flush any outstanding console + // messages to the main process, we delay the exit() by some time + setTimeout(() => exit(code), 500); + } +} diff --git a/src/vs/workbench/node/extensionHostProcess.ts b/src/vs/workbench/node/extensionHostProcess.ts new file mode 100644 index 0000000000..fccefd82c0 --- /dev/null +++ b/src/vs/workbench/node/extensionHostProcess.ts @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ExtensionHostMain, exit } from 'vs/workbench/node/extensionHostMain'; +import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; +import { parse } from 'vs/base/common/marshalling'; +import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; +import { createConnection } from 'net'; +import Event, { filterEvent } from 'vs/base/common/event'; + +interface IRendererConnection { + rpcProtocol: RPCProtocol; + initData: IInitData; +} + +// This calls exit directly in case the initialization is not finished and we need to exit +// Otherwise, if initialization completed we go to extensionHostMain.terminate() +let onTerminate = function () { + exit(); +}; + +function createExtHostProtocol(): TPromise { + + const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST; + + return new TPromise((resolve, reject) => { + + const socket = createConnection(pipeName, () => { + socket.removeListener('error', reject); + resolve(new Protocol(socket)); + }); + socket.once('error', reject); + + }).then(protocol => { + + return new class implements IMessagePassingProtocol { + + private _terminating = false; + + readonly onMessage: Event = filterEvent(protocol.onMessage, msg => { + if (msg.type !== '__$terminate') { + return true; + } + this._terminating = true; + onTerminate(); + return false; + }); + + send(msg: any): void { + if (!this._terminating) { + protocol.send(msg); + } + } + }; + }); +} + +function connectToRenderer(protocol: IMessagePassingProtocol): TPromise { + return new TPromise((c, e) => { + + // Listen init data message + const first = protocol.onMessage(raw => { + first.dispose(); + + const initData = parse(raw); + const rpcProtocol = new RPCProtocol(protocol); + + // Print a console message when rejection isn't handled within N seconds. For details: + // see https://nodejs.org/api/process.html#process_event_unhandledrejection + // and https://nodejs.org/api/process.html#process_event_rejectionhandled + const unhandledPromises: TPromise[] = []; + process.on('unhandledRejection', (reason, promise) => { + unhandledPromises.push(promise); + setTimeout(() => { + const idx = unhandledPromises.indexOf(promise); + if (idx >= 0) { + unhandledPromises.splice(idx, 1); + console.warn('rejected promise not handled within 1 second'); + onUnexpectedError(reason); + } + }, 1000); + }); + process.on('rejectionHandled', promise => { + const idx = unhandledPromises.indexOf(promise); + if (idx >= 0) { + unhandledPromises.splice(idx, 1); + } + }); + + // Print a console message when an exception isn't handled. + process.on('uncaughtException', function (err) { + onUnexpectedError(err); + }); + + // Kill oneself if one's parent dies. Much drama. + setInterval(function () { + try { + process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore. + } catch (e) { + onTerminate(); + } + }, 5000); + + // Tell the outside that we are initialized + protocol.send('initialized'); + + c({ rpcProtocol, initData }); + }); + + // Tell the outside that we are ready to receive messages + protocol.send('ready'); + }); +} + +createExtHostProtocol().then(protocol => { + // connect to main side + return connectToRenderer(protocol); +}).then(renderer => { + // setup things + const extensionHostMain = new ExtensionHostMain(renderer.rpcProtocol, renderer.initData); + onTerminate = () => extensionHostMain.terminate(); + return extensionHostMain.start(); +}).done(null, err => console.error(err)); diff --git a/src/vs/workbench/parts/backup/common/backup.contribution.ts b/src/vs/workbench/parts/backup/common/backup.contribution.ts new file mode 100644 index 0000000000..4677396a17 --- /dev/null +++ b/src/vs/workbench/parts/backup/common/backup.contribution.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { BackupModelTracker } from 'vs/workbench/parts/backup/common/backupModelTracker'; +import { BackupRestorer } from 'vs/workbench/parts/backup/common/backupRestorer'; + +// Register Backup Model Tracker +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + BackupModelTracker +); + +// Register Backup Restorer +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + BackupRestorer +); \ No newline at end of file diff --git a/src/vs/workbench/parts/backup/common/backupModelTracker.ts b/src/vs/workbench/parts/backup/common/backupModelTracker.ts new file mode 100644 index 0000000000..28b647b623 --- /dev/null +++ b/src/vs/workbench/parts/backup/common/backupModelTracker.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import Uri from 'vs/base/common/uri'; +import errors = require('vs/base/common/errors'); +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ITextFileService, TextFileModelChangeEvent, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IFilesConfiguration, AutoSaveConfiguration, CONTENT_CHANGE_EVENT_BUFFER_DELAY } from 'vs/platform/files/common/files'; + +const AUTO_SAVE_AFTER_DELAY_DISABLED_TIME = CONTENT_CHANGE_EVENT_BUFFER_DELAY + 500; + +export class BackupModelTracker implements IWorkbenchContribution { + + public _serviceBrand: any; + + private configuredAutoSaveAfterDelay: boolean; + private toDispose: IDisposable[]; + + constructor( + @IBackupFileService private backupFileService: IBackupFileService, + @ITextFileService private textFileService: ITextFileService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IConfigurationService private configurationService: IConfigurationService + ) { + this.toDispose = []; + + this.registerListeners(); + } + + private registerListeners() { + if (!this.backupFileService.backupEnabled) { + return; + } + + // Listen for text file model changes + this.toDispose.push(this.textFileService.models.onModelContentChanged((e) => this.onTextFileModelChanged(e))); + this.toDispose.push(this.textFileService.models.onModelSaved((e) => this.discardBackup(e.resource))); + this.toDispose.push(this.textFileService.models.onModelDisposed((e) => this.discardBackup(e))); + + // Listen for untitled model changes + this.toDispose.push(this.untitledEditorService.onDidChangeContent((e) => this.onUntitledModelChanged(e))); + this.toDispose.push(this.untitledEditorService.onDidDisposeModel((e) => this.discardBackup(e))); + + // Listen to config changes + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration()))); + } + + private onConfigurationChange(configuration: IFilesConfiguration): void { + if (!configuration || !configuration.files) { + this.configuredAutoSaveAfterDelay = false; + return; + } + this.configuredAutoSaveAfterDelay = + (configuration.files.autoSave === AutoSaveConfiguration.AFTER_DELAY && + configuration.files.autoSaveDelay <= AUTO_SAVE_AFTER_DELAY_DISABLED_TIME); + } + + private onTextFileModelChanged(event: TextFileModelChangeEvent): void { + if (event.kind === StateChange.REVERTED) { + // This must proceed even if auto save after delay is configured in order to clean up + // any backups made before the config change + this.discardBackup(event.resource); + } else if (event.kind === StateChange.CONTENT_CHANGE) { + // Do not backup when auto save after delay is configured + if (!this.configuredAutoSaveAfterDelay) { + const model = this.textFileService.models.get(event.resource); + this.backupFileService.backupResource(model.getResource(), model.getValue(), model.getVersionId()).done(null, errors.onUnexpectedError); + } + } + } + + private onUntitledModelChanged(resource: Uri): void { + if (this.untitledEditorService.isDirty(resource)) { + this.untitledEditorService.loadOrCreate({ resource }).then(model => this.backupFileService.backupResource(resource, model.getValue(), model.getVersionId())).done(null, errors.onUnexpectedError); + } else { + this.discardBackup(resource); + } + } + + private discardBackup(resource: Uri): void { + this.backupFileService.discardResourceBackup(resource).done(null, errors.onUnexpectedError); + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } + + public getId(): string { + return 'vs.backup.backupModelTracker'; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/backup/common/backupRestorer.ts b/src/vs/workbench/parts/backup/common/backupRestorer.ts new file mode 100644 index 0000000000..97221b08f4 --- /dev/null +++ b/src/vs/workbench/parts/backup/common/backupRestorer.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import errors = require('vs/base/common/errors'); +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Position, IResourceInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { Schemas } from 'vs/base/common/network'; + +export class BackupRestorer implements IWorkbenchContribution { + + // {{SQL CARBON EDIT}} + private static readonly SQLQUERY_REGEX = /SQLQuery\d+/; + private static readonly UNTITLED_REGEX = /Untitled-\d+/; + + constructor( + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IPartService private partService: IPartService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IBackupFileService private backupFileService: IBackupFileService, + @ITextFileService private textFileService: ITextFileService, + @IEditorGroupService private groupService: IEditorGroupService + ) { + this.restoreBackups(); + } + + private restoreBackups(): void { + if (this.backupFileService.backupEnabled) { + this.partService.joinCreation().then(() => { + this.doRestoreBackups().done(null, errors.onUnexpectedError); + }); + } + } + + private doRestoreBackups(): TPromise { + + // Find all files and untitled with backups + return this.backupFileService.getWorkspaceFileBackups().then(backups => { + + // Resolve backups that are opened in stacks model + return this.doResolveOpenedBackups(backups).then(unresolved => { + + // Some failed to restore or were not opened at all so we open and resolve them manually + if (unresolved.length > 0) { + return this.doOpenEditors(unresolved).then(() => this.doResolveOpenedBackups(unresolved)); + } + + return void 0; + }); + }); + } + + private doResolveOpenedBackups(backups: URI[]): TPromise { + const stacks = this.groupService.getStacksModel(); + + const restorePromises: TPromise[] = []; + const unresolved: URI[] = []; + + backups.forEach(backup => { + if (stacks.isOpen(backup)) { + if (backup.scheme === Schemas.file) { + restorePromises.push(this.textFileService.models.loadOrCreate(backup).then(null, () => unresolved.push(backup))); + } else if (backup.scheme === UNTITLED_SCHEMA) { + restorePromises.push(this.untitledEditorService.loadOrCreate({ resource: backup }).then(null, () => unresolved.push(backup))); + } + } else { + unresolved.push(backup); + } + }); + + return TPromise.join(restorePromises).then(() => unresolved, () => unresolved); + } + + private doOpenEditors(resources: URI[]): TPromise { + const stacks = this.groupService.getStacksModel(); + const hasOpenedEditors = stacks.groups.length > 0; + const inputs = resources.map((resource, index) => this.resolveInput(resource, index, hasOpenedEditors)); + + // Open all remaining backups as editors and resolve them to load their backups + return this.editorService.openEditors(inputs.map(input => { return { input, position: Position.ONE }; })).then(() => void 0); + } + + private resolveInput(resource: URI, index: number, hasOpenedEditors: boolean): IResourceInput | IUntitledResourceInput { + const options = { pinned: true, preserveFocus: true, inactive: index > 0 || hasOpenedEditors }; + + // {{SQL CARBON EDIT}} + if (resource.scheme === UNTITLED_SCHEMA + && !BackupRestorer.UNTITLED_REGEX.test(resource.fsPath) + && !BackupRestorer.SQLQUERY_REGEX.test(resource.fsPath)) { + // TODO@Ben debt: instead of guessing if an untitled file has an associated file path or not + // this information should be provided by the backup service and stored as meta data within + return { filePath: resource.fsPath, options }; + } + + return { resource, options }; + } + + public getId(): string { + return 'vs.backup.backupRestorer'; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts new file mode 100644 index 0000000000..3d5a3b0b06 --- /dev/null +++ b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts @@ -0,0 +1,159 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as path from 'path'; +import * as cp from 'child_process'; +import * as pfs from 'vs/base/node/pfs'; +import { nfcall } from 'vs/base/common/async'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { Action } from 'vs/base/common/actions'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IEditorService } from 'vs/platform/editor/common/editor'; +import product from 'vs/platform/node/product'; + +interface ILegacyUse { + file: string; + lineNumber: number; +} + +function ignore(code: string, value: T = null): (err: any) => TPromise { + return err => err.code === code ? TPromise.as(value) : TPromise.wrapError(err); +} + +const root = URI.parse(require.toUrl('')).fsPath; +const source = path.resolve(root, '..', 'bin', 'code'); + +function isAvailable(): TPromise { + return pfs.exists(source); +} + +class InstallAction extends Action { + + static ID = 'workbench.action.installCommandLine'; + static LABEL = nls.localize('install', "Install '{0}' command in PATH", product.applicationName); + + constructor( + id: string, + label: string, + @IMessageService private messageService: IMessageService, + @IEditorService private editorService: IEditorService + ) { + super(id, label); + } + + private get target(): string { + return `/usr/local/bin/${product.applicationName}`; + } + + run(): TPromise { + return isAvailable().then(isAvailable => { + if (!isAvailable) { + const message = nls.localize('not available', "This command is not available"); + this.messageService.show(Severity.Info, message); + return undefined; + } + + return this.isInstalled() + .then(isInstalled => { + if (!isAvailable || isInstalled) { + return TPromise.as(null); + } else { + const createSymlink = () => { + return pfs.unlink(this.target) + .then(null, ignore('ENOENT')) + .then(() => pfs.symlink(source, this.target)); + }; + + return createSymlink().then(null, err => { + if (err.code === 'EACCES' || err.code === 'ENOENT') { + return this.createBinFolder() + .then(() => createSymlink()); + } + + return TPromise.wrapError(err); + }); + } + }) + .then(() => { + this.messageService.show(Severity.Info, nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", product.applicationName)); + }); + }); + } + + private isInstalled(): TPromise { + return pfs.lstat(this.target) + .then(stat => stat.isSymbolicLink()) + .then(() => pfs.readlink(this.target)) + .then(link => link === source) + .then(null, ignore('ENOENT', false)); + } + + private createBinFolder(): TPromise { + return new TPromise((c, e) => { + const message = nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."); + const actions = [ + new Action('ok', nls.localize('ok', "OK"), '', true, () => { + const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && chown \\" & (do shell script (\\"whoami\\")) & \\" /usr/local/bin\\" with administrator privileges"'; + + nfcall(cp.exec, command, {}) + .then(null, _ => TPromise.wrapError(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'.")))) + .done(c, e); + + return null; + }), + new Action('cancel2', nls.localize('cancel2', "Cancel"), '', true, () => { e(new Error(nls.localize('aborted', "Aborted"))); return null; }) + ]; + + this.messageService.show(Severity.Info, { message, actions }); + }); + } +} + +class UninstallAction extends Action { + + static ID = 'workbench.action.uninstallCommandLine'; + static LABEL = nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName); + + constructor( + id: string, + label: string, + @IMessageService private messageService: IMessageService + ) { + super(id, label); + } + + private get target(): string { + return `/usr/local/bin/${product.applicationName}`; + } + + run(): TPromise { + return isAvailable().then(isAvailable => { + if (!isAvailable) { + const message = nls.localize('not available', "This command is not available"); + this.messageService.show(Severity.Info, message); + return undefined; + } + + return pfs.unlink(this.target) + .then(null, ignore('ENOENT')) + .then(() => { + this.messageService.show(Severity.Info, nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", product.applicationName)); + }); + }); + } +} + +if (process.platform === 'darwin') { + const category = nls.localize('shellCommand', "Shell Command"); + + const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(InstallAction, InstallAction.ID, InstallAction.LABEL), 'Shell Command: Install \'code\' command in PATH', category); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(UninstallAction, UninstallAction.ID, UninstallAction.LABEL), 'Shell Command: Uninstall \'code\' command from PATH', category); +} diff --git a/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts b/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts new file mode 100644 index 0000000000..eb683a0ead --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import './electron-browser/accessibility'; +import './electron-browser/inspectKeybindings'; +import './electron-browser/menuPreventer'; +import './electron-browser/selectionClipboard'; +import './electron-browser/textMate/inspectTMScopes'; +import './electron-browser/toggleMinimap'; +import './electron-browser/toggleMultiCursorModifier'; +import './electron-browser/toggleRenderControlCharacter'; +import './electron-browser/toggleRenderWhitespace'; +import './electron-browser/toggleWordWrap'; +import './electron-browser/wordWrapMigration'; diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.css b/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.css new file mode 100644 index 0000000000..00a2e5295b --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.css @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .accessibilityHelpWidget { + padding: 10px; + vertical-align: middle; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts b/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts new file mode 100644 index 0000000000..0265da6fca --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts @@ -0,0 +1,337 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./accessibility'; +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as strings from 'vs/base/common/strings'; +import * as dom from 'vs/base/browser/dom'; +import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICommonCodeEditor, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { editorAction, CommonEditorRegistry, EditorAction, EditorCommand } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorWidgetBackground, widgetShadow, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import * as platform from 'vs/base/common/platform'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import URI from 'vs/base/common/uri'; + +const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey('accessibilityHelpWidgetVisible', false); + +@editorContribution +class AccessibilityHelpController extends Disposable implements IEditorContribution { + + private static ID = 'editor.contrib.accessibilityHelpController'; + + public static get(editor: ICommonCodeEditor): AccessibilityHelpController { + return editor.getContribution(AccessibilityHelpController.ID); + } + + private _editor: ICodeEditor; + private _widget: AccessibilityHelpWidget; + + constructor( + editor: ICodeEditor, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(); + + this._editor = editor; + this._widget = this._register(instantiationService.createInstance(AccessibilityHelpWidget, this._editor)); + } + + public getId(): string { + return AccessibilityHelpController.ID; + } + + public show(): void { + this._widget.show(); + } + + public hide(): void { + this._widget.hide(); + } +} + +class AccessibilityHelpWidget extends Widget implements IOverlayWidget { + + private static ID = 'editor.contrib.accessibilityHelpWidget'; + private static WIDTH = 500; + private static HEIGHT = 300; + + private _editor: ICodeEditor; + private _domNode: FastDomNode; + private _contentDomNode: FastDomNode; + private _isVisible: boolean; + private _isVisibleKey: IContextKey; + + constructor( + editor: ICodeEditor, + @IContextKeyService private _contextKeyService: IContextKeyService, + @IKeybindingService private _keybindingService: IKeybindingService, + @IConfigurationService private _configurationService: IConfigurationService, + @IConfigurationEditingService private _configurationEditingService: IConfigurationEditingService, + @IOpenerService private _openerService: IOpenerService + ) { + super(); + + this._editor = editor; + this._isVisibleKey = CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE.bindTo(this._contextKeyService); + + this._domNode = createFastDomNode(document.createElement('div')); + this._domNode.setClassName('accessibilityHelpWidget'); + this._domNode.setWidth(AccessibilityHelpWidget.WIDTH); + this._domNode.setHeight(AccessibilityHelpWidget.HEIGHT); + this._domNode.setDisplay('none'); + this._domNode.setAttribute('role', 'dialog'); + this._domNode.setAttribute('aria-hidden', 'true'); + + this._contentDomNode = createFastDomNode(document.createElement('div')); + this._contentDomNode.setAttribute('role', 'document'); + this._domNode.appendChild(this._contentDomNode); + + this._isVisible = false; + + this._register(this._editor.onDidLayoutChange(() => { + if (this._isVisible) { + this._layout(); + } + })); + + // Intentionally not configurable! + this._register(dom.addStandardDisposableListener(this._contentDomNode.domNode, 'keydown', (e) => { + if (!this._isVisible) { + return; + } + + if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_E)) { + alert(nls.localize('emergencyConfOn', "Now changing the setting `editor.accessibilitySupport` to 'on'.")); + + this._configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { + key: 'editor.accessibilitySupport', + value: 'on' + }); + + e.preventDefault(); + e.stopPropagation(); + } + + if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_H)) { + alert(nls.localize('openingDocs', "Now opening the VS Code Accessibility documentation page.")); + + this._openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=851010')); + + e.preventDefault(); + e.stopPropagation(); + } + })); + + this.onblur(this._contentDomNode.domNode, () => { + this.hide(); + }); + + this._editor.addOverlayWidget(this); + } + + public dispose(): void { + this._editor.removeOverlayWidget(this); + super.dispose(); + } + + public getId(): string { + return AccessibilityHelpWidget.ID; + } + + public getDomNode(): HTMLElement { + return this._domNode.domNode; + } + + public getPosition(): IOverlayWidgetPosition { + return { + preference: null + }; + } + + public show(): void { + if (this._isVisible) { + return; + } + this._isVisible = true; + this._isVisibleKey.set(true); + this._layout(); + this._domNode.setDisplay('block'); + this._domNode.setAttribute('aria-hidden', 'false'); + this._contentDomNode.domNode.tabIndex = 0; + this._buildContent(); + this._contentDomNode.domNode.focus(); + } + + private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string { + let kb = this._keybindingService.lookupKeybinding(commandId); + if (kb) { + return strings.format(msg, kb.getAriaLabel()); + } + return strings.format(noKbMsg, commandId); + } + + private _buildContent() { + let opts = this._editor.getConfiguration(); + let text = nls.localize('introMsg', "Thank you for trying out VS Code's accessibility options."); + + text += '\n\n' + nls.localize('status', "Status:"); + + const configuredValue = this._configurationService.getConfiguration('editor').accessibilitySupport; + const actualValue = opts.accessibilitySupport; + + const emergencyTurnOnMessage = ( + platform.isMacintosh + ? nls.localize('changeConfigToOnMac', "To configure the editor to be permanently optimized for usage with a Screen Reader press Command+E now.") + : nls.localize('changeConfigToOnWinLinux', "To configure the editor to be permanently optimized for usage with a Screen Reader press Control+E now.") + ); + + switch (configuredValue) { + case 'auto': + switch (actualValue) { + case platform.AccessibilitySupport.Unknown: + // Should never happen in VS Code + text += '\n\n - ' + nls.localize('auto_unknown', "The editor is configured to use platform APIs to detect when a Screen Reader is attached, but the current runtime does not support this."); + break; + case platform.AccessibilitySupport.Enabled: + text += '\n\n - ' + nls.localize('auto_on', "The editor has automatically detected a Screen Reader is attached."); + break; + case platform.AccessibilitySupport.Disabled: + text += '\n\n - ' + nls.localize('auto_off', "The editor is configured to automatically detect when a Screen Reader is attached, which is not the case at this time."); + text += ' ' + emergencyTurnOnMessage; + break; + } + break; + case 'on': + text += '\n\n - ' + nls.localize('configuredOn', "The editor is configured to be permanently optimized for usage with a Screen Reader - you can change this by editing the setting `editor.accessibilitySupport`."); + break; + case 'off': + text += '\n\n - ' + nls.localize('configuredOff', "The editor is configured to never be optimized for usage with a Screen Reader."); + text += ' ' + emergencyTurnOnMessage; + break; + } + + const NLS_TAB_FOCUS_MODE_ON = nls.localize('tabFocusModeOnMsg', "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}."); + const NLS_TAB_FOCUS_MODE_ON_NO_KB = nls.localize('tabFocusModeOnMsgNoKb', "Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding."); + const NLS_TAB_FOCUS_MODE_OFF = nls.localize('tabFocusModeOffMsg', "Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}."); + const NLS_TAB_FOCUS_MODE_OFF_NO_KB = nls.localize('tabFocusModeOffMsgNoKb', "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding."); + + if (opts.tabFocusMode) { + text += '\n\n - ' + this._descriptionForCommand(ToggleTabFocusModeAction.ID, NLS_TAB_FOCUS_MODE_ON, NLS_TAB_FOCUS_MODE_ON_NO_KB); + } else { + text += '\n\n - ' + this._descriptionForCommand(ToggleTabFocusModeAction.ID, NLS_TAB_FOCUS_MODE_OFF, NLS_TAB_FOCUS_MODE_OFF_NO_KB); + } + + const openDocMessage = ( + platform.isMacintosh + ? nls.localize('openDocMac', "Press Command+H now to open a browser window with more VS Code information related to Accessibility.") + : nls.localize('openDocWinLinux', "Press Control+H now to open a browser window with more VS Code information related to Accessibility.") + ); + + text += '\n\n' + openDocMessage; + + text += '\n\n' + nls.localize('outroMsg', "You can dismiss this tooltip and return to the editor by pressing Escape or Shift+Escape."); + + this._contentDomNode.domNode.appendChild(renderFormattedText(text)); + // Per https://www.w3.org/TR/wai-aria/roles#document, Authors SHOULD provide a title or label for documents + this._contentDomNode.domNode.setAttribute('aria-label', text); + } + + public hide(): void { + if (!this._isVisible) { + return; + } + this._isVisible = false; + this._isVisibleKey.reset(); + this._domNode.setDisplay('none'); + this._domNode.setAttribute('aria-hidden', 'true'); + this._contentDomNode.domNode.tabIndex = -1; + dom.clearNode(this._contentDomNode.domNode); + + this._editor.focus(); + } + + private _layout(): void { + let editorLayout = this._editor.getLayoutInfo(); + + let top = Math.round((editorLayout.height - AccessibilityHelpWidget.HEIGHT) / 2); + this._domNode.setTop(top); + + let left = Math.round((editorLayout.width - AccessibilityHelpWidget.WIDTH) / 2); + this._domNode.setLeft(left); + } +} + +@editorAction +class ShowAccessibilityHelpAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.showAccessibilityHelp', + label: nls.localize('ShowAccessibilityHelpAction', "Show Accessibility Help"), + alias: 'Show Accessibility Help', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.Alt | KeyCode.F1 + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let controller = AccessibilityHelpController.get(editor); + if (controller) { + controller.show(); + } + } +} + +const AccessibilityHelpCommand = EditorCommand.bindToContribution(AccessibilityHelpController.get); + +CommonEditorRegistry.registerEditorCommand(new AccessibilityHelpCommand({ + id: 'closeAccessibilityHelp', + precondition: CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE, + handler: x => x.hide(), + kbOpts: { + weight: CommonEditorRegistry.commandWeight(100), + kbExpr: EditorContextKeys.focus, + primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] + } +})); + +registerThemingParticipant((theme, collector) => { + let widgetBackground = theme.getColor(editorWidgetBackground); + if (widgetBackground) { + collector.addRule(`.monaco-editor .accessibilityHelpWidget { background-color: ${widgetBackground}; }`); + } + + let widgetShadowColor = theme.getColor(widgetShadow); + if (widgetShadowColor) { + collector.addRule(`.monaco-editor .accessibilityHelpWidget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + } + + let hcBorder = theme.getColor(contrastBorder); + if (hcBorder) { + collector.addRule(`.monaco-editor .accessibilityHelpWidget { border: 2px solid ${hcBorder}; }`); + } +}); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.ts b/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.ts new file mode 100644 index 0000000000..693157625a --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IUntitledResourceInput } from 'vs/platform/editor/common/editor'; + +@editorAction +class InspectKeyMap extends EditorAction { + + constructor() { + super({ + id: 'workbench.action.inspectKeyMappings', + label: nls.localize('workbench.action.inspectKeyMap', "Developer: Inspect Key Mappings"), + alias: 'Developer: Inspect Key Mappings', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const keybindingService = accessor.get(IKeybindingService); + const editorService = accessor.get(IWorkbenchEditorService); + + if (keybindingService instanceof WorkbenchKeybindingService) { + editorService.openEditor({ contents: keybindingService.dumpDebugInfo(), options: { pinned: true } } as IUntitledResourceInput); + } + } +} diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts new file mode 100644 index 0000000000..f8cc81e5f2 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts @@ -0,0 +1,384 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { parse, ParseError } from 'vs/base/common/json'; +import { readFile } from 'vs/base/node/pfs'; +import { CharacterPair, LanguageConfiguration, IAutoClosingPair, IAutoClosingPairConditional, IndentationRule, CommentRule } from 'vs/editor/common/modes/languageConfiguration'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; + +interface IRegExp { + pattern: string; + flags?: string; +} + +interface IIndentationRules { + decreaseIndentPattern: string | IRegExp; + increaseIndentPattern: string | IRegExp; + indentNextLinePattern?: string | IRegExp; + unIndentedLinePattern?: string | IRegExp; +} + +interface ILanguageConfiguration { + comments?: CommentRule; + brackets?: CharacterPair[]; + autoClosingPairs?: (CharacterPair | IAutoClosingPairConditional)[]; + surroundingPairs?: (CharacterPair | IAutoClosingPair)[]; + wordPattern?: string | IRegExp; + indentationRules?: IIndentationRules; +} + +export class LanguageConfigurationFileHandler { + + private _modeService: IModeService; + private _done: boolean[]; + + constructor( + @ITextMateService textMateService: ITextMateService, + @IModeService modeService: IModeService + ) { + this._modeService = modeService; + this._done = []; + + // Listen for hints that a language configuration is needed/usefull and then load it once + this._modeService.onDidCreateMode((mode) => this._loadConfigurationsForMode(mode.getLanguageIdentifier())); + textMateService.onDidEncounterLanguage((languageId) => { + this._loadConfigurationsForMode(this._modeService.getLanguageIdentifier(languageId)); + }); + } + + private _loadConfigurationsForMode(languageIdentifier: LanguageIdentifier): void { + if (this._done[languageIdentifier.id]) { + return; + } + this._done[languageIdentifier.id] = true; + + let configurationFiles = this._modeService.getConfigurationFiles(languageIdentifier.language); + configurationFiles.forEach((configFilePath) => this._handleConfigFile(languageIdentifier, configFilePath)); + } + + private _handleConfigFile(languageIdentifier: LanguageIdentifier, configFilePath: string): void { + readFile(configFilePath).then((fileContents) => { + const errors: ParseError[] = []; + const configuration = parse(fileContents.toString(), errors); + if (errors.length) { + console.error(nls.localize('parseErrors', "Errors parsing {0}: {1}", configFilePath, errors.join('\n'))); + } + this._handleConfig(languageIdentifier, configuration); + }, (err) => { + console.error(err); + }); + } + + private _handleConfig(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): void { + + let richEditConfig: LanguageConfiguration = {}; + + if (configuration.comments) { + richEditConfig.comments = configuration.comments; + } + + if (configuration.brackets) { + richEditConfig.brackets = configuration.brackets; + } + + if (configuration.autoClosingPairs) { + richEditConfig.autoClosingPairs = this._mapCharacterPairs(configuration.autoClosingPairs); + } + + if (configuration.surroundingPairs) { + richEditConfig.surroundingPairs = this._mapCharacterPairs(configuration.surroundingPairs); + } + + if (configuration.wordPattern) { + try { + let wordPattern = this._parseRegex(configuration.wordPattern); + if (wordPattern) { + richEditConfig.wordPattern = wordPattern; + } + } catch (error) { + // Malformed regexes are ignored + } + } + + if (configuration.indentationRules) { + let indentationRules = this._mapIndentationRules(configuration.indentationRules); + if (indentationRules) { + richEditConfig.indentationRules = indentationRules; + } + } + + LanguageConfigurationRegistry.register(languageIdentifier, richEditConfig); + } + + private _parseRegex(value: string | IRegExp) { + if (typeof value === 'string') { + return new RegExp(value, ''); + } else if (typeof value === 'object') { + return new RegExp(value.pattern, value.flags); + } + + return null; + } + + private _mapIndentationRules(indentationRules: IIndentationRules): IndentationRule { + try { + let increaseIndentPattern = this._parseRegex(indentationRules.increaseIndentPattern); + let decreaseIndentPattern = this._parseRegex(indentationRules.decreaseIndentPattern); + + if (increaseIndentPattern && decreaseIndentPattern) { + let result: IndentationRule = { + increaseIndentPattern: increaseIndentPattern, + decreaseIndentPattern: decreaseIndentPattern + }; + + if (indentationRules.indentNextLinePattern) { + result.indentNextLinePattern = this._parseRegex(indentationRules.indentNextLinePattern); + } + if (indentationRules.unIndentedLinePattern) { + result.unIndentedLinePattern = this._parseRegex(indentationRules.unIndentedLinePattern); + } + + return result; + } + } catch (error) { + // Malformed regexes are ignored + } + + return null; + } + + private _mapCharacterPairs(pairs: (CharacterPair | IAutoClosingPairConditional)[]): IAutoClosingPairConditional[] { + return pairs.map(pair => { + if (Array.isArray(pair)) { + return { open: pair[0], close: pair[1] }; + } + return pair; + }); + } +} + +const schemaId = 'vscode://schemas/language-configuration'; +const schema: IJSONSchema = { + default: { + comments: { + blockComment: ['/*', '*/'], + lineComment: '//' + }, + brackets: [['(', ')'], ['[', ']'], ['{', '}']], + autoClosingPairs: [['(', ')'], ['[', ']'], ['{', '}']], + surroundingPairs: [['(', ')'], ['[', ']'], ['{', '}']] + }, + definitions: { + openBracket: { + type: 'string', + description: nls.localize('schema.openBracket', 'The opening bracket character or string sequence.') + }, + closeBracket: { + type: 'string', + description: nls.localize('schema.closeBracket', 'The closing bracket character or string sequence.') + }, + bracketPair: { + type: 'array', + items: [{ + $ref: '#definitions/openBracket' + }, { + $ref: '#definitions/closeBracket' + }] + } + }, + properties: { + comments: { + default: { + blockComment: ['/*', '*/'], + lineComment: '//' + }, + description: nls.localize('schema.comments', 'Defines the comment symbols'), + type: 'object', + properties: { + blockComment: { + type: 'array', + description: nls.localize('schema.blockComments', 'Defines how block comments are marked.'), + items: [{ + type: 'string', + description: nls.localize('schema.blockComment.begin', 'The character sequence that starts a block comment.') + }, { + type: 'string', + description: nls.localize('schema.blockComment.end', 'The character sequence that ends a block comment.') + }] + }, + lineComment: { + type: 'string', + description: nls.localize('schema.lineComment', 'The character sequence that starts a line comment.') + } + } + }, + brackets: { + default: [['(', ')'], ['[', ']'], ['{', '}']], + description: nls.localize('schema.brackets', 'Defines the bracket symbols that increase or decrease the indentation.'), + type: 'array', + items: { + $ref: '#definitions/bracketPair' + } + }, + autoClosingPairs: { + default: [['(', ')'], ['[', ']'], ['{', '}']], + description: nls.localize('schema.autoClosingPairs', 'Defines the bracket pairs. When a opening bracket is entered, the closing bracket is inserted automatically.'), + type: 'array', + items: { + oneOf: [{ + $ref: '#definitions/bracketPair' + }, { + type: 'object', + properties: { + open: { + $ref: '#definitions/openBracket' + }, + close: { + $ref: '#definitions/closeBracket' + }, + notIn: { + type: 'array', + description: nls.localize('schema.autoClosingPairs.notIn', 'Defines a list of scopes where the auto pairs are disabled.'), + items: { + enum: ['string', 'comment'] + } + } + } + }] + } + }, + surroundingPairs: { + default: [['(', ')'], ['[', ']'], ['{', '}']], + description: nls.localize('schema.surroundingPairs', 'Defines the bracket pairs that can be used to surround a selected string.'), + type: 'array', + items: { + oneOf: [{ + $ref: '#definitions/bracketPair' + }, { + type: 'object', + properties: { + open: { + $ref: '#definitions/openBracket' + }, + close: { + $ref: '#definitions/closeBracket' + } + } + }] + } + }, + wordPattern: { + default: '', + description: nls.localize('schema.wordPattern', 'The word definition for the language.'), + type: ['string', 'object'], + properties: { + pattern: { + type: 'string', + description: nls.localize('schema.wordPattern.pattern', 'The RegExp pattern used to match words.'), + default: '', + }, + flags: { + type: 'string', + description: nls.localize('schema.wordPattern.flags', 'The RegExp flags used to match words.'), + default: 'g', + pattern: '^([gimuy]+)$', + patternErrorMessage: nls.localize('schema.wordPattern.flags.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.') + } + } + }, + indentationRules: { + default: { + increaseIndentPattern: '', + decreaseIndentPattern: '' + }, + description: nls.localize('schema.indentationRules', 'The language\'s indentation settings.'), + type: 'object', + properties: { + increaseIndentPattern: { + type: ['string', 'object'], + description: nls.localize('schema.indentationRules.increaseIndentPattern', 'If a line matches this pattern, then all the lines after it should be indented once (until another rule matches).'), + properties: { + pattern: { + type: 'string', + description: nls.localize('schema.indentationRules.increaseIndentPattern.pattern', 'The RegExp pattern for increaseIndentPattern.'), + default: '', + }, + flags: { + type: 'string', + description: nls.localize('schema.indentationRules.increaseIndentPattern.flags', 'The RegExp flags for increaseIndentPattern.'), + default: '', + pattern: '^([gimuy]+)$', + patternErrorMessage: nls.localize('schema.indentationRules.increaseIndentPattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.') + } + } + }, + decreaseIndentPattern: { + type: ['string', 'object'], + description: nls.localize('schema.indentationRules.decreaseIndentPattern', 'If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches).'), + properties: { + pattern: { + type: 'string', + description: nls.localize('schema.indentationRules.decreaseIndentPattern.pattern', 'The RegExp pattern for decreaseIndentPattern.'), + default: '', + }, + flags: { + type: 'string', + description: nls.localize('schema.indentationRules.decreaseIndentPattern.flags', 'The RegExp flags for decreaseIndentPattern.'), + default: '', + pattern: '^([gimuy]+)$', + patternErrorMessage: nls.localize('schema.indentationRules.decreaseIndentPattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.') + } + } + }, + indentNextLinePattern: { + type: ['string', 'object'], + description: nls.localize('schema.indentationRules.indentNextLinePattern', 'If a line matches this pattern, then **only the next line** after it should be indented once.'), + properties: { + pattern: { + type: 'string', + description: nls.localize('schema.indentationRules.indentNextLinePattern.pattern', 'The RegExp pattern for indentNextLinePattern.'), + default: '', + }, + flags: { + type: 'string', + description: nls.localize('schema.indentationRules.indentNextLinePattern.flags', 'The RegExp flags for indentNextLinePattern.'), + default: '', + pattern: '^([gimuy]+)$', + patternErrorMessage: nls.localize('schema.indentationRules.indentNextLinePattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.') + } + } + }, + unIndentedLinePattern: { + type: ['string', 'object'], + description: nls.localize('schema.indentationRules.unIndentedLinePattern', 'If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules.'), + properties: { + pattern: { + type: 'string', + description: nls.localize('schema.indentationRules.unIndentedLinePattern.pattern', 'The RegExp pattern for unIndentedLinePattern.'), + default: '', + }, + flags: { + type: 'string', + description: nls.localize('schema.indentationRules.unIndentedLinePattern.flags', 'The RegExp flags for unIndentedLinePattern.'), + default: '', + pattern: '^([gimuy]+)$', + patternErrorMessage: nls.localize('schema.indentationRules.unIndentedLinePattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.') + } + } + } + } + } + } +}; +let schemaRegistry = Registry.as(Extensions.JSONContribution); +schemaRegistry.registerSchema(schemaId, schema); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/media/WordWrap_16x.svg b/src/vs/workbench/parts/codeEditor/electron-browser/media/WordWrap_16x.svg new file mode 100644 index 0000000000..21058f74e7 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/media/WordWrap_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/media/codeEditor.css b/src/vs/workbench/parts/codeEditor/electron-browser/media/codeEditor.css new file mode 100644 index 0000000000..8a8fb1ec23 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/media/codeEditor.css @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.toggle-word-wrap-action { + background: url('WordWrap_16x.svg') center center no-repeat; +} diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/menuPreventer.ts b/src/vs/workbench/parts/codeEditor/electron-browser/menuPreventer.ts new file mode 100644 index 0000000000..70d88335e7 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/menuPreventer.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { KeyMod } from 'vs/base/common/keyCodes'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; + +/** + * Prevents the top-level menu from showing up when doing Alt + Click in the editor + */ +@editorContribution +export class MenuPreventer extends Disposable implements IEditorContribution { + + private static ID = 'editor.contrib.menuPreventer'; + + private _editor: ICodeEditor; + private _altListeningMouse: boolean; + private _altMouseTriggered: boolean; + + constructor(editor: ICodeEditor) { + super(); + this._editor = editor; + this._altListeningMouse = false; + this._altMouseTriggered = false; + + // A global crossover handler to prevent menu bar from showing up + // When is hold, we will listen to mouse events and prevent + // the release event up if the mouse is triggered. + + this._register(this._editor.onMouseDown((e) => { + if (this._altListeningMouse) { + this._altMouseTriggered = true; + } + })); + + this._register(this._editor.onKeyDown((e) => { + if (e.equals(KeyMod.Alt)) { + if (!this._altListeningMouse) { + this._altMouseTriggered = false; + } + this._altListeningMouse = true; + } + })); + + this._register(this._editor.onKeyUp((e) => { + if (e.equals(KeyMod.Alt)) { + if (this._altMouseTriggered) { + e.preventDefault(); + } + this._altListeningMouse = false; + this._altMouseTriggered = false; + } + })); + } + + public getId(): string { + return MenuPreventer.ID; + } +} diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts b/src/vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts new file mode 100644 index 0000000000..bd19d499cf --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { clipboard } from 'electron'; +import * as platform from 'vs/base/common/platform'; +import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { EndOfLinePreference, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { Range } from 'vs/editor/common/core/range'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; + +@editorContribution +export class SelectionClipboard extends Disposable implements IEditorContribution { + + private static ID = 'editor.contrib.selectionClipboard'; + + constructor(editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService) { + super(); + + if (platform.isLinux) { + let isEnabled = editor.getConfiguration().contribInfo.selectionClipboard; + + this._register(editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { + if (e.contribInfo) { + isEnabled = editor.getConfiguration().contribInfo.selectionClipboard; + } + })); + + this._register(editor.onMouseDown((e: IEditorMouseEvent) => { + if (!isEnabled) { + return; + } + if (!editor.getModel()) { + return; + } + if (e.event.middleButton) { + e.event.preventDefault(); + editor.focus(); + + if (e.target.position) { + editor.setPosition(e.target.position); + } + + process.nextTick(() => { + // TODO@Alex: electron weirdness: calling clipboard.readText('selection') generates a paste event, so no need to execute paste ourselves + clipboard.readText('selection'); + // keybindingService.executeCommand(Handler.Paste, { + // text: clipboard.readText('selection'), + // pasteOnNewLine: false + // }); + }); + } + })); + + let setSelectionToClipboard = this._register(new RunOnceScheduler(() => { + let model = editor.getModel(); + if (!model) { + return; + } + + let selections = editor.getSelections(); + selections = selections.slice(0); + selections.sort(Range.compareRangesUsingStarts); + + let result: string[] = []; + for (let i = 0; i < selections.length; i++) { + let sel = selections[i]; + if (sel.isEmpty()) { + // Only write if all cursors have selection + return; + } + result.push(model.getValueInRange(sel, EndOfLinePreference.TextDefined)); + } + + let textToCopy = result.join(model.getEOL()); + clipboard.writeText(textToCopy, 'selection'); + }, 100)); + + this._register(editor.onDidChangeCursorSelection((e: ICursorSelectionChangedEvent) => { + if (!isEnabled) { + return; + } + setSelectionToClipboard.schedule(); + })); + } + } + + public getId(): string { + return SelectionClipboard.ID; + } + + public dispose(): void { + super.dispose(); + } +} diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.css b/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.css new file mode 100644 index 0000000000..f8fbc89cc7 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.css @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.tm-inspect-widget { + z-index: 50; + -webkit-user-select: text; + -ms-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -o-user-select: text; + user-select: text; + padding: 10px; +} + +.tm-token { + font-family: monospace; +} + +.tm-metadata-separator { + height: 1px; + border: 0; +} + +.tm-token-length { + font-weight: normal; + font-size: 60%; + float: right; +} + +.tm-metadata-table { + width: 100%; +} + +.tm-metadata-value { + font-family: monospace; + text-align: right; +} + +.tm-theme-selector { + font-family: monospace; +} diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts b/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts new file mode 100644 index 0000000000..8dda38bee2 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts @@ -0,0 +1,390 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./inspectTMScopes'; +import * as nls from 'vs/nls'; +import * as dom from 'vs/base/browser/dom'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { escape } from 'vs/base/common/strings'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { Position } from 'vs/editor/common/core/position'; +import { ICommonCodeEditor, IEditorContribution, IModel } from 'vs/editor/common/editorCommon'; +import { editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor, ContentWidgetPositionPreference, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IGrammar, StackElement, IToken } from 'vscode-textmate'; +import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding'; +import { TokenizationRegistry, LanguageIdentifier, FontStyle, StandardTokenType } from 'vs/editor/common/modes'; +import { CharCode } from 'vs/base/common/charCode'; +import { findMatchingThemeRule } from 'vs/workbench/services/textMate/electron-browser/TMHelper'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { Color } from 'vs/base/common/color'; +import { IMessageService } from 'vs/platform/message/common/message'; +import Severity from 'vs/base/common/severity'; +import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; + +@editorContribution +class InspectTMScopesController extends Disposable implements IEditorContribution { + + private static ID = 'editor.contrib.inspectTMScopes'; + + public static get(editor: ICommonCodeEditor): InspectTMScopesController { + return editor.getContribution(InspectTMScopesController.ID); + } + + private _editor: ICodeEditor; + private _textMateService: ITextMateService; + private _themeService: IWorkbenchThemeService; + private _modeService: IModeService; + private _messageService: IMessageService; + private _widget: InspectTMScopesWidget; + + constructor( + editor: ICodeEditor, + @ITextMateService textMateService: ITextMateService, + @IModeService modeService: IModeService, + @IWorkbenchThemeService themeService: IWorkbenchThemeService, + @IMessageService messageService: IMessageService, + ) { + super(); + this._editor = editor; + this._textMateService = textMateService; + this._themeService = themeService; + this._modeService = modeService; + this._messageService = messageService; + this._widget = null; + + this._register(this._editor.onDidChangeModel((e) => this.stop())); + this._register(this._editor.onDidChangeModelLanguage((e) => this.stop())); + this._register(this._editor.onKeyUp((e) => e.keyCode === KeyCode.Escape && this.stop())); + } + + public getId(): string { + return InspectTMScopesController.ID; + } + + public dispose(): void { + this.stop(); + super.dispose(); + } + + public launch(): void { + if (this._widget) { + return; + } + if (!this._editor.getModel()) { + return; + } + this._widget = new InspectTMScopesWidget(this._editor, this._textMateService, this._modeService, this._themeService, this._messageService); + } + + public stop(): void { + if (this._widget) { + this._widget.dispose(); + this._widget = null; + } + } + + public toggle(): void { + if (!this._widget) { + this.launch(); + } else { + this.stop(); + } + } +} + +@editorAction +class InspectTMScopes extends EditorAction { + + constructor() { + super({ + id: 'editor.action.inspectTMScopes', + label: nls.localize('inspectTMScopes', "Developer: Inspect TM Scopes"), + alias: 'Developer: Inspect TM Scopes', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + let controller = InspectTMScopesController.get(editor); + if (controller) { + controller.toggle(); + } + } +} + +interface ICompleteLineTokenization { + startState: StackElement; + tokens1: IToken[]; + tokens2: Uint32Array; + endState: StackElement; +} + +interface IDecodedMetadata { + languageIdentifier: LanguageIdentifier; + tokenType: StandardTokenType; + fontStyle: FontStyle; + foreground: Color; + background: Color; +} + +function renderTokenText(tokenText: string): string { + if (tokenText.length > 40) { + tokenText = tokenText.substr(0, 20) + '…' + tokenText.substr(tokenText.length - 20); + } + let result: string = ''; + for (let charIndex = 0, len = tokenText.length; charIndex < len; charIndex++) { + let charCode = tokenText.charCodeAt(charIndex); + switch (charCode) { + case CharCode.Tab: + result += '→'; + break; + + case CharCode.Space: + result += '·'; + break; + + case CharCode.LessThan: + result += '<'; + break; + + case CharCode.GreaterThan: + result += '>'; + break; + + case CharCode.Ampersand: + result += '&'; + break; + + default: + result += String.fromCharCode(charCode); + } + } + return result; +} + +class InspectTMScopesWidget extends Disposable implements IContentWidget { + + private static _ID = 'editor.contrib.inspectTMScopesWidget'; + + // Editor.IContentWidget.allowEditorOverflow + public readonly allowEditorOverflow = true; + + private _isDisposed: boolean; + private readonly _editor: ICodeEditor; + private readonly _modeService: IModeService; + private readonly _themeService: IWorkbenchThemeService; + private readonly _messageService: IMessageService; + private readonly _model: IModel; + private readonly _domNode: HTMLElement; + private readonly _grammar: TPromise; + + constructor( + editor: ICodeEditor, + textMateService: ITextMateService, + modeService: IModeService, + themeService: IWorkbenchThemeService, + messageService: IMessageService + ) { + super(); + this._isDisposed = false; + this._editor = editor; + this._modeService = modeService; + this._themeService = themeService; + this._messageService = messageService; + this._model = this._editor.getModel(); + this._domNode = document.createElement('div'); + this._domNode.className = 'tm-inspect-widget'; + this._grammar = textMateService.createGrammar(this._model.getLanguageIdentifier().language); + this._beginCompute(this._editor.getPosition()); + this._register(this._editor.onDidChangeCursorPosition((e) => this._beginCompute(this._editor.getPosition()))); + this._editor.addContentWidget(this); + } + + public dispose(): void { + this._isDisposed = true; + this._editor.removeContentWidget(this); + super.dispose(); + } + + public getId(): string { + return InspectTMScopesWidget._ID; + } + + private _beginCompute(position: Position): void { + dom.clearNode(this._domNode); + this._domNode.appendChild(document.createTextNode(nls.localize('inspectTMScopesWidget.loading', "Loading..."))); + this._grammar.then( + (grammar) => this._compute(grammar, position), + (err) => { + this._messageService.show(Severity.Warning, err); + setTimeout(() => { + InspectTMScopesController.get(this._editor).stop(); + }); + } + ); + } + + private _compute(grammar: IGrammar, position: Position): void { + if (this._isDisposed) { + return; + } + let data = this._getTokensAtLine(grammar, position.lineNumber); + + let token1Index = 0; + for (let i = data.tokens1.length - 1; i >= 0; i--) { + let t = data.tokens1[i]; + if (position.column - 1 >= t.startIndex) { + token1Index = i; + break; + } + } + + let token2Index = 0; + for (let i = (data.tokens2.length >>> 1); i >= 0; i--) { + if (position.column - 1 >= data.tokens2[(i << 1)]) { + token2Index = i; + break; + } + } + + let result = ''; + + let tokenStartIndex = data.tokens1[token1Index].startIndex; + let tokenEndIndex = data.tokens1[token1Index].endIndex; + let tokenText = this._model.getLineContent(position.lineNumber).substring(tokenStartIndex, tokenEndIndex); + result += `

${renderTokenText(tokenText)}(${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'})

`; + + result += ``; + + let metadata = this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]); + result += ``; + result += ``; + result += ``; + result += ``; + result += ``; + result += ``; + result += ``; + + let theme = this._themeService.getColorTheme(); + result += ``; + let matchingRule = findMatchingThemeRule(theme, data.tokens1[token1Index].scopes); + if (matchingRule) { + result += `${matchingRule.rawSelector}\n${JSON.stringify(matchingRule.settings, null, '\t')}`; + } else { + result += `No theme selector.`; + } + + result += ``; + + result += `
    `; + for (let i = data.tokens1[token1Index].scopes.length - 1; i >= 0; i--) { + result += `
  • ${escape(data.tokens1[token1Index].scopes[i])}
  • `; + } + result += `
`; + + + this._domNode.innerHTML = result; + this._editor.layoutContentWidget(this); + } + + private _decodeMetadata(metadata: number): IDecodedMetadata { + let colorMap = TokenizationRegistry.getColorMap(); + let languageId = TokenMetadata.getLanguageId(metadata); + let tokenType = TokenMetadata.getTokenType(metadata); + let fontStyle = TokenMetadata.getFontStyle(metadata); + let foreground = TokenMetadata.getForeground(metadata); + let background = TokenMetadata.getBackground(metadata); + return { + languageIdentifier: this._modeService.getLanguageIdentifier(languageId), + tokenType: tokenType, + fontStyle: fontStyle, + foreground: colorMap[foreground], + background: colorMap[background] + }; + } + + private _tokenTypeToString(tokenType: StandardTokenType): string { + switch (tokenType) { + case StandardTokenType.Other: return 'Other'; + case StandardTokenType.Comment: return 'Comment'; + case StandardTokenType.String: return 'String'; + case StandardTokenType.RegEx: return 'RegEx'; + } + return '??'; + } + + private _fontStyleToString(fontStyle: FontStyle): string { + let r = ''; + if (fontStyle & FontStyle.Italic) { + r += 'italic '; + } + if (fontStyle & FontStyle.Bold) { + r += 'bold '; + } + if (fontStyle & FontStyle.Underline) { + r += 'underline '; + } + if (r.length === 0) { + r = '---'; + } + return r; + } + + private _getTokensAtLine(grammar: IGrammar, lineNumber: number): ICompleteLineTokenization { + let stateBeforeLine = this._getStateBeforeLine(grammar, lineNumber); + + let tokenizationResult1 = grammar.tokenizeLine(this._model.getLineContent(lineNumber), stateBeforeLine); + let tokenizationResult2 = grammar.tokenizeLine2(this._model.getLineContent(lineNumber), stateBeforeLine); + + return { + startState: stateBeforeLine, + tokens1: tokenizationResult1.tokens, + tokens2: tokenizationResult2.tokens, + endState: tokenizationResult1.ruleStack + }; + } + + private _getStateBeforeLine(grammar: IGrammar, lineNumber: number): StackElement { + let state: StackElement = null; + + for (let i = 1; i < lineNumber; i++) { + let tokenizationResult = grammar.tokenizeLine(this._model.getLineContent(i), state); + state = tokenizationResult.ruleStack; + } + + return state; + } + + public getDomNode(): HTMLElement { + return this._domNode; + } + + public getPosition(): IContentWidgetPosition { + return { + position: this._editor.getPosition(), + preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] + }; + } +} + +registerThemingParticipant((theme, collector) => { + let border = theme.getColor(editorHoverBorder); + if (border) { + let borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1; + collector.addRule(`.monaco-editor .tm-inspect-widget { border: ${borderWidth}px solid ${border}; }`); + collector.addRule(`.monaco-editor .tm-inspect-widget .tm-metadata-separator { background-color: ${border}; }`); + } + let background = theme.getColor(editorHoverBackground); + if (background) { + collector.addRule(`.monaco-editor .tm-inspect-widget { background-color: ${background}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts new file mode 100644 index 0000000000..9f4d581f57 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; + +@editorAction +export class ToggleMinimapAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.toggleMinimap', + label: nls.localize('toggleMinimap', "View: Toggle Minimap"), + alias: 'View: Toggle Minimap', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const configurationEditingService = accessor.get(IConfigurationEditingService); + + const newValue = !editor.getConfiguration().viewInfo.minimap.enabled; + + configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'editor.minimap.enabled', value: newValue }); + } +} diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts new file mode 100644 index 0000000000..274adc220f --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as nls from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Action } from 'vs/base/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export class ToggleMultiCursorModifierAction extends Action { + + public static ID = 'workbench.action.toggleMultiCursorModifier'; + public static LABEL = nls.localize('toggleLocation', "Toggle Multi-Cursor Modifier"); + + private static multiCursorModifierConfigurationKey = 'editor.multiCursorModifier'; + + constructor( + id: string, + label: string, + @IConfigurationService private configurationService: IConfigurationService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService + ) { + super(id, label); + + this.enabled = !!this.configurationService && !!this.configurationEditingService; + } + + public run(): TPromise { + const editorConf = this.configurationService.getConfiguration<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor'); + const newValue: 'ctrlCmd' | 'alt' = (editorConf.multiCursorModifier === 'ctrlCmd' ? 'alt' : 'ctrlCmd'); + + this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, value: newValue }); + + return TPromise.as(null); + } +} + +const registry = Registry.as(Extensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMultiCursorModifierAction, ToggleMultiCursorModifierAction.ID, ToggleMultiCursorModifierAction.LABEL), 'Toggle Multi-Cursor Modifier'); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts new file mode 100644 index 0000000000..a552dd000e --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; + +@editorAction +export class ToggleRenderControlCharacterAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.toggleRenderControlCharacter', + label: nls.localize('toggleRenderControlCharacters', "View: Toggle Control Characters"), + alias: 'View: Toggle Control Characters', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const configurationEditingService = accessor.get(IConfigurationEditingService); + + let newRenderControlCharacters = !editor.getConfiguration().viewInfo.renderControlCharacters; + + configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'editor.renderControlCharacters', value: newRenderControlCharacters }); + } +} diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts new file mode 100644 index 0000000000..0017d72bb1 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; + +@editorAction +export class ToggleRenderWhitespaceAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.toggleRenderWhitespace', + label: nls.localize('toggleRenderWhitespace', "View: Toggle Render Whitespace"), + alias: 'View: Toggle Render Whitespace', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const configurationEditingService = accessor.get(IConfigurationEditingService); + + let renderWhitespace = editor.getConfiguration().viewInfo.renderWhitespace; + let newRenderWhitespace: string; + if (renderWhitespace === 'none') { + newRenderWhitespace = 'all'; + } else { + newRenderWhitespace = 'none'; + } + + configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'editor.renderWhitespace', value: newRenderWhitespace }); + } +} diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts new file mode 100644 index 0000000000..6387e476ef --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts @@ -0,0 +1,280 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./media/codeEditor'; +import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICommonCodeEditor, IEditorContribution, IModel } from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IMessageService } from 'vs/platform/message/common/message'; +import Severity from 'vs/base/common/severity'; +import URI from 'vs/base/common/uri'; +import { InternalEditorOptions, EDITOR_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; + +const transientWordWrapState = 'transientWordWrapState'; +const isWordWrapMinifiedKey = 'isWordWrapMinified'; +const isDominatedByLongLinesKey = 'isDominatedByLongLines'; +const inDiffEditorKey = 'inDiffEditor'; + +/** + * State written/read by the toggle word wrap action and associated with a particular model. + */ +interface IWordWrapTransientState { + readonly forceWordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; + readonly forceWordWrapMinified: boolean; +} + +interface IWordWrapState { + readonly configuredWordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; + readonly configuredWordWrapMinified: boolean; + readonly transientState: IWordWrapTransientState; +} + +/** + * Store (in memory) the word wrap state for a particular model. + */ +function writeTransientState(model: IModel, state: IWordWrapTransientState, codeEditorService: ICodeEditorService): void { + codeEditorService.setTransientModelProperty(model, transientWordWrapState, state); +} + +/** + * Read (in memory) the word wrap state for a particular model. + */ +function readTransientState(model: IModel, codeEditorService: ICodeEditorService): IWordWrapTransientState { + return codeEditorService.getTransientModelProperty(model, transientWordWrapState); +} + +function readWordWrapState(model: IModel, configurationService: ITextResourceConfigurationService, codeEditorService: ICodeEditorService): IWordWrapState { + const editorConfig = configurationService.getConfiguration(model.uri, 'editor') as { wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; wordWrapMinified: boolean }; + let _configuredWordWrap = editorConfig && (typeof editorConfig.wordWrap === 'string' || typeof editorConfig.wordWrap === 'boolean') ? editorConfig.wordWrap : void 0; + + // Compatibility with old true or false values + if (_configuredWordWrap === true) { + _configuredWordWrap = 'on'; + } else if (_configuredWordWrap === false) { + _configuredWordWrap = 'off'; + } + + const _configuredWordWrapMinified = editorConfig && typeof editorConfig.wordWrapMinified === 'boolean' ? editorConfig.wordWrapMinified : void 0; + const _transientState = readTransientState(model, codeEditorService); + return { + configuredWordWrap: _configuredWordWrap, + configuredWordWrapMinified: (typeof _configuredWordWrapMinified === 'boolean' ? _configuredWordWrapMinified : EDITOR_DEFAULTS.wordWrapMinified), + transientState: _transientState + }; +} + +function toggleWordWrap(editor: ICommonCodeEditor, state: IWordWrapState): IWordWrapState { + if (state.transientState) { + // toggle off => go to null + return { + configuredWordWrap: state.configuredWordWrap, + configuredWordWrapMinified: state.configuredWordWrapMinified, + transientState: null + }; + } + + const config = editor.getConfiguration(); + let transientState: IWordWrapTransientState; + + const actualWrappingInfo = config.wrappingInfo; + if (actualWrappingInfo.isWordWrapMinified) { + // => wrapping due to minified file + transientState = { + forceWordWrap: 'off', + forceWordWrapMinified: false + }; + } else if (state.configuredWordWrap !== 'off') { + // => wrapping is configured to be on (or some variant) + transientState = { + forceWordWrap: 'off', + forceWordWrapMinified: false + }; + } else { + // => wrapping is configured to be off + transientState = { + forceWordWrap: 'on', + forceWordWrapMinified: state.configuredWordWrapMinified + }; + } + + return { + configuredWordWrap: state.configuredWordWrap, + configuredWordWrapMinified: state.configuredWordWrapMinified, + transientState: transientState + }; +} + +function applyWordWrapState(editor: ICommonCodeEditor, state: IWordWrapState): void { + if (state.transientState) { + // toggle is on + editor.updateOptions({ + wordWrap: state.transientState.forceWordWrap, + wordWrapMinified: state.transientState.forceWordWrapMinified + }); + return; + } + + // toggle is off + editor.updateOptions({ + wordWrap: state.configuredWordWrap, + wordWrapMinified: state.configuredWordWrapMinified + }); +} + +@editorAction +class ToggleWordWrapAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.toggleWordWrap', + label: nls.localize('toggle.wordwrap', "View: Toggle Word Wrap"), + alias: 'View: Toggle Word Wrap', + precondition: null, + kbOpts: { + kbExpr: null, + primary: KeyMod.Alt | KeyCode.KEY_Z + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const editorConfiguration = editor.getConfiguration(); + if (editorConfiguration.wrappingInfo.inDiffEditor) { + // Cannot change wrapping settings inside the diff editor + const messageService = accessor.get(IMessageService); + messageService.show(Severity.Info, nls.localize('wordWrap.notInDiffEditor', "Cannot toggle word wrap in a diff editor.")); + return; + } + + const textResourceConfigurationService = accessor.get(ITextResourceConfigurationService); + const codeEditorService = accessor.get(ICodeEditorService); + const model = editor.getModel(); + + if (!canToggleWordWrap(model.uri)) { + return; + } + + // Read the current state + const currentState = readWordWrapState(model, textResourceConfigurationService, codeEditorService); + // Compute the new state + const newState = toggleWordWrap(editor, currentState); + // Write the new state + writeTransientState(model, newState.transientState, codeEditorService); + // Apply the new state + applyWordWrapState(editor, newState); + } +} + +@commonEditorContribution +class ToggleWordWrapController extends Disposable implements IEditorContribution { + + private static _ID = 'editor.contrib.toggleWordWrapController'; + + constructor( + private readonly editor: ICommonCodeEditor, + @IContextKeyService readonly contextKeyService: IContextKeyService, + @ITextResourceConfigurationService readonly configurationService: ITextResourceConfigurationService, + @ICodeEditorService readonly codeEditorService: ICodeEditorService + ) { + super(); + + const configuration = this.editor.getConfiguration(); + const isWordWrapMinified = this.contextKeyService.createKey(isWordWrapMinifiedKey, this._isWordWrapMinified(configuration)); + const isDominatedByLongLines = this.contextKeyService.createKey(isDominatedByLongLinesKey, this._isDominatedByLongLines(configuration)); + const inDiffEditor = this.contextKeyService.createKey(inDiffEditorKey, this._inDiffEditor(configuration)); + + this._register(editor.onDidChangeConfiguration((e) => { + if (!e.wrappingInfo) { + return; + } + const configuration = this.editor.getConfiguration(); + isWordWrapMinified.set(this._isWordWrapMinified(configuration)); + isDominatedByLongLines.set(this._isDominatedByLongLines(configuration)); + inDiffEditor.set(this._inDiffEditor(configuration)); + })); + + this._register(editor.onDidChangeModel((e) => { + // Ensure correct word wrap settings + const newModel = this.editor.getModel(); + if (!newModel) { + return; + } + + const configuration = this.editor.getConfiguration(); + if (this._inDiffEditor(configuration)) { + return; + } + + if (!canToggleWordWrap(newModel.uri)) { + return; + } + + // Read current configured values and toggle state + const desiredState = readWordWrapState(newModel, this.configurationService, this.codeEditorService); + + // Apply the state + applyWordWrapState(editor, desiredState); + })); + } + + private _isWordWrapMinified(config: InternalEditorOptions): boolean { + return config.wrappingInfo.isWordWrapMinified; + } + + private _isDominatedByLongLines(config: InternalEditorOptions): boolean { + return config.wrappingInfo.isDominatedByLongLines; + } + + private _inDiffEditor(config: InternalEditorOptions): boolean { + return config.wrappingInfo.inDiffEditor; + } + + public getId(): string { + return ToggleWordWrapController._ID; + } +} + +function canToggleWordWrap(uri: URI): boolean { + if (!uri) { + return false; + } + return (uri.scheme !== 'output' && uri.scheme !== 'vscode'); +} + +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: 'editor.action.toggleWordWrap', + title: nls.localize('unwrapMinified', "Disable wrapping for this file"), + iconClass: 'toggle-word-wrap-action' + }, + group: 'navigation', + order: 1, + when: ContextKeyExpr.and( + ContextKeyExpr.not(inDiffEditorKey), + ContextKeyExpr.has(isDominatedByLongLinesKey), + ContextKeyExpr.has(isWordWrapMinifiedKey) + ) +}); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: 'editor.action.toggleWordWrap', + title: nls.localize('wrapMinified', "Enable wrapping for this file"), + iconClass: 'toggle-word-wrap-action' + }, + group: 'navigation', + order: 1, + when: ContextKeyExpr.and( + ContextKeyExpr.not(inDiffEditorKey), + ContextKeyExpr.has(isDominatedByLongLinesKey), + ContextKeyExpr.not(isWordWrapMinifiedKey) + ) +}); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.ts b/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.ts new file mode 100644 index 0000000000..1a42a558b9 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { Action } from 'vs/base/common/actions'; +import Severity from 'vs/base/common/severity'; + +interface IStorageData { + dontShowPrompt: boolean; +} + +class WordWrapMigrationStorage { + private static KEY = 'wordWrapMigration'; + + private _storageService: IStorageService; + private _value: IStorageData; + + constructor(storageService: IStorageService) { + this._storageService = storageService; + this._value = this._read(); + } + + private _read(): IStorageData { + let jsonValue = this._storageService.get(WordWrapMigrationStorage.KEY, StorageScope.GLOBAL); + if (!jsonValue) { + return null; + } + try { + return JSON.parse(jsonValue); + } catch (err) { + return null; + } + } + + public get(): IStorageData { + return this._value; + } + + public set(data: IStorageData): void { + this._value = data; + this._storageService.store(WordWrapMigrationStorage.KEY, JSON.stringify(this._value), StorageScope.GLOBAL); + } +} + +@editorContribution +class WordWrapMigrationController extends Disposable implements IEditorContribution { + + private static ID = 'editor.contrib.wordWrapMigrationController'; + private static _checked = false; + + constructor( + editor: ICodeEditor, + @IConfigurationService private configurationService: IConfigurationService, + @IMessageService private messageService: IMessageService, + @IStorageService private storageService: IStorageService, + @IPreferencesService private preferencesService: IPreferencesService + ) { + super(); + + this._promptIfNecessary(); + } + + public getId(): string { + return WordWrapMigrationController.ID; + } + + private _promptIfNecessary(): void { + if (WordWrapMigrationController._checked) { + // Already checked + return; + } + WordWrapMigrationController._checked = true; + + let result = this.configurationService.lookup('editor.wrappingColumn'); + if (typeof result.value === 'undefined') { + // Setting is not used + return; + } + + const storage = new WordWrapMigrationStorage(this.storageService); + const storedData = storage.get(); + if (storedData && storedData.dontShowPrompt) { + // Do not prompt stored + return; + } + + let isUserSetting = (typeof result.user !== 'undefined'); + this._prompt(storage, isUserSetting); + } + + private _prompt(storage: WordWrapMigrationStorage, userSettings: boolean): void { + const okAction = new Action( + 'wordWrapMigration.ok', + nls.localize('wordWrapMigration.ok', "OK"), + null, + true, + () => TPromise.as(true) + ); + const dontShowAgainAction = new Action( + 'wordWrapMigration.dontShowAgain', + nls.localize('wordWrapMigration.dontShowAgain', "Don't show again"), + null, + true, + () => { + storage.set({ + dontShowPrompt: true + }); + return TPromise.as(true); + } + ); + const openSettings = new Action( + 'wordWrapMigration.openSettings', + nls.localize('wordWrapMigration.openSettings', "Open Settings"), + null, + true, + () => { + if (userSettings) { + this.preferencesService.openGlobalSettings(); + } else { + this.preferencesService.openWorkspaceSettings(); + } + return TPromise.as(true); + } + ); + this.messageService.show(Severity.Info, { + message: nls.localize('wordWrapMigration.prompt', "The setting `editor.wrappingColumn` has been deprecated in favor of `editor.wordWrap`."), + actions: [okAction, openSettings, dontShowAgainAction] + }); + } +} diff --git a/src/vs/workbench/parts/debug/browser/breakpointWidget.ts b/src/vs/workbench/parts/debug/browser/breakpointWidget.ts new file mode 100644 index 0000000000..45e5013e7b --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/breakpointWidget.ts @@ -0,0 +1,158 @@ +/*--------------------------------------------------------------------------------------------- + * 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!../browser/media/breakpointWidget'; +import * as nls from 'vs/nls'; +import * as errors from 'vs/base/common/errors'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { isWindows, isMacintosh } from 'vs/base/common/platform'; +import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import * as dom from 'vs/base/browser/dom'; +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IDebugService, IBreakpoint, IRawBreakpoint } from 'vs/workbench/parts/debug/common/debug'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { once } from 'vs/base/common/functional'; +import { attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +const $ = dom.$; +const EXPRESSION_PLACEHOLDER = nls.localize('breakpointWidgetExpressionPlaceholder', "Break when expression evaluates to true. 'Enter' to accept, 'esc' to cancel."); +const EXPRESSION_ARIA_LABEL = nls.localize('breakpointWidgetAriaLabel', "The program will only stop here if this condition is true. Press Enter to accept or Escape to cancel."); +const HIT_COUNT_PLACEHOLDER = nls.localize('breakpointWidgetHitCountPlaceholder', "Break when hit count condition is met. 'Enter' to accept, 'esc' to cancel."); +const HIT_COUNT_ARIA_LABEL = nls.localize('breakpointWidgetHitCountAriaLabel', "The program will only stop here if the hit count is met. Press Enter to accept or Escape to cancel."); + +export class BreakpointWidget extends ZoneWidget { + + private inputBox: InputBox; + private toDispose: lifecycle.IDisposable[]; + private hitCountContext: boolean; + private hitCountInput: string; + private conditionInput: string; + + constructor(editor: ICodeEditor, private lineNumber: number, private column: number, + @IContextViewService private contextViewService: IContextViewService, + @IDebugService private debugService: IDebugService, + @IThemeService private themeService: IThemeService + ) { + super(editor, { showFrame: true, showArrow: false, frameWidth: 1 }); + + this.toDispose = []; + this.hitCountInput = ''; + this.conditionInput = ''; + this.create(); + } + + private get placeholder(): string { + return this.hitCountContext ? HIT_COUNT_PLACEHOLDER : EXPRESSION_PLACEHOLDER; + } + + private get ariaLabel(): string { + return this.hitCountContext ? HIT_COUNT_ARIA_LABEL : EXPRESSION_ARIA_LABEL; + } + + private getInputBoxValue(breakpoint: IBreakpoint): string { + if (this.hitCountContext) { + return breakpoint && breakpoint.hitCondition ? breakpoint.hitCondition : this.hitCountInput; + } + + return breakpoint && breakpoint.condition ? breakpoint.condition : this.conditionInput; + } + + protected _fillContainer(container: HTMLElement): void { + this.setCssClass('breakpoint-widget'); + const uri = this.editor.getModel().uri; + const breakpoint = this.debugService.getModel().getBreakpoints().filter(bp => bp.lineNumber === this.lineNumber && bp.column === this.column && bp.uri.toString() === uri.toString()).pop(); + + this.hitCountContext = breakpoint && breakpoint.hitCondition && !breakpoint.condition; + const selected = this.hitCountContext ? 1 : 0; + const selectBox = new SelectBox([nls.localize('expression', "Expression"), nls.localize('hitCount', "Hit Count")], selected); + this.toDispose.push(attachSelectBoxStyler(selectBox, this.themeService)); + selectBox.render(dom.append(container, $('.breakpoint-select-container'))); + selectBox.onDidSelect(e => { + this.hitCountContext = e.selected === 'Hit Count'; + if (this.hitCountContext) { + this.conditionInput = this.inputBox.value; + } else { + this.hitCountInput = this.inputBox.value; + } + + this.inputBox.setAriaLabel(this.ariaLabel); + this.inputBox.setPlaceHolder(this.placeholder); + this.inputBox.value = this.getInputBoxValue(breakpoint); + }); + + const inputBoxContainer = dom.append(container, $('.inputBoxContainer')); + this.inputBox = new InputBox(inputBoxContainer, this.contextViewService, { + placeholder: this.placeholder, + ariaLabel: this.ariaLabel + }); + this.toDispose.push(attachInputBoxStyler(this.inputBox, this.themeService)); + this.toDispose.push(this.inputBox); + + dom.addClass(this.inputBox.inputElement, isWindows ? 'windows' : isMacintosh ? 'mac' : 'linux'); + this.inputBox.value = this.getInputBoxValue(breakpoint); + // Due to an electron bug we have to do the timeout, otherwise we do not get focus + setTimeout(() => this.inputBox.focus(), 0); + + let disposed = false; + const wrapUp = once((success: boolean) => { + if (!disposed) { + disposed = true; + if (success) { + // if there is already a breakpoint on this location - remove it. + const oldBreakpoint = this.debugService.getModel().getBreakpoints() + .filter(bp => bp.lineNumber === this.lineNumber && bp.column === this.column && bp.uri.toString() === uri.toString()).pop(); + + const raw: IRawBreakpoint = { + lineNumber: this.lineNumber, + column: oldBreakpoint ? oldBreakpoint.column : undefined, + enabled: true, + condition: oldBreakpoint && oldBreakpoint.condition, + hitCondition: oldBreakpoint && oldBreakpoint.hitCondition + }; + + if (this.hitCountContext) { + raw.hitCondition = this.inputBox.value; + if (this.conditionInput) { + raw.condition = this.conditionInput; + } + } else { + raw.condition = this.inputBox.value; + if (this.hitCountInput) { + raw.hitCondition = this.hitCountInput; + } + } + + if (oldBreakpoint) { + this.debugService.removeBreakpoints(oldBreakpoint.getId()).done(null, errors.onUnexpectedError); + } + + this.debugService.addBreakpoints(uri, [raw]).done(null, errors.onUnexpectedError); + } + + this.dispose(); + } + }); + + this.toDispose.push(dom.addStandardDisposableListener(this.inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => { + const isEscape = e.equals(KeyCode.Escape); + const isEnter = e.equals(KeyCode.Enter); + if (isEscape || isEnter) { + e.stopPropagation(); + wrapUp(isEnter); + } + })); + } + + public dispose(): void { + super.dispose(); + lifecycle.dispose(this.toDispose); + setTimeout(() => this.editor.focus(), 0); + } +} diff --git a/src/vs/workbench/parts/debug/browser/debugActionItems.ts b/src/vs/workbench/parts/debug/browser/debugActionItems.ts new file mode 100644 index 0000000000..7d85bcb105 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/debugActionItems.ts @@ -0,0 +1,216 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import * as errors from 'vs/base/common/errors'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import * as dom from 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; +import { SelectActionItem, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { IDebugService } from 'vs/workbench/parts/debug/common/debug'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; + +const $ = dom.$; + +export class StartDebugActionItem extends EventEmitter implements IActionItem { + + private static SEPARATOR = '─────────'; + + public actionRunner: IActionRunner; + private container: HTMLElement; + private start: HTMLElement; + private selectBox: SelectBox; + private options: { label: string, handler: (() => boolean) }[]; + private toDispose: lifecycle.IDisposable[]; + private selected: number; + + constructor( + private context: any, + private action: IAction, + @IDebugService private debugService: IDebugService, + @IThemeService private themeService: IThemeService, + @IConfigurationService private configurationService: IConfigurationService, + @ICommandService private commandService: ICommandService, + @IQuickOpenService private quickOpenService: IQuickOpenService + ) { + super(); + this.toDispose = []; + this.selectBox = new SelectBox([], -1); + this.toDispose.push(attachSelectBoxStyler(this.selectBox, themeService, { + selectBackground: SIDE_BAR_BACKGROUND + })); + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => { + if (e.sourceConfig.launch) { + this.updateOptions(); + } + })); + this.toDispose.push(this.selectBox.onDidSelect(e => { + if (this.options[e.index].handler()) { + this.selected = e.index; + } else { + // Some select options should not remain selected https://github.com/Microsoft/vscode/issues/31526 + this.selectBox.select(this.selected); + } + })); + this.toDispose.push(this.debugService.getConfigurationManager().onDidSelectConfiguration(() => { + this.updateOptions(); + })); + } + + public render(container: HTMLElement): void { + this.container = container; + dom.addClass(container, 'start-debug-action-item'); + this.start = dom.append(container, $('.icon')); + this.start.title = this.action.label; + this.start.tabIndex = 0; + + this.toDispose.push(dom.addDisposableListener(this.start, dom.EventType.CLICK, () => { + this.start.blur(); + this.actionRunner.run(this.action, this.context).done(null, errors.onUnexpectedError); + })); + + this.toDispose.push(dom.addDisposableListener(this.start, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => { + if (this.action.enabled && e.button === 0) { + dom.addClass(this.start, 'active'); + } + })); + this.toDispose.push(dom.addDisposableListener(this.start, dom.EventType.MOUSE_UP, () => { + dom.removeClass(this.start, 'active'); + })); + this.toDispose.push(dom.addDisposableListener(this.start, dom.EventType.MOUSE_OUT, () => { + dom.removeClass(this.start, 'active'); + })); + + this.toDispose.push(dom.addDisposableListener(this.start, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + const event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Enter)) { + this.actionRunner.run(this.action, this.context).done(null, errors.onUnexpectedError); + } + if (event.equals(KeyCode.RightArrow)) { + this.selectBox.focus(); + event.stopPropagation(); + } + })); + + const selectBoxContainer = $('.configuration'); + this.selectBox.render(dom.append(container, selectBoxContainer)); + this.toDispose.push(dom.addDisposableListener(selectBoxContainer, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + const event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.LeftArrow)) { + this.start.focus(); + event.stopPropagation(); + } + })); + this.toDispose.push(attachStylerCallback(this.themeService, { selectBorder }, colors => { + this.container.style.border = colors.selectBorder ? `1px solid ${colors.selectBorder}` : null; + selectBoxContainer.style.borderLeft = colors.selectBorder ? `1px solid ${colors.selectBorder}` : null; + })); + + this.updateOptions(); + } + + public setActionContext(context: any): void { + this.context = context; + } + + public isEnabled(): boolean { + return true; + } + + public focus(fromRight?: boolean): void { + if (fromRight) { + this.selectBox.focus(); + } else { + this.start.focus(); + } + } + + public blur(): void { + this.container.blur(); + } + + public dispose(): void { + this.toDispose = lifecycle.dispose(this.toDispose); + } + + private updateOptions(): void { + this.selected = 0; + this.options = []; + const manager = this.debugService.getConfigurationManager(); + const launches = manager.getLaunches(); + manager.getLaunches().forEach(launch => + launch.getConfigurationNames().forEach(name => { + if (name === manager.selectedName && launch === manager.selectedLaunch) { + this.selected = this.options.length; + } + const label = launches.length > 1 ? `${name} (${launch.name})` : name; + this.options.push({ label, handler: () => { manager.selectConfiguration(launch, name); return true; } }); + })); + + if (this.options.length === 0) { + this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: () => false }); + } + this.options.push({ label: StartDebugActionItem.SEPARATOR, handler: undefined }); + + const disabledIdx = this.options.length - 1; + launches.forEach(l => { + const label = launches.length > 1 ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration..."); + this.options.push({ + label, handler: () => { + this.commandService.executeCommand('debug.addConfiguration', l.workspaceUri.toString()).done(undefined, errors.onUnexpectedError); + return false; + } + }); + }); + + this.selectBox.setOptions(this.options.map(data => data.label), this.selected, disabledIdx); + } +} + +export class FocusProcessActionItem extends SelectActionItem { + constructor( + action: IAction, + @IDebugService private debugService: IDebugService, + @IThemeService themeService: IThemeService + ) { + super(null, action, [], -1); + + this.toDispose.push(attachSelectBoxStyler(this.selectBox, themeService)); + + this.debugService.getViewModel().onDidFocusStackFrame(() => { + const process = this.debugService.getViewModel().focusedProcess; + if (process) { + const index = this.debugService.getModel().getProcesses().indexOf(process); + this.select(index); + } + }); + + this.debugService.getModel().onDidChangeCallStack(() => this.update()); + this.update(); + } + + private update() { + const process = this.debugService.getViewModel().focusedProcess; + const processes = this.debugService.getModel().getProcesses(); + const showRootName = this.debugService.getConfigurationManager().getLaunches().length > 1; + const names = processes.map(p => p.getName(showRootName)); + this.setOptions(names, process ? processes.indexOf(process) : undefined); + } +} diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts new file mode 100644 index 0000000000..ac67f1621f --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -0,0 +1,840 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { Action } from 'vs/base/common/actions'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IDebugService, State, IProcess, IThread, IEnablement, IBreakpoint, IStackFrame, IFunctionBreakpoint, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, IExpression, REPL_ID, ProcessState } + from 'vs/workbench/parts/debug/common/debug'; +import { Variable, Expression, Thread, Breakpoint, Process } from 'vs/workbench/parts/debug/common/debugModel'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { TogglePanelAction } from 'vs/workbench/browser/panel'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; + +export abstract class AbstractDebugAction extends Action { + + protected toDispose: lifecycle.IDisposable[]; + + constructor( + id: string, label: string, cssClass: string, + @IDebugService protected debugService: IDebugService, + @IKeybindingService private keybindingService: IKeybindingService, + public weight?: number + ) { + super(id, label, cssClass, false); + this.toDispose = []; + this.toDispose.push(this.debugService.onDidChangeState(state => this.updateEnablement(state))); + + this.updateLabel(label); + this.updateEnablement(); + } + + public run(e?: any): TPromise { + throw new Error('implement me'); + } + + public get tooltip(): string { + const keybinding = this.keybindingService.lookupKeybinding(this.id); + const keybindingLabel = keybinding && keybinding.getLabel(); + + return keybindingLabel ? `${this.label} (${keybindingLabel})` : this.label; + } + + protected updateLabel(newLabel: string): void { + this.label = newLabel; + } + + protected updateEnablement(state = this.debugService.state): void { + this.enabled = this.isEnabled(state); + } + + protected isEnabled(state: State): boolean { + return true; + } + + public dispose(): void { + super.dispose(); + this.toDispose = lifecycle.dispose(this.toDispose); + } +} + +export class ConfigureAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.configure'; + static LABEL = nls.localize('openLaunchJson', "Open {0}", 'launch.json'); + + constructor(id: string, label: string, + @IDebugService debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IMessageService private messageService: IMessageService + ) { + super(id, label, 'debug-action configure', debugService, keybindingService); + this.toDispose.push(debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateClass())); + this.updateClass(); + } + + public get tooltip(): string { + if (this.debugService.getConfigurationManager().selectedName) { + return ConfigureAction.LABEL; + } + + return nls.localize('launchJsonNeedsConfigurtion', "Configure or Fix 'launch.json'"); + } + + private updateClass(): void { + this.class = this.debugService.getConfigurationManager().selectedName ? 'debug-action configure' : 'debug-action configure notification'; + } + + public run(event?: any): TPromise { + if (!this.contextService.hasWorkspace()) { + this.messageService.show(severity.Info, nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration.")); + return TPromise.as(null); + } + + const sideBySide = !!(event && (event.ctrlKey || event.metaKey)); + return this.debugService.getConfigurationManager().selectedLaunch.openConfigFile(sideBySide); + } +} + +export class StartAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.start'; + static LABEL = nls.localize('startDebug', "Start Debugging"); + + constructor(id: string, label: string, + @IDebugService debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { + super(id, label, 'debug-action start', debugService, keybindingService); + this.debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateEnablement()); + this.debugService.getModel().onDidChangeCallStack(() => this.updateEnablement()); + } + + public run(): TPromise { + const launch = this.debugService.getConfigurationManager().selectedLaunch; + return this.debugService.startDebugging(launch ? launch.workspaceUri : undefined, undefined, this.isNoDebug()); + } + + protected isNoDebug(): boolean { + return false; + } + + // Disabled if the launch drop down shows the launch config that is already running. + protected isEnabled(state: State): boolean { + const processes = this.debugService.getModel().getProcesses(); + const selectedName = this.debugService.getConfigurationManager().selectedName; + const launch = this.debugService.getConfigurationManager().selectedLaunch; + + if (state === State.Initializing) { + return false; + } + if (this.contextService && !this.contextService.hasWorkspace() && processes.length > 0) { + return false; + } + if (processes.some(p => p.getName(false) === selectedName && (!launch || p.session.root.toString() === launch.workspaceUri.toString()))) { + return false; + } + const compound = launch && launch.getCompound(selectedName); + if (compound && compound.configurations && processes.some(p => compound.configurations.indexOf(p.getName(false)) !== -1)) { + return false; + } + + return true; + } +} + +export class RunAction extends StartAction { + static ID = 'workbench.action.debug.run'; + static LABEL = nls.localize('startWithoutDebugging', "Start Without Debugging"); + + protected isNoDebug(): boolean { + return true; + } +} + +export class SelectAndStartAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.selectandstart'; + static LABEL = nls.localize('selectAndStartDebugging', "Select and Start Debugging"); + + constructor(id: string, label: string, + @IDebugService debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + @ICommandService commandService: ICommandService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IFileService fileService: IFileService, + @IQuickOpenService private quickOpenService: IQuickOpenService + ) { + super(id, label, undefined, debugService, keybindingService); + this.quickOpenService = quickOpenService; + } + + public run(): TPromise { + return this.quickOpenService.show('debug '); + } +} + +export class RestartAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.restart'; + static LABEL = nls.localize('restartDebug', "Restart"); + static RECONNECT_LABEL = nls.localize('reconnectDebug', "Reconnect"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action restart', debugService, keybindingService, 70); + this.setLabel(this.debugService.getViewModel().focusedProcess); + this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => this.setLabel(this.debugService.getViewModel().focusedProcess))); + } + + private setLabel(process: IProcess): void { + this.updateLabel(process && process.state === ProcessState.ATTACH ? RestartAction.RECONNECT_LABEL : RestartAction.LABEL); + } + + public run(process: IProcess): TPromise { + if (!(process instanceof Process)) { + process = this.debugService.getViewModel().focusedProcess; + } + if (!process) { + return TPromise.as(null); + } + + if (this.debugService.getModel().getProcesses().length <= 1) { + this.debugService.removeReplExpressions(); + } + return this.debugService.restartProcess(process); + } + + protected isEnabled(state: State): boolean { + return super.isEnabled(state) && state !== State.Inactive; + } +} + +export class StepOverAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.stepOver'; + static LABEL = nls.localize('stepOverDebug', "Step Over"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action step-over', debugService, keybindingService, 20); + } + + public run(thread: IThread): TPromise { + if (!(thread instanceof Thread)) { + thread = this.debugService.getViewModel().focusedThread; + } + + return thread ? thread.next() : TPromise.as(null); + } + + protected isEnabled(state: State): boolean { + return super.isEnabled(state) && state === State.Stopped; + } +} + +export class StepIntoAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.stepInto'; + static LABEL = nls.localize('stepIntoDebug', "Step Into"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action step-into', debugService, keybindingService, 30); + } + + public run(thread: IThread): TPromise { + if (!(thread instanceof Thread)) { + thread = this.debugService.getViewModel().focusedThread; + } + + return thread ? thread.stepIn() : TPromise.as(null); + } + + protected isEnabled(state: State): boolean { + return super.isEnabled(state) && state === State.Stopped; + } +} + +export class StepOutAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.stepOut'; + static LABEL = nls.localize('stepOutDebug', "Step Out"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action step-out', debugService, keybindingService, 40); + } + + public run(thread: IThread): TPromise { + if (!(thread instanceof Thread)) { + thread = this.debugService.getViewModel().focusedThread; + } + + return thread ? thread.stepOut() : TPromise.as(null); + } + + protected isEnabled(state: State): boolean { + return super.isEnabled(state) && state === State.Stopped; + } +} + +export class StopAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.stop'; + static LABEL = nls.localize('stopDebug', "Stop"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action stop', debugService, keybindingService, 80); + } + + public run(process: IProcess): TPromise { + if (!(process instanceof Process)) { + process = this.debugService.getViewModel().focusedProcess; + } + + return this.debugService.stopProcess(process); + } + + protected isEnabled(state: State): boolean { + return super.isEnabled(state) && state !== State.Inactive; + } +} + +export class DisconnectAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.disconnect'; + static LABEL = nls.localize('disconnectDebug', "Disconnect"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action disconnect', debugService, keybindingService, 80); + } + + public run(): TPromise { + const process = this.debugService.getViewModel().focusedProcess; + return this.debugService.stopProcess(process); + } + + protected isEnabled(state: State): boolean { + return super.isEnabled(state) && state !== State.Inactive; + } +} + +export class ContinueAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.continue'; + static LABEL = nls.localize('continueDebug', "Continue"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action continue', debugService, keybindingService, 10); + } + + public run(thread: IThread): TPromise { + if (!(thread instanceof Thread)) { + thread = this.debugService.getViewModel().focusedThread; + } + + return thread ? thread.continue() : TPromise.as(null); + } + + protected isEnabled(state: State): boolean { + return super.isEnabled(state) && state === State.Stopped; + } +} + +export class PauseAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.pause'; + static LABEL = nls.localize('pauseDebug', "Pause"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action pause', debugService, keybindingService, 10); + } + + public run(thread: IThread): TPromise { + if (!(thread instanceof Thread)) { + thread = this.debugService.getViewModel().focusedThread; + } + + return thread ? thread.pause() : TPromise.as(null); + } + + protected isEnabled(state: State): boolean { + return super.isEnabled(state) && state === State.Running; + } +} + +export class RestartFrameAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.restartFrame'; + static LABEL = nls.localize('restartFrame', "Restart Frame"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, undefined, debugService, keybindingService); + } + + public run(frame: IStackFrame): TPromise { + if (!frame) { + frame = this.debugService.getViewModel().focusedStackFrame; + } + + return frame.restart(); + } +} + +export class RemoveBreakpointAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.removeBreakpoint'; + static LABEL = nls.localize('removeBreakpoint', "Remove Breakpoint"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action remove', debugService, keybindingService); + } + + public run(breakpoint: IBreakpoint): TPromise { + return breakpoint instanceof Breakpoint ? this.debugService.removeBreakpoints(breakpoint.getId()) + : this.debugService.removeFunctionBreakpoints(breakpoint.getId()); + } +} + +export class RemoveAllBreakpointsAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.removeAllBreakpoints'; + static LABEL = nls.localize('removeAllBreakpoints', "Remove All Breakpoints"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action remove-all', debugService, keybindingService); + this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); + } + + public run(): TPromise { + return TPromise.join([this.debugService.removeBreakpoints(), this.debugService.removeFunctionBreakpoints()]); + } + + protected isEnabled(state: State): boolean { + const model = this.debugService.getModel(); + return super.isEnabled(state) && (model.getBreakpoints().length > 0 || model.getFunctionBreakpoints().length > 0); + } +} + +export class EnableBreakpointAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.enableBreakpoint'; + static LABEL = nls.localize('enableBreakpoint', "Enable Breakpoint"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, undefined, debugService, keybindingService); + } + + public run(element: IEnablement): TPromise { + return this.debugService.enableOrDisableBreakpoints(true, element); + } +} + +export class DisableBreakpointAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.disableBreakpoint'; + static LABEL = nls.localize('disableBreakpoint', "Disable Breakpoint"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, undefined, debugService, keybindingService); + } + + public run(element: IEnablement): TPromise { + return this.debugService.enableOrDisableBreakpoints(false, element); + } +} + +export class EnableAllBreakpointsAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.enableAllBreakpoints'; + static LABEL = nls.localize('enableAllBreakpoints', "Enable All Breakpoints"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action enable-all-breakpoints', debugService, keybindingService); + this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); + } + + public run(): TPromise { + return this.debugService.enableOrDisableBreakpoints(true); + } + + protected isEnabled(state: State): boolean { + const model = this.debugService.getModel(); + return super.isEnabled(state) && (model.getBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getExceptionBreakpoints()).some(bp => !bp.enabled); + } +} + +export class DisableAllBreakpointsAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.disableAllBreakpoints'; + static LABEL = nls.localize('disableAllBreakpoints', "Disable All Breakpoints"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action disable-all-breakpoints', debugService, keybindingService); + this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); + } + + public run(): TPromise { + return this.debugService.enableOrDisableBreakpoints(false); + } + + protected isEnabled(state: State): boolean { + const model = this.debugService.getModel(); + return super.isEnabled(state) && (model.getBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getExceptionBreakpoints()).some(bp => bp.enabled); + } +} + +export class ToggleBreakpointsActivatedAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.toggleBreakpointsActivatedAction'; + static ACTIVATE_LABEL = nls.localize('activateBreakpoints', "Activate Breakpoints"); + static DEACTIVATE_LABEL = nls.localize('deactivateBreakpoints', "Deactivate Breakpoints"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action breakpoints-activate', debugService, keybindingService); + this.updateLabel(this.debugService.getModel().areBreakpointsActivated() ? ToggleBreakpointsActivatedAction.DEACTIVATE_LABEL : ToggleBreakpointsActivatedAction.ACTIVATE_LABEL); + + this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => { + this.updateLabel(this.debugService.getModel().areBreakpointsActivated() ? ToggleBreakpointsActivatedAction.DEACTIVATE_LABEL : ToggleBreakpointsActivatedAction.ACTIVATE_LABEL); + this.updateEnablement(); + })); + } + + public run(): TPromise { + return this.debugService.setBreakpointsActivated(!this.debugService.getModel().areBreakpointsActivated()); + } + + protected isEnabled(state: State): boolean { + return (this.debugService.getModel().getFunctionBreakpoints().length + this.debugService.getModel().getBreakpoints().length) > 0; + } +} + +export class ReapplyBreakpointsAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.reapplyBreakpointsAction'; + static LABEL = nls.localize('reapplyAllBreakpoints', "Reapply All Breakpoints"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, null, debugService, keybindingService); + this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); + } + + public run(): TPromise { + return this.debugService.setBreakpointsActivated(true); + } + + protected isEnabled(state: State): boolean { + const model = this.debugService.getModel(); + return super.isEnabled(state) && state !== State.Inactive && + (model.getFunctionBreakpoints().length + model.getBreakpoints().length + model.getExceptionBreakpoints().length > 0); + } +} + +export class AddFunctionBreakpointAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.addFunctionBreakpointAction'; + static LABEL = nls.localize('addFunctionBreakpoint', "Add Function Breakpoint"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action add-function-breakpoint', debugService, keybindingService); + } + + public run(): TPromise { + this.debugService.addFunctionBreakpoint(); + return TPromise.as(null); + } +} + +export class RenameFunctionBreakpointAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.renameFunctionBreakpointAction'; + static LABEL = nls.localize('renameFunctionBreakpoint', "Rename Function Breakpoint"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, null, debugService, keybindingService); + } + + public run(fbp: IFunctionBreakpoint): TPromise { + this.debugService.getViewModel().setSelectedFunctionBreakpoint(fbp); + return TPromise.as(null); + } +} + +export class AddConditionalBreakpointAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.addConditionalBreakpointAction'; + static LABEL = nls.localize('addConditionalBreakpoint', "Add Conditional Breakpoint..."); + + constructor(id: string, label: string, + private editor: ICodeEditor, + private lineNumber: number, + @IDebugService debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + ) { + super(id, label, null, debugService, keybindingService); + } + + public run(): TPromise { + this.editor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(this.lineNumber, undefined); + return TPromise.as(null); + } +} + +export class EditConditionalBreakpointAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.editConditionalBreakpointAction'; + static LABEL = nls.localize('editConditionalBreakpoint', "Edit Breakpoint..."); + + constructor(id: string, label: string, + private editor: ICodeEditor, + @IDebugService debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + ) { + super(id, label, null, debugService, keybindingService); + } + + public run(breakpoint: IBreakpoint): TPromise { + this.editor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(breakpoint.lineNumber, breakpoint.column); + return TPromise.as(null); + } +} + + +export class SetValueAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.setValue'; + static LABEL = nls.localize('setValue', "Set Value"); + + constructor(id: string, label: string, private variable: Variable, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, null, debugService, keybindingService); + } + + public run(): TPromise { + if (this.variable instanceof Variable) { + this.debugService.getViewModel().setSelectedExpression(this.variable); + } + + return TPromise.as(null); + } + + protected isEnabled(state: State): boolean { + const process = this.debugService.getViewModel().focusedProcess; + return super.isEnabled(state) && state === State.Stopped && process && process.session.capabilities.supportsSetVariable; + } +} + + +export class AddWatchExpressionAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.addWatchExpression'; + static LABEL = nls.localize('addWatchExpression', "Add Expression"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action add-watch-expression', debugService, keybindingService); + this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); + } + + public run(): TPromise { + return this.debugService.addWatchExpression(); + } + + protected isEnabled(state: State): boolean { + return super.isEnabled(state) && this.debugService.getModel().getWatchExpressions().every(we => !!we.name); + } +} + +export class EditWatchExpressionAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.editWatchExpression'; + static LABEL = nls.localize('editWatchExpression', "Edit Expression"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, undefined, debugService, keybindingService); + } + + public run(expression: Expression): TPromise { + this.debugService.getViewModel().setSelectedExpression(expression); + return TPromise.as(null); + } +} + +export class AddToWatchExpressionsAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.addToWatchExpressions'; + static LABEL = nls.localize('addToWatchExpressions', "Add to Watch"); + + constructor(id: string, label: string, private expression: IExpression, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action add-to-watch', debugService, keybindingService); + } + + public run(): TPromise { + const name = this.expression instanceof Variable ? this.expression.evaluateName : this.expression.name; + return this.debugService.addWatchExpression(name); + } +} + +export class RemoveWatchExpressionAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.removeWatchExpression'; + static LABEL = nls.localize('removeWatchExpression', "Remove Expression"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, undefined, debugService, keybindingService); + } + + public run(expression: Expression): TPromise { + this.debugService.removeWatchExpressions(expression.getId()); + return TPromise.as(null); + } +} + +export class RemoveAllWatchExpressionsAction extends AbstractDebugAction { + static ID = 'workbench.debug.viewlet.action.removeAllWatchExpressions'; + static LABEL = nls.localize('removeAllWatchExpressions', "Remove All Expressions"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action remove-all', debugService, keybindingService); + this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); + } + + public run(): TPromise { + this.debugService.removeWatchExpressions(); + return TPromise.as(null); + } + + protected isEnabled(state: State): boolean { + return super.isEnabled(state) && this.debugService.getModel().getWatchExpressions().length > 0; + } +} + +export class ClearReplAction extends AbstractDebugAction { + static ID = 'workbench.debug.panel.action.clearReplAction'; + static LABEL = nls.localize('clearRepl', "Clear Console"); + + constructor(id: string, label: string, + @IDebugService debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + @IPanelService private panelService: IPanelService + ) { + super(id, label, 'debug-action clear-repl', debugService, keybindingService); + } + + public run(): TPromise { + this.debugService.removeReplExpressions(); + + // focus back to repl + return this.panelService.openPanel(REPL_ID, true); + } +} + +export class ToggleReplAction extends TogglePanelAction { + static ID = 'workbench.debug.action.toggleRepl'; + static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugConsoleAction' }, 'Debug Console'); + private toDispose: lifecycle.IDisposable[]; + + constructor(id: string, label: string, + @IDebugService private debugService: IDebugService, + @IPartService partService: IPartService, + @IPanelService panelService: IPanelService + ) { + super(id, label, REPL_ID, panelService, partService, 'debug-action toggle-repl'); + this.toDispose = []; + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.debugService.getModel().onDidChangeReplElements(() => { + if (!this.isReplVisible()) { + this.class = 'debug-action toggle-repl notification'; + this.tooltip = nls.localize('unreadOutput', "New Output in Debug Console"); + } + })); + this.toDispose.push(this.panelService.onDidPanelOpen(panel => { + if (panel.getId() === REPL_ID) { + this.class = 'debug-action toggle-repl'; + this.tooltip = ToggleReplAction.LABEL; + } + })); + } + + private isReplVisible(): boolean { + const panel = this.panelService.getActivePanel(); + return panel && panel.getId() === REPL_ID; + } + + public dispose(): void { + super.dispose(); + this.toDispose = lifecycle.dispose(this.toDispose); + } +} + +export class FocusReplAction extends Action { + + static ID = 'workbench.debug.action.focusRepl'; + static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusConsole' }, 'Focus Debug Console'); + + + constructor(id: string, label: string, + @IPanelService private panelService: IPanelService + ) { + super(id, label); + } + + public run(): TPromise { + return this.panelService.openPanel(REPL_ID, true); + } +} + +export class FocusProcessAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.focusProcess'; + static LABEL = nls.localize('focusProcess', "Focus Process"); + + constructor(id: string, label: string, + @IDebugService debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(id, label, null, debugService, keybindingService, 100); + } + + public run(processName: string): TPromise { + const isMultiRoot = this.debugService.getConfigurationManager().getLaunches().length > 1; + const process = this.debugService.getModel().getProcesses().filter(p => p.getName(isMultiRoot) === processName).pop(); + return this.debugService.focusStackFrameAndEvaluate(null, process, true).then(() => { + const stackFrame = this.debugService.getViewModel().focusedStackFrame; + if (stackFrame) { + return stackFrame.openInEditor(this.editorService, true); + } + return undefined; + }); + } +} + +// Actions used by the chakra debugger +export class StepBackAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.stepBack'; + static LABEL = nls.localize('stepBackDebug', "Step Back"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action step-back', debugService, keybindingService, 50); + } + + public run(thread: IThread): TPromise { + if (!(thread instanceof Thread)) { + thread = this.debugService.getViewModel().focusedThread; + } + + return thread ? thread.stepBack() : TPromise.as(null); + } + + protected isEnabled(state: State): boolean { + const process = this.debugService.getViewModel().focusedProcess; + return super.isEnabled(state) && state === State.Stopped && + process && process.session.capabilities.supportsStepBack; + } +} + +export class ReverseContinueAction extends AbstractDebugAction { + static ID = 'workbench.action.debug.reverseContinue'; + static LABEL = nls.localize('reverseContinue', "Reverse"); + + constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { + super(id, label, 'debug-action reverse-continue', debugService, keybindingService, 60); + } + + public run(thread: IThread): TPromise { + if (!(thread instanceof Thread)) { + thread = this.debugService.getViewModel().focusedThread; + } + + return thread ? thread.reverseContinue() : TPromise.as(null); + } + + protected isEnabled(state: State): boolean { + const process = this.debugService.getViewModel().focusedProcess; + return super.isEnabled(state) && state === State.Stopped && + process && process.session.capabilities.supportsStepBack; + } +} diff --git a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts new file mode 100644 index 0000000000..4782a8e1d4 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts @@ -0,0 +1,278 @@ +/*--------------------------------------------------------------------------------------------- + * 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!vs/workbench/parts/debug/browser/media/debugActionsWidget'; +import * as errors from 'vs/base/common/errors'; +import * as strings from 'vs/base/common/strings'; +import * as browser from 'vs/base/browser/browser'; +import severity from 'vs/base/common/severity'; +import * as builder from 'vs/base/browser/builder'; +import * as dom from 'vs/base/browser/dom'; +import * as arrays from 'vs/base/common/arrays'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IAction } from 'vs/base/common/actions'; +import { EventType } from 'vs/base/common/events'; +import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IDebugConfiguration, IDebugService, State } from 'vs/workbench/parts/debug/common/debug'; +import { AbstractDebugAction, PauseAction, ContinueAction, StepBackAction, ReverseContinueAction, StopAction, DisconnectAction, StepOverAction, StepIntoAction, StepOutAction, RestartAction, FocusProcessAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { FocusProcessActionItem } from 'vs/workbench/parts/debug/browser/debugActionItems'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Themable } from 'vs/workbench/common/theme'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { registerColor, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { localize } from 'vs/nls'; + +const $ = builder.$; +const DEBUG_ACTIONS_WIDGET_POSITION_KEY = 'debug.actionswidgetposition'; + +export const debugToolBarBackground = registerColor('debugToolBar.background', { + dark: '#333333', + light: '#F3F3F3', + hc: '#000000' +}, localize('debugToolBarBackground', "Debug toolbar background color.")); + +export class DebugActionsWidget extends Themable implements IWorkbenchContribution { + private static ID = 'debug.actionsWidget'; + + private $el: builder.Builder; + private dragArea: builder.Builder; + private actionBar: ActionBar; + private allActions: AbstractDebugAction[]; + private activeActions: AbstractDebugAction[]; + + private isVisible: boolean; + private isBuilt: boolean; + + constructor( + @IMessageService private messageService: IMessageService, + @ITelemetryService private telemetryService: ITelemetryService, + @IDebugService private debugService: IDebugService, + @IInstantiationService private instantiationService: IInstantiationService, + @IPartService private partService: IPartService, + @IStorageService private storageService: IStorageService, + @IConfigurationService private configurationService: IConfigurationService, + @IThemeService themeService: IThemeService + ) { + super(themeService); + + this.$el = $().div().addClass('debug-actions-widget').style('top', `${partService.getTitleBarOffset()}px`); + this.dragArea = $().div().addClass('drag-area'); + this.$el.append(this.dragArea); + + const actionBarContainter = $().div().addClass('.action-bar-container'); + this.$el.append(actionBarContainter); + + this.activeActions = []; + this.actionBar = new ActionBar(actionBarContainter, { + orientation: ActionsOrientation.HORIZONTAL, + actionItemProvider: (action: IAction) => { + if (action.id === FocusProcessAction.ID) { + return this.instantiationService.createInstance(FocusProcessActionItem, action); + } + + return null; + } + }); + + this.updateStyles(); + + this.toUnbind.push(this.actionBar); + this.registerListeners(); + + this.hide(); + this.isBuilt = false; + } + + private registerListeners(): void { + this.toUnbind.push(this.debugService.onDidChangeState(state => this.update(state))); + this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.update(this.debugService.state))); + this.toUnbind.push(this.actionBar.actionRunner.addListener(EventType.RUN, (e: any) => { + // check for error + if (e.error && !errors.isPromiseCanceledError(e.error)) { + this.messageService.show(severity.Error, e.error); + } + + // log in telemetry + if (this.telemetryService) { + this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: 'debugActionsWidget' }); + } + })); + $(window).on(dom.EventType.RESIZE, () => this.setXCoordinate(), this.toUnbind); + + this.dragArea.on(dom.EventType.MOUSE_UP, (event: MouseEvent) => { + const mouseClickEvent = new StandardMouseEvent(event); + if (mouseClickEvent.detail === 2) { + // double click on debug bar centers it again #8250 + const widgetWidth = this.$el.getHTMLElement().clientWidth; + this.setXCoordinate(0.5 * window.innerWidth - 0.5 * widgetWidth); + this.storePosition(); + } + }); + + this.dragArea.on(dom.EventType.MOUSE_DOWN, (event: MouseEvent) => { + const $window = $(window); + this.dragArea.addClass('dragged'); + + $window.on('mousemove', (e: MouseEvent) => { + const mouseMoveEvent = new StandardMouseEvent(e); + // Prevent default to stop editor selecting text #8524 + mouseMoveEvent.preventDefault(); + // Reduce x by width of drag handle to reduce jarring #16604 + this.setXCoordinate(mouseMoveEvent.posx - 14); + }).once('mouseup', (e: MouseEvent) => { + this.storePosition(); + this.dragArea.removeClass('dragged'); + $window.off('mousemove'); + }); + }); + + this.toUnbind.push(this.partService.onTitleBarVisibilityChange(() => this.positionDebugWidget())); + this.toUnbind.push(browser.onDidChangeZoomLevel(() => this.positionDebugWidget())); + } + + private storePosition(): void { + const position = parseFloat(this.$el.getComputedStyle().left) / window.innerWidth; + this.storageService.store(DEBUG_ACTIONS_WIDGET_POSITION_KEY, position, StorageScope.WORKSPACE); + this.telemetryService.publicLog(DEBUG_ACTIONS_WIDGET_POSITION_KEY, { position }); + } + + protected updateStyles(): void { + super.updateStyles(); + + if (this.$el) { + this.$el.style('background-color', this.getColor(debugToolBarBackground)); + + const widgetShadowColor = this.getColor(widgetShadow); + this.$el.style('box-shadow', widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : null); + + const contrastBorderColor = this.getColor(contrastBorder); + this.$el.style('border-style', contrastBorderColor ? 'solid' : null); + this.$el.style('border-width', contrastBorderColor ? '1px' : null); + this.$el.style('border-color', contrastBorderColor); + } + } + + private positionDebugWidget(): void { + const titlebarOffset = this.partService.getTitleBarOffset(); + + $(this.$el).style('top', `${titlebarOffset}px`); + } + + private setXCoordinate(x?: number): void { + if (!this.isVisible) { + return; + } + const widgetWidth = this.$el.getHTMLElement().clientWidth; + if (x === undefined) { + const positionPercentage = this.storageService.get(DEBUG_ACTIONS_WIDGET_POSITION_KEY, StorageScope.WORKSPACE); + x = positionPercentage !== undefined ? parseFloat(positionPercentage) * window.innerWidth : (0.5 * window.innerWidth - 0.5 * widgetWidth); + } + + x = Math.max(0, Math.min(x, window.innerWidth - widgetWidth)); // do not allow the widget to overflow on the right + this.$el.style('left', `${x}px`); + } + + public getId(): string { + return DebugActionsWidget.ID; + } + + private update(state: State): void { + if (state === State.Inactive || this.configurationService.getConfiguration('debug').hideActionBar) { + return this.hide(); + } + + const actions = this.getActions(); + if (!arrays.equals(actions, this.activeActions, (first, second) => first.id === second.id)) { + this.actionBar.clear(); + this.actionBar.push(actions, { icon: true, label: false }); + this.activeActions = actions; + } + this.show(); + } + + private show(): void { + if (this.isVisible) { + return; + } + if (!this.isBuilt) { + this.isBuilt = true; + this.$el.build(builder.withElementById(this.partService.getWorkbenchElementId()).getHTMLElement()); + } + + this.isVisible = true; + this.$el.show(); + this.setXCoordinate(); + } + + private hide(): void { + this.isVisible = false; + this.$el.hide(); + } + + private getActions(): AbstractDebugAction[] { + if (!this.allActions) { + this.allActions = []; + this.allActions.push(this.instantiationService.createInstance(ContinueAction, ContinueAction.ID, ContinueAction.LABEL)); + this.allActions.push(this.instantiationService.createInstance(PauseAction, PauseAction.ID, PauseAction.LABEL)); + this.allActions.push(this.instantiationService.createInstance(StopAction, StopAction.ID, StopAction.LABEL)); + this.allActions.push(this.instantiationService.createInstance(DisconnectAction, DisconnectAction.ID, DisconnectAction.LABEL)); + this.allActions.push(this.instantiationService.createInstance(StepOverAction, StepOverAction.ID, StepOverAction.LABEL)); + this.allActions.push(this.instantiationService.createInstance(StepIntoAction, StepIntoAction.ID, StepIntoAction.LABEL)); + this.allActions.push(this.instantiationService.createInstance(StepOutAction, StepOutAction.ID, StepOutAction.LABEL)); + this.allActions.push(this.instantiationService.createInstance(RestartAction, RestartAction.ID, RestartAction.LABEL)); + this.allActions.push(this.instantiationService.createInstance(StepBackAction, StepBackAction.ID, StepBackAction.LABEL)); + this.allActions.push(this.instantiationService.createInstance(ReverseContinueAction, ReverseContinueAction.ID, ReverseContinueAction.LABEL)); + this.allActions.push(this.instantiationService.createInstance(FocusProcessAction, FocusProcessAction.ID, FocusProcessAction.LABEL)); + this.allActions.forEach(a => { + this.toUnbind.push(a); + }); + } + + const state = this.debugService.state; + const process = this.debugService.getViewModel().focusedProcess; + const attached = process && process.configuration.request === 'attach' && process.configuration.type && !strings.equalsIgnoreCase(process.configuration.type, 'extensionHost'); + + return this.allActions.filter(a => { + if (a.id === ContinueAction.ID) { + return state !== State.Running; + } + if (a.id === PauseAction.ID) { + return state === State.Running; + } + if (a.id === StepBackAction.ID) { + return process && process.session.capabilities.supportsStepBack; + } + if (a.id === ReverseContinueAction.ID) { + return process && process.session.capabilities.supportsStepBack; + } + if (a.id === DisconnectAction.ID) { + return attached; + } + if (a.id === StopAction.ID) { + return !attached; + } + if (a.id === FocusProcessAction.ID) { + return this.debugService.getViewModel().isMultiProcessView(); + } + + return true; + }).sort((first, second) => first.weight - second.weight); + } + + public dispose(): void { + super.dispose(); + + if (this.$el) { + this.$el.destroy(); + delete this.$el; + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/debugContentProvider.ts b/src/vs/workbench/parts/debug/browser/debugContentProvider.ts new file mode 100644 index 0000000000..e5c40fabc7 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/debugContentProvider.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import uri from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { guessMimeTypes, MIME_TEXT } from 'vs/base/common/mime'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { DEBUG_SCHEME, IDebugService, IProcess } from 'vs/workbench/parts/debug/common/debug'; + +export class DebugContentProvider implements IWorkbenchContribution, ITextModelContentProvider { + + constructor( + @ITextModelService textModelResolverService: ITextModelService, + @IDebugService private debugService: IDebugService, + @IModelService private modelService: IModelService, + @IModeService private modeService: IModeService + ) { + textModelResolverService.registerTextModelContentProvider(DEBUG_SCHEME, this); + } + + public getId(): string { + return 'debug.contentprovider'; + } + + public provideTextContent(resource: uri): TPromise { + + let process: IProcess; + if (resource.query) { + const keyvalues = resource.query.split('&'); + for (let keyvalue of keyvalues) { + const pair = keyvalue.split('='); + if (pair.length === 2 && pair[0] === 'session') { + process = this.debugService.findProcessByUUID(decodeURIComponent(pair[1])); + break; + } + } + } + + if (!process) { + // fallback: use focused process + process = this.debugService.getViewModel().focusedProcess; + } + + if (!process) { + return TPromise.wrapError(new Error(localize('unable', "Unable to resolve the resource without a debug session"))); + } + const source = process.sources.get(resource.toString()); + let rawSource: DebugProtocol.Source; + if (source) { + rawSource = source.raw; + } else { + // Remove debug: scheme + rawSource = { path: resource.with({ scheme: '', query: '' }).toString(true) }; + } + + return process.session.source({ sourceReference: source ? source.reference : undefined, source: rawSource }).then(response => { + const mime = response.body.mimeType || guessMimeTypes(resource.toString())[0]; + const modePromise = this.modeService.getOrCreateMode(mime); + const model = this.modelService.createModel(response.body.content, modePromise, resource); + + return model; + }, (err: DebugProtocol.ErrorResponse) => { + this.debugService.sourceIsNotAvailable(resource); + const modePromise = this.modeService.getOrCreateMode(MIME_TEXT); + const model = this.modelService.createModel(err.message, modePromise, resource); + + return model; + }); + } +} diff --git a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts new file mode 100644 index 0000000000..12fcc082af --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts @@ -0,0 +1,277 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { Range } from 'vs/editor/common/core/range'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ServicesAccessor, editorAction, EditorAction, CommonEditorRegistry, EditorCommand } from 'vs/editor/common/editorCommonExtensions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE } from 'vs/workbench/parts/debug/common/debug'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; + +@editorAction +class ToggleBreakpointAction extends EditorAction { + constructor() { + super({ + id: 'editor.debug.action.toggleBreakpoint', + label: nls.localize('toggleBreakpointAction', "Debug: Toggle Breakpoint"), + alias: 'Debug: Toggle Breakpoint', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.F9 + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + const debugService = accessor.get(IDebugService); + + const position = editor.getPosition(); + const modelUri = editor.getModel().uri; + const bps = debugService.getModel().getBreakpoints() + .filter(bp => bp.lineNumber === position.lineNumber && bp.uri.toString() === modelUri.toString()); + + if (bps.length) { + return TPromise.join(bps.map(bp => debugService.removeBreakpoints(bp.getId()))); + } + if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { + return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber }]); + } + + return TPromise.as(null); + } +} + +function addColumnBreakpoint(accessor: ServicesAccessor, editor: ICommonCodeEditor, remove: boolean): TPromise { + const debugService = accessor.get(IDebugService); + + const position = editor.getPosition(); + const modelUri = editor.getModel().uri; + const bp = debugService.getModel().getBreakpoints() + .filter(bp => bp.lineNumber === position.lineNumber && bp.column === position.column && bp.uri.toString() === modelUri.toString()).pop(); + + if (bp) { + return remove ? debugService.removeBreakpoints(bp.getId()) : TPromise.as(null); + } + if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { + return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber, column: position.column }]); + } + + return TPromise.as(null); +} + +@editorAction +class ToggleColumnBreakpointAction extends EditorAction { + constructor() { + super({ + id: 'editor.debug.action.toggleColumnBreakpoint', + label: nls.localize('columnBreakpointAction', "Debug: Column Breakpoint"), + alias: 'Debug: Column Breakpoint', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.Shift | KeyCode.F9 + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + return addColumnBreakpoint(accessor, editor, true); + } +} + +// TODO@Isidor merge two column breakpoints actions together +@editorAction +class ToggleColumnBreakpointContextMenuAction extends EditorAction { + constructor() { + super({ + id: 'editor.debug.action.toggleColumnBreakpointContextMenu', + label: nls.localize('columnBreakpoint', "Add Column Breakpoint"), + alias: 'Toggle Column Breakpoint', + precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, EditorContextKeys.writable), + menuOpts: { + group: 'debug', + order: 1 + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + return addColumnBreakpoint(accessor, editor, false); + } +} + +@editorAction +class ConditionalBreakpointAction extends EditorAction { + + constructor() { + super({ + id: 'editor.debug.action.conditionalBreakpoint', + label: nls.localize('conditionalBreakpointEditorAction', "Debug: Add Conditional Breakpoint..."), + alias: 'Debug: Add Conditional Breakpoint...', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const debugService = accessor.get(IDebugService); + + const { lineNumber, column } = editor.getPosition(); + if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { + editor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(lineNumber, column); + } + } +} + + +@editorAction +class RunToCursorAction extends EditorAction { + + constructor() { + super({ + id: 'editor.debug.action.runToCursor', + label: nls.localize('runToCursor', "Run to Cursor"), + alias: 'Debug: Run to Cursor', + precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, EditorContextKeys.writable, CONTEXT_DEBUG_STATE.isEqualTo('stopped')), + menuOpts: { + group: 'debug', + order: 2 + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + const debugService = accessor.get(IDebugService); + + if (debugService.state !== State.Stopped) { + return TPromise.as(null); + } + const position = editor.getPosition(); + const uri = editor.getModel().uri; + + const oneTimeListener = debugService.getViewModel().focusedProcess.session.onDidEvent(event => { + if (event.event === 'stopped' || event.event === 'exit') { + const toRemove = debugService.getModel().getBreakpoints() + .filter(bp => bp.lineNumber === position.lineNumber && bp.uri.toString() === uri.toString()).pop(); + if (toRemove) { + debugService.removeBreakpoints(toRemove.getId()); + } + oneTimeListener.dispose(); + } + }); + + const bpExists = !!(debugService.getModel().getBreakpoints().filter(bp => bp.column === position.column && bp.lineNumber === position.lineNumber && bp.uri.toString() === uri.toString()).pop()); + return (bpExists ? TPromise.as(null) : debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column: position.column }])).then(() => { + debugService.getViewModel().focusedThread.continue(); + }); + } +} + +@editorAction +class SelectionToReplAction extends EditorAction { + + constructor() { + super({ + id: 'editor.debug.action.selectionToRepl', + label: nls.localize('debugEvaluate', "Debug: Evaluate"), + alias: 'Debug: Evaluate', + precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL), + menuOpts: { + group: 'debug', + order: 0 + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + const debugService = accessor.get(IDebugService); + const panelService = accessor.get(IPanelService); + + const text = editor.getModel().getValueInRange(editor.getSelection()); + return debugService.addReplExpression(text) + .then(() => panelService.openPanel(REPL_ID, true)) + .then(_ => void 0); + } +} + +@editorAction +class SelectionToWatchExpressionsAction extends EditorAction { + + constructor() { + super({ + id: 'editor.debug.action.selectionToWatch', + label: nls.localize('debugAddToWatch', "Debug: Add to Watch"), + alias: 'Debug: Add to Watch', + precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL), + menuOpts: { + group: 'debug', + order: 1 + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + const debugService = accessor.get(IDebugService); + const viewletService = accessor.get(IViewletService); + + const text = editor.getModel().getValueInRange(editor.getSelection()); + return viewletService.openViewlet(VIEWLET_ID).then(() => debugService.addWatchExpression(text)); + } +} + +@editorAction +class ShowDebugHoverAction extends EditorAction { + + constructor() { + super({ + id: 'editor.debug.action.showDebugHover', + label: nls.localize('showDebugHover', "Debug: Show Hover"), + alias: 'Debug: Show Hover', + precondition: CONTEXT_IN_DEBUG_MODE, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_I) + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + const position = editor.getPosition(); + const word = editor.getModel().getWordAtPosition(position); + if (!word) { + return TPromise.as(null); + } + + const range = new Range(position.lineNumber, position.column, position.lineNumber, word.endColumn); + return editor.getContribution(EDITOR_CONTRIBUTION_ID).showHover(range, true); + } +} + +class CloseBreakpointWidgetCommand extends EditorCommand { + + constructor() { + super({ + id: 'closeBreakpointWidget', + precondition: CONTEXT_BREAKPOINT_WIDGET_VISIBLE, + kbOpts: { + weight: CommonEditorRegistry.commandWeight(8), + kbExpr: EditorContextKeys.focus, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } + }); + } + + public runEditorCommand(accessor: ServicesAccessor, editor: ICommonCodeEditor, args: any): void { + return editor.getContribution(EDITOR_CONTRIBUTION_ID).closeBreakpointWidget(); + } +} + +CommonEditorRegistry.registerEditorCommand(new CloseBreakpointWidgetCommand()); diff --git a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts new file mode 100644 index 0000000000..987246a53b --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts @@ -0,0 +1,387 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as objects from 'vs/base/common/objects'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import { Constants } from 'vs/editor/common/core/uint'; +import { Range } from 'vs/editor/common/core/range'; +import { IModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/editorCommon'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IDebugService, IBreakpoint, IRawBreakpoint, State } from 'vs/workbench/parts/debug/common/debug'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import { MarkdownString } from 'vs/base/common/htmlContent'; + +interface IDebugEditorModelData { + model: IModel; + toDispose: lifecycle.IDisposable[]; + breakpointDecorationIds: string[]; + breakpointLines: number[]; + breakpointDecorationsAsMap: Map; + currentStackDecorations: string[]; + dirty: boolean; + topStackFrameRange: Range; +} + +const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; + +export class DebugEditorModelManager implements IWorkbenchContribution { + static ID = 'breakpointManager'; + + private modelDataMap: Map; + private toDispose: lifecycle.IDisposable[]; + + constructor( + @IModelService private modelService: IModelService, + @IDebugService private debugService: IDebugService + ) { + this.modelDataMap = new Map(); + this.toDispose = []; + this.registerListeners(); + } + + public getId(): string { + return DebugEditorModelManager.ID; + } + + public dispose(): void { + this.modelDataMap.forEach(modelData => { + lifecycle.dispose(modelData.toDispose); + modelData.model.deltaDecorations(modelData.breakpointDecorationIds, []); + modelData.model.deltaDecorations(modelData.currentStackDecorations, []); + }); + this.toDispose = lifecycle.dispose(this.toDispose); + + this.modelDataMap.clear(); + } + + private registerListeners(): void { + this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this)); + this.modelService.getModels().forEach(model => this.onModelAdded(model)); + this.toDispose.push(this.modelService.onModelRemoved(this.onModelRemoved, this)); + + this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange())); + this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => this.onFocusStackFrame())); + this.toDispose.push(this.debugService.onDidChangeState(state => { + if (state === State.Inactive) { + this.modelDataMap.forEach(modelData => { + modelData.dirty = false; + modelData.topStackFrameRange = undefined; + }); + } + })); + } + + private onModelAdded(model: IModel): void { + const modelUrlStr = model.uri.toString(); + const breakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp.uri.toString() === modelUrlStr); + + const currentStackDecorations = model.deltaDecorations([], this.createCallStackDecorations(modelUrlStr)); + const breakPointDecorations = model.deltaDecorations([], this.createBreakpointDecorations(breakpoints)); + + const toDispose: lifecycle.IDisposable[] = [model.onDidChangeDecorations((e) => this.onModelDecorationsChanged(modelUrlStr, e))]; + const breakpointDecorationsAsMap = new Map(); + breakPointDecorations.forEach(bpd => breakpointDecorationsAsMap.set(bpd, true)); + + this.modelDataMap.set(modelUrlStr, { + model: model, + toDispose: toDispose, + breakpointDecorationIds: breakPointDecorations, + breakpointLines: breakpoints.map(bp => bp.lineNumber), + breakpointDecorationsAsMap, + currentStackDecorations: currentStackDecorations, + dirty: false, + topStackFrameRange: undefined + }); + } + + private onModelRemoved(model: IModel): void { + const modelUriStr = model.uri.toString(); + if (this.modelDataMap.has(modelUriStr)) { + lifecycle.dispose(this.modelDataMap.get(modelUriStr).toDispose); + this.modelDataMap.delete(modelUriStr); + } + } + + // call stack management. Represent data coming from the debug service. + + private onFocusStackFrame(): void { + this.modelDataMap.forEach((modelData, uri) => { + modelData.currentStackDecorations = modelData.model.deltaDecorations(modelData.currentStackDecorations, this.createCallStackDecorations(uri)); + }); + } + + private createCallStackDecorations(modelUriStr: string): IModelDeltaDecoration[] { + const result: IModelDeltaDecoration[] = []; + const stackFrame = this.debugService.getViewModel().focusedStackFrame; + if (!stackFrame || stackFrame.source.uri.toString() !== modelUriStr) { + return result; + } + + // only show decorations for the currently focused thread. + const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER); + const range = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1); + + // compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame, + // an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line). + const callStack = stackFrame.thread.getCallStack(); + if (callStack && callStack.length && stackFrame === callStack[0]) { + result.push({ + options: DebugEditorModelManager.TOP_STACK_FRAME_MARGIN, + range + }); + + if (stackFrame.thread.stoppedDetails && stackFrame.thread.stoppedDetails.reason === 'exception') { + result.push({ + options: DebugEditorModelManager.TOP_STACK_FRAME_EXCEPTION_DECORATION, + range: columnUntilEOLRange + }); + } else { + result.push({ + options: DebugEditorModelManager.TOP_STACK_FRAME_DECORATION, + range: columnUntilEOLRange + }); + if (stackFrame.range.endLineNumber && stackFrame.range.endColumn) { + result.push({ + options: { className: 'debug-top-stack-frame-range' }, + range: stackFrame.range + }); + } + + if (this.modelDataMap.has(modelUriStr)) { + const modelData = this.modelDataMap.get(modelUriStr); + if (modelData.topStackFrameRange && modelData.topStackFrameRange.startLineNumber === stackFrame.range.startLineNumber && modelData.topStackFrameRange.startColumn !== stackFrame.range.startColumn) { + result.push({ + options: DebugEditorModelManager.TOP_STACK_FRAME_INLINE_DECORATION, + range: columnUntilEOLRange + }); + } + modelData.topStackFrameRange = columnUntilEOLRange; + } + } + } else { + result.push({ + options: DebugEditorModelManager.FOCUSED_STACK_FRAME_MARGIN, + range + }); + if (stackFrame.range.endLineNumber && stackFrame.range.endColumn) { + result.push({ + options: { className: 'debug-focused-stack-frame-range' }, + range: stackFrame.range + }); + } + + result.push({ + options: DebugEditorModelManager.FOCUSED_STACK_FRAME_DECORATION, + range: columnUntilEOLRange + }); + } + + return result; + } + + // breakpoints management. Represent data coming from the debug service and also send data back. + private onModelDecorationsChanged(modelUrlStr: string, e: IModelDecorationsChangedEvent): void { + const modelData = this.modelDataMap.get(modelUrlStr); + if (modelData.breakpointDecorationsAsMap.size === 0) { + // I have no decorations + return; + } + if (!e.changedDecorations.some(decorationId => modelData.breakpointDecorationsAsMap.has(decorationId))) { + // nothing to do, my decorations did not change. + return; + } + + const data: IRawBreakpoint[] = []; + + const lineToBreakpointDataMap = new Map(); + this.debugService.getModel().getBreakpoints().filter(bp => bp.uri.toString() === modelUrlStr).forEach(bp => { + lineToBreakpointDataMap.set(bp.lineNumber, bp); + }); + + const modelUri = modelData.model.uri; + for (let i = 0, len = modelData.breakpointDecorationIds.length; i < len; i++) { + const decorationRange = modelData.model.getDecorationRange(modelData.breakpointDecorationIds[i]); + const lineNumber = modelData.breakpointLines[i]; + // check if the line got deleted. + if (decorationRange.endColumn - decorationRange.startColumn > 0) { + const breakpoint = lineToBreakpointDataMap.get(lineNumber); + // since we know it is collapsed, it cannot grow to multiple lines + data.push({ + lineNumber: decorationRange.startLineNumber, + enabled: breakpoint.enabled, + condition: breakpoint.condition, + hitCondition: breakpoint.hitCondition, + column: breakpoint.column ? decorationRange.startColumn : undefined + }); + } + } + modelData.dirty = this.debugService.state !== State.Inactive; + + const toRemove = this.debugService.getModel().getBreakpoints() + .filter(bp => bp.uri.toString() === modelUri.toString()); + + TPromise.join(toRemove.map(bp => this.debugService.removeBreakpoints(bp.getId()))).then(() => { + this.debugService.addBreakpoints(modelUri, data); + }); + } + + private onBreakpointsChange(): void { + const breakpointsMap = new Map(); + this.debugService.getModel().getBreakpoints().forEach(bp => { + const uriStr = bp.uri.toString(); + if (breakpointsMap.has(uriStr)) { + breakpointsMap.get(uriStr).push(bp); + } else { + breakpointsMap.set(uriStr, [bp]); + } + }); + + breakpointsMap.forEach((bps, uri) => { + if (this.modelDataMap.has(uri)) { + this.updateBreakpoints(this.modelDataMap.get(uri), breakpointsMap.get(uri)); + } + }); + this.modelDataMap.forEach((modelData, uri) => { + if (!breakpointsMap.has(uri)) { + this.updateBreakpoints(modelData, []); + } + }); + } + + private updateBreakpoints(modelData: IDebugEditorModelData, newBreakpoints: IBreakpoint[]): void { + modelData.breakpointDecorationIds = modelData.model.deltaDecorations(modelData.breakpointDecorationIds, this.createBreakpointDecorations(newBreakpoints)); + modelData.breakpointDecorationsAsMap.clear(); + modelData.breakpointDecorationIds.forEach(id => modelData.breakpointDecorationsAsMap.set(id, true)); + modelData.breakpointLines = newBreakpoints.map(bp => bp.lineNumber); + } + + private createBreakpointDecorations(breakpoints: IBreakpoint[]): IModelDeltaDecoration[] { + return breakpoints.map((breakpoint) => { + const range = breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1) + : new Range(breakpoint.lineNumber, 1, breakpoint.lineNumber, Constants.MAX_SAFE_SMALL_INTEGER); // Decoration has to have a width #20688 + return { + options: this.getBreakpointDecorationOptions(breakpoint), + range + }; + }); + } + + private getBreakpointDecorationOptions(breakpoint: IBreakpoint): IModelDecorationOptions { + const activated = this.debugService.getModel().areBreakpointsActivated(); + const state = this.debugService.state; + const debugActive = state === State.Running || state === State.Stopped || state === State.Initializing; + const modelData = this.modelDataMap.get(breakpoint.uri.toString()); + + let result = (!breakpoint.enabled || !activated) ? DebugEditorModelManager.BREAKPOINT_DISABLED_DECORATION : + debugActive && modelData && modelData.dirty && !breakpoint.verified ? DebugEditorModelManager.BREAKPOINT_DIRTY_DECORATION : + debugActive && !breakpoint.verified ? DebugEditorModelManager.BREAKPOINT_UNVERIFIED_DECORATION : + !breakpoint.condition && !breakpoint.hitCondition ? DebugEditorModelManager.BREAKPOINT_DECORATION : null; + + if (result) { + result = objects.clone(result); + if (breakpoint.message) { + result.glyphMarginHoverMessage = new MarkdownString().appendText(breakpoint.message); + } + if (breakpoint.column) { + result.beforeContentClassName = `debug-breakpoint-column ${result.glyphMarginClassName}-column`; + } + + return result; + } + + const process = this.debugService.getViewModel().focusedProcess; + if (process && !process.session.capabilities.supportsConditionalBreakpoints) { + return DebugEditorModelManager.BREAKPOINT_UNSUPPORTED_DECORATION; + } + + const modeId = modelData ? modelData.model.getLanguageIdentifier().language : ''; + let condition: string; + if (breakpoint.condition && breakpoint.hitCondition) { + condition = `Expression: ${breakpoint.condition}\nHitCount: ${breakpoint.hitCondition}`; + } else { + condition = breakpoint.condition ? breakpoint.condition : breakpoint.hitCondition; + } + const glyphMarginHoverMessage = new MarkdownString().appendCodeblock(modeId, condition); + const glyphMarginClassName = 'debug-breakpoint-conditional-glyph'; + const beforeContentClassName = breakpoint.column ? `debug-breakpoint-column ${glyphMarginClassName}-column` : undefined; + + return { + glyphMarginClassName, + glyphMarginHoverMessage, + stickiness, + beforeContentClassName + }; + } + + // editor decorations + + private static BREAKPOINT_DECORATION: IModelDecorationOptions = { + glyphMarginClassName: 'debug-breakpoint-glyph', + stickiness + }; + + private static BREAKPOINT_DISABLED_DECORATION: IModelDecorationOptions = { + glyphMarginClassName: 'debug-breakpoint-disabled-glyph', + glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointDisabledHover', "Disabled Breakpoint")), + stickiness + }; + + private static BREAKPOINT_UNVERIFIED_DECORATION: IModelDecorationOptions = { + glyphMarginClassName: 'debug-breakpoint-unverified-glyph', + glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointUnverifieddHover', "Unverified Breakpoint")), + stickiness + }; + + private static BREAKPOINT_DIRTY_DECORATION: IModelDecorationOptions = { + glyphMarginClassName: 'debug-breakpoint-unverified-glyph', + glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointDirtydHover', "Unverified breakpoint. File is modified, please restart debug session.")), + stickiness + }; + + private static BREAKPOINT_UNSUPPORTED_DECORATION: IModelDecorationOptions = { + glyphMarginClassName: 'debug-breakpoint-unsupported-glyph', + glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointUnsupported', "Conditional breakpoints not supported by this debug type")), + stickiness + }; + + // we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement. + private static TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = { + glyphMarginClassName: 'debug-top-stack-frame-glyph', + stickiness + }; + + private static FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = { + glyphMarginClassName: 'debug-focused-stack-frame-glyph', + stickiness + }; + + private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = { + isWholeLine: true, + inlineClassName: 'debug-remove-token-colors', + className: 'debug-top-stack-frame-line', + stickiness + }; + + private static TOP_STACK_FRAME_EXCEPTION_DECORATION: IModelDecorationOptions = { + isWholeLine: true, + inlineClassName: 'debug-remove-token-colors', + className: 'debug-top-stack-frame-exception-line', + stickiness + }; + + private static TOP_STACK_FRAME_INLINE_DECORATION: IModelDecorationOptions = { + beforeContentClassName: 'debug-top-stack-frame-column' + }; + + private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = { + isWholeLine: true, + inlineClassName: 'debug-remove-token-colors', + className: 'debug-focused-stack-frame-line', + stickiness + }; +} diff --git a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts new file mode 100644 index 0000000000..8f6a4aa4f7 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import Filters = require('vs/base/common/filters'); +import { TPromise } from 'vs/base/common/winjs.base'; +import Quickopen = require('vs/workbench/browser/quickopen'); +import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen'); +import Model = require('vs/base/parts/quickopen/browser/quickOpenModel'); +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { IDebugService, ILaunch } from 'vs/workbench/parts/debug/common/debug'; +import * as errors from 'vs/base/common/errors'; + +class DebugEntry extends Model.QuickOpenEntry { + + constructor(private debugService: IDebugService, private launch: ILaunch, private configurationName: string, highlights: Model.IHighlight[] = []) { + super(highlights); + } + + public getLabel(): string { + return this.debugService.getConfigurationManager().getLaunches().length <= 1 ? this.configurationName : `${this.configurationName} (${this.launch.name})`; + } + + public getAriaLabel(): string { + return nls.localize('entryAriaLabel', "{0}, debug", this.getLabel()); + } + + public run(mode: QuickOpen.Mode, context: Model.IContext): boolean { + if (mode === QuickOpen.Mode.PREVIEW) { + return false; + } + // Run selected debug configuration + this.debugService.getConfigurationManager().selectConfiguration(this.launch, this.configurationName); + this.debugService.startDebugging(this.launch.workspaceUri).done(undefined, errors.onUnexpectedError); + + return true; + } +} + +export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler { + + constructor( + @IQuickOpenService private quickOpenService: IQuickOpenService, + @IDebugService private debugService: IDebugService + ) { + super(); + } + + public getAriaLabel(): string { + return nls.localize('debugAriaLabel', "Type a name of a launch configuration to run."); + } + + public getResults(input: string): TPromise { + const configurations: DebugEntry[] = []; + + for (let launch of this.debugService.getConfigurationManager().getLaunches()) { + launch.getConfigurationNames().map(config => ({ config: config, highlights: Filters.matchesContiguousSubString(input, config) })) + .filter(({ highlights }) => !!highlights) + .forEach(({ config, highlights }) => configurations.push(new DebugEntry(this.debugService, launch, config, highlights))); + } + + return TPromise.as(new Model.QuickOpenModel(configurations)); + } + + public getAutoFocus(input: string): QuickOpen.IAutoFocus { + return { + autoFocusFirstEntry: !!input + }; + } + + public getEmptyLabel(searchString: string): string { + if (searchString.length > 0) { + return nls.localize('noConfigurationsMatching', "No debug configurations matching"); + } + + return nls.localize('noConfigurationsFound', "No debug configurations found. Please create a 'launch.json' file."); + } +} diff --git a/src/vs/workbench/parts/debug/browser/debugViewlet.ts b/src/vs/workbench/parts/debug/browser/debugViewlet.ts new file mode 100644 index 0000000000..d67a0976a6 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/debugViewlet.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/debugViewlet'; +import { Builder } from 'vs/base/browser/builder'; +import * as DOM from 'vs/base/browser/dom'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction } from 'vs/base/common/actions'; +import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { PersistentViewsViewlet } from 'vs/workbench/parts/views/browser/views'; +import { IDebugService, VIEWLET_ID, State } from 'vs/workbench/parts/debug/common/debug'; +import { StartAction, ToggleReplAction, ConfigureAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { StartDebugActionItem } from 'vs/workbench/parts/debug/browser/debugActionItems'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ViewLocation } from 'vs/workbench/parts/views/browser/viewsRegistry'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; + +export class DebugViewlet extends PersistentViewsViewlet { + + private actions: IAction[]; + private startDebugActionItem: StartDebugActionItem; + private progressRunner: IProgressRunner; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IProgressService private progressService: IProgressService, + @IDebugService private debugService: IDebugService, + @IInstantiationService instantiationService: IInstantiationService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IStorageService storageService: IStorageService, + @IThemeService themeService: IThemeService, + @IContextKeyService contextKeyService: IContextKeyService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService + ) { + super(VIEWLET_ID, ViewLocation.Debug, `${VIEWLET_ID}.state`, false, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); + + this.progressRunner = null; + + this._register(this.debugService.onDidChangeState(state => this.onDebugServiceStateChange(state))); + } + + public create(parent: Builder): TPromise { + return super.create(parent).then(() => DOM.addClass(this.viewletContainer, 'debug-viewlet')); + } + + public focus(): void { + super.focus(); + + if (!this.contextService.hasWorkspace()) { + this.views[0].focusBody(); + } + + if (this.startDebugActionItem) { + this.startDebugActionItem.focus(); + } + } + + public getActions(): IAction[] { + if (!this.actions) { + this.actions = []; + this.actions.push(this.instantiationService.createInstance(StartAction, StartAction.ID, StartAction.LABEL)); + if (this.contextService.hasWorkspace()) { + this.actions.push(this.instantiationService.createInstance(ConfigureAction, ConfigureAction.ID, ConfigureAction.LABEL)); + } + this.actions.push(this._register(this.instantiationService.createInstance(ToggleReplAction, ToggleReplAction.ID, ToggleReplAction.LABEL))); + } + + return this.actions; + } + + public getSecondaryActions(): IAction[] { + return []; + } + + public getActionItem(action: IAction): IActionItem { + if (action.id === StartAction.ID && this.contextService.hasWorkspace()) { + this.startDebugActionItem = this.instantiationService.createInstance(StartDebugActionItem, null, action); + return this.startDebugActionItem; + } + + return null; + } + + private onDebugServiceStateChange(state: State): void { + if (this.progressRunner) { + this.progressRunner.done(); + } + + if (state === State.Initializing) { + this.progressRunner = this.progressService.show(true); + } else { + this.progressRunner = null; + } + } +} diff --git a/src/vs/workbench/parts/debug/browser/exceptionWidget.ts b/src/vs/workbench/parts/debug/browser/exceptionWidget.ts new file mode 100644 index 0000000000..374c838a0b --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/exceptionWidget.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * 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!../browser/media/exceptionWidget'; +import * as nls from 'vs/nls'; +import * as dom from 'vs/base/browser/dom'; +import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IDebugService, IExceptionInfo } from 'vs/workbench/parts/debug/common/debug'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; +import { Color } from 'vs/base/common/color'; +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; +const $ = dom.$; + +// theming + +export const debugExceptionWidgetBorder = registerColor('debugExceptionWidget.border', { dark: '#a31515', light: '#a31515', hc: '#a31515' }, nls.localize('debugExceptionWidgetBorder', 'Exception widget border color.')); +export const debugExceptionWidgetBackground = registerColor('debugExceptionWidget.background', { dark: '#a3151540', light: '#a315150d', hc: '#a3151573' }, nls.localize('debugExceptionWidgetBackground', 'Exception widget background color.')); + +export class ExceptionWidget extends ZoneWidget { + + private _backgroundColor: Color; + + constructor(editor: ICodeEditor, private exceptionInfo: IExceptionInfo, private lineNumber: number, + @IContextViewService private contextViewService: IContextViewService, + @IDebugService private debugService: IDebugService, + @IThemeService themeService: IThemeService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(editor, { showFrame: true, showArrow: true, frameWidth: 1, className: 'exception-widget-container' }); + + this._backgroundColor = Color.white; + + this._applyTheme(themeService.getTheme()); + this._disposables.push(themeService.onThemeChange(this._applyTheme.bind(this))); + + + this.create(); + const onDidLayoutChangeScheduler = new RunOnceScheduler(() => this._doLayout(undefined, undefined), 50); + this._disposables.push(this.editor.onDidLayoutChange(() => onDidLayoutChangeScheduler.schedule())); + this._disposables.push(onDidLayoutChangeScheduler); + } + + private _applyTheme(theme: ITheme): void { + this._backgroundColor = theme.getColor(debugExceptionWidgetBackground); + let frameColor = theme.getColor(debugExceptionWidgetBorder); + this.style({ + arrowColor: frameColor, + frameColor: frameColor + }); // style() will trigger _applyStyles + } + + protected _applyStyles(): void { + if (this.container) { + this.container.style.backgroundColor = this._backgroundColor.toString(); + } + super._applyStyles(); + } + + protected _fillContainer(container: HTMLElement): void { + this.setCssClass('exception-widget'); + // Set the font size and line height to the one from the editor configuration. + const fontInfo = this.editor.getConfiguration().fontInfo; + this.container.style.fontSize = `${fontInfo.fontSize}px`; + this.container.style.lineHeight = `${fontInfo.lineHeight}px`; + + let title = $('.title'); + title.textContent = this.exceptionInfo.id ? nls.localize('exceptionThrownWithId', 'Exception has occurred: {0}', this.exceptionInfo.id) : nls.localize('exceptionThrown', 'Exception has occurred.'); + dom.append(container, title); + + if (this.exceptionInfo.description) { + let description = $('.description'); + description.textContent = this.exceptionInfo.description; + dom.append(container, description); + } + + if (this.exceptionInfo.details && this.exceptionInfo.details.stackTrace) { + let stackTrace = $('.stack-trace'); + const linkDetector = this.instantiationService.createInstance(LinkDetector); + const linkedStackTrace = linkDetector.handleLinks(this.exceptionInfo.details.stackTrace); + typeof linkedStackTrace === 'string' ? stackTrace.textContent = linkedStackTrace : stackTrace.appendChild(linkedStackTrace); + dom.append(container, stackTrace); + } + } + + protected _doLayout(heightInPixel: number, widthInPixel: number): void { + // Reload the height with respect to the exception text content and relayout it to match the line count. + this.container.style.height = 'initial'; + + const lineHeight = this.editor.getConfiguration().lineHeight; + const arrowHeight = Math.round(lineHeight / 3); + const computedLinesNumber = Math.ceil((this.container.offsetHeight + arrowHeight) / lineHeight); + + this._relayout(computedLinesNumber); + } +} diff --git a/src/vs/workbench/parts/debug/browser/linkDetector.ts b/src/vs/workbench/parts/debug/browser/linkDetector.ts new file mode 100644 index 0000000000..8be9bbdf34 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/linkDetector.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import strings = require('vs/base/common/strings'); +import uri from 'vs/base/common/uri'; +import { isMacintosh } from 'vs/base/common/platform'; +import * as errors from 'vs/base/common/errors'; +import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import * as nls from 'vs/nls'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +export class LinkDetector { + private static FILE_LOCATION_PATTERNS: RegExp[] = [ + // group 0: full path with line and column + // group 1: full path without line and column, matched by `*.*` in the end to work only on paths with extensions in the end (s.t. node:10352 would not match) + // group 2: drive letter on windows with trailing backslash or leading slash on mac/linux + // group 3: line number, matched by (:(\d+)) + // group 4: column number, matched by ((?::(\d+))?) + // eg: at Context. (c:\Users\someone\Desktop\mocha-runner\test\test.js:26:11) + /(?![\(])(?:file:\/\/)?((?:([a-zA-Z]+:)|[^\(\)<>\'\"\[\]:\s]+)(?:[\\/][^\(\)<>\'\"\[\]:]*)?\.[a-zA-Z]+[0-9]*):(\d+)(?::(\d+))?/g + ]; + + constructor( + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { + // noop + } + + /** + * Matches and handles relative and absolute file links in the string provided. + * Returns element that wraps the processed string, where matched links are replaced by and unmatched parts are surrounded by elements. + * 'onclick' event is attached to all anchored links that opens them in the editor. + * If no links were detected, returns the original string. + */ + public handleLinks(text: string): HTMLElement | string { + let linkContainer: HTMLElement; + + for (let pattern of LinkDetector.FILE_LOCATION_PATTERNS) { + pattern.lastIndex = 0; // the holy grail of software development + let lastMatchIndex = 0; + + let match = pattern.exec(text); + while (match !== null) { + let resource: uri = null; + try { + resource = (match && !strings.startsWith(match[0], 'http')) + && (match[2] || strings.startsWith(match[1], '/') ? uri.file(match[1]) : this.contextService.toResource(match[1])); // TODO@Michel TODO@Isidor (https://github.com/Microsoft/vscode/issues/29190) + } catch (e) { } + + if (!resource) { + match = pattern.exec(text); + continue; + } + if (!linkContainer) { + linkContainer = document.createElement('span'); + } + + let textBeforeLink = text.substring(lastMatchIndex, match.index); + if (textBeforeLink) { + let span = document.createElement('span'); + span.textContent = textBeforeLink; + linkContainer.appendChild(span); + } + + const link = document.createElement('a'); + link.textContent = text.substr(match.index, match[0].length); + link.title = isMacintosh ? nls.localize('fileLinkMac', "Click to follow (Cmd + click opens to the side)") : nls.localize('fileLink', "Click to follow (Ctrl + click opens to the side)"); + linkContainer.appendChild(link); + const line = Number(match[3]); + const column = match[4] ? Number(match[4]) : undefined; + link.onclick = (e) => this.onLinkClick(new StandardMouseEvent(e), resource, line, column); + + lastMatchIndex = pattern.lastIndex; + const currentMatch = match; + match = pattern.exec(text); + + // Append last string part if no more link matches + if (!match) { + let textAfterLink = text.substr(currentMatch.index + currentMatch[0].length); + if (textAfterLink) { + let span = document.createElement('span'); + span.textContent = textAfterLink; + linkContainer.appendChild(span); + } + } + } + } + + return linkContainer || text; + } + + private onLinkClick(event: IMouseEvent, resource: uri, line: number, column: number = 0): void { + const selection = window.getSelection(); + if (selection.type === 'Range') { + return; // do not navigate when user is selecting + } + + event.preventDefault(); + + this.editorService.openEditor({ + resource, + options: { + selection: { + startLineNumber: line, + startColumn: column + } + } + }, event.ctrlKey || event.metaKey).done(null, errors.onUnexpectedError); + } +} diff --git a/src/vs/workbench/parts/debug/browser/media/add-focus.svg b/src/vs/workbench/parts/debug/browser/media/add-focus.svg new file mode 100644 index 0000000000..5e9f5851e8 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/add-focus.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/add-inverse.svg b/src/vs/workbench/parts/debug/browser/media/add-inverse.svg new file mode 100644 index 0000000000..3475c1e196 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/add-inverse.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/add.svg b/src/vs/workbench/parts/debug/browser/media/add.svg new file mode 100644 index 0000000000..bdecdb0e45 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/add.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-conditional-dark.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-conditional-dark.svg new file mode 100644 index 0000000000..720679e07e --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-conditional-dark.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-conditional.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-conditional.svg new file mode 100644 index 0000000000..77dd991c2b --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-conditional.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-dark.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-dark.svg new file mode 100644 index 0000000000..5ccf397efa --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled-dark.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled-dark.svg new file mode 100644 index 0000000000..bf58a18178 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled.svg new file mode 100644 index 0000000000..9780270357 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-hint.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-hint.svg new file mode 100644 index 0000000000..24f2006952 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-hint.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-unsupported-dark.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-unsupported-dark.svg new file mode 100644 index 0000000000..fffcfb8cb4 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-unsupported-dark.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-unsupported.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-unsupported.svg new file mode 100644 index 0000000000..cc0c89e1b7 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-unsupported.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified-dark.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified-dark.svg new file mode 100644 index 0000000000..4a2575a305 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified.svg new file mode 100644 index 0000000000..617e7ede7b --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint.svg new file mode 100644 index 0000000000..7629294bcc --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css b/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css new file mode 100644 index 0000000000..9359b4de9e --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .zone-widget .zone-widget-container.breakpoint-widget { + height: 30px !important; + display: flex; + border-color: #007ACC; +} + +.monaco-editor .zone-widget .zone-widget-container.breakpoint-widget .breakpoint-select-container { + display: flex; + justify-content: center; + flex-direction: column; + padding: 0 10px; +} + +.monaco-editor .zone-widget .zone-widget-container.breakpoint-widget .inputBoxContainer { + flex: 1; +} + +.monaco-editor .zone-widget .zone-widget-container.breakpoint-widget .monaco-inputbox { + border: none; +} + +.monaco-editor .breakpoint-widget .input { + font-family: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback"; + line-height: 22px; + background-color: transparent; + padding: 8px; +} + +.monaco-workbench.mac .monaco-editor .breakpoint-widget .input { + font-size: 11px; +} + +.monaco-workbench.windows .monaco-editor .breakpoint-widget .input, +.monaco-workbench.linux .monaco-editor .breakpoint-widget .input { + font-size: 13px; +} diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoints-activate-inverse.svg b/src/vs/workbench/parts/debug/browser/media/breakpoints-activate-inverse.svg new file mode 100644 index 0000000000..07344800a2 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoints-activate-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoints-activate.svg b/src/vs/workbench/parts/debug/browser/media/breakpoints-activate.svg new file mode 100644 index 0000000000..b32b1cd6de --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoints-activate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/clear-repl-inverse.svg b/src/vs/workbench/parts/debug/browser/media/clear-repl-inverse.svg new file mode 100644 index 0000000000..7df4455d7c --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/clear-repl-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/clear-repl.svg b/src/vs/workbench/parts/debug/browser/media/clear-repl.svg new file mode 100644 index 0000000000..6e4d3a1d48 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/clear-repl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/configure-inverse.svg b/src/vs/workbench/parts/debug/browser/media/configure-inverse.svg new file mode 100644 index 0000000000..61baaea2b8 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/configure-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/configure.svg b/src/vs/workbench/parts/debug/browser/media/configure.svg new file mode 100644 index 0000000000..3dec2ba50f --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/configure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/continue-inverse.svg b/src/vs/workbench/parts/debug/browser/media/continue-inverse.svg new file mode 100644 index 0000000000..f82c45c110 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/continue-inverse.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/continue.svg b/src/vs/workbench/parts/debug/browser/media/continue.svg new file mode 100644 index 0000000000..33affa1a51 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/continue.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/current-and-breakpoint.svg b/src/vs/workbench/parts/debug/browser/media/current-and-breakpoint.svg new file mode 100644 index 0000000000..7a3e6f2635 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/current-and-breakpoint.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/current-arrow.svg b/src/vs/workbench/parts/debug/browser/media/current-arrow.svg new file mode 100644 index 0000000000..bd1ca579f1 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/current-arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/debug-dark.svg b/src/vs/workbench/parts/debug/browser/media/debug-dark.svg new file mode 100644 index 0000000000..9a969fb358 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/debug-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css new file mode 100644 index 0000000000..b1506d8a34 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css @@ -0,0 +1,263 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Activity Bar */ +.monaco-workbench > .activitybar .monaco-action-bar .action-label.debug { + -webkit-mask: url('debug-dark.svg') no-repeat 50% 50%; +} + +.monaco-editor .debug-top-stack-frame-line, +.monaco-editor .debug-top-stack-frame-exception-line { + background: rgba(255, 255, 102, 0.45); +} + +.monaco-editor .debug-top-stack-frame-range { + background: #ffeca0; +} + +.monaco-editor .debug-top-stack-frame-column::before { + background: url('current-arrow.svg') center center no-repeat; +} + +.monaco-editor .debug-focused-stack-frame-line { + background: rgba(206, 231, 206, 0.45); +} + +.monaco-editor .debug-focused-stack-frame-range { + background: rgba(206, 231, 206, 1); +} + +.monaco-editor .debug-breakpoint-hint-glyph { + background: url('breakpoint-hint.svg') center center no-repeat; +} + +.monaco-editor .debug-breakpoint-disabled-glyph, +.monaco-editor .debug-breakpoint-column.debug-breakpoint-disabled-glyph-column::before { + background: url('breakpoint-disabled.svg') center center no-repeat; +} + +.monaco-editor .debug-breakpoint-unverified-glyph, +.monaco-editor .debug-breakpoint-column.debug-breakpoint-unverified-glyph-column::before { + background: url('breakpoint-unverified.svg') center center no-repeat; +} + +.monaco-editor .debug-top-stack-frame-glyph { + background: url('current-arrow.svg') center center no-repeat; +} + +.monaco-editor .debug-focused-stack-frame-glyph { + background: url('stackframe-arrow.svg') center center no-repeat; +} + +.monaco-editor .debug-breakpoint-glyph, +.monaco-editor .debug-breakpoint-column.debug-breakpoint-glyph-column::before { + background: url('breakpoint.svg') center center no-repeat; +} + +.monaco-editor .debug-breakpoint-column::before, +.monaco-editor .debug-top-stack-frame-column::before { + content: " "; + width: 0.9em; + height: 0.8em; + display: inline-block; + margin-right: 2px; + margin-left: 2px; + background-size: 110% !important; + background-position: initial !important; +} + +.monaco-editor .debug-breakpoint-conditional-glyph, +.monaco-editor .debug-breakpoint-column.debug-breakpoint-conditional-glyph-column::before { + background: url('breakpoint-conditional.svg') center center no-repeat; +} + +.monaco-editor .debug-breakpoint-unsupported-glyph, +.monaco-editor .debug-breakpoint-column.debug-breakpoint-unsupported-glyph-column::before { + background: url('breakpoint-unsupported.svg') center center no-repeat; +} + +.monaco-editor .debug-top-stack-frame-glyph.debug-breakpoint-glyph, +.monaco-editor .debug-top-stack-frame-glyph.debug-breakpoint-conditional-glyph, +.monaco-editor .debug-breakpoint-column.debug-breakpoint-glyph-column.debug-top-stack-frame-column::before, +.monaco-editor.vs-dark .debug-top-stack-frame-glyph.debug-breakpoint-glyph, +.monaco-editor.vs-dark .debug-top-stack-frame-glyph.debug-breakpoint-conditional-glyph, +.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-glyph-column.debug-top-stack-frame-column::before { + background: url('current-and-breakpoint.svg') center center no-repeat; +} + +.monaco-editor .debug-focused-stack-frame-glyph.debug-breakpoint-glyph, +.monaco-editor .debug-focused-stack-frame-glyph.debug-breakpoint-conditional-glyph { + background: url('stackframe-and-breakpoint.svg') center center no-repeat; +} + +/* Error editor */ +.debug-error-editor:focus { + outline: none !important; +} + +.debug-error-editor { + padding: 5px 0 0 10px; + box-sizing: border-box; +} + +/* Expressions */ + +.monaco-workbench .monaco-tree-row .expression { + overflow: hidden; + text-overflow: ellipsis; + font-family: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback"; +} + +.monaco-workbench.mac .monaco-tree-row .expression { + font-size: 11px; +} + +.monaco-workbench.windows .monaco-tree-row .expression, +.monaco-workbench.linux .monaco-tree-row .expression { + font-size: 13px; +} + +.monaco-workbench .monaco-tree-row .expression .value { + margin-left: 6px; +} + +.monaco-workbench .monaco-tree-row:not(.selected) .expression .name { + color: #9B46B0; +} + +.monaco-workbench > .monaco-tree-row:not(.selected) .expression .value { + color: rgba(108, 108, 108, 0.8); +} + +.monaco-workbench .monaco-tree-row .expression .unavailable { + font-style: italic; +} + +.monaco-workbench .monaco-tree-row:not(.selected) .expression .error { + color: #E51400; +} + +.monaco-workbench .monaco-tree-row:not(.selected) .expression .value.number { + color: #09885A; +} + +.monaco-workbench .monaco-tree-row:not(.selected) .expression .value.boolean { + color: #0000FF; +} + +.monaco-workbench .monaco-tree-row:not(.selected) .expression .value.string { + color: #A31515; +} + +.vs-dark .monaco-workbench > .monaco-tree-row:not(.selected) .expression .value { + color: rgba(204, 204, 204, 0.6); +} + +.vs-dark .monaco-workbench .monaco-tree-row:not(.selected) .expression .error { + color: #F48771; +} + +.vs-dark .monaco-workbench .monaco-tree-row:not(.selected) .expression .value.number { + color: #B5CEA8; +} + +.hc-black .monaco-workbench .monaco-tree-row:not(.selected) .expression .value.number { + color: #89d185; +} + +.hc-black .monaco-workbench .monaco-tree-row:not(.selected) .expression .value.boolean { + color: #75bdfe; +} + +.hc-black .monaco-workbench .monaco-tree-row:not(.selected) .expression .value.string { + color: #f48771; +} + +.vs-dark .monaco-workbench .monaco-tree-row:not(.selected) .expression .value.boolean { + color: #4E94CE; +} + +.vs-dark .monaco-workbench .monaco-tree-row:not(.selected) .expression .value.string { + color: #CE9178; +} + +.hc-black .monaco-workbench .monaco-tree-row:not(.selected) .expression .error { + color: #F48771; +} + +/* Dark theme */ + +.vs-dark .monaco-workbench .monaco-tree-row:not(.selected) .expression .name { + color: #C586C0; +} + +.monaco-editor.vs-dark .debug-focused-stack-frame-line { + background: rgba(122, 189, 122, 0.3); +} + +.monaco-editor.vs-dark .debug-focused-stack-frame-range { + background: rgba(122, 189, 122, 0.5); +} + +.monaco-editor.vs-dark .debug-top-stack-frame-line, +.monaco-editor.vs-dark .debug-top-stack-frame-exception-line { + background-color: rgba(255, 255, 0, 0.2) +} + +.monaco-editor.vs-dark .debug-top-stack-frame-range { + background-color: rgba(255, 255, 0, 0.3) +} + +.monaco-editor.vs-dark .debug-breakpoint-glyph, +.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-glyph-column::before { + background: url('breakpoint-dark.svg') center center no-repeat; +} + +.monaco-editor.vs-dark .debug-breakpoint-conditional-glyph, +.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-conditional-glyph-column::before { + background: url('breakpoint-conditional-dark.svg') center center no-repeat; +} + +.monaco-editor.vs-dark .debug-breakpoint-unsupported-glyph, +.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-unsupported-glyph-column::before { + background: url('breakpoint-unsupported-dark.svg') center center no-repeat; +} + +.monaco-editor.vs-dark .debug-breakpoint-disabled-glyph, +.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-disabled-glyph-column::before { + background: url('breakpoint-disabled-dark.svg') center center no-repeat; +} + +.monaco-editor.vs-dark .debug-breakpoint-unverified-glyph, +.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-unverified-glyph-column::before { + background: url('breakpoint-unverified-dark.svg') center center no-repeat; +} + +.monaco-editor.vs-dark .debug-focused-stack-frame-glyph { + background: url('stackframe-arrow-dark.svg') center center no-repeat; +} + +.monaco-editor.vs-dark .debug-focused-stack-frame-glyph.debug-breakpoint-glyph, +.monaco-editor.vs-dark .debug-focused-stack-frame-glyph.debug-breakpoint-conditional-glyph { + background: url('stackframe-and-breakpoint-dark.svg') center center no-repeat; +} + +/* High Contrast Theming */ + +.monaco-editor.hc-black .debug-focused-stack-frame-line { + background: rgba(206, 231, 206, 1); +} + +.hc-black .monaco-workbench .monaco-tree-row:not(.selected) .expression .name { + color: inherit; +} + +.hc-black .monaco-editor .debug-top-stack-frame-line { + background: rgba(255, 246, 0, 1); +} + +.hc-black .monaco-editor .debug-remove-token-colors { + color:black; +} diff --git a/src/vs/workbench/parts/debug/browser/media/debugActionsWidget.css b/src/vs/workbench/parts/debug/browser/media/debugActionsWidget.css new file mode 100644 index 0000000000..3d473236e0 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/debugActionsWidget.css @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Debug actions widget */ + +.monaco-workbench .debug-actions-widget { + position: absolute; + z-index: 200; + height: 32px; + display: flex; + padding-left: 7px; +} + +.monaco-workbench .debug-actions-widget .monaco-action-bar .action-item.select-container { + margin-right: 7px; +} + +.monaco-workbench .debug-actions-widget .monaco-action-bar .action-item .select-box { + margin-top: 6px; +} + +.monaco-workbench .debug-actions-widget .drag-area { + cursor: -webkit-grab; + height: 32px; + width: 10px; + background: url('drag.svg') center center no-repeat; +} + +.monaco-workbench .debug-actions-widget .drag-area.dragged { + cursor: -webkit-grabbing; +} + +.monaco-workbench .debug-actions-widget .monaco-action-bar .action-item > .action-label { + width: 32px; + height: 32px; + margin-right: 0; + background-size: 16px; + background-position: center center; + background-repeat: no-repeat; +} + +/* Debug actionbar actions */ + +.monaco-workbench .debug-actions-widget .debug-action.step-over, +.monaco-workbench .debug-actions-widget .debug-action.step-back { + background-image: url('step-over.svg'); +} + +.monaco-workbench .debug-actions-widget .debug-action.step-into { + background-image: url('step-into.svg'); +} + +.monaco-workbench .debug-actions-widget .debug-action.step-out { + background-image: url('step-out.svg'); +} + +.monaco-workbench .debug-actions-widget .debug-action.step-back, +.monaco-workbench .debug-actions-widget .debug-action.reverse-continue { + transform: scaleX(-1); +} + +.monaco-workbench .debug-actions-widget .debug-action.continue, +.monaco-workbench .debug-actions-widget .debug-action.reverse-continue { + background-image: url('continue.svg'); +} + +.monaco-workbench .debug-actions-widget .debug-action.restart { + background-image: url('restart.svg'); +} + +.monaco-workbench .debug-actions-widget .debug-action.pause { + background-image: url('pause.svg'); +} + +.monaco-workbench .debug-actions-widget .debug-action.stop { + background-image: url('stop.svg'); +} + +.monaco-workbench .debug-actions-widget .debug-action.disconnect { + background-image: url('disconnect.svg'); +} + +/* Dark and hc theme actions */ + +.vs-dark .monaco-workbench .debug-actions-widget .debug-action.step-over, +.vs-dark .monaco-workbench .debug-actions-widget .debug-action.step-back, +.hc-black .monaco-workbench .debug-actions-widget .debug-action.step-over, +.hc-black .monaco-workbench .debug-actions-widget .debug-action.step-back { + background-image: url('step-over-inverse.svg'); +} + +.vs-dark .monaco-workbench .debug-actions-widget .debug-action.step-into, +.hc-black .monaco-workbench .debug-actions-widget .debug-action.step-into { + background-image: url('step-into-inverse.svg'); +} + +.vs-dark .monaco-workbench .debug-actions-widget .debug-action.step-out, +.hc-black .monaco-workbench .debug-actions-widget .debug-action.step-out { + background-image: url('step-out-inverse.svg'); +} + +.vs-dark .monaco-workbench .debug-actions-widget .debug-action.continue, +.vs-dark .monaco-workbench .debug-actions-widget .debug-action.reverse-continue, +.hc-black .monaco-workbench .debug-actions-widget .debug-action.continue, +.hc-black .monaco-workbench .debug-actions-widget .debug-action.reverse-continue { + background-image: url('continue-inverse.svg'); +} + +.vs-dark .monaco-workbench .debug-actions-widget .debug-action.restart, +.hc-black .monaco-workbench .debug-actions-widget .debug-action.restart { + background-image: url('restart-inverse.svg'); +} + +.vs-dark .monaco-workbench .debug-actions-widget .debug-action.pause, +.hc-black .monaco-workbench .debug-actions-widget .debug-action.pause { + background-image: url('pause-inverse.svg'); +} + +.vs-dark .monaco-workbench .debug-actions-widget .debug-action.stop, +.hc-black .monaco-workbench .debug-actions-widget .debug-action.stop { + background-image: url('stop-inverse.svg'); +} + +.vs-dark .monaco-workbench .debug-actions-widget .debug-action.disconnect, +.hc-black .monaco-workbench .debug-actions-widget .debug-action.disconnect { + background-image: url('disconnect-inverse.svg'); +} diff --git a/src/vs/workbench/parts/debug/browser/media/debugHover.css b/src/vs/workbench/parts/debug/browser/media/debugHover.css new file mode 100644 index 0000000000..461ac769fa --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/debugHover.css @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .debug-hover-widget { + position: absolute; + margin-top: -1px; + cursor: default; + z-index: 50; + animation-duration: 0.15s; + animation-name: fadeIn; + -webkit-user-select: text; + word-break: break-all; + padding: 4px 5px; +} + +.monaco-editor .debug-hover-widget .complex-value { + width: 324px; +} + +.monaco-editor .debug-hover-widget .complex-value .title { + padding-left: 15px; + padding-right: 2px; + font-size: 11px; + word-break: normal; + text-overflow: ellipsis; + height: 18px; + overflow: hidden; + border-bottom: 1px solid rgba(128, 128, 128, 0.35); +} + +.monaco-editor .debug-hover-widget .debug-hover-tree { + line-height: 18px; +} + +.monaco-editor .debug-hover-widget .debug-hover-tree .monaco-tree .monaco-tree-row > .content { + -webkit-user-select: text; + white-space: pre; +} + +/* Disable tree highlight in debug hover tree. */ +.monaco-editor .debug-hover-widget .debug-hover-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { + background-color: inherit; +} + +.monaco-editor .debug-hover-widget .debug-hover-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row { + cursor: default; +} + +.monaco-editor .debug-hover-widget .debug-hover-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row.has-children { + cursor: pointer; +} + +.monaco-editor .debug-hover-widget pre { + margin-top: 0; + margin-bottom: 0; +} + +.monaco-editor .debugHoverHighlight { + background-color: rgba(173, 214, 255, 0.15); +} + +.monaco-editor .debug-hover-widget .value { + white-space: pre-wrap; + color: rgba(108, 108, 108, 0.8); + overflow: auto; + max-height: 500px; +} + +.monaco-editor .debug-hover-widget .error { + color: #E51400; +} + +.monaco-editor .debug-hover-widget .value.number { + color: #09885A; +} + +.monaco-editor .debug-hover-widget .value.boolean { + color: #0000FF; +} + +.monaco-editor .debug-hover-widget .value.string { + color: #A31515; +} + +/* Dark theme */ + +.monaco-editor.vs-dark .debug-hover-widget .value, +.monaco-editor.hc-black .debug-hover-widget .value { + color: rgba(204, 204, 204, 0.6); +} + +.monaco-editor.vs-dark .debug-hover-widget .error, +.monaco-editor.hc-black .debug-hover-widget .error { + color: #F48771; +} + +.monaco-editor.vs-dark .debug-hover-widget .value.number, +.monaco-editor.hc-black .debug-hover-widget .value.number { + color: #B5CEA8; +} + +.monaco-editor.vs-dark .debug-hover-widget .value.boolean, +.monaco-editor.hc-black .debug-hover-widget .value.boolean { + color: #4E94CE; +} + +.monaco-editor.vs-dark .debug-hover-widget .value.string, +.monaco-editor.hc-black .debug-hover-widget .value.string { + color: #CE9178; +} + +.monaco-editor.vs-dark .debugHoverHighlight, +.monaco-editor.hc-theme .debugHoverHighlight { + background-color: rgba(38, 79, 120, 0.25); +} \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css new file mode 100644 index 0000000000..866dbcb8f1 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css @@ -0,0 +1,396 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Debug viewlet */ + +.debug-viewlet { + height: 100%; +} + +/* Actionbar actions */ + +.monaco-workbench .debug-action.configure { + background: url('configure.svg') center center no-repeat; +} + +.monaco-workbench .debug-action.start { + background: url('continue.svg') center center no-repeat; +} + +.monaco-workbench .debug-action.toggle-repl { + background: url('repl.svg') center center no-repeat; +} + +.monaco-workbench .debug-action.notification:before { + content: ''; + width: 6px; + height: 6px; + background-color: #CC6633; + position: absolute; + top: 11px; + right: 5px; + border-radius: 10px; + border: 1px solid white; +} + +.vs-dark .monaco-workbench .debug-action.start, +.hc-black .monaco-workbench .debug-action.start { + background: url('continue-inverse.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .debug-action.configure, +.hc-black .monaco-workbench .debug-action.configure { + background: url('configure-inverse.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .debug-action.toggle-repl, +.hc-black .monaco-workbench .debug-action.toggle-repl { + background: url('repl-inverse.svg') center center no-repeat; +} + +.monaco-workbench > .part > .title > .title-actions .start-debug-action-item { + display: flex; + align-items: center; + font-size: 11px; + margin-right: 0.3em; + height: 20px; + flex-shrink: 1; + margin-top: 7px; +} + +.monaco-workbench.mac > .part > .title > .title-actions .start-debug-action-item { + border-radius: 4px; +} + +.monaco-workbench > .part > .title > .title-actions .start-debug-action-item .icon { + height: 20px; + width: 20px; + background: url('continue.svg') center center no-repeat; + flex-shrink: 0; + transition: transform 50ms ease; + -webkit-transition: -webkit-transform 50ms ease; +} + +.vs-dark .monaco-workbench > .part > .title > .title-actions .start-debug-action-item .icon, +.hc-black .monaco-workbench > .part > .title > .title-actions .start-debug-action-item .icon { + background: url('continue-inverse.svg') center center no-repeat; +} + +.monaco-workbench .monaco-action-bar .start-debug-action-item .configuration .select-box { + border: none; + margin-top: 0px; + cursor: pointer; +} + +.monaco-workbench .monaco-action-bar .start-debug-action-item .configuration.disabled .select-box { + opacity: 0.7; + font-style: italic; + cursor: initial; +} + +.monaco-workbench > .part > .title > .title-actions .start-debug-action-item .icon.active { + -webkit-transform: scale(1.272019649, 1.272019649); + transform: scale(1.272019649, 1.272019649); +} + +/* Debug viewlet trees */ + +.debug-viewlet .monaco-tree .monaco-tree-row > .content { + line-height: 22px; +} + +.debug-viewlet .line-number { + background: rgba(136, 136, 136, 0.3); + border-radius: 2px; + font-size: 0.9em; + padding: 0 3px; + margin-left: 0.8em; +} + +.debug-viewlet .monaco-tree .monaco-tree-row.selected .line-number, +.debug-viewlet .monaco-tree .monaco-tree-row.selected .thread > .state > .label, +.debug-viewlet .monaco-tree .monaco-tree-row.selected .process > .state > .label { + background-color: #ffffff; + color: #666; +} + +.debug-viewlet .monaco-tree .monaco-tree-row .content .monaco-action-bar { + visibility: hidden; + flex-shrink: 0; +} + +.debug-viewlet .monaco-tree .monaco-tree-row .content .monaco-action-bar .action-label { + width: 16px; + height: 16px; + vertical-align: text-bottom; +} + +.debug-viewlet .monaco-tree .monaco-tree-row:hover .content .monaco-action-bar, +.debug-viewlet .monaco-tree.focused .monaco-tree-row.focused .content .monaco-action-bar { + visibility: visible; +} + +.debug-viewlet .disabled { + opacity: 0.35; +} + +/* Call stack */ + +.debug-viewlet .debug-call-stack-title { + display: flex; + width: 100%; +} + +.debug-viewlet .debug-call-stack-title > .pause-message { + flex: 1; + text-align: right; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + margin: 0px 10px; +} + +.debug-viewlet .debug-call-stack-title > .pause-message > .label { + border-radius: 3px; + padding: 1px 2px; + font-size: 9px; +} + +.debug-viewlet .debug-call-stack-title > .pause-message > .label.exception { + background-color: #A31515; + color: rgb(255, 255, 255); +} + +.vs-dark .debug-viewlet .debug-call-stack-title > .pause-message > .label.exception { + background-color: #6C2022; + color: inherit; +} + +.hc-black .debug-viewlet .debug-call-stack-title > .pause-message > .label.exception { + background-color: #6C2022; + color: inherit; +} + +.debug-viewlet .debug-call-stack .thread, +.debug-viewlet .debug-call-stack .process { + display: flex; +} + +.debug-viewlet .debug-call-stack .thread > .state, +.debug-viewlet .debug-call-stack .process > .state { + flex: 1; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + padding: 0 10px; + text-transform: uppercase; +} + +.debug-viewlet .debug-call-stack .thread > .state > .label, +.debug-viewlet .debug-call-stack .process > .state > .label { + background: rgba(136, 136, 136, 0.3); + border-radius: 2px; + font-size: 0.8em; + padding: 0 3px; +} + +.debug-viewlet .debug-call-stack .stack-frame { + overflow: hidden; + text-overflow: ellipsis; + padding-right: 0.8em; +} + +.debug-viewlet .debug-call-stack .stack-frame.label { + text-align: center; + font-style: italic; +} + +.debug-viewlet .debug-call-stack .stack-frame.subtle { + font-style: italic; +} + +.debug-viewlet .debug-call-stack .stack-frame.label > .file { + display: none; +} + +.debug-viewlet .debug-call-stack .stack-frame > .file { + float: right; +} + +.debug-viewlet .debug-call-stack .stack-frame > .file > .line-number.unavailable { + display: none; +} + +.debug-viewlet .debug-call-stack > .monaco-tree-row:not(.selected) .stack-frame > .file { + color: rgba(108, 108, 108, 0.8); +} + +.vs-dark .debug-viewlet .debug-call-stack > .monaco-tree-row:not(.selected) .stack-frame > .file { + color: rgba(204, 204, 204, 0.6); +} + +.debug-viewlet .debug-call-stack .stack-frame > .file:not(:first-child) { + margin-left: 0.8em; +} + +.debug-viewlet .debug-call-stack .load-more { + font-style: italic; + text-align: center; +} + +.monaco-workbench .debug-viewlet .monaco-tree-row .expression { + white-space: pre; +} + +.debug-viewlet .debug-call-stack .error { + font-style: italic; + text-overflow: ellipsis; + overflow: hidden; +} + +/* Variables & Expression view */ + +.debug-viewlet .scope { + font-weight: bold; + font-size: 11px; +} + +/* Animation of changed values in Debug viewlet */ +@keyframes debugViewletValueChanged { + 0% { background-color: rgba(86, 156, 214, 0) } + 5% { background-color: rgba(86, 156, 214, .75) } + 100% { background-color: rgba(86, 156, 214, .3) } +} + +@keyframes debugViewletValueChangedDark { + 0% { background-color: rgba(86, 156, 214, 0) } + 5% { background-color: rgba(86, 156, 214, .5) } + 100% { background-color: rgba(86, 156, 214, .2) } +} + +.debug-viewlet .monaco-tree-row .expression .value.changed { + padding: 2px; + margin: 4px; + border-radius: 4px; + background-color: rgba(86, 156, 214, .5); + animation-name: debugViewletValueChanged; + animation-duration: .75s; + animation-fill-mode: forwards; +} + +.debug-viewlet .monaco-inputbox { + width: 100%; + line-height: normal; +} + +.debug-viewlet .inputBoxContainer { + box-sizing: border-box; + flex-grow: 1; +} + +.debug-viewlet .debug-watch .monaco-inputbox { + font-family: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback"; +} + +.debug-viewlet .monaco-inputbox > .wrapper { + height: 19px; +} + +.debug-viewlet .monaco-inputbox > .wrapper > .input { + padding: 0px; + color: initial; +} + +.debug-viewlet .watch-expression { + display: flex; +} + +.debug-viewlet .watch-expression .expression { + flex : 1; +} + +.debug-viewlet .debug-action.add-watch-expression, +.debug-viewlet .debug-action.add-function-breakpoint { + background: url('add.svg') center center no-repeat; +} + +.debug-viewlet .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .debug-action.add-watch-expression { + background: url("add-focus.svg") center center no-repeat; +} + +.vs-dark .debug-viewlet .debug-action.add-watch-expression, +.vs-dark .debug-viewlet .debug-action.add-function-breakpoint, +.hc-black .debug-viewlet .debug-action.add-watch-expression { + background: url('add-inverse.svg') center center no-repeat; +} + +.vs-dark .debug-viewlet .monaco-tree-row .expression .value.changed { + animation-name: debugViewletValueChanged; +} + +/* Breakpoints */ + +.debug-viewlet .debug-breakpoints .breakpoint { + display: flex; + padding-right: 0.8em; + flex: 1; + align-items: center; +} + +.debug-viewlet .debug-breakpoints .breakpoint input { + flex-shrink: 0; +} + +.debug-viewlet .debug-breakpoints .breakpoint > .file-path { + opacity: 0.7; + font-size: 0.9em; + margin-left: 0.8em; + flex: 1; + text-overflow: ellipsis; + overflow: hidden; +} + + +.debug-viewlet .debug-action.remove { + background: url('remove.svg') center center no-repeat; +} + +.debug-viewlet .debug-action.remove-all { + background: url('remove-all.svg') center center no-repeat; +} + +.debug-viewlet .debug-action.breakpoints-activate { + background: url('breakpoints-activate.svg') center center no-repeat; +} + +.debug-viewlet .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .debug-action.remove, +.vs-dark .debug-viewlet .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .debug-action.remove { + background: url("remove-focus.svg") center center no-repeat; +} + +.vs-dark .debug-viewlet .debug-action.remove, +.hc-black .debug-viewlet .debug-action.remove { + background: url('remove-inverse.svg') center center no-repeat; +} + +.vs-dark .debug-viewlet .debug-action.remove-all, +.hc-black .debug-viewlet .debug-action.remove-all { + background: url('remove-all-inverse.svg') center center no-repeat; +} + +.vs-dark .debug-viewlet .debug-action.breakpoints-activate, +.hc-black .debug-viewlet .debug-action.breakpoints-activate { + background: url('breakpoints-activate-inverse.svg') center center no-repeat; +} + +/* No workspace view */ + +.debug-viewlet > .noworkspace-view { + padding: 0 20px 0 20px; +} + +.debug-viewlet > .noworkspace-view > p { + line-height: 1.5em; +} diff --git a/src/vs/workbench/parts/debug/browser/media/disconnect-inverse.svg b/src/vs/workbench/parts/debug/browser/media/disconnect-inverse.svg new file mode 100644 index 0000000000..e00fd4062d --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/disconnect-inverse.svg @@ -0,0 +1 @@ +Plan de travail 1 \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/disconnect.svg b/src/vs/workbench/parts/debug/browser/media/disconnect.svg new file mode 100644 index 0000000000..2a87ce11ef --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/disconnect.svg @@ -0,0 +1 @@ +Plan de travail 1 \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/drag.svg b/src/vs/workbench/parts/debug/browser/media/drag.svg new file mode 100644 index 0000000000..7f4145d742 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/drag.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/exceptionWidget.css b/src/vs/workbench/parts/debug/browser/media/exceptionWidget.css new file mode 100644 index 0000000000..d1297ade08 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/exceptionWidget.css @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .zone-widget.exception-widget-container { + overflow: hidden; +} + +.monaco-editor .zone-widget .zone-widget-container.exception-widget { + padding: 6px 10px; + white-space: pre-wrap; + -webkit-user-select: text; + -ms-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -o-user-select: text; + user-select: text; +} + +.monaco-editor .zone-widget .zone-widget-container.exception-widget .title { + font-weight: bold; +} + +.monaco-editor .zone-widget .zone-widget-container.exception-widget .description, +.monaco-editor .zone-widget .zone-widget-container.exception-widget .stack-trace { + font-family: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback"; +} + +.monaco-editor .zone-widget .zone-widget-container.exception-widget .stack-trace { + margin-top: 0.5em; +} + +.monaco-editor .zone-widget .zone-widget-container.exception-widget a { + text-decoration: underline; + cursor: pointer; +} + +/* High Contrast Theming */ + +.monaco-workbench.mac .zone-widget .zone-widget-container.exception-widget { + font-size: 11px; +} + +.monaco-workbench.windows .zone-widget .zone-widget-container.exception-widget, +.monaco-workbench.linux .zone-widget .zone-widget-container.exception-widget { + font-size: 13px; +} diff --git a/src/vs/workbench/parts/debug/browser/media/pause-inverse.svg b/src/vs/workbench/parts/debug/browser/media/pause-inverse.svg new file mode 100644 index 0000000000..7418a23615 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/pause-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/pause.svg b/src/vs/workbench/parts/debug/browser/media/pause.svg new file mode 100644 index 0000000000..f8e3b859b1 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/remove-all-inverse.svg b/src/vs/workbench/parts/debug/browser/media/remove-all-inverse.svg new file mode 100644 index 0000000000..74e8dd8a02 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/remove-all-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/remove-all.svg b/src/vs/workbench/parts/debug/browser/media/remove-all.svg new file mode 100644 index 0000000000..7250ff6b54 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/remove-all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/remove-focus.svg b/src/vs/workbench/parts/debug/browser/media/remove-focus.svg new file mode 100644 index 0000000000..865c5aaea5 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/remove-focus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/remove-inverse.svg b/src/vs/workbench/parts/debug/browser/media/remove-inverse.svg new file mode 100644 index 0000000000..751e89b3b0 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/remove-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/remove.svg b/src/vs/workbench/parts/debug/browser/media/remove.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/repl-inverse.svg b/src/vs/workbench/parts/debug/browser/media/repl-inverse.svg new file mode 100644 index 0000000000..f9fcfba75e --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/repl-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/repl.css b/src/vs/workbench/parts/debug/browser/media/repl.css new file mode 100644 index 0000000000..e097b30a9f --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/repl.css @@ -0,0 +1,182 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Debug repl */ + +.monaco-workbench .repl { + height: 100%; + box-sizing: border-box; + overflow: hidden; +} + +.monaco-workbench .repl .surveyor { + font-family: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback"; + position: absolute; + display: inline-block; + width : auto; + top: 0; + left: 0; + visibility: hidden; +} + +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content { + line-height: 18px; + -webkit-user-select: text; + /* Wrap words but also do not trim whitespace #6275 */ + word-wrap: break-word; + white-space: pre-wrap; + /* Break on all #7533 */ + word-break: break-all; +} + +.monaco-workbench.mac .repl .repl-tree .monaco-tree-row .input.expression, +.monaco-workbench.mac .repl .repl-tree .monaco-tree-row .output.expression { + font-size: 12px; +} + +.monaco-workbench.windows .repl .repl-tree .monaco-tree-row .input.expression, +.monaco-workbench.windows .repl .repl-tree .monaco-tree-row .output.expression, +.monaco-workbench.linux .repl .repl-tree .monaco-tree-row .input.expression, +.monaco-workbench.linux .repl .repl-tree .monaco-tree-row .output.expression { + font-size: 14px; +} + +.monaco-workbench .repl .repl-tree .monaco-tree-row .output.expression > .value { + margin-left: 0px; +} + +.monaco-workbench .repl .repl-tree .monaco-tree-row .output.expression > .annotation { + font-size: inherit; + padding-left: 6px; +} + +.monaco-workbench .repl .repl-tree .monaco-tree-row .output.expression .name:not(:empty) { + margin-right: 6px; +} + +/* Allign twistie since repl tree is smaller and sometimes has paired elements */ + +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row.has-children > .content.input-output-pair:before { + top: 8px; + cursor: pointer; +} + +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row.has-children > .content.input-output-pair:after { + top: 10px; +} + +.monaco-workbench .repl .repl-input-wrapper { + padding-left: 20px; + border-top: 1px solid rgba(128, 128, 128, 0.35); +} + +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row { + cursor: default; +} + +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row.has-children .content .expression { + cursor: pointer; +} + +/* Disable repl hover highlight in tree. */ +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { + background-color: inherit; +} + +/* Only show 'stale expansion' info when the element gets expanded. */ +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row:not(.expanded) > .content.input-output-pair > .output > .annotation::before { + content: ''; +} + +.hc-black .monaco-workbench .repl .repl-input-wrapper { + border-top-color: #6FC3DF; +} + +.monaco-workbench .repl .repl-input-wrapper:before { + left: 8px; + position: absolute; +} + +.monaco-workbench .repl .repl-input-wrapper:before { + content: '\276f'; + line-height: 18px; +} + +.monaco-workbench.linux .repl .repl-input-wrapper:before { + font-size: 9px; +} + +/* Actions */ + +.monaco-workbench .debug-action.clear-repl { + background: url('clear-repl.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .debug-action.clear-repl, +.hc-black .monaco-workbench .debug-action.clear-repl { + background: url('clear-repl-inverse.svg') center center no-repeat; +} + +/* Output coloring */ + +.vs .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression > .warn { + color: #cd9731; +} + +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression > .warn { + color: #cd9731; +} + +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression > .warn { + color: #008000; +} + +.vs .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression > .annotation { + color: #007ACC; +} + +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression > .annotation { + color: #1B80B2; +} + +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression > .annotation { + color: #0000FF; +} + +/* ANSI Codes */ +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-bold { font-weight: bold; } + +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code30, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code90 { color: gray; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code31, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code91 { color: #BE1717; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code32, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code92 { color: #338A2F; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code33, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code93 { color: #BEB817; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code34, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code94 { color: darkblue; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code35, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code95 { color: darkmagenta; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code36, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code96 { color: darkcyan; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code37, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code97 { color: #BDBDBD; } + +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code30, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code90 { color: #A0A0A0; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code31, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code91 { color: #A74747; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code32, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code92 { color: #348F34; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code33, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code93 { color: #5F4C29; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code34, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code94 { color: #6286BB; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code35, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code95 { color: #914191; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code36, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code96 { color: #218D8D; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code37, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code97 { color: #707070; } + +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code30, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code90 { color: gray; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code31, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code91 { color: #A74747; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code32, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code92 { color: #348F34; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code33, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code93 { color: #5F4C29; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code34, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code94 { color: #6286BB; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code35, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code95 { color: #914191; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code36, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code96 { color: #218D8D; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code37, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code97 { color: #707070; } + +/* Links */ +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression a { + text-decoration: underline; + cursor: pointer; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/repl.svg b/src/vs/workbench/parts/debug/browser/media/repl.svg new file mode 100644 index 0000000000..93558e96b4 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/repl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/restart-inverse.svg b/src/vs/workbench/parts/debug/browser/media/restart-inverse.svg new file mode 100644 index 0000000000..1443bb3158 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/restart-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/restart.svg b/src/vs/workbench/parts/debug/browser/media/restart.svg new file mode 100644 index 0000000000..3d7a499db2 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/restart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/stackframe-and-breakpoint-dark.svg b/src/vs/workbench/parts/debug/browser/media/stackframe-and-breakpoint-dark.svg new file mode 100644 index 0000000000..1d6a7c3ec3 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/stackframe-and-breakpoint-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/stackframe-and-breakpoint.svg b/src/vs/workbench/parts/debug/browser/media/stackframe-and-breakpoint.svg new file mode 100644 index 0000000000..2d5823561e --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/stackframe-and-breakpoint.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/stackframe-arrow-dark.svg b/src/vs/workbench/parts/debug/browser/media/stackframe-arrow-dark.svg new file mode 100644 index 0000000000..f965fddd63 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/stackframe-arrow-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/stackframe-arrow.svg b/src/vs/workbench/parts/debug/browser/media/stackframe-arrow.svg new file mode 100644 index 0000000000..e39215f9a7 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/stackframe-arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/step-into-inverse.svg b/src/vs/workbench/parts/debug/browser/media/step-into-inverse.svg new file mode 100644 index 0000000000..b7de8c5fd1 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/step-into-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/step-into.svg b/src/vs/workbench/parts/debug/browser/media/step-into.svg new file mode 100644 index 0000000000..b92fb4ef80 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/step-into.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/step-out-inverse.svg b/src/vs/workbench/parts/debug/browser/media/step-out-inverse.svg new file mode 100644 index 0000000000..fc66ca1b80 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/step-out-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/step-out.svg b/src/vs/workbench/parts/debug/browser/media/step-out.svg new file mode 100644 index 0000000000..0fdc63f6ef --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/step-out.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/step-over-inverse.svg b/src/vs/workbench/parts/debug/browser/media/step-over-inverse.svg new file mode 100644 index 0000000000..98c1026db4 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/step-over-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/step-over.svg b/src/vs/workbench/parts/debug/browser/media/step-over.svg new file mode 100644 index 0000000000..a716cbcf55 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/step-over.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/stop-inverse.svg b/src/vs/workbench/parts/debug/browser/media/stop-inverse.svg new file mode 100644 index 0000000000..a0e6bcb42d --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/stop-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/stop.svg b/src/vs/workbench/parts/debug/browser/media/stop.svg new file mode 100644 index 0000000000..333812dcda --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/stop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts new file mode 100644 index 0000000000..aad8cf5339 --- /dev/null +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -0,0 +1,642 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import uri from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import severity from 'vs/base/common/severity'; +import Event from 'vs/base/common/event'; +import { IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IModel as EditorIModel, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IEditor } from 'vs/platform/editor/common/editor'; +import { Position } from 'vs/editor/common/core/position'; +import { ISuggestion } from 'vs/editor/common/modes'; +import { Source } from 'vs/workbench/parts/debug/common/debugSource'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export const VIEWLET_ID = 'workbench.view.debug'; +export const REPL_ID = 'workbench.panel.repl'; +export const DEBUG_SERVICE_ID = 'debugService'; +export const CONTEXT_DEBUG_TYPE = new RawContextKey('debugType', undefined); +export const CONTEXT_DEBUG_STATE = new RawContextKey('debugState', undefined); +export const CONTEXT_IN_DEBUG_MODE = new RawContextKey('inDebugMode', false); +export const CONTEXT_NOT_IN_DEBUG_MODE: ContextKeyExpr = CONTEXT_IN_DEBUG_MODE.toNegated(); +export const CONTEXT_IN_DEBUG_REPL = new RawContextKey('inDebugRepl', false); +export const CONTEXT_NOT_IN_DEBUG_REPL: ContextKeyExpr = CONTEXT_IN_DEBUG_REPL.toNegated(); +export const CONTEXT_ON_FIRST_DEBUG_REPL_LINE = new RawContextKey('onFirsteDebugReplLine', false); +export const CONTEXT_ON_LAST_DEBUG_REPL_LINE = new RawContextKey('onLastDebugReplLine', false); +export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey('breakpointWidgetVisible', false); +export const CONTEXT_BREAKPOINTS_FOCUSED = new RawContextKey('breakpointsFocused', false); +export const CONTEXT_WATCH_EXPRESSIONS_FOCUSED = new RawContextKey('watchExpressionsFocused', false); +export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey('variablesFocused', false); + +export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug'; +export const DEBUG_SCHEME = 'debug'; +export const INTERNAL_CONSOLE_OPTIONS_SCHEMA = { + enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'], + default: 'openOnFirstSessionStart', + description: nls.localize('internalConsoleOptions', "Controls behavior of the internal debug console.") +}; + +// raw + +export interface IRawModelUpdate { + threadId: number; + sessionId: string; + thread?: DebugProtocol.Thread; + callStack?: DebugProtocol.StackFrame[]; + stoppedDetails?: IRawStoppedDetails; + allThreadsStopped?: boolean; +} + +export interface IRawStoppedDetails { + reason: string; + description?: string; + threadId?: number; + text?: string; + totalFrames?: number; + framesErrorMessage?: string; +} + +// model + +export interface ITreeElement { + getId(): string; +} + +export interface IReplElement extends ITreeElement { + toString(): string; +} + +export interface IExpressionContainer extends ITreeElement { + hasChildren: boolean; + getChildren(): TPromise; +} + +export interface IExpression extends IReplElement, IExpressionContainer { + name: string; + value: string; + valueChanged?: boolean; + type?: string; +} + +export interface ISession { + root: uri; + stackTrace(args: DebugProtocol.StackTraceArguments): TPromise; + exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise; + scopes(args: DebugProtocol.ScopesArguments): TPromise; + variables(args: DebugProtocol.VariablesArguments): TPromise; + evaluate(args: DebugProtocol.EvaluateArguments): TPromise; + + capabilities: DebugProtocol.Capabilities; + disconnect(restart?: boolean, force?: boolean): TPromise; + custom(request: string, args: any): TPromise; + onDidEvent: Event; + onDidInitialize: Event; + restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): TPromise; + + next(args: DebugProtocol.NextArguments): TPromise; + stepIn(args: DebugProtocol.StepInArguments): TPromise; + stepOut(args: DebugProtocol.StepOutArguments): TPromise; + continue(args: DebugProtocol.ContinueArguments): TPromise; + pause(args: DebugProtocol.PauseArguments): TPromise; + stepBack(args: DebugProtocol.StepBackArguments): TPromise; + reverseContinue(args: DebugProtocol.ReverseContinueArguments): TPromise; + + completions(args: DebugProtocol.CompletionsArguments): TPromise; + setVariable(args: DebugProtocol.SetVariableArguments): TPromise; + source(args: DebugProtocol.SourceArguments): TPromise; +} + +export enum ProcessState { + INACTIVE, + ATTACH, + LAUNCH +} + +export interface IProcess extends ITreeElement { + getName(includeRoot: boolean): string; + configuration: IConfig; + session: ISession; + sources: Map; + state: ProcessState; + getThread(threadId: number): IThread; + getAllThreads(): IThread[]; + completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise; +} + +export interface IThread extends ITreeElement { + + /** + * Process the thread belongs to + */ + process: IProcess; + + /** + * Id of the thread generated by the debug adapter backend. + */ + threadId: number; + + /** + * Name of the thread. + */ + name: string; + + /** + * Information about the current thread stop event. Null if thread is not stopped. + */ + stoppedDetails: IRawStoppedDetails; + + /** + * Information about the exception if an 'exception' stopped event raised and DA supports the 'exceptionInfo' request, otherwise null. + */ + exceptionInfo: TPromise; + + /** + * Gets the callstack if it has already been received from the debug + * adapter, otherwise it returns null. + */ + getCallStack(): IStackFrame[]; + + /** + * Invalidates the callstack cache + */ + clearCallStack(): void; + + /** + * Indicates whether this thread is stopped. The callstack for stopped + * threads can be retrieved from the debug adapter. + */ + stopped: boolean; + + next(): TPromise; + stepIn(): TPromise; + stepOut(): TPromise; + stepBack(): TPromise; + continue(): TPromise; + pause(): TPromise; + reverseContinue(): TPromise; +} + +export interface IScope extends IExpressionContainer { + name: string; + expensive: boolean; + range?: IRange; +} + +export interface IStackFrame extends ITreeElement { + thread: IThread; + name: string; + presentationHint: string; + frameId: number; + range: IRange; + source: Source; + getScopes(): TPromise; + getMostSpecificScopes(range: IRange): TPromise; + restart(): TPromise; + toString(): string; + openInEditor(editorService: IWorkbenchEditorService, preserveFocus?: boolean, sideBySide?: boolean): TPromise; +} + +export interface IEnablement extends ITreeElement { + enabled: boolean; +} + +export interface IRawBreakpoint { + lineNumber: number; + column?: number; + enabled?: boolean; + condition?: string; + hitCondition?: string; +} + +export interface IBreakpoint extends IEnablement { + uri: uri; + lineNumber: number; + endLineNumber?: number; + column: number; + endColumn?: number; + condition: string; + hitCondition: string; + verified: boolean; + idFromAdapter: number; + message: string; +} + +export interface IFunctionBreakpoint extends IEnablement { + name: string; + verified: boolean; + idFromAdapter: number; + hitCondition: string; +} + +export interface IExceptionBreakpoint extends IEnablement { + filter: string; + label: string; +} + +export interface IExceptionInfo { + id?: string; + description?: string; + breakMode: string; + details?: DebugProtocol.ExceptionDetails; +} + +// model interfaces + +export interface IViewModel extends ITreeElement { + /** + * Returns the focused debug process or null if no process is stopped. + */ + focusedProcess: IProcess; + + /** + * Returns the focused thread or null if no thread is stopped. + */ + focusedThread: IThread; + + /** + * Returns the focused stack frame or null if there are no stack frames. + */ + focusedStackFrame: IStackFrame; + getSelectedExpression(): IExpression; + getSelectedFunctionBreakpoint(): IFunctionBreakpoint; + setSelectedExpression(expression: IExpression); + setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint): void; + + isMultiProcessView(): boolean; + + onDidFocusProcess: Event; + onDidFocusStackFrame: Event<{ stackFrame: IStackFrame, explicit: boolean }>; + onDidSelectExpression: Event; + onDidSelectFunctionBreakpoint: Event; +} + +export interface IModel extends ITreeElement { + getProcesses(): IProcess[]; + getBreakpoints(): IBreakpoint[]; + areBreakpointsActivated(): boolean; + getFunctionBreakpoints(): IFunctionBreakpoint[]; + getExceptionBreakpoints(): IExceptionBreakpoint[]; + getWatchExpressions(): IExpression[]; + getReplElements(): IReplElement[]; + + onDidChangeBreakpoints: Event; + onDidChangeCallStack: Event; + onDidChangeWatchExpressions: Event; + onDidChangeReplElements: Event; +}; + +// Debug enums + +export enum State { + Inactive, + Initializing, + Stopped, + Running +} + +// Debug configuration interfaces + +export interface IDebugConfiguration { + allowBreakpointsEverywhere: boolean; + openExplorerOnEnd: boolean; + inlineValues: boolean; + hideActionBar: boolean; + internalConsoleOptions: string; +} + +export interface IGlobalConfig { + version: string; + compounds: ICompound[]; + configurations: IConfig[]; +} + +export interface IEnvConfig { + name?: string; + type: string; + request: string; + internalConsoleOptions?: string; + preLaunchTask?: string; + __restart?: any; + __sessionId?: string; + debugServer?: number; + noDebug?: boolean; + port?: number; +} + +export interface IConfig extends IEnvConfig { + windows?: IEnvConfig; + osx?: IEnvConfig; + linux?: IEnvConfig; +} + +export interface ICompound { + name: string; + configurations: string[]; +} + +export interface IAdapterExecutable { + command?: string; + args?: string[]; +} + +export interface IRawEnvAdapter { + type?: string; + label?: string; + program?: string; + args?: string[]; + runtime?: string; + runtimeArgs?: string[]; +} + +export interface IRawAdapter extends IRawEnvAdapter { + adapterExecutableCommand?: string; + enableBreakpointsFor?: { languageIds: string[] }; + configurationAttributes?: any; + configurationSnippets?: IJSONSchemaSnippet[]; + initialConfigurations?: any[] | string; + startSessionCommand?: string; + languages?: string[]; + variables?: { [key: string]: string }; + aiKey?: string; + win?: IRawEnvAdapter; + winx86?: IRawEnvAdapter; + windows?: IRawEnvAdapter; + osx?: IRawEnvAdapter; + linux?: IRawEnvAdapter; +} + +export interface IDebugConfigurationProvider { + type: string; + resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: any): TPromise; + provideDebugConfigurations?(folderUri: uri | undefined): TPromise; +} + +export interface IConfigurationManager { + /** + * Returns true if breakpoints can be set for a given editor model. Depends on mode. + */ + canSetBreakpointsIn(model: EditorIModel): boolean; + + /** + * Returns null for no folder workspace. Otherwise returns a launch object corresponding to the selected debug configuration. + */ + selectedLaunch: ILaunch; + + selectedName: string; + + selectConfiguration(launch: ILaunch, name?: string, debugStarted?: boolean): void; + + getLaunches(): ILaunch[]; + + /** + * Allows to register on change of selected debug configuration. + */ + onDidSelectConfiguration: Event; + + /** + * Returns a "startSessionCommand" contribution for an adapter with the passed type. + * If no type is specified will try to automatically pick an adapter by looking at + * the active editor language and matching it against the "languages" contribution of an adapter. + */ + getStartSessionCommand(type?: string): TPromise<{ command: string, type: string }>; + + registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void; + unregisterDebugConfigurationProvider(handle: number): void; + resolveDebugConfiguration(folderUri: uri | undefined, debugConfiguration: any): TPromise; +} + +export interface ILaunch { + + /** + * Resource pointing to the launch.json this object is wrapping. + */ + uri: uri; + + workspaceUri: uri; + + name: string; + + /** + * Returns a configuration with the specified name. + * Returns null if there is no configuration with the specified name. + */ + getConfiguration(name: string): IConfig; + + /** + * Returns a compound with the specified name. + * Returns null if there is no compound with the specified name. + */ + getCompound(name: string): ICompound; + + /** + * Returns the names of all configurations and compounds. + * Ignores configurations which are invalid. + */ + getConfigurationNames(): string[]; + + /** + * Returns the resolved configuration. + * Replaces os specific values, system variables, interactive variables. + */ + resolveConfiguration(config: IConfig): TPromise; + + /** + * Opens the launch.json file. Creates if it does not exist. + */ + openConfigFile(sideBySide: boolean, type?: string): TPromise; +} + +// Debug service interfaces + +export const IDebugService = createDecorator(DEBUG_SERVICE_ID); + +export interface DebugEvent extends DebugProtocol.Event { + sessionId?: string; +} + +export interface IDebugService { + _serviceBrand: any; + + /** + * Gets the current debug state. + */ + state: State; + + /** + * Allows to register on debug state changes. + */ + onDidChangeState: Event; + + /** + * Allows to register on new process events. + */ + onDidNewProcess: Event; + + /** + * Allows to register on end process events. + */ + onDidEndProcess: Event; + + /** + * Allows to register on custom DAP events. + */ + onDidCustomEvent: Event; + + /** + * Gets the current configuration manager. + */ + getConfigurationManager(): IConfigurationManager; + + /** + * Sets the focused stack frame and evaluates all expressions against the newly focused stack frame, + */ + focusStackFrameAndEvaluate(focusedStackFrame: IStackFrame, process?: IProcess, explicit?: boolean): TPromise; + + /** + * Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes. + */ + addBreakpoints(uri: uri, rawBreakpoints: IRawBreakpoint[]): TPromise; + + /** + * Enables or disables all breakpoints. If breakpoint is passed only enables or disables the passed breakpoint. + * Notifies debug adapter of breakpoint changes. + */ + enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): TPromise; + + /** + * Sets the global activated property for all breakpoints. + * Notifies debug adapter of breakpoint changes. + */ + setBreakpointsActivated(activated: boolean): TPromise; + + /** + * Removes all breakpoints. If id is passed only removes the breakpoint associated with that id. + * Notifies debug adapter of breakpoint changes. + */ + removeBreakpoints(id?: string): TPromise; + + /** + * Adds a new no name function breakpoint. The function breakpoint should be renamed once user enters the name. + */ + addFunctionBreakpoint(): void; + + /** + * Renames an already existing function breakpoint. + * Notifies debug adapter of breakpoint changes. + */ + renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise; + + /** + * Removes all function breakpoints. If id is passed only removes the function breakpoint with the passed id. + * Notifies debug adapter of breakpoint changes. + */ + removeFunctionBreakpoints(id?: string): TPromise; + + /** + * Adds a new expression to the repl. + */ + addReplExpression(name: string): TPromise; + + /** + * Removes all repl expressions. + */ + removeReplExpressions(): void; + + /** + * Appends the passed string to the debug repl. + */ + logToRepl(value: string, sev?: severity): void; + + /** + * Adds a new watch expression and evaluates it against the debug adapter. + */ + addWatchExpression(name?: string): TPromise; + + /** + * Renames a watch expression and evaluates it against the debug adapter. + */ + renameWatchExpression(id: string, newName: string): TPromise; + + /** + * Moves a watch expression to a new possition. Used for reordering watch expressions. + */ + moveWatchExpression(id: string, position: number): void; + + /** + * Removes all watch expressions. If id is passed only removes the watch expression with the passed id. + */ + removeWatchExpressions(id?: string): void; + + /** + * Starts debugging. If the configOrName is not passed uses the selected configuration in the debug dropdown. + * Also saves all files, manages if compounds are present in the configuration + * and calls the startSessionCommand if an adapter registered it. + */ + startDebugging(root: uri, configOrName?: IConfig | string, noDebug?: boolean): TPromise; + + /** + * Creates a new debug process. Depending on the configuration will either 'launch' or 'attach'. + */ + createProcess(root: uri, config: IConfig): TPromise; + + /** + * Find process by ID. + */ + findProcessByUUID(uuid: string): IProcess | null; + + /** + * Restarts a process or creates a new one if there is no active session. + */ + restartProcess(process: IProcess): TPromise; + + /** + * Stops the process. If the process does not exist then stops all processes. + */ + stopProcess(process: IProcess): TPromise; + + /** + * Makes unavailable all sources with the passed uri. Source will appear as grayed out in callstack view. + */ + sourceIsNotAvailable(uri: uri): void; + + /** + * Gets the current debug model. + */ + getModel(): IModel; + + /** + * Gets the current view model. + */ + getViewModel(): IViewModel; +} + +// Editor interfaces +export interface IDebugEditorContribution extends IEditorContribution { + showHover(range: Range, focus: boolean): TPromise; + showBreakpointWidget(lineNumber: number, column: number): void; + closeBreakpointWidget(): void; + addLaunchConfiguration(): TPromise; +} + +// utils + +const _formatPIIRegexp = /{([^}]+)}/g; + +export function formatPII(value: string, excludePII: boolean, args: { [key: string]: string }): string { + return value.replace(_formatPIIRegexp, function (match, group) { + if (excludePII && group.length > 0 && group[0] !== '_') { + return match; + } + + return args && args.hasOwnProperty(group) ? + args[group] : + match; + }); +} diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts new file mode 100644 index 0000000000..c93e6cbee9 --- /dev/null +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -0,0 +1,1063 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import uri from 'vs/base/common/uri'; +import * as paths from 'vs/base/common/paths'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import { generateUuid } from 'vs/base/common/uuid'; +import * as errors from 'vs/base/common/errors'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import severity from 'vs/base/common/severity'; +import { isObject, isString } from 'vs/base/common/types'; +import { distinct } from 'vs/base/common/arrays'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { ISuggestion } from 'vs/editor/common/modes'; +import { Position } from 'vs/editor/common/core/position'; +import { + ITreeElement, IExpression, IExpressionContainer, IProcess, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel, + IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IRawBreakpoint, IExceptionInfo, IReplElement, ProcessState +} from 'vs/workbench/parts/debug/common/debug'; +import { Source } from 'vs/workbench/parts/debug/common/debugSource'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; + +const MAX_REPL_LENGTH = 10000; + +export abstract class AbstractOutputElement implements IReplElement { + private static ID_COUNTER = 0; + + constructor(private id = AbstractOutputElement.ID_COUNTER++) { + // noop + } + + public getId(): string { + return `outputelement:${this.id}`; + } + + abstract toString(): string; +} + +export class OutputElement extends AbstractOutputElement { + + + constructor( + public value: string, + public severity: severity, + ) { + super(); + } + + public toString(): string { + return this.value; + } +} + +export class OutputNameValueElement extends AbstractOutputElement implements IExpression { + + private static MAX_CHILDREN = 1000; // upper bound of children per value + + constructor(public name: string, public valueObj: any, public annotation?: string) { + super(); + } + + public get value(): string { + if (this.valueObj === null) { + return 'null'; + } else if (Array.isArray(this.valueObj)) { + return `Array[${this.valueObj.length}]`; + } else if (isObject(this.valueObj)) { + return 'Object'; + } else if (isString(this.valueObj)) { + return `"${this.valueObj}"`; + } + + return String(this.valueObj) || ''; + } + + public get hasChildren(): boolean { + return (Array.isArray(this.valueObj) && this.valueObj.length > 0) || (isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0); + } + + public getChildren(): TPromise { + let result: IExpression[] = []; + if (Array.isArray(this.valueObj)) { + result = (this.valueObj).slice(0, OutputNameValueElement.MAX_CHILDREN) + .map((v, index) => new OutputNameValueElement(String(index), v)); + } else if (isObject(this.valueObj)) { + result = Object.getOwnPropertyNames(this.valueObj).slice(0, OutputNameValueElement.MAX_CHILDREN) + .map(key => new OutputNameValueElement(key, this.valueObj[key])); + } + + return TPromise.as(result); + } + + public toString(): string { + return this.name ? `${this.name}: ${this.value}` : this.value; + } +} + +export class ExpressionContainer implements IExpressionContainer { + + public static allValues: Map = new Map(); + // Use chunks to support variable paging #9537 + private static BASE_CHUNK_SIZE = 100; + + public valueChanged: boolean; + private _value: string; + protected children: TPromise; + + constructor( + protected process: IProcess, + private _reference: number, + private id: string, + public namedVariables = 0, + public indexedVariables = 0, + private startOfVariables = 0 + ) { } + + public get reference(): number { + return this._reference; + } + + public set reference(value: number) { + this._reference = value; + this.children = undefined; // invalidate children cache + } + + public getChildren(): TPromise { + if (!this.children) { + this.children = this.doGetChildren(); + } + + return this.children; + } + + private doGetChildren(): TPromise { + if (!this.hasChildren) { + return TPromise.as([]); + } + + if (!this.getChildrenInChunks) { + return this.fetchVariables(undefined, undefined, undefined); + } + + // Check if object has named variables, fetch them independent from indexed variables #9670 + return (!!this.namedVariables ? this.fetchVariables(undefined, undefined, 'named') : TPromise.as([])).then(childrenArray => { + // Use a dynamic chunk size based on the number of elements #9774 + let chunkSize = ExpressionContainer.BASE_CHUNK_SIZE; + while (this.indexedVariables > chunkSize * ExpressionContainer.BASE_CHUNK_SIZE) { + chunkSize *= ExpressionContainer.BASE_CHUNK_SIZE; + } + + if (this.indexedVariables > chunkSize) { + // There are a lot of children, create fake intermediate values that represent chunks #9537 + const numberOfChunks = Math.ceil(this.indexedVariables / chunkSize); + for (let i = 0; i < numberOfChunks; i++) { + const start = this.startOfVariables + i * chunkSize; + const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize); + childrenArray.push(new Variable(this.process, this, this.reference, `[${start}..${start + count - 1}]`, '', '', null, count, null, true, start)); + } + + return childrenArray; + } + + return this.fetchVariables(this.startOfVariables, this.indexedVariables, 'indexed') + .then(variables => childrenArray.concat(variables)); + }); + } + + public getId(): string { + return this.id; + } + + public get value(): string { + return this._value; + } + + public get hasChildren(): boolean { + // only variables with reference > 0 have children. + return this.reference > 0; + } + + private fetchVariables(start: number, count: number, filter: 'indexed' | 'named'): TPromise { + return this.process.session.variables({ + variablesReference: this.reference, + start, + count, + filter + }).then(response => { + return response && response.body && response.body.variables ? distinct(response.body.variables.filter(v => !!v && v.name), v => v.name).map( + v => new Variable(this.process, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.type) + ) : []; + }, (e: Error) => [new Variable(this.process, this, 0, null, e.message, '', 0, 0, null, false)]); + } + + // The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked. + private get getChildrenInChunks(): boolean { + return !!this.indexedVariables; + } + + public set value(value: string) { + this._value = value; + this.valueChanged = ExpressionContainer.allValues.get(this.getId()) && + ExpressionContainer.allValues.get(this.getId()) !== Expression.DEFAULT_VALUE && ExpressionContainer.allValues.get(this.getId()) !== value; + ExpressionContainer.allValues.set(this.getId(), value); + } + + public toString(): string { + return this.value; + } +} + +export class Expression extends ExpressionContainer implements IExpression { + static DEFAULT_VALUE = nls.localize('notAvailable', "not available"); + + public available: boolean; + public type: string; + + constructor(public name: string, id = generateUuid()) { + super(null, 0, id); + this.available = false; + // name is not set if the expression is just being added + // in that case do not set default value to prevent flashing #14499 + if (name) { + this.value = Expression.DEFAULT_VALUE; + } + } + + public evaluate(process: IProcess, stackFrame: IStackFrame, context: string): TPromise { + if (!process || (!stackFrame && context !== 'repl')) { + this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate") : Expression.DEFAULT_VALUE; + this.available = false; + this.reference = 0; + + return TPromise.as(null); + } + + this.process = process; + return process.session.evaluate({ + expression: this.name, + frameId: stackFrame ? stackFrame.frameId : undefined, + context + }).then(response => { + this.available = !!(response && response.body); + if (response && response.body) { + this.value = response.body.result; + this.reference = response.body.variablesReference; + this.namedVariables = response.body.namedVariables; + this.indexedVariables = response.body.indexedVariables; + this.type = response.body.type; + } + }, err => { + this.value = err.message; + this.available = false; + this.reference = 0; + }); + } + + public toString(): string { + return `${this.name}\n${this.value}`; + } +} + +export class Variable extends ExpressionContainer implements IExpression { + + // Used to show the error message coming from the adapter when setting the value #7807 + public errorMessage: string; + + constructor( + process: IProcess, + public parent: IExpressionContainer, + reference: number, + public name: string, + public evaluateName: string, + value: string, + namedVariables: number, + indexedVariables: number, + public type: string = null, + public available = true, + startOfVariables = 0 + ) { + super(process, reference, `variable:${parent.getId()}:${name}:${reference}`, namedVariables, indexedVariables, startOfVariables); + this.value = value; + } + + public setVariable(value: string): TPromise { + return this.process.session.setVariable({ + name: this.name, + value, + variablesReference: (this.parent).reference + }).then(response => { + if (response && response.body) { + this.value = response.body.value; + this.type = response.body.type || this.type; + this.reference = response.body.variablesReference; + this.namedVariables = response.body.namedVariables; + this.indexedVariables = response.body.indexedVariables; + } + }, err => { + this.errorMessage = err.message; + }); + } + + public toString(): string { + return `${this.name}: ${this.value}`; + } +} + +export class Scope extends ExpressionContainer implements IScope { + + constructor( + stackFrame: IStackFrame, + public name: string, + reference: number, + public expensive: boolean, + namedVariables: number, + indexedVariables: number, + public range?: IRange + ) { + super(stackFrame.thread.process, reference, `scope:${stackFrame.getId()}:${name}:${reference}`, namedVariables, indexedVariables); + } +} + +export class StackFrame implements IStackFrame { + + private scopes: TPromise; + + constructor( + public thread: IThread, + public frameId: number, + public source: Source, + public name: string, + public presentationHint: string, + public range: IRange, + private index: number + ) { + this.scopes = null; + } + + public getId(): string { + return `stackframe:${this.thread.getId()}:${this.frameId}:${this.index}`; + } + + public getScopes(): TPromise { + if (!this.scopes) { + this.scopes = this.thread.process.session.scopes({ frameId: this.frameId }).then(response => { + return response && response.body && response.body.scopes ? + response.body.scopes.map(rs => new Scope(this, rs.name, rs.variablesReference, rs.expensive, rs.namedVariables, rs.indexedVariables, + rs.line && rs.column && rs.endLine && rs.endColumn ? new Range(rs.line, rs.column, rs.endLine, rs.endColumn) : null)) : []; + }, err => []); + } + + return this.scopes; + } + + public getMostSpecificScopes(range: IRange): TPromise { + return this.getScopes().then(scopes => { + scopes = scopes.filter(s => !s.expensive); + const haveRangeInfo = scopes.some(s => !!s.range); + if (!haveRangeInfo) { + return scopes; + } + + const scopesContainingRange = scopes.filter(scope => scope.range && Range.containsRange(scope.range, range)) + .sort((first, second) => (first.range.endLineNumber - first.range.startLineNumber) - (second.range.endLineNumber - second.range.startLineNumber)); + return scopesContainingRange.length ? scopesContainingRange : scopes; + }); + } + + public restart(): TPromise { + return this.thread.process.session.restartFrame({ frameId: this.frameId }, this.thread.threadId); + } + + public toString(): string { + return `${this.name} (${this.source.inMemory ? this.source.name : this.source.uri.fsPath}:${this.range.startLineNumber})`; + } + + public openInEditor(editorService: IWorkbenchEditorService, preserveFocus?: boolean, sideBySide?: boolean): TPromise { + + return !this.source.available ? TPromise.as(null) : editorService.openEditor({ + resource: this.source.uri, + description: this.source.origin, + options: { + preserveFocus, + selection: this.range, + revealIfVisible: true, + revealInCenterIfOutsideViewport: true, + pinned: !preserveFocus && !this.source.inMemory + } + }, sideBySide); + } +} + +export class Thread implements IThread { + private callStack: IStackFrame[]; + private staleCallStack: IStackFrame[]; + public stoppedDetails: IRawStoppedDetails; + public stopped: boolean; + + constructor(public process: IProcess, public name: string, public threadId: number) { + this.stoppedDetails = null; + this.callStack = []; + this.staleCallStack = []; + this.stopped = false; + } + + public getId(): string { + return `thread:${this.process.getId()}:${this.threadId}`; + } + + public clearCallStack(): void { + if (this.callStack.length) { + this.staleCallStack = this.callStack; + } + this.callStack = []; + } + + public getCallStack(): IStackFrame[] { + return this.callStack; + } + + public getStaleCallStack(): IStackFrame[] { + return this.staleCallStack; + } + + /** + * Queries the debug adapter for the callstack and returns a promise + * which completes once the call stack has been retrieved. + * If the thread is not stopped, it returns a promise to an empty array. + * Only fetches the first stack frame for performance reasons. Calling this method consecutive times + * gets the remainder of the call stack. + */ + public fetchCallStack(levels = 20): TPromise { + if (!this.stopped) { + return TPromise.as(null); + } + + const start = this.callStack.length; + return this.getCallStackImpl(start, levels).then(callStack => { + if (start < this.callStack.length) { + // Set the stack frames for exact position we requested. To make sure no concurrent requests create duplicate stack frames #30660 + this.callStack.splice(start, this.callStack.length - start); + } + this.callStack = this.callStack.concat(callStack || []); + }); + } + + private getCallStackImpl(startFrame: number, levels: number): TPromise { + return this.process.session.stackTrace({ threadId: this.threadId, startFrame, levels }).then(response => { + if (!response || !response.body) { + return []; + } + + if (this.stoppedDetails) { + this.stoppedDetails.totalFrames = response.body.totalFrames; + } + + return response.body.stackFrames.map((rsf, index) => { + let source = new Source(rsf.source); + if (this.process.sources.has(source.uri.toString())) { + source = this.process.sources.get(source.uri.toString()); + } else { + this.process.sources.set(source.uri.toString(), source); + } + + return new StackFrame(this, rsf.id, source, rsf.name, rsf.presentationHint, new Range( + rsf.line, + rsf.column, + rsf.endLine, + rsf.endColumn + ), startFrame + index); + }); + }, (err: Error) => { + if (this.stoppedDetails) { + this.stoppedDetails.framesErrorMessage = err.message; + } + + return []; + }); + } + + /** + * Returns exception info promise if the exception was thrown, otherwise null + */ + public get exceptionInfo(): TPromise { + const session = this.process.session; + if (this.stoppedDetails && this.stoppedDetails.reason === 'exception') { + if (!session.capabilities.supportsExceptionInfoRequest) { + return TPromise.as({ + description: this.stoppedDetails.text, + breakMode: null + }); + } + + return session.exceptionInfo({ threadId: this.threadId }).then(exception => { + if (!exception) { + return null; + } + + return { + id: exception.body.exceptionId, + description: exception.body.description, + breakMode: exception.body.breakMode, + details: exception.body.details + }; + }); + } + + return TPromise.as(null); + } + + public next(): TPromise { + return this.process.session.next({ threadId: this.threadId }); + } + + public stepIn(): TPromise { + return this.process.session.stepIn({ threadId: this.threadId }); + } + + public stepOut(): TPromise { + return this.process.session.stepOut({ threadId: this.threadId }); + } + + public stepBack(): TPromise { + return this.process.session.stepBack({ threadId: this.threadId }); + } + + public continue(): TPromise { + return this.process.session.continue({ threadId: this.threadId }); + } + + public pause(): TPromise { + return this.process.session.pause({ threadId: this.threadId }); + } + + public reverseContinue(): TPromise { + return this.process.session.reverseContinue({ threadId: this.threadId }); + } +} + +export class Process implements IProcess { + + public sources: Map; + private threads: Map; + private inactive = true; + + constructor(public configuration: IConfig, private _session: ISession & ITreeElement) { + this.threads = new Map(); + this.sources = new Map(); + this._session.onDidInitialize(() => this.inactive = false); + } + + public get session(): ISession { + return this._session; + } + + public getName(includeRoot: boolean): string { + return includeRoot ? `${this.configuration.name} (${paths.basename(this.session.root.fsPath)})` : this.configuration.name; + } + + public get state(): ProcessState { + if (this.inactive) { + return ProcessState.INACTIVE; + } + + return this.configuration.type === 'attach' ? ProcessState.ATTACH : ProcessState.LAUNCH; + } + + public getThread(threadId: number): Thread { + return this.threads.get(threadId); + } + + public getAllThreads(): IThread[] { + const result = []; + this.threads.forEach(t => result.push(t)); + return result; + } + + public getId(): string { + return this._session.getId(); + } + + public rawUpdate(data: IRawModelUpdate): void { + + if (data.thread && !this.threads.has(data.threadId)) { + // A new thread came in, initialize it. + this.threads.set(data.threadId, new Thread(this, data.thread.name, data.thread.id)); + } else if (data.thread && data.thread.name) { + // Just the thread name got updated #18244 + this.threads.get(data.threadId).name = data.thread.name; + } + + if (data.stoppedDetails) { + // Set the availability of the threads' callstacks depending on + // whether the thread is stopped or not + if (data.allThreadsStopped) { + this.threads.forEach(thread => { + thread.stoppedDetails = thread.threadId === data.threadId ? data.stoppedDetails : { reason: undefined }; + thread.stopped = true; + thread.clearCallStack(); + }); + } else if (this.threads.has(data.threadId)) { + // One thread is stopped, only update that thread. + const thread = this.threads.get(data.threadId); + thread.stoppedDetails = data.stoppedDetails; + thread.clearCallStack(); + thread.stopped = true; + } + } + } + + public clearThreads(removeThreads: boolean, reference: number = undefined): void { + if (reference) { + if (this.threads.has(reference)) { + const thread = this.threads.get(reference); + thread.clearCallStack(); + thread.stoppedDetails = undefined; + thread.stopped = false; + + if (removeThreads) { + this.threads.delete(reference); + } + } + } else { + this.threads.forEach(thread => { + thread.clearCallStack(); + thread.stoppedDetails = undefined; + thread.stopped = false; + }); + + if (removeThreads) { + this.threads.clear(); + ExpressionContainer.allValues.clear(); + } + } + } + + public completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise { + if (!this.session.capabilities.supportsCompletionsRequest) { + return TPromise.as([]); + } + + return this.session.completions({ + frameId, + text, + column: position.column, + line: position.lineNumber + }).then(response => { + const result: ISuggestion[] = []; + if (response && response.body && response.body.targets) { + response.body.targets.forEach(item => { + if (item && item.label) { + result.push({ + label: item.label, + insertText: item.text || item.label, + type: item.type, + filterText: item.start && item.length && text.substr(item.start, item.length).concat(item.label), + overwriteBefore: item.length || overwriteBefore + }); + } + }); + } + + return result; + }, err => []); + } +} + +export class Breakpoint implements IBreakpoint { + + public verified: boolean; + public idFromAdapter: number; + public message: string; + public endLineNumber: number; + public endColumn: number; + private id: string; + + constructor( + public uri: uri, + public lineNumber: number, + public column: number, + public enabled: boolean, + public condition: string, + public hitCondition: string, + ) { + if (enabled === undefined) { + this.enabled = true; + } + this.verified = false; + this.id = generateUuid(); + } + + public getId(): string { + return this.id; + } +} + +export class FunctionBreakpoint implements IFunctionBreakpoint { + + private id: string; + public verified: boolean; + public idFromAdapter: number; + + constructor(public name: string, public enabled: boolean, public hitCondition: string) { + this.verified = false; + this.id = generateUuid(); + } + + public getId(): string { + return this.id; + } +} + +export class ExceptionBreakpoint implements IExceptionBreakpoint { + + private id: string; + + constructor(public filter: string, public label: string, public enabled: boolean) { + this.id = generateUuid(); + } + + public getId(): string { + return this.id; + } +} + +export class ThreadAndProcessIds implements ITreeElement { + constructor(public processId: string, public threadId: number) { } + + public getId(): string { + return `${this.processId}:${this.threadId}`; + } +} + +export class Model implements IModel { + + private processes: Process[]; + private toDispose: lifecycle.IDisposable[]; + private replElements: IReplElement[]; + private schedulers = new Map(); + private _onDidChangeBreakpoints: Emitter; + private _onDidChangeCallStack: Emitter; + private _onDidChangeWatchExpressions: Emitter; + private _onDidChangeREPLElements: Emitter; + + constructor( + private breakpoints: Breakpoint[], + private breakpointsActivated: boolean, + private functionBreakpoints: FunctionBreakpoint[], + private exceptionBreakpoints: ExceptionBreakpoint[], + private watchExpressions: Expression[] + ) { + this.processes = []; + this.replElements = []; + this.toDispose = []; + this._onDidChangeBreakpoints = new Emitter(); + this._onDidChangeCallStack = new Emitter(); + this._onDidChangeWatchExpressions = new Emitter(); + this._onDidChangeREPLElements = new Emitter(); + } + + public getId(): string { + return 'root'; + } + + public getProcesses(): Process[] { + return this.processes; + } + + public addProcess(configuration: IConfig, session: ISession & ITreeElement): Process { + const process = new Process(configuration, session); + this.processes.push(process); + + return process; + } + + public removeProcess(id: string): void { + this.processes = this.processes.filter(p => p.getId() !== id); + this._onDidChangeCallStack.fire(); + } + + public get onDidChangeBreakpoints(): Event { + return this._onDidChangeBreakpoints.event; + } + + public get onDidChangeCallStack(): Event { + return this._onDidChangeCallStack.event; + } + + public get onDidChangeWatchExpressions(): Event { + return this._onDidChangeWatchExpressions.event; + } + + public get onDidChangeReplElements(): Event { + return this._onDidChangeREPLElements.event; + } + + public rawUpdate(data: IRawModelUpdate): void { + let process = this.processes.filter(p => p.getId() === data.sessionId).pop(); + if (process) { + process.rawUpdate(data); + this._onDidChangeCallStack.fire(); + } + } + + public clearThreads(id: string, removeThreads: boolean, reference: number = undefined): void { + const process = this.processes.filter(p => p.getId() === id).pop(); + this.schedulers.forEach(scheduler => scheduler.dispose()); + this.schedulers.clear(); + + if (process) { + process.clearThreads(removeThreads, reference); + this._onDidChangeCallStack.fire(); + } + } + + public fetchCallStack(thread: Thread): TPromise { + if (thread.process.session.capabilities.supportsDelayedStackTraceLoading) { + // For improved performance load the first stack frame and then load the rest async. + return thread.fetchCallStack(1).then(() => { + if (!this.schedulers.has(thread.getId())) { + this.schedulers.set(thread.getId(), new RunOnceScheduler(() => { + thread.fetchCallStack(19).done(() => this._onDidChangeCallStack.fire(), errors.onUnexpectedError); + }, 420)); + } + + this.schedulers.get(thread.getId()).schedule(); + this._onDidChangeCallStack.fire(); + }); + } + + return thread.fetchCallStack(); + } + + public getBreakpoints(): Breakpoint[] { + return this.breakpoints; + } + + public getFunctionBreakpoints(): IFunctionBreakpoint[] { + return this.functionBreakpoints; + } + + public getExceptionBreakpoints(): IExceptionBreakpoint[] { + return this.exceptionBreakpoints; + } + + public setExceptionBreakpoints(data: DebugProtocol.ExceptionBreakpointsFilter[]): void { + if (data) { + this.exceptionBreakpoints = data.map(d => { + const ebp = this.exceptionBreakpoints.filter(ebp => ebp.filter === d.filter).pop(); + return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : d.default); + }); + } + } + + public areBreakpointsActivated(): boolean { + return this.breakpointsActivated; + } + + public setBreakpointsActivated(activated: boolean): void { + this.breakpointsActivated = activated; + this._onDidChangeBreakpoints.fire(); + } + + public addBreakpoints(uri: uri, rawData: IRawBreakpoint[]): void { + this.breakpoints = this.breakpoints.concat(rawData.map(rawBp => + new Breakpoint(uri, rawBp.lineNumber, rawBp.column, rawBp.enabled, rawBp.condition, rawBp.hitCondition))); + this.breakpointsActivated = true; + this.breakpoints = distinct(this.breakpoints, bp => `${bp.uri.toString()}:${bp.lineNumber}:${bp.column}`); + this._onDidChangeBreakpoints.fire(); + } + + public removeBreakpoints(toRemove: IBreakpoint[]): void { + this.breakpoints = this.breakpoints.filter(bp => !toRemove.some(toRemove => toRemove.getId() === bp.getId())); + this._onDidChangeBreakpoints.fire(); + } + + public updateBreakpoints(data: { [id: string]: DebugProtocol.Breakpoint }): void { + this.breakpoints.forEach(bp => { + const bpData = data[bp.getId()]; + if (bpData) { + bp.lineNumber = bpData.line ? bpData.line : bp.lineNumber; + bp.endLineNumber = bpData.endLine; + bp.column = bpData.column; + bp.endColumn = bpData.endColumn; + bp.verified = bpData.verified; + bp.idFromAdapter = bpData.id; + bp.message = bpData.message; + } + }); + this.breakpoints = distinct(this.breakpoints, bp => `${bp.uri.toString()}:${bp.lineNumber}:${bp.column}`); + + this._onDidChangeBreakpoints.fire(); + } + + public setEnablement(element: IEnablement, enable: boolean): void { + element.enabled = enable; + if (element instanceof Breakpoint && !element.enabled) { + const breakpoint = element; + breakpoint.verified = false; + } + + this._onDidChangeBreakpoints.fire(); + } + + public enableOrDisableAllBreakpoints(enable: boolean): void { + this.breakpoints.forEach(bp => { + bp.enabled = enable; + if (!enable) { + bp.verified = false; + } + }); + this.exceptionBreakpoints.forEach(ebp => ebp.enabled = enable); + this.functionBreakpoints.forEach(fbp => fbp.enabled = enable); + + this._onDidChangeBreakpoints.fire(); + } + + public addFunctionBreakpoint(functionName: string): void { + this.functionBreakpoints.push(new FunctionBreakpoint(functionName, true, null)); + this._onDidChangeBreakpoints.fire(); + } + + public updateFunctionBreakpoints(data: { [id: string]: { name?: string, verified?: boolean; id?: number; hitCondition?: string } }): void { + this.functionBreakpoints.forEach(fbp => { + const fbpData = data[fbp.getId()]; + if (fbpData) { + fbp.name = fbpData.name || fbp.name; + fbp.verified = fbpData.verified; + fbp.idFromAdapter = fbpData.id; + fbp.hitCondition = fbpData.hitCondition; + } + }); + + this._onDidChangeBreakpoints.fire(); + } + + public removeFunctionBreakpoints(id?: string): void { + this.functionBreakpoints = id ? this.functionBreakpoints.filter(fbp => fbp.getId() !== id) : []; + this._onDidChangeBreakpoints.fire(); + } + + public getReplElements(): IReplElement[] { + return this.replElements; + } + + public addReplExpression(process: IProcess, stackFrame: IStackFrame, name: string): TPromise { + const expression = new Expression(name); + this.addReplElements([expression]); + return expression.evaluate(process, stackFrame, 'repl') + .then(() => this._onDidChangeREPLElements.fire()); + } + + public appendToRepl(output: string | IExpression, severity: severity): void { + if (typeof output === 'string') { + const previousOutput = this.replElements.length && (this.replElements[this.replElements.length - 1] as OutputElement); + + const toAdd = output.split('\n').map(line => new OutputElement(line, severity)); + if (previousOutput instanceof OutputElement && severity === previousOutput.severity && toAdd.length) { + previousOutput.value += toAdd.shift().value; + } + if (previousOutput && previousOutput.value === '' && previousOutput.severity !== severity) { + // remove potential empty lines between different output types + this.replElements.pop(); + } + this.addReplElements(toAdd); + } else { + // TODO@Isidor hack, we should introduce a new type which is an output that can fetch children like an expression + (output).severity = severity; + this.addReplElements([output]); + } + + this._onDidChangeREPLElements.fire(); + } + + private addReplElements(newElements: IReplElement[]): void { + this.replElements.push(...newElements); + if (this.replElements.length > MAX_REPL_LENGTH) { + this.replElements.splice(0, this.replElements.length - MAX_REPL_LENGTH); + } + } + + public removeReplExpressions(): void { + if (this.replElements.length > 0) { + this.replElements = []; + this._onDidChangeREPLElements.fire(); + } + } + + public getWatchExpressions(): Expression[] { + return this.watchExpressions; + } + + public addWatchExpression(process: IProcess, stackFrame: IStackFrame, name: string): TPromise { + const we = new Expression(name); + this.watchExpressions.push(we); + if (!name) { + this._onDidChangeWatchExpressions.fire(we); + return TPromise.as(null); + } + + return this.evaluateWatchExpressions(process, stackFrame, we.getId()); + } + + public renameWatchExpression(process: IProcess, stackFrame: IStackFrame, id: string, newName: string): TPromise { + const filtered = this.watchExpressions.filter(we => we.getId() === id); + if (filtered.length === 1) { + filtered[0].name = newName; + // Evaluate all watch expressions again since the new watch expression might have changed some. + return this.evaluateWatchExpressions(process, stackFrame).then(() => { + this._onDidChangeWatchExpressions.fire(filtered[0]); + }); + } + + return TPromise.as(null); + } + + public evaluateWatchExpressions(process: IProcess, stackFrame: IStackFrame, id: string = null): TPromise { + if (id) { + const filtered = this.watchExpressions.filter(we => we.getId() === id); + if (filtered.length !== 1) { + return TPromise.as(null); + } + + return filtered[0].evaluate(process, stackFrame, 'watch').then(() => { + this._onDidChangeWatchExpressions.fire(filtered[0]); + }); + } + + return TPromise.join(this.watchExpressions.map(we => we.evaluate(process, stackFrame, 'watch'))).then(() => { + this._onDidChangeWatchExpressions.fire(); + }); + } + + public removeWatchExpressions(id: string = null): void { + this.watchExpressions = id ? this.watchExpressions.filter(we => we.getId() !== id) : []; + this._onDidChangeWatchExpressions.fire(); + } + + public moveWatchExpression(id: string, position: number): void { + const we = this.watchExpressions.filter(we => we.getId() === id).pop(); + this.watchExpressions = this.watchExpressions.filter(we => we.getId() !== id); + this.watchExpressions = this.watchExpressions.slice(0, position).concat(we, this.watchExpressions.slice(position)); + + this._onDidChangeWatchExpressions.fire(); + } + + public sourceIsNotAvailable(uri: uri): void { + this.processes.forEach(p => { + if (p.sources.has(uri.toString())) { + p.sources.get(uri.toString()).available = false; + } + }); + this._onDidChangeCallStack.fire(); + } + + public dispose(): void { + this.toDispose = lifecycle.dispose(this.toDispose); + } +} diff --git a/src/vs/workbench/parts/debug/common/debugProtocol.d.ts b/src/vs/workbench/parts/debug/common/debugProtocol.d.ts new file mode 100644 index 0000000000..b8a00d9b87 --- /dev/null +++ b/src/vs/workbench/parts/debug/common/debugProtocol.d.ts @@ -0,0 +1,1461 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** Declaration module describing the VS Code debug protocol. + Auto-generated from json schema. Do not edit manually. +*/ +declare module DebugProtocol { + + /** Base class of requests, responses, and events. */ + export interface ProtocolMessage { + /** Sequence number. */ + seq: number; + /** Message type. + Values: 'request', 'response', 'event', etc. + */ + type: string; + } + + /** A client or server-initiated request. */ + export interface Request extends ProtocolMessage { + // type: 'request'; + /** The command to execute. */ + command: string; + /** Object containing arguments for the command. */ + arguments?: any; + } + + /** Server-initiated event. */ + export interface Event extends ProtocolMessage { + // type: 'event'; + /** Type of event. */ + event: string; + /** Event-specific information. */ + body?: any; + } + + /** Response to a request. */ + export interface Response extends ProtocolMessage { + // type: 'response'; + /** Sequence number of the corresponding request. */ + request_seq: number; + /** Outcome of the request. */ + success: boolean; + /** The command requested. */ + command: string; + /** Contains error message if success == false. */ + message?: string; + /** Contains request result if success is true and optional error details if success is false. */ + body?: any; + } + + /** Event message for 'initialized' event type. + This event indicates that the debug adapter is ready to accept configuration requests (e.g. SetBreakpointsRequest, SetExceptionBreakpointsRequest). + A debug adapter is expected to send this event when it is ready to accept configuration requests (but not before the InitializeRequest has finished). + The sequence of events/requests is as follows: + - adapters sends InitializedEvent (after the InitializeRequest has returned) + - frontend sends zero or more SetBreakpointsRequest + - frontend sends one SetFunctionBreakpointsRequest + - frontend sends a SetExceptionBreakpointsRequest if one or more exceptionBreakpointFilters have been defined (or if supportsConfigurationDoneRequest is not defined or false) + - frontend sends other future configuration requests + - frontend sends one ConfigurationDoneRequest to indicate the end of the configuration + */ + export interface InitializedEvent extends Event { + // event: 'initialized'; + } + + /** Event message for 'stopped' event type. + The event indicates that the execution of the debuggee has stopped due to some condition. + This can be caused by a break point previously set, a stepping action has completed, by executing a debugger statement etc. + */ + export interface StoppedEvent extends Event { + // event: 'stopped'; + body: { + /** The reason for the event. + For backward compatibility this string is shown in the UI if the 'description' attribute is missing (but it must not be translated). + Values: 'step', 'breakpoint', 'exception', 'pause', 'entry', etc. + */ + reason: string; + /** The full reason for the event, e.g. 'Paused on exception'. This string is shown in the UI as is. */ + description?: string; + /** The thread which was stopped. */ + threadId?: number; + /** Additional information. E.g. if reason is 'exception', text contains the exception name. This string is shown in the UI. */ + text?: string; + /** If allThreadsStopped is true, a debug adapter can announce that all threads have stopped. + * The client should use this information to enable that all threads can be expanded to access their stacktraces. + * If the attribute is missing or false, only the thread with the given threadId can be expanded. + */ + allThreadsStopped?: boolean; + }; + } + + /** Event message for 'continued' event type. + The event indicates that the execution of the debuggee has continued. + Please note: a debug adapter is not expected to send this event in response to a request that implies that execution continues, e.g. 'launch' or 'continue'. + It is only necessary to send a ContinuedEvent if there was no previous request that implied this. + */ + export interface ContinuedEvent extends Event { + // event: 'continued'; + body: { + /** The thread which was continued. */ + threadId: number; + /** If allThreadsContinued is true, a debug adapter can announce that all threads have continued. */ + allThreadsContinued?: boolean; + }; + } + + /** Event message for 'exited' event type. + The event indicates that the debuggee has exited. + */ + export interface ExitedEvent extends Event { + // event: 'exited'; + body: { + /** The exit code returned from the debuggee. */ + exitCode: number; + }; + } + + /** Event message for 'terminated' event types. + The event indicates that debugging of the debuggee has terminated. + */ + export interface TerminatedEvent extends Event { + // event: 'terminated'; + body?: { + /** A debug adapter may set 'restart' to true (or to an arbitrary object) to request that the front end restarts the session. + The value is not interpreted by the client and passed unmodified as an attribute '__restart' to the launchRequest. + */ + restart?: any; + }; + } + + /** Event message for 'thread' event type. + The event indicates that a thread has started or exited. + */ + export interface ThreadEvent extends Event { + // event: 'thread'; + body: { + /** The reason for the event. + Values: 'started', 'exited', etc. + */ + reason: string; + /** The identifier of the thread. */ + threadId: number; + }; + } + + /** Event message for 'output' event type. + The event indicates that the target has produced some output. + */ + export interface OutputEvent extends Event { + // event: 'output'; + body: { + /** The output category. If not specified, 'console' is assumed. + Values: 'console', 'stdout', 'stderr', 'telemetry', etc. + */ + category?: string; + /** The output to report. */ + output: string; + /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing variablesReference to the VariablesRequest. */ + variablesReference?: number; + /** An optional source location where the output was produced. */ + source?: Source; + /** An optional source location line where the output was produced. */ + line?: number; + /** An optional source location column where the output was produced. */ + column?: number; + /** Optional data to report. For the 'telemetry' category the data will be sent to telemetry, for the other categories the data is shown in JSON format. */ + data?: any; + }; + } + + /** Event message for 'breakpoint' event type. + The event indicates that some information about a breakpoint has changed. + */ + export interface BreakpointEvent extends Event { + // event: 'breakpoint'; + body: { + /** The reason for the event. + Values: 'changed', 'new', etc. + */ + reason: string; + /** The breakpoint. */ + breakpoint: Breakpoint; + }; + } + + /** Event message for 'module' event type. + The event indicates that some information about a module has changed. + */ + export interface ModuleEvent extends Event { + // event: 'module'; + body: { + /** The reason for the event. */ + reason: 'new' | 'changed' | 'removed'; + /** The new, changed, or removed module. In case of 'removed' only the module id is used. */ + module: Module; + }; + } + + /** Event message for 'loadedSource' event type. + The event indicates that some source has been added, changed, or removed from the set of all loaded sources. + */ + export interface LoadedSourceEvent extends Event { + // event: 'loadedSource'; + body: { + /** The reason for the event. */ + reason: 'new' | 'changed' | 'removed'; + /** The new, changed, or removed source. */ + source: Source; + }; + } + + /** Event message for 'process' event type. + The event indicates that the debugger has begun debugging a new process. Either one that it has launched, or one that it has attached to. + */ + export interface ProcessEvent extends Event { + // event: 'process'; + body: { + /** The logical name of the process. This is usually the full path to process's executable file. Example: /home/example/myproj/program.js. */ + name: string; + /** The system process id of the debugged process. This property will be missing for non-system processes. */ + systemProcessId?: number; + /** If true, the process is running on the same computer as the debug adapter. */ + isLocalProcess?: boolean; + /** Describes how the debug engine started debugging this process. + 'launch': Process was launched under the debugger. + 'attach': Debugger attached to an existing process. + 'attachForSuspendedLaunch': A project launcher component has launched a new process in a suspended state and then asked the debugger to attach. + */ + startMethod?: 'launch' | 'attach' | 'attachForSuspendedLaunch'; + }; + } + + /** runInTerminal request; value of command field is 'runInTerminal'. + With this request a debug adapter can run a command in a terminal. + */ + export interface RunInTerminalRequest extends Request { + // command: 'runInTerminal'; + arguments: RunInTerminalRequestArguments; + } + + /** Arguments for 'runInTerminal' request. */ + export interface RunInTerminalRequestArguments { + /** What kind of terminal to launch. */ + kind?: 'integrated' | 'external'; + /** Optional title of the terminal. */ + title?: string; + /** Working directory of the command. */ + cwd: string; + /** List of arguments. The first argument is the command to run. */ + args: string[]; + /** Environment key-value pairs that are added to the default environment. */ + env?: { [key: string]: string; }; + } + + /** Response to Initialize request. */ + export interface RunInTerminalResponse extends Response { + body: { + /** The process ID. */ + processId?: number; + }; + } + + /** On error that is whenever 'success' is false, the body can provide more details. */ + export interface ErrorResponse extends Response { + body: { + /** An optional, structured error message. */ + error?: Message; + }; + } + + /** Initialize request; value of command field is 'initialize'. */ + export interface InitializeRequest extends Request { + // command: 'initialize'; + arguments: InitializeRequestArguments; + } + + /** Arguments for 'initialize' request. */ + export interface InitializeRequestArguments { + /** The ID of the (frontend) client using this adapter. */ + clientID?: string; + /** The ID of the debug adapter. */ + adapterID: string; + /** If true all line numbers are 1-based (default). */ + linesStartAt1?: boolean; + /** If true all column numbers are 1-based (default). */ + columnsStartAt1?: boolean; + /** Determines in what format paths are specified. The default is 'path', which is the native format. + Values: 'path', 'uri', etc. + */ + pathFormat?: string; + /** Client supports the optional type attribute for variables. */ + supportsVariableType?: boolean; + /** Client supports the paging of variables. */ + supportsVariablePaging?: boolean; + /** Client supports the runInTerminal request. */ + supportsRunInTerminalRequest?: boolean; + } + + /** Response to 'initialize' request. */ + export interface InitializeResponse extends Response { + /** The capabilities of this debug adapter. */ + body?: Capabilities; + } + + /** ConfigurationDone request; value of command field is 'configurationDone'. + The client of the debug protocol must send this request at the end of the sequence of configuration requests (which was started by the InitializedEvent). + */ + export interface ConfigurationDoneRequest extends Request { + // command: 'configurationDone'; + arguments?: ConfigurationDoneArguments; + } + + /** Arguments for 'configurationDone' request. + The configurationDone request has no standardized attributes. + */ + export interface ConfigurationDoneArguments { + } + + /** Response to 'configurationDone' request. This is just an acknowledgement, so no body field is required. */ + export interface ConfigurationDoneResponse extends Response { + } + + /** Launch request; value of command field is 'launch'. */ + export interface LaunchRequest extends Request { + // command: 'launch'; + arguments: LaunchRequestArguments; + } + + /** Arguments for 'launch' request. */ + export interface LaunchRequestArguments { + /** If noDebug is true the launch request should launch the program without enabling debugging. */ + noDebug?: boolean; + } + + /** Response to 'launch' request. This is just an acknowledgement, so no body field is required. */ + export interface LaunchResponse extends Response { + } + + /** Attach request; value of command field is 'attach'. */ + export interface AttachRequest extends Request { + // command: 'attach'; + arguments: AttachRequestArguments; + } + + /** Arguments for 'attach' request. + The attach request has no standardized attributes. + */ + export interface AttachRequestArguments { + } + + /** Response to 'attach' request. This is just an acknowledgement, so no body field is required. */ + export interface AttachResponse extends Response { + } + + /** Restart request; value of command field is 'restart'. + Restarts a debug session. If the capability 'supportsRestartRequest' is missing or has the value false, + the client will implement 'restart' by terminating the debug adapter first and then launching it anew. + A debug adapter can override this default behaviour by implementing a restart request + and setting the capability 'supportsRestartRequest' to true. + */ + export interface RestartRequest extends Request { + // command: 'restart'; + arguments?: RestartArguments; + } + + /** Arguments for 'restart' request. + The restart request has no standardized attributes. + */ + export interface RestartArguments { + } + + /** Response to 'restart' request. This is just an acknowledgement, so no body field is required. */ + export interface RestartResponse extends Response { + } + + /** Disconnect request; value of command field is 'disconnect'. */ + export interface DisconnectRequest extends Request { + // command: 'disconnect'; + arguments?: DisconnectArguments; + } + + /** Arguments for 'disconnect' request. */ + export interface DisconnectArguments { + /** Indicates whether the debuggee should be terminated when the debugger is disconnected. + If unspecified, the debug adapter is free to do whatever it thinks is best. + A client can only rely on this attribute being properly honored if a debug adapter returns true for the 'supportTerminateDebuggee' capability. + */ + terminateDebuggee?: boolean; + } + + /** Response to 'disconnect' request. This is just an acknowledgement, so no body field is required. */ + export interface DisconnectResponse extends Response { + } + + /** SetBreakpoints request; value of command field is 'setBreakpoints'. + Sets multiple breakpoints for a single source and clears all previous breakpoints in that source. + To clear all breakpoint for a source, specify an empty array. + When a breakpoint is hit, a StoppedEvent (event type 'breakpoint') is generated. + */ + export interface SetBreakpointsRequest extends Request { + // command: 'setBreakpoints'; + arguments: SetBreakpointsArguments; + } + + /** Arguments for 'setBreakpoints' request. */ + export interface SetBreakpointsArguments { + /** The source location of the breakpoints; either source.path or source.reference must be specified. */ + source: Source; + /** The code locations of the breakpoints. */ + breakpoints?: SourceBreakpoint[]; + /** Deprecated: The code locations of the breakpoints. */ + lines?: number[]; + /** A value of true indicates that the underlying source has been modified which results in new breakpoint locations. */ + sourceModified?: boolean; + } + + /** Response to 'setBreakpoints' request. + Returned is information about each breakpoint created by this request. + This includes the actual code location and whether the breakpoint could be verified. + The breakpoints returned are in the same order as the elements of the 'breakpoints' + (or the deprecated 'lines') in the SetBreakpointsArguments. + */ + export interface SetBreakpointsResponse extends Response { + body: { + /** Information about the breakpoints. The array elements are in the same order as the elements of the 'breakpoints' (or the deprecated 'lines') in the SetBreakpointsArguments. */ + breakpoints: Breakpoint[]; + }; + } + + /** SetFunctionBreakpoints request; value of command field is 'setFunctionBreakpoints'. + Sets multiple function breakpoints and clears all previous function breakpoints. + To clear all function breakpoint, specify an empty array. + When a function breakpoint is hit, a StoppedEvent (event type 'function breakpoint') is generated. + */ + export interface SetFunctionBreakpointsRequest extends Request { + // command: 'setFunctionBreakpoints'; + arguments: SetFunctionBreakpointsArguments; + } + + /** Arguments for 'setFunctionBreakpoints' request. */ + export interface SetFunctionBreakpointsArguments { + /** The function names of the breakpoints. */ + breakpoints: FunctionBreakpoint[]; + } + + /** Response to 'setFunctionBreakpoints' request. + Returned is information about each breakpoint created by this request. + */ + export interface SetFunctionBreakpointsResponse extends Response { + body: { + /** Information about the breakpoints. The array elements correspond to the elements of the 'breakpoints' array. */ + breakpoints: Breakpoint[]; + }; + } + + /** SetExceptionBreakpoints request; value of command field is 'setExceptionBreakpoints'. + The request configures the debuggers response to thrown exceptions. If an exception is configured to break, a StoppedEvent is fired (event type 'exception'). + */ + export interface SetExceptionBreakpointsRequest extends Request { + // command: 'setExceptionBreakpoints'; + arguments: SetExceptionBreakpointsArguments; + } + + /** Arguments for 'setExceptionBreakpoints' request. */ + export interface SetExceptionBreakpointsArguments { + /** IDs of checked exception options. The set of IDs is returned via the 'exceptionBreakpointFilters' capability. */ + filters: string[]; + /** Configuration options for selected exceptions. */ + exceptionOptions?: ExceptionOptions[]; + } + + /** Response to 'setExceptionBreakpoints' request. This is just an acknowledgement, so no body field is required. */ + export interface SetExceptionBreakpointsResponse extends Response { + } + + /** Continue request; value of command field is 'continue'. + The request starts the debuggee to run again. + */ + export interface ContinueRequest extends Request { + // command: 'continue'; + arguments: ContinueArguments; + } + + /** Arguments for 'continue' request. */ + export interface ContinueArguments { + /** Continue execution for the specified thread (if possible). If the backend cannot continue on a single thread but will continue on all threads, it should set the allThreadsContinued attribute in the response to true. */ + threadId: number; + } + + /** Response to 'continue' request. */ + export interface ContinueResponse extends Response { + body: { + /** If true, the continue request has ignored the specified thread and continued all threads instead. If this attribute is missing a value of 'true' is assumed for backward compatibility. */ + allThreadsContinued?: boolean; + }; + } + + /** Next request; value of command field is 'next'. + The request starts the debuggee to run again for one step. + The debug adapter first sends the NextResponse and then a StoppedEvent (event type 'step') after the step has completed. + */ + export interface NextRequest extends Request { + // command: 'next'; + arguments: NextArguments; + } + + /** Arguments for 'next' request. */ + export interface NextArguments { + /** Execute 'next' for this thread. */ + threadId: number; + } + + /** Response to 'next' request. This is just an acknowledgement, so no body field is required. */ + export interface NextResponse extends Response { + } + + /** StepIn request; value of command field is 'stepIn'. + The request starts the debuggee to step into a function/method if possible. + If it cannot step into a target, 'stepIn' behaves like 'next'. + The debug adapter first sends the StepInResponse and then a StoppedEvent (event type 'step') after the step has completed. + If there are multiple function/method calls (or other targets) on the source line, + the optional argument 'targetId' can be used to control into which target the 'stepIn' should occur. + The list of possible targets for a given source line can be retrieved via the 'stepInTargets' request. + */ + export interface StepInRequest extends Request { + // command: 'stepIn'; + arguments: StepInArguments; + } + + /** Arguments for 'stepIn' request. */ + export interface StepInArguments { + /** Execute 'stepIn' for this thread. */ + threadId: number; + /** Optional id of the target to step into. */ + targetId?: number; + } + + /** Response to 'stepIn' request. This is just an acknowledgement, so no body field is required. */ + export interface StepInResponse extends Response { + } + + /** StepOut request; value of command field is 'stepOut'. + The request starts the debuggee to run again for one step. + The debug adapter first sends the StepOutResponse and then a StoppedEvent (event type 'step') after the step has completed. + */ + export interface StepOutRequest extends Request { + // command: 'stepOut'; + arguments: StepOutArguments; + } + + /** Arguments for 'stepOut' request. */ + export interface StepOutArguments { + /** Execute 'stepOut' for this thread. */ + threadId: number; + } + + /** Response to 'stepOut' request. This is just an acknowledgement, so no body field is required. */ + export interface StepOutResponse extends Response { + } + + /** StepBack request; value of command field is 'stepBack'. + The request starts the debuggee to run one step backwards. + The debug adapter first sends the StepBackResponse and then a StoppedEvent (event type 'step') after the step has completed. Clients should only call this request if the capability supportsStepBack is true. + */ + export interface StepBackRequest extends Request { + // command: 'stepBack'; + arguments: StepBackArguments; + } + + /** Arguments for 'stepBack' request. */ + export interface StepBackArguments { + /** Exceute 'stepBack' for this thread. */ + threadId: number; + } + + /** Response to 'stepBack' request. This is just an acknowledgement, so no body field is required. */ + export interface StepBackResponse extends Response { + } + + /** ReverseContinue request; value of command field is 'reverseContinue'. + The request starts the debuggee to run backward. Clients should only call this request if the capability supportsStepBack is true. + */ + export interface ReverseContinueRequest extends Request { + // command: 'reverseContinue'; + arguments: ReverseContinueArguments; + } + + /** Arguments for 'reverseContinue' request. */ + export interface ReverseContinueArguments { + /** Exceute 'reverseContinue' for this thread. */ + threadId: number; + } + + /** Response to 'reverseContinue' request. This is just an acknowledgement, so no body field is required. */ + export interface ReverseContinueResponse extends Response { + } + + /** RestartFrame request; value of command field is 'restartFrame'. + The request restarts execution of the specified stackframe. + The debug adapter first sends the RestartFrameResponse and then a StoppedEvent (event type 'restart') after the restart has completed. + */ + export interface RestartFrameRequest extends Request { + // command: 'restartFrame'; + arguments: RestartFrameArguments; + } + + /** Arguments for 'restartFrame' request. */ + export interface RestartFrameArguments { + /** Restart this stackframe. */ + frameId: number; + } + + /** Response to 'restartFrame' request. This is just an acknowledgement, so no body field is required. */ + export interface RestartFrameResponse extends Response { + } + + /** Goto request; value of command field is 'goto'. + The request sets the location where the debuggee will continue to run. + This makes it possible to skip the execution of code or to executed code again. + The code between the current location and the goto target is not executed but skipped. + The debug adapter first sends the GotoResponse and then a StoppedEvent (event type 'goto'). + */ + export interface GotoRequest extends Request { + // command: 'goto'; + arguments: GotoArguments; + } + + /** Arguments for 'goto' request. */ + export interface GotoArguments { + /** Set the goto target for this thread. */ + threadId: number; + /** The location where the debuggee will continue to run. */ + targetId: number; + } + + /** Response to 'goto' request. This is just an acknowledgement, so no body field is required. */ + export interface GotoResponse extends Response { + } + + /** Pause request; value of command field is 'pause'. + The request suspenses the debuggee. + The debug adapter first sends the PauseResponse and then a StoppedEvent (event type 'pause') after the thread has been paused successfully. + */ + export interface PauseRequest extends Request { + // command: 'pause'; + arguments: PauseArguments; + } + + /** Arguments for 'pause' request. */ + export interface PauseArguments { + /** Pause execution for this thread. */ + threadId: number; + } + + /** Response to 'pause' request. This is just an acknowledgement, so no body field is required. */ + export interface PauseResponse extends Response { + } + + /** StackTrace request; value of command field is 'stackTrace'. The request returns a stacktrace from the current execution state. */ + export interface StackTraceRequest extends Request { + // command: 'stackTrace'; + arguments: StackTraceArguments; + } + + /** Arguments for 'stackTrace' request. */ + export interface StackTraceArguments { + /** Retrieve the stacktrace for this thread. */ + threadId: number; + /** The index of the first frame to return; if omitted frames start at 0. */ + startFrame?: number; + /** The maximum number of frames to return. If levels is not specified or 0, all frames are returned. */ + levels?: number; + /** Specifies details on how to format the stack frames. */ + format?: StackFrameFormat; + } + + /** Response to 'stackTrace' request. */ + export interface StackTraceResponse extends Response { + body: { + /** The frames of the stackframe. If the array has length zero, there are no stackframes available. + This means that there is no location information available. + */ + stackFrames: StackFrame[]; + /** The total number of frames available. */ + totalFrames?: number; + }; + } + + /** Scopes request; value of command field is 'scopes'. + The request returns the variable scopes for a given stackframe ID. + */ + export interface ScopesRequest extends Request { + // command: 'scopes'; + arguments: ScopesArguments; + } + + /** Arguments for 'scopes' request. */ + export interface ScopesArguments { + /** Retrieve the scopes for this stackframe. */ + frameId: number; + } + + /** Response to 'scopes' request. */ + export interface ScopesResponse extends Response { + body: { + /** The scopes of the stackframe. If the array has length zero, there are no scopes available. */ + scopes: Scope[]; + }; + } + + /** Variables request; value of command field is 'variables'. + Retrieves all child variables for the given variable reference. + An optional filter can be used to limit the fetched children to either named or indexed children. + */ + export interface VariablesRequest extends Request { + // command: 'variables'; + arguments: VariablesArguments; + } + + /** Arguments for 'variables' request. */ + export interface VariablesArguments { + /** The Variable reference. */ + variablesReference: number; + /** Optional filter to limit the child variables to either named or indexed. If ommited, both types are fetched. */ + filter?: 'indexed' | 'named'; + /** The index of the first variable to return; if omitted children start at 0. */ + start?: number; + /** The number of variables to return. If count is missing or 0, all variables are returned. */ + count?: number; + /** Specifies details on how to format the Variable values. */ + format?: ValueFormat; + } + + /** Response to 'variables' request. */ + export interface VariablesResponse extends Response { + body: { + /** All (or a range) of variables for the given variable reference. */ + variables: Variable[]; + }; + } + + /** setVariable request; value of command field is 'setVariable'. + Set the variable with the given name in the variable container to a new value. + */ + export interface SetVariableRequest extends Request { + // command: 'setVariable'; + arguments: SetVariableArguments; + } + + /** Arguments for 'setVariable' request. */ + export interface SetVariableArguments { + /** The reference of the variable container. */ + variablesReference: number; + /** The name of the variable. */ + name: string; + /** The value of the variable. */ + value: string; + /** Specifies details on how to format the response value. */ + format?: ValueFormat; + } + + /** Response to 'setVariable' request. */ + export interface SetVariableResponse extends Response { + body: { + /** The new value of the variable. */ + value: string; + /** The type of the new value. Typically shown in the UI when hovering over the value. */ + type?: string; + /** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ + variablesReference?: number; + /** The number of named child variables. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + */ + namedVariables?: number; + /** The number of indexed child variables. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + */ + indexedVariables?: number; + }; + } + + /** Source request; value of command field is 'source'. + The request retrieves the source code for a given source reference. + */ + export interface SourceRequest extends Request { + // command: 'source'; + arguments: SourceArguments; + } + + /** Arguments for 'source' request. */ + export interface SourceArguments { + /** Specifies the source content to load. Either source.path or source.sourceReference must be specified. */ + source?: Source; + /** The reference to the source. This is the same as source.sourceReference. This is provided for backward compatibility since old backends do not understand the 'source' attribute. */ + sourceReference: number; + } + + /** Response to 'source' request. */ + export interface SourceResponse extends Response { + body: { + /** Content of the source reference. */ + content: string; + /** Optional content type (mime type) of the source. */ + mimeType?: string; + }; + } + + /** Thread request; value of command field is 'threads'. + The request retrieves a list of all threads. + */ + export interface ThreadsRequest extends Request { + // command: 'threads'; + } + + /** Response to 'threads' request. */ + export interface ThreadsResponse extends Response { + body: { + /** All threads. */ + threads: Thread[]; + }; + } + + /** Modules can be retrieved from the debug adapter with the ModulesRequest which can either return all modules or a range of modules to support paging. */ + export interface ModulesRequest extends Request { + // command: 'modules'; + arguments: ModulesArguments; + } + + /** Arguments for 'modules' request. */ + export interface ModulesArguments { + /** The index of the first module to return; if omitted modules start at 0. */ + startModule?: number; + /** The number of modules to return. If moduleCount is not specified or 0, all modules are returned. */ + moduleCount?: number; + } + + /** Response to 'modules' request. */ + export interface ModulesResponse extends Response { + body: { + /** All modules or range of modules. */ + modules: Module[]; + /** The total number of modules available. */ + totalModules?: number; + }; + } + + /** Retrieves the set of all sources currently loaded by the debugged process. */ + export interface LoadedSourcesRequest extends Request { + // command: 'loadedSources'; + arguments?: LoadedSourcesArguments; + } + + /** Arguments for 'loadedSources' request. + The 'loadedSources' request has no standardized arguments. + */ + export interface LoadedSourcesArguments { + } + + /** Response to 'loadedSources' request. */ + export interface LoadedSourcesResponse extends Response { + body: { + /** Set of loaded sources. */ + sources: Source[]; + }; + } + + /** Evaluate request; value of command field is 'evaluate'. + Evaluates the given expression in the context of the top most stack frame. + The expression has access to any variables and arguments that are in scope. + */ + export interface EvaluateRequest extends Request { + // command: 'evaluate'; + arguments: EvaluateArguments; + } + + /** Arguments for 'evaluate' request. */ + export interface EvaluateArguments { + /** The expression to evaluate. */ + expression: string; + /** Evaluate the expression in the scope of this stack frame. If not specified, the expression is evaluated in the global scope. */ + frameId?: number; + /** The context in which the evaluate request is run. + Values: + 'watch': evaluate is run in a watch. + 'repl': evaluate is run from REPL console. + 'hover': evaluate is run from a data hover. + etc. + */ + context?: string; + /** Specifies details on how to format the Evaluate result. */ + format?: ValueFormat; + } + + /** Response to 'evaluate' request. */ + export interface EvaluateResponse extends Response { + body: { + /** The result of the evaluate request. */ + result: string; + /** The optional type of the evaluate result. */ + type?: string; + /** Properties of a evaluate result that can be used to determine how to render the result in the UI. */ + presentationHint?: VariablePresentationHint; + /** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ + variablesReference: number; + /** The number of named child variables. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + */ + namedVariables?: number; + /** The number of indexed child variables. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + */ + indexedVariables?: number; + }; + } + + /** StepInTargets request; value of command field is 'stepInTargets'. + This request retrieves the possible stepIn targets for the specified stack frame. + These targets can be used in the 'stepIn' request. + The StepInTargets may only be called if the 'supportsStepInTargetsRequest' capability exists and is true. + */ + export interface StepInTargetsRequest extends Request { + // command: 'stepInTargets'; + arguments: StepInTargetsArguments; + } + + /** Arguments for 'stepInTargets' request. */ + export interface StepInTargetsArguments { + /** The stack frame for which to retrieve the possible stepIn targets. */ + frameId: number; + } + + /** Response to 'stepInTargets' request. */ + export interface StepInTargetsResponse extends Response { + body: { + /** The possible stepIn targets of the specified source location. */ + targets: StepInTarget[]; + }; + } + + /** GotoTargets request; value of command field is 'gotoTargets'. + This request retrieves the possible goto targets for the specified source location. + These targets can be used in the 'goto' request. + The GotoTargets request may only be called if the 'supportsGotoTargetsRequest' capability exists and is true. + */ + export interface GotoTargetsRequest extends Request { + // command: 'gotoTargets'; + arguments: GotoTargetsArguments; + } + + /** Arguments for 'gotoTargets' request. */ + export interface GotoTargetsArguments { + /** The source location for which the goto targets are determined. */ + source: Source; + /** The line location for which the goto targets are determined. */ + line: number; + /** An optional column location for which the goto targets are determined. */ + column?: number; + } + + /** Response to 'gotoTargets' request. */ + export interface GotoTargetsResponse extends Response { + body: { + /** The possible goto targets of the specified location. */ + targets: GotoTarget[]; + }; + } + + /** CompletionsRequest request; value of command field is 'completions'. + Returns a list of possible completions for a given caret position and text. + The CompletionsRequest may only be called if the 'supportsCompletionsRequest' capability exists and is true. + */ + export interface CompletionsRequest extends Request { + // command: 'completions'; + arguments: CompletionsArguments; + } + + /** Arguments for 'completions' request. */ + export interface CompletionsArguments { + /** Returns completions in the scope of this stack frame. If not specified, the completions are returned for the global scope. */ + frameId?: number; + /** One or more source lines. Typically this is the text a user has typed into the debug console before he asked for completion. */ + text: string; + /** The character position for which to determine the completion proposals. */ + column: number; + /** An optional line for which to determine the completion proposals. If missing the first line of the text is assumed. */ + line?: number; + } + + /** Response to 'completions' request. */ + export interface CompletionsResponse extends Response { + body: { + /** The possible completions for . */ + targets: CompletionItem[]; + }; + } + + /** ExceptionInfoRequest request; value of command field is 'exceptionInfo'. + Retrieves the details of the exception that caused the StoppedEvent to be raised. + */ + export interface ExceptionInfoRequest extends Request { + // command: 'exceptionInfo'; + arguments: ExceptionInfoArguments; + } + + /** Arguments for 'exceptionInfo' request. */ + export interface ExceptionInfoArguments { + /** Thread for which exception information should be retrieved. */ + threadId: number; + } + + /** Response to 'exceptionInfo' request. */ + export interface ExceptionInfoResponse extends Response { + body: { + /** ID of the exception that was thrown. */ + exceptionId: string; + /** Descriptive text for the exception provided by the debug adapter. */ + description?: string; + /** Mode that caused the exception notification to be raised. */ + breakMode: ExceptionBreakMode; + /** Detailed information about the exception. */ + details?: ExceptionDetails; + }; + } + + /** Information about the capabilities of a debug adapter. */ + export interface Capabilities { + /** The debug adapter supports the configurationDoneRequest. */ + supportsConfigurationDoneRequest?: boolean; + /** The debug adapter supports function breakpoints. */ + supportsFunctionBreakpoints?: boolean; + /** The debug adapter supports conditional breakpoints. */ + supportsConditionalBreakpoints?: boolean; + /** The debug adapter supports breakpoints that break execution after a specified number of hits. */ + supportsHitConditionalBreakpoints?: boolean; + /** The debug adapter supports a (side effect free) evaluate request for data hovers. */ + supportsEvaluateForHovers?: boolean; + /** Available filters or options for the setExceptionBreakpoints request. */ + exceptionBreakpointFilters?: ExceptionBreakpointsFilter[]; + /** The debug adapter supports stepping back via the stepBack and reverseContinue requests. */ + supportsStepBack?: boolean; + /** The debug adapter supports setting a variable to a value. */ + supportsSetVariable?: boolean; + /** The debug adapter supports restarting a frame. */ + supportsRestartFrame?: boolean; + /** The debug adapter supports the gotoTargetsRequest. */ + supportsGotoTargetsRequest?: boolean; + /** The debug adapter supports the stepInTargetsRequest. */ + supportsStepInTargetsRequest?: boolean; + /** The debug adapter supports the completionsRequest. */ + supportsCompletionsRequest?: boolean; + /** The debug adapter supports the modules request. */ + supportsModulesRequest?: boolean; + /** The set of additional module information exposed by the debug adapter. */ + additionalModuleColumns?: ColumnDescriptor[]; + /** Checksum algorithms supported by the debug adapter. */ + supportedChecksumAlgorithms?: ChecksumAlgorithm[]; + /** The debug adapter supports the RestartRequest. In this case a client should not implement 'restart' by terminating and relaunching the adapter but by calling the RestartRequest. */ + supportsRestartRequest?: boolean; + /** The debug adapter supports 'exceptionOptions' on the setExceptionBreakpoints request. */ + supportsExceptionOptions?: boolean; + /** The debug adapter supports a 'format' attribute on the stackTraceRequest, variablesRequest, and evaluateRequest. */ + supportsValueFormattingOptions?: boolean; + /** The debug adapter supports the exceptionInfo request. */ + supportsExceptionInfoRequest?: boolean; + /** The debug adapter supports the 'terminateDebuggee' attribute on the 'disconnect' request. */ + supportTerminateDebuggee?: boolean; + /** The debug adapter supports the delayed loading of parts of the stack, which requires that both the 'startFrame' and 'levels' arguments and the 'totalFrames' result of the 'StackTrace' request are supported. */ + supportsDelayedStackTraceLoading?: boolean; + /** The debug adapter supports the 'loadedSources' request. */ + supportsLoadedSourcesRequest?: boolean; + } + + /** An ExceptionBreakpointsFilter is shown in the UI as an option for configuring how exceptions are dealt with. */ + export interface ExceptionBreakpointsFilter { + /** The internal ID of the filter. This value is passed to the setExceptionBreakpoints request. */ + filter: string; + /** The name of the filter. This will be shown in the UI. */ + label: string; + /** Initial value of the filter. If not specified a value 'false' is assumed. */ + default?: boolean; + } + + /** A structured message object. Used to return errors from requests. */ + export interface Message { + /** Unique identifier for the message. */ + id: number; + /** A format string for the message. Embedded variables have the form '{name}'. + If variable name starts with an underscore character, the variable does not contain user data (PII) and can be safely used for telemetry purposes. + */ + format: string; + /** An object used as a dictionary for looking up the variables in the format string. */ + variables?: { [key: string]: string; }; + /** If true send to telemetry. */ + sendTelemetry?: boolean; + /** If true show user. */ + showUser?: boolean; + /** An optional url where additional information about this message can be found. */ + url?: string; + /** An optional label that is presented to the user as the UI for opening the url. */ + urlLabel?: string; + } + + /** A Module object represents a row in the modules view. + Two attributes are mandatory: an id identifies a module in the modules view and is used in a ModuleEvent for identifying a module for adding, updating or deleting. + The name is used to minimally render the module in the UI. + + Additional attributes can be added to the module. They will show up in the module View if they have a corresponding ColumnDescriptor. + + To avoid an unnecessary proliferation of additional attributes with similar semantics but different names + we recommend to re-use attributes from the 'recommended' list below first, and only introduce new attributes if nothing appropriate could be found. + */ + export interface Module { + /** Unique identifier for the module. */ + id: number | string; + /** A name of the module. */ + name: string; + /** optional but recommended attributes. + always try to use these first before introducing additional attributes. + + Logical full path to the module. The exact definition is implementation defined, but usually this would be a full path to the on-disk file for the module. + */ + path?: string; + /** True if the module is optimized. */ + isOptimized?: boolean; + /** True if the module is considered 'user code' by a debugger that supports 'Just My Code'. */ + isUserCode?: boolean; + /** Version of Module. */ + version?: string; + /** User understandable description of if symbols were found for the module (ex: 'Symbols Loaded', 'Symbols not found', etc. */ + symbolStatus?: string; + /** Logical full path to the symbol file. The exact definition is implementation defined. */ + symbolFilePath?: string; + /** Module created or modified. */ + dateTimeStamp?: string; + /** Address range covered by this module. */ + addressRange?: string; + } + + /** A ColumnDescriptor specifies what module attribute to show in a column of the ModulesView, how to format it, and what the column's label should be. + It is only used if the underlying UI actually supports this level of customization. + */ + export interface ColumnDescriptor { + /** Name of the attribute rendered in this column. */ + attributeName: string; + /** Header UI label of column. */ + label: string; + /** Format to use for the rendered values in this column. TBD how the format strings looks like. */ + format?: string; + /** Datatype of values in this column. Defaults to 'string' if not specified. */ + type?: 'string' | 'number' | 'boolean' | 'unixTimestampUTC'; + /** Width of this column in characters (hint only). */ + width?: number; + } + + /** The ModulesViewDescriptor is the container for all declarative configuration options of a ModuleView. + For now it only specifies the columns to be shown in the modules view. + */ + export interface ModulesViewDescriptor { + columns: ColumnDescriptor[]; + } + + /** A Thread */ + export interface Thread { + /** Unique identifier for the thread. */ + id: number; + /** A name of the thread. */ + name: string; + } + + /** A Source is a descriptor for source code. It is returned from the debug adapter as part of a StackFrame and it is used by clients when specifying breakpoints. */ + export interface Source { + /** The short name of the source. Every source returned from the debug adapter has a name. When sending a source to the debug adapter this name is optional. */ + name?: string; + /** The path of the source to be shown in the UI. It is only used to locate and load the content of the source if no sourceReference is specified (or its vaule is 0). */ + path?: string; + /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. */ + sourceReference?: number; + /** An optional hint for how to present the source in the UI. A value of 'deemphasize' can be used to indicate that the source is not available or that it is skipped on stepping. */ + presentationHint?: 'normal' | 'emphasize' | 'deemphasize'; + /** The (optional) origin of this source: possible values 'internal module', 'inlined content from source map', etc. */ + origin?: string; + /** An optional list of sources that are related to this source. These may be the source that generated this source. */ + sources?: Source[]; + /** Optional data that a debug adapter might want to loop through the client. The client should leave the data intact and persist it across sessions. The client should not interpret the data. */ + adapterData?: any; + /** The checksums associated with this file. */ + checksums?: Checksum[]; + } + + /** A Stackframe contains the source location. */ + export interface StackFrame { + /** An identifier for the stack frame. It must be unique across all threads. This id can be used to retrieve the scopes of the frame with the 'scopesRequest' or to restart the execution of a stackframe. */ + id: number; + /** The name of the stack frame, typically a method name. */ + name: string; + /** The optional source of the frame. */ + source?: Source; + /** The line within the file of the frame. If source is null or doesn't exist, line is 0 and must be ignored. */ + line: number; + /** The column within the line. If source is null or doesn't exist, column is 0 and must be ignored. */ + column: number; + /** An optional end line of the range covered by the stack frame. */ + endLine?: number; + /** An optional end column of the range covered by the stack frame. */ + endColumn?: number; + /** The module associated with this frame, if any. */ + moduleId?: number | string; + /** An optional hint for how to present this frame in the UI. A value of 'label' can be used to indicate that the frame is an artificial frame that is used as a visual label or separator. A value of 'subtle' can be used to change the appearance of a frame in a 'subtle' way. */ + presentationHint?: 'normal' | 'label' | 'subtle'; + } + + /** A Scope is a named container for variables. Optionally a scope can map to a source or a range within a source. */ + export interface Scope { + /** Name of the scope such as 'Arguments', 'Locals'. */ + name: string; + /** The variables of this scope can be retrieved by passing the value of variablesReference to the VariablesRequest. */ + variablesReference: number; + /** The number of named variables in this scope. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + */ + namedVariables?: number; + /** The number of indexed variables in this scope. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + */ + indexedVariables?: number; + /** If true, the number of variables in this scope is large or expensive to retrieve. */ + expensive: boolean; + /** Optional source for this scope. */ + source?: Source; + /** Optional start line of the range covered by this scope. */ + line?: number; + /** Optional start column of the range covered by this scope. */ + column?: number; + /** Optional end line of the range covered by this scope. */ + endLine?: number; + /** Optional end column of the range covered by this scope. */ + endColumn?: number; + } + + /** A Variable is a name/value pair. + Optionally a variable can have a 'type' that is shown if space permits or when hovering over the variable's name. + An optional 'kind' is used to render additional properties of the variable, e.g. different icons can be used to indicate that a variable is public or private. + If the value is structured (has children), a handle is provided to retrieve the children with the VariablesRequest. + If the number of named or indexed children is large, the numbers should be returned via the optional 'namedVariables' and 'indexedVariables' attributes. + The client can use this optional information to present the children in a paged UI and fetch them in chunks. + */ + export interface Variable { + /** The variable's name. */ + name: string; + /** The variable's value. This can be a multi-line text, e.g. for a function the body of a function. */ + value: string; + /** The type of the variable's value. Typically shown in the UI when hovering over the value. */ + type?: string; + /** Properties of a variable that can be used to determine how to render the variable in the UI. */ + presentationHint?: VariablePresentationHint; + /** Optional evaluatable name of this variable which can be passed to the 'EvaluateRequest' to fetch the variable's value. */ + evaluateName?: string; + /** If variablesReference is > 0, the variable is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ + variablesReference: number; + /** The number of named child variables. + The client can use this optional information to present the children in a paged UI and fetch them in chunks. + */ + namedVariables?: number; + /** The number of indexed child variables. + The client can use this optional information to present the children in a paged UI and fetch them in chunks. + */ + indexedVariables?: number; + } + + /** Optional properties of a variable that can be used to determine how to render the variable in the UI. */ + export interface VariablePresentationHint { + /** The kind of variable. Before introducing additional values, try to use the listed values. + Values: 'property', 'method', 'class', 'data', 'event', 'baseClass', 'innerClass', 'interface', 'mostDerivedClass', etc. + */ + kind?: string; + /** Set of attributes represented as an array of strings. Before introducing additional values, try to use the listed values. + Values: + 'static': Indicates that the object is static. + 'constant': Indicates that the object is a constant. + 'readOnly': Indicates that the object is read only. + 'rawString': Indicates that the object is a raw string. + 'hasObjectId': Indicates that the object can have an Object ID created for it. + 'canHaveObjectId': Indicates that the object has an Object ID associated with it. + 'hasSideEffects': Indicates that the evaluation had side effects. + etc. + */ + attributes?: string[]; + /** Visibility of variable. Before introducing additional values, try to use the listed values. + Values: 'public', 'private', 'protected', 'internal', 'final', etc. + */ + visibility?: string; + } + + /** Properties of a breakpoint passed to the setBreakpoints request. */ + export interface SourceBreakpoint { + /** The source line of the breakpoint. */ + line: number; + /** An optional source column of the breakpoint. */ + column?: number; + /** An optional expression for conditional breakpoints. */ + condition?: string; + /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ + hitCondition?: string; + } + + /** Properties of a breakpoint passed to the setFunctionBreakpoints request. */ + export interface FunctionBreakpoint { + /** The name of the function. */ + name: string; + /** An optional expression for conditional breakpoints. */ + condition?: string; + /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ + hitCondition?: string; + } + + /** Information about a Breakpoint created in setBreakpoints or setFunctionBreakpoints. */ + export interface Breakpoint { + /** An optional unique identifier for the breakpoint. */ + id?: number; + /** If true breakpoint could be set (but not necessarily at the desired location). */ + verified: boolean; + /** An optional message about the state of the breakpoint. This is shown to the user and can be used to explain why a breakpoint could not be verified. */ + message?: string; + /** The source where the breakpoint is located. */ + source?: Source; + /** The start line of the actual range covered by the breakpoint. */ + line?: number; + /** An optional start column of the actual range covered by the breakpoint. */ + column?: number; + /** An optional end line of the actual range covered by the breakpoint. */ + endLine?: number; + /** An optional end column of the actual range covered by the breakpoint. If no end line is given, then the end column is assumed to be in the start line. */ + endColumn?: number; + } + + /** A StepInTarget can be used in the 'stepIn' request and determines into which single target the stepIn request should step. */ + export interface StepInTarget { + /** Unique identifier for a stepIn target. */ + id: number; + /** The name of the stepIn target (shown in the UI). */ + label: string; + } + + /** A GotoTarget describes a code location that can be used as a target in the 'goto' request. + The possible goto targets can be determined via the 'gotoTargets' request. + */ + export interface GotoTarget { + /** Unique identifier for a goto target. This is used in the goto request. */ + id: number; + /** The name of the goto target (shown in the UI). */ + label: string; + /** The line of the goto target. */ + line: number; + /** An optional column of the goto target. */ + column?: number; + /** An optional end line of the range covered by the goto target. */ + endLine?: number; + /** An optional end column of the range covered by the goto target. */ + endColumn?: number; + } + + /** CompletionItems are the suggestions returned from the CompletionsRequest. */ + export interface CompletionItem { + /** The label of this completion item. By default this is also the text that is inserted when selecting this completion. */ + label: string; + /** If text is not falsy then it is inserted instead of the label. */ + text?: string; + /** The item's type. Typically the client uses this information to render the item in the UI with an icon. */ + type?: CompletionItemType; + /** This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added. + If missing the text is added at the location specified by the CompletionsRequest's 'column' attribute. + */ + start?: number; + /** This value determines how many characters are overwritten by the completion text. + If missing the value 0 is assumed which results in the completion text being inserted. + */ + length?: number; + } + + /** Some predefined types for the CompletionItem. Please note that not all clients have specific icons for all of them. */ + export type CompletionItemType = 'method' | 'function' | 'constructor' | 'field' | 'variable' | 'class' | 'interface' | 'module' | 'property' | 'unit' | 'value' | 'enum' | 'keyword' | 'snippet' | 'text' | 'color' | 'file' | 'reference' | 'customcolor'; + + /** Names of checksum algorithms that may be supported by a debug adapter. */ + export type ChecksumAlgorithm = 'MD5' | 'SHA1' | 'SHA256' | 'timestamp'; + + /** The checksum of an item calculated by the specified algorithm. */ + export interface Checksum { + /** The algorithm used to calculate this checksum. */ + algorithm: ChecksumAlgorithm; + /** Value of the checksum. */ + checksum: string; + } + + /** Provides formatting information for a value. */ + export interface ValueFormat { + /** Display the value in hex. */ + hex?: boolean; + } + + /** Provides formatting information for a stack frame. */ + export interface StackFrameFormat extends ValueFormat { + /** Displays parameters for the stack frame. */ + parameters?: boolean; + /** Displays the types of parameters for the stack frame. */ + parameterTypes?: boolean; + /** Displays the names of parameters for the stack frame. */ + parameterNames?: boolean; + /** Displays the values of parameters for the stack frame. */ + parameterValues?: boolean; + /** Displays the line number of the stack frame. */ + line?: boolean; + /** Displays the module of the stack frame. */ + module?: boolean; + /** Includes all stack frames, including those the debug adapter might otherwise hide. */ + includeAll?: boolean; + } + + /** An ExceptionOptions assigns configuration options to a set of exceptions. */ + export interface ExceptionOptions { + /** A path that selects a single or multiple exceptions in a tree. If 'path' is missing, the whole tree is selected. By convention the first segment of the path is a category that is used to group exceptions in the UI. */ + path?: ExceptionPathSegment[]; + /** Condition when a thrown exception should result in a break. */ + breakMode: ExceptionBreakMode; + } + + /** This enumeration defines all possible conditions when a thrown exception should result in a break. + never: never breaks, + always: always breaks, + unhandled: breaks when excpetion unhandled, + userUnhandled: breaks if the exception is not handled by user code. + */ + export type ExceptionBreakMode = 'never' | 'always' | 'unhandled' | 'userUnhandled'; + + /** An ExceptionPathSegment represents a segment in a path that is used to match leafs or nodes in a tree of exceptions. If a segment consists of more than one name, it matches the names provided if 'negate' is false or missing or it matches anything except the names provided if 'negate' is true. */ + export interface ExceptionPathSegment { + /** If false or missing this segment matches the names provided, otherwise it matches anything except the names provided. */ + negate?: boolean; + /** Depending on the value of 'negate' the names that should match or not match. */ + names: string[]; + } + + /** Detailed information about an exception that has occurred. */ + export interface ExceptionDetails { + /** Message contained in the exception. */ + message?: string; + /** Short type name of the exception object. */ + typeName?: string; + /** Fully-qualified type name of the exception object. */ + fullTypeName?: string; + /** Optional expression that can be evaluated in the current scope to obtain the exception object. */ + evaluateName?: string; + /** Stack trace at the time the exception was thrown. */ + stackTrace?: string; + /** Details of the exception contained by this exception, if any. */ + innerException?: ExceptionDetails[]; + } +} + diff --git a/src/vs/workbench/parts/debug/common/debugSource.ts b/src/vs/workbench/parts/debug/common/debugSource.ts new file mode 100644 index 0000000000..bd7e106938 --- /dev/null +++ b/src/vs/workbench/parts/debug/common/debugSource.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import uri from 'vs/base/common/uri'; +import { DEBUG_SCHEME } from 'vs/workbench/parts/debug/common/debug'; + +const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source"); + +export class Source { + + public uri: uri; + public available: boolean; + + constructor(public raw: DebugProtocol.Source) { + if (!raw) { + this.raw = { name: UNKNOWN_SOURCE_LABEL }; + } + const path = this.raw.path || this.raw.name; + this.available = this.raw.name !== UNKNOWN_SOURCE_LABEL; + this.uri = this.raw.sourceReference > 0 ? uri.parse(`${DEBUG_SCHEME}:${path}`) : uri.file(path); + } + + public get name() { + return this.raw.name; + } + + public get origin() { + return this.raw.origin; + } + + public get presentationHint() { + return this.raw.presentationHint; + } + + public get reference() { + return this.raw.sourceReference; + } + + public get inMemory() { + return this.uri.toString().indexOf(`${DEBUG_SCHEME}:`) === 0; + } +} diff --git a/src/vs/workbench/parts/debug/common/debugViewModel.ts b/src/vs/workbench/parts/debug/common/debugViewModel.ts new file mode 100644 index 0000000000..8227986dcc --- /dev/null +++ b/src/vs/workbench/parts/debug/common/debugViewModel.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Event, { Emitter } from 'vs/base/common/event'; +import * as debug from 'vs/workbench/parts/debug/common/debug'; + +export class ViewModel implements debug.IViewModel { + + private _focusedStackFrame: debug.IStackFrame; + private _focusedProcess: debug.IProcess; + private selectedExpression: debug.IExpression; + private selectedFunctionBreakpoint: debug.IFunctionBreakpoint; + private _onDidFocusProcess: Emitter; + private _onDidFocusStackFrame: Emitter<{ stackFrame: debug.IStackFrame, explicit: boolean }>; + private _onDidSelectExpression: Emitter; + private _onDidSelectFunctionBreakpoint: Emitter; + private multiProcessView: boolean; + public changedWorkbenchViewState: boolean; + + constructor() { + this._onDidFocusProcess = new Emitter(); + this._onDidFocusStackFrame = new Emitter<{ stackFrame: debug.IStackFrame, explicit: boolean }>(); + this._onDidSelectExpression = new Emitter(); + this._onDidSelectFunctionBreakpoint = new Emitter(); + this.changedWorkbenchViewState = false; + this.multiProcessView = false; + } + + public getId(): string { + return 'root'; + } + + public get focusedProcess(): debug.IProcess { + return this._focusedProcess; + } + + public get focusedThread(): debug.IThread { + return this._focusedStackFrame ? this._focusedStackFrame.thread : (this._focusedProcess ? this._focusedProcess.getAllThreads().pop() : null); + } + + public get focusedStackFrame(): debug.IStackFrame { + return this._focusedStackFrame; + } + + public setFocusedStackFrame(stackFrame: debug.IStackFrame, process: debug.IProcess, explicit: boolean): void { + this._focusedStackFrame = stackFrame; + if (process !== this._focusedProcess) { + this._focusedProcess = process; + this._onDidFocusProcess.fire(process); + } + this._onDidFocusStackFrame.fire({ stackFrame, explicit }); + } + + public get onDidFocusProcess(): Event { + return this._onDidFocusProcess.event; + } + + public get onDidFocusStackFrame(): Event<{ stackFrame: debug.IStackFrame, explicit: boolean }> { + return this._onDidFocusStackFrame.event; + } + + public getSelectedExpression(): debug.IExpression { + return this.selectedExpression; + } + + public setSelectedExpression(expression: debug.IExpression) { + this.selectedExpression = expression; + this._onDidSelectExpression.fire(expression); + } + + public get onDidSelectExpression(): Event { + return this._onDidSelectExpression.event; + } + + public getSelectedFunctionBreakpoint(): debug.IFunctionBreakpoint { + return this.selectedFunctionBreakpoint; + } + + public setSelectedFunctionBreakpoint(functionBreakpoint: debug.IFunctionBreakpoint): void { + this.selectedFunctionBreakpoint = functionBreakpoint; + this._onDidSelectFunctionBreakpoint.fire(functionBreakpoint); + } + + public get onDidSelectFunctionBreakpoint(): Event { + return this._onDidSelectFunctionBreakpoint.event; + } + + public isMultiProcessView(): boolean { + return this.multiProcessView; + } + + public setMultiProcessView(isMultiProcessView: boolean): void { + this.multiProcessView = isMultiProcessView; + } +} diff --git a/src/vs/workbench/parts/debug/common/replHistory.ts b/src/vs/workbench/parts/debug/common/replHistory.ts new file mode 100644 index 0000000000..1438e73b17 --- /dev/null +++ b/src/vs/workbench/parts/debug/common/replHistory.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const MAX_HISTORY_ENTRIES = 50; + +/** + * The repl history has the following characteristics: + * - the history is stored in local storage up to N items + * - every time a expression is evaluated, it is being added to the history + * - when starting to navigate in history, the current expression is remembered to be able to go back + * - when navigating in history and making changes to any expression, these changes are remembered until a expression is evaluated + * - the navigation state is not remembered so that the user always ends up at the end of the history stack when evaluating a expression + */ +export class ReplHistory { + + private historyPointer: number; + private currentExpressionStoredMarkers: boolean; + private historyOverwrites: Map; + + constructor(private history: string[]) { + this.historyPointer = this.history.length; + this.currentExpressionStoredMarkers = false; + this.historyOverwrites = new Map(); + } + + public next(): string { + return this.navigate(false); + } + + public previous(): string { + return this.navigate(true); + } + + private navigate(previous: boolean): string { + // validate new pointer + let newPointer = -1; + if (previous && this.historyPointer > 0 && this.history.length > this.historyPointer - 1) { + newPointer = this.historyPointer - 1; + } else if (!previous && this.history.length > this.historyPointer + 1) { + newPointer = this.historyPointer + 1; + } + + if (newPointer >= 0) { + + // remember pointer for next navigation + this.historyPointer = newPointer; + + // check for overwrite + if (this.historyOverwrites.has(newPointer.toString())) { + return this.historyOverwrites.get(newPointer.toString()); + } + + return this.history[newPointer]; + } + + return null; + } + + public remember(expression: string, fromPrevious: boolean): void { + let previousPointer: number; + + // this method is called after the user has navigated in the history. Therefor we need to + // restore the value of the pointer from the point when the user started the navigation. + if (fromPrevious) { + previousPointer = this.historyPointer + 1; + } else { + previousPointer = this.historyPointer - 1; + } + + // when the user starts to navigate in history, add the current expression to the history + // once so that the user can always navigate back to it and does not loose its data. + if (previousPointer === this.history.length && !this.currentExpressionStoredMarkers) { + this.history.push(expression); + this.currentExpressionStoredMarkers = true; + } + + // keep edits that are made to history items up until the user actually evaluates a expression + else { + this.historyOverwrites.set(previousPointer.toString(), expression); + } + } + + public evaluated(expression: string): void { + // clear current expression that was stored previously to support history navigation now on evaluate + if (this.currentExpressionStoredMarkers) { + this.history.pop(); + } + + // keep in local history if expression provided and not equal to previous expression stored in history + if (expression && (this.history.length === 0 || this.history[this.history.length - 1] !== expression)) { + this.history.push(expression); + } + + // advance History Pointer to the end + this.historyPointer = this.history.length; + + // reset marker + this.currentExpressionStoredMarkers = false; + + // reset overwrites + this.historyOverwrites.clear(); + } + + public save(): string[] { + // remove current expression from history since it was not evaluated + if (this.currentExpressionStoredMarkers) { + this.history.pop(); + } + if (this.history.length > MAX_HISTORY_ENTRIES) { + this.history = this.history.splice(this.history.length - MAX_HISTORY_ENTRIES, MAX_HISTORY_ENTRIES); + } + + return this.history; + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts new file mode 100644 index 0000000000..bb72cabdca --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * 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!../browser/media/debug.contribution'; +import 'vs/css!../browser/media/debugHover'; +import * as nls from 'vs/nls'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IWorkbenchActionRegistry, Extensions as WorkbenchActionRegistryExtensions } from 'vs/workbench/common/actionRegistry'; +import { ToggleViewletAction, Extensions as ViewletExtensions, ViewletRegistry, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; +import { TogglePanelAction, Extensions as PanelExtensions, PanelRegistry, PanelDescriptor } from 'vs/workbench/browser/panel'; +import { VariablesView, WatchExpressionsView, CallStackView, BreakpointsView } from 'vs/workbench/parts/debug/electron-browser/debugViews'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { IDebugService, VIEWLET_ID, REPL_ID, CONTEXT_NOT_IN_DEBUG_MODE, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA } from 'vs/workbench/parts/debug/common/debug'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { DebugEditorModelManager } from 'vs/workbench/parts/debug/browser/debugEditorModelManager'; +import { + StepOverAction, ClearReplAction, FocusReplAction, StepIntoAction, StepOutAction, StartAction, RestartAction, ContinueAction, StopAction, DisconnectAction, PauseAction, AddFunctionBreakpointAction, + ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction +} from 'vs/workbench/parts/debug/browser/debugActions'; +import { DebugActionsWidget } from 'vs/workbench/parts/debug/browser/debugActionsWidget'; +import * as service from 'vs/workbench/parts/debug/electron-browser/debugService'; +import { DebugContentProvider } from 'vs/workbench/parts/debug/browser/debugContentProvider'; +import 'vs/workbench/parts/debug/electron-browser/debugEditorContribution'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import * as debugCommands from 'vs/workbench/parts/debug/electron-browser/debugCommands'; +import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; +import { StatusBarColorProvider } from 'vs/workbench/parts/debug/electron-browser/statusbarColorProvider'; +import { ViewLocation, ViewsRegistry } from 'vs/workbench/parts/views/browser/viewsRegistry'; + +class OpenDebugViewletAction extends ToggleViewletAction { + public static ID = VIEWLET_ID; + public static LABEL = nls.localize('toggleDebugViewlet', "Show Debug"); + + constructor( + id: string, + label: string, + @IViewletService viewletService: IViewletService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService + ) { + super(id, label, VIEWLET_ID, viewletService, editorService); + } +} + +class OpenDebugPanelAction extends TogglePanelAction { + public static ID = 'workbench.debug.action.toggleRepl'; + public static LABEL = nls.localize('toggleDebugPanel', "Debug Console"); + + constructor( + id: string, + label: string, + @IPanelService panelService: IPanelService, + @IPartService partService: IPartService + ) { + super(id, label, REPL_ID, panelService, partService); + } +} + +// register viewlet +Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( + 'vs/workbench/parts/debug/browser/debugViewlet', + 'DebugViewlet', + VIEWLET_ID, + nls.localize('debug', "Debug"), + 'debug', + 40 +)); + +const openViewletKb: IKeybindings = { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_D +}; +const openPanelKb: IKeybindings = { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y +}; + +// register repl panel +Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( + 'vs/workbench/parts/debug/electron-browser/repl', + 'Repl', + REPL_ID, + nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), + 'repl', + 30, + OpenDebugPanelAction.ID +)); +Registry.as(PanelExtensions.Panels).setDefaultPanelId(REPL_ID); + +// Register default debug views +ViewsRegistry.registerViews([{ id: 'workbench.debug.variablesView', name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, size: 40, location: ViewLocation.Debug, canToggleVisibility: true }]); +ViewsRegistry.registerViews([{ id: 'workbench.debug.watchExpressionsView', name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, size: 10, location: ViewLocation.Debug, canToggleVisibility: true }]); +ViewsRegistry.registerViews([{ id: 'workbench.debug.callStackView', name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, size: 30, location: ViewLocation.Debug, canToggleVisibility: true }]); +ViewsRegistry.registerViews([{ id: 'workbench.debug.breakPointsView', name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, size: 20, location: ViewLocation.Debug, canToggleVisibility: true }]); + +// register action to open viewlet +const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugPanelAction, OpenDebugPanelAction.ID, OpenDebugPanelAction.LABEL, openPanelKb), 'View: Debug Console', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugViewletAction, OpenDebugViewletAction.ID, OpenDebugViewletAction.LABEL, openViewletKb), 'View: Show Debug', nls.localize('view', "View")); + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugEditorModelManager); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugActionsWidget); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider); + +const debugCategory = nls.localize('debugCategory', "Debug"); +registry.registerWorkbenchAction(new SyncActionDescriptor( + StartAction, StartAction.ID, StartAction.LABEL, { primary: KeyCode.F5 }, CONTEXT_NOT_IN_DEBUG_MODE), 'Debug: Start Debugging', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(StepOverAction, StepOverAction.ID, StepOverAction.LABEL, { primary: KeyCode.F10 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Step Over', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(StepIntoAction, StepIntoAction.ID, StepIntoAction.LABEL, { primary: KeyCode.F11 }, CONTEXT_IN_DEBUG_MODE, KeybindingsRegistry.WEIGHT.workbenchContrib(1)), 'Debug: Step Into', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(StepOutAction, StepOutAction.ID, StepOutAction.LABEL, { primary: KeyMod.Shift | KeyCode.F11 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Step Out', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(RestartAction, RestartAction.ID, RestartAction.LABEL, { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Restart', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(StopAction, StopAction.ID, StopAction.LABEL, { primary: KeyMod.Shift | KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Stop', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(DisconnectAction, DisconnectAction.ID, DisconnectAction.LABEL), 'Debug: Disconnect', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(ContinueAction, ContinueAction.ID, ContinueAction.LABEL, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Continue', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(PauseAction, PauseAction.ID, PauseAction.LABEL, { primary: KeyCode.F6 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Pause', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureAction, ConfigureAction.ID, ConfigureAction.LABEL), 'Debug: Open launch.json', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL), 'Debug: Add Function Breakpoint', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(ReapplyBreakpointsAction, ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL), 'Debug: Reapply All Breakpoints', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(RunAction, RunAction.ID, RunAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.F5 }, CONTEXT_NOT_IN_DEBUG_MODE), 'Debug: Start Without Debugging', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL), 'Debug: Remove All Breakpoints', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(EnableAllBreakpointsAction, EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL), 'Debug: Enable All Breakpoints', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL), 'Debug: Disable All Breakpoints', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), 'Debug: Clear Console', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(FocusReplAction, FocusReplAction.ID, FocusReplAction.LABEL), 'Debug: Focus Debug Console', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(SelectAndStartAction, SelectAndStartAction.ID, SelectAndStartAction.LABEL), 'Debug: Select and Start Debugging', debugCategory); + +// Register Quick Open +(Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/debug/browser/debugQuickOpen', + 'DebugQuickOpenHandler', + 'debug ', + 'inLaunchConfigurationsPicker', + nls.localize('debugCommands', "Debug Configuration") + ) +); + +// register service +registerSingleton(IDebugService, service.DebugService); + +// Register configuration +const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); +configurationRegistry.registerConfiguration({ + id: 'debug', + order: 20, + title: nls.localize('debugConfigurationTitle', "Debug"), + type: 'object', + properties: { + 'debug.allowBreakpointsEverywhere': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'allowBreakpointsEverywhere' }, "Allows setting breakpoint in any file"), + default: false + }, + 'debug.openExplorerOnEnd': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'openExplorerOnEnd' }, "Automatically open explorer view on the end of a debug session"), + default: false + }, + 'debug.inlineValues': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'inlineValues' }, "Show variable values inline in editor while debugging"), + default: false + }, + 'debug.hideActionBar': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'hideActionBar' }, "Controls if the floating debug action bar should be hidden"), + default: false + }, + 'debug.internalConsoleOptions': INTERNAL_CONSOLE_OPTIONS_SCHEMA, + 'launch': { + type: 'object', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'launch' }, "Global debug launch configuration. Should be used as an alternative to 'launch.json' that is shared across workspaces"), + default: {} + } + } +}); + +debugCommands.registerCommands(); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts b/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts new file mode 100644 index 0000000000..1add27f5bf --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts @@ -0,0 +1,225 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import uri from 'vs/base/common/uri'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { TPromise } from 'vs/base/common/winjs.base'; +import severity from 'vs/base/common/severity'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import * as errors from 'vs/base/common/errors'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IDebugService, IConfig, IEnablement, CONTEXT_NOT_IN_DEBUG_MODE, CONTEXT_IN_DEBUG_MODE, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution } from 'vs/workbench/parts/debug/common/debug'; +import { Expression, Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/parts/debug/common/debugModel'; +import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; + +export function registerCommands(): void { + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: '_workbench.startDebug', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + handler(accessor: ServicesAccessor, configurationOrName: IConfig | string, folderUri?: uri) { + const debugService = accessor.get(IDebugService); + if (!configurationOrName) { + configurationOrName = debugService.getConfigurationManager().selectedName; + } + + if (!folderUri) { + const selectedLaunch = debugService.getConfigurationManager().selectedLaunch; + folderUri = selectedLaunch ? selectedLaunch.workspaceUri : undefined; + } + + if (typeof configurationOrName === 'string') { + debugService.startDebugging(folderUri, configurationOrName); + } else { + debugService.createProcess(folderUri, configurationOrName); + } + }, + when: CONTEXT_NOT_IN_DEBUG_MODE, + primary: undefined + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.customDebugRequest', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + handler(accessor: ServicesAccessor, request: string, requestArgs: any) { + const process = accessor.get(IDebugService).getViewModel().focusedProcess; + if (process) { + return process.session.custom(request, requestArgs); + } + + return undefined; + }, + when: CONTEXT_IN_DEBUG_MODE, + primary: undefined + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'debug.logToDebugConsole', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + handler(accessor: ServicesAccessor, value: string) { + if (typeof value === 'string') { + const debugService = accessor.get(IDebugService); + // Use warning as severity to get the orange color for messages coming from the debug extension + debugService.logToRepl(value, severity.Warning); + } + }, + when: undefined, + primary: undefined + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'debug.toggleBreakpoint', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5), + when: CONTEXT_BREAKPOINTS_FOCUSED, + primary: KeyCode.Space, + handler: (accessor) => { + const listService = accessor.get(IListService); + const debugService = accessor.get(IDebugService); + const focused = listService.getFocused(); + + // Tree only + if (!(focused instanceof List)) { + const tree = focused; + const element = tree.getFocus(); + debugService.enableOrDisableBreakpoints(!element.enabled, element).done(null, errors.onUnexpectedError); + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'debug.renameWatchExpression', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5), + when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED, + primary: KeyCode.F2, + mac: { primary: KeyCode.Enter }, + handler: (accessor) => { + const listService = accessor.get(IListService); + const debugService = accessor.get(IDebugService); + const focused = listService.getFocused(); + + // Tree only + if (!(focused instanceof List)) { + const element = focused.getFocus(); + if (element instanceof Expression) { + debugService.getViewModel().setSelectedExpression(element); + } + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'debug.setVariable', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5), + when: CONTEXT_VARIABLES_FOCUSED, + primary: KeyCode.F2, + mac: { primary: KeyCode.Enter }, + handler: (accessor) => { + const listService = accessor.get(IListService); + const debugService = accessor.get(IDebugService); + const focused = listService.getFocused(); + + // Tree only + if (!(focused instanceof List)) { + const element = focused.getFocus(); + if (element instanceof Variable) { + debugService.getViewModel().setSelectedExpression(element); + } + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'debug.removeWatchExpression', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED, + primary: KeyCode.Delete, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace }, + handler: (accessor) => { + const listService = accessor.get(IListService); + const debugService = accessor.get(IDebugService); + const focused = listService.getFocused(); + + // Tree only + if (!(focused instanceof List)) { + const element = focused.getFocus(); + if (element instanceof Expression) { + debugService.removeWatchExpressions(element.getId()); + } + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'debug.removeBreakpoint', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: CONTEXT_BREAKPOINTS_FOCUSED, + primary: KeyCode.Delete, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace }, + handler: (accessor) => { + const listService = accessor.get(IListService); + const debugService = accessor.get(IDebugService); + const focused = listService.getFocused(); + + // Tree only + if (!(focused instanceof List)) { + const element = focused.getFocus(); + if (element instanceof Breakpoint) { + debugService.removeBreakpoints(element.getId()).done(null, errors.onUnexpectedError); + } else if (element instanceof FunctionBreakpoint) { + debugService.removeFunctionBreakpoints(element.getId()).done(null, errors.onUnexpectedError); + } + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'debug.installAdditionalDebuggers', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: undefined, + primary: undefined, + handler: (accessor) => { + const viewletService = accessor.get(IViewletService); + return viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('tag:debuggers @sort:installs'); + viewlet.focus(); + }); + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'debug.addConfiguration', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: undefined, + primary: undefined, + handler: (accessor, workspaceUri: string) => { + const manager = accessor.get(IDebugService).getConfigurationManager(); + if (!accessor.get(IWorkspaceContextService).hasWorkspace()) { + accessor.get(IMessageService).show(severity.Info, nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration.")); + return TPromise.as(null); + } + const launch = manager.getLaunches().filter(l => l.workspaceUri.toString() === workspaceUri).pop() || manager.selectedLaunch; + + return launch.openConfigFile(false).done(editor => { + if (editor) { + const codeEditor = editor.getControl(); + if (codeEditor) { + return codeEditor.getContribution(EDITOR_CONTRIBUTION_ID).addLaunchConfiguration(); + } + } + + return undefined; + }); + } + }); +} diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts new file mode 100644 index 0000000000..23186681f3 --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -0,0 +1,593 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as strings from 'vs/base/common/strings'; +import { first } from 'vs/base/common/arrays'; +import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; +import * as objects from 'vs/base/common/objects'; +import uri from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import * as paths from 'vs/base/common/paths'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IModel, isCommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { IEditor } from 'vs/platform/editor/common/editor'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import * as extensionsRegistry from 'vs/platform/extensions/common/extensionsRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, DEBUG_SCHEME, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch } from 'vs/workbench/parts/debug/common/debug'; +import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; + +// debuggers extension point +export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint('debuggers', [], { + description: nls.localize('vscode.extension.contributes.debuggers', 'Contributes debug adapters.'), + type: 'array', + defaultSnippets: [{ body: [{ type: '', extensions: [] }] }], + items: { + type: 'object', + defaultSnippets: [{ body: { type: '', program: '', runtime: '', enableBreakpointsFor: { languageIds: [''] } } }], + properties: { + type: { + description: nls.localize('vscode.extension.contributes.debuggers.type', "Unique identifier for this debug adapter."), + type: 'string' + }, + label: { + description: nls.localize('vscode.extension.contributes.debuggers.label', "Display name for this debug adapter."), + type: 'string' + }, + program: { + description: nls.localize('vscode.extension.contributes.debuggers.program', "Path to the debug adapter program. Path is either absolute or relative to the extension folder."), + type: 'string' + }, + args: { + description: nls.localize('vscode.extension.contributes.debuggers.args', "Optional arguments to pass to the adapter."), + type: 'array' + }, + runtime: { + description: nls.localize('vscode.extension.contributes.debuggers.runtime', "Optional runtime in case the program attribute is not an executable but requires a runtime."), + type: 'string' + }, + runtimeArgs: { + description: nls.localize('vscode.extension.contributes.debuggers.runtimeArgs', "Optional runtime arguments."), + type: 'array' + }, + variables: { + description: nls.localize('vscode.extension.contributes.debuggers.variables', "Mapping from interactive variables (e.g ${action.pickProcess}) in `launch.json` to a command."), + type: 'object' + }, + initialConfigurations: { + description: nls.localize('vscode.extension.contributes.debuggers.initialConfigurations', "Configurations for generating the initial \'launch.json\'."), + type: ['array', 'string'], + }, + languages: { + description: nls.localize('vscode.extension.contributes.debuggers.languages', "List of languages for which the debug extension could be considered the \"default debugger\"."), + type: 'array' + }, + adapterExecutableCommand: { + description: nls.localize('vscode.extension.contributes.debuggers.adapterExecutableCommand', "If specified VS Code will call this command to determine the executable path of the debug adapter and the arguments to pass."), + type: 'string' + }, + startSessionCommand: { + description: nls.localize('vscode.extension.contributes.debuggers.startSessionCommand', "If specified VS Code will call this command for the \"debug\" or \"run\" actions targeted for this extension."), + type: 'string' + }, + configurationSnippets: { + description: nls.localize('vscode.extension.contributes.debuggers.configurationSnippets', "Snippets for adding new configurations in \'launch.json\'."), + type: 'array' + }, + configurationAttributes: { + description: nls.localize('vscode.extension.contributes.debuggers.configurationAttributes', "JSON schema configurations for validating \'launch.json\'."), + type: 'object' + }, + windows: { + description: nls.localize('vscode.extension.contributes.debuggers.windows', "Windows specific settings."), + type: 'object', + properties: { + runtime: { + description: nls.localize('vscode.extension.contributes.debuggers.windows.runtime', "Runtime used for Windows."), + type: 'string' + } + } + }, + osx: { + description: nls.localize('vscode.extension.contributes.debuggers.osx', "OS X specific settings."), + type: 'object', + properties: { + runtime: { + description: nls.localize('vscode.extension.contributes.debuggers.osx.runtime', "Runtime used for OSX."), + type: 'string' + } + } + }, + linux: { + description: nls.localize('vscode.extension.contributes.debuggers.linux', "Linux specific settings."), + type: 'object', + properties: { + runtime: { + description: nls.localize('vscode.extension.contributes.debuggers.linux.runtime', "Runtime used for Linux."), + type: 'string' + } + } + } + } + } +}); + +interface IRawBreakpointContribution { + language: string; +} + +// breakpoints extension point #9037 +const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint('breakpoints', [], { + description: nls.localize('vscode.extension.contributes.breakpoints', 'Contributes breakpoints.'), + type: 'array', + defaultSnippets: [{ body: [{ language: '' }] }], + items: { + type: 'object', + defaultSnippets: [{ body: { language: '' } }], + properties: { + language: { + description: nls.localize('vscode.extension.contributes.breakpoints.language', "Allow breakpoints for this language."), + type: 'string' + }, + } + } +}); + +// debug general schema + +export const schemaId = 'vscode://schemas/launch'; +const defaultCompound: ICompound = { name: 'Compound', configurations: [] }; +const schema: IJSONSchema = { + id: schemaId, + type: 'object', + title: nls.localize('app.launch.json.title', "Launch"), + required: ['version', 'configurations'], + default: { version: '0.2.0', configurations: [], compounds: [] }, + properties: { + version: { + type: 'string', + description: nls.localize('app.launch.json.version', "Version of this file format."), + default: '0.2.0' + }, + configurations: { + type: 'array', + description: nls.localize('app.launch.json.configurations', "List of configurations. Add new configurations or edit existing ones by using IntelliSense."), + items: { + defaultSnippets: [], + 'type': 'object', + oneOf: [] + } + }, + compounds: { + type: 'array', + description: nls.localize('app.launch.json.compounds', "List of compounds. Each compound references multiple configurations which will get launched together."), + items: { + type: 'object', + required: ['name', 'configurations'], + properties: { + name: { + type: 'string', + description: nls.localize('app.launch.json.compound.name', "Name of compound. Appears in the launch configuration drop down menu.") + }, + configurations: { + type: 'array', + default: [], + items: { + type: 'string' + }, + description: nls.localize('app.launch.json.compounds.configurations', "Names of configurations that will be started as part of this compound.") + } + }, + default: defaultCompound + }, + default: [ + defaultCompound + ] + } + } +}; + +const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); +jsonRegistry.registerSchema(schemaId, schema); +const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname'; +const DEBUG_SELECTED_ROOT = 'debug.selectedroot'; + +export class ConfigurationManager implements IConfigurationManager { + private adapters: Adapter[]; + private breakpointModeIdsSet = new Set(); + private launches: ILaunch[]; + private _selectedName: string; + private _selectedLaunch: ILaunch; + private toDispose: IDisposable[]; + private _onDidSelectConfigurationName = new Emitter(); + private _providers: Map; + + constructor( + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IFileService private fileService: IFileService, + @ITelemetryService private telemetryService: ITelemetryService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IConfigurationService private configurationService: IConfigurationService, + @IQuickOpenService private quickOpenService: IQuickOpenService, + @IConfigurationResolverService private configurationResolverService: IConfigurationResolverService, + @IInstantiationService private instantiationService: IInstantiationService, + @ICommandService private commandService: ICommandService, + @IStorageService private storageService: IStorageService, + @ILifecycleService lifecycleService: ILifecycleService, + ) { + this._providers = new Map(); + this.adapters = []; + this.toDispose = []; + this.registerListeners(lifecycleService); + this.initLaunches(); + const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); + const filtered = this.launches.filter(l => l.workspaceUri.toString() === previousSelectedRoot); + const launchToSelect = filtered.length ? filtered[0] : this.launches.length ? this.launches[0] : undefined; + this.selectConfiguration(launchToSelect, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE)); + } + + public registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void { + if (!debugConfigurationProvider) { + return; + } + this._providers.set(handle, debugConfigurationProvider); + const adapter = this.getAdapter(debugConfigurationProvider.type); + if (adapter) { + adapter.hasConfigurationProvider = true; + } + } + + public unregisterDebugConfigurationProvider(handle: number): boolean { + return this._providers.delete(handle); + } + + public resolveDebugConfiguration(folderUri: uri | undefined, debugConfiguration: any): TPromise { + + // collect all candidates + const providers: IDebugConfigurationProvider[] = []; + this._providers.forEach(provider => { + if (provider.type === debugConfiguration.type && provider.resolveDebugConfiguration) { + providers.push(provider); + } + }); + + // pipe the config through the promises sequentially + return providers.reduce((promise, provider) => { + return promise.then(config => { + return provider.resolveDebugConfiguration(folderUri, config); + }); + }, TPromise.as(debugConfiguration)); + } + + public provideDebugConfigurations(folderUri: uri | undefined, type: string): TPromise { + + // collect all candidates + const configs: TPromise[] = []; + this._providers.forEach(provider => { + if (provider.type === type && provider.provideDebugConfigurations) { + configs.push(provider.provideDebugConfigurations(folderUri)); + } + }); + + // combine all configs into one array + return TPromise.join(configs).then(results => { + return [].concat.apply([], results); + }); + } + + private registerListeners(lifecycleService: ILifecycleService): void { + debuggersExtPoint.setHandler((extensions) => { + extensions.forEach(extension => { + extension.value.forEach(rawAdapter => { + if (!rawAdapter.type || (typeof rawAdapter.type !== 'string')) { + extension.collector.error(nls.localize('debugNoType', "Debug adapter 'type' can not be omitted and must be of type 'string'.")); + } + if (rawAdapter.enableBreakpointsFor) { + rawAdapter.enableBreakpointsFor.languageIds.forEach(modeId => { + this.breakpointModeIdsSet.add(modeId); + }); + } + + const duplicate = this.adapters.filter(a => a.type === rawAdapter.type).pop(); + if (duplicate) { + duplicate.merge(rawAdapter, extension.description); + } else { + this.adapters.push(this.instantiationService.createInstance(Adapter, rawAdapter, extension.description)); + } + }); + }); + + // update the schema to include all attributes, snippets and types from extensions. + this.adapters.forEach(adapter => { + const items = (schema.properties['configurations'].items); + const schemaAttributes = adapter.getSchemaAttributes(); + if (schemaAttributes) { + items.oneOf.push(...schemaAttributes); + } + const configurationSnippets = adapter.configurationSnippets; + if (configurationSnippets) { + items.defaultSnippets.push(...configurationSnippets); + } + }); + }); + + breakpointsExtPoint.setHandler(extensions => { + extensions.forEach(ext => { + ext.value.forEach(breakpoints => { + this.breakpointModeIdsSet.add(breakpoints.language); + }); + }); + }); + + this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => { + this.initLaunches(); + const toSelect = this.selectedLaunch && this.selectedLaunch.getConfigurationNames().length ? this.selectedLaunch : first(this.launches, l => !!l.getConfigurationNames().length, this.launches.length ? this.launches[0] : undefined); + this.selectConfiguration(toSelect); + })); + + this.toDispose.push(lifecycleService.onShutdown(this.store, this)); + } + + private initLaunches(): void { + const workspace = this.contextService.getWorkspace(); + this.launches = workspace ? workspace.roots.map(root => this.instantiationService.createInstance(Launch, this, root)) : []; + if (this.launches.indexOf(this._selectedLaunch) === -1) { + this._selectedLaunch = undefined; + } + } + + public getLaunches(): ILaunch[] { + return this.launches; + } + + public get selectedLaunch(): ILaunch { + return this._selectedLaunch; + } + + public get selectedName(): string { + return this._selectedName; + } + + public get onDidSelectConfiguration(): Event { + return this._onDidSelectConfigurationName.event; + } + + public selectConfiguration(launch: ILaunch, name?: string, debugStarted?: boolean): void { + const previousLaunch = this._selectedLaunch; + const previousName = this._selectedName; + + this._selectedLaunch = launch; + const names = launch ? launch.getConfigurationNames() : []; + if (name && names.indexOf(name) >= 0) { + this._selectedName = name; + } + if (names.indexOf(this.selectedName) === -1) { + this._selectedName = names.length ? names[0] : undefined; + } + + if (this.selectedLaunch !== previousLaunch || this.selectedName !== previousName) { + this._onDidSelectConfigurationName.fire(); + } + } + + public canSetBreakpointsIn(model: IModel): boolean { + if (model.uri.scheme !== Schemas.file && model.uri.scheme !== DEBUG_SCHEME) { + return false; + } + if (this.configurationService.getConfiguration('debug').allowBreakpointsEverywhere) { + return true; + } + + const modeId = model ? model.getLanguageIdentifier().language : null; + + return this.breakpointModeIdsSet.has(modeId); + } + + public getAdapter(type: string): Adapter { + return this.adapters.filter(adapter => strings.equalsIgnoreCase(adapter.type, type)).pop(); + } + + public guessAdapter(type?: string): TPromise { + if (type) { + const adapter = this.getAdapter(type); + return TPromise.as(adapter); + } + + const editor = this.editorService.getActiveEditor(); + if (editor) { + const codeEditor = editor.getControl(); + if (isCommonCodeEditor(codeEditor)) { + const model = codeEditor.getModel(); + const language = model ? model.getLanguageIdentifier().language : undefined; + const adapters = this.adapters.filter(a => a.languages && a.languages.indexOf(language) >= 0); + if (adapters.length === 1) { + return TPromise.as(adapters[0]); + } + } + } + + return this.quickOpenService.pick([...this.adapters.filter(a => a.hasInitialConfiguration() || a.hasConfigurationProvider), { label: 'More...', separator: { border: true } }], { placeHolder: nls.localize('selectDebug', "Select Environment") }) + .then(picked => { + if (picked instanceof Adapter) { + return picked; + } + if (picked) { + this.commandService.executeCommand('debug.installAdditionalDebuggers'); + } + return undefined; + }); + } + + public getStartSessionCommand(type?: string): TPromise<{ command: string, type: string }> { + return this.guessAdapter(type).then(adapter => { + if (adapter) { + return { + command: adapter.startSessionCommand, + type: adapter.type + }; + } + return undefined; + }); + } + + private store(): void { + this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE); + if (this._selectedLaunch) { + this.storageService.store(DEBUG_SELECTED_ROOT, this._selectedLaunch.workspaceUri.toString(), StorageScope.WORKSPACE); + } + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } +} + +class Launch implements ILaunch { + + public name: string; + + constructor( + private configurationManager: ConfigurationManager, + public workspaceUri: uri, + @IFileService private fileService: IFileService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IConfigurationService private configurationService: IConfigurationService, + @IConfigurationResolverService private configurationResolverService: IConfigurationResolverService, + ) { + this.name = paths.basename(this.workspaceUri.fsPath); + } + + public getCompound(name: string): ICompound { + const config = this.configurationService.getConfiguration('launch', { resource: this.workspaceUri }); + if (!config || !config.compounds) { + return null; + } + + return config.compounds.filter(compound => compound.name === name).pop(); + } + + public getConfigurationNames(): string[] { + const config = this.configurationService.getConfiguration('launch', { resource: this.workspaceUri }); + if (!config || !config.configurations) { + return []; + } else { + const names = config.configurations.filter(cfg => cfg && typeof cfg.name === 'string').map(cfg => cfg.name); + if (names.length > 0 && config.compounds) { + if (config.compounds) { + names.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length) + .map(compound => compound.name)); + } + } + + return names; + } + } + + public getConfiguration(name: string): IConfig { + const config = this.configurationService.getConfiguration('launch', { resource: this.workspaceUri }); + if (!config || !config.configurations) { + return null; + } + + return config.configurations.filter(config => config && config.name === name).shift(); + } + + public resolveConfiguration(config: IConfig): TPromise { + const result = objects.deepClone(config) as IConfig; + // Set operating system specific properties #1873 + const setOSProperties = (flag: boolean, osConfig: IEnvConfig) => { + if (flag && osConfig) { + Object.keys(osConfig).forEach(key => { + result[key] = osConfig[key]; + }); + } + }; + setOSProperties(isWindows, result.windows); + setOSProperties(isMacintosh, result.osx); + setOSProperties(isLinux, result.linux); + + // massage configuration attributes - append workspace path to relatvie paths, substitute variables in paths. + Object.keys(result).forEach(key => { + result[key] = this.configurationResolverService.resolveAny(this.workspaceUri, result[key]); + }); + + const adapter = this.configurationManager.getAdapter(result.type); + return this.configurationResolverService.resolveInteractiveVariables(result, adapter ? adapter.variables : null); + } + + public get uri(): uri { + return uri.file(paths.join(this.workspaceUri.fsPath, '/.vscode/launch.json')); + } + + public openConfigFile(sideBySide: boolean, type?: string): TPromise { + const resource = this.uri; + let configFileCreated = false; + + return this.fileService.resolveContent(resource).then(content => content, err => { + + // launch.json not found: create one by collecting launch configs from debugConfigProviders + + return this.configurationManager.guessAdapter(type).then(adapter => { + if (adapter) { + return this.configurationManager.provideDebugConfigurations(this.workspaceUri, adapter.type).then(initialConfigs => { + return adapter.getInitialConfigurationContent(this.workspaceUri, initialConfigs); + }); + } else { + return undefined; + } + }).then(content => { + + if (!content) { + return undefined; + } + + configFileCreated = true; + return this.fileService.updateContent(resource, content).then(() => { + // convert string into IContent; see #32135 + return { value: content }; + }); + }); + }).then(content => { + if (!content) { + return undefined; + } + const index = content.value.indexOf(`"${this.configurationManager.selectedName}"`); + let startLineNumber = 1; + for (let i = 0; i < index; i++) { + if (content.value.charAt(i) === '\n') { + startLineNumber++; + } + } + const selection = startLineNumber > 1 ? { startLineNumber, startColumn: 4 } : undefined; + + return this.editorService.openEditor({ + resource: resource, + options: { + forceOpen: true, + selection, + pinned: configFileCreated, // pin only if config file is created #8727 + revealIfVisible: true + }, + }, sideBySide); + }, (error) => { + throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error)); + }); + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts new file mode 100644 index 0000000000..15447b8f25 --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts @@ -0,0 +1,620 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as errors from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import * as env from 'vs/base/common/platform'; +import uri from 'vs/base/common/uri'; +import { visit } from 'vs/base/common/json'; +import { Constants } from 'vs/editor/common/core/uint'; +import { IAction, Action } from 'vs/base/common/actions'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { StandardTokenType } from 'vs/editor/common/modes'; +import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { IDecorationOptions, IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/editorCommon'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { Range } from 'vs/editor/common/core/range'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView'; +import { DebugHoverWidget } from 'vs/workbench/parts/debug/electron-browser/debugHover'; +import { RemoveBreakpointAction, EditConditionalBreakpointAction, EnableBreakpointAction, DisableBreakpointAction, AddConditionalBreakpointAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo } from 'vs/workbench/parts/debug/common/debug'; +import { BreakpointWidget } from 'vs/workbench/parts/debug/browser/breakpointWidget'; +import { ExceptionWidget } from 'vs/workbench/parts/debug/browser/exceptionWidget'; +import { FloatingClickWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { Position } from 'vs/editor/common/core/position'; +import { CoreEditingCommands } from 'vs/editor/common/controller/coreCommands'; +import { first } from 'vs/base/common/arrays'; + +const HOVER_DELAY = 300; +const LAUNCH_JSON_REGEX = /launch\.json$/; +const REMOVE_INLINE_VALUES_DELAY = 100; +const INLINE_VALUE_DECORATION_KEY = 'inlinevaluedecoration'; +const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We want to limit ourselves for perf reasons +const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added +const MAX_TOKENIZATION_LINE_LEN = 500; // If line is too long, then inline values for the line are skipped + +@editorContribution +export class DebugEditorContribution implements IDebugEditorContribution { + + private toDispose: lifecycle.IDisposable[]; + private hoverWidget: DebugHoverWidget; + private showHoverScheduler: RunOnceScheduler; + private hideHoverScheduler: RunOnceScheduler; + private removeInlineValuesScheduler: RunOnceScheduler; + private hoverRange: Range; + + private breakpointHintDecoration: string[]; + private breakpointWidget: BreakpointWidget; + private breakpointWidgetVisible: IContextKey; + private wordToLineNumbersMap: Map; + + private exceptionWidget: ExceptionWidget; + + private configurationWidget: FloatingClickWidget; + + constructor( + private editor: ICodeEditor, + @IDebugService private debugService: IDebugService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IInstantiationService private instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, + @ICommandService private commandService: ICommandService, + @ICodeEditorService private codeEditorService: ICodeEditorService, + @ITelemetryService private telemetryService: ITelemetryService, + @IListService listService: IListService, + @IConfigurationService private configurationService: IConfigurationService, + @IThemeService themeService: IThemeService + ) { + this.breakpointHintDecoration = []; + this.hoverWidget = new DebugHoverWidget(this.editor, this.debugService, listService, this.instantiationService, themeService); + this.toDispose = []; + this.showHoverScheduler = new RunOnceScheduler(() => this.showHover(this.hoverRange, false), HOVER_DELAY); + this.hideHoverScheduler = new RunOnceScheduler(() => this.hoverWidget.hide(), HOVER_DELAY); + this.removeInlineValuesScheduler = new RunOnceScheduler(() => this.editor.removeDecorations(INLINE_VALUE_DECORATION_KEY), REMOVE_INLINE_VALUES_DELAY); + this.registerListeners(); + this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService); + this.updateConfigurationWidgetVisibility(); + this.codeEditorService.registerDecorationType(INLINE_VALUE_DECORATION_KEY, {}); + this.toggleExceptionWidget(); + } + + private getContextMenuActions(breakpoints: IBreakpoint[], uri: uri, lineNumber: number): TPromise<(IAction | ContextSubMenu)[]> { + const actions: (IAction | ContextSubMenu)[] = []; + if (breakpoints.length === 1) { + actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL)); + actions.push(this.instantiationService.createInstance(EditConditionalBreakpointAction, EditConditionalBreakpointAction.ID, EditConditionalBreakpointAction.LABEL, this.editor)); + if (breakpoints[0].enabled) { + actions.push(this.instantiationService.createInstance(DisableBreakpointAction, DisableBreakpointAction.ID, DisableBreakpointAction.LABEL)); + } else { + actions.push(this.instantiationService.createInstance(EnableBreakpointAction, EnableBreakpointAction.ID, EnableBreakpointAction.LABEL)); + } + } else if (breakpoints.length > 1) { + const sorted = breakpoints.sort((first, second) => first.column - second.column); + actions.push(new ContextSubMenu(nls.localize('removeBreakpoints', "Remove Breakpoints"), sorted.map(bp => new Action( + 'removeColumnBreakpoint', + bp.column ? nls.localize('removeBreakpointOnColumn', "Remove Breakpoint on Column {0}", bp.column) : nls.localize('removeLineBreakpoint', "Remove Line Breakpoint"), + null, + true, + () => this.debugService.removeBreakpoints(bp.getId()) + )))); + + actions.push(new ContextSubMenu(nls.localize('editBreakpoints', "Edit Breakpoints"), sorted.map(bp => + new Action('editBreakpoint', + bp.column ? nls.localize('editBreakpointOnColumn', "Edit Breakpoint on Column {0}", bp.column) : nls.localize('editLineBrekapoint', "Edit Line Breakpoint"), + null, + true, + () => TPromise.as(this.editor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(bp.lineNumber, bp.column)) + ) + ))); + + actions.push(new ContextSubMenu(nls.localize('enableDisableBreakpoints', "Enable/Disable Breakpoints"), sorted.map(bp => new Action( + bp.enabled ? 'disableColumnBreakpoint' : 'enableColumnBreakpoint', + bp.enabled ? (bp.column ? nls.localize('disableColumnBreakpoint', "Disable Breakpoint on Column {0}", bp.column) : nls.localize('disableBreakpointOnLine', "Disable Line Breakpoint")) + : (bp.column ? nls.localize('enableBreakpoints', "Enable Breakpoint on Column {0}", bp.column) : nls.localize('enableBreakpointOnLine', "Enable Line Breakpoint")), + null, + true, + () => this.debugService.enableOrDisableBreakpoints(!bp.enabled, bp) + )))); + } else { + actions.push(new Action( + 'addBreakpoint', + nls.localize('addBreakpoint', "Add Breakpoint"), + null, + true, + () => this.debugService.addBreakpoints(uri, [{ lineNumber }]) + )); + actions.push(this.instantiationService.createInstance(AddConditionalBreakpointAction, AddConditionalBreakpointAction.ID, AddConditionalBreakpointAction.LABEL, this.editor, lineNumber)); + } + + return TPromise.as(actions); + } + + private registerListeners(): void { + this.toDispose.push(this.editor.onMouseDown((e: IEditorMouseEvent) => { + if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || /* after last line */ e.target.detail || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { + return; + } + const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(this.editor.getModel()); + const lineNumber = e.target.position.lineNumber; + const uri = this.editor.getModel().uri; + + if (e.event.rightButton || (env.isMacintosh && e.event.leftButton && e.event.ctrlKey)) { + if (!canSetBreakpoints) { + return; + } + + const anchor = { x: e.event.posx + 1, y: e.event.posy }; + const breakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp.lineNumber === lineNumber && bp.uri.toString() === uri.toString()); + + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this.getContextMenuActions(breakpoints, uri, lineNumber), + getActionsContext: () => breakpoints.length ? breakpoints[0] : undefined + }); + } else { + const breakpoints = this.debugService.getModel().getBreakpoints() + .filter(bp => bp.uri.toString() === uri.toString() && bp.lineNumber === lineNumber); + + if (breakpoints.length) { + breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId())); + } else if (canSetBreakpoints) { + this.debugService.addBreakpoints(uri, [{ lineNumber }]); + } + } + })); + + this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => { + let showBreakpointHintAtLineNumber = -1; + if (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN && this.debugService.getConfigurationManager().canSetBreakpointsIn(this.editor.getModel()) && + this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { + if (!e.target.detail) { + // is not after last line + showBreakpointHintAtLineNumber = e.target.position.lineNumber; + } + } + this.ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber); + })); + this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => { + this.ensureBreakpointHintDecoration(-1); + })); + this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(e => this.onFocusStackFrame(e.stackFrame))); + + // hover listeners & hover widget + this.toDispose.push(this.editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(e))); + this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => this.onEditorMouseMove(e))); + this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => { + const rect = this.hoverWidget.getDomNode().getBoundingClientRect(); + // Only hide the hover widget if the editor mouse leave event is outside the hover widget #3528 + if (e.event.posx < rect.left || e.event.posx > rect.right || e.event.posy < rect.top || e.event.posy > rect.bottom) { + this.hideHoverWidget(); + } + })); + this.toDispose.push(this.editor.onKeyDown((e: IKeyboardEvent) => this.onKeyDown(e))); + this.toDispose.push(this.editor.onDidChangeModelContent(() => { + this.wordToLineNumbersMap = null; + })); + this.toDispose.push(this.editor.onDidChangeModel(() => { + const sf = this.debugService.getViewModel().focusedStackFrame; + const model = this.editor.getModel(); + this.editor.updateOptions({ hover: !sf || !model || model.uri.toString() !== sf.source.uri.toString() }); + this.closeBreakpointWidget(); + this.toggleExceptionWidget(); + this.hideHoverWidget(); + this.updateConfigurationWidgetVisibility(); + this.wordToLineNumbersMap = null; + this.updateInlineDecorations(sf); + })); + this.toDispose.push(this.editor.onDidScrollChange(() => this.hideHoverWidget)); + this.toDispose.push(this.debugService.onDidChangeState((state: State) => { + if (state !== State.Stopped) { + this.toggleExceptionWidget(); + } + })); + } + + public getId(): string { + return EDITOR_CONTRIBUTION_ID; + } + + public showHover(range: Range, focus: boolean): TPromise { + const sf = this.debugService.getViewModel().focusedStackFrame; + const model = this.editor.getModel(); + if (sf && model && sf.source.uri.toString() === model.uri.toString()) { + return this.hoverWidget.showAt(range, focus); + } + return undefined; + } + + private marginFreeFromNonDebugDecorations(line: number): boolean { + const decorations = this.editor.getLineDecorations(line); + if (decorations) { + for (const { options } of decorations) { + if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf('debug') === -1) { + return false; + } + } + } + + return true; + } + + private ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber: number): void { + const newDecoration: IModelDeltaDecoration[] = []; + if (showBreakpointHintAtLineNumber !== -1) { + newDecoration.push({ + options: DebugEditorContribution.BREAKPOINT_HELPER_DECORATION, + range: { + startLineNumber: showBreakpointHintAtLineNumber, + startColumn: 1, + endLineNumber: showBreakpointHintAtLineNumber, + endColumn: 1 + } + }); + } + + this.breakpointHintDecoration = this.editor.deltaDecorations(this.breakpointHintDecoration, newDecoration); + } + + private onFocusStackFrame(sf: IStackFrame): void { + const model = this.editor.getModel(); + if (model && sf && sf.source.uri.toString() === model.uri.toString()) { + this.editor.updateOptions({ hover: false }); + this.toggleExceptionWidget(); + } else { + this.editor.updateOptions({ hover: true }); + this.hideHoverWidget(); + } + + this.updateInlineDecorations(sf); + } + + private hideHoverWidget(): void { + if (!this.hideHoverScheduler.isScheduled() && this.hoverWidget.isVisible()) { + this.hideHoverScheduler.schedule(); + } + this.showHoverScheduler.cancel(); + } + + // hover business + + private onEditorMouseDown(mouseEvent: IEditorMouseEvent): void { + if (mouseEvent.target.type === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === DebugHoverWidget.ID) { + return; + } + + this.hideHoverWidget(); + } + + private onEditorMouseMove(mouseEvent: IEditorMouseEvent): void { + if (this.debugService.state !== State.Stopped) { + return; + } + + const targetType = mouseEvent.target.type; + const stopKey = env.isMacintosh ? 'metaKey' : 'ctrlKey'; + + if (targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === DebugHoverWidget.ID && !(mouseEvent.event)[stopKey]) { + // mouse moved on top of debug hover widget + return; + } + if (targetType === MouseTargetType.CONTENT_TEXT) { + if (!mouseEvent.target.range.equalsRange(this.hoverRange)) { + this.hoverRange = mouseEvent.target.range; + this.showHoverScheduler.schedule(); + } + } else { + this.hideHoverWidget(); + } + } + + private onKeyDown(e: IKeyboardEvent): void { + const stopKey = env.isMacintosh ? KeyCode.Meta : KeyCode.Ctrl; + if (e.keyCode !== stopKey) { + // do not hide hover when Ctrl/Meta is pressed + this.hideHoverWidget(); + } + } + + // end hover business + + // breakpoint widget + public showBreakpointWidget(lineNumber: number, column: number): void { + if (this.breakpointWidget) { + this.breakpointWidget.dispose(); + } + + this.breakpointWidget = this.instantiationService.createInstance(BreakpointWidget, this.editor, lineNumber, column); + this.breakpointWidget.show({ lineNumber, column: 1 }, 2); + this.breakpointWidgetVisible.set(true); + } + + public closeBreakpointWidget(): void { + if (this.breakpointWidget) { + this.breakpointWidget.dispose(); + this.breakpointWidget = null; + this.breakpointWidgetVisible.reset(); + this.editor.focus(); + } + } + + // exception widget + private toggleExceptionWidget(): void { + // Toggles exception widget based on the state of the current editor model and debug stack frame + const model = this.editor.getModel(); + const focusedSf = this.debugService.getViewModel().focusedStackFrame; + const callStack = focusedSf ? focusedSf.thread.getCallStack() : null; + if (!model || !focusedSf || !callStack || callStack.length === 0) { + this.closeExceptionWidget(); + return; + } + + // First call stack frame that is available is the frame where exception has been thrown + const exceptionSf = first(callStack, sf => sf.source && sf.source.available, undefined); + if (!exceptionSf) { + this.closeExceptionWidget(); + return; + } + + const sameUri = exceptionSf.source.uri.toString() === model.uri.toString(); + if (this.exceptionWidget && !sameUri) { + this.closeExceptionWidget(); + } else if (sameUri) { + focusedSf.thread.exceptionInfo.then(exceptionInfo => { + if (exceptionInfo && exceptionSf.range.startLineNumber && exceptionSf.range.startColumn) { + this.showExceptionWidget(exceptionInfo, exceptionSf.range.startLineNumber, exceptionSf.range.startColumn); + } + }); + } + } + + private showExceptionWidget(exceptionInfo: IExceptionInfo, lineNumber: number, column: number): void { + if (this.exceptionWidget) { + this.exceptionWidget.dispose(); + } + + this.exceptionWidget = this.instantiationService.createInstance(ExceptionWidget, this.editor, exceptionInfo, lineNumber); + this.exceptionWidget.show({ lineNumber, column }, 0); + } + + private closeExceptionWidget(): void { + if (this.exceptionWidget) { + this.exceptionWidget.dispose(); + this.exceptionWidget = null; + } + } + + // configuration widget + private updateConfigurationWidgetVisibility(): void { + const model = this.editor.getModel(); + if (model && LAUNCH_JSON_REGEX.test(model.uri.toString())) { + this.configurationWidget = this.instantiationService.createInstance(FloatingClickWidget, this.editor, nls.localize('addConfiguration', "Add Configuration..."), null); + this.configurationWidget.render(); + this.toDispose.push(this.configurationWidget.onClick(() => this.addLaunchConfiguration().done(undefined, errors.onUnexpectedError))); + } else if (this.configurationWidget) { + this.configurationWidget.dispose(); + } + } + + public addLaunchConfiguration(): TPromise { + this.telemetryService.publicLog('debug/addLaunchConfiguration'); + let configurationsArrayPosition: Position; + const model = this.editor.getModel(); + let depthInArray = 0; + let lastProperty: string; + + visit(model.getValue(), { + onObjectProperty: (property, offset, length) => { + lastProperty = property; + }, + onArrayBegin: (offset: number, length: number) => { + if (lastProperty === 'configurations' && depthInArray === 0) { + configurationsArrayPosition = model.getPositionAt(offset + 1); + } + depthInArray++; + }, + onArrayEnd: () => { + depthInArray--; + } + }); + + this.editor.focus(); + if (!configurationsArrayPosition) { + return this.commandService.executeCommand('editor.action.triggerSuggest'); + } + + const insertLine = (position: Position): TPromise => { + // Check if there are more characters on a line after a "configurations": [, if yes enter a newline + if (this.editor.getModel().getLineLastNonWhitespaceColumn(position.lineNumber) > position.column) { + this.editor.setPosition(position); + CoreEditingCommands.LineBreakInsert.runEditorCommand(null, this.editor, null); + } + // Check if there is already an empty line to insert suggest, if yes just place the cursor + if (this.editor.getModel().getLineLastNonWhitespaceColumn(position.lineNumber + 1) === 0) { + this.editor.setPosition({ lineNumber: position.lineNumber + 1, column: Constants.MAX_SAFE_SMALL_INTEGER }); + return TPromise.as(null); + } + + this.editor.setPosition(position); + return this.commandService.executeCommand('editor.action.insertLineAfter'); + }; + + return insertLine(configurationsArrayPosition).then(() => this.commandService.executeCommand('editor.action.triggerSuggest')); + } + + private static BREAKPOINT_HELPER_DECORATION: IModelDecorationOptions = { + glyphMarginClassName: 'debug-breakpoint-hint-glyph', + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges + }; + + // Inline Decorations + private updateInlineDecorations(stackFrame: IStackFrame): void { + const model = this.editor.getModel(); + if (!this.configurationService.getConfiguration('debug').inlineValues || + !model || !stackFrame || model.uri.toString() !== stackFrame.source.uri.toString()) { + if (!this.removeInlineValuesScheduler.isScheduled()) { + this.removeInlineValuesScheduler.schedule(); + } + return; + } + + this.removeInlineValuesScheduler.cancel(); + + stackFrame.getMostSpecificScopes(stackFrame.range) + // Get all top level children in the scope chain + .then(scopes => TPromise.join(scopes.map(scope => scope.getChildren() + .then(children => { + let range = new Range(0, 0, stackFrame.range.startLineNumber, stackFrame.range.startColumn); + if (scope.range) { + range = range.setStartPosition(scope.range.startLineNumber, scope.range.startColumn); + } + + return this.createInlineValueDecorationsInsideRange(children, range); + }))).then(decorationsPerScope => { + const allDecorations = decorationsPerScope.reduce((previous, current) => previous.concat(current), []); + this.editor.setDecorations(INLINE_VALUE_DECORATION_KEY, allDecorations); + })); + } + + private createInlineValueDecorationsInsideRange(expressions: IExpression[], range: Range): IDecorationOptions[] { + const nameValueMap = new Map(); + for (let expr of expressions) { + nameValueMap.set(expr.name, expr.value); + // Limit the size of map. Too large can have a perf impact + if (nameValueMap.size >= MAX_NUM_INLINE_VALUES) { + break; + } + } + + const lineToNamesMap: Map = new Map(); + const wordToPositionsMap = this.getWordToPositionsMap(); + + // Compute unique set of names on each line + nameValueMap.forEach((value, name) => { + if (wordToPositionsMap.has(name)) { + for (let position of wordToPositionsMap.get(name)) { + if (range.containsPosition(position)) { + if (!lineToNamesMap.has(position.lineNumber)) { + lineToNamesMap.set(position.lineNumber, []); + } + + if (lineToNamesMap.get(position.lineNumber).indexOf(name) === -1) { + lineToNamesMap.get(position.lineNumber).push(name); + } + } + } + } + }); + + const decorations: IDecorationOptions[] = []; + // Compute decorators for each line + lineToNamesMap.forEach((names, line) => { + const contentText = names.sort((first, second) => { + const content = this.editor.getModel().getLineContent(line); + return content.indexOf(first) - content.indexOf(second); + }).map(name => `${name} = ${nameValueMap.get(name)}`).join(', '); + decorations.push(this.createInlineValueDecoration(line, contentText)); + }); + + return decorations; + } + + private createInlineValueDecoration(lineNumber: number, contentText: string): IDecorationOptions { + // If decoratorText is too long, trim and add ellipses. This could happen for minified files with everything on a single line + if (contentText.length > MAX_INLINE_DECORATOR_LENGTH) { + contentText = contentText.substr(0, MAX_INLINE_DECORATOR_LENGTH) + '...'; + } + + return { + range: { + startLineNumber: lineNumber, + endLineNumber: lineNumber, + startColumn: Constants.MAX_SAFE_SMALL_INTEGER, + endColumn: Constants.MAX_SAFE_SMALL_INTEGER + }, + renderOptions: { + after: { + contentText, + backgroundColor: 'rgba(255, 200, 0, 0.2)', + margin: '10px' + }, + dark: { + after: { + color: 'rgba(255, 255, 255, 0.5)', + } + }, + light: { + after: { + color: 'rgba(0, 0, 0, 0.5)', + } + } + } + }; + } + + private getWordToPositionsMap(): Map { + if (!this.wordToLineNumbersMap) { + this.wordToLineNumbersMap = new Map(); + const model = this.editor.getModel(); + // For every word in every line, map its ranges for fast lookup + for (let lineNumber = 1, len = model.getLineCount(); lineNumber <= len; ++lineNumber) { + const lineContent = model.getLineContent(lineNumber); + + // If line is too long then skip the line + if (lineContent.length > MAX_TOKENIZATION_LINE_LEN) { + continue; + } + + model.forceTokenization(lineNumber); + const lineTokens = model.getLineTokens(lineNumber); + for (let token = lineTokens.firstToken(); !!token; token = token.next()) { + const tokenStr = lineContent.substring(token.startOffset, token.endOffset); + + // Token is a word and not a comment + if (token.tokenType === StandardTokenType.Other) { + DEFAULT_WORD_REGEXP.lastIndex = 0; // We assume tokens will usually map 1:1 to words if they match + const wordMatch = DEFAULT_WORD_REGEXP.exec(tokenStr); + + if (wordMatch) { + const word = wordMatch[0]; + if (!this.wordToLineNumbersMap.has(word)) { + this.wordToLineNumbersMap.set(word, []); + } + + this.wordToLineNumbersMap.get(word).push(new Position(lineNumber, token.startOffset)); + } + } + } + } + } + + return this.wordToLineNumbersMap; + } + + public dispose(): void { + if (this.breakpointWidget) { + this.breakpointWidget.dispose(); + } + if (this.hoverWidget) { + this.hoverWidget.dispose(); + } + if (this.configurationWidget) { + this.configurationWidget.dispose(); + } + this.toDispose = lifecycle.dispose(this.toDispose); + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts new file mode 100644 index 0000000000..ff1b6f570e --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts @@ -0,0 +1,357 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import * as dom from 'vs/base/browser/dom'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { DefaultController, ICancelableEvent, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { IContentWidget, ICodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IDebugService, IExpression, IExpressionContainer } from 'vs/workbench/parts/debug/common/debug'; +import { Expression } from 'vs/workbench/parts/debug/common/debugModel'; +import { VariablesRenderer, renderExpressionValue, VariablesDataSource } from 'vs/workbench/parts/debug/electron-browser/debugViewer'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { attachListStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; + +const $ = dom.$; +const MAX_ELEMENTS_SHOWN = 18; + +export class DebugHoverWidget implements IContentWidget { + + public static ID = 'debug.hoverWidget'; + // editor.IContentWidget.allowEditorOverflow + public allowEditorOverflow = true; + + private _isVisible: boolean; + private domNode: HTMLElement; + private tree: ITree; + private showAtPosition: Position; + private highlightDecorations: string[]; + private complexValueContainer: HTMLElement; + private treeContainer: HTMLElement; + private complexValueTitle: HTMLElement; + private valueContainer: HTMLElement; + private stoleFocus: boolean; + private toDispose: lifecycle.IDisposable[]; + private scrollbar: DomScrollableElement; + + constructor( + private editor: ICodeEditor, + private debugService: IDebugService, + private listService: IListService, + instantiationService: IInstantiationService, + private themeService: IThemeService + ) { + this.toDispose = []; + this.create(instantiationService); + this.registerListeners(); + + this.valueContainer = $('.value'); + this.valueContainer.tabIndex = 0; + this.valueContainer.setAttribute('role', 'tooltip'); + this.scrollbar = new DomScrollableElement(this.valueContainer, { horizontal: ScrollbarVisibility.Hidden }); + this.domNode.appendChild(this.scrollbar.getDomNode()); + this.toDispose.push(this.scrollbar); + + this._isVisible = false; + this.showAtPosition = null; + this.highlightDecorations = []; + + this.editor.addContentWidget(this); + this.editor.applyFontInfo(this.domNode); + } + + private create(instantiationService: IInstantiationService): void { + this.domNode = $('.debug-hover-widget'); + this.complexValueContainer = dom.append(this.domNode, $('.complex-value')); + this.complexValueTitle = dom.append(this.complexValueContainer, $('.title')); + this.treeContainer = dom.append(this.complexValueContainer, $('.debug-hover-tree')); + this.treeContainer.setAttribute('role', 'tree'); + this.tree = new Tree(this.treeContainer, { + dataSource: new VariablesDataSource(), + renderer: instantiationService.createInstance(VariablesHoverRenderer), + controller: new DebugHoverController(this.editor) + }, { + indentPixels: 6, + twistiePixels: 15, + ariaLabel: nls.localize('treeAriaLabel', "Debug Hover"), + keyboardSupport: false + }); + + this.toDispose.push(attachListStyler(this.tree, this.themeService)); + this.toDispose.push(this.listService.register(this.tree)); + this.toDispose.push(attachStylerCallback(this.themeService, { editorHoverBackground, editorHoverBorder }, colors => { + this.domNode.style.backgroundColor = colors.editorHoverBackground; + if (colors.editorHoverBorder) { + this.domNode.style.border = `1px solid ${colors.editorHoverBorder}`; + } else { + this.domNode.style.border = null; + } + })); + } + + private registerListeners(): void { + this.toDispose.push(this.tree.addListener('item:expanded', () => { + this.layoutTree(); + })); + this.toDispose.push(this.tree.addListener('item:collapsed', () => { + this.layoutTree(); + })); + + this.toDispose.push(dom.addStandardDisposableListener(this.domNode, 'keydown', (e: IKeyboardEvent) => { + if (e.equals(KeyCode.Escape)) { + this.hide(); + } + })); + this.toDispose.push(this.editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { + if (e.fontInfo) { + this.editor.applyFontInfo(this.domNode); + } + })); + } + + public isVisible(): boolean { + return this._isVisible; + } + + public getId(): string { + return DebugHoverWidget.ID; + } + + public getDomNode(): HTMLElement { + return this.domNode; + } + + private getExactExpressionRange(lineContent: string, range: Range): Range { + let matchingExpression: string = undefined; + let startOffset = 0; + + // Some example supported expressions: myVar.prop, a.b.c.d, myVar?.prop, myVar->prop, MyClass::StaticProp, *myVar + // Match any character except a set of characters which often break interesting sub-expressions + let expression: RegExp = /([^()\[\]{}<>\s+\-/%~#^;=|,`!]|\->)+/g; + let result: RegExpExecArray = undefined; + + // First find the full expression under the cursor + while (result = expression.exec(lineContent)) { + let start = result.index + 1; + let end = start + result[0].length; + + if (start <= range.startColumn && end >= range.endColumn) { + matchingExpression = result[0]; + startOffset = start; + break; + } + } + + // If there are non-word characters after the cursor, we want to truncate the expression then. + // For example in expression 'a.b.c.d', if the focus was under 'b', 'a.b' would be evaluated. + if (matchingExpression) { + let subExpression: RegExp = /\w+/g; + let subExpressionResult: RegExpExecArray = undefined; + while (subExpressionResult = subExpression.exec(matchingExpression)) { + let subEnd = subExpressionResult.index + 1 + startOffset + subExpressionResult[0].length; + if (subEnd >= range.endColumn) { + break; + } + } + + if (subExpressionResult) { + matchingExpression = matchingExpression.substring(0, subExpression.lastIndex); + } + } + + return matchingExpression ? + new Range(range.startLineNumber, startOffset, range.endLineNumber, startOffset + matchingExpression.length - 1) : + new Range(range.startLineNumber, 0, range.endLineNumber, 0); + } + + public showAt(range: Range, focus: boolean): TPromise { + const pos = range.getStartPosition(); + + const process = this.debugService.getViewModel().focusedProcess; + const lineContent = this.editor.getModel().getLineContent(pos.lineNumber); + const expressionRange = this.getExactExpressionRange(lineContent, range); + // use regex to extract the sub-expression #9821 + const matchingExpression = lineContent.substring(expressionRange.startColumn - 1, expressionRange.endColumn); + if (!matchingExpression) { + return TPromise.as(this.hide()); + } + + let promise: TPromise; + if (process.session.capabilities.supportsEvaluateForHovers) { + const result = new Expression(matchingExpression); + promise = result.evaluate(process, this.debugService.getViewModel().focusedStackFrame, 'hover').then(() => result); + } else { + promise = this.findExpressionInStackFrame(matchingExpression.split('.').map(word => word.trim()).filter(word => !!word), expressionRange); + } + + return promise.then(expression => { + if (!expression || (expression instanceof Expression && !expression.available)) { + this.hide(); + return undefined; + } + + this.highlightDecorations = this.editor.deltaDecorations(this.highlightDecorations, [{ + range: new Range(pos.lineNumber, expressionRange.startColumn, pos.lineNumber, expressionRange.startColumn + matchingExpression.length), + options: { + className: 'hoverHighlight' + } + }]); + + return this.doShow(pos, expression, focus); + }); + } + + private doFindExpression(container: IExpressionContainer, namesToFind: string[]): TPromise { + if (!container) { + return TPromise.as(null); + } + + return container.getChildren().then(children => { + // look for our variable in the list. First find the parents of the hovered variable if there are any. + const filtered = children.filter(v => namesToFind[0] === v.name); + if (filtered.length !== 1) { + return null; + } + + if (namesToFind.length === 1) { + return filtered[0]; + } else { + return this.doFindExpression(filtered[0], namesToFind.slice(1)); + } + }); + } + + private findExpressionInStackFrame(namesToFind: string[], expressionRange: Range): TPromise { + return this.debugService.getViewModel().focusedStackFrame.getMostSpecificScopes(expressionRange) + .then(scopes => TPromise.join(scopes.map(scope => this.doFindExpression(scope, namesToFind)))) + .then(expressions => expressions.filter(exp => !!exp)) + // only show if all expressions found have the same value + .then(expressions => (expressions.length > 0 && expressions.every(e => e.value === expressions[0].value)) ? expressions[0] : null); + } + + private doShow(position: Position, expression: IExpression, focus: boolean, forceValueHover = false): TPromise { + this.showAtPosition = position; + this._isVisible = true; + this.stoleFocus = focus; + + if (!expression.hasChildren || forceValueHover) { + this.complexValueContainer.hidden = true; + this.valueContainer.hidden = false; + renderExpressionValue(expression, this.valueContainer, { + showChanged: false, + preserveWhitespace: true + }); + this.valueContainer.title = ''; + this.editor.layoutContentWidget(this); + this.scrollbar.scanDomNode(); + if (focus) { + this.editor.render(); + this.valueContainer.focus(); + } + + return TPromise.as(null); + } + + this.valueContainer.hidden = true; + this.complexValueContainer.hidden = false; + + return this.tree.setInput(expression).then(() => { + this.complexValueTitle.textContent = expression.value; + this.complexValueTitle.title = expression.value; + this.layoutTree(); + this.editor.layoutContentWidget(this); + this.scrollbar.scanDomNode(); + if (focus) { + this.editor.render(); + this.tree.DOMFocus(); + } + }); + } + + private layoutTree(): void { + const navigator = this.tree.getNavigator(); + let visibleElementsCount = 0; + while (navigator.next()) { + visibleElementsCount++; + } + + if (visibleElementsCount === 0) { + this.doShow(this.showAtPosition, this.tree.getInput(), false, true); + } else { + const height = Math.min(visibleElementsCount, MAX_ELEMENTS_SHOWN) * 18; + + if (this.treeContainer.clientHeight !== height) { + this.treeContainer.style.height = `${height}px`; + this.tree.layout(); + } + } + } + + public hide(): void { + if (!this._isVisible) { + return; + } + + this._isVisible = false; + this.editor.deltaDecorations(this.highlightDecorations, []); + this.highlightDecorations = []; + this.editor.layoutContentWidget(this); + if (this.stoleFocus) { + this.editor.focus(); + } + } + + public getPosition(): IContentWidgetPosition { + return this._isVisible ? { + position: this.showAtPosition, + preference: [ + ContentWidgetPositionPreference.ABOVE, + ContentWidgetPositionPreference.BELOW + ] + } : null; + } + + public dispose(): void { + this.toDispose = lifecycle.dispose(this.toDispose); + } +} + +class DebugHoverController extends DefaultController { + + constructor(private editor: ICodeEditor) { + super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false }); + } + + protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin = 'mouse'): boolean { + if (element.reference > 0) { + super.onLeftClick(tree, element, eventish, origin); + tree.clearFocus(); + tree.deselect(element); + this.editor.focus(); + } + + return true; + } +} + +class VariablesHoverRenderer extends VariablesRenderer { + + public getHeight(tree: ITree, element: any): number { + return 18; + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts new file mode 100644 index 0000000000..26164bfea3 --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -0,0 +1,1192 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import * as paths from 'vs/base/common/paths'; +import * as strings from 'vs/base/common/strings'; +import { generateUuid } from 'vs/base/common/uuid'; +import uri from 'vs/base/common/uri'; +import { Action } from 'vs/base/common/actions'; +import { first, distinct } from 'vs/base/common/arrays'; +import { isObject, isUndefinedOrNull } from 'vs/base/common/types'; +import * as errors from 'vs/base/common/errors'; +import severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as aria from 'vs/base/browser/ui/aria/aria'; +import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files'; +import { IMessageService, CloseAction } from 'vs/platform/message/common/message'; +import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; +import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import * as debug from 'vs/workbench/parts/debug/common/debug'; +import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession'; +import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, OutputNameValueElement, ExpressionContainer, Process } from 'vs/workbench/parts/debug/common/debugModel'; +import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel'; +import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions'; +import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager'; +import { ToggleMarkersPanelAction } from 'vs/workbench/parts/markers/browser/markersPanelActions'; +import { ITaskService, TaskServiceEvents, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService'; +import { TaskError } from 'vs/workbench/parts/tasks/common/taskSystem'; +import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; +import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService'; + +const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; +const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated'; +const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint'; +const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint'; +const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions'; + +interface StartSessionResult { + status: 'ok' | 'initialConfiguration' | 'saveConfiguration'; + content?: string; +}; + +export class DebugService implements debug.IDebugService { + public _serviceBrand: any; + + private sessionStates: Map; + private _onDidChangeState: Emitter; + private _onDidNewProcess: Emitter; + private _onDidEndProcess: Emitter; + private _onDidCustomEvent: Emitter; + private model: Model; + private viewModel: ViewModel; + private allSessionIds: Set; + private configurationManager: ConfigurationManager; + private customTelemetryService: ITelemetryService; + private toDispose: lifecycle.IDisposable[]; + private toDisposeOnSessionEnd: Map; + private inDebugMode: IContextKey; + private debugType: IContextKey; + private debugState: IContextKey; + private breakpointsToSendOnResourceSaved: Set; + private launchJsonChanged: boolean; + + constructor( + @IStorageService private storageService: IStorageService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @ITextFileService private textFileService: ITextFileService, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService, + @IMessageService private messageService: IMessageService, + @IPartService private partService: IPartService, + @IWindowsService private windowsService: IWindowsService, + @IWindowService private windowService: IWindowService, + @IBroadcastService private broadcastService: IBroadcastService, + @ITelemetryService private telemetryService: ITelemetryService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IContextKeyService contextKeyService: IContextKeyService, + @ILifecycleService lifecycleService: ILifecycleService, + @IInstantiationService private instantiationService: IInstantiationService, + @IExtensionService private extensionService: IExtensionService, + @IMarkerService private markerService: IMarkerService, + @ITaskService private taskService: ITaskService, + @IFileService private fileService: IFileService, + @IConfigurationService private configurationService: IConfigurationService, + @ICommandService private commandService: ICommandService + ) { + this.toDispose = []; + this.toDisposeOnSessionEnd = new Map(); + this.breakpointsToSendOnResourceSaved = new Set(); + this._onDidChangeState = new Emitter(); + this._onDidNewProcess = new Emitter(); + this._onDidEndProcess = new Emitter(); + this._onDidCustomEvent = new Emitter(); + this.sessionStates = new Map(); + this.allSessionIds = new Set(); + + this.configurationManager = this.instantiationService.createInstance(ConfigurationManager); + this.toDispose.push(this.configurationManager); + this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); + this.debugType = debug.CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); + this.debugState = debug.CONTEXT_DEBUG_STATE.bindTo(contextKeyService); + + this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(), + this.loadExceptionBreakpoints(), this.loadWatchExpressions()); + this.toDispose.push(this.model); + this.viewModel = new ViewModel(); + + this.registerListeners(lifecycleService); + } + + private registerListeners(lifecycleService: ILifecycleService): void { + this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); + lifecycleService.onShutdown(this.store, this); + lifecycleService.onShutdown(this.dispose, this); + this.toDispose.push(this.broadcastService.onBroadcast(this.onBroadcast, this)); + } + + private onBroadcast(broadcast: IBroadcast): void { + + // attach: PH is ready to be attached to + const process = this.model.getProcesses().filter(p => p.getId() === broadcast.payload.debugId).pop(); + const session = process ? process.session : null; + if (!this.allSessionIds.has(broadcast.payload.debugId)) { + // Ignore attach events for sessions that never existed (wrong vscode windows) + return; + } + + if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) { + if (session) { + this.onSessionEnd(session); + } + + const config = this.configurationManager.selectedLaunch.getConfiguration(this.configurationManager.selectedName); + this.configurationManager.selectedLaunch.resolveConfiguration(config).done(resolvedConfig => { + resolvedConfig.request = 'attach'; + resolvedConfig.port = broadcast.payload.port; + this.doCreateProcess(this.configurationManager.selectedLaunch.workspaceUri, resolvedConfig, broadcast.payload.debugId); + }, errors.onUnexpectedError); + + return; + } + + if (session && broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) { + this.onSessionEnd(session); + return; + } + + // from this point on we require an active session + if (!session) { + return; + } + + // an extension logged output, show it inside the REPL + if (broadcast.channel === EXTENSION_LOG_BROADCAST_CHANNEL) { + let extensionOutput: ILogEntry = broadcast.payload.logEntry; + let sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info; + + let args: any[] = []; + try { + let parsed = JSON.parse(extensionOutput.arguments); + args.push(...Object.getOwnPropertyNames(parsed).map(o => parsed[o])); + } catch (error) { + args.push(extensionOutput.arguments); + } + + // add output for each argument logged + let simpleVals: any[] = []; + for (let i = 0; i < args.length; i++) { + let a = args[i]; + + // undefined gets printed as 'undefined' + if (typeof a === 'undefined') { + simpleVals.push('undefined'); + } + + // null gets printed as 'null' + else if (a === null) { + simpleVals.push('null'); + } + + // objects & arrays are special because we want to inspect them in the REPL + else if (isObject(a) || Array.isArray(a)) { + + // flush any existing simple values logged + if (simpleVals.length) { + this.logToRepl(simpleVals.join(' '), sev); + simpleVals = []; + } + + // show object + this.logToRepl(new OutputNameValueElement((a).prototype, a, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev); + } + + // string: watch out for % replacement directive + // string substitution and formatting @ https://developer.chrome.com/devtools/docs/console + else if (typeof a === 'string') { + let buf = ''; + + for (let j = 0, len = a.length; j < len; j++) { + if (a[j] === '%' && (a[j + 1] === 's' || a[j + 1] === 'i' || a[j + 1] === 'd')) { + i++; // read over substitution + buf += !isUndefinedOrNull(args[i]) ? args[i] : ''; // replace + j++; // read over directive + } else { + buf += a[j]; + } + } + + simpleVals.push(buf); + } + + // number or boolean is joined together + else { + simpleVals.push(a); + } + } + + // flush simple values + // always append a new line for output coming from an extension such that separate logs go to separate lines #23695 + if (simpleVals.length) { + this.logToRepl(simpleVals.join(' ') + '\n', sev); + } + } + } + + private tryToAutoFocusStackFrame(thread: debug.IThread): TPromise { + const callStack = thread.getCallStack(); + if (!callStack.length || this.viewModel.focusedStackFrame) { + return TPromise.as(null); + } + + // focus first stack frame from top that has source location if no other stack frame is focused + const stackFrameToFocus = first(callStack, sf => sf.source && sf.source.available, undefined); + if (!stackFrameToFocus) { + return TPromise.as(null); + } + + this.focusStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError); + if (thread.stoppedDetails) { + this.windowService.focusWindow(); + aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber)); + } + + return stackFrameToFocus.openInEditor(this.editorService); + } + + private registerSessionListeners(process: Process, session: RawDebugSession): void { + + this.toDisposeOnSessionEnd.get(session.getId()).push(session); + + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidInitialize(event => { + aria.status(nls.localize('debuggingStarted', "Debugging started.")); + const sendConfigurationDone = () => { + if (session && session.capabilities.supportsConfigurationDoneRequest) { + return session.configurationDone().done(null, e => { + // Disconnect the debug session on configuration done error #10596 + if (session) { + session.disconnect().done(null, errors.onUnexpectedError); + } + this.messageService.show(severity.Error, e.message); + }); + } + }; + + this.sendAllBreakpoints(process).then(sendConfigurationDone, sendConfigurationDone) + .done(() => this.fetchThreads(session), errors.onUnexpectedError); + })); + + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidStop(event => { + this.updateStateAndEmit(session.getId(), debug.State.Stopped); + const threadId = event.body.threadId; + + session.threads().then(response => { + if (!response || !response.body || !response.body.threads) { + return; + } + + const rawThread = response.body.threads.filter(t => t.id === threadId).pop(); + this.model.rawUpdate({ + sessionId: session.getId(), + thread: rawThread, + threadId, + stoppedDetails: event.body, + allThreadsStopped: event.body.allThreadsStopped + }); + + const thread = process && process.getThread(threadId); + if (thread) { + // Call fetch call stack twice, the first only return the top stack frame. + // Second retrieves the rest of the call stack. For performance reasons #25605 + this.model.fetchCallStack(thread).then(() => { + return this.tryToAutoFocusStackFrame(thread); + }); + } + }, errors.onUnexpectedError); + })); + + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidThread(event => { + if (event.body.reason === 'started') { + this.fetchThreads(session).done(undefined, errors.onUnexpectedError); + } else if (event.body.reason === 'exited') { + this.model.clearThreads(session.getId(), true, event.body.threadId); + } + })); + + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidTerminateDebugee(event => { + aria.status(nls.localize('debuggingStopped', "Debugging stopped.")); + if (session && session.getId() === event.sessionId) { + if (event.body && event.body.restart && process) { + this.restartProcess(process, event.body.restart).done(null, err => this.messageService.show(severity.Error, err.message)); + } else { + session.disconnect().done(null, errors.onUnexpectedError); + } + } + })); + + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidContinued(event => { + const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId; + this.model.clearThreads(session.getId(), false, threadId); + if (this.viewModel.focusedProcess.getId() === session.getId()) { + this.focusStackFrameAndEvaluate(null, this.viewModel.focusedProcess).done(null, errors.onUnexpectedError); + } + this.updateStateAndEmit(session.getId(), debug.State.Running); + })); + + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidOutput(event => { + if (!event.body) { + return; + } + + const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info; + if (event.body.category === 'telemetry') { + // only log telemetry events from debug adapter if the adapter provided the telemetry key + // and the user opted in telemetry + if (this.customTelemetryService && this.telemetryService.isOptedIn) { + this.customTelemetryService.publicLog(event.body.output, event.body.data); + } + } else if (event.body.variablesReference) { + const container = new ExpressionContainer(process, event.body.variablesReference, generateUuid()); + container.getChildren().then(children => { + children.forEach(child => { + // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) + child.name = null; + this.logToRepl(child, outputSeverity); + }); + }); + } else if (typeof event.body.output === 'string') { + this.logToRepl(event.body.output, outputSeverity); + } + })); + + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => { + const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined; + const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop(); + if (breakpoint) { + if (!breakpoint.column) { + event.body.breakpoint.column = undefined; + } + this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint }); + } else { + const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop(); + if (functionBreakpoint) { + this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint }); + } + } + })); + + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidExitAdapter(event => { + // 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905 + const process = this.viewModel.focusedProcess; + if (process && session && process.getId() === session.getId() && strings.equalsIgnoreCase(process.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running && + process && this.contextService.hasWorkspace() && process.configuration.noDebug) { + this.broadcastService.broadcast({ + channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, + payload: [process.session.root.fsPath] + }); + } + if (session && session.getId() === event.sessionId) { + this.onSessionEnd(session); + } + })); + + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidCustomEvent(event => { + this._onDidCustomEvent.fire(event); + })); + } + + private fetchThreads(session: RawDebugSession): TPromise { + return session.threads().then(response => { + if (response && response.body && response.body.threads) { + response.body.threads.forEach(thread => + this.model.rawUpdate({ + sessionId: session.getId(), + threadId: thread.id, + thread + })); + } + }); + } + + private loadBreakpoints(): Breakpoint[] { + let result: Breakpoint[]; + try { + result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => { + return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition); + }); + } catch (e) { } + + return result || []; + } + + private loadFunctionBreakpoints(): FunctionBreakpoint[] { + let result: FunctionBreakpoint[]; + try { + result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => { + return new FunctionBreakpoint(fb.name, fb.enabled, fb.hitCondition); + }); + } catch (e) { } + + return result || []; + } + + private loadExceptionBreakpoints(): ExceptionBreakpoint[] { + let result: ExceptionBreakpoint[]; + try { + result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => { + return new ExceptionBreakpoint(exBreakpoint.filter || exBreakpoint.name, exBreakpoint.label, exBreakpoint.enabled); + }); + } catch (e) { } + + return result || []; + } + + private loadWatchExpressions(): Expression[] { + let result: Expression[]; + try { + result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string, id: string }) => { + return new Expression(watchStoredData.name, watchStoredData.id); + }); + } catch (e) { } + + return result || []; + } + + public get state(): debug.State { + const focusedThread = this.viewModel.focusedThread; + if (focusedThread && focusedThread.stopped) { + return debug.State.Stopped; + } + const focusedProcess = this.viewModel.focusedProcess; + if (focusedProcess && this.sessionStates.has(focusedProcess.getId())) { + return this.sessionStates.get(focusedProcess.getId()); + } + if (this.sessionStates.size > 0) { + return debug.State.Initializing; + } + + return debug.State.Inactive; + } + + public get onDidChangeState(): Event { + return this._onDidChangeState.event; + } + + public get onDidNewProcess(): Event { + return this._onDidNewProcess.event; + } + + public get onDidEndProcess(): Event { + return this._onDidEndProcess.event; + } + + public get onDidCustomEvent(): Event { + return this._onDidCustomEvent.event; + } + + private updateStateAndEmit(sessionId?: string, newState?: debug.State): void { + if (sessionId) { + if (newState === debug.State.Inactive) { + this.sessionStates.delete(sessionId); + } else { + this.sessionStates.set(sessionId, newState); + } + } + + const state = this.state; + const stateLabel = debug.State[state]; + if (stateLabel) { + this.debugState.set(stateLabel.toLowerCase()); + } + this._onDidChangeState.fire(state); + } + + public focusStackFrameAndEvaluate(stackFrame: debug.IStackFrame, process?: debug.IProcess, explicit?: boolean): TPromise { + if (!process) { + const processes = this.model.getProcesses(); + process = stackFrame ? stackFrame.thread.process : processes.length ? processes[0] : null; + } + if (!stackFrame) { + const threads = process ? process.getAllThreads() : null; + const callStack = threads && threads.length ? threads[0].getCallStack() : null; + stackFrame = callStack && callStack.length ? callStack[0] : null; + } + + this.viewModel.setFocusedStackFrame(stackFrame, process, explicit); + this.updateStateAndEmit(); + + return this.model.evaluateWatchExpressions(process, stackFrame); + } + + public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise { + if (breakpoint) { + this.model.setEnablement(breakpoint, enable); + if (breakpoint instanceof Breakpoint) { + return this.sendBreakpoints(breakpoint.uri); + } else if (breakpoint instanceof FunctionBreakpoint) { + return this.sendFunctionBreakpoints(); + } + + return this.sendExceptionBreakpoints(); + } + + this.model.enableOrDisableAllBreakpoints(enable); + return this.sendAllBreakpoints(); + } + + public addBreakpoints(uri: uri, rawBreakpoints: debug.IRawBreakpoint[]): TPromise { + this.model.addBreakpoints(uri, rawBreakpoints); + rawBreakpoints.forEach(rbp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", rbp.lineNumber, uri.fsPath))); + + return this.sendBreakpoints(uri); + } + + public removeBreakpoints(id?: string): TPromise { + const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id); + toRemove.forEach(bp => aria.status(nls.localize('breakpointRemoved', "Removed breakpoint, line {0}, file {1}", bp.lineNumber, bp.uri.fsPath))); + const urisToClear = distinct(toRemove, bp => bp.uri.toString()).map(bp => bp.uri); + + this.model.removeBreakpoints(toRemove); + return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri))); + } + + public setBreakpointsActivated(activated: boolean): TPromise { + this.model.setBreakpointsActivated(activated); + return this.sendAllBreakpoints(); + } + + public addFunctionBreakpoint(): void { + this.model.addFunctionBreakpoint(''); + } + + public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise { + this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } }); + return this.sendFunctionBreakpoints(); + } + + public removeFunctionBreakpoints(id?: string): TPromise { + this.model.removeFunctionBreakpoints(id); + return this.sendFunctionBreakpoints(); + } + + public addReplExpression(name: string): TPromise { + this.telemetryService.publicLog('debugService/addReplExpression'); + return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name) + // Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some. + .then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame, this.viewModel.focusedProcess)); + } + + public removeReplExpressions(): void { + this.model.removeReplExpressions(); + } + + public logToRepl(value: string | debug.IExpression, sev = severity.Info): void { + if (typeof value === 'string' && '[2J'.localeCompare(value) === 0) { + // [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php + this.model.removeReplExpressions(); + } else { + this.model.appendToRepl(value, sev); + } + } + + public addWatchExpression(name: string): TPromise { + return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name); + } + + public renameWatchExpression(id: string, newName: string): TPromise { + return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName); + } + + public moveWatchExpression(id: string, position: number): void { + this.model.moveWatchExpression(id, position); + } + + public removeWatchExpressions(id?: string): void { + this.model.removeWatchExpressions(id); + } + + public startDebugging(root: uri, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise { + + // make sure to save all files and that the configuration is up to date + return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration().then(() => + this.extensionService.onReady().then(() => { + if (this.model.getProcesses().length === 0) { + this.removeReplExpressions(); + } + this.launchJsonChanged = false; + const manager = this.getConfigurationManager(); + const launch = root ? manager.getLaunches().filter(l => l.workspaceUri.toString() === root.toString()).pop() : undefined; + + let config: debug.IConfig, compound: debug.ICompound; + if (!configOrName) { + configOrName = this.configurationManager.selectedName; + } + if (typeof configOrName === 'string' && launch) { + config = launch.getConfiguration(configOrName); + compound = launch.getCompound(configOrName); + } else if (typeof configOrName !== 'string') { + config = configOrName; + } + if (launch) { + // in the drop down the name of the top most compound takes precedence over the launch config name + manager.selectConfiguration(launch, topCompoundName || (typeof configOrName === 'string' ? configOrName : undefined), true); + } + + if (compound) { + if (!compound.configurations) { + return TPromise.wrapError(new Error(nls.localize({ key: 'compoundMustHaveConfigurations', comment: ['compound indicates a "compounds" configuration item', '"configurations" is an attribute and should not be localized'] }, + "Compound must have \"configurations\" attribute set in order to start multiple configurations."))); + } + + return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name, noDebug, topCompoundName || compound.name) : TPromise.as(null))); + } + if (configOrName && !config) { + return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName))); + } + + return manager.getStartSessionCommand(config ? config.type : undefined).then(commandAndType => { + if (noDebug && config) { + config.noDebug = true; + } + + // deprecated code: use DebugConfigurationProvider instead of startSessionCommand + if (commandAndType && commandAndType.command) { + const defaultConfig = noDebug ? { noDebug: true } : {}; + return this.commandService.executeCommand(commandAndType.command, config || defaultConfig, launch ? launch.workspaceUri : undefined).then((result: StartSessionResult) => { + if (launch) { + if (result && result.status === 'initialConfiguration') { + return launch.openConfigFile(false, commandAndType.type); + } + + if (result && result.status === 'saveConfiguration') { + return this.fileService.updateContent(launch.uri, result.content).then(() => launch.openConfigFile(false)); + } + } + return undefined; + }); + } + // end of deprecation + + if (!config && commandAndType.type) { + config = { + type: commandAndType.type, + request: 'launch' + }; + } + + if (config) { + return this.configurationManager.resolveDebugConfiguration(launch ? launch.workspaceUri : undefined, config).then(config => { + + // TODO@AW: handle the 'initialConfiguration' and 'saveConfiguration' cases from above! + return this.createProcess(root, config); + }); + } + if (launch && commandAndType) { + return launch.openConfigFile(false, commandAndType.type); + } + + return undefined; + }); + }) + )); + } + + public findProcessByUUID(uuid: string): debug.IProcess | null { + const processes = this.getModel().getProcesses(); + const result = processes.filter(process => process.getId() === uuid); + if (result.length > 0) { + return result[0]; // there can only be one + } + return null; + } + + public createProcess(root: uri, config: debug.IConfig): TPromise { + return this.textFileService.saveAll().then(() => + (this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => { + if (!resolvedConfig) { + // User canceled resolving of interactive variables, silently return + return undefined; + } + + if (!this.configurationManager.getAdapter(resolvedConfig.type)) { + const message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) : + nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration."); + return TPromise.wrapError(errors.create(message, { actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] })); + } + + return this.runPreLaunchTask(resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => { + const errorCount = resolvedConfig.preLaunchTask ? this.markerService.getStatistics().errors : 0; + const successExitCode = taskSummary && taskSummary.exitCode === 0; + const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0; + if (successExitCode || (errorCount === 0 && !failureExitCode)) { + return this.doCreateProcess(root, resolvedConfig); + } + + this.messageService.show(severity.Error, { + message: errorCount > 1 ? nls.localize('preLaunchTaskErrors', "Build errors have been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) : + errorCount === 1 ? nls.localize('preLaunchTaskError', "Build error has been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) : + nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", resolvedConfig.preLaunchTask, taskSummary.exitCode), + actions: [ + new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => { + this.messageService.hideAll(); + return this.doCreateProcess(root, resolvedConfig); + }), + this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL), + CloseAction + ] + }); + return undefined; + }, (err: TaskError) => { + this.messageService.show(err.severity, { + message: err.message, + actions: [ + this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), + this.taskService.configureAction(), + CloseAction + ] + }); + }); + }, err => { + if (!this.contextService.hasWorkspace()) { + this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type.")); + return undefined; + } + + return this.configurationManager.selectedLaunch.openConfigFile(false).then(openend => { + if (openend) { + this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application. {0}", err.message)); + } + return undefined; + }); + }) + ); + } + + private doCreateProcess(root: uri, configuration: debug.IConfig, sessionId = generateUuid()): TPromise { + configuration.__sessionId = sessionId; + this.allSessionIds.add(sessionId); + this.updateStateAndEmit(sessionId, debug.State.Initializing); + + return this.telemetryService.getTelemetryInfo().then(info => { + const telemetryInfo: { [key: string]: string } = Object.create(null); + telemetryInfo['common.vscodemachineid'] = info.machineId; + telemetryInfo['common.vscodesessionid'] = info.sessionId; + return telemetryInfo; + }).then(data => { + const adapter = this.configurationManager.getAdapter(configuration.type); + const { aiKey, type } = adapter; + const publisher = adapter.extensionDescription.publisher; + this.customTelemetryService = null; + let client: TelemetryClient; + + if (aiKey) { + client = new TelemetryClient( + uri.parse(require.toUrl('bootstrap')).fsPath, + { + serverName: 'Debug Telemetry', + timeout: 1000 * 60 * 5, + args: [`${publisher}.${type}`, JSON.stringify(data), aiKey], + env: { + ELECTRON_RUN_AS_NODE: 1, + PIPE_LOGGING: 'true', + AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp' + } + } + ); + + const channel = client.getChannel('telemetryAppender'); + const appender = new TelemetryAppenderClient(channel); + + this.customTelemetryService = new TelemetryService({ appender }, this.configurationService); + } + + const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService, root); + const process = this.model.addProcess(configuration, session); + + this.toDisposeOnSessionEnd.set(session.getId(), []); + if (client) { + this.toDisposeOnSessionEnd.get(session.getId()).push(client); + } + this.registerSessionListeners(process, session); + + return session.initialize({ + clientID: 'vscode', + adapterID: configuration.type, + pathFormat: 'path', + linesStartAt1: true, + columnsStartAt1: true, + supportsVariableType: true, // #8858 + supportsVariablePaging: true, // #9537 + supportsRunInTerminalRequest: true // #10574 + }).then((result: DebugProtocol.InitializeResponse) => { + this.model.setExceptionBreakpoints(session.capabilities.exceptionBreakpointFilters); + return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration); + }).then((result: DebugProtocol.Response) => { + if (session.disconnected) { + return TPromise.as(null); + } + this._onDidNewProcess.fire(process); + this.focusStackFrameAndEvaluate(null, process); + + const internalConsoleOptions = configuration.internalConsoleOptions || this.configurationService.getConfiguration('debug').internalConsoleOptions; + if (internalConsoleOptions === 'openOnSessionStart' || (!this.viewModel.changedWorkbenchViewState && internalConsoleOptions === 'openOnFirstSessionStart')) { + this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError); + } + + if (!this.viewModel.changedWorkbenchViewState && (this.partService.isVisible(Parts.SIDEBAR_PART) || !this.contextService.hasWorkspace())) { + // We only want to change the workbench view state on the first debug session #5738 and if the side bar is not hidden + this.viewModel.changedWorkbenchViewState = true; + this.viewletService.openViewlet(debug.VIEWLET_ID); + } + + this.extensionService.activateByEvent(`onDebug:${configuration.type}`).done(null, errors.onUnexpectedError); + this.inDebugMode.set(true); + this.debugType.set(configuration.type); + if (this.model.getProcesses().length > 1) { + this.viewModel.setMultiProcessView(true); + } + this.updateStateAndEmit(session.getId(), debug.State.Running); + + return this.telemetryService.publicLog('debugSessionStart', { + type: configuration.type, + breakpointCount: this.model.getBreakpoints().length, + exceptionBreakpoints: this.model.getExceptionBreakpoints(), + watchExpressionsCount: this.model.getWatchExpressions().length, + extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`, + isBuiltin: adapter.extensionDescription.isBuiltin, + launchJsonExists: this.contextService.hasWorkspace() && !!this.configurationService.getConfiguration('launch', { resource: root }) + }); + }).then(() => process, (error: any) => { + if (error instanceof Error && error.message === 'Canceled') { + // Do not show 'canceled' error messages to the user #7906 + return TPromise.as(null); + } + + const errorMessage = error instanceof Error ? error.message : error; + this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage }); + this.updateStateAndEmit(session.getId(), debug.State.Inactive); + if (!session.disconnected) { + session.disconnect().done(null, errors.onUnexpectedError); + } + if (process) { + this.model.removeProcess(process.getId()); + } + // Show the repl if some error got logged there #5870 + if (this.model.getReplElements().length > 0) { + this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError); + } + + const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL); + const actions = (error.actions && error.actions.length) ? error.actions.concat([configureAction]) : [CloseAction, configureAction]; + this.messageService.show(severity.Error, { message: errorMessage, actions }); + return undefined; + }); + }); + } + + private runPreLaunchTask(taskName: string): TPromise { + if (!taskName) { + return TPromise.as(null); + } + + // run a task before starting a debug session + return this.taskService.getTask(taskName).then(task => { + if (!task) { + return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName))); + } + + return this.taskService.getActiveTasks().then(tasks => { + if (tasks.filter(t => t._id === task._id).length) { + // task is already running - nothing to do. + return TPromise.as(null); + } + + const taskPromise = this.taskService.run(task); + if (task.isBackground) { + return new TPromise((c, e) => this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null))); + } + + return taskPromise; + }); + }); + } + + public sourceIsNotAvailable(uri: uri): void { + this.model.sourceIsNotAvailable(uri); + } + + public restartProcess(process: debug.IProcess, restartData?: any): TPromise { + if (process.session.capabilities.supportsRestartRequest) { + return this.textFileService.saveAll().then(() => process.session.custom('restart', null)); + } + const focusedProcess = this.viewModel.focusedProcess; + const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId(); + + return process.session.disconnect(true).then(() => { + if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost')) { + return this.broadcastService.broadcast({ + channel: EXTENSION_RELOAD_BROADCAST_CHANNEL, + payload: [process.session.root.fsPath] + }); + } + + return new TPromise((c, e) => { + setTimeout(() => { + // Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration + let config = process.configuration; + if (this.launchJsonChanged && this.configurationManager.selectedLaunch) { + this.launchJsonChanged = false; + config = this.configurationManager.selectedLaunch.getConfiguration(process.configuration.name) || config; + // Take the type from the process since the debug extension might overwrite it #21316 + config.type = process.configuration.type; + config.noDebug = process.configuration.noDebug; + } + config.__restart = restartData; + this.createProcess(process.session.root, config).then(() => c(null), err => e(err)); + }, 300); + }); + }).then(() => { + if (preserveFocus) { + // Restart should preserve the focused process + const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop(); + if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) { + this.focusStackFrameAndEvaluate(null, restartedProcess); + } + } + }); + } + + public stopProcess(process: debug.IProcess): TPromise { + if (process) { + return process.session.disconnect(false, true); + } + + const processes = this.model.getProcesses(); + if (processes.length) { + return TPromise.join(processes.map(p => p.session.disconnect(false, true))); + } + + this.sessionStates.clear(); + this._onDidChangeState.fire(); + return undefined; + } + + private onSessionEnd(session: RawDebugSession): void { + const bpsExist = this.model.getBreakpoints().length > 0; + const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop(); + this.telemetryService.publicLog('debugSessionStop', { + type: process && process.configuration.type, + success: session.emittedStopped || !bpsExist, + sessionLengthInSeconds: session.getLengthInSeconds(), + breakpointCount: this.model.getBreakpoints().length, + watchExpressionsCount: this.model.getWatchExpressions().length + }); + + this.model.removeProcess(session.getId()); + if (process && process.state !== debug.ProcessState.INACTIVE) { + this._onDidEndProcess.fire(process); + } + + this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId()))); + const focusedProcess = this.viewModel.focusedProcess; + if (focusedProcess && focusedProcess.getId() === session.getId()) { + this.focusStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError); + } + this.updateStateAndEmit(session.getId(), debug.State.Inactive); + + if (this.model.getProcesses().length === 0) { + // set breakpoints back to unverified since the session ended. + const data: { [id: string]: { line: number, verified: boolean, column: number, endLine: number, endColumn: number } } = {}; + this.model.getBreakpoints().forEach(bp => { + data[bp.getId()] = { line: bp.lineNumber, verified: false, column: bp.column, endLine: bp.endLineNumber, endColumn: bp.endColumn }; + }); + this.model.updateBreakpoints(data); + + this.inDebugMode.reset(); + this.debugType.reset(); + this.viewModel.setMultiProcessView(false); + + if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getConfiguration('debug').openExplorerOnEnd) { + this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError); + } + } + } + + public getModel(): debug.IModel { + return this.model; + } + + public getViewModel(): debug.IViewModel { + return this.viewModel; + } + + public getConfigurationManager(): debug.IConfigurationManager { + return this.configurationManager; + } + + private sendAllBreakpoints(process?: debug.IProcess): TPromise { + return TPromise.join(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, process))) + .then(() => this.sendFunctionBreakpoints(process)) + // send exception breakpoints at the end since some debug adapters rely on the order + .then(() => this.sendExceptionBreakpoints(process)); + } + + private sendBreakpoints(modelUri: uri, sourceModified = false, targetProcess?: debug.IProcess): TPromise { + + const sendBreakpointsToProcess = (process: debug.IProcess): TPromise => { + const session = process.session; + if (!session.readyForBreakpoints) { + return TPromise.as(null); + } + if (this.textFileService.isDirty(modelUri)) { + // Only send breakpoints for a file once it is not dirty #8077 + this.breakpointsToSendOnResourceSaved.add(modelUri.toString()); + return TPromise.as(null); + } + + const breakpointsToSend = this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.uri.toString() === modelUri.toString()); + + const source = process.sources.get(modelUri.toString()); + const rawSource = source ? source.raw : { path: paths.normalize(modelUri.fsPath, true), name: paths.basename(modelUri.fsPath) }; + + return session.setBreakpoints({ + source: rawSource, + lines: breakpointsToSend.map(bp => bp.lineNumber), + breakpoints: breakpointsToSend.map(bp => ({ line: bp.lineNumber, column: bp.column, condition: bp.condition, hitCondition: bp.hitCondition })), + sourceModified + }).then(response => { + if (!response || !response.body) { + return; + } + + const data: { [id: string]: DebugProtocol.Breakpoint } = {}; + for (let i = 0; i < breakpointsToSend.length; i++) { + data[breakpointsToSend[i].getId()] = response.body.breakpoints[i]; + if (!breakpointsToSend[i].column) { + // If there was no column sent ignore the breakpoint column response from the adapter + data[breakpointsToSend[i].getId()].column = undefined; + } + } + + this.model.updateBreakpoints(data); + }); + }; + + return this.sendToOneOrAllProcesses(targetProcess, sendBreakpointsToProcess); + } + + private sendFunctionBreakpoints(targetProcess?: debug.IProcess): TPromise { + const sendFunctionBreakpointsToProcess = (process: debug.IProcess): TPromise => { + const session = process.session; + if (!session.readyForBreakpoints || !session.capabilities.supportsFunctionBreakpoints) { + return TPromise.as(null); + } + + const breakpointsToSend = this.model.getFunctionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated()); + return session.setFunctionBreakpoints({ breakpoints: breakpointsToSend }).then(response => { + if (!response || !response.body) { + return; + } + + const data: { [id: string]: { name?: string, verified?: boolean } } = {}; + for (let i = 0; i < breakpointsToSend.length; i++) { + data[breakpointsToSend[i].getId()] = response.body.breakpoints[i]; + } + + this.model.updateFunctionBreakpoints(data); + }); + }; + + return this.sendToOneOrAllProcesses(targetProcess, sendFunctionBreakpointsToProcess); + } + + private sendExceptionBreakpoints(targetProcess?: debug.IProcess): TPromise { + const sendExceptionBreakpointsToProcess = (process: debug.IProcess): TPromise => { + const session = process.session; + if (!session.readyForBreakpoints || this.model.getExceptionBreakpoints().length === 0) { + return TPromise.as(null); + } + + const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled); + return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) }); + }; + + return this.sendToOneOrAllProcesses(targetProcess, sendExceptionBreakpointsToProcess); + } + + private sendToOneOrAllProcesses(process: debug.IProcess, send: (process: debug.IProcess) => TPromise): TPromise { + if (process) { + return send(process); + } + + return TPromise.join(this.model.getProcesses().map(p => send(p))).then(() => void 0); + } + + private onFileChanges(fileChangesEvent: FileChangesEvent): void { + this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp => + fileChangesEvent.contains(bp.uri, FileChangeType.DELETED))); + + fileChangesEvent.getUpdated().forEach(event => { + if (this.breakpointsToSendOnResourceSaved.has(event.resource.toString())) { + this.breakpointsToSendOnResourceSaved.delete(event.resource.toString()); + this.sendBreakpoints(event.resource, true).done(null, errors.onUnexpectedError); + } + if (event.resource.toString().indexOf('.vscode/launch.json') >= 0) { + this.launchJsonChanged = true; + } + }); + } + + private store(): void { + const breakpoints = this.model.getBreakpoints(); + if (breakpoints.length) { + this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(breakpoints), StorageScope.WORKSPACE); + } else { + this.storageService.remove(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE); + } + + if (!this.model.areBreakpointsActivated()) { + this.storageService.store(DEBUG_BREAKPOINTS_ACTIVATED_KEY, 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE); + } + + const functionBreakpoints = this.model.getFunctionBreakpoints(); + if (functionBreakpoints.length) { + this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(functionBreakpoints), StorageScope.WORKSPACE); + } else { + this.storageService.remove(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE); + } + + const exceptionBreakpoints = this.model.getExceptionBreakpoints(); + if (exceptionBreakpoints.length) { + this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE); + } else { + this.storageService.remove(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE); + } + + const watchExpressions = this.model.getWatchExpressions(); + if (watchExpressions.length) { + this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(watchExpressions.map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE); + } else { + this.storageService.remove(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE); + } + } + + public dispose(): void { + this.toDisposeOnSessionEnd.forEach(toDispose => lifecycle.dispose(toDispose)); + this.toDispose = lifecycle.dispose(this.toDispose); + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts new file mode 100644 index 0000000000..0c2ba4684c --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts @@ -0,0 +1,1307 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import * as paths from 'vs/base/common/paths'; +import * as errors from 'vs/base/common/errors'; +import { equalsIgnoreCase } from 'vs/base/common/strings'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import * as dom from 'vs/base/browser/dom'; +import { IMouseEvent, DragMouseEvent } from 'vs/base/browser/mouseEvent'; +import { getPathLabel } from 'vs/base/common/labels'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer, DRAG_OVER_REJECT, IDragAndDropData, IDragOverReaction, IActionProvider } from 'vs/base/parts/tree/browser/tree'; +import { InputBox, IInputValidationOptions } from 'vs/base/browser/ui/inputbox/inputBox'; +import { DefaultController, DefaultDragAndDrop, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults'; +import { Constants } from 'vs/editor/common/core/uint'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions'; +import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import * as debug from 'vs/workbench/parts/debug/common/debug'; +import { Expression, Variable, FunctionBreakpoint, StackFrame, Thread, Process, Breakpoint, ExceptionBreakpoint, Model, Scope, ThreadAndProcessIds } from 'vs/workbench/parts/debug/common/debugModel'; +import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel'; +import { ContinueAction, StepOverAction, PauseAction, ReapplyBreakpointsAction, DisableAllBreakpointsAction, RemoveBreakpointAction, RemoveWatchExpressionAction, AddWatchExpressionAction, EditWatchExpressionAction, RemoveAllBreakpointsAction, EnableAllBreakpointsAction, StepOutAction, StepIntoAction, SetValueAction, RemoveAllWatchExpressionsAction, RestartFrameAction, AddToWatchExpressionsAction, StopAction, RestartAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { CopyValueAction, CopyStackTraceAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions'; +import { Source } from 'vs/workbench/parts/debug/common/debugSource'; +import { once } from 'vs/base/common/functional'; +import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + +const $ = dom.$; +const booleanRegex = /^true|false$/i; +const stringRegex = /^(['"]).*\1$/; +const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; + +export interface IRenderValueOptions { + preserveWhitespace?: boolean; + showChanged?: boolean; + maxValueLength?: number; + showHover?: boolean; +} + +function replaceWhitespace(value: string): string { + const map = { '\n': '\\n', '\r': '\\r', '\t': '\\t' }; + return value.replace(/[\n\r\t]/g, char => map[char]); +} + +export function renderExpressionValue(expressionOrValue: debug.IExpression | string, container: HTMLElement, options: IRenderValueOptions): void { + let value = typeof expressionOrValue === 'string' ? expressionOrValue : expressionOrValue.value; + + // remove stale classes + container.className = 'value'; + // when resolving expressions we represent errors from the server as a variable with name === null. + if (value === null || ((expressionOrValue instanceof Expression || expressionOrValue instanceof Variable) && !expressionOrValue.available)) { + dom.addClass(container, 'unavailable'); + if (value !== Expression.DEFAULT_VALUE) { + dom.addClass(container, 'error'); + } + } else if (!isNaN(+value)) { + dom.addClass(container, 'number'); + } else if (booleanRegex.test(value)) { + dom.addClass(container, 'boolean'); + } else if (stringRegex.test(value)) { + dom.addClass(container, 'string'); + } + + if (options.showChanged && (expressionOrValue).valueChanged && value !== Expression.DEFAULT_VALUE) { + // value changed color has priority over other colors. + container.className = 'value changed'; + } + + if (options.maxValueLength && value.length > options.maxValueLength) { + value = value.substr(0, options.maxValueLength) + '...'; + } + if (value && !options.preserveWhitespace) { + container.textContent = replaceWhitespace(value); + } else { + container.textContent = value; + } + if (options.showHover) { + container.title = value; + } +} + +export function renderVariable(tree: ITree, variable: Variable, data: IVariableTemplateData, showChanged: boolean): void { + if (variable.available) { + data.name.textContent = replaceWhitespace(variable.name); + data.name.title = variable.type ? variable.type : variable.name; + } + + if (variable.value) { + data.name.textContent += variable.name ? ':' : ''; + renderExpressionValue(variable, data.value, { + showChanged, + maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, + preserveWhitespace: false, + showHover: true + }); + } else { + data.value.textContent = ''; + data.value.title = ''; + } +} + +interface IRenameBoxOptions { + initialValue: string; + ariaLabel: string; + placeholder?: string; + validationOptions?: IInputValidationOptions; +} + +function renderRenameBox(debugService: debug.IDebugService, contextViewService: IContextViewService, themeService: IThemeService, tree: ITree, element: any, container: HTMLElement, options: IRenameBoxOptions): void { + let inputBoxContainer = dom.append(container, $('.inputBoxContainer')); + let inputBox = new InputBox(inputBoxContainer, contextViewService, { + validationOptions: options.validationOptions, + placeholder: options.placeholder, + ariaLabel: options.ariaLabel + }); + const styler = attachInputBoxStyler(inputBox, themeService); + + tree.setHighlight(); + inputBox.value = options.initialValue ? options.initialValue : ''; + inputBox.focus(); + inputBox.select(); + + let disposed = false; + const toDispose: [lifecycle.IDisposable] = [inputBox, styler]; + + const wrapUp = once((renamed: boolean) => { + if (!disposed) { + disposed = true; + if (element instanceof Expression && renamed && inputBox.value) { + debugService.renameWatchExpression(element.getId(), inputBox.value).done(null, errors.onUnexpectedError); + } else if (element instanceof Expression && !element.name) { + debugService.removeWatchExpressions(element.getId()); + } else if (element instanceof FunctionBreakpoint && inputBox.value) { + debugService.renameFunctionBreakpoint(element.getId(), renamed ? inputBox.value : element.name).done(null, errors.onUnexpectedError); + } else if (element instanceof FunctionBreakpoint && !element.name) { + debugService.removeFunctionBreakpoints(element.getId()).done(null, errors.onUnexpectedError); + } else if (element instanceof Variable) { + element.errorMessage = null; + if (renamed && element.value !== inputBox.value) { + element.setVariable(inputBox.value) + // if everything went fine we need to refresh ui elements since the variable update can change watch and variables view + .done(() => tree.refresh(element, false), errors.onUnexpectedError); + } + } + + tree.clearHighlight(); + tree.DOMFocus(); + tree.setFocus(element); + + // need to remove the input box since this template will be reused. + container.removeChild(inputBoxContainer); + lifecycle.dispose(toDispose); + } + }); + + toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => { + const isEscape = e.equals(KeyCode.Escape); + const isEnter = e.equals(KeyCode.Enter); + if (isEscape || isEnter) { + e.preventDefault(); + e.stopPropagation(); + wrapUp(isEnter); + } + })); + toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => { + wrapUp(true); + })); +} + +function getSourceName(source: Source, contextService: IWorkspaceContextService, environmentService?: IEnvironmentService): string { + if (source.name) { + return source.name; + } + + return paths.basename(source.uri.fsPath); +} + +export class BaseDebugController extends DefaultController { + + private contributedContextMenu: IMenu; + + constructor( + private actionProvider: IActionProvider, + menuId: MenuId, + @debug.IDebugService protected debugService: debug.IDebugService, + @IWorkbenchEditorService protected editorService: IWorkbenchEditorService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IMenuService menuService: IMenuService + ) { + super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false }); + + this.contributedContextMenu = menuService.createMenu(menuId, contextKeyService); + } + + public onContextMenu(tree: ITree, element: debug.IEnablement, event: ContextMenuEvent): boolean { + if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { + return false; + } + + event.preventDefault(); + event.stopPropagation(); + + tree.setFocus(element); + + if (this.actionProvider.hasSecondaryActions(tree, element)) { + const anchor = { x: event.posx + 1, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this.actionProvider.getSecondaryActions(tree, element).then(actions => { + fillInActions(this.contributedContextMenu, { arg: this.getContext(element) }, actions); + return actions; + }), + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + tree.DOMFocus(); + } + }, + getActionsContext: () => element + }); + + return true; + } + + return false; + } + + protected getContext(element: any): any { + return undefined; + } +} + +// call stack + +export class CallStackController extends BaseDebugController { + + protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean { + if (element instanceof ThreadAndProcessIds) { + return this.showMoreStackFrames(tree, element); + } + if (element instanceof StackFrame) { + super.onLeftClick(tree, element, event); + this.focusStackFrame(element, event, event.detail !== 2); + return true; + } + + return super.onLeftClick(tree, element, event); + } + + protected getContext(element: any): any { + if (element instanceof StackFrame) { + if (element.source.inMemory) { + return element.source.raw.path || element.source.reference; + } + + return element.source.uri.toString(); + } + } + + // user clicked / pressed on 'Load More Stack Frames', get those stack frames and refresh the tree. + public showMoreStackFrames(tree: ITree, threadAndProcessIds: ThreadAndProcessIds): boolean { + const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === threadAndProcessIds.processId).pop(); + const thread = process && process.getThread(threadAndProcessIds.threadId); + if (thread) { + (thread).fetchCallStack() + .done(() => tree.refresh(), errors.onUnexpectedError); + } + + return true; + } + + public focusStackFrame(stackFrame: debug.IStackFrame, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void { + this.debugService.focusStackFrameAndEvaluate(stackFrame, undefined, true).then(() => { + const sideBySide = (event && (event.ctrlKey || event.metaKey)); + return stackFrame.openInEditor(this.editorService, preserveFocus, sideBySide); + }, errors.onUnexpectedError); + } +} + + +export class CallStackActionProvider implements IActionProvider { + + constructor( @IInstantiationService private instantiationService: IInstantiationService, @debug.IDebugService private debugService: debug.IDebugService) { + // noop + } + + public hasActions(tree: ITree, element: any): boolean { + return false; + } + + public getActions(tree: ITree, element: any): TPromise { + return TPromise.as([]); + } + + public hasSecondaryActions(tree: ITree, element: any): boolean { + return element !== tree.getInput(); + } + + public getSecondaryActions(tree: ITree, element: any): TPromise { + const actions: IAction[] = []; + if (element instanceof Process) { + actions.push(this.instantiationService.createInstance(RestartAction, RestartAction.ID, RestartAction.LABEL)); + actions.push(this.instantiationService.createInstance(StopAction, StopAction.ID, StopAction.LABEL)); + } else if (element instanceof Thread) { + const thread = element; + if (thread.stopped) { + actions.push(this.instantiationService.createInstance(ContinueAction, ContinueAction.ID, ContinueAction.LABEL)); + actions.push(this.instantiationService.createInstance(StepOverAction, StepOverAction.ID, StepOverAction.LABEL)); + actions.push(this.instantiationService.createInstance(StepIntoAction, StepIntoAction.ID, StepIntoAction.LABEL)); + actions.push(this.instantiationService.createInstance(StepOutAction, StepOutAction.ID, StepOutAction.LABEL)); + } else { + actions.push(this.instantiationService.createInstance(PauseAction, PauseAction.ID, PauseAction.LABEL)); + } + } else if (element instanceof StackFrame) { + if (element.thread.process.session.capabilities.supportsRestartFrame) { + actions.push(this.instantiationService.createInstance(RestartFrameAction, RestartFrameAction.ID, RestartFrameAction.LABEL)); + } + actions.push(new CopyStackTraceAction(CopyStackTraceAction.ID, CopyStackTraceAction.LABEL)); + } + + return TPromise.as(actions); + } + + public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { + return null; + } +} + +export class CallStackDataSource implements IDataSource { + + public getId(tree: ITree, element: any): string { + if (typeof element === 'string') { + return element; + } + + return element.getId(); + } + + public hasChildren(tree: ITree, element: any): boolean { + return element instanceof Model || element instanceof Process || (element instanceof Thread && (element).stopped); + } + + public getChildren(tree: ITree, element: any): TPromise { + if (element instanceof Thread) { + return this.getThreadChildren(element); + } + if (element instanceof Model) { + return TPromise.as(element.getProcesses()); + } + + const process = element; + return TPromise.as(process.getAllThreads()); + } + + private getThreadChildren(thread: Thread): TPromise { + let callStack: any[] = thread.getCallStack(); + let callStackPromise: TPromise = TPromise.as(null); + if (!callStack || !callStack.length) { + callStackPromise = thread.fetchCallStack().then(() => callStack = thread.getCallStack()); + } + + return callStackPromise.then(() => { + if (callStack.length === 1 && thread.process.session.capabilities.supportsDelayedStackTraceLoading) { + // To reduce flashing of the call stack view simply append the stale call stack + // once we have the correct data the tree will refresh and we will no longer display it. + callStack = callStack.concat(thread.getStaleCallStack().slice(1)); + } + + if (thread.stoppedDetails && thread.stoppedDetails.framesErrorMessage) { + callStack = callStack.concat([thread.stoppedDetails.framesErrorMessage]); + } + if (thread.stoppedDetails && thread.stoppedDetails.totalFrames > callStack.length && callStack.length > 1) { + callStack = callStack.concat([new ThreadAndProcessIds(thread.process.getId(), thread.threadId)]); + } + + return callStack; + }); + } + + public getParent(tree: ITree, element: any): TPromise { + return TPromise.as(null); + } +} + +interface IThreadTemplateData { + thread: HTMLElement; + name: HTMLElement; + state: HTMLElement; + stateLabel: HTMLSpanElement; +} + +interface IProcessTemplateData { + process: HTMLElement; + name: HTMLElement; + state: HTMLElement; + stateLabel: HTMLSpanElement; +} + +interface IErrorTemplateData { + label: HTMLElement; +} + +interface ILoadMoreTemplateData { + label: HTMLElement; +} + +interface IStackFrameTemplateData { + stackFrame: HTMLElement; + label: HTMLElement; + file: HTMLElement; + fileName: HTMLElement; + lineNumber: HTMLElement; +} + +export class CallStackRenderer implements IRenderer { + + private static THREAD_TEMPLATE_ID = 'thread'; + private static STACK_FRAME_TEMPLATE_ID = 'stackFrame'; + private static ERROR_TEMPLATE_ID = 'error'; + private static LOAD_MORE_TEMPLATE_ID = 'loadMore'; + private static PROCESS_TEMPLATE_ID = 'process'; + + constructor( + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + // noop + } + + public getHeight(tree: ITree, element: any): number { + return 22; + } + + public getTemplateId(tree: ITree, element: any): string { + if (element instanceof Process) { + return CallStackRenderer.PROCESS_TEMPLATE_ID; + } + if (element instanceof Thread) { + return CallStackRenderer.THREAD_TEMPLATE_ID; + } + if (element instanceof StackFrame) { + return CallStackRenderer.STACK_FRAME_TEMPLATE_ID; + } + if (typeof element === 'string') { + return CallStackRenderer.ERROR_TEMPLATE_ID; + } + + return CallStackRenderer.LOAD_MORE_TEMPLATE_ID; + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + if (templateId === CallStackRenderer.PROCESS_TEMPLATE_ID) { + let data: IProcessTemplateData = Object.create(null); + data.process = dom.append(container, $('.process')); + data.name = dom.append(data.process, $('.name')); + data.state = dom.append(data.process, $('.state')); + data.stateLabel = dom.append(data.state, $('span.label')); + + return data; + } + + if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) { + let data: ILoadMoreTemplateData = Object.create(null); + data.label = dom.append(container, $('.load-more')); + + return data; + } + if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) { + let data: ILoadMoreTemplateData = Object.create(null); + data.label = dom.append(container, $('.error')); + + return data; + } + if (templateId === CallStackRenderer.THREAD_TEMPLATE_ID) { + let data: IThreadTemplateData = Object.create(null); + data.thread = dom.append(container, $('.thread')); + data.name = dom.append(data.thread, $('.name')); + data.state = dom.append(data.thread, $('.state')); + data.stateLabel = dom.append(data.state, $('span.label')); + + return data; + } + + let data: IStackFrameTemplateData = Object.create(null); + data.stackFrame = dom.append(container, $('.stack-frame')); + data.label = dom.append(data.stackFrame, $('span.label.expression')); + data.file = dom.append(data.stackFrame, $('.file')); + data.fileName = dom.append(data.file, $('span.file-name')); + data.lineNumber = dom.append(data.file, $('span.line-number')); + + return data; + } + + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + if (templateId === CallStackRenderer.PROCESS_TEMPLATE_ID) { + this.renderProcess(element, templateData); + } else if (templateId === CallStackRenderer.THREAD_TEMPLATE_ID) { + this.renderThread(element, templateData); + } else if (templateId === CallStackRenderer.STACK_FRAME_TEMPLATE_ID) { + this.renderStackFrame(element, templateData); + } else if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) { + this.renderError(element, templateData); + } else if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) { + this.renderLoadMore(element, templateData); + } + } + + private renderProcess(process: debug.IProcess, data: IProcessTemplateData): void { + data.process.title = nls.localize({ key: 'process', comment: ['Process is a noun'] }, "Process"); + data.name.textContent = process.getName(this.contextService.hasMultiFolderWorkspace()); + const stoppedThread = process.getAllThreads().filter(t => t.stopped).pop(); + + data.stateLabel.textContent = stoppedThread ? nls.localize('paused', "Paused") + : nls.localize({ key: 'running', comment: ['indicates state'] }, "Running"); + } + + private renderThread(thread: debug.IThread, data: IThreadTemplateData): void { + data.thread.title = nls.localize('thread', "Thread"); + data.name.textContent = thread.name; + + if (thread.stopped) { + data.stateLabel.textContent = thread.stoppedDetails.description || + thread.stoppedDetails.reason ? nls.localize({ key: 'pausedOn', comment: ['indicates reason for program being paused'] }, "Paused on {0}", thread.stoppedDetails.reason) : nls.localize('paused', "Paused"); + } else { + data.stateLabel.textContent = nls.localize({ key: 'running', comment: ['indicates state'] }, "Running"); + } + } + + private renderError(element: string, data: IErrorTemplateData) { + data.label.textContent = element; + data.label.title = element; + } + + private renderLoadMore(element: any, data: ILoadMoreTemplateData): void { + data.label.textContent = nls.localize('loadMoreStackFrames', "Load More Stack Frames"); + } + + private renderStackFrame(stackFrame: debug.IStackFrame, data: IStackFrameTemplateData): void { + dom.toggleClass(data.stackFrame, 'disabled', !stackFrame.source.available || stackFrame.source.presentationHint === 'deemphasize'); + dom.toggleClass(data.stackFrame, 'label', stackFrame.presentationHint === 'label'); + dom.toggleClass(data.stackFrame, 'subtle', stackFrame.presentationHint === 'subtle'); + + data.file.title = stackFrame.source.raw.path || stackFrame.source.name; + if (stackFrame.source.raw.origin) { + data.file.title += `\n${stackFrame.source.raw.origin}`; + } + data.label.textContent = stackFrame.name; + data.label.title = stackFrame.name; + data.fileName.textContent = getSourceName(stackFrame.source, this.contextService, this.environmentService); + if (stackFrame.range.startLineNumber !== undefined) { + data.lineNumber.textContent = `${stackFrame.range.startLineNumber}`; + if (stackFrame.range.startColumn) { + data.lineNumber.textContent += `:${stackFrame.range.startColumn}`; + } + dom.removeClass(data.lineNumber, 'unavailable'); + } else { + dom.addClass(data.lineNumber, 'unavailable'); + } + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + // noop + } +} + +export class CallstackAccessibilityProvider implements IAccessibilityProvider { + + constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) { + // noop + } + + public getAriaLabel(tree: ITree, element: any): string { + if (element instanceof Thread) { + return nls.localize('threadAriaLabel', "Thread {0}, callstack, debug", (element).name); + } + if (element instanceof StackFrame) { + return nls.localize('stackFrameAriaLabel', "Stack Frame {0} line {1} {2}, callstack, debug", (element).name, (element).range.startLineNumber, getSourceName((element).source, this.contextService)); + } + + return null; + } +} + +// variables + +export class VariablesActionProvider implements IActionProvider { + + constructor(private instantiationService: IInstantiationService) { + // noop + } + + public hasActions(tree: ITree, element: any): boolean { + return false; + } + + public getActions(tree: ITree, element: any): TPromise { + return TPromise.as([]); + } + + public hasSecondaryActions(tree: ITree, element: any): boolean { + // Only show context menu on "real" variables. Not on array chunk nodes. + return element instanceof Variable && !!element.value; + } + + public getSecondaryActions(tree: ITree, element: any): TPromise { + const actions: IAction[] = []; + const variable = element; + actions.push(this.instantiationService.createInstance(SetValueAction, SetValueAction.ID, SetValueAction.LABEL, variable)); + actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable)); + actions.push(new Separator()); + actions.push(this.instantiationService.createInstance(AddToWatchExpressionsAction, AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable)); + + return TPromise.as(actions); + } + + public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { + return null; + } +} + +export class VariablesDataSource implements IDataSource { + + public getId(tree: ITree, element: any): string { + return element.getId(); + } + + public hasChildren(tree: ITree, element: any): boolean { + if (element instanceof ViewModel || element instanceof Scope) { + return true; + } + + let variable = element; + return variable.hasChildren && !equalsIgnoreCase(variable.value, 'null'); + } + + public getChildren(tree: ITree, element: any): TPromise { + if (element instanceof ViewModel) { + const focusedStackFrame = (element).focusedStackFrame; + return focusedStackFrame ? focusedStackFrame.getScopes() : TPromise.as([]); + } + + let scope = element; + return scope.getChildren(); + } + + public getParent(tree: ITree, element: any): TPromise { + return TPromise.as(null); + } +} + +interface IScopeTemplateData { + name: HTMLElement; +} + +export interface IVariableTemplateData { + expression: HTMLElement; + name: HTMLElement; + value: HTMLElement; +} + +export class VariablesRenderer implements IRenderer { + + private static SCOPE_TEMPLATE_ID = 'scope'; + private static VARIABLE_TEMPLATE_ID = 'variable'; + + constructor( + @debug.IDebugService private debugService: debug.IDebugService, + @IContextViewService private contextViewService: IContextViewService, + @IThemeService private themeService: IThemeService + ) { + // noop + } + + public getHeight(tree: ITree, element: any): number { + return 22; + } + + public getTemplateId(tree: ITree, element: any): string { + if (element instanceof Scope) { + return VariablesRenderer.SCOPE_TEMPLATE_ID; + } + if (element instanceof Variable) { + return VariablesRenderer.VARIABLE_TEMPLATE_ID; + } + + return null; + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + if (templateId === VariablesRenderer.SCOPE_TEMPLATE_ID) { + let data: IScopeTemplateData = Object.create(null); + data.name = dom.append(container, $('.scope')); + + return data; + } + + let data: IVariableTemplateData = Object.create(null); + data.expression = dom.append(container, $('.expression')); + data.name = dom.append(data.expression, $('span.name')); + data.value = dom.append(data.expression, $('span.value')); + + return data; + } + + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + if (templateId === VariablesRenderer.SCOPE_TEMPLATE_ID) { + this.renderScope(element, templateData); + } else { + const variable = element; + if (variable === this.debugService.getViewModel().getSelectedExpression() || variable.errorMessage) { + renderRenameBox(this.debugService, this.contextViewService, this.themeService, tree, variable, (templateData).expression, { + initialValue: variable.value, + ariaLabel: nls.localize('variableValueAriaLabel', "Type new variable value"), + validationOptions: { + validation: (value: string) => variable.errorMessage ? ({ content: variable.errorMessage }) : null + } + }); + } else { + renderVariable(tree, variable, templateData, true); + } + } + } + + private renderScope(scope: Scope, data: IScopeTemplateData): void { + data.name.textContent = scope.name; + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + // noop + } +} + +export class VariablesAccessibilityProvider implements IAccessibilityProvider { + + public getAriaLabel(tree: ITree, element: any): string { + if (element instanceof Scope) { + return nls.localize('variableScopeAriaLabel', "Scope {0}, variables, debug", (element).name); + } + if (element instanceof Variable) { + return nls.localize('variableAriaLabel', "{0} value {1}, variables, debug", (element).name, (element).value); + } + + return null; + } +} + +export class VariablesController extends BaseDebugController { + + protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean { + // double click on primitive value: open input box to be able to set the value + if (element instanceof Variable && event.detail === 2) { + const expression = element; + this.debugService.getViewModel().setSelectedExpression(expression); + return true; + } + + return super.onLeftClick(tree, element, event); + } +} + +// watch expressions + +export class WatchExpressionsActionProvider implements IActionProvider { + + private instantiationService: IInstantiationService; + + constructor(instantiationService: IInstantiationService) { + this.instantiationService = instantiationService; + } + + public hasActions(tree: ITree, element: any): boolean { + return element instanceof Expression && !!element.name; + } + + public hasSecondaryActions(tree: ITree, element: any): boolean { + return true; + } + + public getActions(tree: ITree, element: any): TPromise { + return TPromise.as([]); + } + + public getSecondaryActions(tree: ITree, element: any): TPromise { + const actions: IAction[] = []; + if (element instanceof Expression) { + const expression = element; + actions.push(this.instantiationService.createInstance(AddWatchExpressionAction, AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL)); + actions.push(this.instantiationService.createInstance(EditWatchExpressionAction, EditWatchExpressionAction.ID, EditWatchExpressionAction.LABEL)); + if (!expression.hasChildren) { + actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, expression.value)); + } + actions.push(new Separator()); + + actions.push(this.instantiationService.createInstance(RemoveWatchExpressionAction, RemoveWatchExpressionAction.ID, RemoveWatchExpressionAction.LABEL)); + actions.push(this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL)); + } else { + actions.push(this.instantiationService.createInstance(AddWatchExpressionAction, AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL)); + if (element instanceof Variable) { + const variable = element; + if (!variable.hasChildren) { + actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable.value)); + } + actions.push(new Separator()); + } + actions.push(this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL)); + } + + return TPromise.as(actions); + } + + public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { + return null; + } +} + +export class WatchExpressionsDataSource implements IDataSource { + + public getId(tree: ITree, element: any): string { + return element.getId(); + } + + public hasChildren(tree: ITree, element: any): boolean { + if (element instanceof Model) { + return true; + } + + const watchExpression = element; + return watchExpression.hasChildren && !equalsIgnoreCase(watchExpression.value, 'null'); + } + + public getChildren(tree: ITree, element: any): TPromise { + if (element instanceof Model) { + return TPromise.as((element).getWatchExpressions()); + } + + let expression = element; + return expression.getChildren(); + } + + public getParent(tree: ITree, element: any): TPromise { + return TPromise.as(null); + } +} + +interface IWatchExpressionTemplateData { + watchExpression: HTMLElement; + expression: HTMLElement; + name: HTMLSpanElement; + value: HTMLSpanElement; +} + +export class WatchExpressionsRenderer implements IRenderer { + + private static WATCH_EXPRESSION_TEMPLATE_ID = 'watchExpression'; + private static VARIABLE_TEMPLATE_ID = 'variables'; + private toDispose: lifecycle.IDisposable[]; + private actionProvider: WatchExpressionsActionProvider; + + constructor( + actionProvider: IActionProvider, + private actionRunner: IActionRunner, + @debug.IDebugService private debugService: debug.IDebugService, + @IContextViewService private contextViewService: IContextViewService, + @IThemeService private themeService: IThemeService + ) { + this.toDispose = []; + this.actionProvider = actionProvider; + } + + public getHeight(tree: ITree, element: any): number { + return 22; + } + + public getTemplateId(tree: ITree, element: any): string { + if (element instanceof Expression) { + return WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID; + } + + return WatchExpressionsRenderer.VARIABLE_TEMPLATE_ID; + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + const createVariableTemplate = ((data: IVariableTemplateData, container: HTMLElement) => { + data.expression = dom.append(container, $('.expression')); + data.name = dom.append(data.expression, $('span.name')); + data.value = dom.append(data.expression, $('span.value')); + }); + + if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) { + const data: IWatchExpressionTemplateData = Object.create(null); + data.watchExpression = dom.append(container, $('.watch-expression')); + createVariableTemplate(data, data.watchExpression); + + return data; + } + + const data: IVariableTemplateData = Object.create(null); + createVariableTemplate(data, container); + + return data; + } + + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) { + this.renderWatchExpression(tree, element, templateData); + } else { + renderVariable(tree, element, templateData, true); + } + } + + private renderWatchExpression(tree: ITree, watchExpression: debug.IExpression, data: IWatchExpressionTemplateData): void { + let selectedExpression = this.debugService.getViewModel().getSelectedExpression(); + if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId()) || (watchExpression instanceof Expression && !watchExpression.name)) { + renderRenameBox(this.debugService, this.contextViewService, this.themeService, tree, watchExpression, data.expression, { + initialValue: watchExpression.name, + placeholder: nls.localize('watchExpressionPlaceholder', "Expression to watch"), + ariaLabel: nls.localize('watchExpressionInputAriaLabel', "Type watch expression") + }); + } + + data.name.textContent = watchExpression.name; + if (watchExpression.value) { + data.name.textContent += ':'; + renderExpressionValue(watchExpression, data.value, { + showChanged: true, + maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, + preserveWhitespace: false, + showHover: true + }); + data.name.title = watchExpression.type ? watchExpression.type : watchExpression.value; + } + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + // noop + } + + public dispose(): void { + this.toDispose = lifecycle.dispose(this.toDispose); + } +} + +export class WatchExpressionsAccessibilityProvider implements IAccessibilityProvider { + + public getAriaLabel(tree: ITree, element: any): string { + if (element instanceof Expression) { + return nls.localize('watchExpressionAriaLabel', "{0} value {1}, watch, debug", (element).name, (element).value); + } + if (element instanceof Variable) { + return nls.localize('watchVariableAriaLabel', "{0} value {1}, watch, debug", (element).name, (element).value); + } + + return null; + } +} + +export class WatchExpressionsController extends BaseDebugController { + + protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean { + // double click on primitive value: open input box to be able to select and copy value. + if (element instanceof Expression && event.detail === 2) { + const expression = element; + this.debugService.getViewModel().setSelectedExpression(expression); + return true; + } + + return super.onLeftClick(tree, element, event); + } +} + +export class WatchExpressionsDragAndDrop extends DefaultDragAndDrop { + + constructor( @debug.IDebugService private debugService: debug.IDebugService) { + super(); + } + + public getDragURI(tree: ITree, element: Expression): string { + if (!(element instanceof Expression)) { + return null; + } + + return element.getId(); + } + + public getDragLabel(tree: ITree, elements: Expression[]): string { + if (elements.length > 1) { + return String(elements.length); + } + + return elements[0].name; + } + + public onDragOver(tree: ITree, data: IDragAndDropData, target: Expression | Model, originalEvent: DragMouseEvent): IDragOverReaction { + if (target instanceof Expression || target instanceof Model) { + return { + accept: true, + autoExpand: false + }; + } + + return DRAG_OVER_REJECT; + } + + public drop(tree: ITree, data: IDragAndDropData, target: Expression | Model, originalEvent: DragMouseEvent): void { + const draggedData = data.getData(); + if (Array.isArray(draggedData)) { + const draggedElement = draggedData[0]; + const watches = this.debugService.getModel().getWatchExpressions(); + const position = target instanceof Model ? watches.length - 1 : watches.indexOf(target); + this.debugService.moveWatchExpression(draggedElement.getId(), position); + } + } + + // {{SQL CARBON EDIT}} + public dropAbort(tree: ITree, data: IDragAndDropData): void { } +} + +// breakpoints + +export class BreakpointsActionProvider implements IActionProvider { + + constructor(private instantiationService: IInstantiationService, private debugService: debug.IDebugService) { + // noop + } + + public hasActions(tree: ITree, element: any): boolean { + return false; + } + + public hasSecondaryActions(tree: ITree, element: any): boolean { + return element instanceof Breakpoint || element instanceof ExceptionBreakpoint || element instanceof FunctionBreakpoint; + } + + public getActions(tree: ITree, element: any): TPromise { + return TPromise.as([]); + } + + public getSecondaryActions(tree: ITree, element: any): TPromise { + const actions: IAction[] = []; + + if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) { + actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL)); + } + if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) { + actions.push(this.instantiationService.createInstance(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL)); + actions.push(new Separator()); + + actions.push(this.instantiationService.createInstance(EnableAllBreakpointsAction, EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL)); + actions.push(this.instantiationService.createInstance(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL)); + } + + actions.push(new Separator()); + actions.push(this.instantiationService.createInstance(ReapplyBreakpointsAction, ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL)); + + return TPromise.as(actions); + } + + public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { + return null; + } +} + +export class BreakpointsDataSource implements IDataSource { + + public getId(tree: ITree, element: any): string { + return element.getId(); + } + + public hasChildren(tree: ITree, element: any): boolean { + return element instanceof Model; + } + + public getChildren(tree: ITree, element: any): TPromise { + const model = element; + const exBreakpoints = model.getExceptionBreakpoints(); + + return TPromise.as(exBreakpoints.concat(model.getFunctionBreakpoints()).concat(model.getBreakpoints())); + } + + public getParent(tree: ITree, element: any): TPromise { + return TPromise.as(null); + } +} + +interface IBaseBreakpointTemplateData { + breakpoint: HTMLElement; + name: HTMLElement; + checkbox: HTMLInputElement; + context: debug.IEnablement; + toDispose: lifecycle.IDisposable[]; +} + +interface IBreakpointTemplateData extends IBaseBreakpointTemplateData { + lineNumber: HTMLElement; + filePath: HTMLElement; +} + +export class BreakpointsRenderer implements IRenderer { + + private static EXCEPTION_BREAKPOINT_TEMPLATE_ID = 'exceptionBreakpoint'; + private static FUNCTION_BREAKPOINT_TEMPLATE_ID = 'functionBreakpoint'; + private static BREAKPOINT_TEMPLATE_ID = 'breakpoint'; + + constructor( + private actionProvider: BreakpointsActionProvider, + private actionRunner: IActionRunner, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @debug.IDebugService private debugService: debug.IDebugService, + @IContextViewService private contextViewService: IContextViewService, + @IThemeService private themeService: IThemeService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + // noop + } + + public getHeight(tree: ITree, element: any): number { + return 22; + } + + public getTemplateId(tree: ITree, element: any): string { + if (element instanceof Breakpoint) { + return BreakpointsRenderer.BREAKPOINT_TEMPLATE_ID; + } + if (element instanceof FunctionBreakpoint) { + return BreakpointsRenderer.FUNCTION_BREAKPOINT_TEMPLATE_ID; + } + if (element instanceof ExceptionBreakpoint) { + return BreakpointsRenderer.EXCEPTION_BREAKPOINT_TEMPLATE_ID; + } + + return null; + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + const data: IBreakpointTemplateData = Object.create(null); + data.breakpoint = dom.append(container, $('.breakpoint')); + + data.checkbox = $('input'); + data.checkbox.type = 'checkbox'; + data.toDispose = []; + data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { + this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); + })); + + dom.append(data.breakpoint, data.checkbox); + + data.name = dom.append(data.breakpoint, $('span.name')); + + if (templateId === BreakpointsRenderer.BREAKPOINT_TEMPLATE_ID) { + data.filePath = dom.append(data.breakpoint, $('span.file-path')); + const lineNumberContainer = dom.append(data.breakpoint, $('.line-number-container')); + data.lineNumber = dom.append(lineNumberContainer, $('span.line-number')); + } + if (templateId === BreakpointsRenderer.EXCEPTION_BREAKPOINT_TEMPLATE_ID) { + dom.addClass(data.breakpoint, 'exception'); + } + + return data; + } + + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + templateData.context = element; + if (templateId === BreakpointsRenderer.EXCEPTION_BREAKPOINT_TEMPLATE_ID) { + this.renderExceptionBreakpoint(element, templateData); + } else if (templateId === BreakpointsRenderer.FUNCTION_BREAKPOINT_TEMPLATE_ID) { + this.renderFunctionBreakpoint(tree, element, templateData); + } else { + this.renderBreakpoint(tree, element, templateData); + } + } + + private renderExceptionBreakpoint(exceptionBreakpoint: debug.IExceptionBreakpoint, data: IBaseBreakpointTemplateData): void { + data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`; + data.breakpoint.title = data.name.textContent; + data.checkbox.checked = exceptionBreakpoint.enabled; + } + + private renderFunctionBreakpoint(tree: ITree, functionBreakpoint: debug.IFunctionBreakpoint, data: IBaseBreakpointTemplateData): void { + const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint(); + if (!functionBreakpoint.name || (selected && selected.getId() === functionBreakpoint.getId())) { + data.name.textContent = ''; + renderRenameBox(this.debugService, this.contextViewService, this.themeService, tree, functionBreakpoint, data.breakpoint, { + initialValue: functionBreakpoint.name, + placeholder: nls.localize('functionBreakpointPlaceholder', "Function to break on"), + ariaLabel: nls.localize('functionBreakPointInputAriaLabel', "Type function breakpoint") + }); + } else { + data.name.textContent = functionBreakpoint.name; + data.checkbox.checked = functionBreakpoint.enabled; + data.breakpoint.title = functionBreakpoint.name; + + // Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099 + const process = this.debugService.getViewModel().focusedProcess; + if ((process && !process.session.capabilities.supportsFunctionBreakpoints) || !this.debugService.getModel().areBreakpointsActivated()) { + tree.addTraits('disabled', [functionBreakpoint]); + if (process && !process.session.capabilities.supportsFunctionBreakpoints) { + data.breakpoint.title = nls.localize('functionBreakpointsNotSupported', "Function breakpoints are not supported by this debug type"); + } + } else { + tree.removeTraits('disabled', [functionBreakpoint]); + } + } + } + + private renderBreakpoint(tree: ITree, breakpoint: debug.IBreakpoint, data: IBreakpointTemplateData): void { + this.debugService.getModel().areBreakpointsActivated() ? tree.removeTraits('disabled', [breakpoint]) : tree.addTraits('disabled', [breakpoint]); + + data.name.textContent = paths.basename(getPathLabel(breakpoint.uri, this.contextService)); + data.lineNumber.textContent = breakpoint.lineNumber.toString(); + if (breakpoint.column) { + data.lineNumber.textContent += `:${breakpoint.column}`; + } + data.filePath.textContent = getPathLabel(paths.dirname(breakpoint.uri.fsPath), this.contextService, this.environmentService); + data.checkbox.checked = breakpoint.enabled; + + const debugActive = this.debugService.state === debug.State.Running || this.debugService.state === debug.State.Stopped || this.debugService.state === debug.State.Initializing; + if (debugActive && !breakpoint.verified) { + tree.addTraits('disabled', [breakpoint]); + if (breakpoint.message) { + data.breakpoint.title = breakpoint.message; + } + } else if (breakpoint.condition || breakpoint.hitCondition) { + data.breakpoint.title = breakpoint.condition ? breakpoint.condition : breakpoint.hitCondition; + } + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + lifecycle.dispose(templateData.toDispose); + } +} + +export class BreakpointsAccessibilityProvider implements IAccessibilityProvider { + + constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) { + // noop + } + + public getAriaLabel(tree: ITree, element: any): string { + if (element instanceof Breakpoint) { + return nls.localize('breakpointAriaLabel', "Breakpoint line {0} {1}, breakpoints, debug", (element).lineNumber, getPathLabel(paths.basename((element).uri.fsPath), this.contextService), this.contextService); + } + if (element instanceof FunctionBreakpoint) { + return nls.localize('functionBreakpointAriaLabel', "Function breakpoint {0}, breakpoints, debug", (element).name); + } + if (element instanceof ExceptionBreakpoint) { + return nls.localize('exceptionBreakpointAriaLabel', "Exception breakpoint {0}, breakpoints, debug", (element).filter); + } + + return null; + } +} + +export class BreakpointsController extends BaseDebugController { + + protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean { + if (element instanceof FunctionBreakpoint && event.detail === 2) { + this.debugService.getViewModel().setSelectedFunctionBreakpoint(element); + return true; + } + if (element instanceof Breakpoint) { + super.onLeftClick(tree, element, event); + this.openBreakpointSource(element, event, event.detail !== 2); + return true; + } + + return super.onLeftClick(tree, element, event); + } + + public openBreakpointSource(breakpoint: Breakpoint, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void { + const sideBySide = (event && (event.ctrlKey || event.metaKey)); + const selection = breakpoint.endLineNumber ? { + startLineNumber: breakpoint.lineNumber, + endLineNumber: breakpoint.endLineNumber, + startColumn: breakpoint.column, + endColumn: breakpoint.endColumn + } : { + startLineNumber: breakpoint.lineNumber, + startColumn: breakpoint.column || 1, + endLineNumber: breakpoint.lineNumber, + endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER + }; + + this.editorService.openEditor({ + resource: breakpoint.uri, + options: { + preserveFocus, + selection, + revealIfVisible: true, + revealInCenterIfOutsideViewport: true, + pinned: !preserveFocus + } + }, sideBySide).done(undefined, errors.onUnexpectedError); + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViews.ts b/src/vs/workbench/parts/debug/electron-browser/debugViews.ts new file mode 100644 index 0000000000..0b8bfff5ce --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/debugViews.ts @@ -0,0 +1,533 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as paths from 'vs/base/common/paths'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import * as dom from 'vs/base/browser/dom'; +import * as builder from 'vs/base/browser/builder'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as errors from 'vs/base/common/errors'; +import { EventType } from 'vs/base/common/events'; +import { IAction } from 'vs/base/common/actions'; +import { prepareActions } from 'vs/workbench/browser/actions'; +import { IHighlightEvent, ITree } from 'vs/base/parts/tree/browser/tree'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { CollapsibleState, ViewSizing } from 'vs/base/browser/ui/splitview/splitview'; +import { CollapseAction } from 'vs/workbench/browser/viewlet'; +import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/parts/views/browser/views'; +import { IDebugService, State, IBreakpoint, IExpression, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED } from 'vs/workbench/parts/debug/common/debug'; +import { Expression, Variable, ExceptionBreakpoint, FunctionBreakpoint, Thread, StackFrame, Breakpoint, ThreadAndProcessIds } from 'vs/workbench/parts/debug/common/debugModel'; +import * as viewer from 'vs/workbench/parts/debug/electron-browser/debugViewer'; +import { AddWatchExpressionAction, RemoveAllWatchExpressionsAction, AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +function renderViewTree(container: HTMLElement): HTMLElement { + const treeContainer = document.createElement('div'); + dom.addClass(treeContainer, 'debug-view-content'); + container.appendChild(treeContainer); + return treeContainer; +} + +const $ = builder.$; +const twistiePixels = 20; + +export class VariablesView extends CollapsibleView { + + private static MEMENTO = 'variablesview.memento'; + private onFocusStackFrameScheduler: RunOnceScheduler; + private variablesFocusedContext: IContextKey; + private settings: any; + + constructor( + initialSize: number, + private options: IViewletViewOptions, + @IContextMenuService contextMenuService: IContextMenuService, + @ITelemetryService private telemetryService: ITelemetryService, + @IDebugService private debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService private instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IListService private listService: IListService, + @IThemeService private themeService: IThemeService + ) { + super(initialSize, { ...(options as IViewOptions), sizing: ViewSizing.Flexible, ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService); + + this.settings = options.viewletSettings; + this.variablesFocusedContext = CONTEXT_VARIABLES_FOCUSED.bindTo(contextKeyService); + // Use scheduler to prevent unnecessary flashing + this.onFocusStackFrameScheduler = new RunOnceScheduler(() => { + // Always clear tree highlight to avoid ending up in a broken state #12203 + this.tree.clearHighlight(); + this.tree.refresh().then(() => { + const stackFrame = this.debugService.getViewModel().focusedStackFrame; + if (stackFrame) { + return stackFrame.getScopes().then(scopes => { + if (scopes.length > 0 && !scopes[0].expensive) { + return this.tree.expand(scopes[0]); + } + return undefined; + }); + } + return undefined; + }).done(null, errors.onUnexpectedError); + }, 400); + } + + public renderHeader(container: HTMLElement): void { + const titleDiv = $('div.title').appendTo(container); + $('span').text(this.options.name).appendTo(titleDiv); + + super.renderHeader(container); + } + + public renderBody(container: HTMLElement): void { + dom.addClass(container, 'debug-variables'); + this.treeContainer = renderViewTree(container); + + this.tree = new Tree(this.treeContainer, { + dataSource: new viewer.VariablesDataSource(), + renderer: this.instantiationService.createInstance(viewer.VariablesRenderer), + accessibilityProvider: new viewer.VariablesAccessibilityProvider(), + controller: this.instantiationService.createInstance(viewer.VariablesController, new viewer.VariablesActionProvider(this.instantiationService), MenuId.DebugVariablesContext) + }, { + ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"), + twistiePixels, + keyboardSupport: false + }); + + this.toDispose.push(attachListStyler(this.tree, this.themeService)); + this.toDispose.push(this.listService.register(this.tree, [this.variablesFocusedContext])); + + const viewModel = this.debugService.getViewModel(); + + this.tree.setInput(viewModel); + + const collapseAction = this.instantiationService.createInstance(CollapseAction, this.tree, false, 'explorer-action collapse-explorer'); + this.toolBar.setActions(prepareActions([collapseAction]))(); + + this.toDispose.push(viewModel.onDidFocusStackFrame(sf => { + // Refresh the tree immediately if it is not visible. + // Otherwise postpone the refresh until user stops stepping. + if (!this.tree.getContentHeight() || sf.explicit) { + this.onFocusStackFrameScheduler.schedule(0); + } else { + this.onFocusStackFrameScheduler.schedule(); + } + })); + this.toDispose.push(this.debugService.onDidChangeState(state => { + collapseAction.enabled = state === State.Running || state === State.Stopped; + })); + + this.toDispose.push(this.debugService.getViewModel().onDidSelectExpression(expression => { + if (!expression || !(expression instanceof Variable)) { + return; + } + + this.tree.refresh(expression, false).then(() => { + this.tree.setHighlight(expression); + this.tree.addOneTimeListener(EventType.HIGHLIGHT, (e: IHighlightEvent) => { + if (!e.highlight) { + this.debugService.getViewModel().setSelectedExpression(null); + } + }); + }).done(null, errors.onUnexpectedError); + })); + } + + public shutdown(): void { + this.settings[VariablesView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED); + super.shutdown(); + } +} + +export class WatchExpressionsView extends CollapsibleView { + + private static MEMENTO = 'watchexpressionsview.memento'; + private onWatchExpressionsUpdatedScheduler: RunOnceScheduler; + private toReveal: IExpression; + private watchExpressionsFocusedContext: IContextKey; + private settings: any; + + constructor( + size: number, + private options: IViewletViewOptions, + @IContextMenuService contextMenuService: IContextMenuService, + @IDebugService private debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService private instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IListService private listService: IListService, + @IThemeService private themeService: IThemeService + ) { + super(size, { ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section"), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService); + this.settings = options.viewletSettings; + + this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(we => { + // only expand when a new watch expression is added. + if (we instanceof Expression) { + this.expand(); + } + })); + this.watchExpressionsFocusedContext = CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(contextKeyService); + + this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => { + this.tree.refresh().done(() => { + return this.toReveal instanceof Expression ? this.tree.reveal(this.toReveal) : TPromise.as(true); + }, errors.onUnexpectedError); + }, 50); + } + + public renderHeader(container: HTMLElement): void { + const titleDiv = $('div.title').appendTo(container); + $('span').text(this.options.name).appendTo(titleDiv); + + super.renderHeader(container); + } + + public renderBody(container: HTMLElement): void { + dom.addClass(container, 'debug-watch'); + this.treeContainer = renderViewTree(container); + + const actionProvider = new viewer.WatchExpressionsActionProvider(this.instantiationService); + this.tree = new Tree(this.treeContainer, { + dataSource: new viewer.WatchExpressionsDataSource(), + renderer: this.instantiationService.createInstance(viewer.WatchExpressionsRenderer, actionProvider, this.actionRunner), + accessibilityProvider: new viewer.WatchExpressionsAccessibilityProvider(), + controller: this.instantiationService.createInstance(viewer.WatchExpressionsController, actionProvider, MenuId.DebugWatchContext), + dnd: this.instantiationService.createInstance(viewer.WatchExpressionsDragAndDrop) + }, { + ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"), + twistiePixels, + keyboardSupport: false + }); + + this.toDispose.push(attachListStyler(this.tree, this.themeService)); + this.toDispose.push(this.listService.register(this.tree, [this.watchExpressionsFocusedContext])); + + this.tree.setInput(this.debugService.getModel()); + + const addWatchExpressionAction = this.instantiationService.createInstance(AddWatchExpressionAction, AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL); + const collapseAction = this.instantiationService.createInstance(CollapseAction, this.tree, true, 'explorer-action collapse-explorer'); + const removeAllWatchExpressionsAction = this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL); + this.toolBar.setActions(prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))(); + + this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(we => { + if (!this.onWatchExpressionsUpdatedScheduler.isScheduled()) { + this.onWatchExpressionsUpdatedScheduler.schedule(); + } + this.toReveal = we; + })); + + this.toDispose.push(this.debugService.getViewModel().onDidSelectExpression(expression => { + if (!expression || !(expression instanceof Expression)) { + return; + } + + this.tree.refresh(expression, false).then(() => { + this.tree.setHighlight(expression); + this.tree.addOneTimeListener(EventType.HIGHLIGHT, (e: IHighlightEvent) => { + if (!e.highlight) { + this.debugService.getViewModel().setSelectedExpression(null); + } + }); + }).done(null, errors.onUnexpectedError); + })); + } + + public shutdown(): void { + this.settings[WatchExpressionsView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED); + super.shutdown(); + } +} + +export class CallStackView extends CollapsibleView { + + private static MEMENTO = 'callstackview.memento'; + private pauseMessage: builder.Builder; + private pauseMessageLabel: builder.Builder; + private onCallStackChangeScheduler: RunOnceScheduler; + private settings: any; + + constructor( + size: number, + private options: IViewletViewOptions, + @IContextMenuService contextMenuService: IContextMenuService, + @ITelemetryService private telemetryService: ITelemetryService, + @IDebugService private debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService private instantiationService: IInstantiationService, + @IListService private listService: IListService, + @IThemeService private themeService: IThemeService + ) { + super(size, { ...(options as IViewOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section"), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService); + this.settings = options.viewletSettings; + + // Create scheduler to prevent unnecessary flashing of tree when reacting to changes + this.onCallStackChangeScheduler = new RunOnceScheduler(() => { + let newTreeInput: any = this.debugService.getModel(); + const processes = this.debugService.getModel().getProcesses(); + if (!this.debugService.getViewModel().isMultiProcessView() && processes.length) { + const threads = processes[0].getAllThreads(); + // Only show the threads in the call stack if there is more than 1 thread. + newTreeInput = threads.length === 1 ? threads[0] : processes[0]; + } + + // Only show the global pause message if we do not display threads. + // Otherwise there will be a pause message per thread and there is no need for a global one. + if (newTreeInput instanceof Thread && newTreeInput.stoppedDetails) { + this.pauseMessageLabel.text(newTreeInput.stoppedDetails.description || nls.localize('debugStopped', "Paused on {0}", newTreeInput.stoppedDetails.reason)); + if (newTreeInput.stoppedDetails.text) { + this.pauseMessageLabel.title(newTreeInput.stoppedDetails.text); + } + newTreeInput.stoppedDetails.reason === 'exception' ? this.pauseMessageLabel.addClass('exception') : this.pauseMessageLabel.removeClass('exception'); + this.pauseMessage.show(); + } else { + this.pauseMessage.hide(); + } + + (this.tree.getInput() === newTreeInput ? this.tree.refresh() : this.tree.setInput(newTreeInput)) + .done(() => this.updateTreeSelection(), errors.onUnexpectedError); + }, 50); + } + + public renderHeader(container: HTMLElement): void { + const title = $('div.debug-call-stack-title').appendTo(container); + $('span.title').text(this.options.name).appendTo(title); + this.pauseMessage = $('span.pause-message').appendTo(title); + this.pauseMessage.hide(); + this.pauseMessageLabel = $('span.label').appendTo(this.pauseMessage); + + super.renderHeader(container); + } + + public renderBody(container: HTMLElement): void { + dom.addClass(container, 'debug-call-stack'); + this.treeContainer = renderViewTree(container); + const actionProvider = this.instantiationService.createInstance(viewer.CallStackActionProvider); + const controller = this.instantiationService.createInstance(viewer.CallStackController, actionProvider, MenuId.DebugCallStackContext); + + this.tree = new Tree(this.treeContainer, { + dataSource: this.instantiationService.createInstance(viewer.CallStackDataSource), + renderer: this.instantiationService.createInstance(viewer.CallStackRenderer), + accessibilityProvider: this.instantiationService.createInstance(viewer.CallstackAccessibilityProvider), + controller + }, { + ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'callStackAriaLabel' }, "Debug Call Stack"), + twistiePixels, + keyboardSupport: false + }); + + this.toDispose.push(attachListStyler(this.tree, this.themeService)); + this.toDispose.push(this.listService.register(this.tree)); + + this.toDispose.push(this.tree.addListener('selection', event => { + if (event && event.payload && event.payload.origin === 'keyboard') { + const element = this.tree.getFocus(); + if (element instanceof ThreadAndProcessIds) { + controller.showMoreStackFrames(this.tree, element); + } else if (element instanceof StackFrame) { + controller.focusStackFrame(element, event, false); + } + } + })); + + this.toDispose.push(this.debugService.getModel().onDidChangeCallStack(() => { + if (!this.onCallStackChangeScheduler.isScheduled()) { + this.onCallStackChangeScheduler.schedule(); + } + })); + this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => + this.updateTreeSelection().done(undefined, errors.onUnexpectedError))); + + // Schedule the update of the call stack tree if the viewlet is opened after a session started #14684 + if (this.debugService.state === State.Stopped) { + this.onCallStackChangeScheduler.schedule(); + } + } + + private updateTreeSelection(): TPromise { + if (!this.tree.getInput()) { + // Tree not initialized yet + return TPromise.as(null); + } + + const stackFrame = this.debugService.getViewModel().focusedStackFrame; + const thread = this.debugService.getViewModel().focusedThread; + const process = this.debugService.getViewModel().focusedProcess; + if (!thread) { + if (!process) { + this.tree.clearSelection(); + return TPromise.as(null); + } + + this.tree.setSelection([process]); + return this.tree.reveal(process); + } + + return this.tree.expandAll([thread.process, thread]).then(() => { + if (!stackFrame) { + return TPromise.as(null); + } + + this.tree.setSelection([stackFrame]); + return this.tree.reveal(stackFrame); + }); + } + + public shutdown(): void { + this.settings[CallStackView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED); + super.shutdown(); + } +} + +export class BreakpointsView extends CollapsibleView { + + private static MAX_VISIBLE_FILES = 9; + private static MEMENTO = 'breakopintsview.memento'; + private breakpointsFocusedContext: IContextKey; + private settings: any; + + constructor( + size: number, + private options: IViewletViewOptions, + @IContextMenuService contextMenuService: IContextMenuService, + @IDebugService private debugService: IDebugService, + @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService private instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IListService private listService: IListService, + @IThemeService private themeService: IThemeService + ) { + super(size, { + ...(options as IViewOptions), + ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section"), + sizing: ViewSizing.Fixed, + initialBodySize: BreakpointsView.getExpandedBodySize(debugService.getModel().getBreakpoints().length + debugService.getModel().getFunctionBreakpoints().length + debugService.getModel().getExceptionBreakpoints().length) + }, keybindingService, contextMenuService); + + this.settings = options.viewletSettings; + this.breakpointsFocusedContext = CONTEXT_BREAKPOINTS_FOCUSED.bindTo(contextKeyService); + this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange())); + } + + public renderHeader(container: HTMLElement): void { + const titleDiv = $('div.title').appendTo(container); + $('span').text(this.options.name).appendTo(titleDiv); + + super.renderHeader(container); + } + + public renderBody(container: HTMLElement): void { + dom.addClass(container, 'debug-breakpoints'); + this.treeContainer = renderViewTree(container); + const actionProvider = new viewer.BreakpointsActionProvider(this.instantiationService, this.debugService); + const controller = this.instantiationService.createInstance(viewer.BreakpointsController, actionProvider, MenuId.DebugBreakpointsContext); + + this.tree = new Tree(this.treeContainer, { + dataSource: new viewer.BreakpointsDataSource(), + renderer: this.instantiationService.createInstance(viewer.BreakpointsRenderer, actionProvider, this.actionRunner), + accessibilityProvider: this.instantiationService.createInstance(viewer.BreakpointsAccessibilityProvider), + controller, + sorter: { + compare(tree: ITree, element: any, otherElement: any): number { + const first = element; + const second = otherElement; + if (first instanceof ExceptionBreakpoint) { + return -1; + } + if (second instanceof ExceptionBreakpoint) { + return 1; + } + if (first instanceof FunctionBreakpoint) { + return -1; + } + if (second instanceof FunctionBreakpoint) { + return 1; + } + + if (first.uri.toString() !== second.uri.toString()) { + return paths.basename(first.uri.fsPath).localeCompare(paths.basename(second.uri.fsPath)); + } + if (first.lineNumber === second.lineNumber) { + return first.column - second.column; + } + + return first.lineNumber - second.lineNumber; + } + } + }, { + ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'breakpointsAriaTreeLabel' }, "Debug Breakpoints"), + twistiePixels, + keyboardSupport: false + }); + + this.toDispose.push(attachListStyler(this.tree, this.themeService)); + this.toDispose.push(this.listService.register(this.tree, [this.breakpointsFocusedContext])); + + this.toDispose.push(this.tree.addListener('selection', event => { + if (event && event.payload && event.payload.origin === 'keyboard') { + const element = this.tree.getFocus(); + if (element instanceof Breakpoint) { + controller.openBreakpointSource(element, event, false); + } + } + })); + + const debugModel = this.debugService.getModel(); + + this.tree.setInput(debugModel); + + this.toDispose.push(this.debugService.getViewModel().onDidSelectFunctionBreakpoint(fbp => { + if (!fbp || !(fbp instanceof FunctionBreakpoint)) { + return; + } + + this.tree.refresh(fbp, false).then(() => { + this.tree.setHighlight(fbp); + this.tree.addOneTimeListener(EventType.HIGHLIGHT, (e: IHighlightEvent) => { + if (!e.highlight) { + this.debugService.getViewModel().setSelectedFunctionBreakpoint(null); + } + }); + }).done(null, errors.onUnexpectedError); + })); + } + + public getActions(): IAction[] { + return [ + this.instantiationService.createInstance(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL), + this.instantiationService.createInstance(ToggleBreakpointsActivatedAction, ToggleBreakpointsActivatedAction.ID, ToggleBreakpointsActivatedAction.ACTIVATE_LABEL), + this.instantiationService.createInstance(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL) + ]; + } + + private onBreakpointsChange(): void { + const model = this.debugService.getModel(); + this.setBodySize(BreakpointsView.getExpandedBodySize( + model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length)); + + if (this.tree) { + this.tree.refresh(); + } + } + + private static getExpandedBodySize(length: number): number { + return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22; + } + + public shutdown(): void { + this.settings[BreakpointsView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED); + super.shutdown(); + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts b/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts new file mode 100644 index 0000000000..40f6f6d3cb --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { Action } from 'vs/base/common/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { removeAnsiEscapeCodes } from 'vs/base/common/strings'; +import { Variable } from 'vs/workbench/parts/debug/common/debugModel'; +import { IDebugService, IStackFrame } from 'vs/workbench/parts/debug/common/debug'; +import { clipboard } from 'electron'; + +export class CopyValueAction extends Action { + static ID = 'workbench.debug.viewlet.action.copyValue'; + static LABEL = nls.localize('copyValue', "Copy Value"); + + constructor(id: string, label: string, private value: any, @IDebugService private debugService: IDebugService) { + super(id, label, 'debug-action copy-value'); + } + + public run(): TPromise { + if (this.value instanceof Variable) { + const frameId = this.debugService.getViewModel().focusedStackFrame.frameId; + const process = this.debugService.getViewModel().focusedProcess; + return process.session.evaluate({ expression: this.value.evaluateName, frameId }).then(result => { + clipboard.writeText(result.body.result); + }, err => clipboard.writeText(this.value.value)); + } + + clipboard.writeText(this.value); + return TPromise.as(null); + } +} + +export class CopyAction extends Action { + static ID = 'workbench.debug.action.copy'; + static LABEL = nls.localize('copy', "Copy"); + + public run(): TPromise { + clipboard.writeText(window.getSelection().toString()); + return TPromise.as(null); + } +} + +export class CopyAllAction extends Action { + static ID = 'workbench.debug.action.copyAll'; + static LABEL = nls.localize('copyAll', "Copy All"); + + constructor(id: string, label: string, private tree: ITree) { + super(id, label); + } + + public run(): TPromise { + let text = ''; + const navigator = this.tree.getNavigator(); + // skip first navigator element - the root node + while (navigator.next()) { + if (text) { + text += `\n`; + } + text += navigator.current().toString(); + } + + clipboard.writeText(removeAnsiEscapeCodes(text)); + return TPromise.as(null); + } +} + +export class CopyStackTraceAction extends Action { + static ID = 'workbench.action.debug.copyStackTrace'; + static LABEL = nls.localize('copyStackTrace', "Copy Call Stack"); + + public run(frame: IStackFrame): TPromise { + clipboard.writeText(frame.thread.getCallStack().map(sf => sf.toString()).join('\n')); + return TPromise.as(null); + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts new file mode 100644 index 0000000000..82124bc83c --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -0,0 +1,534 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import cp = require('child_process'); +import net = require('net'); +import uri from 'vs/base/common/uri'; +import Event, { Emitter } from 'vs/base/common/event'; +import platform = require('vs/base/common/platform'); +import objects = require('vs/base/common/objects'); +import { Action } from 'vs/base/common/actions'; +import errors = require('vs/base/common/errors'); +import { TPromise } from 'vs/base/common/winjs.base'; +import severity from 'vs/base/common/severity'; +import stdfork = require('vs/base/node/stdFork'); +import { IMessageService, CloseAction } from 'vs/platform/message/common/message'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; +import { ITerminalService as IExternalTerminalService } from 'vs/workbench/parts/execution/common/execution'; +import debug = require('vs/workbench/parts/debug/common/debug'); +import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter'; +import { V8Protocol } from 'vs/workbench/parts/debug/node/v8Protocol'; +import { IOutputService } from 'vs/workbench/parts/output/common/output'; +import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { TerminalSupport } from 'vs/workbench/parts/debug/electron-browser/terminalSupport'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export interface SessionExitedEvent extends debug.DebugEvent { + body: { + exitCode: number, + sessionId: string + }; +} + +export interface SessionTerminatedEvent extends debug.DebugEvent { + body: { + restart?: boolean, + sessionId: string + }; +} + +export class RawDebugSession extends V8Protocol implements debug.ISession { + + public emittedStopped: boolean; + public readyForBreakpoints: boolean; + + private serverProcess: cp.ChildProcess; + private socket: net.Socket = null; + private cachedInitServer: TPromise; + private startTime: number; + public disconnected: boolean; + private sentPromises: TPromise[]; + private _capabilities: DebugProtocol.Capabilities; + private allThreadsContinued: boolean; + + private _onDidInitialize: Emitter; + private _onDidStop: Emitter; + private _onDidContinued: Emitter; + private _onDidTerminateDebugee: Emitter; + private _onDidExitAdapter: Emitter; + private _onDidThread: Emitter; + private _onDidOutput: Emitter; + private _onDidBreakpoint: Emitter; + private _onDidCustomEvent: Emitter; + private _onDidEvent: Emitter; + + constructor( + id: string, + private debugServerPort: number, + private adapter: Adapter, + private customTelemetryService: ITelemetryService, + public root: uri, + @IMessageService private messageService: IMessageService, + @ITelemetryService private telemetryService: ITelemetryService, + @IOutputService private outputService: IOutputService, + @ITerminalService private terminalService: ITerminalService, + @IExternalTerminalService private nativeTerminalService: IExternalTerminalService, + @IConfigurationService private configurationService: IConfigurationService + ) { + super(id); + this.emittedStopped = false; + this.readyForBreakpoints = false; + this.allThreadsContinued = true; + this.sentPromises = []; + + this._onDidInitialize = new Emitter(); + this._onDidStop = new Emitter(); + this._onDidContinued = new Emitter(); + this._onDidTerminateDebugee = new Emitter(); + this._onDidExitAdapter = new Emitter(); + this._onDidThread = new Emitter(); + this._onDidOutput = new Emitter(); + this._onDidBreakpoint = new Emitter(); + this._onDidCustomEvent = new Emitter(); + this._onDidEvent = new Emitter(); + } + + public get onDidInitialize(): Event { + return this._onDidInitialize.event; + } + + public get onDidStop(): Event { + return this._onDidStop.event; + } + + public get onDidContinued(): Event { + return this._onDidContinued.event; + } + + public get onDidTerminateDebugee(): Event { + return this._onDidTerminateDebugee.event; + } + + public get onDidExitAdapter(): Event { + return this._onDidExitAdapter.event; + } + + public get onDidThread(): Event { + return this._onDidThread.event; + } + + public get onDidOutput(): Event { + return this._onDidOutput.event; + } + + public get onDidBreakpoint(): Event { + return this._onDidBreakpoint.event; + } + + public get onDidCustomEvent(): Event { + return this._onDidCustomEvent.event; + } + + public get onDidEvent(): Event { + return this._onDidEvent.event; + } + + private initServer(): TPromise { + if (this.cachedInitServer) { + return this.cachedInitServer; + } + + const serverPromise = this.debugServerPort ? this.connectServer(this.debugServerPort) : this.startServer(); + this.cachedInitServer = serverPromise.then(() => { + this.startTime = new Date().getTime(); + }, err => { + this.cachedInitServer = null; + return TPromise.wrapError(err); + }); + + return this.cachedInitServer; + } + + public custom(request: string, args: any): TPromise { + return this.send(request, args); + } + + protected send(command: string, args: any, cancelOnDisconnect = true): TPromise { + return this.initServer().then(() => { + const promise = super.send(command, args).then(response => response, (errorResponse: DebugProtocol.ErrorResponse) => { + const error = errorResponse && errorResponse.body ? errorResponse.body.error : null; + const errorMessage = errorResponse ? errorResponse.message : ''; + const telemetryMessage = error ? debug.formatPII(error.format, true, error.variables) : errorMessage; + if (error && error.sendTelemetry) { + this.telemetryService.publicLog('debugProtocolErrorResponse', { error: telemetryMessage }); + if (this.customTelemetryService) { + this.customTelemetryService.publicLog('debugProtocolErrorResponse', { error: telemetryMessage }); + } + } + + const userMessage = error ? debug.formatPII(error.format, false, error.variables) : errorMessage; + if (error && error.url) { + const label = error.urlLabel ? error.urlLabel : nls.localize('moreInfo', "More Info"); + return TPromise.wrapError(errors.create(userMessage, { + actions: [CloseAction, new Action('debug.moreInfo', label, null, true, () => { + window.open(error.url); + return TPromise.as(null); + })] + })); + } + + return errors.isPromiseCanceledError(errorResponse) ? undefined : TPromise.wrapError(new Error(userMessage)); + }); + + if (cancelOnDisconnect) { + this.sentPromises.push(promise); + } + return promise; + }); + } + + protected onEvent(event: debug.DebugEvent): void { + event.sessionId = this.getId(); + + if (event.event === 'initialized') { + this.readyForBreakpoints = true; + this._onDidInitialize.fire(event); + } else if (event.event === 'stopped') { + this.emittedStopped = true; + this._onDidStop.fire(event); + } else if (event.event === 'continued') { + this.allThreadsContinued = (event).body.allThreadsContinued === false ? false : true; + this._onDidContinued.fire(event); + } else if (event.event === 'thread') { + this._onDidThread.fire(event); + } else if (event.event === 'output') { + this._onDidOutput.fire(event); + } else if (event.event === 'breakpoint') { + this._onDidBreakpoint.fire(event); + } else if (event.event === 'terminated') { + this._onDidTerminateDebugee.fire(event); + } else if (event.event === 'exit') { + this._onDidExitAdapter.fire(event); + } else { + this._onDidCustomEvent.fire(event); + } + + this._onDidEvent.fire(event); + } + + public get capabilities(): DebugProtocol.Capabilities { + return this._capabilities || {}; + } + + public initialize(args: DebugProtocol.InitializeRequestArguments): TPromise { + return this.send('initialize', args).then(response => this.readCapabilities(response)); + } + + private readCapabilities(response: DebugProtocol.Response): DebugProtocol.Response { + if (response) { + this._capabilities = objects.mixin(this._capabilities, response.body); + } + + return response; + } + + public launch(args: DebugProtocol.LaunchRequestArguments): TPromise { + return this.send('launch', args).then(response => this.readCapabilities(response)); + } + + public attach(args: DebugProtocol.AttachRequestArguments): TPromise { + return this.send('attach', args).then(response => this.readCapabilities(response)); + } + + public next(args: DebugProtocol.NextArguments): TPromise { + return this.send('next', args).then(response => { + this.fireFakeContinued(args.threadId); + return response; + }); + } + + public stepIn(args: DebugProtocol.StepInArguments): TPromise { + return this.send('stepIn', args).then(response => { + this.fireFakeContinued(args.threadId); + return response; + }); + } + + public stepOut(args: DebugProtocol.StepOutArguments): TPromise { + return this.send('stepOut', args).then(response => { + this.fireFakeContinued(args.threadId); + return response; + }); + } + + public continue(args: DebugProtocol.ContinueArguments): TPromise { + return this.send('continue', args).then(response => { + if (response && response.body && response.body.allThreadsContinued !== undefined) { + this.allThreadsContinued = response.body.allThreadsContinued; + } + this.fireFakeContinued(args.threadId, this.allThreadsContinued); + return response; + }); + } + + public pause(args: DebugProtocol.PauseArguments): TPromise { + return this.send('pause', args); + } + + public setVariable(args: DebugProtocol.SetVariableArguments): TPromise { + return this.send('setVariable', args); + } + + public restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): TPromise { + return this.send('restartFrame', args).then(response => { + this.fireFakeContinued(threadId); + return response; + }); + } + + public completions(args: DebugProtocol.CompletionsArguments): TPromise { + return this.send('completions', args); + } + + public disconnect(restart = false, force = false): TPromise { + if (this.disconnected && force) { + return this.stopServer(); + } + + // Cancel all sent promises on disconnect so debug trees are not left in a broken state #3666. + // Give a 1s timeout to give a chance for some promises to complete. + setTimeout(() => { + this.sentPromises.forEach(p => p && p.cancel()); + this.sentPromises = []; + }, 1000); + + if ((this.serverProcess || this.socket) && !this.disconnected) { + // point of no return: from now on don't report any errors + this.disconnected = true; + return this.send('disconnect', { restart: restart }, false).then(() => this.stopServer(), () => this.stopServer()); + } + + return TPromise.as(null); + } + + public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): TPromise { + return this.send('setBreakpoints', args); + } + + public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): TPromise { + return this.send('setFunctionBreakpoints', args); + } + + public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): TPromise { + return this.send('setExceptionBreakpoints', args); + } + + public configurationDone(): TPromise { + return this.send('configurationDone', null); + } + + public stackTrace(args: DebugProtocol.StackTraceArguments): TPromise { + return this.send('stackTrace', args); + } + + public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise { + return this.send('exceptionInfo', args); + } + + public scopes(args: DebugProtocol.ScopesArguments): TPromise { + return this.send('scopes', args); + } + + public variables(args: DebugProtocol.VariablesArguments): TPromise { + return this.send('variables', args); + } + + public source(args: DebugProtocol.SourceArguments): TPromise { + return this.send('source', args); + } + + public threads(): TPromise { + return this.send('threads', null); + } + + public evaluate(args: DebugProtocol.EvaluateArguments): TPromise { + return this.send('evaluate', args); + } + + public stepBack(args: DebugProtocol.StepBackArguments): TPromise { + return this.send('stepBack', args).then(response => { + this.fireFakeContinued(args.threadId); + return response; + }); + } + + public reverseContinue(args: DebugProtocol.ReverseContinueArguments): TPromise { + return this.send('reverseContinue', args).then(response => { + this.fireFakeContinued(args.threadId); + return response; + }); + } + + public getLengthInSeconds(): number { + return (new Date().getTime() - this.startTime) / 1000; + } + + protected dispatchRequest(request: DebugProtocol.Request, response: DebugProtocol.Response): void { + + if (request.command === 'runInTerminal') { + + TerminalSupport.runInTerminal(this.terminalService, this.nativeTerminalService, this.configurationService, request.arguments, response).then(() => { + this.sendResponse(response); + }, e => { + response.success = false; + response.message = e.message; + this.sendResponse(response); + }); + } else if (request.command === 'handshake') { + try { + const vsda = require.__$__nodeRequire('vsda'); + const obj = new vsda.signer(); + const sig = obj.sign(request.arguments.value); + response.body = { + signature: sig + }; + this.sendResponse(response); + } catch (e) { + response.success = false; + response.message = e.message; + this.sendResponse(response); + } + } else { + response.success = false; + response.message = `unknown request '${request.command}'`; + this.sendResponse(response); + } + } + + private fireFakeContinued(threadId: number, allThreadsContinued = false): void { + this._onDidContinued.fire({ + type: 'event', + event: 'continued', + body: { + threadId, + allThreadsContinued + }, + seq: undefined + }); + } + + private connectServer(port: number): TPromise { + return new TPromise((c, e) => { + this.socket = net.createConnection(port, '127.0.0.1', () => { + this.connect(this.socket, this.socket); + c(null); + }); + this.socket.on('error', (err: any) => { + e(err); + }); + this.socket.on('close', () => this.onServerExit()); + }); + } + + private startServer(): TPromise { + return this.adapter.getAdapterExecutable(this.root).then(ae => this.launchServer(ae).then(() => { + this.serverProcess.on('error', (err: Error) => this.onServerError(err)); + this.serverProcess.on('exit', (code: number, signal: string) => this.onServerExit()); + + const sanitize = (s: string) => s.toString().replace(/\r?\n$/mg, ''); + // this.serverProcess.stdout.on('data', (data: string) => { + // console.log('%c' + sanitize(data), 'background: #ddd; font-style: italic;'); + // }); + this.serverProcess.stderr.on('data', (data: string) => { + this.outputService.getChannel(ExtensionsChannelId).append(sanitize(data)); + }); + + this.connect(this.serverProcess.stdout, this.serverProcess.stdin); + })); + } + + private launchServer(launch: debug.IAdapterExecutable): TPromise { + return new TPromise((c, e) => { + if (launch.command === 'node') { + if (Array.isArray(launch.args) && launch.args.length > 0) { + stdfork.fork(launch.args[0], launch.args.slice(1), {}, (err, child) => { + if (err) { + e(new Error(nls.localize('unableToLaunchDebugAdapter', "Unable to launch debug adapter from '{0}'.", launch.args[0]))); + } + this.serverProcess = child; + c(null); + }); + } else { + e(new Error(nls.localize('unableToLaunchDebugAdapterNoArgs', "Unable to launch debug adapter."))); + } + } else { + this.serverProcess = cp.spawn(launch.command, launch.args, { + stdio: [ + 'pipe', // stdin + 'pipe', // stdout + 'pipe' // stderr + ], + }); + c(null); + } + }); + } + + private stopServer(): TPromise { + + if (this.socket !== null) { + this.socket.end(); + this.cachedInitServer = null; + } + + this.onEvent({ event: 'exit', type: 'event', seq: 0 }); + if (!this.serverProcess) { + return TPromise.as(null); + } + + this.disconnected = true; + + let ret: TPromise; + // when killing a process in windows its child + // processes are *not* killed but become root + // processes. Therefore we use TASKKILL.EXE + if (platform.isWindows) { + ret = new TPromise((c, e) => { + const killer = cp.exec(`taskkill /F /T /PID ${this.serverProcess.pid}`, function (err, stdout, stderr) { + if (err) { + return e(err); + } + }); + killer.on('exit', c); + killer.on('error', e); + }); + } else { + this.serverProcess.kill('SIGTERM'); + ret = TPromise.as(null); + } + + return ret; + } + + protected onServerError(err: Error): void { + this.messageService.show(severity.Error, nls.localize('stoppingDebugAdapter', "{0}. Stopping the debug adapter.", err.message)); + this.stopServer().done(null, errors.onUnexpectedError); + } + + private onServerExit(): void { + this.serverProcess = null; + this.cachedInitServer = null; + if (!this.disconnected) { + this.messageService.show(severity.Error, nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly")); + } + this.onEvent({ event: 'exit', type: 'event', seq: 0 }); + } + + public dispose(): void { + this.disconnect().done(null, errors.onUnexpectedError); + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts new file mode 100644 index 0000000000..357800e458 --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -0,0 +1,414 @@ +/*--------------------------------------------------------------------------------------------- + * 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!vs/workbench/parts/debug/browser/media/repl'; +import * as nls from 'vs/nls'; +import uri from 'vs/base/common/uri'; +import { wireCancellationToken } from 'vs/base/common/async'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as errors from 'vs/base/common/errors'; +import { IAction } from 'vs/base/common/actions'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import * as dom from 'vs/base/browser/dom'; +import { isMacintosh } from 'vs/base/common/platform'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { ITree, ITreeOptions } from 'vs/base/parts/tree/browser/tree'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { Context as SuggestContext } from 'vs/editor/contrib/suggest/browser/suggest'; +import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; +import { IReadOnlyModel, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Position } from 'vs/editor/common/core/position'; +import * as modes from 'vs/editor/common/modes'; +import { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ReplExpressionsRenderer, ReplExpressionsController, ReplExpressionsDataSource, ReplExpressionsActionProvider, ReplExpressionsAccessibilityProvider } from 'vs/workbench/parts/debug/electron-browser/replViewer'; +import { ReplInputEditor } from 'vs/workbench/parts/debug/electron-browser/replEditor'; +import * as debug from 'vs/workbench/parts/debug/common/debug'; +import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { ReplHistory } from 'vs/workbench/parts/debug/common/replHistory'; +import { Panel } from 'vs/workbench/browser/panel'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { clipboard } from 'electron'; + +const $ = dom.$; + +const replTreeOptions: ITreeOptions = { + twistiePixels: 20, + ariaLabel: nls.localize('replAriaLabel', "Read Eval Print Loop Panel"), + keyboardSupport: false +}; + +const HISTORY_STORAGE_KEY = 'debug.repl.history'; +const IPrivateReplService = createDecorator('privateReplService'); + +export interface IPrivateReplService { + _serviceBrand: any; + navigateHistory(previous: boolean): void; + acceptReplInput(): void; + getVisibleContent(): string; +} + +export class Repl extends Panel implements IPrivateReplService { + public _serviceBrand: any; + + private static HALF_WIDTH_TYPICAL = 'n'; + + private static HISTORY: ReplHistory; + private static REFRESH_DELAY = 500; // delay in ms to refresh the repl for new elements to show + private static REPL_INPUT_INITIAL_HEIGHT = 19; + private static REPL_INPUT_MAX_HEIGHT = 170; + + private tree: ITree; + private renderer: ReplExpressionsRenderer; + private characterWidthSurveyor: HTMLElement; + private treeContainer: HTMLElement; + private replInput: ReplInputEditor; + private replInputContainer: HTMLElement; + private refreshTimeoutHandle: number; + private actions: IAction[]; + private dimension: Dimension; + private replInputHeight: number; + + constructor( + @debug.IDebugService private debugService: debug.IDebugService, + @ITelemetryService telemetryService: ITelemetryService, + @IInstantiationService private instantiationService: IInstantiationService, + @IStorageService private storageService: IStorageService, + @IPanelService private panelService: IPanelService, + @IThemeService protected themeService: IThemeService, + @IModelService private modelService: IModelService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IListService private listService: IListService + ) { + super(debug.REPL_ID, telemetryService, themeService); + + this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT; + this.registerListeners(); + } + + private registerListeners(): void { + this.toUnbind.push(this.debugService.getModel().onDidChangeReplElements(() => { + this.refreshReplElements(this.debugService.getModel().getReplElements().length === 0); + })); + this.toUnbind.push(this.panelService.onDidPanelOpen(panel => this.refreshReplElements(true))); + } + + private refreshReplElements(noDelay: boolean): void { + if (this.tree && this.isVisible()) { + if (this.refreshTimeoutHandle) { + return; // refresh already triggered + } + + const delay = noDelay ? 0 : Repl.REFRESH_DELAY; + this.refreshTimeoutHandle = setTimeout(() => { + this.refreshTimeoutHandle = null; + const previousScrollPosition = this.tree.getScrollPosition(); + this.tree.refresh().then(() => { + if (previousScrollPosition === 1 || previousScrollPosition === 0) { + // Only scroll if we were scrolled all the way down before tree refreshed #10486 + this.tree.setScrollPosition(1); + } + }, errors.onUnexpectedError); + }, delay); + } + } + + public create(parent: Builder): TPromise { + super.create(parent); + const container = dom.append(parent.getHTMLElement(), $('.repl')); + this.treeContainer = dom.append(container, $('.repl-tree')); + this.createReplInput(container); + + this.characterWidthSurveyor = dom.append(container, $('.surveyor')); + this.characterWidthSurveyor.textContent = Repl.HALF_WIDTH_TYPICAL; + for (let i = 0; i < 10; i++) { + this.characterWidthSurveyor.textContent += this.characterWidthSurveyor.textContent; + } + this.characterWidthSurveyor.style.fontSize = isMacintosh ? '12px' : '14px'; + + this.renderer = this.instantiationService.createInstance(ReplExpressionsRenderer); + const controller = this.instantiationService.createInstance(ReplExpressionsController, new ReplExpressionsActionProvider(this.instantiationService), MenuId.DebugConsoleContext); + controller.toFocusOnClick = this.replInput; + + this.tree = new Tree(this.treeContainer, { + dataSource: new ReplExpressionsDataSource(), + renderer: this.renderer, + accessibilityProvider: new ReplExpressionsAccessibilityProvider(), + controller + }, replTreeOptions); + + this.toUnbind.push(attachListStyler(this.tree, this.themeService)); + this.toUnbind.push(this.listService.register(this.tree)); + + if (!Repl.HISTORY) { + Repl.HISTORY = new ReplHistory(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]'))); + } + + return this.tree.setInput(this.debugService.getModel()); + } + + private createReplInput(container: HTMLElement): void { + this.replInputContainer = dom.append(container, $('.repl-input-wrapper')); + + const scopedContextKeyService = this.contextKeyService.createScoped(this.replInputContainer); + this.toUnbind.push(scopedContextKeyService); + debug.CONTEXT_IN_DEBUG_REPL.bindTo(scopedContextKeyService).set(true); + const onFirstReplLine = debug.CONTEXT_ON_FIRST_DEBUG_REPL_LINE.bindTo(scopedContextKeyService); + onFirstReplLine.set(true); + const onLastReplLine = debug.CONTEXT_ON_LAST_DEBUG_REPL_LINE.bindTo(scopedContextKeyService); + onLastReplLine.set(true); + + const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection( + [IContextKeyService, scopedContextKeyService], [IPrivateReplService, this])); + this.replInput = scopedInstantiationService.createInstance(ReplInputEditor, this.replInputContainer, this.getReplInputOptions()); + const model = this.modelService.createModel('', null, uri.parse(`${debug.DEBUG_SCHEME}:input`)); + this.replInput.setModel(model); + + modes.SuggestRegistry.register({ scheme: debug.DEBUG_SCHEME }, { + triggerCharacters: ['.'], + provideCompletionItems: (model: IReadOnlyModel, position: Position, token: CancellationToken): Thenable => { + const word = this.replInput.getModel().getWordAtPosition(position); + const overwriteBefore = word ? word.word.length : 0; + const text = this.replInput.getModel().getLineContent(position.lineNumber); + const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; + const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; + const focusedProcess = this.debugService.getViewModel().focusedProcess; + const completions = focusedProcess ? focusedProcess.completions(frameId, text, position, overwriteBefore) : TPromise.as([]); + return wireCancellationToken(token, completions.then(suggestions => ({ + suggestions + }))); + } + }); + + this.toUnbind.push(this.replInput.onDidScrollChange(e => { + if (!e.scrollHeightChanged) { + return; + } + this.replInputHeight = Math.max(Repl.REPL_INPUT_INITIAL_HEIGHT, Math.min(Repl.REPL_INPUT_MAX_HEIGHT, e.scrollHeight, this.dimension.height)); + this.layout(this.dimension); + })); + this.toUnbind.push(this.replInput.onDidChangeCursorPosition(e => { + onFirstReplLine.set(e.position.lineNumber === 1); + onLastReplLine.set(e.position.lineNumber === this.replInput.getModel().getLineCount()); + })); + + this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.FOCUS, () => dom.addClass(this.replInputContainer, 'synthetic-focus'))); + this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.BLUR, () => dom.removeClass(this.replInputContainer, 'synthetic-focus'))); + } + + public navigateHistory(previous: boolean): void { + const historyInput = previous ? Repl.HISTORY.previous() : Repl.HISTORY.next(); + if (historyInput) { + Repl.HISTORY.remember(this.replInput.getValue(), previous); + this.replInput.setValue(historyInput); + // always leave cursor at the end. + this.replInput.setPosition({ lineNumber: 1, column: historyInput.length + 1 }); + } + } + + public acceptReplInput(): void { + this.debugService.addReplExpression(this.replInput.getValue()); + Repl.HISTORY.evaluated(this.replInput.getValue()); + this.replInput.setValue(''); + // Trigger a layout to shrink a potential multi line input + this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT; + this.layout(this.dimension); + } + + public getVisibleContent(): string { + let text = ''; + const navigator = this.tree.getNavigator(); + // skip first navigator element - the root node + while (navigator.next()) { + if (text) { + text += `\n`; + } + text += navigator.current().toString(); + } + + return text; + } + + public layout(dimension: Dimension): void { + this.dimension = dimension; + if (this.tree) { + this.renderer.setWidth(dimension.width - 25, this.characterWidthSurveyor.clientWidth / this.characterWidthSurveyor.textContent.length); + const treeHeight = dimension.height - this.replInputHeight; + this.treeContainer.style.height = `${treeHeight}px`; + this.tree.layout(treeHeight); + } + this.replInputContainer.style.height = `${this.replInputHeight}px`; + + this.replInput.layout({ width: dimension.width - 20, height: this.replInputHeight }); + } + + public focus(): void { + this.replInput.focus(); + } + + public getActions(): IAction[] { + if (!this.actions) { + this.actions = [ + this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL) + ]; + + this.actions.forEach(a => { + this.toUnbind.push(a); + }); + } + + return this.actions; + } + + public shutdown(): void { + const replHistory = Repl.HISTORY.save(); + if (replHistory.length) { + this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE); + } else { + this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); + } + } + + private getReplInputOptions(): IEditorOptions { + return { + wordWrap: 'on', + overviewRulerLanes: 0, + glyphMargin: false, + lineNumbers: 'off', + folding: false, + selectOnLineNumbers: false, + selectionHighlight: false, + scrollbar: { + horizontal: 'hidden' + }, + lineDecorationsWidth: 0, + scrollBeyondLastLine: false, + renderLineHighlight: 'none', + fixedOverflowWidgets: true, + acceptSuggestionOnEnter: 'smart', + minimap: { + enabled: false + } + }; + } + + public dispose(): void { + this.replInput.dispose(); + super.dispose(); + } +} + +@editorAction +class ReplHistoryPreviousAction extends EditorAction { + + constructor() { + super({ + id: 'repl.action.historyPrevious', + label: nls.localize('actions.repl.historyPrevious', "History Previous"), + alias: 'History Previous', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, debug.CONTEXT_ON_FIRST_DEBUG_REPL_LINE), + primary: KeyCode.UpArrow, + weight: 50 + }, + menuOpts: { + group: 'debug' + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void | TPromise { + accessor.get(IPrivateReplService).navigateHistory(true); + } +} + +@editorAction +class ReplHistoryNextAction extends EditorAction { + + constructor() { + super({ + id: 'repl.action.historyNext', + label: nls.localize('actions.repl.historyNext', "History Next"), + alias: 'History Next', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, debug.CONTEXT_ON_LAST_DEBUG_REPL_LINE), + primary: KeyCode.DownArrow, + weight: 50 + }, + menuOpts: { + group: 'debug' + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void | TPromise { + accessor.get(IPrivateReplService).navigateHistory(false); + } +} + +@editorAction +class AcceptReplInputAction extends EditorAction { + + constructor() { + super({ + id: 'repl.action.acceptInput', + label: nls.localize({ key: 'actions.repl.acceptInput', comment: ['Apply input from the debug console input box'] }, "REPL Accept Input"), + alias: 'REPL Accept Input', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Enter + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void | TPromise { + SuggestController.get(editor).acceptSelectedSuggestion(); + accessor.get(IPrivateReplService).acceptReplInput(); + } +} + +const SuggestCommand = EditorCommand.bindToContribution(SuggestController.get); +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'repl.action.acceptSuggestion', + precondition: ContextKeyExpr.and(debug.CONTEXT_IN_DEBUG_REPL, SuggestContext.Visible), + handler: x => x.acceptSelectedSuggestion(), + kbOpts: { + weight: 50, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.RightArrow + } +})); + +@editorAction +export class ReplCopyAllAction extends EditorAction { + + constructor() { + super({ + id: 'repl.action.copyAll', + label: nls.localize('actions.repl.copyAll', "Debug: Console Copy All"), + alias: 'Debug Console Copy All', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void | TPromise { + clipboard.writeText(accessor.get(IPrivateReplService).getVisibleContent()); + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/replEditor.ts b/src/vs/workbench/parts/debug/electron-browser/replEditor.ts new file mode 100644 index 0000000000..2bf760d45a --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/replEditor.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { EditorAction, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { IEditorContributionCtor } from 'vs/editor/browser/editorBrowser'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICommandService } from 'vs/platform/commands/common/commands'; + +// Allowed Editor Contributions: +import { MenuPreventer } from 'vs/workbench/parts/codeEditor/electron-browser/menuPreventer'; +import { SelectionClipboard } from 'vs/workbench/parts/codeEditor/electron-browser/selectionClipboard'; +import { ContextMenuController } from 'vs/editor/contrib/contextmenu/browser/contextmenu'; +import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; +import { TabCompletionController } from 'vs/workbench/parts/snippets/electron-browser/tabCompletion'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +export class ReplInputEditor extends CodeEditorWidget { + constructor( + domElement: HTMLElement, + options: IEditorOptions, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @ICommandService commandService: ICommandService, + @IContextKeyService contextKeyService: IContextKeyService, + @IThemeService themeService: IThemeService + ) { + super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService, themeService); + } + + protected _getContributions(): IEditorContributionCtor[] { + return [ + MenuPreventer, + SelectionClipboard, + ContextMenuController, + SuggestController, + SnippetController2, + TabCompletionController, + ]; + } + + protected _getActions(): EditorAction[] { + return CommonEditorRegistry.getEditorActions(); + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts new file mode 100644 index 0000000000..8291541912 --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -0,0 +1,434 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction } from 'vs/base/common/actions'; +import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings'; +import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import * as dom from 'vs/base/browser/dom'; +import severity from 'vs/base/common/severity'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { ITree, IAccessibilityProvider, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree'; +import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; +import { IExpressionContainer, IExpression } from 'vs/workbench/parts/debug/common/debug'; +import { Model, OutputNameValueElement, Expression, OutputElement, Variable } from 'vs/workbench/parts/debug/common/debugModel'; +import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/electron-browser/debugViewer'; +import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { CopyAction, CopyAllAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; + +const $ = dom.$; + +export class ReplExpressionsDataSource implements IDataSource { + + public getId(tree: ITree, element: any): string { + return element.getId(); + } + + public hasChildren(tree: ITree, element: any): boolean { + return element instanceof Model || (element).hasChildren; + } + + public getChildren(tree: ITree, element: any): TPromise { + if (element instanceof Model) { + return TPromise.as(element.getReplElements()); + } + if (element instanceof OutputNameValueElement) { + return TPromise.as(element.getChildren()); + } + if (element instanceof OutputElement) { + return TPromise.as(null); + } + + return (element).getChildren(); + } + + public getParent(tree: ITree, element: any): TPromise { + return TPromise.as(null); + } +} + +interface IExpressionTemplateData { + input: HTMLElement; + output: HTMLElement; + value: HTMLElement; + annotation: HTMLElement; +} + +interface IValueOutputTemplateData { + container: HTMLElement; + value: HTMLElement; +} + +interface IKeyValueOutputTemplateData { + container: HTMLElement; + expression: HTMLElement; + name: HTMLElement; + value: HTMLElement; + annotation: HTMLElement; +} + +export class ReplExpressionsRenderer implements IRenderer { + + private static VARIABLE_TEMPLATE_ID = 'variable'; + private static EXPRESSION_TEMPLATE_ID = 'inputOutputPair'; + private static VALUE_OUTPUT_TEMPLATE_ID = 'outputValue'; + private static NAME_VALUE_OUTPUT_TEMPLATE_ID = 'outputNameValue'; + + private static LINE_HEIGHT_PX = 18; + + private width: number; + private characterWidth: number; + + private linkDetector: LinkDetector; + + constructor( + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + this.linkDetector = this.instantiationService.createInstance(LinkDetector); + } + + public getHeight(tree: ITree, element: any): number { + if (element instanceof Variable && (element.hasChildren || (element.name !== null))) { + return ReplExpressionsRenderer.LINE_HEIGHT_PX; + } + if (element instanceof Expression && element.hasChildren) { + return 2 * ReplExpressionsRenderer.LINE_HEIGHT_PX; + } + + return this.getHeightForString(element.value) + (element instanceof Expression ? this.getHeightForString(element.name) : 0); + } + + private getHeightForString(s: string): number { + if (!s || !s.length || !this.width || this.width <= 0 || !this.characterWidth || this.characterWidth <= 0) { + return ReplExpressionsRenderer.LINE_HEIGHT_PX; + } + + // Last new line should be ignored since the repl elements are by design split by rows + if (endsWith(s, '\n')) { + s = s.substr(0, s.length - 1); + } + const lines = removeAnsiEscapeCodes(s).split('\n'); + const numLines = lines.reduce((lineCount: number, line: string) => { + let lineLength = 0; + for (let i = 0; i < line.length; i++) { + lineLength += isFullWidthCharacter(line.charCodeAt(i)) ? 2 : 1; + } + + return lineCount + Math.floor(lineLength * this.characterWidth / this.width); + }, lines.length); + + return ReplExpressionsRenderer.LINE_HEIGHT_PX * numLines; + } + + public setWidth(fullWidth: number, characterWidth: number): void { + this.width = fullWidth; + this.characterWidth = characterWidth; + } + + public getTemplateId(tree: ITree, element: any): string { + if (element instanceof Variable && element.name) { + return ReplExpressionsRenderer.VARIABLE_TEMPLATE_ID; + } + if (element instanceof Expression) { + return ReplExpressionsRenderer.EXPRESSION_TEMPLATE_ID; + } + if (element instanceof OutputElement || (element instanceof Variable && !element.name)) { + // Variable with no name is a top level variable which should be rendered like an output element #17404 + return ReplExpressionsRenderer.VALUE_OUTPUT_TEMPLATE_ID; + } + if (element instanceof OutputNameValueElement) { + return ReplExpressionsRenderer.NAME_VALUE_OUTPUT_TEMPLATE_ID; + } + + return null; + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + if (templateId === ReplExpressionsRenderer.VARIABLE_TEMPLATE_ID) { + let data: IVariableTemplateData = Object.create(null); + data.expression = dom.append(container, $('.expression')); + data.name = dom.append(data.expression, $('span.name')); + data.value = dom.append(data.expression, $('span.value')); + + return data; + } + + if (templateId === ReplExpressionsRenderer.EXPRESSION_TEMPLATE_ID) { + let data: IExpressionTemplateData = Object.create(null); + dom.addClass(container, 'input-output-pair'); + data.input = dom.append(container, $('.input.expression')); + data.output = dom.append(container, $('.output.expression')); + data.value = dom.append(data.output, $('span.value')); + data.annotation = dom.append(data.output, $('span')); + + return data; + } + + if (templateId === ReplExpressionsRenderer.VALUE_OUTPUT_TEMPLATE_ID) { + let data: IValueOutputTemplateData = Object.create(null); + dom.addClass(container, 'output'); + let expression = dom.append(container, $('.output.expression')); + + data.container = container; + data.value = dom.append(expression, $('span.value')); + + return data; + } + + if (templateId === ReplExpressionsRenderer.NAME_VALUE_OUTPUT_TEMPLATE_ID) { + let data: IKeyValueOutputTemplateData = Object.create(null); + dom.addClass(container, 'output'); + + data.container = container; + data.expression = dom.append(container, $('.output.expression')); + data.name = dom.append(data.expression, $('span.name')); + data.value = dom.append(data.expression, $('span.value')); + data.annotation = dom.append(data.expression, $('span')); + + return data; + } + } + + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + if (templateId === ReplExpressionsRenderer.VARIABLE_TEMPLATE_ID) { + renderVariable(tree, element, templateData, false); + } else if (templateId === ReplExpressionsRenderer.EXPRESSION_TEMPLATE_ID) { + this.renderExpression(tree, element, templateData); + } else if (templateId === ReplExpressionsRenderer.VALUE_OUTPUT_TEMPLATE_ID) { + this.renderOutputValue(element, templateData); + } else if (templateId === ReplExpressionsRenderer.NAME_VALUE_OUTPUT_TEMPLATE_ID) { + this.renderOutputNameValue(tree, element, templateData); + } + } + + private renderExpression(tree: ITree, expression: IExpression, templateData: IExpressionTemplateData): void { + templateData.input.textContent = expression.name; + renderExpressionValue(expression, templateData.value, { + preserveWhitespace: !expression.hasChildren, + showHover: false + }); + if (expression.hasChildren) { + templateData.annotation.className = 'annotation octicon octicon-info'; + templateData.annotation.title = nls.localize('stateCapture', "Object state is captured from first evaluation"); + } + } + + private renderOutputValue(output: OutputElement, templateData: IValueOutputTemplateData): void { + + // value + dom.clearNode(templateData.value); + templateData.value.className = ''; + let result = this.handleANSIOutput(output.value); + if (typeof result === 'string') { + renderExpressionValue(result, templateData.value, { + preserveWhitespace: true, + showHover: false + }); + } else { + templateData.value.appendChild(result); + } + + dom.addClass(templateData.value, (output.severity === severity.Warning) ? 'warn' : (output.severity === severity.Error) ? 'error' : 'info'); + } + + private renderOutputNameValue(tree: ITree, output: OutputNameValueElement, templateData: IKeyValueOutputTemplateData): void { + // key + if (output.name) { + templateData.name.textContent = `${output.name}:`; + } else { + templateData.name.textContent = ''; + } + + // value + renderExpressionValue(output.value, templateData.value, { + preserveWhitespace: true, + showHover: false + }); + + // annotation if any + if (output.annotation) { + templateData.annotation.className = 'annotation octicon octicon-info'; + templateData.annotation.title = output.annotation; + } else { + templateData.annotation.className = ''; + templateData.annotation.title = ''; + } + } + + private handleANSIOutput(text: string): HTMLElement | string { + let tokensContainer: HTMLSpanElement; + let currentToken: HTMLSpanElement; + let buffer: string = ''; + + for (let i = 0, len = text.length; i < len; i++) { + + // start of ANSI escape sequence (see http://ascii-table.com/ansi-escape-sequences.php) + if (text.charCodeAt(i) === 27) { + let index = i; + let chr = (++index < len ? text.charAt(index) : null); + if (chr && chr === '[') { + let code: string = null; + chr = (++index < len ? text.charAt(index) : null); + + if (chr && chr >= '0' && chr <= '9') { + code = chr; + chr = (++index < len ? text.charAt(index) : null); + } + + if (chr && chr >= '0' && chr <= '9') { + code += chr; + chr = (++index < len ? text.charAt(index) : null); + } + + if (code === null) { + code = '0'; + } + + if (chr === 'm') { // set text color/mode. + + // only respect text-foreground ranges and ignore the values for "black" & "white" because those + // only make sense in combination with text-background ranges which we currently not support + let parsedMode = parseInt(code, 10); + let token = document.createElement('span'); + if ((parsedMode >= 30 && parsedMode <= 37) || (parsedMode >= 90 && parsedMode <= 97)) { + token.className = 'code' + parsedMode; + } else if (parsedMode === 1) { + token.className = 'code-bold'; + } + + // we need a tokens container now + if (!tokensContainer) { + tokensContainer = document.createElement('span'); + } + + // flush text buffer if we have any + if (buffer) { + this.insert(this.linkDetector.handleLinks(buffer), currentToken || tokensContainer); + buffer = ''; + } + + currentToken = token; + tokensContainer.appendChild(token); + + i = index; + } + } + } + + // normal text + else { + buffer += text[i]; + } + } + + // flush remaining text buffer if we have any + if (buffer) { + let res = this.linkDetector.handleLinks(buffer); + if (typeof res !== 'string' || currentToken) { + if (!tokensContainer) { + tokensContainer = document.createElement('span'); + } + + this.insert(res, currentToken || tokensContainer); + } + } + + return tokensContainer || buffer; + } + + private insert(arg: HTMLElement | string, target: HTMLElement): void { + if (typeof arg === 'string') { + target.textContent = arg; + } else { + target.appendChild(arg); + } + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + // noop + } +} + +export class ReplExpressionsAccessibilityProvider implements IAccessibilityProvider { + + public getAriaLabel(tree: ITree, element: any): string { + if (element instanceof Variable) { + return nls.localize('replVariableAriaLabel', "Variable {0} has value {1}, read eval print loop, debug", (element).name, (element).value); + } + if (element instanceof Expression) { + return nls.localize('replExpressionAriaLabel', "Expression {0} has value {1}, read eval print loop, debug", (element).name, (element).value); + } + if (element instanceof OutputElement) { + return nls.localize('replValueOutputAriaLabel', "{0}, read eval print loop, debug", (element).value); + } + if (element instanceof OutputNameValueElement) { + return nls.localize('replKeyValueOutputAriaLabel', "Output variable {0} has value {1}, read eval print loop, debug", (element).name, (element).value); + } + + return null; + } +} + +export class ReplExpressionsActionProvider implements IActionProvider { + + constructor(private instantiationService: IInstantiationService) { + // noop + } + + public hasActions(tree: ITree, element: any): boolean { + return false; + } + + public getActions(tree: ITree, element: any): TPromise { + return TPromise.as([]); + } + + public hasSecondaryActions(tree: ITree, element: any): boolean { + return true; + } + + public getSecondaryActions(tree: ITree, element: any): TPromise { + const actions: IAction[] = []; + actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL)); + actions.push(new CopyAllAction(CopyAllAction.ID, CopyAllAction.LABEL, tree)); + actions.push(this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL)); + + return TPromise.as(actions); + } + + public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { + return null; + } +} + +export class ReplExpressionsController extends BaseDebugController { + + private lastSelectedString: string = null; + public toFocusOnClick: { focus(): void }; + + protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean { + const mouseEvent = eventish; + // input and output are one element in the tree => we only expand if the user clicked on the output. + if ((element.reference > 0 || (element instanceof OutputNameValueElement && element.hasChildren)) && mouseEvent.target.className.indexOf('input expression') === -1) { + super.onLeftClick(tree, element, eventish, origin); + tree.clearFocus(); + tree.deselect(element); + } + + const selection = window.getSelection(); + if (selection.type !== 'Range' || this.lastSelectedString === selection.toString()) { + // only focus the input if the user is not currently selecting. + this.toFocusOnClick.focus(); + } + this.lastSelectedString = selection.toString(); + + return true; + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts b/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts new file mode 100644 index 0000000000..b18837b525 --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { localize } from 'vs/nls'; +import { registerColor, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; +import { IDebugService, State } from 'vs/workbench/parts/debug/common/debug'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_BACKGROUND, Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_BORDER } from 'vs/workbench/common/theme'; +import { addClass, removeClass } from 'vs/base/browser/dom'; + +// colors for theming + +export const STATUS_BAR_DEBUGGING_BACKGROUND = registerColor('statusBar.debuggingBackground', { + dark: '#CC6633', + light: '#CC6633', + hc: '#CC6633' +}, localize('statusBarDebuggingBackground', "Status bar background color when a program is being debugged. The status bar is shown in the bottom of the window")); + +export const STATUS_BAR_DEBUGGING_FOREGROUND = registerColor('statusBar.debuggingForeground', { + dark: STATUS_BAR_FOREGROUND, + light: STATUS_BAR_FOREGROUND, + hc: STATUS_BAR_FOREGROUND +}, localize('statusBarDebuggingForeground', "Status bar foreground color when a program is being debugged. The status bar is shown in the bottom of the window")); + +export const STATUS_BAR_DEBUGGING_BORDER = registerColor('statusBar.debuggingBorder', { + dark: STATUS_BAR_BORDER, + light: STATUS_BAR_BORDER, + hc: STATUS_BAR_BORDER +}, localize('statusBarDebuggingBorder', "Status bar border color separating to the sidebar and editor when a program is being debugged. The status bar is shown in the bottom of the window")); + +export class StatusBarColorProvider extends Themable implements IWorkbenchContribution { + private static ID = 'debug.statusbarColorProvider'; + + constructor( + @IThemeService themeService: IThemeService, + @IDebugService private debugService: IDebugService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IPartService private partService: IPartService + ) { + super(themeService); + + this.registerListeners(); + } + + private registerListeners(): void { + this.toUnbind.push(this.debugService.onDidChangeState(state => this.updateStyles())); + this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(state => this.updateStyles())); + } + + protected updateStyles(): void { + super.updateStyles(); + + const container = this.partService.getContainer(Parts.STATUSBAR_PART); + if (this.isDebugging()) { + addClass(container, 'debugging'); + } else { + removeClass(container, 'debugging'); + } + + container.style.backgroundColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_DEBUGGING_BACKGROUND, STATUS_BAR_BACKGROUND)); + container.style.color = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_DEBUGGING_FOREGROUND, STATUS_BAR_FOREGROUND)); + + const borderColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_DEBUGGING_BORDER, STATUS_BAR_BORDER)) || this.getColor(contrastBorder); + container.style.borderTopWidth = borderColor ? '1px' : null; + container.style.borderTopStyle = borderColor ? 'solid' : null; + container.style.borderTopColor = borderColor; + } + + private getColorKey(noFolderColor: string, debuggingColor: string, normalColor: string): string { + + // Not debugging + if (!this.isDebugging()) { + if (this.contextService.hasWorkspace()) { + return normalColor; + } + + return noFolderColor; + } + + // Debugging + return debuggingColor; + } + + private isDebugging(): boolean { + if (this.debugService.state === State.Inactive) { + return false; + } + + if (this.isRunningWithoutDebug()) { + return false; + } + + return true; + } + + private isRunningWithoutDebug(): boolean { + const process = this.debugService.getViewModel().focusedProcess; + + return process && process.configuration && process.configuration.noDebug; + } + + public getId(): string { + return StatusBarColorProvider.ID; + } +} + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const statusBarItemDebuggingForeground = theme.getColor(STATUS_BAR_DEBUGGING_FOREGROUND); + if (statusBarItemDebuggingForeground) { + collector.addRule(`.monaco-workbench > .part.statusbar.debugging > .statusbar-item .mask-icon { background-color: ${statusBarItemDebuggingForeground} !important; }`); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts new file mode 100644 index 0000000000..1ec9a371a7 --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as platform from 'vs/base/common/platform'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ITerminalService, ITerminalInstance, ITerminalConfiguration } from 'vs/workbench/parts/terminal/common/terminal'; +import { ITerminalService as IExternalTerminalService } from 'vs/workbench/parts/execution/common/execution'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +const enum ShellType { cmd, powershell, bash }; + +export class TerminalSupport { + + private static integratedTerminalInstance: ITerminalInstance; + private static terminalDisposedListener: IDisposable; + + public static runInTerminal(terminalService: ITerminalService, nativeTerminalService: IExternalTerminalService, configurationService: IConfigurationService, args: DebugProtocol.RunInTerminalRequestArguments, response: DebugProtocol.RunInTerminalResponse): TPromise { + + if (args.kind === 'external') { + return nativeTerminalService.runInTerminal(args.title, args.cwd, args.args, args.env || {}); + } + + let delay = 0; + if (!TerminalSupport.integratedTerminalInstance) { + TerminalSupport.integratedTerminalInstance = terminalService.createInstance({ name: args.title || nls.localize('debug.terminal.title', "debuggee") }); + delay = 2000; // delay the first sendText so that the newly created terminal is ready. + } + if (!TerminalSupport.terminalDisposedListener) { + // React on terminal disposed and check if that is the debug terminal #12956 + TerminalSupport.terminalDisposedListener = terminalService.onInstanceDisposed(terminal => { + if (TerminalSupport.integratedTerminalInstance && TerminalSupport.integratedTerminalInstance.id === terminal.id) { + TerminalSupport.integratedTerminalInstance = null; + } + }); + } + terminalService.setActiveInstance(TerminalSupport.integratedTerminalInstance); + terminalService.showPanel(true); + + return new TPromise((c, e) => { + + setTimeout(() => { + if (TerminalSupport.integratedTerminalInstance) { + const command = this.prepareCommand(args, configurationService); + TerminalSupport.integratedTerminalInstance.sendText(command, true); + c(void 0); + } else { + e(new Error(nls.localize('debug.terminal.not.available.error', "Integrated terminal not available"))); + } + }, delay); + + }); + } + + private static prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments, configurationService: IConfigurationService): string { + + let shellType: ShellType; + + // get the shell configuration for the current platform + let shell: string; + const shell_config = (configurationService.getConfiguration().terminal.integrated).shell; + if (platform.isWindows) { + shell = shell_config.windows; + shellType = ShellType.cmd; + } else if (platform.isLinux) { + shell = shell_config.linux; + shellType = ShellType.bash; + } else if (platform.isMacintosh) { + shell = shell_config.osx; + shellType = ShellType.bash; + } + + // try to determine the shell type + shell = shell.trim().toLowerCase(); + if (shell.indexOf('powershell') >= 0) { + shellType = ShellType.powershell; + } else if (shell.indexOf('cmd.exe') >= 0) { + shellType = ShellType.cmd; + } else if (shell.indexOf('bash') >= 0) { + shellType = ShellType.bash; + } else if (shell.indexOf('git\\bin\\bash.exe') >= 0) { + shellType = ShellType.bash; + } + + let quote: (s: string) => string; + let command = ''; + + switch (shellType) { + + case ShellType.powershell: + + quote = (s: string) => { + s = s.replace(/\'/g, '\'\''); + return s.indexOf(' ') >= 0 || s.indexOf('\'') >= 0 || s.indexOf('"') >= 0 ? `'${s}'` : s; + }; + + if (args.cwd) { + command += `cd '${args.cwd}'; `; + } + if (args.env) { + for (let key in args.env) { + command += `$env:${key}='${args.env[key]}'; `; + } + } + if (args.args && args.args.length > 0) { + const cmd = quote(args.args.shift()); + command += (cmd[0] === '\'') ? `& ${cmd} ` : `${cmd} `; + for (let a of args.args) { + command += `${quote(a)} `; + } + } + break; + + case ShellType.cmd: + + quote = (s: string) => { + s = s.replace(/\"/g, '""'); + return (s.indexOf(' ') >= 0 || s.indexOf('"') >= 0) ? `"${s}"` : s; + }; + + if (args.cwd) { + command += `cd ${quote(args.cwd)} && `; + } + if (args.env) { + command += 'cmd /C "'; + for (let key in args.env) { + command += `set "${key}=${args.env[key]}" && `; + } + } + for (let a of args.args) { + command += `${quote(a)} `; + } + if (args.env) { + command += '"'; + } + break; + + case ShellType.bash: + + quote = (s: string) => { + s = s.replace(/\"/g, '\\"'); + return (s.indexOf(' ') >= 0 || s.indexOf('\\') >= 0) ? `"${s}"` : s; + }; + + if (args.cwd) { + command += `cd ${quote(args.cwd)} ; `; + } + if (args.env) { + command += 'env'; + for (let key in args.env) { + command += ` "${key}=${args.env[key]}"`; + } + command += ' '; + } + for (let a of args.args) { + command += `${quote(a)} `; + } + break; + } + + return command; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts new file mode 100644 index 0000000000..830ab3631c --- /dev/null +++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts @@ -0,0 +1,284 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import fs = require('fs'); +import path = require('path'); +import { parse } from 'vs/base/common/json'; +import * as nls from 'vs/nls'; +import uri from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as strings from 'vs/base/common/strings'; +import * as objects from 'vs/base/common/objects'; +import * as paths from 'vs/base/common/paths'; +import * as platform from 'vs/base/common/platform'; +import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; +import { IConfig, IRawAdapter, IAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA } from 'vs/workbench/parts/debug/common/debug'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ICommandService } from 'vs/platform/commands/common/commands'; + +export class Adapter { + + constructor(private rawAdapter: IRawAdapter, public extensionDescription: IExtensionDescription, + @IConfigurationResolverService private configurationResolverService: IConfigurationResolverService, + @IConfigurationService private configurationService: IConfigurationService, + @ICommandService private commandService: ICommandService + ) { + if (rawAdapter.windows) { + rawAdapter.win = rawAdapter.windows; + } + } + + public hasConfigurationProvider = false; + + public getAdapterExecutable(root: uri, verifyAgainstFS = true): TPromise { + + if (this.rawAdapter.adapterExecutableCommand) { + return this.commandService.executeCommand(this.rawAdapter.adapterExecutableCommand, root.toString()).then(ad => { + return this.verifyAdapterDetails(ad, verifyAgainstFS); + }); + } + + const adapterExecutable = { + command: this.getProgram(), + args: this.getAttributeBasedOnPlatform('args') + }; + const runtime = this.getRuntime(); + if (runtime) { + const runtimeArgs = this.getAttributeBasedOnPlatform('runtimeArgs'); + adapterExecutable.args = (runtimeArgs || []).concat([adapterExecutable.command]).concat(adapterExecutable.args || []); + adapterExecutable.command = runtime; + } + return this.verifyAdapterDetails(adapterExecutable, verifyAgainstFS); + } + + private verifyAdapterDetails(details: IAdapterExecutable, verifyAgainstFS: boolean): TPromise { + + if (details.command) { + if (verifyAgainstFS) { + if (path.isAbsolute(details.command)) { + return new TPromise((c, e) => { + fs.exists(details.command, exists => { + if (exists) { + c(details); + } else { + e(new Error(nls.localize('debugAdapterBinNotFound', "Debug adapter executable '{0}' does not exist.", details.command))); + } + }); + }); + } else { + // relative path + if (details.command.indexOf('/') < 0 && details.command.indexOf('\\') < 0) { + // no separators: command looks like a runtime name like 'node' or 'mono' + return TPromise.as(details); // TODO: check that the runtime is available on PATH + } + } + } else { + return TPromise.as(details); + } + } + + return TPromise.wrapError(new Error(nls.localize({ key: 'debugAdapterCannotDetermineExecutable', comment: ['Adapter executable file not found'] }, + "Cannot determine executable for debug adapter '{0}'.", details.command))); + } + + private getRuntime(): string { + let runtime = this.getAttributeBasedOnPlatform('runtime'); + if (runtime && runtime.indexOf('./') === 0) { + runtime = paths.join(this.extensionDescription.extensionFolderPath, runtime); + } + return runtime; + } + + private getProgram(): string { + let program = this.getAttributeBasedOnPlatform('program'); + if (program) { + program = paths.join(this.extensionDescription.extensionFolderPath, program); + } + return program; + } + + public get aiKey(): string { + return this.rawAdapter.aiKey; + } + + public get label(): string { + return this.rawAdapter.label || this.rawAdapter.type; + } + + public get type(): string { + return this.rawAdapter.type; + } + + public get variables(): { [key: string]: string } { + return this.rawAdapter.variables; + } + + public get configurationSnippets(): IJSONSchemaSnippet[] { + return this.rawAdapter.configurationSnippets; + } + + public get languages(): string[] { + return this.rawAdapter.languages; + } + + public get startSessionCommand(): string { + return this.rawAdapter.startSessionCommand; + } + + public merge(secondRawAdapter: IRawAdapter, extensionDescription: IExtensionDescription): void { + // Give priority to built in debug adapters + if (extensionDescription.isBuiltin) { + this.extensionDescription = extensionDescription; + } + objects.mixin(this.rawAdapter, secondRawAdapter, extensionDescription.isBuiltin); + } + + public hasInitialConfiguration(): boolean { + return !!this.rawAdapter.initialConfigurations; + } + + public getInitialConfigurationContent(folderUri: uri, initialConfigs?: IConfig[]): TPromise { + const editorConfig = this.configurationService.getConfiguration(); + + // deprecated code: use DebugConfigurationProvider instead of command + if (typeof this.rawAdapter.initialConfigurations === 'string') { + // Contributed initialConfigurations is a command that needs to be invoked + // Debug adapter will dynamically provide the full launch.json + // TODO@Isidor stop supporting initialConfigurations + return this.commandService.executeCommand(this.rawAdapter.initialConfigurations, folderUri).then(content => { + // Debug adapter returned the full content of the launch.json - return it after format + try { + const config = parse(content); + config.configurations.push(...initialConfigs); + content = JSON.stringify(config, null, '\t').split('\n').map(line => '\t' + line).join('\n').trim(); + } catch (e) { + // noop + } + + if (editorConfig.editor && editorConfig.editor.insertSpaces) { + content = content.replace(new RegExp('\t', 'g'), strings.repeat(' ', editorConfig.editor.tabSize)); + } + return content; + }); + } + // end of deprecation + + // at this point we got some configs from the package.json and/or from registered DebugConfigurationProviders + let initialConfigurations = this.rawAdapter.initialConfigurations || []; + if (initialConfigs) { + initialConfigurations = initialConfigurations.concat(initialConfigs); + } + + const configs = JSON.stringify(initialConfigurations, null, '\t').split('\n').map(line => '\t' + line).join('\n').trim(); + + const comment1 = nls.localize('launch.config.comment1', "Use IntelliSense to learn about possible attributes."); + const comment2 = nls.localize('launch.config.comment2', "Hover to view descriptions of existing attributes."); + const comment3 = nls.localize('launch.config.comment3', "For more information, visit: {0}", 'https://go.microsoft.com/fwlink/?linkid=830387'); + + let content = [ + '{', + `\t// ${comment1}`, + `\t// ${comment2}`, + `\t// ${comment3}`, + `\t"version": "0.2.0",`, + `\t"configurations": ${configs}`, + '}' + ].join('\n'); + + // fix formatting + if (editorConfig.editor && editorConfig.editor.insertSpaces) { + content = content.replace(new RegExp('\t', 'g'), strings.repeat(' ', editorConfig.editor.tabSize)); + } + + return TPromise.as(content); + }; + + public getSchemaAttributes(): IJSONSchema[] { + if (!this.rawAdapter.configurationAttributes) { + return null; + } + // fill in the default configuration attributes shared by all adapters. + return Object.keys(this.rawAdapter.configurationAttributes).map(request => { + const attributes: IJSONSchema = this.rawAdapter.configurationAttributes[request]; + const defaultRequired = ['name', 'type', 'request']; + attributes.required = attributes.required && attributes.required.length ? defaultRequired.concat(attributes.required) : defaultRequired; + attributes.additionalProperties = false; + attributes.type = 'object'; + if (!attributes.properties) { + attributes.properties = {}; + } + const properties = attributes.properties; + properties['type'] = { + enum: [this.type], + description: nls.localize('debugType', "Type of configuration."), + pattern: '^(?!node2)', + errorMessage: nls.localize('debugTypeNotRecognised', "The debug type is not recognized. Make sure that you have a corresponding debug extension installed and that it is enabled."), + patternErrorMessage: nls.localize('node2NotSupported', "\"node2\" is no longer supported, use \"node\" instead and set the \"protocol\" attribute to \"inspector\".") + }; + properties['name'] = { + type: 'string', + description: nls.localize('debugName', "Name of configuration; appears in the launch configuration drop down menu."), + default: 'Launch' + }; + properties['request'] = { + enum: [request], + description: nls.localize('debugRequest', "Request type of configuration. Can be \"launch\" or \"attach\"."), + }; + properties['debugServer'] = { + type: 'number', + description: nls.localize('debugServer', "For debug extension development only: if a port is specified VS Code tries to connect to a debug adapter running in server mode"), + default: 4711 + }; + properties['preLaunchTask'] = { + type: ['string', 'null'], + default: null, + description: nls.localize('debugPrelaunchTask', "Task to run before debug session starts.") + }; + properties['internalConsoleOptions'] = INTERNAL_CONSOLE_OPTIONS_SCHEMA; + + const osProperties = objects.deepClone(properties); + properties['windows'] = { + type: 'object', + description: nls.localize('debugWindowsConfiguration', "Windows specific launch configuration attributes."), + properties: osProperties + }; + properties['osx'] = { + type: 'object', + description: nls.localize('debugOSXConfiguration', "OS X specific launch configuration attributes."), + properties: osProperties + }; + properties['linux'] = { + type: 'object', + description: nls.localize('debugLinuxConfiguration', "Linux specific launch configuration attributes."), + properties: osProperties + }; + Object.keys(attributes.properties).forEach(name => { + // Use schema allOf property to get independent error reporting #21113 + attributes.properties[name].pattern = attributes.properties[name].pattern || '^(?!.*\\$\\{(env|config|command)\\.)'; + attributes.properties[name].patternErrorMessage = attributes.properties[name].patternErrorMessage || + nls.localize('deprecatedVariables', "'env.', 'config.' and 'command.' are deprecated, use 'env:', 'config:' and 'command:' instead."); + }); + + return attributes; + }); + } + + private getAttributeBasedOnPlatform(key: string): any { + let result: any; + if (platform.isWindows && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') && this.rawAdapter.winx86) { + result = this.rawAdapter.winx86[key]; + } else if (platform.isWindows && this.rawAdapter.win) { + result = this.rawAdapter.win[key]; + } else if (platform.isMacintosh && this.rawAdapter.osx) { + result = this.rawAdapter.osx[key]; + } else if (platform.isLinux && this.rawAdapter.linux) { + result = this.rawAdapter.linux[key]; + } + + return result || this.rawAdapter[key]; + } +} diff --git a/src/vs/workbench/parts/debug/node/telemetryApp.ts b/src/vs/workbench/parts/debug/node/telemetryApp.ts new file mode 100644 index 0000000000..2fc268a977 --- /dev/null +++ b/src/vs/workbench/parts/debug/node/telemetryApp.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; +import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; +import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc'; + +const appender = new AppInsightsAppender(process.argv[2], JSON.parse(process.argv[3]), process.argv[4]); +process.once('exit', () => appender.dispose()); + +const channel = new TelemetryAppenderChannel(appender); +const server = new Server(); +server.registerChannel('telemetryAppender', channel); diff --git a/src/vs/workbench/parts/debug/node/v8Protocol.ts b/src/vs/workbench/parts/debug/node/v8Protocol.ts new file mode 100644 index 0000000000..e28600c5b1 --- /dev/null +++ b/src/vs/workbench/parts/debug/node/v8Protocol.ts @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import stream = require('stream'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { canceled } from 'vs/base/common/errors'; + +export abstract class V8Protocol { + + private static TWO_CRLF = '\r\n\r\n'; + + private outputStream: stream.Writable; + private sequence: number; + private pendingRequests: Map void>; + private rawData: Buffer; + private contentLength: number; + + constructor(private id: string) { + this.sequence = 1; + this.contentLength = -1; + this.pendingRequests = new Map void>(); + this.rawData = new Buffer(0); + } + + public getId(): string { + return this.id; + } + + protected abstract onServerError(err: Error): void; + protected abstract onEvent(event: DebugProtocol.Event): void; + protected abstract dispatchRequest(request: DebugProtocol.Request, response: DebugProtocol.Response); + + protected connect(readable: stream.Readable, writable: stream.Writable): void { + + this.outputStream = writable; + + readable.on('data', (data: Buffer) => { + this.rawData = Buffer.concat([this.rawData, data]); + this.handleData(); + }); + } + + protected send(command: string, args: any): TPromise { + let errorCallback; + return new TPromise((completeDispatch, errorDispatch) => { + errorCallback = errorDispatch; + this.doSend(command, args, (result: R) => { + if (result.success) { + completeDispatch(result); + } else { + errorDispatch(result); + } + }); + }, () => errorCallback(canceled())); + } + + public sendResponse(response: DebugProtocol.Response): void { + if (response.seq > 0) { + console.error(`attempt to send more than one response for command ${response.command}`); + } else { + this.sendMessage('response', response); + } + } + + private doSend(command: string, args: any, clb: (result: DebugProtocol.Response) => void): void { + + const request: any = { + command: command + }; + if (args && Object.keys(args).length > 0) { + request.arguments = args; + } + + this.sendMessage('request', request); + + if (clb) { + // store callback for this request + this.pendingRequests.set(request.seq, clb); + } + } + + private sendMessage(typ: 'request' | 'response' | 'event', message: DebugProtocol.ProtocolMessage): void { + + message.type = typ; + message.seq = this.sequence++; + + const json = JSON.stringify(message); + const length = Buffer.byteLength(json, 'utf8'); + + this.outputStream.write('Content-Length: ' + length.toString() + V8Protocol.TWO_CRLF, 'utf8'); + this.outputStream.write(json, 'utf8'); + } + + private handleData(): void { + while (true) { + if (this.contentLength >= 0) { + if (this.rawData.length >= this.contentLength) { + const message = this.rawData.toString('utf8', 0, this.contentLength); + this.rawData = this.rawData.slice(this.contentLength); + this.contentLength = -1; + if (message.length > 0) { + this.dispatch(message); + } + continue; // there may be more complete messages to process + } + } else { + const s = this.rawData.toString('utf8', 0, this.rawData.length); + const idx = s.indexOf(V8Protocol.TWO_CRLF); + if (idx !== -1) { + const match = /Content-Length: (\d+)/.exec(s); + if (match && match[1]) { + this.contentLength = Number(match[1]); + this.rawData = this.rawData.slice(idx + V8Protocol.TWO_CRLF.length); + continue; // try to handle a complete message + } + } + } + break; + } + } + + private dispatch(body: string): void { + try { + const rawData = JSON.parse(body); + switch (rawData.type) { + case 'event': + this.onEvent(rawData); + break; + case 'response': + const response = rawData; + const clb = this.pendingRequests.get(response.request_seq); + if (clb) { + this.pendingRequests.delete(response.request_seq); + clb(response); + } + break; + case 'request': + const request = rawData; + const resp: DebugProtocol.Response = { + type: 'response', + seq: 0, + command: request.command, + request_seq: request.seq, + success: true + }; + this.dispatchRequest(request, resp); + break; + } + } catch (e) { + this.onServerError(new Error(e.message || e)); + } + } +} diff --git a/src/vs/workbench/parts/debug/test/common/debugSource.test.ts b/src/vs/workbench/parts/debug/test/common/debugSource.test.ts new file mode 100644 index 0000000000..b0618ee1cb --- /dev/null +++ b/src/vs/workbench/parts/debug/test/common/debugSource.test.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 * as assert from 'assert'; +import uri from 'vs/base/common/uri'; +import { Source } from 'vs/workbench/parts/debug/common/debugSource'; + +suite('Debug - Source', () => { + + test('from raw source', () => { + const source = new Source({ + name: 'zz', + path: '/xx/yy/zz', + sourceReference: 0, + presentationHint: 'emphasize' + }); + + assert.equal(source.presentationHint, 'emphasize'); + assert.equal(source.name, 'zz'); + assert.equal(source.inMemory, false); + assert.equal(source.reference, 0); + assert.equal(source.uri.toString(), uri.file('/xx/yy/zz').toString()); + }); + + test('from raw internal source', () => { + const source = new Source({ + name: 'internalModule.js', + sourceReference: 11, + presentationHint: 'deemphasize' + }); + + assert.equal(source.presentationHint, 'deemphasize'); + assert.equal(source.name, 'internalModule.js'); + assert.equal(source.inMemory, true); + assert.equal(source.reference, 11); + }); +}); diff --git a/src/vs/workbench/parts/debug/test/common/debugUtils.test.ts b/src/vs/workbench/parts/debug/test/common/debugUtils.test.ts new file mode 100644 index 0000000000..fa15e687ce --- /dev/null +++ b/src/vs/workbench/parts/debug/test/common/debugUtils.test.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import debug = require('vs/workbench/parts/debug/common/debug'); +import assert = require('assert'); + +suite('Debug - Utils', () => { + test('formatPII', () => { + assert.strictEqual(debug.formatPII('Foo Bar', false, {}), 'Foo Bar'); + assert.strictEqual(debug.formatPII('Foo {key} Bar', false, {}), 'Foo {key} Bar'); + assert.strictEqual(debug.formatPII('Foo {key} Bar', false, { 'key': 'yes' }), 'Foo yes Bar'); + assert.strictEqual(debug.formatPII('Foo {_0} Bar {_0}', true, { '_0': 'yes' }), 'Foo yes Bar yes'); + assert.strictEqual(debug.formatPII('Foo {0} Bar {1}{2}', false, { '0': 'yes' }), 'Foo yes Bar {1}{2}'); + assert.strictEqual(debug.formatPII('Foo {0} Bar {1}{2}', false, { '0': 'yes', '1': 'undefined' }), 'Foo yes Bar undefined{2}'); + assert.strictEqual(debug.formatPII('Foo {_key0} Bar {key1}{key2}', true, { '_key0': 'yes', 'key1': '5', 'key2': 'false' }), 'Foo yes Bar {key1}{key2}'); + assert.strictEqual(debug.formatPII('Foo {_key0} Bar {key1}{key2}', false, { '_key0': 'yes', 'key1': '5', 'key2': 'false' }), 'Foo yes Bar 5false'); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts b/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts new file mode 100644 index 0000000000..7452fc064f --- /dev/null +++ b/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel'; +import { StackFrame, Expression, Thread, Process } from 'vs/workbench/parts/debug/common/debugModel'; +import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug'; + +suite('Debug - View Model', () => { + let model: ViewModel; + + setup(() => { + model = new ViewModel(); + }); + + teardown(() => { + model = null; + }); + + test('focused stack frame', () => { + assert.equal(model.focusedStackFrame, null); + assert.equal(model.focusedThread, null); + const mockSession = new MockSession(); + const process = new Process({ name: 'mockProcess', type: 'node', request: 'launch' }, mockSession); + const thread = new Thread(process, 'myThread', 1); + const frame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startColumn: 1, startLineNumber: 1, endColumn: undefined, endLineNumber: undefined }, 0); + model.setFocusedStackFrame(frame, process, false); + + assert.equal(model.focusedStackFrame.getId(), frame.getId()); + assert.equal(model.focusedThread.threadId, 1); + assert.equal(model.focusedProcess.getId(), process.getId()); + }); + + test('selected expression', () => { + assert.equal(model.getSelectedExpression(), null); + const expression = new Expression('my expression'); + model.setSelectedExpression(expression); + + assert.equal(model.getSelectedExpression(), expression); + }); + + test('multi process view and changed workbench state', () => { + assert.equal(model.changedWorkbenchViewState, false); + assert.equal(model.isMultiProcessView(), false); + model.setMultiProcessView(true); + assert.equal(model.isMultiProcessView(), true); + }); +}); diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts new file mode 100644 index 0000000000..d5ed4bd452 --- /dev/null +++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts @@ -0,0 +1,253 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import uri from 'vs/base/common/uri'; +import Event, { Emitter } from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as debug from 'vs/workbench/parts/debug/common/debug'; + +export class MockDebugService implements debug.IDebugService { + public _serviceBrand: any; + + public get state(): debug.State { + return null; + } + + public get onDidCustomEvent(): Event { + return null; + } + + public get onDidNewProcess(): Event { + return null; + } + + public get onDidEndProcess(): Event { + return null; + } + + public get onDidChangeState(): Event { + return null; + } + + public getConfigurationManager(): debug.IConfigurationManager { + return null; + } + + public focusStackFrameAndEvaluate(focusedStackFrame: debug.IStackFrame): TPromise { + return TPromise.as(null); + } + + public addBreakpoints(uri: uri, rawBreakpoints: debug.IRawBreakpoint[]): TPromise { + return TPromise.as(null); + } + + public enableOrDisableBreakpoints(enabled: boolean): TPromise { + return TPromise.as(null); + } + + public setBreakpointsActivated(): TPromise { + return TPromise.as(null); + } + + public removeBreakpoints(): TPromise { + return TPromise.as(null); + } + + public addFunctionBreakpoint(): void { } + + public moveWatchExpression(id: string, position: number): void { } + + public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise { + return TPromise.as(null); + } + + public removeFunctionBreakpoints(id?: string): TPromise { + return TPromise.as(null); + } + + public addReplExpression(name: string): TPromise { + return TPromise.as(null); + } + + public removeReplExpressions(): void { } + + public addWatchExpression(name?: string): TPromise { + return TPromise.as(null); + } + + public renameWatchExpression(id: string, newName: string): TPromise { + return TPromise.as(null); + } + + public removeWatchExpressions(id?: string): void { } + + public startDebugging(root: uri, configOrName?: debug.IConfig | string, noDebug?: boolean): TPromise { + return TPromise.as(null); + } + + public createProcess(root: uri, config: debug.IConfig): TPromise { + return TPromise.as(null); + } + + public findProcessByUUID(uuid: string): debug.IProcess | null { + return null; + } + + public restartProcess(): TPromise { + return TPromise.as(null); + } + + public stopProcess(): TPromise { + return TPromise.as(null); + } + + public getModel(): debug.IModel { + return null; + } + + public getViewModel(): debug.IViewModel { + return null; + } + + public logToRepl(value: string): void { } + + public sourceIsNotAvailable(uri: uri): void { } +} + +export class MockSession implements debug.ISession { + public readyForBreakpoints = true; + public emittedStopped = true; + + public getId() { + return 'mockrawsession'; + } + + public root: uri; + + public getLengthInSeconds(): number { + return 100; + } + + public stackTrace(args: DebugProtocol.StackTraceArguments): TPromise { + return TPromise.as({ + seq: 1, + type: 'response', + request_seq: 1, + success: true, + command: 'stackTrace', + body: { + stackFrames: [{ + id: 1, + name: 'mock', + line: 5, + column: 6 + }] + } + }); + } + + public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise { + return TPromise.as(null); + } + + public attach(args: DebugProtocol.AttachRequestArguments): TPromise { + return TPromise.as(null); + } + + public scopes(args: DebugProtocol.ScopesArguments): TPromise { + return TPromise.as(null); + } + + public variables(args: DebugProtocol.VariablesArguments): TPromise { + return TPromise.as(null); + } + + evaluate(args: DebugProtocol.EvaluateArguments): TPromise { + return TPromise.as(null); + } + + public get capabilities(): DebugProtocol.Capabilities { + return {}; + } + + public get onDidEvent(): Event { + return null; + } + + public get onDidInitialize(): Event { + const emitter = new Emitter(); + return emitter.event;; + } + + public custom(request: string, args: any): TPromise { + return TPromise.as(null); + } + + public disconnect(restart?: boolean, force?: boolean): TPromise { + return TPromise.as(null); + } + + public threads(): TPromise { + return TPromise.as(null); + } + + public stepIn(args: DebugProtocol.StepInArguments): TPromise { + return TPromise.as(null); + } + + public stepOut(args: DebugProtocol.StepOutArguments): TPromise { + return TPromise.as(null); + } + + public stepBack(args: DebugProtocol.StepBackArguments): TPromise { + return TPromise.as(null); + } + + public continue(args: DebugProtocol.ContinueArguments): TPromise { + return TPromise.as(null); + } + + public reverseContinue(args: DebugProtocol.ReverseContinueArguments): TPromise { + return TPromise.as(null); + } + + public pause(args: DebugProtocol.PauseArguments): TPromise { + return TPromise.as(null); + } + + public setVariable(args: DebugProtocol.SetVariableArguments): TPromise { + return TPromise.as(null); + } + + public restartFrame(args: DebugProtocol.RestartFrameArguments): TPromise { + return TPromise.as(null); + } + + public completions(args: DebugProtocol.CompletionsArguments): TPromise { + return TPromise.as(null); + } + + public next(args: DebugProtocol.NextArguments): TPromise { + return TPromise.as(null); + } + + public source(args: DebugProtocol.SourceArguments): TPromise { + return TPromise.as(null); + } + + public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): TPromise { + return TPromise.as(null); + } + + public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): TPromise { + return TPromise.as(null); + } + + public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): TPromise { + return TPromise.as(null); + } + + public onDidStop: Event = null; +} diff --git a/src/vs/workbench/parts/debug/test/common/replHistory.test.ts b/src/vs/workbench/parts/debug/test/common/replHistory.test.ts new file mode 100644 index 0000000000..9b193d967b --- /dev/null +++ b/src/vs/workbench/parts/debug/test/common/replHistory.test.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ReplHistory } from 'vs/workbench/parts/debug/common/replHistory'; + +suite('Debug - Repl History', () => { + let history: ReplHistory; + + setup(() => { + history = new ReplHistory(['one', 'two', 'three', 'four', 'five']); + }); + + teardown(() => { + history = null; + }); + + test('previous and next', () => { + assert.equal(history.previous(), 'five'); + assert.equal(history.previous(), 'four'); + assert.equal(history.previous(), 'three'); + assert.equal(history.previous(), 'two'); + assert.equal(history.previous(), 'one'); + assert.equal(history.previous(), null); + assert.equal(history.next(), 'two'); + assert.equal(history.next(), 'three'); + assert.equal(history.next(), 'four'); + assert.equal(history.next(), 'five'); + }); + + test('evaluated and remember', () => { + history.evaluated('six'); + assert.equal(history.previous(), 'six'); + assert.equal(history.previous(), 'five'); + assert.equal(history.next(), 'six'); + + history.remember('six++', true); + assert.equal(history.next(), 'six++'); + assert.equal(history.previous(), 'six'); + + history.evaluated('seven'); + assert.equal(history.previous(), 'seven'); + assert.equal(history.previous(), 'six'); + }); +}); diff --git a/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts b/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts new file mode 100644 index 0000000000..d2c6c850e8 --- /dev/null +++ b/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as paths from 'vs/base/common/paths'; +import * as platform from 'vs/base/common/platform'; +import { IRawAdapter } from 'vs/workbench/parts/debug/common/debug'; +import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; + +suite('Debug - Adapter', () => { + let adapter: Adapter; + const extensionFolderPath = 'a/b/c/'; + const rawAdapter = { + type: 'mock', + label: 'Mock Debug', + enableBreakpointsFor: { 'languageIds': ['markdown'] }, + program: './out/mock/mockDebug.js', + args: ['arg1', 'arg2'], + configurationAttributes: { + launch: { + required: ['program'], + properties: { + program: { + 'type': 'string', + 'description': 'Workspace relative path to a text file.', + 'default': 'readme.md' + } + } + } + }, + variables: null, + initialConfigurations: [ + { + name: 'Mock-Debug', + type: 'mock', + request: 'launch', + program: 'readme.md' + } + ] + }; + + setup(() => { + adapter = new Adapter(rawAdapter, { extensionFolderPath, id: 'adapter', name: 'myAdapter', version: '1.0.0', publisher: 'vscode', isBuiltin: false, engines: null }, + null, new TestConfigurationService(), null); + }); + + teardown(() => { + adapter = null; + }); + + test('attributes', () => { + assert.equal(adapter.type, rawAdapter.type); + assert.equal(adapter.label, rawAdapter.label); + + return adapter.getAdapterExecutable(undefined, false).then(details => { + assert.equal(details.command, paths.join(extensionFolderPath, rawAdapter.program)); + assert.deepEqual(details.args, rawAdapter.args); + }); + }); + + test('schema attributes', () => { + const schemaAttribute = adapter.getSchemaAttributes()[0]; + assert.notDeepEqual(schemaAttribute, rawAdapter.configurationAttributes); + Object.keys(rawAdapter.configurationAttributes.launch).forEach(key => { + assert.deepEqual(schemaAttribute[key], rawAdapter.configurationAttributes.launch[key]); + }); + + assert.equal(schemaAttribute['additionalProperties'], false); + assert.equal(!!schemaAttribute['properties']['request'], true); + assert.equal(!!schemaAttribute['properties']['name'], true); + assert.equal(!!schemaAttribute['properties']['type'], true); + assert.equal(!!schemaAttribute['properties']['preLaunchTask'], true); + }); + + test('merge', () => { + + const da: IRawAdapter = { + type: 'mock', + win: { + runtime: 'winRuntime' + }, + linux: { + runtime: 'linuxRuntime' + }, + osx: { + runtime: 'osxRuntime' + }, + runtimeArgs: ['first arg'], + program: 'mockprogram', + args: ['arg'] + }; + + adapter.merge(da, { + name: 'my name', + id: 'my_id', + version: '1.0', + publisher: 'mockPublisher', + isBuiltin: true, + extensionFolderPath: 'a/b/c/d', + engines: null + }); + + return adapter.getAdapterExecutable(undefined, false).then(details => { + assert.equal(details.command, platform.isLinux ? da.linux.runtime : platform.isMacintosh ? da.osx.runtime : da.win.runtime); + assert.deepEqual(details.args, da.runtimeArgs.concat(['a/b/c/d/mockprogram'].concat(da.args))); + }); + }); + + test('initial config file content', () => { + + const expected = ['{', + ' // Use IntelliSense to learn about possible attributes.', + ' // Hover to view descriptions of existing attributes.', + ' // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387', + ' "version": "0.2.0",', + ' "configurations": [', + ' {', + ' "name": "Mock-Debug",', + ' "type": "mock",', + ' "request": "launch",', + ' "program": "readme.md"', + ' }', + ' ]', + '}'].join('\n'); + + return adapter.getInitialConfigurationContent(null).then(content => { + assert.equal(content, expected); + }, err => assert.fail()); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/test/node/debugModel.test.ts b/src/vs/workbench/parts/debug/test/node/debugModel.test.ts new file mode 100644 index 0000000000..9cd09d5561 --- /dev/null +++ b/src/vs/workbench/parts/debug/test/node/debugModel.test.ts @@ -0,0 +1,385 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import uri from 'vs/base/common/uri'; +import severity from 'vs/base/common/severity'; +import { OutputElement, Model, Process, Expression, OutputNameValueElement, StackFrame, Thread } from 'vs/workbench/parts/debug/common/debugModel'; +import * as sinon from 'sinon'; +import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug'; + +suite('Debug - Model', () => { + let model: Model; + let rawSession: MockSession; + + setup(() => { + model = new Model([], true, [], [], []); + rawSession = new MockSession(); + }); + + teardown(() => { + model = null; + }); + + // Breakpoints + + test('breakpoints simple', () => { + const modelUri = uri.file('/myfolder/myfile.js'); + model.addBreakpoints(modelUri, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]); + assert.equal(model.areBreakpointsActivated(), true); + assert.equal(model.getBreakpoints().length, 2); + + model.removeBreakpoints(model.getBreakpoints()); + assert.equal(model.getBreakpoints().length, 0); + }); + + test('breakpoints toggling', () => { + const modelUri = uri.file('/myfolder/myfile.js'); + model.addBreakpoints(modelUri, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]); + model.addBreakpoints(modelUri, [{ lineNumber: 12, enabled: true, condition: 'fake condition' }]); + assert.equal(model.getBreakpoints().length, 3); + model.removeBreakpoints([model.getBreakpoints().pop()]); + assert.equal(model.getBreakpoints().length, 2); + + model.setBreakpointsActivated(false); + assert.equal(model.areBreakpointsActivated(), false); + model.setBreakpointsActivated(true); + assert.equal(model.areBreakpointsActivated(), true); + }); + + test('breakpoints two files', () => { + const modelUri1 = uri.file('/myfolder/my file first.js'); + const modelUri2 = uri.file('/secondfolder/second/second file.js'); + model.addBreakpoints(modelUri1, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]); + model.addBreakpoints(modelUri2, [{ lineNumber: 1, enabled: true }, { lineNumber: 2, enabled: true }, { lineNumber: 3, enabled: false }]); + + assert.equal(model.getBreakpoints().length, 5); + const bp = model.getBreakpoints()[0]; + const update: any = {}; + update[bp.getId()] = { line: 100, verified: false }; + model.updateBreakpoints(update); + assert.equal(bp.lineNumber, 100); + + model.enableOrDisableAllBreakpoints(false); + model.getBreakpoints().forEach(bp => { + assert.equal(bp.enabled, false); + }); + model.setEnablement(bp, true); + assert.equal(bp.enabled, true); + + model.removeBreakpoints(model.getBreakpoints().filter(bp => bp.uri.toString() === modelUri1.toString())); + assert.equal(model.getBreakpoints().length, 3); + }); + + test('breakpoints conditions', () => { + const modelUri1 = uri.file('/myfolder/my file first.js'); + model.addBreakpoints(modelUri1, [{ lineNumber: 5, condition: 'i < 5', hitCondition: '17' }, { lineNumber: 10, condition: 'j < 3' }]); + const breakpoints = model.getBreakpoints(); + + assert.equal(breakpoints[0].condition, 'i < 5'); + assert.equal(breakpoints[0].hitCondition, '17'); + assert.equal(breakpoints[1].condition, 'j < 3'); + assert.equal(!!breakpoints[1].hitCondition, false); + + assert.equal(model.getBreakpoints().length, 2); + model.removeBreakpoints(model.getBreakpoints()); + assert.equal(model.getBreakpoints().length, 0); + }); + + // Threads + + test('threads simple', () => { + const threadId = 1; + const threadName = 'firstThread'; + + model.addProcess({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession); + assert.equal(model.getProcesses().length, 1); + model.rawUpdate({ + sessionId: rawSession.getId(), + threadId: threadId, + thread: { + id: threadId, + name: threadName + } + }); + const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop(); + + assert.equal(process.getThread(threadId).name, threadName); + + model.clearThreads(process.getId(), true); + assert.equal(process.getThread(threadId), null); + assert.equal(model.getProcesses().length, 1); + model.removeProcess(process.getId()); + assert.equal(model.getProcesses().length, 0); + }); + + test('threads multiple wtih allThreadsStopped', () => { + const sessionStub = sinon.spy(rawSession, 'stackTrace'); + + const threadId1 = 1; + const threadName1 = 'firstThread'; + const threadId2 = 2; + const threadName2 = 'secondThread'; + const stoppedReason = 'breakpoint'; + + // Add the threads + model.addProcess({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession); + model.rawUpdate({ + sessionId: rawSession.getId(), + threadId: threadId1, + thread: { + id: threadId1, + name: threadName1 + } + }); + + model.rawUpdate({ + sessionId: rawSession.getId(), + threadId: threadId2, + thread: { + id: threadId2, + name: threadName2 + } + }); + + // Stopped event with all threads stopped + model.rawUpdate({ + sessionId: rawSession.getId(), + threadId: threadId1, + stoppedDetails: { + reason: stoppedReason, + threadId: 1 + }, + allThreadsStopped: true + }); + const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop(); + + const thread1 = process.getThread(threadId1); + const thread2 = process.getThread(threadId2); + + // at the beginning, callstacks are obtainable but not available + assert.equal(process.getAllThreads().length, 2); + assert.equal(thread1.name, threadName1); + assert.equal(thread1.stopped, true); + assert.equal(thread1.getCallStack().length, 0); + assert.equal(thread1.stoppedDetails.reason, stoppedReason); + assert.equal(thread2.name, threadName2); + assert.equal(thread2.stopped, true); + assert.equal(thread2.getCallStack().length, 0); + assert.equal(thread2.stoppedDetails.reason, undefined); + + // after calling getCallStack, the callstack becomes available + // and results in a request for the callstack in the debug adapter + thread1.fetchCallStack().then(() => { + assert.notEqual(thread1.getCallStack().length, 0); + assert.equal(thread2.getCallStack().length, 0); + assert.equal(sessionStub.callCount, 1); + }); + + thread2.fetchCallStack().then(() => { + assert.notEqual(thread1.getCallStack().length, 0); + assert.notEqual(thread2.getCallStack().length, 0); + assert.equal(sessionStub.callCount, 2); + }); + + // calling multiple times getCallStack doesn't result in multiple calls + // to the debug adapter + thread1.fetchCallStack().then(() => { + return thread2.fetchCallStack(); + }).then(() => { + assert.equal(sessionStub.callCount, 4); + }); + + // clearing the callstack results in the callstack not being available + thread1.clearCallStack(); + assert.equal(thread1.stopped, true); + assert.equal(thread1.getCallStack().length, 0); + + thread2.clearCallStack(); + assert.equal(thread2.stopped, true); + assert.equal(thread2.getCallStack().length, 0); + + model.clearThreads(process.getId(), true); + assert.equal(process.getThread(threadId1), null); + assert.equal(process.getThread(threadId2), null); + assert.equal(process.getAllThreads().length, 0); + }); + + test('threads mutltiple without allThreadsStopped', () => { + const sessionStub = sinon.spy(rawSession, 'stackTrace'); + + const stoppedThreadId = 1; + const stoppedThreadName = 'stoppedThread'; + const runningThreadId = 2; + const runningThreadName = 'runningThread'; + const stoppedReason = 'breakpoint'; + model.addProcess({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession); + // Add the threads + model.rawUpdate({ + sessionId: rawSession.getId(), + threadId: stoppedThreadId, + thread: { + id: stoppedThreadId, + name: stoppedThreadName + } + }); + + model.rawUpdate({ + sessionId: rawSession.getId(), + threadId: runningThreadId, + thread: { + id: runningThreadId, + name: runningThreadName + } + }); + + // Stopped event with only one thread stopped + model.rawUpdate({ + sessionId: rawSession.getId(), + threadId: stoppedThreadId, + stoppedDetails: { + reason: stoppedReason, + threadId: 1 + }, + allThreadsStopped: false + }); + const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop(); + + const stoppedThread = process.getThread(stoppedThreadId); + const runningThread = process.getThread(runningThreadId); + + // the callstack for the stopped thread is obtainable but not available + // the callstack for the running thread is not obtainable nor available + assert.equal(stoppedThread.name, stoppedThreadName); + assert.equal(stoppedThread.stopped, true); + assert.equal(process.getAllThreads().length, 2); + assert.equal(stoppedThread.getCallStack().length, 0); + assert.equal(stoppedThread.stoppedDetails.reason, stoppedReason); + assert.equal(runningThread.name, runningThreadName); + assert.equal(runningThread.stopped, false); + assert.equal(runningThread.getCallStack().length, 0); + assert.equal(runningThread.stoppedDetails, undefined); + + // after calling getCallStack, the callstack becomes available + // and results in a request for the callstack in the debug adapter + stoppedThread.fetchCallStack().then(() => { + assert.notEqual(stoppedThread.getCallStack().length, 0); + assert.equal(runningThread.getCallStack().length, 0); + assert.equal(sessionStub.callCount, 1); + }); + + // calling getCallStack on the running thread returns empty array + // and does not return in a request for the callstack in the debug + // adapter + runningThread.fetchCallStack().then(() => { + assert.equal(runningThread.getCallStack().length, 0); + assert.equal(sessionStub.callCount, 1); + }); + + // clearing the callstack results in the callstack not being available + stoppedThread.clearCallStack(); + assert.equal(stoppedThread.stopped, true); + assert.equal(stoppedThread.getCallStack().length, 0); + + model.clearThreads(process.getId(), true); + assert.equal(process.getThread(stoppedThreadId), null); + assert.equal(process.getThread(runningThreadId), null); + assert.equal(process.getAllThreads().length, 0); + }); + + // Expressions + + function assertWatchExpressions(watchExpressions: Expression[], expectedName: string) { + assert.equal(watchExpressions.length, 2); + watchExpressions.forEach(we => { + assert.equal(we.available, false); + assert.equal(we.reference, 0); + assert.equal(we.name, expectedName); + }); + } + + test('watch expressions', () => { + assert.equal(model.getWatchExpressions().length, 0); + const process = new Process({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession); + const thread = new Thread(process, 'mockthread', 1); + const stackFrame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: undefined, endColumn: undefined }, 0); + model.addWatchExpression(process, stackFrame, 'console').done(); + model.addWatchExpression(process, stackFrame, 'console').done(); + let watchExpressions = model.getWatchExpressions(); + assertWatchExpressions(watchExpressions, 'console'); + + model.renameWatchExpression(process, stackFrame, watchExpressions[0].getId(), 'new_name').done(); + model.renameWatchExpression(process, stackFrame, watchExpressions[1].getId(), 'new_name').done(); + assertWatchExpressions(model.getWatchExpressions(), 'new_name'); + + model.evaluateWatchExpressions(process, null); + assertWatchExpressions(model.getWatchExpressions(), 'new_name'); + + model.addWatchExpression(process, stackFrame, 'mockExpression'); + model.moveWatchExpression(model.getWatchExpressions()[2].getId(), 1); + watchExpressions = model.getWatchExpressions(); + assert.equal(watchExpressions[0].name, 'new_name'); + assert.equal(watchExpressions[1].name, 'mockExpression'); + assert.equal(watchExpressions[2].name, 'new_name'); + + model.removeWatchExpressions(); + assert.equal(model.getWatchExpressions().length, 0); + }); + + test('repl expressions', () => { + assert.equal(model.getReplElements().length, 0); + const process = new Process({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession); + const thread = new Thread(process, 'mockthread', 1); + const stackFrame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1); + model.addReplExpression(process, stackFrame, 'myVariable').done(); + model.addReplExpression(process, stackFrame, 'myVariable').done(); + model.addReplExpression(process, stackFrame, 'myVariable').done(); + + assert.equal(model.getReplElements().length, 3); + model.getReplElements().forEach(re => { + assert.equal((re).available, false); + assert.equal((re).name, 'myVariable'); + assert.equal((re).reference, 0); + }); + + model.removeReplExpressions(); + assert.equal(model.getReplElements().length, 0); + }); + + // Repl output + + test('repl output', () => { + model.appendToRepl('first line\n', severity.Error); + model.appendToRepl('second line', severity.Error); + model.appendToRepl('third line', severity.Warning); + model.appendToRepl('fourth line', severity.Error); + + let elements = model.getReplElements(); + assert.equal(elements.length, 4); + assert.equal(elements[0].value, 'first line'); + assert.equal(elements[0].severity, severity.Error); + assert.equal(elements[1].value, 'second line'); + assert.equal(elements[1].severity, severity.Error); + assert.equal(elements[2].value, 'third line'); + assert.equal(elements[2].severity, severity.Warning); + assert.equal(elements[3].value, 'fourth line'); + assert.equal(elements[3].severity, severity.Error); + + model.appendToRepl('1', severity.Warning); + elements = model.getReplElements(); + assert.equal(elements.length, 5); + assert.equal(elements[4].value, '1'); + assert.equal(elements[4].severity, severity.Warning); + + const keyValueObject = { 'key1': 2, 'key2': 'value' }; + model.appendToRepl(new OutputNameValueElement('fake', keyValueObject), null); + const element = model.getReplElements()[5]; + assert.equal(element.value, 'Object'); + assert.deepEqual(element.valueObj, keyValueObject); + + model.removeReplExpressions(); + assert.equal(model.getReplElements().length, 0); + }); +}); diff --git a/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts b/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts new file mode 100644 index 0000000000..1f663d906d --- /dev/null +++ b/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); + +import { TPromise } from 'vs/base/common/winjs.base'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; + +const EMMET_COMMANDS_PREFIX = '>Emmet: '; + +@editorAction +class ShowEmmetCommandsAction extends EditorAction { + + constructor() { + super({ + id: 'workbench.action.showEmmetCommands', + label: nls.localize('showEmmetCommands', "Show Emmet Commands"), + alias: 'Show Emmet Commands', + precondition: EditorContextKeys.writable, + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + const quickOpenService = accessor.get(IQuickOpenService); + quickOpenService.show(EMMET_COMMANDS_PREFIX); + return TPromise.as(null); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/emmet/browser/emmet.browser.contribution.ts b/src/vs/workbench/parts/emmet/browser/emmet.browser.contribution.ts new file mode 100644 index 0000000000..2ba58588d6 --- /dev/null +++ b/src/vs/workbench/parts/emmet/browser/emmet.browser.contribution.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import './actions/showEmmetCommands'; diff --git a/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.ts b/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.ts new file mode 100644 index 0000000000..c1e248c342 --- /dev/null +++ b/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import nls = require('vs/nls'); +import { EmmetEditorAction } from 'vs/workbench/parts/emmet/electron-browser/emmetActions'; +import { editorAction } from 'vs/editor/common/editorCommonExtensions'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; + +@editorAction +class ExpandAbbreviationAction extends EmmetEditorAction { + + constructor() { + super({ + id: 'editor.emmet.action.expandAbbreviation', + label: nls.localize('expandAbbreviationAction', "Emmet: Expand Abbreviation"), + alias: 'Emmet: Expand Abbreviation', + precondition: EditorContextKeys.writable, + actionName: 'expand_abbreviation', + kbOpts: { + primary: KeyCode.Tab, + kbExpr: ContextKeyExpr.and( + EditorContextKeys.textFocus, + EditorContextKeys.tabDoesNotMoveFocus, + ContextKeyExpr.has('config.emmet.triggerExpansionOnTab') + ) + } + }); + + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.ts b/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.ts new file mode 100644 index 0000000000..a1bcbaba3e --- /dev/null +++ b/src/vs/workbench/parts/emmet/electron-browser/emmet.contribution.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import './actions/expandAbbreviation'; + diff --git a/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts b/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts new file mode 100644 index 0000000000..4c21887a6c --- /dev/null +++ b/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { EditorAction, ServicesAccessor, IActionOptions } from 'vs/editor/common/editorCommonExtensions'; +import { grammarsExtPoint, ITMSyntaxExtensionPoint } from 'vs/workbench/services/textMate/electron-browser/TMGrammars'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IExtensionService, ExtensionPointContribution } from 'vs/platform/extensions/common/extensions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; + +interface ModeScopeMap { + [key: string]: string; +} + +export interface IGrammarContributions { + getGrammar(mode: string): string; +} + +export interface ILanguageIdentifierResolver { + getLanguageIdentifier(modeId: LanguageId): LanguageIdentifier; +} + +class GrammarContributions implements IGrammarContributions { + + private static _grammars: ModeScopeMap = null; + + constructor(contributions: ExtensionPointContribution[]) { + if (GrammarContributions._grammars === null) { + this.fillModeScopeMap(contributions); + } + } + + private fillModeScopeMap(contributions: ExtensionPointContribution[]) { + GrammarContributions._grammars = {}; + contributions.forEach((contribution) => { + contribution.value.forEach((grammar) => { + if (grammar.language && grammar.scopeName) { + GrammarContributions._grammars[grammar.language] = grammar.scopeName; + } + }); + }); + } + + public getGrammar(mode): string { + return GrammarContributions._grammars[mode]; + } +} + +export interface IEmmetActionOptions extends IActionOptions { + actionName: string; +} + +export abstract class EmmetEditorAction extends EditorAction { + + protected emmetActionName: string; + + constructor(opts: IEmmetActionOptions) { + super(opts); + this.emmetActionName = opts.actionName; + } + + private static readonly emmetSupportedModes = ['html', 'css', 'xml', 'xsl', 'haml', 'jade', 'jsx', 'slim', 'scss', 'sass', 'less', 'stylus', 'styl', 'svg']; + + private _lastGrammarContributions: TPromise = null; + private _lastExtensionService: IExtensionService = null; + private _withGrammarContributions(extensionService: IExtensionService): TPromise { + if (this._lastExtensionService !== extensionService) { + this._lastExtensionService = extensionService; + this._lastGrammarContributions = extensionService.readExtensionPointContributions(grammarsExtPoint).then((contributions) => { + return new GrammarContributions(contributions); + }); + } + return this._lastGrammarContributions; + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + const extensionService = accessor.get(IExtensionService); + const modeService = accessor.get(IModeService); + const commandService = accessor.get(ICommandService); + + return this._withGrammarContributions(extensionService).then((grammarContributions) => { + + if (this.id === 'editor.emmet.action.expandAbbreviation') { + return commandService.executeCommand('emmet.expandAbbreviation', EmmetEditorAction.getLanguage(modeService, editor, grammarContributions)); + } + + return undefined; + }); + + } + + public static getLanguage(languageIdentifierResolver: ILanguageIdentifierResolver, editor: ICommonCodeEditor, grammars: IGrammarContributions) { + let position = editor.getSelection().getStartPosition(); + editor.getModel().tokenizeIfCheap(position.lineNumber); + let languageId = editor.getModel().getLanguageIdAtPosition(position.lineNumber, position.column); + let language = languageIdentifierResolver.getLanguageIdentifier(languageId).language; + let syntax = language.split('.').pop(); + + let checkParentMode = (): string => { + let languageGrammar = grammars.getGrammar(syntax); + if (!languageGrammar) { + return syntax; + } + let languages = languageGrammar.split('.'); + if (languages.length < 2) { + return syntax; + } + for (let i = 1; i < languages.length; i++) { + const language = languages[languages.length - i]; + if (this.emmetSupportedModes.indexOf(language) !== -1) { + return language; + } + } + return syntax; + }; + + return { + language: syntax, + parentMode: checkParentMode() + }; + } + + +} + + diff --git a/src/vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts b/src/vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts new file mode 100644 index 0000000000..6cb393e35d --- /dev/null +++ b/src/vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IGrammarContributions, ILanguageIdentifierResolver, EmmetEditorAction } from 'vs/workbench/parts/emmet/electron-browser/emmetActions'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import assert = require('assert'); +import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; + +// +// To run the emmet tests only change .vscode/launch.json +// { +// "name": "Stacks Tests", +// "type": "node", +// "request": "launch", +// "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", +// "stopOnEntry": false, +// "args": [ +// "--timeout", +// "999999", +// "--colors", +// "-g", +// "Stacks" <<<--- Emmet +// ], +// Select the 'Stacks Tests' launch config and F5 +// + +class MockGrammarContributions implements IGrammarContributions { + private scopeName; + + constructor(scopeName: string) { + this.scopeName = scopeName; + } + + public getGrammar(mode: string): string { + return this.scopeName; + } +} + +suite('Emmet', () => { + + test('Get language mode and parent mode for emmet', () => { + withMockCodeEditor([], {}, (editor) => { + + function testIsEnabled(mode: string, scopeName: string, expectedLanguage?: string, expectedParentLanguage?: string) { + const languageIdentifier = new LanguageIdentifier(mode, 73); + const languageIdentifierResolver: ILanguageIdentifierResolver = { + getLanguageIdentifier: (languageId: LanguageId) => { + if (languageId === 73) { + return languageIdentifier; + } + throw new Error('Unexpected'); + } + }; + editor.getModel().setMode(languageIdentifier); + let langOutput = EmmetEditorAction.getLanguage(languageIdentifierResolver, editor, new MockGrammarContributions(scopeName)); + assert.equal(langOutput.language, expectedLanguage); + assert.equal(langOutput.parentMode, expectedParentLanguage); + + } + + // syntaxes mapped using the scope name of the grammar + testIsEnabled('markdown', 'text.html.markdown', 'markdown', 'html'); + testIsEnabled('handlebars', 'text.html.handlebars', 'handlebars', 'html'); + testIsEnabled('nunjucks', 'text.html.nunjucks', 'nunjucks', 'html'); + testIsEnabled('laravel-blade', 'text.html.php.laravel-blade', 'laravel-blade', 'html'); + + // languages that have different Language Id and scopeName + // testIsEnabled('razor', 'text.html.cshtml', 'razor', 'html'); + // testIsEnabled('HTML (Eex)', 'text.html.elixir', 'boo', 'html'); + + }); + }); +}); diff --git a/src/vs/workbench/parts/execution/common/execution.ts b/src/vs/workbench/parts/execution/common/execution.ts new file mode 100644 index 0000000000..e50d030e57 --- /dev/null +++ b/src/vs/workbench/parts/execution/common/execution.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IProcessEnvironment } from 'vs/base/common/platform'; + +export const ITerminalService = createDecorator('nativeTerminalService'); + +export interface ITerminalService { + _serviceBrand: any; + openTerminal(path: string): void; + runInTerminal(title: string, cwd: string, args: string[], env: IProcessEnvironment): TPromise; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/execution/electron-browser/TerminalHelper.scpt b/src/vs/workbench/parts/execution/electron-browser/TerminalHelper.scpt new file mode 100644 index 0000000000000000000000000000000000000000..98d4f76d3aaffd854714ab3c66851597bb2d4f6c GIT binary patch literal 14736 zcmeHO2b5IBwms(#$$P088jvKRMNyC}U_gA52H}AYDu}3v%}fsr3^Ul>fMCLe@fk3n zm`LV;fQk_XOemrt5)6nSNHB+I!a9GSThr6P8;1AR|7-pA*ZQq>rf%K3Rj2j|RduT9 z6H68h?r}!6W4n$WG-VS}l^hXb@+gauA%i@RLAm~_cvJ;%s!}xrl2wd%_=%7WQ5M3brQ|JHvVRRcFZ1qP&ixPM`ohUG6EZcXY=U8B89 z$4yEUjU1JVw(Qj^dP-4#qBL1rm_m7CTxlYfDk?2$7wu7891SZaqltJjo|q6XXy?WX zjOSB!1fxz-6!?|WIGRKWDx#4zDlBV>(pGqP3d-{Frj(LMh2`}V_@Wf$F_aV`S3>Pj zy9X8Hzvvz8-l>AP3b1+gps&7Ebf%%OZP(zmA6qp7;aCkH| zDjqE?EiNvdSX44HnwnS|9hWE_7f+;$;>l<%5sxCDEJ~*01<{zQMlxDZR9F~K#7k1q z#Q2hAw6qXy6eXjB;)$_EC9&eZ@nU=uP39+x#-)_OY-*^`HHuIpS_~PcpcircErhI9 z#-%h7axa0t=(~w{jzS|;PsW9kR;^H}N1-8!@Jm;oM0pZls2=FMv~Fq50=z54f2t{Q zw5__Cpz+A*Th%_thqe%tt*!GG$n$AuTGIP?{f_TGAobR1s?yp=NYp z8K-erRR)3a;L43V72>6%bY&TQlfYyw%C$ss$c%4av=sdbXeWOU8%%oC7`#O3Py^zP zRz)LE{z=UPklG!ndT}h7N~I>b%%IdHL(PfDhV@u}@#Pn);*U+1jP&SGG;|mpZa~6@ zvZ$*3Of3SC#vQ1pHU#Y}fC?74ry=1}zutygxF)lxiu~luPvuP>jwX+wBMk`T;LPAh z`9Z#C`B5$42PNOD861h!l3E!MuL#^#gIhj|S{sn8eq~>NM{VR=U%sOT?PBo>snXcE zaV6u6i;1S-gInk*{0y}rE)7RV@=YM$P>l{$C6*XDL1Fn;C!5w~lMUZPZ9TYs500$8 z0V(J3YuP0`S-w{0b}8AZ9JWX5KphQ;mk4{vqNAyk0V&6Ghc7$I25+cShLi1qY^PcP z(E&I-Rg@}@Yoi^JI#U+|5;j@|v)eIrtN{rdtpXW*<;z#)jUJn6bXy?XD5nDj6H60g z;1-4#6oD^~x*#1##~YBa%^GmNUFie^61G_b(00qFZmPT|M(9KZ<%CSLTLaljjnFJS z%*aG+EWk^aKlH$ZN5`XwztKqsBP1> zRI>xsh?h(lJ|UJ!D%U3=^`=|{5(=Ug)sii;S-xP|qB7X5W|6;=oAAIvf7jmr&B4ZY;1m`6Q4}sA>mdD6OH>-HWEwOg;|e z<2{s*Wup$@Gp&`Efnq}-8^X_HI>O>(}d)tks%>k%ql&R45-9%mHC<9l->aDh4E!Qb#%#&K|NT zBJPOZyU?-nzAx`XM#)s-0z;#nd^_UJ7+ERr85)!RvJ0k-u?8fR@6psnR#1r#l9q3Y zPTQTafHcs3HIPXiJP ztQ(Br0!kQ=u$yjlqP!i*+Zll+9D*mp@RHe>Tp+Mi1QUk}EaqYE^Dw_mFd(7idP0#V zdYG&|Oi=Lr7dk9^(#cpIU1UH)$@PRuT}+o4kg(sL)Jxt9dVq{CAroq zNq@?t>tu<%X6U-~mwB+L8w^OOBm*c4{NT@Hvpow>QbO(y>r0E7Ew7p?4@w@15 z0}|@XFt~sjbdLcE`yEE-$%}!!n1SFP*Y9~WTwchM7gS&F&W!i@K%P(6^=LYJyH^$& zy4UrV4<_!T`wd8_ANjDl2WX}NDSIxYSv1>#guUlefjk$;bNkFFhGsh^;FD-hHqFt& z&yCPrbO#NY>Ev7pFYq8eWI#d<8BN9VtS`@&Ysf=RLki(f9+qchp`nM%hM5hI@Ad) zou5F1^W{lH^PNsihD|N-XaR~B(lZ7m)QQP3=V$3T0}|@QWV(bF(enl*>=*t;o(SZL z%%Gol{a!>D%i~${xa!1nnGrq~$Ybfc9zBEJUJ!u$g6nMxn0S$1G9clsJq70fvOFRW z8+zG^LrY$v#Reo4$K`aDJVdYhAZc;D>Uz6^u9OD@c`)6Z!FDHPc?w-Fa|4;XmpC3# zaXhTzcqzl|oIvJOw49SiArg5~Klw<7GX&{1fvUcyYRxJfPpq zCJL~GDPKtRb70k>x^jP(Kobo7TsZW)*rzR#`{Z6jOLS?>*kU%6duXZ5@bRZXyZq8b zJijCr*X1zWEq=Aimc~on2(T}ey92pphS$L&lQ%PLxJKtc~Q69)PrtuY{F&kxc^wAO%xqnQb3 zvM!s}>1ftRXgxM7a6;-;);eD1&|JACkXtI^WsPg&LHLag*|b61_&7oz!~9Or7O*Nx zZ(nW>F2abZjkE@G9i)+V6$J)W&;xTy?_?Vb%9*BkHayv*|EM5cDp5;wkYdgM(9fw<|c>a zixBu$+GapP?$hDba8rtc4c?CwZE1Py{FJDLK zYk;gF0xWK}yKk33<*y0kntx$)TL!|ZflSS`?a2)a%=HS)Mx<}(TLTh8xPn&5)pC_w z$#S(;x=P8FTJu|^@92925<<8FIQ>9B$`x|CK_-N71ytuJ`q_Yl<61%Q$&^5*R6460 z`q>TjJt*ie+4PGJ^`8;?rvmz;19S}p_bdHoKtcf5Kx2NVKMY9N(HdGOmj!ZJW{`&d zaIech_|Qnw zz(a=;`Idf?L?DR@gD;-wURTCL!h8^6Z=>VcVYqrm2p0r$0oCh32c@v_j;EC0vTVp_ zRdkolIfkFoDVbHJmgvxM~ZwYq8h=7($Wd3#Q3;jgu8U#j~^e-EeszX zE(O`=Y#HUtD2y;QB8d?q$ff93hjNV!WaNG^s>xBz(RHFD5@D=1HbO&-g^AJ<7iZ-| zb%aMm_y|PBqF7pM)G{4cjo~?p4V^|;3-Lzd>?x0%Vx&h(q2VLLXc*+$MB?0%Tlo?% zoAL`vlldwMf!EQLpSmz*xTWK{7UW(KNI}NBt8pvJl6*c&M);DCF^(xL!WfTo_3Lp% zi3Jkd?-;8IwsM|8fjz+lZ@8`#e-ujA+T+&Xw+**7+{Q^J%27F=+i`oA^Hu!q5M-_! z%}>N*sd%&`KCwL1>U5yJBGe(m9T35YhH>3^81vL9X$dr9Vu;Z`+Rzq#cI2ZC*752n zILfWL6L&V;NqMM>=Oej`443l^cX249M27J(a;`7KDn${&IF)>q50i5OIVXdnC+DJE zhATNwVdxyzB>d#;K+fJTejXnUc8}%b3?HlPDO_!2sGKE3SP;}=IZMe9HDrYG_;~JW z_;?4V);yC>;BG8uDwrowJ8B7A8XF%w0wH}kxoE7UAnHPm(SE&gaF77V7ltPR4q%P_ z@re;W5kW6!WEz4V3*}Q#RRL3Ff$S1p*Z^=<(E_NWLf;QDI(IOjbyXl2qJCNE-s7&| z_iucXVHX|5$d2dk+{18pCx|u>OHUargADf!O%F9XaWC#|xR*1%_S{Jja63ay55qCs z+X=#(Blb)`)2fLKp+F$fJen$XP6L1#fOB!caQs^yHgn5of5ivFgu3x zc%bwbFr62=d6W(0)A)45r#Y7Tfx&)(^xJQk}6DR@JJ*` z@C+xxGdRYB`Aoxuodkz*OiuRYWPAq&j2b@E!FdJ`;~{*O;UNyrK|D8!!kMdZ!hqnO9G8PKaP|zO=Yb}5>5mTu^6>anq;vRO z!{<0NE#_kBA>HL9mL6KEyONW%=DA42wY!3ROdq2hn@OG^#|Coje(lcV z0g1vmuhoQ$WU;Ry^5 zNt5G>W0P=N8lO;^b@C5hwca7*SnL2_`nI zAJq1yLh$2xg5mK_I#c-?=@>}I3Y7>k!!*Pb`9i}J9mLo3P13;^oQ?U4gy9QaTi5XQ z(ms&(`-fPLRtv-V0!uf|VK44+1Zk4AGd#&z$?bf*w3Rl}nx(DQY@?*LR&S@Ki6=N0 z>?gPKY?DbMkgL zWZX~v%8Ikz_amzZK7iTC{RBUvgQDr@vcCrnP8Agw=^?&GX@J7Y>5nhNyNKpj54^Gq z@=P_9qpX#6mSO}g`}XeIW8ApnqOybNu2kMcSK5o|Y^eJywDatFhthGu00X zXw{13x7x{Wo_aK4FeHhdFhVX8y? z=sdnf8u6`$5%t%tc0C%((>$Jr4{zh!4c`{J4-_AQJ@}o55$0FdP!Cs4APB?LL)U=2 z6Ah)IFAd=u+|+%S;~1Azq(LAJs3}%G`QsCMx1?;C=|?wMgDro!6-MJyKal!i1OiJI zR~xqng7WoCzXFdD!sfdLo6x)6Mzt~cp27FX!BW@o3>VgJOwIUSzR&QzuIa|qMCt@m zr_!bk-{)%M;)m1@q;`d}F}Mg2BG{Y;^ZgRx2Miw#m(D!x(pBToepLSlId~5MPI?sn88pffaB8CQeQ4TNq|4I1&)+Ee01m@@M zUxsn8hN+d#$2{?ZVH~!qSaDOs?)L3&h+CsVp8X3-!&f1_$S)ZV&(S=UzWvkwVSi`) zCltZ{q2zb1`4ZC0{EFe1-K?2S5njx%8eUvJYuexVHT!E|f2(-o_8MAPV*hD)iPLiJ z>zBa(vXATVEaKgs{Z-8qCv@s|aYulcdW_``FXPt@FH6sOx%>vdY50xwjMtmrvOn|N zhTn1}_2?vCZhzu;4CAOtn`%j2`CX6SMN=#IJ;N)U2X9Gjc_qJZ7$-n#sV%8Bf558@ zL0*`F#! z=N*T5Lu$+$_+!I30aN$Xh#K3ke5)7ToNxTtvC)tk*=>Q{c0e`)`||MTm6o(K{3M4z`G05lf6N)a4tx64erfnqr^|idLOwJ6nG-8+3)(Hd-BRuhzf>28 zn*zL%KR3Lwd<$VW+b`@UwwqO>zfiJCYvQ1kH(Au&l&QJVer7*qyHP8BrsPvKfY0?J zHT-H$Ey|+m_7ncXerz}JX5PYIvi$_RJpO{J+VyswU28wGYXZBz!a!DF*Bu~8<=eHD z+=C75NB{O-Ca`NN^5DM3p>!B>^6iGQTsCi}>b2~LaF46)D&ET5><2y`8|?Q&ZeUmM zdC$PEQV4K5Y~Qyl1N;6S?*h9regB~rZ^M%4J-fobOWo`{)YC4vZ`-%*n>5V6fuP9M zbSsYF=h1TeI!@s?;3#?*{lb&&GP~3+v9H-z?PB|iec8TbU$igS=j|fjzE?Jaz^+hl zxz9!103H3yP1*oGS9T9Ju*=n{?s1FNw{Mr7BUE$&!?$mN3B5rYU?h6gBZl252IQ_R z1{h5F9f-g#`w!%|bid&m-!4J(x&rX+Ysl$ImT&bEo1XXhc5x-=9=?4=zrfBTurF7n zA%T6#b)4SX?Gc#v?Th$WH;}%4L7PE5(YMcsx#T$CF47z#PZ9e8-@7SyRoo|89JZp@2O zB=>x(owsxo4i{_3sLs+BF{;}_%ff1J#^_*uGhAQRYw>30&Fe9$k2l$P^LnN~jP~&)*ZQQ00%Ft>PJQmlarbB5wo`FWwO#NnU za`Uy7ys$+Y)0(1qc_=sHpW&}~yImOA=l*T&%(l28R>dyh9a(k(dD*-}14#4j(}A5| z;eI@@Pw%r<@hmRI)#9(Hnw@8#vQOG4?Bn*az|Pyx_fPHfePEwF;L;$lPgEL92<+pP zENcS$SOsgaTD+YCUSuD&kJyLpL-s*C*UquC?JPUfw~xXAbR*{5N01v+nh$Hlc0TkE zzI~`tKk^{zIn$bpoUWIAJ15MgCi!+Ya=L-`?JUiemZIgE$f*~I*oBn8sWzQkpN1aw zMcZ>WZ-I(c88hk-@@8*sle;i)&P0HOZ_0G zp7rUZy~Tu^6)k6N-BUE)>}} { + let configurationRegistry = Registry.as(Extensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': 'externalTerminal', + 'order': 100, + 'title': nls.localize('terminalConfigurationTitle', "External Terminal"), + 'type': 'object', + 'properties': { + 'terminal.explorerKind': { + 'type': 'string', + 'enum': [ + 'integrated', + 'external' + ], + 'description': nls.localize('explorer.openInTerminalKind', "Customizes what kind of terminal to launch."), + 'default': 'integrated', + 'isExecutable': false + }, + 'terminal.external.windowsExec': { + 'type': 'string', + 'description': nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."), + 'default': DEFAULT_TERMINAL_WINDOWS, + 'isExecutable': true + }, + 'terminal.external.osxExec': { + 'type': 'string', + 'description': nls.localize('terminal.external.osxExec', "Customizes which terminal application to run on OS X."), + 'default': DEFAULT_TERMINAL_OSX, + 'isExecutable': true + }, + 'terminal.external.linuxExec': { + 'type': 'string', + 'description': nls.localize('terminal.external.linuxExec', "Customizes which terminal to run on Linux."), + 'default': defaultTerminalLinux, + 'isExecutable': true + } + } + }); +}); + + +export abstract class AbstractOpenInTerminalAction extends Action { + private resource: uri; + + constructor( + id: string, + label: string, + @IWorkbenchEditorService protected editorService: IWorkbenchEditorService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IHistoryService protected historyService: IHistoryService + ) { + super(id, label); + + this.order = 49; // Allow other actions to position before or after + } + + public setResource(resource: uri): void { + this.resource = resource; + this.enabled = !paths.isUNC(this.resource.fsPath); + } + + public getPathToOpen(): string { + let pathToOpen: string; + + // Try workspace path first + const root = this.historyService.getLastActiveWorkspaceRoot(); + pathToOpen = this.resource ? this.resource.fsPath : (root && root.fsPath); + + // Otherwise check if we have an active file open + if (!pathToOpen) { + const file = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); + if (file) { + pathToOpen = paths.dirname(file.fsPath); // take parent folder of file + } + } + + return pathToOpen; + } +} + +export class OpenConsoleAction extends AbstractOpenInTerminalAction { + + public static ID = 'workbench.action.terminal.openNativeConsole'; + public static Label = env.isWindows ? nls.localize('globalConsoleActionWin', "Open New Command Prompt") : + nls.localize('globalConsoleActionMacLinux', "Open New Terminal"); + public static ScopedLabel = env.isWindows ? nls.localize('scopedConsoleActionWin', "Open in Command Prompt") : + nls.localize('scopedConsoleActionMacLinux', "Open in Terminal"); + + constructor( + id: string, + label: string, + @ITerminalService private terminalService: ITerminalService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IHistoryService historyService: IHistoryService + ) { + super(id, label, editorService, contextService, historyService); + } + + public run(event?: any): TPromise { + let pathToOpen = this.getPathToOpen(); + this.terminalService.openTerminal(pathToOpen); + + return TPromise.as(null); + } +} + +export class OpenIntegratedTerminalAction extends AbstractOpenInTerminalAction { + + public static ID = 'workbench.action.terminal.openFolderInIntegratedTerminal'; + public static Label = nls.localize('openFolderInIntegratedTerminal', "Open in Terminal"); + + constructor( + id: string, + label: string, + @IIntegratedTerminalService private integratedTerminalService: IIntegratedTerminalService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IHistoryService historyService: IHistoryService + ) { + super(id, label, editorService, contextService, historyService); + } + + public run(event?: any): TPromise { + let pathToOpen = this.getPathToOpen(); + + var instance = this.integratedTerminalService.createInstance({ cwd: pathToOpen }, true); + if (instance) { + this.integratedTerminalService.setActiveInstance(instance); + this.integratedTerminalService.showPanel(true); + } + return TPromise.as(null); + } +} + +export class ExplorerViewerActionContributor extends ActionBarContributor { + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IConfigurationService private configurationService: IConfigurationService + ) { + super(); + } + + public hasSecondaryActions(context: any): boolean { + return !!explorerItemToFileResource(context.element); + } + + public getSecondaryActions(context: any): IAction[] { + let fileResource = explorerItemToFileResource(context.element); + let resource = fileResource.resource; + + // We want the parent unless this resource is a directory + if (!fileResource.isDirectory) { + resource = uri.file(paths.dirname(resource.fsPath)); + } + + const configuration = this.configurationService.getConfiguration(); + const explorerKind = configuration.terminal.explorerKind; + + if (explorerKind === 'integrated') { + let action = this.instantiationService.createInstance(OpenIntegratedTerminalAction, OpenIntegratedTerminalAction.ID, OpenIntegratedTerminalAction.Label); + action.setResource(resource); + + return [action]; + } else { + let action = this.instantiationService.createInstance(OpenConsoleAction, OpenConsoleAction.ID, OpenConsoleAction.ScopedLabel); + action.setResource(resource); + + return [action]; + } + } +} + +const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); +actionBarRegistry.registerActionBarContributor(Scope.VIEWER, ExplorerViewerActionContributor); + +// Register Global Action to Open Console +Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( + new SyncActionDescriptor( + OpenConsoleAction, + OpenConsoleAction.ID, + OpenConsoleAction.Label, + { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C }, + KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED + ), + env.isWindows ? 'Open New Command Prompt' : 'Open New Terminal' +); diff --git a/src/vs/workbench/parts/execution/electron-browser/iTermHelper.scpt b/src/vs/workbench/parts/execution/electron-browser/iTermHelper.scpt new file mode 100644 index 0000000000000000000000000000000000000000..4cb2ddfc3a7952c02c3d17843c5bdefa2228ebc3 GIT binary patch literal 6790 zcmc&(X?Rt|8Gh&70O6j@C6`4O1ykF!3KBr2wzgOi(*^@x6c8d}xFjKDkN3s|;}*aL z6&G+paSJN$sDO&1xFAB&fC3T41rb~*t2-#l*LUXR<^Tfy(LPTv&%8Nj=9~Gpca}5! zJsQs+-hXIXpNu}eO$h-=Y9jLZ(gXg;eF)|+3FgCw@JKub*sl(LOK*^5?4R{Vn$ zJ`F&>p8w5{3J^jv)LgUk)xU{1UQZ=F3%byWOTO~gmFT?+%q_#DVwhN=(|2L_rbhkY^ zXae0O-N@lR@{|0iWDlU^Cj&o%Ege{nKu1k*ii{ir=!DLi5E$6hb2qxkuAuBjr;KPp zbyY>QvacLu&S7ItOnhNCsXiCF~!=J+?7pvvsKl5c|Y4U&Ro@$y2A z-}sh%?aJ3k?TwV0qVoKTn%t_Qs?q{8=+P|4;J2D!2TkI%{vFaa!48^)7V=e4zG@yc z-5+$PD?8DqH{6WNVQzjA?Q-y2mfzzKnqbFFChLyHahhPqOy*SY9zu6Rkse|6 zFhM!aA9jZ;JJ5y0R>mp{W6?4OFW&sppDzynKn=&ErzThpDWr1-dTD~yz*!3}alMU( zK4J7R8hZH}w!5+&DZPNvpX; z*#^=?Ho3BCAMz>r(m3#ik?QA%Vxub?ZTG10>@O^*V}K^u7^EXzK9vpfiIPuE?+pe% zF_HsV&OoLnB%Mbi({UaCj(;Fa)@wdE{cPQ_7>F}9!CIA$-^<6Ye4Nmyai*6pk_Fiz zWSevi3S$tro}Q@D9M0}XJs8Bn4hB=@S%_$YjbsnzcL>hb1ZNc0^U!XK z`N)-z{OAnz)R8&zVL(2F(m2}>#Rsl@kdSo{p|)YNPGgv-tq+YDj&n7^W~mR;Jpv;& zL6KkRi<9O3puEo<#j9c$YK-*q+lMVV@}8{K$Vv3wu@{qp;sNzPi6-*bJZR=cwLAahjKnIvYhjK&3;U=#LN zQZ5%`G{HuYJe5_htnw2!#*1J-ay1&_9>j!=4P&fLSk%D<954_0nqV_`I-^(MaPM)r zgKbY!9!C7NLMl7jM%EARMzmw0*y;0$>?Aa9$@75PrCbY*2i z*5OX-pj1|9ZitpASu~;y<(lA(Wu~%75bnF0(Da^-3lY-&P^o2p%E zNN~hM+-4bysxUWY2Wgsnv?WP4`E?PhHNlc3g0tnVpuE*gl4_46*`!;IyeZ2yY7#vo zn1G8l!IESUBJxkv1_=q0)OzL)##yq=m1T(lXdWo6AK4frOI=yIA4%RcBw21qQtrp^ z4OiYcthbJ=Fnq~pxdfMLg5}EyQus1lt_fBn=YuS9Wr@$1%e^3tB=@ff;R+L^x-hp( zGr>uWOF7^~Owt6)m(k40WCxR3eI>5a1e?LpWZ2cXMiVSwMq>=7;95KvGWagstqC?dF~nto zD+_#1-0g)W&O_-vA>3oaa&H*-T253sn8^X}ljk(<^EgqJrUV0Z00Qu8sHD^F`Y=B2~bJdP(c!KPy(u9T-RCrC)7V~(e75+=)&t~{C0 zrtyT=P=|>!$CWwzrDLv1$I~VqkNU%y!7= z>*X1HM^co8qjJwjr?2({%j#><`+oEI`PC^N|j?}Yx6XKg*^;1#^833i34XRcqvVohjz zpN-eCL=)_aQcu#q5yBhBo26ka<+m#rA+xG1@oeG5kUL$u^I*1cF*IKbvq|M;AuKav z{4)%0D?I&igjfBj+~LX{`$R?VG2y=3#AhMPa=fVtmRIvIPiDyNa+{JFM(B0}w;9Pd zS>8f}CfG>Mgs!HT*0cNzTQtE&Ya=$wRdS_FR&tdQ zy3)X8Be{iTE4FEZ6Cuwxs`l899hzW|7@Ji)>~ye`Vqf8FO|YN$o3ULc$wa9O$|SB~ zWkvD4XkJyd`MejYgD}nJS;<5yVFkN?&GHSt)daid?7%L3hwn9kHnzYHe2X9C3gP+a z2TuuWFNYf^HlQF4j#=?Vjvn}mGt$EntpTFbif0)EOD z#5kWFDs!+)cw*e;Ilh~Y?#7RrU<0vNwZ%`^qY1`i)l!9Jg4E#WpiF2oCSG1OAz=(d z2Yzd&DTZCeJu@uR8CB{6(o{W{Kk~|~C??ok1Gce;A;?QW5%8S8(j8U!0dl|#S zjcQGq+@R!g79HwZFrO2SFTEtQ9-^Qx`!*H_d6e*Z#41tR?d?gSI*m~ z&6S+Qxh++t@aAWvjF5BDU52B#43nX9j+~8b8NzhNd1#)-bISsW@bs{Zo5nh>`XXn^ zU>PLYa;6Ln%E+eUi7O)xa>jHy^snyxTZll$b7+LQ>vCzk zIg~O7LAu~wuDCS6`Lq_4vpCYB7VMx5X6vC=T2}@gu)qW*yJ=Ah%9(sJ3tdnKHjQzx z1u`t7RF@5zt0EgRS4TEwHby?rTpQ_#x{~^0{vS!n_@kieH_z&oxh}GN3-55-%_`}Z zIX7aewjQ6kItN3RZ)0^wuX@S$bgv~nvoQyKm+$bKmhNnBTAPC|%cGp`T@m(&%UqR% z!1&0JQIQ;(8i^vDIR|___ome+m*daQ_yYr@Zpi-HjyE15RK>Y Mss#F{_3P*S8=C@Or2qf` literal 0 HcmV?d00001 diff --git a/src/vs/workbench/parts/execution/electron-browser/terminal.ts b/src/vs/workbench/parts/execution/electron-browser/terminal.ts new file mode 100644 index 0000000000..a983c2d15b --- /dev/null +++ b/src/vs/workbench/parts/execution/electron-browser/terminal.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import env = require('vs/base/common/platform'); +import * as pfs from 'vs/base/node/pfs'; +import { TPromise } from 'vs/base/common/winjs.base'; + +export const DEFAULT_TERMINAL_LINUX_READY = new TPromise(c => { + if (env.isLinux) { + TPromise.join([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => { + if (isDebian) { + c('x-terminal-emulator'); + } else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') { + c('gnome-terminal'); + } else if (process.env.DESKTOP_SESSION === 'kde-plasma') { + c('konsole'); + } else if (process.env.COLORTERM) { + c(process.env.COLORTERM); + } else if (process.env.TERM) { + c(process.env.TERM); + } else { + c('xterm'); + } + }); + return; + } + + c('xterm'); +}); + +export const DEFAULT_TERMINAL_OSX = 'Terminal.app'; + +export const DEFAULT_TERMINAL_WINDOWS = `${process.env.windir}\\${process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') ? 'Sysnative' : 'System32'}\\cmd.exe`; + +export interface ITerminalConfiguration { + terminal: { + explorerKind: 'integrated' | 'external', + external: { + linuxExec: string, + osxExec: string, + windowsExec: string + } + }; +} diff --git a/src/vs/workbench/parts/execution/electron-browser/terminalService.ts b/src/vs/workbench/parts/execution/electron-browser/terminalService.ts new file mode 100644 index 0000000000..57adc5d386 --- /dev/null +++ b/src/vs/workbench/parts/execution/electron-browser/terminalService.ts @@ -0,0 +1,298 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import cp = require('child_process'); +import path = require('path'); +import processes = require('vs/base/node/processes'); +import nls = require('vs/nls'); +import errors = require('vs/base/common/errors'); +import { assign } from 'vs/base/common/objects'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ITerminalService } from 'vs/workbench/parts/execution/common/execution'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITerminalConfiguration, DEFAULT_TERMINAL_WINDOWS, DEFAULT_TERMINAL_LINUX_READY, DEFAULT_TERMINAL_OSX } from 'vs/workbench/parts/execution/electron-browser/terminal'; +import uri from 'vs/base/common/uri'; +import { IProcessEnvironment } from 'vs/base/common/platform'; + +const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console"); + +enum WinSpawnType { + CMD, + CMDER +} + +export class WinTerminalService implements ITerminalService { + public _serviceBrand: any; + + private static CMD = 'cmd.exe'; + + constructor( + @IConfigurationService private _configurationService: IConfigurationService + ) { + } + + public openTerminal(cwd?: string): void { + const configuration = this._configurationService.getConfiguration(); + + this.spawnTerminal(cp, configuration, processes.getWindowsShell(), cwd) + .done(null, errors.onUnexpectedError); + } + + public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): TPromise { + + const configuration = this._configurationService.getConfiguration(); + const terminalConfig = configuration.terminal.external; + const exec = terminalConfig.windowsExec || DEFAULT_TERMINAL_WINDOWS; + + return new TPromise((c, e) => { + + const title = `"${dir} - ${TERMINAL_TITLE}"`; + const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code + + const cmdArgs = [ + '/c', 'start', title, '/wait', exec, '/c', command + ]; + + // merge environment variables into a copy of the process.env + const env = assign({}, process.env, envVars); + + const options: any = { + cwd: dir, + env: env, + windowsVerbatimArguments: true + }; + + const cmd = cp.spawn(WinTerminalService.CMD, cmdArgs, options); + cmd.on('error', e); + + c(null); + }); + } + + private spawnTerminal(spawner, configuration: ITerminalConfiguration, command: string, cwd?: string): TPromise { + const terminalConfig = configuration.terminal.external; + const exec = terminalConfig.windowsExec || DEFAULT_TERMINAL_WINDOWS; + const spawnType = this.getSpawnType(exec); + + // Make the drive letter uppercase on Windows (see #9448) + if (cwd && cwd[1] === ':') { + cwd = cwd[0].toUpperCase() + cwd.substr(1); + } + + // cmder ignores the environment cwd and instead opts to always open in %USERPROFILE% + // unless otherwise specified + if (spawnType === WinSpawnType.CMDER) { + spawner.spawn(exec, [cwd]); + return TPromise.as(void 0); + } + + // The '""' argument is the window title. Without this, exec doesn't work when the path + // contains spaces + const cmdArgs = ['/c', 'start', '/wait', '""', exec]; + + return new TPromise((c, e) => { + const env = cwd ? { cwd: cwd } : void 0; + const child = spawner.spawn(command, cmdArgs, env); + child.on('error', e); + child.on('exit', () => c(null)); + }); + } + + private getSpawnType(exec: string): WinSpawnType { + const basename = path.basename(exec).toLowerCase(); + if (basename === 'cmder' || basename === 'cmder.exe') { + return WinSpawnType.CMDER; + } + return WinSpawnType.CMD; + } +} + +export class MacTerminalService implements ITerminalService { + public _serviceBrand: any; + + private static OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X + + constructor( + @IConfigurationService private _configurationService: IConfigurationService + ) { } + + public openTerminal(cwd?: string): void { + const configuration = this._configurationService.getConfiguration(); + + this.spawnTerminal(cp, configuration, cwd).done(null, errors.onUnexpectedError); + } + + public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): TPromise { + + const configuration = this._configurationService.getConfiguration(); + const terminalConfig = configuration.terminal.external; + const terminalApp = terminalConfig.osxExec || DEFAULT_TERMINAL_OSX; + + return new TPromise((c, e) => { + + if (terminalApp === DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') { + + // On OS X we launch an AppleScript that creates (or reuses) a Terminal window + // and then launches the program inside that window. + + const script = terminalApp === DEFAULT_TERMINAL_OSX ? 'TerminalHelper' : 'iTermHelper'; + const scriptpath = uri.parse(require.toUrl(`vs/workbench/parts/execution/electron-browser/${script}.scpt`)).fsPath; + + const osaArgs = [ + scriptpath, + '-t', title || TERMINAL_TITLE, + '-w', dir, + ]; + + for (let a of args) { + osaArgs.push('-a'); + osaArgs.push(a); + } + + if (envVars) { + for (let key in envVars) { + osaArgs.push('-e'); + osaArgs.push(key + '=' + envVars[key]); + } + } + + let stderr = ''; + const osa = cp.spawn(MacTerminalService.OSASCRIPT, osaArgs); + osa.on('error', e); + osa.stderr.on('data', (data) => { + stderr += data.toString(); + }); + osa.on('exit', (code: number) => { + if (code === 0) { // OK + c(null); + } else { + if (stderr) { + const lines = stderr.split('\n', 1); + e(new Error(lines[0])); + } else { + e(new Error(nls.localize('mac.terminal.script.failed', "Script '{0}' failed with exit code {1}", script, code))); + } + } + }); + } else { + e(new Error(nls.localize('mac.terminal.type.not.supported', "'{0}' not supported", terminalApp))); + } + }); + } + + private spawnTerminal(spawner, configuration: ITerminalConfiguration, cwd?: string): TPromise { + const terminalConfig = configuration.terminal.external; + const terminalApp = terminalConfig.osxExec || DEFAULT_TERMINAL_OSX; + + return new TPromise((c, e) => { + const child = spawner.spawn('/usr/bin/open', ['-a', terminalApp, cwd]); + child.on('error', e); + child.on('exit', () => c(null)); + }); + } +} + +export class LinuxTerminalService implements ITerminalService { + public _serviceBrand: any; + + private static WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue..."); + + constructor( + @IConfigurationService private _configurationService: IConfigurationService + ) { } + + + public openTerminal(cwd?: string): void { + const configuration = this._configurationService.getConfiguration(); + + this.spawnTerminal(cp, configuration, cwd) + .done(null, errors.onUnexpectedError); + } + + public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): TPromise { + + const configuration = this._configurationService.getConfiguration(); + const terminalConfig = configuration.terminal.external; + const execPromise = terminalConfig.linuxExec ? TPromise.as(terminalConfig.linuxExec) : DEFAULT_TERMINAL_LINUX_READY; + + return new TPromise((c, e) => { + + let termArgs: string[] = []; + //termArgs.push('--title'); + //termArgs.push(`"${TERMINAL_TITLE}"`); + execPromise.then(exec => { + if (exec.indexOf('gnome-terminal') >= 0) { + termArgs.push('-x'); + } else { + termArgs.push('-e'); + } + termArgs.push('bash'); + termArgs.push('-c'); + + const bashCommand = `${quote(args)}; echo; read -p "${LinuxTerminalService.WAIT_MESSAGE}" -n1;`; + termArgs.push(`''${bashCommand}''`); // wrapping argument in two sets of ' because node is so "friendly" that it removes one set... + + // merge environment variables into a copy of the process.env + const env = assign({}, process.env, envVars); + + const options: any = { + cwd: dir, + env: env + }; + + let stderr = ''; + const cmd = cp.spawn(exec, termArgs, options); + cmd.on('error', e); + cmd.stderr.on('data', (data) => { + stderr += data.toString(); + }); + cmd.on('exit', (code: number) => { + if (code === 0) { // OK + c(null); + } else { + if (stderr) { + const lines = stderr.split('\n', 1); + e(new Error(lines[0])); + } else { + e(new Error(nls.localize('linux.term.failed', "'{0}' failed with exit code {1}", exec, code))); + } + } + }); + }); + }); + } + + private spawnTerminal(spawner, configuration: ITerminalConfiguration, cwd?: string): TPromise { + const terminalConfig = configuration.terminal.external; + const execPromise = terminalConfig.linuxExec ? TPromise.as(terminalConfig.linuxExec) : DEFAULT_TERMINAL_LINUX_READY; + const env = cwd ? { cwd: cwd } : void 0; + + return new TPromise((c, e) => { + execPromise.then(exec => { + const child = spawner.spawn(exec, [], env); + child.on('error', e); + child.on('exit', () => c(null)); + }); + }); + } +} + +/** + * Quote args if necessary and combine into a space separated string. + */ +function quote(args: string[]): string { + let r = ''; + for (let a of args) { + if (a.indexOf(' ') >= 0) { + r += '"' + a + '"'; + } else { + r += a; + } + r += ' '; + } + return r; +} diff --git a/src/vs/workbench/parts/execution/test/electron-browser/terminalService.test.ts b/src/vs/workbench/parts/execution/test/electron-browser/terminalService.test.ts new file mode 100644 index 0000000000..f5eb0e52e0 --- /dev/null +++ b/src/vs/workbench/parts/execution/test/electron-browser/terminalService.test.ts @@ -0,0 +1,223 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { deepEqual, equal } from 'assert'; +import { WinTerminalService, LinuxTerminalService, MacTerminalService } from 'vs/workbench/parts/execution/electron-browser/terminalService'; +import { DEFAULT_TERMINAL_WINDOWS, DEFAULT_TERMINAL_LINUX_READY, DEFAULT_TERMINAL_OSX } from 'vs/workbench/parts/execution/electron-browser/terminal'; + +suite('Execution - TerminalService', () => { + let mockOnExit; + let mockOnError; + let mockConfig; + + setup(() => { + mockConfig = { + terminal: { + explorerKind: 'external', + external: { + windowsExec: 'testWindowsShell', + osxExec: 'testOSXShell', + linuxExec: 'testLinuxShell' + } + } + }; + mockOnExit = s => s; + mockOnError = e => e; + }); + + test(`WinTerminalService - uses terminal from configuration`, done => { + let testShell = 'cmd'; + let testCwd = 'path/to/workspace'; + let mockSpawner = { + spawn: (command, args, opts) => { + // assert + equal(command, testShell, 'shell should equal expected'); + equal(args[args.length - 1], mockConfig.terminal.external.windowsExec, 'terminal should equal expected'); + equal(opts.cwd, testCwd, 'opts.cwd should equal expected'); + done(); + return { + on: (evt) => evt + }; + } + }; + let testService = new WinTerminalService(mockConfig); + (testService).spawnTerminal( + mockSpawner, + mockConfig, + testShell, + testCwd, + mockOnExit, + mockOnError + ); + }); + + test(`WinTerminalService - uses default terminal when configuration.terminal.external.windowsExec is undefined`, done => { + let testShell = 'cmd'; + let testCwd = 'path/to/workspace'; + let mockSpawner = { + spawn: (command, args, opts) => { + // assert + equal(args[args.length - 1], DEFAULT_TERMINAL_WINDOWS, 'terminal should equal expected'); + done(); + return { + on: (evt) => evt + }; + } + }; + mockConfig.terminal.external.windowsExec = undefined; + let testService = new WinTerminalService(mockConfig); + (testService).spawnTerminal( + mockSpawner, + mockConfig, + testShell, + testCwd, + mockOnExit, + mockOnError + ); + }); + + test(`WinTerminalService - uses default terminal when configuration.terminal.external.windowsExec is undefined`, done => { + let testShell = 'cmd'; + let testCwd = 'c:/foo'; + let mockSpawner = { + spawn: (command, args, opts) => { + // assert + equal(opts.cwd, 'C:/foo', 'cwd should be uppercase regardless of the case that\'s passed in'); + done(); + return { + on: (evt) => evt + }; + } + }; + let testService = new WinTerminalService(mockConfig); + (testService).spawnTerminal( + mockSpawner, + mockConfig, + testShell, + testCwd, + mockOnExit, + mockOnError + ); + }); + + test(`WinTerminalService - cmder should be spawned differently`, done => { + let testShell = 'cmd'; + mockConfig.terminal.external.windowsExec = 'cmder'; + let testCwd = 'c:/foo'; + let mockSpawner = { + spawn: (command, args, opts) => { + // assert + deepEqual(args, ['C:/foo']); + equal(opts, undefined); + done(); + return { on: (evt) => evt }; + } + }; + let testService = new WinTerminalService(mockConfig); + (testService).spawnTerminal( + mockSpawner, + mockConfig, + testShell, + testCwd, + mockOnExit, + mockOnError + ); + }); + + test(`MacTerminalService - uses terminal from configuration`, done => { + let testCwd = 'path/to/workspace'; + let mockSpawner = { + spawn: (command, args, opts) => { + // assert + equal(args[1], mockConfig.terminal.external.osxExec, 'terminal should equal expected'); + done(); + return { + on: (evt) => evt + }; + } + }; + let testService = new MacTerminalService(mockConfig); + (testService).spawnTerminal( + mockSpawner, + mockConfig, + testCwd, + mockOnExit, + mockOnError + ); + }); + + test(`MacTerminalService - uses default terminal when configuration.terminal.external.osxExec is undefined`, done => { + let testCwd = 'path/to/workspace'; + let mockSpawner = { + spawn: (command, args, opts) => { + // assert + equal(args[1], DEFAULT_TERMINAL_OSX, 'terminal should equal expected'); + done(); + return { + on: (evt) => evt + }; + } + }; + mockConfig.terminal.external.osxExec = undefined; + let testService = new MacTerminalService(mockConfig); + (testService).spawnTerminal( + mockSpawner, + mockConfig, + testCwd, + mockOnExit, + mockOnError + ); + }); + + test(`LinuxTerminalService - uses terminal from configuration`, done => { + let testCwd = 'path/to/workspace'; + let mockSpawner = { + spawn: (command, args, opts) => { + // assert + equal(command, mockConfig.terminal.external.linuxExec, 'terminal should equal expected'); + equal(opts.cwd, testCwd, 'opts.cwd should equal expected'); + done(); + return { + on: (evt) => evt + }; + } + }; + let testService = new LinuxTerminalService(mockConfig); + (testService).spawnTerminal( + mockSpawner, + mockConfig, + testCwd, + mockOnExit, + mockOnError + ); + }); + + test(`LinuxTerminalService - uses default terminal when configuration.terminal.external.linuxExec is undefined`, done => { + DEFAULT_TERMINAL_LINUX_READY.then(defaultTerminalLinux => { + let testCwd = 'path/to/workspace'; + let mockSpawner = { + spawn: (command, args, opts) => { + // assert + equal(command, defaultTerminalLinux, 'terminal should equal expected'); + done(); + return { + on: (evt) => evt + }; + } + }; + mockConfig.terminal.external.linuxExec = undefined; + let testService = new LinuxTerminalService(mockConfig); + (testService).spawnTerminal( + mockSpawner, + mockConfig, + testCwd, + mockOnExit, + mockOnError + ); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/dependenciesViewer.ts b/src/vs/workbench/parts/extensions/browser/dependenciesViewer.ts new file mode 100644 index 0000000000..75934e1950 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/dependenciesViewer.ts @@ -0,0 +1,212 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as dom from 'vs/base/browser/dom'; +import { localize } from 'vs/nls'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise, Promise } from 'vs/base/common/winjs.base'; +import { IDataSource, ITree, IRenderer } from 'vs/base/parts/tree/browser/tree'; +import { DefaultController, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults'; +import { Action } from 'vs/base/common/actions'; +import { IExtensionDependencies, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; +import { once } from 'vs/base/common/event'; +import { domEvent } from 'vs/base/browser/event'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; + +export interface IExtensionTemplateData { + icon: HTMLImageElement; + name: HTMLElement; + identifier: HTMLElement; + author: HTMLElement; + extensionDisposables: IDisposable[]; + extensionDependencies: IExtensionDependencies; +} + +export interface IUnknownExtensionTemplateData { + identifier: HTMLElement; +} + +export class DataSource implements IDataSource { + + public getId(tree: ITree, element: IExtensionDependencies): string { + let id = element.identifier; + this.getParent(tree, element).then(parent => { + id = parent ? this.getId(tree, parent) + '/' + id : id; + }); + return id; + } + + public hasChildren(tree: ITree, element: IExtensionDependencies): boolean { + return element.hasDependencies; + } + + public getChildren(tree: ITree, element: IExtensionDependencies): Promise { + return TPromise.as(element.dependencies); + } + + public getParent(tree: ITree, element: IExtensionDependencies): Promise { + return TPromise.as(element.dependent); + } +} + +export class Renderer implements IRenderer { + + private static EXTENSION_TEMPLATE_ID = 'extension-template'; + private static UNKNOWN_EXTENSION_TEMPLATE_ID = 'unknown-extension-template'; + + constructor( @IInstantiationService private instantiationService: IInstantiationService) { + } + + public getHeight(tree: ITree, element: IExtensionDependencies): number { + return 62; + } + + public getTemplateId(tree: ITree, element: IExtensionDependencies): string { + return element.extension ? Renderer.EXTENSION_TEMPLATE_ID : Renderer.UNKNOWN_EXTENSION_TEMPLATE_ID; + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + if (Renderer.EXTENSION_TEMPLATE_ID === templateId) { + return this.renderExtensionTemplate(tree, container); + } + return this.renderUnknownExtensionTemplate(tree, container); + } + + private renderExtensionTemplate(tree: ITree, container: HTMLElement): IExtensionTemplateData { + dom.addClass(container, 'dependency'); + + const icon = dom.append(container, dom.$('img.icon')); + const details = dom.append(container, dom.$('.details')); + + const header = dom.append(details, dom.$('.header')); + const name = dom.append(header, dom.$('span.name')); + const openExtensionAction = this.instantiationService.createInstance(OpenExtensionAction); + const extensionDisposables = [dom.addDisposableListener(name, 'click', (e: MouseEvent) => { + tree.setFocus(openExtensionAction.extensionDependencies); + tree.setSelection([openExtensionAction.extensionDependencies]); + openExtensionAction.run(e.ctrlKey || e.metaKey); + e.stopPropagation(); + e.preventDefault(); + })]; + const identifier = dom.append(header, dom.$('span.identifier')); + + const footer = dom.append(details, dom.$('.footer')); + const author = dom.append(footer, dom.$('.author')); + return { + icon, + name, + identifier, + author, + extensionDisposables, + set extensionDependencies(e: IExtensionDependencies) { + openExtensionAction.extensionDependencies = e; + } + }; + } + + private renderUnknownExtensionTemplate(tree: ITree, container: HTMLElement): IUnknownExtensionTemplateData { + const messageContainer = dom.append(container, dom.$('div.unknown-dependency')); + dom.append(messageContainer, dom.$('span.error-marker')).textContent = localize('error', "Error"); + dom.append(messageContainer, dom.$('span.message')).textContent = localize('Unknown Dependency', "Unknown Dependency:"); + + const identifier = dom.append(messageContainer, dom.$('span.message')); + return { identifier }; + } + + public renderElement(tree: ITree, element: IExtensionDependencies, templateId: string, templateData: any): void { + if (templateId === Renderer.EXTENSION_TEMPLATE_ID) { + this.renderExtension(tree, element, templateData); + return; + } + this.renderUnknownExtension(tree, element, templateData); + } + + private renderExtension(tree: ITree, element: IExtensionDependencies, data: IExtensionTemplateData): void { + const extension = element.extension; + + const onError = once(domEvent(data.icon, 'error')); + onError(() => data.icon.src = extension.iconUrlFallback, null, data.extensionDisposables); + data.icon.src = extension.iconUrl; + + if (!data.icon.complete) { + data.icon.style.visibility = 'hidden'; + data.icon.onload = () => data.icon.style.visibility = 'inherit'; + } else { + data.icon.style.visibility = 'inherit'; + } + + data.name.textContent = extension.displayName; + data.identifier.textContent = extension.id; + data.author.textContent = extension.publisherDisplayName; + data.extensionDependencies = element; + } + + private renderUnknownExtension(tree: ITree, element: IExtensionDependencies, data: IUnknownExtensionTemplateData): void { + data.identifier.textContent = element.identifier; + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + if (templateId === Renderer.EXTENSION_TEMPLATE_ID) { + templateData.extensionDisposables = dispose((templateData).extensionDisposables); + } + } +} + +export class Controller extends DefaultController { + + constructor( @IExtensionsWorkbenchService private extensionsWorkdbenchService: IExtensionsWorkbenchService) { + super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false }); + + // TODO@Sandeep this should be a command + this.downKeyBindingDispatcher.set(KeyMod.CtrlCmd | KeyCode.Enter, (tree: ITree, event: any) => this.openExtension(tree, true)); + } + + protected onLeftClick(tree: ITree, element: IExtensionDependencies, event: IMouseEvent): boolean { + let currentFoucssed = tree.getFocus(); + if (super.onLeftClick(tree, element, event)) { + if (element.dependent === null) { + if (currentFoucssed) { + tree.setFocus(currentFoucssed); + } else { + tree.focusFirst(); + } + return true; + } + } + return false; + } + + public openExtension(tree: ITree, sideByside: boolean): boolean { + const element: IExtensionDependencies = tree.getFocus(); + if (element.extension) { + this.extensionsWorkdbenchService.open(element.extension, sideByside); + return true; + } + return false; + } +} + +class OpenExtensionAction extends Action { + + private _extensionDependencies: IExtensionDependencies; + + constructor( @IExtensionsWorkbenchService private extensionsWorkdbenchService: IExtensionsWorkbenchService) { + super('extensions.action.openDependency', ''); + } + + public set extensionDependencies(extensionDependencies: IExtensionDependencies) { + this._extensionDependencies = extensionDependencies; + } + + public get extensionDependencies(): IExtensionDependencies { + return this._extensionDependencies; + } + + run(sideByside: boolean): TPromise { + return this.extensionsWorkdbenchService.open(this._extensionDependencies.extension, sideByside); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts new file mode 100644 index 0000000000..b01377d441 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts @@ -0,0 +1,910 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/extensionEditor'; +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { marked } from 'vs/base/common/marked/marked'; +import { always } from 'vs/base/common/async'; +import * as arrays from 'vs/base/common/arrays'; +import { OS } from 'vs/base/common/platform'; +import Event, { Emitter, once, fromEventEmitter, chain } from 'vs/base/common/event'; +import Cache from 'vs/base/common/cache'; +import { Action } from 'vs/base/common/actions'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; +import Severity from 'vs/base/common/severity'; +import { IDisposable, empty, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Builder } from 'vs/base/browser/builder'; +import { domEvent } from 'vs/base/browser/event'; +import { append, $, addClass, removeClass, finalHandler, join } from 'vs/base/browser/dom'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionGalleryService, IExtensionManifest, IKeyBinding, IView } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; +import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies } from 'vs/workbench/parts/extensions/common/extensions'; +import { Renderer, DataSource, Controller } from 'vs/workbench/parts/extensions/browser/dependenciesViewer'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITemplateData } from 'vs/workbench/parts/extensions/browser/extensionsList'; +import { RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, BuiltinStatusLabelAction, ReloadAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; +import WebView from 'vs/workbench/parts/html/browser/webview'; +import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { Position } from 'vs/platform/editor/common/editor'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService, RawContextKey, ContextKeyExpr, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { Command, ICommandOptions } from 'vs/editor/common/editorCommonExtensions'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; + +/** A context key that is set when an extension editor webview has focus. */ +export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS = new RawContextKey('extensionEditorWebviewFocus', undefined); +/** A context key that is set when an extension editor webview not have focus. */ +export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS.toNegated(); +/** A context key that is set when the find widget find input in extension editor webview is focused. */ +export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED = new RawContextKey('extensionEditorFindWidgetInputFocused', false); +/** A context key that is set when the find widget find input in extension editor webview is not focused. */ +export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED.toNegated(); + +function renderBody(body: string): string { + return ` + + + + + + + ${body} + `; +} + +function removeEmbeddedSVGs(documentContent: string): string { + const newDocument = new DOMParser().parseFromString(documentContent, 'text/html'); + + // remove all inline svgs + const allSVGs = newDocument.documentElement.querySelectorAll('svg'); + for (let i = 0; i < allSVGs.length; i++) { + allSVGs[i].parentNode.removeChild(allSVGs[i]); + } + + return newDocument.documentElement.outerHTML; +} + +class NavBar { + + private _onChange = new Emitter(); + get onChange(): Event { return this._onChange.event; } + + private currentId: string = null; + private actions: Action[]; + private actionbar: ActionBar; + + constructor(container: HTMLElement) { + const element = append(container, $('.navbar')); + this.actions = []; + this.actionbar = new ActionBar(element, { animated: false }); + } + + push(id: string, label: string): void { + const run = () => this._update(id); + const action = new Action(id, label, null, true, run); + + this.actions.push(action); + this.actionbar.push(action); + + if (this.actions.length === 1) { + run(); + } + } + + clear(): void { + this.actions = dispose(this.actions); + this.actionbar.clear(); + } + + update(): void { + this._update(this.currentId); + } + + _update(id: string = this.currentId): TPromise { + this.currentId = id; + this._onChange.fire(id); + this.actions.forEach(a => a.enabled = a.id !== id); + return TPromise.as(null); + } + + dispose(): void { + this.actionbar = dispose(this.actionbar); + } +} + +const NavbarSection = { + Readme: 'readme', + Contributions: 'contributions', + Changelog: 'changelog', + Dependencies: 'dependencies' +}; + +interface ILayoutParticipant { + layout(): void; +} + +export class ExtensionEditor extends BaseEditor { + + static ID: string = 'workbench.editor.extension'; + + private icon: HTMLImageElement; + private name: HTMLElement; + private identifier: HTMLElement; + private license: HTMLElement; + private publisher: HTMLElement; + private installCount: HTMLElement; + private rating: HTMLElement; + private description: HTMLElement; + private extensionActionBar: ActionBar; + private navbar: NavBar; + private content: HTMLElement; + + private _highlight: ITemplateData; + private highlightDisposable: IDisposable; + + private extensionReadme: Cache; + private extensionChangelog: Cache; + private extensionManifest: Cache; + private extensionDependencies: Cache; + + private contextKey: IContextKey; + private findInputFocusContextKey: IContextKey; + private layoutParticipants: ILayoutParticipant[] = []; + private contentDisposables: IDisposable[] = []; + private transientDisposables: IDisposable[] = []; + private disposables: IDisposable[]; + private activeWebview: WebView; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IExtensionGalleryService private galleryService: IExtensionGalleryService, + @IConfigurationService private configurationService: IConfigurationService, + @IInstantiationService private instantiationService: IInstantiationService, + @IViewletService private viewletService: IViewletService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IThemeService protected themeService: IThemeService, + @IKeybindingService private keybindingService: IKeybindingService, + @IMessageService private messageService: IMessageService, + @IOpenerService private openerService: IOpenerService, + @IListService private listService: IListService, + @IPartService private partService: IPartService, + @IContextViewService private contextViewService: IContextViewService, + @IContextKeyService private contextKeyService: IContextKeyService, + ) { + super(ExtensionEditor.ID, telemetryService, themeService); + this._highlight = null; + this.highlightDisposable = empty; + this.disposables = []; + this.extensionReadme = null; + this.extensionChangelog = null; + this.extensionManifest = null; + this.extensionDependencies = null; + this.contextKey = KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS.bindTo(contextKeyService); + this.findInputFocusContextKey = KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED.bindTo(contextKeyService); + } + + createEditor(parent: Builder): void { + const container = parent.getHTMLElement(); + + const root = append(container, $('.extension-editor')); + const header = append(root, $('.header')); + + this.icon = append(header, $('img.icon', { draggable: false })); + + const details = append(header, $('.details')); + const title = append(details, $('.title')); + this.name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") })); + this.identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") })); + + const subtitle = append(details, $('.subtitle')); + this.publisher = append(subtitle, $('span.publisher.clickable', { title: localize('publisher', "Publisher name") })); + + this.installCount = append(subtitle, $('span.install', { title: localize('install count', "Install count") })); + + this.rating = append(subtitle, $('span.rating.clickable', { title: localize('rating', "Rating") })); + + this.license = append(subtitle, $('span.license.clickable')); + this.license.textContent = localize('license', 'License'); + this.license.style.display = 'none'; + + this.description = append(details, $('.description')); + + const extensionActions = append(details, $('.actions')); + this.extensionActionBar = new ActionBar(extensionActions, { + animated: false, + actionItemProvider: (action: Action) => { + if (action.id === EnableAction.ID) { + return (action).actionItem; + } + if (action.id === DisableAction.ID) { + return (action).actionItem; + } + return null; + } + }); + this.disposables.push(this.extensionActionBar); + + chain(fromEventEmitter<{ error?: any; }>(this.extensionActionBar, 'run')) + .map(({ error }) => error) + .filter(error => !!error) + .on(this.onError, this, this.disposables); + + const body = append(root, $('.body')); + this.navbar = new NavBar(body); + + this.content = append(body, $('.content')); + } + + setInput(input: ExtensionsInput, options: EditorOptions): TPromise { + const extension = input.extension; + + this.transientDisposables = dispose(this.transientDisposables); + + this.telemetryService.publicLog('extensionGallery:openExtension', extension.telemetryData); + + this.extensionReadme = new Cache(() => extension.getReadme()); + this.extensionChangelog = new Cache(() => extension.getChangelog()); + this.extensionManifest = new Cache(() => extension.getManifest()); + this.extensionDependencies = new Cache(() => this.extensionsWorkbenchService.loadDependencies(extension)); + + const onError = once(domEvent(this.icon, 'error')); + onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables); + this.icon.src = extension.iconUrl; + + this.name.textContent = extension.displayName; + this.identifier.textContent = extension.id; + + this.publisher.textContent = extension.publisherDisplayName; + this.description.textContent = extension.description; + + if (extension.url) { + this.name.onclick = finalHandler(() => window.open(extension.url)); + this.rating.onclick = finalHandler(() => window.open(`${extension.url}#review-details`)); + this.publisher.onclick = finalHandler(() => { + this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .done(viewlet => viewlet.search(`publisher:"${extension.publisherDisplayName}"`)); + }); + + if (extension.licenseUrl) { + this.license.onclick = finalHandler(() => window.open(extension.licenseUrl)); + this.license.style.display = 'initial'; + } else { + this.license.onclick = null; + this.license.style.display = 'none'; + } + } + + const install = this.instantiationService.createInstance(InstallWidget, this.installCount, { extension }); + this.transientDisposables.push(install); + + const ratings = this.instantiationService.createInstance(RatingsWidget, this.rating, { extension }); + this.transientDisposables.push(ratings); + + const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction); + const installAction = this.instantiationService.createInstance(CombinedInstallAction); + const updateAction = this.instantiationService.createInstance(UpdateAction); + const enableAction = this.instantiationService.createInstance(EnableAction); + const disableAction = this.instantiationService.createInstance(DisableAction); + const reloadAction = this.instantiationService.createInstance(ReloadAction); + + installAction.extension = extension; + builtinStatusAction.extension = extension; + updateAction.extension = extension; + enableAction.extension = extension; + disableAction.extension = extension; + reloadAction.extension = extension; + + this.extensionActionBar.clear(); + this.extensionActionBar.push([reloadAction, updateAction, enableAction, disableAction, installAction, builtinStatusAction], { icon: true, label: true }); + this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, builtinStatusAction); + + this.navbar.clear(); + this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables); + this.navbar.push(NavbarSection.Readme, localize('details', "Details")); + this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions")); + this.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog")); + this.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies")); + + this.content.innerHTML = ''; + + return super.setInput(input, options); + } + + changePosition(position: Position): void { + this.navbar.update(); + super.changePosition(position); + } + + showFind(): void { + if (this.activeWebview) { + this.activeWebview.showFind(); + } + } + + hideFind(): void { + if (this.activeWebview) { + this.activeWebview.hideFind(); + } + } + + public showNextFindTerm() { + if (this.activeWebview) { + this.activeWebview.showNextFindTerm(); + } + } + + public showPreviousFindTerm() { + if (this.activeWebview) { + this.activeWebview.showPreviousFindTerm(); + } + } + + private onNavbarChange(extension: IExtension, id: string): void { + this.contentDisposables = dispose(this.contentDisposables); + this.content.innerHTML = ''; + this.activeWebview = null; + switch (id) { + case NavbarSection.Readme: return this.openReadme(); + case NavbarSection.Contributions: return this.openContributions(); + case NavbarSection.Changelog: return this.openChangelog(); + case NavbarSection.Dependencies: return this.openDependencies(extension); + } + } + + private openMarkdown(content: TPromise, noContentCopy: string) { + return this.loadContents(() => content + .then(marked.parse) + .then(renderBody) + .then(removeEmbeddedSVGs) + .then(body => { + const allowedBadgeProviders = this.extensionsWorkbenchService.allowedBadgeProviders; + const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : undefined; + this.activeWebview = new WebView(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions); + const removeLayoutParticipant = arrays.insert(this.layoutParticipants, this.activeWebview); + this.contentDisposables.push(toDisposable(removeLayoutParticipant)); + + this.activeWebview.style(this.themeService.getTheme()); + this.activeWebview.contents = [body]; + + this.activeWebview.onDidClickLink(link => { + // Whitelist supported schemes for links + if (link && ['http', 'https', 'mailto'].indexOf(link.scheme) >= 0) { + this.openerService.open(link); + } + }, null, this.contentDisposables); + this.themeService.onThemeChange(theme => this.activeWebview.style(theme), null, this.contentDisposables); + this.contentDisposables.push(this.activeWebview); + }) + .then(null, () => { + const p = append(this.content, $('p.nocontent')); + p.textContent = noContentCopy; + })); + } + + private openReadme() { + return this.openMarkdown(this.extensionReadme.get(), localize('noReadme', "No README available.")); + } + + private openChangelog() { + return this.openMarkdown(this.extensionChangelog.get(), localize('noChangelog', "No Changelog available.")); + } + + private openContributions() { + return this.loadContents(() => this.extensionManifest.get() + .then(manifest => { + const content = $('div', { class: 'subcontent' }); + const scrollableContent = new DomScrollableElement(content, {}); + + const layout = () => scrollableContent.scanDomNode(); + const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout }); + this.contentDisposables.push(toDisposable(removeLayoutParticipant)); + + const renders = [ + this.renderSettings(content, manifest, layout), + this.renderCommands(content, manifest, layout), + this.renderLanguages(content, manifest, layout), + this.renderThemes(content, manifest, layout), + this.renderJSONValidation(content, manifest, layout), + this.renderDebuggers(content, manifest, layout), + this.renderViews(content, manifest, layout) + ]; + + const isEmpty = !renders.reduce((v, r) => r || v, false); + scrollableContent.scanDomNode(); + + if (isEmpty) { + append(this.content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions"); + return; + } else { + append(this.content, scrollableContent.getDomNode()); + this.contentDisposables.push(scrollableContent); + } + })); + } + + private openDependencies(extension: IExtension) { + if (extension.dependencies.length === 0) { + append(this.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies"); + return; + } + + return this.loadContents(() => { + return this.extensionDependencies.get().then(extensionDependencies => { + const content = $('div', { class: 'subcontent' }); + const scrollableContent = new DomScrollableElement(content, {}); + append(this.content, scrollableContent.getDomNode()); + this.contentDisposables.push(scrollableContent); + + const tree = this.renderDependencies(content, extensionDependencies); + const layout = () => { + scrollableContent.scanDomNode(); + const scrollDimensions = scrollableContent.getScrollDimensions(); + tree.layout(scrollDimensions.height); + }; + const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout }); + this.contentDisposables.push(toDisposable(removeLayoutParticipant)); + + this.contentDisposables.push(tree); + scrollableContent.scanDomNode(); + }, error => { + append(this.content, $('p.nocontent')).textContent = error; + this.messageService.show(Severity.Error, error); + }); + }); + } + + private renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies): Tree { + const renderer = this.instantiationService.createInstance(Renderer); + const controller = this.instantiationService.createInstance(Controller); + const tree = new Tree(container, { + dataSource: new DataSource(), + renderer, + controller + }, { + indentPixels: 40, + twistiePixels: 20, + keyboardSupport: false + }); + + this.contentDisposables.push(attachListStyler(tree, this.themeService)); + + tree.setInput(extensionDependencies); + + this.contentDisposables.push(tree.addListener('selection', event => { + if (event && event.payload && event.payload.origin === 'keyboard') { + controller.openExtension(tree, false); + } + })); + + this.contentDisposables.push(this.listService.register(tree)); + + return tree; + } + + private renderSettings(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const contributes = manifest.contributes; + const configuration = contributes && contributes.configuration; + const properties = configuration && configuration.properties; + const contrib = properties ? Object.keys(properties) : []; + + if (!contrib.length) { + return false; + } + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', null, localize('settings', "Settings ({0})", contrib.length)), + $('table', null, + $('tr', null, + $('th', null, localize('setting name', "Name")), + $('th', null, localize('description', "Description")), + $('th', null, localize('default', "Default")) + ), + ...contrib.map(key => $('tr', null, + $('td', null, $('code', null, key)), + $('td', null, properties[key].description), + $('td', null, $('code', null, properties[key].default)) + )) + ) + ); + + append(container, details); + return true; + } + + private renderDebuggers(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const contributes = manifest.contributes; + const contrib = contributes && contributes.debuggers || []; + + if (!contrib.length) { + return false; + } + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', null, localize('debuggers', "Debuggers ({0})", contrib.length)), + $('table', null, + $('tr', null, + $('th', null, localize('debugger name', "Name")), + $('th', null, localize('debugger type', "Type")), + ), + ...contrib.map(d => $('tr', null, + $('td', null, d.label), + $('td', null, d.type))) + ) + ); + + append(container, details); + return true; + } + + private renderViews(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const contributes = manifest.contributes; + const contrib = contributes && contributes.views || {}; + + let views = <{ id: string, name: string, location: string }[]>Object.keys(contrib).reduce((result, location) => { + let viewsForLocation: IView[] = contrib[location]; + result.push(...viewsForLocation.map(view => ({ ...view, location }))); + return result; + }, []); + + if (!views.length) { + return false; + } + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', null, localize('views', "Views ({0})", views.length)), + $('table', null, + $('tr', null, $('th', null, localize('view id', "ID")), $('th', null, localize('view name', "Name")), $('th', null, localize('view location', "Where"))), + ...views.map(view => $('tr', null, $('td', null, view.id), $('td', null, view.name), $('td', null, view.location))) + ) + ); + + append(container, details); + return true; + } + + private renderThemes(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const contributes = manifest.contributes; + const contrib = contributes && contributes.themes || []; + + if (!contrib.length) { + return false; + } + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', null, localize('themes', "Themes ({0})", contrib.length)), + $('ul', null, ...contrib.map(theme => $('li', null, theme.label))) + ); + + append(container, details); + return true; + } + + private renderJSONValidation(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const contributes = manifest.contributes; + const contrib = contributes && contributes.jsonValidation || []; + + if (!contrib.length) { + return false; + } + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', null, localize('JSON Validation', "JSON Validation ({0})", contrib.length)), + $('ul', null, ...contrib.map(v => $('li', null, v.fileMatch))) + ); + + append(container, details); + return true; + } + + private renderCommands(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const contributes = manifest.contributes; + const rawCommands = contributes && contributes.commands || []; + const commands = rawCommands.map(c => ({ + id: c.command, + title: c.title, + keybindings: [], + menus: [] + })); + + const byId = arrays.index(commands, c => c.id); + + const menus = contributes && contributes.menus || {}; + + Object.keys(menus).forEach(context => { + menus[context].forEach(menu => { + let command = byId[menu.command]; + + if (!command) { + command = { id: menu.command, title: '', keybindings: [], menus: [context] }; + byId[command.id] = command; + commands.push(command); + } else { + command.menus.push(context); + } + }); + }); + + const rawKeybindings = contributes && contributes.keybindings || []; + + rawKeybindings.forEach(rawKeybinding => { + const keybinding = this.resolveKeybinding(rawKeybinding); + + if (!keybinding) { + return; + } + + let command = byId[rawKeybinding.command]; + + if (!command) { + command = { id: rawKeybinding.command, title: '', keybindings: [keybinding], menus: [] }; + byId[command.id] = command; + commands.push(command); + } else { + command.keybindings.push(keybinding); + } + }); + + if (!commands.length) { + return false; + } + + const renderKeybinding = (keybinding: ResolvedKeybinding): HTMLElement => { + const element = $(''); + new KeybindingLabel(element, OS).set(keybinding, null); + return element; + }; + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', null, localize('commands', "Commands ({0})", commands.length)), + $('table', null, + $('tr', null, + $('th', null, localize('command name', "Name")), + $('th', null, localize('description', "Description")), + $('th', null, localize('keyboard shortcuts', "Keyboard Shortcuts")), + $('th', null, localize('menuContexts', "Menu Contexts")) + ), + ...commands.map(c => $('tr', null, + $('td', null, $('code', null, c.id)), + $('td', null, c.title), + $('td', null, ...c.keybindings.map(keybinding => renderKeybinding(keybinding))), + $('td', null, ...c.menus.map(context => $('code', null, context))) + )) + ) + ); + + append(container, details); + return true; + } + + private renderLanguages(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const contributes = manifest.contributes; + const rawLanguages = contributes && contributes.languages || []; + const languages = rawLanguages.map(l => ({ + id: l.id, + name: (l.aliases || [])[0] || l.id, + extensions: l.extensions || [], + hasGrammar: false, + hasSnippets: false + })); + + const byId = arrays.index(languages, l => l.id); + + const grammars = contributes && contributes.grammars || []; + + grammars.forEach(grammar => { + let language = byId[grammar.language]; + + if (!language) { + language = { id: grammar.language, name: grammar.language, extensions: [], hasGrammar: true, hasSnippets: false }; + byId[language.id] = language; + languages.push(language); + } else { + language.hasGrammar = true; + } + }); + + const snippets = contributes && contributes.snippets || []; + + snippets.forEach(snippet => { + let language = byId[snippet.language]; + + if (!language) { + language = { id: snippet.language, name: snippet.language, extensions: [], hasGrammar: false, hasSnippets: true }; + byId[language.id] = language; + languages.push(language); + } else { + language.hasSnippets = true; + } + }); + + if (!languages.length) { + return false; + } + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', null, localize('languages', "Languages ({0})", languages.length)), + $('table', null, + $('tr', null, + $('th', null, localize('language id', "ID")), + $('th', null, localize('language name', "Name")), + $('th', null, localize('file extensions', "File Extensions")), + $('th', null, localize('grammar', "Grammar")), + $('th', null, localize('snippets', "Snippets")) + ), + ...languages.map(l => $('tr', null, + $('td', null, l.id), + $('td', null, l.name), + $('td', null, ...join(l.extensions.map(ext => $('code', null, ext)), ' ')), + $('td', null, document.createTextNode(l.hasGrammar ? '✔︎' : '—')), + $('td', null, document.createTextNode(l.hasSnippets ? '✔︎' : '—')) + )) + ) + ); + + append(container, details); + return true; + } + + private resolveKeybinding(rawKeyBinding: IKeyBinding): ResolvedKeybinding { + let key: string; + + switch (process.platform) { + case 'win32': key = rawKeyBinding.win; break; + case 'linux': key = rawKeyBinding.linux; break; + case 'darwin': key = rawKeyBinding.mac; break; + } + + const keyBinding = KeybindingIO.readKeybinding(key || rawKeyBinding.key, OS); + if (!keyBinding) { + return null; + } + + return this.keybindingService.resolveKeybinding(keyBinding)[0]; + } + + private loadContents(loadingTask: () => TPromise): void { + addClass(this.content, 'loading'); + + let promise = loadingTask(); + promise = always(promise, () => removeClass(this.content, 'loading')); + + this.contentDisposables.push(toDisposable(() => promise.cancel())); + } + + layout(): void { + this.layoutParticipants.forEach(p => p.layout()); + } + + private onError(err: any): void { + if (isPromiseCanceledError(err)) { + return; + } + + this.messageService.show(Severity.Error, err); + } + + dispose(): void { + this._highlight = null; + this.transientDisposables = dispose(this.transientDisposables); + this.disposables = dispose(this.disposables); + super.dispose(); + } +} + +class ShowExtensionEditorFindCommand extends Command { + public runCommand(accessor: ServicesAccessor, args: any): void { + const extensionEditor = this.getExtensionEditor(accessor); + if (extensionEditor) { + extensionEditor.showFind(); + } + } + + private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor { + const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as ExtensionEditor; + if (activeEditor instanceof ExtensionEditor) { + return activeEditor; + } + return null; + } +} +const showCommand = new ShowExtensionEditorFindCommand({ + id: 'editor.action.extensioneditor.showfind', + precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS, + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(showCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +class HideExtensionEditorFindCommand extends Command { + public runCommand(accessor: ServicesAccessor, args: any): void { + const extensionEditor = this.getExtensionEditor(accessor); + if (extensionEditor) { + extensionEditor.hideFind(); + } + } + + private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor { + const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as ExtensionEditor; + if (activeEditor instanceof ExtensionEditor) { + return activeEditor; + } + return null; + } +} +const hideCommand = new ShowExtensionEditorFindCommand({ + id: 'editor.action.extensioneditor.hidefind', + precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS, + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(hideCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +class ShowExtensionEditorFindTermCommand extends Command { + constructor(opts: ICommandOptions, private _next: boolean) { + super(opts); + } + + public runCommand(accessor: ServicesAccessor, args: any): void { + const extensionEditor = this.getExtensionEditor(accessor); + if (extensionEditor) { + if (this._next) { + extensionEditor.showNextFindTerm(); + } else { + extensionEditor.showPreviousFindTerm(); + } + } + } + + private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor { + const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as ExtensionEditor; + if (activeEditor instanceof ExtensionEditor) { + return activeEditor; + } + return null; + } +} + +const showNextFindTermCommand = new ShowExtensionEditorFindTermCommand({ + id: 'editor.action.extensioneditor.showNextFindTerm', + precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED, + kbOpts: { + primary: KeyMod.Alt | KeyCode.DownArrow + } +}, true); +KeybindingsRegistry.registerCommandAndKeybindingRule(showNextFindTermCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +const showPreviousFindTermCommand = new ShowExtensionEditorFindTermCommand({ + id: 'editor.action.extensioneditor.showPreviousFindTerm', + precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED, + kbOpts: { + primary: KeyMod.Alt | KeyCode.UpArrow + } +}, false); +KeybindingsRegistry.registerCommandAndKeybindingRule(showPreviousFindTermCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts new file mode 100644 index 0000000000..9c9237a1c5 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -0,0 +1,1512 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/extensionActions'; +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction, Action } from 'vs/base/common/actions'; +import { Throttler } from 'vs/base/common/async'; +import * as DOM from 'vs/base/browser/dom'; +import severity from 'vs/base/common/severity'; +import paths = require('vs/base/common/paths'); +import Event from 'vs/base/common/event'; +import { ActionItem, IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions'; +import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate'; +import { LocalExtensionType, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { ToggleViewletAction } from 'vs/workbench/browser/viewlet'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IExtensionService, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import URI from 'vs/base/common/uri'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { buttonBackground, buttonForeground, buttonHoverBackground, contrastBorder, registerColor, foreground } from 'vs/platform/theme/common/colorRegistry'; +import { Color } from 'vs/base/common/color'; + +export class InstallAction extends Action { + + private static InstallLabel = localize('installAction', "Install"); + private static InstallingLabel = localize('installing', "Installing"); + + private static Class = 'extension-action prominent install'; + private static InstallingClass = 'extension-action install installing'; + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor( + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super('extensions.install', InstallAction.InstallLabel, InstallAction.Class, false); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + if (!this.extension || this.extension.type === LocalExtensionType.System) { + this.enabled = false; + this.class = InstallAction.Class; + this.label = InstallAction.InstallLabel; + return; + } + + this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && this.extension.state === ExtensionState.Uninstalled; + + if (this.extension.state === ExtensionState.Installing) { + this.label = InstallAction.InstallingLabel; + this.class = InstallAction.InstallingClass; + } else { + this.label = InstallAction.InstallLabel; + this.class = InstallAction.Class; + } + } + + run(): TPromise { + return this.extensionsWorkbenchService.install(this.extension); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class UninstallAction extends Action { + + private static UninstallLabel = localize('uninstallAction', "Uninstall"); + private static UninstallingLabel = localize('Uninstalling', "Uninstalling"); + + private static UninstallClass = 'extension-action uninstall'; + private static UnInstallingClass = 'extension-action uninstall uninstalling'; + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor( + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IMessageService private messageService: IMessageService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + if (!this.extension) { + this.enabled = false; + return; + } + + const state = this.extension.state; + + if (state === ExtensionState.Uninstalling) { + this.label = UninstallAction.UninstallingLabel; + this.class = UninstallAction.UnInstallingClass; + this.enabled = false; + return; + } + + this.label = UninstallAction.UninstallLabel; + this.class = UninstallAction.UninstallClass; + + const installedExtensions = this.extensionsWorkbenchService.local.filter(e => e.id === this.extension.id); + + if (!installedExtensions.length) { + this.enabled = false; + return; + } + + if (installedExtensions[0].type !== LocalExtensionType.User) { + this.enabled = false; + return; + } + + this.enabled = true; + } + + run(): TPromise { + return this.extensionsWorkbenchService.uninstall(this.extension); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class CombinedInstallAction extends Action { + + private static NoExtensionClass = 'extension-action prominent install no-extension'; + private installAction: InstallAction; + private uninstallAction: UninstallAction; + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { + this._extension = extension; + this.installAction.extension = extension; + this.uninstallAction.extension = extension; + } + + constructor( + @IInstantiationService instantiationService: IInstantiationService + ) { + super('extensions.combinedInstall', '', '', false); + + this.installAction = instantiationService.createInstance(InstallAction); + this.uninstallAction = instantiationService.createInstance(UninstallAction); + this.disposables.push(this.installAction, this.uninstallAction); + + this.installAction.onDidChange(this.update, this, this.disposables); + this.uninstallAction.onDidChange(this.update, this, this.disposables); + this.update(); + } + + private update(): void { + if (!this.extension || this.extension.type === LocalExtensionType.System) { + this.enabled = false; + this.class = CombinedInstallAction.NoExtensionClass; + } else if (this.installAction.enabled) { + this.enabled = true; + this.label = this.installAction.label; + this.class = this.installAction.class; + } else if (this.uninstallAction.enabled) { + this.enabled = true; + this.label = this.uninstallAction.label; + this.class = this.uninstallAction.class; + } else if (this.extension.state === ExtensionState.Installing) { + this.enabled = false; + this.label = this.installAction.label; + this.class = this.installAction.class; + } else if (this.extension.state === ExtensionState.Uninstalling) { + this.enabled = false; + this.label = this.uninstallAction.label; + this.class = this.uninstallAction.class; + } else { + this.enabled = false; + this.label = this.installAction.label; + this.class = this.installAction.class; + } + } + + run(): TPromise { + if (this.installAction.enabled) { + return this.installAction.run(); + } else if (this.uninstallAction.enabled) { + return this.uninstallAction.run(); + } + + return TPromise.as(null); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class UpdateAction extends Action { + + private static EnabledClass = 'extension-action prominent update'; + private static DisabledClass = `${UpdateAction.EnabledClass} disabled`; + private static Label = localize('updateAction', "Update"); + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor( + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super('extensions.update', UpdateAction.Label, UpdateAction.DisabledClass, false); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + if (!this.extension) { + this.enabled = false; + this.class = UpdateAction.DisabledClass; + this.label = UpdateAction.Label; + return; + } + + if (this.extension.type !== LocalExtensionType.User) { + this.enabled = false; + this.class = UpdateAction.DisabledClass; + this.label = UpdateAction.Label; + return; + } + + const canInstall = this.extensionsWorkbenchService.canInstall(this.extension); + const isInstalled = this.extension.state === ExtensionState.Installed; + + this.enabled = canInstall && isInstalled && this.extension.outdated; + this.class = this.enabled ? UpdateAction.EnabledClass : UpdateAction.DisabledClass; + this.label = localize('updateTo', "Update to {0}", this.extension.latestVersion); + } + + run(): TPromise { + return this.extensionsWorkbenchService.install(this.extension); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export interface IExtensionAction extends IAction { + extension: IExtension; +} + +export class DropDownMenuActionItem extends ActionItem { + + private disposables: IDisposable[] = []; + private _extension: IExtension; + + constructor(action: IAction, private menuActionGroups: IExtensionAction[][], + @IContextMenuService private contextMenuService: IContextMenuService + ) { + super(null, action, { icon: true, label: true }); + for (const menuActions of menuActionGroups) { + this.disposables = [...this.disposables, ...menuActions]; + } + } + + get extension(): IExtension { return this._extension; } + + set extension(extension: IExtension) { + this._extension = extension; + for (const menuActions of this.menuActionGroups) { + for (const menuAction of menuActions) { + menuAction.extension = extension; + } + } + } + + public showMenu(): void { + const actions = this.getActions(); + let elementPosition = DOM.getDomNodePagePosition(this.builder.getHTMLElement()); + const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.wrap(actions), + actionRunner: this.actionRunner + }); + } + + private getActions(): IAction[] { + let actions: IAction[] = []; + const menuActionGroups = this.menuActionGroups.filter(group => group.some(action => action.enabled)); + for (const menuActions of menuActionGroups) { + actions = [...actions, ...menuActions, new Separator()]; + } + return actions.length ? actions.slice(0, actions.length - 1) : actions; + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class ManageExtensionAction extends Action { + + static ID = 'extensions.manage'; + + private static Class = 'extension-action manage'; + private static HideManageExtensionClass = `${ManageExtensionAction.Class} hide`; + + private _actionItem: DropDownMenuActionItem; + get actionItem(): IActionItem { return this._actionItem; } + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); } + + constructor( + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(ManageExtensionAction.ID); + + this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [ + [ + instantiationService.createInstance(EnableForWorkspaceAction, localize('enableForWorkspaceAction.label', "Enable (Workspace)")), + instantiationService.createInstance(EnableGloballyAction, localize('enableAlwaysAction.label', "Enable (Always)")) + ], + [ + instantiationService.createInstance(DisableForWorkspaceAction, localize('disableForWorkspaceAction.label', "Disable (Workspace)")), + instantiationService.createInstance(DisableGloballyAction, localize('disableAlwaysAction.label', "Disable (Always)")) + ], + [ + instantiationService.createInstance(UninstallAction) + ] + ]); + this.disposables.push(this._actionItem); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.class = ManageExtensionAction.HideManageExtensionClass; + this.tooltip = ''; + this.enabled = false; + if (this.extension && this.extension.type !== LocalExtensionType.System) { + const state = this.extension.state; + this.enabled = state === ExtensionState.Installed; + this.class = this.enabled || state === ExtensionState.Uninstalling ? ManageExtensionAction.Class : ManageExtensionAction.HideManageExtensionClass; + this.tooltip = state === ExtensionState.Uninstalling ? localize('ManageExtensionAction.uninstallingTooltip', "Uninstalling") : ''; + } + } + + public run(): TPromise { + this._actionItem.showMenu(); + return TPromise.wrap(null); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableForWorkspaceAction extends Action implements IExtensionAction { + + static ID = 'extensions.enableForWorkspace'; + static LABEL = localize('enableForWorkspaceAction', "Workspace"); + + private disposables: IDisposable[] = []; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor(label: string, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(EnableForWorkspaceAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = false; + if (this.extension) { + this.enabled = !this.extension.disabledGlobally && this.extension.disabledForWorkspace && this.extensionEnablementService.canEnable(this.extension.id); + } + } + + run(): TPromise { + return this.extensionsWorkbenchService.setEnablement(this.extension, true, true); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableGloballyAction extends Action implements IExtensionAction { + + static ID = 'extensions.enableGlobally'; + static LABEL = localize('enableGloballyAction', "Always"); + + private disposables: IDisposable[] = []; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor(label: string, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(EnableGloballyAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = false; + if (this.extension) { + this.enabled = this.extension.disabledGlobally && this.extensionEnablementService.canEnable(this.extension.id); + } + } + + run(): TPromise { + return this.extensionsWorkbenchService.setEnablement(this.extension, true, false); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableAction extends Action { + + static ID = 'extensions.enable'; + private static EnabledClass = 'extension-action prominent enable'; + private static DisabledClass = `${EnableAction.EnabledClass} disabled`; + + private disposables: IDisposable[] = []; + + private _actionItem: DropDownMenuActionItem; + get actionItem(): IActionItem { return this._actionItem; } + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); } + + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + ) { + super(EnableAction.ID, localize('enableAction', "Enable"), EnableAction.DisabledClass, false); + + this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [ + [ + instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL), + instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL) + ] + ]); + this.disposables.push(this._actionItem); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + if (!this.extension) { + this.enabled = false; + this.class = EnableAction.DisabledClass; + return; + } + + this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.disabledGlobally || this.extension.disabledForWorkspace) && this.extensionEnablementService.canEnable(this.extension.id); + this.class = this.enabled ? EnableAction.EnabledClass : EnableAction.DisabledClass; + } + + public run(): TPromise { + this._actionItem.showMenu(); + return TPromise.wrap(null); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } + +} + +export class DisableForWorkspaceAction extends Action implements IExtensionAction { + + static ID = 'extensions.disableForWorkspace'; + static LABEL = localize('disableForWorkspaceAction', "Workspace"); + + private disposables: IDisposable[] = []; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor(label: string, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(DisableForWorkspaceAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = false; + if (this.extension && this.workspaceContextService.hasWorkspace()) { + this.enabled = this.extension.type !== LocalExtensionType.System && !this.extension.disabledGlobally && !this.extension.disabledForWorkspace; + } + } + + run(): TPromise { + return this.extensionsWorkbenchService.setEnablement(this.extension, false, true); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class DisableGloballyAction extends Action implements IExtensionAction { + + static ID = 'extensions.disableGlobally'; + static LABEL = localize('disableGloballyAction', "Always"); + + private disposables: IDisposable[] = []; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor(label: string, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(DisableGloballyAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = false; + if (this.extension) { + this.enabled = this.extension.type !== LocalExtensionType.System && !this.extension.disabledGlobally && !this.extension.disabledForWorkspace; + } + } + + run(): TPromise { + return this.extensionsWorkbenchService.setEnablement(this.extension, false, false); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class DisableAction extends Action { + + static ID = 'extensions.disable'; + + private static EnabledClass = 'extension-action disable'; + private static DisabledClass = `${DisableAction.EnabledClass} disabled`; + + private disposables: IDisposable[] = []; + private _actionItem: DropDownMenuActionItem; + get actionItem(): IActionItem { return this._actionItem; } + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); } + + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + ) { + super(DisableAction.ID, localize('disableAction', "Disable"), DisableAction.DisabledClass, false); + this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [ + [ + instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL), + instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL) + ] + ]); + this.disposables.push(this._actionItem); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + if (!this.extension) { + this.enabled = false; + this.class = DisableAction.DisabledClass; + return; + } + + this.enabled = this.extension.state === ExtensionState.Installed && this.extension.type !== LocalExtensionType.System && !this.extension.disabledGlobally && !this.extension.disabledForWorkspace; + this.class = this.enabled ? DisableAction.EnabledClass : DisableAction.DisabledClass; + } + + public run(): TPromise { + this._actionItem.showMenu(); + return TPromise.wrap(null); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class CheckForUpdatesAction extends Action { + + static ID = 'workbench.extensions.action.checkForUpdates'; + static LABEL = localize('checkForUpdates', "Check for Updates"); + + constructor( + id = UpdateAllAction.ID, + label = UpdateAllAction.LABEL, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, '', true); + } + + run(): TPromise { + return this.extensionsWorkbenchService.checkForUpdates(); + } +} + +export class ToggleAutoUpdateAction extends Action { + + constructor( + id: string, + label: string, + private autoUpdateValue: boolean, + @IConfigurationService configurationService: IConfigurationService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, '', true); + this.updateEnablement(); + configurationService.onDidUpdateConfiguration(() => this.updateEnablement()); + } + + private updateEnablement(): void { + this.enabled = this.extensionsWorkbenchService.isAutoUpdateEnabled !== this.autoUpdateValue; + } + + run(): TPromise { + return this.extensionsWorkbenchService.setAutoUpdate(this.autoUpdateValue); + } +} + +export class EnableAutoUpdateAction extends ToggleAutoUpdateAction { + + static ID = 'workbench.extensions.action.enableAutoUpdate'; + static LABEL = localize('enableAutoUpdate', "Enable Auto Updating Extensions"); + + constructor( + id = EnableAutoUpdateAction.ID, + label = EnableAutoUpdateAction.LABEL, + @IConfigurationService configurationService: IConfigurationService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, true, configurationService, extensionsWorkbenchService); + } +} + +export class DisableAutoUpdateAction extends ToggleAutoUpdateAction { + + static ID = 'workbench.extensions.action.disableAutoUpdate'; + static LABEL = localize('disableAutoUpdate', "Disable Auto Updating Extensions"); + + constructor( + id = EnableAutoUpdateAction.ID, + label = EnableAutoUpdateAction.LABEL, + @IConfigurationService configurationService: IConfigurationService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, false, configurationService, extensionsWorkbenchService); + } +} + +export class UpdateAllAction extends Action { + + static ID = 'workbench.extensions.action.updateAllExtensions'; + static LABEL = localize('updateAll', "Update All Extensions"); + + private disposables: IDisposable[] = []; + + constructor( + id = UpdateAllAction.ID, + label = UpdateAllAction.LABEL, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, '', false); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private get outdated(): IExtension[] { + return this.extensionsWorkbenchService.local.filter(e => e.outdated && e.state !== ExtensionState.Installing); + } + + private update(): void { + this.enabled = this.outdated.length > 0; + } + + run(): TPromise { + return TPromise.join(this.outdated.map(e => this.extensionsWorkbenchService.install(e))); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class ReloadAction extends Action { + + private static EnabledClass = 'extension-action reload'; + private static DisabledClass = `${ReloadAction.EnabledClass} disabled`; + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + reloadMessaage: string = ''; + private throttler: Throttler; + + constructor( + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IMessageService private messageService: IMessageService, + @IWindowService private windowService: IWindowService, + @IExtensionService private extensionService: IExtensionService + ) { + super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false); + this.throttler = new Throttler(); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.throttler.queue(() => { + this.enabled = false; + this.tooltip = ''; + this.reloadMessaage = ''; + if (!this.extension) { + return TPromise.wrap(null); + } + const state = this.extension.state; + if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) { + return TPromise.wrap(null); + } + return this.extensionService.getExtensions() + .then(runningExtensions => this.computeReloadState(runningExtensions)); + }).done(() => { + this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass; + }); + } + + private computeReloadState(runningExtensions: IExtensionDescription[]): void { + const isInstalled = this.extensionsWorkbenchService.local.some(e => e.id === this.extension.id); + const isUninstalled = this.extension.state === ExtensionState.Uninstalled; + const isDisabled = this.extension.disabledForWorkspace || this.extension.disabledGlobally; + + const filteredExtensions = runningExtensions.filter(e => areSameExtensions(e, this.extension)); + const isExtensionRunning = filteredExtensions.length > 0; + const isDifferentVersionRunning = filteredExtensions.length > 0 && this.extension.version !== filteredExtensions[0].version; + + if (isInstalled) { + if (isDifferentVersionRunning && !isDisabled) { + // Requires reload to run the updated extension + this.enabled = true; + this.tooltip = localize('postUpdateTooltip', "Reload to update"); + this.reloadMessaage = localize('postUpdateMessage', "Reload this window to activate the updated extension '{0}'?", this.extension.displayName); + return; + } + + if (!isExtensionRunning && !isDisabled) { + // Requires reload to enable the extension + this.enabled = true; + this.tooltip = localize('postEnableTooltip', "Reload to activate"); + this.reloadMessaage = localize('postEnableMessage', "Reload this window to activate the extension '{0}'?", this.extension.displayName); + return; + } + + if (isExtensionRunning && isDisabled) { + // Requires reload to disable the extension + this.enabled = true; + this.tooltip = localize('postDisableTooltip', "Reload to deactivate"); + this.reloadMessaage = localize('postDisableMessage', "Reload this window to deactivate the extension '{0}'?", this.extension.displayName); + return; + } + return; + } + + if (isUninstalled && isExtensionRunning) { + // Requires reload to deactivate the extension + this.enabled = true; + this.tooltip = localize('postUninstallTooltip', "Reload to deactivate"); + this.reloadMessaage = localize('postUninstallMessage', "Reload this window to deactivate the uninstalled extension '{0}'?", this.extension.displayName); + return; + } + } + + run(): TPromise { + if (this.messageService.confirm({ message: this.reloadMessaage, type: 'question', primaryButton: localize('reload', "&&Reload Window") })) { + return this.windowService.reloadWindow(); + } + return TPromise.wrap(null); + } +} + +export class OpenExtensionsViewletAction extends ToggleViewletAction { + + static ID = VIEWLET_ID; + static LABEL = localize('toggleExtensionsViewlet', "Show Extensions"); + + constructor( + id: string, + label: string, + @IViewletService viewletService: IViewletService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService + ) { + super(id, label, VIEWLET_ID, viewletService, editorService); + } +} + +export class InstallExtensionsAction extends OpenExtensionsViewletAction { + static ID = 'workbench.extensions.action.installExtensions'; + static LABEL = localize('installExtensions', "Install Extensions"); +} + +export class ShowEnabledExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showEnabledExtensions'; + static LABEL = localize('showEnabledExtensions', 'Show Enabled Extensions'); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, 'clear-extensions', true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@enabled'); + viewlet.focus(); + }); + } +} + +export class ShowInstalledExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showInstalledExtensions'; + static LABEL = localize('showInstalledExtensions', "Show Installed Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, 'clear-extensions', true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@installed'); + viewlet.focus(); + }); + } +} + +export class ShowDisabledExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showDisabledExtensions'; + static LABEL = localize('showDisabledExtensions', "Show Disabled Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, 'null', true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@disabled '); + viewlet.focus(); + }); + } +} + +export class ClearExtensionsInputAction extends Action { + + static ID = 'workbench.extensions.action.clearExtensionsInput'; + static LABEL = localize('clearExtensionsInput', "Clear Extensions Input"); + + private disposables: IDisposable[] = []; + + constructor( + id: string, + label: string, + onSearchChange: Event, + @IViewletService private viewletService: IViewletService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, 'clear-extensions', true); + this.enabled = false; + onSearchChange(this.onSearchChange, this, this.disposables); + } + + private onSearchChange(value: string): void { + this.enabled = !!value; + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search(''); + viewlet.focus(); + }); + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + +export class ShowOutdatedExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.listOutdatedExtensions'; + static LABEL = localize('showOutdatedExtensions', "Show Outdated Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@outdated '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ShowPopularExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showPopularExtensions'; + static LABEL = localize('showPopularExtensions', "Show Popular Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@sort:installs '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ShowRecommendedExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showRecommendedExtensions'; + static LABEL = localize('showRecommendedExtensions', "Show Recommended Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@recommended '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ShowWorkspaceRecommendedExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showWorkspaceRecommendedExtensions'; + static LABEL = localize('showWorkspaceRecommendedExtensions', "Show Workspace Recommended Extensions"); + + constructor( + id: string, + label: string, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, contextService.hasWorkspace()); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@recommended:workspace '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ShowRecommendedKeymapExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showRecommendedKeymapExtensions'; + static LABEL = localize('showRecommendedKeymapExtensions', "Show Recommended Keymaps"); + static SHORT_LABEL = localize('showRecommendedKeymapExtensionsShort', "Keymaps"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@recommended:keymaps '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ShowLanguageExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showLanguageExtensions'; + static LABEL = localize('showLanguageExtensions', "Show Language Extensions"); + static SHORT_LABEL = localize('showLanguageExtensionsShort', "Language Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@sort:installs category:languages '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ShowAzureExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showAzureExtensions'; + static LABEL = localize('showAzureExtensions', "Show Azure Extensions"); + static SHORT_LABEL = localize('showAzureExtensionsShort', "Azure Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@sort:installs azure '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ChangeSortAction extends Action { + + private query: Query; + private disposables: IDisposable[] = []; + + constructor( + id: string, + label: string, + onSearchChange: Event, + private sortBy: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + + if (sortBy === undefined) { + throw new Error('bad arguments'); + } + + this.query = Query.parse(''); + this.enabled = false; + onSearchChange(this.onSearchChange, this, this.disposables); + } + + private onSearchChange(value: string): void { + const query = Query.parse(value); + this.query = new Query(query.value, this.sortBy || query.sortBy); + this.enabled = value && this.query.isValid() && !this.query.equals(query); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search(this.query.toString()); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ConfigureWorkspaceRecommendedExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions'; + static LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)"); + + constructor( + id: string, + label: string, + @IFileService private fileService: IFileService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsService: IExtensionsWorkbenchService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IMessageService private messageService: IMessageService + ) { + super(id, label, null, contextService.hasWorkspace()); + } + + public run(event: any): TPromise { + return this.openExtensionsFile(); + } + + private openExtensionsFile(): TPromise { + if (!this.contextService.hasWorkspace()) { + this.messageService.show(severity.Info, localize('ConfigureWorkspaceRecommendations.noWorkspace', 'Recommendations are only available on a workspace folder.')); + return TPromise.as(undefined); + } + + return this.getOrCreateExtensionsFile().then(value => { + return this.editorService.openEditor({ + resource: value.extensionsFileResource, + options: { + forceOpen: true, + pinned: value.created + }, + }); + }, (error) => TPromise.wrapError(new Error(localize('OpenExtensionsFile.failed', "Unable to create 'extensions.json' file inside the '.vscode' folder ({0}).", error)))); + } + + private getOrCreateExtensionsFile(): TPromise<{ created: boolean, extensionsFileResource: URI }> { + // {{SQL CARBON EDIT}} + const extensionsFileResource = URI.file(paths.join(this.contextService.getLegacyWorkspace().resource.fsPath, '.sqlops', 'extensions.json')); // TODO@Sandeep (https://github.com/Microsoft/vscode/issues/29242) + + return this.fileService.resolveContent(extensionsFileResource).then(content => { + return { created: false, extensionsFileResource }; + }, err => { + return this.fileService.updateContent(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => { + return { created: true, extensionsFileResource }; + }); + }); + } +} + +export class BuiltinStatusLabelAction extends Action { + + private static Class = 'extension-action built-in-status'; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor() { + super('extensions.install', localize('builtin', "Built-in"), '', false); + } + + private update(): void { + if (this.extension && this.extension.type === LocalExtensionType.System) { + this.class = `${BuiltinStatusLabelAction.Class} system`; + } else { + this.class = `${BuiltinStatusLabelAction.Class} user`; + } + } + + run(): TPromise { + return TPromise.as(null); + } +} + +export class DisableAllAction extends Action { + + static ID = 'workbench.extensions.action.disableAll'; + static LABEL = localize('disableAll', "Disable All Installed Extensions"); + + private disposables: IDisposable[] = []; + + constructor( + id: string = DisableAllAction.ID, label: string = DisableAllAction.LABEL, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + ) { + super(id, label); + this.update(); + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + } + + private update(): void { + this.enabled = this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && !e.disabledForWorkspace && !e.disabledGlobally); + } + + run(): TPromise { + return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, false))); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class DisableAllWorkpsaceAction extends Action { + + static ID = 'workbench.extensions.action.disableAllWorkspace'; + static LABEL = localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace"); + + private disposables: IDisposable[] = []; + + constructor( + id: string = DisableAllWorkpsaceAction.ID, label: string = DisableAllWorkpsaceAction.LABEL, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + ) { + super(id, label); + this.update(); + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + } + + private update(): void { + this.enabled = this.workspaceContextService.hasWorkspace() && this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && !e.disabledForWorkspace && !e.disabledGlobally); + } + + run(): TPromise { + return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, false, true))); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableAllAction extends Action { + + static ID = 'workbench.extensions.action.enableAll'; + static LABEL = localize('enableAll', "Enable All Installed Extensions"); + + private disposables: IDisposable[] = []; + + constructor( + id: string = EnableAllAction.ID, label: string = EnableAllAction.LABEL, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + ) { + super(id, label); + this.update(); + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + } + + private update(): void { + this.enabled = this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canEnable(e.id) && e.disabledGlobally); + } + + run(): TPromise { + return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, true))); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableAllWorkpsaceAction extends Action { + + static ID = 'workbench.extensions.action.enableAllWorkspace'; + static LABEL = localize('enableAllWorkspace', "Enable All Installed Extensions for this Workspace"); + + private disposables: IDisposable[] = []; + + constructor( + id: string = EnableAllWorkpsaceAction.ID, label: string = EnableAllWorkpsaceAction.LABEL, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + ) { + super(id, label); + this.update(); + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + } + + private update(): void { + this.enabled = this.workspaceContextService.hasWorkspace() && this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canEnable(e.id) && !e.disabledGlobally && e.disabledForWorkspace); + } + + run(): TPromise { + return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, true, true))); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForLanguage', function (accessor: ServicesAccessor, fileExtension: string) { + const viewletService = accessor.get(IViewletService); + + return viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search(`ext:${fileExtension.replace(/^\./, '')}`); + viewlet.focus(); + }); +}); + +export const extensionButtonProminentBackground = registerColor('extensionButton.prominentBackground', { + dark: '#327e36', + light: '#327e36', + hc: null +}, localize('extensionButtonProminentBackground', "Button background color for actions extension that stand out (e.g. install button).")); + +export const extensionButtonProminentForeground = registerColor('extensionButton.prominentForeground', { + dark: Color.white, + light: Color.white, + hc: null +}, localize('extensionButtonProminentForeground', "Button foreground color for actions extension that stand out (e.g. install button).")); + +export const extensionButtonProminentHoverBackground = registerColor('extensionButton.prominentHoverBackground', { + dark: '#28632b', + light: '#28632b', + hc: null +}, localize('extensionButtonProminentHoverBackground', "Button background hover color for actions extension that stand out (e.g. install button).")); + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const foregroundColor = theme.getColor(foreground); + if (foregroundColor) { + collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`); + } + + const buttonBackgroundColor = theme.getColor(buttonBackground); + if (buttonBackgroundColor) { + collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action { background-color: ${buttonBackgroundColor}; }`); + } + + const buttonForegroundColor = theme.getColor(buttonForeground); + if (buttonForegroundColor) { + collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action { color: ${buttonForegroundColor}; }`); + } + + const buttonHoverBackgroundColor = theme.getColor(buttonHoverBackground); + if (buttonHoverBackgroundColor) { + collector.addRule(`.monaco-action-bar .action-item:hover .action-label.extension-action { background-color: ${buttonHoverBackgroundColor}; }`); + } + + const contrastBorderColor = theme.getColor(contrastBorder); + if (contrastBorderColor) { + collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action { border: 1px solid ${contrastBorderColor}; }`); + } + + const extensionButtonProminentBackgroundColor = theme.getColor(extensionButtonProminentBackground); + if (extensionButtonProminentBackground) { + collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); + } + + const extensionButtonProminentForegroundColor = theme.getColor(extensionButtonProminentForeground); + if (extensionButtonProminentForeground) { + collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); + } + + const extensionButtonProminentHoverBackgroundColor = theme.getColor(extensionButtonProminentHoverBackground); + if (extensionButtonProminentHoverBackground) { + collector.addRule(`.monaco-action-bar .action-item:hover .action-label.extension-action.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/extensionsList.ts b/src/vs/workbench/parts/extensions/browser/extensionsList.ts new file mode 100644 index 0000000000..687de2599c --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/extensionsList.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { append, $, addClass, removeClass, toggleClass } from 'vs/base/browser/dom'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Action } from 'vs/base/common/actions'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IDelegate } from 'vs/base/browser/ui/list/list'; +import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; +import { once } from 'vs/base/common/event'; +import { domEvent } from 'vs/base/browser/event'; +import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; +import { InstallAction, UpdateAction, BuiltinStatusLabelAction, ManageExtensionAction, ReloadAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { Label, RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; +import { EventType } from 'vs/base/common/events'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; + +export interface ITemplateData { + root: HTMLElement; + element: HTMLElement; + icon: HTMLImageElement; + name: HTMLElement; + installCount: HTMLElement; + ratings: HTMLElement; + author: HTMLElement; + description: HTMLElement; + extension: IExtension; + disposables: IDisposable[]; + extensionDisposables: IDisposable[]; +} + +export class Delegate implements IDelegate { + getHeight() { return 62; } + getTemplateId() { return 'extension'; } +} + +const actionOptions = { icon: true, label: true }; + +export class Renderer implements IPagedRenderer { + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IMessageService private messageService: IMessageService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionService private extensionService: IExtensionService + ) { } + + get templateId() { return 'extension'; } + + renderTemplate(root: HTMLElement): ITemplateData { + const element = append(root, $('.extension')); + const icon = append(element, $('img.icon')); + const details = append(element, $('.details')); + const headerContainer = append(details, $('.header-container')); + const header = append(headerContainer, $('.header')); + const name = append(header, $('span.name')); + const version = append(header, $('span.version')); + const installCount = append(header, $('span.install-count')); + const ratings = append(header, $('span.ratings')); + const description = append(details, $('.description.ellipsis')); + const footer = append(details, $('.footer')); + const author = append(footer, $('.author.ellipsis')); + const actionbar = new ActionBar(footer, { + animated: false, + actionItemProvider: (action: Action) => { + if (action.id === ManageExtensionAction.ID) { + return (action).actionItem; + } + return null; + } + }); + actionbar.addListener(EventType.RUN, ({ error }) => error && this.messageService.show(Severity.Error, error)); + + const versionWidget = this.instantiationService.createInstance(Label, version, e => e.version); + const installCountWidget = this.instantiationService.createInstance(InstallWidget, installCount, { small: true }); + const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, ratings, { small: true }); + + const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction); + const installAction = this.instantiationService.createInstance(InstallAction); + const updateAction = this.instantiationService.createInstance(UpdateAction); + const reloadAction = this.instantiationService.createInstance(ReloadAction); + const manageAction = this.instantiationService.createInstance(ManageExtensionAction); + + actionbar.push([reloadAction, updateAction, installAction, builtinStatusAction, manageAction], actionOptions); + const disposables = [versionWidget, installCountWidget, ratingsWidget, builtinStatusAction, updateAction, reloadAction, manageAction, actionbar]; + + return { + root, element, icon, name, installCount, ratings, author, description, disposables, + extensionDisposables: [], + set extension(extension: IExtension) { + versionWidget.extension = extension; + installCountWidget.extension = extension; + ratingsWidget.extension = extension; + builtinStatusAction.extension = extension; + installAction.extension = extension; + updateAction.extension = extension; + reloadAction.extension = extension; + manageAction.extension = extension; + } + }; + } + + renderPlaceholder(index: number, data: ITemplateData): void { + addClass(data.element, 'loading'); + + data.root.removeAttribute('aria-label'); + data.extensionDisposables = dispose(data.extensionDisposables); + data.icon.src = ''; + data.name.textContent = ''; + data.author.textContent = ''; + data.description.textContent = ''; + data.installCount.style.display = 'none'; + data.ratings.style.display = 'none'; + data.extension = null; + } + + renderElement(extension: IExtension, index: number, data: ITemplateData): void { + removeClass(data.element, 'loading'); + + data.extensionDisposables = dispose(data.extensionDisposables); + + this.extensionService.getExtensions().then(enabledExtensions => { + const isExtensionRunning = enabledExtensions.some(e => areSameExtensions(e, extension)); + const isInstalled = this.extensionsWorkbenchService.local.some(e => e.id === extension.id); + toggleClass(data.element, 'disabled', isInstalled && !isExtensionRunning); + }); + + const onError = once(domEvent(data.icon, 'error')); + onError(() => data.icon.src = extension.iconUrlFallback, null, data.extensionDisposables); + data.icon.src = extension.iconUrl; + + if (!data.icon.complete) { + data.icon.style.visibility = 'hidden'; + data.icon.onload = () => data.icon.style.visibility = 'inherit'; + } else { + data.icon.style.visibility = 'inherit'; + } + + data.root.setAttribute('aria-label', extension.displayName); + data.name.textContent = extension.displayName; + data.author.textContent = extension.publisherDisplayName; + data.description.textContent = extension.description; + data.installCount.style.display = ''; + data.ratings.style.display = ''; + data.extension = extension; + } + + disposeTemplate(data: ITemplateData): void { + data.disposables = dispose(data.disposables); + } +} diff --git a/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts b/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts new file mode 100644 index 0000000000..116a93171d --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAutoFocus, Mode, IModel } from 'vs/base/parts/quickopen/common/quickOpen'; +import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenHandler } from 'vs/workbench/browser/quickopen'; +import { IExtensionsViewlet, VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; + +class SimpleEntry extends QuickOpenEntry { + + constructor(private label: string, private action: Function) { + super(); + } + + getLabel(): string { + return this.label; + } + + getAriaLabel(): string { + return this.label; + } + + run(mode: Mode): boolean { + if (mode === Mode.PREVIEW) { + return false; + } + + this.action(); + + return true; + } +} + +export class ExtensionsHandler extends QuickOpenHandler { + + constructor( @IViewletService private viewletService: IViewletService) { + super(); + } + + getResults(text: string): TPromise> { + const label = nls.localize('manage', "Press Enter to manage your extensions."); + const action = () => { + this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .done(viewlet => { + viewlet.search(''); + viewlet.focus(); + }); + }; + + return TPromise.as(new QuickOpenModel([new SimpleEntry(label, action)])); + } + + getEmptyLabel(input: string): string { + return ''; + } + + getAutoFocus(searchValue: string): IAutoFocus { + return { autoFocusFirstEntry: true }; + } +} + +export class GalleryExtensionsHandler extends QuickOpenHandler { + + constructor( @IViewletService private viewletService: IViewletService) { + super(); + } + + getResults(text: string): TPromise> { + const entries: SimpleEntry[] = []; + + if (text) { + const label = nls.localize('searchFor', "Press Enter to search for '{0}' in the Marketplace.", text); + const action = () => { + this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .done(viewlet => { + viewlet.search(text); + viewlet.focus(); + }); + }; + + entries.push(new SimpleEntry(label, action)); + } + + return TPromise.as(new QuickOpenModel(entries)); + } + + getEmptyLabel(input: string): string { + return nls.localize('noExtensionsToInstall', "Type an extension name"); + } + + getAutoFocus(searchValue: string): IAutoFocus { + return { autoFocusFirstEntry: true }; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts new file mode 100644 index 0000000000..ce90fc874d --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts @@ -0,0 +1,161 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/extensionsWidgets'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IExtension, IExtensionsWorkbenchService } from '../common/extensions'; +import { append, $, addClass } from 'vs/base/browser/dom'; +import * as platform from 'vs/base/common/platform'; + +export interface IOptions { + extension?: IExtension; + small?: boolean; +} + +export class Label implements IDisposable { + + private listener: IDisposable; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.render(); } + + constructor( + private element: HTMLElement, + private fn: (extension: IExtension) => string, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + this.render(); + this.listener = extensionsWorkbenchService.onChange(this.render, this); + } + + private render(): void { + this.element.textContent = this.extension ? this.fn(this.extension) : ''; + } + + dispose(): void { + this.listener = dispose(this.listener); + } +} + +export class InstallWidget implements IDisposable { + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.render(); } + + constructor( + private container: HTMLElement, + private options: IOptions, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + this._extension = options.extension; + this.disposables.push(extensionsWorkbenchService.onChange(() => this.render())); + addClass(container, 'extension-install-count'); + this.render(); + } + + private render(): void { + this.container.innerHTML = ''; + + if (!this.extension) { + return; + } + + const installCount = this.extension.installCount; + + if (installCount === null) { + return; + } + + let installLabel: string; + + if (this.options.small) { + if (installCount > 1000000) { + installLabel = `${Math.floor(installCount / 100000) / 10}M`; + } else if (installCount > 1000) { + installLabel = `${Math.floor(installCount / 1000)}K`; + } else { + installLabel = String(installCount); + } + } + else { + installLabel = installCount.toLocaleString(platform.locale); + } + + append(this.container, $('span.octicon.octicon-cloud-download')); + const count = append(this.container, $('span.count')); + count.textContent = installLabel; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + +export class RatingsWidget implements IDisposable { + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.render(); } + + constructor( + private container: HTMLElement, + private options: IOptions, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + this._extension = options.extension; + this.disposables.push(extensionsWorkbenchService.onChange(() => this.render())); + addClass(container, 'extension-ratings'); + + if (options.small) { + addClass(container, 'small'); + } + + this.render(); + } + + private render(): void { + this.container.innerHTML = ''; + + if (!this.extension) { + return; + } + + const rating = Math.round(this.extension.rating * 2) / 2; + + if (this.extension.rating === null) { + return; + } + + if (this.options.small && this.extension.ratingCount === 0) { + return; + } + + if (this.options.small) { + append(this.container, $('span.full.star')); + + const count = append(this.container, $('span.count')); + count.textContent = String(rating); + } else { + for (let i = 1; i <= 5; i++) { + if (rating >= i) { + append(this.container, $('span.full.star')); + } else if (rating >= i - 0.5) { + append(this.container, $('span.half.star')); + } else { + append(this.container, $('span.empty.star')); + } + } + } + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} diff --git a/src/vs/workbench/parts/extensions/browser/media/EmptyStar.svg b/src/vs/workbench/parts/extensions/browser/media/EmptyStar.svg new file mode 100644 index 0000000000..ac80953032 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/EmptyStar.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/vs/workbench/parts/extensions/browser/media/FullStarLight.svg b/src/vs/workbench/parts/extensions/browser/media/FullStarLight.svg new file mode 100644 index 0000000000..9a20fb91c6 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/FullStarLight.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/vs/workbench/parts/extensions/browser/media/HalfStarLight.svg b/src/vs/workbench/parts/extensions/browser/media/HalfStarLight.svg new file mode 100644 index 0000000000..81dbb91844 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/HalfStarLight.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/vs/workbench/parts/extensions/browser/media/clear-inverse.svg b/src/vs/workbench/parts/extensions/browser/media/clear-inverse.svg new file mode 100644 index 0000000000..7df4455d7c --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/clear-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/media/clear.svg b/src/vs/workbench/parts/extensions/browser/media/clear.svg new file mode 100644 index 0000000000..6e4d3a1d48 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/clear.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/media/defaultIcon.png b/src/vs/workbench/parts/extensions/browser/media/defaultIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..2f36a94347418ca5485406b38fc515a2f6c947e5 GIT binary patch literal 1686 zcmV;H25I?;P)YYKv;q9U~mV_3cwY}4#X7qXPq8`-2N9=&SnjIM?aT zfkwELV=o=-XUpiHk97@!2Abfu7%oC``jCCBqW}vv!0pJ>72qm~Y<0!9vllS;wgd3W ztO485z7Q=hjKFrls1OZhxb9j57;x={900E0ki(pCdw{D|oxC|Y$N{&5g36d^)N5!A zF!#Avu)9$mW$BZH3~(Fr^b38ianWGNchwQEP!TRc#Rye7dbd3@&{ky8!~w>Z%TtpX z6d$5_%I^|3wnE2gry7Hla49VOkI-yYKK_&H`FK!>2cso&yLCzfm_l#z z7l-g(zr8rggWK?4XT7+crcHwPN^NazDZT&Yve zC#cnGRd|>zlZb6J&@mN`KARMVq4La5RlGMUvPfgt!*9T-q&=*l-&2@t9!#s*GI_yaKQ**3)shbT`UF6YaC*1+FeZ+*QreM zVA4<6$yt427cMEWQEWL_TqU)-UjXxVY4BnPy*FvAyJffR^x?(Md2c`I9})q>i~aCk z($cN%u;Zvx-n(=s(xrD*!i(Ma-jyD_;K^Dyr-X88?dFte?u+iij@D`|o3(&>e_Bi> z+NaUFy@OMQXcS!T6;`StIx4sjsx1o5Y2c;?%#cURnrXsJ;?d9j`3W zOwmzV?^OoOkf~+*g?wB}-u)ikOqW*5kB{;f9K+tmb*u_E6X1o`2}PILqH3JwKW2Ua zTz=tDp3Y%=?K)>$XHt;&+htB32yAMpOG61aSELMzxC%hF6K-LE0ohKtxpHtqPVOQn z7nXAiD`117uTFY0dGP?8GFQvPB0n(Dah1H;)}_E;NLtOf&m9;2!GW$T<`FM~jA6~y znuv^p7)En&LQbX&J`$LqSS7N}kuJDeJvipgZO+RG+5bXvS7lbnYQPM+&XB9VmHbF3 z%;!1-I97lW=tjlBPDPF}WoG~{fbPlDX$8Xe`|ZaPY_E(j56b+>Ic%SK3Oc&4yl*NT zTL;)v&{lx83UCZ`n?bwQKTjC!$Of=h0*=8Zz$fDG1V-IrZyn0xBK~AO0IT=WhwR;D zSKm`^b*@eHv6TQEgH3>247_O|lt=59(AraDouf_wyt=n;aww0stZyoW_L=y%_{??m g+H}vVLw^Jq0D83(sH_n03jhEB07*qoM6N<$f=0JIbN~PV literal 0 HcmV?d00001 diff --git a/src/vs/workbench/parts/extensions/browser/media/extensionActions.css b/src/vs/workbench/parts/extensions/browser/media/extensionActions.css new file mode 100644 index 0000000000..0090f2bdf3 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/extensionActions.css @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-action-bar .action-item .action-label.extension-action { + padding: 0 5px; + line-height: initial; +} + +.monaco-action-bar .action-item .action-label.clear-extensions { + background: url('clear.svg') center center no-repeat; +} + +.vs-dark .monaco-action-bar .action-item .action-label.clear-extensions, +.hc-black .monaco-action-bar .action-item .action-label.clear-extensions { + background: url('clear-inverse.svg') center center no-repeat; +} + +.monaco-action-bar .action-item .action-label.extension-action.enable:after, +.monaco-action-bar .action-item .action-label.extension-action.disable:after { + content: '▼'; + padding-left: 2px; + font-size: 80%; +} + +.monaco-action-bar .action-item.disabled .action-label.extension-action.install:not(.installing), +.monaco-action-bar .action-item.disabled .action-label.extension-action.uninstall:not(.uninstalling), +.monaco-action-bar .action-item.disabled .action-label.extension-action.update, +.monaco-action-bar .action-item.disabled .action-label.extension-action.enable, +.monaco-action-bar .action-item.disabled .action-label.extension-action.disable, +.monaco-action-bar .action-item.disabled .action-label.extension-action.reload, +.monaco-action-bar .action-item.disabled .action-label.extension-action.built-in-status.user { + display: none; +} + +.monaco-action-bar .action-item .action-label.extension-action.built-in-status { + border-radius: 4px; + color: inherit; + background-color: transparent; + opacity: 0.9; + font-style: italic; +} + +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.extension-action.built-in-status { + font-weight: normal; +} + +.extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.extension-action.manage.hide { + display: none; +} + +.extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.extension-action.manage { + height: 18px; + width: 10px; + border: none; + background: url('manage.svg') center center no-repeat; +} + +.hc-black .extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.extension-action.manage, +.vs-dark .extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.extension-action.manage { + background: url('manage-inverse.svg') center center no-repeat; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/media/extensionEditor.css b/src/vs/workbench/parts/extensions/browser/media/extensionEditor.css new file mode 100644 index 0000000000..60623ba6ee --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/extensionEditor.css @@ -0,0 +1,296 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.extension-editor { + height: 100%; + overflow: hidden; +} + +.extension-editor .clickable { + cursor: pointer; +} + +.extension-editor > .header { + display: flex; + height: 128px; + background: rgba(128, 128, 128, 0.15); + padding: 20px; + overflow: hidden; + font-size: 14px; +} + +.extension-editor > .header > .icon { + height: 128px; + width: 128px; + object-fit: contain; +} + +.extension-editor > .header > .details { + flex: 1; + padding-left: 20px; + overflow: hidden; +} + +.extension-editor > .header > .details > .title { + display: flex; + align-items: center; +} + +.extension-editor > .header > .details > .title > .name { + flex: 0; + font-size: 26px; + line-height: 30px; + font-weight: 600; + white-space: nowrap; +} + +.extension-editor > .header > .details > .title > .identifier { + margin-left: 10px; + font-size: 14px; + opacity: 0.6; + background: rgba(173, 173, 173, 0.31); + padding: 0px 4px; + border-radius: 4px; + user-select: text; + -webkit-user-select: text; + white-space: nowrap; +} + +.extension-editor > .header > .details > .subtitle { + padding-top: 10px; + white-space: nowrap; + height: 20px; + line-height: 20px; +} + +.extension-editor > .header > .details > .subtitle > .publisher { + font-size: 18px; +} + +.extension-editor > .header > .details > .subtitle > .install > .count { + margin-left: 6px; +} + +.extension-editor > .header > .details > .subtitle > span:not(:first-child):not(:empty), +.extension-editor > .header > .details > .subtitle > a:not(:first-child):not(:empty) { + border-left: 1px solid rgba(128, 128, 128, 0.7); + margin-left: 14px; + padding-left: 14px; +} + +.extension-editor > .header > .details > .description { + margin-top: 14px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.extension-editor > .header > .details > .actions { + margin-top: 14px; +} + +.extension-editor > .header > .details > .actions > .monaco-action-bar { + text-align: initial; +} + +.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container { + justify-content: flex-start; +} + +.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .action-label { + font-weight: 600; + padding: 1px 6px; +} + +.extension-editor > .body { + height: calc(100% - 168px); + overflow: hidden; +} + +.extension-editor > .body > .navbar { + height: 36px; + font-weight: bold; + font-size: 14px; + line-height: 36px; + padding-left: 20px; + border-bottom: 1px solid rgba(136, 136, 136, 0.45); + box-sizing: border-box; +} + +.extension-editor > .body > .navbar > .monaco-action-bar > .actions-container { + justify-content: initial; +} + +.extension-editor > .body > .navbar > .monaco-action-bar > .actions-container > .action-item > .action-label { + margin-right: 16px; +} + +.extension-editor > .body > .navbar > .monaco-action-bar > .actions-container > .action-item > .action-label { + font-size: inherit; + opacity: 0.7; +} + +.extension-editor > .body > .navbar > .monaco-action-bar > .actions-container > .action-item.disabled > .action-label { + opacity: 1; + text-decoration: underline; +} + +.extension-editor > .body > .navbar > .monaco-action-bar > .actions-container > .action-item > .action-label:hover { + text-decoration: underline; +} + +.extension-editor > .body > .content { + height: calc(100% - 36px); + position: relative; + overflow: hidden; +} + +.extension-editor > .body > .content.loading { + background: url('loading.svg') center center no-repeat; +} + +.extension-editor > .body > .content > .monaco-scrollable-element { + height: 100%; +} + +.extension-editor > .body > .content > .nocontent { + margin-left: 20px; +} + +.extension-editor > .body > .content > .monaco-scrollable-element > .subcontent { + height: 100%; + padding: 20px; + overflow-y: scroll; + box-sizing: border-box; +} + +.extension-editor > .body > .content table { + width: 100%; + border-spacing: 0; + border-collapse: separate; +} + +.extension-editor > .body > .content details:not(:first-child) { + margin-top: 20px; +} + +.extension-editor > .body > .content details > summary { + cursor: pointer; + margin-bottom: 10px; + font-weight: bold; + font-size: 120%; + border-bottom: 1px solid rgba(128, 128, 128, 0.22); + padding-bottom: 6px; +} + +.extension-editor > .body > .content details > summary:focus { + outline: none; +} + +.extension-editor > .body > .content details > summary::-webkit-details-marker { + color: rgba(128, 128, 128, 0.5); +} + +.extension-editor > .body > .content table tr:nth-child(odd) { + background-color: rgba(130, 130, 130, 0.04); +} + +.extension-editor > .body > .content table tr:not(:first-child):hover { + background-color: rgba(128, 128, 128, 0.15); +} + +.extension-editor > .body > .content table th, +.extension-editor > .body > .content table td { + padding: 2px 16px 2px 4px; +} + +.extension-editor > .body > .content table th:last-child, +.extension-editor > .body > .content table td:last-child { + padding: 2px 4px; +} + +.extension-editor > .body > .content table th { + text-align: left; +} + +.extension-editor > .body > .content table code:not(:empty) { + font-family: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback"; + font-size: 90%; + background-color: rgba(128, 128, 128, 0.17); + border-radius: 4px; + padding: 1px 4px; +} + +.extension-editor .subcontent .monaco-tree-row .content .unknown-dependency { + line-height: 62px; +} + +.extension-editor .subcontent .monaco-tree-row .content .unknown-dependency > .error-marker { + background-color: #BE1100; + padding: 2px 4px; + font-weight: bold; + font-size: 11px; + color: #CCC; +} + +.extension-editor .subcontent .monaco-tree-row .unknown-dependency > .message { + padding-left: 10px; + font-weight: bold; + font-size: 14px; +} + +.extension-editor .subcontent .monaco-tree-row .dependency { + display: flex; + align-items: center; +} + +.extension-editor .subcontent .monaco-tree-row .dependency > .details { + flex: 1; + overflow: hidden; + padding-left: 10px; +} + +.extension-editor .subcontent .monaco-tree-row .dependency > .details > .header { + display: flex; + align-items: center; + height: 19px; + overflow: hidden; +} + +.extension-editor .subcontent .monaco-tree-row .dependency > .icon { + height: 40px; + width: 40px; +} + +.extension-editor .subcontent .monaco-tree-row .dependency > .details > .header > .name { + font-weight: bold; + font-size: 16px; +} + +.extension-editor .subcontent .monaco-tree-row .dependency > .details > .header > .name:hover { + text-decoration: underline; +} + +.extension-editor .subcontent .monaco-tree-row .dependency > .details > .header > .identifier { + font-size: 90%; + opacity: 0.6; + margin-left: 10px; + background: rgba(173, 173, 173, 0.31); + padding: 0px 4px; + border-radius: 4px; +} + +.extension-editor .subcontent .monaco-tree-row .dependency > .details > .footer { + display: flex; + height: 19px; + overflow: hidden; + padding-top: 5px; +} + +.extension-editor .subcontent .monaco-tree-row .dependency > .details > .footer > .author { + font-size: 90%; + font-weight: 600; + opacity: 0.6; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/media/extensionsWidgets.css b/src/vs/workbench/parts/extensions/browser/media/extensionsWidgets.css new file mode 100644 index 0000000000..42df8e1914 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/extensionsWidgets.css @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.extension-ratings { + display: inline-block; +} + +.extension-ratings.small { + font-size: 80%; +} + +.extension-ratings > .star { + display: inline-block; + width: 16px; + height: 16px; + background-repeat: no-repeat; + background-position: center center; +} + +.extension-ratings > .star:not(:first-child) { + margin-left: 3px; +} + +.extension-ratings.small > .star { + width: 10px; + height: 10px; +} + +.extension-ratings > .full { + background-image: url('FullStarLight.svg'); +} + +.extension-ratings > .half { + background-image: url('HalfStarLight.svg'); +} + +.extension-ratings > .empty { + background-image: url('EmptyStar.svg'); +} + +.extension-ratings > .count { + margin-left: 6px; +} + +.extension-ratings.small > .count { + margin-left: 2px; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/media/loading.svg b/src/vs/workbench/parts/extensions/browser/media/loading.svg new file mode 100644 index 0000000000..0098fd9f26 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/loading.svg @@ -0,0 +1,36 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/media/manage-inverse.svg b/src/vs/workbench/parts/extensions/browser/media/manage-inverse.svg new file mode 100644 index 0000000000..61baaea2b8 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/manage-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/media/manage.svg b/src/vs/workbench/parts/extensions/browser/media/manage.svg new file mode 100644 index 0000000000..3dec2ba50f --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/manage.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/browser/media/markdown.css b/src/vs/workbench/parts/extensions/browser/media/markdown.css new file mode 100644 index 0000000000..80ff2f0c2e --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/media/markdown.css @@ -0,0 +1,161 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +body { + padding: 10px 20px; + line-height: 22px; +} + +img { + max-width: 100%; + max-height: 100%; +} + +a { + color: #a2c1e8; + text-decoration: none; +} + +a:focus, +input:focus, +select:focus, +textarea:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +hr { + border: 0; + height: 2px; + border-bottom: 2px solid; +} + +h1 { + padding-bottom: 0.3em; + line-height: 1.2; + border-bottom-width: 1px; + border-bottom-style: solid; +} + +h1, h2, h3 { + font-weight: normal; +} + +a:hover { + color: #a2c1e8; + text-decoration: underline; +} + +table { + border-collapse: collapse; +} + +table > thead > tr > th { + text-align: left; + border-bottom: 1px solid; +} + +table > thead > tr > th, +table > thead > tr > td, +table > tbody > tr > th, +table > tbody > tr > td { + padding: 5px 10px; +} + +table > tbody > tr + tr > td { + border-top: 1px solid; +} + +blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left: 5px solid; +} + +code { + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-size: 14px; + line-height: 19px; +} + +.mac code { + font-size: 12px; + line-height: 18px; +} + +code > div { + padding: 16px; + border-radius: 3px; + overflow: auto; +} + +/** Theming */ + +.vscode-light { + color: rgb(30, 30, 30); +} + +.vscode-dark { + color: #DDD; +} + +.vscode-high-contrast { + color: white; +} + +.vscode-light code { + color: #A31515; +} + +.vscode-dark code { + color: #D7BA7D; +} + +.vscode-light code > div { + background-color: rgba(220, 220, 220, 0.4); +} + +.vscode-dark code > div { + background-color: rgba(10, 10, 10, 0.4); +} + +.vscode-high-contrast code > div { + background-color: rgb(0, 0, 0); +} + +.vscode-high-contrast h1 { + border-color: rgb(0, 0, 0); +} + +.vscode-light table > thead > tr > th { + border-color: rgba(0, 0, 0, 0.69); +} + +.vscode-dark table > thead > tr > th { + border-color: rgba(255, 255, 255, 0.69); +} + +.vscode-light h1, +.vscode-light hr, +.vscode-light table > tbody > tr + tr > td { + border-color: rgba(0, 0, 0, 0.18); +} + +.vscode-dark h1, +.vscode-dark hr, +.vscode-dark table > tbody > tr + tr > td { + border-color: rgba(255, 255, 255, 0.18); +} + +.vscode-light blockquote, +.vscode-dark blockquote { + background: rgba(127, 127, 127, 0.1); + border-color: rgba(0, 122, 204, 0.5); +} + +.vscode-high-contrast blockquote { + background: transparent; + border-color: #fff; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/common/extensionQuery.ts b/src/vs/workbench/parts/extensions/common/extensionQuery.ts new file mode 100644 index 0000000000..491faff470 --- /dev/null +++ b/src/vs/workbench/parts/extensions/common/extensionQuery.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export class Query { + + constructor(public value: string, public sortBy: string) { + this.value = value.trim(); + } + + static parse(value: string): Query { + let sortBy = ''; + + value = value.replace(/@sort:(\w+)(-\w*)?/g, (match, by: string, order: string) => { + sortBy = by; + + return ''; + }); + + return new Query(value, sortBy); + } + + toString(): string { + let result = this.value; + + if (this.sortBy) { + result = `${result}${result ? ' ' : ''}@sort:${this.sortBy}`; + } + + return result; + } + + isValid(): boolean { + return !/@outdated/.test(this.value); + } + + equals(other: Query): boolean { + return this.value === other.value && this.sortBy === other.sortBy; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts new file mode 100644 index 0000000000..0cda3d9180 --- /dev/null +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IViewlet } from 'vs/workbench/common/viewlet'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import Event from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IPager } from 'vs/base/common/paging'; +import { IQueryOptions, IExtensionManifest, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; + +export const VIEWLET_ID = 'workbench.view.extensions'; + +export interface IExtensionsViewlet extends IViewlet { + search(text: string): void; +} + +export enum ExtensionState { + Installing, + Installed, + Uninstalling, + Uninstalled +} + +export interface IExtension { + type: LocalExtensionType; + state: ExtensionState; + name: string; + displayName: string; + id: string; + publisher: string; + publisherDisplayName: string; + version: string; + latestVersion: string; + description: string; + url: string; + iconUrl: string; + iconUrlFallback: string; + licenseUrl: string; + installCount: number; + rating: number; + ratingCount: number; + outdated: boolean; + disabledGlobally: boolean; + disabledForWorkspace: boolean; + dependencies: string[]; + telemetryData: any; + getManifest(): TPromise; + getReadme(): TPromise; + getChangelog(): TPromise; +} + +export interface IExtensionDependencies { + dependencies: IExtensionDependencies[]; + hasDependencies: boolean; + identifier: string; + extension: IExtension; + dependent: IExtensionDependencies; +} + +export const SERVICE_ID = 'extensionsWorkbenchService'; + +export const IExtensionsWorkbenchService = createDecorator(SERVICE_ID); + +export interface IExtensionsWorkbenchService { + _serviceBrand: any; + onChange: Event; + local: IExtension[]; + isAutoUpdateEnabled: boolean; + queryLocal(): TPromise; + queryGallery(options?: IQueryOptions): TPromise>; + canInstall(extension: IExtension): boolean; + install(vsix: string): TPromise; + install(extension: IExtension, promptToInstallDependencies?: boolean): TPromise; + uninstall(extension: IExtension): TPromise; + setEnablement(extension: IExtension, enable: boolean, workspace?: boolean): TPromise; + loadDependencies(extension: IExtension): TPromise; + open(extension: IExtension, sideByside?: boolean): TPromise; + checkForUpdates(): TPromise; + setAutoUpdate(autoUpdate: boolean): TPromise; + allowedBadgeProviders: string[]; +} + +export const ConfigurationKey = 'extensions'; + +export interface IExtensionsConfiguration { + autoUpdate: boolean; + ignoreRecommendations: boolean; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.ts b/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.ts new file mode 100644 index 0000000000..f44fbf9665 --- /dev/null +++ b/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement'; + +export const ExtensionsConfigurationSchemaId = 'vscode://schemas/extensions'; +export const ExtensionsConfigurationSchema: IJSONSchema = { + id: ExtensionsConfigurationSchemaId, + type: 'object', + title: localize('app.extensions.json.title', "Extensions"), + properties: { + recommendations: { + type: 'array', + description: localize('app.extensions.json.recommendations', "List of extensions recommendations. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'."), + items: { + type: 'string', + pattern: EXTENSION_IDENTIFIER_PATTERN, + errorMessage: localize('app.extension.identifier.errorMessage', "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'.") + }, + }, + } +}; + +export const ExtensionsConfigurationInitialContent: string = [ + '{', + '\t// See http://go.microsoft.com/fwlink/?LinkId=827846', + '\t// for the documentation about the extensions.json format', + '\t"recommendations": [', + '\t\t// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp', + '\t\t', + '\t]', + '}' +].join('\n'); \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/common/extensionsInput.ts b/src/vs/workbench/parts/extensions/common/extensionsInput.ts new file mode 100644 index 0000000000..d1efdf0d58 --- /dev/null +++ b/src/vs/workbench/parts/extensions/common/extensionsInput.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { IExtension } from 'vs/workbench/parts/extensions/common/extensions'; +import URI from 'vs/base/common/uri'; + +export class ExtensionsInput extends EditorInput { + + static get ID() { return 'workbench.extensions.input2'; } + get extension(): IExtension { return this._extension; } + + constructor(private _extension: IExtension) { + super(); + } + + getTypeId(): string { + return ExtensionsInput.ID; + } + + getName(): string { + return localize('extensionsInputName', "Extension: {0}", this.extension.displayName); + } + + matches(other: any): boolean { + if (!(other instanceof ExtensionsInput)) { + return false; + } + + const otherExtensionInput = other as ExtensionsInput; + + // TODO@joao is this correct? + return this.extension === otherExtensionInput.extension; + } + + resolve(refresh?: boolean): TPromise { + return TPromise.as(null); + } + + supportsSplitEditor(): boolean { + return false; + } + + getResource(): URI { + return URI.from({ + scheme: 'extension', + path: this.extension.id + }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts new file mode 100644 index 0000000000..353822b4fd --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -0,0 +1,375 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import * as paths from 'vs/base/common/paths'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { forEach } from 'vs/base/common/collections'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { match } from 'vs/base/common/glob'; +import * as json from 'vs/base/common/json'; +import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, LocalExtensionType, EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import product from 'vs/platform/node/product'; +import { IChoiceService, IMessageService } from 'vs/platform/message/common/message'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; +import Severity from 'vs/base/common/severity'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { Schemas } from 'vs/base/common/network'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import * as cp from 'child_process'; +import { distinct } from 'vs/base/common/arrays'; + +interface IExtensionsContent { + recommendations: string[]; +} + +const empty: { [key: string]: any; } = Object.create(null); +const milliSecondsInADay = 1000 * 60 * 60 * 24; + +export class ExtensionTipsService implements IExtensionTipsService { + + _serviceBrand: any; + + private _fileBasedRecommendations: { [id: string]: number; } = Object.create(null); + private _exeBasedRecommendations: string[] = []; + private _availableRecommendations: { [pattern: string]: string[] } = Object.create(null); + private importantRecommendations: { [id: string]: { name: string; pattern: string; } } = Object.create(null); + private importantRecommendationsIgnoreList: string[]; + private _allRecommendations: string[]; + private _disposables: IDisposable[] = []; + + constructor( + @IExtensionGalleryService private _galleryService: IExtensionGalleryService, + @IModelService private _modelService: IModelService, + @IStorageService private storageService: IStorageService, + @IChoiceService private choiceService: IChoiceService, + @IExtensionManagementService private extensionsService: IExtensionManagementService, + @IInstantiationService private instantiationService: IInstantiationService, + @IFileService private fileService: IFileService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IConfigurationService private configurationService: IConfigurationService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, + @IMessageService private messageService: IMessageService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + if (!this._galleryService.isEnabled()) { + return; + } + + this._suggestTips(); + this._suggestWorkspaceRecommendations(); + this._suggestBasedOnExecutables(); + } + + getWorkspaceRecommendations(): TPromise { + if (!this.contextService.hasWorkspace()) { + return TPromise.as([]); + } + // {{SQL CARBON EDIT}} + return this.fileService.resolveContent(this.contextService.toResource(paths.join('.sqlops', 'extensions.json'))).then(content => { //TODO@Sandeep (https://github.com/Microsoft/vscode/issues/29242) + const extensionsContent = json.parse(content.value, []); + if (extensionsContent.recommendations) { + const regEx = new RegExp(EXTENSION_IDENTIFIER_PATTERN); + return extensionsContent.recommendations.filter((element, position) => { + return extensionsContent.recommendations.indexOf(element) === position && regEx.test(element); + }); + } + return []; + }, err => []); + } + + getRecommendations(): string[] { + const allRecomendations = this._getAllRecommendationsInProduct(); + const fileBased = Object.keys(this._fileBasedRecommendations) + .filter(recommendation => allRecomendations.indexOf(recommendation) !== -1); + + const exeBased = distinct(this._exeBasedRecommendations); + + this.telemetryService.publicLog('extensionRecommendations:unfiltered', { fileBased, exeBased }); + + return distinct([...fileBased, ...exeBased]); + } + + getKeymapRecommendations(): string[] { + return product.keymapExtensionTips || []; + } + + private _getAllRecommendationsInProduct(): string[] { + if (!this._allRecommendations) { + this._allRecommendations = [...Object.keys(this.importantRecommendations)]; + forEach(this._availableRecommendations, ({ value: ids }) => { + this._allRecommendations.push(...ids); + }); + } + return this._allRecommendations; + } + + private _suggestTips() { + const extensionTips = product.extensionTips; + if (!extensionTips) { + return; + } + this.importantRecommendations = product.extensionImportantTips || Object.create(null); + this.importantRecommendationsIgnoreList = JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]')); + + // retrieve ids of previous recommendations + const storedRecommendationsJson = JSON.parse(this.storageService.get('extensionsAssistant/recommendations', StorageScope.GLOBAL, '[]')); + if (Array.isArray(storedRecommendationsJson)) { + for (let id of storedRecommendationsJson) { + this._fileBasedRecommendations[id] = Date.now(); + } + } else { + const now = Date.now(); + forEach(storedRecommendationsJson, entry => { + if (typeof entry.value === 'number') { + const diff = (now - entry.value) / milliSecondsInADay; + if (diff > 7) { + delete this._fileBasedRecommendations[entry.value]; + } else { + this._fileBasedRecommendations[entry.key] = entry.value; + } + } + }); + } + + // group ids by pattern, like {**/*.md} -> [ext.foo1, ext.bar2] + this._availableRecommendations = Object.create(null); + forEach(extensionTips, entry => { + let { key: id, value: pattern } = entry; + let ids = this._availableRecommendations[pattern]; + if (!ids) { + this._availableRecommendations[pattern] = [id]; + } else { + ids.push(id); + } + }); + + forEach(product.extensionImportantTips, entry => { + let { key: id, value } = entry; + const { pattern } = value; + let ids = this._availableRecommendations[pattern]; + if (!ids) { + this._availableRecommendations[pattern] = [id]; + } else { + ids.push(id); + } + }); + + this._modelService.onModelAdded(this._suggest, this, this._disposables); + this._modelService.getModels().forEach(model => this._suggest(model)); + } + + private _suggest(model: IModel): void { + const uri = model.uri; + + if (!uri) { + return; + } + + if (uri.scheme === Schemas.inMemory || uri.scheme === Schemas.internal || uri.scheme === Schemas.vscode) { + return; + } + + // re-schedule this bit of the operation to be off + // the critical path - in case glob-match is slow + setImmediate(() => { + + const now = Date.now(); + forEach(this._availableRecommendations, entry => { + let { key: pattern, value: ids } = entry; + if (match(pattern, uri.fsPath)) { + for (let id of ids) { + this._fileBasedRecommendations[id] = now; + } + } + }); + + this.storageService.store( + 'extensionsAssistant/recommendations', + JSON.stringify(this._fileBasedRecommendations), + StorageScope.GLOBAL + ); + + const config = this.configurationService.getConfiguration(ConfigurationKey); + + if (config.ignoreRecommendations) { + return; + } + + this.extensionsService.getInstalled(LocalExtensionType.User).done(local => { + Object.keys(this.importantRecommendations) + .filter(id => this.importantRecommendationsIgnoreList.indexOf(id) === -1) + .filter(id => local.every(local => `${local.manifest.publisher}.${local.manifest.name}` !== id)) + .forEach(id => { + const { pattern, name } = this.importantRecommendations[id]; + + if (!match(pattern, uri.fsPath)) { + return; + } + + const message = localize('reallyRecommended2', "The '{0}' extension is recommended for this file type.", name); + const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); + const options = [ + recommendationsAction.label, + localize('neverShowAgain', "Don't show again"), + localize('close', "Close") + ]; + + this.choiceService.choose(Severity.Info, message, options, 2).done(choice => { + switch (choice) { + case 0: + this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'show' }); + return recommendationsAction.run(); + case 1: this.importantRecommendationsIgnoreList.push(id); + this.storageService.store( + 'extensionsAssistant/importantRecommendationsIgnore', + JSON.stringify(this.importantRecommendationsIgnoreList), + StorageScope.GLOBAL + ); + this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'neverShowAgain' }); + return this.ignoreExtensionRecommendations(); + case 2: + this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'close' }); + } + }, () => { + this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'cancelled' }); + }); + }); + }); + }); + } + + private _suggestWorkspaceRecommendations() { + const storageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; + + if (this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) { + return; + } + + const config = this.configurationService.getConfiguration(ConfigurationKey); + + if (config.ignoreRecommendations) { + return; + } + this.getWorkspaceRecommendations().done(allRecommendations => { + if (!allRecommendations.length) { + return; + } + + this.extensionsService.getInstalled(LocalExtensionType.User).done(local => { + const recommendations = allRecommendations + .filter(id => local.every(local => `${local.manifest.publisher}.${local.manifest.name}` !== id)); + + if (!recommendations.length) { + return; + } + + const message = localize('workspaceRecommended', "This workspace has extension recommendations."); + const action = this.instantiationService.createInstance(ShowWorkspaceRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); + + const options = [ + action.label, + localize('neverShowAgain', "Don't show again"), + localize('close', "Close") + ]; + + this.choiceService.choose(Severity.Info, message, options, 2).done(choice => { + switch (choice) { + case 0: + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); + return action.run(); + case 1: + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); + return this.storageService.store(storageKey, true, StorageScope.WORKSPACE); + case 2: + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'close' }); + } + }, () => { + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); + }); + }); + }); + } + + private ignoreExtensionRecommendations() { + const message = localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations ?"); + const options = [ + localize('ignoreAll', "Yes, Ignore All"), + localize('no', "No"), + localize('cancel', "Cancel") + ]; + + this.choiceService.choose(Severity.Info, message, options, 2).done(choice => { + switch (choice) { + case 0: // If the user ignores the current message and selects different file type + // we should hide all the stacked up messages as he has selected Yes, Ignore All + this.messageService.hideAll(); + return this.setIgnoreRecommendationsConfig(true); + case 1: return this.setIgnoreRecommendationsConfig(false); + } + }); + } + + private _suggestBasedOnExecutables() { + const cmd = process.platform === 'win32' ? 'where' : 'which'; + forEach(product.exeBasedExtensionTips, entry => { + cp.exec(`${cmd} ${entry.value.replace(/,/g, ' ')}`, (err, stdout, stderr) => { + if (stdout) { + this._exeBasedRecommendations.push(entry.key); + } + }); + }); + } + + private setIgnoreRecommendationsConfig(configVal: boolean) { + let target = ConfigurationTarget.USER; + const configKey = 'extensions.ignoreRecommendations'; + this.configurationEditingService.writeConfiguration(target, { key: configKey, value: configVal }); + if (configVal) { + const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; + this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); + } + } + + getKeywordsForExtension(extension: string): string[] { + const keywords = product.extensionKeywords || {}; + return keywords[extension] || []; + } + + getRecommendationsForExtension(extension: string): string[] { + const str = `.${extension}`; + const result = Object.create(null); + + forEach(product.extensionTips || empty, entry => { + let { key: id, value: pattern } = entry; + + if (match(pattern, str)) { + result[id] = true; + } + }); + + forEach(product.extensionImportantTips || empty, entry => { + let { key: id, value } = entry; + + if (match(value.pattern, str)) { + result[id] = true; + } + }); + + return Object.keys(result); + } + + dispose() { + this._disposables = dispose(this._disposables); + } +} diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts new file mode 100644 index 0000000000..9a50c0396b --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -0,0 +1,203 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/extensions'; +import { localize } from 'vs/nls'; +import * as errors from 'vs/base/common/errors'; +import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IExtensionGalleryService, IExtensionTipsService, ExtensionsLabel, ExtensionsChannelId, PreferencesLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/parts/output/common/output'; +import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { VIEWLET_ID, IExtensionsWorkbenchService } from '../common/extensions'; +import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; +import { + OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, + ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction, ConfigureWorkspaceRecommendedExtensionsAction, + EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction +} from 'vs/workbench/parts/extensions/browser/extensionsActions'; +import { OpenExtensionsFolderAction, InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; +import { ExtensionEditor } from 'vs/workbench/parts/extensions/browser/extensionEditor'; +import { StatusUpdater } from 'vs/workbench/parts/extensions/electron-browser/extensionsViewlet'; +import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); +import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeymapExtensions, BetterMergeDisabled } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils'; +import { adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; + +// Singletons +registerSingleton(IExtensionGalleryService, ExtensionGalleryService); +registerSingleton(IExtensionTipsService, ExtensionTipsService); +registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(StatusUpdater); +workbenchRegistry.registerWorkbenchContribution(KeymapExtensions); +workbenchRegistry.registerWorkbenchContribution(BetterMergeDisabled); + +Registry.as(OutputExtensions.OutputChannels) + .registerChannel(ExtensionsChannelId, ExtensionsLabel); + +// Quickopen +Registry.as(Extensions.Quickopen).registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/extensions/browser/extensionsQuickOpen', + 'ExtensionsHandler', + 'ext ', + null, + localize('extensionsCommands', "Manage Extensions"), + true + ) +); + +Registry.as(Extensions.Quickopen).registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/extensions/browser/extensionsQuickOpen', + 'GalleryExtensionsHandler', + 'ext install ', + null, + localize('galleryExtensionsCommands', "Install Gallery Extensions"), + true + ) +); + +// Editor +const editorDescriptor = new EditorDescriptor( + ExtensionEditor.ID, + localize('extension', "Extension"), + 'vs/workbench/parts/extensions/browser/extensionEditor', + 'ExtensionEditor' +); + +Registry.as(EditorExtensions.Editors) + .registerEditor(editorDescriptor, [new SyncDescriptor(ExtensionsInput)]); + +// Viewlet +const viewletDescriptor = new ViewletDescriptor( + 'vs/workbench/parts/extensions/electron-browser/extensionsViewlet', + 'ExtensionsViewlet', + VIEWLET_ID, + localize('extensions', "Extensions"), + 'extensions', + 100 +); + +Registry.as(ViewletExtensions.Viewlets) + .registerViewlet(viewletDescriptor); + +// Global actions +const actionRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); + +const openViewletActionDescriptor = new SyncActionDescriptor(OpenExtensionsViewletAction, OpenExtensionsViewletAction.ID, OpenExtensionsViewletAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_X }); +actionRegistry.registerWorkbenchAction(openViewletActionDescriptor, 'View: Show Extensions', localize('view', "View")); + +const installActionDescriptor = new SyncActionDescriptor(InstallExtensionsAction, InstallExtensionsAction.ID, InstallExtensionsAction.LABEL); +actionRegistry.registerWorkbenchAction(installActionDescriptor, 'Extensions: Install Extensions', ExtensionsLabel); + +const listOutdatedActionDescriptor = new SyncActionDescriptor(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL); +actionRegistry.registerWorkbenchAction(listOutdatedActionDescriptor, 'Extensions: Show Outdated Extensions', ExtensionsLabel); + +const recommendationsActionDescriptor = new SyncActionDescriptor(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL); +actionRegistry.registerWorkbenchAction(recommendationsActionDescriptor, 'Extensions: Show Recommended Extensions', ExtensionsLabel); + +const keymapRecommendationsActionDescriptor = new SyncActionDescriptor(ShowRecommendedKeymapExtensionsAction, ShowRecommendedKeymapExtensionsAction.ID, ShowRecommendedKeymapExtensionsAction.SHORT_LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_M) }); +actionRegistry.registerWorkbenchAction(keymapRecommendationsActionDescriptor, 'Preferences: Keymaps', PreferencesLabel); + +const languageExtensionsActionDescriptor = new SyncActionDescriptor(ShowLanguageExtensionsAction, ShowLanguageExtensionsAction.ID, ShowLanguageExtensionsAction.SHORT_LABEL); +actionRegistry.registerWorkbenchAction(languageExtensionsActionDescriptor, 'Preferences: Language Extensions', PreferencesLabel); + +const azureExtensionsActionDescriptor = new SyncActionDescriptor(ShowAzureExtensionsAction, ShowAzureExtensionsAction.ID, ShowAzureExtensionsAction.SHORT_LABEL); +actionRegistry.registerWorkbenchAction(azureExtensionsActionDescriptor, 'Preferences: Azure Extensions', PreferencesLabel); + +const workspaceRecommendationsActionDescriptor = new SyncActionDescriptor(ShowWorkspaceRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction.ID, ShowWorkspaceRecommendedExtensionsAction.LABEL); +actionRegistry.registerWorkbenchAction(workspaceRecommendationsActionDescriptor, 'Extensions: Show Workspace Recommended Extensions', ExtensionsLabel); + +const popularActionDescriptor = new SyncActionDescriptor(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL); +actionRegistry.registerWorkbenchAction(popularActionDescriptor, 'Extensions: Show Popular Extensions', ExtensionsLabel); + +const enabledActionDescriptor = new SyncActionDescriptor(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL); +actionRegistry.registerWorkbenchAction(enabledActionDescriptor, 'Extensions: Show Enabled Extensions', ExtensionsLabel); + +const installedActionDescriptor = new SyncActionDescriptor(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL); +actionRegistry.registerWorkbenchAction(installedActionDescriptor, 'Extensions: Show Installed Extensions', ExtensionsLabel); + +const disabledActionDescriptor = new SyncActionDescriptor(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL); +actionRegistry.registerWorkbenchAction(disabledActionDescriptor, 'Extensions: Show Disabled Extensions', ExtensionsLabel); + +const updateAllActionDescriptor = new SyncActionDescriptor(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL); +actionRegistry.registerWorkbenchAction(updateAllActionDescriptor, 'Extensions: Update All Extensions', ExtensionsLabel); + +const openExtensionsFolderActionDescriptor = new SyncActionDescriptor(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL); +actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel); + +const openExtensionsFileActionDescriptor = new SyncActionDescriptor(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL); +actionRegistry.registerWorkbenchAction(openExtensionsFileActionDescriptor, 'Extensions: Configure Recommended Extensions (Workspace)', ExtensionsLabel); + +const installVSIXActionDescriptor = new SyncActionDescriptor(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); +actionRegistry.registerWorkbenchAction(installVSIXActionDescriptor, 'Extensions: Install from VSIX...', ExtensionsLabel); + +const disableAllAction = new SyncActionDescriptor(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL); +actionRegistry.registerWorkbenchAction(disableAllAction, 'Extensions: Disable All Installed Extensions', ExtensionsLabel); + +const disableAllWorkspaceAction = new SyncActionDescriptor(DisableAllWorkpsaceAction, DisableAllWorkpsaceAction.ID, DisableAllWorkpsaceAction.LABEL); +actionRegistry.registerWorkbenchAction(disableAllWorkspaceAction, 'Extensions: Disable All Installed Extensions for this Workspace', ExtensionsLabel); + +const enableAllAction = new SyncActionDescriptor(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL); +actionRegistry.registerWorkbenchAction(enableAllAction, 'Extensions: Enable All Installed Extensions', ExtensionsLabel); + +const enableAllWorkspaceAction = new SyncActionDescriptor(EnableAllWorkpsaceAction, EnableAllWorkpsaceAction.ID, EnableAllWorkpsaceAction.LABEL); +actionRegistry.registerWorkbenchAction(enableAllWorkspaceAction, 'Extensions: Enable All Installed Extensions for this Workspace', ExtensionsLabel); + +const checkForUpdatesAction = new SyncActionDescriptor(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL); +actionRegistry.registerWorkbenchAction(checkForUpdatesAction, `Extensions: Check for Updates`, ExtensionsLabel); + +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL), `Extensions: Enable Auto Updating Extensions`, ExtensionsLabel); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL), `Extensions: Disable Auto Updating Extensions`, ExtensionsLabel); + +Registry.as(ConfigurationExtensions.Configuration) + .registerConfiguration({ + id: 'extensions', + order: 30, + title: localize('extensionsConfigurationTitle', "Extensions"), + type: 'object', + properties: { + 'extensions.autoUpdate': { + type: 'boolean', + description: localize('extensionsAutoUpdate', "Automatically update extensions"), + default: true + }, + 'extensions.ignoreRecommendations': { + type: 'boolean', + description: localize('extensionsIgnoreRecommendations', "Ignore extension recommendations"), + default: false + } + } + }); + +const jsonRegistry = Registry.as(jsonContributionRegistry.Extensions.JSONContribution); +jsonRegistry.registerSchema(ExtensionsConfigurationSchemaId, ExtensionsConfigurationSchema); + +// Register Commands +CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccessor, extensionId: string) => { + const extensionService = accessor.get(IExtensionsWorkbenchService); + extensionId = adoptToGalleryExtensionId(extensionId); + const extension = extensionService.local.filter(e => e.id === extensionId); + if (extension.length === 1) { + extensionService.open(extension[0]).done(null, errors.onUnexpectedError); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts new file mode 100644 index 0000000000..22cfc145ae --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import severity from 'vs/base/common/severity'; +import paths = require('vs/base/common/paths'); +import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; +import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { remote } from 'electron'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IFileService } from 'vs/platform/files/common/files'; +import URI from 'vs/base/common/uri'; + +const dialog = remote.dialog; + +export class OpenExtensionsFolderAction extends Action { + + static ID = 'workbench.extensions.action.openExtensionsFolder'; + static LABEL = localize('openExtensionsFolder', "Open Extensions Folder"); + + constructor( + id: string, + label: string, + @IWindowsService private windowsService: IWindowsService, + @IFileService private fileService: IFileService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + super(id, label, null, true); + } + + run(): TPromise { + const extensionsHome = this.environmentService.extensionsPath; + + return this.fileService.resolveFile(URI.file(extensionsHome)).then(file => { + let itemToShow: string; + if (file.hasChildren) { + itemToShow = file.children[0].resource.fsPath; + } else { + itemToShow = paths.normalize(extensionsHome, true); + } + + return this.windowsService.showItemInFolder(itemToShow); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class InstallVSIXAction extends Action { + + static ID = 'workbench.extensions.action.installVSIX'; + static LABEL = localize('installVSIX', "Install from VSIX..."); + + constructor( + id = InstallVSIXAction.ID, + label = InstallVSIXAction.LABEL, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IMessageService private messageService: IMessageService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(id, label, 'extension-action install-vsix', true); + } + + run(): TPromise { + const result = dialog.showOpenDialog(remote.getCurrentWindow(), { + filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }], + properties: ['openFile'] + }); + + if (!result) { + return TPromise.as(null); + } + + return TPromise.join(result.map(vsix => this.extensionsWorkbenchService.install(vsix))).then(() => { + this.messageService.show( + severity.Info, + { + message: localize('InstallVSIXAction.success', "Successfully installed the extension. Restart to enable it."), + actions: [this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, localize('InstallVSIXAction.reloadNow', "Reload Now"))] + } + ); + }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts new file mode 100644 index 0000000000..7be5f3b61a --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts @@ -0,0 +1,184 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as arrays from 'vs/base/common/arrays'; +import { localize } from 'vs/nls'; +import Event, { chain, any, debounceEvent } from 'vs/base/common/event'; +import { onUnexpectedError, canceled } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService, IExtensionTipsService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IMessageService, Severity, IChoiceService } from 'vs/platform/message/common/message'; +import { Action } from 'vs/base/common/actions'; +import { BetterMergeDisabledNowKey, BetterMergeId, getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; + +export interface IExtensionStatus { + identifier: string; + local: ILocalExtension; + globallyEnabled: boolean; +} + +export class KeymapExtensions implements IWorkbenchContribution { + + private disposables: IDisposable[] = []; + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IExtensionTipsService private tipsService: IExtensionTipsService, + @IChoiceService private choiceService: IChoiceService, + @ILifecycleService lifecycleService: ILifecycleService, + @ITelemetryService private telemetryService: ITelemetryService, + ) { + this.disposables.push( + lifecycleService.onShutdown(() => this.dispose()), + instantiationService.invokeFunction(onExtensionChanged)((ids => { + TPromise.join(ids.map(id => this.checkForOtherKeymaps(id))) + .then(null, onUnexpectedError); + })) + ); + } + + getId(): string { + return 'vs.extensions.keymapExtensions'; + } + + private checkForOtherKeymaps(extensionId: string): TPromise { + return this.instantiationService.invokeFunction(getInstalledExtensions).then(extensions => { + const keymaps = extensions.filter(extension => isKeymapExtension(this.tipsService, extension)); + const extension = arrays.first(keymaps, extension => extension.identifier === extensionId); + if (extension && extension.globallyEnabled) { + const otherKeymaps = keymaps.filter(extension => extension.identifier !== extensionId && extension.globallyEnabled); + if (otherKeymaps.length) { + return this.promptForDisablingOtherKeymaps(extension, otherKeymaps); + } + } + return undefined; + }); + } + + private promptForDisablingOtherKeymaps(newKeymap: IExtensionStatus, oldKeymaps: IExtensionStatus[]): TPromise { + const telemetryData: { [key: string]: any; } = { + newKeymap: newKeymap.identifier, + oldKeymaps: oldKeymaps.map(k => k.identifier) + }; + this.telemetryService.publicLog('disableOtherKeymapsConfirmation', telemetryData); + const message = localize('disableOtherKeymapsConfirmation', "Disable other keymaps ({0}) to avoid conflicts between keybindings?", oldKeymaps.map(k => `'${k.local.manifest.displayName}'`).join(', ')); + const options = [ + localize('yes', "Yes"), + localize('no', "No") + ]; + return this.choiceService.choose(Severity.Info, message, options, 1, false) + .then(value => { + const confirmed = value === 0; + telemetryData['confirmed'] = confirmed; + this.telemetryService.publicLog('disableOtherKeymaps', telemetryData); + if (confirmed) { + return TPromise.join(oldKeymaps.map(keymap => { + return this.extensionEnablementService.setEnablement(keymap.identifier, false); + })); + } + return undefined; + }, error => TPromise.wrapError(canceled())) + .then(() => { /* drop resolved value */ }); + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + +export function onExtensionChanged(accessor: ServicesAccessor): Event { + const extensionService = accessor.get(IExtensionManagementService); + const extensionEnablementService = accessor.get(IExtensionEnablementService); + return debounceEvent(any( + chain(any(extensionService.onDidInstallExtension, extensionService.onDidUninstallExtension)) + .map(e => stripVersion(e.id)) + .event, + extensionEnablementService.onEnablementChanged + ), (list, id) => { + if (!list) { + return [id]; + } else if (list.indexOf(id) === -1) { + list.push(id); + } + return list; + }); +} + +export function getInstalledExtensions(accessor: ServicesAccessor): TPromise { + const extensionService = accessor.get(IExtensionManagementService); + const extensionEnablementService = accessor.get(IExtensionEnablementService); + return extensionService.getInstalled().then(extensions => { + const globallyDisabled = extensionEnablementService.getGloballyDisabledExtensions(); + return extensions.map(extension => { + const identifier = stripVersion(extension.id); + return { + identifier, + local: extension, + globallyEnabled: globallyDisabled.indexOf(identifier) === -1 + }; + }); + }); +} + +export function isKeymapExtension(tipsService: IExtensionTipsService, extension: IExtensionStatus): boolean { + const cats = extension.local.manifest.categories; + return cats && cats.indexOf('Keymaps') !== -1 || tipsService.getKeymapRecommendations().indexOf(extension.identifier) !== -1; +} + +function stripVersion(id: string): string { + return getIdAndVersionFromLocalExtensionId(id).id; +} + +export class BetterMergeDisabled implements IWorkbenchContribution { + + constructor( + @IStorageService storageService: IStorageService, + @IMessageService messageService: IMessageService, + @IExtensionService extensionService: IExtensionService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @ITelemetryService telemetryService: ITelemetryService, + ) { + extensionService.onReady().then(() => { + if (storageService.getBoolean(BetterMergeDisabledNowKey, StorageScope.GLOBAL, false)) { + storageService.remove(BetterMergeDisabledNowKey, StorageScope.GLOBAL); + telemetryService.publicLog('betterMergeDisabled'); + messageService.show(Severity.Info, { + message: localize('betterMergeDisabled', "The Better Merge extension is now built-in, the installed extension was disabled and can be uninstalled."), + actions: [ + new Action('uninstall', localize('uninstall', "Uninstall"), null, true, () => { + telemetryService.publicLog('betterMergeUninstall', { + outcome: 'uninstall', + }); + return extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => { + return Promise.all(extensions.filter(e => stripVersion(e.id) === BetterMergeId) + .map(e => extensionManagementService.uninstall(e, true))); + }); + }), + new Action('later', localize('later', "Later"), null, true, () => { + telemetryService.publicLog('betterMergeUninstall', { + outcome: 'later', + }); + return TPromise.as(true); + }) + ] + }); + } + }); + } + + getId(): string { + return 'vs.extensions.betterMergeDisabled'; + } +} diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts new file mode 100644 index 0000000000..42dc6e4f9e --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -0,0 +1,437 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/extensionsViewlet'; +import { localize } from 'vs/nls'; +import { ThrottledDelayer, always } from 'vs/base/common/async'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { isPromiseCanceledError, onUnexpectedError, create as createError } from 'vs/base/common/errors'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Builder, Dimension } from 'vs/base/browser/builder'; +import EventOf, { mapEvent, chain } from 'vs/base/common/event'; +import { IAction } from 'vs/base/common/actions'; +import { domEvent } from 'vs/base/browser/event'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IViewlet } from 'vs/workbench/common/viewlet'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { append, $, addStandardDisposableListener, EventType, addClass, removeClass, toggleClass } from 'vs/base/browser/dom'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionState } from '../common/extensions'; +import { + ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction, + ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, + EnableAutoUpdateAction, DisableAutoUpdateAction +} from 'vs/workbench/parts/extensions/browser/extensionsActions'; +import { LocalExtensionType, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; +import { ExtensionsListView, InstalledExtensionsView, RecommendedExtensionsView } from './extensionsViews'; +import { OpenGlobalSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IMessageService, CloseAction } from 'vs/platform/message/common/message'; +import Severity from 'vs/base/common/severity'; +import { IActivityBarService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activityBarService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { inputForeground, inputBackground, inputBorder } from 'vs/platform/theme/common/colorRegistry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ViewsRegistry, ViewLocation, IViewDescriptor } from 'vs/workbench/parts/views/browser/viewsRegistry'; +import { PersistentViewsViewlet, IView } from 'vs/workbench/parts/views/browser/views'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; + +interface SearchInputEvent extends Event { + target: HTMLInputElement; + immediate?: boolean; +} + +const ExtensionsViewletVisibleContext = new RawContextKey('extensionsViewletVisible', false); +const SearchExtensionsContext = new RawContextKey('searchExtensions', false); +const SearchInstalledExtensionsContext = new RawContextKey('searchInstalledExtensions', false); +const SearchRecommendedExtensionsContext = new RawContextKey('searchRecommendedExtensions', false); + +export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtensionsViewlet { + + private onSearchChange: EventOf; + private extensionsViewletVisibleContextKey: IContextKey; + private searchExtensionsContextKey: IContextKey; + private searchInstalledExtensionsContextKey: IContextKey; + private searchRecommendedExtensionsContextKey: IContextKey; + + private searchDelayer: ThrottledDelayer; + private root: HTMLElement; + + private searchBox: HTMLInputElement; + private extensionsBox: HTMLElement; + private primaryActions: IAction[]; + private secondaryActions: IAction[]; + private disposables: IDisposable[] = []; + + private isAutoUpdateEnabled: boolean; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IProgressService private progressService: IProgressService, + @IInstantiationService instantiationService: IInstantiationService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEditorGroupService private editorInputService: IEditorGroupService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionManagementService private extensionManagementService: IExtensionManagementService, + @IMessageService private messageService: IMessageService, + @IViewletService private viewletService: IViewletService, + @IThemeService themeService: IThemeService, + @IConfigurationService private configurationService: IConfigurationService, + @IStorageService storageService: IStorageService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IContextKeyService contextKeyService: IContextKeyService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService + ) { + super(VIEWLET_ID, ViewLocation.Extensions, `${VIEWLET_ID}.state`, true, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); + + this.registerViews(); + this.searchDelayer = new ThrottledDelayer(500); + this.extensionsViewletVisibleContextKey = ExtensionsViewletVisibleContext.bindTo(contextKeyService); + this.searchExtensionsContextKey = SearchExtensionsContext.bindTo(contextKeyService); + this.searchInstalledExtensionsContextKey = SearchInstalledExtensionsContext.bindTo(contextKeyService); + this.searchRecommendedExtensionsContextKey = SearchRecommendedExtensionsContext.bindTo(contextKeyService); + + this.disposables.push(viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables)); + this.isAutoUpdateEnabled = this.extensionsWorkbenchService.isAutoUpdateEnabled; + + this.configurationService.onDidUpdateConfiguration(() => { + const isAutoUpdateEnabled = this.extensionsWorkbenchService.isAutoUpdateEnabled; + if (this.isAutoUpdateEnabled !== isAutoUpdateEnabled) { + this.isAutoUpdateEnabled = isAutoUpdateEnabled; + this.secondaryActions = null; + this.updateTitleArea(); + } + }, this, this.disposables); + } + + private registerViews(): void { + let viewDescriptors = []; + viewDescriptors.push(this.createMarketPlaceExtensionsListViewDescriptor()); + viewDescriptors.push(this.createInstalledExtensionsListViewDescriptor()); + viewDescriptors.push(this.createSearchInstalledExtensionsListViewDescriptor()); + viewDescriptors.push(this.createRecommendedExtensionsListViewDescriptor()); + ViewsRegistry.registerViews(viewDescriptors); + } + + private createMarketPlaceExtensionsListViewDescriptor(): IViewDescriptor { + return { + id: 'extensions.listView', + name: localize('marketPlace', "Marketplace"), + location: ViewLocation.Extensions, + ctor: ExtensionsListView, + when: ContextKeyExpr.and(ContextKeyExpr.has('extensionsViewletVisible'), ContextKeyExpr.has('searchExtensions'), ContextKeyExpr.not('searchInstalledExtensions')), + size: 100 + }; + } + + private createInstalledExtensionsListViewDescriptor(): IViewDescriptor { + return { + id: 'extensions.installedList', + name: localize('installedExtensions', "Installed"), + location: ViewLocation.Extensions, + ctor: InstalledExtensionsView, + when: ContextKeyExpr.and(ContextKeyExpr.has('extensionsViewletVisible'), ContextKeyExpr.not('searchExtensions')), + size: 50 + }; + } + + private createSearchInstalledExtensionsListViewDescriptor(): IViewDescriptor { + return { + id: 'extensions.searchInstalledList', + name: localize('searchInstalledExtensions', "Installed"), + location: ViewLocation.Extensions, + ctor: InstalledExtensionsView, + when: ContextKeyExpr.and(ContextKeyExpr.has('extensionsViewletVisible'), ContextKeyExpr.has('searchInstalledExtensions')), + size: 50 + }; + } + + private createRecommendedExtensionsListViewDescriptor(): IViewDescriptor { + return { + id: 'extensions.recommendedList', + name: localize('recommendedExtensions', "Recommended"), + location: ViewLocation.Extensions, + ctor: RecommendedExtensionsView, + when: ContextKeyExpr.and(ContextKeyExpr.has('extensionsViewletVisible'), ContextKeyExpr.not('searchExtensions')), + size: 50, + canToggleVisibility: true + }; + } + + create(parent: Builder): TPromise { + parent.addClass('extensions-viewlet'); + this.root = parent.getHTMLElement(); + + const header = append(this.root, $('.header')); + + this.searchBox = append(header, $('input.search-box')); + this.searchBox.placeholder = localize('searchExtensions', "Search Extensions in Marketplace"); + this.disposables.push(addStandardDisposableListener(this.searchBox, EventType.FOCUS, () => addClass(this.searchBox, 'synthetic-focus'))); + this.disposables.push(addStandardDisposableListener(this.searchBox, EventType.BLUR, () => removeClass(this.searchBox, 'synthetic-focus'))); + + this.extensionsBox = append(this.root, $('.extensions')); + + const onKeyDown = chain(domEvent(this.searchBox, 'keydown')) + .map(e => new StandardKeyboardEvent(e)); + onKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(this.onEscape, this, this.disposables); + + const onKeyDownForList = onKeyDown.filter(() => this.count() > 0); + onKeyDownForList.filter(e => e.keyCode === KeyCode.Enter).on(this.onEnter, this, this.disposables); + onKeyDownForList.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this, this.disposables); + onKeyDownForList.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this, this.disposables); + onKeyDownForList.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUpArrow, this, this.disposables); + onKeyDownForList.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDownArrow, this, this.disposables); + + const onSearchInput = domEvent(this.searchBox, 'input') as EventOf; + onSearchInput(e => this.triggerSearch(e.immediate), null, this.disposables); + + this.onSearchChange = mapEvent(onSearchInput, e => e.target.value); + + return this.extensionManagementService.getInstalled(LocalExtensionType.User) + .then(installed => { + if (installed.length === 0) { + this.searchBox.value = '@sort:installs'; + this.searchExtensionsContextKey.set(true); + } + return super.create(new Builder(this.extensionsBox)); + }); + } + + public updateStyles(): void { + super.updateStyles(); + + this.searchBox.style.backgroundColor = this.getColor(inputBackground); + this.searchBox.style.color = this.getColor(inputForeground); + + const inputBorderColor = this.getColor(inputBorder); + this.searchBox.style.borderWidth = inputBorderColor ? '1px' : null; + this.searchBox.style.borderStyle = inputBorderColor ? 'solid' : null; + this.searchBox.style.borderColor = inputBorderColor; + } + + setVisible(visible: boolean): TPromise { + const isVisibilityChanged = this.isVisible() !== visible; + return super.setVisible(visible).then(() => { + if (isVisibilityChanged) { + this.extensionsViewletVisibleContextKey.set(visible); + if (visible) { + this.searchBox.focus(); + this.searchBox.setSelectionRange(0, this.searchBox.value.length); + } + } + }); + } + + focus(): void { + this.searchBox.focus(); + } + + layout(dimension: Dimension): void { + toggleClass(this.root, 'narrow', dimension.width <= 300); + super.layout(new Dimension(dimension.width, dimension.height - 38)); + } + + getOptimalWidth(): number { + return 400; + } + + getActions(): IAction[] { + if (!this.primaryActions) { + this.primaryActions = [ + this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange) + ]; + } + return this.primaryActions; + } + + getSecondaryActions(): IAction[] { + if (!this.secondaryActions) { + this.secondaryActions = [ + this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL), + this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL), + this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL), + this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL), + this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL), + this.instantiationService.createInstance(ShowWorkspaceRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction.ID, ShowWorkspaceRecommendedExtensionsAction.LABEL), + this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL), + new Separator(), + this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Sort By: Install Count"), this.onSearchChange, 'installs'), + this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Sort By: Rating"), this.onSearchChange, 'rating'), + this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Sort By: Name"), this.onSearchChange, 'name'), + new Separator(), + this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL), + ...(this.isAutoUpdateEnabled ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]), + this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL), + new Separator(), + this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL), + this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL) + ]; + } + + return this.secondaryActions; + } + + search(value: string): void { + const event = new Event('input', { bubbles: true }) as SearchInputEvent; + event.immediate = true; + + this.searchBox.value = value; + this.searchBox.dispatchEvent(event); + } + + private triggerSearch(immediate = false): void { + this.searchDelayer.trigger(() => this.doSearch(), immediate || !this.searchBox.value ? 0 : 500) + .done(null, err => this.onError(err)); + } + + private async doSearch(): TPromise { + const value = this.searchBox.value || ''; + this.searchExtensionsContextKey.set(!!value); + this.searchInstalledExtensionsContextKey.set(InstalledExtensionsView.isInsalledExtensionsQuery(value)); + this.searchRecommendedExtensionsContextKey.set(RecommendedExtensionsView.isRecommendedExtensionsQuery(value)); + + await this.updateViews([], !!value); + } + + protected async updateViews(unregisteredViews: IViewDescriptor[] = [], showAll = false): TPromise { + const created = await super.updateViews(); + const toShow = showAll ? this.views : created; + if (toShow.length) { + await this.progress(TPromise.join(toShow.map(view => (view).show(this.searchBox.value)))); + } + return created; + } + + private count(): number { + return this.views.reduce((count, view) => (view).count() + count, 0); + } + + private onEscape(): void { + this.search(''); + } + + private onEnter(): void { + (this.views[0]).select(); + } + + private onUpArrow(): void { + (this.views[0]).showPrevious(); + } + + private onDownArrow(): void { + (this.views[0]).showNext(); + } + + private onPageUpArrow(): void { + (this.views[0]).showPreviousPage(); + } + + private onPageDownArrow(): void { + (this.views[0]).showNextPage(); + } + + private onViewletOpen(viewlet: IViewlet): void { + if (!viewlet || viewlet.getId() === VIEWLET_ID) { + return; + } + + const model = this.editorInputService.getStacksModel(); + + const promises = model.groups.map(group => { + const position = model.positionOfGroup(group); + const inputs = group.getEditors().filter(input => input instanceof ExtensionsInput); + const promises = inputs.map(input => this.editorService.closeEditor(position, input)); + + return TPromise.join(promises); + }); + + TPromise.join(promises).done(null, onUnexpectedError); + } + + private progress(promise: TPromise): TPromise { + const progressRunner = this.progressService.show(true); + return always(promise, () => progressRunner.done()); + } + + private onError(err: any): void { + if (isPromiseCanceledError(err)) { + return; + } + + const message = err && err.message || ''; + + if (/ECONNREFUSED/.test(message)) { + const error = createError(localize('suggestProxyError', "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting."), { + actions: [ + this.instantiationService.createInstance(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL), + CloseAction + ] + }); + + this.messageService.show(Severity.Error, error); + return; + } + + this.messageService.show(Severity.Error, err); + } + + dispose(): void { + this.disposables = dispose(this.disposables); + super.dispose(); + } +} + +export class StatusUpdater implements IWorkbenchContribution { + + private disposables: IDisposable[]; + private badgeHandle: IDisposable; + + constructor( + @IActivityBarService private activityBarService: IActivityBarService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + extensionsWorkbenchService.onChange(this.onServiceChange, this, this.disposables); + } + + getId(): string { + return 'vs.extensions.statusupdater'; + } + + private onServiceChange(): void { + + dispose(this.badgeHandle); + + if (this.extensionsWorkbenchService.local.some(e => e.state === ExtensionState.Installing)) { + this.badgeHandle = this.activityBarService.showActivity(VIEWLET_ID, new ProgressBadge(() => localize('extensions', "Extensions")), 'extensions-badge progress-badge'); + return; + } + + const outdated = this.extensionsWorkbenchService.local.reduce((r, e) => r + (e.outdated ? 1 : 0), 0); + if (outdated > 0) { + const badge = new NumberBadge(outdated, n => localize('outdatedExtensions', '{0} Outdated Extensions', n)); + this.badgeHandle = this.activityBarService.showActivity(VIEWLET_ID, badge, 'extensions-badge count-badge'); + } + } + + dispose(): void { + this.disposables = dispose(this.disposables); + dispose(this.badgeHandle); + } +} diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts new file mode 100644 index 0000000000..14ff5c35f6 --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -0,0 +1,478 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { assign } from 'vs/base/common/objects'; +import { distinct } from 'vs/base/common/arrays'; +import { chain } from 'vs/base/common/event'; +import { isPromiseCanceledError, create as createError } from 'vs/base/common/errors'; +import Severity from 'vs/base/common/severity'; +import { PagedModel, IPagedModel, mergePagers, IPager } from 'vs/base/common/paging'; +import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview'; +import { IMessageService, CloseAction } from 'vs/platform/message/common/message'; +import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { append, $, toggleClass } from 'vs/base/browser/dom'; +import { PagedList } from 'vs/base/browser/ui/list/listPaging'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Delegate, Renderer } from 'vs/workbench/parts/extensions/browser/extensionsList'; +import { IExtension, IExtensionsWorkbenchService } from '../common/extensions'; +import { Query } from '../common/extensionQuery'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachListStyler, attachBadgeStyler } from 'vs/platform/theme/common/styler'; +import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/parts/views/browser/views'; +import { OpenGlobalSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; + +export class ExtensionsListView extends CollapsibleView { + + private messageBox: HTMLElement; + private extensionsList: HTMLElement; + private badge: CountBadge; + + private list: PagedList; + private disposables: IDisposable[] = []; + + constructor( + initialSize: number, + private options: IViewletViewOptions, + @IMessageService private messageService: IMessageService, + @IKeybindingService keybindingService: IKeybindingService, + @IContextMenuService contextMenuService: IContextMenuService, + @IInstantiationService private instantiationService: IInstantiationService, + @IListService private listService: IListService, + @IThemeService private themeService: IThemeService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IExtensionService private extensionService: IExtensionService, + @ICommandService private commandService: ICommandService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEditorGroupService private editorInputService: IEditorGroupService, + @IExtensionTipsService private tipsService: IExtensionTipsService, + @IModeService private modeService: IModeService, + @ITelemetryService private telemetryService: ITelemetryService, + @IProgressService private progressService: IProgressService + ) { + super(initialSize, { ...(options as IViewOptions), ariaHeaderLabel: options.name, sizing: ViewSizing.Flexible, collapsed: !!options.collapsed, initialBodySize: 1 * 62 }, keybindingService, contextMenuService); + } + + renderHeader(container: HTMLElement): void { + const titleDiv = append(container, $('div.title')); + append(titleDiv, $('span')).textContent = this.options.name; + this.badge = new CountBadge(append(container, $('.count-badge-wrapper'))); + this.disposables.push(attachBadgeStyler(this.badge, this.themeService)); + } + + renderBody(container: HTMLElement): void { + this.extensionsList = append(container, $('.extensions-list')); + this.messageBox = append(container, $('.message')); + const delegate = new Delegate(); + const renderer = this.instantiationService.createInstance(Renderer); + this.list = new PagedList(this.extensionsList, delegate, [renderer], { + ariaLabel: localize('extensions', "Extensions"), + keyboardSupport: false + }); + + this.disposables.push(attachListStyler(this.list.widget, this.themeService)); + this.disposables.push(this.listService.register(this.list.widget)); + + chain(this.list.onSelectionChange) + .map(e => e.elements[0]) + .filter(e => !!e) + .on(this.openExtension, this, this.disposables); + + chain(this.list.onPin) + .map(e => e.elements[0]) + .filter(e => !!e) + .on(this.pin, this, this.disposables); + } + + setVisible(visible: boolean): TPromise { + return super.setVisible(visible).then(() => { + if (!visible) { + this.setModel(new PagedModel([])); + } + }); + } + + layoutBody(size: number): void { + this.extensionsList.style.height = size + 'px'; + this.list.layout(size); + } + + async show(query: string): TPromise> { + const model = await this.query(query); + this.setModel(model); + return model; + } + + select(): void { + this.list.setSelection(this.list.getFocus()); + } + + showPrevious(): void { + this.list.focusPrevious(); + this.list.reveal(this.list.getFocus()[0]); + } + + showPreviousPage(): void { + this.list.focusPreviousPage(); + this.list.reveal(this.list.getFocus()[0]); + } + + showNext(): void { + this.list.focusNext(); + this.list.reveal(this.list.getFocus()[0]); + } + + showNextPage(): void { + this.list.focusNextPage(); + this.list.reveal(this.list.getFocus()[0]); + } + + count(): number { + return this.list.length; + } + + private async query(value: string): TPromise> { + const query = Query.parse(value); + + let options: IQueryOptions = { + sortOrder: SortOrder.Default + }; + + switch (query.sortBy) { + case 'installs': options = assign(options, { sortBy: SortBy.InstallCount }); break; + case 'rating': options = assign(options, { sortBy: SortBy.WeightedRating }); break; + case 'name': options = assign(options, { sortBy: SortBy.Title }); break; + } + + if (!value || ExtensionsListView.isInstalledExtensionsQuery(value)) { + // Show installed extensions + value = value ? value.replace(/@installed/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : ''; + + let result = await this.extensionsWorkbenchService.queryLocal(); + + switch (options.sortBy) { + case SortBy.InstallCount: + result = result.sort((e1, e2) => e2.installCount - e1.installCount); + break; + case SortBy.AverageRating: + case SortBy.WeightedRating: + result = result.sort((e1, e2) => e2.rating - e1.rating); + break; + default: + result = result.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)); + break; + } + + if (options.sortOrder === SortOrder.Descending) { + result = result.reverse(); + } + + result = result + .filter(e => e.type === LocalExtensionType.User && e.name.toLowerCase().indexOf(value) > -1); + + return new PagedModel(result); + } + + if (/@outdated/i.test(value)) { + value = value.replace(/@outdated/g, '').trim().toLowerCase(); + + const local = await this.extensionsWorkbenchService.queryLocal(); + const result = local + .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) + .filter(extension => extension.outdated && extension.name.toLowerCase().indexOf(value) > -1); + + return new PagedModel(result); + } + + if (/@disabled/i.test(value)) { + value = value.replace(/@disabled/g, '').trim().toLowerCase(); + + const local = await this.extensionsWorkbenchService.queryLocal(); + const runningExtensions = await this.extensionService.getExtensions(); + + const result = local + .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) + .filter(e => runningExtensions.every(r => !areSameExtensions(r, e)) && e.name.toLowerCase().indexOf(value) > -1); + + return new PagedModel(result); + } + + if (/@enabled/i.test(value)) { + value = value ? value.replace(/@enabled/g, '').trim().toLowerCase() : ''; + + const local = await this.extensionsWorkbenchService.queryLocal(); + + const result = local + .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) + .filter(e => e.type === LocalExtensionType.User && + !(e.disabledForWorkspace || e.disabledGlobally) && + e.name.toLowerCase().indexOf(value) > -1 + ); + + return new PagedModel(result); + } + + if (ExtensionsListView.isWorkspaceRecommendedExtensionsQuery(query.value)) { + return this.getWorkspaceRecommendationsModel(query, options); + } else if (ExtensionsListView.isKeymapsRecommendedExtensionsQuery(query.value)) { + return this.getKeymapRecommendationsModel(query, options); + } else if (/@recommended:all/i.test(query.value)) { + return this.getAllRecommendationsModel(query, options); + } else if (ExtensionsListView.isRecommendedExtensionsQuery(query.value)) { + return this.getRecommendationsModel(query, options); + } + + const pagerPromises: TPromise>[] = []; + let text = query.value; + const extensionRegex = /\bext:([^\s]+)\b/g; + + if (extensionRegex.test(query.value)) { + let names: string[] = []; + + text = query.value.replace(extensionRegex, (m, ext) => { + names.push(...this.tipsService.getRecommendationsForExtension(ext)); + + // Get curated keywords + const keywords = this.tipsService.getKeywordsForExtension(ext); + + // Get mode name + const modeId = this.modeService.getModeIdByFilenameOrFirstLine(`.${ext}`); + const languageName = modeId && this.modeService.getLanguageName(modeId); + const languageTag = languageName ? ` tag:"${languageName}"` : ''; + + // Construct a rich query + return `tag:"__ext_${ext}"${keywords.map(tag => ` tag:${tag}`)}${languageTag}`; + }); + + if (names.length) { + const namesOptions = assign({}, options, { names }); + pagerPromises.push(this.extensionsWorkbenchService.queryGallery(namesOptions)); + } + } + + if (text) { + options = assign(options, { text: text.substr(0, 350) }); + } + + pagerPromises.push(this.extensionsWorkbenchService.queryGallery(options)); + + const pagers = await TPromise.join(pagerPromises); + const pager = pagers.length === 2 ? mergePagers(pagers[0], pagers[1]) : pagers[0]; + + return new PagedModel(pager); + } + + private getAllRecommendationsModel(query: Query, options: IQueryOptions): TPromise> { + const value = query.value.replace(/@recommended:all/g, '').trim().toLowerCase(); + + return this.extensionsWorkbenchService.queryLocal() + .then(result => result.filter(e => e.type === LocalExtensionType.User)) + .then(local => { + return TPromise.join([TPromise.as(this.tipsService.getRecommendations()), this.tipsService.getWorkspaceRecommendations()]) + .then(([recommendations, workspaceRecommendations]) => { + const names = distinct([...recommendations, ...workspaceRecommendations]) + .filter(name => local.every(ext => `${ext.publisher}.${ext.name}` !== name)) + .filter(name => name.toLowerCase().indexOf(value) > -1); + + if (!names.length) { + return TPromise.as(new PagedModel([])); + } + options.source = 'recommendations-all'; + return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length })) + .then(pager => new PagedModel(pager || [])); + }); + }); + } + + private getRecommendationsModel(query: Query, options: IQueryOptions): TPromise> { + const value = query.value.replace(/@recommended/g, '').trim().toLowerCase(); + + return this.extensionsWorkbenchService.queryLocal() + .then(result => result.filter(e => e.type === LocalExtensionType.User)) + .then(local => { + const names = this.tipsService.getRecommendations() + .filter(name => local.every(ext => `${ext.publisher}.${ext.name}` !== name)) + .filter(name => name.toLowerCase().indexOf(value) > -1); + + this.telemetryService.publicLog('extensionRecommendations:open', { count: names.length }); + + if (!names.length) { + return TPromise.as(new PagedModel([])); + } + options.source = 'recommendations'; + return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length })) + .then(pager => new PagedModel(pager || [])); + }); + } + + private getWorkspaceRecommendationsModel(query: Query, options: IQueryOptions): TPromise> { + const value = query.value.replace(/@recommended:workspace/g, '').trim().toLowerCase(); + return this.tipsService.getWorkspaceRecommendations() + .then(recommendations => { + const names = recommendations.filter(name => name.toLowerCase().indexOf(value) > -1); + this.telemetryService.publicLog('extensionWorkspaceRecommendations:open', { count: names.length }); + + if (!names.length) { + return TPromise.as(new PagedModel([])); + } + options.source = 'recommendations-workspace'; + return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length })) + .then(pager => new PagedModel(pager || [])); + }); + } + + private getKeymapRecommendationsModel(query: Query, options: IQueryOptions): TPromise> { + const value = query.value.replace(/@recommended:keymaps/g, '').trim().toLowerCase(); + const names = this.tipsService.getKeymapRecommendations() + .filter(name => name.toLowerCase().indexOf(value) > -1); + this.telemetryService.publicLog('extensionKeymapRecommendations:open', { count: names.length }); + + if (!names.length) { + return TPromise.as(new PagedModel([])); + } + options.source = 'recommendations-keymaps'; + return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length })) + .then(result => new PagedModel(result)); + } + + private setModel(model: IPagedModel) { + this.list.model = model; + this.list.scrollTop = 0; + const count = this.count(); + + toggleClass(this.extensionsList, 'hidden', count === 0); + toggleClass(this.messageBox, 'hidden', count > 0); + this.badge.setCount(count); + + if (count === 0 && this.isVisible()) { + this.messageBox.textContent = localize('no extensions found', "No extensions found."); + } else { + this.messageBox.textContent = ''; + } + } + + private openExtension(extension: IExtension): void { + this.extensionsWorkbenchService.open(extension).done(null, err => this.onError(err)); + } + + private pin(): void { + const activeEditor = this.editorService.getActiveEditor(); + const activeEditorInput = this.editorService.getActiveEditorInput(); + + this.editorInputService.pinEditor(activeEditor.position, activeEditorInput); + } + + + private onError(err: any): void { + if (isPromiseCanceledError(err)) { + return; + } + + const message = err && err.message || ''; + + if (/ECONNREFUSED/.test(message)) { + const error = createError(localize('suggestProxyError', "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting."), { + actions: [ + this.instantiationService.createInstance(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL), + CloseAction + ] + }); + + this.messageService.show(Severity.Error, error); + return; + } + + this.messageService.show(Severity.Error, err); + } + + dispose(): void { + this.disposables = dispose(this.disposables); + super.dispose(); + } + + static isInstalledExtensionsQuery(query: string): boolean { + return /@installed/i.test(query); + } + + static isOutdatedExtensionsQuery(query: string): boolean { + return /@outdated/i.test(query); + } + + static isDisabledExtensionsQuery(query: string): boolean { + return /@disabled/i.test(query); + } + + static isEnabledExtensionsQuery(query: string): boolean { + return /@enabled/i.test(query); + } + + static isRecommendedExtensionsQuery(query: string): boolean { + return /@recommended/i.test(query); + } + + static isWorkspaceRecommendedExtensionsQuery(query: string): boolean { + return /@recommended:workspace/i.test(query); + } + + static isKeymapsRecommendedExtensionsQuery(query: string): boolean { + return /@recommended:keymaps/i.test(query); + } +} + +export class InstalledExtensionsView extends ExtensionsListView { + + public static isInsalledExtensionsQuery(query: string): boolean { + return ExtensionsListView.isInstalledExtensionsQuery(query) + || ExtensionsListView.isOutdatedExtensionsQuery(query) + || ExtensionsListView.isDisabledExtensionsQuery(query) + || ExtensionsListView.isEnabledExtensionsQuery(query); + } + + async show(query: string): TPromise> { + if (InstalledExtensionsView.isInsalledExtensionsQuery(query)) { + return super.show(query); + } + let searchInstalledQuery = '@installed'; + searchInstalledQuery = query ? searchInstalledQuery + ' ' + query : searchInstalledQuery; + return super.show(searchInstalledQuery); + } + +} + +export class RecommendedExtensionsView extends ExtensionsListView { + + public static isRecommendedExtensionsQuery(query: string): boolean { + return ExtensionsListView.isRecommendedExtensionsQuery(query) + || ExtensionsListView.isWorkspaceRecommendedExtensionsQuery(query); + } + + async show(query: string): TPromise> { + if (RecommendedExtensionsView.isRecommendedExtensionsQuery(query)) { + return super.show(query); + } + let searchInstalledQuery = '@recommended:all'; + searchInstalledQuery = query ? searchInstalledQuery + ' ' + query : searchInstalledQuery; + return super.show(searchInstalledQuery); + } + +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensions-dark.svg b/src/vs/workbench/parts/extensions/electron-browser/media/extensions-dark.svg new file mode 100644 index 0000000000..4a1571a3c5 --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensions-dark.svg @@ -0,0 +1,6 @@ + + + + diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css new file mode 100644 index 0000000000..2c42bff43f --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench > .activitybar > .content .monaco-action-bar .action-label.extensions { + -webkit-mask: url('extensions-dark.svg') no-repeat 50% 50%; + -webkit-mask-size: 22px; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css new file mode 100644 index 0000000000..02a2a553d9 --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css @@ -0,0 +1,180 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.extensions-viewlet { + height: 100%; +} + +.extensions-viewlet > .header { + height: 38px; + box-sizing: border-box; + padding: 5px 9px 5px 16px; +} + +.extensions-viewlet > .header > .search-box { + width: 100%; + height: 28px; + line-height: 18px; + box-sizing: border-box; + padding: 4px; + border: 1px solid transparent; + -webkit-appearance: textfield; +} + +.extensions-viewlet > .extensions { + height: calc(100% - 38px); +} + +.extensions-viewlet > .extensions .extensions-list.hidden, +.extensions-viewlet > .extensions .message.hidden { + display: none; + visibility: hidden; +} + +.extensions-viewlet > .extensions .header { + padding-right: 12px; +} + +.extensions-viewlet > .extensions .header > .title { + flex: 1; +} + +.extensions-viewlet > .extensions .message { + padding: 5px 9px 5px 16px; + cursor: default; +} + +.extensions-viewlet > .extensions .extension { + box-sizing: border-box; + width: 100%; + height: 100%; + padding: 0 11px 0 16px; + overflow: hidden; + display: flex; +} + +.extensions-viewlet > .extensions .extension.loading { + background: url('loading.svg') center center no-repeat; +} + +.extensions-viewlet > .extensions .extension > .icon { + width: 42px; + height: 42px; + padding: 10px 14px 10px 0; + flex-shrink: 0; + object-fit: contain; +} + +.extensions-viewlet.narrow > .extensions .extension > .icon { + display: none; +} + +.extensions-viewlet > .extensions .extension.loading > .icon { + display: none; +} + +.extensions-viewlet > .extensions .extension > .details { + flex: 1; + padding: 4px 0; + overflow: hidden; +} + +.extensions-viewlet > .extensions .extension > .details > .header-container { + height: 19px; + display: flex; + overflow: hidden; +} + +.extensions-viewlet > .extensions .extension > .details > .header-container > .header { + display: flex; + align-items: baseline; + flex-wrap: nowrap; + overflow: hidden; + flex: 1; + min-width: 0; + +} + +.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .name { + font-weight: bold; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .version { + opacity: 0.85; + font-size: 80%; + padding-left: 6px; + flex: 1; + min-width: fit-content; +} + +.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .install-count:not(:empty) { + font-size: 80%; + margin: 0 6px; +} + +.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .install-count > .octicon { + font-size: 100%; + margin-right: 2px; +} + +.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .ratings { + text-align: right; +} + +.extensions-viewlet.narrow > .extensions .extension > .details > .header-container > .header > .ratings, +.extensions-viewlet.narrow > .extensions .extension > .details > .header-container > .header > .install-count { + display: none; +} + +.extensions-viewlet > .extensions .extension > .details > .footer { + display: flex; + justify-content: flex-end; + height: 18px; + overflow: hidden; +} + +.extensions-viewlet > .extensions .extension > .details > .footer > .author { + flex: 1; + font-size: 90%; + padding-right: 6px; + opacity: 0.6; + font-weight: 600; +} + +.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar > .actions-container { + flex-wrap: wrap-reverse; +} + +.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar .action-label { + margin-right: 0; + margin-left: 0.3em; + line-height: 15px; +} + +.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar .action-label:not(:empty) { + opacity: 0.9; +} + +.extensions-viewlet > .extensions .extension.disabled > .icon, +.extensions-viewlet > .extensions .extension.disabled > .details > .header-container, +.extensions-viewlet > .extensions .extension.disabled > .details > .description +.extensions-viewlet > .extensions .extension.disabled > .details > .footer > .author { + opacity: 0.5; +} + +.extensions-viewlet > .extensions .extension .ellipsis { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.extensions-badge.progress-badge > .badge-content { + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMiAyIDE0IDE0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDIgMiAxNCAxNCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTkgMTZjLTMuODYgMC03LTMuMTQtNy03czMuMTQtNyA3LTdjMy44NTkgMCA3IDMuMTQxIDcgN3MtMy4xNDEgNy03IDd6bTAtMTIuNmMtMy4wODggMC01LjYgMi41MTMtNS42IDUuNnMyLjUxMiA1LjYgNS42IDUuNiA1LjYtMi41MTIgNS42LTUuNi0yLjUxMi01LjYtNS42LTUuNnptMy44NiA3LjFsLTMuMTYtMS44OTZ2LTMuODA0aC0xLjR2NC41OTZsMy44NCAyLjMwNS43Mi0xLjIwMXoiLz48L3N2Zz4="); + background-position: center center; + background-repeat: no-repeat; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/loading.svg b/src/vs/workbench/parts/extensions/electron-browser/media/loading.svg new file mode 100644 index 0000000000..0098fd9f26 --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/media/loading.svg @@ -0,0 +1,36 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts new file mode 100644 index 0000000000..3a813b0a59 --- /dev/null +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -0,0 +1,856 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import { readFile } from 'vs/base/node/pfs'; +import * as semver from 'semver'; +import * as path from 'path'; +import Event, { Emitter, chain } from 'vs/base/common/event'; +import { index } from 'vs/base/common/arrays'; +import { assign } from 'vs/base/common/objects'; +import { ThrottledDelayer } from 'vs/base/common/async'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { + IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionManifest, + InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionTipsService +} from 'vs/platform/extensionManagement/common/extensionManagement'; +import { getGalleryExtensionIdFromLocal, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IChoiceService, IMessageService } from 'vs/platform/message/common/message'; +import Severity from 'vs/base/common/severity'; +import URI from 'vs/base/common/uri'; +import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenchService, IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IURLService } from 'vs/platform/url/common/url'; +import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import product from 'vs/platform/node/product'; + +interface IExtensionStateProvider { + (extension: Extension): ExtensionState; +} + +class Extension implements IExtension { + + public disabledGlobally = false; + public disabledForWorkspace = false; + + constructor( + private galleryService: IExtensionGalleryService, + private stateProvider: IExtensionStateProvider, + public local: ILocalExtension, + public gallery: IGalleryExtension = null + ) { } + + get type(): LocalExtensionType { + return this.local ? this.local.type : null; + } + + get name(): string { + return this.gallery ? this.gallery.name : this.local.manifest.name; + } + + get displayName(): string { + if (this.gallery) { + return this.gallery.displayName || this.gallery.name; + } + + return this.local.manifest.displayName || this.local.manifest.name; + } + + get id(): string { + if (this.gallery) { + return this.gallery.id; + } + return getGalleryExtensionIdFromLocal(this.local); + } + + get publisher(): string { + return this.gallery ? this.gallery.publisher : this.local.manifest.publisher; + } + + get publisherDisplayName(): string { + if (this.gallery) { + return this.gallery.publisherDisplayName || this.gallery.publisher; + } + + if (this.local.metadata && this.local.metadata.publisherDisplayName) { + return this.local.metadata.publisherDisplayName; + } + + return this.local.manifest.publisher; + } + + get version(): string { + return this.local ? this.local.manifest.version : this.gallery.version; + } + + get latestVersion(): string { + return this.gallery ? this.gallery.version : this.local.manifest.version; + } + + get description(): string { + return this.gallery ? this.gallery.description : this.local.manifest.description; + } + + get url(): string { + if (!product.extensionsGallery) { + return null; + } + + return `${product.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`; + } + + get iconUrl(): string { + return this.galleryIconUrl || this.localIconUrl || this.defaultIconUrl; + } + + get iconUrlFallback(): string { + return this.galleryIconUrlFallback || this.localIconUrl || this.defaultIconUrl; + } + + private get localIconUrl(): string { + return this.local && this.local.manifest.icon + && URI.file(path.join(this.local.path, this.local.manifest.icon)).toString(); + } + + private get galleryIconUrl(): string { + return this.gallery && this.gallery.assets.icon.uri; + } + + private get galleryIconUrlFallback(): string { + return this.gallery && this.gallery.assets.icon.fallbackUri; + } + + private get defaultIconUrl(): string { + return require.toUrl('../browser/media/defaultIcon.png'); + } + + get licenseUrl(): string { + return this.gallery && this.gallery.assets.license && this.gallery.assets.license.uri; + } + + get state(): ExtensionState { + return this.stateProvider(this); + } + + get installCount(): number { + return this.gallery ? this.gallery.installCount : null; + } + + get rating(): number { + return this.gallery ? this.gallery.rating : null; + } + + get ratingCount(): number { + return this.gallery ? this.gallery.ratingCount : null; + } + + get outdated(): boolean { + return !!this.gallery && this.type === LocalExtensionType.User && semver.gt(this.latestVersion, this.version); + } + + get telemetryData(): any { + const { local, gallery } = this; + + if (gallery) { + return getGalleryExtensionTelemetryData(gallery); + } else { + return getLocalExtensionTelemetryData(local); + } + } + + getManifest(): TPromise { + if (this.gallery) { + return this.galleryService.getManifest(this.gallery); + } + + return TPromise.as(this.local.manifest); + } + + getReadme(): TPromise { + if (this.gallery) { + return this.galleryService.getReadme(this.gallery); + } + + if (this.local && this.local.readmeUrl) { + const uri = URI.parse(this.local.readmeUrl); + return readFile(uri.fsPath, 'utf8'); + } + + return TPromise.wrapError(new Error('not available')); + } + + getChangelog(): TPromise { + if (this.gallery && this.gallery.assets.changelog) { + return this.galleryService.getChangelog(this.gallery); + } + + const changelogUrl = this.local && this.local.changelogUrl; + + if (!changelogUrl) { + return TPromise.wrapError(new Error('not available')); + } + + const uri = URI.parse(changelogUrl); + + if (uri.scheme === 'file') { + return readFile(uri.fsPath, 'utf8'); + } + + return TPromise.wrapError(new Error('not available')); + } + + get dependencies(): string[] { + const { local, gallery } = this; + if (local && local.manifest.extensionDependencies) { + return local.manifest.extensionDependencies; + } + if (gallery) { + return gallery.properties.dependencies; + } + return []; + } +} + +class ExtensionDependencies implements IExtensionDependencies { + + private _hasDependencies: boolean = null; + + constructor(private _extension: IExtension, private _identifier: string, private _map: Map, private _dependent: IExtensionDependencies = null) { } + + get hasDependencies(): boolean { + if (this._hasDependencies === null) { + this._hasDependencies = this.computeHasDependencies(); + } + return this._hasDependencies; + } + + get extension(): IExtension { + return this._extension; + } + + get identifier(): string { + return this._identifier; + } + + get dependent(): IExtensionDependencies { + return this._dependent; + } + + get dependencies(): IExtensionDependencies[] { + if (!this.hasDependencies) { + return []; + } + return this._extension.dependencies.map(d => new ExtensionDependencies(this._map.get(d), d, this._map, this)); + } + + private computeHasDependencies(): boolean { + if (this._extension && this._extension.dependencies.length > 0) { + let dependent = this._dependent; + while (dependent !== null) { + if (dependent.identifier === this.identifier) { + return false; + } + dependent = dependent.dependent; + } + return true; + } + return false; + } +} + +enum Operation { + Installing, + Updating, + Uninstalling +} + +interface IActiveExtension { + operation: Operation; + extension: Extension; + start: Date; +} + +function toTelemetryEventName(operation: Operation) { + switch (operation) { + case Operation.Installing: return 'extensionGallery:install'; + case Operation.Updating: return 'extensionGallery:update'; + case Operation.Uninstalling: return 'extensionGallery:uninstall'; + } + + return ''; +} + +export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { + + private static SyncPeriod = 1000 * 60 * 60 * 12; // 12 hours + + _serviceBrand: any; + private stateProvider: IExtensionStateProvider; + private installing: IActiveExtension[] = []; + private uninstalling: IActiveExtension[] = []; + private installed: Extension[] = []; + private syncDelayer: ThrottledDelayer; + private autoUpdateDelayer: ThrottledDelayer; + private disposables: IDisposable[] = []; + + private _onChange: Emitter = new Emitter(); + get onChange(): Event { return this._onChange.event; } + + private _isAutoUpdateEnabled: boolean; + + private _extensionAllowedBadgeProviders: string[]; + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IExtensionManagementService private extensionService: IExtensionManagementService, + @IExtensionGalleryService private galleryService: IExtensionGalleryService, + @IConfigurationService private configurationService: IConfigurationService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, + @ITelemetryService private telemetryService: ITelemetryService, + @IMessageService private messageService: IMessageService, + @IChoiceService private choiceService: IChoiceService, + @IURLService urlService: IURLService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IExtensionTipsService private tipsService: IExtensionTipsService, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IWindowService private windowService: IWindowService + ) { + this.stateProvider = ext => this.getExtensionState(ext); + + extensionService.onInstallExtension(this.onInstallExtension, this, this.disposables); + extensionService.onDidInstallExtension(this.onDidInstallExtension, this, this.disposables); + extensionService.onUninstallExtension(this.onUninstallExtension, this, this.disposables); + extensionService.onDidUninstallExtension(this.onDidUninstallExtension, this, this.disposables); + extensionEnablementService.onEnablementChanged(this.onEnablementChanged, this, this.disposables); + + this.syncDelayer = new ThrottledDelayer(ExtensionsWorkbenchService.SyncPeriod); + this.autoUpdateDelayer = new ThrottledDelayer(1000); + + chain(urlService.onOpenURL) + .filter(uri => /^extension/.test(uri.path)) + .on(this.onOpenExtensionUrl, this, this.disposables); + + // {{SQL CARBON EDIT}} + // this._isAutoUpdateEnabled = this.configurationService.getConfiguration(ConfigurationKey).autoUpdate; + // this.configurationService.onDidUpdateConfiguration(() => { + // const isAutoUpdateEnabled = this.configurationService.getConfiguration(ConfigurationKey).autoUpdate; + // if (this._isAutoUpdateEnabled !== isAutoUpdateEnabled) { + // this._isAutoUpdateEnabled = isAutoUpdateEnabled; + // if (this._isAutoUpdateEnabled) { + // this.checkForUpdates(); + // } + // } + // }, this, this.disposables); + + this.queryLocal().done(() => this.eventuallySyncWithGallery(true)); + } + + get local(): IExtension[] { + const installing = this.installing + .filter(e => !this.installed.some(installed => installed.id === e.extension.id)) + .map(e => e.extension); + + return [...this.installed, ...installing]; + } + + queryLocal(): TPromise { + return this.extensionService.getInstalled().then(result => { + const installedById = index(this.installed, e => e.local.id); + const globallyDisabledExtensions = this.extensionEnablementService.getGloballyDisabledExtensions(); + const workspaceDisabledExtensions = this.extensionEnablementService.getWorkspaceDisabledExtensions(); + this.installed = result.map(local => { + const extension = installedById[local.id] || new Extension(this.galleryService, this.stateProvider, local); + extension.local = local; + extension.disabledGlobally = globallyDisabledExtensions.indexOf(extension.id) !== -1; + extension.disabledForWorkspace = workspaceDisabledExtensions.indexOf(extension.id) !== -1; + return extension; + }); + + this._onChange.fire(); + return this.local; + }); + } + + queryGallery(options: IQueryOptions = {}): TPromise> { + return this.galleryService.query(options) + .then(result => mapPager(result, gallery => this.fromGallery(gallery))) + .then(null, err => { + if (/No extension gallery service configured/.test(err.message)) { + return TPromise.as(singlePagePager([])); + } + + return TPromise.wrapError>(err); + }); + } + + loadDependencies(extension: IExtension): TPromise { + if (!extension.dependencies.length) { + return TPromise.wrap(null); + } + + return this.galleryService.getAllDependencies((extension).gallery) + .then(galleryExtensions => galleryExtensions.map(galleryExtension => this.fromGallery(galleryExtension))) + .then(extensions => [...this.local, ...extensions]) + .then(extensions => { + const map = new Map(); + for (const extension of extensions) { + map.set(extension.id, extension); + } + return new ExtensionDependencies(extension, extension.id, map); + }); + } + + open(extension: IExtension, sideByside: boolean = false): TPromise { + this.telemetryService.publicLog('extensionGallery:open', extension.telemetryData); + return this.editorService.openEditor(this.instantiationService.createInstance(ExtensionsInput, extension), null, sideByside); + } + + private fromGallery(gallery: IGalleryExtension): Extension { + const installed = this.installed.filter(installed => installed.id === gallery.id)[0]; + + if (installed) { + // Loading the compatible version only there is an engine property + // Otherwise falling back to old way so that we will not make many roundtrips + if (gallery.properties.engine) { + this.galleryService.loadCompatibleVersion(gallery).then(compatible => this.syncLocalWithGalleryExtension(installed, compatible)); + } else { + this.syncLocalWithGalleryExtension(installed, gallery); + } + return installed; + } + + return new Extension(this.galleryService, this.stateProvider, null, gallery); + } + + private syncLocalWithGalleryExtension(local: Extension, gallery: IGalleryExtension) { + local.gallery = gallery; + this._onChange.fire(); + this.eventuallyAutoUpdateExtensions(); + } + + checkForUpdates(): TPromise { + return this.syncDelayer.trigger(() => this.syncWithGallery(), 0); + } + + get isAutoUpdateEnabled(): boolean { + return this._isAutoUpdateEnabled; + } + + setAutoUpdate(autoUpdate: boolean): TPromise { + if (this.isAutoUpdateEnabled === autoUpdate) { + return TPromise.as(null); + } + return this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'extensions.autoUpdate', value: autoUpdate }); + } + + private eventuallySyncWithGallery(immediate = false): void { + const loop = () => this.syncWithGallery().then(() => this.eventuallySyncWithGallery()); + const delay = immediate ? 0 : ExtensionsWorkbenchService.SyncPeriod; + + this.syncDelayer.trigger(loop, delay) + .done(null, err => null); + } + + private syncWithGallery(): TPromise { + const names = this.installed + .filter(e => e.type === LocalExtensionType.User) + .map(e => e.id); + + if (names.length === 0) { + return TPromise.as(null); + } + + return this.queryGallery({ names, pageSize: names.length }) as TPromise; + } + + private eventuallyAutoUpdateExtensions(): void { + this.autoUpdateDelayer.trigger(() => this.autoUpdateExtensions()) + .done(null, err => null); + } + + private autoUpdateExtensions(): TPromise { + if (!this.isAutoUpdateEnabled) { + return TPromise.as(null); + } + + const toUpdate = this.local.filter(e => e.outdated && (e.state !== ExtensionState.Installing)); + return TPromise.join(toUpdate.map(e => this.install(e, false))); + } + + canInstall(extension: IExtension): boolean { + if (!(extension instanceof Extension)) { + return false; + } + + return !!(extension as Extension).gallery; + } + + install(extension: string | IExtension, promptToInstallDependencies: boolean = true): TPromise { + if (typeof extension === 'string') { + return this.extensionService.install(extension); + } + + if (!(extension instanceof Extension)) { + return undefined; + } + + const ext = extension as Extension; + const gallery = ext.gallery; + + if (!gallery) { + return TPromise.wrapError(new Error('Missing gallery')); + } + + return this.extensionService.installFromGallery(gallery, promptToInstallDependencies); + } + + setEnablement(extension: IExtension, enable: boolean, workspace: boolean = false): TPromise { + if (extension.type === LocalExtensionType.System) { + return TPromise.wrap(void 0); + } + + return this.promptAndSetEnablement(extension, enable, workspace).then(reload => { + this.telemetryService.publicLog(enable ? 'extension:enable' : 'extension:disable', extension.telemetryData); + }); + } + + uninstall(extension: IExtension): TPromise { + if (!(extension instanceof Extension)) { + return undefined; + } + + const ext = extension as Extension; + const local = ext.local || this.installed.filter(e => e.id === extension.id)[0].local; + + if (!local) { + return TPromise.wrapError(new Error('Missing local')); + } + + return this.extensionService.uninstall(local); + + } + + private promptAndSetEnablement(extension: IExtension, enable: boolean, workspace: boolean): TPromise { + const allDependencies = this.getDependenciesRecursively(extension, this.local, enable, workspace, []); + if (allDependencies.length > 0) { + if (enable) { + return this.promptForDependenciesAndEnable(extension, allDependencies, workspace); + } else { + return this.promptForDependenciesAndDisable(extension, allDependencies, workspace); + } + } + return this.checkAndSetEnablement(extension, [], enable, workspace); + } + + private promptForDependenciesAndEnable(extension: IExtension, dependencies: IExtension[], workspace: boolean): TPromise { + const message = nls.localize('enableDependeciesConfirmation', "Enabling '{0}' also enable its dependencies. Would you like to continue?", extension.displayName); + const options = [ + nls.localize('enable', "Yes"), + nls.localize('doNotEnable', "No") + ]; + return this.choiceService.choose(Severity.Info, message, options, 1, true) + .then(value => { + if (value === 0) { + return this.checkAndSetEnablement(extension, dependencies, true, workspace); + } + return TPromise.as(null); + }); + } + + private promptForDependenciesAndDisable(extension: IExtension, dependencies: IExtension[], workspace: boolean): TPromise { + const message = nls.localize('disableDependeciesConfirmation', "Would you like to disable '{0}' only or its dependencies also?", extension.displayName); + const options = [ + nls.localize('disableOnly', "Only"), + nls.localize('disableAll', "All"), + nls.localize('cancel', "Cancel") + ]; + return this.choiceService.choose(Severity.Info, message, options, 2, true) + .then(value => { + if (value === 0) { + return this.checkAndSetEnablement(extension, [], false, workspace); + } + if (value === 1) { + return this.checkAndSetEnablement(extension, dependencies, false, workspace); + } + return TPromise.as(null); + }); + } + + private checkAndSetEnablement(extension: IExtension, dependencies: IExtension[], enable: boolean, workspace: boolean): TPromise { + if (!enable) { + let dependents = this.getDependentsAfterDisablement(extension, dependencies, this.local, workspace); + if (dependents.length) { + return TPromise.wrapError(new Error(this.getDependentsErrorMessage(extension, dependents))); + } + } + return TPromise.join([extension, ...dependencies].map(e => this.doSetEnablement(e, enable, workspace))); + } + + private getDependenciesRecursively(extension: IExtension, installed: IExtension[], enable: boolean, workspace: boolean, checked: IExtension[]): IExtension[] { + if (checked.indexOf(extension) !== -1) { + return []; + } + checked.push(extension); + if (!extension.dependencies || extension.dependencies.length === 0) { + return []; + } + const dependenciesToDisable = installed.filter(i => { + // Do not include extensions which are already disabled and request is to disable + if (!enable && (workspace ? i.disabledForWorkspace : i.disabledGlobally)) { + return false; + } + return i.type === LocalExtensionType.User && extension.dependencies.indexOf(i.id) !== -1; + }); + const depsOfDeps = []; + for (const dep of dependenciesToDisable) { + depsOfDeps.push(...this.getDependenciesRecursively(dep, installed, enable, workspace, checked)); + } + return [...dependenciesToDisable, ...depsOfDeps]; + } + + private getDependentsAfterDisablement(extension: IExtension, dependencies: IExtension[], installed: IExtension[], workspace: boolean): IExtension[] { + return installed.filter(i => { + if (i.dependencies.length === 0) { + return false; + } + if (i === extension) { + return false; + } + const disabled = workspace ? i.disabledForWorkspace : i.disabledGlobally; + if (disabled) { + return false; + } + if (dependencies.indexOf(i) !== -1) { + return false; + } + return i.dependencies.some(dep => { + if (extension.id === dep) { + return true; + } + return dependencies.some(d => d.id === dep); + }); + }); + } + + private getDependentsErrorMessage(extension: IExtension, dependents: IExtension[]): string { + if (dependents.length === 1) { + return nls.localize('singleDependentError', "Cannot disable extension '{0}'. Extension '{1}' depends on this.", extension.displayName, dependents[0].displayName); + } + if (dependents.length === 2) { + return nls.localize('twoDependentsError', "Cannot disable extension '{0}'. Extensions '{1}' and '{2}' depend on this.", + extension.displayName, dependents[0].displayName, dependents[1].displayName); + } + return nls.localize('multipleDependentsError', "Cannot disable extension '{0}'. Extensions '{1}', '{2}' and others depend on this.", + extension.displayName, dependents[0].displayName, dependents[1].displayName); + } + + private doSetEnablement(extension: IExtension, enable: boolean, workspace: boolean): TPromise { + if (workspace) { + return this.extensionEnablementService.setEnablement(extension.id, enable, workspace); + } + + const globalElablement = this.extensionEnablementService.setEnablement(extension.id, enable, false); + if (enable && this.workspaceContextService.hasWorkspace()) { + const workspaceEnablement = this.extensionEnablementService.setEnablement(extension.id, enable, true); + return TPromise.join([globalElablement, workspaceEnablement]).then(values => values[0] || values[1]); + } + return globalElablement; + } + + get allowedBadgeProviders(): string[] { + if (!this._extensionAllowedBadgeProviders) { + this._extensionAllowedBadgeProviders = (product.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase()); + } + return this._extensionAllowedBadgeProviders; + } + + private onInstallExtension(event: InstallExtensionEvent): void { + const { gallery } = event; + + if (!gallery) { + return; + } + + let extension = this.installed.filter(e => e.id === gallery.id)[0]; + + if (!extension) { + extension = new Extension(this.galleryService, this.stateProvider, null, gallery); + } + + extension.gallery = gallery; + + const start = new Date(); + const operation = Operation.Installing; + this.installing.push({ operation, extension, start }); + + this._onChange.fire(); + } + + private onDidInstallExtension(event: DidInstallExtensionEvent): void { + const { local, zipPath, error, gallery } = event; + const installing = gallery ? this.installing.filter(e => e.extension.id === gallery.id)[0] : null; + const extension: Extension = installing ? installing.extension : zipPath ? new Extension(this.galleryService, this.stateProvider, null) : null; + if (extension) { + this.installing = installing ? this.installing.filter(e => e !== installing) : this.installing; + + if (!error) { + extension.local = local; + + const installed = this.installed.filter(e => e.id === extension.id)[0]; + if (installed) { + if (installing) { + installing.operation = Operation.Updating; + } + installed.local = local; + } else { + this.installed.push(extension); + } + } + if (extension.gallery) { + // Report telemetry only for gallery extensions + this.reportTelemetry(installing, !error); + } + } + this._onChange.fire(); + } + + private onUninstallExtension(id: string): void { + const extension = this.installed.filter(e => e.local.id === id)[0]; + const newLength = this.installed.filter(e => e.local.id !== id).length; + // TODO: Ask @Joao why is this? + if (newLength === this.installed.length) { + return; + } + + const start = new Date(); + const operation = Operation.Uninstalling; + const uninstalling = this.uninstalling.filter(e => e.extension.local.id === id)[0] || { id, operation, extension, start }; + this.uninstalling = [uninstalling, ...this.uninstalling.filter(e => e.extension.local.id !== id)]; + + this._onChange.fire(); + } + + private onDidUninstallExtension({ id, error }: DidUninstallExtensionEvent): void { + if (!error) { + this.installed = this.installed.filter(e => e.local.id !== id); + } + + const uninstalling = this.uninstalling.filter(e => e.extension.local.id === id)[0]; + this.uninstalling = this.uninstalling.filter(e => e.extension.local.id !== id); + if (!uninstalling) { + return; + } + + if (!error) { + this.reportTelemetry(uninstalling, true); + } + + this._onChange.fire(); + } + + private onEnablementChanged(extensionIdentifier: string) { + const [extension] = this.local.filter(e => e.id === extensionIdentifier); + if (extension) { + const globallyDisabledExtensions = this.extensionEnablementService.getGloballyDisabledExtensions(); + const workspaceDisabledExtensions = this.extensionEnablementService.getWorkspaceDisabledExtensions(); + extension.disabledGlobally = globallyDisabledExtensions.indexOf(extension.id) !== -1; + extension.disabledForWorkspace = workspaceDisabledExtensions.indexOf(extension.id) !== -1; + this._onChange.fire(); + } + } + + private getExtensionState(extension: Extension): ExtensionState { + if (extension.gallery && this.installing.some(e => e.extension.gallery && e.extension.gallery.id === extension.gallery.id)) { + return ExtensionState.Installing; + } + + if (this.uninstalling.some(e => e.extension.id === extension.id)) { + return ExtensionState.Uninstalling; + } + + const local = this.installed.filter(e => e === extension || (e.gallery && extension.gallery && e.gallery.id === extension.gallery.id))[0]; + return local ? ExtensionState.Installed : ExtensionState.Uninstalled; + } + + private reportTelemetry(active: IActiveExtension, success: boolean): void { + const data = active.extension.telemetryData; + const duration = new Date().getTime() - active.start.getTime(); + const eventName = toTelemetryEventName(active.operation); + + this.telemetryService.publicLog(eventName, assign(data, { success, duration })); + } + + private onError(err: any): void { + if (isPromiseCanceledError(err)) { + return; + } + + const message = err && err.message || ''; + + if (/getaddrinfo ENOTFOUND|getaddrinfo ENOENT|connect EACCES|connect ECONNREFUSED/.test(message)) { + return; + } + + this.messageService.show(Severity.Error, err); + } + + private onOpenExtensionUrl(uri: URI): void { + const match = /^extension\/([^/]+)$/.exec(uri.path); + + if (!match) { + return; + } + + const extensionId = match[1]; + + this.queryLocal().then(local => { + if (local.some(local => local.id === extensionId)) { + return TPromise.as(null); + } + + return this.queryGallery({ names: [extensionId] }).then(result => { + if (result.total < 1) { + return TPromise.as(null); + } + + const extension = result.firstPage[0]; + + return this.windowService.show().then(() => { + return this.open(extension).then(() => { + const message = nls.localize('installConfirmation', "Would you like to install the '{0}' extension?", extension.displayName, extension.publisher); + const options = [ + nls.localize('install', "Install"), + nls.localize('cancel', "Cancel") + ]; + return this.choiceService.choose(Severity.Info, message, options, 2, false).then(value => { + if (value !== 0) { + return TPromise.as(null); + } + + return this.install(extension); + }); + }); + }); + }); + }).done(undefined, error => this.onError(error)); + } + + dispose(): void { + this.syncDelayer.cancel(); + this.disposables = dispose(this.disposables); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/test/common/extensionQuery.test.ts b/src/vs/workbench/parts/extensions/test/common/extensionQuery.test.ts new file mode 100644 index 0000000000..5825c8569e --- /dev/null +++ b/src/vs/workbench/parts/extensions/test/common/extensionQuery.test.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery'; + +suite('Extension query', () => { + test('parse', () => { + let query = Query.parse(''); + assert.equal(query.value, ''); + assert.equal(query.sortBy, ''); + + query = Query.parse('hello'); + assert.equal(query.value, 'hello'); + assert.equal(query.sortBy, ''); + + query = Query.parse(' hello world '); + assert.equal(query.value, 'hello world'); + assert.equal(query.sortBy, ''); + + query = Query.parse('@sort'); + assert.equal(query.value, '@sort'); + assert.equal(query.sortBy, ''); + + query = Query.parse('@sort:'); + assert.equal(query.value, '@sort:'); + assert.equal(query.sortBy, ''); + + query = Query.parse(' @sort: '); + assert.equal(query.value, '@sort:'); + assert.equal(query.sortBy, ''); + + query = Query.parse('@sort:installs'); + assert.equal(query.value, ''); + assert.equal(query.sortBy, 'installs'); + + query = Query.parse(' @sort:installs '); + assert.equal(query.value, ''); + assert.equal(query.sortBy, 'installs'); + + query = Query.parse('@sort:installs-'); + assert.equal(query.value, ''); + assert.equal(query.sortBy, 'installs'); + + query = Query.parse('@sort:installs-foo'); + assert.equal(query.value, ''); + assert.equal(query.sortBy, 'installs'); + + query = Query.parse('@sort:installs'); + assert.equal(query.value, ''); + assert.equal(query.sortBy, 'installs'); + + query = Query.parse('@sort:installs'); + assert.equal(query.value, ''); + assert.equal(query.sortBy, 'installs'); + + query = Query.parse('vs @sort:installs'); + assert.equal(query.value, 'vs'); + assert.equal(query.sortBy, 'installs'); + + query = Query.parse('vs @sort:installs code'); + assert.equal(query.value, 'vs code'); + assert.equal(query.sortBy, 'installs'); + + query = Query.parse('@sort:installs @sort:ratings'); + assert.equal(query.value, ''); + assert.equal(query.sortBy, 'ratings'); + }); + + test('toString', () => { + let query = new Query('hello', ''); + assert.equal(query.toString(), 'hello'); + + query = new Query('hello world', ''); + assert.equal(query.toString(), 'hello world'); + + query = new Query(' hello ', ''); + assert.equal(query.toString(), 'hello'); + + query = new Query('', 'installs'); + assert.equal(query.toString(), '@sort:installs'); + + query = new Query('', 'installs'); + assert.equal(query.toString(), '@sort:installs'); + + query = new Query('', 'installs'); + assert.equal(query.toString(), '@sort:installs'); + + query = new Query('hello', 'installs'); + assert.equal(query.toString(), 'hello @sort:installs'); + + query = new Query(' hello ', 'installs'); + assert.equal(query.toString(), 'hello @sort:installs'); + }); + + test('isValid', () => { + let query = new Query('hello', ''); + assert(query.isValid()); + + query = new Query('hello world', ''); + assert(query.isValid()); + + query = new Query(' hello ', ''); + assert(query.isValid()); + + query = new Query('', 'installs'); + assert(query.isValid()); + + query = new Query('', 'installs'); + assert(query.isValid()); + + query = new Query('', 'installs'); + assert(query.isValid()); + + query = new Query('', 'installs'); + assert(query.isValid()); + + query = new Query('hello', 'installs'); + assert(query.isValid()); + + query = new Query(' hello ', 'installs'); + assert(query.isValid()); + }); + + test('equals', () => { + let query1 = new Query('hello', ''); + let query2 = new Query('hello', ''); + assert(query1.equals(query2)); + + query2 = new Query('hello world', ''); + assert(!query1.equals(query2)); + + query2 = new Query('hello', 'installs'); + assert(!query1.equals(query2)); + + query2 = new Query('hello', 'installs'); + assert(!query1.equals(query2)); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts new file mode 100644 index 0000000000..1e506ea8ed --- /dev/null +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -0,0 +1,1185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { assign } from 'vs/base/common/objects'; +import { generateUuid } from 'vs/base/common/uuid'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; +import * as ExtensionsActions from 'vs/workbench/parts/extensions/browser/extensionsActions'; +import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; +import { + IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, + DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent +} from 'vs/platform/extensionManagement/common/extensionManagement'; +import { getLocalExtensionIdFromManifest, getGalleryExtensionId, getLocalExtensionIdFromGallery } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; +import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { IURLService } from 'vs/platform/url/common/url'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { Emitter } from 'vs/base/common/event'; +import { IPager } from 'vs/base/common/paging'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +suite('ExtensionsActions Test', () => { + + let instantiationService: TestInstantiationService; + + let installEvent: Emitter, + didInstallEvent: Emitter, + uninstallEvent: Emitter, + didUninstallEvent: Emitter; + + + suiteSetup(() => { + installEvent = new Emitter(); + didInstallEvent = new Emitter(); + uninstallEvent = new Emitter(); + didUninstallEvent = new Emitter(); + + instantiationService = new TestInstantiationService(); + instantiationService.stub(IURLService, { onOpenURL: new Emitter().event }); + instantiationService.stub(ITelemetryService, NullTelemetryService); + + instantiationService.stub(IWorkspaceContextService, new TestContextService()); + instantiationService.stub(IConfigurationService, { onDidUpdateConfiguration: () => { }, getConfiguration: () => ({}) }); + + instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); + + instantiationService.stub(IExtensionManagementService, ExtensionManagementService); + instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event); + instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event); + instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event); + instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event); + + instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + + instantiationService.stub(IExtensionTipsService, ExtensionTipsService); + instantiationService.stub(IExtensionTipsService, 'getKeymapRecommendations', () => []); + }); + + setup(() => { + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); + instantiationService.stub(IExtensionService, { getExtensions: () => TPromise.wrap([]) }); + (instantiationService.get(IExtensionEnablementService)).reset(); + + instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); + }); + + teardown(() => { + (instantiationService.get(IExtensionsWorkbenchService)).dispose(); + }); + + test('Install action is disabled when there is no extension', () => { + const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction); + + assert.ok(!testObject.enabled); + }); + + test('Test Install action when state is installed', (done) => { + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + workbenchService.queryLocal().done(() => { + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { id: local.id }))); + workbenchService.queryGallery().done((paged) => { + testObject.extension = paged.firstPage[0]; + assert.ok(!testObject.enabled); + assert.equal('Install', testObject.label); + assert.equal('extension-action prominent install', testObject.class); + done(); + }); + }); + }); + + test('Test Install action when state is installing', (done) => { + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + workbenchService.queryGallery().done((paged) => { + testObject.extension = paged.firstPage[0]; + installEvent.fire({ id: gallery.uuid, gallery }); + + assert.ok(!testObject.enabled); + assert.equal('Installing', testObject.label); + assert.equal('extension-action install installing', testObject.class); + done(); + }); + }); + + test('Test Install action when state is uninstalled', (done) => { + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + workbenchService.queryGallery().done((paged) => { + testObject.extension = paged.firstPage[0]; + assert.ok(testObject.enabled); + assert.equal('Install', testObject.label); + done(); + }); + }); + + test('Test Install action when extension is system action', (done) => { + const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction); + const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + uninstallEvent.fire(local.id); + didUninstallEvent.fire({ id: local.id }); + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test Install action when extension doesnot has gallery', (done) => { + const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + uninstallEvent.fire(local.id); + didUninstallEvent.fire({ id: local.id }); + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Uninstall action is disabled when there is no extension', () => { + const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction); + + assert.ok(!testObject.enabled); + }); + + test('Test Uninstall action when state is uninstalling', (done) => { + const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + uninstallEvent.fire(local.id); + assert.ok(!testObject.enabled); + assert.equal('Uninstalling', testObject.label); + assert.equal('extension-action uninstall uninstalling', testObject.class); + done(); + }); + }); + + test('Test Uninstall action when state is installed and is user extension', (done) => { + const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + assert.equal('Uninstall', testObject.label); + assert.equal('extension-action uninstall', testObject.class); + done(); + }); + }); + + test('Test Uninstall action when state is installed and is system extension', (done) => { + const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction); + const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + assert.equal('Uninstall', testObject.label); + assert.equal('extension-action uninstall', testObject.class); + done(); + }); + }); + + test('Test Uninstall action after extension is installed', (done) => { + const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(paged => { + testObject.extension = paged.firstPage[0]; + + installEvent.fire({ id: gallery.uuid, gallery }); + didInstallEvent.fire({ id: gallery.uuid, gallery, local: aLocalExtension('a', gallery, gallery) }); + + assert.ok(testObject.enabled); + assert.equal('Uninstall', testObject.label); + assert.equal('extension-action uninstall', testObject.class); + done(); + }); + }); + + test('Test CombinedInstallAction when there is no extension', () => { + const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction); + + assert.ok(!testObject.enabled); + assert.equal('extension-action prominent install no-extension', testObject.class); + }); + + test('Test CombinedInstallAction when extension is system extension', (done) => { + const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction); + const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + assert.equal('extension-action prominent install no-extension', testObject.class); + done(); + }); + }); + + test('Test CombinedInstallAction when installAction is enabled', (done) => { + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + workbenchService.queryGallery().done((paged) => { + testObject.extension = paged.firstPage[0]; + assert.ok(testObject.enabled); + assert.equal('Install', testObject.label); + assert.equal('extension-action prominent install', testObject.class); + done(); + }); + }); + + test('Test CombinedInstallAction when unInstallAction is enabled', (done) => { + const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + assert.equal('Uninstall', testObject.label); + assert.equal('extension-action uninstall', testObject.class); + done(); + }); + }); + + test('Test CombinedInstallAction when state is installing', (done) => { + const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction); + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + workbenchService.queryGallery().done((paged) => { + testObject.extension = paged.firstPage[0]; + installEvent.fire({ id: gallery.uuid, gallery }); + + assert.ok(!testObject.enabled); + assert.equal('Installing', testObject.label); + assert.equal('extension-action install installing', testObject.class); + done(); + }); + }); + + test('Test CombinedInstallAction when state is uninstalling', (done) => { + const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + uninstallEvent.fire(local.id); + assert.ok(!testObject.enabled); + assert.equal('Uninstalling', testObject.label); + assert.equal('extension-action uninstall uninstalling', testObject.class); + done(); + }); + }); + + test('Test UpdateAction when there is no extension', () => { + const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction); + + assert.ok(!testObject.enabled); + }); + + test('Test UpdateAction when extension is uninstalled', (done) => { + const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction); + const gallery = aGalleryExtension('a', { version: '1.0.0' }); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done((paged) => { + testObject.extension = paged.firstPage[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test UpdateAction when extension is installed and not outdated', (done) => { + const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction); + const local = aLocalExtension('a', { version: '1.0.0' }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { id: local.id, version: local.manifest.version }))); + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(extensions => { + assert.ok(!testObject.enabled); + done(); + }); + }); + }); + + test('Test UpdateAction when extension is installed outdated and system extension', (done) => { + const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction); + const local = aLocalExtension('a', { version: '1.0.0' }, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { id: local.id, version: '1.0.1' }))); + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(extensions => { + assert.ok(!testObject.enabled); + done(); + }); + }); + }); + + test('Test UpdateAction when extension is installed outdated and user extension', (done) => { + const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction); + const local = aLocalExtension('a', { version: '1.0.0' }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { id: local.id, version: '1.0.1' }))); + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(extensions => { + assert.ok(testObject.enabled); + done(); + }); + }); + }); + + test('Test UpdateAction when extension is installing and outdated and user extension', (done) => { + const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction); + const local = aLocalExtension('a', { version: '1.0.0' }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + const gallery = aGalleryExtension('a', { id: local.id, version: '1.0.1' }); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(extensions => { + installEvent.fire({ id: local.id, gallery }); + assert.ok(!testObject.enabled); + done(); + }); + }); + }); + + test('Test ManageExtensionAction when there is no extension', () => { + const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction); + + assert.ok(!testObject.enabled); + }); + + test('Test ManageExtensionAction when extension is installed', (done) => { + const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + assert.equal('extension-action manage', testObject.class); + assert.equal('', testObject.tooltip); + + done(); + }); + }); + + test('Test ManageExtensionAction when extension is uninstalled', (done) => { + const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(page => { + testObject.extension = page.firstPage[0]; + assert.ok(!testObject.enabled); + assert.equal('extension-action manage hide', testObject.class); + assert.equal('', testObject.tooltip); + + done(); + }); + }); + + test('Test ManageExtensionAction when extension is installing', (done) => { + const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(page => { + testObject.extension = page.firstPage[0]; + + installEvent.fire({ id: gallery.uuid, gallery }); + assert.ok(!testObject.enabled); + assert.equal('extension-action manage hide', testObject.class); + assert.equal('', testObject.tooltip); + + done(); + }); + }); + + test('Test ManageExtensionAction when extension is queried from gallery and installed', (done) => { + const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(page => { + testObject.extension = page.firstPage[0]; + installEvent.fire({ id: gallery.uuid, gallery }); + didInstallEvent.fire({ id: gallery.uuid, gallery, local: aLocalExtension('a', gallery, gallery) }); + + assert.ok(testObject.enabled); + assert.equal('extension-action manage', testObject.class); + assert.equal('', testObject.tooltip); + + done(); + }); + }); + + test('Test ManageExtensionAction when extension is system extension', (done) => { + const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction); + const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + assert.equal('extension-action manage hide', testObject.class); + assert.equal('', testObject.tooltip); + + done(); + }); + }); + + test('Test ManageExtensionAction when extension is uninstalling', (done) => { + const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + uninstallEvent.fire(local.id); + + assert.ok(!testObject.enabled); + assert.equal('extension-action manage', testObject.class); + assert.equal('Uninstalling', testObject.tooltip); + + done(); + }); + }); + + test('Test EnableForWorkspaceAction when there is no extension', () => { + const testObject: ExtensionsActions.EnableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.EnableForWorkspaceAction, 'id'); + + assert.ok(!testObject.enabled); + }); + + test('Test EnableForWorkspaceAction when there extension is not disabled', (done) => { + const testObject: ExtensionsActions.EnableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.EnableForWorkspaceAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test EnableForWorkspaceAction when there extension is disabled globally', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.EnableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.EnableForWorkspaceAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test EnableForWorkspaceAction when extension is disabled for workspace', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false, true); + const testObject: ExtensionsActions.EnableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.EnableForWorkspaceAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + done(); + }); + }); + + test('Test EnableForWorkspaceAction when the extension is disabled in both', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false, true); + const testObject: ExtensionsActions.EnableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.EnableForWorkspaceAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test EnableGloballyAction when there is no extension', () => { + const testObject: ExtensionsActions.EnableGloballyAction = instantiationService.createInstance(ExtensionsActions.EnableGloballyAction, 'id'); + + assert.ok(!testObject.enabled); + }); + + test('Test EnableGloballyAction when the extension is not disabled', (done) => { + const testObject: ExtensionsActions.EnableGloballyAction = instantiationService.createInstance(ExtensionsActions.EnableGloballyAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test EnableGloballyAction when the extension is disabled for workspace', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false, true); + const testObject: ExtensionsActions.EnableGloballyAction = instantiationService.createInstance(ExtensionsActions.EnableGloballyAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test EnableGloballyAction when the extension is disabled globally', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.EnableGloballyAction = instantiationService.createInstance(ExtensionsActions.EnableGloballyAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + done(); + }); + }); + + test('Test EnableGloballyAction when the extension is disabled in both', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false, true); + const testObject: ExtensionsActions.EnableGloballyAction = instantiationService.createInstance(ExtensionsActions.EnableGloballyAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + done(); + }); + }); + + test('Test EnableAction when there is no extension', () => { + const testObject: ExtensionsActions.EnableAction = instantiationService.createInstance(ExtensionsActions.EnableAction); + + assert.ok(!testObject.enabled); + }); + + test('Test EnableAction when extension is installed and enabled', (done) => { + const testObject: ExtensionsActions.EnableAction = instantiationService.createInstance(ExtensionsActions.EnableAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test EnableAction when extension is installed and disabled globally', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.EnableAction = instantiationService.createInstance(ExtensionsActions.EnableAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + done(); + }); + }); + + test('Test EnableAction when extension is installed and disabled for workspace', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false, true); + const testObject: ExtensionsActions.EnableAction = instantiationService.createInstance(ExtensionsActions.EnableAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + done(); + }); + }); + + test('Test EnableAction when extension is uninstalled', (done) => { + const testObject: ExtensionsActions.EnableAction = instantiationService.createInstance(ExtensionsActions.EnableAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(page => { + testObject.extension = page.firstPage[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test EnableAction when extension is installing', (done) => { + const testObject: ExtensionsActions.EnableAction = instantiationService.createInstance(ExtensionsActions.EnableAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(page => { + testObject.extension = page.firstPage[0]; + + installEvent.fire({ id: gallery.uuid, gallery }); + assert.ok(!testObject.enabled); + + done(); + }); + }); + + test('Test EnableAction when extension is uninstalling', (done) => { + const testObject: ExtensionsActions.EnableAction = instantiationService.createInstance(ExtensionsActions.EnableAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + uninstallEvent.fire(local.id); + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test DisableForWorkspaceAction when there is no extension', () => { + const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, 'id'); + + assert.ok(!testObject.enabled); + }); + + test('Test DisableForWorkspaceAction when the extension is disabled globally', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test DisableForWorkspaceAction when the extension is disabled workspace', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test DisableForWorkspaceAction when extension is enabled', (done) => { + const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + done(); + }); + }); + + test('Test DisableGloballyAction when there is no extension', () => { + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, 'id'); + + assert.ok(!testObject.enabled); + }); + + test('Test DisableGloballyAction when the extension is disabled globally', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test DisableGloballyAction when the extension is disabled for workspace', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false, true); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test DisableGloballyAction when the extension is enabled', (done) => { + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, 'id'); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + done(); + }); + }); + + test('Test DisableAction when there is no extension', () => { + const testObject: ExtensionsActions.DisableAction = instantiationService.createInstance(ExtensionsActions.DisableAction); + + assert.ok(!testObject.enabled); + }); + + test('Test DisableAction when extension is installed and enabled', (done) => { + const testObject: ExtensionsActions.DisableAction = instantiationService.createInstance(ExtensionsActions.DisableAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + done(); + }); + }); + + test('Test DisableAction when extension is installed and disabled globally', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.DisableAction = instantiationService.createInstance(ExtensionsActions.DisableAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test DisableAction when extension is installed and disabled for workspace', (done) => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false, true); + const testObject: ExtensionsActions.DisableAction = instantiationService.createInstance(ExtensionsActions.DisableAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test DisableAction when extension is uninstalled', (done) => { + const testObject: ExtensionsActions.DisableAction = instantiationService.createInstance(ExtensionsActions.DisableAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(page => { + testObject.extension = page.firstPage[0]; + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test DisableAction when extension is installing', (done) => { + const testObject: ExtensionsActions.DisableAction = instantiationService.createInstance(ExtensionsActions.DisableAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done(page => { + testObject.extension = page.firstPage[0]; + + installEvent.fire({ id: gallery.uuid, gallery }); + assert.ok(!testObject.enabled); + + done(); + }); + }); + + test('Test DisableAction when extension is uninstalling', (done) => { + const testObject: ExtensionsActions.DisableAction = instantiationService.createInstance(ExtensionsActions.DisableAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + uninstallEvent.fire(local.id); + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test UpdateAllAction when no installed extensions', () => { + const testObject: ExtensionsActions.UpdateAllAction = instantiationService.createInstance(ExtensionsActions.UpdateAllAction, 'id', 'label'); + + assert.ok(!testObject.enabled); + }); + + test('Test UpdateAllAction when installed extensions are not outdated', (done) => { + const testObject: ExtensionsActions.UpdateAllAction = instantiationService.createInstance(ExtensionsActions.UpdateAllAction, 'id', 'label'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a'), aLocalExtension('b')]); + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test UpdateAllAction when some installed extensions are outdated', (done) => { + const testObject: ExtensionsActions.UpdateAllAction = instantiationService.createInstance(ExtensionsActions.UpdateAllAction, 'id', 'label'); + const local = [aLocalExtension('a', { version: '1.0.1' }), aLocalExtension('b', { version: '1.0.1' }), aLocalExtension('c', { version: '1.0.1' })]; + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', local); + workbenchService.queryLocal().done(() => { + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { id: local[0].id, version: '1.0.2' }), aGalleryExtension('b', { id: local[1].id, version: '1.0.2' }), aGalleryExtension('c', local[2].manifest))); + workbenchService.queryGallery().done(() => { + assert.ok(testObject.enabled); + done(); + }); + }); + }); + + test('Test UpdateAllAction when some installed extensions are outdated and some outdated are being installed', (done) => { + const testObject: ExtensionsActions.UpdateAllAction = instantiationService.createInstance(ExtensionsActions.UpdateAllAction, 'id', 'label'); + const local = [aLocalExtension('a', { version: '1.0.1' }), aLocalExtension('b', { version: '1.0.1' }), aLocalExtension('c', { version: '1.0.1' })]; + const gallery = [aGalleryExtension('a', { id: local[0].id, version: '1.0.2' }), aGalleryExtension('b', { id: local[1].id, version: '1.0.2' }), aGalleryExtension('c', local[2].manifest)]; + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', local); + workbenchService.queryLocal().done(() => { + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...gallery)); + workbenchService.queryGallery().done(() => { + installEvent.fire({ id: local[0].id, gallery: gallery[0] }); + assert.ok(testObject.enabled); + done(); + }); + }); + }); + + test('Test UpdateAllAction when some installed extensions are outdated and all outdated are being installed', (done) => { + const testObject: ExtensionsActions.UpdateAllAction = instantiationService.createInstance(ExtensionsActions.UpdateAllAction, 'id', 'label'); + const local = [aLocalExtension('a', { version: '1.0.1' }), aLocalExtension('b', { version: '1.0.1' }), aLocalExtension('c', { version: '1.0.1' })]; + const gallery = [aGalleryExtension('a', { id: local[0].id, version: '1.0.2' }), aGalleryExtension('b', { id: local[1].id, version: '1.0.2' }), aGalleryExtension('c', local[2].manifest)]; + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', local); + workbenchService.queryLocal().done(() => { + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...gallery)); + workbenchService.queryGallery().done(() => { + installEvent.fire({ id: local[0].id, gallery: gallery[0] }); + installEvent.fire({ id: local[1].id, gallery: gallery[1] }); + assert.ok(!testObject.enabled); + done(); + }); + }); + }); + + test('Test ReloadAction when there is no extension', () => { + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + + assert.ok(!testObject.enabled); + }); + + test('Test ReloadAction when extension state is installing', (done) => { + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + workbenchService.queryGallery().done((paged) => { + testObject.extension = paged.firstPage[0]; + installEvent.fire({ id: gallery.uuid, gallery }); + + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test ReloadAction when extension state is uninstalling', (done) => { + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + uninstallEvent.fire(local.id); + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test ReloadAction when extension is newly installed', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b' }]); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done((paged) => { + testObject.extension = paged.firstPage[0]; + installEvent.fire({ id: gallery.uuid, gallery }); + didInstallEvent.fire({ id: gallery.uuid, gallery, local: aLocalExtension('a', gallery, gallery) }); + + assert.ok(testObject.enabled); + assert.equal('Reload to activate', testObject.tooltip); + assert.equal(`Reload this window to activate the extension 'a'?`, testObject.reloadMessaage); + done(); + }); + }); + + test('Test ReloadAction when extension is installed and uninstalled', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b' }]); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const gallery = aGalleryExtension('a'); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + instantiationService.get(IExtensionsWorkbenchService).queryGallery().done((paged) => { + testObject.extension = paged.firstPage[0]; + const id = getLocalExtensionIdFromGallery(gallery, gallery.version); + installEvent.fire({ id, gallery }); + didInstallEvent.fire({ id, gallery, local: aLocalExtension('a', gallery, { id }) }); + uninstallEvent.fire(id); + didUninstallEvent.fire({ id }); + + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test ReloadAction when extension is uninstalled', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a' }]); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + uninstallEvent.fire(local.id); + didUninstallEvent.fire({ id: local.id }); + + assert.ok(testObject.enabled); + assert.equal('Reload to deactivate', testObject.tooltip); + assert.equal(`Reload this window to deactivate the uninstalled extension 'a'?`, testObject.reloadMessaage); + done(); + }); + }); + + test('Test ReloadAction when extension is uninstalled and installed', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.0' }]); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const local = aLocalExtension('a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => { + testObject.extension = extensions[0]; + uninstallEvent.fire(local.id); + didUninstallEvent.fire({ id: local.id }); + + const gallery = aGalleryExtension('a'); + const id = getLocalExtensionIdFromGallery(gallery, gallery.version); + installEvent.fire({ id, gallery }); + didInstallEvent.fire({ id, gallery, local }); + + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test ReloadAction when extension is updated while running', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.1' }]); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const local = aLocalExtension('a', { version: '1.0.1' }); + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + workbenchService.queryLocal().done(extensions => { + testObject.extension = extensions[0]; + + const gallery = aGalleryExtension('a', { uuid: local.id, version: '1.0.2' }); + installEvent.fire({ id: gallery.uuid, gallery }); + didInstallEvent.fire({ id: gallery.uuid, gallery, local: aLocalExtension('a', gallery, gallery) }); + + assert.ok(testObject.enabled); + assert.equal('Reload to update', testObject.tooltip); + assert.equal(`Reload this window to activate the updated extension 'a'?`, testObject.reloadMessaage); + done(); + + }); + }); + + test('Test ReloadAction when extension is updated when not running', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b' }]); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const local = aLocalExtension('a', { version: '1.0.1' }); + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + workbenchService.queryLocal().done(extensions => { + testObject.extension = extensions[0]; + + const gallery = aGalleryExtension('a', { id: local.id, version: '1.0.2' }); + installEvent.fire({ id: gallery.uuid, gallery }); + didInstallEvent.fire({ id: gallery.uuid, gallery, local: aLocalExtension('a', gallery, gallery) }); + + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test ReloadAction when extension is disabled when running', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a' }]); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const local = aLocalExtension('a'); + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + workbenchService.queryLocal().done(extensions => { + testObject.extension = extensions[0]; + workbenchService.setEnablement(extensions[0], false); + + assert.ok(testObject.enabled); + assert.equal('Reload to deactivate', testObject.tooltip); + assert.equal(`Reload this window to deactivate the extension 'a'?`, testObject.reloadMessaage); + done(); + }); + }); + + test('Test ReloadAction when extension enablement is toggled when running', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.0' }]); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const local = aLocalExtension('a'); + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + workbenchService.queryLocal().done(extensions => { + testObject.extension = extensions[0]; + workbenchService.setEnablement(extensions[0], false); + workbenchService.setEnablement(extensions[0], true); + + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test ReloadAction when extension is enabled when not running', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b' }]); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const local = aLocalExtension('a'); + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + workbenchService.queryLocal().done(extensions => { + testObject.extension = extensions[0]; + workbenchService.setEnablement(extensions[0], true); + + assert.ok(testObject.enabled); + assert.equal('Reload to activate', testObject.tooltip); + assert.equal(`Reload this window to activate the extension 'a'?`, testObject.reloadMessaage); + done(); + }); + }); + + test('Test ReloadAction when extension enablement is toggled when not running', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b' }]); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const local = aLocalExtension('a'); + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + workbenchService.queryLocal().done(extensions => { + testObject.extension = extensions[0]; + workbenchService.setEnablement(extensions[0], true); + workbenchService.setEnablement(extensions[0], false); + + assert.ok(!testObject.enabled); + done(); + }); + }); + + test('Test ReloadAction when extension is updated when not running and enabled', (done) => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b' }]); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); + const local = aLocalExtension('a', { version: '1.0.1' }); + const workbenchService = instantiationService.get(IExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + workbenchService.queryLocal().done(extensions => { + testObject.extension = extensions[0]; + + const gallery = aGalleryExtension('a', { id: local.id, version: '1.0.2' }); + installEvent.fire({ id: gallery.uuid, gallery }); + didInstallEvent.fire({ id: gallery.uuid, gallery, local: aLocalExtension('a', gallery, gallery) }); + workbenchService.setEnablement(extensions[0], true); + + assert.ok(testObject.enabled); + assert.equal('Reload to activate', testObject.tooltip); + assert.equal(`Reload this window to activate the extension 'a'?`, testObject.reloadMessaage); + done(); + }); + }); + + function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension { + const localExtension = Object.create({ manifest: {} }); + assign(localExtension, { type: LocalExtensionType.User, manifest: {} }, properties); + assign(localExtension.manifest, { name, publisher: 'pub', version: '1.0.0' }, manifest); + localExtension.metadata = { id: localExtension.id, publisherId: localExtension.manifest.publisher, publisherDisplayName: 'somename' }; + localExtension.id = getLocalExtensionIdFromManifest(localExtension.manifest); + return localExtension; + } + + function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: any = {}): IGalleryExtension { + const galleryExtension = Object.create({}); + assign(galleryExtension, { name, publisher: 'pub', uuid: generateUuid(), version: '1.0.0', properties: {}, assets: {} }, properties); + assign(galleryExtension.properties, { dependencies: [] }, galleryExtensionProperties); + assign(galleryExtension.assets, assets); + galleryExtension.id = getGalleryExtensionId(galleryExtension.publisher, galleryExtension.name); + return galleryExtension; + } + + function aPage(...objects: T[]): IPager { + return { firstPage: objects, total: objects.length, pageSize: objects.length, getPage: () => null }; + } + +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts new file mode 100644 index 0000000000..7d7fe4e3b9 --- /dev/null +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -0,0 +1,1025 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as sinon from 'sinon'; +import * as assert from 'assert'; +import * as fs from 'fs'; +import { assign } from 'vs/base/common/objects'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IExtensionsWorkbenchService, ExtensionState } from 'vs/workbench/parts/extensions/common/extensions'; +import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; +import { + IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, + DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets +} from 'vs/platform/extensionManagement/common/extensionManagement'; +import { getLocalExtensionIdFromManifest, getGalleryExtensionId, getLocalExtensionIdFromGallery } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; +import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { IURLService } from 'vs/platform/url/common/url'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IPager } from 'vs/base/common/paging'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { IChoiceService } from 'vs/platform/message/common/message'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +suite('ExtensionsWorkbenchService Test', () => { + + let instantiationService: TestInstantiationService; + let testObject: IExtensionsWorkbenchService; + + let installEvent: Emitter, + didInstallEvent: Emitter, + uninstallEvent: Emitter, + didUninstallEvent: Emitter; + + suiteSetup(() => { + installEvent = new Emitter(); + didInstallEvent = new Emitter(); + uninstallEvent = new Emitter(); + didUninstallEvent = new Emitter(); + + instantiationService = new TestInstantiationService(); + instantiationService.stub(IURLService, { onOpenURL: new Emitter().event }); + instantiationService.stub(ITelemetryService, NullTelemetryService); + + instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); + + instantiationService.stub(IWorkspaceContextService, new TestContextService()); + instantiationService.stub(IConfigurationService, { onDidUpdateConfiguration: () => { }, getConfiguration: () => ({}) }); + + instantiationService.stub(IExtensionManagementService, ExtensionManagementService); + instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event); + instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event); + instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event); + instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event); + + instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + + instantiationService.stub(IExtensionTipsService, ExtensionTipsService); + instantiationService.stub(IExtensionTipsService, 'getKeymapRecommendations', () => []); + + instantiationService.stub(IChoiceService, { choose: () => null }); + }); + + setup(() => { + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); + instantiationService.stubPromise(IChoiceService, 'choose', 0); + (instantiationService.get(IExtensionEnablementService)).reset(); + }); + + teardown(() => { + (testObject).dispose(); + }); + + test('test gallery extension', () => { + const expected = aGalleryExtension('expectedName', { + displayName: 'expectedDisplayName', + version: '1.5', + publisherId: 'expectedPublisherId', + publisher: 'expectedPublisher', + publisherDisplayName: 'expectedPublisherDisplayName', + description: 'expectedDescription', + installCount: 1000, + rating: 4, + ratingCount: 100 + }, { + dependencies: ['pub.1', 'pub.2'], + }, { + manifest: { uri: 'uri:manifest', fallbackUri: 'fallback:manifest' }, + readme: { uri: 'uri:readme', fallbackUri: 'fallback:readme' }, + changelog: { uri: 'uri:changelog', fallbackUri: 'fallback:changlog' }, + download: { uri: 'uri:download', fallbackUri: 'fallback:download' }, + icon: { uri: 'uri:icon', fallbackUri: 'fallback:icon' }, + license: { uri: 'uri:license', fallbackUri: 'fallback:license' } + }); + + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(expected)); + + return testObject.queryGallery().then(pagedResponse => { + assert.equal(1, pagedResponse.firstPage.length); + const actual = pagedResponse.firstPage[0]; + + assert.equal(null, actual.type); + assert.equal('expectedName', actual.name); + assert.equal('expectedDisplayName', actual.displayName); + assert.equal('expectedPublisher.expectedname', actual.id); + assert.equal('expectedPublisher', actual.publisher); + assert.equal('expectedPublisherDisplayName', actual.publisherDisplayName); + assert.equal('1.5', actual.version); + assert.equal('1.5', actual.latestVersion); + assert.equal('expectedDescription', actual.description); + assert.equal('uri:icon', actual.iconUrl); + assert.equal('fallback:icon', actual.iconUrlFallback); + assert.equal('uri:license', actual.licenseUrl); + assert.equal(ExtensionState.Uninstalled, actual.state); + assert.equal(1000, actual.installCount); + assert.equal(4, actual.rating); + assert.equal(100, actual.ratingCount); + assert.equal(false, actual.outdated); + assert.deepEqual(['pub.1', 'pub.2'], actual.dependencies); + }); + }); + + test('test for empty installed extensions', () => { + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + assert.deepEqual([], testObject.local); + }); + + test('test for installed extensions', () => { + const expected1 = aLocalExtension('local1', { + publisher: 'localPublisher1', + version: '1.1.0', + displayName: 'localDisplayName1', + description: 'localDescription1', + icon: 'localIcon1', + extensionDependencies: ['pub.1', 'pub.2'], + }, { + type: LocalExtensionType.User, + readmeUrl: 'localReadmeUrl1', + changelogUrl: 'localChangelogUrl1', + path: 'localPath1' + }); + const expected2 = aLocalExtension('local2', { + publisher: 'localPublisher2', + version: '1.2.0', + displayName: 'localDisplayName2', + description: 'localDescription2', + }, { + type: LocalExtensionType.System, + readmeUrl: 'localReadmeUrl2', + changelogUrl: 'localChangelogUrl2', + }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [expected1, expected2]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + const actuals = testObject.local; + assert.equal(2, actuals.length); + + let actual = actuals[0]; + assert.equal(LocalExtensionType.User, actual.type); + assert.equal('local1', actual.name); + assert.equal('localDisplayName1', actual.displayName); + assert.equal('localPublisher1.local1', actual.id); + assert.equal('localPublisher1', actual.publisher); + assert.equal('1.1.0', actual.version); + assert.equal('1.1.0', actual.latestVersion); + assert.equal('localDescription1', actual.description); + assert.equal('file:///localPath1/localIcon1', actual.iconUrl); + assert.equal('file:///localPath1/localIcon1', actual.iconUrlFallback); + assert.equal(null, actual.licenseUrl); + assert.equal(ExtensionState.Installed, actual.state); + assert.equal(null, actual.installCount); + assert.equal(null, actual.rating); + assert.equal(null, actual.ratingCount); + assert.equal(false, actual.outdated); + assert.deepEqual(['pub.1', 'pub.2'], actual.dependencies); + + actual = actuals[1]; + assert.equal(LocalExtensionType.System, actual.type); + assert.equal('local2', actual.name); + assert.equal('localDisplayName2', actual.displayName); + assert.equal('localPublisher2.local2', actual.id); + assert.equal('localPublisher2', actual.publisher); + assert.equal('1.2.0', actual.version); + assert.equal('1.2.0', actual.latestVersion); + assert.equal('localDescription2', actual.description); + assert.ok(fs.existsSync(actual.iconUrl)); + assert.equal(null, actual.licenseUrl); + assert.equal(ExtensionState.Installed, actual.state); + assert.equal(null, actual.installCount); + assert.equal(null, actual.rating); + assert.equal(null, actual.ratingCount); + assert.equal(false, actual.outdated); + assert.deepEqual([], actual.dependencies); + }); + + test('test installed extensions get syncs with gallery', () => { + const local1 = aLocalExtension('local1', { + publisher: 'localPublisher1', + version: '1.1.0', + displayName: 'localDisplayName1', + description: 'localDescription1', + icon: 'localIcon1', + extensionDependencies: ['pub.1', 'pub.2'], + }, { + type: LocalExtensionType.User, + readmeUrl: 'localReadmeUrl1', + changelogUrl: 'localChangelogUrl1', + path: 'localPath1' + }); + const local2 = aLocalExtension('local2', { + publisher: 'localPublisher2', + version: '1.2.0', + displayName: 'localDisplayName2', + description: 'localDescription2', + }, { + type: LocalExtensionType.System, + readmeUrl: 'localReadmeUrl2', + changelogUrl: 'localChangelogUrl2', + }); + const gallery1 = aGalleryExtension(local1.manifest.name, { + id: local1.id, + displayName: 'expectedDisplayName', + version: '1.5.0', + publisherId: 'expectedPublisherId', + publisher: local1.manifest.publisher, + publisherDisplayName: 'expectedPublisherDisplayName', + description: 'expectedDescription', + installCount: 1000, + rating: 4, + ratingCount: 100 + }, { + dependencies: ['pub.1'], + }, { + manifest: { uri: 'uri:manifest', fallbackUri: 'fallback:manifest' }, + readme: { uri: 'uri:readme', fallbackUri: 'fallback:readme' }, + changelog: { uri: 'uri:changelog', fallbackUri: 'fallback:changlog' }, + download: { uri: 'uri:download', fallbackUri: 'fallback:download' }, + icon: { uri: 'uri:icon', fallbackUri: 'fallback:icon' }, + license: { uri: 'uri:license', fallbackUri: 'fallback:license' } + }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local1, local2]); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery1)); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + return eventToPromise(testObject.onChange).then(() => { + const actuals = testObject.local; + assert.equal(2, actuals.length); + + let actual = actuals[0]; + assert.equal(LocalExtensionType.User, actual.type); + assert.equal('local1', actual.name); + assert.equal('expectedDisplayName', actual.displayName); + assert.equal('localPublisher1.local1', actual.id); + assert.equal('localPublisher1', actual.publisher); + assert.equal('1.1.0', actual.version); + assert.equal('1.5.0', actual.latestVersion); + assert.equal('expectedDescription', actual.description); + assert.equal('uri:icon', actual.iconUrl); + assert.equal('fallback:icon', actual.iconUrlFallback); + assert.equal(ExtensionState.Installed, actual.state); + assert.equal('uri:license', actual.licenseUrl); + assert.equal(1000, actual.installCount); + assert.equal(4, actual.rating); + assert.equal(100, actual.ratingCount); + assert.equal(true, actual.outdated); + assert.deepEqual(['pub.1', 'pub.2'], actual.dependencies); + + actual = actuals[1]; + assert.equal(LocalExtensionType.System, actual.type); + assert.equal('local2', actual.name); + assert.equal('localDisplayName2', actual.displayName); + assert.equal('localPublisher2.local2', actual.id); + assert.equal('localPublisher2', actual.publisher); + assert.equal('1.2.0', actual.version); + assert.equal('1.2.0', actual.latestVersion); + assert.equal('localDescription2', actual.description); + assert.ok(fs.existsSync(actual.iconUrl)); + assert.equal(null, actual.licenseUrl); + assert.equal(ExtensionState.Installed, actual.state); + assert.equal(null, actual.installCount); + assert.equal(null, actual.rating); + assert.equal(null, actual.ratingCount); + assert.equal(false, actual.outdated); + assert.deepEqual([], actual.dependencies); + }); + }); + + test('test extension state computation', () => { + const gallery = aGalleryExtension('gallery1'); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + + return testObject.queryGallery().then(page => { + const extension = page.firstPage[0]; + assert.equal(ExtensionState.Uninstalled, extension.state); + + testObject.install(extension); + const id = getLocalExtensionIdFromGallery(gallery, gallery.version); + + // Installing + installEvent.fire({ id, gallery }); + let local = testObject.local; + assert.equal(1, local.length); + const actual = local[0]; + assert.equal(`${gallery.publisher}.${gallery.name}`, actual.id); + assert.equal(ExtensionState.Installing, actual.state); + + // Installed + didInstallEvent.fire({ id, gallery, local: aLocalExtension(gallery.name, gallery, { id }) }); + assert.equal(ExtensionState.Installed, actual.state); + assert.equal(1, testObject.local.length); + + testObject.uninstall(actual); + + // Uninstalling + uninstallEvent.fire(id); + assert.equal(ExtensionState.Uninstalling, actual.state); + + // Uninstalled + didUninstallEvent.fire({ id }); + assert.equal(ExtensionState.Uninstalled, actual.state); + + assert.equal(0, testObject.local.length); + }); + }); + + test('test extension doesnot show outdated for system extensions', () => { + const local = aLocalExtension('a', { version: '1.0.1' }, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension(local.manifest.name, { id: local.id, version: '1.0.2' }))); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + assert.ok(!testObject.local[0].outdated); + }); + + test('test canInstall returns false for extensions with out gallery', () => { + const local = aLocalExtension('a', { version: '1.0.1' }, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + const target = testObject.local[0]; + testObject.uninstall(target); + uninstallEvent.fire(local.id); + didUninstallEvent.fire({ id: local.id }); + + assert.ok(!testObject.canInstall(target)); + }); + + test('test canInstall returns false for a system extension', () => { + const local = aLocalExtension('a', { version: '1.0.1' }, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension(local.manifest.name, { id: local.id }))); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + const target = testObject.local[0]; + + assert.ok(!testObject.canInstall(target)); + }); + + test('test canInstall returns true for extensions with gallery', () => { + const local = aLocalExtension('a', { version: '1.0.1' }, { type: LocalExtensionType.User }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension(local.manifest.name, { id: local.id }))); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + const target = testObject.local[0]; + + return eventToPromise(testObject.onChange).then(() => { + assert.ok(testObject.canInstall(target)); + }); + }); + + test('test onchange event is triggered while installing', () => { + const gallery = aGalleryExtension('gallery1'); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + const target = sinon.spy(); + + return testObject.queryGallery().then(page => { + const extension = page.firstPage[0]; + assert.equal(ExtensionState.Uninstalled, extension.state); + + testObject.install(extension); + installEvent.fire({ id: gallery.uuid, gallery }); + testObject.onChange(target); + + // Installed + didInstallEvent.fire({ id: gallery.uuid, gallery, local: aLocalExtension(gallery.name, gallery, gallery) }); + + assert.ok(target.calledOnce); + }); + }); + + test('test onchange event is triggered when installation is finished', () => { + const gallery = aGalleryExtension('gallery1'); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + const target = sinon.spy(); + + return testObject.queryGallery().then(page => { + const extension = page.firstPage[0]; + assert.equal(ExtensionState.Uninstalled, extension.state); + + testObject.install(extension); + testObject.onChange(target); + + // Installing + installEvent.fire({ id: gallery.uuid, gallery }); + + assert.ok(target.calledOnce); + }); + }); + + test('test onchange event is triggered while uninstalling', () => { + const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + const target = sinon.spy(); + + testObject.uninstall(testObject.local[0]); + testObject.onChange(target); + uninstallEvent.fire(local.id); + + assert.ok(target.calledOnce); + }); + + test('test onchange event is triggered when uninstalling is finished', () => { + const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + const target = sinon.spy(); + + testObject.uninstall(testObject.local[0]); + uninstallEvent.fire(local.id); + testObject.onChange(target); + didUninstallEvent.fire({ id: local.id }); + + assert.ok(target.calledOnce); + }); + + test('test extension dependencies when empty', () => { + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a'))); + + return testObject.queryGallery().then(page => { + return testObject.loadDependencies(page.firstPage[0]).then(dependencies => { + assert.equal(null, dependencies); + }); + }); + }); + + test('test one level extension dependencies without cycle', () => { + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.b', 'pub.c', 'pub.d'] }))); + instantiationService.stubPromise(IExtensionGalleryService, 'getAllDependencies', [aGalleryExtension('b'), aGalleryExtension('c'), aGalleryExtension('d')]); + + return testObject.queryGallery().then(page => { + const extension = page.firstPage[0]; + return testObject.loadDependencies(extension).then(actual => { + assert.ok(actual.hasDependencies); + assert.equal(extension, actual.extension); + assert.equal(null, actual.dependent); + assert.equal(3, actual.dependencies.length); + assert.equal('pub.a', actual.identifier); + let dependent = actual; + + actual = dependent.dependencies[0]; + assert.ok(!actual.hasDependencies); + assert.equal('pub.b', actual.extension.id); + assert.equal('pub.b', actual.identifier); + assert.equal(dependent, actual.dependent); + assert.equal(0, actual.dependencies.length); + + actual = dependent.dependencies[1]; + assert.ok(!actual.hasDependencies); + assert.equal('pub.c', actual.extension.id); + assert.equal('pub.c', actual.identifier); + assert.equal(dependent, actual.dependent); + assert.equal(0, actual.dependencies.length); + + actual = dependent.dependencies[2]; + assert.ok(!actual.hasDependencies); + assert.equal('pub.d', actual.extension.id); + assert.equal('pub.d', actual.identifier); + assert.equal(dependent, actual.dependent); + assert.equal(0, actual.dependencies.length); + }); + }); + }); + + test('test one level extension dependencies with cycle', () => { + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.b', 'pub.a'] }))); + instantiationService.stubPromise(IExtensionGalleryService, 'getAllDependencies', [aGalleryExtension('b'), aGalleryExtension('a')]); + + return testObject.queryGallery().then(page => { + const extension = page.firstPage[0]; + return testObject.loadDependencies(extension).then(actual => { + assert.ok(actual.hasDependencies); + assert.equal(extension, actual.extension); + assert.equal(null, actual.dependent); + assert.equal(2, actual.dependencies.length); + assert.equal('pub.a', actual.identifier); + let dependent = actual; + + actual = dependent.dependencies[0]; + assert.ok(!actual.hasDependencies); + assert.equal('pub.b', actual.extension.id); + assert.equal('pub.b', actual.identifier); + assert.equal(dependent, actual.dependent); + assert.equal(0, actual.dependencies.length); + + actual = dependent.dependencies[1]; + assert.ok(!actual.hasDependencies); + assert.equal('pub.a', actual.extension.id); + assert.equal('pub.a', actual.identifier); + assert.equal(dependent, actual.dependent); + assert.equal(0, actual.dependencies.length); + }); + }); + }); + + test('test one level extension dependencies with missing dependencies', () => { + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.b', 'pub.a'] }))); + instantiationService.stubPromise(IExtensionGalleryService, 'getAllDependencies', [aGalleryExtension('a')]); + + return testObject.queryGallery().then(page => { + const extension = page.firstPage[0]; + return testObject.loadDependencies(extension).then(actual => { + assert.ok(actual.hasDependencies); + assert.equal(extension, actual.extension); + assert.equal(null, actual.dependent); + assert.equal(2, actual.dependencies.length); + assert.equal('pub.a', actual.identifier); + let dependent = actual; + + actual = dependent.dependencies[0]; + assert.ok(!actual.hasDependencies); + assert.equal(null, actual.extension); + assert.equal('pub.b', actual.identifier); + assert.equal(dependent, actual.dependent); + assert.equal(0, actual.dependencies.length); + + actual = dependent.dependencies[1]; + assert.ok(!actual.hasDependencies); + assert.equal('pub.a', actual.extension.id); + assert.equal('pub.a', actual.identifier); + assert.equal(dependent, actual.dependent); + assert.equal(0, actual.dependencies.length); + }); + }); + }); + + test('test one level extension dependencies with in built dependencies', () => { + const local = aLocalExtension('inbuilt', {}, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.inbuilt', 'pub.a'] }))); + instantiationService.stubPromise(IExtensionGalleryService, 'getAllDependencies', [aGalleryExtension('a')]); + + return testObject.queryGallery().then(page => { + const extension = page.firstPage[0]; + return testObject.loadDependencies(extension).then(actual => { + assert.ok(actual.hasDependencies); + assert.equal(extension, actual.extension); + assert.equal(null, actual.dependent); + assert.equal(2, actual.dependencies.length); + assert.equal('pub.a', actual.identifier); + let dependent = actual; + + actual = dependent.dependencies[0]; + assert.ok(!actual.hasDependencies); + assert.equal('pub.inbuilt', actual.extension.id); + assert.equal('pub.inbuilt', actual.identifier); + assert.equal(dependent, actual.dependent); + assert.equal(0, actual.dependencies.length); + + + actual = dependent.dependencies[1]; + assert.ok(!actual.hasDependencies); + assert.equal('pub.a', actual.extension.id); + assert.equal('pub.a', actual.identifier); + assert.equal(dependent, actual.dependent); + assert.equal(0, actual.dependencies.length); + }); + }); + }); + + test('test more than one level of extension dependencies', () => { + const local = aLocalExtension('c', { extensionDependencies: ['pub.d'] }, { type: LocalExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.b', 'pub.c'] }))); + instantiationService.stubPromise(IExtensionGalleryService, 'getAllDependencies', [ + aGalleryExtension('b', {}, { dependencies: ['pub.d', 'pub.e'] }), + aGalleryExtension('d', {}, { dependencies: ['pub.f', 'pub.c'] }), + aGalleryExtension('e')]); + + return testObject.queryGallery().then(page => { + const extension = page.firstPage[0]; + return testObject.loadDependencies(extension).then(a => { + assert.ok(a.hasDependencies); + assert.equal(extension, a.extension); + assert.equal(null, a.dependent); + assert.equal(2, a.dependencies.length); + assert.equal('pub.a', a.identifier); + + let b = a.dependencies[0]; + assert.ok(b.hasDependencies); + assert.equal('pub.b', b.extension.id); + assert.equal('pub.b', b.identifier); + assert.equal(a, b.dependent); + assert.equal(2, b.dependencies.length); + + let c = a.dependencies[1]; + assert.ok(c.hasDependencies); + assert.equal('pub.c', c.extension.id); + assert.equal('pub.c', c.identifier); + assert.equal(a, c.dependent); + assert.equal(1, c.dependencies.length); + + let d = b.dependencies[0]; + assert.ok(d.hasDependencies); + assert.equal('pub.d', d.extension.id); + assert.equal('pub.d', d.identifier); + assert.equal(b, d.dependent); + assert.equal(2, d.dependencies.length); + + let e = b.dependencies[1]; + assert.ok(!e.hasDependencies); + assert.equal('pub.e', e.extension.id); + assert.equal('pub.e', e.identifier); + assert.equal(b, e.dependent); + assert.equal(0, e.dependencies.length); + + let f = d.dependencies[0]; + assert.ok(!f.hasDependencies); + assert.equal(null, f.extension); + assert.equal('pub.f', f.identifier); + assert.equal(d, f.dependent); + assert.equal(0, f.dependencies.length); + + c = d.dependencies[1]; + assert.ok(c.hasDependencies); + assert.equal('pub.c', c.extension.id); + assert.equal('pub.c', c.identifier); + assert.equal(d, c.dependent); + assert.equal(1, c.dependencies.length); + + d = c.dependencies[0]; + assert.ok(!d.hasDependencies); + assert.equal('pub.d', d.extension.id); + assert.equal('pub.d', d.identifier); + assert.equal(c, d.dependent); + assert.equal(0, d.dependencies.length); + + c = a.dependencies[1]; + d = c.dependencies[0]; + assert.ok(d.hasDependencies); + assert.equal('pub.d', d.extension.id); + assert.equal('pub.d', d.identifier); + assert.equal(c, d.dependent); + assert.equal(2, d.dependencies.length); + + f = d.dependencies[0]; + assert.ok(!f.hasDependencies); + assert.equal(null, f.extension); + assert.equal('pub.f', f.identifier); + assert.equal(d, f.dependent); + assert.equal(0, f.dependencies.length); + + c = d.dependencies[1]; + assert.ok(!c.hasDependencies); + assert.equal('pub.c', c.extension.id); + assert.equal('pub.c', c.identifier); + assert.equal(d, c.dependent); + assert.equal(0, c.dependencies.length); + }); + }); + }); + + test('test disabled flags are false for uninstalled extension', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', false, true); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a'))); + return testObject.queryGallery().then(pagedResponse => { + const actual = pagedResponse.firstPage[0]; + + assert.ok(!actual.disabledForWorkspace); + assert.ok(!actual.disabledGlobally); + }); + }); + + test('test disabled flags are false for installed enabled extension', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', false, true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + const actual = testObject.local[0]; + + assert.ok(!actual.disabledForWorkspace); + assert.ok(!actual.disabledGlobally); + }); + + test('test disabled for workspace is set', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.d', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false, true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.e', false, true); + + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + const actual = testObject.local[0]; + + assert.ok(actual.disabledForWorkspace); + assert.ok(!actual.disabledGlobally); + }); + + test('test disabled globally is set', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.d', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', false, true); + + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + const actual = testObject.local[0]; + + assert.ok(!actual.disabledForWorkspace); + assert.ok(actual.disabledGlobally); + }); + + test('test disable flags are updated for user extensions', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', false, true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], false, true); + const actual = testObject.local[0]; + + assert.ok(actual.disabledForWorkspace); + assert.ok(!actual.disabledGlobally); + }); + + test('test enable extension globally when extension is disabled for workspace', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false, true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], true); + const actual = testObject.local[0]; + + assert.ok(!actual.disabledForWorkspace); + assert.ok(!actual.disabledGlobally); + }); + + test('test disable extension globally should not disable for workspace', () => { + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], false); + const actual = testObject.local[0]; + + assert.ok(!actual.disabledForWorkspace); + assert.ok(actual.disabledGlobally); + }); + + test('test disabled flags are not updated for system extensions', () => { + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', {}, { type: LocalExtensionType.System })]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], false); + const actual = testObject.local[0]; + + assert.ok(!actual.disabledForWorkspace); + assert.ok(!actual.disabledGlobally); + }); + + test('test disabled flags are updated on change from outside', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', false, true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + const actual = testObject.local[0]; + + assert.ok(!actual.disabledForWorkspace); + assert.ok(actual.disabledGlobally); + }); + + test('test disable extension with dependencies disable only itself', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], false); + + assert.ok(testObject.local[0].disabledGlobally); + assert.ok(!testObject.local[1].disabledGlobally); + }); + + test('test disable extension with dependencies disable all', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]); + instantiationService.stubPromise(IChoiceService, 'choose', 1); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], false); + + assert.ok(testObject.local[0].disabledGlobally); + assert.ok(testObject.local[1].disabledGlobally); + }); + + test('test disable extension fails if extension is a dependent of other', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + return testObject.setEnablement(testObject.local[1], false).then(() => assert.fail('Should fail'), error => assert.ok(true)); + }); + + test('test disable extension does not fail if its dependency is a dependent of other but chosen to disable only itself', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]); + + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], false); + + assert.ok(testObject.local[0].disabledGlobally); + }); + + test('test disable extension fails if its dependency is a dependent of other', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]); + instantiationService.stubPromise(IChoiceService, 'choose', 1); + + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + return testObject.setEnablement(testObject.local[0], false).then(() => assert.fail('Should fail'), error => assert.ok(true)); + }); + + test('test disable extension if its dependency is a dependent of other disabled extension', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', false); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]); + instantiationService.stubPromise(IChoiceService, 'choose', 1); + + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], false); + + assert.ok(testObject.local[0].disabledGlobally); + assert.ok(testObject.local[1].disabledGlobally); + }); + + test('test disable extension if its dependencys dependency is itself', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b', { extensionDependencies: ['pub.a'] }), aLocalExtension('c')]); + instantiationService.stubPromise(IChoiceService, 'choose', 1); + + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], false); + + assert.ok(testObject.local[0].disabledGlobally); + assert.ok(testObject.local[1].disabledGlobally); + }); + + test('test disable extension if its dependency is dependent and is disabled', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]); + instantiationService.stubPromise(IChoiceService, 'choose', 1); + + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], false); + + assert.ok(testObject.local[0].disabledGlobally); + }); + + test('test disable extension with cyclic dependencies', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', true); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b', { extensionDependencies: ['pub.c'] }), aLocalExtension('c', { extensionDependencies: ['pub.a'] })]); + instantiationService.stubPromise(IChoiceService, 'choose', 1); + + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], false); + + assert.ok(testObject.local[0].disabledGlobally); + assert.ok(testObject.local[1].disabledGlobally); + assert.ok(testObject.local[2].disabledGlobally); + }); + + test('test enable extension with dependencies enable all', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', false); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], true); + + assert.ok(!testObject.local[0].disabledGlobally); + assert.ok(!testObject.local[1].disabledGlobally); + }); + + test('test enable extension with cyclic dependencies', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', false); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b', { extensionDependencies: ['pub.c'] }), aLocalExtension('c', { extensionDependencies: ['pub.a'] })]); + + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + + testObject.setEnablement(testObject.local[0], true); + + assert.ok(!testObject.local[0].disabledGlobally); + assert.ok(!testObject.local[1].disabledGlobally); + assert.ok(!testObject.local[2].disabledGlobally); + }); + + test('test change event is fired when disablement flags are changed', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', false, true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + const target = sinon.spy(); + testObject.onChange(target); + + testObject.setEnablement(testObject.local[0], false); + + assert.ok(target.calledOnce); + }); + + test('test change event is fired when disablement flags are changed from outside', () => { + instantiationService.get(IExtensionEnablementService).setEnablement('pub.c', false); + instantiationService.get(IExtensionEnablementService).setEnablement('pub.b', false, true); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); + testObject = instantiationService.createInstance(ExtensionsWorkbenchService); + const target = sinon.spy(); + testObject.onChange(target); + + instantiationService.get(IExtensionEnablementService).setEnablement('pub.a', false); + + assert.ok(target.calledOnce); + }); + + function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension { + const localExtension = Object.create({ manifest: {} }); + assign(localExtension, { type: LocalExtensionType.User, manifest: {} }, properties); + assign(localExtension.manifest, { name, publisher: 'pub', version: '1.0.0' }, manifest); + localExtension.metadata = { id: localExtension.id, publisherId: localExtension.manifest.publisher, publisherDisplayName: 'somename' }; + localExtension.id = getLocalExtensionIdFromManifest(localExtension.manifest); + return localExtension; + } + + const noAssets: IGalleryExtensionAssets = { + changelog: null, + download: null, + icon: null, + license: null, + manifest: null, + readme: null + }; + + function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: IGalleryExtensionAssets = noAssets): IGalleryExtension { + const galleryExtension = Object.create({}); + assign(galleryExtension, { name, publisher: 'pub', uuid: generateUuid(), version: '1.0.0', properties: {}, assets: {} }, properties); + assign(galleryExtension.properties, { dependencies: [] }, galleryExtensionProperties); + assign(galleryExtension.assets, assets); + galleryExtension.id = getGalleryExtensionId(galleryExtension.publisher, galleryExtension.name); + return galleryExtension; + } + + function aPage(...objects: T[]): IPager { + return { firstPage: objects, total: objects.length, pageSize: objects.length, getPage: () => null }; + } + + function eventToPromise(event: Event, count: number = 1): TPromise { + return new TPromise(c => { + let counter = 0; + event(() => { + if (++counter === count) { + c(null); + } + }); + }); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedback.contribution.ts b/src/vs/workbench/parts/feedback/electron-browser/feedback.contribution.ts new file mode 100644 index 0000000000..4dd7a057ef --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/feedback.contribution.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Registry } from 'vs/platform/registry/common/platform'; +import { StatusbarAlignment, IStatusbarRegistry, Extensions, StatusbarItemDescriptor } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { FeedbackStatusbarItem } from './feedbackStatusbarItem'; + +// Register Statusbar item +Registry.as(Extensions.Statusbar).registerStatusbarItem(new StatusbarItemDescriptor( + FeedbackStatusbarItem, + StatusbarAlignment.RIGHT, + -100 /* Low Priority */ +)); \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts new file mode 100644 index 0000000000..fb25abc195 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts @@ -0,0 +1,364 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/feedback'; +import nls = require('vs/nls'); +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { Dropdown } from 'vs/base/browser/ui/dropdown/dropdown'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import product from 'vs/platform/node/product'; +import * as dom from 'vs/base/browser/dom'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import * as errors from 'vs/base/common/errors'; +import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; +import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { editorWidgetBackground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, buttonBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; + +export interface IFeedback { + feedback: string; + sentiment: number; +} + +export interface IFeedbackService { + submitFeedback(feedback: IFeedback): void; + getCharacterLimit(sentiment: number): number; +} + +export interface IFeedbackDropdownOptions { + contextViewProvider: IContextViewService; + feedbackService?: IFeedbackService; +} + +enum FormEvent { + SENDING, + SENT, + SEND_ERROR +} + +export class FeedbackDropdown extends Dropdown { + protected maxFeedbackCharacters: number; + + protected feedback: string; + protected sentiment: number; + protected aliasEnabled: boolean; + protected isSendingFeedback: boolean; + protected autoHideTimeout: number; + + protected feedbackService: IFeedbackService; + + protected feedbackForm: HTMLFormElement; + protected feedbackDescriptionInput: HTMLTextAreaElement; + protected smileyInput: Builder; + protected frownyInput: Builder; + protected sendButton: Builder; + protected remainingCharacterCount: Builder; + + protected requestFeatureLink: string; + protected reportIssueLink: string; + + private _isPure: boolean; + + constructor( + container: HTMLElement, + options: IFeedbackDropdownOptions, + @ITelemetryService protected telemetryService: ITelemetryService, + @ICommandService private commandService: ICommandService, + @IIntegrityService protected integrityService: IIntegrityService, + @IThemeService private themeService: IThemeService + ) { + super(container, { + contextViewProvider: options.contextViewProvider, + labelRenderer: (container: HTMLElement): IDisposable => { + $(container).addClass('send-feedback', 'mask-icon'); + + return null; + } + }); + + this._isPure = true; + this.integrityService.isPure().then(result => { + if (!result.isPure) { + this._isPure = false; + } + }); + + this.element.addClass('send-feedback'); + this.element.title(nls.localize('sendFeedback', "Tweet Feedback")); + + this.feedbackService = options.feedbackService; + + this.feedback = ''; + this.sentiment = 1; + this.maxFeedbackCharacters = this.feedbackService.getCharacterLimit(this.sentiment); + + this.feedbackForm = null; + this.feedbackDescriptionInput = null; + + this.smileyInput = null; + this.frownyInput = null; + + this.sendButton = null; + + this.reportIssueLink = product.reportIssueUrl; + this.requestFeatureLink = product.requestFeatureUrl; + } + + protected renderContents(container: HTMLElement): IDisposable { + const $form = $('form.feedback-form').attr({ + action: 'javascript:void(0);', + tabIndex: '-1' + }).appendTo(container); + + $(container).addClass('monaco-menu-container'); + + this.feedbackForm = $form.getHTMLElement(); + + $('h2.title').text(nls.localize("label.sendASmile", "Tweet us your feedback.")).appendTo($form); + + this.invoke($('div.cancel').attr('tabindex', '0'), () => { + this.hide(); + }).appendTo($form); + + const $content = $('div.content').appendTo($form); + + const $sentimentContainer = $('div').appendTo($content); + if (!this._isPure) { + $('span').text(nls.localize("patchedVersion1", "Your installation is corrupt.")).appendTo($sentimentContainer); + $('br').appendTo($sentimentContainer); + $('span').text(nls.localize("patchedVersion2", "Please specify this if you submit a bug.")).appendTo($sentimentContainer); + $('br').appendTo($sentimentContainer); + } + $('span').text(nls.localize("sentiment", "How was your experience?")).appendTo($sentimentContainer); + + const $feedbackSentiment = $('div.feedback-sentiment').appendTo($sentimentContainer); + + this.smileyInput = $('div').addClass('sentiment smile').attr({ + 'aria-checked': 'false', + 'aria-label': nls.localize('smileCaption', "Happy"), + 'tabindex': 0, + 'role': 'checkbox' + }); + this.invoke(this.smileyInput, () => { this.setSentiment(true); }).appendTo($feedbackSentiment); + + this.frownyInput = $('div').addClass('sentiment frown').attr({ + 'aria-checked': 'false', + 'aria-label': nls.localize('frownCaption', "Sad"), + 'tabindex': 0, + 'role': 'checkbox' + }); + + this.invoke(this.frownyInput, () => { this.setSentiment(false); }).appendTo($feedbackSentiment); + + if (this.sentiment === 1) { + this.smileyInput.addClass('checked').attr('aria-checked', 'true'); + } else { + this.frownyInput.addClass('checked').attr('aria-checked', 'true'); + } + + const $contactUs = $('div.contactus').appendTo($content); + + $('span').text(nls.localize("other ways to contact us", "Other ways to contact us")).appendTo($contactUs); + + const $contactUsContainer = $('div.channels').appendTo($contactUs); + + $('div').append($('a').attr('target', '_blank').attr('href', '#').text(nls.localize("submit a bug", "Submit a bug")).attr('tabindex', '0')) + .on('click', event => { + dom.EventHelper.stop(event); + this.commandService.executeCommand('workbench.action.reportIssues').done(null, errors.onUnexpectedError); + }) + .appendTo($contactUsContainer); + + $('div').append($('a').attr('target', '_blank').attr('href', this.requestFeatureLink).text(nls.localize("request a missing feature", "Request a missing feature")).attr('tabindex', '0')) + .appendTo($contactUsContainer); + + this.remainingCharacterCount = $('span.char-counter').text(this.getCharCountText(0)); + + $('h3').text(nls.localize("tell us why?", "Tell us why?")) + .append(this.remainingCharacterCount) + .appendTo($form); + + this.feedbackDescriptionInput = $('textarea.feedback-description').attr({ + rows: 3, + maxlength: this.maxFeedbackCharacters, + 'aria-label': nls.localize("commentsHeader", "Comments") + }) + .text(this.feedback).attr('required', 'required') + .on('keyup', () => { + this.updateCharCountText(); + }) + .appendTo($form).domFocus().getHTMLElement(); + + const $buttons = $('div.form-buttons').appendTo($form); + + this.sendButton = this.invoke($('input.send').type('submit').attr('disabled', '').value(nls.localize('tweet', "Tweet")).appendTo($buttons), () => { + if (this.isSendingFeedback) { + return; + } + this.onSubmit(); + }); + + this.toDispose.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground, inputBackground, inputForeground, inputBorder, editorBackground, contrastBorder }, colors => { + $form.style('background-color', colors.editorWidgetBackground); + $form.style('box-shadow', colors.widgetShadow ? `0 2px 8px ${colors.widgetShadow}` : null); + + if (this.feedbackDescriptionInput) { + this.feedbackDescriptionInput.style.backgroundColor = colors.inputBackground; + this.feedbackDescriptionInput.style.color = colors.inputForeground; + this.feedbackDescriptionInput.style.border = `1px solid ${colors.inputBorder || 'transparent'}`; + } + + $contactUs.style('background-color', colors.editorBackground); + $contactUs.style('border', `1px solid ${colors.contrastBorder || 'transparent'}`); + })); + + return { + dispose: () => { + this.feedbackForm = null; + this.feedbackDescriptionInput = null; + this.smileyInput = null; + this.frownyInput = null; + } + }; + } + + private getCharCountText(charCount: number): string { + const remaining = this.maxFeedbackCharacters - charCount; + const text = (remaining === 1) + ? nls.localize("character left", "character left") + : nls.localize("characters left", "characters left"); + + return '(' + remaining + ' ' + text + ')'; + } + + private updateCharCountText(): void { + this.remainingCharacterCount.text(this.getCharCountText(this.feedbackDescriptionInput.value.length)); + this.feedbackDescriptionInput.value ? this.sendButton.removeAttribute('disabled') : this.sendButton.attr('disabled', ''); + } + + protected setSentiment(smile: boolean): void { + if (smile) { + this.smileyInput.addClass('checked'); + this.smileyInput.attr('aria-checked', 'true'); + this.frownyInput.removeClass('checked'); + this.frownyInput.attr('aria-checked', 'false'); + } else { + this.frownyInput.addClass('checked'); + this.frownyInput.attr('aria-checked', 'true'); + this.smileyInput.removeClass('checked'); + this.smileyInput.attr('aria-checked', 'false'); + } + this.sentiment = smile ? 1 : 0; + this.maxFeedbackCharacters = this.feedbackService.getCharacterLimit(this.sentiment); + this.updateCharCountText(); + $(this.feedbackDescriptionInput).attr({ maxlength: this.maxFeedbackCharacters }); + } + + protected invoke(element: Builder, callback: () => void): Builder { + element.on('click', callback); + element.on('keypress', (e) => { + if (e instanceof KeyboardEvent) { + const keyboardEvent = e; + if (keyboardEvent.keyCode === 13 || keyboardEvent.keyCode === 32) { // Enter or Spacebar + callback(); + } + } + }); + return element; + } + + public hide(): void { + if (this.feedbackDescriptionInput) { + this.feedback = this.feedbackDescriptionInput.value; + } + + if (this.autoHideTimeout) { + clearTimeout(this.autoHideTimeout); + this.autoHideTimeout = null; + } + + super.hide(); + } + + public onEvent(e: Event, activeElement: HTMLElement): void { + if (e instanceof KeyboardEvent) { + const keyboardEvent = e; + if (keyboardEvent.keyCode === 27) { // Escape + this.hide(); + } + } + } + + protected onSubmit(): void { + if ((this.feedbackForm.checkValidity && !this.feedbackForm.checkValidity())) { + return; + } + + this.changeFormStatus(FormEvent.SENDING); + + this.feedbackService.submitFeedback({ + feedback: this.feedbackDescriptionInput.value, + sentiment: this.sentiment + }); + + this.changeFormStatus(FormEvent.SENT); + } + + + private changeFormStatus(event: FormEvent): void { + switch (event) { + case FormEvent.SENDING: + this.isSendingFeedback = true; + this.sendButton.setClass('send in-progress'); + this.sendButton.value(nls.localize('feedbackSending', "Sending")); + break; + case FormEvent.SENT: + this.isSendingFeedback = false; + this.sendButton.setClass('send success').value(nls.localize('feedbackSent', "Thanks")); + this.resetForm(); + this.autoHideTimeout = setTimeout(() => { + this.hide(); + }, 1000); + this.sendButton.off(['click', 'keypress']); + this.invoke(this.sendButton, () => { + this.hide(); + this.sendButton.off(['click', 'keypress']); + }); + break; + case FormEvent.SEND_ERROR: + this.isSendingFeedback = false; + this.sendButton.setClass('send error').value(nls.localize('feedbackSendingError', "Try again")); + break; + } + } + + protected resetForm(): void { + if (this.feedbackDescriptionInput) { + this.feedbackDescriptionInput.value = ''; + } + this.sentiment = 1; + this.maxFeedbackCharacters = this.feedbackService.getCharacterLimit(this.sentiment); + this.aliasEnabled = false; + } +} + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + + // Sentiment Buttons + const inputActiveOptionBorderColor = theme.getColor(inputActiveOptionBorder); + if (inputActiveOptionBorderColor) { + collector.addRule(`.monaco-shell .feedback-form .sentiment.checked { border: 1px solid ${inputActiveOptionBorderColor}; }`); + } + + // Links + const linkColor = theme.getColor(buttonBackground) || theme.getColor(contrastBorder); + if (linkColor) { + collector.addRule(`.monaco-shell .feedback-form .content .channels a { color: ${linkColor}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts new file mode 100644 index 0000000000..ac05b541d6 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { FeedbackDropdown, IFeedback, IFeedbackService } from './feedback'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import product from 'vs/platform/node/product'; +import { Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +class TwitterFeedbackService implements IFeedbackService { + + private static TWITTER_URL: string = 'https://twitter.com/intent/tweet'; + private static VIA_NAME: string = 'sqlopsstudio'; // {{SQL CARBON EDIT}} + private static HASHTAGS: string[] = ['HappySqlOps']; // {{SQL CARBON EDIT}} + + private combineHashTagsAsString(): string { + return TwitterFeedbackService.HASHTAGS.join(','); + } + + public submitFeedback(feedback: IFeedback): void { + const queryString = `?${feedback.sentiment === 1 ? `hashtags=${this.combineHashTagsAsString()}&` : null}ref_src=twsrc%5Etfw&related=twitterapi%2Ctwitter&text=${feedback.feedback}&tw_p=tweetbutton&via=${TwitterFeedbackService.VIA_NAME}`; + const url = TwitterFeedbackService.TWITTER_URL + queryString; + + window.open(url); + } + + public getCharacterLimit(sentiment: number): number { + let length: number = 0; + if (sentiment === 1) { + TwitterFeedbackService.HASHTAGS.forEach(element => { + length += element.length + 2; + }); + } + + if (TwitterFeedbackService.VIA_NAME) { + length += ` via @${TwitterFeedbackService.VIA_NAME}`.length; + } + + return 140 - length; + } +} + +export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { + private dropdown: FeedbackDropdown; + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IContextViewService private contextViewService: IContextViewService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IThemeService themeService: IThemeService + ) { + super(themeService); + + this.registerListeners(); + } + + private registerListeners(): void { + this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.updateStyles())); + } + + protected updateStyles(): void { + super.updateStyles(); + + if (this.dropdown) { + this.dropdown.label.style('background-color', this.getColor(this.contextService.hasWorkspace() ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND)); + } + } + + public render(element: HTMLElement): IDisposable { + if (product.sendASmile) { + this.dropdown = this.instantiationService.createInstance(FeedbackDropdown, element, { + contextViewProvider: this.contextViewService, + feedbackService: this.instantiationService.createInstance(TwitterFeedbackService) + }); + + this.updateStyles(); + + return this.dropdown; + } + + return null; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/close-dark.svg b/src/vs/workbench/parts/feedback/electron-browser/media/close-dark.svg new file mode 100644 index 0000000000..ce0e589640 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/media/close-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/close.svg b/src/vs/workbench/parts/feedback/electron-browser/media/close.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/media/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css b/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css new file mode 100644 index 0000000000..14ab11ebf7 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css @@ -0,0 +1,283 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +.monaco-shell .feedback-form { + width: 420px; + top: 30px; + right: 6px; + padding: 1em; + cursor: default; +} + +.monaco-shell .feedback-form h2 { + margin: 0; + padding: 0; + font-weight: normal; + font-size: 1.8em; +} + +.monaco-shell .feedback-form h3 { + margin: 1em 0 0; + padding: 0; + font-weight: normal; + font-size: 1.2em; +} + +.monaco-shell .feedback-form .content { + font-size: 1.2em; +} + +.monaco-shell .feedback-form .content > div { + display: inline-block; + vertical-align: top; + margin-top: 20px; +} + +.monaco-shell .feedback-form .content .contactus { + padding: 10px; + float: right; +} + +.monaco-shell .feedback-form .content .channels { + margin-top: 5px; + font-size: 0.9em; +} + +.monaco-shell .char-counter { + padding-left: 3px; +} + +/* TODO @C5 review link color */ +.monaco-shell .feedback-form .content .channels a { + padding: 2px 0; +} + +.monaco-shell .feedback-form .content .channels a:hover { + text-decoration: underline; +} + +.monaco-shell .feedback-form .feedback-alias, .monaco-shell .feedback-form .feedback-description { + resize: none; + font-size: 1.1em; + margin: 6px 0 0; + width: 100%; + padding: 3px 2px; + box-sizing: border-box; +} + +.monaco-shell .feedback-form .feedback-empty { + margin-top: .2em; +} + +.monaco-shell .feedback-form .feedback-alias-checkbox { + display: inline-block; + vertical-align: text-top; + margin-left: 0; +} + +.monaco-shell .feedback-form .feedback-alias { + height: 26px; +} + +.monaco-shell .feedback-form .feedback-alias:disabled { + opacity: 0.6; +} + +.monaco-shell .feedback-form .cancel { + position: absolute; + top: 0; + right: 0; + margin: .5em 0 0 0; + padding: .5em; + width: 22px; + height: 22px; + border: none; + cursor: pointer; +} + +.monaco-shell .feedback-form .form-buttons { + margin-top: 18px; + text-align: right; +} + +.monaco-shell .feedback-form .send { + padding: .5em 1.5em; + font-size: 1.1em; + margin: 0; + box-sizing: border-box; + transition: width 200ms ease-out; +} + +.monaco-shell .feedback-form .send.in-progress { + width: 22%; +} + +.monaco-shell .feedback-form .send.success, +.monaco-shell .feedback-form .send.error { + width: 20%; +} + +.monaco-shell .feedback-form .sentiment { + height: 32px; + width: 32px; + display: inline-block; + margin: .5em 0; + cursor: pointer; + box-sizing: border-box; +} + +.monaco-shell .feedback-form .sentiment:hover { + background-color: #eaeaea; +} + +/* Statusbar */ +.monaco-shell .statusbar-item > .dropdown.send-feedback { + display: inline-block; +} + +.monaco-shell .statusbar-item > .dropdown.send-feedback > .dropdown-label.send-feedback { + -webkit-mask: url('smiley.svg') no-repeat 50% 50%; /* use mask to be able to change color dynamically */ + width: 26px; +} + +/* Theming */ +.monaco-shell.vs .feedback-form .feedback-alias, .monaco-shell.vs .feedback-form .feedback-description { + font-family: inherit; + border: 1px solid transparent; +} + + +.monaco-shell.vs .feedback-form .cancel { + background: url('close.svg') center center no-repeat; +} + +.monaco-shell.vs .feedback-form .cancel:hover { + background-color: #eaeaea; +} + +.monaco-shell .feedback-form .form-buttons .send { + color: white; + border: none; + cursor: pointer; + background-image: url('twitter.svg'); + background-color: #007ACC; + background-position: left; + background-repeat: no-repeat; + padding-left: 30px; + padding-right: 12px; + border: 4px solid #007ACC; + border-radius: 4px; +} + +.monaco-shell .feedback-form .form-buttons .send.in-progress, +.monaco-shell .feedback-form .form-buttons .send:hover { + background-color: #006BB3; +} + +.monaco-shell .feedback-form .form-buttons .send:disabled { + pointer-events: none; + cursor: not-allowed; + opacity: .65; +} + +.monaco-shell .feedback-form .form-buttons .send.success { + background-color: #2d883e; +} + +.monaco-shell .feedback-form .form-buttons .send.error { + background-color: #E51400; +} + +.monaco-shell.vs-dark .feedback-form h3 { + font-weight: normal; + font-size: 1.2em; +} + +.monaco-shell.vs-dark .feedback-form .sentiment:hover { + background-color: rgba(30,30,30,0.8); +} + +.monaco-shell.vs-dark .feedback-form .feedback-alias, .monaco-shell.vs-dark .feedback-form .feedback-description { + font-family: inherit; +} + +.monaco-shell.vs-dark .feedback-form .cancel, +.monaco-shell.hc-black .feedback-form .cancel { + background: url('close-dark.svg') center center no-repeat; +} + +.monaco-shell.vs-dark .feedback-form .cancel:hover { + background-color: rgba(30,30,30,0.8); +} + +.monaco-shell .feedback-form .sentiment.smile { + background-image: url('happy.svg'); + background-position: center; + background-repeat: no-repeat; +} + +.monaco-shell .feedback-form .sentiment.frown { + background-image: url('sad.svg'); + background-position: center; + background-repeat: no-repeat; +} + +.monaco-shell .feedback-form .infotip { + background-image: url('info.svg'); + background-position: center; + background-repeat: no-repeat; + + height: 16px; + width: 16px; + display: inline-block; + vertical-align: text-bottom; + box-sizing: border-box; + margin-left: 5px; +} + +/* High Contrast Theming */ +.monaco-shell.hc-black .feedback-form { + outline: 2px solid #6fc3df; + outline-offset: -2px; +} + +.monaco-shell.hc-black .feedback-form .feedback-alias, .monaco-shell.hc-black .feedback-form .feedback-description { + font-family: inherit; +} + +.monaco-shell.hc-black .feedback-form .content .contactus { + padding: 10px; + float: right; +} +.monaco-shell.hc-black .feedback-form .cancel { + opacity: 0.6; +} + +.monaco-shell.hc-black .feedback-form .cancel:hover { + opacity: 1; +} + +.monaco-shell.hc-black .feedback-form .form-buttons .send, +.monaco-shell.hc-black .feedback-form .form-buttons .send.in-progress, +.monaco-shell.hc-black .feedback-form .form-buttons .send.success { + background-color: #0C141F; + color: #D4D4D4; + border: 1px solid #6FC3DF; +} + +.monaco-shell.hc-black .feedback-form .form-buttons .send:hover { + background-color: #0C141F; +} + + +.monaco-shell .feedback-form .infotip { + background: none; +} + +.monaco-shell .feedback-form .infotip:before { + content: url('info.svg'); + height: 16px; + width: 16px; + display: inline-block; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/happy.svg b/src/vs/workbench/parts/feedback/electron-browser/media/happy.svg new file mode 100644 index 0000000000..954c221de2 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/media/happy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/info.svg b/src/vs/workbench/parts/feedback/electron-browser/media/info.svg new file mode 100644 index 0000000000..6578b81ea3 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/media/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/sad.svg b/src/vs/workbench/parts/feedback/electron-browser/media/sad.svg new file mode 100644 index 0000000000..c424c268e4 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/media/sad.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/smiley.svg b/src/vs/workbench/parts/feedback/electron-browser/media/smiley.svg new file mode 100644 index 0000000000..6bc9527890 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/media/smiley.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/twitter.svg b/src/vs/workbench/parts/feedback/electron-browser/media/twitter.svg new file mode 100644 index 0000000000..086f90550f --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/media/twitter.svg @@ -0,0 +1,549 @@ + + + + + + diff --git a/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.ts new file mode 100644 index 0000000000..351a7cf182 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor'; +import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; + +/** + * An implementation of editor for binary files like images. + */ +export class BinaryFileEditor extends BaseBinaryResourceEditor { + + public static ID = BINARY_FILE_EDITOR_ID; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IWindowsService windowsService: IWindowsService + ) { + super(BinaryFileEditor.ID, telemetryService, themeService, windowsService); + } + + public getTitle(): string { + return this.input ? this.input.getName() : nls.localize('binaryFileEditor', "Binary File Viewer"); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts new file mode 100644 index 0000000000..1bccdd6a56 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -0,0 +1,258 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import errors = require('vs/base/common/errors'); +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import types = require('vs/base/common/types'); +import paths = require('vs/base/common/paths'); +import { Action } from 'vs/base/common/actions'; +import { VIEWLET_ID, TEXT_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; +import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; +import { EditorOptions, TextEditorOptions, IEditorCloseEvent } from 'vs/workbench/common/editor'; +import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; +import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; +import { ExplorerViewlet } from 'vs/workbench/parts/files/browser/explorerViewlet'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { FileOperationError, FileOperationResult, FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CancelAction } from 'vs/platform/message/common/message'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ScrollType } from 'vs/editor/common/editorCommon'; + +/** + * An implementation of editor for file system resources. + */ +export class TextFileEditor extends BaseTextEditor { + + public static ID = TEXT_FILE_EDITOR_ID; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IFileService private fileService: IFileService, + @IViewletService private viewletService: IViewletService, + @IInstantiationService instantiationService: IInstantiationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IStorageService storageService: IStorageService, + @IHistoryService private historyService: IHistoryService, + @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IThemeService themeService: IThemeService, + @IEditorGroupService editorGroupService: IEditorGroupService, + @IModeService modeService: IModeService, + @ITextFileService textFileService: ITextFileService, + ) { + super(TextFileEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, modeService, textFileService, editorGroupService); + + // Clear view state for deleted files + this.toUnbind.push(this.fileService.onFileChanges(e => this.onFilesChanged(e))); + + // React to editors closing to preserve view state + this.toUnbind.push(editorGroupService.getStacksModel().onWillCloseEditor(e => this.onWillCloseEditor(e))); + } + + private onFilesChanged(e: FileChangesEvent): void { + const deleted = e.getDeleted(); + if (deleted && deleted.length) { + this.clearTextEditorViewState(deleted.map(d => d.resource.toString())); + } + } + + private onWillCloseEditor(e: IEditorCloseEvent): void { + if (e.editor === this.input && this.position === this.editorGroupService.getStacksModel().positionOfGroup(e.group)) { + this.doSaveTextEditorViewState(this.input); + } + } + + public getTitle(): string { + return this.input ? this.input.getName() : nls.localize('textFileEditor', "Text File Editor"); + } + + public get input(): FileEditorInput { + return this._input as FileEditorInput; + } + + public setInput(input: FileEditorInput, options?: EditorOptions): TPromise { + const oldInput = this.input; + super.setInput(input, options); + + // Detect options + const forceOpen = options && options.forceOpen; + + // We have a current input in this editor and are about to either open a new editor or jump to a different + // selection inside the editor. Thus we store the current selection into the navigation history so that + // a user can navigate back to the exact position he left off. + if (oldInput) { + const selection = this.getControl().getSelection(); + if (selection) { + this.historyService.add(oldInput, { selection: { startLineNumber: selection.startLineNumber, startColumn: selection.startColumn } }); + } + } + + // Same Input + if (!forceOpen && input.matches(oldInput)) { + + // TextOptions (avoiding instanceof here for a reason, do not change!) + if (options && types.isFunction((options).apply)) { + (options).apply(this.getControl(), ScrollType.Smooth); + } + + return TPromise.as(null); + } + + // Remember view settings if input changes + this.doSaveTextEditorViewState(oldInput); + + // Different Input (Reload) + return input.resolve(true).then(resolvedModel => { + + // There is a special case where the text editor has to handle binary file editor input: if a binary file + // has been resolved and cached before, it maybe an actual instance of BinaryEditorModel. In this case our text + // editor has to open this model using the binary editor. We return early in this case. + if (resolvedModel instanceof BinaryEditorModel) { + return this.openAsBinary(input, options); + } + + // Check Model state + const textFileModel = resolvedModel; + + const hasInput = !!this.input; + const modelDisposed = textFileModel.isDisposed(); + const inputChanged = hasInput && this.input.getResource().toString() !== textFileModel.getResource().toString(); + if ( + !hasInput || // editor got hidden meanwhile + modelDisposed || // input got disposed meanwhile + inputChanged // a different input was set meanwhile + ) { + return null; + } + + // Editor + const textEditor = this.getControl(); + textEditor.setModel(textFileModel.textEditorModel); + + // Always restore View State if any associated + const editorViewState = this.loadTextEditorViewState(this.input.getResource().toString()); + if (editorViewState) { + textEditor.restoreViewState(editorViewState); + } + + // TextOptions (avoiding instanceof here for a reason, do not change!) + if (options && types.isFunction((options).apply)) { + (options).apply(textEditor, ScrollType.Immediate); + } + }, error => { + + // In case we tried to open a file inside the text editor and the response + // indicates that this is not a text file, reopen the file through the binary + // editor. + if ((error).fileOperationResult === FileOperationResult.FILE_IS_BINARY) { + return this.openAsBinary(input, options); + } + + // Similar, handle case where we were asked to open a folder in the text editor. + if ((error).fileOperationResult === FileOperationResult.FILE_IS_DIRECTORY && this.openAsFolder(input)) { + return; + } + + // Offer to create a file from the error if we have a file not found and the name is valid + if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND && paths.isValidBasename(paths.basename(input.getResource().fsPath))) { + return TPromise.wrapError(errors.create(toErrorMessage(error), { + actions: [ + new Action('workbench.files.action.createMissingFile', nls.localize('createFile', "Create File"), null, true, () => { + return this.fileService.updateContent(input.getResource(), '').then(() => { + + // Open + return this.editorService.openEditor({ + resource: input.getResource(), + options: { + pinned: true // new file gets pinned by default + } + }); + }); + }), + CancelAction + ] + })); + } + + // Otherwise make sure the error bubbles up + return TPromise.wrapError(error); + }); + } + + private openAsBinary(input: FileEditorInput, options: EditorOptions): void { + input.setForceOpenAsBinary(); + this.editorService.openEditor(input, options, this.position).done(null, errors.onUnexpectedError); + } + + private openAsFolder(input: FileEditorInput): boolean { + + // Since we cannot open a folder, we have to restore the previous input if any and close the editor + this.editorService.closeEditor(this.position, this.input).done(() => { + + // Best we can do is to reveal the folder in the explorer + if (this.contextService.isInsideWorkspace(input.getResource())) { + this.viewletService.openViewlet(VIEWLET_ID, true).done((viewlet: ExplorerViewlet) => { + return viewlet.getExplorerView().select(input.getResource(), true); + }, errors.onUnexpectedError); + } + }, errors.onUnexpectedError); + + return true; // in any case we handled it + } + + protected getAriaLabel(): string { + const input = this.input; + const inputName = input && input.getName(); + + let ariaLabel: string; + if (inputName) { + ariaLabel = nls.localize('fileEditorWithInputAriaLabel', "{0}. Text file editor.", inputName); + } else { + ariaLabel = nls.localize('fileEditorAriaLabel', "Text file editor."); + } + + return ariaLabel; + } + + public clearInput(): void { + + // Keep editor view state in settings to restore when coming back + this.doSaveTextEditorViewState(this.input); + + // Clear Model + this.getControl().setModel(null); + + // Pass to super + super.clearInput(); + } + + public shutdown(): void { + + // Save View State + this.doSaveTextEditorViewState(this.input); + + // Call Super + super.shutdown(); + } + + private doSaveTextEditorViewState(input: FileEditorInput): void { + if (input && !input.isDisposed()) { + this.saveTextEditorViewState(input.getResource().toString()); + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/explorerViewlet.ts b/src/vs/workbench/parts/files/browser/explorerViewlet.ts new file mode 100644 index 0000000000..b3a068ac10 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/explorerViewlet.ts @@ -0,0 +1,258 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/explorerviewlet'; +import { localize } from 'vs/nls'; +import { IActionRunner } from 'vs/base/common/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as DOM from 'vs/base/browser/dom'; +import { Builder } from 'vs/base/browser/builder'; +import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, OpenEditorsVisibleCondition } from 'vs/workbench/parts/files/common/files'; +import { PersistentViewsViewlet, IView, IViewletViewOptions } from 'vs/workbench/parts/views/browser/views'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { ActionRunner, FileViewletState } from 'vs/workbench/parts/files/browser/views/explorerViewer'; +import { ExplorerView, IExplorerViewOptions } from 'vs/workbench/parts/files/browser/views/explorerView'; +import { EmptyView } from 'vs/workbench/parts/files/browser/views/emptyView'; +import { OpenEditorsView } from 'vs/workbench/parts/files/browser/views/openEditorsView'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ViewsRegistry, ViewLocation, IViewDescriptor } from 'vs/workbench/parts/views/browser/viewsRegistry'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; + +export class ExplorerViewlet extends PersistentViewsViewlet { + + private static EXPLORER_VIEWS_STATE = 'workbench.explorer.views.state'; + + private viewletState: FileViewletState; + private viewletVisibleContextKey: IContextKey; + private openEditorsVisibleContextKey: IContextKey; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IStorageService protected storageService: IStorageService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IConfigurationService private configurationService: IConfigurationService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService + ) { + super(VIEWLET_ID, ViewLocation.Explorer, ExplorerViewlet.EXPLORER_VIEWS_STATE, true, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); + + this.viewletState = new FileViewletState(); + this.viewletVisibleContextKey = ExplorerViewletVisibleContext.bindTo(contextKeyService); + this.openEditorsVisibleContextKey = OpenEditorsVisibleContext.bindTo(contextKeyService); + + this.registerViews(); + this.onConfigurationUpdated(); + this._register(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated())); + this._register(this.contextService.onDidChangeWorkspaceName(e => this.updateTitleArea())); + } + + public create(parent: Builder): TPromise { + return super.create(parent).then(() => DOM.addClass(this.viewletContainer, 'explorer-viewlet')); + } + + private registerViews(): void { + let viewDescriptors = []; + + viewDescriptors.push(this.createOpenEditorsViewDescriptor()); + + if (this.contextService.hasWorkspace()) { + viewDescriptors.push(this.createExplorerViewDescriptor()); + } else { + viewDescriptors.push(this.createEmptyViewDescriptor()); + } + + ViewsRegistry.registerViews(viewDescriptors); + } + + private createOpenEditorsViewDescriptor(): IViewDescriptor { + return { + id: OpenEditorsView.ID, + name: OpenEditorsView.NAME, + location: ViewLocation.Explorer, + ctor: OpenEditorsView, + order: 0, + when: OpenEditorsVisibleCondition, + canToggleVisibility: true + }; + } + + private createEmptyViewDescriptor(): IViewDescriptor { + return { + id: EmptyView.ID, + name: EmptyView.NAME, + location: ViewLocation.Explorer, + ctor: EmptyView, + order: 1, + canToggleVisibility: false + }; + } + + private createExplorerViewDescriptor(): IViewDescriptor { + return { + id: ExplorerView.ID, + name: localize('folders', "Folders"), + location: ViewLocation.Explorer, + ctor: ExplorerView, + order: 1, + canToggleVisibility: false + }; + } + + private onConfigurationUpdated(): void { + this.openEditorsVisibleContextKey.set(!this.contextService.hasWorkspace() || (this.configurationService.getConfiguration()).explorer.openEditors.visible !== 0); + } + + protected createView(viewDescriptor: IViewDescriptor, initialSize: number, options: IViewletViewOptions): IView { + if (viewDescriptor.id === ExplorerView.ID) { + // Create a delegating editor service for the explorer to be able to delay the refresh in the opened + // editors view above. This is a workaround for being able to double click on a file to make it pinned + // without causing the animation in the opened editors view to kick in and change scroll position. + // We try to be smart and only use the delay if we recognize that the user action is likely to cause + // a new entry in the opened editors view. + const delegatingEditorService = this.instantiationService.createInstance(DelegatingWorkbenchEditorService); + delegatingEditorService.setEditorOpenHandler((input: EditorInput, options?: EditorOptions, arg3?: any) => { + let openEditorsView = this.getOpenEditorsView(); + if (openEditorsView) { + let delay = 0; + + const config = this.configurationService.getConfiguration(); + // No need to delay if preview is disabled + const delayEditorOpeningInOpenedEditors = !!config.workbench.editor.enablePreview; + + if (delayEditorOpeningInOpenedEditors && (arg3 === false /* not side by side */ || typeof arg3 !== 'number' /* no explicit position */)) { + const activeGroup = this.editorGroupService.getStacksModel().activeGroup; + if (!activeGroup || !activeGroup.previewEditor) { + delay = 250; // a new editor entry is likely because there is either no group or no preview in group + } + } + + openEditorsView.setStructuralRefreshDelay(delay); + } + + const onSuccessOrError = (editor?: BaseEditor) => { + let openEditorsView = this.getOpenEditorsView(); + if (openEditorsView) { + openEditorsView.setStructuralRefreshDelay(0); + } + + return editor; + }; + + return this.editorService.openEditor(input, options, arg3).then(onSuccessOrError, onSuccessOrError); + }); + + const explorerInstantiator = this.instantiationService.createChild(new ServiceCollection([IWorkbenchEditorService, delegatingEditorService])); + return explorerInstantiator.createInstance(ExplorerView, initialSize, { ...options, viewletState: this.viewletState }); + } + return super.createView(viewDescriptor, initialSize, options); + } + + public getExplorerView(): ExplorerView { + return this.getView(ExplorerView.ID); + } + + public getOpenEditorsView(): OpenEditorsView { + return this.getView(OpenEditorsView.ID); + } + + public getEmptyView(): EmptyView { + return this.getView(EmptyView.ID); + } + + public setVisible(visible: boolean): TPromise { + this.viewletVisibleContextKey.set(visible); + return super.setVisible(visible); + } + + public focus(): void { + const hasOpenedEditors = !!this.editorGroupService.getStacksModel().activeGroup; + + let openEditorsView = this.getOpenEditorsView(); + if (this.lastFocusedView && this.lastFocusedView.isExpanded() && this.hasSelectionOrFocus(this.lastFocusedView)) { + if (this.lastFocusedView !== openEditorsView || hasOpenedEditors) { + this.lastFocusedView.focusBody(); + return; + } + } + + if (this.hasSelectionOrFocus(openEditorsView) && hasOpenedEditors) { + return openEditorsView.focusBody(); + } + + let explorerView = this.getExplorerView(); + if (this.hasSelectionOrFocus(explorerView)) { + return explorerView.focusBody(); + } + + if (openEditorsView && openEditorsView.isExpanded() && hasOpenedEditors) { + return openEditorsView.focusBody(); // we have entries in the opened editors view to focus on + } + + if (explorerView && explorerView.isExpanded()) { + return explorerView.focusBody(); + } + + let emptyView = this.getEmptyView(); + if (emptyView && emptyView.isExpanded()) { + return emptyView.focusBody(); + } + + super.focus(); + } + + private hasSelectionOrFocus(view: IView): boolean { + if (!view) { + return false; + } + + if (!view.isExpanded()) { + return false; + } + + if (view instanceof ExplorerView || view instanceof OpenEditorsView) { + const viewer = view.getViewer(); + if (!viewer) { + return false; + } + + return !!viewer.getFocus() || (viewer.getSelection() && viewer.getSelection().length > 0); + + } + + return false; + } + + public getActionRunner(): IActionRunner { + if (!this.actionRunner) { + this.actionRunner = new ActionRunner(this.viewletState); + } + return this.actionRunner; + } + + public getViewletState(): FileViewletState { + return this.viewletState; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts new file mode 100644 index 0000000000..d65beda13f --- /dev/null +++ b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts @@ -0,0 +1,342 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import { Registry } from 'vs/platform/registry/common/platform'; +import { Action, IAction } from 'vs/base/common/actions'; +import { isMacintosh } from 'vs/base/common/platform'; +import { ActionItem, BaseActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions, ActionBarContributor } from 'vs/workbench/browser/actions'; +import { GlobalNewUntitledFileAction, SaveFileAsAction, OpenFileAction, ShowOpenedFileInNewWindow, CopyPathAction, GlobalCopyPathAction, RevealInOSAction, GlobalRevealInOSAction, pasteIntoFocusedFilesExplorerViewItem, FocusOpenEditorsView, FocusFilesExplorer, GlobalCompareResourcesAction, GlobalNewFileAction, GlobalNewFolderAction, RevertFileAction, SaveFilesAction, SaveAllAction, SaveFileAction, MoveFileToTrashAction, TriggerRenameFileAction, PasteFileAction, CopyFileAction, SelectResourceForCompareAction, CompareResourcesAction, NewFolderAction, NewFileAction, OpenToSideAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithSavedAction } from 'vs/workbench/parts/files/browser/fileActions'; +import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/parts/files/browser/saveErrorHandler'; +import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { FileStat, Model } from 'vs/workbench/parts/files/common/explorerModel'; +import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { OpenFolderAction, OpenFileFolderAction, AddRootFolderAction, RemoveRootFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { copyFocusedFilesExplorerViewItem, revealInOSFocusedFilesExplorerItem, openFocusedExplorerItemSideBySideCommand, copyPathOfFocusedExplorerItem, copyPathCommand, revealInExplorerCommand, revealInOSCommand, openFolderPickerCommand, openWindowCommand, openFileInNewWindowCommand, deleteFocusedFilesExplorerViewItemCommand, moveFocusedFilesExplorerViewItemToTrashCommand, renameFocusedFilesExplorerViewItemCommand } from 'vs/workbench/parts/files/browser/fileCommands'; +import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { explorerItemToFileResource, ExplorerFocusCondition, FilesExplorerFocusCondition } from 'vs/workbench/parts/files/common/files'; + +class FilesViewerActionContributor extends ActionBarContributor { + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IKeybindingService private keybindingService: IKeybindingService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + super(); + } + + public hasSecondaryActions(context: any): boolean { + const element = context.element; + + // Contribute only on Stat Objects (File Explorer) + return element instanceof FileStat || element instanceof Model; + } + + public getSecondaryActions(context: any): IAction[] { + const stat = (context.element); + const tree = context.viewer; + const actions: IAction[] = []; + let separateOpen = false; + if (stat instanceof Model) { + return [this.instantiationService.createInstance(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL)]; + } + + // Open side by side + if (!stat.isDirectory) { + actions.push(this.instantiationService.createInstance(OpenToSideAction, tree, stat.resource, false)); + separateOpen = true; + } + + if (separateOpen) { + actions.push(new Separator(null, 50)); + } + + // Directory Actions + if (stat.isDirectory && stat.exists) { + + // New File + actions.push(this.instantiationService.createInstance(NewFileAction, tree, stat)); + + // New Folder + actions.push(this.instantiationService.createInstance(NewFolderAction, tree, stat)); + + actions.push(new Separator(null, 50)); + } + + // Compare Files (of same extension) + else if (!stat.isDirectory) { + + // Run Compare + const runCompareAction = this.instantiationService.createInstance(CompareResourcesAction, stat.resource, tree); + if (runCompareAction._isEnabled()) { + actions.push(runCompareAction); + } + + // Select for Compare + actions.push(this.instantiationService.createInstance(SelectResourceForCompareAction, stat.resource, tree)); + + actions.push(new Separator(null, 100)); + } + + if (stat.isRoot && this.environmentService.appQuality !== 'stable') { + let action: Action = this.instantiationService.createInstance(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL); + action.order = 52; + actions.push(action); + if (this.contextService.getWorkspace().roots.length > 1) { + action = this.instantiationService.createInstance(RemoveRootFolderAction, stat.resource, RemoveRootFolderAction.ID, RemoveRootFolderAction.LABEL); + action.order = 53; + actions.push(action); + } + actions.push(new Separator(null, 54)); + } + + // Copy File/Folder + if (!stat.isRoot) { + actions.push(this.instantiationService.createInstance(CopyFileAction, tree, stat)); + } + + // Paste File/Folder + if (stat.isDirectory) { + actions.push(this.instantiationService.createInstance(PasteFileAction, tree, stat)); + } + + // Rename File/Folder + if (!stat.isRoot) { + actions.push(new Separator(null, 150)); + actions.push(this.instantiationService.createInstance(TriggerRenameFileAction, tree, stat)); + // Delete File/Folder + actions.push(this.instantiationService.createInstance(MoveFileToTrashAction, tree, stat)); + } + + // Set Order + let curOrder = 10; + for (let i = 0; i < actions.length; i++) { + const action = actions[i]; + if (!action.order) { + curOrder += 10; + action.order = curOrder; + } else { + curOrder = action.order; + } + } + + return actions; + } + + public getActionItem(context: any, action: Action): BaseActionItem { + if (context && context.element instanceof FileStat) { + + // Any other item with keybinding + const keybinding = this.keybindingService.lookupKeybinding(action.id); + if (keybinding) { + return new ActionItem(context, action, { label: true, keybinding: keybinding.getLabel() }); + } + } + + return null; + } +} + +class ExplorerViewersActionContributor extends ActionBarContributor { + + constructor( @IInstantiationService private instantiationService: IInstantiationService) { + super(); + } + + public hasSecondaryActions(context: any): boolean { + const element = context.element; + + // Contribute only on Files (File Explorer and Open Files Viewer) + return !!explorerItemToFileResource(element); + } + + public getSecondaryActions(context: any): IAction[] { + const actions: IAction[] = []; + + if (this.hasSecondaryActions(context)) { + const fileResource = explorerItemToFileResource(context.element); + const resource = fileResource.resource; + + // Reveal file in OS native explorer + actions.push(this.instantiationService.createInstance(RevealInOSAction, resource)); + + // Copy Path + actions.push(this.instantiationService.createInstance(CopyPathAction, resource)); + } + + return actions; + } +} + +// Contribute to Viewers that show Files +const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); +actionBarRegistry.registerActionBarContributor(Scope.VIEWER, FilesViewerActionContributor); +actionBarRegistry.registerActionBarContributor(Scope.VIEWER, ExplorerViewersActionContributor); + +// Contribute Global Actions +const category = nls.localize('filesCategory', "Files"); + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalCopyPathAction, GlobalCopyPathAction.ID, GlobalCopyPathAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_P) }), 'Files: Copy Path of Active File', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(SaveFileAction, SaveFileAction.ID, SaveFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_S }), 'Files: Save', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(SaveAllAction, SaveAllAction.ID, SaveAllAction.LABEL, { primary: void 0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_S }, win: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_S) } }), 'Files: Save All', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(SaveFilesAction, SaveFilesAction.ID, null /* only for programmatic trigger */), null); +registry.registerWorkbenchAction(new SyncActionDescriptor(RevertFileAction, RevertFileAction.ID, RevertFileAction.LABEL), 'Files: Revert File', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewFileAction, GlobalNewFileAction.ID, GlobalNewFileAction.LABEL), 'Files: New File', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewFolderAction, GlobalNewFolderAction.ID, GlobalNewFolderAction.LABEL), 'Files: New Folder', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalCompareResourcesAction, GlobalCompareResourcesAction.ID, GlobalCompareResourcesAction.LABEL), 'Files: Compare Active File With...', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(FocusOpenEditorsView, FocusOpenEditorsView.ID, FocusOpenEditorsView.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_E) }), 'Files: Focus on Open Editors View', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFilesExplorer, FocusFilesExplorer.ID, FocusFilesExplorer.LABEL), 'Files: Focus on Files Explorer', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowActiveFileInExplorer, ShowActiveFileInExplorer.ID, ShowActiveFileInExplorer.LABEL), 'Files: Reveal Active File in Side Bar', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(CollapseExplorerView, CollapseExplorerView.ID, CollapseExplorerView.LABEL), 'Files: Collapse Folders in Explorer', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(RefreshExplorerView, RefreshExplorerView.ID, RefreshExplorerView.LABEL), 'Files: Refresh Explorer', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(SaveFileAsAction, SaveFileAsAction.ID, SaveFileAsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_S }), 'Files: Save As...', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewUntitledFileAction, GlobalNewUntitledFileAction.ID, GlobalNewUntitledFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_N }), 'Files: New Untitled File', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRevealInOSAction, GlobalRevealInOSAction.ID, GlobalRevealInOSAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_R) }), 'Files: Reveal Active File', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowOpenedFileInNewWindow, ShowOpenedFileInNewWindow.ID, ShowOpenedFileInNewWindow.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_O) }), 'Files: Open Active File in New Window', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(CompareWithSavedAction, CompareWithSavedAction.ID, CompareWithSavedAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_D) }), 'Files: Compare Active File with Saved', category); + +if (isMacintosh) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'Files: Open...', category); +} else { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'Files: Open File...', category); + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'Files: Open Folder...', category); +} + +// Commands +CommandsRegistry.registerCommand('_files.pickFolderAndOpen', openFolderPickerCommand); +CommandsRegistry.registerCommand('_files.windowOpen', openWindowCommand); +CommandsRegistry.registerCommand('workbench.action.files.openFileInNewWindow', openFileInNewWindowCommand); + +const explorerCommandsWeightBonus = 10; // give our commands a little bit more weight over other default list/tree commands + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'explorer.openToSide', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + when: ExplorerFocusCondition, + primary: KeyMod.CtrlCmd | KeyCode.Enter, + mac: { + primary: KeyMod.WinCtrl | KeyCode.Enter + }, + handler: openFocusedExplorerItemSideBySideCommand +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'renameFile', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + when: FilesExplorerFocusCondition, + primary: KeyCode.F2, + mac: { + primary: KeyCode.Enter + }, + handler: renameFocusedFilesExplorerViewItemCommand +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'moveFileToTrash', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + when: FilesExplorerFocusCondition, + primary: KeyCode.Delete, + mac: { + primary: KeyMod.CtrlCmd | KeyCode.Backspace + }, + handler: moveFocusedFilesExplorerViewItemToTrashCommand +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'deleteFile', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + when: FilesExplorerFocusCondition, + primary: KeyMod.Shift | KeyCode.Delete, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Backspace + }, + handler: deleteFocusedFilesExplorerViewItemCommand +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'filesExplorer.copy', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + when: FilesExplorerFocusCondition, + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + handler: copyFocusedFilesExplorerViewItem +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'filesExplorer.paste', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + when: FilesExplorerFocusCondition, + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + handler: pasteIntoFocusedFilesExplorerViewItem +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'copyFilePath', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + when: ExplorerFocusCondition, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C, + win: { + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_C + }, + handler: copyPathOfFocusedExplorerItem +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'revealFileInOS', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + when: ExplorerFocusCondition, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R, + win: { + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_R + }, + handler: revealInOSFocusedFilesExplorerItem +}); + +// Editor Title Context Menu +appendEditorTitleContextMenuItem('_workbench.action.files.revealInOS', RevealInOSAction.LABEL, revealInOSCommand); +appendEditorTitleContextMenuItem('_workbench.action.files.copyPath', CopyPathAction.LABEL, copyPathCommand); +appendEditorTitleContextMenuItem('_workbench.action.files.revealInExplorer', nls.localize('revealInSideBar', "Reveal in Side Bar"), revealInExplorerCommand); + +function appendEditorTitleContextMenuItem(id: string, title: string, command: ICommandHandler): void { + + // Command + CommandsRegistry.registerCommand(id, command); + + // Menu + MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { + command: { id, title }, + when: ContextKeyExpr.equals('resourceScheme', 'file'), + group: '2_files' + }); +} + +// Editor Title Menu for Conflict Resolution +appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite disk contents"), 'save-conflict-action-accept-changes', -10, acceptLocalChangesCommand); +appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to content on disk"), 'save-conflict-action-revert-changes', -9, revertLocalChangesCommand); + +function appendSaveConflictEditorTitleAction(id: string, title: string, iconClass: string, order: number, command: ICommandHandler): void { + + // Command + CommandsRegistry.registerCommand(id, command); + + // Action + MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { id, title, iconClass }, + when: ContextKeyExpr.equals(CONFLICT_RESOLUTION_CONTEXT, true), + group: 'navigation', + order + }); +} \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts new file mode 100644 index 0000000000..1c8856d947 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -0,0 +1,2135 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/fileactions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; +import { sequence, ITask } from 'vs/base/common/async'; +import paths = require('vs/base/common/paths'); +import URI from 'vs/base/common/uri'; +import errors = require('vs/base/common/errors'); +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import strings = require('vs/base/common/strings'); +import { EventType as CommonEventType } from 'vs/base/common/events'; +import severity from 'vs/base/common/severity'; +import diagnostics = require('vs/base/common/diagnostics'); +import { Action, IAction } from 'vs/base/common/actions'; +import { MessageType, IInputValidator } from 'vs/base/browser/ui/inputbox/inputBox'; +import { ITree, IHighlightEvent, IActionProvider } from 'vs/base/parts/tree/browser/tree'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { VIEWLET_ID } from 'vs/workbench/parts/files/common/files'; +import labels = require('vs/base/common/labels'); +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IFileService, IFileStat, FileChangeType } from 'vs/platform/files/common/files'; +import { toResource, IEditorIdentifier, EditorInput } from 'vs/workbench/common/editor'; +import { FileStat, Model, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; +import { ExplorerView } from 'vs/workbench/parts/files/browser/views/explorerView'; +import { ExplorerViewlet } from 'vs/workbench/parts/files/browser/explorerViewlet'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { CollapseAction } from 'vs/workbench/browser/viewlet'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IQuickOpenService, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +// {{SQL CARBON EDIT}} +import { Position, IResourceInput, IEditorInput, IUntitledResourceInput, IEditorOptions } from 'vs/platform/editor/common/editor'; +import { IInstantiationService, IConstructorSignature2, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, IMessageWithAction, IConfirmation, Severity, CancelAction } from 'vs/platform/message/common/message'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; +import { IEditorViewState, IModel } from 'vs/editor/common/editorCommon'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { withFocusedFilesExplorer, revealInOSCommand, revealInExplorerCommand, copyPathCommand } from 'vs/workbench/parts/files/browser/fileCommands'; +import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IMode } from 'vs/editor/common/modes'; + +export interface IEditableData { + action: IAction; + validator: IInputValidator; +} + +export interface IFileViewletState { + actionProvider: IActionProvider; + getEditableData(stat: IFileStat): IEditableData; + setEditable(stat: IFileStat, editableData: IEditableData): void; + clearEditable(stat: IFileStat): void; +} + +export class BaseErrorReportingAction extends Action { + + constructor( + id: string, + label: string, + private _messageService: IMessageService + ) { + super(id, label); + } + + public get messageService() { + return this._messageService; + } + + protected onError(error: any): void { + if (error.message === 'string') { + error = error.message; + } + + this._messageService.show(Severity.Error, toErrorMessage(error, false)); + } + + protected onErrorWithRetry(error: any, retry: () => TPromise, extraAction?: Action): void { + const actions = [ + new Action(this.id, nls.localize('retry', "Retry"), null, true, () => retry()), + CancelAction + ]; + + if (extraAction) { + actions.unshift(extraAction); + } + + const errorWithRetry: IMessageWithAction = { + actions, + message: toErrorMessage(error, false) + }; + + this._messageService.show(Severity.Error, errorWithRetry); + } +} + +export class BaseFileAction extends BaseErrorReportingAction { + private _element: FileStat; + + constructor( + id: string, + label: string, + @IFileService private _fileService: IFileService, + @IMessageService _messageService: IMessageService, + @ITextFileService private _textFileService: ITextFileService + ) { + super(id, label, _messageService); + + this.enabled = false; + } + + public get fileService() { + return this._fileService; + } + + public get textFileService() { + return this._textFileService; + } + + public get element() { + return this._element; + } + + public set element(element: FileStat) { + this._element = element; + } + + _isEnabled(): boolean { + return true; + } + + _updateEnablement(): void { + this.enabled = !!(this._fileService && this._isEnabled()); + } +} + +export class TriggerRenameFileAction extends BaseFileAction { + + public static ID = 'renameFile'; + + private tree: ITree; + private renameAction: BaseRenameAction; + + constructor( + tree: ITree, + element: FileStat, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(TriggerRenameFileAction.ID, nls.localize('rename', "Rename"), fileService, messageService, textFileService); + + this.tree = tree; + this.element = element; + this.renameAction = instantiationService.createInstance(RenameFileAction, element); + this._updateEnablement(); + } + + public validateFileName(parent: IFileStat, name: string): string { + return this.renameAction.validateFileName(this.element.parent, name); + } + + public run(context?: any): TPromise { + if (!context) { + return TPromise.wrapError(new Error('No context provided to BaseEnableFileRenameAction.')); + } + + const viewletState = context.viewletState; + if (!viewletState) { + return TPromise.wrapError(new Error('Invalid viewlet state provided to BaseEnableFileRenameAction.')); + } + + const stat = context.stat; + if (!stat) { + return TPromise.wrapError(new Error('Invalid stat provided to BaseEnableFileRenameAction.')); + } + + viewletState.setEditable(stat, { + action: this.renameAction, + validator: (value) => { + const message = this.validateFileName(this.element.parent, value); + + if (!message) { + return null; + } + + return { + content: message, + formatContent: true, + type: MessageType.ERROR + }; + } + }); + + this.tree.refresh(stat, false).then(() => { + this.tree.setHighlight(stat); + + const unbind = this.tree.addListener(CommonEventType.HIGHLIGHT, (e: IHighlightEvent) => { + if (!e.highlight) { + viewletState.clearEditable(stat); + this.tree.refresh(stat).done(null, errors.onUnexpectedError); + unbind.dispose(); + } + }); + }).done(null, errors.onUnexpectedError); + + return void 0; + } +} + +export abstract class BaseRenameAction extends BaseFileAction { + + constructor( + id: string, + label: string, + element: FileStat, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService + ) { + super(id, label, fileService, messageService, textFileService); + + this.element = element; + } + + public run(context?: any): TPromise { + if (!context) { + return TPromise.wrapError(new Error('No context provided to BaseRenameFileAction.')); + } + + let name = context.value; + if (!name) { + return TPromise.wrapError(new Error('No new name provided to BaseRenameFileAction.')); + } + + // Automatically trim whitespaces and trailing dots to produce nice file names + name = getWellFormedFileName(name); + const existingName = getWellFormedFileName(this.element.name); + + // Return early if name is invalid or didn't change + if (name === existingName || this.validateFileName(this.element.parent, name)) { + return TPromise.as(null); + } + + // Call function and Emit Event through viewer + const promise = this.runAction(name).then(null, (error: any) => { + this.onError(error); + }); + + return promise; + } + + public validateFileName(parent: IFileStat, name: string): string { + let source = this.element.name; + let target = name; + + if (!isLinux) { // allow rename of same file also when case differs (e.g. Game.js => game.js) + source = source.toLowerCase(); + target = target.toLowerCase(); + } + + if (getWellFormedFileName(source) === getWellFormedFileName(target)) { + return null; + } + + return validateFileName(parent, name, false); + } + + public abstract runAction(newName: string): TPromise; +} + +class RenameFileAction extends BaseRenameAction { + + public static ID = 'workbench.files.action.renameFile'; + + constructor( + element: FileStat, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService, + @IBackupFileService private backupFileService: IBackupFileService + ) { + super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, fileService, messageService, textFileService); + + this._updateEnablement(); + } + + public runAction(newName: string): TPromise { + + const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, this.element.resource.fsPath, !isLinux /* ignorecase */)); + const dirtyRenamed: URI[] = []; + return TPromise.join(dirty.map(d => { + + const targetPath = paths.join(this.element.parent.resource.fsPath, newName); + let renamed: URI; + + // If the dirty file itself got moved, just reparent it to the target folder + if (paths.isEqual(this.element.resource.fsPath, d.fsPath)) { + renamed = URI.file(targetPath); + } + + // Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example: + else { + renamed = URI.file(paths.join(targetPath, d.fsPath.substr(this.element.resource.fsPath.length + 1))); + } + + dirtyRenamed.push(renamed); + + const model = this.textFileService.models.get(d); + + return this.backupFileService.backupResource(renamed, model.getValue(), model.getVersionId()); + })) + + // 2. soft revert all dirty since we have backed up their contents + .then(() => this.textFileService.revertAll(dirty, { soft: true /* do not attempt to load content from disk */ })) + + // 3.) run the rename operation + .then(() => this.fileService.rename(this.element.resource, newName).then(null, (error: Error) => { + return TPromise.join(dirtyRenamed.map(d => this.backupFileService.discardResourceBackup(d))).then(() => { + this.onErrorWithRetry(error, () => this.runAction(newName)); + }); + })) + + // 4.) resolve those that were dirty to load their previous dirty contents from disk + .then(() => { + return TPromise.join(dirtyRenamed.map(t => this.textFileService.models.loadOrCreate(t))); + }); + } +} + +/* Base New File/Folder Action */ +export class BaseNewAction extends BaseFileAction { + private presetFolder: FileStat; + private tree: ITree; + private isFile: boolean; + private renameAction: BaseRenameAction; + + constructor( + id: string, + label: string, + tree: ITree, + isFile: boolean, + editableAction: BaseRenameAction, + element: FileStat, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService + ) { + super(id, label, fileService, messageService, textFileService); + + if (element) { + this.presetFolder = element.isDirectory ? element : element.parent; + } + + this.tree = tree; + this.isFile = isFile; + this.renameAction = editableAction; + } + + public run(context?: any): TPromise { + if (!context) { + return TPromise.wrapError(new Error('No context provided to BaseNewAction.')); + } + + const viewletState = context.viewletState; + if (!viewletState) { + return TPromise.wrapError(new Error('Invalid viewlet state provided to BaseNewAction.')); + } + + let folder = this.presetFolder; + if (!folder) { + const focus = this.tree.getFocus(); + if (focus) { + folder = focus.isDirectory ? focus : focus.parent; + } else { + const input: FileStat | Model = this.tree.getInput(); + folder = input instanceof Model ? input.roots[0] : input; + } + } + + if (!folder) { + return TPromise.wrapError(new Error('Invalid parent folder to create.')); + } + + return this.tree.reveal(folder, 0.5).then(() => { + return this.tree.expand(folder).then(() => { + const stat = NewStatPlaceholder.addNewStatPlaceholder(folder, !this.isFile); + + this.renameAction.element = stat; + + viewletState.setEditable(stat, { + action: this.renameAction, + validator: (value) => { + const message = this.renameAction.validateFileName(folder, value); + + if (!message) { + return null; + } + + return { + content: message, + formatContent: true, + type: MessageType.ERROR + }; + } + }); + + return this.tree.refresh(folder).then(() => { + return this.tree.expand(folder).then(() => { + return this.tree.reveal(stat, 0.5).then(() => { + this.tree.setHighlight(stat); + + const unbind = this.tree.addListener(CommonEventType.HIGHLIGHT, (e: IHighlightEvent) => { + if (!e.highlight) { + stat.destroy(); + this.tree.refresh(folder).done(null, errors.onUnexpectedError); + unbind.dispose(); + } + }); + }); + }); + }); + }); + }); + } +} + +/* New File */ +export class NewFileAction extends BaseNewAction { + + constructor( + tree: ITree, + element: FileStat, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super('explorer.newFile', nls.localize('newFile', "New File"), tree, true, instantiationService.createInstance(CreateFileAction, element), null, fileService, messageService, textFileService); + + this.class = 'explorer-action new-file'; + this._updateEnablement(); + } +} + +/* New Folder */ +export class NewFolderAction extends BaseNewAction { + + constructor( + tree: ITree, + element: FileStat, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super('explorer.newFolder', nls.localize('newFolder', "New Folder"), tree, false, instantiationService.createInstance(CreateFolderAction, element), null, fileService, messageService, textFileService); + + this.class = 'explorer-action new-folder'; + this._updateEnablement(); + } +} + +export abstract class BaseGlobalNewAction extends Action { + private toDispose: Action; + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService, + @IInstantiationService private instantiationService: IInstantiationService, + @IMessageService private messageService: IMessageService + ) { + super(id, label); + } + + public run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true).then((viewlet) => { + return TPromise.timeout(100).then(() => { // use a timeout to prevent the explorer from revealing the active file + viewlet.focus(); + + const explorer = viewlet; + const explorerView = explorer.getExplorerView(); + + // Not having a folder opened + if (!explorerView) { + return this.messageService.show(Severity.Info, nls.localize('openFolderFirst', "Open a folder first to create files or folders within.")); + } + + if (!explorerView.isExpanded()) { + explorerView.expand(); + } + + const action = this.toDispose = this.instantiationService.createInstance(this.getAction(), explorerView.getViewer(), null); + + return explorer.getActionRunner().run(action); + }); + }); + } + + protected abstract getAction(): IConstructorSignature2; + + public dispose(): void { + super.dispose(); + + if (this.toDispose) { + this.toDispose.dispose(); + this.toDispose = null; + } + } +} + +/* Create new file from anywhere: Open untitled */ +export class GlobalNewUntitledFileAction extends Action { + public static ID = 'workbench.action.files.newUntitledFile'; + public static LABEL = nls.localize('newUntitledFile', "New Untitled File"); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + // {{SQL CARBON EDIT}} + @IUntitledEditorService private untitledEditorService: IUntitledEditorService + ) { + super(id, label); + } + + public run(): TPromise { + // {{SQL CARBON EDIT}} + const input = this.untitledEditorService.createOrGet(undefined, 'sql'); + return this.editorService.openEditor(input, { pinned: true } as IEditorOptions); // untitled are always pinned + } +} + +/* Create new file from anywhere */ +export class GlobalNewFileAction extends BaseGlobalNewAction { + public static ID = 'explorer.newFile'; + public static LABEL = nls.localize('newFile', "New File"); + + protected getAction(): IConstructorSignature2 { + return NewFileAction; + } +} + +/* Create new folder from anywhere */ +export class GlobalNewFolderAction extends BaseGlobalNewAction { + public static ID = 'explorer.newFolder'; + public static LABEL = nls.localize('newFolder', "New Folder"); + + protected getAction(): IConstructorSignature2 { + return NewFolderAction; + } +} + +/* Create New File/Folder (only used internally by explorerViewer) */ +export abstract class BaseCreateAction extends BaseRenameAction { + + public validateFileName(parent: IFileStat, name: string): string { + if (this.element instanceof NewStatPlaceholder) { + return validateFileName(parent, name, false); + } + + return super.validateFileName(parent, name); + } +} + +/* Create New File (only used internally by explorerViewer) */ +export class CreateFileAction extends BaseCreateAction { + + public static ID = 'workbench.files.action.createFileFromExplorer'; + public static LABEL = nls.localize('createNewFile', "New File"); + + constructor( + element: FileStat, + @IFileService fileService: IFileService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService + ) { + super(CreateFileAction.ID, CreateFileAction.LABEL, element, fileService, messageService, textFileService); + + this._updateEnablement(); + } + + public runAction(fileName: string): TPromise { + return this.fileService.createFile(URI.file(paths.join(this.element.parent.resource.fsPath, fileName))).then(stat => { + return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); + }, (error) => { + this.onErrorWithRetry(error, () => this.runAction(fileName)); + }); + } +} + +/* Create New Folder (only used internally by explorerViewer) */ +export class CreateFolderAction extends BaseCreateAction { + + public static ID = 'workbench.files.action.createFolderFromExplorer'; + public static LABEL = nls.localize('createNewFolder', "New Folder"); + + constructor( + element: FileStat, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService + ) { + super(CreateFolderAction.ID, CreateFolderAction.LABEL, null, fileService, messageService, textFileService); + + this._updateEnablement(); + } + + public runAction(fileName: string): TPromise { + return this.fileService.createFolder(URI.file(paths.join(this.element.parent.resource.fsPath, fileName))).then(null, (error) => { + this.onErrorWithRetry(error, () => this.runAction(fileName)); + }); + } +} + +export class BaseDeleteFileAction extends BaseFileAction { + private tree: ITree; + private useTrash: boolean; + private skipConfirm: boolean; + + constructor( + id: string, + label: string, + tree: ITree, + element: FileStat, + useTrash: boolean, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService + ) { + super(id, label, fileService, messageService, textFileService); + + this.tree = tree; + this.element = element; + this.useTrash = useTrash && !paths.isUNC(element.resource.fsPath); // on UNC shares there is no trash + + this._updateEnablement(); + } + + public run(context?: any): TPromise { + + // Remove highlight + if (this.tree) { + this.tree.clearHighlight(); + } + + // Read context + if (context) { + if (context.event) { + const bypassTrash = (isMacintosh && context.event.altKey) || (!isMacintosh && context.event.shiftKey); + if (bypassTrash) { + this.useTrash = false; + } + } else if (typeof context.useTrash === 'boolean') { + this.useTrash = context.useTrash; + } + } + + let primaryButton: string; + if (this.useTrash) { + primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash"); + } else { + primaryButton = nls.localize({ key: 'deleteButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete"); + } + + // Handle dirty + let revertPromise: TPromise = TPromise.as(null); + const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, this.element.resource.fsPath, !isLinux /* ignorecase */)); + if (dirty.length) { + let message: string; + if (this.element.isDirectory) { + if (dirty.length === 1) { + message = nls.localize('dirtyMessageFolderOneDelete', "You are deleting a folder with unsaved changes in 1 file. Do you want to continue?"); + } else { + message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder with unsaved changes in {0} files. Do you want to continue?", dirty.length); + } + } else { + message = nls.localize('dirtyMessageFileDelete', "You are deleting a file with unsaved changes. Do you want to continue?"); + } + + const res = this.messageService.confirm({ + message, + type: 'warning', + detail: nls.localize('dirtyWarning', "Your changes will be lost if you don't save them."), + primaryButton + }); + + if (!res) { + return TPromise.as(null); + } + + this.skipConfirm = true; // since we already asked for confirmation + revertPromise = this.textFileService.revertAll(dirty); + } + + // Check if file is dirty in editor and save it to avoid data loss + return revertPromise.then(() => { + + // Ask for Confirm + if (!this.skipConfirm) { + let confirm: IConfirmation; + if (this.useTrash) { + confirm = { + message: this.element.isDirectory ? nls.localize('confirmMoveTrashMessageFolder', "Are you sure you want to delete '{0}' and its contents?", this.element.name) : nls.localize('confirmMoveTrashMessageFile', "Are you sure you want to delete '{0}'?", this.element.name), + detail: isWindows ? nls.localize('undoBin', "You can restore from the recycle bin.") : nls.localize('undoTrash', "You can restore from the trash."), + primaryButton, + type: 'question' + }; + } else { + confirm = { + message: this.element.isDirectory ? nls.localize('confirmDeleteMessageFolder', "Are you sure you want to permanently delete '{0}' and its contents?", this.element.name) : nls.localize('confirmDeleteMessageFile', "Are you sure you want to permanently delete '{0}'?", this.element.name), + detail: nls.localize('irreversible', "This action is irreversible!"), + primaryButton, + type: 'warning' + }; + } + + if (!this.messageService.confirm(confirm)) { + return TPromise.as(null); + } + } + + // Call function + const servicePromise = this.fileService.del(this.element.resource, this.useTrash).then(() => { + if (this.element.parent) { + this.tree.setFocus(this.element.parent); // move focus to parent + } + }, (error: any) => { + + // Allow to retry + let extraAction: Action; + if (this.useTrash) { + extraAction = new Action('permanentDelete', nls.localize('permDelete', "Delete Permanently"), null, true, () => { this.useTrash = false; this.skipConfirm = true; return this.run(); }); + } + + this.onErrorWithRetry(error, () => this.run(), extraAction); + + // Focus back to tree + this.tree.DOMFocus(); + }); + + return servicePromise; + }); + } +} + +/* Move File/Folder to trash */ +export class MoveFileToTrashAction extends BaseDeleteFileAction { + public static ID = 'moveFileToTrash'; + + constructor( + tree: ITree, + element: FileStat, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService + ) { + super(MoveFileToTrashAction.ID, nls.localize('delete', "Delete"), tree, element, true, fileService, messageService, textFileService); + } +} + +/* Import File */ +export class ImportFileAction extends BaseFileAction { + + public static ID = 'workbench.files.action.importFile'; + private tree: ITree; + + constructor( + tree: ITree, + element: FileStat, + clazz: string, + @IFileService fileService: IFileService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService + ) { + super(ImportFileAction.ID, nls.localize('importFiles', "Import Files"), fileService, messageService, textFileService); + + this.tree = tree; + this.element = element; + + if (clazz) { + this.class = clazz; + } + + this._updateEnablement(); + } + + public getViewer(): ITree { + return this.tree; + } + + public run(context?: any): TPromise { + const importPromise = TPromise.as(null).then(() => { + const input = context.input as { paths: string[] }; + if (input.paths && input.paths.length > 0) { + + // Find parent for import + let targetElement: FileStat; + if (this.element) { + targetElement = this.element; + } else { + const input: FileStat | Model = this.tree.getInput(); + targetElement = this.tree.getFocus() || (input instanceof Model ? input.roots[0] : input); + } + + if (!targetElement.isDirectory) { + targetElement = targetElement.parent; + } + + // Resolve target to check for name collisions and ask user + return this.fileService.resolveFile(targetElement.resource).then((targetStat: IFileStat) => { + + // Check for name collisions + const targetNames: { [name: string]: IFileStat } = {}; + targetStat.children.forEach((child) => { + targetNames[isLinux ? child.name : child.name.toLowerCase()] = child; + }); + + let overwrite = true; + if (input.paths.some(path => { + return !!targetNames[isLinux ? paths.basename(path) : paths.basename(path).toLowerCase()]; + })) { + const confirm: IConfirmation = { + message: nls.localize('confirmOverwrite', "A file or folder with the same name already exists in the destination folder. Do you want to replace it?"), + detail: nls.localize('irreversible', "This action is irreversible!"), + primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), + type: 'warning' + }; + + overwrite = this.messageService.confirm(confirm); + } + + if (!overwrite) { + return void 0; + } + + // Run import in sequence + const importPromisesFactory: ITask>[] = []; + input.paths.forEach(path => { + importPromisesFactory.push(() => { + const sourceFile = URI.file(path); + const targetFile = URI.file(paths.join(targetElement.resource.fsPath, paths.basename(path))); + + // if the target exists and is dirty, make sure to revert it. otherwise the dirty contents + // of the target file would replace the contents of the imported file. since we already + // confirmed the overwrite before, this is OK. + let revertPromise = TPromise.as(null); + if (this.textFileService.isDirty(targetFile)) { + revertPromise = this.textFileService.revertAll([targetFile], { soft: true }); + } + + return revertPromise.then(() => { + return this.fileService.importFile(sourceFile, targetElement.resource).then(res => { + + // if we only import one file, just open it directly + if (input.paths.length === 1) { + this.editorService.openEditor({ resource: res.stat.resource, options: { pinned: true } }).done(null, errors.onUnexpectedError); + } + }, error => this.onError(error)); + }); + }); + }); + + return sequence(importPromisesFactory); + }); + } + + return void 0; + }); + + return importPromise.then(() => { + this.tree.clearHighlight(); + }, (error: any) => { + this.onError(error); + this.tree.clearHighlight(); + }); + } +} + +// Copy File/Folder +let fileToCopy: FileStat; +export class CopyFileAction extends BaseFileAction { + + public static ID = 'filesExplorer.copy'; + + private tree: ITree; + constructor( + tree: ITree, + element: FileStat, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService + ) { + super(CopyFileAction.ID, nls.localize('copyFile', "Copy"), fileService, messageService, textFileService); + + this.tree = tree; + this.element = element; + this._updateEnablement(); + } + + public run(): TPromise { + + // Remember as file/folder to copy + fileToCopy = this.element; + + // Remove highlight + if (this.tree) { + this.tree.clearHighlight(); + } + + this.tree.DOMFocus(); + + return TPromise.as(null); + } +} + +// Paste File/Folder +export class PasteFileAction extends BaseFileAction { + + public static ID = 'filesExplorer.paste'; + + private tree: ITree; + + constructor( + tree: ITree, + element: FileStat, + @IFileService fileService: IFileService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(PasteFileAction.ID, nls.localize('pasteFile', "Paste"), fileService, messageService, textFileService); + + this.tree = tree; + this.element = element; + if (!this.element) { + const input: FileStat | Model = this.tree.getInput(); + this.element = input instanceof Model ? input.roots[0] : input; + } + this._updateEnablement(); + } + + _isEnabled(): boolean { + + // Need at least a file to copy + if (!fileToCopy) { + return false; + } + + // Check if file was deleted or moved meanwhile + const exists = fileToCopy.root.find(fileToCopy.resource); + if (!exists) { + fileToCopy = null; + return false; + } + + // Check if target is ancestor of pasted folder + if (!paths.isEqual(this.element.resource.fsPath, fileToCopy.resource.fsPath) && paths.isEqualOrParent(this.element.resource.fsPath, fileToCopy.resource.fsPath, !isLinux /* ignorecase */)) { + return false; + } + + return true; + } + + public run(): TPromise { + + // Find target + let target: FileStat; + if (this.element.resource.toString() === fileToCopy.resource.toString()) { + target = this.element.parent; + } else { + target = this.element.isDirectory ? this.element : this.element.parent; + } + + // Reuse duplicate action + const pasteAction = this.instantiationService.createInstance(DuplicateFileAction, this.tree, fileToCopy, target); + + return pasteAction.run().then(() => { + this.tree.DOMFocus(); + }); + } +} + +export const pasteIntoFocusedFilesExplorerViewItem = (accessor: ServicesAccessor) => { + const instantiationService = accessor.get(IInstantiationService); + + withFocusedFilesExplorer(accessor).then(res => { + if (res) { + const pasteAction = instantiationService.createInstance(PasteFileAction, res.tree, res.tree.getFocus()); + if (pasteAction._isEnabled()) { + pasteAction.run().done(null, errors.onUnexpectedError); + } + } + }); +}; + +// Duplicate File/Folder +export class DuplicateFileAction extends BaseFileAction { + private tree: ITree; + private target: IFileStat; + + constructor( + tree: ITree, + element: FileStat, + target: FileStat, + @IFileService fileService: IFileService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IMessageService messageService: IMessageService, + @ITextFileService textFileService: ITextFileService + ) { + super('workbench.files.action.duplicateFile', nls.localize('duplicateFile', "Duplicate"), fileService, messageService, textFileService); + + this.tree = tree; + this.element = element; + this.target = (target && target.isDirectory) ? target : element.parent; + this._updateEnablement(); + } + + public run(): TPromise { + + // Remove highlight + if (this.tree) { + this.tree.clearHighlight(); + } + + // Copy File + const result = this.fileService.copyFile(this.element.resource, this.findTarget()).then(stat => { + if (!stat.isDirectory) { + return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); + } + + return void 0; + }, error => this.onError(error)); + + return result; + } + + private findTarget(): URI { + let name = this.element.name; + + let candidate = URI.file(paths.join(this.target.resource.fsPath, name)); + while (true) { + if (!this.element.root.find(candidate)) { + break; + } + + name = this.toCopyName(name, this.element.isDirectory); + candidate = URI.file(paths.join(this.target.resource.fsPath, name)); + } + + return candidate; + } + + private toCopyName(name: string, isFolder: boolean): string { + + // file.1.txt=>file.2.txt + if (!isFolder && name.match(/(.*\.)(\d+)(\..*)$/)) { + return name.replace(/(.*\.)(\d+)(\..*)$/, (match, g1?, g2?, g3?) => { return g1 + (parseInt(g2) + 1) + g3; }); + } + + // file.txt=>file.1.txt + const lastIndexOfDot = name.lastIndexOf('.'); + if (!isFolder && lastIndexOfDot >= 0) { + return strings.format('{0}.1{1}', name.substr(0, lastIndexOfDot), name.substr(lastIndexOfDot)); + } + + // folder.1=>folder.2 + if (isFolder && name.match(/(\d+)$/)) { + return name.replace(/(\d+)$/, (match: string, ...groups: any[]) => { return String(parseInt(groups[0]) + 1); }); + } + + // file/folder=>file.1/folder.1 + return strings.format('{0}.1', name); + } +} + +// Open to the side +export class OpenToSideAction extends Action { + + public static ID = 'explorer.openToSide'; + public static LABEL = nls.localize('openToSide', "Open to the Side"); + + private tree: ITree; + private resource: URI; + private preserveFocus: boolean; + + constructor( + tree: ITree, + resource: URI, + preserveFocus: boolean, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(OpenToSideAction.ID, OpenToSideAction.LABEL); + + this.tree = tree; + this.preserveFocus = preserveFocus; + this.resource = resource; + + this.updateEnablement(); + } + + private updateEnablement(): void { + const activeEditor = this.editorService.getActiveEditor(); + this.enabled = (!activeEditor || activeEditor.position !== Position.THREE); + } + + public run(): TPromise { + + // Remove highlight + this.tree.clearHighlight(); + + // Set side input + return this.editorService.openEditor({ + resource: this.resource, + options: { + preserveFocus: this.preserveFocus + } + }, true); + } +} + +let globalResourceToCompare: URI; +export class SelectResourceForCompareAction extends Action { + private resource: URI; + private tree: ITree; + + constructor(resource: URI, tree: ITree) { + super('workbench.files.action.selectForCompare', nls.localize('compareSource', "Select for Compare")); + + this.tree = tree; + this.resource = resource; + this.enabled = true; + } + + public run(): TPromise { + + // Remember as source file to compare + globalResourceToCompare = this.resource; + + // Remove highlight + if (this.tree) { + this.tree.clearHighlight(); + this.tree.DOMFocus(); + } + + return TPromise.as(null); + } +} + +// Global Compare with +export class GlobalCompareResourcesAction extends Action { + + public static ID = 'workbench.files.action.compareFileWith'; + public static LABEL = nls.localize('globalCompareFile', "Compare Active File With..."); + + constructor( + id: string, + label: string, + @IQuickOpenService private quickOpenService: IQuickOpenService, + @IInstantiationService private instantiationService: IInstantiationService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IHistoryService private historyService: IHistoryService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IMessageService private messageService: IMessageService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + super(id, label); + } + + public run(): TPromise { + const activeResource = toResource(this.editorService.getActiveEditorInput(), { filter: ['file', 'untitled'] }); + if (activeResource) { + + // Keep as resource to compare + globalResourceToCompare = activeResource; + + // Pick another entry from history + interface IHistoryPickEntry extends IFilePickOpenEntry { + input: IEditorInput | IResourceInput; + } + + const history = this.historyService.getHistory(); + const picks: IHistoryPickEntry[] = history.map(input => { + let resource: URI; + let label: string; + let description: string; + + if (input instanceof EditorInput) { + resource = toResource(input, { filter: ['file', 'untitled'] }); + } else { + resource = (input as IResourceInput).resource; + } + + // Cannot compare file with self - exclude active file + if (!!resource && resource.toString() === globalResourceToCompare.toString()) { + return void 0; + } + + if (!resource) { + return void 0; // only support to compare with files and untitled + } + + label = paths.basename(resource.fsPath); + description = resource.scheme === 'file' ? labels.getPathLabel(paths.dirname(resource.fsPath), this.contextService, this.environmentService) : void 0; + + return { input, resource, label, description }; + }).filter(p => !!p); + + return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickHistory', "Select a previously opened file to compare with"), autoFocus: { autoFocusFirstEntry: true }, matchOnDescription: true }).then(pick => { + if (pick) { + const compareAction = this.instantiationService.createInstance(CompareResourcesAction, pick.resource, null); + if (compareAction._isEnabled()) { + compareAction.run().done(() => compareAction.dispose()); + } else { + this.messageService.show(Severity.Info, nls.localize('unableToFileToCompare', "The selected file can not be compared with '{0}'.", paths.basename(globalResourceToCompare.fsPath))); + } + } + }); + } else { + this.messageService.show(Severity.Info, nls.localize('openFileToCompare', "Open a file first to compare it with another file.")); + } + + return TPromise.as(true); + } +} + +// Compare with Resource +export class CompareResourcesAction extends Action { + private tree: ITree; + private resource: URI; + + constructor( + resource: URI, + tree: ITree, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IEnvironmentService environmentService: IEnvironmentService + ) { + super('workbench.files.action.compareFiles', CompareResourcesAction.computeLabel(resource, contextService, environmentService)); + + this.tree = tree; + this.resource = resource; + } + + private static computeLabel(resource: URI, contextService: IWorkspaceContextService, environmentService: IEnvironmentService): string { + if (globalResourceToCompare) { + let leftResourceName = paths.basename(globalResourceToCompare.fsPath); + let rightResourceName = paths.basename(resource.fsPath); + + // If the file names are identical, add more context by looking at the parent folder + if (leftResourceName === rightResourceName) { + const folderPaths = labels.shorten([ + labels.getPathLabel(paths.dirname(globalResourceToCompare.fsPath), contextService, environmentService), + labels.getPathLabel(paths.dirname(resource.fsPath), contextService, environmentService) + ]); + + leftResourceName = paths.join(folderPaths[0], leftResourceName); + rightResourceName = paths.join(folderPaths[1], rightResourceName); + } + + return nls.localize('compareWith', "Compare '{0}' with '{1}'", leftResourceName, rightResourceName); + } + + return nls.localize('compareFiles', "Compare Files"); + } + + _isEnabled(): boolean { + + // Need at least a resource to compare + if (!globalResourceToCompare) { + return false; + } + + // Check if file was deleted or moved meanwhile (explorer only) + if (this.tree) { + const input = this.tree.getInput(); + if (input instanceof FileStat || input instanceof Model) { + const exists = input instanceof Model ? input.findClosest(globalResourceToCompare) : input.find(globalResourceToCompare); + if (!exists) { + globalResourceToCompare = null; + + return false; + } + } + } + + // Check if target is identical to source + if (this.resource.toString() === globalResourceToCompare.toString()) { + return false; + } + + return true; + } + + public run(): TPromise { + + // Remove highlight + if (this.tree) { + this.tree.clearHighlight(); + } + + return this.editorService.openEditor({ + leftResource: globalResourceToCompare, + rightResource: this.resource + }); + } +} + +// Refresh Explorer Viewer +export class RefreshViewExplorerAction extends Action { + + constructor(explorerView: ExplorerView, clazz: string) { + super('workbench.files.action.refreshFilesExplorer', nls.localize('refresh', "Refresh"), clazz, true, (context: any) => explorerView.refresh()); + } +} + +export abstract class BaseSaveFileAction extends BaseErrorReportingAction { + constructor( + id: string, + label: string, + messageService: IMessageService + ) { + super(id, label, messageService); + } + + public run(context?: any): TPromise { + return this.doRun(context).then(() => true, error => { + this.onError(error); + return null; + }); + } + + protected abstract doRun(context?: any): TPromise; +} + +export abstract class BaseSaveOneFileAction extends BaseSaveFileAction { + private resource: URI; + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @ITextFileService private textFileService: ITextFileService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IMessageService messageService: IMessageService + ) { + super(id, label, messageService); + + this.enabled = true; + } + + public abstract isSaveAs(): boolean; + + public setResource(resource: URI): void { + this.resource = resource; + } + + protected doRun(context: any): TPromise { + let source: URI; + if (this.resource) { + source = this.resource; + } else { + source = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: ['file', 'untitled'] }); + } + + if (source) { + + // Save As (or Save untitled with associated path) + if (this.isSaveAs() || source.scheme === 'untitled') { + let encodingOfSource: string; + if (source.scheme === 'untitled') { + encodingOfSource = this.untitledEditorService.getEncoding(source); + } else if (source.scheme === 'file') { + const textModel = this.textFileService.models.get(source); + encodingOfSource = textModel && textModel.getEncoding(); // text model can be null e.g. if this is a binary file! + } + + let viewStateOfSource: IEditorViewState; + const activeEditor = this.editorService.getActiveEditor(); + const editor = getCodeEditor(activeEditor); + if (editor) { + const activeResource = toResource(activeEditor.input, { supportSideBySide: true, filter: ['file', 'untitled'] }); + if (activeResource && activeResource.toString() === source.toString()) { + viewStateOfSource = editor.saveViewState(); + } + } + + // Special case: an untitled file with associated path gets saved directly unless "saveAs" is true + let savePromise: TPromise; + if (!this.isSaveAs() && source.scheme === 'untitled' && this.untitledEditorService.hasAssociatedFilePath(source)) { + savePromise = this.textFileService.save(source).then((result) => { + if (result) { + return URI.file(source.fsPath); + } + + return null; + }); + } + + // Otherwise, really "Save As..." + else { + savePromise = this.textFileService.saveAs(source); + } + + return savePromise.then((target) => { + if (!target || target.toString() === source.toString()) { + return void 0; // save canceled or same resource used + } + + const replaceWith: IResourceInput = { + resource: target, + encoding: encodingOfSource, + options: { + pinned: true, + viewState: viewStateOfSource + } + }; + + return this.editorService.replaceEditors([{ + toReplace: { resource: source }, + replaceWith + }]).then(() => true); + }); + } + + // Pin the active editor if we are saving it + if (!this.resource) { + const editor = this.editorService.getActiveEditor(); + if (editor) { + this.editorGroupService.pinEditor(editor.position, editor.input); + } + } + + // Just save + return this.textFileService.save(source, { force: true /* force a change to the file to trigger external watchers if any */ }); + } + + return TPromise.as(false); + } +} + +export class SaveFileAction extends BaseSaveOneFileAction { + + public static ID = 'workbench.action.files.save'; + public static LABEL = nls.localize('save', "Save"); + + public isSaveAs(): boolean { + return false; + } +} + +export class SaveFileAsAction extends BaseSaveOneFileAction { + + public static ID = 'workbench.action.files.saveAs'; + public static LABEL = nls.localize('saveAs', "Save As..."); + + public isSaveAs(): boolean { + return true; + } +} + +export abstract class BaseSaveAllAction extends BaseSaveFileAction { + private toDispose: IDisposable[]; + private lastIsDirty: boolean; + + constructor( + id: string, + label: string, + @IWorkbenchEditorService protected editorService: IWorkbenchEditorService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @ITextFileService private textFileService: ITextFileService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IMessageService messageService: IMessageService + ) { + super(id, label, messageService); + + this.toDispose = []; + this.lastIsDirty = this.textFileService.isDirty(); + this.enabled = this.lastIsDirty; + + this.registerListeners(); + } + + protected abstract getSaveAllArguments(context?: any): any; + protected abstract includeUntitled(): boolean; + + private registerListeners(): void { + + // listen to files being changed locally + this.toDispose.push(this.textFileService.models.onModelsDirty(e => this.updateEnablement(true))); + this.toDispose.push(this.textFileService.models.onModelsSaved(e => this.updateEnablement(false))); + this.toDispose.push(this.textFileService.models.onModelsReverted(e => this.updateEnablement(false))); + this.toDispose.push(this.textFileService.models.onModelsSaveError(e => this.updateEnablement(true))); + + if (this.includeUntitled()) { + this.toDispose.push(this.untitledEditorService.onDidChangeDirty(resource => this.updateEnablement(this.untitledEditorService.isDirty(resource)))); + } + } + + private updateEnablement(isDirty: boolean): void { + if (this.lastIsDirty !== isDirty) { + this.enabled = this.textFileService.isDirty(); + this.lastIsDirty = this.enabled; + } + } + + protected doRun(context: any): TPromise { + const stacks = this.editorGroupService.getStacksModel(); + + // Store some properties per untitled file to restore later after save is completed + const mapUntitledToProperties: { [resource: string]: { encoding: string; indexInGroups: number[]; activeInGroups: boolean[] } } = Object.create(null); + this.untitledEditorService.getDirty().forEach(resource => { + const activeInGroups: boolean[] = []; + const indexInGroups: number[] = []; + const encoding = this.untitledEditorService.getEncoding(resource); + + // For each group + stacks.groups.forEach((group, groupIndex) => { + + // Find out if editor is active in group + const activeEditor = group.activeEditor; + const activeResource = toResource(activeEditor, { supportSideBySide: true }); + activeInGroups[groupIndex] = (activeResource && activeResource.toString() === resource.toString()); + + // Find index of editor in group + indexInGroups[groupIndex] = -1; + group.getEditors().forEach((editor, editorIndex) => { + const editorResource = toResource(editor, { supportSideBySide: true }); + if (editorResource && editorResource.toString() === resource.toString()) { + indexInGroups[groupIndex] = editorIndex; + return; + } + }); + }); + + mapUntitledToProperties[resource.toString()] = { encoding, indexInGroups, activeInGroups }; + }); + + // Save all + return this.textFileService.saveAll(this.getSaveAllArguments(context)).then(results => { + + // Reopen saved untitled editors + const untitledToReopen: { input: IResourceInput, position: Position }[] = []; + + results.results.forEach(result => { + if (!result.success || result.source.scheme !== 'untitled') { + return; + } + + const untitledProps = mapUntitledToProperties[result.source.toString()]; + if (!untitledProps) { + return; + } + + // For each position where the untitled file was opened + untitledProps.indexInGroups.forEach((indexInGroup, index) => { + if (indexInGroup >= 0) { + untitledToReopen.push({ + input: { + resource: result.target, + encoding: untitledProps.encoding, + options: { + pinned: true, + index: indexInGroup, + preserveFocus: true, + inactive: !untitledProps.activeInGroups[index] + } + }, + position: index + }); + } + }); + }); + + if (untitledToReopen.length) { + return this.editorService.openEditors(untitledToReopen).then(() => true); + } + + return void 0; + }); + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + + super.dispose(); + } +} + +export class SaveAllAction extends BaseSaveAllAction { + + public static ID = 'workbench.action.files.saveAll'; + public static LABEL = nls.localize('saveAll', "Save All"); + + public get class(): string { + return 'explorer-action save-all'; + } + + protected getSaveAllArguments(): boolean { + return this.includeUntitled(); + } + + protected includeUntitled(): boolean { + return true; + } +} + +export class SaveAllInGroupAction extends BaseSaveAllAction { + + public static ID = 'workbench.files.action.saveAllInGroup'; + public static LABEL = nls.localize('saveAllInGroup', "Save All in Group"); + + public get class(): string { + return 'explorer-action save-all'; + } + + protected getSaveAllArguments(editorIdentifier: IEditorIdentifier): any { + if (!editorIdentifier) { + return this.includeUntitled(); + } + + const editorGroup = editorIdentifier.group; + const resourcesToSave: URI[] = []; + editorGroup.getEditors().forEach(editor => { + const resource = toResource(editor, { supportSideBySide: true, filter: ['file', 'untitled'] }); + if (resource) { + resourcesToSave.push(resource); + } + }); + + return resourcesToSave; + } + + protected includeUntitled(): boolean { + return true; + } +} + +export class SaveFilesAction extends BaseSaveAllAction { + + public static ID = 'workbench.action.files.saveFiles'; + public static LABEL = nls.localize('saveFiles', "Save Dirty Files"); + + protected getSaveAllArguments(): boolean { + return this.includeUntitled(); + } + + protected includeUntitled(): boolean { + return false; + } +} + +export class RevertFileAction extends Action { + + public static ID = 'workbench.action.files.revert'; + public static LABEL = nls.localize('revert', "Revert File"); + + private resource: URI; + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @ITextFileService private textFileService: ITextFileService + ) { + super(id, label); + + this.enabled = true; + } + + public setResource(resource: URI): void { + this.resource = resource; + } + + public run(): TPromise { + let resource: URI; + if (this.resource) { + resource = this.resource; + } else { + resource = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); + } + + if (resource && resource.scheme !== 'untitled') { + return this.textFileService.revert(resource, { force: true }); + } + + return TPromise.as(true); + } +} + +export class FocusOpenEditorsView extends Action { + + public static ID = 'workbench.files.action.focusOpenEditorsView'; + public static LABEL = nls.localize({ key: 'focusOpenEditors', comment: ['Open is an adjective'] }, "Focus on Open Editors View"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label); + } + + public run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true).then((viewlet: ExplorerViewlet) => { + const openEditorsView = viewlet.getOpenEditorsView(); + if (openEditorsView) { + openEditorsView.expand(); + openEditorsView.getViewer().DOMFocus(); + } + }); + } +} + +export class FocusFilesExplorer extends Action { + + public static ID = 'workbench.files.action.focusFilesExplorer'; + public static LABEL = nls.localize('focusFilesExplorer', "Focus on Files Explorer"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label); + } + + public run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true).then((viewlet: ExplorerViewlet) => { + const view = viewlet.getExplorerView(); + if (view) { + view.expand(); + view.getViewer().DOMFocus(); + } + }); + } +} + +export class ShowActiveFileInExplorer extends Action { + + public static ID = 'workbench.files.action.showActiveFileInExplorer'; + public static LABEL = nls.localize('showInExplorer', "Reveal Active File in Side Bar"); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService private instantiationService: IInstantiationService, + @IMessageService private messageService: IMessageService + ) { + super(id, label); + } + + public run(): TPromise { + const fileResource = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); + if (fileResource) { + this.instantiationService.invokeFunction.apply(this.instantiationService, [revealInExplorerCommand, fileResource]); + } else { + this.messageService.show(severity.Info, nls.localize('openFileToShow', "Open a file first to show it in the explorer")); + } + + return TPromise.as(true); + } +} + +export class CollapseExplorerView extends Action { + + public static ID = 'workbench.files.action.collapseExplorerFolders'; + public static LABEL = nls.localize('collapseExplorerFolders', "Collapse Folders in Explorer"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label); + } + + public run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true).then((viewlet: ExplorerViewlet) => { + const explorerView = viewlet.getExplorerView(); + if (explorerView) { + const viewer = explorerView.getViewer(); + if (viewer) { + const action = new CollapseAction(viewer, true, null); + action.run().done(); + action.dispose(); + } + } + }); + } +} + +export class RefreshExplorerView extends Action { + + public static ID = 'workbench.files.action.refreshFilesExplorer'; + public static LABEL = nls.localize('refreshExplorer', "Refresh Explorer"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label); + } + + public run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true).then((viewlet: ExplorerViewlet) => { + const explorerView = viewlet.getExplorerView(); + if (explorerView) { + explorerView.refresh(); + } + }); + } +} + +export class OpenFileAction extends Action { + + static ID = 'workbench.action.files.openFile'; + static LABEL = nls.localize('openFile', "Open File..."); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IWindowService private windowService: IWindowService + ) { + super(id, label); + } + + run(event?: any, data?: ITelemetryData): TPromise { + const fileResource = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); + + return this.windowService.pickFileAndOpen({ telemetryExtraData: data, dialogOptions: { defaultPath: fileResource ? paths.dirname(fileResource.fsPath) : void 0 } }); + } +} + +export class ShowOpenedFileInNewWindow extends Action { + + public static ID = 'workbench.action.files.showOpenedFileInNewWindow'; + public static LABEL = nls.localize('openFileInNewWindow', "Open Active File in New Window"); + + constructor( + id: string, + label: string, + @IWindowsService private windowsService: IWindowsService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IMessageService private messageService: IMessageService + ) { + super(id, label); + } + + public run(): TPromise { + const fileResource = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); + if (fileResource) { + this.windowsService.openWindow([fileResource.fsPath], { forceNewWindow: true, forceOpenWorkspaceAsFile: true }); + } else { + this.messageService.show(severity.Info, nls.localize('openFileToShowInNewWindow', "Open a file first to open in new window")); + } + + return TPromise.as(true); + } +} + +export class RevealInOSAction extends Action { + + public static LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder"); + + constructor( + private resource: URI, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super('revealFileInOS', RevealInOSAction.LABEL); + + this.order = 45; + } + + public run(): TPromise { + this.instantiationService.invokeFunction.apply(this.instantiationService, [revealInOSCommand, this.resource]); + + return TPromise.as(true); + } +} + +export class GlobalRevealInOSAction extends Action { + + public static ID = 'workbench.action.files.revealActiveFileInWindows'; + public static LABEL = isWindows ? nls.localize('revealActiveFileInWindows', "Reveal Active File in Windows Explorer") : (isMacintosh ? nls.localize('revealActiveFileInMac', "Reveal Active File in Finder") : nls.localize('openActiveFileContainer', "Open Containing Folder of Active File")); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService private instantiationService: IInstantiationService, + @IMessageService private messageService: IMessageService + ) { + super(id, label); + } + + public run(): TPromise { + this.instantiationService.invokeFunction.apply(this.instantiationService, [revealInOSCommand]); + + return TPromise.as(true); + } +} + +export class CopyPathAction extends Action { + + public static LABEL = nls.localize('copyPath', "Copy Path"); + + constructor( + private resource: URI, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super('copyFilePath', CopyPathAction.LABEL); + + this.order = 140; + } + + public run(): TPromise { + this.instantiationService.invokeFunction.apply(this.instantiationService, [copyPathCommand, this.resource]); + + return TPromise.as(true); + } +} + +export class GlobalCopyPathAction extends Action { + + public static ID = 'workbench.action.files.copyPathOfActiveFile'; + public static LABEL = nls.localize('copyPathOfActive', "Copy Path of Active File"); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IMessageService private messageService: IMessageService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(id, label); + } + + public run(): TPromise { + this.instantiationService.invokeFunction.apply(this.instantiationService, [copyPathCommand]); + + return TPromise.as(true); + } +} + +export function validateFileName(parent: IFileStat, name: string, allowOverwriting: boolean = false): string { + + // Produce a well formed file name + name = getWellFormedFileName(name); + + // Name not provided + if (!name || name.length === 0 || /^\s+$/.test(name)) { + return nls.localize('emptyFileNameError', "A file or folder name must be provided."); + } + + // Do not allow to overwrite existing file + if (!allowOverwriting) { + if (parent.children && parent.children.some((c) => { + if (isLinux) { + return c.name === name; + } + + return c.name.toLowerCase() === name.toLowerCase(); + })) { + return nls.localize('fileNameExistsError', "A file or folder **{0}** already exists at this location. Please choose a different name.", name); + } + } + + // Invalid File name + if (!paths.isValidBasename(name)) { + return nls.localize('invalidFileNameError', "The name **{0}** is not valid as a file or folder name. Please choose a different name.", name); + } + + // Max length restriction (on Windows) + if (isWindows) { + const fullPathLength = name.length + parent.resource.fsPath.length + 1 /* path segment */; + if (fullPathLength > 255) { + return nls.localize('filePathTooLongError', "The name **{0}** results in a path that is too long. Please choose a shorter name.", name); + } + } + + return null; +} + +export function getWellFormedFileName(filename: string): string { + if (!filename) { + return filename; + } + + // Trim whitespaces + filename = strings.trim(strings.trim(filename, ' '), '\t'); + + // Remove trailing dots + filename = strings.rtrim(filename, '.'); + + return filename; +} + +export class CompareWithSavedAction extends Action implements ITextModelContentProvider { + + public static ID = 'workbench.files.action.compareWithSaved'; + public static LABEL = nls.localize('compareWithSaved', "Compare Active File with Saved"); + + private static SCHEME = 'showModifications'; + + private resource: URI; + private fileWatcher: IDisposable; + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @ITextFileService private textFileService: ITextFileService, + @IFileService private fileService: IFileService, + @IMessageService messageService: IMessageService, + @IModeService private modeService: IModeService, + @IModelService private modelService: IModelService, + @ITextModelService textModelService: ITextModelService + ) { + super(id, label); + + textModelService.registerTextModelContentProvider(CompareWithSavedAction.SCHEME, this); + + this.enabled = true; + } + + public setResource(resource: URI) { + this.resource = resource; + } + + public run(): TPromise { + let resource: URI; + if (this.resource) { + resource = this.resource; + } else { + resource = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); + } + + if (resource && resource.scheme === 'file') { + const name = paths.basename(resource.fsPath); + const editorLabel = nls.localize('modifiedLabel', "{0} (on disk) ↔ {1}", name, name); + + return this.editorService.openEditor({ leftResource: URI.from({ scheme: CompareWithSavedAction.SCHEME, path: resource.fsPath }), rightResource: resource, label: editorLabel }); + } + + return TPromise.as(true); + } + + public provideTextContent(resource: URI): TPromise { + + // Make sure our file from disk is resolved up to date + return this.resolveEditorModel(resource).then(codeEditorModel => { + + // Make sure to keep contents on disk up to date when it changes + if (!this.fileWatcher) { + this.fileWatcher = this.fileService.onFileChanges(changes => { + if (changes.contains(resource, FileChangeType.UPDATED)) { + this.resolveEditorModel(resource, false /* do not create if missing */).done(null, errors.onUnexpectedError); // update model when resource changes + } + }); + + const disposeListener = codeEditorModel.onWillDispose(() => { + disposeListener.dispose(); + this.fileWatcher.dispose(); + this.fileWatcher = void 0; + }); + } + + return codeEditorModel; + }); + } + + private resolveEditorModel(resource: URI, createAsNeeded = true): TPromise { + const fileOnDiskResource = URI.file(resource.fsPath); + + return this.textFileService.resolveTextContent(fileOnDiskResource).then(content => { + let codeEditorModel = this.modelService.getModel(resource); + if (codeEditorModel) { + this.modelService.updateModel(codeEditorModel, content.value); + } else if (createAsNeeded) { + const fileOnDiskModel = this.modelService.getModel(fileOnDiskResource); + + let mode: TPromise; + if (fileOnDiskModel) { + mode = this.modeService.getOrCreateMode(fileOnDiskModel.getModeId()); + } else { + mode = this.modeService.getOrCreateModeByFilenameOrFirstLine(fileOnDiskResource.fsPath); + } + + codeEditorModel = this.modelService.createModel(content.value, mode, resource); + } + + return codeEditorModel; + }); + } + + public dispose(): void { + super.dispose(); + + if (this.fileWatcher) { + this.fileWatcher.dispose(); + this.fileWatcher = void 0; + } + } +} + +// Diagnostics support +let diag: (...args: any[]) => void; +if (!diag) { + diag = diagnostics.register('FileActionsDiagnostics', function (...args: any[]) { + console.log(args[1] + ' - ' + args[0] + ' (time: ' + args[2].getTime() + ' [' + args[2].toUTCString() + '])'); + }); +} diff --git a/src/vs/workbench/parts/files/browser/fileCommands.ts b/src/vs/workbench/parts/files/browser/fileCommands.ts new file mode 100644 index 0000000000..a379ccd044 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/fileCommands.ts @@ -0,0 +1,268 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import paths = require('vs/base/common/paths'); +import severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { toResource } from 'vs/workbench/common/editor'; +import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ExplorerViewlet } from 'vs/workbench/parts/files/browser/explorerViewlet'; +import { VIEWLET_ID, explorerItemToFileResource } from 'vs/workbench/parts/files/common/files'; +import { FileStat, OpenEditor } from 'vs/workbench/parts/files/common/explorerModel'; +import errors = require('vs/base/common/errors'); +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import labels = require('vs/base/common/labels'); +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IMessageService } from 'vs/platform/message/common/message'; + +// Commands + +export const copyPathCommand = (accessor: ServicesAccessor, resource?: URI) => { + + // Without resource, try to look at the active editor + if (!resource) { + const editorGroupService = accessor.get(IEditorGroupService); + const editorService = accessor.get(IWorkbenchEditorService); + const activeEditor = editorService.getActiveEditor(); + + resource = activeEditor ? toResource(activeEditor.input, { supportSideBySide: true, filter: 'file' }) : void 0; + if (activeEditor) { + editorGroupService.focusGroup(activeEditor.position); // focus back to active editor group + } + } + + if (resource) { + const clipboardService = accessor.get(IClipboardService); + clipboardService.writeText(labels.getPathLabel(resource)); + } else { + const messageService = accessor.get(IMessageService); + messageService.show(severity.Info, nls.localize('openFileToCopy', "Open a file first to copy its path")); + } +}; + +export const openFolderPickerCommand = (accessor: ServicesAccessor, forceNewWindow: boolean) => { + const windowService = accessor.get(IWindowService); + + windowService.pickFolderAndOpen({ forceNewWindow }); +}; + +export const openWindowCommand = (accessor: ServicesAccessor, paths: string[], forceNewWindow: boolean) => { + const windowsService = accessor.get(IWindowsService); + windowsService.openWindow(paths, { forceNewWindow }); +}; + +export const openFileInNewWindowCommand = (accessor: ServicesAccessor) => { + const windowService = accessor.get(IWindowService); + const editorService = accessor.get(IWorkbenchEditorService); + + const fileResource = toResource(editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); + + windowService.pickFileAndOpen({ forceNewWindow: true, dialogOptions: { defaultPath: fileResource ? paths.dirname(fileResource.fsPath) : void 0 } }); +}; + +export const revealInOSCommand = (accessor: ServicesAccessor, resource?: URI) => { + + // Without resource, try to look at the active editor + if (!resource) { + const editorService = accessor.get(IWorkbenchEditorService); + + resource = toResource(editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); + } + + if (resource) { + const windowsService = accessor.get(IWindowsService); + windowsService.showItemInFolder(paths.normalize(resource.fsPath, true)); + } else { + const messageService = accessor.get(IMessageService); + messageService.show(severity.Info, nls.localize('openFileToReveal', "Open a file first to reveal")); + } +}; + +export const revealInExplorerCommand = (accessor: ServicesAccessor, resource: URI) => { + const viewletService = accessor.get(IViewletService); + const contextService = accessor.get(IWorkspaceContextService); + + viewletService.openViewlet(VIEWLET_ID, false).then((viewlet: ExplorerViewlet) => { + const isInsideWorkspace = contextService.isInsideWorkspace(resource); + if (isInsideWorkspace) { + const explorerView = viewlet.getExplorerView(); + if (explorerView) { + explorerView.expand(); + explorerView.select(resource, true); + } + } else { + const openEditorsView = viewlet.getOpenEditorsView(); + if (openEditorsView) { + openEditorsView.expand(); + } + } + }); +}; + +function openFocusedFilesExplorerViewItem(accessor: ServicesAccessor, sideBySide: boolean): void { + withFocusedFilesExplorerViewItem(accessor).then(res => { + if (res) { + + // Directory: Toggle expansion + if (res.item.isDirectory) { + res.tree.toggleExpansion(res.item); + } + + // File: Open + else { + const editorService = accessor.get(IWorkbenchEditorService); + editorService.openEditor({ resource: res.item.resource }, sideBySide).done(null, errors.onUnexpectedError); + } + } + }); +} + +function openFocusedOpenedEditorsViewItem(accessor: ServicesAccessor, sideBySide: boolean): void { + withFocusedOpenEditorsViewItem(accessor).then(res => { + if (res) { + const editorService = accessor.get(IWorkbenchEditorService); + + editorService.openEditor(res.item.editorInput, null, sideBySide); + } + }); +} + +function runActionOnFocusedFilesExplorerViewItem(accessor: ServicesAccessor, id: string, context?: any): void { + withFocusedFilesExplorerViewItem(accessor).then(res => { + if (res) { + res.explorer.getViewletState().actionProvider.runAction(res.tree, res.item, id, context).done(null, errors.onUnexpectedError); + } + }); +} + +function withVisibleExplorer(accessor: ServicesAccessor): TPromise { + const viewletService = accessor.get(IViewletService); + + const activeViewlet = viewletService.getActiveViewlet(); + if (!activeViewlet || activeViewlet.getId() !== VIEWLET_ID) { + return TPromise.as(void 0); // Return early if the active viewlet is not the explorer + } + + return viewletService.openViewlet(VIEWLET_ID, false) as TPromise; +}; + +export function withFocusedFilesExplorerViewItem(accessor: ServicesAccessor): TPromise<{ explorer: ExplorerViewlet, tree: ITree, item: FileStat }> { + return withFocusedFilesExplorer(accessor).then(res => { + if (!res) { + return void 0; + } + + const { tree, explorer } = res; + if (!tree || !tree.getFocus()) { + return void 0; + } + + return { explorer, tree, item: tree.getFocus() }; + }); +}; + +export function withFocusedFilesExplorer(accessor: ServicesAccessor): TPromise<{ explorer: ExplorerViewlet, tree: ITree }> { + return withVisibleExplorer(accessor).then(explorer => { + if (!explorer || !explorer.getExplorerView()) { + return void 0; // empty folder or hidden explorer + } + + const tree = explorer.getExplorerView().getViewer(); + + // Ignore if in highlight mode or not focused + if (tree.getHighlight() || !tree.isDOMFocused()) { + return void 0; + } + + return { explorer, tree }; + }); +}; + +function withFocusedOpenEditorsViewItem(accessor: ServicesAccessor): TPromise<{ explorer: ExplorerViewlet, tree: ITree, item: OpenEditor }> { + return withVisibleExplorer(accessor).then(explorer => { + if (!explorer || !explorer.getOpenEditorsView()) { + return void 0; // empty folder or hidden explorer + } + + const tree = explorer.getOpenEditorsView().getViewer(); + + // Ignore if in highlight mode or not focused + const focus = tree.getFocus(); + if (tree.getHighlight() || !tree.isDOMFocused() || !(focus instanceof OpenEditor)) { + return void 0; + } + + return { explorer, tree, item: focus }; + }); +}; + +function withFocusedExplorerItem(accessor: ServicesAccessor): TPromise { + return withFocusedFilesExplorerViewItem(accessor).then(res => { + if (res) { + return res.item; + } + + return withFocusedOpenEditorsViewItem(accessor).then(res => { + if (res) { + return res.item as FileStat | OpenEditor; + } + + return void 0; + }); + }); +}; + +export const renameFocusedFilesExplorerViewItemCommand = (accessor: ServicesAccessor) => { + runActionOnFocusedFilesExplorerViewItem(accessor, 'renameFile'); +}; + +export const deleteFocusedFilesExplorerViewItemCommand = (accessor: ServicesAccessor) => { + runActionOnFocusedFilesExplorerViewItem(accessor, 'moveFileToTrash', { useTrash: false }); +}; + +export const moveFocusedFilesExplorerViewItemToTrashCommand = (accessor: ServicesAccessor) => { + runActionOnFocusedFilesExplorerViewItem(accessor, 'moveFileToTrash', { useTrash: true }); +}; + +export const copyFocusedFilesExplorerViewItem = (accessor: ServicesAccessor) => { + runActionOnFocusedFilesExplorerViewItem(accessor, 'filesExplorer.copy'); +}; + +export const copyPathOfFocusedExplorerItem = (accessor: ServicesAccessor) => { + withFocusedExplorerItem(accessor).then(item => { + const file = explorerItemToFileResource(item); + if (file) { + copyPathCommand(accessor, file.resource); + } + }); +}; + +export const openFocusedExplorerItemSideBySideCommand = (accessor: ServicesAccessor) => { + withFocusedExplorerItem(accessor).then(item => { + if (item instanceof FileStat) { + openFocusedFilesExplorerViewItem(accessor, true); + } else { + openFocusedOpenedEditorsViewItem(accessor, true); + } + }); +}; + +export const revealInOSFocusedFilesExplorerItem = (accessor: ServicesAccessor) => { + withFocusedExplorerItem(accessor).then(item => { + const file = explorerItemToFileResource(item); + if (file) { + revealInOSCommand(accessor, file.resource); + } + }); +}; diff --git a/src/vs/workbench/parts/files/browser/fileResultsNavigation.ts b/src/vs/workbench/parts/files/browser/fileResultsNavigation.ts new file mode 100644 index 0000000000..0f72d703df --- /dev/null +++ b/src/vs/workbench/parts/files/browser/fileResultsNavigation.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { Throttler } from 'vs/base/common/async'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; + +export interface IOpenFileOptions { + editorOptions: IEditorOptions; + sideBySide: boolean; + element: any; + payload: any; +} + +export default class FileResultsNavigation extends Disposable { + + private _openFile: Emitter = new Emitter(); + public readonly openFile: Event = this._openFile.event; + + private throttler: Throttler; + + constructor(private tree: ITree) { + super(); + this.throttler = new Throttler(); + this._register(this.tree.addListener('focus', e => this.onFocus(e))); + this._register(this.tree.addListener('selection', e => this.onSelection(e))); + } + + private onFocus(event: any): void { + const element = this.tree.getFocus(); + this.tree.setSelection([element], { fromFocus: true }); + this._openFile.fire({ + editorOptions: { + preserveFocus: true, + pinned: false, + revealIfVisible: true + }, + sideBySide: false, + element, + payload: event.payload + }); + } + + private onSelection({ payload }: any): void { + if (payload && payload.fromFocus) { + return; + } + let keyboard = payload && payload.origin === 'keyboard'; + let originalEvent: KeyboardEvent | MouseEvent = payload && payload.originalEvent; + + let pinned = (payload && payload.origin === 'mouse' && originalEvent && originalEvent.detail === 2); + if (pinned && originalEvent) { + originalEvent.preventDefault(); // focus moves to editor, we need to prevent default + } + + let sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey)); + let preserveFocus = !((keyboard && (!payload || !payload.preserveFocus)) || pinned || (payload && payload.focusEditor)); + this._openFile.fire({ + editorOptions: { + preserveFocus, + pinned, + revealIfVisible: !sideBySide + }, + sideBySide, + element: this.tree.getSelection()[0], + payload + }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/files.contribution.ts b/src/vs/workbench/parts/files/browser/files.contribution.ts new file mode 100644 index 0000000000..d4b047d084 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/files.contribution.ts @@ -0,0 +1,341 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import URI from 'vs/base/common/uri'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ToggleViewletAction } from 'vs/workbench/browser/viewlet'; +import nls = require('vs/nls'); +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory, EditorInput, IFileEditorInput } from 'vs/workbench/common/editor'; +import { AutoSaveConfiguration, HotExitConfiguration, SUPPORTED_ENCODINGS } from 'vs/platform/files/common/files'; +import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { FILE_EDITOR_INPUT_ID, VIEWLET_ID, SortOrderConfiguration } from 'vs/workbench/parts/files/common/files'; +import { FileEditorTracker } from 'vs/workbench/parts/files/common/editors/fileEditorTracker'; +import { SaveErrorHandler } from 'vs/workbench/parts/files/browser/saveErrorHandler'; +import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; +import { TextFileEditor } from 'vs/workbench/parts/files/browser/editors/textFileEditor'; +import { BinaryFileEditor } from 'vs/workbench/parts/files/browser/editors/binaryFileEditor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import * as platform from 'vs/base/common/platform'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { DirtyFilesTracker } from 'vs/workbench/parts/files/common/dirtyFilesTracker'; + +// Viewlet Action +export class OpenExplorerViewletAction extends ToggleViewletAction { + public static ID = VIEWLET_ID; + public static LABEL = nls.localize('showExplorerViewlet', "Show Explorer"); + + constructor( + id: string, + label: string, + @IViewletService viewletService: IViewletService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService + ) { + super(id, label, VIEWLET_ID, viewletService, editorService); + } +} + +// Register Viewlet +Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( + 'vs/workbench/parts/files/browser/explorerViewlet', + 'ExplorerViewlet', + VIEWLET_ID, + nls.localize('explore', "Explorer"), + 'explore', + 0 +)); + +// {{SQL CARBON EDIT}} +// Registry.as(ViewletExtensions.Viewlets).setDefaultViewletId(VIEWLET_ID); + +const openViewletKb: IKeybindings = { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_E +}; + +// Register Action to Open Viewlet +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction( + new SyncActionDescriptor(OpenExplorerViewletAction, OpenExplorerViewletAction.ID, OpenExplorerViewletAction.LABEL, openViewletKb), + 'View: Show Explorer', + nls.localize('view', "View") +); + +// Register file editors +Registry.as(EditorExtensions.Editors).registerEditor( + new EditorDescriptor( + TextFileEditor.ID, // explicit dependency because we don't want these editors lazy loaded + nls.localize('textFileEditor', "Text File Editor"), + 'vs/workbench/parts/files/browser/editors/textFileEditor', + 'TextFileEditor' + ), + [ + new SyncDescriptor(FileEditorInput) + ] +); + +Registry.as(EditorExtensions.Editors).registerEditor( + new EditorDescriptor( + BinaryFileEditor.ID, // explicit dependency because we don't want these editors lazy loaded + nls.localize('binaryFileEditor', "Binary File Editor"), + 'vs/workbench/parts/files/browser/editors/binaryFileEditor', + 'BinaryFileEditor' + ), + [ + new SyncDescriptor(FileEditorInput) + ] +); + +// Register default file input factory +Registry.as(EditorExtensions.Editors).registerFileInputFactory({ + createFileInput: (resource, encoding, instantiationService): IFileEditorInput => { + return instantiationService.createInstance(FileEditorInput, resource, encoding); + } +}); + +interface ISerializedFileInput { + resource: string; + resourceJSON: object; + encoding?: string; +} + +// Register Editor Input Factory +class FileEditorInputFactory implements IEditorInputFactory { + + constructor( + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService + ) { + } + + public serialize(editorInput: EditorInput): string { + const fileEditorInput = editorInput; + const resource = fileEditorInput.getResource(); + const fileInput: ISerializedFileInput = { + resource: resource.toString(), // Keep for backwards compatibility + resourceJSON: resource.toJSON() + }; + + const encoding = fileEditorInput.getPreferredEncoding(); + if (encoding && encoding !== this.configurationService.lookup('files.encoding', { resource }).value) { + fileInput.encoding = encoding; + } + + return JSON.stringify(fileInput); + } + + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): FileEditorInput { + return instantiationService.invokeFunction(accessor => { + const fileInput: ISerializedFileInput = JSON.parse(serializedEditorInput); + const resource = !!fileInput.resourceJSON ? URI.revive(fileInput.resourceJSON) : URI.parse(fileInput.resource); + const encoding = fileInput.encoding; + + return accessor.get(IWorkbenchEditorService).createInput({ resource, encoding }) as FileEditorInput; + }); + } +} + +Registry.as(EditorExtensions.Editors).registerEditorInputFactory(FILE_EDITOR_INPUT_ID, FileEditorInputFactory); + +// Register File Editor Tracker +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + FileEditorTracker +); + +// Register Save Error Handler +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + SaveErrorHandler +); + +// Register Dirty Files Tracker +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + DirtyFilesTracker +); + +// Configuration +const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + +configurationRegistry.registerConfiguration({ + 'id': 'files', + 'order': 9, + 'title': nls.localize('filesConfigurationTitle', "Files"), + 'type': 'object', + 'properties': { + 'files.exclude': { + 'type': 'object', + 'description': nls.localize('exclude', "Configure glob patterns for excluding files and folders."), + 'default': { '**/.git': true, '**/.svn': true, '**/.hg': true, '**/CVS': true, '**/.DS_Store': true }, + 'scope': ConfigurationScope.RESOURCE, + 'additionalProperties': { + 'anyOf': [ + { + 'type': 'boolean', + 'description': nls.localize('files.exclude.boolean', "The glob pattern to match file paths against. Set to true or false to enable or disable the pattern."), + }, + { + 'type': 'object', + 'properties': { + 'when': { + 'type': 'string', // expression ({ "**/*.js": { "when": "$(basename).js" } }) + 'pattern': '\\w*\\$\\(basename\\)\\w*', + 'default': '$(basename).ext', + 'description': nls.localize('files.exclude.when', "Additional check on the siblings of a matching file. Use $(basename) as variable for the matching file name.") + } + } + } + ] + } + }, + 'files.associations': { + 'type': 'object', + 'description': nls.localize('associations', "Configure file associations to languages (e.g. \"*.extension\": \"html\"). These have precedence over the default associations of the languages installed."), + }, + 'files.encoding': { + 'type': 'string', + 'enum': Object.keys(SUPPORTED_ENCODINGS), + 'default': 'utf8', + 'description': nls.localize('encoding', "The default character set encoding to use when reading and writing files."), + 'scope': ConfigurationScope.RESOURCE + }, + 'files.autoGuessEncoding': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('autoGuessEncoding', "When enabled, will attempt to guess the character set encoding when opening files"), + 'scope': ConfigurationScope.RESOURCE + }, + 'files.eol': { + 'type': 'string', + 'enum': [ + '\n', + '\r\n' + ], + 'default': (platform.isLinux || platform.isMacintosh) ? '\n' : '\r\n', + 'description': nls.localize('eol', "The default end of line character. Use \\n for LF and \\r\\n for CRLF."), + 'scope': ConfigurationScope.RESOURCE + }, + 'files.trimTrailingWhitespace': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('trimTrailingWhitespace', "When enabled, will trim trailing whitespace when saving a file."), + 'overridable': true, + 'scope': ConfigurationScope.RESOURCE + }, + 'files.insertFinalNewline': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('insertFinalNewline', "When enabled, insert a final new line at the end of the file when saving it."), + 'overridable': true, + 'scope': ConfigurationScope.RESOURCE + }, + 'files.autoSave': { + 'type': 'string', + 'enum': [AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, , AutoSaveConfiguration.ON_WINDOW_CHANGE], + 'enumDescriptions': [ + nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.off' }, "A dirty file is never automatically saved."), + nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.afterDelay' }, "A dirty file is automatically saved after the configured 'files.autoSaveDelay'."), + nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.onFocusChange' }, "A dirty file is automatically saved when the editor loses focus."), + nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.onWindowChange' }, "A dirty file is automatically saved when the window loses focus.") + ], + 'default': AutoSaveConfiguration.OFF, + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Accepted values: '{0}', '{1}', '{2}' (editor loses focus), '{3}' (window loses focus). If set to '{4}', you can configure the delay in 'files.autoSaveDelay'.", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY) + }, + 'files.autoSaveDelay': { + 'type': 'number', + 'default': 1000, + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveDelay' }, "Controls the delay in ms after which a dirty file is saved automatically. Only applies when 'files.autoSave' is set to '{0}'", AutoSaveConfiguration.AFTER_DELAY) + }, + 'files.watcherExclude': { + 'type': 'object', + 'default': platform.isWindows /* https://github.com/Microsoft/vscode/issues/23954 */ ? { '**/.git/objects/**': true, '**/.git/subtree-cache/**': true, '**/node_modules/*/**': true } : { '**/.git/objects/**': true, '**/.git/subtree-cache/**': true, '**/node_modules/**': true }, + 'description': nls.localize('watcherExclude', "Configure glob patterns of file paths to exclude from file watching. Patterns must match on absolute paths (i.e. prefix with ** or the full path to match properly). Changing this setting requires a restart. When you experience Code consuming lots of cpu time on startup, you can exclude large folders to reduce the initial load."), + 'scope': ConfigurationScope.RESOURCE + }, + 'files.hotExit': { + 'type': 'string', + 'enum': [HotExitConfiguration.OFF, HotExitConfiguration.ON_EXIT, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE], + 'default': HotExitConfiguration.ON_EXIT, + 'enumDescriptions': [ + nls.localize('hotExit.off', 'Disable hot exit.'), + nls.localize('hotExit.onExit', 'Hot exit will be triggered when the application is closed, that is when the last window is closed on Windows/Linux or when the workbench.action.quit command is triggered (command palette, keybinding, menu). All windows with backups will be restored upon next launch.'), + nls.localize('hotExit.onExitAndWindowClose', 'Hot exit will be triggered when the application is closed, that is when the last window is closed on Windows/Linux or when the workbench.action.quit command is triggered (command palette, keybinding, menu), and also for any window with a folder opened regardless of whether it\'s the last window. All windows without folders opened will be restored upon next launch. To restore folder windows as they were before shutdown set "window.restoreWindows" to "all".') + ], + 'description': nls.localize('hotExit', "Controls whether unsaved files are remembered between sessions, allowing the save prompt when exiting the editor to be skipped.", HotExitConfiguration.ON_EXIT, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) + }, + 'files.useExperimentalFileWatcher': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('useExperimentalFileWatcher', "Use the new experimental file watcher.") + }, + 'files.defaultLanguage': { + 'type': 'string', + 'description': nls.localize('defaultLanguage', "The default language mode that is assigned to new files.") + } + } +}); + +configurationRegistry.registerConfiguration({ + id: 'editor', + order: 5, + title: nls.localize('editorConfigurationTitle', "Editor"), + type: 'object', + properties: { + 'editor.formatOnSave': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be auto-saved, and editor must not be shutting down."), + 'overridable': true + } + } +}); + +configurationRegistry.registerConfiguration({ + 'id': 'explorer', + 'order': 10, + 'title': nls.localize('explorerConfigurationTitle', "File Explorer"), + 'type': 'object', + 'properties': { + 'explorer.openEditors.visible': { + 'type': 'number', + 'description': nls.localize({ key: 'openEditorsVisible', comment: ['Open is an adjective'] }, "Number of editors shown in the Open Editors pane. Set it to 0 to hide the pane."), + 'default': 9 + }, + 'explorer.openEditors.dynamicHeight': { + 'type': 'boolean', + 'description': nls.localize({ key: 'dynamicHeight', comment: ['Open is an adjective'] }, "Controls if the height of the open editors section should adapt dynamically to the number of elements or not."), + 'default': true + }, + 'explorer.autoReveal': { + 'type': 'boolean', + 'description': nls.localize('autoReveal', "Controls if the explorer should automatically reveal and select files when opening them."), + 'default': true + }, + 'explorer.enableDragAndDrop': { + 'type': 'boolean', + 'description': nls.localize('enableDragAndDrop', "Controls if the explorer should allow to move files and folders via drag and drop."), + 'default': true + }, + 'explorer.sortOrder': { + 'type': 'string', + 'enum': [SortOrderConfiguration.DEFAULT, SortOrderConfiguration.MIXED, SortOrderConfiguration.FILES_FIRST, SortOrderConfiguration.TYPE, SortOrderConfiguration.MODIFIED], + 'default': SortOrderConfiguration.DEFAULT, + 'enumDescriptions': [ + nls.localize('sortOrder.default', 'Files and folders are sorted by their names, in alphabetical order. Folders are displayed before files.'), + nls.localize('sortOrder.mixed', 'Files and folders are sorted by their names, in alphabetical order. Files are interwoven with folders.'), + nls.localize('sortOrder.filesFirst', 'Files and folders are sorted by their names, in alphabetical order. Files are displayed before folders.'), + nls.localize('sortOrder.type', 'Files and folders are sorted by their extensions, in alphabetical order. Folders are displayed before files.'), + nls.localize('sortOrder.modified', 'Files and folders are sorted by last modified date, in descending order. Folders are displayed before files.') + ], + 'description': nls.localize('sortOrder', "Controls sorting order of files and folders in the explorer. In addition to the default sorting, you can set the order to 'mixed' (files and folders sorted combined), 'type' (by file type), 'modified' (by last modified date) or 'filesFirst' (sort files before folders).") + } + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/AddFile.svg b/src/vs/workbench/parts/files/browser/media/AddFile.svg new file mode 100644 index 0000000000..032aeba43a --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/AddFile.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/AddFile_inverse.svg b/src/vs/workbench/parts/files/browser/media/AddFile_inverse.svg new file mode 100644 index 0000000000..18b8c02c13 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/AddFile_inverse.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/AddFolder.svg b/src/vs/workbench/parts/files/browser/media/AddFolder.svg new file mode 100644 index 0000000000..722f48507f --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/AddFolder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/AddFolder_inverse.svg b/src/vs/workbench/parts/files/browser/media/AddFolder_inverse.svg new file mode 100644 index 0000000000..d7e483bf25 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/AddFolder_inverse.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/CollapseAll.svg b/src/vs/workbench/parts/files/browser/media/CollapseAll.svg new file mode 100644 index 0000000000..7d11a30f6e --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/CollapseAll.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/CollapseAll_inverse.svg b/src/vs/workbench/parts/files/browser/media/CollapseAll_inverse.svg new file mode 100644 index 0000000000..46a65fff71 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/CollapseAll_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/Preview.svg b/src/vs/workbench/parts/files/browser/media/Preview.svg new file mode 100644 index 0000000000..000f46386e --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/Preview.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/Preview_inverse.svg b/src/vs/workbench/parts/files/browser/media/Preview_inverse.svg new file mode 100644 index 0000000000..6b95a3a247 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/Preview_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/Refresh.svg b/src/vs/workbench/parts/files/browser/media/Refresh.svg new file mode 100644 index 0000000000..e034574819 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/Refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/Refresh_inverse.svg b/src/vs/workbench/parts/files/browser/media/Refresh_inverse.svg new file mode 100644 index 0000000000..d79fdaa4e8 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/Refresh_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/action-close-dark.svg b/src/vs/workbench/parts/files/browser/media/action-close-dark.svg new file mode 100644 index 0000000000..751e89b3b0 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/action-close-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/action-close-dirty-dark.svg b/src/vs/workbench/parts/files/browser/media/action-close-dirty-dark.svg new file mode 100644 index 0000000000..02dafab76f --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/action-close-dirty-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/action-close-dirty-focus.svg b/src/vs/workbench/parts/files/browser/media/action-close-dirty-focus.svg new file mode 100644 index 0000000000..33a3b4aeed --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/action-close-dirty-focus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/action-close-dirty.svg b/src/vs/workbench/parts/files/browser/media/action-close-dirty.svg new file mode 100644 index 0000000000..409e5fa539 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/action-close-dirty.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/action-close-focus.svg b/src/vs/workbench/parts/files/browser/media/action-close-focus.svg new file mode 100644 index 0000000000..865c5aaea5 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/action-close-focus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/action-close.svg b/src/vs/workbench/parts/files/browser/media/action-close.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/action-close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/check-inverse.svg b/src/vs/workbench/parts/files/browser/media/check-inverse.svg new file mode 100644 index 0000000000..c225b2f597 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/check-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/check.svg b/src/vs/workbench/parts/files/browser/media/check.svg new file mode 100644 index 0000000000..3f365c4800 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/closeall.svg b/src/vs/workbench/parts/files/browser/media/closeall.svg new file mode 100644 index 0000000000..7250ff6b54 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/closeall.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/closeall_inverse.svg b/src/vs/workbench/parts/files/browser/media/closeall_inverse.svg new file mode 100644 index 0000000000..74e8dd8a02 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/closeall_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/collapsed-dark.svg b/src/vs/workbench/parts/files/browser/media/collapsed-dark.svg new file mode 100644 index 0000000000..cf5c3641aa --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/collapsed-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/collapsed-hc.svg b/src/vs/workbench/parts/files/browser/media/collapsed-hc.svg new file mode 100644 index 0000000000..145c763338 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/collapsed-hc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/collapsed.svg b/src/vs/workbench/parts/files/browser/media/collapsed.svg new file mode 100644 index 0000000000..3a63808c35 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/collapsed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/expanded-dark.svg b/src/vs/workbench/parts/files/browser/media/expanded-dark.svg new file mode 100644 index 0000000000..73d41e6399 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/expanded-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/expanded-hc.svg b/src/vs/workbench/parts/files/browser/media/expanded-hc.svg new file mode 100644 index 0000000000..d38d4abc89 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/expanded-hc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/expanded.svg b/src/vs/workbench/parts/files/browser/media/expanded.svg new file mode 100644 index 0000000000..75f73adbb0 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/expanded.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/browser/media/explorerviewlet.css new file mode 100644 index 0000000000..2bcbd3996d --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/explorerviewlet.css @@ -0,0 +1,189 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Activity Bar */ +.monaco-workbench > .activitybar .monaco-action-bar .action-label.explore { + -webkit-mask: url('files-dark.svg') no-repeat 50% 50%; +} + +/* --- Explorer viewlet --- */ +.explorer-viewlet { + height: 100%; +} + +.explorer-viewlet .explorer-item, +.explorer-viewlet .open-editor, +.explorer-viewlet .editor-group { + height: 22px; + line-height: 22px; +} + +.explorer-viewlet .explorer-item { + display: flex; /* this helps showing the overflow ellipsis (...) even though we use display:inline-block for the labels */ + flex-wrap: nowrap; +} + +.explorer-viewlet .explorer-item > a, +.explorer-viewlet .open-editor, +.explorer-viewlet .editor-group { + text-overflow: ellipsis; + overflow: hidden; +} + +.explorer-viewlet .explorer-item, +.explorer-viewlet .explorer-item .monaco-inputbox { + flex: 1; +} + +.explorer-viewlet .explorer-item::before { + flex-shrink: 0; /* fix for https://github.com/Microsoft/vscode/issues/13787 */ +} + +.explorer-viewlet .explorer-item.explorer-item-edited .label-name { + flex: 0; /* do not steal space when label is hidden because we are in edit mode */ +} + +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row > .content { + display: flex; +} + +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row > .content > .monaco-action-bar { + visibility: hidden; +} + +.explorer-viewlet .header .monaco-count-badge.hidden { + display: none; + visibility: hidden; +} + +.explorer-folders-view .monaco-tree-row .content { + display: flex; +} + +.explorer-folders-view .monaco-tree-row .content::before { + background-size: 16px; + background-position: 50% 50%; + background-repeat: no-repeat; + padding-right: 6px; + width: 16px; + height: 22px; + display: inline-block; + vertical-align: top; + content: ' '; +} + +.explorer-folders-view.align-icons-and-twisties .monaco-tree-row:not(.has-children) .content::before { + display: none; +} + +.explorer-folders-view .monaco-tree-row.has-children.expanded .content::before { + background-image: url("expanded.svg"); +} + +.explorer-folders-view .monaco-tree-row.has-children .content::before { + display: inline-block; + background-image: url("collapsed.svg"); +} + +.vs-dark .explorer-folders-view .monaco-tree-row.has-children.expanded .content::before { + background-image: url("expanded-dark.svg"); +} + +.vs-dark .explorer-folders-view .monaco-tree-row.has-children .content::before { + background-image: url("collapsed-dark.svg"); +} + +.hc-black .explorer-folders-view .monaco-tree-row.has-children.expanded .content::before { + background-image: url("expanded-hc.svg"); +} + +.hc-black .explorer-folders-view .monaco-tree-row.has-children .content::before { + background-image: url("collapsed-hc.svg"); +} + +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row:hover > .content .monaco-action-bar, +.explorer-viewlet .explorer-open-editors .monaco-tree.focused .monaco-tree-row.focused > .content .monaco-action-bar, +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row > .content.dirty > .monaco-action-bar { + visibility: visible; +} + +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row > .content .monaco-action-bar .action-label { + display: block; +} + +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row > .content .monaco-action-bar .close-editor-action { + width: 8px; + height: 22px; +} + +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row > .content .monaco-action-bar .action-close-all-files, +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row > .content .monaco-action-bar .save-all { + width: 23px; + height: 22px; +} + +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row > .content > .open-editor { + flex: 1; +} + +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row > .content > .editor-group { + flex: 1; +} + +.explorer-viewlet .monaco-count-badge { + padding: 1px 6px; + margin-left: 6px; + border-radius: 0; /* goes better when ellipsis shows up on narrow sidebar */ +} + +.explorer-viewlet .explorer-empty-view { + padding: 0 20px 0 20px; +} + +.explorer-viewlet .explorer-item.nonexistent-root { + opacity: 0.5; +} + +.explorer-viewlet .explorer-item .monaco-inputbox { + width: 100%; + line-height: normal; +} + +.monaco-workbench.linux .explorer-viewlet .explorer-item .monaco-inputbox, +.monaco-workbench.mac .explorer-viewlet .explorer-item .monaco-inputbox { + height: 22px; +} + +.explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input { + padding: 1px 2px; +} + +.monaco-workbench.linux .explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input, +.monaco-workbench.mac .explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input { + padding: 0; + height: 20px; +} + +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row .editor-group { + font-size: 11px; + font-weight: bold; + text-transform: uppercase; + cursor: default; +} + +/* Bold font style does not go well with CJK fonts */ +.explorer-viewlet:lang(zh-Hans) .explorer-open-editors .monaco-tree .monaco-tree-row .editor-group, +.explorer-viewlet:lang(zh-Hant) .explorer-open-editors .monaco-tree .monaco-tree-row .editor-group, +.explorer-viewlet:lang(ja) .explorer-open-editors .monaco-tree .monaco-tree-row .editor-group, +.explorer-viewlet:lang(ko) .explorer-open-editors .monaco-tree .monaco-tree-row .editor-group { + font-weight: normal; +} + +/* High Contrast Theming */ +.hc-black .monaco-workbench .explorer-viewlet .explorer-item, +.hc-black .monaco-workbench .explorer-viewlet .open-editor, +.hc-black .monaco-workbench .explorer-viewlet .editor-group { + line-height: 20px; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/fileactions.css b/src/vs/workbench/parts/files/browser/media/fileactions.css new file mode 100644 index 0000000000..668248c2fd --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/fileactions.css @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .explorer-viewlet .action-close-all-files { + background: url("closeall.svg") center center no-repeat; +} + +.vs-dark .monaco-workbench .explorer-viewlet .action-close-all-files, +.hc-black .monaco-workbench .explorer-viewlet .action-close-all-files { + background: url("closeall_inverse.svg") center center no-repeat; +} + +.monaco-workbench .explorer-action.new-file { + background: url('AddFile.svg') center center no-repeat; +} + +.monaco-workbench .explorer-action.save-all { + background: url('saveall.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .explorer-action.save-all, +.hc-blck .monaco-workbench .explorer-action.save-all { + background: url('saveall_inverse.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .explorer-action.new-file, +.hc-black .monaco-workbench .explorer-action.new-file { + background: url('AddFile_inverse.svg') center center no-repeat; +} + +.monaco-workbench .explorer-action.new-folder { + background: url('AddFolder.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .explorer-action.new-folder, +.hc-black .monaco-workbench .explorer-action.new-folder { + background: url('AddFolder_inverse.svg') center center no-repeat; +} + +.monaco-workbench .explorer-action.refresh-explorer { + background: url('Refresh.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .explorer-action.refresh-explorer, +.hc-black .monaco-workbench .explorer-action.refresh-explorer { + background: url('Refresh_inverse.svg') center center no-repeat; +} + +.monaco-workbench .explorer-action.collapse-explorer { + background: url('CollapseAll.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .explorer-action.collapse-explorer, +.hc-black .monaco-workbench .explorer-action.collapse-explorer { + background: url('CollapseAll_inverse.svg') center center no-repeat; +} + +.monaco-workbench .quick-open-sidebyside-vertical { + background-image: url('split-editor-vertical.svg'); +} + +.vs-dark .monaco-workbench .quick-open-sidebyside-vertical, +.hc-black .monaco-workbench .quick-open-sidebyside-vertical { + background-image: url('split-editor-vertical-inverse.svg'); +} + +.monaco-workbench .quick-open-sidebyside-horizontal { + background-image: url('split-editor-horizontal.svg'); +} + +.vs-dark .monaco-workbench .quick-open-sidebyside-horizontal, +.hc-black .monaco-workbench .quick-open-sidebyside-horizontal { + background-image: url('split-editor-horizontal-inverse.svg'); +} + +.monaco-workbench .save-conflict-action-accept-changes { + background: url('check.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .save-conflict-action-accept-changes { + background: url('check-inverse.svg') center center no-repeat; +} + +.monaco-workbench .save-conflict-action-revert-changes { + background: url('undo.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .save-conflict-action-revert-changes { + background: url('undo-inverse.svg') center center no-repeat; +} + +.monaco-workbench .file-editor-action.action-open-preview { + background: url('Preview.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .file-editor-action.action-open-preview , +.hc-black .monaco-workbench .file-editor-action.action-open-preview { + background: url('Preview_inverse.svg') center center no-repeat; +} + +.explorer-viewlet .explorer-open-editors .close-editor-action { + background: url("action-close.svg") center center no-repeat; +} + +.explorer-viewlet .explorer-open-editors .focused .monaco-tree-row.selected:not(.highlighted) > .content .close-editor-action { + background: url("action-close-focus.svg") center center no-repeat; +} + +.explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row:not(:hover) > .content.dirty > .monaco-action-bar .close-editor-action { + background: url("action-close-dirty.svg") center center no-repeat; +} + +.vs-dark .explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row:not(:hover) > .content.dirty > .monaco-action-bar .close-editor-action, +.hc-black .monaco-workbench .explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row:not(:hover) > .content.dirty > .monaco-action-bar .close-editor-action { + background: url("action-close-dirty-dark.svg") center center no-repeat; +} + +.explorer-viewlet .explorer-open-editors .monaco-tree.focused .monaco-tree-row.selected:not(:hover) > .content.dirty > .monaco-action-bar .close-editor-action { + background: url("action-close-dirty-focus.svg") center center no-repeat; +} + +.vs-dark .monaco-workbench .explorer-viewlet .explorer-open-editors .close-editor-action, +.hc-black .monaco-workbench .explorer-viewlet .explorer-open-editors .close-editor-action { + background: url("action-close-dark.svg") center center no-repeat; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/files-dark.svg b/src/vs/workbench/parts/files/browser/media/files-dark.svg new file mode 100644 index 0000000000..7e3e59b370 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/files-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/saveall.svg b/src/vs/workbench/parts/files/browser/media/saveall.svg new file mode 100644 index 0000000000..5f036a20f7 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/saveall.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/saveall_inverse.svg b/src/vs/workbench/parts/files/browser/media/saveall_inverse.svg new file mode 100644 index 0000000000..6b395b40d6 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/saveall_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/split-editor-horizontal-inverse.svg b/src/vs/workbench/parts/files/browser/media/split-editor-horizontal-inverse.svg new file mode 100644 index 0000000000..4969d2e785 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/split-editor-horizontal-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/split-editor-horizontal.svg b/src/vs/workbench/parts/files/browser/media/split-editor-horizontal.svg new file mode 100644 index 0000000000..c307f0142b --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/split-editor-horizontal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/split-editor-vertical-inverse.svg b/src/vs/workbench/parts/files/browser/media/split-editor-vertical-inverse.svg new file mode 100644 index 0000000000..4eab753669 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/split-editor-vertical-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/split-editor-vertical.svg b/src/vs/workbench/parts/files/browser/media/split-editor-vertical.svg new file mode 100644 index 0000000000..3eeaf7c536 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/split-editor-vertical.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/undo-inverse.svg b/src/vs/workbench/parts/files/browser/media/undo-inverse.svg new file mode 100644 index 0000000000..c060a04160 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/undo-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/undo.svg b/src/vs/workbench/parts/files/browser/media/undo.svg new file mode 100644 index 0000000000..4fe7069372 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/media/undo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/saveErrorHandler.ts b/src/vs/workbench/parts/files/browser/saveErrorHandler.ts new file mode 100644 index 0000000000..0b0989fd6a --- /dev/null +++ b/src/vs/workbench/parts/files/browser/saveErrorHandler.ts @@ -0,0 +1,312 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import errors = require('vs/base/common/errors'); +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import paths = require('vs/base/common/paths'); +import { Action } from 'vs/base/common/actions'; +import URI from 'vs/base/common/uri'; +import { SaveFileAsAction, RevertFileAction, SaveFileAction } from 'vs/workbench/parts/files/browser/fileActions'; +import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, IMessageWithAction, Severity, CancelAction } from 'vs/platform/message/common/message'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { ResourceMap } from 'vs/base/common/map'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IMode } from 'vs/editor/common/modes'; + +export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext'; +export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution'; + +// A handler for save error happening with conflict resolution actions +export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContribution, ITextModelContentProvider { + private messages: ResourceMap<() => void>; + private toUnbind: IDisposable[]; + private conflictResolutionContext: IContextKey; + + constructor( + @IMessageService private messageService: IMessageService, + @ITextFileService private textFileService: ITextFileService, + @ITextModelService private textModelResolverService: ITextModelService, + @IModelService private modelService: IModelService, + @IModeService private modeService: IModeService, + @IInstantiationService private instantiationService: IInstantiationService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IContextKeyService contextKeyService: IContextKeyService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + this.messages = new ResourceMap<() => void>(); + this.conflictResolutionContext = new RawContextKey(CONFLICT_RESOLUTION_CONTEXT, false).bindTo(contextKeyService); + this.toUnbind = []; + + // Register as text model content provider that supports to load a resource as it actually + // is stored on disk as opposed to using the file:// scheme that will return a dirty buffer + // if there is one. + this.textModelResolverService.registerTextModelContentProvider(CONFLICT_RESOLUTION_SCHEME, this); + + // Hook into model + TextFileEditorModel.setSaveErrorHandler(this); + + this.registerListeners(); + } + + public provideTextContent(resource: URI): TPromise { + const fileOnDiskResource = URI.file(resource.fsPath); + + // Make sure our file from disk is resolved up to date + return this.textFileService.resolveTextContent(fileOnDiskResource).then(content => { + let codeEditorModel = this.modelService.getModel(resource); + if (codeEditorModel) { + this.modelService.updateModel(codeEditorModel, content.value); + } else { + const fileOnDiskModel = this.modelService.getModel(fileOnDiskResource); + + let mode: TPromise; + if (fileOnDiskModel) { + mode = this.modeService.getOrCreateMode(fileOnDiskModel.getModeId()); + } else { + mode = this.modeService.getOrCreateModeByFilenameOrFirstLine(fileOnDiskResource.fsPath); + } + + codeEditorModel = this.modelService.createModel(content.value, mode, resource); + } + + return codeEditorModel; + }); + } + + public getId(): string { + return 'vs.files.saveerrorhandler'; + } + + private registerListeners(): void { + this.toUnbind.push(this.textFileService.models.onModelSaved(e => this.onFileSavedOrReverted(e.resource))); + this.toUnbind.push(this.textFileService.models.onModelReverted(e => this.onFileSavedOrReverted(e.resource))); + this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); + } + + private onEditorsChanged(): void { + let isActiveEditorSaveConflictResolution = false; + const activeEditor = this.editorService.getActiveEditor(); + + if (activeEditor && activeEditor.input instanceof DiffEditorInput && activeEditor.input.originalInput instanceof ResourceEditorInput) { + const resource = activeEditor.input.originalInput.getResource(); + isActiveEditorSaveConflictResolution = resource && resource.scheme === CONFLICT_RESOLUTION_SCHEME; + } + + this.conflictResolutionContext.set(isActiveEditorSaveConflictResolution); + } + + private onFileSavedOrReverted(resource: URI): void { + const hideMessage = this.messages.get(resource); + if (hideMessage) { + hideMessage(); + this.messages.delete(resource); + } + } + + public onSaveError(error: any, model: ITextFileEditorModel): void { + let message: IMessageWithAction; + const resource = model.getResource(); + + // Dirty write prevention + if ((error).fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE) { + message = this.instantiationService.createInstance(ResolveSaveConflictMessage, model, null); + } + + // Any other save error + else { + const isReadonly = (error).fileOperationResult === FileOperationResult.FILE_READ_ONLY; + const actions: Action[] = []; + + // Save As + actions.push(new Action('workbench.files.action.saveAs', SaveFileAsAction.LABEL, null, true, () => { + const saveAsAction = this.instantiationService.createInstance(SaveFileAsAction, SaveFileAsAction.ID, SaveFileAsAction.LABEL); + saveAsAction.setResource(resource); + saveAsAction.run().done(() => saveAsAction.dispose(), errors.onUnexpectedError); + + return TPromise.as(true); + })); + + // Discard + actions.push(new Action('workbench.files.action.discard', nls.localize('discard', "Discard"), null, true, () => { + const revertFileAction = this.instantiationService.createInstance(RevertFileAction, RevertFileAction.ID, RevertFileAction.LABEL); + revertFileAction.setResource(resource); + revertFileAction.run().done(() => revertFileAction.dispose(), errors.onUnexpectedError); + + return TPromise.as(true); + })); + + // Retry + if (isReadonly) { + actions.push(new Action('workbench.files.action.overwrite', nls.localize('overwrite', "Overwrite"), null, true, () => { + if (!model.isDisposed()) { + model.save({ overwriteReadonly: true }).done(null, errors.onUnexpectedError); + } + + return TPromise.as(true); + })); + } else { + actions.push(new Action('workbench.files.action.retry', nls.localize('retry', "Retry"), null, true, () => { + const saveFileAction = this.instantiationService.createInstance(SaveFileAction, SaveFileAction.ID, SaveFileAction.LABEL); + saveFileAction.setResource(resource); + saveFileAction.run().done(() => saveFileAction.dispose(), errors.onUnexpectedError); + + return TPromise.as(true); + })); + } + + // Cancel + actions.push(CancelAction); + + let errorMessage: string; + if (isReadonly) { + errorMessage = nls.localize('readonlySaveError', "Failed to save '{0}': File is write protected. Select 'Overwrite' to remove protection.", paths.basename(resource.fsPath)); + } else { + errorMessage = nls.localize('genericSaveError', "Failed to save '{0}': {1}", paths.basename(resource.fsPath), toErrorMessage(error, false)); + } + + message = { + message: errorMessage, + actions + }; + } + + // Show message and keep function to hide in case the file gets saved/reverted + this.messages.set(model.getResource(), this.messageService.show(Severity.Error, message)); + } + + public dispose(): void { + this.toUnbind = dispose(this.toUnbind); + + this.messages.clear(); + } +} + +const pendingResolveSaveConflictMessages: Function[] = []; +function clearPendingResolveSaveConflictMessages(): void { + while (pendingResolveSaveConflictMessages.length > 0) { + pendingResolveSaveConflictMessages.pop()(); + } +} + +// A message with action to resolve a save conflict +class ResolveSaveConflictMessage implements IMessageWithAction { + public message: string; + public actions: Action[]; + + private model: ITextFileEditorModel; + + constructor( + model: ITextFileEditorModel, + message: string, + @IMessageService private messageService: IMessageService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + this.model = model; + + const resource = model.getResource(); + if (message) { + this.message = message; + } else { + this.message = nls.localize('staleSaveError', "Failed to save '{0}': The content on disk is newer. Click on **Compare** to compare your version with the one on disk.", paths.basename(resource.fsPath)); + } + + this.actions = [ + new Action('workbench.files.action.resolveConflict', nls.localize('compareChanges', "Compare"), null, true, () => { + if (!this.model.isDisposed()) { + const name = paths.basename(resource.fsPath); + const editorLabel = nls.localize('saveConflictDiffLabel', "{0} (on disk) ↔ {1} (in {2}) - Resolve save conflict", name, name, this.environmentService.appNameLong); + + return this.editorService.openEditor({ leftResource: URI.from({ scheme: CONFLICT_RESOLUTION_SCHEME, path: resource.fsPath }), rightResource: resource, label: editorLabel, options: { pinned: true } }).then(() => { + + // Inform user + pendingResolveSaveConflictMessages.push(this.messageService.show(Severity.Info, nls.localize('userGuide', "Use the actions in the editor tool bar to the right to either **undo** your changes or **overwrite** the content on disk with your changes"))); + }); + } + + return TPromise.as(true); + }) + ]; + } +} + +export const acceptLocalChangesCommand = (accessor: ServicesAccessor, resource: URI) => { + const editorService = accessor.get(IWorkbenchEditorService); + const resolverService = accessor.get(ITextModelService); + + const editor = editorService.getActiveEditor(); + const input = editor.input; + const position = editor.position; + + resolverService.createModelReference(resource).then(reference => { + const model = reference.object as ITextFileEditorModel; + const localModelValue = model.getValue(); + + clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions + + // revert to be able to save + return model.revert().then(() => { + + // Restore user value + model.textEditorModel.setValue(localModelValue); + + // Trigger save + return model.save().then(() => { + + // Reopen file input + return editorService.openEditor({ resource: model.getResource() }, position).then(() => { + + // Clean up + input.dispose(); + reference.dispose(); + }); + }); + }); + }); +}; + +export const revertLocalChangesCommand = (accessor: ServicesAccessor, resource: URI) => { + const editorService = accessor.get(IWorkbenchEditorService); + const resolverService = accessor.get(ITextModelService); + + const editor = editorService.getActiveEditor(); + const input = editor.input; + const position = editor.position; + + resolverService.createModelReference(resource).then(reference => { + const model = reference.object as ITextFileEditorModel; + + clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions + + // Revert on model + return model.revert().then(() => { + + // Reopen file input + return editorService.openEditor({ resource: model.getResource() }, position).then(() => { + + // Clean up + input.dispose(); + reference.dispose(); + }); + }); + }); +}; diff --git a/src/vs/workbench/parts/files/browser/views/emptyView.ts b/src/vs/workbench/parts/files/browser/views/emptyView.ts new file mode 100644 index 0000000000..3ef134e4c1 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/views/emptyView.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import * as errors from 'vs/base/common/errors'; +import env = require('vs/base/common/platform'); +import DOM = require('vs/base/browser/dom'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction } from 'vs/base/common/actions'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { $ } from 'vs/base/browser/builder'; +import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/parts/views/browser/views'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview'; + +export class EmptyView extends CollapsibleView { + + public static ID: string = 'workbench.explorer.emptyView'; + public static NAME = nls.localize('noWorkspace', "No Folder Opened"); + + private openFolderButton: Button; + + constructor( + initialSize: number, + options: IViewletViewOptions, + @IThemeService private themeService: IThemeService, + @IInstantiationService private instantiationService: IInstantiationService, + @IKeybindingService keybindingService: IKeybindingService, + @IContextMenuService contextMenuService: IContextMenuService + ) { + super(initialSize, { ...(options as IViewOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section"), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService); + } + + public renderHeader(container: HTMLElement): void { + let titleDiv = $('div.title').appendTo(container); + $('span').text(this.name).appendTo(titleDiv); + } + + protected renderBody(container: HTMLElement): void { + DOM.addClass(container, 'explorer-empty-view'); + + let titleDiv = $('div.section').appendTo(container); + $('p').text(nls.localize('noWorkspaceHelp', "You have not yet opened a folder.")).appendTo(titleDiv); + + let section = $('div.section').appendTo(container); + + this.openFolderButton = new Button(section); + attachButtonStyler(this.openFolderButton, this.themeService); + this.openFolderButton.label = nls.localize('openFolder', "Open Folder"); + this.openFolderButton.addListener('click', () => { + const actionClass = env.isMacintosh ? OpenFileFolderAction : OpenFolderAction; + const action = this.instantiationService.createInstance(actionClass, actionClass.ID, actionClass.LABEL); + this.actionRunner.run(action).done(() => { + action.dispose(); + }, err => { + action.dispose(); + errors.onUnexpectedError(err); + }); + }); + } + + layoutBody(size: number): void { + // no-op + } + + public create(): TPromise { + return TPromise.as(null); + } + + public setVisible(visible: boolean): TPromise { + return TPromise.as(null); + } + + public focusBody(): void { + if (this.openFolderButton) { + this.openFolderButton.getElement().focus(); + } + } + + protected reveal(element: any, relativeTop?: number): TPromise { + return TPromise.as(null); + } + + public getActions(): IAction[] { + return []; + } + + public getSecondaryActions(): IAction[] { + return []; + } + + public getActionItem(action: IAction): IActionItem { + return null; + } + + public shutdown(): void { + // Subclass to implement + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/views/explorerView.ts b/src/vs/workbench/parts/files/browser/views/explorerView.ts new file mode 100644 index 0000000000..483671dafb --- /dev/null +++ b/src/vs/workbench/parts/files/browser/views/explorerView.ts @@ -0,0 +1,946 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { Builder, $ } from 'vs/base/browser/builder'; +import URI from 'vs/base/common/uri'; +import { ThrottledDelayer } from 'vs/base/common/async'; +import errors = require('vs/base/common/errors'); +import labels = require('vs/base/common/labels'); +import paths = require('vs/base/common/paths'); +import glob = require('vs/base/common/glob'); +import { Action, IAction } from 'vs/base/common/actions'; +import { prepareActions } from 'vs/workbench/browser/actions'; +import { memoize } from 'vs/base/common/decorators'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, SortOrderConfiguration, SortOrder } from 'vs/workbench/parts/files/common/files'; +import { FileOperation, FileOperationEvent, IResolveFileOptions, FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; +import { RefreshViewExplorerAction, NewFolderAction, NewFileAction } from 'vs/workbench/parts/files/browser/fileActions'; +import { FileDragAndDrop, FileFilter, FileSorter, FileController, FileRenderer, FileDataSource, FileViewletState, FileAccessibilityProvider } from 'vs/workbench/parts/files/browser/views/explorerViewer'; +import { toResource } from 'vs/workbench/common/editor'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import * as DOM from 'vs/base/browser/dom'; +import { CollapseAction } from 'vs/workbench/browser/viewlet'; +import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/parts/views/browser/views'; +import { FileStat, Model } from 'vs/workbench/parts/files/common/explorerModel'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ResourceContextKey, ResourceGlobMatcher } from 'vs/workbench/common/resources'; +import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { isLinux } from 'vs/base/common/platform'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview'; + +export interface IExplorerViewOptions extends IViewletViewOptions { + viewletState: FileViewletState; +} + +export class ExplorerView extends CollapsibleView { + + public static ID: string = 'workbench.explorer.fileView'; + private static EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first + private static EXPLORER_FILE_CHANGES_REFRESH_DELAY = 100; // delay in ms to refresh the explorer from disk file changes + private static EXPLORER_IMPORT_REFRESH_DELAY = 300; // delay in ms to refresh the explorer from imports + + private static MEMENTO_LAST_ACTIVE_FILE_RESOURCE = 'explorer.memento.lastActiveFileResource'; + private static MEMENTO_EXPANDED_FOLDER_RESOURCES = 'explorer.memento.expandedFolderResources'; + + public readonly id: string = ExplorerView.ID; + + private explorerViewer: ITree; + private filter: FileFilter; + private viewletState: FileViewletState; + + private explorerRefreshDelayer: ThrottledDelayer; + private explorerImportDelayer: ThrottledDelayer; + + private resourceContext: ResourceContextKey; + private folderContext: IContextKey; + + private filesExplorerFocusedContext: IContextKey; + private explorerFocusedContext: IContextKey; + + private fileEventsFilter: ResourceGlobMatcher; + + private shouldRefresh: boolean; + private autoReveal: boolean; + private sortOrder: SortOrder; + private settings: object; + + constructor( + initialSize: number, + options: IExplorerViewOptions, + @IMessageService private messageService: IMessageService, + @IContextMenuService contextMenuService: IContextMenuService, + @IInstantiationService private instantiationService: IInstantiationService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IProgressService private progressService: IProgressService, + @IListService private listService: IListService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IFileService private fileService: IFileService, + @IPartService private partService: IPartService, + @IKeybindingService keybindingService: IKeybindingService, + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService private configurationService: IConfigurationService, + @IWorkbenchThemeService private themeService: IWorkbenchThemeService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + super(initialSize, { ...(options as IViewOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section"), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService); + + this.settings = options.viewletSettings; + this.viewletState = options.viewletState; + this.actionRunner = options.actionRunner; + this.autoReveal = true; + + this.explorerRefreshDelayer = new ThrottledDelayer(ExplorerView.EXPLORER_FILE_CHANGES_REFRESH_DELAY); + this.explorerImportDelayer = new ThrottledDelayer(ExplorerView.EXPLORER_IMPORT_REFRESH_DELAY); + + this.resourceContext = instantiationService.createInstance(ResourceContextKey); + this.folderContext = ExplorerFolderContext.bindTo(contextKeyService); + + this.filesExplorerFocusedContext = FilesExplorerFocusedContext.bindTo(contextKeyService); + this.explorerFocusedContext = ExplorerFocusedContext.bindTo(contextKeyService); + + this.fileEventsFilter = instantiationService.createInstance(ResourceGlobMatcher, root => this.getFileEventsExcludes(root), (expression: glob.IExpression) => glob.parse(expression)); + } + + private getFileEventsExcludes(root?: URI): glob.IExpression { + const scope = root ? { resource: root } : void 0; + const configuration = this.configurationService.getConfiguration(undefined, scope); + + return (configuration && configuration.files && configuration.files.exclude) || Object.create(null); + } + + public renderHeader(container: HTMLElement): void { + const titleDiv = $('div.title').appendTo(container); + const titleSpan = $('span').appendTo(titleDiv); + const setHeader = () => { + const workspace = this.contextService.getWorkspace(); + const title = workspace.roots.map(root => labels.getPathLabel(root.fsPath, void 0, this.environmentService)).join(); + titleSpan.text(this.name).title(title); + }; + this.toDispose.push(this.contextService.onDidChangeWorkspaceName(() => setHeader())); + setHeader(); + + super.renderHeader(container); + } + + public get name(): string { + return this.contextService.getWorkspace().name; + } + + public set name(value) { + // noop + } + + public renderBody(container: HTMLElement): void { + this.treeContainer = super.renderViewTree(container); + DOM.addClass(this.treeContainer, 'explorer-folders-view'); + DOM.addClass(this.treeContainer, 'show-file-icons'); + + this.tree = this.createViewer($(this.treeContainer)); + + if (this.toolBar) { + this.toolBar.setActions(prepareActions(this.getActions()), this.getSecondaryActions())(); + } + + const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => { + DOM.toggleClass(this.treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons); + }; + + this.toDispose.push(this.themeService.onDidFileIconThemeChange(onFileIconThemeChange)); + this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => this.refreshFromEvent())); + onFileIconThemeChange(this.themeService.getFileIconTheme()); + } + + public getActions(): IAction[] { + const actions: Action[] = []; + + actions.push(this.instantiationService.createInstance(NewFileAction, this.getViewer(), null)); + actions.push(this.instantiationService.createInstance(NewFolderAction, this.getViewer(), null)); + actions.push(this.instantiationService.createInstance(RefreshViewExplorerAction, this, 'explorer-action refresh-explorer')); + actions.push(this.instantiationService.createInstance(CollapseAction, this.getViewer(), true, 'explorer-action collapse-explorer')); + + // Set Order + for (let i = 0; i < actions.length; i++) { + const action = actions[i]; + action.order = 10 * (i + 1); + } + + return actions; + } + + public create(): TPromise { + + // Update configuration + const configuration = this.configurationService.getConfiguration(); + this.onConfigurationUpdated(configuration); + + // Load and Fill Viewer + return this.doRefresh().then(() => { + + // When the explorer viewer is loaded, listen to changes to the editor input + this.toDispose.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); + + // Also handle configuration updates + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration(), true))); + }); + } + + private onEditorsChanged(): void { + if (!this.autoReveal) { + return; // do not touch selection or focus if autoReveal === false + } + + let clearSelection = true; + let clearFocus = false; + + // Handle files + const activeFile = this.getActiveFile(); + if (activeFile) { + + // Always remember last opened file + this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = activeFile.toString(); + + // Select file if input is inside workspace + if (this.isVisible() && this.contextService.isInsideWorkspace(activeFile)) { + const selection = this.hasSelection(activeFile); + if (!selection) { + this.select(activeFile).done(null, errors.onUnexpectedError); + } + + clearSelection = false; + } + } + + // Handle closed or untitled file (convince explorer to not reopen any file when getting visible) + const activeInput = this.editorService.getActiveEditorInput(); + if (!activeInput || toResource(activeInput, { supportSideBySide: true, filter: 'untitled' })) { + this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = void 0; + clearFocus = true; + } + + // Otherwise clear + if (clearSelection) { + this.explorerViewer.clearSelection(); + } + + if (clearFocus) { + this.explorerViewer.clearFocus(); + } + } + + private onConfigurationUpdated(configuration: IFilesConfiguration, refresh?: boolean): void { + if (this.isDisposed) { + return; // guard against possible race condition when config change causes recreate of views + } + + this.autoReveal = configuration && configuration.explorer && configuration.explorer.autoReveal; + + // Push down config updates to components of viewer + let needsRefresh = false; + if (this.filter) { + needsRefresh = this.filter.updateConfiguration(); + } + + const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; + if (this.sortOrder !== configSortOrder) { + this.sortOrder = configSortOrder; + needsRefresh = true; + } + + // Refresh viewer as needed + if (refresh && needsRefresh) { + this.doRefresh().done(null, errors.onUnexpectedError); + } + } + + public focusBody(): void { + let keepFocus = false; + + // Make sure the current selected element is revealed + if (this.explorerViewer) { + if (this.autoReveal) { + const selection = this.explorerViewer.getSelection(); + if (selection.length > 0) { + this.reveal(selection[0], 0.5).done(null, errors.onUnexpectedError); + } + } + + // Pass Focus to Viewer + this.explorerViewer.DOMFocus(); + keepFocus = true; + } + + // Open the focused element in the editor if there is currently no file opened + const activeFile = this.getActiveFile(); + if (!activeFile) { + this.openFocusedElement(keepFocus); + } + } + + public setVisible(visible: boolean): TPromise { + return super.setVisible(visible).then(() => { + + // Show + if (visible) { + + // If a refresh was requested and we are now visible, run it + let refreshPromise = TPromise.as(null); + if (this.shouldRefresh) { + refreshPromise = this.doRefresh(); + this.shouldRefresh = false; // Reset flag + } + + if (!this.autoReveal) { + return refreshPromise; // do not react to setVisible call if autoReveal === false + } + + // Always select the current navigated file in explorer if input is file editor input + // unless autoReveal is set to false + const activeFile = this.getActiveFile(); + if (activeFile) { + return refreshPromise.then(() => { + return this.select(activeFile); + }); + } + + // Return now if the workbench has not yet been created - in this case the workbench takes care of restoring last used editors + if (!this.partService.isCreated()) { + return TPromise.as(null); + } + + // Otherwise restore last used file: By lastActiveFileResource + let lastActiveFileResource: URI; + if (this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) { + lastActiveFileResource = URI.parse(this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]); + } + + if (lastActiveFileResource && this.isCreated && this.model.findClosest(lastActiveFileResource)) { + this.editorService.openEditor({ resource: lastActiveFileResource, options: { revealIfVisible: true } }).done(null, errors.onUnexpectedError); + + return refreshPromise; + } + + // Otherwise restore last used file: By Explorer selection + return refreshPromise.then(() => { + this.openFocusedElement(); + }); + } + + return void 0; + }); + } + + private openFocusedElement(preserveFocus?: boolean): void { + const stat: FileStat = this.explorerViewer.getFocus(); + if (stat && !stat.isDirectory) { + this.editorService.openEditor({ resource: stat.resource, options: { preserveFocus, revealIfVisible: true } }).done(null, errors.onUnexpectedError); + } + } + + private getActiveFile(): URI { + const input = this.editorService.getActiveEditorInput(); + + // ignore diff editor inputs (helps to get out of diffing when returning to explorer) + if (input instanceof DiffEditorInput) { + return null; + } + + // check for files + return toResource(input, { supportSideBySide: true, filter: 'file' }); + } + + private get isCreated(): boolean { + return !!(this.explorerViewer && this.explorerViewer.getInput()); + } + + @memoize + private get model(): Model { + return this.instantiationService.createInstance(Model); + } + + public createViewer(container: Builder): ITree { + const dataSource = this.instantiationService.createInstance(FileDataSource); + const renderer = this.instantiationService.createInstance(FileRenderer, this.viewletState); + const controller = this.instantiationService.createInstance(FileController, this.viewletState); + const sorter = this.instantiationService.createInstance(FileSorter); + this.filter = this.instantiationService.createInstance(FileFilter); + const dnd = this.instantiationService.createInstance(FileDragAndDrop); + const accessibilityProvider = this.instantiationService.createInstance(FileAccessibilityProvider); + + this.explorerViewer = new Tree(container.getHTMLElement(), { + dataSource, + renderer, + controller, + sorter, + filter: this.filter, + dnd, + accessibilityProvider + }, { + autoExpandSingleChildren: true, + ariaLabel: nls.localize('treeAriaLabel', "Files Explorer"), + twistiePixels: 12, + showTwistie: false, + keyboardSupport: false + }); + + // Theme styler + this.toDispose.push(attachListStyler(this.explorerViewer, this.themeService)); + + // Register to list service + this.toDispose.push(this.listService.register(this.explorerViewer, [this.explorerFocusedContext, this.filesExplorerFocusedContext])); + + // Update Viewer based on File Change Events + this.toDispose.push(this.fileService.onAfterOperation(e => this.onFileOperation(e))); + this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); + + // Update resource context based on focused element + this.toDispose.push(this.explorerViewer.addListener('focus', (e: { focus: FileStat }) => { + this.resourceContext.set(e.focus && e.focus.resource); + this.folderContext.set(e.focus && e.focus.isDirectory); + })); + + // Open when selecting via keyboard + this.toDispose.push(this.explorerViewer.addListener('selection', event => { + if (event && event.payload && event.payload.origin === 'keyboard') { + const element = this.tree.getSelection(); + + if (Array.isArray(element) && element[0] instanceof FileStat) { + if (element[0].isDirectory) { + this.explorerViewer.toggleExpansion(element[0]); + } + + controller.openEditor(element[0], { pinned: false, sideBySide: false, preserveFocus: false }); + } + } + })); + + return this.explorerViewer; + } + + public getOptimalWidth(): number { + const parentNode = this.explorerViewer.getHTMLElement(); + const childNodes = [].slice.call(parentNode.querySelectorAll('.explorer-item > a')); + + return DOM.getLargestChildWidth(parentNode, childNodes); + } + + private onFileOperation(e: FileOperationEvent): void { + if (!this.isCreated) { + return; // ignore if not yet created + } + + // Add + if (e.operation === FileOperation.CREATE || e.operation === FileOperation.IMPORT || e.operation === FileOperation.COPY) { + const addedElement = e.target; + const parentResource = URI.file(paths.dirname(addedElement.resource.fsPath)); + const parents = this.model.findAll(parentResource); + + if (parents.length) { + + // Add the new file to its parent (Model) + parents.forEach(p => { + // We have to check if the parent is resolved #29177 + (p.isDirectoryResolved ? TPromise.as(null) : this.fileService.resolveFile(p.resource)).then(stat => { + if (stat) { + const modelStat = FileStat.create(stat, p.root); + FileStat.mergeLocalWithDisk(modelStat, p); + } + + const childElement = FileStat.create(addedElement, p.root); + p.removeChild(childElement); // make sure to remove any previous version of the file if any + p.addChild(childElement); + // Refresh the Parent (View) + this.explorerViewer.refresh(p).then(() => { + return this.reveal(childElement, 0.5).then(() => { + + // Focus new element + this.explorerViewer.setFocus(childElement); + }); + }).done(null, errors.onUnexpectedError); + }); + }); + } + } + + // Move (including Rename) + else if (e.operation === FileOperation.MOVE) { + const oldResource = e.resource; + const newElement = e.target; + + const oldParentResource = URI.file(paths.dirname(oldResource.fsPath)); + const newParentResource = URI.file(paths.dirname(newElement.resource.fsPath)); + + // Only update focus if renamed/moved element is selected + let restoreFocus = false; + const focus: FileStat = this.explorerViewer.getFocus(); + if (focus && focus.resource && focus.resource.toString() === oldResource.toString()) { + restoreFocus = true; + } + + // Handle Rename + if (oldParentResource && newParentResource && oldParentResource.toString() === newParentResource.toString()) { + const modelElements = this.model.findAll(oldResource); + modelElements.forEach(modelElement => { + // Rename File (Model) + modelElement.rename(newElement); + + // Update Parent (View) + this.explorerViewer.refresh(modelElement.parent).done(() => { + + // Select in Viewer if set + if (restoreFocus) { + this.explorerViewer.setFocus(modelElement); + } + }, errors.onUnexpectedError); + }); + } + + // Handle Move + else if (oldParentResource && newParentResource) { + const newParents = this.model.findAll(newParentResource); + const modelElements = this.model.findAll(oldResource); + + if (newParents.length && modelElements.length) { + + // Move in Model + modelElements.forEach((modelElement, index) => { + const oldParent = modelElement.parent; + modelElement.move(newParents[index], (callback: () => void) => { + // Update old parent + this.explorerViewer.refresh(oldParent).done(callback, errors.onUnexpectedError); + }, () => { + // Update new parent + this.explorerViewer.refresh(newParents[index], true).done(() => this.explorerViewer.expand(newParents[index]), errors.onUnexpectedError); + }); + }); + } + } + } + + // Delete + else if (e.operation === FileOperation.DELETE) { + const modelElements = this.model.findAll(e.resource); + modelElements.forEach(element => { + if (element.parent) { + const parent = element.parent; + // Remove Element from Parent (Model) + parent.removeChild(element); + + // Refresh Parent (View) + const restoreFocus = this.explorerViewer.isDOMFocused(); + this.explorerViewer.refresh(parent).done(() => { + + // Ensure viewer has keyboard focus if event originates from viewer + if (restoreFocus) { + this.explorerViewer.DOMFocus(); + } + }, errors.onUnexpectedError); + } + }); + } + } + + private onFileChanges(e: FileChangesEvent): void { + + // Ensure memento state does not capture a deleted file (we run this from a timeout because + // delete events can result in UI activity that will fill the memento again when multiple + // editors are closing) + setTimeout(() => { + const lastActiveResource: string = this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]; + if (lastActiveResource && e.contains(URI.parse(lastActiveResource), FileChangeType.DELETED)) { + this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = null; + } + }); + + // Check if an explorer refresh is necessary (delayed to give internal events a chance to react first) + // Note: there is no guarantee when the internal events are fired vs real ones. Code has to deal with the fact that one might + // be fired first over the other or not at all. + setTimeout(() => { + if (!this.shouldRefresh && this.shouldRefreshFromEvent(e)) { + this.refreshFromEvent(); + } + }, ExplorerView.EXPLORER_FILE_CHANGES_REACT_DELAY); + } + + private shouldRefreshFromEvent(e: FileChangesEvent): boolean { + + // Filter to the ones we care + e = this.filterFileEvents(e); + + if (!this.isCreated) { + return false; + } + + if (e.gotAdded()) { + const added = e.getAdded(); + + // Check added: Refresh if added file/folder is not part of resolved root and parent is part of it + const ignoredPaths: { [fsPath: string]: boolean } = <{ [fsPath: string]: boolean }>{}; + for (let i = 0; i < added.length; i++) { + const change = added[i]; + if (!this.contextService.isInsideWorkspace(change.resource)) { + continue; // out of workspace file + } + + // Find parent + const parent = paths.dirname(change.resource.fsPath); + + // Continue if parent was already determined as to be ignored + if (ignoredPaths[parent]) { + continue; + } + + // Compute if parent is visible and added file not yet part of it + const parentStat = this.model.findClosest(URI.file(parent)); + if (parentStat && parentStat.isDirectoryResolved && !this.model.findClosest(change.resource)) { + return true; + } + + // Keep track of path that can be ignored for faster lookup + if (!parentStat || !parentStat.isDirectoryResolved) { + ignoredPaths[parent] = true; + } + } + } + + if (e.gotDeleted()) { + const deleted = e.getDeleted(); + + // Check deleted: Refresh if deleted file/folder part of resolved root + for (let j = 0; j < deleted.length; j++) { + const del = deleted[j]; + if (!this.contextService.isInsideWorkspace(del.resource)) { + continue; // out of workspace file + } + + if (this.model.findClosest(del.resource)) { + return true; + } + } + } + + if (this.sortOrder === SortOrderConfiguration.MODIFIED && e.gotUpdated()) { + const updated = e.getUpdated(); + + // Check updated: Refresh if updated file/folder part of resolved root + for (let j = 0; j < updated.length; j++) { + const upd = updated[j]; + if (!this.contextService.isInsideWorkspace(upd.resource)) { + continue; // out of workspace file + } + + if (this.model.findClosest(upd.resource)) { + return true; + } + } + } + + return false; + } + + private filterFileEvents(e: FileChangesEvent): FileChangesEvent { + return new FileChangesEvent(e.changes.filter(change => { + if (!this.contextService.isInsideWorkspace(change.resource)) { + return false; // exclude changes for resources outside of workspace + } + + if (this.fileEventsFilter.matches(change.resource)) { + return false; // excluded via files.exclude setting + } + + return true; + })); + } + + private refreshFromEvent(): void { + if (this.isVisible()) { + this.explorerRefreshDelayer.trigger(() => { + if (!this.explorerViewer.getHighlight()) { + return this.doRefresh(); + } + + return TPromise.as(null); + }).done(null, errors.onUnexpectedError); + } else { + this.shouldRefresh = true; + } + } + + /** + * Refresh the contents of the explorer to get up to date data from the disk about the file structure. + */ + public refresh(): TPromise { + if (!this.explorerViewer || this.explorerViewer.getHighlight()) { + return TPromise.as(null); + } + + // Focus + this.explorerViewer.DOMFocus(); + + // Find resource to focus from active editor input if set + let resourceToFocus: URI; + if (this.autoReveal) { + resourceToFocus = this.getActiveFile(); + if (!resourceToFocus) { + const selection = this.explorerViewer.getSelection(); + if (selection && selection.length === 1) { + resourceToFocus = (selection[0]).resource; + } + } + } + + return this.doRefresh().then(() => { + if (resourceToFocus) { + return this.select(resourceToFocus, true); + } + + return TPromise.as(null); + }); + } + + private doRefresh(): TPromise { + const targetsToResolve: { root: FileStat, resource: URI, options: { resolveTo: URI[] } }[] = []; + this.model.roots.forEach(root => { + const rootAndTargets = { root, resource: root.resource, options: { resolveTo: [] } }; + targetsToResolve.push(rootAndTargets); + }); + + let targetsToExpand: URI[] = []; + if (this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES]) { + targetsToExpand = this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES].map((e: string) => URI.parse(e)); + } else if (this.contextService.hasFolderWorkspace() || (this.contextService.hasMultiFolderWorkspace() && this.model.roots.length === 1)) { + targetsToExpand = this.model.roots.map(root => root.resource); // always expand single folder workspace and multi folder workspace with only 1 root + } + + // First time refresh: Receive target through active editor input or selection and also include settings from previous session + if (!this.isCreated) { + const activeFile = this.getActiveFile(); + if (activeFile) { + const root = this.contextService.getRoot(activeFile); + if (root) { + const found = targetsToResolve.filter(t => t.root.resource.toString() === root.toString()).pop(); + found.options.resolveTo.push(activeFile); + } + } + + targetsToExpand.forEach(toExpand => { + const root = this.contextService.getRoot(toExpand); + if (root) { + const found = targetsToResolve.filter(ttr => ttr.resource.toString() === root.toString()).pop(); + found.options.resolveTo.push(toExpand); + } + }); + } + + // Subsequent refresh: Receive targets through expanded folders in tree + else { + targetsToResolve.forEach(t => { + this.getResolvedDirectories(t.root, t.options.resolveTo); + }); + } + + // Load Root Stat with given target path configured + const promise = this.fileService.resolveFiles(targetsToResolve).then(results => { + // Convert to model + const modelStats = results.map((result, index) => { + if (result.success) { + return FileStat.create(result.stat, targetsToResolve[index].root, targetsToResolve[index].options.resolveTo); + } + + return FileStat.create({ + resource: targetsToResolve[index].resource, + name: paths.basename(targetsToResolve[index].resource.fsPath), + mtime: 0, + etag: undefined, + isDirectory: true, + hasChildren: false + }, targetsToResolve[index].root); + }); + // Subsequent refresh: Merge stat into our local model and refresh tree + modelStats.forEach((modelStat, index) => FileStat.mergeLocalWithDisk(modelStat, this.model.roots[index])); + + const input = this.contextService.hasFolderWorkspace() ? this.model.roots[0] : this.model; + if (input === this.explorerViewer.getInput()) { + return this.explorerViewer.refresh(); + } + + // Preserve expanded elements if tree input changed. + // If it is a brand new tree just expand elements from memento + const expanded = this.explorerViewer.getExpandedElements(); + const statsToExpand = expanded.length ? [this.model.roots[0]].concat(expanded) : + targetsToExpand.map(expand => this.model.findClosest(expand)); + + // Display roots only when multi folder workspace + // Make sure to expand all folders that where expanded in the previous session + return this.explorerViewer.setInput(input).then(() => this.explorerViewer.expandAll(statsToExpand)); + }, e => TPromise.wrapError(e)); + + this.progressService.showWhile(promise, this.partService.isCreated() ? 800 : 3200 /* less ugly initial startup */); + + return promise; + } + + /** + * Given a stat, fills an array of path that make all folders below the stat that are resolved directories. + */ + private getResolvedDirectories(stat: FileStat, resolvedDirectories: URI[]): void { + if (stat.isDirectoryResolved) { + if (!stat.isRoot) { + + // Drop those path which are parents of the current one + for (let i = resolvedDirectories.length - 1; i >= 0; i--) { + const resource = resolvedDirectories[i]; + if (paths.isEqualOrParent(stat.resource.fsPath, resource.fsPath, !isLinux /* ignorecase */)) { + resolvedDirectories.splice(i); + } + } + + // Add to the list of path to resolve + resolvedDirectories.push(stat.resource); + } + + // Recurse into children + for (let i = 0; i < stat.children.length; i++) { + const child = stat.children[i]; + this.getResolvedDirectories(child, resolvedDirectories); + } + } + } + + /** + * Selects and reveal the file element provided by the given resource if its found in the explorer. Will try to + * resolve the path from the disk in case the explorer is not yet expanded to the file yet. + */ + public select(resource: URI, reveal: boolean = this.autoReveal): TPromise { + + // Require valid path + if (!resource) { + return TPromise.as(null); + } + + // If path already selected, just reveal and return + const selection = this.hasSelection(resource); + if (selection) { + return reveal ? this.reveal(selection, 0.5) : TPromise.as(null); + } + + // First try to get the stat object from the input to avoid a roundtrip + if (!this.isCreated) { + return TPromise.as(null); + } + + const fileStat = this.model.findClosest(resource); + if (fileStat) { + return this.doSelect(fileStat, reveal); + } + + // Stat needs to be resolved first and then revealed + const options: IResolveFileOptions = { resolveTo: [resource] }; + const rootUri = this.contextService.getRoot(resource) || this.model.roots[0].resource; + return this.fileService.resolveFile(rootUri, options).then(stat => { + + // Convert to model + const root = this.model.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); + const modelStat = FileStat.create(stat, root, options.resolveTo); + // Update Input with disk Stat + FileStat.mergeLocalWithDisk(modelStat, root); + + // Select and Reveal + return this.explorerViewer.refresh(root).then(() => this.doSelect(root.find(resource), reveal)); + + }, e => { this.messageService.show(Severity.Error, e); }); + } + + private hasSelection(resource: URI): FileStat { + const currentSelection: FileStat[] = this.explorerViewer.getSelection(); + + for (let i = 0; i < currentSelection.length; i++) { + if (currentSelection[i].resource.toString() === resource.toString()) { + return currentSelection[i]; + } + } + + return null; + } + + private doSelect(fileStat: FileStat, reveal: boolean): TPromise { + if (!fileStat) { + return TPromise.as(null); + } + + // Special case: we are asked to reveal and select an element that is not visible + // In this case we take the parent element so that we are at least close to it. + if (!this.filter.isVisible(this.tree, fileStat)) { + fileStat = fileStat.parent; + if (!fileStat) { + return TPromise.as(null); + } + } + + // Reveal depending on flag + let revealPromise: TPromise; + if (reveal) { + revealPromise = this.reveal(fileStat, 0.5); + } else { + revealPromise = TPromise.as(null); + } + + return revealPromise.then(() => { + if (!fileStat.isDirectory) { + this.explorerViewer.setSelection([fileStat]); // Since folders can not be opened, only select files + } + + this.explorerViewer.setFocus(fileStat); + }); + } + + public shutdown(): void { + + // Keep list of expanded folders to restore on next load + if (this.isCreated) { + const expanded = this.explorerViewer.getExpandedElements() + .filter(e => e instanceof FileStat) + .map((e: FileStat) => e.resource.toString()); + + if (expanded.length) { + this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES] = expanded; + } else { + delete this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES]; + } + } + + // Clean up last focused if not set + if (!this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) { + delete this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]; + } + + super.shutdown(); + } + + public dispose(): void { + if (this.toolBar) { + this.toolBar.dispose(); + } + + super.dispose(); + } +} diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts new file mode 100644 index 0000000000..206e970b15 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts @@ -0,0 +1,1010 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import lifecycle = require('vs/base/common/lifecycle'); +import objects = require('vs/base/common/objects'); +import DOM = require('vs/base/browser/dom'); +import URI from 'vs/base/common/uri'; +import { MIME_BINARY } from 'vs/base/common/mime'; +import { once } from 'vs/base/common/functional'; +import paths = require('vs/base/common/paths'); +import errors = require('vs/base/common/errors'); +import { isString } from 'vs/base/common/types'; +import { IAction, ActionRunner as BaseActionRunner, IActionRunner } from 'vs/base/common/actions'; +import comparers = require('vs/base/common/comparers'); +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { isMacintosh, isLinux } from 'vs/base/common/platform'; +import glob = require('vs/base/common/glob'); +import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { ContributableActionProvider } from 'vs/workbench/browser/actions'; +import { IFilesConfiguration, SortOrder } from 'vs/workbench/parts/files/common/files'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { FileOperationError, FileOperationResult, IFileService, FileKind } from 'vs/platform/files/common/files'; +import { ResourceMap } from 'vs/base/common/map'; +import { DuplicateFileAction, ImportFileAction, IEditableData, IFileViewletState } from 'vs/workbench/parts/files/browser/fileActions'; +import { IDataSource, ITree, IAccessibilityProvider, IRenderer, ContextMenuEvent, ISorter, IFilter, IDragAndDropData, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY, DRAG_OVER_ACCEPT_BUBBLE_UP, DRAG_OVER_ACCEPT_BUBBLE_UP_COPY, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree'; +import { DesktopDragAndDropData, ExternalElementsDragAndDropData, SimpleFileResourceDragAndDrop } from 'vs/base/parts/tree/browser/treeDnd'; +import { ClickBehavior, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults'; +import { FileStat, NewStatPlaceholder, Model } from 'vs/workbench/parts/files/common/explorerModel'; +import { DragMouseEvent, IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, IConfirmation, Severity } from 'vs/platform/message/common/message'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions'; +import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { distinct } from 'vs/base/common/arrays'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { getPathLabel } from 'vs/base/common/labels'; +import { extractResources } from 'vs/base/browser/dnd'; + +export class FileDataSource implements IDataSource { + constructor( + @IProgressService private progressService: IProgressService, + @IMessageService private messageService: IMessageService, + @IFileService private fileService: IFileService, + @IPartService private partService: IPartService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { } + + public getId(tree: ITree, stat: FileStat | Model): string { + if (stat instanceof Model) { + return 'model'; + } + + return `${stat.root.resource.toString()}:${stat.getId()}`; + } + + public hasChildren(tree: ITree, stat: FileStat | Model): boolean { + return stat instanceof Model || (stat instanceof FileStat && stat.isDirectory); + } + + public getChildren(tree: ITree, stat: FileStat | Model): TPromise { + if (stat instanceof Model) { + return TPromise.as(stat.roots); + } + + // Return early if stat is already resolved + if (stat.isDirectoryResolved) { + return TPromise.as(stat.children); + } + + // Resolve children and add to fileStat for future lookup + else { + + // Resolve + const promise = this.fileService.resolveFile(stat.resource, { resolveSingleChildDescendants: true }).then(dirStat => { + + // Convert to view model + const modelDirStat = FileStat.create(dirStat, stat.root); + + // Add children to folder + for (let i = 0; i < modelDirStat.children.length; i++) { + stat.addChild(modelDirStat.children[i]); + } + + stat.isDirectoryResolved = true; + + return stat.children; + }, (e: any) => { + stat.exists = false; + stat.hasChildren = false; + if (!stat.isRoot) { + this.messageService.show(Severity.Error, e); + } else { + // We render the roots that do not exist differently, nned to do a refresh + tree.refresh(stat, false); + } + + return []; // we could not resolve any children because of an error + }); + + this.progressService.showWhile(promise, this.partService.isCreated() ? 800 : 3200 /* less ugly initial startup */); + + return promise; + } + } + + public getParent(tree: ITree, stat: FileStat | Model): TPromise { + if (!stat) { + return TPromise.as(null); // can be null if nothing selected in the tree + } + + // Return if root reached + if (tree.getInput() === stat) { + return TPromise.as(null); + } + + // Return if parent already resolved + if (stat instanceof FileStat && stat.parent) { + return TPromise.as(stat.parent); + } + + // We never actually resolve the parent from the disk for performance reasons. It wouldnt make + // any sense to resolve parent by parent with requests to walk up the chain. Instead, the explorer + // makes sure to properly resolve a deep path to a specific file and merges the result with the model. + return TPromise.as(null); + } +} + +export class FileActionProvider extends ContributableActionProvider { + private state: FileViewletState; + + constructor(state: any) { + super(); + + this.state = state; + } + + public hasActions(tree: ITree, stat: FileStat): boolean { + if (stat instanceof NewStatPlaceholder) { + return false; + } + + return super.hasActions(tree, stat); + } + + public getActions(tree: ITree, stat: FileStat): TPromise { + if (stat instanceof NewStatPlaceholder) { + return TPromise.as([]); + } + + return super.getActions(tree, stat); + } + + public hasSecondaryActions(tree: ITree, stat: FileStat | Model): boolean { + if (stat instanceof NewStatPlaceholder) { + return false; + } + + return super.hasSecondaryActions(tree, stat); + } + + public getSecondaryActions(tree: ITree, stat: FileStat | Model): TPromise { + if (stat instanceof NewStatPlaceholder) { + return TPromise.as([]); + } + + return super.getSecondaryActions(tree, stat); + } + + public runAction(tree: ITree, stat: FileStat, action: IAction, context?: any): TPromise; + public runAction(tree: ITree, stat: FileStat, actionID: string, context?: any): TPromise; + public runAction(tree: ITree, stat: FileStat, arg: any, context: any = {}): TPromise { + context = objects.mixin({ + viewletState: this.state, + stat + }, context); + + if (!isString(arg)) { + const action = arg; + if (action.enabled) { + return action.run(context); + } + + return null; + } + + const id = arg; + let promise = this.hasActions(tree, stat) ? this.getActions(tree, stat) : TPromise.as([]); + + return promise.then((actions: IAction[]) => { + for (let i = 0, len = actions.length; i < len; i++) { + if (actions[i].id === id && actions[i].enabled) { + return actions[i].run(context); + } + } + + promise = this.hasSecondaryActions(tree, stat) ? this.getSecondaryActions(tree, stat) : TPromise.as([]); + + return promise.then((actions: IAction[]) => { + for (let i = 0, len = actions.length; i < len; i++) { + if (actions[i].id === id && actions[i].enabled) { + return actions[i].run(context); + } + } + + return null; + }); + }); + } +} + +export class FileViewletState implements IFileViewletState { + private _actionProvider: FileActionProvider; + private editableStats: ResourceMap; + + constructor() { + this._actionProvider = new FileActionProvider(this); + this.editableStats = new ResourceMap(); + } + + public get actionProvider(): FileActionProvider { + return this._actionProvider; + } + + public getEditableData(stat: FileStat): IEditableData { + return this.editableStats.get(stat.resource); + } + + public setEditable(stat: FileStat, editableData: IEditableData): void { + if (editableData) { + this.editableStats.set(stat.resource, editableData); + } + } + + public clearEditable(stat: FileStat): void { + this.editableStats.delete(stat.resource); + } +} + +export class ActionRunner extends BaseActionRunner implements IActionRunner { + private viewletState: FileViewletState; + + constructor(state: FileViewletState) { + super(); + + this.viewletState = state; + } + + public run(action: IAction, context?: any): TPromise { + return super.run(action, { viewletState: this.viewletState }); + } +} + +export interface IFileTemplateData { + label: FileLabel; + container: HTMLElement; +} + +// Explorer Renderer +export class FileRenderer implements IRenderer { + + private static ITEM_HEIGHT = 22; + private static FILE_TEMPLATE_ID = 'file'; + + private state: FileViewletState; + + constructor( + state: FileViewletState, + @IContextViewService private contextViewService: IContextViewService, + @IInstantiationService private instantiationService: IInstantiationService, + @IThemeService private themeService: IThemeService + ) { + this.state = state; + } + + public getHeight(tree: ITree, element: any): number { + return FileRenderer.ITEM_HEIGHT; + } + + public getTemplateId(tree: ITree, element: any): string { + return FileRenderer.FILE_TEMPLATE_ID; + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: IFileTemplateData): void { + templateData.label.dispose(); + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): IFileTemplateData { + const label = this.instantiationService.createInstance(FileLabel, container, void 0); + + return { label, container }; + } + + public renderElement(tree: ITree, stat: FileStat, templateId: string, templateData: IFileTemplateData): void { + const editableData: IEditableData = this.state.getEditableData(stat); + + // File Label + if (!editableData) { + templateData.label.element.style.display = 'block'; + const extraClasses = ['explorer-item']; + if (!stat.exists && stat.isRoot) { + extraClasses.push('nonexistent-root'); + } + templateData.label.setFile(stat.resource, { hidePath: true, fileKind: stat.isRoot ? FileKind.ROOT_FOLDER : stat.isDirectory ? FileKind.FOLDER : FileKind.FILE, extraClasses }); + } + + // Input Box + else { + templateData.label.element.style.display = 'none'; + this.renderInputBox(templateData.container, tree, stat, editableData); + } + } + + private renderInputBox(container: HTMLElement, tree: ITree, stat: FileStat, editableData: IEditableData): void { + + // Use a file label only for the icon next to the input box + const label = this.instantiationService.createInstance(FileLabel, container, void 0); + const extraClasses = ['explorer-item', 'explorer-item-edited']; + const fileKind = stat.isRoot ? FileKind.ROOT_FOLDER : (stat.isDirectory || (stat instanceof NewStatPlaceholder && stat.isDirectoryPlaceholder())) ? FileKind.FOLDER : FileKind.FILE; + const labelOptions: IFileLabelOptions = { hidePath: true, hideLabel: true, fileKind, extraClasses }; + label.setFile(stat.resource, labelOptions); + + // Input field for name + const inputBox = new InputBox(label.element, this.contextViewService, { + validationOptions: { + validation: editableData.validator, + showMessage: true + }, + ariaLabel: nls.localize('fileInputAriaLabel', "Type file name. Press Enter to confirm or Escape to cancel.") + }); + const styler = attachInputBoxStyler(inputBox, this.themeService); + + const parent = paths.dirname(stat.resource.fsPath); + inputBox.onDidChange(value => { + label.setFile(URI.file(paths.join(parent, value)), labelOptions); // update label icon while typing! + }); + + const value = stat.name || ''; + const lastDot = value.lastIndexOf('.'); + + inputBox.value = value; + inputBox.select({ start: 0, end: lastDot > 0 && !stat.isDirectory ? lastDot : value.length }); + inputBox.focus(); + + const done = once(commit => { + tree.clearHighlight(); + + if (commit && inputBox.value) { + this.state.actionProvider.runAction(tree, stat, editableData.action, { value: inputBox.value }); + } + + const restoreFocus = document.activeElement === inputBox.inputElement; // https://github.com/Microsoft/vscode/issues/20269 + setTimeout(() => { + if (restoreFocus) { + tree.DOMFocus(); + } + lifecycle.dispose(toDispose); + container.removeChild(label.element); + }, 0); + }); + + const toDispose = [ + inputBox, + DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_DOWN, (e: IKeyboardEvent) => { + if (e.equals(KeyCode.Enter)) { + if (inputBox.validate()) { + done(true); + } + } else if (e.equals(KeyCode.Escape)) { + done(false); + } + }), + DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => { + done(inputBox.isInputValid()); + }), + label, + styler + ]; + } +} + +// Explorer Accessibility Provider +export class FileAccessibilityProvider implements IAccessibilityProvider { + + public getAriaLabel(tree: ITree, stat: FileStat): string { + return nls.localize('filesExplorerViewerAriaLabel', "{0}, Files Explorer", stat.name); + } +} + +// Explorer Controller +export class FileController extends DefaultController { + private state: FileViewletState; + + private contributedContextMenu: IMenu; + + constructor(state: FileViewletState, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IInstantiationService private instantiationService: IInstantiationService, + @ITelemetryService private telemetryService: ITelemetryService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IMenuService menuService: IMenuService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super({ clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, keyboardSupport: false /* handled via IListService */ }); + + this.contributedContextMenu = menuService.createMenu(MenuId.ExplorerContext, contextKeyService); + + this.state = state; + } + + public onLeftClick(tree: ITree, stat: FileStat | Model, event: IMouseEvent, origin: string = 'mouse'): boolean { + const payload = { origin: origin }; + const isDoubleClick = (origin === 'mouse' && event.detail === 2); + + // Handle Highlight Mode + if (tree.getHighlight()) { + + // Cancel Event + event.preventDefault(); + event.stopPropagation(); + + tree.clearHighlight(payload); + + return false; + } + + // Handle root + if (stat instanceof Model) { + tree.clearFocus(payload); + tree.clearSelection(payload); + + return false; + } + + // Cancel Event + const isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown'; + if (!isMouseDown) { + event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise + } + event.stopPropagation(); + + // Set DOM focus + tree.DOMFocus(); + + // Expand / Collapse + tree.toggleExpansion(stat, event.altKey); + + // Allow to unselect + if (event.shiftKey && !(stat instanceof NewStatPlaceholder)) { + const selection = tree.getSelection(); + if (selection && selection.length > 0 && selection[0] === stat) { + tree.clearSelection(payload); + } + } + + // Select, Focus and open files + else if (!(stat instanceof NewStatPlaceholder)) { + const preserveFocus = !isDoubleClick; + tree.setFocus(stat, payload); + + if (isDoubleClick) { + event.preventDefault(); // focus moves to editor, we need to prevent default + } + + tree.setSelection([stat], payload); + + if (!stat.isDirectory) { + this.openEditor(stat, { preserveFocus, sideBySide: event && (event.ctrlKey || event.metaKey), pinned: isDoubleClick }); + } + } + + return true; + } + + public onContextMenu(tree: ITree, stat: FileStat | Model, event: ContextMenuEvent): boolean { + if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { + return false; + } + + event.preventDefault(); + event.stopPropagation(); + + tree.setFocus(stat); + + if (!this.state.actionProvider.hasSecondaryActions(tree, stat)) { + return true; + } + + const anchor = { x: event.posx + 1, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => { + return this.state.actionProvider.getSecondaryActions(tree, stat).then(actions => { + fillInActions(this.contributedContextMenu, stat instanceof FileStat ? { arg: stat.resource } : null, actions); + return actions; + }); + }, + getActionItem: this.state.actionProvider.getActionItem.bind(this.state.actionProvider, tree, stat), + getActionsContext: (event) => { + return { + viewletState: this.state, + stat, + event + }; + }, + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + tree.DOMFocus(); + } + } + }); + + return true; + } + + public openEditor(stat: FileStat, options: { preserveFocus: boolean; sideBySide: boolean; pinned: boolean; }): void { + if (stat && !stat.isDirectory) { + this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); + + this.editorService.openEditor({ resource: stat.resource, options }, options.sideBySide).done(null, errors.onUnexpectedError); + } + } +} + +// Explorer Sorter +export class FileSorter implements ISorter { + private toDispose: IDisposable[]; + private sortOrder: SortOrder; + + constructor( + @IConfigurationService private configurationService: IConfigurationService + ) { + this.toDispose = []; + + this.onConfigurationUpdated(configurationService.getConfiguration()); + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); + } + + private onConfigurationUpdated(configuration: IFilesConfiguration): void { + this.sortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; + } + + public compare(tree: ITree, statA: FileStat, statB: FileStat): number { + + // Do not sort roots + if (statA.isRoot) { + return -1; + } + if (statB.isRoot) { + return 1; + } + + // Sort Directories + switch (this.sortOrder) { + case 'type': + if (statA.isDirectory && !statB.isDirectory) { + return -1; + } + + if (statB.isDirectory && !statA.isDirectory) { + return 1; + } + + if (statA.isDirectory && statB.isDirectory) { + return comparers.compareFileNames(statA.name, statB.name); + } + + break; + + case 'filesFirst': + if (statA.isDirectory && !statB.isDirectory) { + return 1; + } + + if (statB.isDirectory && !statA.isDirectory) { + return -1; + } + + break; + + default: /* 'default', 'modified' */ + if (statA.isDirectory && !statB.isDirectory) { + return -1; + } + + if (statB.isDirectory && !statA.isDirectory) { + return 1; + } + + break; + } + + // Sort "New File/Folder" placeholders + if (statA instanceof NewStatPlaceholder) { + return -1; + } + + if (statB instanceof NewStatPlaceholder) { + return 1; + } + + // Sort Files + switch (this.sortOrder) { + case 'type': + return comparers.compareFileExtensions(statA.name, statB.name); + + case 'modified': + if (statA.mtime !== statB.mtime) { + return statA.mtime < statB.mtime ? 1 : -1; + } + + return comparers.compareFileNames(statA.name, statB.name); + + default: /* 'default', 'mixed', 'filesFirst' */ + return comparers.compareFileNames(statA.name, statB.name); + } + } +} + +// Explorer Filter +export class FileFilter implements IFilter { + + private static MAX_SIBLINGS_FILTER_THRESHOLD = 2000; + + private hiddenExpressionPerRoot: Map; + + constructor( + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IConfigurationService private configurationService: IConfigurationService + ) { + this.hiddenExpressionPerRoot = new Map(); + this.contextService.onDidChangeWorkspaceRoots(() => this.updateConfiguration()); + } + + public updateConfiguration(): boolean { + let needsRefresh = false; + this.contextService.getWorkspace().roots.forEach(root => { + const configuration = this.configurationService.getConfiguration(undefined, { resource: root }); + const excludesConfig = (configuration && configuration.files && configuration.files.exclude) || Object.create(null); + needsRefresh = needsRefresh || !objects.equals(this.hiddenExpressionPerRoot.get(root.toString()), excludesConfig); + this.hiddenExpressionPerRoot.set(root.toString(), objects.clone(excludesConfig)); // do not keep the config, as it gets mutated under our hoods + }); + + return needsRefresh; + } + + public isVisible(tree: ITree, stat: FileStat): boolean { + return this.doIsVisible(stat); + } + + private doIsVisible(stat: FileStat): boolean { + if (stat instanceof NewStatPlaceholder) { + return true; // always visible + } + + // Workaround for O(N^2) complexity (https://github.com/Microsoft/vscode/issues/9962) + let siblings = stat.parent && stat.parent.children && stat.parent.children; + if (siblings && siblings.length > FileFilter.MAX_SIBLINGS_FILTER_THRESHOLD) { + siblings = void 0; + } + + // Hide those that match Hidden Patterns + const siblingsFn = () => siblings && siblings.map(c => c.name); + const expression = this.hiddenExpressionPerRoot.get(stat.root.resource.toString()) || Object.create(null); + if (glob.match(expression, paths.normalize(paths.relative(stat.root.resource.fsPath, stat.resource.fsPath), true), siblingsFn)) { + return false; // hidden through pattern + } + + return true; + } +} + +// Explorer Drag And Drop Controller +export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { + private toDispose: IDisposable[]; + private dropEnabled: boolean; + + constructor( + @IMessageService private messageService: IMessageService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IProgressService private progressService: IProgressService, + @IFileService private fileService: IFileService, + @IConfigurationService private configurationService: IConfigurationService, + @IInstantiationService private instantiationService: IInstantiationService, + @ITextFileService private textFileService: ITextFileService, + @IBackupFileService private backupFileService: IBackupFileService, + @IWindowService private windowService: IWindowService, + @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + super(stat => this.statToResource(stat)); + + this.toDispose = []; + + this.onConfigurationUpdated(configurationService.getConfiguration()); + + this.registerListeners(); + } + + private statToResource(stat: FileStat): URI { + if (stat.isRoot) { + return null; // Can not move root folder + } + + if (stat.isDirectory) { + return URI.from({ scheme: 'folder', path: stat.resource.fsPath }); // indicates that we are dragging a folder + } + + return stat.resource; + } + + private registerListeners(): void { + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); + } + + private onConfigurationUpdated(config: IFilesConfiguration): void { + this.dropEnabled = config && config.explorer && config.explorer.enableDragAndDrop; + } + + public onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: DragMouseEvent): void { + const sources: FileStat[] = data.getData(); + let source: FileStat = null; + if (sources.length > 0) { + source = sources[0]; + } + + // When dragging folders, make sure to collapse them to free up some space + if (source && source.isDirectory && tree.isExpanded(source)) { + tree.collapse(source, false); + } + + // Apply some datatransfer types to allow for dragging the element outside of the application + if (source) { + if (!source.isDirectory) { + originalEvent.dataTransfer.setData('DownloadURL', [MIME_BINARY, source.name, source.resource.toString()].join(':')); + } + + originalEvent.dataTransfer.setData('text/plain', getPathLabel(source.resource)); + } + } + + public onDragOver(tree: ITree, data: IDragAndDropData, target: FileStat | Model, originalEvent: DragMouseEvent): IDragOverReaction { + if (!this.dropEnabled) { + return DRAG_OVER_REJECT; + } + + const isCopy = originalEvent && ((originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh)); + const fromDesktop = data instanceof DesktopDragAndDropData; + + // Desktop DND + if (fromDesktop) { + const dragData = (data).getData(); + + const types = dragData.types; + const typesArray: string[] = []; + for (let i = 0; i < types.length; i++) { + typesArray.push(types[i]); + } + + if (typesArray.length === 0 || !typesArray.some(type => { return type === 'Files'; })) { + return DRAG_OVER_REJECT; + } + } + + // Other-Tree DND + else if (data instanceof ExternalElementsDragAndDropData) { + return DRAG_OVER_REJECT; + } + + // In-Explorer DND + else { + if (target instanceof Model) { + return DRAG_OVER_REJECT; + } + + const sources: FileStat[] = data.getData(); + if (!Array.isArray(sources)) { + return DRAG_OVER_REJECT; + } + + if (sources.some((source) => { + if (source instanceof NewStatPlaceholder) { + return true; // NewStatPlaceholders can not be moved + } + + if (source.resource.toString() === target.resource.toString()) { + return true; // Can not move anything onto itself + } + + if (!isCopy && paths.isEqual(paths.dirname(source.resource.fsPath), target.resource.fsPath)) { + return true; // Can not move a file to the same parent unless we copy + } + + if (paths.isEqualOrParent(target.resource.fsPath, source.resource.fsPath, !isLinux /* ignorecase */)) { + return true; // Can not move a parent folder into one of its children + } + + return false; + })) { + return DRAG_OVER_REJECT; + } + } + + // All (target = model) + if (target instanceof Model) { + return this.contextService.hasMultiFolderWorkspace() ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(false) : DRAG_OVER_REJECT; // can only drop folders to workspace + } + + // All (target = file/folder) + else { + if (target.isDirectory) { + return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(true) : DRAG_OVER_ACCEPT_BUBBLE_DOWN(true); + } + + const workspace = this.contextService.getWorkspace(); + if (workspace && workspace.roots.every(r => r.toString() !== target.resource.toString())) { + return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_UP_COPY : DRAG_OVER_ACCEPT_BUBBLE_UP; + } + } + + return DRAG_OVER_REJECT; + } + + public drop(tree: ITree, data: IDragAndDropData, target: FileStat | Model, originalEvent: DragMouseEvent): void { + let promise: TPromise = TPromise.as(null); + + // Desktop DND (Import file) + if (data instanceof DesktopDragAndDropData) { + promise = this.handleExternalDrop(tree, data, target, originalEvent); + } + + // In-Explorer DND (Move/Copy file) + else { + if (target instanceof FileStat) { + promise = this.handleExplorerDrop(tree, data, target, originalEvent); + } + } + + this.progressService.showWhile(promise, 800); + + promise.done(null, errors.onUnexpectedError); + } + + // {{SQL CARBON EDIT}} + public dropAbort(tree: ITree, data: IDragAndDropData): void { } + + private handleExternalDrop(tree: ITree, data: DesktopDragAndDropData, target: FileStat | Model, originalEvent: DragMouseEvent): TPromise { + const droppedResources = extractResources(originalEvent.browserEvent as DragEvent, true); + + // Check for dropped external files to be folders + return this.fileService.resolveFiles(droppedResources).then(result => { + + // Pass focus to window + this.windowService.focusWindow(); + + // Handle folders by adding to workspace if we are in workspace context + const folders = result.filter(result => result.stat.isDirectory).map(result => result.stat.resource); + if (folders.length > 0) { + if (this.environmentService.appQuality === 'stable') { + return void 0; // TODO@Ben multi root + } + + if (this.contextService.hasMultiFolderWorkspace()) { + return this.workspaceEditingService.addRoots(folders); + } + + // If we are in single-folder context, ask for confirmation to create a workspace + const result = this.messageService.confirm({ + message: folders.length > 1 ? nls.localize('dropFolders', "Do you want to add the folders to the workspace?") : nls.localize('dropFolder', "Do you want to add the folder to the workspace?"), + type: 'question', + primaryButton: folders.length > 1 ? nls.localize('addFolders', "&&Add Folders") : nls.localize('addFolder', "&&Add Folder") + }); + + if (result) { + const currentRoots = this.contextService.getWorkspace().roots; + const newRoots = [...currentRoots, ...folders]; + + return this.windowService.createAndOpenWorkspace(distinct(newRoots.map(root => root.fsPath))); + } + } + + // Handle dropped files (only support FileStat as target) + else if (target instanceof FileStat) { + const importAction = this.instantiationService.createInstance(ImportFileAction, tree, target, null); + return importAction.run({ + input: { paths: droppedResources.map(res => res.resource.fsPath) } + }); + } + + return void 0; + }); + } + + private handleExplorerDrop(tree: ITree, data: IDragAndDropData, target: FileStat, originalEvent: DragMouseEvent): TPromise { + const source: FileStat = data.getData()[0]; + const isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh); + + return tree.expand(target).then(() => { + + // Reuse duplicate action if user copies + if (isCopy) { + return this.instantiationService.createInstance(DuplicateFileAction, tree, source, target).run(); + } + + const dirtyMoved: URI[] = []; + + // Success: load all files that are dirty again to restore their dirty contents + // Error: discard any backups created during the process + const onSuccess = () => TPromise.join(dirtyMoved.map(t => this.textFileService.models.loadOrCreate(t))); + const onError = (error?: Error, showError?: boolean) => { + if (showError) { + this.messageService.show(Severity.Error, error); + } + + return TPromise.join(dirtyMoved.map(d => this.backupFileService.discardResourceBackup(d))); + }; + + // 1. check for dirty files that are being moved and backup to new target + const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, source.resource.fsPath, !isLinux /* ignorecase */)); + return TPromise.join(dirty.map(d => { + let moved: URI; + + // If the dirty file itself got moved, just reparent it to the target folder + if (paths.isEqual(source.resource.fsPath, d.fsPath)) { + moved = URI.file(paths.join(target.resource.fsPath, source.name)); + } + + // Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example: + else { + moved = URI.file(paths.join(target.resource.fsPath, d.fsPath.substr(source.parent.resource.fsPath.length + 1))); + } + + dirtyMoved.push(moved); + + const model = this.textFileService.models.get(d); + + return this.backupFileService.backupResource(moved, model.getValue(), model.getVersionId()); + })) + + // 2. soft revert all dirty since we have backed up their contents + .then(() => this.textFileService.revertAll(dirty, { soft: true /* do not attempt to load content from disk */ })) + + // 3.) run the move operation + .then(() => { + const targetResource = URI.file(paths.join(target.resource.fsPath, source.name)); + let didHandleConflict = false; + + return this.fileService.moveFile(source.resource, targetResource).then(null, error => { + + // Conflict + if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) { + didHandleConflict = true; + + const confirm: IConfirmation = { + message: nls.localize('confirmOverwriteMessage', "'{0}' already exists in the destination folder. Do you want to replace it?", source.name), + detail: nls.localize('irreversible', "This action is irreversible!"), + primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), + type: 'warning' + }; + + // Move with overwrite if the user confirms + if (this.messageService.confirm(confirm)) { + const targetDirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, targetResource.fsPath, !isLinux /* ignorecase */)); + + // Make sure to revert all dirty in target first to be able to overwrite properly + return this.textFileService.revertAll(targetDirty, { soft: true /* do not attempt to load content from disk */ }).then(() => { + + // Then continue to do the move operation + return this.fileService.moveFile(source.resource, targetResource, true).then(onSuccess, error => onError(error, true)); + }); + } + + return onError(); + } + + return onError(error, true); + }); + }) + + // 4.) resolve those that were dirty to load their previous dirty contents from disk + .then(onSuccess, onError); + }, errors.onUnexpectedError); + } +} diff --git a/src/vs/workbench/parts/files/browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/browser/views/openEditorsView.ts new file mode 100644 index 0000000000..a698ba29db --- /dev/null +++ b/src/vs/workbench/parts/files/browser/views/openEditorsView.ts @@ -0,0 +1,333 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import errors = require('vs/base/common/errors'); +import { RunOnceScheduler } from 'vs/base/common/async'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction } from 'vs/base/common/actions'; +import dom = require('vs/base/browser/dom'); +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { IItemCollapseEvent } from 'vs/base/parts/tree/browser/treeModel'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup } from 'vs/workbench/common/editor'; +import { SaveAllAction } from 'vs/workbench/parts/files/browser/fileActions'; +import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/parts/views/browser/views'; +import { IFilesConfiguration, VIEWLET_ID, OpenEditorsFocusedContext, ExplorerFocusedContext } from 'vs/workbench/parts/files/common/files'; +import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { OpenEditor } from 'vs/workbench/parts/files/common/explorerModel'; +import { Renderer, DataSource, Controller, AccessibilityProvider, ActionProvider, DragAndDrop } from 'vs/workbench/parts/files/browser/views/openEditorsViewer'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { CloseAllEditorsAction } from 'vs/workbench/browser/parts/editor/editorActions'; +import { ToggleEditorLayoutAction } from 'vs/workbench/browser/actions/toggleEditorLayout'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { EditorGroup } from 'vs/workbench/common/editor/editorStacksModel'; +import { attachListStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview'; + +const $ = dom.$; + +export class OpenEditorsView extends CollapsibleView { + + private static DEFAULT_VISIBLE_OPEN_EDITORS = 9; + private static DEFAULT_DYNAMIC_HEIGHT = true; + static ID = 'workbench.explorer.openEditorsView'; + static NAME = nls.localize({ key: 'openEditors', comment: ['Open is an adjective'] }, "Open Editors"); + + private visibleOpenEditors: number; + private dynamicHeight: boolean; + + private model: IEditorStacksModel; + private dirtyCountElement: HTMLElement; + private structuralTreeRefreshScheduler: RunOnceScheduler; + private structuralRefreshDelay: number; + private groupToRefresh: IEditorGroup; + private fullRefreshNeeded: boolean; + + private openEditorsFocusedContext: IContextKey; + private explorerFocusedContext: IContextKey; + + constructor( + initialSize: number, + options: IViewletViewOptions, + @IInstantiationService private instantiationService: IInstantiationService, + @IContextMenuService contextMenuService: IContextMenuService, + @ITextFileService private textFileService: ITextFileService, + @IEditorGroupService editorGroupService: IEditorGroupService, + @IConfigurationService private configurationService: IConfigurationService, + @IKeybindingService keybindingService: IKeybindingService, + @IListService private listService: IListService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IContextKeyService contextKeyService: IContextKeyService, + @IViewletService private viewletService: IViewletService, + @IThemeService private themeService: IThemeService + ) { + super(initialSize, { + ...(options as IViewOptions), + ariaHeaderLabel: nls.localize({ key: 'openEditosrSection', comment: ['Open is an adjective'] }, "Open Editors Section"), + sizing: ViewSizing.Fixed, + initialBodySize: OpenEditorsView.computeExpandedBodySize(editorGroupService.getStacksModel()) + }, keybindingService, contextMenuService); + + this.model = editorGroupService.getStacksModel(); + + this.openEditorsFocusedContext = OpenEditorsFocusedContext.bindTo(contextKeyService); + this.explorerFocusedContext = ExplorerFocusedContext.bindTo(contextKeyService); + + this.structuralRefreshDelay = 0; + this.structuralTreeRefreshScheduler = new RunOnceScheduler(() => this.structuralTreeUpdate(), this.structuralRefreshDelay); + } + + public renderHeader(container: HTMLElement): void { + const titleDiv = dom.append(container, $('.title')); + const titleSpan = dom.append(titleDiv, $('span')); + titleSpan.textContent = this.name; + + this.dirtyCountElement = dom.append(titleDiv, $('.monaco-count-badge')); + + this.toDispose.push((attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => { + const background = colors.badgeBackground ? colors.badgeBackground.toString() : null; + const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : null; + const border = colors.contrastBorder ? colors.contrastBorder.toString() : null; + + this.dirtyCountElement.style.backgroundColor = background; + this.dirtyCountElement.style.color = foreground; + + this.dirtyCountElement.style.borderWidth = border ? '1px' : null; + this.dirtyCountElement.style.borderStyle = border ? 'solid' : null; + this.dirtyCountElement.style.borderColor = border; + }))); + + this.updateDirtyIndicator(); + + super.renderHeader(container); + } + + public getActions(): IAction[] { + return [ + this.instantiationService.createInstance(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL), + this.instantiationService.createInstance(SaveAllAction, SaveAllAction.ID, SaveAllAction.LABEL), + this.instantiationService.createInstance(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL) + ]; + } + + public renderBody(container: HTMLElement): void { + this.treeContainer = super.renderViewTree(container); + dom.addClass(this.treeContainer, 'explorer-open-editors'); + dom.addClass(this.treeContainer, 'show-file-icons'); + + const dataSource = this.instantiationService.createInstance(DataSource); + const actionProvider = this.instantiationService.createInstance(ActionProvider, this.model); + const renderer = this.instantiationService.createInstance(Renderer, actionProvider); + const controller = this.instantiationService.createInstance(Controller, actionProvider, this.model); + const accessibilityProvider = this.instantiationService.createInstance(AccessibilityProvider); + const dnd = this.instantiationService.createInstance(DragAndDrop); + + this.tree = new Tree(this.treeContainer, { + dataSource, + renderer, + controller, + accessibilityProvider, + dnd + }, { + indentPixels: 0, + twistiePixels: 22, + ariaLabel: nls.localize({ key: 'treeAriaLabel', comment: ['Open is an adjective'] }, "Open Editors: List of Active Files"), + showTwistie: false, + keyboardSupport: false + }); + + // Theme styler + this.toDispose.push(attachListStyler(this.tree, this.themeService)); + + // Register to list service + this.toDispose.push(this.listService.register(this.tree, [this.explorerFocusedContext, this.openEditorsFocusedContext])); + + // Open when selecting via keyboard + this.toDispose.push(this.tree.addListener('selection', event => { + if (event && event.payload && event.payload.origin === 'keyboard') { + controller.openEditor(this.tree.getFocus(), { pinned: false, sideBySide: false, preserveFocus: false }); + } + })); + + // Prevent collapsing of editor groups + this.toDispose.push(this.tree.addListener('item:collapsed', (event: IItemCollapseEvent) => { + if (event.item && event.item.getElement() instanceof EditorGroup) { + setTimeout(() => this.tree.expand(event.item.getElement())); // unwind from callback + } + })); + + this.fullRefreshNeeded = true; + this.structuralTreeUpdate(); + } + + public create(): TPromise { + + // Load Config + const configuration = this.configurationService.getConfiguration(); + this.onConfigurationUpdated(configuration); + + // listeners + this.registerListeners(); + + return super.create(); + } + + private registerListeners(): void { + + // update on model changes + this.toDispose.push(this.model.onModelChanged(e => this.onEditorStacksModelChanged(e))); + + // Also handle configuration updates + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); + + // Handle dirty counter + this.toDispose.push(this.untitledEditorService.onDidChangeDirty(e => this.updateDirtyIndicator())); + this.toDispose.push(this.textFileService.models.onModelsDirty(e => this.updateDirtyIndicator())); + this.toDispose.push(this.textFileService.models.onModelsSaved(e => this.updateDirtyIndicator())); + this.toDispose.push(this.textFileService.models.onModelsSaveError(e => this.updateDirtyIndicator())); + this.toDispose.push(this.textFileService.models.onModelsReverted(e => this.updateDirtyIndicator())); + + // We are not updating the tree while the viewlet is not visible. Thus refresh when viewlet becomes visible #6702 + this.toDispose.push(this.viewletService.onDidViewletOpen(viewlet => { + if (viewlet.getId() === VIEWLET_ID) { + this.fullRefreshNeeded = true; + this.structuralTreeUpdate(); + this.updateDirtyIndicator(); + } + })); + } + + private onEditorStacksModelChanged(e: IStacksModelChangeEvent): void { + if (this.isDisposed || !this.isVisible() || !this.tree) { + return; + } + + // Do a minimal tree update based on if the change is structural or not #6670 + if (e.structural) { + // If an editor changed structurally it is enough to refresh the group, otherwise a group changed structurally and we need the full refresh. + // If there are multiple groups to refresh - refresh the whole tree. + if (e.editor && !this.groupToRefresh) { + this.groupToRefresh = e.group; + } else { + this.fullRefreshNeeded = true; + } + this.structuralTreeRefreshScheduler.schedule(this.structuralRefreshDelay); + } else { + const toRefresh = e.editor ? new OpenEditor(e.editor, e.group) : e.group; + this.tree.refresh(toRefresh, false).done(() => this.highlightActiveEditor(), errors.onUnexpectedError); + } + } + + private structuralTreeUpdate(): void { + // View size + this.setBodySize(this.getExpandedBodySize(this.model)); + // Show groups only if there is more than 1 group + const treeInput = this.model.groups.length === 1 ? this.model.groups[0] : this.model; + // TODO@Isidor temporary workaround due to a partial tree refresh issue + this.fullRefreshNeeded = true; + const toRefresh = this.fullRefreshNeeded ? null : this.groupToRefresh; + + (treeInput !== this.tree.getInput() ? this.tree.setInput(treeInput) : this.tree.refresh(toRefresh)).done(() => { + this.fullRefreshNeeded = false; + this.groupToRefresh = null; + + // Always expand all the groups as they are unclickable + return this.tree.expandAll(this.model.groups).then(() => this.highlightActiveEditor()); + }, errors.onUnexpectedError); + } + + private highlightActiveEditor(): void { + if (this.model.activeGroup && this.model.activeGroup.activeEditor /* could be empty */) { + const openEditor = new OpenEditor(this.model.activeGroup.activeEditor, this.model.activeGroup); + this.tree.clearFocus(); + this.tree.clearSelection(); + + if (openEditor) { + this.tree.setFocus(openEditor); + this.tree.setSelection([openEditor]); + const relativeTop = this.tree.getRelativeTop(openEditor); + if (relativeTop <= 0 || relativeTop >= 1) { + // Only reveal the element if it is not visible #8279 + this.tree.reveal(openEditor).done(null, errors.onUnexpectedError); + } + } + } + } + + private onConfigurationUpdated(configuration: IFilesConfiguration): void { + if (this.isDisposed) { + return; // guard against possible race condition when config change causes recreate of views + } + + let visibleOpenEditors = configuration && configuration.explorer && configuration.explorer.openEditors && configuration.explorer.openEditors.visible; + if (typeof visibleOpenEditors === 'number') { + this.visibleOpenEditors = visibleOpenEditors; + } else { + this.visibleOpenEditors = OpenEditorsView.DEFAULT_VISIBLE_OPEN_EDITORS; + } + + let dynamicHeight = configuration && configuration.explorer && configuration.explorer.openEditors && configuration.explorer.openEditors.dynamicHeight; + if (typeof dynamicHeight === 'boolean') { + this.dynamicHeight = dynamicHeight; + } else { + this.dynamicHeight = OpenEditorsView.DEFAULT_DYNAMIC_HEIGHT; + } + + // Adjust expanded body size + this.setBodySize(this.getExpandedBodySize(this.model)); + } + + private updateDirtyIndicator(): void { + let dirty = this.textFileService.getAutoSaveMode() !== AutoSaveMode.AFTER_SHORT_DELAY ? this.textFileService.getDirty().length + : this.untitledEditorService.getDirty().length; + if (dirty === 0) { + dom.addClass(this.dirtyCountElement, 'hidden'); + } else { + this.dirtyCountElement.textContent = nls.localize('dirtyCounter', "{0} unsaved", dirty); + dom.removeClass(this.dirtyCountElement, 'hidden'); + } + } + + private getExpandedBodySize(model: IEditorStacksModel): number { + return OpenEditorsView.computeExpandedBodySize(model, this.visibleOpenEditors, this.dynamicHeight); + } + + private static computeExpandedBodySize(model: IEditorStacksModel, visibleOpenEditors = OpenEditorsView.DEFAULT_VISIBLE_OPEN_EDITORS, dynamicHeight = OpenEditorsView.DEFAULT_DYNAMIC_HEIGHT): number { + let entryCount = model.groups.reduce((sum, group) => sum + group.count, 0); + // We only show the group labels if there is more than 1 group + if (model.groups.length > 1) { + entryCount += model.groups.length; + } + + let itemsToShow: number; + if (dynamicHeight) { + itemsToShow = Math.min(Math.max(visibleOpenEditors, 1), entryCount); + } else { + itemsToShow = Math.max(visibleOpenEditors, 1); + } + + return itemsToShow * Renderer.ITEM_HEIGHT; + } + + public setStructuralRefreshDelay(delay: number): void { + this.structuralRefreshDelay = delay; + } + + public getOptimalWidth(): number { + let parentNode = this.tree.getHTMLElement(); + let childNodes = [].slice.call(parentNode.querySelectorAll('.open-editor > a')); + + return dom.getLargestChildWidth(parentNode, childNodes); + } +} diff --git a/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts b/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts new file mode 100644 index 0000000000..8ee07dbf96 --- /dev/null +++ b/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts @@ -0,0 +1,512 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import errors = require('vs/base/common/errors'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction } from 'vs/base/common/actions'; +import { EditorLabel } from 'vs/workbench/browser/labels'; +import { DefaultController, ClickBehavior, DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults'; +import { IDataSource, ITree, IAccessibilityProvider, IDragAndDropData, IDragOverReaction, DRAG_OVER_ACCEPT, DRAG_OVER_REJECT, ContextMenuEvent, IRenderer } from 'vs/base/parts/tree/browser/tree'; +import { ExternalElementsDragAndDropData, ElementsDragAndDropData, DesktopDragAndDropData } from 'vs/base/parts/tree/browser/treeDnd'; +import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import dom = require('vs/base/browser/dom'); +import { IMouseEvent, DragMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IResourceInput, Position } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IEditorGroup, IEditorStacksModel } from 'vs/workbench/common/editor'; +import { OpenEditor } from 'vs/workbench/parts/files/common/explorerModel'; +import { ContributableActionProvider } from 'vs/workbench/browser/actions'; +import { explorerItemToFileResource } from 'vs/workbench/parts/files/common/files'; +import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { EditorStacksModel, EditorGroup } from 'vs/workbench/common/editor/editorStacksModel'; +import { SaveFileAction, RevertFileAction, SaveFileAsAction, OpenToSideAction, SelectResourceForCompareAction, CompareResourcesAction, SaveAllInGroupAction, CompareWithSavedAction } from 'vs/workbench/parts/files/browser/fileActions'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { CloseOtherEditorsInGroupAction, CloseEditorAction, CloseEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction } from 'vs/workbench/browser/parts/editor/editorActions'; + +const $ = dom.$; + +export class DataSource implements IDataSource { + + public getId(tree: ITree, element: any): string { + if (element instanceof EditorStacksModel) { + return 'root'; + } + if (element instanceof EditorGroup) { + return (element).id.toString(); + } + + return (element).getId(); + } + + public hasChildren(tree: ITree, element: any): boolean { + return element instanceof EditorStacksModel || element instanceof EditorGroup; + } + + public getChildren(tree: ITree, element: any): TPromise { + if (element instanceof EditorStacksModel) { + return TPromise.as((element).groups); + } + + const editorGroup = element; + return TPromise.as(editorGroup.getEditors().map(ei => new OpenEditor(ei, editorGroup))); + } + + public getParent(tree: ITree, element: any): TPromise { + return TPromise.as(null); + } +} + +interface IOpenEditorTemplateData { + container: HTMLElement; + root: EditorLabel; + actionBar: ActionBar; +} + +interface IEditorGroupTemplateData { + root: HTMLElement; + name: HTMLSpanElement; + actionBar: ActionBar; +} + +export class Renderer implements IRenderer { + + public static ITEM_HEIGHT = 22; + private static EDITOR_GROUP_TEMPLATE_ID = 'editorgroup'; + private static OPEN_EDITOR_TEMPLATE_ID = 'openeditor'; + + constructor( + private actionProvider: ActionProvider, + @IInstantiationService private instantiationService: IInstantiationService, + @IKeybindingService private keybindingService: IKeybindingService + ) { + // noop + } + + public getHeight(tree: ITree, element: any): number { + return Renderer.ITEM_HEIGHT; + } + + public getTemplateId(tree: ITree, element: any): string { + if (element instanceof EditorGroup) { + return Renderer.EDITOR_GROUP_TEMPLATE_ID; + } + + return Renderer.OPEN_EDITOR_TEMPLATE_ID; + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + if (templateId === Renderer.EDITOR_GROUP_TEMPLATE_ID) { + const editorGroupTemplate: IEditorGroupTemplateData = Object.create(null); + editorGroupTemplate.root = dom.append(container, $('.editor-group')); + editorGroupTemplate.name = dom.append(editorGroupTemplate.root, $('span.name')); + editorGroupTemplate.actionBar = new ActionBar(container); + + const editorGroupActions = this.actionProvider.getEditorGroupActions(); + editorGroupActions.forEach(a => { + const key = this.keybindingService.lookupKeybinding(a.id); + editorGroupTemplate.actionBar.push(a, { icon: true, label: false, keybinding: key ? key.getLabel() : void 0 }); + }); + + return editorGroupTemplate; + } + + const editorTemplate: IOpenEditorTemplateData = Object.create(null); + editorTemplate.container = container; + editorTemplate.actionBar = new ActionBar(container); + + const openEditorActions = this.actionProvider.getOpenEditorActions(); + openEditorActions.forEach(a => { + const key = this.keybindingService.lookupKeybinding(a.id); + editorTemplate.actionBar.push(a, { icon: true, label: false, keybinding: key ? key.getLabel() : void 0 }); + }); + + editorTemplate.root = this.instantiationService.createInstance(EditorLabel, container, void 0); + + return editorTemplate; + } + + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + if (templateId === Renderer.EDITOR_GROUP_TEMPLATE_ID) { + this.renderEditorGroup(tree, element, templateData); + } else { + this.renderOpenEditor(tree, element, templateData); + } + } + + private renderEditorGroup(tree: ITree, editorGroup: IEditorGroup, templateData: IEditorGroupTemplateData): void { + templateData.name.textContent = editorGroup.label; + templateData.actionBar.context = { group: editorGroup }; + } + + private renderOpenEditor(tree: ITree, editor: OpenEditor, templateData: IOpenEditorTemplateData): void { + editor.isDirty() ? dom.addClass(templateData.container, 'dirty') : dom.removeClass(templateData.container, 'dirty'); + templateData.root.setEditor(editor.editorInput, { italic: editor.isPreview(), extraClasses: ['open-editor'] }); + templateData.actionBar.context = { group: editor.editorGroup, editor: editor.editorInput }; + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + if (templateId === Renderer.OPEN_EDITOR_TEMPLATE_ID) { + (templateData).actionBar.dispose(); + (templateData).root.dispose(); + } + if (templateId === Renderer.EDITOR_GROUP_TEMPLATE_ID) { + (templateData).actionBar.dispose(); + } + } +} + +export class Controller extends DefaultController { + + constructor(private actionProvider: ActionProvider, private model: IEditorStacksModel, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IContextMenuService private contextMenuService: IContextMenuService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + super({ clickBehavior: ClickBehavior.ON_MOUSE_DOWN, keyboardSupport: false }); + } + + public onClick(tree: ITree, element: any, event: IMouseEvent): boolean { + + // Close opened editor on middle mouse click + if (element instanceof OpenEditor && event.browserEvent && event.browserEvent.button === 1 /* Middle Button */) { + const position = this.model.positionOfGroup(element.editorGroup); + + this.editorService.closeEditor(position, element.editorInput).done(null, errors.onUnexpectedError); + + return true; + } + + return super.onClick(tree, element, event); + } + + protected onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean { + const payload = { origin: origin }; + const isDoubleClick = (origin === 'mouse' && event.detail === 2); + + // Cancel Event + const isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown'; + if (!isMouseDown) { + event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise + } + event.stopPropagation(); + + // Status group should never get selected nor expanded/collapsed + if (!(element instanceof OpenEditor)) { + return true; + } + + // Set DOM focus + tree.DOMFocus(); + + // Allow to unselect + if (event.shiftKey) { + const selection = tree.getSelection(); + if (selection && selection.length > 0 && selection[0] === element) { + tree.clearSelection(payload); + } + } + + // Select, Focus and open files + else { + tree.setFocus(element, payload); + + if (isDoubleClick) { + event.preventDefault(); // focus moves to editor, we need to prevent default + } + + tree.setSelection([element], payload); + this.openEditor(element, { preserveFocus: !isDoubleClick, pinned: isDoubleClick, sideBySide: event.ctrlKey || event.metaKey }); + } + + return true; + } + + // Do not allow left / right to expand and collapse groups #7848 + protected onLeft(tree: ITree, event: IKeyboardEvent): boolean { + return true; + } + + protected onRight(tree: ITree, event: IKeyboardEvent): boolean { + return true; + } + + public onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean { + if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { + return false; + } + // Check if clicked on some element + if (element === tree.getInput()) { + return false; + } + + event.preventDefault(); + event.stopPropagation(); + + tree.setFocus(element); + const group = element instanceof EditorGroup ? element : (element).editorGroup; + const editor = element instanceof OpenEditor ? (element).editorInput : undefined; + + let anchor = { x: event.posx + 1, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this.actionProvider.getSecondaryActions(tree, element), + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + tree.DOMFocus(); + } + }, + getActionsContext: () => ({ group, editor }) + }); + + return true; + } + + public openEditor(element: OpenEditor, options: { preserveFocus: boolean; pinned: boolean; sideBySide: boolean; }): void { + if (element) { + this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'openEditors' }); + let position = this.model.positionOfGroup(element.editorGroup); + if (options.sideBySide && position !== Position.THREE) { + position++; + } + this.editorGroupService.activateGroup(this.model.groupAt(position)); + this.editorService.openEditor(element.editorInput, options, position) + .done(() => this.editorGroupService.activateGroup(this.model.groupAt(position)), errors.onUnexpectedError); + } + } +} + +export class AccessibilityProvider implements IAccessibilityProvider { + + getAriaLabel(tree: ITree, element: any): string { + if (element instanceof EditorGroup) { + return nls.localize('editorGroupAriaLabel', "{0}, Editor Group", (element).label); + } + + return nls.localize('openEditorAriaLabel', "{0}, Open Editor", (element).editorInput.getName()); + } +} + +export class ActionProvider extends ContributableActionProvider { + + constructor( + private model: IEditorStacksModel, + @IInstantiationService private instantiationService: IInstantiationService, + @ITextFileService private textFileService: ITextFileService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService + ) { + super(); + } + + public hasActions(tree: ITree, element: any): boolean { + const multipleGroups = this.model.groups.length > 1; + return element instanceof OpenEditor || (element instanceof EditorGroup && multipleGroups); + } + + public getActions(tree: ITree, element: any): TPromise { + if (element instanceof OpenEditor) { + return TPromise.as(this.getOpenEditorActions()); + } + if (element instanceof EditorGroup) { + return TPromise.as(this.getEditorGroupActions()); + } + + return TPromise.as([]); + } + + public getOpenEditorActions(): IAction[] { + return [this.instantiationService.createInstance(CloseEditorAction, CloseEditorAction.ID, CloseEditorAction.LABEL)]; + } + + public getEditorGroupActions(): IAction[] { + const saveAllAction = this.instantiationService.createInstance(SaveAllInGroupAction, SaveAllInGroupAction.ID, SaveAllInGroupAction.LABEL); + + return [ + saveAllAction, + this.instantiationService.createInstance(CloseUnmodifiedEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction.ID, CloseUnmodifiedEditorsInGroupAction.LABEL), + this.instantiationService.createInstance(CloseEditorsInGroupAction, CloseEditorsInGroupAction.ID, CloseEditorsInGroupAction.LABEL) + ]; + } + + public hasSecondaryActions(tree: ITree, element: any): boolean { + return element instanceof OpenEditor || element instanceof EditorGroup; + } + + public getSecondaryActions(tree: ITree, element: any): TPromise { + return super.getSecondaryActions(tree, element).then(result => { + const autoSaveEnabled = this.textFileService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY; + + if (element instanceof EditorGroup) { + if (!autoSaveEnabled) { + result.push(this.instantiationService.createInstance(SaveAllInGroupAction, SaveAllInGroupAction.ID, nls.localize('saveAll', "Save All"))); + result.push(new Separator()); + } + + result.push(this.instantiationService.createInstance(CloseUnmodifiedEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction.ID, nls.localize('closeAllUnmodified', "Close Unmodified"))); + result.push(this.instantiationService.createInstance(CloseEditorsInGroupAction, CloseEditorsInGroupAction.ID, nls.localize('closeAll', "Close All"))); + } else { + const openEditor = element; + const resource = openEditor.getResource(); + if (resource) { + // Open to side + result.unshift(this.instantiationService.createInstance(OpenToSideAction, tree, resource, false)); + + if (!openEditor.isUntitled()) { + + // Files: Save / Revert + if (!autoSaveEnabled) { + result.push(new Separator()); + + const saveAction = this.instantiationService.createInstance(SaveFileAction, SaveFileAction.ID, SaveFileAction.LABEL); + saveAction.setResource(resource); + saveAction.enabled = openEditor.isDirty(); + result.push(saveAction); + + const revertAction = this.instantiationService.createInstance(RevertFileAction, RevertFileAction.ID, RevertFileAction.LABEL); + revertAction.setResource(resource); + revertAction.enabled = openEditor.isDirty(); + result.push(revertAction); + } + } + + // Untitled: Save / Save As + if (openEditor.isUntitled()) { + result.push(new Separator()); + + if (this.untitledEditorService.hasAssociatedFilePath(resource)) { + let saveUntitledAction = this.instantiationService.createInstance(SaveFileAction, SaveFileAction.ID, SaveFileAction.LABEL); + saveUntitledAction.setResource(resource); + result.push(saveUntitledAction); + } + + let saveAsAction = this.instantiationService.createInstance(SaveFileAsAction, SaveFileAsAction.ID, SaveFileAsAction.LABEL); + saveAsAction.setResource(resource); + result.push(saveAsAction); + } + + // Compare Actions + result.push(new Separator()); + + if (!openEditor.isUntitled()) { + const compareWithSavedAction = this.instantiationService.createInstance(CompareWithSavedAction, CompareWithSavedAction.ID, nls.localize('compareWithSaved', "Compare with Saved")); + compareWithSavedAction.setResource(resource); + compareWithSavedAction.enabled = openEditor.isDirty(); + result.push(compareWithSavedAction); + } + + const runCompareAction = this.instantiationService.createInstance(CompareResourcesAction, resource, tree); + if (runCompareAction._isEnabled()) { + result.push(runCompareAction); + } + result.push(this.instantiationService.createInstance(SelectResourceForCompareAction, resource, tree)); + + result.push(new Separator()); + } + + result.push(this.instantiationService.createInstance(CloseEditorAction, CloseEditorAction.ID, nls.localize('close', "Close"))); + const closeOtherEditorsInGroupAction = this.instantiationService.createInstance(CloseOtherEditorsInGroupAction, CloseOtherEditorsInGroupAction.ID, nls.localize('closeOthers', "Close Others")); + closeOtherEditorsInGroupAction.enabled = openEditor.editorGroup.count > 1; + result.push(closeOtherEditorsInGroupAction); + result.push(this.instantiationService.createInstance(CloseUnmodifiedEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction.ID, nls.localize('closeAllUnmodified', "Close Unmodified"))); + result.push(this.instantiationService.createInstance(CloseEditorsInGroupAction, CloseEditorsInGroupAction.ID, nls.localize('closeAll', "Close All"))); + } + + return result; + }); + } +} + +export class DragAndDrop extends DefaultDragAndDrop { + + constructor( + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEditorGroupService private editorGroupService: IEditorGroupService + ) { + super(); + } + + public getDragURI(tree: ITree, element: OpenEditor): string { + if (!(element instanceof OpenEditor)) { + return null; + } + + const resource = element.getResource(); + // Some open editors do not have a resource so use the name as drag identifier instead #7021 + return resource ? resource.toString() : element.editorInput.getName(); + } + + public getDragLabel(tree: ITree, elements: OpenEditor[]): string { + if (elements.length > 1) { + return String(elements.length); + } + + return elements[0].editorInput.getName(); + } + + public onDragOver(tree: ITree, data: IDragAndDropData, target: OpenEditor | EditorGroup, originalEvent: DragMouseEvent): IDragOverReaction { + if (!(target instanceof OpenEditor) && !(target instanceof EditorGroup)) { + return DRAG_OVER_REJECT; + } + + if (data instanceof ExternalElementsDragAndDropData) { + let resource = explorerItemToFileResource(data.getData()[0]); + + if (!resource) { + return DRAG_OVER_REJECT; + } + + return resource.isDirectory ? DRAG_OVER_REJECT : DRAG_OVER_ACCEPT; + } + + if (data instanceof DesktopDragAndDropData) { + return DRAG_OVER_REJECT; + } + + if (!(data instanceof ElementsDragAndDropData)) { + return DRAG_OVER_REJECT; + } + + return DRAG_OVER_ACCEPT; + } + + public drop(tree: ITree, data: IDragAndDropData, target: OpenEditor | EditorGroup, originalEvent: DragMouseEvent): void { + let draggedElement: OpenEditor | EditorGroup; + const model = this.editorGroupService.getStacksModel(); + const positionOfTargetGroup = model.positionOfGroup(target instanceof EditorGroup ? target : target.editorGroup); + const index = target instanceof OpenEditor ? target.editorGroup.indexOf(target.editorInput) : undefined; + // Support drop from explorer viewer + if (data instanceof ExternalElementsDragAndDropData) { + let resource = explorerItemToFileResource(data.getData()[0]); + (resource as IResourceInput).options = { index, pinned: true }; + this.editorService.openEditor(resource, positionOfTargetGroup).done(null, errors.onUnexpectedError); + } + + // Drop within viewer + else { + let source: OpenEditor | EditorGroup[] = data.getData(); + if (Array.isArray(source)) { + draggedElement = source[0]; + } + } + + if (draggedElement) { + if (draggedElement instanceof OpenEditor) { + this.editorGroupService.moveEditor(draggedElement.editorInput, model.positionOfGroup(draggedElement.editorGroup), positionOfTargetGroup, { index }); + } else { + this.editorGroupService.moveGroup(model.positionOfGroup(draggedElement), positionOfTargetGroup); + } + } + } + + // {{SQL CARBON EDIT}} + public dropAbort(tree: ITree, data: IDragAndDropData): void { } +} diff --git a/src/vs/workbench/parts/files/common/dirtyFilesTracker.ts b/src/vs/workbench/parts/files/common/dirtyFilesTracker.ts new file mode 100644 index 0000000000..f01d5a014c --- /dev/null +++ b/src/vs/workbench/parts/files/common/dirtyFilesTracker.ts @@ -0,0 +1,164 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import errors = require('vs/base/common/errors'); +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { VIEWLET_ID } from 'vs/workbench/parts/files/common/files'; +import { TextFileModelChangeEvent, ITextFileService, AutoSaveMode, ModelState } from 'vs/workbench/services/textfile/common/textfiles'; +import { platform, Platform } from 'vs/base/common/platform'; +import { Position } from 'vs/platform/editor/common/editor'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IEditorStacksModel } from 'vs/workbench/common/editor'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import URI from 'vs/base/common/uri'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IActivityBarService, NumberBadge } from 'vs/workbench/services/activity/common/activityBarService'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import arrays = require('vs/base/common/arrays'); + +export class DirtyFilesTracker implements IWorkbenchContribution { + private isDocumentedEdited: boolean; + private toUnbind: IDisposable[]; + private lastDirtyCount: number; + private stacks: IEditorStacksModel; + private badgeHandle: IDisposable; + + constructor( + @ITextFileService private textFileService: ITextFileService, + @ILifecycleService private lifecycleService: ILifecycleService, + @IEditorGroupService editorGroupService: IEditorGroupService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IActivityBarService private activityBarService: IActivityBarService, + @IWindowService private windowService: IWindowService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService + ) { + this.toUnbind = []; + this.isDocumentedEdited = false; + this.stacks = editorGroupService.getStacksModel(); + + this.registerListeners(); + } + + private registerListeners(): void { + + // Local text file changes + this.toUnbind.push(this.untitledEditorService.onDidChangeDirty(e => this.onUntitledDidChangeDirty(e))); + this.toUnbind.push(this.textFileService.models.onModelsDirty(e => this.onTextFilesDirty(e))); + this.toUnbind.push(this.textFileService.models.onModelsSaved(e => this.onTextFilesSaved(e))); + this.toUnbind.push(this.textFileService.models.onModelsSaveError(e => this.onTextFilesSaveError(e))); + this.toUnbind.push(this.textFileService.models.onModelsReverted(e => this.onTextFilesReverted(e))); + + // Lifecycle + this.lifecycleService.onShutdown(this.dispose, this); + } + + private onUntitledDidChangeDirty(resource: URI): void { + const gotDirty = this.untitledEditorService.isDirty(resource); + + if ((!this.isDocumentedEdited && gotDirty) || (this.isDocumentedEdited && !gotDirty)) { + this.updateDocumentEdited(); + } + + if (gotDirty || this.lastDirtyCount > 0) { + this.updateActivityBadge(); + } + } + + private onTextFilesDirty(e: TextFileModelChangeEvent[]): void { + if ((this.textFileService.getAutoSaveMode() !== AutoSaveMode.AFTER_SHORT_DELAY) && !this.isDocumentedEdited) { + this.updateDocumentEdited(); // no indication needed when auto save is enabled for short delay + } + + if (this.textFileService.getAutoSaveMode() !== AutoSaveMode.AFTER_SHORT_DELAY) { + this.updateActivityBadge(); // no indication needed when auto save is enabled for short delay + } + + // If files become dirty but are not opened, we open it in the background unless there are pending to be saved + this.doOpenDirtyResources(arrays.distinct(e.filter(e => { + + // Only dirty models that are not PENDING_SAVE + const model = this.textFileService.models.get(e.resource); + const shouldOpen = model && model.isDirty() && !model.hasState(ModelState.PENDING_SAVE); + + // Only if not open already + return shouldOpen && !this.stacks.isOpen(e.resource); + }).map(e => e.resource), r => r.toString())); + } + + private doOpenDirtyResources(resources: URI[]): void { + const activeEditor = this.editorService.getActiveEditor(); + const activePosition = activeEditor ? activeEditor.position : Position.ONE; + + // Open + this.editorService.openEditors(resources.map(resource => { + return { + input: { + resource, + options: { inactive: true, pinned: true, preserveFocus: true } + }, + position: activePosition + }; + })).done(null, errors.onUnexpectedError); + } + + private onTextFilesSaved(e: TextFileModelChangeEvent[]): void { + if (this.isDocumentedEdited) { + this.updateDocumentEdited(); + } + + if (this.lastDirtyCount > 0) { + this.updateActivityBadge(); + } + } + + private onTextFilesSaveError(e: TextFileModelChangeEvent[]): void { + if (!this.isDocumentedEdited) { + this.updateDocumentEdited(); + } + + this.updateActivityBadge(); + } + + private onTextFilesReverted(e: TextFileModelChangeEvent[]): void { + if (this.isDocumentedEdited) { + this.updateDocumentEdited(); + } + + if (this.lastDirtyCount > 0) { + this.updateActivityBadge(); + } + } + + private updateActivityBadge(): void { + const dirtyCount = this.textFileService.getDirty().length; + this.lastDirtyCount = dirtyCount; + dispose(this.badgeHandle); + if (dirtyCount > 0) { + this.badgeHandle = this.activityBarService.showActivity(VIEWLET_ID, new NumberBadge(dirtyCount, num => nls.localize('dirtyFiles', "{0} unsaved files", dirtyCount)), 'explorer-viewlet-label'); + } + } + + private updateDocumentEdited(): void { + if (platform === Platform.Mac) { + const hasDirtyFiles = this.textFileService.isDirty(); + this.isDocumentedEdited = hasDirtyFiles; + + this.windowService.setDocumentEdited(hasDirtyFiles); + } + } + + public getId(): string { + return 'vs.files.dirtyFilesTracker'; + } + + public dispose(): void { + this.toUnbind = dispose(this.toUnbind); + } +} diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts new file mode 100644 index 0000000000..24a4e3a137 --- /dev/null +++ b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts @@ -0,0 +1,277 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import paths = require('vs/base/common/paths'); +import labels = require('vs/base/common/labels'); +import URI from 'vs/base/common/uri'; +import { EncodingMode, ConfirmResult, EditorInput, IFileEditorInput, ITextEditorModel } from 'vs/workbench/common/editor'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; +import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; +import { BINARY_FILE_EDITOR_ID, TEXT_FILE_EDITOR_ID, FILE_EDITOR_INPUT_ID } from 'vs/workbench/parts/files/common/files'; +import { ITextFileService, AutoSaveMode, ModelState, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; +import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils'; +import { Verbosity } from 'vs/platform/editor/common/editor'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; + +/** + * A file editor input is the input type for the file editor of file system resources. + */ +export class FileEditorInput extends EditorInput implements IFileEditorInput { + private forceOpenAsBinary: boolean; + + private textModelReference: TPromise>; + + private name: string; + private description: string; + private verboseDescription: string; + + private shortTitle: string; + private mediumTitle: string; + private longTitle: string; + + private toUnbind: IDisposable[]; + + /** + * An editor input who's contents are retrieved from file services. + */ + constructor( + private resource: URI, + private preferredEncoding: string, + @IInstantiationService private instantiationService: IInstantiationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ITextFileService private textFileService: ITextFileService, + @IEnvironmentService private environmentService: IEnvironmentService, + @ITextModelService private textModelResolverService: ITextModelService + ) { + super(); + + this.toUnbind = []; + + this.registerListeners(); + } + + private registerListeners(): void { + + // Model changes + this.toUnbind.push(this.textFileService.models.onModelDirty(e => this.onDirtyStateChange(e))); + this.toUnbind.push(this.textFileService.models.onModelSaveError(e => this.onDirtyStateChange(e))); + this.toUnbind.push(this.textFileService.models.onModelSaved(e => this.onDirtyStateChange(e))); + this.toUnbind.push(this.textFileService.models.onModelReverted(e => this.onDirtyStateChange(e))); + this.toUnbind.push(this.textFileService.models.onModelOrphanedChanged(e => this.onModelOrphanedChanged(e))); + } + + private onDirtyStateChange(e: TextFileModelChangeEvent): void { + if (e.resource.toString() === this.resource.toString()) { + this._onDidChangeDirty.fire(); + } + } + + private onModelOrphanedChanged(e: TextFileModelChangeEvent): void { + if (e.resource.toString() === this.resource.toString()) { + this._onDidChangeLabel.fire(); + } + } + + public getResource(): URI { + return this.resource; + } + + public setPreferredEncoding(encoding: string): void { + this.preferredEncoding = encoding; + } + + public getEncoding(): string { + const textModel = this.textFileService.models.get(this.resource); + if (textModel) { + return textModel.getEncoding(); + } + + return this.preferredEncoding; + } + + public getPreferredEncoding(): string { + return this.preferredEncoding; + } + + public setEncoding(encoding: string, mode: EncodingMode): void { + this.preferredEncoding = encoding; + + const textModel = this.textFileService.models.get(this.resource); + if (textModel) { + textModel.setEncoding(encoding, mode); + } + } + + public setForceOpenAsBinary(): void { + this.forceOpenAsBinary = true; + } + + public getTypeId(): string { + return FILE_EDITOR_INPUT_ID; + } + + public getName(): string { + if (!this.name) { + this.name = paths.basename(this.resource.fsPath); + } + + return this.decorateOrphanedFiles(this.name); + } + + public getDescription(verbose?: boolean): string { + if (verbose) { + if (!this.verboseDescription) { + this.verboseDescription = labels.getPathLabel(paths.dirname(this.resource.fsPath), void 0, this.environmentService); + } + } else { + if (!this.description) { + this.description = labels.getPathLabel(paths.dirname(this.resource.fsPath), this.contextService, this.environmentService); + } + } + + return verbose ? this.verboseDescription : this.description; + } + + public getTitle(verbosity: Verbosity): string { + let title: string; + switch (verbosity) { + case Verbosity.SHORT: + title = this.shortTitle ? this.shortTitle : (this.shortTitle = this.getName()); + break; + case Verbosity.MEDIUM: + title = this.mediumTitle ? this.mediumTitle : (this.mediumTitle = labels.getPathLabel(this.resource, this.contextService, this.environmentService)); + break; + case Verbosity.LONG: + title = this.longTitle ? this.longTitle : (this.longTitle = labels.getPathLabel(this.resource, void 0, this.environmentService)); + break; + } + + return this.decorateOrphanedFiles(title); + } + + private decorateOrphanedFiles(label: string): string { + const model = this.textFileService.models.get(this.resource); + if (model && model.hasState(ModelState.ORPHAN)) { + return localize('orphanedFile', "{0} (deleted from disk)", label); + } + + return label; + } + + public isDirty(): boolean { + const model = this.textFileService.models.get(this.resource); + if (!model) { + return false; + } + + if (model.hasState(ModelState.CONFLICT) || model.hasState(ModelState.ERROR)) { + return true; // always indicate dirty state if we are in conflict or error state + } + + if (this.textFileService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { + return false; // fast auto save enabled so we do not declare dirty + } + + return model.isDirty(); + } + + public confirmSave(): ConfirmResult { + return this.textFileService.confirmSave([this.resource]); + } + + public save(): TPromise { + return this.textFileService.save(this.resource); + } + + public revert(): TPromise { + return this.textFileService.revert(this.resource); + } + + public getPreferredEditorId(candidates: string[]): string { + return this.forceOpenAsBinary ? BINARY_FILE_EDITOR_ID : TEXT_FILE_EDITOR_ID; + } + + public resolve(refresh?: boolean): TPromise { + + // Resolve as binary + if (this.forceOpenAsBinary) { + return this.resolveAsBinary(); + } + + // Resolve as text + return this.textFileService.models.loadOrCreate(this.resource, { encoding: this.preferredEncoding, reload: refresh }).then(model => { + + // TODO@Ben this is a bit ugly, because we first resolve the model and then resolve a model reference. the reason being that binary + // or very large files do not resolve to a text file model but should be opened as binary files without text. First calling into + // loadOrCreate ensures we are not creating model references for these kind of resources. + // In addition we have a bit of payload to take into account (encoding, reload) that the text resolver does not handle yet. + if (!this.textModelReference) { + this.textModelReference = this.textModelResolverService.createModelReference(this.resource); + } + + return this.textModelReference.then(ref => ref.object as TextFileEditorModel); + }, error => { + + // In case of an error that indicates that the file is binary or too large, just return with the binary editor model + if ((error).fileOperationResult === FileOperationResult.FILE_IS_BINARY || (error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE) { + return this.resolveAsBinary(); + } + + // Bubble any other error up + return TPromise.wrapError(error); + }); + } + + private resolveAsBinary(): TPromise { + return this.instantiationService.createInstance(BinaryEditorModel, this.resource, this.getName()) + .load() + .then(x => x as BinaryEditorModel); + } + + public isResolved(): boolean { + return !!this.textFileService.models.get(this.resource); + } + + public getTelemetryDescriptor(): object { + const descriptor = super.getTelemetryDescriptor(); + descriptor['resource'] = telemetryURIDescriptor(this.getResource()); + + return descriptor; + } + + public dispose(): void { + + // Model reference + if (this.textModelReference) { + this.textModelReference.done(ref => ref.dispose()); + this.textModelReference = null; + } + + // Listeners + this.toUnbind = dispose(this.toUnbind); + + super.dispose(); + } + + public matches(otherInput: any): boolean { + if (super.matches(otherInput) === true) { + return true; + } + + if (otherInput) { + return otherInput instanceof FileEditorInput && otherInput.resource.toString() === this.resource.toString(); + } + + return false; + } +} diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts new file mode 100644 index 0000000000..f898507569 --- /dev/null +++ b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts @@ -0,0 +1,319 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import errors = require('vs/base/common/errors'); +import URI from 'vs/base/common/uri'; +import paths = require('vs/base/common/paths'); +import { IEditor, IEditorViewState, isCommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { toResource, IEditorStacksModel, SideBySideEditorInput, IEditorGroup, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; +import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; +import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent, indexOf } from 'vs/platform/files/common/files'; +import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { distinct } from 'vs/base/common/arrays'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { isLinux } from 'vs/base/common/platform'; + +export class FileEditorTracker implements IWorkbenchContribution { + private stacks: IEditorStacksModel; + private toUnbind: IDisposable[]; + protected closeOnFileDelete: boolean; + + constructor( + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @ITextFileService private textFileService: ITextFileService, + @ILifecycleService private lifecycleService: ILifecycleService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IFileService private fileService: IFileService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IConfigurationService private configurationService: IConfigurationService + ) { + this.toUnbind = []; + this.stacks = editorGroupService.getStacksModel(); + + this.onConfigurationUpdated(configurationService.getConfiguration()); + + this.registerListeners(); + } + + public getId(): string { + return 'vs.files.fileEditorTracker'; + } + + private registerListeners(): void { + + // Update editors from operation changes + this.toUnbind.push(this.fileService.onAfterOperation(e => this.onFileOperation(e))); + + // Update editors from disk changes + this.toUnbind.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); + + // Lifecycle + this.lifecycleService.onShutdown(this.dispose, this); + + // Configuration + this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); + } + + private onConfigurationUpdated(configuration: IWorkbenchEditorConfiguration): void { + if (configuration.workbench && configuration.workbench.editor && typeof configuration.workbench.editor.closeOnFileDelete === 'boolean') { + this.closeOnFileDelete = configuration.workbench.editor.closeOnFileDelete; + } else { + this.closeOnFileDelete = true; // default + } + } + + // Note: there is some duplication with the other file event handler below. Since we cannot always rely on the disk events + // carrying all necessary data in all environments, we also use the file operation events to make sure operations are handled. + // In any case there is no guarantee if the local event is fired first or the disk one. Thus, code must handle the case + // that the event ordering is random as well as might not carry all information needed. + private onFileOperation(e: FileOperationEvent): void { + + // Handle moves specially when file is opened + if (e.operation === FileOperation.MOVE) { + this.handleMovedFileInOpenedEditors(e.resource, e.target.resource); + } + + // Handle deletes + if (e.operation === FileOperation.DELETE || e.operation === FileOperation.MOVE) { + this.handleDeletes(e.resource, false, e.target ? e.target.resource : void 0); + } + } + + private onFileChanges(e: FileChangesEvent): void { + + // Handle updates + this.handleUpdates(e); + + // Handle deletes + if (e.gotDeleted()) { + this.handleDeletes(e, true); + } + } + + private handleDeletes(arg1: URI | FileChangesEvent, isExternal: boolean, movedTo?: URI): void { + const nonDirtyFileEditors = this.getOpenedFileEditors(false /* non-dirty only */); + nonDirtyFileEditors.forEach(editor => { + const resource = editor.getResource(); + + // Handle deletes in opened editors depending on: + // - the user has not disabled the setting closeOnFileDelete + // - the file change is local or external + // - the input is not resolved (we need to dispose because we cannot restore otherwise since we do not have the contents) + if (this.closeOnFileDelete || !isExternal || !editor.isResolved()) { + + // Do NOT close any opened editor that matches the resource path (either equal or being parent) of the + // resource we move to (movedTo). Otherwise we would close a resource that has been renamed to the same + // path but different casing. + if (movedTo && paths.isEqualOrParent(resource.fsPath, movedTo.fsPath, !isLinux /* ignorecase */) && resource.fsPath.indexOf(movedTo.fsPath) === 0) { + return; + } + + let matches = false; + if (arg1 instanceof FileChangesEvent) { + matches = arg1.contains(resource, FileChangeType.DELETED); + } else { + matches = paths.isEqualOrParent(resource.fsPath, arg1.fsPath, !isLinux /* ignorecase */); + } + + if (!matches) { + return; + } + + // We have received reports of users seeing delete events even though the file still + // exists (network shares issue: https://github.com/Microsoft/vscode/issues/13665). + // Since we do not want to close an editor without reason, we have to check if the + // file is really gone and not just a faulty file event (TODO@Ben revisit when we + // have a more stable file watcher in place for this scenario). + // This only applies to external file events, so we need to check for the isExternal + // flag. + let checkExists: TPromise; + if (isExternal) { + checkExists = TPromise.timeout(100).then(() => this.fileService.existsFile(resource)); + } else { + checkExists = TPromise.as(false); + } + + checkExists.done(exists => { + if (!exists && !editor.isDisposed()) { + editor.dispose(); + } else if (this.environmentService.verbose) { + console.warn(`File exists even though we received a delete event: ${resource.toString()}`); + } + }); + } + }); + } + + private getOpenedFileEditors(dirtyState: boolean): FileEditorInput[] { + const editors: FileEditorInput[] = []; + + const stacks = this.editorGroupService.getStacksModel(); + stacks.groups.forEach(group => { + group.getEditors().forEach(editor => { + if (editor instanceof FileEditorInput) { + if (!!editor.isDirty() === dirtyState) { + editors.push(editor); + } + } else if (editor instanceof SideBySideEditorInput) { + const master = editor.master; + const details = editor.details; + + if (master instanceof FileEditorInput) { + if (!!master.isDirty() === dirtyState) { + editors.push(master); + } + } + + if (details instanceof FileEditorInput) { + if (!!details.isDirty() === dirtyState) { + editors.push(details); + } + } + } + }); + }); + + return editors; + } + + private handleMovedFileInOpenedEditors(oldResource: URI, newResource: URI): void { + const stacks = this.editorGroupService.getStacksModel(); + stacks.groups.forEach(group => { + group.getEditors().forEach(input => { + if (input instanceof FileEditorInput) { + const resource = input.getResource(); + + // Update Editor if file (or any parent of the input) got renamed or moved + if (paths.isEqualOrParent(resource.fsPath, oldResource.fsPath, !isLinux /* ignorecase */)) { + let reopenFileResource: URI; + if (oldResource.toString() === resource.toString()) { + reopenFileResource = newResource; // file got moved + } else { + const index = indexOf(resource.fsPath, oldResource.fsPath, !isLinux /* ignorecase */); + reopenFileResource = URI.file(paths.join(newResource.fsPath, resource.fsPath.substr(index + oldResource.fsPath.length + 1))); // parent folder got moved + } + + // Reopen + this.editorService.openEditor({ + resource: reopenFileResource, + options: { + preserveFocus: true, + pinned: group.isPinned(input), + index: group.indexOf(input), + inactive: !group.isActive(input), + viewState: this.getViewStateFor(oldResource, group) + } + }, stacks.positionOfGroup(group)).done(null, errors.onUnexpectedError); + } + } + }); + }); + } + + private getViewStateFor(resource: URI, group: IEditorGroup): IEditorViewState { + const stacks = this.editorGroupService.getStacksModel(); + const editors = this.editorService.getVisibleEditors(); + + for (let i = 0; i < editors.length; i++) { + const editor = editors[i]; + if (editor && editor.position === stacks.positionOfGroup(group)) { + const resource = toResource(editor.input, { filter: 'file' }); + if (resource && paths.isEqual(resource.fsPath, resource.fsPath)) { + const control = editor.getControl(); + if (isCommonCodeEditor(control)) { + return control.saveViewState(); + } + } + } + } + + return void 0; + } + + private handleUpdates(e: FileChangesEvent): void { + + // Collect distinct (saved) models to update. + // + // Note: we also consider the added event because it could be that a file was added + // and updated right after. + const modelsToUpdate = distinct([...e.getUpdated(), ...e.getAdded()] + .map(u => this.textFileService.models.get(u.resource)) + .filter(model => model && !model.isDirty()), m => m.getResource().toString()); + + // Handle updates to visible editors specially to preserve view state + const visibleModels = this.handleUpdatesToVisibleEditors(e); + + // Handle updates to remaining models that are not visible + modelsToUpdate.forEach(model => { + if (visibleModels.indexOf(model) >= 0) { + return; // already updated + } + + // Load model to update + model.load().done(null, errors.onUnexpectedError); + }); + } + + private handleUpdatesToVisibleEditors(e: FileChangesEvent): ITextFileEditorModel[] { + const updatedModels: ITextFileEditorModel[] = []; + + const editors = this.editorService.getVisibleEditors(); + editors.forEach(editor => { + const fileResource = toResource(editor.input, { filter: 'file', supportSideBySide: true }); + + // File Editor + if (fileResource) { + + // File got added or updated, so check for model and update + // Note: we also consider the added event because it could be that a file was added + // and updated right after. + if (e.contains(fileResource, FileChangeType.UPDATED) || e.contains(fileResource, FileChangeType.ADDED)) { + + // Text file: check for last save time + const textModel = this.textFileService.models.get(fileResource); + if (textModel) { + + // We only ever update models that are in good saved state + if (!textModel.isDirty()) { + const codeEditor = editor.getControl() as IEditor; + const viewState = codeEditor.saveViewState(); + const lastKnownEtag = textModel.getETag(); + + textModel.load().done(() => { + + // only restore the view state if the model changed and the editor is still showing it + if (textModel.getETag() !== lastKnownEtag && codeEditor.getModel() === textModel.textEditorModel) { + codeEditor.restoreViewState(viewState); + } + }, errors.onUnexpectedError); + + updatedModels.push(textModel); + } + } + + // Binary file: always update + else if (editor.getId() === BINARY_FILE_EDITOR_ID) { + this.editorService.openEditor(editor.input, { forceOpen: true, preserveFocus: true }, editor.position).done(null, errors.onUnexpectedError); + } + } + } + }); + + return updatedModels; + } + + public dispose(): void { + this.toUnbind = dispose(this.toUnbind); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts new file mode 100644 index 0000000000..c4b5b51112 --- /dev/null +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -0,0 +1,426 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import URI from 'vs/base/common/uri'; +import paths = require('vs/base/common/paths'); +import { ResourceMap } from 'vs/base/common/map'; +import { isLinux } from 'vs/base/common/platform'; +import { IFileStat, isParent } from 'vs/platform/files/common/files'; +import { IEditorInput } from 'vs/platform/editor/common/editor'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IEditorGroup, toResource } from 'vs/workbench/common/editor'; + +export enum StatType { + FILE, + FOLDER, + ANY +} + +export class Model { + + private _roots: FileStat[]; + + constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) { + const setRoots = () => this._roots = this.contextService.getWorkspace().roots.map(uri => new FileStat(uri, undefined)); + this.contextService.onDidChangeWorkspaceRoots(() => setRoots()); + setRoots(); + } + + public get roots(): FileStat[] { + return this._roots; + } + + /** + * Returns an array of child stat from this stat that matches with the provided path. + * Starts matching from the first root. + * Will return empty array in case the FileStat does not exist. + */ + public findAll(resource: URI): FileStat[] { + return this.roots.map(root => root.find(resource)).filter(stat => !!stat); + } + + /** + * Returns a FileStat that matches the passed resource. + * In case multiple FileStat are matching the resource (same folder opened multiple times) returns the FileStat that has the closest root. + * Will return null in case the FileStat does not exist. + */ + public findClosest(resource: URI): FileStat { + const rootUri = this.contextService.getRoot(resource); + if (rootUri) { + const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); + if (root) { + return root.find(resource); + } + } + + return null; + } +} + +export class FileStat implements IFileStat { + public resource: URI; + public name: string; + public mtime: number; + public etag: string; + public isDirectory: boolean; + public hasChildren: boolean; + public children: FileStat[]; + public parent: FileStat; + + public exists: boolean; + public isDirectoryResolved: boolean; + + constructor(resource: URI, public root: FileStat, isDirectory?: boolean, hasChildren?: boolean, name: string = paths.basename(resource.fsPath), mtime?: number, etag?: string) { + this.resource = resource; + this.name = name; + this.isDirectory = !!isDirectory; + this.hasChildren = isDirectory && hasChildren; + this.etag = etag; + this.mtime = mtime; + + // Prepare child stat array + if (this.isDirectory) { + this.children = []; + } + if (!this.root) { + this.root = this; + } + + this.isDirectoryResolved = false; + this.exists = true; + } + + public getId(): string { + return this.resource.toString(); + } + + public get isRoot(): boolean { + return this.resource.toString() === this.root.resource.toString(); + } + + public static create(raw: IFileStat, root: FileStat, resolveTo?: URI[]): FileStat { + const stat = new FileStat(raw.resource, root, raw.isDirectory, raw.hasChildren, raw.name, raw.mtime, raw.etag); + + // Recursively add children if present + if (stat.isDirectory) { + + // isDirectoryResolved is a very important indicator in the stat model that tells if the folder was fully resolved + // the folder is fully resolved if either it has a list of children or the client requested this by using the resolveTo + // array of resource path to resolve. + stat.isDirectoryResolved = !!raw.children || (!!resolveTo && resolveTo.some((r) => { + return paths.isEqualOrParent(r.fsPath, stat.resource.fsPath, !isLinux /* ignorecase */); + })); + + // Recurse into children + if (raw.children) { + for (let i = 0, len = raw.children.length; i < len; i++) { + const child = FileStat.create(raw.children[i], root, resolveTo); + child.parent = stat; + stat.children.push(child); + stat.hasChildren = stat.children.length > 0; + } + } + } + + return stat; + } + + /** + * Merges the stat which was resolved from the disk with the local stat by copying over properties + * and children. The merge will only consider resolved stat elements to avoid overwriting data which + * exists locally. + */ + public static mergeLocalWithDisk(disk: FileStat, local: FileStat): void { + if (disk.resource.toString() !== local.resource.toString()) { + return; // Merging only supported for stats with the same resource + } + + // Stop merging when a folder is not resolved to avoid loosing local data + const mergingDirectories = disk.isDirectory || local.isDirectory; + if (mergingDirectories && local.isDirectoryResolved && !disk.isDirectoryResolved) { + return; + } + + // Properties + local.resource = disk.resource; + local.name = disk.name; + local.isDirectory = disk.isDirectory; + local.hasChildren = disk.isDirectory && disk.hasChildren; + local.mtime = disk.mtime; + local.isDirectoryResolved = disk.isDirectoryResolved; + + // Merge Children if resolved + if (mergingDirectories && disk.isDirectoryResolved) { + + // Map resource => stat + const oldLocalChildren = new ResourceMap(); + if (local.children) { + local.children.forEach((localChild: FileStat) => { + oldLocalChildren.set(localChild.resource, localChild); + }); + } + + // Clear current children + local.children = []; + + // Merge received children + disk.children.forEach((diskChild: FileStat) => { + const formerLocalChild = oldLocalChildren.get(diskChild.resource); + + // Existing child: merge + if (formerLocalChild) { + FileStat.mergeLocalWithDisk(diskChild, formerLocalChild); + formerLocalChild.parent = local; + local.children.push(formerLocalChild); + } + + // New child: add + else { + diskChild.parent = local; + local.children.push(diskChild); + } + }); + } + } + + /** + * Adds a child element to this folder. + */ + public addChild(child: FileStat): void { + + // Inherit some parent properties to child + child.parent = this; + child.updateResource(false); + + this.children.push(child); + this.hasChildren = this.children.length > 0; + } + + /** + * Returns true if this stat is a directory that contains a child with the given name. + * + * @param ignoreCase if true, will check for the name ignoring case. + * @param type the type of stat to check for. + */ + public hasChild(name: string, ignoreCase?: boolean, type: StatType = StatType.ANY): boolean { + for (let i = 0; i < this.children.length; i++) { + const child = this.children[i]; + if ((type === StatType.FILE && child.isDirectory) || (type === StatType.FOLDER && !child.isDirectory)) { + continue; + } + + // Check for Identity + if (child.name === name) { + return true; + } + + // Also consider comparing without case + if (ignoreCase && child.name.toLowerCase() === name.toLowerCase()) { + return true; + } + } + + return false; + } + + /** + * Removes a child element from this folder. + */ + public removeChild(child: FileStat): void { + for (let i = 0; i < this.children.length; i++) { + if (this.children[i].resource.toString() === child.resource.toString()) { + this.children.splice(i, 1); + break; + } + } + + this.hasChildren = this.children.length > 0; + } + + /** + * Moves this element under a new parent element. + */ + public move(newParent: FileStat, fnBetweenStates?: (callback: () => void) => void, fnDone?: () => void): void { + if (!fnBetweenStates) { + fnBetweenStates = (cb: () => void) => { cb(); }; + } + + this.parent.removeChild(this); + + fnBetweenStates(() => { + newParent.removeChild(this); // make sure to remove any previous version of the file if any + newParent.addChild(this); + this.updateResource(true); + if (fnDone) { + fnDone(); + } + }); + } + + private updateResource(recursive: boolean): void { + this.resource = URI.file(paths.join(this.parent.resource.fsPath, this.name)); + + if (recursive) { + if (this.isDirectory && this.hasChildren && this.children) { + this.children.forEach((child: FileStat) => { + child.updateResource(true); + }); + } + } + } + + /** + * Tells this stat that it was renamed. This requires changes to all children of this stat (if any) + * so that the path property can be updated properly. + */ + public rename(renamedStat: IFileStat): void { + + // Merge a subset of Properties that can change on rename + this.name = renamedStat.name; + this.mtime = renamedStat.mtime; + + // Update Paths including children + this.updateResource(true); + } + + /** + * Returns a child stat from this stat that matches with the provided path. + * Will return "null" in case the child does not exist. + */ + public find(resource: URI): FileStat { + + // Return if path found + if (paths.isEqual(resource.fsPath, this.resource.fsPath, !isLinux /* ignorecase */)) { + return this; + } + + // Return if not having any children + if (!this.hasChildren) { + return null; + } + + for (let i = 0; i < this.children.length; i++) { + const child = this.children[i]; + + if (paths.isEqual(resource.fsPath, child.resource.fsPath, !isLinux /* ignorecase */)) { + return child; + } + + if (child.isDirectory && isParent(resource.fsPath, child.resource.fsPath, !isLinux /* ignorecase */)) { + return child.find(resource); + } + } + + return null; //Unable to find + } +} + +/* A helper that can be used to show a placeholder when creating a new stat */ +export class NewStatPlaceholder extends FileStat { + + private static ID = 0; + + private id: number; + private directoryPlaceholder: boolean; + + constructor(isDirectory: boolean, root: FileStat) { + super(URI.file(''), root); + + this.id = NewStatPlaceholder.ID++; + this.isDirectoryResolved = isDirectory; + this.directoryPlaceholder = isDirectory; + } + + public destroy(): void { + this.parent.removeChild(this); + + this.isDirectoryResolved = void 0; + this.name = void 0; + this.isDirectory = void 0; + this.hasChildren = void 0; + this.mtime = void 0; + } + + public getId(): string { + return `new-stat-placeholder:${this.id}:${this.parent.resource.toString()}`; + } + + public isDirectoryPlaceholder(): boolean { + return this.directoryPlaceholder; + } + + public addChild(child: NewStatPlaceholder): void { + throw new Error('Can\'t perform operations in NewStatPlaceholder.'); + } + + public hasChild(name: string, ignoreCase?: boolean): boolean { + return false; + } + + public removeChild(child: NewStatPlaceholder): void { + throw new Error('Can\'t perform operations in NewStatPlaceholder.'); + } + + public move(newParent: NewStatPlaceholder): void { + throw new Error('Can\'t perform operations in NewStatPlaceholder.'); + } + + public rename(renamedStat: NewStatPlaceholder): void { + throw new Error('Can\'t perform operations in NewStatPlaceholder.'); + } + + public find(resource: URI): NewStatPlaceholder { + return null; + } + + public static addNewStatPlaceholder(parent: FileStat, isDirectory: boolean): NewStatPlaceholder { + const child = new NewStatPlaceholder(isDirectory, parent.root); + + // Inherit some parent properties to child + child.parent = parent; + parent.children.push(child); + + parent.hasChildren = parent.children.length > 0; + + return child; + } +} + +export class OpenEditor { + + constructor(private editor: IEditorInput, private group: IEditorGroup) { + // noop + } + + public get editorInput() { + return this.editor; + } + + public get editorGroup() { + return this.group; + } + + public getId(): string { + return `openeditor:${this.group.id}:${this.group.indexOf(this.editor)}:${this.editor.getName()}:${this.editor.getDescription()}`; + } + + public isPreview(): boolean { + return this.group.isPreview(this.editor); + } + + public isUntitled(): boolean { + return !!toResource(this.editor, { supportSideBySide: true, filter: 'untitled' }); + } + + public isDirty(): boolean { + return this.editor.isDirty(); + } + + public getResource(): URI { + return toResource(this.editor, { supportSideBySide: true, filter: ['file', 'untitled'] }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts new file mode 100644 index 0000000000..737a18d832 --- /dev/null +++ b/src/vs/workbench/parts/files/common/files.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; +import { IFilesConfiguration } from 'vs/platform/files/common/files'; +import { FileStat, OpenEditor } from 'vs/workbench/parts/files/common/explorerModel'; +import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +/** + * Explorer viewlet id. + */ +export const VIEWLET_ID = 'workbench.view.explorer'; + +/** + * Context Keys to use with keybindings for the Explorer and Open Editors view + */ +const explorerViewletVisibleId = 'explorerViewletVisible'; +const filesExplorerFocusId = 'filesExplorerFocus'; +const openEditorsVisibleId = 'openEditorsVisible'; +const openEditorsFocusId = 'openEditorsFocus'; +const explorerViewletFocusId = 'explorerViewletFocus'; +const explorerResourceIsFolderId = 'explorerResourceIsFolder'; + +export const ExplorerViewletVisibleContext = new RawContextKey(explorerViewletVisibleId, true); +export const ExplorerFolderContext = new RawContextKey(explorerResourceIsFolderId, false); +export const FilesExplorerFocusedContext = new RawContextKey(filesExplorerFocusId, false); +export const OpenEditorsVisibleContext = new RawContextKey(openEditorsVisibleId, false); +export const OpenEditorsFocusedContext = new RawContextKey(openEditorsFocusId, false); +export const ExplorerFocusedContext = new RawContextKey(explorerViewletFocusId, false); + +export const OpenEditorsVisibleCondition = ContextKeyExpr.has(openEditorsVisibleId); +export const FilesExplorerFocusCondition = ContextKeyExpr.and(ContextKeyExpr.has(explorerViewletVisibleId), ContextKeyExpr.has(filesExplorerFocusId)); +export const ExplorerFocusCondition = ContextKeyExpr.and(ContextKeyExpr.has(explorerViewletVisibleId), ContextKeyExpr.has(explorerViewletFocusId)); + +/** + * File editor input id. + */ +export const FILE_EDITOR_INPUT_ID = 'workbench.editors.files.fileEditorInput'; + +/** + * Text file editor id. + */ +export const TEXT_FILE_EDITOR_ID = 'workbench.editors.files.textFileEditor'; + +/** + * Binary file editor id. + */ +export const BINARY_FILE_EDITOR_ID = 'workbench.editors.files.binaryFileEditor'; + +export interface IFilesConfiguration extends IFilesConfiguration, IWorkbenchEditorConfiguration { + explorer: { + openEditors: { + visible: number; + dynamicHeight: boolean; + }; + autoReveal: boolean; + enableDragAndDrop: boolean; + sortOrder: SortOrder; + }; + editor: IEditorOptions; +} + +export interface IFileResource { + resource: URI; + isDirectory?: boolean; +} + +/** + * Helper to get an explorer item from an object. + */ +export function explorerItemToFileResource(obj: FileStat | OpenEditor): IFileResource { + if (obj instanceof FileStat) { + const stat = obj as FileStat; + + return { + resource: stat.resource, + isDirectory: stat.isDirectory + }; + } + + if (obj instanceof OpenEditor) { + const editor = obj as OpenEditor; + const resource = editor.getResource(); + if (resource && resource.scheme === 'file') { + return { + resource: editor.getResource() + }; + } + } + + return null; +} + +export const SortOrderConfiguration = { + DEFAULT: 'default', + MIXED: 'mixed', + FILES_FIRST: 'filesFirst', + TYPE: 'type', + MODIFIED: 'modified' +}; + +export type SortOrder = 'default' | 'mixed' | 'filesFirst' | 'type' | 'modified'; \ No newline at end of file diff --git a/src/vs/workbench/parts/files/test/browser/explorerModel.test.ts b/src/vs/workbench/parts/files/test/browser/explorerModel.test.ts new file mode 100644 index 0000000000..2b3e98b1ac --- /dev/null +++ b/src/vs/workbench/parts/files/test/browser/explorerModel.test.ts @@ -0,0 +1,276 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { isUndefinedOrNull, isArray } from 'vs/base/common/types'; +import { isLinux, isWindows } from 'vs/base/common/platform'; +import URI from 'vs/base/common/uri'; +import { join } from 'vs/base/common/paths'; +import { validateFileName } from 'vs/workbench/parts/files/browser/fileActions'; +import { FileStat } from 'vs/workbench/parts/files/common/explorerModel'; + +function createStat(path: string, name: string, isFolder: boolean, hasChildren: boolean, size: number, mtime: number): FileStat { + return new FileStat(toResource(path), undefined, isFolder, hasChildren, name, mtime); +} + +function toResource(path) { + return URI.file(join('C:\\', path)); +} + +suite('Files - View Model', () => { + + test('Properties', function () { + const d = new Date().getTime(); + let s = createStat('/path/to/stat', 'sName', true, true, 8096, d); + + assert.strictEqual(s.isDirectoryResolved, false); + assert.strictEqual(s.resource.fsPath, toResource('/path/to/stat').fsPath); + assert.strictEqual(s.name, 'sName'); + assert.strictEqual(s.isDirectory, true); + assert.strictEqual(s.hasChildren, true); + assert.strictEqual(s.mtime, new Date(d).getTime()); + assert(isArray(s.children) && s.children.length === 0); + + s = createStat('/path/to/stat', 'sName', false, false, 8096, d); + assert(isUndefinedOrNull(s.children)); + }); + + test('Add and Remove Child, check for hasChild', function () { + const d = new Date().getTime(); + const s = createStat('/path/to/stat', 'sName', true, false, 8096, d); + + const child1 = createStat('/path/to/stat/foo', 'foo', true, false, 8096, d); + const child2 = createStat('/path/to/stat/bar.html', 'bar', false, false, 8096, d); + const child4 = createStat('/otherpath/to/other/otherbar.html', 'otherbar.html', false, false, 8096, d); + + assert(!s.hasChild(child1.name)); + assert(!s.hasChild(child2.name)); + + s.addChild(child1); + assert(s.hasChild(child1.name)); + assert(!s.hasChild(child1.name.toUpperCase())); + assert(s.hasChild(child1.name.toUpperCase(), true)); + + assert(s.children.length === 1); + assert(s.hasChildren); + + s.removeChild(child1); + s.addChild(child1); + assert(s.children.length === 1); + + s.removeChild(child1); + assert(!s.hasChildren); + assert(s.children.length === 0); + + // Assert that adding a child updates its path properly + s.addChild(child4); + assert.strictEqual(child4.resource.fsPath, toResource('/path/to/stat/' + child4.name).fsPath); + }); + + test('Move', function () { + const d = new Date().getTime(); + + const s1 = createStat('/', '/', true, false, 8096, d); + const s2 = createStat('/path', 'path', true, false, 8096, d); + const s3 = createStat('/path/to', 'to', true, false, 8096, d); + const s4 = createStat('/path/to/stat', 'stat', false, false, 8096, d); + + s1.addChild(s2); + s2.addChild(s3); + s3.addChild(s4); + + s4.move(s1); + + assert.strictEqual(s3.children.length, 0); + assert.strictEqual(s3.hasChildren, false); + + assert.strictEqual(s1.children.length, 2); + + // Assert the new path of the moved element + assert.strictEqual(s4.resource.fsPath, toResource('/' + s4.name).fsPath); + + // Move a subtree with children + const leaf = createStat('/leaf', 'leaf', true, false, 8096, d); + const leafC1 = createStat('/leaf/folder', 'folder', true, false, 8096, d); + const leafCC2 = createStat('/leaf/folder/index.html', 'index.html', true, false, 8096, d); + + leaf.addChild(leafC1); + leafC1.addChild(leafCC2); + s1.addChild(leaf); + + leafC1.move(s3); + assert.strictEqual(leafC1.resource.fsPath, URI.file(s3.resource.fsPath + '/' + leafC1.name).fsPath); + assert.strictEqual(leafCC2.resource.fsPath, URI.file(leafC1.resource.fsPath + '/' + leafCC2.name).fsPath); + }); + + test('Rename', function () { + const d = new Date().getTime(); + + const s1 = createStat('/', '/', true, false, 8096, d); + const s2 = createStat('/path', 'path', true, false, 8096, d); + const s3 = createStat('/path/to', 'to', true, false, 8096, d); + const s4 = createStat('/path/to/stat', 'stat', true, false, 8096, d); + + s1.addChild(s2); + s2.addChild(s3); + s3.addChild(s4); + + const s2renamed = createStat('/otherpath', 'otherpath', true, true, 8096, d); + s2.rename(s2renamed); + + // Verify the paths have changed including children + assert.strictEqual(s2.name, s2renamed.name); + assert.strictEqual(s2.resource.fsPath, s2renamed.resource.fsPath); + assert.strictEqual(s3.resource.fsPath, toResource('/otherpath/to').fsPath); + assert.strictEqual(s4.resource.fsPath, toResource('/otherpath/to/stat').fsPath); + + const s4renamed = createStat('/otherpath/to/statother.js', 'statother.js', true, false, 8096, d); + s4.rename(s4renamed); + assert.strictEqual(s4.name, s4renamed.name); + assert.strictEqual(s4.resource.fsPath, s4renamed.resource.fsPath); + }); + + test('Find', function () { + const d = new Date().getTime(); + + const s1 = createStat('/', '/', true, false, 8096, d); + const s2 = createStat('/path', 'path', true, false, 8096, d); + const s3 = createStat('/path/to', 'to', true, false, 8096, d); + const s4 = createStat('/path/to/stat', 'stat', true, false, 8096, d); + const s4Upper = createStat('/path/to/STAT', 'stat', true, false, 8096, d); + + const child1 = createStat('/path/to/stat/foo', 'foo', true, false, 8096, d); + const child2 = createStat('/path/to/stat/foo/bar.html', 'bar.html', false, false, 8096, d); + + s1.addChild(s2); + s2.addChild(s3); + s3.addChild(s4); + s4.addChild(child1); + child1.addChild(child2); + + assert.strictEqual(s1.find(child2.resource), child2); + assert.strictEqual(s1.find(child1.resource), child1); + assert.strictEqual(s1.find(s4.resource), s4); + assert.strictEqual(s1.find(s3.resource), s3); + assert.strictEqual(s1.find(s2.resource), s2); + + if (isLinux) { + assert.ok(!s1.find(s4Upper.resource)); + } else { + assert.strictEqual(s1.find(s4Upper.resource), s4); + } + + assert.strictEqual(s1.find(toResource('foobar')), null); + + assert.strictEqual(s1.find(toResource('/')), s1); + }); + + test('Find with mixed case', function () { + const d = new Date().getTime(); + + const s1 = createStat('/', '/', true, false, 8096, d); + const s2 = createStat('/path', 'path', true, false, 8096, d); + const s3 = createStat('/path/to', 'to', true, false, 8096, d); + const s4 = createStat('/path/to/stat', 'stat', true, false, 8096, d); + + const child1 = createStat('/path/to/stat/foo', 'foo', true, false, 8096, d); + const child2 = createStat('/path/to/stat/foo/bar.html', 'bar.html', false, false, 8096, d); + + s1.addChild(s2); + s2.addChild(s3); + s3.addChild(s4); + s4.addChild(child1); + child1.addChild(child2); + + if (isLinux) { // linux is case sensitive + assert.ok(!s1.find(toResource('/path/to/stat/Foo'))); + assert.ok(!s1.find(toResource('/Path/to/stat/foo/bar.html'))); + } else { + assert.ok(s1.find(toResource('/path/to/stat/Foo'))); + assert.ok(s1.find(toResource('/Path/to/stat/foo/bar.html'))); + } + }); + + test('Validate File Name (For Create)', function () { + const d = new Date().getTime(); + const s = createStat('/path/to/stat', 'sName', true, true, 8096, d); + const sChild = createStat('/path/to/stat/alles.klar', 'alles.klar', true, true, 8096, d); + s.addChild(sChild); + + assert(validateFileName(s, null) !== null); + assert(validateFileName(s, '') !== null); + assert(validateFileName(s, ' ') !== null); + assert(validateFileName(s, 'Read Me') === null, 'name containing space'); + assert(validateFileName(s, 'foo/bar') !== null); + assert(validateFileName(s, 'foo\\bar') !== null); + if (isWindows) { + assert(validateFileName(s, 'foo:bar') !== null); + assert(validateFileName(s, 'foo*bar') !== null); + assert(validateFileName(s, 'foo?bar') !== null); + assert(validateFileName(s, 'foobar') !== null); + assert(validateFileName(s, 'foo|bar') !== null); + } + assert(validateFileName(s, 'alles.klar') !== null); + + assert(validateFileName(s, '.foo') === null); + assert(validateFileName(s, 'foo.bar') === null); + assert(validateFileName(s, 'foo') === null); + }); + + test('Validate File Name (For Rename)', function () { + const d = new Date().getTime(); + const s = createStat('/path/to/stat', 'sName', true, true, 8096, d); + const sChild = createStat('/path/to/stat/alles.klar', 'alles.klar', true, true, 8096, d); + s.addChild(sChild); + + assert(validateFileName(s, 'alles.klar') !== null); + + if (isLinux) { + assert(validateFileName(s, 'Alles.klar') === null); + assert(validateFileName(s, 'Alles.Klar') === null); + } else { + assert(validateFileName(s, 'Alles.klar') !== null); + assert(validateFileName(s, 'Alles.Klar') !== null); + } + + assert(validateFileName(s, '.foo') === null); + assert(validateFileName(s, 'foo.bar') === null); + assert(validateFileName(s, 'foo') === null); + }); + + test('Merge Local with Disk', function () { + const d = new Date().toUTCString(); + + const merge1 = new FileStat(URI.file(join('C:\\', '/path/to')), undefined, true, false, 'to', Date.now(), d); + const merge2 = new FileStat(URI.file(join('C:\\', '/path/to')), undefined, true, false, 'to', Date.now(), new Date(0).toUTCString()); + + // Merge Properties + FileStat.mergeLocalWithDisk(merge2, merge1); + assert.strictEqual(merge1.mtime, merge2.mtime); + + // Merge Child when isDirectoryResolved=false is a no-op + merge2.addChild(new FileStat(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, 'foo.html', Date.now(), d)); + FileStat.mergeLocalWithDisk(merge2, merge1); + assert.strictEqual(merge1.children.length, 0); + + // Merge Child with isDirectoryResolved=true + const child = new FileStat(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, 'foo.html', Date.now(), d); + merge2.removeChild(child); + merge2.addChild(child); + merge2.isDirectoryResolved = true; + FileStat.mergeLocalWithDisk(merge2, merge1); + assert.strictEqual(merge1.children.length, 1); + assert.strictEqual(merge1.children[0].name, 'foo.html'); + assert.deepEqual(merge1.children[0].parent, merge1, 'Check parent'); + + // Verify that merge does not replace existing children, but updates properties in that case + const existingChild = merge1.children[0]; + FileStat.mergeLocalWithDisk(merge2, merge1); + assert.ok(existingChild === merge1.children[0]); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts new file mode 100644 index 0000000000..0f6a42792a --- /dev/null +++ b/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts @@ -0,0 +1,211 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { join } from 'vs/base/common/paths'; +import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { workbenchInstantiationService, TestTextFileService, TestEditorGroupService, createFileInput } from 'vs/workbench/test/workbenchTestServices'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { EncodingMode } from 'vs/workbench/common/editor'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { FileOperationResult, FileOperationError } from 'vs/platform/files/common/files'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { Verbosity } from 'vs/platform/editor/common/editor'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IModelService } from 'vs/editor/common/services/modelService'; + +function toResource(path) { + return URI.file(join('C:\\', new Buffer(this.test.fullTitle()).toString('base64'), path)); +} + +class ServiceAccessor { + constructor( + @IWorkbenchEditorService public editorService: IWorkbenchEditorService, + @ITextFileService public textFileService: TestTextFileService, + @IModelService public modelService: IModelService, + @IEditorGroupService public editorGroupService: TestEditorGroupService + ) { + } +} + +suite('Files - FileEditorInput', () => { + + let instantiationService: IInstantiationService; + let accessor: ServiceAccessor; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + }); + + test('Basics', function (done) { + let input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), void 0); + const otherInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/otherfile.js'), void 0); + const otherInputSame = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/file.js'), void 0); + + assert(input.matches(input)); + assert(input.matches(otherInputSame)); + assert(!input.matches(otherInput)); + assert(!input.matches(null)); + assert.ok(input.getName()); + assert.ok(input.getDescription()); + assert.ok(input.getTitle(Verbosity.SHORT)); + + assert.strictEqual('file.js', input.getName()); + + assert.strictEqual(toResource.call(this, '/foo/bar/file.js').fsPath, input.getResource().fsPath); + assert(input.getResource() instanceof URI); + + input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar.html'), void 0); + + const inputToResolve: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), void 0); + const sameOtherInput: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), void 0); + + return inputToResolve.resolve(true).then(resolved => { + assert.ok(inputToResolve.isResolved()); + + const resolvedModelA = resolved; + return inputToResolve.resolve(true).then(resolved => { + assert(resolvedModelA === resolved); // OK: Resolved Model cached globally per input + + return sameOtherInput.resolve(true).then(otherResolved => { + assert(otherResolved === resolvedModelA); // OK: Resolved Model cached globally per input + + inputToResolve.dispose(); + + return inputToResolve.resolve(true).then(resolved => { + assert(resolvedModelA === resolved); // Model is still the same because we had 2 clients + + inputToResolve.dispose(); + sameOtherInput.dispose(); + + resolvedModelA.dispose(); + + return inputToResolve.resolve(true).then((resolved: TextFileEditorModel) => { + assert(resolvedModelA !== resolved); // Different instance, because input got disposed + + let stat = resolved.getStat(); + return inputToResolve.resolve(true).then((resolved: TextFileEditorModel) => { + assert(stat !== resolved.getStat()); // Different stat, because resolve always goes to the server for refresh + + stat = resolved.getStat(); + return inputToResolve.resolve(false).then((resolved: TextFileEditorModel) => { + assert(stat === resolved.getStat()); // Same stat, because not refreshed + + done(); + }); + }); + }); + }); + }); + }); + }); + }); + + test('matches', function () { + const input1 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), void 0); + const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), void 0); + const input3 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/other.js'), void 0); + const input2Upper = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/UPDATEFILE.js'), void 0); + + assert.strictEqual(input1.matches(null), false); + assert.strictEqual(input1.matches(input1), true); + assert.strictEqual(input1.matches(input2), true); + assert.strictEqual(input1.matches(input3), false); + + assert.strictEqual(input1.matches(input2Upper), false); + }); + + test('getEncoding/setEncoding', function (done) { + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), void 0); + + input.setEncoding('utf16', EncodingMode.Encode); + assert.equal(input.getEncoding(), 'utf16'); + + return input.resolve(true).then((resolved: TextFileEditorModel) => { + assert.equal(input.getEncoding(), resolved.getEncoding()); + + resolved.dispose(); + + done(); + }); + }); + + test('save', function (done) { + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), void 0); + + return input.resolve(true).then((resolved: TextFileEditorModel) => { + resolved.textEditorModel.setValue('changed'); + assert.ok(input.isDirty()); + + input.save().then(() => { + assert.ok(!input.isDirty()); + + resolved.dispose(); + + done(); + }); + }); + }); + + test('revert', function (done) { + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), void 0); + + return input.resolve(true).then((resolved: TextFileEditorModel) => { + resolved.textEditorModel.setValue('changed'); + assert.ok(input.isDirty()); + + input.revert().then(() => { + assert.ok(!input.isDirty()); + + resolved.dispose(); + + done(); + }); + }); + }); + + test('resolve handles binary files', function (done) { + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), void 0); + + accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_IS_BINARY)); + + return input.resolve(true).then(resolved => { + assert.ok(resolved); + + resolved.dispose(); + + done(); + }); + }); + + test('disposes model when not open anymore', function (done) { + const resource = toResource.call(this, '/path/index.txt'); + + const input = createFileInput(instantiationService, resource); + + input.resolve().then((model: TextFileEditorModel) => { + const stacks = accessor.editorGroupService.getStacksModel(); + const group = stacks.openGroup('group', true); + group.openEditor(input); + + accessor.editorGroupService.fireChange(); + + assert.ok(!model.isDisposed()); + + group.closeEditor(input); + accessor.editorGroupService.fireChange(); + assert.ok(model.isDisposed()); + + model.dispose(); + assert.ok(!accessor.modelService.getModel(model.getResource())); + + done(); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/files/test/browser/fileEditorTracker.test.ts b/src/vs/workbench/parts/files/test/browser/fileEditorTracker.test.ts new file mode 100644 index 0000000000..11287369a7 --- /dev/null +++ b/src/vs/workbench/parts/files/test/browser/fileEditorTracker.test.ts @@ -0,0 +1,209 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { FileEditorTracker } from 'vs/workbench/parts/files/common/editors/fileEditorTracker'; +import URI from 'vs/base/common/uri'; +import { join } from 'vs/base/common/paths'; +import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { workbenchInstantiationService, TestTextFileService, TestFileService } from 'vs/workbench/test/workbenchTestServices'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { EditorStacksModel } from 'vs/workbench/common/editor/editorStacksModel'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { FileOperation, FileOperationEvent, FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { once } from 'vs/base/common/event'; + +class TestFileEditorTracker extends FileEditorTracker { + + setCloseOnFileDelete(value: boolean): void { + this.closeOnFileDelete = value; + } +} + +function toResource(path) { + return URI.file(join('C:\\', new Buffer(this.test.fullTitle()).toString('base64'), path)); +} + +class ServiceAccessor { + constructor( + @IWorkbenchEditorService public editorService: IWorkbenchEditorService, + @IEditorGroupService public editorGroupService: IEditorGroupService, + @ITextFileService public textFileService: TestTextFileService, + @IFileService public fileService: TestFileService + ) { + } +} + +suite('Files - FileEditorTracker', () => { + + let instantiationService: IInstantiationService; + let accessor: ServiceAccessor; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + }); + + test('disposes input when resource gets deleted - local file changes', function () { + const stacks = accessor.editorGroupService.getStacksModel() as EditorStacksModel; + const group = stacks.openGroup('first', true); + + const tracker = instantiationService.createInstance(FileEditorTracker); + assert.ok(tracker); + + const parent = toResource.call(this, '/foo/bar'); + const resource = toResource.call(this, '/foo/bar/updatefile.js'); + let input = instantiationService.createInstance(FileEditorInput, resource, void 0); + group.openEditor(input); + + assert.ok(!input.isDisposed()); + + accessor.fileService.fireAfterOperation(new FileOperationEvent(resource, FileOperation.DELETE)); + assert.ok(input.isDisposed()); + group.closeEditor(input); + + input = instantiationService.createInstance(FileEditorInput, resource, void 0); + group.openEditor(input); + + const other = toResource.call(this, '/foo/barfoo'); + + accessor.fileService.fireAfterOperation(new FileOperationEvent(other, FileOperation.DELETE)); + assert.ok(!input.isDisposed()); + + accessor.fileService.fireAfterOperation(new FileOperationEvent(parent, FileOperation.DELETE)); + assert.ok(input.isDisposed()); + + // Move + const to = toResource.call(this, '/foo/barfoo/change.js'); + accessor.fileService.fireAfterOperation(new FileOperationEvent(resource, FileOperation.MOVE, to)); + assert.ok(input.isDisposed()); + + tracker.dispose(); + }); + + test('disposes input when resource gets deleted - local file changes - even when closeOnFileDelete = false', function () { + const stacks = accessor.editorGroupService.getStacksModel() as EditorStacksModel; + const group = stacks.openGroup('first', true); + + const tracker = instantiationService.createInstance(TestFileEditorTracker); + tracker.setCloseOnFileDelete(false); + assert.ok(tracker); + + const parent = toResource.call(this, '/foo/bar'); + const resource = toResource.call(this, '/foo/bar/updatefile.js'); + let input = instantiationService.createInstance(FileEditorInput, resource, void 0); + group.openEditor(input); + + assert.ok(!input.isDisposed()); + + accessor.fileService.fireAfterOperation(new FileOperationEvent(resource, FileOperation.DELETE)); + assert.ok(input.isDisposed()); + group.closeEditor(input); + + input = instantiationService.createInstance(FileEditorInput, resource, void 0); + group.openEditor(input); + + const other = toResource.call(this, '/foo/barfoo'); + + accessor.fileService.fireAfterOperation(new FileOperationEvent(other, FileOperation.DELETE)); + assert.ok(!input.isDisposed()); + + accessor.fileService.fireAfterOperation(new FileOperationEvent(parent, FileOperation.DELETE)); + assert.ok(input.isDisposed()); + + // Move + const to = toResource.call(this, '/foo/barfoo/change.js'); + accessor.fileService.fireAfterOperation(new FileOperationEvent(resource, FileOperation.MOVE, to)); + assert.ok(input.isDisposed()); + + tracker.dispose(); + }); + + test('disposes when resource gets deleted - remote file changes', function (done) { + const stacks = accessor.editorGroupService.getStacksModel() as EditorStacksModel; + const group = stacks.openGroup('first', true); + + const tracker = instantiationService.createInstance(FileEditorTracker); + assert.ok(tracker); + + const parent = toResource.call(this, '/foo/bar'); + const resource = toResource.call(this, '/foo/bar/updatefile.js'); + let input = instantiationService.createInstance(FileEditorInput, resource, void 0); + group.openEditor(input); + + assert.ok(!input.isDisposed()); + + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource, type: FileChangeType.DELETED }])); + + once(input.onDispose)(() => { + assert.ok(input.isDisposed()); + group.closeEditor(input); + + input = instantiationService.createInstance(FileEditorInput, resource, void 0); + group.openEditor(input); + + const other = toResource.call(this, '/foo/barfoo'); + + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: other, type: FileChangeType.DELETED }])); + assert.ok(!input.isDisposed()); + + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: parent, type: FileChangeType.DELETED }])); + + once(input.onDispose)(() => { + assert.ok(input.isDisposed()); + + tracker.dispose(); + done(); + }); + }); + }); + + test('keeps open when resource gets deleted - remote file changes - closeOnFileDelete = false', function () { + const stacks = accessor.editorGroupService.getStacksModel() as EditorStacksModel; + const group = stacks.openGroup('first', true); + + const tracker = instantiationService.createInstance(TestFileEditorTracker); + tracker.setCloseOnFileDelete(false); + assert.ok(tracker); + + const resource = toResource.call(this, '/foo/bar/updatefile.js'); + let input = instantiationService.createInstance(FileEditorInput, resource, void 0); + group.openEditor(input); + + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource, type: FileChangeType.DELETED }])); + + assert.ok(!input.isDisposed()); + + tracker.dispose(); + }); + + test('file change event updates model', function (done) { + const tracker = instantiationService.createInstance(FileEditorTracker); + + const resource = toResource.call(this, '/path/index.txt'); + + accessor.textFileService.models.loadOrCreate(resource).then((model: TextFileEditorModel) => { + model.textEditorModel.setValue('Super Good'); + assert.equal(model.getValue(), 'Super Good'); + + model.save().then(() => { + + // change event (watcher) + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource, type: FileChangeType.UPDATED }])); + + assert.equal(model.getValue(), 'Hello Html'); + + tracker.dispose(); + + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/html/browser/html.contribution.ts b/src/vs/workbench/parts/html/browser/html.contribution.ts new file mode 100644 index 0000000000..d29a65d7fe --- /dev/null +++ b/src/vs/workbench/parts/html/browser/html.contribution.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./media/htmlPreviewPart'; +import URI from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Position as EditorPosition } from 'vs/platform/editor/common/editor'; +import { HtmlInput, HtmlInputOptions } from '../common/htmlInput'; +import { HtmlPreviewPart } from 'vs/workbench/parts/html/browser/htmlPreviewPart'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { MenuRegistry } from 'vs/platform/actions/common/actions'; +import { WebviewElement } from 'vs/workbench/parts/html/browser/webview'; +import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; + +function getActivePreviewsForResource(accessor: ServicesAccessor, resource: URI | string) { + const uri = resource instanceof URI ? resource : URI.parse(resource); + return accessor.get(IWorkbenchEditorService).getVisibleEditors() + .filter(c => c instanceof HtmlPreviewPart && c.model) + .map(e => e as HtmlPreviewPart) + .filter(e => e.model.uri.scheme === uri.scheme && e.model.uri.fsPath === uri.fsPath); +} + +// --- Register Editor +(Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor(HtmlPreviewPart.ID, + localize('html.editor.label', "Html Preview"), + 'vs/workbench/parts/html/browser/htmlPreviewPart', + 'HtmlPreviewPart'), + [new SyncDescriptor(HtmlInput)]); + +// --- Register Commands + +const defaultPreviewHtmlOptions: HtmlInputOptions = { + allowScripts: true, + allowSvgs: true +}; + +CommandsRegistry.registerCommand('_workbench.previewHtml', function ( + accessor: ServicesAccessor, + resource: URI | string, + position?: EditorPosition, + label?: string, + options?: HtmlInputOptions +) { + const uri = resource instanceof URI ? resource : URI.parse(resource); + label = label || uri.fsPath; + + let input: HtmlInput; + + // Find already opened HTML input if any + const stacks = accessor.get(IEditorGroupService).getStacksModel(); + const targetGroup = stacks.groupAt(position) || stacks.activeGroup; + if (targetGroup) { + const existingInput = targetGroup.getEditor(uri); + if (existingInput instanceof HtmlInput) { + input = existingInput; + } + } + + const inputOptions = (Object as any).assign({}, options || defaultPreviewHtmlOptions); + const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); + inputOptions.svgWhiteList = extensionsWorkbenchService.allowedBadgeProviders; + + // Otherwise, create new input and open it + if (!input) { + input = accessor.get(IInstantiationService).createInstance(HtmlInput, label, '', uri, inputOptions); + } else { + input.setName(label); // make sure to use passed in label + } + + return accessor.get(IWorkbenchEditorService) + .openEditor(input, { pinned: true }, position) + .then(editor => true); +}); + +CommandsRegistry.registerCommand('_workbench.htmlPreview.postMessage', function ( + accessor: ServicesAccessor, + resource: URI | string, + message: any +) { + const activePreviews = getActivePreviewsForResource(accessor, resource); + for (const preview of activePreviews) { + preview.sendMessage(message); + } + return activePreviews.length > 0; +}); + +CommandsRegistry.registerCommand('_workbench.htmlPreview.updateOptions', function ( + accessor: ServicesAccessor, + resource: URI | string, + options: HtmlInputOptions +) { + + const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); + const inputOptions: HtmlInputOptions = options; + const allowedBadgeProviders = extensionsWorkbenchService.allowedBadgeProviders; + inputOptions.svgWhiteList = allowedBadgeProviders; + + const uri = resource instanceof URI ? resource : URI.parse(resource); + const activePreviews = getActivePreviewsForResource(accessor, resource); + for (const preview of activePreviews) { + if (preview.input && preview.input instanceof HtmlInput) { + const input = accessor.get(IInstantiationService).createInstance(HtmlInput, preview.input.getName(), '', uri, options); + preview.setInput(input); + } + } +}); + +CommandsRegistry.registerCommand('_webview.openDevTools', function () { + const elements = document.querySelectorAll('webview.ready'); + for (let i = 0; i < elements.length; i++) { + try { + (elements.item(i) as WebviewElement).openDevTools(); + } catch (e) { + console.error(e); + } + } +}); + +MenuRegistry.addCommand({ + id: '_webview.openDevTools', + title: localize('devtools.webview', "Developer: Webview Tools") +}); diff --git a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts new file mode 100644 index 0000000000..cc09a75813 --- /dev/null +++ b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts @@ -0,0 +1,236 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import { empty as EmptyDisposable, IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; +import { EditorOptions, EditorInput } from 'vs/workbench/common/editor'; +import { Position } from 'vs/platform/editor/common/editor'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; +import { HtmlInput, HtmlInputOptions, areHtmlInputOptionsEqual } from 'vs/workbench/parts/html/common/htmlInput'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { Parts, IPartService } from 'vs/workbench/services/part/common/partService'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; + +import Webview, { WebviewOptions } from './webview'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { WebviewEditor } from './webviewEditor'; + + +/** + * An implementation of editor for showing HTML content in an IFrame by leveraging the HTML input. + */ +export class HtmlPreviewPart extends WebviewEditor { + + static ID: string = 'workbench.editor.htmlPreviewPart'; + static class: string = 'htmlPreviewPart'; + + private _webviewDisposables: IDisposable[]; + + private _modelRef: IReference; + public get model(): IModel { return this._modelRef && this._modelRef.object.textEditorModel; } + private _modelChangeSubscription = EmptyDisposable; + private _themeChangeSubscription = EmptyDisposable; + + private scrollYPercentage: number = 0; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @ITextModelService private textModelResolverService: ITextModelService, + @IThemeService themeService: IThemeService, + @IOpenerService private readonly openerService: IOpenerService, + @IPartService private partService: IPartService, + @IStorageService storageService: IStorageService, + @IContextViewService private _contextViewService: IContextViewService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(HtmlPreviewPart.ID, telemetryService, themeService, storageService, contextKeyService); + } + + dispose(): void { + // remove from dom + this._webviewDisposables = dispose(this._webviewDisposables); + + // unhook listeners + this._themeChangeSubscription.dispose(); + this._modelChangeSubscription.dispose(); + + // dipose model ref + dispose(this._modelRef); + super.dispose(); + } + + protected createEditor(parent: Builder): void { + this.content = document.createElement('div'); + this.content.style.position = 'absolute'; + this.content.classList.add(HtmlPreviewPart.class); + parent.getHTMLElement().appendChild(this.content); + } + + private get webview(): Webview { + if (!this._webview) { + let webviewOptions: WebviewOptions = {}; + if (this.input && this.input instanceof HtmlInput) { + webviewOptions = this.input.options; + } + + this._webview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this._contextViewService, this.contextKey, this.findInputFocusContextKey, webviewOptions); + if (this.input && this.input instanceof HtmlInput) { + const state = this.loadViewState(this.input.getResource()); + this.scrollYPercentage = state ? state.scrollYPercentage : 0; + this.webview.initialScrollProgress = this.scrollYPercentage; + + const resourceUri = this.input.getResource(); + this.webview.baseUrl = resourceUri.toString(true); + } + this.onThemeChange(this.themeService.getTheme()); + this._webviewDisposables = [ + this._webview, + this._webview.onDidClickLink(uri => this.openerService.open(uri)), + this._webview.onDidScroll(data => { + this.scrollYPercentage = data.scrollYPercentage; + }), + ]; + } + return this._webview; + } + + public changePosition(position: Position): void { + // what this actually means is that we got reparented. that + // has caused the webview to stop working and we need to reset it + this._doSetVisible(false); + this._doSetVisible(true); + + super.changePosition(position); + } + + protected setEditorVisible(visible: boolean, position?: Position): void { + this._doSetVisible(visible); + super.setEditorVisible(visible, position); + } + + private _doSetVisible(visible: boolean): void { + if (!visible) { + this._themeChangeSubscription.dispose(); + this._modelChangeSubscription.dispose(); + this._webviewDisposables = dispose(this._webviewDisposables); + this._webview = undefined; + } else { + this._themeChangeSubscription = this.themeService.onThemeChange(this.onThemeChange.bind(this)); + + if (this._hasValidModel()) { + this._modelChangeSubscription = this.model.onDidChangeContent(() => this.webview.contents = this.model.getLinesContent()); + this.webview.contents = this.model.getLinesContent(); + } + } + } + + private _hasValidModel(): boolean { + return this._modelRef && this.model && !this.model.isDisposed(); + } + + public layout(dimension: Dimension): void { + const { width, height } = dimension; + this.content.style.width = `${width}px`; + this.content.style.height = `${height}px`; + if (this._webview) { + this._webview.layout(); + } + } + + public focus(): void { + this.webview.focus(); + } + + public clearInput(): void { + if (this.input instanceof HtmlInput) { + this.saveViewState(this.input.getResource(), { + scrollYPercentage: this.scrollYPercentage + }); + } + dispose(this._modelRef); + this._modelRef = undefined; + super.clearInput(); + } + + public shutdown(): void { + if (this.input instanceof HtmlInput) { + this.saveViewState(this.input.getResource(), { + scrollYPercentage: this.scrollYPercentage + }); + } + super.shutdown(); + } + + public sendMessage(data: any): void { + this.webview.sendMessage(data); + } + + public setInput(input: EditorInput, options?: EditorOptions): TPromise { + + if (this.input && this.input.matches(input) && this._hasValidModel() && this.input instanceof HtmlInput && input instanceof HtmlInput && areHtmlInputOptionsEqual(this.input.options, input.options)) { + return TPromise.as(undefined); + } + + let oldOptions: HtmlInputOptions | undefined = undefined; + + if (this.input instanceof HtmlInput) { + oldOptions = this.input.options; + this.saveViewState(this.input.getResource(), { + scrollYPercentage: this.scrollYPercentage + }); + } + + if (this._modelRef) { + this._modelRef.dispose(); + } + this._modelChangeSubscription.dispose(); + + if (!(input instanceof HtmlInput)) { + return TPromise.wrapError(new Error('Invalid input')); + } + + return super.setInput(input, options).then(() => { + const resourceUri = input.getResource(); + return this.textModelResolverService.createModelReference(resourceUri).then(ref => { + const model = ref.object; + + if (model instanceof BaseTextEditorModel) { + this._modelRef = ref; + } + + if (!this.model) { + return TPromise.wrapError(new Error(localize('html.voidInput', "Invalid editor input."))); + } + + if (oldOptions && !areHtmlInputOptionsEqual(oldOptions, input.options)) { + this._doSetVisible(false); + } + + this._modelChangeSubscription = this.model.onDidChangeContent(() => { + if (this.model) { + this.scrollYPercentage = 0; + this.webview.contents = this.model.getLinesContent(); + } + }); + const state = this.loadViewState(resourceUri); + this.scrollYPercentage = state ? state.scrollYPercentage : 0; + this.webview.baseUrl = resourceUri.toString(true); + this.webview.options = input.options; + this.webview.contents = this.model.getLinesContent(); + this.webview.initialScrollProgress = this.scrollYPercentage; + return undefined; + }); + }); + } +} diff --git a/src/vs/workbench/parts/html/browser/media/htmlPreviewPart.css b/src/vs/workbench/parts/html/browser/media/htmlPreviewPart.css new file mode 100644 index 0000000000..7e1f29be5f --- /dev/null +++ b/src/vs/workbench/parts/html/browser/media/htmlPreviewPart.css @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.htmlPreviewPart { + overflow: hidden; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/html/browser/webview-pre.js b/src/vs/workbench/parts/html/browser/webview-pre.js new file mode 100644 index 0000000000..53aa2ee2ec --- /dev/null +++ b/src/vs/workbench/parts/html/browser/webview-pre.js @@ -0,0 +1,271 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// @ts-check +(function () { + 'use strict'; + + const ipcRenderer = require('electron').ipcRenderer; + + // state + var firstLoad = true; + var loadTimeout; + var pendingMessages = []; + + const initData = { + initialScrollProgress: undefined + }; + + function styleBody(body) { + if (!body) { + return; + } + body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast'); + body.classList.add(initData.activeTheme); + } + + /** + * @return {HTMLIFrameElement} + */ + function getActiveFrame() { + return document.getElementById('active-frame'); + } + + /** + * @return {HTMLIFrameElement} + */ + function getPendingFrame() { + return document.getElementById('pending-frame'); + } + + /** + * @param {MouseEvent} event + */ + function handleInnerClick(event) { + if (!event || !event.view || !event.view.document) { + return; + } + /** @type {any} */ + var node = event.target; + while (node) { + if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) { + var baseElement = event.view.document.getElementsByTagName("base")[0]; + if (node.getAttribute("href") === "#") { + event.view.scrollTo(0, 0); + } else if (node.hash && (node.getAttribute("href") === node.hash || (baseElement && node.href.indexOf(baseElement.href) >= 0))) { + var scrollTarget = event.view.document.getElementById(node.hash.substr(1, node.hash.length - 1)); + if (scrollTarget) { + scrollTarget.scrollIntoView(); + } + } else { + ipcRenderer.sendToHost("did-click-link", node.href); + } + event.preventDefault(); + break; + } + node = node.parentNode; + } + } + + var isHandlingScroll = false; + function handleInnerScroll(event) { + if (isHandlingScroll) { + return; + } + + const progress = event.target.body.scrollTop / event.target.body.clientHeight; + if (isNaN(progress)) { + return; + } + + isHandlingScroll = true; + window.requestAnimationFrame(function () { + try { + ipcRenderer.sendToHost('did-scroll', progress); + } catch (e) { + // noop + } + isHandlingScroll = false; + }); + } + + document.addEventListener('DOMContentLoaded', function () { + ipcRenderer.on('baseUrl', function (event, value) { + initData.baseUrl = value; + }); + + ipcRenderer.on('styles', function (event, value, activeTheme) { + initData.styles = value; + initData.activeTheme = activeTheme; + + // webview + var target = getActiveFrame(); + if (!target) { + return; + } + var body = target.contentDocument.getElementsByTagName('body'); + styleBody(body[0]); + + // iframe + var defaultStyles = target.contentDocument.getElementById('_defaultStyles'); + if (defaultStyles) { + defaultStyles.innerHTML = initData.styles; + } + }); + + // propagate focus + ipcRenderer.on('focus', function () { + const target = getActiveFrame(); + if (target) { + target.contentWindow.focus(); + } + }); + + // update iframe-contents + ipcRenderer.on('content', function (_event, data) { + const options = data.options; + const text = data.contents.join('\n'); + const newDocument = new DOMParser().parseFromString(text, 'text/html'); + + // set base-url if applicable + if (initData.baseUrl && newDocument.head.getElementsByTagName('base').length === 0) { + const baseElement = newDocument.createElement('base'); + baseElement.href = initData.baseUrl; + newDocument.head.appendChild(baseElement); + } + + // apply default styles + const defaultStyles = newDocument.createElement('style'); + defaultStyles.id = '_defaultStyles'; + defaultStyles.innerHTML = initData.styles; + if (newDocument.head.hasChildNodes()) { + newDocument.head.insertBefore(defaultStyles, newDocument.head.firstChild); + } else { + newDocument.head.appendChild(defaultStyles); + } + + styleBody(newDocument.body); + + const frame = getActiveFrame(); + + // keep current scrollTop around and use later + var setInitialScrollPosition; + if (firstLoad) { + firstLoad = false; + setInitialScrollPosition = function (body) { + if (!isNaN(initData.initialScrollProgress)) { + if (body.scrollTop === 0) { + body.scrollTop = body.clientHeight * initData.initialScrollProgress; + } + } + }; + } else { + const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? frame.contentDocument.body.scrollTop : 0; + setInitialScrollPosition = function (body) { + if (body.scrollTop === 0) { + body.scrollTop = scrollY; + } + }; + } + + // Clean up old pending frames and set current one as new one + const previousPendingFrame = getPendingFrame(); + if (previousPendingFrame) { + previousPendingFrame.setAttribute('id', ''); + document.body.removeChild(previousPendingFrame); + } + pendingMessages = []; + + const newFrame = document.createElement('iframe'); + newFrame.setAttribute('id', 'pending-frame'); + newFrame.setAttribute('frameborder', '0'); + newFrame.setAttribute('sandbox', options.allowScripts ? 'allow-scripts allow-forms allow-same-origin' : 'allow-same-origin'); + newFrame.style.cssText = "display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden"; + document.body.appendChild(newFrame); + + // write new content onto iframe + newFrame.contentDocument.open('text/html', 'replace'); + newFrame.contentWindow.onbeforeunload = function () { + console.log('prevented webview navigation'); + return false; + }; + + var onLoad = function (contentDocument, contentWindow) { + if (contentDocument.body) { + // Workaround for https://github.com/Microsoft/vscode/issues/12865 + // check new scrollTop and reset if neccessary + setInitialScrollPosition(contentDocument.body); + + // Bubble out link clicks + contentDocument.body.addEventListener('click', handleInnerClick); + } + + const newFrame = getPendingFrame(); + if (newFrame && newFrame.contentDocument === contentDocument) { + const oldActiveFrame = getActiveFrame(); + if (oldActiveFrame) { + document.body.removeChild(oldActiveFrame); + } + newFrame.setAttribute('id', 'active-frame'); + newFrame.style.visibility = 'visible'; + contentWindow.addEventListener('scroll', handleInnerScroll); + + pendingMessages.forEach(function(data) { + contentWindow.postMessage(data, document.location.origin); + }); + pendingMessages = []; + } + }; + + clearTimeout(loadTimeout); + loadTimeout = undefined; + loadTimeout = setTimeout(function () { + clearTimeout(loadTimeout); + loadTimeout = undefined; + onLoad(newFrame.contentDocument, newFrame.contentWindow); + }, 200); + + newFrame.contentWindow.addEventListener('load', function (e) { + if (loadTimeout) { + clearTimeout(loadTimeout); + loadTimeout = undefined; + onLoad(e.target, this); + } + }); + + // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off + // and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden + newFrame.contentDocument.write(''); + newFrame.contentDocument.write(newDocument.documentElement.innerHTML); + newFrame.contentDocument.close(); + + ipcRenderer.sendToHost('did-set-content'); + }); + + // Forward message to the embedded iframe + ipcRenderer.on('message', function (event, data) { + const pending = getPendingFrame(); + if (pending) { + pendingMessages.push(data); + } else { + const target = getActiveFrame(); + if (target) { + target.contentWindow.postMessage(data, document.location.origin); + } + } + }); + + ipcRenderer.on('initial-scroll-position', function (event, progress) { + initData.initialScrollProgress = progress; + }); + + // forward messages from the embedded iframe + window.onmessage = function (message) { + ipcRenderer.sendToHost(message.data.command, message.data.data); + }; + + // signal ready + ipcRenderer.sendToHost('webview-ready', process.pid); + }); +}()); \ No newline at end of file diff --git a/src/vs/workbench/parts/html/browser/webview.html b/src/vs/workbench/parts/html/browser/webview.html new file mode 100644 index 0000000000..04e44c27fd --- /dev/null +++ b/src/vs/workbench/parts/html/browser/webview.html @@ -0,0 +1,8 @@ + + + + Virtual Document + + + + \ No newline at end of file diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts new file mode 100644 index 0000000000..ab6b6af6b8 --- /dev/null +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -0,0 +1,466 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import { addDisposableListener, addClass } from 'vs/base/browser/dom'; +import { editorBackground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { ITheme, LIGHT, DARK } from 'vs/platform/theme/common/themeService'; +import { WebviewFindWidget } from './webviewFindWidget'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export declare interface WebviewElement extends HTMLElement { + src: string; + preload: string; + send(channel: string, ...args: any[]); + openDevTools(): any; + getWebContents(): any; + findInPage(value: string, options?: WebviewElementFindInPageOptions); + stopFindInPage(action: string); +} + +export class StopFindInPageActions { + static clearSelection = 'clearSelection'; + static keepSelection = 'keepSelection'; + static activateSelection = 'activateSelection'; +} + +export interface WebviewElementFindInPageOptions { + forward?: boolean; + findNext?: boolean; + matchCase?: boolean; + wordStart?: boolean; + medialCapitalAsWordStart?: boolean; +} + +export interface FoundInPageResults { + requestId: number; + activeMatchOrdinal: number; + matches: number; + selectionArea: any; +} + +type ApiThemeClassName = 'vscode-light' | 'vscode-dark' | 'vscode-high-contrast'; + +export interface WebviewOptions { + allowScripts?: boolean; + allowSvgs?: boolean; + svgWhiteList?: string[]; +} + +export default class Webview { + private static index: number = 0; + + private _webview: WebviewElement; + private _ready: TPromise; + private _disposables: IDisposable[] = []; + private _onDidClickLink = new Emitter(); + + private _onDidScroll = new Emitter<{ scrollYPercentage: number }>(); + private _onFoundInPageResults = new Emitter(); + + private _webviewFindWidget: WebviewFindWidget; + private _findStarted: boolean = false; + + constructor( + private parent: HTMLElement, + private _styleElement: Element, + @IContextViewService private _contextViewService: IContextViewService, + private _contextKey: IContextKey, + private _findInputContextKey: IContextKey, + private _options: WebviewOptions = {}, + ) { + this._webview = document.createElement('webview'); + this._webview.setAttribute('partition', this._options.allowSvgs ? 'webview' : `webview${Webview.index++}`); + + // disable auxclick events (see https://developers.google.com/web/updates/2016/10/auxclick) + this._webview.setAttribute('disableblinkfeatures', 'Auxclick'); + + this._webview.setAttribute('disableguestresize', ''); + this._webview.setAttribute('webpreferences', 'contextIsolation=yes'); + + this._webview.style.flex = '0 1'; + this._webview.style.width = '0'; + this._webview.style.height = '0'; + this._webview.style.outline = '0'; + + this._webview.preload = require.toUrl('./webview-pre.js'); + this._webview.src = require.toUrl('./webview.html'); + + this._ready = new TPromise(resolve => { + const subscription = addDisposableListener(this._webview, 'ipc-message', (event) => { + if (event.channel === 'webview-ready') { + // console.info('[PID Webview] ' event.args[0]); + addClass(this._webview, 'ready'); // can be found by debug command + + subscription.dispose(); + resolve(this); + } + }); + }); + + if (!this._options.allowSvgs) { + let loaded = false; + const subscription = addDisposableListener(this._webview, 'did-start-loading', () => { + if (loaded) { + return; + } + loaded = true; + + const contents = this._webview.getWebContents(); + if (!contents) { + return; + } + + contents.session.webRequest.onBeforeRequest((details, callback) => { + if (details.url.indexOf('.svg') > 0) { + const uri = URI.parse(details.url); + if (uri && !uri.scheme.match(/file/i) && (uri.path as any).endsWith('.svg') && !this.isAllowedSvg(uri)) { + this.onDidBlockSvg(); + return callback({ cancel: true }); + } + } + return callback({}); + }); + + contents.session.webRequest.onHeadersReceived((details, callback) => { + const contentType: string[] = (details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']) as any; + if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { + const uri = URI.parse(details.url); + if (uri && !this.isAllowedSvg(uri)) { + this.onDidBlockSvg(); + return callback({ cancel: true }); + } + } + return callback({ cancel: false, responseHeaders: details.responseHeaders }); + }); + }); + + this._disposables.push(subscription); + } + + this._disposables.push( + addDisposableListener(this._webview, 'console-message', function (e: { level: number; message: string; line: number; sourceId: string; }) { + console.log(`[Embedded Page] ${e.message}`); + }), + addDisposableListener(this._webview, 'dom-ready', () => { + this.layout(); + }), + addDisposableListener(this._webview, 'crashed', () => { + console.error('embedded page crashed'); + }), + addDisposableListener(this._webview, 'ipc-message', (event) => { + if (event.channel === 'did-click-link') { + let [uri] = event.args; + this._onDidClickLink.fire(URI.parse(uri)); + return; + } + + if (event.channel === 'did-set-content') { + this._webview.style.flex = ''; + this._webview.style.width = '100%'; + this._webview.style.height = '100%'; + this.layout(); + return; + } + + if (event.channel === 'did-scroll') { + if (event.args && typeof event.args[0] === 'number') { + this._onDidScroll.fire({ scrollYPercentage: event.args[0] }); + } + return; + } + }), + addDisposableListener(this._webview, 'focus', () => { + if (this._contextKey) { + this._contextKey.set(true); + } + }), + addDisposableListener(this._webview, 'blur', () => { + if (this._contextKey) { + this._contextKey.reset(); + } + }), + addDisposableListener(this._webview, 'found-in-page', (event) => { + this._onFoundInPageResults.fire(event.result); + }) + ); + + this._webviewFindWidget = new WebviewFindWidget(this._contextViewService, this); + this._disposables.push(this._webviewFindWidget); + + if (parent) { + parent.appendChild(this._webviewFindWidget.getDomNode()); + parent.appendChild(this._webview); + } + } + + public notifyFindWidgetFocusChanged(isFocused: boolean) { + this._contextKey.set(isFocused || document.activeElement === this._webview); + } + + public notifyFindWidgetInputFocusChanged(isFocused: boolean) { + this._findInputContextKey.set(isFocused); + } + + dispose(): void { + this._onDidClickLink.dispose(); + this._disposables = dispose(this._disposables); + + if (this._webview.parentElement) { + this._webview.parentElement.removeChild(this._webview); + const findWidgetDomNode = this._webviewFindWidget.getDomNode(); + findWidgetDomNode.parentElement.removeChild(findWidgetDomNode); + } + } + + get onDidClickLink(): Event { + return this._onDidClickLink.event; + } + + get onDidScroll(): Event<{ scrollYPercentage: number }> { + return this._onDidScroll.event; + } + + get onFindResults(): Event { + return this._onFoundInPageResults.event; + } + + private _send(channel: string, ...args: any[]): void { + this._ready + .then(() => this._webview.send(channel, ...args)) + .done(void 0, console.error); + } + + set initialScrollProgress(value: number) { + this._send('initial-scroll-position', value); + } + + set options(value: WebviewOptions) { + this._options = value; + } + + set contents(value: string[]) { + this._send('content', { + contents: value, + options: this._options + }); + } + + set baseUrl(value: string) { + this._send('baseUrl', value); + } + + focus(): void { + this._webview.focus(); + this._send('focus'); + } + + public sendMessage(data: any): void { + this._send('message', data); + } + + private onDidBlockSvg() { + this.sendMessage({ + name: 'vscode-did-block-svg' + }); + } + + style(theme: ITheme): void { + const { fontFamily, fontWeight, fontSize } = window.getComputedStyle(this._styleElement); // TODO@theme avoid styleElement + + let value = ` + :root { + --background-color: ${theme.getColor(editorBackground)}; + --color: ${theme.getColor(editorForeground)}; + --font-family: ${fontFamily}; + --font-weight: ${fontWeight}; + --font-size: ${fontSize}; + } + body { + background-color: var(--background-color); + color: var(--color); + font-family: var(--font-family); + font-weight: var(--font-weight); + font-size: var(--font-size); + margin: 0; + padding: 0 20px; + } + + img { + max-width: 100%; + max-height: 100%; + } + a:focus, + input:focus, + select:focus, + textarea:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; + } + ::-webkit-scrollbar { + width: 10px; + height: 10px; + }`; + + + let activeTheme: ApiThemeClassName; + + if (theme.type === LIGHT) { + value += ` + ::-webkit-scrollbar-thumb { + background-color: rgba(100, 100, 100, 0.4); + } + ::-webkit-scrollbar-thumb:hover { + background-color: rgba(100, 100, 100, 0.7); + } + ::-webkit-scrollbar-thumb:active { + background-color: rgba(0, 0, 0, 0.6); + }`; + + activeTheme = 'vscode-light'; + + } else if (theme.type === DARK) { + value += ` + ::-webkit-scrollbar-thumb { + background-color: rgba(121, 121, 121, 0.4); + } + ::-webkit-scrollbar-thumb:hover { + background-color: rgba(100, 100, 100, 0.7); + } + ::-webkit-scrollbar-thumb:active { + background-color: rgba(85, 85, 85, 0.8); + }`; + + activeTheme = 'vscode-dark'; + + } else { + value += ` + ::-webkit-scrollbar-thumb { + background-color: rgba(111, 195, 223, 0.3); + } + ::-webkit-scrollbar-thumb:hover { + background-color: rgba(111, 195, 223, 0.8); + } + ::-webkit-scrollbar-thumb:active { + background-color: rgba(111, 195, 223, 0.8); + }`; + + activeTheme = 'vscode-high-contrast'; + } + + this._send('styles', value, activeTheme); + + this._webviewFindWidget.updateTheme(theme); + } + + public layout(): void { + const contents = (this._webview as any).getWebContents(); + if (!contents || contents.isDestroyed()) { + return; + } + const window = contents.getOwnerBrowserWindow(); + if (!window || !window.webContents || window.webContents.isDestroyed()) { + return; + } + window.webContents.getZoomFactor(factor => { + if (contents.isDestroyed()) { + return; + } + + contents.setZoomFactor(factor); + + const width = this.parent.clientWidth; + const height = this.parent.clientHeight; + contents.setSize({ + normal: { + width: Math.floor(width * factor), + height: Math.floor(height * factor) + } + }); + }); + } + + private isAllowedSvg(uri: URI): boolean { + if (this._options.allowSvgs) { + return true; + } + if (this._options.svgWhiteList) { + return this._options.svgWhiteList.indexOf(uri.authority.toLowerCase()) >= 0; + } + return false; + } + + public startFind(value: string, options?: WebviewElementFindInPageOptions) { + if (!value) { + return; + } + + // ensure options is defined without modifying the original + options = options || {}; + + // FindNext must be false for a first request + const findOptions: WebviewElementFindInPageOptions = { + forward: options.forward, + findNext: false, + matchCase: options.matchCase, + medialCapitalAsWordStart: options.medialCapitalAsWordStart + }; + + this._findStarted = true; + this._webview.findInPage(value, findOptions); + return; + } + + /** + * Webviews expose a stateful find API. + * Successive calls to find will move forward or backward through onFindResults + * depending on the supplied options. + * + * @param {string} value The string to search for. Empty strings are ignored. + * @param {WebviewElementFindInPageOptions} [options] + * + * @memberOf Webview + */ + public find(value: string, options?: WebviewElementFindInPageOptions): void { + // Searching with an empty value will throw an exception + if (!value) { + return; + } + + if (!this._findStarted) { + this.startFind(value, options); + return; + } + + this._webview.findInPage(value, options); + } + + public stopFind(keepSelection?: boolean): void { + this._findStarted = false; + this._webview.stopFindInPage(keepSelection ? StopFindInPageActions.keepSelection : StopFindInPageActions.clearSelection); + } + + public showFind() { + this._webviewFindWidget.reveal(); + } + + public hideFind() { + this._webviewFindWidget.hide(); + } + + public showNextFindTerm() { + this._webviewFindWidget.showNextFindTerm(); + } + + public showPreviousFindTerm() { + this._webviewFindWidget.showPreviousFindTerm(); + } +} diff --git a/src/vs/workbench/parts/html/browser/webviewEditor.ts b/src/vs/workbench/parts/html/browser/webviewEditor.ts new file mode 100644 index 0000000000..cad63a35ed --- /dev/null +++ b/src/vs/workbench/parts/html/browser/webviewEditor.ts @@ -0,0 +1,188 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { BaseWebviewEditor } from 'vs/workbench/browser/parts/editor/webviewEditor'; +import { IStorageService } from 'vs/platform/storage/common/storage'; + +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { Command, ICommandOptions } from 'vs/editor/common/editorCommonExtensions'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { ContextKeyExpr, IContextKey, RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; + +import WebView from './webview'; +import { Builder } from 'vs/base/browser/builder'; + +export interface HtmlPreviewEditorViewState { + scrollYPercentage: number; +} + +/** A context key that is set when a webview editor has focus. */ +export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS = new RawContextKey('webviewEditorFocus', undefined); +/** A context key that is set when a webview editor does not have focus. */ +export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS.toNegated(); +/** A context key that is set when the find widget find input in webview editor webview is focused. */ +export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED = new RawContextKey('webviewEditorFindWidgetInputFocused', false); +/** A context key that is set when the find widget find input in webview editor webview is not focused. */ +export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED.toNegated(); + +/** + * This class is only intended to be subclassed and not instantiated. + */ +export abstract class WebviewEditor extends BaseWebviewEditor { + + protected _webviewFocusContextKey: IContextKey; + protected _webview: WebView; + protected content: HTMLElement; + protected contextKey: IContextKey; + protected findInputFocusContextKey: IContextKey; + + constructor( + id: string, + telemetryService: ITelemetryService, + themeService: IThemeService, + storageService: IStorageService, + contextKeyService: IContextKeyService, + ) { + super(id, telemetryService, themeService, storageService); + if (contextKeyService) { + this.contextKey = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS.bindTo(contextKeyService); + this.findInputFocusContextKey = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED.bindTo(contextKeyService); + } + } + + public showFind() { + if (this._webview) { + this._webview.showFind(); + } + } + + public hideFind() { + if (this._webview) { + this._webview.hideFind(); + } + } + + public showNextFindTerm() { + if (this._webview) { + this._webview.showNextFindTerm(); + } + } + + public showPreviousFindTerm() { + if (this._webview) { + this._webview.showPreviousFindTerm(); + } + } + + public updateStyles() { + super.updateStyles(); + if (this._webview) { + this._webview.style(this.themeService.getTheme()); + } + } + + public get isWebviewEditor() { + return true; + } + + protected abstract createEditor(parent: Builder); +} + +class ShowWebViewEditorFindCommand extends Command { + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = this.getWebViewEditor(accessor); + if (webViewEditor) { + webViewEditor.showFind(); + } + } + + private getWebViewEditor(accessor: ServicesAccessor): WebviewEditor { + const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as WebviewEditor; + if (activeEditor.isWebviewEditor) { + return activeEditor; + } + return null; + } +} +const showFindCommand = new ShowWebViewEditorFindCommand({ + id: 'editor.action.webvieweditor.showFind', + precondition: KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(showFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +class HideWebViewEditorFindCommand extends Command { + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = this.getWebViewEditor(accessor); + if (webViewEditor) { + webViewEditor.hideFind(); + } + } + + private getWebViewEditor(accessor: ServicesAccessor): WebviewEditor { + const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as WebviewEditor; + if (activeEditor.isWebviewEditor) { + return activeEditor; + } + return null; + } +} +const hideCommand = new HideWebViewEditorFindCommand({ + id: 'editor.action.webvieweditor.hideFind', + precondition: KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, + kbOpts: { + primary: KeyCode.Escape + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(hideCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +class ShowWebViewEditorFindTermCommand extends Command { + constructor(opts: ICommandOptions, private _next: boolean) { + super(opts); + } + + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = this.getWebViewEditor(accessor); + if (webViewEditor) { + if (this._next) { + webViewEditor.showNextFindTerm(); + } else { + webViewEditor.showPreviousFindTerm(); + } + } + } + + private getWebViewEditor(accessor: ServicesAccessor): WebviewEditor { + const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as WebviewEditor; + if (activeEditor.isWebviewEditor) { + return activeEditor; + } + return null; + } +} + +const showNextFindTermCommand = new ShowWebViewEditorFindTermCommand({ + id: 'editor.action.webvieweditor.showNextFindTerm', + precondition: KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED, + kbOpts: { + primary: KeyMod.Alt | KeyCode.DownArrow + } +}, true); +KeybindingsRegistry.registerCommandAndKeybindingRule(showNextFindTermCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +const showPreviousFindTermCommand = new ShowWebViewEditorFindTermCommand({ + id: 'editor.action.webvieweditor.showPreviousFindTerm', + precondition: KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED, + kbOpts: { + primary: KeyMod.Alt | KeyCode.UpArrow + } +}, false); +KeybindingsRegistry.registerCommandAndKeybindingRule(showPreviousFindTermCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); diff --git a/src/vs/workbench/parts/html/browser/webviewFindWidget.ts b/src/vs/workbench/parts/html/browser/webviewFindWidget.ts new file mode 100644 index 0000000000..79e333eb34 --- /dev/null +++ b/src/vs/workbench/parts/html/browser/webviewFindWidget.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SimpleFindWidget } from 'vs/editor/contrib/find/browser/simpleFindWidget'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import Webview from './webview'; + +export class WebviewFindWidget extends SimpleFindWidget { + + constructor( + @IContextViewService _contextViewService: IContextViewService, + private webview: Webview + ) { + super(_contextViewService); + + this.find = this.find.bind(this); + this.hide = this.hide.bind(this); + this.onInputChanged = this.onInputChanged.bind(this); + } + + public find(previous) { + let val = this.inputValue; + if (this.webview !== null && val) { + this.webview.find(val, { findNext: true, forward: !previous }); + } + }; + + public hide() { + super.hide(); + this.webview.stopFind(true); + this.webview.focus(); + } + + public onInputChanged() { + if (!this.webview) { + return; + } + + let val = this.inputValue; + if (val) { + this.webview.startFind(val); + } else { + this.webview.stopFind(false); + } + } + + protected onFocusTrackerFocus() { + this.webview.notifyFindWidgetFocusChanged(true); + } + + protected onFocusTrackerBlur() { + this.webview.notifyFindWidgetFocusChanged(false); + } + + protected onFindInputFocusTrackerFocus() { + this.webview.notifyFindWidgetInputFocusChanged(true); + } + + protected onFindInputFocusTrackerBlur() { + this.webview.notifyFindWidgetInputFocusChanged(false); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/html/common/htmlInput.ts b/src/vs/workbench/parts/html/common/htmlInput.ts new file mode 100644 index 0000000000..01770f66cf --- /dev/null +++ b/src/vs/workbench/parts/html/common/htmlInput.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; + + +export interface HtmlInputOptions { + allowScripts?: boolean; + allowSvgs?: boolean; + svgWhiteList?: string[]; +} + +export function areHtmlInputOptionsEqual(left: HtmlInputOptions, right: HtmlInputOptions) { + return left.allowScripts === right.allowScripts && left.allowSvgs === right.allowSvgs; +} + +export class HtmlInput extends ResourceEditorInput { + constructor( + name: string, + description: string, + resource: URI, + public readonly options: HtmlInputOptions, + @ITextModelService textModelResolverService: ITextModelService + ) { + super(name, description, resource, textModelResolverService); + } +} diff --git a/src/vs/workbench/parts/markers/browser/markersPanel.ts b/src/vs/workbench/parts/markers/browser/markersPanel.ts new file mode 100644 index 0000000000..8dc10f3342 --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/markersPanel.ts @@ -0,0 +1,418 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/markers'; + +import * as errors from 'vs/base/common/errors'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Delayer } from 'vs/base/common/async'; +import dom = require('vs/base/browser/dom'); +import builder = require('vs/base/browser/builder'); +import { IAction, Action } from 'vs/base/common/actions'; +import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { toResource } from 'vs/workbench/common/editor'; +import { Panel } from 'vs/workbench/browser/panel'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import Constants from 'vs/workbench/parts/markers/common/constants'; +import { IProblemsConfiguration, MarkersModel, Marker, Resource, FilterOptions } from 'vs/workbench/parts/markers/common/markersModel'; +import { Controller } from 'vs/workbench/parts/markers/browser/markersTreeController'; +import Tree = require('vs/base/parts/tree/browser/tree'); +import TreeImpl = require('vs/base/parts/tree/browser/treeImpl'); +import * as Viewer from 'vs/workbench/parts/markers/browser/markersTreeViewer'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CollapseAllAction, FilterAction, FilterInputBoxActionItem } from 'vs/workbench/parts/markers/browser/markersPanelActions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import Messages from 'vs/workbench/parts/markers/common/messages'; +import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; +import { ContributableActionProvider } from 'vs/workbench/browser/actions'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import FileResultsNavigation from 'vs/workbench/parts/files/browser/fileResultsNavigation'; +import { debounceEvent } from 'vs/base/common/event'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { SimpleFileResourceDragAndDrop } from 'vs/base/parts/tree/browser/treeDnd'; + +export class MarkersPanel extends Panel { + + public markersModel: MarkersModel; + + private delayedRefresh: Delayer; + + private lastSelectedRelativeTop: number = 0; + private currentActiveFile: URI = null; + private hasToAutoReveal: boolean; + + private tree: Tree.ITree; + private autoExpanded: Set; + private rangeHighlightDecorations: RangeHighlightDecorations; + + private actions: IAction[]; + private filterAction: FilterAction; + private collapseAllAction: IAction; + + private treeContainer: HTMLElement; + private messageBoxContainer: HTMLElement; + private messageBox: HTMLElement; + + private markerFocusContextKey: IContextKey; + private currentFileGotAddedToMarkersData: boolean = false; + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IMarkerService private markerService: IMarkerService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IConfigurationService private configurationService: IConfigurationService, + @IContextKeyService private contextKeyService: IContextKeyService, + @ITelemetryService telemetryService: ITelemetryService, + @IListService private listService: IListService, + @IThemeService themeService: IThemeService + ) { + super(Constants.MARKERS_PANEL_ID, telemetryService, themeService); + this.delayedRefresh = new Delayer(500); + this.autoExpanded = new Set(); + this.markerFocusContextKey = Constants.MarkerFocusContextKey.bindTo(contextKeyService); + } + + public create(parent: builder.Builder): TPromise { + super.create(parent); + this.markersModel = new MarkersModel(); + + this.rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); + this.toUnbind.push(this.rangeHighlightDecorations); + + dom.addClass(parent.getHTMLElement(), 'markers-panel'); + + const conf = this.configurationService.getConfiguration(); + this.onConfigurationsUpdated(conf); + + let container = dom.append(parent.getHTMLElement(), dom.$('.markers-panel-container')); + + this.createMessageBox(container); + this.createTree(container); + + this.createActions(); + this.createListeners(); + + this.render(); + + return TPromise.as(null); + } + + public getTitle(): string { + return Messages.MARKERS_PANEL_TITLE_PROBLEMS; + } + + public layout(dimension: builder.Dimension): void { + this.tree.layout(dimension.height); + } + + public focus(): void { + if (this.tree.isDOMFocused()) { + return; + } + + if (this.markersModel.hasFilteredResources()) { + this.tree.DOMFocus(); + if (this.tree.getSelection().length === 0) { + this.tree.focusFirst(); + } + this.highlightCurrentSelectedMarkerRange(); + this.autoReveal(true); + } else { + this.messageBox.focus(); + } + } + + public setVisible(visible: boolean): TPromise { + let promise: TPromise = super.setVisible(visible); + if (!visible) { + this.rangeHighlightDecorations.removeHighlightRange(); + } + return promise; + } + + public getActions(): IAction[] { + this.collapseAllAction.enabled = this.markersModel.hasFilteredResources(); + return this.actions; + } + + public openFileAtElement(element: any, preserveFocus: boolean, sideByside: boolean, pinned: boolean): boolean { + if (element instanceof Marker) { + const marker: Marker = element; + this.telemetryService.publicLog('problems.marker.opened', { source: marker.marker.source }); + this.editorService.openEditor({ + resource: marker.resource, + options: { + selection: marker.range, + preserveFocus, + pinned, + revealIfVisible: true + }, + }, sideByside).done(editor => { + if (editor && preserveFocus) { + this.rangeHighlightDecorations.highlightRange(marker, editor.getControl()); + } else { + this.rangeHighlightDecorations.removeHighlightRange(); + } + }, errors.onUnexpectedError); + return true; + } else { + this.rangeHighlightDecorations.removeHighlightRange(); + } + return false; + } + + private refreshPanel(): TPromise { + this.collapseAllAction.enabled = this.markersModel.hasFilteredResources(); + dom.toggleClass(this.treeContainer, 'hidden', !this.markersModel.hasFilteredResources()); + this.renderMessage(); + if (this.markersModel.hasFilteredResources()) { + return this.tree.refresh().then(() => { + this.autoExpand(); + }); + } + return TPromise.as(null); + } + + public updateFilter(filter: string) { + this.markersModel.update(new FilterOptions(filter)); + this.autoExpanded = new Set(); + this.refreshPanel(); + this.autoReveal(); + } + + private createMessageBox(parent: HTMLElement): void { + this.messageBoxContainer = dom.append(parent, dom.$('.message-box-container')); + this.messageBox = dom.append(this.messageBoxContainer, dom.$('span')); + this.messageBox.setAttribute('tabindex', '0'); + } + + private createTree(parent: HTMLElement): void { + this.treeContainer = dom.append(parent, dom.$('.tree-container')); + dom.addClass(this.treeContainer, 'show-file-icons'); + const actionProvider = this.instantiationService.createInstance(ContributableActionProvider); + const renderer = this.instantiationService.createInstance(Viewer.Renderer, this.getActionRunner(), actionProvider); + const dnd = new SimpleFileResourceDragAndDrop(obj => obj instanceof Resource ? obj.uri : void 0); + let controller = this.instantiationService.createInstance(Controller); + this.tree = new TreeImpl.Tree(this.treeContainer, { + dataSource: new Viewer.DataSource(), + renderer, + controller, + sorter: new Viewer.Sorter(), + accessibilityProvider: new Viewer.MarkersTreeAccessibilityProvider(), + dnd + }, { + indentPixels: 0, + twistiePixels: 20, + ariaLabel: Messages.MARKERS_PANEL_ARIA_LABEL_PROBLEMS_TREE, + keyboardSupport: false + }); + + this._register(attachListStyler(this.tree, this.themeService)); + + this._register(this.tree.addListener('focus', (e: { focus: any }) => { + this.markerFocusContextKey.set(e.focus instanceof Marker); + })); + + const fileResultsNavigation = this._register(new FileResultsNavigation(this.tree)); + this._register(debounceEvent(fileResultsNavigation.openFile, (last, event) => event, 75, true)(options => { + this.openFileAtElement(options.element, options.editorOptions.preserveFocus, options.editorOptions.pinned, options.sideBySide); + })); + + const focusTracker = this._register(dom.trackFocus(this.tree.getHTMLElement())); + focusTracker.addBlurListener(() => { + this.markerFocusContextKey.set(false); + }); + + this.toUnbind.push(this.listService.register(this.tree)); + } + + private createActions(): void { + this.collapseAllAction = this.instantiationService.createInstance(CollapseAllAction, this.tree, true); + this.filterAction = new FilterAction(this); + this.actions = [ + this.filterAction, + this.collapseAllAction + ]; + this.actions.forEach(a => { + this.toUnbind.push(a); + }); + } + + private createListeners(): void { + this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationsUpdated(this.configurationService.getConfiguration()))); + this.toUnbind.push(this.markerService.onMarkerChanged(this.onMarkerChanged, this)); + this.toUnbind.push(this.editorGroupService.onEditorsChanged(this.onEditorsChanged, this)); + this.toUnbind.push(this.tree.addListener('selection', () => this.onSelected())); + } + + private onMarkerChanged(changedResources: URI[]) { + this.currentFileGotAddedToMarkersData = this.currentFileGotAddedToMarkersData || this.isCurrentFileGotAddedToMarkersData(changedResources); + this.updateResources(changedResources); + this.delayedRefresh.trigger(() => { + this.refreshPanel(); + this.updateRangeHighlights(); + if (this.currentFileGotAddedToMarkersData) { + this.autoReveal(); + this.currentFileGotAddedToMarkersData = false; + } + }); + } + + private isCurrentFileGotAddedToMarkersData(changedResources: URI[]) { + if (!this.currentActiveFile) { + return false; + } + const resourceForCurrentActiveFile = this.getResourceForCurrentActiveFile(); + if (resourceForCurrentActiveFile) { + return false; + } + return changedResources.some(r => r.toString() === this.currentActiveFile.toString()); + } + + private onEditorsChanged(): void { + this.currentActiveFile = toResource(this.editorService.getActiveEditorInput(), { filter: 'file' }); + this.autoReveal(); + } + + private onConfigurationsUpdated(conf: IProblemsConfiguration): void { + this.hasToAutoReveal = conf && conf.problems && conf.problems.autoReveal; + } + + private onSelected(): void { + let selection = this.tree.getSelection(); + if (selection && selection.length > 0) { + this.lastSelectedRelativeTop = this.tree.getRelativeTop(selection[0]); + } + } + + private updateResources(resources: URI[]) { + const bulkUpdater = this.markersModel.getBulkUpdater(); + for (const resource of resources) { + bulkUpdater.add(resource, this.markerService.read({ resource })); + } + bulkUpdater.done(); + for (const resource of resources) { + if (!this.markersModel.hasResource(resource)) { + this.autoExpanded.delete(resource.toString()); + } + } + } + + private render(): void { + let allMarkers = this.markerService.read(); + this.markersModel.update(allMarkers); + this.tree.setInput(this.markersModel).then(this.autoExpand.bind(this)); + dom.toggleClass(this.treeContainer, 'hidden', !this.markersModel.hasFilteredResources()); + this.renderMessage(); + } + + private renderMessage(): void { + let message = this.markersModel.getMessage(); + this.messageBox.textContent = message; + dom.toggleClass(this.messageBoxContainer, 'hidden', this.markersModel.hasFilteredResources()); + } + + private autoExpand(): void { + for (const resource of this.markersModel.filteredResources) { + const resourceUri = resource.uri.toString(); + if (!this.autoExpanded.has(resourceUri)) { + this.tree.expand(resource).done(null, errors.onUnexpectedError); + this.autoExpanded.add(resourceUri); + } + } + } + + private autoReveal(focus: boolean = false): void { + let conf = this.configurationService.getConfiguration(); + if (conf && conf.problems && conf.problems.autoReveal) { + this.revealMarkersForCurrentActiveEditor(focus); + } + } + + private revealMarkersForCurrentActiveEditor(focus: boolean = false): void { + let currentActiveResource = this.getResourceForCurrentActiveFile(); + if (currentActiveResource) { + if (this.tree.isExpanded(currentActiveResource) && this.hasSelectedMarkerFor(currentActiveResource)) { + this.tree.reveal(this.tree.getSelection()[0], this.lastSelectedRelativeTop); + if (focus) { + this.tree.setFocus(this.tree.getSelection()[0]); + } + } else { + this.tree.reveal(currentActiveResource, 0); + if (focus) { + this.tree.setFocus(currentActiveResource); + this.tree.setSelection([currentActiveResource]); + } + } + } else if (focus) { + this.tree.setSelection([]); + this.tree.focusFirst(); + } + } + + private getResourceForCurrentActiveFile(): Resource { + if (this.currentActiveFile) { + let resources = this.markersModel.filteredResources.filter((resource): boolean => { + return this.currentActiveFile.toString() === resource.uri.toString(); + }); + return resources.length > 0 ? resources[0] : null; + } + return null; + } + + private hasSelectedMarkerFor(resource): boolean { + let selectedElement = this.tree.getSelection(); + if (selectedElement && selectedElement.length > 0) { + if (selectedElement[0] instanceof Marker) { + if (resource.uri.toString() === selectedElement[0].marker.resource.toString()) { + return true; + } + } + } + return false; + } + + private updateRangeHighlights() { + this.rangeHighlightDecorations.removeHighlightRange(); + if (this.tree.isDOMFocused()) { + this.highlightCurrentSelectedMarkerRange(); + } + } + + private highlightCurrentSelectedMarkerRange() { + let selections = this.tree.getSelection(); + if (selections && selections.length === 1 && selections[0] instanceof Marker) { + const marker: Marker = selections[0]; + this.rangeHighlightDecorations.highlightRange(marker); + } + } + + public getActionItem(action: Action): IActionItem { + if (action.id === FilterAction.ID) { + return this.instantiationService.createInstance(FilterInputBoxActionItem, this, action); + } + return super.getActionItem(action); + } + + public getFocusElement(): Resource | Marker { + return this.tree.getFocus(); + } + + public dispose(): void { + super.dispose(); + + this.delayedRefresh.cancel(); + this.tree.dispose(); + this.markersModel.dispose(); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/browser/markersPanelActions.ts b/src/vs/workbench/parts/markers/browser/markersPanelActions.ts new file mode 100644 index 0000000000..c4bae18a2a --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/markersPanelActions.ts @@ -0,0 +1,164 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Delayer } from 'vs/base/common/async'; +import * as DOM from 'vs/base/browser/dom'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction, Action } from 'vs/base/common/actions'; +import { BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { TogglePanelAction } from 'vs/workbench/browser/panel'; +import Messages from 'vs/workbench/parts/markers/common/messages'; +import Constants from 'vs/workbench/parts/markers/common/constants'; +import { MarkersPanel } from 'vs/workbench/parts/markers/browser/markersPanel'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { CollapseAllAction as TreeCollapseAction } from 'vs/base/parts/tree/browser/treeDefaults'; +import Tree = require('vs/base/parts/tree/browser/tree'); +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; + +export class ToggleMarkersPanelAction extends TogglePanelAction { + + public static ID = 'workbench.actions.view.problems'; + public static LABEL = Messages.MARKERS_PANEL_TOGGLE_LABEL; + + constructor(id: string, label: string, + @IPartService partService: IPartService, + @IPanelService panelService: IPanelService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + super(id, label, Constants.MARKERS_PANEL_ID, panelService, partService); + } + + public run(): TPromise { + let promise = super.run(); + if (this.isPanelFocused()) { + this.telemetryService.publicLog('problems.used'); + } + return promise; + } +} + +export class ToggleErrorsAndWarningsAction extends TogglePanelAction { + + public static ID: string = 'workbench.action.showErrorsWarnings'; + public static LABEL = Messages.SHOW_ERRORS_WARNINGS_ACTION_LABEL; + + constructor(id: string, label: string, + @IPartService partService: IPartService, + @IPanelService panelService: IPanelService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + super(id, label, Constants.MARKERS_PANEL_ID, panelService, partService); + } + + public run(): TPromise { + let promise = super.run(); + if (this.isPanelFocused()) { + this.telemetryService.publicLog('problems.used'); + } + return promise; + } +} + +export class CollapseAllAction extends TreeCollapseAction { + + constructor(viewer: Tree.ITree, enabled: boolean, + @ITelemetryService private telemetryService: ITelemetryService) { + super(viewer, enabled); + } + + public run(context?: any): TPromise { + this.telemetryService.publicLog('problems.collapseAll.used'); + return super.run(context); + } + +} + +export class FilterAction extends Action { + + public static ID: string = 'workbench.actions.problems.filter'; + + constructor(private markersPanel: MarkersPanel) { + super(FilterAction.ID, Messages.MARKERS_PANEL_ACTION_TOOLTIP_FILTER, 'markers-panel-action-filter', true); + } + +} + +export class FilterInputBoxActionItem extends BaseActionItem { + + protected toDispose: lifecycle.IDisposable[]; + + private delayedFilterUpdate: Delayer; + + constructor(private markersPanel: MarkersPanel, action: IAction, + @IContextViewService private contextViewService: IContextViewService, + @IThemeService private themeService: IThemeService, + @ITelemetryService private telemetryService: ITelemetryService) { + super(markersPanel, action); + this.toDispose = []; + this.delayedFilterUpdate = new Delayer(500); + } + + public render(container: HTMLElement): void { + DOM.addClass(container, 'markers-panel-action-filter'); + let filterInputBox = new InputBox(container, this.contextViewService, { + placeholder: Messages.MARKERS_PANEL_FILTER_PLACEHOLDER, + ariaLabel: Messages.MARKERS_PANEL_FILTER_PLACEHOLDER + }); + this.toDispose.push(attachInputBoxStyler(filterInputBox, this.themeService)); + filterInputBox.value = this.markersPanel.markersModel.filterOptions.completeFilter; + this.toDispose.push(filterInputBox.onDidChange(filter => this.delayedFilterUpdate.trigger(() => this.updateFilter(filter)))); + this.toDispose.push(DOM.addStandardDisposableListener(filterInputBox.inputElement, 'keyup', (keyboardEvent) => this.onInputKeyUp(keyboardEvent, filterInputBox))); + this.toDispose.push(DOM.addStandardDisposableListener(container, 'keydown', this.handleKeyboardEvent)); + this.toDispose.push(DOM.addStandardDisposableListener(container, 'keyup', this.handleKeyboardEvent)); + } + + private updateFilter(filter: string) { + this.markersPanel.updateFilter(filter); + this.reportFilteringUsed(); + } + + private reportFilteringUsed(): void { + let data = {}; + data['errors'] = this.markersPanel.markersModel.filterOptions.filterErrors; + data['warnings'] = this.markersPanel.markersModel.filterOptions.filterWarnings; + data['infos'] = this.markersPanel.markersModel.filterOptions.filterInfos; + this.telemetryService.publicLog('problems.filter', data); + } + + public dispose(): void { + this.toDispose = lifecycle.dispose(this.toDispose); + super.dispose(); + } + + // Action toolbar is swallowing some keys for action items which should not be for an input box + private handleKeyboardEvent(e: IKeyboardEvent) { + switch (e.keyCode) { + case KeyCode.Space: + case KeyCode.LeftArrow: + case KeyCode.RightArrow: + case KeyCode.Escape: + e.stopPropagation(); + break; + } + } + + private onInputKeyUp(keyboardEvent: IKeyboardEvent, filterInputBox: InputBox) { + switch (keyboardEvent.keyCode) { + case KeyCode.Escape: + filterInputBox.value = ''; + return; + default: + return; + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/browser/markersTreeController.ts b/src/vs/workbench/parts/markers/browser/markersTreeController.ts new file mode 100644 index 0000000000..31a956bc46 --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/markersTreeController.ts @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as mouse from 'vs/base/browser/mouseEvent'; +import tree = require('vs/base/parts/tree/browser/tree'); +import treedefaults = require('vs/base/parts/tree/browser/treeDefaults'); +import { MarkersModel } from 'vs/workbench/parts/markers/common/markersModel'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions'; +import { IAction } from 'vs/base/common/actions'; +import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; + +export class Controller extends treedefaults.DefaultController { + + private contextMenu: IMenu; + + constructor( + @IContextMenuService private contextMenuService: IContextMenuService, + @IMenuService menuService: IMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService private _keybindingService: IKeybindingService + ) { + super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN, keyboardSupport: false }); + this.contextMenu = menuService.createMenu(MenuId.ProblemsPanelContext, contextKeyService); + } + + protected onLeftClick(tree: tree.ITree, element: any, event: mouse.IMouseEvent): boolean { + let currentFoucssed = tree.getFocus(); + if (super.onLeftClick(tree, element, event)) { + if (element instanceof MarkersModel) { + if (currentFoucssed) { + tree.setFocus(currentFoucssed); + } else { + tree.focusFirst(); + } + } + return true; + } + return false; + } + + public onContextMenu(tree: tree.ITree, element: any, event: tree.ContextMenuEvent): boolean { + tree.setFocus(element); + const actions = this._getMenuActions(); + if (!actions.length) { + return true; + } + const anchor = { x: event.posx + 1, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + + getActions: () => { + return TPromise.as(actions); + }, + + getActionItem: (action) => { + const keybinding = this._keybindingService.lookupKeybinding(action.id); + if (keybinding) { + return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + } + return null; + }, + + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + tree.DOMFocus(); + } + } + }); + + return true; + } + + private _getMenuActions(): IAction[] { + const result: IAction[] = []; + const groups = this.contextMenu.getActions(); + + for (let group of groups) { + const [, actions] = group; + result.push(...actions); + result.push(new Separator()); + } + result.pop(); // remove last separator + return result; + } +} diff --git a/src/vs/workbench/parts/markers/browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/browser/markersTreeViewer.ts new file mode 100644 index 0000000000..0fb540f1b6 --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/markersTreeViewer.ts @@ -0,0 +1,232 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise, Promise } from 'vs/base/common/winjs.base'; +import * as dom from 'vs/base/browser/dom'; +import * as network from 'vs/base/common/network'; +import { IDataSource, ITree, IRenderer, IAccessibilityProvider, ISorter, IActionProvider } from 'vs/base/parts/tree/browser/tree'; +import { IActionRunner } from 'vs/base/common/actions'; +import Severity from 'vs/base/common/severity'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { FileLabel, ResourceLabel } from 'vs/workbench/browser/labels'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { IMarker } from 'vs/platform/markers/common/markers'; +import { MarkersModel, Resource, Marker } from 'vs/workbench/parts/markers/common/markersModel'; +import Messages from 'vs/workbench/parts/markers/common/messages'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +interface IAnyResourceTemplateData { + count: CountBadge; + styler: IDisposable; +} + +interface IResourceTemplateData extends IAnyResourceTemplateData { + resourceLabel: ResourceLabel; +} + +interface IFileResourceTemplateData extends IAnyResourceTemplateData { + fileLabel: FileLabel; +} + +interface IMarkerTemplateData { + icon: HTMLElement; + source: HighlightedLabel; + description: HighlightedLabel; + lnCol: HTMLElement; +} + +export class DataSource implements IDataSource { + public getId(tree: ITree, element: any): string { + if (element instanceof MarkersModel) { + return 'root'; + } + if (element instanceof Resource) { + return element.uri.toString(); + } + if (element instanceof Marker) { + return (element).id; + } + return ''; + } + + public hasChildren(tree: ITree, element: any): boolean { + return element instanceof MarkersModel || element instanceof Resource; + } + + public getChildren(tree: ITree, element: any): Promise { + if (element instanceof MarkersModel) { + return TPromise.as((element).filteredResources); + } + if (element instanceof Resource) { + return TPromise.as(element.markers); + } + return null; + } + + public getParent(tree: ITree, element: any): Promise { + return TPromise.as(null); + } +} + +export class Renderer implements IRenderer { + + private static RESOURCE_TEMPLATE_ID = 'resource-template'; + private static FILE_RESOURCE_TEMPLATE_ID = 'file-resource-template'; + private static MARKER_TEMPLATE_ID = 'marker-template'; + + constructor(private actionRunner: IActionRunner, + private actionProvider: IActionProvider, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IInstantiationService private instantiationService: IInstantiationService, + @IThemeService private themeService: IThemeService + ) { + } + + public getHeight(tree: ITree, element: any): number { + return 22; + } + + public getTemplateId(tree: ITree, element: any): string { + if (element instanceof Resource) { + if ((element).uri.scheme === network.Schemas.file || (element).uri.scheme === network.Schemas.untitled) { + return Renderer.FILE_RESOURCE_TEMPLATE_ID; + } else { + return Renderer.RESOURCE_TEMPLATE_ID; + } + } + if (element instanceof Marker) { + return Renderer.MARKER_TEMPLATE_ID; + } + return ''; + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + dom.addClass(container, 'markers-panel-tree-entry'); + switch (templateId) { + case Renderer.FILE_RESOURCE_TEMPLATE_ID: + return this.renderFileResourceTemplate(container); + case Renderer.RESOURCE_TEMPLATE_ID: + return this.renderResourceTemplate(container); + case Renderer.MARKER_TEMPLATE_ID: + return this.renderMarkerTemplate(container); + } + } + + private renderFileResourceTemplate(container: HTMLElement): IFileResourceTemplateData { + const data: IFileResourceTemplateData = Object.create(null); + const resourceLabelContainer = dom.append(container, dom.$('.resource-label-container')); + data.fileLabel = this.instantiationService.createInstance(FileLabel, resourceLabelContainer, { supportHighlights: true }); + + const badgeWrapper = dom.append(container, dom.$('.count-badge-wrapper')); + data.count = new CountBadge(badgeWrapper); + data.styler = attachBadgeStyler(data.count, this.themeService); + + return data; + } + + private renderResourceTemplate(container: HTMLElement): IResourceTemplateData { + const data: IResourceTemplateData = Object.create(null); + const resourceLabelContainer = dom.append(container, dom.$('.resource-label-container')); + data.resourceLabel = this.instantiationService.createInstance(ResourceLabel, resourceLabelContainer, { supportHighlights: true }); + + const badgeWrapper = dom.append(container, dom.$('.count-badge-wrapper')); + data.count = new CountBadge(badgeWrapper); + data.styler = attachBadgeStyler(data.count, this.themeService); + + return data; + } + + private renderMarkerTemplate(container: HTMLElement): IMarkerTemplateData { + const data: IMarkerTemplateData = Object.create(null); + data.icon = dom.append(container, dom.$('.marker-icon')); + data.source = new HighlightedLabel(dom.append(container, dom.$(''))); + data.description = new HighlightedLabel(dom.append(container, dom.$('.marker-description'))); + data.lnCol = dom.append(container, dom.$('span.marker-line')); + return data; + } + + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + switch (templateId) { + case Renderer.FILE_RESOURCE_TEMPLATE_ID: + case Renderer.RESOURCE_TEMPLATE_ID: + return this.renderResourceElement(tree, element, templateData); + case Renderer.MARKER_TEMPLATE_ID: + return this.renderMarkerElement(tree, (element), templateData); + } + } + + private renderResourceElement(tree: ITree, element: Resource, templateData: IAnyResourceTemplateData) { + if ((templateData).fileLabel) { + (templateData).fileLabel.setFile(element.uri, { matches: element.matches }); + } else if ((templateData).resourceLabel) { + (templateData).resourceLabel.setLabel({ name: element.name, description: element.uri.toString(), resource: element.uri }, { matches: element.matches }); + } + templateData.count.setCount(element.markers.length); + } + + private renderMarkerElement(tree: ITree, element: Marker, templateData: IMarkerTemplateData) { + let marker = element.marker; + templateData.icon.className = 'icon ' + Renderer.iconClassNameFor(marker); + templateData.description.set(marker.message, element.labelMatches); + templateData.description.element.title = marker.message; + + dom.toggleClass(templateData.source.element, 'marker-source', !!marker.source); + templateData.source.set(marker.source, element.sourceMatches); + + templateData.lnCol.textContent = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(marker.startLineNumber, marker.startColumn); + } + + private static iconClassNameFor(element: IMarker): string { + switch (element.severity) { + case Severity.Ignore: + return 'info'; + case Severity.Info: + return 'info'; + case Severity.Warning: + return 'warning'; + case Severity.Error: + return 'error'; + } + return ''; + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + if (templateId === Renderer.FILE_RESOURCE_TEMPLATE_ID) { + (templateData).fileLabel.dispose(); + (templateData).styler.dispose(); + } + if (templateId === Renderer.RESOURCE_TEMPLATE_ID) { + (templateData).resourceLabel.dispose(); + (templateData).styler.dispose(); + } + } +} + +export class MarkersTreeAccessibilityProvider implements IAccessibilityProvider { + + public getAriaLabel(tree: ITree, element: any): string { + if (element instanceof Resource) { + return Messages.MARKERS_TREE_ARIA_LABEL_RESOURCE(element.name, element.markers.length); + } + if (element instanceof Marker) { + return Messages.MARKERS_TREE_ARIA_LABEL_MARKER(element.marker); + } + return null; + } + +} + +export class Sorter implements ISorter { + + public compare(tree: ITree, element: any, otherElement: any): number { + return MarkersModel.compare(element, otherElement); + } + +} \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/browser/markersWorkbenchContributions.ts b/src/vs/workbench/parts/markers/browser/markersWorkbenchContributions.ts new file mode 100644 index 0000000000..e558d9b9b8 --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/markersWorkbenchContributions.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Messages from 'vs/workbench/parts/markers/common/messages'; +import Constants from 'vs/workbench/parts/markers/common/constants'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { ToggleMarkersPanelAction, ToggleErrorsAndWarningsAction } from 'vs/workbench/parts/markers/browser/markersPanelActions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { MarkersPanel } from 'vs/workbench/parts/markers/browser/markersPanel'; + +export function registerContributions(): void { + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.MARKER_OPEN_SIDE_ACTION_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.MarkerFocusContextKey), + primary: KeyMod.CtrlCmd | KeyCode.Enter, + mac: { + primary: KeyMod.WinCtrl | KeyCode.Enter + }, + handler: (accessor, args: any) => { + const markersPanel = (accessor.get(IPanelService).getActivePanel()); + markersPanel.openFileAtElement(markersPanel.getFocusElement(), false, true, true); + } + }); + + // configuration + Registry.as(Extensions.Configuration).registerConfiguration({ + 'id': 'problems', + 'order': 101, + 'title': Messages.PROBLEMS_PANEL_CONFIGURATION_TITLE, + 'type': 'object', + 'properties': { + 'problems.autoReveal': { + 'description': Messages.PROBLEMS_PANEL_CONFIGURATION_AUTO_REVEAL, + 'type': 'boolean', + 'default': true + } + } + }); + + // markers panel + Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( + 'vs/workbench/parts/markers/browser/markersPanel', + 'MarkersPanel', + Constants.MARKERS_PANEL_ID, + Messages.MARKERS_PANEL_TITLE_PROBLEMS, + 'markersPanel', + 10, + ToggleMarkersPanelAction.ID + )); + + // actions + const registry = Registry.as(ActionExtensions.WorkbenchActions); + registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_M + }), 'View: Show Problems', Messages.MARKERS_PANEL_VIEW_CATEGORY); + + // Retaining old action to show errors and warnings, so that custom bindings to this action for existing users works. + registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleErrorsAndWarningsAction, ToggleErrorsAndWarningsAction.ID, ToggleErrorsAndWarningsAction.LABEL), 'Show Errors and Warnings'); +} \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/browser/media/markers.css b/src/vs/workbench/parts/markers/browser/media/markers.css new file mode 100644 index 0000000000..7a93b6e24c --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/media/markers.css @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-action-bar .action-item.markers-panel-action-filter { + max-width: 400px; + min-width: 100px; + flex: 1; + cursor: default; + margin: 4px 10px 0 0; +} + +.monaco-action-bar .action-item.markers-panel-action-filter .monaco-inputbox { + height: 25px; +} + +.vs .monaco-action-bar .action-item.markers-panel-action-filter .monaco-inputbox { + height: 25px; + border: 1px solid #ddd; +} + +.markers-panel .markers-panel-container { + height: 100%; +} + +.markers-panel .markers-panel-container .message-box-container { + line-height: 22px; + padding-left: 20px; +} + +.markers-panel .markers-panel-container .message-box-container span:focus { + outline: none; +} + +.markers-panel .markers-panel-container .hidden { + display: none; +} + +.markers-panel .markers-panel-container .tree-container { + height: 100%; +} + +.markers-panel .markers-panel-container .tree-container.hidden, +.markers-panel .markers-panel-container .message-box-container.hidden { + display: none; + visibility: hidden; +} + +.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry { + display: flex; + line-height: 22px; +} + +.hc-black .markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry { + line-height: 20px; +} + +.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry .marker-stats { + display: inline-block; + margin-left: 10px; +} + +.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry .count-badge-wrapper { + margin-left: 10px; +} + +.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry .marker-description { + margin-right: 5px; + text-overflow: ellipsis; + overflow: hidden; +} + +.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry .marker-source:before { + content: '['; +} + +.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry .marker-source:after { + content: ']'; +} + +.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry .marker-source, +.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry .marker-line { + opacity: 0.7; + margin-right: 5px; +} + +.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry .highlight { + font-weight: bold; +} + +.markers-panel .icon { + height: 22px; + margin-right: 4px; + margin-left: 4px; + flex: 0 0 16px; +} + +.markers-panel .icon.warning { + background: url('status-warning.svg') center center no-repeat; +} + +.markers-panel .icon.error { + background: url('status-error.svg') center center no-repeat; +} + +.markers-panel .icon.info { + background: url('status-info.svg') center center no-repeat; +} + +.vs-dark .markers-panel .icon.warning { + background: url('status-warning-inverse.svg') center center no-repeat; +} + +.vs-dark .markers-panel .icon.error { + background: url('status-error-inverse.svg') center center no-repeat; +} + +.vs-dark .markers-panel .icon.info { + background: url('status-info-inverse.svg') center center no-repeat; +} diff --git a/src/vs/workbench/parts/markers/browser/media/status-error-inverse.svg b/src/vs/workbench/parts/markers/browser/media/status-error-inverse.svg new file mode 100644 index 0000000000..3c852a7ffd --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/media/status-error-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/browser/media/status-error.svg b/src/vs/workbench/parts/markers/browser/media/status-error.svg new file mode 100644 index 0000000000..a1ddb39fed --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/media/status-error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/browser/media/status-info-inverse.svg b/src/vs/workbench/parts/markers/browser/media/status-info-inverse.svg new file mode 100644 index 0000000000..d38c363e0e --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/media/status-info-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/browser/media/status-info.svg b/src/vs/workbench/parts/markers/browser/media/status-info.svg new file mode 100644 index 0000000000..6e2e22f67b --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/media/status-info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/browser/media/status-warning-inverse.svg b/src/vs/workbench/parts/markers/browser/media/status-warning-inverse.svg new file mode 100644 index 0000000000..df44e61b32 --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/media/status-warning-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/browser/media/status-warning.svg b/src/vs/workbench/parts/markers/browser/media/status-warning.svg new file mode 100644 index 0000000000..f4e2a84b0a --- /dev/null +++ b/src/vs/workbench/parts/markers/browser/media/status-warning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/common/constants.ts b/src/vs/workbench/parts/markers/common/constants.ts new file mode 100644 index 0000000000..fffa5c6958 --- /dev/null +++ b/src/vs/workbench/parts/markers/common/constants.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 { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export default { + MARKERS_PANEL_ID: 'workbench.panel.markers', + MARKER_COPY_ACTION_ID: 'problems.action.copy', + MARKER_OPEN_SIDE_ACTION_ID: 'problems.action.openToSide', + + MarkerFocusContextKey: new RawContextKey('problemFocus', false) +}; diff --git a/src/vs/workbench/parts/markers/common/markersModel.ts b/src/vs/workbench/parts/markers/common/markersModel.ts new file mode 100644 index 0000000000..f5fbfddcc2 --- /dev/null +++ b/src/vs/workbench/parts/markers/common/markersModel.ts @@ -0,0 +1,366 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as paths from 'vs/base/common/paths'; +import * as types from 'vs/base/common/types'; +import Severity from 'vs/base/common/severity'; +import URI from 'vs/base/common/uri'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { IMarker, MarkerStatistics } from 'vs/platform/markers/common/markers'; +import { IFilter, IMatch, or, matchesContiguousSubString, matchesPrefix, matchesFuzzy } from 'vs/base/common/filters'; +import Messages from 'vs/workbench/parts/markers/common/messages'; +import { Schemas } from 'vs/base/common/network'; + +export interface BulkUpdater { + add(resource: URI, markers: IMarker[]); + done(); +} + +export class Resource { + + private _name: string = null; + private _path: string = null; + + constructor(public uri: URI, public markers: Marker[], + public statistics: MarkerStatistics, + public matches: IMatch[] = []) { + } + + public get path(): string { + if (this._path === null) { + this._path = this.uri.fsPath; + } + return this._path; + } + + public get name(): string { + if (this._name === null) { + this._name = paths.basename(this.uri.fsPath); + } + return this._name; + } +} + +export class Marker { + constructor(public id: string, public marker: IMarker, + public labelMatches: IMatch[] = [], + public sourceMatches: IMatch[] = []) { } + + public get resource(): URI { + return this.marker.resource; + } + + public get range(): IRange { + return this.marker; + } + + public toString(): string { + return [`file: '${this.marker.resource}'`, + `severity: '${Severity.toString(this.marker.severity)}'`, + `message: '${this.marker.message}'`, + `at: '${this.marker.startLineNumber},${this.marker.startColumn}'`, + `source: '${this.marker.source ? this.marker.source : ''}'`].join('\n'); + } + +} + +export class FilterOptions { + + static _filter: IFilter = or(matchesPrefix, matchesContiguousSubString); + static _fuzzyFilter: IFilter = or(matchesPrefix, matchesContiguousSubString, matchesFuzzy); + + private _filterErrors: boolean = false; + private _filterWarnings: boolean = false; + private _filterInfos: boolean = false; + private _filter: string = ''; + private _completeFilter: string = ''; + + constructor(filter: string = '') { + if (filter) { + this.parse(filter); + } + } + + public get filterErrors(): boolean { + return this._filterErrors; + } + + public get filterWarnings(): boolean { + return this._filterWarnings; + } + + public get filterInfos(): boolean { + return this._filterInfos; + } + + public get filter(): string { + return this._filter; + } + + public get completeFilter(): string { + return this._completeFilter; + } + + public hasFilters(): boolean { + return !!this._filter; + } + + private parse(filter: string) { + this._completeFilter = filter; + this._filter = filter.trim(); + this._filterErrors = this.matches(this._filter, Messages.MARKERS_PANEL_FILTER_ERRORS); + this._filterWarnings = this.matches(this._filter, Messages.MARKERS_PANEL_FILTER_WARNINGS); + this._filterInfos = this.matches(this._filter, Messages.MARKERS_PANEL_FILTER_INFOS); + } + + private matches(prefix: string, word: string): boolean { + let result = matchesPrefix(prefix, word); + return result && result.length > 0; + } +} + +export class MarkersModel { + + private markersByResource: Map; + + private _filteredResources: Resource[]; + private _nonFilteredResources: Resource[]; + private _filterOptions: FilterOptions; + + constructor(markers: IMarker[] = []) { + this.markersByResource = new Map(); + this._filterOptions = new FilterOptions(); + this.update(markers); + } + + public get filterOptions(): FilterOptions { + return this._filterOptions; + } + + public get filteredResources(): Resource[] { + return this._filteredResources; + } + + public hasFilteredResources(): boolean { + return this.filteredResources.length > 0; + } + + public hasResources(): boolean { + return this.markersByResource.size > 0; + } + + public hasResource(resource: URI): boolean { + return this.markersByResource.has(resource.toString()); + } + + public get nonFilteredResources(): Resource[] { + return this._nonFilteredResources; + } + + public getBulkUpdater(): BulkUpdater { + return { + add: (resourceUri: URI, markers: IMarker[]) => { + this.updateResource(resourceUri, markers); + }, + done: () => { + this.refresh(); + } + }; + } + + public update(filterOptions: FilterOptions); + public update(resourceUri: URI, markers: IMarker[]); + public update(markers: IMarker[]); + public update(arg1?: any, arg2?: any) { + if (arg1 instanceof FilterOptions) { + this._filterOptions = arg1; + } + + if (arg1 instanceof URI) { + this.updateResource(arg1, arg2); + } + + if (types.isArray(arg1)) { + this.updateMarkers(arg1); + } + + this.refresh(); + } + + private refresh(): void { + this.refreshResources(); + } + + private refreshResources(): void { + this._nonFilteredResources = []; + this._filteredResources = []; + this.markersByResource.forEach((values, uri) => { + const filteredResource = this.toFilteredResource(URI.parse(uri), values); + if (filteredResource.markers.length) { + this._filteredResources.push(filteredResource); + } else { + this._nonFilteredResources.push(filteredResource); + } + }); + } + + private updateResource(resourceUri: URI, markers: IMarker[]) { + if (this.markersByResource.has(resourceUri.toString())) { + this.markersByResource.delete(resourceUri.toString()); + } + if (markers.length > 0) { + this.markersByResource.set(resourceUri.toString(), markers); + } + } + + private updateMarkers(markers: IMarker[]) { + markers.forEach((marker: IMarker) => { + let uri: URI = marker.resource; + let markers: IMarker[] = this.markersByResource.get(uri.toString()); + if (!markers) { + markers = []; + this.markersByResource.set(uri.toString(), markers); + } + markers.push(marker); + }); + } + + private toFilteredResource(uri: URI, values: IMarker[]) { + let markers: Marker[] = []; + for (let i = 0; i < values.length; i++) { + const m = values[i]; + if (uri.scheme !== Schemas.walkThrough && uri.scheme !== Schemas.walkThroughSnippet && (!this._filterOptions.hasFilters() || this.filterMarker(m))) { + markers.push(this.toMarker(m, i, uri.toString())); + } + } + const matches = this._filterOptions.hasFilters() ? FilterOptions._filter(this._filterOptions.filter, paths.basename(uri.fsPath)) : []; + return new Resource(uri, markers, this.getStatistics(values), matches || []); + } + + private toMarker(marker: IMarker, index: number, uri: string): Marker { + const labelMatches = this._filterOptions.hasFilters() ? FilterOptions._fuzzyFilter(this._filterOptions.filter, marker.message) : []; + const sourceMatches = marker.source && this._filterOptions.hasFilters() ? FilterOptions._filter(this._filterOptions.filter, marker.source) : []; + return new Marker(uri + index, marker, labelMatches || [], sourceMatches || []); + } + + private filterMarker(marker: IMarker): boolean { + if (this._filterOptions.filterErrors && Severity.Error === marker.severity) { + return true; + } + if (this._filterOptions.filterWarnings && Severity.Warning === marker.severity) { + return true; + } + if (this._filterOptions.filterInfos && Severity.Info === marker.severity) { + return true; + } + if (!!FilterOptions._fuzzyFilter(this._filterOptions.filter, marker.message)) { + return true; + } + if (!!FilterOptions._filter(this._filterOptions.filter, paths.basename(marker.resource.fsPath))) { + return true; + } + if (!!marker.source && !!FilterOptions._filter(this._filterOptions.filter, marker.source)) { + return true; + } + return false; + } + + private getStatistics(markers: IMarker[]): MarkerStatistics { + let errors = 0, warnings = 0, infos = 0, unknowns = 0; + for (const marker of markers) { + switch (marker.severity) { + case Severity.Error: + errors++; + break; + case Severity.Warning: + warnings++; + break; + case Severity.Info: + infos++; + break; + default: + unknowns++; + break; + } + } + return { errors, warnings, infos, unknowns }; + } + + public dispose(): void { + this.markersByResource.clear(); + this._filteredResources = []; + this._nonFilteredResources = []; + } + + public getTitle(markerStatistics: MarkerStatistics): string { + let title = MarkersModel.getStatisticsLabel(markerStatistics); + return title ? title : Messages.MARKERS_PANEL_TITLE_PROBLEMS; + } + + public getMessage(): string { + if (this.hasFilteredResources()) { + return ''; + } + if (this.hasResources()) { + if (this._filterOptions.hasFilters()) { + return Messages.MARKERS_PANEL_NO_PROBLEMS_FILTERS; + } + } + return Messages.MARKERS_PANEL_NO_PROBLEMS_BUILT; + } + + public static getStatisticsLabel(markerStatistics: MarkerStatistics, onlyErrors: boolean = false): string { + let label = this.getLabel('', markerStatistics.errors, Messages.MARKERS_PANEL_SINGLE_ERROR_LABEL, Messages.MARKERS_PANEL_MULTIPLE_ERRORS_LABEL); + if (!onlyErrors) { + label = this.getLabel(label, markerStatistics.warnings, Messages.MARKERS_PANEL_SINGLE_WARNING_LABEL, Messages.MARKERS_PANEL_MULTIPLE_WARNINGS_LABEL); + label = this.getLabel(label, markerStatistics.infos, Messages.MARKERS_PANEL_SINGLE_INFO_LABEL, Messages.MARKERS_PANEL_MULTIPLE_INFOS_LABEL); + label = this.getLabel(label, markerStatistics.unknowns, Messages.MARKERS_PANEL_SINGLE_UNKNOWN_LABEL, Messages.MARKERS_PANEL_MULTIPLE_UNKNOWNS_LABEL); + } + return label; + } + + private static getLabel(title: string, markersCount: number, singleMarkerString: string, multipleMarkersFunction: (markersCount: number) => string): string { + if (markersCount <= 0) { + return title; + } + title = title ? title + ', ' : ''; + title += markersCount === 1 ? singleMarkerString : multipleMarkersFunction(markersCount); + return title; + } + + public static compare(a: any, b: any): number { + if (a instanceof Resource && b instanceof Resource) { + return MarkersModel.compareResources(a, b); + } + if (a instanceof Marker && b instanceof Marker) { + return MarkersModel.compareMarkers(a, b); + } + return 0; + } + + private static compareResources(a: Resource, b: Resource): number { + if (a.statistics.errors === 0 && b.statistics.errors > 0) { + return 1; + } + if (b.statistics.errors === 0 && a.statistics.errors > 0) { + return -1; + } + return a.path.localeCompare(b.path) || a.name.localeCompare(b.name); + } + + private static compareMarkers(a: Marker, b: Marker): number { + if (a.marker.severity === b.marker.severity) { + return Range.compareRangesUsingStarts(a.marker, b.marker); + } + return a.marker.severity > b.marker.severity ? -1 : 1; + } +} + +export interface IProblemsConfiguration { + problems: { + autoReveal: boolean + }; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/common/messages.ts b/src/vs/workbench/parts/markers/common/messages.ts new file mode 100644 index 0000000000..4b21191648 --- /dev/null +++ b/src/vs/workbench/parts/markers/common/messages.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import Severity from 'vs/base/common/severity'; +import { IMarker } from 'vs/platform/markers/common/markers'; + +export default class Messages { + + public static MARKERS_PANEL_VIEW_CATEGORY: string = nls.localize('viewCategory', "View"); + public static MARKERS_PANEL_TOGGLE_LABEL: string = nls.localize('problems.view.show.label', "Show Problems"); + + public static PROBLEMS_PANEL_CONFIGURATION_TITLE: string = nls.localize('problems.panel.configuration.title', "Problems View"); + public static PROBLEMS_PANEL_CONFIGURATION_AUTO_REVEAL: string = nls.localize('problems.panel.configuration.autoreveal', "Controls if Problems view should automatically reveal files when opening them"); + + public static MARKERS_PANEL_TITLE_PROBLEMS: string = nls.localize('markers.panel.title.problems', "Problems"); + public static MARKERS_PANEL_ARIA_LABEL_PROBLEMS_TREE: string = nls.localize('markers.panel.aria.label.problems.tree', "Problems grouped by files"); + + public static MARKERS_PANEL_NO_PROBLEMS_BUILT: string = nls.localize('markers.panel.no.problems.build', "No problems have been detected in the workspace so far."); + public static MARKERS_PANEL_NO_PROBLEMS_FILTERS: string = nls.localize('markers.panel.no.problems.filters', "No results found with provided filter criteria"); + + public static MARKERS_PANEL_ACTION_TOOLTIP_FILTER: string = nls.localize('markers.panel.action.filter', "Filter Problems"); + public static MARKERS_PANEL_FILTER_PLACEHOLDER: string = nls.localize('markers.panel.filter.placeholder', "Filter by type or text"); + public static MARKERS_PANEL_FILTER_ERRORS: string = nls.localize('markers.panel.filter.errors', "errors"); + public static MARKERS_PANEL_FILTER_WARNINGS: string = nls.localize('markers.panel.filter.warnings', "warnings"); + public static MARKERS_PANEL_FILTER_INFOS: string = nls.localize('markers.panel.filter.infos', "infos"); + + public static MARKERS_PANEL_SINGLE_ERROR_LABEL: string = nls.localize('markers.panel.single.error.label', "1 Error"); + public static MARKERS_PANEL_MULTIPLE_ERRORS_LABEL = (noOfErrors: number): string => { return nls.localize('markers.panel.multiple.errors.label', "{0} Errors", '' + noOfErrors); }; + public static MARKERS_PANEL_SINGLE_WARNING_LABEL: string = nls.localize('markers.panel.single.warning.label', "1 Warning"); + public static MARKERS_PANEL_MULTIPLE_WARNINGS_LABEL = (noOfWarnings: number): string => { return nls.localize('markers.panel.multiple.warnings.label', "{0} Warnings", '' + noOfWarnings); }; + public static MARKERS_PANEL_SINGLE_INFO_LABEL: string = nls.localize('markers.panel.single.info.label', "1 Info"); + public static MARKERS_PANEL_MULTIPLE_INFOS_LABEL = (noOfInfos: number): string => { return nls.localize('markers.panel.multiple.infos.label', "{0} Infos", '' + noOfInfos); }; + public static MARKERS_PANEL_SINGLE_UNKNOWN_LABEL: string = nls.localize('markers.panel.single.unknown.label', "1 Unknown"); + public static MARKERS_PANEL_MULTIPLE_UNKNOWNS_LABEL = (noOfUnknowns: number): string => { return nls.localize('markers.panel.multiple.unknowns.label', "{0} Unknowns", '' + noOfUnknowns); }; + + public static MARKERS_PANEL_AT_LINE_COL_NUMBER = (ln: number, col: number): string => { return nls.localize('markers.panel.at.ln.col.number', "({0}, {1})", '' + ln, '' + col); }; + + public static MARKERS_TREE_ARIA_LABEL_RESOURCE = (fileName, noOfProblems): string => { return nls.localize('problems.tree.aria.label.resource', "{0} with {1} problems", fileName, noOfProblems); }; + public static MARKERS_TREE_ARIA_LABEL_MARKER = (marker: IMarker): string => { + switch (marker.severity) { + case Severity.Error: + return marker.source ? nls.localize('problems.tree.aria.label.error.marker', "Error generated by {0}: {1} at line {2} and character {3}", marker.source, marker.message, marker.startLineNumber, marker.startColumn) + : nls.localize('problems.tree.aria.label.error.marker.nosource', "Error: {0} at line {1} and character {2}", marker.message, marker.startLineNumber, marker.startColumn); + case Severity.Warning: + return marker.source ? nls.localize('problems.tree.aria.label.warning.marker', "Warning generated by {0}: {1} at line {2} and character {3}", marker.source, marker.message, marker.startLineNumber, marker.startColumn) + : nls.localize('problems.tree.aria.label.warning.marker.nosource', "Warning: {0} at line {1} and character {2}", marker.message, marker.startLineNumber, marker.startColumn); + + case Severity.Info: + return marker.source ? nls.localize('problems.tree.aria.label.info.marker', "Info generated by {0}: {1} at line {2} and character {3}", marker.source, marker.message, marker.startLineNumber, marker.startColumn) + : nls.localize('problems.tree.aria.label.info.marker.nosource', "Info: {0} at line {1} and character {2}", marker.message, marker.startLineNumber, marker.startColumn); + default: + return marker.source ? nls.localize('problems.tree.aria.label.marker', "Problem generated by {0}: {1} at line {2} and character {3}", marker.source, marker.message, marker.startLineNumber, marker.startColumn) + : nls.localize('problems.tree.aria.label.marker.nosource', "Problem: {0} at line {1} and character {2}", marker.message, marker.startLineNumber, marker.startColumn); + } + } + + public static SHOW_ERRORS_WARNINGS_ACTION_LABEL: string = nls.localize('errors.warnings.show.label', "Show Errors and Warnings"); +} \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.ts b/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.ts new file mode 100644 index 0000000000..99919dc660 --- /dev/null +++ b/src/vs/workbench/parts/markers/electron-browser/markersElectronContributions.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { clipboard } from 'electron'; +import { Marker } from 'vs/workbench/parts/markers/common/markersModel'; +import Constants from 'vs/workbench/parts/markers/common/constants'; +import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; +import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { MarkersPanel } from 'vs/workbench/parts/markers/browser/markersPanel'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; + + +export function registerContributions(): void { + registerAction({ + id: Constants.MARKER_COPY_ACTION_ID, + title: localize('copyMarker', "Copy"), + handler(accessor) { + copyMarker(accessor.get(IPanelService)); + }, + menu: { + menuId: MenuId.ProblemsPanelContext, + when: Constants.MarkerFocusContextKey + }, + keybinding: { + keys: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_C + }, + when: Constants.MarkerFocusContextKey + } + }); +} + +function copyMarker(panelService: IPanelService) { + const activePanel = panelService.getActivePanel(); + if (activePanel instanceof MarkersPanel) { + const element = (activePanel).getFocusElement(); + if (element instanceof Marker) { + clipboard.writeText(`${element}`); + } + } +} + +interface IActionDescriptor { + id: string; + handler: ICommandHandler; + + // ICommandUI + title: string; + category?: string; + iconClass?: string; + f1?: boolean; + + // + menu?: { + menuId: MenuId, + when?: ContextKeyExpr; + group?: string; + }; + + // + keybinding?: { + when?: ContextKeyExpr; + weight?: number; + keys: IKeybindings; + }; +} + +function registerAction(desc: IActionDescriptor) { + + const { id, handler, title, category, iconClass, menu, keybinding } = desc; + + // 1) register as command + CommandsRegistry.registerCommand(id, handler); + + // 2) menus + let command = { id, title, iconClass, category }; + if (menu) { + let { menuId, when, group } = menu; + MenuRegistry.appendMenuItem(menuId, { + command, + when, + group + }); + } + + // 3) keybindings + if (keybinding) { + let { when, weight, keys } = keybinding; + KeybindingsRegistry.registerKeybindingRule({ + id, + when, + weight, + primary: keys.primary, + secondary: keys.secondary, + linux: keys.linux, + mac: keys.mac, + win: keys.win + }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/markers.contribution.ts b/src/vs/workbench/parts/markers/markers.contribution.ts new file mode 100644 index 0000000000..d49afd443a --- /dev/null +++ b/src/vs/workbench/parts/markers/markers.contribution.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { registerContributions } from 'vs/workbench/parts/markers/browser/markersWorkbenchContributions'; +import { registerContributions as registerElectronContributions } from 'vs/workbench/parts/markers/electron-browser/markersElectronContributions'; + +registerContributions(); +registerElectronContributions(); \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/test/common/markersModel.test.ts b/src/vs/workbench/parts/markers/test/common/markersModel.test.ts new file mode 100644 index 0000000000..507efc0099 --- /dev/null +++ b/src/vs/workbench/parts/markers/test/common/markersModel.test.ts @@ -0,0 +1,197 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import assert = require('assert'); +import URI from 'vs/base/common/uri'; +import Severity from 'vs/base/common/severity'; +import { IMarker } from 'vs/platform/markers/common/markers'; +import { MarkersModel, Marker, Resource } from 'vs/workbench/parts/markers/common/markersModel'; + +suite('MarkersModel Test', () => { + + test('getFilteredResource return markers grouped by resource', function () { + let marker1 = aMarker('res1'); + let marker2 = aMarker('res2'); + let marker3 = aMarker('res1'); + let marker4 = aMarker('res3'); + let marker5 = aMarker('res4'); + let marker6 = aMarker('res2'); + let testObject = new MarkersModel([marker1, marker2, marker3, marker4, marker5, marker6]); + + let actuals = testObject.filteredResources; + + assert.equal(4, actuals.length); + + assert.ok(compareResource(actuals[0], 'res1')); + assert.equal(2, actuals[0].markers.length); + assert.ok(hasMarker(actuals[0].markers, marker1)); + assert.ok(hasMarker(actuals[0].markers, marker3)); + + assert.ok(compareResource(actuals[1], 'res2')); + assert.equal(2, actuals[1].markers.length); + assert.ok(hasMarker(actuals[1].markers, marker2)); + assert.ok(hasMarker(actuals[1].markers, marker6)); + + assert.ok(compareResource(actuals[2], 'res3')); + assert.equal(1, actuals[2].markers.length); + assert.ok(hasMarker(actuals[2].markers, marker4)); + + assert.ok(compareResource(actuals[3], 'res4')); + assert.equal(1, actuals[3].markers.length); + assert.ok(hasMarker(actuals[3].markers, marker5)); + }); + + test('sort palces resources with no errors at the end', function () { + let marker1 = aMarker('a/res1', Severity.Warning); + let marker2 = aMarker('a/res2'); + let marker3 = aMarker('res4'); + let marker4 = aMarker('b/res3'); + let marker5 = aMarker('res4'); + let marker6 = aMarker('c/res2', Severity.Info); + let testObject = new MarkersModel([marker1, marker2, marker3, marker4, marker5, marker6]); + + let actuals = testObject.filteredResources.sort(MarkersModel.compare); + + assert.equal(5, actuals.length); + assert.ok(compareResource(actuals[0], 'a/res2')); + assert.ok(compareResource(actuals[1], 'b/res3')); + assert.ok(compareResource(actuals[2], 'res4')); + assert.ok(compareResource(actuals[3], 'a/res1')); + assert.ok(compareResource(actuals[4], 'c/res2')); + }); + + test('sort resources by file path', function () { + let marker1 = aMarker('a/res1'); + let marker2 = aMarker('a/res2'); + let marker3 = aMarker('res4'); + let marker4 = aMarker('b/res3'); + let marker5 = aMarker('res4'); + let marker6 = aMarker('c/res2'); + let testObject = new MarkersModel([marker1, marker2, marker3, marker4, marker5, marker6]); + + let actuals = testObject.filteredResources.sort(MarkersModel.compare); + + assert.equal(5, actuals.length); + assert.ok(compareResource(actuals[0], 'a/res1')); + assert.ok(compareResource(actuals[1], 'a/res2')); + assert.ok(compareResource(actuals[2], 'b/res3')); + assert.ok(compareResource(actuals[3], 'c/res2')); + assert.ok(compareResource(actuals[4], 'res4')); + }); + + test('sort markers by severity, line and column', function () { + let marker1 = aWarningWithRange(8, 1, 9, 3); + let marker2 = aWarningWithRange(3); + let marker3 = anErrorWithRange(8, 1, 9, 3); + let marker4 = anIgnoreWithRange(5); + let marker5 = anInfoWithRange(8, 1, 8, 4, 'ab'); + let marker6 = anErrorWithRange(3); + let marker7 = anErrorWithRange(5); + let marker8 = anInfoWithRange(5); + let marker9 = anErrorWithRange(8, 1, 8, 4, 'ab'); + let marker10 = anErrorWithRange(10); + let marker11 = anErrorWithRange(8, 1, 8, 4, 'ba'); + let marker12 = anIgnoreWithRange(3); + let marker13 = aWarningWithRange(5); + let marker14 = anErrorWithRange(4); + let marker15 = anErrorWithRange(8, 2, 8, 4); + let testObject = new MarkersModel([marker1, marker2, marker3, marker4, marker5, marker6, marker7, marker8, marker9, marker10, marker11, marker12, marker13, marker14, marker15]); + + let actuals = testObject.filteredResources[0].markers.sort(MarkersModel.compare); + + assert.equal(actuals[0].marker, marker6); + assert.equal(actuals[1].marker, marker14); + assert.equal(actuals[2].marker, marker7); + assert.equal(actuals[3].marker, marker9); + assert.equal(actuals[4].marker, marker11); + assert.equal(actuals[5].marker, marker3); + assert.equal(actuals[6].marker, marker15); + assert.equal(actuals[7].marker, marker10); + assert.equal(actuals[8].marker, marker2); + assert.equal(actuals[9].marker, marker13); + assert.equal(actuals[10].marker, marker1); + assert.equal(actuals[11].marker, marker8); + assert.equal(actuals[12].marker, marker5); + assert.equal(actuals[13].marker, marker12); + assert.equal(actuals[14].marker, marker4); + }); + + test('toString()', function () { + assert.equal(`file: 'file:///a/res1'\nseverity: 'Error'\nmessage: 'some message'\nat: '10,5'\nsource: 'tslint'`, new Marker('', aMarker('a/res1')).toString()); + assert.equal(`file: 'file:///a/res2'\nseverity: 'Warning'\nmessage: 'some message'\nat: '10,5'\nsource: 'tslint'`, new Marker('', aMarker('a/res2', Severity.Warning)).toString()); + assert.equal(`file: 'file:///a/res2'\nseverity: 'Info'\nmessage: 'Info'\nat: '1,2'\nsource: ''`, new Marker('', aMarker('a/res2', Severity.Info, 1, 2, 1, 8, 'Info', '')).toString()); + assert.equal(`file: 'file:///a/res2'\nseverity: ''\nmessage: 'Ignore message'\nat: '1,2'\nsource: 'Ignore'`, new Marker('', aMarker('a/res2', Severity.Ignore, 1, 2, 1, 8, 'Ignore message', 'Ignore')).toString()); + }); + + function hasMarker(markers: Marker[], marker: IMarker): boolean { + return markers.filter((m): boolean => { + return m.marker === marker; + }).length === 1; + } + + function compareResource(a: Resource, b: string): boolean { + return a.uri.toString() === URI.file(b).toString(); + } + + function anErrorWithRange(startLineNumber: number = 10, + startColumn: number = 5, + endLineNumber: number = startLineNumber + 1, + endColumn: number = startColumn + 5, + message: string = 'some message', + ): IMarker { + return aMarker('some resource', Severity.Error, startLineNumber, startColumn, endLineNumber, endColumn, message); + } + + function aWarningWithRange(startLineNumber: number = 10, + startColumn: number = 5, + endLineNumber: number = startLineNumber + 1, + endColumn: number = startColumn + 5, + message: string = 'some message', + ): IMarker { + return aMarker('some resource', Severity.Warning, startLineNumber, startColumn, endLineNumber, endColumn, message); + } + + function anInfoWithRange(startLineNumber: number = 10, + startColumn: number = 5, + endLineNumber: number = startLineNumber + 1, + endColumn: number = startColumn + 5, + message: string = 'some message', + ): IMarker { + return aMarker('some resource', Severity.Info, startLineNumber, startColumn, endLineNumber, endColumn, message); + } + + function anIgnoreWithRange(startLineNumber: number = 10, + startColumn: number = 5, + endLineNumber: number = startLineNumber + 1, + endColumn: number = startColumn + 5, + message: string = 'some message', + ): IMarker { + return aMarker('some resource', Severity.Ignore, startLineNumber, startColumn, endLineNumber, endColumn, message); + } + + function aMarker(resource: string = 'some resource', + severity: Severity = Severity.Error, + startLineNumber: number = 10, + startColumn: number = 5, + endLineNumber: number = startLineNumber + 1, + endColumn: number = startColumn + 5, + message: string = 'some message', + source: string = 'tslint' + ): IMarker { + return { + owner: 'someOwner', + resource: URI.file(resource), + severity, + message, + startLineNumber, + startColumn, + endLineNumber, + endColumn, + source + }; + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/output/browser/media/clear_output.svg b/src/vs/workbench/parts/output/browser/media/clear_output.svg new file mode 100644 index 0000000000..6e4d3a1d48 --- /dev/null +++ b/src/vs/workbench/parts/output/browser/media/clear_output.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/output/browser/media/clear_output_inverse.svg b/src/vs/workbench/parts/output/browser/media/clear_output_inverse.svg new file mode 100644 index 0000000000..7df4455d7c --- /dev/null +++ b/src/vs/workbench/parts/output/browser/media/clear_output_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/output/browser/media/output.css b/src/vs/workbench/parts/output/browser/media/output.css new file mode 100644 index 0000000000..9076cbf901 --- /dev/null +++ b/src/vs/workbench/parts/output/browser/media/output.css @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .output-action.clear-output { + background: url('clear_output.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .output-action.clear-output, +.hc-black .monaco-workbench .output-action.clear-output { + background: url('clear_output_inverse.svg') center center no-repeat; +} + +.monaco-workbench .output-action.output-scroll-lock { + background: url('output_lock.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .output-action.output-scroll-lock, +.hc-black .monaco-workbench .output-action.output-scroll-lock { + background: url('output_lock_inverse.svg') center center no-repeat; +} + +.monaco-workbench .output-action.output-scroll-unlock { + background: url('output_unlock.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .output-action.output-scroll-unlock, +.hc-black .monaco-workbench .output-action.output-scroll-unlock { + background: url('output_unlock_inverse.svg') center center no-repeat; +} diff --git a/src/vs/workbench/parts/output/browser/media/output_lock.svg b/src/vs/workbench/parts/output/browser/media/output_lock.svg new file mode 100644 index 0000000000..ad3968a775 --- /dev/null +++ b/src/vs/workbench/parts/output/browser/media/output_lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/output/browser/media/output_lock_inverse.svg b/src/vs/workbench/parts/output/browser/media/output_lock_inverse.svg new file mode 100644 index 0000000000..876fe60e4f --- /dev/null +++ b/src/vs/workbench/parts/output/browser/media/output_lock_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/output/browser/media/output_unlock.svg b/src/vs/workbench/parts/output/browser/media/output_unlock.svg new file mode 100644 index 0000000000..39933c43aa --- /dev/null +++ b/src/vs/workbench/parts/output/browser/media/output_unlock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/output/browser/media/output_unlock_inverse.svg b/src/vs/workbench/parts/output/browser/media/output_unlock_inverse.svg new file mode 100644 index 0000000000..4827413fc7 --- /dev/null +++ b/src/vs/workbench/parts/output/browser/media/output_unlock_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/output/browser/output.contribution.ts b/src/vs/workbench/parts/output/browser/output.contribution.ts new file mode 100644 index 0000000000..a23bfc0499 --- /dev/null +++ b/src/vs/workbench/parts/output/browser/output.contribution.ts @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { OutputService } from 'vs/workbench/parts/output/browser/outputServices'; +import { ToggleOutputAction, ClearOutputAction } from 'vs/workbench/parts/output/browser/outputActions'; +import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT } from 'vs/workbench/parts/output/common/output'; +import { PanelRegistry, Extensions, PanelDescriptor } from 'vs/workbench/browser/panel'; +import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; + +// Register Service +registerSingleton(IOutputService, OutputService); + +// Register Output Mode +ModesRegistry.registerLanguage({ + id: OUTPUT_MODE_ID, + extensions: [], + aliases: [null], + mimetypes: [OUTPUT_MIME] +}); + +// Register Output Panel +Registry.as(Extensions.Panels).registerPanel(new PanelDescriptor( + 'vs/workbench/parts/output/browser/outputPanel', + 'OutputPanel', + OUTPUT_PANEL_ID, + nls.localize('output', "Output"), + 'output', + 20, + ToggleOutputAction.ID +)); + +// register toggle output action globally +const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleOutputAction, ToggleOutputAction.ID, ToggleOutputAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_U, + linux: { + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_H) // On Ubuntu Ctrl+Shift+U is taken by some global OS command + } +}), 'View: Toggle Output', nls.localize('viewCategory', "View")); + +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearOutputAction, ClearOutputAction.ID, ClearOutputAction.LABEL), + 'View: Clear Output', nls.localize('viewCategory', "View")); + + +interface IActionDescriptor { + id: string; + handler: ICommandHandler; + + // ICommandUI + title: string; + category?: string; + iconClass?: string; + f1?: boolean; + + // menus + menu?: { + menuId: MenuId, + when?: ContextKeyExpr; + group?: string; + }; + + // keybindings + keybinding?: { + when?: ContextKeyExpr; + weight: number; + keys: IKeybindings; + }; +} + +function registerAction(desc: IActionDescriptor) { + + const { id, handler, title, category, iconClass, f1, menu, keybinding } = desc; + + // 1) register as command + CommandsRegistry.registerCommand(id, handler); + + // 2) command palette + let command = { id, title, iconClass, category }; + if (f1) { + MenuRegistry.addCommand(command); + } + + // 3) menus + if (menu) { + let { menuId, when, group } = menu; + MenuRegistry.appendMenuItem(menuId, { + command, + when, + group + }); + } + + // 4) keybindings + if (keybinding) { + let { when, weight, keys } = keybinding; + KeybindingsRegistry.registerKeybindingRule({ + id, + when, + weight, + primary: keys.primary, + secondary: keys.secondary, + linux: keys.linux, + mac: keys.mac, + win: keys.win + }); + } +} + +// Define clear command, contribute to editor context menu +registerAction({ + id: 'editor.action.clearoutput', + title: nls.localize('clearOutput.label', "Clear Output"), + menu: { + menuId: MenuId.EditorContext, + when: CONTEXT_IN_OUTPUT + }, + handler(accessor) { + accessor.get(IOutputService).getActiveChannel().clear(); + } +}); diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts new file mode 100644 index 0000000000..0ce7f8a8f1 --- /dev/null +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import { IAction, Action } from 'vs/base/common/actions'; +import { IOutputService, OUTPUT_PANEL_ID } from 'vs/workbench/parts/output/common/output'; +import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { TogglePanelAction } from 'vs/workbench/browser/panel'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +export class ToggleOutputAction extends TogglePanelAction { + + public static ID = 'workbench.action.output.toggleOutput'; + public static LABEL = nls.localize('toggleOutput', "Toggle Output"); + + constructor( + id: string, label: string, + @IPartService partService: IPartService, + @IPanelService panelService: IPanelService, + ) { + super(id, label, OUTPUT_PANEL_ID, panelService, partService); + } +} + +export class ClearOutputAction extends Action { + + public static ID = 'workbench.output.action.clearOutput'; + public static LABEL = nls.localize('clearOutput', "Clear Output"); + + constructor( + id: string, label: string, + @IOutputService private outputService: IOutputService, + @IPanelService private panelService: IPanelService + ) { + super(id, label, 'output-action clear-output'); + } + + public run(): TPromise { + this.outputService.getActiveChannel().clear(); + this.panelService.getActivePanel().focus(); + + return TPromise.as(true); + } +} + +export class ToggleOutputScrollLockAction extends Action { + + public static ID = 'workbench.output.action.toggleOutputScrollLock'; + public static LABEL = nls.localize({ key: 'toggleOutputScrollLock', comment: ['Turn on / off automatic output scrolling'] }, "Toggle Output Scroll Lock"); + + private toDispose: IDisposable[] = []; + + constructor(id: string, label: string, + @IOutputService private outputService: IOutputService) { + super(id, label, 'output-action output-scroll-unlock'); + this.toDispose.push(this.outputService.onActiveOutputChannel(channel => this.setClass(this.outputService.getActiveChannel().scrollLock))); + } + + public run(): TPromise { + const activeChannel = this.outputService.getActiveChannel(); + if (activeChannel) { + activeChannel.scrollLock = !activeChannel.scrollLock; + this.setClass(activeChannel.scrollLock); + } + + return TPromise.as(true); + } + + private setClass(locked: boolean) { + if (locked) { + this.class = 'output-action output-scroll-lock'; + } else { + this.class = 'output-action output-scroll-unlock'; + } + } + + public dispose() { + super.dispose(); + this.toDispose = dispose(this.toDispose); + } +} + +export class SwitchOutputAction extends Action { + + public static ID = 'workbench.output.action.switchBetweenOutputs'; + + constructor( @IOutputService private outputService: IOutputService) { + super(SwitchOutputAction.ID, nls.localize('switchToOutput.label', "Switch to Output")); + + this.class = 'output-action switch-to-output'; + } + + public run(channelId?: string): TPromise { + return this.outputService.getChannel(channelId).show(); + } +} + +export class SwitchOutputActionItem extends SelectActionItem { + + constructor( + action: IAction, + @IOutputService private outputService: IOutputService, + @IThemeService themeService: IThemeService + ) { + super(null, action, [], 0); + + this.toDispose.push(this.outputService.onOutputChannel(() => { + const activeChannelIndex = this.getSelected(this.outputService.getActiveChannel().id); + this.setOptions(this.getOptions(), activeChannelIndex); + })); + this.toDispose.push(this.outputService.onActiveOutputChannel(activeChannelId => this.setOptions(this.getOptions(), this.getSelected(activeChannelId)))); + this.toDispose.push(attachSelectBoxStyler(this.selectBox, themeService)); + + this.setOptions(this.getOptions(), this.getSelected(this.outputService.getActiveChannel().id)); + } + + protected getActionContext(option: string): string { + const channel = this.outputService.getChannels().filter(channelData => channelData.label === option).pop(); + + return channel ? channel.id : option; + } + + private getOptions(): string[] { + return this.outputService.getChannels().map(c => c.label); + } + + private getSelected(outputId: string): number { + if (!outputId) { + return undefined; + } + + return Math.max(0, this.outputService.getChannels().map(c => c.id).indexOf(outputId)); + } +} diff --git a/src/vs/workbench/parts/output/browser/outputPanel.ts b/src/vs/workbench/parts/output/browser/outputPanel.ts new file mode 100644 index 0000000000..ea40e0c15b --- /dev/null +++ b/src/vs/workbench/parts/output/browser/outputPanel.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/output'; +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action, IAction } from 'vs/base/common/actions'; +import { Builder } from 'vs/base/browser/builder'; +import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; +import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; +import { OutputEditors, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT } from 'vs/workbench/parts/output/common/output'; +import { SwitchOutputAction, SwitchOutputActionItem, ClearOutputAction, ToggleOutputScrollLockAction } from 'vs/workbench/parts/output/browser/outputActions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; + +export class OutputPanel extends TextResourceEditor { + private actions: IAction[]; + private scopedInstantiationService: IInstantiationService; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IInstantiationService instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, + @IThemeService themeService: IThemeService, + @IOutputService private outputService: IOutputService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IEditorGroupService editorGroupService: IEditorGroupService, + @IModeService modeService: IModeService, + @ITextFileService textFileService: ITextFileService + ) { + super(telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, modeService, textFileService); + + this.scopedInstantiationService = instantiationService; + } + + public getId(): string { + return OUTPUT_PANEL_ID; + } + + public getActions(): IAction[] { + if (!this.actions) { + this.actions = [ + this.instantiationService.createInstance(SwitchOutputAction), + this.instantiationService.createInstance(ClearOutputAction, ClearOutputAction.ID, ClearOutputAction.LABEL), + this.instantiationService.createInstance(ToggleOutputScrollLockAction, ToggleOutputScrollLockAction.ID, ToggleOutputScrollLockAction.LABEL) + ]; + + this.actions.forEach(a => { + this.toUnbind.push(a); + }); + } + + return this.actions; + } + + public getActionItem(action: Action): IActionItem { + if (action.id === SwitchOutputAction.ID) { + return this.instantiationService.createInstance(SwitchOutputActionItem, action); + } + + return super.getActionItem(action); + } + + protected getConfigurationOverrides(): IEditorOptions { + const options = super.getConfigurationOverrides(); + options.wordWrap = 'on'; // all output editors wrap + options.lineNumbers = 'off'; // all output editors hide line numbers + options.glyphMargin = false; + options.lineDecorationsWidth = 20; + options.rulers = []; + options.folding = false; + options.scrollBeyondLastLine = false; + options.renderLineHighlight = 'none'; + options.minimap = { enabled: false }; + + const outputConfig = this.configurationService.getConfiguration(null, '[Log]'); + if (outputConfig && outputConfig['editor.minimap.enabled']) { + options.minimap = { enabled: true }; + } + + return options; + } + + protected getAriaLabel(): string { + const channel = this.outputService.getActiveChannel(); + + return channel ? nls.localize('outputPanelWithInputAriaLabel', "{0}, Output panel", channel.label) : nls.localize('outputPanelAriaLabel', "Output panel"); + } + + public setInput(input: EditorInput, options?: EditorOptions): TPromise { + return super.setInput(input, options).then(() => this.revealLastLine()); + } + + protected createEditor(parent: Builder): void { + + // First create the scoped instantation service and only then construct the editor using the scoped service + const scopedContextKeyService = this.contextKeyService.createScoped(parent.getHTMLElement()); + this.toUnbind.push(scopedContextKeyService); + this.scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); + super.createEditor(parent); + + CONTEXT_IN_OUTPUT.bindTo(scopedContextKeyService).set(true); + this.setInput(OutputEditors.getInstance(this.instantiationService, this.outputService.getActiveChannel()), null); + } + + public get instantiationService(): IInstantiationService { + return this.scopedInstantiationService; + } +} diff --git a/src/vs/workbench/parts/output/browser/outputServices.ts b/src/vs/workbench/parts/output/browser/outputServices.ts new file mode 100644 index 0000000000..4f1363d6bc --- /dev/null +++ b/src/vs/workbench/parts/output/browser/outputServices.ts @@ -0,0 +1,383 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import strings = require('vs/base/common/strings'); +import Event, { Emitter } from 'vs/base/common/event'; +import { binarySearch } from 'vs/base/common/arrays'; +import URI from 'vs/base/common/uri'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IEditor } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { IOutputChannelIdentifier, OutputEditors, IOutputEvent, IOutputChannel, IOutputService, IOutputDelta, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, MAX_OUTPUT_LENGTH, OUTPUT_SCHEME, OUTPUT_MIME } from 'vs/workbench/parts/output/common/output'; +import { OutputPanel } from 'vs/workbench/parts/output/browser/outputPanel'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { OutputLinkProvider } from 'vs/workbench/parts/output/common/outputLinkProvider'; +import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; + +const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; + +export class BufferedContent { + + private data: string[] = []; + private dataIds: number[] = []; + private idPool = 0; + private length = 0; + + public append(content: string): void { + this.data.push(content); + this.dataIds.push(++this.idPool); + this.length += content.length; + this.trim(); + } + + public clear(): void { + this.data.length = 0; + this.dataIds.length = 0; + this.length = 0; + } + + private trim(): void { + if (this.length < MAX_OUTPUT_LENGTH * 1.2) { + return; + } + + while (this.length > MAX_OUTPUT_LENGTH) { + this.dataIds.shift(); + const removed = this.data.shift(); + this.length -= removed.length; + } + } + + public getDelta(previousDelta?: IOutputDelta): IOutputDelta { + let idx = -1; + if (previousDelta) { + idx = binarySearch(this.dataIds, previousDelta.id, (a, b) => a - b); + } + + const id = this.idPool; + if (idx >= 0) { + const value = strings.removeAnsiEscapeCodes(this.data.slice(idx + 1).join('')); + return { value, id, append: true }; + } else { + const value = strings.removeAnsiEscapeCodes(this.data.join('')); + return { value, id }; + } + } +} + +export class OutputService implements IOutputService { + + public _serviceBrand: any; + + private receivedOutput: Map = new Map(); + private channels: Map = new Map(); + + private activeChannelId: string; + + private _onOutput: Emitter; + private _onOutputChannel: Emitter; + private _onActiveOutputChannel: Emitter; + + private _outputLinkDetector: OutputLinkProvider; + private _outputContentProvider: OutputContentProvider; + private _outputPanel: OutputPanel; + + constructor( + @IStorageService private storageService: IStorageService, + @IInstantiationService private instantiationService: IInstantiationService, + @IPanelService private panelService: IPanelService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IModelService modelService: IModelService, + @ITextModelService textModelResolverService: ITextModelService + ) { + this._onOutput = new Emitter(); + this._onOutputChannel = new Emitter(); + this._onActiveOutputChannel = new Emitter(); + + const channels = this.getChannels(); + this.activeChannelId = this.storageService.get(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE, channels && channels.length > 0 ? channels[0].id : null); + + this._outputLinkDetector = instantiationService.createInstance(OutputLinkProvider); + + this._outputContentProvider = instantiationService.createInstance(OutputContentProvider, this); + + // Register as text model content provider for output + textModelResolverService.registerTextModelContentProvider(OUTPUT_SCHEME, this._outputContentProvider); + } + + public get onOutput(): Event { + return this._onOutput.event; + } + + public get onOutputChannel(): Event { + return this._onOutputChannel.event; + } + + public get onActiveOutputChannel(): Event { + return this._onActiveOutputChannel.event; + } + + public getChannel(id: string): IOutputChannel { + if (!this.channels.has(id)) { + const channelData = Registry.as(Extensions.OutputChannels).getChannel(id); + + const self = this; + this.channels.set(id, { + id, + label: channelData ? channelData.label : id, + getOutput(before?: IOutputDelta) { + return self.getOutput(id, before); + }, + get scrollLock() { + return self._outputContentProvider.scrollLock(id); + }, + set scrollLock(value: boolean) { + self._outputContentProvider.setScrollLock(id, value); + }, + append: (output: string) => this.append(id, output), + show: (preserveFocus: boolean) => this.showOutput(id, preserveFocus), + clear: () => this.clearOutput(id), + dispose: () => this.removeOutput(id) + }); + } + + return this.channels.get(id); + } + + public getChannels(): IOutputChannelIdentifier[] { + return Registry.as(Extensions.OutputChannels).getChannels(); + } + + private append(channelId: string, output: string): void { + + // Initialize + if (!this.receivedOutput.has(channelId)) { + this.receivedOutput.set(channelId, new BufferedContent()); + + this._onOutputChannel.fire(channelId); // emit event that we have a new channel + } + + // Store + if (output) { + const channel = this.receivedOutput.get(channelId); + channel.append(output); + } + + this._onOutput.fire({ channelId: channelId, isClear: false }); + } + + public getActiveChannel(): IOutputChannel { + return this.getChannel(this.activeChannelId); + } + + private getOutput(channelId: string, previousDelta: IOutputDelta): IOutputDelta { + if (this.receivedOutput.has(channelId)) { + return this.receivedOutput.get(channelId).getDelta(previousDelta); + } + + return undefined; + } + + private clearOutput(channelId: string): void { + if (this.receivedOutput.has(channelId)) { + this.receivedOutput.get(channelId).clear(); + this._onOutput.fire({ channelId: channelId, isClear: true }); + } + } + + private removeOutput(channelId: string): void { + this.receivedOutput.delete(channelId); + Registry.as(Extensions.OutputChannels).removeChannel(channelId); + if (this.activeChannelId === channelId) { + const channels = this.getChannels(); + this.activeChannelId = channels.length ? channels[0].id : undefined; + if (this._outputPanel && this.activeChannelId) { + this._outputPanel.setInput(OutputEditors.getInstance(this.instantiationService, this.getChannel(this.activeChannelId)), EditorOptions.create({ preserveFocus: true })); + } + this._onActiveOutputChannel.fire(this.activeChannelId); + } + + this._onOutputChannel.fire(channelId); + } + + private showOutput(channelId: string, preserveFocus?: boolean): TPromise { + const panel = this.panelService.getActivePanel(); + if (this.activeChannelId === channelId && panel && panel.getId() === OUTPUT_PANEL_ID) { + return TPromise.as(panel); + } + + this.activeChannelId = channelId; + this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannelId, StorageScope.WORKSPACE); + this._onActiveOutputChannel.fire(channelId); // emit event that a new channel is active + + return this.panelService.openPanel(OUTPUT_PANEL_ID, !preserveFocus).then((outputPanel: OutputPanel) => { + this._outputPanel = outputPanel; + return outputPanel && outputPanel.setInput(OutputEditors.getInstance(this.instantiationService, this.getChannel(this.activeChannelId)), EditorOptions.create({ preserveFocus: preserveFocus })). + then(() => outputPanel); + }); + } +} + +class OutputContentProvider implements ITextModelContentProvider { + + private static OUTPUT_DELAY = 300; + + private bufferedOutput = new Map(); + private appendOutputScheduler: { [channel: string]: RunOnceScheduler; }; + private channelIdsWithScrollLock: Set = new Set(); + private toDispose: IDisposable[]; + + constructor( + private outputService: IOutputService, + @IModelService private modelService: IModelService, + @IModeService private modeService: IModeService, + @IPanelService private panelService: IPanelService + ) { + this.appendOutputScheduler = Object.create(null); + this.toDispose = []; + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.outputService.onOutput(e => this.onOutputReceived(e))); + this.toDispose.push(this.outputService.onActiveOutputChannel(channel => this.scheduleOutputAppend(channel))); + this.toDispose.push(this.panelService.onDidPanelOpen(panel => { + if (panel.getId() === OUTPUT_PANEL_ID) { + this.appendOutput(); + } + })); + } + + private onOutputReceived(e: IOutputEvent): void { + const model = this.getModel(e.channelId); + if (!model) { + return; // only react if we have a known model + } + + // Append to model + if (e.isClear) { + model.setValue(''); + } else { + this.scheduleOutputAppend(e.channelId); + } + } + + private getModel(channel: string): IModel { + return this.modelService.getModel(URI.from({ scheme: OUTPUT_SCHEME, path: channel })); + } + + private scheduleOutputAppend(channel: string): void { + if (!this.isVisible(channel)) { + return; // only if the output channel is visible + } + + let scheduler = this.appendOutputScheduler[channel]; + if (!scheduler) { + scheduler = new RunOnceScheduler(() => { + if (this.isVisible(channel)) { + this.appendOutput(channel); + } + }, OutputContentProvider.OUTPUT_DELAY); + + this.appendOutputScheduler[channel] = scheduler; + this.toDispose.push(scheduler); + } + + if (scheduler.isScheduled()) { + return; // only if not already scheduled + } + + scheduler.schedule(); + } + + private appendOutput(channel?: string): void { + if (!channel) { + const activeChannel = this.outputService.getActiveChannel(); + channel = activeChannel && activeChannel.id; + } + + if (!channel) { + return; // return if we do not have a valid channel to append to + } + + const model = this.getModel(channel); + if (!model) { + return; // only react if we have a known model + } + + const bufferedOutput = this.bufferedOutput.get(channel); + const newOutput = this.outputService.getChannel(channel).getOutput(bufferedOutput); + if (!newOutput) { + model.setValue(''); + return; + } + this.bufferedOutput.set(channel, newOutput); + + // just fill in the full (trimmed) output if we exceed max length + if (!newOutput.append) { + model.setValue(newOutput.value); + } + + // otherwise append + else { + const lastLine = model.getLineCount(); + const lastLineMaxColumn = model.getLineMaxColumn(lastLine); + + model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), newOutput.value)]); + } + + if (!this.channelIdsWithScrollLock.has(channel)) { + // reveal last line + const panel = this.panelService.getActivePanel(); + (panel).revealLastLine(); + } + } + + private isVisible(channel: string): boolean { + const panel = this.panelService.getActivePanel(); + + return panel && panel.getId() === OUTPUT_PANEL_ID && this.outputService.getActiveChannel().id === channel; + } + + public scrollLock(channelId): boolean { + return this.channelIdsWithScrollLock.has(channelId); + } + + public setScrollLock(channelId: string, value: boolean): void { + if (value) { + this.channelIdsWithScrollLock.add(channelId); + } else { + this.channelIdsWithScrollLock.delete(channelId); + } + } + + public provideTextContent(resource: URI): TPromise { + const output = this.outputService.getChannel(resource.fsPath).getOutput(); + const content = output ? output.value : ''; + + let codeEditorModel = this.modelService.getModel(resource); + if (!codeEditorModel) { + codeEditorModel = this.modelService.createModel(content, this.modeService.getOrCreateMode(OUTPUT_MIME), resource); + } + + return TPromise.as(codeEditorModel); + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } +} diff --git a/src/vs/workbench/parts/output/common/output.ts b/src/vs/workbench/parts/output/common/output.ts new file mode 100644 index 0000000000..f01809840c --- /dev/null +++ b/src/vs/workbench/parts/output/common/output.ts @@ -0,0 +1,215 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import Event from 'vs/base/common/event'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IEditor } from 'vs/platform/editor/common/editor'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import nls = require('vs/nls'); +import URI from 'vs/base/common/uri'; + +/** + * Mime type used by the output editor. + */ +export const OUTPUT_MIME = 'text/x-code-output'; + +/** + * Output resource scheme. + */ +export const OUTPUT_SCHEME = 'output'; + +/** + * Id used by the output editor. + */ +export const OUTPUT_MODE_ID = 'Log'; + +/** + * Output panel id + */ +export const OUTPUT_PANEL_ID = 'workbench.panel.output'; + +export const Extensions = { + OutputChannels: 'workbench.contributions.outputChannels' +}; + +export const OUTPUT_SERVICE_ID = 'outputService'; + +export const MAX_OUTPUT_LENGTH = 10000 /* Max. number of output lines to show in output */ * 100 /* Guestimated chars per line */; + +export const CONTEXT_IN_OUTPUT = new RawContextKey('inOutput', false); + +/** + * The output event informs when new output got received. + */ +export interface IOutputEvent { + channelId: string; + isClear: boolean; +} + +export const IOutputService = createDecorator(OUTPUT_SERVICE_ID); + +/** + * The output service to manage output from the various processes running. + */ +export interface IOutputService { + _serviceBrand: any; + + /** + * Given the channel id returns the output channel instance. + * Channel should be first registered via OutputChannelRegistry. + */ + getChannel(id: string): IOutputChannel; + + /** + * Returns an array of all known output channels as identifiers. + */ + getChannels(): IOutputChannelIdentifier[]; + + /** + * Returns the currently active channel. + * Only one channel can be active at a given moment. + */ + getActiveChannel(): IOutputChannel; + + /** + * Allows to register on Output events. + */ + onOutput: Event; + + /** + * Allows to register on a output channel being added or removed + */ + onOutputChannel: Event; + + /** + * Allows to register on active output channel change. + */ + onActiveOutputChannel: Event; +} + +export interface IOutputDelta { + readonly value: string; + readonly id: number; + readonly append?: boolean; +} + +export interface IOutputChannel { + + /** + * Identifier of the output channel. + */ + id: string; + + /** + * Label of the output channel to be displayed to the user. + */ + label: string; + + /** + * Returns the value indicating whether the channel has scroll locked. + */ + scrollLock: boolean; + + /** + * Appends output to the channel. + */ + append(output: string): void; + + /** + * Returns the received output content. + * If a delta is passed, returns only the content that came after the passed delta. + */ + getOutput(previousDelta?: IOutputDelta): IOutputDelta; + + /** + * Opens the output for this channel. + */ + show(preserveFocus?: boolean): TPromise; + + /** + * Clears all received output for this channel. + */ + clear(): void; + + /** + * Disposes the output channel. + */ + dispose(): void; +} + +export interface IOutputChannelIdentifier { + id: string; + label: string; +} + +export interface IOutputChannelRegistry { + + /** + * Make an output channel known to the output world. + */ + registerChannel(id: string, name: string): void; + + /** + * Returns the list of channels known to the output world. + */ + getChannels(): IOutputChannelIdentifier[]; + + /** + * Returns the channel with the passed id. + */ + getChannel(id: string): IOutputChannelIdentifier; + + /** + * Remove the output channel with the passed id. + */ + removeChannel(id: string): void; +} + +class OutputChannelRegistry implements IOutputChannelRegistry { + private channels = new Map(); + + public registerChannel(id: string, label: string): void { + if (!this.channels.has(id)) { + this.channels.set(id, { id, label }); + } + } + + public getChannels(): IOutputChannelIdentifier[] { + const result: IOutputChannelIdentifier[] = []; + this.channels.forEach(value => result.push(value)); + return result; + } + + public getChannel(id: string): IOutputChannelIdentifier { + return this.channels.get(id); + } + + public removeChannel(id: string): void { + this.channels.delete(id); + } +} + +Registry.add(Extensions.OutputChannels, new OutputChannelRegistry()); + +export class OutputEditors { + + private static instances: { [channel: string]: ResourceEditorInput; } = Object.create(null); + + public static getInstance(instantiationService: IInstantiationService, channel: IOutputChannel): ResourceEditorInput { + if (OutputEditors.instances[channel.id]) { + return OutputEditors.instances[channel.id]; + } + + const resource = URI.from({ scheme: OUTPUT_SCHEME, path: channel.id }); + + OutputEditors.instances[channel.id] = instantiationService.createInstance(ResourceEditorInput, nls.localize('output', "Output"), channel ? nls.localize('channel', "for '{0}'", channel.label) : '', resource); + + return OutputEditors.instances[channel.id]; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/output/common/outputLinkComputer.ts b/src/vs/workbench/parts/output/common/outputLinkComputer.ts new file mode 100644 index 0000000000..3d6a661358 --- /dev/null +++ b/src/vs/workbench/parts/output/common/outputLinkComputer.ts @@ -0,0 +1,182 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IMirrorModel, IWorkerContext } from 'vs/editor/common/services/editorSimpleWorker'; +import { ILink } from 'vs/editor/common/modes'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import paths = require('vs/base/common/paths'); +import strings = require('vs/base/common/strings'); +import arrays = require('vs/base/common/arrays'); +import { Range } from 'vs/editor/common/core/range'; + +export interface ICreateData { + workspaceFolders: string[]; +} + +export interface IResourceCreator { + toResource: (folderRelativePath: string) => URI; +} + +export class OutputLinkComputer { + private ctx: IWorkerContext; + private patterns: Map; + + constructor(ctx: IWorkerContext, createData: ICreateData) { + this.ctx = ctx; + this.patterns = new Map(); + + this.computePatterns(createData); + } + + private computePatterns(createData: ICreateData): void { + + // Produce patterns for each workspace root we are configured with + // This means that we will be able to detect links for paths that + // contain any of the workspace roots as segments. + const workspaceFolders = createData.workspaceFolders.map(r => URI.parse(r)); + workspaceFolders.forEach(workspaceFolder => { + const patterns = OutputLinkComputer.createPatterns(workspaceFolder); + this.patterns.set(workspaceFolder.fsPath, patterns); + }); + } + + private getModel(uri: string): IMirrorModel { + const models = this.ctx.getMirrorModels(); + for (let i = 0; i < models.length; i++) { + const model = models[i]; + if (model.uri.toString() === uri) { + return model; + } + } + + return null; + } + + public computeLinks(uri: string): TPromise { + const model = this.getModel(uri); + if (!model) { + return void 0; + } + + const links: ILink[] = []; + const lines = model.getValue().split(/\r\n|\r|\n/); + + // For each workspace root patterns + this.patterns.forEach((folderPatterns, folderPath) => { + const resourceCreator: IResourceCreator = { + toResource: (folderRelativePath: string): URI => { + if (typeof folderRelativePath === 'string') { + return URI.file(paths.join(folderPath, folderRelativePath)); + } + + return null; + } + }; + + for (let i = 0, len = lines.length; i < len; i++) { + links.push(...OutputLinkComputer.detectLinks(lines[i], i + 1, folderPatterns, resourceCreator)); + } + }); + + return TPromise.as(links); + } + + public static createPatterns(workspaceFolder: URI): RegExp[] { + const patterns: RegExp[] = []; + + const workspaceFolderVariants = arrays.distinct([ + paths.normalize(workspaceFolder.fsPath, true), + paths.normalize(workspaceFolder.fsPath, false) + ]); + + workspaceFolderVariants.forEach(workspaceFolderVariant => { + + // Example: /workspaces/express/server.js on line 8, column 13 + patterns.push(new RegExp(strings.escapeRegExpCharacters(workspaceFolderVariant) + '(\\S*) on line ((\\d+)(, column (\\d+))?)', 'gi')); + + // Example: /workspaces/express/server.js:line 8, column 13 + patterns.push(new RegExp(strings.escapeRegExpCharacters(workspaceFolderVariant) + '(\\S*):line ((\\d+)(, column (\\d+))?)', 'gi')); + + // Example: /workspaces/mankala/Features.ts(45): error + // Example: /workspaces/mankala/Features.ts (45): error + // Example: /workspaces/mankala/Features.ts(45,18): error + // Example: /workspaces/mankala/Features.ts (45,18): error + patterns.push(new RegExp(strings.escapeRegExpCharacters(workspaceFolderVariant) + '([^\\s\\(\\)]*)(\\s?\\((\\d+)(,(\\d+))?)\\)', 'gi')); + + // Example: at /workspaces/mankala/Game.ts + // Example: at /workspaces/mankala/Game.ts:336 + // Example: at /workspaces/mankala/Game.ts:336:9 + patterns.push(new RegExp(strings.escapeRegExpCharacters(workspaceFolderVariant) + '([^:\\s\\(\\)<>\'\"\\[\\]]*)(:(\\d+))?(:(\\d+))?', 'gi')); + }); + + return patterns; + } + + /** + * Detect links. Made public static to allow for tests. + */ + public static detectLinks(line: string, lineIndex: number, patterns: RegExp[], resourceCreator: IResourceCreator): ILink[] { + const links: ILink[] = []; + + patterns.forEach(pattern => { + pattern.lastIndex = 0; // the holy grail of software development + + let match: RegExpExecArray; + let offset = 0; + while ((match = pattern.exec(line)) !== null) { + + // Convert the relative path information to a resource that we can use in links + const folderRelativePath = strings.rtrim(match[1], '.').replace(/\\/g, '/'); // remove trailing "." that likely indicate end of sentence + let resource: string; + try { + resource = resourceCreator.toResource(folderRelativePath).toString(); + } catch (error) { + continue; // we might find an invalid URI and then we dont want to loose all other links + } + + // Append line/col information to URI if matching + if (match[3]) { + const lineNumber = match[3]; + + if (match[5]) { + const columnNumber = match[5]; + resource = strings.format('{0}#{1},{2}', resource, lineNumber, columnNumber); + } else { + resource = strings.format('{0}#{1}', resource, lineNumber); + } + } + + const fullMatch = strings.rtrim(match[0], '.'); // remove trailing "." that likely indicate end of sentence + + const index = line.indexOf(fullMatch, offset); + offset += index + fullMatch.length; + + const linkRange = { + startColumn: index + 1, + startLineNumber: lineIndex, + endColumn: index + 1 + fullMatch.length, + endLineNumber: lineIndex + }; + + if (links.some(link => Range.areIntersectingOrTouching(link.range, linkRange))) { + return; // Do not detect duplicate links + } + + links.push({ + range: linkRange, + url: resource + }); + } + }); + + return links; + } +} + +export function create(ctx: IWorkerContext, createData: ICreateData): OutputLinkComputer { + return new OutputLinkComputer(ctx, createData); +} diff --git a/src/vs/workbench/parts/output/common/outputLinkProvider.ts b/src/vs/workbench/parts/output/common/outputLinkProvider.ts new file mode 100644 index 0000000000..c8bd7df352 --- /dev/null +++ b/src/vs/workbench/parts/output/common/outputLinkProvider.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { RunOnceScheduler, wireCancellationToken } from 'vs/base/common/async'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { LinkProviderRegistry, ILink } from 'vs/editor/common/modes'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { OUTPUT_MODE_ID } from 'vs/workbench/parts/output/common/output'; +import { MonacoWebWorker, createWebWorker } from 'vs/editor/common/services/webWorker'; +import { ICreateData, OutputLinkComputer } from 'vs/workbench/parts/output/common/outputLinkComputer'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; + +export class OutputLinkProvider { + + private static DISPOSE_WORKER_TIME = 3 * 60 * 1000; // dispose worker after 3 minutes of inactivity + + private worker: MonacoWebWorker; + private disposeWorkerScheduler: RunOnceScheduler; + private linkProviderRegistration: IDisposable; + private workspacesCount: number; + + constructor( + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IModelService private modelService: IModelService + ) { + this.workspacesCount = 0; + this.disposeWorkerScheduler = new RunOnceScheduler(() => this.disposeWorker(), OutputLinkProvider.DISPOSE_WORKER_TIME); + + this.registerListeners(); + this.updateLinkProviderWorker(); + } + + private registerListeners(): void { + this.contextService.onDidChangeWorkspaceRoots(() => this.updateLinkProviderWorker()); + } + + private updateLinkProviderWorker(): void { + + // We have a workspace + if (this.contextService.hasWorkspace()) { + + // Register link provider unless done already + if (!this.linkProviderRegistration) { + this.linkProviderRegistration = LinkProviderRegistry.register({ language: OUTPUT_MODE_ID, scheme: '*' }, { + provideLinks: (model, token): Thenable => { + return wireCancellationToken(token, this.provideLinks(model.uri)); + } + }); + } + + // Update link provider worker if workspace roots changed + const newWorkspacesCount = this.contextService.getWorkspace().roots.length; + if (this.workspacesCount !== newWorkspacesCount) { + this.workspacesCount = newWorkspacesCount; + + // Next computer will trigger recompute + this.disposeWorker(); + this.disposeWorkerScheduler.cancel(); + } + } + + // Dispose link provider when no longer having a workspace + else if (this.linkProviderRegistration) { + this.workspacesCount = 0; + dispose(this.linkProviderRegistration); + this.linkProviderRegistration = void 0; + this.disposeWorker(); + this.disposeWorkerScheduler.cancel(); + } + } + + private getOrCreateWorker(): MonacoWebWorker { + this.disposeWorkerScheduler.schedule(); + + if (!this.worker) { + const createData: ICreateData = { + workspaceFolders: this.contextService.getWorkspace().roots.map(root => root.toString()) + }; + + this.worker = createWebWorker(this.modelService, { + moduleId: 'vs/workbench/parts/output/common/outputLinkComputer', + createData, + label: 'outputLinkComputer' + }); + } + + return this.worker; + } + + private provideLinks(modelUri: URI): TPromise { + return this.getOrCreateWorker().withSyncedResources([modelUri]).then(linkComputer => { + return linkComputer.computeLinks(modelUri.toString()); + }); + } + + private disposeWorker(): void { + if (this.worker) { + this.worker.dispose(); + this.worker = null; + } + } +} diff --git a/src/vs/workbench/parts/output/test/bufferedContent.test.ts b/src/vs/workbench/parts/output/test/bufferedContent.test.ts new file mode 100644 index 0000000000..9d2f82edac --- /dev/null +++ b/src/vs/workbench/parts/output/test/bufferedContent.test.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { BufferedContent } from 'vs/workbench/parts/output/browser/outputServices'; + +suite('Workbench - Output Buffered Content', () => { + + test('Buffered Content - Simple', () => { + const bufferedContent = new BufferedContent(); + bufferedContent.append('first'); + bufferedContent.append('second'); + bufferedContent.append('third'); + const delta = bufferedContent.getDelta(); + assert.equal(bufferedContent.getDelta().value, 'firstsecondthird'); + bufferedContent.clear(); + assert.equal(bufferedContent.getDelta().value, ''); + assert.equal(bufferedContent.getDelta(delta).value, ''); + }); + + test('Buffered Content - Appending Output', () => { + const bufferedContent = new BufferedContent(); + bufferedContent.append('first'); + const firstDelta = bufferedContent.getDelta(); + bufferedContent.append('second'); + bufferedContent.append('third'); + const secondDelta = bufferedContent.getDelta(firstDelta); + assert.equal(secondDelta.append, true); + assert.equal(secondDelta.value, 'secondthird'); + bufferedContent.append('fourth'); + bufferedContent.append('fifth'); + assert.equal(bufferedContent.getDelta(firstDelta).value, 'secondthirdfourthfifth'); + assert.equal(bufferedContent.getDelta(secondDelta).value, 'fourthfifth'); + }); + + test('Buffered Content - Lots of Output', function () { + this.timeout(10000); + const bufferedContent = new BufferedContent(); + bufferedContent.append('first line'); + const firstDelta = bufferedContent.getDelta(); + let longString = ''; + for (let i = 0; i < 5000; i++) { + bufferedContent.append(i.toString()); + longString += i.toString(); + } + const secondDelta = bufferedContent.getDelta(firstDelta); + assert.equal(secondDelta.append, true); + assert.equal(secondDelta.value.substr(secondDelta.value.length - 4), '4999'); + longString = longString + longString + longString + longString; + bufferedContent.append(longString); + bufferedContent.append(longString); + const thirdDelta = bufferedContent.getDelta(firstDelta); + assert.equal(!!thirdDelta.append, true); + assert.equal(thirdDelta.value.substr(thirdDelta.value.length - 4), '4999'); + + bufferedContent.clear(); + assert.equal(bufferedContent.getDelta().value, ''); + }); +}); diff --git a/src/vs/workbench/parts/output/test/outputLinkProvider.test.ts b/src/vs/workbench/parts/output/test/outputLinkProvider.test.ts new file mode 100644 index 0000000000..8be51e10ec --- /dev/null +++ b/src/vs/workbench/parts/output/test/outputLinkProvider.test.ts @@ -0,0 +1,422 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { isMacintosh, isLinux } from 'vs/base/common/platform'; +import { OutputLinkComputer } from 'vs/workbench/parts/output/common/outputLinkComputer'; +import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; + +function toOSPath(p: string): string { + if (isMacintosh || isLinux) { + return p.replace(/\\/g, '/'); + } + + return p; +} + +suite('Workbench - OutputWorker', () => { + + test('OutputWorker - Link detection', function () { + let patternsSlash = OutputLinkComputer.createPatterns( + URI.file('C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala') + ); + + let patternsBackSlash = OutputLinkComputer.createPatterns( + URI.file('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala') + ); + + let contextService = new TestContextService(); + + let line = toOSPath('Foo bar'); + let result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 0); + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 0); + + // Example: at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString()); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 84); + + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString()); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 84); + + // Example: at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts:336 + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts:336 in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#336'); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 88); + + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts:336 in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#336'); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 88); + + // Example: at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts:336:9 + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts:336:9 in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#336,9'); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 90); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#336,9'); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 90); + + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts:336:9 in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#336,9'); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 90); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#336,9'); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 90); + + // Example: at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts>dir + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts>dir in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString()); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 84); + + // Example: at [C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts:336:9] + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts:336:9] in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#336,9'); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 90); + + // Example: at [C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts] + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts] in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString()); + + // Example: C:\Users\someone\AppData\Local\Temp\_monacodata_9888\workspaces\express\server.js on line 8 + line = toOSPath('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts on line 8'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#8'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 90); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#8'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 90); + + // Example: C:\Users\someone\AppData\Local\Temp\_monacodata_9888\workspaces\express\server.js on line 8, column 13 + line = toOSPath('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts on line 8, column 13'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#8,13'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 101); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#8,13'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 101); + + line = toOSPath('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts on LINE 8, COLUMN 13'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#8,13'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 101); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#8,13'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 101); + + // Example: C:\Users\someone\AppData\Local\Temp\_monacodata_9888\workspaces\express\server.js:line 8 + line = toOSPath('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts:line 8'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#8'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 87); + + // Example: at File.put (C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Game.ts) + line = toOSPath(' at File.put (C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Game.ts)'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString()); + assert.equal(result[0].range.startColumn, 15); + assert.equal(result[0].range.endColumn, 94); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString()); + assert.equal(result[0].range.startColumn, 15); + assert.equal(result[0].range.endColumn, 94); + + // Example: at File.put (C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Game.ts:278) + line = toOSPath(' at File.put (C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Game.ts:278)'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#278'); + assert.equal(result[0].range.startColumn, 15); + assert.equal(result[0].range.endColumn, 98); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#278'); + assert.equal(result[0].range.startColumn, 15); + assert.equal(result[0].range.endColumn, 98); + + // Example: at File.put (C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Game.ts:278:34) + line = toOSPath(' at File.put (C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Game.ts:278:34)'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#278,34'); + assert.equal(result[0].range.startColumn, 15); + assert.equal(result[0].range.endColumn, 101); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#278,34'); + assert.equal(result[0].range.startColumn, 15); + assert.equal(result[0].range.endColumn, 101); + + line = toOSPath(' at File.put (C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Game.ts:278:34)'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#278,34'); + assert.equal(result[0].range.startColumn, 15); + assert.equal(result[0].range.endColumn, 101); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString() + '#278,34'); + assert.equal(result[0].range.startColumn, 15); + assert.equal(result[0].range.endColumn, 101); + + // Example: C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Features.ts(45): error + line = toOSPath('C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/lib/something/Features.ts(45): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 102); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 102); + + // Example: C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Features.ts (45,18): error + line = toOSPath('C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/lib/something/Features.ts (45): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 103); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 103); + + // Example: C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Features.ts(45,18): error + line = toOSPath('C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/lib/something/Features.ts(45,18): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 105); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 105); + + line = toOSPath('C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/lib/something/Features.ts(45,18): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 105); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 105); + + // Example: C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Features.ts (45,18): error + line = toOSPath('C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/lib/something/Features.ts (45,18): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 106); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 106); + + line = toOSPath('C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/lib/something/Features.ts (45,18): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 106); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 106); + + // Example: C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Features.ts(45): error + line = toOSPath('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\lib\\something\\Features.ts(45): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 102); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 102); + + // Example: C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Features.ts (45,18): error + line = toOSPath('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\lib\\something\\Features.ts (45): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 103); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 103); + + // Example: C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Features.ts(45,18): error + line = toOSPath('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\lib\\something\\Features.ts(45,18): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 105); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 105); + + line = toOSPath('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\lib\\something\\Features.ts(45,18): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 105); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 105); + + // Example: C:/Users/someone/AppData/Local/Temp/_monacodata_9888/workspaces/mankala/Features.ts (45,18): error + line = toOSPath('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\lib\\something\\Features.ts (45,18): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 106); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 106); + + line = toOSPath('C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\lib\\something\\Features.ts (45,18): error'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 106); + + result = OutputLinkComputer.detectLinks(line, 1, patternsBackSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/lib/something/Features.ts').toString() + '#45,18'); + assert.equal(result[0].range.startColumn, 1); + assert.equal(result[0].range.endColumn, 106); + + // Example: at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts. + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts. in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString()); + assert.equal(result[0].range.startColumn, 5); + assert.equal(result[0].range.endColumn, 84); + + // Example: at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + + // Example: at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game\\ + line = toOSPath(' at C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game\\ in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + + // Example: at "C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts" + line = toOSPath(' at "C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts" in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString()); + assert.equal(result[0].range.startColumn, 6); + assert.equal(result[0].range.endColumn, 85); + + // Example: at 'C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts' + line = toOSPath(' at \'C:\\Users\\someone\\AppData\\Local\\Temp\\_monacodata_9888\\workspaces\\mankala\\Game.ts\' in'); + result = OutputLinkComputer.detectLinks(line, 1, patternsSlash, contextService); + assert.equal(result.length, 1); + assert.equal(result[0].url, contextService.toResource('/Game.ts').toString()); + assert.equal(result[0].range.startColumn, 6); + assert.equal(result[0].range.endColumn, 85); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts new file mode 100644 index 0000000000..c4a5a6914a --- /dev/null +++ b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts @@ -0,0 +1,218 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import product from 'vs/platform/node/product'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ITimerService } from 'vs/workbench/services/timer/common/timerService'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ReportPerformanceIssueAction } from 'vs/workbench/electron-browser/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { join } from 'path'; +import { localize } from 'vs/nls'; +import { toPromise, filterEvent } from 'vs/base/common/event'; +import { platform, Platform } from 'vs/base/common/platform'; +import { readdir } from 'vs/base/node/pfs'; +import { release } from 'os'; +import { stopProfiling } from 'vs/base/node/profiler'; +import { virtualMachineHint } from 'vs/base/node/id'; + +class ProfilingHint implements IWorkbenchContribution { + + // p95 to p95 by os&release + static readonly _percentiles: { [key: string]: [number, number] } = { + ['Windows_6.3.9600']: [35782, 35782], + ['Windows_6.1.7601']: [11160, 18366], + ['Windows_10.0.16199']: [10423, 17222], + ['Windows_10.0.16193']: [7503, 11033], + ['Windows_10.0.16188']: [8544, 8807], + ['Windows_10.0.15063']: [11085, 16837], + ['Windows_10.0.14393']: [12585, 32662], + ['Windows_10.0.10586']: [7047, 10944], + ['Windows_10.0.10240']: [16176, 16176], + ['Mac_16.7.0']: [2192, 4050], + ['Mac_16.6.0']: [8043, 10608], + ['Mac_16.5.0']: [4912, 11348], + ['Mac_16.4.0']: [3900, 4200], + ['Mac_16.3.0']: [7327, 7327], + ['Mac_16.1.0']: [6090, 6555], + ['Mac_16.0.0']: [32574, 32574], + ['Mac_15.6.0']: [16082, 17469], + ['Linux_4.9.0-3-amd64']: [2092, 2197], + ['Linux_4.9.0-2-amd64']: [9779, 9779], + ['Linux_4.8.0-52-generic']: [12803, 13257], + ['Linux_4.8.0-51-generic']: [2670, 2797], + ['Linux_4.8.0-040800-generic']: [3954, 3954], + ['Linux_4.4.0-78-generic']: [4218, 5891], + ['Linux_4.4.0-77-generic']: [6166, 6166], + ['Linux_4.11.2']: [1323, 1323], + ['Linux_4.10.15-200.fc25.x86_64']: [9270, 9480], + ['Linux_4.10.13-1-ARCH']: [7116, 8511], + ['Linux_4.10.11-100.fc24.x86_64']: [1845, 1845], + ['Linux_4.10.0-21-generic']: [14805, 16050], + ['Linux_3.19.0-84-generic']: [4840, 4840], + ['Linux_3.11.10-29-desktop']: [1637, 2891], + }; + + private static readonly _myPercentiles = ProfilingHint._percentiles[`${Platform[platform]}_${release()}`]; + + constructor( + @IWindowsService private readonly _windowsService: IWindowsService, + @ITimerService private readonly _timerService: ITimerService, + @IMessageService private readonly _messageService: IMessageService, + @IEnvironmentService private readonly _envService: IEnvironmentService, + @IStorageService private readonly _storageService: IStorageService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + ) { + + setTimeout(() => this._checkTimersAndSuggestToProfile(), 5000); + } + + getId(): string { + return 'performance.ProfilingHint'; + } + + private _checkTimersAndSuggestToProfile() { + + // Only initial startups, not when already profiling + if (!this._timerService.isInitialStartup || this._envService.args['prof-startup']) { + return; + } + + // Check that we have some data about this + // OS version to which we can compare this startup. + // Then only go for startups between the 90 and + // 95th percentile. + if (!Array.isArray(ProfilingHint._myPercentiles)) { + return; + } + const [p80, p90] = ProfilingHint._myPercentiles; + const { ellapsed } = this._timerService.startupMetrics; + if (ellapsed < p80 || ellapsed > p90) { + return; + } + + // Ignore virtual machines and only ask users + // to profile with a certain propability + if (virtualMachineHint.value() >= .5 || Math.ceil(Math.random() * 1000) !== 1) { + return; + } + + // Don't ask for the stable version, only + // ask once per version/build + if (this._envService.appQuality === 'stable') { + // don't ask in stable + return; + } + const mementoKey = `performance.didPromptToProfile.${product.commit}`; + const value = this._storageService.get(mementoKey, StorageScope.GLOBAL, undefined); + if (value !== undefined) { + // only ask once per version + return; + } + + const profile = this._messageService.confirm({ + type: 'info', + message: localize('slow', "Slow startup detected"), + detail: localize('slow.detail', "Sorry that you just had a slow startup. Please restart '{0}' with profiling enabled, share the profiles with us, and we will work hard to make startup great again.", this._envService.appNameLong), + primaryButton: 'Restart and profile' + }); + + this._telemetryService.publicLog('profileStartupInvite', { + acceptedInvite: profile + }); + + if (profile) { + this._storageService.store(mementoKey, 'didProfile', StorageScope.GLOBAL); + this._windowsService.relaunch({ addArgs: ['--prof-startup'] }); + } else { + this._storageService.store(mementoKey, 'didReject', StorageScope.GLOBAL); + } + } +} + +class StartupProfiler implements IWorkbenchContribution { + + constructor( + @IWindowsService private readonly _windowsService: IWindowsService, + @IMessageService private readonly _messageService: IMessageService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ILifecycleService lifecycleService: ILifecycleService, + @IExtensionService extensionService: IExtensionService, + ) { + // wait for everything to be ready + TPromise.join([ + extensionService.onReady(), + toPromise(filterEvent(lifecycleService.onDidChangePhase, phase => phase === LifecyclePhase.Running)), + ]).then(() => { + this._stopProfiling(); + }); + } + + getId(): string { + return 'performance.StartupProfiler'; + } + + private _stopProfiling(): void { + + const { profileStartup } = this._environmentService; + if (!profileStartup) { + return; + } + + stopProfiling(profileStartup.dir, profileStartup.prefix).then(() => { + readdir(profileStartup.dir).then(files => { + return files.filter(value => value.indexOf(profileStartup.prefix) === 0); + }).then(files => { + const profileFiles = files.reduce((prev, cur) => `${prev}${join(profileStartup.dir, cur)}\n`, '\n'); + + const primaryButton = this._messageService.confirm({ + type: 'info', + message: localize('prof.message', "Successfully created profiles."), + detail: localize('prof.detail', "Please create an issue and manually attach the following files:\n{0}", profileFiles), + primaryButton: localize('prof.restartAndFileIssue', "Create Issue and Restart"), + secondaryButton: localize('prof.restart', "Restart") + }); + + if (primaryButton) { + const action = this._instantiationService.createInstance(ReportPerformanceIssueAction, ReportPerformanceIssueAction.ID, ReportPerformanceIssueAction.LABEL); + TPromise.join([ + this._windowsService.showItemInFolder(join(profileStartup.dir, files[0])), + action.run(`:warning: Make sure to **attach** these files from your *home*-directory: :warning:\n${files.map(file => `-\`${file}\``).join('\n')}`) + ]).then(() => { + // keep window stable until restart is selected + this._messageService.confirm({ + type: 'info', + message: localize('prof.thanks', "Thanks for helping us."), + detail: localize('prof.detail.restart', "A final restart is required to continue to use '{0}'. Again, thank you for your contribution.", this._environmentService.appNameLong), + primaryButton: localize('prof.restart', "Restart"), + secondaryButton: null + }); + // now we are ready to restart + this._windowsService.relaunch({ removeArgs: ['--prof-startup'] }); + }); + + } else { + // simply restart + this._windowsService.relaunch({ removeArgs: ['--prof-startup'] }); + } + }); + }); + } +} + +const registry = Registry.as(Extensions.Workbench); +registry.registerWorkbenchContribution(ProfilingHint); +registry.registerWorkbenchContribution(StartupProfiler); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts b/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts new file mode 100644 index 0000000000..294e8f593c --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts @@ -0,0 +1,298 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/keybindings'; +import * as nls from 'vs/nls'; +import { OS } from 'vs/base/common/platform'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Disposable } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; +import * as dom from 'vs/base/browser/dom'; +import { InputBox, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { Dimension } from 'vs/base/browser/builder'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { ScrollType } from 'vs/editor/common/editorCommon'; + +class KeybindingInputWidget extends Widget { + + private readonly inputBox: InputBox; + + private _acceptChords: boolean; + private _firstPart: ResolvedKeybinding; + private _chordPart: ResolvedKeybinding; + private _inputValue: string; + + private _onKeybinding = this._register(new Emitter<[ResolvedKeybinding, ResolvedKeybinding]>()); + public readonly onKeybinding: Event<[ResolvedKeybinding, ResolvedKeybinding]> = this._onKeybinding.event; + + private _onEnter = this._register(new Emitter()); + public readonly onEnter: Event = this._onEnter.event; + + private _onEscape = this._register(new Emitter()); + public readonly onEscape: Event = this._onEscape.event; + + private _onBlur = this._register(new Emitter()); + public readonly onBlur: Event = this._onBlur.event; + + constructor(parent: HTMLElement, private options: IInputOptions, + @IContextViewService private contextViewService: IContextViewService, + @IKeybindingService private keybindingService: IKeybindingService, + @IThemeService themeService: IThemeService + ) { + super(); + this.inputBox = this._register(new InputBox(parent, this.contextViewService, this.options)); + this._register(attachInputBoxStyler(this.inputBox, themeService)); + this.onkeydown(this.inputBox.inputElement, e => this._onKeyDown(e)); + this.onblur(this.inputBox.inputElement, (e) => this._onBlur.fire()); + + this.oninput(this.inputBox.inputElement, (e) => { + // Prevent other characters from showing up + this.setInputValue(this._inputValue); + }); + + this._acceptChords = true; + this._firstPart = null; + this._chordPart = null; + } + + public setInputValue(value: string): void { + this._inputValue = value; + this.inputBox.value = this._inputValue; + } + + public focus(): void { + this.inputBox.focus(); + } + + public reset() { + this._firstPart = null; + this._chordPart = null; + } + + public setAcceptChords(acceptChords: boolean) { + this._acceptChords = acceptChords; + this._chordPart = null; + } + + private _onKeyDown(keyboardEvent: IKeyboardEvent): void { + keyboardEvent.preventDefault(); + keyboardEvent.stopPropagation(); + if (keyboardEvent.equals(KeyCode.Enter)) { + this._onEnter.fire(); + return; + } + if (keyboardEvent.equals(KeyCode.Escape)) { + this._onEscape.fire(); + return; + } + this.printKeybinding(keyboardEvent); + } + + private printKeybinding(keyboardEvent: IKeyboardEvent): void { + const keybinding = this.keybindingService.resolveKeyboardEvent(keyboardEvent); + const info = `code: ${keyboardEvent.browserEvent.code}, keyCode: ${keyboardEvent.browserEvent.keyCode}, key: ${keyboardEvent.browserEvent.key} => UI: ${keybinding.getAriaLabel()}, user settings: ${keybinding.getUserSettingsLabel()}, dispatch: ${keybinding.getDispatchParts()[0]}`; + + if (this._acceptChords) { + const hasFirstPart = (this._firstPart && this._firstPart.getDispatchParts()[0] !== null); + const hasChordPart = (this._chordPart && this._chordPart.getDispatchParts()[0] !== null); + if (hasFirstPart && hasChordPart) { + // Reset + this._firstPart = keybinding; + this._chordPart = null; + } else if (!hasFirstPart) { + this._firstPart = keybinding; + } else { + this._chordPart = keybinding; + } + } else { + this._firstPart = keybinding; + } + + let value = ''; + if (this._firstPart) { + value = this._firstPart.getUserSettingsLabel(); + } + if (this._chordPart) { + value = value + ' ' + this._chordPart.getUserSettingsLabel(); + } + this.setInputValue(value); + + this.inputBox.inputElement.title = info; + this._onKeybinding.fire([this._firstPart, this._chordPart]); + } +} + +export class DefineKeybindingWidget extends Widget { + + private static WIDTH = 400; + private static HEIGHT = 90; + + private _domNode: FastDomNode; + private _keybindingInputWidget: KeybindingInputWidget; + private _outputNode: HTMLElement; + + private _firstPart: ResolvedKeybinding = null; + private _chordPart: ResolvedKeybinding = null; + private _isVisible: boolean = false; + + private _onHide = this._register(new Emitter()); + + constructor( + parent: HTMLElement, + @IKeybindingService private keybindingService: IKeybindingService, + @IInstantiationService private instantiationService: IInstantiationService, + @IThemeService private themeService: IThemeService + ) { + super(); + this.create(); + if (parent) { + dom.append(parent, this._domNode.domNode); + } + } + + get domNode(): HTMLElement { + return this._domNode.domNode; + } + + define(): TPromise { + this._keybindingInputWidget.reset(); + return new TPromise((c, e) => { + if (!this._isVisible) { + this._isVisible = true; + this._domNode.setDisplay('block'); + + this._firstPart = null; + this._chordPart = null; + this._keybindingInputWidget.setInputValue(''); + dom.clearNode(this._outputNode); + this._keybindingInputWidget.focus(); + } + const disposable = this._onHide.event(() => { + if (this._firstPart) { + let r = this._firstPart.getUserSettingsLabel(); + if (this._chordPart) { + r = r + ' ' + this._chordPart.getUserSettingsLabel(); + } + c(r); + } else { + c(null); + } + disposable.dispose(); + }); + }); + } + + layout(layout: Dimension): void { + let top = Math.round((layout.height - DefineKeybindingWidget.HEIGHT) / 2); + this._domNode.setTop(top); + + let left = Math.round((layout.width - DefineKeybindingWidget.WIDTH) / 2); + this._domNode.setLeft(left); + } + + private create(): void { + this._domNode = createFastDomNode(document.createElement('div')); + this._domNode.setDisplay('none'); + this._domNode.setClassName('defineKeybindingWidget'); + this._domNode.setWidth(DefineKeybindingWidget.WIDTH); + this._domNode.setHeight(DefineKeybindingWidget.HEIGHT); + dom.append(this._domNode.domNode, dom.$('.message', null, nls.localize('defineKeybinding.initial', "Press desired key combination and ENTER. ESCAPE to cancel."))); + + this._register(attachStylerCallback(this.themeService, { editorWidgetBackground, widgetShadow }, colors => { + this._domNode.domNode.style.backgroundColor = colors.editorWidgetBackground; + + if (colors.widgetShadow) { + this._domNode.domNode.style.boxShadow = `0 2px 8px ${colors.widgetShadow}`; + } else { + this._domNode.domNode.style.boxShadow = null; + } + })); + + this._keybindingInputWidget = this._register(this.instantiationService.createInstance(KeybindingInputWidget, this._domNode.domNode, {})); + this._register(this._keybindingInputWidget.onKeybinding(keybinding => this.printKeybinding(keybinding))); + this._register(this._keybindingInputWidget.onEnter(() => this.hide())); + this._register(this._keybindingInputWidget.onEscape(() => this.onCancel())); + this._register(this._keybindingInputWidget.onBlur(() => this.onCancel())); + + this._outputNode = dom.append(this._domNode.domNode, dom.$('.output')); + } + + private printKeybinding(keybinding: [ResolvedKeybinding, ResolvedKeybinding]): void { + const [firstPart, chordPart] = keybinding; + this._firstPart = firstPart; + this._chordPart = chordPart; + dom.clearNode(this._outputNode); + new KeybindingLabel(this._outputNode, OS).set(this._firstPart, null); + if (this._chordPart) { + this._outputNode.appendChild(document.createTextNode(nls.localize('defineKeybinding.chordsTo', "chord to"))); + new KeybindingLabel(this._outputNode, OS).set(this._chordPart, null); + } + } + + private onCancel(): void { + this._firstPart = null; + this._chordPart = null; + this.hide(); + } + + private hide(): void { + this._domNode.setDisplay('none'); + this._isVisible = false; + this._onHide.fire(); + } +} + +export class DefineKeybindingOverlayWidget extends Disposable implements IOverlayWidget { + + private static ID = 'editor.contrib.defineKeybindingWidget'; + + private readonly _widget: DefineKeybindingWidget; + + constructor(private _editor: ICodeEditor, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(); + + this._widget = instantiationService.createInstance(DefineKeybindingWidget, null); + this._editor.addOverlayWidget(this); + } + + public getId(): string { + return DefineKeybindingOverlayWidget.ID; + } + + public getDomNode(): HTMLElement { + return this._widget.domNode; + } + + public getPosition(): IOverlayWidgetPosition { + return { + preference: null + }; + } + + public dispose(): void { + this._editor.removeOverlayWidget(this); + super.dispose(); + } + + public start(): TPromise { + this._editor.revealPositionInCenterIfOutsideViewport(this._editor.getPosition(), ScrollType.Smooth); + const layoutInfo = this._editor.getLayoutInfo(); + this._widget.layout(new Dimension(layoutInfo.width, layoutInfo.height)); + return this._widget.define(); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts new file mode 100644 index 0000000000..bdf28df4d0 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -0,0 +1,808 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/keybindingsEditor'; +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Delayer } from 'vs/base/common/async'; +import * as DOM from 'vs/base/browser/dom'; +import { OS } from 'vs/base/common/platform'; +import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; +import { Builder, Dimension } from 'vs/base/browser/builder'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; +import { IAction } from 'vs/base/common/actions'; +import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { KeybindingsEditorModel, IKeybindingItemEntry, IListEntry, KEYBINDING_ENTRY_TEMPLATE_ID, KEYBINDING_HEADER_TEMPLATE_ID } from 'vs/workbench/parts/preferences/common/keybindingsEditorModel'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService, IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; +import { SearchWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { DefineKeybindingWidget } from 'vs/workbench/parts/preferences/browser/keybindingWidgets'; +import { + IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_COPY, + KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS +} from 'vs/workbench/parts/preferences/common/preferences'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { IDelegate, IRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; +import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IChoiceService, IMessageService, Severity } from 'vs/platform/message/common/message'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { listHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; + +let $ = DOM.$; + +export class KeybindingsEditorInput extends EditorInput { + + public static ID: string = 'workbench.input.keybindings'; + public readonly keybindingsModel: KeybindingsEditorModel; + + constructor( @IInstantiationService private instantiationService: IInstantiationService) { + super(); + this.keybindingsModel = instantiationService.createInstance(KeybindingsEditorModel, OS); + } + + getTypeId(): string { + return KeybindingsEditorInput.ID; + } + + getName(): string { + return localize('keybindingsInputName', "Keyboard Shortcuts"); + } + + resolve(refresh?: boolean): TPromise { + return TPromise.as(this.keybindingsModel); + } + + matches(otherInput: any): boolean { + return otherInput instanceof KeybindingsEditorInput; + } +} + +export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor { + + public static ID: string = 'workbench.editor.keybindings'; + + private keybindingsEditorModel: KeybindingsEditorModel; + + private headerContainer: HTMLElement; + private searchWidget: SearchWidget; + + private overlayContainer: HTMLElement; + private defineKeybindingWidget: DefineKeybindingWidget; + + private keybindingsListContainer: HTMLElement; + private unAssignedKeybindingItemToRevealAndFocus: IKeybindingItemEntry; + private listEntries: IListEntry[]; + private keybindingsList: List; + + private dimension: Dimension; + private delayedFiltering: Delayer; + private latestEmptyFilters: string[] = []; + private delayedFilterLogging: Delayer; + private keybindingsEditorContextKey: IContextKey; + private keybindingFocusContextKey: IContextKey; + private searchFocusContextKey: IContextKey; + private sortByPrecedence: Checkbox; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IKeybindingService private keybindingsService: IKeybindingService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IPreferencesService private preferencesService: IPreferencesService, + @IKeybindingEditingService private keybindingEditingService: IKeybindingEditingService, + @IListService private listService: IListService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IChoiceService private choiceService: IChoiceService, + @IMessageService private messageService: IMessageService, + @IClipboardService private clipboardService: IClipboardService, + @IInstantiationService private instantiationService: IInstantiationService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(KeybindingsEditor.ID, telemetryService, themeService); + this.delayedFiltering = new Delayer(300); + this._register(keybindingsService.onDidUpdateKeybindings(() => this.render())); + + this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService); + this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService); + this.keybindingFocusContextKey = CONTEXT_KEYBINDING_FOCUS.bindTo(this.contextKeyService); + this.delayedFilterLogging = new Delayer(1000); + } + + createEditor(parent: Builder): void { + const parentElement = parent.getHTMLElement(); + + const keybindingsEditorElement = DOM.append(parentElement, $('div', { class: 'keybindings-editor' })); + + this.createOverlayContainer(keybindingsEditorElement); + this.createHeader(keybindingsEditorElement); + this.createBody(keybindingsEditorElement); + + const focusTracker = this._register(DOM.trackFocus(parentElement)); + this._register(focusTracker.addFocusListener(() => this.keybindingsEditorContextKey.set(true))); + this._register(focusTracker.addBlurListener(() => this.keybindingsEditorContextKey.reset())); + } + + setInput(input: KeybindingsEditorInput): TPromise { + const oldInput = this.input; + return super.setInput(input) + .then(() => { + if (!input.matches(oldInput)) { + this.render(); + } + }); + } + + clearInput(): void { + super.clearInput(); + this.searchWidget.clear(); + this.keybindingsEditorContextKey.reset(); + this.keybindingFocusContextKey.reset(); + } + + layout(dimension: Dimension): void { + this.dimension = dimension; + this.searchWidget.layout(dimension); + + this.overlayContainer.style.width = dimension.width + 'px'; + this.overlayContainer.style.height = dimension.height + 'px'; + this.defineKeybindingWidget.layout(this.dimension); + + this.layoutKebindingsList(); + } + + focus(): void { + const activeKeybindingEntry = this.activeKeybindingEntry; + if (activeKeybindingEntry) { + this.selectEntry(activeKeybindingEntry); + } else { + this.searchWidget.focus(); + } + } + + get activeKeybindingEntry(): IKeybindingItemEntry { + const focusedElement = this.keybindingsList.getFocusedElements()[0]; + return focusedElement && focusedElement.templateId === KEYBINDING_ENTRY_TEMPLATE_ID ? focusedElement : null; + } + + defineKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise { + this.selectEntry(keybindingEntry); + this.showOverlayContainer(); + return this.defineKeybindingWidget.define().then(key => { + this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_DEFINE, keybindingEntry.keybindingItem.command, key); + if (key) { + return this.keybindingEditingService.editKeybinding(key, keybindingEntry.keybindingItem.keybindingItem) + .then(() => { + if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering + this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry; + } + }); + } + return null; + }).then(() => { + this.hideOverlayContainer(); + this.selectEntry(keybindingEntry); + }, error => { + this.hideOverlayContainer(); + this.onKeybindingEditingError(error); + this.selectEntry(keybindingEntry); + return error; + }); + } + + removeKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise { + this.selectEntry(keybindingEntry); + if (keybindingEntry.keybindingItem.keybinding) { // This should be a pre-condition + this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_REMOVE, keybindingEntry.keybindingItem.command, keybindingEntry.keybindingItem.keybinding); + return this.keybindingEditingService.removeKeybinding(keybindingEntry.keybindingItem.keybindingItem) + .then(() => this.focus(), + error => { + this.onKeybindingEditingError(error); + this.selectEntry(keybindingEntry); + }); + } + return TPromise.as(null); + } + + resetKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise { + this.selectEntry(keybindingEntry); + this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_RESET, keybindingEntry.keybindingItem.command, keybindingEntry.keybindingItem.keybinding); + return this.keybindingEditingService.resetKeybinding(keybindingEntry.keybindingItem.keybindingItem) + .then(() => { + if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering + this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry; + } + this.selectEntry(keybindingEntry); + }, + error => { + this.onKeybindingEditingError(error); + this.selectEntry(keybindingEntry); + }); + } + + copyKeybinding(keybinding: IKeybindingItemEntry): TPromise { + this.selectEntry(keybinding); + this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_COPY, keybinding.keybindingItem.command, keybinding.keybindingItem.keybinding); + const userFriendlyKeybinding: IUserFriendlyKeybinding = { + command: keybinding.keybindingItem.command, + key: keybinding.keybindingItem.keybinding ? keybinding.keybindingItem.keybinding.getUserSettingsLabel() : '' + }; + if (keybinding.keybindingItem.when) { + userFriendlyKeybinding.when = keybinding.keybindingItem.when; + } + this.clipboardService.writeText(JSON.stringify(userFriendlyKeybinding, null, ' ')); + return TPromise.as(null); + } + + search(filter: string): void { + this.searchWidget.focus(); + } + + showConflicts(keybindingEntry: IKeybindingItemEntry): TPromise { + const value = `"${keybindingEntry.keybindingItem.keybinding.getAriaLabel()}"`; + if (value !== this.searchWidget.getValue()) { + this.searchWidget.setValue(value); + } + return TPromise.as(null); + } + + private createOverlayContainer(parent: HTMLElement): void { + this.overlayContainer = DOM.append(parent, $('.overlay-container')); + this.overlayContainer.style.position = 'absolute'; + this.overlayContainer.style.zIndex = '10'; + this.defineKeybindingWidget = this._register(this.instantiationService.createInstance(DefineKeybindingWidget, this.overlayContainer)); + this.hideOverlayContainer(); + } + + private showOverlayContainer() { + this.overlayContainer.style.display = 'block'; + } + + private hideOverlayContainer() { + this.overlayContainer.style.display = 'none'; + } + + private createHeader(parent: HTMLElement): void { + this.headerContainer = DOM.append(parent, $('.keybindings-header')); + + const searchContainer = DOM.append(this.headerContainer, $('.search-container')); + this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, searchContainer, { + ariaLabel: localize('SearchKeybindings.AriaLabel', "Search keybindings"), + placeholder: localize('SearchKeybindings.Placeholder', "Search keybindings"), + focusKey: this.searchFocusContextKey + })); + this._register(this.searchWidget.onDidChange(searchValue => this.delayedFiltering.trigger(() => this.filterKeybindings()))); + + this.sortByPrecedence = this._register(new Checkbox({ + actionClassName: 'sort-by-precedence', + isChecked: false, + onChange: () => this.renderKeybindingsEntries(false), + title: localize('sortByPrecedene', "Sort by Precedence") + })); + searchContainer.appendChild(this.sortByPrecedence.domNode); + + this.createOpenKeybindingsElement(this.headerContainer); + } + + private createOpenKeybindingsElement(parent: HTMLElement): void { + const openKeybindingsContainer = DOM.append(parent, $('.open-keybindings-container')); + DOM.append(openKeybindingsContainer, $('', null, localize('header-message', "For advanced customizations open and edit"))); + const fileElement = DOM.append(openKeybindingsContainer, $('.file-name', null, localize('keybindings-file-name', "keybindings.json"))); + fileElement.tabIndex = 0; + + this._register(DOM.addDisposableListener(fileElement, DOM.EventType.CLICK, () => this.preferencesService.openGlobalKeybindingSettings(true))); + this._register(DOM.addDisposableListener(fileElement, DOM.EventType.KEY_UP, e => { + let keyboardEvent = new StandardKeyboardEvent(e); + switch (keyboardEvent.keyCode) { + case KeyCode.Enter: + this.preferencesService.openGlobalKeybindingSettings(true); + keyboardEvent.preventDefault(); + keyboardEvent.stopPropagation(); + return; + } + })); + } + + private createBody(parent: HTMLElement): void { + const bodyContainer = DOM.append(parent, $('.keybindings-body')); + this.createList(bodyContainer); + } + + private createList(parent: HTMLElement): void { + this.keybindingsListContainer = DOM.append(parent, $('.keybindings-list-container')); + + this.keybindingsList = this._register(new List(this.keybindingsListContainer, new Delegate(), [new KeybindingHeaderRenderer(), new KeybindingItemRenderer(this, this.keybindingsService)], + { identityProvider: e => e.id, keyboardSupport: false, mouseSupport: true, ariaLabel: localize('keybindingsLabel', "Keybindings") })); + this._register(this.keybindingsList.onContextMenu(e => this.onContextMenu(e))); + this._register(this.keybindingsList.onFocusChange(e => this.onFocusChange(e))); + this._register(this.keybindingsList.onDOMFocus(() => { + DOM.addClass(this.keybindingsList.getHTMLElement(), 'focused'); + })); + this._register(this.keybindingsList.onDOMBlur(() => { + DOM.removeClass(this.keybindingsList.getHTMLElement(), 'focused'); + this.keybindingFocusContextKey.reset(); + })); + + this._register(attachListStyler(this.keybindingsList, this.themeService)); + this._register(this.listService.register(this.keybindingsList)); + } + + private render(): TPromise { + if (this.input) { + return this.input.resolve() + .then((keybindingsModel: KeybindingsEditorModel) => this.keybindingsEditorModel = keybindingsModel) + .then(() => this.keybindingsEditorModel.resolve()) + .then(() => this.renderKeybindingsEntries(false)); + } + return TPromise.as(null); + } + + private filterKeybindings(): void { + this.renderKeybindingsEntries(this.searchWidget.hasFocus()); + this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(this.searchWidget.getValue())); + } + + private renderKeybindingsEntries(reset: boolean): void { + if (this.keybindingsEditorModel) { + const filter = this.searchWidget.getValue(); + const keybindingsEntries: IKeybindingItemEntry[] = this.keybindingsEditorModel.fetch(filter, this.sortByPrecedence.checked); + if (keybindingsEntries.length === 0) { + this.latestEmptyFilters.push(filter); + } + const currentSelectedIndex = this.keybindingsList.getSelection()[0]; + this.listEntries = [{ id: 'keybinding-header-entry', templateId: KEYBINDING_HEADER_TEMPLATE_ID }, ...keybindingsEntries]; + this.keybindingsList.splice(0, this.keybindingsList.length, this.listEntries); + this.layoutKebindingsList(); + + if (reset) { + this.keybindingsList.setSelection([]); + this.keybindingsList.setFocus([]); + } else { + if (this.unAssignedKeybindingItemToRevealAndFocus) { + const index = this.getNewIndexOfUnassignedKeybinding(this.unAssignedKeybindingItemToRevealAndFocus); + if (index !== -1) { + this.keybindingsList.reveal(index, 0.2); + this.selectEntry(index); + } + this.unAssignedKeybindingItemToRevealAndFocus = null; + } else if (currentSelectedIndex !== -1 && currentSelectedIndex < this.listEntries.length) { + this.selectEntry(currentSelectedIndex); + } else if (this.editorService.getActiveEditor() === this) { + this.focus(); + } + } + } + } + + private layoutKebindingsList(): void { + const listHeight = this.dimension.height - (DOM.getDomNodePagePosition(this.headerContainer).height + 12 /*padding*/); + this.keybindingsListContainer.style.height = `${listHeight}px`; + this.keybindingsList.layout(listHeight); + } + + private getIndexOf(listEntry: IListEntry): number { + const index = this.listEntries.indexOf(listEntry); + if (index === -1) { + for (let i = 0; i < this.listEntries.length; i++) { + if (this.listEntries[i].id === listEntry.id) { + return i; + } + } + } + return index; + } + + private getNewIndexOfUnassignedKeybinding(unassignedKeybinding: IKeybindingItemEntry): number { + for (let index = 0; index < this.listEntries.length; index++) { + const entry = this.listEntries[index]; + if (entry.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) { + const keybindingItemEntry = (entry); + if (keybindingItemEntry.keybindingItem.command === unassignedKeybinding.keybindingItem.command) { + return index; + } + } + } + return -1; + } + + private selectEntry(keybindingItemEntry: IKeybindingItemEntry | number): void { + const index = typeof keybindingItemEntry === 'number' ? keybindingItemEntry : this.getIndexOf(keybindingItemEntry); + if (index !== -1) { + this.keybindingsList.getHTMLElement().focus(); + this.keybindingsList.setFocus([index]); + this.keybindingsList.setSelection([index]); + } + } + + focusKeybindings(): void { + this.keybindingsList.getHTMLElement().focus(); + const currentFocusIndices = this.keybindingsList.getFocus(); + this.keybindingsList.setFocus([currentFocusIndices.length ? currentFocusIndices[0] : 0]); + } + + private onContextMenu(e: IListContextMenuEvent): void { + if (e.element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) { + this.selectEntry(e.element); + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => TPromise.as([ + this.createCopyAction(e.element), + new Separator(), + this.createDefineAction(e.element), + this.createRemoveAction(e.element), + this.createResetAction(e.element), + new Separator(), + this.createShowConflictsAction(e.element)]) + }); + } + } + + private onFocusChange(e: IListEvent): void { + this.keybindingFocusContextKey.reset(); + const element = e.elements[0]; + if (!element) { + return; + } + if (element.templateId === KEYBINDING_HEADER_TEMPLATE_ID) { + this.keybindingsList.focusNext(); + return; + } + if (element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) { + this.keybindingFocusContextKey.set(true); + } + } + + private createDefineAction(keybindingItemEntry: IKeybindingItemEntry): IAction { + return { + label: keybindingItemEntry.keybindingItem.keybinding ? localize('changeLabel', "Change Keybinding") : localize('addLabel', "Add Keybinding"), + enabled: true, + id: KEYBINDINGS_EDITOR_COMMAND_DEFINE, + run: () => this.defineKeybinding(keybindingItemEntry) + }; + } + + private createRemoveAction(keybindingItem: IKeybindingItemEntry): IAction { + return { + label: localize('removeLabel', "Remove Keybinding"), + enabled: !!keybindingItem.keybindingItem.keybinding, + id: KEYBINDINGS_EDITOR_COMMAND_REMOVE, + run: () => this.removeKeybinding(keybindingItem) + }; + } + + private createResetAction(keybindingItem: IKeybindingItemEntry): IAction { + return { + label: localize('resetLabel', "Reset Keybinding"), + enabled: !keybindingItem.keybindingItem.keybindingItem.isDefault, + id: KEYBINDINGS_EDITOR_COMMAND_RESET, + run: () => this.resetKeybinding(keybindingItem) + }; + } + + private createShowConflictsAction(keybindingItem: IKeybindingItemEntry): IAction { + return { + label: localize('showConflictsLabel', "Show Conflicts"), + enabled: !!keybindingItem.keybindingItem.keybinding, + id: KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS, + run: () => this.showConflicts(keybindingItem) + }; + } + + private createCopyAction(keybindingItem: IKeybindingItemEntry): IAction { + return { + label: localize('copyLabel', "Copy"), + enabled: true, + id: KEYBINDINGS_EDITOR_COMMAND_COPY, + run: () => this.copyKeybinding(keybindingItem) + }; + } + + private reportFilteringUsed(filter: string): void { + if (filter) { + let data = { + filter, + emptyFilters: this.getLatestEmptyFiltersForTelemetry() + }; + this.latestEmptyFilters = []; + this.telemetryService.publicLog('keybindings.filter', data); + } + } + + /** + * Put a rough limit on the size of the telemetry data, since otherwise it could be an unbounded large amount + * of data. 8192 is the max size of a property value. This is rough since that probably includes ""s, etc. + */ + private getLatestEmptyFiltersForTelemetry(): string[] { + let cumulativeSize = 0; + return this.latestEmptyFilters.filter(filterText => (cumulativeSize += filterText.length) <= 8192); + } + + private reportKeybindingAction(action: string, command: string, keybinding: ResolvedKeybinding | string): void { + this.telemetryService.publicLog(action, { command, keybinding: keybinding ? (typeof keybinding === 'string' ? keybinding : keybinding.getUserSettingsLabel()) : '' }); + } + + private onKeybindingEditingError(error: any): void { + this.messageService.show(Severity.Error, typeof error === 'string' ? error : localize('error', "Error '{0}' while editing keybinding. Please open 'keybindings.json' file and check.", `${error}`)); + } +} + +class Delegate implements IDelegate { + + getHeight(element: IListEntry) { + if (element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) { + const commandIdMatched = (element).keybindingItem.commandLabel && (element).commandIdMatches; + const commandDefaultLabelMatched = !!(element).commandDefaultLabelMatches; + if (commandIdMatched && commandDefaultLabelMatched) { + return 60; + } + if (commandIdMatched || commandDefaultLabelMatched) { + return 40; + } + } + if (element.templateId === KEYBINDING_HEADER_TEMPLATE_ID) { + return 30; + } + return 24; + } + + getTemplateId(element: IListEntry) { + return element.templateId; + } +} + +interface KeybindingItemTemplate { + parent: HTMLElement; + actions: ActionsColumn; + command: CommandColumn; + keybinding: KeybindingColumn; + source: SourceColumn; + when: WhenColumn; +} + +class KeybindingHeaderRenderer implements IRenderer { + + get templateId(): string { return KEYBINDING_HEADER_TEMPLATE_ID; } + + constructor() { } + + renderTemplate(container: HTMLElement): any { + DOM.addClass(container, 'keybindings-list-header'); + DOM.append(container, + $('.header.actions'), + $('.header.command', null, localize('command', "Command")), + $('.header.keybinding', null, localize('keybinding', "Keybinding")), + $('.header.source', null, localize('source', "Source")), + $('.header.when', null, localize('when', "When"))); + return {}; + } + + renderElement(entry: IListEntry, index: number, template: any): void { + } + + disposeTemplate(template: any): void { + } +} + +class KeybindingItemRenderer implements IRenderer { + + get templateId(): string { return KEYBINDING_ENTRY_TEMPLATE_ID; } + + constructor(private keybindingsEditor: IKeybindingsEditor, private keybindingsService: IKeybindingService) { } + + renderTemplate(container: HTMLElement): KeybindingItemTemplate { + DOM.addClass(container, 'keybinding-item'); + const actions = new ActionsColumn(container, this.keybindingsEditor, this.keybindingsService); + const command = new CommandColumn(container, this.keybindingsEditor); + const keybinding = new KeybindingColumn(container, this.keybindingsEditor); + const source = new SourceColumn(container, this.keybindingsEditor); + const when = new WhenColumn(container, this.keybindingsEditor); + container.setAttribute('aria-labelledby', [command.id, keybinding.id, source.id, when.id].join(' ')); + return { + parent: container, + actions, + command, + keybinding, + source, + when + }; + } + + renderElement(keybindingEntry: IKeybindingItemEntry, index: number, template: KeybindingItemTemplate): void { + DOM.toggleClass(template.parent, 'even', index % 2 === 0); + template.actions.render(keybindingEntry); + template.command.render(keybindingEntry); + template.keybinding.render(keybindingEntry); + template.source.render(keybindingEntry); + template.when.render(keybindingEntry); + } + + disposeTemplate(template: KeybindingItemTemplate): void { + } +} + +abstract class Column { + + static COUNTER = 0; + + protected element: HTMLElement; + readonly id: string; + + constructor(protected parent: HTMLElement, protected keybindingsEditor: IKeybindingsEditor) { + this.element = this.create(parent); + this.id = this.element.getAttribute('id'); + } + + abstract create(parent: HTMLElement): HTMLElement; +} + +class ActionsColumn extends Column { + + private actionBar: ActionBar; + + constructor(parent: HTMLElement, keybindingsEditor: IKeybindingsEditor, private keybindingsService: IKeybindingService) { + super(parent, keybindingsEditor); + } + + create(parent: HTMLElement): HTMLElement { + const actionsContainer = DOM.append(parent, $('.column.actions', { id: 'actions_' + ++Column.COUNTER })); + this.actionBar = new ActionBar(actionsContainer, { animated: false }); + return actionsContainer; + } + + render(keybindingItemEntry: IKeybindingItemEntry): void { + this.actionBar.clear(); + const actions = []; + if (keybindingItemEntry.keybindingItem.keybinding) { + actions.push(this.createEditAction(keybindingItemEntry)); + } else { + actions.push(this.createAddAction(keybindingItemEntry)); + } + this.actionBar.push(actions, { icon: true }); + } + + private createEditAction(keybindingItemEntry: IKeybindingItemEntry): IAction { + const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE); + return { + class: 'edit', + enabled: true, + id: 'editKeybinding', + tooltip: keybinding ? localize('editKeybindingLabelWithKey', "Change Keybinding {0}", `(${keybinding.getLabel()})`) : localize('editKeybindingLabel', "Change Keybinding"), + run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry) + }; + } + + private createAddAction(keybindingItemEntry: IKeybindingItemEntry): IAction { + const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE); + return { + class: 'add', + enabled: true, + id: 'addKeybinding', + tooltip: keybinding ? localize('addKeybindingLabelWithKey', "Add Keybinding {0}", `(${keybinding.getLabel()})`) : localize('addKeybindingLabel', "Add Keybinding"), + run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry) + }; + } +} + +class CommandColumn extends Column { + + private commandColumn: HTMLElement; + + create(parent: HTMLElement): HTMLElement { + this.commandColumn = DOM.append(parent, $('.column.command', { id: 'command_' + ++Column.COUNTER })); + return this.commandColumn; + } + + render(keybindingItemEntry: IKeybindingItemEntry): void { + DOM.clearNode(this.commandColumn); + const keybindingItem = keybindingItemEntry.keybindingItem; + const commandIdMatched = !!(keybindingItem.commandLabel && keybindingItemEntry.commandIdMatches); + const commandDefaultLabelMatched = !!keybindingItemEntry.commandDefaultLabelMatches; + DOM.toggleClass(this.commandColumn, 'vertical-align-column', commandIdMatched || commandDefaultLabelMatched); + this.commandColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); + if (keybindingItem.commandLabel) { + const commandLabel = new HighlightedLabel(this.commandColumn); + commandLabel.set(keybindingItem.commandLabel, keybindingItemEntry.commandLabelMatches); + commandLabel.element.title = keybindingItem.command; + } + if (keybindingItemEntry.commandDefaultLabelMatches) { + new HighlightedLabel(DOM.append(this.commandColumn, $('.command-default-label'))).set(keybindingItem.commandDefaultLabel, keybindingItemEntry.commandDefaultLabelMatches); + } + if (keybindingItemEntry.commandIdMatches || !keybindingItem.commandLabel) { + new HighlightedLabel(DOM.append(this.commandColumn, $('.code'))).set(keybindingItem.command, keybindingItemEntry.commandIdMatches); + } + } + + private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { + return localize('commandAriaLabel', "Command is {0}.", keybindingItemEntry.keybindingItem.commandLabel ? keybindingItemEntry.keybindingItem.commandLabel : keybindingItemEntry.keybindingItem.command); + } +} + +class KeybindingColumn extends Column { + + private keybindingColumn: HTMLElement; + + create(parent: HTMLElement): HTMLElement { + this.keybindingColumn = DOM.append(parent, $('.column.keybinding', { id: 'keybinding_' + ++Column.COUNTER })); + return this.keybindingColumn; + } + + render(keybindingItemEntry: IKeybindingItemEntry): void { + DOM.clearNode(this.keybindingColumn); + this.keybindingColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); + if (keybindingItemEntry.keybindingItem.keybinding) { + new KeybindingLabel(this.keybindingColumn, OS).set(keybindingItemEntry.keybindingItem.keybinding, keybindingItemEntry.keybindingMatches); + } + } + + private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { + return keybindingItemEntry.keybindingItem.keybinding ? localize('keybindingAriaLabel', "Keybinding is {0}.", keybindingItemEntry.keybindingItem.keybinding.getAriaLabel()) : localize('noKeybinding', "No Keybinding assigned."); + } +} + +class SourceColumn extends Column { + + private sourceColumn: HTMLElement; + + create(parent: HTMLElement): HTMLElement { + this.sourceColumn = DOM.append(parent, $('.column.source', { id: 'source_' + ++Column.COUNTER })); + return this.sourceColumn; + } + + render(keybindingItemEntry: IKeybindingItemEntry): void { + DOM.clearNode(this.sourceColumn); + this.sourceColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); + new HighlightedLabel(this.sourceColumn).set(keybindingItemEntry.keybindingItem.source, keybindingItemEntry.sourceMatches); + } + + private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { + return localize('sourceAriaLabel', "Source is {0}.", keybindingItemEntry.keybindingItem.source); + } +} + +class WhenColumn extends Column { + + private whenColumn: HTMLElement; + + create(parent: HTMLElement): HTMLElement { + const column = DOM.append(parent, $('.column.when')); + this.whenColumn = DOM.append(column, $('div', { id: 'when_' + ++Column.COUNTER })); + return this.whenColumn; + } + + render(keybindingItemEntry: IKeybindingItemEntry): void { + DOM.clearNode(this.whenColumn); + this.whenColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); + DOM.toggleClass(this.whenColumn, 'code', !!keybindingItemEntry.keybindingItem.when); + DOM.toggleClass(this.whenColumn, 'empty', !keybindingItemEntry.keybindingItem.when); + if (keybindingItemEntry.keybindingItem.when) { + new HighlightedLabel(this.whenColumn).set(keybindingItemEntry.keybindingItem.when, keybindingItemEntry.whenMatches); + this.whenColumn.title = keybindingItemEntry.keybindingItem.when; + } else { + this.whenColumn.textContent = '—'; + } + } + + private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { + return keybindingItemEntry.keybindingItem.when ? localize('whenAriaLabel', "When is {0}.", keybindingItemEntry.keybindingItem.when) : localize('noWhen', "No when context."); + } +} + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const listHighlightForegroundColor = theme.getColor(listHighlightForeground); + if (listHighlightForegroundColor) { + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .highlight { color: ${listHighlightForegroundColor}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts new file mode 100644 index 0000000000..143264e032 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts @@ -0,0 +1,394 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { KeyCode, KeyMod, KeyChord, SimpleKeybinding } from 'vs/base/common/keyCodes'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ServicesAccessor, registerEditorCommand, EditorCommand } from 'vs/editor/common/editorCommonExtensions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; +import { SmartSnippetInserter } from 'vs/workbench/parts/preferences/common/smartSnippetInserter'; +import { DefineKeybindingOverlayWidget } from 'vs/workbench/parts/preferences/browser/keybindingWidgets'; +import { FloatingClickWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { parseTree, Node } from 'vs/base/common/json'; +import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO'; +import { ScanCodeBinding } from 'vs/workbench/services/keybinding/common/scanCode'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { WindowsNativeResolvedKeybinding } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; +import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService'; +import { overviewRulerInfo, overviewRulerError } from 'vs/editor/common/view/editorColorRegistry'; + +const NLS_LAUNCH_MESSAGE = nls.localize('defineKeybinding.start', "Define Keybinding"); +const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutErrorMessage', "You won't be able to produce this key combination under your current keyboard layout."); + +const INTERESTING_FILE = /keybindings\.json$/; + +@editorContribution +export class DefineKeybindingController extends Disposable implements editorCommon.IEditorContribution { + + private static ID = 'editor.contrib.defineKeybinding'; + + public static get(editor: editorCommon.ICommonCodeEditor): DefineKeybindingController { + return editor.getContribution(DefineKeybindingController.ID); + } + + private _keybindingWidgetRenderer: KeybindingWidgetRenderer; + private _keybindingDecorationRenderer: KeybindingEditorDecorationsRenderer; + + constructor( + private _editor: ICodeEditor, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(); + + this._keybindingWidgetRenderer = null; + this._keybindingDecorationRenderer = null; + + this._register(this._editor.onDidChangeModel(e => this._update())); + this._update(); + } + + public getId(): string { + return DefineKeybindingController.ID; + } + + public get keybindingWidgetRenderer(): KeybindingWidgetRenderer { + return this._keybindingWidgetRenderer; + } + + public dispose(): void { + this._disposeKeybindingWidgetRenderer(); + this._disposeKeybindingDecorationRenderer(); + super.dispose(); + } + + private _update(): void { + if (!isInterestingEditorModel(this._editor)) { + this._disposeKeybindingWidgetRenderer(); + this._disposeKeybindingDecorationRenderer(); + return; + } + + // Decorations are shown for the default keybindings.json **and** for the user keybindings.json + this._createKeybindingDecorationRenderer(); + + // The button to define keybindings is shown only for the user keybindings.json + if (!this._editor.getConfiguration().readOnly) { + this._createKeybindingWidgetRenderer(); + } else { + this._disposeKeybindingWidgetRenderer(); + } + } + + private _createKeybindingWidgetRenderer(): void { + if (!this._keybindingWidgetRenderer) { + this._keybindingWidgetRenderer = this._instantiationService.createInstance(KeybindingWidgetRenderer, this._editor); + } + } + + private _disposeKeybindingWidgetRenderer(): void { + if (this._keybindingWidgetRenderer) { + this._keybindingWidgetRenderer.dispose(); + this._keybindingWidgetRenderer = null; + } + } + + private _createKeybindingDecorationRenderer(): void { + if (!this._keybindingDecorationRenderer) { + this._keybindingDecorationRenderer = this._instantiationService.createInstance(KeybindingEditorDecorationsRenderer, this._editor); + } + } + + private _disposeKeybindingDecorationRenderer(): void { + if (this._keybindingDecorationRenderer) { + this._keybindingDecorationRenderer.dispose(); + this._keybindingDecorationRenderer = null; + } + } +} + +export class KeybindingWidgetRenderer extends Disposable { + + private _launchWidget: FloatingClickWidget; + private _defineWidget: DefineKeybindingOverlayWidget; + + constructor( + private _editor: ICodeEditor, + @IInstantiationService private _instantiationService: IInstantiationService + ) { + super(); + this._launchWidget = this._register(this._instantiationService.createInstance(FloatingClickWidget, this._editor, NLS_LAUNCH_MESSAGE, DefineKeybindingCommand.ID)); + this._register(this._launchWidget.onClick(() => this.showDefineKeybindingWidget())); + this._defineWidget = this._register(this._instantiationService.createInstance(DefineKeybindingOverlayWidget, this._editor)); + + this._launchWidget.render(); + } + + public showDefineKeybindingWidget(): void { + this._defineWidget.start().then(keybinding => this._onAccepted(keybinding)); + } + + private _onAccepted(keybinding: string): void { + this._editor.focus(); + if (keybinding) { + let regexp = new RegExp(/\\/g); + let backslash = regexp.test(keybinding); + if (backslash) { + keybinding = keybinding.slice(0, -1) + '\\\\'; + } + let snippetText = [ + '{', + '\t"key": ' + JSON.stringify(keybinding) + ',', + '\t"command": "${1:commandId}",', + '\t"when": "${2:editorTextFocus}"', + '}$0' + ].join('\n'); + + let smartInsertInfo = SmartSnippetInserter.insertSnippet(this._editor.getModel(), this._editor.getPosition()); + snippetText = smartInsertInfo.prepend + snippetText + smartInsertInfo.append; + this._editor.setPosition(smartInsertInfo.position); + + SnippetController2.get(this._editor).insert(snippetText, 0, 0); + } + } +} + +export class KeybindingEditorDecorationsRenderer extends Disposable { + + private _updateDecorations: RunOnceScheduler; + private _dec: string[] = []; + + constructor( + private _editor: ICodeEditor, + @IKeybindingService private _keybindingService: IKeybindingService, + ) { + super(); + + this._updateDecorations = this._register(new RunOnceScheduler(() => this._updateDecorationsNow(), 500)); + + let model = this._editor.getModel(); + this._register(model.onDidChangeContent(() => this._updateDecorations.schedule())); + this._register(this._keybindingService.onDidUpdateKeybindings((e) => this._updateDecorations.schedule())); + this._register({ + dispose: () => { + this._dec = this._editor.deltaDecorations(this._dec, []); + this._updateDecorations.cancel(); + } + }); + this._updateDecorations.schedule(); + } + + private _updateDecorationsNow(): void { + const model = this._editor.getModel(); + + let newDecorations: editorCommon.IModelDeltaDecoration[] = []; + + const root = parseTree(model.getValue()); + if (root && Array.isArray(root.children)) { + for (let i = 0, len = root.children.length; i < len; i++) { + const entry = root.children[i]; + const dec = this._getDecorationForEntry(model, entry); + if (dec !== null) { + newDecorations.push(dec); + } + } + } + + this._dec = this._editor.deltaDecorations(this._dec, newDecorations); + } + + private _getDecorationForEntry(model: editorCommon.IModel, entry: Node): editorCommon.IModelDeltaDecoration { + if (!Array.isArray(entry.children)) { + return null; + } + for (let i = 0, len = entry.children.length; i < len; i++) { + const prop = entry.children[i]; + if (prop.type !== 'property') { + continue; + } + if (!Array.isArray(prop.children) || prop.children.length !== 2) { + continue; + } + const key = prop.children[0]; + if (key.value !== 'key') { + continue; + } + const value = prop.children[1]; + if (value.type !== 'string') { + continue; + } + + const resolvedKeybindings = this._keybindingService.resolveUserBinding(value.value); + if (resolvedKeybindings.length === 0) { + return this._createDecoration(true, null, null, model, value); + } + const resolvedKeybinding = resolvedKeybindings[0]; + let usLabel: string = null; + if (resolvedKeybinding instanceof WindowsNativeResolvedKeybinding) { + usLabel = resolvedKeybinding.getUSLabel(); + } + if (!resolvedKeybinding.isWYSIWYG()) { + return this._createDecoration(false, resolvedKeybinding.getLabel(), usLabel, model, value); + } + if (/abnt_|oem_/.test(value.value)) { + return this._createDecoration(false, resolvedKeybinding.getLabel(), usLabel, model, value); + } + const expectedUserSettingsLabel = resolvedKeybinding.getUserSettingsLabel(); + if (!KeybindingEditorDecorationsRenderer._userSettingsFuzzyEquals(value.value, expectedUserSettingsLabel)) { + return this._createDecoration(false, resolvedKeybinding.getLabel(), usLabel, model, value); + } + return null; + } + return null; + } + + static _userSettingsFuzzyEquals(a: string, b: string): boolean { + a = a.trim().toLowerCase(); + b = b.trim().toLowerCase(); + + if (a === b) { + return true; + } + + const [parsedA1, parsedA2] = KeybindingIO._readUserBinding(a); + const [parsedB1, parsedB2] = KeybindingIO._readUserBinding(b); + + return ( + this._userBindingEquals(parsedA1, parsedB1) + && this._userBindingEquals(parsedA2, parsedB2) + ); + } + + private static _userBindingEquals(a: SimpleKeybinding | ScanCodeBinding, b: SimpleKeybinding | ScanCodeBinding): boolean { + if (a === null && b === null) { + return true; + } + if (!a || !b) { + return false; + } + + if (a instanceof SimpleKeybinding && b instanceof SimpleKeybinding) { + return a.equals(b); + } + + if (a instanceof ScanCodeBinding && b instanceof ScanCodeBinding) { + return a.equals(b); + } + + return false; + } + + private _createDecoration(isError: boolean, uiLabel: string, usLabel: string, model: editorCommon.IModel, keyNode: Node): editorCommon.IModelDeltaDecoration { + let msg: MarkdownString; + let className: string; + let beforeContentClassName: string; + let overviewRulerColor: ThemeColor; + + if (isError) { + // this is the error case + msg = new MarkdownString().appendText(NLS_KB_LAYOUT_ERROR_MESSAGE); + className = 'keybindingError'; + beforeContentClassName = 'inlineKeybindingError'; + overviewRulerColor = themeColorFromId(overviewRulerError); + } else { + // this is the info case + if (usLabel && uiLabel !== usLabel) { + msg = new MarkdownString( + nls.localize({ + key: 'defineKeybinding.kbLayoutLocalAndUSMessage', + comment: [ + 'Please translate maintaining the stars (*) around the placeholders such that they will be rendered in bold.', + 'The placeholders will contain a keyboard combination e.g. Ctrl+Shift+/' + ] + }, "**{0}** for your current keyboard layout (**{1}** for US standard).", uiLabel, usLabel) + ); + } else { + msg = new MarkdownString( + nls.localize({ + key: 'defineKeybinding.kbLayoutLocalMessage', + comment: [ + 'Please translate maintaining the stars (*) around the placeholder such that it will be rendered in bold.', + 'The placeholder will contain a keyboard combination e.g. Ctrl+Shift+/' + ] + }, "**{0}** for your current keyboard layout.", uiLabel) + ); + } + className = 'keybindingInfo'; + beforeContentClassName = 'inlineKeybindingInfo'; + overviewRulerColor = themeColorFromId(overviewRulerInfo); + } + + const startPosition = model.getPositionAt(keyNode.offset); + const endPosition = model.getPositionAt(keyNode.offset + keyNode.length); + const range = new Range( + startPosition.lineNumber, startPosition.column, + endPosition.lineNumber, endPosition.column + ); + + // icon + highlight + message decoration + return { + range: range, + options: { + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: className, + beforeContentClassName: beforeContentClassName, + hoverMessage: msg, + overviewRuler: { + color: overviewRulerColor, + darkColor: overviewRulerColor, + position: editorCommon.OverviewRulerLane.Right + } + } + }; + } + +} + +class DefineKeybindingCommand extends EditorCommand { + + static ID = 'editor.action.defineKeybinding'; + + constructor() { + super({ + id: DefineKeybindingCommand.ID, + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.languageId.isEqualTo('json')), + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K) + } + }); + } + + public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + if (!isInterestingEditorModel(editor) || editor.getConfiguration().readOnly) { + return; + } + let controller = DefineKeybindingController.get(editor); + if (controller && controller.keybindingWidgetRenderer) { + controller.keybindingWidgetRenderer.showDefineKeybindingWidget(); + } + } +} + +function isInterestingEditorModel(editor: editorCommon.ICommonCodeEditor): boolean { + let model = editor.getModel(); + if (!model) { + return false; + } + let url = model.uri.toString(); + return INTERESTING_FILE.test(url); +} + +registerEditorCommand(new DefineKeybindingCommand()); diff --git a/src/vs/workbench/parts/preferences/browser/media/add.svg b/src/vs/workbench/parts/preferences/browser/media/add.svg new file mode 100644 index 0000000000..bdecdb0e45 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/add.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/add_inverse.svg b/src/vs/workbench/parts/preferences/browser/media/add_inverse.svg new file mode 100644 index 0000000000..3475c1e196 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/add_inverse.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/collapseAll.svg b/src/vs/workbench/parts/preferences/browser/media/collapseAll.svg new file mode 100644 index 0000000000..7d11a30f6e --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/collapseAll.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/collapseAll_inverse.svg b/src/vs/workbench/parts/preferences/browser/media/collapseAll_inverse.svg new file mode 100644 index 0000000000..46a65fff71 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/collapseAll_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/collapsed-dark.svg b/src/vs/workbench/parts/preferences/browser/media/collapsed-dark.svg new file mode 100644 index 0000000000..cf5c3641aa --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/collapsed-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/collapsed.svg b/src/vs/workbench/parts/preferences/browser/media/collapsed.svg new file mode 100644 index 0000000000..3a63808c35 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/collapsed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/edit.svg b/src/vs/workbench/parts/preferences/browser/media/edit.svg new file mode 100644 index 0000000000..ecde924084 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/edit_inverse.svg b/src/vs/workbench/parts/preferences/browser/media/edit_inverse.svg new file mode 100644 index 0000000000..da956cb2c6 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/edit_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/expanded-dark.svg b/src/vs/workbench/parts/preferences/browser/media/expanded-dark.svg new file mode 100644 index 0000000000..73d41e6399 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/expanded-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/expanded.svg b/src/vs/workbench/parts/preferences/browser/media/expanded.svg new file mode 100644 index 0000000000..75f73adbb0 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/expanded.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/info.svg b/src/vs/workbench/parts/preferences/browser/media/info.svg new file mode 100644 index 0000000000..6578b81ea3 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/keybindings.css b/src/vs/workbench/parts/preferences/browser/media/keybindings.css new file mode 100644 index 0000000000..d3a2a62210 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/keybindings.css @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.defineKeybindingWidget { + padding: 10px; + position: absolute; +} + +.defineKeybindingWidget .message { + width: 400px; + text-align: center; +} + +.defineKeybindingWidget .monaco-inputbox, +.defineKeybindingWidget .output { + margin-top:10px; + width: 400px; + display: block; + text-align: center; +} + +.defineKeybindingWidget .input { + text-align: center; +} + +.defineKeybindingWidget .output { + display: flex; + justify-content: center; +} + +.defineKeybindingWidget .output .monaco-keybinding { + margin: 0px 4px; +} + +/* Editor decorations */ +.monaco-editor .inlineKeybindingInfo:before { + margin: 0.2em 0.1em 0 0.1em; + content:" "; + display:inline-block; + height:0.8em; + width:1em; + background: url(info.svg) 0px -0.1em no-repeat; + background-size: 0.9em; +} + +.monaco-editor .inlineKeybindingError:before { + margin: 0.1em 0.1em 0 0.1em; + content:" "; + display:inline-block; + height:0.8em; + width:1em; + background: url(status-error.svg) 0px -0.1em no-repeat; + background-size: 1em; +} + +.monaco-editor .keybindingInfo { + box-shadow: inset 0 0 0 1px #B9B9B9; + background-color: rgba(100, 100, 250, 0.2); +} + +.monaco-editor .keybindingError { + box-shadow: inset 0 0 0 1px #B9B9B9; + background-color: rgba(250, 100, 100, 0.2); +} \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/keybindingsEditor.css b/src/vs/workbench/parts/preferences/browser/media/keybindingsEditor.css new file mode 100644 index 0000000000..ad84a05576 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/keybindingsEditor.css @@ -0,0 +1,190 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.keybindings-editor { + padding: 11px 0px 0px 27px; +} + +/* header styling */ + +.keybindings-editor > .keybindings-header { + padding: 0px 10px 11px 0; +} + +.keybindings-editor > .keybindings-header > .search-container { + position: relative; +} + +.keybindings-editor > .keybindings-header > .search-container > .sort-by-precedence { + position: absolute; + top: 0; + right: 10px; + margin-top: 5px; + background: url('sort_precedence.svg') center center no-repeat; +} + +.hc-black .keybindings-editor > .keybindings-header > .search-container > .sort-by-precedence, +.vs-dark .keybindings-editor > .keybindings-header > .search-container > .sort-by-precedence { + background: url('sort_precedence_inverse.svg') center center no-repeat; +} + +.keybindings-editor > .keybindings-header .search-container > .settings-search-input { + vertical-align: middle; +} + +.keybindings-editor > .keybindings-header .search-container > .settings-search-input > .monaco-inputbox { + height: 30px; +} + +.keybindings-editor > .keybindings-header .search-container > .settings-search-input > .monaco-inputbox .input { + font-size: 14px; + padding-left:10px; +} + +.keybindings-editor > .keybindings-header .open-keybindings-container { + margin-top: 10px; + opacity: 0.7; + display: flex; +} + +.keybindings-editor > .keybindings-header .open-keybindings-container > .file-name { + text-decoration: underline; + cursor: pointer; + margin-left: 4px; +} + +/** List based styling **/ + +.keybindings-editor > .keybindings-body .keybindings-list-container { + width: 100%; + border-spacing: 0; + border-collapse: separate; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row { + cursor: default; + display: flex; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.even:not(.focused):not(.selected):not(:hover), +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(:focus) .monaco-list-row.focused.even:not(.selected):not(:hover), +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(.focused) .monaco-list-row.focused.even:not(.selected):not(:hover) { + background-color: rgba(130, 130, 130, 0.04); +} + +.keybindings-editor > .keybindings-body .keybindings-list-container .monaco-list-row > .header { + text-align: left; + font-weight: bold; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .header, +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .column { + align-items: center; + display: flex; + overflow: hidden; + margin-right: 6px; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .actions { + width: 24px; + padding-right: 2px; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .command { + flex: 0.75; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .command.vertical-align-column { + flex-direction: column; + align-items: flex-start; + justify-content: center; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .command .command-default-label { + opacity: 0.8; + margin-top: 2px; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .keybinding { + flex: 0.5; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .keybinding .monaco-highlighted-label { + padding-left: 10px; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .source { + flex: 0.25; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .when { + flex: 1; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .when .empty { + padding-left: 4px; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .command .monaco-highlighted-label, +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .source .monaco-highlighted-label, +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .when .monaco-highlighted-label { + overflow: hidden; + text-overflow: ellipsis; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column > .code { + font-family: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback"; + font-size: 90%; + opacity: 0.8; + display: flex; + overflow: hidden; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column > .code.strong { + padding: 1px 4px; + background-color: rgba(128, 128, 128, 0.17); + border-radius: 4px; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .highlight { + font-weight: bold; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar { + display: none; + flex: 1; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.selected > .column.actions .monaco-action-bar, +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list.focused .monaco-list-row.focused > .column.actions .monaco-action-bar, +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row:hover > .column.actions .monaco-action-bar { + display: flex; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon { + width:16px; + height: 16px; + cursor: pointer; + margin-top: 3px; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.edit { + background: url('edit.svg') center center no-repeat; + transform: rotate(-90deg); +} + +.hc-black .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.edit, +.vs-dark .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.edit { + background: url('edit_inverse.svg') center center no-repeat; +} + +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.add { + background: url('add.svg') center center no-repeat; +} + +.hc-black .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.add, +.vs-dark .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.add { + background: url('add_inverse.svg') center center no-repeat; +} diff --git a/src/vs/workbench/parts/preferences/browser/media/preferences.css b/src/vs/workbench/parts/preferences/browser/media/preferences.css new file mode 100644 index 0000000000..7cd0056f07 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/preferences.css @@ -0,0 +1,241 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.preferences-editor > .preferences-header { + display: flex; + flex-wrap: wrap; + padding-left: 27px; + padding-right: 32px; + padding-bottom: 11px; + padding-top: 11px; +} + +.preferences-editor > .preferences-header.vertical-layout { + flex-direction: column; + align-items: flex-end; + padding-bottom: 5px; +} + +.preferences-editor > .preferences-editors-container.side-by-side-preferences-editor { + position: relative; +} + +.settings-targets-widget { + flex-wrap: wrap; + margin: 4px 0 4px 18px; + display: flex; + border-radius: 4px; + padding: 0 8px; + cursor: pointer; +} + +.settings-targets-widget > .settings-target { + font-size: 11px; + padding: 2px 4px 0 0; + white-space: nowrap; + overflow: hidden; + flex: 1; + display: flex; +} + +.settings-targets-widget > .settings-target > .settings-target-label { + text-transform: uppercase; +} + +.settings-targets-widget > .settings-target > .settings-target-details { + margin-left: 0.5em; + font-size: 10px; + opacity: 0.7; +} + +.settings-targets-widget > .settings-target > .settings-target-details.empty { + margin-left: 0; +} + +.settings-targets-widget > .settings-target-dropdown-icon { + padding-left: 0.5em; + padding-top: 4px; + font-size: 12px; +} + +.preferences-header > .settings-header-widget { + flex: 1; + display: flex; + position: relative; + align-self: stretch; +} + +.settings-header-widget > .settings-header-container { + padding-top: 8px; + padding-left: 27px; + padding-right: 32px; +} + +.settings-header-widget > .settings-count-widget { + margin: 6px 0px; + padding: 0px 8px; + position: absolute; + right: 10px; + border-radius: 2px; +} + +.settings-header-widget > .settings-count-widget.hide { + display: none; +} + +.settings-header-widget > .settings-search-container { + flex: 1; +} + +.settings-header-widget > .settings-search-container > .settings-search-input { + vertical-align: middle; +} + +.settings-header-widget > .settings-search-container > .settings-search-input > .monaco-inputbox { + height: 30px; +} + +.vs .settings-header-widget > .settings-search-container > .settings-search-input > .monaco-inputbox { + border: 1px solid #ddd; +} + +.settings-header-widget > .settings-search-container > .settings-search-input > .monaco-inputbox .input { + font-size: 14px; + padding-left:10px; +} + +.monaco-editor .settings-header-widget .title-container { + display: flex; +} + +.vs .monaco-editor .settings-header-widget .title-container { + color: #6f6f6f; +} +.vs-dark .monaco-editor .settings-header-widget .title-container { + color: #bbbbbb; +} +.hc-black .monaco-editor .settings-header-widget .title-container { + color: white; +} + +.monaco-editor .settings-header-widget .title-container .title { + font-weight: bold; + white-space: nowrap; + text-transform: uppercase; +} + +.monaco-editor .settings-header-widget .title-container .message { + white-space: nowrap; +} + +.monaco-editor .settings-group-title-widget { + z-index: 1; +} + +.monaco-editor .settings-group-title-widget .title-container { + width: 100%; + cursor: pointer; + font-weight: bold; + -webkit-user-select: none; + user-select: none; + display: flex; +} + +.vs .monaco-editor .settings-group-title-widget .title-container { + color: #6f6f6f; +} + +.monaco-editor .settings-group-title-widget .title-container .title { + white-space: nowrap; + overflow: hidden; +} + +.vs-dark .monaco-editor .settings-group-title-widget .title-container { + color: #bbbbbb; +} +.hc-black .monaco-editor .settings-group-title-widget .title-container { + color: white; +} + +.monaco-editor.vs-dark .settings-group-title-widget .title-container.focused, +.monaco-editor.vs .settings-group-title-widget .title-container.focused { + outline: none !important; +} + +.monaco-editor .settings-group-title-widget .title-container.focused, +.monaco-editor .settings-group-title-widget .title-container:hover { + background-color: rgba(153, 153, 153, 0.2); +} + +.monaco-editor.hc-black .settings-group-title-widget .title-container.focused { + outline: 1px dotted #f38518; +} + +.monaco-editor .settings-group-title-widget .title-container .expand-collapse-icon { + background: url(expanded.svg) 50% 50% no-repeat; + width: 16px; + height: 100%; +} + +.monaco-editor.vs-dark .settings-group-title-widget .title-container .expand-collapse-icon, +.monaco-editor.hc-black .settings-group-title-widget .title-container .expand-collapse-icon { + background: url(expanded-dark.svg) 50% 50% no-repeat; +} + +.monaco-editor .settings-group-title-widget .title-container.collapsed .expand-collapse-icon { + background: url(collapsed.svg) 50% 50% no-repeat; +} + +.monaco-editor.vs-dark .settings-group-title-widget .title-container.collapsed .expand-collapse-icon, +.monaco-editor.hc-black .settings-group-title-widget .title-container.collapsed .expand-collapse-icon { + background: url(collapsed-dark.svg) 50% 50% no-repeat; +} + +.monaco-editor .edit-preferences-widget { + background: url('edit.svg') center center no-repeat; + transform: rotate(-90deg); + width:16px; + height: 16px; + cursor: pointer; +} + +.monaco-editor .edit-preferences-widget.hidden { + display: none; + visibility: hidden; +} + +.monaco-editor.hc-black .edit-preferences-widget, +.monaco-editor.vs-dark .edit-preferences-widget { + background: url('edit_inverse.svg') center center no-repeat; +} + +.monaco-editor .unsupportedWorkbenhSettingInfo:before { + content:" "; + display:inline-block; + height:1em; + width:1em; + background: url(info.svg) 50% 50% no-repeat; + background-size: 0.9em; +} + +.monaco-editor .dim-configuration { + color: #b1b1b1; +} + +.monaco-editor .floating-click-widget { + padding: 10px; + border-radius: 5px; + cursor: pointer; +} + +.title-actions .action-item .icon.collapseAll, +.editor-actions .action-item .icon.collapseAll { + background: url('collapseAll.svg') center center no-repeat; +} + +.vs-dark .title-actions .action-item .icon.collapseAll, +.vs-dark .editor-actions .action-item .icon.collapseAll { + background: url('collapseAll_inverse.svg') center center no-repeat; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/sort_precedence.svg b/src/vs/workbench/parts/preferences/browser/media/sort_precedence.svg new file mode 100644 index 0000000000..25b013f5c3 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/sort_precedence.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/sort_precedence_inverse.svg b/src/vs/workbench/parts/preferences/browser/media/sort_precedence_inverse.svg new file mode 100644 index 0000000000..1b4301884a --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/sort_precedence_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/status-error.svg b/src/vs/workbench/parts/preferences/browser/media/status-error.svg new file mode 100644 index 0000000000..a1ddb39fed --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/status-error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts new file mode 100644 index 0000000000..1ed22cf9c0 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts @@ -0,0 +1,249 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import URI from 'vs/base/common/uri'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; +import { EditorInput, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory } from 'vs/workbench/common/editor'; +import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { DefaultPreferencesEditorInput, PreferencesEditor, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; +import { KeybindingsEditor, KeybindingsEditorInput } from 'vs/workbench/parts/preferences/browser/keybindingsEditor'; +import { OpenGlobalSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenWorkspaceSettingsAction, OpenFolderSettingsAction, ConfigureLanguageBasedSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; +import { + IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_SEARCH, + KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS +} from 'vs/workbench/parts/preferences/common/preferences'; +import { PreferencesService } from 'vs/workbench/parts/preferences/browser/preferencesService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { PreferencesContentProvider } from 'vs/workbench/parts/preferences/common/preferencesContentProvider'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; + +registerSingleton(IPreferencesService, PreferencesService); + +Registry.as(EditorExtensions.Editors).registerEditor( + new EditorDescriptor( + PreferencesEditor.ID, + nls.localize('defaultPreferencesEditor', "Default Preferences Editor"), + 'vs/workbench/parts/preferences/browser/preferencesEditor', + 'PreferencesEditor' + ), + [ + new SyncDescriptor(PreferencesEditorInput) + ] +); + +Registry.as(EditorExtensions.Editors).registerEditor( + new EditorDescriptor( + KeybindingsEditor.ID, + nls.localize('keybindingsEditor', "Keybindings Editor"), + 'vs/workbench/parts/preferences/browser/keybindingsEditor', + 'KeybindingsEditor' + ), + [ + new SyncDescriptor(KeybindingsEditorInput) + ] +); + +interface ISerializedPreferencesEditorInput { + name: string; + description: string; + + detailsSerialized: string; + masterSerialized: string; + + detailsTypeId: string; + masterTypeId: string; +} + +// Register Preferences Editor Input Factory +class PreferencesEditorInputFactory implements IEditorInputFactory { + + public serialize(editorInput: EditorInput): string { + const input = editorInput; + + if (input.details && input.master) { + const registry = Registry.as(EditorExtensions.Editors); + const detailsInputFactory = registry.getEditorInputFactory(input.details.getTypeId()); + const masterInputFactory = registry.getEditorInputFactory(input.master.getTypeId()); + + if (detailsInputFactory && masterInputFactory) { + const detailsSerialized = detailsInputFactory.serialize(input.details); + const masterSerialized = masterInputFactory.serialize(input.master); + + if (detailsSerialized && masterSerialized) { + return JSON.stringify({ + name: input.getName(), + description: input.getDescription(), + detailsSerialized, + masterSerialized, + detailsTypeId: input.details.getTypeId(), + masterTypeId: input.master.getTypeId() + }); + } + } + } + + return null; + } + + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { + const deserialized: ISerializedPreferencesEditorInput = JSON.parse(serializedEditorInput); + + const registry = Registry.as(EditorExtensions.Editors); + const detailsInputFactory = registry.getEditorInputFactory(deserialized.detailsTypeId); + const masterInputFactory = registry.getEditorInputFactory(deserialized.masterTypeId); + + if (detailsInputFactory && masterInputFactory) { + const detailsInput = detailsInputFactory.deserialize(instantiationService, deserialized.detailsSerialized); + const masterInput = masterInputFactory.deserialize(instantiationService, deserialized.masterSerialized); + + if (detailsInput && masterInput) { + return new PreferencesEditorInput(deserialized.name, deserialized.description, detailsInput, masterInput); + } + } + + return null; + } +} + +class KeybindingsEditorInputFactory implements IEditorInputFactory { + + public serialize(editorInput: EditorInput): string { + const input = editorInput; + return JSON.stringify({ + name: input.getName(), + typeId: input.getTypeId() + }); + } + + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { + return instantiationService.createInstance(KeybindingsEditorInput); + } +} + + +interface ISerializedDefaultPreferencesEditorInput { + resource: string; +} + +// Register Default Preferences Editor Input Factory +class DefaultPreferencesEditorInputFactory implements IEditorInputFactory { + + public serialize(editorInput: EditorInput): string { + const input = editorInput; + + const serialized: ISerializedDefaultPreferencesEditorInput = { resource: input.getResource().toString() }; + + return JSON.stringify(serialized); + } + + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { + const deserialized: ISerializedDefaultPreferencesEditorInput = JSON.parse(serializedEditorInput); + + return instantiationService.createInstance(DefaultPreferencesEditorInput, URI.parse(deserialized.resource)); + } +} + +Registry.as(EditorExtensions.Editors).registerEditorInputFactory(PreferencesEditorInput.ID, PreferencesEditorInputFactory); +Registry.as(EditorExtensions.Editors).registerEditorInputFactory(DefaultPreferencesEditorInput.ID, DefaultPreferencesEditorInputFactory); +Registry.as(EditorExtensions.Editors).registerEditorInputFactory(KeybindingsEditorInput.ID, KeybindingsEditorInputFactory); + +// Contribute Global Actions +const category = nls.localize('preferences', "Preferences"); +const registry = Registry.as(Extensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_COMMA }), 'Preferences: Open User Settings', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceSettingsAction, OpenWorkspaceSettingsAction.ID, OpenWorkspaceSettingsAction.LABEL), 'Preferences: Open Workspace Settings', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderSettingsAction, OpenFolderSettingsAction.ID, OpenFolderSettingsAction.LABEL), 'Preferences: Open Folder Settings', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsAction, OpenGlobalKeybindingsAction.ID, OpenGlobalKeybindingsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_S) }), 'Preferences: Open Keyboard Shortcuts', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsFileAction, OpenGlobalKeybindingsFileAction.ID, OpenGlobalKeybindingsFileAction.LABEL, { primary: null }), 'Preferences: Open Keyboard Shortcuts File', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureLanguageBasedSettingsAction, ConfigureLanguageBasedSettingsAction.ID, ConfigureLanguageBasedSettingsAction.LABEL), 'Preferences: Configure Language Specific Settings...', category); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_DEFINE, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), + handler: (accessor, args: any) => { + const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor; + editor.defineKeybinding(editor.activeKeybindingEntry); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_REMOVE, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), + primary: KeyCode.Delete, + mac: { + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.Backspace) + }, + handler: (accessor, args: any) => { + const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor; + editor.removeKeybinding(editor.activeKeybindingEntry); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_RESET, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), + primary: null, + handler: (accessor, args: any) => { + const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor; + editor.resetKeybinding(editor.activeKeybindingEntry); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_SEARCH, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), + primary: KeyMod.CtrlCmd | KeyCode.KEY_F, + handler: (accessor, args: any) => (accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor).search('') +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), + primary: null, + handler: (accessor, args: any) => { + const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor; + editor.showConflicts(editor.activeKeybindingEntry); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_COPY, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + handler: (accessor, args: any) => { + const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor; + editor.copyKeybinding(editor.activeKeybindingEntry); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS), + primary: KeyCode.DownArrow, + handler: (accessor, args: any) => { + const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor; + editor.focusKeybindings(); + } +}); + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(PreferencesContentProvider); \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts new file mode 100644 index 0000000000..28713eb97a --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -0,0 +1,173 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as nls from 'vs/nls'; +import URI from 'vs/base/common/uri'; +import { Action } from 'vs/base/common/actions'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; +import { IPreferencesService, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; + +export class OpenGlobalSettingsAction extends Action { + + public static ID = 'workbench.action.openGlobalSettings'; + public static LABEL = nls.localize('openGlobalSettings', "Open User Settings"); + + constructor( + id: string, + label: string, + @IPreferencesService private preferencesService: IPreferencesService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + return this.preferencesService.openGlobalSettings(); + } +} + +export class OpenGlobalKeybindingsAction extends Action { + + public static ID = 'workbench.action.openGlobalKeybindings'; + public static LABEL = nls.localize('openGlobalKeybindings', "Open Keyboard Shortcuts"); + + constructor( + id: string, + label: string, + @IPreferencesService private preferencesService: IPreferencesService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + return this.preferencesService.openGlobalKeybindingSettings(false); + } +} + +export class OpenGlobalKeybindingsFileAction extends Action { + + public static ID = 'workbench.action.openGlobalKeybindingsFile'; + public static LABEL = nls.localize('openGlobalKeybindingsFile', "Open Keyboard Shortcuts File"); + + constructor( + id: string, + label: string, + @IPreferencesService private preferencesService: IPreferencesService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + return this.preferencesService.openGlobalKeybindingSettings(true); + } +} + +export class OpenWorkspaceSettingsAction extends Action { + + public static ID = 'workbench.action.openWorkspaceSettings'; + public static LABEL = nls.localize('openWorkspaceSettings', "Open Workspace Settings"); + + constructor( + id: string, + label: string, + @IPreferencesService private preferencesService: IPreferencesService, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService + ) { + super(id, label); + this.enabled = this.workspaceContextService.hasWorkspace(); + } + + public run(event?: any): TPromise { + return this.preferencesService.openWorkspaceSettings(); + } +} + +export class OpenFolderSettingsAction extends Action { + + public static ID = 'workbench.action.openFolderSettings'; + public static LABEL = nls.localize('openFolderSettings', "Open Folder Settings"); + + constructor( + id: string, + label: string, + @IPreferencesService private preferencesService: IPreferencesService, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IQuickOpenService private quickOpenService: IQuickOpenService + ) { + super(id, label); + this.enabled = this.workspaceContextService.hasMultiFolderWorkspace(); + } + + public run(): TPromise { + const picks: IPickOpenEntry[] = this.workspaceContextService.getWorkspace().roots.map((root, index) => { + return { + label: getSettingsTargetName(ConfigurationTarget.FOLDER, root, this.workspaceContextService), + id: `${index}` + }; + }); + + return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickFolder', "Select Folder") }) + .then(pick => { + if (pick) { + return this.preferencesService.openFolderSettings(this.workspaceContextService.getWorkspace().roots[parseInt(pick.id)]); + } + return undefined; + }); + + } +} + +export class ConfigureLanguageBasedSettingsAction extends Action { + + public static ID = 'workbench.action.configureLanguageBasedSettings'; + public static LABEL = nls.localize('configureLanguageBasedSettings', "Configure Language Specific Settings..."); + + constructor( + id: string, + label: string, + @IModeService private modeService: IModeService, + @IQuickOpenService private quickOpenService: IQuickOpenService, + @IPreferencesService private preferencesService: IPreferencesService + ) { + super(id, label); + } + + public run(): TPromise { + const languages = this.modeService.getRegisteredLanguageNames(); + const picks: IPickOpenEntry[] = languages.sort().map((lang, index) => { + let description: string = nls.localize('languageDescriptionConfigured', "({0})", this.modeService.getModeIdForLanguageName(lang.toLowerCase())); + // construct a fake resource to be able to show nice icons if any + let fakeResource: URI; + const extensions = this.modeService.getExtensions(lang); + if (extensions && extensions.length) { + fakeResource = URI.file(extensions[0]); + } else { + const filenames = this.modeService.getFilenames(lang); + if (filenames && filenames.length) { + fakeResource = URI.file(filenames[0]); + } + } + return { + label: lang, + resource: fakeResource, + description + }; + }); + + return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language") }) + .then(pick => { + if (pick) { + return this.modeService.getOrCreateModeByLanguageName(pick.label) + .then(mode => this.preferencesService.configureSettingsForLanguage(mode.getLanguageIdentifier().language)); + } + return undefined; + }); + + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts new file mode 100644 index 0000000000..95499ba8d2 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -0,0 +1,948 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as nls from 'vs/nls'; +import URI from 'vs/base/common/uri'; +import * as DOM from 'vs/base/browser/dom'; +import { Delayer } from 'vs/base/common/async'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import { ArrayNavigator, INavigator } from 'vs/base/common/iterator'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { toResource, SideBySideEditorInput, EditorOptions, EditorInput, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; +import { IEditorControl, Position, Verbosity } from 'vs/platform/editor/common/editor'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; +import { CodeEditor } from 'vs/editor/browser/codeEditor'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { + IPreferencesService, ISettingsGroup, ISetting, IFilterResult, + CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, ISettingsEditorModel +} from 'vs/workbench/parts/preferences/common/preferences'; +import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; +import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; +import { ICodeEditor, IEditorContributionCtor } from 'vs/editor/browser/editorBrowser'; +import { SearchWidget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { ContextKeyExpr, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { Command } from 'vs/editor/common/editorCommonExtensions'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { VSash } from 'vs/base/browser/ui/sash/sash'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { IPreferencesRenderer, DefaultSettingsRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer, FolderSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; + +// Ignore following contributions +import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; +import { FindController } from 'vs/editor/contrib/find/browser/find'; +import { SelectionHighlighter } from 'vs/editor/contrib/find/common/findController'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Registry } from 'vs/platform/registry/common/platform'; + +export class PreferencesEditorInput extends SideBySideEditorInput { + public static ID: string = 'workbench.editorinputs.preferencesEditorInput'; + + getTypeId(): string { + return PreferencesEditorInput.ID; + } + + public getTitle(verbosity: Verbosity): string { + return this.master.getTitle(verbosity); + } +} + +export class DefaultPreferencesEditorInput extends ResourceEditorInput { + public static ID = 'workbench.editorinputs.defaultpreferences'; + constructor(defaultSettingsResource: URI, + @ITextModelService textModelResolverService: ITextModelService + ) { + super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, textModelResolverService); + } + + getTypeId(): string { + return DefaultPreferencesEditorInput.ID; + } + + matches(other: any): boolean { + if (!super.matches(other)) { + return false; + } + if (!(other instanceof DefaultPreferencesEditorInput)) { + return false; + } + return true; + } +} + +export class PreferencesEditor extends BaseEditor { + + public static ID: string = 'workbench.editor.preferencesEditor'; + + private defaultSettingsEditorContextKey: IContextKey; + private focusSettingsContextKey: IContextKey; + private headerContainer: HTMLElement; + private searchWidget: SearchWidget; + private settingsTargetsWidget: SettingsTargetsWidget; + private sideBySidePreferencesWidget: SideBySidePreferencesWidget; + private preferencesRenderers: PreferencesRenderers; + + private delayedFilterLogging: Delayer; + + private latestEmptyFilters: string[] = []; + private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget = null; + + constructor( + @IPreferencesService private preferencesService: IPreferencesService, + @IEnvironmentService private environmentService: IEnvironmentService, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IInstantiationService private instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + ) { + super(PreferencesEditor.ID, telemetryService, themeService); + this.defaultSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(this.contextKeyService); + this.focusSettingsContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(this.contextKeyService); + this.delayedFilterLogging = new Delayer(1000); + } + + public createEditor(parent: Builder): void { + const parentElement = parent.getHTMLElement(); + DOM.addClass(parentElement, 'preferences-editor'); + + this.headerContainer = DOM.append(parentElement, DOM.$('.preferences-header')); + + this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, this.headerContainer, { + ariaLabel: nls.localize('SearchSettingsWidget.AriaLabel', "Search settings"), + placeholder: nls.localize('SearchSettingsWidget.Placeholder', "Search Settings"), + focusKey: this.focusSettingsContextKey + })); + this._register(this.searchWidget.onDidChange(value => this.filterPreferences(value.trim()))); + this._register(this.searchWidget.onNavigate(shift => this.preferencesRenderers.focusNextPreference(!shift))); + this._register(this.searchWidget.onFocus(() => this.lastFocusedWidget = this.searchWidget)); + this.lastFocusedWidget = this.searchWidget; + + this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, this.headerContainer, this.preferencesService.userSettingsResource, ConfigurationTarget.USER)); + this._register(this.settingsTargetsWidget.onDidTargetChange(target => this.switchSettings(target))); + + const editorsContainer = DOM.append(parentElement, DOM.$('.preferences-editors-container')); + this.sideBySidePreferencesWidget = this._register(this.instantiationService.createInstance(SideBySidePreferencesWidget, editorsContainer)); + this._register(this.sideBySidePreferencesWidget.onFocus(() => this.lastFocusedWidget = this.sideBySidePreferencesWidget)); + + this.preferencesRenderers = this._register(new PreferencesRenderers()); + this._register(this.workspaceContextService.onDidChangeWorkspaceRoots(() => this.onWorkspaceRootsChanged())); + } + + public setInput(newInput: PreferencesEditorInput, options?: EditorOptions): TPromise { + this.defaultSettingsEditorContextKey.set(true); + const oldInput = this.input; + return super.setInput(newInput, options).then(() => this.updateInput(oldInput, newInput, options)); + } + + public layout(dimension: Dimension): void { + DOM.toggleClass(this.headerContainer, 'vertical-layout', dimension.width < 700); + this.searchWidget.layout(dimension); + const headerHeight = DOM.getTotalHeight(this.headerContainer); + this.sideBySidePreferencesWidget.layout(new Dimension(dimension.width, dimension.height - headerHeight)); + } + + public getControl(): IEditorControl { + return this.sideBySidePreferencesWidget.getControl(); + } + + public focus(): void { + if (this.lastFocusedWidget) { + this.lastFocusedWidget.focus(); + } + } + + public focusSearch(filter?: string): void { + if (filter) { + this.searchWidget.setValue(filter); + } + + this.searchWidget.focus(); + } + + public focusSettingsFileEditor(): void { + if (this.sideBySidePreferencesWidget) { + this.sideBySidePreferencesWidget.focus(); + } + } + + public clearInput(): void { + this.defaultSettingsEditorContextKey.set(false); + this.sideBySidePreferencesWidget.clearInput(); + super.clearInput(); + } + + protected setEditorVisible(visible: boolean, position: Position): void { + this.sideBySidePreferencesWidget.setEditorVisible(visible, position); + super.setEditorVisible(visible, position); + } + + public changePosition(position: Position): void { + this.sideBySidePreferencesWidget.changePosition(position); + super.changePosition(position); + } + + private updateInput(oldInput: PreferencesEditorInput, newInput: PreferencesEditorInput, options?: EditorOptions): TPromise { + const resource = toResource(newInput.master); + this.settingsTargetsWidget.setTarget(this.getSettingsConfigurationTargetUri(resource), this.getSettingsConfigurationTarget(resource)); + + return this.sideBySidePreferencesWidget.setInput(newInput.details, newInput.master, options).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { + this.preferencesRenderers.defaultPreferencesRenderer = defaultPreferencesRenderer; + this.preferencesRenderers.editablePreferencesRenderer = editablePreferencesRenderer; + this.filterPreferences(this.searchWidget.getValue()); + }); + } + + private getSettingsConfigurationTarget(resource: URI): ConfigurationTarget { + if (this.preferencesService.userSettingsResource.fsPath === resource.fsPath) { + return ConfigurationTarget.USER; + } + if (this.preferencesService.workspaceSettingsResource.fsPath === resource.fsPath) { + return ConfigurationTarget.WORKSPACE; + } + if (this.workspaceContextService.getRoot(resource)) { + return ConfigurationTarget.FOLDER; + } + return null; + } + + private getSettingsConfigurationTargetUri(resource: URI): URI { + if (this.preferencesService.userSettingsResource.fsPath === resource.fsPath) { + return resource; + } + if (this.preferencesService.workspaceSettingsResource.fsPath === resource.fsPath) { + return resource; + } + + return this.workspaceContextService.getRoot(resource); + } + + private onWorkspaceRootsChanged(): void { + if (this.input) { + const settingsResource = toResource((this.input).master); + const targetResource = this.getSettingsConfigurationTargetUri(settingsResource); + if (!targetResource) { + this.switchSettings(this.preferencesService.userSettingsResource); + } + } + } + + private switchSettings(resource: URI): void { + // Focus the editor if this editor is not active editor + if (this.editorService.getActiveEditor() !== this) { + this.focus(); + } + const promise = this.input.isDirty() ? this.input.save() : TPromise.as(true); + promise.done(value => this.preferencesService.switchSettings(this.getSettingsConfigurationTarget(resource), resource)); + } + + private filterPreferences(filter: string) { + const count = this.preferencesRenderers.filterPreferences(filter); + const message = filter ? this.showSearchResultsMessage(count) : nls.localize('totalSettingsMessage', "Total {0} Settings", count); + this.searchWidget.showMessage(message, count); + if (count === 0) { + this.latestEmptyFilters.push(filter); + } + this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter)); + } + + private showSearchResultsMessage(count: number): string { + return count === 0 ? nls.localize('noSettingsFound', "No Results") : + count === 1 ? nls.localize('oneSettingFound', "1 Setting matched") : + nls.localize('settingsFound', "{0} Settings matched", count); + } + + private reportFilteringUsed(filter: string): void { + if (filter) { + let data = { + filter, + emptyFilters: this.getLatestEmptyFiltersForTelemetry() + }; + this.latestEmptyFilters = []; + this.telemetryService.publicLog('defaultSettings.filter', data); + } + } + + /** + * Put a rough limit on the size of the telemetry data, since otherwise it could be an unbounded large amount + * of data. 8192 is the max size of a property value. This is rough since that probably includes ""s, etc. + */ + private getLatestEmptyFiltersForTelemetry(): string[] { + let cumulativeSize = 0; + return this.latestEmptyFilters.filter(filterText => (cumulativeSize += filterText.length) <= 8192); + } +} + +class SettingsNavigator implements INavigator { + + private iterator: ArrayNavigator; + + constructor(settings: ISetting[]) { + this.iterator = new ArrayNavigator(settings); + } + + public next(): ISetting { + return this.iterator.next() || this.iterator.first(); + } + + public previous(): ISetting { + return this.iterator.previous() || this.iterator.last(); + } + + public parent(): ISetting { + return this.iterator.parent(); + } + + public first(): ISetting { + return this.iterator.first(); + } + + public last(): ISetting { + return this.iterator.last(); + } + + public current(): ISetting { + return this.iterator.current(); + } +} + +class PreferencesRenderers extends Disposable { + + private _defaultPreferencesRenderer: IPreferencesRenderer; + private _editablePreferencesRenderer: IPreferencesRenderer; + private _settingsNavigator: SettingsNavigator; + + private _disposables: IDisposable[] = []; + + public get defaultPreferencesRenderer(): IPreferencesRenderer { + return this._defaultPreferencesRenderer; + } + + public set defaultPreferencesRenderer(defaultPreferencesRenderer: IPreferencesRenderer) { + if (this._defaultPreferencesRenderer !== defaultPreferencesRenderer) { + this._defaultPreferencesRenderer = defaultPreferencesRenderer; + + this._disposables = dispose(this._disposables); + + if (this._defaultPreferencesRenderer) { + this._defaultPreferencesRenderer.onUpdatePreference(({ key, value, source }) => this._updatePreference(key, value, source, this._editablePreferencesRenderer), this, this._disposables); + this._defaultPreferencesRenderer.onFocusPreference(preference => this._focusPreference(preference, this._editablePreferencesRenderer), this, this._disposables); + this._defaultPreferencesRenderer.onClearFocusPreference(preference => this._clearFocus(preference, this._editablePreferencesRenderer), this, this._disposables); + } + } + } + + public set editablePreferencesRenderer(editableSettingsRenderer: IPreferencesRenderer) { + this._editablePreferencesRenderer = editableSettingsRenderer; + } + + public filterPreferences(filter: string): number { + const defaultPreferencesFilterResult = this._filterPreferences(filter, this._defaultPreferencesRenderer); + const editablePreferencesFilterResult = this._filterPreferences(filter, this._editablePreferencesRenderer); + + const defaultPreferencesFilteredGroups = defaultPreferencesFilterResult ? defaultPreferencesFilterResult.filteredGroups : this._getAllPreferences(this._defaultPreferencesRenderer); + const editablePreferencesFilteredGroups = editablePreferencesFilterResult ? editablePreferencesFilterResult.filteredGroups : this._getAllPreferences(this._editablePreferencesRenderer); + const consolidatedSettings = this._consolidateSettings(editablePreferencesFilteredGroups, defaultPreferencesFilteredGroups); + this._settingsNavigator = new SettingsNavigator(filter ? consolidatedSettings : []); + + return consolidatedSettings.length; + } + + public focusNextPreference(forward: boolean = true) { + const setting = forward ? this._settingsNavigator.next() : this._settingsNavigator.previous(); + this._focusPreference(setting, this._defaultPreferencesRenderer); + this._focusPreference(setting, this._editablePreferencesRenderer); + } + + private _getAllPreferences(preferencesRenderer: IPreferencesRenderer): ISettingsGroup[] { + return preferencesRenderer ? (preferencesRenderer.preferencesModel).settingsGroups : []; + } + + private _filterPreferences(filter: string, preferencesRenderer: IPreferencesRenderer): IFilterResult { + let filterResult = null; + if (preferencesRenderer) { + filterResult = filter ? (preferencesRenderer.preferencesModel).filterSettings(filter) : null; + preferencesRenderer.filterPreferences(filterResult); + } + return filterResult; + } + + private _focusPreference(preference: ISetting, preferencesRenderer: IPreferencesRenderer): void { + if (preference && preferencesRenderer) { + preferencesRenderer.focusPreference(preference); + } + } + + private _clearFocus(preference: ISetting, preferencesRenderer: IPreferencesRenderer): void { + if (preference && preferencesRenderer) { + preferencesRenderer.clearFocus(preference); + } + } + + private _updatePreference(key: string, value: any, source: ISetting, preferencesRenderer: IPreferencesRenderer): void { + if (preferencesRenderer) { + preferencesRenderer.updatePreference(key, value, source); + } + } + + private _consolidateSettings(editableSettingsGroups: ISettingsGroup[], defaultSettingsGroups: ISettingsGroup[]): ISetting[] { + const editableSettings = this._flatten(editableSettingsGroups); + const defaultSettings = this._flatten(defaultSettingsGroups).filter(secondarySetting => !editableSettings.some(primarySetting => primarySetting.key === secondarySetting.key)); + return [...editableSettings, ...defaultSettings]; + } + + private _flatten(settingsGroups: ISettingsGroup[]): ISetting[] { + const settings: ISetting[] = []; + for (const group of settingsGroups) { + for (const section of group.sections) { + settings.push(...section.settings); + } + } + return settings; + } + + public dispose(): void { + dispose(this._disposables); + super.dispose(); + } +} + +class SideBySidePreferencesWidget extends Widget { + + private dimension: Dimension; + + private defaultPreferencesEditor: DefaultPreferencesEditor; + private editablePreferencesEditor: BaseEditor; + private defaultPreferencesEditorContainer: HTMLElement; + private editablePreferencesEditorContainer: HTMLElement; + + private _onFocus: Emitter = new Emitter(); + readonly onFocus: Event = this._onFocus.event; + + private lastFocusedEditor: BaseEditor; + + private sash: VSash; + + constructor(parent: HTMLElement, @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService) { + super(); + this.create(parent); + } + + private create(parentElement: HTMLElement): void { + DOM.addClass(parentElement, 'side-by-side-preferences-editor'); + this.createSash(parentElement); + + this.defaultPreferencesEditorContainer = DOM.append(parentElement, DOM.$('.default-preferences-editor-container')); + this.defaultPreferencesEditorContainer.style.position = 'absolute'; + this.defaultPreferencesEditor = this._register(this.instantiationService.createInstance(DefaultPreferencesEditor)); + this.defaultPreferencesEditor.create(new Builder(this.defaultPreferencesEditorContainer)); + this.defaultPreferencesEditor.setVisible(true); + (this.defaultPreferencesEditor.getControl()).onDidFocusEditor(() => this.lastFocusedEditor = this.defaultPreferencesEditor); + + this.editablePreferencesEditorContainer = DOM.append(parentElement, DOM.$('.editable-preferences-editor-container')); + this.editablePreferencesEditorContainer.style.position = 'absolute'; + + this._register(attachStylerCallback(this.themeService, { scrollbarShadow }, colors => { + const shadow = colors.scrollbarShadow ? colors.scrollbarShadow.toString() : null; + + if (shadow) { + this.editablePreferencesEditorContainer.style.boxShadow = `-6px 0 5px -5px ${shadow}`; + } else { + this.editablePreferencesEditorContainer.style.boxShadow = null; + } + })); + + const focusTracker = this._register(DOM.trackFocus(parentElement)); + this._register(focusTracker.addFocusListener(() => this._onFocus.fire())); + } + + public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options?: EditorOptions): TPromise<{ defaultPreferencesRenderer: IPreferencesRenderer, editablePreferencesRenderer: IPreferencesRenderer }> { + return this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput) + .then(() => { + this.dolayout(this.sash.getVerticalSashLeft()); + return TPromise.join([this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, toResource(editablePreferencesEditorInput), options), + this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options)]) + .then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => ({ defaultPreferencesRenderer, editablePreferencesRenderer })); + }); + } + + public layout(dimension: Dimension): void { + this.dimension = dimension; + this.sash.setDimenesion(this.dimension); + } + + public focus(): void { + if (this.lastFocusedEditor) { + this.lastFocusedEditor.focus(); + } + } + + public getControl(): IEditorControl { + return this.editablePreferencesEditor ? this.editablePreferencesEditor.getControl() : null; + } + + public clearInput(): void { + if (this.editablePreferencesEditor) { + this.editablePreferencesEditor.clearInput(); + } + } + + public setEditorVisible(visible: boolean, position: Position): void { + if (this.editablePreferencesEditor) { + this.editablePreferencesEditor.setVisible(visible, position); + } + } + + public changePosition(position: Position): void { + if (this.editablePreferencesEditor) { + this.editablePreferencesEditor.changePosition(position); + } + } + + private getOrCreateEditablePreferencesEditor(editorInput: EditorInput): TPromise { + if (this.editablePreferencesEditor) { + return TPromise.as(this.editablePreferencesEditor); + } + const descriptor = Registry.as(EditorExtensions.Editors).getEditor(editorInput); + return this.instantiationService.createInstance(descriptor) + .then((editor: BaseEditor) => { + this.editablePreferencesEditor = editor; + this.editablePreferencesEditor.create(new Builder(this.editablePreferencesEditorContainer)); + this.editablePreferencesEditor.setVisible(true); + (this.editablePreferencesEditor.getControl()).onDidFocusEditor(() => this.lastFocusedEditor = this.editablePreferencesEditor); + this.lastFocusedEditor = this.editablePreferencesEditor; + return editor; + }); + } + + private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions): TPromise> { + return editor.setInput(input, options) + .then(() => (editor.getControl()).getContribution(editorContributionId).updatePreferencesRenderer(associatedPreferencesModelUri)); + } + + private createSash(parentElement: HTMLElement): void { + this.sash = this._register(new VSash(parentElement, 220)); + this._register(this.sash.onPositionChange(position => this.dolayout(position))); + } + + private dolayout(splitPoint: number): void { + if (!this.editablePreferencesEditor || !this.dimension) { + return; + } + const masterEditorWidth = this.dimension.width - splitPoint; + const detailsEditorWidth = this.dimension.width - masterEditorWidth; + + this.defaultPreferencesEditorContainer.style.width = `${detailsEditorWidth}px`; + this.defaultPreferencesEditorContainer.style.height = `${this.dimension.height}px`; + this.defaultPreferencesEditorContainer.style.left = '0px'; + + this.editablePreferencesEditorContainer.style.width = `${masterEditorWidth}px`; + this.editablePreferencesEditorContainer.style.height = `${this.dimension.height}px`; + this.editablePreferencesEditorContainer.style.left = `${splitPoint}px`; + + this.defaultPreferencesEditor.layout(new Dimension(detailsEditorWidth, this.dimension.height)); + this.editablePreferencesEditor.layout(new Dimension(masterEditorWidth, this.dimension.height)); + } + + private disposeEditors(): void { + if (this.defaultPreferencesEditor) { + this.defaultPreferencesEditor.dispose(); + this.defaultPreferencesEditor = null; + } + if (this.editablePreferencesEditor) { + this.editablePreferencesEditor.dispose(); + this.editablePreferencesEditor = null; + } + } + + public dispose(): void { + this.disposeEditors(); + super.dispose(); + } +} + +export class EditableSettingsEditor extends BaseTextEditor { + + public static ID: string = 'workbench.editor.settingsEditor'; + + private modelDisposables: IDisposable[] = []; + private saveDelayer: Delayer; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, + @IThemeService themeService: IThemeService, + @IPreferencesService private preferencesService: IPreferencesService, + @IModelService private modelService: IModelService, + @IModeService modeService: IModeService, + @ITextFileService textFileService: ITextFileService, + @IEditorGroupService editorGroupService: IEditorGroupService + ) { + super(EditableSettingsEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, modeService, textFileService, editorGroupService); + this._register({ dispose: () => dispose(this.modelDisposables) }); + this.saveDelayer = new Delayer(1000); + } + + protected createEditor(parent: Builder): void { + super.createEditor(parent); + + const codeEditor = getCodeEditor(this); + if (codeEditor) { + this._register(codeEditor.onDidChangeModel(() => this.onDidModelChange())); + } + } + + protected getAriaLabel(): string { + const input = this.input; + const inputName = input && input.getName(); + + let ariaLabel: string; + if (inputName) { + ariaLabel = nls.localize('fileEditorWithInputAriaLabel', "{0}. Text file editor.", inputName); + } else { + ariaLabel = nls.localize('fileEditorAriaLabel', "Text file editor."); + } + + return ariaLabel; + } + + setInput(input: EditorInput, options: EditorOptions): TPromise { + return super.setInput(input, options) + .then(() => this.input.resolve() + .then(editorModel => editorModel.load()) + .then(editorModel => this.getControl().setModel((editorModel).textEditorModel))); + } + + clearInput(): void { + this.modelDisposables = dispose(this.modelDisposables); + super.clearInput(); + } + + private onDidModelChange(): void { + this.modelDisposables = dispose(this.modelDisposables); + const model = getCodeEditor(this).getModel(); + if (model) { + this.preferencesService.createPreferencesEditorModel(model.uri) + .then(preferencesEditorModel => { + const settingsEditorModel = preferencesEditorModel; + this.modelDisposables.push(settingsEditorModel); + this.modelDisposables.push(model.onDidChangeContent(() => this.saveDelayer.trigger(() => settingsEditorModel.save()))); + }); + } + } +} + +export class DefaultPreferencesEditor extends BaseTextEditor { + + public static ID: string = 'workbench.editor.defaultPreferences'; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, + @IThemeService themeService: IThemeService, + @IPreferencesService private preferencesService: IPreferencesService, + @IModelService private modelService: IModelService, + @IModeService modeService: IModeService, + @ITextFileService textFileService: ITextFileService, + @IEditorGroupService editorGroupService: IEditorGroupService + ) { + super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, modeService, textFileService, editorGroupService); + } + + public createEditorControl(parent: Builder, configuration: IEditorOptions): editorCommon.IEditor { + return this.instantiationService.createInstance(DefaultPreferencesCodeEditor, parent.getHTMLElement(), configuration); + } + + protected getConfigurationOverrides(): IEditorOptions { + const options = super.getConfigurationOverrides(); + options.readOnly = true; + if (this.input) { + options.lineNumbers = 'off'; + options.renderLineHighlight = 'none'; + options.scrollBeyondLastLine = false; + options.folding = false; + options.renderWhitespace = 'none'; + options.wordWrap = 'on'; + options.renderIndentGuides = false; + options.rulers = []; + options.glyphMargin = true; + options.minimap = { + enabled: false + }; + } + return options; + } + + setInput(input: DefaultPreferencesEditorInput, options: EditorOptions): TPromise { + return super.setInput(input, options) + .then(() => this.input.resolve() + .then(editorModel => editorModel.load()) + .then(editorModel => this.getControl().setModel((editorModel).textEditorModel))); + } + + public layout(dimension: Dimension) { + this.getControl().layout(dimension); + } + + protected getAriaLabel(): string { + return nls.localize('preferencesAriaLabel', "Default preferences. Readonly text editor."); + } +} + +class DefaultPreferencesCodeEditor extends CodeEditor { + + protected _getContributions(): IEditorContributionCtor[] { + let contributions = super._getContributions(); + let skipContributions = [FoldingController.prototype, SelectionHighlighter.prototype, FindController.prototype]; + contributions = contributions.filter(c => skipContributions.indexOf(c.prototype) === -1); + contributions.push(DefaultSettingsEditorContribution); + return contributions; + } + +} + +interface ISettingsEditorContribution extends editorCommon.IEditorContribution { + + updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise>; + +} + +abstract class AbstractSettingsEditorContribution extends Disposable { + + private preferencesRendererCreationPromise: TPromise>; + + constructor(protected editor: ICodeEditor, + @IInstantiationService protected instantiationService: IInstantiationService, + @IPreferencesService protected preferencesService: IPreferencesService, + @IWorkspaceContextService protected workspaceContextService: IWorkspaceContextService + ) { + super(); + this._register(this.editor.onDidChangeModel(() => this._onModelChanged())); + } + + updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise> { + if (!this.preferencesRendererCreationPromise) { + this.preferencesRendererCreationPromise = this._createPreferencesRenderer(); + } + + if (this.preferencesRendererCreationPromise) { + return this._hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri) + .then(changed => changed ? this._updatePreferencesRenderer(associatedPreferencesModelUri) : this.preferencesRendererCreationPromise); + } + + return TPromise.as(null); + } + + private _onModelChanged(): void { + const model = this.editor.getModel(); + this.disposePreferencesRenderer(); + if (model) { + this.preferencesRendererCreationPromise = this._createPreferencesRenderer(); + } + } + + private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): TPromise { + return this.preferencesRendererCreationPromise.then(preferencesRenderer => { + return !(preferencesRenderer && preferencesRenderer.associatedPreferencesModel && preferencesRenderer.associatedPreferencesModel.uri.fsPath === associatedPreferencesModelUri.fsPath); + }); + } + + private _updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise> { + return this.preferencesService.createPreferencesEditorModel(associatedPreferencesModelUri) + .then(associatedPreferencesEditorModel => { + return this.preferencesRendererCreationPromise.then(preferencesRenderer => { + if (preferencesRenderer) { + if (preferencesRenderer.associatedPreferencesModel) { + preferencesRenderer.associatedPreferencesModel.dispose(); + } + preferencesRenderer.associatedPreferencesModel = associatedPreferencesEditorModel; + } + return preferencesRenderer; + }); + }); + } + + private disposePreferencesRenderer(): void { + if (this.preferencesRendererCreationPromise) { + this.preferencesRendererCreationPromise.then(preferencesRenderer => { + if (preferencesRenderer) { + if (preferencesRenderer.associatedPreferencesModel) { + preferencesRenderer.associatedPreferencesModel.dispose(); + } + preferencesRenderer.dispose(); + } + }); + this.preferencesRendererCreationPromise = TPromise.as(null); + } + } + + dispose() { + this.disposePreferencesRenderer(); + super.dispose(); + } + + protected abstract _createPreferencesRenderer(): TPromise>; +} + +class DefaultSettingsEditorContribution extends AbstractSettingsEditorContribution implements ISettingsEditorContribution { + + static ID: string = 'editor.contrib.defaultsettings'; + + getId(): string { + return DefaultSettingsEditorContribution.ID; + } + + protected _createPreferencesRenderer(): TPromise> { + return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri) + .then(editorModel => { + if (editorModel instanceof DefaultSettingsEditorModel && this.editor.getModel()) { + const preferencesRenderer = this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel); + preferencesRenderer.render(); + return preferencesRenderer; + } + return null; + }); + } +} + +@editorContribution +class SettingsEditorContribution extends AbstractSettingsEditorContribution implements ISettingsEditorContribution { + + static ID: string = 'editor.contrib.settings'; + + getId(): string { + return SettingsEditorContribution.ID; + } + + protected _createPreferencesRenderer(): TPromise> { + if (this.isSettingsModel()) { + return TPromise.join([this.preferencesService.createPreferencesEditorModel(this.preferencesService.defaultSettingsResource), this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)]) + .then(([defaultSettingsModel, settingsModel]) => { + if (settingsModel instanceof SettingsEditorModel && this.editor.getModel()) { + switch (settingsModel.configurationTarget) { + case ConfigurationTarget.USER: + return this.instantiationService.createInstance(UserSettingsRenderer, this.editor, settingsModel, defaultSettingsModel); + case ConfigurationTarget.WORKSPACE: + return this.instantiationService.createInstance(WorkspaceSettingsRenderer, this.editor, settingsModel, defaultSettingsModel); + case ConfigurationTarget.FOLDER: + return this.instantiationService.createInstance(FolderSettingsRenderer, this.editor, settingsModel, defaultSettingsModel); + } + } + return null; + }) + .then(preferencesRenderer => { + if (preferencesRenderer) { + preferencesRenderer.render(); + } + return preferencesRenderer; + }); + } + return null; + } + + private isSettingsModel(): boolean { + const model = this.editor.getModel(); + if (!model) { + return false; + } + + if (this.preferencesService.userSettingsResource && this.preferencesService.userSettingsResource.fsPath === model.uri.fsPath) { + return true; + } + + if (this.preferencesService.workspaceSettingsResource && this.preferencesService.workspaceSettingsResource.fsPath === model.uri.fsPath) { + return true; + } + + const workspace = this.workspaceContextService.getWorkspace(); + if (workspace) { + for (const root of workspace.roots) { + const folderSettingsResource = this.preferencesService.getFolderSettingsResource(root); + if (folderSettingsResource && folderSettingsResource.fsPath === model.uri.fsPath) { + return true; + } + } + } + + return false; + } + +} + +abstract class SettingsCommand extends Command { + + protected getPreferencesEditor(accessor: ServicesAccessor): PreferencesEditor { + const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor(); + if (activeEditor instanceof PreferencesEditor) { + return activeEditor; + } + return null; + + } + +} +class StartSearchDefaultSettingsCommand extends SettingsCommand { + + public runCommand(accessor: ServicesAccessor, args: any): void { + const preferencesEditor = this.getPreferencesEditor(accessor); + if (preferencesEditor) { + preferencesEditor.focusSearch(); + } + } + +} +const command = new StartSearchDefaultSettingsCommand({ + id: SETTINGS_EDITOR_COMMAND_SEARCH, + precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR), + kbOpts: { primary: KeyMod.CtrlCmd | KeyCode.KEY_F } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(command.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +class FocusSettingsFileEditorCommand extends SettingsCommand { + + public runCommand(accessor: ServicesAccessor, args: any): void { + const preferencesEditor = this.getPreferencesEditor(accessor); + if (preferencesEditor) { + preferencesEditor.focusSettingsFileEditor(); + } + } + +} +const focusSettingsFileEditorCommand = new FocusSettingsFileEditorCommand({ + id: SETTINGS_EDITOR_COMMAND_FOCUS_FILE, + precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, + kbOpts: { primary: KeyCode.DownArrow } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(focusSettingsFileEditorCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts new file mode 100644 index 0000000000..42babeb21e --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -0,0 +1,1109 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as nls from 'vs/nls'; +import { Delayer } from 'vs/base/common/async'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IAction } from 'vs/base/common/actions'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Registry } from 'vs/platform/registry/common/platform'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, ISettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferences'; +import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView'; +import { SettingsGroupTitleWidget, EditPreferenceWidget, SettingsHeaderWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; +import { IConfigurationEditingService, ConfigurationEditingError, ConfigurationEditingErrorCode, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { overrideIdentifierFromKey } from 'vs/platform/configuration/common/model'; +import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { MarkdownString } from 'vs/base/common/htmlContent'; + +export interface IPreferencesRenderer extends IDisposable { + preferencesModel: IPreferencesEditorModel; + associatedPreferencesModel: IPreferencesEditorModel; + + onFocusPreference: Event; + onClearFocusPreference: Event; + onUpdatePreference: Event<{ key: string, value: any, source: T }>; + + render(): void; + updatePreference(key: string, value: any, source: T): void; + filterPreferences(filterResult: IFilterResult): void; + focusPreference(setting: T): void; + clearFocus(setting: T): void; +} + + +export class UserSettingsRenderer extends Disposable implements IPreferencesRenderer { + + private settingHighlighter: SettingHighlighter; + private editSettingActionRenderer: EditSettingRenderer; + private highlightMatchesRenderer: HighlightMatchesRenderer; + private modelChangeDelayer: Delayer = new Delayer(200); + + private _onFocusPreference: Emitter = new Emitter(); + public readonly onFocusPreference: Event = this._onFocusPreference.event; + + private _onUpdatePreference: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>(); + public readonly onUpdatePreference: Event<{ key: string, value: any, source: ISetting }> = this._onUpdatePreference.event; + + private _onClearFocusPreference: Emitter = new Emitter(); + public readonly onClearFocusPreference: Event = this._onClearFocusPreference.event; + + private filterResult: IFilterResult; + + constructor(protected editor: ICodeEditor, public readonly preferencesModel: SettingsEditorModel, private _associatedPreferencesModel: IPreferencesEditorModel, + @IPreferencesService protected preferencesService: IPreferencesService, + @ITelemetryService private telemetryService: ITelemetryService, + @ITextFileService private textFileService: ITextFileService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, + @IMessageService private messageService: IMessageService, + @IInstantiationService protected instantiationService: IInstantiationService + ) { + super(); + this._register(preferencesModel); + this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference)); + this.highlightMatchesRenderer = this._register(instantiationService.createInstance(HighlightMatchesRenderer, editor)); + this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.preferencesModel, this.settingHighlighter)); + this._register(this.editSettingActionRenderer.onUpdateSetting(({ key, value, source }) => this.updatePreference(key, value, source))); + this._register(this.editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); + + this.createHeader(); + } + + public get associatedPreferencesModel(): IPreferencesEditorModel { + return this._associatedPreferencesModel; + } + + public set associatedPreferencesModel(associatedPreferencesModel: IPreferencesEditorModel) { + this._associatedPreferencesModel = associatedPreferencesModel; + this.editSettingActionRenderer.associatedPreferencesModel = associatedPreferencesModel; + } + + protected createHeader(): void { + this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyUserSettingsHeader', "Place your settings here to overwrite the Default Settings.")); + } + + public render(): void { + this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this.associatedPreferencesModel); + if (this.filterResult) { + this.filterPreferences(this.filterResult); + } + } + + public updatePreference(key: string, value: any, source: ISetting): void { + this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [key] }); + const overrideIdentifier = source.overrideOf ? overrideIdentifierFromKey(source.overrideOf.key) : null; + const resource = this.preferencesModel.uri; + this.configurationEditingService.writeConfiguration(this.preferencesModel.configurationTarget, { key, value }, { donotSave: this.textFileService.isDirty(resource), donotNotifyError: true, scopes: { overrideIdentifier, resource } }) + .then(() => this.onSettingUpdated(source), error => { + this.messageService.show(Severity.Error, this.toErrorMessage(error, this.preferencesModel.configurationTarget)); + }); + } + + private toErrorMessage(error: ConfigurationEditingError, target: ConfigurationTarget): string { + switch (error.code) { + case ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION: { + return nls.localize('errorInvalidConfiguration', "Unable to write into settings. Correct errors/warnings in the file and try again."); + }; + } + return error.message; + } + + private onModelChanged(): void { + if (!this.editor.getModel()) { + // model could have been disposed during the delay + return; + } + this.render(); + } + + private onSettingUpdated(setting: ISetting) { + this.editor.focus(); + setting = this.getSetting(setting); + if (setting) { + // TODO:@sandy Selection range should be template range + this.editor.setSelection(setting.valueRange); + this.settingHighlighter.highlight(setting, true); + } + } + + private getSetting(setting: ISetting): ISetting { + const { key, overrideOf } = setting; + if (overrideOf) { + const setting = this.getSetting(overrideOf); + for (const override of setting.overrides) { + if (override.key === key) { + return override; + } + } + return null; + } + return this.preferencesModel.getPreference(key); + } + + public filterPreferences(filterResult: IFilterResult): void { + this.filterResult = filterResult; + this.settingHighlighter.clear(true); + this.highlightMatchesRenderer.render(filterResult ? filterResult.matches : []); + } + + public focusPreference(setting: ISetting): void { + const s = this.getSetting(setting); + if (s) { + this.settingHighlighter.highlight(s, true); + } else { + this.settingHighlighter.clear(true); + } + } + + public clearFocus(setting: ISetting): void { + this.settingHighlighter.clear(true); + } +} + +export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer { + + private untrustedSettingRenderer: UnsupportedWorkspaceSettingsRenderer; + private workspaceConfigurationRenderer: WorkspaceConfigurationRenderer; + + constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel, associatedPreferencesModel: IPreferencesEditorModel, + @IPreferencesService preferencesService: IPreferencesService, + @ITelemetryService telemetryService: ITelemetryService, + @ITextFileService textFileService: ITextFileService, + @IConfigurationEditingService configurationEditingService: IConfigurationEditingService, + @IMessageService messageService: IMessageService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(editor, preferencesModel, associatedPreferencesModel, preferencesService, telemetryService, textFileService, configurationEditingService, messageService, instantiationService); + this.untrustedSettingRenderer = this._register(instantiationService.createInstance(UnsupportedWorkspaceSettingsRenderer, editor, preferencesModel)); + this.workspaceConfigurationRenderer = this._register(instantiationService.createInstance(WorkspaceConfigurationRenderer, editor, preferencesModel)); + } + + protected createHeader(): void { + this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyWorkspaceSettingsHeader', "Place your settings here to overwrite the User Settings.")); + } + + public render(): void { + super.render(); + this.untrustedSettingRenderer.render(); + this.workspaceConfigurationRenderer.render(); + } +} + +export class FolderSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer { + + private unsupportedWorkbenchSettingsRenderer: UnsupportedWorkbenchSettingsRenderer; + + constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel, associatedPreferencesModel: IPreferencesEditorModel, + @IPreferencesService preferencesService: IPreferencesService, + @ITelemetryService telemetryService: ITelemetryService, + @ITextFileService textFileService: ITextFileService, + @IConfigurationEditingService configurationEditingService: IConfigurationEditingService, + @IMessageService messageService: IMessageService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(editor, preferencesModel, associatedPreferencesModel, preferencesService, telemetryService, textFileService, configurationEditingService, messageService, instantiationService); + this.unsupportedWorkbenchSettingsRenderer = this._register(instantiationService.createInstance(UnsupportedWorkbenchSettingsRenderer, editor, preferencesModel)); + } + + protected createHeader(): void { + this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyFolderSettingsHeader', "Place your folder settings here to overwrite those from the Workspace Settings.")); + } + + public render(): void { + super.render(); + this.unsupportedWorkbenchSettingsRenderer.render(); + } +} + +export class DefaultSettingsRenderer extends Disposable implements IPreferencesRenderer { + + private _associatedPreferencesModel: IPreferencesEditorModel; + private settingHighlighter: SettingHighlighter; + private settingsHeaderRenderer: DefaultSettingsHeaderRenderer; + private settingsGroupTitleRenderer: SettingsGroupTitleRenderer; + private filteredMatchesRenderer: FilteredMatchesRenderer; + private hiddenAreasRenderer: HiddenAreasRenderer; + private editSettingActionRenderer: EditSettingRenderer; + + private _onUpdatePreference: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>(); + public readonly onUpdatePreference: Event<{ key: string, value: any, source: ISetting }> = this._onUpdatePreference.event; + + private _onFocusPreference: Emitter = new Emitter(); + public readonly onFocusPreference: Event = this._onFocusPreference.event; + + private _onClearFocusPreference: Emitter = new Emitter(); + public readonly onClearFocusPreference: Event = this._onClearFocusPreference.event; + + private filterResult: IFilterResult; + + constructor(protected editor: ICodeEditor, public readonly preferencesModel: DefaultSettingsEditorModel, + @IPreferencesService protected preferencesService: IPreferencesService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService protected instantiationService: IInstantiationService + ) { + super(); + this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference)); + this.settingsHeaderRenderer = this._register(instantiationService.createInstance(DefaultSettingsHeaderRenderer, editor, preferencesModel.configurationScope)); + this.settingsGroupTitleRenderer = this._register(instantiationService.createInstance(SettingsGroupTitleRenderer, editor)); + this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor)); + this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, preferencesModel, this.settingHighlighter)); + this._register(this.editSettingActionRenderer.onUpdateSetting(e => this._onUpdatePreference.fire(e))); + const paranthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, preferencesModel.settingsGroups)); + this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, paranthesisHidingRenderer])); + + this._register(this.settingsGroupTitleRenderer.onHiddenAreasChanged(() => this.hiddenAreasRenderer.render())); + } + + public get associatedPreferencesModel(): IPreferencesEditorModel { + return this._associatedPreferencesModel; + } + + public set associatedPreferencesModel(associatedPreferencesModel: IPreferencesEditorModel) { + this._associatedPreferencesModel = associatedPreferencesModel; + this.editSettingActionRenderer.associatedPreferencesModel = associatedPreferencesModel; + } + + public render() { + this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups); + this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel); + this.hiddenAreasRenderer.render(); + this.settingHighlighter.clear(true); + this.settingsGroupTitleRenderer.showGroup(1); + this.hiddenAreasRenderer.render(); + } + + public filterPreferences(filterResult: IFilterResult): void { + this.filterResult = filterResult; + if (!filterResult) { + this.settingHighlighter.clear(true); + this.filteredMatchesRenderer.render(null); + this.settingsHeaderRenderer.render(this.preferencesModel.settingsGroups); + this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups); + this.settingsGroupTitleRenderer.showGroup(1); + this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel); + } else { + this.filteredMatchesRenderer.render(filterResult); + this.settingsHeaderRenderer.render(filterResult.filteredGroups); + this.settingsGroupTitleRenderer.render(filterResult.filteredGroups); + this.settingHighlighter.clear(true); + this.editSettingActionRenderer.render(filterResult.filteredGroups, this._associatedPreferencesModel); + } + this.hiddenAreasRenderer.render(); + } + + public focusPreference(s: ISetting): void { + const setting = this.getSetting(s); + if (setting) { + this.settingsGroupTitleRenderer.showSetting(setting); + this.settingHighlighter.highlight(setting, true); + } else { + this.settingHighlighter.clear(true); + } + } + + private getSetting(setting: ISetting): ISetting { + const { key, overrideOf } = setting; + if (overrideOf) { + const setting = this.getSetting(overrideOf); + for (const override of setting.overrides) { + if (override.key === key) { + return override; + } + } + return null; + } + const settingsGroups = this.filterResult ? this.filterResult.filteredGroups : this.preferencesModel.settingsGroups; + return this.getPreference(key, settingsGroups); + } + + private getPreference(key: string, settingsGroups: ISettingsGroup[]): ISetting { + for (const group of settingsGroups) { + for (const section of group.sections) { + for (const setting of section.settings) { + if (setting.key === key) { + return setting; + } + } + } + } + return null; + } + + public clearFocus(setting: ISetting): void { + this.settingHighlighter.clear(true); + } + + public collapseAll() { + this.settingsGroupTitleRenderer.collapseAll(); + } + + public updatePreference(key: string, value: any, source: ISetting): void { + } +} + +export interface HiddenAreasProvider { + hiddenAreas: IRange[]; +} + +export class StaticContentHidingRenderer extends Disposable implements HiddenAreasProvider { + + constructor(private editor: ICodeEditor, private settingsGroups: ISettingsGroup[] + ) { + super(); + } + + get hiddenAreas(): IRange[] { + const model = this.editor.getModel(); + return [ + { + startLineNumber: 1, + startColumn: model.getLineMinColumn(1), + endLineNumber: 2, + endColumn: model.getLineMaxColumn(2) + }, + { + startLineNumber: this.settingsGroups[0].range.endLineNumber + 1, + startColumn: model.getLineMinColumn(this.settingsGroups[0].range.endLineNumber + 1), + endLineNumber: this.settingsGroups[0].range.endLineNumber + 4, + endColumn: model.getLineMaxColumn(this.settingsGroups[0].range.endLineNumber + 4) + }, + { + startLineNumber: model.getLineCount() - 1, + startColumn: model.getLineMinColumn(model.getLineCount() - 1), + endLineNumber: model.getLineCount(), + endColumn: model.getLineMaxColumn(model.getLineCount()) + } + ]; + } + +} + +class DefaultSettingsHeaderRenderer extends Disposable { + + private settingsHeaderWidget: SettingsHeaderWidget; + + constructor(private editor: ICodeEditor, scope: ConfigurationScope) { + super(); + const title = scope === ConfigurationScope.RESOURCE ? nls.localize('defaultFolderSettingsTitle', "Default Folder Settings") : nls.localize('defaultSettingsTitle', "Default Settings"); + this.settingsHeaderWidget = this._register(new SettingsHeaderWidget(editor, title)); + } + + public render(settingsGroups: ISettingsGroup[]) { + if (settingsGroups.length) { + this.settingsHeaderWidget.setMessage(''); + } else { + this.settingsHeaderWidget.setMessage(nls.localize('noSettingsFound', "No Settings Found.")); + } + } +} + +export class SettingsGroupTitleRenderer extends Disposable implements HiddenAreasProvider { + + private _onHiddenAreasChanged: Emitter = new Emitter(); + get onHiddenAreasChanged(): Event { return this._onHiddenAreasChanged.event; }; + + private settingsGroups: ISettingsGroup[]; + private hiddenGroups: ISettingsGroup[] = []; + private settingsGroupTitleWidgets: SettingsGroupTitleWidget[]; + private disposables: IDisposable[] = []; + + constructor(private editor: ICodeEditor, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(); + } + + public get hiddenAreas(): IRange[] { + const hiddenAreas: IRange[] = []; + for (const group of this.hiddenGroups) { + hiddenAreas.push(group.range); + } + return hiddenAreas; + } + + public render(settingsGroups: ISettingsGroup[]) { + this.disposeWidgets(); + this.settingsGroups = settingsGroups.slice(); + this.settingsGroupTitleWidgets = []; + for (const group of this.settingsGroups.slice().reverse()) { + const settingsGroupTitleWidget = this.instantiationService.createInstance(SettingsGroupTitleWidget, this.editor, group); + settingsGroupTitleWidget.render(); + this.settingsGroupTitleWidgets.push(settingsGroupTitleWidget); + this.disposables.push(settingsGroupTitleWidget); + this.disposables.push(settingsGroupTitleWidget.onToggled(collapsed => this.onToggled(collapsed, settingsGroupTitleWidget.settingsGroup))); + } + this.settingsGroupTitleWidgets.reverse(); + } + + public showGroup(group: number) { + this.hiddenGroups = this.settingsGroups.filter((g, i) => i !== group - 1); + for (const groupTitleWidget of this.settingsGroupTitleWidgets.filter((g, i) => i !== group - 1)) { + groupTitleWidget.toggleCollapse(true); + } + this._onHiddenAreasChanged.fire(); + } + + public showSetting(setting: ISetting): void { + const settingsGroupTitleWidget = this.settingsGroupTitleWidgets.filter(widget => Range.containsRange(widget.settingsGroup.range, setting.range))[0]; + if (settingsGroupTitleWidget && settingsGroupTitleWidget.isCollapsed()) { + settingsGroupTitleWidget.toggleCollapse(false); + this.hiddenGroups.splice(this.hiddenGroups.indexOf(settingsGroupTitleWidget.settingsGroup), 1); + this._onHiddenAreasChanged.fire(); + } + } + + public collapseAll() { + this.editor.setPosition({ lineNumber: 1, column: 1 }); + this.hiddenGroups = this.settingsGroups.slice(); + for (const groupTitleWidget of this.settingsGroupTitleWidgets) { + groupTitleWidget.toggleCollapse(true); + } + this._onHiddenAreasChanged.fire(); + } + + private onToggled(collapsed: boolean, group: ISettingsGroup) { + const index = this.hiddenGroups.indexOf(group); + if (collapsed) { + const currentPosition = this.editor.getPosition(); + if (group.range.startLineNumber <= currentPosition.lineNumber && group.range.endLineNumber >= currentPosition.lineNumber) { + this.editor.setPosition({ lineNumber: group.range.startLineNumber - 1, column: 1 }); + } + this.hiddenGroups.push(group); + } else { + this.hiddenGroups.splice(index, 1); + } + this._onHiddenAreasChanged.fire(); + } + + private disposeWidgets() { + this.hiddenGroups = []; + this.disposables = dispose(this.disposables); + } + + public dispose() { + this.disposeWidgets(); + super.dispose(); + } +} + +export class HiddenAreasRenderer extends Disposable { + + constructor(private editor: ICodeEditor, private hiddenAreasProviders: HiddenAreasProvider[], + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(); + } + + public render() { + const ranges: IRange[] = []; + for (const hiddenAreaProvider of this.hiddenAreasProviders) { + ranges.push(...hiddenAreaProvider.hiddenAreas); + } + this.editor.setHiddenAreas(ranges); + } + + public dispose() { + this.editor.setHiddenAreas([]); + super.dispose(); + } +} + +export class FilteredMatchesRenderer extends Disposable implements HiddenAreasProvider { + + private decorationIds: string[] = []; + public hiddenAreas: IRange[] = []; + + constructor(private editor: ICodeEditor, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(); + } + + public render(result: IFilterResult): void { + const model = this.editor.getModel(); + this.hiddenAreas = []; + this.editor.changeDecorations(changeAccessor => { + this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []); + }); + if (result) { + this.hiddenAreas = this.computeHiddenRanges(result.filteredGroups, result.allGroups, model); + this.editor.changeDecorations(changeAccessor => { + this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, result.matches.map(match => this.createDecoration(match, model))); + }); + } + } + + private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { + return { + range, + options: { + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'findMatch' + } + }; + } + + private computeHiddenRanges(filteredGroups: ISettingsGroup[], allSettingsGroups: ISettingsGroup[], model: editorCommon.IModel): IRange[] { + const notMatchesRanges: IRange[] = []; + for (const group of allSettingsGroups) { + const filteredGroup = filteredGroups.filter(g => g.title === group.title)[0]; + if (!filteredGroup) { + notMatchesRanges.push({ + startLineNumber: group.range.startLineNumber - 1, + startColumn: model.getLineMinColumn(group.range.startLineNumber - 1), + endLineNumber: group.range.endLineNumber, + endColumn: model.getLineMaxColumn(group.range.endLineNumber), + }); + } else { + for (const section of group.sections) { + if (section.titleRange) { + if (!this.containsLine(section.titleRange.startLineNumber, filteredGroup)) { + notMatchesRanges.push(this.createCompleteRange(section.titleRange, model)); + } + } + for (const setting of section.settings) { + if (!this.containsLine(setting.range.startLineNumber, filteredGroup)) { + notMatchesRanges.push(this.createCompleteRange(setting.range, model)); + } + } + } + } + } + return notMatchesRanges; + } + + private containsLine(lineNumber: number, settingsGroup: ISettingsGroup): boolean { + if (settingsGroup.titleRange && lineNumber >= settingsGroup.titleRange.startLineNumber && lineNumber <= settingsGroup.titleRange.endLineNumber) { + return true; + } + + for (const section of settingsGroup.sections) { + if (section.titleRange && lineNumber >= section.titleRange.startLineNumber && lineNumber <= section.titleRange.endLineNumber) { + return true; + } + + for (const setting of section.settings) { + if (lineNumber >= setting.range.startLineNumber && lineNumber <= setting.range.endLineNumber) { + return true; + } + } + } + return false; + } + + private createCompleteRange(range: IRange, model: editorCommon.IModel): IRange { + return { + startLineNumber: range.startLineNumber, + startColumn: model.getLineMinColumn(range.startLineNumber), + endLineNumber: range.endLineNumber, + endColumn: model.getLineMaxColumn(range.endLineNumber) + }; + } + + public dispose() { + if (this.decorationIds) { + this.decorationIds = this.editor.changeDecorations(changeAccessor => { + return changeAccessor.deltaDecorations(this.decorationIds, []); + }); + } + super.dispose(); + } +} + +export class HighlightMatchesRenderer extends Disposable { + + private decorationIds: string[] = []; + + constructor(private editor: ICodeEditor, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(); + } + + public render(matches: IRange[]): void { + const model = this.editor.getModel(); + this.editor.changeDecorations(changeAccessor => { + this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []); + }); + if (matches.length) { + this.editor.changeDecorations(changeAccessor => { + this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, matches.map(match => this.createDecoration(match, model))); + }); + } + } + + private static _FIND_MATCH = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'findMatch' + }); + + private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { + return { + range, + options: HighlightMatchesRenderer._FIND_MATCH + }; + } + + public dispose() { + if (this.decorationIds) { + this.decorationIds = this.editor.changeDecorations(changeAccessor => { + return changeAccessor.deltaDecorations(this.decorationIds, []); + }); + } + super.dispose(); + } +} + +class EditSettingRenderer extends Disposable { + + private editPreferenceWidgetForCusorPosition: EditPreferenceWidget; + private editPreferenceWidgetForMouseMove: EditPreferenceWidget; + + private settingsGroups: ISettingsGroup[]; + public associatedPreferencesModel: IPreferencesEditorModel; + private toggleEditPreferencesForMouseMoveDelayer: Delayer; + + private _onUpdateSetting: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>(); + public readonly onUpdateSetting: Event<{ key: string, value: any, source: ISetting }> = this._onUpdateSetting.event; + + constructor(private editor: ICodeEditor, private masterSettingsModel: ISettingsEditorModel, + private settingHighlighter: SettingHighlighter, + @IPreferencesService private preferencesService: IPreferencesService, + @IInstantiationService private instantiationService: IInstantiationService, + @IContextMenuService private contextMenuService: IContextMenuService + ) { + super(); + + this.editPreferenceWidgetForCusorPosition = this._register(this.instantiationService.createInstance(EditPreferenceWidget, editor)); + this.editPreferenceWidgetForMouseMove = this._register(this.instantiationService.createInstance(EditPreferenceWidget, editor)); + this.toggleEditPreferencesForMouseMoveDelayer = new Delayer(75); + + this._register(this.editPreferenceWidgetForCusorPosition.onClick(e => this.onEditSettingClicked(this.editPreferenceWidgetForCusorPosition, e))); + this._register(this.editPreferenceWidgetForMouseMove.onClick(e => this.onEditSettingClicked(this.editPreferenceWidgetForMouseMove, e))); + + this._register(this.editor.onDidChangeCursorPosition(positionChangeEvent => this.onPositionChanged(positionChangeEvent))); + this._register(this.editor.onMouseMove(mouseMoveEvent => this.onMouseMoved(mouseMoveEvent))); + this._register(this.editor.onDidChangeConfiguration(() => this.onConfigurationChanged())); + } + + public render(settingsGroups: ISettingsGroup[], associatedPreferencesModel: IPreferencesEditorModel): void { + this.editPreferenceWidgetForCusorPosition.hide(); + this.editPreferenceWidgetForMouseMove.hide(); + this.settingsGroups = settingsGroups; + this.associatedPreferencesModel = associatedPreferencesModel; + + const settings = this.getSettings(this.editor.getPosition().lineNumber); + if (settings.length) { + this.showEditPreferencesWidget(this.editPreferenceWidgetForCusorPosition, settings); + } + } + + private isDefaultSettings(): boolean { + return this.masterSettingsModel instanceof DefaultSettingsEditorModel; + } + + private onConfigurationChanged(): void { + if (!this.editor.getConfiguration().viewInfo.glyphMargin) { + this.editPreferenceWidgetForCusorPosition.hide(); + this.editPreferenceWidgetForMouseMove.hide(); + } + } + + private onPositionChanged(positionChangeEvent: ICursorPositionChangedEvent) { + this.editPreferenceWidgetForMouseMove.hide(); + const settings = this.getSettings(positionChangeEvent.position.lineNumber); + if (settings.length) { + this.showEditPreferencesWidget(this.editPreferenceWidgetForCusorPosition, settings); + } else { + this.editPreferenceWidgetForCusorPosition.hide(); + } + } + + private onMouseMoved(mouseMoveEvent: IEditorMouseEvent): void { + const editPreferenceWidget = this.getEditPreferenceWidgetUnderMouse(mouseMoveEvent); + if (editPreferenceWidget) { + this.onMouseOver(editPreferenceWidget); + return; + } + this.settingHighlighter.clear(); + this.toggleEditPreferencesForMouseMoveDelayer.trigger(() => this.toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent)); + } + + private getEditPreferenceWidgetUnderMouse(mouseMoveEvent: IEditorMouseEvent): EditPreferenceWidget { + if (mouseMoveEvent.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN) { + const line = mouseMoveEvent.target.position.lineNumber; + if (this.editPreferenceWidgetForMouseMove.getLine() === line && this.editPreferenceWidgetForMouseMove.isVisible()) { + return this.editPreferenceWidgetForMouseMove; + } + if (this.editPreferenceWidgetForCusorPosition.getLine() === line && this.editPreferenceWidgetForCusorPosition.isVisible()) { + return this.editPreferenceWidgetForCusorPosition; + } + } + return null; + } + + private toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent: IEditorMouseEvent): void { + const settings = mouseMoveEvent.target.position ? this.getSettings(mouseMoveEvent.target.position.lineNumber) : null; + if (settings && settings.length) { + this.showEditPreferencesWidget(this.editPreferenceWidgetForMouseMove, settings); + } else { + this.editPreferenceWidgetForMouseMove.hide(); + } + } + + private showEditPreferencesWidget(editPreferencesWidget: EditPreferenceWidget, settings: ISetting[]) { + const line = settings[0].valueRange.startLineNumber; + if (this.editor.getConfiguration().viewInfo.glyphMargin && this.marginFreeFromOtherDecorations(line)) { + editPreferencesWidget.show(line, nls.localize('editTtile', "Edit"), settings); + const editPreferenceWidgetToHide = editPreferencesWidget === this.editPreferenceWidgetForCusorPosition ? this.editPreferenceWidgetForMouseMove : this.editPreferenceWidgetForCusorPosition; + editPreferenceWidgetToHide.hide(); + } + } + + private marginFreeFromOtherDecorations(line: number): boolean { + const decorations = this.editor.getLineDecorations(line); + if (decorations) { + for (const { options } of decorations) { + if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf(EditPreferenceWidget.GLYPH_MARGIN_CLASS_NAME) === -1) { + return false; + } + } + } + return true; + } + + private getSettings(lineNumber: number): ISetting[] { + const configurationMap = this.getConfigurationsMap(); + return this.getSettingsAtLineNumber(lineNumber).filter(setting => { + let configurationNode = configurationMap[setting.key]; + if (configurationNode) { + if (this.isDefaultSettings()) { + return true; + } + if (configurationNode.type === 'boolean' || configurationNode.enum) { + if ((this.masterSettingsModel).configurationTarget !== ConfigurationTarget.FOLDER) { + return true; + } + if (configurationNode.scope === ConfigurationScope.RESOURCE) { + return true; + } + } + } + return false; + }); + } + + private getSettingsAtLineNumber(lineNumber: number): ISetting[] { + const settings = []; + for (const group of this.settingsGroups) { + if (group.range.startLineNumber > lineNumber) { + break; + } + if (lineNumber >= group.range.startLineNumber && lineNumber <= group.range.endLineNumber) { + for (const section of group.sections) { + for (const setting of section.settings) { + if (setting.range.startLineNumber > lineNumber) { + break; + } + if (lineNumber >= setting.range.startLineNumber && lineNumber <= setting.range.endLineNumber) { + if (!this.isDefaultSettings() && setting.overrides.length) { + // Only one level because override settings cannot have override settings + for (const overrideSetting of setting.overrides) { + if (lineNumber >= overrideSetting.range.startLineNumber && lineNumber <= overrideSetting.range.endLineNumber) { + settings.push(overrideSetting); + } + } + } else { + settings.push(setting); + } + } + } + } + } + } + return settings; + } + + private onMouseOver(editPreferenceWidget: EditPreferenceWidget): void { + this.settingHighlighter.highlight(editPreferenceWidget.preferences[0]); + } + + private onEditSettingClicked(editPreferenceWidget: EditPreferenceWidget, e: IEditorMouseEvent): void { + const anchor = { x: e.event.posx + 1, y: e.event.posy + 10 }; + const actions = this.getSettings(editPreferenceWidget.getLine()).length === 1 ? this.getActions(editPreferenceWidget.preferences[0], this.getConfigurationsMap()[editPreferenceWidget.preferences[0].key]) + : editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.wrap(actions) + }); + } + + private getConfigurationsMap(): { [qualifiedKey: string]: IConfigurationPropertySchema } { + return Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); + } + + private getActions(setting: ISetting, jsonSchema: IJSONSchema): IAction[] { + if (jsonSchema.type === 'boolean') { + return [{ + id: 'truthyValue', + label: 'true', + enabled: true, + run: () => this.updateSetting(setting.key, true, setting) + }, { + id: 'falsyValue', + label: 'false', + enabled: true, + run: () => this.updateSetting(setting.key, false, setting) + }]; + } + if (jsonSchema.enum) { + return jsonSchema.enum.map(value => { + return { + id: value, + label: JSON.stringify(value), + enabled: true, + run: () => this.updateSetting(setting.key, value, setting) + }; + }); + } + return this.getDefaultActions(setting); + } + + private getDefaultActions(setting: ISetting): IAction[] { + const settingInOtherModel = this.associatedPreferencesModel.getPreference(setting.key); + if (this.isDefaultSettings()) { + return [{ + id: 'setDefaultValue', + label: settingInOtherModel ? nls.localize('replaceDefaultValue', "Replace in Settings") : nls.localize('copyDefaultValue', "Copy to Settings"), + enabled: true, + run: () => this.updateSetting(setting.key, setting.value, setting) + }]; + } + return []; + } + + private updateSetting(key: string, value: any, source: ISetting): void { + this._onUpdateSetting.fire({ key, value, source }); + } +} + +class SettingHighlighter extends Disposable { + + private fixedHighlighter: RangeHighlightDecorations; + private volatileHighlighter: RangeHighlightDecorations; + private highlightedSetting: ISetting; + + constructor(private editor: editorCommon.ICommonCodeEditor, private focusEventEmitter: Emitter, private clearFocusEventEmitter: Emitter, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(); + this.fixedHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); + this.volatileHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); + this.fixedHighlighter.onHighlghtRemoved(() => this.clearFocusEventEmitter.fire(this.highlightedSetting)); + this.volatileHighlighter.onHighlghtRemoved(() => this.clearFocusEventEmitter.fire(this.highlightedSetting)); + } + + highlight(setting: ISetting, fix: boolean = false) { + this.highlightedSetting = setting; + this.volatileHighlighter.removeHighlightRange(); + this.fixedHighlighter.removeHighlightRange(); + + const highlighter = fix ? this.fixedHighlighter : this.volatileHighlighter; + highlighter.highlightRange({ + range: setting.valueRange, + resource: this.editor.getModel().uri + }, this.editor); + + this.editor.revealLineInCenterIfOutsideViewport(setting.valueRange.startLineNumber, editorCommon.ScrollType.Smooth); + this.focusEventEmitter.fire(setting); + } + + clear(fix: boolean = false): void { + this.volatileHighlighter.removeHighlightRange(); + if (fix) { + this.fixedHighlighter.removeHighlightRange(); + } + this.clearFocusEventEmitter.fire(this.highlightedSetting); + } +} + +class UnsupportedWorkspaceSettingsRenderer extends Disposable { + + constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, + @IMarkerService private markerService: IMarkerService + ) { + super(); + this._register(this.configurationService.onDidUpdateConfiguration(() => this.render())); + } + + private getMarkerMessage(settingKey): string { + switch (settingKey) { + case 'php.validate.executablePath': + return nls.localize('unsupportedPHPExecutablePathSetting', "This setting must be a User Setting. To configure PHP for the workspace, open a PHP file and click on 'PHP Path' in the status bar."); + default: + return nls.localize('unsupportedWorkspaceSetting', "This setting must be a User Setting."); + } + } + + public render(): void { + const unsupportedWorkspaceKeys = this.configurationService.getUnsupportedWorkspaceKeys(); + if (unsupportedWorkspaceKeys.length) { + const markerData: IMarkerData[] = []; + for (const unsupportedKey of unsupportedWorkspaceKeys) { + const setting = this.workspaceSettingsEditorModel.getPreference(unsupportedKey); + if (setting) { + markerData.push({ + severity: Severity.Warning, + startLineNumber: setting.keyRange.startLineNumber, + startColumn: setting.keyRange.startColumn, + endLineNumber: setting.keyRange.endLineNumber, + endColumn: setting.keyRange.endColumn, + message: this.getMarkerMessage(unsupportedKey) + }); + } + } + if (markerData.length) { + this.markerService.changeOne('preferencesEditor', this.workspaceSettingsEditorModel.uri, markerData); + } else { + this.markerService.remove('preferencesEditor', [this.workspaceSettingsEditorModel.uri]); + } + } + } + + public dispose(): void { + this.markerService.remove('preferencesEditor', [this.workspaceSettingsEditorModel.uri]); + super.dispose(); + } +} + +class UnsupportedWorkbenchSettingsRenderer extends Disposable { + + private decorationIds: string[] = []; + private renderingDelayer: Delayer = new Delayer(200); + + constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, + ) { + super(); + this._register(this.editor.getModel().onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render()))); + } + + public render(): void { + const ranges: IRange[] = []; + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); + for (const settingsGroup of this.workspaceSettingsEditorModel.settingsGroups) { + for (const section of settingsGroup.sections) { + for (const setting of section.settings) { + if (configurationRegistry[setting.key] && configurationRegistry[setting.key].scope === ConfigurationScope.WINDOW) { + ranges.push({ + startLineNumber: setting.keyRange.startLineNumber, + startColumn: setting.keyRange.startColumn - 1, + endLineNumber: setting.valueRange.endLineNumber, + endColumn: setting.valueRange.endColumn + }); + } + } + } + } + this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range, this.editor.getModel())))); + } + + private static _DIM_CONFIGUARATION_ = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'dim-configuration', + beforeContentClassName: 'unsupportedWorkbenhSettingInfo', + hoverMessage: new MarkdownString().appendText(nls.localize('unsupportedWorkbenchSetting', "This setting cannot be applied now. It will be applied when you open this folder directly.")) + }); + + private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { + return { + range, + options: UnsupportedWorkbenchSettingsRenderer._DIM_CONFIGUARATION_ + }; + } + + public dispose(): void { + if (this.decorationIds) { + this.decorationIds = this.editor.changeDecorations(changeAccessor => { + return changeAccessor.deltaDecorations(this.decorationIds, []); + }); + } + super.dispose(); + } +} + +class WorkspaceConfigurationRenderer extends Disposable { + + private decorationIds: string[] = []; + private renderingDelayer: Delayer = new Delayer(200); + + constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService + ) { + super(); + this._register(this.editor.getModel().onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render()))); + } + + public render(): void { + if (this.workspaceContextService.hasMultiFolderWorkspace()) { + this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, [])); + + const ranges: IRange[] = []; + for (const settingsGroup of this.workspaceSettingsEditorModel.settingsGroups) { + for (const section of settingsGroup.sections) { + for (const setting of section.settings) { + if (setting.key !== 'settings') { + ranges.push({ + startLineNumber: setting.keyRange.startLineNumber, + startColumn: setting.keyRange.startColumn - 1, + endLineNumber: setting.valueRange.endLineNumber, + endColumn: setting.valueRange.endColumn + }); + } + } + } + } + this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range, this.editor.getModel())))); + } + } + + private static _DIM_CONFIGURATION_ = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'dim-configuration' + }); + + private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { + return { + range, + options: WorkspaceConfigurationRenderer._DIM_CONFIGURATION_ + }; + } + + public dispose(): void { + if (this.decorationIds) { + this.decorationIds = this.editor.changeDecorations(changeAccessor => { + return changeAccessor.deltaDecorations(this.decorationIds, []); + }); + } + super.dispose(); + } +} diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts new file mode 100644 index 0000000000..49ad1fd32e --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -0,0 +1,422 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/preferences'; +import * as network from 'vs/base/common/network'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as nls from 'vs/nls'; +import URI from 'vs/base/common/uri'; +import * as paths from 'vs/base/common/paths'; +import { ResourceMap } from 'vs/base/common/map'; +import * as labels from 'vs/base/common/labels'; +import * as strings from 'vs/base/common/strings'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Emitter } from 'vs/base/common/event'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { Position as EditorPosition, IEditor } from 'vs/platform/editor/common/editor'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; +import { IMessageService, Severity, IChoiceService } from 'vs/platform/message/common/message'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IPreferencesService, IPreferencesEditorModel, ISetting, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences'; +import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, WorkspaceConfigModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; +import { KeybindingsEditorInput } from 'vs/workbench/parts/preferences/browser/keybindingsEditor'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; + + +interface IWorkbenchSettingsConfiguration { + workbench: { + settings: { + openDefaultSettings: boolean; + } + }; +} + +const emptyEditableSettingsContent = '{\n}'; + +export class PreferencesService extends Disposable implements IPreferencesService { + + _serviceBrand: any; + + // TODO:@sandy merge these models into editor inputs by extending resource editor model + private defaultPreferencesEditorModels: ResourceMap>>; + private lastOpenedSettingsInput: PreferencesEditorInput = null; + + private _onDispose: Emitter = new Emitter(); + + constructor( + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IFileService private fileService: IFileService, + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, + @IMessageService private messageService: IMessageService, + @IChoiceService private choiceService: IChoiceService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IInstantiationService private instantiationService: IInstantiationService, + @IStorageService private storageService: IStorageService, + @IEnvironmentService private environmentService: IEnvironmentService, + @ITelemetryService private telemetryService: ITelemetryService, + @ITextModelService private textModelResolverService: ITextModelService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, + @IExtensionService private extensionService: IExtensionService, + @IKeybindingService keybindingService: IKeybindingService, + @IModelService private modelService: IModelService, + @IJSONEditingService private jsonEditingService: IJSONEditingService + ) { + super(); + this.defaultPreferencesEditorModels = new ResourceMap>>(); + this.editorGroupService.onEditorsChanged(() => { + const activeEditorInput = this.editorService.getActiveEditorInput(); + if (activeEditorInput instanceof PreferencesEditorInput) { + this.lastOpenedSettingsInput = activeEditorInput; + } + }); + + // The default keybindings.json updates based on keyboard layouts, so here we make sure + // if a model has been given out we update it accordingly. + keybindingService.onDidUpdateKeybindings(() => { + const model = modelService.getModel(this.defaultKeybindingsResource); + if (!model) { + // model has not been given out => nothing to do + return; + } + modelService.updateModel(model, defaultKeybindingsContents(keybindingService)); + }); + } + + readonly defaultSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/settings.json' }); + readonly defaultResourceSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/resourceSettings.json' }); + readonly defaultKeybindingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' }); + private readonly workspaceConfigSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'settings', path: '/workspaceSettings.json' }); + + get userSettingsResource(): URI { + return this.getEditableSettingsURI(ConfigurationTarget.USER); + } + + get workspaceSettingsResource(): URI { + return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE); + } + + getFolderSettingsResource(resource: URI): URI { + return this.getEditableSettingsURI(ConfigurationTarget.FOLDER, resource); + } + + resolveContent(uri: URI): TPromise { + const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE); + if (workspaceSettingsUri && workspaceSettingsUri.fsPath === uri.fsPath) { + return this.resolveSettingsContentFromWorkspaceConfiguration(); + } + return this.createPreferencesEditorModel(uri) + .then(preferencesEditorModel => preferencesEditorModel ? preferencesEditorModel.content : null); + } + + createPreferencesEditorModel(uri: URI): TPromise> { + let promise = this.defaultPreferencesEditorModels.get(uri); + if (promise) { + return promise; + } + + if (this.defaultSettingsResource.fsPath === uri.fsPath) { + promise = TPromise.join([this.extensionService.onReady(), this.fetchMostCommonlyUsedSettings()]) + .then(result => { + const mostCommonSettings = result[1]; + const model = this.instantiationService.createInstance(DefaultSettingsEditorModel, uri, mostCommonSettings, ConfigurationScope.WINDOW); + return model; + }); + this.defaultPreferencesEditorModels.set(uri, promise); + return promise; + } + + if (this.defaultResourceSettingsResource.fsPath === uri.fsPath) { + promise = TPromise.join([this.extensionService.onReady(), this.fetchMostCommonlyUsedSettings()]) + .then(result => { + const mostCommonSettings = result[1]; + const model = this.instantiationService.createInstance(DefaultSettingsEditorModel, uri, mostCommonSettings, ConfigurationScope.RESOURCE); + return model; + }); + this.defaultPreferencesEditorModels.set(uri, promise); + return promise; + } + + if (this.defaultKeybindingsResource.fsPath === uri.fsPath) { + const model = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri); + promise = TPromise.wrap(model); + this.defaultPreferencesEditorModels.set(uri, promise); + return promise; + } + + if (this.workspaceConfigSettingsResource.fsPath === uri.fsPath) { + promise = this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, uri); + this.defaultPreferencesEditorModels.set(uri, promise); + return promise; + } + + if (this.getEditableSettingsURI(ConfigurationTarget.USER).fsPath === uri.fsPath) { + return this.createEditableSettingsEditorModel(ConfigurationTarget.USER, uri); + } + + const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE); + if (workspaceSettingsUri && workspaceSettingsUri.fsPath === uri.fsPath) { + return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, workspaceSettingsUri); + } + + if (this.contextService.hasMultiFolderWorkspace()) { + return this.createEditableSettingsEditorModel(ConfigurationTarget.FOLDER, uri); + } + + return TPromise.wrap>(null); + } + + openGlobalSettings(): TPromise { + return this.doOpenSettings(ConfigurationTarget.USER, this.userSettingsResource); + } + + openWorkspaceSettings(): TPromise { + if (!this.contextService.hasWorkspace()) { + this.messageService.show(Severity.Info, nls.localize('openFolderFirst', "Open a folder first to create workspace settings")); + return TPromise.as(null); + } + return this.doOpenSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource); + } + + openFolderSettings(folder: URI): TPromise { + return this.doOpenSettings(ConfigurationTarget.FOLDER, this.getEditableSettingsURI(ConfigurationTarget.FOLDER, folder)); + } + + switchSettings(target: ConfigurationTarget, resource: URI): TPromise { + const activeEditor = this.editorService.getActiveEditor(); + const activeEditorInput = activeEditor.input; + if (activeEditorInput instanceof PreferencesEditorInput) { + return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource)) + .then(toInput => { + const replaceWith = new PreferencesEditorInput(this.getPreferencesEditorInputName(target, resource), toInput.getDescription(), this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(target)), toInput); + return this.editorService.replaceEditors([{ + toReplace: this.lastOpenedSettingsInput, + replaceWith + }], activeEditor.position).then(() => { + this.lastOpenedSettingsInput = replaceWith; + }); + }); + } else { + this.doOpenSettings(target, resource); + return undefined; + } + } + + openGlobalKeybindingSettings(textual: boolean): TPromise { + this.telemetryService.publicLog('openKeybindings', { textual }); + if (textual) { + const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + '\n[\n]'; + const editableKeybindings = URI.file(this.environmentService.appKeybindingsPath); + + // Create as needed and open in editor + return this.createIfNotExists(editableKeybindings, emptyContents).then(() => { + return this.editorService.openEditors([ + { input: { resource: this.defaultKeybindingsResource, options: { pinned: true }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }, position: EditorPosition.ONE }, + { input: { resource: editableKeybindings, options: { pinned: true } }, position: EditorPosition.TWO }, + ]).then(() => { + this.editorGroupService.focusGroup(EditorPosition.TWO); + }); + }); + + } + return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true }).then(() => null); + } + + configureSettingsForLanguage(language: string): void { + this.openGlobalSettings() + .then(editor => { + const codeEditor = getCodeEditor(editor); + this.getPosition(language, codeEditor) + .then(position => { + codeEditor.setPosition(position); + codeEditor.focus(); + }); + }); + } + + private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI): TPromise { + const openDefaultSettings = !!this.configurationService.getConfiguration().workbench.settings.openDefaultSettings; + return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource) + .then(editableSettingsEditorInput => { + if (openDefaultSettings) { + const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget)); + const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, editableSettingsEditorInput); + this.lastOpenedSettingsInput = preferencesEditorInput; + return this.editorService.openEditor(preferencesEditorInput, { pinned: true }); + } + return this.editorService.openEditor(editableSettingsEditorInput, { pinned: true }); + }); + } + + private getDefaultSettingsResource(configurationTarget: ConfigurationTarget): URI { + if (configurationTarget === ConfigurationTarget.FOLDER) { + return this.defaultResourceSettingsResource; + } + return this.defaultSettingsResource; + } + + private getPreferencesEditorInputName(target: ConfigurationTarget, resource: URI): string { + const name = getSettingsTargetName(target, resource, this.contextService); + return target === ConfigurationTarget.FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name; + } + + private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): TPromise { + return this.createSettingsIfNotExists(target, resource) + .then(() => this.editorService.createInput({ resource })); + } + + private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): TPromise { + const settingsUri = this.getEditableSettingsURI(configurationTarget, resource); + if (settingsUri) { + if (settingsUri.fsPath === this.workspaceConfigSettingsResource.fsPath) { + return TPromise.join([this.textModelResolverService.createModelReference(settingsUri), this.textModelResolverService.createModelReference(this.contextService.getWorkspace().configuration)]) + .then(([reference, workspaceConfigReference]) => this.instantiationService.createInstance(WorkspaceConfigModel, reference, workspaceConfigReference, configurationTarget, this._onDispose.event)); + } + return this.textModelResolverService.createModelReference(settingsUri) + .then(reference => this.instantiationService.createInstance(SettingsEditorModel, reference, configurationTarget)); + } + return TPromise.wrap(null); + } + + private resolveSettingsContentFromWorkspaceConfiguration(): TPromise { + if (this.contextService.hasMultiFolderWorkspace()) { + return this.textModelResolverService.createModelReference(this.contextService.getWorkspace().configuration) + .then(reference => { + const model = reference.object.textEditorModel; + const settingsContent = WorkspaceConfigModel.getSettingsContentFromConfigContent(model.getValue()); + reference.dispose(); + return TPromise.as(settingsContent ? settingsContent : emptyEditableSettingsContent); + }); + } + return TPromise.as(null); + } + + private getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): URI { + switch (configurationTarget) { + case ConfigurationTarget.USER: + return URI.file(this.environmentService.appSettingsPath); + case ConfigurationTarget.WORKSPACE: + const workspace = this.contextService.getWorkspace(); + if (this.contextService.hasFolderWorkspace()) { + // {{SQL CARBON EDIT}} + return this.toResource(paths.join('.sqlops', 'settings.json'), workspace.roots[0]); + } + if (this.contextService.hasMultiFolderWorkspace()) { + return workspace.configuration; + } + return null; + case ConfigurationTarget.FOLDER: + const root = this.contextService.getRoot(resource); + // {{SQL CARBON EDIT}} + return root ? this.toResource(paths.join('.sqlops', 'settings.json'), root) : null; + } + return null; + } + + private toResource(relativePath: string, root: URI): URI { + return URI.file(paths.join(root.fsPath, relativePath)); + } + + private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): TPromise { + if (this.contextService.hasMultiFolderWorkspace() && target === ConfigurationTarget.WORKSPACE) { + if (!this.configurationService.keys().workspace.length) { + return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(null, () => { }); + } + } + return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { }); + } + + private createIfNotExists(resource: URI, contents: string): TPromise { + return this.fileService.resolveContent(resource, { acceptTextOnly: true }).then(null, error => { + if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) { + return this.fileService.updateContent(resource, contents).then(null, error => { + return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", labels.getPathLabel(resource, this.contextService, this.environmentService), error))); + }); + } + + return TPromise.wrapError(error); + }); + } + + private fetchMostCommonlyUsedSettings(): TPromise { + return TPromise.wrap([ + 'files.autoSave', + 'editor.fontSize', + 'editor.fontFamily', + 'editor.tabSize', + 'editor.renderWhitespace', + 'editor.cursorStyle', + 'editor.multiCursorModifier', + 'editor.insertSpaces', + 'editor.wordWrap', + 'files.exclude', + 'files.associations' + ]); + } + + private getPosition(language: string, codeEditor: ICommonCodeEditor): TPromise { + return this.createPreferencesEditorModel(this.userSettingsResource) + .then((settingsModel: IPreferencesEditorModel) => { + const languageKey = `[${language}]`; + let setting = settingsModel.getPreference(languageKey); + const model = codeEditor.getModel(); + const configuration = this.configurationService.getConfiguration<{ tabSize: number; insertSpaces: boolean }>('editor'); + const { eol } = this.configurationService.getConfiguration<{ eol: string }>('files'); + if (setting) { + if (setting.overrides.length) { + const lastSetting = setting.overrides[setting.overrides.length - 1]; + let content; + if (lastSetting.valueRange.endLineNumber === setting.range.endLineNumber) { + content = ',' + eol + this.spaces(2, configuration) + eol + this.spaces(1, configuration); + } else { + content = ',' + eol + this.spaces(2, configuration); + } + const editOperation = EditOperation.insert(new Position(lastSetting.valueRange.endLineNumber, lastSetting.valueRange.endColumn), content); + model.pushEditOperations([], [editOperation], () => []); + return { lineNumber: lastSetting.valueRange.endLineNumber + 1, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber + 1) }; + } + return { lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 }; + } + return this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: languageKey, value: {} }, { donotSave: true }) + .then(() => { + setting = settingsModel.getPreference(languageKey); + let content = eol + this.spaces(2, configuration) + eol + this.spaces(1, configuration); + let editOperation = EditOperation.insert(new Position(setting.valueRange.endLineNumber, setting.valueRange.endColumn - 1), content); + model.pushEditOperations([], [editOperation], () => []); + let lineNumber = setting.valueRange.endLineNumber + 1; + settingsModel.dispose(); + return { lineNumber, column: model.getLineMaxColumn(lineNumber) }; + }); + }); + } + + private spaces(count: number, { tabSize, insertSpaces }: { tabSize: number; insertSpaces: boolean }): string { + return insertSpaces ? strings.repeat(' ', tabSize * count) : strings.repeat('\t', count); + } + + public dispose(): void { + this._onDispose.fire(); + this.defaultPreferencesEditorModels.clear(); + super.dispose(); + } +} diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts new file mode 100644 index 0000000000..dd931b3101 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -0,0 +1,652 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import URI from 'vs/base/common/uri'; +import { Dimension } from 'vs/base/browser/builder'; +import * as DOM from 'vs/base/browser/dom'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Widget } from 'vs/base/browser/ui/widget'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference, IViewZone, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { InputBox, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ISettingsGroup, IPreferencesService, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { attachInputBoxStyler, attachStylerCallback, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { Position } from 'vs/editor/common/core/position'; +import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { buttonBackground, buttonForeground, badgeForeground, badgeBackground, contrastBorder, errorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ISelectBoxStyles, defaultStyles } from 'vs/base/browser/ui/selectBox/selectBox'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Color } from 'vs/base/common/color'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { MarkdownString } from 'vs/base/common/htmlContent'; + +export class SettingsHeaderWidget extends Widget implements IViewZone { + + private id: number; + private _domNode: HTMLElement; + + private titleContainer: HTMLElement; + private messageElement: HTMLElement; + + constructor(private editor: ICodeEditor, private title: string) { + super(); + this.create(); + this._register(this.editor.onDidChangeConfiguration(() => this.layout())); + this._register(this.editor.onDidLayoutChange(() => this.layout())); + } + + get domNode(): HTMLElement { + return this._domNode; + } + + get heightInLines(): number { + return 1; + } + + get afterLineNumber(): number { + return 0; + } + + private create() { + this._domNode = DOM.$('.settings-header-widget'); + + this.titleContainer = DOM.append(this._domNode, DOM.$('.title-container')); + if (this.title) { + DOM.append(this.titleContainer, DOM.$('.title')).textContent = this.title; + } + this.messageElement = DOM.append(this.titleContainer, DOM.$('.message')); + if (this.title) { + this.messageElement.style.paddingLeft = '12px'; + } + + this.editor.changeViewZones(accessor => { + this.id = accessor.addZone(this); + this.layout(); + }); + } + + public setMessage(message: string): void { + this.messageElement.textContent = message; + } + + private layout(): void { + const configuration = this.editor.getConfiguration(); + this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px'; + if (!configuration.contribInfo.folding) { + this.titleContainer.style.paddingLeft = '12px'; + } + } + + public dispose() { + this.editor.changeViewZones(accessor => { + accessor.removeZone(this.id); + }); + super.dispose(); + } +} + +export class SettingsGroupTitleWidget extends Widget implements IViewZone { + + private id: number; + private _afterLineNumber: number; + private _domNode: HTMLElement; + + private titleContainer: HTMLElement; + private icon: HTMLElement; + private title: HTMLElement; + + private _onToggled = this._register(new Emitter()); + public onToggled: Event = this._onToggled.event; + + private previousPosition: Position; + + constructor(private editor: ICodeEditor, public settingsGroup: ISettingsGroup) { + super(); + this.create(); + this._register(this.editor.onDidChangeConfiguration(() => this.layout())); + this._register(this.editor.onDidLayoutChange(() => this.layout())); + this._register(this.editor.onDidChangeCursorPosition((e) => this.onCursorChange(e))); + } + + get domNode(): HTMLElement { + return this._domNode; + } + + get heightInLines(): number { + return 1.5; + } + + get afterLineNumber(): number { + return this._afterLineNumber; + } + + private create() { + this._domNode = DOM.$('.settings-group-title-widget'); + + this.titleContainer = DOM.append(this._domNode, DOM.$('.title-container')); + this.titleContainer.tabIndex = 0; + this.onclick(this.titleContainer, () => this.toggle()); + this.onkeydown(this.titleContainer, (e) => this.onKeyDown(e)); + const focusTracker = this._register(DOM.trackFocus(this.titleContainer)); + focusTracker.addFocusListener(() => this.toggleFocus(true)); + focusTracker.addBlurListener(() => this.toggleFocus(false)); + + this.icon = DOM.append(this.titleContainer, DOM.$('.expand-collapse-icon')); + this.title = DOM.append(this.titleContainer, DOM.$('.title')); + this.title.textContent = this.settingsGroup.title + ` (${this.settingsGroup.sections.reduce((count, section) => count + section.settings.length, 0)})`; + + this.layout(); + } + + public render() { + this._afterLineNumber = this.settingsGroup.range.startLineNumber - 2; + this.editor.changeViewZones(accessor => { + this.id = accessor.addZone(this); + this.layout(); + }); + } + + public toggleCollapse(collapse: boolean) { + DOM.toggleClass(this.titleContainer, 'collapsed', collapse); + } + + public toggleFocus(focus: boolean): void { + DOM.toggleClass(this.titleContainer, 'focused', focus); + } + + public isCollapsed(): boolean { + return DOM.hasClass(this.titleContainer, 'collapsed'); + } + + private layout(): void { + const configuration = this.editor.getConfiguration(); + const layoutInfo = this.editor.getLayoutInfo(); + this._domNode.style.width = layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth + 'px'; + this.titleContainer.style.lineHeight = configuration.lineHeight + 3 + 'px'; + this.titleContainer.style.height = configuration.lineHeight + 3 + 'px'; + this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px'; + this.icon.style.minWidth = `${this.getIconSize(16)}px`; + } + + private getIconSize(minSize: number): number { + const fontSize = this.editor.getConfiguration().fontInfo.fontSize; + return fontSize > 8 ? Math.max(fontSize, minSize) : 12; + } + + private onKeyDown(keyboardEvent: IKeyboardEvent): void { + switch (keyboardEvent.keyCode) { + case KeyCode.Enter: + case KeyCode.Space: + this.toggle(); + break; + case KeyCode.LeftArrow: + this.collapse(true); + break; + case KeyCode.RightArrow: + this.collapse(false); + break; + case KeyCode.UpArrow: + if (this.settingsGroup.range.startLineNumber - 3 !== 1) { + this.editor.focus(); + const lineNumber = this.settingsGroup.range.startLineNumber - 2; + this.editor.setPosition({ lineNumber, column: this.editor.getModel().getLineMinColumn(lineNumber) }); + } + break; + case KeyCode.DownArrow: + const lineNumber = this.isCollapsed() ? this.settingsGroup.range.startLineNumber : this.settingsGroup.range.startLineNumber - 1; + this.editor.focus(); + this.editor.setPosition({ lineNumber, column: this.editor.getModel().getLineMinColumn(lineNumber) }); + break; + } + } + + private toggle() { + this.collapse(!this.isCollapsed()); + } + + private collapse(collapse: boolean) { + if (collapse !== this.isCollapsed()) { + DOM.toggleClass(this.titleContainer, 'collapsed', collapse); + this._onToggled.fire(collapse); + } + } + + private onCursorChange(e: ICursorPositionChangedEvent): void { + if (e.source !== 'mouse' && this.focusTitle(e.position)) { + this.titleContainer.focus(); + } + } + + private focusTitle(currentPosition: Position): boolean { + const previousPosition = this.previousPosition; + this.previousPosition = currentPosition; + if (!previousPosition) { + return false; + } + if (previousPosition.lineNumber === currentPosition.lineNumber) { + return false; + } + if (currentPosition.lineNumber === this.settingsGroup.range.startLineNumber - 1 || currentPosition.lineNumber === this.settingsGroup.range.startLineNumber - 2) { + return true; + } + if (this.isCollapsed() && currentPosition.lineNumber === this.settingsGroup.range.endLineNumber) { + return true; + } + return false; + } + + public dispose() { + this.editor.changeViewZones(accessor => { + accessor.removeZone(this.id); + }); + super.dispose(); + } +} + +export class SettingsTargetsWidget extends Widget { + + public actionRunner: IActionRunner; + private settingsTargetsContainer: HTMLSelectElement; + private targetLabel: HTMLSelectElement; + private targetDetails: HTMLSelectElement; + + private _onDidTargetChange: Emitter = new Emitter(); + public readonly onDidTargetChange: Event = this._onDidTargetChange.event; + + private borderColor: Color; + + constructor(parent: HTMLElement, private uri: URI, private target: ConfigurationTarget, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IPreferencesService private preferencesService: IPreferencesService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IThemeService themeService: IThemeService) { + super(); + + this.borderColor = defaultStyles.selectBorder; + this.create(parent); + this._register(attachSelectBoxStyler(this, themeService, { + selectBackground: SIDE_BAR_BACKGROUND + })); + } + + public setTarget(uri: URI, target: ConfigurationTarget): void { + this.uri = uri; + this.target = target; + this.updateLabel(); + } + + private create(parent: HTMLElement): void { + this.settingsTargetsContainer = DOM.append(parent, DOM.$('.settings-targets-widget')); + this.settingsTargetsContainer.style.width = this.workspaceContextService.hasMultiFolderWorkspace() ? '200px' : '150px'; + + const targetElement = DOM.append(this.settingsTargetsContainer, DOM.$('.settings-target')); + this.targetLabel = DOM.append(targetElement, DOM.$('.settings-target-label')); + this.targetDetails = DOM.append(targetElement, DOM.$('.settings-target-details')); + this.updateLabel(); + + this.onclick(this.settingsTargetsContainer, e => this.showContextMenu(e)); + + DOM.append(this.settingsTargetsContainer, DOM.$('.settings-target-dropdown-icon.octicon.octicon-triangle-down')); + + this.applyStyles(); + } + + private updateLabel(): void { + this.targetLabel.textContent = getSettingsTargetName(this.target, this.uri, this.workspaceContextService); + const details = ConfigurationTarget.FOLDER === this.target ? localize('folderSettingsDetails', "Folder Settings") : ''; + this.targetDetails.textContent = details; + DOM.toggleClass(this.targetDetails, 'empty', !details); + } + + private showContextMenu(event: IMouseEvent): void { + const actions = this.getSettingsTargetsActions(); + let elementPosition = DOM.getDomNodePagePosition(this.settingsTargetsContainer); + const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 5 }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.wrap(actions) + }); + event.stopPropagation(); + event.preventDefault(); + } + + private getSettingsTargetsActions(): IAction[] { + const actions: IAction[] = []; + const userSettingsResource = this.preferencesService.userSettingsResource; + actions.push({ + id: 'userSettingsTarget', + label: getSettingsTargetName(ConfigurationTarget.USER, userSettingsResource, this.workspaceContextService), + checked: this.uri.fsPath === userSettingsResource.fsPath, + enabled: true, + run: () => this.onTargetClicked(userSettingsResource) + }); + + if (this.workspaceContextService.hasWorkspace()) { + const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource; + actions.push({ + id: 'workspaceSettingsTarget', + label: getSettingsTargetName(ConfigurationTarget.WORKSPACE, workspaceSettingsResource, this.workspaceContextService), + checked: this.uri.fsPath === workspaceSettingsResource.fsPath, + enabled: true, + run: () => this.onTargetClicked(workspaceSettingsResource) + }); + } + + if (this.workspaceContextService.hasMultiFolderWorkspace()) { + actions.push(new Separator()); + actions.push(...this.workspaceContextService.getWorkspace().roots.map((root, index) => { + return { + id: 'folderSettingsTarget' + index, + label: getSettingsTargetName(ConfigurationTarget.FOLDER, root, this.workspaceContextService), + checked: this.uri.fsPath === root.fsPath, + enabled: true, + run: () => this.onTargetClicked(root) + }; + })); + } + + return actions; + } + + private onTargetClicked(target: URI): void { + if (this.uri.fsPath === target.fsPath) { + return; + } + this._onDidTargetChange.fire(target); + } + + style(styles: ISelectBoxStyles): void { + this.borderColor = styles.selectBorder; + this.applyStyles(); + } + + private applyStyles(): void { + if (this.settingsTargetsContainer) { + this.settingsTargetsContainer.style.border = this.borderColor ? `1px solid ${this.borderColor}` : null; + } + } +} + +export interface SearchOptions extends IInputOptions { + focusKey?: IContextKey; +} + +export class SearchWidget extends Widget { + + public domNode: HTMLElement; + + private countElement: HTMLElement; + private searchContainer: HTMLElement; + private inputBox: InputBox; + + private _onDidChange: Emitter = this._register(new Emitter()); + public readonly onDidChange: Event = this._onDidChange.event; + + private _onNavigate: Emitter = this._register(new Emitter()); + public readonly onNavigate: Event = this._onNavigate.event; + + private _onFocus: Emitter = this._register(new Emitter()); + public readonly onFocus: Event = this._onFocus.event; + + constructor(parent: HTMLElement, protected options: SearchOptions, + @IContextViewService private contextViewService: IContextViewService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService private themeService: IThemeService + ) { + super(); + this.create(parent); + } + + private create(parent: HTMLElement) { + this.domNode = DOM.append(parent, DOM.$('div.settings-header-widget')); + this.createSearchContainer(DOM.append(this.domNode, DOM.$('div.settings-search-container'))); + this.countElement = DOM.append(this.domNode, DOM.$('.settings-count-widget')); + this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder }, colors => { + const background = colors.badgeBackground ? colors.badgeBackground.toString() : null; + const border = colors.contrastBorder ? colors.contrastBorder.toString() : null; + + this.countElement.style.backgroundColor = background; + + this.countElement.style.borderWidth = border ? '1px' : null; + this.countElement.style.borderStyle = border ? 'solid' : null; + this.countElement.style.borderColor = border; + + this.styleCountElementForeground(); + })); + this.inputBox.inputElement.setAttribute('aria-live', 'assertive'); + + const focusTracker = this._register(DOM.trackFocus(this.inputBox.inputElement)); + this._register(focusTracker.addFocusListener(() => this._onFocus.fire())); + + if (this.options.focusKey) { + this._register(focusTracker.addFocusListener(() => this.options.focusKey.set(true))); + this._register(focusTracker.addBlurListener(() => this.options.focusKey.set(false))); + } + } + + private createSearchContainer(searchContainer: HTMLElement) { + this.searchContainer = searchContainer; + const searchInput = DOM.append(this.searchContainer, DOM.$('div.settings-search-input')); + this.inputBox = this._register(this.createInputBox(searchInput)); + this._register(this.inputBox.onDidChange(value => this._onDidChange.fire(value))); + this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown(e)); + } + + protected createInputBox(parent: HTMLElement): InputBox { + const box = this._register(new InputBox(parent, this.contextViewService, this.options)); + this._register(attachInputBoxStyler(box, this.themeService)); + + return box; + } + + public showMessage(message: string, count: number): void { + this.countElement.textContent = message; + this.inputBox.inputElement.setAttribute('aria-label', message); + DOM.toggleClass(this.countElement, 'no-results', count === 0); + this.inputBox.inputElement.style.paddingRight = DOM.getTotalWidth(this.countElement) + 20 + 'px'; + this.styleCountElementForeground(); + } + + private styleCountElementForeground() { + const colorId = DOM.hasClass(this.countElement, 'no-results') ? errorForeground : badgeForeground; + const color = this.themeService.getTheme().getColor(colorId); + this.countElement.style.color = color ? color.toString() : null; + } + + public layout(dimension: Dimension) { + if (dimension.width < 400) { + DOM.addClass(this.countElement, 'hide'); + this.inputBox.inputElement.style.paddingRight = '0px'; + } else { + DOM.removeClass(this.countElement, 'hide'); + this.inputBox.inputElement.style.paddingRight = DOM.getTotalWidth(this.countElement) + 20 + 'px'; + } + } + + public focus() { + this.inputBox.focus(); + if (this.getValue()) { + this.inputBox.select(); + } + } + + public hasFocus(): boolean { + return this.inputBox.hasFocus(); + } + + public clear() { + this.inputBox.value = ''; + } + + public getValue(): string { + return this.inputBox.value; + } + + public setValue(value: string): string { + return this.inputBox.value = value; + } + + private _onKeyDown(keyboardEvent: IKeyboardEvent): void { + let handled = false; + switch (keyboardEvent.keyCode) { + case KeyCode.Enter: + this._onNavigate.fire(keyboardEvent.shiftKey); + handled = true; + break; + case KeyCode.Escape: + this.clear(); + handled = true; + break; + } + if (handled) { + keyboardEvent.preventDefault(); + keyboardEvent.stopPropagation(); + } + } + + public dispose(): void { + if (this.options.focusKey) { + this.options.focusKey.set(false); + } + super.dispose(); + } +} + +export class FloatingClickWidget extends Widget implements IOverlayWidget { + + private _domNode: HTMLElement; + + private _onClick: Emitter = this._register(new Emitter()); + public onClick: Event = this._onClick.event; + + constructor( + private editor: ICodeEditor, + private label: string, + private keyBindingAction: string, + @IKeybindingService keybindingService: IKeybindingService, + @IThemeService private themeService: IThemeService + ) { + super(); + + if (keyBindingAction) { + let keybinding = keybindingService.lookupKeybinding(keyBindingAction); + if (keybinding) { + this.label += ' (' + keybinding.getLabel() + ')'; + } + } + } + + public render() { + this._domNode = DOM.$('.floating-click-widget'); + this._register(attachStylerCallback(this.themeService, { buttonBackground, buttonForeground }, colors => { + this._domNode.style.backgroundColor = colors.buttonBackground; + this._domNode.style.color = colors.buttonForeground; + })); + + DOM.append(this._domNode, DOM.$('')).textContent = this.label; + this.onclick(this._domNode, e => this._onClick.fire()); + this.editor.addOverlayWidget(this); + } + + public dispose(): void { + this.editor.removeOverlayWidget(this); + super.dispose(); + } + + public getId(): string { + return 'editor.overlayWidget.floatingClickWidget'; + } + + public getDomNode(): HTMLElement { + return this._domNode; + } + + public getPosition(): IOverlayWidgetPosition { + return { + preference: OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER + }; + } +} + +export class EditPreferenceWidget extends Disposable { + + public static GLYPH_MARGIN_CLASS_NAME = 'edit-preferences-widget'; + + private _line: number; + private _preferences: T[]; + + private _editPreferenceDecoration: string[]; + + private _onClick: Emitter = new Emitter(); + public get onClick(): Event { return this._onClick.event; } + + constructor(private editor: ICodeEditor + ) { + super(); + this._editPreferenceDecoration = []; + this._register(this.editor.onMouseDown((e: IEditorMouseEvent) => { + if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || /* after last line */ e.target.detail || !this.isVisible()) { + return; + } + this._onClick.fire(e); + })); + } + + get preferences(): T[] { + return this._preferences; + } + + getLine(): number { + return this._line; + } + + show(line: number, hoverMessage: string, preferences: T[]): void { + this._preferences = preferences; + const newDecoration: editorCommon.IModelDeltaDecoration[] = []; + this._line = line; + newDecoration.push({ + options: { + glyphMarginClassName: EditPreferenceWidget.GLYPH_MARGIN_CLASS_NAME, + glyphMarginHoverMessage: new MarkdownString().appendText(hoverMessage), + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + }, + range: { + startLineNumber: line, + startColumn: 1, + endLineNumber: line, + endColumn: 1 + } + }); + this._editPreferenceDecoration = this.editor.deltaDecorations(this._editPreferenceDecoration, newDecoration); + } + + hide(): void { + this._editPreferenceDecoration = this.editor.deltaDecorations(this._editPreferenceDecoration, []); + } + + isVisible(): boolean { + return this._editPreferenceDecoration.length > 0; + } + + dispose(): void { + this.hide(); + super.dispose(); + } +} diff --git a/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.ts b/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.ts new file mode 100644 index 0000000000..cdcdb0a8d3 --- /dev/null +++ b/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.ts @@ -0,0 +1,563 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { distinct } from 'vs/base/common/arrays'; +import * as strings from 'vs/base/common/strings'; +import { OperatingSystem, language, LANGUAGE_DEFAULT } from 'vs/base/common/platform'; +import { IMatch, IFilter, or, matchesContiguousSubString, matchesPrefix, matchesCamelCase, matchesWords } from 'vs/base/common/filters'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ResolvedKeybinding, ResolvedKeybindingPart } from 'vs/base/common/keyCodes'; +import { AriaLabelProvider, UserSettingsLabelProvider, UILabelProvider, ModifierLabels as ModLabels } from 'vs/base/common/keybindingLabels'; +import { CommonEditorRegistry, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { MenuRegistry, ILocalizedString, SyncActionDescriptor, ICommandAction } from 'vs/platform/actions/common/actions'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { EditorModel } from 'vs/workbench/common/editor'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; + +export const KEYBINDING_ENTRY_TEMPLATE_ID = 'keybinding.entry.template'; +export const KEYBINDING_HEADER_TEMPLATE_ID = 'keybinding.header.template'; + +export interface KeybindingMatch { + ctrlKey?: boolean; + shiftKey?: boolean; + altKey?: boolean; + metaKey?: boolean; + keyCode?: boolean; +} + +export interface KeybindingMatches { + firstPart: KeybindingMatch; + chordPart: KeybindingMatch; +} + +export interface IListEntry { + id: string; + templateId: string; +} + +export interface IKeybindingItemEntry extends IListEntry { + keybindingItem: IKeybindingItem; + commandIdMatches?: IMatch[]; + commandLabelMatches?: IMatch[]; + commandDefaultLabelMatches?: IMatch[]; + sourceMatches?: IMatch[]; + whenMatches?: IMatch[]; + keybindingMatches?: KeybindingMatches; +} + +export interface IKeybindingItem { + keybinding: ResolvedKeybinding; + keybindingItem: ResolvedKeybindingItem; + commandLabel: string; + commandDefaultLabel: string; + command: string; + source: string; + when: string; +} + +interface ModifierLabels { + ui: ModLabels; + aria: ModLabels; + user: ModLabels; +} + +const wordFilter = or(matchesPrefix, matchesWords, matchesContiguousSubString); + +export class KeybindingsEditorModel extends EditorModel { + + private _keybindingItems: IKeybindingItem[]; + private _keybindingItemsSortedByPrecedence: IKeybindingItem[]; + private modifierLabels: ModifierLabels; + + constructor( + private os: OperatingSystem, + @IKeybindingService private keybindingsService: IKeybindingService, + @IExtensionService private extensionService: IExtensionService + ) { + super(); + this.modifierLabels = { + ui: UILabelProvider.modifierLabels[os], + aria: AriaLabelProvider.modifierLabels[os], + user: UserSettingsLabelProvider.modifierLabels[os] + }; + } + + public fetch(searchValue: string, sortByPrecedence: boolean = false): IKeybindingItemEntry[] { + searchValue = searchValue.trim(); + const quoteAtFirstChar = searchValue.charAt(0) === '"'; + const quoteAtLastChar = searchValue.charAt(searchValue.length - 1) === '"'; + if (quoteAtFirstChar) { + searchValue = searchValue.substring(1); + } + if (quoteAtLastChar) { + searchValue = searchValue.substring(0, searchValue.length - 1); + } + searchValue = searchValue.trim(); + return this.fetchKeybindingItems(sortByPrecedence ? this._keybindingItemsSortedByPrecedence : this._keybindingItems, searchValue, quoteAtFirstChar && quoteAtLastChar); + } + + private fetchKeybindingItems(keybindingItems: IKeybindingItem[], searchValue: string, completeMatch: boolean): IKeybindingItemEntry[] { + if (!searchValue) { + return keybindingItems.map(keybindingItem => ({ id: KeybindingsEditorModel.getId(keybindingItem), keybindingItem, templateId: KEYBINDING_ENTRY_TEMPLATE_ID })); + } + + const result: IKeybindingItemEntry[] = []; + const words = searchValue.split(' '); + const keybindingWords = this.splitKeybindingWords(words); + for (const keybindingItem of keybindingItems) { + let keybindingMatches = new KeybindingItemMatches(this.modifierLabels, keybindingItem, searchValue, words, keybindingWords, completeMatch); + if (keybindingMatches.commandIdMatches + || keybindingMatches.commandLabelMatches + || keybindingMatches.commandDefaultLabelMatches + || keybindingMatches.sourceMatches + || keybindingMatches.whenMatches + || keybindingMatches.keybindingMatches) { + result.push({ + id: KeybindingsEditorModel.getId(keybindingItem), + templateId: KEYBINDING_ENTRY_TEMPLATE_ID, + commandLabelMatches: keybindingMatches.commandLabelMatches, + commandDefaultLabelMatches: keybindingMatches.commandDefaultLabelMatches, + keybindingItem, + keybindingMatches: keybindingMatches.keybindingMatches, + commandIdMatches: keybindingMatches.commandIdMatches, + sourceMatches: keybindingMatches.sourceMatches, + whenMatches: keybindingMatches.whenMatches + }); + } + } + return result; + } + + private splitKeybindingWords(wordsSeparatedBySpaces: string[]): string[] { + const result = []; + for (const word of wordsSeparatedBySpaces) { + result.push(...word.split('+')); + } + return result; + } + + public resolve(): TPromise { + return this.extensionService.onReady() + .then(() => { + const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); + const editorActions = CommonEditorRegistry.getEditorActions().reduce((editorActions, editorAction) => { + editorActions[editorAction.id] = editorAction; + return editorActions; + }, {}); + + this._keybindingItemsSortedByPrecedence = []; + const boundCommands: Map = new Map(); + for (const keybinding of this.keybindingsService.getKeybindings()) { + if (keybinding.command) { // Skip keybindings without commands + this._keybindingItemsSortedByPrecedence.push(KeybindingsEditorModel.toKeybindingEntry(keybinding.command, keybinding, workbenchActionsRegistry, editorActions)); + boundCommands.set(keybinding.command, true); + } + } + + const commandsWithDefaultKeybindings = this.keybindingsService.getDefaultKeybindings().map(keybinding => keybinding.command); + for (const command of KeybindingResolver.getAllUnboundCommands(boundCommands)) { + const keybindingItem = new ResolvedKeybindingItem(null, command, null, null, commandsWithDefaultKeybindings.indexOf(command) === -1); + this._keybindingItemsSortedByPrecedence.push(KeybindingsEditorModel.toKeybindingEntry(command, keybindingItem, workbenchActionsRegistry, editorActions)); + } + this._keybindingItems = this._keybindingItemsSortedByPrecedence.slice(0).sort((a, b) => KeybindingsEditorModel.compareKeybindingData(a, b)); + return this; + }); + } + + private static getId(keybindingItem: IKeybindingItem): string { + return keybindingItem.command + (keybindingItem.keybinding ? keybindingItem.keybinding.getAriaLabel() : '') + keybindingItem.source + keybindingItem.when; + } + + private static compareKeybindingData(a: IKeybindingItem, b: IKeybindingItem): number { + if (a.keybinding && !b.keybinding) { + return -1; + } + if (b.keybinding && !a.keybinding) { + return 1; + } + if (a.commandLabel && !b.commandLabel) { + return -1; + } + if (b.commandLabel && !a.commandLabel) { + return 1; + } + if (a.commandLabel && b.commandLabel) { + if (a.commandLabel !== b.commandLabel) { + return a.commandLabel.localeCompare(b.commandLabel); + } + } + if (a.command === b.command) { + return a.keybindingItem.isDefault ? 1 : -1; + } + return a.command.localeCompare(b.command); + } + + private static toKeybindingEntry(command: string, keybindingItem: ResolvedKeybindingItem, workbenchActionsRegistry: IWorkbenchActionRegistry, editorActions: {}): IKeybindingItem { + const workbenchAction = workbenchActionsRegistry.getWorkbenchAction(command); + const menuCommand = MenuRegistry.getCommand(command); + const editorAction: EditorAction = editorActions[command]; + return { + keybinding: keybindingItem.resolvedKeybinding, + keybindingItem, + command, + commandLabel: KeybindingsEditorModel.getCommandLabel(workbenchAction, menuCommand, editorAction), + commandDefaultLabel: KeybindingsEditorModel.getCommandDefaultLabel(workbenchAction, menuCommand, workbenchActionsRegistry), + when: keybindingItem.when ? keybindingItem.when.serialize() : '', + source: keybindingItem.isDefault ? localize('default', "Default") : localize('user', "User") + }; + } + + private static getCommandDefaultLabel(workbenchAction: SyncActionDescriptor, menuCommand: ICommandAction, workbenchActionsRegistry: IWorkbenchActionRegistry): string { + if (language !== LANGUAGE_DEFAULT) { + if (workbenchAction) { + return workbenchActionsRegistry.getAlias(workbenchAction.id); + } + + if (menuCommand && menuCommand.title && (menuCommand.title).original) { + return (menuCommand.title).original; + } + } + return null; + } + + private static getCommandLabel(workbenchAction: SyncActionDescriptor, menuCommand: ICommandAction, editorAction: EditorAction): string { + if (workbenchAction) { + return workbenchAction.label; + } + + if (menuCommand) { + return typeof menuCommand.title === 'string' ? menuCommand.title : menuCommand.title.value; + } + + if (editorAction) { + return editorAction.label; + } + + return ''; + } +} + +class KeybindingItemMatches { + + public readonly commandIdMatches: IMatch[] = null; + public readonly commandLabelMatches: IMatch[] = null; + public readonly commandDefaultLabelMatches: IMatch[] = null; + public readonly sourceMatches: IMatch[] = null; + public readonly whenMatches: IMatch[] = null; + public readonly keybindingMatches: KeybindingMatches = null; + + constructor(private modifierLabels: ModifierLabels, keybindingItem: IKeybindingItem, private searchValue: string, private words: string[], private keybindingWords: string[], private completeMatch: boolean) { + this.commandIdMatches = this.matches(searchValue, keybindingItem.command, or(matchesWords, matchesCamelCase), words); + this.commandLabelMatches = keybindingItem.commandLabel ? this.matches(searchValue, keybindingItem.commandLabel, (word, wordToMatchAgainst) => matchesWords(word, keybindingItem.commandLabel, true), words) : null; + this.commandDefaultLabelMatches = keybindingItem.commandDefaultLabel ? this.matches(searchValue, keybindingItem.commandDefaultLabel, (word, wordToMatchAgainst) => matchesWords(word, keybindingItem.commandDefaultLabel, true), words) : null; + this.sourceMatches = this.matches(searchValue, keybindingItem.source, (word, wordToMatchAgainst) => matchesWords(word, keybindingItem.source, true), words); + this.whenMatches = keybindingItem.when ? this.matches(searchValue, keybindingItem.when, or(matchesWords, matchesCamelCase), words) : null; + this.keybindingMatches = keybindingItem.keybinding ? this.matchesKeybinding(keybindingItem.keybinding, searchValue, keybindingWords) : null; + } + + private matches(searchValue: string, wordToMatchAgainst: string, wordMatchesFilter: IFilter, words: string[]): IMatch[] { + let matches = wordFilter(searchValue, wordToMatchAgainst); + if (!matches) { + matches = this.matchesWords(words, wordToMatchAgainst, wordMatchesFilter); + } + if (matches) { + matches = this.filterAndSort(matches); + } + return matches; + } + + private matchesWords(words: string[], wordToMatchAgainst: string, wordMatchesFilter: IFilter): IMatch[] { + let matches = []; + for (const word of words) { + const wordMatches = wordMatchesFilter(word, wordToMatchAgainst); + if (wordMatches) { + matches = [...(matches || []), ...wordMatches]; + } else { + matches = null; + break; + } + } + return matches; + } + + private filterAndSort(matches: IMatch[]): IMatch[] { + return distinct(matches, (a => a.start + '.' + a.end)).filter(match => !matches.some(m => !(m.start === match.start && m.end === match.end) && (m.start <= match.start && m.end >= match.end))).sort((a, b) => a.start - b.start); + } + + private matchesKeybinding(keybinding: ResolvedKeybinding, searchValue: string, words: string[]): KeybindingMatches { + const [firstPart, chordPart] = keybinding.getParts(); + + if (strings.compareIgnoreCase(searchValue, keybinding.getAriaLabel()) === 0 || strings.compareIgnoreCase(searchValue, keybinding.getLabel()) === 0) { + return { + firstPart: this.createCompleteMatch(firstPart), + chordPart: this.createCompleteMatch(chordPart) + }; + } + + let firstPartMatch: KeybindingMatch = {}; + let chordPartMatch: KeybindingMatch = {}; + + const matchedWords = []; + let firstPartMatchedWords = []; + let chordPartMatchedWords = []; + let matchFirstPart = true; + for (let index = 0; index < words.length; index++) { + const word = words[index]; + let firstPartMatched = false; + let chordPartMatched = false; + + matchFirstPart = matchFirstPart && !firstPartMatch.keyCode; + let matchChordPart = !chordPartMatch.keyCode; + + if (matchFirstPart) { + firstPartMatched = this.matchPart(firstPart, firstPartMatch, word); + if (firstPartMatch.keyCode) { + for (const cordPartMatchedWordIndex of chordPartMatchedWords) { + if (firstPartMatchedWords.indexOf(cordPartMatchedWordIndex) === -1) { + matchedWords.splice(matchedWords.indexOf(cordPartMatchedWordIndex), 1); + } + } + chordPartMatch = {}; + chordPartMatchedWords = []; + matchChordPart = false; + } + } + + if (matchChordPart) { + chordPartMatched = this.matchPart(chordPart, chordPartMatch, word); + } + + if (firstPartMatched) { + firstPartMatchedWords.push(index); + } + if (chordPartMatched) { + chordPartMatchedWords.push(index); + } + if (firstPartMatched || chordPartMatched) { + matchedWords.push(index); + } + + matchFirstPart = matchFirstPart && this.isModifier(word); + } + if (matchedWords.length !== words.length) { + return null; + } + if (this.completeMatch && (!this.isCompleteMatch(firstPart, firstPartMatch) || !this.isCompleteMatch(chordPart, chordPartMatch))) { + return null; + } + return this.hasAnyMatch(firstPartMatch) || this.hasAnyMatch(chordPartMatch) ? { firstPart: firstPartMatch, chordPart: chordPartMatch } : null; + } + + private matchPart(part: ResolvedKeybindingPart, match: KeybindingMatch, word: string): boolean { + let matched = false; + if (this.matchesMetaModifier(part, word)) { + matched = true; + match.metaKey = true; + } + if (this.matchesCtrlModifier(part, word)) { + matched = true; + match.ctrlKey = true; + } + if (this.matchesShiftModifier(part, word)) { + matched = true; + match.shiftKey = true; + } + if (this.matchesAltModifier(part, word)) { + matched = true; + match.altKey = true; + } + if (this.matchesKeyCode(part, word)) { + match.keyCode = true; + matched = true; + } + return matched; + } + + private matchesKeyCode(keybinding: ResolvedKeybindingPart, word: string): boolean { + if (!keybinding) { + return false; + } + const ariaLabel = keybinding.keyAriaLabel; + if (this.completeMatch || ariaLabel.length === 1 || word.length === 1) { + if (strings.compareIgnoreCase(ariaLabel, word) === 0) { + return true; + } + } else { + if (matchesContiguousSubString(word, ariaLabel)) { + return true; + } + } + return false; + } + + private matchesMetaModifier(keybinding: ResolvedKeybindingPart, word: string): boolean { + if (!keybinding) { + return false; + } + if (!keybinding.metaKey) { + return false; + } + return this.wordMatchesMetaModifier(word); + } + + private wordMatchesMetaModifier(word: string): boolean { + if (matchesPrefix(this.modifierLabels.ui.metaKey, word)) { + return true; + } + if (matchesPrefix(this.modifierLabels.aria.metaKey, word)) { + return true; + } + if (matchesPrefix(this.modifierLabels.user.metaKey, word)) { + return true; + } + if (matchesPrefix(localize('meta', "meta"), word)) { + return true; + } + return false; + } + + private matchesCtrlModifier(keybinding: ResolvedKeybindingPart, word: string): boolean { + if (!keybinding) { + return false; + } + if (!keybinding.ctrlKey) { + return false; + } + return this.wordMatchesCtrlModifier(word); + } + + private wordMatchesCtrlModifier(word: string): boolean { + if (matchesPrefix(this.modifierLabels.ui.ctrlKey, word)) { + return true; + } + if (matchesPrefix(this.modifierLabels.aria.ctrlKey, word)) { + return true; + } + if (matchesPrefix(this.modifierLabels.user.ctrlKey, word)) { + return true; + } + return false; + } + + private matchesShiftModifier(keybinding: ResolvedKeybindingPart, word: string): boolean { + if (!keybinding) { + return false; + } + if (!keybinding.shiftKey) { + return false; + } + return this.wordMatchesShiftModifier(word); + } + + private wordMatchesShiftModifier(word: string): boolean { + if (matchesPrefix(this.modifierLabels.ui.shiftKey, word)) { + return true; + } + if (matchesPrefix(this.modifierLabels.aria.shiftKey, word)) { + return true; + } + if (matchesPrefix(this.modifierLabels.user.shiftKey, word)) { + return true; + } + return false; + } + + private matchesAltModifier(keybinding: ResolvedKeybindingPart, word: string): boolean { + if (!keybinding) { + return false; + } + if (!keybinding.altKey) { + return false; + } + return this.wordMatchesAltModifier(word); + } + + private wordMatchesAltModifier(word: string): boolean { + if (matchesPrefix(this.modifierLabels.ui.altKey, word)) { + return true; + } + if (matchesPrefix(this.modifierLabels.aria.altKey, word)) { + return true; + } + if (matchesPrefix(this.modifierLabels.user.altKey, word)) { + return true; + } + if (matchesPrefix(localize('option', "option"), word)) { + return true; + } + return false; + } + + private hasAnyMatch(keybindingMatch: KeybindingMatch): boolean { + return keybindingMatch.altKey || + keybindingMatch.ctrlKey || + keybindingMatch.metaKey || + keybindingMatch.shiftKey || + keybindingMatch.keyCode; + } + + private isCompleteMatch(part: ResolvedKeybindingPart, match: KeybindingMatch): boolean { + if (!part) { + return true; + } + if (!match.keyCode) { + return false; + } + if (part.metaKey && !match.metaKey) { + return false; + } + if (part.altKey && !match.altKey) { + return false; + } + if (part.ctrlKey && !match.ctrlKey) { + return false; + } + if (part.shiftKey && !match.shiftKey) { + return false; + } + return true; + } + + private createCompleteMatch(part: ResolvedKeybindingPart): KeybindingMatch { + let match: KeybindingMatch = {}; + if (part) { + match.keyCode = true; + if (part.metaKey) { + match.metaKey = true; + } + if (part.altKey) { + match.altKey = true; + } + if (part.ctrlKey) { + match.ctrlKey = true; + } + if (part.shiftKey) { + match.shiftKey = true; + } + } + return match; + } + + private isModifier(word: string): boolean { + if (this.wordMatchesAltModifier(word)) { + return true; + } + if (this.wordMatchesCtrlModifier(word)) { + return true; + } + if (this.wordMatchesMetaModifier(word)) { + return true; + } + if (this.wordMatchesShiftModifier(word)) { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts new file mode 100644 index 0000000000..de61f65278 --- /dev/null +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import * as paths from 'vs/base/common/paths'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IEditor } from 'vs/platform/editor/common/editor'; +import { IKeybindingItemEntry } from 'vs/workbench/parts/preferences/common/keybindingsEditorModel'; +import { IRange } from 'vs/editor/common/core/range'; +import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +export interface ISettingsGroup { + id: string; + range: IRange; + title: string; + titleRange: IRange; + sections: ISettingsSection[]; +} + +export interface ISettingsSection { + titleRange?: IRange; + title?: string; + settings: ISetting[]; +} + +export interface ISetting { + range: IRange; + key: string; + keyRange: IRange; + value: any; + valueRange: IRange; + description: string[]; + descriptionRanges: IRange[]; + overrides?: ISetting[]; + overrideOf?: ISetting; +} + +export interface IFilterResult { + filteredGroups: ISettingsGroup[]; + allGroups: ISettingsGroup[]; + matches: IRange[]; +} + +export interface IPreferencesEditorModel { + uri: URI; + content: string; + getPreference(key: string): T; + dispose(): void; +} + +export interface ISettingsEditorModel extends IPreferencesEditorModel { + settingsGroups: ISettingsGroup[]; + groupsTerms: string[]; + filterSettings(filter: string): IFilterResult; +} + +export interface IKeybindingsEditorModel extends IPreferencesEditorModel { +} + +export const IPreferencesService = createDecorator('preferencesService'); + +export interface IPreferencesService { + _serviceBrand: any; + + defaultSettingsResource: URI; + defaultResourceSettingsResource: URI; + userSettingsResource: URI; + workspaceSettingsResource: URI; + getFolderSettingsResource(resource: URI): URI; + + resolveContent(uri: URI): TPromise; + createPreferencesEditorModel(uri: URI): TPromise>; + + openGlobalSettings(): TPromise; + openWorkspaceSettings(): TPromise; + openFolderSettings(folder: URI): TPromise; + switchSettings(target: ConfigurationTarget, resource: URI): TPromise; + openGlobalKeybindingSettings(textual: boolean): TPromise; + + configureSettingsForLanguage(language: string): void; +} + + +export interface IKeybindingsEditor extends IEditor { + + activeKeybindingEntry: IKeybindingItemEntry; + + search(filter: string): void; + focusKeybindings(): void; + defineKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; + removeKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; + resetKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; + copyKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; + showConflicts(keybindingEntry: IKeybindingItemEntry): TPromise; +} + +export function getSettingsTargetName(target: ConfigurationTarget, resource: URI, workspaceContextService: IWorkspaceContextService): string { + switch (target) { + case ConfigurationTarget.USER: + return localize('userSettingsTarget', "User Settings"); + case ConfigurationTarget.WORKSPACE: + return localize('workspaceSettingsTarget', "Workspace Settings"); + case ConfigurationTarget.FOLDER: + const root = workspaceContextService.getRoot(resource); + return root ? paths.basename(root.fsPath) : ''; + } +} + +export const CONTEXT_SETTINGS_EDITOR = new RawContextKey('inSettingsEditor', false); +export const CONTEXT_SETTINGS_SEARCH_FOCUS = new RawContextKey('inSettingsSearch', false); +export const CONTEXT_KEYBINDINGS_EDITOR = new RawContextKey('inKeybindings', false); +export const CONTEXT_KEYBINDINGS_SEARCH_FOCUS = new RawContextKey('inKeybindingsSearch', false); +export const CONTEXT_KEYBINDING_FOCUS = new RawContextKey('keybindingFocus', false); + +export const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; +export const SETTINGS_EDITOR_COMMAND_FOCUS_FILE = 'settings.action.focusSettingsFile'; +export const KEYBINDINGS_EDITOR_COMMAND_SEARCH = 'keybindings.editor.searchKeybindings'; +export const KEYBINDINGS_EDITOR_COMMAND_DEFINE = 'keybindings.editor.defineKeybinding'; +export const KEYBINDINGS_EDITOR_COMMAND_REMOVE = 'keybindings.editor.removeKeybinding'; +export const KEYBINDINGS_EDITOR_COMMAND_RESET = 'keybindings.editor.resetKeybinding'; +export const KEYBINDINGS_EDITOR_COMMAND_COPY = 'keybindings.editor.copyKeybindingEntry'; +export const KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS = 'keybindings.editor.showConflicts'; +export const KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS = 'keybindings.editor.focusKeybindings'; \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts b/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts new file mode 100644 index 0000000000..ea2f20fb0f --- /dev/null +++ b/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModel } from 'vs/editor/common/editorCommon'; +import JSONContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; + +const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); + +export class PreferencesContentProvider implements IWorkbenchContribution { + + constructor( + @IModelService private modelService: IModelService, + @ITextModelService private textModelResolverService: ITextModelService, + @IPreferencesService private preferencesService: IPreferencesService, + @IModeService private modeService: IModeService + ) { + this.start(); + } + + public getId(): string { + return 'vs.contentprovider'; + } + + private start(): void { + this.textModelResolverService.registerTextModelContentProvider('vscode', { + provideTextContent: (uri: URI): TPromise => { + if (uri.scheme !== 'vscode') { + return null; + } + if (uri.authority === 'schemas') { + let schemas = schemaRegistry.getSchemaContributions().schemas; + let schema = schemas[uri.toString()]; + if (schema) { + let modelContent = JSON.stringify(schema); + let mode = this.modeService.getOrCreateMode('json'); + return TPromise.as(this.modelService.createModel(modelContent, mode, uri)); + } + } + return this.preferencesService.resolveContent(uri) + .then(content => { + if (content !== null && content !== void 0) { + let mode = this.modeService.getOrCreateMode('json'); + const model = this.modelService.createModel(content, mode, uri); + return TPromise.as(model); + } + return null; + }); + } + }); + } +} diff --git a/src/vs/workbench/parts/preferences/common/preferencesModels.ts b/src/vs/workbench/parts/preferences/common/preferencesModels.ts new file mode 100644 index 0000000000..6574c97eb5 --- /dev/null +++ b/src/vs/workbench/parts/preferences/common/preferencesModels.ts @@ -0,0 +1,920 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as strings from 'vs/base/common/strings'; +import { assign } from 'vs/base/common/objects'; +import { distinct } from 'vs/base/common/arrays'; +import URI from 'vs/base/common/uri'; +import { IReference } from 'vs/base/common/lifecycle'; +import Event from 'vs/base/common/event'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { visit, JSONVisitor } from 'vs/base/common/json'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { EditorModel } from 'vs/workbench/common/editor'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN, IConfigurationPropertySchema, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { ISettingsEditorModel, IKeybindingsEditorModel, ISettingsGroup, ISetting, IFilterResult, ISettingsSection } from 'vs/workbench/parts/preferences/common/preferences'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IMatch, or, matchesContiguousSubString, matchesPrefix, matchesCamelCase, matchesWords } from 'vs/base/common/filters'; +import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IRange } from 'vs/editor/common/core/range'; +import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Queue } from 'vs/base/common/async'; +import { IFileService } from 'vs/platform/files/common/files'; + +class SettingMatches { + + private readonly descriptionMatchingWords: Map = new Map(); + private readonly keyMatchingWords: Map = new Map(); + private readonly valueMatchingWords: Map = new Map(); + + public readonly matches: IRange[]; + + constructor(searchString: string, setting: ISetting, private valuesMatcher: (filter: string, setting: ISetting) => IRange[]) { + this.matches = distinct(this._findMatchesInSetting(searchString, setting), (match) => `${match.startLineNumber}_${match.startColumn}_${match.endLineNumber}_${match.endColumn}_`); + } + + private _findMatchesInSetting(searchString: string, setting: ISetting): IRange[] { + const result = this._doFindMatchesInSetting(searchString, setting); + if (setting.overrides && setting.overrides.length) { + for (const subSetting of setting.overrides) { + const subSettingMatches = new SettingMatches(searchString, subSetting, this.valuesMatcher); + let words = searchString.split(' '); + const descriptionRanges: IRange[] = this.getRangesForWords(words, this.descriptionMatchingWords, [subSettingMatches.descriptionMatchingWords, subSettingMatches.keyMatchingWords, subSettingMatches.valueMatchingWords]); + const keyRanges: IRange[] = this.getRangesForWords(words, this.keyMatchingWords, [subSettingMatches.descriptionMatchingWords, subSettingMatches.keyMatchingWords, subSettingMatches.valueMatchingWords]); + const subSettingKeyRanges: IRange[] = this.getRangesForWords(words, subSettingMatches.keyMatchingWords, [this.descriptionMatchingWords, this.keyMatchingWords, subSettingMatches.valueMatchingWords]); + const subSettinValueRanges: IRange[] = this.getRangesForWords(words, subSettingMatches.valueMatchingWords, [this.descriptionMatchingWords, this.keyMatchingWords, subSettingMatches.keyMatchingWords]); + result.push(...descriptionRanges, ...keyRanges, ...subSettingKeyRanges, ...subSettinValueRanges); + result.push(...subSettingMatches.matches); + } + } + return result; + } + + private _doFindMatchesInSetting(searchString: string, setting: ISetting): IRange[] { + const registry: { [qualifiedKey: string]: IJSONSchema } = Registry.as(Extensions.Configuration).getConfigurationProperties(); + const schema: IJSONSchema = registry[setting.key]; + + let words = searchString.split(' '); + const settingKeyAsWords: string = setting.key.split('.').join(' '); + + for (const word of words) { + for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) { + const descriptionMatches = matchesWords(word, setting.description[lineIndex], true); + if (descriptionMatches) { + this.descriptionMatchingWords.set(word, descriptionMatches.map(match => this.toDescriptionRange(setting, match, lineIndex))); + } + } + + const keyMatches = or(matchesWords, matchesCamelCase)(word, settingKeyAsWords); + if (keyMatches) { + this.keyMatchingWords.set(word, keyMatches.map(match => this.toKeyRange(setting, match))); + } + + const valueMatches = typeof setting.value === 'string' ? matchesContiguousSubString(word, setting.value) : null; + if (valueMatches) { + this.valueMatchingWords.set(word, valueMatches.map(match => this.toValueRange(setting, match))); + } else if (schema && schema.enum && schema.enum.some(enumValue => typeof enumValue === 'string' && !!matchesContiguousSubString(word, enumValue))) { + this.valueMatchingWords.set(word, []); + } + } + + const descriptionRanges: IRange[] = []; + for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) { + const matches = or(matchesContiguousSubString)(searchString, setting.description[lineIndex] || '') || []; + descriptionRanges.push(...matches.map(match => this.toDescriptionRange(setting, match, lineIndex))); + } + if (descriptionRanges.length === 0) { + descriptionRanges.push(...this.getRangesForWords(words, this.descriptionMatchingWords, [this.keyMatchingWords, this.valueMatchingWords])); + } + + const keyMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.key); + const keyRanges: IRange[] = keyMatches ? keyMatches.map(match => this.toKeyRange(setting, match)) : this.getRangesForWords(words, this.keyMatchingWords, [this.descriptionMatchingWords, this.valueMatchingWords]); + + let valueRanges: IRange[] = []; + if (setting.value && typeof setting.value === 'string') { + const valueMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.value); + valueRanges = valueMatches ? valueMatches.map(match => this.toValueRange(setting, match)) : this.getRangesForWords(words, this.valueMatchingWords, [this.keyMatchingWords, this.descriptionMatchingWords]); + } else { + valueRanges = this.valuesMatcher(searchString, setting); + } + + return [...descriptionRanges, ...keyRanges, ...valueRanges]; + } + + private getRangesForWords(words: string[], from: Map, others: Map[]): IRange[] { + const result: IRange[] = []; + for (const word of words) { + const ranges = from.get(word); + if (ranges) { + result.push(...ranges); + } else if (others.every(o => !o.has(word))) { + return []; + } + } + return result; + } + + private toKeyRange(setting: ISetting, match: IMatch): IRange { + return { + startLineNumber: setting.keyRange.startLineNumber, + startColumn: setting.keyRange.startColumn + match.start, + endLineNumber: setting.keyRange.startLineNumber, + endColumn: setting.keyRange.startColumn + match.end + }; + } + + private toDescriptionRange(setting: ISetting, match: IMatch, lineIndex: number): IRange { + return { + startLineNumber: setting.descriptionRanges[lineIndex].startLineNumber, + startColumn: setting.descriptionRanges[lineIndex].startColumn + match.start, + endLineNumber: setting.descriptionRanges[lineIndex].endLineNumber, + endColumn: setting.descriptionRanges[lineIndex].startColumn + match.end + }; + } + + private toValueRange(setting: ISetting, match: IMatch): IRange { + return { + startLineNumber: setting.valueRange.startLineNumber, + startColumn: setting.valueRange.startColumn + match.start + 1, + endLineNumber: setting.valueRange.startLineNumber, + endColumn: setting.valueRange.startColumn + match.end + 1 + }; + } +} + + +export abstract class AbstractSettingsModel extends EditorModel { + + public get groupsTerms(): string[] { + return this.settingsGroups.map(group => '@' + group.id); + } + + protected doFilterSettings(filter: string, allGroups: ISettingsGroup[]): IFilterResult { + if (!filter) { + return { + filteredGroups: allGroups, + allGroups, + matches: [] + }; + } + + const group = this.filterByGroupTerm(filter); + if (group) { + return { + filteredGroups: [group], + allGroups, + matches: [] + }; + } + + const matches: IRange[] = []; + const filteredGroups: ISettingsGroup[] = []; + const regex = strings.createRegExp(filter, false, { global: true }); + for (const group of allGroups) { + const groupMatched = regex.test(group.title); + const sections: ISettingsSection[] = []; + for (const section of group.sections) { + const settings: ISetting[] = []; + for (const setting of section.settings) { + const settingMatches = new SettingMatches(filter, setting, (filter, setting) => this.findValueMatches(filter, setting)).matches; + if (groupMatched || settingMatches.length > 0) { + settings.push(setting); + } + matches.push(...settingMatches); + } + if (settings.length) { + sections.push({ + title: section.title, + settings, + titleRange: section.titleRange + }); + } + } + if (sections.length) { + filteredGroups.push({ + id: group.id, + title: group.title, + titleRange: group.titleRange, + sections, + range: group.range + }); + } + } + return { filteredGroups, matches, allGroups }; + } + + private filterByGroupTerm(filter: string): ISettingsGroup { + if (this.groupsTerms.indexOf(filter) !== -1) { + const id = filter.substring(1); + return this.settingsGroups.filter(group => group.id === id)[0]; + } + return null; + } + + public getPreference(key: string): ISetting { + for (const group of this.settingsGroups) { + for (const section of group.sections) { + for (const setting of section.settings) { + if (key === setting.key) { + return setting; + } + } + } + } + return null; + } + + public abstract settingsGroups: ISettingsGroup[]; + + protected abstract findValueMatches(filter: string, setting: ISetting): IRange[]; +} + +export class SettingsEditorModel extends AbstractSettingsModel implements ISettingsEditorModel { + + private _settingsGroups: ISettingsGroup[]; + protected settingsModel: IModel; + private queue: Queue; + + constructor(reference: IReference, private _configurationTarget: ConfigurationTarget, @ITextFileService protected textFileService: ITextFileService) { + super(); + this.settingsModel = reference.object.textEditorModel; + this._register(this.onDispose(() => reference.dispose())); + this._register(this.settingsModel.onDidChangeContent(() => { + this._settingsGroups = null; + })); + this.queue = new Queue(); + } + + public get uri(): URI { + return this.settingsModel.uri; + } + + public get configurationTarget(): ConfigurationTarget { + return this._configurationTarget; + } + + public get settingsGroups(): ISettingsGroup[] { + if (!this._settingsGroups) { + this.parse(); + } + return this._settingsGroups; + } + + public get content(): string { + return this.settingsModel.getValue(); + } + + public filterSettings(filter: string): IFilterResult { + return this.doFilterSettings(filter, this.settingsGroups); + } + + public save(): TPromise { + return this.queue.queue(() => this.doSave()); + } + + protected doSave(): TPromise { + return this.textFileService.save(this.uri); + } + + protected findValueMatches(filter: string, setting: ISetting): IRange[] { + return this.settingsModel.findMatches(filter, setting.valueRange, false, false, null, false).map(match => match.range); + } + + private parse() { + const model = this.settingsModel; + const settings: ISetting[] = []; + let overrideSetting: ISetting = null; + + let currentProperty: string = null; + let currentParent: any = []; + let previousParents: any[] = []; + let range = { + startLineNumber: 0, + startColumn: 0, + endLineNumber: 0, + endColumn: 0 + }; + + function onValue(value: any, offset: number, length: number) { + if (Array.isArray(currentParent)) { + (currentParent).push(value); + } else if (currentProperty) { + currentParent[currentProperty] = value; + } + if (previousParents.length === 1 || (previousParents.length === 2 && overrideSetting !== null)) { + // settings value started + const setting = previousParents.length === 1 ? settings[settings.length - 1] : overrideSetting.overrides[overrideSetting.overrides.length - 1]; + if (setting) { + let valueStartPosition = model.getPositionAt(offset); + let valueEndPosition = model.getPositionAt(offset + length); + setting.value = value; + setting.valueRange = { + startLineNumber: valueStartPosition.lineNumber, + startColumn: valueStartPosition.column, + endLineNumber: valueEndPosition.lineNumber, + endColumn: valueEndPosition.column + }; + setting.range = assign(setting.range, { + endLineNumber: valueEndPosition.lineNumber, + endColumn: valueEndPosition.column + }); + } + } + } + let visitor: JSONVisitor = { + onObjectBegin: (offset: number, length: number) => { + if (previousParents.length === 0) { + // Settings started + let position = model.getPositionAt(offset); + range.startLineNumber = position.lineNumber; + range.startColumn = position.column; + } + let object = {}; + onValue(object, offset, length); + currentParent = object; + currentProperty = null; + previousParents.push(currentParent); + }, + onObjectProperty: (name: string, offset: number, length: number) => { + currentProperty = name; + if (previousParents.length === 1 || (previousParents.length === 2 && overrideSetting !== null)) { + // setting started + let settingStartPosition = model.getPositionAt(offset); + const setting: ISetting = { + description: [], + key: name, + keyRange: { + startLineNumber: settingStartPosition.lineNumber, + startColumn: settingStartPosition.column + 1, + endLineNumber: settingStartPosition.lineNumber, + endColumn: settingStartPosition.column + length + }, + range: { + startLineNumber: settingStartPosition.lineNumber, + startColumn: settingStartPosition.column, + endLineNumber: 0, + endColumn: 0 + }, + value: null, + valueRange: null, + descriptionRanges: null, + overrides: [], + overrideOf: overrideSetting + }; + if (previousParents.length === 1) { + settings.push(setting); + if (OVERRIDE_PROPERTY_PATTERN.test(name)) { + overrideSetting = setting; + } + } else { + overrideSetting.overrides.push(setting); + } + } + }, + onObjectEnd: (offset: number, length: number) => { + currentParent = previousParents.pop(); + if (previousParents.length === 1 || (previousParents.length === 2 && overrideSetting !== null)) { + // setting ended + const setting = previousParents.length === 1 ? settings[settings.length - 1] : overrideSetting.overrides[overrideSetting.overrides.length - 1]; + if (setting) { + let valueEndPosition = model.getPositionAt(offset + length); + setting.valueRange = assign(setting.valueRange, { + endLineNumber: valueEndPosition.lineNumber, + endColumn: valueEndPosition.column + }); + setting.range = assign(setting.range, { + endLineNumber: valueEndPosition.lineNumber, + endColumn: valueEndPosition.column + }); + } + + if (previousParents.length === 1) { + overrideSetting = null; + } + } + if (previousParents.length === 0) { + // settings ended + let position = model.getPositionAt(offset); + range.endLineNumber = position.lineNumber; + range.endColumn = position.column; + } + }, + onArrayBegin: (offset: number, length: number) => { + let array = []; + onValue(array, offset, length); + previousParents.push(currentParent); + currentParent = array; + currentProperty = null; + }, + onArrayEnd: (offset: number, length: number) => { + currentParent = previousParents.pop(); + if (previousParents.length === 1 || (previousParents.length === 2 && overrideSetting !== null)) { + // setting value ended + const setting = previousParents.length === 1 ? settings[settings.length - 1] : overrideSetting.overrides[overrideSetting.overrides.length - 1]; + if (setting) { + let valueEndPosition = model.getPositionAt(offset + length); + setting.valueRange = assign(setting.valueRange, { + endLineNumber: valueEndPosition.lineNumber, + endColumn: valueEndPosition.column + }); + setting.range = assign(setting.range, { + endLineNumber: valueEndPosition.lineNumber, + endColumn: valueEndPosition.column + }); + } + } + }, + onLiteralValue: onValue, + onError: (error) => { + const setting = settings[settings.length - 1]; + if (setting && (!setting.range || !setting.keyRange || !setting.valueRange)) { + settings.pop(); + } + } + }; + if (!model.isDisposed()) { + visit(model.getValue(), visitor); + } + this._settingsGroups = settings.length > 0 ? [{ + sections: [ + { + settings + } + ], + title: null, + titleRange: null, + range + }] : []; + } +} + +export class WorkspaceConfigModel extends SettingsEditorModel implements ISettingsEditorModel { + + private workspaceConfigModel: IModel; + private workspaceConfigEtag: string; + + constructor( + reference: IReference, + workspaceConfigModelReference: IReference, + _configurationTarget: ConfigurationTarget, + onDispose: Event, + @IFileService private fileService: IFileService, + @ITextModelService private textModelResolverService: ITextModelService, + @ITextFileService textFileService: ITextFileService + ) { + super(reference, _configurationTarget, textFileService); + + this._register(workspaceConfigModelReference); + this.workspaceConfigModel = workspaceConfigModelReference.object.textEditorModel; + + // Only listen to state changes. Content changes without saving are not synced. + this._register(this.textFileService.models.get(this.workspaceConfigModel.uri).onDidStateChange(statChange => this._onWorkspaceConfigFileStateChanged(statChange))); + this.onDispose(() => super.dispose()); + } + + protected doSave(): TPromise { + if (this.textFileService.isDirty(this.workspaceConfigModel.uri)) { + // Throw an error? + return TPromise.as(null); + } + + const content = this.createWorkspaceConfigContentFromSettingsModel(); + if (content !== this.workspaceConfigModel.getValue()) { + return this.fileService.updateContent(this.workspaceConfigModel.uri, content) + .then(stat => this.workspaceConfigEtag = stat.etag); + } + + return TPromise.as(null); + } + + private createWorkspaceConfigContentFromSettingsModel(): string { + const workspaceConfigContent = this.workspaceConfigModel.getValue(); + const { settingsPropertyEndsAt, nodeAfterSettingStartsAt } = WorkspaceConfigModel.parseWorkspaceConfigContent(workspaceConfigContent); + const workspaceConfigEndsAt = workspaceConfigContent.lastIndexOf('}'); + + // Settings property exist in Workspace Configuration and has Ending Brace + if (settingsPropertyEndsAt !== -1 && workspaceConfigEndsAt > settingsPropertyEndsAt) { + + // Place settings at the end + let from = workspaceConfigContent.indexOf(':', settingsPropertyEndsAt) + 1; + let to = workspaceConfigEndsAt; + let settingsContent = this.settingsModel.getValue(); + + // There is a node after settings property + // Place settings before that node + if (nodeAfterSettingStartsAt !== -1) { + settingsContent += ','; + to = nodeAfterSettingStartsAt; + } + + return workspaceConfigContent.substring(0, from) + settingsContent + workspaceConfigContent.substring(to); + } + + // Settings property does not exist. Place it at the end + return workspaceConfigContent.substring(0, workspaceConfigEndsAt) + `,\n"settings": ${this.settingsModel.getValue()}\n` + workspaceConfigContent.substring(workspaceConfigEndsAt); + } + + private _onWorkspaceConfigFileStateChanged(stateChange: StateChange): void { + let hasToUpdate = false; + switch (stateChange) { + case StateChange.SAVED: + hasToUpdate = this.workspaceConfigEtag !== this.textFileService.models.get(this.workspaceConfigModel.uri).getETag(); + break; + } + if (hasToUpdate) { + this.onWorkspaceConfigFileContentChanged(); + } + } + + private onWorkspaceConfigFileContentChanged(): void { + this.workspaceConfigEtag = this.textFileService.models.get(this.workspaceConfigModel.uri).getETag(); + const settingsValue = WorkspaceConfigModel.getSettingsContentFromConfigContent(this.workspaceConfigModel.getValue()); + if (settingsValue) { + this.settingsModel.setValue(settingsValue); + } + } + + dispose() { + // Not disposable by default + } + + static getSettingsContentFromConfigContent(workspaceConfigContent: string): string { + const { settingsPropertyEndsAt, nodeAfterSettingStartsAt } = WorkspaceConfigModel.parseWorkspaceConfigContent(workspaceConfigContent); + + const workspaceConfigEndsAt = workspaceConfigContent.lastIndexOf('}'); + + if (settingsPropertyEndsAt !== -1) { + const from = workspaceConfigContent.indexOf(':', settingsPropertyEndsAt) + 1; + const to = nodeAfterSettingStartsAt !== -1 ? nodeAfterSettingStartsAt : workspaceConfigEndsAt; + return workspaceConfigContent.substring(from, to); + } + + return null; + } + + static parseWorkspaceConfigContent(content: string): { settingsPropertyEndsAt: number, nodeAfterSettingStartsAt: number } { + + let settingsPropertyEndsAt = -1; + let nodeAfterSettingStartsAt = -1; + + let rootProperties = []; + let ancestors = []; + let currentProperty = ''; + + visit(content, { + onObjectProperty: (name: string, offset: number, length: number) => { + currentProperty = name; + if (ancestors.length === 1) { + rootProperties.push(name); + if (rootProperties[rootProperties.length - 1] === 'settings') { + settingsPropertyEndsAt = offset + length; + } + if (rootProperties[rootProperties.length - 2] === 'settings') { + nodeAfterSettingStartsAt = offset; + } + } + }, + onObjectBegin: (offset: number, length: number) => { + ancestors.push(currentProperty); + }, + onObjectEnd: (offset: number, length: number) => { + ancestors.pop(); + } + }, { allowTrailingComma: true }); + + return { settingsPropertyEndsAt, nodeAfterSettingStartsAt }; + } +} + +export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ISettingsEditorModel { + + private _allSettingsGroups: ISettingsGroup[]; + private _content: string; + private _contentByLines: string[]; + + constructor(private _uri: URI, private _mostCommonlyUsedSettingsKeys: string[], readonly configurationScope: ConfigurationScope) { + super(); + } + + public get uri(): URI { + return this._uri; + } + + public get content(): string { + if (!this._content) { + this.parse(); + } + return this._content; + } + + public get settingsGroups(): ISettingsGroup[] { + if (!this._allSettingsGroups) { + this.parse(); + } + return this._allSettingsGroups; + } + + public get mostCommonlyUsedSettings(): ISettingsGroup { + return this.settingsGroups[0]; + } + + public filterSettings(filter: string): IFilterResult { + return this.doFilterSettings(filter, this.settingsGroups); + } + + public getPreference(key: string): ISetting { + for (const group of this.settingsGroups) { + for (const section of group.sections) { + for (const setting of section.settings) { + if (setting.key === key) { + return setting; + } + } + } + } + return null; + } + + private parse() { + const configurations = Registry.as(Extensions.Configuration).getConfigurations().slice(); + const settingsGroups = this.removeEmptySettingsGroups(configurations.sort(this.compareConfigurationNodes).reduce((result, config, index, array) => this.parseConfig(config, result, array), [])); + const mostCommonlyUsed = this.getMostCommonlyUsedSettings(settingsGroups); + this._allSettingsGroups = [mostCommonlyUsed, ...settingsGroups]; + this._content = this.toContent(mostCommonlyUsed, settingsGroups); + } + + private getMostCommonlyUsedSettings(allSettingsGroups: ISettingsGroup[]): ISettingsGroup { + const map: Map = new Map(); + for (const group of allSettingsGroups) { + for (const section of group.sections) { + for (const setting of section.settings) { + map.set(setting.key, setting); + } + } + } + const settings = this._mostCommonlyUsedSettingsKeys.map(key => { + const setting = map.get(key); + if (setting) { + return { + description: setting.description, + key: setting.key, + value: setting.value, + range: null, + valueRange: null, + overrides: [] + }; + } + return null; + }).filter(setting => !!setting); + + return { + id: 'mostCommonlyUsed', + range: null, + title: nls.localize('commonlyUsed', "Commonly Used"), + titleRange: null, + sections: [ + { + settings + } + ] + }; + } + + private parseConfig(config: IConfigurationNode, result: ISettingsGroup[], configurations: IConfigurationNode[], settingsGroup?: ISettingsGroup): ISettingsGroup[] { + let title = config.title; + if (!title) { + const configWithTitleAndSameId = configurations.filter(c => c.id === config.id && c.title)[0]; + if (configWithTitleAndSameId) { + title = configWithTitleAndSameId.title; + } + } + if (title) { + if (!settingsGroup) { + settingsGroup = result.filter(g => g.title === title)[0]; + if (!settingsGroup) { + settingsGroup = { sections: [{ settings: [] }], id: config.id, title: title, titleRange: null, range: null }; + result.push(settingsGroup); + } + } else { + settingsGroup.sections[settingsGroup.sections.length - 1].title = title; + } + } + if (config.properties) { + if (!settingsGroup) { + settingsGroup = { sections: [{ settings: [] }], id: config.id, title: config.id, titleRange: null, range: null }; + result.push(settingsGroup); + } + const configurationSettings: ISetting[] = this.parseSettings(config.properties); + if (configurationSettings.length) { + settingsGroup.sections[settingsGroup.sections.length - 1].settings.push(...configurationSettings); + } + } + if (config.allOf) { + config.allOf.forEach(c => this.parseConfig(c, result, configurations, settingsGroup)); + } + return result; + } + + private removeEmptySettingsGroups(settingsGroups: ISettingsGroup[]): ISettingsGroup[] { + const result = []; + for (const settingsGroup of settingsGroups) { + settingsGroup.sections = settingsGroup.sections.filter(section => section.settings.length > 0); + if (settingsGroup.sections.length) { + result.push(settingsGroup); + } + } + return result; + } + + private parseSettings(settingsObject: { [path: string]: IConfigurationPropertySchema; }): ISetting[] { + let result = []; + for (let key in settingsObject) { + const prop = settingsObject[key]; + if (!prop.deprecationMessage && this.matchesScope(prop)) { + const value = prop.default; + const description = (prop.description || '').split('\n'); + const overrides = OVERRIDE_PROPERTY_PATTERN.test(key) ? this.parseOverrideSettings(prop.default) : []; + result.push({ key, value, description, range: null, keyRange: null, valueRange: null, descriptionRanges: [], overrides }); + } + } + return result; + } + + private parseOverrideSettings(overrideSettings: any): ISetting[] { + return Object.keys(overrideSettings).map((key) => ({ key, value: overrideSettings[key], description: [], range: null, keyRange: null, valueRange: null, descriptionRanges: [], overrides: [] })); + } + + private matchesScope(property: IConfigurationNode): boolean { + if (this.configurationScope === ConfigurationScope.WINDOW) { + return true; + } + return property.scope === this.configurationScope; + } + + private compareConfigurationNodes(c1: IConfigurationNode, c2: IConfigurationNode): number { + if (typeof c1.order !== 'number') { + return 1; + } + if (typeof c2.order !== 'number') { + return -1; + } + if (c1.order === c2.order) { + const title1 = c1.title || ''; + const title2 = c2.title || ''; + return title1.localeCompare(title2); + } + return c1.order - c2.order; + } + + private toContent(mostCommonlyUsed: ISettingsGroup, settingsGroups: ISettingsGroup[]): string { + this._contentByLines = []; + this._contentByLines.push('['); + this.pushGroups([mostCommonlyUsed]); + this._contentByLines.push(','); + this.pushGroups(settingsGroups); + this._contentByLines.push(']'); + return this._contentByLines.join('\n'); + } + + private pushGroups(settingsGroups: ISettingsGroup[]): void { + let lastSetting: ISetting = null; + this._contentByLines.push('{'); + this._contentByLines.push(''); + for (const group of settingsGroups) { + lastSetting = this.pushGroup(group); + } + if (lastSetting) { + const content = this._contentByLines[lastSetting.range.endLineNumber - 2]; + this._contentByLines[lastSetting.range.endLineNumber - 2] = content.substring(0, content.length - 1); + } + this._contentByLines.push('}'); + } + + private pushGroup(group: ISettingsGroup): ISetting { + const indent = ' '; + let lastSetting: ISetting = null; + this._contentByLines.push(''); + let groupStart = this._contentByLines.length + 1; + for (const section of group.sections) { + if (section.title) { + let sectionTitleStart = this._contentByLines.length + 1; + this.addDescription([section.title], indent, this._contentByLines); + section.titleRange = { startLineNumber: sectionTitleStart, startColumn: 1, endLineNumber: this._contentByLines.length, endColumn: this._contentByLines[this._contentByLines.length - 1].length }; + } + + if (section.settings.length) { + for (const setting of section.settings) { + this.pushSetting(setting, indent); + lastSetting = setting; + } + } else { + this._contentByLines.push('// ' + nls.localize('noSettings', "No Settings")); + this._contentByLines.push(''); + } + + } + group.range = { startLineNumber: groupStart, startColumn: 1, endLineNumber: this._contentByLines.length, endColumn: this._contentByLines[this._contentByLines.length - 1].length }; + return lastSetting; + } + + private pushSetting(setting: ISetting, indent: string): void { + const settingStart = this._contentByLines.length + 1; + setting.descriptionRanges = []; + const descriptionPreValue = indent + '// '; + for (const line of setting.description) { + this._contentByLines.push(descriptionPreValue + line); + setting.descriptionRanges.push({ startLineNumber: this._contentByLines.length, startColumn: this._contentByLines[this._contentByLines.length - 1].indexOf(line) + 1, endLineNumber: this._contentByLines.length, endColumn: this._contentByLines[this._contentByLines.length - 1].length }); + } + + let preValueConent = indent; + const keyString = JSON.stringify(setting.key); + preValueConent += keyString; + setting.keyRange = { startLineNumber: this._contentByLines.length + 1, startColumn: preValueConent.indexOf(setting.key) + 1, endLineNumber: this._contentByLines.length + 1, endColumn: setting.key.length }; + + preValueConent += ': '; + const valueStart = this._contentByLines.length + 1; + this.pushValue(setting, preValueConent, indent); + + setting.valueRange = { startLineNumber: valueStart, startColumn: preValueConent.length + 1, endLineNumber: this._contentByLines.length, endColumn: this._contentByLines[this._contentByLines.length - 1].length + 1 }; + this._contentByLines[this._contentByLines.length - 1] += ','; + this._contentByLines.push(''); + setting.range = { startLineNumber: settingStart, startColumn: 1, endLineNumber: this._contentByLines.length, endColumn: this._contentByLines[this._contentByLines.length - 1].length }; + } + + private pushValue(setting: ISetting, preValueConent: string, indent: string): void { + let valueString = JSON.stringify(setting.value, null, indent); + if (valueString && (typeof setting.value === 'object')) { + if (setting.overrides.length) { + this._contentByLines.push(preValueConent + ' {'); + for (const subSetting of setting.overrides) { + this.pushSetting(subSetting, indent + indent); + this._contentByLines.pop(); + } + const lastSetting = setting.overrides[setting.overrides.length - 1]; + const content = this._contentByLines[lastSetting.range.endLineNumber - 2]; + this._contentByLines[lastSetting.range.endLineNumber - 2] = content.substring(0, content.length - 1); + this._contentByLines.push(indent + '}'); + } else { + const mulitLineValue = valueString.split('\n'); + this._contentByLines.push(preValueConent + mulitLineValue[0]); + for (let i = 1; i < mulitLineValue.length; i++) { + this._contentByLines.push(indent + mulitLineValue[i]); + } + } + } else { + this._contentByLines.push(preValueConent + valueString); + } + } + + private addDescription(description: string[], indent: string, result: string[]) { + for (const line of description) { + result.push(indent + '// ' + line); + } + } + + protected findValueMatches(filter: string, setting: ISetting): IRange[] { + return []; + } + + public dispose(): void { + // Not disposable + } +} + +export function defaultKeybindingsContents(keybindingService: IKeybindingService): string { + const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Overwrite key bindings by placing them into your key bindings file."); + return defaultsHeader + '\n' + keybindingService.getDefaultKeybindingsContent(); +} + +export class DefaultKeybindingsEditorModel implements IKeybindingsEditorModel { + + private _content: string; + + constructor(private _uri: URI, @IKeybindingService private keybindingService: IKeybindingService) { + } + + public get uri(): URI { + return this._uri; + } + + public get content(): string { + if (!this._content) { + this._content = defaultKeybindingsContents(this.keybindingService); + } + return this._content; + } + + public getPreference(): any { + return null; + } + + public dispose(): void { + // Not disposable + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/common/smartSnippetInserter.ts b/src/vs/workbench/parts/preferences/common/smartSnippetInserter.ts new file mode 100644 index 0000000000..82b6a4016d --- /dev/null +++ b/src/vs/workbench/parts/preferences/common/smartSnippetInserter.ts @@ -0,0 +1,159 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { JSONScanner, createScanner as createJSONScanner, SyntaxKind as JSONSyntaxKind } from 'vs/base/common/json'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; + +export interface InsertSnippetResult { + position: Position; + prepend: string; + append: string; +} + +export class SmartSnippetInserter { + + private static hasOpenBrace(scanner: JSONScanner): boolean { + + while (scanner.scan() !== JSONSyntaxKind.EOF) { + let kind = scanner.getToken(); + + if (kind === JSONSyntaxKind.OpenBraceToken) { + return true; + } + } + + return false; + } + + private static offsetToPosition(model: editorCommon.ITextModel, offset: number): Position { + let offsetBeforeLine = 0; + let eolLength = model.getEOL().length; + let lineCount = model.getLineCount(); + for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) { + let lineTotalLength = model.getLineContent(lineNumber).length + eolLength; + let offsetAfterLine = offsetBeforeLine + lineTotalLength; + + if (offsetAfterLine > offset) { + return new Position( + lineNumber, + offset - offsetBeforeLine + 1 + ); + } + offsetBeforeLine = offsetAfterLine; + } + return new Position( + lineCount, + model.getLineMaxColumn(lineCount) + ); + } + + public static insertSnippet(model: editorCommon.ITextModel, _position: Position): InsertSnippetResult { + + let desiredPosition = model.getValueLengthInRange(new Range(1, 1, _position.lineNumber, _position.column)); + + // [ { } , { } ] + enum State { + INVALID = 0, + AFTER_OBJECT = 1, + BEFORE_OBJECT = 2, + } + let currentState = State.INVALID; + let lastValidPos = -1; + let lastValidState = State.INVALID; + + let scanner = createJSONScanner(model.getValue()); + let arrayLevel = 0; + let objLevel = 0; + + let checkRangeStatus = (pos: number, state: State) => { + if (state !== State.INVALID && arrayLevel === 1 && objLevel === 0) { + currentState = state; + lastValidPos = pos; + lastValidState = state; + } else { + if (currentState !== State.INVALID) { + currentState = State.INVALID; + lastValidPos = scanner.getTokenOffset(); + } + } + }; + + while (scanner.scan() !== JSONSyntaxKind.EOF) { + let currentPos = scanner.getPosition(); + let kind = scanner.getToken(); + + let goodKind = false; + switch (kind) { + case JSONSyntaxKind.OpenBracketToken: + goodKind = true; + arrayLevel++; + checkRangeStatus(currentPos, State.BEFORE_OBJECT); + break; + case JSONSyntaxKind.CloseBracketToken: + goodKind = true; + arrayLevel--; + checkRangeStatus(currentPos, State.INVALID); + break; + case JSONSyntaxKind.CommaToken: + goodKind = true; + checkRangeStatus(currentPos, State.BEFORE_OBJECT); + break; + case JSONSyntaxKind.OpenBraceToken: + goodKind = true; + objLevel++; + checkRangeStatus(currentPos, State.INVALID); + break; + case JSONSyntaxKind.CloseBraceToken: + goodKind = true; + objLevel--; + checkRangeStatus(currentPos, State.AFTER_OBJECT); + break; + case JSONSyntaxKind.Trivia: + case JSONSyntaxKind.LineBreakTrivia: + goodKind = true; + } + + if (currentPos >= desiredPosition && (currentState !== State.INVALID || lastValidPos !== -1)) { + let acceptPosition: number; + let acceptState: State; + + if (currentState !== State.INVALID) { + acceptPosition = (goodKind ? currentPos : scanner.getTokenOffset()); + acceptState = currentState; + } else { + acceptPosition = lastValidPos; + acceptState = lastValidState; + } + + if (acceptState as State === State.AFTER_OBJECT) { + return { + position: this.offsetToPosition(model, acceptPosition), + prepend: ',', + append: '' + }; + } else { + scanner.setPosition(acceptPosition); + return { + position: this.offsetToPosition(model, acceptPosition), + prepend: '', + append: this.hasOpenBrace(scanner) ? ',' : '' + }; + } + } + } + + // no valid position found! + let modelLineCount = model.getLineCount(); + return { + position: new Position(modelLineCount, model.getLineMaxColumn(modelLineCount)), + prepend: '\n[', + append: ']' + }; + } +} diff --git a/src/vs/workbench/parts/preferences/test/browser/keybindingsEditorContribution.test.ts b/src/vs/workbench/parts/preferences/test/browser/keybindingsEditorContribution.test.ts new file mode 100644 index 0000000000..a579bf8d18 --- /dev/null +++ b/src/vs/workbench/parts/preferences/test/browser/keybindingsEditorContribution.test.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { KeybindingEditorDecorationsRenderer } from 'vs/workbench/parts/preferences/browser/keybindingsEditorContribution'; + +suite('KeybindingsEditorContribution', () => { + + function assertUserSettingsFuzzyEquals(a: string, b: string, expected: boolean): void { + const actual = KeybindingEditorDecorationsRenderer._userSettingsFuzzyEquals(a, b); + const message = expected ? `${a} == ${b}` : `${a} != ${b}`; + assert.equal(actual, expected, 'fuzzy: ' + message); + } + + function assertEqual(a: string, b: string): void { + assertUserSettingsFuzzyEquals(a, b, true); + } + + function assertDifferent(a: string, b: string): void { + assertUserSettingsFuzzyEquals(a, b, false); + } + + test('_userSettingsFuzzyEquals', () => { + assertEqual('a', 'a'); + assertEqual('a', 'A'); + assertEqual('ctrl+a', 'CTRL+A'); + assertEqual('ctrl+a', ' CTRL+A '); + + assertEqual('ctrl+shift+a', 'shift+ctrl+a'); + assertEqual('ctrl+shift+a ctrl+alt+b', 'shift+ctrl+a alt+ctrl+b'); + + assertDifferent('ctrl+[KeyA]', 'ctrl+a'); + + // issue #23335 + assertEqual('cmd+shift+p', 'shift+cmd+p'); + assertEqual('cmd+shift+p', 'shift-cmd-p'); + }); +}); diff --git a/src/vs/workbench/parts/preferences/test/common/keybindingsEditorModel.test.ts b/src/vs/workbench/parts/preferences/test/common/keybindingsEditorModel.test.ts new file mode 100644 index 0000000000..b54254b408 --- /dev/null +++ b/src/vs/workbench/parts/preferences/test/common/keybindingsEditorModel.test.ts @@ -0,0 +1,633 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as uuid from 'vs/base/common/uuid'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { OS, OperatingSystem } from 'vs/base/common/platform'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Action } from 'vs/base/common/actions'; +import { KeyCode, SimpleKeybinding, ChordKeybinding } from 'vs/base/common/keyCodes'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingsEditorModel, IKeybindingItemEntry } from 'vs/workbench/parts/preferences/common/keybindingsEditorModel'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; + +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; + +interface Modifiers { + metaKey?: boolean; + ctrlKey?: boolean; + altKey?: boolean; + shiftKey?: boolean; +} + +class AnAction extends Action { + constructor(id: string) { + super(id); + } +} + +suite('Keybindings Editor Model test', () => { + + let instantiationService: TestInstantiationService; + let testObject: KeybindingsEditorModel; + + setup(() => { + instantiationService = new TestInstantiationService(); + + instantiationService.stub(IKeybindingService, {}); + instantiationService.stub(IExtensionService, {}, 'onReady', () => TPromise.as(null)); + + testObject = instantiationService.createInstance(KeybindingsEditorModel, OS); + + CommandsRegistry.registerCommand('command_without_keybinding', () => { }); + }); + + test('fetch returns default keybindings', () => { + const expected = prepareKeybindingService( + aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }) + ); + + return testObject.resolve().then(() => { + const actuals = asResolvedKeybindingItems(testObject.fetch('')); + assertKeybindingItems(actuals, expected); + }); + }); + + test('fetch returns default keybindings at the top', () => { + const expected = prepareKeybindingService( + aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }) + ); + + return testObject.resolve().then(() => { + const actuals = asResolvedKeybindingItems(testObject.fetch('').slice(0, 2), true); + assertKeybindingItems(actuals, expected); + }); + }); + + test('fetch returns default keybindings sorted by command id', () => { + const keybindings = prepareKeybindingService( + aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: 'c' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Backspace } }) + ); + const expected = [keybindings[2], keybindings[0], keybindings[1]]; + + return testObject.resolve().then(() => { + const actuals = asResolvedKeybindingItems(testObject.fetch('')); + assertKeybindingItems(actuals, expected); + }); + }); + + test('fetch returns user keybinding first if default and user has same id', () => { + const sameId = 'b' + uuid.generateUuid(); + const keybindings = prepareKeybindingService( + aResolvedKeybindingItem({ command: sameId, firstPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: sameId, firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape }, isDefault: false }) + ); + const expected = [keybindings[1], keybindings[0]]; + + return testObject.resolve().then(() => { + const actuals = asResolvedKeybindingItems(testObject.fetch('')); + assertKeybindingItems(actuals, expected); + }); + }); + + test('fetch returns keybinding with titles first', () => { + const keybindings = prepareKeybindingService( + aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: 'c' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: 'd' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }) + ); + + registerCommandWithTitle(keybindings[1].command, 'B Title'); + registerCommandWithTitle(keybindings[3].command, 'A Title'); + + const expected = [keybindings[3], keybindings[1], keybindings[0], keybindings[2]]; + instantiationService.stub(IKeybindingService, 'getKeybindings', () => keybindings); + instantiationService.stub(IKeybindingService, 'getDefaultKeybindings', () => keybindings); + + return testObject.resolve().then(() => { + const actuals = asResolvedKeybindingItems(testObject.fetch('')); + assertKeybindingItems(actuals, expected); + }); + }); + + test('fetch returns keybinding with user first if title and id matches', () => { + const sameId = 'b' + uuid.generateUuid(); + const keybindings = prepareKeybindingService( + aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: sameId, firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: 'c' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: sameId, firstPart: { keyCode: KeyCode.Escape }, isDefault: false }) + ); + + registerCommandWithTitle(keybindings[1].command, 'Same Title'); + registerCommandWithTitle(keybindings[3].command, 'Same Title'); + const expected = [keybindings[3], keybindings[1], keybindings[0], keybindings[2]]; + + return testObject.resolve().then(() => { + const actuals = asResolvedKeybindingItems(testObject.fetch('')); + assertKeybindingItems(actuals, expected); + }); + }); + + test('fetch returns default keybindings sorted by precedence', () => { + const expected = prepareKeybindingService( + aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: 'c' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }), + aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Backspace } }) + ); + + return testObject.resolve().then(() => { + const actuals = asResolvedKeybindingItems(testObject.fetch('', true)); + assertKeybindingItems(actuals, expected); + }); + }); + + test('convert keybinding without title to entry', () => { + const expected = aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2' }); + prepareKeybindingService(expected); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('')[0]; + assert.equal(actual.keybindingItem.command, expected.command); + assert.equal(actual.keybindingItem.commandLabel, ''); + assert.equal(actual.keybindingItem.commandDefaultLabel, null); + assert.equal(actual.keybindingItem.keybinding.getAriaLabel(), expected.resolvedKeybinding.getAriaLabel()); + assert.equal(actual.keybindingItem.when, expected.when.serialize()); + }); + }); + + test('convert keybinding with title to entry', () => { + const expected = aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2' }); + prepareKeybindingService(expected); + registerCommandWithTitle(expected.command, 'Some Title'); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('')[0]; + assert.equal(actual.keybindingItem.command, expected.command); + assert.equal(actual.keybindingItem.commandLabel, 'Some Title'); + assert.equal(actual.keybindingItem.commandDefaultLabel, null); + assert.equal(actual.keybindingItem.keybinding.getAriaLabel(), expected.resolvedKeybinding.getAriaLabel()); + assert.equal(actual.keybindingItem.when, expected.when.serialize()); + }); + }); + + test('convert without title and binding to entry', () => { + CommandsRegistry.registerCommand('command_without_keybinding', () => { }); + prepareKeybindingService(); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('').filter(element => element.keybindingItem.command === 'command_without_keybinding')[0]; + assert.equal(actual.keybindingItem.command, 'command_without_keybinding'); + assert.equal(actual.keybindingItem.commandLabel, ''); + assert.equal(actual.keybindingItem.commandDefaultLabel, null); + assert.equal(actual.keybindingItem.keybinding, null); + assert.equal(actual.keybindingItem.when, ''); + }); + }); + + test('convert with title and wihtout binding to entry', () => { + const id = 'a' + uuid.generateUuid(); + registerCommandWithTitle(id, 'some title'); + prepareKeybindingService(); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('').filter(element => element.keybindingItem.command === id)[0]; + assert.equal(actual.keybindingItem.command, id); + assert.equal(actual.keybindingItem.commandLabel, 'some title'); + assert.equal(actual.keybindingItem.commandDefaultLabel, null); + assert.equal(actual.keybindingItem.keybinding, null); + assert.equal(actual.keybindingItem.when, ''); + }); + }); + + test('filter by command id', () => { + const id = 'workbench.action.increaseViewSize'; + registerCommandWithTitle(id, 'some title'); + prepareKeybindingService(); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('workbench action view size').filter(element => element.keybindingItem.command === id)[0]; + assert.ok(actual); + }); + }); + + test('filter by command title', () => { + const id = 'a' + uuid.generateUuid(); + registerCommandWithTitle(id, 'Increase view size'); + prepareKeybindingService(); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('increase size').filter(element => element.keybindingItem.command === id)[0]; + assert.ok(actual); + }); + }); + + test('filter by default source', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2' }); + prepareKeybindingService(expected); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('default').filter(element => element.keybindingItem.command === command)[0]; + assert.ok(actual); + }); + }); + + test('filter by user source', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2', isDefault: false }); + prepareKeybindingService(expected); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('user').filter(element => element.keybindingItem.command === command)[0]; + assert.ok(actual); + }); + }); + + test('filter by when context', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('when context').filter(element => element.keybindingItem.command === command)[0]; + assert.ok(actual); + }); + }); + + test('filter by cmd key', () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('cmd').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by meta key', () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('meta').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by command key', () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('command').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by windows key', () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Windows); + + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('windows').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by alt key', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('alt').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by option key', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('option').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by ctrl key', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('ctrl').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by control key', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('control').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by shift key', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('shift').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by arrow', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('arrow').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { keyCode: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by modifier and key', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('alt right').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true, keyCode: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by key and modifier', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('right alt').filter(element => element.keybindingItem.command === command); + assert.equal(0, actual.length); + }); + }); + + test('filter by modifiers and key', () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true, metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('alt cmd esc').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true, metaKey: true, keyCode: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by modifiers in random order and key', () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('cmd shift esc').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true, shiftKey: true, keyCode: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter by first part', () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('cmd shift esc').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true, shiftKey: true, keyCode: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter matches in chord part', () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('cmd del').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, { keyCode: true }); + }); + }); + + test('filter matches first part and in chord part', () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.UpArrow }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('cmd shift esc del').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true, metaKey: true, keyCode: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, { keyCode: true }); + }); + }); + + test('filter exact matches', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('"ctrl c"').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true, keyCode: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter exact matches with first and chord part', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('"shift meta escape ctrl c"').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true, metaKey: true, keyCode: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, { ctrlKey: true, keyCode: true }); + }); + }); + + test('filter exact matches with first and chord part no results', () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.UpArrow }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('"cmd shift esc del"').filter(element => element.keybindingItem.command === command); + assert.equal(0, actual.length); + }); + }); + + test('filter matches with + separator', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('"control+c"').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true, keyCode: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, {}); + }); + }); + + test('filter matches with + separator in first and chord parts', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('"shift+meta+escape ctrl+c"').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true, metaKey: true, keyCode: true }); + assert.deepEqual(actual[0].keybindingMatches.chordPart, { keyCode: true, ctrlKey: true }); + }); + }); + + test('filter exact matches with space #32993', () => { + const command = 'a' + uuid.generateUuid(); + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Space, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Backspace, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); + + return testObject.resolve().then(() => { + const actual = testObject.fetch('"ctrl+space"').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + }); + }); + + function prepareKeybindingService(...keybindingItems: ResolvedKeybindingItem[]): ResolvedKeybindingItem[] { + instantiationService.stub(IKeybindingService, 'getKeybindings', () => keybindingItems); + instantiationService.stub(IKeybindingService, 'getDefaultKeybindings', () => keybindingItems); + return keybindingItems; + + } + + function registerCommandWithTitle(command: string, title: string): void { + const registry = Registry.as(ActionExtensions.WorkbenchActions); + registry.registerWorkbenchAction(new SyncActionDescriptor(AnAction, command, title, { primary: null }), ''); + } + + function assertKeybindingItems(actual: ResolvedKeybindingItem[], expected: ResolvedKeybindingItem[]) { + assert.equal(actual.length, expected.length); + for (let i = 0; i < actual.length; i++) { + assertKeybindingItem(actual[i], expected[i]); + } + } + + function assertKeybindingItem(actual: ResolvedKeybindingItem, expected: ResolvedKeybindingItem): void { + assert.equal(actual.command, expected.command); + if (actual.when) { + assert.ok(!!expected.when); + assert.equal(actual.when.serialize(), expected.when.serialize()); + } else { + assert.ok(!expected.when); + } + assert.equal(actual.isDefault, expected.isDefault); + + if (actual.resolvedKeybinding) { + assert.ok(!!expected.resolvedKeybinding); + assert.equal(actual.resolvedKeybinding.getLabel(), expected.resolvedKeybinding.getLabel()); + } else { + assert.ok(!expected.resolvedKeybinding); + } + } + + function aResolvedKeybindingItem({ command, when, isDefault, firstPart, chordPart }: { command?: string, when?: string, isDefault?: boolean, firstPart?: { keyCode: KeyCode, modifiers?: Modifiers }, chordPart?: { keyCode: KeyCode, modifiers?: Modifiers } }): ResolvedKeybindingItem { + const aSimpleKeybinding = function (part: { keyCode: KeyCode, modifiers?: Modifiers }): SimpleKeybinding { + const { ctrlKey, shiftKey, altKey, metaKey } = part.modifiers || { ctrlKey: false, shiftKey: false, altKey: false, metaKey: false }; + return new SimpleKeybinding(ctrlKey, shiftKey, altKey, metaKey, part.keyCode); + }; + const keybinding = firstPart ? chordPart ? new ChordKeybinding(aSimpleKeybinding(firstPart), aSimpleKeybinding(chordPart)) : aSimpleKeybinding(firstPart) : null; + return new ResolvedKeybindingItem(keybinding ? new USLayoutResolvedKeybinding(keybinding, OS) : null, command || 'some command', null, when ? ContextKeyExpr.deserialize(when) : null, isDefault === void 0 ? true : isDefault); + } + + function asResolvedKeybindingItems(keybindingEntries: IKeybindingItemEntry[], keepUnassigned: boolean = false): ResolvedKeybindingItem[] { + if (!keepUnassigned) { + keybindingEntries = keybindingEntries.filter(keybindingEntry => !!keybindingEntry.keybindingItem.keybinding); + } + return keybindingEntries.map(entry => entry.keybindingItem.keybindingItem); + } + + +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/test/common/smartSnippetInserter.test.ts b/src/vs/workbench/parts/preferences/test/common/smartSnippetInserter.test.ts new file mode 100644 index 0000000000..e6da410c40 --- /dev/null +++ b/src/vs/workbench/parts/preferences/test/common/smartSnippetInserter.test.ts @@ -0,0 +1,164 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { SmartSnippetInserter } from 'vs/workbench/parts/preferences/common/smartSnippetInserter'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { Position } from 'vs/editor/common/core/position'; + +suite('SmartSnippetInserter', () => { + + function testSmartSnippetInserter(text: string[], runner: (assert: (desiredPos: Position, pos: Position, prepend: string, append: string) => void) => void): void { + let model = TextModel.createFromString(text.join('\n')); + runner((desiredPos, pos, prepend, append) => { + let actual = SmartSnippetInserter.insertSnippet(model, desiredPos); + let expected = { + position: pos, + prepend, + append + }; + assert.deepEqual(actual, expected); + }); + model.dispose(); + } + + test('empty text', () => { + testSmartSnippetInserter([ + ], (assert) => { + assert(new Position(1, 1), new Position(1, 1), '\n[', ']'); + }); + + testSmartSnippetInserter([ + ' ' + ], (assert) => { + assert(new Position(1, 1), new Position(1, 2), '\n[', ']'); + assert(new Position(1, 2), new Position(1, 2), '\n[', ']'); + }); + + testSmartSnippetInserter([ + '// just some text' + ], (assert) => { + assert(new Position(1, 1), new Position(1, 18), '\n[', ']'); + assert(new Position(1, 18), new Position(1, 18), '\n[', ']'); + }); + + testSmartSnippetInserter([ + '// just some text', + '' + ], (assert) => { + assert(new Position(1, 1), new Position(2, 1), '\n[', ']'); + assert(new Position(1, 18), new Position(2, 1), '\n[', ']'); + assert(new Position(2, 1), new Position(2, 1), '\n[', ']'); + }); + }); + + test('empty array 1', () => { + testSmartSnippetInserter([ + '// just some text', + '[]' + ], (assert) => { + assert(new Position(1, 1), new Position(2, 2), '', ''); + assert(new Position(2, 1), new Position(2, 2), '', ''); + assert(new Position(2, 2), new Position(2, 2), '', ''); + assert(new Position(2, 3), new Position(2, 2), '', ''); + }); + }); + + test('empty array 2', () => { + testSmartSnippetInserter([ + '// just some text', + '[', + ']' + ], (assert) => { + assert(new Position(1, 1), new Position(2, 2), '', ''); + assert(new Position(2, 1), new Position(2, 2), '', ''); + assert(new Position(2, 2), new Position(2, 2), '', ''); + assert(new Position(3, 1), new Position(3, 1), '', ''); + assert(new Position(3, 2), new Position(3, 1), '', ''); + }); + }); + + test('empty array 3', () => { + testSmartSnippetInserter([ + '// just some text', + '[', + '// just some text', + ']' + ], (assert) => { + assert(new Position(1, 1), new Position(2, 2), '', ''); + assert(new Position(2, 1), new Position(2, 2), '', ''); + assert(new Position(2, 2), new Position(2, 2), '', ''); + assert(new Position(3, 1), new Position(3, 1), '', ''); + assert(new Position(3, 2), new Position(3, 1), '', ''); + assert(new Position(4, 1), new Position(4, 1), '', ''); + assert(new Position(4, 2), new Position(4, 1), '', ''); + }); + }); + + test('one element array 1', () => { + testSmartSnippetInserter([ + '// just some text', + '[', + '{}', + ']' + ], (assert) => { + assert(new Position(1, 1), new Position(2, 2), '', ','); + assert(new Position(2, 1), new Position(2, 2), '', ','); + assert(new Position(2, 2), new Position(2, 2), '', ','); + assert(new Position(3, 1), new Position(3, 1), '', ','); + assert(new Position(3, 2), new Position(3, 1), '', ','); + assert(new Position(3, 3), new Position(3, 3), ',', ''); + assert(new Position(4, 1), new Position(4, 1), ',', ''); + assert(new Position(4, 2), new Position(4, 1), ',', ''); + }); + }); + + test('two elements array 1', () => { + testSmartSnippetInserter([ + '// just some text', + '[', + '{},', + '{}', + ']' + ], (assert) => { + assert(new Position(1, 1), new Position(2, 2), '', ','); + assert(new Position(2, 1), new Position(2, 2), '', ','); + assert(new Position(2, 2), new Position(2, 2), '', ','); + assert(new Position(3, 1), new Position(3, 1), '', ','); + assert(new Position(3, 2), new Position(3, 1), '', ','); + assert(new Position(3, 3), new Position(3, 3), ',', ''); + assert(new Position(3, 4), new Position(3, 4), '', ','); + assert(new Position(4, 1), new Position(4, 1), '', ','); + assert(new Position(4, 2), new Position(4, 1), '', ','); + assert(new Position(4, 3), new Position(4, 3), ',', ''); + assert(new Position(5, 1), new Position(5, 1), ',', ''); + assert(new Position(5, 2), new Position(5, 1), ',', ''); + }); + }); + + test('two elements array 2', () => { + testSmartSnippetInserter([ + '// just some text', + '[', + '{},{}', + ']' + ], (assert) => { + assert(new Position(1, 1), new Position(2, 2), '', ','); + assert(new Position(2, 1), new Position(2, 2), '', ','); + assert(new Position(2, 2), new Position(2, 2), '', ','); + assert(new Position(3, 1), new Position(3, 1), '', ','); + assert(new Position(3, 2), new Position(3, 1), '', ','); + assert(new Position(3, 3), new Position(3, 3), ',', ''); + assert(new Position(3, 4), new Position(3, 4), '', ','); + assert(new Position(3, 5), new Position(3, 4), '', ','); + assert(new Position(3, 6), new Position(3, 6), ',', ''); + assert(new Position(4, 1), new Position(4, 1), ',', ''); + assert(new Position(4, 2), new Position(4, 1), ',', ''); + }); + }); + +}); diff --git a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts new file mode 100644 index 0000000000..5411acd488 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts @@ -0,0 +1,616 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import arrays = require('vs/base/common/arrays'); +import types = require('vs/base/common/types'); +import { language, LANGUAGE_DEFAULT } from 'vs/base/common/platform'; +import { Action } from 'vs/base/common/actions'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { Mode, IEntryRunContext, IAutoFocus, IModel, IQuickNavigateConfiguration } from 'vs/base/parts/quickopen/common/quickOpen'; +import { QuickOpenEntryGroup, IHighlight, QuickOpenModel, QuickOpenEntry } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { SyncActionDescriptor, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { QuickOpenHandler, IWorkbenchQuickOpenConfiguration } from 'vs/workbench/browser/quickopen'; +import { IEditorAction, IEditor, isCommonCodeEditor, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { matchesWords, matchesPrefix, matchesContiguousSubString, or } from 'vs/base/common/filters'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, Severity, IMessageWithAction } from 'vs/platform/message/common/message'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { editorAction, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { once } from 'vs/base/common/event'; +import { BoundedMap, ISerializedBoundedLinkedMap } from 'vs/base/common/map'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; + +export const ALL_COMMANDS_PREFIX = '>'; + +let lastCommandPaletteInput: string; +let commandHistory: BoundedMap; +let commandCounter = 1; + +function resolveCommandHistory(configurationService: IConfigurationService): number { + const config = configurationService.getConfiguration(); + + let commandHistory = config.workbench && config.workbench.commandPalette && config.workbench.commandPalette.history; + if (typeof commandHistory !== 'number') { + commandHistory = CommandsHistory.DEFAULT_COMMANDS_HISTORY_LENGTH; + } + + return commandHistory; +} + +class CommandsHistory { + + public static readonly DEFAULT_COMMANDS_HISTORY_LENGTH = 50; + + private static readonly PREF_KEY_CACHE = 'commandPalette.mru.cache'; + private static readonly PREF_KEY_COUNTER = 'commandPalette.mru.counter'; + + private commandHistoryLength: number; + + constructor( + @IStorageService private storageService: IStorageService, + @ILifecycleService private lifecycleService: ILifecycleService, + @IConfigurationService private configurationService: IConfigurationService + ) { + this.updateConfiguration(); + this.load(); + + this.registerListeners(); + } + + private updateConfiguration(): void { + this.commandHistoryLength = resolveCommandHistory(this.configurationService); + + if (commandHistory) { + commandHistory.setLimit(this.commandHistoryLength); + } + } + + private load(): void { + const raw = this.storageService.get(CommandsHistory.PREF_KEY_CACHE); + let deserializedCache: ISerializedBoundedLinkedMap; + if (raw) { + try { + deserializedCache = JSON.parse(raw); + } catch (error) { + // invalid data + } + } + + commandHistory = new BoundedMap(this.commandHistoryLength, 1, deserializedCache); + commandCounter = this.storageService.getInteger(CommandsHistory.PREF_KEY_COUNTER, void 0, commandCounter); + } + + private registerListeners(): void { + this.configurationService.onDidUpdateConfiguration(e => this.updateConfiguration()); + once(this.lifecycleService.onShutdown)(reason => this.save()); + } + + private save(): void { + this.storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(commandHistory.serialize())); + this.storageService.store(CommandsHistory.PREF_KEY_COUNTER, commandCounter); + } + + public push(commandId: string): void { + + // make MRU by deleting it first + commandHistory.delete(commandId); + + // set counter to command + commandHistory.set(commandId, commandCounter++); + } + + public get(commandId: string): number { + return commandHistory.get(commandId); + } +} + +export class ShowAllCommandsAction extends Action { + + public static ID = 'workbench.action.showCommands'; + public static LABEL = nls.localize('showTriggerActions', "Show All Commands"); + + constructor( + id: string, + label: string, + @IQuickOpenService private quickOpenService: IQuickOpenService, + @IConfigurationService private configurationService: IConfigurationService + ) { + super(id, label); + } + + public run(context?: any): TPromise { + const config = this.configurationService.getConfiguration(); + const restoreInput = config.workbench && config.workbench.commandPalette && config.workbench.commandPalette.preserveInput === true; + + // Show with last command palette input if any and configured + let value = ALL_COMMANDS_PREFIX; + if (restoreInput && lastCommandPaletteInput) { + value = `${value}${lastCommandPaletteInput}`; + } + + this.quickOpenService.show(value, { inputSelection: lastCommandPaletteInput ? { start: 1 /* after prefix */, end: value.length } : void 0 }); + + return TPromise.as(null); + } +} + +export class ClearCommandHistoryAction extends Action { + + public static ID = 'workbench.action.clearCommandHistory'; + public static LABEL = nls.localize('clearCommandHistory', "Clear Command History"); + + constructor( + id: string, + label: string, + @IStorageService private storageService: IStorageService, + @IConfigurationService private configurationService: IConfigurationService + ) { + super(id, label); + } + + public run(context?: any): TPromise { + const commandHistoryLength = resolveCommandHistory(this.configurationService); + if (commandHistoryLength > 0) { + commandHistory = new BoundedMap(commandHistoryLength); + commandCounter = 1; + } + + return TPromise.as(null); + } +} + +@editorAction +class CommandPaletteEditorAction extends EditorAction { + + constructor() { + super({ + id: ShowAllCommandsAction.ID, + label: nls.localize('showCommands.label', "Command Palette..."), + alias: 'Command Palette', + precondition: null, + menuOpts: { + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise { + const quickOpenService = accessor.get(IQuickOpenService); + + // Show with prefix + quickOpenService.show(ALL_COMMANDS_PREFIX); + + return TPromise.as(null); + } +} + +abstract class BaseCommandEntry extends QuickOpenEntryGroup { + private description: string; + private alias: string; + private labelLowercase: string; + private keybindingAriaLabel: string; + + constructor( + private commandId: string, + private keybinding: ResolvedKeybinding, + private label: string, + alias: string, + highlights: { label: IHighlight[], alias: IHighlight[] }, + private onBeforeRun: (commandId: string) => void, + @IMessageService protected messageService: IMessageService, + @ITelemetryService protected telemetryService: ITelemetryService + ) { + super(); + + this.labelLowercase = this.label.toLowerCase(); + this.keybindingAriaLabel = keybinding ? keybinding.getAriaLabel() : void 0; + + if (this.label !== alias) { + this.alias = alias; + } else { + highlights.alias = null; + } + + this.setHighlights(highlights.label, null, highlights.alias); + } + + public getCommandId(): string { + return this.commandId; + } + + public getLabel(): string { + return this.label; + } + + public getSortLabel(): string { + return this.labelLowercase; + } + + public getDescription(): string { + return this.description; + } + + public setDescription(description: string): void { + this.description = description; + } + + public getKeybinding(): ResolvedKeybinding { + return this.keybinding; + } + + public getDetail(): string { + return this.alias; + } + + public getAriaLabel(): string { + if (this.keybindingAriaLabel) { + return nls.localize('entryAriaLabelWithKey', "{0}, {1}, commands", this.getLabel(), this.keybindingAriaLabel); + } + + return nls.localize('entryAriaLabel', "{0}, commands", this.getLabel()); + } + + protected onError(error?: Error): void; + protected onError(messagesWithAction?: IMessageWithAction): void; + protected onError(arg1?: any): void { + const messagesWithAction: IMessageWithAction = arg1; + if (messagesWithAction && typeof messagesWithAction.message === 'string' && Array.isArray(messagesWithAction.actions)) { + this.messageService.show(Severity.Error, messagesWithAction); + } else { + this.messageService.show(Severity.Error, !arg1 ? nls.localize('canNotRun', "Command '{0}' can not be run from here.", this.label) : toErrorMessage(arg1)); + } + } + + public run(mode: Mode, context: IEntryRunContext): boolean { + if (mode === Mode.OPEN) { + this.runAction(this.getAction()); + + return true; + } + + return false; + } + + protected abstract getAction(): Action | IEditorAction; + + protected runAction(action: Action | IEditorAction): void { + + // Indicate onBeforeRun + this.onBeforeRun(this.commandId); + + // Use a timeout to give the quick open widget a chance to close itself first + TPromise.timeout(50).done(() => { + if (action && (!(action instanceof Action) || action.enabled)) { + try { + this.telemetryService.publicLog('workbenchActionExecuted', { id: action.id, from: 'quick open' }); + (action.run() || TPromise.as(null)).done(() => { + if (action instanceof Action) { + action.dispose(); + } + }, err => this.onError(err)); + } catch (error) { + this.onError(error); + } + } else { + this.messageService.show(Severity.Info, nls.localize('actionNotEnabled', "Command '{0}' is not enabled in the current context.", this.getLabel())); + } + }, err => this.onError(err)); + } +} + +class CommandEntry extends BaseCommandEntry { + + constructor( + commandId: string, + keybinding: ResolvedKeybinding, + label: string, + meta: string, + highlights: { label: IHighlight[], alias: IHighlight[] }, + private actionDescriptor: SyncActionDescriptor, + onBeforeRun: (commandId: string) => void, + @IInstantiationService private instantiationService: IInstantiationService, + @IMessageService messageService: IMessageService, + @ITelemetryService telemetryService: ITelemetryService + ) { + super(commandId, keybinding, label, meta, highlights, onBeforeRun, messageService, telemetryService); + } + + protected getAction(): Action | IEditorAction { + return this.instantiationService.createInstance(this.actionDescriptor.syncDescriptor); + } +} + +class EditorActionCommandEntry extends BaseCommandEntry { + + constructor( + commandId: string, + keybinding: ResolvedKeybinding, + label: string, + meta: string, + highlights: { label: IHighlight[], alias: IHighlight[] }, + private action: IEditorAction, + onBeforeRun: (commandId: string) => void, + @IMessageService messageService: IMessageService, + @ITelemetryService telemetryService: ITelemetryService + ) { + super(commandId, keybinding, label, meta, highlights, onBeforeRun, messageService, telemetryService); + } + + protected getAction(): Action | IEditorAction { + return this.action; + } +} + +class ActionCommandEntry extends BaseCommandEntry { + + constructor( + commandId: string, + keybinding: ResolvedKeybinding, + label: string, + alias: string, + highlights: { label: IHighlight[], alias: IHighlight[] }, + private action: Action, + onBeforeRun: (commandId: string) => void, + @IMessageService messageService: IMessageService, + @ITelemetryService telemetryService: ITelemetryService + ) { + super(commandId, keybinding, label, alias, highlights, onBeforeRun, messageService, telemetryService); + } + + protected getAction(): Action | IEditorAction { + return this.action; + } +} + +const wordFilter = or(matchesPrefix, matchesWords, matchesContiguousSubString); + +export class CommandsHandler extends QuickOpenHandler { + private lastSearchValue: string; + private commandHistoryEnabled: boolean; + private commandsHistory: CommandsHistory; + + constructor( + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService private instantiationService: IInstantiationService, + @IKeybindingService private keybindingService: IKeybindingService, + @IMenuService private menuService: IMenuService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IConfigurationService private configurationService: IConfigurationService + ) { + super(); + + this.commandsHistory = this.instantiationService.createInstance(CommandsHistory); + + this.configurationService.onDidUpdateConfiguration(e => this.updateConfiguration()); + this.updateConfiguration(); + } + + private updateConfiguration(): void { + this.commandHistoryEnabled = resolveCommandHistory(this.configurationService) > 0; + } + + public getResults(searchValue: string): TPromise { + searchValue = searchValue.trim(); + this.lastSearchValue = searchValue; + + // Workbench Actions + let workbenchEntries: CommandEntry[] = []; + const workbenchActions = Registry.as(ActionExtensions.WorkbenchActions).getWorkbenchActions(); + workbenchEntries = this.actionDescriptorsToEntries(workbenchActions, searchValue); + + // Editor Actions + const activeEditor = this.editorService.getActiveEditor(); + const activeEditorControl = activeEditor ? activeEditor.getControl() : null; + + let editorActions: IEditorAction[] = []; + if (activeEditorControl) { + const editor = activeEditorControl; + if (types.isFunction(editor.getSupportedActions)) { + editorActions = editor.getSupportedActions(); + } + } + + const editorEntries = this.editorActionsToEntries(editorActions, searchValue); + + // Other Actions + const menu = isCommonCodeEditor(activeEditorControl) + ? activeEditorControl.invokeWithinContext(accessor => this.menuService.createMenu(MenuId.CommandPalette, accessor.get(IContextKeyService))) + : this.menuService.createMenu(MenuId.CommandPalette, this.contextKeyService); + + const menuActions = menu.getActions().reduce((r, [, actions]) => [...r, ...actions], []); + const commandEntries = this.menuItemActionsToEntries(menuActions, searchValue); + + // Concat + let entries = [...workbenchEntries, ...editorEntries, ...commandEntries]; + + // Remove duplicates + entries = arrays.distinct(entries, entry => `${entry.getLabel()}${entry.getGroupLabel()}${entry.getCommandId()}`); + + // Handle label clashes + const commandLabels = new Set(); + entries.forEach(entry => { + const commandLabel = `${entry.getLabel()}${entry.getGroupLabel()}`; + if (commandLabels.has(commandLabel)) { + entry.setDescription(entry.getCommandId()); + } else { + commandLabels.add(commandLabel); + } + }); + + // Sort by MRU order and fallback to name otherwie + entries = entries.sort((elementA, elementB) => { + const counterA = this.commandsHistory.get(elementA.getCommandId()); + const counterB = this.commandsHistory.get(elementB.getCommandId()); + + if (counterA && counterB) { + return counterA > counterB ? -1 : 1; // use more recently used command before older + } + + if (counterA) { + return -1; // first command was used, so it wins over the non used one + } + + if (counterB) { + return 1; // other command was used so it wins over the command + } + + // both commands were never used, so we sort by name + return elementA.getSortLabel().localeCompare(elementB.getSortLabel()); + }); + + // Introduce group marker border between recently used and others + // only if we have recently used commands in the result set + const firstEntry = entries[0]; + if (firstEntry && this.commandsHistory.get(firstEntry.getCommandId())) { + firstEntry.setGroupLabel(nls.localize('recentlyUsed', "recently used")); + for (let i = 1; i < entries.length; i++) { + const entry = entries[i]; + if (!this.commandsHistory.get(entry.getCommandId())) { + entry.setShowBorder(true); + entry.setGroupLabel(nls.localize('morecCommands', "other commands")); + break; + } + } + } + + return TPromise.as(new QuickOpenModel(entries)); + } + + private actionDescriptorsToEntries(actionDescriptors: SyncActionDescriptor[], searchValue: string): CommandEntry[] { + const entries: CommandEntry[] = []; + const registry = Registry.as(ActionExtensions.WorkbenchActions); + + for (let i = 0; i < actionDescriptors.length; i++) { + const actionDescriptor = actionDescriptors[i]; + if (actionDescriptor.label) { + + // Label (with optional category) + let label = actionDescriptor.label; + const category = registry.getCategory(actionDescriptor.id); + if (category) { + label = nls.localize('commandLabel', "{0}: {1}", category, label); + } + + // Alias for non default languages + const alias = (language !== LANGUAGE_DEFAULT) ? registry.getAlias(actionDescriptor.id) : null; + const labelHighlights = wordFilter(searchValue, label); + const aliasHighlights = alias ? wordFilter(searchValue, alias) : null; + + if (labelHighlights || aliasHighlights) { + entries.push(this.instantiationService.createInstance(CommandEntry, actionDescriptor.id, this.keybindingService.lookupKeybinding(actionDescriptor.id), label, alias, { label: labelHighlights, alias: aliasHighlights }, actionDescriptor, id => this.onBeforeRunCommand(id))); + } + } + } + + return entries; + } + + private editorActionsToEntries(actions: IEditorAction[], searchValue: string): EditorActionCommandEntry[] { + const entries: EditorActionCommandEntry[] = []; + + for (let i = 0; i < actions.length; i++) { + const action = actions[i]; + if (action.id === ShowAllCommandsAction.ID) { + continue; // avoid duplicates + } + + const label = action.label; + if (label) { + + // Alias for non default languages + const alias = (language !== LANGUAGE_DEFAULT) ? action.alias : null; + const labelHighlights = wordFilter(searchValue, label); + const aliasHighlights = alias ? wordFilter(searchValue, alias) : null; + + if (labelHighlights || aliasHighlights) { + entries.push(this.instantiationService.createInstance(EditorActionCommandEntry, action.id, this.keybindingService.lookupKeybinding(action.id), label, alias, { label: labelHighlights, alias: aliasHighlights }, action, id => this.onBeforeRunCommand(id))); + } + } + } + + return entries; + } + + private onBeforeRunCommand(commandId: string): void { + + // Remember as last command palette input + lastCommandPaletteInput = this.lastSearchValue; + + // Remember in commands history + this.commandsHistory.push(commandId); + } + + private menuItemActionsToEntries(actions: MenuItemAction[], searchValue: string): ActionCommandEntry[] { + const entries: ActionCommandEntry[] = []; + + for (let action of actions) { + const title = typeof action.item.title === 'string' ? action.item.title : action.item.title.value; + let category, label = title; + if (action.item.category) { + category = typeof action.item.category === 'string' ? action.item.category : action.item.category.value; + label = nls.localize('cat.title', "{0}: {1}", category, title); + } + + if (label) { + const labelHighlights = wordFilter(searchValue, label); + + // Add an 'alias' in original language when running in different locale + const aliasTitle = (language !== LANGUAGE_DEFAULT && typeof action.item.title !== 'string') ? action.item.title.original : null; + const aliasCategory = (language !== LANGUAGE_DEFAULT && category && typeof action.item.category !== 'string') ? action.item.category.original : null; + let alias; + if (aliasTitle && category) { + alias = aliasCategory ? `${aliasCategory}: ${aliasTitle}` : `${category}: ${aliasTitle}`; + } else if (aliasTitle) { + alias = aliasTitle; + } + const aliasHighlights = alias ? wordFilter(searchValue, alias) : null; + + if (labelHighlights || aliasHighlights) { + entries.push(this.instantiationService.createInstance(ActionCommandEntry, action.id, this.keybindingService.lookupKeybinding(action.item.id), label, alias, { label: labelHighlights, alias: aliasHighlights }, action, id => this.onBeforeRunCommand(id))); + } + } + } + + return entries; + } + + public getAutoFocus(searchValue: string, context: { model: IModel, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus { + let autoFocusPrefixMatch = searchValue.trim(); + + if (autoFocusPrefixMatch && this.commandHistoryEnabled) { + const firstEntry = context.model && context.model.entries[0]; + if (firstEntry instanceof BaseCommandEntry && this.commandsHistory.get(firstEntry.getCommandId())) { + autoFocusPrefixMatch = void 0; // keep focus on MRU element if we have history elements + } + } + + return { + autoFocusFirstEntry: true, + autoFocusPrefixMatch + }; + } + + public getEmptyLabel(searchString: string): string { + return nls.localize('noCommandsMatching', "No commands matching"); + } + + public onClose(canceled: boolean): void { + if (canceled) { + lastCommandPaletteInput = void 0; // clear last input when user canceled quick open + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts new file mode 100644 index 0000000000..ccde6ba545 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts @@ -0,0 +1,284 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import types = require('vs/base/common/types'); +import errors = require('vs/base/common/errors'); +import { IEntryRunContext, Mode, IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; +import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { KeyMod } from 'vs/base/common/keyCodes'; +import { QuickOpenHandler, EditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen'; +import { IEditor, IModelDecorationsChangeAccessor, OverviewRulerLane, IModelDeltaDecoration, IEditorViewState, ITextModel, IDiffEditorModel, ScrollType } from 'vs/editor/common/editorCommon'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Position, IEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; +import { IRange } from 'vs/editor/common/core/range'; +import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry'; +import { themeColorFromId } from 'vs/platform/theme/common/themeService'; + +export const GOTO_LINE_PREFIX = ':'; + +export class GotoLineAction extends QuickOpenAction { + + public static ID = 'workbench.action.gotoLine'; + public static LABEL = nls.localize('gotoLine', "Go to Line..."); + + constructor(actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService) { + super(actionId, actionLabel, GOTO_LINE_PREFIX, quickOpenService); + } +} + +class GotoLineEntry extends EditorQuickOpenEntry { + private line: number; + private column: number; + private handler: GotoLineHandler; + + constructor(line: string, editorService: IWorkbenchEditorService, handler: GotoLineHandler) { + super(editorService); + + this.parseInput(line); + this.handler = handler; + } + + private parseInput(line: string) { + const numbers = line.split(/,|:|#/).map(part => parseInt(part, 10)).filter(part => !isNaN(part)); + this.line = numbers[0]; + this.column = numbers[1]; + } + + public getLabel(): string { + + // Inform user about valid range if input is invalid + const maxLineNumber = this.getMaxLineNumber(); + if (this.invalidRange(maxLineNumber)) { + if (maxLineNumber > 0) { + return nls.localize('gotoLineLabelEmptyWithLimit', "Type a line number between 1 and {0} to navigate to", maxLineNumber); + } + + return nls.localize('gotoLineLabelEmpty', "Type a line number to navigate to"); + } + + // Input valid, indicate action + return this.column ? nls.localize('gotoLineColumnLabel', "Go to line {0} and character {1}", this.line, this.column) : nls.localize('gotoLineLabel', "Go to line {0}", this.line); + } + + private invalidRange(maxLineNumber: number = this.getMaxLineNumber()): boolean { + return !this.line || !types.isNumber(this.line) || (maxLineNumber > 0 && types.isNumber(this.line) && this.line > maxLineNumber); + } + + private getMaxLineNumber(): number { + const editor = this.editorService.getActiveEditor(); + const editorControl = editor.getControl(); + let model = editorControl.getModel(); + if (model && (model).modified && (model).original) { + model = (model).modified; // Support for diff editor models + } + + return model && types.isFunction((model).getLineCount) ? (model).getLineCount() : -1; + } + + public run(mode: Mode, context: IEntryRunContext): boolean { + if (mode === Mode.OPEN) { + return this.runOpen(context); + } + + return this.runPreview(); + } + + public getInput(): IEditorInput { + return this.editorService.getActiveEditorInput(); + } + + public getOptions(): ITextEditorOptions { + return { + selection: this.toSelection() + }; + } + + public runOpen(context: IEntryRunContext): boolean { + + // No-op if range is not valid + if (this.invalidRange()) { + return false; + } + + // Check for sideBySide use + const sideBySide = context.keymods.indexOf(KeyMod.CtrlCmd) >= 0; + if (sideBySide) { + this.editorService.openEditor(this.getInput(), this.getOptions(), true).done(null, errors.onUnexpectedError); + } + + // Apply selection and focus + const range = this.toSelection(); + const activeEditor = this.editorService.getActiveEditor(); + if (activeEditor) { + const editor = activeEditor.getControl(); + editor.setSelection(range); + editor.revealRangeInCenter(range, ScrollType.Smooth); + } + + return true; + } + + public runPreview(): boolean { + + // No-op if range is not valid + if (this.invalidRange()) { + this.handler.clearDecorations(); + + return false; + } + + // Select Line Position + const range = this.toSelection(); + const activeEditor = this.editorService.getActiveEditor(); + if (activeEditor) { + const editorControl = activeEditor.getControl(); + editorControl.revealRangeInCenter(range, ScrollType.Smooth); + + // Decorate if possible + if (types.isFunction(editorControl.changeDecorations)) { + this.handler.decorateOutline(range, editorControl, activeEditor.position); + } + } + + return false; + } + + private toSelection(): IRange { + return { + startLineNumber: this.line, + startColumn: this.column || 1, + endLineNumber: this.line, + endColumn: this.column || 1 + }; + } +} + +interface IEditorLineDecoration { + rangeHighlightId: string; + lineDecorationId: string; + position: Position; +} + +export class GotoLineHandler extends QuickOpenHandler { + private rangeHighlightDecorationId: IEditorLineDecoration; + private lastKnownEditorViewState: IEditorViewState; + + constructor( @IWorkbenchEditorService private editorService: IWorkbenchEditorService) { + super(); + } + + public getAriaLabel(): string { + return nls.localize('gotoLineHandlerAriaLabel', "Type a line number to navigate to."); + } + + public getResults(searchValue: string): TPromise { + searchValue = searchValue.trim(); + + // Remember view state to be able to restore on cancel + if (!this.lastKnownEditorViewState) { + const editor = this.editorService.getActiveEditor(); + this.lastKnownEditorViewState = (editor.getControl()).saveViewState(); + } + + return TPromise.as(new QuickOpenModel([new GotoLineEntry(searchValue, this.editorService, this)])); + } + + public canRun(): boolean | string { + const canRun = getCodeEditor(this.editorService.getActiveEditor()) !== null; + + return canRun ? true : nls.localize('cannotRunGotoLine', "Open a text file first to go to a line"); + } + + public decorateOutline(range: IRange, editor: IEditor, position: Position): void { + editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => { + const deleteDecorations: string[] = []; + + if (this.rangeHighlightDecorationId) { + deleteDecorations.push(this.rangeHighlightDecorationId.lineDecorationId); + deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId); + this.rangeHighlightDecorationId = null; + } + + const newDecorations: IModelDeltaDecoration[] = [ + // rangeHighlight at index 0 + { + range: range, + options: { + className: 'rangeHighlight', + isWholeLine: true + } + }, + + // lineDecoration at index 1 + { + range: range, + options: { + overviewRuler: { + color: themeColorFromId(overviewRulerRangeHighlight), + darkColor: themeColorFromId(overviewRulerRangeHighlight), + position: OverviewRulerLane.Full + } + } + } + ]; + + const decorations = changeAccessor.deltaDecorations(deleteDecorations, newDecorations); + const rangeHighlightId = decorations[0]; + const lineDecorationId = decorations[1]; + + this.rangeHighlightDecorationId = { + rangeHighlightId: rangeHighlightId, + lineDecorationId: lineDecorationId, + position: position + }; + }); + } + + public clearDecorations(): void { + if (this.rangeHighlightDecorationId) { + this.editorService.getVisibleEditors().forEach((editor) => { + if (editor.position === this.rangeHighlightDecorationId.position) { + const editorControl = editor.getControl(); + editorControl.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => { + changeAccessor.deltaDecorations([ + this.rangeHighlightDecorationId.lineDecorationId, + this.rangeHighlightDecorationId.rangeHighlightId + ], []); + }); + } + }); + + this.rangeHighlightDecorationId = null; + } + } + + public onClose(canceled: boolean): void { + + // Clear Highlight Decorations if present + this.clearDecorations(); + + // Restore selection if canceled + if (canceled && this.lastKnownEditorViewState) { + const activeEditor = this.editorService.getActiveEditor(); + if (activeEditor) { + const editor = activeEditor.getControl(); + editor.restoreViewState(this.lastKnownEditorViewState); + } + } + + this.lastKnownEditorViewState = null; + } + + public getAutoFocus(searchValue: string): IAutoFocus { + return { + autoFocusFirstEntry: searchValue.trim().length > 0 + }; + } +} diff --git a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts new file mode 100644 index 0000000000..ec910b6a05 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts @@ -0,0 +1,593 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/gotoSymbolHandler'; +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import errors = require('vs/base/common/errors'); +import types = require('vs/base/common/types'); +import strings = require('vs/base/common/strings'); +import { IEntryRunContext, Mode, IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; +import { QuickOpenModel, IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenHandler, EditorQuickOpenEntryGroup, QuickOpenAction } from 'vs/workbench/browser/quickopen'; +import filters = require('vs/base/common/filters'); +import { KeyMod } from 'vs/base/common/keyCodes'; +import { IEditor, IModelDecorationsChangeAccessor, OverviewRulerLane, IModelDeltaDecoration, IModel, ITokenizedModel, IDiffEditorModel, IEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { Position, IEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/common/quickOpen'; +import { DocumentSymbolProviderRegistry, SymbolInformation, symbolKindToCssClass } from 'vs/editor/common/modes'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; +import { IRange } from 'vs/editor/common/core/range'; +import { themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry'; + +export const GOTO_SYMBOL_PREFIX = '@'; +export const SCOPE_PREFIX = ':'; + +export class GotoSymbolAction extends QuickOpenAction { + + public static ID = 'workbench.action.gotoSymbol'; + public static LABEL = nls.localize('gotoSymbol', "Go to Symbol in File..."); + + constructor(actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService) { + super(actionId, actionLabel, GOTO_SYMBOL_PREFIX, quickOpenService); + } +} + +class OutlineModel extends QuickOpenModel { + private outline: Outline; + + constructor(outline: Outline, entries: SymbolEntry[]) { + super(entries); + + this.outline = outline; + } + + public applyFilter(searchValue: string): void { + + // Normalize search + let normalizedSearchValue = searchValue; + if (searchValue.indexOf(SCOPE_PREFIX) === 0) { + normalizedSearchValue = normalizedSearchValue.substr(SCOPE_PREFIX.length); + } + + // Check for match and update visibility and group label + this.entries.forEach((entry: SymbolEntry) => { + + // Clear all state first + entry.setGroupLabel(null); + entry.setShowBorder(false); + entry.setHighlights(null); + entry.setHidden(false); + + // Filter by search + if (normalizedSearchValue) { + const highlights = filters.matchesFuzzy(normalizedSearchValue, entry.getLabel()); + if (highlights) { + entry.setHighlights(highlights); + entry.setHidden(false); + } else if (!entry.isHidden()) { + entry.setHidden(true); + } + } + }); + + // Sort properly if actually searching + if (searchValue) { + if (searchValue.indexOf(SCOPE_PREFIX) === 0) { + this.entries.sort(this.sortScoped.bind(this, searchValue.toLowerCase())); + } else { + this.entries.sort(this.sortNormal.bind(this, searchValue.toLowerCase())); + } + } + + // Otherwise restore order as appearing in outline + else { + this.entries.sort((a: SymbolEntry, b: SymbolEntry) => a.getIndex() - b.getIndex()); + } + + // Mark all type groups + const visibleResults = this.getEntries(true); + if (visibleResults.length > 0 && searchValue.indexOf(SCOPE_PREFIX) === 0) { + let currentType: string = null; + let currentResult: SymbolEntry = null; + let typeCounter = 0; + + for (let i = 0; i < visibleResults.length; i++) { + const result = visibleResults[i]; + + // Found new type + if (currentType !== result.getType()) { + + // Update previous result with count + if (currentResult) { + currentResult.setGroupLabel(this.renderGroupLabel(currentType, typeCounter, this.outline)); + } + + currentType = result.getType(); + currentResult = result; + typeCounter = 1; + + result.setShowBorder(i > 0); + } + + // Existing type, keep counting + else { + typeCounter++; + } + } + + // Update previous result with count + if (currentResult) { + currentResult.setGroupLabel(this.renderGroupLabel(currentType, typeCounter, this.outline)); + } + } + + // Mark first entry as outline + else if (visibleResults.length > 0) { + visibleResults[0].setGroupLabel(nls.localize('symbols', "symbols ({0})", visibleResults.length)); + } + } + + private sortNormal(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number { + + // Handle hidden elements + if (elementA.isHidden() && elementB.isHidden()) { + return 0; + } else if (elementA.isHidden()) { + return 1; + } else if (elementB.isHidden()) { + return -1; + } + + const elementAName = elementA.getLabel().toLowerCase(); + const elementBName = elementB.getLabel().toLowerCase(); + + // Compare by name + const r = elementAName.localeCompare(elementBName); + if (r !== 0) { + return r; + } + + // If name identical sort by range instead + const elementARange = elementA.getRange(); + const elementBRange = elementB.getRange(); + + return elementARange.startLineNumber - elementBRange.startLineNumber; + } + + private sortScoped(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number { + + // Handle hidden elements + if (elementA.isHidden() && elementB.isHidden()) { + return 0; + } else if (elementA.isHidden()) { + return 1; + } else if (elementB.isHidden()) { + return -1; + } + + // Remove scope char + searchValue = searchValue.substr(SCOPE_PREFIX.length); + + // Sort by type first if scoped search + const elementAType = elementA.getType(); + const elementBType = elementB.getType(); + let r = elementAType.localeCompare(elementBType); + if (r !== 0) { + return r; + } + + // Special sort when searching in scoped mode + if (searchValue) { + const elementAName = elementA.getLabel().toLowerCase(); + const elementBName = elementB.getLabel().toLowerCase(); + + // Compare by name + r = elementAName.localeCompare(elementBName); + if (r !== 0) { + return r; + } + } + + // Default to sort by range + const elementARange = elementA.getRange(); + const elementBRange = elementB.getRange(); + + return elementARange.startLineNumber - elementBRange.startLineNumber; + } + + private renderGroupLabel(type: string, count: number, outline: Outline): string { + + const pattern = OutlineModel.getDefaultGroupLabelPatterns()[type]; + if (pattern) { + return strings.format(pattern, count); + } + + return type; + } + + private static getDefaultGroupLabelPatterns(): { [type: string]: string } { + const result: { [type: string]: string } = Object.create(null); + result['method'] = nls.localize('method', "methods ({0})"); + result['function'] = nls.localize('function', "functions ({0})"); + result['constructor'] = nls.localize('_constructor', "constructors ({0})"); + result['variable'] = nls.localize('variable', "variables ({0})"); + result['class'] = nls.localize('class', "classes ({0})"); + result['interface'] = nls.localize('interface', "interfaces ({0})"); + result['namespace'] = nls.localize('namespace', "namespaces ({0})"); + result['package'] = nls.localize('package', "packages ({0})"); + result['module'] = nls.localize('modules', "modules ({0})"); + result['property'] = nls.localize('property', "properties ({0})"); + result['enum'] = nls.localize('enum', "enumerations ({0})"); + result['string'] = nls.localize('string', "strings ({0})"); + result['rule'] = nls.localize('rule', "rules ({0})"); + result['file'] = nls.localize('file', "files ({0})"); + result['array'] = nls.localize('array', "arrays ({0})"); + result['number'] = nls.localize('number', "numbers ({0})"); + result['boolean'] = nls.localize('boolean', "booleans ({0})"); + result['object'] = nls.localize('object', "objects ({0})"); + result['key'] = nls.localize('key', "keys ({0})"); + return result; + } +} + +class SymbolEntry extends EditorQuickOpenEntryGroup { + private editorService: IWorkbenchEditorService; + private index: number; + private name: string; + private type: string; + private icon: string; + private description: string; + private range: IRange; + private handler: GotoSymbolHandler; + + constructor(index: number, name: string, type: string, description: string, icon: string, range: IRange, highlights: IHighlight[], editorService: IWorkbenchEditorService, handler: GotoSymbolHandler) { + super(); + + this.index = index; + this.name = name; + this.type = type; + this.icon = icon; + this.description = description; + this.range = range; + this.setHighlights(highlights); + this.editorService = editorService; + this.handler = handler; + } + + public getIndex(): number { + return this.index; + } + + public getLabel(): string { + return this.name; + } + + public getAriaLabel(): string { + return nls.localize('entryAriaLabel', "{0}, symbols", this.getLabel()); + } + + public getIcon(): string { + return this.icon; + } + + public getDescription(): string { + return this.description; + } + + public getType(): string { + return this.type; + } + + public getRange(): IRange { + return this.range; + } + + public getInput(): IEditorInput { + return this.editorService.getActiveEditorInput(); + } + + public getOptions(): ITextEditorOptions { + return { + selection: this.toSelection() + }; + } + + public run(mode: Mode, context: IEntryRunContext): boolean { + if (mode === Mode.OPEN) { + return this.runOpen(context); + } + + return this.runPreview(); + } + + private runOpen(context: IEntryRunContext): boolean { + + // Check for sideBySide use + const sideBySide = context.keymods.indexOf(KeyMod.CtrlCmd) >= 0; + if (sideBySide) { + this.editorService.openEditor(this.getInput(), this.getOptions(), true).done(null, errors.onUnexpectedError); + } + + // Apply selection and focus + else { + const range = this.toSelection(); + const activeEditor = this.editorService.getActiveEditor(); + if (activeEditor) { + const editor = activeEditor.getControl(); + editor.setSelection(range); + editor.revealRangeInCenter(range, ScrollType.Smooth); + } + } + + return true; + } + + private runPreview(): boolean { + + // Select Outline Position + const range = this.toSelection(); + const activeEditor = this.editorService.getActiveEditor(); + if (activeEditor) { + const editorControl = activeEditor.getControl(); + editorControl.revealRangeInCenter(range, ScrollType.Smooth); + + // Decorate if possible + if (types.isFunction(editorControl.changeDecorations)) { + this.handler.decorateOutline(this.range, range, editorControl, activeEditor.position); + } + } + + return false; + } + + private toSelection(): IRange { + return { + startLineNumber: this.range.startLineNumber, + startColumn: this.range.startColumn || 1, + endLineNumber: this.range.startLineNumber, + endColumn: this.range.startColumn || 1 + }; + } +} + +interface Outline { + entries: SymbolInformation[]; +} + +interface IEditorLineDecoration { + rangeHighlightId: string; + lineDecorationId: string; + position: Position; +} + +export class GotoSymbolHandler extends QuickOpenHandler { + private outlineToModelCache: { [modelId: string]: OutlineModel; }; + private rangeHighlightDecorationId: IEditorLineDecoration; + private lastKnownEditorViewState: IEditorViewState; + private activeOutlineRequest: TPromise; + + constructor( + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(); + + this.outlineToModelCache = {}; + } + + public getResults(searchValue: string): TPromise { + searchValue = searchValue.trim(); + + // Remember view state to be able to restore on cancel + if (!this.lastKnownEditorViewState) { + const editor = this.editorService.getActiveEditor(); + this.lastKnownEditorViewState = (editor.getControl()).saveViewState(); + } + + // Resolve Outline Model + return this.getActiveOutline().then(outline => { + + // Filter by search + outline.applyFilter(searchValue); + + return outline; + }); + } + + public getEmptyLabel(searchString: string): string { + if (searchString.length > 0) { + return nls.localize('noSymbolsMatching', "No symbols matching"); + } + + return nls.localize('noSymbolsFound', "No symbols found"); + } + + public getAriaLabel(): string { + return nls.localize('gotoSymbolHandlerAriaLabel', "Type to narrow down symbols of the currently active editor."); + } + + public canRun(): boolean | string { + let canRun = false; + + const editorControl: IEditor = getCodeEditor(this.editorService.getActiveEditor()); + if (editorControl) { + let model = editorControl.getModel(); + if (model && (model).modified && (model).original) { + model = (model).modified; // Support for diff editor models + } + + if (model && types.isFunction((model).getLanguageIdentifier)) { + canRun = DocumentSymbolProviderRegistry.has(model); + } + } + + return canRun ? true : editorControl !== null ? nls.localize('cannotRunGotoSymbolInFile', "No symbol information for the file") : nls.localize('cannotRunGotoSymbol', "Open a text file first to go to a symbol"); + } + + public getAutoFocus(searchValue: string): IAutoFocus { + searchValue = searchValue.trim(); + + // Remove any type pattern (:) from search value as needed + if (searchValue.indexOf(SCOPE_PREFIX) === 0) { + searchValue = searchValue.substr(SCOPE_PREFIX.length); + } + + return { + autoFocusPrefixMatch: searchValue, + autoFocusFirstEntry: !!searchValue + }; + } + + private toQuickOpenEntries(flattened: SymbolInformation[]): SymbolEntry[] { + const results: SymbolEntry[] = []; + + for (let i = 0; i < flattened.length; i++) { + const element = flattened[i]; + const label = strings.trim(element.name); + + // Show parent scope as description + const description: string = element.containerName; + const icon = symbolKindToCssClass(element.kind); + + // Add + results.push(new SymbolEntry(i, + label, icon, description, icon, + element.location.range, null, this.editorService, this + )); + } + + return results; + } + + private getActiveOutline(): TPromise { + if (!this.activeOutlineRequest) { + this.activeOutlineRequest = this.doGetActiveOutline(); + } + + return this.activeOutlineRequest; + } + + private doGetActiveOutline(): TPromise { + const editorControl: IEditor = getCodeEditor(this.editorService.getActiveEditor()); + if (editorControl) { + let model = editorControl.getModel(); + if (model && (model).modified && (model).original) { + model = (model).modified; // Support for diff editor models + } + + if (model && types.isFunction((model).getLanguageIdentifier)) { + + // Ask cache first + const modelId = (model).id; + if (this.outlineToModelCache[modelId]) { + return TPromise.as(this.outlineToModelCache[modelId]); + } + + return getDocumentSymbols(model).then(outline => { + + const model = new OutlineModel(outline, this.toQuickOpenEntries(outline.entries)); + + this.outlineToModelCache = {}; // Clear cache, only keep 1 outline + this.outlineToModelCache[modelId] = model; + + return model; + }); + } + } + + return TPromise.as(null); + } + + public decorateOutline(fullRange: IRange, startRange: IRange, editor: IEditor, position: Position): void { + editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => { + const deleteDecorations: string[] = []; + + if (this.rangeHighlightDecorationId) { + deleteDecorations.push(this.rangeHighlightDecorationId.lineDecorationId); + deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId); + this.rangeHighlightDecorationId = null; + } + + const newDecorations: IModelDeltaDecoration[] = [ + + // rangeHighlight at index 0 + { + range: fullRange, + options: { + className: 'rangeHighlight', + isWholeLine: true + } + }, + + // lineDecoration at index 1 + { + range: startRange, + options: { + overviewRuler: { + color: themeColorFromId(overviewRulerRangeHighlight), + darkColor: themeColorFromId(overviewRulerRangeHighlight), + position: OverviewRulerLane.Full + } + } + } + + ]; + + const decorations = changeAccessor.deltaDecorations(deleteDecorations, newDecorations); + const rangeHighlightId = decorations[0]; + const lineDecorationId = decorations[1]; + + this.rangeHighlightDecorationId = { + rangeHighlightId: rangeHighlightId, + lineDecorationId: lineDecorationId, + position: position + }; + }); + } + + public clearDecorations(): void { + if (this.rangeHighlightDecorationId) { + this.editorService.getVisibleEditors().forEach(editor => { + if (editor.position === this.rangeHighlightDecorationId.position) { + const editorControl = editor.getControl(); + editorControl.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => { + changeAccessor.deltaDecorations([ + this.rangeHighlightDecorationId.lineDecorationId, + this.rangeHighlightDecorationId.rangeHighlightId + ], []); + }); + } + }); + + this.rangeHighlightDecorationId = null; + } + } + + public onClose(canceled: boolean): void { + + // Clear Cache + this.outlineToModelCache = {}; + + // Clear Highlight Decorations if present + this.clearDecorations(); + + // Restore selection if canceled + if (canceled && this.lastKnownEditorViewState) { + const activeEditor = this.editorService.getActiveEditor(); + if (activeEditor) { + const editor = activeEditor.getControl(); + editor.restoreViewState(this.lastKnownEditorViewState); + } + } + + this.lastKnownEditorViewState = null; + this.activeOutlineRequest = null; + } +} diff --git a/src/vs/workbench/parts/quickopen/browser/helpHandler.ts b/src/vs/workbench/parts/quickopen/browser/helpHandler.ts new file mode 100644 index 0000000000..f152c39b33 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/helpHandler.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import types = require('vs/base/common/types'); +import { Registry } from 'vs/platform/registry/common/platform'; +import { Mode, IEntryRunContext, IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; +import { QuickOpenModel, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IQuickOpenRegistry, Extensions, QuickOpenHandler } from 'vs/workbench/browser/quickopen'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; + +export const HELP_PREFIX = '?'; + +class HelpEntry extends QuickOpenEntryGroup { + private prefix: string; + private description: string; + private quickOpenService: IQuickOpenService; + private openOnPreview: boolean; + + constructor(prefix: string, description: string, quickOpenService: IQuickOpenService, openOnPreview: boolean) { + super(); + + this.prefix = prefix || '\u2026'; + this.description = description; + this.quickOpenService = quickOpenService; + this.openOnPreview = openOnPreview; + } + + public getLabel(): string { + return this.prefix; + } + + public getAriaLabel(): string { + return nls.localize('entryAriaLabel', "{0}, picker help", this.getLabel()); + } + + public getDescription(): string { + return this.description; + } + + public run(mode: Mode, context: IEntryRunContext): boolean { + if (mode === Mode.OPEN || this.openOnPreview) { + this.quickOpenService.show(this.prefix); + } + + return false; + } +} + +export class HelpHandler extends QuickOpenHandler { + + constructor( @IQuickOpenService private quickOpenService: IQuickOpenService) { + super(); + } + + public getResults(searchValue: string): TPromise { + searchValue = searchValue.trim(); + + const registry = (Registry.as(Extensions.Quickopen)); + const handlerDescriptors = registry.getQuickOpenHandlers(); + + const defaultHandler = registry.getDefaultQuickOpenHandler(); + if (defaultHandler) { + handlerDescriptors.push(defaultHandler); + } + + const workbenchScoped: HelpEntry[] = []; + const editorScoped: HelpEntry[] = []; + let entry: HelpEntry; + + handlerDescriptors.sort((h1, h2) => h1.prefix.localeCompare(h2.prefix)).forEach((handlerDescriptor) => { + if (handlerDescriptor.prefix !== HELP_PREFIX) { + + // Descriptor has multiple help entries + if (types.isArray(handlerDescriptor.helpEntries)) { + for (let j = 0; j < handlerDescriptor.helpEntries.length; j++) { + const helpEntry = handlerDescriptor.helpEntries[j]; + + if (helpEntry.prefix.indexOf(searchValue) === 0) { + entry = new HelpEntry(helpEntry.prefix, helpEntry.description, this.quickOpenService, searchValue.length > 0); + if (helpEntry.needsEditor) { + editorScoped.push(entry); + } else { + workbenchScoped.push(entry); + } + } + } + } + + // Single Help entry for descriptor + else if (handlerDescriptor.prefix.indexOf(searchValue) === 0) { + entry = new HelpEntry(handlerDescriptor.prefix, handlerDescriptor.description, this.quickOpenService, searchValue.length > 0); + workbenchScoped.push(entry); + } + } + }); + + // Add separator for workbench scoped handlers + if (workbenchScoped.length > 0) { + workbenchScoped[0].setGroupLabel(nls.localize('globalCommands', "global commands")); + } + + // Add separator for editor scoped handlers + if (editorScoped.length > 0) { + editorScoped[0].setGroupLabel(nls.localize('editorCommands', "editor commands")); + if (workbenchScoped.length > 0) { + editorScoped[0].setShowBorder(true); + } + } + + return TPromise.as(new QuickOpenModel([...workbenchScoped, ...editorScoped])); + } + + public getAutoFocus(searchValue: string): IAutoFocus { + searchValue = searchValue.trim(); + return { + autoFocusFirstEntry: searchValue.length > 0, + autoFocusPrefixMatch: searchValue + }; + } +} diff --git a/src/vs/workbench/parts/quickopen/browser/media/Constant_16x.svg b/src/vs/workbench/parts/quickopen/browser/media/Constant_16x.svg new file mode 100644 index 0000000000..ed2a175100 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/Constant_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Constant_16x_inverse.svg b/src/vs/workbench/parts/quickopen/browser/media/Constant_16x_inverse.svg new file mode 100644 index 0000000000..173e427f96 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/Constant_16x_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/EnumItem_16x.svg b/src/vs/workbench/parts/quickopen/browser/media/EnumItem_16x.svg new file mode 100644 index 0000000000..aa901ec193 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/EnumItem_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/EnumItem_inverse_16x.svg b/src/vs/workbench/parts/quickopen/browser/media/EnumItem_inverse_16x.svg new file mode 100644 index 0000000000..791759092f --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/EnumItem_inverse_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode.svg b/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode.svg new file mode 100644 index 0000000000..0e202ec10b --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode_inverse.svg b/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode_inverse.svg new file mode 100644 index 0000000000..a508edcd3d --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode.svg b/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode.svg new file mode 100644 index 0000000000..ba2f2d091c --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode_inverse.svg b/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode_inverse.svg new file mode 100644 index 0000000000..21e1e814b2 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode.svg b/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode.svg new file mode 100644 index 0000000000..e776cbc565 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode_inverse.svg b/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode_inverse.svg new file mode 100644 index 0000000000..1b76b62be9 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode.svg b/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode.svg new file mode 100644 index 0000000000..788cc8d645 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode_inverse.svg b/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode_inverse.svg new file mode 100644 index 0000000000..6cec71cb03 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/gotoSymbolHandler.css b/src/vs/workbench/parts/quickopen/browser/media/gotoSymbolHandler.css new file mode 100644 index 0000000000..ae0dbe9c6f --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/gotoSymbolHandler.css @@ -0,0 +1,154 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constant { + background-image: url('Constant_16x.svg'); + background-repeat: no-repeat; + background-position: 0 -2px; +} +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constant, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constant { + background-image: url('Constant_16x_inverse.svg'); +} + +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum-member { + background-image: url('EnumItem_16x.svg'); + background-repeat: no-repeat; + background-position: 0 -2px; +} +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum-member, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum-member { + background-image: url('EnumItem_inverse_16x.svg'); +} + +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.struct { + background-image: url('Structure_16x_vscode.svg'); + background-repeat: no-repeat; + background-position: 0 -2px; +} +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.struct, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.struct { + background-image: url('Structure_16x_vscode_inverse.svg'); +} + +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.event { + background-image: url('Event_16x_vscode.svg'); + background-repeat: no-repeat; + background-position: 0 -2px; +} +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.event, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.event { + background-image: url('Event_16x_vscode_inverse.svg'); +} + +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.operator { + background-image: url('Operator_16x_vscode.svg'); + background-repeat: no-repeat; + background-position: 0 -2px; +} +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.operator, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.operator { + background-image: url('Operator_16x_vscode_inverse.svg'); +} + +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.type-parameter { + background-image: url('Template_16x_vscode.svg'); + background-repeat: no-repeat; + background-position: 0 -2px; +} +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.type-parameter, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.type-parameter { + background-image: url('Template_16x_vscode_inverse.svg'); +} + +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.object, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.namespace, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.package, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.key, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.array, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.number, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.null, +.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.boolean { + background-image: url('symbol-sprite.svg'); + background-repeat: no-repeat; +} + +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method, +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function, +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor { background-position: 0 -4px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field, +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable { background-position: -22px -4px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class { background-position: -43px -3px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface { background-position: -63px -4px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.object, +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.namespace, +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.package, +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module { background-position: -82px -4px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property { background-position: -102px -3px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum { background-position: -122px -3px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.key, +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string { background-position: -202px -3px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule { background-position: -242px -4px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file { background-position: -262px -4px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.array { background-position: -302px -4px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.number { background-position: -322px -4px; } +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.null, +.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.boolean { background-position: -343px -4px; } + +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method, +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function, +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor { background-position: 0 -24px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field, +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable { background-position: -22px -24px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class { background-position: -43px -23px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface { background-position: -63px -24px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.object, +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.namespace, +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.package, +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.object, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.namespace, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.package, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module { background-position: -82px -24px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property { background-position: -102px -23px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.key, +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.key, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string { background-position: -202px -23px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum { background-position: -122px -23px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule { background-position: -242px -24px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file { background-position: -262px -24px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.array, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.array { background-position: -302px -24px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.number, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.number { background-position: -322px -24px; } +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.null, +.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.boolean, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.null, +.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.boolean { background-position: -342px -24px; } diff --git a/src/vs/workbench/parts/quickopen/browser/media/symbol-sprite.svg b/src/vs/workbench/parts/quickopen/browser/media/symbol-sprite.svg new file mode 100644 index 0000000000..ee9a63dcf6 --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/media/symbol-sprite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts new file mode 100644 index 0000000000..499f118d0f --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import env = require('vs/base/common/platform'); +import nls = require('vs/nls'); +import { QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions as QuickOpenExtensions } from 'vs/workbench/browser/quickopen'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { GotoSymbolAction, GOTO_SYMBOL_PREFIX, SCOPE_PREFIX } from 'vs/workbench/parts/quickopen/browser/gotoSymbolHandler'; +import { ShowAllCommandsAction, ALL_COMMANDS_PREFIX, ClearCommandHistoryAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; +import { GotoLineAction, GOTO_LINE_PREFIX } from 'vs/workbench/parts/quickopen/browser/gotoLineHandler'; +import { HELP_PREFIX } from 'vs/workbench/parts/quickopen/browser/helpHandler'; +import { VIEW_PICKER_PREFIX, OpenViewPickerAction, QuickOpenViewPickerAction } from 'vs/workbench/parts/quickopen/browser/viewPickerHandler'; +import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; + +// Register Actions +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(ClearCommandHistoryAction, ClearCommandHistoryAction.ID, ClearCommandHistoryAction.LABEL), 'Clear Command History'); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllCommandsAction, ShowAllCommandsAction.ID, ShowAllCommandsAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_P, + secondary: [KeyCode.F1] +}), 'Show All Commands'); + +registry.registerWorkbenchAction(new SyncActionDescriptor(GotoLineAction, GotoLineAction.ID, GotoLineAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_G, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G } +}), 'Go to Line...'); + +registry.registerWorkbenchAction(new SyncActionDescriptor(GotoSymbolAction, GotoSymbolAction.ID, GotoSymbolAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O +}), 'Go to Symbol in File...'); + +const inViewsPickerContextKey = 'inViewsPicker'; +const inViewsPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(inViewsPickerContextKey)); + +const viewPickerKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.KEY_Q, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_Q }, linux: { primary: null } }; + +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenViewPickerAction, OpenViewPickerAction.ID, OpenViewPickerAction.LABEL), 'Open View'); +registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenViewPickerAction, QuickOpenViewPickerAction.ID, QuickOpenViewPickerAction.LABEL, viewPickerKeybinding), 'Quick Open View'); + +const quickOpenNavigateNextInViewPickerId = 'workbench.action.quickOpenNavigateNextInViewPicker'; +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: quickOpenNavigateNextInViewPickerId, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + handler: getQuickNavigateHandler(quickOpenNavigateNextInViewPickerId, true), + when: inViewsPickerContext, + primary: viewPickerKeybinding.primary, + linux: viewPickerKeybinding.linux, + mac: viewPickerKeybinding.mac +}); + +const quickOpenNavigatePreviousInViewPickerId = 'workbench.action.quickOpenNavigatePreviousInViewPicker'; +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: quickOpenNavigatePreviousInViewPickerId, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + handler: getQuickNavigateHandler(quickOpenNavigatePreviousInViewPickerId, false), + when: inViewsPickerContext, + primary: viewPickerKeybinding.primary | KeyMod.Shift, + linux: viewPickerKeybinding.linux, + mac: { + primary: viewPickerKeybinding.mac.primary | KeyMod.Shift + } +}); + +// Register Quick Open Handler + +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/quickopen/browser/commandsHandler', + 'CommandsHandler', + ALL_COMMANDS_PREFIX, + 'inCommandsPicker', + nls.localize('commandsHandlerDescriptionDefault', "Show and Run Commands") + ) +); + +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/quickopen/browser/gotoLineHandler', + 'GotoLineHandler', + GOTO_LINE_PREFIX, + null, + [ + { + prefix: GOTO_LINE_PREFIX, + needsEditor: true, + description: env.isMacintosh ? nls.localize('gotoLineDescriptionMac', "Go to Line") : nls.localize('gotoLineDescriptionWin', "Go to Line") + }, + ] + ) +); + +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/quickopen/browser/gotoSymbolHandler', + 'GotoSymbolHandler', + GOTO_SYMBOL_PREFIX, + 'inFileSymbolsPicker', + [ + { + prefix: GOTO_SYMBOL_PREFIX, + needsEditor: true, + description: nls.localize('gotoSymbolDescription', "Go to Symbol in File") + }, + { + prefix: GOTO_SYMBOL_PREFIX + SCOPE_PREFIX, + needsEditor: true, + description: nls.localize('gotoSymbolDescriptionScoped', "Go to Symbol in File by Category") + } + ] + ) +); + +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/quickopen/browser/helpHandler', + 'HelpHandler', + HELP_PREFIX, + null, + nls.localize('helpDescription', "Show Help") + ) +); + +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/quickopen/browser/viewPickerHandler', + 'ViewPickerHandler', + VIEW_PICKER_PREFIX, + inViewsPickerContextKey, + [ + { + prefix: VIEW_PICKER_PREFIX, + needsEditor: false, + description: nls.localize('viewPickerDescription', "Open View") + } + ] + ) +); \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts new file mode 100644 index 0000000000..e46e68873b --- /dev/null +++ b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts @@ -0,0 +1,210 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import errors = require('vs/base/common/errors'); +import strings = require('vs/base/common/strings'); +import scorer = require('vs/base/common/scorer'); +import { Mode, IEntryRunContext, IAutoFocus, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen'; +import { QuickOpenModel, QuickOpenEntryGroup, QuickOpenEntry } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenHandler, QuickOpenAction } from 'vs/workbench/browser/quickopen'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IOutputService, OUTPUT_PANEL_ID } from 'vs/workbench/parts/output/common/output'; +import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/parts/terminal/common/terminal'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { Action } from 'vs/base/common/actions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; + +export const VIEW_PICKER_PREFIX = 'view '; + +export class ViewEntry extends QuickOpenEntryGroup { + + constructor( + private label: string, + private category: string, + private open: () => void + ) { + super(); + } + + public getLabel(): string { + return this.label; + } + + public getCategory(): string { + return this.category; + } + + public getAriaLabel(): string { + return nls.localize('entryAriaLabel', "{0}, view picker", this.getLabel()); + } + + public run(mode: Mode, context: IEntryRunContext): boolean { + if (mode === Mode.OPEN) { + return this.runOpen(context); + } + + return super.run(mode, context); + } + + private runOpen(context: IEntryRunContext): boolean { + setTimeout(() => { + this.open(); + }, 0); + + return true; + } +} + +export class ViewPickerHandler extends QuickOpenHandler { + + constructor( + @IViewletService private viewletService: IViewletService, + @IOutputService private outputService: IOutputService, + @ITerminalService private terminalService: ITerminalService, + @IPanelService private panelService: IPanelService + ) { + super(); + } + + public getResults(searchValue: string): TPromise { + searchValue = searchValue.trim(); + const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase(); + + const viewEntries = this.getViewEntries(); + + const entries = viewEntries.filter(e => { + if (!searchValue) { + return true; + } + + if (!scorer.matches(e.getLabel(), normalizedSearchValueLowercase) && !scorer.matches(e.getCategory(), normalizedSearchValueLowercase)) { + return false; + } + + const { labelHighlights, descriptionHighlights } = QuickOpenEntry.highlight(e, searchValue); + e.setHighlights(labelHighlights, descriptionHighlights); + + return true; + }); + + let lastCategory: string; + entries.forEach((e, index) => { + if (lastCategory !== e.getCategory()) { + lastCategory = e.getCategory(); + + e.setShowBorder(index > 0); + e.setGroupLabel(lastCategory); + } else { + e.setShowBorder(false); + e.setGroupLabel(void 0); + } + }); + + return TPromise.as(new QuickOpenModel(entries)); + } + + private getViewEntries(): ViewEntry[] { + const viewEntries: ViewEntry[] = []; + + // Viewlets + const viewlets = this.viewletService.getViewlets(); + viewlets.forEach((viewlet, index) => { + const viewsCategory = nls.localize('views', "Views"); + const entry = new ViewEntry(viewlet.name, viewsCategory, () => this.viewletService.openViewlet(viewlet.id, true).done(null, errors.onUnexpectedError)); + viewEntries.push(entry); + }); + + const terminals = this.terminalService.terminalInstances; + + // Panels + const panels = this.panelService.getPanels().filter(p => { + if (p.id === OUTPUT_PANEL_ID) { + return false; // since we already show output channels below + } + + if (p.id === TERMINAL_PANEL_ID && terminals.length > 0) { + return false; // since we already show terminal instances below + } + + return true; + }); + panels.forEach((panel, index) => { + const panelsCategory = nls.localize('panels', "Panels"); + const entry = new ViewEntry(panel.name, panelsCategory, () => this.panelService.openPanel(panel.id, true).done(null, errors.onUnexpectedError)); + + viewEntries.push(entry); + }); + + // Terminals + terminals.forEach((terminal, index) => { + const terminalsCategory = nls.localize('terminals', "Terminal"); + const entry = new ViewEntry(nls.localize('terminalTitle', "{0}: {1}", index + 1, terminal.title), terminalsCategory, () => { + this.terminalService.showPanel(true).done(() => { + this.terminalService.setActiveInstance(terminal); + }, errors.onUnexpectedError); + }); + + viewEntries.push(entry); + }); + + // Output Channels + const channels = this.outputService.getChannels(); + channels.forEach((channel, index) => { + const outputCategory = nls.localize('channels', "Output"); + const entry = new ViewEntry(channel.label, outputCategory, () => this.outputService.getChannel(channel.id).show().done(null, errors.onUnexpectedError)); + + viewEntries.push(entry); + }); + + return viewEntries; + } + + public getAutoFocus(searchValue: string, context: { model: IModel, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus { + return { + autoFocusFirstEntry: !!searchValue || !!context.quickNavigateConfiguration + }; + } +} + +export class OpenViewPickerAction extends QuickOpenAction { + + public static ID = 'workbench.action.openView'; + public static LABEL = nls.localize('openView', "Open View"); + + constructor( + id: string, + label: string, + @IQuickOpenService quickOpenService: IQuickOpenService + ) { + super(id, label, VIEW_PICKER_PREFIX, quickOpenService); + } +} + +export class QuickOpenViewPickerAction extends Action { + + public static ID = 'workbench.action.quickOpenView'; + public static LABEL = nls.localize('quickOpenView', "Quick Open View"); + + constructor( + id: string, + label: string, + @IQuickOpenService private quickOpenService: IQuickOpenService, + @IKeybindingService private keybindingService: IKeybindingService + ) { + super(id, label); + } + + public run(): TPromise { + const keys = this.keybindingService.lookupKeybindings(this.id); + + this.quickOpenService.show(VIEW_PICKER_PREFIX, { quickNavigateConfiguration: { keybindings: keys } }); + + return TPromise.as(true); + } +} diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts new file mode 100644 index 0000000000..1c4d74f201 --- /dev/null +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { IWindowsService, IWindowService, IWindowsConfiguration } from 'vs/platform/windows/common/windows'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { localize } from 'vs/nls'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; + +interface IConfiguration extends IWindowsConfiguration { + update: { channel: string; }; + telemetry: { enableCrashReporter: boolean }; +} + +export class SettingsChangeRelauncher implements IWorkbenchContribution { + + private toDispose: IDisposable[] = []; + + private titleBarStyle: 'native' | 'custom'; + private nativeTabs: boolean; + private updateChannel: string; + private enableCrashReporter: boolean; + private rootCount: number; + private firstRootPath: string; + + constructor( + @IWindowsService private windowsService: IWindowsService, + @IWindowService private windowService: IWindowService, + @IConfigurationService private configurationService: IConfigurationService, + @IPreferencesService private preferencesService: IPreferencesService, + @IEnvironmentService private envService: IEnvironmentService, + @IMessageService private messageService: IMessageService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IExtensionService private extensionService: IExtensionService + ) { + const workspace = this.contextService.getWorkspace(); + if (workspace) { + this.rootCount = workspace.roots.length; + this.firstRootPath = workspace.roots.length > 0 ? workspace.roots[0].fsPath : void 0; + } else { + this.rootCount = 0; + } + + this.onConfigurationChange(configurationService.getConfiguration(), false); + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration(), true))); + this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots())); + } + + private onConfigurationChange(config: IConfiguration, notify: boolean): void { + let changed = false; + + // Titlebar style + if (config.window && config.window.titleBarStyle !== this.titleBarStyle && (config.window.titleBarStyle === 'native' || config.window.titleBarStyle === 'custom')) { + this.titleBarStyle = config.window.titleBarStyle; + changed = true; + } + + // Native tabs + if (config.window && typeof config.window.nativeTabs === 'boolean' && config.window.nativeTabs !== this.nativeTabs) { + this.nativeTabs = config.window.nativeTabs; + changed = true; + } + + // Update channel + if (config.update && typeof config.update.channel === 'string' && config.update.channel !== this.updateChannel) { + this.updateChannel = config.update.channel; + changed = true; + } + + // Crash reporter + if (config.telemetry && typeof config.telemetry.enableCrashReporter === 'boolean' && config.telemetry.enableCrashReporter !== this.enableCrashReporter) { + this.enableCrashReporter = config.telemetry.enableCrashReporter; + changed = true; + } + + // Notify only when changed and we are the focused window (avoids notification spam across windows) + if (notify && changed) { + this.doConfirm( + localize('relaunchSettingMessage', "A setting has changed that requires a restart to take effect."), + localize('relaunchSettingDetail', "Press the restart button to restart {0} and enable the setting.", this.envService.appNameLong), + localize('restart', "Restart"), + () => this.windowsService.relaunch(Object.create(null)) + ); + } + } + + private onDidChangeWorkspaceRoots(): void { + const workspace = this.contextService.getWorkspace(); + + const newRootCount = workspace ? workspace.roots.length : 0; + const newFirstRootPath = workspace && workspace.roots.length > 0 ? workspace.roots[0].fsPath : void 0; + + let reloadWindow = false; + let reloadExtensionHost = false; + + if (this.rootCount === 0 && newRootCount > 0) { + reloadWindow = true; // transition: from 0 folders to 1+ + } else if (this.rootCount > 0 && newRootCount === 0) { + reloadWindow = true; // transition: from 1+ folders to 0 + } + + if (this.firstRootPath !== newFirstRootPath) { + reloadExtensionHost = true; // first root folder changed (impact on deprecated workspace.rootPath API) + } + + this.rootCount = newRootCount; + this.firstRootPath = newFirstRootPath; + + // Reload window if this is needed + if (reloadWindow) { + this.doConfirm( + localize('relaunchWorkspaceMessage', "This workspace change requires a reload of our extension system."), + void 0, + localize('reload', "Reload"), + () => this.windowService.reloadWindow() + ); + } + + // Reload extension host if this is needed + else if (reloadExtensionHost) { + this.extensionService.restartExtensionHost(); + } + } + + private doConfirm(message: string, detail: string, primaryButton: string, confirmed: () => void): void { + this.windowService.isFocused().then(focused => { + if (focused) { + const confirm = this.messageService.confirm({ + type: 'info', + message, + detail, + primaryButton + }); + + if (confirm) { + confirmed(); + } + } + }); + } + + public getId(): string { + return 'workbench.relauncher'; + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } +} + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(SettingsChangeRelauncher); diff --git a/src/vs/workbench/parts/scm/common/scm.ts b/src/vs/workbench/parts/scm/common/scm.ts new file mode 100644 index 0000000000..33df5ecec3 --- /dev/null +++ b/src/vs/workbench/parts/scm/common/scm.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export const VIEWLET_ID = 'workbench.view.scm'; \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts new file mode 100644 index 0000000000..53a16ac88f --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts @@ -0,0 +1,332 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); + +import 'vs/css!./media/dirtydiffDecorator'; +import { ThrottledDelayer, always } from 'vs/base/common/async'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as ext from 'vs/workbench/common/contributions'; +import * as common from 'vs/editor/common/editorCommon'; +import * as widget from 'vs/editor/browser/codeEditor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import URI from 'vs/base/common/uri'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { ISCMService } from 'vs/workbench/services/scm/common/scm'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { registerThemingParticipant, ITheme, ICssStyleCollector, themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { localize } from 'vs/nls'; +import { Color, RGBA } from 'vs/base/common/color'; + +export const editorGutterModifiedBackground = registerColor('editorGutter.modifiedBackground', { + dark: Color.fromHex('#00bcf2').transparent(0.6), + light: Color.fromHex('#007acc').transparent(0.6), + hc: Color.fromHex('#007acc').transparent(0.6) +}, localize('editorGutterModifiedBackground', "Editor gutter background color for lines that are modified.")); + +export const editorGutterAddedBackground = registerColor('editorGutter.addedBackground', { + dark: Color.fromHex('#7fba00').transparent(0.6), + light: Color.fromHex('#2d883e').transparent(0.6), + hc: Color.fromHex('#2d883e').transparent(0.6) +}, localize('editorGutterAddedBackground', "Editor gutter background color for lines that are added.")); + +export const editorGutterDeletedBackground = registerColor('editorGutter.deletedBackground', { + dark: Color.fromHex('#b9131a').transparent(0.76), + light: Color.fromHex('#b9131a').transparent(0.76), + hc: Color.fromHex('#b9131a').transparent(0.76) +}, localize('editorGutterDeletedBackground', "Editor gutter background color for lines that are deleted.")); + + +const overviewRulerDefault = new Color(new RGBA(0, 122, 204, 0.6)); +export const overviewRulerModifiedForeground = registerColor('editorOverviewRuler.modifiedForeground', { dark: overviewRulerDefault, light: overviewRulerDefault, hc: overviewRulerDefault }, nls.localize('overviewRulerModifiedForeground', 'Overview ruler marker color for modified content.')); +export const overviewRulerAddedForeground = registerColor('editorOverviewRuler.addedForeground', { dark: overviewRulerDefault, light: overviewRulerDefault, hc: overviewRulerDefault }, nls.localize('overviewRulerAddedForeground', 'Overview ruler marker color for added content.')); +export const overviewRulerDeletedForeground = registerColor('editorOverviewRuler.deletedForeground', { dark: overviewRulerDefault, light: overviewRulerDefault, hc: overviewRulerDefault }, nls.localize('overviewRulerDeletedForeground', 'Overview ruler marker color for deleted content.')); + + +class DirtyDiffModelDecorator { + + static MODIFIED_DECORATION_OPTIONS = ModelDecorationOptions.register({ + linesDecorationsClassName: 'dirty-diff-modified-glyph', + isWholeLine: true, + overviewRuler: { + color: themeColorFromId(overviewRulerModifiedForeground), + darkColor: themeColorFromId(overviewRulerModifiedForeground), + position: common.OverviewRulerLane.Left + } + }); + + static ADDED_DECORATION_OPTIONS = ModelDecorationOptions.register({ + linesDecorationsClassName: 'dirty-diff-added-glyph', + isWholeLine: true, + overviewRuler: { + color: themeColorFromId(overviewRulerAddedForeground), + darkColor: themeColorFromId(overviewRulerAddedForeground), + position: common.OverviewRulerLane.Left + } + }); + + static DELETED_DECORATION_OPTIONS = ModelDecorationOptions.register({ + linesDecorationsClassName: 'dirty-diff-deleted-glyph', + isWholeLine: true, + overviewRuler: { + color: themeColorFromId(overviewRulerDeletedForeground), + darkColor: themeColorFromId(overviewRulerDeletedForeground), + position: common.OverviewRulerLane.Left + } + }); + + private decorations: string[]; + private baselineModel: common.IModel; + private diffDelayer: ThrottledDelayer; + private _originalURIPromise: TPromise; + private toDispose: IDisposable[]; + + constructor( + private model: common.IModel, + private uri: URI, + @ISCMService private scmService: ISCMService, + @IModelService private modelService: IModelService, + @IEditorWorkerService private editorWorkerService: IEditorWorkerService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ITextModelService private textModelResolverService: ITextModelService + ) { + this.decorations = []; + this.diffDelayer = new ThrottledDelayer(200); + this.toDispose = []; + this.triggerDiff(); + this.toDispose.push(model.onDidChangeContent(() => this.triggerDiff())); + this.toDispose.push(scmService.onDidChangeRepository(() => this.triggerDiff())); + } + + private triggerDiff(): TPromise { + if (!this.diffDelayer) { + return TPromise.as(null); + } + + return this.diffDelayer + .trigger(() => this.diff()) + .then((diff: common.IChange[]) => { + if (!this.model || this.model.isDisposed() || !this.baselineModel || this.baselineModel.isDisposed()) { + return undefined; // disposed + } + + if (this.baselineModel.getValueLength() === 0) { + diff = []; + } + + return this.decorations = this.model.deltaDecorations(this.decorations, DirtyDiffModelDecorator.changesToDecorations(diff || [])); + }); + } + + private diff(): TPromise { + return this.getOriginalURIPromise().then(originalURI => { + if (!this.model || this.model.isDisposed() || !originalURI) { + return TPromise.as([]); // disposed + } + + if (!this.editorWorkerService.canComputeDirtyDiff(originalURI, this.model.uri)) { + return TPromise.as([]); // Files too large + } + + return this.editorWorkerService.computeDirtyDiff(originalURI, this.model.uri, true); + }); + } + + private getOriginalURIPromise(): TPromise { + if (this._originalURIPromise) { + return this._originalURIPromise; + } + + this._originalURIPromise = this.getOriginalResource() + .then(originalUri => { + if (!originalUri) { + return null; + } + + return this.textModelResolverService.createModelReference(originalUri) + .then(ref => { + this.baselineModel = ref.object.textEditorModel; + + this.toDispose.push(ref); + this.toDispose.push(ref.object.textEditorModel.onDidChangeContent(() => this.triggerDiff())); + + return originalUri; + }); + }); + + return always(this._originalURIPromise, () => { + this._originalURIPromise = null; + }); + } + + private async getOriginalResource(): TPromise { + for (const repository of this.scmService.repositories) { + const result = repository.provider.getOriginalResource(this.uri); + + if (result) { + return result; + } + } + + return null; + } + + private static changesToDecorations(diff: common.IChange[]): common.IModelDeltaDecoration[] { + return diff.map((change) => { + const startLineNumber = change.modifiedStartLineNumber; + const endLineNumber = change.modifiedEndLineNumber || startLineNumber; + + // Added + if (change.originalEndLineNumber === 0) { + return { + range: { + startLineNumber: startLineNumber, startColumn: 1, + endLineNumber: endLineNumber, endColumn: 1 + }, + options: DirtyDiffModelDecorator.ADDED_DECORATION_OPTIONS + }; + } + + // Removed + if (change.modifiedEndLineNumber === 0) { + return { + range: { + startLineNumber: startLineNumber, startColumn: 1, + endLineNumber: startLineNumber, endColumn: 1 + }, + options: DirtyDiffModelDecorator.DELETED_DECORATION_OPTIONS + }; + } + + // Modified + return { + range: { + startLineNumber: startLineNumber, startColumn: 1, + endLineNumber: endLineNumber, endColumn: 1 + }, + options: DirtyDiffModelDecorator.MODIFIED_DECORATION_OPTIONS + }; + }); + } + + dispose(): void { + this.toDispose = dispose(this.toDispose); + + if (this.model && !this.model.isDisposed()) { + this.model.deltaDecorations(this.decorations, []); + } + + this.model = null; + this.baselineModel = null; + this.decorations = null; + + if (this.diffDelayer) { + this.diffDelayer.cancel(); + this.diffDelayer = null; + } + } +} + +export class DirtyDiffDecorator implements ext.IWorkbenchContribution { + + private models: common.IModel[] = []; + private decorators: { [modelId: string]: DirtyDiffModelDecorator } = Object.create(null); + private toDispose: IDisposable[] = []; + + constructor( + @IMessageService private messageService: IMessageService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEditorGroupService editorGroupService: IEditorGroupService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + this.toDispose.push(editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); + } + + getId(): string { + return 'git.DirtyDiffModelDecorator'; + } + + private onEditorsChanged(): void { + // HACK: This is the best current way of figuring out whether to draw these decorations + // or not. Needs context from the editor, to know whether it is a diff editor, in place editor + // etc. + + const models = this.editorService.getVisibleEditors() + + // map to the editor controls + .map(e => e.getControl()) + + // only interested in code editor widgets + .filter(c => c instanceof widget.CodeEditor) + + // map to models + .map(e => (e).getModel()) + + // remove nulls and duplicates + .filter((m, i, a) => !!m && !!m.uri && a.indexOf(m, i + 1) === -1) + + // get the associated resource + .map(m => ({ model: m, uri: m.uri })); + + const newModels = models.filter(p => this.models.every(m => p.model !== m)); + const oldModels = this.models.filter(m => models.every(p => p.model !== m)); + + newModels.forEach(({ model, uri }) => this.onModelVisible(model, uri)); + oldModels.forEach(m => this.onModelInvisible(m)); + + this.models = models.map(p => p.model); + } + + private onModelVisible(model: common.IModel, uri: URI): void { + this.decorators[model.id] = this.instantiationService.createInstance(DirtyDiffModelDecorator, model, uri); + } + + private onModelInvisible(model: common.IModel): void { + this.decorators[model.id].dispose(); + delete this.decorators[model.id]; + } + + dispose(): void { + this.toDispose = dispose(this.toDispose); + this.models.forEach(m => this.decorators[m.id].dispose()); + this.models = null; + this.decorators = null; + } +} + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const editorGutterModifiedBackgroundColor = theme.getColor(editorGutterModifiedBackground); + if (editorGutterModifiedBackgroundColor) { + collector.addRule(`.monaco-editor .dirty-diff-modified-glyph { border-left: 3px solid ${editorGutterModifiedBackgroundColor}; }`); + } + + const editorGutterAddedBackgroundColor = theme.getColor(editorGutterAddedBackground); + if (editorGutterAddedBackgroundColor) { + collector.addRule(`.monaco-editor .dirty-diff-added-glyph { border-left: 3px solid ${editorGutterAddedBackgroundColor}; }`); + } + + const editorGutteDeletedBackgroundColor = theme.getColor(editorGutterDeletedBackground); + if (editorGutteDeletedBackgroundColor) { + collector.addRule(` + .monaco-editor .dirty-diff-deleted-glyph:after { + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + border-left: 4px solid ${editorGutteDeletedBackgroundColor}; + } + `); + } +}); diff --git a/src/vs/workbench/parts/scm/electron-browser/media/check-inverse.svg b/src/vs/workbench/parts/scm/electron-browser/media/check-inverse.svg new file mode 100644 index 0000000000..c225b2f597 --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/media/check-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/media/check.svg b/src/vs/workbench/parts/scm/electron-browser/media/check.svg new file mode 100644 index 0000000000..d45df06edf --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/media/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css b/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css new file mode 100644 index 0000000000..7debe213e3 --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .dirty-diff-modified-glyph, +.monaco-editor .dirty-diff-added-glyph, +.monaco-editor .dirty-diff-deleted-glyph:after { + margin-left: 5px; +} + +.monaco-editor .dirty-diff-deleted-glyph:after { + content: ''; + position: absolute; + bottom: -4px; + box-sizing: border-box; + width: 4px; + height: 0; + z-index: 9; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/media/icon-dark.svg b/src/vs/workbench/parts/scm/electron-browser/media/icon-dark.svg new file mode 100644 index 0000000000..2b8f14d282 --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/media/icon-dark.svg @@ -0,0 +1,12 @@ + + + + repo-forked + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/media/icon-light.svg b/src/vs/workbench/parts/scm/electron-browser/media/icon-light.svg new file mode 100644 index 0000000000..97e9f8a893 --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/media/icon-light.svg @@ -0,0 +1,12 @@ + + + + repo-forked + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css new file mode 100644 index 0000000000..ea701ad8e0 --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench > .activitybar > .content .monaco-action-bar .action-label.scm { + -webkit-mask: url('icon-dark.svg') no-repeat 50% 50%; + -webkit-mask-size: 19px; +} + +.monaco-workbench .viewlet.scm-viewlet .collapsible.header .actions { + width: initial; + flex: 1; +} + +.scm-viewlet .empty-message { + padding: 10px 22px 0 22px; + opacity: 0.5; +} + +.scm-viewlet:not(.empty) .empty-message, +.scm-viewlet.empty .monaco-split-view{ + display: none; +} + +.scm-viewlet .scm-status { + height: 100%; +} + +.scm-viewlet .monaco-list-row { + padding: 0 12px 0 20px; + line-height: 22px; +} + +.scm-viewlet .monaco-list-row > .resource-group { + display: flex; + height: 100%; +} + +.scm-viewlet .monaco-list-row > .resource-group > .name { + flex: 1; + font-size: 11px; + font-weight: bold; + text-transform: uppercase; + overflow: hidden; + text-overflow: ellipsis; +} + +.scm-viewlet .monaco-list-row > .resource { + display: flex; + height: 100%; +} + +.scm-viewlet .monaco-list-row > .resource.faded { + opacity: 0.7; +} + +.scm-viewlet .monaco-list-row > .resource > .name { + flex: 1; + overflow: hidden; +} + +.scm-viewlet .monaco-list-row > .resource > .name > .monaco-icon-label { + width: 100%; + height: 100%; + overflow: hidden; + text-overflow: ellipsis; +} + +.scm-viewlet .monaco-list-row > .resource > .name.strike-through > .monaco-icon-label > .label-name { + text-decoration: line-through; +} + +.scm-viewlet .monaco-list-row > .resource > .decoration-icon { + width: 16px; + height: 100%; + background-repeat: no-repeat; + background-position: 50% 50%; +} + + +.scm-viewlet .monaco-list .monaco-list-row > .resource-group > .actions, +.scm-viewlet .monaco-list .monaco-list-row > .resource > .actions { + display: none; +} + +.scm-viewlet .monaco-list .monaco-list-row:hover > .resource-group > .actions, +.scm-viewlet .monaco-list .monaco-list-row:hover > .resource > .actions, +.scm-viewlet .monaco-list .monaco-list-row.selected > .resource-group > .actions, +.scm-viewlet .monaco-list .monaco-list-row.focused > .resource-group > .actions, +.scm-viewlet .monaco-list .monaco-list-row.selected > .resource > .actions, +.scm-viewlet .monaco-list .monaco-list-row.focused > .resource > .actions, +.scm-viewlet .monaco-list:not(.selection-multiple) .monaco-list-row > .resource:hover > .actions { + display: block; +} + +.scm-viewlet .monaco-list-row > .resource > .actions .action-label, +.scm-viewlet .monaco-list-row > .resource-group > .actions .action-label { + width: 16px; + height: 100%; + background-position: 50% 50%; + background-repeat: no-repeat; +} + +.scm-viewlet .scm-editor { + padding: 5px 9px 5px 16px; +} + +.scm-viewlet .scm-editor { + box-sizing: border-box; + padding: 5px 9px 5px 16px; +} + +.scm-viewlet .scm-editor > .monaco-inputbox { + width: 100%; +} + +.scm-viewlet .scm-editor > .monaco-inputbox > .wrapper > .mirror { + max-height: 134px; +} + +.scm-viewlet .scm-editor > .monaco-inputbox > .wrapper > textarea.input { + min-height: 26px; +} + +.scm-viewlet .scm-editor.scroll > .monaco-inputbox > .wrapper > textarea.input { + overflow-y: scroll; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts b/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts new file mode 100644 index 0000000000..4769376f0a --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { DirtyDiffDecorator } from './dirtydiffDecorator'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ToggleViewletAction } from 'vs/workbench/browser/viewlet'; +import { VIEWLET_ID } from 'vs/workbench/parts/scm/common/scm'; +import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { StatusUpdater, StatusBarController } from './scmActivity'; + +class OpenSCMViewletAction extends ToggleViewletAction { + + static ID = VIEWLET_ID; + static LABEL = localize('toggleGitViewlet', "Show Git"); + + constructor(id: string, label: string, @IViewletService viewletService: IViewletService, @IWorkbenchEditorService editorService: IWorkbenchEditorService) { + super(id, label, VIEWLET_ID, viewletService, editorService); + } +} + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(DirtyDiffDecorator); + +const viewletDescriptor = new ViewletDescriptor( + 'vs/workbench/parts/scm/electron-browser/scmViewlet', + 'SCMViewlet', + VIEWLET_ID, + localize('source control', "Source Control"), + 'scm', + 36 +); + +Registry.as(ViewletExtensions.Viewlets) + .registerViewlet(viewletDescriptor); + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(StatusUpdater); + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(StatusBarController); + +// Register Action to Open Viewlet +Registry.as(WorkbenchActionExtensions.WorkbenchActions).registerWorkbenchAction( + new SyncActionDescriptor(OpenSCMViewletAction, VIEWLET_ID, localize('toggleSCMViewlet', "Show SCM"), { + primary: null, + win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G }, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G }, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_G } + }), + 'View: Show SCM', + localize('view', "View") +); diff --git a/src/vs/workbench/parts/scm/electron-browser/scmActivity.ts b/src/vs/workbench/parts/scm/electron-browser/scmActivity.ts new file mode 100644 index 0000000000..f783870db9 --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/scmActivity.ts @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import { IDisposable, dispose, empty as EmptyDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { filterEvent } from 'vs/base/common/event'; +import { VIEWLET_ID } from 'vs/workbench/parts/scm/common/scm'; +import { ISCMService, ISCMRepository } from 'vs/workbench/services/scm/common/scm'; +import { IActivityBarService, NumberBadge } from 'vs/workbench/services/activity/common/activityBarService'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar'; + +export class StatusUpdater implements IWorkbenchContribution { + + private static ID = 'vs.scm.statusUpdater'; + + private badgeDisposable: IDisposable = EmptyDisposable; + private disposables: IDisposable[] = []; + + constructor( + @ISCMService private scmService: ISCMService, + @IActivityBarService private activityBarService: IActivityBarService + ) { + this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); + this.render(); + } + + private onDidAddRepository(repository: ISCMRepository): void { + const changeDisposable = repository.provider.onDidChange(() => this.render()); + + const onDidRemove = filterEvent(this.scmService.onDidRemoveRepository, e => e === repository); + const removeDisposable = onDidRemove(() => { + disposable.dispose(); + this.disposables = this.disposables.filter(d => d !== removeDisposable); + this.render(); + }); + + const disposable = combinedDisposable([changeDisposable, removeDisposable]); + this.disposables.push(disposable); + } + + getId(): string { + return StatusUpdater.ID; + } + + private render(): void { + this.badgeDisposable.dispose(); + + const count = this.scmService.repositories.reduce((r, repository) => { + if (typeof repository.provider.count === 'number') { + return r + repository.provider.count; + } else { + return r + repository.provider.resources.reduce((r, g) => r + g.resources.length, 0); + } + }, 0); + + if (count > 0) { + const badge = new NumberBadge(count, num => localize('scmPendingChangesBadge', '{0} pending changes', num)); + this.badgeDisposable = this.activityBarService.showActivity(VIEWLET_ID, badge, 'scm-viewlet-label'); + } else { + this.badgeDisposable = EmptyDisposable; + } + } + + dispose(): void { + this.badgeDisposable.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class StatusBarController implements IWorkbenchContribution { + + private static ID = 'vs.scm.statusBarController'; + + private statusBarDisposable: IDisposable = EmptyDisposable; + private focusDisposable: IDisposable = EmptyDisposable; + private focusedRepository: ISCMRepository | undefined = undefined; + private focusedProviderContextKey: IContextKey; + private disposables: IDisposable[] = []; + + constructor( + @ISCMService private scmService: ISCMService, + @IStatusbarService private statusbarService: IStatusbarService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + this.focusedProviderContextKey = contextKeyService.createKey('scmProvider', void 0); + this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); + + if (this.scmService.repositories.length > 0) { + this.onDidFocusRepository(this.scmService.repositories[0]); + } + } + + getId(): string { + return StatusBarController.ID; + } + + private onDidAddRepository(repository: ISCMRepository): void { + const changeDisposable = repository.onDidFocus(() => this.onDidFocusRepository(repository)); + const onDidRemove = filterEvent(this.scmService.onDidRemoveRepository, e => e === repository); + const removeDisposable = onDidRemove(() => { + disposable.dispose(); + this.disposables = this.disposables.filter(d => d !== removeDisposable); + + if (this.scmService.repositories.length === 0) { + this.focusedProviderContextKey.set(undefined); + } else if (this.focusedRepository === repository) { + this.scmService.repositories[0].focus(); + } + }); + + const disposable = combinedDisposable([changeDisposable, removeDisposable]); + this.disposables.push(disposable); + + if (this.scmService.repositories.length === 1) { + this.onDidFocusRepository(repository); + } + } + + private onDidFocusRepository(repository: ISCMRepository): void { + if (this.focusedRepository !== repository) { + this.focusedRepository = repository; + this.focusedProviderContextKey.set(repository.provider.id); + } + + this.focusDisposable.dispose(); + this.focusDisposable = repository.provider.onDidChange(() => this.render(repository)); + this.render(repository); + } + + private render(repository: ISCMRepository): void { + this.statusBarDisposable.dispose(); + + const commands = repository.provider.statusBarCommands || []; + const disposables = commands.map(c => this.statusbarService.addEntry({ + text: c.title, + tooltip: `${repository.provider.label} - ${c.tooltip}`, + command: c.id, + arguments: c.arguments + }, MainThreadStatusBarAlignment.LEFT, 10000)); + + this.statusBarDisposable = combinedDisposable(disposables); + } + + dispose(): void { + this.focusDisposable.dispose(); + this.statusBarDisposable.dispose(); + this.disposables = dispose(this.disposables); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts b/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts new file mode 100644 index 0000000000..69531d5da3 --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/scmViewlet'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; +import { IAction } from 'vs/base/common/actions'; +import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { ISCMProvider, ISCMResource, ISCMResourceGroup } from 'vs/workbench/services/scm/common/scm'; +import { getSCMResourceContextKey } from './scmUtil'; + +export class SCMMenus implements IDisposable { + + private contextKeyService: IContextKeyService; + private titleMenu: IMenu; + private titleActions: IAction[] = []; + private titleSecondaryActions: IAction[] = []; + + private _onDidChangeTitle = new Emitter(); + get onDidChangeTitle(): Event { return this._onDidChangeTitle.event; } + + private disposables: IDisposable[] = []; + + constructor( + private provider: ISCMProvider | undefined, + @IContextKeyService contextKeyService: IContextKeyService, + @IMenuService private menuService: IMenuService + ) { + this.contextKeyService = contextKeyService.createScoped(); + const scmProviderKey = this.contextKeyService.createKey('scmProvider', void 0); + + if (provider) { + scmProviderKey.set(provider.contextValue); + } else { + scmProviderKey.set(''); + } + + this.titleMenu = this.menuService.createMenu(MenuId.SCMTitle, this.contextKeyService); + this.disposables.push(this.titleMenu); + + this.titleMenu.onDidChange(this.updateTitleActions, this, this.disposables); + this.updateTitleActions(); + } + + private updateTitleActions(): void { + this.titleActions = []; + this.titleSecondaryActions = []; + // TODO@joao: second arg used to be null + fillInActions(this.titleMenu, { shouldForwardArgs: true }, { primary: this.titleActions, secondary: this.titleSecondaryActions }); + this._onDidChangeTitle.fire(); + } + + getTitleActions(): IAction[] { + return this.titleActions; + } + + getTitleSecondaryActions(): IAction[] { + return this.titleSecondaryActions; + } + + getResourceGroupActions(group: ISCMResourceGroup): IAction[] { + return this.getActions(MenuId.SCMResourceGroupContext, group).primary; + } + + getResourceGroupContextActions(group: ISCMResourceGroup): IAction[] { + return this.getActions(MenuId.SCMResourceGroupContext, group).secondary; + } + + getResourceActions(resource: ISCMResource): IAction[] { + return this.getActions(MenuId.SCMResourceContext, resource).primary; + } + + getResourceContextActions(resource: ISCMResource): IAction[] { + return this.getActions(MenuId.SCMResourceContext, resource).secondary; + } + + private getActions(menuId: MenuId, resource: ISCMResourceGroup | ISCMResource): { primary: IAction[]; secondary: IAction[]; } { + const contextKeyService = this.contextKeyService.createScoped(); + contextKeyService.createKey('scmResourceGroup', getSCMResourceContextKey(resource)); + + const menu = this.menuService.createMenu(menuId, contextKeyService); + const primary = []; + const secondary = []; + const result = { primary, secondary }; + fillInActions(menu, { shouldForwardArgs: true }, result, g => g === 'inline'); + + menu.dispose(); + contextKeyService.dispose(); + + return result; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/scmUtil.ts b/src/vs/workbench/parts/scm/electron-browser/scmUtil.ts new file mode 100644 index 0000000000..709f6e1955 --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/scmUtil.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ISCMResourceGroup, ISCMResource } from 'vs/workbench/services/scm/common/scm'; + +export function isSCMResource(element: ISCMResourceGroup | ISCMResource): element is ISCMResource { + return !!(element as ISCMResource).sourceUri; +} + +export function getSCMResourceContextKey(resource: ISCMResourceGroup | ISCMResource): string { + return isSCMResource(resource) ? resource.resourceGroup.id : resource.id; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts new file mode 100644 index 0000000000..b177bf1d5b --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -0,0 +1,630 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/scmViewlet'; +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { chain } from 'vs/base/common/event'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Builder } from 'vs/base/browser/builder'; +import { PersistentViewsViewlet, CollapsibleView, IViewletViewOptions, IView, IViewOptions } from 'vs/workbench/parts/views/browser/views'; +import { append, $, toggleClass, trackFocus } from 'vs/base/browser/dom'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { IDelegate, IRenderer, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; +import { VIEWLET_ID } from 'vs/workbench/parts/scm/common/scm'; +import { FileLabel } from 'vs/workbench/browser/labels'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { ISCMService, ISCMRepository, ISCMResourceGroup, ISCMResource } from 'vs/workbench/services/scm/common/scm'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IAction, Action, IActionItem, ActionRunner } from 'vs/base/common/actions'; +import { MenuItemActionItem } from 'vs/platform/actions/browser/menuItemActionItem'; +import { SCMMenus } from './scmMenus'; +import { ActionBar, IActionItemProvider, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; +import { comparePaths } from 'vs/base/common/comparers'; +import { isSCMResource } from './scmUtil'; +import { attachListStyler, attachBadgeStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import Severity from 'vs/base/common/severity'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ViewLocation, ViewsRegistry, IViewDescriptor } from 'vs/workbench/parts/views/browser/viewsRegistry'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview'; +import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions'; +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import * as platform from 'vs/base/common/platform'; +import { domEvent } from 'vs/base/browser/event'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; + +// TODO@Joao +// Need to subclass MenuItemActionItem in order to respect +// the action context coming from any action bar, without breaking +// existing users +class SCMMenuItemActionItem extends MenuItemActionItem { + + onClick(event: MouseEvent): void { + event.preventDefault(); + event.stopPropagation(); + + this.actionRunner.run(this._commandAction, this._context) + .done(undefined, err => this._messageService.show(Severity.Error, err)); + } +} + +function identityProvider(r: ISCMResourceGroup | ISCMResource): string { + if (isSCMResource(r)) { + const group = r.resourceGroup; + const provider = group.provider; + return `${provider.contextValue}/${group.id}/${r.sourceUri.toString()}`; + } else { + const provider = r.provider; + return `${provider.contextValue}/${r.id}`; + } +} + +interface SearchInputEvent extends Event { + target: HTMLInputElement; + immediate?: boolean; +} + +interface ResourceGroupTemplate { + name: HTMLElement; + count: CountBadge; + actionBar: ActionBar; + dispose: () => void; +} + +class ResourceGroupRenderer implements IRenderer { + + static TEMPLATE_ID = 'resource group'; + get templateId(): string { return ResourceGroupRenderer.TEMPLATE_ID; } + + constructor( + private scmMenus: SCMMenus, + private actionItemProvider: IActionItemProvider, + private themeService: IThemeService + ) { } + + renderTemplate(container: HTMLElement): ResourceGroupTemplate { + const element = append(container, $('.resource-group')); + const name = append(element, $('.name')); + const actionsContainer = append(element, $('.actions')); + const actionBar = new ActionBar(actionsContainer, { actionItemProvider: this.actionItemProvider }); + const countContainer = append(element, $('.count')); + const count = new CountBadge(countContainer); + const styler = attachBadgeStyler(count, this.themeService); + + return { + name, count, actionBar, dispose: () => { + actionBar.dispose(); + styler.dispose(); + } + }; + } + + renderElement(group: ISCMResourceGroup, index: number, template: ResourceGroupTemplate): void { + template.name.textContent = group.label; + template.count.setCount(group.resources.length); + template.actionBar.clear(); + template.actionBar.context = group; + template.actionBar.push(this.scmMenus.getResourceGroupActions(group), { icon: true, label: false }); + } + + disposeTemplate(template: ResourceGroupTemplate): void { + template.dispose(); + } +} + +interface ResourceTemplate { + element: HTMLElement; + name: HTMLElement; + fileLabel: FileLabel; + decorationIcon: HTMLElement; + actionBar: ActionBar; + dispose: () => void; +} + +class MultipleSelectionActionRunner extends ActionRunner { + + constructor(private getSelectedResources: () => ISCMResource[]) { + super(); + } + + runAction(action: IAction, context: ISCMResource): TPromise { + if (action instanceof MenuItemAction) { + const selection = this.getSelectedResources(); + const filteredSelection = selection.filter(s => s !== context); + + if (selection.length === filteredSelection.length || selection.length === 1) { + return action.run(context); + } + + return action.run(context, ...filteredSelection); + } + + return super.runAction(action, context); + } +} + +class ResourceRenderer implements IRenderer { + + static TEMPLATE_ID = 'resource'; + get templateId(): string { return ResourceRenderer.TEMPLATE_ID; } + + constructor( + private scmMenus: SCMMenus, + private actionItemProvider: IActionItemProvider, + private getSelectedResources: () => ISCMResource[], + @IThemeService private themeService: IThemeService, + @IInstantiationService private instantiationService: IInstantiationService + ) { } + + renderTemplate(container: HTMLElement): ResourceTemplate { + const element = append(container, $('.resource')); + const name = append(element, $('.name')); + const fileLabel = this.instantiationService.createInstance(FileLabel, name, void 0); + const actionsContainer = append(element, $('.actions')); + const actionBar = new ActionBar(actionsContainer, { + actionItemProvider: this.actionItemProvider, + actionRunner: new MultipleSelectionActionRunner(this.getSelectedResources) + }); + + const decorationIcon = append(element, $('.decoration-icon')); + + return { + element, name, fileLabel, decorationIcon, actionBar, dispose: () => { + actionBar.dispose(); + fileLabel.dispose(); + } + }; + } + + renderElement(resource: ISCMResource, index: number, template: ResourceTemplate): void { + template.fileLabel.setFile(resource.sourceUri); + template.actionBar.clear(); + template.actionBar.context = resource; + template.actionBar.push(this.scmMenus.getResourceActions(resource), { icon: true, label: false }); + toggleClass(template.name, 'strike-through', resource.decorations.strikeThrough); + toggleClass(template.element, 'faded', resource.decorations.faded); + + const theme = this.themeService.getTheme(); + const icon = theme.type === LIGHT ? resource.decorations.icon : resource.decorations.iconDark; + + if (icon) { + template.decorationIcon.style.backgroundImage = `url('${icon}')`; + template.decorationIcon.title = resource.decorations.tooltip; + } else { + template.decorationIcon.style.backgroundImage = ''; + } + } + + disposeTemplate(template: ResourceTemplate): void { + template.dispose(); + } +} + +class Delegate implements IDelegate { + + getHeight() { return 22; } + + getTemplateId(element: ISCMResourceGroup | ISCMResource) { + return isSCMResource(element) ? ResourceRenderer.TEMPLATE_ID : ResourceGroupRenderer.TEMPLATE_ID; + } +} + +function resourceSorter(a: ISCMResource, b: ISCMResource): number { + return comparePaths(a.sourceUri.fsPath, b.sourceUri.fsPath); +} + +class SourceControlViewDescriptor implements IViewDescriptor { + + // This ID magic needs to happen in order to preserve + // good splitview state when reloading the workbench + static idCount = 0; + static freeIds: string[] = []; + + readonly id: string; + + get repository(): ISCMRepository { return this._repository; } + get name(): string { return this._repository.provider.label; } + get ctor(): any { return null; } + get location(): ViewLocation { return ViewLocation.SCM; } + + constructor(private _repository: ISCMRepository) { + if (SourceControlViewDescriptor.freeIds.length > 0) { + this.id = SourceControlViewDescriptor.freeIds.shift(); + } else { + this.id = `scm${SourceControlViewDescriptor.idCount++}`; + } + } + + dispose(): void { + SourceControlViewDescriptor.freeIds.push(this.id); + } +} + +class SourceControlView extends CollapsibleView { + + private cachedHeight: number | undefined; + private inputBoxContainer: HTMLElement; + private inputBox: InputBox; + private listContainer: HTMLElement; + private list: List; + private menus: SCMMenus; + private disposables: IDisposable[] = []; + + constructor( + initialSize: number, + private repository: ISCMRepository, + options: IViewletViewOptions, + @IKeybindingService protected keybindingService: IKeybindingService, + @IThemeService protected themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IContextViewService protected contextViewService: IContextViewService, + @IListService protected listService: IListService, + @ICommandService protected commandService: ICommandService, + @IMessageService protected messageService: IMessageService, + @IWorkbenchEditorService protected editorService: IWorkbenchEditorService, + @IEditorGroupService protected editorGroupService: IEditorGroupService, + @IInstantiationService protected instantiationService: IInstantiationService + ) { + super(initialSize, { ...(options as IViewOptions), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService); + + this.menus = instantiationService.createInstance(SCMMenus, repository.provider); + this.menus.onDidChangeTitle(this.updateActions, this, this.disposables); + } + + renderHeader(container: HTMLElement): void { + const title = append(container, $('div.title')); + title.textContent = this.name; + + super.renderHeader(container); + } + + renderBody(container: HTMLElement): void { + const focusTracker = trackFocus(container); + this.disposables.push(focusTracker.addFocusListener(() => this.repository.focus())); + this.disposables.push(focusTracker); + + // Input + this.inputBoxContainer = append(container, $('.scm-editor')); + + this.inputBox = new InputBox(this.inputBoxContainer, this.contextViewService, { + placeholder: localize('commitMessage', "Message (press {0} to commit)", platform.isMacintosh ? 'Cmd+Enter' : 'Ctrl+Enter'), + flexibleHeight: true + }); + this.disposables.push(attachInputBoxStyler(this.inputBox, this.themeService)); + this.disposables.push(this.inputBox); + + this.inputBox.value = this.repository.input.value; + this.inputBox.onDidChange(value => this.repository.input.value = value, null, this.disposables); + this.repository.input.onDidChange(value => this.inputBox.value = value, null, this.disposables); + this.disposables.push(this.inputBox.onDidHeightChange(() => this.layoutBody())); + + chain(domEvent(this.inputBox.inputElement, 'keydown')) + .map(e => new StandardKeyboardEvent(e)) + .filter(e => e.equals(KeyMod.CtrlCmd | KeyCode.Enter) || e.equals(KeyMod.CtrlCmd | KeyCode.KEY_S)) + .on(this.onDidAcceptInput, this, this.disposables); + + if (this.repository.provider.onDidChangeCommitTemplate) { + this.repository.provider.onDidChangeCommitTemplate(this.updateInputBox, this, this.disposables); + } + + this.updateInputBox(); + + // List + + this.listContainer = append(container, $('.scm-status.show-file-icons')); + const delegate = new Delegate(); + + const actionItemProvider = action => this.getActionItem(action); + + const renderers = [ + new ResourceGroupRenderer(this.menus, actionItemProvider, this.themeService), + this.instantiationService.createInstance(ResourceRenderer, this.menus, actionItemProvider, () => this.getSelectedResources()), + ]; + + this.list = new List(this.listContainer, delegate, renderers, { + identityProvider, + keyboardSupport: false + }); + + this.disposables.push(attachListStyler(this.list, this.themeService)); + this.disposables.push(this.listService.register(this.list)); + + chain(this.list.onOpen) + .map(e => e.elements[0]) + .filter(e => !!e && isSCMResource(e)) + .on(this.open, this, this.disposables); + + chain(this.list.onPin) + .map(e => e.elements[0]) + .filter(e => !!e && isSCMResource(e)) + .on(this.pin, this, this.disposables); + + this.list.onContextMenu(this.onListContextMenu, this, this.disposables); + this.disposables.push(this.list); + + this.repository.provider.onDidChange(this.updateList, this, this.disposables); + this.updateList(); + } + + layoutBody(height: number = this.cachedHeight): void { + if (!height === undefined) { + return; + } + + this.list.layout(height); + this.cachedHeight = height; + this.inputBox.layout(); + + const editorHeight = this.inputBox.height; + const listHeight = height - (editorHeight + 12 /* margin */); + this.listContainer.style.height = `${listHeight}px`; + this.list.layout(listHeight); + + toggleClass(this.inputBoxContainer, 'scroll', editorHeight >= 134); + } + + focus(): void { + if (this.isExpanded()) { + this.inputBox.focus(); + } + } + + getActions(): IAction[] { + return this.menus.getTitleActions(); + } + + getSecondaryActions(): IAction[] { + return this.menus.getTitleSecondaryActions(); + } + + getActionItem(action: IAction): IActionItem { + if (!(action instanceof MenuItemAction)) { + return undefined; + } + + return new SCMMenuItemActionItem(action, this.keybindingService, this.messageService); + } + + getActionsContext(): any { + return this.repository.provider; + } + + private updateList(): void { + const elements = this.repository.provider.resources + .reduce<(ISCMResourceGroup | ISCMResource)[]>((r, g) => [...r, g, ...g.resources.sort(resourceSorter)], []); + + this.list.splice(0, this.list.length, elements); + } + + private open(e: ISCMResource): void { + if (!e.command) { + return; + } + + this.commandService.executeCommand(e.command.id, ...e.command.arguments) + .done(undefined, onUnexpectedError); + } + + private pin(): void { + const activeEditor = this.editorService.getActiveEditor(); + const activeEditorInput = this.editorService.getActiveEditorInput(); + this.editorGroupService.pinEditor(activeEditor.position, activeEditorInput); + } + + private onListContextMenu(e: IListContextMenuEvent): void { + const element = e.element; + let actions: IAction[]; + + if (isSCMResource(element)) { + actions = this.menus.getResourceContextActions(element); + } else { + actions = this.menus.getResourceGroupContextActions(element); + } + + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => TPromise.as(actions), + getActionsContext: () => element, + actionRunner: new MultipleSelectionActionRunner(() => this.getSelectedResources()) + }); + } + + private getSelectedResources(): ISCMResource[] { + return this.list.getSelectedElements() + .filter(r => isSCMResource(r)) as ISCMResource[]; + } + + private updateInputBox(): void { + if (typeof this.repository.provider.commitTemplate === 'undefined') { + return; + } + + this.inputBox.value = this.repository.provider.commitTemplate; + } + + private onDidAcceptInput(): void { + if (!this.repository.provider.acceptInputCommand) { + return; + } + + const id = this.repository.provider.acceptInputCommand.id; + const args = this.repository.provider.acceptInputCommand.arguments; + + this.commandService.executeCommand(id, ...args) + .done(undefined, onUnexpectedError); + } + + dispose(): void { + this.disposables = dispose(this.disposables); + super.dispose(); + } +} + +class InstallAdditionalSCMProvidersAction extends Action { + + constructor( @IViewletService private viewletService: IViewletService) { + super('scm.installAdditionalSCMProviders', localize('installAdditionalSCMProviders', "Install Additional SCM Providers..."), '', true); + } + + run(): TPromise { + return this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true).then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('category:"SCM Providers" @sort:installs'); + viewlet.focus(); + }); + } +} + +export class SCMViewlet extends PersistentViewsViewlet { + + private menus: SCMMenus; + private repositoryToViewDescriptor = new Map(); + private disposables: IDisposable[] = []; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @ISCMService protected scmService: ISCMService, + @IInstantiationService instantiationService: IInstantiationService, + @IContextViewService protected contextViewService: IContextViewService, + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService protected keybindingService: IKeybindingService, + @IMessageService protected messageService: IMessageService, + @IListService protected listService: IListService, + @IContextMenuService contextMenuService: IContextMenuService, + @IThemeService protected themeService: IThemeService, + @ICommandService protected commandService: ICommandService, + @IEditorGroupService protected editorGroupService: IEditorGroupService, + @IWorkbenchEditorService protected editorService: IWorkbenchEditorService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IStorageService storageService: IStorageService, + @IExtensionService extensionService: IExtensionService + ) { + super(VIEWLET_ID, ViewLocation.SCM, 'scm', true, + telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); + + this.menus = instantiationService.createInstance(SCMMenus, undefined); + this.menus.onDidChangeTitle(this.updateTitleArea, this, this.disposables); + } + + private onDidAddRepository(repository: ISCMRepository): void { + const viewDescriptor = new SourceControlViewDescriptor(repository); + this.repositoryToViewDescriptor.set(repository.provider.id, viewDescriptor); + + ViewsRegistry.registerViews([viewDescriptor]); + toggleClass(this.getContainer().getHTMLElement(), 'empty', this.views.length === 0); + this.updateTitleArea(); + } + + private onDidRemoveRepository(repository: ISCMRepository): void { + const viewDescriptor = this.repositoryToViewDescriptor.get(repository.provider.id); + this.repositoryToViewDescriptor.delete(repository.provider.id); + viewDescriptor.dispose(); + + ViewsRegistry.deregisterViews([viewDescriptor.id], ViewLocation.SCM); + toggleClass(this.getContainer().getHTMLElement(), 'empty', this.views.length === 0); + this.updateTitleArea(); + } + + async create(parent: Builder): TPromise { + await super.create(parent); + + parent.addClass('scm-viewlet', 'empty'); + // {{SQL CARBON EDIT}} + append(parent.getHTMLElement(), $('div.empty-message', null, localize('no open repo', "There are no active source control providers"))); + + this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); + this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); + this.scmService.repositories.forEach(p => this.onDidAddRepository(p)); + } + + protected createView(viewDescriptor: IViewDescriptor, initialSize: number, options: IViewletViewOptions): IView { + if (viewDescriptor instanceof SourceControlViewDescriptor) { + return this.instantiationService.createInstance(SourceControlView, initialSize, viewDescriptor.repository, options); + } + + return this.instantiationService.createInstance(viewDescriptor.ctor, initialSize, options); + } + + protected getDefaultViewSize(): number | undefined { + return this.dimension && this.dimension.height / this.views.length; + } + + getOptimalWidth(): number { + return 400; + } + + getTitle(): string { + const title = localize('source control', "Source Control"); + const views = ViewsRegistry.getViews(ViewLocation.SCM); + + if (views.length === 1) { + const view = views[0]; + return localize('viewletTitle', "{0}: {1}", title, view.name); + } else { + return title; + } + } + + getActions(): IAction[] { + if (this.showHeaderInTitleArea() && this.views.length === 1) { + return this.views[0].getActions(); + } + + return this.menus.getTitleActions(); + } + + getSecondaryActions(): IAction[] { + let result: IAction[]; + + if (this.showHeaderInTitleArea() && this.views.length === 1) { + result = [ + ...this.views[0].getSecondaryActions(), + new Separator() + ]; + } else { + result = this.menus.getTitleSecondaryActions(); + + if (result.length > 0) { + result.push(new Separator()); + } + } + + // {{SQL CARBON EDIT}} + // result.push(this.instantiationService.createInstance(InstallAdditionalSCMProvidersAction)); + + return result; + } + + getActionItem(action: IAction): IActionItem { + if (!(action instanceof MenuItemAction)) { + return undefined; + } + + return new SCMMenuItemActionItem(action, this.keybindingService, this.messageService); + } + + dispose(): void { + this.disposables = dispose(this.disposables); + super.dispose(); + } +} diff --git a/src/vs/workbench/parts/search/browser/media/CollapseAll.svg b/src/vs/workbench/parts/search/browser/media/CollapseAll.svg new file mode 100644 index 0000000000..7d11a30f6e --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/CollapseAll.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/CollapseAll_inverse.svg b/src/vs/workbench/parts/search/browser/media/CollapseAll_inverse.svg new file mode 100644 index 0000000000..46a65fff71 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/CollapseAll_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/Refresh.svg b/src/vs/workbench/parts/search/browser/media/Refresh.svg new file mode 100644 index 0000000000..e034574819 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/Refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/Refresh_inverse.svg b/src/vs/workbench/parts/search/browser/media/Refresh_inverse.svg new file mode 100644 index 0000000000..d79fdaa4e8 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/Refresh_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/action-query-clear-dark.svg b/src/vs/workbench/parts/search/browser/media/action-query-clear-dark.svg new file mode 100644 index 0000000000..751e89b3b0 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/action-query-clear-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/action-query-clear.svg b/src/vs/workbench/parts/search/browser/media/action-query-clear.svg new file mode 100644 index 0000000000..58af853679 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/action-query-clear.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/action-remove-dark.svg b/src/vs/workbench/parts/search/browser/media/action-remove-dark.svg new file mode 100644 index 0000000000..751e89b3b0 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/action-remove-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/action-remove-focus.svg b/src/vs/workbench/parts/search/browser/media/action-remove-focus.svg new file mode 100644 index 0000000000..865c5aaea5 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/action-remove-focus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/action-remove.svg b/src/vs/workbench/parts/search/browser/media/action-remove.svg new file mode 100644 index 0000000000..fde34404d4 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/action-remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/clear-search-results-dark.svg b/src/vs/workbench/parts/search/browser/media/clear-search-results-dark.svg new file mode 100644 index 0000000000..7df4455d7c --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/clear-search-results-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/clear-search-results.svg b/src/vs/workbench/parts/search/browser/media/clear-search-results.svg new file mode 100644 index 0000000000..6e4d3a1d48 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/clear-search-results.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/configure-inverse.svg b/src/vs/workbench/parts/search/browser/media/configure-inverse.svg new file mode 100644 index 0000000000..61baaea2b8 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/configure-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/configure.svg b/src/vs/workbench/parts/search/browser/media/configure.svg new file mode 100644 index 0000000000..3dec2ba50f --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/configure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/ellipsis-inverse.svg b/src/vs/workbench/parts/search/browser/media/ellipsis-inverse.svg new file mode 100644 index 0000000000..1140c11ecc --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/ellipsis-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/ellipsis.svg b/src/vs/workbench/parts/search/browser/media/ellipsis.svg new file mode 100644 index 0000000000..b61e2d0c75 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/ellipsis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/excludeSettings-dark.svg b/src/vs/workbench/parts/search/browser/media/excludeSettings-dark.svg new file mode 100644 index 0000000000..3eeedcb41c --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/excludeSettings-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/excludeSettings.svg b/src/vs/workbench/parts/search/browser/media/excludeSettings.svg new file mode 100644 index 0000000000..79decb032b --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/excludeSettings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/expando-collapsed-dark.svg b/src/vs/workbench/parts/search/browser/media/expando-collapsed-dark.svg new file mode 100644 index 0000000000..6f3abfce78 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/expando-collapsed-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/expando-collapsed.svg b/src/vs/workbench/parts/search/browser/media/expando-collapsed.svg new file mode 100644 index 0000000000..5dcb87c772 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/expando-collapsed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/expando-expanded-dark.svg b/src/vs/workbench/parts/search/browser/media/expando-expanded-dark.svg new file mode 100644 index 0000000000..22dfac04f1 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/expando-expanded-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/expando-expanded.svg b/src/vs/workbench/parts/search/browser/media/expando-expanded.svg new file mode 100644 index 0000000000..e55ccd923e --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/expando-expanded.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/gitignore-dark.svg b/src/vs/workbench/parts/search/browser/media/gitignore-dark.svg new file mode 100644 index 0000000000..4bff371b89 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/gitignore-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/gitignore.svg b/src/vs/workbench/parts/search/browser/media/gitignore.svg new file mode 100644 index 0000000000..d30c4a347e --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/gitignore.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/pattern-dark.svg b/src/vs/workbench/parts/search/browser/media/pattern-dark.svg new file mode 100644 index 0000000000..1e13142b41 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/pattern-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/pattern.svg b/src/vs/workbench/parts/search/browser/media/pattern.svg new file mode 100644 index 0000000000..d2253f6a97 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/pattern.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/replace-all-inverse.svg b/src/vs/workbench/parts/search/browser/media/replace-all-inverse.svg new file mode 100644 index 0000000000..45312b6089 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/replace-all-inverse.svg @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/vs/workbench/parts/search/browser/media/replace-all.svg b/src/vs/workbench/parts/search/browser/media/replace-all.svg new file mode 100644 index 0000000000..4254f7c6d1 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/replace-all.svg @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/vs/workbench/parts/search/browser/media/replace-inverse.svg b/src/vs/workbench/parts/search/browser/media/replace-inverse.svg new file mode 100644 index 0000000000..9a59e26378 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/replace-inverse.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/vs/workbench/parts/search/browser/media/replace.svg b/src/vs/workbench/parts/search/browser/media/replace.svg new file mode 100644 index 0000000000..8b1eb0de23 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/replace.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/vs/workbench/parts/search/browser/media/search-dark.svg b/src/vs/workbench/parts/search/browser/media/search-dark.svg new file mode 100644 index 0000000000..35bda969be --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/search-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/search.contribution.css b/src/vs/workbench/parts/search/browser/media/search.contribution.css new file mode 100644 index 0000000000..cd9bafb062 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/search.contribution.css @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Activity Bar */ +.monaco-workbench > .activitybar .monaco-action-bar .action-label.search { + -webkit-mask: url('search-dark.svg') no-repeat 50% 50%; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/searchviewlet.css b/src/vs/workbench/parts/search/browser/media/searchviewlet.css new file mode 100644 index 0000000000..47c34cc602 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/media/searchviewlet.css @@ -0,0 +1,413 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.search-viewlet .search-widgets-container { + margin: 0px 9px 0 2px; + padding-top: 6px; +} + +.search-viewlet .search-widget .toggle-replace-button { + position: absolute; + top: 0; + left: 0; + width: 16px; + height: 100%; + + -webkit-box-sizing: border-box; + -o-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; +} + +.search-viewlet .search-widget .search-container, +.search-viewlet .search-widget .replace-container { + margin-left: 17px; +} + +.search-viewlet .search-widget .input-box, +.search-viewlet .search-widget .input-box .monaco-inputbox { + height: 25px; +} + +.search-viewlet .search-widget .monaco-findInput { + display: inline-block; + vertical-align: middle; +} + +.search-viewlet .search-widget .replace-container { + margin-top: 6px; + position: relative; + display: inline-flex; +} + +.search-viewlet .search-widget .replace-container.disabled { + display: none; +} + +.search-viewlet .search-widget .replace-container .monaco-action-bar { + margin-left: 3px; +} + +.search-viewlet .search-widget .replace-container .monaco-action-bar .action-item .icon { + background-repeat: no-repeat; + width: 20px; + height: 25px; +} + +.search-viewlet .query-clear { + width: 20px; + height: 20px; + position: absolute; + top: 3px; + right: 3px; + cursor: pointer; +} + +.search-viewlet .query-details { + min-height: 1em; + position: relative; + margin: 0 0 0 17px; +} + +.search-viewlet .query-details .more { + position: absolute; + margin-right: 0.3em; + right: 0; + cursor: pointer; + width: 16px; + height: 13px; + z-index: 2; /* Force it above the search results message, which has a negative top margin */ +} + +.hc-black .monaco-workbench .search-viewlet .query-details .more, +.vs-dark .monaco-workbench .search-viewlet .query-details .more { + background: url('ellipsis-inverse.svg') top center no-repeat; +} + +.vs .monaco-workbench .search-viewlet .query-details .more { + background: url('ellipsis.svg') top center no-repeat; +} + +.search-viewlet .query-details .file-types { + display: none; +} + +.search-viewlet .query-details .file-types > .monaco-inputbox { + width: 100%; + height: 25px; +} + +.search-viewlet .query-details.more .file-types { + display: inherit; +} + +.search-viewlet .query-details.more .file-types:last-child { + padding-bottom: 10px; +} + +.search-viewlet .query-details .search-pattern-info { + width: 16px; + height: 16px; +} + +.search-viewlet .query-details .search-configure-exclusions { + width: 16px; + height: 16px; +} + +.monaco-action-bar .action-label.search-configure-exclusions { + margin-right: 2px; +} + +.vs-dark .monaco-workbench .search-configure-exclusions, +.hc-black .monaco-workbench .search-configure-exclusions { + background: url('configure-inverse.svg') center center no-repeat; + opacity: .7; +} + +.vs .monaco-workbench .search-configure-exclusions { + background: url('configure.svg') center center no-repeat; + opacity: 0.7; +} + +.search-viewlet .query-details.more h4 { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + padding: 4px 0 0; + margin: 0; + font-size: 11px; + font-weight: normal; +} + +.search-viewlet .messages { + margin-top: -5px; + cursor: default; +} + +.search-viewlet .message { + padding-left: 22px; + padding-right: 22px; + padding-top: 0px; +} + +.search-viewlet .message p:first-child { + margin-top: 0px; + margin-bottom: 0px; + padding-bottom: 4px; + -webkit-user-select: text; + user-select: text; +} + +.search-viewlet > .results > .monaco-tree .sub-content { + overflow: hidden; +} + +.search-viewlet .foldermatch, +.search-viewlet .filematch { + display: flex; + position: relative; + line-height: 22px; + padding: 0; +} + +.search-viewlet .foldermatch .monaco-icon-label, +.search-viewlet .filematch .monaco-icon-label { + flex: 1; +} + +.search-viewlet .foldermatch .directory, +.search-viewlet .filematch .directory { + opacity: 0.7; + font-size: 0.9em; + margin-left: 0.8em; +} + +.search-viewlet .linematch { + position: relative; + line-height: 22px; + display: flex; +} + +.search-viewlet .linematch > .match { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; +} + +.search-viewlet .linematch.changedOrRemoved { + font-style: italic; +} + +.search-viewlet .query-clear { + background: url("action-query-clear.svg") center center no-repeat; +} + +.search-viewlet .monaco-tree .monaco-tree-row .monaco-action-bar { + line-height: 1em; + display: none; + padding: 0 0.8em 0 0.4em; +} + +.search-viewlet .monaco-tree .monaco-tree-row .monaco-action-bar .action-item { + margin: 0; +} + +.search-viewlet .monaco-tree .monaco-tree-row.focused .monaco-action-bar { + width: 0; /* in order to support a11y with keyboard, we use width: 0 to hide the actions, which still allows to "Tab" into the actions */ + display: block; +} + +.search-viewlet .monaco-tree .monaco-tree-row:hover:not(.highlighted) .monaco-action-bar, +.search-viewlet .monaco-tree .monaco-tree-row.focused .monaco-action-bar { + width: inherit; + display: block; +} + +.search-viewlet .monaco-tree .monaco-tree-row .monaco-action-bar .action-label { + margin-right: 0.2em; + margin-top: 4px; + background-repeat: no-repeat; + width: 16px; + height: 16px; +} + +.search-viewlet .action-remove { + background: url("action-remove.svg") center center no-repeat; +} + +.search-viewlet .action-replace { + background-image: url('replace.svg'); +} + +.search-viewlet .action-replace-all { + background: url('replace-all.svg') center center no-repeat; +} + +.hc-black .search-viewlet .action-replace, +.vs-dark .search-viewlet .action-replace { + background-image: url('replace-inverse.svg'); +} + +.hc-black .search-viewlet .action-replace-all, +.vs-dark .search-viewlet .action-replace-all { + background: url('replace-all-inverse.svg') center center no-repeat; +} + +.search-viewlet .label { + font-style: italic; +} + +.search-viewlet .monaco-count-badge { + margin-right: 12px; +} + +.search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .filematch .monaco-count-badge, +.search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .foldermatch .monaco-count-badge, +.search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .linematch .monaco-count-badge, +.search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .filematch .monaco-count-badge, +.search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .foldermatch .monaco-count-badge, +.search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .linematch .monaco-count-badge { + display: none; +} + +.search-viewlet .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .action-remove, +.vs-dark .monaco-workbench .search-viewlet .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .action-remove { + background: url("action-remove-focus.svg") center center no-repeat; +} + +.monaco-workbench .search-action.refresh { + background: url('Refresh.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .search-action.refresh, +.hc-black .monaco-workbench .search-action.refresh { + background: url('Refresh_inverse.svg') center center no-repeat; +} + +.monaco-workbench .search-action.collapse { + background: url('CollapseAll.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .search-action.collapse, +.hc-black .monaco-workbench .search-action.collapse { + background: url('CollapseAll_inverse.svg') center center no-repeat; +} + +.monaco-workbench .search-action.clear-search-results { + background: url('clear-search-results.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .search-action.clear-search-results, +.hc-black .monaco-workbench .search-action.clear-search-results { + background: url('clear-search-results-dark.svg') center center no-repeat; +} + +.vs .monaco-workbench .search-viewlet .query-details .file-types .controls > .custom-checkbox.pattern { + background: url('pattern.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .search-viewlet .query-details .file-types .controls > .custom-checkbox.pattern, +.hc-black .monaco-workbench .search-viewlet .query-details .file-types .controls > .custom-checkbox.pattern { + background: url('pattern-dark.svg') center center no-repeat; +} + +.vs .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useIgnoreFiles { + background: url('gitignore.svg') center center no-repeat; + background-size: 18px 18px; +} + +.vs-dark .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useIgnoreFiles, +.hc-black .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useIgnoreFiles { + background: url('gitignore-dark.svg') center center no-repeat; + background-size: 18px 18px; +} + +.vs .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useExcludeSettings { + background: url('excludeSettings.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useExcludeSettings, +.hc-black .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useExcludeSettings { + background: url('excludeSettings-dark.svg') center center no-repeat; +} + +.search-viewlet .replace.findInFileMatch { + text-decoration: line-through; +} + +.search-viewlet .findInFileMatch, +.search-viewlet .replaceMatch { + white-space: pre; +} + +.hc-black .monaco-workbench .search-viewlet .replaceMatch, +.hc-black .monaco-workbench .search-viewlet .findInFileMatch { + background: none !important; + box-sizing: border-box; +} + +.monaco-workbench .search-viewlet a.prominent { + text-decoration: underline; +} + +/* Theming */ + +.vs .search-viewlet .search-widget .toggle-replace-button:hover { + background-color: rgba(0, 0, 0, 0.1) !important; +} + +.vs-dark .search-viewlet .search-widget .toggle-replace-button:hover { + background-color: rgba(255, 255, 255, 0.1) !important; +} + +.vs .search-viewlet .search-widget .toggle-replace-button.collapse { + background-image: url('expando-collapsed.svg'); +} + +.vs .search-viewlet .search-widget .toggle-replace-button.expand { + background-image: url('expando-expanded.svg'); +} + +.vs-dark .search-viewlet .query-clear { + background: url("action-query-clear-dark.svg") center center no-repeat; +} + +.vs-dark .search-viewlet .action-remove, +.hc-black .search-viewlet .action-remove { + background: url("action-remove-dark.svg") center center no-repeat; +} + +.vs-dark .search-viewlet .message { + opacity: .5; +} + +.vs-dark .search-viewlet .foldermatch, +.vs-dark .search-viewlet .filematch { + padding: 0; +} + +.vs-dark .search-viewlet .search-widget .toggle-replace-button.expand, +.hc-black .search-viewlet .search-widget .toggle-replace-button.expand { + background-image: url('expando-expanded-dark.svg'); +} + +.vs-dark .search-viewlet .search-widget .toggle-replace-button.collapse, +.hc-black .search-viewlet .search-widget .toggle-replace-button.collapse { + background-image: url('expando-collapsed-dark.svg'); +} + +/* High Contrast Theming */ + +.hc-black .monaco-workbench .search-viewlet .foldermatch, +.hc-black .monaco-workbench .search-viewlet .filematch, +.hc-black .monaco-workbench .search-viewlet .linematch { + line-height: 20px; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts new file mode 100644 index 0000000000..1b0fd7b66a --- /dev/null +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -0,0 +1,367 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as arrays from 'vs/base/common/arrays'; +import * as objects from 'vs/base/common/objects'; +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import { ThrottledDelayer } from 'vs/base/common/async'; +import types = require('vs/base/common/types'); +import { isWindows } from 'vs/base/common/platform'; +import strings = require('vs/base/common/strings'); +import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; +import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenHandler } from 'vs/workbench/browser/quickopen'; +import { FileEntry, OpenFileHandler, FileQuickOpenModel } from 'vs/workbench/parts/search/browser/openFileHandler'; +import * as openSymbolHandler from 'vs/workbench/parts/search/browser/openSymbolHandler'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ISearchStats, ICachedSearchStats, IUncachedSearchStats } from 'vs/platform/search/common/search'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkbenchSearchConfiguration } from 'vs/workbench/parts/search/common/search'; + +const objects_assign: (destination: T, source: U) => T & U = objects.assign; + +interface ISearchWithRange { + search: string; + range: IRange; +} + +interface ITimerEventData { + searchLength: number; + unsortedResultDuration: number; + sortedResultDuration: number; + resultCount: number; + symbols: { + fromCache: boolean; + }; + files: { + fromCache: boolean; + unsortedResultDuration: number; + sortedResultDuration: number; + resultCount: number; + } & ({ + traversal: string; + errors: string[]; + fileWalkStartDuration: number; + fileWalkResultDuration: number; + directoriesWalked: number; + filesWalked: number; + cmdForkStartTime?: number; + cmdForkResultTime?: number; + cmdResultCount?: number; + } | { + cacheLookupStartDuration: number; + cacheFilterStartDuration: number; + cacheLookupResultDuration: number; + cacheEntryCount: number; + joined?: any; + }); +} + +interface ITelemetryData { + searchLength: number; + unsortedResultTime: number; + sortedResultTime: number; + resultCount: number; + symbols: { + fromCache: boolean; + }; + files: ISearchStats; +} + +// OpenSymbolHandler is used from an extension and must be in the main bundle file so it can load +export import OpenSymbolHandler = openSymbolHandler.OpenSymbolHandler; +import { IRange } from 'vs/editor/common/core/range'; + +export class OpenAnythingHandler extends QuickOpenHandler { + + private static LINE_COLON_PATTERN = /[#|:|\(](\d*)([#|:|,](\d*))?\)?$/; + + private static FILE_SEARCH_DELAY = 300; + private static SYMBOL_SEARCH_DELAY = 500; // go easier on those symbols! + + private static MAX_DISPLAYED_RESULTS = 512; + + private openSymbolHandler: OpenSymbolHandler; + private openFileHandler: OpenFileHandler; + private searchDelayer: ThrottledDelayer; + private pendingSearch: TPromise; + private isClosed: boolean; + private scorerCache: { [key: string]: number }; + private includeSymbols: boolean; + + constructor( + @IMessageService private messageService: IMessageService, + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService private configurationService: IConfigurationService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + super(); + + this.scorerCache = Object.create(null); + this.searchDelayer = new ThrottledDelayer(OpenAnythingHandler.FILE_SEARCH_DELAY); + + this.openSymbolHandler = instantiationService.createInstance(OpenSymbolHandler); + this.openFileHandler = instantiationService.createInstance(OpenFileHandler); + + this.updateHandlers(this.configurationService.getConfiguration()); + + this.registerListeners(); + } + + private registerListeners(): void { + this.configurationService.onDidUpdateConfiguration(e => this.updateHandlers(this.configurationService.getConfiguration())); + } + + private updateHandlers(configuration: IWorkbenchSearchConfiguration): void { + this.includeSymbols = configuration && configuration.search && configuration.search.quickOpen && configuration.search.quickOpen.includeSymbols; + + // Files + this.openFileHandler.setOptions({ + forceUseIcons: this.includeSymbols // only need icons for file results if we mix with symbol results + }); + + // Symbols + this.openSymbolHandler.setOptions({ + skipDelay: true, // we have our own delay + skipLocalSymbols: true, // we only want global symbols + skipSorting: true // we sort combined with file results + }); + } + + public getResults(searchValue: string): TPromise { + const startTime = Date.now(); + + this.cancelPendingSearch(); + this.isClosed = false; // Treat this call as the handler being in use + + // Massage search value + searchValue = searchValue.replace(/ /g, ''); // get rid of all whitespace + if (isWindows) { + searchValue = searchValue.replace(/\//g, '\\'); // Help Windows users to search for paths when using slash + } + + const searchWithRange = this.extractRange(searchValue); // Find a suitable range from the pattern looking for ":" and "#" + if (searchWithRange) { + searchValue = searchWithRange.search; // ignore range portion in query + } + + if (!searchValue) { + return TPromise.as(new QuickOpenModel()); // Respond directly to empty search + } + + // The throttler needs a factory for its promises + const promiseFactory = () => { + const resultPromises: TPromise[] = []; + + // File Results + const filePromise = this.openFileHandler.getResults(searchValue, OpenAnythingHandler.MAX_DISPLAYED_RESULTS); + resultPromises.push(filePromise); + + // Symbol Results (unless disabled or a range or absolute path is specified) + if (this.includeSymbols && !searchWithRange) { + resultPromises.push(this.openSymbolHandler.getResults(searchValue)); + } + + // Join and sort unified + this.pendingSearch = TPromise.join(resultPromises).then(results => { + this.pendingSearch = null; + + // If the quick open widget has been closed meanwhile, ignore the result + if (this.isClosed) { + return TPromise.as(new QuickOpenModel()); + } + + // Combine results. + const mergedResults = [].concat(...results.map(r => r.entries)); + + // Sort + const unsortedResultTime = Date.now(); + const normalizedSearchValue = strings.stripWildcards(searchValue).toLowerCase(); + const compare = (elementA: QuickOpenEntry, elementB: QuickOpenEntry) => QuickOpenEntry.compareByScore(elementA, elementB, searchValue, normalizedSearchValue, this.scorerCache); + const viewResults = arrays.top(mergedResults, compare, OpenAnythingHandler.MAX_DISPLAYED_RESULTS); + const sortedResultTime = Date.now(); + + // Apply range and highlights to file entries + viewResults.forEach(entry => { + if (entry instanceof FileEntry) { + entry.setRange(searchWithRange ? searchWithRange.range : null); + + const { labelHighlights, descriptionHighlights } = QuickOpenEntry.highlight(entry, searchValue, true /* fuzzy highlight */); + entry.setHighlights(labelHighlights, descriptionHighlights); + } + }); + + const duration = new Date().getTime() - startTime; + filePromise.then(fileModel => { + const data = this.createTimerEventData(startTime, { + searchLength: searchValue.length, + unsortedResultTime, + sortedResultTime, + resultCount: mergedResults.length, + symbols: { fromCache: false }, + files: fileModel.stats, + }); + + this.telemetryService.publicLog('openAnything', objects.assign(data, { duration })); + }); + + return TPromise.as(new QuickOpenModel(viewResults)); + }, (error: Error) => { + this.pendingSearch = null; + this.messageService.show(Severity.Error, error); + return null; + }); + + return this.pendingSearch; + }; + + // Trigger through delayer to prevent accumulation while the user is typing (except when expecting results to come from cache) + return this.hasShortResponseTime() ? promiseFactory() : this.searchDelayer.trigger(promiseFactory, this.includeSymbols ? OpenAnythingHandler.SYMBOL_SEARCH_DELAY : OpenAnythingHandler.FILE_SEARCH_DELAY); + } + + public hasShortResponseTime(): boolean { + if (!this.includeSymbols) { + return this.openFileHandler.hasShortResponseTime(); + } + + return this.openFileHandler.hasShortResponseTime() && this.openSymbolHandler.hasShortResponseTime(); + } + + private extractRange(value: string): ISearchWithRange { + if (!value) { + return null; + } + + let range: IRange = null; + + // Find Line/Column number from search value using RegExp + const patternMatch = OpenAnythingHandler.LINE_COLON_PATTERN.exec(value); + if (patternMatch && patternMatch.length > 1) { + const startLineNumber = parseInt(patternMatch[1], 10); + + // Line Number + if (types.isNumber(startLineNumber)) { + range = { + startLineNumber: startLineNumber, + startColumn: 1, + endLineNumber: startLineNumber, + endColumn: 1 + }; + + // Column Number + if (patternMatch.length > 3) { + const startColumn = parseInt(patternMatch[3], 10); + if (types.isNumber(startColumn)) { + range = { + startLineNumber: range.startLineNumber, + startColumn: startColumn, + endLineNumber: range.endLineNumber, + endColumn: startColumn + }; + } + } + } + + // User has typed "something:" or "something#" without a line number, in this case treat as start of file + else if (patternMatch[1] === '') { + range = { + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 1 + }; + } + } + + if (range) { + return { + search: value.substr(0, patternMatch.index), // clear range suffix from search value + range: range + }; + } + + return null; + } + + public getGroupLabel(): string { + return this.includeSymbols ? nls.localize('fileAndTypeResults', "file and symbol results") : nls.localize('fileResults', "file results"); + } + + public getAutoFocus(searchValue: string): IAutoFocus { + return { + autoFocusFirstEntry: true + }; + } + + public onOpen(): void { + this.openSymbolHandler.onOpen(); + this.openFileHandler.onOpen(); + } + + public onClose(canceled: boolean): void { + this.isClosed = true; + + // Cancel any pending search + this.cancelPendingSearch(); + + // Clear Cache + this.scorerCache = Object.create(null); + + // Propagate + this.openSymbolHandler.onClose(canceled); + this.openFileHandler.onClose(canceled); + } + + private cancelPendingSearch(): void { + if (this.pendingSearch) { + this.pendingSearch.cancel(); + this.pendingSearch = null; + } + } + + private createTimerEventData(startTime: number, telemetry: ITelemetryData): ITimerEventData { + return { + searchLength: telemetry.searchLength, + unsortedResultDuration: telemetry.unsortedResultTime - startTime, + sortedResultDuration: telemetry.sortedResultTime - startTime, + resultCount: telemetry.resultCount, + symbols: telemetry.symbols, + files: telemetry.files && this.createFileEventData(startTime, telemetry.files) + }; + } + + private createFileEventData(startTime: number, stats: ISearchStats) { + const cached = stats as ICachedSearchStats; + const uncached = stats as IUncachedSearchStats; + + return objects_assign({ + fromCache: stats.fromCache, + unsortedResultDuration: stats.unsortedResultTime && stats.unsortedResultTime - startTime, + sortedResultDuration: stats.sortedResultTime && stats.sortedResultTime - startTime, + resultCount: stats.resultCount + }, stats.fromCache ? { + cacheLookupStartDuration: cached.cacheLookupStartTime - startTime, + cacheFilterStartDuration: cached.cacheFilterStartTime - startTime, + cacheLookupResultDuration: cached.cacheLookupResultTime - startTime, + cacheEntryCount: cached.cacheEntryCount, + joined: cached.joined && this.createFileEventData(startTime, cached.joined) + } : { + traversal: uncached.traversal, + errors: uncached.errors, + fileWalkStartDuration: uncached.fileWalkStartTime - startTime, + fileWalkResultDuration: uncached.fileWalkResultTime - startTime, + directoriesWalked: uncached.directoriesWalked, + filesWalked: uncached.filesWalked, + cmdForkStartDuration: uncached.cmdForkStartTime && uncached.cmdForkStartTime - startTime, + cmdForkResultDuration: uncached.cmdForkResultTime && uncached.cmdForkResultTime - startTime, + cmdResultCount: uncached.cmdResultCount + }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/openFileHandler.ts b/src/vs/workbench/parts/search/browser/openFileHandler.ts new file mode 100644 index 0000000000..6e3e9d90d2 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/openFileHandler.ts @@ -0,0 +1,300 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import errors = require('vs/base/common/errors'); +import nls = require('vs/nls'); +import paths = require('vs/base/common/paths'); +import labels = require('vs/base/common/labels'); +import * as objects from 'vs/base/common/objects'; +import { defaultGenerator } from 'vs/base/common/idGenerator'; +import URI from 'vs/base/common/uri'; +import { IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { getIconClasses } from 'vs/workbench/browser/labels'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; +import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenHandler, EditorQuickOpenEntry } from 'vs/workbench/browser/quickopen'; +import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IQueryOptions, ISearchService, ISearchStats, ISearchQuery } from 'vs/platform/search/common/search'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IRange } from 'vs/editor/common/core/range'; +import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/common/search'; + +export class FileQuickOpenModel extends QuickOpenModel { + + constructor(entries: QuickOpenEntry[], public stats?: ISearchStats) { + super(entries); + } +} + +export class FileEntry extends EditorQuickOpenEntry { + private range: IRange; + + constructor( + private resource: URI, + private name: string, + private description: string, + private icon: string, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IModeService private modeService: IModeService, + @IModelService private modelService: IModelService, + @IConfigurationService private configurationService: IConfigurationService, + @IWorkspaceContextService contextService: IWorkspaceContextService + ) { + super(editorService); + } + + public getLabel(): string { + return this.name; + } + + public getLabelOptions(): IIconLabelOptions { + return { + extraClasses: getIconClasses(this.modelService, this.modeService, this.resource) + }; + } + + public getAriaLabel(): string { + return nls.localize('entryAriaLabel', "{0}, file picker", this.getLabel()); + } + + public getDescription(): string { + return this.description; + } + + public getIcon(): string { + return this.icon; + } + + public getResource(): URI { + return this.resource; + } + + public setRange(range: IRange): void { + this.range = range; + } + + public isFile(): boolean { + return true; // TODO@Ben debt with editor history merging + } + + public getInput(): IResourceInput | EditorInput { + const input: IResourceInput = { + resource: this.resource, + options: { + pinned: !this.configurationService.getConfiguration().workbench.editor.enablePreviewFromQuickOpen + } + }; + + if (this.range) { + input.options.selection = this.range; + } + + return input; + } +} + +export interface IOpenFileOptions { + forceUseIcons: boolean; +} + +export class OpenFileHandler extends QuickOpenHandler { + private options: IOpenFileOptions; + private queryBuilder: QueryBuilder; + private cacheState: CacheState; + + constructor( + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IInstantiationService private instantiationService: IInstantiationService, + @IWorkbenchThemeService private themeService: IWorkbenchThemeService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ISearchService private searchService: ISearchService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + super(); + + this.queryBuilder = this.instantiationService.createInstance(QueryBuilder); + } + + public setOptions(options: IOpenFileOptions) { + this.options = options; + } + + public getResults(searchValue: string, maxSortedResults?: number): TPromise { + searchValue = searchValue.trim(); + + // Respond directly to empty search + if (!searchValue) { + return TPromise.as(new FileQuickOpenModel([])); + } + + // Do find results + return this.doFindResults(searchValue, this.cacheState.cacheKey, maxSortedResults); + } + + private doFindResults(searchValue: string, cacheKey?: string, maxSortedResults?: number): TPromise { + const query: IQueryOptions = { + extraFileResources: getOutOfWorkspaceEditorResources(this.editorGroupService, this.contextService), + filePattern: searchValue, + cacheKey: cacheKey + }; + + if (typeof maxSortedResults === 'number') { + query.maxResults = maxSortedResults; + query.sortByScore = true; + } + + let iconClass: string; + if (this.options && this.options.forceUseIcons && !this.themeService.getFileIconTheme()) { + iconClass = 'file'; // only use a generic file icon if we are forced to use an icon and have no icon theme set otherwise + } + + const folderResources = this.contextService.hasWorkspace() ? this.contextService.getWorkspace().roots : []; + return this.searchService.search(this.queryBuilder.file(folderResources, query)).then((complete) => { + const results: QuickOpenEntry[] = []; + for (let i = 0; i < complete.results.length; i++) { + const fileMatch = complete.results[i]; + + const label = paths.basename(fileMatch.resource.fsPath); + const description = labels.getPathLabel(paths.dirname(fileMatch.resource.fsPath), this.contextService, this.environmentService); + + results.push(this.instantiationService.createInstance(FileEntry, fileMatch.resource, label, description, iconClass)); + } + + return new FileQuickOpenModel(results, complete.stats); + }); + } + + public hasShortResponseTime(): boolean { + return this.isCacheLoaded; + } + + public onOpen(): void { + this.cacheState = new CacheState(cacheKey => this.cacheQuery(cacheKey), query => this.searchService.search(query), cacheKey => this.searchService.clearCache(cacheKey), this.cacheState); + this.cacheState.load(); + } + + private cacheQuery(cacheKey: string): ISearchQuery { + const options: IQueryOptions = { + extraFileResources: getOutOfWorkspaceEditorResources(this.editorGroupService, this.contextService), + filePattern: '', + cacheKey: cacheKey, + maxResults: 0, + sortByScore: true + }; + + const folderResources = this.contextService.hasWorkspace() ? this.contextService.getWorkspace().roots : []; + const query = this.queryBuilder.file(folderResources, options); + + return query; + } + + public get isCacheLoaded(): boolean { + return this.cacheState && this.cacheState.isLoaded; + } + + public getGroupLabel(): string { + return nls.localize('searchResults', "search results"); + } + + public getAutoFocus(searchValue: string): IAutoFocus { + return { + autoFocusFirstEntry: true + }; + } +} + +enum LoadingPhase { + Created = 1, + Loading, + Loaded, + Errored, + Disposed +} + +/** + * Exported for testing. + */ +export class CacheState { + + private _cacheKey = defaultGenerator.nextId(); + private query: ISearchQuery; + + private loadingPhase = LoadingPhase.Created; + private promise: TPromise; + + constructor(cacheQuery: (cacheKey: string) => ISearchQuery, private doLoad: (query: ISearchQuery) => TPromise, private doDispose: (cacheKey: string) => TPromise, private previous: CacheState) { + this.query = cacheQuery(this._cacheKey); + if (this.previous) { + const current = objects.assign({}, this.query, { cacheKey: null }); + const previous = objects.assign({}, this.previous.query, { cacheKey: null }); + if (!objects.equals(current, previous)) { + this.previous.dispose(); + this.previous = null; + } + } + } + + public get cacheKey(): string { + return this.loadingPhase === LoadingPhase.Loaded || !this.previous ? this._cacheKey : this.previous.cacheKey; + } + + public get isLoaded(): boolean { + const isLoaded = this.loadingPhase === LoadingPhase.Loaded; + return isLoaded || !this.previous ? isLoaded : this.previous.isLoaded; + } + + public get isUpdating(): boolean { + const isUpdating = this.loadingPhase === LoadingPhase.Loading; + return isUpdating || !this.previous ? isUpdating : this.previous.isUpdating; + } + + public load(): void { + if (this.isUpdating) { + return; + } + this.loadingPhase = LoadingPhase.Loading; + this.promise = this.doLoad(this.query) + .then(() => { + this.loadingPhase = LoadingPhase.Loaded; + if (this.previous) { + this.previous.dispose(); + this.previous = null; + } + }, err => { + this.loadingPhase = LoadingPhase.Errored; + errors.onUnexpectedError(err); + }); + } + + public dispose(): void { + if (this.promise) { + this.promise.then(null, () => { }) + .then(() => { + this.loadingPhase = LoadingPhase.Disposed; + return this.doDispose(this._cacheKey); + }).then(null, err => { + errors.onUnexpectedError(err); + }); + } else { + this.loadingPhase = LoadingPhase.Disposed; + } + if (this.previous) { + this.previous.dispose(); + this.previous = null; + } + } +} diff --git a/src/vs/workbench/parts/search/browser/openSymbolHandler.ts b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts new file mode 100644 index 0000000000..c948b45bca --- /dev/null +++ b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts @@ -0,0 +1,215 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { ThrottledDelayer } from 'vs/base/common/async'; +import { QuickOpenHandler, EditorQuickOpenEntry } from 'vs/workbench/browser/quickopen'; +import { QuickOpenModel, QuickOpenEntry } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; +import filters = require('vs/base/common/filters'); +import strings = require('vs/base/common/strings'); +import { Range } from 'vs/editor/common/core/range'; +import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; +import labels = require('vs/base/common/labels'); +import { SymbolInformation, symbolKindToCssClass } from 'vs/editor/common/modes'; +import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceSymbolProvider, getWorkspaceSymbols } from 'vs/workbench/parts/search/common/search'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { basename } from 'vs/base/common/paths'; + +class SymbolEntry extends EditorQuickOpenEntry { + + private _bearingResolve: TPromise; + + constructor( + private _bearing: SymbolInformation, + private _provider: IWorkspaceSymbolProvider, + @IConfigurationService private _configurationService: IConfigurationService, + @IWorkspaceContextService private _contextService: IWorkspaceContextService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IEnvironmentService private _environmentService: IEnvironmentService + ) { + super(editorService); + } + + public getLabel(): string { + return this._bearing.name; + } + + public getAriaLabel(): string { + return nls.localize('entryAriaLabel', "{0}, symbols picker", this.getLabel()); + } + + public getDescription(): string { + const containerName = this._bearing.containerName; + if (this._bearing.location.uri) { + if (containerName) { + return `${containerName} — ${basename(this._bearing.location.uri.fsPath)}`; + } else { + return labels.getPathLabel(this._bearing.location.uri, this._contextService, this._environmentService); + } + } + return containerName; + } + + public getIcon(): string { + return symbolKindToCssClass(this._bearing.kind); + } + + public getResource(): URI { + return this._bearing.location.uri; + } + + public run(mode: Mode, context: IEntryRunContext): boolean { + + // resolve this type bearing if neccessary + if (!this._bearingResolve + && typeof this._provider.resolveWorkspaceSymbol === 'function' + && !this._bearing.location.range + ) { + + this._bearingResolve = this._provider.resolveWorkspaceSymbol(this._bearing).then(result => { + this._bearing = result || this._bearing; + return this; + }, onUnexpectedError); + } + + TPromise.as(this._bearingResolve) + .then(_ => super.run(mode, context)) + .done(undefined, onUnexpectedError); + + // hide if OPEN + return mode === Mode.OPEN; + } + + public getInput(): IResourceInput | EditorInput { + let input: IResourceInput = { + resource: this._bearing.location.uri, + options: { + pinned: !this._configurationService.getConfiguration().workbench.editor.enablePreviewFromQuickOpen + } + }; + + if (this._bearing.location.range) { + input.options.selection = Range.collapseToStart(this._bearing.location.range); + } + + return input; + } + + public static compare(elementA: SymbolEntry, elementB: SymbolEntry, searchValue: string): number { + + // Sort by Type if name is identical + const elementAName = elementA.getLabel().toLowerCase(); + const elementBName = elementB.getLabel().toLowerCase(); + if (elementAName === elementBName) { + let elementAType = symbolKindToCssClass(elementA._bearing.kind); + let elementBType = symbolKindToCssClass(elementB._bearing.kind); + return elementAType.localeCompare(elementBType); + } + + return QuickOpenEntry.compare(elementA, elementB, searchValue); + } +} + +export interface IOpenSymbolOptions { + skipSorting: boolean; + skipLocalSymbols: boolean; + skipDelay: boolean; +} + +export class OpenSymbolHandler extends QuickOpenHandler { + + private static SEARCH_DELAY = 500; // This delay accommodates for the user typing a word and then stops typing to start searching + + private delayer: ThrottledDelayer; + private options: IOpenSymbolOptions; + + constructor( @IInstantiationService private instantiationService: IInstantiationService) { + super(); + + this.delayer = new ThrottledDelayer(OpenSymbolHandler.SEARCH_DELAY); + this.options = Object.create(null); + } + + public setOptions(options: IOpenSymbolOptions) { + this.options = options; + } + + public canRun(): boolean | string { + return true; + } + + public getResults(searchValue: string): TPromise { + searchValue = searchValue.trim(); + + let promise: TPromise; + if (!this.options.skipDelay) { + promise = this.delayer.trigger(() => this.doGetResults(searchValue)); // Run search with delay as needed + } else { + promise = this.doGetResults(searchValue); + } + + return promise.then(e => new QuickOpenModel(e)); + } + + private doGetResults(searchValue: string): TPromise { + return getWorkspaceSymbols(searchValue).then(tuples => { + const result: SymbolEntry[] = []; + for (let tuple of tuples) { + const [provider, bearings] = tuple; + this.fillInSymbolEntries(result, provider, bearings, searchValue); + } + + // Sort (Standalone only) + if (!this.options.skipSorting) { + searchValue = searchValue ? strings.stripWildcards(searchValue.toLowerCase()) : searchValue; + return result.sort((a, b) => SymbolEntry.compare(a, b, searchValue)); + } else { + return result; + } + }); + } + + private fillInSymbolEntries(bucket: SymbolEntry[], provider: IWorkspaceSymbolProvider, types: SymbolInformation[], searchValue: string): void { + + // Convert to Entries + for (let element of types) { + if (this.options.skipLocalSymbols && !!element.containerName) { + continue; // ignore local symbols if we are told so + } + + const entry = this.instantiationService.createInstance(SymbolEntry, element, provider); + entry.setHighlights(filters.matchesFuzzy(searchValue, entry.getLabel())); + bucket.push(entry); + } + } + + public getGroupLabel(): string { + return nls.localize('symbols', "symbol results"); + } + + public getEmptyLabel(searchString: string): string { + if (searchString.length > 0) { + return nls.localize('noSymbolsMatching', "No symbols matching"); + } + return nls.localize('noSymbolsWithoutInput', "Type to search for symbols"); + } + + public getAutoFocus(searchValue: string): IAutoFocus { + return { + autoFocusFirstEntry: true, + autoFocusPrefixMatch: searchValue.trim() + }; + } +} diff --git a/src/vs/workbench/parts/search/browser/patternInputWidget.ts b/src/vs/workbench/parts/search/browser/patternInputWidget.ts new file mode 100644 index 0000000000..5d128237ee --- /dev/null +++ b/src/vs/workbench/parts/search/browser/patternInputWidget.ts @@ -0,0 +1,271 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import * as dom from 'vs/base/browser/dom'; +import { $ } from 'vs/base/browser/builder'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; +import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { InputBox, IInputValidator } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import CommonEvent, { Emitter } from 'vs/base/common/event'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachInputBoxStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { HistoryNavigator } from 'vs/base/common/history'; + +export interface IOptions { + placeholder?: string; + width?: number; + validation?: IInputValidator; + ariaLabel?: string; +} + +export class PatternInputWidget extends Widget { + + static OPTION_CHANGE: string = 'optionChange'; + + public inputFocusTracker: dom.IFocusTracker; + + protected onOptionChange: (event: Event) => void; + private width: number; + private placeholder: string; + private ariaLabel: string; + + private domNode: HTMLElement; + private inputNode: HTMLInputElement; + protected inputBox: InputBox; + + private history: HistoryNavigator; + + private _onSubmit = this._register(new Emitter()); + public onSubmit: CommonEvent = this._onSubmit.event; + + private _onCancel = this._register(new Emitter()); + public onCancel: CommonEvent = this._onCancel.event; + + constructor(parent: HTMLElement, private contextViewProvider: IContextViewProvider, protected themeService: IThemeService, options: IOptions = Object.create(null)) { + super(); + this.history = new HistoryNavigator(); + this.onOptionChange = null; + this.width = options.width || 100; + this.placeholder = options.placeholder || ''; + this.ariaLabel = options.ariaLabel || nls.localize('defaultLabel', "input"); + + this.domNode = null; + this.inputNode = null; + this.inputBox = null; + + this.render(); + + parent.appendChild(this.domNode); + } + + public dispose(): void { + super.dispose(); + if (this.inputFocusTracker) { + this.inputFocusTracker.dispose(); + } + } + + public on(eventType: string, handler: (event: Event) => void): PatternInputWidget { + switch (eventType) { + case 'keydown': + case 'keyup': + $(this.inputBox.inputElement).on(eventType, handler); + break; + case PatternInputWidget.OPTION_CHANGE: + this.onOptionChange = handler; + break; + } + return this; + } + + public setWidth(newWidth: number): void { + this.width = newWidth; + this.domNode.style.width = this.width + 'px'; + this.contextViewProvider.layout(); + this.setInputWidth(); + } + + public getValue(): string { + return this.inputBox.value; + } + + public setValue(value: string): void { + if (this.inputBox.value !== value) { + this.inputBox.value = value; + } + } + + + public select(): void { + this.inputBox.select(); + } + + public focus(): void { + this.inputBox.focus(); + } + + public inputHasFocus(): boolean { + return this.inputBox.hasFocus(); + } + + private setInputWidth(): void { + this.inputBox.width = this.width - this.getSubcontrolsWidth() - 2; // 2 for input box border + } + + protected getSubcontrolsWidth(): number { + return 0; + } + + public getHistory(): string[] { + return this.history.getHistory(); + } + + public setHistory(history: string[]) { + this.history = new HistoryNavigator(history); + } + + public onSearchSubmit(): void { + const value = this.getValue(); + if (value) { + this.history.addIfNotPresent(value); + } + } + + public showNextTerm() { + let next = this.history.next(); + if (next) { + this.setValue(next); + } + } + + public showPreviousTerm() { + let previous; + if (this.getValue().length === 0) { + previous = this.history.current(); + } else { + this.history.addIfNotPresent(this.getValue()); + previous = this.history.previous(); + } + if (previous) { + this.setValue(previous); + } + } + + private render(): void { + this.domNode = document.createElement('div'); + this.domNode.style.width = this.width + 'px'; + $(this.domNode).addClass('monaco-findInput'); + + this.inputBox = new InputBox(this.domNode, this.contextViewProvider, { + placeholder: this.placeholder || '', + ariaLabel: this.ariaLabel || '', + validationOptions: { + validation: null, + showMessage: true + } + }); + this._register(attachInputBoxStyler(this.inputBox, this.themeService)); + this.inputFocusTracker = dom.trackFocus(this.inputBox.inputElement); + this.onkeyup(this.inputBox.inputElement, (keyboardEvent) => this.onInputKeyUp(keyboardEvent)); + + let controls = document.createElement('div'); + controls.className = 'controls'; + this.renderSubcontrols(controls); + + this.domNode.appendChild(controls); + this.setInputWidth(); + } + + protected renderSubcontrols(controlsDiv: HTMLDivElement): void { + } + + private onInputKeyUp(keyboardEvent: IKeyboardEvent) { + switch (keyboardEvent.keyCode) { + case KeyCode.Enter: + this._onSubmit.fire(); + return; + case KeyCode.Escape: + this._onCancel.fire(); + return; + default: + return; + } + } +} + +export class ExcludePatternInputWidget extends PatternInputWidget { + + constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, themeService: IThemeService, private telemetryService: ITelemetryService, options: IOptions = Object.create(null)) { + super(parent, contextViewProvider, themeService, options); + } + + private useIgnoreFilesBox: Checkbox; + private useExcludeSettingsBox: Checkbox; + + public dispose(): void { + super.dispose(); + this.useIgnoreFilesBox.dispose(); + this.useExcludeSettingsBox.dispose(); + } + + public useExcludeSettings(): boolean { + return this.useExcludeSettingsBox.checked; + } + + public setUseExcludeSettings(value: boolean) { + this.useExcludeSettingsBox.checked = value; + } + + public useIgnoreFiles(): boolean { + return this.useIgnoreFilesBox.checked; + } + + public setUseIgnoreFiles(value: boolean): void { + this.useIgnoreFilesBox.checked = value; + } + + protected getSubcontrolsWidth(): number { + return super.getSubcontrolsWidth() + this.useIgnoreFilesBox.width() + this.useExcludeSettingsBox.width(); + } + + protected renderSubcontrols(controlsDiv: HTMLDivElement): void { + this.useIgnoreFilesBox = new Checkbox({ + actionClassName: 'useIgnoreFiles', + title: nls.localize('useIgnoreFilesDescription', "Use Ignore Files"), + isChecked: false, + onChange: (viaKeyboard) => { + this.telemetryService.publicLog('search.useIgnoreFiles.toggled'); + this.onOptionChange(null); + if (!viaKeyboard) { + this.inputBox.focus(); + } + } + }); + this._register(attachCheckboxStyler(this.useIgnoreFilesBox, this.themeService)); + + this.useExcludeSettingsBox = new Checkbox({ + actionClassName: 'useExcludeSettings', + title: nls.localize('useExcludeSettingsDescription', "Use Exclude Settings"), + isChecked: false, + onChange: (viaKeyboard) => { + this.telemetryService.publicLog('search.useExcludeSettings.toggled'); + this.onOptionChange(null); + if (!viaKeyboard) { + this.inputBox.focus(); + } + } + }); + this._register(attachCheckboxStyler(this.useExcludeSettingsBox, this.themeService)); + + controlsDiv.appendChild(this.useIgnoreFilesBox.domNode); + controlsDiv.appendChild(this.useExcludeSettingsBox.domNode); + super.renderSubcontrols(controlsDiv); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/replaceContributions.ts b/src/vs/workbench/parts/search/browser/replaceContributions.ts new file mode 100644 index 0000000000..72d51dec13 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/replaceContributions.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 { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; +import { ReplaceService, ReplacePreviewContentProvider } from 'vs/workbench/parts/search/browser/replaceService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; + +export function registerContributions(): void { + registerSingleton(IReplaceService, ReplaceService); + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ReplacePreviewContentProvider); +} diff --git a/src/vs/workbench/parts/search/browser/replaceService.ts b/src/vs/workbench/parts/search/browser/replaceService.ts new file mode 100644 index 0000000000..0828e8d647 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/replaceService.ts @@ -0,0 +1,194 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as errors from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import * as network from 'vs/base/common/network'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; +import { IEditorService } from 'vs/platform/editor/common/editor'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { Match, FileMatch, FileMatchOrMatch, ISearchWorkbenchService } from 'vs/workbench/parts/search/common/searchModel'; +import { BulkEdit, IResourceEdit, createBulkEdit } from 'vs/editor/common/services/bulkEdit'; +import { IProgressRunner } from 'vs/platform/progress/common/progress'; +import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IModel, ScrollType } from 'vs/editor/common/editorCommon'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IFileService } from 'vs/platform/files/common/files'; + +const REPLACE_PREVIEW = 'replacePreview'; + +const toReplaceResource = (fileResource: URI): URI => { + return fileResource.with({ scheme: network.Schemas.internal, fragment: REPLACE_PREVIEW, query: JSON.stringify({ scheme: fileResource.scheme }) }); +}; + +const toFileResource = (replaceResource: URI): URI => { + return replaceResource.with({ scheme: JSON.parse(replaceResource.query)['scheme'], fragment: '', query: '' }); +}; + +export class ReplacePreviewContentProvider implements ITextModelContentProvider, IWorkbenchContribution { + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @ITextModelService private textModelResolverService: ITextModelService + ) { + this.textModelResolverService.registerTextModelContentProvider(network.Schemas.internal, this); + } + + public getId(): string { + return 'replace.preview.contentprovider'; + } + + public provideTextContent(uri: URI): TPromise { + if (uri.fragment === REPLACE_PREVIEW) { + return this.instantiationService.createInstance(ReplacePreviewModel).resolve(uri); + } + return null; + } +} + +class ReplacePreviewModel extends Disposable { + constructor( + @IModelService private modelService: IModelService, + @IModeService private modeService: IModeService, + @ITextModelService private textModelResolverService: ITextModelService, + @IReplaceService private replaceService: IReplaceService, + @ISearchWorkbenchService private searchWorkbenchService: ISearchWorkbenchService + ) { + super(); + } + + resolve(replacePreviewUri: URI): TPromise { + const fileResource = toFileResource(replacePreviewUri); + const fileMatch = this.searchWorkbenchService.searchModel.searchResult.matches().filter(match => match.resource().toString() === fileResource.toString())[0]; + return this.textModelResolverService.createModelReference(fileResource).then(ref => { + ref = this._register(ref); + const sourceModel = ref.object.textEditorModel; + const sourceModelModeId = sourceModel.getLanguageIdentifier().language; + const replacePreviewModel = this.modelService.createModel(sourceModel.getValue(), this.modeService.getOrCreateMode(sourceModelModeId), replacePreviewUri); + this._register(fileMatch.onChange(modelChange => this.update(sourceModel, replacePreviewModel, fileMatch, modelChange))); + this._register(this.searchWorkbenchService.searchModel.onReplaceTermChanged(() => this.update(sourceModel, replacePreviewModel, fileMatch))); + this._register(fileMatch.onDispose(() => replacePreviewModel.dispose())); // TODO@Sandeep we should not dispose a model directly but rather the reference (depends on https://github.com/Microsoft/vscode/issues/17073) + this._register(replacePreviewModel.onWillDispose(() => this.dispose())); + this._register(sourceModel.onWillDispose(() => this.dispose())); + return replacePreviewModel; + }); + } + + private update(sourceModel: IModel, replacePreviewModel: IModel, fileMatch: FileMatch, override: boolean = false): void { + if (!sourceModel.isDisposed() && !replacePreviewModel.isDisposed()) { + this.replaceService.updateReplacePreview(fileMatch, override); + } + } +} + +export class ReplaceService implements IReplaceService { + + public _serviceBrand: any; + + constructor( + @ITelemetryService private telemetryService: ITelemetryService, + @IFileService private fileService: IFileService, + @IEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService private instantiationService: IInstantiationService, + @ITextModelService private textModelResolverService: ITextModelService, + @ISearchWorkbenchService private searchWorkbenchService: ISearchWorkbenchService + ) { + } + + public replace(match: Match): TPromise + public replace(files: FileMatch[], progress?: IProgressRunner): TPromise + public replace(match: FileMatchOrMatch, progress?: IProgressRunner, resource?: URI): TPromise + public replace(arg: any, progress: IProgressRunner = null, resource: URI = null): TPromise { + + let bulkEdit: BulkEdit = createBulkEdit(this.textModelResolverService, null, this.fileService); + bulkEdit.progress(progress); + + if (arg instanceof Match) { + let match = arg; + bulkEdit.add([this.createEdit(match, match.replaceString, resource)]); + } + + if (arg instanceof FileMatch) { + arg = [arg]; + } + + if (arg instanceof Array) { + arg.forEach(element => { + let fileMatch = element; + if (fileMatch.count() > 0) { + fileMatch.matches().forEach(match => { + bulkEdit.add([this.createEdit(match, match.replaceString, resource)]); + }); + } + }); + } + + return bulkEdit.finish(); + } + + public openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + this.telemetryService.publicLog('replace.open.previewEditor'); + const fileMatch = element instanceof Match ? element.parent() : element; + + return this.editorService.openEditor({ + leftResource: fileMatch.resource(), + rightResource: toReplaceResource(fileMatch.resource()), + label: nls.localize('fileReplaceChanges', "{0} ↔ {1} (Replace Preview)", fileMatch.name(), fileMatch.name()), + options: { + preserveFocus, + pinned, + revealIfVisible: true + } + }).then(editor => { + this.updateReplacePreview(fileMatch).then(() => { + let editorControl = (editor.getControl()); + if (element instanceof Match) { + editorControl.revealLineInCenter(element.range().startLineNumber, ScrollType.Immediate); + } + }); + }, errors.onUnexpectedError); + } + + public updateReplacePreview(fileMatch: FileMatch, override: boolean = false): TPromise { + const replacePreviewUri = toReplaceResource(fileMatch.resource()); + return TPromise.join([this.textModelResolverService.createModelReference(fileMatch.resource()), this.textModelResolverService.createModelReference(replacePreviewUri)]) + .then(([sourceModelRef, replaceModelRef]) => { + const sourceModel = sourceModelRef.object.textEditorModel; + const replaceModel = replaceModelRef.object.textEditorModel; + let returnValue = TPromise.wrap(null); + // If model is disposed do not update + if (sourceModel && replaceModel) { + if (override) { + replaceModel.setValue(sourceModel.getValue()); + } else { + replaceModel.undo(); + } + returnValue = this.replace(fileMatch, null, replacePreviewUri); + } + return returnValue.then(() => { + sourceModelRef.dispose(); + replaceModelRef.dispose(); + }); + }); + } + + private createEdit(match: Match, text: string, resource: URI = null): IResourceEdit { + let fileMatch: FileMatch = match.parent(); + let resourceEdit: IResourceEdit = { + resource: resource !== null ? resource : fileMatch.resource(), + range: match.range(), + newText: text + }; + return resourceEdit; + } +} diff --git a/src/vs/workbench/parts/search/browser/search.contribution.ts b/src/vs/workbench/parts/search/browser/search.contribution.ts new file mode 100644 index 0000000000..c74f7edd9d --- /dev/null +++ b/src/vs/workbench/parts/search/browser/search.contribution.ts @@ -0,0 +1,374 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/search.contribution'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction, Action } from 'vs/base/common/actions'; +import { explorerItemToFileResource } from 'vs/workbench/parts/files/common/files'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions, ActionBarContributor } from 'vs/workbench/browser/actions'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions as QuickOpenExtensions } from 'vs/workbench/browser/quickopen'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { getSelectionSearchString } from 'vs/editor/contrib/find/common/find'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import * as searchActions from 'vs/workbench/parts/search/browser/searchActions'; +import { Model } from 'vs/workbench/parts/files/common/explorerModel'; +import * as Constants from 'vs/workbench/parts/search/common/constants'; +import { registerContributions as replaceContributions } from 'vs/workbench/parts/search/browser/replaceContributions'; +import { registerContributions as searchWidgetContributions } from 'vs/workbench/parts/search/browser/searchWidget'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding, ShowPreviousFindTermKeybinding, ShowNextFindTermKeybinding } from 'vs/editor/contrib/find/common/findModel'; +import { ISearchWorkbenchService, SearchWorkbenchService } from 'vs/workbench/parts/search/common/searchModel'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { SearchViewlet } from 'vs/workbench/parts/search/browser/searchViewlet'; +import { ListFocusContext } from 'vs/platform/list/browser/listService'; +import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/parts/output/common/output'; +import { defaultQuickOpenContextKey } from 'vs/workbench/browser/parts/quickopen/quickopen'; + +registerSingleton(ISearchWorkbenchService, SearchWorkbenchService); +replaceContributions(); +searchWidgetContributions(); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.action.search.toggleQueryDetails', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: Constants.SearchViewletVisibleKey, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_J, + handler: accessor => { + let viewletService = accessor.get(IViewletService); + viewletService.openViewlet(Constants.VIEWLET_ID, true) + .then((viewlet: SearchViewlet) => viewlet.toggleQueryDetails()); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.FocusSearchFromResults, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.FirstMatchFocusKey), + primary: KeyCode.UpArrow, + handler: (accessor, args: any) => { + const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); + searchViewlet.focusPreviousInputBox(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.OpenMatchToSide, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.FileMatchOrMatchFocusKey), + primary: KeyMod.CtrlCmd | KeyCode.Enter, + mac: { + primary: KeyMod.WinCtrl | KeyCode.Enter + }, + handler: (accessor, args: any) => { + const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); + const tree: ITree = searchViewlet.getControl(); + searchViewlet.open(tree.getFocus(), false, true, true); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.CancelActionId, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, ListFocusContext), + primary: KeyCode.Escape, + handler: (accessor, args: any) => { + const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); + searchViewlet.cancelSearch(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.RemoveActionId, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.FileMatchOrMatchFocusKey), + primary: KeyCode.Delete, + mac: { + primary: KeyMod.CtrlCmd | KeyCode.Backspace, + }, + handler: (accessor, args: any) => { + const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); + const tree: ITree = searchViewlet.getControl(); + accessor.get(IInstantiationService).createInstance(searchActions.RemoveAction, tree, tree.getFocus(), searchViewlet).run(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.ReplaceActionId, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceActiveKey, Constants.MatchFocusKey), + primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.KEY_1, + handler: (accessor, args: any) => { + const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); + const tree: ITree = searchViewlet.getControl(); + accessor.get(IInstantiationService).createInstance(searchActions.ReplaceAction, tree, tree.getFocus(), searchViewlet).run(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.ReplaceAllInFileActionId, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceActiveKey, Constants.FileFocusKey), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter, + handler: (accessor, args: any) => { + const searchViewlet: SearchViewlet = accessor.get(IViewletService).getActiveViewlet(); + const tree: ITree = searchViewlet.getControl(); + accessor.get(IInstantiationService).createInstance(searchActions.ReplaceAllAction, tree, tree.getFocus(), searchViewlet).run(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.CloseReplaceWidgetActionId, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceInputBoxFocusedKey), + primary: KeyCode.Escape, + handler: (accessor, args: any) => { + accessor.get(IInstantiationService).createInstance(searchActions.CloseReplaceAction, Constants.CloseReplaceWidgetActionId, '').run(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: searchActions.FocusNextInputAction.ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.InputBoxFocusedKey), + primary: KeyCode.DownArrow, + handler: (accessor, args: any) => { + accessor.get(IInstantiationService).createInstance(searchActions.FocusNextInputAction, searchActions.FocusNextInputAction.ID, '').run(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: searchActions.FocusPreviousInputAction.ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.InputBoxFocusedKey, Constants.SearchInputBoxFocusedKey.toNegated()), + primary: KeyCode.UpArrow, + handler: (accessor, args: any) => { + accessor.get(IInstantiationService).createInstance(searchActions.FocusPreviousInputAction, searchActions.FocusPreviousInputAction.ID, '').run(); + } +}); + +CommandsRegistry.registerCommand(searchActions.FindInFolderAction.ID, searchActions.findInFolderCommand); + +class ExplorerViewerActionContributor extends ActionBarContributor { + private _instantiationService: IInstantiationService; + private _contextService: IWorkspaceContextService; + + constructor( @IInstantiationService instantiationService: IInstantiationService, @IWorkspaceContextService contextService: IWorkspaceContextService) { + super(); + + this._instantiationService = instantiationService; + this._contextService = contextService; + } + + public hasSecondaryActions(context: any): boolean { + let element = context.element; + + // Contribute only on file resources and model (context menu for multi root) + if (element instanceof Model) { + return true; + } + + let fileResource = explorerItemToFileResource(element); + if (!fileResource) { + return false; + } + + return fileResource.isDirectory; + } + + public getSecondaryActions(context: any): IAction[] { + let actions: IAction[] = []; + + if (this.hasSecondaryActions(context)) { + let action: Action; + if (context.element instanceof Model) { + action = this._instantiationService.createInstance(searchActions.FindInWorkspaceAction); + } else { + let fileResource = explorerItemToFileResource(context.element); + action = this._instantiationService.createInstance(searchActions.FindInFolderAction, fileResource.resource); + } + + action.order = 55; + actions.push(action); + + actions.push(new Separator('', 56)); + } + + return actions; + } +} + +const ACTION_ID = 'workbench.action.showAllSymbols'; +const ACTION_LABEL = nls.localize('showTriggerActions', "Go to Symbol in Workspace..."); +const ALL_SYMBOLS_PREFIX = '#'; + +class ShowAllSymbolsAction extends Action { + + constructor( + actionId: string, actionLabel: string, + @IQuickOpenService private quickOpenService: IQuickOpenService, + @ICodeEditorService private editorService: ICodeEditorService) { + super(actionId, actionLabel); + this.enabled = !!this.quickOpenService; + } + + public run(context?: any): TPromise { + + let prefix = ALL_SYMBOLS_PREFIX; + let inputSelection: { start: number; end: number; } = void 0; + let editor = this.editorService.getFocusedCodeEditor(); + const word = editor && getSelectionSearchString(editor); + if (word) { + prefix = prefix + word; + inputSelection = { start: 1, end: word.length + 1 }; + } + + this.quickOpenService.show(prefix, { inputSelection }); + + return TPromise.as(null); + } +} + +// Register Viewlet +Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( + 'vs/workbench/parts/search/browser/searchViewlet', + 'SearchViewlet', + Constants.VIEWLET_ID, + nls.localize('name', "Search"), + 'search', + 10 +)); + +// Actions +const registry = Registry.as(ActionExtensions.WorkbenchActions); + +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.OpenSearchViewletAction, Constants.VIEWLET_ID, nls.localize('showSearchViewlet', "Show Search"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, + Constants.SearchViewletVisibleKey.toNegated()), 'View: Show Search', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusActiveEditorAction, Constants.FocusActiveEditorActionId, '', { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, + ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.SearchInputBoxFocusedKey)), ''); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FindInFilesAction, Constants.FindInFilesActionId, nls.localize('findInFiles', "Find in Files"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, + Constants.SearchInputBoxFocusedKey.toNegated()), 'Find in Files'); + +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusNextSearchResultAction, searchActions.FocusNextSearchResultAction.ID, searchActions.FocusNextSearchResultAction.LABEL, { primary: KeyCode.F4 }), ''); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusPreviousSearchResultAction, searchActions.FocusPreviousSearchResultAction.ID, searchActions.FocusPreviousSearchResultAction.LABEL, { primary: KeyMod.Shift | KeyCode.F4 }), ''); + +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ReplaceInFilesAction, searchActions.ReplaceInFilesAction.ID, searchActions.ReplaceInFilesAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Replace in Files'); + +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ToggleCaseSensitiveAction, Constants.ToggleCaseSensitiveActionId, '', ToggleCaseSensitiveKeybinding, ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.SearchInputBoxFocusedKey)), ''); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ToggleWholeWordAction, Constants.ToggleWholeWordActionId, '', ToggleWholeWordKeybinding, ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.SearchInputBoxFocusedKey)), ''); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ToggleRegexAction, Constants.ToggleRegexActionId, '', ToggleRegexKeybinding, ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.SearchInputBoxFocusedKey)), ''); + +// Terms navigation actions +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowNextSearchTermAction, searchActions.ShowNextSearchTermAction.ID, searchActions.ShowNextSearchTermAction.LABEL, ShowNextFindTermKeybinding, searchActions.ShowNextSearchTermAction.CONTEXT_KEY_EXPRESSION), 'Show Next Search Term', 'Search'); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowPreviousSearchTermAction, searchActions.ShowPreviousSearchTermAction.ID, searchActions.ShowPreviousSearchTermAction.LABEL, ShowPreviousFindTermKeybinding, searchActions.ShowPreviousSearchTermAction.CONTEXT_KEY_EXPRESSION), 'Show Previous Search Term', 'Search'); + +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowNextSearchIncludeAction, searchActions.ShowNextSearchIncludeAction.ID, searchActions.ShowNextSearchIncludeAction.LABEL, ShowNextFindTermKeybinding, searchActions.ShowNextSearchIncludeAction.CONTEXT_KEY_EXPRESSION), 'Show Next Search Include Pattern', 'Search'); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowPreviousSearchIncludeAction, searchActions.ShowPreviousSearchIncludeAction.ID, searchActions.ShowPreviousSearchIncludeAction.LABEL, ShowPreviousFindTermKeybinding, searchActions.ShowPreviousSearchIncludeAction.CONTEXT_KEY_EXPRESSION), 'Show Previous Search Include Pattern', 'Search'); + +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowNextSearchExcludeAction, searchActions.ShowNextSearchExcludeAction.ID, searchActions.ShowNextSearchExcludeAction.LABEL, ShowNextFindTermKeybinding, searchActions.ShowNextSearchExcludeAction.CONTEXT_KEY_EXPRESSION), 'Show Next Search Exclude Pattern', 'Search'); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ShowPreviousSearchExcludeAction, searchActions.ShowPreviousSearchExcludeAction.ID, searchActions.ShowPreviousSearchExcludeAction.LABEL, ShowPreviousFindTermKeybinding, searchActions.ShowPreviousSearchExcludeAction.CONTEXT_KEY_EXPRESSION), 'Show Previous Search Exclude Pattern', 'Search'); + +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllSymbolsAction, ACTION_ID, ACTION_LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), 'Go to Symbol in Workspace...'); + +// Contribute to Explorer Viewer +const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); +actionBarRegistry.registerActionBarContributor(Scope.VIEWER, ExplorerViewerActionContributor); + +// Register Quick Open Handler +Registry.as(QuickOpenExtensions.Quickopen).registerDefaultQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/search/browser/openAnythingHandler', + 'OpenAnythingHandler', + '', + defaultQuickOpenContextKey, + nls.localize('openAnythingHandlerDescription', "Go to File") + ) +); + +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/search/browser/openAnythingHandler', + 'OpenSymbolHandler', + ALL_SYMBOLS_PREFIX, + 'inWorkspaceSymbolsPicker', + [ + { + prefix: ALL_SYMBOLS_PREFIX, + needsEditor: false, + description: nls.localize('openSymbolDescriptionNormal', "Go to Symbol in Workspace") + } + ] + ) +); + +// Search output channel +const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); +outputChannelRegistry.registerChannel('search', nls.localize('searchOutputChannelTitle', "Search")); + +// Configuration +const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); +configurationRegistry.registerConfiguration({ + 'id': 'search', + 'order': 13, + 'title': nls.localize('searchConfigurationTitle', "Search"), + 'type': 'object', + 'properties': { + 'search.exclude': { + 'type': 'object', + 'description': nls.localize('exclude', "Configure glob patterns for excluding files and folders in searches. Inherits all glob patterns from the files.exclude setting."), + 'default': { '**/node_modules': true, '**/bower_components': true }, + 'additionalProperties': { + 'anyOf': [ + { + 'type': 'boolean', + 'description': nls.localize('exclude.boolean', "The glob pattern to match file paths against. Set to true or false to enable or disable the pattern."), + }, + { + 'type': 'object', + 'properties': { + 'when': { + 'type': 'string', // expression ({ "**/*.js": { "when": "$(basename).js" } }) + 'pattern': '\\w*\\$\\(basename\\)\\w*', + 'default': '$(basename).ext', + 'description': nls.localize('exclude.when', 'Additional check on the siblings of a matching file. Use $(basename) as variable for the matching file name.') + } + } + } + ] + }, + 'scope': ConfigurationScope.RESOURCE + }, + 'search.useRipgrep': { + 'type': 'boolean', + 'description': nls.localize('useRipgrep', "Controls whether to use ripgrep in text search"), + 'default': true + }, + 'search.useIgnoreFilesByDefault': { + 'type': 'boolean', + 'description': nls.localize('useIgnoreFilesByDefault', "Controls whether to use .gitignore and .ignore files by default when searching in a new workspace."), + 'default': false + }, + 'search.quickOpen.includeSymbols': { + 'type': 'boolean', + 'description': nls.localize('search.quickOpen.includeSymbols', "Configure to include results from a global symbol search in the file results for Quick Open."), + 'default': false + } + } +}); diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts new file mode 100644 index 0000000000..0445f3cef1 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -0,0 +1,642 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import DOM = require('vs/base/browser/dom'); +import errors = require('vs/base/common/errors'); +import paths = require('vs/base/common/paths'); +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { Action } from 'vs/base/common/actions'; +import { ToggleViewletAction } from 'vs/workbench/browser/viewlet'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { INavigator } from 'vs/base/common/iterator'; +import { SearchViewlet } from 'vs/workbench/parts/search/browser/searchViewlet'; +import { Match, FileMatch, FileMatchOrMatch, FolderMatch, RenderableMatch } from 'vs/workbench/parts/search/common/searchModel'; +import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; +import * as Constants from 'vs/workbench/parts/search/common/constants'; +import { CollapseAllAction as TreeCollapseAction } from 'vs/base/parts/tree/browser/treeDefaults'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ResolvedKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { toResource } from 'vs/workbench/common/editor'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { explorerItemToFileResource } from 'vs/workbench/parts/files/common/files'; +import { OS } from 'vs/base/common/platform'; +import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; + +export function isSearchViewletFocused(viewletService: IViewletService): boolean { + let activeViewlet = viewletService.getActiveViewlet(); + let activeElement = document.activeElement; + return activeViewlet && activeViewlet.getId() === Constants.VIEWLET_ID && activeElement && DOM.isAncestor(activeElement, (activeViewlet).getContainer().getHTMLElement()); +} + +export function appendKeyBindingLabel(label: string, keyBinding: number | ResolvedKeybinding, keyBindingService2: IKeybindingService): string { + if (typeof keyBinding === 'number') { + const resolvedKeybindings = keyBindingService2.resolveKeybinding(createKeybinding(keyBinding, OS)); + return doAppendKeyBindingLabel(label, resolvedKeybindings.length > 0 ? resolvedKeybindings[0] : null); + } else { + return doAppendKeyBindingLabel(label, keyBinding); + } +} + +function doAppendKeyBindingLabel(label: string, keyBinding: ResolvedKeybinding): string { + return keyBinding ? label + ' (' + keyBinding.getLabel() + ')' : label; +} + +export class ToggleCaseSensitiveAction extends Action { + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + let searchViewlet = this.viewletService.getActiveViewlet(); + searchViewlet.toggleCaseSensitive(); + return TPromise.as(null); + } +} + +export class ToggleWholeWordAction extends Action { + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + let searchViewlet = this.viewletService.getActiveViewlet(); + searchViewlet.toggleWholeWords(); + return TPromise.as(null); + } +} + +export class ToggleRegexAction extends Action { + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + let searchViewlet = this.viewletService.getActiveViewlet(); + searchViewlet.toggleRegex(); + return TPromise.as(null); + } +} + +export class ShowNextSearchIncludeAction extends Action { + + public static ID = 'search.history.showNextIncludePattern'; + public static LABEL = nls.localize('nextSearchIncludePattern', "Show Next Search Include Pattern"); + public static CONTEXT_KEY_EXPRESSION: ContextKeyExpr = ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.PatternIncludesFocusedKey); + + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IContextKeyService private contextKeyService: IContextKeyService + ) { + super(id, label); + this.enabled = this.contextKeyService.contextMatchesRules(ShowNextSearchIncludeAction.CONTEXT_KEY_EXPRESSION); + } + + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchIncludePattern; + searchAndReplaceWidget.showNextTerm(); + return TPromise.as(null); + } +} + +export class ShowPreviousSearchIncludeAction extends Action { + + public static ID = 'search.history.showPreviousIncludePattern'; + public static LABEL = nls.localize('previousSearchIncludePattern', "Show Previous Search Include Pattern"); + public static CONTEXT_KEY_EXPRESSION: ContextKeyExpr = ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.PatternIncludesFocusedKey); + + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IContextKeyService private contextKeyService: IContextKeyService + ) { + super(id, label); + this.enabled = this.contextKeyService.contextMatchesRules(ShowPreviousSearchIncludeAction.CONTEXT_KEY_EXPRESSION); + } + + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchIncludePattern; + searchAndReplaceWidget.showPreviousTerm(); + return TPromise.as(null); + } +} + +export class ShowNextSearchExcludeAction extends Action { + + public static ID = 'search.history.showNextExcludePattern'; + public static LABEL = nls.localize('nextSearchExcludePattern', "Show Next Search Exclude Pattern"); + public static CONTEXT_KEY_EXPRESSION: ContextKeyExpr = ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.PatternExcludesFocusedKey); + + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IContextKeyService private contextKeyService: IContextKeyService + ) { + super(id, label); + this.enabled = this.contextKeyService.contextMatchesRules(ShowNextSearchExcludeAction.CONTEXT_KEY_EXPRESSION); + } + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchExcludePattern; + searchAndReplaceWidget.showNextTerm(); + return TPromise.as(null); + } +} + +export class ShowPreviousSearchExcludeAction extends Action { + + public static ID = 'search.history.showPreviousExcludePattern'; + public static LABEL = nls.localize('previousSearchExcludePattern', "Show Previous Search Exclude Pattern"); + public static CONTEXT_KEY_EXPRESSION: ContextKeyExpr = ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.PatternExcludesFocusedKey); + + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IContextKeyService private contextKeyService: IContextKeyService + ) { + super(id, label); + this.enabled = this.contextKeyService.contextMatchesRules(ShowPreviousSearchExcludeAction.CONTEXT_KEY_EXPRESSION); + } + + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchExcludePattern; + searchAndReplaceWidget.showPreviousTerm(); + return TPromise.as(null); + } +} + +export class ShowNextSearchTermAction extends Action { + + public static ID = 'search.history.showNext'; + public static LABEL = nls.localize('nextSearchTerm', "Show Next Search Term"); + public static CONTEXT_KEY_EXPRESSION: ContextKeyExpr = ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.SearchInputBoxFocusedKey); + + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IContextKeyService private contextKeyService: IContextKeyService + ) { + super(id, label); + this.enabled = this.contextKeyService.contextMatchesRules(ShowNextSearchTermAction.CONTEXT_KEY_EXPRESSION); + + } + + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchAndReplaceWidget; + searchAndReplaceWidget.showNextSearchTerm(); + return TPromise.as(null); + } +} + +export class ShowPreviousSearchTermAction extends Action { + + public static ID = 'search.history.showPrevious'; + public static LABEL = nls.localize('previousSearchTerm', "Show Previous Search Term"); + public static CONTEXT_KEY_EXPRESSION: ContextKeyExpr = ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.SearchInputBoxFocusedKey); + + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IContextKeyService private contextKeyService: IContextKeyService + ) { + super(id, label); + this.enabled = this.contextKeyService.contextMatchesRules(ShowPreviousSearchTermAction.CONTEXT_KEY_EXPRESSION); + } + + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchAndReplaceWidget; + searchAndReplaceWidget.showPreviousSearchTerm(); + return TPromise.as(null); + } +} + +export class FocusNextInputAction extends Action { + + public static ID = 'search.focus.nextInputBox'; + public static LABEL = nls.localize('focusNextInputBox', "Focus Next Input Box"); + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + (this.viewletService.getActiveViewlet()).focusNextInputBox(); + return TPromise.as(null); + } +} + +export class FocusPreviousInputAction extends Action { + + public static ID = 'search.focus.previousInputBox'; + public static LABEL = nls.localize('focusPreviousInputBox', "Focus Previous Input Box"); + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + (this.viewletService.getActiveViewlet()).focusPreviousInputBox(); + return TPromise.as(null); + } +} + +export class OpenSearchViewletAction extends ToggleViewletAction { + + constructor(id: string, label: string, @IViewletService viewletService: IViewletService, @IWorkbenchEditorService editorService: IWorkbenchEditorService) { + super(id, label, Constants.VIEWLET_ID, viewletService, editorService); + } + + public run(): TPromise { + const activeViewlet = this.viewletService.getActiveViewlet(); + const searchViewletWasOpen = activeViewlet && activeViewlet.getId() === Constants.VIEWLET_ID; + + return super.run().then(() => { + if (!searchViewletWasOpen) { + // Get the search viewlet and ensure that 'replace' is collapsed + const searchViewlet = this.viewletService.getActiveViewlet(); + if (searchViewlet && searchViewlet.getId() === Constants.VIEWLET_ID) { + const searchAndReplaceWidget = (searchViewlet).searchAndReplaceWidget; + searchAndReplaceWidget.toggleReplace(false); + } + } + }); + } + +} + +export class FocusActiveEditorAction extends Action { + + constructor(id: string, label: string, @IWorkbenchEditorService private editorService: IWorkbenchEditorService) { + super(id, label); + } + + public run(): TPromise { + let editor = this.editorService.getActiveEditor(); + if (editor) { + editor.focus(); + } + return TPromise.as(true); + } + +} + +export abstract class FindOrReplaceInFilesAction extends Action { + + constructor(id: string, label: string, private viewletService: IViewletService, + private expandSearchReplaceWidget: boolean, private selectWidgetText, private focusReplace) { + super(id, label); + } + + public run(): TPromise { + const viewlet = this.viewletService.getActiveViewlet(); + const searchViewletWasOpen = viewlet && viewlet.getId() === Constants.VIEWLET_ID; + return this.viewletService.openViewlet(Constants.VIEWLET_ID, true).then((viewlet) => { + if (!searchViewletWasOpen || this.expandSearchReplaceWidget) { + const searchAndReplaceWidget = (viewlet).searchAndReplaceWidget; + searchAndReplaceWidget.toggleReplace(this.expandSearchReplaceWidget); + searchAndReplaceWidget.focus(this.selectWidgetText, this.focusReplace); + } + }); + } +} + +export class FindInFilesAction extends FindOrReplaceInFilesAction { + + constructor(id: string, label: string, @IViewletService viewletService: IViewletService) { + super(id, label, viewletService, /*expandSearchReplaceWidget=*/false, /*selectWidgetText=*/true, /*focusReplace=*/false); + } +} + +export class ReplaceInFilesAction extends FindOrReplaceInFilesAction { + + public static ID = 'workbench.action.replaceInFiles'; + public static LABEL = nls.localize('replaceInFiles', "Replace in Files"); + + constructor(id: string, label: string, @IViewletService viewletService: IViewletService) { + super(id, label, viewletService, /*expandSearchReplaceWidget=*/true, /*selectWidgetText=*/false, /*focusReplace=*/true); + } +} + +export class CloseReplaceAction extends Action { + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchAndReplaceWidget; + searchAndReplaceWidget.toggleReplace(false); + searchAndReplaceWidget.focus(); + return TPromise.as(null); + } +} + +export class FindInWorkspaceAction extends Action { + + public static ID = 'filesExplorer.findInWorkspace'; + + constructor( @IViewletService private viewletService: IViewletService) { + super(FindInWorkspaceAction.ID, nls.localize('findInWorkspace', "Find in Workspace...")); + } + + public run(event?: any): TPromise { + return this.viewletService.openViewlet(Constants.VIEWLET_ID, true).then((viewlet: SearchViewlet) => { + viewlet.searchInFolder(null); + }); + } +} + +export class FindInFolderAction extends Action { + + public static ID = 'filesExplorer.findInFolder'; + + private resource: URI; + + constructor(resource: URI, @IInstantiationService private instantiationService: IInstantiationService) { + super(FindInFolderAction.ID, nls.localize('findInFolder', "Find in Folder...")); + + this.resource = resource; + } + + public run(event?: any): TPromise { + return this.instantiationService.invokeFunction.apply(this.instantiationService, [findInFolderCommand, this.resource]); + } +} + +export const findInFolderCommand = (accessor: ServicesAccessor, resource?: URI) => { + const listService = accessor.get(IListService); + const viewletService = accessor.get(IViewletService); + + if (!URI.isUri(resource)) { + const focused = listService.getFocused() ? listService.getFocused().getFocus() : void 0; + if (focused) { + const file = explorerItemToFileResource(focused); + if (file) { + resource = file.isDirectory ? file.resource : URI.file(paths.dirname(file.resource.fsPath)); + } + } + } + + viewletService.openViewlet(Constants.VIEWLET_ID, true).then((viewlet: SearchViewlet) => { + if (resource) { + viewlet.searchInFolder(resource); + } + }).done(null, errors.onUnexpectedError); +}; + +export class RefreshAction extends Action { + + constructor(private viewlet: SearchViewlet) { + super('refresh'); + + this.label = nls.localize('RefreshAction.label', "Refresh"); + this.enabled = false; + this.class = 'search-action refresh'; + } + + public run(): TPromise { + this.viewlet.onQueryChanged(true); + + return TPromise.as(null); + } +} + +export class CollapseAllAction extends TreeCollapseAction { + + constructor(viewlet: SearchViewlet) { + super(viewlet.getControl(), false); + this.class = 'search-action collapse'; + } +} + +export class ClearSearchResultsAction extends Action { + + constructor(private viewlet: SearchViewlet) { + super('clearSearchResults'); + + this.label = nls.localize('ClearSearchResultsAction.label', "Clear Search Results"); + this.enabled = false; + this.class = 'search-action clear-search-results'; + } + + public run(): TPromise { + this.viewlet.clearSearchResults(); + + return TPromise.as(null); + } +} + +export class FocusNextSearchResultAction extends Action { + public static ID = 'search.action.focusNextSearchResult'; + public static LABEL = nls.localize('FocusNextSearchResult.label', "Focus Next Search Result"); + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + return this.viewletService.openViewlet(Constants.VIEWLET_ID).then((searchViewlet: SearchViewlet) => { + searchViewlet.selectNextMatch(); + }); + } +} + +export class FocusPreviousSearchResultAction extends Action { + public static ID = 'search.action.focusPreviousSearchResult'; + public static LABEL = nls.localize('FocusPreviousSearchResult.label', "Focus Previous Search Result"); + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + return this.viewletService.openViewlet(Constants.VIEWLET_ID).then((searchViewlet: SearchViewlet) => { + searchViewlet.selectPreviousMatch(); + }); + } +} + +export abstract class AbstractSearchAndReplaceAction extends Action { + + /** + * Returns element to focus after removing the given element + */ + public getElementToFocusAfterRemoved(viewer: ITree, elementToBeRemoved: RenderableMatch): RenderableMatch { + let elementToFocus = this.getNextElementAfterRemoved(viewer, elementToBeRemoved); + if (!elementToFocus) { + elementToFocus = this.getPreviousElementAfterRemoved(viewer, elementToBeRemoved); + } + return elementToFocus; + } + + public getNextElementAfterRemoved(viewer: ITree, element: RenderableMatch): RenderableMatch { + let navigator: INavigator = this.getNavigatorAt(element, viewer); + if (element instanceof FolderMatch) { + // If file match is removed then next element is the next file match + while (!!navigator.next() && !(navigator.current() instanceof FolderMatch)) { }; + } else if (element instanceof FileMatch) { + // If file match is removed then next element is the next file match + while (!!navigator.next() && !(navigator.current() instanceof FileMatch)) { }; + } else { + navigator.next(); + } + return navigator.current(); + } + + public getPreviousElementAfterRemoved(viewer: ITree, element: RenderableMatch): RenderableMatch { + let navigator: INavigator = this.getNavigatorAt(element, viewer); + let previousElement = navigator.previous(); + if (element instanceof Match && element.parent().matches().length === 1) { + // If this is the only match, then the file match is also removed + // Hence take the previous element to file match + previousElement = navigator.previous(); + } + return previousElement; + } + + private getNavigatorAt(element: RenderableMatch, viewer: ITree): INavigator { + let navigator: INavigator = viewer.getNavigator(); + while (navigator.current() !== element && !!navigator.next()) { } + return navigator; + } +} + +export class RemoveAction extends AbstractSearchAndReplaceAction { + + constructor(private viewer: ITree, private element: RenderableMatch) { + super('remove', nls.localize('RemoveAction.label', "Remove"), 'action-remove'); + } + + public run(): TPromise { + let nextFocusElement = this.getElementToFocusAfterRemoved(this.viewer, this.element); + if (nextFocusElement) { + this.viewer.setFocus(nextFocusElement); + } + + let elementToRefresh: any; + const element = this.element; + if (element instanceof FolderMatch) { + let parent = element.parent(); + parent.remove(element); + elementToRefresh = parent; + } else if (element instanceof FileMatch) { + let parent = element.parent(); + parent.remove(element); + elementToRefresh = parent; + } else if (element instanceof Match) { + let parent = element.parent(); + parent.remove(element); + elementToRefresh = parent.count() === 0 ? parent.parent() : parent; + } + + this.viewer.DOMFocus(); + return this.viewer.refresh(elementToRefresh); + } + +} + +export class ReplaceAllAction extends AbstractSearchAndReplaceAction { + + constructor(private viewer: ITree, private fileMatch: FileMatch, private viewlet: SearchViewlet, + @IReplaceService private replaceService: IReplaceService, + @IKeybindingService keyBindingService: IKeybindingService, + @ITelemetryService private telemetryService: ITelemetryService) { + super(Constants.ReplaceAllInFileActionId, appendKeyBindingLabel(nls.localize('file.replaceAll.label', "Replace All"), keyBindingService.lookupKeybinding(Constants.ReplaceAllInFileActionId), keyBindingService), 'action-replace-all'); + } + + public run(): TPromise { + this.telemetryService.publicLog('replaceAll.action.selected'); + let nextFocusElement = this.getElementToFocusAfterRemoved(this.viewer, this.fileMatch); + return this.fileMatch.parent().replace(this.fileMatch).then(() => { + if (nextFocusElement) { + this.viewer.setFocus(nextFocusElement); + } + this.viewer.DOMFocus(); + this.viewlet.open(this.fileMatch, true); + }); + } +} + +export class ReplaceAction extends AbstractSearchAndReplaceAction { + + constructor(private viewer: ITree, private element: Match, private viewlet: SearchViewlet, + @IReplaceService private replaceService: IReplaceService, + @IKeybindingService keyBindingService: IKeybindingService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @ITelemetryService private telemetryService: ITelemetryService) { + super(Constants.ReplaceActionId, appendKeyBindingLabel(nls.localize('match.replace.label', "Replace"), keyBindingService.lookupKeybinding(Constants.ReplaceActionId), keyBindingService), 'action-replace'); + } + + public run(): TPromise { + this.enabled = false; + this.telemetryService.publicLog('replace.action.selected'); + + return this.element.parent().replace(this.element).then(() => { + let elementToFocus = this.getElementToFocusAfterReplace(); + if (elementToFocus) { + this.viewer.setFocus(elementToFocus); + } + let elementToShowReplacePreview = this.getElementToShowReplacePreview(elementToFocus); + this.viewer.DOMFocus(); + if (!elementToShowReplacePreview || this.hasToOpenFile()) { + this.viewlet.open(this.element, true); + } else { + this.replaceService.openReplacePreview(elementToShowReplacePreview, true); + } + }); + } + + private getElementToFocusAfterReplace(): Match { + let navigator: INavigator = this.viewer.getNavigator(); + let fileMatched = false; + let elementToFocus = null; + do { + elementToFocus = navigator.current(); + if (elementToFocus instanceof Match) { + if (elementToFocus.parent().id() === this.element.parent().id()) { + fileMatched = true; + if (this.element.range().getStartPosition().isBeforeOrEqual((elementToFocus).range().getStartPosition())) { + // Closest next match in the same file + break; + } + } else if (fileMatched) { + // First match in the next file (if expanded) + break; + } + } else if (fileMatched) { + if (!this.viewer.isExpanded(elementToFocus)) { + // Next file match (if collapsed) + break; + } + } + } while (!!navigator.next()); + return elementToFocus; + } + + private getElementToShowReplacePreview(elementToFocus: FileMatchOrMatch): Match { + if (this.hasSameParent(elementToFocus)) { + return elementToFocus; + } + let previousElement = this.getPreviousElementAfterRemoved(this.viewer, this.element); + if (this.hasSameParent(previousElement)) { + return previousElement; + } + return null; + } + + private hasSameParent(element: RenderableMatch): boolean { + return element && element instanceof Match && element.parent().resource() === this.element.parent().resource(); + } + + private hasToOpenFile(): boolean { + const file = toResource(this.editorService.getActiveEditorInput(), { filter: 'file' }); + if (file) { + return paths.isEqual(file.fsPath, this.element.parent().resource().fsPath); + } + return false; + } +} diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts new file mode 100644 index 0000000000..98b7cf604f --- /dev/null +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -0,0 +1,329 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as paths from 'vs/base/common/paths'; +import * as DOM from 'vs/base/browser/dom'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { FileLabel } from 'vs/workbench/browser/labels'; +import { ITree, IDataSource, ISorter, IAccessibilityProvider, IFilter, IRenderer } from 'vs/base/parts/tree/browser/tree'; +import { Match, SearchResult, FileMatch, FileMatchOrMatch, SearchModel, FolderMatch } from 'vs/workbench/parts/search/common/searchModel'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { Range } from 'vs/editor/common/core/range'; +import { SearchViewlet } from 'vs/workbench/parts/search/browser/searchViewlet'; +import { RemoveAction, ReplaceAllAction, ReplaceAction } from 'vs/workbench/parts/search/browser/searchActions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { getPathLabel } from 'vs/base/common/labels'; +import { FileKind } from 'vs/platform/files/common/files'; + +export class SearchDataSource implements IDataSource { + + private static AUTOEXPAND_CHILD_LIMIT = 10; + + constructor(private includeFolderMatch: boolean = true) { } + + public getId(tree: ITree, element: any): string { + if (element instanceof FolderMatch) { + return element.id(); + } + + if (element instanceof FileMatch) { + return element.id(); + } + + if (element instanceof Match) { + return element.id(); + } + + return 'root'; + } + + private _getChildren(element: any): any[] { + if (element instanceof FileMatch) { + return element.matches(); + } else if (element instanceof FolderMatch) { + return element.matches(); + } else if (element instanceof SearchResult) { + const folderMatches = element.folderMatches(); + return folderMatches.length > 2 ? // "Other files" + workspace folder = 2 + folderMatches.filter(fm => !fm.isEmpty()) : + element.matches(); + } + + return []; + } + + public getChildren(tree: ITree, element: any): TPromise { + return TPromise.as(this._getChildren(element)); + } + + public hasChildren(tree: ITree, element: any): boolean { + return element instanceof FileMatch || element instanceof FolderMatch || element instanceof SearchResult; + } + + public getParent(tree: ITree, element: any): TPromise { + let value: any = null; + + if (element instanceof Match) { + value = element.parent(); + } else if (element instanceof FileMatch) { + value = this.includeFolderMatch ? element.parent() : element.parent().parent(); + } else if (element instanceof FolderMatch) { + value = element.parent(); + } + + return TPromise.as(value); + } + + public shouldAutoexpand(tree: ITree, element: any): boolean { + const numChildren = this._getChildren(element).length; + if (numChildren <= 0) { + return false; + } + return numChildren < SearchDataSource.AUTOEXPAND_CHILD_LIMIT || element instanceof FolderMatch; + } +} + +export class SearchSorter implements ISorter { + + public compare(tree: ITree, elementA: FileMatchOrMatch, elementB: FileMatchOrMatch): number { + if (elementA instanceof FolderMatch && elementB instanceof FolderMatch) { + return elementA.index() - elementB.index(); + } + + if (elementA instanceof FileMatch && elementB instanceof FileMatch) { + return elementA.resource().fsPath.localeCompare(elementB.resource().fsPath) || elementA.name().localeCompare(elementB.name()); + } + + if (elementA instanceof Match && elementB instanceof Match) { + return Range.compareRangesUsingStarts(elementA.range(), elementB.range()); + } + + return undefined; + } +} + +interface IFolderMatchTemplate { + label: FileLabel; + badge: CountBadge; + actions: ActionBar; +} + +interface IFileMatchTemplate { + label: FileLabel; + badge: CountBadge; + actions: ActionBar; +} + +interface IMatchTemplate { + parent: HTMLElement; + before: HTMLElement; + match: HTMLElement; + replace: HTMLElement; + after: HTMLElement; + actions: ActionBar; +} + +export class SearchRenderer extends Disposable implements IRenderer { + + private static FOLDER_MATCH_TEMPLATE_ID = 'folderMatch'; + private static FILE_MATCH_TEMPLATE_ID = 'fileMatch'; + private static MATCH_TEMPLATE_ID = 'match'; + + constructor( + actionRunner: IActionRunner, + private viewlet: SearchViewlet, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IInstantiationService private instantiationService: IInstantiationService, + @IThemeService private themeService: IThemeService + ) { + super(); + } + + public getHeight(tree: ITree, element: any): number { + return 22; + } + + public getTemplateId(tree: ITree, element: any): string { + if (element instanceof FolderMatch) { + return SearchRenderer.FOLDER_MATCH_TEMPLATE_ID; + } else if (element instanceof FileMatch) { + return SearchRenderer.FILE_MATCH_TEMPLATE_ID; + } else if (element instanceof Match) { + return SearchRenderer.MATCH_TEMPLATE_ID; + } + return null; + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + if (templateId === SearchRenderer.FOLDER_MATCH_TEMPLATE_ID) { + return this.renderFolderMatchTemplate(tree, templateId, container); + } + + if (templateId === SearchRenderer.FILE_MATCH_TEMPLATE_ID) { + return this.renderFileMatchTemplate(tree, templateId, container); + } + + if (templateId === SearchRenderer.MATCH_TEMPLATE_ID) { + return this.renderMatchTemplate(tree, templateId, container); + } + + return null; + } + + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + if (SearchRenderer.FOLDER_MATCH_TEMPLATE_ID === templateId) { + this.renderFolderMatch(tree, element, templateData); + } else if (SearchRenderer.FILE_MATCH_TEMPLATE_ID === templateId) { + this.renderFileMatch(tree, element, templateData); + } else if (SearchRenderer.MATCH_TEMPLATE_ID === templateId) { + this.renderMatch(tree, element, templateData); + } + } + + private renderFolderMatchTemplate(tree: ITree, templateId: string, container: HTMLElement): IFolderMatchTemplate { + let folderMatchElement = DOM.append(container, DOM.$('.foldermatch')); + const label = this.instantiationService.createInstance(FileLabel, folderMatchElement, void 0); + const badge = new CountBadge(DOM.append(folderMatchElement, DOM.$('.badge'))); + this._register(attachBadgeStyler(badge, this.themeService)); + const actions = new ActionBar(folderMatchElement, { animated: false }); + return { label, badge, actions }; + } + + private renderFileMatchTemplate(tree: ITree, templateId: string, container: HTMLElement): IFileMatchTemplate { + let fileMatchElement = DOM.append(container, DOM.$('.filematch')); + const label = this.instantiationService.createInstance(FileLabel, fileMatchElement, void 0); + const badge = new CountBadge(DOM.append(fileMatchElement, DOM.$('.badge'))); + this._register(attachBadgeStyler(badge, this.themeService)); + const actions = new ActionBar(fileMatchElement, { animated: false }); + return { label, badge, actions }; + } + + private renderMatchTemplate(tree: ITree, templateId: string, container: HTMLElement): IMatchTemplate { + DOM.addClass(container, 'linematch'); + + const parent = DOM.append(container, DOM.$('a.plain.match')); + const before = DOM.append(parent, DOM.$('span')); + const match = DOM.append(parent, DOM.$('span.findInFileMatch')); + const replace = DOM.append(parent, DOM.$('span.replaceMatch')); + const after = DOM.append(parent, DOM.$('span')); + const actions = new ActionBar(container, { animated: false }); + + return { + parent, + before, + match, + replace, + after, + actions + }; + } + + private renderFolderMatch(tree: ITree, folderMatch: FolderMatch, templateData: IFolderMatchTemplate): void { + if (folderMatch.hasRoot()) { + templateData.label.setFile(folderMatch.resource(), { fileKind: FileKind.ROOT_FOLDER }); + } else { + templateData.label.setValue(nls.localize('searchFolderMatch.other.label', "Other files")); + } + let count = folderMatch.fileCount(); + templateData.badge.setCount(count); + templateData.badge.setTitleFormat(count > 1 ? nls.localize('searchFileMatches', "{0} files found", count) : nls.localize('searchFileMatch', "{0} file found", count)); + + templateData.actions.clear(); + templateData.actions.push([new RemoveAction(tree, folderMatch)], { icon: true, label: false }); + } + + private renderFileMatch(tree: ITree, fileMatch: FileMatch, templateData: IFileMatchTemplate): void { + const folderMatch = fileMatch.parent(); + const root = folderMatch.hasRoot() ? folderMatch.resource() : undefined; + templateData.label.setFile(fileMatch.resource(), { root }); + let count = fileMatch.count(); + templateData.badge.setCount(count); + templateData.badge.setTitleFormat(count > 1 ? nls.localize('searchMatches', "{0} matches found", count) : nls.localize('searchMatch', "{0} match found", count)); + + let input = tree.getInput(); + templateData.actions.clear(); + + const actions: IAction[] = []; + if (input.searchModel.isReplaceActive() && count > 0) { + actions.push(this.instantiationService.createInstance(ReplaceAllAction, tree, fileMatch, this.viewlet)); + } + actions.push(new RemoveAction(tree, fileMatch)); + templateData.actions.push(actions, { icon: true, label: false }); + } + + private renderMatch(tree: ITree, match: Match, templateData: IMatchTemplate): void { + let preview = match.preview(); + const searchModel: SearchModel = (tree.getInput()).searchModel; + const replace = searchModel.isReplaceActive() && !!searchModel.replaceString; + + templateData.before.textContent = preview.before; + templateData.match.textContent = preview.inside; + DOM.toggleClass(templateData.match, 'replace', replace); + templateData.replace.textContent = replace ? match.replaceString : ''; + templateData.after.textContent = preview.after; + templateData.parent.title = (preview.before + (replace ? match.replaceString : preview.inside) + preview.after).trim().substr(0, 999); + + templateData.actions.clear(); + if (searchModel.isReplaceActive()) { + templateData.actions.push([this.instantiationService.createInstance(ReplaceAction, tree, match, this.viewlet), new RemoveAction(tree, match)], { icon: true, label: false }); + } else { + templateData.actions.push([new RemoveAction(tree, match)], { icon: true, label: false }); + } + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + if (SearchRenderer.FOLDER_MATCH_TEMPLATE_ID === templateId) { + (templateData).label.dispose(); + } + if (SearchRenderer.FILE_MATCH_TEMPLATE_ID === templateId) { + (templateData).label.dispose(); + } + } +} + +export class SearchAccessibilityProvider implements IAccessibilityProvider { + + constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) { + } + + public getAriaLabel(tree: ITree, element: FileMatchOrMatch): string { + if (element instanceof FolderMatch) { + return nls.localize('folderMatchAriaLabel', "{0} matches in folder root {1}, Search result", element.count(), element.name()); + } + + if (element instanceof FileMatch) { + const path = getPathLabel(element.resource(), this.contextService) || element.resource().fsPath; + + return nls.localize('fileMatchAriaLabel', "{0} matches in file {1} of folder {2}, Search result", element.count(), element.name(), paths.dirname(path)); + } + + if (element instanceof Match) { + const match = element; + const searchModel: SearchModel = (tree.getInput()).searchModel; + const replace = searchModel.isReplaceActive() && !!searchModel.replaceString; + const preview = match.preview(); + const range = match.range(); + if (replace) { + return nls.localize('replacePreviewResultAria', "Replace term {0} with {1} at column position {2} in line with text {3}", preview.inside, match.replaceString, range.startColumn + 1, match.text()); + } + return nls.localize('searchResultAria', "Found term {0} at column position {1} in line with text {2}", preview.inside, range.startColumn + 1, match.text()); + } + return undefined; + } +} + +export class SearchFilter implements IFilter { + + public isVisible(tree: ITree, element: any): boolean { + return !(element instanceof FileMatch || element instanceof FolderMatch) || element.matches().length > 0; + } +} diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts new file mode 100644 index 0000000000..4ce4208ba1 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -0,0 +1,1477 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/searchviewlet'; +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { Emitter, debounceEvent } from 'vs/base/common/event'; +import { ICommonCodeEditor, isCommonCodeEditor, isCommonDiffEditor } from 'vs/editor/common/editorCommon'; +import lifecycle = require('vs/base/common/lifecycle'); +import errors = require('vs/base/common/errors'); +import aria = require('vs/base/browser/ui/aria/aria'); +import env = require('vs/base/common/platform'); +import { Delayer } from 'vs/base/common/async'; +import URI from 'vs/base/common/uri'; +import strings = require('vs/base/common/strings'); +import * as paths from 'vs/base/common/paths'; +import dom = require('vs/base/browser/dom'); +import { IAction, Action } from 'vs/base/common/actions'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { Dimension, Builder, $ } from 'vs/base/browser/builder'; +import { FindInput } from 'vs/base/browser/ui/findinput/findInput'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { Scope } from 'vs/workbench/common/memento'; +import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; +import { Viewlet } from 'vs/workbench/browser/viewlet'; +import { Match, FileMatch, SearchModel, FileMatchOrMatch, IChangeEvent, ISearchWorkbenchService } from 'vs/workbench/parts/search/common/searchModel'; +import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; +import { ISearchProgressItem, ISearchComplete, ISearchQuery, IQueryOptions, ISearchConfiguration } from 'vs/platform/search/common/search'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, IConfirmation } from 'vs/platform/message/common/message'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { PatternInputWidget, ExcludePatternInputWidget } from 'vs/workbench/parts/search/browser/patternInputWidget'; +import { SearchRenderer, SearchDataSource, SearchSorter, SearchAccessibilityProvider, SearchFilter } from 'vs/workbench/parts/search/browser/searchResultsView'; +import { SearchWidget, ISearchWidgetOptions } from 'vs/workbench/parts/search/browser/searchWidget'; +import { RefreshAction, CollapseAllAction, ClearSearchResultsAction } from 'vs/workbench/parts/search/browser/searchActions'; +import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; +import Severity from 'vs/base/common/severity'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import * as Constants from 'vs/workbench/parts/search/common/constants'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { IThemeService, ITheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorFindMatchHighlight, diffInserted, diffRemoved, diffInsertedOutline, diffRemovedOutline, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import FileResultsNavigation from 'vs/workbench/parts/files/browser/fileResultsNavigation'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { IOutputService } from 'vs/workbench/parts/output/common/output'; +import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/common/search'; +import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; +import { SimpleFileResourceDragAndDrop } from 'vs/base/parts/tree/browser/treeDnd'; + +export class SearchViewlet extends Viewlet { + + private static MAX_TEXT_RESULTS = 10000; + private static SHOW_REPLACE_STORAGE_KEY = 'vs.search.show.replace'; + + private isDisposed: boolean; + + private loading: boolean; + private queryBuilder: QueryBuilder; + private viewModel: SearchModel; + private callOnModelChange: lifecycle.IDisposable[]; + + private viewletVisible: IContextKey; + private inputBoxFocused: IContextKey; + private inputPatternIncludesFocused: IContextKey; + private inputPatternExclusionsFocused: IContextKey; + private firstMatchFocused: IContextKey; + private fileMatchOrMatchFocused: IContextKey; + private fileMatchFocused: IContextKey; + private matchFocused: IContextKey; + + private actionRegistry: { [key: string]: Action; }; + private tree: ITree; + private viewletSettings: any; + private domNode: Builder; + private messages: Builder; + private searchWidgetsContainer: Builder; + private searchWidget: SearchWidget; + private size: Dimension; + private queryDetails: HTMLElement; + private inputPatternExcludes: ExcludePatternInputWidget; + private inputPatternIncludes: PatternInputWidget; + private results: Builder; + + private currentSelectedFileMatch: FileMatch; + + private selectCurrentMatchEmitter: Emitter; + private delayedRefresh: Delayer; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IFileService private fileService: IFileService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IProgressService private progressService: IProgressService, + @IMessageService private messageService: IMessageService, + @IStorageService private storageService: IStorageService, + @IContextViewService private contextViewService: IContextViewService, + @IInstantiationService private instantiationService: IInstantiationService, + @IConfigurationService private configurationService: IConfigurationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ISearchWorkbenchService private searchWorkbenchService: ISearchWorkbenchService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IReplaceService private replaceService: IReplaceService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IPreferencesService private preferencesService: IPreferencesService, + @IListService private listService: IListService, + @IThemeService protected themeService: IThemeService, + @IOutputService private outputService: IOutputService + ) { + super(Constants.VIEWLET_ID, telemetryService, themeService); + + this.viewletVisible = Constants.SearchViewletVisibleKey.bindTo(contextKeyService); + this.inputBoxFocused = Constants.InputBoxFocusedKey.bindTo(this.contextKeyService); + this.inputPatternIncludesFocused = Constants.PatternIncludesFocusedKey.bindTo(this.contextKeyService); + this.inputPatternExclusionsFocused = Constants.PatternExcludesFocusedKey.bindTo(this.contextKeyService); + this.firstMatchFocused = Constants.FirstMatchFocusKey.bindTo(contextKeyService); + this.fileMatchOrMatchFocused = Constants.FileMatchOrMatchFocusKey.bindTo(contextKeyService); + this.fileMatchFocused = Constants.FileFocusKey.bindTo(contextKeyService); + this.matchFocused = Constants.MatchFocusKey.bindTo(this.contextKeyService); + this.callOnModelChange = []; + + this.queryBuilder = this.instantiationService.createInstance(QueryBuilder); + this.viewletSettings = this.getMemento(storageService, Scope.WORKSPACE); + + this.toUnbind.push(this.fileService.onFileChanges(e => this.onFilesChanged(e))); + this.toUnbind.push(this.untitledEditorService.onDidChangeDirty(e => this.onUntitledDidChangeDirty(e))); + + this.selectCurrentMatchEmitter = new Emitter(); + debounceEvent(this.selectCurrentMatchEmitter.event, (l, e) => e, 100, /*leading=*/true) + (() => this.selectCurrentMatch()); + + this.delayedRefresh = new Delayer(250); + } + + public create(parent: Builder): TPromise { + super.create(parent); + + this.viewModel = this.searchWorkbenchService.searchModel; + let builder: Builder; + this.domNode = parent.div({ + 'class': 'search-viewlet' + }, (div) => { + builder = div; + }); + + builder.div({ 'class': ['search-widgets-container'] }, (div) => { + this.searchWidgetsContainer = div; + }); + this.createSearchWidget(this.searchWidgetsContainer); + + const filePatterns = this.viewletSettings['query.filePatterns'] || ''; + const patternExclusions = this.viewletSettings['query.folderExclusions'] || ''; + const patternExclusionsHistory = this.viewletSettings['query.folderExclusionsHistory'] || []; + const patternIncludes = this.viewletSettings['query.folderIncludes'] || ''; + const patternIncludesHistory = this.viewletSettings['query.folderIncludesHistory'] || []; + const queryDetailsExpanded = this.viewletSettings['query.queryDetailsExpanded'] || ''; + const useIgnoreFiles = typeof this.viewletSettings['query.useIgnoreFiles'] === 'boolean' ? + this.viewletSettings['query.useIgnoreFiles'] : + this.configurationService.getConfiguration().search.useIgnoreFilesByDefault; + const useExcludeSettings = true; + + this.queryDetails = this.searchWidgetsContainer.div({ 'class': ['query-details'] }, (builder) => { + builder.div({ 'class': 'more', 'tabindex': 0, 'role': 'button', 'title': nls.localize('moreSearch', "Toggle Search Details") }) + .on(dom.EventType.CLICK, (e) => { + dom.EventHelper.stop(e); + this.toggleQueryDetails(true); + }).on(dom.EventType.KEY_UP, (e: KeyboardEvent) => { + let event = new StandardKeyboardEvent(e); + + if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { + dom.EventHelper.stop(e); + this.toggleQueryDetails(); + } + }); + + //folder includes list + builder.div({ 'class': 'file-types' }, (builder) => { + let title = nls.localize('searchScope.includes', "files to include"); + builder.element('h4', { text: title }); + + this.inputPatternIncludes = new PatternInputWidget(builder.getContainer(), this.contextViewService, this.themeService, { + ariaLabel: nls.localize('label.includes', 'Search Include Patterns') + }); + + this.inputPatternIncludes.setValue(patternIncludes); + this.inputPatternIncludes.setHistory(patternIncludesHistory); + + this.inputPatternIncludes + .on(FindInput.OPTION_CHANGE, (e) => { + this.onQueryChanged(false); + }); + + this.inputPatternIncludes.onSubmit(() => this.onQueryChanged(true, true)); + this.inputPatternIncludes.onCancel(() => this.viewModel.cancelSearch()); // Cancel search without focusing the search widget + this.trackInputBox(this.inputPatternIncludes.inputFocusTracker, this.inputPatternIncludesFocused); + }); + + //pattern exclusion list + builder.div({ 'class': 'file-types' }, (builder) => { + let title = nls.localize('searchScope.excludes', "files to exclude"); + builder.element('h4', { text: title }); + + this.inputPatternExcludes = new ExcludePatternInputWidget(builder.getContainer(), this.contextViewService, this.themeService, this.telemetryService, { + ariaLabel: nls.localize('label.excludes', 'Search Exclude Patterns') + }); + + this.inputPatternExcludes.setValue(patternExclusions); + this.inputPatternExcludes.setUseIgnoreFiles(useIgnoreFiles); + this.inputPatternExcludes.setUseExcludeSettings(useExcludeSettings); + this.inputPatternExcludes.setHistory(patternExclusionsHistory); + + this.inputPatternExcludes + .on(FindInput.OPTION_CHANGE, (e) => { + this.onQueryChanged(false); + }); + + this.inputPatternExcludes.onSubmit(() => this.onQueryChanged(true, true)); + this.inputPatternExcludes.onSubmit(() => this.onQueryChanged(true, true)); + this.inputPatternExcludes.onCancel(() => this.viewModel.cancelSearch()); // Cancel search without focusing the search widget + this.trackInputBox(this.inputPatternExcludes.inputFocusTracker, this.inputPatternExclusionsFocused); + }); + }).getHTMLElement(); + + this.messages = builder.div({ 'class': 'messages' }).hide().clone(); + if (!this.contextService.hasWorkspace()) { + this.searchWithoutFolderMessage(this.clearMessage()); + } + + this.createSearchResultsView(builder); + + this.actionRegistry = {}; + let actions: Action[] = [new CollapseAllAction(this), new RefreshAction(this), new ClearSearchResultsAction(this)]; + actions.forEach((action) => { + this.actionRegistry[action.id] = action; + }); + + if (filePatterns !== '' || patternExclusions !== '' || patternIncludes !== '' || queryDetailsExpanded !== '') { + this.toggleQueryDetails(true, true, true); + } + + this.toUnbind.push(this.viewModel.searchResult.onChange((event) => this.onSearchResultsChanged(event))); + + return TPromise.as(null); + } + + public get searchAndReplaceWidget(): SearchWidget { + return this.searchWidget; + } + + public get searchIncludePattern(): PatternInputWidget { + return this.inputPatternIncludes; + } + + public get searchExcludePattern(): PatternInputWidget { + return this.inputPatternExcludes; + } + + private createSearchWidget(builder: Builder): void { + let contentPattern = this.viewletSettings['query.contentPattern'] || ''; + let isRegex = this.viewletSettings['query.regex'] === true; + let isWholeWords = this.viewletSettings['query.wholeWords'] === true; + let isCaseSensitive = this.viewletSettings['query.caseSensitive'] === true; + let searchHistory = this.viewletSettings['query.searchHistory'] || []; + + this.searchWidget = this.instantiationService.createInstance(SearchWidget, builder, { + value: contentPattern, + isRegex: isRegex, + isCaseSensitive: isCaseSensitive, + isWholeWords: isWholeWords, + history: searchHistory + }); + + if (this.storageService.getBoolean(SearchViewlet.SHOW_REPLACE_STORAGE_KEY, StorageScope.WORKSPACE, true)) { + this.searchWidget.toggleReplace(true); + } + + this.toUnbind.push(this.searchWidget); + + this.toUnbind.push(this.searchWidget.onSearchSubmit((refresh) => this.onQueryChanged(refresh))); + this.toUnbind.push(this.searchWidget.onSearchCancel(() => this.cancelSearch())); + this.toUnbind.push(this.searchWidget.searchInput.onDidOptionChange((viaKeyboard) => this.onQueryChanged(true, viaKeyboard))); + + this.toUnbind.push(this.searchWidget.onReplaceToggled(() => this.onReplaceToggled())); + this.toUnbind.push(this.searchWidget.onReplaceStateChange((state) => { + this.viewModel.replaceActive = state; + this.tree.refresh(); + })); + this.toUnbind.push(this.searchWidget.onReplaceValueChanged((value) => { + this.viewModel.replaceString = this.searchWidget.getReplaceValue(); + this.delayedRefresh.trigger(() => this.tree.refresh()); + })); + + this.toUnbind.push(this.searchWidget.onReplaceAll(() => this.replaceAll())); + this.trackInputBox(this.searchWidget.searchInputFocusTracker); + this.trackInputBox(this.searchWidget.replaceInputFocusTracker); + } + + private trackInputBox(inputFocusTracker: dom.IFocusTracker, contextKey?: IContextKey): void { + this.toUnbind.push(inputFocusTracker.addFocusListener(() => { + this.inputBoxFocused.set(true); + if (contextKey) { + contextKey.set(true); + } + })); + this.toUnbind.push(inputFocusTracker.addBlurListener(() => { + this.inputBoxFocused.set(this.searchWidget.searchInputHasFocus() + || this.searchWidget.replaceInputHasFocus() + || this.inputPatternIncludes.inputHasFocus() + || this.inputPatternExcludes.inputHasFocus()); + if (contextKey) { + contextKey.set(false); + } + })); + } + + private onReplaceToggled(): void { + this.layout(this.size); + + const isReplaceShown = this.searchAndReplaceWidget.isReplaceShown(); + if (!isReplaceShown) { + this.storageService.store(SearchViewlet.SHOW_REPLACE_STORAGE_KEY, false, StorageScope.WORKSPACE); + } else { + this.storageService.remove(SearchViewlet.SHOW_REPLACE_STORAGE_KEY); + } + } + + private onSearchResultsChanged(event?: IChangeEvent): TPromise { + return this.refreshTree(event).then(() => { + this.searchWidget.setReplaceAllActionState(!this.viewModel.searchResult.isEmpty()); + this.updateSearchResultCount(); + }); + } + + private refreshTree(event?: IChangeEvent): TPromise { + if (!event || event.added || event.removed) { + return this.tree.refresh(this.viewModel.searchResult); + } else { + if (event.elements.length === 1) { + return this.tree.refresh(event.elements[0]); + } else { + return this.tree.refresh(event.elements); + } + } + } + + private replaceAll(): void { + if (this.viewModel.searchResult.count() === 0) { + return; + } + + let progressRunner = this.progressService.show(100); + + let occurrences = this.viewModel.searchResult.count(); + let fileCount = this.viewModel.searchResult.fileCount(); + let replaceValue = this.searchWidget.getReplaceValue() || ''; + let afterReplaceAllMessage = this.buildAfterReplaceAllMessage(occurrences, fileCount, replaceValue); + + let confirmation: IConfirmation = { + title: nls.localize('replaceAll.confirmation.title', "Replace All"), + message: this.buildReplaceAllConfirmationMessage(occurrences, fileCount, replaceValue), + primaryButton: nls.localize('replaceAll.confirm.button', "Replace"), + type: 'question' + }; + + if (this.messageService.confirm(confirmation)) { + this.searchWidget.setReplaceAllActionState(false); + this.viewModel.searchResult.replaceAll(progressRunner).then(() => { + progressRunner.done(); + this.clearMessage() + .p({ text: afterReplaceAllMessage }); + }, (error) => { + progressRunner.done(); + errors.isPromiseCanceledError(error); + this.messageService.show(Severity.Error, error); + }); + } + } + + private buildAfterReplaceAllMessage(occurrences: number, fileCount: number, replaceValue?: string) { + if (occurrences === 1) { + if (fileCount === 1) { + if (replaceValue) { + return nls.localize('replaceAll.occurrence.file.message', "Replaced {0} occurrence across {1} file with '{2}'.", occurrences, fileCount, replaceValue); + } + + return nls.localize('removeAll.occurrence.file.message', "Replaced {0} occurrence across {1} file'.", occurrences, fileCount); + } + + if (replaceValue) { + return nls.localize('replaceAll.occurrence.files.message', "Replaced {0} occurrence across {1} files with '{2}'.", occurrences, fileCount, replaceValue); + } + + return nls.localize('removeAll.occurrence.files.message', "Replaced {0} occurrence across {1} files.", occurrences, fileCount); + } + + if (fileCount === 1) { + if (replaceValue) { + return nls.localize('replaceAll.occurrences.file.message', "Replaced {0} occurrences across {1} file with '{2}'.", occurrences, fileCount, replaceValue); + } + + return nls.localize('removeAll.occurrences.file.message', "Replaced {0} occurrences across {1} file'.", occurrences, fileCount); + } + + if (replaceValue) { + return nls.localize('replaceAll.occurrences.files.message', "Replaced {0} occurrences across {1} files with '{2}'.", occurrences, fileCount, replaceValue); + } + + return nls.localize('removeAll.occurrences.files.message', "Replaced {0} occurrences across {1} files.", occurrences, fileCount); + } + + private buildReplaceAllConfirmationMessage(occurrences: number, fileCount: number, replaceValue?: string) { + if (occurrences === 1) { + if (fileCount === 1) { + if (replaceValue) { + return nls.localize('removeAll.occurrence.file.confirmation.message', "Replace {0} occurrence across {1} file with '{2}'?", occurrences, fileCount, replaceValue); + } + + return nls.localize('replaceAll.occurrence.file.confirmation.message', "Replace {0} occurrence across {1} file'?", occurrences, fileCount); + } + + if (replaceValue) { + return nls.localize('removeAll.occurrence.files.confirmation.message', "Replace {0} occurrence across {1} files with '{2}'?", occurrences, fileCount, replaceValue); + } + + return nls.localize('replaceAll.occurrence.files.confirmation.message', "Replace {0} occurrence across {1} files?", occurrences, fileCount); + } + + if (fileCount === 1) { + if (replaceValue) { + return nls.localize('removeAll.occurrences.file.confirmation.message', "Replace {0} occurrences across {1} file with '{2}'?", occurrences, fileCount, replaceValue); + } + + return nls.localize('replaceAll.occurrences.file.confirmation.message', "Replace {0} occurrences across {1} file'?", occurrences, fileCount); + } + + if (replaceValue) { + return nls.localize('removeAll.occurrences.files.confirmation.message', "Replace {0} occurrences across {1} files with '{2}'?", occurrences, fileCount, replaceValue); + } + + return nls.localize('replaceAll.occurrences.files.confirmation.message', "Replace {0} occurrences across {1} files?", occurrences, fileCount); + } + + private clearMessage(): Builder { + return this.messages.empty().show() + .asContainer().div({ 'class': 'message' }) + .asContainer(); + } + + private createSearchResultsView(builder: Builder): void { + builder.div({ 'class': 'results' }, (div) => { + this.results = div; + this.results.addClass('show-file-icons'); + + let dataSource = new SearchDataSource(this.contextService.hasMultiFolderWorkspace()); + let renderer = this.instantiationService.createInstance(SearchRenderer, this.getActionRunner(), this); + let dnd = new SimpleFileResourceDragAndDrop(obj => obj instanceof FileMatch ? obj.resource() : void 0); + + this.tree = new Tree(div.getHTMLElement(), { + dataSource: dataSource, + renderer: renderer, + sorter: new SearchSorter(), + filter: new SearchFilter(), + accessibilityProvider: this.instantiationService.createInstance(SearchAccessibilityProvider), + dnd + }, { + ariaLabel: nls.localize('treeAriaLabel', "Search Results"), + keyboardSupport: false + }); + + this.toUnbind.push(attachListStyler(this.tree, this.themeService)); + + this.tree.setInput(this.viewModel.searchResult); + this.toUnbind.push(renderer); + + this.toUnbind.push(this.listService.register(this.tree)); + const fileResultsNavigation = this._register(new FileResultsNavigation(this.tree)); + this._register(debounceEvent(fileResultsNavigation.openFile, (last, event) => event, 75, true)(options => { + if (options.element instanceof Match) { + let selectedMatch: Match = options.element; + if (this.currentSelectedFileMatch) { + this.currentSelectedFileMatch.setSelectedMatch(null); + } + this.currentSelectedFileMatch = selectedMatch.parent(); + this.currentSelectedFileMatch.setSelectedMatch(selectedMatch); + if (!(options.payload && options.payload.preventEditorOpen)) { + this.onFocus(selectedMatch, options.editorOptions.preserveFocus, options.sideBySide, options.editorOptions.pinned); + } + } + })); + + this.toUnbind.push(this.tree.onDOMFocus(e => { + const focus = this.tree.getFocus(); + this.firstMatchFocused.set(this.tree.getNavigator().first() === this.tree.getFocus()); + this.fileMatchOrMatchFocused.set(true); + this.fileMatchFocused.set(focus instanceof FileMatch); + this.matchFocused.set(focus instanceof Match); + })); + + this.toUnbind.push(this.tree.onDOMBlur(e => { + this.firstMatchFocused.reset(); + this.fileMatchOrMatchFocused.reset(); + this.fileMatchFocused.reset(); + this.matchFocused.reset(); + })); + + + }); + } + + public selectCurrentMatch(): void { + const focused = this.tree.getFocus(); + const eventPayload = { focusEditor: true }; + this.tree.setSelection([focused], eventPayload); + } + + public selectNextMatch(): void { + const [selected]: FileMatchOrMatch[] = this.tree.getSelection(); + + // Expand the initial selected node, if needed + if (selected instanceof FileMatch) { + if (!this.tree.isExpanded(selected)) { + this.tree.expand(selected); + } + } + + let navigator = this.tree.getNavigator(selected, /*subTreeOnly=*/false); + + let next = navigator.next(); + if (!next) { + // Reached the end - get a new navigator from the root. + // .first and .last only work when subTreeOnly = true. Maybe there's a simpler way. + navigator = this.tree.getNavigator(this.tree.getInput(), /*subTreeOnly*/true); + next = navigator.first(); + } + + // Expand and go past FileMatch nodes + if (!(next instanceof Match)) { + if (!this.tree.isExpanded(next)) { + this.tree.expand(next); + } + + // Select the FileMatch's first child + next = navigator.next(); + } + + // Reveal the newly selected element + const eventPayload = { preventEditorOpen: true }; + this.tree.setFocus(next, eventPayload); + this.tree.setSelection([next], eventPayload); + this.tree.reveal(next); + this.selectCurrentMatchEmitter.fire(); + } + + public selectPreviousMatch(): void { + const [selected]: FileMatchOrMatch[] = this.tree.getSelection(); + let navigator = this.tree.getNavigator(selected, /*subTreeOnly=*/false); + + let prev = navigator.previous(); + + // Expand and go past FileMatch nodes + if (!(prev instanceof Match)) { + prev = navigator.previous(); + if (!prev) { + // Wrap around. Get a new tree starting from the root + navigator = this.tree.getNavigator(this.tree.getInput(), /*subTreeOnly*/true); + prev = navigator.last(); + + // This is complicated because .last will set the navigator to the last FileMatch, + // so expand it and FF to its last child + this.tree.expand(prev); + let tmp; + while (tmp = navigator.next()) { + prev = tmp; + } + } + + if (!(prev instanceof Match)) { + // There is a second non-Match result, which must be a collapsed FileMatch. + // Expand it then select its last child. + navigator.next(); + this.tree.expand(prev); + prev = navigator.previous(); + } + } + + // Reveal the newly selected element + if (prev) { + const eventPayload = { preventEditorOpen: true }; + this.tree.setFocus(prev, eventPayload); + this.tree.setSelection([prev], eventPayload); + this.tree.reveal(prev); + this.selectCurrentMatchEmitter.fire(); + } + } + + public setVisible(visible: boolean): TPromise { + let promise: TPromise; + this.viewletVisible.set(visible); + if (visible) { + promise = super.setVisible(visible); + this.tree.onVisible(); + } else { + this.tree.onHidden(); + promise = super.setVisible(visible); + } + + // Enable highlights if there are searchresults + if (this.viewModel) { + this.viewModel.searchResult.toggleHighlights(visible); + } + + // Open focused element from results in case the editor area is otherwise empty + if (visible && !this.editorService.getActiveEditor()) { + let focus = this.tree.getFocus(); + if (focus) { + this.onFocus(focus, true); + } + } + + return promise; + } + + public focus(): void { + super.focus(); + + let selectedText = this.getSearchTextFromEditor(); + if (selectedText) { + this.searchWidget.searchInput.setValue(selectedText); + } + this.searchWidget.focus(); + } + + public focusNextInputBox(): void { + if (this.searchWidget.searchInputHasFocus()) { + if (this.searchWidget.isReplaceShown()) { + this.searchWidget.focus(true, true); + } else { + this.moveFocusFromSearchOrReplace(); + } + return; + } + + if (this.searchWidget.replaceInputHasFocus()) { + this.moveFocusFromSearchOrReplace(); + return; + } + + if (this.inputPatternIncludes.inputHasFocus()) { + this.inputPatternExcludes.focus(); + this.inputPatternExcludes.select(); + return; + } + + if (this.inputPatternExcludes.inputHasFocus()) { + this.selectTreeIfNotSelected(); + return; + } + } + + private moveFocusFromSearchOrReplace() { + if (this.showsFileTypes()) { + this.toggleQueryDetails(true, this.showsFileTypes()); + } else { + this.selectTreeIfNotSelected(); + } + } + + public focusPreviousInputBox(): void { + if (this.searchWidget.searchInputHasFocus()) { + return; + } + + if (this.searchWidget.replaceInputHasFocus()) { + this.searchWidget.focus(true); + return; + } + + if (this.inputPatternIncludes.inputHasFocus()) { + this.searchWidget.focus(true, true); + return; + } + + if (this.inputPatternExcludes.inputHasFocus()) { + this.inputPatternIncludes.focus(); + this.inputPatternIncludes.select(); + return; + } + + if (this.tree.isDOMFocused()) { + this.moveFocusFromResults(); + return; + } + } + + private moveFocusFromResults(): void { + if (this.showsFileTypes()) { + this.toggleQueryDetails(true, true, false, true); + } else { + this.searchWidget.focus(true, true); + } + } + + private reLayout(): void { + if (this.isDisposed) { + return; + } + + this.searchWidget.setWidth(this.size.width - 28 /* container margin */); + + this.inputPatternExcludes.setWidth(this.size.width - 28 /* container margin */); + this.inputPatternIncludes.setWidth(this.size.width - 28 /* container margin */); + + const messagesSize = this.messages.isHidden() ? 0 : dom.getTotalHeight(this.messages.getHTMLElement()); + const searchResultContainerSize = this.size.height - + messagesSize - + dom.getTotalHeight(this.searchWidgetsContainer.getContainer()); + + this.results.style({ height: searchResultContainerSize + 'px' }); + + this.tree.layout(searchResultContainerSize); + } + + public layout(dimension: Dimension): void { + this.size = dimension; + this.reLayout(); + } + + public getControl(): ITree { + return this.tree; + } + + public clearSearchResults(): void { + this.viewModel.searchResult.clear(); + this.showEmptyStage(); + if (!this.contextService.hasWorkspace()) { + this.searchWithoutFolderMessage(this.clearMessage()); + } + this.searchWidget.clear(); + this.viewModel.cancelSearch(); + } + + public cancelSearch(): boolean { + if (this.viewModel.cancelSearch()) { + this.searchWidget.focus(); + return true; + } + return false; + } + + private selectTreeIfNotSelected(): void { + if (this.tree.getInput()) { + this.tree.DOMFocus(); + let selection = this.tree.getSelection(); + if (selection.length === 0) { + this.tree.focusNext(); + } + } + } + + private getSearchTextFromEditor(): string { + if (!this.editorService.getActiveEditor()) { + return null; + } + + let editorControl = this.editorService.getActiveEditor().getControl(); + if (isCommonDiffEditor(editorControl)) { + if (editorControl.getOriginalEditor().isFocused()) { + editorControl = editorControl.getOriginalEditor(); + } else { + editorControl = editorControl.getModifiedEditor(); + } + } + + if (!isCommonCodeEditor(editorControl)) { + return null; + } + + const codeEditor: ICommonCodeEditor = editorControl; + const range = codeEditor.getSelection(); + if (!range) { + return null; + } + + if (range.isEmpty() && !this.searchWidget.searchInput.getValue()) { + const wordAtPosition = codeEditor.getModel().getWordAtPosition(range.getStartPosition()); + if (wordAtPosition) { + return wordAtPosition.word; + } + } + + if (!range.isEmpty() && range.startLineNumber === range.endLineNumber) { + let searchText = editorControl.getModel().getLineContent(range.startLineNumber); + searchText = searchText.substring(range.startColumn - 1, range.endColumn - 1); + return searchText; + } + + return null; + } + + private showsFileTypes(): boolean { + return dom.hasClass(this.queryDetails, 'more'); + } + + public toggleCaseSensitive(): void { + this.searchWidget.searchInput.setCaseSensitive(!this.searchWidget.searchInput.getCaseSensitive()); + this.onQueryChanged(true, true); + } + + public toggleWholeWords(): void { + this.searchWidget.searchInput.setWholeWords(!this.searchWidget.searchInput.getWholeWords()); + this.onQueryChanged(true, true); + } + + public toggleRegex(): void { + this.searchWidget.searchInput.setRegex(!this.searchWidget.searchInput.getRegex()); + this.onQueryChanged(true, true); + } + + public toggleQueryDetails(moveFocus?: boolean, show?: boolean, skipLayout?: boolean, reverse?: boolean): void { + this.telemetryService.publicLog('search.toggleQueryDetails'); + + let cls = 'more'; + show = typeof show === 'undefined' ? !dom.hasClass(this.queryDetails, cls) : Boolean(show); + this.viewletSettings['query.queryDetailsExpanded'] = show; + skipLayout = Boolean(skipLayout); + + if (show) { + dom.addClass(this.queryDetails, cls); + if (moveFocus) { + if (reverse) { + this.inputPatternExcludes.focus(); + this.inputPatternExcludes.select(); + } else { + this.inputPatternIncludes.focus(); + this.inputPatternIncludes.select(); + } + } + } else { + dom.removeClass(this.queryDetails, cls); + if (moveFocus) { + this.searchWidget.focus(); + } + } + + if (!skipLayout && this.size) { + this.layout(this.size); + } + } + + public searchInFolder(resource: URI): void { + let folderPath = null; + const workspace = this.contextService.getWorkspace(); + if (workspace && resource) { + if (this.contextService.hasFolderWorkspace()) { + // Show relative path from the root for single-root mode + folderPath = paths.relative(workspace.roots[0].fsPath, resource.fsPath); + if (folderPath && folderPath !== '.') { + folderPath = './' + folderPath; + } + } else { + const owningRoot = this.contextService.getRoot(resource); + if (owningRoot) { + const owningRootBasename = paths.basename(owningRoot.fsPath); + + // If this root is the only one with its basename, use a relative ./ path. If there is another, use an absolute path + const isUniqueRoot = workspace.roots.filter(root => paths.basename(root.fsPath) === owningRootBasename).length === 1; + if (isUniqueRoot) { + folderPath = `./${owningRootBasename}/${paths.relative(owningRoot.fsPath, resource.fsPath)}`; + } else { + folderPath = resource.fsPath; + } + } + } + } + + if (!folderPath || folderPath === '.') { + this.inputPatternIncludes.setValue(''); + this.searchWidget.focus(); + return; + } + + // Show 'files to include' box + if (!this.showsFileTypes()) { + this.toggleQueryDetails(true, true); + } + + this.inputPatternIncludes.setValue(folderPath); + this.searchWidget.focus(false); + } + + public onQueryChanged(rerunQuery: boolean, preserveFocus?: boolean): void { + const isRegex = this.searchWidget.searchInput.getRegex(); + const isWholeWords = this.searchWidget.searchInput.getWholeWords(); + const isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive(); + const contentPattern = this.searchWidget.searchInput.getValue(); + const excludePatternText = this.inputPatternExcludes.getValue().trim(); + const includePatternText = this.inputPatternIncludes.getValue().trim(); + const useIgnoreFiles = this.inputPatternExcludes.useIgnoreFiles(); + const useExcludeSettings = this.inputPatternExcludes.useExcludeSettings(); + + if (!rerunQuery) { + return; + } + + if (contentPattern.length === 0) { + return; + } + + // Validate regex is OK + if (isRegex) { + let regExp: RegExp; + try { + regExp = new RegExp(contentPattern); + } catch (e) { + return; // malformed regex + } + + if (strings.regExpLeadsToEndlessLoop(regExp)) { + return; // endless regex + } + } + + const content = { + pattern: contentPattern, + isRegExp: isRegex, + isCaseSensitive: isCaseSensitive, + isWordMatch: isWholeWords, + wordSeparators: this.configurationService.getConfiguration().editor.wordSeparators + }; + + const excludePattern = this.inputPatternExcludes.getValue(); + const includePattern = this.inputPatternIncludes.getValue(); + + const options: IQueryOptions = { + extraFileResources: getOutOfWorkspaceEditorResources(this.editorGroupService, this.contextService), + maxResults: SearchViewlet.MAX_TEXT_RESULTS, + disregardIgnoreFiles: !useIgnoreFiles, + disregardExcludeSettings: !useExcludeSettings, + excludePattern, + includePattern + }; + const folderResources = this.contextService.hasWorkspace() ? this.contextService.getWorkspace().roots : []; + + const onQueryValidationError = (err: Error) => { + this.searchWidget.searchInput.showMessage({ content: err.message, type: MessageType.ERROR }); + this.viewModel.searchResult.clear(); + }; + + let query: ISearchQuery; + try { + query = this.queryBuilder.text(content, folderResources, options); + } catch (err) { + onQueryValidationError(err); + } + + this.validateQuery(query).then(() => { + this.onQueryTriggered(query, excludePatternText, includePatternText); + + if (!preserveFocus) { + this.searchWidget.focus(false); // focus back to input field + } + }, onQueryValidationError); + } + + private validateQuery(query: ISearchQuery): TPromise { + // Validate folderQueries + const folderQueriesExistP = + query.folderQueries.map(fq => { + return this.fileService.existsFile(fq.folder); + }); + + return TPromise.join(folderQueriesExistP).then(existResults => { + const nonExistantFolders = existResults.map((exists, i) => ({ exists, query: query.folderQueries[i] })) + .filter(folderExists => !folderExists.exists); + + if (nonExistantFolders.length) { + const nonExistantPath = nonExistantFolders[0].query.folder.fsPath; + const searchPathNotFoundError = nls.localize('searchPathNotFoundError', "Search path not found: {0}", nonExistantPath); + return TPromise.wrapError(new Error(searchPathNotFoundError)); + } + + return undefined; + }); + } + + private onQueryTriggered(query: ISearchQuery, excludePatternText: string, includePatternText: string): void { + this.inputPatternExcludes.onSearchSubmit(); + this.inputPatternIncludes.onSearchSubmit(); + + this.viewModel.cancelSearch(); + + // Progress total is 100.0% for more progress bar granularity + let progressTotal = 1000; + let progressWorked = 0; + + let progressRunner = query.useRipgrep ? + this.progressService.show(/*infinite=*/true) : + this.progressService.show(progressTotal); + + this.loading = true; + this.searchWidget.searchInput.clearMessage(); + this.showEmptyStage(); + + let isDone = false; + const outputChannel = this.outputService.getChannel('search'); + let onComplete = (completed?: ISearchComplete) => { + if (query.useRipgrep) { + outputChannel.append('\n'); + } + + isDone = true; + + // Complete up to 100% as needed + if (completed && !query.useRipgrep) { + progressRunner.worked(progressTotal - progressWorked); + setTimeout(() => progressRunner.done(), 200); + } else { + progressRunner.done(); + } + + // Do final render, then expand if just 1 file with less than 50 matches + this.onSearchResultsChanged().then(() => { + if (this.viewModel.searchResult.count() === 1) { + const onlyMatch = this.viewModel.searchResult.matches()[0]; + if (onlyMatch.count() < 50) { + return this.tree.expand(onlyMatch); + } + } + + return null; + }).done(null, errors.onUnexpectedError); + + this.viewModel.replaceString = this.searchWidget.getReplaceValue(); + + let hasResults = !this.viewModel.searchResult.isEmpty(); + this.loading = false; + + this.actionRegistry['refresh'].enabled = true; + this.actionRegistry['vs.tree.collapse'].enabled = hasResults; + this.actionRegistry['clearSearchResults'].enabled = hasResults; + + if (completed && completed.limitHit) { + this.searchWidget.searchInput.showMessage({ + content: nls.localize('searchMaxResultsWarning', "The result set only contains a subset of all matches. Please be more specific in your search to narrow down the results."), + type: MessageType.WARNING + }); + } + + if (!hasResults) { + let hasExcludes = !!excludePatternText; + let hasIncludes = !!includePatternText; + let message: string; + + if (!completed) { + message = nls.localize('searchCanceled', "Search was canceled before any results could be found - "); + } else if (hasIncludes && hasExcludes) { + message = nls.localize('noResultsIncludesExcludes', "No results found in '{0}' excluding '{1}' - ", includePatternText, excludePatternText); + } else if (hasIncludes) { + message = nls.localize('noResultsIncludes', "No results found in '{0}' - ", includePatternText); + } else if (hasExcludes) { + message = nls.localize('noResultsExcludes', "No results found excluding '{0}' - ", excludePatternText); + } else { + message = nls.localize('noResultsFound', "No results found. Review your settings for configured exclusions and ignore files - "); + } + + // Indicate as status to ARIA + aria.status(message); + + this.tree.onHidden(); + this.results.hide(); + const div = this.clearMessage(); + const p = $(div).p({ text: message }); + + if (!completed) { + $(p).a({ + 'class': ['pointer', 'prominent'], + text: nls.localize('rerunSearch.message', "Search again") + }).on(dom.EventType.CLICK, (e: MouseEvent) => { + dom.EventHelper.stop(e, false); + + this.onQueryChanged(true); + }); + } else if (hasIncludes || hasExcludes) { + $(p).a({ + 'class': ['pointer', 'prominent'], + 'tabindex': '0', + text: nls.localize('rerunSearchInAll.message', "Search again in all files") + }).on(dom.EventType.CLICK, (e: MouseEvent) => { + dom.EventHelper.stop(e, false); + + this.inputPatternExcludes.setValue(''); + this.inputPatternIncludes.setValue(''); + + this.onQueryChanged(true); + }); + } else { + $(p).a({ + 'class': ['pointer', 'prominent'], + 'tabindex': '0', + text: nls.localize('openSettings.message', "Open Settings") + }).on(dom.EventType.CLICK, (e: MouseEvent) => { + dom.EventHelper.stop(e, false); + + let editorPromise = this.contextService.hasWorkspace() ? this.preferencesService.openWorkspaceSettings() : this.preferencesService.openGlobalSettings(); + editorPromise.done(editor => { + if (editor instanceof PreferencesEditor) { + editor.focusSearch('.exclude'); + } + }, errors.onUnexpectedError); + }); + } + + if (completed) { + $(p).span({ + text: ' - ' + }); + + $(p).a({ + 'class': ['pointer', 'prominent'], + 'tabindex': '0', + text: nls.localize('openSettings.learnMore', "Learn More") + }).on(dom.EventType.CLICK, (e: MouseEvent) => { + dom.EventHelper.stop(e, false); + + window.open('https://go.microsoft.com/fwlink/?linkid=853977'); + }); + } + + if (!this.contextService.hasWorkspace()) { + this.searchWithoutFolderMessage(div); + } + } else { + this.viewModel.searchResult.toggleHighlights(true); // show highlights + + // Indicate final search result count for ARIA + aria.status(nls.localize('ariaSearchResultsStatus', "Search returned {0} results in {1} files", this.viewModel.searchResult.count(), this.viewModel.searchResult.fileCount())); + } + }; + + let onError = (e: any) => { + if (query.useRipgrep) { + outputChannel.append('\n'); + } + + if (errors.isPromiseCanceledError(e)) { + onComplete(null); + } else { + this.loading = false; + isDone = true; + progressRunner.done(); + this.searchWidget.searchInput.showMessage({ content: e.message, type: MessageType.ERROR }); + this.viewModel.searchResult.clear(); + } + }; + + let total: number = 0; + let worked: number = 0; + let visibleMatches = 0; + let onProgress = (p: ISearchProgressItem) => { + // Progress + if (p.total) { + total = p.total; + } + if (p.worked) { + worked = p.worked; + } + + if (p.message) { + outputChannel.append(p.message); + } + }; + + // Handle UI updates in an interval to show frequent progress and results + let uiRefreshHandle = setInterval(() => { + if (isDone) { + window.clearInterval(uiRefreshHandle); + return; + } + + if (!query.useRipgrep) { + // Progress bar update + let fakeProgress = true; + if (total > 0 && worked > 0) { + let ratio = Math.round((worked / total) * progressTotal); + if (ratio > progressWorked) { // never show less progress than what we have already + progressRunner.worked(ratio - progressWorked); + progressWorked = ratio; + fakeProgress = false; + } + } + + // Fake progress up to 90%, or when actual progress beats it + const fakeMax = 900; + const fakeMultiplier = 12; + if (fakeProgress && progressWorked < fakeMax) { + // Linearly decrease the rate of fake progress. + // 1 is the smallest allowed amount of progress. + const fakeAmt = Math.round((fakeMax - progressWorked) / fakeMax * fakeMultiplier) || 1; + progressWorked += fakeAmt; + progressRunner.worked(fakeAmt); + } + } + + // Search result tree update + const fileCount = this.viewModel.searchResult.fileCount(); + if (visibleMatches !== fileCount) { + visibleMatches = fileCount; + this.tree.refresh().done(null, errors.onUnexpectedError); + + this.updateSearchResultCount(); + } + if (fileCount > 0) { + // since we have results now, enable some actions + if (!this.actionRegistry['vs.tree.collapse'].enabled) { + this.actionRegistry['vs.tree.collapse'].enabled = true; + } + } + }, 100); + + this.searchWidget.setReplaceAllActionState(false); + + this.viewModel.search(query).done(onComplete, onError, onProgress); + } + + private updateSearchResultCount(): void { + const fileCount = this.viewModel.searchResult.fileCount(); + const msgWasHidden = this.messages.isHidden(); + if (fileCount > 0) { + const div = this.clearMessage(); + $(div).p({ text: this.buildResultCountMessage(this.viewModel.searchResult.count(), fileCount) }); + if (msgWasHidden) { + this.reLayout(); + } + } else if (!msgWasHidden) { + this.messages.hide(); + } + } + + private buildResultCountMessage(resultCount: number, fileCount: number): string { + if (resultCount === 1 && fileCount === 1) { + return nls.localize('search.file.result', "{0} result in {1} file", resultCount, fileCount); + } else if (resultCount === 1) { + return nls.localize('search.files.result', "{0} result in {1} files", resultCount, fileCount); + } else if (fileCount === 1) { + return nls.localize('search.file.results', "{0} results in {1} file", resultCount, fileCount); + } else { + return nls.localize('search.files.results', "{0} results in {1} files", resultCount, fileCount); + } + } + + private searchWithoutFolderMessage(div: Builder): void { + $(div).p({ text: nls.localize('searchWithoutFolder', "You have not yet opened a folder. Only open files are currently searched - ") }) + .asContainer().a({ + 'class': ['pointer', 'prominent'], + 'tabindex': '0', + text: nls.localize('openFolder', "Open Folder") + }).on(dom.EventType.CLICK, (e: MouseEvent) => { + dom.EventHelper.stop(e, false); + + const actionClass = env.isMacintosh ? OpenFileFolderAction : OpenFolderAction; + const action = this.instantiationService.createInstance(actionClass, actionClass.ID, actionClass.LABEL); + this.actionRunner.run(action).done(() => { + action.dispose(); + }, err => { + action.dispose(); + errors.onUnexpectedError(err); + }); + }); + } + + private showEmptyStage(): void { + + // disable 'result'-actions + this.actionRegistry['refresh'].enabled = false; + this.actionRegistry['vs.tree.collapse'].enabled = false; + this.actionRegistry['clearSearchResults'].enabled = false; + + // clean up ui + // this.replaceService.disposeAllReplacePreviews(); + this.messages.hide(); + this.results.show(); + this.tree.onVisible(); + this.currentSelectedFileMatch = null; + } + + private onFocus(lineMatch: any, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + if (!(lineMatch instanceof Match)) { + this.viewModel.searchResult.rangeHighlightDecorations.removeHighlightRange(); + return TPromise.as(true); + } + + this.telemetryService.publicLog('searchResultChosen'); + + return (this.viewModel.isReplaceActive() && !!this.viewModel.replaceString) ? + this.replaceService.openReplacePreview(lineMatch, preserveFocus, sideBySide, pinned) : + this.open(lineMatch, preserveFocus, sideBySide, pinned); + } + + public open(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + let selection = this.getSelectionFrom(element); + let resource = element instanceof Match ? element.parent().resource() : (element).resource(); + return this.editorService.openEditor({ + resource: resource, + options: { + preserveFocus, + pinned, + selection, + revealIfVisible: !sideBySide + } + }, sideBySide).then(editor => { + if (editor && element instanceof Match && preserveFocus) { + this.viewModel.searchResult.rangeHighlightDecorations.highlightRange({ + resource, + range: element.range() + }, editor.getControl()); + } else { + this.viewModel.searchResult.rangeHighlightDecorations.removeHighlightRange(); + } + }, errors.onUnexpectedError); + } + + private getSelectionFrom(element: FileMatchOrMatch): any { + let match: Match = null; + if (element instanceof Match) { + match = element; + } + if (element instanceof FileMatch && element.count() > 0) { + match = element.matches()[element.matches().length - 1]; + } + if (match) { + let range = match.range(); + if (this.viewModel.isReplaceActive() && !!this.viewModel.replaceString) { + let replaceString = match.replaceString; + return { + startLineNumber: range.startLineNumber, + startColumn: range.startColumn, + endLineNumber: range.startLineNumber, + endColumn: range.startColumn + replaceString.length + }; + } + return range; + } + return void 0; + } + + private onUntitledDidChangeDirty(resource: URI): void { + if (!this.viewModel) { + return; + } + + // remove search results from this resource as it got disposed + if (!this.untitledEditorService.isDirty(resource)) { + let matches = this.viewModel.searchResult.matches(); + for (let i = 0, len = matches.length; i < len; i++) { + if (resource.toString() === matches[i].resource().toString()) { + this.viewModel.searchResult.remove(matches[i]); + } + } + } + } + + private onFilesChanged(e: FileChangesEvent): void { + if (!this.viewModel) { + return; + } + + let matches = this.viewModel.searchResult.matches(); + + for (let i = 0, len = matches.length; i < len; i++) { + if (e.contains(matches[i].resource(), FileChangeType.DELETED)) { + this.viewModel.searchResult.remove(matches[i]); + } + } + } + + public getActions(): IAction[] { + return [ + this.actionRegistry['refresh'], + this.actionRegistry['vs.tree.collapse'], + this.actionRegistry['clearSearchResults'] + ]; + } + + public shutdown(): void { + const isRegex = this.searchWidget.searchInput.getRegex(); + const isWholeWords = this.searchWidget.searchInput.getWholeWords(); + const isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive(); + const contentPattern = this.searchWidget.searchInput.getValue(); + const patternExcludes = this.inputPatternExcludes.getValue().trim(); + const patternIncludes = this.inputPatternIncludes.getValue().trim(); + const useIgnoreFiles = this.inputPatternExcludes.useIgnoreFiles(); + const searchHistory = this.searchWidget.getHistory(); + const patternExcludesHistory = this.inputPatternExcludes.getHistory(); + const patternIncludesHistory = this.inputPatternIncludes.getHistory(); + + // store memento + this.viewletSettings['query.contentPattern'] = contentPattern; + this.viewletSettings['query.searchHistory'] = searchHistory; + this.viewletSettings['query.regex'] = isRegex; + this.viewletSettings['query.wholeWords'] = isWholeWords; + this.viewletSettings['query.caseSensitive'] = isCaseSensitive; + this.viewletSettings['query.folderExclusions'] = patternExcludes; + this.viewletSettings['query.folderIncludes'] = patternIncludes; + this.viewletSettings['query.folderExclusionsHistory'] = patternExcludesHistory; + this.viewletSettings['query.folderIncludesHistory'] = patternIncludesHistory; + this.viewletSettings['query.useIgnoreFiles'] = useIgnoreFiles; + + super.shutdown(); + } + + public dispose(): void { + this.isDisposed = true; + + if (this.tree) { + this.tree.dispose(); + } + + this.searchWidget.dispose(); + this.inputPatternIncludes.dispose(); + this.inputPatternExcludes.dispose(); + + this.viewModel.dispose(); + + super.dispose(); + } +} + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const matchHighlightColor = theme.getColor(editorFindMatchHighlight); + if (matchHighlightColor) { + collector.addRule(`.monaco-workbench .search-viewlet .findInFileMatch { background-color: ${matchHighlightColor}; }`); + } + + const diffInsertedColor = theme.getColor(diffInserted); + if (diffInsertedColor) { + collector.addRule(`.monaco-workbench .search-viewlet .replaceMatch { background-color: ${diffInsertedColor}; }`); + } + + const diffRemovedColor = theme.getColor(diffRemoved); + if (diffRemovedColor) { + collector.addRule(`.monaco-workbench .search-viewlet .replace.findInFileMatch { background-color: ${diffRemovedColor}; }`); + } + + const diffInsertedOutlineColor = theme.getColor(diffInsertedOutline); + if (diffInsertedOutlineColor) { + collector.addRule(`.monaco-workbench .search-viewlet .replaceMatch:not(:empty) { border: 1px dashed ${diffInsertedOutlineColor}; }`); + } + + const diffRemovedOutlineColor = theme.getColor(diffRemovedOutline); + if (diffRemovedOutlineColor) { + collector.addRule(`.monaco-workbench .search-viewlet .replace.findInFileMatch { border: 1px dashed ${diffRemovedOutlineColor}; }`); + } + + const activeContrastBorderColor = theme.getColor(activeContrastBorder); + if (activeContrastBorderColor) { + collector.addRule(` + .monaco-workbench .search-viewlet .findInFileMatch { border: 1px dashed ${activeContrastBorderColor}; } + `); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts new file mode 100644 index 0000000000..7d9ca2be86 --- /dev/null +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -0,0 +1,388 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as strings from 'vs/base/common/strings'; +import * as dom from 'vs/base/browser/dom'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { Action } from 'vs/base/common/actions'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { FindInput, IFindInputOptions } from 'vs/base/browser/ui/findinput/findInput'; +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ContextKeyExpr, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Builder } from 'vs/base/browser/builder'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { isSearchViewletFocused, appendKeyBindingLabel } from 'vs/workbench/parts/search/browser/searchActions'; +import { CONTEXT_FIND_WIDGET_NOT_VISIBLE } from 'vs/editor/contrib/find/common/findController'; +import { HistoryNavigator } from 'vs/base/common/history'; +import * as Constants from 'vs/workbench/parts/search/common/constants'; +import { attachInputBoxStyler, attachFindInputBoxStyler, attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; + +export interface ISearchWidgetOptions { + value?: string; + isRegex?: boolean; + isCaseSensitive?: boolean; + isWholeWords?: boolean; + history?: string[]; +} + +class ReplaceAllAction extends Action { + + private static fgInstance: ReplaceAllAction = null; + public static ID: string = 'search.action.replaceAll'; + + static get INSTANCE(): ReplaceAllAction { + if (ReplaceAllAction.fgInstance === null) { + ReplaceAllAction.fgInstance = new ReplaceAllAction(); + } + return ReplaceAllAction.fgInstance; + } + + private _searchWidget: SearchWidget = null; + + constructor() { + super(ReplaceAllAction.ID, '', 'action-replace-all', false); + } + + set searchWidget(searchWidget: SearchWidget) { + this._searchWidget = searchWidget; + } + + run(): TPromise { + if (this._searchWidget) { + return this._searchWidget.triggerReplaceAll(); + } + return TPromise.as(null); + } +} + +export class SearchWidget extends Widget { + + private static REPLACE_ALL_DISABLED_LABEL = nls.localize('search.action.replaceAll.disabled.label', "Replace All (Submit Search to Enable)"); + private static REPLACE_ALL_ENABLED_LABEL = (keyBindingService2: IKeybindingService): string => { + let kb = keyBindingService2.lookupKeybinding(ReplaceAllAction.ID); + return appendKeyBindingLabel(nls.localize('search.action.replaceAll.enabled.label', "Replace All"), kb, keyBindingService2); + } + + public domNode: HTMLElement; + public searchInput: FindInput; + private searchInputBoxFocused: IContextKey; + private replaceInputBoxFocused: IContextKey; + private replaceInput: InputBox; + + public searchInputFocusTracker: dom.IFocusTracker; + public replaceInputFocusTracker: dom.IFocusTracker; + + private replaceContainer: HTMLElement; + private toggleReplaceButton: Button; + private replaceAllAction: ReplaceAllAction; + private replaceActive: IContextKey; + private replaceActionBar: ActionBar; + + private searchHistory: HistoryNavigator; + + private _onSearchSubmit = this._register(new Emitter()); + public onSearchSubmit: Event = this._onSearchSubmit.event; + + private _onSearchCancel = this._register(new Emitter()); + public onSearchCancel: Event = this._onSearchCancel.event; + + private _onReplaceToggled = this._register(new Emitter()); + public onReplaceToggled: Event = this._onReplaceToggled.event; + + private _onReplaceStateChange = this._register(new Emitter()); + public onReplaceStateChange: Event = this._onReplaceStateChange.event; + + private _onReplaceValueChanged = this._register(new Emitter()); + public onReplaceValueChanged: Event = this._onReplaceValueChanged.event; + + private _onReplaceAll = this._register(new Emitter()); + public onReplaceAll: Event = this._onReplaceAll.event; + + constructor( + container: Builder, + options: ISearchWidgetOptions, + @IContextViewService private contextViewService: IContextViewService, + @IThemeService private themeService: IThemeService, + @IContextKeyService private keyBindingService: IContextKeyService, + @IKeybindingService private keyBindingService2: IKeybindingService, + ) { + super(); + this.searchHistory = new HistoryNavigator(options.history); + this.replaceActive = Constants.ReplaceActiveKey.bindTo(this.keyBindingService); + this.searchInputBoxFocused = Constants.SearchInputBoxFocusedKey.bindTo(this.keyBindingService); + this.replaceInputBoxFocused = Constants.ReplaceInputBoxFocusedKey.bindTo(this.keyBindingService); + this.render(container, options); + } + + public focus(select: boolean = true, focusReplace: boolean = false): void { + if ((!focusReplace && this.searchInput.inputBox.hasFocus()) + || (focusReplace && this.replaceInput.hasFocus())) { + return; + } + + if (focusReplace && this.isReplaceShown()) { + this.replaceInput.focus(); + if (select) { + this.replaceInput.select(); + } + } else { + this.searchInput.focus(); + if (select) { + this.searchInput.select(); + } + } + } + + public setWidth(width: number) { + this.searchInput.setWidth(width); + this.replaceInput.width = width - 28; + } + + public clear() { + this.searchInput.clear(); + this.replaceInput.value = ''; + this.setReplaceAllActionState(false); + } + + public isReplaceShown(): boolean { + return !dom.hasClass(this.replaceContainer, 'disabled'); + } + + public getReplaceValue(): string { + return this.replaceInput.value; + } + + public toggleReplace(show?: boolean): void { + if (show === void 0 || show !== this.isReplaceShown()) { + this.onToggleReplaceButton(); + } + } + + public getHistory(): string[] { + return this.searchHistory.getHistory(); + } + + public showNextSearchTerm() { + let next = this.searchHistory.next(); + if (next) { + this.searchInput.setValue(next); + } + } + + public showPreviousSearchTerm() { + let previous; + if (this.searchInput.getValue().length === 0) { + previous = this.searchHistory.current(); + } else { + this.searchHistory.addIfNotPresent(this.searchInput.getValue()); + previous = this.searchHistory.previous(); + } + if (previous) { + this.searchInput.setValue(previous); + } + } + + public searchInputHasFocus(): boolean { + return this.searchInputBoxFocused.get(); + } + + public replaceInputHasFocus(): boolean { + return this.replaceInput.hasFocus(); + } + + private render(container: Builder, options: ISearchWidgetOptions): void { + this.domNode = container.div({ 'class': 'search-widget' }).style({ position: 'relative' }).getHTMLElement(); + this.renderToggleReplaceButton(this.domNode); + + this.renderSearchInput(this.domNode, options); + this.renderReplaceInput(this.domNode); + } + + private renderToggleReplaceButton(parent: HTMLElement): void { + this.toggleReplaceButton = this._register(new Button(parent)); + attachButtonStyler(this.toggleReplaceButton, this.themeService, { + buttonBackground: SIDE_BAR_BACKGROUND, + buttonHoverBackground: SIDE_BAR_BACKGROUND + }); + this.toggleReplaceButton.icon = 'toggle-replace-button collapse'; + this.toggleReplaceButton.addListener('click', () => this.onToggleReplaceButton()); + this.toggleReplaceButton.getElement().title = nls.localize('search.replace.toggle.button.title', "Toggle Replace"); + } + + private renderSearchInput(parent: HTMLElement, options: ISearchWidgetOptions): void { + let inputOptions: IFindInputOptions = { + label: nls.localize('label.Search', 'Search: Type Search Term and press Enter to search or Escape to cancel'), + validation: (value: string) => this.validatSearchInput(value), + placeholder: nls.localize('search.placeHolder', "Search"), + appendCaseSensitiveLabel: appendKeyBindingLabel('', this.keyBindingService2.lookupKeybinding(Constants.ToggleCaseSensitiveActionId), this.keyBindingService2), + appendWholeWordsLabel: appendKeyBindingLabel('', this.keyBindingService2.lookupKeybinding(Constants.ToggleWholeWordActionId), this.keyBindingService2), + appendRegexLabel: appendKeyBindingLabel('', this.keyBindingService2.lookupKeybinding(Constants.ToggleRegexActionId), this.keyBindingService2) + }; + + let searchInputContainer = dom.append(parent, dom.$('.search-container.input-box')); + this.searchInput = this._register(new FindInput(searchInputContainer, this.contextViewService, inputOptions)); + this._register(attachFindInputBoxStyler(this.searchInput, this.themeService)); + this.searchInput.onKeyUp((keyboardEvent: IKeyboardEvent) => this.onSearchInputKeyUp(keyboardEvent)); + this.searchInput.setValue(options.value || ''); + this.searchInput.setRegex(!!options.isRegex); + this.searchInput.setCaseSensitive(!!options.isCaseSensitive); + this.searchInput.setWholeWords(!!options.isWholeWords); + this._register(this.onSearchSubmit(() => { + this.searchHistory.add(this.searchInput.getValue()); + })); + + this.searchInputFocusTracker = this._register(dom.trackFocus(this.searchInput.inputBox.inputElement)); + this._register(this.searchInputFocusTracker.addFocusListener(() => { + this.searchInputBoxFocused.set(true); + })); + this._register(this.searchInputFocusTracker.addBlurListener(() => { + this.searchInputBoxFocused.set(false); + })); + } + + private renderReplaceInput(parent: HTMLElement): void { + this.replaceContainer = dom.append(parent, dom.$('.replace-container.disabled')); + let replaceBox = dom.append(this.replaceContainer, dom.$('.input-box')); + this.replaceInput = this._register(new InputBox(replaceBox, this.contextViewService, { + ariaLabel: nls.localize('label.Replace', 'Replace: Type replace term and press Enter to preview or Escape to cancel'), + placeholder: nls.localize('search.replace.placeHolder', "Replace") + })); + this._register(attachInputBoxStyler(this.replaceInput, this.themeService)); + this.onkeyup(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyUp(keyboardEvent)); + this.replaceInput.onDidChange(() => this._onReplaceValueChanged.fire()); + this.searchInput.inputBox.onDidChange(() => this.onSearchInputChanged()); + + this.replaceAllAction = ReplaceAllAction.INSTANCE; + this.replaceAllAction.searchWidget = this; + this.replaceAllAction.label = SearchWidget.REPLACE_ALL_DISABLED_LABEL; + this.replaceActionBar = this._register(new ActionBar(this.replaceContainer)); + this.replaceActionBar.push([this.replaceAllAction], { icon: true, label: false }); + + this.replaceInputFocusTracker = this._register(dom.trackFocus(this.replaceInput.inputElement)); + this._register(this.replaceInputFocusTracker.addFocusListener(() => { + this.replaceInputBoxFocused.set(true); + })); + this._register(this.replaceInputFocusTracker.addBlurListener(() => { + this.replaceInputBoxFocused.set(false); + })); + } + + triggerReplaceAll(): TPromise { + this._onReplaceAll.fire(); + return TPromise.as(null); + } + + private onToggleReplaceButton(): void { + dom.toggleClass(this.replaceContainer, 'disabled'); + dom.toggleClass(this.toggleReplaceButton.getElement(), 'collapse'); + dom.toggleClass(this.toggleReplaceButton.getElement(), 'expand'); + this.updateReplaceActiveState(); + this._onReplaceToggled.fire(); + } + + public setReplaceAllActionState(enabled: boolean): void { + if (this.replaceAllAction.enabled !== enabled) { + this.replaceAllAction.enabled = enabled; + this.replaceAllAction.label = enabled ? SearchWidget.REPLACE_ALL_ENABLED_LABEL(this.keyBindingService2) : SearchWidget.REPLACE_ALL_DISABLED_LABEL; + this.updateReplaceActiveState(); + } + } + + private isReplaceActive(): boolean { + return this.replaceActive.get(); + } + + private updateReplaceActiveState(): void { + let currentState = this.isReplaceActive(); + let newState = this.isReplaceShown() && this.replaceAllAction.enabled; + if (currentState !== newState) { + this.replaceActive.set(newState); + this._onReplaceStateChange.fire(newState); + } + } + + private validatSearchInput(value: string): any { + if (value.length === 0) { + return null; + } + if (!this.searchInput.getRegex()) { + return null; + } + let regExp: RegExp; + try { + regExp = new RegExp(value); + } catch (e) { + return { content: e.message }; + } + if (strings.regExpLeadsToEndlessLoop(regExp)) { + return { content: nls.localize('regexp.validationFailure', "Expression matches everything") }; + } + } + + private onSearchInputChanged(): void { + this.setReplaceAllActionState(false); + } + + private onSearchInputKeyUp(keyboardEvent: IKeyboardEvent) { + switch (keyboardEvent.keyCode) { + case KeyCode.Enter: + this.submitSearch(); + return; + case KeyCode.Escape: + this._onSearchCancel.fire(); + return; + default: + return; + } + } + + private onReplaceInputKeyUp(keyboardEvent: IKeyboardEvent) { + switch (keyboardEvent.keyCode) { + case KeyCode.Enter: + this.submitSearch(); + return; + default: + return; + } + } + + private submitSearch(refresh: boolean = true): void { + if (this.searchInput.getValue()) { + this._onSearchSubmit.fire(refresh); + } + } + + public dispose(): void { + this.setReplaceAllActionState(false); + this.replaceAllAction.searchWidget = null; + this.replaceActionBar = null; + super.dispose(); + } +} + +export function registerContributions() { + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: ReplaceAllAction.ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceActiveKey, CONTEXT_FIND_WIDGET_NOT_VISIBLE), + primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.Enter, + handler: accessor => { + if (isSearchViewletFocused(accessor.get(IViewletService))) { + ReplaceAllAction.INSTANCE.run(); + } + } + }); +} diff --git a/src/vs/workbench/parts/search/common/constants.ts b/src/vs/workbench/parts/search/common/constants.ts new file mode 100644 index 0000000000..a47ce6c410 --- /dev/null +++ b/src/vs/workbench/parts/search/common/constants.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const VIEWLET_ID = 'workbench.view.search'; + +export const FindInFilesActionId = 'workbench.action.findInFiles'; +export const FocusActiveEditorActionId = 'search.action.focusActiveEditor'; + +export const FocusSearchFromResults = 'search.action.focusSearchFromResults'; +export const OpenMatchToSide = 'search.action.openResultToSide'; +export const CancelActionId = 'search.action.cancel'; +export const RemoveActionId = 'search.action.remove'; +export const ReplaceActionId = 'search.action.replace'; +export const ReplaceAllInFileActionId = 'search.action.replaceAllInFile'; +export const ToggleCaseSensitiveActionId = 'toggleSearchCaseSensitive'; +export const ToggleWholeWordActionId = 'toggleSearchWholeWord'; +export const ToggleRegexActionId = 'toggleSearchRegex'; +export const CloseReplaceWidgetActionId = 'closeReplaceInFilesWidget'; + +export const SearchViewletVisibleKey = new RawContextKey('searchViewletVisible', true); +export const InputBoxFocusedKey = new RawContextKey('inputBoxFocus', false); +export const SearchInputBoxFocusedKey = new RawContextKey('searchInputBoxFocus', false); +export const ReplaceInputBoxFocusedKey = new RawContextKey('replaceInputBoxFocus', false); +export const PatternIncludesFocusedKey = new RawContextKey('patternIncludesInputBoxFocus', false); +export const PatternExcludesFocusedKey = new RawContextKey('patternExcludesInputBoxFocus', false); +export const ReplaceActiveKey = new RawContextKey('replaceActive', false); + +export const FirstMatchFocusKey = new RawContextKey('firstMatchFocus', false); +export const FileMatchOrMatchFocusKey = new RawContextKey('fileMatchOrMatchFocus', false); +export const FileFocusKey = new RawContextKey('fileMatchFocus', false); +export const MatchFocusKey = new RawContextKey('matchFocus', false); \ No newline at end of file diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts new file mode 100644 index 0000000000..88ee4685fc --- /dev/null +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -0,0 +1,310 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import * as arrays from 'vs/base/common/arrays'; +import * as objects from 'vs/base/common/objects'; +import * as collections from 'vs/base/common/collections'; +import * as glob from 'vs/base/common/glob'; +import * as paths from 'vs/base/common/paths'; +import * as strings from 'vs/base/common/strings'; +import uri from 'vs/base/common/uri'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IPatternInfo, IQueryOptions, IFolderQuery, ISearchQuery, QueryType, ISearchConfiguration, getExcludes, pathIncludedInQuery } from 'vs/platform/search/common/search'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export interface ISearchPathPattern { + searchPath: uri; + pattern?: string; +} + +export interface ISearchPathsResult { + searchPaths?: ISearchPathPattern[]; + pattern?: glob.IExpression; +} + +export class QueryBuilder { + + constructor( + @IConfigurationService private configurationService: IConfigurationService, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService) { + } + + public text(contentPattern: IPatternInfo, folderResources?: uri[], options?: IQueryOptions): ISearchQuery { + return this.query(QueryType.Text, contentPattern, folderResources, options); + } + + public file(folderResources?: uri[], options?: IQueryOptions): ISearchQuery { + return this.query(QueryType.File, null, folderResources, options); + } + + private query(type: QueryType, contentPattern: IPatternInfo, folderResources?: uri[], options: IQueryOptions = {}): ISearchQuery { + let { searchPaths, pattern: includePattern } = this.parseSearchPaths(options.includePattern); + let excludePattern = this.parseExcludePattern(options.excludePattern); + + // Build folderQueries from searchPaths, if given, otherwise folderResources + let folderQueries = folderResources && folderResources.map(uri => this.getFolderQueryForRoot(uri, options)); + if (searchPaths && searchPaths.length) { + const allRootExcludes = folderQueries && this.mergeExcludesFromFolderQueries(folderQueries); + folderQueries = searchPaths.map(searchPath => this.getFolderQueryForSearchPath(searchPath)); + if (allRootExcludes) { + excludePattern = objects.mixin(excludePattern || Object.create(null), allRootExcludes); + } + } + + const useRipgrep = !folderResources || folderResources.every(folder => { + const folderConfig = this.configurationService.getConfiguration(undefined, { resource: folder }); + return folderConfig.search.useRipgrep; + }); + + const query = { + type, + folderQueries, + usingSearchPaths: !!(searchPaths && searchPaths.length), + extraFileResources: options.extraFileResources, + filePattern: options.filePattern, + excludePattern, + includePattern, + maxResults: options.maxResults, + sortByScore: options.sortByScore, + cacheKey: options.cacheKey, + contentPattern: contentPattern, + useRipgrep, + disregardIgnoreFiles: options.disregardIgnoreFiles, + disregardExcludeSettings: options.disregardExcludeSettings + }; + + // Filter extraFileResources against global include/exclude patterns - they are already expected to not belong to a workspace + let extraFileResources = options.extraFileResources && options.extraFileResources.filter(extraFile => pathIncludedInQuery(query, extraFile.fsPath)); + query.extraFileResources = extraFileResources && extraFileResources.length ? extraFileResources : undefined; + + return query; + } + + /** + * Take the includePattern as seen in the search viewlet, and split into components that look like searchPaths, and + * glob patterns. Glob patterns are expanded from 'foo/bar' to '{foo/bar/**, **\/foo/bar}. + * + * Public for test. + */ + public parseSearchPaths(pattern: string): ISearchPathsResult { + const isSearchPath = (segment: string) => { + // A segment is a search path if it is an absolute path or starts with ./ + return paths.isAbsolute(segment) || strings.startsWith(segment, './') || strings.startsWith(segment, '.\\'); + }; + + const segments = splitGlobPattern(pattern); + const groups = collections.groupBy(segments, + segment => isSearchPath(segment) ? 'searchPaths' : 'exprSegments'); + + const expandedExprSegments = (groups.exprSegments || []) + .map(p => { + if (p[0] === '.') { + p = '*' + p; // convert ".js" to "*.js" + } + + return expandGlobalGlob(p); + }); + const exprSegments = arrays.flatten(expandedExprSegments); + + const result: ISearchPathsResult = {}; + const searchPaths = this.expandSearchPathPatterns(groups.searchPaths); + if (searchPaths && searchPaths.length) { + result.searchPaths = searchPaths; + } + + const includePattern = patternListToIExpression(exprSegments); + if (includePattern) { + result.pattern = includePattern; + } + + return result; + } + + /** + * Takes the input from the excludePattern as seen in the searchViewlet. Runs the same algorithm as parseSearchPaths, + * but the result is a single IExpression that encapsulates all the exclude patterns. + */ + public parseExcludePattern(pattern: string): glob.IExpression | undefined { + const result = this.parseSearchPaths(pattern); + let excludeExpression = glob.getEmptyExpression(); + if (result.pattern) { + excludeExpression = objects.mixin(excludeExpression, result.pattern); + } + + if (result.searchPaths) { + result.searchPaths.forEach(searchPath => { + const excludeFsPath = searchPath.searchPath.fsPath; + const excludePath = searchPath.pattern ? + paths.join(excludeFsPath, searchPath.pattern) : + excludeFsPath; + + excludeExpression[excludePath] = true; + }); + } + + return Object.keys(excludeExpression).length ? excludeExpression : undefined; + } + + private mergeExcludesFromFolderQueries(folderQueries: IFolderQuery[]): glob.IExpression | undefined { + const mergedExcludes = folderQueries.reduce((merged: glob.IExpression, fq: IFolderQuery) => { + if (fq.excludePattern) { + objects.mixin(merged, this.getAbsoluteIExpression(fq.excludePattern, fq.folder.fsPath)); + } + + return merged; + }, Object.create(null)); + + // Don't return an empty IExpression + return Object.keys(mergedExcludes).length ? mergedExcludes : undefined; + } + + private getAbsoluteIExpression(expr: glob.IExpression, root: string): glob.IExpression { + return Object.keys(expr) + .reduce((absExpr: glob.IExpression, key: string) => { + if (expr[key] && !paths.isAbsolute(key)) { + const absPattern = paths.join(root, key); + absExpr[absPattern] = true; + } + + return absExpr; + }, Object.create(null)); + } + + private getExcludesForFolder(folderConfig: ISearchConfiguration, options: IQueryOptions): glob.IExpression | undefined { + return options.disregardExcludeSettings ? + undefined : + getExcludes(folderConfig); + } + + /** + * Split search paths (./ or absolute paths in the includePatterns) into absolute paths and globs applied to those paths + */ + private expandSearchPathPatterns(searchPaths: string[]): ISearchPathPattern[] { + if (!this.workspaceContextService.hasWorkspace() || !searchPaths || !searchPaths.length) { + // No workspace => ignore search paths + return []; + } + + const searchPathPatterns = arrays.flatten(searchPaths.map(searchPath => { + // 1 open folder => just resolve the search paths to absolute paths + const { pathPortion, globPortion } = splitGlobFromPath(searchPath); + const pathPortions = this.expandAbsoluteSearchPaths(pathPortion); + return pathPortions.map(searchPath => { + return { + searchPath: uri.file(searchPath), + pattern: globPortion + }; + }); + })); + + return searchPathPatterns.filter(arrays.uniqueFilter(searchPathPattern => searchPathPattern.searchPath.toString())); + } + + /** + * Takes a searchPath like `./a/foo` and expands it to absolute paths for all the workspaces it matches. + */ + private expandAbsoluteSearchPaths(searchPath: string): string[] { + if (paths.isAbsolute(searchPath)) { + return [paths.normalize(searchPath)]; + } + + const workspace = this.workspaceContextService.getWorkspace(); + if (this.workspaceContextService.hasFolderWorkspace()) { + return [paths.normalize( + paths.join(workspace.roots[0].fsPath, searchPath))]; + } else if (searchPath === './') { + return []; // ./ or ./**/foo makes sense for single-folder but not multi-folder workspaces + } else { + const relativeSearchPathMatch = searchPath.match(/\.[\/\\]([^\/\\]+)([\/\\].+)?/); + if (relativeSearchPathMatch) { + const searchPathRoot = relativeSearchPathMatch[1]; + const matchingRoots = workspace.roots.filter(root => paths.basename(root.fsPath) === searchPathRoot); + if (matchingRoots.length) { + return matchingRoots.map(root => { + return relativeSearchPathMatch[2] ? + paths.normalize(paths.join(root.fsPath, relativeSearchPathMatch[2])) : + root.fsPath; + }); + } else { + // No root folder with name + const searchPathNotFoundError = nls.localize('search.noWorkspaceWithName', "No folder in workspace with name: {0}", searchPathRoot); + throw new Error(searchPathNotFoundError); + } + } else { + // Malformed ./ search path, ignore + } + } + + return []; + } + + private getFolderQueryForSearchPath(searchPath: ISearchPathPattern): IFolderQuery { + const folder = searchPath.searchPath; + const folderConfig = this.configurationService.getConfiguration(undefined, { resource: folder }); + return { + folder, + includePattern: searchPath.pattern && patternListToIExpression([searchPath.pattern]), + fileEncoding: folderConfig.files && folderConfig.files.encoding + }; + } + + private getFolderQueryForRoot(folder: uri, options?: IQueryOptions): IFolderQuery { + const folderConfig = this.configurationService.getConfiguration(undefined, { resource: folder }); + return { + folder, + excludePattern: this.getExcludesForFolder(folderConfig, options), + fileEncoding: folderConfig.files && folderConfig.files.encoding + }; + } +} + +function splitGlobFromPath(searchPath: string): { pathPortion: string, globPortion?: string } { + const globCharMatch = searchPath.match(/[\*\{\}\(\)\[\]\?]/); + if (globCharMatch) { + const globCharIdx = globCharMatch.index; + const lastSlashMatch = searchPath.substr(0, globCharIdx).match(/[/|\\][^/\\]*$/); + if (lastSlashMatch) { + let pathPortion = searchPath.substr(0, lastSlashMatch.index); + if (!pathPortion.match(/[/\\]/)) { + // If the last slash was the only slash, then we now have '' or 'C:'. Append a slash. + pathPortion += '/'; + } + + return { + pathPortion, + globPortion: searchPath.substr(lastSlashMatch.index + 1) + }; + } + } + + // No glob char, or malformed + return { + pathPortion: searchPath + }; +} + +function patternListToIExpression(patterns: string[]): glob.IExpression { + return patterns.length ? + patterns.reduce((glob, cur) => { glob[cur] = true; return glob; }, Object.create(null)) : + undefined; +} + +function splitGlobPattern(pattern: string): string[] { + return glob.splitGlobAware(pattern, ',') + .map(s => s.trim()) + .filter(s => !!s.length); +} + +/** + * Note - we used {} here previously but ripgrep can't handle nested {} patterns. See https://github.com/Microsoft/vscode/issues/32761 + */ +function expandGlobalGlob(pattern: string): string[] { + return [ + `**/${pattern}/**`, + `**/${pattern}` + ]; +} diff --git a/src/vs/workbench/parts/search/common/replace.ts b/src/vs/workbench/parts/search/common/replace.ts new file mode 100644 index 0000000000..9924573f1d --- /dev/null +++ b/src/vs/workbench/parts/search/common/replace.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import { Match, FileMatch, FileMatchOrMatch } from 'vs/workbench/parts/search/common/searchModel'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IProgressRunner } from 'vs/platform/progress/common/progress'; + +export const IReplaceService = createDecorator('replaceService'); + +export interface IReplaceService { + + _serviceBrand: any; + + /** + * Replaces the given match in the file that match belongs to + */ + replace(match: Match): TPromise; + + /** + * Replace all the matches from the given file matches in the files + * You can also pass the progress runner to update the progress of replacing. + */ + replace(files: FileMatch[], progress?: IProgressRunner): TPromise; + + /** + * Opens the replace preview for given file match or match + */ + openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise; + + /** + * Update the replace preview for the given file. + * If `override` is `true`, then replace preview is constructed from source model + */ + updateReplacePreview(file: FileMatch, override?: boolean): TPromise; +} diff --git a/src/vs/workbench/parts/search/common/search.ts b/src/vs/workbench/parts/search/common/search.ts new file mode 100644 index 0000000000..738ee5ec14 --- /dev/null +++ b/src/vs/workbench/parts/search/common/search.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { onUnexpectedError, illegalArgument } from 'vs/base/common/errors'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { ISearchConfiguration } from 'vs/platform/search/common/search'; +import glob = require('vs/base/common/glob'); +import { SymbolInformation } from 'vs/editor/common/modes'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import URI from 'vs/base/common/uri'; +import { toResource } from 'vs/workbench/common/editor'; + +export interface IWorkspaceSymbolProvider { + provideWorkspaceSymbols(search: string): TPromise; + resolveWorkspaceSymbol?: (item: SymbolInformation) => TPromise; +} + +export namespace WorkspaceSymbolProviderRegistry { + + const _supports: IWorkspaceSymbolProvider[] = []; + + export function register(support: IWorkspaceSymbolProvider): IDisposable { + + if (support) { + _supports.push(support); + } + + return { + dispose() { + if (support) { + let idx = _supports.indexOf(support); + if (idx >= 0) { + _supports.splice(idx, 1); + support = undefined; + } + } + } + }; + } + + export function all(): IWorkspaceSymbolProvider[] { + return _supports.slice(0); + } +} + +export function getWorkspaceSymbols(query: string): TPromise<[IWorkspaceSymbolProvider, SymbolInformation[]][]> { + + const result: [IWorkspaceSymbolProvider, SymbolInformation[]][] = []; + + const promises = WorkspaceSymbolProviderRegistry.all().map(support => { + return support.provideWorkspaceSymbols(query).then(value => { + if (Array.isArray(value)) { + result.push([support, value]); + } + }, onUnexpectedError); + }); + + return TPromise.join(promises).then(_ => result); +} + +CommonEditorRegistry.registerLanguageCommand('_executeWorkspaceSymbolProvider', function (accessor, args: { query: string; }) { + let { query } = args; + if (typeof query !== 'string') { + throw illegalArgument(); + } + return getWorkspaceSymbols(query); +}); + +export interface IWorkbenchSearchConfiguration extends ISearchConfiguration { + search: { + quickOpen: { + includeSymbols: boolean; + }, + exclude: glob.IExpression, + useRipgrep: boolean, + useIgnoreFilesByDefault: boolean + }; +} + +/** + * Helper to return all opened editors with resources not belonging to the currently opened workspace. + */ +export function getOutOfWorkspaceEditorResources(editorGroupService: IEditorGroupService, contextService: IWorkspaceContextService): URI[] { + const resources: URI[] = []; + + editorGroupService.getStacksModel().groups.forEach(group => { + const editors = group.getEditors(); + editors.forEach(editor => { + const fileResource = toResource(editor, { supportSideBySide: true, filter: 'file' }); + if (fileResource && !contextService.isInsideWorkspace(fileResource)) { + resources.push(fileResource); + } + }); + }); + + return resources; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts new file mode 100644 index 0000000000..3c1d0053d6 --- /dev/null +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -0,0 +1,821 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import paths = require('vs/base/common/paths'); +import objects = require('vs/base/common/objects'); +import strings = require('vs/base/common/strings'); +import errors = require('vs/base/common/errors'); +import { RunOnceScheduler } from 'vs/base/common/async'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { TPromise, PPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { values, ResourceMap, TrieMap } from 'vs/base/common/map'; +import Event, { Emitter, fromPromise, stopwatch, any } from 'vs/base/common/event'; +import { ISearchService, ISearchProgressItem, ISearchComplete, ISearchQuery, IPatternInfo, IFileMatch } from 'vs/platform/search/common/search'; +import { ReplacePattern } from 'vs/platform/search/common/replace'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Range } from 'vs/editor/common/core/range'; +import { IModel, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness, FindMatch } from 'vs/editor/common/editorCommon'; +import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; +import { IProgressRunner } from 'vs/platform/progress/common/progress'; +import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; +import { themeColorFromId } from 'vs/platform/theme/common/themeService'; + +export class Match { + + private _lineText: string; + private _id: string; + private _range: Range; + + constructor(private _parent: FileMatch, text: string, lineNumber: number, offset: number, length: number) { + this._lineText = text; + this._range = new Range(1 + lineNumber, 1 + offset, 1 + lineNumber, 1 + offset + length); + this._id = this._parent.id() + '>' + lineNumber + '>' + offset + this.getMatchString(); + } + + public id(): string { + return this._id; + } + + public parent(): FileMatch { + return this._parent; + } + + public text(): string { + return this._lineText; + } + + public range(): Range { + return this._range; + } + + public preview(): { before: string; inside: string; after: string; } { + let before = this._lineText.substring(0, this._range.startColumn - 1), + inside = this.getMatchString(), + after = this._lineText.substring(this._range.endColumn - 1, Math.min(this._range.endColumn + 150, this._lineText.length)); + + before = strings.lcut(before, 26); + + return { + before, + inside, + after, + }; + } + + public get replaceString(): string { + let searchModel = this.parent().parent().searchModel; + let matchString = this.getMatchString(); + let replaceString = searchModel.replacePattern.getReplaceString(matchString); + + // If match string is not matching then regex pattern has a lookahead expression + if (replaceString === null) { + replaceString = searchModel.replacePattern.getReplaceString(matchString + this._lineText.substring(this._range.endColumn - 1)); + } + + // Match string is still not matching. Could be unsupported matches (multi-line). + if (replaceString === null) { + replaceString = searchModel.replacePattern.pattern; + } + + return replaceString; + } + + public getMatchString(): string { + return this._lineText.substring(this._range.startColumn - 1, this._range.endColumn - 1); + } +} + +export class FileMatch extends Disposable { + + private static _CURRENT_FIND_MATCH = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'currentFindMatch', + overviewRuler: { + color: themeColorFromId(overviewRulerFindMatchForeground), + darkColor: themeColorFromId(overviewRulerFindMatchForeground), + position: OverviewRulerLane.Center + } + }); + + private static _FIND_MATCH = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'findMatch', + overviewRuler: { + color: themeColorFromId(overviewRulerFindMatchForeground), + darkColor: themeColorFromId(overviewRulerFindMatchForeground), + position: OverviewRulerLane.Center + } + }); + + private static getDecorationOption(selected: boolean): ModelDecorationOptions { + return (selected ? FileMatch._CURRENT_FIND_MATCH : FileMatch._FIND_MATCH); + } + + private _onChange = this._register(new Emitter()); + public onChange: Event = this._onChange.event; + + private _onDispose = this._register(new Emitter()); + public onDispose: Event = this._onDispose.event; + + private _resource: URI; + private _model: IModel; + private _modelListener: IDisposable; + private _matches: Map; + private _removedMatches: Set; + private _selectedMatch: Match; + + private _updateScheduler: RunOnceScheduler; + private _modelDecorations: string[] = []; + + constructor(private _query: IPatternInfo, private _maxResults: number, private _parent: FolderMatch, private rawMatch: IFileMatch, + @IModelService private modelService: IModelService, @IReplaceService private replaceService: IReplaceService) { + super(); + this._resource = this.rawMatch.resource; + this._matches = new Map(); + this._removedMatches = new Set(); + this._updateScheduler = new RunOnceScheduler(this.updateMatchesForModel.bind(this), 250); + + this.createMatches(); + this.registerListeners(); + } + + private createMatches(): void { + let model = this.modelService.getModel(this._resource); + if (model) { + this.bindModel(model); + this.updateMatchesForModel(); + } else { + this.rawMatch.lineMatches.forEach((rawLineMatch) => { + rawLineMatch.offsetAndLengths.forEach(offsetAndLength => { + let match = new Match(this, rawLineMatch.preview, rawLineMatch.lineNumber, offsetAndLength[0], offsetAndLength[1]); + this.add(match); + }); + }); + } + } + + private registerListeners(): void { + this._register(this.modelService.onModelAdded((model: IModel) => { + if (model.uri.toString() === this._resource.toString()) { + this.bindModel(model); + } + })); + } + + private bindModel(model: IModel): void { + this._model = model; + this._modelListener = this._model.onDidChangeContent(() => { + this._updateScheduler.schedule(); + }); + this._model.onWillDispose(() => this.onModelWillDispose()); + this.updateHighlights(); + } + + private onModelWillDispose(): void { + // Update matches because model might have some dirty changes + this.updateMatchesForModel(); + this.unbindModel(); + } + + private unbindModel(): void { + if (this._model) { + this._updateScheduler.cancel(); + this._model.deltaDecorations(this._modelDecorations, []); + this._model = null; + this._modelListener.dispose(); + } + } + + private updateMatchesForModel(): void { + // this is called from a timeout and might fire + // after the model has been disposed + if (!this._model) { + return; + } + this._matches = new Map(); + let matches = this._model + .findMatches(this._query.pattern, this._model.getFullModelRange(), this._query.isRegExp, this._query.isCaseSensitive, this._query.isWordMatch ? this._query.wordSeparators : null, false, this._maxResults); + + this.updateMatches(matches, true); + } + + private updatesMatchesForLineAfterReplace(lineNumber: number, modelChange: boolean): void { + const range = { + startLineNumber: lineNumber, + startColumn: this._model.getLineMinColumn(lineNumber), + endLineNumber: lineNumber, + endColumn: this._model.getLineMaxColumn(lineNumber) + }; + const oldMatches = values(this._matches).filter(match => match.range().startLineNumber === lineNumber); + oldMatches.forEach(match => this._matches.delete(match.id())); + + const matches = this._model.findMatches(this._query.pattern, range, this._query.isRegExp, this._query.isCaseSensitive, this._query.isWordMatch ? this._query.wordSeparators : null, false, this._maxResults); + this.updateMatches(matches, modelChange); + } + + private updateMatches(matches: FindMatch[], modelChange: boolean) { + matches.forEach(m => { + let match = new Match(this, this._model.getLineContent(m.range.startLineNumber), m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endColumn - m.range.startColumn); + if (!this._removedMatches.has(match.id())) { + this.add(match); + if (this.isMatchSelected(match)) { + this._selectedMatch = match; + } + } + }); + + this._onChange.fire(modelChange); + this.updateHighlights(); + } + + public updateHighlights(): void { + if (!this._model) { + return; + } + + if (this.parent().showHighlights) { + this._modelDecorations = this._model.deltaDecorations(this._modelDecorations, this.matches().map(match => { + range: match.range(), + options: FileMatch.getDecorationOption(this.isMatchSelected(match)) + })); + } else { + this._modelDecorations = this._model.deltaDecorations(this._modelDecorations, []); + } + } + + public id(): string { + return this.resource().toString(); + } + + public parent(): FolderMatch { + return this._parent; + } + + public matches(): Match[] { + return values(this._matches); + } + + public remove(match: Match): void { + this.removeMatch(match); + this._removedMatches.add(match.id()); + this._onChange.fire(false); + } + + public replace(toReplace: Match): TPromise { + return this.replaceService.replace(toReplace) + .then(() => this.updatesMatchesForLineAfterReplace(toReplace.range().startLineNumber, false)); + } + + public setSelectedMatch(match: Match) { + if (match) { + if (!this._matches.has(match.id())) { + return; + } + if (this.isMatchSelected(match)) { + return; + } + } + this._selectedMatch = match; + this.updateHighlights(); + } + + public getSelectedMatch(): Match { + return this._selectedMatch; + } + + public isMatchSelected(match: Match): boolean { + return this._selectedMatch && this._selectedMatch.id() === match.id(); + } + + public count(): number { + return this.matches().length; + } + + public resource(): URI { + return this._resource; + } + + public name(): string { + return paths.basename(this.resource().fsPath); + } + + public add(match: Match, trigger?: boolean) { + this._matches.set(match.id(), match); + if (trigger) { + this._onChange.fire(true); + } + } + + private removeMatch(match: Match) { + this._matches.delete(match.id()); + if (this.isMatchSelected(match)) { + this.setSelectedMatch(null); + } else { + this.updateHighlights(); + } + } + + public dispose(): void { + this.setSelectedMatch(null); + this.unbindModel(); + this._onDispose.fire(); + super.dispose(); + } +} + +export interface IChangeEvent { + elements: FileMatch[]; + added?: boolean; + removed?: boolean; +} + +export class FolderMatch extends Disposable { + + private _onChange = this._register(new Emitter()); + public onChange: Event = this._onChange.event; + + private _onDispose = this._register(new Emitter()); + public onDispose: Event = this._onDispose.event; + + private _fileMatches: ResourceMap; + private _unDisposedFileMatches: ResourceMap; + private _replacingAll: boolean = false; + + constructor(private _resource: URI, private _id: string, private _index: number, private _query: ISearchQuery, private _parent: SearchResult, private _searchModel: SearchModel, @IReplaceService private replaceService: IReplaceService, @ITelemetryService private telemetryService: ITelemetryService, + @IInstantiationService private instantiationService: IInstantiationService) { + super(); + this._fileMatches = new ResourceMap(); + this._unDisposedFileMatches = new ResourceMap(); + } + + public get searchModel(): SearchModel { + return this._searchModel; + } + + public get showHighlights(): boolean { + return this._parent.showHighlights; + } + + public set replacingAll(b: boolean) { + this._replacingAll = b; + } + + public id(): string { + return this._id; + } + + public resource(): URI { + return this._resource; + } + + public index(): number { + return this._index; + } + + public name(): string { + return paths.basename(this.resource().fsPath); + } + + public parent(): SearchResult { + return this._parent; + } + + public hasRoot(): boolean { + return this._resource.fsPath !== ''; + } + + public add(raw: IFileMatch[], silent: boolean): void { + let changed: FileMatch[] = []; + raw.forEach((rawFileMatch) => { + if (!this._fileMatches.has(rawFileMatch.resource)) { + let fileMatch = this.instantiationService.createInstance(FileMatch, this._query.contentPattern, this._query.maxResults, this, rawFileMatch); + this.doAdd(fileMatch); + changed.push(fileMatch); + let disposable = fileMatch.onChange(() => this.onFileChange(fileMatch)); + fileMatch.onDispose(() => disposable.dispose()); + } + }); + if (!silent && changed.length) { + this._onChange.fire({ elements: changed, added: true }); + } + } + + public clear(): void { + let changed: FileMatch[] = this.matches(); + this.disposeMatches(); + this._onChange.fire({ elements: changed, removed: true }); + } + + public remove(match: FileMatch): void { + this.doRemove(match); + } + + public replace(match: FileMatch): TPromise { + return this.replaceService.replace([match]).then(() => { + this.doRemove(match, false, true); + }); + } + + public matches(): FileMatch[] { + return this._fileMatches.values(); + } + + public isEmpty(): boolean { + return this.fileCount() === 0; + } + + public fileCount(): number { + return this._fileMatches.size; + } + + public count(): number { + return this.matches().reduce((prev, match) => prev + match.count(), 0); + } + + private onFileChange(fileMatch: FileMatch): void { + let added: boolean = false; + let removed: boolean = false; + if (!this._fileMatches.has(fileMatch.resource())) { + this.doAdd(fileMatch); + added = true; + } + if (fileMatch.count() === 0) { + this.doRemove(fileMatch, false, false); + added = false; + removed = true; + } + if (!this._replacingAll) { + this._onChange.fire({ elements: [fileMatch], added: added, removed: removed }); + } + } + + private doAdd(fileMatch: FileMatch): void { + this._fileMatches.set(fileMatch.resource(), fileMatch); + if (this._unDisposedFileMatches.has(fileMatch.resource())) { + this._unDisposedFileMatches.delete(fileMatch.resource()); + } + } + + private doRemove(fileMatch: FileMatch, dispose: boolean = true, trigger: boolean = true): void { + this._fileMatches.delete(fileMatch.resource()); + if (dispose) { + fileMatch.dispose(); + } else { + this._unDisposedFileMatches.set(fileMatch.resource(), fileMatch); + } + + if (trigger) { + this._onChange.fire({ elements: [fileMatch], removed: true }); + } + } + + private disposeMatches(): void { + this._fileMatches.values().forEach((fileMatch: FileMatch) => fileMatch.dispose()); + this._unDisposedFileMatches.values().forEach((fileMatch: FileMatch) => fileMatch.dispose()); + this._fileMatches.clear(); + this._unDisposedFileMatches.clear(); + } + + public dispose(): void { + this.disposeMatches(); + this._onDispose.fire(); + super.dispose(); + } +} + +export class SearchResult extends Disposable { + + private _onChange = this._register(new Emitter()); + public onChange: Event = this._onChange.event; + + private _folderMatches: FolderMatch[] = []; + private _folderMatchesMap: TrieMap = new TrieMap(); + private _query: ISearchQuery = null; + private _showHighlights: boolean; + + private _rangeHighlightDecorations: RangeHighlightDecorations; + + constructor(private _searchModel: SearchModel, @IReplaceService private replaceService: IReplaceService, @ITelemetryService private telemetryService: ITelemetryService, + @IInstantiationService private instantiationService: IInstantiationService) { + super(); + this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); + } + + public set query(query: ISearchQuery) { + // When updating the query we could change the roots, so ensure we clean up the old roots first. + this.clear(); + this._query = query; + const otherFiles = URI.parse(''); + this._folderMatches = (query.folderQueries || []).map((fq) => fq.folder).concat([otherFiles]).map((resource, index) => { + const id = resource.toString() || 'otherFiles'; + const folderMatch = this.instantiationService.createInstance(FolderMatch, resource, id, index, query, this, this._searchModel); + const disposable = folderMatch.onChange((event) => this._onChange.fire(event)); + folderMatch.onDispose(() => disposable.dispose()); + return folderMatch; + }); + // otherFiles is the fallback for missing values in the TrieMap. So we do not insert it. + this._folderMatches.slice(0, this.folderMatches.length - 1) + .forEach(fm => this._folderMatchesMap.insert(fm.resource().fsPath, fm)); + } + + public get searchModel(): SearchModel { + return this._searchModel; + } + + public add(allRaw: IFileMatch[], silent: boolean = false): void { + // Split up raw into a list per folder so we can do a batch add per folder. + let rawPerFolder = new ResourceMap(); + this._folderMatches.forEach((folderMatch) => rawPerFolder.set(folderMatch.resource(), [])); + allRaw.forEach(rawFileMatch => { + let folderMatch = this.getFolderMatch(rawFileMatch.resource); + rawPerFolder.get(folderMatch.resource()).push(rawFileMatch); + }); + rawPerFolder.forEach((raw) => { + if (!raw.length) { + return; + } + let folderMatch = this.getFolderMatch(raw[0].resource); + folderMatch.add(raw, silent); + }); + } + + public clear(): void { + this._folderMatches.forEach((folderMatch) => folderMatch.clear()); + this.disposeMatches(); + } + + public remove(match: FileMatch | FolderMatch): void { + if (match instanceof FileMatch) { + this.getFolderMatch(match.resource()).remove(match); + } else { + match.clear(); + } + } + + public replace(match: FileMatch): TPromise { + return this.getFolderMatch(match.resource()).replace(match); + } + + public replaceAll(progressRunner: IProgressRunner): TPromise { + this.replacingAll = true; + + const promise = this.replaceService.replace(this.matches(), progressRunner); + const onDone = stopwatch(fromPromise(promise)); + onDone(duration => this.telemetryService.publicLog('replaceAll.started', { duration })); + + return promise.then(() => { + this.replacingAll = false; + this.clear(); + }, () => { + this.replacingAll = false; + }); + } + + public folderMatches(): FolderMatch[] { + return this._folderMatches.concat(); + } + + public matches(): FileMatch[] { + let matches: FileMatch[][] = []; + this._folderMatches.forEach((folderMatch) => { + matches.push(folderMatch.matches()); + }); + return [].concat(...matches); + } + + public isEmpty(): boolean { + return this._folderMatches.every((folderMatch) => folderMatch.isEmpty()); + } + + public fileCount(): number { + return this.folderMatches().reduce((prev, match) => prev + match.fileCount(), 0); + } + + public folderCount(): number { + return this.folderMatches().reduce((prev, match) => prev + (match.fileCount() > 0 ? 1 : 0), 0); + } + + public count(): number { + return this.matches().reduce((prev, match) => prev + match.count(), 0); + } + + public get showHighlights(): boolean { + return this._showHighlights; + } + + public toggleHighlights(value: boolean): void { + if (this._showHighlights === value) { + return; + } + this._showHighlights = value; + let selectedMatch: Match = null; + this.matches().forEach((fileMatch: FileMatch) => { + fileMatch.updateHighlights(); + if (!selectedMatch) { + selectedMatch = fileMatch.getSelectedMatch(); + } + }); + if (this._showHighlights && selectedMatch) { + this._rangeHighlightDecorations.highlightRange({ + resource: selectedMatch.parent().resource(), + range: selectedMatch.range() + }); + } else { + this._rangeHighlightDecorations.removeHighlightRange(); + } + } + + public get rangeHighlightDecorations(): RangeHighlightDecorations { + return this._rangeHighlightDecorations; + } + + private getFolderMatch(resource: URI): FolderMatch { + const folderMatch = this._folderMatchesMap.findSubstr(resource.fsPath); + return folderMatch ? folderMatch : this.otherFiles; + } + + private get otherFiles(): FolderMatch { + return this._folderMatches[this._folderMatches.length - 1]; + } + + private set replacingAll(running: boolean) { + this._folderMatches.forEach((folderMatch) => { + folderMatch.replacingAll = running; + }); + } + + private disposeMatches(): void { + this._folderMatches.forEach(folderMatch => folderMatch.dispose()); + this._folderMatches = []; + this._folderMatchesMap = new TrieMap(); + this._rangeHighlightDecorations.removeHighlightRange(); + } + + public dispose(): void { + this.disposeMatches(); + this._rangeHighlightDecorations.dispose(); + super.dispose(); + } +} + +export class SearchModel extends Disposable { + + private _searchResult: SearchResult; + private _searchQuery: ISearchQuery = null; + private _replaceActive: boolean = false; + private _replaceString: string = null; + private _replacePattern: ReplacePattern = null; + + private _onReplaceTermChanged: Emitter = this._register(new Emitter()); + public onReplaceTermChanged: Event = this._onReplaceTermChanged.event; + + private currentRequest: PPromise; + + constructor( @ISearchService private searchService: ISearchService, @ITelemetryService private telemetryService: ITelemetryService, @IInstantiationService private instantiationService: IInstantiationService) { + super(); + this._searchResult = this.instantiationService.createInstance(SearchResult, this); + } + + public isReplaceActive(): boolean { + return this._replaceActive; + } + + public set replaceActive(replaceActive: boolean) { + this._replaceActive = replaceActive; + } + + public get replacePattern(): ReplacePattern { + return this._replacePattern; + } + + public get replaceString(): string { + return this._replaceString; + } + + public set replaceString(replaceString: string) { + this._replaceString = replaceString; + if (this._searchQuery) { + this._replacePattern = new ReplacePattern(replaceString, this._searchQuery.contentPattern); + } + this._onReplaceTermChanged.fire(); + } + + public get searchResult(): SearchResult { + return this._searchResult; + } + + public search(query: ISearchQuery): PPromise { + this.cancelSearch(); + this._searchQuery = query; + this.currentRequest = this.searchService.search(this._searchQuery); + + this.searchResult.clear(); + + this._searchResult.query = this._searchQuery; + this._replacePattern = new ReplacePattern(this._replaceString, this._searchQuery.contentPattern); + + const onDone = fromPromise(this.currentRequest); + const progressEmitter = new Emitter(); + const onFirstRender = any(onDone, progressEmitter.event); + const onFirstRenderStopwatch = stopwatch(onFirstRender); + onFirstRenderStopwatch(duration => this.telemetryService.publicLog('searchResultsFirstRender', { duration })); + + const onDoneStopwatch = stopwatch(onDone); + const start = Date.now(); + + onDoneStopwatch(duration => this.telemetryService.publicLog('searchResultsFinished', { duration })); + + const currentRequest = this.currentRequest; + this.currentRequest.then( + value => this.onSearchCompleted(value, Date.now() - start), + e => this.onSearchError(e, Date.now() - start), + p => { + progressEmitter.fire(); + this.onSearchProgress(p); + } + ); + + // this.currentRequest may be completed (and nulled) immediately + return currentRequest; + } + + private onSearchCompleted(completed: ISearchComplete, duration: number): ISearchComplete { + this.currentRequest = null; + + if (completed) { + this._searchResult.add(completed.results, false); + } + const options: IPatternInfo = objects.assign({}, this._searchQuery.contentPattern); + delete options.pattern; + this.telemetryService.publicLog('searchResultsShown', { + count: this._searchResult.count(), + fileCount: this._searchResult.fileCount(), + options, + duration, + useRipgrep: this._searchQuery.useRipgrep + }); + return completed; + } + + private onSearchError(e: any, duration: number): void { + if (errors.isPromiseCanceledError(e)) { + this.onSearchCompleted(null, duration); + } + } + + private onSearchProgress(p: ISearchProgressItem): void { + if (p.resource) { + this._searchResult.add([p], true); + } + } + + public cancelSearch(): boolean { + if (this.currentRequest) { + this.currentRequest.cancel(); + this.currentRequest = null; + return true; + } + return false; + } + + public dispose(): void { + this.cancelSearch(); + this.searchResult.dispose(); + super.dispose(); + } +} + +export type FileMatchOrMatch = FileMatch | Match; + +export type RenderableMatch = FolderMatch | FileMatch | Match; + +export class SearchWorkbenchService implements ISearchWorkbenchService { + + _serviceBrand: any; + private _searchModel: SearchModel; + + constructor( @IInstantiationService private instantiationService: IInstantiationService) { + } + + get searchModel(): SearchModel { + if (!this._searchModel) { + this._searchModel = this.instantiationService.createInstance(SearchModel); + } + return this._searchModel; + } +} + +export const ISearchWorkbenchService = createDecorator('searchWorkbenchService'); + +export interface ISearchWorkbenchService { + _serviceBrand: any; + + readonly searchModel: SearchModel; +} diff --git a/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts b/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts new file mode 100644 index 0000000000..e344e644ce --- /dev/null +++ b/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts @@ -0,0 +1,203 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import * as errors from 'vs/base/common/errors'; +import * as objects from 'vs/base/common/objects'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { CacheState } from 'vs/workbench/parts/search/browser/openFileHandler'; +import { DeferredTPromise } from 'vs/base/test/common/utils'; +import { QueryType, ISearchQuery } from 'vs/platform/search/common/search'; + +suite('CacheState', () => { + + test('reuse old cacheKey until new cache is loaded', function () { + + const cache = new MockCache(); + + const first = createCacheState(cache); + const firstKey = first.cacheKey; + assert.strictEqual(first.isLoaded, false); + assert.strictEqual(first.isUpdating, false); + + first.load(); + assert.strictEqual(first.isLoaded, false); + assert.strictEqual(first.isUpdating, true); + + cache.loading[firstKey].complete(null); + assert.strictEqual(first.isLoaded, true); + assert.strictEqual(first.isUpdating, false); + + const second = createCacheState(cache, first); + second.load(); + assert.strictEqual(second.isLoaded, true); + assert.strictEqual(second.isUpdating, true); + assert.strictEqual(Object.keys(cache.disposing).length, 0); + assert.strictEqual(second.cacheKey, firstKey); // still using old cacheKey + + const secondKey = cache.cacheKeys[1]; + cache.loading[secondKey].complete(null); + assert.strictEqual(second.isLoaded, true); + assert.strictEqual(second.isUpdating, false); + assert.strictEqual(Object.keys(cache.disposing).length, 1); + assert.strictEqual(second.cacheKey, secondKey); + }); + + test('do not spawn additional load if previous is still loading', function () { + + const cache = new MockCache(); + + const first = createCacheState(cache); + const firstKey = first.cacheKey; + first.load(); + assert.strictEqual(first.isLoaded, false); + assert.strictEqual(first.isUpdating, true); + assert.strictEqual(Object.keys(cache.loading).length, 1); + + const second = createCacheState(cache, first); + second.load(); + assert.strictEqual(second.isLoaded, false); + assert.strictEqual(second.isUpdating, true); + assert.strictEqual(cache.cacheKeys.length, 2); + assert.strictEqual(Object.keys(cache.loading).length, 1); // still only one loading + assert.strictEqual(second.cacheKey, firstKey); + + cache.loading[firstKey].complete(null); + assert.strictEqual(second.isLoaded, true); + assert.strictEqual(second.isUpdating, false); + assert.strictEqual(Object.keys(cache.disposing).length, 0); + }); + + test('do not use previous cacheKey if query changed', function () { + + const cache = new MockCache(); + + const first = createCacheState(cache); + const firstKey = first.cacheKey; + first.load(); + cache.loading[firstKey].complete(null); + assert.strictEqual(first.isLoaded, true); + assert.strictEqual(first.isUpdating, false); + assert.strictEqual(Object.keys(cache.disposing).length, 0); + + cache.baseQuery.excludePattern = { '**/node_modules': true }; + const second = createCacheState(cache, first); + assert.strictEqual(second.isLoaded, false); + assert.strictEqual(second.isUpdating, false); + assert.strictEqual(Object.keys(cache.disposing).length, 1); + + second.load(); + assert.strictEqual(second.isLoaded, false); + assert.strictEqual(second.isUpdating, true); + assert.notStrictEqual(second.cacheKey, firstKey); // not using old cacheKey + const secondKey = cache.cacheKeys[1]; + assert.strictEqual(second.cacheKey, secondKey); + + cache.loading[secondKey].complete(null); + assert.strictEqual(second.isLoaded, true); + assert.strictEqual(second.isUpdating, false); + assert.strictEqual(Object.keys(cache.disposing).length, 1); + }); + + test('dispose propagates', function () { + + const cache = new MockCache(); + + const first = createCacheState(cache); + const firstKey = first.cacheKey; + first.load(); + cache.loading[firstKey].complete(null); + const second = createCacheState(cache, first); + assert.strictEqual(second.isLoaded, true); + assert.strictEqual(second.isUpdating, false); + assert.strictEqual(Object.keys(cache.disposing).length, 0); + + second.dispose(); + assert.strictEqual(second.isLoaded, false); + assert.strictEqual(second.isUpdating, false); + assert.strictEqual(Object.keys(cache.disposing).length, 1); + assert.ok(cache.disposing[firstKey]); + }); + + test('keep using old cacheKey when loading fails', function () { + + const cache = new MockCache(); + + const first = createCacheState(cache); + const firstKey = first.cacheKey; + first.load(); + cache.loading[firstKey].complete(null); + + const second = createCacheState(cache, first); + second.load(); + const secondKey = cache.cacheKeys[1]; + const origErrorHandler = errors.errorHandler.getUnexpectedErrorHandler(); + try { + errors.setUnexpectedErrorHandler(() => null); + cache.loading[secondKey].error('loading failed'); + } finally { + errors.setUnexpectedErrorHandler(origErrorHandler); + } + assert.strictEqual(second.isLoaded, true); + assert.strictEqual(second.isUpdating, false); + assert.strictEqual(Object.keys(cache.loading).length, 2); + assert.strictEqual(Object.keys(cache.disposing).length, 0); + assert.strictEqual(second.cacheKey, firstKey); // keep using old cacheKey + + const third = createCacheState(cache, second); + third.load(); + assert.strictEqual(third.isLoaded, true); + assert.strictEqual(third.isUpdating, true); + assert.strictEqual(Object.keys(cache.loading).length, 3); + assert.strictEqual(Object.keys(cache.disposing).length, 0); + assert.strictEqual(third.cacheKey, firstKey); + + const thirdKey = cache.cacheKeys[2]; + cache.loading[thirdKey].complete(null); + assert.strictEqual(third.isLoaded, true); + assert.strictEqual(third.isUpdating, false); + assert.strictEqual(Object.keys(cache.loading).length, 3); + assert.strictEqual(Object.keys(cache.disposing).length, 2); + assert.strictEqual(third.cacheKey, thirdKey); // recover with next successful load + }); + + function createCacheState(cache: MockCache, previous?: CacheState): CacheState { + return new CacheState( + cacheKey => cache.query(cacheKey), + query => cache.load(query), + cacheKey => cache.dispose(cacheKey), + previous + ); + } + + class MockCache { + + public cacheKeys: string[] = []; + public loading: { [cacheKey: string]: DeferredTPromise } = {}; + public disposing: { [cacheKey: string]: DeferredTPromise } = {}; + + public baseQuery: ISearchQuery = { + type: QueryType.File + }; + + public query(cacheKey: string): ISearchQuery { + this.cacheKeys.push(cacheKey); + return objects.assign({ cacheKey: cacheKey }, this.baseQuery); + } + + public load(query: ISearchQuery): TPromise { + const promise = new DeferredTPromise(); + this.loading[query.cacheKey] = promise; + return promise; + } + + public dispose(cacheKey: string): TPromise { + const promise = new DeferredTPromise(); + this.disposing[cacheKey] = promise; + return promise; + } + } +}); diff --git a/src/vs/workbench/parts/search/test/browser/searchActions.test.ts b/src/vs/workbench/parts/search/test/browser/searchActions.test.ts new file mode 100644 index 0000000000..283f07cad2 --- /dev/null +++ b/src/vs/workbench/parts/search/test/browser/searchActions.test.ts @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { TestInstantiationService, stubFunction } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { Match, FileMatch, FileMatchOrMatch } from 'vs/workbench/parts/search/common/searchModel'; +import { ReplaceAction } from 'vs/workbench/parts/search/browser/searchActions'; +import { ArrayNavigator } from 'vs/base/common/iterator'; +import { IFileMatch } from 'vs/platform/search/common/search'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; +import { OS } from 'vs/base/common/platform'; +import { Keybinding } from 'vs/base/common/keyCodes'; + +suite('Search Actions', () => { + + let instantiationService: TestInstantiationService; + let counter: number; + + setup(() => { + instantiationService = new TestInstantiationService(); + instantiationService.stub(IModelService, stubModelService(instantiationService)); + instantiationService.stub(IKeybindingService, {}); + instantiationService.stub(IKeybindingService, 'resolveKeybinding', (keybinding: Keybinding) => [new USLayoutResolvedKeybinding(keybinding, OS)]); + instantiationService.stub(IKeybindingService, 'lookupKeybinding', (id: string) => null); + counter = 0; + }); + + test('get next element to focus after removing a match when it has next sibling match', function () { + let fileMatch1 = aFileMatch(); + let fileMatch2 = aFileMatch(); + let data = [fileMatch1, aMatch(fileMatch1), aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), aMatch(fileMatch2)]; + let tree = aTree(data); + let target = data[2]; + let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); + + let actual = testObject.getElementToFocusAfterRemoved(tree, target); + + assert.equal(data[3], actual); + }); + + test('get next element to focus after removing a match when it does not have next sibling match', function () { + let fileMatch1 = aFileMatch(); + let fileMatch2 = aFileMatch(); + let data = [fileMatch1, aMatch(fileMatch1), aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), aMatch(fileMatch2)]; + let tree = aTree(data); + let target = data[5]; + let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); + + let actual = testObject.getElementToFocusAfterRemoved(tree, target); + + assert.equal(data[4], actual); + }); + + test('get next element to focus after removing a match when it does not have next sibling match and previous match is file match', function () { + let fileMatch1 = aFileMatch(); + let fileMatch2 = aFileMatch(); + let data = [fileMatch1, aMatch(fileMatch1), aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2)]; + let tree = aTree(data); + let target = data[4]; + let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); + + let actual = testObject.getElementToFocusAfterRemoved(tree, target); + + assert.equal(data[2], actual); + }); + + test('get next element to focus after removing a match when it is the only match', function () { + let fileMatch1 = aFileMatch(); + let data = [fileMatch1, aMatch(fileMatch1)]; + let tree = aTree(data); + let target = data[1]; + let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); + + let actual = testObject.getElementToFocusAfterRemoved(tree, target); + + assert.equal(void 0, actual); + }); + + test('get next element to focus after removing a file match when it has next sibling', function () { + let fileMatch1 = aFileMatch(); + let fileMatch2 = aFileMatch(); + let fileMatch3 = aFileMatch(); + let data = [fileMatch1, aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), fileMatch3, aMatch(fileMatch3)]; + let tree = aTree(data); + let target = data[2]; + let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); + + let actual = testObject.getElementToFocusAfterRemoved(tree, target); + + assert.equal(data[4], actual); + }); + + test('get next element to focus after removing a file match when it has no next sibling', function () { + let fileMatch1 = aFileMatch(); + let fileMatch2 = aFileMatch(); + let fileMatch3 = aFileMatch(); + let data = [fileMatch1, aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), fileMatch3, aMatch(fileMatch3)]; + let tree = aTree(data); + let target = data[4]; + let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); + + let actual = testObject.getElementToFocusAfterRemoved(tree, target); + + assert.equal(data[3], actual); + }); + + test('get next element to focus after removing a file match when it is only match', function () { + let fileMatch1 = aFileMatch(); + let data = [fileMatch1, aMatch(fileMatch1)]; + let tree = aTree(data); + let target = data[0]; + let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null); + + let actual = testObject.getElementToFocusAfterRemoved(tree, target); + + assert.equal(void 0, actual); + }); + + function aFileMatch(): FileMatch { + let rawMatch: IFileMatch = { + resource: URI.file('somepath' + ++counter), + lineMatches: [] + }; + return instantiationService.createInstance(FileMatch, null, null, null, rawMatch); + } + + function aMatch(fileMatch: FileMatch): Match { + let match = new Match(fileMatch, 'some match', ++counter, 0, 2); + fileMatch.add(match); + return match; + } + + function aTree(elements: FileMatchOrMatch[]): any { + return stubFunction(Tree, 'getNavigator', () => { return new ArrayNavigator(elements); }); + } + + function stubModelService(instantiationService: TestInstantiationService): IModelService { + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + return instantiationService.createInstance(ModelServiceImpl); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts new file mode 100644 index 0000000000..8267671b6d --- /dev/null +++ b/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import uri from 'vs/base/common/uri'; +import { Match, FileMatch, SearchResult } from 'vs/workbench/parts/search/common/searchModel'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { SearchSorter, SearchDataSource } from 'vs/workbench/parts/search/browser/searchResultsView'; +import { IFileMatch, ILineMatch } from 'vs/platform/search/common/search'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { IModelService } from 'vs/editor/common/services/modelService'; + +suite('Search - Viewlet', () => { + let instantiation: TestInstantiationService; + + setup(() => { + instantiation = new TestInstantiationService(); + instantiation.stub(IModelService, stubModelService(instantiation)); + }); + + test('Data Source', function () { + let ds = new SearchDataSource(); + let result: SearchResult = instantiation.createInstance(SearchResult, null); + result.query = { type: 1, folderQueries: [{ folder: uri.parse('file://c:/') }] }; + result.add([{ + resource: uri.parse('file:///c:/foo'), + lineMatches: [{ lineNumber: 1, preview: 'bar', offsetAndLengths: [[0, 1]] }] + }]); + + let fileMatch = result.matches()[0]; + let lineMatch = fileMatch.matches()[0]; + + assert.equal(ds.getId(null, result), 'root'); + assert.equal(ds.getId(null, fileMatch), 'file:///c%3A/foo'); + assert.equal(ds.getId(null, lineMatch), 'file:///c%3A/foo>1>0b'); + + assert(!ds.hasChildren(null, 'foo')); + assert(ds.hasChildren(null, result)); + assert(ds.hasChildren(null, fileMatch)); + assert(!ds.hasChildren(null, lineMatch)); + }); + + test('Sorter', function () { + let fileMatch1 = aFileMatch('C:\\foo'); + let fileMatch2 = aFileMatch('C:\\with\\path'); + let fileMatch3 = aFileMatch('C:\\with\\path\\foo'); + let lineMatch1 = new Match(fileMatch1, 'bar', 1, 1, 1); + let lineMatch2 = new Match(fileMatch1, 'bar', 2, 1, 1); + let lineMatch3 = new Match(fileMatch1, 'bar', 2, 1, 1); + + let s = new SearchSorter(); + + assert(s.compare(null, fileMatch1, fileMatch2) < 0); + assert(s.compare(null, fileMatch2, fileMatch1) > 0); + assert(s.compare(null, fileMatch1, fileMatch1) === 0); + assert(s.compare(null, fileMatch2, fileMatch3) < 0); + + assert(s.compare(null, lineMatch1, lineMatch2) < 0); + assert(s.compare(null, lineMatch2, lineMatch1) > 0); + assert(s.compare(null, lineMatch2, lineMatch3) === 0); + }); + + function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ILineMatch[]): FileMatch { + let rawMatch: IFileMatch = { + resource: uri.file('C:\\' + path), + lineMatches: lineMatches + }; + return instantiation.createInstance(FileMatch, null, null, searchResult, rawMatch); + } + + function stubModelService(instantiationService: TestInstantiationService): IModelService { + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + return instantiationService.createInstance(ModelServiceImpl); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts new file mode 100644 index 0000000000..166dec77ef --- /dev/null +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -0,0 +1,682 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { IExpression } from 'vs/base/common/glob'; +import * as paths from 'vs/base/common/paths'; +import * as arrays from 'vs/base/common/arrays'; +import uri from 'vs/base/common/uri'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { QueryBuilder, ISearchPathsResult } from 'vs/workbench/parts/search/common/queryBuilder'; +import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; + +import { ISearchQuery, QueryType, IPatternInfo, IFolderQuery } from 'vs/platform/search/common/search'; + +suite('QueryBuilder', () => { + const PATTERN_INFO: IPatternInfo = { pattern: 'a' }; + const ROOT_1 = fixPath('/foo/root1'); + const ROOT_1_URI = getUri(ROOT_1); + + let instantiationService: TestInstantiationService; + let queryBuilder: QueryBuilder; + let mockConfigService: TestConfigurationService; + let mockContextService: TestContextService; + let mockWorkspace: Workspace; + + setup(() => { + instantiationService = new TestInstantiationService(); + + mockConfigService = new TestConfigurationService(); + mockConfigService.setUserConfiguration('search', { useRipgrep: true }); + instantiationService.stub(IConfigurationService, mockConfigService); + + mockContextService = new TestContextService(); + mockWorkspace = new Workspace('workspace', 'workspace', [ROOT_1_URI]); + mockContextService.setWorkspace(mockWorkspace); + instantiationService.stub(IWorkspaceContextService, mockContextService); + + queryBuilder = instantiationService.createInstance(QueryBuilder); + }); + + test('simple text pattern', () => { + assertEqualQueries( + queryBuilder.text(PATTERN_INFO), + { + contentPattern: PATTERN_INFO, + type: QueryType.Text, + useRipgrep: true + }); + }); + + test('folderResources', () => { + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI] + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ folder: ROOT_1_URI }], + type: QueryType.Text, + useRipgrep: true + }); + }); + + test('simple exclude setting', () => { + mockConfigService.setUserConfiguration('search', { + useRipgrep: true, + exclude: { + 'bar/**': true + } + }); + + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI] + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: ROOT_1_URI, + excludePattern: { 'bar/**': true } + }], + type: QueryType.Text, + useRipgrep: true + }); + }); + + test('simple include', () => { + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI], + { includePattern: './bar' } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: getUri(fixPath(paths.join(ROOT_1, 'bar'))) + }], + type: QueryType.Text, + useRipgrep: true + }); + + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI], + { includePattern: '.\\bar' } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: getUri(fixPath(paths.join(ROOT_1, 'bar'))) + }], + type: QueryType.Text, + useRipgrep: true + }); + }); + + test('exclude setting and searchPath', () => { + mockConfigService.setUserConfiguration('search', { + useRipgrep: true, + exclude: { + 'foo/**/*.js': true + } + }); + + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI], + { includePattern: './foo' } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: getUri(paths.join(ROOT_1, 'foo')) + }], + excludePattern: { [paths.join(ROOT_1, 'foo/**/*.js')]: true }, + type: QueryType.Text, + useRipgrep: true + }); + }); + + test('multiroot exclude settings', () => { + const ROOT_2 = fixPath('/project/root2'); + const ROOT_2_URI = getUri(ROOT_2); + const ROOT_3 = fixPath('/project/root3'); + const ROOT_3_URI = getUri(ROOT_3); + mockWorkspace.roots = [ROOT_1_URI, ROOT_2_URI, ROOT_3_URI]; + mockWorkspace.configuration = uri.file(fixPath('/config')); + + mockConfigService.setUserConfiguration('search', { + useRipgrep: true, + exclude: { 'foo/**/*.js': true } + }, ROOT_1_URI); + + mockConfigService.setUserConfiguration('search', { + useRipgrep: true, + exclude: { 'bar': true } + }, ROOT_2_URI); + + // There are 3 roots, the first two have search.exclude settings, test that the correct basic query is returned + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI, ROOT_2_URI, ROOT_3_URI] + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [ + { folder: ROOT_1_URI, excludePattern: patternsToIExpression('foo/**/*.js') }, + { folder: ROOT_2_URI, excludePattern: patternsToIExpression('bar') }, + { folder: ROOT_3_URI } + ], + type: QueryType.Text, + useRipgrep: true + } + ); + + // Now test that it merges the root excludes when an 'include' is used + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI, ROOT_2_URI, ROOT_3_URI], + { includePattern: './root2/src' } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [ + { folder: getUri(paths.join(ROOT_2, 'src')) } + ], + excludePattern: patternsToIExpression(paths.join(ROOT_1, 'foo/**/*.js'), paths.join(ROOT_2, 'bar')), + type: QueryType.Text, + useRipgrep: true + } + ); + }); + + test('simple exclude input pattern', () => { + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI], + { excludePattern: 'foo' } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: ROOT_1_URI + }], + type: QueryType.Text, + excludePattern: patternsToIExpression(...globalGlob('foo')), + useRipgrep: true + }); + }); + + test('exclude ./ syntax', () => { + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI], + { excludePattern: './bar' } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: ROOT_1_URI + }], + excludePattern: patternsToIExpression(fixPath(paths.join(ROOT_1, 'bar'))), + type: QueryType.Text, + useRipgrep: true + }); + + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI], + { excludePattern: './bar/**/*.ts' } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: ROOT_1_URI + }], + excludePattern: patternsToIExpression(fixPath(paths.join(ROOT_1, 'bar/**/*.ts'))), + type: QueryType.Text, + useRipgrep: true + }); + + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI], + { excludePattern: '.\\bar\\**\\*.ts' } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: ROOT_1_URI + }], + excludePattern: patternsToIExpression(fixPath(paths.join(ROOT_1, 'bar/**/*.ts'))), + type: QueryType.Text, + useRipgrep: true + }); + }); + + test('extraFileResources', () => { + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI], + { extraFileResources: [getUri('/foo/bar.js')] } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: ROOT_1_URI + }], + extraFileResources: [getUri('/foo/bar.js')], + type: QueryType.Text, + useRipgrep: true + }); + + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI], + { + extraFileResources: [getUri('/foo/bar.js')], + excludePattern: '*.js' + } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: ROOT_1_URI + }], + excludePattern: patternsToIExpression(...globalGlob('*.js')), + type: QueryType.Text, + useRipgrep: true + }); + + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + [ROOT_1_URI], + { + extraFileResources: [getUri('/foo/bar.js')], + includePattern: '*.txt' + } + ), + { + contentPattern: PATTERN_INFO, + folderQueries: [{ + folder: ROOT_1_URI + }], + includePattern: patternsToIExpression(...globalGlob('*.txt')), + type: QueryType.Text, + useRipgrep: true + }); + }); + + suite('parseSearchPaths', () => { + test('simple includes', () => { + function testSimpleIncludes(includePattern: string, expectedPatterns: string[]): void { + assert.deepEqual( + queryBuilder.parseSearchPaths(includePattern), + { + pattern: patternsToIExpression(...arrays.flatten(expectedPatterns.map(globalGlob))) + }, + includePattern); + } + + [ + ['a', ['a']], + ['a/b', ['a/b']], + ['a/b, c', ['a/b', 'c']], + ['a,.txt', ['a', '*.txt']], + ['a,,,b', ['a', 'b']], + ['**/a,b/**', ['**/a', 'b/**']] + ].forEach(([includePattern, expectedPatterns]) => testSimpleIncludes(includePattern, expectedPatterns)); + }); + + function testIncludes(includePattern: string, expectedResult: ISearchPathsResult): void { + assertEqualSearchPathResults( + queryBuilder.parseSearchPaths(includePattern), + expectedResult, + includePattern); + } + + function testIncludesDataItem([includePattern, expectedResult]): void { + testIncludes(includePattern, expectedResult); + } + + test('absolute includes', () => { + [ + [ + fixPath('/foo/bar'), + { + searchPaths: [{ searchPath: getUri('/foo/bar') }] + } + ], + [ + fixPath('/foo/bar') + ',' + 'a', + { + searchPaths: [{ searchPath: getUri('/foo/bar') }], + pattern: patternsToIExpression(...globalGlob('a')) + } + ], + [ + fixPath('/foo/bar') + ',' + fixPath('/1/2'), + { + searchPaths: [{ searchPath: getUri('/foo/bar') }, { searchPath: getUri('/1/2') }] + } + ], + [ + fixPath('/foo/bar') + ',' + fixPath('/foo/../foo/bar/fooar/..'), + { + searchPaths: [{ + searchPath: getUri('/foo/bar') + }] + } + ], + [ + fixPath('/foo/bar/**/*.ts'), + { + searchPaths: [{ + searchPath: getUri('/foo/bar'), + pattern: '**/*.ts' + }] + } + ], + [ + fixPath('/foo/bar/*a/b/c'), + { + searchPaths: [{ + searchPath: getUri('/foo/bar'), + pattern: '*a/b/c' + }] + } + ], + [ + fixPath('/*a/b/c'), + { + searchPaths: [{ + searchPath: getUri('/'), + pattern: '*a/b/c' + }] + } + ], + [ + fixPath('/foo/{b,c}ar'), + { + searchPaths: [{ + searchPath: getUri('/foo'), + pattern: '{b,c}ar' + }] + } + ] + ].forEach(testIncludesDataItem); + }); + + test('relative includes w/single root folder', () => { + [ + [ + './a', + { + searchPaths: [{ + searchPath: getUri(ROOT_1 + '/a') + }] + } + ], + [ + './a/*b/c', + { + searchPaths: [{ + searchPath: getUri(ROOT_1 + '/a'), + pattern: '*b/c' + }] + } + ], + [ + './a/*b/c, ' + fixPath('/project/foo'), + { + searchPaths: [ + { + searchPath: getUri(ROOT_1 + '/a'), + pattern: '*b/c' + }, + { + searchPath: getUri('/project/foo') + }] + } + ], + [ + './a/b/..,./a', + { + searchPaths: [{ + searchPath: getUri(ROOT_1 + '/a') + }] + } + ], + ].forEach(testIncludesDataItem); + }); + + test('relative includes w/two root folders', () => { + const ROOT_2 = '/project/root2'; + mockWorkspace.roots = [ROOT_1_URI, getUri(ROOT_2)]; + mockWorkspace.configuration = uri.file(fixPath('config')); + + [ + [ + './root1', + { + searchPaths: [{ + searchPath: getUri(ROOT_1) + }] + } + ], + [ + './root2', + { + searchPaths: [{ + searchPath: getUri(ROOT_2), + }] + } + ], + [ + './root1/a/**/b, ./root2/**/*.txt', + { + searchPaths: [ + { + searchPath: getUri(ROOT_1 + '/a'), + pattern: '**/b' + }, + { + searchPath: getUri(ROOT_2), + pattern: '**/*.txt' + }] + } + ] + ].forEach(testIncludesDataItem); + }); + + test('relative includes w/multiple ambiguous root folders', () => { + const ROOT_2 = '/project/rootB'; + const ROOT_3 = '/otherproject/rootB'; + mockWorkspace.roots = [ROOT_1_URI, getUri(ROOT_2), getUri(ROOT_3)]; + mockWorkspace.configuration = uri.file(fixPath('/config')); + + [ + [ + '', + { + searchPaths: undefined + } + ], + [ + './', + { + searchPaths: undefined + } + ], + [ + './root1', + { + searchPaths: [{ + searchPath: getUri(ROOT_1) + }] + } + ], + [ + './root1,./', + { + searchPaths: [{ + searchPath: getUri(ROOT_1) + }] + } + ], + [ + './rootB', + { + searchPaths: [ + { + searchPath: getUri(ROOT_2), + }, + { + searchPath: getUri(ROOT_3), + }] + } + ], + [ + './rootB/a/**/b, ./rootB/b/**/*.txt', + { + searchPaths: [ + { + searchPath: getUri(ROOT_2 + '/a'), + pattern: '**/b' + }, + { + searchPath: getUri(ROOT_3 + '/a'), + pattern: '**/b' + }, + { + searchPath: getUri(ROOT_2 + '/b'), + pattern: '**/*.txt' + }, + { + searchPath: getUri(ROOT_3 + '/b'), + pattern: '**/*.txt' + }] + } + ] + ].forEach(testIncludesDataItem); + }); + }); +}); + +function assertEqualQueries(actual: ISearchQuery, expected: ISearchQuery): void { + const folderQueryToCompareObject = (fq: IFolderQuery) => { + return { + path: fq.folder.fsPath, + excludePattern: normalizeExpression(fq.excludePattern), + includePattern: normalizeExpression(fq.includePattern), + fileEncoding: fq.fileEncoding + }; + }; + + // Avoid comparing URI objects, not a good idea + if (expected.folderQueries) { + assert.deepEqual(actual.folderQueries.map(folderQueryToCompareObject), expected.folderQueries.map(folderQueryToCompareObject)); + delete actual.folderQueries; + delete expected.folderQueries; + } + + if (expected.extraFileResources) { + assert.deepEqual(actual.extraFileResources.map(extraFile => extraFile.fsPath), expected.extraFileResources.map(extraFile => extraFile.fsPath)); + delete expected.extraFileResources; + delete actual.extraFileResources; + } + + delete actual.usingSearchPaths; + actual.includePattern = normalizeExpression(actual.includePattern); + actual.excludePattern = normalizeExpression(actual.excludePattern); + cleanUndefinedQueryValues(actual); + + assert.deepEqual(actual, expected); +} + +function assertEqualSearchPathResults(actual: ISearchPathsResult, expected: ISearchPathsResult, message?: string): void { + cleanUndefinedQueryValues(actual); + assert.deepEqual(actual.pattern, expected.pattern, message); + + assert.equal(actual.searchPaths && actual.searchPaths.length, expected.searchPaths && expected.searchPaths.length); + if (actual.searchPaths) { + actual.searchPaths.forEach((searchPath, i) => { + const expectedSearchPath = expected.searchPaths[i]; + assert.equal(searchPath.pattern, expectedSearchPath.pattern); + assert.equal(searchPath.searchPath.toString(), expectedSearchPath.searchPath.toString()); + }); + } +} + +/** + * Recursively delete all undefined property values from the search query, to make it easier to + * assert.deepEqual with some expected object. + */ +function cleanUndefinedQueryValues(q: any): void { + for (let key in q) { + if (q[key] === undefined) { + delete q[key]; + } else if (typeof q[key] === 'object') { + cleanUndefinedQueryValues(q[key]); + } + } + + return q; +} + +function globalGlob(pattern: string): string[] { + return [ + `**/${pattern}/**`, + `**/${pattern}` + ]; +} + +function patternsToIExpression(...patterns: string[]): IExpression { + return patterns.length ? + patterns.reduce((glob, cur) => { glob[cur] = true; return glob; }, Object.create(null)) : + undefined; +} + +function getUri(slashPath: string): uri { + return uri.file(fixPath(slashPath)); +} + +function fixPath(slashPath: string): string { + return process.platform === 'win32' ? + (slashPath.match(/^c:/) ? slashPath : paths.join('c:', ...slashPath.split('/'))) : + slashPath; +} + +function normalizeExpression(expression: IExpression): IExpression { + if (!expression) { + return expression; + } + + const normalized = Object.create(null); + Object.keys(expression).forEach(key => { + normalized[key.replace(/\\/g, '/')] = true; + }); + + return normalized; +} diff --git a/src/vs/workbench/parts/search/test/common/searchModel.test.ts b/src/vs/workbench/parts/search/test/common/searchModel.test.ts new file mode 100644 index 0000000000..206ac07f62 --- /dev/null +++ b/src/vs/workbench/parts/search/test/common/searchModel.test.ts @@ -0,0 +1,320 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { DeferredPPromise } from 'vs/base/test/common/utils'; +import { PPromise } from 'vs/base/common/winjs.base'; +import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; +import URI from 'vs/base/common/uri'; +import { IFileMatch, IFolderQuery, ILineMatch, ISearchService, ISearchComplete, ISearchProgressItem, IUncachedSearchStats } from 'vs/platform/search/common/search'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { Range } from 'vs/editor/common/core/range'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; + +const nullEvent = new class { + + public id: number; + public topic: string; + public name: string; + public description: string; + public data: any; + + public startTime: Date; + public stopTime: Date; + + public stop(): void { + return; + } + + public timeTaken(): number { + return -1; + } +}; + + +suite('SearchModel', () => { + + let instantiationService: TestInstantiationService; + let restoreStubs; + + const testSearchStats: IUncachedSearchStats = { + fromCache: false, + resultCount: 4, + traversal: 'node', + errors: [], + fileWalkStartTime: 0, + fileWalkResultTime: 1, + directoriesWalked: 2, + filesWalked: 3 + }; + + const folderQueries: IFolderQuery[] = [ + { folder: URI.parse('file://c:/') } + ]; + + setup(() => { + restoreStubs = []; + instantiationService = new TestInstantiationService(); + instantiationService.stub(ITelemetryService, NullTelemetryService); + instantiationService.stub(IModelService, stubModelService(instantiationService)); + instantiationService.stub(ISearchService, {}); + instantiationService.stub(ISearchService, 'search', PPromise.as({ results: [] })); + }); + + teardown(() => { + restoreStubs.forEach(element => { + element.restore(); + }); + }); + + test('Search Model: Search adds to results', function () { + let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))]; + instantiationService.stub(ISearchService, 'search', PPromise.as({ results: results })); + + let testObject: SearchModel = instantiationService.createInstance(SearchModel); + testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + + let actual = testObject.searchResult.matches(); + + assert.equal(2, actual.length); + assert.equal('file://c:/1', actual[0].resource().toString()); + + let actuaMatches = actual[0].matches(); + assert.equal(2, actuaMatches.length); + assert.equal('preview 1', actuaMatches[0].text()); + assert.ok(new Range(2, 2, 2, 5).equalsRange(actuaMatches[0].range())); + assert.equal('preview 1', actuaMatches[1].text()); + assert.ok(new Range(2, 5, 2, 12).equalsRange(actuaMatches[1].range())); + + actuaMatches = actual[1].matches(); + assert.equal(1, actuaMatches.length); + assert.equal('preview 2', actuaMatches[0].text()); + assert.ok(new Range(2, 1, 2, 2).equalsRange(actuaMatches[0].range())); + }); + + test('Search Model: Search adds to results during progress', function (done) { + let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))]; + let promise = new DeferredPPromise(); + instantiationService.stub(ISearchService, 'search', promise); + + let testObject = instantiationService.createInstance(SearchModel); + let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + + promise.progress(results[0]); + promise.progress(results[1]); + promise.complete({ results: [], stats: testSearchStats }); + + result.done(() => { + let actual = testObject.searchResult.matches(); + + assert.equal(2, actual.length); + assert.equal('file://c:/1', actual[0].resource().toString()); + + let actuaMatches = actual[0].matches(); + assert.equal(2, actuaMatches.length); + assert.equal('preview 1', actuaMatches[0].text()); + assert.ok(new Range(2, 2, 2, 5).equalsRange(actuaMatches[0].range())); + assert.equal('preview 1', actuaMatches[1].text()); + assert.ok(new Range(2, 5, 2, 12).equalsRange(actuaMatches[1].range())); + + actuaMatches = actual[1].matches(); + assert.equal(1, actuaMatches.length); + assert.equal('preview 2', actuaMatches[0].text()); + assert.ok(new Range(2, 1, 2, 2).equalsRange(actuaMatches[0].range())); + + done(); + }); + }); + + test('Search Model: Search reports telemetry on search completed', function () { + let target = instantiationService.spy(ITelemetryService, 'publicLog'); + let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))]; + instantiationService.stub(ISearchService, 'search', PPromise.as({ results: results })); + + let testObject = instantiationService.createInstance(SearchModel); + testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + + assert.ok(target.calledOnce); + const data = target.args[0]; + data[1].duration = -1; + assert.deepEqual(['searchResultsShown', { count: 3, fileCount: 2, options: {}, duration: -1, useRipgrep: undefined }], data); + }); + + test('Search Model: Search reports timed telemetry on search when progress is not called', function (done) { + let target2 = sinon.spy(); + stub(nullEvent, 'stop', target2); + let target1 = sinon.stub().returns(nullEvent); + instantiationService.stub(ITelemetryService, 'publicLog', target1); + + instantiationService.stub(ISearchService, 'search', PPromise.as({ results: [] })); + + let testObject = instantiationService.createInstance(SearchModel); + const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + + setTimeout(() => { + result.done(() => { + assert.ok(target1.calledWith('searchResultsFirstRender')); + assert.ok(target1.calledWith('searchResultsFinished')); + + done(); + }); + }, 0); + }); + + test('Search Model: Search reports timed telemetry on search when progress is called', function (done) { + let target2 = sinon.spy(); + stub(nullEvent, 'stop', target2); + let target1 = sinon.stub().returns(nullEvent); + instantiationService.stub(ITelemetryService, 'publicLog', target1); + + let promise = new DeferredPPromise(); + instantiationService.stub(ISearchService, 'search', promise); + + let testObject = instantiationService.createInstance(SearchModel); + let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + + promise.progress(aRawMatch('file://c:/1', aLineMatch('some preview'))); + promise.complete({ results: [], stats: testSearchStats }); + + setTimeout(() => { + result.done(() => { + assert.ok(target1.calledWith('searchResultsFirstRender')); + assert.ok(target1.calledWith('searchResultsFinished')); + // assert.equal(1, target2.callCount); + + done(); + }); + }, 0); + }); + + test('Search Model: Search reports timed telemetry on search when error is called', function (done) { + let target2 = sinon.spy(); + stub(nullEvent, 'stop', target2); + let target1 = sinon.stub().returns(nullEvent); + instantiationService.stub(ITelemetryService, 'publicLog', target1); + + let promise = new DeferredPPromise(); + instantiationService.stub(ISearchService, 'search', promise); + + let testObject = instantiationService.createInstance(SearchModel); + let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + + promise.error('error'); + + setTimeout(() => { + result.done(() => { }, () => { + assert.ok(target1.calledWith('searchResultsFirstRender')); + assert.ok(target1.calledWith('searchResultsFinished')); + // assert.ok(target2.calledOnce); + + done(); + }); + }, 0); + }); + + test('Search Model: Search reports timed telemetry on search when error is cancelled error', function (done) { + let target2 = sinon.spy(); + stub(nullEvent, 'stop', target2); + let target1 = sinon.stub().returns(nullEvent); + instantiationService.stub(ITelemetryService, 'publicLog', target1); + + let promise = new DeferredPPromise(); + instantiationService.stub(ISearchService, 'search', promise); + + let testObject = instantiationService.createInstance(SearchModel); + let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + + promise.cancel(); + + setTimeout(() => { + result.done(() => { }, () => { + assert.ok(target1.calledWith('searchResultsFirstRender')); + assert.ok(target1.calledWith('searchResultsFinished')); + // assert.ok(target2.calledOnce); + done(); + }); + }, 0); + }); + + test('Search Model: Search results are cleared during search', function () { + let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))]; + instantiationService.stub(ISearchService, 'search', PPromise.as({ results: results })); + let testObject: SearchModel = instantiationService.createInstance(SearchModel); + testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + assert.ok(!testObject.searchResult.isEmpty()); + + instantiationService.stub(ISearchService, 'search', new DeferredPPromise()); + + testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + assert.ok(testObject.searchResult.isEmpty()); + }); + + test('Search Model: Previous search is cancelled when new search is called', function () { + let target = sinon.spy(); + instantiationService.stub(ISearchService, 'search', new DeferredPPromise((c, e, p) => { }, target)); + let testObject: SearchModel = instantiationService.createInstance(SearchModel); + + testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + instantiationService.stub(ISearchService, 'search', new DeferredPPromise()); + testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); + + assert.ok(target.calledOnce); + }); + + test('getReplaceString returns proper replace string for regExpressions', function () { + let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]]))]; + instantiationService.stub(ISearchService, 'search', PPromise.as({ results: results })); + + let testObject: SearchModel = instantiationService.createInstance(SearchModel); + testObject.search({ contentPattern: { pattern: 're' }, type: 1, folderQueries }); + testObject.replaceString = 'hello'; + let match = testObject.searchResult.matches()[0].matches()[0]; + assert.equal('hello', match.replaceString); + + testObject.search({ contentPattern: { pattern: 're', isRegExp: true }, type: 1, folderQueries }); + match = testObject.searchResult.matches()[0].matches()[0]; + assert.equal('hello', match.replaceString); + + testObject.search({ contentPattern: { pattern: 're(?:vi)', isRegExp: true }, type: 1, folderQueries }); + match = testObject.searchResult.matches()[0].matches()[0]; + assert.equal('hello', match.replaceString); + + testObject.search({ contentPattern: { pattern: 'r(e)(?:vi)', isRegExp: true }, type: 1, folderQueries }); + match = testObject.searchResult.matches()[0].matches()[0]; + assert.equal('hello', match.replaceString); + + testObject.search({ contentPattern: { pattern: 'r(e)(?:vi)', isRegExp: true }, type: 1, folderQueries }); + testObject.replaceString = 'hello$1'; + match = testObject.searchResult.matches()[0].matches()[0]; + assert.equal('helloe', match.replaceString); + }); + + function aRawMatch(resource: string, ...lineMatches: ILineMatch[]): IFileMatch { + return { resource: URI.parse(resource), lineMatches }; + } + + function aLineMatch(preview: string, lineNumber: number = 1, offsetAndLengths: number[][] = [[0, 1]]): ILineMatch { + return { preview, lineNumber, offsetAndLengths }; + } + + function stub(arg1, arg2, arg3): sinon.SinonStub { + const stub = sinon.stub(arg1, arg2, arg3); + restoreStubs.push(stub); + return stub; + } + + function stubModelService(instantiationService: TestInstantiationService): IModelService { + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + return instantiationService.createInstance(ModelServiceImpl); + } + +}); diff --git a/src/vs/workbench/parts/search/test/common/searchResult.test.ts b/src/vs/workbench/parts/search/test/common/searchResult.test.ts new file mode 100644 index 0000000000..42ae8c2242 --- /dev/null +++ b/src/vs/workbench/parts/search/test/common/searchResult.test.ts @@ -0,0 +1,387 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { Match, FileMatch, SearchResult, SearchModel } from 'vs/workbench/parts/search/common/searchModel'; +import URI from 'vs/base/common/uri'; +import { IFileMatch, ILineMatch } from 'vs/platform/search/common/search'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { Range } from 'vs/editor/common/core/range'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; + +suite('SearchResult', () => { + + let instantiationService: TestInstantiationService; + + setup(() => { + instantiationService = new TestInstantiationService(); + instantiationService.stub(ITelemetryService, NullTelemetryService); + instantiationService.stub(IModelService, stubModelService(instantiationService)); + instantiationService.stubPromise(IReplaceService, {}); + instantiationService.stubPromise(IReplaceService, 'replace', null); + }); + + test('Line Match', function () { + let fileMatch = aFileMatch('folder/file.txt', null); + let lineMatch = new Match(fileMatch, 'foo bar', 1, 0, 3); + assert.equal(lineMatch.text(), 'foo bar'); + assert.equal(lineMatch.range().startLineNumber, 2); + assert.equal(lineMatch.range().endLineNumber, 2); + assert.equal(lineMatch.range().startColumn, 1); + assert.equal(lineMatch.range().endColumn, 4); + assert.equal('file:///folder/file.txt>1>0foo', lineMatch.id()); + }); + + test('Line Match - Remove', function () { + let fileMatch = aFileMatch('folder/file.txt', aSearchResult(), ...[{ + preview: 'foo bar', + lineNumber: 1, + offsetAndLengths: [[0, 3]] + }]); + let lineMatch = fileMatch.matches()[0]; + fileMatch.remove(lineMatch); + assert.equal(fileMatch.matches().length, 0); + }); + + test('File Match', function () { + let fileMatch = aFileMatch('folder/file.txt'); + assert.equal(fileMatch.matches(), 0); + assert.equal(fileMatch.resource().toString(), 'file:///folder/file.txt'); + assert.equal(fileMatch.name(), 'file.txt'); + + fileMatch = aFileMatch('file.txt'); + assert.equal(fileMatch.matches(), 0); + assert.equal(fileMatch.resource().toString(), 'file:///file.txt'); + assert.equal(fileMatch.name(), 'file.txt'); + }); + + test('File Match: Select an existing match', function () { + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ + preview: 'foo', + lineNumber: 1, + offsetAndLengths: [[0, 3]] + }, { + preview: 'bar', + lineNumber: 1, + offsetAndLengths: [[5, 3]] + }]); + + testObject.setSelectedMatch(testObject.matches()[0]); + + assert.equal(testObject.matches()[0], testObject.getSelectedMatch()); + }); + + test('File Match: Select non existing match', function () { + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ + preview: 'foo', + lineNumber: 1, + offsetAndLengths: [[0, 3]] + }, { + preview: 'bar', + lineNumber: 1, + offsetAndLengths: [[5, 3]] + }]); + let target = testObject.matches()[0]; + testObject.remove(target); + + testObject.setSelectedMatch(target); + + assert.equal(undefined, testObject.getSelectedMatch()); + }); + + test('File Match: isSelected return true for selected match', function () { + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ + preview: 'foo', + lineNumber: 1, + offsetAndLengths: [[0, 3]] + }, { + preview: 'bar', + lineNumber: 1, + offsetAndLengths: [[5, 3]] + }]); + let target = testObject.matches()[0]; + testObject.setSelectedMatch(target); + + assert.ok(testObject.isMatchSelected(target)); + }); + + test('File Match: isSelected return false for un-selected match', function () { + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ + preview: 'foo', + lineNumber: 1, + offsetAndLengths: [[0, 3]] + }, { + preview: 'bar', + lineNumber: 1, + offsetAndLengths: [[5, 3]] + }]); + + testObject.setSelectedMatch(testObject.matches()[0]); + + assert.ok(!testObject.isMatchSelected(testObject.matches()[1])); + }); + + test('File Match: unselect', function () { + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ + preview: 'foo', + lineNumber: 1, + offsetAndLengths: [[0, 3]] + }, { + preview: 'bar', + lineNumber: 1, + offsetAndLengths: [[5, 3]] + }]); + + testObject.setSelectedMatch(testObject.matches()[0]); + testObject.setSelectedMatch(null); + + assert.equal(null, testObject.getSelectedMatch()); + }); + + test('File Match: unselect when not selected', function () { + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ + preview: 'foo', + lineNumber: 1, + offsetAndLengths: [[0, 3]] + }, { + preview: 'bar', + lineNumber: 1, + offsetAndLengths: [[5, 3]] + }]); + + testObject.setSelectedMatch(null); + + assert.equal(null, testObject.getSelectedMatch()); + }); + + test('Alle Drei Zusammen', function () { + let searchResult = instantiationService.createInstance(SearchResult, null); + let fileMatch = aFileMatch('far/boo', searchResult); + let lineMatch = new Match(fileMatch, 'foo bar', 1, 0, 3); + + assert(lineMatch.parent() === fileMatch); + assert(fileMatch.parent() === searchResult); + }); + + test('Adding a raw match will add a file match with line matches', function () { + let testObject = aSearchResult(); + let target = [aRawMatch('file://c:/', aLineMatch('preview 1', 1, [[1, 3], [4, 7]]), aLineMatch('preview 2'))]; + + testObject.add(target); + + assert.equal(3, testObject.count()); + + let actual = testObject.matches(); + assert.equal(1, actual.length); + assert.equal('file://c:/', actual[0].resource().toString()); + + let actuaMatches = actual[0].matches(); + assert.equal(3, actuaMatches.length); + + assert.equal('preview 1', actuaMatches[0].text()); + assert.ok(new Range(2, 2, 2, 5).equalsRange(actuaMatches[0].range())); + + assert.equal('preview 1', actuaMatches[1].text()); + assert.ok(new Range(2, 5, 2, 12).equalsRange(actuaMatches[1].range())); + + assert.equal('preview 2', actuaMatches[2].text()); + assert.ok(new Range(2, 1, 2, 2).equalsRange(actuaMatches[2].range())); + }); + + test('Adding multiple raw matches', function () { + let testObject = aSearchResult(); + let target = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))]; + + testObject.add(target); + + assert.equal(3, testObject.count()); + + let actual = testObject.matches(); + assert.equal(2, actual.length); + assert.equal('file://c:/1', actual[0].resource().toString()); + + let actuaMatches = actual[0].matches(); + assert.equal(2, actuaMatches.length); + assert.equal('preview 1', actuaMatches[0].text()); + assert.ok(new Range(2, 2, 2, 5).equalsRange(actuaMatches[0].range())); + assert.equal('preview 1', actuaMatches[1].text()); + assert.ok(new Range(2, 5, 2, 12).equalsRange(actuaMatches[1].range())); + + actuaMatches = actual[1].matches(); + assert.equal(1, actuaMatches.length); + assert.equal('preview 2', actuaMatches[0].text()); + assert.ok(new Range(2, 1, 2, 2).equalsRange(actuaMatches[0].range())); + }); + + test('Dispose disposes matches', function () { + let target1 = sinon.spy(); + let target2 = sinon.spy(); + + let testObject = aSearchResult(); + testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1')), aRawMatch('file://c:/2', aLineMatch('preview 2'))]); + + testObject.matches()[0].onDispose(target1); + testObject.matches()[1].onDispose(target2); + + testObject.dispose(); + + assert.ok(testObject.isEmpty()); + assert.ok(target1.calledOnce); + assert.ok(target2.calledOnce); + }); + + test('remove triggers change event', function () { + let target = sinon.spy(); + let testObject = aSearchResult(); + testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]); + let objectRoRemove = testObject.matches()[0]; + testObject.onChange(target); + + testObject.remove(objectRoRemove); + + assert.ok(target.calledOnce); + assert.deepEqual([{ elements: [objectRoRemove], removed: true }], target.args[0]); + }); + + test('remove triggers change event', function () { + let target = sinon.spy(); + let testObject = aSearchResult(); + testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]); + let objectRoRemove = testObject.matches()[0]; + testObject.onChange(target); + + testObject.remove(objectRoRemove); + + assert.ok(target.calledOnce); + assert.deepEqual([{ elements: [objectRoRemove], removed: true }], target.args[0]); + }); + + test('Removing all line matches and adding back will add file back to result', function () { + let testObject = aSearchResult(); + testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]); + let target = testObject.matches()[0]; + let matchToRemove = target.matches()[0]; + target.remove(matchToRemove); + + assert.ok(testObject.isEmpty()); + target.add(matchToRemove, true); + + assert.equal(1, testObject.fileCount()); + assert.equal(target, testObject.matches()[0]); + }); + + test('replace should remove the file match', function () { + instantiationService.stubPromise(IReplaceService, 'replace', null); + let testObject = aSearchResult(); + testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]); + + testObject.replace(testObject.matches()[0]); + + assert.ok(testObject.isEmpty()); + }); + + test('replace should trigger the change event', function () { + let target = sinon.spy(); + instantiationService.stubPromise(IReplaceService, 'replace', null); + let testObject = aSearchResult(); + testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]); + testObject.onChange(target); + let objectRoRemove = testObject.matches()[0]; + + testObject.replace(objectRoRemove); + + assert.ok(target.calledOnce); + assert.deepEqual([{ elements: [objectRoRemove], removed: true }], target.args[0]); + }); + + test('replaceAll should remove all file matches', function () { + instantiationService.stubPromise(IReplaceService, 'replace', null); + let testObject = aSearchResult(); + testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1')), aRawMatch('file://c:/2', aLineMatch('preview 2'))]); + + testObject.replaceAll(null); + + assert.ok(testObject.isEmpty()); + }); + + //// ----- utils + //function lineHasDecorations(model: editor.IModel, lineNumber: number, decorations: { start: number; end: number; }[]): void { + // let lineDecorations:typeof decorations = []; + // let decs = model.getLineDecorations(lineNumber); + // for (let i = 0, len = decs.length; i < len; i++) { + // lineDecorations.push({ + // start: decs[i].range.startColumn, + // end: decs[i].range.endColumn + // }); + // } + // assert.deepEqual(lineDecorations, decorations); + //} + // + //function lineHasNoDecoration(model: editor.IModel, lineNumber: number): void { + // lineHasDecorations(model, lineNumber, []); + //} + // + //function lineHasDecoration(model: editor.IModel, lineNumber: number, start: number, end: number): void { + // lineHasDecorations(model, lineNumber, [{ + // start: start, + // end: end + // }]); + //} + //// ----- end utils + // + //test('Model Highlights', function () { + // + // let fileMatch = instantiation.createInstance(FileMatch, null, toUri('folder\\file.txt')); + // fileMatch.add(new Match(fileMatch, 'line2', 1, 0, 2)); + // fileMatch.connect(); + // lineHasDecoration(oneModel, 2, 1, 3); + //}); + // + //test('Dispose', function () { + // + // let fileMatch = instantiation.createInstance(FileMatch, null, toUri('folder\\file.txt')); + // fileMatch.add(new Match(fileMatch, 'line2', 1, 0, 2)); + // fileMatch.connect(); + // lineHasDecoration(oneModel, 2, 1, 3); + // + // fileMatch.dispose(); + // lineHasNoDecoration(oneModel, 2); + //}); + + function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ILineMatch[]): FileMatch { + let rawMatch: IFileMatch = { + resource: URI.file('/' + path), + lineMatches: lineMatches + }; + return instantiationService.createInstance(FileMatch, null, null, searchResult, rawMatch); + } + + function aSearchResult(): SearchResult { + let searchModel = instantiationService.createInstance(SearchModel); + searchModel.searchResult.query = { type: 1, folderQueries: [{ folder: URI.parse('file://c:/') }] }; + return searchModel.searchResult; + } + + function aRawMatch(resource: string, ...lineMatches: ILineMatch[]): IFileMatch { + return { resource: URI.parse(resource), lineMatches }; + } + + function aLineMatch(preview: string, lineNumber: number = 1, offsetAndLengths: number[][] = [[0, 1]]): ILineMatch { + return { preview, lineNumber, offsetAndLengths }; + } + + function stubModelService(instantiationService: TestInstantiationService): IModelService { + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + return instantiationService.createInstance(ModelServiceImpl); + } +}); diff --git a/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts b/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts new file mode 100644 index 0000000000..2ce81e12d4 --- /dev/null +++ b/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts @@ -0,0 +1,199 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { parse } from 'vs/base/common/json'; +import { join } from 'path'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { readFile } from 'vs/base/node/pfs'; +import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; +import { ISnippetsService, ISnippet } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchModeService'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { SnippetParser, Placeholder, Variable, Text } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { EditorSnippetVariableResolver } from 'vs/editor/contrib/snippet/browser/snippetVariables'; + +interface ISnippetsExtensionPoint { + language: string; + path: string; +} + +let snippetsExtensionPoint = ExtensionsRegistry.registerExtensionPoint('snippets', [languagesExtPoint], { + description: nls.localize('vscode.extension.contributes.snippets', 'Contributes snippets.'), + type: 'array', + defaultSnippets: [{ body: [{ language: '', path: '' }] }], + items: { + type: 'object', + defaultSnippets: [{ body: { language: '${1:id}', path: './snippets/${2:id}.json.' } }], + properties: { + language: { + description: nls.localize('vscode.extension.contributes.snippets-language', 'Language identifier for which this snippet is contributed to.'), + type: 'string' + }, + path: { + description: nls.localize('vscode.extension.contributes.snippets-path', 'Path of the snippets file. The path is relative to the extension folder and typically starts with \'./snippets/\'.'), + type: 'string' + } + } + } +}); + +export class MainProcessTextMateSnippet implements IWorkbenchContribution { + + constructor( + @IModeService private _modeService: IModeService, + @ISnippetsService private _snippetService: ISnippetsService + ) { + snippetsExtensionPoint.setHandler((extensions) => { + for (let i = 0; i < extensions.length; i++) { + let tmSnippets = extensions[i].value; + for (let j = 0; j < tmSnippets.length; j++) { + this._withSnippetContribution(extensions[i].description.name, extensions[i].description.extensionFolderPath, tmSnippets[j], extensions[i].collector); + } + } + }); + } + + getId() { + return 'tmSnippetExtension'; + } + + private _withSnippetContribution(extensionName: string, extensionFolderPath: string, snippet: ISnippetsExtensionPoint, collector: ExtensionMessageCollector): void { + if (!snippet.language || (typeof snippet.language !== 'string') || !this._modeService.isRegisteredMode(snippet.language)) { + collector.error(nls.localize('invalid.language', "Unknown language in `contributes.{0}.language`. Provided value: {1}", snippetsExtensionPoint.name, String(snippet.language))); + return; + } + if (!snippet.path || (typeof snippet.path !== 'string')) { + collector.error(nls.localize('invalid.path.0', "Expected string in `contributes.{0}.path`. Provided value: {1}", snippetsExtensionPoint.name, String(snippet.path))); + return; + } + let normalizedAbsolutePath = join(extensionFolderPath, snippet.path); + + if (normalizedAbsolutePath.indexOf(extensionFolderPath) !== 0) { + collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", snippetsExtensionPoint.name, normalizedAbsolutePath, extensionFolderPath)); + } + + let modeId = snippet.language; + let languageIdentifier = this._modeService.getLanguageIdentifier(modeId); + if (languageIdentifier) { + readAndRegisterSnippets(this._snippetService, languageIdentifier, normalizedAbsolutePath, extensionName, collector); + } + } +} + +export function readAndRegisterSnippets( + snippetService: ISnippetsService, languageIdentifier: LanguageIdentifier, filePath: string, + extensionName?: string, collector?: ExtensionMessageCollector +): TPromise { + + return readFile(filePath).then(fileContents => { + let snippets = parseSnippetFile(fileContents.toString(), extensionName, collector); + snippetService.registerSnippets(languageIdentifier.id, snippets, filePath); + }, err => { + if (err && err.code === 'ENOENT') { + snippetService.registerSnippets(languageIdentifier.id, [], filePath); + } else { + throw err; + } + }); +} + +function parseSnippetFile(snippetFileContent: string, extensionName?: string, collector?: ExtensionMessageCollector): ISnippet[] { + let snippetsObj = parse(snippetFileContent); + if (!snippetsObj || typeof snippetsObj !== 'object') { + return []; + } + + let topLevelProperties = Object.keys(snippetsObj); + let result: ISnippet[] = []; + + let processSnippet = (snippet: any, name: string) => { + let prefix = snippet['prefix']; + let body = snippet['body']; + + if (Array.isArray(body)) { + body = body.join('\n'); + } + + if (typeof prefix !== 'string' || typeof body !== 'string') { + return; + } + + snippet = { + name, + extensionName, + prefix, + description: snippet['description'] || name, + codeSnippet: body + }; + + const didRewrite = _rewriteBogousVariables(snippet); + if (didRewrite && collector) { + collector.warn(nls.localize( + 'badVariableUse', + "The \"{0}\"-snippet very likely confuses snippet-variables and snippet-placeholders. See https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax for more details.", + name + )); + } + + result.push(snippet); + }; + + topLevelProperties.forEach(topLevelProperty => { + let scopeOrTemplate = snippetsObj[topLevelProperty]; + if (scopeOrTemplate['body'] && scopeOrTemplate['prefix']) { + processSnippet(scopeOrTemplate, topLevelProperty); + } else { + let snippetNames = Object.keys(scopeOrTemplate); + snippetNames.forEach(name => { + processSnippet(scopeOrTemplate[name], name); + }); + } + }); + return result; +} + +export function _rewriteBogousVariables(snippet: ISnippet): boolean { + const textmateSnippet = new SnippetParser().parse(snippet.codeSnippet, false); + + let placeholders = new Map(); + let placeholderMax = 0; + for (const placeholder of textmateSnippet.placeholders) { + placeholderMax = Math.max(placeholderMax, placeholder.index); + } + + let didChange = false; + let stack = [...textmateSnippet.children]; + + while (stack.length > 0) { + let marker = stack.shift(); + + if ( + marker instanceof Variable + && marker.children.length === 0 + && !EditorSnippetVariableResolver.VariableNames[marker.name] + ) { + // a 'variable' without a default value and not being one of our supported + // variables is automatically turing into a placeholder. This is to restore + // a bug we had before. So `${foo}` becomes `${N:foo}` + const index = placeholders.has(marker.name) ? placeholders.get(marker.name) : ++placeholderMax; + placeholders.set(marker.name, index); + + const synthetic = new Placeholder(index).appendChild(new Text(marker.name)); + textmateSnippet.replace(marker, [synthetic]); + didChange = true; + + } else { + // recurse + stack.push(...marker.children); + } + } + + snippet.codeSnippet = textmateSnippet.toTextmateString(); + return didChange; +} diff --git a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts new file mode 100644 index 0000000000..340733a974 --- /dev/null +++ b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts @@ -0,0 +1,139 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { LanguageId } from 'vs/editor/common/modes'; +import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ISnippetsService, ISnippet } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; + +interface ISnippetPick extends IPickOpenEntry { + snippet: ISnippet; +} + +class Args { + + static fromUser(arg: any): Args { + if (!arg || typeof arg !== 'object') { + return Args._empty; + } + let { snippet, name, langId } = arg; + if (typeof snippet !== 'string') { + snippet = undefined; + } + if (typeof name !== 'string') { + name = undefined; + } + if (typeof langId !== 'string') { + langId = undefined; + } + return new Args(snippet, name, langId); + } + + private static _empty = new Args(undefined, undefined, undefined); + + private constructor( + public readonly snippet: string, + public readonly name: string, + public readonly langId: string + ) { + + } + +} + +@editorAction +class InsertSnippetAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.insertSnippet', + label: nls.localize('snippet.suggestions.label', "Insert Snippet"), + alias: 'Insert Snippet', + precondition: EditorContextKeys.writable + }); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor, arg: any): TPromise { + const modeService = accessor.get(IModeService); + const snippetService = accessor.get(ISnippetsService); + + if (!editor.getModel()) { + return undefined; + } + + const quickOpenService = accessor.get(IQuickOpenService); + const { lineNumber, column } = editor.getPosition(); + let { snippet, name, langId } = Args.fromUser(arg); + + return new TPromise((resolve, reject) => { + + if (snippet) { + return resolve({ + codeSnippet: snippet, + description: undefined, + name: undefined, + extensionName: undefined, + prefix: undefined + }); + } + + let languageId: LanguageId; + if (langId) { + languageId = modeService.getLanguageIdentifier(langId).id; + } else { + editor.getModel().tokenizeIfCheap(lineNumber); + languageId = editor.getModel().getLanguageIdAtPosition(lineNumber, column); + + // validate the `languageId` to ensure this is a user + // facing language with a name and the chance to have + // snippets, else fall back to the outer language + const { language } = modeService.getLanguageIdentifier(languageId); + if (!modeService.getLanguageName(language)) { + languageId = editor.getModel().getLanguageIdentifier().id; + } + } + + if (name) { + // take selected snippet + snippetService.visitSnippets(languageId, snippet => { + if (snippet.name !== name) { + return true; + } + resolve(snippet); + return false; + }); + } else { + // let user pick a snippet + const picks: ISnippetPick[] = []; + snippetService.visitSnippets(languageId, snippet => { + picks.push({ + label: snippet.prefix, + detail: snippet.description, + snippet + }); + return true; + }); + return quickOpenService.pick(picks, { matchOnDetail: true }).then(pick => resolve(pick && pick.snippet), reject); + } + }).then(snippet => { + if (snippet) { + SnippetController2.get(editor).insert(snippet.codeSnippet, 0, 0); + } + }); + } +} + +// compatibility command to make sure old keybinding are still working +CommandsRegistry.registerCommand('editor.action.showSnippets', accessor => { + return accessor.get(ICommandService).executeCommand('editor.action.insertSnippet'); +}); diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts new file mode 100644 index 0000000000..149ba8040d --- /dev/null +++ b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/workbench/parts/snippets/electron-browser/snippetsService'; +import 'vs/workbench/parts/snippets/electron-browser/insertSnippet'; +import 'vs/workbench/parts/snippets/electron-browser/tabCompletion'; + +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { fileExists, writeFile } from 'vs/base/node/pfs'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { join } from 'path'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import * as errors from 'vs/base/common/errors'; +import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import * as nls from 'vs/nls'; +import * as snippetsTracker from './snippetsTracker'; +import * as tmSnippets from './TMSnippets'; +import * as winjs from 'vs/base/common/winjs.base'; +import * as workbenchContributions from 'vs/workbench/common/contributions'; + +namespace OpenSnippetsAction { + + const id = 'workbench.action.openSnippets'; + + CommandsRegistry.registerCommand(id, accessor => { + + const modeService = accessor.get(IModeService); + const quickOpenService = accessor.get(IQuickOpenService); + const environmentService = accessor.get(IEnvironmentService); + const windowsService = accessor.get(IWindowsService); + + function openFile(filePath: string): winjs.TPromise { + return windowsService.openWindow([filePath], { forceReuseWindow: true }); + } + + var modeIds = modeService.getRegisteredModes(); + var picks: IPickOpenEntry[] = []; + modeIds.forEach((modeId) => { + var name = modeService.getLanguageName(modeId); + if (name) { + picks.push({ label: name, id: modeId }); + } + }); + picks = picks.sort((e1, e2) => + e1.label.localeCompare(e2.label) + ); + + return quickOpenService.pick(picks, { placeHolder: nls.localize('openSnippet.pickLanguage', "Select Language for Snippet") }).then((language) => { + if (language) { + var snippetPath = join(environmentService.appSettingsHome, 'snippets', language.id + '.json'); + return fileExists(snippetPath).then((success) => { + if (success) { + return openFile(snippetPath); + } + var defaultContent = [ + '{', + '/*', + '\t// Place your snippets for ' + language.label + ' here. Each snippet is defined under a snippet name and has a prefix, body and ', + '\t// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:', + '\t// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the ', + '\t// same ids are connected.', + '\t// Example:', + '\t"Print to console": {', + '\t\t"prefix": "log",', + '\t\t"body": [', + '\t\t\t"console.log(\'$1\');",', + '\t\t\t"$2"', + '\t\t],', + '\t\t"description": "Log output to console"', + '\t}', + '*/', + '}' + ].join('\n'); + return writeFile(snippetPath, defaultContent).then(() => { + return openFile(snippetPath); + }, (err) => { + errors.onUnexpectedError(nls.localize('openSnippet.errorOnCreate', 'Unable to create {0}', snippetPath)); + }); + }); + } + return winjs.TPromise.as(null); + }); + }); + + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id, + title: { value: nls.localize('openSnippet.label', "Open User Snippets"), original: 'Preferences: Open User Snippets' }, + category: nls.localize('preferences', "Preferences") + } + }); +} + + +const schemaId = 'vscode://schemas/snippets'; +const schema: IJSONSchema = { + 'id': schemaId, + 'defaultSnippets': [{ + 'label': nls.localize('snippetSchema.json.default', "Empty snippet"), + 'body': { '${1:snippetName}': { 'prefix': '${2:prefix}', 'body': '${3:snippet}', 'description': '${4:description}' } } + }], + 'type': 'object', + 'description': nls.localize('snippetSchema.json', 'User snippet configuration'), + 'additionalProperties': { + 'type': 'object', + 'required': ['prefix', 'body'], + 'properties': { + 'prefix': { + 'description': nls.localize('snippetSchema.json.prefix', 'The prefix to used when selecting the snippet in intellisense'), + 'type': 'string' + }, + 'body': { + 'description': nls.localize('snippetSchema.json.body', 'The snippet content. Use \'$1\', \'${1:defaultText}\' to define cursor positions, use \'$0\' for the final cursor position. Insert variable values with \'${varName}\' and \'${varName:defaultText}\', e.g \'This is file: $TM_FILENAME\'.'), + 'type': ['string', 'array'], + 'items': { + 'type': 'string' + } + }, + 'description': { + 'description': nls.localize('snippetSchema.json.description', 'The snippet description.'), + 'type': 'string' + } + }, + 'additionalProperties': false + } +}; + +Registry + .as(JSONContributionRegistry.Extensions.JSONContribution) + .registerSchema(schemaId, schema); + +Registry + .as(workbenchContributions.Extensions.Workbench) + .registerWorkbenchContribution(snippetsTracker.SnippetsTracker); + +Registry + .as(workbenchContributions.Extensions.Workbench) + .registerWorkbenchContribution(tmSnippets.MainProcessTextMateSnippet); diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts new file mode 100644 index 0000000000..b8192d075f --- /dev/null +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -0,0 +1,225 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { localize } from 'vs/nls'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { ISuggestSupport, ISuggestResult, ISuggestion, LanguageId, SuggestionType, SnippetType } from 'vs/editor/common/modes'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { setSnippetSuggestSupport } from 'vs/editor/contrib/suggest/browser/suggest'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { Position } from 'vs/editor/common/core/position'; +import { overlap, compare, startsWith } from 'vs/base/common/strings'; +import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; + +export const ISnippetsService = createDecorator('snippetService'); + +export interface ISnippetsService { + + _serviceBrand: any; + + registerSnippets(languageId: LanguageId, snippets: ISnippet[], owner: string): void; + + visitSnippets(languageId: LanguageId, accept: (snippet: ISnippet) => boolean): void; + + getSnippets(languageId: LanguageId): ISnippet[]; +} + +export interface ISnippet { + name: string; + prefix: string; + description: string; + codeSnippet: string; + extensionName?: string; +} + +export class SnippetsService implements ISnippetsService { + + _serviceBrand: any; + + private readonly _snippets = new Map>(); + + constructor( + @IModeService modeService: IModeService + ) { + setSnippetSuggestSupport(new SnippetSuggestProvider(modeService, this)); + } + + registerSnippets(languageId: LanguageId, snippets: ISnippet[], fileName: string): void { + if (!this._snippets.has(languageId)) { + this._snippets.set(languageId, new Map()); + } + this._snippets.get(languageId).set(fileName, snippets); + } + + visitSnippets(languageId: LanguageId, accept: (snippet: ISnippet) => boolean): void { + const modeSnippets = this._snippets.get(languageId); + if (modeSnippets) { + modeSnippets.forEach(snippets => { + let result = snippets.every(accept); + if (!result) { + return; + } + }); + } + } + + getSnippets(languageId: LanguageId): ISnippet[] { + const modeSnippets = this._snippets.get(languageId); + const ret: ISnippet[] = []; + if (modeSnippets) { + modeSnippets.forEach(snippets => { + ret.push(...snippets); + }); + } + return ret; + } +} + +registerSingleton(ISnippetsService, SnippetsService); + +export interface ISimpleModel { + getLineContent(lineNumber: number): string; +} + +export class SnippetSuggestion implements ISuggestion { + + private static _userSnippet = localize('source.snippet', "User Snippet"); + + label: string; + detail: string; + insertText: string; + documentation: string; + overwriteBefore: number; + sortText: string; + noAutoAccept: boolean; + type: SuggestionType; + snippetType: SnippetType; + + + constructor( + readonly snippet: ISnippet, + overwriteBefore: number + ) { + this.label = snippet.prefix; + this.detail = localize('detail.snippet', "{0} ({1})", snippet.description, snippet.extensionName || SnippetSuggestion._userSnippet); + this.insertText = snippet.codeSnippet; + this.overwriteBefore = overwriteBefore; + this.sortText = `${snippet.prefix}-${snippet.extensionName || ''}`; + this.noAutoAccept = true; + this.type = 'snippet'; + this.snippetType = 'textmate'; + } + + resolve(): this { + this.documentation = new SnippetParser().text(this.snippet.codeSnippet); + return this; + } + + static compareByLabel(a: SnippetSuggestion, b: SnippetSuggestion): number { + return compare(a.label, b.label); + } +} + + +export class SnippetSuggestProvider implements ISuggestSupport { + + constructor( + @IModeService private _modeService: IModeService, + @ISnippetsService private _snippets: ISnippetsService + ) { + // + } + + provideCompletionItems(model: IModel, position: Position): ISuggestResult { + + const languageId = this._getLanguageIdAtPosition(model, position); + const snippets = this._snippets.getSnippets(languageId); + const suggestions: SnippetSuggestion[] = []; + + const lowWordUntil = model.getWordUntilPosition(position).word.toLowerCase(); + const lowLineUntil = model.getLineContent(position.lineNumber).substr(Math.max(0, position.column - 100), position.column - 1).toLowerCase(); + + for (const snippet of snippets) { + + const lowPrefix = snippet.prefix.toLowerCase(); + let overwriteBefore = 0; + let accetSnippet = true; + + if (lowWordUntil.length > 0 && startsWith(lowPrefix, lowWordUntil)) { + // cheap match on the (none-empty) current word + overwriteBefore = lowWordUntil.length; + accetSnippet = true; + + } else if (lowLineUntil.length > 0 && lowLineUntil.match(/[^\s]$/)) { + // compute overlap between snippet and (none-empty) line on text + overwriteBefore = overlap(lowLineUntil, snippet.prefix.toLowerCase()); + accetSnippet = overwriteBefore > 0 && !model.getWordAtPosition(new Position(position.lineNumber, position.column - overwriteBefore)); + } + + if (accetSnippet) { + suggestions.push(new SnippetSuggestion(snippet, overwriteBefore)); + } + } + + // dismbiguate suggestions with same labels + let lastItem: SnippetSuggestion; + for (const item of suggestions.sort(SnippetSuggestion.compareByLabel)) { + if (lastItem && lastItem.label === item.label) { + // use the disambiguateLabel instead of the actual label + lastItem.label = localize('snippetSuggest.longLabel', "{0}, {1}", lastItem.label, lastItem.snippet.name); + item.label = localize('snippetSuggest.longLabel', "{0}, {1}", item.label, item.snippet.name); + } + lastItem = item; + } + + return { suggestions }; + } + + resolveCompletionItem?(model: IModel, position: Position, item: ISuggestion): ISuggestion { + return (item instanceof SnippetSuggestion) ? item.resolve() : item; + } + + private _getLanguageIdAtPosition(model: IModel, position: Position): LanguageId { + // validate the `languageId` to ensure this is a user + // facing language with a name and the chance to have + // snippets, else fall back to the outer language + model.tokenizeIfCheap(position.lineNumber); + let languageId = model.getLanguageIdAtPosition(position.lineNumber, position.column); + let { language } = this._modeService.getLanguageIdentifier(languageId); + if (!this._modeService.getLanguageName(language)) { + languageId = model.getLanguageIdentifier().id; + } + return languageId; + } + + +} + +export function getNonWhitespacePrefix(model: ISimpleModel, position: Position): string { + /** + * Do not analyze more characters + */ + const MAX_PREFIX_LENGTH = 100; + + let line = model.getLineContent(position.lineNumber).substr(0, position.column - 1); + + let minChIndex = Math.max(0, line.length - MAX_PREFIX_LENGTH); + for (let chIndex = line.length - 1; chIndex >= minChIndex; chIndex--) { + let ch = line.charAt(chIndex); + + if (/\s/.test(ch)) { + return line.substr(chIndex + 1); + } + } + + if (minChIndex === 0) { + return line; + } + + return ''; +} + diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts new file mode 100644 index 0000000000..48da96f59e --- /dev/null +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { join } from 'path'; +import { mkdirp, fileExists } from 'vs/base/node/pfs'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { readAndRegisterSnippets } from './TMSnippets'; +import { ISnippetsService } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { watch } from 'fs'; +import { IModeService } from 'vs/editor/common/services/modeService'; + +export class SnippetsTracker implements IWorkbenchContribution { + + private readonly _snippetFolder: string; + private readonly _toDispose: IDisposable[]; + + constructor( + @IModeService modeService: IModeService, + @ISnippetsService snippetService: ISnippetsService, + @IEnvironmentService environmentService: IEnvironmentService, + @IExtensionService extensionService: IExtensionService + ) { + this._snippetFolder = join(environmentService.appSettingsHome, 'snippets'); + this._toDispose = []; + + // Whenever a mode is being created check if a snippet file exists + // and iff so read all snippets from it. + this._toDispose.push(modeService.onDidCreateMode(mode => { + const snippetPath = join(this._snippetFolder, `${mode.getId()}.json`); + fileExists(snippetPath) + .then(exists => exists && readAndRegisterSnippets(snippetService, mode.getLanguageIdentifier(), snippetPath)) + .done(undefined, onUnexpectedError); + })); + + // Install a FS watcher on the snippet directory and when an + // event occurs update the snippets for that one snippet. + mkdirp(this._snippetFolder).then(() => { + const watcher = watch(this._snippetFolder); + this._toDispose.push({ dispose: () => watcher.close() }); + watcher.on('change', (type, filename) => { + if (typeof filename !== 'string') { + return; + } + extensionService.onReady().then(() => { + const langName = filename.replace(/\.json$/, '').toLowerCase(); + const langId = modeService.getLanguageIdentifier(langName); + return langId && readAndRegisterSnippets(snippetService, langId, join(this._snippetFolder, filename)); + }, onUnexpectedError); + }); + }); + } + + getId(): string { + return 'vs.snippets.snippetsTracker'; + } + + dispose(): void { + dispose(this._toDispose); + } +} diff --git a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts new file mode 100644 index 0000000000..3f66ceb523 --- /dev/null +++ b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { RawContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ISnippetsService, getNonWhitespacePrefix, ISnippet, SnippetSuggestion } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { endsWith } from 'vs/base/common/strings'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { CommonEditorRegistry, commonEditorContribution, EditorCommand } from 'vs/editor/common/editorCommonExtensions'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; +import { showSimpleSuggestions } from 'vs/editor/contrib/suggest/browser/suggest'; +import { IConfigurationRegistry, Extensions as ConfigExt } from 'vs/platform/configuration/common/configurationRegistry'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; + +@commonEditorContribution +export class TabCompletionController implements editorCommon.IEditorContribution { + + private static ID = 'editor.tabCompletionController'; + static ContextKey = new RawContextKey('hasSnippetCompletions', undefined); + + public static get(editor: editorCommon.ICommonCodeEditor): TabCompletionController { + return editor.getContribution(TabCompletionController.ID); + } + + private readonly _editor: editorCommon.ICommonCodeEditor; + private readonly _snippetController: SnippetController2; + private readonly _dispoables: IDisposable[] = []; + private readonly _snippets: ISnippet[] = []; + + constructor( + editor: editorCommon.ICommonCodeEditor, + @IContextKeyService contextKeyService: IContextKeyService, + @ISnippetsService snippetService: ISnippetsService + ) { + this._editor = editor; + this._snippetController = SnippetController2.get(editor); + + const hasSnippets = TabCompletionController.ContextKey.bindTo(contextKeyService); + this._dispoables.push(editor.onDidChangeCursorSelection(e => { + + this._snippets.length = 0; + let selectFn: (snippet: ISnippet) => boolean; + + if (e.selection.isEmpty()) { + // empty selection -> real text (no whitespace) left of cursor + const prefix = getNonWhitespacePrefix(editor.getModel(), editor.getPosition()); + selectFn = prefix && (snippet => endsWith(prefix, snippet.prefix)); + + } else { + // actual selection -> snippet must be a full match + const selected = editor.getModel().getValueInRange(e.selection); + selectFn = snippet => selected === snippet.prefix; + } + + if (selectFn) { + snippetService.visitSnippets(editor.getModel().getLanguageIdentifier().id, s => { + if (selectFn(s)) { + this._snippets.push(s); + } + return true; + }); + } + hasSnippets.set(this._snippets.length > 0); + })); + } + + getId(): string { + return TabCompletionController.ID; + } + + dispose(): void { + dispose(this._dispoables); + } + + performSnippetCompletions(): void { + + if (this._snippets.length === 1) { + // one -> just insert + const [snippet] = this._snippets; + this._snippetController.insert(snippet.codeSnippet, snippet.prefix.length, 0); + + } else if (this._snippets.length > 1) { + // two or more -> show IntelliSense box + showSimpleSuggestions(this._editor, this._snippets.map(snippet => new SnippetSuggestion(snippet, snippet.prefix.length))); + } + } +} + +const TabCompletionCommand = EditorCommand.bindToContribution(TabCompletionController.get); + +CommonEditorRegistry.registerEditorCommand(new TabCompletionCommand({ + id: 'insertSnippet', + precondition: TabCompletionController.ContextKey, + handler: x => x.performSnippetCompletions(), + kbOpts: { + weight: KeybindingsRegistry.WEIGHT.editorContrib(), + kbExpr: ContextKeyExpr.and( + EditorContextKeys.textFocus, + EditorContextKeys.tabDoesNotMoveFocus, + SnippetController2.InSnippetMode.toNegated(), + ContextKeyExpr.has('config.editor.tabCompletion') + ), + primary: KeyCode.Tab + } +})); + + +Registry.as(ConfigExt.Configuration).registerConfiguration({ + id: 'editor', + order: 5, + type: 'object', + properties: { + 'editor.tabCompletion': { + 'type': 'boolean', + 'default': false, + 'description': localize('tabCompletion', "Insert snippets when their prefix matches. Works best when 'quickSuggestions' aren't enabled.") + }, + } +}); diff --git a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsRegistry.test.ts b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsRegistry.test.ts new file mode 100644 index 0000000000..9b35a7db05 --- /dev/null +++ b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsRegistry.test.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { getNonWhitespacePrefix } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; +import { Position } from 'vs/editor/common/core/position'; + +suite('getNonWhitespacePrefix', () => { + + function assertGetNonWhitespacePrefix(line: string, column: number, expected: string): void { + let model = { + getLineContent: (lineNumber: number) => line + }; + let actual = getNonWhitespacePrefix(model, new Position(1, column)); + assert.equal(actual, expected); + } + + test('empty line', () => { + assertGetNonWhitespacePrefix('', 1, ''); + }); + + test('singleWordLine', () => { + assertGetNonWhitespacePrefix('something', 1, ''); + assertGetNonWhitespacePrefix('something', 2, 's'); + assertGetNonWhitespacePrefix('something', 3, 'so'); + assertGetNonWhitespacePrefix('something', 4, 'som'); + assertGetNonWhitespacePrefix('something', 5, 'some'); + assertGetNonWhitespacePrefix('something', 6, 'somet'); + assertGetNonWhitespacePrefix('something', 7, 'someth'); + assertGetNonWhitespacePrefix('something', 8, 'somethi'); + assertGetNonWhitespacePrefix('something', 9, 'somethin'); + assertGetNonWhitespacePrefix('something', 10, 'something'); + }); + + test('two word line', () => { + assertGetNonWhitespacePrefix('something interesting', 1, ''); + assertGetNonWhitespacePrefix('something interesting', 2, 's'); + assertGetNonWhitespacePrefix('something interesting', 3, 'so'); + assertGetNonWhitespacePrefix('something interesting', 4, 'som'); + assertGetNonWhitespacePrefix('something interesting', 5, 'some'); + assertGetNonWhitespacePrefix('something interesting', 6, 'somet'); + assertGetNonWhitespacePrefix('something interesting', 7, 'someth'); + assertGetNonWhitespacePrefix('something interesting', 8, 'somethi'); + assertGetNonWhitespacePrefix('something interesting', 9, 'somethin'); + assertGetNonWhitespacePrefix('something interesting', 10, 'something'); + assertGetNonWhitespacePrefix('something interesting', 11, ''); + assertGetNonWhitespacePrefix('something interesting', 12, 'i'); + assertGetNonWhitespacePrefix('something interesting', 13, 'in'); + assertGetNonWhitespacePrefix('something interesting', 14, 'int'); + assertGetNonWhitespacePrefix('something interesting', 15, 'inte'); + assertGetNonWhitespacePrefix('something interesting', 16, 'inter'); + assertGetNonWhitespacePrefix('something interesting', 17, 'intere'); + assertGetNonWhitespacePrefix('something interesting', 18, 'interes'); + assertGetNonWhitespacePrefix('something interesting', 19, 'interest'); + assertGetNonWhitespacePrefix('something interesting', 20, 'interesti'); + assertGetNonWhitespacePrefix('something interesting', 21, 'interestin'); + assertGetNonWhitespacePrefix('something interesting', 22, 'interesting'); + }); + + test('many separators', () => { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions?redirectlocale=en-US&redirectslug=JavaScript%2FGuide%2FRegular_Expressions#special-white-space + // \s matches a single white space character, including space, tab, form feed, line feed. + // Equivalent to [ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]. + + assertGetNonWhitespacePrefix('something interesting', 22, 'interesting'); + assertGetNonWhitespacePrefix('something\tinteresting', 22, 'interesting'); + assertGetNonWhitespacePrefix('something\finteresting', 22, 'interesting'); + assertGetNonWhitespacePrefix('something\vinteresting', 22, 'interesting'); + assertGetNonWhitespacePrefix('something\u00a0interesting', 22, 'interesting'); + assertGetNonWhitespacePrefix('something\u2000interesting', 22, 'interesting'); + assertGetNonWhitespacePrefix('something\u2028interesting', 22, 'interesting'); + assertGetNonWhitespacePrefix('something\u3000interesting', 22, 'interesting'); + assertGetNonWhitespacePrefix('something\ufeffinteresting', 22, 'interesting'); + + }); +}); diff --git a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsRewrite.test.ts b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsRewrite.test.ts new file mode 100644 index 0000000000..e1e2fe3b49 --- /dev/null +++ b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsRewrite.test.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { _rewriteBogousVariables } from 'vs/workbench/parts/snippets/electron-browser/TMSnippets'; + +suite('TMSnippets', function () { + + function assertRewrite(input: string, expected: string): void { + let snippet = { codeSnippet: input, description: undefined, extensionName: undefined, name: undefined, prefix: undefined }; + _rewriteBogousVariables(snippet); + assert.equal(snippet.codeSnippet, expected); + } + + test('bogous variable rewrite', function () { + + assertRewrite('foo', 'foo'); + assertRewrite('hello $1 world$0', 'hello $1 world$0'); + + assertRewrite('$foo and $foo', '${1:foo} and ${1:foo}'); + assertRewrite('$1 and $SELECTION and $foo', '$1 and ${SELECTION} and ${2:foo}'); + + + assertRewrite( + [ + 'for (var ${index} = 0; ${index} < ${array}.length; ${index}++) {', + '\tvar ${element} = ${array}[${index}];', + '\t$0', + '}' + ].join('\n'), + [ + 'for (var ${1:index} = 0; ${1:index} < ${2:array}.length; ${1:index}++) {', + '\tvar ${3:element} = ${2:array}[${1:index}];', + '\t$0', + '\\}' + ].join('\n') + ); + }); + + test('Snippet choices: unable to escape comma and pipe, #31521', function () { + assertRewrite('console.log(${1|not\\, not, five, 5, 1 23|});', 'console.log(${1|not\\, not, five, 5, 1 23|});'); + }); +}); diff --git a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts new file mode 100644 index 0000000000..3e7b1f83bb --- /dev/null +++ b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { SnippetsService, ISnippet, SnippetSuggestProvider } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; +import { Position } from 'vs/editor/common/core/position'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; +import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; +import { Model } from 'vs/editor/common/model/model'; + +suite('SnippetsService', function () { + + suiteSetup(function () { + ModesRegistry.registerLanguage({ + id: 'fooLang', + extensions: ['.fooLang',] + }); + }); + + let modeService: ModeServiceImpl; + let snippetService: SnippetsService; + + setup(function () { + modeService = new ModeServiceImpl(); + snippetService = new SnippetsService(modeService); + + snippetService.registerSnippets(modeService.getLanguageIdentifier('fooLang').id, [{ + prefix: 'bar', + codeSnippet: 'barCodeSnippet', + name: 'barTest', + description: '' + }, { + prefix: 'bazz', + codeSnippet: 'bazzCodeSnippet', + name: 'bazzTest', + description: '' + }], 'fooFile.json'); + }); + + test('snippet completions - simple', function () { + + const provider = new SnippetSuggestProvider(modeService, snippetService); + const model = Model.createFromString('', undefined, modeService.getLanguageIdentifier('fooLang')); + + const result = provider.provideCompletionItems(model, new Position(1, 1)); + + assert.equal(result.incomplete, undefined); + assert.equal(result.suggestions.length, 2); + }); + + test('snippet completions - with prefix', function () { + + const provider = new SnippetSuggestProvider(modeService, snippetService); + const model = Model.createFromString('bar', undefined, modeService.getLanguageIdentifier('fooLang')); + + const result = provider.provideCompletionItems(model, new Position(1, 4)); + + assert.equal(result.incomplete, undefined); + assert.equal(result.suggestions.length, 1); + assert.equal(result.suggestions[0].label, 'bar'); + assert.equal(result.suggestions[0].insertText, 'barCodeSnippet'); + }); + + test('Cannot use "[{ + prefix: '[{ + prefix: 'foo', + codeSnippet: '$0', + name: '', + description: '' + }], 'fooFile.json'); + + const provider = new SnippetSuggestProvider(modeService, snippetService); + + let model = Model.createFromString('\n\t\n>/head>', undefined, modeService.getLanguageIdentifier('fooLang')); + let result = provider.provideCompletionItems(model, new Position(1, 1)); + assert.equal(result.suggestions.length, 1); + + result = provider.provideCompletionItems(model, new Position(2, 2)); + assert.equal(result.suggestions.length, 1); + }); +}); diff --git a/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.ts b/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.ts new file mode 100644 index 0000000000..880547f764 --- /dev/null +++ b/src/vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { language } from 'vs/base/common/platform'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { FileChangeType, IFileService } from 'vs/platform/files/common/files'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import pkg from 'vs/platform/node/package'; +import product, { ISurveyData } from 'vs/platform/node/product'; + +class LanguageSurvey { + + constructor( + data: ISurveyData, + instantiationService: IInstantiationService, + storageService: IStorageService, + messageService: IMessageService, + telemetryService: ITelemetryService, + fileService: IFileService, + modelService: IModelService + ) { + const SESSION_COUNT_KEY = `${data.surveyId}.sessionCount`; + const LAST_SESSION_DATE_KEY = `${data.surveyId}.lastSessionDate`; + const SKIP_VERSION_KEY = `${data.surveyId}.skipVersion`; + const IS_CANDIDATE_KEY = `${data.surveyId}.isCandidate`; + const EDITED_LANGUAGE_COUNT_KEY = `${data.surveyId}.editedCount`; + const EDITED_LANGUAGE_DATE_KEY = `${data.surveyId}.editedDate`; + + const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, ''); + if (skipVersion) { + return; + } + const date = new Date().toDateString(); + + if (storageService.getInteger(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) < data.editCount) { + fileService.onFileChanges(e => { + e.getUpdated().forEach(event => { + if (event.type === FileChangeType.UPDATED) { + const model = modelService.getModel(event.resource); + if (model && model.getModeId() === data.languageId && date !== storageService.get(EDITED_LANGUAGE_DATE_KEY, StorageScope.GLOBAL)) { + const editedCount = storageService.getInteger(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; + storageService.store(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL); + storageService.store(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL); + } + } + }); + }); + } + + const lastSessionDate = storageService.get(LAST_SESSION_DATE_KEY, StorageScope.GLOBAL, new Date(0).toDateString()); + if (date === lastSessionDate) { + return; + } + + const sessionCount = storageService.getInteger(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; + storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL); + storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL); + + if (sessionCount < 9) { + return; + } + if (storageService.getInteger(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) < data.editCount) { + return; + } + + const isCandidate = storageService.getBoolean(IS_CANDIDATE_KEY, StorageScope.GLOBAL, false) + || Math.random() < data.userProbability; + + storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL); + + if (!isCandidate) { + storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + return; + } + + const message = nls.localize('helpUs', "Help us improve our support for {0}", data.languageId); + + const takeSurveyAction = new Action('takeSurvey', nls.localize('takeShortSurvey', "Take Short Survey"), '', true, () => { + telemetryService.publicLog(`${data.surveyId}.survey/takeShortSurvey`); + return telemetryService.getTelemetryInfo().then(info => { + window.open(`${data.surveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + }); + }); + + const remindMeLaterAction = new Action('later', nls.localize('remindLater', "Remind Me later"), '', true, () => { + telemetryService.publicLog(`${data.surveyId}.survey/remindMeLater`); + storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL); + return TPromise.as(null); + }); + + const neverAgainAction = new Action('never', nls.localize('neverAgain', "Don't Show Again"), '', true, () => { + telemetryService.publicLog(`${data.surveyId}.survey/dontShowAgain`); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + return TPromise.as(null); + }); + + const actions = [neverAgainAction, remindMeLaterAction, takeSurveyAction]; + telemetryService.publicLog(`${data.surveyId}.survey/userAsked`); + messageService.show(Severity.Info, { message, actions }); + } + +} + +class LanguageSurveysContribution implements IWorkbenchContribution { + + private surveys: LanguageSurvey[]; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @IMessageService messageService: IMessageService, + @ITelemetryService telemetryService: ITelemetryService, + @IFileService fileService: IFileService, + @IModelService modelService: IModelService + ) { + this.surveys = product.surveys.filter(surveyData => surveyData.surveyId && surveyData.editCount && surveyData.languageId && surveyData.surveyUrl && surveyData.userProbability).map(surveyData => + new LanguageSurvey(surveyData, instantiationService, storageService, messageService, telemetryService, fileService, modelService)); + } + + getId(): string { + return 'languagesurveys.contribution'; + } +} + +if (language === 'en' && product.surveys && product.surveys.length) { + const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); + workbenchRegistry.registerWorkbenchContribution(LanguageSurveysContribution); +} \ No newline at end of file diff --git a/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.ts b/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.ts new file mode 100644 index 0000000000..37f5f82d9e --- /dev/null +++ b/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { language } from 'vs/base/common/platform'; +import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import pkg from 'vs/platform/node/package'; +import product from 'vs/platform/node/product'; + +const PROBABILITY = 0.15; +const SESSION_COUNT_KEY = 'nps/sessionCount'; +const LAST_SESSION_DATE_KEY = 'nps/lastSessionDate'; +const SKIP_VERSION_KEY = 'nps/skipVersion'; +const IS_CANDIDATE_KEY = 'nps/isCandidate'; + +class NPSContribution implements IWorkbenchContribution { + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @IMessageService messageService: IMessageService, + @ITelemetryService telemetryService: ITelemetryService + ) { + const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, ''); + + if (skipVersion) { + return; + } + + const date = new Date().toDateString(); + const lastSessionDate = storageService.get(LAST_SESSION_DATE_KEY, StorageScope.GLOBAL, new Date(0).toDateString()); + + if (date === lastSessionDate) { + return; + } + + const sessionCount = storageService.getInteger(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; + storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL); + storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL); + + if (sessionCount < 9) { + return; + } + + const isCandidate = storageService.getBoolean(IS_CANDIDATE_KEY, StorageScope.GLOBAL, false) + || Math.random() < PROBABILITY; + + storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL); + + if (!isCandidate) { + storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + return; + } + + const message = nls.localize('surveyQuestion', "Do you mind taking a quick feedback survey?"); + + const takeSurveyAction = new Action('nps.takeSurvey', nls.localize('takeSurvey', "Take Survey"), '', true, () => { + return telemetryService.getTelemetryInfo().then(info => { + window.open(`${product.npsSurveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + }); + }); + + const remindMeLaterAction = new Action('nps.later', nls.localize('remindLater', "Remind Me later"), '', true, () => { + storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL); + return TPromise.as(null); + }); + + const neverAgainAction = new Action('nps.never', nls.localize('neverAgain', "Don't Show Again"), '', true, () => { + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + return TPromise.as(null); + }); + + const actions = [neverAgainAction, remindMeLaterAction, takeSurveyAction]; + + messageService.show(Severity.Info, { message, actions }); + } + + getId(): string { + return 'nps'; + } +} + +if (language === 'en' && product.npsSurveyUrl) { + const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); + workbenchRegistry.registerWorkbenchContribution(NPSContribution); +} \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/browser/quickOpen.ts b/src/vs/workbench/parts/tasks/browser/quickOpen.ts new file mode 100644 index 0000000000..4c1d3339c5 --- /dev/null +++ b/src/vs/workbench/parts/tasks/browser/quickOpen.ts @@ -0,0 +1,204 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import Filters = require('vs/base/common/filters'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action, IAction } from 'vs/base/common/actions'; +import { IStringDictionary } from 'vs/base/common/collections'; + +import Quickopen = require('vs/workbench/browser/quickopen'); +import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen'); +import Model = require('vs/base/parts/quickopen/browser/quickOpenModel'); +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; + +import { Task, CustomTask, ContributedTask } from 'vs/workbench/parts/tasks/common/tasks'; +import { ITaskService, RunOptions } from 'vs/workbench/parts/tasks/common/taskService'; +import { ActionBarContributor, ContributableActionProvider } from 'vs/workbench/browser/actions'; + +export class TaskEntry extends Model.QuickOpenEntry { + + constructor(protected taskService: ITaskService, protected quickOpenService: IQuickOpenService, protected _task: CustomTask | ContributedTask, highlights: Model.IHighlight[] = []) { + super(highlights); + } + + public getLabel(): string { + return this.task._label; + } + + public getAriaLabel(): string { + return nls.localize('entryAriaLabel', "{0}, tasks", this.getLabel()); + } + + public get task(): CustomTask | ContributedTask { + return this._task; + } + + protected doRun(task: CustomTask | ContributedTask, options?: RunOptions): boolean { + this.taskService.run(task, options); + if (!task.command || task.command.presentation.focus) { + this.quickOpenService.close(); + return false; + } + return true; + } +} + +export class TaskGroupEntry extends Model.QuickOpenEntryGroup { + constructor(entry: TaskEntry, groupLabel: string, withBorder: boolean) { + super(entry, groupLabel, withBorder); + } +} + +export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler { + + private tasks: TPromise<(CustomTask | ContributedTask)[]>; + + + constructor( + protected quickOpenService: IQuickOpenService, + protected taskService: ITaskService + ) { + super(); + + this.quickOpenService = quickOpenService; + this.taskService = taskService; + } + + public onOpen(): void { + this.tasks = this.getTasks(); + } + + public onClose(canceled: boolean): void { + this.tasks = undefined; + } + + public getResults(input: string): TPromise { + return this.tasks.then((tasks) => { + let entries: Model.QuickOpenEntry[] = []; + if (tasks.length === 0) { + return new Model.QuickOpenModel(entries); + } + let recentlyUsedTasks = this.taskService.getRecentlyUsedTasks(); + let recent: (CustomTask | ContributedTask)[] = []; + let configured: CustomTask[] = []; + let detected: ContributedTask[] = []; + let taskMap: IStringDictionary = Object.create(null); + tasks.forEach(task => taskMap[Task.getKey(task)] = task); + recentlyUsedTasks.keys().forEach(key => { + let task = taskMap[key]; + if (task) { + recent.push(task); + } + }); + for (let task of tasks) { + if (!recentlyUsedTasks.has(Task.getKey(task))) { + if (CustomTask.is(task)) { + configured.push(task); + } else { + detected.push(task); + } + } + } + let hasRecentlyUsed: boolean = recent.length > 0; + this.fillEntries(entries, input, recent, nls.localize('recentlyUsed', 'recently used tasks')); + configured = configured.sort((a, b) => a._label.localeCompare(b._label)); + let hasConfigured = configured.length > 0; + this.fillEntries(entries, input, configured, nls.localize('configured', 'configured tasks'), hasRecentlyUsed); + detected = detected.sort((a, b) => a._label.localeCompare(b._label)); + this.fillEntries(entries, input, detected, nls.localize('detected', 'detected tasks'), hasRecentlyUsed || hasConfigured); + return new Model.QuickOpenModel(entries, new ContributableActionProvider()); + }); + } + + private fillEntries(entries: Model.QuickOpenEntry[], input: string, tasks: (CustomTask | ContributedTask)[], groupLabel: string, withBorder: boolean = false) { + let first = true; + for (let task of tasks) { + let highlights = Filters.matchesFuzzy(input, task._label); + if (!highlights) { + continue; + } + if (first) { + first = false; + entries.push(new TaskGroupEntry(this.createEntry(task, highlights), groupLabel, withBorder)); + } else { + entries.push(this.createEntry(task, highlights)); + } + } + } + + protected abstract getTasks(): TPromise<(CustomTask | ContributedTask)[]>; + + protected abstract createEntry(task: CustomTask | ContributedTask, highlights: Model.IHighlight[]): TaskEntry; + + public getAutoFocus(input: string): QuickOpen.IAutoFocus { + return { + autoFocusFirstEntry: !!input + }; + } +} + +class CustomizeTaskAction extends Action { + + private static ID = 'workbench.action.tasks.customizeTask'; + private static LABEL = nls.localize('customizeTask', "Configure Task"); + + constructor(private taskService: ITaskService, private quickOpenService: IQuickOpenService, private task: CustomTask | ContributedTask) { + super(CustomizeTaskAction.ID, CustomizeTaskAction.LABEL); + this.updateClass(); + } + + public updateClass(): void { + this.class = 'quick-open-task-configure'; + } + + public run(context: any): TPromise { + if (ContributedTask.is(this.task)) { + return this.taskService.customize(this.task, undefined, true).then(() => { + this.quickOpenService.close(); + }); + } else { + return this.taskService.openConfig(this.task).then(() => { + this.quickOpenService.close(); + }); + } + } +} + +export class QuickOpenActionContributor extends ActionBarContributor { + + constructor( @ITaskService private taskService: ITaskService, @IQuickOpenService private quickOpenService: IQuickOpenService) { + super(); + } + + public hasActions(context: any): boolean { + let task = this.getTask(context); + + return !!task; + } + + public getActions(context: any): IAction[] { + let actions: Action[] = []; + let task = this.getTask(context); + if (task && ContributedTask.is(task) || CustomTask.is(task)) { + actions.push(new CustomizeTaskAction(this.taskService, this.quickOpenService, task)); + } + return actions; + } + + private getTask(context: any): CustomTask | ContributedTask { + if (!context) { + return undefined; + } + let element = context.element; + if (element instanceof TaskEntry) { + return element.task; + } else if (element instanceof TaskGroupEntry) { + return (element.getEntry() as TaskEntry).task; + } + return undefined; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts b/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts new file mode 100644 index 0000000000..877ee34ba1 --- /dev/null +++ b/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen'); +import Model = require('vs/base/parts/quickopen/browser/quickOpenModel'); +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; + +import { CustomTask, ContributedTask } from 'vs/workbench/parts/tasks/common/tasks'; +import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; + + +import * as base from './quickOpen'; + +class TaskEntry extends base.TaskEntry { + constructor(taskService: ITaskService, quickOpenService: IQuickOpenService, task: CustomTask | ContributedTask, highlights: Model.IHighlight[] = []) { + super(taskService, quickOpenService, task, highlights); + } + + public run(mode: QuickOpen.Mode, context: Model.IContext): boolean { + if (mode === QuickOpen.Mode.PREVIEW) { + return false; + } + let task = this._task; + return this.doRun(task, { attachProblemMatcher: true }); + } +} + +export class QuickOpenHandler extends base.QuickOpenHandler { + private activationPromise: TPromise; + + constructor( + @IQuickOpenService quickOpenService: IQuickOpenService, + @ITaskService taskService: ITaskService, + @IExtensionService extensionService: IExtensionService + ) { + super(quickOpenService, taskService); + this.activationPromise = extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'); + } + + public getAriaLabel(): string { + return nls.localize('tasksAriaLabel', "Type the name of a task to run"); + } + + protected getTasks(): TPromise<(CustomTask | ContributedTask)[]> { + return this.activationPromise.then(() => { + return this.taskService.tasks().then(tasks => tasks.filter((task): task is CustomTask | ContributedTask => ContributedTask.is(task) || CustomTask.is(task))); + }); + } + + protected createEntry(task: CustomTask | ContributedTask, highlights: Model.IHighlight[]): base.TaskEntry { + return new TaskEntry(this.taskService, this.quickOpenService, task, highlights); + } + + public getEmptyLabel(searchString: string): string { + if (searchString.length > 0) { + return nls.localize('noTasksMatching', "No tasks matching"); + } + return nls.localize('noTasksFound', "No tasks found"); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/common/problemCollectors.ts b/src/vs/workbench/parts/tasks/common/problemCollectors.ts new file mode 100644 index 0000000000..85e0b27399 --- /dev/null +++ b/src/vs/workbench/parts/tasks/common/problemCollectors.ts @@ -0,0 +1,459 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IStringDictionary, INumberDictionary } from 'vs/base/common/collections'; +import URI from 'vs/base/common/uri'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +import { IModelService } from 'vs/editor/common/services/modelService'; + +import { ILineMatcher, createLineMatcher, ProblemMatcher, ProblemMatch, ApplyToKind, WatchingPattern, getResource } from 'vs/platform/markers/common/problemMatcher'; +import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers'; + +export namespace ProblemCollectorEvents { + export let WatchingBeginDetected: string = 'watchingBeginDetected'; + export let WatchingEndDetected: string = 'watchingEndDetected'; +} + +export interface IProblemMatcher { + processLine(line: string): void; +} + +export class AbstractProblemCollector extends EventEmitter implements IDisposable { + + private matchers: INumberDictionary; + private activeMatcher: ILineMatcher; + private _numberOfMatches: number; + private buffer: string[]; + private bufferLength: number; + private openModels: IStringDictionary; + private modelListeners: IDisposable[]; + + // [owner] -> AppyToKind + private applyToByOwner: Map; + // [owner] -> [resource] -> URI + private resourcesToClean: Map>; + // [owner] -> [resource] -> [markerkey] -> markerData + private markers: Map>>; + // [owner] -> [resource] -> number; + private deliveredMarkers: Map>; + + constructor(problemMatchers: ProblemMatcher[], protected markerService: IMarkerService, private modelService: IModelService) { + super(); + this.matchers = Object.create(null); + this.bufferLength = 1; + problemMatchers.map(elem => createLineMatcher(elem)).forEach((matcher) => { + let length = matcher.matchLength; + if (length > this.bufferLength) { + this.bufferLength = length; + } + let value = this.matchers[length]; + if (!value) { + value = []; + this.matchers[length] = value; + } + value.push(matcher); + }); + this.buffer = []; + this.activeMatcher = null; + this._numberOfMatches = 0; + this.openModels = Object.create(null); + this.modelListeners = []; + this.applyToByOwner = new Map(); + for (let problemMatcher of problemMatchers) { + let current = this.applyToByOwner.get(problemMatcher.owner); + if (current === void 0) { + this.applyToByOwner.set(problemMatcher.owner, problemMatcher.applyTo); + } else { + this.applyToByOwner.set(problemMatcher.owner, this.mergeApplyTo(current, problemMatcher.applyTo)); + } + } + this.resourcesToClean = new Map>(); + this.markers = new Map>>(); + this.deliveredMarkers = new Map>(); + this.modelService.onModelAdded((model) => { + this.openModels[model.uri.toString()] = true; + }, this, this.modelListeners); + this.modelService.onModelRemoved((model) => { + delete this.openModels[model.uri.toString()]; + }, this, this.modelListeners); + this.modelService.getModels().forEach(model => this.openModels[model.uri.toString()] = true); + } + + public dispose() { + this.modelListeners.forEach(disposable => disposable.dispose()); + } + + public get numberOfMatches(): number { + return this._numberOfMatches; + } + + protected tryFindMarker(line: string): ProblemMatch { + let result: ProblemMatch = null; + if (this.activeMatcher) { + result = this.activeMatcher.next(line); + if (result) { + this._numberOfMatches++; + return result; + } + this.clearBuffer(); + this.activeMatcher = null; + } + if (this.buffer.length < this.bufferLength) { + this.buffer.push(line); + } else { + let end = this.buffer.length - 1; + for (let i = 0; i < end; i++) { + this.buffer[i] = this.buffer[i + 1]; + } + this.buffer[end] = line; + } + + result = this.tryMatchers(); + if (result) { + this.clearBuffer(); + } + return result; + } + + protected isOpen(resource: URI): boolean { + return !!this.openModels[resource.toString()]; + } + + protected shouldApplyMatch(result: ProblemMatch): boolean { + switch (result.description.applyTo) { + case ApplyToKind.allDocuments: + return true; + case ApplyToKind.openDocuments: + return !!this.openModels[result.resource.toString()]; + case ApplyToKind.closedDocuments: + return !this.openModels[result.resource.toString()]; + default: + return true; + } + } + + private mergeApplyTo(current: ApplyToKind, value: ApplyToKind): ApplyToKind { + if (current === value || current === ApplyToKind.allDocuments) { + return current; + } + return ApplyToKind.allDocuments; + } + + private tryMatchers(): ProblemMatch { + this.activeMatcher = null; + let length = this.buffer.length; + for (let startIndex = 0; startIndex < length; startIndex++) { + let candidates = this.matchers[length - startIndex]; + if (!candidates) { + continue; + } + for (let i = 0; i < candidates.length; i++) { + let matcher = candidates[i]; + let result = matcher.handle(this.buffer, startIndex); + if (result.match) { + this._numberOfMatches++; + if (result.continue) { + this.activeMatcher = matcher; + } + return result.match; + } + } + } + return null; + } + + private clearBuffer(): void { + if (this.buffer.length > 0) { + this.buffer = []; + } + } + + protected recordResourcesToClean(owner: string): void { + let resourceSetToClean = this.getResourceSetToClean(owner); + this.markerService.read({ owner: owner }).forEach(marker => resourceSetToClean.set(marker.resource.toString(), marker.resource)); + } + + protected recordResourceToClean(owner: string, resource: URI): void { + this.getResourceSetToClean(owner).set(resource.toString(), resource); + } + + protected removeResourceToClean(owner: string, resource: string): void { + let resourceSet = this.resourcesToClean.get(owner); + if (resourceSet) { + resourceSet.delete(resource); + } + } + + private getResourceSetToClean(owner: string): Map { + let result = this.resourcesToClean.get(owner); + if (!result) { + result = new Map(); + this.resourcesToClean.set(owner, result); + } + return result; + } + + protected cleanAllMarkers(): void { + this.resourcesToClean.forEach((value, owner) => { + this._cleanMarkers(owner, value); + }); + this.resourcesToClean = new Map>(); + } + + protected cleanMarkers(owner: string): void { + let toClean = this.resourcesToClean.get(owner); + if (toClean) { + this._cleanMarkers(owner, toClean); + this.resourcesToClean.delete(owner); + } + } + + private _cleanMarkers(owner: string, toClean: Map): void { + let uris: URI[] = []; + let applyTo = this.applyToByOwner.get(owner); + toClean.forEach((uri, uriAsString) => { + if ( + applyTo === ApplyToKind.allDocuments || + (applyTo === ApplyToKind.openDocuments && this.openModels[uriAsString]) || + (applyTo === ApplyToKind.closedDocuments && !this.openModels[uriAsString]) + ) { + uris.push(uri); + } + }); + this.markerService.remove(owner, uris); + } + + protected recordMarker(marker: IMarkerData, owner: string, resourceAsString: string): void { + let markersPerOwner = this.markers.get(owner); + if (!markersPerOwner) { + markersPerOwner = new Map>(); + this.markers.set(owner, markersPerOwner); + } + let markersPerResource = markersPerOwner.get(resourceAsString); + if (!markersPerResource) { + markersPerResource = new Map(); + markersPerOwner.set(resourceAsString, markersPerResource); + } + let key = IMarkerData.makeKey(marker); + if (!markersPerResource.has(key)) { + markersPerResource.set(key, marker); + } + } + + protected reportMarkers(): void { + this.markers.forEach((markersPerOwner, owner) => { + let develieredMarkersPerOwner = this.getDeliveredMarkersPerOwner(owner); + markersPerOwner.forEach((markers, resource) => { + this.deliverMarkersPerOwnerAndResourceResolved(owner, resource, markers, develieredMarkersPerOwner); + }); + }); + } + + protected deliverMarkersPerOwnerAndResource(owner: string, resource: string): void { + let markersPerOwner = this.markers.get(owner); + if (!markersPerOwner) { + return; + } + let deliveredMarkersPerOwner = this.getDeliveredMarkersPerOwner(owner); + let markersPerResource = markersPerOwner.get(resource); + if (!markersPerResource) { + return; + } + this.deliverMarkersPerOwnerAndResourceResolved(owner, resource, markersPerResource, deliveredMarkersPerOwner); + } + + private deliverMarkersPerOwnerAndResourceResolved(owner: string, resource: string, markers: Map, reported: Map): void { + if (markers.size !== reported.get(resource)) { + let toSet: IMarkerData[] = []; + markers.forEach(value => toSet.push(value)); + this.markerService.changeOne(owner, URI.parse(resource), toSet); + reported.set(resource, markers.size); + } + } + + private getDeliveredMarkersPerOwner(owner): Map { + let result = this.deliveredMarkers.get(owner); + if (!result) { + result = new Map(); + this.deliveredMarkers.set(owner, result); + } + return result; + } +} + +export enum ProblemHandlingStrategy { + Clean +} + +export class StartStopProblemCollector extends AbstractProblemCollector implements IProblemMatcher { + private owners: string[]; + private strategy: ProblemHandlingStrategy; + + private currentOwner: string; + private currentResource: string; + + constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService, strategy: ProblemHandlingStrategy = ProblemHandlingStrategy.Clean) { + super(problemMatchers, markerService, modelService); + let ownerSet: { [key: string]: boolean; } = Object.create(null); + problemMatchers.forEach(description => ownerSet[description.owner] = true); + this.owners = Object.keys(ownerSet); + this.strategy = strategy; + this.owners.forEach((owner) => { + this.recordResourcesToClean(owner); + }); + } + + public processLine(line: string): void { + let markerMatch = this.tryFindMarker(line); + if (!markerMatch) { + return; + } + + let owner = markerMatch.description.owner; + let resource = markerMatch.resource; + let resourceAsString = resource.toString(); + this.removeResourceToClean(owner, resourceAsString); + let shouldApplyMatch = this.shouldApplyMatch(markerMatch); + if (shouldApplyMatch) { + this.recordMarker(markerMatch.marker, owner, resourceAsString); + if (this.currentOwner !== owner || this.currentResource !== resourceAsString) { + if (this.currentOwner && this.currentResource) { + this.deliverMarkersPerOwnerAndResource(this.currentOwner, this.currentResource); + } + this.currentOwner = owner; + this.currentResource = resourceAsString; + } + } + } + + public done(): void { + this.reportMarkers(); + this.cleanAllMarkers(); + } +} + +interface OwnedWatchingPattern { + problemMatcher: ProblemMatcher; + pattern: WatchingPattern; +} + +export class WatchingProblemCollector extends AbstractProblemCollector implements IProblemMatcher { + + private problemMatchers: ProblemMatcher[]; + private watchingBeginsPatterns: OwnedWatchingPattern[]; + private watchingEndsPatterns: OwnedWatchingPattern[]; + + // Current State + private currentOwner: string; + private currentResource: string; + + constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService) { + super(problemMatchers, markerService, modelService); + this.problemMatchers = problemMatchers; + this.resetCurrentResource(); + this.watchingBeginsPatterns = []; + this.watchingEndsPatterns = []; + this.problemMatchers.forEach(matcher => { + if (matcher.watching) { + this.watchingBeginsPatterns.push({ problemMatcher: matcher, pattern: matcher.watching.beginsPattern }); + this.watchingEndsPatterns.push({ problemMatcher: matcher, pattern: matcher.watching.endsPattern }); + } + }); + } + + public aboutToStart(): void { + this.problemMatchers.forEach(matcher => { + if (matcher.watching && matcher.watching.activeOnStart) { + this.emit(ProblemCollectorEvents.WatchingBeginDetected, {}); + this.recordResourcesToClean(matcher.owner); + } + }); + } + + public processLine(line: string): void { + if (this.tryBegin(line) || this.tryFinish(line)) { + return; + } + let markerMatch = this.tryFindMarker(line); + if (!markerMatch) { + return; + } + let resource = markerMatch.resource; + let owner = markerMatch.description.owner; + let resourceAsString = resource.toString(); + this.removeResourceToClean(owner, resourceAsString); + let shouldApplyMatch = this.shouldApplyMatch(markerMatch); + if (shouldApplyMatch) { + this.recordMarker(markerMatch.marker, owner, resourceAsString); + if (this.currentOwner !== owner || this.currentResource !== resourceAsString) { + this.reportMarkersForCurrentResource(); + this.currentOwner = owner; + this.currentResource = resourceAsString; + } + } + } + + public forceDelivery(): void { + this.reportMarkersForCurrentResource(); + } + + private tryBegin(line: string): boolean { + let result = false; + for (let i = 0; i < this.watchingBeginsPatterns.length; i++) { + let beginMatcher = this.watchingBeginsPatterns[i]; + let matches = beginMatcher.pattern.regexp.exec(line); + if (matches) { + result = true; + this.emit(ProblemCollectorEvents.WatchingBeginDetected, {}); + this.resetCurrentResource(); + let owner = beginMatcher.problemMatcher.owner; + let file = matches[beginMatcher.pattern.file]; + if (file) { + let resource = getResource(file, beginMatcher.problemMatcher); + this.recordResourceToClean(owner, resource); + } else { + this.recordResourcesToClean(owner); + } + } + } + return result; + } + + private tryFinish(line: string): boolean { + let result = false; + for (let i = 0; i < this.watchingEndsPatterns.length; i++) { + let endMatcher = this.watchingEndsPatterns[i]; + let matches = endMatcher.pattern.regexp.exec(line); + if (matches) { + this.emit(ProblemCollectorEvents.WatchingEndDetected, {}); + result = true; + let owner = endMatcher.problemMatcher.owner; + this.resetCurrentResource(); + this.cleanMarkers(owner); + } + } + return result; + } + + public done(): void { + this.reportMarkers(); + this.cleanAllMarkers(); + } + + private resetCurrentResource(): void { + this.reportMarkersForCurrentResource(); + this.currentOwner = null; + this.currentResource = null; + } + + private reportMarkersForCurrentResource(): void { + if (this.currentOwner && this.currentResource) { + this.deliverMarkersPerOwnerAndResource(this.currentOwner, this.currentResource); + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts new file mode 100644 index 0000000000..45d040e094 --- /dev/null +++ b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as Types from 'vs/base/common/types'; +import * as Objects from 'vs/base/common/objects'; + +import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry'; + +import * as Tasks from 'vs/workbench/parts/tasks/common/tasks'; + + +const taskDefinitionSchema: IJSONSchema = { + type: 'object', + additionalProperties: false, + properties: { + type: { + type: 'string', + description: nls.localize('TaskDefinition.description', 'The actual task type') + }, + required: { + type: 'array', + items: { + type: 'string' + } + }, + properties: { + type: 'object', + description: nls.localize('TaskDefinition.properties', 'Additional properties of the task type'), + additionalProperties: { + $ref: 'http://json-schema.org/draft-04/schema#' + } + } + } +}; + +namespace Configuration { + export interface TaskDefinition { + type?: string; + required?: string[]; + properties?: IJSONSchemaMap; + } + + export function from(value: TaskDefinition, messageCollector: ExtensionMessageCollector): Tasks.TaskDefinition { + if (!value) { + return undefined; + } + let taskType = Types.isString(value.type) ? value.type : undefined; + if (!taskType || taskType.length === 0) { + messageCollector.error(nls.localize('TaskTypeConfiguration.noType', 'The task type configuration is missing the required \'taskType\' property')); + return undefined; + } + let required: string[] = []; + if (Array.isArray(value.required)) { + for (let element of value.required) { + if (Types.isString(element)) { + required.push(element); + } + } + } + return { taskType, required: required.length >= 0 ? required : undefined, properties: value.properties ? Objects.deepClone(value.properties) : undefined }; + } +} + + +const taskDefinitionsExtPoint = ExtensionsRegistry.registerExtensionPoint('taskDefinitions', [], { + description: nls.localize('TaskDefinitionExtPoint', 'Contributes task kinds'), + type: 'array', + items: taskDefinitionSchema +}); + +export interface ITaskDefinitionRegistry { + onReady(): TPromise; + + exists(key: string): boolean; + get(key: string): Tasks.TaskDefinition; + all(): Tasks.TaskDefinition[]; +} + +class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { + + private taskTypes: IStringDictionary; + private readyPromise: TPromise; + + constructor() { + this.taskTypes = Object.create(null); + this.readyPromise = new TPromise((resolve, reject) => { + taskDefinitionsExtPoint.setHandler((extensions) => { + try { + extensions.forEach(extension => { + let taskTypes = extension.value; + for (let taskType of taskTypes) { + let type = Configuration.from(taskType, extension.collector); + if (type) { + this.taskTypes[type.taskType] = type; + } + } + }); + } catch (error) { + } + resolve(undefined); + }); + }); + } + + public onReady(): TPromise { + return this.readyPromise; + } + + public get(key: string): Tasks.TaskDefinition { + return this.taskTypes[key]; + } + + public exists(key: string): boolean { + return !!this.taskTypes[key]; + } + + public all(): Tasks.TaskDefinition[] { + return Object.keys(this.taskTypes).map(key => this.taskTypes[key]); + } +} + +export const TaskDefinitionRegistry: ITaskDefinitionRegistry = new TaskDefinitionRegistryImpl(); \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/common/taskService.ts b/src/vs/workbench/parts/tasks/common/taskService.ts new file mode 100644 index 0000000000..5bc9a00cd3 --- /dev/null +++ b/src/vs/workbench/parts/tasks/common/taskService.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { IEventEmitter } from 'vs/base/common/eventEmitter'; +import { LinkedMap } from 'vs/base/common/map'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Task, ContributedTask, CustomTask, TaskSet } from 'vs/workbench/parts/tasks/common/tasks'; +import { ITaskSummary, TaskEvent, TaskType, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem'; + +export { ITaskSummary, Task, TaskEvent, TaskType, TaskTerminateResponse }; + +export const ITaskService = createDecorator('taskService'); + +export namespace TaskServiceEvents { + export let Active: string = 'active'; + export let Inactive: string = 'inactive'; + export let ConfigChanged: string = 'configChanged'; + export let Terminated: string = 'terminated'; + export let Changed: string = 'changed'; +} + +export interface ITaskProvider { + provideTasks(): TPromise; +} + +export interface RunOptions { + attachProblemMatcher?: boolean; +} + +export interface CustomizationProperties { + group?: string | { kind?: string; isDefault?: boolean; }; + problemMatcher?: string | string[]; +} + +export interface ITaskService extends IEventEmitter { + _serviceBrand: any; + configureAction(): Action; + build(): TPromise; + rebuild(): TPromise; + clean(): TPromise; + runTest(): TPromise; + run(task: string | Task, options?: RunOptions): TPromise; + inTerminal(): boolean; + isActive(): TPromise; + getActiveTasks(): TPromise; + restart(task: string | Task): void; + terminate(task: string | Task): TPromise; + terminateAll(): TPromise; + tasks(): TPromise; + /** + * @param identifier The task's name, label or defined identifier. + */ + getTask(identifier: string): TPromise; + getTasksForGroup(group: string): TPromise; + getRecentlyUsedTasks(): LinkedMap; + + canCustomize(): boolean; + customize(task: ContributedTask | CustomTask, properties?: {}, openConfig?: boolean): TPromise; + openConfig(task: CustomTask): TPromise; + + registerTaskProvider(handle: number, taskProvider: ITaskProvider): void; + unregisterTaskProvider(handle: number): boolean; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/common/taskSystem.ts b/src/vs/workbench/parts/tasks/common/taskSystem.ts new file mode 100644 index 0000000000..638529cee4 --- /dev/null +++ b/src/vs/workbench/parts/tasks/common/taskSystem.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { TerminateResponse } from 'vs/base/common/processes'; +import { IEventEmitter } from 'vs/base/common/eventEmitter'; +import { Task } from './tasks'; + +export enum TaskErrors { + NotConfigured, + RunningTask, + NoBuildTask, + NoTestTask, + ConfigValidationError, + TaskNotFound, + NoValidTaskRunner, + UnknownError +} + +export class TaskError { + public severity: Severity; + public message: string; + public code: TaskErrors; + + constructor(severity: Severity, message: string, code: TaskErrors) { + this.severity = severity; + this.message = message; + this.code = code; + } +} + +export interface TelemetryEvent { + // How the task got trigger. Is either shortcut or command + trigger: string; + + runner: 'terminal' | 'output'; + + taskKind: string; + + // The command triggered + command: string; + + // Whether the task ran successful + success: boolean; + + // The exit code + exitCode?: number; +} + +export namespace Triggers { + export let shortcut: string = 'shortcut'; + export let command: string = 'command'; +} + +export interface ITaskSummary { + /** + * Exit code of the process. + */ + exitCode?: number; +} + +export enum TaskExecuteKind { + Started = 1, + Active = 2 +} + +export interface ITaskExecuteResult { + kind: TaskExecuteKind; + promise: TPromise; + started?: { + restartOnFileChanges?: string; + }; + active?: { + same: boolean; + background: boolean; + }; +} + +export namespace TaskSystemEvents { + export let Active: string = 'active'; + export let Inactive: string = 'inactive'; + export let Terminated: string = 'terminated'; + export let Changed: string = 'changed'; +} + +export enum TaskType { + SingleRun, + Watching +} + +export interface TaskEvent { + taskId?: string; + taskName?: string; + type?: TaskType; + group?: string; + __task?: Task; +} + +export interface ITaskResolver { + resolve(identifier: string): Task; +} + +export interface TaskTerminateResponse extends TerminateResponse { + task: Task | undefined; +} + +export interface ITaskSystem extends IEventEmitter { + run(task: Task, resolver: ITaskResolver): ITaskExecuteResult; + isActive(): TPromise; + isActiveSync(): boolean; + getActiveTasks(): Task[]; + canAutoTerminate(): boolean; + terminate(id: string): TPromise; + terminateAll(): TPromise; + revealTask(task: Task): boolean; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/common/taskTemplates.ts b/src/vs/workbench/parts/tasks/common/taskTemplates.ts new file mode 100644 index 0000000000..51faf1c519 --- /dev/null +++ b/src/vs/workbench/parts/tasks/common/taskTemplates.ts @@ -0,0 +1,350 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; + +import { IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; + +export interface TaskEntry extends IPickOpenEntry { + sort?: string; + autoDetect: boolean; + content: string; +} + +const dotnetBuild: TaskEntry = { + id: 'dotnetCore', + label: '.NET Core', + sort: 'NET Core', + autoDetect: false, + description: nls.localize('dotnetCore', 'Executes .NET Core build command'), + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "2.0.0",', + '\t"tasks": [', + '\t\t{', + '\t\t\t"taskName": "build",', + '\t\t\t"command": "dotnet build",', + '\t\t\t"type": "shell",', + '\t\t\t"group": "build",', + '\t\t\t"presentation": {', + '\t\t\t\t"reveal": "silent"', + '\t\t\t},', + '\t\t\t"problemMatcher": "$msCompile"', + '\t\t}', + '\t]', + '}' + ].join('\n') +}; + +const msbuild: TaskEntry = { + id: 'msbuild', + label: 'MSBuild', + autoDetect: false, + description: nls.localize('msbuild', 'Executes the build target'), + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "2.0.0",', + '\t"tasks": [', + '\t\t{', + '\t\t\t"taskName": "build",', + '\t\t\t"type": "process",', + '\t\t\t"command": "msbuild",', + '\t\t\t"args": [', + '\t\t\t\t// Ask msbuild to generate full paths for file names.', + '\t\t\t\t"/property:GenerateFullPaths=true",', + '\t\t\t\t"/t:build"', + '\t\t\t],', + '\t\t\t"group": "build",', + '\t\t\t"presentation": {', + '\t\t\t\t// Reveal the output only if unrecognized errors occur.', + '\t\t\t\t"reveal": "silent"', + '\t\t\t},', + '\t\t\t// Use the standard MS compiler pattern to detect errors, warnings and infos', + '\t\t\t"problemMatcher": "$msCompile"', + '\t\t}', + '\t]', + '}' + ].join('\n') +}; + +const command: TaskEntry = { + id: 'externalCommand', + label: 'Others', + autoDetect: false, + description: nls.localize('externalCommand', 'Example to run an arbitrary external command'), + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "2.0.0",', + '\t"tasks": [', + '\t\t{', + '\t\t\t"taskName": "echo",', + '\t\t\t"type": "shell",', + '\t\t\t"command": "echo Hello"', + '\t\t}', + '\t]', + '}' + ].join('\n') +}; + +const maven: TaskEntry = { + id: 'maven', + label: 'maven', + sort: 'MVN', + autoDetect: false, + description: nls.localize('Maven', 'Executes common maven commands'), + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "2.0.0",', + '\t"tasks": [', + '\t\t{', + '\t\t\t"taskName": "verify",', + '\t\t\t"type": "shell",', + '\t\t\t"command": "mvn -B verify",', + '\t\t\t"group": "build"', + '\t\t},', + '\t\t{', + '\t\t\t"taskName": "test",', + '\t\t\t"type": "shell",', + '\t\t\t"command": "mvn -B test",', + '\t\t\t"group": "test"', + '\t\t}', + '\t]', + '}' + ].join('\n') +}; + +export let templates: TaskEntry[] = [dotnetBuild, msbuild, maven].sort((a, b) => { + return (a.sort || a.label).localeCompare(b.sort || b.label); +}); +templates.push(command); + + +/** Version 1.0 templates + * +const gulp: TaskEntry = { + id: 'gulp', + label: 'Gulp', + autoDetect: true, + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "0.1.0",', + '\t"command": "gulp",', + '\t"isShellCommand": true,', + '\t"args": ["--no-color"],', + '\t"showOutput": "always"', + '}' + ].join('\n') +}; + +const grunt: TaskEntry = { + id: 'grunt', + label: 'Grunt', + autoDetect: true, + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "0.1.0",', + '\t"command": "grunt",', + '\t"isShellCommand": true,', + '\t"args": ["--no-color"],', + '\t"showOutput": "always"', + '}' + ].join('\n') +}; + +const npm: TaskEntry = { + id: 'npm', + label: 'npm', + sort: 'NPM', + autoDetect: false, + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "0.1.0",', + '\t"command": "npm",', + '\t"isShellCommand": true,', + '\t"showOutput": "always",', + '\t"suppressTaskName": true,', + '\t"tasks": [', + '\t\t{', + '\t\t\t"taskName": "install",', + '\t\t\t"args": ["install"]', + '\t\t},', + '\t\t{', + '\t\t\t"taskName": "update",', + '\t\t\t"args": ["update"]', + '\t\t},', + '\t\t{', + '\t\t\t"taskName": "test",', + '\t\t\t"args": ["run", "test"]', + '\t\t}', + '\t]', + '}' + ].join('\n') +}; + +const tscConfig: TaskEntry = { + id: 'tsc.config', + label: 'TypeScript - tsconfig.json', + autoDetect: false, + description: nls.localize('tsc.config', 'Compiles a TypeScript project'), + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "0.1.0",', + '\t"command": "tsc",', + '\t"isShellCommand": true,', + '\t"args": ["-p", "."],', + '\t"showOutput": "silent",', + '\t"problemMatcher": "$tsc"', + '}' + ].join('\n') +}; + +const tscWatch: TaskEntry = { + id: 'tsc.watch', + label: 'TypeScript - Watch Mode', + autoDetect: false, + description: nls.localize('tsc.watch', 'Compiles a TypeScript project in watch mode'), + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "0.1.0",', + '\t"command": "tsc",', + '\t"isShellCommand": true,', + '\t"args": ["-w", "-p", "."],', + '\t"showOutput": "silent",', + '\t"isBackground": true,', + '\t"problemMatcher": "$tsc-watch"', + '}' + ].join('\n') +}; + +const dotnetBuild: TaskEntry = { + id: 'dotnetCore', + label: '.NET Core', + sort: 'NET Core', + autoDetect: false, + description: nls.localize('dotnetCore', 'Executes .NET Core build command'), + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "0.1.0",', + '\t"command": "dotnet",', + '\t"isShellCommand": true,', + '\t"args": [],', + '\t"tasks": [', + '\t\t{', + '\t\t\t"taskName": "build",', + '\t\t\t"args": [ ],', + '\t\t\t"isBuildCommand": true,', + '\t\t\t"showOutput": "silent",', + '\t\t\t"problemMatcher": "$msCompile"', + '\t\t}', + '\t]', + '}' + ].join('\n') +}; + +const msbuild: TaskEntry = { + id: 'msbuild', + label: 'MSBuild', + autoDetect: false, + description: nls.localize('msbuild', 'Executes the build target'), + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "0.1.0",', + '\t"command": "msbuild",', + '\t"args": [', + '\t\t// Ask msbuild to generate full paths for file names.', + '\t\t"/property:GenerateFullPaths=true"', + '\t],', + '\t"taskSelector": "/t:",', + '\t"showOutput": "silent",', + '\t"tasks": [', + '\t\t{', + '\t\t\t"taskName": "build",', + '\t\t\t// Show the output window only if unrecognized errors occur.', + '\t\t\t"showOutput": "silent",', + '\t\t\t// Use the standard MS compiler pattern to detect errors, warnings and infos', + '\t\t\t"problemMatcher": "$msCompile"', + '\t\t}', + '\t]', + '}' + ].join('\n') +}; + +const command: TaskEntry = { + id: 'externalCommand', + label: 'Others', + autoDetect: false, + description: nls.localize('externalCommand', 'Example to run an arbitrary external command'), + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "0.1.0",', + '\t"command": "echo",', + '\t"isShellCommand": true,', + '\t"args": ["Hello World"],', + '\t"showOutput": "always"', + '}' + ].join('\n') +}; + +const maven: TaskEntry = { + id: 'maven', + label: 'maven', + sort: 'MVN', + autoDetect: false, + description: nls.localize('Maven', 'Executes common maven commands'), + content: [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + '\t"version": "0.1.0",', + '\t"command": "mvn",', + '\t"isShellCommand": true,', + '\t"showOutput": "always",', + '\t"suppressTaskName": true,', + '\t"tasks": [', + '\t\t{', + '\t\t\t"taskName": "verify",', + '\t\t\t"args": ["-B", "verify"],', + '\t\t\t"isBuildCommand": true', + '\t\t},', + '\t\t{', + '\t\t\t"taskName": "test",', + '\t\t\t"args": ["-B", "test"],', + '\t\t\t"isTestCommand": true', + '\t\t}', + '\t]', + '}' + ].join('\n') +}; + +export let templates: TaskEntry[] = [gulp, grunt, tscConfig, tscWatch, dotnetBuild, msbuild, npm, maven].sort((a, b) => { + return (a.sort || a.label).localeCompare(b.sort || b.label); +}); +templates.push(command); +*/ diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts new file mode 100644 index 0000000000..c156e3ca62 --- /dev/null +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -0,0 +1,455 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as Types from 'vs/base/common/types'; +import { IJSONSchemaMap } from 'vs/base/common/jsonSchema'; + +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ProblemMatcher } from 'vs/platform/markers/common/problemMatcher'; + +export interface ShellConfiguration { + /** + * The shell executable. + */ + executable: string; + /** + * The arguments to be passed to the shell executable. + */ + args?: string[]; +} + +export namespace ShellConfiguration { + export function is(value: any): value is ShellConfiguration { + let candidate: ShellConfiguration = value; + return candidate && Types.isString(candidate.executable) && (candidate.args === void 0 || Types.isStringArray(candidate.args)); + } +} + +export interface CommandOptions { + + /** + * The shell to use if the task is a shell command. + */ + shell?: ShellConfiguration; + + /** + * The current working directory of the executed program or shell. + * If omitted VSCode's current workspace root is used. + */ + cwd?: string; + + /** + * The environment of the executed program or shell. If omitted + * the parent process' environment is used. + */ + env?: { [key: string]: string; }; +} + +export enum RevealKind { + /** + * Always brings the terminal to front if the task is executed. + */ + Always = 1, + + /** + * Only brings the terminal to front if a problem is detected executing the task + * (e.g. the task couldn't be started because). + */ + Silent = 2, + + /** + * The terminal never comes to front when the task is executed. + */ + Never = 3 +} + +export namespace RevealKind { + export function fromString(value: string): RevealKind { + switch (value.toLowerCase()) { + case 'always': + return RevealKind.Always; + case 'silent': + return RevealKind.Silent; + case 'never': + return RevealKind.Never; + default: + return RevealKind.Always; + } + } +} + +export enum PanelKind { + + /** + * Shares a panel with other tasks. This is the default. + */ + Shared = 1, + + /** + * Uses a dedicated panel for this tasks. The panel is not + * shared with other tasks. + */ + Dedicated = 2, + + /** + * Creates a new panel whenever this task is executed. + */ + New = 3 +} + +export namespace PanelKind { + export function fromString(value: string): PanelKind { + switch (value.toLowerCase()) { + case 'shared': + return PanelKind.Shared; + case 'dedicated': + return PanelKind.Dedicated; + case 'new': + return PanelKind.New; + default: + return PanelKind.Shared; + } + } +} + +export interface PresentationOptions { + /** + * Controls whether the task output is reveal in the user interface. + * Defaults to `RevealKind.Always`. + */ + reveal: RevealKind; + + /** + * Controls whether the command associated with the task is echoed + * in the user interface. + */ + echo: boolean; + + /** + * Controls whether the panel showing the task output is taking focus. + */ + focus: boolean; + + /** + * Controls if the task panel is used for this task only (dedicated), + * shared between tasks (shared) or if a new panel is created on + * every task execution (new). Defaults to `TaskInstanceKind.Shared` + */ + panel: PanelKind; +} + +export enum RuntimeType { + Shell = 1, + Process = 2 +} + +export namespace RuntimeType { + export function fromString(value: string): RuntimeType { + switch (value.toLowerCase()) { + case 'shell': + return RuntimeType.Shell; + case 'process': + return RuntimeType.Process; + default: + return RuntimeType.Process; + } + } +} + +export interface CommandConfiguration { + + /** + * The task type + */ + runtime: RuntimeType; + + /** + * The command to execute + */ + name: string; + + /** + * Additional command options. + */ + options?: CommandOptions; + + /** + * Command arguments. + */ + args?: string[]; + + /** + * The task selector if needed. + */ + taskSelector?: string; + + /** + * Whether to suppress the task name when merging global args + * + */ + suppressTaskName?: boolean; + + /** + * Describes how the task is presented in the UI. + */ + presentation: PresentationOptions; +} + +export namespace TaskGroup { + export const Clean: 'clean' = 'clean'; + + export const Build: 'build' = 'build'; + + export const Rebuild: 'rebuild' = 'rebuild'; + + export const Test: 'test' = 'test'; + + export function is(value: string): value is string { + return value === Clean || value === Build || value === Rebuild || value === Test; + } +} + +export type TaskGroup = 'clean' | 'build' | 'rebuild' | 'test'; + + +export namespace TaskSourceKind { + export const Workspace: 'workspace' = 'workspace'; + export const Extension: 'extension' = 'extension'; + export const Composite: 'composite' = 'composite'; +} + +export interface TaskSourceConfigElement { + file: string; + index: number; + element: any; +} + +export interface WorkspaceTaskSource { + kind: 'workspace'; + label: string; + config: TaskSourceConfigElement; + customizes?: TaskIdentifier; +} + +export interface ExtensionTaskSource { + kind: 'extension'; + label: string; + extension: string; +} + +export interface CompositeTaskSource { + kind: 'composite'; + label: string; +} + +export type TaskSource = WorkspaceTaskSource | ExtensionTaskSource | CompositeTaskSource; + +export interface TaskIdentifier { + _key: string; + type: string; +} + +export interface ConfigurationProperties { + + /** + * The task's name + */ + name?: string; + + /** + * The task's name + */ + identifier?: string; + + /** + * the task's group; + */ + group?: string; + + /** + * Whether this task is a primary task in the task group. + */ + isDefaultGroupEntry?: boolean; + + /** + * The presentation options + */ + presentation?: PresentationOptions; + + /** + * Whether the task is a background task or not. + */ + isBackground?: boolean; + + /** + * Whether the task should prompt on close for confirmation if running. + */ + promptOnClose?: boolean; + + /** + * The other tasks this task depends on. + */ + dependsOn?: string[]; + + /** + * The problem watchers to use for this task + */ + problemMatchers?: (string | ProblemMatcher)[]; +} + +export interface CommonTask { + + /** + * The task's internal id + */ + _id: string; + + /** + * The cached label. + */ + _label: string; + + type: string; +} + +export interface CustomTask extends CommonTask, ConfigurationProperties { + + type: 'custom'; + + /** + * Indicated the source of the task (e.g tasks.json or extension) + */ + _source: WorkspaceTaskSource; + + name: string; + + identifier: string; + + /** + * The command configuration + */ + command: CommandConfiguration; +} + +export namespace CustomTask { + export function is(value: any): value is CustomTask { + let candidate: CustomTask = value; + return candidate && candidate.type === 'custom'; + } +} + +export interface ConfiguringTask extends CommonTask, ConfigurationProperties { + + /** + * Indicated the source of the task (e.g tasks.json or extension) + */ + _source: WorkspaceTaskSource; + + configures: TaskIdentifier; +} + +export namespace ConfiguringTask { + export function is(value: any): value is ConfiguringTask { + let candidate: ConfiguringTask = value; + return candidate && candidate.configures && Types.isString(candidate.configures.type) && value.command === void 0; + } +} + +export interface ContributedTask extends CommonTask, ConfigurationProperties { + + /** + * Indicated the source of the task (e.g tasks.json or extension) + */ + _source: ExtensionTaskSource; + + defines: TaskIdentifier; + + hasDefinedMatchers: boolean; + + /** + * The command configuration + */ + command: CommandConfiguration; +} + +export namespace ContributedTask { + export function is(value: any): value is ContributedTask { + let candidate: ContributedTask = value; + return candidate && candidate.defines && Types.isString(candidate.defines.type) && candidate.command !== void 0; + } +} + +export interface CompositeTask extends CommonTask, ConfigurationProperties { + /** + * Indicated the source of the task (e.g tasks.json or extension) + */ + _source: CompositeTaskSource; + + type: 'composite'; + + identifier: string; +} + +export namespace CompositeTask { + export function is(value: any): value is CompositeTask { + let candidate = value as CompositeTask; + return candidate && candidate._source && candidate._source.kind === TaskSourceKind.Composite; + } +} + +export type Task = CustomTask | ContributedTask | CompositeTask; + +export namespace Task { + export function getKey(task: Task): string { + if (CustomTask.is(task) || CompositeTask.is(task)) { + return task.identifier; + } else { + return task.defines._key; + } + } + + export function getTelemetryKind(task: Task): string { + if (ContributedTask.is(task)) { + return 'extension'; + } else if (CustomTask.is(task)) { + if (task._source.customizes) { + return 'workspace>extension'; + } else { + return 'workspace'; + } + } else if (CompositeTask.is(task)) { + return 'composite'; + } else { + return 'unknown'; + } + } +} + + +export enum ExecutionEngine { + Process = 1, + Terminal = 2 +} + +export namespace ExecutionEngine { + export const _default: ExecutionEngine = ExecutionEngine.Terminal; +} + +export enum JsonSchemaVersion { + V0_1_0 = 1, + V2_0_0 = 2 +} + +export interface TaskSet { + tasks: Task[]; + extension?: IExtensionDescription; +} + +export interface TaskDefinition { + taskType: string; + required: string[]; + properties: IJSONSchemaMap; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.ts new file mode 100644 index 0000000000..8d3b398c0a --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.ts @@ -0,0 +1,244 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; + +import { Schemas } from 'vs/platform/markers/common/problemMatcher'; + +const schema: IJSONSchema = { + definitions: { + showOutputType: { + type: 'string', + enum: ['always', 'silent', 'never'] + }, + options: { + type: 'object', + description: nls.localize('JsonSchema.options', 'Additional command options'), + properties: { + cwd: { + type: 'string', + description: nls.localize('JsonSchema.options.cwd', 'The current working directory of the executed program or script. If omitted Code\'s current workspace root is used.') + }, + env: { + type: 'object', + additionalProperties: { + type: 'string' + }, + description: nls.localize('JsonSchema.options.env', 'The environment of the executed program or shell. If omitted the parent process\' environment is used.') + } + }, + additionalProperties: { + type: ['string', 'array', 'object'] + } + }, + problemMatcherType: { + oneOf: [ + { + type: 'string', + }, + Schemas.LegacyProblemMatcher, + { + type: 'array', + items: { + anyOf: [ + Schemas.LegacyProblemMatcher, + { + type: 'string', + } + ] + } + } + ] + }, + shellConfiguration: { + type: 'object', + additionalProperties: false, + description: nls.localize('JsonSchema.shellConfiguration', 'Configures the shell to be used.'), + properties: { + executable: { + type: 'string', + description: nls.localize('JsonSchema.shell.executable', 'The shell to be used.') + }, + args: { + type: 'array', + description: nls.localize('JsonSchema.shell.args', 'The shell arguments.'), + items: { + type: 'string' + } + } + } + }, + commandConfiguration: { + type: 'object', + additionalProperties: false, + properties: { + command: { + type: 'string', + description: nls.localize('JsonSchema.command', 'The command to be executed. Can be an external program or a shell command.') + }, + args: { + type: 'array', + description: nls.localize('JsonSchema.tasks.args', 'Arguments passed to the command when this task is invoked.'), + items: { + type: 'string' + } + }, + options: { + $ref: '#/definitions/options' + } + } + }, + taskDescription: { + type: 'object', + required: ['taskName'], + additionalProperties: false, + properties: { + taskName: { + type: 'string', + description: nls.localize('JsonSchema.tasks.taskName', "The task's name") + }, + command: { + type: 'string', + description: nls.localize('JsonSchema.command', 'The command to be executed. Can be an external program or a shell command.') + }, + args: { + type: 'array', + description: nls.localize('JsonSchema.tasks.args', 'Arguments passed to the command when this task is invoked.'), + items: { + type: 'string' + } + }, + options: { + $ref: '#/definitions/options' + }, + windows: { + $ref: '#/definitions/commandConfiguration', + description: nls.localize('JsonSchema.tasks.windows', 'Windows specific command configuration') + }, + osx: { + $ref: '#/definitions/commandConfiguration', + description: nls.localize('JsonSchema.tasks.mac', 'Mac specific command configuration') + }, + linux: { + $ref: '#/definitions/commandConfiguration', + description: nls.localize('JsonSchema.tasks.linux', 'Linux specific command configuration') + }, + suppressTaskName: { + type: 'boolean', + description: nls.localize('JsonSchema.tasks.suppressTaskName', 'Controls whether the task name is added as an argument to the command. If omitted the globally defined value is used.'), + default: true + }, + showOutput: { + $ref: '#/definitions/showOutputType', + description: nls.localize('JsonSchema.tasks.showOutput', 'Controls whether the output of the running task is shown or not. If omitted the globally defined value is used.') + }, + echoCommand: { + type: 'boolean', + description: nls.localize('JsonSchema.echoCommand', 'Controls whether the executed command is echoed to the output. Default is false.'), + default: true + }, + isWatching: { + type: 'boolean', + deprecationMessage: nls.localize('JsonSchema.tasks.watching.deprecation', 'Deprecated. Use isBackground instead.'), + description: nls.localize('JsonSchema.tasks.watching', 'Whether the executed task is kept alive and is watching the file system.'), + default: true + }, + isBackground: { + type: 'boolean', + description: nls.localize('JsonSchema.tasks.background', 'Whether the executed task is kept alive and is running in the background.'), + default: true + }, + promptOnClose: { + type: 'boolean', + description: nls.localize('JsonSchema.tasks.promptOnClose', 'Whether the user is prompted when VS Code closes with a running task.'), + default: false + }, + isBuildCommand: { + type: 'boolean', + description: nls.localize('JsonSchema.tasks.build', 'Maps this task to Code\'s default build command.'), + default: true + }, + isTestCommand: { + type: 'boolean', + description: nls.localize('JsonSchema.tasks.test', 'Maps this task to Code\'s default test command.'), + default: true + }, + problemMatcher: { + $ref: '#/definitions/problemMatcherType', + description: nls.localize('JsonSchema.tasks.matchers', 'The problem matcher(s) to use. Can either be a string or a problem matcher definition or an array of strings and problem matchers.') + } + } + }, + taskRunnerConfiguration: { + type: 'object', + properties: { + command: { + type: 'string', + description: nls.localize('JsonSchema.command', 'The command to be executed. Can be an external program or a shell command.') + }, + args: { + type: 'array', + description: nls.localize('JsonSchema.args', 'Additional arguments passed to the command.'), + items: { + type: 'string' + } + }, + options: { + $ref: '#/definitions/options' + }, + showOutput: { + $ref: '#/definitions/showOutputType', + description: nls.localize('JsonSchema.showOutput', 'Controls whether the output of the running task is shown or not. If omitted \'always\' is used.') + }, + isWatching: { + type: 'boolean', + deprecationMessage: nls.localize('JsonSchema.watching.deprecation', 'Deprecated. Use isBackground instead.'), + description: nls.localize('JsonSchema.watching', 'Whether the executed task is kept alive and is watching the file system.'), + default: true + }, + isBackground: { + type: 'boolean', + description: nls.localize('JsonSchema.background', 'Whether the executed task is kept alive and is running in the background.'), + default: true + }, + promptOnClose: { + type: 'boolean', + description: nls.localize('JsonSchema.promptOnClose', 'Whether the user is prompted when VS Code closes with a running background task.'), + default: false + }, + echoCommand: { + type: 'boolean', + description: nls.localize('JsonSchema.echoCommand', 'Controls whether the executed command is echoed to the output. Default is false.'), + default: true + }, + suppressTaskName: { + type: 'boolean', + description: nls.localize('JsonSchema.suppressTaskName', 'Controls whether the task name is added as an argument to the command. Default is false.'), + default: true + }, + taskSelector: { + type: 'string', + description: nls.localize('JsonSchema.taskSelector', 'Prefix to indicate that an argument is task.') + }, + problemMatcher: { + $ref: '#/definitions/problemMatcherType', + description: nls.localize('JsonSchema.matchers', 'The problem matcher(s) to use. Can either be a string or a problem matcher definition or an array of strings and problem matchers.') + }, + tasks: { + type: 'array', + description: nls.localize('JsonSchema.tasks', 'The task configurations. Usually these are enrichments of task already defined in the external task runner.'), + items: { + type: 'object', + $ref: '#/definitions/taskDescription' + } + } + } + } + } +}; + +export default schema; \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.ts new file mode 100644 index 0000000000..bbd1ce5242 --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import * as Objects from 'vs/base/common/objects'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; + +import { ProblemMatcherRegistry } from 'vs/platform/markers/common/problemMatcher'; + +import commonSchema from './jsonSchemaCommon'; + +const schema: IJSONSchema = { + oneOf: [ + { + allOf: [ + { + type: 'object', + required: ['version'], + properties: { + version: { + type: 'string', + enum: ['0.1.0'], + description: nls.localize('JsonSchema.version', 'The config\'s version number') + }, + _runner: { + deprecationMessage: nls.localize('JsonSchema._runner', 'The runner has graduated. Use the offical runner property') + }, + runner: { + type: 'string', + enum: ['process', 'terminal'], + default: 'process', + description: nls.localize('JsonSchema.runner', 'Defines whether the task is executed as a process and the output is shown in the output window or inside the terminal.') + }, + windows: { + $ref: '#/definitions/taskRunnerConfiguration', + description: nls.localize('JsonSchema.windows', 'Windows specific command configuration') + }, + osx: { + $ref: '#/definitions/taskRunnerConfiguration', + description: nls.localize('JsonSchema.mac', 'Mac specific command configuration') + }, + linux: { + $ref: '#/definitions/taskRunnerConfiguration', + description: nls.localize('JsonSchema.linux', 'Linux specific command configuration') + } + } + }, + { + $ref: '#/definitions/taskRunnerConfiguration' + } + ] + } + ] +}; + +const shellCommand: IJSONSchema = { + type: 'boolean', + default: true, + description: nls.localize('JsonSchema.shell', 'Specifies whether the command is a shell command or an external program. Defaults to false if omitted.') +}; + +schema.definitions = Objects.deepClone(commonSchema.definitions); +let definitions = schema.definitions; +definitions['commandConfiguration']['properties']['isShellCommand'] = Objects.deepClone(shellCommand); +definitions['taskDescription']['properties']['isShellCommand'] = Objects.deepClone(shellCommand); +definitions['taskRunnerConfiguration']['properties']['isShellCommand'] = Objects.deepClone(shellCommand); + +Object.getOwnPropertyNames(definitions).forEach(key => { + let newKey = key + '1'; + definitions[newKey] = definitions[key]; + delete definitions[key]; +}); + +function fixReferences(literal: any) { + if (Array.isArray(literal)) { + literal.forEach(fixReferences); + } else if (typeof literal === 'object') { + if (literal['$ref']) { + literal['$ref'] = literal['$ref'] + '1'; + } + Object.getOwnPropertyNames(literal).forEach(property => { + let value = literal[property]; + if (Array.isArray(value) || typeof value === 'object') { + fixReferences(value); + } + }); + } +} +fixReferences(schema); + +ProblemMatcherRegistry.onReady().then(() => { + try { + let matcherIds = ProblemMatcherRegistry.keys().map(key => '$' + key); + definitions.problemMatcherType1.oneOf[0].enum = matcherIds; + (definitions.problemMatcherType1.oneOf[2].items as IJSONSchema).anyOf[1].enum = matcherIds; + } catch (err) { + console.log('Installing problem matcher ids failed'); + } +}); + +export default schema; \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts new file mode 100644 index 0000000000..3b085a6d30 --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts @@ -0,0 +1,338 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import * as Objects from 'vs/base/common/objects'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; + +import commonSchema from './jsonSchemaCommon'; + +import { ProblemMatcherRegistry } from 'vs/platform/markers/common/problemMatcher'; +import { TaskDefinitionRegistry } from '../common/taskDefinitionRegistry'; + +function fixReferences(literal: any) { + if (Array.isArray(literal)) { + literal.forEach(fixReferences); + } else if (typeof literal === 'object') { + if (literal['$ref']) { + literal['$ref'] = literal['$ref'] + '2'; + } + Object.getOwnPropertyNames(literal).forEach(property => { + let value = literal[property]; + if (Array.isArray(value) || typeof value === 'object') { + fixReferences(value); + } + }); + } +} + +const shellCommand: IJSONSchema = { + anyOf: [ + { + type: 'boolean', + default: true, + description: nls.localize('JsonSchema.shell', 'Specifies whether the command is a shell command or an external program. Defaults to false if omitted.') + }, + { + $ref: '#definitions/shellConfiguration' + } + ], + deprecationMessage: nls.localize('JsonSchema.tasks.isShellCommand.deprecated', 'The property isShellCommand is deprecated. Use the type property of the task and the shell property in the options instead. See also the 1.14 release notes.') +}; + +const dependsOn: IJSONSchema = { + anyOf: [ + { + type: 'string', + default: true, + description: nls.localize('JsonSchema.tasks.dependsOn.string', 'Another task this task depends on.') + }, + { + type: 'array', + description: nls.localize('JsonSchema.tasks.dependsOn.array', 'The other tasks this task depends on.'), + items: { + type: 'string' + } + } + ] +}; + +const presentation: IJSONSchema = { + type: 'object', + default: { + echo: true, + reveal: 'always', + focus: false, + panel: 'shared' + }, + description: nls.localize('JsonSchema.tasks.presentation', 'Configures the panel that is used to present the task\'s ouput and reads its input.'), + additionalProperties: false, + properties: { + echo: { + type: 'boolean', + default: true, + description: nls.localize('JsonSchema.tasks.presentation.echo', 'Controls whether the executed command is echoed to the panel. Default is true.') + }, + focus: { + type: 'boolean', + default: false, + description: nls.localize('JsonSchema.tasks.presentation.focus', 'Controls whether the panel takes focus. Default is false. If set to true the panel is revealed as well.') + }, + reveal: { + type: 'string', + enum: ['always', 'silent', 'never'], + enumDescriptions: [ + nls.localize('JsonSchema.tasks.presentation.reveal.always', 'Always reveals the terminal when this task is executed.'), + nls.localize('JsonSchema.tasks.presentation.reveal.silent', 'Only reveals the terminal if no problem matcher is associated with the task and an errors occurs executing it.'), + nls.localize('JsonSchema.tasks.presentation.reveal.never', 'Never reveals the terminal when this task is executed.'), + ], + default: 'always', + description: nls.localize('JsonSchema.tasks.presentation.reveals', 'Controls whether the panel running the task is revealed or not. Default is \"always\".') + }, + panel: { + type: 'string', + enum: ['shared', 'dedicated', 'new'], + default: 'shared', + description: nls.localize('JsonSchema.tasks.presentation.instance', 'Controls if the panel is shared between tasks, dedicated to this task or a new one is created on every run.') + } + } +}; + +const terminal: IJSONSchema = Objects.deepClone(presentation); +terminal.deprecationMessage = nls.localize('JsonSchema.tasks.terminal', 'The terminal property is deprecated. Use presentation instead'); + +const group: IJSONSchema = { + oneOf: [ + { + type: 'string', + }, + { + type: 'object', + properties: { + kind: { + type: 'string', + default: 'none', + description: nls.localize('JsonSchema.tasks.group.kind', 'The task\'s execution group.') + }, + isDefault: { + type: 'boolean', + default: false, + description: nls.localize('JsonSchema.tasks.group.isDefault', 'Defines if this task is the default task in the group.') + } + } + }, + ], + enum: [ + { kind: 'build', isDefault: true }, + { kind: 'test', isDefault: true }, + 'build', + 'test', + 'none' + ], + enumDescriptions: [ + nls.localize('JsonSchema.tasks.group.defaultBuild', 'Marks the tasks as the default build task.'), + nls.localize('JsonSchema.tasks.group.defaultTest', 'Marks the tasks as the default test task.'), + nls.localize('JsonSchema.tasks.group.build', 'Marks the tasks as a build task accesible through the \'Run Build Task\' command.'), + nls.localize('JsonSchema.tasks.group.test', 'Marks the tasks as a test task accesible through the \'Run Test Task\' command.'), + nls.localize('JsonSchema.tasks.group.none', 'Assigns the task to no group') + ], + description: nls.localize('JsonSchema.tasks.group', 'Defines to which execution group this task belongs to. It supports "build" to add it to the build group and "test" to add it to the test group.') +}; + +const taskType: IJSONSchema = { + type: 'string', + enum: ['shell', 'process'], + default: 'process', + description: nls.localize('JsonSchema.tasks.type', 'Defines whether the task is run as a process or as a command inside a shell. Default is process.') +}; + +const version: IJSONSchema = { + type: 'string', + enum: ['2.0.0'], + description: nls.localize('JsonSchema.version', 'The config\'s version number.') +}; + +const identifier: IJSONSchema = { + type: 'string', + description: nls.localize('JsonSchema.tasks.identifier', 'A user defined identifier to reference the task in launch.json or a dependsOn clause.') +}; + +let taskConfiguration: IJSONSchema = { + type: 'object', + additionalProperties: false, + properties: { + label: { + type: 'string', + description: nls.localize('JsonSchema.tasks.taskLabel', "The task's label") + }, + taskName: { + type: 'string', + description: nls.localize('JsonSchema.tasks.taskName', 'The task\'s name'), + deprecationMessage: nls.localize('JsonSchema.tasks.taskName.deprecated', 'The task\'s name property is deprecated. Use the label property instead.') + }, + identifier: Objects.deepClone(identifier), + group: Objects.deepClone(group), + isBackground: { + type: 'boolean', + description: nls.localize('JsonSchema.tasks.background', 'Whether the executed task is kept alive and is running in the background.'), + default: true + }, + promptOnClose: { + type: 'boolean', + description: nls.localize('JsonSchema.tasks.promptOnClose', 'Whether the user is prompted when VS Code closes with a running task.'), + default: false + }, + presentation: Objects.deepClone(presentation), + problemMatcher: { + $ref: '#/definitions/problemMatcherType', + description: nls.localize('JsonSchema.tasks.matchers', 'The problem matcher(s) to use. Can either be a string or a problem matcher definition or an array of strings and problem matchers.') + } + } +}; + +let taskDefinitions: IJSONSchema[] = []; +TaskDefinitionRegistry.onReady().then(() => { + for (let taskType of TaskDefinitionRegistry.all()) { + let schema: IJSONSchema = Objects.deepClone(taskConfiguration); + // Since we do this after the schema is assigned we need to patch the refs. + schema.properties.type = { + type: 'string', + description: nls.localize('JsonSchema.customizations.customizes.type', 'The task type to customize'), + enum: [taskType.taskType] + }; + if (taskType.required) { + schema.required = taskType.required.slice(); + } + for (let key of Object.keys(taskType.properties)) { + let property = taskType.properties[key]; + schema.properties[key] = Objects.deepClone(property); + } + fixReferences(schema); + taskDefinitions.push(schema); + } +}); + +let customize = Objects.deepClone(taskConfiguration); +customize.properties.customize = { + type: 'string', + deprecationMessage: nls.localize('JsonSchema.tasks.customize.deprecated', 'The customize property is deprecated. See the 1.14 release notes on how to migrate to the new task customization approach') +}; +taskDefinitions.push(customize); + +let definitions = Objects.deepClone(commonSchema.definitions); +let taskDescription: IJSONSchema = definitions.taskDescription; +taskDescription.properties.isShellCommand = Objects.deepClone(shellCommand); +taskDescription.properties.dependsOn = dependsOn; +taskDescription.properties.identifier = Objects.deepClone(identifier); +taskDescription.properties.type = Objects.deepClone(taskType); +taskDescription.properties.presentation = Objects.deepClone(presentation); +taskDescription.properties.terminal = terminal; +taskDescription.properties.group = Objects.deepClone(group); +definitions.showOutputType.deprecationMessage = nls.localize( + 'JsonSchema.tasks.showOputput.deprecated', + 'The property showOutput is deprecated. Use the reveal property inside the presentation property instead. See also the 1.14 release notes.' +); +definitions.taskDescription.properties.echoCommand.deprecationMessage = nls.localize( + 'JsonSchema.tasks.echoCommand.deprecated', + 'The property echoCommand is deprecated. Use the echo property inside the presentation property instead. See also the 1.14 release notes.' +); +definitions.taskDescription.properties.suppressTaskName.deprecationMessage = nls.localize( + 'JsonSchema.tasks.suppressTaskName.deprecated', + 'The property suppressTaskName is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.' +); +definitions.taskDescription.properties.isBuildCommand.deprecationMessage = nls.localize( + 'JsonSchema.tasks.isBuildCommand.deprecated', + 'The property isBuildCommand is deprecated. Use the group property instead. See also the 1.14 release notes.' +); +definitions.taskDescription.properties.isTestCommand.deprecationMessage = nls.localize( + 'JsonSchema.tasks.isTestCommand.deprecated', + 'The property isTestCommand is deprecated. Use the group property instead. See also the 1.14 release notes.' +); + +taskDefinitions.push({ + $ref: '#/definitions/taskDescription' +} as IJSONSchema); + +let tasks = definitions.taskRunnerConfiguration.properties.tasks; +tasks.items = { + oneOf: taskDefinitions +}; + +definitions.commandConfiguration.properties.isShellCommand = Objects.deepClone(shellCommand); +definitions.options.properties.shell = { + $ref: '#/definitions/shellConfiguration' +}; + +definitions.taskRunnerConfiguration.properties.isShellCommand = Objects.deepClone(shellCommand); +definitions.taskRunnerConfiguration.properties.type = Objects.deepClone(taskType); +definitions.taskRunnerConfiguration.properties.group = Objects.deepClone(group); +definitions.taskRunnerConfiguration.properties.presentation = Objects.deepClone(presentation); +definitions.taskRunnerConfiguration.properties.suppressTaskName.deprecationMessage = nls.localize( + 'JsonSchema.tasks.suppressTaskName.deprecated', + 'The property suppressTaskName is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.' +); +definitions.taskRunnerConfiguration.properties.taskSelector.deprecationMessage = nls.localize( + 'JsonSchema.tasks.taskSelector.deprecated', + 'The property taskSelector is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.' +); + +let osSpecificTaskRunnerConfiguration = Objects.deepClone(definitions.taskRunnerConfiguration); +delete osSpecificTaskRunnerConfiguration.properties.tasks; +osSpecificTaskRunnerConfiguration.additionalProperties = false; +definitions.osSpecificTaskRunnerConfiguration = osSpecificTaskRunnerConfiguration; +definitions.taskRunnerConfiguration.properties.version = Objects.deepClone(version); + +const schema: IJSONSchema = { + oneOf: [ + { + 'allOf': [ + { + type: 'object', + required: ['version'], + properties: { + version: Objects.deepClone(version), + windows: { + '$ref': '#/definitions/osSpecificTaskRunnerConfiguration', + 'description': nls.localize('JsonSchema.windows', 'Windows specific command configuration') + }, + osx: { + '$ref': '#/definitions/osSpecificTaskRunnerConfiguration', + 'description': nls.localize('JsonSchema.mac', 'Mac specific command configuration') + }, + linux: { + '$ref': '#/definitions/osSpecificTaskRunnerConfiguration', + 'description': nls.localize('JsonSchema.linux', 'Linux specific command configuration') + } + } + }, + { + $ref: '#/definitions/taskRunnerConfiguration' + } + ] + } + ] +}; + +schema.definitions = definitions; + +Object.getOwnPropertyNames(definitions).forEach(key => { + let newKey = key + '2'; + definitions[newKey] = definitions[key]; + delete definitions[key]; +}); +fixReferences(schema); + +ProblemMatcherRegistry.onReady().then(() => { + try { + let matcherIds = ProblemMatcherRegistry.keys().map(key => '$' + key); + definitions.problemMatcherType2.oneOf[0].enum = matcherIds; + (definitions.problemMatcherType2.oneOf[2].items as IJSONSchema).anyOf[1].enum = matcherIds; + } catch (err) { + console.log('Installing problem matcher ids failed'); + } +}); + +export default schema; \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/media/configure-inverse.svg b/src/vs/workbench/parts/tasks/electron-browser/media/configure-inverse.svg new file mode 100644 index 0000000000..61baaea2b8 --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/media/configure-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/media/configure.svg b/src/vs/workbench/parts/tasks/electron-browser/media/configure.svg new file mode 100644 index 0000000000..3dec2ba50f --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/media/configure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/media/status-error.svg b/src/vs/workbench/parts/tasks/electron-browser/media/status-error.svg new file mode 100644 index 0000000000..61b16362cb --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/media/status-error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/media/status-info.svg b/src/vs/workbench/parts/tasks/electron-browser/media/status-info.svg new file mode 100644 index 0000000000..16306356ba --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/media/status-info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/media/status-warning.svg b/src/vs/workbench/parts/tasks/electron-browser/media/status-warning.svg new file mode 100644 index 0000000000..5951d69539 --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/media/status-warning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/media/task.contribution.css b/src/vs/workbench/parts/tasks/electron-browser/media/task.contribution.css new file mode 100644 index 0000000000..ae9ed3d6dd --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/media/task.contribution.css @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.task-statusbar-runningItem { + display: inline-block; +} + +.task-statusbar-runningItem-label { + display: inline-block; + cursor: pointer; + padding: 0 5px 0 5px; +} + +.task-statusbar-runningItem-label > .octicon { + font-size: 11px; + vertical-align: -webkit-baseline-middle; + height: 19px; +} + +.task-statusbar-item { + display: inline-block; +} + +.task-statusbar-item-icon { + background: url('task.svg') 50% 2px no-repeat; + background-size: 18px; + cursor: pointer; + height: 22px; + width: 24px; + vertical-align: top; +} + +.task-statusbar-item-progress { + width: 6px; + height: 18px; + padding: 0px 2px 0px 2px; + display: inline-block; + text-align: center; + vertical-align: top; +} + +.task-statusbar-item-label { + display: inline-block; + cursor: pointer; + padding: 0 5px 0 0; +} + +.task-statusbar-item-label > .task-statusbar-item-label-counter { + display: inline-block; + vertical-align: top; + padding-left: 2px; + padding-right: 2px; +} + +.task-statusbar-item-label > .task-statusbar-item-label-error, +.task-statusbar-item-label > .task-statusbar-item-label-warning, +.task-statusbar-item-label > .task-statusbar-item-label-info { + display: inline-block; + padding-right: 8px; + width: 8px; + height: 22px; +} + +.task-statusbar-item-label > .task-statusbar-item-label-error { + -webkit-mask: url('status-error.svg') no-repeat 50% 50%; + -webkit-mask-size: 11px; +} + +.task-statusbar-item-label > .task-statusbar-item-label-warning { + -webkit-mask: url('status-warning.svg') no-repeat 50% 50%; + -webkit-mask-size: 11px; +} + +.task-statusbar-item-label > .task-statusbar-item-label-info { + -webkit-mask: url('status-info.svg') no-repeat 50% 50%; + -webkit-mask-size: 11px; +} + +.monaco-workbench .quick-open-task-configure { + background-image: url('configure.svg'); +} + +.vs-dark .monaco-workbench .quick-open-task-configure, +.hc-black .monaco-workbench .quick-open-task-configure { + background-image: url('configure-inverse.svg'); +} diff --git a/src/vs/workbench/parts/tasks/electron-browser/media/task.svg b/src/vs/workbench/parts/tasks/electron-browser/media/task.svg new file mode 100644 index 0000000000..7692f495b3 --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/media/task.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts new file mode 100644 index 0000000000..0d1c1b035f --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -0,0 +1,2125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./media/task.contribution'; +import 'vs/workbench/parts/tasks/browser/taskQuickOpen'; + +import * as nls from 'vs/nls'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import Severity from 'vs/base/common/severity'; +import * as Objects from 'vs/base/common/objects'; +import URI from 'vs/base/common/uri'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { Action } from 'vs/base/common/actions'; +import * as Dom from 'vs/base/browser/dom'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import * as Builder from 'vs/base/browser/builder'; +import * as Types from 'vs/base/common/types'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { TerminateResponseCode } from 'vs/base/common/processes'; +import * as strings from 'vs/base/common/strings'; +import { ValidationStatus, ValidationState } from 'vs/base/common/parsers'; +import * as UUID from 'vs/base/common/uuid'; +import { LinkedMap, Touch } from 'vs/base/common/map'; +import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; + +import { Registry } from 'vs/platform/registry/common/platform'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { SyncActionDescriptor, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IEditor } from 'vs/platform/editor/common/editor'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IMarkerService, MarkerStatistics } from 'vs/platform/markers/common/markers'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ProblemMatcherRegistry, NamedProblemMatcher } from 'vs/platform/markers/common/problemMatcher'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IProgressService2, IProgressOptions, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IWindowService } from 'vs/platform/windows/common/windows'; + + +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModelService } from 'vs/editor/common/services/modelService'; + + +import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); +import { IJSONSchema } from 'vs/base/common/jsonSchema'; + +import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { IStatusbarItem, IStatusbarRegistry, Extensions as StatusbarExtensions, StatusbarItemDescriptor, StatusbarAlignment } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; + +import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import Constants from 'vs/workbench/parts/markers/common/constants'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { IConfigurationEditingService, ConfigurationTarget, IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IOutputService, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannel } from 'vs/workbench/parts/output/common/output'; +import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; + +import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; + +import { ITaskSystem, ITaskResolver, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskSystemEvents, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem'; +import { Task, CustomTask, ConfiguringTask, ContributedTask, CompositeTask, TaskSet, TaskGroup, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, TaskIdentifier } from 'vs/workbench/parts/tasks/common/tasks'; +import { ITaskService, TaskServiceEvents, ITaskProvider, TaskEvent, RunOptions, CustomizationProperties } from 'vs/workbench/parts/tasks/common/taskService'; +import { templates as taskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates'; + +import * as TaskConfig from '../node/taskConfiguration'; +import { ProcessTaskSystem } from 'vs/workbench/parts/tasks/node/processTaskSystem'; +import { TerminalTaskSystem } from './terminalTaskSystem'; +import { ProcessRunnerDetector } from 'vs/workbench/parts/tasks/node/processRunnerDetector'; +import { QuickOpenActionContributor } from '../browser/quickOpen'; + +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + +import { Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; + +let $ = Builder.$; +let tasksCategory = nls.localize('tasksCategory', "Tasks"); + +abstract class OpenTaskConfigurationAction extends Action { + + constructor(id: string, label: string, + private taskService: ITaskService, + private configurationService: IConfigurationService, + private editorService: IWorkbenchEditorService, private fileService: IFileService, + private contextService: IWorkspaceContextService, private outputService: IOutputService, + private messageService: IMessageService, private quickOpenService: IQuickOpenService, + private environmentService: IEnvironmentService, + private configurationResolverService: IConfigurationResolverService, + private extensionService: IExtensionService, + private telemetryService: ITelemetryService) { + + super(id, label); + } + + public run(event?: any): TPromise { + if (!this.contextService.hasWorkspace()) { + this.messageService.show(Severity.Info, nls.localize('ConfigureTaskRunnerAction.noWorkspace', 'Tasks are only available on a workspace folder.')); + return TPromise.as(undefined); + } + let sideBySide = !!(event && (event.ctrlKey || event.metaKey)); + let configFileCreated = false; + return this.fileService.resolveFile(this.contextService.toResource('.vscode/tasks.json')).then((success) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + + return success; + }, (err: any) => { + return this.quickOpenService.pick(taskTemplates, { placeHolder: nls.localize('ConfigureTaskRunnerAction.quickPick.template', 'Select a Task Runner') }).then(selection => { + if (!selection) { + return undefined; + } + let contentPromise: TPromise; + if (selection.autoDetect) { + const outputChannel = this.outputService.getChannel(TaskService.OutputChannelId); + outputChannel.show(true); + outputChannel.append(nls.localize('ConfigureTaskRunnerAction.autoDetecting', 'Auto detecting tasks for {0}', selection.id) + '\n'); + let detector = new ProcessRunnerDetector(this.fileService, this.contextService, this.configurationResolverService); + contentPromise = detector.detect(false, selection.id).then((value) => { + let config = value.config; + if (value.stderr && value.stderr.length > 0) { + value.stderr.forEach((line) => { + outputChannel.append(line + '\n'); + }); + if (config && (!config.tasks || config.tasks.length === 0)) { + this.messageService.show(Severity.Warning, nls.localize('ConfigureTaskRunnerAction.autoDetect', 'Auto detecting the task system failed. Using default template. Consult the task output for details.')); + return selection.content; + } else { + this.messageService.show(Severity.Warning, nls.localize('ConfigureTaskRunnerAction.autoDetectError', 'Auto detecting the task system produced errors. Consult the task output for details.')); + } + } + if (config) { + if (value.stdout && value.stdout.length > 0) { + value.stdout.forEach(line => outputChannel.append(line + '\n')); + } + let content = JSON.stringify(config, null, '\t'); + content = [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + ].join('\n') + content.substr(1); + return content; + } else { + return selection.content; + } + }); + } else { + contentPromise = TPromise.as(selection.content); + } + return contentPromise.then(content => { + let editorConfig = this.configurationService.getConfiguration(); + if (editorConfig.editor.insertSpaces) { + content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); + } + configFileCreated = true; + return this.fileService.createFile(this.contextService.toResource('.vscode/tasks.json'), content).then((result) => { + this.telemetryService.publicLog(TaskService.TemplateTelemetryEventName, { + templateId: selection.id, + autoDetect: selection.autoDetect + }); + return result; + }); // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + }); + /* 2.0 version + let content = selection.content; + let editorConfig = this.configurationService.getConfiguration(); + if (editorConfig.editor.insertSpaces) { + content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); + } + configFileCreated = true; + return this.fileService.createFile(this.contextService.toResource('.vscode/tasks.json'), content); + */ + }); + }).then((stat) => { + if (!stat) { + return undefined; + } + // // (2) Open editor with configuration file + return this.editorService.openEditor({ + resource: stat.resource, + options: { + forceOpen: true, + pinned: configFileCreated // pin only if config file is created #8727 + } + }, sideBySide); + }, (error) => { + throw new Error(nls.localize('ConfigureTaskRunnerAction.failed', "Unable to create the 'tasks.json' file inside the '.vscode' folder. Consult the task output for details.")); + }); + } +} + +class ConfigureTaskRunnerAction extends OpenTaskConfigurationAction { + public static ID = 'workbench.action.tasks.configureTaskRunner'; + public static TEXT = nls.localize('ConfigureTaskRunnerAction.label', "Configure Task Runner"); + + constructor(id: string, label: string, + @ITaskService taskService, @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, + @IWorkspaceContextService contextService: IWorkspaceContextService, @IOutputService outputService: IOutputService, + @IMessageService messageService: IMessageService, @IQuickOpenService quickOpenService: IQuickOpenService, + @IEnvironmentService environmentService: IEnvironmentService, + @IConfigurationResolverService configurationResolverService: IConfigurationResolverService, + @IExtensionService extensionService, + @ITelemetryService telemetryService) { + super(id, label, taskService, configurationService, editorService, fileService, contextService, + outputService, messageService, quickOpenService, environmentService, configurationResolverService, + extensionService, telemetryService); + } +} + +class ConfigureBuildTaskAction extends OpenTaskConfigurationAction { + public static ID = 'workbench.action.tasks.configureBuildTask'; + public static TEXT = nls.localize('ConfigureBuildTaskAction.label', "Configure Build Task"); + + constructor(id: string, label: string, + @ITaskService taskService, @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, + @IWorkspaceContextService contextService: IWorkspaceContextService, @IOutputService outputService: IOutputService, + @IMessageService messageService: IMessageService, @IQuickOpenService quickOpenService: IQuickOpenService, + @IEnvironmentService environmentService: IEnvironmentService, + @IConfigurationResolverService configurationResolverService: IConfigurationResolverService, + @IExtensionService extensionService, + @ITelemetryService telemetryService) { + super(id, label, taskService, configurationService, editorService, fileService, contextService, + outputService, messageService, quickOpenService, environmentService, configurationResolverService, + extensionService, telemetryService); + } +} + +class CloseMessageAction extends Action { + + public static ID = 'workbench.action.build.closeMessage'; + public static TEXT = nls.localize('CloseMessageAction.label', 'Close'); + + public closeFunction: () => void; + + constructor() { + super(CloseMessageAction.ID, CloseMessageAction.TEXT); + } + public run(): TPromise { + if (this.closeFunction) { + this.closeFunction(); + } + return TPromise.as(undefined); + } +} + +class ViewTerminalAction extends Action { + + public static ID = 'workbench.action.build.viewTerminal'; + public static TEXT = nls.localize('ShowTerminalAction.label', 'View Terminal'); + + constructor( @ITerminalService private terminalService: ITerminalService) { + super(ViewTerminalAction.ID, ViewTerminalAction.TEXT); + } + + public run(): TPromise { + this.terminalService.showPanel(); + return TPromise.as(undefined); + } +} + +class BuildStatusBarItem extends Themable implements IStatusbarItem { + private intervalToken: any; + private activeCount: number; + private static progressChars: string = '|/-\\'; + private icons: HTMLElement[]; + + constructor( + @IPanelService private panelService: IPanelService, + @IMarkerService private markerService: IMarkerService, + @IOutputService private outputService: IOutputService, + @ITaskService private taskService: ITaskService, + @IPartService private partService: IPartService, + @IThemeService themeService: IThemeService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { + super(themeService); + + this.activeCount = 0; + this.icons = []; + + this.registerListeners(); + } + + private registerListeners(): void { + this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.updateStyles())); + } + + protected updateStyles(): void { + super.updateStyles(); + + this.icons.forEach(icon => { + icon.style.backgroundColor = this.getColor(this.contextService.hasWorkspace() ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND); + }); + } + + public render(container: HTMLElement): IDisposable { + let callOnDispose: IDisposable[] = []; + + const element = document.createElement('div'); + const progress = document.createElement('div'); + const label = document.createElement('a'); + const errorIcon = document.createElement('div'); + const warningIcon = document.createElement('div'); + const infoIcon = document.createElement('div'); + const error = document.createElement('div'); + const warning = document.createElement('div'); + const info = document.createElement('div'); + + Dom.addClass(element, 'task-statusbar-item'); + + Dom.addClass(progress, 'task-statusbar-item-progress'); + element.appendChild(progress); + progress.innerHTML = BuildStatusBarItem.progressChars[0]; + $(progress).hide(); + + Dom.addClass(label, 'task-statusbar-item-label'); + element.appendChild(label); + element.title = nls.localize('problems', "Problems"); + + Dom.addClass(errorIcon, 'task-statusbar-item-label-error'); + Dom.addClass(errorIcon, 'mask-icon'); + label.appendChild(errorIcon); + this.icons.push(errorIcon); + + Dom.addClass(error, 'task-statusbar-item-label-counter'); + error.innerHTML = '0'; + label.appendChild(error); + + Dom.addClass(warningIcon, 'task-statusbar-item-label-warning'); + Dom.addClass(warningIcon, 'mask-icon'); + label.appendChild(warningIcon); + this.icons.push(warningIcon); + + Dom.addClass(warning, 'task-statusbar-item-label-counter'); + warning.innerHTML = '0'; + label.appendChild(warning); + + Dom.addClass(infoIcon, 'task-statusbar-item-label-info'); + Dom.addClass(infoIcon, 'mask-icon'); + label.appendChild(infoIcon); + this.icons.push(infoIcon); + $(infoIcon).hide(); + + Dom.addClass(info, 'task-statusbar-item-label-counter'); + label.appendChild(info); + $(info).hide(); + + callOnDispose.push(Dom.addDisposableListener(label, 'click', (e: MouseEvent) => { + const panel = this.panelService.getActivePanel(); + if (panel && panel.getId() === Constants.MARKERS_PANEL_ID) { + this.partService.setPanelHidden(true); + } else { + this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true); + } + })); + + let updateStatus = (element: HTMLDivElement, icon: HTMLDivElement, stats: number): boolean => { + if (stats > 0) { + element.innerHTML = stats.toString(); + $(element).show(); + $(icon).show(); + return true; + } else { + $(element).hide(); + $(icon).hide(); + return false; + } + }; + + let manyMarkers = nls.localize('manyMarkers', "99+"); + let updateLabel = (stats: MarkerStatistics) => { + error.innerHTML = stats.errors < 100 ? stats.errors.toString() : manyMarkers; + warning.innerHTML = stats.warnings < 100 ? stats.warnings.toString() : manyMarkers; + updateStatus(info, infoIcon, stats.infos); + }; + + this.markerService.onMarkerChanged((changedResources) => { + updateLabel(this.markerService.getStatistics()); + }); + + callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Active, (event: TaskEvent) => { + if (this.ignoreEvent(event)) { + return; + } + this.activeCount++; + if (this.activeCount === 1) { + let index = 1; + let chars = BuildStatusBarItem.progressChars; + progress.innerHTML = chars[0]; + this.intervalToken = setInterval(() => { + progress.innerHTML = chars[index]; + index++; + if (index >= chars.length) { + index = 0; + } + }, 50); + $(progress).show(); + } + })); + + callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Inactive, (event: TaskEvent) => { + if (this.ignoreEvent(event)) { + return; + } + // Since the exiting of the sub process is communicated async we can't order inactive and terminate events. + // So try to treat them accordingly. + if (this.activeCount > 0) { + this.activeCount--; + if (this.activeCount === 0) { + $(progress).hide(); + if (this.intervalToken) { + clearInterval(this.intervalToken); + this.intervalToken = null; + } + } + } + })); + + callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Terminated, (event: TaskEvent) => { + if (this.ignoreEvent(event)) { + return; + } + if (this.activeCount !== 0) { + $(progress).hide(); + if (this.intervalToken) { + clearInterval(this.intervalToken); + this.intervalToken = null; + } + this.activeCount = 0; + } + })); + + container.appendChild(element); + + this.updateStyles(); + + return { + dispose: () => { + callOnDispose = dispose(callOnDispose); + } + }; + } + + private ignoreEvent(event: TaskEvent): boolean { + if (!this.taskService.inTerminal()) { + return false; + } + if (event.group !== TaskGroup.Build) { + return true; + } + if (!event.__task) { + return false; + } + return event.__task.problemMatchers === void 0 || event.__task.problemMatchers.length === 0; + } +} + +class TaskStatusBarItem extends Themable implements IStatusbarItem { + + constructor( + @IPanelService private panelService: IPanelService, + @IMarkerService private markerService: IMarkerService, + @IOutputService private outputService: IOutputService, + @ITaskService private taskService: ITaskService, + @IPartService private partService: IPartService, + @IThemeService themeService: IThemeService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + ) { + super(themeService); + } + + protected updateStyles(): void { + super.updateStyles(); + } + + public render(container: HTMLElement): IDisposable { + + let callOnDispose: IDisposable[] = []; + const element = document.createElement('a'); + Dom.addClass(element, 'task-statusbar-runningItem'); + + let labelElement = document.createElement('div'); + Dom.addClass(labelElement, 'task-statusbar-runningItem-label'); + element.appendChild(labelElement); + + let label = new OcticonLabel(labelElement); + label.title = nls.localize('runningTasks', "Show Running Tasks"); + + $(element).hide(); + + callOnDispose.push(Dom.addDisposableListener(labelElement, 'click', (e: MouseEvent) => { + (this.taskService as TaskService).runShowTasks(); + })); + + let updateStatus = (): void => { + this.taskService.getActiveTasks().then(tasks => { + if (tasks.length === 0) { + $(element).hide(); + } else { + label.text = `$(tools) ${tasks.length}`; + $(element).show(); + } + }); + }; + + callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Changed, (event: TaskEvent) => { + updateStatus(); + })); + + container.appendChild(element); + + this.updateStyles(); + updateStatus(); + + return { + dispose: () => { + callOnDispose = dispose(callOnDispose); + } + }; + } +} + +interface TaskServiceEventData { + error?: any; +} + +class NullTaskSystem extends EventEmitter implements ITaskSystem { + public run(task: Task): ITaskExecuteResult { + return { + kind: TaskExecuteKind.Started, + promise: TPromise.as({}) + }; + } + public revealTask(task: Task): boolean { + return false; + } + public isActive(): TPromise { + return TPromise.as(false); + } + public isActiveSync(): boolean { + return false; + } + public getActiveTasks(): Task[] { + return []; + } + public canAutoTerminate(): boolean { + return true; + } + public terminate(task: string | Task): TPromise { + return TPromise.as({ success: true, task: undefined }); + } + public terminateAll(): TPromise { + return TPromise.as([]); + } +} + +class ProblemReporter implements TaskConfig.IProblemReporter { + + private _validationStatus: ValidationStatus; + + constructor(private _outputChannel: IOutputChannel) { + this._validationStatus = new ValidationStatus(); + } + + public info(message: string): void { + this._validationStatus.state = ValidationState.Info; + this._outputChannel.append(message + '\n'); + } + + public warn(message: string): void { + this._validationStatus.state = ValidationState.Warning; + this._outputChannel.append(message + '\n'); + } + + public error(message: string): void { + this._validationStatus.state = ValidationState.Error; + this._outputChannel.append(message + '\n'); + } + + public fatal(message: string): void { + this._validationStatus.state = ValidationState.Fatal; + this._outputChannel.append(message + '\n'); + } + + public get status(): ValidationStatus { + return this._validationStatus; + } + + public clearOutput(): void { + this._outputChannel.clear(); + } +} + +interface WorkspaceTaskResult { + set: TaskSet; + configurations: { + byIdentifier: IStringDictionary; + }; + hasErrors: boolean; +} + +interface WorkspaceConfigurationResult { + config: TaskConfig.ExternalTaskRunnerConfiguration; + hasErrors: boolean; +} + +interface TaskCustomizationTelementryEvent { + properties: string[]; +} + +class TaskService extends EventEmitter implements ITaskService { + + // private static autoDetectTelemetryName: string = 'taskServer.autoDetect'; + private static RecentlyUsedTasks_Key = 'workbench.tasks.recentlyUsedTasks'; + private static RanTaskBefore_Key = 'workbench.tasks.ranTaskBefore'; + + private static CustomizationTelemetryEventName: string = 'taskService.customize'; + public static TemplateTelemetryEventName: string = 'taskService.template'; + + public _serviceBrand: any; + public static SERVICE_ID: string = 'taskService'; + public static OutputChannelId: string = 'tasks'; + public static OutputChannelLabel: string = nls.localize('tasks', "Tasks"); + + private modeService: IModeService; + private configurationService: IConfigurationService; + private configurationEditingService: IConfigurationEditingService; + private markerService: IMarkerService; + private outputService: IOutputService; + private messageService: IMessageService; + private fileService: IFileService; + private telemetryService: ITelemetryService; + private editorService: IWorkbenchEditorService; + private contextService: IWorkspaceContextService; + private textFileService: ITextFileService; + private modelService: IModelService; + private extensionService: IExtensionService; + private quickOpenService: IQuickOpenService; + + private _configHasErrors: boolean; + private _providers: Map; + + private _workspaceTasksPromise: TPromise; + + private _taskSystem: ITaskSystem; + private _taskSystemListeners: IDisposable[]; + private _recentlyUsedTasks: LinkedMap; + + private _outputChannel: IOutputChannel; + + constructor( @IModeService modeService: IModeService, @IConfigurationService configurationService: IConfigurationService, + @IConfigurationEditingService configurationEditingService: IConfigurationEditingService, + @IMarkerService markerService: IMarkerService, @IOutputService outputService: IOutputService, + @IMessageService messageService: IMessageService, @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IFileService fileService: IFileService, @IWorkspaceContextService contextService: IWorkspaceContextService, + @ITelemetryService telemetryService: ITelemetryService, @ITextFileService textFileService: ITextFileService, + @ILifecycleService lifecycleService: ILifecycleService, + @IModelService modelService: IModelService, @IExtensionService extensionService: IExtensionService, + @IQuickOpenService quickOpenService: IQuickOpenService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IConfigurationResolverService private configurationResolverService: IConfigurationResolverService, + @ITerminalService private terminalService: ITerminalService, + @IWorkbenchEditorService private workbenchEditorService: IWorkbenchEditorService, + @IStorageService private storageService: IStorageService, + @IProgressService2 private progressService: IProgressService2, + @IOpenerService private openerService: IOpenerService, + @IWindowService private _windowServive: IWindowService + ) { + + super(); + this.modeService = modeService; + this.configurationService = configurationService; + this.configurationEditingService = configurationEditingService; + this.markerService = markerService; + this.outputService = outputService; + this.messageService = messageService; + this.editorService = editorService; + this.fileService = fileService; + this.contextService = contextService; + this.telemetryService = telemetryService; + this.textFileService = textFileService; + this.modelService = modelService; + this.extensionService = extensionService; + this.quickOpenService = quickOpenService; + + this._configHasErrors = false; + this._workspaceTasksPromise = undefined; + this._taskSystemListeners = []; + this._outputChannel = this.outputService.getChannel(TaskService.OutputChannelId); + this._providers = new Map(); + this.configurationService.onDidUpdateConfiguration(() => { + if (!this._taskSystem && !this._workspaceTasksPromise) { + return; + } + this.updateWorkspaceTasks(); + if (!this._taskSystem) { + return; + } + let currentExecutionEngine = this._taskSystem instanceof TerminalTaskSystem + ? ExecutionEngine.Terminal + : this._taskSystem instanceof ProcessTaskSystem + ? ExecutionEngine.Process + : ExecutionEngine._default; + if (currentExecutionEngine !== this.getExecutionEngine()) { + this.messageService.show( + Severity.Info, + { + message: nls.localize( + 'TaskSystem.noHotSwap', + 'Changing the task execution engine requires to reload the Window' + ), + actions: [ + new ReloadWindowAction(ReloadWindowAction.ID, ReloadWindowAction.LABEL, this._windowServive), + new CloseMessageAction() + ] + } + ); + } + }); + lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown())); + this.registerCommands(); + } + + private registerCommands(): void { + CommandsRegistry.registerCommand('workbench.action.tasks.runTask', (accessor, arg) => { + this.runTaskCommand(accessor, arg); + }); + + CommandsRegistry.registerCommand('workbench.action.tasks.restartTask', (accessor, arg) => { + this.runRestartTaskCommand(accessor, arg); + }); + + CommandsRegistry.registerCommand('workbench.action.tasks.terminate', (accessor, arg) => { + this.runTerminateCommand(); + }); + + CommandsRegistry.registerCommand('workbench.action.tasks.showLog', () => { + if (!this.canRunCommand()) { + return; + } + this.showOutput(); + }); + + CommandsRegistry.registerCommand('workbench.action.tasks.build', () => { + if (!this.canRunCommand()) { + return; + } + this.runBuildCommand(); + }); + + KeybindingsRegistry.registerKeybindingRule({ + id: 'workbench.action.tasks.build', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: undefined, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_B + }); + + CommandsRegistry.registerCommand('workbench.action.tasks.test', () => { + if (!this.canRunCommand()) { + return; + } + this.runTestCommand(); + }); + + CommandsRegistry.registerCommand('workbench.action.tasks.configureDefaultBuildTask', () => { + this.runConfigureDefaultBuildTask(); + }); + + CommandsRegistry.registerCommand('workbench.action.tasks.configureDefaultTestTask', () => { + this.runConfigureDefaultTestTask(); + }); + + CommandsRegistry.registerCommand('workbench.action.tasks.showTasks', () => { + this.runShowTasks(); + }); + } + + private showOutput(): void { + this._outputChannel.show(true); + } + + private disposeTaskSystemListeners(): void { + this._taskSystemListeners = dispose(this._taskSystemListeners); + } + + public registerTaskProvider(handle: number, provider: ITaskProvider): void { + if (!provider) { + return; + } + this._providers.set(handle, provider); + } + + public unregisterTaskProvider(handle: number): boolean { + return this._providers.delete(handle); + } + + public getTask(identifier: string): TPromise { + return this.getTaskSets().then((sets) => { + let resolver = this.createResolver(sets); + return resolver.resolve(identifier); + }); + } + + public tasks(): TPromise { + return this.getTaskSets().then((sets) => { + let result: Task[] = []; + for (let set of sets) { + result.push(...set.tasks); + } + return result; + }); + }; + + public isActive(): TPromise { + if (!this._taskSystem) { + return TPromise.as(false); + } + return this._taskSystem.isActive(); + } + + public getActiveTasks(): TPromise { + if (!this._taskSystem) { + return TPromise.as([]); + } + return TPromise.as(this._taskSystem.getActiveTasks()); + } + + public getRecentlyUsedTasks(): LinkedMap { + if (this._recentlyUsedTasks) { + return this._recentlyUsedTasks; + } + this._recentlyUsedTasks = new LinkedMap(); + let storageValue = this.storageService.get(TaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE); + if (storageValue) { + try { + let values: string[] = JSON.parse(storageValue); + if (Array.isArray(values)) { + for (let value of values) { + this._recentlyUsedTasks.set(value, value); + } + } + } catch (error) { + // Ignore. We use the empty result + } + } + return this._recentlyUsedTasks; + } + + private saveRecentlyUsedTasks(): void { + if (!this._recentlyUsedTasks) { + return; + } + let values = this._recentlyUsedTasks.values(); + if (values.length > 30) { + values = values.slice(0, 30); + } + this.storageService.store(TaskService.RecentlyUsedTasks_Key, JSON.stringify(values), StorageScope.WORKSPACE); + } + + private openDocumentation(): void { + this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?LinkId=733558')); + } + + public build(): TPromise { + return this.getTaskSets().then((values) => { + let runnable = this.createRunnableTask(values, TaskGroup.Build); + if (!runnable || !runnable.task) { + if (this.getJsonSchemaVersion() === JsonSchemaVersion.V0_1_0) { + throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask1', 'No build task defined. Mark a task with \'isBuildCommand\' in the tasks.json file.'), TaskErrors.NoBuildTask); + } else { + throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask2', 'No build task defined. Mark a task with as a \'build\' group in the tasks.json file.'), TaskErrors.NoBuildTask); + } + } + return this.executeTask(runnable.task, runnable.resolver); + }).then(value => value, (error) => { + this.handleError(error); + return TPromise.wrapError(error); + }); + } + + public rebuild(): TPromise { + return TPromise.wrapError(new Error('Not implemented')); + } + + public clean(): TPromise { + return TPromise.wrapError(new Error('Not implemented')); + } + + public runTest(): TPromise { + return this.getTaskSets().then((values) => { + let runnable = this.createRunnableTask(values, TaskGroup.Test); + if (!runnable || !runnable.task) { + if (this.getJsonSchemaVersion() === JsonSchemaVersion.V0_1_0) { + throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask1', 'No test task defined. Mark a task with \'isTestCommand\' in the tasks.json file.'), TaskErrors.NoTestTask); + } else { + throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask2', 'No test task defined. Mark a task with as a \'test\' group in the tasks.json file.'), TaskErrors.NoTestTask); + } + } + return this.executeTask(runnable.task, runnable.resolver); + }).then(value => value, (error) => { + this.handleError(error); + return TPromise.wrapError(error); + }); + } + + public run(task: string | Task, options?: RunOptions): TPromise { + return this.getTaskSets().then((values) => { + let resolver = this.createResolver(values); + let requested: string; + let toExecute: Task; + if (Types.isString(task)) { + requested = task; + toExecute = resolver.resolve(task); + } else { + requested = task.name; + toExecute = task; + } + if (!toExecute) { + throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Requested task {0} to execute not found.', requested), TaskErrors.TaskNotFound); + } else { + if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(toExecute) && !CompositeTask.is(toExecute)) { + return this.attachProblemMatcher(toExecute).then((toExecute) => { + if (toExecute) { + return this.executeTask(toExecute, resolver); + } else { + return TPromise.as(undefined); + } + }); + } + return this.executeTask(toExecute, resolver); + } + }).then(value => value, (error) => { + this.handleError(error); + return TPromise.wrapError(error); + }); + } + + private shouldAttachProblemMatcher(task: Task): boolean { + if (!this.canCustomize()) { + return false; + } + if (task.group !== void 0 && task.group !== TaskGroup.Build) { + return false; + } + if (task.problemMatchers !== void 0 && task.problemMatchers.length > 0) { + return false; + } + if (ContributedTask.is(task)) { + return !task.hasDefinedMatchers && task.problemMatchers.length === 0; + } + if (CustomTask.is(task)) { + let configProperties: TaskConfig.ConfigurationProperties = task._source.config.element; + return configProperties.problemMatcher === void 0; + } + return false; + } + + private attachProblemMatcher(task: ContributedTask | CustomTask): TPromise { + interface ProblemMatcherPickEntry extends IPickOpenEntry { + matcher: NamedProblemMatcher; + never?: boolean; + learnMore?: boolean; + } + let entries: ProblemMatcherPickEntry[] = []; + for (let key of ProblemMatcherRegistry.keys()) { + let matcher = ProblemMatcherRegistry.get(key); + if (matcher.deprecated) { + continue; + } + if (matcher.name === matcher.label) { + entries.push({ label: matcher.name, matcher: matcher }); + } else { + entries.push({ + label: matcher.label, + description: `$${matcher.name}`, + matcher: matcher + }); + } + } + if (entries.length > 0) { + entries = entries.sort((a, b) => a.label.localeCompare(b.label)); + entries[0].separator = { border: true }; + entries.unshift( + { label: nls.localize('TaskService.attachProblemMatcher.continueWithout', 'Continue without scanning the task output'), matcher: undefined }, + { label: nls.localize('TaskService.attachProblemMatcher.never', 'Never scan the task output'), matcher: undefined, never: true }, + { label: nls.localize('TaskService.attachProblemMatcher.learnMoreAbout', 'Learn more about scanning the task output'), matcher: undefined, learnMore: true } + ); + return this.quickOpenService.pick(entries, { + placeHolder: nls.localize('selectProblemMatcher', 'Select for which kind of errors and warnings to scan the task output'), + autoFocus: { autoFocusFirstEntry: true } + }).then((selected) => { + if (selected) { + if (selected.learnMore) { + this.openDocumentation(); + return undefined; + } else if (selected.never) { + this.customize(task, { problemMatcher: [] }, true); + return task; + } else if (selected.matcher) { + let newTask = Objects.deepClone(task); + let matcherReference = `$${selected.matcher.name}`; + newTask.problemMatchers = [matcherReference]; + this.customize(task, { problemMatcher: [matcherReference] }, true); + return newTask; + } else { + return task; + } + } else { + return undefined; + } + }); + } + return TPromise.as(task); + } + + public getTasksForGroup(group: string): TPromise { + return this.getTaskSets().then((values) => { + let result: Task[] = []; + for (let value of values) { + for (let task of value.tasks) { + if (task.group === group) { + result.push(task); + } + } + } + return result; + }); + } + + public canCustomize(): boolean { + return this.getJsonSchemaVersion() === JsonSchemaVersion.V2_0_0; + } + + public customize(task: ContributedTask | CustomTask, properties?: CustomizationProperties, openConfig?: boolean): TPromise { + let configuration = this.getConfiguration(); + if (configuration.hasParseErrors) { + this.messageService.show(Severity.Warning, nls.localize('customizeParseErrors', 'The current task configuration has errors. Please fix the errors first before customizing a task.')); + return TPromise.as(undefined); + } + + let fileConfig = configuration.config; + let index: number; + let toCustomize: TaskConfig.CustomTask | TaskConfig.ConfiguringTask; + let taskConfig = CustomTask.is(task) ? task._source.config : undefined; + if (taskConfig && taskConfig.element) { + index = taskConfig.index; + toCustomize = taskConfig.element; + } else if (ContributedTask.is(task)) { + toCustomize = { + }; + let identifier: TaskConfig.TaskIdentifier = Objects.assign(Object.create(null), task.defines); + delete identifier['_key']; + Object.keys(identifier).forEach(key => toCustomize[key] = identifier[key]); + if (task.problemMatchers && task.problemMatchers.length > 0 && Types.isStringArray(task.problemMatchers)) { + toCustomize.problemMatcher = task.problemMatchers; + } + } + if (!toCustomize) { + return TPromise.as(undefined); + } + if (properties) { + for (let property of Object.getOwnPropertyNames(properties)) { + let value = properties[property]; + if (value !== void 0 && value !== null) { + toCustomize[property] = value; + } + } + } else { + if (toCustomize.problemMatcher === void 0 && task.problemMatchers === void 0 || task.problemMatchers.length === 0) { + toCustomize.problemMatcher = []; + } + } + + let promise: TPromise; + if (!fileConfig) { + let value = { + version: '2.0.0', + tasks: [toCustomize] + }; + let content = [ + '{', + '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', + '\t// for the documentation about the tasks.json format', + ].join('\n') + JSON.stringify(value, null, '\t').substr(1); + let editorConfig = this.configurationService.getConfiguration(); + if (editorConfig.editor.insertSpaces) { + content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); + } + promise = this.fileService.createFile(this.contextService.toResource('.vscode/tasks.json'), content).then(() => { }); // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + } else { + let value: IConfigurationValue = { key: undefined, value: undefined }; + // We have a global task configuration + if (index === -1) { + if (properties.problemMatcher !== void 0) { + fileConfig.problemMatcher = properties.problemMatcher; + value.key = 'tasks.problemMatchers'; + value.value = fileConfig.problemMatcher; + promise = this.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, value); + } else if (properties.group !== void 0) { + fileConfig.group = properties.group; + value.key = 'tasks.group'; + value.value = fileConfig.group; + promise = this.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, value); + } + } else { + if (!Array.isArray(fileConfig.tasks)) { + fileConfig.tasks = []; + } + value.key = 'tasks.tasks'; + value.value = fileConfig.tasks; + if (index === void 0) { + fileConfig.tasks.push(toCustomize); + } else { + fileConfig.tasks[index] = toCustomize; + } + promise = this.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, value); + } + }; + if (!promise) { + return TPromise.as(undefined); + } + return promise.then(() => { + let event: TaskCustomizationTelementryEvent = { + properties: properties ? Object.getOwnPropertyNames(properties) : [] + }; + this.telemetryService.publicLog(TaskService.CustomizationTelemetryEventName, event); + if (openConfig) { + let resource = this.contextService.toResource('.vscode/tasks.json'); // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + this.editorService.openEditor({ + resource: resource, + options: { + forceOpen: true, + pinned: false + } + }, false); + } + }); + } + + public openConfig(task: CustomTask): TPromise { + let resource = this.contextService.toResource(task._source.config.file); + return this.editorService.openEditor({ + resource: resource, + options: { + forceOpen: true, + pinned: false + } + }, false).then(() => undefined); + } + + private createRunnableTask(sets: TaskSet[], group: TaskGroup): { task: Task; resolver: ITaskResolver } { + let idMap: IStringDictionary = Object.create(null); + let labelMap: IStringDictionary = Object.create(null); + let identifierMap: IStringDictionary = Object.create(null); + + let workspaceTasks: Task[] = []; + let extensionTasks: Task[] = []; + sets.forEach((set) => { + set.tasks.forEach((task) => { + idMap[task._id] = task; + labelMap[task._label] = task; + identifierMap[task.identifier] = task; + if (group && task.group === group) { + if (task._source.kind === TaskSourceKind.Workspace) { + workspaceTasks.push(task); + } else { + extensionTasks.push(task); + } + } + }); + }); + let resolver: ITaskResolver = { + resolve: (id: string) => { + return idMap[id] || labelMap[id] || identifierMap[id]; + } + }; + if (workspaceTasks.length > 0) { + if (workspaceTasks.length > 1) { + this._outputChannel.append(nls.localize('moreThanOneBuildTask', 'There are many build tasks defined in the tasks.json. Executing the first one.\n')); + } + return { task: workspaceTasks[0], resolver }; + } + if (extensionTasks.length === 0) { + return undefined; + } + + // We can only have extension tasks if we are in version 2.0.0. Then we can even run + // multiple build tasks. + if (extensionTasks.length === 1) { + return { task: extensionTasks[0], resolver }; + } else { + let id: string = UUID.generateUuid(); + let task: CompositeTask = { + _id: id, + _source: { kind: TaskSourceKind.Composite, label: 'composite' }, + _label: id, + type: 'composite', + name: id, + identifier: id, + dependsOn: extensionTasks.map(task => task._id) + }; + return { task, resolver }; + } + } + + private createResolver(sets: TaskSet[]): ITaskResolver { + let labelMap: IStringDictionary = Object.create(null); + let identifierMap: IStringDictionary = Object.create(null); + + sets.forEach((set) => { + set.tasks.forEach((task) => { + labelMap[task._label] = task; + identifierMap[task.identifier] = task; + }); + }); + return { + resolve: (id: string) => { + return labelMap[id] || identifierMap[id]; + } + }; + } + + private executeTask(task: Task, resolver: ITaskResolver): TPromise { + if (!this.storageService.get(TaskService.RanTaskBefore_Key, StorageScope.GLOBAL)) { + this.storageService.store(TaskService.RanTaskBefore_Key, true, StorageScope.GLOBAL); + } + return ProblemMatcherRegistry.onReady().then(() => { + return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved + let executeResult = this.getTaskSystem().run(task, resolver); + this.getRecentlyUsedTasks().set(Task.getKey(task), Task.getKey(task), Touch.First); + if (executeResult.kind === TaskExecuteKind.Active) { + let active = executeResult.active; + if (active.same) { + if (active.background) { + this.messageService.show(Severity.Info, nls.localize('TaskSystem.activeSame.background', 'The task \'{0}\' is already active and in background mode. To terminate it use `Terminate Task...` from the Tasks menu.', task._label)); + } else { + this.messageService.show(Severity.Info, nls.localize('TaskSystem.activeSame.noBackground', 'The task \'{0}\' is already active. To terminate it use `Terminate Task...` from the Tasks menu.', task._label)); + } + } else { + throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is already a task running. Terminate it first before executing another task.'), TaskErrors.RunningTask); + } + } + return executeResult.promise; + }); + }); + } + + public restart(task: string | Task): void { + if (!this._taskSystem) { + return; + } + const id: string = Types.isString(task) ? task : task._id; + this._taskSystem.terminate(id).then((response) => { + if (response.success) { + this.emit(TaskServiceEvents.Terminated, {}); + this.run(task); + } else { + this.messageService.show(Severity.Warning, nls.localize('TaskSystem.restartFailed', 'Failed to terminate and restart task {0}', Types.isString(task) ? task : task.name)); + } + return response; + }); + } + + public terminate(task: string | Task): TPromise { + if (!this._taskSystem) { + return TPromise.as({ success: true, task: undefined }); + } + const id: string = Types.isString(task) ? task : task._id; + return this._taskSystem.terminate(id); + } + + public terminateAll(): TPromise { + if (!this._taskSystem) { + return TPromise.as([]); + } + return this._taskSystem.terminateAll(); + } + + private getTaskSystem(): ITaskSystem { + if (this._taskSystem) { + return this._taskSystem; + } + let engine = this.getExecutionEngine(); + if (engine === ExecutionEngine.Terminal) { + this._taskSystem = new TerminalTaskSystem( + this.terminalService, this.outputService, this.markerService, + this.modelService, this.configurationResolverService, this.telemetryService, + this.workbenchEditorService, this.contextService, + TaskService.OutputChannelId + ); + } else { + let system = new ProcessTaskSystem( + this.markerService, this.modelService, this.telemetryService, this.outputService, + this.configurationResolverService, this.contextService, TaskService.OutputChannelId, + ); + system.hasErrors(this._configHasErrors); + this._taskSystem = system; + } + this._taskSystemListeners.push(this._taskSystem.addListener(TaskSystemEvents.Active, (event) => this.emit(TaskServiceEvents.Active, event))); + this._taskSystemListeners.push(this._taskSystem.addListener(TaskSystemEvents.Inactive, (event) => this.emit(TaskServiceEvents.Inactive, event))); + this._taskSystemListeners.push(this._taskSystem.addListener(TaskSystemEvents.Terminated, (event) => this.emit(TaskServiceEvents.Terminated, event))); + this._taskSystemListeners.push(this._taskSystem.addListener(TaskSystemEvents.Changed, () => this.emit(TaskServiceEvents.Changed))); + return this._taskSystem; + } + + private getTaskSets(): TPromise { + return this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask').then(() => { + return new TPromise((resolve, reject) => { + let result: TaskSet[] = []; + let counter: number = 0; + let done = (value: TaskSet) => { + if (value) { + result.push(value); + } + if (--counter === 0) { + resolve(result); + } + }; + let error = () => { + if (--counter === 0) { + resolve(result); + } + }; + if (this.getJsonSchemaVersion() === JsonSchemaVersion.V2_0_0 && this._providers.size > 0) { + this._providers.forEach((provider) => { + counter++; + provider.provideTasks().done(done, error); + }); + } else { + resolve(result); + } + }); + }).then((result) => { + return this.getWorkspaceTasks().then((workspaceTaskResult) => { + let workspaceTasksToDelete: Task[] = []; + let configurations = workspaceTaskResult.configurations; + let legacyTaskConfigurations = workspaceTaskResult.set ? this.getLegacyTaskConfigurations(workspaceTaskResult.set) : undefined; + if (configurations || legacyTaskConfigurations) { + for (let set of result) { + for (let i = 0; i < set.tasks.length; i++) { + let task = set.tasks[i]; + if (!ContributedTask.is(task)) { + continue; + } + if (configurations) { + let configuringTask = configurations.byIdentifier[task.defines._key]; + if (configuringTask) { + set.tasks[i] = TaskConfig.createCustomTask(task, configuringTask); + continue; + } + } + if (legacyTaskConfigurations) { + let configuringTask = legacyTaskConfigurations[task.defines._key]; + if (configuringTask) { + set.tasks[i] = TaskConfig.createCustomTask(task, configuringTask); + workspaceTasksToDelete.push(configuringTask); + set.tasks[i] = configuringTask; + continue; + } + } + } + } + } + if (workspaceTaskResult.set) { + if (workspaceTasksToDelete.length > 0) { + let tasks = workspaceTaskResult.set.tasks; + let newSet: TaskSet = { + extension: workspaceTaskResult.set.extension, + tasks: [] + }; + let toDelete = workspaceTasksToDelete.reduce>((map, task) => { + map[task._id] = true; + return map; + }, Object.create(null)); + newSet.tasks = tasks.filter(task => !toDelete[task._id]); + result.push(newSet); + } else { + result.push(workspaceTaskResult.set); + } + } + return result; + }, () => { + // If we can't read the tasks.json file provide at least the contributed tasks + return result; + }); + }); + } + + private getLegacyTaskConfigurations(workspaceTasks: TaskSet): IStringDictionary { + let result: IStringDictionary; + function getResult() { + if (result) { + return result; + } + result = Object.create(null); + return result; + } + for (let task of workspaceTasks.tasks) { + if (CustomTask.is(task)) { + let commandName = task.command && task.command.name; + // This is for backwards compatibility with the 0.1.0 task annotation code + // if we had a gulp, jake or grunt command a task specification was a annotation + if (commandName === 'gulp' || commandName === 'grunt' || commandName === 'jake') { + let identifier: TaskIdentifier = TaskConfig.getTaskIdentifier({ + type: commandName, + task: task.name + } as TaskConfig.TaskIdentifier); + getResult()[identifier._key] = task; + } + } + } + return result; + } + + private getWorkspaceTasks(): TPromise { + if (this._workspaceTasksPromise) { + return this._workspaceTasksPromise; + } + this.updateWorkspaceTasks(); + return this._workspaceTasksPromise; + } + + private updateWorkspaceTasks(): void { + this._workspaceTasksPromise = this.computeWorkspaceTasks().then(value => { + this._configHasErrors = value.hasErrors; + if (this._taskSystem instanceof ProcessTaskSystem) { + this._taskSystem.hasErrors(this._configHasErrors); + } + return value; + }); + } + + private computeWorkspaceTasks(): TPromise { + let configPromise: TPromise; + { + let { config, hasParseErrors } = this.getConfiguration(); + if (hasParseErrors) { + return TPromise.as({ set: undefined, hasErrors: true, configurations: undefined }); + } + let engine = ExecutionEngine._default; + if (config) { + engine = TaskConfig.ExecutionEngine.from(config); + if (engine === ExecutionEngine.Process) { + if (this.hasDetectorSupport(config)) { + configPromise = new ProcessRunnerDetector(this.fileService, this.contextService, this.configurationResolverService, config).detect(true).then((value): WorkspaceConfigurationResult => { + let hasErrors = this.printStderr(value.stderr); + let detectedConfig = value.config; + if (!detectedConfig) { + return { config, hasErrors }; + } + let result: TaskConfig.ExternalTaskRunnerConfiguration = Objects.clone(config); + let configuredTasks: IStringDictionary = Object.create(null); + if (!result.tasks) { + if (detectedConfig.tasks) { + result.tasks = detectedConfig.tasks; + } + } else { + result.tasks.forEach(task => configuredTasks[task.taskName] = task); + detectedConfig.tasks.forEach((task) => { + if (!configuredTasks[task.taskName]) { + result.tasks.push(task); + } + }); + } + return { config: result, hasErrors }; + }); + } else { + configPromise = TPromise.as({ config, hasErrors: false }); + } + } else { + configPromise = TPromise.as({ config, hasErrors: false }); + } + } else { + if (engine === ExecutionEngine.Terminal) { + configPromise = TPromise.as({ config, hasErrors: false }); + } else { + configPromise = new ProcessRunnerDetector(this.fileService, this.contextService, this.configurationResolverService).detect(true).then((value) => { + let hasErrors = this.printStderr(value.stderr); + return { config: value.config, hasErrors }; + }); + } + } + } + return configPromise.then((resolved) => { + return ProblemMatcherRegistry.onReady().then((): WorkspaceTaskResult => { + if (!resolved || !resolved.config) { + return { set: undefined, configurations: undefined, hasErrors: resolved !== void 0 ? resolved.hasErrors : false }; + } + let problemReporter = new ProblemReporter(this._outputChannel); + let parseResult = TaskConfig.parse(resolved.config, problemReporter); + let hasErrors = false; + if (!parseResult.validationStatus.isOK()) { + hasErrors = true; + this.showOutput(); + } + if (problemReporter.status.isFatal()) { + problemReporter.fatal(nls.localize('TaskSystem.configurationErrors', 'Error: the provided task configuration has validation errors and can\'t not be used. Please correct the errors first.')); + return { set: undefined, configurations: undefined, hasErrors }; + } + let customizedTasks: { byIdentifier: IStringDictionary; }; + if (parseResult.configured && parseResult.configured.length > 0) { + customizedTasks = { + byIdentifier: Object.create(null) + }; + for (let task of parseResult.configured) { + customizedTasks.byIdentifier[task.configures._key] = task; + } + } + return { set: { tasks: parseResult.custom }, configurations: customizedTasks, hasErrors }; + }); + }); + } + + private getExecutionEngine(): ExecutionEngine { + let { config } = this.getConfiguration(); + if (!config) { + return ExecutionEngine._default; + } + return TaskConfig.ExecutionEngine.from(config); + } + + private getJsonSchemaVersion(): JsonSchemaVersion { + let { config } = this.getConfiguration(); + if (!config) { + return JsonSchemaVersion.V2_0_0; + } + return TaskConfig.JsonSchemaVersion.from(config); + } + + private getConfiguration(): { config: TaskConfig.ExternalTaskRunnerConfiguration; hasParseErrors: boolean } { + let result = this.contextService.hasWorkspace() ? this.configurationService.getConfiguration('tasks', { resource: this.contextService.getLegacyWorkspace().resource }) : undefined; + if (!result) { + return { config: undefined, hasParseErrors: false }; + } + let parseErrors: string[] = (result as any).$parseErrors; + if (parseErrors) { + let isAffected = false; + for (let i = 0; i < parseErrors.length; i++) { + if (/tasks\.json$/.test(parseErrors[i])) { + isAffected = true; + break; + } + } + if (isAffected) { + this._outputChannel.append(nls.localize('TaskSystem.invalidTaskJson', 'Error: The content of the tasks.json file has syntax errors. Please correct them before executing a task.\n')); + this.showOutput(); + return { config: undefined, hasParseErrors: true }; + } + } + return { config: result, hasParseErrors: false }; + } + + private printStderr(stderr: string[]): boolean { + let result = false; + if (stderr && stderr.length > 0) { + stderr.forEach((line) => { + result = true; + this._outputChannel.append(line + '\n'); + }); + this._outputChannel.show(true); + } + return result; + } + + public inTerminal(): boolean { + if (this._taskSystem) { + return this._taskSystem instanceof TerminalTaskSystem; + } + return this.getExecutionEngine() === ExecutionEngine.Terminal; + } + + private hasDetectorSupport(config: TaskConfig.ExternalTaskRunnerConfiguration): boolean { + if (!config.command || !this.contextService.hasWorkspace()) { + return false; + } + return ProcessRunnerDetector.supports(config.command); + } + + public configureAction(): Action { + return new ConfigureTaskRunnerAction(ConfigureTaskRunnerAction.ID, ConfigureTaskRunnerAction.TEXT, this, + this.configurationService, this.editorService, this.fileService, this.contextService, + this.outputService, this.messageService, this.quickOpenService, this.environmentService, this.configurationResolverService, + this.extensionService, this.telemetryService); + } + + private configureBuildTask(): Action { + return new ConfigureBuildTaskAction(ConfigureBuildTaskAction.ID, ConfigureBuildTaskAction.TEXT, this, + this.configurationService, this.editorService, this.fileService, this.contextService, + this.outputService, this.messageService, this.quickOpenService, this.environmentService, this.configurationResolverService, + this.extensionService, this.telemetryService); + } + + public beforeShutdown(): boolean | TPromise { + if (!this._taskSystem) { + return false; + } + this.saveRecentlyUsedTasks(); + if (!this._taskSystem.isActiveSync()) { + return false; + } + // The terminal service kills all terminal on shutdown. So there + // is nothing we can do to prevent this here. + if (this._taskSystem instanceof TerminalTaskSystem) { + return false; + } + if (this._taskSystem.canAutoTerminate() || this.messageService.confirm({ + message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'), + primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"), + type: 'question' + })) { + return this._taskSystem.terminateAll().then((responses) => { + let success = true; + let code: number = undefined; + for (let response of responses) { + success = success && response.success; + // We only have a code in the old output runner which only has one task + // So we can use the first code. + if (code === void 0 && response.code !== void 0) { + code = response.code; + } + } + if (success) { + this.emit(TaskServiceEvents.Terminated, {}); + this._taskSystem = null; + this.disposeTaskSystemListeners(); + return false; // no veto + } else if (code && code === TerminateResponseCode.ProcessNotFound) { + return !this.messageService.confirm({ + message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'), + primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"), + type: 'info' + }); + } + return true; // veto + }, (err) => { + return true; // veto + }); + } else { + return true; // veto + } + } + + private getConfigureAction(code: TaskErrors): Action { + switch (code) { + case TaskErrors.NoBuildTask: + return this.configureBuildTask(); + default: + return this.configureAction(); + } + } + + private handleError(err: any): void { + let showOutput = true; + if (err instanceof TaskError) { + let buildError = err; + let needsConfig = buildError.code === TaskErrors.NotConfigured || buildError.code === TaskErrors.NoBuildTask || buildError.code === TaskErrors.NoTestTask; + let needsTerminate = buildError.code === TaskErrors.RunningTask; + if (needsConfig || needsTerminate) { + let closeAction = new CloseMessageAction(); + let action: Action = needsConfig + ? this.getConfigureAction(buildError.code) + : new Action( + 'workbench.action.tasks.terminate', + nls.localize('TerminateAction.label', "Terminate Task"), + undefined, true, () => { this.runTerminateCommand(); return TPromise.as(undefined); }); + closeAction.closeFunction = this.messageService.show(buildError.severity, { message: buildError.message, actions: [action, closeAction] }); + } else { + this.messageService.show(buildError.severity, buildError.message); + } + } else if (err instanceof Error) { + let error = err; + this.messageService.show(Severity.Error, error.message); + } else if (Types.isString(err)) { + this.messageService.show(Severity.Error, err); + } else { + this.messageService.show(Severity.Error, nls.localize('TaskSystem.unknownError', 'An error has occurred while running a task. See task log for details.')); + } + if (showOutput) { + this._outputChannel.show(true); + } + } + + private canRunCommand(): boolean { + if (!this.contextService.hasWorkspace()) { + this.messageService.show(Severity.Info, nls.localize('TaskService.noWorkspace', 'Tasks are only available on a workspace folder.')); + return false; + } + return true; + } + + private showQuickPick(tasks: Task[], placeHolder: string, group: boolean = false, sort: boolean = false): TPromise { + if (tasks === void 0 || tasks === null || tasks.length === 0) { + return TPromise.as(undefined); + } + interface TaskQickPickEntry extends IPickOpenEntry { + task: Task; + } + function TaskQickPickEntry(task: Task): TaskQickPickEntry { + return { label: task._label, task }; + } + function fillEntries(entries: TaskQickPickEntry[], tasks: Task[], groupLabel: string, withBorder: boolean = false): void { + let first = true; + for (let task of tasks) { + if (first) { + first = false; + let entry = TaskQickPickEntry(task); + entry.separator = { label: groupLabel, border: withBorder }; + entries.push(entry); + } else { + entries.push(TaskQickPickEntry(task)); + } + } + } + let entries: TaskQickPickEntry[]; + if (group) { + entries = []; + if (tasks.length === 1) { + entries.push(TaskQickPickEntry(tasks[0])); + } else { + let recentlyUsedTasks = this.getRecentlyUsedTasks(); + let recent: Task[] = []; + let configured: Task[] = []; + let detected: Task[] = []; + let taskMap: IStringDictionary = Object.create(null); + tasks.forEach(task => taskMap[Task.getKey(task)] = task); + recentlyUsedTasks.keys().forEach(key => { + let task = taskMap[key]; + if (task) { + recent.push(task); + } + }); + for (let task of tasks) { + if (!recentlyUsedTasks.has(Task.getKey(task))) { + if (task._source.kind === TaskSourceKind.Workspace) { + configured.push(task); + } else { + detected.push(task); + } + } + } + let hasRecentlyUsed: boolean = recent.length > 0; + fillEntries(entries, recent, nls.localize('recentlyUsed', 'recently used tasks')); + configured = configured.sort((a, b) => a._label.localeCompare(b._label)); + let hasConfigured = configured.length > 0; + fillEntries(entries, configured, nls.localize('configured', 'configured tasks'), hasRecentlyUsed); + detected = detected.sort((a, b) => a._label.localeCompare(b._label)); + fillEntries(entries, detected, nls.localize('detected', 'detected tasks'), hasRecentlyUsed || hasConfigured); + } + } else { + entries = tasks.map(task => { return { label: task._label, task }; }); + if (sort) { + entries = entries.sort((a, b) => a.task._label.localeCompare(b.task._label)); + } + } + return this.quickOpenService.pick(entries, { placeHolder, autoFocus: { autoFocusFirstEntry: true } }).then(entry => entry ? entry.task : undefined); + } + + private runTaskCommand(accessor: ServicesAccessor, arg: any): void { + if (!this.canRunCommand()) { + return; + } + if (Types.isString(arg)) { + this.getTask(arg).then((task) => { + if (task) { + this.run(task); + } else { + this.quickOpenService.show('task '); + } + }, () => { + this.quickOpenService.show('task '); + }); + } else { + this.quickOpenService.show('task '); + } + } + + private runBuildCommand(): void { + if (!this.canRunCommand()) { + return; + } + if (this.getJsonSchemaVersion() === JsonSchemaVersion.V0_1_0) { + this.build(); + return; + } + let options: IProgressOptions = { + location: ProgressLocation.Window, + title: nls.localize('TaskService.fetchingBuildTasks', 'Fetching build tasks...') + }; + let promise = this.getTasksForGroup(TaskGroup.Build).then((tasks) => { + if (tasks.length === 0) { + this.messageService.show( + Severity.Info, + { + message: nls.localize('TaskService.noBuildTaskTerminal', 'No Build Task found. Press \'Configure Build Task\' to define one.'), + actions: [this.configureBuildTask(), new CloseMessageAction()] + } + ); + return; + } + let primaries: Task[] = []; + for (let task of tasks) { + // We only have build tasks here + if (task.isDefaultGroupEntry) { + primaries.push(task); + } + } + if (primaries.length === 1) { + this.run(primaries[0]); + return; + } + this.showQuickPick(tasks, nls.localize('TaskService.pickBuildTask', 'Select the build task to run'), true).then((task) => { + if (task) { + this.run(task, { attachProblemMatcher: true }); + } + }); + }); + this.progressService.withProgress(options, () => promise); + } + + private runTestCommand(): void { + if (!this.canRunCommand()) { + return; + } + if (this.getJsonSchemaVersion() === JsonSchemaVersion.V0_1_0) { + this.runTest(); + return; + } + let options: IProgressOptions = { + location: ProgressLocation.Window, + title: nls.localize('TaskService.fetchingTestTasks', 'Fetching test tasks...') + }; + let promise = this.getTasksForGroup(TaskGroup.Test).then((tasks) => { + if (tasks.length === 0) { + this.messageService.show( + Severity.Info, + { + message: nls.localize('TaskService.noTestTaskTerminal', 'No Test Task found. Press \'Configure Task Runner\' to define one.'), + actions: [this.configureAction(), new CloseMessageAction()] + } + ); + return; + } + let primaries: Task[] = []; + for (let task of tasks) { + // We only have test task here. + if (task.isDefaultGroupEntry) { + primaries.push(task); + } + } + if (primaries.length === 1) { + this.run(primaries[0]); + return; + } + this.showQuickPick(tasks, nls.localize('TaskService.pickTestTask', 'Select the test task to run'), true).then((task) => { + if (task) { + this.run(task); + } + }); + }); + this.progressService.withProgress(options, () => promise); + } + + private runTerminateCommand(): void { + if (!this.canRunCommand()) { + return; + } + if (this.inTerminal()) { + this.getActiveTasks().then((activeTasks) => { + if (activeTasks.length === 0) { + this.messageService.show(Severity.Info, nls.localize('TaskService.noTaskRunning', 'No task is currently running.')); + return; + } + this.showQuickPick(activeTasks, nls.localize('TaskService.tastToTerminate', 'Select task to terminate'), false, true).then(task => { + if (task) { + this.terminate(task); + } + }); + }); + } else { + this.isActive().then((active) => { + if (active) { + this.terminateAll().then((responses) => { + // the output runner has only one task + let response = responses[0]; + if (response.success) { + return; + } + if (response.code && response.code === TerminateResponseCode.ProcessNotFound) { + this.messageService.show(Severity.Error, nls.localize('TerminateAction.noProcess', 'The launched process doesn\'t exist anymore. If the task spawned background tasks exiting VS Code might result in orphaned processes.')); + } else { + this.messageService.show(Severity.Error, nls.localize('TerminateAction.failed', 'Failed to terminate running task')); + } + }); + } + }); + } + } + + private runRestartTaskCommand(accessor: ServicesAccessor, arg: any): void { + if (!this.canRunCommand()) { + return; + } + if (this.inTerminal()) { + this.getActiveTasks().then((activeTasks) => { + if (activeTasks.length === 0) { + this.messageService.show(Severity.Info, nls.localize('TaskService.noTaskToRestart', 'No task to restart.')); + return; + } + this.showQuickPick(activeTasks, nls.localize('TaskService.tastToRestart', 'Select the task to restart'), false, true).then(task => { + if (task) { + this.restart(task); + } + }); + }); + } else { + this.getActiveTasks().then((activeTasks) => { + if (activeTasks.length === 0) { + return; + } + let task = activeTasks[0]; + this.restart(task); + }); + } + } + + private runConfigureDefaultBuildTask(): void { + if (!this.canRunCommand()) { + return; + } + if (this.getJsonSchemaVersion() === JsonSchemaVersion.V2_0_0) { + this.tasks().then((tasks => { + if (tasks.length === 0) { + this.configureBuildTask().run(); + return; + } + let defaultTask: Task; + for (let task of tasks) { + if (task.group === TaskGroup.Build && task.isDefaultGroupEntry) { + defaultTask = task; + break; + } + } + if (defaultTask) { + this.messageService.show(Severity.Info, nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task.', defaultTask._label)); + return; + } + this.showQuickPick(tasks, nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), true).then((task) => { + if (!task) { + return; + } + if (!CompositeTask.is(task)) { + this.customize(task, { group: { kind: 'build', isDefault: true } }, true); + } + }); + })); + } else { + this.configureBuildTask().run(); + } + } + + private runConfigureDefaultTestTask(): void { + if (!this.canRunCommand()) { + return; + } + if (this.getJsonSchemaVersion() === JsonSchemaVersion.V2_0_0) { + this.tasks().then((tasks => { + if (tasks.length === 0) { + this.configureAction().run(); + } + let defaultTask: Task; + for (let task of tasks) { + if (task.group === TaskGroup.Test && task.isDefaultGroupEntry) { + defaultTask = task; + break; + } + } + if (defaultTask) { + this.messageService.show(Severity.Info, nls.localize('TaskService.defaultTestTaskExists', '{0} is already marked as the default test task.', defaultTask._label)); + return; + } + this.showQuickPick(tasks, nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), true).then((task) => { + if (!task) { + return; + } + if (!CompositeTask.is(task)) { + this.customize(task, { group: { kind: 'test', isDefault: true } }, true); + } + }); + })); + } else { + this.configureAction().run(); + } + } + + public runShowTasks(): void { + if (!this.canRunCommand()) { + return; + } + if (!this._taskSystem) { + this.messageService.show(Severity.Info, nls.localize('TaskService.noTaskIsRunning', 'No task is running.')); + return; + } + this.getActiveTasks().then((tasks) => { + if (tasks.length === 0) { + this.messageService.show(Severity.Info, nls.localize('TaskService.noTaskIsRunning', 'No task is running.')); + } else if (tasks.length === 1) { + if (this._taskSystem) { + this._taskSystem.revealTask(tasks[0]); + } + } else { + this.showQuickPick(tasks, nls.localize('TaskService.pickShowTask', 'Select the task to show its output'), false, true).then((task) => { + if (!task || !this._taskSystem) { + return; + } + this._taskSystem.revealTask(task); + }); + } + }); + } +} + +// {{SQL CARBON EDIT}} +// Removing all gulp task runner commands for carbon +/* +let workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureTaskRunnerAction, ConfigureTaskRunnerAction.ID, ConfigureTaskRunnerAction.TEXT), 'Tasks: Configure Task Runner', tasksCategory); + +MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, category: { value: tasksCategory, original: 'Tasks' } }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, category: { value: tasksCategory, original: 'Tasks' } }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.restartTask', title: { value: nls.localize('RestartTaskAction.label', "Restart Running Task"), original: 'Restart Running Task' }, category: { value: tasksCategory, original: 'Tasks' } }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.showTasks', title: { value: nls.localize('ShowTasksAction.label', "Show Running Tasks"), original: 'Show Running Tasks' }, category: { value: tasksCategory, original: 'Tasks' } }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.terminate', title: { value: nls.localize('TerminateAction.label', "Terminate Task"), original: 'Terminate Task' }, category: { value: tasksCategory, original: 'Tasks' } }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.build', title: { value: nls.localize('BuildAction.label', "Run Build Task"), original: 'Run Build Task' }, category: { value: tasksCategory, original: 'Tasks' } }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.test', title: { value: nls.localize('TestAction.label', "Run Test Task"), original: 'Run Test Task' }, category: { value: tasksCategory, original: 'Tasks' } }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.configureDefaultBuildTask', title: { value: nls.localize('ConfigureDefaultBuildTask.label', "Configure Default Build Task"), original: 'Configure Default Build Task' }, category: { value: tasksCategory, original: 'Tasks' } }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.configureDefaultTestTask', title: { value: nls.localize('ConfigureDefaultTestTask.label', "Configure Default Test Task"), original: 'Configure Default Test Task' }, category: { value: tasksCategory, original: 'Tasks' } }); +// MenuRegistry.addCommand( { id: 'workbench.action.tasks.rebuild', title: nls.localize('RebuildAction.label', 'Run Rebuild Task'), category: tasksCategory }); +// {{SQL CARBON EDIT}} +*/ +// MenuRegistry.addCommand( { id: 'workbench.action.tasks.clean', title: nls.localize('CleanAction.label', 'Run Clean Task'), category: tasksCategory }); + +// Task Service +registerSingleton(ITaskService, TaskService); + +// Register Quick Open +const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); +const tasksPickerContextKey = 'inTasksPicker'; + +quickOpenRegistry.registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/tasks/browser/taskQuickOpen', + 'QuickOpenHandler', + 'task ', + tasksPickerContextKey, + nls.localize('quickOpen.task', "Run Task") + ) +); + +const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); +actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionContributor); + +// Status bar +let statusbarRegistry = Registry.as(StatusbarExtensions.Statusbar); +statusbarRegistry.registerStatusbarItem(new StatusbarItemDescriptor(BuildStatusBarItem, StatusbarAlignment.LEFT, 50 /* Medium Priority */)); +statusbarRegistry.registerStatusbarItem(new StatusbarItemDescriptor(TaskStatusBarItem, StatusbarAlignment.LEFT, 50 /* Medium Priority */)); + +// Output channel +let outputChannelRegistry = Registry.as(OutputExt.OutputChannels); +outputChannelRegistry.registerChannel(TaskService.OutputChannelId, TaskService.OutputChannelLabel); + +// (Registry.as(WorkbenchExtensions.Workbench)).registerWorkbenchContribution(TaskServiceParticipant); + +// tasks.json validation +let schemaId = 'vscode://schemas/tasks'; +let schema: IJSONSchema = { + id: schemaId, + description: 'Task definition file', + type: 'object', + default: { + version: '0.1.0', + command: 'myCommand', + isShellCommand: false, + args: [], + showOutput: 'always', + tasks: [ + { + taskName: 'build', + showOutput: 'silent', + isBuildCommand: true, + problemMatcher: ['$tsc', '$lessCompile'] + } + ] + } +}; + +import schemaVersion1 from './jsonSchema_v1'; +import schemaVersion2 from './jsonSchema_v2'; +schema.definitions = { + ...schemaVersion1.definitions, + ...schemaVersion2.definitions, +}; +schema.oneOf = [...schemaVersion2.oneOf, ...schemaVersion1.oneOf]; + + +let jsonRegistry = Registry.as(jsonContributionRegistry.Extensions.JSONContribution); +jsonRegistry.registerSchema(schemaId, schema); \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts new file mode 100644 index 0000000000..b638548b64 --- /dev/null +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -0,0 +1,751 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import fs = require('fs'); +import path = require('path'); + +import * as nls from 'vs/nls'; +import * as Objects from 'vs/base/common/objects'; +import * as Types from 'vs/base/common/types'; +import { CharCode } from 'vs/base/common/charCode'; +import * as Platform from 'vs/base/common/platform'; +import * as Async from 'vs/base/common/async'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { LinkedMap, Touch } from 'vs/base/common/map'; +import Severity from 'vs/base/common/severity'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as TPath from 'vs/base/common/paths'; +// import URI from 'vs/base/common/uri'; + +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ProblemMatcher, ProblemMatcherRegistry /*, ProblemPattern, getResource */ } from 'vs/platform/markers/common/problemMatcher'; + +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal'; +import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common/output'; +import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors'; +import { Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind } from 'vs/workbench/parts/tasks/common/tasks'; +import { + ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver, + TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType, TaskTerminateResponse +} from 'vs/workbench/parts/tasks/common/taskSystem'; + +class TerminalDecoder { + // See https://en.wikipedia.org/wiki/ANSI_escape_code & http://stackoverflow.com/questions/25189651/how-to-remove-ansi-control-chars-vt100-from-a-java-string & + // https://www.npmjs.com/package/strip-ansi + private static ANSI_CONTROL_SEQUENCE: RegExp = /\x1b[[()#;?]*(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-ORZcf-nqry=><]/g; + private static OPERATING_SYSTEM_COMMAND_SEQUENCE: RegExp = /\x1b[\]](?:.*)(?:\x07|\x1b\\)/g; + + private remaining: string; + + public write(data: string): string[] { + let result: string[] = []; + data = data.replace(TerminalDecoder.ANSI_CONTROL_SEQUENCE, ''); + data = data.replace(TerminalDecoder.OPERATING_SYSTEM_COMMAND_SEQUENCE, ''); + let value = this.remaining + ? this.remaining + data + : data; + + if (value.length < 1) { + return result; + } + let start = 0; + let ch: number; + while (start < value.length && ((ch = value.charCodeAt(start)) === CharCode.CarriageReturn || ch === CharCode.LineFeed)) { + start++; + } + let idx = start; + while (idx < value.length) { + ch = value.charCodeAt(idx); + if (ch === CharCode.CarriageReturn || ch === CharCode.LineFeed) { + result.push(value.substring(start, idx)); + idx++; + while (idx < value.length && ((ch = value.charCodeAt(idx)) === CharCode.CarriageReturn || ch === CharCode.LineFeed)) { + idx++; + } + start = idx; + } else { + idx++; + } + } + this.remaining = start < value.length ? value.substr(start) : undefined; + return result; + } + + public end(): string { + return this.remaining; + } +} + +interface PrimaryTerminal { + terminal: ITerminalInstance; + busy: boolean; +} + +interface TerminalData { + terminal: ITerminalInstance; + lastTask: string; +} + +interface ActiveTerminalData { + terminal: ITerminalInstance; + task: Task; + promise: TPromise; +} + +export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { + + public static TelemetryEventName: string = 'taskService'; + + private outputChannel: IOutputChannel; + private activeTasks: IStringDictionary; + private terminals: IStringDictionary; + private idleTaskTerminals: LinkedMap; + private sameTaskTerminals: IStringDictionary; + + constructor(private terminalService: ITerminalService, private outputService: IOutputService, + private markerService: IMarkerService, private modelService: IModelService, + private configurationResolverService: IConfigurationResolverService, + private telemetryService: ITelemetryService, + private workbenchEditorService: IWorkbenchEditorService, + private contextService: IWorkspaceContextService, + outputChannelId: string) { + super(); + + this.outputChannel = this.outputService.getChannel(outputChannelId); + this.activeTasks = Object.create(null); + this.terminals = Object.create(null); + this.idleTaskTerminals = new LinkedMap(); + this.sameTaskTerminals = Object.create(null); + } + + public log(value: string): void { + this.outputChannel.append(value + '\n'); + } + + protected showOutput(): void { + this.outputChannel.show(true); + } + + public run(task: Task, resolver: ITaskResolver, trigger: string = Triggers.command): ITaskExecuteResult { + let terminalData = this.activeTasks[task._id]; + if (terminalData && terminalData.promise) { + let reveal = RevealKind.Always; + let focus = false; + if (CustomTask.is(task) || ContributedTask.is(task)) { + reveal = task.command.presentation.reveal; + focus = task.command.presentation.focus; + } + if (reveal === RevealKind.Always || focus) { + this.terminalService.setActiveInstance(terminalData.terminal); + this.terminalService.showPanel(focus); + } + return { kind: TaskExecuteKind.Active, active: { same: true, background: task.isBackground }, promise: terminalData.promise }; + } + + try { + return { kind: TaskExecuteKind.Started, started: {}, promise: this.executeTask(Object.create(null), task, resolver, trigger) }; + } catch (error) { + if (error instanceof TaskError) { + throw error; + } else if (error instanceof Error) { + this.log(error.message); + throw new TaskError(Severity.Error, error.message, TaskErrors.UnknownError); + } else { + this.log(error.toString()); + throw new TaskError(Severity.Error, nls.localize('TerminalTaskSystem.unknownError', 'A unknown error has occurred while executing a task. See task output log for details.'), TaskErrors.UnknownError); + } + } + } + + + public revealTask(task: Task): boolean { + let terminalData = this.activeTasks[task._id]; + if (!terminalData) { + return false; + } + this.terminalService.setActiveInstance(terminalData.terminal); + if (CustomTask.is(task) || ContributedTask.is(task)) { + this.terminalService.showPanel(task.command.presentation.focus); + } + return true; + } + + public isActive(): TPromise { + return TPromise.as(this.isActiveSync()); + } + + public isActiveSync(): boolean { + return Object.keys(this.activeTasks).length > 0; + } + + public canAutoTerminate(): boolean { + return Object.keys(this.activeTasks).every(key => !this.activeTasks[key].task.promptOnClose); + } + + public getActiveTasks(): Task[] { + return Object.keys(this.activeTasks).map(key => this.activeTasks[key].task); + } + + public terminate(id: string): TPromise { + let activeTerminal = this.activeTasks[id]; + if (!activeTerminal) { + return TPromise.as({ success: false, task: undefined }); + }; + return new TPromise((resolve, reject) => { + let terminal = activeTerminal.terminal; + const onExit = terminal.onExit(() => { + let task = activeTerminal.task; + try { + onExit.dispose(); + let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group, __task: task }; + this.emit(TaskSystemEvents.Terminated, event); + } catch (error) { + // Do nothing. + } + resolve({ success: true, task: task }); + }); + terminal.dispose(); + }); + } + + public terminateAll(): TPromise { + let promises: TPromise[] = []; + Object.keys(this.activeTasks).forEach((key) => { + let terminalData = this.activeTasks[key]; + let terminal = terminalData.terminal; + promises.push(new TPromise((resolve, reject) => { + const onExit = terminal.onExit(() => { + let task = terminalData.task; + try { + onExit.dispose(); + let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group, __task: task }; + this.emit(TaskSystemEvents.Terminated, event); + } catch (error) { + // Do nothing. + } + resolve({ success: true, task: terminalData.task }); + }); + })); + terminal.dispose(); + }); + this.activeTasks = Object.create(null); + return TPromise.join(promises); + } + + private executeTask(startedTasks: IStringDictionary>, task: Task, resolver: ITaskResolver, trigger: string): TPromise { + let promises: TPromise[] = []; + if (task.dependsOn) { + task.dependsOn.forEach((identifier) => { + let task = resolver.resolve(identifier); + if (task) { + let promise = startedTasks[task._id]; + if (!promise) { + promise = this.executeTask(startedTasks, task, resolver, trigger); + startedTasks[task._id] = promise; + } + promises.push(promise); + } + }); + } + + if ((ContributedTask.is(task) || CustomTask.is(task)) && (task.command)) { + return TPromise.join(promises).then((summaries): TPromise | ITaskSummary => { + for (let summary of summaries) { + if (summary.exitCode !== 0) { + return { exitCode: summary.exitCode }; + } + } + return this.executeCommand(task, trigger); + }); + } else { + return TPromise.join(promises).then((summaries): ITaskSummary => { + for (let summary of summaries) { + if (summary.exitCode !== 0) { + return { exitCode: summary.exitCode }; + } + } + return { exitCode: 0 }; + }); + } + } + + private executeCommand(task: CustomTask | ContributedTask, trigger: string): TPromise { + let terminal: ITerminalInstance = undefined; + let executedCommand: string = undefined; + let promise: TPromise = undefined; + if (task.isBackground) { + promise = new TPromise((resolve, reject) => { + const problemMatchers = this.resolveMatchers(task.problemMatchers); + let watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this.markerService, this.modelService); + let toUnbind: IDisposable[] = []; + let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.Watching, group: task.group, __task: task }; + let eventCounter: number = 0; + toUnbind.push(watchingProblemMatcher.addListener(ProblemCollectorEvents.WatchingBeginDetected, () => { + eventCounter++; + this.emit(TaskSystemEvents.Active, event); + })); + toUnbind.push(watchingProblemMatcher.addListener(ProblemCollectorEvents.WatchingEndDetected, () => { + eventCounter--; + this.emit(TaskSystemEvents.Inactive, event); + })); + watchingProblemMatcher.aboutToStart(); + let delayer: Async.Delayer = null; + let decoder = new TerminalDecoder(); + [terminal, executedCommand] = this.createTerminal(task); + const registeredLinkMatchers = this.registerLinkMatchers(terminal, problemMatchers); + const onData = terminal.onData((data: string) => { + decoder.write(data).forEach(line => { + watchingProblemMatcher.processLine(line); + if (delayer === null) { + delayer = new Async.Delayer(3000); + } + delayer.trigger(() => { + watchingProblemMatcher.forceDelivery(); + delayer = null; + }); + }); + }); + const onExit = terminal.onExit((exitCode) => { + onData.dispose(); + onExit.dispose(); + delete this.activeTasks[task._id]; + this.emit(TaskSystemEvents.Changed); + switch (task.command.presentation.panel) { + case PanelKind.Dedicated: + this.sameTaskTerminals[task._id] = terminal.id.toString(); + break; + case PanelKind.Shared: + this.idleTaskTerminals.set(task._id, terminal.id.toString(), Touch.First); + break; + } + let remaining = decoder.end(); + if (remaining) { + watchingProblemMatcher.processLine(remaining); + } + watchingProblemMatcher.dispose(); + registeredLinkMatchers.forEach(handle => terminal.deregisterLinkMatcher(handle)); + toUnbind = dispose(toUnbind); + toUnbind = null; + for (let i = 0; i < eventCounter; i++) { + this.emit(TaskSystemEvents.Inactive, event); + } + eventCounter = 0; + let reveal = task.command.presentation.reveal; + if (exitCode && exitCode === 1 && watchingProblemMatcher.numberOfMatches === 0 && reveal !== RevealKind.Never) { + this.terminalService.setActiveInstance(terminal); + this.terminalService.showPanel(false); + } + resolve({ exitCode }); + }); + }); + } else { + promise = new TPromise((resolve, reject) => { + [terminal, executedCommand] = this.createTerminal(task); + let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group, __task: task }; + this.emit(TaskSystemEvents.Active, event); + let decoder = new TerminalDecoder(); + let problemMatchers = this.resolveMatchers(task.problemMatchers); + let startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService); + const registeredLinkMatchers = this.registerLinkMatchers(terminal, problemMatchers); + const onData = terminal.onData((data: string) => { + decoder.write(data).forEach((line) => { + startStopProblemMatcher.processLine(line); + }); + }); + const onExit = terminal.onExit((exitCode) => { + onData.dispose(); + onExit.dispose(); + delete this.activeTasks[task._id]; + this.emit(TaskSystemEvents.Changed); + switch (task.command.presentation.panel) { + case PanelKind.Dedicated: + this.sameTaskTerminals[task._id] = terminal.id.toString(); + break; + case PanelKind.Shared: + this.idleTaskTerminals.set(task._id, terminal.id.toString(), Touch.First); + break; + } + let remaining = decoder.end(); + if (remaining) { + startStopProblemMatcher.processLine(remaining); + } + startStopProblemMatcher.done(); + startStopProblemMatcher.dispose(); + registeredLinkMatchers.forEach(handle => terminal.deregisterLinkMatcher(handle)); + this.emit(TaskSystemEvents.Inactive, event); + // See https://github.com/Microsoft/vscode/issues/31965 + if (exitCode === 0 && startStopProblemMatcher.numberOfMatches > 0) { + exitCode = 1; + } + resolve({ exitCode }); + }); + }); + } + this.terminalService.setActiveInstance(terminal); + if (task.command.presentation.reveal === RevealKind.Always || (task.command.presentation.reveal === RevealKind.Silent && task.problemMatchers.length === 0)) { + this.terminalService.showPanel(task.command.presentation.focus); + } + this.activeTasks[task._id] = { terminal, task, promise }; + this.emit(TaskSystemEvents.Changed); + return promise.then((summary) => { + try { + let telemetryEvent: TelemetryEvent = { + trigger: trigger, + runner: 'terminal', + taskKind: Task.getTelemetryKind(task), + command: this.getSanitizedCommand(executedCommand), + success: true, + exitCode: summary.exitCode + }; + this.telemetryService.publicLog(TerminalTaskSystem.TelemetryEventName, telemetryEvent); + } catch (error) { + } + return summary; + }, (error) => { + try { + let telemetryEvent: TelemetryEvent = { + trigger: trigger, + runner: 'terminal', + taskKind: Task.getTelemetryKind(task), + command: this.getSanitizedCommand(executedCommand), + success: false + }; + this.telemetryService.publicLog(TerminalTaskSystem.TelemetryEventName, telemetryEvent); + } catch (error) { + } + return TPromise.wrapError(error); + }); + } + + private createTerminal(task: CustomTask | ContributedTask): [ITerminalInstance, string] { + let options = this.resolveOptions(task.command.options); + let { command, args } = this.resolveCommandAndArgs(task); + let terminalName = nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', task.name); + let waitOnExit: boolean | string = false; + if (task.command.presentation.reveal !== RevealKind.Never || !task.isBackground) { + waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'); + }; + let shellLaunchConfig: IShellLaunchConfig = undefined; + let isShellCommand = task.command.runtime === RuntimeType.Shell; + if (isShellCommand) { + if (Platform.isWindows && ((options.cwd && TPath.isUNC(options.cwd)) || (!options.cwd && TPath.isUNC(process.cwd())))) { + throw new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive.'), TaskErrors.UnknownError); + } + shellLaunchConfig = { name: terminalName, executable: null, args: null, waitOnExit }; + let shellSpecified: boolean = false; + let shellOptions: ShellConfiguration = task.command.options && task.command.options.shell; + if (shellOptions && shellOptions.executable) { + shellLaunchConfig.executable = shellOptions.executable; + shellSpecified = true; + if (shellOptions.args) { + shellLaunchConfig.args = shellOptions.args.slice(); + } else { + shellLaunchConfig.args = []; + } + } else { + this.terminalService.configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig); + } + let shellArgs = shellLaunchConfig.args.slice(0); + let toAdd: string[] = []; + let commandLine = args && args.length > 0 ? `${command} ${args.join(' ')}` : `${command}`; + let windowsShellArgs: boolean = false; + if (Platform.isWindows) { + windowsShellArgs = true; + let basename = path.basename(shellLaunchConfig.executable).toLowerCase(); + if (basename === 'powershell.exe') { + if (!shellSpecified) { + toAdd.push('-Command'); + } + } else if (basename === 'bash.exe') { + windowsShellArgs = false; + if (!shellSpecified) { + toAdd.push('-c'); + } + } else { + if (!shellSpecified) { + toAdd.push('/d', '/c'); + } + } + } else { + if (!shellSpecified) { + toAdd.push('-c'); + } + } + toAdd.forEach(element => { + if (!shellArgs.some(arg => arg.toLowerCase() === element)) { + shellArgs.push(element); + } + }); + shellArgs.push(commandLine); + shellLaunchConfig.args = windowsShellArgs ? shellArgs.join(' ') : shellArgs; + if (task.command.presentation.echo) { + shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${commandLine} <\x1b[0m\n`; + } + } else { + let cwd = options && options.cwd ? options.cwd : process.cwd(); + // On Windows executed process must be described absolute. Since we allowed command without an + // absolute path (e.g. "command": "node") we need to find the executable in the CWD or PATH. + let executable = Platform.isWindows && !isShellCommand ? this.findExecutable(command, cwd) : command; + shellLaunchConfig = { + name: terminalName, + executable: executable, + args, + waitOnExit + }; + if (task.command.presentation.echo) { + let getArgsToEcho = (args: string | string[]): string => { + if (!args || args.length === 0) { + return ''; + } + if (Types.isString(args)) { + return args; + } + return args.join(' '); + }; + shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`; + } + } + if (options.cwd) { + shellLaunchConfig.cwd = options.cwd; + } + if (options.env) { + let env: IStringDictionary = Object.create(null); + Object.keys(process.env).forEach((key) => { + env[key] = process.env[key]; + }); + Object.keys(options.env).forEach((key) => { + env[key] = options.env[key]; + }); + shellLaunchConfig.env = env; + } + let prefersSameTerminal = task.command.presentation.panel === PanelKind.Dedicated; + let allowsSharedTerminal = task.command.presentation.panel === PanelKind.Shared; + + let terminalToReuse: TerminalData; + if (prefersSameTerminal) { + let terminalId = this.sameTaskTerminals[task._id]; + if (terminalId) { + terminalToReuse = this.terminals[terminalId]; + delete this.sameTaskTerminals[task._id]; + } + } else if (allowsSharedTerminal) { + let terminalId = this.idleTaskTerminals.remove(task._id) || this.idleTaskTerminals.shift(); + if (terminalId) { + terminalToReuse = this.terminals[terminalId]; + } + } + if (terminalToReuse) { + terminalToReuse.terminal.reuseTerminal(shellLaunchConfig); + return [terminalToReuse.terminal, command]; + } + + const result = this.terminalService.createInstance(shellLaunchConfig); + const key = result.id.toString(); + result.onDisposed((terminal) => { + let terminalData = this.terminals[key]; + if (terminalData) { + delete this.terminals[key]; + delete this.sameTaskTerminals[terminalData.lastTask]; + this.idleTaskTerminals.delete(terminalData.lastTask); + } + }); + this.terminals[key] = { terminal: result, lastTask: task._id }; + return [result, command]; + } + + private resolveCommandAndArgs(task: CustomTask | ContributedTask): { command: string, args: string[] } { + // First we need to use the command args: + let args: string[] = task.command.args ? task.command.args.slice() : []; + args = this.resolveVariables(args); + let command: string = this.resolveVariable(task.command.name); + return { command, args }; + } + + private findExecutable(command: string, cwd: string): string { + // If we have an absolute path then we take it. + if (path.isAbsolute(command)) { + return command; + } + let dir = path.dirname(command); + if (dir !== '.') { + // We have a directory. So leave the command as is. + return command; + } + // We have a simple file name. We get the path variable from the env + // and try to find the executable on the path. + if (!process.env.PATH) { + return command; + } + let paths: string[] = (process.env.PATH as string).split(path.delimiter); + for (let pathEntry of paths) { + // The path entry is absolute. + let fullPath: string; + if (path.isAbsolute(pathEntry)) { + fullPath = path.join(pathEntry, command); + } else { + fullPath = path.join(cwd, pathEntry, command); + } + if (fs.existsSync(fullPath)) { + return fullPath; + } + let withExtension = fullPath + '.com'; + if (fs.existsSync(withExtension)) { + return withExtension; + } + withExtension = fullPath + '.exe'; + if (fs.existsSync(withExtension)) { + return withExtension; + } + } + return command; + } + + private resolveVariables(value: string[]): string[] { + return value.map(s => this.resolveVariable(s)); + } + + private resolveMatchers(values: (string | ProblemMatcher)[]): ProblemMatcher[] { + if (values === void 0 || values === null || values.length === 0) { + return []; + } + let result: ProblemMatcher[] = []; + values.forEach((value) => { + let matcher: ProblemMatcher; + if (Types.isString(value)) { + if (value[0] === '$') { + matcher = ProblemMatcherRegistry.get(value.substring(1)); + } else { + matcher = ProblemMatcherRegistry.get(value); + } + } else { + matcher = value; + } + if (!matcher) { + this.outputChannel.append(nls.localize('unkownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); + return; + } + if (!matcher.filePrefix) { + result.push(matcher); + } else { + let copy = Objects.clone(matcher); + copy.filePrefix = this.resolveVariable(copy.filePrefix); + result.push(copy); + } + }); + return result; + } + + private resolveVariable(value: string): string { + // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 + return this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, value); + } + + private resolveOptions(options: CommandOptions): CommandOptions { + if (options === void 0 || options === null) { + return { cwd: this.resolveVariable('${cwd}') }; + } + let result: CommandOptions = Types.isString(options.cwd) + ? { cwd: this.resolveVariable(options.cwd) } + : { cwd: this.resolveVariable('${cwd}') }; + if (options.env) { + result.env = Object.create(null); + Object.keys(options.env).forEach((key) => { + let value: any = options.env[key]; + if (Types.isString(value)) { + result.env[key] = this.resolveVariable(value); + } else { + result.env[key] = value.toString(); + } + }); + } + return result; + } + + private registerLinkMatchers(terminal: ITerminalInstance, problemMatchers: ProblemMatcher[]): number[] { + let result: number[] = []; + /* + let handlePattern = (matcher: ProblemMatcher, pattern: ProblemPattern): void => { + if (pattern.regexp instanceof RegExp && Types.isNumber(pattern.file)) { + result.push(terminal.registerLinkMatcher(pattern.regexp, (match: string) => { + let resource: URI = getResource(match, matcher); + if (resource) { + this.workbenchEditorService.openEditor({ + resource: resource + }); + } + }, 0)); + } + }; + + for (let problemMatcher of problemMatchers) { + if (Array.isArray(problemMatcher.pattern)) { + for (let pattern of problemMatcher.pattern) { + handlePattern(problemMatcher, pattern); + } + } else if (problemMatcher.pattern) { + handlePattern(problemMatcher, problemMatcher.pattern); + } + } + */ + return result; + } + + private static doubleQuotes = /^[^"].* .*[^"]$/; + protected ensureDoubleQuotes(value: string) { + if (TerminalTaskSystem.doubleQuotes.test(value)) { + return { + value: '"' + value + '"', + quoted: true + }; + } else { + return { + value: value, + quoted: value.length > 0 && value[0] === '"' && value[value.length - 1] === '"' + }; + } + } + + private static WellKnowCommands: IStringDictionary = { + 'ant': true, + 'cmake': true, + 'eslint': true, + 'gradle': true, + 'grunt': true, + 'gulp': true, + 'jake': true, + 'jenkins': true, + 'jshint': true, + 'make': true, + 'maven': true, + 'msbuild': true, + 'msc': true, + 'nmake': true, + 'npm': true, + 'rake': true, + 'tsc': true, + 'xbuild': true + }; + + public getSanitizedCommand(cmd: string): string { + let result = cmd.toLowerCase(); + let index = result.lastIndexOf(path.sep); + if (index !== -1) { + result = result.substring(index + 1); + } + if (TerminalTaskSystem.WellKnowCommands[result]) { + return result; + } + return 'other'; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts new file mode 100644 index 0000000000..4ca14a0f59 --- /dev/null +++ b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts @@ -0,0 +1,388 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import * as Objects from 'vs/base/common/objects'; +import * as Paths from 'vs/base/common/paths'; +import { TPromise } from 'vs/base/common/winjs.base'; +import Strings = require('vs/base/common/strings'); +import Collections = require('vs/base/common/collections'); + +import { CommandOptions, Source, ErrorData } from 'vs/base/common/processes'; +import { LineProcess } from 'vs/base/node/processes'; + +import { IFileService } from 'vs/platform/files/common/files'; + +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; + +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +import * as Tasks from '../common/tasks'; +import * as TaskConfig from './taskConfiguration'; + +let build: string = 'build'; +let test: string = 'test'; +let defaultValue: string = 'default'; + +interface TaskInfo { + index: number; + exact: number; +} + +interface TaskInfos { + build: TaskInfo; + test: TaskInfo; +} + +interface TaskDetectorMatcher { + init(); + match(tasks: string[], line: string); +} + +interface DetectorConfig { + matcher: TaskDetectorMatcher; + arg: string; +} + +class RegexpTaskMatcher implements TaskDetectorMatcher { + private regexp: RegExp; + + constructor(regExp: RegExp) { + this.regexp = regExp; + } + + init() { + } + + match(tasks: string[], line: string) { + let matches = this.regexp.exec(line); + if (matches && matches.length > 0) { + tasks.push(matches[1]); + } + } +} + +class GruntTaskMatcher implements TaskDetectorMatcher { + private tasksStart: boolean; + private tasksEnd: boolean; + private descriptionOffset: number; + + init() { + this.tasksStart = false; + this.tasksEnd = false; + this.descriptionOffset = null; + } + + match(tasks: string[], line: string) { + // grunt lists tasks as follows (description is wrapped into a new line if too long): + // ... + // Available tasks + // uglify Minify files with UglifyJS. * + // jshint Validate files with JSHint. * + // test Alias for "jshint", "qunit" tasks. + // default Alias for "jshint", "qunit", "concat", "uglify" tasks. + // long Alias for "eslint", "qunit", "browserify", "sass", + // "autoprefixer", "uglify", tasks. + // + // Tasks run in the order specified + if (!this.tasksStart && !this.tasksEnd) { + if (line.indexOf('Available tasks') === 0) { + this.tasksStart = true; + } + } + else if (this.tasksStart && !this.tasksEnd) { + if (line.indexOf('Tasks run in the order specified') === 0) { + this.tasksEnd = true; + } else { + if (this.descriptionOffset === null) { + this.descriptionOffset = line.match(/\S \S/).index + 1; + } + let taskName = line.substr(0, this.descriptionOffset).trim(); + if (taskName.length > 0) { + tasks.push(taskName); + } + } + } + } +} + +export interface DetectorResult { + config: TaskConfig.ExternalTaskRunnerConfiguration; + stdout: string[]; + stderr: string[]; +} + +export class ProcessRunnerDetector { + + private static Version: string = '0.1.0'; + + private static SupportedRunners: Collections.IStringDictionary = { + 'gulp': true, + 'jake': true, + 'grunt': true + }; + + private static TaskMatchers: Collections.IStringDictionary = { + 'gulp': { matcher: new RegexpTaskMatcher(/^(.*)$/), arg: '--tasks-simple' }, + 'jake': { matcher: new RegexpTaskMatcher(/^jake\s+([^\s]+)\s/), arg: '--tasks' }, + 'grunt': { matcher: new GruntTaskMatcher(), arg: '--help' }, + }; + + public static supports(runner: string): boolean { + return ProcessRunnerDetector.SupportedRunners[runner]; + } + + private static detectorConfig(runner: string): DetectorConfig { + return ProcessRunnerDetector.TaskMatchers[runner]; + } + + private static DefaultProblemMatchers: string[] = ['$lessCompile', '$tsc', '$jshint']; + + private fileService: IFileService; + private contextService: IWorkspaceContextService; + private configurationResolverService: IConfigurationResolverService; + private taskConfiguration: TaskConfig.ExternalTaskRunnerConfiguration; + private _stderr: string[]; + private _stdout: string[]; + private _cwd: string; + + constructor(fileService: IFileService, contextService: IWorkspaceContextService, configurationResolverService: IConfigurationResolverService, config: TaskConfig.ExternalTaskRunnerConfiguration = null) { + this.fileService = fileService; + this.contextService = contextService; + this.configurationResolverService = configurationResolverService; + this.taskConfiguration = config; + this._stderr = []; + this._stdout = []; + this._cwd = this.contextService.hasWorkspace() ? Paths.normalize(this.contextService.getLegacyWorkspace().resource.fsPath, true) : ''; + } + + public get stderr(): string[] { + return this._stderr; + } + + public get stdout(): string[] { + return this._stdout; + } + + public detect(list: boolean = false, detectSpecific?: string): TPromise { + if (this.taskConfiguration && this.taskConfiguration.command && ProcessRunnerDetector.supports(this.taskConfiguration.command)) { + let config = ProcessRunnerDetector.detectorConfig(this.taskConfiguration.command); + let args = (this.taskConfiguration.args || []).concat(config.arg); + let options: CommandOptions = this.taskConfiguration.options ? this.resolveCommandOptions(this.taskConfiguration.options) : { cwd: this._cwd }; + let isShellCommand = !!this.taskConfiguration.isShellCommand; + // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 + return this.runDetection( + new LineProcess(this.taskConfiguration.command, this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, args), isShellCommand, options), + this.taskConfiguration.command, isShellCommand, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); + } else { + if (detectSpecific) { + let detectorPromise: TPromise; + if ('gulp' === detectSpecific) { + detectorPromise = this.tryDetectGulp(list); + } else if ('jake' === detectSpecific) { + detectorPromise = this.tryDetectJake(list); + } else if ('grunt' === detectSpecific) { + detectorPromise = this.tryDetectGrunt(list); + } + return detectorPromise.then((value) => { + if (value) { + return value; + } else { + return { config: null, stdout: this.stdout, stderr: this.stderr }; + } + }); + } else { + return this.tryDetectGulp(list).then((value) => { + if (value) { + return value; + } + return this.tryDetectJake(list).then((value) => { + if (value) { + return value; + } + return this.tryDetectGrunt(list).then((value) => { + if (value) { + return value; + } + return { config: null, stdout: this.stdout, stderr: this.stderr }; + }); + }); + }); + } + } + } + + private resolveCommandOptions(options: CommandOptions): CommandOptions { + // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 + let result = Objects.clone(options); + if (result.cwd) { + result.cwd = this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, result.cwd); + } + if (result.env) { + result.env = this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, result.env); + } + return result; + } + + private tryDetectGulp(list: boolean): TPromise { + return this.fileService.resolveFile(this.contextService.toResource('gulpfile.js')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + let config = ProcessRunnerDetector.detectorConfig('gulp'); + let process = new LineProcess('gulp', [config.arg, '--no-color'], true, { cwd: this._cwd }); + return this.runDetection(process, 'gulp', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); + }, (err: any) => { + return null; + }); + } + + private tryDetectGrunt(list: boolean): TPromise { + return this.fileService.resolveFile(this.contextService.toResource('Gruntfile.js')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + let config = ProcessRunnerDetector.detectorConfig('grunt'); + let process = new LineProcess('grunt', [config.arg, '--no-color'], true, { cwd: this._cwd }); + return this.runDetection(process, 'grunt', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); + }, (err: any) => { + return null; + }); + } + + private tryDetectJake(list: boolean): TPromise { + let run = () => { + let config = ProcessRunnerDetector.detectorConfig('jake'); + let process = new LineProcess('jake', [config.arg], true, { cwd: this._cwd }); + return this.runDetection(process, 'jake', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); + }; + return this.fileService.resolveFile(this.contextService.toResource('Jakefile')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + return run(); + }, (err: any) => { + return this.fileService.resolveFile(this.contextService.toResource('Jakefile.js')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + return run(); + }, (err: any) => { + return null; + }); + }); + } + + private runDetection(process: LineProcess, command: string, isShellCommand: boolean, matcher: TaskDetectorMatcher, problemMatchers: string[], list: boolean): TPromise { + let tasks: string[] = []; + matcher.init(); + return process.start().then((success) => { + if (tasks.length === 0) { + if (success.cmdCode !== 0) { + if (command === 'gulp') { + this._stderr.push(nls.localize('TaskSystemDetector.noGulpTasks', 'Running gulp --tasks-simple didn\'t list any tasks. Did you run npm install?')); + } else if (command === 'jake') { + this._stderr.push(nls.localize('TaskSystemDetector.noJakeTasks', 'Running jake --tasks didn\'t list any tasks. Did you run npm install?')); + } + } + return { config: null, stdout: this._stdout, stderr: this._stderr }; + } + let result: TaskConfig.ExternalTaskRunnerConfiguration = { + version: ProcessRunnerDetector.Version, + command: command, + isShellCommand: isShellCommand + }; + // Hack. We need to remove this. + if (command === 'gulp') { + result.args = ['--no-color']; + } + result.tasks = this.createTaskDescriptions(tasks, problemMatchers, list); + return { config: result, stdout: this._stdout, stderr: this._stderr }; + }, (err: ErrorData) => { + let error = err.error; + if ((error).code === 'ENOENT') { + if (command === 'gulp') { + this._stderr.push(nls.localize('TaskSystemDetector.noGulpProgram', 'Gulp is not installed on your system. Run npm install -g gulp to install it.')); + } else if (command === 'jake') { + this._stderr.push(nls.localize('TaskSystemDetector.noJakeProgram', 'Jake is not installed on your system. Run npm install -g jake to install it.')); + } else if (command === 'grunt') { + this._stderr.push(nls.localize('TaskSystemDetector.noGruntProgram', 'Grunt is not installed on your system. Run npm install -g grunt to install it.')); + } + } else { + this._stderr.push(nls.localize('TaskSystemDetector.noProgram', 'Program {0} was not found. Message is {1}', command, error.message)); + } + return { config: null, stdout: this._stdout, stderr: this._stderr }; + }, (progress) => { + if (progress.source === Source.stderr) { + this._stderr.push(progress.line); + return; + } + let line = Strings.removeAnsiEscapeCodes(progress.line); + let matches = matcher.match(tasks, line); + if (matches && matches.length > 0) { + tasks.push(matches[1]); + } + }); + } + + private createTaskDescriptions(tasks: string[], problemMatchers: string[], list: boolean): TaskConfig.CustomTask[] { + let taskConfigs: TaskConfig.CustomTask[] = []; + if (list) { + tasks.forEach((task) => { + taskConfigs.push({ + taskName: task, + args: [] + }); + }); + } else { + let taskInfos: TaskInfos = { + build: { index: -1, exact: -1 }, + test: { index: -1, exact: -1 } + }; + tasks.forEach((task, index) => { + this.testBuild(taskInfos.build, task, index); + this.testTest(taskInfos.test, task, index); + }); + if (taskInfos.build.index !== -1) { + let name = tasks[taskInfos.build.index]; + this._stdout.push(nls.localize('TaskSystemDetector.buildTaskDetected', 'Build task named \'{0}\' detected.', name)); + taskConfigs.push({ + taskName: name, + args: [], + group: Tasks.TaskGroup.Build, + problemMatcher: problemMatchers + }); + } + if (taskInfos.test.index !== -1) { + let name = tasks[taskInfos.test.index]; + this._stdout.push(nls.localize('TaskSystemDetector.testTaskDetected', 'Test task named \'{0}\' detected.', name)); + taskConfigs.push({ + taskName: name, + args: [], + group: Tasks.TaskGroup.Test, + }); + } + } + return taskConfigs; + } + + private testBuild(taskInfo: TaskInfo, taskName: string, index: number): void { + if (taskName === build) { + taskInfo.index = index; + taskInfo.exact = 4; + } else if ((Strings.startsWith(taskName, build) || Strings.endsWith(taskName, build)) && taskInfo.exact < 4) { + taskInfo.index = index; + taskInfo.exact = 3; + } else if (taskName.indexOf(build) !== -1 && taskInfo.exact < 3) { + taskInfo.index = index; + taskInfo.exact = 2; + } else if (taskName === defaultValue && taskInfo.exact < 2) { + taskInfo.index = index; + taskInfo.exact = 1; + } + } + + private testTest(taskInfo: TaskInfo, taskName: string, index: number): void { + if (taskName === test) { + taskInfo.index = index; + taskInfo.exact = 3; + } else if ((Strings.startsWith(taskName, test) || Strings.endsWith(taskName, test)) && taskInfo.exact < 3) { + taskInfo.index = index; + taskInfo.exact = 2; + } else if (taskName.indexOf(test) !== -1 && taskInfo.exact < 2) { + taskInfo.index = index; + taskInfo.exact = 1; + } + } +} diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts new file mode 100644 index 0000000000..856bf53446 --- /dev/null +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -0,0 +1,399 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import * as Objects from 'vs/base/common/objects'; +import * as Types from 'vs/base/common/types'; +import * as Platform from 'vs/base/common/platform'; +import { TPromise, Promise } from 'vs/base/common/winjs.base'; +import * as Async from 'vs/base/common/async'; +import Severity from 'vs/base/common/severity'; +import * as Strings from 'vs/base/common/strings'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; + +import { SuccessData, ErrorData } from 'vs/base/common/processes'; +import { LineProcess, LineData } from 'vs/base/node/processes'; + +import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common/output'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; + +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ProblemMatcher, ProblemMatcherRegistry } from 'vs/platform/markers/common/problemMatcher'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors'; +import { + ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TelemetryEvent, Triggers, + TaskSystemEvents, TaskEvent, TaskType, TaskTerminateResponse +} from 'vs/workbench/parts/tasks/common/taskSystem'; +import { Task, CustomTask, CommandOptions, RevealKind, CommandConfiguration, RuntimeType } from 'vs/workbench/parts/tasks/common/tasks'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; + +export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { + + public static TelemetryEventName: string = 'taskService'; + + private markerService: IMarkerService; + private modelService: IModelService; + private outputService: IOutputService; + private telemetryService: ITelemetryService; + private configurationResolverService: IConfigurationResolverService; + private contextService: IWorkspaceContextService; + + private outputChannel: IOutputChannel; + + private errorsShown: boolean; + private childProcess: LineProcess; + private activeTask: CustomTask; + private activeTaskPromise: TPromise; + + constructor(markerService: IMarkerService, modelService: IModelService, telemetryService: ITelemetryService, + outputService: IOutputService, configurationResolverService: IConfigurationResolverService, contextService: IWorkspaceContextService, outputChannelId: string) { + super(); + this.markerService = markerService; + this.modelService = modelService; + this.outputService = outputService; + this.contextService = contextService; + this.telemetryService = telemetryService; + this.configurationResolverService = configurationResolverService; + + this.childProcess = null; + this.activeTask = null; + this.activeTaskPromise = null; + this.outputChannel = this.outputService.getChannel(outputChannelId); + this.errorsShown = true; + } + + public isActive(): TPromise { + return TPromise.as(!!this.childProcess); + } + + public isActiveSync(): boolean { + return !!this.childProcess; + } + + public getActiveTasks(): Task[] { + let result: Task[] = []; + if (this.activeTask) { + result.push(this.activeTask); + } + return result; + } + + public run(task: Task): ITaskExecuteResult { + if (this.activeTask) { + return { kind: TaskExecuteKind.Active, active: { same: this.activeTask._id === task._id, background: this.activeTask.isBackground }, promise: this.activeTaskPromise }; + } + return this.executeTask(task); + } + + public revealTask(task: Task): boolean { + this.showOutput(); + return true; + } + + public hasErrors(value: boolean): void { + this.errorsShown = !value; + } + + public canAutoTerminate(): boolean { + if (this.childProcess) { + if (this.activeTask) { + return !this.activeTask.promptOnClose; + } + return false; + } + return true; + } + + public terminate(_id: string): TPromise { + if (!this.activeTask || this.activeTask._id !== _id) { + return TPromise.as({ success: false, task: undefined }); + } + return this.terminateAll()[0]; + } + + public terminateAll(): TPromise { + if (this.childProcess) { + let task = this.activeTask; + return this.childProcess.terminate().then((response) => { + let result: TaskTerminateResponse = Objects.assign({ task: task }, response); + let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group }; + this.emit(TaskSystemEvents.Terminated, event); + return [result]; + }); + } + return TPromise.as([{ success: true, task: undefined }]); + } + + private executeTask(task: Task, trigger: string = Triggers.command): ITaskExecuteResult { + if (!CustomTask.is(task)) { + throw new Error('The process task system can only execute custom tasks.'); + } + let telemetryEvent: TelemetryEvent = { + trigger: trigger, + runner: 'output', + taskKind: Task.getTelemetryKind(task), + command: 'other', + success: true + }; + try { + let result = this.doExecuteTask(task, telemetryEvent); + result.promise = result.promise.then((success) => { + this.telemetryService.publicLog(ProcessTaskSystem.TelemetryEventName, telemetryEvent); + return success; + }, (err: any) => { + telemetryEvent.success = false; + this.telemetryService.publicLog(ProcessTaskSystem.TelemetryEventName, telemetryEvent); + return TPromise.wrapError(err); + }); + return result; + } catch (err) { + telemetryEvent.success = false; + this.telemetryService.publicLog(ProcessTaskSystem.TelemetryEventName, telemetryEvent); + if (err instanceof TaskError) { + throw err; + } else if (err instanceof Error) { + let error = err; + this.outputChannel.append(error.message); + throw new TaskError(Severity.Error, error.message, TaskErrors.UnknownError); + } else { + this.outputChannel.append(err.toString()); + throw new TaskError(Severity.Error, nls.localize('TaskRunnerSystem.unknownError', 'A unknown error has occurred while executing a task. See task output log for details.'), TaskErrors.UnknownError); + } + } + } + + private doExecuteTask(task: CustomTask, telemetryEvent: TelemetryEvent): ITaskExecuteResult { + let taskSummary: ITaskSummary = {}; + let commandConfig: CommandConfiguration = task.command; + if (!this.errorsShown) { + this.showOutput(); + this.errorsShown = true; + } else { + this.clearOutput(); + } + + let args: string[] = commandConfig.args ? commandConfig.args.slice() : []; + args = this.resolveVariables(args); + let command: string = this.resolveVariable(commandConfig.name); + this.childProcess = new LineProcess(command, args, commandConfig.runtime === RuntimeType.Shell, this.resolveOptions(commandConfig.options)); + telemetryEvent.command = this.childProcess.getSanitizedCommand(); + // we have no problem matchers defined. So show the output log + let reveal = task.command.presentation.reveal; + if (reveal === RevealKind.Always || (reveal === RevealKind.Silent && task.problemMatchers.length === 0)) { + this.showOutput(); + } + + if (commandConfig.presentation.echo) { + let prompt: string = Platform.isWindows ? '>' : '$'; + this.log(`running command${prompt} ${command} ${args.join(' ')}`); + } + if (task.isBackground) { + let watchingProblemMatcher = new WatchingProblemCollector(this.resolveMatchers(task.problemMatchers), this.markerService, this.modelService); + let toUnbind: IDisposable[] = []; + let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.Watching, group: task.group }; + let eventCounter: number = 0; + toUnbind.push(watchingProblemMatcher.addListener(ProblemCollectorEvents.WatchingBeginDetected, () => { + eventCounter++; + this.emit(TaskSystemEvents.Active, event); + })); + toUnbind.push(watchingProblemMatcher.addListener(ProblemCollectorEvents.WatchingEndDetected, () => { + eventCounter--; + this.emit(TaskSystemEvents.Inactive, event); + })); + watchingProblemMatcher.aboutToStart(); + let delayer: Async.Delayer = null; + this.activeTask = task; + this.activeTaskPromise = this.childProcess.start().then((success): ITaskSummary => { + this.childProcessEnded(); + watchingProblemMatcher.dispose(); + toUnbind = dispose(toUnbind); + toUnbind = null; + for (let i = 0; i < eventCounter; i++) { + this.emit(TaskSystemEvents.Inactive, event); + } + eventCounter = 0; + if (!this.checkTerminated(task, success)) { + this.log(nls.localize('TaskRunnerSystem.watchingBuildTaskFinished', '\nWatching build tasks has finished.')); + } + if (success.cmdCode && success.cmdCode === 1 && watchingProblemMatcher.numberOfMatches === 0 && reveal !== RevealKind.Never) { + this.showOutput(); + } + taskSummary.exitCode = success.cmdCode; + return taskSummary; + }, (error: ErrorData) => { + this.childProcessEnded(); + watchingProblemMatcher.dispose(); + toUnbind = dispose(toUnbind); + toUnbind = null; + for (let i = 0; i < eventCounter; i++) { + this.emit(TaskSystemEvents.Inactive, event); + } + eventCounter = 0; + return this.handleError(task, error); + }, (progress: LineData) => { + let line = Strings.removeAnsiEscapeCodes(progress.line); + this.outputChannel.append(line + '\n'); + watchingProblemMatcher.processLine(line); + if (delayer === null) { + delayer = new Async.Delayer(3000); + } + delayer.trigger(() => { + watchingProblemMatcher.forceDelivery(); + return null; + }).then(() => { + delayer = null; + }); + }); + let result: ITaskExecuteResult = (task).tscWatch + ? { kind: TaskExecuteKind.Started, started: { restartOnFileChanges: '**/*.ts' }, promise: this.activeTaskPromise } + : { kind: TaskExecuteKind.Started, started: {}, promise: this.activeTaskPromise }; + return result; + } else { + let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group }; + this.emit(TaskSystemEvents.Active, event); + let startStopProblemMatcher = new StartStopProblemCollector(this.resolveMatchers(task.problemMatchers), this.markerService, this.modelService); + this.activeTask = task; + this.activeTaskPromise = this.childProcess.start().then((success): ITaskSummary => { + this.childProcessEnded(); + startStopProblemMatcher.done(); + startStopProblemMatcher.dispose(); + this.checkTerminated(task, success); + this.emit(TaskSystemEvents.Inactive, event); + if (success.cmdCode && success.cmdCode === 1 && startStopProblemMatcher.numberOfMatches === 0 && reveal !== RevealKind.Never) { + this.showOutput(); + } + taskSummary.exitCode = success.cmdCode; + return taskSummary; + }, (error: ErrorData) => { + this.childProcessEnded(); + startStopProblemMatcher.dispose(); + this.emit(TaskSystemEvents.Inactive, event); + return this.handleError(task, error); + }, (progress) => { + let line = Strings.removeAnsiEscapeCodes(progress.line); + this.outputChannel.append(line + '\n'); + startStopProblemMatcher.processLine(line); + }); + return { kind: TaskExecuteKind.Started, started: {}, promise: this.activeTaskPromise }; + } + } + + private childProcessEnded(): void { + this.childProcess = null; + this.activeTask = null; + this.activeTaskPromise = null; + } + + private handleError(task: CustomTask, errorData: ErrorData): Promise { + let makeVisible = false; + if (errorData.error && !errorData.terminated) { + let args: string = task.command.args ? task.command.args.join(' ') : ''; + this.log(nls.localize('TaskRunnerSystem.childProcessError', 'Failed to launch external program {0} {1}.', task.command.name, args)); + this.outputChannel.append(errorData.error.message); + makeVisible = true; + } + + if (errorData.stdout) { + this.outputChannel.append(errorData.stdout); + makeVisible = true; + } + if (errorData.stderr) { + this.outputChannel.append(errorData.stderr); + makeVisible = true; + } + makeVisible = this.checkTerminated(task, errorData) || makeVisible; + if (makeVisible) { + this.showOutput(); + } + + const error: Error & ErrorData = errorData.error || new Error(); + error.stderr = errorData.stderr; + error.stdout = errorData.stdout; + error.terminated = errorData.terminated; + return TPromise.wrapError(error); + } + + private checkTerminated(task: Task, data: SuccessData | ErrorData): boolean { + if (data.terminated) { + this.log(nls.localize('TaskRunnerSystem.cancelRequested', '\nThe task \'{0}\' was terminated per user request.', task.name)); + return true; + } + return false; + } + + private resolveOptions(options: CommandOptions): CommandOptions { + let result: CommandOptions = { cwd: this.resolveVariable(options.cwd) }; + if (options.env) { + result.env = Object.create(null); + Object.keys(options.env).forEach((key) => { + let value: any = options.env[key]; + if (Types.isString(value)) { + result.env[key] = this.resolveVariable(value); + } else { + result.env[key] = value.toString(); + } + }); + } + return result; + } + + private resolveVariables(value: string[]): string[] { + return value.map(s => this.resolveVariable(s)); + } + + private resolveMatchers(values: (string | ProblemMatcher)[]): ProblemMatcher[] { + if (values === void 0 || values === null || values.length === 0) { + return []; + } + let result: ProblemMatcher[] = []; + values.forEach((value) => { + let matcher: ProblemMatcher; + if (Types.isString(value)) { + if (value[0] === '$') { + matcher = ProblemMatcherRegistry.get(value.substring(1)); + } else { + matcher = ProblemMatcherRegistry.get(value); + } + } else { + matcher = value; + } + if (!matcher) { + this.outputChannel.append(nls.localize('unkownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); + return; + } + if (!matcher.filePrefix) { + result.push(matcher); + } else { + let copy = Objects.clone(matcher); + copy.filePrefix = this.resolveVariable(copy.filePrefix); + result.push(copy); + } + }); + return result; + } + + private resolveVariable(value: string): string { + // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 + return this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, value); + } + + public log(value: string): void { + this.outputChannel.append(value + '\n'); + } + + private showOutput(): void { + this.outputChannel.show(true); + } + + private clearOutput(): void { + this.outputChannel.clear(); + } +} diff --git a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts new file mode 100644 index 0000000000..300d436bff --- /dev/null +++ b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts @@ -0,0 +1,1909 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as crypto from 'crypto'; + +import nls = require('vs/nls'); + +import * as Objects from 'vs/base/common/objects'; +import { IStringDictionary } from 'vs/base/common/collections'; +import * as Platform from 'vs/base/common/platform'; +import * as Types from 'vs/base/common/types'; +import * as UUID from 'vs/base/common/uuid'; + +import { ValidationStatus, IProblemReporter as IProblemReporterBase, NullProblemReporter as NullProblemReporterBase } from 'vs/base/common/parsers'; +import { + NamedProblemMatcher, ProblemMatcher, ProblemMatcherParser, Config as ProblemMatcherConfig, + isNamedProblemMatcher, ProblemMatcherRegistry +} from 'vs/platform/markers/common/problemMatcher'; + +import * as Tasks from '../common/tasks'; +import { TaskDefinitionRegistry } from '../common/taskDefinitionRegistry'; + +/** + * Defines the problem handling strategy + */ +export class ProblemHandling { + /** + * Cleans all problems for the owner defined in the + * error pattern. + */ + public static clean: string = 'cleanMatcherMatchers'; +} + +export interface ShellConfiguration { + executable: string; + args?: string[]; +} + +export interface CommandOptions { + /** + * The current working directory of the executed program or shell. + * If omitted VSCode's current workspace root is used. + */ + cwd?: string; + + /** + * The additional environment of the executed program or shell. If omitted + * the parent process' environment is used. + */ + env?: IStringDictionary; + + /** + * The shell configuration; + */ + shell?: ShellConfiguration; +} + +export interface PresentationOptions { + /** + * Controls whether the terminal executing a task is brought to front or not. + * Defaults to `RevealKind.Always`. + */ + reveal?: string; + + /** + * Controls whether the executed command is printed to the output window or terminal as well. + */ + echo?: boolean; + + /** + * Controls whether the terminal is focus when this task is executed + */ + focus?: boolean; + + /** + * Controls whether the task runs in a new terminal + */ + panel?: string; +} + +export interface TaskIdentifier { + type?: string; +} + +export interface LegacyTaskProperties { + /** + * @deprecated Use `isBackground` instead. + * Whether the executed command is kept alive and is watching the file system. + */ + isWatching?: boolean; + + /** + * @deprecated Use `group` instead. + * Whether this task maps to the default build command. + */ + isBuildCommand?: boolean; + + /** + * @deprecated Use `group` instead. + * Whether this task maps to the default test command. + */ + isTestCommand?: boolean; +} + +export interface LegacyCommandProperties { + + /** + * Whether this is a shell or process + */ + type?: string; + + /** + * @deprecated Use presentation options + * Controls whether the output view of the running tasks is brought to front or not. + * See BaseTaskRunnerConfiguration#showOutput for details. + */ + showOutput?: string; + + /** + * @deprecated Use presentation options + * Controls whether the executed command is printed to the output windows as well. + */ + echoCommand?: boolean; + + /** + * @deprecated Use presentation instead + */ + terminal?: PresentationOptions; + + /** + * @deprecated Use inline commands. + * See BaseTaskRunnerConfiguration#suppressTaskName for details. + */ + suppressTaskName?: boolean; + + /** + * Some commands require that the task argument is highlighted with a special + * prefix (e.g. /t: for msbuild). This property can be used to control such + * a prefix. + */ + taskSelector?: string; + + /** + * @deprecated use the task type instead. + * Specifies whether the command is a shell command and therefore must + * be executed in a shell interpreter (e.g. cmd.exe, bash, ...). + * + * Defaults to false if omitted. + */ + isShellCommand?: boolean | ShellConfiguration; +} + +export interface BaseCommandProperties { + + /** + * Whether the task is a shell task or a process task. + */ + runtime?: string; + + /** + * The command to be executed. Can be an external program or a shell + * command. + */ + command?: string; + + /** + * The command options used when the command is executed. Can be omitted. + */ + options?: CommandOptions; + + /** + * The arguments passed to the command or additional arguments passed to the + * command when using a global command. + */ + args?: string[]; +} + + +export interface CommandProperties extends BaseCommandProperties { + + /** + * Windows specific command properties + */ + windows?: BaseCommandProperties; + + /** + * OSX specific command properties + */ + osx?: BaseCommandProperties; + + /** + * linux specific command properties + */ + linux?: BaseCommandProperties; +} + +export interface GroupKind { + kind?: string; + isDefault?: boolean; +} + +export interface ConfigurationProperties { + /** + * The task's name + */ + taskName?: string; + + /** + * The UI label used for the task. + */ + label?: string; + + /** + * An optional indentifier which can be used to reference a task + * in a dependsOn or other attributes. + */ + identifier?: string; + + /** + * Whether the executed command is kept alive and runs in the background. + */ + isBackground?: boolean; + + /** + * Whether the task should prompt on close for confirmation if running. + */ + promptOnClose?: boolean; + + /** + * Defines the group the task belongs too. + */ + group?: string | GroupKind; + + /** + * The other tasks the task depend on + */ + dependsOn?: string | string[]; + + /** + * Controls the behavior of the used terminal + */ + presentation?: PresentationOptions; + + /** + * The problem matcher(s) to use to capture problems in the tasks + * output. + */ + problemMatcher?: ProblemMatcherConfig.ProblemMatcherType; +} + +export interface CustomTask extends CommandProperties, ConfigurationProperties { + /** + * Custom tasks have the type 'custom' + */ + type?: string; + +} + +export interface ConfiguringTask extends ConfigurationProperties { + /** + * The contributed type of the task + */ + type?: string; +} + +/** + * The base task runner configuration + */ +export interface BaseTaskRunnerConfiguration { + + /** + * The command to be executed. Can be an external program or a shell + * command. + */ + command?: string; + + /** + * @deprecated Use type instead + * + * Specifies whether the command is a shell command and therefore must + * be executed in a shell interpreter (e.g. cmd.exe, bash, ...). + * + * Defaults to false if omitted. + */ + isShellCommand?: boolean; + + /** + * The task type + */ + type?: string; + + /** + * The command options used when the command is executed. Can be omitted. + */ + options?: CommandOptions; + + /** + * The arguments passed to the command. Can be omitted. + */ + args?: string[]; + + /** + * Controls whether the output view of the running tasks is brought to front or not. + * Valid values are: + * "always": bring the output window always to front when a task is executed. + * "silent": only bring it to front if no problem matcher is defined for the task executed. + * "never": never bring the output window to front. + * + * If omitted "always" is used. + */ + showOutput?: string; + + /** + * Controls whether the executed command is printed to the output windows as well. + */ + echoCommand?: boolean; + + /** + * The group + */ + group?: string | GroupKind; + /** + * Controls the behavior of the used terminal + */ + presentation?: PresentationOptions; + + /** + * If set to false the task name is added as an additional argument to the + * command when executed. If set to true the task name is suppressed. If + * omitted false is used. + */ + suppressTaskName?: boolean; + + /** + * Some commands require that the task argument is highlighted with a special + * prefix (e.g. /t: for msbuild). This property can be used to control such + * a prefix. + */ + taskSelector?: string; + + /** + * The problem matcher(s) to used if a global command is exucuted (e.g. no tasks + * are defined). A tasks.json file can either contain a global problemMatcher + * property or a tasks property but not both. + */ + problemMatcher?: ProblemMatcherConfig.ProblemMatcherType; + + /** + * @deprecated Use `isBackground` instead. + * + * Specifies whether a global command is a watching the filesystem. A task.json + * file can either contain a global isWatching property or a tasks property + * but not both. + */ + isWatching?: boolean; + + /** + * Specifies whether a global command is a background task. + */ + isBackground?: boolean; + + /** + * Whether the task should prompt on close for confirmation if running. + */ + promptOnClose?: boolean; + + /** + * The configuration of the available tasks. A tasks.json file can either + * contain a global problemMatcher property or a tasks property but not both. + */ + tasks?: (CustomTask | ConfiguringTask)[]; + + /** + * Problem matcher declarations + */ + declares?: ProblemMatcherConfig.NamedProblemMatcher[]; +} + +/** + * A configuration of an external build system. BuildConfiguration.buildSystem + * must be set to 'program' + */ +export interface ExternalTaskRunnerConfiguration extends BaseTaskRunnerConfiguration { + + _runner?: string; + + /** + * Determines the runner to use + */ + runner?: string; + + /** + * The config's version number + */ + version: string; + + /** + * Windows specific task configuration + */ + windows?: BaseTaskRunnerConfiguration; + + /** + * Mac specific task configuration + */ + osx?: BaseTaskRunnerConfiguration; + + /** + * Linux speciif task configuration + */ + linux?: BaseTaskRunnerConfiguration; +} + +enum ProblemMatcherKind { + Unknown, + String, + ProblemMatcher, + Array +} + +const EMPTY_ARRAY: any[] = []; +Object.freeze(EMPTY_ARRAY); + +function assignProperty(target: T, source: Partial, key: K) { + if (source[key] !== void 0) { + target[key] = source[key]; + } +} + +function fillProperty(target: T, source: Partial, key: K) { + if (target[key] === void 0 && source[key] !== void 0) { + target[key] = source[key]; + } +} + + +interface ParserType { + isEmpty(value: T): boolean; + assignProperties(target: T, source: T): T; + fillProperties(target: T, source: T): T; + fillDefaults(value: T, context: ParseContext): T; + freeze(value: T): Readonly; +} + +interface MetaData { + property: keyof T; + type?: ParserType; +} + + +function _isEmpty(this: void, value: T, properties: MetaData[]): boolean { + if (value === void 0 || value === null) { + return true; + } + for (let meta of properties) { + let property = value[meta.property]; + if (property !== void 0 && property !== null) { + if (meta.type !== void 0 && !meta.type.isEmpty(property)) { + return false; + } else if (!Array.isArray(property) || property.length > 0) { + return false; + } + } + } + return true; +} + +function _assignProperties(this: void, target: T, source: T, properties: MetaData[]): T { + if (_isEmpty(source, properties)) { + return target; + } + if (_isEmpty(target, properties)) { + return source; + } + for (let meta of properties) { + let property = meta.property; + let value: any; + if (meta.type !== void 0) { + value = meta.type.assignProperties(target[property], source[property]); + } else { + value = source[property]; + } + if (value !== void 0 && value !== null) { + target[property] = value; + } + } + return target; +} + +function _fillProperties(this: void, target: T, source: T, properties: MetaData[]): T { + if (_isEmpty(source, properties)) { + return target; + } + if (_isEmpty(target, properties)) { + return source; + } + for (let meta of properties) { + let property = meta.property; + let value: any; + if (meta.type) { + value = meta.type.fillProperties(target[property], source[property]); + } else if (target[property] === void 0) { + value = source[property]; + } + if (value !== void 0 && value !== null) { + target[property] = value; + } + } + return target; +} + +function _fillDefaults(this: void, target: T, defaults: T, properties: MetaData[], context: ParseContext): T { + if (target && Object.isFrozen(target)) { + return target; + } + if (target === void 0 || target === null) { + if (defaults !== void 0 && defaults !== null) { + return Objects.deepClone(defaults); + } else { + return undefined; + } + } + for (let meta of properties) { + let property = meta.property; + if (target[property] !== void 0) { + continue; + } + let value: any; + if (meta.type) { + value = meta.type.fillDefaults(target[property], context); + } else { + value = defaults[property]; + } + + if (value !== void 0 && value !== null) { + target[property] = value; + } + } + return target; +} + +function _freeze(this: void, target: T, properties: MetaData[]): Readonly { + if (target === void 0 || target === null) { + return undefined; + } + if (Object.isFrozen(target)) { + return target; + } + for (let meta of properties) { + if (meta.type) { + let value = target[meta.property]; + if (value) { + meta.type.freeze(value); + } + } + } + Object.freeze(target); + return target; +} + +interface ParseContext { + problemReporter: IProblemReporter; + namedProblemMatchers: IStringDictionary; + uuidMap: UUIDMap; + engine: Tasks.ExecutionEngine; + schemaVersion: Tasks.JsonSchemaVersion; +} + + +namespace ShellConfiguration { + + const properties: MetaData[] = [{ property: 'executable' }, { property: 'args' }]; + + export function is(value: any): value is ShellConfiguration { + let candidate: ShellConfiguration = value; + return candidate && Types.isString(candidate.executable) && (candidate.args === void 0 || Types.isStringArray(candidate.args)); + } + + export function from(this: void, config: ShellConfiguration, context: ParseContext): Tasks.ShellConfiguration { + if (!is(config)) { + return undefined; + } + let result: ShellConfiguration = { executable: config.executable }; + if (config.args !== void 0) { + result.args = config.args.slice(); + } + return result; + } + + export function isEmpty(this: void, value: Tasks.ShellConfiguration): boolean { + return _isEmpty(value, properties); + } + + export function assignProperties(this: void, target: Tasks.ShellConfiguration, source: Tasks.ShellConfiguration): Tasks.ShellConfiguration { + return _assignProperties(target, source, properties); + } + + export function fillProperties(this: void, target: Tasks.ShellConfiguration, source: Tasks.ShellConfiguration): Tasks.ShellConfiguration { + return _fillProperties(target, source, properties); + } + + export function fillDefaults(this: void, value: Tasks.ShellConfiguration, context: ParseContext): Tasks.ShellConfiguration { + return value; + } + + export function freeze(this: void, value: Tasks.ShellConfiguration): Readonly { + if (!value) { + return undefined; + } + return Object.freeze(value); + } +} + +namespace CommandOptions { + + const properties: MetaData[] = [{ property: 'cwd' }, { property: 'env' }, { property: 'shell', type: ShellConfiguration }]; + const defaults: CommandOptions = { cwd: '${workspaceRoot}' }; + + export function from(this: void, options: CommandOptions, context: ParseContext): Tasks.CommandOptions { + let result: Tasks.CommandOptions = {}; + if (options.cwd !== void 0) { + if (Types.isString(options.cwd)) { + result.cwd = options.cwd; + } else { + context.problemReporter.warn(nls.localize('ConfigurationParser.invalidCWD', 'Warning: options.cwd must be of type string. Ignoring value {0}\n', options.cwd)); + } + } + if (options.env !== void 0) { + result.env = Objects.clone(options.env); + } + result.shell = ShellConfiguration.from(options.shell, context); + return isEmpty(result) ? undefined : result; + } + + export function isEmpty(value: Tasks.CommandOptions): boolean { + return _isEmpty(value, properties); + } + + export function assignProperties(target: Tasks.CommandOptions, source: Tasks.CommandOptions): Tasks.CommandOptions { + if (isEmpty(source)) { + return target; + } + if (isEmpty(target)) { + return source; + } + assignProperty(target, source, 'cwd'); + if (target.env === void 0) { + target.env = source.env; + } else if (source.env !== void 0) { + let env: { [key: string]: string; } = Object.create(null); + Object.keys(target.env).forEach(key => env[key] = target.env[key]); + Object.keys(source.env).forEach(key => env[key] = source.env[key]); + target.env = env; + } + target.shell = ShellConfiguration.assignProperties(target.shell, source.shell); + return target; + } + + export function fillProperties(target: Tasks.CommandOptions, source: Tasks.CommandOptions): Tasks.CommandOptions { + return _fillProperties(target, source, properties); + } + + export function fillDefaults(value: Tasks.CommandOptions, context: ParseContext): Tasks.CommandOptions { + return _fillDefaults(value, defaults, properties, context); + } + + export function freeze(value: Tasks.CommandOptions): Readonly { + return _freeze(value, properties); + } +} + +namespace CommandConfiguration { + + export namespace PresentationOptions { + const properties: MetaData[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'panel' }]; + + interface PresentationOptionsShape extends LegacyCommandProperties { + presentation?: PresentationOptions; + } + + export function from(this: void, config: PresentationOptionsShape, context: ParseContext): Tasks.PresentationOptions { + let echo: boolean; + let reveal: Tasks.RevealKind; + let focus: boolean; + let panel: Tasks.PanelKind; + if (Types.isBoolean(config.echoCommand)) { + echo = config.echoCommand; + } + if (Types.isString(config.showOutput)) { + reveal = Tasks.RevealKind.fromString(config.showOutput); + } + let presentation = config.presentation || config.terminal; + if (presentation) { + if (Types.isBoolean(presentation.echo)) { + echo = presentation.echo; + } + if (Types.isString(presentation.reveal)) { + reveal = Tasks.RevealKind.fromString(presentation.reveal); + } + if (Types.isBoolean(presentation.focus)) { + focus = presentation.focus; + } + if (Types.isString(presentation.panel)) { + panel = Tasks.PanelKind.fromString(presentation.panel); + } + } + if (echo === void 0 && reveal === void 0 && focus === void 0 && panel === void 0) { + return undefined; + } + return { echo, reveal, focus, panel }; + } + + export function assignProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions): Tasks.PresentationOptions { + return _assignProperties(target, source, properties); + } + + export function fillProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions): Tasks.PresentationOptions { + return _fillProperties(target, source, properties); + } + + export function fillDefaults(value: Tasks.PresentationOptions, context: ParseContext): Tasks.PresentationOptions { + let defaultEcho = context.engine === Tasks.ExecutionEngine.Terminal ? true : false; + return _fillDefaults(value, { echo: defaultEcho, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared }, properties, context); + } + + export function freeze(value: Tasks.PresentationOptions): Readonly { + return _freeze(value, properties); + } + + export function isEmpty(this: void, value: Tasks.PresentationOptions): boolean { + return _isEmpty(value, properties); + } + } + + interface BaseCommandConfiguationShape extends BaseCommandProperties, LegacyCommandProperties { + } + + interface CommandConfiguationShape extends BaseCommandConfiguationShape { + windows?: BaseCommandConfiguationShape; + osx?: BaseCommandConfiguationShape; + linux?: BaseCommandConfiguationShape; + } + + const properties: MetaData[] = [ + { property: 'runtime' }, { property: 'name' }, { property: 'options', type: CommandOptions }, + { property: 'args' }, { property: 'taskSelector' }, { property: 'suppressTaskName' }, + { property: 'presentation', type: PresentationOptions } + ]; + + export function from(this: void, config: CommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration { + let result: Tasks.CommandConfiguration = fromBase(config, context); + + let osConfig: Tasks.CommandConfiguration = undefined; + if (config.windows && Platform.platform === Platform.Platform.Windows) { + osConfig = fromBase(config.windows, context); + } else if (config.osx && Platform.platform === Platform.Platform.Mac) { + osConfig = fromBase(config.osx, context); + } else if (config.linux && Platform.platform === Platform.Platform.Linux) { + osConfig = fromBase(config.linux, context); + } + if (osConfig) { + result = assignProperties(result, osConfig); + } + return isEmpty(result) ? undefined : result; + } + + function fromBase(this: void, config: BaseCommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration { + let result: Tasks.CommandConfiguration = { + name: undefined, + runtime: undefined, + presentation: undefined + }; + if (Types.isString(config.command)) { + result.name = config.command; + } + if (Types.isString(config.type)) { + if (config.type === 'shell' || config.type === 'process') { + result.runtime = Tasks.RuntimeType.fromString(config.type); + } + } + let isShellConfiguration = ShellConfiguration.is(config.isShellCommand); + if (Types.isBoolean(config.isShellCommand) || isShellConfiguration) { + result.runtime = Tasks.RuntimeType.Shell; + } else if (config.isShellCommand !== void 0) { + result.runtime = !!config.isShellCommand ? Tasks.RuntimeType.Shell : Tasks.RuntimeType.Process; + } + if (Types.isString(config.runtime)) { + result.runtime = Tasks.RuntimeType.fromString(config.runtime); + } + if (config.args !== void 0) { + if (Types.isStringArray(config.args)) { + result.args = config.args.slice(0); + } else { + context.problemReporter.error(nls.localize('ConfigurationParser.noargs', 'Error: command arguments must be an array of strings. Provided value is:\n{0}', config.args ? JSON.stringify(config.args, undefined, 4) : 'undefined')); + } + } + if (config.options !== void 0) { + result.options = CommandOptions.from(config.options, context); + if (result.options && result.options.shell === void 0 && isShellConfiguration) { + result.options.shell = ShellConfiguration.from(config.isShellCommand as ShellConfiguration, context); + if (context.engine !== Tasks.ExecutionEngine.Terminal) { + context.problemReporter.warn(nls.localize('ConfigurationParser.noShell', 'Warning: shell configuration is only supported when executing tasks in the terminal.')); + } + } + } + let panel = PresentationOptions.from(config, context); + if (panel) { + result.presentation = panel; + } + if (Types.isString(config.taskSelector)) { + result.taskSelector = config.taskSelector; + } + if (Types.isBoolean(config.suppressTaskName)) { + result.suppressTaskName = config.suppressTaskName; + } + return isEmpty(result) ? undefined : result; + } + + export function isEmpty(value: Tasks.CommandConfiguration): boolean { + return _isEmpty(value, properties); + } + + export function onlyTerminalBehaviour(value: Tasks.CommandConfiguration): boolean { + return value && + value.presentation && (value.presentation.echo !== void 0 || value.presentation.reveal !== void 0) && + value.name === void 0 && value.runtime === void 0 && value.args === void 0 && CommandOptions.isEmpty(value.options); + } + + export function assignProperties(target: Tasks.CommandConfiguration, source: Tasks.CommandConfiguration): Tasks.CommandConfiguration { + if (isEmpty(source)) { + return target; + } + if (isEmpty(target)) { + return source; + } + assignProperty(target, source, 'name'); + assignProperty(target, source, 'runtime'); + assignProperty(target, source, 'taskSelector'); + assignProperty(target, source, 'suppressTaskName'); + if (source.args !== void 0) { + if (target.args === void 0) { + target.args = source.args; + } else { + target.args = target.args.concat(source.args); + } + } + target.presentation = PresentationOptions.assignProperties(target.presentation, source.presentation); + target.options = CommandOptions.assignProperties(target.options, source.options); + return target; + } + + export function fillProperties(target: Tasks.CommandConfiguration, source: Tasks.CommandConfiguration): Tasks.CommandConfiguration { + return _fillProperties(target, source, properties); + } + + export function fillGlobals(target: Tasks.CommandConfiguration, source: Tasks.CommandConfiguration, taskName: string): Tasks.CommandConfiguration { + if (isEmpty(source)) { + return target; + } + target = target || { + name: undefined, + runtime: undefined, + presentation: undefined + }; + if (target.name === void 0) { + fillProperty(target, source, 'name'); + fillProperty(target, source, 'taskSelector'); + fillProperty(target, source, 'suppressTaskName'); + let args: string[] = source.args ? source.args.slice() : []; + if (!target.suppressTaskName) { + if (target.taskSelector !== void 0) { + args.push(target.taskSelector + taskName); + } else { + args.push(taskName); + } + } + if (target.args) { + args = args.concat(target.args); + } + target.args = args; + } + fillProperty(target, source, 'runtime'); + + target.presentation = PresentationOptions.fillProperties(target.presentation, source.presentation); + target.options = CommandOptions.fillProperties(target.options, source.options); + + return target; + } + + export function fillDefaults(value: Tasks.CommandConfiguration, context: ParseContext): void { + if (!value || Object.isFrozen(value)) { + return; + } + if (value.name !== void 0 && value.runtime === void 0) { + value.runtime = Tasks.RuntimeType.Process; + } + value.presentation = PresentationOptions.fillDefaults(value.presentation, context); + if (!isEmpty(value)) { + value.options = CommandOptions.fillDefaults(value.options, context); + } + if (value.args === void 0) { + value.args = EMPTY_ARRAY; + } + if (value.suppressTaskName === void 0) { + value.suppressTaskName = false; + } + } + + export function freeze(value: Tasks.CommandConfiguration): Readonly { + return _freeze(value, properties); + } +} + +namespace ProblemMatcherConverter { + + export function namedFrom(this: void, declares: ProblemMatcherConfig.NamedProblemMatcher[], context: ParseContext): IStringDictionary { + let result: IStringDictionary = Object.create(null); + + if (!Types.isArray(declares)) { + return result; + } + (declares).forEach((value) => { + let namedProblemMatcher = (new ProblemMatcherParser(context.problemReporter)).parse(value); + if (isNamedProblemMatcher(namedProblemMatcher)) { + result[namedProblemMatcher.name] = namedProblemMatcher; + } else { + context.problemReporter.error(nls.localize('ConfigurationParser.noName', 'Error: Problem Matcher in declare scope must have a name:\n{0}\n', JSON.stringify(value, undefined, 4))); + } + }); + return result; + } + + export function from(this: void, config: ProblemMatcherConfig.ProblemMatcherType, context: ParseContext): ProblemMatcher[] { + let result: ProblemMatcher[] = []; + if (config === void 0) { + return result; + } + let kind = getProblemMatcherKind(config); + if (kind === ProblemMatcherKind.Unknown) { + context.problemReporter.warn(nls.localize( + 'ConfigurationParser.unknownMatcherKind', + 'Warning: the defined problem matcher is unknown. Supported types are string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n', + JSON.stringify(config, null, 4))); + return result; + } else if (kind === ProblemMatcherKind.String || kind === ProblemMatcherKind.ProblemMatcher) { + let matcher = resolveProblemMatcher(config as ProblemMatcherConfig.ProblemMatcher, context); + if (matcher) { + result.push(matcher); + } + } else if (kind === ProblemMatcherKind.Array) { + let problemMatchers = <(string | ProblemMatcherConfig.ProblemMatcher)[]>config; + problemMatchers.forEach(problemMatcher => { + let matcher = resolveProblemMatcher(problemMatcher, context); + if (matcher) { + result.push(matcher); + } + }); + } + return result; + } + + function getProblemMatcherKind(this: void, value: ProblemMatcherConfig.ProblemMatcherType): ProblemMatcherKind { + if (Types.isString(value)) { + return ProblemMatcherKind.String; + } else if (Types.isArray(value)) { + return ProblemMatcherKind.Array; + } else if (!Types.isUndefined(value)) { + return ProblemMatcherKind.ProblemMatcher; + } else { + return ProblemMatcherKind.Unknown; + } + } + + function resolveProblemMatcher(this: void, value: string | ProblemMatcherConfig.ProblemMatcher, context: ParseContext): ProblemMatcher { + if (Types.isString(value)) { + let variableName = value; + if (variableName.length > 1 && variableName[0] === '$') { + variableName = variableName.substring(1); + let global = ProblemMatcherRegistry.get(variableName); + if (global) { + return Objects.clone(global); + } + let localProblemMatcher = context.namedProblemMatchers[variableName]; + if (localProblemMatcher) { + localProblemMatcher = Objects.clone(localProblemMatcher); + // remove the name + delete localProblemMatcher.name; + return localProblemMatcher; + } + } + context.problemReporter.error(nls.localize('ConfigurationParser.invalidVaraibleReference', 'Error: Invalid problemMatcher reference: {0}\n', value)); + return undefined; + } else { + let json = value; + return new ProblemMatcherParser(context.problemReporter).parse(json); + } + } +} + +namespace TaskIdentifier { + export function from(this: void, value: TaskIdentifier): Tasks.TaskIdentifier { + if (!value || !Types.isString(value.type)) { + return undefined; + } + const hash = crypto.createHash('md5'); + hash.update(JSON.stringify(value)); + let key = hash.digest('hex'); + let result: Tasks.TaskIdentifier = { + _key: key, + type: value.type + }; + result = Objects.assign(result, value); + return result; + } +} + +const source: Tasks.TaskSource = { + kind: Tasks.TaskSourceKind.Workspace, + label: 'Workspace', + config: undefined +}; + +namespace GroupKind { + export function from(this: void, external: string | GroupKind): [string, boolean] { + if (external === void 0) { + return undefined; + } + if (Types.isString(external)) { + if (Tasks.TaskGroup.is(external)) { + return [external, false]; + } else { + return undefined; + } + } + if (!Types.isString(external.kind) || !Tasks.TaskGroup.is(external.kind)) { + return undefined; + } + let group: string = external.kind; + let isDefault: boolean = !!external.isDefault; + + return [group, isDefault]; + } +} + +namespace ConfigurationProperties { + + const properties: MetaData[] = [ + + { property: 'name' }, { property: 'identifier' }, { property: 'group' }, { property: 'isBackground' }, + { property: 'promptOnClose' }, { property: 'dependsOn' }, + { property: 'presentation', type: CommandConfiguration.PresentationOptions }, { property: 'problemMatchers' } + ]; + + export function from(this: void, external: ConfigurationProperties, context: ParseContext, includePresentation): Tasks.ConfigurationProperties { + if (!external) { + return undefined; + } + let result: Tasks.ConfigurationProperties = {}; + if (Types.isString(external.taskName)) { + result.name = external.taskName; + } + if (Types.isString(external.label) && context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0) { + result.name = external.label; + } + if (Types.isString(external.identifier)) { + result.identifier = external.identifier; + } + if (external.isBackground !== void 0) { + result.isBackground = !!external.isBackground; + } + if (external.promptOnClose !== void 0) { + result.promptOnClose = !!external.promptOnClose; + } + if (external.group !== void 0) { + if (Types.isString(external.group) && Tasks.TaskGroup.is(external.group)) { + result.group = external.group; + result.isDefaultGroupEntry = false; + } else { + let values = GroupKind.from(external.group); + if (values) { + result.group = values[0]; + result.isDefaultGroupEntry = values[1]; + } + } + } + if (external.dependsOn !== void 0) { + if (Types.isString(external.dependsOn)) { + result.dependsOn = [external.dependsOn]; + } else if (Types.isStringArray(external.dependsOn)) { + result.dependsOn = external.dependsOn.slice(); + } + } + if (includePresentation && (external.presentation !== void 0 || (external as LegacyCommandProperties).terminal !== void 0)) { + result.presentation = CommandConfiguration.PresentationOptions.from(external, context); + } + if (external.problemMatcher) { + result.problemMatchers = ProblemMatcherConverter.from(external.problemMatcher, context); + } + return isEmpty(result) ? undefined : result; + } + + export function isEmpty(this: void, value: Tasks.ConfigurationProperties): boolean { + return _isEmpty(value, properties); + } +} + +namespace ConfiguringTask { + + const grunt = 'grunt.'; + const jake = 'jake.'; + const gulp = 'gulp.'; + const npm = 'vscode.npm.'; + const typescript = 'vscode.typescript.'; + + interface CustomizeShape { + customize: string; + } + + export function from(this: void, external: ConfiguringTask, context: ParseContext, index: number): Tasks.ConfiguringTask { + if (!external) { + return undefined; + } + let type = external.type; + let customize = (external as CustomizeShape).customize; + if (!type && !customize) { + context.problemReporter.error(nls.localize('ConfigurationParser.noTaskType', 'Error: tasks configuration must have a type property. The configuration will be ignored.\n{0}\n', JSON.stringify(external, null, 4))); + return undefined; + } + let typeDeclaration = TaskDefinitionRegistry.get(type); + if (!typeDeclaration) { + let message = nls.localize('ConfigurationParser.noTypeDefinition', 'Error: there is no registered task type \'{0}\'. Did you miss to install an extension that provides a corresponding task provider?', type); + context.problemReporter.error(message); + return undefined; + } + let identifier: TaskIdentifier; + if (Types.isString(customize)) { + if (customize.indexOf(grunt) === 0) { + identifier = { type: 'grunt', task: customize.substring(grunt.length) } as TaskIdentifier; + } else if (customize.indexOf(jake) === 0) { + identifier = { type: 'jake', task: customize.substring(jake.length) } as TaskIdentifier; + } else if (customize.indexOf(gulp) === 0) { + identifier = { type: 'gulp', task: customize.substring(gulp.length) } as TaskIdentifier; + } else if (customize.indexOf(npm) === 0) { + identifier = { type: 'npm', script: customize.substring(npm.length + 4) } as TaskIdentifier; + } else if (customize.indexOf(typescript) === 0) { + identifier = { type: 'typescript', tsconfig: customize.substring(typescript.length + 6) } as TaskIdentifier; + } + } else { + identifier = { + type + }; + Object.keys(typeDeclaration.properties).forEach((property) => { + let value = external[property]; + if (value !== void 0 && value !== null) { + identifier[property] = value; + } + }); + } + let taskIdentifier = TaskIdentifier.from(identifier); + let configElement: Tasks.TaskSourceConfigElement = { + file: '.vscode\\tasks.json', + index, + element: external + }; + let result: Tasks.ConfiguringTask = { + type: type, + configures: taskIdentifier, + _id: taskIdentifier._key, + _source: Objects.assign({}, source, { config: configElement }), + _label: undefined + }; + let configuration = ConfigurationProperties.from(external, context, true); + if (configuration) { + result = Objects.assign(result, configuration); + if (result.name) { + result._label = result.name; + } else { + let label = result.configures.type; + if (typeDeclaration.required && typeDeclaration.required.length > 0) { + for (let required of typeDeclaration.required) { + let value = result.configures[required]; + if (value) { + label = label + ' ' + value; + break; + } + } + } + result._label = label; + } + if (!result.identifier) { + result.identifier = taskIdentifier._key; + } + } + return result; + } +} + +namespace CustomTask { + + export function from(this: void, external: CustomTask, context: ParseContext, index: number): Tasks.CustomTask { + if (!external) { + return undefined; + } + let type = external.type; + if (type === void 0 || type === null) { + type = 'custom'; + } + if (type !== 'custom' && type !== 'shell' && type !== 'process') { + context.problemReporter.error(nls.localize('ConfigurationParser.notCustom', 'Error: tasks is not declared as a custom task. The configuration will be ignored.\n{0}\n', JSON.stringify(external, null, 4))); + return undefined; + } + let taskName = external.taskName; + if (!taskName) { + context.problemReporter.error(nls.localize('ConfigurationParser.noTaskName', 'Error: tasks must provide a taskName property. The task will be ignored.\n{0}\n', JSON.stringify(external, null, 4))); + return undefined; + } + + let result: Tasks.CustomTask = { + type: 'custom', + _id: context.uuidMap.getUUID(taskName), + _source: Objects.assign({}, source, { config: { index, element: external, file: '.vscode\\tasks.json' } }), + _label: taskName, + name: taskName, + identifier: taskName, + command: undefined + }; + let configuration = ConfigurationProperties.from(external, context, false); + if (configuration) { + result = Objects.assign(result, configuration); + } + let supportLegacy: boolean = true; //context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0; + if (supportLegacy) { + let legacy: LegacyTaskProperties = external as LegacyTaskProperties; + if (result.isBackground === void 0 && legacy.isWatching !== void 0) { + result.isBackground = !!legacy.isWatching; + } + if (result.group === void 0) { + if (legacy.isBuildCommand === true) { + result.group = Tasks.TaskGroup.Build; + } else if (legacy.isTestCommand === true) { + result.group = Tasks.TaskGroup.Test; + } + } + } + let command: Tasks.CommandConfiguration = CommandConfiguration.from(external, context); + if (command) { + result.command = command; + } + if (external.command !== void 0) { + // if the task has its own command then we suppress the + // task name by default. + command.suppressTaskName = true; + } + return result; + } + + export function fillGlobals(task: Tasks.CustomTask, globals: Globals): void { + // We only merge a command from a global definition if there is no dependsOn + if (task.dependsOn === void 0) { + task.command = CommandConfiguration.fillGlobals(task.command, globals.command, task.name); + } + // promptOnClose is inferred from isBackground if available + if (task.promptOnClose === void 0 && task.isBackground === void 0 && globals.promptOnClose !== void 0) { + task.promptOnClose = globals.promptOnClose; + } + } + + export function fillDefaults(task: Tasks.CustomTask, context: ParseContext): void { + CommandConfiguration.fillDefaults(task.command, context); + if (task.promptOnClose === void 0) { + task.promptOnClose = task.isBackground !== void 0 ? !task.isBackground : true; + } + if (task.isBackground === void 0) { + task.isBackground = false; + } + if (task.problemMatchers === void 0) { + task.problemMatchers = EMPTY_ARRAY; + } + if (task.group !== void 0 && task.isDefaultGroupEntry === void 0) { + task.isDefaultGroupEntry = false; + } + } + + export function createCustomTask(contributedTask: Tasks.ContributedTask, configuredProps: Tasks.ConfigurationProperties & { _id: string, _source: Tasks.WorkspaceTaskSource }): Tasks.CustomTask { + let result: Tasks.CustomTask = { + _id: configuredProps._id, + _source: Objects.assign({}, configuredProps._source, { customizes: contributedTask.defines }), + _label: configuredProps.name || contributedTask._label, + type: 'custom', + command: contributedTask.command, + name: configuredProps.name || contributedTask.name, + identifier: configuredProps.identifier || contributedTask.identifier + }; + let resultConfigProps: Tasks.ConfigurationProperties = result; + + assignProperty(resultConfigProps, configuredProps, 'group'); + assignProperty(resultConfigProps, configuredProps, 'isDefaultGroupEntry'); + assignProperty(resultConfigProps, configuredProps, 'isBackground'); + assignProperty(resultConfigProps, configuredProps, 'dependsOn'); + assignProperty(resultConfigProps, configuredProps, 'problemMatchers'); + assignProperty(resultConfigProps, configuredProps, 'promptOnClose'); + result.command.presentation = CommandConfiguration.PresentationOptions.assignProperties( + result.command.presentation, configuredProps.presentation); + + let contributedConfigProps: Tasks.ConfigurationProperties = contributedTask; + fillProperty(resultConfigProps, contributedConfigProps, 'group'); + fillProperty(resultConfigProps, contributedConfigProps, 'isDefaultGroupEntry'); + fillProperty(resultConfigProps, contributedConfigProps, 'isBackground'); + fillProperty(resultConfigProps, contributedConfigProps, 'dependsOn'); + fillProperty(resultConfigProps, contributedConfigProps, 'problemMatchers'); + fillProperty(resultConfigProps, contributedConfigProps, 'promptOnClose'); + result.command.presentation = CommandConfiguration.PresentationOptions.fillProperties( + result.command.presentation, contributedConfigProps.presentation); + + return result; + } +} + +interface TaskParseResult { + custom: Tasks.CustomTask[]; + configured: Tasks.ConfiguringTask[]; +} + +namespace TaskParser { + + function isCustomTask(value: CustomTask | ConfiguringTask): value is CustomTask { + let type = value.type; + let customize = (value as any).customize; + return customize === void 0 && (type === void 0 || type === null || type === 'custom' || type === 'shell' || type === 'process'); + } + + export function from(this: void, externals: (CustomTask | ConfiguringTask)[], globals: Globals, context: ParseContext): TaskParseResult { + let result: TaskParseResult = { custom: [], configured: [] }; + if (!externals) { + return result; + } + let defaultBuildTask: { task: Tasks.Task; rank: number; } = { task: undefined, rank: -1 }; + let defaultTestTask: { task: Tasks.Task; rank: number; } = { task: undefined, rank: -1 }; + let schema2_0_0: boolean = context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0; + + for (let index = 0; index < externals.length; index++) { + let external = externals[index]; + if (isCustomTask(external)) { + let customTask = CustomTask.from(external, context, index); + if (customTask) { + CustomTask.fillGlobals(customTask, globals); + CustomTask.fillDefaults(customTask, context); + if (context.engine === Tasks.ExecutionEngine.Terminal && customTask.command && customTask.command.name && customTask.command.runtime === Tasks.RuntimeType.Shell && customTask.command.args && customTask.command.args.length > 0) { + if (hasUnescapedSpaces(customTask.command.name) || customTask.command.args.some(hasUnescapedSpaces)) { + context.problemReporter.warn( + nls.localize( + 'taskConfiguration.shellArgs', + 'Warning: the task \'{0}\' is a shell command and either the command name or one of its arguments has unescaped spaces. To ensure correct command line quoting please merge args into the command.', + customTask.name + ) + ); + } + } + if (schema2_0_0) { + if ((customTask.command === void 0 || customTask.command.name === void 0) && (customTask.dependsOn === void 0 || customTask.dependsOn.length === 0)) { + context.problemReporter.error(nls.localize( + 'taskConfiguration.noCommandOrDependsOn', 'Error: the task \'{0}\' neither specifies a command nor a dependsOn property. The task will be ignored. Its definition is:\n{1}', + customTask.name, JSON.stringify(external, undefined, 4) + )); + continue; + } + } else { + if (customTask.command === void 0 || customTask.command.name === void 0) { + context.problemReporter.warn(nls.localize( + 'taskConfiguration.noCommand', 'Error: the task \'{0}\' doesn\'t define a command. The task will be ignored. Its definition is:\n{1}', + customTask.name, JSON.stringify(external, undefined, 4) + )); + continue; + } + } + if (customTask.group === Tasks.TaskGroup.Build && defaultBuildTask.rank < 2) { + defaultBuildTask.task = customTask; + defaultBuildTask.rank = 2; + } else if (customTask.group === Tasks.TaskGroup.Test && defaultTestTask.rank < 2) { + defaultTestTask.task = customTask; + defaultTestTask.rank = 2; + } else if (customTask.name === 'build' && defaultBuildTask.rank < 1) { + defaultBuildTask.task = customTask; + defaultBuildTask.rank = 1; + } else if (customTask.name === 'test' && defaultTestTask.rank < 1) { + defaultTestTask.task = customTask; + defaultTestTask.rank = 1; + } + result.custom.push(customTask); + } + } else { + let configuredTask = ConfiguringTask.from(external, context, index); + if (configuredTask) { + result.configured.push(configuredTask); + } + } + } + if (defaultBuildTask.rank > -1 && defaultBuildTask.rank < 2) { + defaultBuildTask.task.group = Tasks.TaskGroup.Build; + defaultBuildTask.task.isDefaultGroupEntry = false; + } else if (defaultTestTask.rank > -1 && defaultTestTask.rank < 2) { + defaultTestTask.task.group = Tasks.TaskGroup.Test; + defaultTestTask.task.isDefaultGroupEntry = false; + } + + return result; + } + + export function assignTasks(target: Tasks.CustomTask[], source: Tasks.CustomTask[]): Tasks.CustomTask[] { + if (source === void 0 || source.length === 0) { + return target; + } + if (target === void 0 || target.length === 0) { + return source; + } + + if (source) { + // Tasks are keyed by ID but we need to merge by name + let map: IStringDictionary = Object.create(null); + target.forEach((task) => { + map[task.name] = task; + }); + + source.forEach((task) => { + map[task.name] = task; + }); + let newTarget: Tasks.CustomTask[] = []; + target.forEach(task => { + newTarget.push(map[task.name]); + delete map[task.name]; + }); + Object.keys(map).forEach(key => newTarget.push(map[key])); + target = newTarget; + } + return target; + } + + function hasUnescapedSpaces(this: void, value: string): boolean { + if (Platform.isWindows) { + if (value.length >= 2 && value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') { + return false; + } + return value.indexOf(' ') !== -1; + } else { + if (value.length >= 2 && ((value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') || (value.charAt(0) === '\'' && value.charAt(value.length - 1) === '\''))) { + return false; + } + for (let i = 0; i < value.length; i++) { + let ch = value.charAt(i); + if (ch === ' ') { + if (i === 0 || value.charAt(i - 1) !== '\\') { + return true; + } + } + } + return false; + } + } + + export function quickParse(this: void, externals: (CustomTask | ConfiguringTask)[], context: ParseContext): (Tasks.CustomTask | Tasks.ConfiguringTask)[] { + if (!externals) { + return undefined; + } + let result: (Tasks.CustomTask | Tasks.ConfiguringTask)[] = []; + for (let index = 0; index < externals.length; index++) { + let external = externals[index]; + if (isCustomTask(external)) { + result.push(CustomTask.from(external, context, index)); + } else { + result.push(ConfiguringTask.from(external, context, index)); + } + } + return result; + } +} + +interface Globals { + command?: Tasks.CommandConfiguration; + promptOnClose?: boolean; + suppressTaskName?: boolean; +} + +namespace Globals { + + export function from(config: ExternalTaskRunnerConfiguration, context: ParseContext): Globals { + let result = fromBase(config, context); + let osGlobals: Globals = undefined; + if (config.windows && Platform.platform === Platform.Platform.Windows) { + osGlobals = fromBase(config.windows, context); + } else if (config.osx && Platform.platform === Platform.Platform.Mac) { + osGlobals = fromBase(config.osx, context); + } else if (config.linux && Platform.platform === Platform.Platform.Linux) { + osGlobals = fromBase(config.linux, context); + } + if (osGlobals) { + result = Globals.assignProperties(result, osGlobals); + } + let command = CommandConfiguration.from(config, context); + if (command) { + result.command = command; + } + Globals.fillDefaults(result, context); + Globals.freeze(result); + return result; + } + + export function fromBase(this: void, config: BaseTaskRunnerConfiguration, context: ParseContext): Globals { + let result: Globals = {}; + if (config.suppressTaskName !== void 0) { + result.suppressTaskName = !!config.suppressTaskName; + } + if (config.promptOnClose !== void 0) { + result.promptOnClose = !!config.promptOnClose; + } + return result; + } + + export function isEmpty(value: Globals): boolean { + return !value || value.command === void 0 && value.promptOnClose === void 0 && value.suppressTaskName === void 0; + } + + export function assignProperties(target: Globals, source: Globals): Globals { + if (isEmpty(source)) { + return target; + } + if (isEmpty(target)) { + return source; + } + assignProperty(target, source, 'promptOnClose'); + assignProperty(target, source, 'suppressTaskName'); + return target; + } + + export function fillDefaults(value: Globals, context: ParseContext): void { + if (!value) { + return; + } + CommandConfiguration.fillDefaults(value.command, context); + if (value.suppressTaskName === void 0) { + value.suppressTaskName = false; + } + if (value.promptOnClose === void 0) { + value.promptOnClose = true; + } + } + + export function freeze(value: Globals): void { + Object.freeze(value); + if (value.command) { + CommandConfiguration.freeze(value.command); + } + } +} + +export namespace ExecutionEngine { + + export function from(config: ExternalTaskRunnerConfiguration): Tasks.ExecutionEngine { + let runner = config.runner || config._runner; + let result: Tasks.ExecutionEngine; + if (runner) { + switch (runner) { + case 'terminal': + result = Tasks.ExecutionEngine.Terminal; + break; + case 'process': + result = Tasks.ExecutionEngine.Process; + break; + } + } + let schemaVersion = JsonSchemaVersion.from(config); + if (schemaVersion === Tasks.JsonSchemaVersion.V0_1_0) { + return result || Tasks.ExecutionEngine.Process; + } else if (schemaVersion === Tasks.JsonSchemaVersion.V2_0_0) { + return Tasks.ExecutionEngine.Terminal; + } else { + throw new Error('Shouldn\'t happen.'); + } + } +} + +export namespace JsonSchemaVersion { + + const _default: Tasks.JsonSchemaVersion = Tasks.JsonSchemaVersion.V2_0_0; + + export function from(config: ExternalTaskRunnerConfiguration): Tasks.JsonSchemaVersion { + let version = config.version; + if (!version) { + return _default; + } + switch (version) { + case '0.1.0': + return Tasks.JsonSchemaVersion.V0_1_0; + case '2.0.0': + return Tasks.JsonSchemaVersion.V2_0_0; + default: + return _default; + } + } +} + +export interface ParseResult { + validationStatus: ValidationStatus; + custom: Tasks.CustomTask[]; + configured: Tasks.ConfiguringTask[]; + engine: Tasks.ExecutionEngine; +} + +export interface IProblemReporter extends IProblemReporterBase { + clearOutput(): void; +} + +class NullProblemReporter extends NullProblemReporterBase implements IProblemReporter { + clearOutput(): void { }; +} + +class UUIDMap { + + private last: IStringDictionary; + private current: IStringDictionary; + + constructor(other?: UUIDMap) { + this.current = Object.create(null); + if (other) { + for (let key of Object.keys(other.current)) { + let value = other.current[key]; + if (Array.isArray(value)) { + this.current[key] = value.slice(); + } else { + this.current[key] = value; + } + } + } + } + + public start(): void { + this.last = this.current; + this.current = Object.create(null); + } + + public getUUID(identifier: string): string { + let lastValue = this.last[identifier]; + let result: string; + if (lastValue !== void 0) { + if (Array.isArray(lastValue)) { + result = lastValue.shift(); + if (lastValue.length === 0) { + delete this.last[identifier]; + } + } else { + result = lastValue; + delete this.last[identifier]; + } + } + if (result === void 0) { + result = UUID.generateUuid(); + } + let currentValue = this.current[identifier]; + if (currentValue === void 0) { + this.current[identifier] = result; + } else { + if (Array.isArray(currentValue)) { + currentValue.push(result); + } else { + let arrayValue: string[] = [currentValue]; + arrayValue.push(result); + this.current[identifier] = arrayValue; + } + } + return result; + } + + public finish(): void { + this.last = undefined; + } +} + +class ConfigurationParser { + + private problemReporter: IProblemReporter; + private uuidMap: UUIDMap; + + constructor(problemReporter: IProblemReporter, uuidMap: UUIDMap) { + this.problemReporter = problemReporter; + this.uuidMap = uuidMap; + } + + public run(fileConfig: ExternalTaskRunnerConfiguration): ParseResult { + let engine = ExecutionEngine.from(fileConfig); + let schemaVersion = JsonSchemaVersion.from(fileConfig); + if (engine === Tasks.ExecutionEngine.Terminal) { + this.problemReporter.clearOutput(); + } + let context: ParseContext = { + problemReporter: this.problemReporter, + uuidMap: this.uuidMap, + namedProblemMatchers: undefined, + engine, + schemaVersion + }; + let taskParseResult = this.createTaskRunnerConfiguration(fileConfig, context); + return { + validationStatus: this.problemReporter.status, + custom: taskParseResult.custom, + configured: taskParseResult.configured, + engine + }; + } + + private createTaskRunnerConfiguration(fileConfig: ExternalTaskRunnerConfiguration, context: ParseContext): TaskParseResult { + let globals = Globals.from(fileConfig, context); + if (this.problemReporter.status.isFatal()) { + return { custom: [], configured: [] }; + } + context.namedProblemMatchers = ProblemMatcherConverter.namedFrom(fileConfig.declares, context); + let globalTasks: Tasks.CustomTask[]; + let externalGlobalTasks: (ConfiguringTask | CustomTask)[]; + if (fileConfig.windows && Platform.platform === Platform.Platform.Windows) { + globalTasks = TaskParser.from(fileConfig.windows.tasks, globals, context).custom; + externalGlobalTasks = fileConfig.windows.tasks; + } else if (fileConfig.osx && Platform.platform === Platform.Platform.Mac) { + globalTasks = TaskParser.from(fileConfig.osx.tasks, globals, context).custom; + externalGlobalTasks = fileConfig.osx.tasks; + } else if (fileConfig.linux && Platform.platform === Platform.Platform.Linux) { + globalTasks = TaskParser.from(fileConfig.linux.tasks, globals, context).custom; + externalGlobalTasks = fileConfig.linux.tasks; + } + if (context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0 && globalTasks && globalTasks.length > 0 && externalGlobalTasks && externalGlobalTasks.length > 0) { + let taskContent: string[] = []; + for (let task of externalGlobalTasks) { + taskContent.push(JSON.stringify(task, null, 4)); + } + context.problemReporter.error( + nls.localize( + 'TaskParse.noOsSpecificGlobalTasks', + 'Task version 2.0.0 doesn\'t support global OS specific tasks. Convert them to a task with a OS specific command. Affected tasks are:\n{0}', taskContent.join('\n')) + ); + } + + let result: TaskParseResult = { custom: undefined, configured: undefined }; + if (fileConfig.tasks) { + result = TaskParser.from(fileConfig.tasks, globals, context); + } + if (globalTasks) { + result.custom = TaskParser.assignTasks(result.custom, globalTasks); + } + + if ((!result.custom || result.custom.length === 0) && (globals.command && globals.command.name)) { + let matchers: ProblemMatcher[] = ProblemMatcherConverter.from(fileConfig.problemMatcher, context); + let isBackground = fileConfig.isBackground ? !!fileConfig.isBackground : fileConfig.isWatching ? !!fileConfig.isWatching : undefined; + let task: Tasks.CustomTask = { + _id: context.uuidMap.getUUID(globals.command.name), + _source: Objects.assign({}, source, { config: { index: -1, element: fileConfig } }), + _label: globals.command.name, + type: 'custom', + name: globals.command.name, + identifier: globals.command.name, + group: Tasks.TaskGroup.Build, + command: { + name: undefined, + runtime: undefined, + presentation: undefined, + suppressTaskName: true + }, + isBackground: isBackground, + problemMatchers: matchers + }; + let value = GroupKind.from(fileConfig.group); + if (value) { + task.group = value[0]; + task.isDefaultGroupEntry = value[1]; + } else if (fileConfig.group === 'none') { + task.group = undefined; + } + CustomTask.fillGlobals(task, globals); + CustomTask.fillDefaults(task, context); + result.custom = [task]; + } + result.custom = result.custom || []; + result.configured = result.configured || []; + return result; + } +} + +let uuidMap: UUIDMap = new UUIDMap(); +export function parse(configuration: ExternalTaskRunnerConfiguration, logger: IProblemReporter): ParseResult { + try { + uuidMap.start(); + return (new ConfigurationParser(logger, uuidMap)).run(configuration); + } finally { + uuidMap.finish(); + } +} + +export function createCustomTask(contributedTask: Tasks.ContributedTask, configuredProps: Tasks.ConfigurationProperties & { _id: string; _source: Tasks.WorkspaceTaskSource }): Tasks.CustomTask { + return CustomTask.createCustomTask(contributedTask, configuredProps); +} + +export function getTaskIdentifier(value: TaskIdentifier): Tasks.TaskIdentifier { + return TaskIdentifier.from(value); +} + +export function findTaskIndex(fileConfig: ExternalTaskRunnerConfiguration, task: Tasks.Task): number { + if (!fileConfig || !fileConfig.tasks) { + return undefined; + } + if (fileConfig.tasks.length === 0) { + return -1; + } + let localMap = new UUIDMap(uuidMap); + let context: ParseContext = { + problemReporter: this.problemReporter, + uuidMap: localMap, + namedProblemMatchers: undefined, + engine: ExecutionEngine.from(fileConfig), + schemaVersion: JsonSchemaVersion.from(fileConfig) + }; + try { + localMap.start(); + let tasks = TaskParser.quickParse(fileConfig.tasks, context); + for (let i = 0; i < tasks.length; i++) { + if (task._id === tasks[i]._id) { + return i; + } + } + return -1; + } finally { + localMap.finish(); + } +} + +/* +class VersionConverter { + constructor(private problemReporter: IProblemReporter) { + } + + public convert(fromConfig: ExternalTaskRunnerConfiguration): ExternalTaskRunnerConfiguration { + let result: ExternalTaskRunnerConfiguration; + result.version = '2.0.0'; + if (Array.isArray(fromConfig.tasks)) { + + } else { + result.tasks = []; + } + + + return result; + } + + private convertGlobalTask(fromConfig: ExternalTaskRunnerConfiguration): TaskDescription { + let command: string = this.getGlobalCommand(fromConfig); + if (!command) { + this.problemReporter.error(nls.localize('Converter.noGlobalName', 'No global command specified. Can\'t convert to 2.0.0 version.')); + return undefined; + } + let result: TaskDescription = { + taskName: command + }; + if (fromConfig.isShellCommand) { + result.type = 'shell'; + } else { + result.type = 'process'; + result.args = fromConfig.args; + } + if (fromConfig.) + + return result; + } + + private getGlobalCommand(fromConfig: ExternalTaskRunnerConfiguration): string { + if (fromConfig.command) { + return fromConfig.command; + } else if (fromConfig.windows && fromConfig.windows.command) { + return fromConfig.windows.command; + } else if (fromConfig.osx && fromConfig.osx.command) { + return fromConfig.osx.command; + } else if (fromConfig.linux && fromConfig.linux.command) { + return fromConfig.linux.command; + } else { + return undefined; + } + } + + private createCommandLine(command: string, args: string[], isWindows: boolean): string { + let result: string[]; + let commandHasSpace = false; + let argHasSpace = false; + if (TaskDescription.hasUnescapedSpaces(command)) { + result.push(`"${command}"`); + commandHasSpace = true; + } else { + result.push(command); + } + if (args) { + for (let arg of args) { + if (TaskDescription.hasUnescapedSpaces(arg)) { + result.push(`"${arg}"`); + argHasSpace= true; + } else { + result.push(arg); + } + } + } + return result.join(' '); + } + +} +*/ \ No newline at end of file diff --git a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts new file mode 100644 index 0000000000..168310ef6e --- /dev/null +++ b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts @@ -0,0 +1,1673 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import Severity from 'vs/base/common/severity'; +import * as UUID from 'vs/base/common/uuid'; + +import * as Platform from 'vs/base/common/platform'; +import { ValidationStatus } from 'vs/base/common/parsers'; +import { ProblemMatcher, FileLocationKind, ProblemPattern, ApplyToKind } from 'vs/platform/markers/common/problemMatcher'; + +import * as Tasks from 'vs/workbench/parts/tasks/common/tasks'; +import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask } from 'vs/workbench/parts/tasks/node/taskConfiguration'; + +class ProblemReporter implements IProblemReporter { + + private _validationStatus: ValidationStatus = new ValidationStatus(); + + public receivedMessage: boolean = false; + public lastMessage: string = undefined; + + public info(message: string): void { + this.log(message); + } + + public warn(message: string): void { + this.log(message); + } + + public error(message: string): void { + this.log(message); + } + + public fatal(message: string): void { + this.log(message); + } + + public get status(): ValidationStatus { + return this._validationStatus; + } + + private log(message: string): void { + this.receivedMessage = true; + this.lastMessage = message; + } + + public clearOutput(): void { + this.receivedMessage = false; + this.lastMessage = undefined; + } +} + +class ConfiguationBuilder { + + public result: Tasks.Task[]; + private builders: CustomTaskBuilder[]; + + constructor() { + this.result = []; + this.builders = []; + } + + public task(name: string, command: string): CustomTaskBuilder { + let builder = new CustomTaskBuilder(this, name, command); + this.builders.push(builder); + this.result.push(builder.result); + return builder; + } + + public done(): void { + for (let builder of this.builders) { + builder.done(); + } + } +} + +class PresentationBuilder { + + public result: Tasks.PresentationOptions; + + constructor(public parent: CommandConfigurationBuilder) { + this.result = { echo: false, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared }; + } + + public echo(value: boolean): PresentationBuilder { + this.result.echo = value; + return this; + } + + public reveal(value: Tasks.RevealKind): PresentationBuilder { + this.result.reveal = value; + return this; + } + + public focus(value: boolean): PresentationBuilder { + this.result.focus = value; + return this; + } + + public instance(value: Tasks.PanelKind): PresentationBuilder { + this.result.panel = value; + return this; + } + + public done(): void { + } +} + +class CommandConfigurationBuilder { + public result: Tasks.CommandConfiguration; + + private presentationBuilder: PresentationBuilder; + + constructor(public parent: CustomTaskBuilder, command: string) { + this.presentationBuilder = new PresentationBuilder(this); + this.result = { + name: command, + runtime: Tasks.RuntimeType.Process, + args: [], + options: { + cwd: '${workspaceRoot}' + }, + presentation: this.presentationBuilder.result, + suppressTaskName: false + }; + } + + public name(value: string): CommandConfigurationBuilder { + this.result.name = value; + return this; + } + + public runtime(value: Tasks.RuntimeType): CommandConfigurationBuilder { + this.result.runtime = value; + return this; + } + + public args(value: string[]): CommandConfigurationBuilder { + this.result.args = value; + return this; + } + + public options(value: Tasks.CommandOptions): CommandConfigurationBuilder { + this.result.options = value; + return this; + } + + public taskSelector(value: string): CommandConfigurationBuilder { + this.result.taskSelector = value; + return this; + } + + public suppressTaskName(value: boolean): CommandConfigurationBuilder { + this.result.suppressTaskName = value; + return this; + } + + public presentation(): PresentationBuilder { + return this.presentationBuilder; + } + + public done(taskName: string): void { + this.result.args = this.result.args.map(arg => arg === '$name' ? taskName : arg); + this.presentationBuilder.done(); + } +} + +class CustomTaskBuilder { + + public result: Tasks.CustomTask; + private commandBuilder: CommandConfigurationBuilder; + + constructor(public parent: ConfiguationBuilder, name: string, command: string) { + this.commandBuilder = new CommandConfigurationBuilder(this, command); + this.result = { + _id: name, + _source: { kind: Tasks.TaskSourceKind.Workspace, label: 'workspace', config: { element: undefined, index: -1, file: '.vscode/tasks.json' } }, + _label: name, + type: 'custom', + identifier: name, + name: name, + command: this.commandBuilder.result, + isBackground: false, + promptOnClose: true, + problemMatchers: [] + }; + } + + public identifier(value: string): CustomTaskBuilder { + this.result.identifier = value; + return this; + } + + public group(value: Tasks.TaskGroup): CustomTaskBuilder { + this.result.group = value; + this.result.isDefaultGroupEntry = false; + return this; + } + + public isPrimary(value: boolean): CustomTaskBuilder { + this.result.isDefaultGroupEntry = value; + return this; + } + + public isBackground(value: boolean): CustomTaskBuilder { + this.result.isBackground = value; + return this; + } + + public promptOnClose(value: boolean): CustomTaskBuilder { + this.result.promptOnClose = value; + return this; + } + + public problemMatcher(): ProblemMatcherBuilder { + let builder = new ProblemMatcherBuilder(this); + this.result.problemMatchers.push(builder.result); + return builder; + } + + public command(): CommandConfigurationBuilder { + return this.commandBuilder; + } + + public done(): void { + this.commandBuilder.done(this.result.name); + } +} + +class ProblemMatcherBuilder { + + public static DEFAULT_UUID = UUID.generateUuid(); + + public result: ProblemMatcher; + + constructor(public parent: CustomTaskBuilder) { + this.result = { + owner: ProblemMatcherBuilder.DEFAULT_UUID, + applyTo: ApplyToKind.allDocuments, + severity: undefined, + fileLocation: FileLocationKind.Relative, + filePrefix: '${cwd}', + pattern: undefined + }; + } + + public owner(value: string): ProblemMatcherBuilder { + this.result.owner = value; + return this; + } + + public applyTo(value: ApplyToKind): ProblemMatcherBuilder { + this.result.applyTo = value; + return this; + } + + public severity(value: Severity): ProblemMatcherBuilder { + this.result.severity = value; + return this; + } + + public fileLocation(value: FileLocationKind): ProblemMatcherBuilder { + this.result.fileLocation = value; + return this; + } + + public filePrefix(value: string): ProblemMatcherBuilder { + this.result.filePrefix = value; + return this; + } + + public pattern(regExp: RegExp): PatternBuilder { + let builder = new PatternBuilder(this, regExp); + if (!this.result.pattern) { + this.result.pattern = builder.result; + } + return builder; + } +} + +class PatternBuilder { + public result: ProblemPattern; + + constructor(public parent: ProblemMatcherBuilder, regExp: RegExp) { + this.result = { + regexp: regExp, + file: 1, + message: 0, + line: 2, + character: 3 + }; + } + + public file(value: number): PatternBuilder { + this.result.file = value; + return this; + } + + public message(value: number): PatternBuilder { + this.result.message = value; + return this; + } + + public location(value: number): PatternBuilder { + this.result.location = value; + return this; + } + + public line(value: number): PatternBuilder { + this.result.line = value; + return this; + } + + public character(value: number): PatternBuilder { + this.result.character = value; + return this; + } + + public endLine(value: number): PatternBuilder { + this.result.endLine = value; + return this; + } + + public endCharacter(value: number): PatternBuilder { + this.result.endCharacter = value; + return this; + } + + public code(value: number): PatternBuilder { + this.result.code = value; + return this; + } + + public severity(value: number): PatternBuilder { + this.result.severity = value; + return this; + } + + public loop(value: boolean): PatternBuilder { + this.result.loop = value; + return this; + } +} + +function testDefaultProblemMatcher(external: ExternalTaskRunnerConfiguration, resolved: number) { + let reporter = new ProblemReporter(); + let result = parse(external, reporter); + assert.ok(!reporter.receivedMessage); + assert.strictEqual(result.custom.length, 1); + let task = result.custom[0]; + assert.ok(task); + assert.strictEqual(task.problemMatchers.length, resolved); +} + +function testConfiguration(external: ExternalTaskRunnerConfiguration, builder: ConfiguationBuilder): void { + builder.done(); + let reporter = new ProblemReporter(); + let result = parse(external, reporter); + if (reporter.receivedMessage) { + assert.ok(false, reporter.lastMessage); + } + assertConfiguration(result, builder.result); +} + +class TaskGroupMap { + private _store: { [key: string]: Tasks.Task[] }; + + constructor() { + this._store = Object.create(null); + } + + public add(group: string, task: Tasks.Task): void { + let tasks = this._store[group]; + if (!tasks) { + tasks = []; + this._store[group] = tasks; + } + tasks.push(task); + } + + public static assert(actual: TaskGroupMap, expected: TaskGroupMap): void { + let actualKeys = Object.keys(actual._store); + let expectedKeys = Object.keys(expected._store); + if (actualKeys.length === 0 && expectedKeys.length === 0) { + return; + } + assert.strictEqual(actualKeys.length, expectedKeys.length); + actualKeys.forEach(key => assert.ok(expected._store[key])); + expectedKeys.forEach(key => actual._store[key]); + actualKeys.forEach((key) => { + let actualTasks = actual._store[key]; + let expectedTasks = expected._store[key]; + assert.strictEqual(actualTasks.length, expectedTasks.length); + if (actualTasks.length === 1) { + assert.strictEqual(actualTasks[0].name, expectedTasks[0].name); + return; + } + let expectedTaskMap: { [key: string]: boolean } = Object.create(null); + expectedTasks.forEach(task => expectedTaskMap[task.name] = true); + actualTasks.forEach(task => delete expectedTaskMap[task.name]); + assert.strictEqual(Object.keys(expectedTaskMap).length, 0); + }); + } +} + +function assertConfiguration(result: ParseResult, expected: Tasks.Task[]): void { + assert.ok(result.validationStatus.isOK()); + let actual = result.custom; + assert.strictEqual(typeof actual, typeof expected); + if (!actual) { + return; + } + + // We can't compare Ids since the parser uses UUID which are random + // So create a new map using the name. + let actualTasks: { [key: string]: Tasks.Task; } = Object.create(null); + let actualId2Name: { [key: string]: string; } = Object.create(null); + let actualTaskGroups = new TaskGroupMap(); + actual.forEach(task => { + assert.ok(!actualTasks[task.name]); + actualTasks[task.name] = task; + actualId2Name[task._id] = task.name; + if (task.group) { + actualTaskGroups.add(task.group, task); + } + }); + let expectedTasks: { [key: string]: Tasks.Task; } = Object.create(null); + let expectedTaskGroup = new TaskGroupMap(); + expected.forEach(task => { + assert.ok(!expectedTasks[task.name]); + expectedTasks[task.name] = task; + if (task.group) { + expectedTaskGroup.add(task.group, task); + } + }); + let actualKeys = Object.keys(actualTasks); + assert.strictEqual(actualKeys.length, expected.length); + actualKeys.forEach((key) => { + let actualTask = actualTasks[key]; + let expectedTask = expectedTasks[key]; + assert.ok(expectedTask); + assertTask(actualTask, expectedTask); + }); + TaskGroupMap.assert(actualTaskGroups, expectedTaskGroup); +} + +function assertTask(actual: Tasks.Task, expected: Tasks.Task) { + assert.ok(actual._id); + assert.strictEqual(actual.name, expected.name, 'name'); + if (!Tasks.CompositeTask.is(actual) && !Tasks.CompositeTask.is(expected)) { + assertCommandConfiguration(actual.command, expected.command); + } + assert.strictEqual(actual.isBackground, expected.isBackground, 'isBackground'); + assert.strictEqual(typeof actual.problemMatchers, typeof expected.problemMatchers); + assert.strictEqual(actual.promptOnClose, expected.promptOnClose, 'promptOnClose'); + assert.strictEqual(actual.group, expected.group, 'group'); + assert.strictEqual(actual.isDefaultGroupEntry, expected.isDefaultGroupEntry, 'isPrimaryGroupEntry'); + if (actual.problemMatchers && expected.problemMatchers) { + assert.strictEqual(actual.problemMatchers.length, expected.problemMatchers.length); + for (let i = 0; i < actual.problemMatchers.length; i++) { + assertProblemMatcher(actual.problemMatchers[i], expected.problemMatchers[i]); + } + } +} + +function assertCommandConfiguration(actual: Tasks.CommandConfiguration, expected: Tasks.CommandConfiguration) { + assert.strictEqual(typeof actual, typeof expected); + if (actual && expected) { + assertPresentation(actual.presentation, expected.presentation); + assert.strictEqual(actual.name, expected.name, 'name'); + assert.strictEqual(actual.runtime, expected.runtime, 'runtime type'); + assert.strictEqual(actual.suppressTaskName, expected.suppressTaskName, 'suppressTaskName'); + assert.strictEqual(actual.taskSelector, expected.taskSelector, 'taskSelector'); + assert.deepEqual(actual.args, expected.args, 'args'); + assert.strictEqual(typeof actual.options, typeof expected.options); + if (actual.options && expected.options) { + assert.strictEqual(actual.options.cwd, expected.options.cwd, 'cwd'); + assert.strictEqual(typeof actual.options.env, typeof expected.options.env, 'env'); + if (actual.options.env && expected.options.env) { + assert.deepEqual(actual.options.env, expected.options.env, 'env'); + } + } + } +} + +function assertPresentation(actual: Tasks.PresentationOptions, expected: Tasks.PresentationOptions) { + assert.strictEqual(typeof actual, typeof expected); + if (actual && expected) { + assert.strictEqual(actual.echo, expected.echo); + assert.strictEqual(actual.reveal, expected.reveal); + } +} + +function assertProblemMatcher(actual: string | ProblemMatcher, expected: string | ProblemMatcher) { + assert.strictEqual(typeof actual, typeof expected); + if (typeof actual === 'string' && typeof expected === 'string') { + assert.strictEqual(actual, expected, 'Problem matcher references are different'); + return; + } + if (typeof actual !== 'string' && typeof expected !== 'string') { + if (expected.owner === ProblemMatcherBuilder.DEFAULT_UUID) { + try { + UUID.parse(actual.owner); + } catch (err) { + assert.fail(actual.owner, 'Owner must be a UUID'); + } + } else { + assert.strictEqual(actual.owner, expected.owner); + } + assert.strictEqual(actual.applyTo, expected.applyTo); + assert.strictEqual(actual.severity, expected.severity); + assert.strictEqual(actual.fileLocation, expected.fileLocation); + assert.strictEqual(actual.filePrefix, expected.filePrefix); + if (actual.pattern && expected.pattern) { + assertProblemPatterns(actual.pattern, expected.pattern); + } + } +} + +function assertProblemPatterns(actual: ProblemPattern | ProblemPattern[], expected: ProblemPattern | ProblemPattern[]) { + assert.strictEqual(typeof actual, typeof expected); + if (Array.isArray(actual)) { + let actuals = actual; + let expecteds = expected; + assert.strictEqual(actuals.length, expecteds.length); + for (let i = 0; i < actuals.length; i++) { + assertProblemPattern(actuals[i], expecteds[i]); + } + } else { + assertProblemPattern(actual, expected); + } +} + +function assertProblemPattern(actual: ProblemPattern, expected: ProblemPattern) { + assert.equal(actual.regexp.toString(), expected.regexp.toString()); + assert.strictEqual(actual.file, expected.file); + assert.strictEqual(actual.message, expected.message); + if (typeof expected.location !== 'undefined') { + assert.strictEqual(actual.location, expected.location); + } else { + assert.strictEqual(actual.line, expected.line); + assert.strictEqual(actual.character, expected.character); + assert.strictEqual(actual.endLine, expected.endLine); + assert.strictEqual(actual.endCharacter, expected.endCharacter); + } + assert.strictEqual(actual.code, expected.code); + assert.strictEqual(actual.severity, expected.severity); + assert.strictEqual(actual.loop, expected.loop); +} + +suite('Tasks version 0.1.0', () => { + test('tasks: all default', () => { + let builder = new ConfiguationBuilder(); + builder.task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true); + testConfiguration( + { + version: '0.1.0', + command: 'tsc' + }, builder); + }); + + test('tasks: global isShellCommand', () => { + let builder = new ConfiguationBuilder(); + builder.task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + runtime(Tasks.RuntimeType.Shell); + testConfiguration( + { + version: '0.1.0', + command: 'tsc', + isShellCommand: true + }, + builder); + }); + + test('tasks: global show output silent', () => { + let builder = new ConfiguationBuilder(); + builder. + task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + presentation().reveal(Tasks.RevealKind.Silent); + testConfiguration( + { + version: '0.1.0', + command: 'tsc', + showOutput: 'silent' + }, + builder + ); + }); + + test('tasks: global promptOnClose default', () => { + let builder = new ConfiguationBuilder(); + builder.task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true); + testConfiguration( + { + version: '0.1.0', + command: 'tsc', + promptOnClose: true + }, + builder + ); + }); + + test('tasks: global promptOnClose', () => { + let builder = new ConfiguationBuilder(); + builder.task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + promptOnClose(false). + command().suppressTaskName(true); + testConfiguration( + { + version: '0.1.0', + command: 'tsc', + promptOnClose: false + }, + builder + ); + }); + + test('tasks: global promptOnClose default watching', () => { + let builder = new ConfiguationBuilder(); + builder.task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + isBackground(true). + promptOnClose(false). + command().suppressTaskName(true); + testConfiguration( + { + version: '0.1.0', + command: 'tsc', + isWatching: true + }, + builder + ); + }); + + test('tasks: global show output never', () => { + let builder = new ConfiguationBuilder(); + builder. + task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + presentation().reveal(Tasks.RevealKind.Never); + testConfiguration( + { + version: '0.1.0', + command: 'tsc', + showOutput: 'never' + }, + builder + ); + }); + + test('tasks: global echo Command', () => { + let builder = new ConfiguationBuilder(); + builder. + task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + presentation(). + echo(true); + testConfiguration( + { + version: '0.1.0', + command: 'tsc', + echoCommand: true + }, + builder + ); + }); + + test('tasks: global args', () => { + let builder = new ConfiguationBuilder(); + builder. + task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + args(['--p']); + testConfiguration( + { + version: '0.1.0', + command: 'tsc', + args: [ + '--p' + ] + }, + builder + ); + }); + + test('tasks: options - cwd', () => { + let builder = new ConfiguationBuilder(); + builder. + task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + options({ + cwd: 'myPath' + }); + testConfiguration( + { + version: '0.1.0', + command: 'tsc', + options: { + cwd: 'myPath' + } + }, + builder + ); + }); + + test('tasks: options - env', () => { + let builder = new ConfiguationBuilder(); + builder. + task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + options({ cwd: '${workspaceRoot}', env: { key: 'value' } }); + testConfiguration( + { + version: '0.1.0', + command: 'tsc', + options: { + env: { + key: 'value' + } + } + }, + builder + ); + }); + + test('tasks: os windows', () => { + let name: string = Platform.isWindows ? 'tsc.win' : 'tsc'; + let builder = new ConfiguationBuilder(); + builder. + task(name, name). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true); + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + windows: { + command: 'tsc.win' + } + }; + testConfiguration(external, builder); + }); + + test('tasks: os windows & global isShellCommand', () => { + let name: string = Platform.isWindows ? 'tsc.win' : 'tsc'; + let builder = new ConfiguationBuilder(); + builder. + task(name, name). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + runtime(Tasks.RuntimeType.Shell); + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + isShellCommand: true, + windows: { + command: 'tsc.win' + } + }; + testConfiguration(external, builder); + }); + + test('tasks: os mac', () => { + let name: string = Platform.isMacintosh ? 'tsc.osx' : 'tsc'; + let builder = new ConfiguationBuilder(); + builder. + task(name, name). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true); + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + osx: { + command: 'tsc.osx' + } + }; + testConfiguration(external, builder); + }); + + test('tasks: os linux', () => { + let name: string = Platform.isLinux ? 'tsc.linux' : 'tsc'; + let builder = new ConfiguationBuilder(); + builder. + task(name, name). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true); + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + linux: { + command: 'tsc.linux' + } + }; + testConfiguration(external, builder); + }); + + test('tasks: overwrite showOutput', () => { + let builder = new ConfiguationBuilder(); + builder. + task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + presentation().reveal(Platform.isWindows ? Tasks.RevealKind.Always : Tasks.RevealKind.Never); + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + showOutput: 'never', + windows: { + showOutput: 'always' + } + }; + testConfiguration(external, builder); + }); + + test('tasks: overwrite echo Command', () => { + let builder = new ConfiguationBuilder(); + builder. + task('tsc', 'tsc'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + presentation(). + echo(Platform.isWindows ? false : true); + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + echoCommand: true, + windows: { + echoCommand: false + } + }; + testConfiguration(external, builder); + }); + + test('tasks: global problemMatcher one', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + problemMatcher: '$msCompile' + }; + testDefaultProblemMatcher(external, 1); + }); + + test('tasks: global problemMatcher two', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + problemMatcher: ['$eslint-compact', '$msCompile'] + }; + testDefaultProblemMatcher(external, 2); + }); + + test('tasks: task definition', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc').command().args(['$name']); + testConfiguration(external, builder); + }); + + test('tasks: build task', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName', + isBuildCommand: true + } as CustomTask + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc').group(Tasks.TaskGroup.Build).command().args(['$name']); + testConfiguration(external, builder); + }); + + test('tasks: default build task', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'build' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('build', 'tsc').group(Tasks.TaskGroup.Build).command().args(['$name']); + testConfiguration(external, builder); + }); + + test('tasks: test task', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName', + isTestCommand: true + } as CustomTask + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc').group(Tasks.TaskGroup.Test).command().args(['$name']); + testConfiguration(external, builder); + }); + + test('tasks: default test task', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'test' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('test', 'tsc').group(Tasks.TaskGroup.Test).command().args(['$name']); + testConfiguration(external, builder); + }); + + test('tasks: task with values', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'test', + showOutput: 'never', + echoCommand: true, + args: ['--p'], + isWatching: true + } as CustomTask + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('test', 'tsc'). + group(Tasks.TaskGroup.Test). + isBackground(true). + promptOnClose(false). + command().args(['$name', '--p']). + presentation(). + echo(true).reveal(Tasks.RevealKind.Never); + + testConfiguration(external, builder); + }); + + test('tasks: task inherits global values', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + showOutput: 'never', + echoCommand: true, + tasks: [ + { + taskName: 'test' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('test', 'tsc'). + group(Tasks.TaskGroup.Test). + command().args(['$name']).presentation(). + echo(true).reveal(Tasks.RevealKind.Never); + + testConfiguration(external, builder); + }); + + test('tasks: problem matcher default', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName', + problemMatcher: { + pattern: { + regexp: 'abc' + } + } + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + command().args(['$name']).parent. + problemMatcher().pattern(/abc/); + testConfiguration(external, builder); + }); + + test('tasks: problem matcher .* regular expression', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName', + problemMatcher: { + pattern: { + regexp: '.*' + } + } + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + command().args(['$name']).parent. + problemMatcher().pattern(/.*/); + testConfiguration(external, builder); + }); + + test('tasks: problem matcher owner, applyTo, severity and fileLocation', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName', + problemMatcher: { + owner: 'myOwner', + applyTo: 'closedDocuments', + severity: 'warning', + fileLocation: 'absolute', + pattern: { + regexp: 'abc' + } + } + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + command().args(['$name']).parent. + problemMatcher(). + owner('myOwner'). + applyTo(ApplyToKind.closedDocuments). + severity(Severity.Warning). + fileLocation(FileLocationKind.Absolute). + filePrefix(undefined). + pattern(/abc/); + testConfiguration(external, builder); + }); + + test('tasks: problem matcher fileLocation and filePrefix', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName', + problemMatcher: { + fileLocation: ['relative', 'myPath'], + pattern: { + regexp: 'abc' + } + } + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + command().args(['$name']).parent. + problemMatcher(). + fileLocation(FileLocationKind.Relative). + filePrefix('myPath'). + pattern(/abc/); + testConfiguration(external, builder); + }); + + test('tasks: problem pattern location', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName', + problemMatcher: { + pattern: { + regexp: 'abc', + file: 10, + message: 11, + location: 12, + severity: 13, + code: 14 + } + } + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + command().args(['$name']).parent. + problemMatcher(). + pattern(/abc/).file(10).message(11).location(12).severity(13).code(14); + testConfiguration(external, builder); + }); + + test('tasks: problem pattern line & column', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName', + problemMatcher: { + pattern: { + regexp: 'abc', + file: 10, + message: 11, + line: 12, + column: 13, + endLine: 14, + endColumn: 15, + severity: 16, + code: 17 + } + } + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + command().args(['$name']).parent. + problemMatcher(). + pattern(/abc/).file(10).message(11). + line(12).character(13).endLine(14).endCharacter(15). + severity(16).code(17); + testConfiguration(external, builder); + }); + + test('tasks: prompt on close default', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + promptOnClose(true). + command().args(['$name']); + testConfiguration(external, builder); + }); + + test('tasks: prompt on close watching', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName', + isWatching: true + } as CustomTask + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + isBackground(true).promptOnClose(false). + command().args(['$name']); + testConfiguration(external, builder); + }); + + test('tasks: prompt on close set', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskName', + promptOnClose: false + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + promptOnClose(false). + command().args(['$name']); + testConfiguration(external, builder); + }); + + test('tasks: task selector set', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + taskSelector: '/t:', + tasks: [ + { + taskName: 'taskName', + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + command(). + taskSelector('/t:'). + args(['/t:taskName']); + testConfiguration(external, builder); + }); + + test('tasks: suppress task name set', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + suppressTaskName: false, + tasks: [ + { + taskName: 'taskName', + suppressTaskName: true + } as CustomTask + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + command().suppressTaskName(true); + testConfiguration(external, builder); + }); + + test('tasks: suppress task name inherit', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + suppressTaskName: true, + tasks: [ + { + taskName: 'taskName' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskName', 'tsc'). + command().suppressTaskName(true); + testConfiguration(external, builder); + }); + + test('tasks: two tasks', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskNameOne' + }, + { + taskName: 'taskNameTwo' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskNameOne', 'tsc'). + command().args(['$name']); + builder.task('taskNameTwo', 'tsc'). + command().args(['$name']); + testConfiguration(external, builder); + }); + + test('tasks: with command', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + tasks: [ + { + taskName: 'taskNameOne', + command: 'tsc' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskNameOne', 'tsc').command().suppressTaskName(true); + testConfiguration(external, builder); + }); + + test('tasks: two tasks with command', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + tasks: [ + { + taskName: 'taskNameOne', + command: 'tsc' + }, + { + taskName: 'taskNameTwo', + command: 'dir' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskNameOne', 'tsc').command().suppressTaskName(true); + builder.task('taskNameTwo', 'dir').command().suppressTaskName(true); + testConfiguration(external, builder); + }); + + test('tasks: with command and args', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + tasks: [ + { + taskName: 'taskNameOne', + command: 'tsc', + isShellCommand: true, + args: ['arg'], + options: { + cwd: 'cwd', + env: { + env: 'env' + } + } + } as CustomTask + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskNameOne', 'tsc').command().suppressTaskName(true). + runtime(Tasks.RuntimeType.Shell).args(['arg']).options({ cwd: 'cwd', env: { env: 'env' } }); + testConfiguration(external, builder); + }); + + test('tasks: with command os specific', () => { + let name: string = Platform.isWindows ? 'tsc.win' : 'tsc'; + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + tasks: [ + { + taskName: 'taskNameOne', + command: 'tsc', + windows: { + command: 'tsc.win' + } + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskNameOne', name).command().suppressTaskName(true); + testConfiguration(external, builder); + }); + + test('tasks: with Windows specific args', () => { + let args: string[] = Platform.isWindows ? ['arg1', 'arg2'] : ['arg1']; + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + tasks: [ + { + taskName: 'tsc', + command: 'tsc', + args: ['arg1'], + windows: { + args: ['arg2'] + } + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('tsc', 'tsc').command().suppressTaskName(true).args(args); + testConfiguration(external, builder); + }); + + test('tasks: with Linux specific args', () => { + let args: string[] = Platform.isLinux ? ['arg1', 'arg2'] : ['arg1']; + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + tasks: [ + { + taskName: 'tsc', + command: 'tsc', + args: ['arg1'], + linux: { + args: ['arg2'] + } + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('tsc', 'tsc').command().suppressTaskName(true).args(args); + testConfiguration(external, builder); + }); + + test('tasks: global command and task command properties', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + tasks: [ + { + taskName: 'taskNameOne', + isShellCommand: true, + } as CustomTask + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskNameOne', 'tsc').command().runtime(Tasks.RuntimeType.Shell).args(['$name']); + testConfiguration(external, builder); + }); + + test('tasks: global and tasks args', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + args: ['global'], + tasks: [ + { + taskName: 'taskNameOne', + args: ['local'] + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskNameOne', 'tsc').command().args(['global', '$name', 'local']); + testConfiguration(external, builder); + }); + + test('tasks: global and tasks args with task selector', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + command: 'tsc', + args: ['global'], + taskSelector: '/t:', + tasks: [ + { + taskName: 'taskNameOne', + args: ['local'] + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('taskNameOne', 'tsc').command().taskSelector('/t:').args(['global', '/t:taskNameOne', 'local']); + testConfiguration(external, builder); + }); +}); + +suite('Tasks version 2.0.0', () => { + test('Build workspace task', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '2.0.0', + tasks: [ + { + taskName: 'dir', + command: 'dir', + type: 'shell', + group: 'build' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('dir', 'dir'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + runtime(Tasks.RuntimeType.Shell). + presentation().echo(true); + testConfiguration(external, builder); + }); + test('Global group none', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '2.0.0', + command: 'dir', + type: 'shell', + group: 'none' + }; + let builder = new ConfiguationBuilder(); + builder.task('dir', 'dir'). + command().suppressTaskName(true). + runtime(Tasks.RuntimeType.Shell). + presentation().echo(true); + testConfiguration(external, builder); + }); + test('Global group build', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '2.0.0', + command: 'dir', + type: 'shell', + group: 'build' + }; + let builder = new ConfiguationBuilder(); + builder.task('dir', 'dir'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + runtime(Tasks.RuntimeType.Shell). + presentation().echo(true); + testConfiguration(external, builder); + }); + test('Global group default build', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '2.0.0', + command: 'dir', + type: 'shell', + group: { kind: 'build', isDefault: true } + }; + let builder = new ConfiguationBuilder(); + builder.task('dir', 'dir'). + group(Tasks.TaskGroup.Build). + isPrimary(true). + command().suppressTaskName(true). + runtime(Tasks.RuntimeType.Shell). + presentation().echo(true); + testConfiguration(external, builder); + }); + test('Local group none', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '2.0.0', + tasks: [ + { + taskName: 'dir', + command: 'dir', + type: 'shell', + group: 'none' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('dir', 'dir'). + command().suppressTaskName(true). + runtime(Tasks.RuntimeType.Shell). + presentation().echo(true); + testConfiguration(external, builder); + }); + test('Local group build', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '2.0.0', + tasks: [ + { + taskName: 'dir', + command: 'dir', + type: 'shell', + group: 'build' + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('dir', 'dir'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + runtime(Tasks.RuntimeType.Shell). + presentation().echo(true); + testConfiguration(external, builder); + }); + test('Local group default build', () => { + let external: ExternalTaskRunnerConfiguration = { + version: '2.0.0', + tasks: [ + { + taskName: 'dir', + command: 'dir', + type: 'shell', + group: { kind: 'build', isDefault: true } + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('dir', 'dir'). + group(Tasks.TaskGroup.Build). + isPrimary(true). + command().suppressTaskName(true). + runtime(Tasks.RuntimeType.Shell). + presentation().echo(true); + testConfiguration(external, builder); + }); +}); + +suite('Bugs / regression tests', () => { + test('Bug 19548', () => { + if (Platform.isLinux) { + return; + } + let external: ExternalTaskRunnerConfiguration = { + version: '0.1.0', + windows: { + command: 'powershell', + options: { + cwd: '${workspaceRoot}' + }, + tasks: [ + { + taskName: 'composeForDebug', + suppressTaskName: true, + args: [ + '-ExecutionPolicy', + 'RemoteSigned', + '.\\dockerTask.ps1', + '-ComposeForDebug', + '-Environment', + 'debug' + ], + isBuildCommand: false, + showOutput: 'always', + echoCommand: true + } as CustomTask + ] + }, + osx: { + command: '/bin/bash', + options: { + cwd: '${workspaceRoot}' + }, + tasks: [ + { + taskName: 'composeForDebug', + suppressTaskName: true, + args: [ + '-c', + './dockerTask.sh composeForDebug debug' + ], + isBuildCommand: false, + showOutput: 'always' + } as CustomTask + ] + } + }; + let builder = new ConfiguationBuilder(); + if (Platform.isWindows) { + builder.task('composeForDebug', 'powershell'). + command().suppressTaskName(true). + args(['-ExecutionPolicy', 'RemoteSigned', '.\\dockerTask.ps1', '-ComposeForDebug', '-Environment', 'debug']). + options({ cwd: '${workspaceRoot}' }). + presentation().echo(true).reveal(Tasks.RevealKind.Always); + testConfiguration(external, builder); + } else if (Platform.isMacintosh) { + builder.task('composeForDebug', '/bin/bash'). + command().suppressTaskName(true). + args(['-c', './dockerTask.sh composeForDebug debug']). + options({ cwd: '${workspaceRoot}' }). + presentation().reveal(Tasks.RevealKind.Always); + testConfiguration(external, builder); + } + }); + + test('Bug 28489', () => { + let external = { + version: '0.1.0', + command: '', + isShellCommand: true, + args: [''], + showOutput: 'always', + 'tasks': [ + { + taskName: 'build', + command: 'bash', + args: [ + 'build.sh' + ] + } + ] + }; + let builder = new ConfiguationBuilder(); + builder.task('build', 'bash'). + group(Tasks.TaskGroup.Build). + command().suppressTaskName(true). + args(['build.sh']). + runtime(Tasks.RuntimeType.Shell); + testConfiguration(external, builder); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts b/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts new file mode 100644 index 0000000000..f188e60332 --- /dev/null +++ b/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import strings = require('vs/base/common/strings'); +import scorer = require('vs/base/common/scorer'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { Mode, IEntryRunContext, IAutoFocus, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen'; +import { QuickOpenModel, QuickOpenEntry } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenHandler } from 'vs/workbench/browser/quickopen'; +import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { ContributableActionProvider } from 'vs/workbench/browser/actions'; + +export class TerminalEntry extends QuickOpenEntry { + + constructor( + private label: string, + private terminalService: ITerminalService + ) { + super(); + } + + public getLabel(): string { + return this.label; + } + + public getAriaLabel(): string { + return nls.localize('termEntryAriaLabel', "{0}, terminal picker", this.getLabel()); + } + + public run(mode: Mode, context: IEntryRunContext): boolean { + if (mode === Mode.OPEN) { + setTimeout(() => { + this.terminalService.setActiveInstanceByIndex(parseInt(this.label.split(':')[0], 10) - 1); + this.terminalService.showPanel(true); + }, 0); + return true; + } + + return super.run(mode, context); + } +} + +export class CreateTerminal extends QuickOpenEntry { + + constructor( + private label: string, + private terminalService: ITerminalService + ) { + super(); + } + + public getLabel(): string { + return this.label; + } + + public getAriaLabel(): string { + return nls.localize('termCreateEntryAriaLabel', "{0}, create new terminal", this.getLabel()); + } + + public run(mode: Mode, context: IEntryRunContext): boolean { + if (mode === Mode.OPEN) { + setTimeout(() => { + const newTerminal = this.terminalService.createInstance(); + this.terminalService.setActiveInstance(newTerminal); + this.terminalService.showPanel(true); + }, 0); + return true; + } + + return super.run(mode, context); + } +} + +export class TerminalPickerHandler extends QuickOpenHandler { + + constructor( + @ITerminalService private terminalService: ITerminalService, + @IPanelService private panelService: IPanelService + ) { + super(); + } + + public getResults(searchValue: string): TPromise { + searchValue = searchValue.trim(); + const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase(); + + const terminalEntries: QuickOpenEntry[] = this.getTerminals(); + terminalEntries.push(new CreateTerminal(nls.localize("'workbench.action.terminal.newplus", "$(plus) Create New Integrated Terminal"), this.terminalService)); + + const entries = terminalEntries.filter(e => { + if (!searchValue) { + return true; + } + + if (!scorer.matches(e.getLabel(), normalizedSearchValueLowercase)) { + return false; + } + + const { labelHighlights, descriptionHighlights } = QuickOpenEntry.highlight(e, searchValue); + e.setHighlights(labelHighlights, descriptionHighlights); + + return true; + }); + + return TPromise.as(new QuickOpenModel(entries, new ContributableActionProvider())); + } + + private getTerminals(): TerminalEntry[] { + const terminals = this.terminalService.getInstanceLabels(); + const terminalEntries = terminals.map(terminal => { + return new TerminalEntry(terminal, this.terminalService); + }); + return terminalEntries; + } + + public getAutoFocus(searchValue: string, context: { model: IModel, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus { + return { + autoFocusFirstEntry: !!searchValue || !!context.quickNavigateConfiguration + }; + } + + public getEmptyLabel(searchString: string): string { + if (searchString.length > 0) { + return nls.localize('noTerminalsMatching', "No terminals matching"); + } + return nls.localize('noTerminalsFound', "No terminals open"); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/browser/terminalWidgetManager.ts b/src/vs/workbench/parts/terminal/browser/terminalWidgetManager.ts new file mode 100644 index 0000000000..96c35feecc --- /dev/null +++ b/src/vs/workbench/parts/terminal/browser/terminalWidgetManager.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITerminalConfigHelper } from 'vs/workbench/parts/terminal/common/terminal'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; + +export class TerminalWidgetManager { + private _container: HTMLElement; + private _xtermViewport: HTMLElement; + + private _messageWidget: MessageWidget; + private _messageListeners: IDisposable[] = []; + + constructor( + private _configHelper: ITerminalConfigHelper, + terminalWrapper: HTMLElement + ) { + this._container = document.createElement('div'); + this._container.classList.add('terminal-widget-overlay'); + terminalWrapper.appendChild(this._container); + + this._initTerminalHeightWatcher(terminalWrapper); + } + + private _initTerminalHeightWatcher(terminalWrapper: HTMLElement) { + // Watch the xterm.js viewport for style changes and do a layout if it changes + this._xtermViewport = terminalWrapper.querySelector('.xterm-viewport'); + const mutationObserver = new MutationObserver(() => this._refreshHeight()); + mutationObserver.observe(this._xtermViewport, { attributes: true, attributeFilter: ['style'] }); + } + + public showMessage(left: number, top: number, text: string): void { + dispose(this._messageWidget); + this._messageListeners = dispose(this._messageListeners); + this._messageWidget = new MessageWidget(this._container, left, top, text); + } + + public closeMessage(): void { + this._messageListeners = dispose(this._messageListeners); + if (this._messageWidget) { + this._messageListeners.push(MessageWidget.fadeOut(this._messageWidget)); + } + } + + private _refreshHeight(): void { + this._container.style.height = this._xtermViewport.style.height; + } +} + +class MessageWidget { + private _domNode: HTMLDivElement; + + public get left(): number { return this._left; } + public get top(): number { return this._top; } + public get text(): string { return this._text; } + public get domNode(): HTMLElement { return this._domNode; } + + public static fadeOut(messageWidget: MessageWidget): IDisposable { + let handle: number; + const dispose = () => { + messageWidget.dispose(); + clearTimeout(handle); + messageWidget.domNode.removeEventListener('animationend', dispose); + }; + handle = setTimeout(dispose, 110); + messageWidget.domNode.addEventListener('animationend', dispose); + messageWidget.domNode.classList.add('fadeOut'); + return { dispose }; + } + + constructor( + private _container: HTMLElement, + private _left: number, + private _top: number, + private _text: string + ) { + this._domNode = document.createElement('div'); + this._domNode.style.position = 'absolute'; + this._domNode.style.left = `${_left}px`; + this._domNode.style.bottom = `${_container.offsetHeight - _top}px`; + this._domNode.classList.add('terminal-message-widget', 'fadeIn'); + this._domNode.textContent = _text; + this._container.appendChild(this._domNode); + } + + public dispose(): void { + if (this.domNode.parentElement === this._container) { + this._container.removeChild(this.domNode); + } + } +} diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts new file mode 100644 index 0000000000..01185de293 --- /dev/null +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -0,0 +1,379 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event from 'vs/base/common/event'; +import platform = require('vs/base/common/platform'); +import { IDisposable } from 'vs/base/common/lifecycle'; +import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const TERMINAL_PANEL_ID = 'workbench.panel.terminal'; + +export const TERMINAL_SERVICE_ID = 'terminalService'; + +export const TERMINAL_DEFAULT_RIGHT_CLICK_COPY_PASTE = platform.isWindows; + +/** A context key that is set when the integrated terminal has focus. */ +export const KEYBINDING_CONTEXT_TERMINAL_FOCUS = new RawContextKey('terminalFocus', undefined); +/** A context key that is set when the integrated terminal does not have focus. */ +export const KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_TERMINAL_FOCUS.toNegated(); + +/** A keybinding context key that is set when the integrated terminal has text selected. */ +export const KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED = new RawContextKey('terminalTextSelected', undefined); +/** A keybinding context key that is set when the integrated terminal does not have text selected. */ +export const KEYBINDING_CONTEXT_TERMINAL_TEXT_NOT_SELECTED: ContextKeyExpr = KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED.toNegated(); + +/** A context key that is set when the find widget in integrated terminal is visible. */ +export const KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE = new RawContextKey('terminalFindWidgetVisible', undefined); +/** A context key that is set when the find widget in integrated terminal is not visible. */ +export const KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.toNegated(); +/** A context key that is set when the find widget find input in integrated terminal is focused. */ +export const KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED = new RawContextKey('terminalFindWidgetInputFocused', false); +/** A context key that is set when the find widget find input in integrated terminal is not focused. */ +export const KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED.toNegated(); + +export const IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY = 'terminal.integrated.isWorkspaceShellAllowed'; +export const NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY = 'terminal.integrated.neverSuggestSelectWindowsShell'; + +export const ITerminalService = createDecorator(TERMINAL_SERVICE_ID); + +export const TerminalCursorStyle = { + BLOCK: 'block', + LINE: 'line', + UNDERLINE: 'underline' +}; + +export interface ITerminalConfiguration { + shell: { + linux: string; + osx: string; + windows: string; + }; + shellArgs: { + linux: string[]; + osx: string[]; + windows: string[]; + }; + enableBold: boolean; + rightClickCopyPaste: boolean; + cursorBlinking: boolean; + cursorStyle: string; + fontFamily: string; + fontLigatures: boolean; + fontSize: number; + lineHeight: number; + setLocaleVariables: boolean; + scrollback: number; + commandsToSkipShell: string[]; + cwd: string; + confirmOnExit: boolean; + env: { + linux: { [key: string]: string }; + osx: { [key: string]: string }; + windows: { [key: string]: string }; + }; +} + +export interface ITerminalConfigHelper { + config: ITerminalConfiguration; + getFont(): ITerminalFont; + /** + * Merges the default shell path and args into the provided launch configuration + */ + mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig): void; + /** Sets whether a workspace shell configuration is allowed or not */ + setWorkspaceShellAllowed(isAllowed: boolean): void; +} + +export interface ITerminalFont { + fontFamily: string; + fontSize: string; + lineHeight: number; + charWidth: number; + charHeight: number; +} + +export interface IShellLaunchConfig { + /** The name of the terminal, if this is not set the name of the process will be used. */ + name?: string; + /** The shell executable (bash, cmd, etc.). */ + executable?: string; + /** + * The CLI arguments to use with executable, a string[] is in argv format and will be escaped, + * a string is in "CommandLine" pre-escaped format and will be used as is. The string option is + * only supported on Windows and will throw an exception if used on macOS or Linux. + */ + args?: string[] | string; + /** + * The current working directory of the terminal, this overrides the `terminal.integrated.cwd` + * settings key. + */ + cwd?: string; + /** + * A custom environment for the terminal, if this is not set the environment will be inherited + * from the VS Code process. + */ + env?: { [key: string]: string }; + /** + * Whether to ignore a custom cwd from the `terminal.integrated.cwd` settings key (eg. if the + * shell is being launched by an extension). + */ + ignoreConfigurationCwd?: boolean; + + /** Whether to wait for a key press before closing the terminal. */ + waitOnExit?: boolean | string; + + /** + * A string including ANSI escape sequences that will be written to the terminal emulator + * _before_ the terminal process has launched, a trailing \n is added at the end of the string. + * This allows for example the terminal instance to display a styled message as the first line + * of the terminal. Use \x1b over \033 or \e for the escape control character. + */ + initialText?: string; +} + +export interface ITerminalService { + _serviceBrand: any; + + activeTerminalInstanceIndex: number; + configHelper: ITerminalConfigHelper; + onActiveInstanceChanged: Event; + onInstanceDisposed: Event; + onInstanceProcessIdReady: Event; + onInstanceData: Event<{ instance: ITerminalInstance, data: string }>; + onInstancesChanged: Event; + onInstanceTitleChanged: Event; + terminalInstances: ITerminalInstance[]; + + createInstance(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance; + getInstanceFromId(terminalId: number): ITerminalInstance; + getInstanceFromIndex(terminalIndex: number): ITerminalInstance; + getInstanceLabels(): string[]; + getActiveInstance(): ITerminalInstance; + setActiveInstance(terminalInstance: ITerminalInstance): void; + setActiveInstanceByIndex(terminalIndex: number): void; + setActiveInstanceToNext(): void; + setActiveInstanceToPrevious(): void; + getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance; + + showPanel(focus?: boolean): TPromise; + hidePanel(): void; + focusFindWidget(): TPromise; + hideFindWidget(): void; + showNextFindTermFindWidget(): void; + showPreviousFindTermFindWidget(): void; + + setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; + updateConfig(): void; + selectDefaultWindowsShell(): TPromise; + setWorkspaceShellAllowed(isAllowed: boolean): void; +} + +export interface ITerminalInstance { + /** + * The ID of the terminal instance, this is an arbitrary number only used to identify the + * terminal instance. + */ + id: number; + + /** + * The process ID of the shell process. + */ + processId: number; + + /** + * An event that fires when the terminal instance's title changes. + */ + onTitleChanged: Event; + + /** + * An event that fires when the terminal instance is disposed. + */ + onDisposed: Event; + + /** + * The title of the terminal. This is either title or the process currently running or an + * explicit name given to the terminal instance through the extension API. + * + * @readonly + */ + title: string; + + /** + * The focus state of the terminal before exiting. + * + * @readonly + */ + hadFocusOnExit: boolean; + + /** + * False when the title is set by an API or the user. We check this to make sure we + * do not override the title when the process title changes in the terminal. + */ + isTitleSetByProcess: boolean; + + /** + * Dispose the terminal instance, removing it from the panel/service and freeing up resources. + */ + dispose(): void; + + /** + * Registers a link matcher, allowing custom link patterns to be matched and handled. + * @param regex The regular expression the search for, specifically this searches the + * textContent of the rows. You will want to use \s to match a space ' ' character for example. + * @param handler The callback when the link is called. + * @param matchIndex The index of the link from the regex.match(html) call. This defaults to 0 + * (for regular expressions without capture groups). + * @param validationCallback A callback which can be used to validate the link after it has been + * added to the DOM. + * @return The ID of the new matcher, this can be used to deregister. + */ + registerLinkMatcher(regex: RegExp, handler: (url: string) => void, matchIndex?: number, validationCallback?: (uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => void): number; + + /** + * Deregisters a link matcher if it has been registered. + * @param matcherId The link matcher's ID (returned after register) + * @return Whether a link matcher was found and deregistered. + */ + deregisterLinkMatcher(matcherId: number): void; + + /** + * Check if anything is selected in terminal. + */ + hasSelection(): boolean; + + /** + * Copies the terminal selection to the clipboard. + */ + copySelection(): void; + + /** + * Current selection in the terminal. + */ + readonly selection: string | undefined; + + /** + * Clear current selection. + */ + clearSelection(): void; + + /** + * Select all text in the terminal. + */ + selectAll(): void; + + /** + * Find the next instance of the term + */ + findNext(term: string): boolean; + + /** + * Find the previous instance of the term + */ + findPrevious(term: string): boolean; + + /** + * Notifies the terminal that the find widget's focus state has been changed. + */ + notifyFindWidgetFocusChanged(isFocused: boolean): void; + + /** + * Focuses the terminal instance. + * + * @param focus Force focus even if there is a selection. + */ + focus(force?: boolean): void; + + /** + * Focuses and pastes the contents of the clipboard into the terminal instance. + */ + paste(): void; + + /** + * Send text to the terminal instance. The text is written to the stdin of the underlying pty + * process (shell) of the terminal instance. + * + * @param text The text to send. + * @param addNewLine Whether to add a new line to the text being sent, this is normally + * required to run a command in the terminal. The character(s) added are \n or \r\n + * depending on the platform. This defaults to `true`. + */ + sendText(text: string, addNewLine: boolean): void; + + /** Scroll the terminal buffer down 1 line. */ + scrollDownLine(): void; + /** Scroll the terminal buffer down 1 page. */ + scrollDownPage(): void; + /** Scroll the terminal buffer to the bottom. */ + scrollToBottom(): void; + /** Scroll the terminal buffer up 1 line. */ + scrollUpLine(): void; + /** Scroll the terminal buffer up 1 page. */ + scrollUpPage(): void; + /** Scroll the terminal buffer to the top. */ + scrollToTop(): void; + + /** + * Clears the terminal buffer, leaving only the prompt line. + */ + clear(): void; + + /** + * Attaches the terminal instance to an element on the DOM, before this is called the terminal + * instance process may run in the background but cannot be displayed on the UI. + * + * @param container The element to attach the terminal instance to. + */ + attachToElement(container: HTMLElement): void; + + /** + * Updates the configuration of the terminal instance. + */ + updateConfig(): void; + + /** + * Configure the dimensions of the terminal instance. + * + * @param dimension The dimensions of the container. + */ + layout(dimension: { width: number, height: number }): void; + + /** + * Sets whether the terminal instance's element is visible in the DOM. + * + * @param visible Whether the element is visible. + */ + setVisible(visible: boolean): void; + + /** + * Attach a listener to the data stream from the terminal's pty process. + * + * @param listener The listener function which takes the processes' data stream (including + * ANSI escape sequences). + */ + onData(listener: (data: string) => void): IDisposable; + + /** + * Attach a listener that fires when the terminal's pty process exits. + * + * @param listener The listener function which takes the processes' exit code, an exit code of + * null means the process was killed as a result of the ITerminalInstance being disposed. + */ + onExit(listener: (exitCode: number) => void): IDisposable; + + /** + * Immediately kills the terminal's current pty process and launches a new one to replace it. + * + * @param shell The new launch configuration. + */ + reuseTerminal(shell?: IShellLaunchConfig): void; + + /** + * Sets the title of the terminal instance. + */ + setTitle(title: string, eventFromProcess: boolean): void; +} diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts new file mode 100644 index 0000000000..787b8fb28e --- /dev/null +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -0,0 +1,234 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as errors from 'vs/base/common/errors'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID } from 'vs/workbench/parts/terminal/common/terminal'; +import { TPromise } from 'vs/base/common/winjs.base'; + +export abstract class TerminalService implements ITerminalService { + public _serviceBrand: any; + + protected _isShuttingDown: boolean; + protected _terminalFocusContextKey: IContextKey; + protected _findWidgetVisible: IContextKey; + protected _terminalContainer: HTMLElement; + protected _onInstancesChanged: Emitter; + protected _onInstanceDisposed: Emitter; + protected _onInstanceProcessIdReady: Emitter; + protected _onInstanceData: Emitter<{ instance: ITerminalInstance, data: string }>; + protected _onInstanceTitleChanged: Emitter; + protected _terminalInstances: ITerminalInstance[]; + + private _activeTerminalInstanceIndex: number; + private _onActiveInstanceChanged: Emitter; + + public get activeTerminalInstanceIndex(): number { return this._activeTerminalInstanceIndex; } + public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } + public get onInstanceDisposed(): Event { return this._onInstanceDisposed.event; } + public get onInstanceProcessIdReady(): Event { return this._onInstanceProcessIdReady.event; } + public get onInstanceData(): Event<{ instance: ITerminalInstance, data: string }> { return this._onInstanceData.event; } + public get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } + public get onInstancesChanged(): Event { return this._onInstancesChanged.event; } + public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } + + public abstract get configHelper(): ITerminalConfigHelper; + + constructor( + @IContextKeyService private _contextKeyService: IContextKeyService, + @IConfigurationService private _configurationService: IConfigurationService, + @IPanelService protected _panelService: IPanelService, + @IPartService private _partService: IPartService, + @ILifecycleService lifecycleService: ILifecycleService + ) { + this._terminalInstances = []; + this._activeTerminalInstanceIndex = 0; + this._isShuttingDown = false; + + this._onActiveInstanceChanged = new Emitter(); + this._onInstanceDisposed = new Emitter(); + this._onInstanceProcessIdReady = new Emitter(); + this._onInstanceData = new Emitter<{ instance: ITerminalInstance, data: string }>(); + this._onInstanceTitleChanged = new Emitter(); + this._onInstancesChanged = new Emitter(); + + this._configurationService.onDidUpdateConfiguration(() => this.updateConfig()); + lifecycleService.onWillShutdown(event => event.veto(this._onWillShutdown())); + this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); + this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService); + this.onInstanceDisposed((terminalInstance) => { this._removeInstance(terminalInstance); }); + } + + protected abstract _showTerminalCloseConfirmation(): boolean; + public abstract createInstance(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance; + public abstract getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance; + public abstract selectDefaultWindowsShell(): TPromise; + public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; + + private _onWillShutdown(): boolean { + if (this.terminalInstances.length === 0) { + // No terminal instances, don't veto + return false; + } + + if (this.configHelper.config.confirmOnExit) { + // veto if configured to show confirmation and the user choosed not to exit + if (this._showTerminalCloseConfirmation()) { + return true; + } + } + + // Dispose all terminal instances and don't veto + this._isShuttingDown = true; + this.terminalInstances.forEach(instance => { + instance.dispose(); + }); + return false; + } + + public getInstanceLabels(): string[] { + return this._terminalInstances.map((instance, index) => `${index + 1}: ${instance.title}`); + } + + private _removeInstance(terminalInstance: ITerminalInstance): void { + let index = this.terminalInstances.indexOf(terminalInstance); + let wasActiveInstance = terminalInstance === this.getActiveInstance(); + if (index !== -1) { + this.terminalInstances.splice(index, 1); + } + if (wasActiveInstance && this.terminalInstances.length > 0) { + let newIndex = index < this.terminalInstances.length ? index : this.terminalInstances.length - 1; + this.setActiveInstanceByIndex(newIndex); + if (terminalInstance.hadFocusOnExit) { + this.getActiveInstance().focus(true); + } + } + // Hide the panel if there are no more instances, provided that VS Code is not shutting + // down. When shutting down the panel is locked in place so that it is restored upon next + // launch. + if (this.terminalInstances.length === 0 && !this._isShuttingDown) { + this.hidePanel(); + } + this._onInstancesChanged.fire(); + if (wasActiveInstance) { + this._onActiveInstanceChanged.fire(); + } + } + + public getActiveInstance(): ITerminalInstance { + if (this.activeTerminalInstanceIndex < 0 || this.activeTerminalInstanceIndex >= this.terminalInstances.length) { + return null; + + } + return this.terminalInstances[this.activeTerminalInstanceIndex]; + } + + public getInstanceFromId(terminalId: number): ITerminalInstance { + return this.terminalInstances[this._getIndexFromId(terminalId)]; + } + + public getInstanceFromIndex(terminalIndex: number): ITerminalInstance { + return this.terminalInstances[terminalIndex]; + } + + public setActiveInstance(terminalInstance: ITerminalInstance): void { + this.setActiveInstanceByIndex(this._getIndexFromId(terminalInstance.id)); + } + + public setActiveInstanceByIndex(terminalIndex: number): void { + if (terminalIndex >= this._terminalInstances.length) { + return; + } + const didInstanceChange = this._activeTerminalInstanceIndex !== terminalIndex; + this._activeTerminalInstanceIndex = terminalIndex; + this._terminalInstances.forEach((terminalInstance, i) => { + terminalInstance.setVisible(i === terminalIndex); + }); + // Only fire the event if there was a change + if (didInstanceChange) { + this._onActiveInstanceChanged.fire(); + } + } + + public setActiveInstanceToNext(): void { + if (this.terminalInstances.length <= 1) { + return; + } + let newIndex = this._activeTerminalInstanceIndex + 1; + if (newIndex >= this.terminalInstances.length) { + newIndex = 0; + } + this.setActiveInstanceByIndex(newIndex); + } + + public setActiveInstanceToPrevious(): void { + if (this.terminalInstances.length <= 1) { + return; + } + let newIndex = this._activeTerminalInstanceIndex - 1; + if (newIndex < 0) { + newIndex = this.terminalInstances.length - 1; + } + this.setActiveInstanceByIndex(newIndex); + } + + public showPanel(focus?: boolean): TPromise { + return new TPromise((complete) => { + let panel = this._panelService.getActivePanel(); + if (!panel || panel.getId() !== TERMINAL_PANEL_ID) { + return this._panelService.openPanel(TERMINAL_PANEL_ID, focus).then(() => { + if (focus) { + this.getActiveInstance().focus(true); + } + complete(void 0); + }); + } else { + if (focus) { + this.getActiveInstance().focus(true); + } + complete(void 0); + } + return undefined; + }); + } + + public hidePanel(): void { + const panel = this._panelService.getActivePanel(); + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + this._partService.setPanelHidden(true).done(undefined, errors.onUnexpectedError); + } + } + + public abstract focusFindWidget(): TPromise; + public abstract hideFindWidget(): void; + public abstract showNextFindTermFindWidget(): void; + public abstract showPreviousFindTermFindWidget(): void; + + private _getIndexFromId(terminalId: number): number { + let terminalIndex = -1; + this.terminalInstances.forEach((terminalInstance, i) => { + if (terminalInstance.id === terminalId) { + terminalIndex = i; + } + }); + if (terminalIndex === -1) { + throw new Error(`Terminal with ID ${terminalId} does not exist (has it already been disposed?)`); + } + return terminalIndex; + } + + public updateConfig(): void { + this.terminalInstances.forEach(instance => instance.updateConfig()); + } + + public setWorkspaceShellAllowed(isAllowed: boolean): void { + this.configHelper.setWorkspaceShellAllowed(isAllowed); + } +} diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/configure-inverse.svg b/src/vs/workbench/parts/terminal/electron-browser/media/configure-inverse.svg new file mode 100644 index 0000000000..61baaea2b8 --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/media/configure-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/configure.svg b/src/vs/workbench/parts/terminal/electron-browser/media/configure.svg new file mode 100644 index 0000000000..3dec2ba50f --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/media/configure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/kill-inverse.svg b/src/vs/workbench/parts/terminal/electron-browser/media/kill-inverse.svg new file mode 100644 index 0000000000..c26674c56d --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/media/kill-inverse.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/kill.svg b/src/vs/workbench/parts/terminal/electron-browser/media/kill.svg new file mode 100644 index 0000000000..8338bc8187 --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/media/kill.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/new-inverse.svg b/src/vs/workbench/parts/terminal/electron-browser/media/new-inverse.svg new file mode 100644 index 0000000000..d8583f46e7 --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/media/new-inverse.svg @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/new.svg b/src/vs/workbench/parts/terminal/electron-browser/media/new.svg new file mode 100644 index 0000000000..efa4e8121f --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/media/new.svg @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/scrollbar.css b/src/vs/workbench/parts/terminal/electron-browser/media/scrollbar.css new file mode 100644 index 0000000000..dd3b7ad8c2 --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/media/scrollbar.css @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .panel.integrated-terminal .xterm-viewport { + /* Use the hack presented in http://stackoverflow.com/a/38748186/1156119 to get opacity transitions working on the scrollbar */ + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + transition: background-color 800ms linear; +} + +.monaco-workbench .panel.integrated-terminal .xterm-viewport::-webkit-scrollbar { + width: 10px; +} + +.monaco-workbench .panel.integrated-terminal .xterm-viewport::-webkit-scrollbar-track { + opacity: 0; +} + +.monaco-workbench .panel.integrated-terminal .xterm-viewport::-webkit-scrollbar-thumb { + background-color: inherit; +} + +.monaco-workbench .panel.integrated-terminal .xterm.focus .xterm-viewport, +.monaco-workbench .panel.integrated-terminal .xterm:focus .xterm-viewport, +.monaco-workbench .panel.integrated-terminal .xterm:hover .xterm-viewport { + transition: opacity 100ms linear; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover { + transition: opacity 0ms linear; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:window-inactive { + background-color: inherit; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css b/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css new file mode 100644 index 0000000000..ac797209ee --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .panel.integrated-terminal { + align-content: flex-start; + align-items: baseline; + display: flex; + flex-direction: column; + background-color: transparent!important; + user-select: initial; + position: relative; + overflow: hidden; +} + +.monaco-workbench .panel.integrated-terminal .terminal-outer-container { + height: 100%; + padding-left: 20px; /*Don't use right padding in case xterm.js misbehaves*/ + padding-bottom: 2px; + width: 100%; + box-sizing: border-box; +} + +.monaco-workbench .panel.integrated-terminal .terminal-wrapper { + display: none; +} +.monaco-workbench .panel.integrated-terminal .terminal-wrapper.active { + display: block; + position: absolute; + bottom: 2px; /* Matches padding-bottom on .terminal-outer-container */ + top: 0; +} + +.monaco-workbench .panel.integrated-terminal .xterm a:not(.xterm-invalid-link) { + /* To support message box sizing */ + position: relative; +} + +.monaco-workbench .panel.integrated-terminal .terminal-wrapper > div { + height: 100%; +} + +.monaco-workbench .panel.integrated-terminal .xterm-viewport { + /* Align the viewport to the bottom of the panel, just like the terminal */ + position: absolute; + right: -20px; + bottom: 0; + left: 0; +} + +.monaco-workbench .panel.integrated-terminal { + font-variant-ligatures: none; +} + +.monaco-workbench .panel.integrated-terminal.enable-ligatures { + font-variant-ligatures: normal; +} + + +.monaco-workbench .panel.integrated-terminal.disable-bold .xterm-bold { + font-weight: normal !important; +} + +.monaco-workbench .panel.integrated-terminal .xterm a.active { + cursor: pointer; + text-decoration: underline; +} + +/* Terminal actions */ + +/* Light theme */ +.monaco-workbench .terminal-action.kill { background: url('kill.svg') center center no-repeat; } +.monaco-workbench .terminal-action.new { background: url('new.svg') center center no-repeat; } +/* Dark theme / HC theme */ +.vs-dark .monaco-workbench .terminal-action.kill, .hc-black .monaco-workbench .terminal-action.kill { background: url('kill-inverse.svg') center center no-repeat; } +.vs-dark .monaco-workbench .terminal-action.new, .hc-black .monaco-workbench .terminal-action.new { background: url('new-inverse.svg') center center no-repeat; } + +.vs-dark .monaco-workbench.mac .panel.integrated-terminal .xterm-rows, +.hc-black .monaco-workbench.mac .panel.integrated-terminal .xterm-rows { + cursor: -webkit-image-set(url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=') 1x, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC') 2x) 5 8, text; +} + +.monaco-workbench .quick-open-terminal-configure { + background-image: url('configure.svg'); +} + +.vs-dark .monaco-workbench .quick-open-terminal-configure, +.hc-black .monaco-workbench .quick-open-terminal-configure { + background-image: url('configure-inverse.svg'); +} diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/widgets.css b/src/vs/workbench/parts/terminal/electron-browser/media/widgets.css new file mode 100644 index 0000000000..f2ecaa888f --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/media/widgets.css @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .terminal-widget-overlay { + position: absolute; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; +} + +.monaco-workbench .terminal-message-widget { + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; + font-size: 12px; + line-height: 19px; + padding: 4px 5px; + animation: fadein 100ms linear; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css b/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css new file mode 100644 index 0000000000..69b1ffd1c2 --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css @@ -0,0 +1,2103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .panel.integrated-terminal .xterm { + position: relative; + height: 100%; + user-select: none; +} + +.monaco-workbench .panel.integrated-terminal .xterm:focus { + /* Hide outline when focus jumps from xterm to the text area */ + outline: none; +} + +.hc-black .monaco-workbench .panel.integrated-terminal .xterm.focus::before, +.hc-black .monaco-workbench .panel.integrated-terminal .xterm:focus::before { + display: block; + content: ""; + border: 2px solid #f38518; + position: absolute; + left: -5px; + top: 0; + right: -5px; + bottom: 0; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-helpers { + position: absolute; + top: 0; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-helper-textarea { + position: absolute; + /* + * HACK: to fix IE's blinking cursor + * Move textarea out of the screen to the far left, so that the cursor is not visible. + */ + left: -9999em; + top: 0; + opacity: 0; + width: 0; + height: 0; + z-index: -10; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-helper-textarea:focus { + /* Override the general vscode style applies `opacity:1!important` to textareas */ + opacity: 0 !important; +} + +.monaco-workbench .panel.integrated-terminal .xterm a { + color: inherit; + cursor: inherit; + text-decoration: none; +} + +.monaco-workbench .panel.integrated-terminal .xterm:not(.focus):not(:focus) .terminal-cursor { + background-color: transparent; + outline-width: 1px; + outline-style: solid; + outline-offset: -1px; +} + +.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-blink-on:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus .terminal-cursor, +.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-blink-on:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar):focus .terminal-cursor { + background-color: transparent; + color: inherit; +} + +.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-bar .terminal-cursor, +.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-underline .terminal-cursor { + position: relative; +} +.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-bar .terminal-cursor::before, +.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-underline .terminal-cursor::before { + content: ""; + display: block; + position: absolute; +} +.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-bar .terminal-cursor::before { + top: 0; + bottom: 0; + left: 0; + width: 1px; +} +.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-underline .terminal-cursor::before { + bottom: 0; + left: 0; + right: 0; + height: 1px; +} +.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-bar.focus.xterm-cursor-blink.xterm-cursor-blink-on .terminal-cursor::before, +.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-underline.focus.xterm-cursor-blink.xterm-cursor-blink-on .terminal-cursor::before { + background-color: transparent !important; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-viewport { + overflow-y: scroll; +} + +.terminal .xterm-wide-char, +.terminal .xterm-normal-char { + display: inline-block; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-rows { + position: absolute; + left: 0; + bottom: 0; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-rows > div { + /* Lines containing spans and text nodes ocassionally wrap despite being the same width (#327) */ + white-space: nowrap; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-scroll-area { + visibility: hidden; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-char-measure-element { + display: inline-block; + visibility: hidden; + position: absolute; + left: -9999em; +} + +.monaco-workbench .panel.integrated-terminal .xterm.enable-mouse-events { + /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ + cursor: default; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-selection { + position: absolute; + left: 0; + bottom: 0; + z-index: 1; + opacity: 0.3; + pointer-events: none; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-selection div { + position: absolute; +} +/* TODO: Remove in favor of theme keys, see #28397*/ +.monaco-workbench .panel.integrated-terminal .xterm .xterm-selection div { background-color: #000; } +.vs-dark .monaco-workbench .panel.integrated-terminal .xterm .xterm-selection div { background-color: #FFF; } +.hc-black .monaco-workbench .panel.integrated-terminal .xterm .xterm-selection div { background-color: #FFF; } + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bold { + font-weight: bold; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-underline { + text-decoration: underline; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-blink { + text-decoration: blink; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-hidden { + visibility: hidden; +} + +/* Composition view */ + +.terminal .composition-view { + background: #000; + color: #FFF; + display: none; + position: absolute; + white-space: nowrap; + z-index: 1; +} + +.terminal .composition-view.active { + display: block; +} + +/* Terminal colors 16-255 */ + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-16 { + color: #000000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-16 { + background-color: #000000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-17 { + color: #00005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-17 { + background-color: #00005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-18 { + color: #000087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-18 { + background-color: #000087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-19 { + color: #0000af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-19 { + background-color: #0000af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-20 { + color: #0000d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-20 { + background-color: #0000d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-21 { + color: #0000ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-21 { + background-color: #0000ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-22 { + color: #005f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-22 { + background-color: #005f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-23 { + color: #005f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-23 { + background-color: #005f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-24 { + color: #005f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-24 { + background-color: #005f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-25 { + color: #005faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-25 { + background-color: #005faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-26 { + color: #005fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-26 { + background-color: #005fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-27 { + color: #005fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-27 { + background-color: #005fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-28 { + color: #008700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-28 { + background-color: #008700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-29 { + color: #00875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-29 { + background-color: #00875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-30 { + color: #008787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-30 { + background-color: #008787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-31 { + color: #0087af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-31 { + background-color: #0087af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-32 { + color: #0087d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-32 { + background-color: #0087d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-33 { + color: #0087ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-33 { + background-color: #0087ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-34 { + color: #00af00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-34 { + background-color: #00af00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-35 { + color: #00af5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-35 { + background-color: #00af5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-36 { + color: #00af87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-36 { + background-color: #00af87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-37 { + color: #00afaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-37 { + background-color: #00afaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-38 { + color: #00afd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-38 { + background-color: #00afd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-39 { + color: #00afff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-39 { + background-color: #00afff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-40 { + color: #00d700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-40 { + background-color: #00d700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-41 { + color: #00d75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-41 { + background-color: #00d75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-42 { + color: #00d787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-42 { + background-color: #00d787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-43 { + color: #00d7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-43 { + background-color: #00d7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-44 { + color: #00d7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-44 { + background-color: #00d7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-45 { + color: #00d7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-45 { + background-color: #00d7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-46 { + color: #00ff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-46 { + background-color: #00ff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-47 { + color: #00ff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-47 { + background-color: #00ff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-48 { + color: #00ff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-48 { + background-color: #00ff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-49 { + color: #00ffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-49 { + background-color: #00ffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-50 { + color: #00ffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-50 { + background-color: #00ffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-51 { + color: #00ffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-51 { + background-color: #00ffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-52 { + color: #5f0000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-52 { + background-color: #5f0000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-53 { + color: #5f005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-53 { + background-color: #5f005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-54 { + color: #5f0087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-54 { + background-color: #5f0087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-55 { + color: #5f00af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-55 { + background-color: #5f00af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-56 { + color: #5f00d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-56 { + background-color: #5f00d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-57 { + color: #5f00ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-57 { + background-color: #5f00ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-58 { + color: #5f5f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-58 { + background-color: #5f5f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-59 { + color: #5f5f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-59 { + background-color: #5f5f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-60 { + color: #5f5f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-60 { + background-color: #5f5f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-61 { + color: #5f5faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-61 { + background-color: #5f5faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-62 { + color: #5f5fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-62 { + background-color: #5f5fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-63 { + color: #5f5fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-63 { + background-color: #5f5fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-64 { + color: #5f8700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-64 { + background-color: #5f8700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-65 { + color: #5f875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-65 { + background-color: #5f875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-66 { + color: #5f8787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-66 { + background-color: #5f8787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-67 { + color: #5f87af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-67 { + background-color: #5f87af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-68 { + color: #5f87d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-68 { + background-color: #5f87d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-69 { + color: #5f87ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-69 { + background-color: #5f87ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-70 { + color: #5faf00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-70 { + background-color: #5faf00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-71 { + color: #5faf5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-71 { + background-color: #5faf5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-72 { + color: #5faf87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-72 { + background-color: #5faf87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-73 { + color: #5fafaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-73 { + background-color: #5fafaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-74 { + color: #5fafd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-74 { + background-color: #5fafd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-75 { + color: #5fafff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-75 { + background-color: #5fafff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-76 { + color: #5fd700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-76 { + background-color: #5fd700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-77 { + color: #5fd75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-77 { + background-color: #5fd75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-78 { + color: #5fd787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-78 { + background-color: #5fd787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-79 { + color: #5fd7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-79 { + background-color: #5fd7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-80 { + color: #5fd7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-80 { + background-color: #5fd7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-81 { + color: #5fd7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-81 { + background-color: #5fd7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-82 { + color: #5fff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-82 { + background-color: #5fff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-83 { + color: #5fff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-83 { + background-color: #5fff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-84 { + color: #5fff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-84 { + background-color: #5fff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-85 { + color: #5fffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-85 { + background-color: #5fffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-86 { + color: #5fffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-86 { + background-color: #5fffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-87 { + color: #5fffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-87 { + background-color: #5fffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-88 { + color: #870000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-88 { + background-color: #870000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-89 { + color: #87005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-89 { + background-color: #87005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-90 { + color: #870087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-90 { + background-color: #870087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-91 { + color: #8700af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-91 { + background-color: #8700af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-92 { + color: #8700d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-92 { + background-color: #8700d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-93 { + color: #8700ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-93 { + background-color: #8700ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-94 { + color: #875f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-94 { + background-color: #875f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-95 { + color: #875f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-95 { + background-color: #875f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-96 { + color: #875f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-96 { + background-color: #875f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-97 { + color: #875faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-97 { + background-color: #875faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-98 { + color: #875fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-98 { + background-color: #875fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-99 { + color: #875fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-99 { + background-color: #875fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-100 { + color: #878700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-100 { + background-color: #878700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-101 { + color: #87875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-101 { + background-color: #87875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-102 { + color: #878787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-102 { + background-color: #878787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-103 { + color: #8787af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-103 { + background-color: #8787af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-104 { + color: #8787d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-104 { + background-color: #8787d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-105 { + color: #8787ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-105 { + background-color: #8787ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-106 { + color: #87af00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-106 { + background-color: #87af00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-107 { + color: #87af5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-107 { + background-color: #87af5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-108 { + color: #87af87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-108 { + background-color: #87af87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-109 { + color: #87afaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-109 { + background-color: #87afaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-110 { + color: #87afd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-110 { + background-color: #87afd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-111 { + color: #87afff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-111 { + background-color: #87afff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-112 { + color: #87d700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-112 { + background-color: #87d700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-113 { + color: #87d75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-113 { + background-color: #87d75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-114 { + color: #87d787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-114 { + background-color: #87d787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-115 { + color: #87d7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-115 { + background-color: #87d7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-116 { + color: #87d7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-116 { + background-color: #87d7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-117 { + color: #87d7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-117 { + background-color: #87d7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-118 { + color: #87ff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-118 { + background-color: #87ff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-119 { + color: #87ff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-119 { + background-color: #87ff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-120 { + color: #87ff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-120 { + background-color: #87ff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-121 { + color: #87ffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-121 { + background-color: #87ffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-122 { + color: #87ffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-122 { + background-color: #87ffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-123 { + color: #87ffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-123 { + background-color: #87ffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-124 { + color: #af0000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-124 { + background-color: #af0000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-125 { + color: #af005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-125 { + background-color: #af005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-126 { + color: #af0087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-126 { + background-color: #af0087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-127 { + color: #af00af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-127 { + background-color: #af00af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-128 { + color: #af00d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-128 { + background-color: #af00d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-129 { + color: #af00ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-129 { + background-color: #af00ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-130 { + color: #af5f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-130 { + background-color: #af5f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-131 { + color: #af5f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-131 { + background-color: #af5f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-132 { + color: #af5f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-132 { + background-color: #af5f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-133 { + color: #af5faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-133 { + background-color: #af5faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-134 { + color: #af5fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-134 { + background-color: #af5fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-135 { + color: #af5fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-135 { + background-color: #af5fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-136 { + color: #af8700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-136 { + background-color: #af8700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-137 { + color: #af875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-137 { + background-color: #af875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-138 { + color: #af8787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-138 { + background-color: #af8787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-139 { + color: #af87af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-139 { + background-color: #af87af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-140 { + color: #af87d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-140 { + background-color: #af87d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-141 { + color: #af87ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-141 { + background-color: #af87ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-142 { + color: #afaf00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-142 { + background-color: #afaf00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-143 { + color: #afaf5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-143 { + background-color: #afaf5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-144 { + color: #afaf87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-144 { + background-color: #afaf87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-145 { + color: #afafaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-145 { + background-color: #afafaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-146 { + color: #afafd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-146 { + background-color: #afafd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-147 { + color: #afafff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-147 { + background-color: #afafff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-148 { + color: #afd700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-148 { + background-color: #afd700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-149 { + color: #afd75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-149 { + background-color: #afd75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-150 { + color: #afd787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-150 { + background-color: #afd787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-151 { + color: #afd7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-151 { + background-color: #afd7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-152 { + color: #afd7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-152 { + background-color: #afd7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-153 { + color: #afd7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-153 { + background-color: #afd7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-154 { + color: #afff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-154 { + background-color: #afff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-155 { + color: #afff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-155 { + background-color: #afff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-156 { + color: #afff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-156 { + background-color: #afff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-157 { + color: #afffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-157 { + background-color: #afffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-158 { + color: #afffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-158 { + background-color: #afffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-159 { + color: #afffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-159 { + background-color: #afffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-160 { + color: #d70000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-160 { + background-color: #d70000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-161 { + color: #d7005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-161 { + background-color: #d7005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-162 { + color: #d70087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-162 { + background-color: #d70087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-163 { + color: #d700af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-163 { + background-color: #d700af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-164 { + color: #d700d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-164 { + background-color: #d700d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-165 { + color: #d700ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-165 { + background-color: #d700ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-166 { + color: #d75f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-166 { + background-color: #d75f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-167 { + color: #d75f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-167 { + background-color: #d75f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-168 { + color: #d75f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-168 { + background-color: #d75f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-169 { + color: #d75faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-169 { + background-color: #d75faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-170 { + color: #d75fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-170 { + background-color: #d75fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-171 { + color: #d75fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-171 { + background-color: #d75fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-172 { + color: #d78700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-172 { + background-color: #d78700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-173 { + color: #d7875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-173 { + background-color: #d7875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-174 { + color: #d78787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-174 { + background-color: #d78787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-175 { + color: #d787af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-175 { + background-color: #d787af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-176 { + color: #d787d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-176 { + background-color: #d787d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-177 { + color: #d787ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-177 { + background-color: #d787ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-178 { + color: #d7af00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-178 { + background-color: #d7af00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-179 { + color: #d7af5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-179 { + background-color: #d7af5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-180 { + color: #d7af87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-180 { + background-color: #d7af87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-181 { + color: #d7afaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-181 { + background-color: #d7afaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-182 { + color: #d7afd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-182 { + background-color: #d7afd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-183 { + color: #d7afff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-183 { + background-color: #d7afff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-184 { + color: #d7d700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-184 { + background-color: #d7d700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-185 { + color: #d7d75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-185 { + background-color: #d7d75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-186 { + color: #d7d787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-186 { + background-color: #d7d787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-187 { + color: #d7d7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-187 { + background-color: #d7d7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-188 { + color: #d7d7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-188 { + background-color: #d7d7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-189 { + color: #d7d7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-189 { + background-color: #d7d7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-190 { + color: #d7ff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-190 { + background-color: #d7ff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-191 { + color: #d7ff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-191 { + background-color: #d7ff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-192 { + color: #d7ff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-192 { + background-color: #d7ff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-193 { + color: #d7ffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-193 { + background-color: #d7ffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-194 { + color: #d7ffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-194 { + background-color: #d7ffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-195 { + color: #d7ffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-195 { + background-color: #d7ffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-196 { + color: #ff0000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-196 { + background-color: #ff0000; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-197 { + color: #ff005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-197 { + background-color: #ff005f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-198 { + color: #ff0087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-198 { + background-color: #ff0087; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-199 { + color: #ff00af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-199 { + background-color: #ff00af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-200 { + color: #ff00d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-200 { + background-color: #ff00d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-201 { + color: #ff00ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-201 { + background-color: #ff00ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-202 { + color: #ff5f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-202 { + background-color: #ff5f00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-203 { + color: #ff5f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-203 { + background-color: #ff5f5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-204 { + color: #ff5f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-204 { + background-color: #ff5f87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-205 { + color: #ff5faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-205 { + background-color: #ff5faf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-206 { + color: #ff5fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-206 { + background-color: #ff5fd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-207 { + color: #ff5fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-207 { + background-color: #ff5fff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-208 { + color: #ff8700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-208 { + background-color: #ff8700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-209 { + color: #ff875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-209 { + background-color: #ff875f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-210 { + color: #ff8787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-210 { + background-color: #ff8787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-211 { + color: #ff87af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-211 { + background-color: #ff87af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-212 { + color: #ff87d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-212 { + background-color: #ff87d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-213 { + color: #ff87ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-213 { + background-color: #ff87ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-214 { + color: #ffaf00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-214 { + background-color: #ffaf00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-215 { + color: #ffaf5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-215 { + background-color: #ffaf5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-216 { + color: #ffaf87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-216 { + background-color: #ffaf87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-217 { + color: #ffafaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-217 { + background-color: #ffafaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-218 { + color: #ffafd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-218 { + background-color: #ffafd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-219 { + color: #ffafff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-219 { + background-color: #ffafff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-220 { + color: #ffd700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-220 { + background-color: #ffd700; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-221 { + color: #ffd75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-221 { + background-color: #ffd75f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-222 { + color: #ffd787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-222 { + background-color: #ffd787; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-223 { + color: #ffd7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-223 { + background-color: #ffd7af; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-224 { + color: #ffd7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-224 { + background-color: #ffd7d7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-225 { + color: #ffd7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-225 { + background-color: #ffd7ff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-226 { + color: #ffff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-226 { + background-color: #ffff00; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-227 { + color: #ffff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-227 { + background-color: #ffff5f; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-228 { + color: #ffff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-228 { + background-color: #ffff87; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-229 { + color: #ffffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-229 { + background-color: #ffffaf; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-230 { + color: #ffffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-230 { + background-color: #ffffd7; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-231 { + color: #ffffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-231 { + background-color: #ffffff; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-232 { + color: #080808; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-232 { + background-color: #080808; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-233 { + color: #121212; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-233 { + background-color: #121212; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-234 { + color: #1c1c1c; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-234 { + background-color: #1c1c1c; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-235 { + color: #262626; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-235 { + background-color: #262626; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-236 { + color: #303030; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-236 { + background-color: #303030; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-237 { + color: #3a3a3a; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-237 { + background-color: #3a3a3a; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-238 { + color: #444444; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-238 { + background-color: #444444; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-239 { + color: #4e4e4e; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-239 { + background-color: #4e4e4e; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-240 { + color: #585858; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-240 { + background-color: #585858; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-241 { + color: #626262; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-241 { + background-color: #626262; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-242 { + color: #6c6c6c; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-242 { + background-color: #6c6c6c; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-243 { + color: #767676; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-243 { + background-color: #767676; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-244 { + color: #808080; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-244 { + background-color: #808080; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-245 { + color: #8a8a8a; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-245 { + background-color: #8a8a8a; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-246 { + color: #949494; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-246 { + background-color: #949494; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-247 { + color: #9e9e9e; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-247 { + background-color: #9e9e9e; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-248 { + color: #a8a8a8; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-248 { + background-color: #a8a8a8; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-249 { + color: #b2b2b2; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-249 { + background-color: #b2b2b2; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-250 { + color: #bcbcbc; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-250 { + background-color: #bcbcbc; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-251 { + color: #c6c6c6; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-251 { + background-color: #c6c6c6; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-252 { + color: #d0d0d0; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-252 { + background-color: #d0d0d0; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-253 { + color: #dadada; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-253 { + background-color: #dadada; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-254 { + color: #e4e4e4; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-254 { + background-color: #e4e4e4; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-255 { + color: #eeeeee; +} + +.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-255 { + background-color: #eeeeee; +} diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts new file mode 100644 index 0000000000..7b040be5a7 --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -0,0 +1,360 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/scrollbar'; +import 'vs/css!./media/terminal'; +import 'vs/css!./media/xterm'; +import 'vs/css!./media/widgets'; +import * as debugActions from 'vs/workbench/parts/debug/browser/debugActions'; +import * as nls from 'vs/nls'; +import * as panel from 'vs/workbench/browser/panel'; +import * as platform from 'vs/base/common/platform'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, TERMINAL_DEFAULT_RIGHT_CLICK_COPY_PASTE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TerminalCursorStyle } from 'vs/workbench/parts/terminal/common/terminal'; +import { TERMINAL_DEFAULT_SHELL_LINUX, TERMINAL_DEFAULT_SHELL_OSX, TERMINAL_DEFAULT_SHELL_WINDOWS } from 'vs/workbench/parts/terminal/electron-browser/terminal'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KillTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, FocusTerminalAtIndexAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, ShowNextFindTermTerminalFindWidgetAction, ShowPreviousFindTermTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { TerminalService } from 'vs/workbench/parts/terminal/electron-browser/terminalService'; +import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { OpenNextRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction, FocusActiveGroupAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction } from 'vs/workbench/browser/parts/editor/editorActions'; +import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { registerColors } from './terminalColorRegistry'; +import { NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction } from 'vs/workbench/electron-browser/actions'; +import { QUICKOPEN_ACTION_ID, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; +import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; +import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; + +const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); + +const inTerminalsPicker = 'inTerminalPicker'; + +quickOpenRegistry.registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/terminal/browser/terminalQuickOpen', + 'TerminalPickerHandler', + TERMINAL_PICKER_PREFIX, + inTerminalsPicker, + nls.localize('quickOpen.terminal', "Show All Opened Terminals") + ) +); + +const quickOpenNavigateNextInTerminalPickerId = 'workbench.action.quickOpenNavigateNextInTerminalPicker'; +CommandsRegistry.registerCommand( + quickOpenNavigateNextInTerminalPickerId, { handler: getQuickNavigateHandler(quickOpenNavigateNextInTerminalPickerId, true) }); + +const quickOpenNavigatePreviousInTerminalPickerId = 'workbench.action.quickOpenNavigatePreviousInTerminalPicker'; +CommandsRegistry.registerCommand( + quickOpenNavigatePreviousInTerminalPickerId, { handler: getQuickNavigateHandler(quickOpenNavigatePreviousInTerminalPickerId, false) }); + + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenTermAction, QuickOpenTermAction.ID, QuickOpenTermAction.LABEL), 'Quick Open Terminal'); +const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); +actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionTermContributor); + +let configurationRegistry = Registry.as(Extensions.Configuration); +configurationRegistry.registerConfiguration({ + 'id': 'terminal', + 'order': 100, + 'title': nls.localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"), + 'type': 'object', + 'properties': { + 'terminal.integrated.shell.linux': { + 'description': nls.localize('terminal.integrated.shell.linux', "The path of the shell that the terminal uses on Linux."), + 'type': 'string', + 'default': TERMINAL_DEFAULT_SHELL_LINUX + }, + 'terminal.integrated.shellArgs.linux': { + 'description': nls.localize('terminal.integrated.shellArgs.linux', "The command line arguments to use when on the Linux terminal."), + 'type': 'array', + 'items': { + 'type': 'string' + }, + 'default': [] + }, + 'terminal.integrated.shell.osx': { + 'description': nls.localize('terminal.integrated.shell.osx', "The path of the shell that the terminal uses on OS X."), + 'type': 'string', + 'default': TERMINAL_DEFAULT_SHELL_OSX + }, + 'terminal.integrated.shellArgs.osx': { + 'description': nls.localize('terminal.integrated.shellArgs.osx', "The command line arguments to use when on the OS X terminal."), + 'type': 'array', + 'items': { + 'type': 'string' + }, + // Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This + // is the reason terminals on macOS typically run login shells by default which set up + // the environment. See http://unix.stackexchange.com/a/119675/115410 + 'default': ['-l'] + }, + 'terminal.integrated.shell.windows': { + 'description': nls.localize('terminal.integrated.shell.windows', "The path of the shell that the terminal uses on Windows. When using shells shipped with Windows (cmd, PowerShell or Bash on Ubuntu)."), + 'type': 'string', + 'default': TERMINAL_DEFAULT_SHELL_WINDOWS + }, + 'terminal.integrated.shellArgs.windows': { + 'description': nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal."), + 'type': 'array', + 'items': { + 'type': 'string' + }, + 'default': [] + }, + 'terminal.integrated.rightClickCopyPaste': { + 'description': nls.localize('terminal.integrated.rightClickCopyPaste', "When set, this will prevent the context menu from appearing when right clicking within the terminal, instead it will copy when there is a selection and paste when there is no selection."), + 'type': 'boolean', + 'default': TERMINAL_DEFAULT_RIGHT_CLICK_COPY_PASTE + }, + 'terminal.integrated.fontFamily': { + 'description': nls.localize('terminal.integrated.fontFamily', "Controls the font family of the terminal, this defaults to editor.fontFamily's value."), + 'type': 'string' + }, + 'terminal.integrated.fontLigatures': { + 'description': nls.localize('terminal.integrated.fontLigatures', "Controls whether font ligatures are enabled in the terminal."), + 'type': 'boolean', + 'default': false + }, + 'terminal.integrated.fontSize': { + 'description': nls.localize('terminal.integrated.fontSize', "Controls the font size in pixels of the terminal."), + 'type': 'number', + 'default': EDITOR_FONT_DEFAULTS.fontSize + }, + 'terminal.integrated.lineHeight': { + 'description': nls.localize('terminal.integrated.lineHeight', "Controls the line height of the terminal, this number is multipled by the terminal font size to get the actual line-height in pixels."), + 'type': 'number', + 'default': 1.2 + }, + 'terminal.integrated.enableBold': { + 'type': 'boolean', + 'description': nls.localize('terminal.integrated.enableBold', "Whether to enable bold text within the terminal, this requires support from the terminal shell."), + 'default': true + }, + 'terminal.integrated.cursorBlinking': { + 'description': nls.localize('terminal.integrated.cursorBlinking', "Controls whether the terminal cursor blinks."), + 'type': 'boolean', + 'default': false + }, + 'terminal.integrated.cursorStyle': { + 'description': nls.localize('terminal.integrated.cursorStyle', "Controls the style of terminal cursor."), + 'enum': [TerminalCursorStyle.BLOCK, TerminalCursorStyle.LINE, TerminalCursorStyle.UNDERLINE], + 'default': TerminalCursorStyle.BLOCK + }, + 'terminal.integrated.scrollback': { + 'description': nls.localize('terminal.integrated.scrollback', "Controls the maximum amount of lines the terminal keeps in its buffer."), + 'type': 'number', + 'default': 1000 + }, + 'terminal.integrated.setLocaleVariables': { + 'description': nls.localize('terminal.integrated.setLocaleVariables', "Controls whether locale variables are set at startup of the terminal, this defaults to true on OS X, false on other platforms."), + 'type': 'boolean', + 'default': platform.isMacintosh + }, + 'terminal.integrated.cwd': { + 'description': nls.localize('terminal.integrated.cwd', "An explicit start path where the terminal will be launched, this is used as the current working directory (cwd) for the shell process. This may be particularly useful in workspace settings if the root directory is not a convenient cwd."), + 'type': 'string', + 'default': undefined + }, + 'terminal.integrated.confirmOnExit': { + 'description': nls.localize('terminal.integrated.confirmOnExit', "Whether to confirm on exit if there are active terminal sessions."), + 'type': 'boolean', + 'default': false + }, + 'terminal.integrated.commandsToSkipShell': { + 'description': nls.localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell and instead always be handled by Code. This allows the use of keybindings that would normally be consumed by the shell to act the same as when the terminal is not focused, for example ctrl+p to launch Quick Open."), + 'type': 'array', + 'items': { + 'type': 'string' + }, + 'default': [ + ToggleTabFocusModeAction.ID, + FocusActiveGroupAction.ID, + QUICKOPEN_ACTION_ID, + ShowAllCommandsAction.ID, + CreateNewTerminalAction.ID, + CopyTerminalSelectionAction.ID, + KillTerminalAction.ID, + FocusActiveTerminalAction.ID, + FocusPreviousTerminalAction.ID, + FocusNextTerminalAction.ID, + FocusTerminalAtIndexAction.getId(1), + FocusTerminalAtIndexAction.getId(2), + FocusTerminalAtIndexAction.getId(3), + FocusTerminalAtIndexAction.getId(4), + FocusTerminalAtIndexAction.getId(5), + FocusTerminalAtIndexAction.getId(6), + FocusTerminalAtIndexAction.getId(7), + FocusTerminalAtIndexAction.getId(8), + FocusTerminalAtIndexAction.getId(9), + TerminalPasteAction.ID, + RunSelectedTextInTerminalAction.ID, + RunActiveFileInTerminalAction.ID, + ToggleTerminalAction.ID, + ScrollDownTerminalAction.ID, + ScrollDownPageTerminalAction.ID, + ScrollToBottomTerminalAction.ID, + ScrollUpTerminalAction.ID, + ScrollUpPageTerminalAction.ID, + ScrollToTopTerminalAction.ID, + ClearTerminalAction.ID, + debugActions.StartAction.ID, + debugActions.StopAction.ID, + debugActions.RunAction.ID, + debugActions.RestartAction.ID, + debugActions.ContinueAction.ID, + debugActions.PauseAction.ID, + OpenNextRecentlyUsedEditorInGroupAction.ID, + OpenPreviousRecentlyUsedEditorInGroupAction.ID, + FocusFirstGroupAction.ID, + FocusSecondGroupAction.ID, + FocusThirdGroupAction.ID, + SelectAllTerminalAction.ID, + FocusTerminalFindWidgetAction.ID, + HideTerminalFindWidgetAction.ID, + ShowPreviousFindTermTerminalFindWidgetAction.ID, + ShowNextFindTermTerminalFindWidgetAction.ID, + NavigateUpAction.ID, + NavigateDownAction.ID, + NavigateRightAction.ID, + NavigateLeftAction.ID, + DeleteWordLeftTerminalAction.ID, + DeleteWordRightTerminalAction.ID, + 'workbench.action.quickOpenView' + ].sort() + }, + 'terminal.integrated.env.osx': { + 'description': nls.localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the VS Code process to be used by the terminal on OS X"), + 'type': 'object', + 'default': {} + }, + 'terminal.integrated.env.linux': { + 'description': nls.localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Linux"), + 'type': 'object', + 'default': {} + }, + 'terminal.integrated.env.windows': { + 'description': nls.localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Windows"), + 'type': 'object', + 'default': {} + } + } +}); + +registerSingleton(ITerminalService, TerminalService); + +(Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( + 'vs/workbench/parts/terminal/electron-browser/terminalPanel', + 'TerminalPanel', + TERMINAL_PANEL_ID, + nls.localize('terminal', "Terminal"), + 'terminal', + 40, + ToggleTerminalAction.ID +)); + +// On mac cmd+` is reserved to cycle between windows, that's why the keybindings use WinCtrl +const category = nls.localize('terminalCategory', "Terminal"); +let actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.LABEL), 'Terminal: Kill the Active Terminal Instance', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, CopyTerminalSelectionAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C } +}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Copy Selection', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKTICK, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_BACKTICK } +}), 'Terminal: Create New Integrated Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveTerminalAction, FocusActiveTerminalAction.ID, FocusActiveTerminalAction.LABEL), 'Terminal: Focus Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextTerminalAction, FocusNextTerminalAction.ID, FocusNextTerminalAction.LABEL), 'Terminal: Focus Next Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousTerminalAction, FocusPreviousTerminalAction.ID, FocusPreviousTerminalAction.LABEL), 'Terminal: Focus Previous Terminal', category); +for (let i = 1; i < 10; i++) { + actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalAtIndexAction, FocusTerminalAtIndexAction.getId(i), FocusTerminalAtIndexAction.getLabel(i)), 'Terminal: Focus Terminal ' + i, category); +} +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V }, + // Don't apply to Mac since cmd+v works + mac: { primary: null } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL, { + // Don't use ctrl+a by default as that would override the common go to start + // of prompt shell binding + primary: null, + // Technically this doesn't need to be here as it will fall back to this + // behavior anyway when handed to xterm.js, having this handled by VS Code + // makes it easier for users to see how it works though. + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_A } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select All', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunSelectedTextInTerminalAction, RunSelectedTextInTerminalAction.ID, RunSelectedTextInTerminalAction.LABEL), 'Terminal: Run Selected Text In Active Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunActiveFileInTerminalAction, RunActiveFileInTerminalAction.ID, RunActiveFileInTerminalAction.LABEL), 'Terminal: Run Active File In Active Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTerminalAction, ToggleTerminalAction.ID, ToggleTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.US_BACKTICK, + mac: { primary: KeyMod.WinCtrl | KeyCode.US_BACKTICK } +}), 'View: Toggle Integrated Terminal', nls.localize('viewCategory', "View")); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownTerminalAction, ScrollDownTerminalAction.ID, ScrollDownTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.DownArrow, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Line)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownPageTerminalAction, ScrollDownPageTerminalAction.ID, ScrollDownPageTerminalAction.LABEL, { + primary: KeyMod.Shift | KeyCode.PageDown, + mac: { primary: KeyCode.PageDown } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Page)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToBottomTerminalAction, ScrollToBottomTerminalAction.ID, ScrollToBottomTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.End, + linux: { primary: KeyMod.Shift | KeyCode.End } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Bottom', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpTerminalAction, ScrollUpTerminalAction.ID, ScrollUpTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.UpArrow, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Line)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpPageTerminalAction, ScrollUpPageTerminalAction.ID, ScrollUpPageTerminalAction.LABEL, { + primary: KeyMod.Shift | KeyCode.PageUp, + mac: { primary: KeyCode.PageUp } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Page)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToTopTerminalAction, ScrollToTopTerminalAction.ID, ScrollToTopTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.Home, + linux: { primary: KeyMod.Shift | KeyCode.Home } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Top', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_K, + linux: { primary: null } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingsRegistry.WEIGHT.workbenchContrib(1)), 'Terminal: Clear', category); +if (platform.isWindows) { + actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); +} +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(AllowWorkspaceShellTerminalCommand, AllowWorkspaceShellTerminalCommand.ID, AllowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Allow Workspace Shell Configuration', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisallowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand.ID, DisallowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Disallow Workspace Shell Configuration', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RenameTerminalAction, RenameTerminalAction.ID, RenameTerminalAction.LABEL), 'Terminal: Rename', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Find Widget', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(HideTerminalFindWidgetAction, HideTerminalFindWidgetAction.ID, HideTerminalFindWidgetAction.LABEL, { + primary: KeyCode.Escape, + secondary: [KeyCode.Shift | KeyCode.Escape] +}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Hide Find Widget', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextFindTermTerminalFindWidgetAction, ShowNextFindTermTerminalFindWidgetAction.ID, ShowNextFindTermTerminalFindWidgetAction.LABEL, { + primary: KeyMod.Alt | KeyCode.DownArrow +}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Find History Show Next', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousFindTermTerminalFindWidgetAction, ShowPreviousFindTermTerminalFindWidgetAction.ID, ShowPreviousFindTermTerminalFindWidgetAction.LABEL, { + primary: KeyMod.Alt | KeyCode.UpArrow +}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Find History Show Previous', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordLeftTerminalAction, DeleteWordLeftTerminalAction.ID, DeleteWordLeftTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.Backspace, + mac: { primary: KeyMod.Alt | KeyCode.Backspace } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Before Cursor', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordRightTerminalAction, DeleteWordRightTerminalAction.ID, DeleteWordRightTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.Delete, + mac: { primary: KeyMod.Alt | KeyCode.Delete } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word After Cursor', category); +registerColors(); + diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.ts new file mode 100644 index 0000000000..62d5ab02b8 --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as cp from 'child_process'; +import * as os from 'os'; +import * as platform from 'vs/base/common/platform'; +import * as processes from 'vs/base/node/processes'; + +export const TERMINAL_DEFAULT_SHELL_LINUX = !platform.isWindows ? (process.env.SHELL || 'sh') : 'sh'; +export const TERMINAL_DEFAULT_SHELL_OSX = !platform.isWindows ? (process.env.SHELL || 'sh') : 'sh'; + +const isAtLeastWindows10 = platform.isWindows && parseFloat(os.release()) >= 10; +const is32ProcessOn64Windows = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'); +const powerShellPath = `${process.env.windir}\\${is32ProcessOn64Windows ? 'Sysnative' : 'System32'}\\WindowsPowerShell\\v1.0\\powershell.exe`; + +export const TERMINAL_DEFAULT_SHELL_WINDOWS = isAtLeastWindows10 ? powerShellPath : processes.getWindowsShell(); + +export interface ITerminalProcessFactory { + create(env: { [key: string]: string }): cp.ChildProcess; +} diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts new file mode 100644 index 0000000000..7f5fba1be5 --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -0,0 +1,796 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as os from 'os'; +import { Action, IAction } from 'vs/base/common/actions'; +import { EndOfLinePreference } from 'vs/editor/common/editorCommon'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/parts/terminal/common/terminal'; +import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { TogglePanelAction } from 'vs/workbench/browser/panel'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { ActionBarContributor } from 'vs/workbench/browser/actions'; +import { TerminalEntry } from 'vs/workbench/parts/terminal/browser/terminalQuickOpen'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +export const TERMINAL_PICKER_PREFIX = 'term '; + +export class ToggleTerminalAction extends TogglePanelAction { + + public static ID = 'workbench.action.terminal.toggleTerminal'; + public static LABEL = nls.localize('workbench.action.terminal.toggleTerminal', "Toggle Integrated Terminal"); + + constructor( + id: string, label: string, + @IPanelService panelService: IPanelService, + @IPartService partService: IPartService, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label, TERMINAL_PANEL_ID, panelService, partService); + } + + public run(event?: any): TPromise { + if (this.terminalService.terminalInstances.length === 0) { + // If there is not yet an instance attempt to create it here so that we can suggest a + // new shell on Windows (and not do so when the panel is restored on reload). + this.terminalService.createInstance(undefined, true); + } + return super.run(); + } +} + +export class KillTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.kill'; + public static LABEL = nls.localize('workbench.action.terminal.kill', "Kill the Active Terminal Instance"); + public static PANEL_LABEL = nls.localize('workbench.action.terminal.kill.short', "Kill Terminal"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + this.class = 'terminal-action kill'; + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + this.terminalService.getActiveInstance().dispose(); + if (this.terminalService.terminalInstances.length > 0) { + this.terminalService.showPanel(true); + } + } + return TPromise.as(void 0); + } +} + +export class QuickKillTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.quickKill'; + public static LABEL = nls.localize('workbench.action.terminal.quickKill', "Kill Terminal Instance"); + + constructor( + id: string, label: string, + private terminalEntry: TerminalEntry, + @ITerminalService private terminalService: ITerminalService, + @IQuickOpenService private quickOpenService: IQuickOpenService + ) { + super(id, label); + this.class = 'terminal-action kill'; + } + + public run(event?: any): TPromise { + const terminalIndex = parseInt(this.terminalEntry.getLabel().split(':')[0]) - 1; + const terminal = this.terminalService.getInstanceFromIndex(terminalIndex); + if (terminal) { + terminal.dispose(); + } + if (this.terminalService.terminalInstances.length > 0 && this.terminalService.activeTerminalInstanceIndex !== terminalIndex) { + this.terminalService.setActiveInstanceByIndex(Math.min(terminalIndex, this.terminalService.terminalInstances.length - 1)); + } + return TPromise.timeout(50).then(result => this.quickOpenService.show(TERMINAL_PICKER_PREFIX, null)); + } +} + +/** + * Copies the terminal selection. Note that since the command palette takes focus from the terminal, + * this cannot be triggered through the command palette. + */ +export class CopyTerminalSelectionAction extends Action { + + public static ID = 'workbench.action.terminal.copySelection'; + public static LABEL = nls.localize('workbench.action.terminal.copySelection', "Copy Selection"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + terminalInstance.copySelection(); + } + return TPromise.as(void 0); + } +} + +export class SelectAllTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.selectAll'; + public static LABEL = nls.localize('workbench.action.terminal.selectAll', "Select All"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + terminalInstance.selectAll(); + } + return TPromise.as(void 0); + } +} + +export class DeleteWordLeftTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.deleteWordLeft'; + public static LABEL = nls.localize('workbench.action.terminal.deleteWordLeft', "Delete Word Left"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + // Send ctrl+W + terminalInstance.sendText(String.fromCharCode('W'.charCodeAt(0) - 64), false); + } + return TPromise.as(void 0); + } +} + +export class DeleteWordRightTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.deleteWordRight'; + public static LABEL = nls.localize('workbench.action.terminal.deleteWordRight', "Delete Word Right"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + // Send alt+D + terminalInstance.sendText('\x1bD', false); + } + return TPromise.as(void 0); + } +} + +export class CreateNewTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.new'; + public static LABEL = nls.localize('workbench.action.terminal.new', "Create New Integrated Terminal"); + public static PANEL_LABEL = nls.localize('workbench.action.terminal.new.short', "New Terminal"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + this.class = 'terminal-action new'; + } + + public run(event?: any): TPromise { + const instance = this.terminalService.createInstance(undefined, true); + if (!instance) { + return TPromise.as(void 0); + } + this.terminalService.setActiveInstance(instance); + return this.terminalService.showPanel(true); + } +} + +export class FocusActiveTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.focus'; + public static LABEL = nls.localize('workbench.action.terminal.focus', "Focus Terminal"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + const instance = this.terminalService.getActiveOrCreateInstance(true); + if (!instance) { + return TPromise.as(void 0); + } + this.terminalService.setActiveInstance(instance); + return this.terminalService.showPanel(true); + } +} + +export class FocusNextTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.focusNext'; + public static LABEL = nls.localize('workbench.action.terminal.focusNext', "Focus Next Terminal"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + this.terminalService.setActiveInstanceToNext(); + return this.terminalService.showPanel(true); + } +} + +export class FocusTerminalAtIndexAction extends Action { + private static ID_PREFIX = 'workbench.action.terminal.focusAtIndex'; + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + this.terminalService.setActiveInstanceByIndex(this.getTerminalNumber() - 1); + return this.terminalService.showPanel(true); + } + + public static getId(n: number): string { + return FocusTerminalAtIndexAction.ID_PREFIX + n; + } + + public static getLabel(n: number): string { + return nls.localize('workbench.action.terminal.focusAtIndex', 'Focus Terminal {0}', n); + } + + private getTerminalNumber(): number { + return parseInt(this.id.substr(FocusTerminalAtIndexAction.ID_PREFIX.length)); + } +} + +export class FocusPreviousTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.focusPrevious'; + public static LABEL = nls.localize('workbench.action.terminal.focusPrevious', "Focus Previous Terminal"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + this.terminalService.setActiveInstanceToPrevious(); + return this.terminalService.showPanel(true); + } +} +export class TerminalPasteAction extends Action { + + public static ID = 'workbench.action.terminal.paste'; + public static LABEL = nls.localize('workbench.action.terminal.paste', "Paste into Active Terminal"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + const instance = this.terminalService.getActiveOrCreateInstance(); + if (instance) { + instance.paste(); + } + return TPromise.as(void 0); + } +} + +export class SelectDefaultShellWindowsTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.selectDefaultShell'; + public static LABEL = nls.localize('workbench.action.terminal.DefaultShell', "Select Default Shell"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + return this.terminalService.selectDefaultWindowsShell(); + } +} + +export class RunSelectedTextInTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.runSelectedText'; + public static LABEL = nls.localize('workbench.action.terminal.runSelectedText', "Run Selected Text In Active Terminal"); + + constructor( + id: string, label: string, + @ICodeEditorService private codeEditorService: ICodeEditorService, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + const instance = this.terminalService.getActiveOrCreateInstance(); + if (!instance) { + return TPromise.as(void 0); + } + let editor = this.codeEditorService.getFocusedCodeEditor(); + if (!editor) { + return TPromise.as(void 0); + } + let selection = editor.getSelection(); + let text: string; + if (selection.isEmpty()) { + text = editor.getModel().getLineContent(selection.selectionStartLineNumber).trim(); + } else { + let endOfLinePreference = os.EOL === '\n' ? EndOfLinePreference.LF : EndOfLinePreference.CRLF; + text = editor.getModel().getValueInRange(selection, endOfLinePreference); + } + instance.sendText(text, true); + return this.terminalService.showPanel(); + } +} + +export class RunActiveFileInTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.runActiveFile'; + public static LABEL = nls.localize('workbench.action.terminal.runActiveFile', "Run Active File In Active Terminal"); + + constructor( + id: string, label: string, + @ICodeEditorService private codeEditorService: ICodeEditorService, + @ITerminalService private terminalService: ITerminalService, + @IMessageService private messageService: IMessageService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + const instance = this.terminalService.getActiveOrCreateInstance(); + if (!instance) { + return TPromise.as(void 0); + } + const editor = this.codeEditorService.getFocusedCodeEditor(); + if (!editor) { + return TPromise.as(void 0); + } + const uri = editor.getModel().uri; + if (uri.scheme !== 'file') { + this.messageService.show(Severity.Warning, nls.localize('workbench.action.terminal.runActiveFile.noFile', 'Only files on disk can be run in the terminal')); + return TPromise.as(void 0); + } + instance.sendText(uri.fsPath, true); + return this.terminalService.showPanel(); + } +} + +export class SwitchTerminalInstanceAction extends Action { + + public static ID = 'workbench.action.terminal.switchTerminalInstance'; + public static LABEL = nls.localize('workbench.action.terminal.switchTerminalInstance', "Switch Terminal Instance"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(SwitchTerminalInstanceAction.ID, SwitchTerminalInstanceAction.LABEL); + this.class = 'terminal-action switch-terminal-instance'; + } + + public run(item?: string): TPromise { + if (!item || !item.split) { + return TPromise.as(null); + } + const selectedTerminalIndex = parseInt(item.split(':')[0], 10) - 1; + this.terminalService.setActiveInstanceByIndex(selectedTerminalIndex); + return this.terminalService.showPanel(true); + } +} + +export class SwitchTerminalInstanceActionItem extends SelectActionItem { + + constructor( + action: IAction, + @ITerminalService private terminalService: ITerminalService, + @IThemeService themeService: IThemeService + ) { + super(null, action, terminalService.getInstanceLabels(), terminalService.activeTerminalInstanceIndex); + + this.toDispose.push(terminalService.onInstancesChanged(this._updateItems, this)); + this.toDispose.push(terminalService.onActiveInstanceChanged(this._updateItems, this)); + this.toDispose.push(terminalService.onInstanceTitleChanged(this._updateItems, this)); + this.toDispose.push(attachSelectBoxStyler(this.selectBox, themeService)); + } + + private _updateItems(): void { + this.setOptions(this.terminalService.getInstanceLabels(), this.terminalService.activeTerminalInstanceIndex); + } +} + +export class ScrollDownTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.scrollDown'; + public static LABEL = nls.localize('workbench.action.terminal.scrollDown', "Scroll Down (Line)"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + terminalInstance.scrollDownLine(); + } + return TPromise.as(void 0); + } +} + +export class ScrollDownPageTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.scrollDownPage'; + public static LABEL = nls.localize('workbench.action.terminal.scrollDownPage', "Scroll Down (Page)"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + terminalInstance.scrollDownPage(); + } + return TPromise.as(void 0); + } +} + +export class ScrollToBottomTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.scrollToBottom'; + public static LABEL = nls.localize('workbench.action.terminal.scrollToBottom', "Scroll to Bottom"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + terminalInstance.scrollToBottom(); + } + return TPromise.as(void 0); + } +} + +export class ScrollUpTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.scrollUp'; + public static LABEL = nls.localize('workbench.action.terminal.scrollUp', "Scroll Up (Line)"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + terminalInstance.scrollUpLine(); + } + return TPromise.as(void 0); + } +} + +export class ScrollUpPageTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.scrollUpPage'; + public static LABEL = nls.localize('workbench.action.terminal.scrollUpPage', "Scroll Up (Page)"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + terminalInstance.scrollUpPage(); + } + return TPromise.as(void 0); + } +} + +export class ScrollToTopTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.scrollToTop'; + public static LABEL = nls.localize('workbench.action.terminal.scrollToTop', "Scroll to Top"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + terminalInstance.scrollToTop(); + } + return TPromise.as(void 0); + } +} + +export class ClearTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.clear'; + public static LABEL = nls.localize('workbench.action.terminal.clear', "Clear"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + let terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + terminalInstance.clear(); + } + return TPromise.as(void 0); + } +} + +export class AllowWorkspaceShellTerminalCommand extends Action { + + public static ID = 'workbench.action.terminal.allowWorkspaceShell'; + public static LABEL = nls.localize('workbench.action.terminal.allowWorkspaceShell', "Allow Workspace Shell Configuration"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + this.terminalService.setWorkspaceShellAllowed(true); + return TPromise.as(void 0); + } +} + +export class DisallowWorkspaceShellTerminalCommand extends Action { + + public static ID = 'workbench.action.terminal.disallowWorkspaceShell'; + public static LABEL = nls.localize('workbench.action.terminal.disallowWorkspaceShell', "Disallow Workspace Shell Configuration"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + this.terminalService.setWorkspaceShellAllowed(false); + return TPromise.as(void 0); + } +} + +export class RenameTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.rename'; + public static LABEL = nls.localize('workbench.action.terminal.rename', "Rename"); + + constructor( + id: string, label: string, + @IQuickOpenService protected quickOpenService: IQuickOpenService, + @ITerminalService protected terminalService: ITerminalService + ) { + super(id, label); + } + + public run(terminal?: TerminalEntry): TPromise { + const terminalInstance = terminal ? this.terminalService.getInstanceFromIndex(parseInt(terminal.getLabel().split(':')[0], 10) - 1) : this.terminalService.getActiveInstance(); + if (!terminalInstance) { + return TPromise.as(void 0); + } + return this.quickOpenService.input({ + value: terminalInstance.title, + prompt: nls.localize('workbench.action.terminal.rename.prompt', "Enter terminal name"), + }).then(name => { + if (name) { + terminalInstance.setTitle(name, false); + } + }); + } +} + +export class FocusTerminalFindWidgetAction extends Action { + + public static ID = 'workbench.action.terminal.focusFindWidget'; + public static LABEL = nls.localize('workbench.action.terminal.focusFindWidget', "Focus Find Widget"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(): TPromise { + return this.terminalService.focusFindWidget(); + } +} + +export class HideTerminalFindWidgetAction extends Action { + + public static ID = 'workbench.action.terminal.hideFindWidget'; + public static LABEL = nls.localize('workbench.action.terminal.hideFindWidget', "Hide Find Widget"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(): TPromise { + return TPromise.as(this.terminalService.hideFindWidget()); + } +} + +export class ShowNextFindTermTerminalFindWidgetAction extends Action { + + public static ID = 'workbench.action.terminal.findWidget.history.showNext'; + public static LABEL = nls.localize('nextTerminalFindTerm', "Show Next Find Term"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(): TPromise { + return TPromise.as(this.terminalService.showNextFindTermFindWidget()); + } +} + +export class ShowPreviousFindTermTerminalFindWidgetAction extends Action { + + public static ID = 'workbench.action.terminal.findWidget.history.showPrevious'; + public static LABEL = nls.localize('previousTerminalFindTerm', "Show Previous Find Term"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(): TPromise { + return TPromise.as(this.terminalService.showPreviousFindTermFindWidget()); + } +} + + +export class QuickOpenActionTermContributor extends ActionBarContributor { + + constructor( + @ITerminalService private terminalService: ITerminalService, + @IQuickOpenService private quickOpenService: IQuickOpenService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(); + } + + public getActions(context: any): IAction[] { + let actions: Action[] = []; + if (context.element instanceof TerminalEntry) { + actions.push(this.instantiationService.createInstance(RenameTerminalQuickOpenAction, RenameTerminalQuickOpenAction.ID, RenameTerminalQuickOpenAction.LABEL, context.element)); + actions.push(this.instantiationService.createInstance(QuickKillTerminalAction, QuickKillTerminalAction.ID, QuickKillTerminalAction.LABEL, context.element)); + } + return actions; + } + + public hasActions(context: any): boolean { + return true; + } +} + +export class QuickOpenTermAction extends Action { + + public static ID = 'workbench.action.quickOpenTerm'; + public static LABEL = nls.localize('quickOpenTerm', "Terminal: Switch Active Terminal"); + + constructor( + id: string, + label: string, + @IQuickOpenService private quickOpenService: IQuickOpenService + ) { + super(id, label); + } + + public run(): TPromise { + return this.quickOpenService.show(TERMINAL_PICKER_PREFIX, null); + } +} + +export class RenameTerminalQuickOpenAction extends RenameTerminalAction { + + constructor( + id: string, label: string, + private terminal: TerminalEntry, + @IQuickOpenService quickOpenService: IQuickOpenService, + @ITerminalService terminalService: ITerminalService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(id, label, quickOpenService, terminalService); + this.class = 'quick-open-terminal-configure'; + } + + public run(): TPromise { + super.run(this.terminal) + // This timeout is needed to make sure the previous quickOpen has time to close before we show the next one + .then(() => TPromise.timeout(50)) + .then(result => this.quickOpenService.show(TERMINAL_PICKER_PREFIX, null)); + return TPromise.as(null); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.ts new file mode 100644 index 0000000000..2b06080fda --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; + +import { registerColor, ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; + +/** + * The color identifiers for the terminal's ansi colors. The index in the array corresponds to the index + * of the color in the terminal color table. + */ +export const ansiColorIdentifiers: ColorIdentifier[] = []; + +export const TERMINAL_BACKGROUND_COLOR = registerColor('terminal.background', null, nls.localize('terminal.background', 'The background color of the terminal, this allows coloring the terminal differently to the panel.')); +export const TERMINAL_FOREGROUND_COLOR = registerColor('terminal.foreground', { + light: '#333333', + dark: '#CCCCCC', + hc: '#FFFFFF' +}, nls.localize('terminal.foreground', 'The foreground color of the terminal.')); +export const TERMINAL_CURSOR_FOREGROUND_COLOR = registerColor('terminalCursor.foreground', null, nls.localize('terminalCursor.foreground', 'The foreground color of the terminal cursor.')); +export const TERMINAL_CURSOR_BACKGROUND_COLOR = registerColor('terminalCursor.background', null, nls.localize('terminalCursor.background', 'The background color of the terminal cursor. Allows customizing the color of a character overlapped by a block cursor.')); + +// TODO: Reinstate, see #28397 +// export const TERMINAL_SELECTION_BACKGROUND_COLOR = registerColor('terminal.selectionBackground', { +// light: '#000', +// dark: '#FFF', +// hc: '#FFF' +// }, nls.localize('terminal.selectionBackground', 'The selection background color of the terminal.')); + +const ansiColorMap = { + 'terminal.ansiBlack': { + index: 0, + defaults: { + light: '#000000', + dark: '#000000', + hc: '#000000' + } + }, + 'terminal.ansiRed': { + index: 1, + defaults: { + light: '#cd3131', + dark: '#cd3131', + hc: '#cd0000' + } + }, + 'terminal.ansiGreen': { + index: 2, + defaults: { + light: '#00BC00', + dark: '#0DBC79', + hc: '#00cd00' + } + }, + 'terminal.ansiYellow': { + index: 3, + defaults: { + light: '#949800', + dark: '#e5e510', + hc: '#cdcd00' + } + }, + 'terminal.ansiBlue': { + index: 4, + defaults: { + light: '#0451a5', + dark: '#2472c8', + hc: '#0000ee' + } + }, + 'terminal.ansiMagenta': { + index: 5, + defaults: { + light: '#bc05bc', + dark: '#bc3fbc', + hc: '#cd00cd' + } + }, + 'terminal.ansiCyan': { + index: 6, + defaults: { + light: '#0598bc', + dark: '#11a8cd', + hc: '#00cdcd' + } + }, + 'terminal.ansiWhite': { + index: 7, + defaults: { + light: '#555555', + dark: '#e5e5e5', + hc: '#e5e5e5' + } + }, + 'terminal.ansiBrightBlack': { + index: 8, + defaults: { + light: '#666666', + dark: '#666666', + hc: '#7f7f7f' + } + }, + 'terminal.ansiBrightRed': { + index: 9, + defaults: { + light: '#cd3131', + dark: '#f14c4c', + hc: '#ff0000' + } + }, + 'terminal.ansiBrightGreen': { + index: 10, + defaults: { + light: '#14CE14', + dark: '#23d18b', + hc: '#00ff00' + } + }, + 'terminal.ansiBrightYellow': { + index: 11, + defaults: { + light: '#b5ba00', + dark: '#f5f543', + hc: '#ffff00' + } + }, + 'terminal.ansiBrightBlue': { + index: 12, + defaults: { + light: '#0451a5', + dark: '#3b8eea', + hc: '#5c5cff' + } + }, + 'terminal.ansiBrightMagenta': { + index: 13, + defaults: { + light: '#bc05bc', + dark: '#d670d6', + hc: '#ff00ff' + } + }, + 'terminal.ansiBrightCyan': { + index: 14, + defaults: { + light: '#0598bc', + dark: '#29b8db', + hc: '#00ffff' + } + }, + 'terminal.ansiBrightWhite': { + index: 15, + defaults: { + light: '#a5a5a5', + dark: '#e5e5e5', + hc: '#ffffff' + } + } +}; + +export function registerColors(): void { + for (let id in ansiColorMap) { + let entry = ansiColorMap[id]; + let colorName = id.substring(13); + ansiColorIdentifiers[entry.index] = registerColor(id, entry.defaults, nls.localize('terminal.ansiColor', '\'{0}\' ansi color in the terminal.', colorName)); + } + +} diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts new file mode 100644 index 0000000000..5b5f0b39ca --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as path from 'path'; +import * as platform from 'vs/base/common/platform'; +import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IChoiceService } from 'vs/platform/message/common/message'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ITerminalConfiguration, ITerminalConfigHelper, ITerminalFont, IShellLaunchConfig, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY } from 'vs/workbench/parts/terminal/common/terminal'; +import { TPromise } from 'vs/base/common/winjs.base'; +import Severity from 'vs/base/common/severity'; + +interface IEditorConfiguration { + editor: IEditorOptions; +} + +interface IFullTerminalConfiguration { + terminal: { + integrated: ITerminalConfiguration; + }; +} + +const DEFAULT_LINE_HEIGHT = 1.2; + +/** + * Encapsulates terminal configuration logic, the primary purpose of this file is so that platform + * specific test cases can be written. + */ +export class TerminalConfigHelper implements ITerminalConfigHelper { + public panelContainer: HTMLElement; + + private _charMeasureElement: HTMLElement; + private _lastFontMeasurement: ITerminalFont; + + public constructor( + private _platform: platform.Platform, + @IConfigurationService private _configurationService: IConfigurationService, + @IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService, + @IChoiceService private _choiceService: IChoiceService, + @IStorageService private _storageService: IStorageService) { + } + + public get config(): ITerminalConfiguration { + return this._configurationService.getConfiguration().terminal.integrated; + } + + private _measureFont(fontFamily: string, fontSize: number, lineHeight: number): ITerminalFont { + // Create charMeasureElement if it hasn't been created or if it was orphaned by its parent + if (!this._charMeasureElement || !this._charMeasureElement.parentElement) { + this._charMeasureElement = document.createElement('div'); + this.panelContainer.appendChild(this._charMeasureElement); + } + const style = this._charMeasureElement.style; + style.display = 'block'; + style.fontFamily = fontFamily; + style.fontSize = fontSize + 'px'; + style.lineHeight = lineHeight.toString(10); + this._charMeasureElement.innerText = 'X'; + const rect = this._charMeasureElement.getBoundingClientRect(); + style.display = 'none'; + + // Bounding client rect was invalid, use last font measurement if available. + if (this._lastFontMeasurement && !rect.width && !rect.height) { + return this._lastFontMeasurement; + } + + this._lastFontMeasurement = { + fontFamily, + fontSize: fontSize + 'px', + lineHeight, + charWidth: rect.width, + charHeight: rect.height + }; + return this._lastFontMeasurement; + } + + /** + * Gets the font information based on the terminal.integrated.fontFamily + * terminal.integrated.fontSize, terminal.integrated.lineHeight configuration properties + */ + public getFont(): ITerminalFont { + const config = this._configurationService.getConfiguration(); + const editorConfig = (config).editor; + const terminalConfig = this.config; + + const fontFamily = terminalConfig.fontFamily || editorConfig.fontFamily; + let fontSize = this._toInteger(terminalConfig.fontSize, 0); + if (fontSize <= 0) { + fontSize = EDITOR_FONT_DEFAULTS.fontSize; + } + let lineHeight = terminalConfig.lineHeight <= 0 ? DEFAULT_LINE_HEIGHT : terminalConfig.lineHeight; + if (!lineHeight) { + lineHeight = DEFAULT_LINE_HEIGHT; + } + + return this._measureFont(fontFamily, fontSize, lineHeight); + } + + public setWorkspaceShellAllowed(isAllowed: boolean): void { + this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, isAllowed, StorageScope.WORKSPACE); + } + + public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig): void { + // Check whether there is a workspace setting + const platformKey = platform.isWindows ? 'windows' : platform.isMacintosh ? 'osx' : 'linux'; + const shellConfigValue = this._workspaceConfigurationService.lookup(`terminal.integrated.shell.${platformKey}`); + const shellArgsConfigValue = this._workspaceConfigurationService.lookup(`terminal.integrated.shellArgs.${platformKey}`); + + // Check if workspace setting exists and whether it's whitelisted + let isWorkspaceShellAllowed = false; + if (shellConfigValue.workspace !== undefined || shellArgsConfigValue.workspace !== undefined) { + isWorkspaceShellAllowed = this._storageService.getBoolean(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, StorageScope.WORKSPACE, undefined); + } + + // Check if the value is neither blacklisted (false) or whitelisted (true) and ask for + // permission + if (isWorkspaceShellAllowed === undefined) { + let shellString: string; + if (shellConfigValue.workspace) { + shellString = `"${shellConfigValue.workspace}"`; + } + let argsString: string; + if (shellArgsConfigValue.workspace) { + argsString = `[${shellArgsConfigValue.workspace.map(v => '"' + v + '"').join(', ')}]`; + } + // Should not be localized as it's json-like syntax referencing settings keys + let changeString: string; + if (shellConfigValue.workspace !== undefined) { + if (shellArgsConfigValue.workspace !== undefined) { + changeString = `shell: ${shellString}, shellArgs: ${argsString}`; + } else { + changeString = `shell: ${shellString}`; + } + } else { // if (shellArgsConfigValue.workspace !== undefined) + changeString = `shellArgs: ${argsString}`; + } + const message = nls.localize('terminal.integrated.allowWorkspaceShell', "Do you allow {0} (defined as a workspace setting) to be launched in the terminal?", changeString); + const options = [nls.localize('allow', "Allow"), nls.localize('disallow', "Disallow")]; + this._choiceService.choose(Severity.Info, message, options, 1).then(choice => { + if (choice === 0) { + this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, true, StorageScope.WORKSPACE); + } else { + this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, false, StorageScope.WORKSPACE); + } + return TPromise.as(null); + }); + } + + shell.executable = (isWorkspaceShellAllowed ? shellConfigValue.value : shellConfigValue.user) || shellConfigValue.default; + shell.args = (isWorkspaceShellAllowed ? shellArgsConfigValue.value : shellArgsConfigValue.user) || shellArgsConfigValue.default; + + // Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's + // safe to assume that this was used by accident as Sysnative does not + // exist and will break the terminal in non-WoW64 environments. + if (platform.isWindows && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')) { + const sysnativePath = path.join(process.env.windir, 'Sysnative').toLowerCase(); + if (shell.executable.toLowerCase().indexOf(sysnativePath) === 0) { + shell.executable = path.join(process.env.windir, 'System32', shell.executable.substr(sysnativePath.length)); + } + } + } + + private _toInteger(source: any, minimum?: number): number { + let r = parseInt(source, 10); + if (isNaN(r)) { + r = 0; + } + if (typeof minimum === 'number') { + r = Math.max(minimum, r); + } + return r; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.ts new file mode 100644 index 0000000000..0e21f57c7f --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalFindWidget.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SimpleFindWidget } from 'vs/editor/contrib/find/browser/simpleFindWidget'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED } from 'vs/workbench/parts/terminal/common/terminal'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export class TerminalFindWidget extends SimpleFindWidget { + protected _findInputFocused: IContextKey; + + constructor( + @IContextViewService _contextViewService: IContextViewService, + @IContextKeyService private _contextKeyService: IContextKeyService, + @ITerminalService private _terminalService: ITerminalService + ) { + super(_contextViewService); + this._findInputFocused = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED.bindTo(this._contextKeyService); + } + + public find(previous) { + let val = this.inputValue; + let instance = this._terminalService.getActiveInstance(); + if (instance !== null) { + if (previous) { + instance.findPrevious(val); + } else { + instance.findNext(val); + } + } + }; + + public hide() { + super.hide(); + this._terminalService.getActiveInstance().focus(); + } + + protected onInputChanged() { + // Ignore input changes for now + } + + protected onFocusTrackerFocus() { + this._terminalService.getActiveInstance().notifyFindWidgetFocusChanged(true); + } + + protected onFocusTrackerBlur() { + this._terminalService.getActiveInstance().notifyFindWidgetFocusChanged(false); + } + + protected onFindInputFocusTrackerFocus() { + this._findInputFocused.set(true); + } + + protected onFindInputFocusTrackerBlur() { + this._findInputFocused.reset(); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts new file mode 100644 index 0000000000..34a7e6d006 --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -0,0 +1,958 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as cp from 'child_process'; +import * as os from 'os'; +import * as path from 'path'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import * as nls from 'vs/nls'; +import * as platform from 'vs/base/common/platform'; +import * as dom from 'vs/base/browser/dom'; +import Event, { Emitter } from 'vs/base/common/event'; +import Uri from 'vs/base/common/uri'; +import { WindowsShellHelper } from 'vs/workbench/parts/terminal/electron-browser/windowsShellHelper'; +import { Terminal as XTermTerminal } from 'xterm'; +import { Dimension } from 'vs/base/browser/builder'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { ITerminalInstance, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal'; +import { ITerminalProcessFactory } from 'vs/workbench/parts/terminal/electron-browser/terminal'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; +import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; +import { TerminalLinkHandler } from 'vs/workbench/parts/terminal/electron-browser/terminalLinkHandler'; +import { TerminalWidgetManager } from 'vs/workbench/parts/terminal/browser/terminalWidgetManager'; +import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import pkg from 'vs/platform/node/package'; + +/** The amount of time to consider terminal errors to be related to the launch */ +const LAUNCHING_DURATION = 500; + +// Enable search functionality in xterm.js instance +XTermTerminal.loadAddon('search'); + +class StandardTerminalProcessFactory implements ITerminalProcessFactory { + public create(env: { [key: string]: string }): cp.ChildProcess { + return cp.fork('./terminalProcess', [], { + env, + cwd: Uri.parse(path.dirname(require.toUrl('./terminalProcess'))).fsPath + }); + } +} + +enum ProcessState { + // The process has not been initialized yet. + UNINITIALIZED, + // The process is currently launching, the process is marked as launching + // for a short duration after being created and is helpful to indicate + // whether the process died as a result of bad shell and args. + LAUNCHING, + // The process is running normally. + RUNNING, + // The process was killed during launch, likely as a result of bad shell and + // args. + KILLED_DURING_LAUNCH, + // The process was killed by the user (the event originated from VS Code). + KILLED_BY_USER, + // The process was killed by itself, for example the shell crashed or `exit` + // was run. + KILLED_BY_PROCESS +} + +export class TerminalInstance implements ITerminalInstance { + private static readonly EOL_REGEX = /\r?\n/g; + + private static _terminalProcessFactory: ITerminalProcessFactory = new StandardTerminalProcessFactory(); + private static _lastKnownDimensions: Dimension = null; + private static _idCounter = 1; + + private _id: number; + private _isExiting: boolean; + private _hadFocusOnExit: boolean; + private _isVisible: boolean; + private _processState: ProcessState; + private _processReady: TPromise; + private _isDisposed: boolean; + private _onDisposed: Emitter; + private _onDataForApi: Emitter<{ instance: ITerminalInstance, data: string }>; + private _onProcessIdReady: Emitter; + private _onTitleChanged: Emitter; + private _process: cp.ChildProcess; + private _processId: number; + private _skipTerminalCommands: string[]; + private _title: string; + private _instanceDisposables: lifecycle.IDisposable[]; + private _processDisposables: lifecycle.IDisposable[]; + private _wrapperElement: HTMLDivElement; + private _xterm: XTermTerminal; + private _xtermElement: HTMLDivElement; + private _terminalHasTextContextKey: IContextKey; + private _cols: number; + private _rows: number; + private _messageTitleListener: (message: { type: string, content: string }) => void; + private _preLaunchInputQueue: string; + private _initialCwd: string; + private _windowsShellHelper: WindowsShellHelper; + + private _widgetManager: TerminalWidgetManager; + private _linkHandler: TerminalLinkHandler; + + public get id(): number { return this._id; } + public get processId(): number { return this._processId; } + public get onDisposed(): Event { return this._onDisposed.event; } + public get onDataForApi(): Event<{ instance: ITerminalInstance, data: string }> { return this._onDataForApi.event; } + public get onProcessIdReady(): Event { return this._onProcessIdReady.event; } + public get onTitleChanged(): Event { return this._onTitleChanged.event; } + public get title(): string { return this._title; } + public get hadFocusOnExit(): boolean { return this._hadFocusOnExit; } + public get isTitleSetByProcess(): boolean { return !!this._messageTitleListener; } + + public constructor( + private _terminalFocusContextKey: IContextKey, + private _configHelper: TerminalConfigHelper, + private _container: HTMLElement, + private _shellLaunchConfig: IShellLaunchConfig, + @IContextKeyService private _contextKeyService: IContextKeyService, + @IKeybindingService private _keybindingService: IKeybindingService, + @IMessageService private _messageService: IMessageService, + @IPanelService private _panelService: IPanelService, + @IWorkspaceContextService private _contextService: IWorkspaceContextService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IClipboardService private _clipboardService: IClipboardService, + @IHistoryService private _historyService: IHistoryService + ) { + this._instanceDisposables = []; + this._processDisposables = []; + this._skipTerminalCommands = []; + this._isExiting = false; + this._hadFocusOnExit = false; + this._processState = ProcessState.UNINITIALIZED; + this._isVisible = false; + this._isDisposed = false; + this._id = TerminalInstance._idCounter++; + this._terminalHasTextContextKey = KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED.bindTo(this._contextKeyService); + this._preLaunchInputQueue = ''; + + this._onDisposed = new Emitter(); + this._onDataForApi = new Emitter<{ instance: ITerminalInstance, data: string }>(); + this._onProcessIdReady = new Emitter(); + this._onTitleChanged = new Emitter(); + + // Create a promise that resolves when the pty is ready + this._processReady = new TPromise(c => { + this.onProcessIdReady(() => c(void 0)); + }); + + this._initDimensions(); + this._createProcess(); + this._createXterm(); + + if (platform.isWindows) { + this._processReady.then(() => { + if (!this._isDisposed) { + this._windowsShellHelper = new WindowsShellHelper(this._processId, this._shellLaunchConfig.executable, this, this._xterm); + } + }); + } + + // Only attach xterm.js to the DOM if the terminal panel has been opened before. + if (_container) { + this.attachToElement(_container); + } + } + + public addDisposable(disposable: lifecycle.IDisposable): void { + this._instanceDisposables.push(disposable); + } + + private _initDimensions(): void { + // The terminal panel needs to have been created + if (!this._container) { + return; + } + + const computedStyle = window.getComputedStyle(this._container); + const width = parseInt(computedStyle.getPropertyValue('width').replace('px', ''), 10); + const height = parseInt(computedStyle.getPropertyValue('height').replace('px', ''), 10); + this._evaluateColsAndRows(width, height); + } + + /** + * Evaluates and sets the cols and rows of the terminal if possible. + * @param width The width of the container. + * @param height The height of the container. + * @return The terminal's width if it requires a layout. + */ + private _evaluateColsAndRows(width: number, height: number): number { + const dimension = this._getDimension(width, height); + if (!dimension) { + return null; + } + const font = this._configHelper.getFont(); + this._cols = Math.max(Math.floor(dimension.width / font.charWidth), 1); + this._rows = Math.max(Math.floor(dimension.height / font.charHeight), 1); + return dimension.width; + } + + private _getDimension(width: number, height: number): Dimension { + // The font needs to have been initialized + const font = this._configHelper.getFont(); + if (!font || !font.charWidth || !font.charHeight) { + return null; + } + + // The panel is minimized + if (!height) { + return TerminalInstance._lastKnownDimensions; + } else { + // Trigger scroll event manually so that the viewport's scroll area is synced. This + // needs to happen otherwise its scrollTop value is invalid when the panel is toggled as + // it gets removed and then added back to the DOM (resetting scrollTop to 0). + // Upstream issue: https://github.com/sourcelair/xterm.js/issues/291 + if (this._xterm) { + this._xterm.emit('scroll', this._xterm.buffer.ydisp); + } + } + + const outerContainer = document.querySelector('.terminal-outer-container'); + const outerContainerStyle = getComputedStyle(outerContainer); + const padding = parseInt(outerContainerStyle.paddingLeft.split('px')[0], 10); + const paddingBottom = parseInt(outerContainerStyle.paddingBottom.split('px')[0], 10); + + // Use left padding as right padding, right padding is not defined in CSS just in case + // xterm.js causes an unexpected overflow. + const innerWidth = width - padding * 2; + const innerHeight = height - paddingBottom; + + TerminalInstance._lastKnownDimensions = new Dimension(innerWidth, innerHeight); + return TerminalInstance._lastKnownDimensions; + } + + /** + * Create xterm.js instance and attach data listeners. + */ + protected _createXterm(): void { + this._xterm = new XTermTerminal({ + scrollback: this._configHelper.config.scrollback + }); + if (this._shellLaunchConfig.initialText) { + this._xterm.writeln(this._shellLaunchConfig.initialText); + } + this._process.on('message', (message) => this._sendPtyDataToXterm(message)); + this._xterm.on('data', (data) => { + if (this._processId) { + // Send data if the pty is ready + this._process.send({ + event: 'input', + data + }); + } else { + // If the pty is not ready, queue the data received from + // xterm.js until the pty is ready + this._preLaunchInputQueue += data; + } + return false; + }); + this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, platform.platform, this._initialCwd); + this._linkHandler.registerLocalLinkHandler(); + } + + public attachToElement(container: HTMLElement): void { + if (this._wrapperElement) { + throw new Error('The terminal instance has already been attached to a container'); + } + + this._container = container; + this._wrapperElement = document.createElement('div'); + dom.addClass(this._wrapperElement, 'terminal-wrapper'); + this._xtermElement = document.createElement('div'); + + this._xterm.open(this._xtermElement); + this._xterm.attachCustomKeyEventHandler((event: KeyboardEvent) => { + // Disable all input if the terminal is exiting + if (this._isExiting) { + return false; + } + + // Skip processing by xterm.js of keyboard events that resolve to commands described + // within commandsToSkipShell + const standardKeyboardEvent = new StandardKeyboardEvent(event); + const resolveResult = this._keybindingService.softDispatch(standardKeyboardEvent, standardKeyboardEvent.target); + if (resolveResult && this._skipTerminalCommands.some(k => k === resolveResult.commandId)) { + event.preventDefault(); + return false; + } + + // If tab focus mode is on, tab is not passed to the terminal + if (TabFocus.getTabFocusMode() && event.keyCode === 9) { + return false; + } + + return undefined; + }); + this._instanceDisposables.push(dom.addDisposableListener(this._xterm.element, 'mouseup', (event: KeyboardEvent) => { + // Wait until mouseup has propagated through the DOM before + // evaluating the new selection state. + setTimeout(() => this._refreshSelectionContextKey(), 0); + })); + + // xterm.js currently drops selection on keyup as we need to handle this case. + this._instanceDisposables.push(dom.addDisposableListener(this._xterm.element, 'keyup', (event: KeyboardEvent) => { + // Wait until keyup has propagated through the DOM before evaluating + // the new selection state. + setTimeout(() => this._refreshSelectionContextKey(), 0); + })); + + const xtermHelper: HTMLElement = this._xterm.element.querySelector('.xterm-helpers'); + const focusTrap: HTMLElement = document.createElement('div'); + focusTrap.setAttribute('tabindex', '0'); + dom.addClass(focusTrap, 'focus-trap'); + this._instanceDisposables.push(dom.addDisposableListener(focusTrap, 'focus', (event: FocusEvent) => { + let currentElement = focusTrap; + while (!dom.hasClass(currentElement, 'part')) { + currentElement = currentElement.parentElement; + } + const hidePanelElement = currentElement.querySelector('.hide-panel-action'); + hidePanelElement.focus(); + })); + xtermHelper.insertBefore(focusTrap, this._xterm.textarea); + + this._instanceDisposables.push(dom.addDisposableListener(this._xterm.textarea, 'focus', (event: KeyboardEvent) => { + this._terminalFocusContextKey.set(true); + })); + this._instanceDisposables.push(dom.addDisposableListener(this._xterm.textarea, 'blur', (event: KeyboardEvent) => { + this._terminalFocusContextKey.reset(); + this._refreshSelectionContextKey(); + })); + this._instanceDisposables.push(dom.addDisposableListener(this._xterm.element, 'focus', (event: KeyboardEvent) => { + this._terminalFocusContextKey.set(true); + })); + this._instanceDisposables.push(dom.addDisposableListener(this._xterm.element, 'blur', (event: KeyboardEvent) => { + this._terminalFocusContextKey.reset(); + this._refreshSelectionContextKey(); + })); + + this._wrapperElement.appendChild(this._xtermElement); + this._widgetManager = new TerminalWidgetManager(this._configHelper, this._wrapperElement); + this._linkHandler.setWidgetManager(this._widgetManager); + this._container.appendChild(this._wrapperElement); + + const computedStyle = window.getComputedStyle(this._container); + const width = parseInt(computedStyle.getPropertyValue('width').replace('px', ''), 10); + const height = parseInt(computedStyle.getPropertyValue('height').replace('px', ''), 10); + this.layout(new Dimension(width, height)); + this.setVisible(this._isVisible); + this.updateConfig(); + + // If IShellLaunchConfig.waitOnExit was true and the process finished before the terminal + // panel was initialized. + if (this._xterm.getOption('disableStdin')) { + this._attachPressAnyKeyToCloseListener(); + } + } + + public registerLinkMatcher(regex: RegExp, handler: (url: string) => void, matchIndex?: number, validationCallback?: (uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => void): number { + return this._linkHandler.registerCustomLinkHandler(regex, handler, matchIndex, validationCallback); + } + + public deregisterLinkMatcher(linkMatcherId: number): void { + this._xterm.deregisterLinkMatcher(linkMatcherId); + } + + public hasSelection(): boolean { + // {{SQL CARBON EDIT}} + return this._xterm && this._xterm.hasSelection ? this._xterm.hasSelection() : false; + } + + public copySelection(): void { + if (this.hasSelection()) { + this._clipboardService.writeText(this._xterm.getSelection()); + } else { + this._messageService.show(Severity.Warning, nls.localize('terminal.integrated.copySelection.noSelection', 'The terminal has no selection to copy')); + } + } + + get selection(): string | undefined { + return this.hasSelection() ? this._xterm.getSelection() : undefined; + } + + public clearSelection(): void { + this._xterm.clearSelection(); + } + + public selectAll(): void { + // Focus here to ensure the terminal context key is set + this._xterm.focus(); + this._xterm.selectAll(); + } + + public findNext(term: string): boolean { + return this._xterm.findNext(term); + } + + public findPrevious(term: string): boolean { + return this._xterm.findPrevious(term); + } + + public notifyFindWidgetFocusChanged(isFocused: boolean): void { + // In order to support escape to close the find widget when the terminal + // is focused terminalFocus needs to be true when either the terminal or + // the find widget are focused. + this._terminalFocusContextKey.set(isFocused || document.activeElement === this._xterm.textarea); + } + + public dispose(): void { + if (this._windowsShellHelper) { + this._windowsShellHelper.dispose(); + } + if (this._linkHandler) { + this._linkHandler.dispose(); + } + if (this._xterm && this._xterm.element) { + this._hadFocusOnExit = dom.hasClass(this._xterm.element, 'focus'); + } + if (this._wrapperElement) { + this._container.removeChild(this._wrapperElement); + this._wrapperElement = null; + } + if (this._xterm) { + this._xterm.destroy(); + this._xterm = null; + } + if (this._process) { + if (this._process.connected) { + // If the process was still connected this dispose came from + // within VS Code, not the process, so mark the process as + // killed by the user. + this._processState = ProcessState.KILLED_BY_USER; + this._process.send({ event: 'shutdown' }); + } + this._process = null; + } + if (!this._isDisposed) { + this._isDisposed = true; + this._onDisposed.fire(this); + } + this._processDisposables = lifecycle.dispose(this._processDisposables); + this._instanceDisposables = lifecycle.dispose(this._instanceDisposables); + } + + public focus(force?: boolean): void { + if (!this._xterm) { + return; + } + const text = window.getSelection().toString(); + if (!text || force) { + this._xterm.focus(); + } + } + + public paste(): void { + this.focus(); + document.execCommand('paste'); + } + + public sendText(text: string, addNewLine: boolean): void { + this._processReady.then(() => { + // Normalize line endings to 'enter' press. + text = text.replace(TerminalInstance.EOL_REGEX, '\r'); + if (addNewLine && text.substr(text.length - 1) !== '\r') { + text += '\r'; + } + this._process.send({ + event: 'input', + data: text + }); + }); + } + + public setVisible(visible: boolean): void { + this._isVisible = visible; + if (this._wrapperElement) { + dom.toggleClass(this._wrapperElement, 'active', visible); + } + if (visible && this._xterm) { + // Trigger a manual scroll event which will sync the viewport and scroll bar. This is + // necessary if the number of rows in the terminal has decreased while it was in the + // background since scrollTop changes take no effect but the terminal's position does + // change since the number of visible rows decreases. + this._xterm.emit('scroll', this._xterm.buffer.ydisp); + } + } + + public scrollDownLine(): void { + this._xterm.scrollDisp(1); + } + + public scrollDownPage(): void { + this._xterm.scrollPages(1); + } + + public scrollToBottom(): void { + this._xterm.scrollToBottom(); + } + + public scrollUpLine(): void { + this._xterm.scrollDisp(-1); + } + + public scrollUpPage(): void { + this._xterm.scrollPages(-1); + } + + public scrollToTop(): void { + this._xterm.scrollToTop(); + } + + public clear(): void { + this._xterm.clear(); + } + + private _refreshSelectionContextKey() { + const activePanel = this._panelService.getActivePanel(); + const isActive = activePanel && activePanel.getId() === TERMINAL_PANEL_ID; + this._terminalHasTextContextKey.set(isActive && this.hasSelection()); + } + + protected _getCwd(shell: IShellLaunchConfig, root: Uri): string { + if (shell.cwd) { + return shell.cwd; + } + + let cwd: string; + + // TODO: Handle non-existent customCwd + if (!shell.ignoreConfigurationCwd) { + // Evaluate custom cwd first + const customCwd = this._configHelper.config.cwd; + if (customCwd) { + if (path.isAbsolute(customCwd)) { + cwd = customCwd; + } else if (root) { + cwd = path.normalize(path.join(root.fsPath, customCwd)); + } + } + } + + // If there was no custom cwd or it was relative with no workspace + if (!cwd) { + cwd = root ? root.fsPath : os.homedir(); + } + + return TerminalInstance._sanitizeCwd(cwd); + } + + protected _createProcess(): void { + const locale = this._configHelper.config.setLocaleVariables ? platform.locale : undefined; + if (!this._shellLaunchConfig.executable) { + this._configHelper.mergeDefaultShellPathAndArgs(this._shellLaunchConfig); + } + this._initialCwd = this._getCwd(this._shellLaunchConfig, this._historyService.getLastActiveWorkspaceRoot()); + let envFromConfig: IStringDictionary; + if (platform.isWindows) { + envFromConfig = { ...process.env }; + for (let configKey in this._configHelper.config.env['windows']) { + let actualKey = configKey; + for (let envKey in envFromConfig) { + if (configKey.toLowerCase() === envKey.toLowerCase()) { + actualKey = envKey; + break; + } + } + envFromConfig[actualKey] = this._configHelper.config.env['windows'][configKey]; + } + } else { + const platformKey = platform.isMacintosh ? 'osx' : 'linux'; + envFromConfig = { ...process.env, ...this._configHelper.config.env[platformKey] }; + } + const env = TerminalInstance.createTerminalEnv(envFromConfig, this._shellLaunchConfig, this._initialCwd, locale, this._cols, this._rows); + this._process = cp.fork(Uri.parse(require.toUrl('bootstrap')).fsPath, ['--type=terminal'], { + env, + cwd: Uri.parse(path.dirname(require.toUrl('../node/terminalProcess'))).fsPath + }); + this._processState = ProcessState.LAUNCHING; + + if (this._shellLaunchConfig.name) { + this.setTitle(this._shellLaunchConfig.name, false); + } else { + // Only listen for process title changes when a name is not provided + this.setTitle(this._shellLaunchConfig.executable, true); + this._messageTitleListener = (message) => { + if (message.type === 'title') { + this.setTitle(message.content ? message.content : '', true); + } + }; + this._process.on('message', this._messageTitleListener); + } + this._process.on('message', (message) => { + if (message.type === 'pid') { + this._processId = message.content; + + // Send any queued data that's waiting + if (this._preLaunchInputQueue.length > 0) { + this._process.send({ + event: 'input', + data: this._preLaunchInputQueue + }); + this._preLaunchInputQueue = null; + } + this._onProcessIdReady.fire(this); + } + }); + this._process.on('exit', exitCode => this._onPtyProcessExit(exitCode)); + setTimeout(() => { + if (this._processState === ProcessState.LAUNCHING) { + this._processState = ProcessState.RUNNING; + } + }, LAUNCHING_DURATION); + } + + private _sendPtyDataToXterm(message: { type: string, content: string }): void { + if (message.type === 'data') { + if (this._widgetManager) { + this._widgetManager.closeMessage(); + } + if (this._xterm) { + this._xterm.write(message.content); + } + } + } + + private _onPtyProcessExit(exitCode: number): void { + // Prevent dispose functions being triggered multiple times + if (this._isExiting) { + return; + } + + this._isExiting = true; + this._process = null; + let exitCodeMessage: string; + if (exitCode) { + exitCodeMessage = nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode); + } + + // If the process is marked as launching then mark the process as killed + // during launch. This typically means that there is a problem with the + // shell and args. + if (this._processState === ProcessState.LAUNCHING) { + this._processState = ProcessState.KILLED_DURING_LAUNCH; + } + + // If TerminalInstance did not know about the process exit then it was + // triggered by the process, not on VS Code's side. + if (this._processState === ProcessState.RUNNING) { + this._processState = ProcessState.KILLED_BY_PROCESS; + } + + // Only trigger wait on exit when the exit was *not* triggered by the + // user (via the `workbench.action.terminal.kill` command). + if (this._shellLaunchConfig.waitOnExit && this._processState !== ProcessState.KILLED_BY_USER) { + if (exitCode) { + this._xterm.writeln(exitCodeMessage); + } + let message = typeof this._shellLaunchConfig.waitOnExit === 'string' + ? this._shellLaunchConfig.waitOnExit + : nls.localize('terminal.integrated.waitOnExit', 'Press any key to close the terminal'); + // Bold the message and add an extra new line to make it stand out from the rest of the output + message = `\n\x1b[1m${message}\x1b[0m`; + this._xterm.writeln(message); + // Disable all input if the terminal is exiting and listen for next keypress + this._xterm.setOption('disableStdin', true); + if (this._xterm.textarea) { + this._attachPressAnyKeyToCloseListener(); + } + } else { + this.dispose(); + if (exitCode) { + if (this._processState === ProcessState.KILLED_DURING_LAUNCH) { + let args = ''; + if (typeof this._shellLaunchConfig.args === 'string') { + args = this._shellLaunchConfig.args; + } else if (this._shellLaunchConfig.args && this._shellLaunchConfig.args.length) { + args = ' ' + this._shellLaunchConfig.args.map(a => { + if (typeof a === 'string' && a.indexOf(' ') !== -1) { + return `'${a}'`; + } + return a; + }).join(' '); + } + this._messageService.show(Severity.Error, nls.localize('terminal.integrated.launchFailed', 'The terminal process command `{0}{1}` failed to launch (exit code: {2})', this._shellLaunchConfig.executable, args, exitCode)); + } else { + this._messageService.show(Severity.Error, exitCodeMessage); + } + } + } + } + + private _attachPressAnyKeyToCloseListener() { + this._processDisposables.push(dom.addDisposableListener(this._xterm.textarea, 'keypress', (event: KeyboardEvent) => { + this.dispose(); + event.preventDefault(); + })); + } + + public reuseTerminal(shell?: IShellLaunchConfig): void { + // Kill and clean up old process + if (this._process) { + this._process.removeAllListeners('exit'); + if (this._process.connected) { + this._process.kill(); + } + this._process = null; + } + lifecycle.dispose(this._processDisposables); + this._processDisposables = []; + + // Ensure new processes' output starts at start of new line + this._xterm.write('\n\x1b[G'); + + // Print initialText if specified + if (shell.initialText) { + this._xterm.writeln(shell.initialText); + } + + // Initialize new process + const oldTitle = this._title; + this._shellLaunchConfig = shell; + this._createProcess(); + if (oldTitle !== this._title) { + this.setTitle(this._title, true); + } + this._process.on('message', (message) => this._sendPtyDataToXterm(message)); + + // Clean up waitOnExit state + if (this._isExiting && this._shellLaunchConfig.waitOnExit) { + this._xterm.setOption('disableStdin', false); + this._isExiting = false; + } + + // Set the new shell launch config + this._shellLaunchConfig = shell; + } + + // TODO: This should be private/protected + // TODO: locale should not be optional + public static createTerminalEnv(parentEnv: IStringDictionary, shell: IShellLaunchConfig, cwd: string, locale?: string, cols?: number, rows?: number): IStringDictionary { + const env = shell.env ? shell.env : TerminalInstance._cloneEnv(parentEnv); + env['PTYPID'] = process.pid.toString(); + env['PTYSHELL'] = shell.executable; + env['TERM_PROGRAM'] = 'vscode'; + env['TERM_PROGRAM_VERSION'] = pkg.version; + if (shell.args) { + if (typeof shell.args === 'string') { + env[`PTYSHELLCMDLINE`] = shell.args; + } else { + shell.args.forEach((arg, i) => env[`PTYSHELLARG${i}`] = arg); + } + } + env['PTYCWD'] = cwd; + env['LANG'] = TerminalInstance._getLangEnvVariable(locale); + if (cols && rows) { + env['PTYCOLS'] = cols.toString(); + env['PTYROWS'] = rows.toString(); + } + env['AMD_ENTRYPOINT'] = 'vs/workbench/parts/terminal/node/terminalProcess'; + return env; + } + + public onData(listener: (data: string) => void): lifecycle.IDisposable { + let callback = (message) => { + if (message.type === 'data') { + listener(message.content); + } + }; + this._process.on('message', callback); + return { + dispose: () => { + if (this._process) { + this._process.removeListener('message', callback); + } + } + }; + } + + public onExit(listener: (exitCode: number) => void): lifecycle.IDisposable { + if (this._process) { + this._process.on('exit', listener); + } + return { + dispose: () => { + if (this._process) { + this._process.removeListener('exit', listener); + } + } + }; + } + + private static _sanitizeCwd(cwd: string) { + // Make the drive letter uppercase on Windows (see #9448) + if (platform.platform === platform.Platform.Windows && cwd && cwd[1] === ':') { + return cwd[0].toUpperCase() + cwd.substr(1); + } + return cwd; + } + + private static _cloneEnv(env: IStringDictionary): IStringDictionary { + const newEnv: IStringDictionary = Object.create(null); + Object.keys(env).forEach((key) => { + newEnv[key] = env[key]; + }); + return newEnv; + } + + private static _getLangEnvVariable(locale?: string) { + const parts = locale ? locale.split('-') : []; + const n = parts.length; + if (n === 0) { + // Fallback to en_US to prevent possible encoding issues. + return 'en_US.UTF-8'; + } + if (n === 1) { + // app.getLocale can return just a language without a variant, fill in the variant for + // supported languages as many shells expect a 2-part locale. + const languageVariants = { + de: 'DE', + en: 'US', + es: 'ES', + fr: 'FR', + it: 'IT', + ja: 'JP', + ko: 'KR', + ru: 'RU', + zh: 'CN' + }; + if (parts[0] in languageVariants) { + parts.push(languageVariants[parts[0]]); + } + } else { + // Ensure the variant is uppercase + parts[1] = parts[1].toUpperCase(); + } + return parts.join('_') + '.UTF-8'; + } + + public updateConfig(): void { + this._setCursorBlink(this._configHelper.config.cursorBlinking); + this._setCursorStyle(this._configHelper.config.cursorStyle); + this._setCommandsToSkipShell(this._configHelper.config.commandsToSkipShell); + this._setScrollback(this._configHelper.config.scrollback); + } + + private _setCursorBlink(blink: boolean): void { + if (this._xterm && this._xterm.getOption('cursorBlink') !== blink) { + this._xterm.setOption('cursorBlink', blink); + this._xterm.refresh(0, this._xterm.rows - 1); + } + } + + private _setCursorStyle(style: string): void { + if (this._xterm && this._xterm.getOption('cursorStyle') !== style) { + // 'line' is used instead of bar in VS Code to be consistent with editor.cursorStyle + const xtermOption = style === 'line' ? 'bar' : style; + this._xterm.setOption('cursorStyle', xtermOption); + } + } + + private _setCommandsToSkipShell(commands: string[]): void { + this._skipTerminalCommands = commands; + } + + private _setScrollback(lineCount: number): void { + if (this._xterm && this._xterm.getOption('scrollback') !== lineCount) { + this._xterm.setOption('scrollback', lineCount); + } + } + + public layout(dimension: Dimension): void { + const terminalWidth = this._evaluateColsAndRows(dimension.width, dimension.height); + if (!terminalWidth) { + return; + } + if (this._xterm) { + this._xterm.resize(this._cols, this._rows); + this._xterm.element.style.width = terminalWidth + 'px'; + } + this._processReady.then(() => { + if (this._process && this._process.connected) { + // The child process could aready be terminated + try { + this._process.send({ + event: 'resize', + cols: this._cols, + rows: this._rows + }); + } catch (error) { + // We tried to write to a closed pipe / channel. + if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') { + throw (error); + } + } + } + }); + } + + public static setTerminalProcessFactory(factory: ITerminalProcessFactory): void { + this._terminalProcessFactory = factory; + } + + public setTitle(title: string, eventFromProcess: boolean): void { + if (!title) { + return; + } + if (eventFromProcess) { + title = path.basename(title); + if (platform.isWindows) { + // Remove the .exe extension + title = title.split('.exe')[0]; + } + } else { + // If the title has not been set by the API or the rename command, unregister the handler that + // automatically updates the terminal name + if (this._process && this._messageTitleListener) { + this._process.removeListener('message', this._messageTitleListener); + this._messageTitleListener = null; + } + } + const didTitleChange = title !== this._title; + this._title = title; + if (didTitleChange) { + this._onTitleChanged.fire(title); + } + } +} + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + + // Scrollbar + const scrollbarSliderBackgroundColor = theme.getColor(scrollbarSliderBackground); + if (scrollbarSliderBackgroundColor) { + collector.addRule(` + .monaco-workbench .panel.integrated-terminal .xterm.focus .xterm-viewport, + .monaco-workbench .panel.integrated-terminal .xterm:focus .xterm-viewport, + .monaco-workbench .panel.integrated-terminal .xterm:hover .xterm-viewport { background-color: ${scrollbarSliderBackgroundColor}; }` + ); + } + + const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground); + if (scrollbarSliderHoverBackgroundColor) { + collector.addRule(`.monaco-workbench .panel.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover { background-color: ${scrollbarSliderHoverBackgroundColor}; }`); + } + + const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground); + if (scrollbarSliderActiveBackgroundColor) { + collector.addRule(`.monaco-workbench .panel.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:active { background-color: ${scrollbarSliderActiveBackgroundColor}; }`); + } +}); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts new file mode 100644 index 0000000000..d596d4940f --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts @@ -0,0 +1,331 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as dom from 'vs/base/browser/dom'; +import * as nls from 'vs/nls'; +import * as path from 'path'; +import * as platform from 'vs/base/common/platform'; +import * as pfs from 'vs/base/node/pfs'; +import Uri from 'vs/base/common/uri'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { TerminalWidgetManager } from 'vs/workbench/parts/terminal/browser/terminalWidgetManager'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +const pathPrefix = '(\\.\\.?|\\~)'; +const pathSeparatorClause = '\\/'; +// '":; are allowed in paths but they are often separators so ignore them +// Also disallow \\ to prevent a catastropic backtracking case #24798 +const excludedPathCharactersClause = '[^\\0\\s!$`&*()\\[\\]+\'":;\\\\]'; +/** A regex that matches paths in the form /foo, ~/foo, ./foo, ../foo, foo/bar */ +const unixLocalLinkClause = '((' + pathPrefix + '|(' + excludedPathCharactersClause + ')+)?(' + pathSeparatorClause + '(' + excludedPathCharactersClause + ')+)+)'; + +const winDrivePrefix = '[a-zA-Z]:'; +const winPathPrefix = '(' + winDrivePrefix + '|\\.\\.?|\\~)'; +const winPathSeparatorClause = '(\\\\|\\/)'; +const winExcludedPathCharactersClause = '[^\\0<>\\?\\|\\/\\s!$`&*()\\[\\]+\'":;]'; +/** A regex that matches paths in the form c:\foo, ~\foo, .\foo, ..\foo, foo\bar */ +const winLocalLinkClause = '((' + winPathPrefix + '|(' + winExcludedPathCharactersClause + ')+)?(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)+)'; + +/** As xterm reads from DOM, space in that case is nonbreaking char ASCII code - 160, +replacing space with nonBreakningSpace or space ASCII code - 32. */ +const lineAndColumnClause = [ + '((\\S*) on line ((\\d+)(, column (\\d+))?))', // (file path) on line 8, column 13 + '((\\S*):line ((\\d+)(, column (\\d+))?))', // (file path):line 8, column 13 + '(([^\\s\\(\\)]*)(\\s?[\\(\\[](\\d+)(,\\s?(\\d+))?)[\\)\\]])', // (file path)(45), (file path) (45), (file path)(45,18), (file path) (45,18), (file path)(45, 18), (file path) (45, 18), also with [] + '(([^:\\s\\(\\)<>\'\"\\[\\]]*)(:(\\d+))?(:(\\d+))?)' // (file path):336, (file path):336:9 +].join('|').replace(/ /g, `[${'\u00A0'} ]`); + +// Changing any regex may effect this value, hence changes this as well if required. +const winLineAndColumnMatchIndex = 12; +const unixLineAndColumnMatchIndex = 23; + +// Each line and column clause have 6 groups (ie no. of expressions in round brackets) +const lineAndColumnClauseGroupCount = 6; + +/** Higher than local link, lower than hypertext */ +const CUSTOM_LINK_PRIORITY = -1; +/** Lowest */ +const LOCAL_LINK_PRIORITY = -2; + +export type XtermLinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void; +export type XtermLinkMatcherValidationCallback = (uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => void; + +export class TerminalLinkHandler { + private _hoverDisposables: IDisposable[] = []; + private _mouseMoveDisposable: IDisposable; + private _widgetManager: TerminalWidgetManager; + + private _localLinkPattern: RegExp; + + constructor( + private _xterm: any, + private _platform: platform.Platform, + private _initialCwd: string, + @IOpenerService private _openerService: IOpenerService, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, + @IConfigurationService private _configurationService: IConfigurationService + ) { + const baseLocalLinkClause = _platform === platform.Platform.Windows ? winLocalLinkClause : unixLocalLinkClause; + // Append line and column number regex + this._localLinkPattern = new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`); + + this._xterm.setHypertextLinkHandler(this._wrapLinkHandler(uri => { + this._handleHypertextLink(uri); + })); + + this._xterm.setHypertextValidationCallback((uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => { + this._validateWebLink(uri, element, callback); + }); + } + + public setWidgetManager(widgetManager: TerminalWidgetManager): void { + this._widgetManager = widgetManager; + } + + public registerCustomLinkHandler(regex: RegExp, handler: (uri: string) => void, matchIndex?: number, validationCallback?: XtermLinkMatcherValidationCallback): number { + const wrappedValidationCallback = (uri, element, callback) => { + this._addTooltipEventListeners(element); + if (validationCallback) { + validationCallback(uri, element, callback); + } else { + callback(true); + } + }; + return this._xterm.registerLinkMatcher(regex, this._wrapLinkHandler(handler), { + matchIndex, + validationCallback: wrappedValidationCallback, + priority: CUSTOM_LINK_PRIORITY + }); + } + + public registerLocalLinkHandler(): number { + const wrappedHandler = this._wrapLinkHandler(url => { + this._handleLocalLink(url); + }); + + return this._xterm.registerLinkMatcher(this._localLinkRegex, wrappedHandler, { + validationCallback: (link: string, element: HTMLElement, callback: (isValid: boolean) => void) => this._validateLocalLink(link, element, callback), + priority: LOCAL_LINK_PRIORITY + }); + } + + public dispose(): void { + this._hoverDisposables = dispose(this._hoverDisposables); + this._mouseMoveDisposable = dispose(this._mouseMoveDisposable); + } + + private _wrapLinkHandler(handler: (uri: string) => boolean | void): XtermLinkMatcherHandler { + return (event: MouseEvent, uri: string) => { + // Prevent default electron link handling so Alt+Click mode works normally + event.preventDefault(); + // Require correct modifier on click + if (!this._isLinkActivationModifierDown(event)) { + return false; + } + return handler(uri); + }; + } + + protected get _localLinkRegex(): RegExp { + return this._localLinkPattern; + } + + private _handleLocalLink(link: string): TPromise { + return this._resolvePath(link).then(resolvedLink => { + if (!resolvedLink) { + return void 0; + } + + let normalizedPath = path.normalize(path.resolve(resolvedLink)); + const normalizedUrl = this.extractLinkUrl(normalizedPath); + + normalizedPath = this._formatLocalLinkPath(normalizedPath); + + let resource = Uri.file(normalizedUrl); + resource = resource.with({ + fragment: Uri.parse(normalizedPath).fragment + }); + + return this._openerService.open(resource); + }); + } + + private _validateLocalLink(link: string, element: HTMLElement, callback: (isValid: boolean) => void): void { + this._resolvePath(link).then(resolvedLink => { + if (resolvedLink) { + this._addTooltipEventListeners(element); + } + callback(!!resolvedLink); + }); + } + + private _validateWebLink(link: string, element: HTMLElement, callback: (isValid: boolean) => void): void { + this._addTooltipEventListeners(element); + callback(true); + } + + private _handleHypertextLink(url: string): void { + let uri = Uri.parse(url); + this._openerService.open(uri); + } + + private _isLinkActivationModifierDown(event: MouseEvent): boolean { + const editorConf = this._configurationService.getConfiguration<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor'); + if (editorConf.multiCursorModifier === 'ctrlCmd') { + return !!event.altKey; + } + return platform.isMacintosh ? event.metaKey : event.ctrlKey; + } + + private _getLinkHoverString(): string { + const editorConf = this._configurationService.getConfiguration<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor'); + if (editorConf.multiCursorModifier === 'ctrlCmd') { + return nls.localize('terminalLinkHandler.followLinkAlt', 'Alt + click to follow link'); + } + if (platform.isMacintosh) { + return nls.localize('terminalLinkHandler.followLinkCmd', 'Cmd + click to follow link'); + } + return nls.localize('terminalLinkHandler.followLinkCtrl', 'Ctrl + click to follow link'); + } + + private _addTooltipEventListeners(element: HTMLElement): void { + let timeout = null; + let isMessageShowing = false; + this._hoverDisposables.push(dom.addDisposableListener(element, dom.EventType.MOUSE_OVER, e => { + element.classList.toggle('active', this._isLinkActivationModifierDown(e)); + this._mouseMoveDisposable = dom.addDisposableListener(element, dom.EventType.MOUSE_MOVE, e => { + element.classList.toggle('active', this._isLinkActivationModifierDown(e)); + }); + timeout = setTimeout(() => { + this._widgetManager.showMessage(element.offsetLeft, element.offsetTop, this._getLinkHoverString()); + isMessageShowing = true; + }, 500); + })); + this._hoverDisposables.push(dom.addDisposableListener(element, dom.EventType.MOUSE_OUT, () => { + element.classList.remove('active'); + if (this._mouseMoveDisposable) { + this._mouseMoveDisposable.dispose(); + } + clearTimeout(timeout); + this._widgetManager.closeMessage(); + isMessageShowing = false; + })); + } + + protected _preprocessPath(link: string): string { + if (this._platform === platform.Platform.Windows) { + // Resolve ~ -> %HOMEDRIVE%\%HOMEPATH% + if (link.charAt(0) === '~') { + if (!process.env.HOMEDRIVE || !process.env.HOMEPATH) { + return null; + } + link = `${process.env.HOMEDRIVE}\\${process.env.HOMEPATH + link.substring(1)}`; + } + + // Resolve relative paths (.\a, ..\a, ~\a, a\b) + if (!link.match('^' + winDrivePrefix)) { + if (!this._initialCwd) { + // Abort if no workspace is open + return null; + } + link = path.join(this._initialCwd, link); + } + } + // Resolve workspace path . | .. | -> /. | /.. | / + else if (link.charAt(0) !== '/' && link.charAt(0) !== '~') { + if (!this._initialCwd) { + // Abort if no workspace is open + return null; + } + link = path.join(this._initialCwd, link); + } + return link; + } + + private _resolvePath(link: string): TPromise { + link = this._preprocessPath(link); + if (!link) { + return TPromise.as(void 0); + } + + const linkUrl = this.extractLinkUrl(link); + if (!linkUrl) { + return TPromise.as(void 0); + } + + // Open an editor if the path exists + return pfs.fileExists(linkUrl).then(isFile => { + if (!isFile) { + return null; + } + return link; + }); + } + + /** + * Appends line number and column number to link if they exists. + * @param link link to format, will become link#line_num,col_num. + */ + private _formatLocalLinkPath(link: string): string { + const lineColumnInfo: LineColumnInfo = this.extractLineColumnInfo(link); + if (lineColumnInfo.lineNumber) { + link += `#${lineColumnInfo.lineNumber}`; + + if (lineColumnInfo.columnNumber) { + link += `,${lineColumnInfo.columnNumber}`; + } + } + + return link; + } + + /** + * Returns line and column number of URl if that is present. + * + * @param link Url link which may contain line and column number. + */ + public extractLineColumnInfo(link: string): LineColumnInfo { + const matches: string[] = this._localLinkRegex.exec(link); + const lineColumnInfo: LineColumnInfo = {}; + const lineAndColumnMatchIndex = this._platform === platform.Platform.Windows ? winLineAndColumnMatchIndex : unixLineAndColumnMatchIndex; + + for (let i = 0; i < lineAndColumnClause.length; i++) { + const lineMatchIndex = lineAndColumnMatchIndex + (lineAndColumnClauseGroupCount * i); + const rowNumber = matches[lineMatchIndex]; + if (rowNumber) { + lineColumnInfo['lineNumber'] = rowNumber; + // Check if column number exists + const columnNumber = matches[lineMatchIndex + 2]; + if (columnNumber) { + lineColumnInfo['columnNumber'] = columnNumber; + } + break; + } + } + + return lineColumnInfo; + } + + /** + * Returns url from link as link may contain line and column information. + * + * @param link url link which may contain line and column number. + */ + public extractLinkUrl(link: string): string { + const matches: string[] = this._localLinkRegex.exec(link); + if (!matches) { + return null; + } + return matches[1]; + } +} + +export interface LineColumnInfo { + lineNumber?: string; + columnNumber?: string; +}; diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts new file mode 100644 index 0000000000..591bffbd16 --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -0,0 +1,381 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as dom from 'vs/base/browser/dom'; +import * as nls from 'vs/nls'; +import * as platform from 'vs/base/common/platform'; +import { Action, IAction } from 'vs/base/common/actions'; +import { Builder, Dimension } from 'vs/base/browser/builder'; +import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ITerminalService, ITerminalFont, TERMINAL_PANEL_ID } from 'vs/workbench/parts/terminal/common/terminal'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; +import { TerminalFindWidget } from './terminalFindWidget'; +import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR } from './terminalColorRegistry'; +import { ColorIdentifier, editorHoverBackground, editorHoverBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; +import { KillTerminalAction, CreateNewTerminalAction, SwitchTerminalInstanceAction, SwitchTerminalInstanceActionItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; +import { Panel } from 'vs/workbench/browser/panel'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; + +export class TerminalPanel extends Panel { + + private _actions: IAction[]; + private _copyContextMenuAction: IAction; + private _contextMenuActions: IAction[]; + private _cancelContextMenu: boolean = false; + private _font: ITerminalFont; + private _fontStyleElement: HTMLElement; + private _parentDomElement: HTMLElement; + private _terminalContainer: HTMLElement; + private _themeStyleElement: HTMLElement; + private _findWidget: TerminalFindWidget; + + constructor( + @IConfigurationService private _configurationService: IConfigurationService, + @IContextMenuService private _contextMenuService: IContextMenuService, + @IContextViewService private _contextViewService: IContextViewService, + @IInstantiationService private _instantiationService: IInstantiationService, + @ITerminalService private _terminalService: ITerminalService, + @IThemeService protected themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService + ) { + super(TERMINAL_PANEL_ID, telemetryService, themeService); + } + + public create(parent: Builder): TPromise { + super.create(parent); + this._parentDomElement = parent.getHTMLElement(); + dom.addClass(this._parentDomElement, 'integrated-terminal'); + this._themeStyleElement = document.createElement('style'); + this._fontStyleElement = document.createElement('style'); + + this._terminalContainer = document.createElement('div'); + dom.addClass(this._terminalContainer, 'terminal-outer-container'); + + this._findWidget = this._instantiationService.createInstance(TerminalFindWidget); + + this._parentDomElement.appendChild(this._themeStyleElement); + this._parentDomElement.appendChild(this._fontStyleElement); + this._parentDomElement.appendChild(this._terminalContainer); + this._parentDomElement.appendChild(this._findWidget.getDomNode()); + + this._attachEventListeners(); + + this._terminalService.setContainers(this.getContainer().getHTMLElement(), this._terminalContainer); + + this._register(this.themeService.onThemeChange(theme => this._updateTheme(theme))); + this._register(this._configurationService.onDidUpdateConfiguration(() => this._updateFont())); + this._updateFont(); + this._updateTheme(); + + // Force another layout (first is setContainers) since config has changed + this.layout(new Dimension(this._terminalContainer.offsetWidth, this._terminalContainer.offsetHeight)); + return TPromise.as(void 0); + } + + public layout(dimension?: Dimension): void { + if (!dimension) { + return; + } + this._terminalService.terminalInstances.forEach((t) => { + t.layout(dimension); + }); + } + + public setVisible(visible: boolean): TPromise { + if (visible) { + if (this._terminalService.terminalInstances.length > 0) { + this._updateFont(); + this._updateTheme(); + } else { + return super.setVisible(visible).then(() => { + const instance = this._terminalService.createInstance(); + if (instance) { + this._updateFont(); + this._updateTheme(); + } + return TPromise.as(void 0); + }); + } + } + return super.setVisible(visible); + } + + public getActions(): IAction[] { + if (!this._actions) { + this._actions = [ + this._instantiationService.createInstance(SwitchTerminalInstanceAction, SwitchTerminalInstanceAction.ID, SwitchTerminalInstanceAction.LABEL), + this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.PANEL_LABEL), + this._instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL) + ]; + this._actions.forEach(a => { + this._register(a); + }); + } + return this._actions; + } + + private _getContextMenuActions(): IAction[] { + if (!this._contextMenuActions) { + this._copyContextMenuAction = this._instantiationService.createInstance(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, nls.localize('copy', "Copy")); + this._contextMenuActions = [ + this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, nls.localize('createNewTerminal', "New Terminal")), + new Separator(), + this._copyContextMenuAction, + this._instantiationService.createInstance(TerminalPasteAction, TerminalPasteAction.ID, nls.localize('paste', "Paste")), + this._instantiationService.createInstance(SelectAllTerminalAction, SelectAllTerminalAction.ID, nls.localize('selectAll', "Select All")), + new Separator(), + this._instantiationService.createInstance(ClearTerminalAction, ClearTerminalAction.ID, nls.localize('clear', "Clear")) + ]; + this._contextMenuActions.forEach(a => { + this._register(a); + }); + } + const activeInstance = this._terminalService.getActiveInstance(); + this._copyContextMenuAction.enabled = activeInstance && activeInstance.hasSelection(); + return this._contextMenuActions; + } + + public getActionItem(action: Action): IActionItem { + if (action.id === SwitchTerminalInstanceAction.ID) { + return this._instantiationService.createInstance(SwitchTerminalInstanceActionItem, action); + } + + return super.getActionItem(action); + } + + public focus(): void { + const activeInstance = this._terminalService.getActiveInstance(); + if (activeInstance) { + activeInstance.focus(true); + } + } + + public focusFindWidget() { + const activeInstance = this._terminalService.getActiveInstance(); + if (activeInstance && activeInstance.hasSelection() && activeInstance.selection.indexOf('\n') === -1) { + this._findWidget.reveal(activeInstance.selection); + } else { + this._findWidget.reveal(); + } + } + + public hideFindWidget() { + this._findWidget.hide(); + } + + public showNextFindTermFindWidget(): void { + this._findWidget.showNextFindTerm(); + } + + public showPreviousFindTermFindWidget(): void { + this._findWidget.showPreviousFindTerm(); + } + + private _attachEventListeners(): void { + this._register(dom.addDisposableListener(this._parentDomElement, 'mousedown', (event: MouseEvent) => { + if (this._terminalService.terminalInstances.length === 0) { + return; + } + + if (event.which === 2 && platform.isLinux) { + // Drop selection and focus terminal on Linux to enable middle button paste when click + // occurs on the selection itself. + this._terminalService.getActiveInstance().focus(); + } else if (event.which === 3) { + if (this._terminalService.configHelper.config.rightClickCopyPaste) { + let terminal = this._terminalService.getActiveInstance(); + if (terminal.hasSelection()) { + terminal.copySelection(); + terminal.clearSelection(); + } else { + terminal.paste(); + } + // Clear selection after all click event bubbling is finished on Mac to prevent + // right-click selecting a word which is seemed cannot be disabled. There is a + // flicker when pasting but this appears to give the best experience if the + // setting is enabled. + if (platform.isMacintosh) { + setTimeout(() => { + terminal.clearSelection(); + }, 0); + } + this._cancelContextMenu = true; + } + } + })); + this._register(dom.addDisposableListener(this._parentDomElement, 'contextmenu', (event: MouseEvent) => { + if (!this._cancelContextMenu) { + const standardEvent = new StandardMouseEvent(event); + let anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy }; + this._contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.as(this._getContextMenuActions()), + getActionsContext: () => this._parentDomElement + }); + } + this._cancelContextMenu = false; + })); + this._register(dom.addDisposableListener(this._parentDomElement, 'click', (event) => { + if (event.which === 3) { + return; + } + + const instance = this._terminalService.getActiveInstance(); + if (instance) { + this._terminalService.getActiveInstance().focus(); + } + })); + this._register(dom.addDisposableListener(this._parentDomElement, 'keyup', (event: KeyboardEvent) => { + if (event.keyCode === 27) { + // Keep terminal open on escape + event.stopPropagation(); + } + })); + this._register(dom.addDisposableListener(this._parentDomElement, dom.EventType.DROP, (e: DragEvent) => { + if (e.target === this._parentDomElement || dom.isAncestor(e.target as HTMLElement, this._parentDomElement)) { + if (!e.dataTransfer) { + return; + } + + // Check if the file was dragged from the tree explorer + let uri = e.dataTransfer.getData('URL'); + if (uri) { + uri = URI.parse(uri).path; + } else if (e.dataTransfer.files.length > 0) { + // Check if the file was dragged from the filesystem + uri = URI.file(e.dataTransfer.files[0].path).fsPath; + } + + if (!uri) { + return; + } + + const terminal = this._terminalService.getActiveInstance(); + terminal.sendText(TerminalPanel.preparePathForTerminal(uri), false); + } + })); + } + + private _updateTheme(theme?: ITheme): void { + if (!theme) { + theme = this.themeService.getTheme(); + } + + let css = ''; + ansiColorIdentifiers.forEach((colorId: ColorIdentifier, index: number) => { + if (colorId) { // should not happen, all indices should have a color defined. + let color = theme.getColor(colorId); + css += `.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-${index} { color: ${color}; }` + + `.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-${index} { background-color: ${color}; }`; + } + }); + const bgColor = theme.getColor(TERMINAL_BACKGROUND_COLOR); + if (bgColor) { + css += `.monaco-workbench .panel.integrated-terminal .terminal-outer-container { background-color: ${bgColor}; }`; + } + const fgColor = theme.getColor(TERMINAL_FOREGROUND_COLOR); + if (fgColor) { + css += `.monaco-workbench .panel.integrated-terminal .xterm { color: ${fgColor}; }`; + } + + const cursorFgColor = theme.getColor(TERMINAL_CURSOR_FOREGROUND_COLOR) || fgColor; + if (cursorFgColor) { + css += `.monaco-workbench .panel.integrated-terminal .xterm:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus .terminal-cursor,` + + `.monaco-workbench .panel.integrated-terminal .xterm:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar):focus .terminal-cursor { background-color: ${cursorFgColor} }` + + `.monaco-workbench .panel.integrated-terminal .xterm:not(.focus):not(:focus) .terminal-cursor { outline-color: ${cursorFgColor}; }` + + `.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-bar .terminal-cursor::before,` + + `.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-underline .terminal-cursor::before { background-color: ${cursorFgColor}; }` + + `.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-bar.focus.xterm-cursor-blink .terminal-cursor::before,` + + `.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-style-underline.focus.xterm-cursor-blink .terminal-cursor::before { background-color: ${cursorFgColor}; }`; + } + + const cursorBgColor = theme.getColor(TERMINAL_CURSOR_BACKGROUND_COLOR) || bgColor || theme.getColor(PANEL_BACKGROUND); + if (cursorBgColor) { + css += `.monaco-workbench .panel.integrated-terminal .xterm:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus .terminal-cursor,` + + `.monaco-workbench .panel.integrated-terminal .xterm:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar):focus .terminal-cursor { color: ${cursorBgColor} }`; + } + + // TODO: Reinstate, see #28397 + // const selectionColor = theme.getColor(TERMINAL_SELECTION_BACKGROUND_COLOR); + // if (selectionColor) { + // css += `.monaco-workbench .panel.integrated-terminal .xterm .xterm-selection div { background-color: ${selectionColor}; }`; + // } + // Borrow the editor's hover background for now + let hoverBackground = theme.getColor(editorHoverBackground); + if (hoverBackground) { + css += `.monaco-workbench .panel.integrated-terminal .terminal-message-widget { background-color: ${hoverBackground}; }`; + } + let hoverBorder = theme.getColor(editorHoverBorder); + if (hoverBorder) { + css += `.monaco-workbench .panel.integrated-terminal .terminal-message-widget { border: 1px solid ${hoverBorder}; }`; + } + let hoverForeground = theme.getColor(editorForeground); + if (hoverForeground) { + css += `.monaco-workbench .panel.integrated-terminal .terminal-message-widget { color: ${hoverForeground}; }`; + } + + this._themeStyleElement.innerHTML = css; + this._findWidget.updateTheme(theme); + } + + private _updateFont(): void { + if (this._terminalService.terminalInstances.length === 0) { + return; + } + let newFont = this._terminalService.configHelper.getFont(); + dom.toggleClass(this._parentDomElement, 'enable-ligatures', this._terminalService.configHelper.config.fontLigatures); + dom.toggleClass(this._parentDomElement, 'disable-bold', !this._terminalService.configHelper.config.enableBold); + if (!this._font || this._fontsDiffer(this._font, newFont)) { + this._fontStyleElement.innerHTML = '.monaco-workbench .panel.integrated-terminal .xterm {' + + `font-family: ${newFont.fontFamily};` + + `font-size: ${newFont.fontSize};` + + `line-height: ${newFont.lineHeight};` + + '}'; + this._font = newFont; + } + this.layout(new Dimension(this._parentDomElement.offsetWidth, this._parentDomElement.offsetHeight)); + } + + private _fontsDiffer(a: ITerminalFont, b: ITerminalFont): boolean { + return a.charHeight !== b.charHeight || + a.charWidth !== b.charWidth || + a.fontFamily !== b.fontFamily || + a.fontSize !== b.fontSize || + a.lineHeight !== b.lineHeight; + } + + /** + * Adds quotes to a path if it contains whitespaces + */ + public static preparePathForTerminal(path: string): string { + if (platform.isWindows) { + if (/\s+/.test(path)) { + return `"${path}"`; + } + return path; + } + path = path.replace(/(%5C|\\)/g, '\\\\'); + const charsToEscape = [ + ' ', '\'', '"', '?', ':', ';', '!', '*', '(', ')', '{', '}', '[', ']' + ]; + for (let i = 0; i < path.length; i++) { + const indexOfChar = charsToEscape.indexOf(path.charAt(i)); + if (indexOfChar >= 0) { + path = `${path.substring(0, i)}\\${path.charAt(i)}${path.substring(i + 1)}`; + i++; // Skip char due to escape char being added + } + } + return path; + } +} diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts new file mode 100644 index 0000000000..00631651ca --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts @@ -0,0 +1,246 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as pfs from 'vs/base/node/pfs'; +import * as platform from 'vs/base/common/platform'; +import product from 'vs/platform/node/product'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IQuickOpenService, IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; +import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, TERMINAL_PANEL_ID } from 'vs/workbench/parts/terminal/common/terminal'; +import { TerminalService as AbstractTerminalService } from 'vs/workbench/parts/terminal/common/terminalService'; +import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; +import { TerminalInstance } from 'vs/workbench/parts/terminal/electron-browser/terminalInstance'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChoiceService } from 'vs/platform/message/common/message'; +import Severity from 'vs/base/common/severity'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { TERMINAL_DEFAULT_SHELL_WINDOWS } from 'vs/workbench/parts/terminal/electron-browser/terminal'; +import { TerminalPanel } from 'vs/workbench/parts/terminal/electron-browser/terminalPanel'; +import { IWindowService } from 'vs/platform/windows/common/windows'; + +export class TerminalService extends AbstractTerminalService implements ITerminalService { + private _configHelper: TerminalConfigHelper; + public get configHelper(): ITerminalConfigHelper { return this._configHelper; }; + + constructor( + @IContextKeyService _contextKeyService: IContextKeyService, + @IConfigurationService _configurationService: IConfigurationService, + @IPanelService _panelService: IPanelService, + @IPartService _partService: IPartService, + @ILifecycleService _lifecycleService: ILifecycleService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IWindowService private _windowService: IWindowService, + @IQuickOpenService private _quickOpenService: IQuickOpenService, + @IConfigurationEditingService private _configurationEditingService: IConfigurationEditingService, + @IChoiceService private _choiceService: IChoiceService, + @IStorageService private _storageService: IStorageService + ) { + super(_contextKeyService, _configurationService, _panelService, _partService, _lifecycleService); + + this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, platform.platform); + } + + public createInstance(shell: IShellLaunchConfig = {}, wasNewTerminalAction?: boolean): ITerminalInstance { + let terminalInstance = this._instantiationService.createInstance(TerminalInstance, + this._terminalFocusContextKey, + this._configHelper, + this._terminalContainer, + shell); + terminalInstance.addDisposable(terminalInstance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged)); + terminalInstance.addDisposable(terminalInstance.onDisposed(this._onInstanceDisposed.fire, this._onInstanceDisposed)); + terminalInstance.addDisposable(terminalInstance.onDataForApi(this._onInstanceData.fire, this._onInstanceData)); + terminalInstance.addDisposable(terminalInstance.onProcessIdReady(this._onInstanceProcessIdReady.fire, this._onInstanceProcessIdReady)); + this.terminalInstances.push(terminalInstance); + if (this.terminalInstances.length === 1) { + // It's the first instance so it should be made active automatically + this.setActiveInstanceByIndex(0); + } + this._onInstancesChanged.fire(); + this._suggestShellChange(wasNewTerminalAction); + return terminalInstance; + } + + public focusFindWidget(): TPromise { + return this.showPanel(false).then(() => { + let panel = this._panelService.getActivePanel() as TerminalPanel; + panel.focusFindWidget(); + this._findWidgetVisible.set(true); + }); + } + + public hideFindWidget(): void { + const panel = this._panelService.getActivePanel() as TerminalPanel; + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + panel.hideFindWidget(); + this._findWidgetVisible.reset(); + panel.focus(); + } + } + + public showNextFindTermFindWidget(): void { + const panel = this._panelService.getActivePanel() as TerminalPanel; + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + panel.showNextFindTermFindWidget(); + } + } + + public showPreviousFindTermFindWidget(): void { + const panel = this._panelService.getActivePanel() as TerminalPanel; + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + panel.showPreviousFindTermFindWidget(); + } + } + + private _suggestShellChange(wasNewTerminalAction?: boolean): void { + // Only suggest on Windows since $SHELL works great for macOS/Linux + if (!platform.isWindows) { + return; + } + + // Only suggest when the terminal instance is being created by an explicit user action to + // launch a terminal, as opposed to something like tasks, debug, panel restore, etc. + if (!wasNewTerminalAction) { + return; + } + + // Don't suggest if the user has explicitly opted out + const neverSuggest = this._storageService.getBoolean(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, StorageScope.GLOBAL, false); + if (neverSuggest) { + return; + } + + // Never suggest if the setting is non-default already (ie. they set the setting manually) + if (this._configHelper.config.shell.windows !== TERMINAL_DEFAULT_SHELL_WINDOWS) { + this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true); + return; + } + + const message = nls.localize('terminal.integrated.chooseWindowsShellInfo', "You can change the default terminal shell by selecting the customize button."); + const options = [nls.localize('customize', "Customize"), nls.localize('cancel', "Cancel"), nls.localize('never again', "OK, Never Show Again")]; + this._choiceService.choose(Severity.Info, message, options, 1).then(choice => { + switch (choice) { + case 0: + return this.selectDefaultWindowsShell().then(shell => { + if (!shell) { + return TPromise.as(null); + } + // Launch a new instance with the newly selected shell + const instance = this.createInstance({ + executable: shell, + args: this._configHelper.config.shellArgs.windows + }); + if (instance) { + this.setActiveInstance(instance); + } + return TPromise.as(null); + }); + case 1: + return TPromise.as(null); + case 2: + this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true); + default: + return TPromise.as(null); + } + }); + } + + public selectDefaultWindowsShell(): TPromise { + return this._detectWindowsShells().then(shells => { + const options: IPickOptions = { + placeHolder: nls.localize('terminal.integrated.chooseWindowsShell', "Select your preferred terminal shell, you can change this later in your settings") + }; + return this._quickOpenService.pick(shells, options).then(value => { + if (!value) { + return null; + } + const shell = value.description; + const configChange = { key: 'terminal.integrated.shell.windows', value: shell }; + return this._configurationEditingService.writeConfiguration(ConfigurationTarget.USER, configChange).then(() => shell); + }); + }); + } + + private _detectWindowsShells(): TPromise { + // Determine the correct System32 path. We want to point to Sysnative + // when the 32-bit version of VS Code is running on a 64-bit machine. + // The reason for this is because PowerShell's important PSReadline + // module doesn't work if this is not the case. See #27915. + const is32ProcessOn64Windows = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'); + const system32Path = `${process.env['windir']}\\${is32ProcessOn64Windows ? 'Sysnative' : 'System32'}`; + const expectedLocations = { + 'Command Prompt': [`${system32Path}\\cmd.exe`], + PowerShell: [`${system32Path}\\WindowsPowerShell\\v1.0\\powershell.exe`], + 'WSL Bash': [`${system32Path}\\bash.exe`], + 'Git Bash': [ + `${process.env['ProgramW6432']}\\Git\\bin\\bash.exe`, + `${process.env['ProgramW6432']}\\Git\\usr\\bin\\bash.exe`, + `${process.env['ProgramFiles']}\\Git\\bin\\bash.exe`, + `${process.env['ProgramFiles']}\\Git\\usr\\bin\\bash.exe`, + ] + }; + const promises: TPromise<[string, string]>[] = []; + Object.keys(expectedLocations).forEach(key => promises.push(this._validateShellPaths(key, expectedLocations[key]))); + return TPromise.join(promises).then(results => { + return results.filter(result => !!result).map(result => { + return { + label: result[0], + description: result[1] + }; + }); + }); + } + + private _validateShellPaths(label: string, potentialPaths: string[]): TPromise<[string, string]> { + const current = potentialPaths.shift(); + return pfs.fileExists(current).then(exists => { + if (!exists) { + if (potentialPaths.length === 0) { + return null; + } + return this._validateShellPaths(label, potentialPaths); + } + return [label, current] as [string, string]; + }); + } + + public getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance { + const activeInstance = this.getActiveInstance(); + return activeInstance ? activeInstance : this.createInstance(undefined, wasNewTerminalAction); + } + + protected _showTerminalCloseConfirmation(): boolean { + const cancelId = 1; + let message; + if (this.terminalInstances.length === 1) { + message = nls.localize('terminalService.terminalCloseConfirmationSingular', "There is an active terminal session, do you want to kill it?"); + } else { + message = nls.localize('terminalService.terminalCloseConfirmationPlural', "There are {0} active terminal sessions, do you want to kill them?", this.terminalInstances.length); + } + const opts: Electron.ShowMessageBoxOptions = { + title: product.nameLong, + message, + type: 'warning', + buttons: [nls.localize('yes', "Yes"), nls.localize('cancel', "Cancel")], + noLink: true, + cancelId + }; + return this._windowService.showMessageBox(opts) === cancelId; + } + + public setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void { + this._configHelper.panelContainer = panelContainer; + this._terminalContainer = terminalContainer; + this._terminalInstances.forEach(terminalInstance => { + terminalInstance.attachToElement(this._terminalContainer); + }); + } +} diff --git a/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts new file mode 100644 index 0000000000..3bbe74bc8c --- /dev/null +++ b/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as platform from 'vs/base/common/platform'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Emitter, debounceEvent } from 'vs/base/common/event'; +import { ITerminalInstance } from 'vs/workbench/parts/terminal/common/terminal'; +import { Terminal as XTermTerminal } from 'xterm'; + +const SHELL_EXECUTABLES = ['cmd.exe', 'powershell.exe', 'bash.exe']; + +let windowsProcessTree; + +export class WindowsShellHelper { + private _childProcessIdStack: number[]; + private _onCheckShell: Emitter>; + private _isDisposed: boolean; + private _currentRequest: TPromise; + + public constructor( + private _rootProcessId: number, + private _rootShellExecutable: string, + private _terminalInstance: ITerminalInstance, + private _xterm: XTermTerminal + ) { + if (!platform.isWindows) { + throw new Error(`WindowsShellHelper cannot be instantiated on ${platform.platform}`); + } + + if (!windowsProcessTree) { + windowsProcessTree = require.__$__nodeRequire('windows-process-tree'); + } + + this._childProcessIdStack = [this._rootProcessId]; + this._isDisposed = false; + this._onCheckShell = new Emitter>(); + // The debounce is necessary to prevent multiple processes from spawning when + // the enter key or output is spammed + debounceEvent(this._onCheckShell.event, (l, e) => e, 150, true)(() => { + setTimeout(() => { + this.checkShell(); + }, 50); + }); + + this._xterm.on('lineFeed', () => this._onCheckShell.fire()); + this._xterm.on('keypress', () => this._onCheckShell.fire()); + } + + private checkShell(): void { + if (platform.isWindows && this._terminalInstance.isTitleSetByProcess) { + this.getShellName().then(title => { + if (!this._isDisposed) { + this._terminalInstance.setTitle(title, true); + } + }); + } + } + + private traverseTree(tree: any): string { + if (!tree) { + return ''; + } + if (SHELL_EXECUTABLES.indexOf(tree.name) === -1) { + return tree.name; + } + if (!tree.children || tree.children.length === 0) { + return tree.name; + } + let favouriteChild = 0; + for (; favouriteChild < tree.children.length; favouriteChild++) { + const child = tree.children[favouriteChild]; + if (!child.children || child.children.length === 0) { + break; + } + if (child.children[0].name !== 'conhost.exe') { + break; + } + } + if (favouriteChild >= tree.children.length) { + return tree.name; + } + return this.traverseTree(tree.children[favouriteChild]); + } + + public dispose(): void { + this._isDisposed = true; + } + + /** + * Returns the innermost shell executable running in the terminal + */ + public getShellName(): TPromise { + if (this._isDisposed) { + return TPromise.as(''); + } + // Prevent multiple requests at once, instead return current request + if (this._currentRequest) { + return this._currentRequest; + } + this._currentRequest = new TPromise(resolve => { + windowsProcessTree(this._rootProcessId, (tree) => { + const name = this.traverseTree(tree); + this._currentRequest = null; + resolve(name); + }); + }); + return this._currentRequest; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts new file mode 100644 index 0000000000..18a1b07d5d --- /dev/null +++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as cp from 'child_process'; +import * as os from 'os'; +import * as path from 'path'; +import * as pty from 'node-pty'; + +// The pty process needs to be run in its own child process to get around maxing out CPU on Mac, +// see https://github.com/electron/electron/issues/38 +var shellName: string; +if (os.platform() === 'win32') { + shellName = path.basename(process.env.PTYSHELL); +} else { + // Using 'xterm-256color' here helps ensure that the majority of Linux distributions will use a + // color prompt as defined in the default ~/.bashrc file. + shellName = 'xterm-256color'; +} +var shell = process.env.PTYSHELL; +var args = getArgs(); +var cwd = process.env.PTYCWD; +var cols = process.env.PTYCOLS; +var rows = process.env.PTYROWS; +var currentTitle = ''; + +setupPlanB(process.env.PTYPID); +cleanEnv(); + +interface IOptions { + name: string; + cwd: string; + cols?: number; + rows?: number; +} + +var options: IOptions = { + name: shellName, + cwd +}; +if (cols && rows) { + options.cols = parseInt(cols, 10); + options.rows = parseInt(rows, 10); +} + +var ptyProcess = pty.fork(shell, args, options); + +var closeTimeout; +var exitCode; + +// Allow any trailing data events to be sent before the exit event is sent. +// See https://github.com/Tyriar/node-pty/issues/72 +function queueProcessExit() { + if (closeTimeout) { + clearTimeout(closeTimeout); + } + closeTimeout = setTimeout(function () { + if (process.platform === 'win32') { + // Forcefully kill the entire process tree under the shell process + // on Windows as ptyProcess.kill can leave some lingering processes. + // See https://github.com/Microsoft/vscode/issues/26807 + cp.execFile('taskkill.exe', ['/T', '/F', '/PID', ptyProcess.pid.toString()]).on('close', () => { + process.exit(exitCode); + }); + } else { + ptyProcess.kill(); + process.exit(exitCode); + } + }, 250); +} + +ptyProcess.on('data', function (data) { + process.send({ + type: 'data', + content: data + }); + if (closeTimeout) { + clearTimeout(closeTimeout); + queueProcessExit(); + } +}); + +ptyProcess.on('exit', function (code) { + exitCode = code; + queueProcessExit(); +}); + +process.on('message', function (message) { + if (message.event === 'input') { + ptyProcess.write(message.data); + } else if (message.event === 'resize') { + ptyProcess.resize(message.cols, message.rows); + } else if (message.event === 'shutdown') { + queueProcessExit(); + } +}); + +sendProcessId(); +setupTitlePolling(); + +function getArgs() { + if (process.env['PTYSHELLCMDLINE']) { + return process.env['PTYSHELLCMDLINE']; + } + var args = []; + var i = 0; + while (process.env['PTYSHELLARG' + i]) { + args.push(process.env['PTYSHELLARG' + i]); + i++; + } + return args; +} + +function cleanEnv() { + var keys = [ + 'AMD_ENTRYPOINT', + 'ELECTRON_RUN_AS_NODE', + 'PTYCWD', + 'PTYPID', + 'PTYSHELL', + 'PTYCOLS', + 'PTYROWS', + 'PTYSHELLCMDLINE' + ]; + keys.forEach(function (key) { + if (process.env[key]) { + delete process.env[key]; + } + }); + var i = 0; + while (process.env['PTYSHELLARG' + i]) { + delete process.env['PTYSHELLARG' + i]; + } +} + +function setupPlanB(parentPid) { + setInterval(function () { + try { + process.kill(parentPid, 0); // throws an exception if the main process doesn't exist anymore. + } catch (e) { + process.exit(); + } + }, 5000); +} + +function sendProcessId() { + process.send({ + type: 'pid', + content: ptyProcess.pid + }); +} + +function setupTitlePolling() { + sendProcessTitle(); + setInterval(function () { + if (currentTitle !== ptyProcess.process) { + sendProcessTitle(); + } + }, 200); +} + +function sendProcessTitle() { + process.send({ + type: 'title', + content: ptyProcess.process + }); + currentTitle = ptyProcess.process; +} diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalColorRegistry.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalColorRegistry.test.ts new file mode 100644 index 0000000000..63a2b0a4dd --- /dev/null +++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalColorRegistry.test.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { Extensions as ThemeingExtensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ansiColorIdentifiers, registerColors } from 'vs/workbench/parts/terminal/electron-browser/terminalColorRegistry'; +import { ITheme, ThemeType } from 'vs/platform/theme/common/themeService'; +import { Color } from 'vs/base/common/color'; + +registerColors(); + +let themingRegistry = Registry.as(ThemeingExtensions.ColorContribution); +function getMockTheme(type: ThemeType): ITheme { + let theme = { + selector: '', + label: '', + type: type, + getColor: (colorId) => themingRegistry.resolveDefaultColor(colorId, theme), + defines: () => true + }; + return theme; +} + +suite('Workbench - TerminalColorRegistry', () => { + + test('hc colors', function () { + let theme = getMockTheme('hc'); + let colors = ansiColorIdentifiers.map(colorId => Color.Format.CSS.formatHexA(theme.getColor(colorId), true)); + + assert.deepEqual(colors, [ + '#000000', + '#cd0000', + '#00cd00', + '#cdcd00', + '#0000ee', + '#cd00cd', + '#00cdcd', + '#e5e5e5', + '#7f7f7f', + '#ff0000', + '#00ff00', + '#ffff00', + '#5c5cff', + '#ff00ff', + '#00ffff', + '#ffffff' + ], 'The high contrast terminal colors should be used when the hc theme is active'); + + }); + + test('light colors', function () { + let theme = getMockTheme('light'); + let colors = ansiColorIdentifiers.map(colorId => Color.Format.CSS.formatHexA(theme.getColor(colorId), true)); + + assert.deepEqual(colors, [ + '#000000', + '#cd3131', + '#00bc00', + '#949800', + '#0451a5', + '#bc05bc', + '#0598bc', + '#555555', + '#666666', + '#cd3131', + '#14ce14', + '#b5ba00', + '#0451a5', + '#bc05bc', + '#0598bc', + '#a5a5a5' + ], 'The light terminal colors should be used when the light theme is active'); + + }); + + test('dark colors', function () { + let theme = getMockTheme('dark'); + let colors = ansiColorIdentifiers.map(colorId => Color.Format.CSS.formatHexA(theme.getColor(colorId), true)); + + assert.deepEqual(colors, [ + '#000000', + '#cd3131', + '#0dbc79', + '#e5e510', + '#2472c8', + '#bc3fbc', + '#11a8cd', + '#e5e5e5', + '#666666', + '#f14c4c', + '#23d18b', + '#f5f543', + '#3b8eea', + '#d670d6', + '#29b8db', + '#e5e5e5' + ], 'The dark terminal colors should be used when a dark theme is active'); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts new file mode 100644 index 0000000000..4006ba9400 --- /dev/null +++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts @@ -0,0 +1,158 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { IConfigurationService, getConfigurationValue, IConfigurationValue, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; +import { Platform } from 'vs/base/common/platform'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; +import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; + + +class MockConfigurationService implements IConfigurationService { + public _serviceBrand: any; + public serviceId = IConfigurationService; + public constructor(private configuration: any = {}) { } + public reloadConfiguration(section?: string): TPromise { return TPromise.as(this.getConfiguration()); } + public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { return { value: getConfigurationValue(this.getConfiguration(), key), default: getConfigurationValue(this.getConfiguration(), key), user: getConfigurationValue(this.getConfiguration(), key), workspace: void 0, folder: void 0 }; } + public keys() { return { default: [], user: [], workspace: [], folder: [] }; } + public values() { return {}; } + public getConfiguration(): any { return this.configuration; } + public getConfigurationData(): any { return null; } + public onDidUpdateConfiguration() { return { dispose() { } }; } +} + +suite('Workbench - TerminalConfigHelper', () => { + let fixture: HTMLElement; + + setup(() => { + fixture = document.body; + }); + + test('TerminalConfigHelper - getFont fontFamily', function () { + let configurationService: IConfigurationService; + let configHelper: TerminalConfigHelper; + + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo' + }, + terminal: { + integrated: { + fontFamily: 'bar' + } + } + }); + configHelper = new TerminalConfigHelper(Platform.Linux, configurationService, null, null, null); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontFamily, 'bar', 'terminal.integrated.fontFamily should be selected over editor.fontFamily'); + + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo' + }, + terminal: { + integrated: { + fontFamily: 0 + } + } + }); + configHelper = new TerminalConfigHelper(Platform.Linux, configurationService, null, null, null); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontFamily, 'foo', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set'); + }); + + test('TerminalConfigHelper - getFont fontSize', function () { + let configurationService: IConfigurationService; + let configHelper: TerminalConfigHelper; + + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo', + fontSize: 1 + }, + terminal: { + integrated: { + fontFamily: 'bar', + fontSize: 2 + } + } + }); + configHelper = new TerminalConfigHelper(Platform.Linux, configurationService, null, null, null); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontSize, '2px', 'terminal.integrated.fontSize should be selected over editor.fontSize'); + + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo', + fontSize: 0 + }, + terminal: { + integrated: { + fontFamily: 0, + fontSize: 0 + } + } + }); + configHelper = new TerminalConfigHelper(Platform.Linux, configurationService, null, null, null); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontSize, `${EDITOR_FONT_DEFAULTS.fontSize}px`, 'The default editor font size should be used when editor.fontSize is 0 and terminal.integrated.fontSize not set'); + + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo', + fontSize: 0 + }, + terminal: { + integrated: { + fontFamily: 0, + fontSize: -10 + } + } + }); + configHelper = new TerminalConfigHelper(Platform.Linux, configurationService, null, null, null); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontSize, `${EDITOR_FONT_DEFAULTS.fontSize}px`, 'The default editor font size should be used when editor.fontSize is < 0 and terminal.integrated.fontSize not set'); + }); + + test('TerminalConfigHelper - getFont lineHeight', function () { + let configurationService: IConfigurationService; + let configHelper: TerminalConfigHelper; + + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo', + lineHeight: 1 + }, + terminal: { + integrated: { + fontFamily: 0, + lineHeight: 2 + } + } + }); + configHelper = new TerminalConfigHelper(Platform.Linux, configurationService, null, null, null); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().lineHeight, 2, 'terminal.integrated.lineHeight should be selected over editor.lineHeight'); + + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo', + lineHeight: 1 + }, + terminal: { + integrated: { + fontFamily: 0, + lineHeight: 0 + } + } + }); + configHelper = new TerminalConfigHelper(Platform.Linux, configurationService, null, null, null); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().lineHeight, 1.2, 'editor.lineHeight should be 1.2 when terminal.integrated.lineHeight not set'); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalInstance.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalInstance.test.ts new file mode 100644 index 0000000000..67f653d56a --- /dev/null +++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalInstance.test.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as os from 'os'; +import Uri from 'vs/base/common/uri'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { TerminalInstance } from 'vs/workbench/parts/terminal/electron-browser/terminalInstance'; +import { IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { TestMessageService, TestContextService, TestHistoryService } from 'vs/workbench/test/workbenchTestServices'; +import { MockContextKeyService, MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; + +class TestTerminalInstance extends TerminalInstance { + public _getCwd(shell: IShellLaunchConfig, root: Uri): string { + return super._getCwd(shell, root); + } + + protected _createProcess(): void { } + protected _createXterm(): void { } +} + +suite('Workbench - TerminalInstance', () => { + + let instantiationService: TestInstantiationService; + + setup(() => { + instantiationService = new TestInstantiationService(); + instantiationService.stub(IMessageService, new TestMessageService()); + instantiationService.stub(IHistoryService, new TestHistoryService()); + }); + + test('TerminalInstance - createTerminalEnv', function () { + const shell1 = { + executable: '/bin/foosh', + args: ['-bar', 'baz'] + }; + const parentEnv1: IStringDictionary = { + ok: true + }; + const env1 = TerminalInstance.createTerminalEnv(parentEnv1, shell1, '/foo', 'en-au'); + assert.ok(env1['ok'], 'Parent environment is copied'); + assert.deepStrictEqual(parentEnv1, { ok: true }, 'Parent environment is unchanged'); + assert.equal(env1['PTYPID'], process.pid.toString(), 'PTYPID is equal to the current PID'); + assert.equal(env1['PTYSHELL'], '/bin/foosh', 'PTYSHELL is equal to the provided shell'); + assert.equal(env1['PTYSHELLARG0'], '-bar', 'PTYSHELLARG0 is equal to the first shell argument'); + assert.equal(env1['PTYSHELLARG1'], 'baz', 'PTYSHELLARG1 is equal to the first shell argument'); + assert.ok(!('PTYSHELLARG2' in env1), 'PTYSHELLARG2 is unset'); + assert.equal(env1['PTYCWD'], '/foo', 'PTYCWD is equal to requested cwd'); + assert.equal(env1['LANG'], 'en_AU.UTF-8', 'LANG is equal to the requested locale with UTF-8'); + + const shell2 = { + executable: '/bin/foosh', + args: [] + }; + const parentEnv2: IStringDictionary = { + LANG: 'en_US.UTF-8' + }; + const env2 = TerminalInstance.createTerminalEnv(parentEnv2, shell2, '/foo', 'en-au'); + assert.ok(!('PTYSHELLARG0' in env2), 'PTYSHELLARG0 is unset'); + assert.equal(env2['PTYCWD'], '/foo', 'PTYCWD is equal to /foo'); + assert.equal(env2['LANG'], 'en_AU.UTF-8', 'LANG is equal to the requested locale with UTF-8'); + + const env3 = TerminalInstance.createTerminalEnv(parentEnv1, shell1, '/', null); + assert.equal(env3['LANG'], 'en_US.UTF-8', 'LANG is equal to en_US.UTF-8 as fallback.'); // More info on issue #14586 + + const env4 = TerminalInstance.createTerminalEnv(parentEnv2, shell1, '/', null); + assert.equal(env4['LANG'], 'en_US.UTF-8', 'LANG is equal to the parent environment\'s LANG'); + }); + + suite('_getCwd', () => { + let instance: TestTerminalInstance; + let instantiationService: TestInstantiationService; + let configHelper: { config: { cwd: string } }; + + setup(() => { + let contextKeyService = new MockContextKeyService(); + let keybindingService = new MockKeybindingService(); + let terminalFocusContextKey = contextKeyService.createKey('test', false); + instantiationService = new TestInstantiationService(); + instantiationService.stub(IMessageService, new TestMessageService()); + instantiationService.stub(IWorkspaceContextService, new TestContextService()); + instantiationService.stub(IKeybindingService, keybindingService); + instantiationService.stub(IContextKeyService, contextKeyService); + instantiationService.stub(IHistoryService, new TestHistoryService()); + configHelper = { + config: { + cwd: null + } + }; + instance = instantiationService.createInstance(TestTerminalInstance, terminalFocusContextKey, configHelper, null, null); + }); + + // This helper checks the paths in a cross-platform friendly manner + function assertPathsMatch(a: string, b: string): void { + assert.equal(Uri.file(a).fsPath, Uri.file(b).fsPath); + } + + test('should default to os.homedir() for an empty workspace', () => { + assertPathsMatch(instance._getCwd({ executable: null, args: [] }, null), os.homedir()); + }); + + test('should use to the workspace if it exists', () => { + assertPathsMatch(instance._getCwd({ executable: null, args: [] }, Uri.file('/foo')), '/foo'); + }); + + test('should use an absolute custom cwd as is', () => { + configHelper.config.cwd = '/foo'; + assertPathsMatch(instance._getCwd({ executable: null, args: [] }, null), '/foo'); + }); + + test('should normalize a relative custom cwd against the workspace path', () => { + configHelper.config.cwd = 'foo'; + assertPathsMatch(instance._getCwd({ executable: null, args: [] }, Uri.file('/bar')), '/bar/foo'); + configHelper.config.cwd = './foo'; + assertPathsMatch(instance._getCwd({ executable: null, args: [] }, Uri.file('/bar')), '/bar/foo'); + configHelper.config.cwd = '../foo'; + assertPathsMatch(instance._getCwd({ executable: null, args: [] }, Uri.file('/bar'), ), '/foo'); + }); + + test('should fall back for relative a custom cwd that doesn\'t have a workspace', () => { + configHelper.config.cwd = 'foo'; + assertPathsMatch(instance._getCwd({ executable: null, args: [] }, null), os.homedir()); + configHelper.config.cwd = './foo'; + assertPathsMatch(instance._getCwd({ executable: null, args: [] }, null), os.homedir()); + configHelper.config.cwd = '../foo'; + assertPathsMatch(instance._getCwd({ executable: null, args: [] }, null), os.homedir()); + }); + + test('should ignore custom cwd when told to ignore', () => { + configHelper.config.cwd = '/foo'; + assertPathsMatch(instance._getCwd({ executable: null, args: [], ignoreConfigurationCwd: true }, Uri.file('/bar')), '/bar'); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts new file mode 100644 index 0000000000..36b85527e6 --- /dev/null +++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts @@ -0,0 +1,204 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { Platform } from 'vs/base/common/platform'; +import { TerminalLinkHandler, LineColumnInfo } from 'vs/workbench/parts/terminal/electron-browser/terminalLinkHandler'; +import * as strings from 'vs/base/common/strings'; +import * as path from 'path'; +import * as sinon from 'sinon'; + +class TestTerminalLinkHandler extends TerminalLinkHandler { + public get localLinkRegex(): RegExp { + return this._localLinkRegex; + } + public preprocessPath(link: string): string { + return this._preprocessPath(link); + } +} + +class TestXterm { + public setHypertextLinkHandler() { } + public setHypertextValidationCallback() { } +} + +interface LinkFormatInfo { + urlFormat: string; + line?: string; + column?: string; +} + +suite('Workbench - TerminalLinkHandler', () => { + suite('localLinkRegex', () => { + test('Windows', () => { + const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null, null, null, null); + function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { + assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); + assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); + assert.equal(terminalLinkHandler.extractLinkUrl(`;${link};`), linkUrl); + assert.equal(terminalLinkHandler.extractLinkUrl(`(${link})`), linkUrl); + + if (lineNo) { + const lineColumnInfo: LineColumnInfo = terminalLinkHandler.extractLineColumnInfo(link); + assert.equal(lineColumnInfo.lineNumber, lineNo); + + if (columnNo) { + assert.equal(lineColumnInfo.columnNumber, columnNo); + } + } + } + + function generateAndTestLinks() { + const linkUrls = [ + 'c:\\foo', + 'c:/foo', + '.\\foo', + './foo', + '..\\foo', + '~\\foo', + '~/foo', + 'c:/a/long/path', + 'c:\\a\\long\\path', + 'c:\\mixed/slash\\path', + 'a/relative/path' + ]; + + const supportedLinkFormats: LinkFormatInfo[] = [ + { urlFormat: '{0}' }, + { urlFormat: '{0} on line {1}', line: '5' }, + { urlFormat: '{0} on line {1}, column {2}', line: '5', column: '3' }, + { urlFormat: '{0}:line {1}', line: '5' }, + { urlFormat: '{0}:line {1}, column {2}', line: '5', column: '3' }, + { urlFormat: '{0}({1})', line: '5' }, + { urlFormat: '{0} ({1})', line: '5' }, + { urlFormat: '{0}({1},{2})', line: '5', column: '3' }, + { urlFormat: '{0} ({1},{2})', line: '5', column: '3' }, + { urlFormat: '{0}({1}, {2})', line: '5', column: '3' }, + { urlFormat: '{0} ({1}, {2})', line: '5', column: '3' }, + { urlFormat: '{0}:{1}', line: '5' }, + { urlFormat: '{0}:{1}:{2}', line: '5', column: '3' }, + { urlFormat: '{0}[{1}]', line: '5' }, + { urlFormat: '{0} [{1}]', line: '5' }, + { urlFormat: '{0}[{1},{2}]', line: '5', column: '3' }, + { urlFormat: '{0} [{1},{2}]', line: '5', column: '3' }, + { urlFormat: '{0}[{1}, {2}]', line: '5', column: '3' }, + { urlFormat: '{0} [{1}, {2}]', line: '5', column: '3' } + ]; + + linkUrls.forEach(linkUrl => { + supportedLinkFormats.forEach(linkFormatInfo => { + testLink( + strings.format(linkFormatInfo.urlFormat, linkUrl, linkFormatInfo.line, linkFormatInfo.column), + linkUrl, + linkFormatInfo.line, + linkFormatInfo.column + ); + }); + }); + } + + generateAndTestLinks(); + }); + + test('Linux', () => { + const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null, null, null, null); + function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { + assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); + assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); + assert.equal(terminalLinkHandler.extractLinkUrl(`;${link};`), linkUrl); + assert.equal(terminalLinkHandler.extractLinkUrl(`(${link})`), linkUrl); + + if (lineNo) { + const lineColumnInfo: LineColumnInfo = terminalLinkHandler.extractLineColumnInfo(link); + assert.equal(lineColumnInfo.lineNumber, lineNo); + + if (columnNo) { + assert.equal(lineColumnInfo.columnNumber, columnNo); + } + } + } + + function generateAndTestLinks() { + const linkUrls = [ + '/foo', + '~/foo', + './foo', + '../foo', + '/a/long/path', + 'a/relative/path' + ]; + + const supportedLinkFormats: LinkFormatInfo[] = [ + { urlFormat: '{0}' }, + // { urlFormat: '{0} on line {1}', line: '5' }, + // { urlFormat: '{0} on line {1}, column {2}', line: '5', column: '3' }, + // { urlFormat: '{0}:line {1}', line: '5' }, + // { urlFormat: '{0}:line {1}, column {2}', line: '5', column: '3' }, + { urlFormat: '{0}({1})', line: '5' }, + { urlFormat: '{0} ({1})', line: '5' }, + { urlFormat: '{0}({1},{2})', line: '5', column: '3' }, + { urlFormat: '{0} ({1},{2})', line: '5', column: '3' }, + { urlFormat: '{0}:{1}', line: '5' }, + { urlFormat: '{0}:{1}:{2}', line: '5', column: '3' }, + { urlFormat: '{0}[{1}]', line: '5' }, + { urlFormat: '{0} [{1}]', line: '5' }, + { urlFormat: '{0}[{1},{2}]', line: '5', column: '3' }, + { urlFormat: '{0} [{1},{2}]', line: '5', column: '3' } + ]; + + linkUrls.forEach(linkUrl => { + supportedLinkFormats.forEach(linkFormatInfo => { + testLink( + strings.format(linkFormatInfo.urlFormat, linkUrl, linkFormatInfo.line, linkFormatInfo.column), + linkUrl, + linkFormatInfo.line, + linkFormatInfo.column + ); + }); + }); + } + + generateAndTestLinks(); + }); + }); + + suite('preprocessPath', () => { + test('Windows', () => { + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, 'C:\\base', null, null, null); + + let stub = sinon.stub(path, 'join', function (arg1, arg2) { + return arg1 + '\\' + arg2; + }); + assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base\\./src/file1'); + assert.equal(linkHandler.preprocessPath('src\\file2'), 'C:\\base\\src\\file2'); + assert.equal(linkHandler.preprocessPath('C:\\absolute\\path\\file3'), 'C:\\absolute\\path\\file3'); + + stub.restore(); + }); + + test('Linux', () => { + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, '/base', null, null, null); + + let stub = sinon.stub(path, 'join', function (arg1, arg2) { + return arg1 + '/' + arg2; + }); + + assert.equal(linkHandler.preprocessPath('./src/file1'), '/base/./src/file1'); + assert.equal(linkHandler.preprocessPath('src/file2'), '/base/src/file2'); + assert.equal(linkHandler.preprocessPath('/absolute/path/file3'), '/absolute/path/file3'); + stub.restore(); + }); + + test('No Workspace', () => { + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null, null, null, null); + + assert.equal(linkHandler.preprocessPath('./src/file1'), null); + assert.equal(linkHandler.preprocessPath('src/file2'), null); + assert.equal(linkHandler.preprocessPath('/absolute/path/file3'), '/absolute/path/file3'); + }); + }); +}); diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalPanel.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalPanel.test.ts new file mode 100644 index 0000000000..4292840420 --- /dev/null +++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalPanel.test.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { TerminalPanel } from 'vs/workbench/parts/terminal/electron-browser/terminalPanel'; +import * as platform from 'vs/base/common/platform'; + +suite('Workbench - TerminalPanel', () => { + test('preparePathForTerminal', function () { + if (platform.isWindows) { + assert.equal(TerminalPanel.preparePathForTerminal('C:\\foo'), 'C:\\foo'); + assert.equal(TerminalPanel.preparePathForTerminal('C:\\foo bar'), '"C:\\foo bar"'); + return; + } + assert.equal(TerminalPanel.preparePathForTerminal('/a/\\foo bar"\'? ;\'?? :'), '/a/\\\\foo\\ bar\\"\\\'\\?\\ \\;\\\'\\?\\?\\ \\ \\:'); + assert.equal(TerminalPanel.preparePathForTerminal('/\\\'"?:;!*(){}[]'), '/\\\\\\\'\\"\\?\\:\\;\\!\\*\\(\\)\\{\\}\\[\\]'); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts new file mode 100644 index 0000000000..16864922db --- /dev/null +++ b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts @@ -0,0 +1,217 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { firstIndex } from 'vs/base/common/arrays'; +import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; +import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; +import { IWorkbenchThemeService, COLOR_THEME_SETTING, ICON_THEME_SETTING } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions'; +import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { Delayer } from 'vs/base/common/async'; +import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IColorRegistry, Extensions as ColorRegistryExtensions } from 'vs/platform/theme/common/colorRegistry'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Color } from 'vs/base/common/color'; + +export class SelectColorThemeAction extends Action { + + static ID = 'workbench.action.selectTheme'; + static LABEL = localize('selectTheme.label', "Color Theme"); + + constructor( + id: string, + label: string, + @IQuickOpenService private quickOpenService: IQuickOpenService, + @IMessageService private messageService: IMessageService, + @IWorkbenchThemeService private themeService: IWorkbenchThemeService, + @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, + @IViewletService private viewletService: IViewletService, + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService + ) { + super(id, label); + } + + run(): TPromise { + return this.themeService.getColorThemes().then(themes => { + const currentTheme = this.themeService.getColorTheme(); + + const pickInMarketPlace = findInMarketplacePick(this.viewletService, 'category:themes', localize('installColorThemes', "Install Additional Color Themes...")); + + const picks: IPickOpenEntry[] = themes + .map(theme => ({ id: theme.id, label: theme.label, description: theme.description })) + .sort((t1, t2) => t1.label.localeCompare(t2.label)); + + const selectTheme = (theme, applyTheme) => { + if (theme === pickInMarketPlace) { + theme = currentTheme; + } + let target = null; + if (applyTheme) { + let confValue = this.configurationService.lookup(COLOR_THEME_SETTING); + target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; + } + + this.themeService.setColorTheme(theme.id, target).done(null, + err => { + this.themeService.setColorTheme(currentTheme.id, null); + } + ); + }; + + const placeHolder = localize('themes.selectTheme', "Select Color Theme (Up/Down Keys to Preview)"); + const autoFocusIndex = firstIndex(picks, p => p.id === currentTheme.id); + const delayer = new Delayer(100); + + if (this.extensionGalleryService.isEnabled()) { + picks.push(pickInMarketPlace); + } + + return this.quickOpenService.pick(picks, { placeHolder, autoFocus: { autoFocusIndex } }) + .then( + theme => delayer.trigger(() => selectTheme(theme || currentTheme, true), 0), + null, + theme => delayer.trigger(() => selectTheme(theme, false)) + ); + }); + } +} + +class SelectIconThemeAction extends Action { + + static ID = 'workbench.action.selectIconTheme'; + static LABEL = localize('selectIconTheme.label', "File Icon Theme"); + + constructor( + id: string, + label: string, + @IQuickOpenService private quickOpenService: IQuickOpenService, + @IMessageService private messageService: IMessageService, + @IWorkbenchThemeService private themeService: IWorkbenchThemeService, + @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, + @IViewletService private viewletService: IViewletService, + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService + + ) { + super(id, label); + } + + run(): TPromise { + return this.themeService.getFileIconThemes().then(themes => { + const currentTheme = this.themeService.getFileIconTheme(); + + const pickInMarketPlace = findInMarketplacePick(this.viewletService, 'tag:icon-theme', localize('installIconThemes', "Install Additional File Icon Themes...")); + + const picks: IPickOpenEntry[] = themes + .map(theme => ({ id: theme.id, label: theme.label, description: theme.description })) + .sort((t1, t2) => t1.label.localeCompare(t2.label)); + + picks.splice(0, 0, { id: '', label: localize('noIconThemeLabel', 'None'), description: localize('noIconThemeDesc', 'Disable file icons') }); + + const selectTheme = (theme, applyTheme) => { + if (theme === pickInMarketPlace) { + theme = currentTheme; + } + let target = null; + if (applyTheme) { + let confValue = this.configurationService.lookup(ICON_THEME_SETTING); + target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; + } + this.themeService.setFileIconTheme(theme && theme.id, target).done(null, + err => { + this.messageService.show(Severity.Info, localize('problemChangingIconTheme', "Problem setting icon theme: {0}", err.message)); + this.themeService.setFileIconTheme(currentTheme.id, null); + } + ); + }; + + const placeHolder = localize('themes.selectIconTheme', "Select File Icon Theme"); + const autoFocusIndex = firstIndex(picks, p => p.id === currentTheme.id); + const delayer = new Delayer(100); + + + if (this.extensionGalleryService.isEnabled()) { + picks.push(pickInMarketPlace); + } + + return this.quickOpenService.pick(picks, { placeHolder, autoFocus: { autoFocusIndex } }) + .then( + theme => delayer.trigger(() => selectTheme(theme || currentTheme, true), 0), + null, + theme => delayer.trigger(() => selectTheme(theme, false)) + ); + }); + } +} + +function findInMarketplacePick(viewletService: IViewletService, query: string, label: string) { + return { + id: 'themes.findmore', + label: label, + separator: { border: true }, + alwaysShow: true, + run: () => viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => { + (viewlet).search(query); + viewlet.focus(); + }) + }; +} + +class GenerateColorThemeAction extends Action { + + static ID = 'workbench.action.generateColorTheme'; + static LABEL = localize('generateColorTheme.label', "Generate Color Theme From Current Settings"); + + constructor( + id: string, + label: string, + @IWorkbenchThemeService private themeService: IWorkbenchThemeService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + ) { + super(id, label); + } + + run(): TPromise { + let theme = this.themeService.getColorTheme(); + let colorRegistry = Registry.as(ColorRegistryExtensions.ColorContribution); + let resultingColors = {}; + colorRegistry.getColors().map(c => { + let color = theme.getColor(c.id, false); + if (color) { + resultingColors[c.id] = Color.Format.CSS.formatHexA(color, true); + } + }); + let contents = JSON.stringify({ + type: theme.type, + colors: resultingColors, + tokenColors: theme.tokenColors + }, null, '\t'); + return this.editorService.openEditor({ contents, language: 'json' }); + } +} + +const category = localize('preferences', "Preferences"); + +const colorThemeDescriptor = new SyncActionDescriptor(SelectColorThemeAction, SelectColorThemeAction.ID, SelectColorThemeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_T) }); +Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(colorThemeDescriptor, 'Preferences: Color Theme', category); + +const iconThemeDescriptor = new SyncActionDescriptor(SelectIconThemeAction, SelectIconThemeAction.ID, SelectIconThemeAction.LABEL); +Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(iconThemeDescriptor, 'Preferences: File Icon Theme', category); + + +const developerCategory = localize('developer', "Developer"); + +const generateColorThemeDescriptor = new SyncActionDescriptor(GenerateColorThemeAction, GenerateColorThemeAction.ID, GenerateColorThemeAction.LABEL); +Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(generateColorThemeDescriptor, 'Developer: Generate Color Theme From Current Settings', developerCategory); diff --git a/src/vs/workbench/parts/themes/test/electron-browser/fixtures/foo.js b/src/vs/workbench/parts/themes/test/electron-browser/fixtures/foo.js new file mode 100644 index 0000000000..469a6a71cb --- /dev/null +++ b/src/vs/workbench/parts/themes/test/electron-browser/fixtures/foo.js @@ -0,0 +1,14 @@ +const webdriver = require('selenium-webdriver'); + +function mergeObjects(target) { + var sources = []; + for (var _i = 1; _i < arguments.length; _i++) { + sources[_i - 1] = arguments[_i]; + } + sources.forEach(function (source) { + for (var key in source) { + target[key] = source[key]; + } + }); + return target; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts b/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts new file mode 100644 index 0000000000..cecbf97466 --- /dev/null +++ b/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts @@ -0,0 +1,267 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import paths = require('vs/base/common/paths'); +import URI from 'vs/base/common/uri'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import pfs = require('vs/base/node/pfs'); +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { toResource } from 'vs/workbench/common/editor'; +import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; +import { IGrammar, StackElement } from 'vscode-textmate'; +import { TokenizationRegistry } from 'vs/editor/common/modes'; +import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding'; +import { ThemeRule, findMatchingThemeRule } from 'vs/workbench/services/textMate/electron-browser/TMHelper'; +import { Color } from 'vs/base/common/color'; + +interface IToken { + c: string; + t: string; + r: { [themeName: string]: string; }; +} + +interface IThemedToken { + text: string; + color: Color; +} + +interface IThemesResult { + [themeName: string]: { + document: ThemeDocument; + tokens: IThemedToken[]; + }; +} + +class ThemeDocument { + private readonly _theme: IColorTheme; + private readonly _cache: { [scopes: string]: ThemeRule; }; + private readonly _defaultColor: string; + + constructor(theme: IColorTheme) { + this._theme = theme; + this._cache = Object.create(null); + this._defaultColor = '#000000'; + for (let i = 0, len = this._theme.tokenColors.length; i < len; i++) { + let rule = this._theme.tokenColors[i]; + if (!rule.scope) { + this._defaultColor = rule.settings.foreground; + } + } + } + + private _generateExplanation(selector: string, color: Color): string { + return `${selector}: ${Color.Format.CSS.formatHexA(color, true).toUpperCase()}`; + } + + public explainTokenColor(scopes: string, color: Color): string { + + let matchingRule = this._findMatchingThemeRule(scopes); + if (!matchingRule) { + let expected = Color.fromHex(this._defaultColor); + // No matching rule + if (!color.equals(expected)) { + throw new Error(`[${this._theme.label}]: Unexpected color ${Color.Format.CSS.formatHexA(color)} for ${scopes}. Expected default ${Color.Format.CSS.formatHexA(expected)}`); + } + return this._generateExplanation('default', color); + } + + let expected = Color.fromHex(matchingRule.settings.foreground); + if (!color.equals(expected)) { + throw new Error(`[${this._theme.label}]: Unexpected color ${Color.Format.CSS.formatHexA(color)} for ${scopes}. Expected ${Color.Format.CSS.formatHexA(expected)} coming in from ${matchingRule.rawSelector}`); + } + return this._generateExplanation(matchingRule.rawSelector, color); + } + + private _findMatchingThemeRule(scopes: string): ThemeRule { + if (!this._cache[scopes]) { + this._cache[scopes] = findMatchingThemeRule(this._theme, scopes.split(' ')); + } + return this._cache[scopes]; + } +} + +class Snapper { + + constructor( + @IModeService private modeService: IModeService, + @IWorkbenchThemeService private themeService: IWorkbenchThemeService, + @ITextMateService private textMateService: ITextMateService + ) { + } + + private _themedTokenize(grammar: IGrammar, lines: string[]): IThemedToken[] { + let colorMap = TokenizationRegistry.getColorMap(); + let state: StackElement = null; + let result: IThemedToken[] = [], resultLen = 0; + for (let i = 0, len = lines.length; i < len; i++) { + let line = lines[i]; + + let tokenizationResult = grammar.tokenizeLine2(line, state); + + for (let j = 0, lenJ = tokenizationResult.tokens.length >>> 1; j < lenJ; j++) { + let startOffset = tokenizationResult.tokens[(j << 1)]; + let metadata = tokenizationResult.tokens[(j << 1) + 1]; + let endOffset = j + 1 < lenJ ? tokenizationResult.tokens[((j + 1) << 1)] : line.length; + let tokenText = line.substring(startOffset, endOffset); + + let color = TokenMetadata.getForeground(metadata); + + result[resultLen++] = { + text: tokenText, + color: colorMap[color] + }; + } + + state = tokenizationResult.ruleStack; + } + + return result; + } + + private _tokenize(grammar: IGrammar, lines: string[]): IToken[] { + let state: StackElement = null; + let result: IToken[] = [], resultLen = 0; + for (let i = 0, len = lines.length; i < len; i++) { + let line = lines[i]; + + let tokenizationResult = grammar.tokenizeLine(line, state); + let lastScopes: string = null; + + for (let j = 0, lenJ = tokenizationResult.tokens.length; j < lenJ; j++) { + let token = tokenizationResult.tokens[j]; + let tokenText = line.substring(token.startIndex, token.endIndex); + let tokenScopes = token.scopes.join(' '); + + if (lastScopes === tokenScopes) { + result[resultLen - 1].c += tokenText; + } else { + lastScopes = tokenScopes; + result[resultLen++] = { + c: tokenText, + t: tokenScopes, + r: { + dark_plus: null, + light_plus: null, + dark_vs: null, + light_vs: null, + hc_black: null, + } + }; + } + } + + state = tokenizationResult.ruleStack; + } + return result; + } + + private _getThemesResult(grammar: IGrammar, lines: string[]): TPromise { + let currentTheme = this.themeService.getColorTheme(); + + let getThemeName = (id: string) => { + let part = 'vscode-theme-defaults-themes-'; + let startIdx = id.indexOf(part); + if (startIdx !== -1) { + return id.substring(startIdx + part.length, id.length - 5); + } + return void 0; + }; + + let result: IThemesResult = {}; + + return this.themeService.getColorThemes().then(themeDatas => { + let defaultThemes = themeDatas.filter(themeData => !!getThemeName(themeData.id)); + return TPromise.join(defaultThemes.map(defaultTheme => { + let themeId = defaultTheme.id; + return this.themeService.setColorTheme(themeId, null).then(success => { + if (success) { + let themeName = getThemeName(themeId); + result[themeName] = { + document: new ThemeDocument(this.themeService.getColorTheme()), + tokens: this._themedTokenize(grammar, lines) + }; + } + }); + })); + }).then(_ => { + return this.themeService.setColorTheme(currentTheme.id, null).then(_ => { + return result; + }); + }); + } + + private _enrichResult(result: IToken[], themesResult: IThemesResult): void { + let index: { [themeName: string]: number; } = {}; + let themeNames = Object.keys(themesResult); + for (let t = 0; t < themeNames.length; t++) { + let themeName = themeNames[t]; + index[themeName] = 0; + } + + for (let i = 0, len = result.length; i < len; i++) { + let token = result[i]; + + for (let t = 0; t < themeNames.length; t++) { + let themeName = themeNames[t]; + let themedToken = themesResult[themeName].tokens[index[themeName]]; + + themedToken.text = themedToken.text.substr(token.c.length); + token.r[themeName] = themesResult[themeName].document.explainTokenColor(token.t, themedToken.color); + if (themedToken.text.length === 0) { + index[themeName]++; + } + } + } + } + + public captureSyntaxTokens(fileName: string, content: string): TPromise { + return this.modeService.getOrCreateModeByFilenameOrFirstLine(fileName).then(mode => { + return this.textMateService.createGrammar(mode.getId()).then((grammar) => { + let lines = content.split(/\r\n|\r|\n/); + + let result = this._tokenize(grammar, lines); + return this._getThemesResult(grammar, lines).then((themesResult) => { + this._enrichResult(result, themesResult); + return result.filter(t => t.c.length > 0); + }); + }); + }); + } +} + +CommandsRegistry.registerCommand('_workbench.captureSyntaxTokens', function (accessor: ServicesAccessor, resource: URI) { + + let process = (resource: URI) => { + let filePath = resource.fsPath; + let fileName = paths.basename(filePath); + let snapper = accessor.get(IInstantiationService).createInstance(Snapper); + + return pfs.readFile(filePath).then(content => { + return snapper.captureSyntaxTokens(fileName, content.toString()); + }); + }; + + if (!resource) { + let editorService = accessor.get(IWorkbenchEditorService); + let file = toResource(editorService.getActiveEditorInput(), { filter: 'file' }); + if (file) { + process(file).then(result => { + console.log(result); + }); + } else { + console.log('No file editor active'); + } + } else { + return process(resource); + } + return undefined; +}); + diff --git a/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.ts b/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.ts new file mode 100644 index 0000000000..4c17f11dcc --- /dev/null +++ b/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; + + + +class UnsupportedWorkspaceSettingsContribution implements IWorkbenchContribution { + + private static storageKey = 'workspace.settings.unsupported.warning'; + private toDispose: IDisposable[] = []; + private isUntrusted = false; + + constructor( + @ILifecycleService lifecycleService: ILifecycleService, + @IWorkspaceConfigurationService private workspaceConfigurationService: IWorkspaceConfigurationService, + @IPreferencesService private preferencesService: IPreferencesService, + @IMessageService private messageService: IMessageService, + @ITelemetryService private telemetryService: ITelemetryService, + @IStorageService private storageService: IStorageService + ) { + lifecycleService.onShutdown(this.dispose, this); + this.toDispose.push(this.workspaceConfigurationService.onDidUpdateConfiguration(e => this.checkWorkspaceSettings())); + } + + getId(): string { + return 'unsupportedWorkspaceSettings'; + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } + + private checkWorkspaceSettings(): void { + if (this.isUntrusted) { + return; + } + + const configurationKeys = this.workspaceConfigurationService.getUnsupportedWorkspaceKeys(); + this.isUntrusted = configurationKeys.length > 0; + if (this.isUntrusted && !this.hasShownWarning()) { + this.showWarning(configurationKeys); + } + } + + private hasShownWarning(): boolean { + return this.storageService.getBoolean(UnsupportedWorkspaceSettingsContribution.storageKey, StorageScope.WORKSPACE, false); + } + + private rememberWarningWasShown(): void { + this.storageService.store(UnsupportedWorkspaceSettingsContribution.storageKey, true, StorageScope.WORKSPACE); + } + + private showWarning(unsupportedKeys: string[]): void { + const message = nls.localize('unsupportedWorkspaceSettings', 'This Workspace contains settings that can only be set in User Settings. ({0})', unsupportedKeys.join(', ')); + + const openWorkspaceSettings = new Action('unsupportedWorkspaceSettings.openWorkspaceSettings', nls.localize('openWorkspaceSettings', 'Open Workspace Settings'), '', true, () => { + this.telemetryService.publicLog('workspace.settings.unsupported.review'); + this.rememberWarningWasShown(); + return this.preferencesService.openWorkspaceSettings(); + }); + + const openDocumentation = new Action('unsupportedWorkspaceSettings.openDocumentation', nls.localize('openDocumentation', 'Learn More'), '', true, () => { + this.telemetryService.publicLog('workspace.settings.unsupported.documentation'); + this.rememberWarningWasShown(); + window.open('https://go.microsoft.com/fwlink/?linkid=839878'); // Don't change link. + return TPromise.as(true); + }); + + const close = new Action('unsupportedWorkspaceSettings.Ignore', nls.localize('ignore', 'Ignore'), '', true, () => { + this.telemetryService.publicLog('workspace.settings.unsupported.ignore'); + this.rememberWarningWasShown(); + return TPromise.as(true); + }); + + const actions = [openWorkspaceSettings, openDocumentation, close]; + this.messageService.show(Severity.Warning, { message, actions }); + this.telemetryService.publicLog('workspace.settings.unsupported.warning'); + } +} + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(UnsupportedWorkspaceSettingsContribution); diff --git a/src/vs/workbench/parts/update/electron-browser/media/markdown.css b/src/vs/workbench/parts/update/electron-browser/media/markdown.css new file mode 100644 index 0000000000..fd54827f7b --- /dev/null +++ b/src/vs/workbench/parts/update/electron-browser/media/markdown.css @@ -0,0 +1,165 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +body { + padding: 10px 20px; + line-height: 22px; +} + +img { + max-width: 100%; + max-height: 100%; +} + +a { + color: #4080D0; + text-decoration: none; +} + +a:focus, +input:focus, +select:focus, +textarea:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +hr { + border: 0; + height: 2px; + border-bottom: 2px solid; +} + +h1 { + padding-bottom: 0.3em; + line-height: 1.2; + border-bottom-width: 1px; + border-bottom-style: solid; +} + +h1, h2, h3 { + font-weight: normal; +} + +a:hover { + color: #4080D0; + text-decoration: underline; +} + +table { + border-collapse: collapse; +} + +table > thead > tr > th { + text-align: left; + border-bottom: 1px solid; +} + +table > thead > tr > th, +table > thead > tr > td, +table > tbody > tr > th, +table > tbody > tr > td { + padding: 5px 10px; +} + +table > tbody > tr + tr > td { + border-top: 1px solid; +} + +blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left: 5px solid; +} + +code { + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-size: 14px; + line-height: 19px; +} + +.mac code { + font-size: 12px; + line-height: 18px; +} + +code > div { + padding: 16px; + border-radius: 3px; + overflow: auto; +} + +.monaco-tokenized-source { + white-space: pre; +} + +/** Theming */ + +.vscode-light { + color: rgb(30, 30, 30); +} + +.vscode-dark { + color: #DDD; +} + +.vscode-high-contrast { + color: white; +} + +.vscode-light code { + color: #A31515; +} + +.vscode-dark code { + color: #D7BA7D; +} + +.vscode-light code > div { + background-color: rgba(220, 220, 220, 0.4); +} + +.vscode-dark code > div { + background-color: rgba(10, 10, 10, 0.4); +} + +.vscode-high-contrast code > div { + background-color: rgb(0, 0, 0); +} + +.vscode-high-contrast h1 { + border-color: rgb(0, 0, 0); +} + +.vscode-light table > thead > tr > th { + border-color: rgba(0, 0, 0, 0.69); +} + +.vscode-dark table > thead > tr > th { + border-color: rgba(255, 255, 255, 0.69); +} + +.vscode-light h1, +.vscode-light hr, +.vscode-light table > tbody > tr + tr > td { + border-color: rgba(0, 0, 0, 0.18); +} + +.vscode-dark h1, +.vscode-dark hr, +.vscode-dark table > tbody > tr + tr > td { + border-color: rgba(255, 255, 255, 0.18); +} + +.vscode-light blockquote, +.vscode-dark blockquote { + background: rgba(127, 127, 127, 0.1); + border-color: rgba(0, 122, 204, 0.5); +} + +.vscode-high-contrast blockquote { + background: transparent; + border-color: #fff; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/update/electron-browser/media/update.contribution.css b/src/vs/workbench/parts/update/electron-browser/media/update.contribution.css new file mode 100644 index 0000000000..78cfde1432 --- /dev/null +++ b/src/vs/workbench/parts/update/electron-browser/media/update.contribution.css @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.update-activity { + -webkit-mask: url('update.svg') no-repeat 50% 50%; + -webkit-mask-size: 22px; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/update/electron-browser/media/update.svg b/src/vs/workbench/parts/update/electron-browser/media/update.svg new file mode 100644 index 0000000000..3dec2ba50f --- /dev/null +++ b/src/vs/workbench/parts/update/electron-browser/media/update.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts new file mode 100644 index 0000000000..51aceb9a4f --- /dev/null +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { marked } from 'vs/base/common/marked/marked'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Builder } from 'vs/base/browser/builder'; +import { append, $ } from 'vs/base/browser/dom'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ReleaseNotesInput } from './releaseNotesInput'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import WebView from 'vs/workbench/parts/html/browser/webview'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; +import { WebviewEditor } from 'vs/workbench/parts/html/browser/webviewEditor'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; + +function renderBody(body: string): string { + return ` + + + + + + + + ${body} + `; +} + +export class ReleaseNotesEditor extends WebviewEditor { + + static ID: string = 'workbench.editor.releaseNotes'; + + private contentDisposables: IDisposable[] = []; + private scrollYPercentage: number = 0; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService protected themeService: IThemeService, + @IOpenerService private openerService: IOpenerService, + @IModeService private modeService: IModeService, + @IPartService private partService: IPartService, + @IStorageService storageService: IStorageService, + @IContextViewService private _contextViewService: IContextViewService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(ReleaseNotesEditor.ID, telemetryService, themeService, storageService, contextKeyService); + } + + createEditor(parent: Builder): void { + const container = parent.getHTMLElement(); + this.content = append(container, $('.release-notes', { 'style': 'height: 100%; position: relative; overflow: hidden;' })); + } + + async setInput(input: ReleaseNotesInput, options: EditorOptions): TPromise { + if (this.input && this.input.matches(input)) { + return undefined; + } + + const { text } = input; + + this.contentDisposables = dispose(this.contentDisposables); + this.content.innerHTML = ''; + + await super.setInput(input, options); + + const result = []; + const renderer = new marked.Renderer(); + renderer.code = (code, lang) => { + const modeId = this.modeService.getModeIdForLanguageName(lang); + result.push(this.modeService.getOrCreateMode(modeId)); + return ''; + }; + + marked(text, { renderer }); + await TPromise.join(result); + + renderer.code = (code, lang) => { + const modeId = this.modeService.getModeIdForLanguageName(lang); + return `${tokenizeToString(code, modeId)}`; + }; + + const body = renderBody(marked(text, { renderer })); + this._webview = new WebView(this.content, this.partService.getContainer(Parts.EDITOR_PART), this._contextViewService, this.contextKey, this.findInputFocusContextKey); + if (this.input && this.input instanceof ReleaseNotesInput) { + const state = this.loadViewState(this.input.version); + if (state) { + this._webview.initialScrollProgress = state.scrollYPercentage; + } + } + this.onThemeChange(this.themeService.getTheme()); + this._webview.contents = [body]; + + this._webview.onDidClickLink(link => this.openerService.open(link), null, this.contentDisposables); + this._webview.onDidScroll(event => { + this.scrollYPercentage = event.scrollYPercentage; + }, null, this.contentDisposables); + this.themeService.onThemeChange(this.onThemeChange, this, this.contentDisposables); + this.contentDisposables.push(this._webview); + this.contentDisposables.push(toDisposable(() => this._webview = null)); + } + + layout(): void { + if (this._webview) { + this._webview.layout(); + } + } + + focus(): void { + if (!this._webview) { + return; + } + + this._webview.focus(); + } + + dispose(): void { + this.contentDisposables = dispose(this.contentDisposables); + super.dispose(); + } + + protected getViewState() { + return { + scrollYPercentage: this.scrollYPercentage + }; + } + + public clearInput(): void { + if (this.input instanceof ReleaseNotesInput) { + this.saveViewState(this.input.version, { + scrollYPercentage: this.scrollYPercentage + }); + } + super.clearInput(); + } + + public shutdown(): void { + if (this.input instanceof ReleaseNotesInput) { + this.saveViewState(this.input.version, { + scrollYPercentage: this.scrollYPercentage + }); + } + super.shutdown(); + } +} diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.ts new file mode 100644 index 0000000000..64b7de4b39 --- /dev/null +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesInput.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput } from 'vs/workbench/common/editor'; + +export class ReleaseNotesInput extends EditorInput { + + static get ID() { return 'workbench.releaseNotes.input'; } + + + get version(): string { return this._version; } + get text(): string { return this._text; } + + constructor(private _version: string, private _text: string) { + super(); + } + + getTypeId(): string { + return ReleaseNotesInput.ID; + } + + getName(): string { + return localize('releaseNotesInputName', "Release Notes: {0}", this.version); + } + + matches(other: any): boolean { + if (!(other instanceof ReleaseNotesInput)) { + return false; + } + + const otherInput = other as ReleaseNotesInput; + return this.version === otherInput.version; + } + + resolve(refresh?: boolean): TPromise { + return TPromise.as(null); + } + + supportsSplitEditor(): boolean { + return false; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/update/electron-browser/update.contribution.ts b/src/vs/workbench/parts/update/electron-browser/update.contribution.ts new file mode 100644 index 0000000000..84589e9b3b --- /dev/null +++ b/src/vs/workbench/parts/update/electron-browser/update.contribution.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import 'vs/css!./media/update.contribution'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { ReleaseNotesEditor } from 'vs/workbench/parts/update/electron-browser/releaseNotesEditor'; +import { ReleaseNotesInput } from 'vs/workbench/parts/update/electron-browser/releaseNotesInput'; +import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IGlobalActivityRegistry, GlobalActivityExtensions } from 'vs/workbench/browser/activity'; +import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, Win3264BitContribution } from './update'; + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(ProductContribution); + +if (process.platform === 'win32' && process.arch === 'ia32') { + Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(Win3264BitContribution); +} + +Registry.as(GlobalActivityExtensions) + .registerActivity(UpdateContribution); + +// Editor +const editorDescriptor = new EditorDescriptor( + ReleaseNotesEditor.ID, + nls.localize('release notes', "Release notes"), + 'vs/workbench/parts/update/electron-browser/releaseNotesEditor', + 'ReleaseNotesEditor' +); + +Registry.as(EditorExtensions.Editors) + .registerEditor(editorDescriptor, [new SyncDescriptor(ReleaseNotesInput)]); + +Registry.as(ActionExtensions.WorkbenchActions) + .registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), 'Show Release Notes'); + +// Configuration: Update +const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); +configurationRegistry.registerConfiguration({ + 'id': 'update', + 'order': 15, + 'title': nls.localize('updateConfigurationTitle', "Update"), + 'type': 'object', + 'properties': { + 'update.channel': { + 'type': 'string', + 'enum': ['none', 'default'], + 'default': 'default', + 'description': nls.localize('updateChannel', "Configure whether you receive automatic updates from an update channel. Requires a restart after change.") + } + } +}); diff --git a/src/vs/workbench/parts/update/electron-browser/update.ts b/src/vs/workbench/parts/update/electron-browser/update.ts new file mode 100644 index 0000000000..d021731719 --- /dev/null +++ b/src/vs/workbench/parts/update/electron-browser/update.ts @@ -0,0 +1,474 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction, Action } from 'vs/base/common/actions'; +import { mapEvent } from 'vs/base/common/event'; +import { IDisposable, dispose, empty as EmptyDisposable } from 'vs/base/common/lifecycle'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IMessageService, CloseAction, Severity } from 'vs/platform/message/common/message'; +import pkg from 'vs/platform/node/package'; +import product from 'vs/platform/node/product'; +import URI from 'vs/base/common/uri'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IActivityBarService, NumberBadge } from 'vs/workbench/services/activity/common/activityBarService'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ReleaseNotesInput } from 'vs/workbench/parts/update/electron-browser/releaseNotesInput'; +import { IGlobalActivity } from 'vs/workbench/browser/activity'; +import { IRequestService } from 'vs/platform/request/node/request'; +import { asText } from 'vs/base/node/request'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IUpdateService, State as UpdateState } from 'vs/platform/update/common/update'; +import * as semver from 'semver'; +import { OS, isLinux, isWindows } from 'vs/base/common/platform'; + +class ApplyUpdateAction extends Action { + constructor( @IUpdateService private updateService: IUpdateService) { + super('update.applyUpdate', nls.localize('updateNow', "Update Now"), null, true); + } + + run(): TPromise { + return this.updateService.quitAndInstall(); + } +} + +const NotNowAction = new Action( + 'update.later', + nls.localize('later', "Later"), + null, + true, + () => TPromise.as(true) +); + +const releaseNotesCache: { [version: string]: TPromise; } = Object.create(null); + +export function loadReleaseNotes(accessor: ServicesAccessor, version: string): TPromise { + const requestService = accessor.get(IRequestService); + const keybindingService = accessor.get(IKeybindingService); + const match = /^(\d+\.\d+)\./.exec(version); + + if (!match) { + return TPromise.wrapError(new Error('not found')); + } + + const versionLabel = match[1].replace(/\./g, '_'); + const baseUrl = 'https://code.visualstudio.com/raw'; + const url = `${baseUrl}/v${versionLabel}.md`; + const unassigned = nls.localize('unassigned', "unassigned"); + + const patchKeybindings = (text: string): string => { + const kb = (match: string, kb: string) => { + const keybinding = keybindingService.lookupKeybinding(kb); + + if (!keybinding) { + return unassigned; + } + + return keybinding.getLabel(); + }; + + const kbstyle = (match: string, kb: string) => { + const keybinding = KeybindingIO.readKeybinding(kb, OS); + + if (!keybinding) { + return unassigned; + } + + const resolvedKeybindings = keybindingService.resolveKeybinding(keybinding); + + if (resolvedKeybindings.length === 0) { + return unassigned; + } + + return resolvedKeybindings[0].getLabel(); + }; + + return text + .replace(/kb\(([a-z.\d\-]+)\)/gi, kb) + .replace(/kbstyle\(([^\)]+)\)/gi, kbstyle); + }; + + if (!releaseNotesCache[version]) { + releaseNotesCache[version] = requestService.request({ url }) + .then(asText) + .then(text => patchKeybindings(text)); + } + + return releaseNotesCache[version]; +} + +export class OpenLatestReleaseNotesInBrowserAction extends Action { + + constructor( + @IOpenerService private openerService: IOpenerService + ) { + super('update.openLatestReleaseNotes', nls.localize('releaseNotes', "Release Notes"), null, true); + } + + run(): TPromise { + const uri = URI.parse(product.releaseNotesUrl); + return this.openerService.open(uri); + } +} + +export abstract class AbstractShowReleaseNotesAction extends Action { + + constructor( + id: string, + label: string, + private returnValue: boolean, + private version: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(id, label, null, true); + } + + run(): TPromise { + if (!this.enabled) { + return TPromise.as(false); + } + + this.enabled = false; + + return this.instantiationService.invokeFunction(loadReleaseNotes, this.version) + .then(text => this.editorService.openEditor(this.instantiationService.createInstance(ReleaseNotesInput, this.version, text), { pinned: true })) + .then(() => true) + .then(null, () => { + const action = this.instantiationService.createInstance(OpenLatestReleaseNotesInBrowserAction); + return action.run().then(() => false); + }); + } +} + +export class ShowReleaseNotesAction extends AbstractShowReleaseNotesAction { + + constructor( + returnValue: boolean, + version: string, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super('update.showReleaseNotes', nls.localize('releaseNotes', "Release Notes"), returnValue, version, editorService, instantiationService); + } +} + +export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction { + + static ID = 'update.showCurrentReleaseNotes'; + static LABEL = nls.localize('showReleaseNotes', "Show Release Notes"); + + constructor( + id = ShowCurrentReleaseNotesAction.ID, + label = ShowCurrentReleaseNotesAction.LABEL, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(id, label, true, pkg.version, editorService, instantiationService); + } +} + +export class DownloadAction extends Action { + + constructor(private url: string, @IUpdateService private updateService: IUpdateService) { + super('update.download', nls.localize('downloadNow', "Download Now"), null, true); + } + + run(): TPromise { + return this.updateService.quitAndInstall(); + } +} + +const LinkAction = (id: string, message: string, licenseUrl: string) => new Action( + id, message, null, true, + () => { window.open(licenseUrl); return TPromise.as(null); } +); + +export class ProductContribution implements IWorkbenchContribution { + + private static KEY = 'releaseNotes/lastVersion'; + getId() { return 'vs.product'; } + + constructor( + @IStorageService storageService: IStorageService, + @IInstantiationService instantiationService: IInstantiationService, + @IMessageService messageService: IMessageService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService + ) { + // {{SQL CARBON EDIT}} + /* + const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, ''); + + // was there an update? if so, open release notes + if (product.releaseNotesUrl && lastVersion && pkg.version !== lastVersion) { + instantiationService.invokeFunction(loadReleaseNotes, pkg.version).then( + text => editorService.openEditor(instantiationService.createInstance(ReleaseNotesInput, pkg.version, text), { pinned: true }), + () => { + messageService.show(Severity.Info, { + message: nls.localize('read the release notes', "Welcome to {0} v{1}! Would you like to read the Release Notes?", product.nameLong, pkg.version), + actions: [ + instantiationService.createInstance(OpenLatestReleaseNotesInBrowserAction), + CloseAction + ] + }); + }); + } + + // should we show the new license? + if (product.licenseUrl && lastVersion && semver.satisfies(lastVersion, '<1.0.0') && semver.satisfies(pkg.version, '>=1.0.0')) { + messageService.show(Severity.Info, { + message: nls.localize('licenseChanged', "Our license terms have changed, please go through them.", product.nameLong, pkg.version), + actions: [ + LinkAction('update.showLicense', nls.localize('license', "Read License"), product.licenseUrl), + CloseAction + ] + }); + } + + storageService.store(ProductContribution.KEY, pkg.version, StorageScope.GLOBAL); + */ + } +} + +class NeverShowAgain { + + private readonly key: string; + + readonly action = new Action(`neverShowAgain:${this.key}`, nls.localize('neveragain', "Never Show Again"), undefined, true, () => { + return TPromise.wrap(this.storageService.store(this.key, true, StorageScope.GLOBAL)); + }); + + constructor(key: string, @IStorageService private storageService: IStorageService) { + this.key = `neverShowAgain:${key}`; + } + + shouldShow(): boolean { + return !this.storageService.getBoolean(this.key, StorageScope.GLOBAL, false); + } +} + +export class Win3264BitContribution implements IWorkbenchContribution { + + private static KEY = 'update/win32-64bits'; + private static URL = 'https://code.visualstudio.com/updates/v1_15#_windows-64-bit'; + private static INSIDER_URL = 'https://github.com/Microsoft/vscode-docs/blob/vnext/release-notes/v1_15.md#windows-64-bit'; + + getId() { return 'vs.win32-64bit'; } + + constructor( + @IStorageService storageService: IStorageService, + @IInstantiationService instantiationService: IInstantiationService, + @IMessageService messageService: IMessageService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService + ) { + const neverShowAgain = new NeverShowAgain(Win3264BitContribution.KEY, storageService); + + if (!neverShowAgain.shouldShow()) { + return; + } + + const url = product.quality === 'insider' + ? Win3264BitContribution.INSIDER_URL + : Win3264BitContribution.URL; + + messageService.show(Severity.Info, { + message: nls.localize('64bitisavailable', "{0} for 64-bit Windows is now available!", product.nameShort), + actions: [ + LinkAction('update.show64bitreleasenotes', nls.localize('learn more', "Learn More"), url), + CloseAction, + neverShowAgain.action + ] + }); + } +} + +class CommandAction extends Action { + + constructor( + commandId: string, + label: string, + @ICommandService private commandService: ICommandService + ) { + super(`command-action:${commandId}`, label, undefined, true, () => commandService.executeCommand(commandId)); + } +} + +export class UpdateContribution implements IGlobalActivity { + + private static readonly showCommandsId = 'workbench.action.showCommands'; + private static readonly openSettingsId = 'workbench.action.openGlobalSettings'; + private static readonly openKeybindingsId = 'workbench.action.openGlobalKeybindings'; + private static readonly selectColorThemeId = 'workbench.action.selectTheme'; + private static readonly selectIconThemeId = 'workbench.action.selectIconTheme'; + + get id() { return 'vs.update'; } + get name() { return ''; } + get cssClass() { return 'update-activity'; } + + private badgeDisposable: IDisposable = EmptyDisposable; + private disposables: IDisposable[] = []; + + constructor( + @IStorageService private storageService: IStorageService, + @ICommandService private commandService: ICommandService, + @IInstantiationService private instantiationService: IInstantiationService, + @IMessageService private messageService: IMessageService, + @IUpdateService private updateService: IUpdateService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IActivityBarService private activityBarService: IActivityBarService + ) { + const onUpdateAvailable = isLinux + ? mapEvent(updateService.onUpdateAvailable, e => e.version) + : mapEvent(updateService.onUpdateReady, e => e.version); + + onUpdateAvailable(this.onUpdateAvailable, this, this.disposables); + updateService.onError(this.onError, this, this.disposables); + updateService.onUpdateNotAvailable(this.onUpdateNotAvailable, this, this.disposables); + + updateService.onStateChange(this.onUpdateStateChange, this, this.disposables); + this.onUpdateStateChange(this.updateService.state); + + /* + The `update/lastKnownVersion` and `update/updateNotificationTime` storage keys are used in + combination to figure out when to show a message to the user that he should update. + + This message should appear if the user has received an update notification but hasn't + updated since 5 days. + */ + + const currentVersion = product.commit; + const lastKnownVersion = this.storageService.get('update/lastKnownVersion', StorageScope.GLOBAL); + + // if current version != stored version, clear both fields + if (currentVersion !== lastKnownVersion) { + this.storageService.remove('update/lastKnownVersion', StorageScope.GLOBAL); + this.storageService.remove('update/updateNotificationTime', StorageScope.GLOBAL); + } + } + + private onUpdateStateChange(state: UpdateState): void { + this.badgeDisposable.dispose(); + + const isUpdateAvailable = isLinux + ? state === UpdateState.UpdateAvailable + : state === UpdateState.UpdateDownloaded; + + if (isUpdateAvailable) { + const badge = new NumberBadge(1, () => nls.localize('updateIsReady', "New {0} update available.", product.nameShort)); + this.badgeDisposable = this.activityBarService.showGlobalActivity(this.id, badge); + } + } + + private onUpdateAvailable(version: string): void { + const currentVersion = product.commit; + const currentMillis = new Date().getTime(); + const lastKnownVersion = this.storageService.get('update/lastKnownVersion', StorageScope.GLOBAL); + + // if version != stored version, save version and date + if (currentVersion !== lastKnownVersion) { + this.storageService.store('update/lastKnownVersion', currentVersion, StorageScope.GLOBAL); + this.storageService.store('update/updateNotificationTime', currentMillis, StorageScope.GLOBAL); + } + + const updateNotificationMillis = this.storageService.getInteger('update/updateNotificationTime', StorageScope.GLOBAL, currentMillis); + const diffDays = (currentMillis - updateNotificationMillis) / (1000 * 60 * 60 * 24); + + // if 5 days have passed from stored date, show message service + if (diffDays > 5) { + this.showUpdateNotification(version); + } + } + + private showUpdateNotification(version: string): void { + const releaseNotesAction = this.instantiationService.createInstance(ShowReleaseNotesAction, false, version); + + if (isLinux) { + const downloadAction = this.instantiationService.createInstance(DownloadAction, version); + + this.messageService.show(severity.Info, { + message: nls.localize('thereIsUpdateAvailable', "There is an available update."), + actions: [downloadAction, NotNowAction, releaseNotesAction] + }); + } else { + const applyUpdateAction = this.instantiationService.createInstance(ApplyUpdateAction); + + this.messageService.show(severity.Info, { + message: nls.localize('updateAvailable', "{0} will be updated after it restarts.", product.nameLong), + actions: [applyUpdateAction, NotNowAction, releaseNotesAction] + }); + } + } + + private onUpdateNotAvailable(explicit: boolean): void { + if (!explicit) { + return; + } + + this.messageService.show(severity.Info, nls.localize('noUpdatesAvailable', "There are no updates currently available.")); + } + + private onError(err: any): void { + this.messageService.show(severity.Error, err); + } + + getActions(): IAction[] { + const updateAction = this.getUpdateAction(); + + return [ + new CommandAction(UpdateContribution.showCommandsId, nls.localize('commandPalette', "Command Palette..."), this.commandService), + new Separator(), + new CommandAction(UpdateContribution.openSettingsId, nls.localize('settings', "Settings"), this.commandService), + new CommandAction(UpdateContribution.openKeybindingsId, nls.localize('keyboardShortcuts', "Keyboard Shortcuts"), this.commandService), + new Separator(), + new CommandAction(UpdateContribution.selectColorThemeId, nls.localize('selectTheme.label', "Color Theme"), this.commandService), + new CommandAction(UpdateContribution.selectIconThemeId, nls.localize('themes.selectIconTheme.label', "File Icon Theme"), this.commandService), + new Separator(), + updateAction + ]; + } + + private getUpdateAction(): IAction { + switch (this.updateService.state) { + case UpdateState.Uninitialized: + return new Action('update.notavailable', nls.localize('not available', "Updates Not Available"), undefined, false); + + case UpdateState.CheckingForUpdate: + return new Action('update.checking', nls.localize('checkingForUpdates', "Checking For Updates..."), undefined, false); + + case UpdateState.UpdateAvailable: + if (isLinux) { + return new Action('update.linux.available', nls.localize('DownloadUpdate', "Download Available Update"), undefined, true, () => + this.updateService.quitAndInstall()); + } + + const updateAvailableLabel = isWindows + ? nls.localize('DownloadingUpdate', "Downloading Update...") + : nls.localize('InstallingUpdate', "Installing Update..."); + + return new Action('update.available', updateAvailableLabel, undefined, false); + + case UpdateState.UpdateDownloaded: + return new Action('update.restart', nls.localize('restartToUpdate', "Restart to Update..."), undefined, true, () => + this.updateService.quitAndInstall()); + + default: + return new Action('update.check', nls.localize('checkForUpdates', "Check for Updates..."), undefined, this.updateService.state === UpdateState.Idle, () => + this.updateService.checkForUpdates(true)); + } + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/views/browser/media/views.css b/src/vs/workbench/parts/views/browser/media/views.css new file mode 100644 index 0000000000..3c099bb7d8 --- /dev/null +++ b/src/vs/workbench/parts/views/browser/media/views.css @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.custom-view-tree-node-item { + display: flex; + height: 22px; + line-height: 22px; +} + +.custom-view-tree-node-item > .custom-view-tree-node-item-icon { + background-size: 16px; + background-position: left center; + background-repeat: no-repeat; + padding-right: 6px; + width: 16px; + height: 22px; + -webkit-font-smoothing: antialiased; +} + +.custom-view-tree-node-item > .custom-view-tree-node-item-label { + flex: 1; + text-overflow: ellipsis; + overflow: hidden; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/views/browser/treeView.ts b/src/vs/workbench/parts/views/browser/treeView.ts new file mode 100644 index 0000000000..41542d49ac --- /dev/null +++ b/src/vs/workbench/parts/views/browser/treeView.ts @@ -0,0 +1,459 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./media/views'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose, empty as EmptyDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as DOM from 'vs/base/browser/dom'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { IAction, IActionItem, ActionRunner } from 'vs/base/common/actions'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { ClickBehavior, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults'; +import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { attachListStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; +import { createActionItem, fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { ITree, IDataSource, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ViewsRegistry } from 'vs/workbench/parts/views/browser/viewsRegistry'; +import { ITreeViewDataProvider, ITreeItem, TreeItemCollapsibleState, TreeViewItemHandleArg } from 'vs/workbench/parts/views/common/views'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { CollapsibleState, ViewSizing } from 'vs/base/browser/ui/splitview/splitview'; +import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/parts/views/browser/views'; +import { ICommandService } from 'vs/platform/commands/common/commands'; + +export class TreeView extends CollapsibleView { + + private menus: Menus; + private viewFocusContext: IContextKey; + private activated: boolean = false; + private treeInputPromise: TPromise; + + private dataProviderElementChangeListener: IDisposable; + private disposables: IDisposable[] = []; + + constructor( + initialSize: number, + private options: IViewletViewOptions, + @IMessageService private messageService: IMessageService, + @IKeybindingService keybindingService: IKeybindingService, + @IContextMenuService contextMenuService: IContextMenuService, + @IInstantiationService private instantiationService: IInstantiationService, + @IListService private listService: IListService, + @IThemeService private themeService: IThemeService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IExtensionService private extensionService: IExtensionService, + @ICommandService private commandService: ICommandService + ) { + super(initialSize, { ...(options as IViewOptions), ariaHeaderLabel: options.name, sizing: ViewSizing.Flexible, collapsed: options.collapsed === void 0 ? true : options.collapsed }, keybindingService, contextMenuService); + this.menus = this.instantiationService.createInstance(Menus, this.id); + this.viewFocusContext = this.contextKeyService.createKey(this.id, void 0); + this.menus.onDidChangeTitle(() => this.updateActions(), this, this.disposables); + this.themeService.onThemeChange(() => this.tree.refresh() /* soft refresh */, this, this.disposables); + if (!options.collapsed) { + this.activate(); + } + } + + public renderHeader(container: HTMLElement): void { + const titleDiv = $('div.title').appendTo(container); + $('span').text(this.options.name).appendTo(titleDiv); + super.renderHeader(container); + } + + public renderBody(container: HTMLElement): void { + this.treeContainer = super.renderViewTree(container); + DOM.addClass(this.treeContainer, 'tree-explorer-viewlet-tree-view'); + + this.tree = this.createViewer($(this.treeContainer)); + this.setInput(); + } + + protected changeState(state: CollapsibleState): void { + super.changeState(state); + if (state === CollapsibleState.EXPANDED) { + this.activate(); + } + } + + private activate() { + if (!this.activated && this.extensionService) { + this.extensionService.activateByEvent(`onView:${this.id}`); + this.activated = true; + this.setInput(); + } + } + + public createViewer(container: Builder): ITree { + const dataSource = this.instantiationService.createInstance(TreeDataSource, this.id); + const renderer = this.instantiationService.createInstance(TreeRenderer); + const controller = this.instantiationService.createInstance(TreeController, this.id, this.menus); + const tree = new Tree(container.getHTMLElement(), { + dataSource, + renderer, + controller + }, { + keyboardSupport: false + }); + + this.toDispose.push(attachListStyler(tree, this.themeService)); + this.toDispose.push(this.listService.register(tree, [this.viewFocusContext])); + tree.addListener('selection', (event: any) => this.onSelection()); + return tree; + } + + getActions(): IAction[] { + return [...this.menus.getTitleActions()]; + } + + getSecondaryActions(): IAction[] { + return this.menus.getTitleSecondaryActions(); + } + + getActionItem(action: IAction): IActionItem { + return createActionItem(action, this.keybindingService, this.messageService); + } + + public setVisible(visible: boolean): TPromise { + return super.setVisible(visible); + } + + private setInput(): TPromise { + if (this.tree) { + if (!this.treeInputPromise) { + if (this.listenToDataProvider()) { + this.treeInputPromise = this.tree.setInput(new Root()); + } else { + this.treeInputPromise = new TPromise((c, e) => { + this.disposables.push(ViewsRegistry.onTreeViewDataProviderRegistered(id => { + if (this.id === id) { + if (this.listenToDataProvider()) { + this.tree.setInput(new Root()).then(() => c(null)); + } + } + })); + }); + } + } + return this.treeInputPromise; + } + return TPromise.as(null); + } + + private listenToDataProvider(): boolean { + let dataProvider = ViewsRegistry.getTreeViewDataProvider(this.id); + if (dataProvider) { + if (this.dataProviderElementChangeListener) { + this.dataProviderElementChangeListener.dispose(); + } + this.dataProviderElementChangeListener = dataProvider.onDidChange(element => this.refresh(element)); + const disposable = dataProvider.onDispose(() => { + this.dataProviderElementChangeListener.dispose(); + this.tree.setInput(new Root()); + disposable.dispose(); + }); + return true; + } + return false; + } + + public getOptimalWidth(): number { + const parentNode = this.tree.getHTMLElement(); + const childNodes = [].slice.call(parentNode.querySelectorAll('.outline-item-label > a')); + + return DOM.getLargestChildWidth(parentNode, childNodes); + } + + private onSelection(): void { + const selection: ITreeItem = this.tree.getSelection()[0]; + if (selection) { + if (selection.command) { + this.commandService.executeCommand(selection.command.id, ...(selection.command.arguments || [])); + } + } + } + + private refresh(elements: ITreeItem[]): void { + elements = elements ? elements : [this.tree.getInput()]; + for (const element of elements) { + element.children = null; + this.tree.refresh(element); + } + } + + dispose(): void { + dispose(this.disposables); + if (this.dataProviderElementChangeListener) { + this.dataProviderElementChangeListener.dispose(); + } + dispose(this.disposables); + super.dispose(); + } +} + +class Root implements ITreeItem { + label = 'root'; + handle = -1; + collapsibleState = TreeItemCollapsibleState.Expanded; +} + +class TreeDataSource implements IDataSource { + + constructor( + private id: string, + @IProgressService private progressService: IProgressService + ) { + } + + public getId(tree: ITree, node: ITreeItem): string { + return '' + node.handle; + } + + public hasChildren(tree: ITree, node: ITreeItem): boolean { + if (!this.getDataProvider()) { + return false; + } + return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded; + } + + public getChildren(tree: ITree, node: ITreeItem): TPromise { + if (node.children) { + return TPromise.as(node.children); + } + + const dataProvider = this.getDataProvider(); + if (dataProvider) { + const promise = node instanceof Root ? dataProvider.getElements() : dataProvider.getChildren(node); + this.progressService.showWhile(promise, 100); + return promise.then(children => { + node.children = children; + return children; + }); + } + return TPromise.as(null); + } + + public shouldAutoexpand(tree: ITree, node: ITreeItem): boolean { + return node.collapsibleState === TreeItemCollapsibleState.Expanded; + } + + public getParent(tree: ITree, node: any): TPromise { + return TPromise.as(null); + } + + private getDataProvider(): ITreeViewDataProvider { + return ViewsRegistry.getTreeViewDataProvider(this.id); + } +} + +interface ITreeExplorerTemplateData { + icon: Builder; + label: Builder; +} + +class TreeRenderer implements IRenderer { + + private static ITEM_HEIGHT = 22; + private static TREE_TEMPLATE_ID = 'treeExplorer'; + + constructor( @IThemeService private themeService: IThemeService) { + } + + public getHeight(tree: ITree, element: any): number { + return TreeRenderer.ITEM_HEIGHT; + } + + public getTemplateId(tree: ITree, element: any): string { + return TreeRenderer.TREE_TEMPLATE_ID; + } + + public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): ITreeExplorerTemplateData { + const el = $(container); + const item = $('.custom-view-tree-node-item'); + item.appendTo(el); + + const icon = $('.custom-view-tree-node-item-icon').appendTo(item); + const label = $('.custom-view-tree-node-item-label').appendTo(item); + const link = $('a.label').appendTo(label); + + return { label: link, icon }; + } + + public renderElement(tree: ITree, node: ITreeItem, templateId: string, templateData: ITreeExplorerTemplateData): void { + templateData.label.text(node.label).title(node.label); + + const theme = this.themeService.getTheme(); + const icon = theme.type === LIGHT ? node.icon : node.iconDark; + + if (icon) { + templateData.icon.getHTMLElement().style.backgroundImage = `url('${icon}')`; + DOM.addClass(templateData.icon.getHTMLElement(), 'custom-view-tree-node-item-icon'); + } else { + templateData.icon.getHTMLElement().style.backgroundImage = ''; + DOM.removeClass(templateData.icon.getHTMLElement(), 'custom-view-tree-node-item-icon'); + } + } + + public disposeTemplate(tree: ITree, templateId: string, templateData: ITreeExplorerTemplateData): void { + } +} + +class TreeController extends DefaultController { + + constructor( + private treeViewId: string, + private menus: Menus, + @IContextMenuService private contextMenuService: IContextMenuService, + @IKeybindingService private _keybindingService: IKeybindingService + ) { + super({ clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, keyboardSupport: false }); + } + + public onContextMenu(tree: ITree, node: ITreeItem, event: ContextMenuEvent): boolean { + event.preventDefault(); + event.stopPropagation(); + + tree.setFocus(node); + const actions = this.menus.getResourceContextActions(node); + if (!actions.length) { + return true; + } + const anchor = { x: event.posx + 1, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + + getActions: () => { + return TPromise.as(actions); + }, + + getActionItem: (action) => { + const keybinding = this._keybindingService.lookupKeybinding(action.id); + if (keybinding) { + return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + } + return null; + }, + + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + tree.DOMFocus(); + } + }, + + getActionsContext: () => ({ $treeViewId: this.treeViewId, $treeItemHandle: node.handle }), + + actionRunner: new MultipleSelectionActionRunner(() => tree.getSelection()) + }); + + return true; + } +} + +class MultipleSelectionActionRunner extends ActionRunner { + + constructor(private getSelectedResources: () => any[]) { + super(); + } + + runAction(action: IAction, context: any): TPromise { + if (action instanceof MenuItemAction) { + const selection = this.getSelectedResources(); + const filteredSelection = selection.filter(s => s !== context); + + if (selection.length === filteredSelection.length || selection.length === 1) { + return action.run(context); + } + + return action.run(context, ...filteredSelection); + } + + return super.runAction(action, context); + } +} + +class Menus implements IDisposable { + + private disposables: IDisposable[] = []; + private titleDisposable: IDisposable = EmptyDisposable; + private titleActions: IAction[] = []; + private titleSecondaryActions: IAction[] = []; + + private _onDidChangeTitle = new Emitter(); + get onDidChangeTitle(): Event { return this._onDidChangeTitle.event; } + + constructor( + private id: string, + @IContextKeyService private contextKeyService: IContextKeyService, + @IMenuService private menuService: IMenuService + ) { + if (this.titleDisposable) { + this.titleDisposable.dispose(); + this.titleDisposable = EmptyDisposable; + } + + const _contextKeyService = this.contextKeyService.createScoped(); + _contextKeyService.createKey('view', id); + + const titleMenu = this.menuService.createMenu(MenuId.ViewTitle, _contextKeyService); + const updateActions = () => { + this.titleActions = []; + this.titleSecondaryActions = []; + fillInActions(titleMenu, null, { primary: this.titleActions, secondary: this.titleSecondaryActions }); + this._onDidChangeTitle.fire(); + }; + + const listener = titleMenu.onDidChange(updateActions); + updateActions(); + + this.titleDisposable = toDisposable(() => { + listener.dispose(); + titleMenu.dispose(); + _contextKeyService.dispose(); + this.titleActions = []; + this.titleSecondaryActions = []; + }); + } + + getTitleActions(): IAction[] { + return this.titleActions; + } + + getTitleSecondaryActions(): IAction[] { + return this.titleSecondaryActions; + } + + getResourceContextActions(element: ITreeItem): IAction[] { + return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary; + } + + private getActions(menuId: MenuId, context: { key: string, value: string }): { primary: IAction[]; secondary: IAction[]; } { + const contextKeyService = this.contextKeyService.createScoped(); + contextKeyService.createKey('view', this.id); + contextKeyService.createKey(context.key, context.value); + + const menu = this.menuService.createMenu(menuId, contextKeyService); + const primary = []; + const secondary = []; + const result = { primary, secondary }; + fillInActions(menu, { shouldForwardArgs: true }, result); + + menu.dispose(); + contextKeyService.dispose(); + + return result; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/views/browser/views.ts b/src/vs/workbench/parts/views/browser/views.ts new file mode 100644 index 0000000000..4b368a9896 --- /dev/null +++ b/src/vs/workbench/parts/views/browser/views.ts @@ -0,0 +1,786 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IThemable, attachStyler } from 'vs/platform/theme/common/styler'; +import * as errors from 'vs/base/common/errors'; +import * as DOM from 'vs/base/browser/dom'; +import { $, Dimension, Builder } from 'vs/base/browser/builder'; +import { Scope } from 'vs/workbench/common/memento'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { prepareActions } from 'vs/workbench/browser/actions'; +import { Viewlet, ViewletRegistry, Extensions } from 'vs/workbench/browser/viewlet'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { DelayedDragHandler } from 'vs/base/browser/dnd'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { AbstractCollapsibleView, CollapsibleState, IView as IBaseView, SplitView, ViewSizing } from 'vs/base/browser/ui/splitview/splitview'; +import { ViewsRegistry, ViewLocation, IViewDescriptor } from 'vs/workbench/parts/views/browser/viewsRegistry'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND } from 'vs/workbench/common/theme'; +import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; + +export interface IViewOptions { + + id: string; + + name: string; + + actionRunner: IActionRunner; + + collapsed: boolean; + +} + +export interface IViewConstructorSignature { + + new(initialSize: number, options: IViewOptions, ...services: { _serviceBrand: any; }[]): IView; + +} + +export interface IView extends IBaseView, IThemable { + + id: string; + + name: string; + + getHeaderElement(): HTMLElement; + + create(): TPromise; + + setVisible(visible: boolean): TPromise; + + isVisible(): boolean; + + getActions(): IAction[]; + + getSecondaryActions(): IAction[]; + + getActionItem(action: IAction): IActionItem; + + getActionsContext(): any; + + showHeader(): boolean; + + hideHeader(): boolean; + + focusBody(): void; + + isExpanded(): boolean; + + expand(): void; + + collapse(): void; + + getOptimalWidth(): number; + + shutdown(): void; +} + +export interface ICollapsibleViewOptions extends IViewOptions { + + ariaHeaderLabel?: string; + + sizing: ViewSizing; + + initialBodySize?: number; + +} + +export abstract class CollapsibleView extends AbstractCollapsibleView implements IView { + + readonly id: string; + readonly name: string; + + protected treeContainer: HTMLElement; + protected tree: ITree; + protected toDispose: IDisposable[]; + protected toolBar: ToolBar; + protected actionRunner: IActionRunner; + protected isDisposed: boolean; + + private _isVisible: boolean; + + private dragHandler: DelayedDragHandler; + + constructor( + initialSize: number, + options: ICollapsibleViewOptions, + protected keybindingService: IKeybindingService, + protected contextMenuService: IContextMenuService + ) { + super(initialSize, { + ariaHeaderLabel: options.ariaHeaderLabel, + sizing: options.sizing, + bodySize: options.initialBodySize ? options.initialBodySize : 4 * 22, + initialState: options.collapsed ? CollapsibleState.COLLAPSED : CollapsibleState.EXPANDED, + }); + + this.id = options.id; + this.name = options.name; + this.actionRunner = options.actionRunner; + this.toDispose = []; + } + + protected changeState(state: CollapsibleState): void { + this.updateTreeVisibility(this.tree, state === CollapsibleState.EXPANDED); + + super.changeState(state); + } + + get draggableLabel(): string { return this.name; } + + public create(): TPromise { + return TPromise.as(null); + } + + getHeaderElement(): HTMLElement { + return this.header; + } + + public renderHeader(container: HTMLElement): void { + + // Tool bar + this.toolBar = new ToolBar($('div.actions').appendTo(container).getHTMLElement(), this.contextMenuService, { + orientation: ActionsOrientation.HORIZONTAL, + actionItemProvider: (action) => this.getActionItem(action), + ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.name), + getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id) + }); + this.toolBar.actionRunner = this.actionRunner; + this.updateActions(); + + // Expand on drag over + this.dragHandler = new DelayedDragHandler(container, () => { + if (!this.isExpanded()) { + this.expand(); + } + }); + } + + protected updateActions(): void { + this.toolBar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))(); + this.toolBar.context = this.getActionsContext(); + } + + protected renderViewTree(container: HTMLElement): HTMLElement { + const treeContainer = document.createElement('div'); + container.appendChild(treeContainer); + + return treeContainer; + } + + public getViewer(): ITree { + return this.tree; + } + + public isVisible(): boolean { + return this._isVisible; + } + + public setVisible(visible: boolean): TPromise { + if (this._isVisible !== visible) { + this._isVisible = visible; + this.updateTreeVisibility(this.tree, visible && this.state === CollapsibleState.EXPANDED); + } + + return TPromise.as(null); + } + + public focusBody(): void { + this.focusTree(); + } + + protected reveal(element: any, relativeTop?: number): TPromise { + if (!this.tree) { + return TPromise.as(null); // return early if viewlet has not yet been created + } + + return this.tree.reveal(element, relativeTop); + } + + public layoutBody(size: number): void { + if (this.tree) { + this.treeContainer.style.height = size + 'px'; + this.tree.layout(size); + } + } + + public getActions(): IAction[] { + return []; + } + + public getSecondaryActions(): IAction[] { + return []; + } + + public getActionItem(action: IAction): IActionItem { + return null; + } + + public getActionsContext(): any { + return undefined; + } + + public shutdown(): void { + // Subclass to implement + } + + public getOptimalWidth(): number { + return 0; + } + + public dispose(): void { + this.isDisposed = true; + this.treeContainer = null; + + if (this.tree) { + this.tree.dispose(); + } + + if (this.dragHandler) { + this.dragHandler.dispose(); + } + + this.toDispose = dispose(this.toDispose); + + if (this.toolBar) { + this.toolBar.dispose(); + } + + super.dispose(); + } + + private updateTreeVisibility(tree: ITree, isVisible: boolean): void { + if (!tree) { + return; + } + + if (isVisible) { + $(tree.getHTMLElement()).show(); + } else { + $(tree.getHTMLElement()).hide(); // make sure the tree goes out of the tabindex world by hiding it + } + + if (isVisible) { + tree.onVisible(); + } else { + tree.onHidden(); + } + } + + private focusTree(): void { + if (!this.tree) { + return; // return early if viewlet has not yet been created + } + + // Make sure the current selected element is revealed + const selection = this.tree.getSelection(); + if (selection.length > 0) { + this.reveal(selection[0], 0.5).done(null, errors.onUnexpectedError); + } + + // Pass Focus to Viewer + this.tree.DOMFocus(); + } +} + +export interface IViewletViewOptions extends IViewOptions { + + viewletSettings: object; + +} + +export interface IViewState { + + collapsed: boolean; + + size: number | undefined; + + isHidden: boolean; + + order: number; + +} + +export class ViewsViewlet extends Viewlet { + + protected viewletContainer: HTMLElement; + protected lastFocusedView: IView; + + private splitView: SplitView; + private viewHeaderContextMenuListeners: IDisposable[] = []; + protected dimension: Dimension; + private viewletSettings: object; + + private readonly viewsContextKeys: Set = new Set(); + protected viewsStates: Map = new Map(); + private areExtensionsReady: boolean = false; + + constructor( + id: string, + private location: ViewLocation, + private showHeaderInTitleWhenSingleView: boolean, + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService protected storageService: IStorageService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IContextKeyService protected contextKeyService: IContextKeyService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService + ) { + super(id, telemetryService, themeService); + + this.viewletSettings = this.getMemento(storageService, Scope.WORKSPACE); + + this._register(ViewsRegistry.onViewsRegistered(this.onViewsRegistered, this)); + this._register(ViewsRegistry.onViewsDeregistered(this.onViewsDeregistered, this)); + this._register(contextKeyService.onDidChangeContext(keys => this.onContextChanged(keys))); + + extensionService.onReady().then(() => { + this.areExtensionsReady = true; + this.onViewsUpdated(); + }); + } + + public create(parent: Builder): TPromise { + super.create(parent); + + this.viewletContainer = DOM.append(parent.getHTMLElement(), DOM.$('')); + this.splitView = this._register(new SplitView(this.viewletContainer, { canChangeOrderByDragAndDrop: true })); + this.attachSplitViewStyler(this.splitView); + this._register(this.splitView.onFocus((view: IView) => this.lastFocusedView = view)); + this._register(this.splitView.onDidOrderChange(() => { + const views = this.splitView.getViews(); + for (let order = 0; order < views.length; order++) { + this.viewsStates.get(views[order].id).order = order; + } + })); + + return this.onViewsRegistered(ViewsRegistry.getViews(this.location)) + .then(() => { + this.lastFocusedView = this.splitView.getViews()[0]; + this.focus(); + }); + } + + public getTitle(): string { + let title = Registry.as(Extensions.Viewlets).getViewlet(this.getId()).name; + if (this.showHeaderInTitleArea() && this.splitView.getViews()[0]) { + title += ': ' + this.splitView.getViews()[0].name; + } + return title; + } + + public getActions(): IAction[] { + if (this.showHeaderInTitleArea() && this.splitView.getViews()[0]) { + return this.splitView.getViews()[0].getActions(); + } + return []; + } + + public getSecondaryActions(): IAction[] { + if (this.showHeaderInTitleArea() && this.splitView.getViews()[0]) { + return this.splitView.getViews()[0].getSecondaryActions(); + } + return []; + } + + public getContextMenuActions(): IAction[] { + return this.getViewDescriptorsFromRegistry(true) + .filter(viewDescriptor => viewDescriptor.canToggleVisibility && this.contextKeyService.contextMatchesRules(viewDescriptor.when)) + .map(viewDescriptor => ({ + id: `${viewDescriptor.id}.toggleVisibility`, + label: viewDescriptor.name, + checked: this.isCurrentlyVisible(viewDescriptor), + enabled: true, + run: () => this.toggleViewVisibility(viewDescriptor.id) + })); + } + + public setVisible(visible: boolean): TPromise { + return super.setVisible(visible) + .then(() => TPromise.join(this.splitView.getViews().filter(view => view.isVisible() !== visible) + .map((view) => view.setVisible(visible)))) + .then(() => void 0); + } + + public focus(): void { + super.focus(); + + if (this.lastFocusedView) { + this.lastFocusedView.focus(); + } else if (this.views.length > 0) { + this.views[0].focus(); + } + } + + public layout(dimension: Dimension): void { + this.dimension = dimension; + this.layoutViews(); + } + + public getOptimalWidth(): number { + const additionalMargin = 16; + const optimalWidth = Math.max(...this.splitView.getViews().map(view => view.getOptimalWidth() || 0)); + return optimalWidth + additionalMargin; + } + + public shutdown(): void { + this.splitView.getViews().forEach((view) => view.shutdown()); + super.shutdown(); + } + + private layoutViews(): void { + if (this.splitView) { + this.splitView.layout(this.dimension.height); + for (const view of this.splitView.getViews()) { + let viewState = this.updateViewStateSize(view); + this.viewsStates.set(view.id, viewState); + } + } + } + + private toggleViewVisibility(id: string): void { + const view = this.getView(id); + let viewState = this.viewsStates.get(id); + if (view) { + viewState = viewState || this.createViewState(view); + viewState.isHidden = true; + } else { + viewState = viewState || { collapsed: true, size: void 0, isHidden: false, order: void 0 }; + viewState.isHidden = false; + } + this.viewsStates.set(id, viewState); + this.updateViews(); + } + + private onViewsRegistered(views: IViewDescriptor[]): TPromise { + this.viewsContextKeys.clear(); + for (const viewDescriptor of this.getViewDescriptorsFromRegistry()) { + if (viewDescriptor.when) { + for (const key of viewDescriptor.when.keys()) { + this.viewsContextKeys.add(key); + } + } + } + + return this.updateViews(); + } + + private onViewsDeregistered(views: IViewDescriptor[]): TPromise { + return this.updateViews(views); + } + + private onContextChanged(keys: string[]): void { + if (!keys) { + return; + } + + let hasToUpdate: boolean = false; + for (const key of keys) { + if (this.viewsContextKeys.has(key)) { + hasToUpdate = true; + break; + } + } + + if (hasToUpdate) { + this.updateViews(); + } + } + + protected updateViews(unregisteredViews: IViewDescriptor[] = []): TPromise { + if (this.splitView) { + + const registeredViews = this.getViewDescriptorsFromRegistry(); + const [visible, toAdd, toRemove] = registeredViews.reduce<[IViewDescriptor[], IViewDescriptor[], IViewDescriptor[]]>((result, viewDescriptor) => { + const isCurrentlyVisible = this.isCurrentlyVisible(viewDescriptor); + const canBeVisible = this.canBeVisible(viewDescriptor); + + if (canBeVisible) { + result[0].push(viewDescriptor); + } + + if (!isCurrentlyVisible && canBeVisible) { + result[1].push(viewDescriptor); + } + + if (isCurrentlyVisible && !canBeVisible) { + result[2].push(viewDescriptor); + } + + return result; + + }, [[], [], unregisteredViews]); + + const toCreate = []; + + if (toAdd.length || toRemove.length) { + for (const view of this.splitView.getViews()) { + let viewState = this.viewsStates.get(view.id); + if (!viewState || typeof viewState.size === 'undefined' || view.size !== viewState.size || !view.isExpanded() !== viewState.collapsed) { + viewState = this.updateViewStateSize(view); + this.viewsStates.set(view.id, viewState); + } + } + if (toRemove.length) { + for (const viewDescriptor of toRemove) { + let view = this.getView(viewDescriptor.id); + this.splitView.removeView(view); + if (this.lastFocusedView === view) { + this.lastFocusedView = null; + } + } + } + + for (const viewDescriptor of toAdd) { + let viewState = this.viewsStates.get(viewDescriptor.id); + let index = visible.indexOf(viewDescriptor); + const view = this.createView(viewDescriptor, + viewState ? viewState.size : this.getDefaultViewSize(), + { + id: viewDescriptor.id, + name: viewDescriptor.name, + actionRunner: this.getActionRunner(), + collapsed: viewState ? viewState.collapsed : void 0, + viewletSettings: this.viewletSettings + }); + toCreate.push(view); + + this.attachViewStyler(view); + this.splitView.addView(view, viewState && viewState.size ? Math.max(viewState.size, 1) : viewDescriptor.size, index); + } + + return TPromise.join(toCreate.map(view => view.create())) + .then(() => this.onViewsUpdated()) + .then(() => toCreate); + } + } + return TPromise.as([]); + } + + protected getDefaultViewSize(): number | undefined { + return undefined; + } + + private attachViewStyler(widget: IThemable, options?: { noContrastBorder?: boolean }): IDisposable { + return attachStyler(this.themeService, { + headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND, + headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND, + headerHighContrastBorder: (options && options.noContrastBorder) ? null : contrastBorder + }, widget); + } + + private attachSplitViewStyler(widget: IThemable): IDisposable { + return attachStyler(this.themeService, { + dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND + }, widget); + } + + private isCurrentlyVisible(viewDescriptor: IViewDescriptor): boolean { + return !!this.getView(viewDescriptor.id); + } + + private canBeVisible(viewDescriptor: IViewDescriptor): boolean { + const viewstate = this.viewsStates.get(viewDescriptor.id); + if (viewDescriptor.canToggleVisibility && viewstate && viewstate.isHidden) { + return false; + } + return this.contextKeyService.contextMatchesRules(viewDescriptor.when); + } + + private onViewsUpdated(): TPromise { + if (!this.splitView) { + return TPromise.as(null); + } + + if (this.showHeaderInTitleArea()) { + if (this.splitView.getViews()[0]) { + this.splitView.getViews()[0].hideHeader(); + if (!this.splitView.getViews()[0].isExpanded()) { + this.splitView.getViews()[0].expand(); + } + } + } else { + for (const view of this.splitView.getViews()) { + view.showHeader(); + } + } + + // Update title area since the title actions have changed. + this.updateTitleArea(); + + this.viewHeaderContextMenuListeners = dispose(this.viewHeaderContextMenuListeners); + for (const viewDescriptor of this.getViewDescriptorsFromRegistry()) { + const view = this.getView(viewDescriptor.id); + if (view) { + this.viewHeaderContextMenuListeners.push(DOM.addDisposableListener(view.getHeaderElement(), DOM.EventType.CONTEXT_MENU, e => { + e.stopPropagation(); + e.preventDefault(); + if (viewDescriptor.canToggleVisibility) { + this.onContextMenu(new StandardMouseEvent(e), view); + } + })); + } + } + + if (this.dimension) { + this.layoutViews(); + } + + return this.setVisible(this.isVisible()); + } + + private onContextMenu(event: StandardMouseEvent, view: IView): void { + event.stopPropagation(); + event.preventDefault(); + + let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.as([{ + id: `${view.id}.removeView`, + label: nls.localize('hideView', "Hide from Side Bar"), + enabled: true, + run: () => this.toggleViewVisibility(view.id) + }]), + }); + } + + protected showHeaderInTitleArea(): boolean { + if (!this.showHeaderInTitleWhenSingleView) { + return false; + } + if (this.splitView.getViews().length > 1) { + return false; + } + if (ViewLocation.getContributedViewLocation(this.location.id) && !this.areExtensionsReady) { + // Checks in cache so that view do not jump. See #29609 + let visibleViewsCount = 0; + const viewDecriptors = this.getViewDescriptorsFromRegistry(); + this.viewsStates.forEach((viewState, id) => { + const viewDescriptor = viewDecriptors.filter(viewDescriptor => viewDescriptor.id === id)[0]; + const isHidden = viewState.isHidden || (viewDescriptor && !this.contextKeyService.contextMatchesRules(viewDescriptor.when)); + if (!isHidden) { + visibleViewsCount++; + } + }); + return visibleViewsCount === 1; + } + return true; + } + + protected getViewDescriptorsFromRegistry(defaultOrder: boolean = false): IViewDescriptor[] { + return ViewsRegistry.getViews(this.location) + .sort((a, b) => { + const viewStateA = this.viewsStates.get(a.id); + const viewStateB = this.viewsStates.get(b.id); + const orderA = !defaultOrder && viewStateA ? viewStateA.order : a.order; + const orderB = !defaultOrder && viewStateB ? viewStateB.order : b.order; + + if (orderB === void 0 || orderB === null) { + return -1; + } + if (orderA === void 0 || orderA === null) { + return 1; + } + + return orderA - orderB; + }); + } + + protected createView(viewDescriptor: IViewDescriptor, initialSize: number, options: IViewletViewOptions): IView { + return this.instantiationService.createInstance(viewDescriptor.ctor, initialSize, options); + } + + protected get views(): IView[] { + return this.splitView ? this.splitView.getViews() : []; + } + + protected getView(id: string): IView { + return this.splitView.getViews().filter(view => view.id === id)[0]; + } + + private updateViewStateSize(view: IView): IViewState { + const currentState = this.viewsStates.get(view.id); + const newViewState = this.createViewState(view); + return currentState ? { ...currentState, collapsed: newViewState.collapsed, size: newViewState.size } : newViewState; + } + + protected createViewState(view: IView): IViewState { + const collapsed = !view.isExpanded(); + const size = collapsed && view instanceof CollapsibleView ? view.previousSize : view.size; + return { + collapsed, + size: size && size > 0 ? size : void 0, + isHidden: false, + order: this.splitView.getViews().indexOf(view) + }; + } +} + +export class PersistentViewsViewlet extends ViewsViewlet { + + constructor( + id: string, + location: ViewLocation, + private viewletStateStorageId: string, + showHeaderInTitleWhenSingleView: boolean, + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService storageService: IStorageService, + @IInstantiationService instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IContextKeyService contextKeyService: IContextKeyService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService + ) { + super(id, location, showHeaderInTitleWhenSingleView, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); + this.loadViewsStates(); + } + + shutdown(): void { + this.saveViewsStates(); + super.shutdown(); + } + + private saveViewsStates(): void { + const viewsStates = {}; + const registeredViewDescriptors = this.getViewDescriptorsFromRegistry(); + this.viewsStates.forEach((viewState, id) => { + const view = this.getView(id); + if (view) { + viewState = this.createViewState(view); + viewsStates[id] = { size: viewState.size, collapsed: viewState.collapsed, isHidden: viewState.isHidden, order: viewState.order }; + } else { + const viewDescriptor = registeredViewDescriptors.filter(v => v.id === id)[0]; + if (viewDescriptor) { + viewsStates[id] = viewState; + } + } + }); + + this.storageService.store(this.viewletStateStorageId, JSON.stringify(viewsStates), this.contextService.hasWorkspace() ? StorageScope.WORKSPACE : StorageScope.GLOBAL); + } + + private loadViewsStates(): void { + const viewsStates = JSON.parse(this.storageService.get(this.viewletStateStorageId, this.contextService.hasWorkspace() ? StorageScope.WORKSPACE : StorageScope.GLOBAL, '{}')); + Object.keys(viewsStates).forEach(id => this.viewsStates.set(id, viewsStates[id])); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/views/browser/viewsExtensionPoint.ts b/src/vs/workbench/parts/views/browser/viewsExtensionPoint.ts new file mode 100644 index 0000000000..ba42921ad5 --- /dev/null +++ b/src/vs/workbench/parts/views/browser/viewsExtensionPoint.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { localize } from 'vs/nls'; +import { forEach } from 'vs/base/common/collections'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; +import { ViewLocation, ViewsRegistry, IViewDescriptor } from 'vs/workbench/parts/views/browser/viewsRegistry'; +import { TreeView } from 'vs/workbench/parts/views/browser/treeView'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; + +namespace schema { + + // --views contribution point + + export interface IUserFriendlyViewDescriptor { + id: string; + name: string; + when?: string; + } + + export function isValidViewDescriptors(viewDescriptors: IUserFriendlyViewDescriptor[], collector: ExtensionMessageCollector): boolean { + if (!Array.isArray(viewDescriptors)) { + collector.error(localize('requirearray', "views must be an array")); + return false; + } + + for (let descriptor of viewDescriptors) { + if (typeof descriptor.id !== 'string') { + collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'id')); + return false; + } + if (typeof descriptor.name !== 'string') { + collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'name')); + return false; + } + if (descriptor.when && typeof descriptor.when !== 'string') { + collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when')); + return false; + } + } + + return true; + } + + const viewDescriptor: IJSONSchema = { + type: 'object', + properties: { + id: { + description: localize('vscode.extension.contributes.view.id', 'Identifier of the view. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.'), + type: 'string' + }, + name: { + description: localize('vscode.extension.contributes.view.name', 'The human-readable name of the view. Will be shown'), + type: 'string' + }, + when: { + description: localize('vscode.extension.contributes.view.when', 'Condition which must be true to show this view'), + type: 'string' + }, + } + }; + + export const viewsContribution: IJSONSchema = { + description: localize('vscode.extension.contributes.views', "Contributes views to the editor"), + type: 'object', + properties: { + 'explorer': { + description: localize('views.explorer', "Explorer View"), + type: 'array', + items: viewDescriptor + }, + 'debug': { + description: localize('views.debug', "Debug View"), + type: 'array', + items: viewDescriptor + } + } + }; +} + +ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyViewDescriptor[] }>('views', [], schema.viewsContribution).setHandler(extensions => { + for (let extension of extensions) { + const { value, collector } = extension; + + forEach(value, entry => { + if (!schema.isValidViewDescriptors(entry.value, collector)) { + return; + } + + const location = ViewLocation.getContributedViewLocation(entry.key); + if (!location) { + collector.warn(localize('locationId.invalid', "`{0}` is not a valid view location", entry.key)); + return; + } + + const viewDescriptors = entry.value.map(item => ({ + id: item.id, + name: item.name, + ctor: TreeView, + location, + when: ContextKeyExpr.deserialize(item.when), + canToggleVisibility: true + })); + ViewsRegistry.registerViews(viewDescriptors); + }); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/views/browser/viewsRegistry.ts b/src/vs/workbench/parts/views/browser/viewsRegistry.ts new file mode 100644 index 0000000000..798c00500a --- /dev/null +++ b/src/vs/workbench/parts/views/browser/viewsRegistry.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Event, { Emitter } from 'vs/base/common/event'; +import { IViewConstructorSignature } from 'vs/workbench/parts/views/browser/views'; +import { ITreeViewDataProvider } from 'vs/workbench/parts/views/common/views'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; + +export class ViewLocation { + + static readonly Explorer = new ViewLocation('explorer'); + static readonly Debug = new ViewLocation('debug'); + static readonly Extensions = new ViewLocation('extensions'); + static readonly SCM = new ViewLocation('scm'); + + constructor(private _id: string) { + } + + get id(): string { + return this._id; + } + + static getContributedViewLocation(value: string): ViewLocation { + switch (value) { + case ViewLocation.Explorer.id: return ViewLocation.Explorer; + case ViewLocation.Debug.id: return ViewLocation.Debug; + } + return void 0; + } +} + +export interface IViewDescriptor { + + readonly id: string; + + readonly name: string; + + readonly location: ViewLocation; + + readonly ctor: IViewConstructorSignature; + + readonly when?: ContextKeyExpr; + + readonly order?: number; + + readonly size?: number; + + readonly canToggleVisibility?: boolean; +} + +export interface IViewsRegistry { + + readonly onViewsRegistered: Event; + + readonly onViewsDeregistered: Event; + + readonly onTreeViewDataProviderRegistered: Event; + + registerViews(views: IViewDescriptor[]): void; + + deregisterViews(ids: string[], location: ViewLocation): void; + + registerTreeViewDataProvider(id: string, factory: ITreeViewDataProvider): void; + + deregisterTreeViewDataProviders(): void; + + getViews(loc: ViewLocation): IViewDescriptor[]; + + getTreeViewDataProvider(id: string): ITreeViewDataProvider; + +} + +export const ViewsRegistry: IViewsRegistry = new class { + + private _onViewsRegistered: Emitter = new Emitter(); + readonly onViewsRegistered: Event = this._onViewsRegistered.event; + + private _onViewsDeregistered: Emitter = new Emitter(); + readonly onViewsDeregistered: Event = this._onViewsDeregistered.event; + + private _onTreeViewDataProviderRegistered: Emitter = new Emitter(); + readonly onTreeViewDataProviderRegistered: Event = this._onTreeViewDataProviderRegistered.event; + + private _views: Map = new Map(); + private _treeViewDataPoviders: Map = new Map(); + + registerViews(viewDescriptors: IViewDescriptor[]): void { + if (viewDescriptors.length) { + for (const viewDescriptor of viewDescriptors) { + let views = this._views.get(viewDescriptor.location); + if (!views) { + views = []; + this._views.set(viewDescriptor.location, views); + } + views.push(viewDescriptor); + } + this._onViewsRegistered.fire(viewDescriptors); + } + } + + deregisterViews(ids: string[], location: ViewLocation): void { + const views = this._views.get(location); + + if (!views) { + return; + } + + const viewsToDeregister = views.filter(view => ids.indexOf(view.id) !== -1); + + if (viewsToDeregister.length) { + this._views.set(location, views.filter(view => ids.indexOf(view.id) === -1)); + } + + this._onViewsDeregistered.fire(viewsToDeregister); + } + + registerTreeViewDataProvider(id: string, factory: ITreeViewDataProvider) { + if (!this.isViewRegistered(id)) { + // TODO: throw error + } + this._treeViewDataPoviders.set(id, factory); + this._onTreeViewDataProviderRegistered.fire(id); + } + + deregisterTreeViewDataProviders(): void { + this._treeViewDataPoviders.clear(); + } + + getViews(loc: ViewLocation): IViewDescriptor[] { + return this._views.get(loc) || []; + } + + getTreeViewDataProvider(id: string): ITreeViewDataProvider { + return this._treeViewDataPoviders.get(id); + } + + private isViewRegistered(id: string): boolean { + let registered = false; + this._views.forEach(views => registered = registered || views.some(view => view.id === id)); + return registered; + } +}; \ No newline at end of file diff --git a/src/vs/workbench/parts/views/common/views.ts b/src/vs/workbench/parts/views/common/views.ts new file mode 100644 index 0000000000..dfdd0b2e0b --- /dev/null +++ b/src/vs/workbench/parts/views/common/views.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import Event from 'vs/base/common/event'; +import { Command } from 'vs/editor/common/modes'; + +export type TreeViewItemHandleArg = { + $treeViewId: string, + $treeItemHandle: number +}; + +export enum TreeItemCollapsibleState { + None = 0, + Collapsed = 1, + Expanded = 2 +} + +export interface ITreeItem { + + handle: number; + + label: string; + + icon?: string; + + iconDark?: string; + + contextValue?: string; + + command?: Command; + + children?: ITreeItem[]; + + collapsibleState?: TreeItemCollapsibleState; +} + +export interface ITreeViewDataProvider { + + onDidChange: Event; + + onDispose: Event; + + getElements(): TPromise; + + getChildren(element: ITreeItem): TPromise; + +} \ No newline at end of file diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.css b/src/vs/workbench/parts/watermark/electron-browser/watermark.css new file mode 100644 index 0000000000..1eac3dcf0e --- /dev/null +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.css @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .part.editor.empty.has-watermark { + background-position-y: calc(50% - 100px); +} + +.monaco-workbench > .part.editor > .content > .watermark { + display: none; /* only visible when no editors are opened */ +} + +.monaco-workbench > .part.editor.empty.has-watermark > .content > .watermark { + display: block; + position: absolute; + width: 100%; + top: calc(50% + 55px); + text-align: center; + white-space: nowrap; + overflow: hidden; +} + +.monaco-workbench > .part.editor.empty > .content > .watermark > .watermark-box { + display: inline-table; + border-collapse: separate; + border-spacing: 13px 17px; +} + +.monaco-workbench > .part.editor.empty.max-height-478px > .content > .watermark { + display: none; +} +.monaco-workbench .part.editor.empty.max-height-478px { + background-position-y: 50%; +} + +.monaco-workbench > .part.editor.empty > .content > .watermark dl { + display: table-row; + opacity: .8; + cursor: default; +} + +.monaco-workbench > .part.editor.empty > .content > .watermark dt { + text-align: right; + letter-spacing: 0.04em +} + +.monaco-workbench > .part.editor.empty > .content > .watermark dd { + text-align: left; +} + +.monaco-workbench > .part.editor.empty > .content > .watermark dd > .shortcuts { + padding-left: 0.5em; + padding-right: 0.5em; +} + +.monaco-workbench.mac > .part.editor.empty > .content > .watermark dd > .shortcuts { + letter-spacing: 0.15em; + font-family: "Lucida Grande", sans-serif; +} + +.monaco-workbench > .part.editor.empty > .content > .watermark dd > .unbound { + padding-left: 0.5em; + padding-right: 0.5em; +} + +.monaco-workbench.mac > .part.editor.empty > .content > .watermark dd > .unbound { + font-family: "Lucida Grande", sans-serif; +} + +.monaco-workbench > .part.editor.empty > .content > .watermark dt, +.monaco-workbench > .part.editor.empty > .content > .watermark dd { + display: table-cell; +} + +.monaco-workbench > .part.editor.empty > .content > .watermark dt, +.monaco-workbench > .part.editor.empty > .content > .watermark dl { + color: rgba(0,0,0,.68); +} + +.vs-dark .monaco-workbench > .part.editor.empty > .content > .watermark dt, +.vs-dark .monaco-workbench > .part.editor.empty > .content > .watermark dl { + color: rgba(255,255,255,.6); +} + +.hc-black .monaco-workbench > .part.editor.empty > .content > .watermark dt { + color: #FFF; +} +.hc-black .monaco-workbench > .part.editor.empty > .content > .watermark dl { + color: #FFF; + opacity: 1; +} diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts new file mode 100644 index 0000000000..c25eaf9e36 --- /dev/null +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts @@ -0,0 +1,228 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./watermark'; +import { $, Builder } from 'vs/base/browser/builder'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { assign } from 'vs/base/common/objects'; +import { isMacintosh } from 'vs/base/common/platform'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import * as nls from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { OpenRecentAction } from 'vs/workbench/electron-browser/actions'; +import { GlobalNewUntitledFileAction, OpenFileAction } from 'vs/workbench/parts/files/browser/fileActions'; +import { OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; +import { Parts, IPartService } from 'vs/workbench/services/part/common/partService'; +import { StartAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { FindInFilesActionId } from 'vs/workbench/parts/search/common/constants'; +import { ToggleTerminalAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; +import { escape } from 'vs/base/common/strings'; +import { QUICKOPEN_ACTION_ID } from 'vs/workbench/browser/parts/quickopen/quickopen'; + +// {{SQL CARBON EDIT}} +import { OpenConnectionsViewletAction } from 'sql/parts/registeredServer/common/registeredServer.contribution'; + + +interface WatermarkEntry { + text: string; + ids: string[]; + mac?: boolean; +} + +// {{SQL CARBON EDIT}} +const showServers: WatermarkEntry = { + text: 'Show Servers', + ids: [OpenConnectionsViewletAction.ID] +}; + +const newSqlFile: WatermarkEntry = { + text: 'New SQL File', + ids: [GlobalNewUntitledFileAction.ID] +}; + +const showCommands: WatermarkEntry = { + text: nls.localize('watermark.showCommands', "Show All Commands"), + ids: [ShowAllCommandsAction.ID] +}; +const quickOpen: WatermarkEntry = { + text: nls.localize('watermark.quickOpen', "Go to File"), + ids: [QUICKOPEN_ACTION_ID] +}; +const openFileNonMacOnly: WatermarkEntry = { + text: nls.localize('watermark.openFile', "Open File"), + ids: [OpenFileAction.ID], + mac: false +}; +const openFolderNonMacOnly: WatermarkEntry = { + text: nls.localize('watermark.openFolder', "Open Folder"), + ids: [OpenFolderAction.ID], + mac: false +}; +const openFileOrFolderMacOnly: WatermarkEntry = { + text: nls.localize('watermark.openFileFolder', "Open File or Folder"), + ids: [OpenFileFolderAction.ID], + mac: true +}; +const openRecent: WatermarkEntry = { + text: nls.localize('watermark.openRecent', "Open Recent"), + ids: [OpenRecentAction.ID] +}; +const newUntitledFile: WatermarkEntry = { + text: nls.localize('watermark.newUntitledFile', "New Untitled File"), + ids: [GlobalNewUntitledFileAction.ID] +}; +const newUntitledFileMacOnly: WatermarkEntry = assign({ mac: true }, newUntitledFile); +const toggleTerminal: WatermarkEntry = { + text: nls.localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), + ids: [ToggleTerminalAction.ID] +}; + +const findInFiles: WatermarkEntry = { + text: nls.localize('watermark.findInFiles', "Find in Files"), + ids: [FindInFilesActionId] +}; +const startDebugging: WatermarkEntry = { + text: nls.localize('watermark.startDebugging', "Start Debugging"), + ids: [StartAction.ID] +}; + +// {{SQL CARBON EDIT}} +const newUserEntries = [ + showServers, + newSqlFile, + findInFiles +]; + +const noFolderEntries = [ + showServers, + newSqlFile, + findInFiles +]; + +const folderEntries = [ + showServers, + newSqlFile, + findInFiles +]; + +const UNBOUND = nls.localize('watermark.unboundCommand', "unbound"); + +export class WatermarkContribution implements IWorkbenchContribution { + + private toDispose: IDisposable[] = []; + private watermark: Builder; + private enabled: boolean; + + constructor( + @ILifecycleService lifecycleService: ILifecycleService, + @IPartService private partService: IPartService, + @IKeybindingService private keybindingService: IKeybindingService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ITelemetryService private telemetryService: ITelemetryService, + @IConfigurationService private configurationService: IConfigurationService + ) { + lifecycleService.onShutdown(this.dispose, this); + this.partService.joinCreation().then(() => { + this.enabled = this.configurationService.lookup('workbench.tips.enabled').value; + if (this.enabled) { + this.create(); + } + }); + this.configurationService.onDidUpdateConfiguration(e => { + const enabled = this.configurationService.lookup('workbench.tips.enabled').value; + if (enabled !== this.enabled) { + this.enabled = enabled; + if (this.enabled) { + this.create(); + } else { + this.destroy(); + } + } + }); + } + + public getId() { + return 'vs.watermark'; + } + + private create(): void { + const container = this.partService.getContainer(Parts.EDITOR_PART); + container.classList.add('has-watermark'); + + this.watermark = $() + .div({ 'class': 'watermark' }); + const box = $(this.watermark) + .div({ 'class': 'watermark-box' }); + const folder = this.contextService.hasWorkspace(); + const selected = folder ? folderEntries : noFolderEntries + .filter(entry => !('mac' in entry) || entry.mac === isMacintosh); + const update = () => { + const builder = $(box); + builder.clearChildren(); + selected.map(entry => { + builder.element('dl', {}, dl => { + dl.element('dt', {}, dt => dt.text(entry.text)); + dl.element('dd', {}, dd => dd.innerHtml( + entry.ids + .map(id => { + let k = this.keybindingService.lookupKeybinding(id); + if (k) { + return `${escape(k.getLabel())}`; + } + return `${escape(UNBOUND)}`; + }) + .join(' / ') + )); + }); + }); + }; + const layout = () => { + const { height } = container.getBoundingClientRect(); + container.classList[height <= 478 ? 'add' : 'remove']('max-height-478px'); + }; + update(); + this.watermark.build(container.firstElementChild as HTMLElement, 0); + layout(); + this.toDispose.push(this.keybindingService.onDidUpdateKeybindings(update)); + this.toDispose.push(this.partService.onEditorLayout(layout)); + } + + private destroy(): void { + if (this.watermark) { + this.watermark.destroy(); + this.partService.getContainer(Parts.EDITOR_PART).classList.remove('has-watermark'); + this.dispose(); + } + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } +} + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(WatermarkContribution); + +Registry.as(ConfigurationExtensions.Configuration) + .registerConfiguration({ + 'id': 'workbench', + 'order': 7, + 'title': nls.localize('workbenchConfigurationTitle', "Workbench"), + 'properties': { + 'workbench.tips.enabled': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('tips.enabled', "When enabled, will show the watermark tips when no editor is open.") + }, + } + }); \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/code-icon.svg b/src/vs/workbench/parts/welcome/code-icon.svg new file mode 100644 index 0000000000..cc61f81ea5 --- /dev/null +++ b/src/vs/workbench/parts/welcome/code-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution.ts b/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution.ts new file mode 100644 index 0000000000..7df9792440 --- /dev/null +++ b/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Registry } from 'vs/platform/registry/common/platform'; +import { GettingStarted } from './gettingStarted'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; + +Registry + .as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(GettingStarted); \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.ts b/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.ts new file mode 100644 index 0000000000..7e34af98fe --- /dev/null +++ b/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import * as platform from 'vs/base/common/platform'; +import product from 'vs/platform/node/product'; + +abstract class AbstractGettingStarted implements IWorkbenchContribution { + protected static hideWelcomeSettingskey = 'workbench.hide.welcome'; + + protected welcomePageURL: string; + protected appName: string; + + constructor( + @IStorageService private storageService: IStorageService, + @IEnvironmentService environmentService: IEnvironmentService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + this.appName = product.nameLong; + + if (product.welcomePage && !environmentService.isExtensionDevelopment /* do not open a browser when we run an extension */) { + this.welcomePageURL = product.welcomePage; + this.handleWelcome(); + } + } + + protected handleWelcome(): void { + let firstStartup = !this.storageService.get(AbstractGettingStarted.hideWelcomeSettingskey); + + if (firstStartup && this.welcomePageURL) { + this.telemetryService.getTelemetryInfo().then(info => { + let url = this.getUrl(info); + this.openExternal(url); + this.storageService.store(AbstractGettingStarted.hideWelcomeSettingskey, true); + }); + } + } + + private getUrl(telemetryInfo: ITelemetryInfo): string { + return `${this.welcomePageURL}&&from=${this.appName}&&id=${telemetryInfo.machineId}`; + } + + protected openExternal(url: string) { + throw new Error('implement me'); + } + + getId(): string { + return 'vs.gettingstarted'; + } +} + +export class GettingStarted implements IWorkbenchContribution { + + private static hideWelcomeSettingskey = 'workbench.hide.welcome'; + + private welcomePageURL: string; + private appName: string; + + constructor( + @IStorageService private storageService: IStorageService, + @IEnvironmentService environmentService: IEnvironmentService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + this.appName = product.nameLong; + + /* do not open a browser when we run an extension or --skip-getting-started is provided */ + if (product.welcomePage && !environmentService.isExtensionDevelopment && !environmentService.skipGettingStarted) { + this.welcomePageURL = product.welcomePage; + this.handleWelcome(); + } + } + + getId(): string { + return 'vs.gettingstarted'; + } + + private getUrl(telemetryInfo: ITelemetryInfo): string { + return `${this.welcomePageURL}&&from=${this.appName}&&id=${telemetryInfo.machineId}`; + } + + private openExternal(url: string) { + // Don't open the welcome page as the root user on Linux, this is due to a bug with xdg-open + // which recommends against running itself as root. + if (platform.isLinux && platform.isRootUser) { + return; + } + window.open(url); + } + + private handleWelcome(): void { + //make sure the user is online, otherwise refer to the next run to show the welcome page + if (!navigator.onLine) { + return; + } + + let firstStartup = !this.storageService.get(GettingStarted.hideWelcomeSettingskey); + + if (firstStartup && this.welcomePageURL) { + this.telemetryService.getTelemetryInfo().then(info => { + let url = this.getUrl(info); + this.openExternal(url); + this.storageService.store(GettingStarted.hideWelcomeSettingskey, true); + }); + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/gettingStarted/test/common/gettingStarted.test.ts b/src/vs/workbench/parts/welcome/gettingStarted/test/common/gettingStarted.test.ts new file mode 100644 index 0000000000..3b22caed2d --- /dev/null +++ b/src/vs/workbench/parts/welcome/gettingStarted/test/common/gettingStarted.test.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +// import * as assert from 'assert'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; + +suite('Workbench - GettingStarted', () => { + let instantiation: TestInstantiationService = null; + let welcomePageEnvConfig: string = null; + let hideWelcomeSettingsValue: string = null; + // let machineId: string = null; + let appName: string = null; + + suiteSetup(() => { + instantiation = new TestInstantiationService(); + instantiation.stub(IWorkspaceContextService, { + getConfiguration: () => { + return { + env: { + welcomePage: welcomePageEnvConfig, + appName: appName + } + }; + } + }); + instantiation.stub(IStorageService, { + get: () => hideWelcomeSettingsValue, + store: (value) => hideWelcomeSettingsValue = value + }); + }); + + suiteTeardown(() => { + instantiation = null; + }); + + setup(() => { + welcomePageEnvConfig = null; + hideWelcomeSettingsValue = null; + appName = null; + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette-dark.svg b/src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette-dark.svg new file mode 100644 index 0000000000..fe832cddce --- /dev/null +++ b/src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette-dark.svg @@ -0,0 +1 @@ +Asset 8 \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette.svg b/src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette.svg new file mode 100644 index 0000000000..56e91eb841 --- /dev/null +++ b/src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette.svg @@ -0,0 +1 @@ +Asset 9 \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.css b/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.css new file mode 100644 index 0000000000..1300605c96 --- /dev/null +++ b/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.css @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench > .welcomeOverlay { + position: absolute; + top: 0px; + left: 0px; + height: 100%; + width: 100%; + z-index: 9999; + font-size: 10px; + transition: font-size .25s; +} + +#workbench\.parts\.editor { + transition: filter .25s, opacity .2s; +} + +.monaco-workbench.blur-background #workbench\.parts\.panel, +.monaco-workbench.blur-background #workbench\.parts\.sidebar, +.monaco-workbench.blur-background #workbench\.parts\.editor { + filter: blur(2px); + opacity: .4; +} + +.hc-black .monaco-workbench.blur-background #workbench\.parts\.panel, +.hc-black .monaco-workbench.blur-background #workbench\.parts\.sidebar, +.hc-black .monaco-workbench.blur-background #workbench\.parts\.editor { + opacity: .2; +} + +.monaco-workbench > .welcomeOverlay > .key { + font-size: 1.6em; +} + +.monaco-workbench > .welcomeOverlay > .key > .label { + padding: 0 1ex; +} + +.monaco-workbench > .welcomeOverlay > .key > .shortcut { + letter-spacing: 0.15em; + font-size: 0.8125em; + font-family: "Lucida Grande", sans-serif; +} + +.monaco-workbench > .welcomeOverlay > .key.explorer { + position: absolute; + top: 13px; + left: 60px; +} + +.monaco-workbench > .welcomeOverlay > .key.search { + position: absolute; + top: 63px; + left: 60px; +} + +.monaco-workbench > .welcomeOverlay > .key.git { + position: absolute; + top: 113px; + left: 60px; +} + +.monaco-workbench > .welcomeOverlay > .key.debug { + position: absolute; + top: 163px; + left: 60px; +} + +.monaco-workbench > .welcomeOverlay > .key.extensions { + position: absolute; + top: 213px; + left: 60px; +} + +.monaco-workbench > .welcomeOverlay > .key.watermark { + position: absolute; + top: calc(50% + 30px); + left: calc(50% + 30px); +} + +.monaco-workbench > .welcomeOverlay > .key.watermark > .label { + vertical-align: super; +} + +@media (max-height: 500px) { + .monaco-workbench > .welcomeOverlay > .key.watermark { + display: none; + } +} + +.monaco-workbench > .welcomeOverlay > .key.problems { + position: absolute; + bottom: 25px; + left: 45px; +} + +.monaco-workbench > .welcomeOverlay > .key.problems > .label, +.monaco-workbench > .welcomeOverlay > .key.problems > .shortcut { + vertical-align: super; +} + +.monaco-workbench > .welcomeOverlay > .key.openfile { + position: absolute; + bottom: 25px; + right: 45px; +} + +.monaco-workbench > .welcomeOverlay > .key.openfile > .label { + vertical-align: super; +} + +.monaco-workbench > .welcomeOverlay > .key.commandPalette { + position: absolute; + top: 210px; + left: calc(50% - 52px); + transition: top .25s, left .25s; +} + +.monaco-workbench > .welcomeOverlay > .key.commandPalette > .label { + vertical-align: sub; +} + +.monaco-workbench > .welcomeOverlay > .key.commandPalette > .shortcut { + padding-left: 0.5em; + vertical-align: sub; +} + +.monaco-workbench > .welcomeOverlay > .commandPalettePlaceholder { + position: absolute; + top: 2px; + left: calc(50% - 193px); + width: 386px; + height: 197px; + background-image: url('media/commandpalette.svg'); + background-repeat: no-repeat; + background-size: 100%; +} + +.vs-dark .monaco-workbench > .welcomeOverlay > .commandPalettePlaceholder, +.hc-black .monaco-workbench > .welcomeOverlay > .commandPalettePlaceholder { + background-image: url('media/commandpalette-dark.svg'); +} +@media screen and (max-width: 880px) { + .monaco-workbench > .welcomeOverlay { + font-size: 8px; + } + .monaco-workbench > .welcomeOverlay > .commandPalettePlaceholder { + display: none; + } + .monaco-workbench > .welcomeOverlay > .key.commandPalette { + top: 10px; + left: 50%; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts new file mode 100644 index 0000000000..bf16fb7fdd --- /dev/null +++ b/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts @@ -0,0 +1,252 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./welcomeOverlay'; +import { $, Builder } from 'vs/base/browser/builder'; +import * as dom from 'vs/base/browser/dom'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Parts, IPartService } from 'vs/workbench/services/part/common/partService'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { localize } from 'vs/nls'; +import { Action } from 'vs/base/common/actions'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { textPreformatForeground, foreground } from 'vs/platform/theme/common/colorRegistry'; +import { Color } from 'vs/base/common/color'; + +interface Key { + id: string; + arrow: string; + label: string; + command?: string; + arrowLast?: boolean; + withEditor?: boolean; +} + +const keys: Key[] = [ + { + id: 'explorer', + arrow: '←', + label: localize('welcomeOverlay.explorer', "File explorer"), + command: 'workbench.view.explorer' + }, + { + id: 'search', + arrow: '←', + label: localize('welcomeOverlay.search', "Search across files"), + command: 'workbench.view.search' + }, + { + id: 'git', + arrow: '←', + label: localize('welcomeOverlay.git', "Source code management"), + command: 'workbench.view.scm' + }, + { + id: 'debug', + arrow: '←', + label: localize('welcomeOverlay.debug', "Launch and debug"), + command: 'workbench.view.debug' + }, + { + id: 'extensions', + arrow: '←', + label: localize('welcomeOverlay.extensions', "Manage extensions"), + command: 'workbench.view.extensions' + }, + // { + // id: 'watermark', + // arrow: '⤹', + // label: localize('welcomeOverlay.watermark', "Command Hints"), + // withEditor: false + // }, + { + id: 'problems', + arrow: '⤹', + label: localize('welcomeOverlay.problems', "View errors and warnings"), + command: 'workbench.actions.view.problems' + }, + // { + // id: 'openfile', + // arrow: '⤸', + // label: localize('welcomeOverlay.openfile', "File Properties"), + // arrowLast: true, + // withEditor: true + // }, + { + id: 'commandPalette', + arrow: '↖', + label: localize('welcomeOverlay.commandPalette', "Find and run all commands"), + command: ShowAllCommandsAction.ID + }, +]; + +const OVERLAY_VISIBLE = new RawContextKey('interfaceOverviewVisible', false); + +let welcomeOverlay: WelcomeOverlay; + +export class WelcomeOverlayAction extends Action { + + public static ID = 'workbench.action.showInterfaceOverview'; + public static LABEL = localize('welcomeOverlay', "User Interface Overview"); + + constructor( + id: string, + label: string, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(id, label); + } + + public run(): TPromise { + if (!welcomeOverlay) { + welcomeOverlay = this.instantiationService.createInstance(WelcomeOverlay); + } + welcomeOverlay.show(); + return null; + } +} + +export class HideWelcomeOverlayAction extends Action { + + public static ID = 'workbench.action.hideInterfaceOverview'; + public static LABEL = localize('hideWelcomeOverlay', "Hide Interface Overview"); + + constructor( + id: string, + label: string + ) { + super(id, label); + } + + public run(): TPromise { + if (welcomeOverlay) { + welcomeOverlay.hide(); + } + return null; + } +} + +class WelcomeOverlay { + + private _toDispose: IDisposable[] = []; + private _overlayVisible: IContextKey; + private _overlay: Builder; + + constructor( + @IPartService private partService: IPartService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @ICommandService private commandService: ICommandService, + @IContextKeyService private _contextKeyService: IContextKeyService, + @IKeybindingService private keybindingService: IKeybindingService + ) { + this._overlayVisible = OVERLAY_VISIBLE.bindTo(this._contextKeyService); + this.create(); + } + + private create(): void { + const container = this.partService.getContainer(Parts.EDITOR_PART); + + const offset = this.partService.getTitleBarOffset(); + this._overlay = $(container.parentElement) + .div({ 'class': 'welcomeOverlay' }) + .style({ top: `${offset}px` }) + .style({ height: `calc(100% - ${offset}px)` }) + .display('none'); + + this._overlay.on('click', () => this.hide(), this._toDispose); + this.commandService.onWillExecuteCommand(() => this.hide()); + + $(this._overlay).div({ 'class': 'commandPalettePlaceholder' }); + + const editorOpen = !!this.editorService.getVisibleEditors().length; + keys.filter(key => !('withEditor' in key) || key.withEditor === editorOpen) + .forEach(({ id, arrow, label, command, arrowLast }) => { + const div = $(this._overlay).div({ 'class': ['key', id] }); + if (!arrowLast) { + $(div).span({ 'class': 'arrow' }).innerHtml(arrow); + } + $(div).span({ 'class': 'label' }).text(label); + if (command) { + const shortcut = this.keybindingService.lookupKeybinding(command); + if (shortcut) { + $(div).span({ 'class': 'shortcut' }).text(shortcut.getLabel()); + } + } + if (arrowLast) { + $(div).span({ 'class': 'arrow' }).innerHtml(arrow); + } + }); + } + + public show() { + if (this._overlay.style('display') !== 'block') { + this._overlay.display('block'); + const workbench = document.querySelector('.monaco-workbench') as HTMLElement; + dom.addClass(workbench, 'blur-background'); + this._overlayVisible.set(true); + this.updateProblemsKey(); + } + } + + private updateProblemsKey() { + const problems = document.querySelector('.task-statusbar-item'); + const key = this._overlay.getHTMLElement().querySelector('.key.problems') as HTMLElement; + if (problems instanceof HTMLElement) { + const target = problems.getBoundingClientRect(); + const bounds = this._overlay.getHTMLElement().getBoundingClientRect(); + const bottom = bounds.bottom - target.top + 3; + const left = (target.left + target.right) / 2 - bounds.left; + key.style.bottom = bottom + 'px'; + key.style.left = left + 'px'; + } else { + key.style.bottom = null; + key.style.left = null; + } + } + + public hide() { + if (this._overlay.style('display') !== 'none') { + this._overlay.display('none'); + const workbench = document.querySelector('.monaco-workbench') as HTMLElement; + dom.removeClass(workbench, 'blur-background'); + this._overlayVisible.reset(); + } + } + + dispose() { + this._toDispose = dispose(this._toDispose); + } +} + +// {SQL CARBON EDIT} +// remove Interface Overview command registrations + +// theming + +registerThemingParticipant((theme, collector) => { + const key = theme.getColor(foreground); + if (key) { + collector.addRule(`.monaco-workbench > .welcomeOverlay > .key { color: ${key}; }`); + } + const backgroundColor = Color.fromHex(theme.type === 'light' ? '#FFFFFF85' : '#00000085'); + if (backgroundColor) { + collector.addRule(`.monaco-workbench > .welcomeOverlay { background: ${backgroundColor}; }`); + } + const shortcut = theme.getColor(textPreformatForeground); + if (shortcut) { + collector.addRule(`.monaco-workbench > .welcomeOverlay > .key > .shortcut { color: ${shortcut}; }`); + } +}); diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts new file mode 100644 index 0000000000..bc669c6132 --- /dev/null +++ b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { escape } from 'vs/base/common/strings'; +import { localize } from 'vs/nls'; + +export function used() { +} + +export default () => ` + +`; diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts new file mode 100644 index 0000000000..47bf59039f --- /dev/null +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { localize } from 'vs/nls'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { WelcomePageContribution, WelcomePageAction, WelcomeInputFactory } from 'vs/workbench/parts/welcome/page/electron-browser/welcomePage'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; + +Registry.as(ConfigurationExtensions.Configuration) + .registerConfiguration({ + 'id': 'workbench', + 'order': 7, + 'title': localize('workbenchConfigurationTitle', "Workbench"), + 'properties': { + 'workbench.startupEditor': { + 'type': 'string', + 'enum': ['none', 'welcomePage', 'newUntitledFile'], + 'enumDescriptions': [ + localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."), + localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the Welcome page (default)."), + localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file."), + ], + 'default': 'welcomePage', + 'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none is restored from the previous session. Select 'none' to start without an editor, 'welcomePage' to open the Welcome page (default), 'newUntitledFile' to open a new untitled file (only opening an empty workspace).") + }, + } + }); + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(WelcomePageContribution); + +Registry.as(ActionExtensions.WorkbenchActions) + .registerWorkbenchAction(new SyncActionDescriptor(WelcomePageAction, WelcomePageAction.ID, WelcomePageAction.LABEL), 'Help: Welcome', localize('help', "Help")); + +Registry.as(EditorExtensions.Editors).registerEditorInputFactory(WelcomeInputFactory.ID, WelcomeInputFactory); diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css new file mode 100644 index 0000000000..5b0132f5ac --- /dev/null +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css @@ -0,0 +1,226 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench > .part.editor > .content .welcomePageContainer { + align-items: center; + display: flex; + justify-content: center; + min-width: 100%; + min-height: 100%; +} + +.monaco-workbench > .part.editor > .content .welcomePage { + width: 90%; + max-width: 1200px; + font-size: 10px; +} + +.monaco-workbench > .part.editor > .content .welcomePage .row { + display: flex; + flex-flow: row; +} + +.monaco-workbench > .part.editor > .content .welcomePage .row * { + min-width: 0; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.monaco-workbench > .part.editor > .content .welcomePage p { + font-size: 1.3em; +} + +.monaco-workbench > .part.editor > .content .welcomePage .keyboard { + font-family: "Lucida Grande", sans-serif;/* Keyboard shortcuts */ +} + +.monaco-workbench > .part.editor > .content .welcomePage a { + text-decoration: none; +} + +.monaco-workbench > .part.editor > .content .welcomePage a:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +.monaco-workbench > .part.editor > .content .welcomePage h1 { + padding: 0; + margin: 0; + border: none; + font-weight: normal; + font-size: 3.6em; + white-space: nowrap; +} + +.monaco-workbench > .part.editor > .content .welcomePage .title { + margin-top: 1em; + margin-bottom: 1em; + flex: 1 100%; +} + +.monaco-workbench > .part.editor > .content .welcomePage .subtitle { + margin-top: .8em; + font-size: 2.6em; + display: block; +} + +.hc-black .monaco-workbench > .part.editor > .content .welcomePage .subtitle { + font-weight: 200; +} + +.monaco-workbench > .part.editor > .content .welcomePage .splash, +.monaco-workbench > .part.editor > .content .welcomePage .commands { + flex: 1 1 0; +} + +.monaco-workbench > .part.editor > .content .welcomePage h2 { + font-weight: 200; + margin-top: 17px; + margin-bottom: 5px; + font-size: 1.9em; + line-height: initial; +} + +.monaco-workbench > .part.editor > .content .welcomePage .splash .section { + margin-bottom: 5em; +} + +.monaco-workbench > .part.editor > .content .welcomePage .splash ul { + margin: 0; + font-size: 1.3em; + list-style: none; + padding: 0; +} + +.monaco-workbench > .part.editor > .content .welcomePage.emptyRecent .splash .recent .list { + display: none; +} +.monaco-workbench > .part.editor > .content .welcomePage .splash .recent .none { + display: none; +} +.monaco-workbench > .part.editor > .content .welcomePage.emptyRecent .splash .recent .none { + display: initial; +} + +.monaco-workbench > .part.editor > .content .welcomePage .splash .recent li.moreRecent { + margin-top: 5px; +} + +.monaco-workbench > .part.editor > .content .welcomePage .splash .recent li { + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.monaco-workbench > .part.editor > .content .welcomePage .splash .recent .path { + padding-left: 1em; +} + +.monaco-workbench > .part.editor > .content .welcomePage .splash .title, +.monaco-workbench > .part.editor > .content .welcomePage .splash .showOnStartup { + white-space: nowrap; +} + +.monaco-workbench > .part.editor > .content .welcomePage .splash .showOnStartup input[type=checkbox] { + vertical-align: bottom; +} + +.monaco-workbench > .part.editor > .content .welcomePage .commands ul { + list-style: none; + padding: 0; +} +.monaco-workbench > .part.editor > .content .welcomePage .commands li { + margin: 7px 0px; +} +.monaco-workbench > .part.editor > .content .welcomePage .commands li button { + margin: 1px; + padding: 12px 10px; + width: calc(100% - 2px); + height: 5em; + font-size: 1.3em; + text-align: left; + cursor: pointer; + white-space: nowrap; +} + +.monaco-workbench > .part.editor > .content .welcomePage .commands li button > span { + display: inline-block; + width:100%; +} + +.monaco-workbench > .part.editor > .content .welcomePage .commands li button h3 { + font-weight: normal; + font-size: 1em; + margin: 0; + margin-bottom: .25em; +} + +.monaco-workbench > .part.editor > .content .welcomePage .commands li button { + border: none; +} + +.hc-black .monaco-workbench > .part.editor > .content .welcomePage .commands li button > h3 { + font-weight: bold; +} + +.monaco-workbench > .part.editor > .content .welcomePage .commands li button:focus { + outline-style: solid; + outline-width: 1px; +} + +.hc-black .monaco-workbench > .part.editor > .content .welcomePage .commands li button { + border-width: 1px; + border-style: solid; +} + +.hc-black .monaco-workbench > .part.editor > .content .welcomePage .commands li button:hover { + outline-width: 1px; + outline-style: dashed; + outline-offset: -5px; +} + +.monaco-workbench > .part.editor > .content .welcomePage .commands li button .enabledExtension { + display: none; +} +.monaco-workbench > .part.editor > .content .welcomePage .commands li button .installExtension.installed { + display: none; +} +.monaco-workbench > .part.editor > .content .welcomePage .commands li button .enabledExtension.installed { + display: inline; +} + +.monaco-workbench > .part.editor > .content .welcomePageContainer.max-height-685px .title { + display: none; +} + +.file-icons-enabled .show-file-icons .vs_code_welcome_page-name-file-icon.file-icon::before { + content: ' '; + background-image: url('../../code-icon.svg'); +} + +.monaco-workbench > .part.editor > .content .welcomePage .mac-only, +.monaco-workbench > .part.editor > .content .welcomePage .windows-only, +.monaco-workbench > .part.editor > .content .welcomePage .linux-only { + display: none; +} +.monaco-workbench.mac > .part.editor > .content .welcomePage .mac-only { + display: initial; +} +.monaco-workbench.windows > .part.editor > .content .welcomePage .windows-only { + display: initial; +} +.monaco-workbench.linux > .part.editor > .content .welcomePage .linux-only { + display: initial; +} +.monaco-workbench.mac > .part.editor > .content .welcomePage li.mac-only { + display: list-item; +} +.monaco-workbench.windows > .part.editor > .content .welcomePage li.windows-only { + display: list-item; +} +.monaco-workbench.linux > .part.editor > .content .welcomePage li.linux-only { + display: list-item; +} diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts new file mode 100644 index 0000000000..e96e38e024 --- /dev/null +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -0,0 +1,564 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./welcomePage'; +import URI from 'vs/base/common/uri'; +import * as path from 'path'; +import * as arrays from 'vs/base/common/arrays'; +import { WalkThroughInput } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughInput'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Position } from 'vs/platform/editor/common/editor'; +import { onUnexpectedError, isPromiseCanceledError } from 'vs/base/common/errors'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { localize } from 'vs/nls'; +import { Action } from 'vs/base/common/actions'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExperimentService } from 'vs/platform/telemetry/common/experiments'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { Schemas } from 'vs/base/common/network'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IMessageService, Severity, CloseAction } from 'vs/platform/message/common/message'; +import { getInstalledExtensions, IExtensionStatus, onExtensionChanged, isKeymapExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils'; +import { IExtensionEnablementService, IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { used } from 'vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page'; +import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { tildify } from 'vs/base/common/labels'; +import { isLinux } from 'vs/base/common/platform'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { getExtraColor } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils'; +import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; + +used(); + +const configurationKey = 'workbench.startupEditor'; +const oldConfigurationKey = 'workbench.welcome.enabled'; +const telemetryFrom = 'welcomePage'; + +export class WelcomePageContribution implements IWorkbenchContribution { + + constructor( + @IPartService partService: IPartService, + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IBackupFileService backupFileService: IBackupFileService, + @ITelemetryService telemetryService: ITelemetryService, + @ILifecycleService lifecycleService: ILifecycleService, + @IStorageService storageService: IStorageService + ) { + const enabled = isWelcomePageEnabled(configurationService); + if (enabled && lifecycleService.startupKind !== StartupKind.ReloadedWindow) { + TPromise.join([ + backupFileService.hasBackups(), + partService.joinCreation() + ]).then(([hasBackups]) => { + const activeInput = editorService.getActiveEditorInput(); + if (!activeInput && !hasBackups) { + return instantiationService.createInstance(WelcomePage) + .openEditor(); + } + return undefined; + }).then(null, onUnexpectedError); + } + } + + public getId() { + return 'vs.welcomePage'; + } +} + +function isWelcomePageEnabled(configurationService: IConfigurationService) { + const startupEditor = configurationService.lookup(configurationKey); + if (!startupEditor.user && !startupEditor.workspace) { + const welcomeEnabled = configurationService.lookup(oldConfigurationKey); + if (welcomeEnabled.value !== undefined && welcomeEnabled.value !== null) { + return welcomeEnabled.value; + } + } + return startupEditor.value === 'welcomePage'; +} + +export class WelcomePageAction extends Action { + + public static ID = 'workbench.action.showWelcomePage'; + public static LABEL = localize('welcomePage', "Welcome"); + + constructor( + id: string, + label: string, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(id, label); + } + + public run(): TPromise { + return this.instantiationService.createInstance(WelcomePage) + .openEditor() + .then(() => undefined); + } +} + +interface ExtensionSuggestion { + name: string; + title?: string; + id: string; + isKeymap?: boolean; + isCommand?: boolean; +} + +const extensionPacks: ExtensionSuggestion[] = [ + { name: localize('welcomePage.javaScript', "JavaScript"), id: 'dbaeumer.vscode-eslint' }, + { name: localize('welcomePage.typeScript', "TypeScript"), id: 'eg2.tslint' }, + { name: localize('welcomePage.python', "Python"), id: 'donjayamanne.python' }, + // { name: localize('welcomePage.go', "Go"), id: 'lukehoban.go' }, + { name: localize('welcomePage.php', "PHP"), id: 'felixfbecker.php-pack' }, + { name: localize('welcomePage.azure', "Azure"), title: localize('welcomePage.showAzureExtensions', "Show Azure extensions"), id: 'workbench.extensions.action.showAzureExtensions', isCommand: true }, + { name: localize('welcomePage.docker', "Docker"), id: 'PeterJausovec.vscode-docker' }, +]; + +const keymapExtensions: ExtensionSuggestion[] = [ + { name: localize('welcomePage.vim', "Vim"), id: 'vscodevim.vim', isKeymap: true }, + { name: localize('welcomePage.sublime', "Sublime"), id: 'ms-vscode.sublime-keybindings', isKeymap: true }, + { name: localize('welcomePage.atom', "Atom"), id: 'ms-vscode.atom-keybindings', isKeymap: true }, +]; + +interface Strings { + installEvent: string; + installedEvent: string; + detailsEvent: string; + + alreadyInstalled: string; + reloadAfterInstall: string; + installing: string; + extensionNotFound: string; +} + +const extensionPackStrings: Strings = { + installEvent: 'installExtension', + installedEvent: 'installedExtension', + detailsEvent: 'detailsExtension', + + alreadyInstalled: localize('welcomePage.extensionPackAlreadyInstalled', "Support for {0} is already installed."), + reloadAfterInstall: localize('welcomePage.willReloadAfterInstallingExtensionPack', "The window will reload after installing additional support for {0}."), + installing: localize('welcomePage.installingExtensionPack', "Installing additional support for {0}..."), + extensionNotFound: localize('welcomePage.extensionPackNotFound', "Support for {0} with id {1} could not be found."), +}; + +const keymapStrings: Strings = { + installEvent: 'installKeymap', + installedEvent: 'installedKeymap', + detailsEvent: 'detailsKeymap', + + alreadyInstalled: localize('welcomePage.keymapAlreadyInstalled', "The {0} keyboard shortcuts are already installed."), + reloadAfterInstall: localize('welcomePage.willReloadAfterInstallingKeymap', "The window will reload after installing the {0} keyboard shortcuts."), + installing: localize('welcomePage.installingKeymap', "Installing the {0} keyboard shortcuts..."), + extensionNotFound: localize('welcomePage.keymapNotFound', "The {0} keyboard shortcuts with id {1} could not be found."), +}; + +const welcomeInputTypeId = 'workbench.editors.welcomePageInput'; + +class WelcomePage { + + private disposables: IDisposable[] = []; + + readonly editorInput: WalkThroughInput; + + constructor( + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService private instantiationService: IInstantiationService, + @IWindowService private windowService: IWindowService, + @IWindowsService private windowsService: IWindowsService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IConfigurationService private configurationService: IConfigurationService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IMessageService private messageService: IMessageService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, + @IExtensionManagementService private extensionManagementService: IExtensionManagementService, + @IExtensionTipsService private tipsService: IExtensionTipsService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @ILifecycleService lifecycleService: ILifecycleService, + @IThemeService private themeService: IThemeService, + @IExperimentService private experimentService: IExperimentService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + this.disposables.push(lifecycleService.onShutdown(() => this.dispose())); + + const recentlyOpened = this.windowService.getRecentlyOpened(); + const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions); + const resource = URI.parse(require.toUrl('./vs_code_welcome_page')) + .with({ + scheme: Schemas.walkThrough, + query: JSON.stringify({ moduleId: 'vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page' }) + }); + this.editorInput = this.instantiationService.createInstance(WalkThroughInput, { + typeId: welcomeInputTypeId, + name: localize('welcome.title', "Welcome"), + resource, + telemetryFrom, + onReady: container => this.onReady(container, recentlyOpened, installedExtensions) + }); + } + + public openEditor() { + return this.editorService.openEditor(this.editorInput, { pinned: true }, Position.ONE); + } + + private onReady(container: HTMLElement, recentlyOpened: TPromise<{ files: string[]; workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]; }>, installedExtensions: TPromise): void { + const enabled = isWelcomePageEnabled(this.configurationService); + const showOnStartup = container.querySelector('#showOnStartup'); + if (enabled) { + showOnStartup.setAttribute('checked', 'checked'); + } + showOnStartup.addEventListener('click', e => { + this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: configurationKey, value: showOnStartup.checked ? 'welcomePage' : 'newUntitledFile' }); + }); + + recentlyOpened.then(({ workspaces }) => { + const context = this.contextService.getWorkspace(); + workspaces = workspaces.filter(workspace => { + if (this.contextService.hasMultiFolderWorkspace() && typeof workspace !== 'string' && context.id === workspace.id) { + return false; // do not show current workspace + } + + if (this.contextService.hasFolderWorkspace() && isSingleFolderWorkspaceIdentifier(workspace) && this.pathEquals(context.roots[0].fsPath, workspace)) { + return false; // do not show current workspace (single folder case) + } + + return true; + }); + if (!workspaces.length) { + const recent = container.querySelector('.welcomePage') as HTMLElement; + recent.classList.add('emptyRecent'); + return; + } + const ul = container.querySelector('.recent ul'); + const before = ul.firstElementChild; + workspaces.slice(0, 5).forEach(workspace => { + let label: string; + let parent: string; + let wsPath: string; + if (isSingleFolderWorkspaceIdentifier(workspace)) { + label = path.basename(workspace); + parent = path.dirname(workspace); + wsPath = workspace; + } else { + label = getWorkspaceLabel(workspace, this.environmentService); + parent = path.dirname(workspace.configPath); + wsPath = workspace.configPath; + } + + const li = document.createElement('li'); + + const a = document.createElement('a'); + let name = label; + let parentFolder = parent; + if (!name && parentFolder) { + const tmp = name; + name = parentFolder; + parentFolder = tmp; + } + const tildifiedParentFolder = tildify(parentFolder, this.environmentService.userHome); + + a.innerText = name; + a.title = label; + a.setAttribute('aria-label', localize('welcomePage.openFolderWithPath', "Open folder {0} with path {1}", name, tildifiedParentFolder)); + a.href = 'javascript:void(0)'; + a.addEventListener('click', e => { + this.telemetryService.publicLog('workbenchActionExecuted', { + id: 'openRecentFolder', + from: telemetryFrom + }); + this.windowsService.openWindow([wsPath], { forceNewWindow: e.ctrlKey || e.metaKey }); + e.preventDefault(); + e.stopPropagation(); + }); + li.appendChild(a); + + const span = document.createElement('span'); + span.classList.add('path'); + span.classList.add('detail'); + span.innerText = tildifiedParentFolder; + span.title = label; + li.appendChild(span); + + ul.insertBefore(li, before); + }); + }).then(null, onUnexpectedError); + + this.addExtensionList(container, '.extensionPackList', extensionPacks, extensionPackStrings); + this.addExtensionList(container, '.keymapList', keymapExtensions, keymapStrings); + + this.updateInstalledExtensions(container, installedExtensions); + this.disposables.push(this.instantiationService.invokeFunction(onExtensionChanged)(ids => { + for (const id of ids) { + if (container.querySelector(`.installExtension[data-extension="${id}"], .enabledExtension[data-extension="${id}"]`)) { + const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions); + this.updateInstalledExtensions(container, installedExtensions); + break; + } + }; + })); + + if (this.experimentService.getExperiments().deployToAzureQuickLink) { + container.querySelector('.showInterfaceOverview').remove(); + } else { + container.querySelector('.deployToAzure').remove(); + } + } + + private addExtensionList(container: HTMLElement, listSelector: string, suggestions: ExtensionSuggestion[], strings: Strings) { + const list = container.querySelector(listSelector); + if (list) { + suggestions.forEach((extension, i) => { + if (i) { + list.appendChild(document.createTextNode(localize('welcomePage.extensionListSeparator', ", "))); + } + + const a = document.createElement('a'); + a.innerText = extension.name; + a.title = extension.title || (extension.isKeymap ? localize('welcomePage.installKeymap', "Install {0} keymap", extension.name) : localize('welcomePage.installExtensionPack', "Install additional support for {0}", extension.name)); + if (extension.isCommand) { + a.href = `command:${extension.id}`; + list.appendChild(a); + } else { + a.classList.add('installExtension'); + a.setAttribute('data-extension', extension.id); + a.href = 'javascript:void(0)'; + a.addEventListener('click', e => { + this.installExtension(extension, strings); + e.preventDefault(); + e.stopPropagation(); + }); + list.appendChild(a); + + const span = document.createElement('span'); + span.innerText = extension.name; + span.title = extension.isKeymap ? localize('welcomePage.installedKeymap', "{0} keymap is already installed", extension.name) : localize('welcomePage.installedExtensionPack', "{0} support is already installed", extension.name); + span.classList.add('enabledExtension'); + span.setAttribute('data-extension', extension.id); + list.appendChild(span); + } + }); + } + } + + private pathEquals(path1: string, path2: string): boolean { + if (!isLinux) { + path1 = path1.toLowerCase(); + path2 = path2.toLowerCase(); + } + + return path1 === path2; + } + + private installExtension(extensionSuggestion: ExtensionSuggestion, strings: Strings): void { + this.telemetryService.publicLog(strings.installEvent, { + from: telemetryFrom, + extensionId: extensionSuggestion.id, + }); + this.instantiationService.invokeFunction(getInstalledExtensions).then(extensions => { + const installedExtension = arrays.first(extensions, extension => extension.identifier === extensionSuggestion.id); + if (installedExtension && installedExtension.globallyEnabled) { + this.telemetryService.publicLog(strings.installedEvent, { + from: telemetryFrom, + extensionId: extensionSuggestion.id, + outcome: 'already_enabled', + }); + this.messageService.show(Severity.Info, strings.alreadyInstalled.replace('{0}', extensionSuggestion.name)); + return; + } + const foundAndInstalled = installedExtension ? TPromise.as(true) : this.extensionGalleryService.query({ names: [extensionSuggestion.id] }) + .then(result => { + const [extension] = result.firstPage; + if (!extension) { + return false; + } + return this.extensionManagementService.installFromGallery(extension, false) + .then(() => { + // TODO: Do this as part of the install to avoid multiple events. + return this.extensionEnablementService.setEnablement(extensionSuggestion.id, false); + }).then(() => { + return true; + }); + }); + this.messageService.show(Severity.Info, { + message: strings.reloadAfterInstall.replace('{0}', extensionSuggestion.name), + actions: [ + new Action('ok', localize('ok', "OK"), null, true, () => { + const messageDelay = TPromise.timeout(300); + messageDelay.then(() => { + this.messageService.show(Severity.Info, { + message: strings.installing.replace('{0}', extensionSuggestion.name), + actions: [CloseAction] + }); + }); + TPromise.join(extensionSuggestion.isKeymap ? extensions.filter(extension => isKeymapExtension(this.tipsService, extension) && extension.globallyEnabled) + .map(extension => { + return this.extensionEnablementService.setEnablement(extension.identifier, false); + }) : []).then(() => { + return foundAndInstalled.then(found => { + messageDelay.cancel(); + if (found) { + return this.extensionEnablementService.setEnablement(extensionSuggestion.id, true) + .then(() => { + this.telemetryService.publicLog(strings.installedEvent, { + from: telemetryFrom, + extensionId: extensionSuggestion.id, + outcome: installedExtension ? 'enabled' : 'installed', + }); + return this.windowService.reloadWindow(); + }); + } else { + this.telemetryService.publicLog(strings.installedEvent, { + from: telemetryFrom, + extensionId: extensionSuggestion.id, + outcome: 'not_found', + }); + this.messageService.show(Severity.Error, strings.extensionNotFound.replace('{0}', extensionSuggestion.name).replace('{1}', extensionSuggestion.id)); + return undefined; + } + }); + }).then(null, err => { + this.telemetryService.publicLog(strings.installedEvent, { + from: telemetryFrom, + extensionId: extensionSuggestion.id, + outcome: isPromiseCanceledError(err) ? 'canceled' : 'error', + error: String(err), + }); + this.messageService.show(Severity.Error, err); + }); + return TPromise.as(true); + }), + new Action('details', localize('details', "Details"), null, true, () => { + this.telemetryService.publicLog(strings.detailsEvent, { + from: telemetryFrom, + extensionId: extensionSuggestion.id, + }); + this.extensionsWorkbenchService.queryGallery({ names: [extensionSuggestion.id] }) + .then(result => this.extensionsWorkbenchService.open(result.firstPage[0])) + .then(null, onUnexpectedError); + return TPromise.as(false); + }), + new Action('cancel', localize('cancel', "Cancel"), null, true, () => { + this.telemetryService.publicLog(strings.installedEvent, { + from: telemetryFrom, + extensionId: extensionSuggestion.id, + outcome: 'user_canceled', + }); + return TPromise.as(true); + }) + ] + }); + }).then(null, err => { + this.telemetryService.publicLog(strings.installedEvent, { + from: telemetryFrom, + extensionId: extensionSuggestion.id, + outcome: isPromiseCanceledError(err) ? 'canceled' : 'error', + error: String(err), + }); + this.messageService.show(Severity.Error, err); + }); + } + + private updateInstalledExtensions(container: HTMLElement, installedExtensions: TPromise) { + installedExtensions.then(extensions => { + const elements = container.querySelectorAll('.installExtension, .enabledExtension'); + for (let i = 0; i < elements.length; i++) { + elements[i].classList.remove('installed'); + } + extensions.filter(ext => ext.globallyEnabled) + .map(ext => ext.identifier) + .forEach(id => { + const install = container.querySelectorAll(`.installExtension[data-extension="${id}"]`); + for (let i = 0; i < install.length; i++) { + install[i].classList.add('installed'); + } + const enabled = container.querySelectorAll(`.enabledExtension[data-extension="${id}"]`); + for (let i = 0; i < enabled.length; i++) { + enabled[i].classList.add('installed'); + } + }); + }).then(null, onUnexpectedError); + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + + +export class WelcomeInputFactory implements IEditorInputFactory { + + static ID = welcomeInputTypeId; + + public serialize(editorInput: EditorInput): string { + return '{}'; + } + + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): WalkThroughInput { + return instantiationService.createInstance(WelcomePage) + .editorInput; + } +} + +// theming + +const buttonBackground = registerColor('welcomePage.buttonBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonBackground', 'Background color for the buttons on the Welcome page.')); +const buttonHoverBackground = registerColor('welcomePage.buttonHoverBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonHoverBackground', 'Hover background color for the buttons on the Welcome page.')); + +registerThemingParticipant((theme, collector) => { + const foregroundColor = theme.getColor(foreground); + if (foregroundColor) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .caption { color: ${foregroundColor}; }`); + } + const descriptionColor = theme.getColor(descriptionForeground); + if (descriptionColor) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .detail { color: ${descriptionColor}; }`); + } + const buttonColor = getExtraColor(theme, buttonBackground, { dark: 'rgba(0, 0, 0, .2)', extra_dark: 'rgba(200, 235, 255, .042)', light: 'rgba(0,0,0,.04)', hc: 'black' }); + if (buttonColor) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .commands li button { background: ${buttonColor}; }`); + } + const buttonHoverColor = getExtraColor(theme, buttonHoverBackground, { dark: 'rgba(200, 235, 255, .072)', extra_dark: 'rgba(200, 235, 255, .072)', light: 'rgba(0,0,0,.10)', hc: null }); + if (buttonHoverColor) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .commands li button:hover { background: ${buttonHoverColor}; }`); + } + const link = theme.getColor(textLinkForeground); + if (link) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage a { color: ${link}; }`); + } + const activeLink = theme.getColor(textLinkActiveForeground); + if (activeLink) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage a:hover, + .monaco-workbench > .part.editor > .content .welcomePage a:active { color: ${activeLink}; }`); + } + const focusColor = theme.getColor(focusBorder); + if (focusColor) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage a:focus { outline-color: ${focusColor}; }`); + } + const border = theme.getColor(contrastBorder); + if (border) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .commands li button { border-color: ${border}; }`); + } + const activeBorder = theme.getColor(activeContrastBorder); + if (activeBorder) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .commands li button:hover { outline-color: ${activeBorder}; }`); + } +}); diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts new file mode 100644 index 0000000000..3793937f54 --- /dev/null +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { localize } from 'vs/nls'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Position } from 'vs/platform/editor/common/editor'; +import { Action } from 'vs/base/common/actions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughInput'; +import { Schemas } from 'vs/base/common/network'; +import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; + +const typeId = 'workbench.editors.walkThroughInput'; +const inputOptions: WalkThroughInputOptions = { + typeId, + name: localize('editorWalkThrough.title', "Interactive Playground"), + resource: URI.parse(require.toUrl('./vs_code_editor_walkthrough.md')) + .with({ scheme: Schemas.walkThrough }), + telemetryFrom: 'walkThrough' +}; + +export class EditorWalkThroughAction extends Action { + + public static ID = 'workbench.action.showInteractivePlayground'; + public static LABEL = localize('editorWalkThrough', "Interactive Playground"); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(id, label); + } + + public run(): TPromise { + const input = this.instantiationService.createInstance(WalkThroughInput, inputOptions); + return this.editorService.openEditor(input, { pinned: true }, Position.ONE) + .then(() => void (0)); + } +} + +export class EditorWalkThroughInputFactory implements IEditorInputFactory { + + static ID = typeId; + + public serialize(editorInput: EditorInput): string { + return '{}'; + } + + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): WalkThroughInput { + return instantiationService.createInstance(WalkThroughInput, inputOptions); + } +} diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md new file mode 100644 index 0000000000..1edc236faf --- /dev/null +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md @@ -0,0 +1,165 @@ +## Interactive Editor Playground +The core editor in VS Code is packed with features. This page highlights a number of them and lets you interactively try them out through the use of a number of embedded editors. For full details on the editor features for VS Code and more head over to our [documentation](command:workbench.action.openDocumentationUrl). + +* [Multi-cursor Editing](#multi-cursor-editing) - block selection, select all occurrences, add additional cursors and more +* [IntelliSense](#intellisense) - get code assistance and parameter suggestions for your code and external modules. +* [Line Actions](#line-actions) - quickly move lines around to re-order your code. +* [Rename Refactoring](#rename-refactoring) - Quickly rename symbols across your code base. +* [Formatting](#formatting) - keep your code looking great with inbuilt document & selection formatting. +* [Code Folding](#code-folding) - focus on the most relevant parts of your code by folding other areas. +* [Errors and Warnings](#errors-and-warnings) - see errors and warning as you type. +* [Snippets](#snippets) - spend less time typing with snippets. +* [Emmet](#emmet) - integrated Emmet support takes HTML and CSS editing to the next level. + + + +### Multi-Cursor Editing +Using multiple cursors allows you to edit multiple parts of the document at once, greatly improving your productivity. Try the following actions in the code block below: +1. Box Selection - press any combination of kb(cursorColumnSelectDown), kb(cursorColumnSelectRight), kb(cursorColumnSelectUp), kb(cursorColumnSelectLeft) to select a block of text, you can also press `⇧⌥``Shift+Alt` while selecting text with the mouse. +2. Add a cursor - press kb(editor.action.insertCursorAbove) or kb(editor.action.insertCursorBelow) to add a new cursor above or below, you can also use your mouse with +Click to add a cursor anywhere. +3. Create cursors on all occurrences of a string - select one instance of a string e.g. `background-color` and press kb(editor.action.selectHighlights). Now you can replace all instances by simply typing. + +That is the tip of the iceberg for multi-cursor editing have a look at the `selection menu` and our handy [keyboard reference guide](command:workbench.action.keybindingsReference) for additional actions. + +```css +#p1 {background-color: #ff0000;} /* red */ +#p2 {background-color: #00ff00;} /* green */ +#p3 {background-color: #0000ff;} /* blue */ +``` + +> **CSS Tip:** you may have noticed in the example above for CSS we also provide color swatches inline, additionally if you hover over an element such as `#p1` we will show how this is represented in HTML. A simple example of some language specific editor features. + +### IntelliSense + +Visual Studio Code comes with powerful IntelliSense for JavaScript and TypeScript pre-installed. In the below example, position the text cursor in front of the error underline, right after the dot and press kb(editor.action.triggerSuggest) to invoke IntelliSense. Notice how the suggestion comes from the Request API. + +```js +var express = require('express'); +var app = express(); + +app.get('/', function (req, res) { + res.send(`Hello ${req.}`); +}); + +app.listen(3000); +``` + +>**Tip:** while we ship JavaScript and TypeScript support out of the box other languages can be upgraded with better IntelliSense through one of the many [extensions](command:workbench.extensions.action.showPopularExtensions). + + +### Line Actions +Since it's very common to want to work with the entire text in a line we provide a set of useful shortcuts to help with this. +1. Copy a line and insert it above or below the current position with kb(editor.action.copyLinesDownAction) or kb(editor.action.copyLinesUpAction) respectively. +2. Move an entire line or selection of lines up or down with kb(editor.action.moveLinesUpAction) and kb(editor.action.moveLinesDownAction) respectively. +3. Delete the entire line with kb(editor.action.deleteLines). + +```json +{ + "name": "John", + "age": 31, + "city": "New York" +} +``` + +>**Tip:** Another very common task is to comment out a block of code - you can toggle commenting by pressing kb(editor.action.commentLine). + + + +### Rename Refactoring +It's easy to rename a symbol such as a function name or variable name. Hit kb(editor.action.rename) while in the symbol `Book` to rename all instances - this will occur across all files in a project. You can also see refactoring in the right click context menu. + +```js +// Reference the function +new Book("War of the Worlds", "H G Wells"); +new Book("The Martian", "Andy Weir"); + +/** + * Represents a book. + */ +function Book(title, author) { + this.title = title; + this.author = author; +} +``` + +> **JSDoc Tip:** The example above also showcased another way to get IntelliSense hints by using `JSDoc` comments. You can try this out by invoking the `Book` function and seeing the enhanced context in the IntelliSense Experience for the function as well as parameters. + + +### Formatting +Keeping your code looking great is hard without a good formatter. Luckily it's easy to format content either the entire document with kb(editor.action.formatDocument) or formatting can be applied to the current selection with kb(editor.action.formatSelection). Both of these options are also available through the right click context menu. + +```js +var cars = ["Saab", "Volvo", "BMW"]; + +for (var i=0; i < cars.length; i++) { +// Drive the car +console.log(`This is the manufacturer [${cars[i]}])`); + } +``` + +>**Tip:** Additional formatters are available in the [extension gallery](command:workbench.extensions.action.showPopularExtensions). Formatting support can also be configured via [settings](command:workbench.action.openGlobalSettings) e.g. enabling `editor.formatOnSave`. + + +### Code Folding +In a large file it can often be useful to collapse sections of code to increase readability. To do this you can simply press kb(editor.fold) to `fold` the code - press kb(editor.unfold) to `unfold`. Folding can also be done with the +/- icons in the left gutter. To fold all sections kb(editor.foldAll) or to unfold all kb(editor.unfoldAll). + +```html +
+
+
    +
  • +
  • +
+
+
+

+
+
+``` + +>**Tip:** Folding is based on indentation and as a result can apply to all languages. Simply indent your code to create a foldable section you can fold a certain number of levels with shortcuts like kb(editor.foldLevel1) through to kb(editor.foldLevel5). + +### Errors and Warnings +Errors and warnings are highlighted as you edit your code with `squiggles`. In the sample below you can see a number of syntax errors. By pressing kb(editor.action.marker.next) you can navigate across them in sequence and see the detailed error message. As you correct them the `squiggles` and `scrollbar indicators` will update. + +```js +// This code has a few syntax errors +Console.log(add(1, 1.5)); + + +function Add(a : Number, b : Number) : Int { + return a + b; +} +``` + + +### Snippets +You can greatly accelerate your editing through the use of snippets. Simply start typing `try` and select `trycatch` from the suggestion list and press kb(insertSnippet) to create a `try`->`catch` block. Your cursor will be placed on the text `error` for easy editing. If more than one parameter exists then press kb(jumpToNextSnippetPlaceholder) to jump to it. + +```js + +``` + +>**Tip:** the extension gallery includes snippets for almost every framework and language imaginable [extensions](command:workbench.extensions.action.showPopularExtensions). You can also create your own [user defined snippets](command:workbench.action.openSnippets). + + +### Emmet +Emmet takes the snippets idea to a whole new level: you can type CSS-like expressions that can be dynamically parsed, and produce output depending on what you type in the abbreviation. To use Emmet simply run the command `Emmet: Expand Abbreviation` with cursor at the end of a valid Emmet abbreviation or snippet and the expansion will occur. + +```html +ul>li.item$*5 +``` + +>**Tip:** The [Emmet cheat sheet](http://docs.emmet.io/cheat-sheet/) is a great source of Emmet syntax suggestions. To expand Emmet abbreviations and snippets using the `tab` key use the `emmet.triggerExpansionOnTab` [setting](command:workbench.action.openGlobalSettings). Check out the docs on [Emmet in VS Code](https://code.visualstudio.com/docs/editor/emmet) to learn more. + + + +## Thanks! +Well if you have got this far then you will have touched on some of the editing features in Visual Studio Code. But don't stop now :) We have lots of additional [documentation](https://code.visualstudio.com/docs), [introductory videos](https://code.visualstudio.com/docs/getstarted/introvideos) and [tips and tricks](https://go.microsoft.com/fwlink/?linkid=852118) for the product that will help you learn how to use it. And while you are here, here are a few additional things you can try: +- Open the Integrated Terminal by pressing kb(workbench.action.terminal.toggleTerminal) then see what's possible by [reviewing the terminal documentation](https://code.visualstudio.com/docs/editor/integrated-terminal) +- Work with version control by pressing kb(workbench.view.scm) understand how to stage, commit, change branches, and view diffs and more by reviewing the [version control documentation](https://code.visualstudio.com/docs/editor/versioncontrol) +- Browse thousands of extensions in our integrated gallery by pressing with kb(workbench.view.extensions) the [documentation](https://code.visualstudio.com/docs/editor/extension-gallery) will show you how to see the most popular extensions, disable installed ones and more. + +OK that's all for now, + +Happy Coding! \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts new file mode 100644 index 0000000000..e8292ed497 --- /dev/null +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { localize } from 'vs/nls'; +import { WalkThroughInput } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughInput'; +import { WalkThroughPart, WALK_THROUGH_FOCUS } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart'; +import { WalkThroughArrowUpAction, WalkThroughArrowDownAction, WalkThroughPageUpAction, WalkThroughPageDownAction } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions'; +import { WalkThroughContentProvider, WalkThroughSnippetContentProvider } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider'; +import { EditorWalkThroughAction, EditorWalkThroughInputFactory } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; + +Registry.as(EditorExtensions.Editors) + .registerEditor(new EditorDescriptor(WalkThroughPart.ID, + localize('walkThrough.editor.label', "Interactive Playground"), + 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart', + 'WalkThroughPart'), + [new SyncDescriptor(WalkThroughInput)]); + +Registry.as(Extensions.WorkbenchActions) + .registerWorkbenchAction( + new SyncActionDescriptor(EditorWalkThroughAction, EditorWalkThroughAction.ID, EditorWalkThroughAction.LABEL), + 'Help: Interactive Playground', localize('help', "Help")); + +Registry.as(EditorExtensions.Editors).registerEditorInputFactory(EditorWalkThroughInputFactory.ID, EditorWalkThroughInputFactory); + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(WalkThroughContentProvider); + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(WalkThroughSnippetContentProvider); + +Registry.as(Extensions.WorkbenchActions) + .registerWorkbenchAction(new SyncActionDescriptor(WalkThroughArrowUpAction, WalkThroughArrowUpAction.ID, WalkThroughArrowUpAction.LABEL, { primary: KeyCode.UpArrow }, ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.textFocus.toNegated())), 'Interactive Playground: Scroll Up (Line)', localize('interactivePlayground', "Interactive Playground")); + +Registry.as(Extensions.WorkbenchActions) + .registerWorkbenchAction(new SyncActionDescriptor(WalkThroughArrowDownAction, WalkThroughArrowDownAction.ID, WalkThroughArrowDownAction.LABEL, { primary: KeyCode.DownArrow }, ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.textFocus.toNegated())), 'Interactive Playground: Scroll Down (Line)', localize('interactivePlayground', "Interactive Playground")); + +Registry.as(Extensions.WorkbenchActions) + .registerWorkbenchAction(new SyncActionDescriptor(WalkThroughPageUpAction, WalkThroughPageUpAction.ID, WalkThroughPageUpAction.LABEL, { primary: KeyCode.PageUp }, ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.textFocus.toNegated())), 'Interactive Playground: Scroll Up (Page)', localize('interactivePlayground', "Interactive Playground")); + +Registry.as(Extensions.WorkbenchActions) + .registerWorkbenchAction(new SyncActionDescriptor(WalkThroughPageDownAction, WalkThroughPageDownAction.ID, WalkThroughPageDownAction.LABEL, { primary: KeyCode.PageDown }, ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.textFocus.toNegated())), 'Interactive Playground: Scroll Down (Page)', localize('interactivePlayground', "Interactive Playground")); diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts new file mode 100644 index 0000000000..980cc026a4 --- /dev/null +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { localize } from 'vs/nls'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Action } from 'vs/base/common/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { WalkThroughPart } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart'; + +export class WalkThroughArrowUpAction extends Action { + + public static ID = 'workbench.action.interactivePlayground.arrowUp'; + public static LABEL = localize('editorWalkThrough.arrowUp', "Scroll Up (Line)"); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(id, label); + } + + public run(): TPromise { + const editor = this.editorService.getActiveEditor(); + if (editor instanceof WalkThroughPart) { + editor.arrowUp(); + } + return null; + } +} + +export class WalkThroughArrowDownAction extends Action { + + public static ID = 'workbench.action.interactivePlayground.arrowDown'; + public static LABEL = localize('editorWalkThrough.arrowDown', "Scroll Down (Line)"); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(id, label); + } + + public run(): TPromise { + const editor = this.editorService.getActiveEditor(); + if (editor instanceof WalkThroughPart) { + editor.arrowDown(); + } + return null; + } +} + +export class WalkThroughPageUpAction extends Action { + + public static ID = 'workbench.action.interactivePlayground.pageUp'; + public static LABEL = localize('editorWalkThrough.pageUp', "Scroll Up (Page)"); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(id, label); + } + + public run(): TPromise { + const editor = this.editorService.getActiveEditor(); + if (editor instanceof WalkThroughPart) { + editor.pageUp(); + } + return null; + } +} + +export class WalkThroughPageDownAction extends Action { + + public static ID = 'workbench.action.interactivePlayground.pageDown'; + public static LABEL = localize('editorWalkThrough.pageDown', "Scroll Down (Page)"); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(id, label); + } + + public run(): TPromise { + const editor = this.editorService.getActiveEditor(); + if (editor instanceof WalkThroughPart) { + editor.pageDown(); + } + return null; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.css b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.css new file mode 100644 index 0000000000..7a072782da --- /dev/null +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench > .part.editor > .content .walkThroughContent { + box-sizing: border-box; + padding: 10px 20px; + line-height: 22px; + user-select: initial; + -webkit-user-select: initial; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent img { + max-width: 100%; + max-height: 100%; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent a { + text-decoration: none; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent a:focus, +.monaco-workbench > .part.editor > .content .walkThroughContent input:focus, +.monaco-workbench > .part.editor > .content .walkThroughContent select:focus, +.monaco-workbench > .part.editor > .content .walkThroughContent textarea:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent hr { + border: 0; + height: 2px; + border-bottom: 2px solid; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent h1, +.monaco-workbench > .part.editor > .content .walkThroughContent h2, +.monaco-workbench > .part.editor > .content .walkThroughContent h3 { + font-weight: lighter; + margin-top: 20px; + margin-bottom: 10px; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent h1 { + padding-bottom: 0.3em; + line-height: 1.2; + border-bottom-width: 1px; + border-bottom-style: solid; + font-size: 40px; + margin-bottom: 15px; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent h2 { + font-size: 30px; + margin-top: 30px; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent h3 { + font-size: 22px; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent h4 { + font-size: 12px; + text-transform: uppercase; + margin-top: 30px; + margin-bottom: 10px; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent a:hover { + text-decoration: underline; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent table { + border-collapse: collapse; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent table > thead > tr > th { + text-align: left; + border-bottom: 1px solid; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent table > thead > tr > th, +.monaco-workbench > .part.editor > .content .walkThroughContent table > thead > tr > td, +.monaco-workbench > .part.editor > .content .walkThroughContent table > tbody > tr > th, +.monaco-workbench > .part.editor > .content .walkThroughContent table > tbody > tr > td { + padding: 5px 10px; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent table > tbody > tr + tr > td { + border-top: 1px solid; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left: 5px solid; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent code, +.monaco-workbench > .part.editor > .content .walkThroughContent .shortcut { + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-size: 14px; + line-height: 19px; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left: 5px solid; +} + +.monaco-workbench > .part.editor > .content .walkThroughContent .monaco-tokenized-source { + white-space: pre; +} + +.file-icons-enabled .show-file-icons .vs_code_editor_walkthrough\.md-name-file-icon.md-ext-file-icon.markdown-lang-file-icon.file-icon::before { + content: ' '; + background-image: url('../../code-icon.svg'); +} + +.monaco-workbench > .part.editor > .content .walkThroughContent .mac-only, +.monaco-workbench > .part.editor > .content .walkThroughContent .windows-only, +.monaco-workbench > .part.editor > .content .walkThroughContent .linux-only { + display: none; +} +.monaco-workbench.mac > .part.editor > .content .walkThroughContent .mac-only { + display: initial; +} +.monaco-workbench.windows > .part.editor > .content .walkThroughContent .windows-only { + display: initial; +} +.monaco-workbench.linux > .part.editor > .content .walkThroughContent .linux-only { + display: initial; +} + +.hc-black .monaco-workbench > .part.editor > .content .walkThroughContent .monaco-editor { + border-width: 1px; + border-style: solid; +} diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts new file mode 100644 index 0000000000..68469c4d7c --- /dev/null +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts @@ -0,0 +1,587 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./walkThroughPart'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import * as strings from 'vs/base/common/strings'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { $, Dimension, Builder } from 'vs/base/browser/builder'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { EditorOptions } from 'vs/workbench/common/editor'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { WalkThroughInput } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughInput'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { marked } from 'vs/base/common/marked/marked'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { CodeEditor } from 'vs/editor/browser/codeEditor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { localize } from 'vs/nls'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { Scope } from 'vs/workbench/common/memento'; +import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { once } from 'vs/base/common/event'; +import { isObject } from 'vs/base/common/types'; +import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, textPreformatForeground, contrastBorder, textBlockQuoteBackground, textBlockQuoteBorder } from 'vs/platform/theme/common/colorRegistry'; +import { getExtraColor } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils'; +import { UILabelProvider } from 'vs/base/common/keybindingLabels'; +import { OS, OperatingSystem } from 'vs/base/common/platform'; + +export const WALK_THROUGH_FOCUS = new RawContextKey('interactivePlaygroundFocus', false); + +const UNBOUND_COMMAND = localize('walkThrough.unboundCommand', "unbound"); +const WALK_THROUGH_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'walkThroughEditorViewState'; + +interface IViewState { + scrollTop: number; + scrollLeft: number; +} + +interface IWalkThroughEditorViewState { + viewState: IViewState; +} + +interface IWalkThroughEditorViewStates { + 0?: IWalkThroughEditorViewState; + 1?: IWalkThroughEditorViewState; + 2?: IWalkThroughEditorViewState; +} + +class WalkThroughCodeEditor extends CodeEditor { + + constructor( + domElement: HTMLElement, + options: IEditorOptions, + private telemetryData: Object, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @ICommandService commandService: ICommandService, + @IContextKeyService contextKeyService: IContextKeyService, + @IThemeService themeService: IThemeService + ) { + super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService, themeService); + } + + getTelemetryData() { + return this.telemetryData; + } +} + +export class WalkThroughPart extends BaseEditor { + + static ID: string = 'workbench.editor.walkThroughPart'; + + private disposables: IDisposable[] = []; + private contentDisposables: IDisposable[] = []; + private content: HTMLDivElement; + private scrollbar: DomScrollableElement; + private editorFocus: IContextKey; + private size: Dimension; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IInstantiationService private instantiationService: IInstantiationService, + @IThemeService protected themeService: IThemeService, + @IOpenerService private openerService: IOpenerService, + @IFileService private fileService: IFileService, + @IModelService protected modelService: IModelService, + @IKeybindingService private keybindingService: IKeybindingService, + @IStorageService private storageService: IStorageService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IConfigurationService private configurationService: IConfigurationService, + @IModeService private modeService: IModeService, + @IMessageService private messageService: IMessageService, + @IPartService private partService: IPartService + ) { + super(WalkThroughPart.ID, telemetryService, themeService); + this.editorFocus = WALK_THROUGH_FOCUS.bindTo(this.contextKeyService); + } + + createEditor(parent: Builder): void { + const container = parent.getHTMLElement(); + + this.content = document.createElement('div'); + this.content.tabIndex = 0; + this.content.style.outlineStyle = 'none'; + + this.scrollbar = new DomScrollableElement(this.content, { + horizontal: ScrollbarVisibility.Auto, + vertical: ScrollbarVisibility.Auto + }); + this.disposables.push(this.scrollbar); + container.appendChild(this.scrollbar.getDomNode()); + + this.registerFocusHandlers(); + this.registerClickHandler(); + + this.disposables.push(this.scrollbar.onScroll(e => this.updatedScrollPosition())); + } + + private updatedScrollPosition() { + const scrollDimensions = this.scrollbar.getScrollDimensions(); + const scrollPosition = this.scrollbar.getScrollPosition(); + const scrollHeight = scrollDimensions.scrollHeight; + if (scrollHeight && this.input instanceof WalkThroughInput) { + const scrollTop = scrollPosition.scrollTop; + const height = scrollDimensions.height; + this.input.relativeScrollPosition(scrollTop / scrollHeight, (scrollTop + height) / scrollHeight); + } + } + + private addEventListener(element: E, type: K, listener: (this: E, ev: HTMLElementEventMap[K]) => any, useCapture?: boolean): IDisposable; + private addEventListener(element: E, type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): IDisposable; + private addEventListener(element: E, type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): IDisposable { + element.addEventListener(type, listener, useCapture); + return { dispose: () => { element.removeEventListener(type, listener, useCapture); } }; + } + + private registerFocusHandlers() { + this.disposables.push(this.addEventListener(this.content, 'mousedown', e => { + this.focus(); + })); + this.disposables.push(this.addEventListener(this.content, 'focus', e => { + this.editorFocus.set(true); + })); + this.disposables.push(this.addEventListener(this.content, 'blur', e => { + this.editorFocus.reset(); + })); + this.disposables.push(this.addEventListener(this.content, 'focusin', e => { + // Work around scrolling as side-effect of setting focus on the offscreen zone widget (#18929) + if (e.target instanceof HTMLElement && e.target.classList.contains('zone-widget-container')) { + const scrollPosition = this.scrollbar.getScrollPosition(); + this.content.scrollTop = scrollPosition.scrollTop; + this.content.scrollLeft = scrollPosition.scrollLeft; + } + })); + } + + private registerClickHandler() { + this.content.addEventListener('click', event => { + for (let node = event.target as HTMLElement; node; node = node.parentNode as HTMLElement) { + if (node instanceof HTMLAnchorElement && node.href) { + let baseElement = window.document.getElementsByTagName('base')[0] || window.location; + if (baseElement && node.href.indexOf(baseElement.href) >= 0 && node.hash) { + let scrollTarget = this.content.querySelector(node.hash); + this.telemetryService.publicLog('revealInDocument', { + hash: node.hash, + broken: !scrollTarget, + from: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined + }); + const innerContent = this.content.firstElementChild; + if (scrollTarget && innerContent) { + const targetTop = scrollTarget.getBoundingClientRect().top - 20; + const containerTop = innerContent.getBoundingClientRect().top; + this.scrollbar.setScrollPosition({ scrollTop: targetTop - containerTop }); + } + } else { + this.open(URI.parse(node.href)); + } + event.preventDefault(); + break; + } else if (node instanceof HTMLButtonElement) { + const href = node.getAttribute('data-href'); + if (href) { + this.open(URI.parse(href)); + } + break; + } else if (node === event.currentTarget) { + break; + } + } + }); + } + + private open(uri: URI) { + if (uri.scheme === 'http' || uri.scheme === 'https') { + this.telemetryService.publicLog('openExternal', { + uri: uri.toString(true), + from: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined + }); + } + if (uri.scheme === 'command' && uri.path === 'git.clone' && !CommandsRegistry.getCommand('git.clone')) { + this.messageService.show(Severity.Info, localize('walkThrough.gitNotFound', "It looks like Git is not installed on your system.")); + return; + } + this.openerService.open(this.addFrom(uri)); + } + + private addFrom(uri: URI) { + if (uri.scheme !== 'command' || !(this.input instanceof WalkThroughInput)) { + return uri; + } + const query = uri.query ? JSON.parse(uri.query) : {}; + query.from = this.input.getTelemetryFrom(); + return uri.with({ query: JSON.stringify(query) }); + } + + layout(size: Dimension): void { + this.size = size; + $(this.content).style({ height: `${size.height}px`, width: `${size.width}px` }); + this.updateSizeClasses(); + this.contentDisposables.forEach(disposable => { + if (disposable instanceof CodeEditor) { + disposable.layout(); + } + }); + this.scrollbar.scanDomNode(); + } + + private updateSizeClasses() { + const innerContent = this.content.firstElementChild; + if (this.size && innerContent) { + const classList = innerContent.classList; + classList[this.size.height <= 685 ? 'add' : 'remove']('max-height-685px'); + } + } + + focus(): void { + let active = document.activeElement; + while (active && active !== this.content) { + active = active.parentElement; + } + if (!active) { + this.content.focus(); + } + this.editorFocus.set(true); + } + + arrowUp() { + const scrollPosition = this.scrollbar.getScrollPosition(); + this.scrollbar.setScrollPosition({ scrollTop: scrollPosition.scrollTop - this.getArrowScrollHeight() }); + } + + arrowDown() { + const scrollPosition = this.scrollbar.getScrollPosition(); + this.scrollbar.setScrollPosition({ scrollTop: scrollPosition.scrollTop + this.getArrowScrollHeight() }); + } + + private getArrowScrollHeight() { + let fontSize = this.configurationService.lookup('editor.fontSize').value; + if (typeof fontSize !== 'number' || fontSize < 1) { + fontSize = 12; + } + return 3 * fontSize; + } + + pageUp() { + const scrollDimensions = this.scrollbar.getScrollDimensions(); + const scrollPosition = this.scrollbar.getScrollPosition(); + this.scrollbar.setScrollPosition({ scrollTop: scrollPosition.scrollTop - scrollDimensions.height }); + } + + pageDown() { + const scrollDimensions = this.scrollbar.getScrollDimensions(); + const scrollPosition = this.scrollbar.getScrollPosition(); + this.scrollbar.setScrollPosition({ scrollTop: scrollPosition.scrollTop + scrollDimensions.height }); + } + + setInput(input: WalkThroughInput, options: EditorOptions): TPromise { + if (this.input instanceof WalkThroughInput && this.input.matches(input)) { + return TPromise.as(undefined); + } + + if (this.input instanceof WalkThroughInput) { + this.saveTextEditorViewState(this.input.getResource()); + } + + this.contentDisposables = dispose(this.contentDisposables); + this.content.innerHTML = ''; + + return super.setInput(input, options) + .then(() => { + return input.resolve(true); + }) + .then(model => { + const content = model.main.textEditorModel.getLinesContent().join('\n'); + if (!strings.endsWith(input.getResource().path, '.md')) { + this.content.innerHTML = content; + this.updateSizeClasses(); + this.decorateContent(); + this.contentDisposables.push(this.keybindingService.onDidUpdateKeybindings(() => this.decorateContent())); + if (input.onReady) { + input.onReady(this.content.firstElementChild as HTMLElement); + } + this.scrollbar.scanDomNode(); + this.loadTextEditorViewState(input.getResource()); + this.updatedScrollPosition(); + return; + } + + let i = 0; + const renderer = new marked.Renderer(); + renderer.code = (code, lang) => { + const id = `snippet-${model.snippets[i++].textEditorModel.uri.fragment}`; + return `
`; + }; + const innerContent = document.createElement('div'); + innerContent.classList.add('walkThroughContent'); // only for markdown files + const markdown = this.expandMacros(content); + innerContent.innerHTML = marked(markdown, { renderer }); + this.content.appendChild(innerContent); + + model.snippets.forEach((snippet, i) => { + const model = snippet.textEditorModel; + const id = `snippet-${model.uri.fragment}`; + const div = innerContent.querySelector(`#${id.replace(/\./g, '\\.')}`) as HTMLElement; + + const options = this.getEditorOptions(snippet.textEditorModel.getModeId()); + const telemetryData = { + target: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined, + snippet: i + }; + const editor = this.instantiationService.createInstance(WalkThroughCodeEditor, div, options, telemetryData); + editor.setModel(model); + this.contentDisposables.push(editor); + + const updateHeight = (initial: boolean) => { + const lineHeight = editor.getConfiguration().lineHeight; + const height = `${Math.max(model.getLineCount() + 1, 4) * lineHeight}px`; + if (div.style.height !== height) { + div.style.height = height; + editor.layout(); + if (!initial) { + this.scrollbar.scanDomNode(); + } + } + }; + updateHeight(true); + this.contentDisposables.push(editor.onDidChangeModelContent(() => updateHeight(false))); + this.contentDisposables.push(editor.onDidChangeCursorPosition(e => { + const innerContent = this.content.firstElementChild; + if (innerContent) { + const targetTop = div.getBoundingClientRect().top; + const containerTop = innerContent.getBoundingClientRect().top; + const lineHeight = editor.getConfiguration().lineHeight; + const lineTop = (targetTop + (e.position.lineNumber - 1) * lineHeight) - containerTop; + const lineBottom = lineTop + lineHeight; + const scrollDimensions = this.scrollbar.getScrollDimensions(); + const scrollPosition = this.scrollbar.getScrollPosition(); + const scrollTop = scrollPosition.scrollTop; + const height = scrollDimensions.height; + if (scrollTop > lineTop) { + this.scrollbar.setScrollPosition({ scrollTop: lineTop }); + } else if (scrollTop < lineBottom - height) { + this.scrollbar.setScrollPosition({ scrollTop: lineBottom - height }); + } + } + })); + + this.contentDisposables.push(this.configurationService.onDidUpdateConfiguration(() => { + if (snippet.textEditorModel) { + editor.updateOptions(this.getEditorOptions(snippet.textEditorModel.getModeId())); + } + })); + + this.contentDisposables.push(once(editor.onMouseDown)(() => { + this.telemetryService.publicLog('walkThroughSnippetInteraction', { + from: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined, + type: 'mouseDown', + snippet: i + }); + })); + this.contentDisposables.push(once(editor.onKeyDown)(() => { + this.telemetryService.publicLog('walkThroughSnippetInteraction', { + from: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined, + type: 'keyDown', + snippet: i + }); + })); + this.contentDisposables.push(once(editor.onDidChangeModelContent)(() => { + this.telemetryService.publicLog('walkThroughSnippetInteraction', { + from: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined, + type: 'changeModelContent', + snippet: i + }); + })); + }); + this.updateSizeClasses(); + this.multiCursorModifier(); + this.contentDisposables.push(this.configurationService.onDidUpdateConfiguration(() => this.multiCursorModifier())); + if (input.onReady) { + input.onReady(innerContent); + } + this.scrollbar.scanDomNode(); + this.loadTextEditorViewState(input.getResource()); + this.updatedScrollPosition(); + }); + } + + private getEditorOptions(language: string): IEditorOptions { + const config = this.configurationService.getConfiguration('editor', { overrideIdentifier: language }); + return { + ...isObject(config) ? config : Object.create(null), + scrollBeyondLastLine: false, + scrollbar: { + verticalScrollbarSize: 14, + horizontal: 'auto', + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false + }, + overviewRulerLanes: 3, + fixedOverflowWidgets: true, + lineNumbersMinChars: 1, + minimap: { enabled: false }, + }; + } + + private expandMacros(input: string) { + return input.replace(/kb\(([a-z.\d\-]+)\)/gi, (match: string, kb: string) => { + const keybinding = this.keybindingService.lookupKeybinding(kb); + const shortcut = keybinding ? keybinding.getLabel() : UNBOUND_COMMAND; + return `${strings.escape(shortcut)}`; + }); + } + + private decorateContent() { + const keys = this.content.querySelectorAll('.shortcut[data-command]'); + Array.prototype.forEach.call(keys, (key: Element) => { + const command = key.getAttribute('data-command'); + const keybinding = command && this.keybindingService.lookupKeybinding(command); + const label = keybinding ? keybinding.getLabel() : UNBOUND_COMMAND; + while (key.firstChild) { + key.removeChild(key.firstChild); + } + key.appendChild(document.createTextNode(label)); + }); + const ifkeys = this.content.querySelectorAll('.if_shortcut[data-command]'); + Array.prototype.forEach.call(ifkeys, (key: HTMLElement) => { + const command = key.getAttribute('data-command'); + const keybinding = command && this.keybindingService.lookupKeybinding(command); + key.style.display = !keybinding ? 'none' : ''; + }); + } + + private multiCursorModifier() { + const labels = UILabelProvider.modifierLabels[OS]; + const setting = this.configurationService.lookup('editor.multiCursorModifier'); + const modifier = labels[setting.value === 'ctrlCmd' ? (OS === OperatingSystem.Macintosh ? 'metaKey' : 'ctrlKey') : 'altKey']; + const keys = this.content.querySelectorAll('.multi-cursor-modifier'); + Array.prototype.forEach.call(keys, (key: Element) => { + while (key.firstChild) { + key.removeChild(key.firstChild); + } + key.appendChild(document.createTextNode(modifier)); + }); + } + + private saveTextEditorViewState(resource: URI): void { + const memento = this.getMemento(this.storageService, Scope.WORKSPACE); + let editorViewStateMemento = memento[WALK_THROUGH_EDITOR_VIEW_STATE_PREFERENCE_KEY]; + if (!editorViewStateMemento) { + editorViewStateMemento = Object.create(null); + memento[WALK_THROUGH_EDITOR_VIEW_STATE_PREFERENCE_KEY] = editorViewStateMemento; + } + + const scrollPosition = this.scrollbar.getScrollPosition(); + const editorViewState: IWalkThroughEditorViewState = { + viewState: { + scrollTop: scrollPosition.scrollTop, + scrollLeft: scrollPosition.scrollLeft + } + }; + + let fileViewState: IWalkThroughEditorViewStates = editorViewStateMemento[resource.toString()]; + if (!fileViewState) { + fileViewState = Object.create(null); + editorViewStateMemento[resource.toString()] = fileViewState; + } + + if (typeof this.position === 'number') { + fileViewState[this.position] = editorViewState; + } + } + + private loadTextEditorViewState(resource: URI) { + const memento = this.getMemento(this.storageService, Scope.WORKSPACE); + const editorViewStateMemento = memento[WALK_THROUGH_EDITOR_VIEW_STATE_PREFERENCE_KEY]; + if (editorViewStateMemento) { + const fileViewState: IWalkThroughEditorViewStates = editorViewStateMemento[resource.toString()]; + if (fileViewState) { + const state: IWalkThroughEditorViewState = fileViewState[this.position]; + if (state) { + this.scrollbar.setScrollPosition(state.viewState); + } + } + } + } + + public clearInput(): void { + if (this.input instanceof WalkThroughInput) { + this.saveTextEditorViewState(this.input.getResource()); + } + super.clearInput(); + } + + public shutdown(): void { + if (this.input instanceof WalkThroughInput) { + this.saveTextEditorViewState(this.input.getResource()); + } + super.shutdown(); + } + + dispose(): void { + this.editorFocus.reset(); + this.contentDisposables = dispose(this.contentDisposables); + this.disposables = dispose(this.disposables); + super.dispose(); + } +} + +// theming + +const embeddedEditorBackground = registerColor('walkThrough.embeddedEditorBackground', { dark: null, light: null, hc: null }, localize('walkThrough.embeddedEditorBackground', 'Background color for the embedded editors on the Interactive Playground.')); + +registerThemingParticipant((theme, collector) => { + const color = getExtraColor(theme, embeddedEditorBackground, { dark: 'rgba(0, 0, 0, .4)', extra_dark: 'rgba(200, 235, 255, .064)', light: 'rgba(0,0,0,.08)', hc: null }); + if (color) { + collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent .monaco-editor-background, + .monaco-workbench > .part.editor > .content .walkThroughContent .margin-view-overlays { background: ${color}; }`); + } + const link = theme.getColor(textLinkForeground); + if (link) { + collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent a { color: ${link}; }`); + } + const activeLink = theme.getColor(textLinkActiveForeground); + if (activeLink) { + collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent a:hover, + .monaco-workbench > .part.editor > .content .walkThroughContent a:active { color: ${activeLink}; }`); + } + const focusColor = theme.getColor(focusBorder); + if (focusColor) { + collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent a:focus { outline-color: ${focusColor}; }`); + } + const shortcut = theme.getColor(textPreformatForeground); + if (shortcut) { + collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent code, + .monaco-workbench > .part.editor > .content .walkThroughContent .shortcut { color: ${shortcut}; }`); + } + const border = theme.getColor(contrastBorder); + if (border) { + collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent .monaco-editor { border-color: ${border}; }`); + } + const quoteBackground = theme.getColor(textBlockQuoteBackground); + if (quoteBackground) { + collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent blockquote { background: ${quoteBackground}; }`); + } + const quoteBorder = theme.getColor(textBlockQuoteBorder); + if (quoteBorder) { + collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent blockquote { border-color: ${quoteBorder}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts new file mode 100644 index 0000000000..ba296c7cb1 --- /dev/null +++ b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { marked } from 'vs/base/common/marked/marked'; +import { Schemas } from 'vs/base/common/network'; +import { IRawTextSource } from 'vs/editor/common/model/textSource'; + +export class WalkThroughContentProvider implements ITextModelContentProvider, IWorkbenchContribution { + + constructor( + @ITextModelService private textModelResolverService: ITextModelService, + @ITextFileService private textFileService: ITextFileService, + @IModeService private modeService: IModeService, + @IModelService private modelService: IModelService, + ) { + this.textModelResolverService.registerTextModelContentProvider(Schemas.walkThrough, this); + } + + public provideTextContent(resource: URI): TPromise { + const query = resource.query ? JSON.parse(resource.query) : {}; + const content: TPromise = (query.moduleId ? new TPromise((resolve, reject) => { + require([query.moduleId], content => { + try { + resolve(content.default()); + } catch (err) { + reject(err); + } + }); + }) : this.textFileService.resolveTextContent(URI.file(resource.fsPath)).then(content => content.value)); + return content.then(content => { + let codeEditorModel = this.modelService.getModel(resource); + if (!codeEditorModel) { + codeEditorModel = this.modelService.createModel(content, this.modeService.getOrCreateModeByFilenameOrFirstLine(resource.fsPath), resource); + } else { + this.modelService.updateModel(codeEditorModel, content); + } + + return codeEditorModel; + }); + } + + public getId(): string { + return 'vs.walkThroughContentProvider'; + } +} + +export class WalkThroughSnippetContentProvider implements ITextModelContentProvider, IWorkbenchContribution { + + constructor( + @ITextModelService private textModelResolverService: ITextModelService, + @ITextFileService private textFileService: ITextFileService, + @IModeService private modeService: IModeService, + @IModelService private modelService: IModelService, + ) { + this.textModelResolverService.registerTextModelContentProvider(Schemas.walkThroughSnippet, this); + } + + public provideTextContent(resource: URI): TPromise { + return this.textFileService.resolveTextContent(URI.file(resource.fsPath)).then(content => { + let codeEditorModel = this.modelService.getModel(resource); + if (!codeEditorModel) { + const j = parseInt(resource.fragment); + + let codeSnippet = ''; + let languageName = ''; + let i = 0; + const renderer = new marked.Renderer(); + renderer.code = (code, lang) => { + if (i++ === j) { + codeSnippet = code; + languageName = lang; + } + return ''; + }; + + const markdown = content.value.lines.join('\n'); + marked(markdown, { renderer }); + + const modeId = this.modeService.getModeIdForLanguageName(languageName); + const mode = this.modeService.getOrCreateMode(modeId); + codeEditorModel = this.modelService.createModel(codeSnippet, mode, resource); + } else { + this.modelService.updateModel(codeEditorModel, content.value); + } + + return codeEditorModel; + }); + } + + public getId(): string { + return 'vs.walkThroughSnippetContentProvider'; + } +} diff --git a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughInput.ts b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughInput.ts new file mode 100644 index 0000000000..467c28107e --- /dev/null +++ b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughInput.ts @@ -0,0 +1,186 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput, EditorModel, ITextEditorModel } from 'vs/workbench/common/editor'; +import URI from 'vs/base/common/uri'; +import { IReference, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { marked } from 'vs/base/common/marked/marked'; +import { Schemas } from 'vs/base/common/network'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; + +export class WalkThroughModel extends EditorModel { + + constructor( + private mainRef: IReference, + private snippetRefs: IReference[] + ) { + super(); + } + + get main() { + return this.mainRef.object; + } + + get snippets() { + return this.snippetRefs.map(snippet => snippet.object); + } + + dispose() { + this.snippetRefs.forEach(ref => ref.dispose()); + this.mainRef.dispose(); + super.dispose(); + } +} + +export interface WalkThroughInputOptions { + readonly typeId: string; + readonly name: string; + readonly description?: string; + readonly resource: URI; + readonly telemetryFrom: string; + readonly onReady?: (container: HTMLElement) => void; +} + +export class WalkThroughInput extends EditorInput { + + private disposables: IDisposable[] = []; + + private promise: TPromise; + + private resolveTime: number; + private maxTopScroll = 0; + private maxBottomScroll = 0; + + constructor( + private options: WalkThroughInputOptions, + @ITelemetryService private telemetryService: ITelemetryService, + @ILifecycleService lifecycleService: ILifecycleService, + @ITextModelService private textModelResolverService: ITextModelService + ) { + super(); + this.disposables.push(lifecycleService.onShutdown(e => this.disposeTelemetry(e))); + } + + getResource(): URI { + return this.options.resource; + } + + getTypeId(): string { + return this.options.typeId; + } + + getName(): string { + return this.options.name; + } + + getDescription(): string { + return this.options.description || ''; + } + + getTelemetryFrom(): string { + return this.options.telemetryFrom; + } + + getTelemetryDescriptor(): object { + const descriptor = super.getTelemetryDescriptor(); + descriptor['target'] = this.getTelemetryFrom(); + descriptor['resource'] = telemetryURIDescriptor(this.options.resource); + return descriptor; + } + + get onReady() { + return this.options.onReady; + } + + resolve(refresh?: boolean): TPromise { + if (!this.promise) { + this.resolveTelemetry(); + this.promise = this.textModelResolverService.createModelReference(this.options.resource) + .then(ref => { + if (strings.endsWith(this.getResource().path, '.html')) { + return new WalkThroughModel(ref, []); + } + + const snippets: TPromise>[] = []; + let i = 0; + const renderer = new marked.Renderer(); + renderer.code = (code, lang) => { + const resource = this.options.resource.with({ scheme: Schemas.walkThroughSnippet, fragment: `${i++}.${lang}` }); + snippets.push(this.textModelResolverService.createModelReference(resource)); + return ''; + }; + + const markdown = ref.object.textEditorModel.getLinesContent().join('\n'); + marked(markdown, { renderer }); + + return TPromise.join(snippets) + .then(refs => new WalkThroughModel(ref, refs)); + }); + } + + return this.promise; + } + + matches(otherInput: any): boolean { + if (super.matches(otherInput) === true) { + return true; + } + + if (otherInput instanceof WalkThroughInput) { + let otherResourceEditorInput = otherInput; + + // Compare by properties + return otherResourceEditorInput.options.resource.toString() === this.options.resource.toString(); + } + + return false; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + + if (this.promise) { + this.promise.then(model => model.dispose()); + this.promise = null; + } + + this.disposeTelemetry(); + + super.dispose(); + } + + public relativeScrollPosition(topScroll: number, bottomScroll: number) { + this.maxTopScroll = Math.max(this.maxTopScroll, topScroll); + this.maxBottomScroll = Math.max(this.maxBottomScroll, bottomScroll); + } + + private resolveTelemetry() { + if (!this.resolveTime) { + this.resolveTime = Date.now(); + this.telemetryService.publicLog('resolvingInput', { + target: this.getTelemetryFrom(), + }); + } + } + + private disposeTelemetry(reason?: ShutdownReason) { + if (this.resolveTime) { + this.telemetryService.publicLog('disposingInput', { + target: this.getTelemetryFrom(), + timeSpent: (Date.now() - this.resolveTime) / 60, + reason: reason ? ShutdownReason[reason] : 'DISPOSE', + maxTopScroll: this.maxTopScroll, + maxBottomScroll: this.maxBottomScroll, + }); + this.resolveTime = null; + } + } +} diff --git a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils.ts b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils.ts new file mode 100644 index 0000000000..8ed102c7d9 --- /dev/null +++ b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ITheme } from 'vs/platform/theme/common/themeService'; +import { editorBackground, ColorDefaults, ColorValue } from 'vs/platform/theme/common/colorRegistry'; + +export function getExtraColor(theme: ITheme, colorId: string, defaults: ColorDefaults & { extra_dark: string }): ColorValue { + const color = theme.getColor(colorId); + if (color) { + return color; + } + + if (theme.type === 'dark') { + const background = theme.getColor(editorBackground); + if (background && background.getRelativeLuminance() < 0.004) { + return defaults.extra_dark; + } + } + + return defaults[theme.type]; +} \ No newline at end of file diff --git a/src/vs/workbench/services/activity/common/activityBarService.ts b/src/vs/workbench/services/activity/common/activityBarService.ts new file mode 100644 index 0000000000..e70f9f267e --- /dev/null +++ b/src/vs/workbench/services/activity/common/activityBarService.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export interface IBadge { + getDescription(): string; +} + +export class BaseBadge implements IBadge { + public descriptorFn: (args: any) => string; + + constructor(descriptorFn: (args: any) => string) { + this.descriptorFn = descriptorFn; + } + + public getDescription(): string { + return this.descriptorFn(null); + } +} + +export class NumberBadge extends BaseBadge { + public number: number; + + constructor(number: number, descriptorFn: (args: any) => string) { + super(descriptorFn); + + this.number = number; + } + + public getDescription(): string { + return this.descriptorFn(this.number); + } +} + +export class TextBadge extends BaseBadge { + public text: string; + + constructor(text: string, descriptorFn: (args: any) => string) { + super(descriptorFn); + + this.text = text; + } +} + +export class IconBadge extends BaseBadge { + + constructor(descriptorFn: (args: any) => string) { + super(descriptorFn); + } +} + +export class ProgressBadge extends BaseBadge { +} + +export const IActivityBarService = createDecorator('activityBarService'); + +export interface IActivityBarService { + _serviceBrand: any; + + /** + * Show activity in the activitybar for the given global activity. + */ + showGlobalActivity(globalActivityId: string, badge: IBadge): IDisposable; + + /** + * Show activity in the activitybar for the given viewlet. + */ + showActivity(viewletId: string, badge: IBadge, clazz?: string): IDisposable; + + /** + * Unpins a viewlet from the activitybar. + */ + unpin(viewletId: string): void; + + /** + * Pin a viewlet inside the activity bar. + */ + pin(viewletId: string): void; + + /** + * Find out if a viewlet is pinned in the activity bar. + */ + isPinned(viewletId: string): boolean; + + /** + * Reorder viewlet ordering by moving a viewlet to the location of another viewlet. + */ + move(viewletId: string, toViewletId: string): void; +} diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts new file mode 100644 index 0000000000..b34bf8a75c --- /dev/null +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import Uri from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IResolveContentOptions, IUpdateContentOptions } from 'vs/platform/files/common/files'; +import { IRawTextSource } from 'vs/editor/common/model/textSource'; + +export const IBackupFileService = createDecorator('backupFileService'); + +export const BACKUP_FILE_RESOLVE_OPTIONS: IResolveContentOptions = { acceptTextOnly: true, encoding: 'utf-8' }; +export const BACKUP_FILE_UPDATE_OPTIONS: IUpdateContentOptions = { encoding: 'utf-8' }; + +/** + * A service that handles any I/O and state associated with the backup system. + */ +export interface IBackupFileService { + _serviceBrand: any; + + /** + * If backups are enabled. + */ + backupEnabled: boolean; + + /** + * Finds out if there are any backups stored. + */ + hasBackups(): TPromise; + + /** + * Loads the backup resource for a particular resource within the current workspace. + * + * @param resource The resource that is backed up. + * @return The backup resource if any. + */ + loadBackupResource(resource: Uri): TPromise; + + /** + * Backs up a resource. + * + * @param resource The resource to back up. + * @param content The content of the resource. + * @param versionId The version id of the resource to backup. + */ + backupResource(resource: Uri, content: string, versionId?: number): TPromise; + + /** + * Gets a list of file backups for the current workspace. + * + * @return The list of backups. + */ + getWorkspaceFileBackups(): TPromise; + + /** + * Parses backup raw text content into the content, removing the metadata that is also stored + * in the file. + * + * @param rawText The IRawTextProvider from a backup resource. + * @return The backup file's backed up content. + */ + parseBackupContent(textSource: IRawTextSource): string; + + /** + * Discards the backup associated with a resource if it exists.. + * + * @param resource The resource whose backup is being discarded discard to back up. + */ + discardResourceBackup(resource: Uri): TPromise; + + /** + * Discards all backups associated with the current workspace and prevents further backups from + * being made. + */ + discardAllWorkspaceBackups(): TPromise; +} diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts new file mode 100644 index 0000000000..9055911cbd --- /dev/null +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -0,0 +1,268 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as path from 'path'; +import * as crypto from 'crypto'; +import * as platform from 'vs/base/common/platform'; +import pfs = require('vs/base/node/pfs'); +import Uri from 'vs/base/common/uri'; +import { Queue } from 'vs/base/common/async'; +import { IBackupFileService, BACKUP_FILE_UPDATE_OPTIONS } from 'vs/workbench/services/backup/common/backup'; +import { IFileService } from 'vs/platform/files/common/files'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { readToMatchingString } from 'vs/base/node/stream'; +import { TextSource, IRawTextSource } from 'vs/editor/common/model/textSource'; +import { DefaultEndOfLine } from 'vs/editor/common/editorCommon'; + +export interface IBackupFilesModel { + resolve(backupRoot: string): TPromise; + + add(resource: Uri, versionId?: number): void; + has(resource: Uri, versionId?: number): boolean; + get(): Uri[]; + remove(resource: Uri): void; + count(): number; + clear(): void; +} + +export class BackupFilesModel implements IBackupFilesModel { + private cache: { [resource: string]: number /* version ID */ } = Object.create(null); + + public resolve(backupRoot: string): TPromise { + return pfs.readDirsInDir(backupRoot).then(backupSchemas => { + + // For all supported schemas + return TPromise.join(backupSchemas.map(backupSchema => { + + // Read backup directory for backups + const backupSchemaPath = path.join(backupRoot, backupSchema); + return pfs.readdir(backupSchemaPath).then(backupHashes => { + + // Remember known backups in our caches + backupHashes.forEach(backupHash => { + const backupResource = Uri.file(path.join(backupSchemaPath, backupHash)); + this.add(backupResource); + }); + }); + })); + }).then(() => this, error => this); + } + + public add(resource: Uri, versionId = 0): void { + this.cache[resource.toString()] = versionId; + } + + public count(): number { + return Object.keys(this.cache).length; + } + + public has(resource: Uri, versionId?: number): boolean { + const cachedVersionId = this.cache[resource.toString()]; + if (typeof cachedVersionId !== 'number') { + return false; // unknown resource + } + + if (typeof versionId === 'number') { + return versionId === cachedVersionId; // if we are asked with a specific version ID, make sure to test for it + } + + return true; + } + + public get(): Uri[] { + return Object.keys(this.cache).map(k => Uri.parse(k)); + } + + public remove(resource: Uri): void { + delete this.cache[resource.toString()]; + } + + public clear(): void { + this.cache = Object.create(null); + } +} + +export class BackupFileService implements IBackupFileService { + + public _serviceBrand: any; + + private static readonly META_MARKER = '\n'; + + private isShuttingDown: boolean; + private ready: TPromise; + /** + * Ensure IO operations on individual files are performed in order, this could otherwise lead + * to unexpected behavior when backups are persisted and discarded in the wrong order. + */ + private ioOperationQueues: { [path: string]: Queue }; + + constructor( + private backupWorkspacePath: string, + @IFileService private fileService: IFileService + ) { + this.isShuttingDown = false; + this.ioOperationQueues = {}; + this.ready = this.init(); + } + + public get backupEnabled(): boolean { + return !!this.backupWorkspacePath; // Hot exit requires a backup path + } + + private init(): TPromise { + const model = new BackupFilesModel(); + + if (!this.backupEnabled) { + return TPromise.as(model); + } + + return model.resolve(this.backupWorkspacePath); + } + + public hasBackups(): TPromise { + return this.ready.then(model => { + return model.count() > 0; + }); + } + + public loadBackupResource(resource: Uri): TPromise { + return this.ready.then(model => { + const backupResource = this.getBackupResource(resource); + if (!backupResource) { + return void 0; + } + + // Return directly if we have a known backup with that resource + if (model.has(backupResource)) { + return backupResource; + } + + // Otherwise: on Windows and Mac pre v1.11 we used to store backups in lowercase format + // Therefor we also want to check if we have backups of this old format hanging around + // TODO@Ben migration + if (platform.isWindows || platform.isMacintosh) { + const legacyBackupResource = this.getBackupResource(resource, true /* legacyMacWindowsFormat */); + if (model.has(legacyBackupResource)) { + return legacyBackupResource; + } + } + + return void 0; + }); + } + + public backupResource(resource: Uri, content: string, versionId?: number): TPromise { + if (this.isShuttingDown) { + return TPromise.as(void 0); + } + + return this.ready.then(model => { + const backupResource = this.getBackupResource(resource); + if (!backupResource) { + return void 0; + } + + if (model.has(backupResource, versionId)) { + return void 0; // return early if backup version id matches requested one + } + + // Add metadata to top of file + content = `${resource.toString()}${BackupFileService.META_MARKER}${content}`; + + return this.getResourceIOQueue(backupResource).queue(() => { + return this.fileService.updateContent(backupResource, content, BACKUP_FILE_UPDATE_OPTIONS).then(() => model.add(backupResource, versionId)); + }); + }); + } + + public discardResourceBackup(resource: Uri): TPromise { + return this.ready.then(model => { + const backupResource = this.getBackupResource(resource); + if (!backupResource) { + return void 0; + } + + return this.getResourceIOQueue(backupResource).queue(() => { + return pfs.del(backupResource.fsPath).then(() => model.remove(backupResource)); + }).then(() => { + + // On Windows and Mac pre v1.11 we used to store backups in lowercase format + // Therefor we also want to check if we have backups of this old format laying around + // TODO@Ben migration + if (platform.isWindows || platform.isMacintosh) { + const legacyBackupResource = this.getBackupResource(resource, true /* legacyMacWindowsFormat */); + if (model.has(legacyBackupResource)) { + return this.getResourceIOQueue(legacyBackupResource).queue(() => { + return pfs.del(legacyBackupResource.fsPath).then(() => model.remove(legacyBackupResource)); + }); + } + } + + return TPromise.as(void 0); + }); + }); + } + + private getResourceIOQueue(resource: Uri) { + const key = resource.toString(); + if (!this.ioOperationQueues[key]) { + const queue = new Queue(); + queue.onFinished(() => { + queue.dispose(); + delete this.ioOperationQueues[key]; + }); + this.ioOperationQueues[key] = queue; + } + return this.ioOperationQueues[key]; + } + + public discardAllWorkspaceBackups(): TPromise { + this.isShuttingDown = true; + + return this.ready.then(model => { + if (!this.backupEnabled) { + return void 0; + } + + return pfs.del(this.backupWorkspacePath).then(() => model.clear()); + }); + } + + public getWorkspaceFileBackups(): TPromise { + return this.ready.then(model => { + const readPromises: TPromise[] = []; + + model.get().forEach(fileBackup => { + readPromises.push( + readToMatchingString(fileBackup.fsPath, BackupFileService.META_MARKER, 2000, 10000) + .then(Uri.parse) + ); + }); + + return TPromise.join(readPromises); + }); + } + + public parseBackupContent(rawTextSource: IRawTextSource): string { + const textSource = TextSource.fromRawTextSource(rawTextSource, DefaultEndOfLine.LF); + return textSource.lines.slice(1).join(textSource.EOL); // The first line of a backup text file is the file name + } + + protected getBackupResource(resource: Uri, legacyMacWindowsFormat?: boolean): Uri { + if (!this.backupEnabled) { + return null; + } + + return Uri.file(path.join(this.backupWorkspacePath, resource.scheme, this.hashPath(resource, legacyMacWindowsFormat))); + } + + private hashPath(resource: Uri, legacyMacWindowsFormat?: boolean): string { + const caseAwarePath = legacyMacWindowsFormat ? resource.fsPath.toLowerCase() : resource.fsPath; + + return crypto.createHash('md5').update(caseAwarePath).digest('hex'); + } +} diff --git a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts new file mode 100644 index 0000000000..8529c9bbbc --- /dev/null +++ b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts @@ -0,0 +1,382 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as platform from 'vs/base/common/platform'; +import crypto = require('crypto'); +import os = require('os'); +import fs = require('fs'); +import path = require('path'); +import extfs = require('vs/base/node/extfs'); +import pfs = require('vs/base/node/pfs'); +import Uri from 'vs/base/common/uri'; +import { BackupFileService, BackupFilesModel } from 'vs/workbench/services/backup/node/backupFileService'; +import { FileService } from 'vs/workbench/services/files/node/fileService'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { parseArgs } from 'vs/platform/environment/node/argv'; +import { RawTextSource } from 'vs/editor/common/model/textSource'; +import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { Workspace } from 'vs/platform/workspace/common/workspace'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; + +class TestEnvironmentService extends EnvironmentService { + + constructor(private _backupHome: string, private _backupWorkspacesPath: string) { + super(parseArgs(process.argv), process.execPath); + } + + get backupHome(): string { return this._backupHome; } + + get backupWorkspacesPath(): string { return this._backupWorkspacesPath; } +} + +const parentDir = path.join(os.tmpdir(), 'vsctests', 'service'); +const backupHome = path.join(parentDir, 'Backups'); +const workspacesJsonPath = path.join(backupHome, 'workspaces.json'); + +const workspaceResource = Uri.file(platform.isWindows ? 'c:\\workspace' : '/workspace'); +const workspaceBackupPath = path.join(backupHome, crypto.createHash('md5').update(workspaceResource.fsPath).digest('hex')); +const fooFile = Uri.file(platform.isWindows ? 'c:\\Foo' : '/Foo'); +const barFile = Uri.file(platform.isWindows ? 'c:\\Bar' : '/Bar'); +const untitledFile = Uri.from({ scheme: 'untitled', path: 'Untitled-1' }); +const fooBackupPath = path.join(workspaceBackupPath, 'file', crypto.createHash('md5').update(fooFile.fsPath).digest('hex')); +const fooBackupPathLegacy = path.join(workspaceBackupPath, 'file', crypto.createHash('md5').update(fooFile.fsPath.toLowerCase()).digest('hex')); +const barBackupPath = path.join(workspaceBackupPath, 'file', crypto.createHash('md5').update(barFile.fsPath).digest('hex')); +const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', crypto.createHash('md5').update(untitledFile.fsPath).digest('hex')); + +class TestBackupFileService extends BackupFileService { + constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) { + const fileService = new FileService(new TestContextService(new Workspace(workspace.fsPath, workspace.fsPath, [workspace])), new TestConfigurationService(), { disableWatcher: true }); + + super(workspaceBackupPath, fileService); + } + + public getBackupResource(resource: Uri, legacyMacWindowsFormat?: boolean): Uri { + return super.getBackupResource(resource, legacyMacWindowsFormat); + } +} + +suite('BackupFileService', () => { + let service: TestBackupFileService; + + setup(done => { + service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); + + // Delete any existing backups completely and then re-create it. + extfs.del(backupHome, os.tmpdir(), () => { + pfs.mkdirp(backupHome).then(() => { + pfs.writeFile(workspacesJsonPath, '').then(() => { + done(); + }); + }); + }); + }); + + teardown(done => { + extfs.del(backupHome, os.tmpdir(), done); + }); + + suite('getBackupResource', () => { + test('should get the correct backup path for text files', () => { + // Format should be: /// + const backupResource = fooFile; + const workspaceHash = crypto.createHash('md5').update(workspaceResource.fsPath).digest('hex'); + const filePathHash = crypto.createHash('md5').update(backupResource.fsPath).digest('hex'); + const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'file', filePathHash)).fsPath; + assert.equal(service.getBackupResource(backupResource).fsPath, expectedPath); + }); + + test('should get the correct backup path for untitled files', () => { + // Format should be: /// + const backupResource = Uri.from({ scheme: 'untitled', path: 'Untitled-1' }); + const workspaceHash = crypto.createHash('md5').update(workspaceResource.fsPath).digest('hex'); + const filePathHash = crypto.createHash('md5').update(backupResource.fsPath).digest('hex'); + const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'untitled', filePathHash)).fsPath; + assert.equal(service.getBackupResource(backupResource).fsPath, expectedPath); + }); + }); + + suite('loadBackupResource', () => { + test('should return whether a backup resource exists', done => { + pfs.mkdirp(path.dirname(fooBackupPath)).then(() => { + fs.writeFileSync(fooBackupPath, 'foo'); + service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); + service.loadBackupResource(fooFile).then(resource => { + assert.ok(resource); + assert.equal(path.basename(resource.fsPath), path.basename(fooBackupPath)); + return service.hasBackups().then(hasBackups => { + assert.ok(hasBackups); + done(); + }); + }); + }); + }); + + test('should return whether a backup resource exists - legacy support (read old lowercase format as fallback)', done => { + if (platform.isLinux) { + done(); + return; // only on mac and windows + } + + pfs.mkdirp(path.dirname(fooBackupPath)).then(() => { + fs.writeFileSync(fooBackupPathLegacy, 'foo'); + service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); + service.loadBackupResource(fooFile).then(resource => { + assert.ok(resource); + assert.equal(path.basename(resource.fsPath), path.basename(fooBackupPathLegacy)); + return service.hasBackups().then(hasBackups => { + assert.ok(hasBackups); + done(); + }); + }); + }); + }); + + test('should return whether a backup resource exists - legacy support #2 (both cases present, return case sensitive backup)', done => { + if (platform.isLinux) { + done(); + return; // only on mac and windows + } + + pfs.mkdirp(path.dirname(fooBackupPath)).then(() => { + fs.writeFileSync(fooBackupPath, 'foo'); + fs.writeFileSync(fooBackupPathLegacy, 'foo'); + service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); + service.loadBackupResource(fooFile).then(resource => { + assert.ok(resource); + assert.equal(path.basename(resource.fsPath), path.basename(fooBackupPath)); + return service.hasBackups().then(hasBackups => { + assert.ok(hasBackups); + done(); + }); + }); + }); + }); + }); + + suite('backupResource', () => { + test('text file', function (done: () => void) { + service.backupResource(fooFile, 'test').then(() => { + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.equal(fs.existsSync(fooBackupPath), true); + assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); + done(); + }); + }); + + test('untitled file', function (done: () => void) { + service.backupResource(untitledFile, 'test').then(() => { + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + assert.equal(fs.existsSync(untitledBackupPath), true); + assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); + done(); + }); + }); + }); + + suite('discardResourceBackup', () => { + test('text file', function (done: () => void) { + service.backupResource(fooFile, 'test').then(() => { + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + service.discardResourceBackup(fooFile).then(() => { + assert.equal(fs.existsSync(fooBackupPath), false); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0); + done(); + }); + }); + }); + + test('untitled file', function (done: () => void) { + service.backupResource(untitledFile, 'test').then(() => { + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + service.discardResourceBackup(untitledFile).then(() => { + assert.equal(fs.existsSync(untitledBackupPath), false); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 0); + done(); + }); + }); + }); + + test('text file - legacy support (dicard lowercase backup file if present)', done => { + if (platform.isLinux) { + done(); + return; // only on mac and windows + } + + pfs.mkdirp(path.dirname(fooBackupPath)).then(() => { + fs.writeFileSync(fooBackupPathLegacy, 'foo'); + service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); + service.backupResource(fooFile, 'test').then(() => { + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2); + service.discardResourceBackup(fooFile).then(() => { + assert.equal(fs.existsSync(fooBackupPath), false); + assert.equal(fs.existsSync(fooBackupPathLegacy), false); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0); + done(); + }); + }); + }); + }); + }); + + suite('discardAllWorkspaceBackups', () => { + test('text file', function (done: () => void) { + service.backupResource(fooFile, 'test').then(() => { + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + service.backupResource(barFile, 'test').then(() => { + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2); + service.discardAllWorkspaceBackups().then(() => { + assert.equal(fs.existsSync(fooBackupPath), false); + assert.equal(fs.existsSync(barBackupPath), false); + assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false); + done(); + }); + }); + }); + }); + + test('untitled file', function (done: () => void) { + service.backupResource(untitledFile, 'test').then(() => { + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + service.discardAllWorkspaceBackups().then(() => { + assert.equal(fs.existsSync(untitledBackupPath), false); + assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'untitled')), false); + done(); + }); + }); + }); + + test('should disable further backups', function (done: () => void) { + service.discardAllWorkspaceBackups().then(() => { + service.backupResource(untitledFile, 'test').then(() => { + assert.equal(fs.existsSync(workspaceBackupPath), false); + done(); + }); + }); + }); + }); + + suite('getWorkspaceFileBackups', () => { + test('("file") - text file', done => { + service.backupResource(fooFile, `test`).then(() => { + service.getWorkspaceFileBackups().then(textFiles => { + assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath]); + service.backupResource(barFile, `test`).then(() => { + service.getWorkspaceFileBackups().then(textFiles => { + assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath, barFile.fsPath]); + done(); + }); + }); + }); + }); + }); + + test('("file") - untitled file', done => { + service.backupResource(untitledFile, `test`).then(() => { + service.getWorkspaceFileBackups().then(textFiles => { + assert.deepEqual(textFiles.map(f => f.fsPath), [untitledFile.fsPath]); + done(); + }); + }); + }); + + test('("untitled") - untitled file', done => { + service.backupResource(untitledFile, `test`).then(() => { + service.getWorkspaceFileBackups().then(textFiles => { + assert.deepEqual(textFiles.map(f => f.fsPath), ['Untitled-1']); + done(); + }); + }); + }); + }); + + test('parseBackupContent', () => { + test('should separate metadata from content', () => { + const textSource = RawTextSource.fromString('metadata\ncontent'); + assert.equal(service.parseBackupContent(textSource), 'content'); + }); + }); +}); + +suite('BackupFilesModel', () => { + test('simple', () => { + const model = new BackupFilesModel(); + + const resource1 = Uri.file('test.html'); + + assert.equal(model.has(resource1), false); + + model.add(resource1); + + assert.equal(model.has(resource1), true); + assert.equal(model.has(resource1, 0), true); + assert.equal(model.has(resource1, 1), false); + + model.remove(resource1); + + assert.equal(model.has(resource1), false); + + model.add(resource1); + + assert.equal(model.has(resource1), true); + assert.equal(model.has(resource1, 0), true); + assert.equal(model.has(resource1, 1), false); + + model.clear(); + + assert.equal(model.has(resource1), false); + + model.add(resource1, 1); + + assert.equal(model.has(resource1), true); + assert.equal(model.has(resource1, 0), false); + assert.equal(model.has(resource1, 1), true); + + const resource2 = Uri.file('test1.html'); + const resource3 = Uri.file('test2.html'); + const resource4 = Uri.file('test3.html'); + + model.add(resource2); + model.add(resource3); + model.add(resource4); + + assert.equal(model.has(resource1), true); + assert.equal(model.has(resource2), true); + assert.equal(model.has(resource3), true); + assert.equal(model.has(resource4), true); + }); + + test('resolve', (done) => { + pfs.mkdirp(path.dirname(fooBackupPath)).then(() => { + fs.writeFileSync(fooBackupPath, 'foo'); + + const model = new BackupFilesModel(); + + model.resolve(workspaceBackupPath).then(model => { + assert.equal(model.has(Uri.file(fooBackupPath)), true); + + done(); + }); + }); + }); + + test('get', () => { + const model = new BackupFilesModel(); + + assert.deepEqual(model.get(), []); + + const file1 = Uri.file('/root/file/foo.html'); + const file2 = Uri.file('/root/file/bar.html'); + const untitled = Uri.file('/root/untitled/bar.html'); + + model.add(file1); + model.add(file2); + model.add(untitled); + + assert.deepEqual(model.get().map(f => f.fsPath), [file1.fsPath, file2.fsPath, untitled.fsPath]); + }); +}); diff --git a/src/vs/workbench/services/configuration/common/configuration.ts b/src/vs/workbench/services/configuration/common/configuration.ts new file mode 100644 index 0000000000..646f798172 --- /dev/null +++ b/src/vs/workbench/services/configuration/common/configuration.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const CONFIG_DEFAULT_NAME = 'settings'; + +// {{SQL CARBON EDIT}} +export const WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME = '.sqlops'; +export const WORKSPACE_CONFIG_DEFAULT_PATH = `${WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME}/${CONFIG_DEFAULT_NAME}.json`; + +export const IWorkspaceConfigurationService = createDecorator('configurationService'); + +export interface IWorkspaceConfigurationService extends IConfigurationService { + + /** + * Returns untrusted configuration keys for the current workspace. + */ + getUnsupportedWorkspaceKeys(): string[]; + +} + +export const WORKSPACE_STANDALONE_CONFIGURATIONS = { + 'tasks': `${WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME}/tasks.json`, + 'launch': `${WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME}/launch.json` +}; \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/common/configurationEditing.ts b/src/vs/workbench/services/configuration/common/configurationEditing.ts new file mode 100644 index 0000000000..6cdaff652c --- /dev/null +++ b/src/vs/workbench/services/configuration/common/configurationEditing.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; + +export const IConfigurationEditingService = createDecorator('configurationEditingService'); + +export enum ConfigurationEditingErrorCode { + + /** + * Error when trying to write a configuration key that is not registered. + */ + ERROR_UNKNOWN_KEY, + + /** + * Error when trying to write an invalid folder configuration key to folder settings. + */ + ERROR_INVALID_FOLDER_CONFIGURATION, + + /** + * Error when trying to write to user target but not supported for provided key. + */ + ERROR_INVALID_USER_TARGET, + + /** + * Error when trying to write a configuration key to folder target + */ + ERROR_INVALID_FOLDER_TARGET, + + /** + * Error when trying to write to the workspace configuration without having a workspace opened. + */ + ERROR_NO_WORKSPACE_OPENED, + + /** + * Error when trying to write and save to the configuration file while it is dirty in the editor. + */ + ERROR_CONFIGURATION_FILE_DIRTY, + + /** + * Error when trying to write to a configuration file that contains JSON errors. + */ + ERROR_INVALID_CONFIGURATION +} + +export class ConfigurationEditingError extends Error { + constructor(message: string, public code: ConfigurationEditingErrorCode) { + super(message); + } +} + +export enum ConfigurationTarget { + + /** + * Targets the user configuration file for writing. + */ + USER, + + /** + * Targets the workspace configuration file for writing. This only works if a workspace is opened. + */ + WORKSPACE, + + /** + * Targets the folder configuration file for writing. This only works if a workspace is opened. + */ + FOLDER +} + +export interface IConfigurationValue { + key: string; + value: any; +} + +export interface IConfigurationEditingOptions { + /** + * If `true`, do not saves the configuration. Default is `false`. + */ + donotSave?: boolean; + /** + * If `true`, do not notifies the error to user by showing the message box. Default is `false`. + */ + donotNotifyError?: boolean; + /** + * Scope of configuration to be written into. + */ + scopes?: IConfigurationOverrides; +} + +export interface IConfigurationEditingService { + + _serviceBrand: ServiceIdentifier; + + /** + * Allows to write the configuration value to either the user or workspace configuration file and save it if asked to save. + * The returned promise will be in error state in any of the error cases from [ConfigurationEditingErrorCode](#ConfigurationEditingErrorCode) + */ + writeConfiguration(target: ConfigurationTarget, value: IConfigurationValue, options?: IConfigurationEditingOptions): TPromise; +} \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts new file mode 100644 index 0000000000..b61ed56976 --- /dev/null +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -0,0 +1,181 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { clone } from 'vs/base/common/objects'; +import { CustomConfigurationModel, toValuesTree } from 'vs/platform/configuration/common/model'; +import { ConfigurationModel } from 'vs/platform/configuration/common/configuration'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, IConfigurationPropertySchema, Extensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration'; +import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; + +export class WorkspaceConfigurationModel extends CustomConfigurationModel { + + private _raw: T; + private _folders: IStoredWorkspaceFolder[]; + private _worksapaceSettings: ConfigurationModel; + private _tasksConfiguration: ConfigurationModel; + private _launchConfiguration: ConfigurationModel; + private _workspaceConfiguration: ConfigurationModel; + + public update(content: string): void { + super.update(content); + this._worksapaceSettings = new ConfigurationModel(this._worksapaceSettings.contents, this._worksapaceSettings.keys, this.overrides); + this._workspaceConfiguration = this.consolidate(); + } + + get folders(): IStoredWorkspaceFolder[] { + return this._folders; + } + + get workspaceConfiguration(): ConfigurationModel { + return this._workspaceConfiguration; + } + + protected processRaw(raw: T): void { + this._raw = raw; + + this._folders = (this._raw['folders'] || []) as IStoredWorkspaceFolder[]; + this._worksapaceSettings = this.parseConfigurationModel('settings'); + this._tasksConfiguration = this.parseConfigurationModel('tasks'); + this._launchConfiguration = this.parseConfigurationModel('launch'); + + super.processRaw(raw); + } + + private parseConfigurationModel(section: string): ConfigurationModel { + const rawSection = this._raw[section] || {}; + const contents = toValuesTree(rawSection, message => console.error(`Conflict in section '${section}' of workspace configuration file ${message}`)); + return new ConfigurationModel(contents, Object.keys(rawSection)); + } + + private consolidate(): ConfigurationModel { + const keys: string[] = [...this._worksapaceSettings.keys, + ...this._tasksConfiguration.keys.map(key => `tasks.${key}`), + ...this._launchConfiguration.keys.map(key => `launch.${key}`)]; + + const mergedContents = new ConfigurationModel({}, keys) + .merge(this._worksapaceSettings) + .merge(this._tasksConfiguration) + .merge(this._launchConfiguration); + + return new ConfigurationModel(mergedContents.contents, keys, mergedContents.overrides); + } +} + +export class ScopedConfigurationModel extends CustomConfigurationModel { + + constructor(content: string, name: string, public readonly scope: string) { + super(null, name); + this.update(content); + } + + public update(content: string): void { + super.update(content); + const contents = Object.create(null); + contents[this.scope] = this.contents; + this._contents = contents; + } + +} + +export class FolderSettingsModel extends CustomConfigurationModel { + + private _raw: T; + private _unsupportedKeys: string[]; + + protected processRaw(raw: T): void { + this._raw = raw; + const processedRaw = {}; + this._unsupportedKeys = []; + const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + for (let key in raw) { + if (this.isNotExecutable(key, configurationProperties)) { + processedRaw[key] = raw[key]; + } else { + this._unsupportedKeys.push(key); + } + } + return super.processRaw(processedRaw); + } + + public reprocess(): void { + this.processRaw(this._raw); + } + + public get unsupportedKeys(): string[] { + return this._unsupportedKeys || []; + } + + private isNotExecutable(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): boolean { + const propertySchema = configurationProperties[key]; + if (!propertySchema) { + return true; // Unknown propertis are ignored from checks + } + return !propertySchema.isExecutable; + } + + public createWorkspaceConfigurationModel(): ConfigurationModel { + return this.createScopedConfigurationModel(ConfigurationScope.WINDOW); + } + + public createFolderScopedConfigurationModel(): ConfigurationModel { + return this.createScopedConfigurationModel(ConfigurationScope.RESOURCE); + } + + private createScopedConfigurationModel(scope: ConfigurationScope): ConfigurationModel { + const workspaceRaw = {}; + const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + for (let key in this._raw) { + if (this.getScope(key, configurationProperties) === scope) { + workspaceRaw[key] = this._raw[key]; + } + } + const workspaceContents = toValuesTree(workspaceRaw, message => console.error(`Conflict in workspace settings file: ${message}`)); + const workspaceKeys = Object.keys(workspaceRaw); + return new ConfigurationModel(workspaceContents, workspaceKeys, clone(this._overrides)); + } + + private getScope(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): ConfigurationScope { + const propertySchema = configurationProperties[key]; + return propertySchema ? propertySchema.scope : ConfigurationScope.WINDOW; + } +} + +export class FolderConfigurationModel extends CustomConfigurationModel { + + constructor(public readonly workspaceSettingsConfig: FolderSettingsModel, private scopedConfigs: ScopedConfigurationModel[], private scope: ConfigurationScope) { + super(); + this.consolidate(); + } + + private consolidate(): void { + this._contents = {}; + this._overrides = []; + + this.doMerge(this, ConfigurationScope.WINDOW === this.scope ? this.workspaceSettingsConfig : this.workspaceSettingsConfig.createFolderScopedConfigurationModel()); + for (const configModel of this.scopedConfigs) { + this.doMerge(this, configModel); + } + } + + public get keys(): string[] { + const keys: string[] = [...this.workspaceSettingsConfig.keys]; + this.scopedConfigs.forEach(scopedConfigModel => { + Object.keys(WORKSPACE_STANDALONE_CONFIGURATIONS).forEach(scope => { + if (scopedConfigModel.scope === scope) { + keys.push(...scopedConfigModel.keys.map(key => `${scope}.${key}`)); + } + }); + }); + return keys; + } + + public update(): void { + this.workspaceSettingsConfig.reprocess(); + this.consolidate(); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/common/jsonEditing.ts b/src/vs/workbench/services/configuration/common/jsonEditing.ts new file mode 100644 index 0000000000..6d3066daf4 --- /dev/null +++ b/src/vs/workbench/services/configuration/common/jsonEditing.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; + +export const IJSONEditingService = createDecorator('jsonEditingService'); + +export enum JSONEditingErrorCode { + + /** + * Error when trying to write and save to the file while it is dirty in the editor. + */ + ERROR_FILE_DIRTY, + + /** + * Error when trying to write to a file that contains JSON errors. + */ + ERROR_INVALID_FILE +} + +export class JSONEditingError extends Error { + constructor(message: string, public code: JSONEditingErrorCode) { + super(message); + } +} + +export interface IJSONValue { + key: string; + value: any; +} + +export interface IJSONEditingService { + + _serviceBrand: ServiceIdentifier; + + write(resource: URI, value: IJSONValue, save: boolean): TPromise; +} \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts new file mode 100644 index 0000000000..b91a9f33b4 --- /dev/null +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -0,0 +1,916 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import * as paths from 'vs/base/common/paths'; +import { TPromise } from 'vs/base/common/winjs.base'; +import Event, { Emitter } from 'vs/base/common/event'; +import { StrictResourceMap } from 'vs/base/common/map'; +import { equals, coalesce } from 'vs/base/common/arrays'; +import * as objects from 'vs/base/common/objects'; +import * as errors from 'vs/base/common/errors'; +import * as collections from 'vs/base/common/collections'; +import { Disposable, toDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { readFile, stat } from 'vs/base/node/pfs'; +import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import * as extfs from 'vs/base/node/extfs'; +import { IWorkspaceContextService, IWorkspace, Workspace, ILegacyWorkspace, LegacyWorkspace } from 'vs/platform/workspace/common/workspace'; +import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; +import { isLinux } from 'vs/base/common/platform'; +import { ConfigWatcher } from 'vs/base/node/config'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { CustomConfigurationModel } from 'vs/platform/configuration/common/model'; +import { WorkspaceConfigurationModel, ScopedConfigurationModel, FolderConfigurationModel, FolderSettingsModel } from 'vs/workbench/services/configuration/common/configurationModels'; +import { IConfigurationServiceEvent, ConfigurationSource, IConfigurationKeys, IConfigurationValue, ConfigurationModel, IConfigurationOverrides, Configuration as BaseConfiguration, IConfigurationValues, IConfigurationData } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceConfigurationService, WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME, WORKSPACE_STANDALONE_CONFIGURATIONS, WORKSPACE_CONFIG_DEFAULT_PATH } from 'vs/workbench/services/configuration/common/configuration'; +import { ConfigurationService as GlobalConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import * as nls from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, editorConfigurationSchemaId, IDefaultConfigurationExtension, validateProperty, ConfigurationScope, schemaId } from 'vs/platform/configuration/common/configurationRegistry'; +import { createHash } from 'crypto'; +import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; + +interface IStat { + resource: URI; + isDirectory?: boolean; + children?: { resource: URI; }[]; +} + +interface IContent { + resource: URI; + value: string; +} + +interface IWorkspaceConfiguration { + workspace: T; + consolidated: any; +} + +type IWorkspaceFoldersConfiguration = { [rootFolder: string]: { folders: string[]; } }; + +const configurationRegistry = Registry.as(Extensions.Configuration); + +// BEGIN VSCode extension point `configuration` +const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint('configuration', [], { + description: nls.localize('vscode.extension.contributes.configuration', 'Contributes configuration settings.'), + type: 'object', + defaultSnippets: [{ body: { title: '', properties: {} } }], + properties: { + title: { + description: nls.localize('vscode.extension.contributes.configuration.title', 'A summary of the settings. This label will be used in the settings file as separating comment.'), + type: 'string' + }, + properties: { + description: nls.localize('vscode.extension.contributes.configuration.properties', 'Description of the configuration properties.'), + type: 'object', + additionalProperties: { + anyOf: [ + { $ref: 'http://json-schema.org/draft-04/schema#' }, + { + type: 'object', + properties: { + isExecutable: { + type: 'boolean' + }, + scope: { + type: 'string', + enum: ['window', 'resource'], + default: 'window', + enumDescriptions: [ + nls.localize('scope.window.description', "Window specific configuration, which can be configured in the User or Workspace settings."), + nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the User, Workspace or Folder settings.") + ], + description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `window` and `resource`.") + } + } + } + ] + } + } + } +}); +configurationExtPoint.setHandler(extensions => { + const configurations: IConfigurationNode[] = []; + + + for (let i = 0; i < extensions.length; i++) { + const configuration = objects.clone(extensions[i].value); + const collector = extensions[i].collector; + + if (configuration.type && configuration.type !== 'object') { + collector.warn(nls.localize('invalid.type', "if set, 'configuration.type' must be set to 'object")); + } else { + configuration.type = 'object'; + } + + if (configuration.title && (typeof configuration.title !== 'string')) { + collector.error(nls.localize('invalid.title', "'configuration.title' must be a string")); + } + + validateProperties(configuration, collector); + + configuration.id = extensions[i].description.id; + configurations.push(configuration); + } + + configurationRegistry.registerConfigurations(configurations, false); +}); +// END VSCode extension point `configuration` + +// BEGIN VSCode extension point `configurationDefaults` +const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint('configurationDefaults', [], { + description: nls.localize('vscode.extension.contributes.defaultConfiguration', 'Contributes default editor configuration settings by language.'), + type: 'object', + defaultSnippets: [{ body: {} }], + patternProperties: { + '\\[.*\\]$': { + type: 'object', + default: {}, + $ref: editorConfigurationSchemaId, + } + } +}); +defaultConfigurationExtPoint.setHandler(extensions => { + const defaultConfigurations: IDefaultConfigurationExtension[] = extensions.map(extension => { + const id = extension.description.id; + const name = extension.description.name; + const defaults = objects.clone(extension.value); + return { + id, name, defaults + }; + }); + configurationRegistry.registerDefaultConfigurations(defaultConfigurations); +}); +// END VSCode extension point `configurationDefaults` + +function validateProperties(configuration: IConfigurationNode, collector: ExtensionMessageCollector): void { + let properties = configuration.properties; + if (properties) { + if (typeof properties !== 'object') { + collector.error(nls.localize('invalid.properties', "'configuration.properties' must be an object")); + configuration.properties = {}; + } + for (let key in properties) { + const message = validateProperty(key); + const propertyConfiguration = configuration.properties[key]; + propertyConfiguration.scope = propertyConfiguration.scope && propertyConfiguration.scope.toString() === 'resource' ? ConfigurationScope.RESOURCE : ConfigurationScope.WINDOW; + if (message) { + collector.warn(message); + delete properties[key]; + } + } + } + let subNodes = configuration.allOf; + if (subNodes) { + for (let node of subNodes) { + validateProperties(node, collector); + } + } +} + +export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService { + + public _serviceBrand: any; + + protected workspace: Workspace = null; + protected legacyWorkspace: LegacyWorkspace = null; + protected _configuration: Configuration; + + protected readonly _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; + + protected readonly _onDidChangeWorkspaceRoots: Emitter = this._register(new Emitter()); + public readonly onDidChangeWorkspaceRoots: Event = this._onDidChangeWorkspaceRoots.event; + + protected readonly _onDidChangeWorkspaceName: Emitter = this._register(new Emitter()); + public readonly onDidChangeWorkspaceName: Event = this._onDidChangeWorkspaceName.event; + + constructor() { + super(); + this._configuration = new Configuration(new BaseConfiguration(new ConfigurationModel(), new ConfigurationModel()), new ConfigurationModel(), new StrictResourceMap>(), this.workspace); + } + + public getLegacyWorkspace(): ILegacyWorkspace { + return this.legacyWorkspace; + } + + public getWorkspace(): IWorkspace { + return this.workspace; + } + + public hasWorkspace(): boolean { + return !!this.workspace; + } + + public hasFolderWorkspace(): boolean { + return this.workspace && !this.workspace.configuration; + } + + public hasMultiFolderWorkspace(): boolean { + return this.workspace && !!this.workspace.configuration; + } + + public getRoot(resource: URI): URI { + return this.workspace ? this.workspace.getRoot(resource) : null; + } + + private get workspaceUri(): URI { + return this.workspace ? this.workspace.roots[0] : null; + } + + public isInsideWorkspace(resource: URI): boolean { + return !!this.getRoot(resource); + } + + public toResource(workspaceRelativePath: string): URI { + return this.workspace ? this.legacyWorkspace.toResource(workspaceRelativePath) : null; + } + + public initialize(trigger: boolean = true): TPromise { + this.resetCaches(); + return this.updateConfiguration() + .then(() => { + if (trigger) { + this.triggerConfigurationChange(); + } + }); + } + + public reloadConfiguration(section?: string): TPromise { + return TPromise.as(this.getConfiguration(section)); + } + + public getConfigurationData(): IConfigurationData { + return this._configuration.toData(); + } + + public getConfiguration(section?: string, overrides?: IConfigurationOverrides): C { + return this._configuration.getValue(section, overrides); + } + + public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { + return this._configuration.lookup(key, overrides); + } + + public keys(overrides?: IConfigurationOverrides): IConfigurationKeys { + return this._configuration.keys(overrides); + } + + public values(): IConfigurationValues { + return this._configuration.values(); + } + + public getUnsupportedWorkspaceKeys(): string[] { + return []; + } + + public isInWorkspaceContext(): boolean { + return false; + } + + protected triggerConfigurationChange(): void { + this._onDidUpdateConfiguration.fire({ source: ConfigurationSource.Workspace, sourceConfig: void 0 }); + } + + public handleWorkspaceFileEvents(event: FileChangesEvent): void { + // implemented by sub classes + } + + protected resetCaches(): void { + // implemented by sub classes + } + + protected updateConfiguration(): TPromise { + // implemented by sub classes + return TPromise.as(false); + } +} + +export class EmptyWorkspaceServiceImpl extends WorkspaceService { + + private baseConfigurationService: GlobalConfigurationService; + + constructor(environmentService: IEnvironmentService) { + super(); + this.baseConfigurationService = this._register(new GlobalConfigurationService(environmentService)); + this._register(this.baseConfigurationService.onDidUpdateConfiguration(e => this.onBaseConfigurationChanged(e))); + this.resetCaches(); + } + + public reloadConfiguration(section?: string): TPromise { + const current = this._configuration; + return this.baseConfigurationService.reloadConfiguration() + .then(() => this.initialize(false)) // Reinitialize to ensure we are hitting the disk + .then(() => { + // Check and trigger + if (!this._configuration.equals(current)) { + this.triggerConfigurationChange(); + } + return super.reloadConfiguration(section); + }); + } + + private onBaseConfigurationChanged({ source, sourceConfig }: IConfigurationServiceEvent): void { + if (this._configuration.updateBaseConfiguration(this.baseConfigurationService.configuration())) { + this._onDidUpdateConfiguration.fire({ source, sourceConfig }); + } + } + + protected resetCaches(): void { + this._configuration = new Configuration(this.baseConfigurationService.configuration(), new ConfigurationModel(), new StrictResourceMap>(), null); + } + + protected triggerConfigurationChange(): void { + this._onDidUpdateConfiguration.fire({ source: ConfigurationSource.User, sourceConfig: this._configuration.user.contents }); + } +} + +export class WorkspaceServiceImpl extends WorkspaceService { + + public _serviceBrand: any; + + private workspaceConfigPath: URI; + private folderPath: URI; + private baseConfigurationService: GlobalConfigurationService; + private workspaceConfiguration: WorkspaceConfiguration; + private cachedFolderConfigs: StrictResourceMap>; + + constructor(private workspaceIdentifier: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, private environmentService: IEnvironmentService, private workspacesService: IWorkspacesService, private workspaceSettingsRootFolder: string = WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME) { + super(); + + if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) { + this.folderPath = URI.file(workspaceIdentifier); + } else { + this.workspaceConfigPath = URI.file(workspaceIdentifier.configPath); + } + + this.workspaceConfiguration = this._register(new WorkspaceConfiguration()); + this.baseConfigurationService = this._register(new GlobalConfigurationService(environmentService)); + } + + public getUnsupportedWorkspaceKeys(): string[] { + return this.hasFolderWorkspace() ? this._configuration.getFolderConfigurationModel(this.workspace.roots[0]).workspaceSettingsConfig.unsupportedKeys : []; + } + + public initialize(trigger: boolean = true): TPromise { + if (!this.workspace) { + return this.initializeWorkspace() + .then(() => super.initialize(trigger)); + } + + if (this.hasMultiFolderWorkspace()) { + return this.workspaceConfiguration.load(this.workspaceConfigPath) + .then(() => super.initialize(trigger)); + } + + return super.initialize(trigger); + } + + public reloadConfiguration(section?: string): TPromise { + const current = this._configuration; + return this.baseConfigurationService.reloadConfiguration() + .then(() => this.initialize(false)) // Reinitialize to ensure we are hitting the disk + .then(() => { + // Check and trigger + if (!this._configuration.equals(current)) { + this.triggerConfigurationChange(); + } + return super.reloadConfiguration(section); + }); + } + + public handleWorkspaceFileEvents(event: FileChangesEvent): void { + TPromise.join(this.workspace.roots.map(folder => this.cachedFolderConfigs.get(folder).handleWorkspaceFileEvents(event))) // handle file event for each folder + .then(folderConfigurations => + folderConfigurations.map((configuration, index) => ({ configuration, folder: this.workspace.roots[index] })) + .filter(folderConfiguration => !!folderConfiguration.configuration) // Filter folders which are not impacted by events + .map(folderConfiguration => this.updateFolderConfiguration(folderConfiguration.folder, folderConfiguration.configuration, true)) // Update the configuration of impacted folders + .reduce((result, value) => result || value, false)) // Check if the effective configuration of folder is changed + .then(changed => changed ? this.triggerConfigurationChange() : void 0); // Trigger event if changed + } + + protected resetCaches(): void { + this.cachedFolderConfigs = new StrictResourceMap>(); + this._configuration = new Configuration(this.baseConfigurationService.configuration(), new ConfigurationModel(), new StrictResourceMap>(), this.workspace); + this.initCachesForFolders(this.workspace.roots); + } + + private initializeWorkspace(): TPromise { + return (this.workspaceConfigPath ? this.initializeMulitFolderWorkspace() : this.initializeSingleFolderWorkspace()) + .then(() => { + this._register(this.baseConfigurationService.onDidUpdateConfiguration(e => this.onBaseConfigurationChanged(e))); + }); + } + + // TODO@Sandeep use again once we can change workspace without window reload + // private onWorkspaceChange(configPath: URI): TPromise { + // let workspaceName = this.workspace.name; + // this.workspaceConfigPath = configPath; + + // // Reset the workspace if current workspace is single folder + // if (this.hasFolderWorkspace()) { + // this.folderPath = null; + // this.workspace = null; + // } + + // // Update workspace configuration path with new path + // else { + // this.workspace.configuration = configPath; + // this.workspace.name = getWorkspaceLabel({ id: this.workspace.id, configPath: this.workspace.configuration.fsPath }, this.environmentService); + // } + + // return this.initialize().then(() => { + // if (workspaceName !== this.workspace.name) { + // this._onDidChangeWorkspaceName.fire(); + // } + // }); + // } + + private initializeMulitFolderWorkspace(): TPromise { + this.registerWorkspaceConfigSchema(); + return this.workspaceConfiguration.load(this.workspaceConfigPath) + .then(() => { + const workspaceConfigurationModel = this.workspaceConfiguration.workspaceConfigurationModel; + const workspaceFolders = this.parseWorkspaceFolders(workspaceConfigurationModel.folders); + if (!workspaceFolders.length) { + return TPromise.wrapError(new Error('Invalid workspace configuraton file ' + this.workspaceConfigPath)); + } + const workspaceId = (this.workspaceIdentifier as IWorkspaceIdentifier).id; + const workspaceName = getWorkspaceLabel({ id: workspaceId, configPath: this.workspaceConfigPath.fsPath }, this.environmentService); + this.workspace = new Workspace(workspaceId, workspaceName, workspaceFolders, this.workspaceConfigPath); + this.legacyWorkspace = new LegacyWorkspace(this.workspace.roots[0]); + this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged())); + return null; + }); + } + + private parseWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[]): URI[] { + return coalesce(configuredFolders.map(configuredFolder => { + const path = configuredFolder.path; + if (!path) { + return void 0; + } + + if (paths.isAbsolute(path)) { + return URI.file(path); + } + + return URI.file(paths.join(paths.dirname(this.workspaceConfigPath.fsPath), path)); + })); + } + + private registerWorkspaceConfigSchema(): void { + const contributionRegistry = Registry.as(JSONExtensions.JSONContribution); + if (!contributionRegistry.getSchemaContributions().schemas['vscode://schemas/workspaceConfig']) { + contributionRegistry.registerSchema('vscode://schemas/workspaceConfig', { + default: { + folders: [ + { + path: '' + } + ], + settings: { + } + }, + required: ['folders'], + properties: { + 'folders': { + minItems: 1, + uniqueItems: true, + description: nls.localize('workspaceConfig.folders.description', "List of folders to be loaded in the workspace. Must be a file path. e.g. `/root/folderA` or `./folderA` for a relative path that will be resolved against the location of the workspace file."), + items: { + type: 'object', + default: { path: '' }, + properties: { + path: { + type: 'string', + description: nls.localize('workspaceConfig.folder.description', "A file path. e.g. `/root/folderA` or `./folderA` for a relative path that will be resolved against the location of the workspace file.") + } + } + } + }, + 'settings': { + type: 'object', + default: {}, + description: nls.localize('workspaceConfig.settings.description', "Workspace settings"), + $ref: schemaId + } + } + }); + } + } + + private initializeSingleFolderWorkspace(): TPromise { + return stat(this.folderPath.fsPath) + .then(workspaceStat => { + const ctime = isLinux ? workspaceStat.ino : workspaceStat.birthtime.getTime(); // On Linux, birthtime is ctime, so we cannot use it! We use the ino instead! + const id = createHash('md5').update(this.folderPath.fsPath).update(ctime ? String(ctime) : '').digest('hex'); + const folder = URI.file(this.folderPath.fsPath); + this.workspace = new Workspace(id, paths.basename(this.folderPath.fsPath), [folder], null); + this.legacyWorkspace = new LegacyWorkspace(folder, ctime); + return TPromise.as(null); + }); + } + + private initCachesForFolders(folders: URI[]): void { + for (const folder of folders) { + this.cachedFolderConfigs.set(folder, this._register(new FolderConfiguration(folder, this.workspaceSettingsRootFolder, this.hasMultiFolderWorkspace() ? ConfigurationScope.RESOURCE : ConfigurationScope.WINDOW))); + this.updateFolderConfiguration(folder, new FolderConfigurationModel(new FolderSettingsModel(null), [], ConfigurationScope.RESOURCE), false); + } + } + + protected updateConfiguration(folders: URI[] = this.workspace.roots): TPromise { + return TPromise.join([...folders.map(folder => this.cachedFolderConfigs.get(folder).loadConfiguration() + .then(configuration => this.updateFolderConfiguration(folder, configuration, true)))]) + .then(changed => changed.reduce((result, value) => result || value, false)) + .then(changed => this.updateWorkspaceConfiguration(true) || changed); + } + + private onBaseConfigurationChanged({ source, sourceConfig }: IConfigurationServiceEvent): void { + if (source === ConfigurationSource.Default) { + this.workspace.roots.forEach(folder => this._configuration.getFolderConfigurationModel(folder).update()); + } + if (this._configuration.updateBaseConfiguration(this.baseConfigurationService.configuration())) { + this._onDidUpdateConfiguration.fire({ source, sourceConfig }); + } + } + + private onWorkspaceConfigurationChanged(): void { + let configuredFolders = this.parseWorkspaceFolders(this.workspaceConfiguration.workspaceConfigurationModel.folders); + const foldersChanged = !equals(this.workspace.roots, configuredFolders, (r1, r2) => r1.fsPath === r2.fsPath); + if (foldersChanged) { // TODO@Sandeep be smarter here about detecting changes + this.workspace.roots = configuredFolders; + this.onFoldersChanged() + .then(configurationChanged => { + this._onDidChangeWorkspaceRoots.fire(); + if (configurationChanged) { + this.triggerConfigurationChange(); + } + }); + } else { + const configurationChanged = this.updateWorkspaceConfiguration(true); + if (configurationChanged) { + this.triggerConfigurationChange(); + } + } + } + + private onFoldersChanged(): TPromise { + let configurationChangedOnRemoval = false; + + // Remove the configurations of deleted folders + for (const key of this.cachedFolderConfigs.keys()) { + if (!this.workspace.roots.filter(folder => folder.toString() === key.toString())[0]) { + this.cachedFolderConfigs.delete(key); + if (this._configuration.deleteFolderConfiguration(key)) { + configurationChangedOnRemoval = true; + } + } + } + + // Initialize the newly added folders + const toInitialize = this.workspace.roots.filter(folder => !this.cachedFolderConfigs.has(folder)); + if (toInitialize.length) { + this.initCachesForFolders(toInitialize); + return this.updateConfiguration(toInitialize) + .then(changed => configurationChangedOnRemoval || changed); + } else if (configurationChangedOnRemoval) { + this.updateWorkspaceConfiguration(false); + return TPromise.as(true); + } + return TPromise.as(false); + } + + private updateFolderConfiguration(folder: URI, folderConfiguration: FolderConfigurationModel, compare: boolean): boolean { + let configurationChanged = this._configuration.updateFolderConfiguration(folder, folderConfiguration, compare); + if (this.hasFolderWorkspace()) { + // Workspace configuration changed + configurationChanged = this.updateWorkspaceConfiguration(compare) || configurationChanged; + } + return configurationChanged; + } + + private updateWorkspaceConfiguration(compare: boolean): boolean { + const workspaceConfiguration = this.hasMultiFolderWorkspace() ? this.workspaceConfiguration.workspaceConfigurationModel.workspaceConfiguration : this._configuration.getFolderConfigurationModel(this.workspace.roots[0]); + return this._configuration.updateWorkspaceConfiguration(workspaceConfiguration, compare); + } + + protected triggerConfigurationChange(): void { + this._onDidUpdateConfiguration.fire({ source: ConfigurationSource.Workspace, sourceConfig: this._configuration.getFolderConfigurationModel(this.workspace.roots[0]).contents }); + } +} + +class WorkspaceConfiguration extends Disposable { + + private _workspaceConfigPath: URI; + private _workspaceConfigurationWatcher: ConfigWatcher>; + private _workspaceConfigurationWatcherDisposables: IDisposable[] = []; + + private _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; + + + load(workspaceConfigPath: URI): TPromise { + if (this._workspaceConfigPath && this._workspaceConfigPath.fsPath === workspaceConfigPath.fsPath) { + return this._reload(); + } + + this._workspaceConfigPath = workspaceConfigPath; + + this._workspaceConfigurationWatcherDisposables = dispose(this._workspaceConfigurationWatcherDisposables); + return new TPromise((c, e) => { + this._workspaceConfigurationWatcher = new ConfigWatcher(this._workspaceConfigPath.fsPath, { + changeBufferDelay: 300, onError: error => errors.onUnexpectedError(error), defaultConfig: new WorkspaceConfigurationModel(null, this._workspaceConfigPath.fsPath), parse: (content: string, parseErrors: any[]) => { + const workspaceConfigurationModel = new WorkspaceConfigurationModel(content, this._workspaceConfigPath.fsPath); + parseErrors = [...workspaceConfigurationModel.errors]; + return workspaceConfigurationModel; + }, initCallback: () => c(null) + }); + this._workspaceConfigurationWatcherDisposables.push(toDisposable(() => this._workspaceConfigurationWatcher.dispose())); + this._workspaceConfigurationWatcher.onDidUpdateConfiguration(() => this._onDidUpdateConfiguration.fire(), this, this._workspaceConfigurationWatcherDisposables); + }); + } + + get workspaceConfigurationModel(): WorkspaceConfigurationModel { + return this._workspaceConfigurationWatcher ? this._workspaceConfigurationWatcher.getConfig() : new WorkspaceConfigurationModel(); + } + + private _reload(): TPromise { + return new TPromise(c => this._workspaceConfigurationWatcher.reload(() => c(null))); + } + + dispose(): void { + dispose(this._workspaceConfigurationWatcherDisposables); + super.dispose(); + } +} + +class FolderConfiguration extends Disposable { + + private static RELOAD_CONFIGURATION_DELAY = 50; + + private bulkFetchFromWorkspacePromise: TPromise; + private workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: TPromise> }; + + private reloadConfigurationScheduler: RunOnceScheduler; + private reloadConfigurationEventEmitter: Emitter> = new Emitter>(); + + constructor(private folder: URI, private configFolderRelativePath: string, private scope: ConfigurationScope) { + super(); + + this.workspaceFilePathToConfiguration = Object.create(null); + this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.loadConfiguration().then(configuration => this.reloadConfigurationEventEmitter.fire(configuration), errors.onUnexpectedError), FolderConfiguration.RELOAD_CONFIGURATION_DELAY)); + } + + loadConfiguration(): TPromise> { + // Load workspace locals + return this.loadWorkspaceConfigFiles().then(workspaceConfigFiles => { + // Consolidate (support *.json files in the workspace settings folder) + const workspaceSettingsConfig = >workspaceConfigFiles[WORKSPACE_CONFIG_DEFAULT_PATH] || new FolderSettingsModel(null); + const otherConfigModels = Object.keys(workspaceConfigFiles).filter(key => key !== WORKSPACE_CONFIG_DEFAULT_PATH).map(key => >workspaceConfigFiles[key]); + return new FolderConfigurationModel(workspaceSettingsConfig, otherConfigModels, this.scope); + }); + } + + private loadWorkspaceConfigFiles(): TPromise<{ [relativeWorkspacePath: string]: ConfigurationModel }> { + // once: when invoked for the first time we fetch json files that contribute settings + if (!this.bulkFetchFromWorkspacePromise) { + this.bulkFetchFromWorkspacePromise = resolveStat(this.toResource(this.configFolderRelativePath)).then(stat => { + if (!stat.isDirectory) { + return TPromise.as([]); + } + + return resolveContents(stat.children.filter(stat => { + const isJson = paths.extname(stat.resource.fsPath) === '.json'; + if (!isJson) { + return false; // only JSON files + } + + return this.isWorkspaceConfigurationFile(this.toFolderRelativePath(stat.resource)); // only workspace config files + }).map(stat => stat.resource)); + }, err => [] /* never fail this call */) + .then((contents: IContent[]) => { + contents.forEach(content => this.workspaceFilePathToConfiguration[this.toFolderRelativePath(content.resource)] = TPromise.as(this.createConfigModel(content))); + }, errors.onUnexpectedError); + } + + // on change: join on *all* configuration file promises so that we can merge them into a single configuration object. this + // happens whenever a config file changes, is deleted, or added + return this.bulkFetchFromWorkspacePromise.then(() => TPromise.join(this.workspaceFilePathToConfiguration)); + } + + public handleWorkspaceFileEvents(event: FileChangesEvent): TPromise> { + const events = event.changes; + let affectedByChanges = false; + + // Find changes that affect workspace configuration files + for (let i = 0, len = events.length; i < len; i++) { + const resource = events[i].resource; + const isJson = paths.extname(resource.fsPath) === '.json'; + const isDeletedSettingsFolder = (events[i].type === FileChangeType.DELETED && paths.isEqual(paths.basename(resource.fsPath), this.configFolderRelativePath)); + if (!isJson && !isDeletedSettingsFolder) { + continue; // only JSON files or the actual settings folder + } + + const workspacePath = this.toFolderRelativePath(resource); + if (!workspacePath) { + continue; // event is not inside workspace + } + + // Handle case where ".vscode" got deleted + if (workspacePath === this.configFolderRelativePath && events[i].type === FileChangeType.DELETED) { + this.workspaceFilePathToConfiguration = Object.create(null); + affectedByChanges = true; + } + + // only valid workspace config files + if (!this.isWorkspaceConfigurationFile(workspacePath)) { + continue; + } + + // insert 'fetch-promises' for add and update events and + // remove promises for delete events + switch (events[i].type) { + case FileChangeType.DELETED: + affectedByChanges = collections.remove(this.workspaceFilePathToConfiguration, workspacePath); + break; + case FileChangeType.UPDATED: + case FileChangeType.ADDED: + this.workspaceFilePathToConfiguration[workspacePath] = resolveContent(resource).then(content => this.createConfigModel(content), errors.onUnexpectedError); + affectedByChanges = true; + } + } + + if (!affectedByChanges) { + return TPromise.as(null); + } + + return new TPromise((c, e) => { + let disposable = this.reloadConfigurationEventEmitter.event(configuration => { + disposable.dispose(); + c(configuration); + }); + // trigger reload of the configuration if we are affected by changes + if (!this.reloadConfigurationScheduler.isScheduled()) { + this.reloadConfigurationScheduler.schedule(); + } + }); + } + + private createConfigModel(content: IContent): ConfigurationModel { + const path = this.toFolderRelativePath(content.resource); + if (path === WORKSPACE_CONFIG_DEFAULT_PATH) { + return new FolderSettingsModel(content.value, content.resource.toString()); + } else { + const matches = /\/([^\.]*)*\.json/.exec(path); + if (matches && matches[1]) { + return new ScopedConfigurationModel(content.value, content.resource.toString(), matches[1]); + } + } + + return new CustomConfigurationModel(null); + } + + private isWorkspaceConfigurationFile(folderRelativePath: string): boolean { + return [WORKSPACE_CONFIG_DEFAULT_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS.launch, WORKSPACE_STANDALONE_CONFIGURATIONS.tasks].some(p => p === folderRelativePath); + } + + private toResource(folderRelativePath: string): URI { + if (typeof folderRelativePath === 'string') { + return URI.file(paths.join(this.folder.fsPath, folderRelativePath)); + } + + return null; + } + + private toFolderRelativePath(resource: URI, toOSPath?: boolean): string { + if (this.contains(resource)) { + return paths.normalize(paths.relative(this.folder.fsPath, resource.fsPath), toOSPath); + } + + return null; + } + + private contains(resource: URI): boolean { + if (resource) { + return paths.isEqualOrParent(resource.fsPath, this.folder.fsPath, !isLinux /* ignorecase */); + } + + return false; + } +} + +// node.hs helper functions + +function resolveContents(resources: URI[]): TPromise { + const contents: IContent[] = []; + + return TPromise.join(resources.map(resource => { + return resolveContent(resource).then(content => { + contents.push(content); + }); + })).then(() => contents); +} + +function resolveContent(resource: URI): TPromise { + return readFile(resource.fsPath).then(contents => ({ resource, value: contents.toString() })); +} + +function resolveStat(resource: URI): TPromise { + return new TPromise((c, e) => { + extfs.readdir(resource.fsPath, (error, children) => { + if (error) { + if ((error).code === 'ENOTDIR') { + c({ resource }); + } else { + e(error); + } + } else { + c({ + resource, + isDirectory: true, + children: children.map(child => { return { resource: URI.file(paths.join(resource.fsPath, child)) }; }) + }); + } + }); + }); +} + +export class Configuration extends BaseConfiguration { + + constructor(private _baseConfiguration: BaseConfiguration, workspaceConfiguration: ConfigurationModel, protected folders: StrictResourceMap>, workspace: Workspace) { + super(_baseConfiguration.defaults, _baseConfiguration.user, workspaceConfiguration, folders, workspace); + } + + updateBaseConfiguration(baseConfiguration: BaseConfiguration): boolean { + const current = new Configuration(this._baseConfiguration, this._workspaceConfiguration, this.folders, this._workspace); + + this._baseConfiguration = baseConfiguration; + this._defaults = this._baseConfiguration.defaults; + this._user = this._baseConfiguration.user; + this.merge(); + + return !this.equals(current); + } + + updateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel, compare: boolean = true): boolean { + const current = new Configuration(this._baseConfiguration, this._workspaceConfiguration, this.folders, this._workspace); + + this._workspaceConfiguration = workspaceConfiguration; + this.merge(); + + return compare && !this.equals(current); + } + + updateFolderConfiguration(resource: URI, configuration: FolderConfigurationModel, compare: boolean): boolean { + const current = this.getValue(null, { resource }); + + this.folders.set(resource, configuration); + this.mergeFolder(resource); + + return compare && !objects.equals(current, this.getValue(null, { resource })); + } + + deleteFolderConfiguration(folder: URI): boolean { + if (this._workspace && this._workspace.roots.length > 0 && this._workspace.roots[0].fsPath === folder.fsPath) { + // Do not remove workspace configuration + return false; + } + + const changed = this.folders.get(folder).keys.length > 0; + this.folders.delete(folder); + this._foldersConsolidatedConfigurations.delete(folder); + return changed; + } + + getFolderConfigurationModel(folder: URI): FolderConfigurationModel { + return >this.folders.get(folder); + } + + equals(other: any): boolean { + if (!other || !(other instanceof Configuration)) { + return false; + } + + if (!objects.equals(this.getValue(), other.getValue())) { + return false; + } + + if (this._foldersConsolidatedConfigurations.size !== other._foldersConsolidatedConfigurations.size) { + return false; + } + + for (const resource of this._foldersConsolidatedConfigurations.keys()) { + if (!objects.equals(this.getValue(null, { resource }), other.getValue(null, { resource }))) { + return false; + } + } + + return true; + } +} diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/node/configurationEditingService.ts new file mode 100644 index 0000000000..926a6775ac --- /dev/null +++ b/src/vs/workbench/services/configuration/node/configurationEditingService.ts @@ -0,0 +1,353 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import * as paths from 'vs/base/common/paths'; +import URI from 'vs/base/common/uri'; +import * as json from 'vs/base/common/json'; +import * as encoding from 'vs/base/node/encoding'; +import strings = require('vs/base/common/strings'); +import { setProperty } from 'vs/base/common/jsonEdit'; +import { Queue } from 'vs/base/common/async'; +import { Edit } from 'vs/base/common/jsonFormatter'; +import { IReference } from 'vs/base/common/lifecycle'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IConfigurationService, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; +import { keyFromOverrideIdentifier } from 'vs/platform/configuration/common/model'; +import { WORKSPACE_CONFIG_DEFAULT_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IConfigurationEditingService, ConfigurationEditingErrorCode, ConfigurationEditingError, ConfigurationTarget, IConfigurationValue, IConfigurationEditingOptions } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { OVERRIDE_PROPERTY_PATTERN, IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IChoiceService, IMessageService, Severity } from 'vs/platform/message/common/message'; +import { ICommandService } from 'vs/platform/commands/common/commands'; + +interface IConfigurationEditOperation extends IConfigurationValue { + jsonPath: json.JSONPath; + resource: URI; + isWorkspaceStandalone?: boolean; +} + +interface IValidationResult { + error?: ConfigurationEditingErrorCode; + exists?: boolean; +} + +interface ConfigurationEditingOptions extends IConfigurationEditingOptions { + force?: boolean; +} + +export class ConfigurationEditingService implements IConfigurationEditingService { + + public _serviceBrand: any; + + private queue: Queue; + + constructor( + @IConfigurationService private configurationService: IConfigurationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IFileService private fileService: IFileService, + @ITextModelService private textModelResolverService: ITextModelService, + @ITextFileService private textFileService: ITextFileService, + @IChoiceService private choiceService: IChoiceService, + @IMessageService private messageService: IMessageService, + @ICommandService private commandService: ICommandService + ) { + this.queue = new Queue(); + } + + writeConfiguration(target: ConfigurationTarget, value: IConfigurationValue, options: IConfigurationEditingOptions = {}): TPromise { + return this.queue.queue(() => this.doWriteConfiguration(target, value, options) // queue up writes to prevent race conditions + .then(() => null, + error => { + if (!options.donotNotifyError) { + this.onError(error, target, value, options.scopes); + } + return TPromise.wrapError(error); + })); + } + + private doWriteConfiguration(target: ConfigurationTarget, value: IConfigurationValue, options: ConfigurationEditingOptions): TPromise { + const operation = this.getConfigurationEditOperation(target, value, options.scopes || {}); + + const checkDirtyConfiguration = !(options.force || options.donotSave); + const saveConfiguration = options.force || !options.donotSave; + return this.resolveAndValidate(target, operation, checkDirtyConfiguration, options.scopes || {}) + .then(reference => this.writeToBuffer(reference.object.textEditorModel, operation, saveConfiguration) + .then(() => reference.dispose())); + } + + private writeToBuffer(model: editorCommon.IModel, operation: IConfigurationEditOperation, save: boolean): TPromise { + const edit = this.getEdits(model, operation)[0]; + if (this.applyEditsToBuffer(edit, model) && save) { + return this.textFileService.save(operation.resource, { skipSaveParticipants: true /* programmatic change */ }) + // Reload the configuration so that we make sure all parties are updated + .then(() => this.configurationService.reloadConfiguration()); + } + return TPromise.as(null); + } + + private applyEditsToBuffer(edit: Edit, model: editorCommon.IModel): boolean { + const startPosition = model.getPositionAt(edit.offset); + const endPosition = model.getPositionAt(edit.offset + edit.length); + const range = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column); + let currentText = model.getValueInRange(range); + if (edit.content !== currentText) { + const editOperation = currentText ? EditOperation.replace(range, edit.content) : EditOperation.insert(startPosition, edit.content); + model.pushEditOperations([new Selection(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column)], [editOperation], () => []); + return true; + } + return false; + } + + private onError(error: ConfigurationEditingError, target: ConfigurationTarget, value: IConfigurationValue, scopes: IConfigurationOverrides): void { + switch (error.code) { + case ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION: + this.onInvalidConfigurationError(error, target); + break; + case ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY: + this.onConfigurationFileDirtyError(error, target, value, scopes); + break; + default: + this.messageService.show(Severity.Error, error.message); + } + } + + private onInvalidConfigurationError(error: ConfigurationEditingError, target: ConfigurationTarget): void { + this.choiceService.choose(Severity.Error, error.message, [nls.localize('open', "Open Settings"), nls.localize('close', "Close")], 1) + .then(option => { + switch (option) { + case 0: + this.openSettings(target); + } + }); + } + + private onConfigurationFileDirtyError(error: ConfigurationEditingError, target: ConfigurationTarget, value: IConfigurationValue, scopes: IConfigurationOverrides): void { + this.choiceService.choose(Severity.Error, error.message, [nls.localize('saveAndRetry', "Save Settings and Retry"), nls.localize('open', "Open Settings"), nls.localize('close', "Close")], 2) + .then(option => { + switch (option) { + case 0: + this.writeConfiguration(target, value, { force: true, scopes }); + break; + case 1: + this.openSettings(target); + break; + } + }); + } + + private openSettings(target: ConfigurationTarget): void { + this.commandService.executeCommand(ConfigurationTarget.USER === target ? 'workbench.action.openGlobalSettings' : 'workbench.action.openWorkspaceSettings'); + } + + private wrapError(code: ConfigurationEditingErrorCode, target: ConfigurationTarget, operation: IConfigurationEditOperation): TPromise { + const message = this.toErrorMessage(code, target, operation); + + return TPromise.wrapError(new ConfigurationEditingError(message, code)); + } + + private toErrorMessage(error: ConfigurationEditingErrorCode, target: ConfigurationTarget, operation: IConfigurationEditOperation): string { + switch (error) { + + // API constraints + case ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY: return nls.localize('errorUnknownKey', "Unable to write to {0} because {1} is not a registered configuration.", this.stringifyTarget(target), operation.key); + case ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_CONFIGURATION: return nls.localize('errorInvalidFolderConfiguration', "Unable to write to Folder Settings because {0} does not support the folder resource scope.", operation.key); + case ConfigurationEditingErrorCode.ERROR_INVALID_USER_TARGET: return nls.localize('errorInvalidUserTarget', "Unable to write to User Settings because {0} does not support for global scope.", operation.key); + case ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_TARGET: return nls.localize('errorInvalidFolderTarget', "Unable to write to Folder Settings because no resource is provided."); + case ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED: return nls.localize('errorNoWorkspaceOpened', "Unable to write to {0} because no workspace is opened. Please open a workspace first and try again.", this.stringifyTarget(target)); + + // User issues + case ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION: { + if (target === ConfigurationTarget.USER) { + return nls.localize('errorInvalidConfiguration', "Unable to write into settings. Please open **User Settings** to correct errors/warnings in the file and try again."); + } + + return nls.localize('errorInvalidConfigurationWorkspace', "Unable to write into settings. Please open **Workspace Settings** to correct errors/warnings in the file and try again."); + }; + case ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY: { + if (target === ConfigurationTarget.USER) { + return nls.localize('errorConfigurationFileDirty', "Unable to write into settings because the file is dirty. Please save the **User Settings** file and try again."); + } + + return nls.localize('errorConfigurationFileDirtyWorkspace', "Unable to write into settings because the file is dirty. Please save the **Workspace Settings** file and try again."); + }; + } + } + + private stringifyTarget(target: ConfigurationTarget): string { + switch (target) { + case ConfigurationTarget.USER: + return nls.localize('userTarget', "User Settings"); + case ConfigurationTarget.WORKSPACE: + return nls.localize('workspaceTarget', "Workspace Settings"); + case ConfigurationTarget.FOLDER: + return nls.localize('folderTarget', "Folder Settings"); + } + } + + private getEdits(model: editorCommon.IModel, edit: IConfigurationEditOperation): Edit[] { + const { tabSize, insertSpaces } = model.getOptions(); + const eol = model.getEOL(); + const { value, jsonPath } = edit; + + // Without jsonPath, the entire configuration file is being replaced, so we just use JSON.stringify + if (!jsonPath.length) { + const content = JSON.stringify(value, null, insertSpaces ? strings.repeat(' ', tabSize) : '\t'); + return [{ + content, + length: content.length, + offset: 0 + }]; + } + + return setProperty(model.getValue(), jsonPath, value, { tabSize, insertSpaces, eol }); + } + + private resolveModelReference(resource: URI): TPromise> { + return this.fileService.existsFile(resource) + .then(exists => { + const result = exists ? TPromise.as(null) : this.fileService.updateContent(resource, '{}', { encoding: encoding.UTF8 }); + return result.then(() => this.textModelResolverService.createModelReference(resource)); + }); + } + + private hasParseErrors(model: editorCommon.IModel, operation: IConfigurationEditOperation): boolean { + // If we write to a workspace standalone file and replace the entire contents (no key provided) + // we can return here because any parse errors can safely be ignored since all contents are replaced + if (operation.isWorkspaceStandalone && !operation.key) { + return false; + } + const parseErrors: json.ParseError[] = []; + json.parse(model.getValue(), parseErrors, { allowTrailingComma: true }); + return parseErrors.length > 0; + } + + private resolveAndValidate(target: ConfigurationTarget, operation: IConfigurationEditOperation, checkDirty: boolean, overrides: IConfigurationOverrides): TPromise> { + + // Any key must be a known setting from the registry (unless this is a standalone config) + if (!operation.isWorkspaceStandalone) { + const validKeys = this.configurationService.keys().default; + if (validKeys.indexOf(operation.key) < 0 && !OVERRIDE_PROPERTY_PATTERN.test(operation.key)) { + return this.wrapError(ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY, target, operation); + } + } + + // Target cannot be user if is standalone + if (operation.isWorkspaceStandalone && target === ConfigurationTarget.USER) { + return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_USER_TARGET, target, operation); + } + + // Target cannot be workspace or folder if no workspace opened + if ((target === ConfigurationTarget.WORKSPACE || target === ConfigurationTarget.FOLDER) && !this.contextService.hasWorkspace()) { + return this.wrapError(ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED, target, operation); + } + + if (target === ConfigurationTarget.FOLDER) { + if (!operation.resource) { + return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_TARGET, target, operation); + } + + const configurationProperties = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); + if (configurationProperties[operation.key].scope !== ConfigurationScope.RESOURCE) { + return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_CONFIGURATION, target, operation); + } + } + + return this.resolveModelReference(operation.resource) + .then(reference => { + const model = reference.object.textEditorModel; + + if (this.hasParseErrors(model, operation)) { + return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION, target, operation); + } + + // Target cannot be dirty if not writing into buffer + if (checkDirty && this.textFileService.isDirty(operation.resource)) { + return this.wrapError(ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY, target, operation); + } + return reference; + }); + } + + private getConfigurationEditOperation(target: ConfigurationTarget, config: IConfigurationValue, overrides: IConfigurationOverrides): IConfigurationEditOperation { + + const workspace = this.contextService.getWorkspace(); + + // Check for standalone workspace configurations + if (config.key) { + const standaloneConfigurationKeys = Object.keys(WORKSPACE_STANDALONE_CONFIGURATIONS); + for (let i = 0; i < standaloneConfigurationKeys.length; i++) { + const key = standaloneConfigurationKeys[i]; + const resource = this.getConfigurationFileResource(target, WORKSPACE_STANDALONE_CONFIGURATIONS[key], overrides.resource); + + // Check for prefix + if (config.key === key) { + const jsonPath = workspace && workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath ? [key] : []; + return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource, isWorkspaceStandalone: true }; + } + + // Check for prefix. + const keyPrefix = `${key}.`; + if (config.key.indexOf(keyPrefix) === 0) { + const jsonPath = workspace && workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath ? [key, config.key.substr(keyPrefix.length)] : [config.key.substr(keyPrefix.length)]; + return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource, isWorkspaceStandalone: true }; + } + } + } + + let key = config.key; + let jsonPath = overrides.overrideIdentifier ? [keyFromOverrideIdentifier(overrides.overrideIdentifier), key] : [key]; + if (target === ConfigurationTarget.USER) { + return { key, jsonPath, value: config.value, resource: URI.file(this.environmentService.appSettingsPath) }; + } + + const resource = this.getConfigurationFileResource(target, WORKSPACE_CONFIG_DEFAULT_PATH, overrides.resource); + if (workspace && workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath) { + jsonPath = ['settings', ...jsonPath]; + } + return { key, jsonPath, value: config.value, resource }; + } + + private getConfigurationFileResource(target: ConfigurationTarget, relativePath: string, resource: URI): URI { + if (target === ConfigurationTarget.USER) { + return URI.file(this.environmentService.appSettingsPath); + } + + const workspace = this.contextService.getWorkspace(); + + if (workspace) { + + if (target === ConfigurationTarget.WORKSPACE) { + return this.contextService.hasMultiFolderWorkspace() ? workspace.configuration : this.toResource(relativePath, workspace.roots[0]); + } + + if (target === ConfigurationTarget.FOLDER && this.contextService.hasMultiFolderWorkspace()) { + if (resource) { + const root = this.contextService.getRoot(resource); + if (root) { + return this.toResource(relativePath, root); + } + } + } + } + return null; + } + + private toResource(relativePath: string, root: URI): URI { + return URI.file(paths.join(root.fsPath, relativePath)); + } +} diff --git a/src/vs/workbench/services/configuration/node/jsonEditingService.ts b/src/vs/workbench/services/configuration/node/jsonEditingService.ts new file mode 100644 index 0000000000..aa0810a235 --- /dev/null +++ b/src/vs/workbench/services/configuration/node/jsonEditingService.ts @@ -0,0 +1,134 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import * as json from 'vs/base/common/json'; +import * as encoding from 'vs/base/node/encoding'; +import * as strings from 'vs/base/common/strings'; +import { setProperty } from 'vs/base/common/jsonEdit'; +import { Queue } from 'vs/base/common/async'; +import { Edit } from 'vs/base/common/jsonFormatter'; +import { IReference } from 'vs/base/common/lifecycle'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { IJSONEditingService, IJSONValue, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; + +export class JSONEditingService implements IJSONEditingService { + + public _serviceBrand: any; + + private queue: Queue; + + constructor( + @IFileService private fileService: IFileService, + @ITextModelService private textModelResolverService: ITextModelService, + @ITextFileService private textFileService: ITextFileService + ) { + this.queue = new Queue(); + } + + write(resource: URI, value: IJSONValue, save: boolean): TPromise { + return this.queue.queue(() => this.doWriteConfiguration(resource, value, save)); // queue up writes to prevent race conditions + } + + private doWriteConfiguration(resource: URI, value: IJSONValue, save: boolean): TPromise { + return this.resolveAndValidate(resource, save) + .then(reference => this.writeToBuffer(reference.object.textEditorModel, value)); + } + + private writeToBuffer(model: editorCommon.IModel, value: IJSONValue): TPromise { + const edit = this.getEdits(model, value)[0]; + if (this.applyEditsToBuffer(edit, model)) { + return this.textFileService.save(model.uri); + } + return TPromise.as(null); + } + + private applyEditsToBuffer(edit: Edit, model: editorCommon.IModel): boolean { + const startPosition = model.getPositionAt(edit.offset); + const endPosition = model.getPositionAt(edit.offset + edit.length); + const range = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column); + let currentText = model.getValueInRange(range); + if (edit.content !== currentText) { + const editOperation = currentText ? EditOperation.replace(range, edit.content) : EditOperation.insert(startPosition, edit.content); + model.pushEditOperations([new Selection(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column)], [editOperation], () => []); + return true; + } + return false; + } + + private getEdits(model: editorCommon.IModel, configurationValue: IJSONValue): Edit[] { + const { tabSize, insertSpaces } = model.getOptions(); + const eol = model.getEOL(); + const { key, value } = configurationValue; + + // Without key, the entire settings file is being replaced, so we just use JSON.stringify + if (!key) { + const content = JSON.stringify(value, null, insertSpaces ? strings.repeat(' ', tabSize) : '\t'); + return [{ + content, + length: content.length, + offset: 0 + }]; + } + + return setProperty(model.getValue(), [key], value, { tabSize, insertSpaces, eol }); + } + + private resolveModelReference(resource: URI): TPromise> { + return this.fileService.existsFile(resource) + .then(exists => { + const result = exists ? TPromise.as(null) : this.fileService.updateContent(resource, '{}', { encoding: encoding.UTF8 }); + return result.then(() => this.textModelResolverService.createModelReference(resource)); + }); + } + + private hasParseErrors(model: editorCommon.IModel): boolean { + const parseErrors: json.ParseError[] = []; + json.parse(model.getValue(), parseErrors, { allowTrailingComma: true }); + return parseErrors.length > 0; + } + + private resolveAndValidate(resource: URI, checkDirty: boolean): TPromise> { + return this.resolveModelReference(resource) + .then(reference => { + const model = reference.object.textEditorModel; + + if (this.hasParseErrors(model)) { + return this.wrapError>(JSONEditingErrorCode.ERROR_INVALID_FILE); + } + + // Target cannot be dirty if not writing into buffer + if (checkDirty && this.textFileService.isDirty(resource)) { + return this.wrapError>(JSONEditingErrorCode.ERROR_FILE_DIRTY); + } + return reference; + }); + } + + private wrapError(code: JSONEditingErrorCode): TPromise { + const message = this.toErrorMessage(code); + return TPromise.wrapError(new JSONEditingError(message, code)); + } + + private toErrorMessage(error: JSONEditingErrorCode): string { + switch (error) { + // User issues + case JSONEditingErrorCode.ERROR_INVALID_FILE: { + return nls.localize('errorInvalidFile', "Unable to write into the file. Please open the file to correct errors/warnings in the file and try again."); + }; + case JSONEditingErrorCode.ERROR_FILE_DIRTY: { + return nls.localize('errorFileDirty', "Unable to write into the file because the file is dirty. Please save the file and try again."); + }; + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts new file mode 100644 index 0000000000..98e11af1fd --- /dev/null +++ b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { FolderConfigurationModel, ScopedConfigurationModel, FolderSettingsModel } from 'vs/workbench/services/configuration/common/configurationModels'; +import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; + +suite('ConfigurationService - Model', () => { + + test('Test scoped configs are undefined', () => { + const settingsConfig = new FolderSettingsModel(JSON.stringify({ + awesome: true + })); + + const testObject = new FolderConfigurationModel(settingsConfig, [], ConfigurationScope.WINDOW); + + assert.equal(testObject.getContentsFor('task'), undefined); + }); + + test('Test consolidate (settings and tasks)', () => { + const settingsConfig = new FolderSettingsModel(JSON.stringify({ + awesome: true + })); + + const tasksConfig = new ScopedConfigurationModel(JSON.stringify({ + awesome: false + }), '', 'tasks'); + + const expected = { + awesome: true, + tasks: { + awesome: false + } + }; + + assert.deepEqual(new FolderConfigurationModel(settingsConfig, [tasksConfig], ConfigurationScope.WINDOW).contents, expected); + }); + + test('Test consolidate (settings and launch)', () => { + const settingsConfig = new FolderSettingsModel(JSON.stringify({ + awesome: true + })); + + const launchConfig = new ScopedConfigurationModel(JSON.stringify({ + awesome: false + }), '', 'launch'); + + const expected = { + awesome: true, + launch: { + awesome: false + } + }; + + assert.deepEqual(new FolderConfigurationModel(settingsConfig, [launchConfig], ConfigurationScope.WINDOW).contents, expected); + }); + + test('Test consolidate (settings and launch and tasks) - launch/tasks wins over settings file', () => { + const settingsConfig = new FolderSettingsModel(JSON.stringify({ + awesome: true, + launch: { + launchConfig: 'defined', + otherLaunchConfig: 'alsoDefined' + }, + tasks: { + taskConfig: 'defined', + otherTaskConfig: 'alsoDefined' + } + })); + + const tasksConfig = new ScopedConfigurationModel(JSON.stringify({ + taskConfig: 'overwritten', + }), '', 'tasks'); + + const launchConfig = new ScopedConfigurationModel(JSON.stringify({ + launchConfig: 'overwritten', + }), '', 'launch'); + + const expected = { + awesome: true, + launch: { + launchConfig: 'overwritten', + otherLaunchConfig: 'alsoDefined' + }, + tasks: { + taskConfig: 'overwritten', + otherTaskConfig: 'alsoDefined' + } + }; + + assert.deepEqual(new FolderConfigurationModel(settingsConfig, [launchConfig, tasksConfig], ConfigurationScope.WINDOW).contents, expected); + assert.deepEqual(new FolderConfigurationModel(settingsConfig, [tasksConfig, launchConfig], ConfigurationScope.WINDOW).contents, expected); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/test/node/configuration.test.ts b/src/vs/workbench/services/configuration/test/node/configuration.test.ts new file mode 100644 index 0000000000..e29b0851a5 --- /dev/null +++ b/src/vs/workbench/services/configuration/test/node/configuration.test.ts @@ -0,0 +1,464 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import assert = require('assert'); +import os = require('os'); +import path = require('path'); +import fs = require('fs'); +import * as sinon from 'sinon'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { parseArgs } from 'vs/platform/environment/node/argv'; +import extfs = require('vs/base/node/extfs'); +import uuid = require('vs/base/common/uuid'); +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { WorkspaceServiceImpl, WorkspaceService } from 'vs/workbench/services/configuration/node/configuration'; +import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; + +class SettingsTestEnvironmentService extends EnvironmentService { + + constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome) { + super(args, _execPath); + } + + get appSettingsPath(): string { return this.customAppSettingsHome; } +} + +suite('WorkspaceConfigurationService - Node', () => { + + function createWorkspace(callback: (workspaceDir: string, globalSettingsFile: string, cleanUp: (callback: () => void) => void) => void): void { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const workspaceDir = path.join(parentDir, 'workspaceconfig', id); + // {{SQL CARBON EDIT}} + const workspaceSettingsDir = path.join(workspaceDir, '.sqlops'); + const globalSettingsFile = path.join(workspaceDir, 'config.json'); + + extfs.mkdirp(workspaceSettingsDir, 493, (error) => { + callback(workspaceDir, globalSettingsFile, (callback) => extfs.del(parentDir, os.tmpdir(), () => { }, callback)); + }); + } + + function createService(workspaceDir: string, globalSettingsFile: string): TPromise { + const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); + const service = new WorkspaceServiceImpl(workspaceDir, environmentService, null); + + return service.initialize().then(() => service); + } + + test('defaults', (done: () => void) => { + interface ITestSetting { + workspace: { + service: { + testSetting: string; + } + }; + } + + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_test_workspace', + 'type': 'object', + 'properties': { + 'workspace.service.testSetting': { + 'type': 'string', + 'default': 'isSet' + } + } + }); + + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + const config = service.getConfiguration(); + assert.equal(config.workspace.service.testSetting, 'isSet'); + + service.dispose(); + + cleanUp(done); + }); + }); + }); + + test('globals', (done: () => void) => { + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.tabs": true }'); + + service.reloadConfiguration().then(() => { + const config = service.getConfiguration<{ testworkbench: { editor: { tabs: boolean } } }>(); + assert.equal(config.testworkbench.editor.tabs, true); + + service.dispose(); + + cleanUp(done); + }); + }); + }); + }); + + test('reload configuration emits events', (done: () => void) => { + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.tabs": true }'); + + return service.initialize().then(() => { + service.onDidUpdateConfiguration(event => { + const config = service.getConfiguration<{ testworkbench: { editor: { tabs: boolean } } }>(); + assert.equal(config.testworkbench.editor.tabs, false); + + service.dispose(); + + cleanUp(done); + }); + + fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.tabs": false }'); + + // this has to trigger the event since the config changes + service.reloadConfiguration().done(); + }); + + }); + }); + }); + + test('globals override defaults', (done: () => void) => { + interface ITestSetting { + workspace: { + service: { + testSetting: string; + } + }; + } + + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_test_workspace', + 'type': 'object', + 'properties': { + 'workspace.service.testSetting': { + 'type': 'string', + 'default': 'isSet' + } + } + }); + + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + fs.writeFileSync(globalSettingsFile, '{ "workspace.service.testSetting": "isChanged" }'); + + service.reloadConfiguration().then(() => { + const config = service.getConfiguration(); + assert.equal(config.workspace.service.testSetting, 'isChanged'); + + service.dispose(); + + cleanUp(done); + }); + }); + }); + }); + + test('workspace settings', (done: () => void) => { + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + // {{SQL CARBON EDIT}} + fs.writeFileSync(path.join(workspaceDir, '.sqlops', 'settings.json'), '{ "testworkbench.editor.icons": true }'); + + service.reloadConfiguration().then(() => { + const config = service.getConfiguration<{ testworkbench: { editor: { icons: boolean } } }>(); + assert.equal(config.testworkbench.editor.icons, true); + + service.dispose(); + + cleanUp(done); + }); + }); + }); + }); + + test('workspace settings override user settings', (done: () => void) => { + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.icons": false, "testworkbench.other.setting": true }'); + // {{SQL CARBON EDIT}} + fs.writeFileSync(path.join(workspaceDir, '.sqlops', 'settings.json'), '{ "testworkbench.editor.icons": true }'); + + service.reloadConfiguration().then(() => { + const config = service.getConfiguration<{ testworkbench: { editor: { icons: boolean }, other: { setting: string } } }>(); + assert.equal(config.testworkbench.editor.icons, true); + assert.equal(config.testworkbench.other.setting, true); + + service.dispose(); + + cleanUp(done); + }); + }); + }); + }); + + test('workspace change triggers event', (done: () => void) => { + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + service.onDidUpdateConfiguration(event => { + const config = service.getConfiguration<{ testworkbench: { editor: { icons: boolean } } }>(); + assert.equal(config.testworkbench.editor.icons, true); + assert.equal(service.getConfiguration().testworkbench.editor.icons, true); + + service.dispose(); + + cleanUp(done); + }); + + // {{SQL CARBON EDIT}} + const settingsFile = path.join(workspaceDir, '.sqlops', 'settings.json'); + fs.writeFileSync(settingsFile, '{ "testworkbench.editor.icons": true }'); + + const event = new FileChangesEvent([{ resource: URI.file(settingsFile), type: FileChangeType.ADDED }]); + service.handleWorkspaceFileEvents(event); + }); + }); + }); + + test('workspace reload should triggers event if content changed', (done: () => void) => { + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + // {{SQL CARBON EDIT}} + const settingsFile = path.join(workspaceDir, '.sqlops', 'settings.json'); + fs.writeFileSync(settingsFile, '{ "testworkbench.editor.icons": true }'); + + const target = sinon.stub(); + service.onDidUpdateConfiguration(event => target()); + + fs.writeFileSync(settingsFile, '{ "testworkbench.editor.icons": false }'); + + service.reloadConfiguration().done(() => { + assert.ok(target.calledOnce); + service.dispose(); + + cleanUp(done); + }); + }); + }); + }); + + test('workspace reload should not trigger event if nothing changed', (done: () => void) => { + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + // {{SQL CARBON EDIT}} + const settingsFile = path.join(workspaceDir, '.sqlops', 'settings.json'); + fs.writeFileSync(settingsFile, '{ "testworkbench.editor.icons": true }'); + + service.reloadConfiguration().done(() => { + const target = sinon.stub(); + service.onDidUpdateConfiguration(event => target()); + + service.reloadConfiguration().done(() => { + assert.ok(!target.called); + service.dispose(); + + cleanUp(done); + }); + }); + }); + }); + }); + + test('workspace reload should not trigger event if there is no model', (done: () => void) => { + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + const target = sinon.stub(); + service.onDidUpdateConfiguration(event => target()); + service.reloadConfiguration().done(() => { + assert.ok(!target.called); + service.dispose(); + cleanUp(done); + }); + }); + }); + }); + + + test('lookup', (done: () => void) => { + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'workspaceLookup.service.testSetting': { + 'type': 'string', + 'default': 'isSet' + } + } + }); + + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + let res = service.lookup('something.missing'); + assert.ok(!res.default); + assert.ok(!res.user); + assert.ok(!res.workspace); + assert.ok(!res.value); + + res = service.lookup('workspaceLookup.service.testSetting'); + assert.equal(res.default, 'isSet'); + assert.equal(res.value, 'isSet'); + assert.ok(!res.user); + assert.ok(!res.workspace); + + fs.writeFileSync(globalSettingsFile, '{ "workspaceLookup.service.testSetting": true }'); + + return service.reloadConfiguration().then(() => { + res = service.lookup('workspaceLookup.service.testSetting'); + assert.equal(res.default, 'isSet'); + assert.equal(res.user, true); + assert.equal(res.value, true); + assert.ok(!res.workspace); + + // {{SQL CARBON EDIT}} + const settingsFile = path.join(workspaceDir, '.sqlops', 'settings.json'); + fs.writeFileSync(settingsFile, '{ "workspaceLookup.service.testSetting": 55 }'); + + return service.reloadConfiguration().then(() => { + res = service.lookup('workspaceLookup.service.testSetting'); + assert.equal(res.default, 'isSet'); + assert.equal(res.user, true); + assert.equal(res.workspace, 55); + assert.equal(res.value, 55); + + service.dispose(); + + cleanUp(done); + }); + + }); + + }); + }); + }); + + test('keys', (done: () => void) => { + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'workspaceLookup.service.testSetting': { + 'type': 'string', + 'default': 'isSet' + } + } + }); + + function contains(array: string[], key: string): boolean { + return array.indexOf(key) >= 0; + } + + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + let keys = service.keys(); + + assert.ok(!contains(keys.default, 'something.missing')); + assert.ok(!contains(keys.user, 'something.missing')); + assert.ok(!contains(keys.workspace, 'something.missing')); + + assert.ok(contains(keys.default, 'workspaceLookup.service.testSetting')); + assert.ok(!contains(keys.user, 'workspaceLookup.service.testSetting')); + assert.ok(!contains(keys.workspace, 'workspaceLookup.service.testSetting')); + + fs.writeFileSync(globalSettingsFile, '{ "workspaceLookup.service.testSetting": true }'); + + return service.reloadConfiguration().then(() => { + keys = service.keys(); + + assert.ok(contains(keys.default, 'workspaceLookup.service.testSetting')); + assert.ok(contains(keys.user, 'workspaceLookup.service.testSetting')); + assert.ok(!contains(keys.workspace, 'workspaceLookup.service.testSetting')); + + // {{SQL CARBON EDIT}} + const settingsFile = path.join(workspaceDir, '.sqlops', 'settings.json'); + fs.writeFileSync(settingsFile, '{ "workspaceLookup.service.testSetting": 55 }'); + + return service.reloadConfiguration().then(() => { + keys = service.keys(); + + assert.ok(contains(keys.default, 'workspaceLookup.service.testSetting')); + assert.ok(contains(keys.user, 'workspaceLookup.service.testSetting')); + assert.ok(contains(keys.workspace, 'workspaceLookup.service.testSetting')); + + // {{SQL CARBON EDIT}} + const settingsFile = path.join(workspaceDir, '.sqlops', 'tasks.json'); + fs.writeFileSync(settingsFile, '{ "workspaceLookup.service.taskTestSetting": 55 }'); + + return service.reloadConfiguration().then(() => { + keys = service.keys(); + + assert.ok(!contains(keys.default, 'tasks.workspaceLookup.service.taskTestSetting')); + assert.ok(!contains(keys.user, 'tasks.workspaceLookup.service.taskTestSetting')); + assert.ok(contains(keys.workspace, 'tasks.workspaceLookup.service.taskTestSetting')); + + service.dispose(); + + cleanUp(done); + }); + }); + }); + }); + }); + }); + + test('values', (done: () => void) => { + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'workspaceLookup.service.testSetting': { + 'type': 'string', + 'default': 'isSet' + } + } + }); + + createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { + return createService(workspaceDir, globalSettingsFile).then(service => { + let values = service.values(); + let value = values['workspaceLookup.service.testSetting']; + + assert.ok(value); + assert.equal(value.default, 'isSet'); + + fs.writeFileSync(globalSettingsFile, '{ "workspaceLookup.service.testSetting": true }'); + + return service.reloadConfiguration().then(() => { + values = service.values(); + value = values['workspaceLookup.service.testSetting']; + + assert.ok(value); + assert.equal(value.user, true); + + // {{SQL CARBON EDIT}} + const settingsFile = path.join(workspaceDir, '.sqlops', 'settings.json'); + fs.writeFileSync(settingsFile, '{ "workspaceLookup.service.testSetting": 55 }'); + + return service.reloadConfiguration().then(() => { + values = service.values(); + value = values['workspaceLookup.service.testSetting']; + + assert.ok(value); + assert.equal(value.user, true); + assert.equal(value.workspace, 55); + + done(); + }); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts new file mode 100644 index 0000000000..4357664d32 --- /dev/null +++ b/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts @@ -0,0 +1,315 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as sinon from 'sinon'; +import assert = require('assert'); +import os = require('os'); +import path = require('path'); +import fs = require('fs'); +import * as json from 'vs/base/common/json'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { parseArgs } from 'vs/platform/environment/node/argv'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import extfs = require('vs/base/node/extfs'); +import { TestTextFileService, TestEditorGroupService, TestLifecycleService, TestBackupFileService } from 'vs/workbench/test/workbenchTestServices'; +import uuid = require('vs/base/common/uuid'); +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { WorkspaceService, EmptyWorkspaceServiceImpl, WorkspaceServiceImpl } from 'vs/workbench/services/configuration/node/configuration'; +import { FileService } from 'vs/workbench/services/files/node/fileService'; +import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; +import { ConfigurationTarget, ConfigurationEditingError, ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IFileService } from 'vs/platform/files/common/files'; +import { WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { IChoiceService, IMessageService } from 'vs/platform/message/common/message'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; + +class SettingsTestEnvironmentService extends EnvironmentService { + + constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome) { + super(args, _execPath); + } + + get appSettingsPath(): string { return this.customAppSettingsHome; } +} + +suite('ConfigurationEditingService', () => { + + let instantiationService: TestInstantiationService; + let testObject: ConfigurationEditingService; + let parentDir; + let workspaceDir; + let globalSettingsFile; + let workspaceSettingsDir; + let choiceService; + + suiteSetup(() => { + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationEditing.service.testSetting': { + 'type': 'string', + 'default': 'isSet' + }, + 'configurationEditing.service.testSettingTwo': { + 'type': 'string', + 'default': 'isSet' + }, + 'configurationEditing.service.testSettingThree': { + 'type': 'string', + 'default': 'isSet' + } + } + }); + }); + + setup(() => { + return setUpWorkspace() + .then(() => setUpServices()); + }); + + function setUpWorkspace(): TPromise { + return new TPromise((c, e) => { + const id = uuid.generateUuid(); + parentDir = path.join(os.tmpdir(), 'vsctests', id); + workspaceDir = path.join(parentDir, 'workspaceconfig', id); + globalSettingsFile = path.join(workspaceDir, 'config.json'); + // {{SQL CARBON EDIT}} + workspaceSettingsDir = path.join(workspaceDir, '.sqlops'); + extfs.mkdirp(workspaceSettingsDir, 493, (error) => { + if (error) { + e(error); + } else { + c(null); + } + }); + }); + } + + function setUpServices(noWorkspace: boolean = false): TPromise { + // Clear services if they are already created + clearServices(); + + instantiationService = new TestInstantiationService(); + const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); + instantiationService.stub(IEnvironmentService, environmentService); + const workspacesService = instantiationService.stub(IWorkspacesService, {}); + const workspaceService = noWorkspace ? new EmptyWorkspaceServiceImpl(environmentService) : new WorkspaceServiceImpl(workspaceDir, environmentService, workspacesService); + instantiationService.stub(IWorkspaceContextService, workspaceService); + instantiationService.stub(IConfigurationService, workspaceService); + instantiationService.stub(ILifecycleService, new TestLifecycleService()); + instantiationService.stub(IEditorGroupService, new TestEditorGroupService()); + instantiationService.stub(ITelemetryService, NullTelemetryService); + instantiationService.stub(IModeService, ModeServiceImpl); + instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); + instantiationService.stub(IFileService, new FileService(workspaceService, new TestConfigurationService(), { disableWatcher: true })); + instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); + instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + instantiationService.stub(IBackupFileService, new TestBackupFileService()); + choiceService = instantiationService.stub(IChoiceService, { + choose: (severity, message, options, cancelId): TPromise => { + return TPromise.as(cancelId); + } + }); + instantiationService.stub(IMessageService, { + show: (severity, message, options, cancelId): void => { } + }); + + testObject = instantiationService.createInstance(ConfigurationEditingService); + return workspaceService.initialize(); + } + + teardown(() => { + clearServices(); + return clearWorkspace(); + }); + + function clearServices(): void { + if (instantiationService) { + const configuraitonService = instantiationService.get(IConfigurationService); + if (configuraitonService) { + configuraitonService.dispose(); + } + instantiationService = null; + } + } + + function clearWorkspace(): TPromise { + return new TPromise((c, e) => { + if (parentDir) { + extfs.del(parentDir, os.tmpdir(), () => c(null), () => c(null)); + } else { + c(null); + } + }).then(() => parentDir = null); + } + + test('errors cases - invalid key', () => { + return testObject.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'unknown.key', value: 'value' }) + .then(() => assert.fail('Should fail with ERROR_UNKNOWN_KEY'), + (error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY)); + }); + + test('errors cases - invalid target', () => { + return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'tasks.something', value: 'value' }) + .then(() => assert.fail('Should fail with ERROR_INVALID_TARGET'), + (error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_INVALID_USER_TARGET)); + }); + + test('errors cases - no workspace', () => { + return setUpServices(true) + .then(() => testObject.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'configurationEditing.service.testSetting', value: 'value' })) + .then(() => assert.fail('Should fail with ERROR_NO_WORKSPACE_OPENED'), + (error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED)); + }); + + test('errors cases - invalid configuration', () => { + fs.writeFileSync(globalSettingsFile, ',,,,,,,,,,,,,,'); + return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }) + .then(() => assert.fail('Should fail with ERROR_INVALID_CONFIGURATION'), + (error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION)); + }); + + test('errors cases - dirty', () => { + instantiationService.stub(ITextFileService, 'isDirty', true); + return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }) + .then(() => assert.fail('Should fail with ERROR_CONFIGURATION_FILE_DIRTY error.'), + (error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY)); + }); + + test('dirty error is not thrown if not asked to save', () => { + instantiationService.stub(ITextFileService, 'isDirty', true); + return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotSave: true }) + .then(() => null, error => assert.fail('Should not fail.')); + }); + + test('do not notify error', () => { + instantiationService.stub(ITextFileService, 'isDirty', true); + const target = sinon.stub(); + instantiationService.stubPromise(IChoiceService, 'choose', target); + return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotNotifyError: true }) + .then(() => assert.fail('Should fail with ERROR_CONFIGURATION_FILE_DIRTY error.'), + (error: ConfigurationEditingError) => { + assert.equal(false, target.calledOnce); + assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY); + }); + }); + + test('write one setting - empty file', () => { + return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }) + .then(() => { + const contents = fs.readFileSync(globalSettingsFile).toString('utf8'); + const parsed = json.parse(contents); + assert.equal(parsed['configurationEditing.service.testSetting'], 'value'); + assert.equal(instantiationService.get(IConfigurationService).lookup('configurationEditing.service.testSetting').value, 'value'); + }); + }); + + test('write one setting - existing file', () => { + fs.writeFileSync(globalSettingsFile, '{ "my.super.setting": "my.super.value" }'); + return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }) + .then(() => { + const contents = fs.readFileSync(globalSettingsFile).toString('utf8'); + const parsed = json.parse(contents); + assert.equal(parsed['configurationEditing.service.testSetting'], 'value'); + assert.equal(parsed['my.super.setting'], 'my.super.value'); + + const configurationService = instantiationService.get(IConfigurationService); + assert.equal(configurationService.lookup('configurationEditing.service.testSetting').value, 'value'); + assert.equal(configurationService.lookup('my.super.setting').value, 'my.super.value'); + }); + }); + + test('write workspace standalone setting - empty file', () => { + return testObject.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'tasks.service.testSetting', value: 'value' }) + .then(() => { + const target = path.join(workspaceDir, WORKSPACE_STANDALONE_CONFIGURATIONS['tasks']); + const contents = fs.readFileSync(target).toString('utf8'); + const parsed = json.parse(contents); + assert.equal(parsed['service.testSetting'], 'value'); + const configurationService = instantiationService.get(IConfigurationService); + assert.equal(configurationService.lookup('tasks.service.testSetting').value, 'value'); + }); + }); + + test('write workspace standalone setting - existing file', () => { + const target = path.join(workspaceDir, WORKSPACE_STANDALONE_CONFIGURATIONS['launch']); + fs.writeFileSync(target, '{ "my.super.setting": "my.super.value" }'); + return testObject.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'launch.service.testSetting', value: 'value' }) + .then(() => { + const contents = fs.readFileSync(target).toString('utf8'); + const parsed = json.parse(contents); + assert.equal(parsed['service.testSetting'], 'value'); + assert.equal(parsed['my.super.setting'], 'my.super.value'); + + const configurationService = instantiationService.get(IConfigurationService); + assert.equal(configurationService.lookup('launch.service.testSetting').value, 'value'); + assert.equal(configurationService.lookup('launch.my.super.setting').value, 'my.super.value'); + }); + }); + + test('write workspace standalone setting - empty file - full JSON', () => { + return testObject.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'tasks', value: { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] } }) + .then(() => { + const target = path.join(workspaceDir, WORKSPACE_STANDALONE_CONFIGURATIONS['tasks']); + const contents = fs.readFileSync(target).toString('utf8'); + const parsed = json.parse(contents); + + assert.equal(parsed['version'], '1.0.0'); + assert.equal(parsed['tasks'][0]['taskName'], 'myTask'); + }); + }); + + test('write workspace standalone setting - existing file - full JSON', () => { + const target = path.join(workspaceDir, WORKSPACE_STANDALONE_CONFIGURATIONS['launch']); + fs.writeFileSync(target, '{ "my.super.setting": "my.super.value" }'); + return testObject.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'tasks', value: { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] } }) + .then(() => { + const target = path.join(workspaceDir, WORKSPACE_STANDALONE_CONFIGURATIONS['tasks']); + const contents = fs.readFileSync(target).toString('utf8'); + const parsed = json.parse(contents); + + assert.equal(parsed['version'], '1.0.0'); + assert.equal(parsed['tasks'][0]['taskName'], 'myTask'); + }); + }); + + test('write workspace standalone setting - existing file with JSON errors - full JSON', () => { + const target = path.join(workspaceDir, WORKSPACE_STANDALONE_CONFIGURATIONS['launch']); + fs.writeFileSync(target, '{ "my.super.setting": '); // invalid JSON + return testObject.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'tasks', value: { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] } }) + .then(() => { + const target = path.join(workspaceDir, WORKSPACE_STANDALONE_CONFIGURATIONS['tasks']); + const contents = fs.readFileSync(target).toString('utf8'); + const parsed = json.parse(contents); + + assert.equal(parsed['version'], '1.0.0'); + assert.equal(parsed['tasks'][0]['taskName'], 'myTask'); + }); + }); +}); diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts new file mode 100644 index 0000000000..ba7afe474d --- /dev/null +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import uri from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IConfigurationResolverService = createDecorator('configurationResolverService'); + +export interface IConfigurationResolverService { + _serviceBrand: any; + + // TODO@Isidor improve this API + resolve(root: uri, value: string): string; + resolve(root: uri, value: string[]): string[]; + resolve(root: uri, value: IStringDictionary): IStringDictionary; + resolveAny(root: uri, value: T): T; + resolveInteractiveVariables(configuration: any, interactiveVariablesMap: { [key: string]: string }): TPromise; +} diff --git a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts new file mode 100644 index 0000000000..c21750dc5b --- /dev/null +++ b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts @@ -0,0 +1,263 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import uri from 'vs/base/common/uri'; +import * as paths from 'vs/base/common/paths'; +import * as types from 'vs/base/common/types'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { sequence } from 'vs/base/common/async'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { toResource } from 'vs/workbench/common/editor'; + +export class ConfigurationResolverService implements IConfigurationResolverService { + _serviceBrand: any; + private _execPath: string; + private _workspaceRoot: string; + + constructor( + envVariables: { [key: string]: string }, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEnvironmentService environmentService: IEnvironmentService, + @IConfigurationService private configurationService: IConfigurationService, + @ICommandService private commandService: ICommandService, + ) { + this._execPath = environmentService.execPath; + Object.keys(envVariables).forEach(key => { + this[`env:${key}`] = envVariables[key]; + }); + } + + private get execPath(): string { + return this._execPath; + } + + private get cwd(): string { + return this.workspaceRoot; + } + + private get workspaceRoot(): string { + return this._workspaceRoot; + } + + private get workspaceRootFolderName(): string { + return this.workspaceRoot ? paths.basename(this.workspaceRoot) : ''; + } + + private get file(): string { + return this.getFilePath(); + } + + private get relativeFile(): string { + return (this.workspaceRoot) ? paths.relative(this.workspaceRoot, this.file) : this.file; + } + + private get fileBasename(): string { + return paths.basename(this.getFilePath()); + } + + private get fileBasenameNoExtension(): string { + const basename = this.fileBasename; + return basename.slice(0, basename.length - paths.extname(basename).length); + } + + private get fileDirname(): string { + return paths.dirname(this.getFilePath()); + } + + private get fileExtname(): string { + return paths.extname(this.getFilePath()); + } + + private get lineNumber(): string { + const activeEditor = this.editorService.getActiveEditor(); + if (activeEditor) { + const editorControl = (activeEditor.getControl()); + if (editorControl) { + const lineNumber = editorControl.getSelection().positionLineNumber; + return String(lineNumber); + } + } + + return ''; + } + + private getFilePath(): string { + let input = this.editorService.getActiveEditorInput(); + if (!input) { + return ''; + } + let fileResource = toResource(input, { filter: 'file' }); + if (!fileResource) { + return ''; + } + return paths.normalize(fileResource.fsPath, true); + } + + public resolve(root: uri, value: string): string; + public resolve(root: uri, value: string[]): string[]; + public resolve(root: uri, value: IStringDictionary): IStringDictionary; + public resolve(root: uri, value: any): any { + this._workspaceRoot = root.fsPath.toString(); + if (types.isString(value)) { + return this.resolveString(root, value); + } else if (types.isArray(value)) { + return this.resolveArray(root, value); + } else if (types.isObject(value)) { + return this.resolveLiteral(root, value); + } + + return value; + } + + public resolveAny(root: uri, value: T): T; + public resolveAny(root: uri, value: any): any { + this._workspaceRoot = root.fsPath.toString(); + if (types.isString(value)) { + return this.resolveString(root, value); + } else if (types.isArray(value)) { + return this.resolveAnyArray(root, value); + } else if (types.isObject(value)) { + return this.resolveAnyLiteral(root, value); + } + + return value; + } + + private resolveString(root: uri, value: string): string { + let regexp = /\$\{(.*?)\}/g; + const originalValue = value; + const resolvedString = value.replace(regexp, (match: string, name: string) => { + let newValue = (this)[name]; + if (types.isString(newValue)) { + return newValue; + } else { + return match && match.indexOf('env:') > 0 ? '' : match; + } + }); + + return this.resolveConfigVariable(root, resolvedString, originalValue); + } + + private resolveConfigVariable(root: uri, value: string, originalValue: string): string { + const replacer = (match: string, name: string) => { + let config = this.configurationService.getConfiguration(); + let newValue: any; + try { + const keys: string[] = name.split('.'); + if (!keys || keys.length <= 0) { + return ''; + } + while (keys.length > 1) { + const key = keys.shift(); + if (!config || !config.hasOwnProperty(key)) { + return ''; + } + config = config[key]; + } + newValue = config && config.hasOwnProperty(keys[0]) ? config[keys[0]] : ''; + } catch (e) { + return ''; + } + if (types.isString(newValue)) { + // Prevent infinite recursion and also support nested references (or tokens) + return newValue === originalValue ? '' : this.resolveString(root, newValue); + } else { + return this.resolve(root, newValue) + ''; + } + }; + + return value.replace(/\$\{config:(.+?)\}/g, replacer); + } + + private resolveLiteral(root: uri, values: IStringDictionary | string[]>): IStringDictionary | string[]> { + let result: IStringDictionary | string[]> = Object.create(null); + Object.keys(values).forEach(key => { + let value = values[key]; + result[key] = this.resolve(root, value); + }); + return result; + } + + private resolveAnyLiteral(root: uri, values: T): T; + private resolveAnyLiteral(root: uri, values: any): any { + let result: IStringDictionary | string[]> = Object.create(null); + Object.keys(values).forEach(key => { + let value = values[key]; + result[key] = this.resolveAny(root, value); + }); + return result; + } + + private resolveArray(root: uri, value: string[]): string[] { + return value.map(s => this.resolveString(root, s)); + } + + private resolveAnyArray(root: uri, value: T[]): T[]; + private resolveAnyArray(root: uri, value: any[]): any[] { + return value.map(s => this.resolveAny(root, s)); + } + + /** + * Resolve all interactive variables in configuration #6569 + */ + public resolveInteractiveVariables(configuration: any, interactiveVariablesMap: { [key: string]: string }): TPromise { + if (!configuration) { + return TPromise.as(null); + } + + // We need a map from interactive variables to keys because we only want to trigger an command once per key - + // even though it might occur multiple times in configuration #7026. + const interactiveVariablesToSubstitutes: { [interactiveVariable: string]: { object: any, key: string }[] } = {}; + const findInteractiveVariables = (object: any) => { + Object.keys(object).forEach(key => { + if (object[key] && typeof object[key] === 'object') { + findInteractiveVariables(object[key]); + } else if (typeof object[key] === 'string') { + const matches = /\${command:(.+)}/.exec(object[key]); + if (matches && matches.length === 2) { + const interactiveVariable = matches[1]; + if (!interactiveVariablesToSubstitutes[interactiveVariable]) { + interactiveVariablesToSubstitutes[interactiveVariable] = []; + } + interactiveVariablesToSubstitutes[interactiveVariable].push({ object, key }); + } + } + }); + }; + findInteractiveVariables(configuration); + let substitionCanceled = false; + + const factory: { (): TPromise }[] = Object.keys(interactiveVariablesToSubstitutes).map(interactiveVariable => { + return () => { + let commandId: string = null; + commandId = interactiveVariablesMap ? interactiveVariablesMap[interactiveVariable] : null; + if (!commandId) { + // Just launch any command if the interactive variable is not contributed by the adapter #12735 + commandId = interactiveVariable; + } + + return this.commandService.executeCommand(commandId, configuration).then(result => { + if (result) { + interactiveVariablesToSubstitutes[interactiveVariable].forEach(substitute => { + if (substitute.object[substitute.key].indexOf(`\${command:${interactiveVariable}}`) >= 0) { + substitute.object[substitute.key] = substitute.object[substitute.key].replace(`\${command:${interactiveVariable}}`, result); + } + }); + } else { + substitionCanceled = true; + } + }); + }; + }); + + return sequence(factory).then(() => substitionCanceled ? null : configuration); + } +} diff --git a/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts new file mode 100644 index 0000000000..02780e6010 --- /dev/null +++ b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts @@ -0,0 +1,365 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert = require('assert'); +import uri from 'vs/base/common/uri'; +import platform = require('vs/base/common/platform'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { IConfigurationService, getConfigurationValue, IConfigurationOverrides, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/node/configurationResolverService'; +import { TestEnvironmentService, TestEditorService } from 'vs/workbench/test/workbenchTestServices'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; + +suite('Configuration Resolver Service', () => { + let configurationResolverService: IConfigurationResolverService; + let envVariables: { [key: string]: string } = { key1: 'Value for Key1', key2: 'Value for Key2' }; + let mockCommandService: MockCommandService; + let editorService: TestEditorService; + let workspaceUri: uri; + + + setup(() => { + mockCommandService = new MockCommandService(); + editorService = new TestEditorService(); + workspaceUri = uri.parse('file:///VSCode/workspaceLocation'); + configurationResolverService = new ConfigurationResolverService(envVariables, editorService, TestEnvironmentService, new TestConfigurationService(), mockCommandService); + }); + + teardown(() => { + configurationResolverService = null; + }); + + + test('substitute one', () => { + if (platform.isWindows) { + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${workspaceRoot} xyz'), 'abc \\VSCode\\workspaceLocation xyz'); + } else { + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${workspaceRoot} xyz'), 'abc /VSCode/workspaceLocation xyz'); + } + }); + + test('workspace root folder name', () => { + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${workspaceRootFolderName} xyz'), 'abc workspaceLocation xyz'); + }); + + test('current selected line number', () => { + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${lineNumber} xyz'), `abc ${editorService.mockLineNumber} xyz`); + }); + + test('substitute many', () => { + if (platform.isWindows) { + assert.strictEqual(configurationResolverService.resolve(workspaceUri, '${workspaceRoot} - ${workspaceRoot}'), '\\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation'); + } else { + assert.strictEqual(configurationResolverService.resolve(workspaceUri, '${workspaceRoot} - ${workspaceRoot}'), '/VSCode/workspaceLocation - /VSCode/workspaceLocation'); + } + }); + + test('substitute one env variable', () => { + if (platform.isWindows) { + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${workspaceRoot} ${env:key1} xyz'), 'abc \\VSCode\\workspaceLocation Value for Key1 xyz'); + } else { + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${workspaceRoot} ${env:key1} xyz'), 'abc /VSCode/workspaceLocation Value for Key1 xyz'); + } + }); + + test('substitute many env variable', () => { + if (platform.isWindows) { + assert.strictEqual(configurationResolverService.resolve(workspaceUri, '${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), '\\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for Key1 - Value for Key2'); + } else { + assert.strictEqual(configurationResolverService.resolve(workspaceUri, '${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), '/VSCode/workspaceLocation - /VSCode/workspaceLocation Value for Key1 - Value for Key2'); + } + }); + + test('substitute one configuration variable', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo' + }, + terminal: { + integrated: { + fontFamily: 'bar' + } + } + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); + }); + + test('substitute many configuration variables', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo' + }, + terminal: { + integrated: { + fontFamily: 'bar' + } + } + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz'); + }); + + test('substitute nested configuration variables', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo ${workspaceRoot} ${config:terminal.integrated.fontFamily}' + }, + terminal: { + integrated: { + fontFamily: 'bar' + } + } + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + if (platform.isWindows) { + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo \\VSCode\\workspaceLocation bar bar xyz'); + } else { + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo /VSCode/workspaceLocation bar bar xyz'); + } + }); + + test('substitute accidental self referenced configuration variables', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo ${workspaceRoot} ${config:terminal.integrated.fontFamily} ${config:editor.fontFamily}' + }, + terminal: { + integrated: { + fontFamily: 'bar' + } + } + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + if (platform.isWindows) { + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo \\VSCode\\workspaceLocation bar bar xyz'); + } else { + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo /VSCode/workspaceLocation bar bar xyz'); + } + }); + + test('substitute one env variable and a configuration variable', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo' + }, + terminal: { + integrated: { + fontFamily: 'bar' + } + } + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + if (platform.isWindows) { + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${workspaceRoot} ${env:key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for Key1 xyz'); + } else { + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${workspaceRoot} ${env:key1} xyz'), 'abc foo /VSCode/workspaceLocation Value for Key1 xyz'); + } + }); + + test('substitute many env variable and a configuration variable', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo' + }, + terminal: { + integrated: { + fontFamily: 'bar' + } + } + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + if (platform.isWindows) { + assert.strictEqual(service.resolve(workspaceUri, '${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), 'foo bar \\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for Key1 - Value for Key2'); + } else { + assert.strictEqual(service.resolve(workspaceUri, '${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), 'foo bar /VSCode/workspaceLocation - /VSCode/workspaceLocation Value for Key1 - Value for Key2'); + } + }); + + test('mixed types of configuration variables', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo', + lineNumbers: 123, + insertSpaces: false + }, + terminal: { + integrated: { + fontFamily: 'bar' + } + }, + json: { + schemas: [ + { + fileMatch: [ + '/myfile', + '/myOtherfile' + ], + url: 'schemaURL' + } + ] + } + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:editor.lineNumbers} ${config:editor.insertSpaces} xyz'), 'abc foo 123 false xyz'); + }); + + test('configuration should not evaluate Javascript', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: { + abc: 'foo' + } + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor[\'abc\'.substr(0)]} xyz'), 'abc xyz'); + }); + + test('uses empty string as fallback', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: {} + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.abc} xyz'), 'abc xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.abc.def} xyz'), 'abc xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:panel} xyz'), 'abc xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:panel.abc} xyz'), 'abc xyz'); + }); + + test('is restricted to own properties', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: {} + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.__proto__} xyz'), 'abc xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.toString} xyz'), 'abc xyz'); + }); + + test('configuration variables with invalid accessor', () => { + let configurationService: IConfigurationService; + configurationService = new MockConfigurationService({ + editor: { + fontFamily: 'foo' + } + }); + + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:} xyz'), 'abc ${config:} xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor..fontFamily} xyz'), 'abc xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.none.none2} xyz'), 'abc xyz'); + }); + + test('interactive variable simple', () => { + const configuration = { + 'name': 'Attach to Process', + 'type': 'node', + 'request': 'attach', + 'processId': '${command:interactiveVariable1}', + 'port': 5858, + 'sourceMaps': false, + 'outDir': null + }; + const interactiveVariables = Object.create(null); + interactiveVariables['interactiveVariable1'] = 'command1'; + interactiveVariables['interactiveVariable2'] = 'command2'; + + configurationResolverService.resolveInteractiveVariables(configuration, interactiveVariables).then(resolved => { + assert.deepEqual(resolved, { + 'name': 'Attach to Process', + 'type': 'node', + 'request': 'attach', + 'processId': 'command1', + 'port': 5858, + 'sourceMaps': false, + 'outDir': null + }); + + assert.equal(1, mockCommandService.callCount); + }); + }); + + test('interactive variable complex', () => { + const configuration = { + 'name': 'Attach to Process', + 'type': 'node', + 'request': 'attach', + 'processId': '${command:interactiveVariable1}', + 'port': '${command:interactiveVariable2}', + 'sourceMaps': false, + 'outDir': 'src/${command:interactiveVariable2}', + 'env': { + 'processId': '__${command:interactiveVariable2}__', + } + }; + const interactiveVariables = Object.create(null); + interactiveVariables['interactiveVariable1'] = 'command1'; + interactiveVariables['interactiveVariable2'] = 'command2'; + + configurationResolverService.resolveInteractiveVariables(configuration, interactiveVariables).then(resolved => { + assert.deepEqual(resolved, { + 'name': 'Attach to Process', + 'type': 'node', + 'request': 'attach', + 'processId': 'command1', + 'port': 'command2', + 'sourceMaps': false, + 'outDir': 'src/command2', + 'env': { + 'processId': '__command2__', + } + }); + + assert.equal(2, mockCommandService.callCount); + }); + }); +}); + + +class MockConfigurationService implements IConfigurationService { + public _serviceBrand: any; + public serviceId = IConfigurationService; + public constructor(private configuration: any = {}) { } + public reloadConfiguration(section?: string): TPromise { return TPromise.as(this.getConfiguration()); } + public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { return { value: getConfigurationValue(this.getConfiguration(), key), default: getConfigurationValue(this.getConfiguration(), key), user: getConfigurationValue(this.getConfiguration(), key), workspace: void 0, folder: void 0 }; } + public keys() { return { default: [], user: [], workspace: [], folder: [] }; } + public values() { return {}; } + public getConfiguration(): any { return this.configuration; } + public getConfigurationData(): any { return null; } + public onDidUpdateConfiguration() { return { dispose() { } }; } +} + +class MockCommandService implements ICommandService { + + public _serviceBrand: any; + public callCount = 0; + + onWillExecuteCommand = () => ({ dispose: () => { } }); + + public executeCommand(commandId: string, ...args: any[]): TPromise { + this.callCount++; + return TPromise.as(commandId); + } +} diff --git a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts new file mode 100644 index 0000000000..690d41c74d --- /dev/null +++ b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import severity from 'vs/base/common/severity'; +import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import dom = require('vs/base/browser/dom'); +import { IContextMenuService, IContextMenuDelegate, ContextSubMenu, IEvent } from 'vs/platform/contextview/browser/contextView'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; + +import { remote, webFrame } from 'electron'; +import { unmnemonicLabel } from 'vs/base/common/labels'; + +export class ContextMenuService implements IContextMenuService { + + public _serviceBrand: any; + + constructor( + @IMessageService private messageService: IMessageService, + @ITelemetryService private telemetryService: ITelemetryService, + @IKeybindingService private keybindingService: IKeybindingService + ) { + } + + public showContextMenu(delegate: IContextMenuDelegate): void { + delegate.getActions().then(actions => { + if (!actions.length) { + return TPromise.as(null); + } + + return TPromise.timeout(0).then(() => { // https://github.com/Microsoft/vscode/issues/3638 + const menu = this.createMenu(delegate, actions); + const anchor = delegate.getAnchor(); + let x: number, y: number; + + if (dom.isHTMLElement(anchor)) { + let elementPosition = dom.getDomNodePagePosition(anchor); + + x = elementPosition.left; + y = elementPosition.top + elementPosition.height; + } else { + const pos = <{ x: number; y: number; }>anchor; + x = pos.x; + y = pos.y; + } + + let zoom = webFrame.getZoomFactor(); + x *= zoom; + y *= zoom; + + menu.popup(remote.getCurrentWindow(), Math.floor(x), Math.floor(y)); + if (delegate.onHide) { + delegate.onHide(undefined); + } + }); + }); + } + + private createMenu(delegate: IContextMenuDelegate, entries: (IAction | ContextSubMenu)[]): Electron.Menu { + const menu = new remote.Menu(); + const actionRunner = delegate.actionRunner || new ActionRunner(); + + entries.forEach(e => { + if (e instanceof Separator) { + menu.append(new remote.MenuItem({ type: 'separator' })); + } else if (e instanceof ContextSubMenu) { + const submenu = new remote.MenuItem({ + submenu: this.createMenu(delegate, e.entries), + label: unmnemonicLabel(e.label) + }); + + menu.append(submenu); + } else { + const options: Electron.MenuItemOptions = { + label: unmnemonicLabel(e.label), + checked: !!e.checked || !!e.radio, + type: !!e.checked ? 'checkbox' : !!e.radio ? 'radio' : void 0, + enabled: !!e.enabled, + click: (menuItem, win, event) => { + this.runAction(actionRunner, e, delegate, event); + } + }; + + const keybinding = !!delegate.getKeyBinding ? delegate.getKeyBinding(e) : this.keybindingService.lookupKeybinding(e.id); + if (keybinding) { + const electronAccelerator = keybinding.getElectronAccelerator(); + if (electronAccelerator) { + options.accelerator = electronAccelerator; + } else { + const label = keybinding.getLabel(); + if (label) { + options.label = `${options.label} [${label}]`; + } + } + } + + const item = new remote.MenuItem(options); + + menu.append(item); + } + }); + + return menu; + } + + private runAction(actionRunner: IActionRunner, actionToRun: IAction, delegate: IContextMenuDelegate, event: IEvent): void { + this.telemetryService.publicLog('workbenchActionExecuted', { id: actionToRun.id, from: 'contextMenu' }); + + const context = delegate.getActionsContext ? delegate.getActionsContext(event) : event; + const res = actionRunner.run(actionToRun, context) || TPromise.as(null); + + res.done(null, e => this.messageService.show(severity.Error, e)); + } +} diff --git a/src/vs/workbench/services/crashReporter/common/crashReporterService.ts b/src/vs/workbench/services/crashReporter/common/crashReporterService.ts new file mode 100644 index 0000000000..22bd51871c --- /dev/null +++ b/src/vs/workbench/services/crashReporter/common/crashReporterService.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ICrashReporterService = createDecorator('crashReporterService'); + +export const TELEMETRY_SECTION_ID = 'telemetry'; + +export interface ICrashReporterConfig { + enableCrashReporter: boolean; +} + +const configurationRegistry = Registry.as(Extensions.Configuration); +configurationRegistry.registerConfiguration({ + 'id': TELEMETRY_SECTION_ID, + 'order': 110, + title: nls.localize('telemetryConfigurationTitle', "Telemetry"), + 'type': 'object', + 'properties': { + 'telemetry.enableCrashReporter': { + 'type': 'boolean', + 'description': nls.localize('telemetry.enableCrashReporting', "Enable crash reports to be sent to Microsoft.\nThis option requires restart to take effect."), + 'default': true + } + } +}); + +export interface ICrashReporterService { + _serviceBrand: any; + getChildProcessStartOptions(processName: string): Electron.CrashReporterStartOptions; +} + +export const NullCrashReporterService: ICrashReporterService = { + _serviceBrand: undefined, + getChildProcessStartOptions(processName: string) { return undefined; } +}; \ No newline at end of file diff --git a/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts b/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts new file mode 100644 index 0000000000..de4997d19f --- /dev/null +++ b/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { assign, clone } from 'vs/base/common/objects'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { crashReporter } from 'electron'; +import product from 'vs/platform/node/product'; +import pkg from 'vs/platform/node/package'; +import * as os from 'os'; +import { ICrashReporterService, TELEMETRY_SECTION_ID, ICrashReporterConfig } from 'vs/workbench/services/crashReporter/common/crashReporterService'; +import { isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; + +export class CrashReporterService implements ICrashReporterService { + + public _serviceBrand: any; + + private options: Electron.CrashReporterStartOptions; + private isEnabled: boolean; + + constructor( + @ITelemetryService private telemetryService: ITelemetryService, + @IWindowsService private windowsService: IWindowsService, + @IConfigurationService configurationService: IConfigurationService + ) { + const config = configurationService.getConfiguration(TELEMETRY_SECTION_ID); + this.isEnabled = !!config.enableCrashReporter; + + if (this.isEnabled) { + this.startCrashReporter(); + } + } + + private startCrashReporter(): void { + + // base options with product info + this.options = { + companyName: product.crashReporter.companyName, + productName: product.crashReporter.productName, + submitURL: this.getSubmitURL(), + extra: { + vscode_version: pkg.version, + vscode_commit: product.commit + } + }; + + // mixin telemetry info + this.telemetryService.getTelemetryInfo() + .then(info => { + assign(this.options.extra, { + vscode_sessionId: info.sessionId, + vscode_machineId: info.machineId + }); + + // start crash reporter right here + crashReporter.start(clone(this.options)); + + // start crash reporter in the main process + return this.windowsService.startCrashReporter(this.options); + }) + .done(null, onUnexpectedError); + } + + private getSubmitURL(): string { + let submitURL: string; + if (isWindows) { + submitURL = product.hockeyApp[`win32-${process.arch}`]; + } else if (isMacintosh) { + submitURL = product.hockeyApp.darwin; + } else if (isLinux) { + submitURL = product.hockeyApp[`linux-${process.arch}`]; + } + + return submitURL; + } + + public getChildProcessStartOptions(name: string): Electron.CrashReporterStartOptions { + + // Experimental crash reporting support for child processes on Mac only for now + if (this.isEnabled && isMacintosh) { + const childProcessOptions = clone(this.options); + childProcessOptions.extra.processName = name; + childProcessOptions.crashesDirectory = os.tmpdir(); + + return childProcessOptions; + } + + return void 0; + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts new file mode 100644 index 0000000000..04e2b32c3e --- /dev/null +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -0,0 +1,363 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import network = require('vs/base/common/network'); +import { Registry } from 'vs/platform/registry/common/platform'; +import { basename, dirname } from 'vs/base/common/paths'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorInput, EditorOptions, TextEditorOptions, IEditorRegistry, Extensions, SideBySideEditorInput, IFileEditorInput, IFileInputFactory } from 'vs/workbench/common/editor'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IEditor, IResourceInput, IResourceDiffInput, IResourceSideBySideInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import nls = require('vs/nls'); +import { getPathLabel } from 'vs/base/common/labels'; +import { ResourceMap } from 'vs/base/common/map'; +import { once } from 'vs/base/common/event'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + +export interface IEditorPart { + openEditor(input?: IEditorInput, options?: IEditorOptions | ITextEditorOptions, sideBySide?: boolean): TPromise; + openEditor(input?: IEditorInput, options?: IEditorOptions | ITextEditorOptions, position?: Position): TPromise; + openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions | ITextEditorOptions }[]): TPromise; + replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions | ITextEditorOptions }[], position?: Position): TPromise; + closeEditor(position: Position, input: IEditorInput): TPromise; + closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise; + closeAllEditors(except?: Position): TPromise; + getActiveEditor(): BaseEditor; + getVisibleEditors(): IEditor[]; + getActiveEditorInput(): IEditorInput; +} + +type ICachedEditorInput = ResourceEditorInput | IFileEditorInput; + +export class WorkbenchEditorService implements IWorkbenchEditorService { + + public _serviceBrand: any; + + private static CACHE: ResourceMap = new ResourceMap(); + + private editorPart: IEditorPart | IWorkbenchEditorService; + private fileInputFactory: IFileInputFactory; + + constructor( + editorPart: IEditorPart | IWorkbenchEditorService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IInstantiationService private instantiationService: IInstantiationService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + this.editorPart = editorPart; + this.fileInputFactory = Registry.as(Extensions.Editors).getFileInputFactory(); + } + + public getActiveEditor(): IEditor { + return this.editorPart.getActiveEditor(); + } + + public getActiveEditorInput(): IEditorInput { + return this.editorPart.getActiveEditorInput(); + } + + public getVisibleEditors(): IEditor[] { + return this.editorPart.getVisibleEditors(); + } + + public isVisible(input: IEditorInput, includeSideBySide: boolean): boolean { + if (!input) { + return false; + } + + return this.getVisibleEditors().some(editor => { + if (!editor.input) { + return false; + } + + if (input.matches(editor.input)) { + return true; + } + + if (includeSideBySide && editor.input instanceof SideBySideEditorInput) { + const sideBySideInput = editor.input; + return input.matches(sideBySideInput.master) || input.matches(sideBySideInput.details); + } + + return false; + }); + } + + public openEditor(input: IEditorInput, options?: IEditorOptions, sideBySide?: boolean): TPromise; + public openEditor(input: IEditorInput, options?: IEditorOptions, position?: Position): TPromise; + public openEditor(input: IResourceInputType, position?: Position): TPromise; + public openEditor(input: IResourceInputType, sideBySide?: boolean): TPromise; + public openEditor(input: any, arg2?: any, arg3?: any): TPromise { + if (!input) { + return TPromise.as(null); + } + + // Workbench Input Support + if (input instanceof EditorInput) { + return this.doOpenEditor(input, this.toOptions(arg2), arg3); + } + + // Support opening foreign resources (such as a http link that points outside of the workbench) + const resourceInput = input; + if (resourceInput.resource instanceof URI) { + const schema = resourceInput.resource.scheme; + if (schema === network.Schemas.http || schema === network.Schemas.https) { + window.open(resourceInput.resource.toString(true)); + + return TPromise.as(null); + } + } + + // Untyped Text Editor Support (required for code that uses this service below workbench level) + const textInput = input; + const typedInput = this.createInput(textInput); + if (typedInput) { + return this.doOpenEditor(typedInput, TextEditorOptions.from(textInput), arg2); + } + + return TPromise.as(null); + } + + private toOptions(options?: IEditorOptions | EditorOptions): EditorOptions { + if (!options || options instanceof EditorOptions) { + return options as EditorOptions; + } + + const textOptions: ITextEditorOptions = options; + if (!!textOptions.selection) { + return TextEditorOptions.create(options); + } + + return EditorOptions.create(options); + } + + /** + * Allow subclasses to implement their own behavior for opening editor (see below). + */ + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { + return this.editorPart.openEditor(input, options, arg3); + } + + public openEditors(editors: { input: IResourceInputType, position: Position }[]): TPromise; + public openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions }[]): TPromise; + public openEditors(editors: any[]): TPromise { + const inputs = editors.map(editor => this.createInput(editor.input)); + const typedInputs: { input: IEditorInput, position: Position, options?: EditorOptions }[] = inputs.map((input, index) => { + const options = editors[index].input instanceof EditorInput ? this.toOptions(editors[index].options) : TextEditorOptions.from(editors[index].input); + + return { + input, + options, + position: editors[index].position + }; + }); + + return this.editorPart.openEditors(typedInputs); + } + + public replaceEditors(editors: { toReplace: IResourceInputType, replaceWith: IResourceInputType }[], position?: Position): TPromise; + public replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions }[], position?: Position): TPromise; + public replaceEditors(editors: any[], position?: Position): TPromise { + const toReplaceInputs = editors.map(editor => this.createInput(editor.toReplace)); + const replaceWithInputs = editors.map(editor => this.createInput(editor.replaceWith)); + const typedReplacements: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: EditorOptions }[] = editors.map((editor, index) => { + const options = editor.toReplace instanceof EditorInput ? this.toOptions(editor.options) : TextEditorOptions.from(editor.replaceWith); + + return { + toReplace: toReplaceInputs[index], + replaceWith: replaceWithInputs[index], + options + }; + }); + + return this.editorPart.replaceEditors(typedReplacements, position); + } + + public closeEditor(position: Position, input: IEditorInput): TPromise { + return this.doCloseEditor(position, input); + } + + protected doCloseEditor(position: Position, input: IEditorInput): TPromise { + return this.editorPart.closeEditor(position, input); + } + + public closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise { + return this.editorPart.closeEditors(position, filter); + } + + public closeAllEditors(except?: Position): TPromise { + return this.editorPart.closeAllEditors(except); + } + + public createInput(input: IEditorInput): EditorInput; + public createInput(input: IResourceInputType): EditorInput; + public createInput(input: any): IEditorInput { + + // Workbench Input Support + if (input instanceof EditorInput) { + return input; + } + + // Side by Side Support + const resourceSideBySideInput = input; + if (resourceSideBySideInput.masterResource && resourceSideBySideInput.detailResource) { + const masterInput = this.createInput({ resource: resourceSideBySideInput.masterResource }); + const detailInput = this.createInput({ resource: resourceSideBySideInput.detailResource }); + + return new SideBySideEditorInput(resourceSideBySideInput.label || masterInput.getName(), typeof resourceSideBySideInput.description === 'string' ? resourceSideBySideInput.description : masterInput.getDescription(), detailInput, masterInput); + } + + // Diff Editor Support + const resourceDiffInput = input; + if (resourceDiffInput.leftResource && resourceDiffInput.rightResource) { + const leftInput = this.createInput({ resource: resourceDiffInput.leftResource }); + const rightInput = this.createInput({ resource: resourceDiffInput.rightResource }); + const label = resourceDiffInput.label || this.toDiffLabel(resourceDiffInput.leftResource, resourceDiffInput.rightResource, this.workspaceContextService, this.environmentService); + + return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput); + } + + // Untitled file support + const untitledInput = input; + if (!untitledInput.resource || typeof untitledInput.filePath === 'string' || (untitledInput.resource instanceof URI && untitledInput.resource.scheme === UNTITLED_SCHEMA)) { + // {{SQL CARBON EDIT}} + return this.untitledEditorService.createOrGet(untitledInput.filePath ? URI.file(untitledInput.filePath) : untitledInput.resource, 'sql', untitledInput.contents, untitledInput.encoding); + } + + const resourceInput = input; + + // Files support + if (resourceInput.resource instanceof URI && resourceInput.resource.scheme === network.Schemas.file) { + return this.createOrGet(resourceInput.resource, this.instantiationService, resourceInput.label, resourceInput.description, resourceInput.encoding); + } + + // Any other resource + else if (resourceInput.resource instanceof URI) { + const label = resourceInput.label || basename(resourceInput.resource.fsPath); + let description: string; + if (typeof resourceInput.description === 'string') { + description = resourceInput.description; + } else if (resourceInput.resource.scheme === network.Schemas.file) { + description = dirname(resourceInput.resource.fsPath); + } + + return this.createOrGet(resourceInput.resource, this.instantiationService, label, description); + } + + return null; + } + + private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string, description: string, encoding?: string): ICachedEditorInput { + if (WorkbenchEditorService.CACHE.has(resource)) { + const input = WorkbenchEditorService.CACHE.get(resource); + if (input instanceof ResourceEditorInput) { + input.setName(label); + input.setDescription(description); + } else { + input.setPreferredEncoding(encoding); + } + + return input; + } + + let input: ICachedEditorInput; + if (resource.scheme === network.Schemas.file) { + input = this.fileInputFactory.createFileInput(resource, encoding, instantiationService); + } else { + input = instantiationService.createInstance(ResourceEditorInput, label, description, resource); + } + + WorkbenchEditorService.CACHE.set(resource, input); + once(input.onDispose)(() => { + WorkbenchEditorService.CACHE.delete(resource); + }); + + return input; + } + + private toDiffLabel(res1: URI, res2: URI, context: IWorkspaceContextService, environment: IEnvironmentService): string { + const leftName = getPathLabel(res1.fsPath, context, environment); + const rightName = getPathLabel(res2.fsPath, context, environment); + + return nls.localize('compareLabels', "{0} ↔ {1}", leftName, rightName); + } +} + +export interface IEditorOpenHandler { + (input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + (input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; +} + +export interface IEditorCloseHandler { + (position: Position, input: IEditorInput): TPromise; +} + +/** + * Subclass of workbench editor service that delegates all calls to the provided editor service. Subclasses can choose to override the behavior + * of openEditor() and closeEditor() by providing a handler. + * + * This gives clients a chance to override the behavior of openEditor() and closeEditor(). + */ +export class DelegatingWorkbenchEditorService extends WorkbenchEditorService { + private editorOpenHandler: IEditorOpenHandler; + private editorCloseHandler: IEditorCloseHandler; + + constructor( + @IUntitledEditorService untitledEditorService: IUntitledEditorService, + @IInstantiationService instantiationService: IInstantiationService, + @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IEnvironmentService environmentService: IEnvironmentService + ) { + super( + editorService, + untitledEditorService, + workspaceContextService, + instantiationService, + environmentService + ); + } + + public setEditorOpenHandler(handler: IEditorOpenHandler): void { + this.editorOpenHandler = handler; + } + + public setEditorCloseHandler(handler: IEditorCloseHandler): void { + this.editorCloseHandler = handler; + } + + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { + const handleOpen = this.editorOpenHandler ? this.editorOpenHandler(input, options, arg3) : TPromise.as(void 0); + + return handleOpen.then(editor => { + if (editor) { + return TPromise.as(editor); + } + + return super.doOpenEditor(input, options, arg3); + }); + } + + protected doCloseEditor(position: Position, input: IEditorInput): TPromise { + const handleClose = this.editorCloseHandler ? this.editorCloseHandler(position, input) : TPromise.as(void 0); + + return handleClose.then(() => { + return super.doCloseEditor(position, input); + }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts new file mode 100644 index 0000000000..a19af74e12 --- /dev/null +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IEditorService, IEditor, IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IResourceInput, IResourceDiffInput, IResourceSideBySideInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor'; + +export const IWorkbenchEditorService = createDecorator('editorService'); + +export type IResourceInputType = IResourceInput | IUntitledResourceInput | IResourceDiffInput | IResourceSideBySideInput; + +/** + * The editor service allows to open editors and work on the active + * editor input and models. + */ +export interface IWorkbenchEditorService extends IEditorService { + _serviceBrand: ServiceIdentifier; + + /** + * Returns the currently active editor or null if none. + */ + getActiveEditor(): IEditor; + + /** + * Returns the currently active editor input or null if none. + */ + getActiveEditorInput(): IEditorInput; + + /** + * Returns an array of visible editors. + */ + getVisibleEditors(): IEditor[]; + + /** + * Returns if the provided input is currently visible. + * + * @param includeDiff if set to true, will also consider diff editors to find out if the provided + * input is opened either on the left or right hand side of the diff editor. + */ + isVisible(input: IEditorInput, includeDiff: boolean): boolean; + + /** + * Opens an Editor on the given input with the provided options at the given position. If sideBySide parameter + * is provided, causes the editor service to decide in what position to open the input. + */ + openEditor(input: IEditorInput, options?: IEditorOptions | ITextEditorOptions, position?: Position): TPromise; + openEditor(input: IEditorInput, options?: IEditorOptions | ITextEditorOptions, sideBySide?: boolean): TPromise; + + /** + * Specific overload to open an instance of IResourceInput, IResourceDiffInput or IResourceSideBySideInput. + */ + openEditor(input: IResourceInputType, position?: Position): TPromise; + openEditor(input: IResourceInputType, sideBySide?: boolean): TPromise; + + /** + * Similar to #openEditor() but allows to open multiple editors for different positions at the same time. If there are + * more than one editor per position, only the first one will be active and the others stacked behind inactive. + */ + openEditors(editors: { input: IResourceInputType, position: Position }[]): TPromise; + openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions | ITextEditorOptions }[]): TPromise; + + /** + * Given a list of editors to replace, will look across all groups where this editor is open (active or hidden) + * and replace it with the new editor and the provied options. + */ + replaceEditors(editors: { toReplace: IResourceInputType, replaceWith: IResourceInputType }[], position?: Position): TPromise; + replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions | ITextEditorOptions }[], position?: Position): TPromise; + + /** + * Closes the editor at the provided position. + */ + closeEditor(position: Position, input: IEditorInput): TPromise; + + /** + * Closes editors of a specific group at the provided position. If the optional editor is provided to exclude, it + * will not be closed. The direction can be used in that case to control if all other editors should get closed, + * or towards a specific direction. + */ + closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise; + + /** + * Closes all editors across all groups. The optional position allows to keep one group alive. + */ + closeAllEditors(except?: Position): TPromise; + + /** + * Allows to resolve an untyped input to a workbench typed instanceof editor input + */ + createInput(input: IResourceInputType): IEditorInput; +} \ No newline at end of file diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts new file mode 100644 index 0000000000..34207f3d46 --- /dev/null +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -0,0 +1,282 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { Promise, TPromise } from 'vs/base/common/winjs.base'; +import paths = require('vs/base/common/paths'); +import { Position, Direction, IEditor } from 'vs/platform/editor/common/editor'; +import URI from 'vs/base/common/uri'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorInput, EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor'; +import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; +import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; +import { DelegatingWorkbenchEditorService, WorkbenchEditorService, IEditorPart } from 'vs/workbench/services/editor/browser/editorService'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; + +let activeEditor: BaseEditor = { + getSelection: function () { + return 'test.selection'; + } +}; + +let openedEditorInput; +let openedEditorOptions; +let openedEditorPosition; + +function toResource(path) { + return URI.from({ scheme: 'custom', path }); +} + +function toFileResource(path) { + return URI.file(paths.join('C:\\', new Buffer(this.test.fullTitle()).toString('base64'), path)); +} + +class TestEditorPart implements IEditorPart { + private activeInput; + + public getId(): string { + return null; + } + + public openEditors(args: any[]): Promise { + return TPromise.as([]); + } + + public replaceEditors(editors: { toReplace: EditorInput, replaceWith: EditorInput, options?: any }[]): TPromise { + return TPromise.as([]); + } + + public closeEditors(position: Position, filter?: { except?: EditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise { + return TPromise.as(null); + } + + public closeAllEditors(except?: Position): TPromise { + return TPromise.as(null); + } + + public closeEditor(position: Position, input: EditorInput): TPromise { + return TPromise.as(null); + } + + public openEditor(input?: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + public openEditor(input?: EditorInput, options?: EditorOptions, position?: Position): TPromise; + public openEditor(input?: EditorInput, options?: EditorOptions, arg?: any): TPromise { + openedEditorInput = input; + openedEditorOptions = options; + openedEditorPosition = arg; + + return TPromise.as(activeEditor); + } + + public getActiveEditor(): BaseEditor { + return activeEditor; + } + + public setActiveEditorInput(input: EditorInput) { + this.activeInput = input; + } + + public getActiveEditorInput(): EditorInput { + return this.activeInput; + } + + public getVisibleEditors(): IEditor[] { + return [activeEditor]; + } +} + +suite('WorkbenchEditorService', () => { + + test('basics', function () { + let instantiationService = workbenchInstantiationService(); + + let activeInput: EditorInput = instantiationService.createInstance(FileEditorInput, toFileResource.call(this, '/something.js'), void 0); + + let testEditorPart = new TestEditorPart(); + testEditorPart.setActiveEditorInput(activeInput); + let service: WorkbenchEditorService = instantiationService.createInstance(WorkbenchEditorService, testEditorPart); + + assert.strictEqual(service.getActiveEditor(), activeEditor); + assert.strictEqual(service.getActiveEditorInput(), activeInput); + + // Open EditorInput + service.openEditor(activeInput, null).then((editor) => { + assert.strictEqual(openedEditorInput, activeInput); + assert.strictEqual(openedEditorOptions, null); + assert.strictEqual(editor, activeEditor); + assert.strictEqual(service.getVisibleEditors().length, 1); + assert(service.getVisibleEditors()[0] === editor); + }); + + service.openEditor(activeInput, null, Position.ONE).then((editor) => { + assert.strictEqual(openedEditorInput, activeInput); + assert.strictEqual(openedEditorOptions, null); + assert.strictEqual(editor, activeEditor); + assert.strictEqual(service.getVisibleEditors().length, 1); + assert(service.getVisibleEditors()[0] === editor); + }); + + // Open Untyped Input (file) + service.openEditor({ resource: toFileResource.call(this, '/index.html'), options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => { + assert.strictEqual(editor, activeEditor); + + assert(openedEditorInput instanceof FileEditorInput); + let contentInput = openedEditorInput; + assert.strictEqual(contentInput.getResource().fsPath, toFileResource.call(this, '/index.html').fsPath); + + assert(openedEditorOptions instanceof TextEditorOptions); + let textEditorOptions = openedEditorOptions; + assert(textEditorOptions.hasOptionsDefined()); + }); + + // Open Untyped Input (file, encoding) + service.openEditor({ resource: toFileResource.call(this, '/index.html'), encoding: 'utf16le', options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => { + assert.strictEqual(editor, activeEditor); + + assert(openedEditorInput instanceof FileEditorInput); + let contentInput = openedEditorInput; + assert.equal(contentInput.getPreferredEncoding(), 'utf16le'); + }); + + // Open Untyped Input (untitled) + service.openEditor({ options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => { + assert.strictEqual(editor, activeEditor); + + assert(openedEditorInput instanceof UntitledEditorInput); + + assert(openedEditorOptions instanceof TextEditorOptions); + let textEditorOptions = openedEditorOptions; + assert(textEditorOptions.hasOptionsDefined()); + }); + + // Open Untyped Input (untitled with contents) + service.openEditor({ contents: 'Hello Untitled', options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => { + assert.strictEqual(editor, activeEditor); + + assert(openedEditorInput instanceof UntitledEditorInput); + + const untitledInput = openedEditorInput as UntitledEditorInput; + untitledInput.resolve().then(model => { + assert.equal(model.getValue(), 'Hello Untitled'); + }); + }); + + // Open Untyped Input (untitled with file path) + service.openEditor({ filePath: '/some/path.txt', options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => { + assert.strictEqual(editor, activeEditor); + + assert(openedEditorInput instanceof UntitledEditorInput); + + const untitledInput = openedEditorInput as UntitledEditorInput; + assert.ok(untitledInput.hasAssociatedFilePath); + }); + }); + + test('caching', function () { + let instantiationService = workbenchInstantiationService(); + + let activeInput: EditorInput = instantiationService.createInstance(FileEditorInput, toFileResource.call(this, '/something.js'), void 0); + + let testEditorPart = new TestEditorPart(); + testEditorPart.setActiveEditorInput(activeInput); + let service: WorkbenchEditorService = instantiationService.createInstance(WorkbenchEditorService, testEditorPart); + + // Cached Input (Files) + const fileResource1 = toFileResource.call(this, '/foo/bar/cache1.js'); + const fileInput1 = service.createInput({ resource: fileResource1 }); + assert.ok(fileInput1); + + const fileResource2 = toFileResource.call(this, '/foo/bar/cache2.js'); + const fileInput2 = service.createInput({ resource: fileResource2 }); + assert.ok(fileInput2); + + assert.notEqual(fileInput1, fileInput2); + + const fileInput1Again = service.createInput({ resource: fileResource1 }); + assert.equal(fileInput1Again, fileInput1); + + fileInput1Again.dispose(); + + assert.ok(fileInput1.isDisposed()); + + const fileInput1AgainAndAgain = service.createInput({ resource: fileResource1 }); + assert.notEqual(fileInput1AgainAndAgain, fileInput1); + assert.ok(!fileInput1AgainAndAgain.isDisposed()); + + // Cached Input (Resource) + const resource1 = toResource.call(this, '/foo/bar/cache1.js'); + const input1 = service.createInput({ resource: resource1 }); + assert.ok(input1); + + const resource2 = toResource.call(this, '/foo/bar/cache2.js'); + const input2 = service.createInput({ resource: resource2 }); + assert.ok(input2); + + assert.notEqual(input1, input2); + + const input1Again = service.createInput({ resource: resource1 }); + assert.equal(input1Again, input1); + + input1Again.dispose(); + + assert.ok(input1.isDisposed()); + + const input1AgainAndAgain = service.createInput({ resource: resource1 }); + assert.notEqual(input1AgainAndAgain, input1); + assert.ok(!input1AgainAndAgain.isDisposed()); + }); + + test('delegate', function (done) { + let instantiationService = workbenchInstantiationService(); + let activeInput: EditorInput = instantiationService.createInstance(FileEditorInput, toFileResource.call(this, '/something.js'), void 0); + + let testEditorPart = new TestEditorPart(); + testEditorPart.setActiveEditorInput(activeInput); + + instantiationService.createInstance(WorkbenchEditorService, testEditorPart); + class MyEditor extends BaseEditor { + + constructor(id: string) { + super(id, null, new TestThemeService()); + } + + getId(): string { + return 'myEditor'; + } + + public layout(): void { + + } + + public createEditor(): any { + + } + } + let ed = instantiationService.createInstance(MyEditor, 'my.editor'); + + let inp = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.parse('my://resource')); + let delegate = instantiationService.createInstance(DelegatingWorkbenchEditorService); + delegate.setEditorOpenHandler((input, options?) => { + assert.strictEqual(input, inp); + + return TPromise.as(ed); + }); + + delegate.setEditorCloseHandler((position, input) => { + assert.strictEqual(input, inp); + + done(); + + return TPromise.as(void 0); + }); + + delegate.openEditor(inp); + delegate.closeEditor(0, inp); + }); +}); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts new file mode 100644 index 0000000000..fdb30a4418 --- /dev/null +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -0,0 +1,500 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { stringify } from 'vs/base/common/marshalling'; +import * as objects from 'vs/base/common/objects'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { isWindows, isLinux } from 'vs/base/common/platform'; +import { findFreePort } from 'vs/base/node/ports'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { ILifecycleService, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ChildProcess, fork } from 'child_process'; +import { ipcRenderer as ipc } from 'electron'; +import product from 'vs/platform/node/product'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { generateRandomPipeName, Protocol } from 'vs/base/parts/ipc/node/ipc.net'; +import { createServer, Server, Socket } from 'net'; +import Event, { Emitter, debounceEvent, mapEvent, any } from 'vs/base/common/event'; +import { fromEventEmitter } from 'vs/base/node/event'; +import { IInitData, IWorkspaceData } from 'vs/workbench/api/node/extHost.protocol'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { ICrashReporterService } from 'vs/workbench/services/crashReporter/common/crashReporterService'; +import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService'; +import { isEqual } from 'vs/base/common/paths'; +import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, ILogEntry, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; + +export class ExtensionHostProcessWorker { + + private _onCrashed: Emitter<[number, string]> = new Emitter<[number, string]>(); + public readonly onCrashed: Event<[number, string]> = this._onCrashed.event; + + private readonly _toDispose: IDisposable[]; + + private readonly _isExtensionDevHost: boolean; + private readonly _isExtensionDevDebug: boolean; + private readonly _isExtensionDevDebugBrk: boolean; + private readonly _isExtensionDevTestFromCli: boolean; + + // State + private _lastExtensionHostError: string; + private _terminating: boolean; + + // Resources, in order they get acquired/created when .start() is called: + private _namedPipeServer: Server; + private _extensionHostProcess: ChildProcess; + private _extensionHostConnection: Socket; + private _messageProtocol: TPromise; + + constructor( + /* intentionally not injected */private readonly _extensionService: IExtensionService, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @IMessageService private readonly _messageService: IMessageService, + @IWindowsService private readonly _windowsService: IWindowsService, + @IWindowService private readonly _windowService: IWindowService, + @IBroadcastService private readonly _broadcastService: IBroadcastService, + @ILifecycleService private readonly _lifecycleService: ILifecycleService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWorkspaceConfigurationService private readonly _configurationService: IWorkspaceConfigurationService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @ICrashReporterService private readonly _crashReporterService: ICrashReporterService + ) { + // handle extension host lifecycle a bit special when we know we are developing an extension that runs inside + this._isExtensionDevHost = this._environmentService.isExtensionDevelopment; + this._isExtensionDevDebug = (typeof this._environmentService.debugExtensionHost.port === 'number'); + this._isExtensionDevDebugBrk = !!this._environmentService.debugExtensionHost.break; + this._isExtensionDevTestFromCli = this._isExtensionDevHost && !!this._environmentService.extensionTestsPath && !this._environmentService.debugExtensionHost.break; + + this._lastExtensionHostError = null; + this._terminating = false; + + this._namedPipeServer = null; + this._extensionHostProcess = null; + this._extensionHostConnection = null; + this._messageProtocol = null; + + this._toDispose = []; + this._toDispose.push(this._onCrashed); + this._toDispose.push(this._lifecycleService.onWillShutdown((e) => this._onWillShutdown(e))); + this._toDispose.push(this._lifecycleService.onShutdown(reason => this.terminate())); + this._toDispose.push(this._broadcastService.onBroadcast(b => this._onBroadcast(b))); + + const globalExitListener = () => this.terminate(); + process.once('exit', globalExitListener); + this._toDispose.push({ + dispose: () => { + process.removeListener('exit', globalExitListener); + } + }); + } + + public dispose(): void { + this.terminate(); + } + + private _onBroadcast(broadcast: IBroadcast): void { + + // Close Ext Host Window Request + if (broadcast.channel === EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL && this._isExtensionDevHost) { + const extensionPaths = broadcast.payload as string[]; + if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this._environmentService.extensionDevelopmentPath, path, !isLinux))) { + this._windowService.closeWindow(); + } + } + + if (broadcast.channel === EXTENSION_RELOAD_BROADCAST_CHANNEL && this._isExtensionDevHost) { + const extensionPaths = broadcast.payload as string[]; + if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this._environmentService.extensionDevelopmentPath, path, !isLinux))) { + this._windowService.reloadWindow(); + } + } + } + + public start(): TPromise { + if (this._terminating) { + // .terminate() was called + return null; + } + + if (!this._messageProtocol) { + this._messageProtocol = TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then((data: [string, number]) => { + const pipeName = data[0]; + // The port will be 0 if there's no need to debug or if a free port was not found + const port = data[1]; + + const opts = { + env: objects.mixin(objects.clone(process.env), { + AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess', + PIPE_LOGGING: 'true', + VERBOSE_LOGGING: true, + VSCODE_WINDOW_ID: String(this._windowService.getCurrentWindowId()), + VSCODE_IPC_HOOK_EXTHOST: pipeName, + VSCODE_HANDLES_UNCAUGHT_ERRORS: true, + ELECTRON_NO_ASAR: '1' + }), + // We only detach the extension host on windows. Linux and Mac orphan by default + // and detach under Linux and Mac create another process group. + // We detach because we have noticed that when the renderer exits, its child processes + // (i.e. extension host) are taken down in a brutal fashion by the OS + detached: !!isWindows, + execArgv: port + ? ['--nolazy', (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + port] + : undefined, + silent: true + }; + + const crashReporterOptions = this._crashReporterService.getChildProcessStartOptions('extensionHost'); + if (crashReporterOptions) { + opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions); + } + + // Run Extension Host as fork of current process + this._extensionHostProcess = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts); + + // Catch all output coming from the extension host process + type Output = { data: string, format: string[] }; + this._extensionHostProcess.stdout.setEncoding('utf8'); + this._extensionHostProcess.stderr.setEncoding('utf8'); + const onStdout = fromEventEmitter(this._extensionHostProcess.stdout, 'data'); + const onStderr = fromEventEmitter(this._extensionHostProcess.stderr, 'data'); + const onOutput = any( + mapEvent(onStdout, o => ({ data: `%c${o}`, format: [''] })), + mapEvent(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] })) + ); + + // Debounce all output, so we can render it in the Chrome console as a group + const onDebouncedOutput = debounceEvent(onOutput, (r, o) => { + return r + ? { data: r.data + o.data, format: [...r.format, ...o.format] } + : { data: o.data, format: o.format }; + }, 100); + + // Print out extension host output + onDebouncedOutput(data => { + console.group('Extension Host'); + console.log(data.data, ...data.format); + console.groupEnd(); + }); + + // Support logging from extension host + this._extensionHostProcess.on('message', msg => { + if (msg && (msg).type === '__$console') { + this._logExtensionHostMessage(msg); + } + }); + + // Lifecycle + this._extensionHostProcess.on('error', (err) => this._onExtHostProcessError(err)); + this._extensionHostProcess.on('exit', (code: number, signal: string) => this._onExtHostProcessExit(code, signal)); + + // Notify debugger that we are ready to attach to the process if we run a development extension + if (this._isExtensionDevHost && port) { + this._broadcastService.broadcast({ + channel: EXTENSION_ATTACH_BROADCAST_CHANNEL, + payload: { + debugId: this._environmentService.debugExtensionHost.debugId, + port + } + }); + } + + // Help in case we fail to start it + let startupTimeoutHandle: number; + if (!this._environmentService.isBuilt || this._isExtensionDevHost) { + startupTimeoutHandle = setTimeout(() => { + const msg = this._isExtensionDevDebugBrk + ? nls.localize('extensionHostProcess.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.") + : nls.localize('extensionHostProcess.startupFail', "Extension host did not start in 10 seconds, that might be a problem."); + + this._messageService.show(Severity.Warning, msg); + }, 10000); + } + + // Initialize extension host process with hand shakes + return this._tryExtHostHandshake().then((protocol) => { + clearTimeout(startupTimeoutHandle); + return protocol; + }); + }); + } + + return this._messageProtocol; + } + + /** + * Start a server (`this._namedPipeServer`) that listens on a named pipe and return the named pipe name. + */ + private _tryListenOnPipe(): TPromise { + return new TPromise((resolve, reject) => { + const pipeName = generateRandomPipeName(); + + this._namedPipeServer = createServer(); + this._namedPipeServer.on('error', reject); + this._namedPipeServer.listen(pipeName, () => { + this._namedPipeServer.removeListener('error', reject); + resolve(pipeName); + }); + }); + } + + /** + * Find a free port if extension host debugging is enabled. + */ + private _tryFindDebugPort(): TPromise { + const extensionHostPort = this._environmentService.debugExtensionHost.port; + if (typeof extensionHostPort !== 'number') { + return TPromise.wrap(0); + } + return new TPromise((c, e) => { + findFreePort(extensionHostPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */, (port) => { + if (!port) { + console.warn('%c[Extension Host] %cCould not find a free port for debugging', 'color: blue', 'color: black'); + return c(void 0); + } + if (port !== extensionHostPort) { + console.warn(`%c[Extension Host] %cProvided debugging port ${extensionHostPort} is not free, using ${port} instead.`, 'color: blue', 'color: black'); + } + if (this._isExtensionDevDebugBrk) { + console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color: black'); + } else { + console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color: black'); + } + return c(port); + }); + }); + } + + private _tryExtHostHandshake(): TPromise { + + return new TPromise((resolve, reject) => { + + // Wait for the extension host to connect to our named pipe + // and wrap the socket in the message passing protocol + let handle = setTimeout(() => { + this._namedPipeServer.close(); + this._namedPipeServer = null; + reject('timeout'); + }, 60 * 1000); + + this._namedPipeServer.on('connection', socket => { + clearTimeout(handle); + this._namedPipeServer.close(); + this._namedPipeServer = null; + this._extensionHostConnection = socket; + resolve(new Protocol(this._extensionHostConnection)); + }); + + }).then((protocol) => { + + // 1) wait for the incoming `ready` event and send the initialization data. + // 2) wait for the incoming `initialized` event. + return new TPromise((resolve, reject) => { + + let handle = setTimeout(() => { + reject('timeout'); + }, 60 * 1000); + + const disposable = protocol.onMessage(msg => { + + if (msg === 'ready') { + // 1) Extension Host is ready to receive messages, initialize it + this._createExtHostInitData().then(data => protocol.send(stringify(data))); + return; + } + + if (msg === 'initialized') { + // 2) Extension Host is initialized + + clearTimeout(handle); + + // stop listening for messages here + disposable.dispose(); + + // release this promise + resolve(protocol); + return; + } + + console.error(`received unexpected message during handshake phase from the extension host: `, msg); + }); + + }); + + }); + } + + private _createExtHostInitData(): TPromise { + return TPromise.join([this._telemetryService.getTelemetryInfo(), this._extensionService.getExtensions()]).then(([telemetryInfo, extensionDescriptions]) => { + const r: IInitData = { + parentPid: process.pid, + environment: { + isExtensionDevelopmentDebug: this._isExtensionDevDebug, + appRoot: this._environmentService.appRoot, + appSettingsHome: this._environmentService.appSettingsHome, + disableExtensions: this._environmentService.disableExtensions, + userExtensionsHome: this._environmentService.extensionsPath, + extensionDevelopmentPath: this._environmentService.extensionDevelopmentPath, + extensionTestsPath: this._environmentService.extensionTestsPath, + // globally disable proposed api when built and not insiders developing extensions + enableProposedApiForAll: !this._environmentService.isBuilt || (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0), + enableProposedApiFor: this._environmentService.args['enable-proposed-api'] || [] + }, + workspace: this._contextService.getWorkspace(), + extensions: extensionDescriptions, + configuration: this._configurationService.getConfigurationData(), + telemetryInfo + }; + return r; + }); + } + + private _logExtensionHostMessage(logEntry: ILogEntry) { + let args = []; + try { + let parsed = JSON.parse(logEntry.arguments); + args.push(...Object.getOwnPropertyNames(parsed).map(o => parsed[o])); + } catch (error) { + args.push(logEntry.arguments); + } + + // If the first argument is a string, check for % which indicates that the message + // uses substitution for variables. In this case, we cannot just inject our colored + // [Extension Host] to the front because it breaks substitution. + let consoleArgs = []; + if (typeof args[0] === 'string' && args[0].indexOf('%') >= 0) { + consoleArgs = [`%c[Extension Host]%c ${args[0]}`, 'color: blue', 'color: black', ...args.slice(1)]; + } else { + consoleArgs = ['%c[Extension Host]', 'color: blue', ...args]; + } + + // Send to local console unless we run tests from cli + if (!this._isExtensionDevTestFromCli) { + console[logEntry.severity].apply(console, consoleArgs); + } + + // Log on main side if running tests from cli + if (this._isExtensionDevTestFromCli) { + this._windowsService.log(logEntry.severity, ...args); + } + + // Broadcast to other windows if we are in development mode + else if (!this._environmentService.isBuilt || this._isExtensionDevHost) { + this._broadcastService.broadcast({ + channel: EXTENSION_LOG_BROADCAST_CHANNEL, + payload: { + logEntry, + debugId: this._environmentService.debugExtensionHost.debugId + } + }); + } + } + + private _onExtHostProcessError(err: any): void { + let errorMessage = toErrorMessage(err); + if (errorMessage === this._lastExtensionHostError) { + return; // prevent error spam + } + + this._lastExtensionHostError = errorMessage; + + this._messageService.show(Severity.Error, nls.localize('extensionHostProcess.error', "Error from the extension host: {0}", errorMessage)); + } + + private _onExtHostProcessExit(code: number, signal: string): void { + if (this._terminating) { + // Expected termination path (we asked the process to terminate) + return; + } + + // Unexpected termination + if (!this._isExtensionDevHost) { + this._onCrashed.fire([code, signal]); + } + + // Expected development extension termination: When the extension host goes down we also shutdown the window + else if (!this._isExtensionDevTestFromCli) { + this._windowService.closeWindow(); + } + + // When CLI testing make sure to exit with proper exit code + else { + ipc.send('vscode:exit', code); + } + } + + public terminate(): void { + if (this._terminating) { + return; + } + this._terminating = true; + + dispose(this._toDispose); + + if (!this._messageProtocol) { + // .start() was not called + return; + } + + this._messageProtocol.then((protocol) => { + + // Send the extension host a request to terminate itself + // (graceful termination) + protocol.send({ + type: '__$terminate' + }); + + // Give the extension host 60s, after which we will + // try to kill the process and release any resources + setTimeout(() => this._cleanResources(), 60 * 1000); + + }, (err) => { + + // Establishing a protocol with the extension host failed, so + // try to kill the process and release any resources. + this._cleanResources(); + }); + } + + private _cleanResources(): void { + if (this._namedPipeServer) { + this._namedPipeServer.close(); + this._namedPipeServer = null; + } + if (this._extensionHostConnection) { + this._extensionHostConnection.end(); + this._extensionHostConnection = null; + } + if (this._extensionHostProcess) { + this._extensionHostProcess.kill(); + this._extensionHostProcess = null; + } + } + + private _onWillShutdown(event: ShutdownEvent): void { + + // If the extension development host was started without debugger attached we need + // to communicate this back to the main side to terminate the debug session + if (this._isExtensionDevHost && !this._isExtensionDevTestFromCli && !this._isExtensionDevDebug) { + this._broadcastService.broadcast({ + channel: EXTENSION_TERMINATE_BROADCAST_CHANNEL, + payload: { + debugId: this._environmentService.debugExtensionHost.debugId + } + }); + + event.veto(TPromise.timeout(100 /* wait a bit for IPC to get delivered */).then(() => false)); + } + } +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionPoints.ts b/src/vs/workbench/services/extensions/electron-browser/extensionPoints.ts new file mode 100644 index 0000000000..35801217f5 --- /dev/null +++ b/src/vs/workbench/services/extensions/electron-browser/extensionPoints.ts @@ -0,0 +1,363 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; +import * as Platform from 'vs/base/common/platform'; +import pfs = require('vs/base/node/pfs'); +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { groupBy, values } from 'vs/base/common/collections'; +import { join, normalize, extname } from 'path'; +import json = require('vs/base/common/json'); +import Types = require('vs/base/common/types'); +import { isValidExtensionDescription } from 'vs/platform/extensions/node/extensionValidator'; +import * as semver from 'semver'; +import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; + +const MANIFEST_FILE = 'package.json'; + +const devMode = !!process.env['VSCODE_DEV']; +interface NlsConfiguration { + locale: string; + pseudo: boolean; +} +const nlsConfig: NlsConfiguration = { + locale: Platform.locale, + pseudo: Platform.locale === 'pseudo' +}; + +export interface ILog { + error(source: string, message: string): void; + warn(source: string, message: string): void; + info(source: string, message: string): void; +} + +abstract class ExtensionManifestHandler { + + protected _ourVersion: string; + protected _log: ILog; + protected _absoluteFolderPath: string; + protected _isBuiltin: boolean; + protected _absoluteManifestPath: string; + + constructor(ourVersion: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean) { + this._ourVersion = ourVersion; + this._log = log; + this._absoluteFolderPath = absoluteFolderPath; + this._isBuiltin = isBuiltin; + this._absoluteManifestPath = join(absoluteFolderPath, MANIFEST_FILE); + } +} + +class ExtensionManifestParser extends ExtensionManifestHandler { + + public parse(): TPromise { + return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => { + try { + return JSON.parse(manifestContents.toString()); + } catch (e) { + this._log.error(this._absoluteFolderPath, nls.localize('jsonParseFail', "Failed to parse {0}: {1}.", this._absoluteManifestPath, getParseErrorMessage(e.message))); + } + return null; + }, (err) => { + if (err.code === 'ENOENT') { + return null; + } + + this._log.error(this._absoluteFolderPath, nls.localize('fileReadFail', "Cannot read file {0}: {1}.", this._absoluteManifestPath, err.message)); + return null; + }); + } +} + +class ExtensionManifestNLSReplacer extends ExtensionManifestHandler { + + public replaceNLS(extensionDescription: IExtensionDescription): TPromise { + let extension = extname(this._absoluteManifestPath); + let basename = this._absoluteManifestPath.substr(0, this._absoluteManifestPath.length - extension.length); + + return pfs.fileExists(basename + '.nls' + extension).then(exists => { + if (!exists) { + return extensionDescription; + } + return ExtensionManifestNLSReplacer.findMessageBundles(basename).then((messageBundle) => { + if (!messageBundle.localized) { + return extensionDescription; + } + return pfs.readFile(messageBundle.localized).then(messageBundleContent => { + let errors: json.ParseError[] = []; + let messages: { [key: string]: string; } = json.parse(messageBundleContent.toString(), errors); + + return ExtensionManifestNLSReplacer.resolveOriginalMessageBundle(messageBundle.original, errors).then(originalMessages => { + if (errors.length > 0) { + errors.forEach((error) => { + this._log.error(this._absoluteFolderPath, nls.localize('jsonsParseFail', "Failed to parse {0} or {1}: {2}.", messageBundle.localized, messageBundle.original, getParseErrorMessage(error.error))); + }); + return extensionDescription; + } + + ExtensionManifestNLSReplacer._replaceNLStrings(extensionDescription, messages, originalMessages, this._log, this._absoluteFolderPath); + return extensionDescription; + }); + }, (err) => { + this._log.error(this._absoluteFolderPath, nls.localize('fileReadFail', "Cannot read file {0}: {1}.", messageBundle.localized, err.message)); + return null; + }); + }); + }); + } + + /** + * Parses original message bundle, returns null if the original message bundle is null. + */ + private static resolveOriginalMessageBundle(originalMessageBundle: string, errors: json.ParseError[]) { + return new TPromise<{ [key: string]: string; }>((c, e, p) => { + if (originalMessageBundle) { + pfs.readFile(originalMessageBundle).then(originalBundleContent => { + c(json.parse(originalBundleContent.toString(), errors)); + }); + } else { + c(null); + } + }); + } + + /** + * Finds localized message bundle and the original (unlocalized) one. + * If the localized file is not present, returns null for the original and marks original as localized. + */ + private static findMessageBundles(basename: string): TPromise<{ localized: string, original: string }> { + return new TPromise<{ localized: string, original: string }>((c, e, p) => { + function loop(basename: string, locale: string): void { + let toCheck = `${basename}.nls.${locale}.json`; + pfs.fileExists(toCheck).then(exists => { + if (exists) { + c({ localized: toCheck, original: `${basename}.nls.json` }); + } + let index = locale.lastIndexOf('-'); + if (index === -1) { + c({ localized: `${basename}.nls.json`, original: null }); + } else { + locale = locale.substring(0, index); + loop(basename, locale); + } + }); + } + + if (devMode || nlsConfig.pseudo || !nlsConfig.locale) { + return c({ localized: basename + '.nls.json', original: null }); + } + loop(basename, nlsConfig.locale); + }); + } + + /** + * This routine makes the following assumptions: + * The root element is an object literal + */ + private static _replaceNLStrings(literal: T, messages: { [key: string]: string; }, originalMessages: { [key: string]: string }, log: ILog, messageScope: string): void { + function processEntry(obj: any, key: string | number, command?: boolean) { + let value = obj[key]; + if (Types.isString(value)) { + let str = value; + let length = str.length; + if (length > 1 && str[0] === '%' && str[length - 1] === '%') { + let messageKey = str.substr(1, length - 2); + let message = messages[messageKey]; + if (message) { + if (nlsConfig.pseudo) { + // FF3B and FF3D is the Unicode zenkaku representation for [ and ] + message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D'; + } + obj[key] = command && (key === 'title' || key === 'category') && originalMessages ? { value: message, original: originalMessages[messageKey] } : message; + } else { + log.warn(messageScope, nls.localize('missingNLSKey', "Couldn't find message for key {0}.", messageKey)); + } + } + } else if (Types.isObject(value)) { + for (let k in value) { + if (value.hasOwnProperty(k)) { + k === 'commands' ? processEntry(value, k, true) : processEntry(value, k, command); + } + } + } else if (Types.isArray(value)) { + for (let i = 0; i < value.length; i++) { + processEntry(value, i, command); + } + } + } + + for (let key in literal) { + if (literal.hasOwnProperty(key)) { + processEntry(literal, key); + } + }; + } +} + +class ExtensionManifestValidator extends ExtensionManifestHandler { + validate(_extensionDescription: IExtensionDescription): IExtensionDescription { + // Relax the readonly properties here, it is the one place where we check and normalize values + interface IRelaxedExtensionDescription { + id: string; + name: string; + version: string; + publisher: string; + isBuiltin: boolean; + extensionFolderPath: string; + engines: { + vscode: string; + }; + main?: string; + enableProposedApi?: boolean; + } + let extensionDescription = _extensionDescription; + extensionDescription.isBuiltin = this._isBuiltin; + + let notices: string[] = []; + if (!isValidExtensionDescription(this._ourVersion, this._absoluteFolderPath, extensionDescription, notices)) { + notices.forEach((error) => { + this._log.error(this._absoluteFolderPath, error); + }); + return null; + } + + // in this case the notices are warnings + notices.forEach((error) => { + this._log.warn(this._absoluteFolderPath, error); + }); + + // id := `publisher.name` + extensionDescription.id = `${extensionDescription.publisher}.${extensionDescription.name}`; + + // main := absolutePath(`main`) + if (extensionDescription.main) { + extensionDescription.main = join(this._absoluteFolderPath, extensionDescription.main); + } + + extensionDescription.extensionFolderPath = this._absoluteFolderPath; + + return extensionDescription; + } +} + +export class ExtensionScanner { + + /** + * Read the extension defined in `absoluteFolderPath` + */ + public static scanExtension( + version: string, + log: ILog, + absoluteFolderPath: string, + isBuiltin: boolean + ): TPromise { + absoluteFolderPath = normalize(absoluteFolderPath); + + let parser = new ExtensionManifestParser(version, log, absoluteFolderPath, isBuiltin); + return parser.parse().then((extensionDescription) => { + if (extensionDescription === null) { + return null; + } + + let nlsReplacer = new ExtensionManifestNLSReplacer(version, log, absoluteFolderPath, isBuiltin); + return nlsReplacer.replaceNLS(extensionDescription); + }).then((extensionDescription) => { + if (extensionDescription === null) { + return null; + } + + let validator = new ExtensionManifestValidator(version, log, absoluteFolderPath, isBuiltin); + return validator.validate(extensionDescription); + }); + } + + /** + * Scan a list of extensions defined in `absoluteFolderPath` + */ + public static scanExtensions( + version: string, + log: ILog, + absoluteFolderPath: string, + isBuiltin: boolean + ): TPromise { + let obsolete = TPromise.as({}); + + if (!isBuiltin) { + obsolete = pfs.readFile(join(absoluteFolderPath, '.obsolete'), 'utf8') + .then(raw => JSON.parse(raw)) + .then(null, err => ({})); + } + + return obsolete.then(obsolete => { + return pfs.readDirsInDir(absoluteFolderPath) + .then(folders => { + if (isBuiltin) { + return folders; + } + + // TODO: align with extensionsService + const nonGallery: string[] = []; + const gallery: { folder: string; id: string; version: string; }[] = []; + + folders.forEach(folder => { + if (obsolete[folder]) { + return; + } + + const { id, version } = getIdAndVersionFromLocalExtensionId(folder); + + if (!id || !version) { + nonGallery.push(folder); + return; + } + + gallery.push({ folder, id, version }); + }); + + const byId = values(groupBy(gallery, p => p.id)); + const latest = byId.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version))[0]) + .map(a => a.folder); + + return [...nonGallery, ...latest]; + }) + .then(folders => TPromise.join(folders.map(f => this.scanExtension(version, log, join(absoluteFolderPath, f), isBuiltin)))) + .then(extensionDescriptions => extensionDescriptions.filter(item => item !== null)) + .then(null, err => { + log.error(absoluteFolderPath, err); + return []; + }); + }); + } + + /** + * Combination of scanExtension and scanExtensions: If an extension manifest is found at root, we load just this extension, + * otherwise we assume the folder contains multiple extensions. + */ + public static scanOneOrMultipleExtensions( + version: string, + log: ILog, + absoluteFolderPath: string, + isBuiltin: boolean + ): TPromise { + return pfs.fileExists(join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => { + if (exists) { + return this.scanExtension(version, log, absoluteFolderPath, isBuiltin).then((extensionDescription) => { + if (extensionDescription === null) { + return []; + } + return [extensionDescription]; + }); + } + return this.scanExtensions(version, log, absoluteFolderPath, isBuiltin); + }, (err) => { + log.error(absoluteFolderPath, err); + return []; + }); + } +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts new file mode 100644 index 0000000000..67ac40cc20 --- /dev/null +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -0,0 +1,449 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import * as errors from 'vs/base/common/errors'; +import Severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import pkg from 'vs/platform/node/package'; +import * as path from 'path'; +import URI from 'vs/base/common/uri'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; +import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService, ExtensionPointContribution, ActivationTimes } from 'vs/platform/extensions/common/extensions'; +import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { areSameExtensions, getGloballyDisabledExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry'; +import { ExtensionScanner, ILog } from 'vs/workbench/services/extensions/electron-browser/extensionPoints'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService'; +import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; +import { MainThreadService } from 'vs/workbench/services/thread/electron-browser/threadService'; +import { Barrier } from 'vs/workbench/services/extensions/node/barrier'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { Action } from 'vs/base/common/actions'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions')); + +function messageWithSource(msg: IMessage): string { + return messageWithSource2(msg.source, msg.message); +} + +function messageWithSource2(source: string, message: string): string { + if (source) { + return `[${source}]: ${message}`; + } + return message; +} + +const hasOwnProperty = Object.hasOwnProperty; +const NO_OP_VOID_PROMISE = TPromise.as(void 0); + +export class ExtensionService implements IExtensionService { + public _serviceBrand: any; + + private _registry: ExtensionDescriptionRegistry; + private readonly _barrier: Barrier; + private readonly _isDev: boolean; + private readonly _extensionsStatus: { [id: string]: IExtensionsStatus }; + private _allRequestedActivateEvents: { [activationEvent: string]: boolean; }; + + + // --- Members used per extension host process + + /** + * A map of already activated events to speed things up if the same activation event is triggered multiple times. + */ + private _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; }; + private _extensionHostProcessActivationTimes: { [id: string]: ActivationTimes; }; + private _extensionHostProcessWorker: ExtensionHostProcessWorker; + private _extensionHostProcessThreadService: MainThreadService; + private _extensionHostProcessCustomers: IDisposable[]; + /** + * winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object. + */ + private _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>; + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IMessageService private readonly _messageService: IMessageService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService, + @IStorageService private readonly _storageService: IStorageService, + @IWindowService private readonly _windowService: IWindowService + ) { + this._registry = null; + this._barrier = new Barrier(); + this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment; + this._extensionsStatus = {}; + this._allRequestedActivateEvents = Object.create(null); + + this._extensionHostProcessFinishedActivateEvents = Object.create(null); + this._extensionHostProcessActivationTimes = Object.create(null); + this._extensionHostProcessWorker = null; + this._extensionHostProcessThreadService = null; + this._extensionHostProcessCustomers = []; + this._extensionHostProcessProxy = null; + + this._startExtensionHostProcess([]); + this._scanAndHandleExtensions(); + } + + public restartExtensionHost(): void { + this._stopExtensionHostProcess(); + this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents)); + } + + private _stopExtensionHostProcess(): void { + this._extensionHostProcessFinishedActivateEvents = Object.create(null); + this._extensionHostProcessActivationTimes = Object.create(null); + if (this._extensionHostProcessWorker) { + this._extensionHostProcessWorker.dispose(); + this._extensionHostProcessWorker = null; + } + if (this._extensionHostProcessThreadService) { + this._extensionHostProcessThreadService.dispose(); + this._extensionHostProcessThreadService = null; + } + for (let i = 0, len = this._extensionHostProcessCustomers.length; i < len; i++) { + const customer = this._extensionHostProcessCustomers[i]; + try { + customer.dispose(); + } catch (err) { + errors.onUnexpectedError(err); + } + } + this._extensionHostProcessCustomers = []; + this._extensionHostProcessProxy = null; + } + + private _startExtensionHostProcess(initialActivationEvents: string[]): void { + this._stopExtensionHostProcess(); + + this._extensionHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this); + this._extensionHostProcessWorker.onCrashed(([code, signal]) => this._onExtensionHostCrashed(code, signal)); + this._extensionHostProcessProxy = this._extensionHostProcessWorker.start().then( + (protocol) => { + return { value: this._createExtensionHostCustomers(protocol) }; + }, + (err) => { + console.error('Error received from starting extension host'); + console.error(err); + return null; + } + ); + this._extensionHostProcessProxy.then(() => { + initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent)); + }); + } + + private _onExtensionHostCrashed(code: number, signal: string): void { + const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, (): TPromise => { + return this._windowService.openDevTools().then(() => false); + }); + + const restart = new Action('restart', nls.localize('restart', "Restart Extension Host"), '', true, (): TPromise => { + this._messageService.hideAll(); + this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents)); + return TPromise.as(true); + }); + + console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); + this._stopExtensionHostProcess(); + + let message = nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly."); + if (code === 87) { + message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive."); + } + this._messageService.show(Severity.Error, { + message: message, + actions: [ + openDevTools, + restart + ] + }); + } + + private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape { + + this._extensionHostProcessThreadService = this._instantiationService.createInstance(MainThreadService, protocol); + const extHostContext: IExtHostContext = this._extensionHostProcessThreadService; + + // Named customers + const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers(); + for (let i = 0, len = namedCustomers.length; i < len; i++) { + const [id, ctor] = namedCustomers[i]; + const instance = this._instantiationService.createInstance(ctor, extHostContext); + this._extensionHostProcessCustomers.push(instance); + this._extensionHostProcessThreadService.set(id, instance); + } + + // Customers + const customers = ExtHostCustomersRegistry.getCustomers(); + for (let i = 0, len = customers.length; i < len; i++) { + const ctor = customers[i]; + const instance = this._instantiationService.createInstance(ctor, extHostContext); + this._extensionHostProcessCustomers.push(instance); + } + + // Check that no named customers are missing + const expected: ProxyIdentifier[] = Object.keys(MainContext).map((key) => MainContext[key]); + this._extensionHostProcessThreadService.assertRegistered(expected); + + return this._extensionHostProcessThreadService.get(ExtHostContext.ExtHostExtensionService); + } + + // ---- begin IExtensionService + + public activateByEvent(activationEvent: string): TPromise { + if (this._barrier.isOpen()) { + // Extensions have been scanned and interpreted + + if (!this._registry.containsActivationEvent(activationEvent)) { + // There is no extension that is interested in this activation event + return NO_OP_VOID_PROMISE; + } + + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents[activationEvent] = true; + + return this._activateByEvent(activationEvent); + } else { + // Extensions have not been scanned yet. + + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents[activationEvent] = true; + + return this._barrier.wait().then(() => this._activateByEvent(activationEvent)); + } + } + + protected _activateByEvent(activationEvent: string): TPromise { + if (this._extensionHostProcessFinishedActivateEvents[activationEvent]) { + return NO_OP_VOID_PROMISE; + } + return this._extensionHostProcessProxy.then((proxy) => { + return proxy.value.$activateByEvent(activationEvent); + }).then(() => { + this._extensionHostProcessFinishedActivateEvents[activationEvent] = true; + }); + } + + public onReady(): TPromise { + return this._barrier.wait(); + } + + public getExtensions(): TPromise { + return this.onReady().then(() => { + return this._registry.getAllExtensionDescriptions(); + }); + } + + public readExtensionPointContributions(extPoint: IExtensionPoint): TPromise[]> { + return this.onReady().then(() => { + let availableExtensions = this._registry.getAllExtensionDescriptions(); + + let result: ExtensionPointContribution[] = [], resultLen = 0; + for (let i = 0, len = availableExtensions.length; i < len; i++) { + let desc = availableExtensions[i]; + + if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) { + result[resultLen++] = new ExtensionPointContribution(desc, desc.contributes[extPoint.name]); + } + } + + return result; + }); + } + + public getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { + return this._extensionsStatus; + } + + public getExtensionsActivationTimes(): { [id: string]: ActivationTimes; } { + return this._extensionHostProcessActivationTimes; + } + + // ---- end IExtensionService + + // --- impl + + private _scanAndHandleExtensions(): void { + + const log = new Logger((severity, source, message) => { + this._logOrShowMessage(severity, this._isDev ? messageWithSource2(source, message) : message); + }); + + ExtensionService._scanInstalledExtensions(this._environmentService, log).then((installedExtensions) => { + const disabledExtensions = [ + ...getGloballyDisabledExtensions(this._extensionEnablementService, this._storageService, installedExtensions), + ...this._extensionEnablementService.getWorkspaceDisabledExtensions() + ]; + + this._telemetryService.publicLog('extensionsScanned', { + totalCount: installedExtensions.length, + disabledCount: disabledExtensions.length + }); + + if (disabledExtensions.length === 0) { + return installedExtensions; + } + return installedExtensions.filter(e => disabledExtensions.every(id => !areSameExtensions({ id }, e))); + + }).then((extensionDescriptions) => { + this._registry = new ExtensionDescriptionRegistry(extensionDescriptions); + + let availableExtensions = this._registry.getAllExtensionDescriptions(); + let extensionPoints = ExtensionsRegistry.getExtensionPoints(); + + let messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg); + + for (let i = 0, len = extensionPoints.length; i < len; i++) { + ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); + } + + this._barrier.open(); + }); + } + + private _handleExtensionPointMessage(msg: IMessage) { + + if (!this._extensionsStatus[msg.source]) { + this._extensionsStatus[msg.source] = { messages: [] }; + } + this._extensionsStatus[msg.source].messages.push(msg); + + if (msg.source === this._environmentService.extensionDevelopmentPath) { + // This message is about the extension currently being developed + this._showMessageToUser(msg.type, messageWithSource(msg)); + } else { + this._logMessageInConsole(msg.type, messageWithSource(msg)); + } + + if (!this._isDev && msg.extensionId) { + const { type, extensionId, extensionPointId, message } = msg; + this._telemetryService.publicLog('extensionsMessage', { + type, extensionId, extensionPointId, message + }); + } + } + + private static _scanInstalledExtensions(environmentService: IEnvironmentService, log: ILog): TPromise { + const version = pkg.version; + const builtinExtensions = ExtensionScanner.scanExtensions(version, log, SystemExtensionsRoot, true); + const userExtensions = environmentService.disableExtensions || !environmentService.extensionsPath ? TPromise.as([]) : ExtensionScanner.scanExtensions(version, log, environmentService.extensionsPath, false); + const developedExtensions = environmentService.disableExtensions || !environmentService.isExtensionDevelopment ? TPromise.as([]) : ExtensionScanner.scanOneOrMultipleExtensions(version, log, environmentService.extensionDevelopmentPath, false); + + return TPromise.join([builtinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => { + const builtinExtensions = extensionDescriptions[0]; + const userExtensions = extensionDescriptions[1]; + const developedExtensions = extensionDescriptions[2]; + + let result: { [extensionId: string]: IExtensionDescription; } = {}; + builtinExtensions.forEach((builtinExtension) => { + result[builtinExtension.id] = builtinExtension; + }); + userExtensions.forEach((userExtension) => { + if (result.hasOwnProperty(userExtension.id)) { + log.warn(userExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionFolderPath, userExtension.extensionFolderPath)); + } + result[userExtension.id] = userExtension; + }); + developedExtensions.forEach(developedExtension => { + log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionFolderPath)); + if (result.hasOwnProperty(developedExtension.id)) { + log.warn(developedExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionFolderPath, developedExtension.extensionFolderPath)); + } + result[developedExtension.id] = developedExtension; + }); + + return Object.keys(result).map(name => result[name]); + }).then(null, err => { + log.error('', err); + return []; + }); + } + + private static _handleExtensionPoint(extensionPoint: ExtensionPoint, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void { + let users: IExtensionPointUser[] = [], usersLen = 0; + for (let i = 0, len = availableExtensions.length; i < len; i++) { + let desc = availableExtensions[i]; + + if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) { + users[usersLen++] = { + description: desc, + value: desc.contributes[extensionPoint.name], + collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name) + }; + } + } + + extensionPoint.acceptUsers(users); + } + + private _showMessageToUser(severity: Severity, msg: string): void { + if (severity === Severity.Error || severity === Severity.Warning) { + this._messageService.show(severity, msg); + } else { + this._logMessageInConsole(severity, msg); + } + } + + private _logMessageInConsole(severity: Severity, msg: string): void { + if (severity === Severity.Error) { + console.error(msg); + } else if (severity === Severity.Warning) { + console.warn(msg); + } else { + console.log(msg); + } + } + + // -- called by extension host + + public _logOrShowMessage(severity: Severity, msg: string): void { + if (this._isDev) { + this._showMessageToUser(severity, msg); + } else { + this._logMessageInConsole(severity, msg); + } + } + + public _onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number): void { + this._extensionHostProcessActivationTimes[extensionId] = new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime); + } +} + +export class Logger implements ILog { + + private readonly _messageHandler: (severity: Severity, source: string, message: string) => void; + + constructor( + messageHandler: (severity: Severity, source: string, message: string) => void + ) { + this._messageHandler = messageHandler; + } + + public error(source: string, message: string): void { + this._messageHandler(Severity.Error, source, message); + } + + public warn(source: string, message: string): void { + this._messageHandler(Severity.Warning, source, message); + } + + public info(source: string, message: string): void { + this._messageHandler(Severity.Info, source, message); + } +} diff --git a/src/vs/workbench/services/extensions/node/barrier.ts b/src/vs/workbench/services/extensions/node/barrier.ts new file mode 100644 index 0000000000..e0c40e1ea7 --- /dev/null +++ b/src/vs/workbench/services/extensions/node/barrier.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; + +/** + * A barrier that is initially closed and then becomes opened permanently. + */ +export class Barrier { + + private _isOpen: boolean; + private _promise: TPromise; + private _completePromise: (v: boolean) => void; + + constructor() { + this._isOpen = false; + this._promise = new TPromise((c, e, p) => { + this._completePromise = c; + }, () => { + console.warn('You should really not try to cancel this ready promise!'); + }); + } + + public isOpen(): boolean { + return this._isOpen; + } + + public open(): void { + this._isOpen = true; + this._completePromise(true); + } + + public wait(): TPromise { + return this._promise; + } +} diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts new file mode 100644 index 0000000000..6486762ba7 --- /dev/null +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; + +const hasOwnProperty = Object.hasOwnProperty; + +export class ExtensionDescriptionRegistry { + private _extensionsMap: { [extensionId: string]: IExtensionDescription; }; + private _extensionsArr: IExtensionDescription[]; + private _activationMap: { [activationEvent: string]: IExtensionDescription[]; }; + + constructor(extensionDescriptions: IExtensionDescription[]) { + this._extensionsMap = {}; + this._extensionsArr = []; + this._activationMap = {}; + + for (let i = 0, len = extensionDescriptions.length; i < len; i++) { + let extensionDescription = extensionDescriptions[i]; + + if (hasOwnProperty.call(this._extensionsMap, extensionDescription.id)) { + // No overwriting allowed! + console.error('Extension `' + extensionDescription.id + '` is already registered'); + continue; + } + + this._extensionsMap[extensionDescription.id] = extensionDescription; + this._extensionsArr.push(extensionDescription); + + if (Array.isArray(extensionDescription.activationEvents)) { + for (let j = 0, lenJ = extensionDescription.activationEvents.length; j < lenJ; j++) { + let activationEvent = extensionDescription.activationEvents[j]; + this._activationMap[activationEvent] = this._activationMap[activationEvent] || []; + this._activationMap[activationEvent].push(extensionDescription); + } + } + } + } + + public containsActivationEvent(activationEvent: string): boolean { + return hasOwnProperty.call(this._activationMap, activationEvent); + } + + public getExtensionDescriptionsForActivationEvent(activationEvent: string): IExtensionDescription[] { + if (!hasOwnProperty.call(this._activationMap, activationEvent)) { + return []; + } + return this._activationMap[activationEvent].slice(0); + } + + public getAllExtensionDescriptions(): IExtensionDescription[] { + return this._extensionsArr.slice(0); + } + + public getExtensionDescription(extensionId: string): IExtensionDescription { + if (!hasOwnProperty.call(this._extensionsMap, extensionId)) { + return null; + } + return this._extensionsMap[extensionId]; + } +} diff --git a/src/vs/workbench/services/extensions/node/lazyPromise.ts b/src/vs/workbench/services/extensions/node/lazyPromise.ts new file mode 100644 index 0000000000..1d7e46e5ff --- /dev/null +++ b/src/vs/workbench/services/extensions/node/lazyPromise.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise, ValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; + +export class LazyPromise { + + private _onCancel: () => void; + + private _actual: TPromise; + private _actualOk: ValueCallback; + private _actualErr: ErrorCallback; + + private _hasValue: boolean; + private _value: any; + + private _hasErr: boolean; + private _err: any; + + private _isCanceled: boolean; + + constructor(onCancel: () => void) { + this._onCancel = onCancel; + this._actual = null; + this._actualOk = null; + this._actualErr = null; + this._hasValue = false; + this._value = null; + this._hasErr = false; + this._err = null; + this._isCanceled = false; + } + + private _ensureActual(): TPromise { + if (!this._actual) { + this._actual = new TPromise((c, e) => { + this._actualOk = c; + this._actualErr = e; + }, this._onCancel); + + if (this._hasValue) { + this._actualOk(this._value); + } + + if (this._hasErr) { + this._actualErr(this._err); + } + } + return this._actual; + } + + public resolveOk(value: any): void { + if (this._isCanceled || this._hasErr) { + return; + } + + this._hasValue = true; + this._value = value; + + if (this._actual) { + this._actualOk(value); + } + } + + public resolveErr(err: any): void { + if (this._isCanceled || this._hasValue) { + return; + } + + this._hasErr = true; + this._err = err; + + if (this._actual) { + this._actualErr(err); + } + } + + public then(success: any, error: any): any { + if (this._isCanceled) { + return; + } + + return this._ensureActual().then(success, error); + } + + public done(success: any, error: any): void { + if (this._isCanceled) { + return; + } + + this._ensureActual().done(success, error); + } + + public cancel(): void { + if (this._hasValue || this._hasErr) { + return; + } + + this._isCanceled = true; + + if (this._actual) { + this._actual.cancel(); + } else { + this._onCancel(); + } + } +} diff --git a/src/vs/workbench/services/extensions/node/rpcProtocol.ts b/src/vs/workbench/services/extensions/node/rpcProtocol.ts new file mode 100644 index 0000000000..87a74a6c5c --- /dev/null +++ b/src/vs/workbench/services/extensions/node/rpcProtocol.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as marshalling from 'vs/base/common/marshalling'; +import * as errors from 'vs/base/common/errors'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { LazyPromise } from 'vs/workbench/services/extensions/node/lazyPromise'; + +export interface IDispatcher { + invoke(proxyId: string, methodName: string, args: any[]): any; +} + +export class RPCProtocol { + + private _isDisposed: boolean; + private _bigHandler: IDispatcher; + private _lastMessageId: number; + private readonly _invokedHandlers: { [req: string]: TPromise; }; + private readonly _pendingRPCReplies: { [msgId: string]: LazyPromise; }; + private readonly _multiplexor: RPCMultiplexer; + + constructor(protocol: IMessagePassingProtocol) { + this._isDisposed = false; + this._bigHandler = null; + this._lastMessageId = 0; + this._invokedHandlers = Object.create(null); + this._pendingRPCReplies = {}; + this._multiplexor = new RPCMultiplexer(protocol, (msg) => this._receiveOneMessage(msg)); + } + + public dispose(): void { + this._isDisposed = true; + + // Release all outstanding promises with a canceled error + Object.keys(this._pendingRPCReplies).forEach((msgId) => { + const pending = this._pendingRPCReplies[msgId]; + pending.resolveErr(errors.canceled()); + }); + } + + private _receiveOneMessage(rawmsg: string): void { + if (this._isDisposed) { + console.warn('Received message after being shutdown: ', rawmsg); + return; + } + let msg = marshalling.parse(rawmsg); + + if (msg.seq) { + if (!this._pendingRPCReplies.hasOwnProperty(msg.seq)) { + console.warn('Got reply to unknown seq'); + return; + } + let reply = this._pendingRPCReplies[msg.seq]; + delete this._pendingRPCReplies[msg.seq]; + + if (msg.err) { + let err = msg.err; + if (msg.err.$isError) { + err = new Error(); + err.name = msg.err.name; + err.message = msg.err.message; + err.stack = msg.err.stack; + } + reply.resolveErr(err); + return; + } + + reply.resolveOk(msg.res); + return; + } + + if (msg.cancel) { + if (this._invokedHandlers[msg.cancel]) { + this._invokedHandlers[msg.cancel].cancel(); + } + return; + } + + if (msg.err) { + console.error(msg.err); + return; + } + + let rpcId = msg.rpcId; + + if (!this._bigHandler) { + throw new Error('got message before big handler attached!'); + } + + let req = msg.req; + + this._invokedHandlers[req] = this._invokeHandler(rpcId, msg.method, msg.args); + + this._invokedHandlers[req].then((r) => { + delete this._invokedHandlers[req]; + this._multiplexor.send(MessageFactory.replyOK(req, r)); + }, (err) => { + delete this._invokedHandlers[req]; + this._multiplexor.send(MessageFactory.replyErr(req, err)); + }); + } + + private _invokeHandler(proxyId: string, methodName: string, args: any[]): TPromise { + try { + return TPromise.as(this._bigHandler.invoke(proxyId, methodName, args)); + } catch (err) { + return TPromise.wrapError(err); + } + } + + public callOnRemote(proxyId: string, methodName: string, args: any[]): TPromise { + if (this._isDisposed) { + return TPromise.wrapError(errors.canceled()); + } + + let req = String(++this._lastMessageId); + let result = new LazyPromise(() => { + this._multiplexor.send(MessageFactory.cancel(req)); + }); + + this._pendingRPCReplies[req] = result; + + this._multiplexor.send(MessageFactory.request(req, proxyId, methodName, args)); + + return result; + } + + public setDispatcher(handler: IDispatcher): void { + this._bigHandler = handler; + } +} + +/** + * Sends/Receives multiple messages in one go: + * - multiple messages to be sent from one stack get sent in bulk at `process.nextTick`. + * - each incoming message is handled in a separate `process.nextTick`. + */ +class RPCMultiplexer { + + private readonly _protocol: IMessagePassingProtocol; + private readonly _sendAccumulatedBound: () => void; + + private _messagesToSend: string[]; + + constructor(protocol: IMessagePassingProtocol, onMessage: (msg: string) => void) { + this._protocol = protocol; + this._sendAccumulatedBound = this._sendAccumulated.bind(this); + + this._messagesToSend = []; + + this._protocol.onMessage(data => { + for (let i = 0, len = data.length; i < len; i++) { + onMessage(data[i]); + } + }); + } + + private _sendAccumulated(): void { + const tmp = this._messagesToSend; + this._messagesToSend = []; + this._protocol.send(tmp); + } + + public send(msg: string): void { + if (this._messagesToSend.length === 0) { + process.nextTick(this._sendAccumulatedBound); + } + this._messagesToSend.push(msg); + } +} + +class MessageFactory { + public static cancel(req: string): string { + return `{"cancel":"${req}"}`; + } + + public static request(req: string, rpcId: string, method: string, args: any[]): string { + return `{"req":"${req}","rpcId":"${rpcId}","method":"${method}","args":${marshalling.stringify(args)}}`; + } + + public static replyOK(req: string, res: any): string { + if (typeof res === 'undefined') { + return `{"seq":"${req}"}`; + } + return `{"seq":"${req}","res":${marshalling.stringify(res)}}`; + } + + public static replyErr(req: string, err: any): string { + if (typeof err === 'undefined') { + return `{"seq":"${req}","err":null}`; + } + return `{"seq":"${req}","err":${marshalling.stringify(errors.transformErrorForSerialization(err))}}`; + } +} diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts new file mode 100644 index 0000000000..7235bee897 --- /dev/null +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -0,0 +1,308 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import paths = require('vs/base/common/paths'); +import encoding = require('vs/base/node/encoding'); +import errors = require('vs/base/common/errors'); +import uri from 'vs/base/common/uri'; +import { toResource } from 'vs/workbench/common/editor'; +import { FileOperation, FileOperationEvent, IFileService, IFilesConfiguration, IResolveFileOptions, IFileStat, IResolveFileResult, IContent, IStreamContent, IImportResult, IResolveContentOptions, IUpdateContentOptions, FileChangesEvent } from 'vs/platform/files/common/files'; +import { FileService as NodeFileService, IFileServiceOptions, IEncodingOverride } from 'vs/workbench/services/files/node/fileService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { Action } from 'vs/base/common/actions'; +import { ResourceMap } from 'vs/base/common/map'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IMessageService, IMessageWithAction, Severity, CloseAction } from 'vs/platform/message/common/message'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import Event, { Emitter } from 'vs/base/common/event'; + +import { shell } from 'electron'; + +export class FileService implements IFileService { + + public _serviceBrand: any; + + // If we run with .NET framework < 4.5, we need to detect this error to inform the user + private static NET_VERSION_ERROR = 'System.MissingMethodException'; + private static NET_VERSION_ERROR_IGNORE_KEY = 'ignoreNetVersionError'; + + private raw: IFileService; + + private toUnbind: IDisposable[]; + private activeOutOfWorkspaceWatchers: ResourceMap; + + protected _onFileChanges: Emitter; + private _onAfterOperation: Emitter; + + constructor( + @IConfigurationService private configurationService: IConfigurationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @ILifecycleService private lifecycleService: ILifecycleService, + @IMessageService private messageService: IMessageService, + @IStorageService private storageService: IStorageService + ) { + this.toUnbind = []; + this.activeOutOfWorkspaceWatchers = new ResourceMap(); + + this._onFileChanges = new Emitter(); + this.toUnbind.push(this._onFileChanges); + + this._onAfterOperation = new Emitter(); + this.toUnbind.push(this._onAfterOperation); + + const configuration = this.configurationService.getConfiguration(); + + let watcherIgnoredPatterns: string[] = []; + if (configuration.files && configuration.files.watcherExclude) { + watcherIgnoredPatterns = Object.keys(configuration.files.watcherExclude).filter(k => !!configuration.files.watcherExclude[k]); + } + + // build config + const fileServiceConfig: IFileServiceOptions = { + errorLogger: (msg: string) => this.onFileServiceError(msg), + encodingOverride: this.getEncodingOverrides(), + watcherIgnoredPatterns, + verboseLogging: environmentService.verbose, + useExperimentalFileWatcher: configuration.files.useExperimentalFileWatcher + }; + + // create service + this.raw = new NodeFileService(contextService, configurationService, fileServiceConfig); + + // Listeners + this.registerListeners(); + } + + public get onFileChanges(): Event { + return this._onFileChanges.event; + } + + public get onAfterOperation(): Event { + return this._onAfterOperation.event; + } + + private onFileServiceError(msg: string): void { + errors.onUnexpectedError(msg); + + // Detect if we run < .NET Framework 4.5 + if (typeof msg === 'string' && msg.indexOf(FileService.NET_VERSION_ERROR) >= 0 && !this.storageService.getBoolean(FileService.NET_VERSION_ERROR_IGNORE_KEY, StorageScope.WORKSPACE)) { + this.messageService.show(Severity.Warning, { + message: nls.localize('netVersionError', "The Microsoft .NET Framework 4.5 is required. Please follow the link to install it."), + actions: [ + new Action('install.net', nls.localize('installNet', "Download .NET Framework 4.5"), null, true, () => { + window.open('https://go.microsoft.com/fwlink/?LinkId=786533'); + + return TPromise.as(true); + }), + new Action('net.error.ignore', nls.localize('neverShowAgain', "Don't Show Again"), '', true, () => { + this.storageService.store(FileService.NET_VERSION_ERROR_IGNORE_KEY, true, StorageScope.WORKSPACE); + + return TPromise.as(null); + }), + CloseAction + ] + }); + } + } + + private registerListeners(): void { + + // File events + this.toUnbind.push(this.raw.onFileChanges(e => this._onFileChanges.fire(e))); + this.toUnbind.push(this.raw.onAfterOperation(e => this._onAfterOperation.fire(e))); + + // Config changes + this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration()))); + + // Editor changing + this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); + + // Root changes + this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots())); + + // Lifecycle + this.lifecycleService.onShutdown(this.dispose, this); + } + + private onDidChangeWorkspaceRoots(): void { + this.updateOptions({ encodingOverride: this.getEncodingOverrides() }); + } + + private getEncodingOverrides(): IEncodingOverride[] { + const encodingOverride: IEncodingOverride[] = []; + encodingOverride.push({ resource: uri.file(this.environmentService.appSettingsHome), encoding: encoding.UTF8 }); + if (this.contextService.hasWorkspace()) { + this.contextService.getWorkspace().roots.forEach(root => { + // {{SQL CARBON EDIT}} + encodingOverride.push({ resource: uri.file(paths.join(root.fsPath, '.sqlops')), encoding: encoding.UTF8 }); + }); + } + + return encodingOverride; + } + + private onEditorsChanged(): void { + this.handleOutOfWorkspaceWatchers(); + } + + private handleOutOfWorkspaceWatchers(): void { + const visibleOutOfWorkspacePaths = new ResourceMap(); + this.editorService.getVisibleEditors().map(editor => { + return toResource(editor.input, { supportSideBySide: true, filter: 'file' }); + }).filter(fileResource => { + return !!fileResource && !this.contextService.isInsideWorkspace(fileResource); + }).forEach(resource => { + visibleOutOfWorkspacePaths.set(resource, resource); + }); + + // Handle no longer visible out of workspace resources + this.activeOutOfWorkspaceWatchers.forEach(resource => { + if (!visibleOutOfWorkspacePaths.get(resource)) { + this.unwatchFileChanges(resource); + this.activeOutOfWorkspaceWatchers.delete(resource); + } + }); + + // Handle newly visible out of workspace resources + visibleOutOfWorkspacePaths.forEach(resource => { + if (!this.activeOutOfWorkspaceWatchers.get(resource)) { + this.watchFileChanges(resource); + this.activeOutOfWorkspaceWatchers.set(resource, resource); + } + }); + } + + private onConfigurationChange(configuration: IFilesConfiguration): void { + this.updateOptions(configuration.files); + } + + public updateOptions(options: object): void { + this.raw.updateOptions(options); + } + + public resolveFile(resource: uri, options?: IResolveFileOptions): TPromise { + return this.raw.resolveFile(resource, options); + } + + public resolveFiles(toResolve: { resource: uri, options?: IResolveFileOptions }[]): TPromise { + return this.raw.resolveFiles(toResolve); + } + + public existsFile(resource: uri): TPromise { + return this.raw.existsFile(resource); + } + + public resolveContent(resource: uri, options?: IResolveContentOptions): TPromise { + return this.raw.resolveContent(resource, options); + } + + public resolveStreamContent(resource: uri, options?: IResolveContentOptions): TPromise { + return this.raw.resolveStreamContent(resource, options); + } + + public updateContent(resource: uri, value: string, options?: IUpdateContentOptions): TPromise { + return this.raw.updateContent(resource, value, options); + } + + public moveFile(source: uri, target: uri, overwrite?: boolean): TPromise { + return this.raw.moveFile(source, target, overwrite); + } + + public copyFile(source: uri, target: uri, overwrite?: boolean): TPromise { + return this.raw.copyFile(source, target, overwrite); + } + + public createFile(resource: uri, content?: string): TPromise { + return this.raw.createFile(resource, content); + } + + public createFolder(resource: uri): TPromise { + return this.raw.createFolder(resource); + } + + public touchFile(resource: uri): TPromise { + return this.raw.touchFile(resource); + } + + public rename(resource: uri, newName: string): TPromise { + return this.raw.rename(resource, newName); + } + + public del(resource: uri, useTrash?: boolean): TPromise { + if (useTrash) { + return this.doMoveItemToTrash(resource); + } + + return this.raw.del(resource); + } + + private doMoveItemToTrash(resource: uri): TPromise { + const absolutePath = resource.fsPath; + const result = shell.moveItemToTrash(absolutePath); + if (!result) { + return TPromise.wrapError(new Error(nls.localize('trashFailed', "Failed to move '{0}' to the trash", paths.basename(absolutePath)))); + } + + this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.DELETE)); + + return TPromise.as(null); + } + + public importFile(source: uri, targetFolder: uri): TPromise { + return this.raw.importFile(source, targetFolder).then((result) => { + return { + isNew: result && result.isNew, + stat: result && result.stat + }; + }); + } + + public watchFileChanges(resource: uri): void { + if (!resource) { + return; + } + + if (resource.scheme !== 'file') { + return; // only support files + } + + // return early if the resource is inside the workspace for which we have another watcher in place + if (this.contextService.isInsideWorkspace(resource)) { + return; + } + + this.raw.watchFileChanges(resource); + } + + public unwatchFileChanges(resource: uri): void { + this.raw.unwatchFileChanges(resource); + } + + public getEncoding(resource: uri, preferredEncoding?: string): string { + return this.raw.getEncoding(resource, preferredEncoding); + } + + public dispose(): void { + this.toUnbind = dispose(this.toUnbind); + + // Dispose watchers if any + this.activeOutOfWorkspaceWatchers.forEach(resource => this.unwatchFileChanges(resource)); + this.activeOutOfWorkspaceWatchers.clear(); + + // Dispose service + this.raw.dispose(); + } +} diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts new file mode 100644 index 0000000000..b0bcaf686f --- /dev/null +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { FileService } from 'vs/workbench/services/files/electron-browser/fileService'; +import { IContent, IStreamContent, IFileStat, IResolveContentOptions, IUpdateContentOptions, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; +import { TPromise } from 'vs/base/common/winjs.base'; +import Event from 'vs/base/common/event'; +import { EventEmitter } from 'events'; +import { basename } from 'path'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export interface IRemoteFileSystemProvider { + onDidChange: Event; + resolve(resource: URI): TPromise; + update(resource: URI, content: string): TPromise; +} + +export class RemoteFileService extends FileService { + + private readonly _provider = new Map(); + + registerProvider(authority: string, provider: IRemoteFileSystemProvider): IDisposable { + if (this._provider.has(authority)) { + throw new Error(); + } + + this._provider.set(authority, provider); + const reg = provider.onDidChange(e => { + // forward change events + this._onFileChanges.fire(new FileChangesEvent([{ resource: e, type: FileChangeType.UPDATED }])); + }); + return { + dispose: () => { + this._provider.delete(authority); + reg.dispose(); + } + }; + } + + // --- resolve + + resolveContent(resource: URI, options?: IResolveContentOptions): TPromise { + if (this._provider.has(resource.authority)) { + return this._doResolveContent(resource); + } + + return super.resolveContent(resource, options); + } + + resolveStreamContent(resource: URI, options?: IResolveContentOptions): TPromise { + if (this._provider.has(resource.authority)) { + return this._doResolveContent(resource).then(RemoteFileService._asStreamContent); + } + + return super.resolveStreamContent(resource, options); + } + + private async _doResolveContent(resource: URI): TPromise { + + const stat = RemoteFileService._createFakeStat(resource); + const value = await this._provider.get(resource.authority).resolve(resource); + return { ...stat, value }; + } + + // --- saving + + updateContent(resource: URI, value: string, options?: IUpdateContentOptions): TPromise { + if (this._provider.has(resource.authority)) { + return this._doUpdateContent(resource, value).then(RemoteFileService._createFakeStat); + } + + return super.updateContent(resource, value, options); + } + + private async _doUpdateContent(resource: URI, content: string): TPromise { + await this._provider.get(resource.authority).update(resource, content); + return resource; + } + + // --- util + + private static _createFakeStat(resource: URI): IFileStat { + + return { + resource, + name: basename(resource.path), + encoding: 'utf8', + mtime: Date.now(), + etag: Date.now().toString(16), + isDirectory: false, + hasChildren: false + }; + } + + private static _asStreamContent(content: IContent): IStreamContent { + const emitter = new EventEmitter(); + const { value } = content; + const result = content; + result.value = emitter; + setTimeout(() => { + emitter.emit('data', value); + emitter.emit('end'); + }, 0); + return result; + } +} diff --git a/src/vs/workbench/services/files/node/fileService.ts b/src/vs/workbench/services/files/node/fileService.ts new file mode 100644 index 0000000000..888280dde6 --- /dev/null +++ b/src/vs/workbench/services/files/node/fileService.ts @@ -0,0 +1,981 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import paths = require('path'); +import fs = require('fs'); +import os = require('os'); +import crypto = require('crypto'); +import assert = require('assert'); + +import { isParent, FileOperation, FileOperationEvent, IContent, IFileService, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, IImportResult, MAX_FILE_SIZE, FileChangesEvent, IFilesConfiguration } from 'vs/platform/files/common/files'; +import { isEqualOrParent } from 'vs/base/common/paths'; +import { ResourceMap } from 'vs/base/common/map'; +import arrays = require('vs/base/common/arrays'); +import baseMime = require('vs/base/common/mime'); +import { TPromise } from 'vs/base/common/winjs.base'; +import types = require('vs/base/common/types'); +import objects = require('vs/base/common/objects'); +import extfs = require('vs/base/node/extfs'); +import { nfcall, ThrottledDelayer } from 'vs/base/common/async'; +import uri from 'vs/base/common/uri'; +import nls = require('vs/nls'); +import { isWindows, isLinux } from 'vs/base/common/platform'; +import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +import pfs = require('vs/base/node/pfs'); +import encoding = require('vs/base/node/encoding'); +import { IMimeAndEncoding, detectMimesFromFile } from 'vs/base/node/mime'; +import flow = require('vs/base/node/flow'); +import { FileWatcher as UnixWatcherService } from 'vs/workbench/services/files/node/watcher/unix/watcherService'; +import { FileWatcher as WindowsWatcherService } from 'vs/workbench/services/files/node/watcher/win32/watcherService'; +import { toFileChangesEvent, normalize, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; +import Event, { Emitter } from 'vs/base/common/event'; +import { FileWatcher as NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/watcherService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export interface IEncodingOverride { + resource: uri; + encoding: string; +} + +export interface IFileServiceOptions { + tmpDir?: string; + errorLogger?: (msg: string) => void; + encodingOverride?: IEncodingOverride[]; + watcherIgnoredPatterns?: string[]; + disableWatcher?: boolean; + verboseLogging?: boolean; + useExperimentalFileWatcher?: boolean; +} + +function etag(stat: fs.Stats): string; +function etag(size: number, mtime: number): string; +function etag(arg1: any, arg2?: any): string { + let size: number; + let mtime: number; + if (typeof arg2 === 'number') { + size = arg1; + mtime = arg2; + } else { + size = (arg1).size; + mtime = (arg1).mtime.getTime(); + } + + return `"${crypto.createHash('sha1').update(String(size) + String(mtime)).digest('hex')}"`; +} + +export class FileService implements IFileService { + + public _serviceBrand: any; + + private static FS_EVENT_DELAY = 50; // aggregate and only emit events when changes have stopped for this duration (in ms) + private static FS_REWATCH_DELAY = 300; // delay to rewatch a file that was renamed or deleted (in ms) + + private tmpPath: string; + private options: IFileServiceOptions; + + private _onFileChanges: Emitter; + private _onAfterOperation: Emitter; + + private toDispose: IDisposable[]; + + private activeFileChangesWatchers: ResourceMap; + private fileChangesWatchDelayer: ThrottledDelayer; + private undeliveredRawFileChangesEvents: IRawFileChange[]; + + private activeWorkspaceChangeWatcher: IDisposable; + private currentWorkspaceRootsCount: number; + + constructor( + private contextService: IWorkspaceContextService, + private configurationService: IConfigurationService, + options: IFileServiceOptions, + ) { + this.toDispose = []; + this.options = options || Object.create(null); + this.tmpPath = this.options.tmpDir || os.tmpdir(); + this.currentWorkspaceRootsCount = contextService.hasWorkspace() ? contextService.getWorkspace().roots.length : 0; + + this._onFileChanges = new Emitter(); + this.toDispose.push(this._onFileChanges); + + this._onAfterOperation = new Emitter(); + this.toDispose.push(this._onAfterOperation); + + if (!this.options.errorLogger) { + this.options.errorLogger = console.error; + } + + if (this.currentWorkspaceRootsCount > 0 && !this.options.disableWatcher) { + this.setupWorkspaceWatching(); + } + + this.activeFileChangesWatchers = new ResourceMap(); + this.fileChangesWatchDelayer = new ThrottledDelayer(FileService.FS_EVENT_DELAY); + this.undeliveredRawFileChangesEvents = []; + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots())); + } + + private onDidChangeWorkspaceRoots(): void { + const newRootCount = this.contextService.hasWorkspace() ? this.contextService.getWorkspace().roots.length : 0; + + let restartWorkspaceWatcher = false; + if (this.currentWorkspaceRootsCount <= 1 && newRootCount > 1) { + restartWorkspaceWatcher = true; // transition: from 1 or 0 folders to 2+ + } else if (this.currentWorkspaceRootsCount > 1 && newRootCount <= 1) { + restartWorkspaceWatcher = true; // transition: from 2+ folders to 1 or 0 + } + + if (restartWorkspaceWatcher) { + this.setupWorkspaceWatching(); + } + + this.currentWorkspaceRootsCount = newRootCount; + } + + public get onFileChanges(): Event { + return this._onFileChanges.event; + } + + public get onAfterOperation(): Event { + return this._onAfterOperation.event; + } + + public updateOptions(options: IFileServiceOptions): void { + if (options) { + objects.mixin(this.options, options); // overwrite current options + } + } + + private setupWorkspaceWatching(): void { + + // dispose old if any + if (this.activeWorkspaceChangeWatcher) { + this.activeWorkspaceChangeWatcher.dispose(); + } + + // new watcher: use it if setting tells us so or we run in multi-root environment + if (this.options.useExperimentalFileWatcher || this.contextService.getWorkspace().roots.length > 1) { + this.activeWorkspaceChangeWatcher = toDisposable(this.setupNsfwWorkspaceWatching().startWatching()); + } + + // old watcher + else { + if (isWindows) { + this.activeWorkspaceChangeWatcher = toDisposable(this.setupWin32WorkspaceWatching().startWatching()); + } else { + this.activeWorkspaceChangeWatcher = toDisposable(this.setupUnixWorkspaceWatching().startWatching()); + } + } + } + + private setupWin32WorkspaceWatching(): WindowsWatcherService { + return new WindowsWatcherService(this.contextService, this.options.watcherIgnoredPatterns, e => this._onFileChanges.fire(e), this.options.errorLogger, this.options.verboseLogging); + } + + private setupUnixWorkspaceWatching(): UnixWatcherService { + return new UnixWatcherService(this.contextService, this.options.watcherIgnoredPatterns, e => this._onFileChanges.fire(e), this.options.errorLogger, this.options.verboseLogging); + } + + private setupNsfwWorkspaceWatching(): NsfwWatcherService { + return new NsfwWatcherService(this.contextService, this.configurationService, e => this._onFileChanges.fire(e), this.options.errorLogger, this.options.verboseLogging); + } + + public resolveFile(resource: uri, options?: IResolveFileOptions): TPromise { + return this.resolve(resource, options); + } + + public resolveFiles(toResolve: { resource: uri, options?: IResolveFileOptions }[]): TPromise { + return TPromise.join(toResolve.map(resourceAndOptions => this.resolve(resourceAndOptions.resource, resourceAndOptions.options) + .then(stat => ({ stat, success: true }), error => ({ stat: undefined, success: false })))); + } + + public existsFile(resource: uri): TPromise { + return this.resolveFile(resource).then(() => true, () => false); + } + + public resolveContent(resource: uri, options?: IResolveContentOptions): TPromise { + return this.doResolveContent(resource, options, (stat, enc) => this.resolveFileContent(stat, enc)); + } + + public resolveStreamContent(resource: uri, options?: IResolveContentOptions): TPromise { + return this.doResolveContent(resource, options, (stat, enc) => this.resolveFileStreamContent(stat, enc)); + } + + private doResolveContent(resource: uri, options: IResolveContentOptions, contentResolver: (stat: IFileStat, enc?: string) => TPromise): TPromise { + const absolutePath = this.toAbsolutePath(resource); + + // Guard early against attempts to resolve an invalid file path + if (resource.scheme !== 'file' || !resource.fsPath) { + return TPromise.wrapError(new FileOperationError( + nls.localize('fileInvalidPath', "Invalid file resource ({0})", resource.toString()), + FileOperationResult.FILE_INVALID_PATH + )); + } + + // 1.) resolve resource + return this.resolve(resource).then((model): TPromise => { + + // Return early if resource is a directory + if (model.isDirectory) { + return TPromise.wrapError(new FileOperationError( + nls.localize('fileIsDirectoryError', "File is directory ({0})", absolutePath), + FileOperationResult.FILE_IS_DIRECTORY + )); + } + + // Return early if file not modified since + if (options && options.etag && options.etag === model.etag) { + return TPromise.wrapError(new FileOperationError(nls.localize('fileNotModifiedError', "File not modified since"), FileOperationResult.FILE_NOT_MODIFIED_SINCE)); + } + + // Return early if file is too large to load + if (types.isNumber(model.size) && model.size > MAX_FILE_SIZE) { + return TPromise.wrapError(new FileOperationError(nls.localize('fileTooLargeError', "File too large to open"), FileOperationResult.FILE_TOO_LARGE)); + } + + // 2.) detect mimes + const autoGuessEncoding = (options && options.autoGuessEncoding) || this.configuredAutoGuessEncoding(resource); + return detectMimesFromFile(absolutePath, { autoGuessEncoding }).then((detected: IMimeAndEncoding) => { + const isText = detected.mimes.indexOf(baseMime.MIME_BINARY) === -1; + + // Return error early if client only accepts text and this is not text + if (options && options.acceptTextOnly && !isText) { + return TPromise.wrapError(new FileOperationError( + nls.localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), + FileOperationResult.FILE_IS_BINARY + )); + } + + let preferredEncoding: string; + if (options && options.encoding) { + if (detected.encoding === encoding.UTF8 && options.encoding === encoding.UTF8) { + preferredEncoding = encoding.UTF8_with_bom; // indicate the file has BOM if we are to resolve with UTF 8 + } else { + preferredEncoding = options.encoding; // give passed in encoding highest priority + } + } else if (detected.encoding) { + if (detected.encoding === encoding.UTF8) { + preferredEncoding = encoding.UTF8_with_bom; // if we detected UTF-8, it can only be because of a BOM + } else { + preferredEncoding = detected.encoding; + } + } else if (this.configuredEncoding(resource) === encoding.UTF8_with_bom) { + preferredEncoding = encoding.UTF8; // if we did not detect UTF 8 BOM before, this can only be UTF 8 then + } + + // 3.) get content + return contentResolver(model, preferredEncoding); + }); + }, (error) => { + + // bubble up existing file operation results + if (!types.isUndefinedOrNull((error).fileOperationResult)) { + return TPromise.wrapError(error); + } + + // check if the file does not exist + return pfs.exists(absolutePath).then(exists => { + + // Return if file not found + if (!exists) { + return TPromise.wrapError(new FileOperationError( + nls.localize('fileNotFoundError', "File not found ({0})", absolutePath), + FileOperationResult.FILE_NOT_FOUND + )); + } + + // otherwise just give up + return TPromise.wrapError(error); + }); + }); + } + + public updateContent(resource: uri, value: string, options: IUpdateContentOptions = Object.create(null)): TPromise { + const absolutePath = this.toAbsolutePath(resource); + + // 1.) check file + return this.checkFile(absolutePath, options).then(exists => { + let createParentsPromise: TPromise; + if (exists) { + createParentsPromise = TPromise.as(null); + } else { + createParentsPromise = pfs.mkdirp(paths.dirname(absolutePath)); + } + + // 2.) create parents as needed + return createParentsPromise.then(() => { + const encodingToWrite = this.getEncoding(resource, options.encoding); + let addBomPromise: TPromise = TPromise.as(false); + + // UTF_16 BE and LE as well as UTF_8 with BOM always have a BOM + if (encodingToWrite === encoding.UTF16be || encodingToWrite === encoding.UTF16le || encodingToWrite === encoding.UTF8_with_bom) { + addBomPromise = TPromise.as(true); + } + + // Existing UTF-8 file: check for options regarding BOM + else if (exists && encodingToWrite === encoding.UTF8) { + if (options.overwriteEncoding) { + addBomPromise = TPromise.as(false); // if we are to overwrite the encoding, we do not preserve it if found + } else { + addBomPromise = encoding.detectEncodingByBOM(absolutePath).then(enc => enc === encoding.UTF8); // otherwise preserve it if found + } + } + + // 3.) check to add UTF BOM + return addBomPromise.then(addBom => { + + // 4.) set contents and resolve + return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { mode: 0o666, flag: 'w' }).then(undefined, error => { + if (!exists || error.code !== 'EPERM' || !isWindows) { + return TPromise.wrapError(error); + } + + // On Windows and if the file exists with an EPERM error, we try a different strategy of saving the file + // by first truncating the file and then writing with r+ mode. This helps to save hidden files on Windows + // (see https://github.com/Microsoft/vscode/issues/931) + + // 5.) truncate + return pfs.truncate(absolutePath, 0).then(() => { + + // 6.) set contents (this time with r+ mode) and resolve again + return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { mode: 0o666, flag: 'r+' }); + }); + }); + }); + }); + }); + } + + private doSetContentsAndResolve(resource: uri, absolutePath: string, value: string, addBOM: boolean, encodingToWrite: string, options: { mode?: number; flag?: string; }): TPromise { + let writeFilePromise: TPromise; + + // Write fast if we do UTF 8 without BOM + if (!addBOM && encodingToWrite === encoding.UTF8) { + writeFilePromise = pfs.writeFile(absolutePath, value, options); + } + + // Otherwise use encoding lib + else { + const encoded = encoding.encode(value, encodingToWrite, { addBOM }); + writeFilePromise = pfs.writeFile(absolutePath, encoded, options); + } + + // set contents + return writeFilePromise.then(() => { + + // resolve + return this.resolve(resource); + }); + } + + public createFile(resource: uri, content: string = ''): TPromise { + + // Create file + return this.updateContent(resource, content).then(result => { + + // Events + this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, result)); + + return result; + }); + } + + public createFolder(resource: uri): TPromise { + + // 1.) Create folder + const absolutePath = this.toAbsolutePath(resource); + return pfs.mkdirp(absolutePath).then(() => { + + // 2.) Resolve + return this.resolve(resource).then(result => { + + // Events + this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, result)); + + return result; + }); + }); + } + + public touchFile(resource: uri): TPromise { + const absolutePath = this.toAbsolutePath(resource); + + // 1.) check file + return this.checkFile(absolutePath).then(exists => { + let createPromise: TPromise; + if (exists) { + createPromise = TPromise.as(null); + } else { + createPromise = this.createFile(resource); + } + + // 2.) create file as needed + return createPromise.then(() => { + + // 3.) update atime and mtime + return pfs.touch(absolutePath).then(() => { + + // 4.) resolve + return this.resolve(resource); + }); + }); + }); + } + + public rename(resource: uri, newName: string): TPromise { + const newPath = paths.join(paths.dirname(resource.fsPath), newName); + + return this.moveFile(resource, uri.file(newPath)); + } + + public moveFile(source: uri, target: uri, overwrite?: boolean): TPromise { + return this.moveOrCopyFile(source, target, false, overwrite); + } + + public copyFile(source: uri, target: uri, overwrite?: boolean): TPromise { + return this.moveOrCopyFile(source, target, true, overwrite); + } + + private moveOrCopyFile(source: uri, target: uri, keepCopy: boolean, overwrite: boolean): TPromise { + const sourcePath = this.toAbsolutePath(source); + const targetPath = this.toAbsolutePath(target); + + // 1.) move / copy + return this.doMoveOrCopyFile(sourcePath, targetPath, keepCopy, overwrite).then(() => { + + // 2.) resolve + return this.resolve(target).then(result => { + + // Events + this._onAfterOperation.fire(new FileOperationEvent(source, keepCopy ? FileOperation.COPY : FileOperation.MOVE, result)); + + return result; + }); + }); + } + + private doMoveOrCopyFile(sourcePath: string, targetPath: string, keepCopy: boolean, overwrite: boolean): TPromise { + + // 1.) check if target exists + return pfs.exists(targetPath).then(exists => { + const isCaseRename = sourcePath.toLowerCase() === targetPath.toLowerCase(); + const isSameFile = sourcePath === targetPath; + + // Return early with conflict if target exists and we are not told to overwrite + if (exists && !isCaseRename && !overwrite) { + return TPromise.wrapError(new FileOperationError(nls.localize('fileMoveConflict', "Unable to move/copy. File already exists at destination."), FileOperationResult.FILE_MOVE_CONFLICT)); + } + + // 2.) make sure target is deleted before we move/copy unless this is a case rename of the same file + let deleteTargetPromise = TPromise.as(void 0); + if (exists && !isCaseRename) { + if (isEqualOrParent(sourcePath, targetPath, !isLinux /* ignorecase */)) { + return TPromise.wrapError(new Error(nls.localize('unableToMoveCopyError', "Unable to move/copy. File would replace folder it is contained in."))); // catch this corner case! + } + + deleteTargetPromise = this.del(uri.file(targetPath)); + } + + return deleteTargetPromise.then(() => { + + // 3.) make sure parents exists + return pfs.mkdirp(paths.dirname(targetPath)).then(() => { + + // 4.) copy/move + if (isSameFile) { + return TPromise.as(null); + } else if (keepCopy) { + return nfcall(extfs.copy, sourcePath, targetPath); + } else { + return nfcall(extfs.mv, sourcePath, targetPath); + } + }).then(() => exists); + }); + }); + } + + public importFile(source: uri, targetFolder: uri): TPromise { + const sourcePath = this.toAbsolutePath(source); + const targetResource = uri.file(paths.join(targetFolder.fsPath, paths.basename(source.fsPath))); + const targetPath = this.toAbsolutePath(targetResource); + + // 1.) resolve + return pfs.stat(sourcePath).then(stat => { + if (stat.isDirectory()) { + return TPromise.wrapError(new Error(nls.localize('foldersCopyError', "Folders cannot be copied into the workspace. Please select individual files to copy them."))); // for now we do not allow to import a folder into a workspace + } + + // 2.) copy + return this.doMoveOrCopyFile(sourcePath, targetPath, true, true).then(exists => { + + // 3.) resolve + return this.resolve(targetResource).then(stat => { + + // Events + this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.IMPORT, stat)); + + return { isNew: !exists, stat: stat }; + }); + }); + }); + } + + public del(resource: uri): TPromise { + const absolutePath = this.toAbsolutePath(resource); + + return pfs.del(absolutePath, this.tmpPath).then(() => { + + // Events + this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.DELETE)); + }); + } + + // Helpers + + private toAbsolutePath(arg1: uri | IFileStat): string { + let resource: uri; + if (arg1 instanceof uri) { + resource = arg1; + } else { + resource = (arg1).resource; + } + + assert.ok(resource && resource.scheme === 'file', 'Invalid resource: ' + resource); + + return paths.normalize(resource.fsPath); + } + + private resolve(resource: uri, options: IResolveFileOptions = Object.create(null)): TPromise { + return this.toStatResolver(resource) + .then(model => model.resolve(options)); + } + + private toStatResolver(resource: uri): TPromise { + const absolutePath = this.toAbsolutePath(resource); + + return pfs.stat(absolutePath).then(stat => { + return new StatResolver(resource, stat.isDirectory(), stat.mtime.getTime(), stat.size, this.options.verboseLogging); + }); + } + + private resolveFileStreamContent(model: IFileStat, enc?: string): TPromise { + + // Return early if file is too large to load + if (types.isNumber(model.size) && model.size > MAX_FILE_SIZE) { + return TPromise.wrapError(new FileOperationError(nls.localize('fileTooLargeError', "File too large to open"), FileOperationResult.FILE_TOO_LARGE)); + } + + const absolutePath = this.toAbsolutePath(model); + const fileEncoding = this.getEncoding(model.resource, enc); + + const reader = fs.createReadStream(absolutePath).pipe(encoding.decodeStream(fileEncoding)); // decode takes care of stripping any BOMs from the file content + + const content = model as IFileStat & IStreamContent; + content.value = reader; + content.encoding = fileEncoding; // make sure to store the encoding in the model to restore it later when writing + + return TPromise.as(content); + } + + private resolveFileContent(model: IFileStat, enc?: string): TPromise { + return this.resolveFileStreamContent(model, enc).then(streamContent => { + return new TPromise((c, e) => { + let done = false; + const chunks: string[] = []; + + streamContent.value.on('data', buf => { + chunks.push(buf); + }); + + streamContent.value.on('error', error => { + if (!done) { + done = true; + e(error); + } + }); + + streamContent.value.on('end', () => { + const content: IContent = streamContent; + content.value = chunks.join(''); + + if (!done) { + done = true; + c(content); + } + }); + }); + }); + } + + public getEncoding(resource: uri, preferredEncoding?: string): string { + let fileEncoding: string; + + const override = this.getEncodingOverride(resource); + if (override) { + fileEncoding = override; + } else if (preferredEncoding) { + fileEncoding = preferredEncoding; + } else { + fileEncoding = this.configuredEncoding(resource); + } + + if (!fileEncoding || !encoding.encodingExists(fileEncoding)) { + fileEncoding = encoding.UTF8; // the default is UTF 8 + } + + return fileEncoding; + } + + private configuredAutoGuessEncoding(resource: uri): boolean { + const config = this.configurationService.getConfiguration(void 0, { resource }) as IFilesConfiguration; + + return config && config.files && config.files.autoGuessEncoding === true; + } + + private configuredEncoding(resource: uri): string { + const config = this.configurationService.getConfiguration(void 0, { resource }) as IFilesConfiguration; + + return config && config.files && config.files.encoding; + } + + private getEncodingOverride(resource: uri): string { + if (resource && this.options.encodingOverride && this.options.encodingOverride.length) { + for (let i = 0; i < this.options.encodingOverride.length; i++) { + const override = this.options.encodingOverride[i]; + + // check if the resource is a child of the resource with override and use + // the provided encoding in that case + if (isParent(resource.fsPath, override.resource.fsPath, !isLinux /* ignorecase */)) { + return override.encoding; + } + } + } + + return null; + } + + private checkFile(absolutePath: string, options: IUpdateContentOptions = Object.create(null)): TPromise { + return pfs.exists(absolutePath).then(exists => { + if (exists) { + return pfs.stat(absolutePath).then(stat => { + if (stat.isDirectory()) { + return TPromise.wrapError(new Error('Expected file is actually a directory')); + } + + // Dirty write prevention + if (typeof options.mtime === 'number' && typeof options.etag === 'string' && options.mtime < stat.mtime.getTime()) { + + // Find out if content length has changed + if (options.etag !== etag(stat.size, options.mtime)) { + return TPromise.wrapError(new FileOperationError(nls.localize('fileModifiedError', "File Modified Since"), FileOperationResult.FILE_MODIFIED_SINCE)); + } + } + + let mode = stat.mode; + const readonly = !(mode & 128); + + // Throw if file is readonly and we are not instructed to overwrite + if (readonly && !options.overwriteReadonly) { + return TPromise.wrapError(new FileOperationError( + nls.localize('fileReadOnlyError', "File is Read Only"), + FileOperationResult.FILE_READ_ONLY + )); + } + + if (readonly) { + mode = mode | 128; + return pfs.chmod(absolutePath, mode).then(() => exists); + } + + return TPromise.as(exists); + }); + } + + return TPromise.as(exists); + }); + } + + public watchFileChanges(resource: uri): void { + assert.ok(resource && resource.scheme === 'file', `Invalid resource for watching: ${resource}`); + + // Create or get watcher for provided path + let watcher = this.activeFileChangesWatchers.get(resource); + if (!watcher) { + const fsPath = resource.fsPath; + + try { + watcher = fs.watch(fsPath); // will be persistent but not recursive + } catch (error) { + return; // the path might not exist anymore, ignore this error and return + } + + this.activeFileChangesWatchers.set(resource, watcher); + + // eventType is either 'rename' or 'change' + const fsName = paths.basename(resource.fsPath); + watcher.on('change', (eventType: string, filename: string) => { + const renamedOrDeleted = ((filename && filename !== fsName) || eventType === 'rename'); + + // The file was either deleted or renamed. Many tools apply changes to files in an + // atomic way ("Atomic Save") by first renaming the file to a temporary name and then + // renaming it back to the original name. Our watcher will detect this as a rename + // and then stops to work on Mac and Linux because the watcher is applied to the + // inode and not the name. The fix is to detect this case and trying to watch the file + // again after a certain delay. + // In addition, we send out a delete event if after a timeout we detect that the file + // does indeed not exist anymore. + if (renamedOrDeleted) { + + // Very important to dispose the watcher which now points to a stale inode + this.unwatchFileChanges(resource); + + // Wait a bit and try to install watcher again, assuming that the file was renamed quickly ("Atomic Save") + setTimeout(() => { + this.existsFile(resource).done(exists => { + + // File still exists, so reapply the watcher + if (exists) { + this.watchFileChanges(resource); + } + + // File seems to be really gone, so emit a deleted event + else { + this.onRawFileChange({ + type: FileChangeType.DELETED, + path: fsPath + }); + } + }); + }, FileService.FS_REWATCH_DELAY); + } + + // Handle raw file change + this.onRawFileChange({ + type: FileChangeType.UPDATED, + path: fsPath + }); + }); + + // Errors + watcher.on('error', (error: string) => { + this.options.errorLogger(error); + }); + } + } + + private onRawFileChange(event: IRawFileChange): void { + + // add to bucket of undelivered events + this.undeliveredRawFileChangesEvents.push(event); + + if (this.options.verboseLogging) { + console.log('%c[node.js Watcher]%c', 'color: green', 'color: black', event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]', event.path); + } + + // handle emit through delayer to accommodate for bulk changes + this.fileChangesWatchDelayer.trigger(() => { + const buffer = this.undeliveredRawFileChangesEvents; + this.undeliveredRawFileChangesEvents = []; + + // Normalize + const normalizedEvents = normalize(buffer); + + // Logging + if (this.options.verboseLogging) { + normalizedEvents.forEach(r => { + console.log('%c[node.js Watcher]%c >> normalized', 'color: green', 'color: black', r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]', r.path); + }); + } + + // Emit + this._onFileChanges.fire(toFileChangesEvent(normalizedEvents)); + + return TPromise.as(null); + }); + } + + public unwatchFileChanges(resource: uri): void { + const watcher = this.activeFileChangesWatchers.get(resource); + if (watcher) { + watcher.close(); + this.activeFileChangesWatchers.delete(resource); + } + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + + if (this.activeWorkspaceChangeWatcher) { + this.activeWorkspaceChangeWatcher.dispose(); + this.activeWorkspaceChangeWatcher = null; + } + + this.activeFileChangesWatchers.forEach(watcher => watcher.close()); + this.activeFileChangesWatchers.clear(); + } +} + +export class StatResolver { + private resource: uri; + private isDirectory: boolean; + private mtime: number; + private name: string; + private etag: string; + private size: number; + private verboseLogging: boolean; + + constructor(resource: uri, isDirectory: boolean, mtime: number, size: number, verboseLogging: boolean) { + assert.ok(resource && resource.scheme === 'file', 'Invalid resource: ' + resource); + + this.resource = resource; + this.isDirectory = isDirectory; + this.mtime = mtime; + this.name = paths.basename(resource.fsPath); + this.etag = etag(size, mtime); + this.size = size; + + this.verboseLogging = verboseLogging; + } + + public resolve(options: IResolveFileOptions): TPromise { + + // General Data + const fileStat: IFileStat = { + resource: this.resource, + isDirectory: this.isDirectory, + hasChildren: undefined, + name: this.name, + etag: this.etag, + size: this.size, + mtime: this.mtime + }; + + // File Specific Data + if (!this.isDirectory) { + return TPromise.as(fileStat); + } + + // Directory Specific Data + else { + + // Convert the paths from options.resolveTo to absolute paths + let absoluteTargetPaths: string[] = null; + if (options && options.resolveTo) { + absoluteTargetPaths = []; + options.resolveTo.forEach(resource => { + absoluteTargetPaths.push(resource.fsPath); + }); + } + + return new TPromise((c, e) => { + + // Load children + this.resolveChildren(this.resource.fsPath, absoluteTargetPaths, options && options.resolveSingleChildDescendants, (children) => { + children = arrays.coalesce(children); // we don't want those null children (could be permission denied when reading a child) + fileStat.hasChildren = children && children.length > 0; + fileStat.children = children || []; + + c(fileStat); + }); + }); + } + } + + private resolveChildren(absolutePath: string, absoluteTargetPaths: string[], resolveSingleChildDescendants: boolean, callback: (children: IFileStat[]) => void): void { + extfs.readdir(absolutePath, (error: Error, files: string[]) => { + if (error) { + if (this.verboseLogging) { + console.error(error); + } + + return callback(null); // return - we might not have permissions to read the folder + } + + // for each file in the folder + flow.parallel(files, (file: string, clb: (error: Error, children: IFileStat) => void) => { + const fileResource = uri.file(paths.resolve(absolutePath, file)); + let fileStat: fs.Stats; + const $this = this; + + flow.sequence( + function onError(error: Error): void { + if ($this.verboseLogging) { + console.error(error); + } + + clb(null, null); // return - we might not have permissions to read the folder or stat the file + }, + + function stat(): void { + fs.stat(fileResource.fsPath, this); + }, + + function countChildren(fsstat: fs.Stats): void { + fileStat = fsstat; + + if (fileStat.isDirectory()) { + extfs.readdir(fileResource.fsPath, (error, result) => { + this(null, result ? result.length : 0); + }); + } else { + this(null, 0); + } + }, + + function resolve(childCount: number): void { + const childStat: IFileStat = { + resource: fileResource, + isDirectory: fileStat.isDirectory(), + hasChildren: childCount > 0, + name: file, + mtime: fileStat.mtime.getTime(), + etag: etag(fileStat), + size: fileStat.size + }; + + // Return early for files + if (!fileStat.isDirectory()) { + return clb(null, childStat); + } + + // Handle Folder + let resolveFolderChildren = false; + if (files.length === 1 && resolveSingleChildDescendants) { + resolveFolderChildren = true; + } else if (childCount > 0 && absoluteTargetPaths && absoluteTargetPaths.some(targetPath => isEqualOrParent(targetPath, fileResource.fsPath, !isLinux /* ignorecase */))) { + resolveFolderChildren = true; + } + + // Continue resolving children based on condition + if (resolveFolderChildren) { + $this.resolveChildren(fileResource.fsPath, absoluteTargetPaths, resolveSingleChildDescendants, children => { + children = arrays.coalesce(children); // we don't want those null children + childStat.hasChildren = children && children.length > 0; + childStat.children = children || []; + + clb(null, childStat); + }); + } + + // Otherwise return result + else { + clb(null, childStat); + } + }); + }, (errors, result) => { + callback(result); + }); + }); + } +} diff --git a/src/vs/workbench/services/files/node/watcher/common.ts b/src/vs/workbench/services/files/node/watcher/common.ts new file mode 100644 index 0000000000..4e66a24642 --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/common.ts @@ -0,0 +1,119 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import uri from 'vs/base/common/uri'; +import { FileChangeType, FileChangesEvent, isParent } from 'vs/platform/files/common/files'; +import { isLinux } from 'vs/base/common/platform'; + +export interface IRawFileChange { + type: FileChangeType; + path: string; +} + +export function toFileChangesEvent(changes: IRawFileChange[]): FileChangesEvent { + + // map to file changes event that talks about URIs + return new FileChangesEvent(changes.map((c) => { + return { + type: c.type, + resource: uri.file(c.path) + }; + })); +} + +/** + * Given events that occurred, applies some rules to normalize the events + */ +export function normalize(changes: IRawFileChange[]): IRawFileChange[] { + + // Build deltas + let normalizer = new EventNormalizer(); + for (let i = 0; i < changes.length; i++) { + let event = changes[i]; + normalizer.processEvent(event); + } + + return normalizer.normalize(); +} + +class EventNormalizer { + private normalized: IRawFileChange[]; + private mapPathToChange: { [path: string]: IRawFileChange }; + + constructor() { + this.normalized = []; + this.mapPathToChange = Object.create(null); + } + + public processEvent(event: IRawFileChange): void { + + // Event path already exists + let existingEvent = this.mapPathToChange[event.path]; + if (existingEvent) { + let currentChangeType = existingEvent.type; + let newChangeType = event.type; + + // ignore CREATE followed by DELETE in one go + if (currentChangeType === FileChangeType.ADDED && newChangeType === FileChangeType.DELETED) { + delete this.mapPathToChange[event.path]; + this.normalized.splice(this.normalized.indexOf(existingEvent), 1); + } + + // flatten DELETE followed by CREATE into CHANGE + else if (currentChangeType === FileChangeType.DELETED && newChangeType === FileChangeType.ADDED) { + existingEvent.type = FileChangeType.UPDATED; + } + + // Do nothing. Keep the created event + else if (currentChangeType === FileChangeType.ADDED && newChangeType === FileChangeType.UPDATED) { + } + + // Otherwise apply change type + else { + existingEvent.type = newChangeType; + } + } + + // Otherwise Store + else { + this.normalized.push(event); + this.mapPathToChange[event.path] = event; + } + } + + public normalize(): IRawFileChange[] { + let addedChangeEvents: IRawFileChange[] = []; + let deletedPaths: string[] = []; + + // This algorithm will remove all DELETE events up to the root folder + // that got deleted if any. This ensures that we are not producing + // DELETE events for each file inside a folder that gets deleted. + // + // 1.) split ADD/CHANGE and DELETED events + // 2.) sort short deleted paths to the top + // 3.) for each DELETE, check if there is a deleted parent and ignore the event in that case + return this.normalized.filter(e => { + if (e.type !== 2) { + addedChangeEvents.push(e); + return false; // remove ADD / CHANGE + } + + return true; // keep DELETE + }).sort((e1, e2) => { + return e1.path.length - e2.path.length; // shortest path first + }).filter(e => { + if (deletedPaths.some(d => isParent(e.path, d, !isLinux /* ignorecase */))) { + return false; // DELETE is ignored if parent is deleted already + } + + // otherwise mark as deleted + deletedPaths.push(e.path); + + return true; + }).concat(addedChangeEvents); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts new file mode 100644 index 0000000000..734d35e222 --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts @@ -0,0 +1,176 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as glob from 'vs/base/common/glob'; +import * as paths from 'vs/base/common/paths'; +import * as path from 'path'; +import * as platform from 'vs/base/common/platform'; +import * as watcher from 'vs/workbench/services/files/node/watcher/common'; +import * as nsfw from 'nsfw'; +import { IWatcherService, IWatcherRequest } from 'vs/workbench/services/files/node/watcher/nsfw/watcher'; +import { TPromise, ProgressCallback, TValueCallback } from 'vs/base/common/winjs.base'; +import { ThrottledDelayer } from 'vs/base/common/async'; +import { FileChangeType } from 'vs/platform/files/common/files'; +import { normalizeNFC } from 'vs/base/common/strings'; + +const nsfwActionToRawChangeType: { [key: number]: number } = []; +nsfwActionToRawChangeType[nsfw.actions.CREATED] = FileChangeType.ADDED; +nsfwActionToRawChangeType[nsfw.actions.MODIFIED] = FileChangeType.UPDATED; +nsfwActionToRawChangeType[nsfw.actions.DELETED] = FileChangeType.DELETED; + +interface IWatcherObjet { + start(): any; + stop(): any; +} + +interface IPathWatcher { + ready: TPromise; + watcher?: IWatcherObjet; + ignored: string[]; +} + +export class NsfwWatcherService implements IWatcherService { + private static FS_EVENT_DELAY = 50; // aggregate and only emit events when changes have stopped for this duration (in ms) + + private _pathWatchers: { [watchPath: string]: IPathWatcher } = {}; + private _watcherPromise: TPromise; + private _progressCallback: ProgressCallback; + private _verboseLogging: boolean; + + + public initialize(verboseLogging: boolean): TPromise { + this._verboseLogging = verboseLogging; + this._watcherPromise = new TPromise((c, e, p) => { + this._progressCallback = p; + }); + return this._watcherPromise; + } + + private _watch(request: IWatcherRequest): void { + let undeliveredFileEvents: watcher.IRawFileChange[] = []; + const fileEventDelayer = new ThrottledDelayer(NsfwWatcherService.FS_EVENT_DELAY); + + let readyPromiseCallback: TValueCallback; + this._pathWatchers[request.basePath] = { + ready: new TPromise(c => readyPromiseCallback = c), + ignored: request.ignored + }; + + nsfw(request.basePath, events => { + for (let i = 0; i < events.length; i++) { + const e = events[i]; + + // Logging + if (this._verboseLogging) { + const logPath = e.action === nsfw.actions.RENAMED ? path.join(e.directory, e.oldFile) + ' -> ' + e.newFile : path.join(e.directory, e.file); + console.log(e.action === nsfw.actions.CREATED ? '[CREATED]' : e.action === nsfw.actions.DELETED ? '[DELETED]' : e.action === nsfw.actions.MODIFIED ? '[CHANGED]' : '[RENAMED]', logPath); + } + + // Convert nsfw event to IRawFileChange and add to queue + let absolutePath: string; + if (e.action === nsfw.actions.RENAMED) { + // Rename fires when a file's name changes within a single directory + absolutePath = path.join(e.directory, e.oldFile); + if (!this._isPathIgnored(absolutePath, this._pathWatchers[request.basePath].ignored)) { + undeliveredFileEvents.push({ type: FileChangeType.DELETED, path: absolutePath }); + } + absolutePath = path.join(e.directory, e.newFile); + if (!this._isPathIgnored(absolutePath, this._pathWatchers[request.basePath].ignored)) { + undeliveredFileEvents.push({ type: FileChangeType.ADDED, path: absolutePath }); + } + } else { + absolutePath = path.join(e.directory, e.file); + if (!this._isPathIgnored(absolutePath, this._pathWatchers[request.basePath].ignored)) { + undeliveredFileEvents.push({ + type: nsfwActionToRawChangeType[e.action], + path: absolutePath + }); + } + } + } + + // Delay and send buffer + fileEventDelayer.trigger(() => { + const events = undeliveredFileEvents; + undeliveredFileEvents = []; + + // Mac uses NFD unicode form on disk, but we want NFC + if (platform.isMacintosh) { + events.forEach(e => e.path = normalizeNFC(e.path)); + } + + // Broadcast to clients normalized + const res = watcher.normalize(events); + this._progressCallback(res); + + // Logging + if (this._verboseLogging) { + res.forEach(r => { + console.log(' >> normalized', r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]', r.path); + }); + } + + return TPromise.as(null); + }); + }).then(watcher => { + this._pathWatchers[request.basePath].watcher = watcher; + const startPromise = watcher.start(); + startPromise.then(() => readyPromiseCallback(watcher)); + return startPromise; + }); + } + + public setRoots(roots: IWatcherRequest[]): TPromise { + const promises: TPromise[] = []; + const normalizedRoots = this._normalizeRoots(roots); + + // Gather roots that are not currently being watched + const rootsToStartWatching = normalizedRoots.filter(r => { + return !(r.basePath in this._pathWatchers); + }); + + // Gather current roots that don't exist in the new roots array + const rootsToStopWatching = Object.keys(this._pathWatchers).filter(r => { + return normalizedRoots.every(normalizedRoot => normalizedRoot.basePath !== r); + }); + + // Logging + if (this._verboseLogging) { + console.log(`Start watching: [${rootsToStartWatching.map(r => r.basePath).join(',')}]\nStop watching: [${rootsToStopWatching.join(',')}]`); + } + + // Stop watching some roots + rootsToStopWatching.forEach(root => { + this._pathWatchers[root].ready.then(watcher => watcher.stop()); + delete this._pathWatchers[root]; + }); + + // Start watching some roots + rootsToStartWatching.forEach(root => this._watch(root)); + + // Refresh ignored arrays in case they changed + roots.forEach(root => { + if (root.basePath in this._pathWatchers) { + this._pathWatchers[root.basePath].ignored = root.ignored; + } + }); + + return TPromise.join(promises).then(() => void 0); + } + + /** + * Normalizes a set of root paths by removing any root paths that are + * sub-paths of other roots. + */ + protected _normalizeRoots(roots: IWatcherRequest[]): IWatcherRequest[] { + return roots.filter(r => roots.every(other => { + return !(r.basePath.length > other.basePath.length && paths.isEqualOrParent(r.basePath, other.basePath)); + })); + } + + private _isPathIgnored(absolutePath: string, ignored: string[]): boolean { + return ignored && ignored.some(ignore => glob.match(ignore, absolutePath)); + } +} diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts b/src/vs/workbench/services/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts new file mode 100644 index 0000000000..3760758f43 --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import assert = require('assert'); +import platform = require('vs/base/common/platform'); + +import { NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService'; +import { IWatcherRequest } from 'vs/workbench/services/files/node/watcher/nsfw/watcher'; + +class TestNsfwWatcherService extends NsfwWatcherService { + public normalizeRoots(roots: string[]): string[] { + // Work with strings as paths to simplify testing + const requests: IWatcherRequest[] = roots.map(r => { + return { basePath: r, ignored: [] }; + }); + return this._normalizeRoots(requests).map(r => r.basePath); + } +} + +suite('NSFW Watcher Service', () => { + suite('_normalizeRoots', () => { + test('should not impacts roots that don\'t overlap', () => { + const service = new TestNsfwWatcherService(); + if (platform.isWindows) { + assert.deepEqual(service.normalizeRoots(['C:\\a']), ['C:\\a']); + assert.deepEqual(service.normalizeRoots(['C:\\a', 'C:\\b']), ['C:\\a', 'C:\\b']); + assert.deepEqual(service.normalizeRoots(['C:\\a', 'C:\\b', 'C:\\c\\d\\e']), ['C:\\a', 'C:\\b', 'C:\\c\\d\\e']); + } else { + assert.deepEqual(service.normalizeRoots(['/a']), ['/a']); + assert.deepEqual(service.normalizeRoots(['/a', '/b']), ['/a', '/b']); + assert.deepEqual(service.normalizeRoots(['/a', '/b', '/c/d/e']), ['/a', '/b', '/c/d/e']); + } + }); + + test('should remove sub-folders of other roots', () => { + const service = new TestNsfwWatcherService(); + if (platform.isWindows) { + assert.deepEqual(service.normalizeRoots(['C:\\a', 'C:\\a\\b']), ['C:\\a']); + assert.deepEqual(service.normalizeRoots(['C:\\a', 'C:\\b', 'C:\\a\\b']), ['C:\\a', 'C:\\b']); + assert.deepEqual(service.normalizeRoots(['C:\\b\\a', 'C:\\a', 'C:\\b', 'C:\\a\\b']), ['C:\\a', 'C:\\b']); + assert.deepEqual(service.normalizeRoots(['C:\\a', 'C:\\a\\b', 'C:\\a\\c\\d']), ['C:\\a']); + } else { + assert.deepEqual(service.normalizeRoots(['/a', '/a/b']), ['/a']); + assert.deepEqual(service.normalizeRoots(['/a', '/b', '/a/b']), ['/a', '/b']); + assert.deepEqual(service.normalizeRoots(['/b/a', '/a', '/b', '/a/b']), ['/a', '/b']); + assert.deepEqual(service.normalizeRoots(['/a', '/a/b', '/a/c/d']), ['/a']); + } + }); + }); +}); diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts new file mode 100644 index 0000000000..d4fbcc011c --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; + +export interface IWatcherRequest { + basePath: string; + ignored: string[]; +} + +export interface IWatcherService { + initialize(verboseLogging: boolean): TPromise; + setRoots(roots: IWatcherRequest[]): TPromise; +} + +export interface IFileWatcher { + startWatching(): () => void; + addFolder(folder: string): void; +} \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherApp.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherApp.ts new file mode 100644 index 0000000000..12bffe7f75 --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherApp.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; +import { WatcherChannel } from 'vs/workbench/services/files/node/watcher/nsfw/watcherIpc'; +import { NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService'; + +const server = new Server(); +const service = new NsfwWatcherService(); +const channel = new WatcherChannel(service); +server.registerChannel('watcher', channel); \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts new file mode 100644 index 0000000000..a58f2962bd --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IWatcherRequest, IWatcherService } from './watcher'; + +export interface IWatcherChannel extends IChannel { + call(command: 'initialize', verboseLogging: boolean): TPromise; + call(command: 'setRoots', request: IWatcherRequest[]): TPromise; + call(command: string, arg: any): TPromise; +} + +export class WatcherChannel implements IWatcherChannel { + + constructor(private service: IWatcherService) { } + + call(command: string, arg: any): TPromise { + switch (command) { + case 'initialize': return this.service.initialize(arg); + case 'setRoots': return this.service.setRoots(arg); + } + return undefined; + } +} + +export class WatcherChannelClient implements IWatcherService { + + constructor(private channel: IWatcherChannel) { } + + initialize(verboseLogging: boolean): TPromise { + return this.channel.call('initialize', verboseLogging); + } + + setRoots(roots: IWatcherRequest[]): TPromise { + return this.channel.call('setRoots', roots); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts new file mode 100644 index 0000000000..a07b2b64f2 --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; +import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; +import uri from 'vs/base/common/uri'; +import { toFileChangesEvent, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; +import { IWatcherChannel, WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/nsfw/watcherIpc'; +import { FileChangesEvent, IFilesConfiguration } from 'vs/platform/files/common/files'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; + +export class FileWatcher { + private static MAX_RESTARTS = 5; + + private service: WatcherChannelClient; + private isDisposed: boolean; + private restartCounter: number; + private toDispose: IDisposable[]; + + constructor( + private contextService: IWorkspaceContextService, + private configurationService: IConfigurationService, + private onFileChanges: (changes: FileChangesEvent) => void, + private errorLogger: (msg: string) => void, + private verboseLogging: boolean, + ) { + this.isDisposed = false; + this.restartCounter = 0; + this.toDispose = []; + } + + public startWatching(): () => void { + const args = ['--type=watcherService']; + + const client = new Client( + uri.parse(require.toUrl('bootstrap')).fsPath, + { + serverName: 'Watcher', + args, + env: { + AMD_ENTRYPOINT: 'vs/workbench/services/files/node/watcher/nsfw/watcherApp', + PIPE_LOGGING: 'true', + VERBOSE_LOGGING: this.verboseLogging + } + } + ); + this.toDispose.push(client); + + // Initialize watcher + const channel = getNextTickChannel(client.getChannel('watcher')); + this.service = new WatcherChannelClient(channel); + this.service.initialize(this.verboseLogging).then(null, err => { + if (!this.isDisposed && !(err instanceof Error && err.name === 'Canceled' && err.message === 'Canceled')) { + return TPromise.wrapError(err); // the service lib uses the promise cancel error to indicate the process died, we do not want to bubble this up + } + return void 0; + }, (events: IRawFileChange[]) => this.onRawFileEvents(events)).done(() => { + + // our watcher app should never be completed because it keeps on watching. being in here indicates + // that the watcher process died and we want to restart it here. we only do it a max number of times + if (!this.isDisposed) { + if (this.restartCounter <= FileWatcher.MAX_RESTARTS) { + this.errorLogger('[FileWatcher] terminated unexpectedly and is restarted again...'); + this.restartCounter++; + this.startWatching(); + } else { + this.errorLogger('[FileWatcher] failed to start after retrying for some time, giving up. Please report this as a bug report!'); + } + } + }, error => { + if (!this.isDisposed) { + this.errorLogger(error); + } + }); + + // Start watching + this.updateRoots(); + this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => this.updateRoots())); + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(() => this.updateRoots())); + + return () => this.dispose(); + } + + private updateRoots() { + if (this.isDisposed) { + return; + } + + const roots = this.contextService.getWorkspace().roots; + this.service.setRoots(roots.map(root => { + // Fetch the root's watcherExclude setting and return it + const configuration = this.configurationService.getConfiguration(undefined, { + resource: root + }); + let ignored: string[] = []; + if (configuration.files && configuration.files.watcherExclude) { + ignored = Object.keys(configuration.files.watcherExclude).filter(k => !!configuration.files.watcherExclude[k]); + } + return { + basePath: root.fsPath, + ignored + }; + })); + } + + private onRawFileEvents(events: IRawFileChange[]): void { + if (this.isDisposed) { + return; + } + + // Emit through event emitter + if (events.length > 0) { + this.onFileChanges(toFileChangesEvent(events)); + } + } + + private dispose(): void { + this.isDisposed = true; + this.toDispose = dispose(this.toDispose); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts new file mode 100644 index 0000000000..01a69730e2 --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import chokidar = require('chokidar'); +import fs = require('fs'); + +import gracefulFs = require('graceful-fs'); +gracefulFs.gracefulify(fs); + +import { TPromise } from 'vs/base/common/winjs.base'; +import { FileChangeType } from 'vs/platform/files/common/files'; +import { ThrottledDelayer } from 'vs/base/common/async'; +import strings = require('vs/base/common/strings'); +import { realcaseSync } from 'vs/base/node/extfs'; +import { isMacintosh } from 'vs/base/common/platform'; +import watcher = require('vs/workbench/services/files/node/watcher/common'); +import { IWatcherRequest, IWatcherService } from './watcher'; + +export class ChokidarWatcherService implements IWatcherService { + + private static FS_EVENT_DELAY = 50; // aggregate and only emit events when changes have stopped for this duration (in ms) + private static EVENT_SPAM_WARNING_THRESHOLD = 60 * 1000; // warn after certain time span of event spam + + private spamCheckStartTime: number; + private spamWarningLogged: boolean; + + public watch(request: IWatcherRequest): TPromise { + const watcherOpts: chokidar.IOptions = { + ignoreInitial: true, + ignorePermissionErrors: true, + followSymlinks: true, // this is the default of chokidar and supports file events through symlinks + ignored: request.ignored, + interval: 1000, // while not used in normal cases, if any error causes chokidar to fallback to polling, increase its intervals + binaryInterval: 1000, + disableGlobbing: true // fix https://github.com/Microsoft/vscode/issues/4586 + }; + + // Chokidar fails when the basePath does not match case-identical to the path on disk + // so we have to find the real casing of the path and do some path massaging to fix this + // see https://github.com/paulmillr/chokidar/issues/418 + const originalBasePath = request.basePath; + const realBasePath = isMacintosh ? (realcaseSync(originalBasePath) || originalBasePath) : originalBasePath; + const realBasePathLength = realBasePath.length; + const realBasePathDiffers = (originalBasePath !== realBasePath); + + if (realBasePathDiffers) { + console.warn(`Watcher basePath does not match version on disk and was corrected (original: ${originalBasePath}, real: ${realBasePath})`); + } + + const chokidarWatcher = chokidar.watch(realBasePath, watcherOpts); + + // Detect if for some reason the native watcher library fails to load + if (isMacintosh && !chokidarWatcher.options.useFsEvents) { + console.error('Watcher is not using native fsevents library and is falling back to unefficient polling.'); + } + + let undeliveredFileEvents: watcher.IRawFileChange[] = []; + const fileEventDelayer = new ThrottledDelayer(ChokidarWatcherService.FS_EVENT_DELAY); + + return new TPromise((c, e, p) => { + chokidarWatcher.on('all', (type: string, path: string) => { + if (path.indexOf(realBasePath) < 0) { + return; // we really only care about absolute paths here in our basepath context here + } + + // Make sure to convert the path back to its original basePath form if the realpath is different + if (realBasePathDiffers) { + path = originalBasePath + path.substr(realBasePathLength); + } + + let event: watcher.IRawFileChange = null; + + // Change + if (type === 'change') { + event = { type: 0, path }; + } + + // Add + else if (type === 'add' || type === 'addDir') { + event = { type: 1, path }; + } + + // Delete + else if (type === 'unlink' || type === 'unlinkDir') { + event = { type: 2, path }; + } + + if (event) { + + // Logging + if (request.verboseLogging) { + console.log(event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]', event.path); + } + + // Check for spam + const now = Date.now(); + if (undeliveredFileEvents.length === 0) { + this.spamWarningLogged = false; + this.spamCheckStartTime = now; + } else if (!this.spamWarningLogged && this.spamCheckStartTime + ChokidarWatcherService.EVENT_SPAM_WARNING_THRESHOLD < now) { + this.spamWarningLogged = true; + console.warn(strings.format('Watcher is busy catching up with {0} file changes in 60 seconds. Latest changed path is "{1}"', undeliveredFileEvents.length, event.path)); + } + + // Add to buffer + undeliveredFileEvents.push(event); + + // Delay and send buffer + fileEventDelayer.trigger(() => { + const events = undeliveredFileEvents; + undeliveredFileEvents = []; + + // Broadcast to clients normalized + const res = watcher.normalize(events); + p(res); + + // Logging + if (request.verboseLogging) { + res.forEach(r => { + console.log(' >> normalized', r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]', r.path); + }); + } + + return TPromise.as(null); + }); + } + }); + + chokidarWatcher.on('error', (error: Error) => { + if (error) { + console.error(error.toString()); + } + }); + }, () => { + chokidarWatcher.close(); + fileEventDelayer.cancel(); + }); + } +} diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcher.ts b/src/vs/workbench/services/files/node/watcher/unix/watcher.ts new file mode 100644 index 0000000000..259aa6722f --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/unix/watcher.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; + +export interface IWatcherRequest { + basePath: string; + ignored: string[]; + verboseLogging: boolean; +} + +export interface IWatcherService { + watch(request: IWatcherRequest): TPromise; +} diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts new file mode 100644 index 0000000000..4065126218 --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherApp.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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; +import { WatcherChannel } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc'; +import { ChokidarWatcherService } from 'vs/workbench/services/files/node/watcher/unix/chokidarWatcherService'; + +const server = new Server(); +const service = new ChokidarWatcherService(); +const channel = new WatcherChannel(service); +server.registerChannel('watcher', channel); \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts new file mode 100644 index 0000000000..12235b76ee --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IWatcherRequest, IWatcherService } from './watcher'; + +export interface IWatcherChannel extends IChannel { + call(command: 'watch', request: IWatcherRequest): TPromise; + call(command: string, arg: any): TPromise; +} + +export class WatcherChannel implements IWatcherChannel { + + constructor(private service: IWatcherService) { } + + call(command: string, arg: any): TPromise { + switch (command) { + case 'watch': return this.service.watch(arg); + } + return undefined; + } +} + +export class WatcherChannelClient implements IWatcherService { + + constructor(private channel: IWatcherChannel) { } + + watch(request: IWatcherRequest): TPromise { + return this.channel.call('watch', request); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts new file mode 100644 index 0000000000..520e9bc7ad --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; +import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; +import uri from 'vs/base/common/uri'; +import { toFileChangesEvent, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; +import { IWatcherChannel, WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc'; +import { FileChangesEvent } from 'vs/platform/files/common/files'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { normalize } from 'path'; + +export class FileWatcher { + private static MAX_RESTARTS = 5; + + private isDisposed: boolean; + private restartCounter: number; + + constructor( + private contextService: IWorkspaceContextService, + private ignored: string[], + private onFileChanges: (changes: FileChangesEvent) => void, + private errorLogger: (msg: string) => void, + private verboseLogging: boolean + ) { + this.isDisposed = false; + this.restartCounter = 0; + } + + public startWatching(): () => void { + const args = ['--type=watcherService']; + + const client = new Client( + uri.parse(require.toUrl('bootstrap')).fsPath, + { + serverName: 'Watcher', + args, + env: { + AMD_ENTRYPOINT: 'vs/workbench/services/files/node/watcher/unix/watcherApp', + PIPE_LOGGING: 'true', + VERBOSE_LOGGING: this.verboseLogging + } + } + ); + + const channel = getNextTickChannel(client.getChannel('watcher')); + const service = new WatcherChannelClient(channel); + + // Start watching + const basePath: string = normalize(this.contextService.getWorkspace().roots[0].fsPath); + service.watch({ basePath: basePath, ignored: this.ignored, verboseLogging: this.verboseLogging }).then(null, err => { + if (!this.isDisposed && !(err instanceof Error && err.name === 'Canceled' && err.message === 'Canceled')) { + return TPromise.wrapError(err); // the service lib uses the promise cancel error to indicate the process died, we do not want to bubble this up + } + + return void 0; + }, (events: IRawFileChange[]) => this.onRawFileEvents(events)).done(() => { + + // our watcher app should never be completed because it keeps on watching. being in here indicates + // that the watcher process died and we want to restart it here. we only do it a max number of times + if (!this.isDisposed) { + if (this.restartCounter <= FileWatcher.MAX_RESTARTS) { + this.errorLogger('[FileWatcher] terminated unexpectedly and is restarted again...'); + this.restartCounter++; + this.startWatching(); + } else { + this.errorLogger('[FileWatcher] failed to start after retrying for some time, giving up. Please report this as a bug report!'); + } + } + }, error => { + if (!this.isDisposed) { + this.errorLogger(error); + } + }); + + return () => { + this.isDisposed = true; + client.dispose(); + }; + } + + private onRawFileEvents(events: IRawFileChange[]): void { + if (this.isDisposed) { + return; + } + + // Emit through event emitter + if (events.length > 0) { + this.onFileChanges(toFileChangesEvent(events)); + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/win32/CodeHelper.exe b/src/vs/workbench/services/files/node/watcher/win32/CodeHelper.exe new file mode 100644 index 0000000000000000000000000000000000000000..4d4b673782552467a10c962b2006c0a229878153 GIT binary patch literal 83968 zcmeFa2Ut`|(Yf^BNFy#IPzx zKv6Jb#H<8!fI(3}QEv5_85j|FefRzEcmMm`J-es6ySl2ny1F`^9vnJm38Er|Xz;eQ zAao2u{5X^FuYnMWc6q{xR0T3N*kV#6Ut>J|bKUC^-` zgffpa7{n@;7sUrctSbVYNgKHTSRbGpgtKLz7;IN~ux=DYN9tV>s@K7`qabw|vyw!= z0rDsU4npiyDl`p6po*h(95u&S3N;nliAJHNG9l=uFUEG$v7w^pARDyhXedyU!OpZ~ zEWlJzm13@j8O1m`%q)(AZD~E@#0d!sm%|60+2+8@U^DogAg)HU(B{zi8W5G2&|wvr zd`-YWh86_fpFC0E&@7pd)Q03ES$UlSAx^8OLLZ@7^bw~dG+2$)Hnq~A3N&*tAMgOA z@u4bglJ5zdWyz2;*TeF0^AV6kLBmm(L~)Z*Aa(BnVq+8Q*w9cjgy0UOJ$^8Sxju+c zqp8x&4Il#Lkk#fwyt@iTBNciLjlxI;IW&tQlw_RjK8TWx(`g`&HaU=C-Gibjrc1`n z5DPlT$cV`{0_~`Ls1(E85XAB!Eh&b|H^y;BDy}vq8QY`#Pyv{TA)?@&@P3xk@9 z$tlTTa~k7218@w18OSyVn^{;=G`dN$+NAQK|FAW?kut4;3d-OJz%mRq1_z{K9RyG& zn@#o&d8!&yLycv(3`*xPIjlbv7%XuOEP%!`LYlIKl%gD2Yc(269MU;7&7Qg(x>iq3 zhMbtGJCJo@%JHpmXwi>D)9R_jp=axXDZu0Dsh9*DA36vP6o8?J25yc6(g8ICQ(*+cqaBPs zG=V)x0>={P_k#pOlkb2712w)Q4k_Om;P4DVe@O5LNH8#LN-$0m42-D~jI#s-BP2sG z5OWQ}80dST5kugDF|HWHMM8x5Za6Bf3-k~?XL2aXq;fD$fY;MVmtau&?wFMgI0iXR z0u$~d@xa3b9&x^;CWC>LP33z)AcK)-SenX+R$yt8qFFMcS)^#@4|IxhS|pvESRko_ zoLB{E14={Cg$mJTAx%V4kE}CSZR{huCXCqAkgVdN4GtG^Ha6GnPi6 z7z>41EgaBeEg_JMfkQwYQ=6iXdhW%xq@%l82wRSVjR zhV-DPK$qtTz(-;1G55s;egG(oA}tvJv}`%%!yp~&51R3ZLrk+9MdJ@P!+Zq9yQ_*b zYax@t_a}2<{8|*LX{$`p{3F(sf?~`^0@{MmNX1p;!0`5+0WpSP6rj^!UKI}l;9d+t z0HA~zMdAgZ8zU7P3G+b65Cj5&U^D=ErY3(3giQWefN3xvi^YI>#gc^>MdB6GVCE6$ zf@p>Snu0CFC=#zIPzIzLjYDIwg z(1;;}2)r7B2N==xURE|%J#Bj0LKns(Kor0VIfRVUVC~9=B^mS{;}LPOkFEmL0Z6;jAU!CGABKtH0a7ABZ?l{n$UT&0-R04ToM+M@IDDYkWi6^Y0OC& zLK{e9pg0oFBr)qqc$s#LHW59A>M)QJy#{EkNVtKXO=qA#=~%Z<^eTw68CY{i5{8m+ zA_>iz7(a?xLsLR4m~)v7w1s&KDWRKW{4?aT(Oed;(GC(;k&q%c6zXEn#=L$cOku~c z87P;H@kdE`i;Z=73J?q_v7p!|RFF0-*6D!5|EWML4S{h99KlkTUoU&NCXOg|Ac4#&(Exh@4S}2}x**{$h<}lO&9%RH5Qp_91Oo$s&Gf7M$&}&mMSB#t$P^Qb>M}Ayv`An@q z!WPO}VMwM>5aA5#v_Kfu5XwMFggbcta@-jpHIEn$mbr$z30jI&nTrV~+C%0o)uOWE zC@fS!rZQQ}h(PE)C6ZJwF%D8?lGFhr6k71EBz2tNL8?NW0$nD9t<7xxrincB!ql9wZQgQAVLN>X>^$|;@EK}m`UTN)ko&771% zT0$44aNDHlqS@jUN}@jo58O*yP+Wo@IxJ410!9j7$1-lm@6;LivR; zpD;kLWNgMonq-YKksfO{g^Lc#*u@B)5~t8SIcM;4M^fudv_ft&_&Dx+Zz*_;`v{Og z-$?k0#51WFF9(nSPr^`52DU(Nq>#K+@GU6~1=RyyO5Y877DdAQJ8@%D&Ti;|QVFu< zisNFa!o)PPG7YJrC=k3Ul7!Ppm_))163!#xQWCBuVJ-=G0W3rZ$@p;+o+05yfalPC zfa6dlz{%jJ7%GtPHQGk6hWH-(8yE}D&>Ns#%jirP3m($dh$ciZvY?3MihTM)ZQRB8aX8E?1kF z#tcM`B*vD=VeTMYNg8ibA8&Mkd6V!)r?g-H6%Z)>xsJC1w)MAj_d}0_H3h`;gKz0=c!`GB32AF9?C;6__D6)<$VZB1# zge-`esDgymBt!(pP)W!op)(0xNElAS3=-y&@C*qN1#@vp=uE;85*CrLf`o{QX$TS; zl5ijidlHk0bA&hL6s4GQfzq8yg}wymQp7ZXEkr!@StiT^1Xux|Q^~=oPryh_N0*QY zx}5?MK#V7Hhyvmw@sd!atfkaZXhPf{Wgl<4c-17u5h$FD(xeCo30N87=SN%$^liKo zqCok$bQW2*Jj`3e&Ok9kW3qL9f%8BGh3#R4ia&88)G;bl5Jw{XBd7Aj!bik~#>Vj? zkRT>>s>>u^_!JUB=3`VqXlyj-3$cqaOZ?mG{$0W{;35}bCFrJJ!wh<4Q5*$1zG<*uE;U3A0ia?H- z;wNky10p3%4~>fF1qUM+kAXge-CU8&aJPZ}5JI!K;n3T6FiOB}?}rPHiRDd*O!VeO zPlN<%Nte*5s4ys2+CHAXq`QdQLFzPav9bJE(hvhueXucpWGF*UMd13Jr zC*tnaCPR#aW`a=;v}va-C>I05V`a4k1Zm>oFrFlmF+rO+N%Lbq$O?;$ij0#ndD{qz z90!C42fIcJVqj2mfw2UR87IU?hXcM>aBv)ag+h+9W5a;3;NTuo?OKO7G!#4x4^0O0R}UH7cYr4vL~K&hx4XFE203HoO#I8H#P#oh#zJf5;ur!#BgW; zuxKo_Oaw2{cLGdR>Ux}I613` zsBL1B%@q&+VtqlS=+LM^@e?M%PysVGX?XYes3@{d5TSXCMYGfJbju#B4cd+rDACgtk+dmMe* zIC}ja2XrS`SMgz}2#Af07v5yve$oR61r zq&-LayW5fN?i(K$6AxpWBo9-PWrznial5!mJSPbcL;_gK@neGBrp3d0DlQ3$$4|0Z zBym!%rvNXFCdR_36anKAtmR0wTQnZ=hKIr=6+KZh#tet$C0;@cPX?e+6pNx^Qyh&Z zqJD^rFeVO`;;{CC94_Pt0EWTG2LVcg7>rqxd@&Cfa$*3V0F-eMCLro9-L1D z&K2U`@cK(}%42~BmWa3vJ+5DI{PI?&@% ztPtw$M^a&9Vl(1qG9#t(fd=b^4Tg=5^~2_l11^Hoos^lYAygG`*a<=g87fg2@cEje#HA2o zG=@raE&&wsT)=5VL{C+L&ZSV4l&D<9qCp-DB1$-dF$9I7L^Ve&c$KIOjtaRoq!HlF zG`!uT!3h!KaH*<#N^K>m8Zx0oRa1@%SyWf7RCg!|wyi8ZQZfZ+a3X(HS`jM3A~_Tp za3w}p1*njo8UiY;CyR?ZsU{I*ItkJ=E}Q_6)x;GbAOqB;KpCovEH)P)sgNQK5rnQP zlP(sbszA}jQm`_JBb);)a1u^cQqt803NVgXpdwg=(A8oy>1010g?+=T;G z;XZM2P#hd#Fe$2fs^+-*U@7>1jLXAKgw05{3P3|>5dunPA{q{KK`!Y&T%;;I4eh2X zJj1}$!ZT#zJn%y^fJREXpfY%}xUj3BG)s%gR8ke*kabQ~bx^_5lR%xVeBAt7cUqQ` zO+kP7{?(g6{z~etS(25v5A4ZQF%P#jhYPgIa2O-AR)_Bp&?gXFuIe;2GCY@F_1hmCD-gt|gsw+rhP-aE8MSa-WH+dkcU+4iyT4y%$r z-Fr_6onY59d_q{C2>4E>Kp>_qe5Hf`ppFC_%isqhaKH->Tq1)CP&5K!Lt}hk=P8{x zVJhP%;LnldBfW!$nejC%6E}F_G5_k)+=cB2=RCB>k#7a?XMuj-?BkCsfQMX@G&3GRNI#i zoirWpE_Q&&eLCb9_{0wIxVRtYAY=b%n6p|+-fq<_R zZO^7~n7m}{VUtLpH{xa>i4>^#7RV_0G7}8Y4Go8wC-Q~uLNJ6r$Q|PNIZdnmF8;zH zWfq4{WUlxNhBQZlLaq=YKq_AMyTghbZ-Bi8o|FOM?ChG&ZgO^@6oLj4`mm(EvQ&uS_L3TG6mi@PRV${sTWl zL1H+_#p`~&6Ty1}OdSi<7|Lqn4Bx^oN1Odq#3AD00ceFdgW!b<=p^bkO@Qe2^ z(z@EhUc{#LMaUVx?h~Z7@MaNjVevMvP5X|@u>#6Oc=6+bRDs_cQWHr|OdkXF$5xyO z`>i;j`5DJWyTArq*gD$4HWXG!AP4K=E8)W}fc22J0Zxgn`FHx*k@oWgIe7ONPww&B zY9rRA{jL(%qZetnANkr_uf6T;NS=YvLU=zn6}&;b5B)nnT;4oCpTUak&Uo5T^z?9r zdu!OMVV#Hj__)}eFnl1n7?06#5lg8UzeFj2X9Vo2t36!@`X{b`|9EPQ`pAOPI)igd zXlqPf+!#h(hCG*a+Pnk}FJb4IHwyFEb1RHyvS#mFV4bIYq~Q9P{RwaLR=KNhJ4QKl zk~QCV8$D>KY4XSwM`$^$B%?v+RGeuuP425F23I^%SYESss^Jf=qgL?6wuNVEo8LN7P)o~M{|3XAeG2$f_qbo>b%vKA@m4; zIWGt;y>;pG<#f5`mk%C1NHe-y6|aE0nkF{7v^a!c*Hk7vv~!F3^@&?wzRGk!!w2=6 zb#?qe>l)-SV|v=2w*?jzO2VxZ`kYdZb`ahzY?AZp5>dU;;?R;+>qDAqZ$t)MUwoI! z9X&_qd&}PbiPv(jcE4CuYh5ai3`*F z?w23*$D(a&v4@{@vVmRrj!dU}qAMnk9IWIQTt0NeGcLXYO$?VYxVHf`1Q)>}HcsrP0VK<~KFulY>G11wUzn?m}| z$T^XL<5n%yS#Z;`u;i)=pyj$Lr5`wTm{>D*S;q6_v5rS9LXd~uoh+vnt8t-n+a8LT z^6%IhD<_0c`hqU+7xZ8NcFRXm)`{qdZZCp2&T+7Ec=}<=dKN)-9u(8Kuqk!knsm4A zQ%+KX)Rv4Zt?YZZ{KhP^$x2nroYEJ*z1t`3R)Ev_PcLoJvu;X-@ndy-LSB@dJ|=Q1 zIBQ&M;C%T;bZI(+>YP_8D(!N>;zG-Qz722atSz|K9wO#^+G`YP;qSA?U&|-L&ISQ!S;!6fG1>%L?{U zK=(^3S8Gz8j{;!{^1A|rz}-bRt+x$Q@2ynW44DQ{Vq~9H3X5C7|cepBK*lzNP2a!5($zD+AW;poH{l%w2r)xc=j5jdxzoa}iEr zIEcpStDe)$3RWJJfR?6J51z5_*4x}a?V7)+s3MD5h5PDgLw3Iroh|#bT`G#6;+VnA*DuODIsysDPu+1nW~=`Gs}~1{jym-E=|vaHSCBG|$5gq> zZ+;sT-P}^C+_?R;+BS=ZihYku&wf{!IBJiXQsKs`&gkSUgLkeCkyq|r&mR25N#*m) z^95s4r!{`+wAw`&c=XGt{=)<+!Obyjor%wlyW5`;P5c;B)K97KiLRovBh%?#d8V3H z{voCPk4Fn_zpLMhx1m)|YrMGB1)1f48P)sSy@m7kd=B|`&7e5%@@M7Y>RCBxode2G zt6ul|o&Btnebckb3Z3Rm^0D^Sq6LeZDB4C-dlRf zS9#rl!FO3PjTh&J6DszNxvNf}Hu%^lUu1l)uu8OC+c#D3p6C8|j`Q~fO~wvZTpB+Q zW#yb5)&~{cujP#UK2C4k z``Q76%R#xWV;<0v-B(W4TAhZfjD~`~sf(gGKy7(g!MSLJ!IAJwe=u}wmwm-LoFDQQ z=239rhp>E&w6x){R=zWrp1F z5H!9xQ%**o*qHjjnG0gu~{=h-!#o$_5RK`C8+u`@LY}A8EEhIk4Ot{&d}0zj`g9mLvG=-ThOu-wvK@-~95!SJk>3 zsdF0hEd*}XZe10evndH?Uk>fN1)hT&tZd}4iejkEIQzH*ueO|kd?DSiL#t+Z49EDX}uPCp2|S3eSo z&H0*DPcK`moX(yLPCTgT&6hP!gRWH8_U_l0ezMzwX{EfE3xei$#_aJTOe<1Y3x}){4m!Ip(G=f_qgh(Hx?$b%AXF%-qAQN;%!Y)bTaTE?)ebQ zL^y#7X5 zW7fnjl}8-pZAx;CW^|2kUW?ENH|O|U>|3JzwZ)BAg;`xYAt6UJzclaolGQI)31PdS zMJ#PDaE&?^u4P@;UCxvW`(7h&-5xCdqmf0mHD0dw5y}>>>t^;s^x=`}@P2TYT!r6t z-nWA<%5zUS=5Jl0jEY$2yoOEh0d7=uS>WZp05jZ*KHW=i3jgHnr;k^!?}JO(;n@3I zSkT6?oH2A1LVG9rs(P^LS_&Wxp$~`9!iIq(t+J;#`{#rmRsfl<;ow1#J?sGkvW+~4 z4k>ywHuJ-WN9~h2g6^7BS&G`Pp@K~Yc z*!mt9Ock6}HW(L8`S^Ao!O9J%CC>K^RuB>!j6|Y4nouoR7J2W~&!~S!i(0>-?6P3W zB7)^|hn4+F`P?e|Cdc!GKRneG&J8O?vvc1Jb)Ym@JStBdR6pFqhEa5``p(oLO6f%h z^KKohdG7A`;X84|%P>O=QGLtx&uuYi=FZU0OWgc|yM1qDQC;4xr8(Z0LeZeT&Gk0= z54PXspMUd0srkj)$MEez^Iy!^%b5 z_or2fbnq+|zUR$>*L!lnZ>l`rqEkn{YQfBJF++P}{N}H{@FIZuoTYb;B!H%Un;mYRlte6NXZ=p3+A?R1UqlyZh)i9XglHCEycFeN^?!& z=j9X2M)v)7!TQkL`s+ujYYOt_<>=SjZKK4TdfO{6=y6eH!`y~9i^CsX>X&lAyess+ zL2Hq2ZNG&Vt;@s9LX)m}eNCOWd9_NyP^anPRlEBKp01t?4NjO+R2r|g3_a;s7zur> zA}hQpW%NgN0ueSNIUQ<=HzQbz8)%yH0m<0k8zM3X}E^$Of zfA)P5$sthSw=!e%UAwF2xI4POL2+?8C5Mj;y{XUwOXjHHakG<5*S>5%?!&3*FL%oA z0hynzc(pO2P&e3j!>BcxWvey9vz^Y1rfo1PN>)wGg4OH6+|hq1uXmh&bxvYKO(ln4 zxNigvJy~8Pzg3~AK7QzMg~F$+=PGMvpEIy%UKPA$EG^NOa&W6W&P&|?{n#s>Yp-%A zm4~&~c%?6@!#j&3M~-~U*1y%Aa>^{jZa6*U$qS9Hndy~{mhbjjmRzqVG8Z$I2f%8j z!6YW97qKS)I31k1+YIf{Z6=>fy?SO=Z5x~U$AHpF71|j=AmZd!R?&xqG5wvIi+e8j z6V!TGT3+puy6FCc7+lIiryEQv=D1fCe?3zf=O9UH{ZrB z#2o`B{6ycGb|EOt2I?4WX;E^Uvuk_Vy!b}r(?NG}(RF9wst44pIsAr)!ex&hDUaUN z6?DpW4hFsd)DoK0eCnE(t+#)YKa=wc*H?)eGBD@?N|~NM?8=VDcVV_^KhYJ+Ed z9U8VD76CiWMU`)zvJa)sFSByrv>&|V*vC5>pgx1a&Cc3ifDEj(Uf=6F;G`B2e#&g% zI~am8Mx>;l_-u6R<+1+FOVjddaN~y+quDXl>N(@Hg$IlJ%jxfrRy(a8x!!CB5!55! zglfU>T=nK;^TFNw_Pq}X3i|H#Du-+)Hv#vz!#QsROQSNSaYi0g&iHor3zb_0P`m1s`&J*AMh z%^a<_2Cr@hPrN+;;_)FHs{UNyK=h~%MB5j6oQJ74*U$rImpy-es9PPW3G3ecO63sR zg}K$W#9a27SC7=+1&*1GcVzYUCzR5Wv2*U1JGXD2YYy8Ie%bF~XWxo5`*vGsQ6U()_;dAh3b)};oT$+UMj4!+c_D*3{HRIXHX>G^@G)5eK4+YKAP zTAmhHgKihg;u_IT<;X-~kXNdvq7i}o;HwP~C zad!?x;Ty5SYV{GYfga1{EaYNH3dY3!FK-E!^-<- z(2_k`#FTFAlwGw{C+Gh~lyS9`P@TV*K%QJXthhRN<@x$A9;~bU~Ttlslpa4ak z?DO01uj${V7`n>)Mm@OI zQs>W-_57v{&51$!IRv-Tf3Y*P)T7>gs11uQ4}!HOoB&i8Xu)a3%=sLx4eMr}=iOfu zu#9sjDY-9yE|_8BPSp%p+#K8AOD8}{FAB~fYG$6t2N9h!D%W}j_Au0G7`JrJ;Rycs zqkYffa|`v1vDQONZ_3$2=Np~2@KvsI30h*7WKT(oq#lS#(SVZ&w@-EBtQFG`)p_Id zBP?$V@8YUMQI7&wOlayIr28cVl=&}=8u-8uZGi>h!g(r>y^#S!c3xuD^S&GSgh^y#N}xy!S}n#*>ZZIQ`7 z4^CswcC|qNKTIEkq}%(qZ+EZnJU7aAzVqR@&R;@KS=xD)T@#g;mw&z$-$>|FmKa@8 zVqV#(Q~T+{OgOD_-+HF}TmESd99MPCbs&^d65(ts%Z0C}OmuJ<_P3{HQi5*=)|^wv zo{u^F9L@mY4yKQ5-k^|~I1Z0Fyw30n;gxp|TGJRzRLRaB0Rg}$CMrE@& z==y9mc-7I3ENygSt`54nfQxP|G)1=;nW5W@yP`WwyQA_Iw&?ykXY_EBCwjEi4?W&K z5Ykx5A77!bpWdP7 z&o$`Vmrv;X*GBZcxe0y$4xa=5eg3t;zZUpy3&7vG_-)$%PYD0Z;y~a3Mq`rwpGyGF z&Wc=XQp!KD24;s}AkwtEkrMt>Q%KlB0>SC>pG%No2MN+@{I(K5vddKC_YCn9duug* zi@!s55CQl$ewk<+l8pU|U6vncWQEB}_$7NAKgr#(0Gx%}y`x9UfoWR0Vjx?tRQF%9 zOUlQ5EoKUY4i4K1z^Gr9F0mgbvFe1?Q?bHvSqV7(Yj$z@Kw`)c*G{fo9sY*BmA}n4 zT+AZX;P2Qw;3w3{{`vQ&Ba0-<0?<}IbPck5f`E=n*O8@cv)>O`stM-oklpBql77KY z5S_6Ac-mQ>Vg940zvL&B$%bslE{7}jgQo55r`eXtgreH9i)FTv;1~!b?Igxp$#Uq+ zCLmu8SLm zFc61DRB0|^U102GM8=27zM~pZP_u|P=gu}v?mI?^JE4Q>1LDvM0Q~(B0K`=e` z7tWIpBGKJHk?7uGRQKQ*`tYO>-ZQAavIKp4QHDOhynz~Cm!pQa_fX@zhp4Hx0yT-M z;jH;NoHM^gUq8QZJ8S;;oLTG$|HA*LEC9pA|AzYhhf;z3KjCO;(N{6+|Jy7;ju(Zx zJ${cvLJl0iRY)7Tw2;5F=a1yT^4IqNiM)-zBgUV}rS!P&KXG(GE~W?4zFNA@?HD>D zC+S;SoDHEW>JC3JbVx3ycUAy$*#7sDVd$7#Oy8d*2Y{D&V*CDryd6Ekl`#BBZY-52 zwtq{je1gyd$HGe|(r3hEay?}7$y}K{Y*}Z|Ng$I0=_@x&BM}EiL_2 zT1#Nqkh=VpJ}oT{>aFy0WQQereo^0+7P~g_z(*d>{+0Y*a;?(I$lui0iuNNp3<-Vy zLLUn$br(ZvjQET8YV`-OOB)06OMj5a6#JVDgXS;%O-wKKXBh^$o*mkgq;KmlG7N-N zRUm2ePfU-!QQ9A68E~HXA?ck}$prhy{%$BC`5_?bTUt!P^goR!9WjvQgBJE3jaMBo zNc1I}vHjHdLpw;clF;KJ;pc(91BQ;s+8BRk_-lG7wOt+lhQ1wxZqL7?mto-i-lQr~ z35)();bhH$;WtG~4Ao)dv>EZ|*l#NGGb4$S7Kg)9a`10y$&iG|iQGqjho2^brC?tR z`y~h)C80!=A!r4x1pL_dywes!j1Xw^RET|OAEjl^Oq9-CR@r2 zVt$9RrA3{s+qSlma<>aX6ke^;H1MUJc6l;M2^n5bs2D=x7o;*|K*`pX(6uN3kqjus z1nuSiL?&LiwY2`t^Vu?SHDT+l%%56<(0g-H>oVL%fIHQ%0P=fCgZMtd8WJ&4p-;jrgY1~GJ zm*Qe9Kq*~IyPF~egZza2GhK^Bq9jL7e6_0sx)zzwyM&_T14>79GK3tg4u1X{y5B7K zH|z16c4@V9Tj?FzSzIyPZf%4ewHrw%_JcOUj{Tt3ui6MZ@+%oXZ6o}_&iX(3{m+D? z2mjoUej>zu2iozce%YQ-+{bW}OM4{_A=!>llK&%F2ZZejq*B{SRQMI0Y|#vLd=wy+ z&_0w(R55_qFR3Jzk<&5zYa&uQ7W9{*q&mnF0mnyNJcJdJu*g81M)+?!kzc*Y5P!Zx zs0o(&_z~f+Gw>9pSCAocDiVjvLUD+h@dLju|3C6b%a!UO(Mu@NQ>wSL9@2W@?F@d} z>fM5X!gJ&6Vh>GRV%o!;T^4y3;~xU{82 z@NW#n--Mzizblw7S&L_pzbR<_J;C+uvHPe886$=?&a5dWUw z-)}t9*7-O5*8>0BE%576S#~7uKn-C0H7_2cRM{H%T1yA4fP!q8plKX#$3n!}cnVjP ztgC+z)RqktI0s8mI><$AZ;{&V?nsHQUTf(p^tDRdImHT;u}Ja@M=l<{(}(n056t-Wnr0E zwzv$qWkZ(POx}CJr1z3h&JU=|1M4lazcUI0WKyOM{kat$ZXNS96@2B9qDfQJC z;aZD`|I-WrC+5 zrYhI$M+OoNIRxNkxLVPGH&IuUGPR)zGcz`v5E_xvWYB=e(%85h30ge&iSYzo$4rc- zb{3z&V7w*;SUQn(Yy-14yqO*o3xkJaVkV0>GZ23o?_>s#4855lC>d`HCKJ53{f29_B#wXxnh|?qx+@1i9Cie*1b|x}5xNetlIb{3ibj zYN~lE-apsBf%&PHoTJF!#(-vqqkF`%K(v;QO_5&^QcqoUo zc=FQ{5ACq@XDI+ui-mLX1_OYcRwsPM&5@*)2pU|-VW{IQoUeuNUCE1&<2y>Xr~gr2 zyYkxW(`wUJeWjR|AM1k|ac!#MErM4F<0z2IULBBfN<*B7X)rCx1HAwA?=Wk~N&gOW zQk?(DnGDhX-NWxU_zx5=Ss7)1X&HU>DA)1}p~D8!GTrVgPfo~*Jo&Jv)vfBt4M*ck zZ0GB92D+`ubU&|nTSM2)jpILNrkv|%z0ocngNA=(u25gO`_i6Ir;}?NzIu(=-z|TB zO7Q7zZ-Rt#e}2yKZYCqw+!rx+y4PCzto3fzX~?iv@;u+Wxh9bDzK(7f*~l)m&^Tk7 zV(7shQ(WuwsfXW}i_3K>iiH*<7ZmMnKyznSc5hA({8Rb7?>C{Dlf&#SiZ6TjUo_$Z zGcshzQT>Z&0vcOBE}vD{oPPNH?1y>Y#aCi=qOW+(EN__6=`(w4`b-nEeU%9T5ev>= zVe4$KzbNc(IOJpP@*@gcQ-+)5I~Uh>dk}VZjm2==EsqvuG`*?m0Gxf1L zHE+zyf-*vx?h#u>CznO;FN&cWQL=T?67KK1u_kCj@nc?}xS<;S{Y5IXxUr@~8pn^m z(k0v6^9)opv(q-#p8-V_t_#PuxyJQ@MF_DZ^nyxSfX1J4Bi$~p85TNtqo*aTRlIfC z7;Cg?Zg``^i`pPUUO2l*^mV^rbEkxrypS1d6`Nk$>gR_NODb)f&)BXN4y0dPGcvSz zjm1r?uZIogg$nT3toKzbe{!F^_xb7;<-^FXyWJeUV*WTw#J0$MdM^;E_faUUJBZ9J zwbE=4AoFKhY11Iss+D#Sf(Wg&ItWZ44@f2{6dnTx^~~TXVAm?P9-x8fvny9q<%P{9 zqRX@B&S{~I&(Bi0SGCfb4j>P;%wV5kGn9)o?>hB6eb;Yw8;$a&goCPu6hr0MHiethZk>TprXwFij9>b z`@QSeEH)au_ipJk!+|2Lv`?-ky)tM|#}C;{-=BT;{DqtT`xl>4W$6}MYZ#W(MH2(k z8#njcU&$0Me>ukX%^kf&QSrA)A3w}>G+`+e@6S7+OPO7z_jKLhyZS{XsfvNIgY|D# zKTv*J8Dn?MY?EH`{-aA6rt8lntX{1#t8bT16>qGj1?7iw8cReAF8gJn+(&orzS&Vd z^w^80sfqC)ZF8#GUo<`ESDJAzxClK8s*`WtyP0|MazfSH(-Z3=Mr!O`_1W}-%goPO zX|e2`s9vey+$>!{O2aq`h){1&{iR!V;Ce&-`a!6`}g5aoK$#=J3O^p3rJ z-+#@i6tm7_8#!UZO0!ypPw^FgGeF64Uz-;R_jl3*LxcJ)nv-;kaF5FgYZ@F zigs-uwfNx$e<(k4n=4^=`p75RrQK5wd=l9&&c7VvQ6yRpe@`9$Q4ELj+4EmcK9F*G z+Wv=g#{hYg*KzoVP%>Zi$sRjq@zc|Vn)`wZ##)Y%vuRdmnaUTv-k+m?wqj1a-OZRM z)$cd2K*hcp!%rX-GWA->MxpU=e%?l&v;xi z*R)8K{Bp`0yA!kNF~bCVsJ8-Y&Sv+Wp8NV#U75U1^TxG9i*mB!iq|b?*Xz$$Q8;Kb zcd2FL7xMzA>vKD)eZ0}%^u*)$?v0aei)+{RH)LNfc^MuuaM=?V4x<4%HcsYKzl^+# zRau<>HYrG0+3@a`6UWEh;O%WqiW;L2+EnZCec?BmH5l?zfkG zm-oX`2MoF*$`1%zd_F+nW&3@1-03W%UK&DoC*8@e=LHWB?0zx(?$ooxC<^Jx;mO-7 z_Z&BUb${2l;nSWOXb26APWR&!ryn=}Q_lDCvN?s1*Dl_w;I=)+o2Hi* zt?+d4TBcZIBzL^ILqF;NE96HoTl36 zs>LV!hdt?;bd}jLdwtG{f@=~A-()YY=~jR236)+ZqT8Ijnb)9_5}W4}SNkye`pwRd za0y<^!YvcShZEm>ILd4Ew62@8pi;i#VD4klSDl`p^xd;IHdWbR*PgXwtmBg>!@wa{ z*I&vmE=k&^^>}&kE4jYKnsBCQ(m~i=j&C~C!BlL zU)_{73<>Q_D{9}kDzwmVXsEqde5u4DT)^RXdQmn!anR|Tn^y?S@66NsE`Mmmmb%w_ z!-{iimoD|Aqq!p}&ZT?Qk~WWN%u??Xe(Th5{|HkoQ7!-p15K~rc;lMq&n~=3bL@3M z{z=Z5x!H+6Ub&iv@DizGlLf^zh|Jcw2R04 z4I4Raep#pa5gZ*}N%M{SYaaA_R~85@o)tzJ#B` zp#-X0DSYR?YI|7Sy>VMhKAimOZaR`cmV~j*I^keuLa{;gmDvSqJ4X!OYJr5No6N3V zwbtk=k3!^LRf0!b=J*x8K2A593M?5OCmJ63B!(qw?0Bh>eVY#d`pVhvh7Ypo`7dH- zL=W@~M#zghZpgcXjbG1Ee9F4pn6g34><9OPA`@Si>jK7(M`8x8xIf!0b7JM)yYV-Z z&DAMGI-w#}miuZGi&efCUe-TS9hQgCDrYX^nOW%lM#ngxXQl(znvVwYMFtH|m&|ig zetqfnanr3)SgA!OrGW<$qpWtkT(#yV*6GyimD*V=)#R_wx+U-$+C)Ktl$tIL7mL7P z>o<-iEQJdKDVi@{q|b0o@G0A;q9K%D>BV*E-Pxkcvn}gy2`uK&E8NekYK>WwZJO|G z+Kgy=UGO97$RR}$6#Y}XBRZ)rrA*cp;i0kon14+iAL_*&3ZZw8{D40*%vteVcm>5CVgRuSiH}{oY%NvQQxDR zCr{JU|31m-!Ku9g9v{MqL{Xy|v#yBwwO-%-p1p2S&VT{J-EQ%A(?6ejQf#Pq06lX2 z`kd1|ZDDg|!jbJY>R(3Pt$2G{5j@E#n_dz2J=1ti{^a~8#n;c*s9inweU3q0bEYxQ zolB3oVX<(%T^1euE zOe~GePMXRruFZ=7wByFhwVk1FyMtg>*8r4L<2Z}6KWeaXS?t&MMXxm!5)W#$oc?~` ziA%1^u-&N7yU3!s=M~azuI1Z{)+IEAJi0k!edLR`=KXl=km`azwGJIn+VRJjPx|xK zSMlu^eV9m2PN%a54fNZLj_O986S=`Z+A-B3R;g3j9r%;gPI(P}=FT@Yi=32uhgEnT zv3pQteR8&oi$JHudQWL;?Ym0Pk%Z@X^l)+S(0iBT71C+VWiPX)RWEP~d)hUqoBF)N z^aI}l)fgh>9-9m9Jr)++8CzGk=oZwp*Rn(C^Tw!>>#N^trRnJo5a`d+EU&8bRXTgd zm{PnvkIWQmP8rtHv+%;^)qR+v(%xTfz8ulBo3_X-XrcFR3QUIjE{~s#H!bZm-7f?P66`SIOY73 zIq~;Swd{T3a>L}>$Ft$yQdE*IxOz{w!iubk5?4C zhc`2KyFIL1$z48LV{2)}WqZ>#kIbr9=dAVi+f6Gteyq!xuSd*2>9Yq`khGiy`PCmE zho$t7kBIEn=bFX&k!z>Dj~=m)23@0S)%l0VyJQfo(dgZ>8gD3hY&D?Xb#h;B@;ww|(*V=(8=Ep{EhZ+wPl z@rQ@IS0a=WGi@kkm{N6wu|IM6sJ%^eWdA2mCNx)#_PH6#DrZNMu!&f=(%9&hBrw~Q}0&%NwdNltev9jx=9l$^*H=%^Gacx9cw=(D>-@T$vOyT(v(XUhiBvn%C8p6q^{>C|U$H=i?o zKMuLEW6G)0ho|*wlON=LE4yt;y!?aKP*}Knit6m8mGVUwPJZj6Cc1ESeBHHY`W_2D zrp)v85;;5wx}%DOW&`@q9w`4dM%1hDfu2&PHQF*{!JieUyUs~$oKU;Ev01Tj|JPmP zC~x2RZsGU7Yrq^|#%7;iKsQu1NItMBydVigC*;3d;>(C?6h5G``MD z>yy%FkJjYqZXznSeJC)R~mUuq^oL)7Gf`&=B*TTqC4~8 zu#Le-{pf{TO8t-1gz{5V{T=fcLM4#A^Q`r^CxJlPt;ilAj0)dbC(i0td8M2QlS}-3zJ^hslsQGU zaRI$zd&26(l%D(5^VN4GK6c+P`ugRYWrn<3u~Jarg3@yEsC|yL54%ks@>Jo9!ODep zQ=VP>z`PXEW3BF@%Pm(nn4%()L5>pc)G)G(p6g$;-b9y2&agY*F*vTFsU~u($l)OE z)Yy^5)~b=YDMXy_yps9zns#0oB08~cr-=M`PoX zw|zEeKAmUk{M~Om>hj5_R6aab#kz_Rm!;o&7G z&5={0h5pEtUkwjLYRr$TNL_4xArl$iN(oN+hol`PC&Y`LKEHeP2XHqDY*dGv~i z6-HX=Bl~GQ`eWAYNlmb>q@Ocx>)qJL{(+Ap?p!-M{Y6UL8_}3mbpw=cBlHiLcw%P!gkJXbhd}- znpJCKvpShp!?2bXJfQjgrww&C9?wxopDR+$ePE=z%I_V=#C_dC(a~|)?^nL~WUKYP zXqvCHW##~ysLORD^Y`it3VqF;Bh7-U}F(L=SK5#Rv4ry zT)xUE#!ltK^Zbi_R|)I1znE+qHOA+EwRa`pQ50!=0?4W2A|AL8n=ul>gZo!mf#noM zG%5=#y5c56knlr*1Xw`DL_|;#`8fn#1rKoXSj1yJ7J;a%peu)fTp~seIYbDD%bot; zx2CJ9?sWHb&rBx)t>^is?yuhO>+YJm>-=WN|8McRqjxp=_s8Ml*WcK_{N36up31xE z%-cd~b;tbeqbIL?D(^TYn}HtO+A3YzWA0jwud!!Sp2{1JfrGABCUH<< zYmW+>)01xPF|X3UF|si)quPbZHS#`wEH#~@3N4>%+jraSPYe2;y|i1MX9tZ=dF1W0 zsw}(b>Wq$^tM|EmUDH`j>g+jkLC&3ny4LJc(5C0)e@wqE`;&vse*0F}7Z+qNt`N?c zSLy!dPv?EqdQ#KBubl9k)XT=LoHu`Yh42gWDs6bXe#eY{_|E3Ob4i=loerFM=;@Y| zF$|MY?XE!`?oR2`ecj+=Q?tgs+xPL_b1wZdGil=7ZVmIN^r-Rj!KDSSS8qA;Xpe(y zdd|7`i#|yc_jJ2FAGJO>WgPFmbKHCV=2uT%wsYSdq25)SO>6t%*W({uu&U|&l@k{B z+;Y`-jcfgQ&%Dr*CcUr7zoO;hjI`NxCm)@4$?rc~xvy@c$F@Gy0wYPazv>dc^oxhz zy#3{jVYBO^1dXP{%9te^;mJ^qyIZS1+uyWZc??n`dnL&v%HK*s(!A zx@IqpeEA-*taxDZ^H+T_a_pw58I{{_T{b6m;&T^0u`~1PFB`mfar4h#Ju<3O^@Wqa zuiSC#7Y$O=n?y~9Kvlch}^s=88j^A=()MqsoT+yNCgsLmvt8{JsJDV;z{_*?= z(q28(c+30EM;1MYA-3CQbxBWeSBsC}U-w@A`frMdXV!VqZ zCof-cdGoXbd!I@@oYu8O1QGr$_ItcjDZ7AB?yn|1XQ?S4qxpGv?`S!*2R%TK?)Dy++l! ze&83aZksgi@3o4i-T1;^XK$T+>5)ozR(Q4Ycb#|Eza{4@ILB@}?=Mv+J@!Ve^rCOJ z)M&qQaKofWW|SOrHHCCMpk>T`XxGkp8)tHu**LA6^LK4Qd7vaQiw8%0(68o{)&M3Q z8iEQy{Xv|f{lyr_%PsIff}}YN$1G4g5ND)MTLbxc*26xKli7%f5uiG!n-U;Xd(Z)p zFgWvH!}@&{sOfzm*gPIX;Ch7O?rf`p;$*oSNo?p4rm4}yj@6M71h zwH>~(pn9jG9)O$A@F++atL&;CzPzu3hWjEk?8lJFtv2h-yFgV>1ziAVH;^~kmG^-< zarrjDQY!*Y0W~eJdH}XRgHC{iaeeL<+0}O<%C~{p`|@WXa3&~j*;PF_eH-OXddQt6 z9=QG!>vK2BZh1dc{?X(|S}Zux`hdO#-4HE(Jn`WFSCCI1<9Sz~D(Q>i z&IeU+%NI)=c#c%c(SnlQi~PV*4fF^|In7crdP< zg{`Xm1PpTou6|zSRns9KWuInnf}=EKx04@5*8uHRvEXb{4R>uBZA|7PVb6)iIE#4+ z{u-)$Zf>N-UKwpB$S|cPyMz4a7(n2_onacz^eG{`)B)}+)38Qa%|iDo(4}s2GY)Ae z`)rV5N>g?(@=H10?4_fspzrmJA%pJN5BrZ!IzU`y&^$e@uu5C@M9D98gFETa|1`R0 zQZ&|SrhX^QFm*>TsOJXDE2N;c<&IfWVO``DJ;O zt@s3oU+hghvSUA_hBFon#hDs(j*+pL`Oj1G_q2#cFk{GWl_Ub5a)ve0p$F1`DResSY_tgI9|G%Ay=h@5WcB;PriI83D;8d&s5-GdX!Kw29 z7liCm2j#u}6Qt}?2eiv$``3@`r(6H$m)`$X-%U67={o-5CbNv=_wh>{|8g1swQ1(-V!=+nE&SbW54s?Wi|(Fm0$k`&PDi4HI8o1x45i-;M&Ak z*FSI`+%$n)TJHhnl7_OY2I1{M8)Q7~fY-m2`Wg^bHdS`%Mm*=LM}zo$?F`pH)t2ew zvB8JyU>;Ws3Hm)y%W}hxbWZEvcuajl4ayp);tEm+YB|w_K-vnRzMxH@&e77x6A%9P zfeaIr4vYfehzD;Fc>!s-iZ2KqsQiPFAzq%q$9+wPNu&;NKBd8zj(D*Kl^@U&whkKS za%$hc++EwZ{=E`?)1dGtf-iK?6LbP3jNkWQ&6K9v&d(-k$9~=|Sy=5|iNF~o4*>SH z4`Ri`=W3hZ9-!g=d8@3_&;4UFK-Ys3{y!FkE`akEkkJ+?@g87p?l;eO)^JCnhBIQ3 zw1nXTy*Z#;K~;jl7&K4d<~WK;2e`*Z+g|X9c68rXM3pdHH1am+4$zrF<0}pRkfS}Q z#9~W?NyEBg0jL+KR%u`=?J^*joemZwd;o~o4xIJ|a$W=Cz7)2Vl23bq0~QcEJZXw_ zXJK;0QBWmf=jr=~LZLPczzN(U0!N0k|HhljgpH%bg5YKB%RL>jNsc&qT$}dEC_#F4cB? zG9G;c)39&3d?r@x>Kq6AcAMthq3z$as9Zi1H|(k$bh|y1viJ`CU{_@^JrgjYDQ)~H zGE&drJN2_}L6w7UY25H5O&HYDEsdWz@Ja~wX!ubM+b|q+WS`;~D9aB{yR_s165nDx zdY8{Z@WPL^ECgK#S|{So_3?Csxm`hEWo7u0FQM4&^p7%r3u0ef7~Dgq;Vv^#hbjB$ zK0@Sq1~3l}q)#|@2mIXQsGT@+P^|go8YlUz)=X`#9VyzvXqrM0ju($^kS-AH4*11+ zIV&Igoq=28r_Fg^+!06oG(1?xaXy=pAwDqdPWU-Dt75jgS^w_-l0#gE$%jk!8;l2n zut2fLieJjJ`JF-Pc)}90=nR z`}c45GwneO0zYBxS~69fv!FgdlN;l_+Y82eZqF;X&U*CcP6pz*Ve|8D zNz{#~bjiZelm>nleq?VU`b70T$$)!fwW4*V<#lC_|LFeh+M3BtJmFt4x|X(Y!~2ey z)riv2|Dhk(X_I3s4XwI8lR7w(|(v;D?-Ku?%$>ZWgAyh(hcC4lPzLE_)L z=1mRvA;*ke%Dig)C87?nlOZVlTo1}~f+U6#Hvhjq8s~Yh;Iap+i?e8~G6aD?TAPsn z2WtINVh+~kJ(BWsK3d(w9t3__mYvOc(nGt}N;Mkir}~j$?eyQH{kBc;C{6quW_D8l zOU>(1|04R2OP`nT3vKuQqfZ&^bmbH#LC8)k z{U?qc66br`<@svtg&OYAmD#-a-;u-5yJT0kU6=%-;1`P(ijN-^YZur?m-*IA{U4)` zgna(TPXds^2LIUzn+(Dv2IET*q z#A@OnKLp1_$soZ=+^UVZGeE;Zhd_xPzXiiY$N)CNsJN|eB)A0B${HT4AG{*tD{#as z16T+XE0$REq0ofLpu;cm{>O^TPrg`X;P}C&SML&MPnIQq%IZf3?i1Ph>66y;A;p}k zAH5|yt(y#Xy4H1GuI99@GPvm)Y0KrB0#ycG$I>cSx^{d26dB6pS^+ogG7d7d1o>X8 z!H2aRH7V`Ux*d8@_=`iy!ag3tief|`=4vk3MY>%`8>a#m&@OrqtCC=2DH0bPh4N~0 z)e`I~T*U;tB3Cu>-WslQqMfeWkU;p$%9c2EToR|mt;-?hl5$G9p^rl1SM(zFB=sis zsOfq&^h~{Tofhd5l|kgkk1QQdK96*ey1`}<{J7NGn?YJ)n+=K! z8*xg*lHsloS|r1cA2x>l81w@u>Ut%lsSJ7z6gM^%AGqa#xIaU*m3huO$9i|0$DG)o z9qgn<#1Y$DpoCzv;1g`^&OtAHqmOlu4fSJcOTf?l^z#LiXy2*JccH#}cKEFE($8@u z%Uww8faMDPy{G~A!bD^9gO6!>FHO<+sACV7kLg9;V?Kxv`Gd2R8LS2QOts!}r62Jjds- z^3l2&mtEp%?E=T2e_9c#5@nf*z(-zOKjDR!QyR`I9w_`o!ZK{9^lG?~Sa+y#bW0QK z+3s9F%<)(|V=c?hZ_Q^-qsIFkSK~Kob8m*h(pr|C-$<-GtZA(AUNOpV)S6%QTg$TZ ztLf>srm@DCXMA3lO1IJ619Hb^0}w+@}TNxC!Lu7-|!K6 zeK!;R|5gH@Q~SUNMp^fi;BC`Ju+|@Ld2HHOoC`hKzVd-F(d{dn_UU@Sk?m8?FGkv@ zJ}|oJLdJoe$?zVL6=TjfeUm=g_X)?SfEx)n=k&zd2edMXr8)5W*L_K&gxDYWm{mZEHdB}OP4+l@Y7GLGg@l#EsDjpTrrD4csT7Ycpdlewke9uY{c0rqnomVqrbNU70m}e3;=9yrMxSJlP;K4#+=d~#i zb|^V7V#^a_5`ODxguU-yi9_P58Nn%WOF5)m;&cWSa#HTdCkp-Kk%gkCLh6lkOkq%4 zkZ~SBpd-)nr5Vxnxc>}_ zxoo@gt@@##&r=z<=3K;ipu0d~bB~|rZRHOhCS#44e#<%d;@f7%Xk|wm-!T;qk1Ws5 z_Hz#(*Ft%@Thgt3yFKq-?t!r0dtxVx409`=YxAS&SMrgz-Muq1&d!cjKI6S&aJu)$ zu3Q=BrF<_K-0q!{W!c%MLiuH>f3Iar-6rz=E9>u1$|UVb{d01@$W1qisDEU+v_<`k zJ&3v~)%utJ#Ww#x9E(Z#{}1O1=CD3B4Y~tloH=5}jd6uGSf+?`261vB zmi8XPvR)3n$Q#zo&zhFa3UaEBv)p<5LVBvOYR~sOUI&WvgZQ<*o9!%EmU~p&vOds67kf zJdvwTxChb7!;L@VxRzHxb74i~9W1HuWqCDF_r5C5-SNVI*WxMW{rh6i`=jEFFKd1w zBhLS#;kiE6c>1N0arBGx4RBtB470O0&bm!Iet5sk?u$P&@w^|d*($+X$TLo?xipx= z;RiqS9p1gc^iCP&fd7tPN-t?fpE{*ZV2?C;I#18g9uufcl{?&QI9)!@|*l)NgvdSO>2q$J`TMKU?a^GgQBm z8#r9+JE?1=6aOzxeB8pF%5eLCbmM0ice0Gc|G&%fSO&G}GiEw~x< zE&P{B25@)6*y917WhwKhHsWbE^A47^)2!;h;xd*-yh{&i|PH9`Q2>biu(Pyp!%Y(FaDbl4a0iucJQpGmkpZ~{v*O* zke%B)WoS@{$}wHdhs8Vzfj5+0C|oz*8ZcgUaA z(!eSlHkftpotZr(Ysd%_qDD=KDm!BUiobb8yFr8cXZ6Y$0=$hf1`SGS9N{n|XK3z_ z&e?qidc@N#MdU--xtYC&=Aca7kC2?q!9#&Fv-hnzS;MmWXWpBc>yc}7$u$gflFxO+ zQ3es;lsPQ3f4Dz?Tc>2?cFrC)@P9LNQo=*C+V$#{nF}@c$>^V(sS72TG`2w_p*J>R oZ`IgP0ur=pET0fO;+6XK=XBRVTh;=h(8Q}xcR_!Z&rSpX56 void, + private errorCallback: (error: string) => void, + private verboseLogging: boolean + ) { + this.restartCounter = 0; + + this.startWatcher(); + } + + private startWatcher(): void { + const args = [this.watchedFolder]; + if (this.verboseLogging) { + args.push('-verbose'); + } + + this.handle = cp.spawn(uri.parse(require.toUrl('vs/workbench/services/files/node/watcher/win32/CodeHelper.exe')).fsPath, args); + + const stdoutLineDecoder = new decoder.LineDecoder(); + + // Events over stdout + this.handle.stdout.on('data', (data: NodeBuffer) => { + + // Collect raw events from output + const rawEvents: IRawFileChange[] = []; + stdoutLineDecoder.write(data).forEach((line) => { + const eventParts = line.split('|'); + if (eventParts.length === 2) { + const changeType = Number(eventParts[0]); + const absolutePath = eventParts[1]; + + // File Change Event (0 Changed, 1 Created, 2 Deleted) + if (changeType >= 0 && changeType < 3) { + + // Support ignores + if (this.ignored && this.ignored.some(ignore => glob.match(ignore, absolutePath))) { + return; + } + + // Otherwise record as event + rawEvents.push({ + type: OutOfProcessWin32FolderWatcher.changeTypeMap[changeType], + path: absolutePath + }); + } + + // 3 Logging + else { + console.log('%c[File Watcher]', 'color: darkgreen', eventParts[1]); + } + } + }); + + // Trigger processing of events through the delayer to batch them up properly + if (rawEvents.length > 0) { + this.eventCallback(rawEvents); + } + }); + + // Errors + this.handle.on('error', (error: Error) => this.onError(error)); + this.handle.stderr.on('data', (data: NodeBuffer) => this.onError(data)); + + // Exit + this.handle.on('exit', (code: number, signal: string) => this.onExit(code, signal)); + } + + private onError(error: Error | NodeBuffer): void { + this.errorCallback('[FileWatcher] process error: ' + error.toString()); + } + + private onExit(code: number, signal: string): void { + if (this.handle) { // exit while not yet being disposed is unexpected! + this.errorCallback(`[FileWatcher] terminated unexpectedly (code: ${code}, signal: ${signal})`); + + if (this.restartCounter <= OutOfProcessWin32FolderWatcher.MAX_RESTARTS) { + this.errorCallback('[FileWatcher] is restarted again...'); + this.restartCounter++; + this.startWatcher(); // restart + } else { + this.errorCallback('[FileWatcher] Watcher failed to start after retrying for some time, giving up. Please report this as a bug report!'); + } + } + } + + public dispose(): void { + if (this.handle) { + this.handle.kill(); + this.handle = null; + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts b/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts new file mode 100644 index 0000000000..97cac93c3a --- /dev/null +++ b/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IRawFileChange, toFileChangesEvent } from 'vs/workbench/services/files/node/watcher/common'; +import { OutOfProcessWin32FolderWatcher } from 'vs/workbench/services/files/node/watcher/win32/csharpWatcherService'; +import { FileChangesEvent } from 'vs/platform/files/common/files'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { normalize } from 'path'; +import { rtrim, endsWith } from 'vs/base/common/strings'; +import { sep } from 'vs/base/common/paths'; + +export class FileWatcher { + private isDisposed: boolean; + + constructor( + private contextService: IWorkspaceContextService, + private ignored: string[], + private onFileChanges: (changes: FileChangesEvent) => void, + private errorLogger: (msg: string) => void, + private verboseLogging: boolean + ) { + } + + public startWatching(): () => void { + let basePath: string = normalize(this.contextService.getWorkspace().roots[0].fsPath); + + if (basePath && basePath.indexOf('\\\\') === 0 && endsWith(basePath, sep)) { + // for some weird reason, node adds a trailing slash to UNC paths + // we never ever want trailing slashes as our base path unless + // someone opens root ("/"). + // See also https://github.com/nodejs/io.js/issues/1765 + basePath = rtrim(basePath, sep); + } + + const watcher = new OutOfProcessWin32FolderWatcher( + basePath, + this.ignored, + events => this.onRawFileEvents(events), + error => this.onError(error), + this.verboseLogging + ); + + return () => { + this.isDisposed = true; + watcher.dispose(); + }; + } + + private onRawFileEvents(events: IRawFileChange[]): void { + if (this.isDisposed) { + return; + } + + // Emit through event emitter + if (events.length > 0) { + this.onFileChanges(toFileChangesEvent(events)); + } + } + + private onError(error: string): void { + if (!this.isDisposed) { + this.errorLogger(error); + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/files/test/node/fileService.test.ts b/src/vs/workbench/services/files/test/node/fileService.test.ts new file mode 100644 index 0000000000..bbcfa731fb --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fileService.test.ts @@ -0,0 +1,814 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import fs = require('fs'); +import path = require('path'); +import os = require('os'); +import assert = require('assert'); + +import { TPromise } from 'vs/base/common/winjs.base'; +import { FileService, IEncodingOverride } from 'vs/workbench/services/files/node/fileService'; +import { FileOperation, FileOperationEvent, FileChangesEvent, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files'; +import uri from 'vs/base/common/uri'; +import uuid = require('vs/base/common/uuid'); +import extfs = require('vs/base/node/extfs'); +import encodingLib = require('vs/base/node/encoding'); +import utils = require('vs/workbench/services/files/test/node/utils'); +import { onError } from 'vs/base/test/common/utils'; +import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { Workspace } from 'vs/platform/workspace/common/workspace'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; + +suite('FileService', () => { + let service: FileService; + let parentDir = path.join(os.tmpdir(), 'vsctests', 'service'); + let testDir: string; + + setup(function (done) { + let id = uuid.generateUuid(); + testDir = path.join(parentDir, id); + let sourceDir = require.toUrl('./fixtures/service'); + + extfs.copy(sourceDir, testDir, (error) => { + if (error) { + return onError(error, done); + } + + service = new FileService(new TestContextService(new Workspace(testDir, testDir, [uri.file(testDir)])), new TestConfigurationService(), { disableWatcher: true }); + done(); + }); + }); + + teardown((done) => { + service.dispose(); + extfs.del(parentDir, os.tmpdir(), () => { }, done); + }); + + test('createFile', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const contents = 'Hello World'; + const resource = uri.file(path.join(testDir, 'test.txt')); + service.createFile(resource, contents).done(s => { + assert.equal(s.name, 'test.txt'); + assert.equal(fs.existsSync(s.resource.fsPath), true); + assert.equal(fs.readFileSync(s.resource.fsPath), contents); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.CREATE); + assert.equal(event.target.resource.fsPath, resource.fsPath); + toDispose.dispose(); + + done(); + }, error => onError(error, done)); + }); + + test('createFolder', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + service.resolveFile(uri.file(testDir)).done(parent => { + const resource = uri.file(path.join(parent.resource.fsPath, 'newFolder')); + + return service.createFolder(resource).then(f => { + assert.equal(f.name, 'newFolder'); + assert.equal(fs.existsSync(f.resource.fsPath), true); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.CREATE); + assert.equal(event.target.resource.fsPath, resource.fsPath); + assert.equal(event.target.isDirectory, true); + toDispose.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('touchFile', function (done: () => void) { + service.touchFile(uri.file(path.join(testDir, 'test.txt'))).done(s => { + assert.equal(s.name, 'test.txt'); + assert.equal(fs.existsSync(s.resource.fsPath), true); + assert.equal(fs.readFileSync(s.resource.fsPath).length, 0); + + const stat = fs.statSync(s.resource.fsPath); + + return TPromise.timeout(10).then(() => { + return service.touchFile(s.resource).done(s => { + const statNow = fs.statSync(s.resource.fsPath); + assert.ok(statNow.mtime.getTime() >= stat.mtime.getTime()); // one some OS the resolution seems to be 1s, so we use >= here + assert.equal(statNow.size, stat.size); + + done(); + }); + }); + }, error => onError(error, done)); + }); + + test('renameFile', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = uri.file(path.join(testDir, 'index.html')); + service.resolveFile(resource).done(source => { + return service.rename(source.resource, 'other.html').then(renamed => { + assert.equal(fs.existsSync(renamed.resource.fsPath), true); + assert.equal(fs.existsSync(source.resource.fsPath), false); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.MOVE); + assert.equal(event.target.resource.fsPath, renamed.resource.fsPath); + toDispose.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('renameFolder', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = uri.file(path.join(testDir, 'deep')); + service.resolveFile(resource).done(source => { + return service.rename(source.resource, 'deeper').then(renamed => { + assert.equal(fs.existsSync(renamed.resource.fsPath), true); + assert.equal(fs.existsSync(source.resource.fsPath), false); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.MOVE); + assert.equal(event.target.resource.fsPath, renamed.resource.fsPath); + toDispose.dispose(); + + done(); + }); + }); + }); + + test('renameFile - MIX CASE', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = uri.file(path.join(testDir, 'index.html')); + service.resolveFile(resource).done(source => { + return service.rename(source.resource, 'INDEX.html').then(renamed => { + assert.equal(fs.existsSync(renamed.resource.fsPath), true); + assert.equal(path.basename(renamed.resource.fsPath), 'INDEX.html'); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.MOVE); + assert.equal(event.target.resource.fsPath, renamed.resource.fsPath); + toDispose.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('moveFile', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = uri.file(path.join(testDir, 'index.html')); + service.resolveFile(resource).done(source => { + return service.moveFile(source.resource, uri.file(path.join(testDir, 'other.html'))).then(renamed => { + assert.equal(fs.existsSync(renamed.resource.fsPath), true); + assert.equal(fs.existsSync(source.resource.fsPath), false); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.MOVE); + assert.equal(event.target.resource.fsPath, renamed.resource.fsPath); + toDispose.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('move - FILE_MOVE_CONFLICT', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + service.resolveFile(uri.file(path.join(testDir, 'index.html'))).done(source => { + return service.moveFile(source.resource, uri.file(path.join(testDir, 'binary.txt'))).then(null, (e: FileOperationError) => { + assert.equal(e.fileOperationResult, FileOperationResult.FILE_MOVE_CONFLICT); + + assert.ok(!event); + toDispose.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('moveFile - MIX CASE', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = uri.file(path.join(testDir, 'index.html')); + service.resolveFile(resource).done(source => { + return service.moveFile(source.resource, uri.file(path.join(testDir, 'INDEX.html'))).then(renamed => { + assert.equal(fs.existsSync(renamed.resource.fsPath), true); + assert.equal(path.basename(renamed.resource.fsPath), 'INDEX.html'); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.MOVE); + assert.equal(event.target.resource.fsPath, renamed.resource.fsPath); + toDispose.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('moveFile - overwrite folder with file', function (done: () => void) { + let createEvent: FileOperationEvent; + let moveEvent: FileOperationEvent; + let deleteEvent: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + if (e.operation === FileOperation.CREATE) { + createEvent = e; + } else if (e.operation === FileOperation.DELETE) { + deleteEvent = e; + } else if (e.operation === FileOperation.MOVE) { + moveEvent = e; + } + }); + + service.resolveFile(uri.file(testDir)).done(parent => { + const folderResource = uri.file(path.join(parent.resource.fsPath, 'conway.js')); + return service.createFolder(folderResource).then(f => { + const resource = uri.file(path.join(testDir, 'deep', 'conway.js')); + return service.moveFile(resource, f.resource, true).then(moved => { + assert.equal(fs.existsSync(moved.resource.fsPath), true); + assert.ok(fs.statSync(moved.resource.fsPath).isFile); + + assert.ok(createEvent); + assert.ok(deleteEvent); + assert.ok(moveEvent); + + assert.equal(moveEvent.resource.fsPath, resource.fsPath); + assert.equal(moveEvent.target.resource.fsPath, moved.resource.fsPath); + + assert.equal(deleteEvent.resource.fsPath, folderResource.fsPath); + + toDispose.dispose(); + + done(); + }); + }); + }, error => onError(error, done)); + }); + + test('copyFile', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + service.resolveFile(uri.file(path.join(testDir, 'index.html'))).done(source => { + const resource = uri.file(path.join(testDir, 'other.html')); + return service.copyFile(source.resource, resource).then(copied => { + assert.equal(fs.existsSync(copied.resource.fsPath), true); + assert.equal(fs.existsSync(source.resource.fsPath), true); + + assert.ok(event); + assert.equal(event.resource.fsPath, source.resource.fsPath); + assert.equal(event.operation, FileOperation.COPY); + assert.equal(event.target.resource.fsPath, copied.resource.fsPath); + toDispose.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('copyFile - overwrite folder with file', function (done: () => void) { + let createEvent: FileOperationEvent; + let copyEvent: FileOperationEvent; + let deleteEvent: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + if (e.operation === FileOperation.CREATE) { + createEvent = e; + } else if (e.operation === FileOperation.DELETE) { + deleteEvent = e; + } else if (e.operation === FileOperation.COPY) { + copyEvent = e; + } + }); + + service.resolveFile(uri.file(testDir)).done(parent => { + const folderResource = uri.file(path.join(parent.resource.fsPath, 'conway.js')); + return service.createFolder(folderResource).then(f => { + const resource = uri.file(path.join(testDir, 'deep', 'conway.js')); + return service.copyFile(resource, f.resource, true).then(copied => { + assert.equal(fs.existsSync(copied.resource.fsPath), true); + assert.ok(fs.statSync(copied.resource.fsPath).isFile); + + assert.ok(createEvent); + assert.ok(deleteEvent); + assert.ok(copyEvent); + + assert.equal(copyEvent.resource.fsPath, resource.fsPath); + assert.equal(copyEvent.target.resource.fsPath, copied.resource.fsPath); + + assert.equal(deleteEvent.resource.fsPath, folderResource.fsPath); + + toDispose.dispose(); + + done(); + }); + }); + }, error => onError(error, done)); + }); + + test('importFile', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + service.resolveFile(uri.file(path.join(testDir, 'deep'))).done(target => { + const resource = uri.file(require.toUrl('./fixtures/service/index.html')); + return service.importFile(resource, target.resource).then(res => { + assert.equal(res.isNew, true); + assert.equal(fs.existsSync(res.stat.resource.fsPath), true); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.IMPORT); + assert.equal(event.target.resource.fsPath, res.stat.resource.fsPath); + toDispose.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('importFile - MIX CASE', function (done: () => void) { + service.resolveFile(uri.file(path.join(testDir, 'index.html'))).done(source => { + return service.rename(source.resource, 'CONWAY.js').then(renamed => { // index.html => CONWAY.js + assert.equal(fs.existsSync(renamed.resource.fsPath), true); + assert.ok(fs.readdirSync(testDir).some(f => f === 'CONWAY.js')); + + return service.resolveFile(uri.file(path.join(testDir, 'deep', 'conway.js'))).done(source => { + return service.importFile(source.resource, uri.file(testDir)).then(res => { // CONWAY.js => conway.js + assert.equal(fs.existsSync(res.stat.resource.fsPath), true); + assert.ok(fs.readdirSync(testDir).some(f => f === 'conway.js')); + + done(); + }); + }); + }); + }, error => onError(error, done)); + }); + + test('importFile - overwrite folder with file', function (done: () => void) { + let createEvent: FileOperationEvent; + let importEvent: FileOperationEvent; + let deleteEvent: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + if (e.operation === FileOperation.CREATE) { + createEvent = e; + } else if (e.operation === FileOperation.DELETE) { + deleteEvent = e; + } else if (e.operation === FileOperation.IMPORT) { + importEvent = e; + } + }); + + service.resolveFile(uri.file(testDir)).done(parent => { + const folderResource = uri.file(path.join(parent.resource.fsPath, 'conway.js')); + return service.createFolder(folderResource).then(f => { + const resource = uri.file(path.join(testDir, 'deep', 'conway.js')); + return service.importFile(resource, uri.file(testDir)).then(res => { + assert.equal(fs.existsSync(res.stat.resource.fsPath), true); + assert.ok(fs.readdirSync(testDir).some(f => f === 'conway.js')); + assert.ok(fs.statSync(res.stat.resource.fsPath).isFile); + + assert.ok(createEvent); + assert.ok(deleteEvent); + assert.ok(importEvent); + + assert.equal(importEvent.resource.fsPath, resource.fsPath); + assert.equal(importEvent.target.resource.fsPath, res.stat.resource.fsPath); + + assert.equal(deleteEvent.resource.fsPath, folderResource.fsPath); + + toDispose.dispose(); + + done(); + }); + }); + }, error => onError(error, done)); + }); + + test('importFile - same file', function (done: () => void) { + service.resolveFile(uri.file(path.join(testDir, 'index.html'))).done(source => { + return service.importFile(source.resource, uri.file(path.dirname(source.resource.fsPath))).then(imported => { + assert.equal(imported.stat.size, source.size); + + done(); + }); + }, error => onError(error, done)); + }); + + test('deleteFile', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = uri.file(path.join(testDir, 'deep', 'conway.js')); + service.resolveFile(resource).done(source => { + return service.del(source.resource).then(() => { + assert.equal(fs.existsSync(source.resource.fsPath), false); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.DELETE); + toDispose.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('deleteFolder', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = uri.file(path.join(testDir, 'deep')); + service.resolveFile(resource).done(source => { + return service.del(source.resource).then(() => { + assert.equal(fs.existsSync(source.resource.fsPath), false); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.DELETE); + toDispose.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('resolveFile', function (done: () => void) { + service.resolveFile(uri.file(testDir), { resolveTo: [uri.file(path.join(testDir, 'deep'))] }).done(r => { + assert.equal(r.children.length, 6); + + let deep = utils.getByName(r, 'deep'); + assert.equal(deep.children.length, 4); + + done(); + }, error => onError(error, done)); + }); + + test('resolveFiles', function (done: () => void) { + service.resolveFiles([ + { resource: uri.file(testDir), options: { resolveTo: [uri.file(path.join(testDir, 'deep'))] } }, + { resource: uri.file(path.join(testDir, 'deep')) } + ]).then(res => { + const r1 = res[0].stat; + + assert.equal(r1.children.length, 6); + + let deep = utils.getByName(r1, 'deep'); + assert.equal(deep.children.length, 4); + + const r2 = res[1].stat; + assert.equal(r2.children.length, 4); + assert.equal(r2.name, 'deep'); + + done(); + }, error => onError(error, done)); + }); + + test('existsFile', function (done: () => void) { + service.existsFile(uri.file(testDir)).then((exists) => { + assert.equal(exists, true); + + service.existsFile(uri.file(testDir + 'something')).then((exists) => { + assert.equal(exists, false); + + done(); + }); + }, error => onError(error, done)); + }); + + test('updateContent', function (done: () => void) { + let resource = uri.file(path.join(testDir, 'small.txt')); + + service.resolveContent(resource).done(c => { + assert.equal(c.value, 'Small File'); + + c.value = 'Updates to the small file'; + + return service.updateContent(c.resource, c.value).then(c => { + assert.equal(fs.readFileSync(resource.fsPath), 'Updates to the small file'); + + done(); + }); + }, error => onError(error, done)); + }); + + test('updateContent - use encoding (UTF 16 BE)', function (done: () => void) { + let resource = uri.file(path.join(testDir, 'small.txt')); + let encoding = 'utf16be'; + + service.resolveContent(resource).done(c => { + c.encoding = encoding; + + return service.updateContent(c.resource, c.value, { encoding: encoding }).then(c => { + return encodingLib.detectEncodingByBOM(c.resource.fsPath).then((enc) => { + assert.equal(enc, encodingLib.UTF16be); + + return service.resolveContent(resource).then(c => { + assert.equal(c.encoding, encoding); + + done(); + }); + }); + }); + }, error => onError(error, done)); + }); + + test('updateContent - encoding preserved (UTF 16 LE)', function (done: () => void) { + let encoding = 'utf16le'; + let resource = uri.file(path.join(testDir, 'some_utf16le.css')); + + service.resolveContent(resource).done(c => { + assert.equal(c.encoding, encoding); + + c.value = 'Some updates'; + + return service.updateContent(c.resource, c.value, { encoding: encoding }).then(c => { + return encodingLib.detectEncodingByBOM(c.resource.fsPath).then((enc) => { + assert.equal(enc, encodingLib.UTF16le); + + return service.resolveContent(resource).then(c => { + assert.equal(c.encoding, encoding); + + done(); + }); + }); + }); + }, error => onError(error, done)); + }); + + test('resolveContent - FILE_IS_BINARY', function (done: () => void) { + let resource = uri.file(path.join(testDir, 'binary.txt')); + + service.resolveContent(resource, { acceptTextOnly: true }).done(null, (e: FileOperationError) => { + assert.equal(e.fileOperationResult, FileOperationResult.FILE_IS_BINARY); + + return service.resolveContent(uri.file(path.join(testDir, 'small.txt')), { acceptTextOnly: true }).then(r => { + assert.equal(r.name, 'small.txt'); + + done(); + }); + }, error => onError(error, done)); + }); + + test('resolveContent - FILE_IS_DIRECTORY', function (done: () => void) { + let resource = uri.file(path.join(testDir, 'deep')); + + service.resolveContent(resource).done(null, (e: FileOperationError) => { + assert.equal(e.fileOperationResult, FileOperationResult.FILE_IS_DIRECTORY); + + done(); + }, error => onError(error, done)); + }); + + test('resolveContent - FILE_NOT_FOUND', function (done: () => void) { + let resource = uri.file(path.join(testDir, '404.html')); + + service.resolveContent(resource).done(null, (e: FileOperationError) => { + assert.equal(e.fileOperationResult, FileOperationResult.FILE_NOT_FOUND); + + done(); + }, error => onError(error, done)); + }); + + test('resolveContent - FILE_NOT_MODIFIED_SINCE', function (done: () => void) { + let resource = uri.file(path.join(testDir, 'index.html')); + + service.resolveContent(resource).done(c => { + return service.resolveContent(resource, { etag: c.etag }).then(null, (e: FileOperationError) => { + assert.equal(e.fileOperationResult, FileOperationResult.FILE_NOT_MODIFIED_SINCE); + + done(); + }); + }, error => onError(error, done)); + }); + + test('resolveContent - FILE_MODIFIED_SINCE', function (done: () => void) { + let resource = uri.file(path.join(testDir, 'index.html')); + + service.resolveContent(resource).done(c => { + fs.writeFileSync(resource.fsPath, 'Updates Incoming!'); + + return service.updateContent(resource, c.value, { etag: c.etag, mtime: c.mtime - 1000 }).then(null, (e: FileOperationError) => { + assert.equal(e.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE); + + done(); + }); + }, error => onError(error, done)); + }); + + test('resolveContent - encoding picked up', function (done: () => void) { + let resource = uri.file(path.join(testDir, 'index.html')); + let encoding = 'windows1252'; + + service.resolveContent(resource, { encoding: encoding }).done(c => { + assert.equal(c.encoding, encoding); + + done(); + }, error => onError(error, done)); + }); + + test('resolveContent - user overrides BOM', function (done: () => void) { + let resource = uri.file(path.join(testDir, 'some_utf16le.css')); + + service.resolveContent(resource, { encoding: 'windows1252' }).done(c => { + assert.equal(c.encoding, 'windows1252'); + + done(); + }, error => onError(error, done)); + }); + + test('resolveContent - BOM removed', function (done: () => void) { + let resource = uri.file(path.join(testDir, 'some_utf8_bom.txt')); + + service.resolveContent(resource).done(c => { + assert.equal(encodingLib.detectEncodingByBOMFromBuffer(new Buffer(c.value), 512), null); + + done(); + }, error => onError(error, done)); + }); + + test('resolveContent - invalid encoding', function (done: () => void) { + let resource = uri.file(path.join(testDir, 'index.html')); + + service.resolveContent(resource, { encoding: 'superduper' }).done(c => { + assert.equal(c.encoding, 'utf8'); + + done(); + }, error => onError(error, done)); + }); + + test('watchFileChanges', function (done: () => void) { + let toWatch = uri.file(path.join(testDir, 'index.html')); + + service.watchFileChanges(toWatch); + + service.onFileChanges((e: FileChangesEvent) => { + assert.ok(e); + + service.unwatchFileChanges(toWatch); + done(); + }); + + setTimeout(() => { + fs.writeFileSync(toWatch.fsPath, 'Changes'); + }, 100); + }); + + test('watchFileChanges - support atomic save', function (done: () => void) { + let toWatch = uri.file(path.join(testDir, 'index.html')); + + service.watchFileChanges(toWatch); + + service.onFileChanges((e: FileChangesEvent) => { + assert.ok(e); + + service.unwatchFileChanges(toWatch); + done(); + }); + + setTimeout(() => { + // Simulate atomic save by deleting the file, creating it under different name + // and then replacing the previously deleted file with those contents + const renamed = `${toWatch.fsPath}.bak`; + fs.unlinkSync(toWatch.fsPath); + fs.writeFileSync(renamed, 'Changes'); + fs.renameSync(renamed, toWatch.fsPath); + }, 100); + }); + + test('options - encoding', function (done: () => void) { + + // setup + let _id = uuid.generateUuid(); + let _testDir = path.join(parentDir, _id); + let _sourceDir = require.toUrl('./fixtures/service'); + + extfs.copy(_sourceDir, _testDir, () => { + let encodingOverride: IEncodingOverride[] = []; + encodingOverride.push({ + resource: uri.file(path.join(testDir, 'deep')), + encoding: 'utf16le' + }); + + let configurationService = new TestConfigurationService(); + configurationService.setUserConfiguration('files', { encoding: 'windows1252' }); + + let _service = new FileService(new TestContextService(new Workspace(_testDir, _testDir, [uri.file(_testDir)])), configurationService, { + encodingOverride, + disableWatcher: true + }); + + _service.resolveContent(uri.file(path.join(testDir, 'index.html'))).done(c => { + assert.equal(c.encoding, 'windows1252'); + + return _service.resolveContent(uri.file(path.join(testDir, 'deep', 'conway.js'))).done(c => { + assert.equal(c.encoding, 'utf16le'); + + // teardown + _service.dispose(); + done(); + }); + }); + }); + }); + + test('UTF 8 BOMs', function (done: () => void) { + + // setup + let _id = uuid.generateUuid(); + let _testDir = path.join(parentDir, _id); + let _sourceDir = require.toUrl('./fixtures/service'); + let resource = uri.file(path.join(testDir, 'index.html')); + + let _service = new FileService(new TestContextService(new Workspace(_testDir, _testDir, [uri.file(_testDir)])), new TestConfigurationService(), { + disableWatcher: true + }); + + extfs.copy(_sourceDir, _testDir, () => { + fs.readFile(resource.fsPath, (error, data) => { + assert.equal(encodingLib.detectEncodingByBOMFromBuffer(data, 512), null); + + // Update content: UTF_8 => UTF_8_BOM + _service.updateContent(resource, 'Hello Bom', { encoding: encodingLib.UTF8_with_bom }).done(() => { + fs.readFile(resource.fsPath, (error, data) => { + assert.equal(encodingLib.detectEncodingByBOMFromBuffer(data, 512), encodingLib.UTF8); + + // Update content: PRESERVE BOM when using UTF-8 + _service.updateContent(resource, 'Please stay Bom', { encoding: encodingLib.UTF8 }).done(() => { + fs.readFile(resource.fsPath, (error, data) => { + assert.equal(encodingLib.detectEncodingByBOMFromBuffer(data, 512), encodingLib.UTF8); + + // Update content: REMOVE BOM + _service.updateContent(resource, 'Go away Bom', { encoding: encodingLib.UTF8, overwriteEncoding: true }).done(() => { + fs.readFile(resource.fsPath, (error, data) => { + assert.equal(encodingLib.detectEncodingByBOMFromBuffer(data, 512), null); + + // Update content: BOM comes not back + _service.updateContent(resource, 'Do not come back Bom', { encoding: encodingLib.UTF8 }).done(() => { + fs.readFile(resource.fsPath, (error, data) => { + assert.equal(encodingLib.detectEncodingByBOMFromBuffer(data, 512), null); + + _service.dispose(); + done(); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); +}); diff --git a/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/company.js b/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/company.js new file mode 100644 index 0000000000..ca4a62bf24 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/company.js @@ -0,0 +1,23 @@ +'use strict'; +/// +var Workforce; +(function (Workforce_1) { + var Company = (function () { + function Company() { + } + return Company; + })(); + (function (property, Workforce, IEmployee) { + if (property === void 0) { property = employees; } + if (IEmployee === void 0) { IEmployee = []; } + property; + calculateMonthlyExpenses(); + { + var result = 0; + for (var i = 0; i < employees.length; i++) { + result += employees[i].calculatePay(); + } + return result; + } + }); +})(Workforce || (Workforce = {})); diff --git a/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/conway.js b/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/conway.js new file mode 100644 index 0000000000..e99829c832 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/conway.js @@ -0,0 +1,117 @@ +'use strict'; +var Conway; +(function (Conway) { + var Cell = (function () { + function Cell() { + } + return Cell; + })(); + (function (property, number, property, number, property, boolean) { + if (property === void 0) { property = row; } + if (property === void 0) { property = col; } + if (property === void 0) { property = live; } + }); + var GameOfLife = (function () { + function GameOfLife() { + } + return GameOfLife; + })(); + (function () { + property; + gridSize = 50; + property; + canvasSize = 600; + property; + lineColor = '#cdcdcd'; + property; + liveColor = '#666'; + property; + deadColor = '#eee'; + property; + initialLifeProbability = 0.5; + property; + animationRate = 60; + property; + cellSize = 0; + property; + context: ICanvasRenderingContext2D; + property; + world = createWorld(); + circleOfLife(); + function createWorld() { + return travelWorld(function (cell) { + cell.live = Math.random() < initialLifeProbability; + return cell; + }); + } + function circleOfLife() { + world = travelWorld(function (cell) { + cell = world[cell.row][cell.col]; + draw(cell); + return resolveNextGeneration(cell); + }); + setTimeout(function () { circleOfLife(); }, animationRate); + } + function resolveNextGeneration(cell) { + var count = countNeighbors(cell); + var newCell = new Cell(cell.row, cell.col, cell.live); + if (count < 2 || count > 3) + newCell.live = false; + else if (count == 3) + newCell.live = true; + return newCell; + } + function countNeighbors(cell) { + var neighbors = 0; + for (var row = -1; row <= 1; row++) { + for (var col = -1; col <= 1; col++) { + if (row == 0 && col == 0) + continue; + if (isAlive(cell.row + row, cell.col + col)) { + neighbors++; + } + } + } + return neighbors; + } + function isAlive(row, col) { + // todo - need to guard with worl[row] exists? + if (row < 0 || col < 0 || row >= gridSize || col >= gridSize) + return false; + return world[row][col].live; + } + function travelWorld(callback) { + var result = []; + for (var row = 0; row < gridSize; row++) { + var rowData = []; + for (var col = 0; col < gridSize; col++) { + rowData.push(callback(new Cell(row, col, false))); + } + result.push(rowData); + } + return result; + } + function draw(cell) { + if (context == null) + context = createDrawingContext(); + if (cellSize == 0) + cellSize = canvasSize / gridSize; + context.strokeStyle = lineColor; + context.strokeRect(cell.row * cellSize, cell.col * cellSize, cellSize, cellSize); + context.fillStyle = cell.live ? liveColor : deadColor; + context.fillRect(cell.row * cellSize, cell.col * cellSize, cellSize, cellSize); + } + function createDrawingContext() { + var canvas = document.getElementById('conway-canvas'); + if (canvas == null) { + canvas = document.createElement('canvas'); + canvas.id = "conway-canvas"; + canvas.width = canvasSize; + canvas.height = canvasSize; + document.body.appendChild(canvas); + } + return canvas.getContext('2d'); + } + }); +})(Conway || (Conway = {})); +var game = new Conway.GameOfLife(); diff --git a/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/employee.js b/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/employee.js new file mode 100644 index 0000000000..69c58aa5c8 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/employee.js @@ -0,0 +1,38 @@ +'use strict'; +var Workforce; +(function (Workforce) { + var Employee = (function () { + function Employee() { + } + return Employee; + })(); + (property); + name: string, property; + basepay: number; + implements; + IEmployee; + { + name; + basepay; + } + var SalesEmployee = (function () { + function SalesEmployee() { + } + return SalesEmployee; + })(); + (); + Employee(name, basepay); + { + function calculatePay() { + var multiplier = (document.getElementById("mult")), as = any, value; + return _super.calculatePay.call(this) * multiplier + bonus; + } + } + var employee = new Employee('Bob', 1000); + var salesEmployee = new SalesEmployee('Jim', 800, 400); + salesEmployee.calclatePay(); // error: No member 'calclatePay' on SalesEmployee +})(Workforce || (Workforce = {})); +extern; +var $; +var s = Workforce.salesEmployee.calculatePay(); +$('#results').text(s); diff --git a/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/small.js b/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/small.js new file mode 100644 index 0000000000..5e57b4c943 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/resolver/examples/small.js @@ -0,0 +1,24 @@ +'use strict'; +var M; +(function (M) { + var C = (function () { + function C() { + } + return C; + })(); + (function (x, property, number) { + if (property === void 0) { property = w; } + var local = 1; + // unresolved symbol because x is local + //self.x++; + self.w--; // ok because w is a property + property; + f = function (y) { + return y + x + local + w + self.w; + }; + function sum(z) { + return z + f(z) + w + self.w; + } + }); +})(M || (M = {})); +var c = new M.C(12, 5); diff --git a/src/vs/workbench/services/files/test/node/fixtures/resolver/index.html b/src/vs/workbench/services/files/test/node/fixtures/resolver/index.html new file mode 100644 index 0000000000..bccd24d927 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/resolver/index.html @@ -0,0 +1,121 @@ + + + + + Strada + + + + + + + + +

TypeScript

+
+ + +
+ + +
+ +
Press 'run' to execute code...
+
...write your results into #results...
+
+ + + diff --git a/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/company.js b/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/company.js new file mode 100644 index 0000000000..ca4a62bf24 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/company.js @@ -0,0 +1,23 @@ +'use strict'; +/// +var Workforce; +(function (Workforce_1) { + var Company = (function () { + function Company() { + } + return Company; + })(); + (function (property, Workforce, IEmployee) { + if (property === void 0) { property = employees; } + if (IEmployee === void 0) { IEmployee = []; } + property; + calculateMonthlyExpenses(); + { + var result = 0; + for (var i = 0; i < employees.length; i++) { + result += employees[i].calculatePay(); + } + return result; + } + }); +})(Workforce || (Workforce = {})); diff --git a/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/conway.js b/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/conway.js new file mode 100644 index 0000000000..e99829c832 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/conway.js @@ -0,0 +1,117 @@ +'use strict'; +var Conway; +(function (Conway) { + var Cell = (function () { + function Cell() { + } + return Cell; + })(); + (function (property, number, property, number, property, boolean) { + if (property === void 0) { property = row; } + if (property === void 0) { property = col; } + if (property === void 0) { property = live; } + }); + var GameOfLife = (function () { + function GameOfLife() { + } + return GameOfLife; + })(); + (function () { + property; + gridSize = 50; + property; + canvasSize = 600; + property; + lineColor = '#cdcdcd'; + property; + liveColor = '#666'; + property; + deadColor = '#eee'; + property; + initialLifeProbability = 0.5; + property; + animationRate = 60; + property; + cellSize = 0; + property; + context: ICanvasRenderingContext2D; + property; + world = createWorld(); + circleOfLife(); + function createWorld() { + return travelWorld(function (cell) { + cell.live = Math.random() < initialLifeProbability; + return cell; + }); + } + function circleOfLife() { + world = travelWorld(function (cell) { + cell = world[cell.row][cell.col]; + draw(cell); + return resolveNextGeneration(cell); + }); + setTimeout(function () { circleOfLife(); }, animationRate); + } + function resolveNextGeneration(cell) { + var count = countNeighbors(cell); + var newCell = new Cell(cell.row, cell.col, cell.live); + if (count < 2 || count > 3) + newCell.live = false; + else if (count == 3) + newCell.live = true; + return newCell; + } + function countNeighbors(cell) { + var neighbors = 0; + for (var row = -1; row <= 1; row++) { + for (var col = -1; col <= 1; col++) { + if (row == 0 && col == 0) + continue; + if (isAlive(cell.row + row, cell.col + col)) { + neighbors++; + } + } + } + return neighbors; + } + function isAlive(row, col) { + // todo - need to guard with worl[row] exists? + if (row < 0 || col < 0 || row >= gridSize || col >= gridSize) + return false; + return world[row][col].live; + } + function travelWorld(callback) { + var result = []; + for (var row = 0; row < gridSize; row++) { + var rowData = []; + for (var col = 0; col < gridSize; col++) { + rowData.push(callback(new Cell(row, col, false))); + } + result.push(rowData); + } + return result; + } + function draw(cell) { + if (context == null) + context = createDrawingContext(); + if (cellSize == 0) + cellSize = canvasSize / gridSize; + context.strokeStyle = lineColor; + context.strokeRect(cell.row * cellSize, cell.col * cellSize, cellSize, cellSize); + context.fillStyle = cell.live ? liveColor : deadColor; + context.fillRect(cell.row * cellSize, cell.col * cellSize, cellSize, cellSize); + } + function createDrawingContext() { + var canvas = document.getElementById('conway-canvas'); + if (canvas == null) { + canvas = document.createElement('canvas'); + canvas.id = "conway-canvas"; + canvas.width = canvasSize; + canvas.height = canvasSize; + document.body.appendChild(canvas); + } + return canvas.getContext('2d'); + } + }); +})(Conway || (Conway = {})); +var game = new Conway.GameOfLife(); diff --git a/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/employee.js b/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/employee.js new file mode 100644 index 0000000000..69c58aa5c8 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/employee.js @@ -0,0 +1,38 @@ +'use strict'; +var Workforce; +(function (Workforce) { + var Employee = (function () { + function Employee() { + } + return Employee; + })(); + (property); + name: string, property; + basepay: number; + implements; + IEmployee; + { + name; + basepay; + } + var SalesEmployee = (function () { + function SalesEmployee() { + } + return SalesEmployee; + })(); + (); + Employee(name, basepay); + { + function calculatePay() { + var multiplier = (document.getElementById("mult")), as = any, value; + return _super.calculatePay.call(this) * multiplier + bonus; + } + } + var employee = new Employee('Bob', 1000); + var salesEmployee = new SalesEmployee('Jim', 800, 400); + salesEmployee.calclatePay(); // error: No member 'calclatePay' on SalesEmployee +})(Workforce || (Workforce = {})); +extern; +var $; +var s = Workforce.salesEmployee.calculatePay(); +$('#results').text(s); diff --git a/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/small.js b/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/small.js new file mode 100644 index 0000000000..5e57b4c943 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/resolver/other/deep/small.js @@ -0,0 +1,24 @@ +'use strict'; +var M; +(function (M) { + var C = (function () { + function C() { + } + return C; + })(); + (function (x, property, number) { + if (property === void 0) { property = w; } + var local = 1; + // unresolved symbol because x is local + //self.x++; + self.w--; // ok because w is a property + property; + f = function (y) { + return y + x + local + w + self.w; + }; + function sum(z) { + return z + f(z) + w + self.w; + } + }); +})(M || (M = {})); +var c = new M.C(12, 5); diff --git a/src/vs/workbench/services/files/test/node/fixtures/resolver/site.css b/src/vs/workbench/services/files/test/node/fixtures/resolver/site.css new file mode 100644 index 0000000000..c5cea74684 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/resolver/site.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------- +The base color for this template is #5c87b2. If you'd like +to use a different color start by replacing all instances of +#5c87b2 with your new color. +----------------------------------------------------------*/ +body +{ + background-color: #5c87b2; + font-size: .75em; + font-family: Segoe UI, Verdana, Helvetica, Sans-Serif; + margin: 8px; + padding: 0; + color: #696969; +} + +h1, h2, h3, h4, h5, h6 +{ + color: #000; + font-size: 40px; + margin: 0px; +} + +textarea +{ + font-family: Consolas +} + +#results +{ + margin-top: 2em; + margin-left: 2em; + color: black; + font-size: medium; +} + diff --git a/src/vs/workbench/services/files/test/node/fixtures/service/binary.txt b/src/vs/workbench/services/files/test/node/fixtures/service/binary.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc30693d792253bf83b60e6f9bac20311570a186 GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^oNn{1`ISV`@iy0XB4ude`@%$AjKtcBs*NBqf{L-T2REFS;{F0JNg)$>O13e=> zBSSL<4QEY-kc|A?#9{@f#M0cvygUV6g^ZGt0xNy}Vz6qxl+?0f-TXYgywnn%7SXFf zBSSo0978H@y*=+J$iTqEq;UDB{Up_Iw;Y)-?SJ%)JW!Uhl4UK2&W{}$K=T +var Workforce; +(function (Workforce_1) { + var Company = (function () { + function Company() { + } + return Company; + })(); + (function (property, Workforce, IEmployee) { + if (property === void 0) { property = employees; } + if (IEmployee === void 0) { IEmployee = []; } + property; + calculateMonthlyExpenses(); + { + var result = 0; + for (var i = 0; i < employees.length; i++) { + result += employees[i].calculatePay(); + } + return result; + } + }); +})(Workforce || (Workforce = {})); diff --git a/src/vs/workbench/services/files/test/node/fixtures/service/deep/conway.js b/src/vs/workbench/services/files/test/node/fixtures/service/deep/conway.js new file mode 100644 index 0000000000..e99829c832 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/service/deep/conway.js @@ -0,0 +1,117 @@ +'use strict'; +var Conway; +(function (Conway) { + var Cell = (function () { + function Cell() { + } + return Cell; + })(); + (function (property, number, property, number, property, boolean) { + if (property === void 0) { property = row; } + if (property === void 0) { property = col; } + if (property === void 0) { property = live; } + }); + var GameOfLife = (function () { + function GameOfLife() { + } + return GameOfLife; + })(); + (function () { + property; + gridSize = 50; + property; + canvasSize = 600; + property; + lineColor = '#cdcdcd'; + property; + liveColor = '#666'; + property; + deadColor = '#eee'; + property; + initialLifeProbability = 0.5; + property; + animationRate = 60; + property; + cellSize = 0; + property; + context: ICanvasRenderingContext2D; + property; + world = createWorld(); + circleOfLife(); + function createWorld() { + return travelWorld(function (cell) { + cell.live = Math.random() < initialLifeProbability; + return cell; + }); + } + function circleOfLife() { + world = travelWorld(function (cell) { + cell = world[cell.row][cell.col]; + draw(cell); + return resolveNextGeneration(cell); + }); + setTimeout(function () { circleOfLife(); }, animationRate); + } + function resolveNextGeneration(cell) { + var count = countNeighbors(cell); + var newCell = new Cell(cell.row, cell.col, cell.live); + if (count < 2 || count > 3) + newCell.live = false; + else if (count == 3) + newCell.live = true; + return newCell; + } + function countNeighbors(cell) { + var neighbors = 0; + for (var row = -1; row <= 1; row++) { + for (var col = -1; col <= 1; col++) { + if (row == 0 && col == 0) + continue; + if (isAlive(cell.row + row, cell.col + col)) { + neighbors++; + } + } + } + return neighbors; + } + function isAlive(row, col) { + // todo - need to guard with worl[row] exists? + if (row < 0 || col < 0 || row >= gridSize || col >= gridSize) + return false; + return world[row][col].live; + } + function travelWorld(callback) { + var result = []; + for (var row = 0; row < gridSize; row++) { + var rowData = []; + for (var col = 0; col < gridSize; col++) { + rowData.push(callback(new Cell(row, col, false))); + } + result.push(rowData); + } + return result; + } + function draw(cell) { + if (context == null) + context = createDrawingContext(); + if (cellSize == 0) + cellSize = canvasSize / gridSize; + context.strokeStyle = lineColor; + context.strokeRect(cell.row * cellSize, cell.col * cellSize, cellSize, cellSize); + context.fillStyle = cell.live ? liveColor : deadColor; + context.fillRect(cell.row * cellSize, cell.col * cellSize, cellSize, cellSize); + } + function createDrawingContext() { + var canvas = document.getElementById('conway-canvas'); + if (canvas == null) { + canvas = document.createElement('canvas'); + canvas.id = "conway-canvas"; + canvas.width = canvasSize; + canvas.height = canvasSize; + document.body.appendChild(canvas); + } + return canvas.getContext('2d'); + } + }); +})(Conway || (Conway = {})); +var game = new Conway.GameOfLife(); diff --git a/src/vs/workbench/services/files/test/node/fixtures/service/deep/employee.js b/src/vs/workbench/services/files/test/node/fixtures/service/deep/employee.js new file mode 100644 index 0000000000..69c58aa5c8 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/service/deep/employee.js @@ -0,0 +1,38 @@ +'use strict'; +var Workforce; +(function (Workforce) { + var Employee = (function () { + function Employee() { + } + return Employee; + })(); + (property); + name: string, property; + basepay: number; + implements; + IEmployee; + { + name; + basepay; + } + var SalesEmployee = (function () { + function SalesEmployee() { + } + return SalesEmployee; + })(); + (); + Employee(name, basepay); + { + function calculatePay() { + var multiplier = (document.getElementById("mult")), as = any, value; + return _super.calculatePay.call(this) * multiplier + bonus; + } + } + var employee = new Employee('Bob', 1000); + var salesEmployee = new SalesEmployee('Jim', 800, 400); + salesEmployee.calclatePay(); // error: No member 'calclatePay' on SalesEmployee +})(Workforce || (Workforce = {})); +extern; +var $; +var s = Workforce.salesEmployee.calculatePay(); +$('#results').text(s); diff --git a/src/vs/workbench/services/files/test/node/fixtures/service/deep/small.js b/src/vs/workbench/services/files/test/node/fixtures/service/deep/small.js new file mode 100644 index 0000000000..5e57b4c943 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/service/deep/small.js @@ -0,0 +1,24 @@ +'use strict'; +var M; +(function (M) { + var C = (function () { + function C() { + } + return C; + })(); + (function (x, property, number) { + if (property === void 0) { property = w; } + var local = 1; + // unresolved symbol because x is local + //self.x++; + self.w--; // ok because w is a property + property; + f = function (y) { + return y + x + local + w + self.w; + }; + function sum(z) { + return z + f(z) + w + self.w; + } + }); +})(M || (M = {})); +var c = new M.C(12, 5); diff --git a/src/vs/workbench/services/files/test/node/fixtures/service/index.html b/src/vs/workbench/services/files/test/node/fixtures/service/index.html new file mode 100644 index 0000000000..bccd24d927 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/service/index.html @@ -0,0 +1,121 @@ + + + + + Strada + + + + + + + + +

TypeScript

+
+ + +
+ + +
+ +
Press 'run' to execute code...
+
...write your results into #results...
+
+ + + diff --git a/src/vs/workbench/services/files/test/node/fixtures/service/small.txt b/src/vs/workbench/services/files/test/node/fixtures/service/small.txt new file mode 100644 index 0000000000..da2e8042fb --- /dev/null +++ b/src/vs/workbench/services/files/test/node/fixtures/service/small.txt @@ -0,0 +1 @@ +Small File \ No newline at end of file diff --git a/src/vs/workbench/services/files/test/node/fixtures/service/some_utf16le.css b/src/vs/workbench/services/files/test/node/fixtures/service/some_utf16le.css new file mode 100644 index 0000000000000000000000000000000000000000..aea04aa2cd17f11ccadedd0f133fcdbc3e797790 GIT binary patch literal 1408 zcmc(fNl(K-429nriT^NC4@ihEEG^rS3!GW*X_~YZNs5}5vWUM9e4Yf-r1XqN8BaXV z_Vbc_eRj34_CKs$?dVPuMRFP{Q0`BmW=eSGXyZ~YKux)KT7XIQ1S0TfSL$2pE7X}z z@pPmP$Q!JkUh%Nho<^J#u#ebV+y#2aO%XHTJmNNHMMRF2fpB=Nxa4R8?o2<#uk*Nq zNr-KxF}i>#3DVvRiwrN3=RU)`#jbO8T7k9iXD2sxcyS z%i-Pc4u-a@dm*(QQxmIq2Cluv{Q^uq>&u$G_1P-krcP76k-J%R=w_dDs%_|)xG5G1 ztjyvT^cY_@QTI&10rG*jIeY>l1YUEECebtd0^6|E-g-n12lF!LCT_${LCo=Qy|rj( z%;8G|^8{wZD(1mZy#}J{sNJ?S(E0CE { + + test('resolve file', function (done: () => void) { + let resolver = create('/index.html'); + resolver.resolve(null).then(result => { + assert.ok(!result.isDirectory); + assert.equal(result.name, 'index.html'); + assert.ok(!!result.etag); + + resolver = create('examples'); + return resolver.resolve(null).then(result => { + assert.ok(result.isDirectory); + }); + }) + .done(() => done(), done); + }); + + test('resolve directory', function (done: () => void) { + let testsElements = ['examples', 'other', 'index.html', 'site.css']; + + let resolver = create('/'); + + resolver.resolve(null).then(result => { + assert.ok(result); + assert.ok(result.children); + assert.ok(result.hasChildren); + assert.ok(result.isDirectory); + assert.equal(result.children.length, testsElements.length); + + assert.ok(result.children.every((entry) => { + return testsElements.some((name) => { + return path.basename(entry.resource.fsPath) === name; + }); + })); + + result.children.forEach((value) => { + assert.ok(path.basename(value.resource.fsPath)); + if (['examples', 'other'].indexOf(path.basename(value.resource.fsPath)) >= 0) { + assert.ok(value.isDirectory); + assert.ok(value.hasChildren); + } else if (path.basename(value.resource.fsPath) === 'index.html') { + assert.ok(!value.isDirectory); + assert.ok(value.hasChildren === false); + } else if (path.basename(value.resource.fsPath) === 'site.css') { + assert.ok(!value.isDirectory); + assert.ok(value.hasChildren === false); + } else { + assert.ok(!'Unexpected value ' + path.basename(value.resource.fsPath)); + } + }); + }) + .done(() => done(), done); + }); + + test('resolve directory - resolveTo single directory', function (done: () => void) { + let resolver = create('/'); + + resolver.resolve({ resolveTo: [toResource('other/deep')] }).then(result => { + assert.ok(result); + assert.ok(result.children); + assert.ok(result.hasChildren); + assert.ok(result.isDirectory); + + let children = result.children; + assert.equal(children.length, 4); + + let other = utils.getByName(result, 'other'); + assert.ok(other); + assert.ok(other.hasChildren); + + let deep = utils.getByName(other, 'deep'); + assert.ok(deep); + assert.ok(deep.hasChildren); + assert.equal(deep.children.length, 4); + }) + .done(() => done(), done); + }); + + test('resolve directory - resolveTo single directory - mixed casing', function (done: () => void) { + let resolver = create('/'); + + resolver.resolve({ resolveTo: [toResource('other/Deep')] }).then(result => { + assert.ok(result); + assert.ok(result.children); + assert.ok(result.hasChildren); + assert.ok(result.isDirectory); + + let children = result.children; + assert.equal(children.length, 4); + + let other = utils.getByName(result, 'other'); + assert.ok(other); + assert.ok(other.hasChildren); + + let deep = utils.getByName(other, 'deep'); + if (isLinux) { // Linux has case sensitive file system + assert.ok(deep); + assert.ok(deep.hasChildren); + assert.ok(!deep.children); // not resolved because we got instructed to resolve other/Deep with capital D + } else { + assert.ok(deep); + assert.ok(deep.hasChildren); + assert.equal(deep.children.length, 4); + } + }) + .done(() => done(), done); + }); + + test('resolve directory - resolveTo multiple directories', function (done: () => void) { + let resolver = create('/'); + + resolver.resolve({ resolveTo: [toResource('other/deep'), toResource('examples')] }).then(result => { + assert.ok(result); + assert.ok(result.children); + assert.ok(result.hasChildren); + assert.ok(result.isDirectory); + + let children = result.children; + assert.equal(children.length, 4); + + let other = utils.getByName(result, 'other'); + assert.ok(other); + assert.ok(other.hasChildren); + + let deep = utils.getByName(other, 'deep'); + assert.ok(deep); + assert.ok(deep.hasChildren); + assert.equal(deep.children.length, 4); + + let examples = utils.getByName(result, 'examples'); + assert.ok(examples); + assert.ok(examples.hasChildren); + assert.equal(examples.children.length, 4); + }) + .done(() => done(), done); + }); + + test('resolve directory - resolveSingleChildFolders', function (done: () => void) { + let resolver = create('/other'); + + resolver.resolve({ resolveSingleChildDescendants: true }).then(result => { + assert.ok(result); + assert.ok(result.children); + assert.ok(result.hasChildren); + assert.ok(result.isDirectory); + + let children = result.children; + assert.equal(children.length, 1); + + let deep = utils.getByName(result, 'deep'); + assert.ok(deep); + assert.ok(deep.hasChildren); + assert.equal(deep.children.length, 4); + }) + .done(() => done(), done); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/files/test/node/utils.ts b/src/vs/workbench/services/files/test/node/utils.ts new file mode 100644 index 0000000000..9a28ac02d5 --- /dev/null +++ b/src/vs/workbench/services/files/test/node/utils.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IFileStat } from 'vs/platform/files/common/files'; + +export function getByName(root: IFileStat, name: string): IFileStat { + for (let i = 0; i < root.children.length; i++) { + if (root.children[i].name === name) { + return root.children[i]; + } + } + + return null; +} \ No newline at end of file diff --git a/src/vs/workbench/services/files/test/node/watcher.test.ts b/src/vs/workbench/services/files/test/node/watcher.test.ts new file mode 100644 index 0000000000..7f59d6995d --- /dev/null +++ b/src/vs/workbench/services/files/test/node/watcher.test.ts @@ -0,0 +1,226 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import assert = require('assert'); + +import platform = require('vs/base/common/platform'); +import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; +import uri from 'vs/base/common/uri'; +import { IRawFileChange, toFileChangesEvent, normalize } from 'vs/workbench/services/files/node/watcher/common'; +import Event, { Emitter } from 'vs/base/common/event'; + +class TestFileWatcher { + private _onFileChanges: Emitter; + + constructor() { + this._onFileChanges = new Emitter(); + } + + public get onFileChanges(): Event { + return this._onFileChanges.event; + } + + public report(changes: IRawFileChange[]): void { + this.onRawFileEvents(changes); + } + + private onRawFileEvents(events: IRawFileChange[]): void { + + // Normalize + let normalizedEvents = normalize(events); + + // Emit through event emitter + if (normalizedEvents.length > 0) { + this._onFileChanges.fire(toFileChangesEvent(normalizedEvents)); + } + } +} + +enum Path { + UNIX, + WINDOWS, + UNC +}; + +suite('Watcher', () => { + + test('watching - simple add/update/delete', function (done: () => void) { + const watch = new TestFileWatcher(); + + const added = uri.file('/users/data/src/added.txt'); + const updated = uri.file('/users/data/src/updated.txt'); + const deleted = uri.file('/users/data/src/deleted.txt'); + + const raw: IRawFileChange[] = [ + { path: added.fsPath, type: FileChangeType.ADDED }, + { path: updated.fsPath, type: FileChangeType.UPDATED }, + { path: deleted.fsPath, type: FileChangeType.DELETED }, + ]; + + watch.onFileChanges(e => { + assert.ok(e); + assert.equal(e.changes.length, 3); + assert.ok(e.contains(added, FileChangeType.ADDED)); + assert.ok(e.contains(updated, FileChangeType.UPDATED)); + assert.ok(e.contains(deleted, FileChangeType.DELETED)); + + done(); + }); + + watch.report(raw); + }); + + let pathSpecs = platform.isWindows ? [Path.WINDOWS, Path.UNC] : [Path.UNIX]; + pathSpecs.forEach((p) => { + test('watching - delete only reported for top level folder (' + p + ')', function (done: () => void) { + const watch = new TestFileWatcher(); + + const deletedFolderA = uri.file(p === Path.UNIX ? '/users/data/src/todelete1' : p === Path.WINDOWS ? 'C:\\users\\data\\src\\todelete1' : '\\\\localhost\\users\\data\\src\\todelete1'); + const deletedFolderB = uri.file(p === Path.UNIX ? '/users/data/src/todelete2' : p === Path.WINDOWS ? 'C:\\users\\data\\src\\todelete2' : '\\\\localhost\\users\\data\\src\\todelete2'); + const deletedFolderBF1 = uri.file(p === Path.UNIX ? '/users/data/src/todelete2/file.txt' : p === Path.WINDOWS ? 'C:\\users\\data\\src\\todelete2\\file.txt' : '\\\\localhost\\users\\data\\src\\todelete2\\file.txt'); + const deletedFolderBF2 = uri.file(p === Path.UNIX ? '/users/data/src/todelete2/more/test.txt' : p === Path.WINDOWS ? 'C:\\users\\data\\src\\todelete2\\more\\test.txt' : '\\\\localhost\\users\\data\\src\\todelete2\\more\\test.txt'); + const deletedFolderBF3 = uri.file(p === Path.UNIX ? '/users/data/src/todelete2/super/bar/foo.txt' : p === Path.WINDOWS ? 'C:\\users\\data\\src\\todelete2\\super\\bar\\foo.txt' : '\\\\localhost\\users\\data\\src\\todelete2\\super\\bar\\foo.txt'); + const deletedFileA = uri.file(p === Path.UNIX ? '/users/data/src/deleteme.txt' : p === Path.WINDOWS ? 'C:\\users\\data\\src\\deleteme.txt' : '\\\\localhost\\users\\data\\src\\deleteme.txt'); + + const addedFile = uri.file(p === Path.UNIX ? '/users/data/src/added.txt' : p === Path.WINDOWS ? 'C:\\users\\data\\src\\added.txt' : '\\\\localhost\\users\\data\\src\\added.txt'); + const updatedFile = uri.file(p === Path.UNIX ? '/users/data/src/updated.txt' : p === Path.WINDOWS ? 'C:\\users\\data\\src\\updated.txt' : '\\\\localhost\\users\\data\\src\\updated.txt'); + + const raw: IRawFileChange[] = [ + { path: deletedFolderA.fsPath, type: FileChangeType.DELETED }, + { path: deletedFolderB.fsPath, type: FileChangeType.DELETED }, + { path: deletedFolderBF1.fsPath, type: FileChangeType.DELETED }, + { path: deletedFolderBF2.fsPath, type: FileChangeType.DELETED }, + { path: deletedFolderBF3.fsPath, type: FileChangeType.DELETED }, + { path: deletedFileA.fsPath, type: FileChangeType.DELETED }, + { path: addedFile.fsPath, type: FileChangeType.ADDED }, + { path: updatedFile.fsPath, type: FileChangeType.UPDATED } + ]; + + watch.onFileChanges(e => { + assert.ok(e); + assert.equal(e.changes.length, 5); + + assert.ok(e.contains(deletedFolderA, FileChangeType.DELETED)); + assert.ok(e.contains(deletedFolderB, FileChangeType.DELETED)); + assert.ok(e.contains(deletedFileA, FileChangeType.DELETED)); + assert.ok(e.contains(addedFile, FileChangeType.ADDED)); + assert.ok(e.contains(updatedFile, FileChangeType.UPDATED)); + + done(); + }); + + watch.report(raw); + }); + }); + + test('watching - event normalization: ignore CREATE followed by DELETE', function (done: () => void) { + const watch = new TestFileWatcher(); + + const created = uri.file('/users/data/src/related'); + const deleted = uri.file('/users/data/src/related'); + const unrelated = uri.file('/users/data/src/unrelated'); + + const raw: IRawFileChange[] = [ + { path: created.fsPath, type: FileChangeType.ADDED }, + { path: deleted.fsPath, type: FileChangeType.DELETED }, + { path: unrelated.fsPath, type: FileChangeType.UPDATED }, + ]; + + watch.onFileChanges(e => { + assert.ok(e); + assert.equal(e.changes.length, 1); + + assert.ok(e.contains(unrelated, FileChangeType.UPDATED)); + + done(); + }); + + watch.report(raw); + }); + + test('watching - event normalization: flatten DELETE followed by CREATE into CHANGE', function (done: () => void) { + const watch = new TestFileWatcher(); + + const deleted = uri.file('/users/data/src/related'); + const created = uri.file('/users/data/src/related'); + const unrelated = uri.file('/users/data/src/unrelated'); + + const raw: IRawFileChange[] = [ + { path: deleted.fsPath, type: FileChangeType.DELETED }, + { path: created.fsPath, type: FileChangeType.ADDED }, + { path: unrelated.fsPath, type: FileChangeType.UPDATED }, + ]; + + watch.onFileChanges(e => { + assert.ok(e); + assert.equal(e.changes.length, 2); + + assert.ok(e.contains(deleted, FileChangeType.UPDATED)); + assert.ok(e.contains(unrelated, FileChangeType.UPDATED)); + + done(); + }); + + watch.report(raw); + }); + + test('watching - event normalization: ignore UPDATE when CREATE received', function (done: () => void) { + const watch = new TestFileWatcher(); + + const created = uri.file('/users/data/src/related'); + const updated = uri.file('/users/data/src/related'); + const unrelated = uri.file('/users/data/src/unrelated'); + + const raw: IRawFileChange[] = [ + { path: created.fsPath, type: FileChangeType.ADDED }, + { path: updated.fsPath, type: FileChangeType.UPDATED }, + { path: unrelated.fsPath, type: FileChangeType.UPDATED }, + ]; + + watch.onFileChanges(e => { + assert.ok(e); + assert.equal(e.changes.length, 2); + + assert.ok(e.contains(created, FileChangeType.ADDED)); + assert.ok(!e.contains(created, FileChangeType.UPDATED)); + assert.ok(e.contains(unrelated, FileChangeType.UPDATED)); + + done(); + }); + + watch.report(raw); + }); + + test('watching - event normalization: apply DELETE', function (done: () => void) { + const watch = new TestFileWatcher(); + + const updated = uri.file('/users/data/src/related'); + const updated2 = uri.file('/users/data/src/related'); + const deleted = uri.file('/users/data/src/related'); + const unrelated = uri.file('/users/data/src/unrelated'); + + const raw: IRawFileChange[] = [ + { path: updated.fsPath, type: FileChangeType.UPDATED }, + { path: updated2.fsPath, type: FileChangeType.UPDATED }, + { path: unrelated.fsPath, type: FileChangeType.UPDATED }, + { path: updated.fsPath, type: FileChangeType.DELETED } + ]; + + watch.onFileChanges(e => { + assert.ok(e); + assert.equal(e.changes.length, 2); + + assert.ok(e.contains(deleted, FileChangeType.DELETED)); + assert.ok(!e.contains(updated, FileChangeType.UPDATED)); + assert.ok(e.contains(unrelated, FileChangeType.UPDATED)); + + done(); + }); + + watch.report(raw); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/group/common/groupService.ts b/src/vs/workbench/services/group/common/groupService.ts new file mode 100644 index 0000000000..a1130519b9 --- /dev/null +++ b/src/vs/workbench/services/group/common/groupService.ts @@ -0,0 +1,134 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { Position, IEditorInput } from 'vs/platform/editor/common/editor'; +import { IEditorStacksModel, IEditorGroup } from 'vs/workbench/common/editor'; +import Event from 'vs/base/common/event'; + +export enum GroupArrangement { + MINIMIZE_OTHERS, + EVEN +} + +export type GroupOrientation = 'vertical' | 'horizontal'; + +export const IEditorGroupService = createDecorator('editorGroupService'); + +export interface ITabOptions { + showTabs?: boolean; + tabCloseButton?: 'left' | 'right' | 'off'; + showIcons?: boolean; + previewEditors?: boolean; +} + +export interface IMoveOptions { + index?: number; + inactive?: boolean; + preserveFocus?: boolean; +} + +/** + * The editor service allows to open editors and work on the active + * editor input and models. + */ +export interface IEditorGroupService { + _serviceBrand: ServiceIdentifier; + + /** + * Emitted when editors or inputs change. Examples: opening, closing of editors. Active editor change. + */ + onEditorsChanged: Event; + + /** + * Emitted when opening an editor fails. + */ + onEditorOpenFail: Event; + + /** + * Emitted when a editors are moved to another position. + */ + onEditorsMoved: Event; + + /** + * Emitted when the editor group orientation was changed. + */ + onGroupOrientationChanged: Event; + + /** + * Emitted when tab options changed. + */ + onTabOptionsChanged: Event; + + /** + * Keyboard focus the editor group at the provided position. + */ + focusGroup(group: IEditorGroup): void; + focusGroup(position: Position): void; + + /** + * Activate the editor group at the provided position without moving focus. + */ + activateGroup(group: IEditorGroup): void; + activateGroup(position: Position): void; + + /** + * Allows to move the editor group from one position to another. + */ + moveGroup(from: IEditorGroup, to: IEditorGroup): void; + moveGroup(from: Position, to: Position): void; + + /** + * Allows to arrange editor groups according to the GroupArrangement enumeration. + */ + arrangeGroups(arrangement: GroupArrangement): void; + + /** + * Changes the editor group layout between vertical and horizontal orientation. Only applies + * if more than one editor is opened. + */ + setGroupOrientation(orientation: GroupOrientation): void; + + /** + * Returns the current editor group layout. + */ + getGroupOrientation(): GroupOrientation; + + /** + * Resize visible editor groups + */ + resizeGroup(position: Position, groupSizeChange: number): void; + + /** + * Adds the pinned state to an editor, removing it from being a preview editor. + */ + pinEditor(group: IEditorGroup, input: IEditorInput): void; + pinEditor(position: Position, input: IEditorInput): void; + + /** + * Removes the pinned state of an editor making it a preview editor. + */ + unpinEditor(group: IEditorGroup, input: IEditorInput): void; + unpinEditor(position: Position, input: IEditorInput): void; + + /** + * Moves an editor from one group to another. The index in the group is optional. + * The inactive option is applied when moving across groups. + */ + moveEditor(input: IEditorInput, from: IEditorGroup, to: IEditorGroup, moveOptions?: IMoveOptions): void; + moveEditor(input: IEditorInput, from: Position, to: Position, moveOptions?: IMoveOptions): void; + + /** + * Provides access to the editor stacks model + */ + getStacksModel(): IEditorStacksModel; + + /** + * Returns tab options. + */ + getTabOptions(): ITabOptions; +} \ No newline at end of file diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts new file mode 100644 index 0000000000..4979566cf1 --- /dev/null +++ b/src/vs/workbench/services/history/browser/history.ts @@ -0,0 +1,708 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import errors = require('vs/base/common/errors'); +import URI from 'vs/base/common/uri'; +import { IEditor } from 'vs/editor/common/editorCommon'; +import { IEditor as IBaseEditor, IEditorInput, ITextEditorOptions, IResourceInput } from 'vs/platform/editor/common/editor'; +import { EditorInput, IEditorCloseEvent, IEditorRegistry, Extensions, toResource, IEditorGroup } from 'vs/workbench/common/editor'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { FileChangesEvent, IFileService, FileChangeType } from 'vs/platform/files/common/files'; +import { Selection } from 'vs/editor/common/core/selection'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { once } from 'vs/base/common/event'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; +import { getExcludes, ISearchConfiguration } from 'vs/platform/search/common/search'; +import { parse, IExpression } from 'vs/base/common/glob'; +import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ResourceGlobMatcher } from 'vs/workbench/common/resources'; + +/** + * Stores the selection & view state of an editor and allows to compare it to other selection states. + */ +export class EditorState { + + private static EDITOR_SELECTION_THRESHOLD = 5; // number of lines to move in editor to justify for new state + + constructor(private _editorInput: IEditorInput, private _selection: Selection) { + } + + public get editorInput(): IEditorInput { + return this._editorInput; + } + + public get selection(): Selection { + return this._selection; + } + + public justifiesNewPushState(other: EditorState, event?: ICursorPositionChangedEvent): boolean { + if (!this._editorInput.matches(other._editorInput)) { + return true; // push different editor inputs + } + + if (!Selection.isISelection(this._selection) || !Selection.isISelection(other._selection)) { + return true; // unknown selections + } + + if (event && event.source === 'api') { + return true; // always let API source win (e.g. "Go to definition" should add a history entry) + } + + const myLineNumber = Math.min(this._selection.selectionStartLineNumber, this._selection.positionLineNumber); + const otherLineNumber = Math.min(other._selection.selectionStartLineNumber, other._selection.positionLineNumber); + + if (Math.abs(myLineNumber - otherLineNumber) < EditorState.EDITOR_SELECTION_THRESHOLD) { + return false; // ignore selection changes in the range of EditorState.EDITOR_SELECTION_THRESHOLD lines + } + + return true; + } +} + +interface ISerializedFileHistoryEntry { + resource?: string; + resourceJSON: object; +} + +export abstract class BaseHistoryService { + + protected toUnbind: IDisposable[]; + + private activeEditorListeners: IDisposable[]; + + constructor( + protected editorGroupService: IEditorGroupService, + protected editorService: IWorkbenchEditorService + ) { + this.toUnbind = []; + this.activeEditorListeners = []; + + // Listeners + this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); + } + + private onEditorsChanged(): void { + + // Dispose old listeners + dispose(this.activeEditorListeners); + this.activeEditorListeners = []; + + const activeEditor = this.editorService.getActiveEditor(); + + // Propagate to history + this.handleActiveEditorChange(activeEditor); + + // Apply listener for selection changes if this is a text editor + const control = getCodeEditor(activeEditor); + if (control) { + this.activeEditorListeners.push(control.onDidChangeCursorPosition(event => { + this.handleEditorSelectionChangeEvent(activeEditor, event); + })); + } + } + + protected abstract handleExcludesChange(): void; + + protected abstract handleEditorSelectionChangeEvent(editor?: IBaseEditor, event?: ICursorPositionChangedEvent): void; + + protected abstract handleActiveEditorChange(editor?: IBaseEditor): void; + + public dispose(): void { + this.toUnbind = dispose(this.toUnbind); + } +} + +interface IStackEntry { + input: IEditorInput | IResourceInput; + options?: ITextEditorOptions; + timestamp: number; +} + +interface IRecentlyClosedFile { + resource: URI; + index: number; +} + +export class HistoryService extends BaseHistoryService implements IHistoryService { + + public _serviceBrand: any; + + private static STORAGE_KEY = 'history.entries'; + private static MAX_HISTORY_ITEMS = 200; + private static MAX_STACK_ITEMS = 20; + private static MAX_RECENTLY_CLOSED_EDITORS = 20; + private static MERGE_EVENT_CHANGES_THRESHOLD = 100; + + private stack: IStackEntry[]; + private index: number; + private navigatingInStack: boolean; + private currentFileEditorState: EditorState; + + private history: (IEditorInput | IResourceInput)[]; + private recentlyClosedFiles: IRecentlyClosedFile[]; + private loaded: boolean; + private registry: IEditorRegistry; + private resourceFilter: ResourceGlobMatcher; + + constructor( + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IEditorGroupService editorGroupService: IEditorGroupService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IStorageService private storageService: IStorageService, + @IConfigurationService private configurationService: IConfigurationService, + @ILifecycleService private lifecycleService: ILifecycleService, + @IFileService private fileService: IFileService, + @IWindowsService private windowService: IWindowsService, + @IInstantiationService private instantiationService: IInstantiationService, + ) { + super(editorGroupService, editorService); + + this.index = -1; + this.stack = []; + this.recentlyClosedFiles = []; + this.loaded = false; + this.registry = Registry.as(Extensions.Editors); + this.resourceFilter = instantiationService.createInstance(ResourceGlobMatcher, root => this.getExcludes(root), (expression: IExpression) => parse(expression)); + + this.registerListeners(); + } + + private getExcludes(root?: URI): IExpression { + const scope = root ? { resource: root } : void 0; + + return getExcludes(this.configurationService.getConfiguration(void 0, scope)); + } + + private registerListeners(): void { + this.toUnbind.push(this.lifecycleService.onShutdown(reason => this.save())); + this.toUnbind.push(this.editorGroupService.onEditorOpenFail(editor => this.remove(editor))); + this.toUnbind.push(this.editorGroupService.getStacksModel().onEditorClosed(event => this.onEditorClosed(event))); + this.toUnbind.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); + this.toUnbind.push(this.resourceFilter.onExpressionChange(() => this.handleExcludesChange())); + } + + private onFileChanges(e: FileChangesEvent): void { + if (e.gotDeleted()) { + this.remove(e); // remove from history files that got deleted or moved + } + } + + private onEditorClosed(event: IEditorCloseEvent): void { + + // Track closing of pinned editor to support to reopen closed editors + if (event.pinned) { + const file = toResource(event.editor, { filter: 'file' }); // we only support files to reopen + if (file) { + + // Remove all inputs matching and add as last recently closed + this.removeFromRecentlyClosedFiles(event.editor); + this.recentlyClosedFiles.push({ resource: file, index: event.index }); + + // Bounding + if (this.recentlyClosedFiles.length > HistoryService.MAX_RECENTLY_CLOSED_EDITORS) { + this.recentlyClosedFiles.shift(); + } + } + } + } + + public reopenLastClosedEditor(): void { + this.ensureHistoryLoaded(); + + const stacks = this.editorGroupService.getStacksModel(); + + let lastClosedFile = this.recentlyClosedFiles.pop(); + while (lastClosedFile && this.isFileOpened(lastClosedFile.resource, stacks.activeGroup)) { + lastClosedFile = this.recentlyClosedFiles.pop(); // pop until we find a file that is not opened + } + + if (lastClosedFile) { + this.editorService.openEditor({ resource: lastClosedFile.resource, options: { pinned: true, index: lastClosedFile.index } }); + } + } + + public forward(acrossEditors?: boolean): void { + if (this.stack.length > this.index + 1) { + if (acrossEditors) { + this.doForwardAcrossEditors(); + } else { + this.doForwardInEditors(); + } + } + } + + private doForwardInEditors(): void { + this.index++; + this.navigate(); + } + + private doForwardAcrossEditors(): void { + let currentIndex = this.index; + const currentEntry = this.stack[this.index]; + + // Find the next entry that does not match our current entry + while (this.stack.length > currentIndex + 1) { + currentIndex++; + + const previousEntry = this.stack[currentIndex]; + if (!this.matches(currentEntry.input, previousEntry.input)) { + this.index = currentIndex; + this.navigate(true /* across editors */); + break; + } + } + } + + public back(acrossEditors?: boolean): void { + if (this.index > 0) { + if (acrossEditors) { + this.doBackAcrossEditors(); + } else { + this.doBackInEditors(); + } + } + } + + private doBackInEditors(): void { + this.index--; + this.navigate(); + } + + private doBackAcrossEditors(): void { + let currentIndex = this.index; + const currentEntry = this.stack[this.index]; + + // Find the next previous entry that does not match our current entry + while (currentIndex > 0) { + currentIndex--; + + const previousEntry = this.stack[currentIndex]; + if (!this.matches(currentEntry.input, previousEntry.input)) { + this.index = currentIndex; + this.navigate(true /* across editors */); + break; + } + } + } + + public clear(): void { + this.ensureHistoryLoaded(); + + this.index = -1; + this.stack.splice(0); + this.history = []; + this.recentlyClosedFiles = []; + } + + private navigate(acrossEditors?: boolean): void { + const entry = this.stack[this.index]; + + let options = entry.options; + if (options && !acrossEditors /* ignore line/col options when going across editors */) { + options.revealIfOpened = true; + } else { + options = { revealIfOpened: true }; + } + + this.navigatingInStack = true; + + let openEditorPromise: TPromise; + if (entry.input instanceof EditorInput) { + openEditorPromise = this.editorService.openEditor(entry.input, options); + } else { + openEditorPromise = this.editorService.openEditor({ resource: (entry.input as IResourceInput).resource, options }); + } + + openEditorPromise.done(() => { + this.navigatingInStack = false; + }, error => { + this.navigatingInStack = false; + errors.onUnexpectedError(error); + }); + } + + protected handleEditorSelectionChangeEvent(editor?: IBaseEditor, event?: ICursorPositionChangedEvent): void { + this.handleEditorEventInStack(editor, event); + } + + protected handleActiveEditorChange(editor?: IBaseEditor): void { + this.handleEditorEventInHistory(editor); + this.handleEditorEventInStack(editor); + } + + private handleEditorEventInHistory(editor?: IBaseEditor): void { + const input = editor ? editor.input : void 0; + + // Ensure we have at least a name to show and not configured to exclude input + if (!input || !input.getName() || !this.include(input)) { + return; + } + + this.ensureHistoryLoaded(); + + const historyInput = this.preferResourceInput(input); + + // Remove any existing entry and add to the beginning + this.removeFromHistory(input); + this.history.unshift(historyInput); + + // Respect max entries setting + if (this.history.length > HistoryService.MAX_HISTORY_ITEMS) { + this.history.pop(); + } + + // Remove this from the history unless the history input is a resource + // that can easily be restored even when the input gets disposed + if (historyInput instanceof EditorInput) { + const onceDispose = once(historyInput.onDispose); + onceDispose(() => { + this.removeFromHistory(input); + }); + } + } + + private include(input: IEditorInput | IResourceInput): boolean { + if (input instanceof EditorInput) { + return true; // include any non files + } + + const resourceInput = input as IResourceInput; + + return !this.resourceFilter.matches(resourceInput.resource); + } + + protected handleExcludesChange(): void { + this.removeExcludedFromHistory(); + } + + public remove(input: IEditorInput | IResourceInput): void; + public remove(input: FileChangesEvent): void; + public remove(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { + this.removeFromHistory(arg1); + this.removeFromStack(arg1); + this.removeFromRecentlyClosedFiles(arg1); + this.removeFromRecentlyOpened(arg1); + } + + private removeExcludedFromHistory(): void { + this.ensureHistoryLoaded(); + + this.history = this.history.filter(e => this.include(e)); + } + + private removeFromHistory(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { + this.ensureHistoryLoaded(); + + this.history = this.history.filter(e => !this.matches(arg1, e)); + } + + private handleEditorEventInStack(editor: IBaseEditor, event?: ICursorPositionChangedEvent): void { + const control = getCodeEditor(editor); + + // treat editor changes that happen as part of stack navigation specially + // we do not want to add a new stack entry as a matter of navigating the + // stack but we need to keep our currentFileEditorState up to date with + // the navigtion that occurs. + if (this.navigatingInStack) { + if (control && editor.input) { + this.currentFileEditorState = new EditorState(editor.input, control.getSelection()); + } else { + this.currentFileEditorState = null; // we navigated to a non file editor + } + + return; + } + + if (control && editor.input) { + this.handleTextEditorEvent(editor, control, event); + + return; + } + + this.currentFileEditorState = null; // at this time we have no active file editor view state + + if (editor && editor.input) { + this.handleNonTextEditorEvent(editor); + } + } + + private handleTextEditorEvent(editor: IBaseEditor, editorControl: IEditor, event?: ICursorPositionChangedEvent): void { + const stateCandidate = new EditorState(editor.input, editorControl.getSelection()); + if (!this.currentFileEditorState || this.currentFileEditorState.justifiesNewPushState(stateCandidate, event)) { + this.currentFileEditorState = stateCandidate; + + let options: ITextEditorOptions; + + const selection = editorControl.getSelection(); + if (selection) { + options = { + selection: { startLineNumber: selection.startLineNumber, startColumn: selection.startColumn } + }; + } + + this.add(editor.input, options, true /* from event */); + } + } + + private handleNonTextEditorEvent(editor: IBaseEditor): void { + const currentStack = this.stack[this.index]; + if (currentStack && this.matches(editor.input, currentStack.input)) { + return; // do not push same editor input again + } + + this.add(editor.input, void 0, true /* from event */); + } + + public add(input: IEditorInput, options?: ITextEditorOptions, fromEvent?: boolean): void { + if (!this.navigatingInStack) { + this.addToStack(input, options, fromEvent); + } + } + + private addToStack(input: IEditorInput, options?: ITextEditorOptions, fromEvent?: boolean): void { + + // Overwrite an entry in the stack if we have a matching input that comes + // with editor options to indicate that this entry is more specific. Also + // prevent entries that have the exact same options. Finally, Overwrite + // entries if it came from an event and we detect that the change came in + // very fast which indicates that it was not coming in from a user change + // but rather rapid programmatic changes. We just take the last of the changes + // to not cause too many entries on the stack. + let replace = false; + if (this.stack[this.index]) { + const currentEntry = this.stack[this.index]; + if (this.matches(input, currentEntry.input) && (this.sameOptions(currentEntry.options, options) || (fromEvent && Date.now() - currentEntry.timestamp < HistoryService.MERGE_EVENT_CHANGES_THRESHOLD))) { + replace = true; + } + } + + const stackInput = this.preferResourceInput(input); + const entry = { input: stackInput, options, timestamp: fromEvent ? Date.now() : void 0 }; + + // If we are not at the end of history, we remove anything after + if (this.stack.length > this.index + 1) { + this.stack = this.stack.slice(0, this.index + 1); + } + + // Replace at current position + if (replace) { + this.stack[this.index] = entry; + } + + // Add to stack at current position + else { + this.index++; + this.stack.splice(this.index, 0, entry); + + // Check for limit + if (this.stack.length > HistoryService.MAX_STACK_ITEMS) { + this.stack.shift(); // remove first and dispose + if (this.index > 0) { + this.index--; + } + } + } + + // Remove this from the stack unless the stack input is a resource + // that can easily be restored even when the input gets disposed + if (stackInput instanceof EditorInput) { + const onceDispose = once(stackInput.onDispose); + onceDispose(() => { + this.removeFromStack(input); + }); + } + } + + private preferResourceInput(input: IEditorInput): IEditorInput | IResourceInput { + const file = toResource(input, { filter: 'file' }); + if (file) { + return { resource: file }; + } + + return input; + } + + private sameOptions(optionsA?: ITextEditorOptions, optionsB?: ITextEditorOptions): boolean { + if (!optionsA && !optionsB) { + return true; + } + + if ((!optionsA && optionsB) || (optionsA && !optionsB)) { + return false; + } + + const s1 = optionsA.selection; + const s2 = optionsB.selection; + + if (!s1 && !s2) { + return true; + } + + if ((!s1 && s2) || (s1 && !s2)) { + return false; + } + + return s1.startLineNumber === s2.startLineNumber; // we consider the history entry same if we are on the same line + } + + private removeFromStack(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { + this.stack = this.stack.filter(e => !this.matches(arg1, e.input)); + this.index = this.stack.length - 1; // reset index + } + + private removeFromRecentlyClosedFiles(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { + this.recentlyClosedFiles = this.recentlyClosedFiles.filter(e => !this.matchesFile(e.resource, arg1)); + } + + private removeFromRecentlyOpened(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { + if (arg1 instanceof EditorInput || arg1 instanceof FileChangesEvent) { + return; // for now do not delete from file events since recently open are likely out of workspace files for which there are no delete events + } + + const input = arg1 as IResourceInput; + + this.windowService.removeFromRecentlyOpened([input.resource.fsPath]); + } + + private isFileOpened(resource: URI, group: IEditorGroup): boolean { + if (!group) { + return false; + } + + if (!group.contains(resource)) { + return false; // fast check + } + + return group.getEditors().some(e => this.matchesFile(resource, e)); + } + + private matches(arg1: IEditorInput | IResourceInput | FileChangesEvent, inputB: IEditorInput | IResourceInput): boolean { + if (arg1 instanceof FileChangesEvent) { + if (inputB instanceof EditorInput) { + return false; // we only support this for IResourceInput + } + + const resourceInputB = inputB as IResourceInput; + + return arg1.contains(resourceInputB.resource, FileChangeType.DELETED); + } + + if (arg1 instanceof EditorInput && inputB instanceof EditorInput) { + return arg1.matches(inputB); + } + + if (arg1 instanceof EditorInput) { + return this.matchesFile((inputB as IResourceInput).resource, arg1); + } + + if (inputB instanceof EditorInput) { + return this.matchesFile((arg1 as IResourceInput).resource, inputB); + } + + const resourceInputA = arg1 as IResourceInput; + const resourceInputB = inputB as IResourceInput; + + return resourceInputA && resourceInputB && resourceInputA.resource.toString() === resourceInputB.resource.toString(); + } + + private matchesFile(resource: URI, arg2: IEditorInput | IResourceInput | FileChangesEvent): boolean { + if (arg2 instanceof FileChangesEvent) { + return arg2.contains(resource, FileChangeType.DELETED); + } + + if (arg2 instanceof EditorInput) { + const file = toResource(arg2, { filter: 'file' }); + + return file && file.toString() === resource.toString(); + } + + const resourceInput = arg2 as IResourceInput; + + return resourceInput && resourceInput.resource.toString() === resource.toString(); + } + + public getHistory(): (IEditorInput | IResourceInput)[] { + this.ensureHistoryLoaded(); + + return this.history.slice(0); + } + + private ensureHistoryLoaded(): void { + if (!this.loaded) { + this.loadHistory(); + } + + this.loaded = true; + } + + private save(): void { + if (!this.history) { + return; // nothing to save because history was not used + } + + const entries: ISerializedFileHistoryEntry[] = this.history.map(input => { + if (input instanceof EditorInput) { + return void 0; // only file resource inputs are serializable currently + } + + return { resourceJSON: (input as IResourceInput).resource.toJSON() }; + }).filter(serialized => !!serialized); + + this.storageService.store(HistoryService.STORAGE_KEY, JSON.stringify(entries), StorageScope.WORKSPACE); + } + + private loadHistory(): void { + let entries: ISerializedFileHistoryEntry[] = []; + + const entriesRaw = this.storageService.get(HistoryService.STORAGE_KEY, StorageScope.WORKSPACE); + if (entriesRaw) { + entries = JSON.parse(entriesRaw); + } + + this.history = entries.map(entry => { + const serializedFileInput = entry as ISerializedFileHistoryEntry; + if (serializedFileInput.resource || serializedFileInput.resourceJSON) { + return { resource: !!serializedFileInput.resourceJSON ? URI.revive(serializedFileInput.resourceJSON) : URI.parse(serializedFileInput.resource) } as IResourceInput; + } + + return void 0; + }).filter(input => !!input); + } + + public getLastActiveWorkspaceRoot(): URI { + if (!this.contextService.hasWorkspace()) { + return void 0; + } + + const history = this.getHistory(); + for (let i = 0; i < history.length; i++) { + const input = history[i]; + if (input instanceof EditorInput) { + continue; + } + + const resourceInput = input as IResourceInput; + const resourceWorkspace = this.contextService.getRoot(resourceInput.resource); + if (resourceWorkspace) { + return resourceWorkspace; + } + } + + // fallback to first workspace + return this.contextService.getWorkspace().roots[0]; + } +} diff --git a/src/vs/workbench/services/history/common/history.ts b/src/vs/workbench/services/history/common/history.ts new file mode 100644 index 0000000000..9d06076204 --- /dev/null +++ b/src/vs/workbench/services/history/common/history.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IEditorInput, ITextEditorOptions, IResourceInput } from 'vs/platform/editor/common/editor'; +import URI from 'vs/base/common/uri'; + +export const IHistoryService = createDecorator('historyService'); + +export interface IHistoryService { + + _serviceBrand: ServiceIdentifier; + + /** + * Re-opens the last closed editor if any. + */ + reopenLastClosedEditor(): void; + + /** + * Add an entry to the navigation stack of the history. + */ + add(input: IEditorInput, options?: ITextEditorOptions): void; + + /** + * Navigate forwards in history. + * + * @param acrossEditors instructs the history to skip navigation entries that + * are only within the same document. + */ + forward(acrossEditors?: boolean): void; + + /** + * Navigate backwards in history. + * + * @param acrossEditors instructs the history to skip navigation entries that + * are only within the same document. + */ + back(acrossEditors?: boolean): void; + + /** + * Removes an entry from history. + */ + remove(input: IEditorInput | IResourceInput): void; + + /** + * Clears all history. + */ + clear(): void; + + /** + * Get the entire history of opened editors. + */ + getHistory(): (IEditorInput | IResourceInput)[]; + + /** + * Looking at the editor history, returns the workspace root of the last file that was + * inside the workspace and part of the editor history. + */ + getLastActiveWorkspaceRoot(): URI; +} \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts new file mode 100644 index 0000000000..e44c8f74f9 --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -0,0 +1,265 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { isArray } from 'vs/base/common/types'; +import { Queue } from 'vs/base/common/async'; +import { IReference, Disposable } from 'vs/base/common/lifecycle'; +import * as json from 'vs/base/common/json'; +import { Edit } from 'vs/base/common/jsonFormatter'; +import { setProperty } from 'vs/base/common/jsonEdit'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IFileService } from 'vs/platform/files/common/files'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; + + +export const IKeybindingEditingService = createDecorator('keybindingEditingService'); + +export interface IKeybindingEditingService { + + _serviceBrand: ServiceIdentifier; + + editKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): TPromise; + + removeKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise; + + resetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise; +} + +export class KeybindingsEditingService extends Disposable implements IKeybindingEditingService { + + public _serviceBrand: any; + private queue: Queue; + + private resource: URI = URI.file(this.environmentService.appKeybindingsPath); + + constructor( + @ITextModelService private textModelResolverService: ITextModelService, + @ITextFileService private textFileService: ITextFileService, + @IFileService private fileService: IFileService, + @IConfigurationService private configurationService: IConfigurationService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + super(); + this.queue = new Queue(); + } + + editKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): TPromise { + return this.queue.queue(() => this.doEditKeybinding(key, keybindingItem)); // queue up writes to prevent race conditions + } + + resetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { + return this.queue.queue(() => this.doResetKeybinding(keybindingItem)); // queue up writes to prevent race conditions + } + + removeKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { + return this.queue.queue(() => this.doRemoveKeybinding(keybindingItem)); // queue up writes to prevent race conditions + } + + private doEditKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): TPromise { + return this.resolveAndValidate() + .then(reference => { + const model = reference.object.textEditorModel; + if (keybindingItem.isDefault) { + this.updateDefaultKeybinding(key, keybindingItem, model); + } else { + this.updateUserKeybinding(key, keybindingItem, model); + } + return this.save().then(() => reference.dispose()); + }); + } + + private doRemoveKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { + return this.resolveAndValidate() + .then(reference => { + const model = reference.object.textEditorModel; + if (keybindingItem.isDefault) { + this.removeDefaultKeybinding(keybindingItem, model); + } else { + this.removeUserKeybinding(keybindingItem, model); + } + return this.save().then(() => reference.dispose()); + }); + } + + private doResetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { + return this.resolveAndValidate() + .then(reference => { + const model = reference.object.textEditorModel; + if (!keybindingItem.isDefault) { + this.removeUserKeybinding(keybindingItem, model); + this.removeUnassignedDefaultKeybinding(keybindingItem, model); + } + return this.save().then(() => reference.dispose()); + }); + } + + private save(): TPromise { + return this.textFileService.save(this.resource); + } + + private updateUserKeybinding(newKey: string, keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void { + const { tabSize, insertSpaces } = model.getOptions(); + const eol = model.getEOL(); + const userKeybindingEntries = json.parse(model.getValue()); + const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries); + if (userKeybindingEntryIndex !== -1) { + this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex, 'key'], newKey, { tabSize, insertSpaces, eol })[0], model); + } + } + + private updateDefaultKeybinding(newKey: string, keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void { + const { tabSize, insertSpaces } = model.getOptions(); + const eol = model.getEOL(); + const userKeybindingEntries = json.parse(model.getValue()); + const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries); + if (userKeybindingEntryIndex !== -1) { + // Update the keybinding with new key + this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex, 'key'], newKey, { tabSize, insertSpaces, eol })[0], model); + } else { + // Add the new keybinidng with new key + this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(newKey, keybindingItem.command, keybindingItem.when, false), { tabSize, insertSpaces, eol })[0], model); + } + if (keybindingItem.resolvedKeybinding) { + // Unassign the default keybinding + this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(keybindingItem.resolvedKeybinding.getUserSettingsLabel(), keybindingItem.command, keybindingItem.when, true), { tabSize, insertSpaces, eol })[0], model); + } + } + + private removeUserKeybinding(keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void { + const { tabSize, insertSpaces } = model.getOptions(); + const eol = model.getEOL(); + const userKeybindingEntries = json.parse(model.getValue()); + const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries); + if (userKeybindingEntryIndex !== -1) { + this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex], void 0, { tabSize, insertSpaces, eol })[0], model); + } + } + + private removeDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void { + const { tabSize, insertSpaces } = model.getOptions(); + const eol = model.getEOL(); + this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(keybindingItem.resolvedKeybinding.getUserSettingsLabel(), keybindingItem.command, keybindingItem.when, true), { tabSize, insertSpaces, eol })[0], model); + } + + private removeUnassignedDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void { + const { tabSize, insertSpaces } = model.getOptions(); + const eol = model.getEOL(); + const userKeybindingEntries = json.parse(model.getValue()); + const index = this.findUnassignedDefaultKeybindingEntryIndex(keybindingItem, userKeybindingEntries); + if (index !== -1) { + this.applyEditsToBuffer(setProperty(model.getValue(), [index], void 0, { tabSize, insertSpaces, eol })[0], model); + } + } + + private findUserKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number { + for (let index = 0; index < userKeybindingEntries.length; index++) { + const keybinding = userKeybindingEntries[index]; + if (keybinding.command === keybindingItem.command) { + if (!keybinding.when && !keybindingItem.when) { + return index; + } + if (keybinding.when && keybindingItem.when) { + if (ContextKeyExpr.deserialize(keybinding.when).serialize() === keybindingItem.when.serialize()) { + return index; + } + } + } + } + return -1; + } + + private findUnassignedDefaultKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number { + for (let index = 0; index < userKeybindingEntries.length; index++) { + if (userKeybindingEntries[index].command === `-${keybindingItem.command}`) { + return index; + } + } + return -1; + } + + private asObject(key: string, command: string, when: ContextKeyExpr, negate: boolean): any { + const object = { key }; + object['command'] = negate ? `-${command}` : command; + if (when) { + object['when'] = when.serialize(); + } + return object; + } + + + private applyEditsToBuffer(edit: Edit, model: editorCommon.IModel): void { + const startPosition = model.getPositionAt(edit.offset); + const endPosition = model.getPositionAt(edit.offset + edit.length); + const range = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column); + let currentText = model.getValueInRange(range); + const editOperation = currentText ? EditOperation.replace(range, edit.content) : EditOperation.insert(startPosition, edit.content); + model.pushEditOperations([new Selection(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column)], [editOperation], () => []); + } + + + private resolveModelReference(): TPromise> { + return this.fileService.existsFile(this.resource) + .then(exists => { + const EOL = this.configurationService.getConfiguration('files', { overrideIdentifier: 'json' })['eol']; + const result = exists ? TPromise.as(null) : this.fileService.updateContent(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' }); + return result.then(() => this.textModelResolverService.createModelReference(this.resource)); + }); + } + + private resolveAndValidate(): TPromise> { + + // Target cannot be dirty if not writing into buffer + if (this.textFileService.isDirty(this.resource)) { + return TPromise.wrapError>(new Error(localize('errorKeybindingsFileDirty', "Unable to write because the file is dirty. Please save the **Keybindings** file and try again."))); + } + + return this.resolveModelReference() + .then(reference => { + const model = reference.object.textEditorModel; + const EOL = model.getEOL(); + if (model.getValue()) { + const parsed = this.parse(model); + if (parsed.parseErrors.length) { + return TPromise.wrapError>(new Error(localize('parseErrors', "Unable to write keybindings. Please open **Keybindings file** to correct errors/warnings in the file and try again."))); + } + if (parsed.result) { + if (!isArray(parsed.result)) { + return TPromise.wrapError>(new Error(localize('errorInvalidConfiguration', "Unable to write keybindings. **Keybindings file** has an object which is not of type Array. Please open the file to clean up and try again."))); + } + } else { + const content = EOL + '[]'; + this.applyEditsToBuffer({ content, length: content.length, offset: model.getValue().length }, model); + } + } else { + const content = this.getEmptyContent(EOL); + this.applyEditsToBuffer({ content, length: content.length, offset: 0 }, model); + } + return reference; + }); + } + + private parse(model: editorCommon.IModel): { result: IUserFriendlyKeybinding[], parseErrors: json.ParseError[] } { + const parseErrors: json.ParseError[] = []; + const result = json.parse(model.getValue(), parseErrors, { allowTrailingComma: true }); + return { result, parseErrors }; + } + + private getEmptyContent(EOL: string): string { + return '// ' + localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + EOL + '[]'; + } +} diff --git a/src/vs/workbench/services/keybinding/common/keybindingIO.ts b/src/vs/workbench/services/keybinding/common/keybindingIO.ts new file mode 100644 index 0000000000..6bb407e9f2 --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/keybindingIO.ts @@ -0,0 +1,194 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { Keybinding, SimpleKeybinding, ChordKeybinding, KeyCodeUtils } from 'vs/base/common/keyCodes'; +import { OperatingSystem } from 'vs/base/common/platform'; +import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { ScanCodeBinding, ScanCodeUtils } from 'vs/workbench/services/keybinding/common/scanCode'; + +export interface IUserKeybindingItem { + firstPart: SimpleKeybinding | ScanCodeBinding; + chordPart: SimpleKeybinding | ScanCodeBinding; + command: string; + commandArgs?: any; + when: ContextKeyExpr; +} + +export class KeybindingIO { + + public static writeKeybindingItem(out: OutputBuilder, item: ResolvedKeybindingItem, OS: OperatingSystem): void { + let quotedSerializedKeybinding = JSON.stringify(item.resolvedKeybinding.getUserSettingsLabel()); + out.write(`{ "key": ${rightPaddedString(quotedSerializedKeybinding + ',', 25)} "command": `); + + let serializedWhen = item.when ? item.when.serialize() : ''; + let quotedSerializeCommand = JSON.stringify(item.command); + if (serializedWhen.length > 0) { + out.write(`${quotedSerializeCommand},`); + out.writeLine(); + out.write(` "when": "${serializedWhen}" `); + } else { + out.write(`${quotedSerializeCommand} `); + } + // out.write(String(item.weight1 + '-' + item.weight2)); + out.write('}'); + } + + public static readUserKeybindingItem(input: IUserFriendlyKeybinding, OS: OperatingSystem): IUserKeybindingItem { + const [firstPart, chordPart] = (typeof input.key === 'string' ? this._readUserBinding(input.key) : [null, null]); + const when = (typeof input.when === 'string' ? ContextKeyExpr.deserialize(input.when) : null); + const command = (typeof input.command === 'string' ? input.command : null); + const commandArgs = (typeof input.args !== 'undefined' ? input.args : null); + return { + firstPart: firstPart, + chordPart: chordPart, + command: command, + commandArgs: commandArgs, + when: when + }; + } + + private static _readModifiers(input: string) { + input = input.toLowerCase().trim(); + + let ctrl = false; + let shift = false; + let alt = false; + let meta = false; + + let matchedModifier: boolean; + + do { + matchedModifier = false; + if (/^ctrl(\+|\-)/.test(input)) { + ctrl = true; + input = input.substr('ctrl-'.length); + matchedModifier = true; + } + if (/^shift(\+|\-)/.test(input)) { + shift = true; + input = input.substr('shift-'.length); + matchedModifier = true; + } + if (/^alt(\+|\-)/.test(input)) { + alt = true; + input = input.substr('alt-'.length); + matchedModifier = true; + } + if (/^meta(\+|\-)/.test(input)) { + meta = true; + input = input.substr('meta-'.length); + matchedModifier = true; + } + if (/^win(\+|\-)/.test(input)) { + meta = true; + input = input.substr('win-'.length); + matchedModifier = true; + } + if (/^cmd(\+|\-)/.test(input)) { + meta = true; + input = input.substr('cmd-'.length); + matchedModifier = true; + } + } while (matchedModifier); + + let key: string; + + const firstSpaceIdx = input.indexOf(' '); + if (firstSpaceIdx > 0) { + key = input.substring(0, firstSpaceIdx); + input = input.substring(firstSpaceIdx); + } else { + key = input; + input = ''; + } + + return { + remains: input, + ctrl, + shift, + alt, + meta, + key + }; + } + + private static _readSimpleKeybinding(input: string): [SimpleKeybinding, string] { + const mods = this._readModifiers(input); + const keyCode = KeyCodeUtils.fromUserSettings(mods.key); + return [new SimpleKeybinding(mods.ctrl, mods.shift, mods.alt, mods.meta, keyCode), mods.remains]; + } + + public static readKeybinding(input: string, OS: OperatingSystem): Keybinding { + if (!input) { + return null; + } + + let [firstPart, remains] = this._readSimpleKeybinding(input); + let chordPart: SimpleKeybinding = null; + if (remains.length > 0) { + [chordPart] = this._readSimpleKeybinding(remains); + } + + if (chordPart) { + return new ChordKeybinding(firstPart, chordPart); + } + return firstPart; + } + + private static _readSimpleUserBinding(input: string): [SimpleKeybinding | ScanCodeBinding, string] { + const mods = this._readModifiers(input); + const scanCodeMatch = mods.key.match(/^\[([^\]]+)\]$/); + if (scanCodeMatch) { + const strScanCode = scanCodeMatch[1]; + const scanCode = ScanCodeUtils.lowerCaseToEnum(strScanCode); + return [new ScanCodeBinding(mods.ctrl, mods.shift, mods.alt, mods.meta, scanCode), mods.remains]; + } + const keyCode = KeyCodeUtils.fromUserSettings(mods.key); + return [new SimpleKeybinding(mods.ctrl, mods.shift, mods.alt, mods.meta, keyCode), mods.remains]; + } + + static _readUserBinding(input: string): [SimpleKeybinding | ScanCodeBinding, SimpleKeybinding | ScanCodeBinding] { + if (!input) { + return [null, null]; + } + + let [firstPart, remains] = this._readSimpleUserBinding(input); + let chordPart: SimpleKeybinding | ScanCodeBinding = null; + if (remains.length > 0) { + [chordPart] = this._readSimpleUserBinding(remains); + } + return [firstPart, chordPart]; + } +} + +function rightPaddedString(str: string, minChars: number): string { + if (str.length < minChars) { + return str + (new Array(minChars - str.length).join(' ')); + } + return str; +} + +export class OutputBuilder { + + private _lines: string[] = []; + private _currentLine: string = ''; + + write(str: string): void { + this._currentLine += str; + } + + writeLine(str: string = ''): void { + this._lines.push(this._currentLine + str); + this._currentLine = ''; + } + + toString(): string { + this.writeLine(); + return this._lines.join('\n'); + } +} diff --git a/src/vs/workbench/services/keybinding/common/keyboardMapper.ts b/src/vs/workbench/services/keybinding/common/keyboardMapper.ts new file mode 100644 index 0000000000..f0db78036d --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/keyboardMapper.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { ScanCodeBinding } from 'vs/workbench/services/keybinding/common/scanCode'; + +export interface IKeyboardMapper { + dumpDebugInfo(): string; + resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[]; + resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding; + resolveUserBinding(firstPart: SimpleKeybinding | ScanCodeBinding, chordPart: SimpleKeybinding | ScanCodeBinding): ResolvedKeybinding[]; +} diff --git a/src/vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper.ts new file mode 100644 index 0000000000..921a897db3 --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper.ts @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { OperatingSystem } from 'vs/base/common/platform'; +import { ResolvedKeybinding, SimpleKeybinding, Keybinding, KeyCode, ChordKeybinding } from 'vs/base/common/keyCodes'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; +import { ScanCodeBinding, ScanCode, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/workbench/services/keybinding/common/scanCode'; + +export interface IMacLinuxKeyMapping { + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; + + valueIsDeadKey?: boolean; + withShiftIsDeadKey?: boolean; + withAltGrIsDeadKey?: boolean; + withShiftAltGrIsDeadKey?: boolean; +} + +export interface IMacLinuxKeyboardMapping { + [scanCode: string]: IMacLinuxKeyMapping; +} + +/** + * A keyboard mapper to be used when reading the keymap from the OS fails. + */ +export class MacLinuxFallbackKeyboardMapper implements IKeyboardMapper { + + /** + * OS (can be Linux or Macintosh) + */ + private readonly _OS: OperatingSystem; + + constructor(OS: OperatingSystem) { + this._OS = OS; + } + + public dumpDebugInfo(): string { + return 'FallbackKeyboardMapper dispatching on keyCode'; + } + + public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] { + return [new USLayoutResolvedKeybinding(keybinding, this._OS)]; + } + + public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding { + let keybinding = new SimpleKeybinding( + keyboardEvent.ctrlKey, + keyboardEvent.shiftKey, + keyboardEvent.altKey, + keyboardEvent.metaKey, + keyboardEvent.keyCode + ); + return new USLayoutResolvedKeybinding(keybinding, this._OS); + } + + private _scanCodeToKeyCode(scanCode: ScanCode): KeyCode { + const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode]; + if (immutableKeyCode !== -1) { + return immutableKeyCode; + } + + switch (scanCode) { + case ScanCode.KeyA: return KeyCode.KEY_A; + case ScanCode.KeyB: return KeyCode.KEY_B; + case ScanCode.KeyC: return KeyCode.KEY_C; + case ScanCode.KeyD: return KeyCode.KEY_D; + case ScanCode.KeyE: return KeyCode.KEY_E; + case ScanCode.KeyF: return KeyCode.KEY_F; + case ScanCode.KeyG: return KeyCode.KEY_G; + case ScanCode.KeyH: return KeyCode.KEY_H; + case ScanCode.KeyI: return KeyCode.KEY_I; + case ScanCode.KeyJ: return KeyCode.KEY_J; + case ScanCode.KeyK: return KeyCode.KEY_K; + case ScanCode.KeyL: return KeyCode.KEY_L; + case ScanCode.KeyM: return KeyCode.KEY_M; + case ScanCode.KeyN: return KeyCode.KEY_N; + case ScanCode.KeyO: return KeyCode.KEY_O; + case ScanCode.KeyP: return KeyCode.KEY_P; + case ScanCode.KeyQ: return KeyCode.KEY_Q; + case ScanCode.KeyR: return KeyCode.KEY_R; + case ScanCode.KeyS: return KeyCode.KEY_S; + case ScanCode.KeyT: return KeyCode.KEY_T; + case ScanCode.KeyU: return KeyCode.KEY_U; + case ScanCode.KeyV: return KeyCode.KEY_V; + case ScanCode.KeyW: return KeyCode.KEY_W; + case ScanCode.KeyX: return KeyCode.KEY_X; + case ScanCode.KeyY: return KeyCode.KEY_Y; + case ScanCode.KeyZ: return KeyCode.KEY_Z; + case ScanCode.Digit1: return KeyCode.KEY_1; + case ScanCode.Digit2: return KeyCode.KEY_2; + case ScanCode.Digit3: return KeyCode.KEY_3; + case ScanCode.Digit4: return KeyCode.KEY_4; + case ScanCode.Digit5: return KeyCode.KEY_5; + case ScanCode.Digit6: return KeyCode.KEY_6; + case ScanCode.Digit7: return KeyCode.KEY_7; + case ScanCode.Digit8: return KeyCode.KEY_8; + case ScanCode.Digit9: return KeyCode.KEY_9; + case ScanCode.Digit0: return KeyCode.KEY_0; + case ScanCode.Minus: return KeyCode.US_MINUS; + case ScanCode.Equal: return KeyCode.US_EQUAL; + case ScanCode.BracketLeft: return KeyCode.US_OPEN_SQUARE_BRACKET; + case ScanCode.BracketRight: return KeyCode.US_CLOSE_SQUARE_BRACKET; + case ScanCode.Backslash: return KeyCode.US_BACKSLASH; + case ScanCode.IntlHash: return KeyCode.Unknown; // missing + case ScanCode.Semicolon: return KeyCode.US_SEMICOLON; + case ScanCode.Quote: return KeyCode.US_QUOTE; + case ScanCode.Backquote: return KeyCode.US_BACKTICK; + case ScanCode.Comma: return KeyCode.US_COMMA; + case ScanCode.Period: return KeyCode.US_DOT; + case ScanCode.Slash: return KeyCode.US_SLASH; + case ScanCode.IntlBackslash: return KeyCode.OEM_102; + } + return KeyCode.Unknown; + } + + private _resolveSimpleUserBinding(binding: SimpleKeybinding | ScanCodeBinding): SimpleKeybinding { + if (!binding) { + return null; + } + if (binding instanceof SimpleKeybinding) { + return binding; + } + const keyCode = this._scanCodeToKeyCode(binding.scanCode); + if (keyCode === KeyCode.Unknown) { + return null; + } + return new SimpleKeybinding(binding.ctrlKey, binding.shiftKey, binding.altKey, binding.metaKey, keyCode); + } + + public resolveUserBinding(firstPart: SimpleKeybinding | ScanCodeBinding, chordPart: SimpleKeybinding | ScanCodeBinding): ResolvedKeybinding[] { + const _firstPart = this._resolveSimpleUserBinding(firstPart); + const _chordPart = this._resolveSimpleUserBinding(chordPart); + if (_firstPart && _chordPart) { + return [new USLayoutResolvedKeybinding(new ChordKeybinding(_firstPart, _chordPart), this._OS)]; + } + if (_firstPart) { + return [new USLayoutResolvedKeybinding(_firstPart, this._OS)]; + } + return []; + } +} diff --git a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts new file mode 100644 index 0000000000..6a9b6a8ec1 --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts @@ -0,0 +1,1236 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { OperatingSystem } from 'vs/base/common/platform'; +import { KeyCode, ResolvedKeybinding, KeyCodeUtils, SimpleKeybinding, Keybinding, KeybindingType, ResolvedKeybindingPart } from 'vs/base/common/keyCodes'; +import { ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE, IMMUTABLE_KEY_CODE_TO_CODE, ScanCodeBinding } from 'vs/workbench/services/keybinding/common/scanCode'; +import { CharCode } from 'vs/base/common/charCode'; +import { UILabelProvider, AriaLabelProvider, UserSettingsLabelProvider, ElectronAcceleratorLabelProvider } from 'vs/base/common/keybindingLabels'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; + +export interface IMacLinuxKeyMapping { + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} + +function macLinuxKeyMappingEquals(a: IMacLinuxKeyMapping, b: IMacLinuxKeyMapping): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return ( + a.value === b.value + && a.withShift === b.withShift + && a.withAltGr === b.withAltGr + && a.withShiftAltGr === b.withShiftAltGr + ); +} + +export interface IMacLinuxKeyboardMapping { + [scanCode: string]: IMacLinuxKeyMapping; +} + +export function macLinuxKeyboardMappingEquals(a: IMacLinuxKeyboardMapping, b: IMacLinuxKeyboardMapping): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + for (let scanCode = 0; scanCode < ScanCode.MAX_VALUE; scanCode++) { + const strScanCode = ScanCodeUtils.toString(scanCode); + const aEntry = a[strScanCode]; + const bEntry = b[strScanCode]; + if (!macLinuxKeyMappingEquals(aEntry, bEntry)) { + return false; + } + } + return true; +} + +/** + * A map from character to key codes. + * e.g. Contains entries such as: + * - '/' => { keyCode: KeyCode.US_SLASH, shiftKey: false } + * - '?' => { keyCode: KeyCode.US_SLASH, shiftKey: true } + */ +const CHAR_CODE_TO_KEY_CODE: { keyCode: KeyCode; shiftKey: boolean }[] = []; + +export class NativeResolvedKeybinding extends ResolvedKeybinding { + + private readonly _mapper: MacLinuxKeyboardMapper; + private readonly _OS: OperatingSystem; + private readonly _firstPart: ScanCodeBinding; + private readonly _chordPart: ScanCodeBinding; + + constructor(mapper: MacLinuxKeyboardMapper, OS: OperatingSystem, firstPart: ScanCodeBinding, chordPart: ScanCodeBinding) { + super(); + this._mapper = mapper; + this._OS = OS; + this._firstPart = firstPart; + this._chordPart = chordPart; + } + + public getLabel(): string { + let firstPart = this._mapper.getUILabelForScanCodeBinding(this._firstPart); + let chordPart = this._mapper.getUILabelForScanCodeBinding(this._chordPart); + return UILabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, this._OS); + } + + public getAriaLabel(): string { + let firstPart = this._mapper.getAriaLabelForScanCodeBinding(this._firstPart); + let chordPart = this._mapper.getAriaLabelForScanCodeBinding(this._chordPart); + return AriaLabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, this._OS); + } + + public getElectronAccelerator(): string { + if (this._chordPart !== null) { + // Electron cannot handle chords + return null; + } + + let firstPart = this._mapper.getElectronAcceleratorLabelForScanCodeBinding(this._firstPart); + return ElectronAcceleratorLabelProvider.toLabel(this._firstPart, firstPart, null, null, this._OS); + } + + public getUserSettingsLabel(): string { + let firstPart = this._mapper.getUserSettingsLabelForScanCodeBinding(this._firstPart); + let chordPart = this._mapper.getUserSettingsLabelForScanCodeBinding(this._chordPart); + return UserSettingsLabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, this._OS); + } + + private _isWYSIWYG(binding: ScanCodeBinding): boolean { + if (!binding) { + return true; + } + if (IMMUTABLE_CODE_TO_KEY_CODE[binding.scanCode] !== -1) { + return true; + } + let a = this._mapper.getAriaLabelForScanCodeBinding(binding); + let b = this._mapper.getUserSettingsLabelForScanCodeBinding(binding); + + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return (a.toLowerCase() === b.toLowerCase()); + } + + public isWYSIWYG(): boolean { + return (this._isWYSIWYG(this._firstPart) && this._isWYSIWYG(this._chordPart)); + } + + public isChord(): boolean { + return (this._chordPart ? true : false); + } + + public getParts(): [ResolvedKeybindingPart, ResolvedKeybindingPart] { + return [ + this._toResolvedKeybindingPart(this._firstPart), + this._toResolvedKeybindingPart(this._chordPart) + ]; + } + + private _toResolvedKeybindingPart(binding: ScanCodeBinding): ResolvedKeybindingPart { + if (!binding) { + return null; + } + + return new ResolvedKeybindingPart( + binding.ctrlKey, + binding.shiftKey, + binding.altKey, + binding.metaKey, + this._mapper.getUILabelForScanCodeBinding(binding), + this._mapper.getAriaLabelForScanCodeBinding(binding) + ); + } + + public getDispatchParts(): [string, string] { + let firstPart = this._firstPart ? this._mapper.getDispatchStrForScanCodeBinding(this._firstPart) : null; + let chordPart = this._chordPart ? this._mapper.getDispatchStrForScanCodeBinding(this._chordPart) : null; + return [firstPart, chordPart]; + } +} + +interface IScanCodeMapping { + scanCode: ScanCode; + value: number; + withShift: number; + withAltGr: number; + withShiftAltGr: number; +} + +class ScanCodeCombo { + public readonly ctrlKey: boolean; + public readonly shiftKey: boolean; + public readonly altKey: boolean; + public readonly scanCode: ScanCode; + + constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, scanCode: ScanCode) { + this.ctrlKey = ctrlKey; + this.shiftKey = shiftKey; + this.altKey = altKey; + this.scanCode = scanCode; + } + + public toString(): string { + return `${this.ctrlKey ? 'Ctrl+' : ''}${this.shiftKey ? 'Shift+' : ''}${this.altKey ? 'Alt+' : ''}${ScanCodeUtils.toString(this.scanCode)}`; + } + + public equals(other: ScanCodeCombo): boolean { + return ( + this.ctrlKey === other.ctrlKey + && this.shiftKey === other.shiftKey + && this.altKey === other.altKey + && this.scanCode === other.scanCode + ); + } + + private getProducedCharCode(mapping: IMacLinuxKeyMapping): string { + if (!mapping) { + return ''; + } + if (this.ctrlKey && this.shiftKey && this.altKey) { + return mapping.withShiftAltGr; + } + if (this.ctrlKey && this.altKey) { + return mapping.withAltGr; + } + if (this.shiftKey) { + return mapping.withShift; + } + return mapping.value; + } + + public getProducedChar(mapping: IMacLinuxKeyMapping): string { + const charCode = MacLinuxKeyboardMapper.getCharCode(this.getProducedCharCode(mapping)); + if (charCode === 0) { + return ' --- '; + } + if (charCode >= CharCode.U_Combining_Grave_Accent && charCode <= CharCode.U_Combining_Latin_Small_Letter_X) { + // combining + return 'U+' + charCode.toString(16); + } + return ' ' + String.fromCharCode(charCode) + ' '; + } +} + +class KeyCodeCombo { + public readonly ctrlKey: boolean; + public readonly shiftKey: boolean; + public readonly altKey: boolean; + public readonly keyCode: KeyCode; + + constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, keyCode: KeyCode) { + this.ctrlKey = ctrlKey; + this.shiftKey = shiftKey; + this.altKey = altKey; + this.keyCode = keyCode; + } + + public toString(): string { + return `${this.ctrlKey ? 'Ctrl+' : ''}${this.shiftKey ? 'Shift+' : ''}${this.altKey ? 'Alt+' : ''}${KeyCodeUtils.toString(this.keyCode)}`; + } +} + +class ScanCodeKeyCodeMapper { + + /** + * ScanCode combination => KeyCode combination. + * Only covers relevant modifiers ctrl, shift, alt (since meta does not influence the mappings). + */ + private readonly _scanCodeToKeyCode: number[][] = []; + /** + * inverse of `_scanCodeToKeyCode`. + * KeyCode combination => ScanCode combination. + * Only covers relevant modifiers ctrl, shift, alt (since meta does not influence the mappings). + */ + private readonly _keyCodeToScanCode: number[][] = []; + + constructor() { + this._scanCodeToKeyCode = []; + this._keyCodeToScanCode = []; + } + + public registrationComplete(): void { + // IntlHash and IntlBackslash are rare keys, so ensure they don't end up being the preferred... + this._moveToEnd(ScanCode.IntlHash); + this._moveToEnd(ScanCode.IntlBackslash); + } + + private _moveToEnd(scanCode: ScanCode): void { + for (let mod = 0; mod < 8; mod++) { + const encodedKeyCodeCombos = this._scanCodeToKeyCode[(scanCode << 3) + mod]; + if (!encodedKeyCodeCombos) { + continue; + } + for (let i = 0, len = encodedKeyCodeCombos.length; i < len; i++) { + const encodedScanCodeCombos = this._keyCodeToScanCode[encodedKeyCodeCombos[i]]; + if (encodedScanCodeCombos.length === 1) { + continue; + } + for (let j = 0, len = encodedScanCodeCombos.length; j < len; j++) { + const entry = encodedScanCodeCombos[j]; + const entryScanCode = (entry >>> 3); + if (entryScanCode === scanCode) { + // Move this entry to the end + for (let k = j + 1; k < len; k++) { + encodedScanCodeCombos[k - 1] = encodedScanCodeCombos[k]; + } + encodedScanCodeCombos[len - 1] = entry; + } + } + } + } + } + + public registerIfUnknown(scanCodeCombo: ScanCodeCombo, keyCodeCombo: KeyCodeCombo): void { + if (keyCodeCombo.keyCode === KeyCode.Unknown) { + return; + } + const scanCodeComboEncoded = this._encodeScanCodeCombo(scanCodeCombo); + const keyCodeComboEncoded = this._encodeKeyCodeCombo(keyCodeCombo); + + const keyCodeIsDigit = (keyCodeCombo.keyCode >= KeyCode.KEY_0 && keyCodeCombo.keyCode <= KeyCode.KEY_9); + const keyCodeIsLetter = (keyCodeCombo.keyCode >= KeyCode.KEY_A && keyCodeCombo.keyCode <= KeyCode.KEY_Z); + + const existingKeyCodeCombos = this._scanCodeToKeyCode[scanCodeComboEncoded]; + + // Allow a scan code to map to multiple key codes if it is a digit or a letter key code + if (keyCodeIsDigit || keyCodeIsLetter) { + // Only check that we don't insert the same entry twice + if (existingKeyCodeCombos) { + for (let i = 0, len = existingKeyCodeCombos.length; i < len; i++) { + if (existingKeyCodeCombos[i] === keyCodeComboEncoded) { + // avoid duplicates + return; + } + } + } + } else { + // Don't allow multiples + if (existingKeyCodeCombos && existingKeyCodeCombos.length !== 0) { + return; + } + } + + this._scanCodeToKeyCode[scanCodeComboEncoded] = this._scanCodeToKeyCode[scanCodeComboEncoded] || []; + this._scanCodeToKeyCode[scanCodeComboEncoded].unshift(keyCodeComboEncoded); + + this._keyCodeToScanCode[keyCodeComboEncoded] = this._keyCodeToScanCode[keyCodeComboEncoded] || []; + this._keyCodeToScanCode[keyCodeComboEncoded].unshift(scanCodeComboEncoded); + } + + public lookupKeyCodeCombo(keyCodeCombo: KeyCodeCombo): ScanCodeCombo[] { + const keyCodeComboEncoded = this._encodeKeyCodeCombo(keyCodeCombo); + const scanCodeCombosEncoded = this._keyCodeToScanCode[keyCodeComboEncoded]; + if (!scanCodeCombosEncoded || scanCodeCombosEncoded.length === 0) { + return []; + } + + let result: ScanCodeCombo[] = []; + for (let i = 0, len = scanCodeCombosEncoded.length; i < len; i++) { + const scanCodeComboEncoded = scanCodeCombosEncoded[i]; + + const ctrlKey = (scanCodeComboEncoded & 0b001) ? true : false; + const shiftKey = (scanCodeComboEncoded & 0b010) ? true : false; + const altKey = (scanCodeComboEncoded & 0b100) ? true : false; + const scanCode: ScanCode = (scanCodeComboEncoded >>> 3); + + result[i] = new ScanCodeCombo(ctrlKey, shiftKey, altKey, scanCode); + } + return result; + } + + public lookupScanCodeCombo(scanCodeCombo: ScanCodeCombo): KeyCodeCombo[] { + const scanCodeComboEncoded = this._encodeScanCodeCombo(scanCodeCombo); + const keyCodeCombosEncoded = this._scanCodeToKeyCode[scanCodeComboEncoded]; + if (!keyCodeCombosEncoded || keyCodeCombosEncoded.length === 0) { + return []; + } + + let result: KeyCodeCombo[] = []; + for (let i = 0, len = keyCodeCombosEncoded.length; i < len; i++) { + const keyCodeComboEncoded = keyCodeCombosEncoded[i]; + + const ctrlKey = (keyCodeComboEncoded & 0b001) ? true : false; + const shiftKey = (keyCodeComboEncoded & 0b010) ? true : false; + const altKey = (keyCodeComboEncoded & 0b100) ? true : false; + const keyCode: KeyCode = (keyCodeComboEncoded >>> 3); + + result[i] = new KeyCodeCombo(ctrlKey, shiftKey, altKey, keyCode); + } + return result; + } + + public guessStableKeyCode(scanCode: ScanCode): KeyCode { + if (scanCode >= ScanCode.Digit1 && scanCode <= ScanCode.Digit0) { + // digits are ok + switch (scanCode) { + case ScanCode.Digit1: return KeyCode.KEY_1; + case ScanCode.Digit2: return KeyCode.KEY_2; + case ScanCode.Digit3: return KeyCode.KEY_3; + case ScanCode.Digit4: return KeyCode.KEY_4; + case ScanCode.Digit5: return KeyCode.KEY_5; + case ScanCode.Digit6: return KeyCode.KEY_6; + case ScanCode.Digit7: return KeyCode.KEY_7; + case ScanCode.Digit8: return KeyCode.KEY_8; + case ScanCode.Digit9: return KeyCode.KEY_9; + case ScanCode.Digit0: return KeyCode.KEY_0; + } + } + + // Lookup the scanCode with and without shift and see if the keyCode is stable + const keyCodeCombos1 = this.lookupScanCodeCombo(new ScanCodeCombo(false, false, false, scanCode)); + const keyCodeCombos2 = this.lookupScanCodeCombo(new ScanCodeCombo(false, true, false, scanCode)); + if (keyCodeCombos1.length === 1 && keyCodeCombos2.length === 1) { + const shiftKey1 = keyCodeCombos1[0].shiftKey; + const keyCode1 = keyCodeCombos1[0].keyCode; + const shiftKey2 = keyCodeCombos2[0].shiftKey; + const keyCode2 = keyCodeCombos2[0].keyCode; + if (keyCode1 === keyCode2 && shiftKey1 !== shiftKey2) { + // This looks like a stable mapping + return keyCode1; + } + } + + return -1; + } + + private _encodeScanCodeCombo(scanCodeCombo: ScanCodeCombo): number { + return this._encode(scanCodeCombo.ctrlKey, scanCodeCombo.shiftKey, scanCodeCombo.altKey, scanCodeCombo.scanCode); + } + + private _encodeKeyCodeCombo(keyCodeCombo: KeyCodeCombo): number { + return this._encode(keyCodeCombo.ctrlKey, keyCodeCombo.shiftKey, keyCodeCombo.altKey, keyCodeCombo.keyCode); + } + + private _encode(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, principal: number): number { + return ( + ((ctrlKey ? 1 : 0) << 0) + | ((shiftKey ? 1 : 0) << 1) + | ((altKey ? 1 : 0) << 2) + | principal << 3 + ) >>> 0; + } +} + +export class MacLinuxKeyboardMapper implements IKeyboardMapper { + + /** + * Is the keyboard type ISO (on Mac) + */ + private readonly _isISOKeyboard: boolean; + /** + * Is this the standard US keyboard layout? + */ + private readonly _isUSStandard: boolean; + /** + * OS (can be Linux or Macintosh) + */ + private readonly _OS: OperatingSystem; + /** + * used only for debug purposes. + */ + private readonly _codeInfo: IMacLinuxKeyMapping[]; + /** + * Maps ScanCode combos <-> KeyCode combos. + */ + private readonly _scanCodeKeyCodeMapper: ScanCodeKeyCodeMapper; + /** + * UI label for a ScanCode. + */ + private readonly _scanCodeToLabel: string[] = []; + /** + * Dispatching string for a ScanCode. + */ + private readonly _scanCodeToDispatch: string[] = []; + + constructor(isISOKeyboard: boolean, isUSStandard: boolean, rawMappings: IMacLinuxKeyboardMapping, OS: OperatingSystem) { + this._isISOKeyboard = isISOKeyboard; + this._isUSStandard = isUSStandard; + this._OS = OS; + this._codeInfo = []; + this._scanCodeKeyCodeMapper = new ScanCodeKeyCodeMapper(); + this._scanCodeToLabel = []; + this._scanCodeToDispatch = []; + + const _registerIfUnknown = ( + hwCtrlKey: 0 | 1, hwShiftKey: 0 | 1, hwAltKey: 0 | 1, scanCode: ScanCode, + kbCtrlKey: 0 | 1, kbShiftKey: 0 | 1, kbAltKey: 0 | 1, keyCode: KeyCode, + ): void => { + this._scanCodeKeyCodeMapper.registerIfUnknown( + new ScanCodeCombo(hwCtrlKey ? true : false, hwShiftKey ? true : false, hwAltKey ? true : false, scanCode), + new KeyCodeCombo(kbCtrlKey ? true : false, kbShiftKey ? true : false, kbAltKey ? true : false, keyCode) + ); + }; + + const _registerAllCombos = (_ctrlKey: 0 | 1, _shiftKey: 0 | 1, _altKey: 0 | 1, scanCode: ScanCode, keyCode: KeyCode): void => { + for (let ctrlKey = _ctrlKey; ctrlKey <= 1; ctrlKey++) { + for (let shiftKey = _shiftKey; shiftKey <= 1; shiftKey++) { + for (let altKey = _altKey; altKey <= 1; altKey++) { + _registerIfUnknown( + ctrlKey, shiftKey, altKey, scanCode, + ctrlKey, shiftKey, altKey, keyCode + ); + } + } + } + }; + + // Initialize `_scanCodeToLabel` + for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) { + this._scanCodeToLabel[scanCode] = null; + } + + // Initialize `_scanCodeToDispatch` + for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) { + this._scanCodeToDispatch[scanCode] = null; + } + + // Handle immutable mappings + for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) { + const keyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode]; + if (keyCode !== -1) { + _registerAllCombos(0, 0, 0, scanCode, keyCode); + this._scanCodeToLabel[scanCode] = KeyCodeUtils.toString(keyCode); + + if (keyCode === KeyCode.Unknown || keyCode === KeyCode.Ctrl || keyCode === KeyCode.Meta || keyCode === KeyCode.Alt || keyCode === KeyCode.Shift) { + this._scanCodeToDispatch[scanCode] = null; // cannot dispatch on this ScanCode + } else { + this._scanCodeToDispatch[scanCode] = `[${ScanCodeUtils.toString(scanCode)}]`; + } + } + } + + // Try to identify keyboard layouts where characters A-Z are missing + // and forcefully map them to their corresponding scan codes if that is the case + const missingLatinLettersOverride: { [scanCode: string]: IMacLinuxKeyMapping; } = {}; + + { + let producesLatinLetter: boolean[] = []; + for (let strScanCode in rawMappings) { + if (rawMappings.hasOwnProperty(strScanCode)) { + const scanCode = ScanCodeUtils.toEnum(strScanCode); + if (scanCode === ScanCode.None) { + continue; + } + if (IMMUTABLE_CODE_TO_KEY_CODE[scanCode] !== -1) { + continue; + } + + const rawMapping = rawMappings[strScanCode]; + const value = MacLinuxKeyboardMapper.getCharCode(rawMapping.value); + + if (value >= CharCode.a && value <= CharCode.z) { + const upperCaseValue = CharCode.A + (value - CharCode.a); + producesLatinLetter[upperCaseValue] = true; + } + } + } + + const _registerLetterIfMissing = (charCode: CharCode, scanCode: ScanCode, value: string, withShift: string): void => { + if (!producesLatinLetter[charCode]) { + missingLatinLettersOverride[ScanCodeUtils.toString(scanCode)] = { + value: value, + withShift: withShift, + withAltGr: '', + withShiftAltGr: '' + }; + } + }; + + // Ensure letters are mapped + _registerLetterIfMissing(CharCode.A, ScanCode.KeyA, 'a', 'A'); + _registerLetterIfMissing(CharCode.B, ScanCode.KeyB, 'b', 'B'); + _registerLetterIfMissing(CharCode.C, ScanCode.KeyC, 'c', 'C'); + _registerLetterIfMissing(CharCode.D, ScanCode.KeyD, 'd', 'D'); + _registerLetterIfMissing(CharCode.E, ScanCode.KeyE, 'e', 'E'); + _registerLetterIfMissing(CharCode.F, ScanCode.KeyF, 'f', 'F'); + _registerLetterIfMissing(CharCode.G, ScanCode.KeyG, 'g', 'G'); + _registerLetterIfMissing(CharCode.H, ScanCode.KeyH, 'h', 'H'); + _registerLetterIfMissing(CharCode.I, ScanCode.KeyI, 'i', 'I'); + _registerLetterIfMissing(CharCode.J, ScanCode.KeyJ, 'j', 'J'); + _registerLetterIfMissing(CharCode.K, ScanCode.KeyK, 'k', 'K'); + _registerLetterIfMissing(CharCode.L, ScanCode.KeyL, 'l', 'L'); + _registerLetterIfMissing(CharCode.M, ScanCode.KeyM, 'm', 'M'); + _registerLetterIfMissing(CharCode.N, ScanCode.KeyN, 'n', 'N'); + _registerLetterIfMissing(CharCode.O, ScanCode.KeyO, 'o', 'O'); + _registerLetterIfMissing(CharCode.P, ScanCode.KeyP, 'p', 'P'); + _registerLetterIfMissing(CharCode.Q, ScanCode.KeyQ, 'q', 'Q'); + _registerLetterIfMissing(CharCode.R, ScanCode.KeyR, 'r', 'R'); + _registerLetterIfMissing(CharCode.S, ScanCode.KeyS, 's', 'S'); + _registerLetterIfMissing(CharCode.T, ScanCode.KeyT, 't', 'T'); + _registerLetterIfMissing(CharCode.U, ScanCode.KeyU, 'u', 'U'); + _registerLetterIfMissing(CharCode.V, ScanCode.KeyV, 'v', 'V'); + _registerLetterIfMissing(CharCode.W, ScanCode.KeyW, 'w', 'W'); + _registerLetterIfMissing(CharCode.X, ScanCode.KeyX, 'x', 'X'); + _registerLetterIfMissing(CharCode.Y, ScanCode.KeyY, 'y', 'Y'); + _registerLetterIfMissing(CharCode.Z, ScanCode.KeyZ, 'z', 'Z'); + } + + let mappings: IScanCodeMapping[] = [], mappingsLen = 0; + for (let strScanCode in rawMappings) { + if (rawMappings.hasOwnProperty(strScanCode)) { + const scanCode = ScanCodeUtils.toEnum(strScanCode); + if (scanCode === ScanCode.None) { + continue; + } + if (IMMUTABLE_CODE_TO_KEY_CODE[scanCode] !== -1) { + continue; + } + + this._codeInfo[scanCode] = rawMappings[strScanCode]; + + const rawMapping = missingLatinLettersOverride[strScanCode] || rawMappings[strScanCode]; + const value = MacLinuxKeyboardMapper.getCharCode(rawMapping.value); + const withShift = MacLinuxKeyboardMapper.getCharCode(rawMapping.withShift); + const withAltGr = MacLinuxKeyboardMapper.getCharCode(rawMapping.withAltGr); + const withShiftAltGr = MacLinuxKeyboardMapper.getCharCode(rawMapping.withShiftAltGr); + + const mapping: IScanCodeMapping = { + scanCode: scanCode, + value: value, + withShift: withShift, + withAltGr: withAltGr, + withShiftAltGr: withShiftAltGr, + }; + mappings[mappingsLen++] = mapping; + + this._scanCodeToDispatch[scanCode] = `[${ScanCodeUtils.toString(scanCode)}]`; + + if (value >= CharCode.a && value <= CharCode.z) { + const upperCaseValue = CharCode.A + (value - CharCode.a); + this._scanCodeToLabel[scanCode] = String.fromCharCode(upperCaseValue); + } else if (value >= CharCode.A && value <= CharCode.Z) { + this._scanCodeToLabel[scanCode] = String.fromCharCode(value); + } else if (value) { + this._scanCodeToLabel[scanCode] = String.fromCharCode(value); + } else { + this._scanCodeToLabel[scanCode] = null; + } + } + } + + // Handle all `withShiftAltGr` entries + for (let i = mappings.length - 1; i >= 0; i--) { + const mapping = mappings[i]; + const scanCode = mapping.scanCode; + const withShiftAltGr = mapping.withShiftAltGr; + if (withShiftAltGr === mapping.withAltGr || withShiftAltGr === mapping.withShift || withShiftAltGr === mapping.value) { + // handled below + continue; + } + const kb = MacLinuxKeyboardMapper._charCodeToKb(withShiftAltGr); + if (!kb) { + continue; + } + const kbShiftKey = kb.shiftKey; + const keyCode = kb.keyCode; + + if (kbShiftKey) { + // Ctrl+Shift+Alt+ScanCode => Shift+KeyCode + _registerIfUnknown(1, 1, 1, scanCode, 0, 1, 0, keyCode); // Ctrl+Alt+ScanCode => Shift+KeyCode + } else { + // Ctrl+Shift+Alt+ScanCode => KeyCode + _registerIfUnknown(1, 1, 1, scanCode, 0, 0, 0, keyCode); // Ctrl+Alt+ScanCode => KeyCode + } + } + // Handle all `withAltGr` entries + for (let i = mappings.length - 1; i >= 0; i--) { + const mapping = mappings[i]; + const scanCode = mapping.scanCode; + const withAltGr = mapping.withAltGr; + if (withAltGr === mapping.withShift || withAltGr === mapping.value) { + // handled below + continue; + } + const kb = MacLinuxKeyboardMapper._charCodeToKb(withAltGr); + if (!kb) { + continue; + } + const kbShiftKey = kb.shiftKey; + const keyCode = kb.keyCode; + + if (kbShiftKey) { + // Ctrl+Alt+ScanCode => Shift+KeyCode + _registerIfUnknown(1, 0, 1, scanCode, 0, 1, 0, keyCode); // Ctrl+Alt+ScanCode => Shift+KeyCode + } else { + // Ctrl+Alt+ScanCode => KeyCode + _registerIfUnknown(1, 0, 1, scanCode, 0, 0, 0, keyCode); // Ctrl+Alt+ScanCode => KeyCode + } + } + // Handle all `withShift` entries + for (let i = mappings.length - 1; i >= 0; i--) { + const mapping = mappings[i]; + const scanCode = mapping.scanCode; + const withShift = mapping.withShift; + if (withShift === mapping.value) { + // handled below + continue; + } + const kb = MacLinuxKeyboardMapper._charCodeToKb(withShift); + if (!kb) { + continue; + } + const kbShiftKey = kb.shiftKey; + const keyCode = kb.keyCode; + + if (kbShiftKey) { + // Shift+ScanCode => Shift+KeyCode + _registerIfUnknown(0, 1, 0, scanCode, 0, 1, 0, keyCode); // Shift+ScanCode => Shift+KeyCode + _registerIfUnknown(0, 1, 1, scanCode, 0, 1, 1, keyCode); // Shift+Alt+ScanCode => Shift+Alt+KeyCode + _registerIfUnknown(1, 1, 0, scanCode, 1, 1, 0, keyCode); // Ctrl+Shift+ScanCode => Ctrl+Shift+KeyCode + _registerIfUnknown(1, 1, 1, scanCode, 1, 1, 1, keyCode); // Ctrl+Shift+Alt+ScanCode => Ctrl+Shift+Alt+KeyCode + } else { + // Shift+ScanCode => KeyCode + _registerIfUnknown(0, 1, 0, scanCode, 0, 0, 0, keyCode); // Shift+ScanCode => KeyCode + _registerIfUnknown(0, 1, 0, scanCode, 0, 1, 0, keyCode); // Shift+ScanCode => Shift+KeyCode + _registerIfUnknown(0, 1, 1, scanCode, 0, 0, 1, keyCode); // Shift+Alt+ScanCode => Alt+KeyCode + _registerIfUnknown(0, 1, 1, scanCode, 0, 1, 1, keyCode); // Shift+Alt+ScanCode => Shift+Alt+KeyCode + _registerIfUnknown(1, 1, 0, scanCode, 1, 0, 0, keyCode); // Ctrl+Shift+ScanCode => Ctrl+KeyCode + _registerIfUnknown(1, 1, 0, scanCode, 1, 1, 0, keyCode); // Ctrl+Shift+ScanCode => Ctrl+Shift+KeyCode + _registerIfUnknown(1, 1, 1, scanCode, 1, 0, 1, keyCode); // Ctrl+Shift+Alt+ScanCode => Ctrl+Alt+KeyCode + _registerIfUnknown(1, 1, 1, scanCode, 1, 1, 1, keyCode); // Ctrl+Shift+Alt+ScanCode => Ctrl+Shift+Alt+KeyCode + } + } + // Handle all `value` entries + for (let i = mappings.length - 1; i >= 0; i--) { + const mapping = mappings[i]; + const scanCode = mapping.scanCode; + const kb = MacLinuxKeyboardMapper._charCodeToKb(mapping.value); + if (!kb) { + continue; + } + const kbShiftKey = kb.shiftKey; + const keyCode = kb.keyCode; + + if (kbShiftKey) { + // ScanCode => Shift+KeyCode + _registerIfUnknown(0, 0, 0, scanCode, 0, 1, 0, keyCode); // ScanCode => Shift+KeyCode + _registerIfUnknown(0, 0, 1, scanCode, 0, 1, 1, keyCode); // Alt+ScanCode => Shift+Alt+KeyCode + _registerIfUnknown(1, 0, 0, scanCode, 1, 1, 0, keyCode); // Ctrl+ScanCode => Ctrl+Shift+KeyCode + _registerIfUnknown(1, 0, 1, scanCode, 1, 1, 1, keyCode); // Ctrl+Alt+ScanCode => Ctrl+Shift+Alt+KeyCode + } else { + // ScanCode => KeyCode + _registerIfUnknown(0, 0, 0, scanCode, 0, 0, 0, keyCode); // ScanCode => KeyCode + _registerIfUnknown(0, 0, 1, scanCode, 0, 0, 1, keyCode); // Alt+ScanCode => Alt+KeyCode + _registerIfUnknown(0, 1, 0, scanCode, 0, 1, 0, keyCode); // Shift+ScanCode => Shift+KeyCode + _registerIfUnknown(0, 1, 1, scanCode, 0, 1, 1, keyCode); // Shift+Alt+ScanCode => Shift+Alt+KeyCode + _registerIfUnknown(1, 0, 0, scanCode, 1, 0, 0, keyCode); // Ctrl+ScanCode => Ctrl+KeyCode + _registerIfUnknown(1, 0, 1, scanCode, 1, 0, 1, keyCode); // Ctrl+Alt+ScanCode => Ctrl+Alt+KeyCode + _registerIfUnknown(1, 1, 0, scanCode, 1, 1, 0, keyCode); // Ctrl+Shift+ScanCode => Ctrl+Shift+KeyCode + _registerIfUnknown(1, 1, 1, scanCode, 1, 1, 1, keyCode); // Ctrl+Shift+Alt+ScanCode => Ctrl+Shift+Alt+KeyCode + } + } + // Handle all left-over available digits + _registerAllCombos(0, 0, 0, ScanCode.Digit1, KeyCode.KEY_1); + _registerAllCombos(0, 0, 0, ScanCode.Digit2, KeyCode.KEY_2); + _registerAllCombos(0, 0, 0, ScanCode.Digit3, KeyCode.KEY_3); + _registerAllCombos(0, 0, 0, ScanCode.Digit4, KeyCode.KEY_4); + _registerAllCombos(0, 0, 0, ScanCode.Digit5, KeyCode.KEY_5); + _registerAllCombos(0, 0, 0, ScanCode.Digit6, KeyCode.KEY_6); + _registerAllCombos(0, 0, 0, ScanCode.Digit7, KeyCode.KEY_7); + _registerAllCombos(0, 0, 0, ScanCode.Digit8, KeyCode.KEY_8); + _registerAllCombos(0, 0, 0, ScanCode.Digit9, KeyCode.KEY_9); + _registerAllCombos(0, 0, 0, ScanCode.Digit0, KeyCode.KEY_0); + + this._scanCodeKeyCodeMapper.registrationComplete(); + } + + public dumpDebugInfo(): string { + let result: string[] = []; + + let immutableSamples = [ + ScanCode.ArrowUp, + ScanCode.Numpad0 + ]; + + let cnt = 0; + result.push(`isUSStandard: ${this._isUSStandard}`); + result.push(`----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------`); + for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) { + if (IMMUTABLE_CODE_TO_KEY_CODE[scanCode] !== -1) { + if (immutableSamples.indexOf(scanCode) === -1) { + continue; + } + } + + if (cnt % 4 === 0) { + result.push(`| HW Code combination | Key | KeyCode combination | Pri | UI label | User settings | Electron accelerator | Dispatching string | WYSIWYG |`); + result.push(`----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------`); + } + cnt++; + + const mapping = this._codeInfo[scanCode]; + + for (let mod = 0; mod < 8; mod++) { + const hwCtrlKey = (mod & 0b001) ? true : false; + const hwShiftKey = (mod & 0b010) ? true : false; + const hwAltKey = (mod & 0b100) ? true : false; + const scanCodeCombo = new ScanCodeCombo(hwCtrlKey, hwShiftKey, hwAltKey, scanCode); + const resolvedKb = this.resolveKeyboardEvent({ + ctrlKey: scanCodeCombo.ctrlKey, + shiftKey: scanCodeCombo.shiftKey, + altKey: scanCodeCombo.altKey, + metaKey: false, + keyCode: -1, + code: ScanCodeUtils.toString(scanCode) + }); + + const outScanCodeCombo = scanCodeCombo.toString(); + const outKey = scanCodeCombo.getProducedChar(mapping); + const ariaLabel = resolvedKb.getAriaLabel(); + const outUILabel = (ariaLabel ? ariaLabel.replace(/Control\+/, 'Ctrl+') : null); + const outUserSettings = resolvedKb.getUserSettingsLabel(); + const outElectronAccelerator = resolvedKb.getElectronAccelerator(); + const outDispatchStr = resolvedKb.getDispatchParts()[0]; + + const isWYSIWYG = (resolvedKb ? resolvedKb.isWYSIWYG() : false); + const outWYSIWYG = (isWYSIWYG ? ' ' : ' NO '); + + const kbCombos = this._scanCodeKeyCodeMapper.lookupScanCodeCombo(scanCodeCombo); + if (kbCombos.length === 0) { + result.push(`| ${this._leftPad(outScanCodeCombo, 30)} | ${outKey} | ${this._leftPad('', 25)} | ${this._leftPad('', 3)} | ${this._leftPad(outUILabel, 25)} | ${this._leftPad(outUserSettings, 30)} | ${this._leftPad(outElectronAccelerator, 25)} | ${this._leftPad(outDispatchStr, 30)} | ${outWYSIWYG} |`); + } else { + for (let i = 0, len = kbCombos.length; i < len; i++) { + const kbCombo = kbCombos[i]; + // find out the priority of this scan code for this key code + let colPriority = '-'; + + const scanCodeCombos = this._scanCodeKeyCodeMapper.lookupKeyCodeCombo(kbCombo); + if (scanCodeCombos.length === 1) { + // no need for priority, this key code combo maps to precisely this scan code combo + colPriority = ''; + } else { + let priority = -1; + for (let j = 0; j < scanCodeCombos.length; j++) { + if (scanCodeCombos[j].equals(scanCodeCombo)) { + priority = j + 1; + break; + } + } + colPriority = String(priority); + } + + const outKeybinding = kbCombo.toString(); + if (i === 0) { + result.push(`| ${this._leftPad(outScanCodeCombo, 30)} | ${outKey} | ${this._leftPad(outKeybinding, 25)} | ${this._leftPad(colPriority, 3)} | ${this._leftPad(outUILabel, 25)} | ${this._leftPad(outUserSettings, 30)} | ${this._leftPad(outElectronAccelerator, 25)} | ${this._leftPad(outDispatchStr, 30)} | ${outWYSIWYG} |`); + } else { + // secondary keybindings + result.push(`| ${this._leftPad('', 30)} | | ${this._leftPad(outKeybinding, 25)} | ${this._leftPad(colPriority, 3)} | ${this._leftPad('', 25)} | ${this._leftPad('', 30)} | ${this._leftPad('', 25)} | ${this._leftPad('', 30)} | |`); + } + } + } + + } + result.push(`----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------`); + } + + return result.join('\n'); + } + + private _leftPad(str: string, cnt: number): string { + if (str === null) { + str = 'null'; + } + while (str.length < cnt) { + str = ' ' + str; + } + return str; + } + + public simpleKeybindingToScanCodeBinding(keybinding: SimpleKeybinding): ScanCodeBinding[] { + // Avoid double Enter bindings (both ScanCode.NumpadEnter and ScanCode.Enter point to KeyCode.Enter) + if (keybinding.keyCode === KeyCode.Enter) { + return [new ScanCodeBinding(keybinding.ctrlKey, keybinding.shiftKey, keybinding.altKey, keybinding.metaKey, ScanCode.Enter)]; + } + + const scanCodeCombos = this._scanCodeKeyCodeMapper.lookupKeyCodeCombo( + new KeyCodeCombo(keybinding.ctrlKey, keybinding.shiftKey, keybinding.altKey, keybinding.keyCode) + ); + + let result: ScanCodeBinding[] = []; + for (let i = 0, len = scanCodeCombos.length; i < len; i++) { + const scanCodeCombo = scanCodeCombos[i]; + result[i] = new ScanCodeBinding(scanCodeCombo.ctrlKey, scanCodeCombo.shiftKey, scanCodeCombo.altKey, keybinding.metaKey, scanCodeCombo.scanCode); + } + return result; + } + + public getUILabelForScanCodeBinding(binding: ScanCodeBinding): string { + if (!binding) { + return null; + } + if (binding.isDuplicateModifierCase()) { + return ''; + } + if (this._OS === OperatingSystem.Macintosh) { + switch (binding.scanCode) { + case ScanCode.ArrowLeft: + return '←'; + case ScanCode.ArrowUp: + return '↑'; + case ScanCode.ArrowRight: + return '→'; + case ScanCode.ArrowDown: + return '↓'; + } + } + return this._scanCodeToLabel[binding.scanCode]; + } + + public getAriaLabelForScanCodeBinding(binding: ScanCodeBinding): string { + if (!binding) { + return null; + } + if (binding.isDuplicateModifierCase()) { + return ''; + } + return this._scanCodeToLabel[binding.scanCode]; + } + + public getDispatchStrForScanCodeBinding(keypress: ScanCodeBinding): string { + const codeDispatch = this._scanCodeToDispatch[keypress.scanCode]; + if (!codeDispatch) { + return null; + } + let result = ''; + + if (keypress.ctrlKey) { + result += 'ctrl+'; + } + if (keypress.shiftKey) { + result += 'shift+'; + } + if (keypress.altKey) { + result += 'alt+'; + } + if (keypress.metaKey) { + result += 'meta+'; + } + result += codeDispatch; + + return result; + } + + public getUserSettingsLabelForScanCodeBinding(binding: ScanCodeBinding): string { + if (!binding) { + return null; + } + if (binding.isDuplicateModifierCase()) { + return ''; + } + + const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[binding.scanCode]; + if (immutableKeyCode !== -1) { + return KeyCodeUtils.toUserSettingsUS(immutableKeyCode).toLowerCase(); + } + + // Check if this scanCode always maps to the same keyCode and back + let constantKeyCode: KeyCode = this._scanCodeKeyCodeMapper.guessStableKeyCode(binding.scanCode); + if (constantKeyCode !== -1) { + // Verify that this is a good key code that can be mapped back to the same scan code + let reverseBindings = this.simpleKeybindingToScanCodeBinding(new SimpleKeybinding(binding.ctrlKey, binding.shiftKey, binding.altKey, binding.metaKey, constantKeyCode)); + for (let i = 0, len = reverseBindings.length; i < len; i++) { + const reverseBinding = reverseBindings[i]; + if (reverseBinding.scanCode === binding.scanCode) { + return KeyCodeUtils.toUserSettingsUS(constantKeyCode).toLowerCase(); + } + } + } + + return this._scanCodeToDispatch[binding.scanCode]; + } + + private _getElectronLabelForKeyCode(keyCode: KeyCode): string { + if (keyCode >= KeyCode.NUMPAD_0 && keyCode <= KeyCode.NUMPAD_DIVIDE) { + // Electron cannot handle numpad keys + return null; + } + + switch (keyCode) { + case KeyCode.UpArrow: + return 'Up'; + case KeyCode.DownArrow: + return 'Down'; + case KeyCode.LeftArrow: + return 'Left'; + case KeyCode.RightArrow: + return 'Right'; + } + + // electron menus always do the correct rendering on Windows + return KeyCodeUtils.toString(keyCode); + } + + public getElectronAcceleratorLabelForScanCodeBinding(binding: ScanCodeBinding): string { + if (!binding) { + return null; + } + if (binding.isDuplicateModifierCase()) { + return null; + } + + const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[binding.scanCode]; + if (immutableKeyCode !== -1) { + return this._getElectronLabelForKeyCode(immutableKeyCode); + } + + // Check if this scanCode always maps to the same keyCode and back + const constantKeyCode: KeyCode = this._scanCodeKeyCodeMapper.guessStableKeyCode(binding.scanCode); + + if (!this._isUSStandard) { + // Electron cannot handle these key codes on anything else than standard US + const isOEMKey = ( + constantKeyCode === KeyCode.US_SEMICOLON + || constantKeyCode === KeyCode.US_EQUAL + || constantKeyCode === KeyCode.US_COMMA + || constantKeyCode === KeyCode.US_MINUS + || constantKeyCode === KeyCode.US_DOT + || constantKeyCode === KeyCode.US_SLASH + || constantKeyCode === KeyCode.US_BACKTICK + || constantKeyCode === KeyCode.US_OPEN_SQUARE_BRACKET + || constantKeyCode === KeyCode.US_BACKSLASH + || constantKeyCode === KeyCode.US_CLOSE_SQUARE_BRACKET + ); + + if (isOEMKey) { + return null; + } + } + + if (constantKeyCode !== -1) { + return this._getElectronLabelForKeyCode(constantKeyCode); + } + + return null; + } + + public resolveKeybinding(keybinding: Keybinding): NativeResolvedKeybinding[] { + let result: NativeResolvedKeybinding[] = [], resultLen = 0; + + if (keybinding.type === KeybindingType.Chord) { + const firstParts = this.simpleKeybindingToScanCodeBinding(keybinding.firstPart); + const chordParts = this.simpleKeybindingToScanCodeBinding(keybinding.chordPart); + + for (let i = 0, len = firstParts.length; i < len; i++) { + const firstPart = firstParts[i]; + for (let j = 0, lenJ = chordParts.length; j < lenJ; j++) { + const chordPart = chordParts[j]; + + result[resultLen++] = new NativeResolvedKeybinding(this, this._OS, firstPart, chordPart); + } + } + } else { + const firstParts = this.simpleKeybindingToScanCodeBinding(keybinding); + + for (let i = 0, len = firstParts.length; i < len; i++) { + const firstPart = firstParts[i]; + + result[resultLen++] = new NativeResolvedKeybinding(this, this._OS, firstPart, null); + } + } + + return result; + } + + public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): NativeResolvedKeybinding { + let code = ScanCodeUtils.toEnum(keyboardEvent.code); + + // Treat NumpadEnter as Enter + if (code === ScanCode.NumpadEnter) { + code = ScanCode.Enter; + } + + if (this._OS === OperatingSystem.Macintosh && this._isISOKeyboard) { + // See https://github.com/Microsoft/vscode/issues/24153 + // On OSX, on ISO keyboards, Chromium swaps the scan codes + // of IntlBackslash and Backquote. + + switch (code) { + case ScanCode.IntlBackslash: + code = ScanCode.Backquote; + break; + case ScanCode.Backquote: + code = ScanCode.IntlBackslash; + break; + } + } + + const keyCode = keyboardEvent.keyCode; + + if ( + (keyCode === KeyCode.LeftArrow) + || (keyCode === KeyCode.UpArrow) + || (keyCode === KeyCode.RightArrow) + || (keyCode === KeyCode.DownArrow) + || (keyCode === KeyCode.Delete) + || (keyCode === KeyCode.Insert) + || (keyCode === KeyCode.Home) + || (keyCode === KeyCode.End) + || (keyCode === KeyCode.PageDown) + || (keyCode === KeyCode.PageUp) + ) { + // "Dispatch" on keyCode for these key codes to workaround issues with remote desktoping software + // where the scan codes appear to be incorrect (see https://github.com/Microsoft/vscode/issues/24107) + const immutableScanCode = IMMUTABLE_KEY_CODE_TO_CODE[keyCode]; + if (immutableScanCode !== -1) { + code = immutableScanCode; + } + + } else { + + if ( + (code === ScanCode.Numpad1) + || (code === ScanCode.Numpad2) + || (code === ScanCode.Numpad3) + || (code === ScanCode.Numpad4) + || (code === ScanCode.Numpad5) + || (code === ScanCode.Numpad6) + || (code === ScanCode.Numpad7) + || (code === ScanCode.Numpad8) + || (code === ScanCode.Numpad9) + || (code === ScanCode.Numpad0) + || (code === ScanCode.NumpadDecimal) + ) { + // "Dispatch" on keyCode for all numpad keys in order for NumLock to work correctly + if (keyCode >= 0) { + const immutableScanCode = IMMUTABLE_KEY_CODE_TO_CODE[keyCode]; + if (immutableScanCode !== -1) { + code = immutableScanCode; + } + } + } + } + + const keypress = new ScanCodeBinding(keyboardEvent.ctrlKey, keyboardEvent.shiftKey, keyboardEvent.altKey, keyboardEvent.metaKey, code); + return new NativeResolvedKeybinding(this, this._OS, keypress, null); + } + + private _resolveSimpleUserBinding(binding: SimpleKeybinding | ScanCodeBinding): ScanCodeBinding[] { + if (!binding) { + return []; + } + if (binding instanceof ScanCodeBinding) { + return [binding]; + } + return this.simpleKeybindingToScanCodeBinding(binding); + } + + public resolveUserBinding(_firstPart: SimpleKeybinding | ScanCodeBinding, _chordPart: SimpleKeybinding | ScanCodeBinding): ResolvedKeybinding[] { + const firstParts = this._resolveSimpleUserBinding(_firstPart); + const chordParts = this._resolveSimpleUserBinding(_chordPart); + + let result: NativeResolvedKeybinding[] = [], resultLen = 0; + for (let i = 0, len = firstParts.length; i < len; i++) { + const firstPart = firstParts[i]; + if (_chordPart) { + for (let j = 0, lenJ = chordParts.length; j < lenJ; j++) { + const chordPart = chordParts[j]; + + result[resultLen++] = new NativeResolvedKeybinding(this, this._OS, firstPart, chordPart); + } + } else { + result[resultLen++] = new NativeResolvedKeybinding(this, this._OS, firstPart, null); + } + } + return result; + } + + private static _charCodeToKb(charCode: number): { keyCode: KeyCode; shiftKey: boolean } { + if (charCode < CHAR_CODE_TO_KEY_CODE.length) { + return CHAR_CODE_TO_KEY_CODE[charCode]; + } + return null; + } + + /** + * Attempt to map a combining character to a regular one that renders the same way. + * + * To the brave person following me: Good Luck! + * https://www.compart.com/en/unicode/bidiclass/NSM + */ + public static getCharCode(char: string): number { + if (char.length === 0) { + return 0; + } + const charCode = char.charCodeAt(0); + switch (charCode) { + case CharCode.U_Combining_Grave_Accent: return CharCode.U_GRAVE_ACCENT; + case CharCode.U_Combining_Acute_Accent: return CharCode.U_ACUTE_ACCENT; + case CharCode.U_Combining_Circumflex_Accent: return CharCode.U_CIRCUMFLEX; + case CharCode.U_Combining_Tilde: return CharCode.U_SMALL_TILDE; + case CharCode.U_Combining_Macron: return CharCode.U_MACRON; + case CharCode.U_Combining_Overline: return CharCode.U_OVERLINE; + case CharCode.U_Combining_Breve: return CharCode.U_BREVE; + case CharCode.U_Combining_Dot_Above: return CharCode.U_DOT_ABOVE; + case CharCode.U_Combining_Diaeresis: return CharCode.U_DIAERESIS; + case CharCode.U_Combining_Ring_Above: return CharCode.U_RING_ABOVE; + case CharCode.U_Combining_Double_Acute_Accent: return CharCode.U_DOUBLE_ACUTE_ACCENT; + } + return charCode; + } +} + +(function () { + function define(charCode: number, keyCode: KeyCode, shiftKey: boolean): void { + for (let i = CHAR_CODE_TO_KEY_CODE.length; i < charCode; i++) { + CHAR_CODE_TO_KEY_CODE[i] = null; + } + CHAR_CODE_TO_KEY_CODE[charCode] = { keyCode: keyCode, shiftKey: shiftKey }; + } + + for (let chCode = CharCode.A; chCode <= CharCode.Z; chCode++) { + define(chCode, KeyCode.KEY_A + (chCode - CharCode.A), true); + } + + for (let chCode = CharCode.a; chCode <= CharCode.z; chCode++) { + define(chCode, KeyCode.KEY_A + (chCode - CharCode.a), false); + } + + define(CharCode.Semicolon, KeyCode.US_SEMICOLON, false); + define(CharCode.Colon, KeyCode.US_SEMICOLON, true); + + define(CharCode.Equals, KeyCode.US_EQUAL, false); + define(CharCode.Plus, KeyCode.US_EQUAL, true); + + define(CharCode.Comma, KeyCode.US_COMMA, false); + define(CharCode.LessThan, KeyCode.US_COMMA, true); + + define(CharCode.Dash, KeyCode.US_MINUS, false); + define(CharCode.Underline, KeyCode.US_MINUS, true); + + define(CharCode.Period, KeyCode.US_DOT, false); + define(CharCode.GreaterThan, KeyCode.US_DOT, true); + + define(CharCode.Slash, KeyCode.US_SLASH, false); + define(CharCode.QuestionMark, KeyCode.US_SLASH, true); + + define(CharCode.BackTick, KeyCode.US_BACKTICK, false); + define(CharCode.Tilde, KeyCode.US_BACKTICK, true); + + define(CharCode.OpenSquareBracket, KeyCode.US_OPEN_SQUARE_BRACKET, false); + define(CharCode.OpenCurlyBrace, KeyCode.US_OPEN_SQUARE_BRACKET, true); + + define(CharCode.Backslash, KeyCode.US_BACKSLASH, false); + define(CharCode.Pipe, KeyCode.US_BACKSLASH, true); + + define(CharCode.CloseSquareBracket, KeyCode.US_CLOSE_SQUARE_BRACKET, false); + define(CharCode.CloseCurlyBrace, KeyCode.US_CLOSE_SQUARE_BRACKET, true); + + define(CharCode.SingleQuote, KeyCode.US_QUOTE, false); + define(CharCode.DoubleQuote, KeyCode.US_QUOTE, true); +})(); diff --git a/src/vs/workbench/services/keybinding/common/scanCode.ts b/src/vs/workbench/services/keybinding/common/scanCode.ts new file mode 100644 index 0000000000..42b62bfc03 --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/scanCode.ts @@ -0,0 +1,691 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { KeyCode } from 'vs/base/common/keyCodes'; + +/** + * keyboardEvent.code + */ +export const enum ScanCode { + None, + + Hyper, + Super, + Fn, + FnLock, + Suspend, + Resume, + Turbo, + Sleep, + WakeUp, + KeyA, + KeyB, + KeyC, + KeyD, + KeyE, + KeyF, + KeyG, + KeyH, + KeyI, + KeyJ, + KeyK, + KeyL, + KeyM, + KeyN, + KeyO, + KeyP, + KeyQ, + KeyR, + KeyS, + KeyT, + KeyU, + KeyV, + KeyW, + KeyX, + KeyY, + KeyZ, + Digit1, + Digit2, + Digit3, + Digit4, + Digit5, + Digit6, + Digit7, + Digit8, + Digit9, + Digit0, + Enter, + Escape, + Backspace, + Tab, + Space, + Minus, + Equal, + BracketLeft, + BracketRight, + Backslash, + IntlHash, + Semicolon, + Quote, + Backquote, + Comma, + Period, + Slash, + CapsLock, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + PrintScreen, + ScrollLock, + Pause, + Insert, + Home, + PageUp, + Delete, + End, + PageDown, + ArrowRight, + ArrowLeft, + ArrowDown, + ArrowUp, + NumLock, + NumpadDivide, + NumpadMultiply, + NumpadSubtract, + NumpadAdd, + NumpadEnter, + Numpad1, + Numpad2, + Numpad3, + Numpad4, + Numpad5, + Numpad6, + Numpad7, + Numpad8, + Numpad9, + Numpad0, + NumpadDecimal, + IntlBackslash, + ContextMenu, + Power, + NumpadEqual, + F13, + F14, + F15, + F16, + F17, + F18, + F19, + F20, + F21, + F22, + F23, + F24, + Open, + Help, + Select, + Again, + Undo, + Cut, + Copy, + Paste, + Find, + AudioVolumeMute, + AudioVolumeUp, + AudioVolumeDown, + NumpadComma, + IntlRo, + KanaMode, + IntlYen, + Convert, + NonConvert, + Lang1, + Lang2, + Lang3, + Lang4, + Lang5, + Abort, + Props, + NumpadParenLeft, + NumpadParenRight, + NumpadBackspace, + NumpadMemoryStore, + NumpadMemoryRecall, + NumpadMemoryClear, + NumpadMemoryAdd, + NumpadMemorySubtract, + NumpadClear, + NumpadClearEntry, + ControlLeft, + ShiftLeft, + AltLeft, + MetaLeft, + ControlRight, + ShiftRight, + AltRight, + MetaRight, + BrightnessUp, + BrightnessDown, + MediaPlay, + MediaRecord, + MediaFastForward, + MediaRewind, + MediaTrackNext, + MediaTrackPrevious, + MediaStop, + Eject, + MediaPlayPause, + MediaSelect, + LaunchMail, + LaunchApp2, + LaunchApp1, + SelectTask, + LaunchScreenSaver, + BrowserSearch, + BrowserHome, + BrowserBack, + BrowserForward, + BrowserStop, + BrowserRefresh, + BrowserFavorites, + ZoomToggle, + MailReply, + MailForward, + MailSend, + + MAX_VALUE +} + +const scanCodeIntToStr: string[] = []; +const scanCodeStrToInt: { [code: string]: number; } = Object.create(null); +const scanCodeLowerCaseStrToInt: { [code: string]: number; } = Object.create(null); + +export const ScanCodeUtils = { + lowerCaseToEnum: (scanCode: string) => scanCodeLowerCaseStrToInt[scanCode] || ScanCode.None, + toEnum: (scanCode: string) => scanCodeStrToInt[scanCode] || ScanCode.None, + toString: (scanCode: ScanCode) => scanCodeIntToStr[scanCode] || 'None' +}; + +/** + * -1 if a ScanCode => KeyCode mapping depends on kb layout. + */ +export const IMMUTABLE_CODE_TO_KEY_CODE: KeyCode[] = []; + +/** + * -1 if a KeyCode => ScanCode mapping depends on kb layout. + */ +export const IMMUTABLE_KEY_CODE_TO_CODE: ScanCode[] = []; + +export class ScanCodeBinding { + public readonly ctrlKey: boolean; + public readonly shiftKey: boolean; + public readonly altKey: boolean; + public readonly metaKey: boolean; + public readonly scanCode: ScanCode; + + constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, scanCode: ScanCode) { + this.ctrlKey = ctrlKey; + this.shiftKey = shiftKey; + this.altKey = altKey; + this.metaKey = metaKey; + this.scanCode = scanCode; + } + + public equals(other: ScanCodeBinding): boolean { + return ( + this.ctrlKey === other.ctrlKey + && this.shiftKey === other.shiftKey + && this.altKey === other.altKey + && this.metaKey === other.metaKey + && this.scanCode === other.scanCode + ); + } + + /** + * Does this keybinding refer to the key code of a modifier and it also has the modifier flag? + */ + public isDuplicateModifierCase(): boolean { + return ( + (this.ctrlKey && (this.scanCode === ScanCode.ControlLeft || this.scanCode === ScanCode.ControlRight)) + || (this.shiftKey && (this.scanCode === ScanCode.ShiftLeft || this.scanCode === ScanCode.ShiftRight)) + || (this.altKey && (this.scanCode === ScanCode.AltLeft || this.scanCode === ScanCode.AltRight)) + || (this.metaKey && (this.scanCode === ScanCode.MetaLeft || this.scanCode === ScanCode.MetaRight)) + ); + } +} + +(function () { + function d(intScanCode: ScanCode, strScanCode: string): void { + scanCodeIntToStr[intScanCode] = strScanCode; + scanCodeStrToInt[strScanCode] = intScanCode; + scanCodeLowerCaseStrToInt[strScanCode.toLowerCase()] = intScanCode; + } + d(ScanCode.None, 'None'); + d(ScanCode.Hyper, 'Hyper'); + d(ScanCode.Super, 'Super'); + d(ScanCode.Fn, 'Fn'); + d(ScanCode.FnLock, 'FnLock'); + d(ScanCode.Suspend, 'Suspend'); + d(ScanCode.Resume, 'Resume'); + d(ScanCode.Turbo, 'Turbo'); + d(ScanCode.Sleep, 'Sleep'); + d(ScanCode.WakeUp, 'WakeUp'); + d(ScanCode.KeyA, 'KeyA'); + d(ScanCode.KeyB, 'KeyB'); + d(ScanCode.KeyC, 'KeyC'); + d(ScanCode.KeyD, 'KeyD'); + d(ScanCode.KeyE, 'KeyE'); + d(ScanCode.KeyF, 'KeyF'); + d(ScanCode.KeyG, 'KeyG'); + d(ScanCode.KeyH, 'KeyH'); + d(ScanCode.KeyI, 'KeyI'); + d(ScanCode.KeyJ, 'KeyJ'); + d(ScanCode.KeyK, 'KeyK'); + d(ScanCode.KeyL, 'KeyL'); + d(ScanCode.KeyM, 'KeyM'); + d(ScanCode.KeyN, 'KeyN'); + d(ScanCode.KeyO, 'KeyO'); + d(ScanCode.KeyP, 'KeyP'); + d(ScanCode.KeyQ, 'KeyQ'); + d(ScanCode.KeyR, 'KeyR'); + d(ScanCode.KeyS, 'KeyS'); + d(ScanCode.KeyT, 'KeyT'); + d(ScanCode.KeyU, 'KeyU'); + d(ScanCode.KeyV, 'KeyV'); + d(ScanCode.KeyW, 'KeyW'); + d(ScanCode.KeyX, 'KeyX'); + d(ScanCode.KeyY, 'KeyY'); + d(ScanCode.KeyZ, 'KeyZ'); + d(ScanCode.Digit1, 'Digit1'); + d(ScanCode.Digit2, 'Digit2'); + d(ScanCode.Digit3, 'Digit3'); + d(ScanCode.Digit4, 'Digit4'); + d(ScanCode.Digit5, 'Digit5'); + d(ScanCode.Digit6, 'Digit6'); + d(ScanCode.Digit7, 'Digit7'); + d(ScanCode.Digit8, 'Digit8'); + d(ScanCode.Digit9, 'Digit9'); + d(ScanCode.Digit0, 'Digit0'); + d(ScanCode.Enter, 'Enter'); + d(ScanCode.Escape, 'Escape'); + d(ScanCode.Backspace, 'Backspace'); + d(ScanCode.Tab, 'Tab'); + d(ScanCode.Space, 'Space'); + d(ScanCode.Minus, 'Minus'); + d(ScanCode.Equal, 'Equal'); + d(ScanCode.BracketLeft, 'BracketLeft'); + d(ScanCode.BracketRight, 'BracketRight'); + d(ScanCode.Backslash, 'Backslash'); + d(ScanCode.IntlHash, 'IntlHash'); + d(ScanCode.Semicolon, 'Semicolon'); + d(ScanCode.Quote, 'Quote'); + d(ScanCode.Backquote, 'Backquote'); + d(ScanCode.Comma, 'Comma'); + d(ScanCode.Period, 'Period'); + d(ScanCode.Slash, 'Slash'); + d(ScanCode.CapsLock, 'CapsLock'); + d(ScanCode.F1, 'F1'); + d(ScanCode.F2, 'F2'); + d(ScanCode.F3, 'F3'); + d(ScanCode.F4, 'F4'); + d(ScanCode.F5, 'F5'); + d(ScanCode.F6, 'F6'); + d(ScanCode.F7, 'F7'); + d(ScanCode.F8, 'F8'); + d(ScanCode.F9, 'F9'); + d(ScanCode.F10, 'F10'); + d(ScanCode.F11, 'F11'); + d(ScanCode.F12, 'F12'); + d(ScanCode.PrintScreen, 'PrintScreen'); + d(ScanCode.ScrollLock, 'ScrollLock'); + d(ScanCode.Pause, 'Pause'); + d(ScanCode.Insert, 'Insert'); + d(ScanCode.Home, 'Home'); + d(ScanCode.PageUp, 'PageUp'); + d(ScanCode.Delete, 'Delete'); + d(ScanCode.End, 'End'); + d(ScanCode.PageDown, 'PageDown'); + d(ScanCode.ArrowRight, 'ArrowRight'); + d(ScanCode.ArrowLeft, 'ArrowLeft'); + d(ScanCode.ArrowDown, 'ArrowDown'); + d(ScanCode.ArrowUp, 'ArrowUp'); + d(ScanCode.NumLock, 'NumLock'); + d(ScanCode.NumpadDivide, 'NumpadDivide'); + d(ScanCode.NumpadMultiply, 'NumpadMultiply'); + d(ScanCode.NumpadSubtract, 'NumpadSubtract'); + d(ScanCode.NumpadAdd, 'NumpadAdd'); + d(ScanCode.NumpadEnter, 'NumpadEnter'); + d(ScanCode.Numpad1, 'Numpad1'); + d(ScanCode.Numpad2, 'Numpad2'); + d(ScanCode.Numpad3, 'Numpad3'); + d(ScanCode.Numpad4, 'Numpad4'); + d(ScanCode.Numpad5, 'Numpad5'); + d(ScanCode.Numpad6, 'Numpad6'); + d(ScanCode.Numpad7, 'Numpad7'); + d(ScanCode.Numpad8, 'Numpad8'); + d(ScanCode.Numpad9, 'Numpad9'); + d(ScanCode.Numpad0, 'Numpad0'); + d(ScanCode.NumpadDecimal, 'NumpadDecimal'); + d(ScanCode.IntlBackslash, 'IntlBackslash'); + d(ScanCode.ContextMenu, 'ContextMenu'); + d(ScanCode.Power, 'Power'); + d(ScanCode.NumpadEqual, 'NumpadEqual'); + d(ScanCode.F13, 'F13'); + d(ScanCode.F14, 'F14'); + d(ScanCode.F15, 'F15'); + d(ScanCode.F16, 'F16'); + d(ScanCode.F17, 'F17'); + d(ScanCode.F18, 'F18'); + d(ScanCode.F19, 'F19'); + d(ScanCode.F20, 'F20'); + d(ScanCode.F21, 'F21'); + d(ScanCode.F22, 'F22'); + d(ScanCode.F23, 'F23'); + d(ScanCode.F24, 'F24'); + d(ScanCode.Open, 'Open'); + d(ScanCode.Help, 'Help'); + d(ScanCode.Select, 'Select'); + d(ScanCode.Again, 'Again'); + d(ScanCode.Undo, 'Undo'); + d(ScanCode.Cut, 'Cut'); + d(ScanCode.Copy, 'Copy'); + d(ScanCode.Paste, 'Paste'); + d(ScanCode.Find, 'Find'); + d(ScanCode.AudioVolumeMute, 'AudioVolumeMute'); + d(ScanCode.AudioVolumeUp, 'AudioVolumeUp'); + d(ScanCode.AudioVolumeDown, 'AudioVolumeDown'); + d(ScanCode.NumpadComma, 'NumpadComma'); + d(ScanCode.IntlRo, 'IntlRo'); + d(ScanCode.KanaMode, 'KanaMode'); + d(ScanCode.IntlYen, 'IntlYen'); + d(ScanCode.Convert, 'Convert'); + d(ScanCode.NonConvert, 'NonConvert'); + d(ScanCode.Lang1, 'Lang1'); + d(ScanCode.Lang2, 'Lang2'); + d(ScanCode.Lang3, 'Lang3'); + d(ScanCode.Lang4, 'Lang4'); + d(ScanCode.Lang5, 'Lang5'); + d(ScanCode.Abort, 'Abort'); + d(ScanCode.Props, 'Props'); + d(ScanCode.NumpadParenLeft, 'NumpadParenLeft'); + d(ScanCode.NumpadParenRight, 'NumpadParenRight'); + d(ScanCode.NumpadBackspace, 'NumpadBackspace'); + d(ScanCode.NumpadMemoryStore, 'NumpadMemoryStore'); + d(ScanCode.NumpadMemoryRecall, 'NumpadMemoryRecall'); + d(ScanCode.NumpadMemoryClear, 'NumpadMemoryClear'); + d(ScanCode.NumpadMemoryAdd, 'NumpadMemoryAdd'); + d(ScanCode.NumpadMemorySubtract, 'NumpadMemorySubtract'); + d(ScanCode.NumpadClear, 'NumpadClear'); + d(ScanCode.NumpadClearEntry, 'NumpadClearEntry'); + d(ScanCode.ControlLeft, 'ControlLeft'); + d(ScanCode.ShiftLeft, 'ShiftLeft'); + d(ScanCode.AltLeft, 'AltLeft'); + d(ScanCode.MetaLeft, 'MetaLeft'); + d(ScanCode.ControlRight, 'ControlRight'); + d(ScanCode.ShiftRight, 'ShiftRight'); + d(ScanCode.AltRight, 'AltRight'); + d(ScanCode.MetaRight, 'MetaRight'); + d(ScanCode.BrightnessUp, 'BrightnessUp'); + d(ScanCode.BrightnessDown, 'BrightnessDown'); + d(ScanCode.MediaPlay, 'MediaPlay'); + d(ScanCode.MediaRecord, 'MediaRecord'); + d(ScanCode.MediaFastForward, 'MediaFastForward'); + d(ScanCode.MediaRewind, 'MediaRewind'); + d(ScanCode.MediaTrackNext, 'MediaTrackNext'); + d(ScanCode.MediaTrackPrevious, 'MediaTrackPrevious'); + d(ScanCode.MediaStop, 'MediaStop'); + d(ScanCode.Eject, 'Eject'); + d(ScanCode.MediaPlayPause, 'MediaPlayPause'); + d(ScanCode.MediaSelect, 'MediaSelect'); + d(ScanCode.LaunchMail, 'LaunchMail'); + d(ScanCode.LaunchApp2, 'LaunchApp2'); + d(ScanCode.LaunchApp1, 'LaunchApp1'); + d(ScanCode.SelectTask, 'SelectTask'); + d(ScanCode.LaunchScreenSaver, 'LaunchScreenSaver'); + d(ScanCode.BrowserSearch, 'BrowserSearch'); + d(ScanCode.BrowserHome, 'BrowserHome'); + d(ScanCode.BrowserBack, 'BrowserBack'); + d(ScanCode.BrowserForward, 'BrowserForward'); + d(ScanCode.BrowserStop, 'BrowserStop'); + d(ScanCode.BrowserRefresh, 'BrowserRefresh'); + d(ScanCode.BrowserFavorites, 'BrowserFavorites'); + d(ScanCode.ZoomToggle, 'ZoomToggle'); + d(ScanCode.MailReply, 'MailReply'); + d(ScanCode.MailForward, 'MailForward'); + d(ScanCode.MailSend, 'MailSend'); +})(); + +(function () { + for (let i = 0; i <= ScanCode.MAX_VALUE; i++) { + IMMUTABLE_CODE_TO_KEY_CODE[i] = -1; + } + + for (let i = 0; i <= KeyCode.MAX_VALUE; i++) { + IMMUTABLE_KEY_CODE_TO_CODE[i] = -1; + } + + function define(code: ScanCode, keyCode: KeyCode): void { + IMMUTABLE_CODE_TO_KEY_CODE[code] = keyCode; + + if ( + (keyCode !== KeyCode.Unknown) + && (keyCode !== KeyCode.Enter) + && (keyCode !== KeyCode.Ctrl) + && (keyCode !== KeyCode.Shift) + && (keyCode !== KeyCode.Alt) + && (keyCode !== KeyCode.Meta) + ) { + IMMUTABLE_KEY_CODE_TO_CODE[keyCode] = code; + } + } + + // Manually added due to the exclusion above (due to duplication with NumpadEnter) + IMMUTABLE_KEY_CODE_TO_CODE[KeyCode.Enter] = ScanCode.Enter; + + define(ScanCode.None, KeyCode.Unknown); + define(ScanCode.Hyper, KeyCode.Unknown); + define(ScanCode.Super, KeyCode.Unknown); + define(ScanCode.Fn, KeyCode.Unknown); + define(ScanCode.FnLock, KeyCode.Unknown); + define(ScanCode.Suspend, KeyCode.Unknown); + define(ScanCode.Resume, KeyCode.Unknown); + define(ScanCode.Turbo, KeyCode.Unknown); + define(ScanCode.Sleep, KeyCode.Unknown); + define(ScanCode.WakeUp, KeyCode.Unknown); + // define(ScanCode.KeyA, KeyCode.Unknown); + // define(ScanCode.KeyB, KeyCode.Unknown); + // define(ScanCode.KeyC, KeyCode.Unknown); + // define(ScanCode.KeyD, KeyCode.Unknown); + // define(ScanCode.KeyE, KeyCode.Unknown); + // define(ScanCode.KeyF, KeyCode.Unknown); + // define(ScanCode.KeyG, KeyCode.Unknown); + // define(ScanCode.KeyH, KeyCode.Unknown); + // define(ScanCode.KeyI, KeyCode.Unknown); + // define(ScanCode.KeyJ, KeyCode.Unknown); + // define(ScanCode.KeyK, KeyCode.Unknown); + // define(ScanCode.KeyL, KeyCode.Unknown); + // define(ScanCode.KeyM, KeyCode.Unknown); + // define(ScanCode.KeyN, KeyCode.Unknown); + // define(ScanCode.KeyO, KeyCode.Unknown); + // define(ScanCode.KeyP, KeyCode.Unknown); + // define(ScanCode.KeyQ, KeyCode.Unknown); + // define(ScanCode.KeyR, KeyCode.Unknown); + // define(ScanCode.KeyS, KeyCode.Unknown); + // define(ScanCode.KeyT, KeyCode.Unknown); + // define(ScanCode.KeyU, KeyCode.Unknown); + // define(ScanCode.KeyV, KeyCode.Unknown); + // define(ScanCode.KeyW, KeyCode.Unknown); + // define(ScanCode.KeyX, KeyCode.Unknown); + // define(ScanCode.KeyY, KeyCode.Unknown); + // define(ScanCode.KeyZ, KeyCode.Unknown); + // define(ScanCode.Digit1, KeyCode.Unknown); + // define(ScanCode.Digit2, KeyCode.Unknown); + // define(ScanCode.Digit3, KeyCode.Unknown); + // define(ScanCode.Digit4, KeyCode.Unknown); + // define(ScanCode.Digit5, KeyCode.Unknown); + // define(ScanCode.Digit6, KeyCode.Unknown); + // define(ScanCode.Digit7, KeyCode.Unknown); + // define(ScanCode.Digit8, KeyCode.Unknown); + // define(ScanCode.Digit9, KeyCode.Unknown); + // define(ScanCode.Digit0, KeyCode.Unknown); + define(ScanCode.Enter, KeyCode.Enter); + define(ScanCode.Escape, KeyCode.Escape); + define(ScanCode.Backspace, KeyCode.Backspace); + define(ScanCode.Tab, KeyCode.Tab); + define(ScanCode.Space, KeyCode.Space); + // define(ScanCode.Minus, KeyCode.Unknown); + // define(ScanCode.Equal, KeyCode.Unknown); + // define(ScanCode.BracketLeft, KeyCode.Unknown); + // define(ScanCode.BracketRight, KeyCode.Unknown); + // define(ScanCode.Backslash, KeyCode.Unknown); + // define(ScanCode.IntlHash, KeyCode.Unknown); + // define(ScanCode.Semicolon, KeyCode.Unknown); + // define(ScanCode.Quote, KeyCode.Unknown); + // define(ScanCode.Backquote, KeyCode.Unknown); + // define(ScanCode.Comma, KeyCode.Unknown); + // define(ScanCode.Period, KeyCode.Unknown); + // define(ScanCode.Slash, KeyCode.Unknown); + define(ScanCode.CapsLock, KeyCode.CapsLock); + define(ScanCode.F1, KeyCode.F1); + define(ScanCode.F2, KeyCode.F2); + define(ScanCode.F3, KeyCode.F3); + define(ScanCode.F4, KeyCode.F4); + define(ScanCode.F5, KeyCode.F5); + define(ScanCode.F6, KeyCode.F6); + define(ScanCode.F7, KeyCode.F7); + define(ScanCode.F8, KeyCode.F8); + define(ScanCode.F9, KeyCode.F9); + define(ScanCode.F10, KeyCode.F10); + define(ScanCode.F11, KeyCode.F11); + define(ScanCode.F12, KeyCode.F12); + define(ScanCode.PrintScreen, KeyCode.Unknown); + define(ScanCode.ScrollLock, KeyCode.ScrollLock); + define(ScanCode.Pause, KeyCode.PauseBreak); + define(ScanCode.Insert, KeyCode.Insert); + define(ScanCode.Home, KeyCode.Home); + define(ScanCode.PageUp, KeyCode.PageUp); + define(ScanCode.Delete, KeyCode.Delete); + define(ScanCode.End, KeyCode.End); + define(ScanCode.PageDown, KeyCode.PageDown); + define(ScanCode.ArrowRight, KeyCode.RightArrow); + define(ScanCode.ArrowLeft, KeyCode.LeftArrow); + define(ScanCode.ArrowDown, KeyCode.DownArrow); + define(ScanCode.ArrowUp, KeyCode.UpArrow); + define(ScanCode.NumLock, KeyCode.NumLock); + define(ScanCode.NumpadDivide, KeyCode.NUMPAD_DIVIDE); + define(ScanCode.NumpadMultiply, KeyCode.NUMPAD_MULTIPLY); + define(ScanCode.NumpadSubtract, KeyCode.NUMPAD_SUBTRACT); + define(ScanCode.NumpadAdd, KeyCode.NUMPAD_ADD); + define(ScanCode.NumpadEnter, KeyCode.Enter); // Duplicate + define(ScanCode.Numpad1, KeyCode.NUMPAD_1); + define(ScanCode.Numpad2, KeyCode.NUMPAD_2); + define(ScanCode.Numpad3, KeyCode.NUMPAD_3); + define(ScanCode.Numpad4, KeyCode.NUMPAD_4); + define(ScanCode.Numpad5, KeyCode.NUMPAD_5); + define(ScanCode.Numpad6, KeyCode.NUMPAD_6); + define(ScanCode.Numpad7, KeyCode.NUMPAD_7); + define(ScanCode.Numpad8, KeyCode.NUMPAD_8); + define(ScanCode.Numpad9, KeyCode.NUMPAD_9); + define(ScanCode.Numpad0, KeyCode.NUMPAD_0); + define(ScanCode.NumpadDecimal, KeyCode.NUMPAD_DECIMAL); + // define(ScanCode.IntlBackslash, KeyCode.Unknown); + define(ScanCode.ContextMenu, KeyCode.ContextMenu); + define(ScanCode.Power, KeyCode.Unknown); + define(ScanCode.NumpadEqual, KeyCode.Unknown); + define(ScanCode.F13, KeyCode.F13); + define(ScanCode.F14, KeyCode.F14); + define(ScanCode.F15, KeyCode.F15); + define(ScanCode.F16, KeyCode.F16); + define(ScanCode.F17, KeyCode.F17); + define(ScanCode.F18, KeyCode.F18); + define(ScanCode.F19, KeyCode.F19); + define(ScanCode.F20, KeyCode.Unknown); + define(ScanCode.F21, KeyCode.Unknown); + define(ScanCode.F22, KeyCode.Unknown); + define(ScanCode.F23, KeyCode.Unknown); + define(ScanCode.F24, KeyCode.Unknown); + define(ScanCode.Open, KeyCode.Unknown); + define(ScanCode.Help, KeyCode.Unknown); + define(ScanCode.Select, KeyCode.Unknown); + define(ScanCode.Again, KeyCode.Unknown); + define(ScanCode.Undo, KeyCode.Unknown); + define(ScanCode.Cut, KeyCode.Unknown); + define(ScanCode.Copy, KeyCode.Unknown); + define(ScanCode.Paste, KeyCode.Unknown); + define(ScanCode.Find, KeyCode.Unknown); + define(ScanCode.AudioVolumeMute, KeyCode.Unknown); + define(ScanCode.AudioVolumeUp, KeyCode.Unknown); + define(ScanCode.AudioVolumeDown, KeyCode.Unknown); + define(ScanCode.NumpadComma, KeyCode.NUMPAD_SEPARATOR); + // define(ScanCode.IntlRo, KeyCode.Unknown); + define(ScanCode.KanaMode, KeyCode.Unknown); + // define(ScanCode.IntlYen, KeyCode.Unknown); + define(ScanCode.Convert, KeyCode.Unknown); + define(ScanCode.NonConvert, KeyCode.Unknown); + define(ScanCode.Lang1, KeyCode.Unknown); + define(ScanCode.Lang2, KeyCode.Unknown); + define(ScanCode.Lang3, KeyCode.Unknown); + define(ScanCode.Lang4, KeyCode.Unknown); + define(ScanCode.Lang5, KeyCode.Unknown); + define(ScanCode.Abort, KeyCode.Unknown); + define(ScanCode.Props, KeyCode.Unknown); + define(ScanCode.NumpadParenLeft, KeyCode.Unknown); + define(ScanCode.NumpadParenRight, KeyCode.Unknown); + define(ScanCode.NumpadBackspace, KeyCode.Unknown); + define(ScanCode.NumpadMemoryStore, KeyCode.Unknown); + define(ScanCode.NumpadMemoryRecall, KeyCode.Unknown); + define(ScanCode.NumpadMemoryClear, KeyCode.Unknown); + define(ScanCode.NumpadMemoryAdd, KeyCode.Unknown); + define(ScanCode.NumpadMemorySubtract, KeyCode.Unknown); + define(ScanCode.NumpadClear, KeyCode.Unknown); + define(ScanCode.NumpadClearEntry, KeyCode.Unknown); + define(ScanCode.ControlLeft, KeyCode.Ctrl); // Duplicate + define(ScanCode.ShiftLeft, KeyCode.Shift); // Duplicate + define(ScanCode.AltLeft, KeyCode.Alt); // Duplicate + define(ScanCode.MetaLeft, KeyCode.Meta); // Duplicate + define(ScanCode.ControlRight, KeyCode.Ctrl); // Duplicate + define(ScanCode.ShiftRight, KeyCode.Shift); // Duplicate + define(ScanCode.AltRight, KeyCode.Alt); // Duplicate + define(ScanCode.MetaRight, KeyCode.Meta); // Duplicate + define(ScanCode.BrightnessUp, KeyCode.Unknown); + define(ScanCode.BrightnessDown, KeyCode.Unknown); + define(ScanCode.MediaPlay, KeyCode.Unknown); + define(ScanCode.MediaRecord, KeyCode.Unknown); + define(ScanCode.MediaFastForward, KeyCode.Unknown); + define(ScanCode.MediaRewind, KeyCode.Unknown); + define(ScanCode.MediaTrackNext, KeyCode.Unknown); + define(ScanCode.MediaTrackPrevious, KeyCode.Unknown); + define(ScanCode.MediaStop, KeyCode.Unknown); + define(ScanCode.Eject, KeyCode.Unknown); + define(ScanCode.MediaPlayPause, KeyCode.Unknown); + define(ScanCode.MediaSelect, KeyCode.Unknown); + define(ScanCode.LaunchMail, KeyCode.Unknown); + define(ScanCode.LaunchApp2, KeyCode.Unknown); + define(ScanCode.LaunchApp1, KeyCode.Unknown); + define(ScanCode.SelectTask, KeyCode.Unknown); + define(ScanCode.LaunchScreenSaver, KeyCode.Unknown); + define(ScanCode.BrowserSearch, KeyCode.Unknown); + define(ScanCode.BrowserHome, KeyCode.Unknown); + define(ScanCode.BrowserBack, KeyCode.Unknown); + define(ScanCode.BrowserForward, KeyCode.Unknown); + define(ScanCode.BrowserStop, KeyCode.Unknown); + define(ScanCode.BrowserRefresh, KeyCode.Unknown); + define(ScanCode.BrowserFavorites, KeyCode.Unknown); + define(ScanCode.ZoomToggle, KeyCode.Unknown); + define(ScanCode.MailReply, KeyCode.Unknown); + define(ScanCode.MailForward, KeyCode.Unknown); + define(ScanCode.MailSend, KeyCode.Unknown); +})(); diff --git a/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts new file mode 100644 index 0000000000..ed36904c6c --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts @@ -0,0 +1,754 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { KeyCode, KeyCodeUtils, ResolvedKeybinding, Keybinding, SimpleKeybinding, KeybindingType, ResolvedKeybindingPart } from 'vs/base/common/keyCodes'; +import { ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE, ScanCodeBinding } from 'vs/workbench/services/keybinding/common/scanCode'; +import { CharCode } from 'vs/base/common/charCode'; +import { UILabelProvider, AriaLabelProvider, ElectronAcceleratorLabelProvider, UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; +import { OperatingSystem } from 'vs/base/common/platform'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; + +export interface IWindowsKeyMapping { + vkey: string; + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} + +function windowsKeyMappingEquals(a: IWindowsKeyMapping, b: IWindowsKeyMapping): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return ( + a.vkey === b.vkey + && a.value === b.value + && a.withShift === b.withShift + && a.withAltGr === b.withAltGr + && a.withShiftAltGr === b.withShiftAltGr + ); +} + +export interface IWindowsKeyboardMapping { + [scanCode: string]: IWindowsKeyMapping; +} + +export function windowsKeyboardMappingEquals(a: IWindowsKeyboardMapping, b: IWindowsKeyboardMapping): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + for (let scanCode = 0; scanCode < ScanCode.MAX_VALUE; scanCode++) { + const strScanCode = ScanCodeUtils.toString(scanCode); + const aEntry = a[strScanCode]; + const bEntry = b[strScanCode]; + if (!windowsKeyMappingEquals(aEntry, bEntry)) { + return false; + } + } + return true; +} + + +const LOG = false; +function log(str: string): void { + if (LOG) { + console.info(str); + } +} + +const NATIVE_KEY_CODE_TO_KEY_CODE: { [nativeKeyCode: string]: KeyCode; } = _getNativeMap(); + +export interface IScanCodeMapping { + scanCode: ScanCode; + keyCode: KeyCode; + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} + +export class WindowsNativeResolvedKeybinding extends ResolvedKeybinding { + + private readonly _mapper: WindowsKeyboardMapper; + private readonly _firstPart: SimpleKeybinding; + private readonly _chordPart: SimpleKeybinding; + + constructor(mapper: WindowsKeyboardMapper, firstPart: SimpleKeybinding, chordPart: SimpleKeybinding) { + super(); + this._mapper = mapper; + this._firstPart = firstPart; + this._chordPart = chordPart; + } + + private _getUILabelForKeybinding(keybinding: SimpleKeybinding): string { + if (!keybinding) { + return null; + } + if (keybinding.isDuplicateModifierCase()) { + return ''; + } + return this._mapper.getUILabelForKeyCode(keybinding.keyCode); + } + + public getLabel(): string { + let firstPart = this._getUILabelForKeybinding(this._firstPart); + let chordPart = this._getUILabelForKeybinding(this._chordPart); + return UILabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows); + } + + private _getUSLabelForKeybinding(keybinding: SimpleKeybinding): string { + if (!keybinding) { + return null; + } + if (keybinding.isDuplicateModifierCase()) { + return ''; + } + return KeyCodeUtils.toString(keybinding.keyCode); + } + + public getUSLabel(): string { + let firstPart = this._getUSLabelForKeybinding(this._firstPart); + let chordPart = this._getUSLabelForKeybinding(this._chordPart); + return UILabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows); + } + + private _getAriaLabelForKeybinding(keybinding: SimpleKeybinding): string { + if (!keybinding) { + return null; + } + if (keybinding.isDuplicateModifierCase()) { + return ''; + } + return this._mapper.getAriaLabelForKeyCode(keybinding.keyCode); + } + + public getAriaLabel(): string { + let firstPart = this._getAriaLabelForKeybinding(this._firstPart); + let chordPart = this._getAriaLabelForKeybinding(this._chordPart); + return AriaLabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows); + } + + private _keyCodeToElectronAccelerator(keyCode: KeyCode): string { + if (keyCode >= KeyCode.NUMPAD_0 && keyCode <= KeyCode.NUMPAD_DIVIDE) { + // Electron cannot handle numpad keys + return null; + } + + switch (keyCode) { + case KeyCode.UpArrow: + return 'Up'; + case KeyCode.DownArrow: + return 'Down'; + case KeyCode.LeftArrow: + return 'Left'; + case KeyCode.RightArrow: + return 'Right'; + } + + // electron menus always do the correct rendering on Windows + return KeyCodeUtils.toString(keyCode); + } + + private _getElectronAcceleratorLabelForKeybinding(keybinding: SimpleKeybinding): string { + if (!keybinding) { + return null; + } + if (keybinding.isDuplicateModifierCase()) { + return null; + } + return this._keyCodeToElectronAccelerator(keybinding.keyCode); + } + + public getElectronAccelerator(): string { + if (this._chordPart !== null) { + // Electron cannot handle chords + return null; + } + + let firstPart = this._getElectronAcceleratorLabelForKeybinding(this._firstPart); + return ElectronAcceleratorLabelProvider.toLabel(this._firstPart, firstPart, null, null, OperatingSystem.Windows); + } + + private _getUserSettingsLabelForKeybinding(keybinding: SimpleKeybinding): string { + if (!keybinding) { + return null; + } + if (keybinding.isDuplicateModifierCase()) { + return ''; + } + return this._mapper.getUserSettingsLabelForKeyCode(keybinding.keyCode); + } + + public getUserSettingsLabel(): string { + let firstPart = this._getUserSettingsLabelForKeybinding(this._firstPart); + let chordPart = this._getUserSettingsLabelForKeybinding(this._chordPart); + let result = UserSettingsLabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows); + return (result ? result.toLowerCase() : result); + } + + public isWYSIWYG(): boolean { + if (this._firstPart && !this._isWYSIWYG(this._firstPart.keyCode)) { + return false; + } + if (this._chordPart && !this._isWYSIWYG(this._chordPart.keyCode)) { + return false; + } + return true; + } + + private _isWYSIWYG(keyCode: KeyCode): boolean { + if ( + keyCode === KeyCode.LeftArrow + || keyCode === KeyCode.UpArrow + || keyCode === KeyCode.RightArrow + || keyCode === KeyCode.DownArrow + ) { + return true; + } + const ariaLabel = this._mapper.getAriaLabelForKeyCode(keyCode); + const userSettingsLabel = this._mapper.getUserSettingsLabelForKeyCode(keyCode); + return (ariaLabel === userSettingsLabel); + } + + public isChord(): boolean { + return (this._chordPart ? true : false); + } + + public getParts(): [ResolvedKeybindingPart, ResolvedKeybindingPart] { + return [ + this._toResolvedKeybindingPart(this._firstPart), + this._toResolvedKeybindingPart(this._chordPart) + ]; + } + + private _toResolvedKeybindingPart(keybinding: SimpleKeybinding): ResolvedKeybindingPart { + if (!keybinding) { + return null; + } + + return new ResolvedKeybindingPart( + keybinding.ctrlKey, + keybinding.shiftKey, + keybinding.altKey, + keybinding.metaKey, + this._getUILabelForKeybinding(keybinding), + this._getAriaLabelForKeybinding(keybinding) + ); + } + + public getDispatchParts(): [string, string] { + let firstPart = this._firstPart ? this._getDispatchStr(this._firstPart) : null; + let chordPart = this._chordPart ? this._getDispatchStr(this._chordPart) : null; + return [firstPart, chordPart]; + } + + private _getDispatchStr(keybinding: SimpleKeybinding): string { + if (keybinding.isModifierKey()) { + return null; + } + let result = ''; + + if (keybinding.ctrlKey) { + result += 'ctrl+'; + } + if (keybinding.shiftKey) { + result += 'shift+'; + } + if (keybinding.altKey) { + result += 'alt+'; + } + if (keybinding.metaKey) { + result += 'meta+'; + } + result += KeyCodeUtils.toString(keybinding.keyCode); + + return result; + } + + private static getProducedCharCode(kb: ScanCodeBinding, mapping: IScanCodeMapping): string { + if (!mapping) { + return null; + } + if (kb.ctrlKey && kb.shiftKey && kb.altKey) { + return mapping.withShiftAltGr; + } + if (kb.ctrlKey && kb.altKey) { + return mapping.withAltGr; + } + if (kb.shiftKey) { + return mapping.withShift; + } + return mapping.value; + } + + public static getProducedChar(kb: ScanCodeBinding, mapping: IScanCodeMapping): string { + const char = this.getProducedCharCode(kb, mapping); + if (char === null || char.length === 0) { + return ' --- '; + } + return ' ' + char + ' '; + } +} + +export class WindowsKeyboardMapper implements IKeyboardMapper { + + public readonly isUSStandard: boolean; + private readonly _codeInfo: IScanCodeMapping[]; + private readonly _scanCodeToKeyCode: KeyCode[]; + private readonly _keyCodeToLabel: string[] = []; + private readonly _keyCodeExists: boolean[]; + + constructor(isUSStandard: boolean, rawMappings: IWindowsKeyboardMapping) { + this.isUSStandard = isUSStandard; + this._scanCodeToKeyCode = []; + this._keyCodeToLabel = []; + this._keyCodeExists = []; + this._keyCodeToLabel[KeyCode.Unknown] = KeyCodeUtils.toString(KeyCode.Unknown); + + for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) { + const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode]; + if (immutableKeyCode !== -1) { + this._scanCodeToKeyCode[scanCode] = immutableKeyCode; + this._keyCodeToLabel[immutableKeyCode] = KeyCodeUtils.toString(immutableKeyCode); + this._keyCodeExists[immutableKeyCode] = true; + } + } + + let producesLetter: boolean[] = []; + + this._codeInfo = []; + for (let strCode in rawMappings) { + if (rawMappings.hasOwnProperty(strCode)) { + const scanCode = ScanCodeUtils.toEnum(strCode); + if (scanCode === ScanCode.None) { + log(`Unknown scanCode ${strCode} in mapping.`); + continue; + } + const rawMapping = rawMappings[strCode]; + + const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode]; + if (immutableKeyCode !== -1) { + const keyCode = NATIVE_KEY_CODE_TO_KEY_CODE[rawMapping.vkey] || KeyCode.Unknown; + if (keyCode === KeyCode.Unknown || immutableKeyCode === keyCode) { + continue; + } + if (scanCode !== ScanCode.NumpadComma) { + // Looks like ScanCode.NumpadComma doesn't always map to KeyCode.NUMPAD_SEPARATOR + // e.g. on POR - PTB + continue; + } + } + + const value = rawMapping.value; + const withShift = rawMapping.withShift; + const withAltGr = rawMapping.withAltGr; + const withShiftAltGr = rawMapping.withShiftAltGr; + const keyCode = NATIVE_KEY_CODE_TO_KEY_CODE[rawMapping.vkey] || KeyCode.Unknown; + + const mapping: IScanCodeMapping = { + scanCode: scanCode, + keyCode: keyCode, + value: value, + withShift: withShift, + withAltGr: withAltGr, + withShiftAltGr: withShiftAltGr, + }; + this._codeInfo[scanCode] = mapping; + this._scanCodeToKeyCode[scanCode] = keyCode; + + if (keyCode === KeyCode.Unknown) { + continue; + } + this._keyCodeExists[keyCode] = true; + + if (value.length === 0) { + // This key does not produce strings + this._keyCodeToLabel[keyCode] = null; + } + + else if (value.length > 1) { + // This key produces a letter representable with multiple UTF-16 code units. + this._keyCodeToLabel[keyCode] = value; + } + + else { + const charCode = value.charCodeAt(0); + + if (charCode >= CharCode.a && charCode <= CharCode.z) { + const upperCaseValue = CharCode.A + (charCode - CharCode.a); + producesLetter[upperCaseValue] = true; + this._keyCodeToLabel[keyCode] = String.fromCharCode(CharCode.A + (charCode - CharCode.a)); + } + + else if (charCode >= CharCode.A && charCode <= CharCode.Z) { + producesLetter[charCode] = true; + this._keyCodeToLabel[keyCode] = value; + } + + else { + this._keyCodeToLabel[keyCode] = value; + } + } + } + } + + // Handle keyboard layouts where latin characters are not produced e.g. Cyrillic + const _registerLetterIfMissing = (charCode: CharCode, keyCode: KeyCode): void => { + if (!producesLetter[charCode]) { + this._keyCodeToLabel[keyCode] = String.fromCharCode(charCode); + } + }; + _registerLetterIfMissing(CharCode.A, KeyCode.KEY_A); + _registerLetterIfMissing(CharCode.B, KeyCode.KEY_B); + _registerLetterIfMissing(CharCode.C, KeyCode.KEY_C); + _registerLetterIfMissing(CharCode.D, KeyCode.KEY_D); + _registerLetterIfMissing(CharCode.E, KeyCode.KEY_E); + _registerLetterIfMissing(CharCode.F, KeyCode.KEY_F); + _registerLetterIfMissing(CharCode.G, KeyCode.KEY_G); + _registerLetterIfMissing(CharCode.H, KeyCode.KEY_H); + _registerLetterIfMissing(CharCode.I, KeyCode.KEY_I); + _registerLetterIfMissing(CharCode.J, KeyCode.KEY_J); + _registerLetterIfMissing(CharCode.K, KeyCode.KEY_K); + _registerLetterIfMissing(CharCode.L, KeyCode.KEY_L); + _registerLetterIfMissing(CharCode.M, KeyCode.KEY_M); + _registerLetterIfMissing(CharCode.N, KeyCode.KEY_N); + _registerLetterIfMissing(CharCode.O, KeyCode.KEY_O); + _registerLetterIfMissing(CharCode.P, KeyCode.KEY_P); + _registerLetterIfMissing(CharCode.Q, KeyCode.KEY_Q); + _registerLetterIfMissing(CharCode.R, KeyCode.KEY_R); + _registerLetterIfMissing(CharCode.S, KeyCode.KEY_S); + _registerLetterIfMissing(CharCode.T, KeyCode.KEY_T); + _registerLetterIfMissing(CharCode.U, KeyCode.KEY_U); + _registerLetterIfMissing(CharCode.V, KeyCode.KEY_V); + _registerLetterIfMissing(CharCode.W, KeyCode.KEY_W); + _registerLetterIfMissing(CharCode.X, KeyCode.KEY_X); + _registerLetterIfMissing(CharCode.Y, KeyCode.KEY_Y); + _registerLetterIfMissing(CharCode.Z, KeyCode.KEY_Z); + } + + public dumpDebugInfo(): string { + let result: string[] = []; + + let immutableSamples = [ + ScanCode.ArrowUp, + ScanCode.Numpad0 + ]; + + let cnt = 0; + result.push(`-----------------------------------------------------------------------------------------------------------------------------------------`); + for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) { + if (IMMUTABLE_CODE_TO_KEY_CODE[scanCode] !== -1) { + if (immutableSamples.indexOf(scanCode) === -1) { + continue; + } + } + + if (cnt % 6 === 0) { + result.push(`| HW Code combination | Key | KeyCode combination | UI label | User settings | WYSIWYG |`); + result.push(`-----------------------------------------------------------------------------------------------------------------------------------------`); + } + cnt++; + + const mapping = this._codeInfo[scanCode]; + const strCode = ScanCodeUtils.toString(scanCode); + + let mods = [0b000, 0b010, 0b101, 0b111]; + for (let modIndex = 0; modIndex < mods.length; modIndex++) { + const mod = mods[modIndex]; + const ctrlKey = (mod & 0b001) ? true : false; + const shiftKey = (mod & 0b010) ? true : false; + const altKey = (mod & 0b100) ? true : false; + const scanCodeBinding = new ScanCodeBinding(ctrlKey, shiftKey, altKey, false, scanCode); + const kb = this._resolveSimpleUserBinding(scanCodeBinding); + const strKeyCode = (kb ? KeyCodeUtils.toString(kb.keyCode) : null); + const resolvedKb = (kb ? new WindowsNativeResolvedKeybinding(this, kb, null) : null); + + const outScanCode = `${ctrlKey ? 'Ctrl+' : ''}${shiftKey ? 'Shift+' : ''}${altKey ? 'Alt+' : ''}${strCode}`; + const ariaLabel = (resolvedKb ? resolvedKb.getAriaLabel() : null); + const outUILabel = (ariaLabel ? ariaLabel.replace(/Control\+/, 'Ctrl+') : null); + const outUserSettings = (resolvedKb ? resolvedKb.getUserSettingsLabel() : null); + const outKey = WindowsNativeResolvedKeybinding.getProducedChar(scanCodeBinding, mapping); + const outKb = (strKeyCode ? `${ctrlKey ? 'Ctrl+' : ''}${shiftKey ? 'Shift+' : ''}${altKey ? 'Alt+' : ''}${strKeyCode}` : null); + const isWYSIWYG = (resolvedKb ? resolvedKb.isWYSIWYG() : false); + const outWYSIWYG = (isWYSIWYG ? ' ' : ' NO '); + result.push(`| ${this._leftPad(outScanCode, 30)} | ${outKey} | ${this._leftPad(outKb, 25)} | ${this._leftPad(outUILabel, 25)} | ${this._leftPad(outUserSettings, 25)} | ${outWYSIWYG} |`); + } + result.push(`-----------------------------------------------------------------------------------------------------------------------------------------`); + } + + + return result.join('\n'); + } + + private _leftPad(str: string, cnt: number): string { + if (str === null) { + str = 'null'; + } + while (str.length < cnt) { + str = ' ' + str; + } + return str; + } + + public getUILabelForKeyCode(keyCode: KeyCode): string { + return this._getLabelForKeyCode(keyCode); + } + + public getAriaLabelForKeyCode(keyCode: KeyCode): string { + return this._getLabelForKeyCode(keyCode); + } + + public getUserSettingsLabelForKeyCode(keyCode: KeyCode): string { + if (this.isUSStandard) { + return KeyCodeUtils.toUserSettingsUS(keyCode); + } + return KeyCodeUtils.toUserSettingsGeneral(keyCode); + } + + private _getLabelForKeyCode(keyCode: KeyCode): string { + return this._keyCodeToLabel[keyCode] || KeyCodeUtils.toString(KeyCode.Unknown); + } + + public resolveKeybinding(keybinding: Keybinding): WindowsNativeResolvedKeybinding[] { + if (keybinding.type === KeybindingType.Chord) { + const firstPartKeyCode = keybinding.firstPart.keyCode; + const chordPartKeyCode = keybinding.chordPart.keyCode; + if (!this._keyCodeExists[firstPartKeyCode] || !this._keyCodeExists[chordPartKeyCode]) { + return []; + } + return [new WindowsNativeResolvedKeybinding(this, keybinding.firstPart, keybinding.chordPart)]; + } else { + if (!this._keyCodeExists[keybinding.keyCode]) { + return []; + } + return [new WindowsNativeResolvedKeybinding(this, keybinding, null)]; + } + } + + public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): WindowsNativeResolvedKeybinding { + const keybinding = new SimpleKeybinding(keyboardEvent.ctrlKey, keyboardEvent.shiftKey, keyboardEvent.altKey, keyboardEvent.metaKey, keyboardEvent.keyCode); + return new WindowsNativeResolvedKeybinding(this, keybinding, null); + } + + private _resolveSimpleUserBinding(binding: SimpleKeybinding | ScanCodeBinding): SimpleKeybinding { + if (!binding) { + return null; + } + if (binding instanceof SimpleKeybinding) { + if (!this._keyCodeExists[binding.keyCode]) { + return null; + } + return binding; + } + const keyCode = this._scanCodeToKeyCode[binding.scanCode] || KeyCode.Unknown; + if (keyCode === KeyCode.Unknown || !this._keyCodeExists[keyCode]) { + return null; + } + return new SimpleKeybinding(binding.ctrlKey, binding.shiftKey, binding.altKey, binding.metaKey, keyCode); + } + + public resolveUserBinding(firstPart: SimpleKeybinding | ScanCodeBinding, chordPart: SimpleKeybinding | ScanCodeBinding): ResolvedKeybinding[] { + const _firstPart = this._resolveSimpleUserBinding(firstPart); + const _chordPart = this._resolveSimpleUserBinding(chordPart); + if (_firstPart && _chordPart) { + return [new WindowsNativeResolvedKeybinding(this, _firstPart, _chordPart)]; + } + if (_firstPart) { + return [new WindowsNativeResolvedKeybinding(this, _firstPart, null)]; + } + return []; + } +} + + +// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx +// See https://github.com/Microsoft/node-native-keymap/blob/master/deps/chromium/keyboard_codes_win.h +function _getNativeMap() { + return { + VK_BACK: KeyCode.Backspace, + VK_TAB: KeyCode.Tab, + VK_CLEAR: KeyCode.Unknown, // MISSING + VK_RETURN: KeyCode.Enter, + VK_SHIFT: KeyCode.Shift, + VK_CONTROL: KeyCode.Ctrl, + VK_MENU: KeyCode.Alt, + VK_PAUSE: KeyCode.PauseBreak, + VK_CAPITAL: KeyCode.CapsLock, + VK_KANA: KeyCode.Unknown, // MISSING + VK_HANGUL: KeyCode.Unknown, // MISSING + VK_JUNJA: KeyCode.Unknown, // MISSING + VK_FINAL: KeyCode.Unknown, // MISSING + VK_HANJA: KeyCode.Unknown, // MISSING + VK_KANJI: KeyCode.Unknown, // MISSING + VK_ESCAPE: KeyCode.Escape, + VK_CONVERT: KeyCode.Unknown, // MISSING + VK_NONCONVERT: KeyCode.Unknown, // MISSING + VK_ACCEPT: KeyCode.Unknown, // MISSING + VK_MODECHANGE: KeyCode.Unknown, // MISSING + VK_SPACE: KeyCode.Space, + VK_PRIOR: KeyCode.PageUp, + VK_NEXT: KeyCode.PageDown, + VK_END: KeyCode.End, + VK_HOME: KeyCode.Home, + VK_LEFT: KeyCode.LeftArrow, + VK_UP: KeyCode.UpArrow, + VK_RIGHT: KeyCode.RightArrow, + VK_DOWN: KeyCode.DownArrow, + VK_SELECT: KeyCode.Unknown, // MISSING + VK_PRINT: KeyCode.Unknown, // MISSING + VK_EXECUTE: KeyCode.Unknown, // MISSING + VK_SNAPSHOT: KeyCode.Unknown, // MISSING + VK_INSERT: KeyCode.Insert, + VK_DELETE: KeyCode.Delete, + VK_HELP: KeyCode.Unknown, // MISSING + + VK_0: KeyCode.KEY_0, + VK_1: KeyCode.KEY_1, + VK_2: KeyCode.KEY_2, + VK_3: KeyCode.KEY_3, + VK_4: KeyCode.KEY_4, + VK_5: KeyCode.KEY_5, + VK_6: KeyCode.KEY_6, + VK_7: KeyCode.KEY_7, + VK_8: KeyCode.KEY_8, + VK_9: KeyCode.KEY_9, + VK_A: KeyCode.KEY_A, + VK_B: KeyCode.KEY_B, + VK_C: KeyCode.KEY_C, + VK_D: KeyCode.KEY_D, + VK_E: KeyCode.KEY_E, + VK_F: KeyCode.KEY_F, + VK_G: KeyCode.KEY_G, + VK_H: KeyCode.KEY_H, + VK_I: KeyCode.KEY_I, + VK_J: KeyCode.KEY_J, + VK_K: KeyCode.KEY_K, + VK_L: KeyCode.KEY_L, + VK_M: KeyCode.KEY_M, + VK_N: KeyCode.KEY_N, + VK_O: KeyCode.KEY_O, + VK_P: KeyCode.KEY_P, + VK_Q: KeyCode.KEY_Q, + VK_R: KeyCode.KEY_R, + VK_S: KeyCode.KEY_S, + VK_T: KeyCode.KEY_T, + VK_U: KeyCode.KEY_U, + VK_V: KeyCode.KEY_V, + VK_W: KeyCode.KEY_W, + VK_X: KeyCode.KEY_X, + VK_Y: KeyCode.KEY_Y, + VK_Z: KeyCode.KEY_Z, + + VK_LWIN: KeyCode.Meta, + VK_COMMAND: KeyCode.Meta, + VK_RWIN: KeyCode.Meta, + VK_APPS: KeyCode.Unknown, // MISSING + VK_SLEEP: KeyCode.Unknown, // MISSING + VK_NUMPAD0: KeyCode.NUMPAD_0, + VK_NUMPAD1: KeyCode.NUMPAD_1, + VK_NUMPAD2: KeyCode.NUMPAD_2, + VK_NUMPAD3: KeyCode.NUMPAD_3, + VK_NUMPAD4: KeyCode.NUMPAD_4, + VK_NUMPAD5: KeyCode.NUMPAD_5, + VK_NUMPAD6: KeyCode.NUMPAD_6, + VK_NUMPAD7: KeyCode.NUMPAD_7, + VK_NUMPAD8: KeyCode.NUMPAD_8, + VK_NUMPAD9: KeyCode.NUMPAD_9, + VK_MULTIPLY: KeyCode.NUMPAD_MULTIPLY, + VK_ADD: KeyCode.NUMPAD_ADD, + VK_SEPARATOR: KeyCode.NUMPAD_SEPARATOR, + VK_SUBTRACT: KeyCode.NUMPAD_SUBTRACT, + VK_DECIMAL: KeyCode.NUMPAD_DECIMAL, + VK_DIVIDE: KeyCode.NUMPAD_DIVIDE, + VK_F1: KeyCode.F1, + VK_F2: KeyCode.F2, + VK_F3: KeyCode.F3, + VK_F4: KeyCode.F4, + VK_F5: KeyCode.F5, + VK_F6: KeyCode.F6, + VK_F7: KeyCode.F7, + VK_F8: KeyCode.F8, + VK_F9: KeyCode.F9, + VK_F10: KeyCode.F10, + VK_F11: KeyCode.F11, + VK_F12: KeyCode.F12, + VK_F13: KeyCode.F13, + VK_F14: KeyCode.F14, + VK_F15: KeyCode.F15, + VK_F16: KeyCode.F16, + VK_F17: KeyCode.F17, + VK_F18: KeyCode.F18, + VK_F19: KeyCode.F19, + VK_F20: KeyCode.Unknown, // MISSING + VK_F21: KeyCode.Unknown, // MISSING + VK_F22: KeyCode.Unknown, // MISSING + VK_F23: KeyCode.Unknown, // MISSING + VK_F24: KeyCode.Unknown, // MISSING + VK_NUMLOCK: KeyCode.NumLock, + VK_SCROLL: KeyCode.ScrollLock, + VK_LSHIFT: KeyCode.Shift, + VK_RSHIFT: KeyCode.Shift, + VK_LCONTROL: KeyCode.Ctrl, + VK_RCONTROL: KeyCode.Ctrl, + VK_LMENU: KeyCode.Unknown, // MISSING + VK_RMENU: KeyCode.Unknown, // MISSING + VK_BROWSER_BACK: KeyCode.Unknown, // MISSING + VK_BROWSER_FORWARD: KeyCode.Unknown, // MISSING + VK_BROWSER_REFRESH: KeyCode.Unknown, // MISSING + VK_BROWSER_STOP: KeyCode.Unknown, // MISSING + VK_BROWSER_SEARCH: KeyCode.Unknown, // MISSING + VK_BROWSER_FAVORITES: KeyCode.Unknown, // MISSING + VK_BROWSER_HOME: KeyCode.Unknown, // MISSING + VK_VOLUME_MUTE: KeyCode.Unknown, // MISSING + VK_VOLUME_DOWN: KeyCode.Unknown, // MISSING + VK_VOLUME_UP: KeyCode.Unknown, // MISSING + VK_MEDIA_NEXT_TRACK: KeyCode.Unknown, // MISSING + VK_MEDIA_PREV_TRACK: KeyCode.Unknown, // MISSING + VK_MEDIA_STOP: KeyCode.Unknown, // MISSING + VK_MEDIA_PLAY_PAUSE: KeyCode.Unknown, // MISSING + VK_MEDIA_LAUNCH_MAIL: KeyCode.Unknown, // MISSING + VK_MEDIA_LAUNCH_MEDIA_SELECT: KeyCode.Unknown, // MISSING + VK_MEDIA_LAUNCH_APP1: KeyCode.Unknown, // MISSING + VK_MEDIA_LAUNCH_APP2: KeyCode.Unknown, // MISSING + VK_OEM_1: KeyCode.US_SEMICOLON, + VK_OEM_PLUS: KeyCode.US_EQUAL, + VK_OEM_COMMA: KeyCode.US_COMMA, + VK_OEM_MINUS: KeyCode.US_MINUS, + VK_OEM_PERIOD: KeyCode.US_DOT, + VK_OEM_2: KeyCode.US_SLASH, + VK_OEM_3: KeyCode.US_BACKTICK, + VK_ABNT_C1: KeyCode.ABNT_C1, + VK_ABNT_C2: KeyCode.ABNT_C2, + VK_OEM_4: KeyCode.US_OPEN_SQUARE_BRACKET, + VK_OEM_5: KeyCode.US_BACKSLASH, + VK_OEM_6: KeyCode.US_CLOSE_SQUARE_BRACKET, + VK_OEM_7: KeyCode.US_QUOTE, + VK_OEM_8: KeyCode.OEM_8, + VK_OEM_102: KeyCode.OEM_102, + VK_PROCESSKEY: KeyCode.Unknown, // MISSING + VK_PACKET: KeyCode.Unknown, // MISSING + VK_DBE_SBCSCHAR: KeyCode.Unknown, // MISSING + VK_DBE_DBCSCHAR: KeyCode.Unknown, // MISSING + VK_ATTN: KeyCode.Unknown, // MISSING + VK_CRSEL: KeyCode.Unknown, // MISSING + VK_EXSEL: KeyCode.Unknown, // MISSING + VK_EREOF: KeyCode.Unknown, // MISSING + VK_PLAY: KeyCode.Unknown, // MISSING + VK_ZOOM: KeyCode.Unknown, // MISSING + VK_NONAME: KeyCode.Unknown, // MISSING + VK_PA1: KeyCode.Unknown, // MISSING + VK_OEM_CLEAR: KeyCode.Unknown, // MISSING + VK_UNKNOWN: KeyCode.Unknown, + }; +} diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts new file mode 100644 index 0000000000..13365d1eac --- /dev/null +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -0,0 +1,588 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { ResolvedKeybinding, Keybinding } from 'vs/base/common/keyCodes'; +import { OS, OperatingSystem } from 'vs/base/common/platform'; +import { toDisposable } from 'vs/base/common/lifecycle'; +import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; +import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; +import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IKeybindingEvent, IUserFriendlyKeybinding, KeybindingSource, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IKeybindingItem, KeybindingsRegistry, IKeybindingRule2 } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { keybindingsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { ConfigWatcher } from 'vs/base/node/config'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import * as dom from 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { KeybindingIO, OutputBuilder, IUserKeybindingItem } from 'vs/workbench/services/keybinding/common/keybindingIO'; +import * as nativeKeymap from 'native-keymap'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { WindowsKeyboardMapper, IWindowsKeyboardMapping, windowsKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; +import { IMacLinuxKeyboardMapping, MacLinuxKeyboardMapper, macLinuxKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; +import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Extensions as ConfigExtensions, IConfigurationRegistry, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { onUnexpectedError } from 'vs/base/common/errors'; + +export class KeyboardMapperFactory { + public static INSTANCE = new KeyboardMapperFactory(); + + private _isISOKeyboard: boolean; + private _layoutInfo: nativeKeymap.IKeyboardLayoutInfo; + private _rawMapping: nativeKeymap.IKeyboardMapping; + private _keyboardMapper: IKeyboardMapper; + private _initialized: boolean; + + private _onDidChangeKeyboardMapper: Emitter = new Emitter(); + public onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; + + private constructor() { + this._isISOKeyboard = false; + this._layoutInfo = null; + this._rawMapping = null; + this._keyboardMapper = null; + this._initialized = false; + } + + public _onKeyboardLayoutChanged(isISOKeyboard: boolean): void { + isISOKeyboard = !!isISOKeyboard; + if (this._initialized) { + this._setKeyboardData(isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } else { + this._isISOKeyboard = isISOKeyboard; + } + } + + public getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { + if (!this._initialized) { + this._setKeyboardData(this._isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + if (dispatchConfig === DispatchConfig.KeyCode) { + // Forcefully set to use keyCode + return new MacLinuxFallbackKeyboardMapper(OS); + } + return this._keyboardMapper; + } + + public getCurrentKeyboardLayout(): nativeKeymap.IKeyboardLayoutInfo { + if (!this._initialized) { + this._setKeyboardData(this._isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + return this._layoutInfo; + } + + private static _isUSStandard(_kbInfo: nativeKeymap.IKeyboardLayoutInfo): boolean { + if (OS === OperatingSystem.Linux) { + const kbInfo = _kbInfo; + return (kbInfo && kbInfo.layout === 'us'); + } + + if (OS === OperatingSystem.Macintosh) { + const kbInfo = _kbInfo; + return (kbInfo && kbInfo.id === 'com.apple.keylayout.US'); + } + + if (OS === OperatingSystem.Windows) { + const kbInfo = _kbInfo; + return (kbInfo && kbInfo.name === '00000409'); + } + + return false; + } + + public getRawKeyboardMapping(): nativeKeymap.IKeyboardMapping { + if (!this._initialized) { + this._setKeyboardData(this._isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + return this._rawMapping; + } + + private _setKeyboardData(isISOKeyboard: boolean, layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): void { + this._layoutInfo = layoutInfo; + + if (this._initialized && this._isISOKeyboard === isISOKeyboard && KeyboardMapperFactory._equals(this._rawMapping, rawMapping)) { + // nothing to do... + return; + } + + this._initialized = true; + this._isISOKeyboard = isISOKeyboard; + this._rawMapping = rawMapping; + this._keyboardMapper = KeyboardMapperFactory._createKeyboardMapper(this._isISOKeyboard, this._layoutInfo, this._rawMapping); + this._onDidChangeKeyboardMapper.fire(); + } + + private static _createKeyboardMapper(isISOKeyboard: boolean, layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): IKeyboardMapper { + const isUSStandard = KeyboardMapperFactory._isUSStandard(layoutInfo); + if (OS === OperatingSystem.Windows) { + return new WindowsKeyboardMapper(isUSStandard, rawMapping); + } + + if (Object.keys(rawMapping).length === 0) { + // Looks like reading the mappings failed (most likely Mac + Japanese/Chinese keyboard layouts) + return new MacLinuxFallbackKeyboardMapper(OS); + } + + if (OS === OperatingSystem.Macintosh) { + const kbInfo = layoutInfo; + if (kbInfo.id === 'com.apple.keylayout.DVORAK-QWERTYCMD') { + // Use keyCode based dispatching for DVORAK - QWERTY ⌘ + return new MacLinuxFallbackKeyboardMapper(OS); + } + } + + return new MacLinuxKeyboardMapper(isISOKeyboard, isUSStandard, rawMapping, OS); + } + + private static _equals(a: nativeKeymap.IKeyboardMapping, b: nativeKeymap.IKeyboardMapping): boolean { + if (OS === OperatingSystem.Windows) { + return windowsKeyboardMappingEquals(a, b); + } + + return macLinuxKeyboardMappingEquals(a, b); + } +} + +interface ContributedKeyBinding { + command: string; + key: string; + when?: string; + mac?: string; + linux?: string; + win?: string; +} + +function isContributedKeyBindingsArray(thing: ContributedKeyBinding | ContributedKeyBinding[]): thing is ContributedKeyBinding[] { + return Array.isArray(thing); +} + +function isValidContributedKeyBinding(keyBinding: ContributedKeyBinding, rejects: string[]): boolean { + if (!keyBinding) { + rejects.push(nls.localize('nonempty', "expected non-empty value.")); + return false; + } + if (typeof keyBinding.command !== 'string') { + rejects.push(nls.localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'command')); + return false; + } + if (typeof keyBinding.key !== 'string') { + rejects.push(nls.localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'key')); + return false; + } + if (keyBinding.when && typeof keyBinding.when !== 'string') { + rejects.push(nls.localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when')); + return false; + } + if (keyBinding.mac && typeof keyBinding.mac !== 'string') { + rejects.push(nls.localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'mac')); + return false; + } + if (keyBinding.linux && typeof keyBinding.linux !== 'string') { + rejects.push(nls.localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'linux')); + return false; + } + if (keyBinding.win && typeof keyBinding.win !== 'string') { + rejects.push(nls.localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'win')); + return false; + } + return true; +} + +let keybindingType: IJSONSchema = { + type: 'object', + default: { command: '', key: '' }, + properties: { + command: { + description: nls.localize('vscode.extension.contributes.keybindings.command', 'Identifier of the command to run when keybinding is triggered.'), + type: 'string' + }, + key: { + description: nls.localize('vscode.extension.contributes.keybindings.key', 'Key or key sequence (separate keys with plus-sign and sequences with space, e.g Ctrl+O and Ctrl+L L for a chord).'), + type: 'string' + }, + mac: { + description: nls.localize('vscode.extension.contributes.keybindings.mac', 'Mac specific key or key sequence.'), + type: 'string' + }, + linux: { + description: nls.localize('vscode.extension.contributes.keybindings.linux', 'Linux specific key or key sequence.'), + type: 'string' + }, + win: { + description: nls.localize('vscode.extension.contributes.keybindings.win', 'Windows specific key or key sequence.'), + type: 'string' + }, + when: { + description: nls.localize('vscode.extension.contributes.keybindings.when', 'Condition when the key is active.'), + type: 'string' + } + } +}; + +let keybindingsExtPoint = ExtensionsRegistry.registerExtensionPoint('keybindings', [], { + description: nls.localize('vscode.extension.contributes.keybindings', "Contributes keybindings."), + oneOf: [ + keybindingType, + { + type: 'array', + items: keybindingType + } + ] +}); + +export const enum DispatchConfig { + Code, + KeyCode +} + +function getDispatchConfig(configurationService: IConfigurationService): DispatchConfig { + const keyboard = configurationService.getConfiguration('keyboard'); + const r = (keyboard ? (keyboard).dispatch : null); + return (r === 'keyCode' ? DispatchConfig.KeyCode : DispatchConfig.Code); +} + +export class WorkbenchKeybindingService extends AbstractKeybindingService { + + private _keyboardMapper: IKeyboardMapper; + private _cachedResolver: KeybindingResolver; + private _firstTimeComputingResolver: boolean; + private userKeybindings: ConfigWatcher; + + constructor( + windowElement: Window, + @IContextKeyService contextKeyService: IContextKeyService, + @ICommandService commandService: ICommandService, + @ITelemetryService private telemetryService: ITelemetryService, + @IMessageService messageService: IMessageService, + @IEnvironmentService environmentService: IEnvironmentService, + @IStatusbarService statusBarService: IStatusbarService, + @IConfigurationService configurationService: IConfigurationService + ) { + super(contextKeyService, commandService, messageService, statusBarService); + + let dispatchConfig = getDispatchConfig(configurationService); + configurationService.onDidUpdateConfiguration((e) => { + let newDispatchConfig = getDispatchConfig(configurationService); + if (dispatchConfig === newDispatchConfig) { + return; + } + + dispatchConfig = newDispatchConfig; + this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); + this.updateResolver({ source: KeybindingSource.Default }); + }); + + this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); + KeyboardMapperFactory.INSTANCE.onDidChangeKeyboardMapper(() => { + this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); + this.updateResolver({ source: KeybindingSource.Default }); + }); + + this._cachedResolver = null; + this._firstTimeComputingResolver = true; + + this.userKeybindings = new ConfigWatcher(environmentService.appKeybindingsPath, { defaultConfig: [], onError: error => onUnexpectedError(error) }); + this.toDispose.push(toDisposable(() => this.userKeybindings.dispose())); + + keybindingsExtPoint.setHandler((extensions) => { + let commandAdded = false; + + for (let extension of extensions) { + commandAdded = this._handleKeybindingsExtensionPointUser(extension.description.isBuiltin, extension.value, extension.collector) || commandAdded; + } + + if (commandAdded) { + this.updateResolver({ source: KeybindingSource.Default }); + } + }); + + this.toDispose.push(this.userKeybindings.onDidUpdateConfiguration(event => this.updateResolver({ + source: KeybindingSource.User, + keybindings: event.config + }))); + + this.toDispose.push(dom.addDisposableListener(windowElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + let keyEvent = new StandardKeyboardEvent(e); + let shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target); + if (shouldPreventDefault) { + keyEvent.preventDefault(); + } + })); + + keybindingsTelemetry(telemetryService, this); + let data = KeyboardMapperFactory.INSTANCE.getCurrentKeyboardLayout(); + telemetryService.publicLog('keyboardLayout', { + currentKeyboardLayout: data + }); + } + + public dumpDebugInfo(): string { + const layoutInfo = JSON.stringify(KeyboardMapperFactory.INSTANCE.getCurrentKeyboardLayout(), null, '\t'); + const mapperInfo = this._keyboardMapper.dumpDebugInfo(); + const rawMapping = JSON.stringify(KeyboardMapperFactory.INSTANCE.getRawKeyboardMapping(), null, '\t'); + return `Layout info:\n${layoutInfo}\n${mapperInfo}\n\nRaw mapping:\n${rawMapping}`; + } + + private _safeGetConfig(): IUserFriendlyKeybinding[] { + let rawConfig = this.userKeybindings.getConfig(); + if (Array.isArray(rawConfig)) { + return rawConfig; + } + return []; + } + + public customKeybindingsCount(): number { + let userKeybindings = this._safeGetConfig(); + + return userKeybindings.length; + } + + private updateResolver(event: IKeybindingEvent): void { + this._cachedResolver = null; + this._onDidUpdateKeybindings.fire(event); + } + + protected _getResolver(): KeybindingResolver { + if (!this._cachedResolver) { + const defaults = this._resolveKeybindingItems(KeybindingsRegistry.getDefaultKeybindings(), true); + const overrides = this._resolveUserKeybindingItems(this._getExtraKeybindings(this._firstTimeComputingResolver), false); + this._cachedResolver = new KeybindingResolver(defaults, overrides); + this._firstTimeComputingResolver = false; + } + return this._cachedResolver; + } + + private _resolveKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { + let result: ResolvedKeybindingItem[] = [], resultLen = 0; + for (let i = 0, len = items.length; i < len; i++) { + const item = items[i]; + const when = (item.when ? item.when.normalize() : null); + const keybinding = item.keybinding; + if (!keybinding) { + // This might be a removal keybinding item in user settings => accept it + result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault); + } else { + const resolvedKeybindings = this.resolveKeybinding(keybinding); + for (let j = 0; j < resolvedKeybindings.length; j++) { + result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybindings[j], item.command, item.commandArgs, when, isDefault); + } + } + } + + return result; + } + + private _resolveUserKeybindingItems(items: IUserKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { + let result: ResolvedKeybindingItem[] = [], resultLen = 0; + for (let i = 0, len = items.length; i < len; i++) { + const item = items[i]; + const when = (item.when ? item.when.normalize() : null); + const firstPart = item.firstPart; + const chordPart = item.chordPart; + if (!firstPart) { + // This might be a removal keybinding item in user settings => accept it + result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault); + } else { + const resolvedKeybindings = this._keyboardMapper.resolveUserBinding(firstPart, chordPart); + for (let j = 0; j < resolvedKeybindings.length; j++) { + result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybindings[j], item.command, item.commandArgs, when, isDefault); + } + } + } + + return result; + } + + private _getExtraKeybindings(isFirstTime: boolean): IUserKeybindingItem[] { + let extraUserKeybindings: IUserFriendlyKeybinding[] = this._safeGetConfig(); + if (!isFirstTime) { + let cnt = extraUserKeybindings.length; + + this.telemetryService.publicLog('customKeybindingsChanged', { + keyCount: cnt + }); + } + + return extraUserKeybindings.map((k) => KeybindingIO.readUserKeybindingItem(k, OS)); + } + + public resolveKeybinding(kb: Keybinding): ResolvedKeybinding[] { + return this._keyboardMapper.resolveKeybinding(kb); + } + + public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding { + return this._keyboardMapper.resolveKeyboardEvent(keyboardEvent); + } + + public resolveUserBinding(userBinding: string): ResolvedKeybinding[] { + const [firstPart, chordPart] = KeybindingIO._readUserBinding(userBinding); + return this._keyboardMapper.resolveUserBinding(firstPart, chordPart); + } + + private _handleKeybindingsExtensionPointUser(isBuiltin: boolean, keybindings: ContributedKeyBinding | ContributedKeyBinding[], collector: ExtensionMessageCollector): boolean { + if (isContributedKeyBindingsArray(keybindings)) { + let commandAdded = false; + for (let i = 0, len = keybindings.length; i < len; i++) { + commandAdded = this._handleKeybinding(isBuiltin, i + 1, keybindings[i], collector) || commandAdded; + } + return commandAdded; + } else { + return this._handleKeybinding(isBuiltin, 1, keybindings, collector); + } + } + + private _handleKeybinding(isBuiltin: boolean, idx: number, keybindings: ContributedKeyBinding, collector: ExtensionMessageCollector): boolean { + + let rejects: string[] = []; + let commandAdded = false; + + if (isValidContributedKeyBinding(keybindings, rejects)) { + let rule = this._asCommandRule(isBuiltin, idx++, keybindings); + if (rule) { + KeybindingsRegistry.registerKeybindingRule2(rule); + commandAdded = true; + } + } + + if (rejects.length > 0) { + collector.error(nls.localize( + 'invalid.keybindings', + "Invalid `contributes.{0}`: {1}", + keybindingsExtPoint.name, + rejects.join('\n') + )); + } + + return commandAdded; + } + + private _asCommandRule(isBuiltin: boolean, idx: number, binding: ContributedKeyBinding): IKeybindingRule2 { + + let { command, when, key, mac, linux, win } = binding; + + let weight: number; + if (isBuiltin) { + weight = KeybindingsRegistry.WEIGHT.builtinExtension(idx); + } else { + weight = KeybindingsRegistry.WEIGHT.externalExtension(idx); + } + + let desc = { + id: command, + when: ContextKeyExpr.deserialize(when), + weight: weight, + primary: KeybindingIO.readKeybinding(key, OS), + mac: mac && { primary: KeybindingIO.readKeybinding(mac, OS) }, + linux: linux && { primary: KeybindingIO.readKeybinding(linux, OS) }, + win: win && { primary: KeybindingIO.readKeybinding(win, OS) } + }; + + if (!desc.primary && !desc.mac && !desc.linux && !desc.win) { + return undefined; + } + + return desc; + } + + public getDefaultKeybindingsContent(): string { + const resolver = this._getResolver(); + const defaultKeybindings = resolver.getDefaultKeybindings(); + const boundCommands = resolver.getDefaultBoundCommands(); + return ( + WorkbenchKeybindingService._getDefaultKeybindings(defaultKeybindings) + + '\n\n' + + WorkbenchKeybindingService._getAllCommandsAsComment(boundCommands) + ); + } + + private static _getDefaultKeybindings(defaultKeybindings: ResolvedKeybindingItem[]): string { + let out = new OutputBuilder(); + out.writeLine('['); + + let lastIndex = defaultKeybindings.length - 1; + defaultKeybindings.forEach((k, index) => { + KeybindingIO.writeKeybindingItem(out, k, OS); + if (index !== lastIndex) { + out.writeLine(','); + } else { + out.writeLine(); + } + }); + out.writeLine(']'); + return out.toString(); + } + + private static _getAllCommandsAsComment(boundCommands: Map): string { + const unboundCommands = KeybindingResolver.getAllUnboundCommands(boundCommands); + let pretty = unboundCommands.sort().join('\n// - '); + return '// ' + nls.localize('unboundCommands', "Here are other available commands: ") + '\n// - ' + pretty; + } +} + +let schemaId = 'vscode://schemas/keybindings'; +let schema: IJSONSchema = { + 'id': schemaId, + 'type': 'array', + 'title': nls.localize('keybindings.json.title', "Keybindings configuration"), + 'items': { + 'required': ['key'], + 'type': 'object', + 'defaultSnippets': [{ 'body': { 'key': '$1', 'command': '$2', 'when': '$3' } }], + 'properties': { + 'key': { + 'type': 'string', + 'description': nls.localize('keybindings.json.key', "Key or key sequence (separated by space)"), + }, + 'command': { + 'description': nls.localize('keybindings.json.command', "Name of the command to execute"), + }, + 'when': { + 'type': 'string', + 'description': nls.localize('keybindings.json.when', "Condition when the key is active.") + }, + 'args': { + 'description': nls.localize('keybindings.json.args', "Arguments to pass to the command to execute.") + } + } + } +}; + +let schemaRegistry = Registry.as(Extensions.JSONContribution); +schemaRegistry.registerSchema(schemaId, schema); + +if (OS === OperatingSystem.Macintosh || OS === OperatingSystem.Linux) { + + const configurationRegistry = Registry.as(ConfigExtensions.Configuration); + const keyboardConfiguration: IConfigurationNode = { + 'id': 'keyboard', + 'order': 15, + 'type': 'object', + 'title': nls.localize('keyboardConfigurationTitle', "Keyboard"), + 'overridable': true, + 'properties': { + 'keyboard.dispatch': { + 'type': 'string', + 'enum': ['code', 'keyCode'], + 'default': 'code', + 'description': nls.localize('dispatch', "Controls the dispatching logic for key presses to use either `keydown.code` (recommended) or `keydown.keyCode`.") + } + } + }; + + configurationRegistry.registerConfiguration(keyboardConfiguration); + +} diff --git a/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts new file mode 100644 index 0000000000..46047eabaf --- /dev/null +++ b/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import Severity from 'vs/base/common/severity'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { ILifecycleService, ShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, handleVetos } from 'vs/platform/lifecycle/common/lifecycle'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ipcRenderer as ipc } from 'electron'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IWindowService } from 'vs/platform/windows/common/windows'; + +export class LifecycleService implements ILifecycleService { + + private static readonly _lastShutdownReasonKey = 'lifecyle.lastShutdownReason'; + + public _serviceBrand: any; + + private readonly _onDidChangePhase = new Emitter(); + private readonly _onWillShutdown = new Emitter(); + private readonly _onShutdown = new Emitter(); + private readonly _startupKind: StartupKind; + + private _phase: LifecyclePhase = LifecyclePhase.Starting; + + constructor( + @IMessageService private _messageService: IMessageService, + @IWindowService private _windowService: IWindowService, + @IStorageService private _storageService: IStorageService + ) { + this._registerListeners(); + + const lastShutdownReason = this._storageService.getInteger(LifecycleService._lastShutdownReasonKey, StorageScope.WORKSPACE); + this._storageService.remove(LifecycleService._lastShutdownReasonKey, StorageScope.WORKSPACE); + if (lastShutdownReason === ShutdownReason.RELOAD) { + this._startupKind = StartupKind.ReloadedWindow; + } else if (lastShutdownReason === ShutdownReason.LOAD) { + this._startupKind = StartupKind.ReopenedWindow; + } else { + this._startupKind = StartupKind.NewWindow; + } + } + + public get phase(): LifecyclePhase { + return this._phase; + } + + public set phase(value: LifecyclePhase) { + if (this._phase !== value) { + this._phase = value; + this._onDidChangePhase.fire(value); + } + } + + public get startupKind(): StartupKind { + return this._startupKind; + } + + public get onDidChangePhase(): Event { + return this._onDidChangePhase.event; + } + + public get onWillShutdown(): Event { + return this._onWillShutdown.event; + } + + public get onShutdown(): Event { + return this._onShutdown.event; + } + + private _registerListeners(): void { + const windowId = this._windowService.getCurrentWindowId(); + + // Main side indicates that window is about to unload, check for vetos + ipc.on('vscode:beforeUnload', (event, reply: { okChannel: string, cancelChannel: string, reason: ShutdownReason, payload: object }) => { + this.phase = LifecyclePhase.ShuttingDown; + this._storageService.store(LifecycleService._lastShutdownReasonKey, JSON.stringify(reply.reason), StorageScope.WORKSPACE); + + // trigger onWillShutdown events and veto collecting + this.onBeforeUnload(reply.reason, reply.payload).done(veto => { + if (veto) { + this._storageService.remove(LifecycleService._lastShutdownReasonKey, StorageScope.WORKSPACE); + this.phase = LifecyclePhase.Running; // reset this flag since the shutdown has been vetoed! + ipc.send(reply.cancelChannel, windowId); + } else { + this._onShutdown.fire(reply.reason); + ipc.send(reply.okChannel, windowId); + } + }); + }); + } + + private onBeforeUnload(reason: ShutdownReason, payload?: object): TPromise { + const vetos: (boolean | TPromise)[] = []; + + this._onWillShutdown.fire({ + veto(value) { + vetos.push(value); + }, + reason, + payload + }); + + return handleVetos(vetos, err => this._messageService.show(Severity.Error, toErrorMessage(err))); + } +} diff --git a/src/vs/workbench/services/message/browser/media/messageList.css b/src/vs/workbench/services/message/browser/media/messageList.css new file mode 100644 index 0000000000..5ced532fb1 --- /dev/null +++ b/src/vs/workbench/services/message/browser/media/messageList.css @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.global-message-list { + font-size: 12px; + position: absolute; + z-index: 300; + list-style-type: none; + line-height: 35px; + margin: 0; + padding: 0; + width: 70%; + left: 15%; +} + +.hc-black .global-message-list { + outline: 2px solid; + outline-offset: -2px; + box-shadow: none; +} + +.global-message-list.transition { + -webkit-transition: top 200ms linear; + -ms-transition: top 200ms linear; + -moz-transition: top 200ms linear; + -khtml-transition: top 200ms linear; + -o-transition: top 200ms linear; + transition: top 200ms linear; +} + +.global-message-list ul.message-list { + margin: 0; + padding: 0; +} + +.global-message-list li.message-list-entry { + padding-left: 10px; + padding-right: 10px; + overflow: hidden; + -webkit-box-sizing: border-box; + -o-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; /* Important so that borders added don't add to the overal size */ + height: 35px; +} + +.global-message-list li.message-list-entry-with-action { + padding-right: 0; +} + +.global-message-list li.message-list-entry .message-left-side { + -webkit-user-select: text; + -ms-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -o-user-select: text; + user-select: text; +} + +.global-message-list li.message-list-entry .message-left-side.severity { + padding: 2px 4px; + margin-right: 10px; + font-weight: bold; + font-size: 11px; + -webkit-user-select: none; + -ms-user-select: none; + -khtml-user-select: none; + -moz-user-select: -moz-none; + -o-user-select: none; + user-select: none; +} + +.global-message-list li.message-list-entry .message-left-side.message-overflow-ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: block; +} + +.global-message-list li.message-list-entry .actions-container { + float: right; +} + +.global-message-list li.message-list-entry .actions-container .message-action { + display: inline-block; +} + +.global-message-list li.message-list-entry .actions-container .message-action .action-button { + padding-left: 10px; + padding-right: 10px; + display: inline-block; + color: inherit; + text-decoration: none; + cursor: pointer; +} + +.hc-black .global-message-list li.message-list-entry .message-left-side.severity { + border: 1px solid; +} + +.hc-black .global-message-list li.message-list-entry .actions-container .message-action .action-button { + border: 1px solid; +} + +.hc-black .global-message-list li.message-list-entry .actions-container .message-action .action-button:focus { + outline-offset: -4px; /* Helps high-contrast outline avoid clipping. */ +} + +/* TODO@theme */ + +.vs .global-message-list li.message-list-entry .actions-container .message-action .action-button:focus, +.vs-dark .global-message-list li.message-list-entry .actions-container .message-action .action-button:focus { + outline-color: rgba(255, 255, 255, .5); /* buttons have a blue color, so focus indication needs to be different */ +} \ No newline at end of file diff --git a/src/vs/workbench/services/message/browser/messageList.ts b/src/vs/workbench/services/message/browser/messageList.ts new file mode 100644 index 0000000000..2271cd9696 --- /dev/null +++ b/src/vs/workbench/services/message/browser/messageList.ts @@ -0,0 +1,490 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/messageList'; +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { Builder, $ } from 'vs/base/browser/builder'; +import DOM = require('vs/base/browser/dom'); +import * as browser from 'vs/base/browser/browser'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import aria = require('vs/base/browser/ui/aria/aria'); +import types = require('vs/base/common/types'); +import Event, { Emitter } from 'vs/base/common/event'; +import { Action } from 'vs/base/common/actions'; +import htmlRenderer = require('vs/base/browser/htmlContentRenderer'); +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { NOTIFICATIONS_FOREGROUND, NOTIFICATIONS_BACKGROUND, NOTIFICATIONS_BUTTON_BACKGROUND, NOTIFICATIONS_BUTTON_HOVER_BACKGROUND, NOTIFICATIONS_BUTTON_FOREGROUND, NOTIFICATIONS_INFO_BACKGROUND, NOTIFICATIONS_WARNING_BACKGROUND, NOTIFICATIONS_ERROR_BACKGROUND, NOTIFICATIONS_INFO_FOREGROUND, NOTIFICATIONS_WARNING_FOREGROUND, NOTIFICATIONS_ERROR_FOREGROUND } from 'vs/workbench/common/theme'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Color } from 'vs/base/common/color'; + +export enum Severity { + Info, + Warning, + Error +} + +export interface IMessageWithAction { + message: string; + actions: Action[]; + source: string; +} + +interface IMessageEntry { + id: any; + text: string; + source: string; + severity: Severity; + time: number; + count?: number; + actions: Action[]; + onHide: () => void; +} + +export class IMessageListOptions { + purgeInterval: number; + maxMessages: number; + maxMessageLength: number; +} + +const DEFAULT_MESSAGE_LIST_OPTIONS = { + purgeInterval: 10000, + maxMessages: 5, + maxMessageLength: 500 +}; + +export class MessageList { + private messages: IMessageEntry[]; + private messageListPurger: TPromise; + private messageListContainer: Builder; + + private container: HTMLElement; + private options: IMessageListOptions; + + private _onMessagesShowing: Emitter; + private _onMessagesCleared: Emitter; + + private toDispose: IDisposable[]; + + private background = Color.fromHex('#333333'); + private foreground = Color.fromHex('#EEEEEE'); + private widgetShadow = Color.fromHex('#000000'); + private outlineBorder: Color; + private buttonBackground = Color.fromHex('#0E639C'); + private buttonForeground = this.foreground; + private infoBackground = Color.fromHex('#007ACC'); + private infoForeground = this.foreground; + private warningBackground = Color.fromHex('#B89500'); + private warningForeground = this.foreground; + private errorBackground = Color.fromHex('#BE1100'); + private errorForeground = this.foreground; + + constructor( + container: HTMLElement, + private telemetryService: ITelemetryService, + options: IMessageListOptions = DEFAULT_MESSAGE_LIST_OPTIONS + ) { + this.toDispose = []; + this.messages = []; + this.messageListPurger = null; + this.container = container; + this.options = options; + + this._onMessagesShowing = new Emitter(); + this._onMessagesCleared = new Emitter(); + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(browser.onDidChangeFullscreen(() => this.positionMessageList())); + this.toDispose.push(browser.onDidChangeZoomLevel(() => this.positionMessageList())); + this.toDispose.push(registerThemingParticipant((theme, collector) => { + this.background = theme.getColor(NOTIFICATIONS_BACKGROUND); + this.foreground = theme.getColor(NOTIFICATIONS_FOREGROUND); + this.widgetShadow = theme.getColor(widgetShadow); + this.outlineBorder = theme.getColor(contrastBorder); + this.buttonBackground = theme.getColor(NOTIFICATIONS_BUTTON_BACKGROUND); + this.buttonForeground = theme.getColor(NOTIFICATIONS_BUTTON_FOREGROUND); + this.infoBackground = theme.getColor(NOTIFICATIONS_INFO_BACKGROUND); + this.infoForeground = theme.getColor(NOTIFICATIONS_INFO_FOREGROUND); + this.warningBackground = theme.getColor(NOTIFICATIONS_WARNING_BACKGROUND); + this.warningForeground = theme.getColor(NOTIFICATIONS_WARNING_FOREGROUND); + this.errorBackground = theme.getColor(NOTIFICATIONS_ERROR_BACKGROUND); + this.errorForeground = theme.getColor(NOTIFICATIONS_ERROR_FOREGROUND); + + const buttonHoverBackgroundColor = theme.getColor(NOTIFICATIONS_BUTTON_HOVER_BACKGROUND); + if (buttonHoverBackgroundColor) { + collector.addRule(`.global-message-list li.message-list-entry .actions-container .message-action .action-button:hover { background-color: ${buttonHoverBackgroundColor} !important; }`); + } + + this.updateStyles(); + })); + } + + public get onMessagesShowing(): Event { + return this._onMessagesShowing.event; + } + + public get onMessagesCleared(): Event { + return this._onMessagesCleared.event; + } + + public updateStyles(): void { + if (this.messageListContainer) { + this.messageListContainer.style('background-color', this.background ? this.background.toString() : null); + this.messageListContainer.style('color', this.foreground ? this.foreground.toString() : null); + this.messageListContainer.style('outline-color', this.outlineBorder ? this.outlineBorder.toString() : null); + this.messageListContainer.style('box-shadow', this.widgetShadow ? `0 5px 8px ${this.widgetShadow}` : null); + } + } + + public showMessage(severity: Severity, message: string, onHide?: () => void): () => void; + public showMessage(severity: Severity, message: Error, onHide?: () => void): () => void; + public showMessage(severity: Severity, message: string[], onHide?: () => void): () => void; + public showMessage(severity: Severity, message: Error[], onHide?: () => void): () => void; + public showMessage(severity: Severity, message: IMessageWithAction, onHide?: () => void): () => void; + public showMessage(severity: Severity, message: any, onHide?: () => void): () => void { + if (Array.isArray(message)) { + const closeFns: Function[] = []; + message.forEach((msg: any) => closeFns.push(this.showMessage(severity, msg, onHide))); + + return () => closeFns.forEach((fn) => fn()); + } + + // Return only if we are unable to extract a message text + const messageText = this.getMessageText(message); + if (!messageText || typeof messageText !== 'string') { + return () => {/* empty */ }; + } + + // Show message + return this.doShowMessage(message, messageText, severity, onHide); + } + + private getMessageText(message: any): string { + if (types.isString(message)) { + return message; + } + + if (message instanceof Error) { + return toErrorMessage(message, false); + } + + if (message && (message).message) { + return (message).message; + } + + return null; + } + + private doShowMessage(id: string, message: string, severity: Severity, onHide: () => void): () => void; + private doShowMessage(id: Error, message: string, severity: Severity, onHide: () => void): () => void; + private doShowMessage(id: IMessageWithAction, message: string, severity: Severity, onHide: () => void): () => void; + private doShowMessage(id: any, message: string, severity: Severity, onHide: () => void): () => void { + + // Trigger Auto-Purge of messages to keep list small + this.purgeMessages(); + + // Store in Memory (new messages come first so that they show up on top) + this.messages.unshift({ + id: id, + text: message, + severity: severity, + time: Date.now(), + actions: (id).actions, + source: (id).source, + onHide + }); + + // Render + this.renderMessages(true, 1); + + // Support in Screen Readers too + let alertText: string; + if (severity === Severity.Error) { + alertText = nls.localize('alertErrorMessage', "Error: {0}", message); + } else if (severity === Severity.Warning) { + alertText = nls.localize('alertWarningMessage', "Warning: {0}", message); + } else { + alertText = nls.localize('alertInfoMessage', "Info: {0}", message); + } + + aria.alert(alertText); + + return () => this.hideMessage(id); + } + + private renderMessages(animate: boolean, delta: number): void { + const container = $(this.container); + + // Lazily create, otherwise clear old + if (!this.messageListContainer) { + this.messageListContainer = $('.global-message-list').appendTo(container); + } else { + $(this.messageListContainer).empty(); + $(this.messageListContainer).removeClass('transition'); + } + + // Support animation for messages by moving the container out of view and then in + if (animate) { + $(this.messageListContainer).style('top', '-35px'); + } + + // Render Messages as List Items + $(this.messageListContainer).ul({ 'class': 'message-list' }, ul => { + const messages = this.prepareMessages(); + if (messages.length > 0) { + this._onMessagesShowing.fire(); + } else { + this._onMessagesCleared.fire(); + } + + messages.forEach((message: IMessageEntry, index: number) => { + this.renderMessage(message, $(ul), messages.length, delta); + }); + + // Support animation for messages by moving the container out of view and then in + if (animate) { + setTimeout(() => { + this.positionMessageList(); + $(this.messageListContainer).addClass('transition'); + }, 50 /* Need this delay to reliably get the animation on some browsers */); + } + }); + + // Styles + this.updateStyles(); + } + + private positionMessageList(animate?: boolean): void { + if (!this.messageListContainer) { + return; // not yet created + } + + $(this.messageListContainer).removeClass('transition'); // disable any animations + + let position = 0; + if (!browser.isFullscreen() && DOM.hasClass(this.container, 'titlebar-style-custom')) { + position = 22 / browser.getZoomFactor(); // adjust the position based on title bar size and zoom factor + } + + $(this.messageListContainer).style('top', `${position}px`); + } + + private renderMessage(message: IMessageEntry, container: Builder, total: number, delta: number): void { + container.li({ class: 'message-list-entry message-list-entry-with-action' }, li => { + + // Actions (if none provided, add one default action to hide message) + const messageActions = this.getMessageActions(message); + li.div({ class: 'actions-container' }, actionContainer => { + for (let i = 0; i < messageActions.length; i++) { + const action = messageActions[i]; + actionContainer.div({ class: 'message-action' }, div => { + div.a({ class: 'action-button', tabindex: '0', role: 'button' }) + .style('border-color', this.outlineBorder ? this.outlineBorder.toString() : null) + .style('background-color', this.buttonBackground ? this.buttonBackground.toString() : null) + .style('color', this.buttonForeground ? this.buttonForeground.toString() : null) + .text(action.label) + .on([DOM.EventType.CLICK, DOM.EventType.KEY_DOWN], e => { + if (e instanceof KeyboardEvent) { + const event = new StandardKeyboardEvent(e); + if (!event.equals(KeyCode.Enter) && !event.equals(KeyCode.Space)) { + return; // Only handle Enter/Escape for keyboard access + } + } + + DOM.EventHelper.stop(e, true); + + this.telemetryService.publicLog('workbenchActionExecuted', { id: action.id, from: 'message' }); + + (action.run() || TPromise.as(null)) + .then(null, error => this.showMessage(Severity.Error, error)) + .done(r => { + if (typeof r === 'boolean' && r === false) { + return; + } + + this.hideMessage(message.text); // hide all matching the text since there may be duplicates + }); + }); + }); + } + }); + + // Text + const text = message.text.substr(0, this.options.maxMessageLength); + li.div({ class: 'message-left-side' }, div => { + div.addClass('message-overflow-ellipsis'); + + // Severity indicator + const sev = message.severity; + const label = (sev === Severity.Error) ? nls.localize('error', "Error") : (sev === Severity.Warning) ? nls.localize('warning', "Warn") : nls.localize('info', "Info"); + const color = (sev === Severity.Error) ? this.errorBackground : (sev === Severity.Warning) ? this.warningBackground : this.infoBackground; + const foregroundColor = (sev === Severity.Error) ? this.errorForeground : (sev === Severity.Warning) ? this.warningForeground : this.infoForeground; + const sevLabel = $().span({ class: `message-left-side severity ${sev === Severity.Error ? 'app-error' : sev === Severity.Warning ? 'app-warning' : 'app-info'}`, text: label }); + sevLabel.style('border-color', this.outlineBorder ? this.outlineBorder.toString() : null); + sevLabel.style('background-color', color ? color.toString() : null); + sevLabel.style('color', foregroundColor ? foregroundColor.toString() : null); + sevLabel.appendTo(div); + + // Error message + const messageContentElement = htmlRenderer.renderFormattedText(text, { + inline: true, + className: 'message-left-side', + }); + + // Hover title + const title = message.source ? `[${message.source}] ${messageContentElement.textContent}` : messageContentElement.textContent; + + sevLabel.title(title); + + $(messageContentElement as HTMLElement).title(title).appendTo(div); + }); + }); + } + + private getMessageActions(message: IMessageEntry): Action[] { + let messageActions: Action[]; + if (message.actions && message.actions.length > 0) { + messageActions = message.actions; + } else { + messageActions = [ + new Action('close.message.action', nls.localize('close', "Close"), null, true, () => { + this.hideMessage(message.text); // hide all matching the text since there may be duplicates + + return TPromise.as(true); + }) + ]; + } + + return messageActions; + } + + private prepareMessages(): IMessageEntry[] { + + // Aggregate Messages by text to reduce their count + const messages: IMessageEntry[] = []; + const handledMessages: { [message: string]: number; } = {}; + + let offset = 0; + for (let i = 0; i < this.messages.length; i++) { + const message = this.messages[i]; + if (types.isUndefinedOrNull(handledMessages[message.text])) { + message.count = 1; + messages.push(message); + handledMessages[message.text] = offset++; + } else { + messages[handledMessages[message.text]].count++; + } + } + + if (messages.length > this.options.maxMessages) { + return messages.splice(messages.length - this.options.maxMessages, messages.length); + } + + return messages; + } + + private disposeMessages(messages: IMessageEntry[]): void { + messages.forEach(message => { + if (message.onHide) { + message.onHide(); + } + + if (message.actions) { + message.actions.forEach(action => { + action.dispose(); + }); + } + }); + } + + public hideMessages(): void { + this.hideMessage(); + } + + public show(): void { + if (this.messageListContainer && this.messageListContainer.isHidden()) { + this.messageListContainer.show(); + } + } + + public hide(): void { + if (this.messageListContainer && !this.messageListContainer.isHidden()) { + this.messageListContainer.hide(); + } + } + + private hideMessage(messageText?: string): void; + private hideMessage(messageObj?: any): void { + let messageFound = false; + + for (let i = 0; i < this.messages.length; i++) { + const message = this.messages[i]; + let hide = false; + + // Hide specific message + if (messageObj) { + hide = ((types.isString(messageObj) && message.text === messageObj) || message.id === messageObj); + } + + // Hide all messages + else { + hide = true; + } + + if (hide) { + this.disposeMessages(this.messages.splice(i, 1)); + i--; + messageFound = true; + } + } + + if (messageFound) { + this.renderMessages(false, -1); + } + } + + private purgeMessages(): void { + + // Cancel previous + if (this.messageListPurger) { + this.messageListPurger.cancel(); + } + + // Configure + this.messageListPurger = TPromise.timeout(this.options.purgeInterval).then(() => { + let needsUpdate = false; + let counter = 0; + + for (let i = 0; i < this.messages.length; i++) { + const message = this.messages[i]; + + // Only purge infos and warnings and only if they are not providing actions + if (message.severity !== Severity.Error && !message.actions) { + this.disposeMessages(this.messages.splice(i, 1)); + counter--; + i--; + needsUpdate = true; + } + } + + if (needsUpdate) { + this.renderMessages(false, counter); + } + }); + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } +} diff --git a/src/vs/workbench/services/message/browser/messageService.ts b/src/vs/workbench/services/message/browser/messageService.ts new file mode 100644 index 0000000000..056b773b67 --- /dev/null +++ b/src/vs/workbench/services/message/browser/messageService.ts @@ -0,0 +1,150 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import errors = require('vs/base/common/errors'); +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import types = require('vs/base/common/types'); +import { MessageList, Severity as BaseSeverity } from 'vs/workbench/services/message/browser/messageList'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IMessageService, IMessageWithAction, IConfirmation, Severity } from 'vs/platform/message/common/message'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import Event from 'vs/base/common/event'; + +interface IBufferedMessage { + severity: Severity; + message: any; + onHide: () => void; + disposeFn: () => void; +} + +export class WorkbenchMessageService implements IMessageService { + + public _serviceBrand: any; + + private handler: MessageList; + private toDispose: IDisposable[]; + + private canShowMessages: boolean; + private messageBuffer: IBufferedMessage[]; + + constructor( + container: HTMLElement, + telemetryService: ITelemetryService + ) { + this.handler = new MessageList(container, telemetryService); + this.messageBuffer = []; + this.canShowMessages = true; + this.toDispose = [this.handler]; + } + + public get onMessagesShowing(): Event { + return this.handler.onMessagesShowing; + } + + public get onMessagesCleared(): Event { + return this.handler.onMessagesCleared; + } + + public suspend(): void { + this.canShowMessages = false; + this.handler.hide(); + } + + public resume(): void { + this.canShowMessages = true; + this.handler.show(); + + // Release messages from buffer + while (this.messageBuffer.length) { + const bufferedMessage = this.messageBuffer.pop(); + bufferedMessage.disposeFn = this.show(bufferedMessage.severity, bufferedMessage.message, bufferedMessage.onHide); + } + } + + private toBaseSeverity(severity: Severity): BaseSeverity { + switch (severity) { + case Severity.Info: + return BaseSeverity.Info; + + case Severity.Warning: + return BaseSeverity.Warning; + } + + return BaseSeverity.Error; + } + + public show(sev: Severity, message: string, onHide?: () => void): () => void; + public show(sev: Severity, message: Error, onHide?: () => void): () => void; + public show(sev: Severity, message: string[], onHide?: () => void): () => void; + public show(sev: Severity, message: Error[], onHide?: () => void): () => void; + public show(sev: Severity, message: IMessageWithAction, onHide?: () => void): () => void; + public show(sev: Severity, message: any, onHide?: () => void): () => void { + if (!message) { + return () => void 0; // guard against undefined messages + } + + if (Array.isArray(message)) { + let closeFns: Function[] = []; + message.forEach((msg: any) => closeFns.push(this.show(sev, msg, onHide))); + + return () => closeFns.forEach((fn) => fn()); + } + + if (errors.isPromiseCanceledError(message)) { + return () => void 0; // this kind of error should not be shown + } + + if (types.isNumber(message.severity)) { + sev = message.severity; + } + + return this.doShow(sev, message, onHide); + } + + private doShow(sev: Severity, message: any, onHide?: () => void): () => void { + + // Check flag if we can show a message now + if (!this.canShowMessages) { + const messageObj: IBufferedMessage = { + severity: sev, + message, + onHide, + disposeFn: () => this.messageBuffer.splice(this.messageBuffer.indexOf(messageObj), 1) + }; + this.messageBuffer.push(messageObj); + + // Return function that allows to remove message from buffer + return () => messageObj.disposeFn(); + } + + // Show in Console + if (sev === Severity.Error) { + console.error(toErrorMessage(message, true)); + } + + // Show in Global Handler + return this.handler.showMessage(this.toBaseSeverity(sev), message, onHide); + } + + public hideAll(): void { + if (this.handler) { + this.handler.hideMessages(); + } + } + + public confirm(confirmation: IConfirmation): boolean { + let messageText = confirmation.message; + if (confirmation.detail) { + messageText = messageText + '\n\n' + confirmation.detail; + } + + return window.confirm(messageText); + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/message/electron-browser/messageService.ts b/src/vs/workbench/services/message/electron-browser/messageService.ts new file mode 100644 index 0000000000..6fa3e86739 --- /dev/null +++ b/src/vs/workbench/services/message/electron-browser/messageService.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import product from 'vs/platform/node/product'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { WorkbenchMessageService } from 'vs/workbench/services/message/browser/messageService'; +import { IConfirmation, Severity, IChoiceService } from 'vs/platform/message/common/message'; +import { isLinux } from 'vs/base/common/platform'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Action } from 'vs/base/common/actions'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { mnemonicButtonLabel } from 'vs/base/common/labels'; + +export class MessageService extends WorkbenchMessageService implements IChoiceService { + + constructor( + container: HTMLElement, + @IWindowService private windowService: IWindowService, + @ITelemetryService telemetryService: ITelemetryService + ) { + super(container, telemetryService); + } + + public confirm(confirmation: IConfirmation): boolean { + + const buttons: string[] = []; + if (confirmation.primaryButton) { + buttons.push(confirmation.primaryButton); + } else { + buttons.push(nls.localize({ key: 'yesButton', comment: ['&& denotes a mnemonic'] }, "&&Yes")); + } + + if (confirmation.secondaryButton) { + buttons.push(confirmation.secondaryButton); + } else if (typeof confirmation.secondaryButton === 'undefined') { + buttons.push(nls.localize('cancelButton', "Cancel")); + } + + let opts: Electron.ShowMessageBoxOptions = { + title: confirmation.title, + message: confirmation.message, + buttons, + defaultId: 0, + cancelId: 1 + }; + + if (confirmation.detail) { + opts.detail = confirmation.detail; + } + + if (confirmation.type) { + opts.type = confirmation.type; + } + + let result = this.showMessageBox(opts); + + return result === 0 ? true : false; + } + + public choose(severity: Severity, message: string, options: string[], cancelId: number, modal: boolean = false): TPromise { + if (modal) { + const type: 'none' | 'info' | 'error' | 'question' | 'warning' = severity === Severity.Info ? 'question' : severity === Severity.Error ? 'error' : severity === Severity.Warning ? 'warning' : 'none'; + return TPromise.wrap(this.showMessageBox({ message, buttons: options, type, cancelId })); + } + + let onCancel: () => void = null; + + const promise = new TPromise((c, e) => { + const callback = (index: number) => () => { + c(index); + return TPromise.as(true); + }; + + const actions = options.map((option, index) => new Action('?', option, '', true, callback(index))); + + onCancel = this.show(severity, { message, actions }, () => promise.cancel()); + }, () => onCancel()); + + return promise; + } + + private showMessageBox(opts: Electron.ShowMessageBoxOptions): number { + opts.buttons = opts.buttons.map(button => mnemonicButtonLabel(button)); + opts.buttons = isLinux ? opts.buttons.reverse() : opts.buttons; + + if (opts.defaultId !== void 0) { + opts.defaultId = isLinux ? opts.buttons.length - opts.defaultId - 1 : opts.defaultId; + } + + if (opts.cancelId !== void 0) { + opts.cancelId = isLinux ? opts.buttons.length - opts.cancelId - 1 : opts.cancelId; + } + + opts.noLink = true; + opts.title = opts.title || product.nameLong; + + const result = this.windowService.showMessageBox(opts); + return isLinux ? opts.buttons.length - result - 1 : result; + } +} diff --git a/src/vs/workbench/services/mode/common/workbenchModeService.ts b/src/vs/workbench/services/mode/common/workbenchModeService.ts new file mode 100644 index 0000000000..d522f72e7c --- /dev/null +++ b/src/vs/workbench/services/mode/common/workbenchModeService.ts @@ -0,0 +1,207 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import * as paths from 'vs/base/common/paths'; +import { TPromise } from 'vs/base/common/winjs.base'; +import mime = require('vs/base/common/mime'); +import { IFilesConfiguration } from 'vs/platform/files/common/files'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; +import { ILanguageExtensionPoint, IValidLanguageExtensionPoint } from 'vs/editor/common/services/modeService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; + +export const languagesExtPoint: IExtensionPoint = ExtensionsRegistry.registerExtensionPoint('languages', [], { + description: nls.localize('vscode.extension.contributes.languages', 'Contributes language declarations.'), + type: 'array', + items: { + type: 'object', + defaultSnippets: [{ body: { id: '${1:languageId}', aliases: ['${2:label}'], extensions: ['${3:extension}'], configuration: './language-configuration.json' } }], + properties: { + id: { + description: nls.localize('vscode.extension.contributes.languages.id', 'ID of the language.'), + type: 'string' + }, + aliases: { + description: nls.localize('vscode.extension.contributes.languages.aliases', 'Name aliases for the language.'), + type: 'array', + items: { + type: 'string' + } + }, + extensions: { + description: nls.localize('vscode.extension.contributes.languages.extensions', 'File extensions associated to the language.'), + default: ['.foo'], + type: 'array', + items: { + type: 'string' + } + }, + filenames: { + description: nls.localize('vscode.extension.contributes.languages.filenames', 'File names associated to the language.'), + type: 'array', + items: { + type: 'string' + } + }, + filenamePatterns: { + description: nls.localize('vscode.extension.contributes.languages.filenamePatterns', 'File name glob patterns associated to the language.'), + type: 'array', + items: { + type: 'string' + } + }, + mimetypes: { + description: nls.localize('vscode.extension.contributes.languages.mimetypes', 'Mime types associated to the language.'), + type: 'array', + items: { + type: 'string' + } + }, + firstLine: { + description: nls.localize('vscode.extension.contributes.languages.firstLine', 'A regular expression matching the first line of a file of the language.'), + type: 'string' + }, + configuration: { + description: nls.localize('vscode.extension.contributes.languages.configuration', 'A relative path to a file containing configuration options for the language.'), + type: 'string', + default: './language-configuration.json' + } + } + } +}); + +export class WorkbenchModeServiceImpl extends ModeServiceImpl { + private _configurationService: IConfigurationService; + private _extensionService: IExtensionService; + private _onReadyPromise: TPromise; + + constructor( + @IExtensionService extensionService: IExtensionService, + @IConfigurationService configurationService: IConfigurationService + ) { + super(); + this._configurationService = configurationService; + this._extensionService = extensionService; + + languagesExtPoint.setHandler((extensions: IExtensionPointUser[]) => { + let allValidLanguages: IValidLanguageExtensionPoint[] = []; + + for (let i = 0, len = extensions.length; i < len; i++) { + let extension = extensions[i]; + + if (!Array.isArray(extension.value)) { + extension.collector.error(nls.localize('invalid', "Invalid `contributes.{0}`. Expected an array.", languagesExtPoint.name)); + continue; + } + + for (let j = 0, lenJ = extension.value.length; j < lenJ; j++) { + let ext = extension.value[j]; + if (isValidLanguageExtensionPoint(ext, extension.collector)) { + let configuration = (ext.configuration ? paths.join(extension.description.extensionFolderPath, ext.configuration) : ext.configuration); + allValidLanguages.push({ + id: ext.id, + extensions: ext.extensions, + filenames: ext.filenames, + filenamePatterns: ext.filenamePatterns, + firstLine: ext.firstLine, + aliases: ext.aliases, + mimetypes: ext.mimetypes, + configuration: configuration + }); + } + } + } + + ModesRegistry.registerLanguages(allValidLanguages); + + }); + + this._configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this._configurationService.getConfiguration())); + + this.onDidCreateMode((mode) => { + this._extensionService.activateByEvent(`onLanguage:${mode.getId()}`).done(null, onUnexpectedError); + }); + } + + protected _onReady(): TPromise { + if (!this._onReadyPromise) { + const configuration = this._configurationService.getConfiguration(); + this._onReadyPromise = this._extensionService.onReady().then(() => { + this.onConfigurationChange(configuration); + + return true; + }); + } + + return this._onReadyPromise; + } + + private onConfigurationChange(configuration: IFilesConfiguration): void { + + // Clear user configured mime associations + mime.clearTextMimes(true /* user configured */); + + // Register based on settings + if (configuration.files && configuration.files.associations) { + Object.keys(configuration.files.associations).forEach(pattern => { + const langId = configuration.files.associations[pattern]; + const mimetype = this.getMimeForMode(langId) || `text/x-${langId}`; + + mime.registerTextMime({ id: langId, mime: mimetype, filepattern: pattern, userConfigured: true }); + }); + } + } +} + +function isUndefinedOrStringArray(value: string[]): boolean { + if (typeof value === 'undefined') { + return true; + } + if (!Array.isArray(value)) { + return false; + } + return value.every(item => typeof item === 'string'); +} + +function isValidLanguageExtensionPoint(value: ILanguageExtensionPoint, collector: ExtensionMessageCollector): boolean { + if (!value) { + collector.error(nls.localize('invalid.empty', "Empty value for `contributes.{0}`", languagesExtPoint.name)); + return false; + } + if (typeof value.id !== 'string') { + collector.error(nls.localize('require.id', "property `{0}` is mandatory and must be of type `string`", 'id')); + return false; + } + if (!isUndefinedOrStringArray(value.extensions)) { + collector.error(nls.localize('opt.extensions', "property `{0}` can be omitted and must be of type `string[]`", 'extensions')); + return false; + } + if (!isUndefinedOrStringArray(value.filenames)) { + collector.error(nls.localize('opt.filenames', "property `{0}` can be omitted and must be of type `string[]`", 'filenames')); + return false; + } + if (typeof value.firstLine !== 'undefined' && typeof value.firstLine !== 'string') { + collector.error(nls.localize('opt.firstLine', "property `{0}` can be omitted and must be of type `string`", 'firstLine')); + return false; + } + if (typeof value.configuration !== 'undefined' && typeof value.configuration !== 'string') { + collector.error(nls.localize('opt.configuration', "property `{0}` can be omitted and must be of type `string`", 'configuration')); + return false; + } + if (!isUndefinedOrStringArray(value.aliases)) { + collector.error(nls.localize('opt.aliases', "property `{0}` can be omitted and must be of type `string[]`", 'aliases')); + return false; + } + if (!isUndefinedOrStringArray(value.mimetypes)) { + collector.error(nls.localize('opt.mimetypes', "property `{0}` can be omitted and must be of type `string[]`", 'mimetypes')); + return false; + } + return true; +} diff --git a/src/vs/workbench/services/panel/common/panelService.ts b/src/vs/workbench/services/panel/common/panelService.ts new file mode 100644 index 0000000000..c5e88d8891 --- /dev/null +++ b/src/vs/workbench/services/panel/common/panelService.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 Event from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IPanel } from 'vs/workbench/common/panel'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; + +export const IPanelService = createDecorator('panelService'); + +export interface IPanelIdentifier { + id: string; + name: string; + commandId: string; +} + +export interface IPanelService { + _serviceBrand: ServiceIdentifier; + + onDidPanelOpen: Event; + + onDidPanelClose: Event; + + /** + * Opens a panel with the given identifier and pass keyboard focus to it if specified. + */ + openPanel(id: string, focus?: boolean): TPromise; + + /** + * Returns the current active panel or null if none + */ + getActivePanel(): IPanel; + + /** + * Returns all registered panels + */ + getPanels(): IPanelIdentifier[]; +} diff --git a/src/vs/workbench/services/part/common/partService.ts b/src/vs/workbench/services/part/common/partService.ts new file mode 100644 index 0000000000..1da7c0b1b6 --- /dev/null +++ b/src/vs/workbench/services/part/common/partService.ts @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import Event from 'vs/base/common/event'; + +export enum Parts { + ACTIVITYBAR_PART, + SIDEBAR_PART, + PANEL_PART, + EDITOR_PART, + STATUSBAR_PART, + TITLEBAR_PART +} + +export enum Position { + LEFT, + RIGHT +} + +export interface ILayoutOptions { + toggleMaximizedPanel?: boolean; +} + +export const IPartService = createDecorator('partService'); + +export interface IPartService { + _serviceBrand: ServiceIdentifier; + + /** + * Emits when the visibility of the title bar changes. + */ + onTitleBarVisibilityChange: Event; + + /** + * Emits when the editor part's layout changes. + */ + onEditorLayout: Event; + + /** + * Asks the part service to layout all parts. + */ + layout(options?: ILayoutOptions): void; + + /** + * Asks the part service to if all parts have been created. + */ + isCreated(): boolean; + + /** + * Promise is complete when all parts have been created. + */ + joinCreation(): TPromise; + + /** + * Returns whether the given part has the keyboard focus or not. + */ + hasFocus(part: Parts): boolean; + + /** + * Returns the parts HTML element, if there is one. + */ + getContainer(part: Parts): HTMLElement; + + /** + * Returns if the part is visible. + */ + isVisible(part: Parts): boolean; + + /** + * Set activity bar hidden or not + */ + setActivityBarHidden(hidden: boolean): void; + + /** + * Number of pixels (adjusted for zooming) that the title bar (if visible) pushes down the workbench contents. + */ + getTitleBarOffset(): number; + + /** + * Set sidebar hidden or not + */ + setSideBarHidden(hidden: boolean): TPromise; + + /** + * Set panel part hidden or not + */ + setPanelHidden(hidden: boolean): TPromise; + + /** + * Maximizes the panel height if the panel is not already maximized. + * Shrinks the panel to the default starting size if the panel is maximized. + */ + toggleMaximizedPanel(): void; + + /** + * Returns true if the panel is maximized. + */ + isPanelMaximized(): boolean; + + /** + * Gets the current side bar position. Note that the sidebar can be hidden too. + */ + getSideBarPosition(): Position; + + /** + * Returns the identifier of the element that contains the workbench. + */ + getWorkbenchElementId(): string; + + /** + * Toggles the workbench in and out of zen mode - parts get hidden and window goes fullscreen. + */ + toggleZenMode(): void; + + /** + * Resizes currently focused part on main access + */ + resizePart(part: Parts, sizeChange: number): void; +} \ No newline at end of file diff --git a/src/vs/workbench/services/progress/browser/media/progress.svg b/src/vs/workbench/services/progress/browser/media/progress.svg new file mode 100644 index 0000000000..c3633c0dda --- /dev/null +++ b/src/vs/workbench/services/progress/browser/media/progress.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/src/vs/workbench/services/progress/browser/media/progressService2.css b/src/vs/workbench/services/progress/browser/media/progressService2.css new file mode 100644 index 0000000000..ded88981a9 --- /dev/null +++ b/src/vs/workbench/services/progress/browser/media/progressService2.css @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench > .part.statusbar > .statusbar-item.progress { + background-image: url(progress.svg); + background-repeat: no-repeat; + background-position: left; + padding-left: 14px; + display: inline; +} + +.monaco-workbench .progress-badge > .badge-content { + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMiAyIDE0IDE0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDIgMiAxNCAxNCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTkgMTZjLTMuODYgMC03LTMuMTQtNy03czMuMTQtNyA3LTdjMy44NTkgMCA3IDMuMTQxIDcgN3MtMy4xNDEgNy03IDd6bTAtMTIuNmMtMy4wODggMC01LjYgMi41MTMtNS42IDUuNnMyLjUxMiA1LjYgNS42IDUuNiA1LjYtMi41MTIgNS42LTUuNi0yLjUxMi01LjYtNS42LTUuNnptMy44NiA3LjFsLTMuMTYtMS44OTZ2LTMuODA0aC0xLjR2NC41OTZsMy44NCAyLjMwNS43Mi0xLjIwMXoiLz48L3N2Zz4="); + background-position: center center; + background-repeat: no-repeat; +} diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts new file mode 100644 index 0000000000..a057e352d4 --- /dev/null +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -0,0 +1,257 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import lifecycle = require('vs/base/common/lifecycle'); +import { TPromise } from 'vs/base/common/winjs.base'; +import types = require('vs/base/common/types'); +import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; + +interface ProgressState { + infinite?: boolean; + total?: number; + worked?: number; + done?: boolean; + whilePromise?: TPromise; +} + +export abstract class ScopedService { + + protected toDispose: lifecycle.IDisposable[]; + + constructor(private viewletService: IViewletService, private panelService: IPanelService, private scopeId: string) { + this.toDispose = []; + this.registerListeners(); + } + + public registerListeners(): void { + this.toDispose.push(this.viewletService.onDidViewletOpen(viewlet => this.onScopeOpened(viewlet.getId()))); + this.toDispose.push(this.panelService.onDidPanelOpen(panel => this.onScopeOpened(panel.getId()))); + + this.toDispose.push(this.viewletService.onDidViewletClose(viewlet => this.onScopeClosed(viewlet.getId()))); + this.toDispose.push(this.panelService.onDidPanelClose(panel => this.onScopeClosed(panel.getId()))); + } + + private onScopeClosed(scopeId: string) { + if (scopeId === this.scopeId) { + this.onScopeDeactivated(); + } + } + + private onScopeOpened(scopeId: string) { + if (scopeId === this.scopeId) { + this.onScopeActivated(); + } + } + + public abstract onScopeActivated(): void; + + public abstract onScopeDeactivated(): void; +} + +export class WorkbenchProgressService extends ScopedService implements IProgressService { + public _serviceBrand: any; + private isActive: boolean; + private progressbar: ProgressBar; + private progressState: ProgressState; + + constructor( + progressbar: ProgressBar, + scopeId: string, + isActive: boolean, + @IViewletService viewletService: IViewletService, + @IPanelService panelService: IPanelService + ) { + super(viewletService, panelService, scopeId); + + this.progressbar = progressbar; + this.isActive = isActive || types.isUndefinedOrNull(scopeId); // If service is unscoped, enable by default + this.progressState = Object.create(null); + } + + public onScopeDeactivated(): void { + this.isActive = false; + } + + public onScopeActivated(): void { + this.isActive = true; + + // Return early if progress state indicates that progress is done + if (this.progressState.done) { + return; + } + + // Replay Infinite Progress from Promise + if (this.progressState.whilePromise) { + this.doShowWhile(); + } + + // Replay Infinite Progress + else if (this.progressState.infinite) { + this.progressbar.infinite().getContainer().show(); + } + + // Replay Finite Progress (Total & Worked) + else { + if (this.progressState.total) { + this.progressbar.total(this.progressState.total).getContainer().show(); + } + + if (this.progressState.worked) { + this.progressbar.worked(this.progressState.worked).getContainer().show(); + } + } + } + + private clearProgressState(): void { + this.progressState.infinite = void 0; + this.progressState.done = void 0; + this.progressState.worked = void 0; + this.progressState.total = void 0; + this.progressState.whilePromise = void 0; + } + + public show(infinite: boolean, delay?: number): IProgressRunner; + public show(total: number, delay?: number): IProgressRunner; + public show(infiniteOrTotal: any, delay?: number): IProgressRunner { + let infinite: boolean; + let total: number; + + // Sort out Arguments + if (infiniteOrTotal === false || infiniteOrTotal === true) { + infinite = infiniteOrTotal; + } else { + total = infiniteOrTotal; + } + + // Reset State + this.clearProgressState(); + + // Keep in State + this.progressState.infinite = infinite; + this.progressState.total = total; + + // Active: Show Progress + if (this.isActive) { + + // Infinite: Start Progressbar and Show after Delay + if (!types.isUndefinedOrNull(infinite)) { + if (types.isUndefinedOrNull(delay)) { + this.progressbar.infinite().getContainer().show(); + } else { + this.progressbar.infinite().getContainer().showDelayed(delay); + } + } + + // Finite: Start Progressbar and Show after Delay + else if (!types.isUndefinedOrNull(total)) { + if (types.isUndefinedOrNull(delay)) { + this.progressbar.total(total).getContainer().show(); + } else { + this.progressbar.total(total).getContainer().showDelayed(delay); + } + } + } + + return { + total: (total: number) => { + this.progressState.infinite = false; + this.progressState.total = total; + + if (this.isActive) { + this.progressbar.total(total); + } + }, + + worked: (worked: number) => { + + // Verify first that we are either not active or the progressbar has a total set + if (!this.isActive || this.progressbar.hasTotal()) { + this.progressState.infinite = false; + if (this.progressState.worked) { + this.progressState.worked += worked; + } else { + this.progressState.worked = worked; + } + + if (this.isActive) { + this.progressbar.worked(worked); + } + } + + // Otherwise the progress bar does not support worked(), we fallback to infinite() progress + else { + this.progressState.infinite = true; + this.progressState.worked = void 0; + this.progressState.total = void 0; + this.progressbar.infinite().getContainer().show(); + } + }, + + done: () => { + this.progressState.infinite = false; + this.progressState.done = true; + + if (this.isActive) { + this.progressbar.stop().getContainer().hide(); + } + } + }; + } + + public showWhile(promise: TPromise, delay?: number): TPromise { + let stack: boolean = !!this.progressState.whilePromise; + + // Reset State + if (!stack) { + this.clearProgressState(); + } + + // Otherwise join with existing running promise to ensure progress is accurate + else { + promise = TPromise.join([promise, this.progressState.whilePromise]); + } + + // Keep Promise in State + this.progressState.whilePromise = promise; + + let stop = () => { + + // If this is not the last promise in the list of joined promises, return early + if (!!this.progressState.whilePromise && this.progressState.whilePromise !== promise) { + return; + } + + // The while promise is either null or equal the promise we last hooked on + this.clearProgressState(); + + if (this.isActive) { + this.progressbar.stop().getContainer().hide(); + } + }; + + this.doShowWhile(delay); + + return promise.then(stop, stop); + } + + private doShowWhile(delay?: number): void { + + // Show Progress when active + if (this.isActive) { + if (types.isUndefinedOrNull(delay)) { + this.progressbar.infinite().getContainer().show(); + } else { + this.progressbar.infinite().getContainer().showDelayed(delay); + } + } + } + + public dispose(): void { + this.toDispose = lifecycle.dispose(this.toDispose); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts new file mode 100644 index 0000000000..7a34649ae8 --- /dev/null +++ b/src/vs/workbench/services/progress/browser/progressService2.ts @@ -0,0 +1,188 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import 'vs/css!./media/progressService2'; +import * as dom from 'vs/base/browser/dom'; +import { localize } from 'vs/nls'; +import { IActivityBarService, ProgressBadge } from 'vs/workbench/services/activity/common/activityBarService'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IProgressService2, IProgressOptions, ProgressLocation, IProgress, IProgressStep, Progress, emptyProgress } from 'vs/platform/progress/common/progress'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { StatusbarAlignment, IStatusbarRegistry, StatusbarItemDescriptor, Extensions, IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { always } from 'vs/base/common/async'; + +class WindowProgressItem implements IStatusbarItem { + + static Instance: WindowProgressItem; + + private _element: HTMLElement; + private _label: OcticonLabel; + + constructor() { + WindowProgressItem.Instance = this; + } + + render(element: HTMLElement): IDisposable { + this._element = element; + this._label = new OcticonLabel(this._element); + this._element.classList.add('progress'); + this.hide(); + return null; + } + + set text(value: string) { + this._label.text = value; + } + + set title(value: string) { + this._label.title = value; + } + + hide() { + dom.hide(this._element); + } + + show() { + dom.show(this._element); + } +} + + +export class ProgressService2 implements IProgressService2 { + + _serviceBrand: any; + + private _stack: [IProgressOptions, Progress][] = []; + + constructor( + @IActivityBarService private _activityBar: IActivityBarService, + @IViewletService private _viewletService: IViewletService + ) { + // + } + + withProgress(options: IProgressOptions, task: (progress: IProgress<{ message?: string, percentage?: number }>) => TPromise): void { + const { location } = options; + switch (location) { + case ProgressLocation.Window: + this._withWindowProgress(options, task); + break; + case ProgressLocation.Scm: + this._withViewletProgress('workbench.view.scm', task); + break; + default: + console.warn(`Bad progress location: ${location}`); + } + } + + + private _withWindowProgress(options: IProgressOptions, callback: (progress: IProgress<{ message?: string, percentage?: number }>) => TPromise): void { + + const task: [IProgressOptions, Progress] = [options, new Progress(() => this._updateWindowProgress())]; + + const promise = callback(task[1]); + + let delayHandle = setTimeout(() => { + delayHandle = undefined; + this._stack.unshift(task); + this._updateWindowProgress(); + + // show progress for at least 150ms + always(TPromise.join([ + TPromise.timeout(150), + promise + ]), () => { + const idx = this._stack.indexOf(task); + this._stack.splice(idx, 1); + this._updateWindowProgress(); + }); + + }, 150); + + // cancel delay if promise finishes below 150ms + always(promise, () => clearTimeout(delayHandle)); + } + + private _updateWindowProgress(idx: number = 0) { + if (idx >= this._stack.length) { + WindowProgressItem.Instance.hide(); + } else { + const [options, progress] = this._stack[idx]; + + let text = options.title; + if (progress.value && progress.value.message) { + text = progress.value.message; + } + + if (!text) { + // no message -> no progress. try with next on stack + this._updateWindowProgress(idx + 1); + return; + } + + let title = text; + if (options.title && options.title !== title) { + title = localize('progress.subtitle', "{0} - {1}", options.title, title); + } + if (options.tooltip) { + title = localize('progress.title', "{0}: {1}", options.tooltip, title); + } + + WindowProgressItem.Instance.text = text; + WindowProgressItem.Instance.title = title; + WindowProgressItem.Instance.show(); + } + } + + private _withViewletProgress(viewletId: string, task: (progress: IProgress<{ message?: string, percentage?: number }>) => TPromise): void { + + const promise = task(emptyProgress); + + // show in viewlet + const viewletProgress = this._viewletService.getProgressIndicator(viewletId); + if (viewletProgress) { + viewletProgress.showWhile(promise); + } + + // show activity bar + let activityProgress: IDisposable; + let delayHandle = setTimeout(() => { + delayHandle = undefined; + const handle = this._activityBar.showActivity( + viewletId, + new ProgressBadge(() => ''), + 'progress-badge' + ); + const startTimeVisible = Date.now(); + const minTimeVisible = 300; + activityProgress = { + dispose() { + const d = Date.now() - startTimeVisible; + if (d < minTimeVisible) { + // should at least show for Nms + setTimeout(() => handle.dispose(), minTimeVisible - d); + } else { + // shown long enough + handle.dispose(); + } + } + }; + }, 300); + + always(promise, () => { + clearTimeout(delayHandle); + dispose(activityProgress); + }); + } +} + + +Registry.as(Extensions.Statusbar).registerStatusbarItem( + new StatusbarItemDescriptor(WindowProgressItem, StatusbarAlignment.LEFT) +); diff --git a/src/vs/workbench/services/progress/test/progressService.test.ts b/src/vs/workbench/services/progress/test/progressService.test.ts new file mode 100644 index 0000000000..d8caef8853 --- /dev/null +++ b/src/vs/workbench/services/progress/test/progressService.test.ts @@ -0,0 +1,291 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { IAction, IActionItem } from 'vs/base/common/actions'; +import { Promise, TPromise } from 'vs/base/common/winjs.base'; +import { IEditorControl } from 'vs/platform/editor/common/editor'; +import { Viewlet, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; +import { IPanel } from 'vs/workbench/common/panel'; +import { WorkbenchProgressService, ScopedService } from 'vs/workbench/services/progress/browser/progressService'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IViewlet } from 'vs/workbench/common/viewlet'; +import { Emitter } from 'vs/base/common/event'; + +let activeViewlet: Viewlet = {}; + +class TestViewletService implements IViewletService { + public _serviceBrand: any; + + onDidViewletOpenEmitter = new Emitter(); + onDidViewletCloseEmitter = new Emitter(); + + onDidViewletOpen = this.onDidViewletOpenEmitter.event; + onDidViewletClose = this.onDidViewletCloseEmitter.event; + + public openViewlet(id: string, focus?: boolean): TPromise { + return TPromise.as(null); + } + + public getViewlets(): ViewletDescriptor[] { + return []; + } + + public getActiveViewlet(): IViewlet { + return activeViewlet; + } + + public dispose() { + } + + public getDefaultViewletId(): string { + return 'workbench.view.explorer'; + } + + public getViewlet(id: string): ViewletDescriptor { + return null; + } + + public getProgressIndicator(id: string) { + return null; + } +} + +class TestPanelService implements IPanelService { + public _serviceBrand: any; + + onDidPanelOpen = new Emitter().event; + onDidPanelClose = new Emitter().event; + + public openPanel(id: string, focus?: boolean): Promise { + return TPromise.as(null); + } + + public getPanels(): any[] { + return []; + } + + public getActivePanel(): IViewlet { + return activeViewlet; + } + + public dispose() { + } +} + +class TestViewlet implements IViewlet { + + constructor(private id: string) { } + + getId(): string { + return this.id; + } + + /** + * Returns the name of this composite to show in the title area. + */ + getTitle(): string { + return this.id; + } + + /** + * Returns the primary actions of the composite. + */ + getActions(): IAction[] { + return []; + } + + /** + * Returns the secondary actions of the composite. + */ + getSecondaryActions(): IAction[] { + return []; + } + + /** + * Returns an array of actions to show in the context menu of the composite + */ + public getContextMenuActions(): IAction[] { + return []; + } + + /** + * Returns the action item for a specific action. + */ + getActionItem(action: IAction): IActionItem { + return null; + } + + /** + * Returns the underlying control of this composite. + */ + getControl(): IEditorControl { + return null; + } + + /** + * Asks the underlying control to focus. + */ + focus(): void { + } + + getOptimalWidth(): number { + return 10; + } +} + +class TestScopedService extends ScopedService { + public isActive: boolean; + + constructor(viewletService: IViewletService, panelService: IPanelService, scopeId: string) { + super(viewletService, panelService, scopeId); + } + public onScopeActivated() { + this.isActive = true; + } + + public onScopeDeactivated() { + this.isActive = false; + } +} + +class TestProgressBar { + public fTotal: number; + public fWorked: number; + public fInfinite: boolean; + public fDone: boolean; + + constructor() { + } + + public infinite() { + this.fDone = null; + this.fInfinite = true; + + return this; + } + + public total(total: number) { + this.fDone = null; + this.fTotal = total; + + return this; + } + + public hasTotal() { + return !!this.fTotal; + } + + public worked(worked: number) { + this.fDone = null; + + if (this.fWorked) { + this.fWorked += worked; + } else { + this.fWorked = worked; + } + + return this; + } + + public done() { + this.fDone = true; + + this.fInfinite = null; + this.fWorked = null; + this.fTotal = null; + + return this; + } + + public stop() { + return this.done(); + } + + public getContainer() { + return { + show: function () { }, + hide: function () { } + }; + } +} + +suite('Progress Service', () => { + + test('ScopedService', () => { + let viewletService = new TestViewletService(); + let panelService = new TestPanelService(); + let service = new TestScopedService(viewletService, panelService, 'test.scopeId'); + const testViewlet = new TestViewlet('test.scopeId'); + + assert(!service.isActive); + viewletService.onDidViewletOpenEmitter.fire(testViewlet); + assert(service.isActive); + + viewletService.onDidViewletCloseEmitter.fire(testViewlet); + assert(!service.isActive); + + }); + + test('WorkbenchProgressService', function () { + let testProgressBar = new TestProgressBar(); + let viewletService = new TestViewletService(); + let panelService = new TestPanelService(); + let service = new WorkbenchProgressService((testProgressBar), 'test.scopeId', true, viewletService, panelService); + + // Active: Show (Infinite) + let fn = service.show(true); + assert.strictEqual(true, testProgressBar.fInfinite); + fn.done(); + assert.strictEqual(true, testProgressBar.fDone); + + // Active: Show (Total / Worked) + fn = service.show(100); + assert.strictEqual(false, !!testProgressBar.fInfinite); + assert.strictEqual(100, testProgressBar.fTotal); + fn.worked(20); + assert.strictEqual(20, testProgressBar.fWorked); + fn.total(80); + assert.strictEqual(80, testProgressBar.fTotal); + fn.done(); + assert.strictEqual(true, testProgressBar.fDone); + + // Inactive: Show (Infinite) + const testViewlet = new TestViewlet('test.scopeId'); + viewletService.onDidViewletCloseEmitter.fire(testViewlet); + service.show(true); + assert.strictEqual(false, !!testProgressBar.fInfinite); + viewletService.onDidViewletOpenEmitter.fire(testViewlet); + assert.strictEqual(true, testProgressBar.fInfinite); + + // Inactive: Show (Total / Worked) + viewletService.onDidViewletCloseEmitter.fire(testViewlet); + fn = service.show(100); + fn.total(80); + fn.worked(20); + assert.strictEqual(false, !!testProgressBar.fTotal); + viewletService.onDidViewletOpenEmitter.fire(testViewlet); + assert.strictEqual(20, testProgressBar.fWorked); + assert.strictEqual(80, testProgressBar.fTotal); + + // Acive: Show While + let p = TPromise.as(null); + service.showWhile(p).then(() => { + assert.strictEqual(true, testProgressBar.fDone); + + viewletService.onDidViewletCloseEmitter.fire(testViewlet); + p = TPromise.as(null); + service.showWhile(p).then(() => { + assert.strictEqual(true, testProgressBar.fDone); + + viewletService.onDidViewletOpenEmitter.fire(testViewlet); + assert.strictEqual(true, testProgressBar.fDone); + }); + }); + }); +}); diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts new file mode 100644 index 0000000000..6eaae52ee9 --- /dev/null +++ b/src/vs/workbench/services/scm/common/scm.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import Event from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Command } from 'vs/editor/common/modes'; + +export interface IBaselineResourceProvider { + getBaselineResource(resource: URI): TPromise; +} + +export const ISCMService = createDecorator('scm'); + +export interface ISCMResourceDecorations { + icon?: URI; + iconDark?: URI; + tooltip?: string; + strikeThrough?: boolean; + faded?: boolean; +} + +export interface ISCMResource { + readonly resourceGroup: ISCMResourceGroup; + readonly sourceUri: URI; + readonly command?: Command; + readonly decorations: ISCMResourceDecorations; +} + +export interface ISCMResourceGroup { + readonly provider: ISCMProvider; + readonly label: string; + readonly id: string; + readonly resources: ISCMResource[]; +} + +export interface ISCMProvider extends IDisposable { + readonly label: string; + readonly id: string; + readonly contextValue: string; + readonly resources: ISCMResourceGroup[]; + readonly onDidChange: Event; + readonly count?: number; + readonly commitTemplate?: string; + readonly onDidChangeCommitTemplate?: Event; + readonly acceptInputCommand?: Command; + readonly statusBarCommands?: Command[]; + + getOriginalResource(uri: URI): TPromise; +} + +export interface ISCMInput { + value: string; + readonly onDidChange: Event; +} + +export interface ISCMRepository extends IDisposable { + readonly onDidFocus: Event; + readonly provider: ISCMProvider; + readonly input: ISCMInput; + focus(): void; +} + +export interface ISCMService { + + readonly _serviceBrand: any; + readonly onDidAddRepository: Event; + readonly onDidRemoveRepository: Event; + readonly onDidChangeRepository: Event; + + readonly repositories: ISCMRepository[]; + + registerSCMProvider(provider: ISCMProvider): ISCMRepository; +} \ No newline at end of file diff --git a/src/vs/workbench/services/scm/common/scmService.ts b/src/vs/workbench/services/scm/common/scmService.ts new file mode 100644 index 0000000000..c542d51253 --- /dev/null +++ b/src/vs/workbench/services/scm/common/scmService.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository } from './scm'; + +class SCMInput implements ISCMInput { + + private _value = ''; + + get value(): string { + return this._value; + } + + set value(value: string) { + this._value = value; + this._onDidChange.fire(value); + } + + private _onDidChange = new Emitter(); + get onDidChange(): Event { return this._onDidChange.event; } +} + +class SCMRepository implements ISCMRepository { + + private _onDidFocus = new Emitter(); + readonly onDidFocus: Event = this._onDidFocus.event; + + readonly input: ISCMInput = new SCMInput(); + + constructor( + public readonly provider: ISCMProvider, + private disposable: IDisposable + ) { } + + focus(): void { + this._onDidFocus.fire(); + } + + dispose(): void { + this.disposable.dispose(); + this.provider.dispose(); + } +} + +export class SCMService implements ISCMService { + + _serviceBrand; + + private _providerIds = new Set(); + private _repositories: ISCMRepository[] = []; + get repositories(): ISCMRepository[] { return [...this._repositories]; } + + private _onDidAddProvider = new Emitter(); + get onDidAddRepository(): Event { return this._onDidAddProvider.event; } + + private _onDidRemoveProvider = new Emitter(); + get onDidRemoveRepository(): Event { return this._onDidRemoveProvider.event; } + + private _onDidChangeProvider = new Emitter(); + get onDidChangeRepository(): Event { return this._onDidChangeProvider.event; } + + constructor() { } + + registerSCMProvider(provider: ISCMProvider): ISCMRepository { + if (this._providerIds.has(provider.id)) { + throw new Error(`SCM Provider ${provider.id} already exists.`); + } + + this._providerIds.add(provider.id); + + const disposable = toDisposable(() => { + const index = this._repositories.indexOf(repository); + + if (index < 0) { + return; + } + + this._providerIds.delete(provider.id); + this._repositories.splice(index, 1); + this._onDidRemoveProvider.fire(repository); + }); + + const repository = new SCMRepository(provider, disposable); + this._repositories.push(repository); + this._onDidAddProvider.fire(repository); + + return repository; + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts new file mode 100644 index 0000000000..972470edbc --- /dev/null +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -0,0 +1,741 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as childProcess from 'child_process'; +import { StringDecoder, NodeStringDecoder } from 'string_decoder'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import fs = require('fs'); +import path = require('path'); +import { isEqualOrParent } from 'vs/base/common/paths'; +import { Readable } from 'stream'; +import { TPromise } from 'vs/base/common/winjs.base'; + +import scorer = require('vs/base/common/scorer'); +import objects = require('vs/base/common/objects'); +import arrays = require('vs/base/common/arrays'); +import platform = require('vs/base/common/platform'); +import strings = require('vs/base/common/strings'); +import types = require('vs/base/common/types'); +import glob = require('vs/base/common/glob'); +import { IProgress, IUncachedSearchStats } from 'vs/platform/search/common/search'; + +import extfs = require('vs/base/node/extfs'); +import flow = require('vs/base/node/flow'); +import { IRawFileMatch, ISerializedSearchComplete, IRawSearch, ISearchEngine, IFolderSearch } from './search'; + +enum Traversal { + Node = 1, + MacFind, + WindowsDir, + LinuxFind +} + +interface IDirectoryEntry { + base: string; + relativePath: string; + basename: string; +} + +interface IDirectoryTree { + rootEntries: IDirectoryEntry[]; + pathToEntries: { [relativePath: string]: IDirectoryEntry[] }; +} + +export class FileWalker { + private config: IRawSearch; + private filePattern: string; + private normalizedFilePatternLowercase: string; + private includePattern: glob.ParsedExpression; + private maxResults: number; + private maxFilesize: number; + private isLimitHit: boolean; + private resultCount: number; + private isCanceled: boolean; + private fileWalkStartTime: number; + private directoriesWalked: number; + private filesWalked: number; + private traversal: Traversal; + private errors: string[]; + private cmdForkStartTime: number; + private cmdForkResultTime: number; + private cmdResultCount: number; + + private folderExcludePatterns: Map; + private globalExcludePattern: glob.ParsedExpression; + + private walkedPaths: { [path: string]: boolean; }; + + constructor(config: IRawSearch) { + this.config = config; + this.filePattern = config.filePattern; + this.includePattern = config.includePattern && glob.parse(config.includePattern); + this.maxResults = config.maxResults || null; + this.maxFilesize = config.maxFilesize || null; + this.walkedPaths = Object.create(null); + this.resultCount = 0; + this.isLimitHit = false; + this.directoriesWalked = 0; + this.filesWalked = 0; + this.traversal = Traversal.Node; + this.errors = []; + + if (this.filePattern) { + this.normalizedFilePatternLowercase = strings.stripWildcards(this.filePattern).toLowerCase(); + } + + this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern); + this.folderExcludePatterns = new Map(); + + config.folderQueries.forEach(folderQuery => { + const folderExcludeExpression: glob.IExpression = objects.assign({}, folderQuery.excludePattern || {}, this.config.excludePattern || {}); + + // Add excludes for other root folders + config.folderQueries + .map(rootFolderQuery => rootFolderQuery.folder) + .filter(rootFolder => rootFolder !== folderQuery.folder) + .forEach(otherRootFolder => { + // Exclude nested root folders + if (isEqualOrParent(otherRootFolder, folderQuery.folder)) { + folderExcludeExpression[path.relative(folderQuery.folder, otherRootFolder)] = true; + } + }); + + this.folderExcludePatterns.set(folderQuery.folder, new AbsoluteAndRelativeParsedExpression(folderExcludeExpression, folderQuery.folder)); + }); + } + + public cancel(): void { + this.isCanceled = true; + } + + public walk(folderQueries: IFolderSearch[], extraFiles: string[], onResult: (result: IRawFileMatch) => void, done: (error: Error, isLimitHit: boolean) => void): void { + this.fileWalkStartTime = Date.now(); + + // Support that the file pattern is a full path to a file that exists + this.checkFilePatternAbsoluteMatch((exists, size) => { + if (this.isCanceled) { + return done(null, this.isLimitHit); + } + + // Report result from file pattern if matching + if (exists) { + this.resultCount++; + onResult({ + relativePath: this.filePattern, + basename: path.basename(this.filePattern), + size + }); + + // Optimization: a match on an absolute path is a good result and we do not + // continue walking the entire root paths array for other matches because + // it is very unlikely that another file would match on the full absolute path + return done(null, this.isLimitHit); + } + + // For each extra file + if (extraFiles) { + extraFiles.forEach(extraFilePath => { + const basename = path.basename(extraFilePath); + if (this.globalExcludePattern && this.globalExcludePattern(extraFilePath, basename)) { + return; // excluded + } + + // File: Check for match on file pattern and include pattern + this.matchFile(onResult, { relativePath: extraFilePath /* no workspace relative path */, basename }); + }); + } + + let traverse = this.nodeJSTraversal; + if (!this.maxFilesize) { + if (platform.isMacintosh) { + this.traversal = Traversal.MacFind; + traverse = this.findTraversal; + // Disable 'dir' for now (#11181, #11179, #11183, #11182). + } /* else if (platform.isWindows) { + this.traversal = Traversal.WindowsDir; + traverse = this.windowsDirTraversal; + } */ else if (platform.isLinux) { + this.traversal = Traversal.LinuxFind; + traverse = this.findTraversal; + } + } + + const isNodeTraversal = traverse === this.nodeJSTraversal; + if (!isNodeTraversal) { + this.cmdForkStartTime = Date.now(); + } + + // For each root folder + flow.parallel(folderQueries, (folderQuery: IFolderSearch, rootFolderDone: (err: Error, result: void) => void) => { + this.call(traverse, this, folderQuery, onResult, (err?: Error) => { + if (err) { + if (isNodeTraversal) { + rootFolderDone(err, undefined); + } else { + // fallback + const errorMessage = toErrorMessage(err); + console.error(errorMessage); + this.errors.push(errorMessage); + this.nodeJSTraversal(folderQuery, onResult, err => rootFolderDone(err, undefined)); + } + } else { + rootFolderDone(undefined, undefined); + } + }); + }, (err, result) => { + done(err ? err[0] : null, this.isLimitHit); + }); + }); + } + + private call(fun: Function, that: any, ...args: any[]): void { + try { + fun.apply(that, args); + } catch (e) { + args[args.length - 1](e); + } + } + + private findTraversal(folderQuery: IFolderSearch, onResult: (result: IRawFileMatch) => void, cb: (err?: Error) => void): void { + const rootFolder = folderQuery.folder; + const isMac = platform.isMacintosh; + let done = (err?: Error) => { + done = () => { }; + cb(err); + }; + let leftover = ''; + let first = true; + const tree = this.initDirectoryTree(); + const cmd = this.spawnFindCmd(folderQuery); + this.collectStdout(cmd, 'utf8', (err: Error, stdout?: string, last?: boolean) => { + if (err) { + done(err); + return; + } + + // Mac: uses NFD unicode form on disk, but we want NFC + const normalized = leftover + (isMac ? strings.normalizeNFC(stdout) : stdout); + const relativeFiles = normalized.split('\n./'); + if (first && normalized.length >= 2) { + first = false; + relativeFiles[0] = relativeFiles[0].trim().substr(2); + } + + if (last) { + const n = relativeFiles.length; + relativeFiles[n - 1] = relativeFiles[n - 1].trim(); + if (!relativeFiles[n - 1]) { + relativeFiles.pop(); + } + } else { + leftover = relativeFiles.pop(); + } + + if (relativeFiles.length && relativeFiles[0].indexOf('\n') !== -1) { + done(new Error('Splitting up files failed')); + return; + } + + this.addDirectoryEntries(tree, rootFolder, relativeFiles, onResult); + + if (last) { + this.matchDirectoryTree(tree, rootFolder, onResult); + done(); + } + }); + } + + // protected windowsDirTraversal(rootFolder: string, onResult: (result: IRawFileMatch) => void, done: (err?: Error) => void): void { + // const cmd = childProcess.spawn('cmd', ['/U', '/c', 'dir', '/s', '/b', '/a-d', rootFolder]); + // this.readStdout(cmd, 'ucs2', (err: Error, stdout?: string) => { + // if (err) { + // done(err); + // return; + // } + + // const relativeFiles = stdout.split(`\r\n${rootFolder}\\`); + // relativeFiles[0] = relativeFiles[0].trim().substr(rootFolder.length + 1); + // const n = relativeFiles.length; + // relativeFiles[n - 1] = relativeFiles[n - 1].trim(); + // if (!relativeFiles[n - 1]) { + // relativeFiles.pop(); + // } + + // if (relativeFiles.length && relativeFiles[0].indexOf('\n') !== -1) { + // done(new Error('Splitting up files failed')); + // return; + // } + + // this.matchFiles(rootFolder, relativeFiles, onResult); + + // done(); + // }); + // } + + /** + * Public for testing. + */ + public spawnFindCmd(folderQuery: IFolderSearch) { + const excludePattern = this.folderExcludePatterns.get(folderQuery.folder); + const basenames = excludePattern.getBasenameTerms(); + const pathTerms = excludePattern.getPathTerms(); + let args = ['-L', '.']; + if (basenames.length || pathTerms.length) { + args.push('-not', '(', '('); + for (const basename of basenames) { + args.push('-name', basename); + args.push('-o'); + } + for (const path of pathTerms) { + args.push('-path', path); + args.push('-o'); + } + args.pop(); + args.push(')', '-prune', ')'); + } + args.push('-type', 'f'); + return childProcess.spawn('find', args, { cwd: folderQuery.folder }); + } + + /** + * Public for testing. + */ + public readStdout(cmd: childProcess.ChildProcess, encoding: string, cb: (err: Error, stdout?: string) => void): void { + let all = ''; + this.collectStdout(cmd, encoding, (err: Error, stdout?: string, last?: boolean) => { + if (err) { + cb(err); + return; + } + + all += stdout; + if (last) { + cb(null, all); + } + }); + } + + private collectStdout(cmd: childProcess.ChildProcess, encoding: string, cb: (err: Error, stdout?: string, last?: boolean) => void): void { + let done = (err: Error, stdout?: string, last?: boolean) => { + if (err || last) { + done = () => { }; + this.cmdForkResultTime = Date.now(); + } + cb(err, stdout, last); + }; + + this.forwardData(cmd.stdout, encoding, done); + const stderr = this.collectData(cmd.stderr); + + cmd.on('error', (err: Error) => { + done(err); + }); + + cmd.on('close', (code: number) => { + if (code !== 0) { + done(new Error(`find failed with error code ${code}: ${this.decodeData(stderr, encoding)}`)); + } else { + done(null, '', true); + } + }); + } + + private forwardData(stream: Readable, encoding: string, cb: (err: Error, stdout?: string) => void): NodeStringDecoder { + const decoder = new StringDecoder(encoding); + stream.on('data', (data: Buffer) => { + cb(null, decoder.write(data)); + }); + return decoder; + } + + private collectData(stream: Readable): Buffer[] { + const buffers: Buffer[] = []; + stream.on('data', (data: Buffer) => { + buffers.push(data); + }); + return buffers; + } + + private decodeData(buffers: Buffer[], encoding: string): string { + const decoder = new StringDecoder(encoding); + return buffers.map(buffer => decoder.write(buffer)).join(''); + } + + private initDirectoryTree(): IDirectoryTree { + const tree: IDirectoryTree = { + rootEntries: [], + pathToEntries: Object.create(null) + }; + tree.pathToEntries['.'] = tree.rootEntries; + return tree; + } + + private addDirectoryEntries({ pathToEntries }: IDirectoryTree, base: string, relativeFiles: string[], onResult: (result: IRawFileMatch) => void) { + this.cmdResultCount += relativeFiles.length; + + // Support relative paths to files from a root resource (ignores excludes) + if (relativeFiles.indexOf(this.filePattern) !== -1) { + const basename = path.basename(this.filePattern); + this.matchFile(onResult, { base: base, relativePath: this.filePattern, basename }); + } + + function add(relativePath: string) { + const basename = path.basename(relativePath); + const dirname = path.dirname(relativePath); + let entries = pathToEntries[dirname]; + if (!entries) { + entries = pathToEntries[dirname] = []; + add(dirname); + } + entries.push({ + base, + relativePath, + basename + }); + } + relativeFiles.forEach(add); + } + + private matchDirectoryTree({ rootEntries, pathToEntries }: IDirectoryTree, rootFolder: string, onResult: (result: IRawFileMatch) => void) { + const self = this; + const excludePattern = this.folderExcludePatterns.get(rootFolder); + const filePattern = this.filePattern; + function matchDirectory(entries: IDirectoryEntry[]) { + self.directoriesWalked++; + for (let i = 0, n = entries.length; i < n; i++) { + const entry = entries[i]; + const { relativePath, basename } = entry; + + // Check exclude pattern + // If the user searches for the exact file name, we adjust the glob matching + // to ignore filtering by siblings because the user seems to know what she + // is searching for and we want to include the result in that case anyway + if (excludePattern.test(relativePath, basename, () => filePattern !== basename ? entries.map(entry => entry.basename) : [])) { + continue; + } + + const sub = pathToEntries[relativePath]; + if (sub) { + matchDirectory(sub); + } else { + self.filesWalked++; + if (relativePath === filePattern) { + continue; // ignore file if its path matches with the file pattern because that is already matched above + } + + self.matchFile(onResult, entry); + } + }; + } + matchDirectory(rootEntries); + } + + private nodeJSTraversal(folderQuery: IFolderSearch, onResult: (result: IRawFileMatch) => void, done: (err?: Error) => void): void { + this.directoriesWalked++; + extfs.readdir(folderQuery.folder, (error: Error, files: string[]) => { + if (error || this.isCanceled || this.isLimitHit) { + return done(); + } + + // Support relative paths to files from a root resource (ignores excludes) + return this.checkFilePatternRelativeMatch(folderQuery.folder, (match, size) => { + if (this.isCanceled || this.isLimitHit) { + return done(); + } + + // Report result from file pattern if matching + if (match) { + this.resultCount++; + onResult({ + base: folderQuery.folder, + relativePath: this.filePattern, + basename: path.basename(this.filePattern), + size + }); + } + + return this.doWalk(folderQuery, '', files, onResult, done); + }); + }); + } + + public getStats(): IUncachedSearchStats { + return { + fromCache: false, + traversal: Traversal[this.traversal], + errors: this.errors, + fileWalkStartTime: this.fileWalkStartTime, + fileWalkResultTime: Date.now(), + directoriesWalked: this.directoriesWalked, + filesWalked: this.filesWalked, + resultCount: this.resultCount, + cmdForkStartTime: this.cmdForkStartTime, + cmdForkResultTime: this.cmdForkResultTime, + cmdResultCount: this.cmdResultCount + }; + } + + private checkFilePatternAbsoluteMatch(clb: (exists: boolean, size?: number) => void): void { + if (!this.filePattern || !path.isAbsolute(this.filePattern)) { + return clb(false); + } + + return fs.stat(this.filePattern, (error, stat) => { + return clb(!error && !stat.isDirectory(), stat && stat.size); // only existing files + }); + } + + private checkFilePatternRelativeMatch(basePath: string, clb: (matchPath: string, size?: number) => void): void { + if (!this.filePattern || path.isAbsolute(this.filePattern)) { + return clb(null); + } + + const absolutePath = path.join(basePath, this.filePattern); + + return fs.stat(absolutePath, (error, stat) => { + return clb(!error && !stat.isDirectory() ? absolutePath : null, stat && stat.size); // only existing files + }); + } + + private doWalk(folderQuery: IFolderSearch, relativeParentPath: string, files: string[], onResult: (result: IRawFileMatch) => void, done: (error: Error) => void): void { + const rootFolder = folderQuery.folder; + + // Execute tasks on each file in parallel to optimize throughput + flow.parallel(files, (file: string, clb: (error: Error, result: {}) => void): void => { + + // Check canceled + if (this.isCanceled || this.isLimitHit) { + return clb(null, undefined); + } + + // If the user searches for the exact file name, we adjust the glob matching + // to ignore filtering by siblings because the user seems to know what she + // is searching for and we want to include the result in that case anyway + let siblings = files; + if (this.config.filePattern === file) { + siblings = []; + } + + // Check exclude pattern + let currentRelativePath = relativeParentPath ? [relativeParentPath, file].join(path.sep) : file; + if (this.folderExcludePatterns.get(folderQuery.folder).test(currentRelativePath, file, () => siblings)) { + return clb(null, undefined); + } + + // Use lstat to detect links + let currentAbsolutePath = [rootFolder, currentRelativePath].join(path.sep); + fs.lstat(currentAbsolutePath, (error, lstat) => { + if (error || this.isCanceled || this.isLimitHit) { + return clb(null, undefined); + } + + // If the path is a link, we must instead use fs.stat() to find out if the + // link is a directory or not because lstat will always return the stat of + // the link which is always a file. + this.statLinkIfNeeded(currentAbsolutePath, lstat, (error, stat) => { + if (error || this.isCanceled || this.isLimitHit) { + return clb(null, undefined); + } + + // Directory: Follow directories + if (stat.isDirectory()) { + this.directoriesWalked++; + + // to really prevent loops with links we need to resolve the real path of them + return this.realPathIfNeeded(currentAbsolutePath, lstat, (error, realpath) => { + if (error || this.isCanceled || this.isLimitHit) { + return clb(null, undefined); + } + + if (this.walkedPaths[realpath]) { + return clb(null, undefined); // escape when there are cycles (can happen with symlinks) + } + + this.walkedPaths[realpath] = true; // remember as walked + + // Continue walking + return extfs.readdir(currentAbsolutePath, (error: Error, children: string[]): void => { + if (error || this.isCanceled || this.isLimitHit) { + return clb(null, undefined); + } + + this.doWalk(folderQuery, currentRelativePath, children, onResult, err => clb(err, undefined)); + }); + }); + } + + // File: Check for match on file pattern and include pattern + else { + this.filesWalked++; + if (currentRelativePath === this.filePattern) { + return clb(null, undefined); // ignore file if its path matches with the file pattern because checkFilePatternRelativeMatch() takes care of those + } + + if (this.maxFilesize && types.isNumber(stat.size) && stat.size > this.maxFilesize) { + return clb(null, undefined); // ignore file if max file size is hit + } + + this.matchFile(onResult, { base: rootFolder, relativePath: currentRelativePath, basename: file, size: stat.size }); + } + + // Unwind + return clb(null, undefined); + }); + }); + }, (error: Error[]): void => { + if (error) { + error = arrays.coalesce(error); // find any error by removing null values first + } + + return done(error && error.length > 0 ? error[0] : null); + }); + } + + private matchFile(onResult: (result: IRawFileMatch) => void, candidate: IRawFileMatch): void { + if (this.isFilePatternMatch(candidate.relativePath) && (!this.includePattern || this.includePattern(candidate.relativePath, candidate.basename))) { + this.resultCount++; + + if (this.maxResults && this.resultCount > this.maxResults) { + this.isLimitHit = true; + } + + if (!this.isLimitHit) { + onResult(candidate); + } + } + } + + private isFilePatternMatch(path: string): boolean { + + // Check for search pattern + if (this.filePattern) { + if (this.filePattern === '*') { + return true; // support the all-matching wildcard + } + + return scorer.matches(path, this.normalizedFilePatternLowercase); + } + + // No patterns means we match all + return true; + } + + private statLinkIfNeeded(path: string, lstat: fs.Stats, clb: (error: Error, stat: fs.Stats) => void): void { + if (lstat.isSymbolicLink()) { + return fs.stat(path, clb); // stat the target the link points to + } + + return clb(null, lstat); // not a link, so the stat is already ok for us + } + + private realPathIfNeeded(path: string, lstat: fs.Stats, clb: (error: Error, realpath?: string) => void): void { + if (lstat.isSymbolicLink()) { + return fs.realpath(path, (error, realpath) => { + if (error) { + return clb(error); + } + + return clb(null, realpath); + }); + } + + return clb(null, path); + } +} + +export class Engine implements ISearchEngine { + private folderQueries: IFolderSearch[]; + private extraFiles: string[]; + private walker: FileWalker; + + constructor(config: IRawSearch) { + this.folderQueries = config.folderQueries; + this.extraFiles = config.extraFiles; + + this.walker = new FileWalker(config); + } + + public search(onResult: (result: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchComplete) => void): void { + this.walker.walk(this.folderQueries, this.extraFiles, onResult, (err: Error, isLimitHit: boolean) => { + done(err, { + limitHit: isLimitHit, + stats: this.walker.getStats() + }); + }); + } + + public cancel(): void { + this.walker.cancel(); + } +} + +/** + * This class exists to provide one interface on top of two ParsedExpressions, one for absolute expressions and one for relative expressions. + * The absolute and relative expressions don't "have" to be kept separate, but this keeps us from having to path.join every single + * file searched, it's only used for a text search with a searchPath + */ +class AbsoluteAndRelativeParsedExpression { + private absoluteParsedExpr: glob.ParsedExpression; + private relativeParsedExpr: glob.ParsedExpression; + + constructor(expr: glob.IExpression, private root: string) { + this.init(expr); + } + + /** + * Split the IExpression into its absolute and relative components, and glob.parse them separately. + */ + private init(expr: glob.IExpression): void { + let absoluteGlobExpr: glob.IExpression; + let relativeGlobExpr: glob.IExpression; + Object.keys(expr) + .filter(key => expr[key]) + .forEach(key => { + if (path.isAbsolute(key)) { + absoluteGlobExpr = absoluteGlobExpr || glob.getEmptyExpression(); + absoluteGlobExpr[key] = expr[key]; + } else { + relativeGlobExpr = relativeGlobExpr || glob.getEmptyExpression(); + relativeGlobExpr[key] = expr[key]; + } + }); + + this.absoluteParsedExpr = absoluteGlobExpr && glob.parse(absoluteGlobExpr, { trimForExclusions: true }); + this.relativeParsedExpr = relativeGlobExpr && glob.parse(relativeGlobExpr, { trimForExclusions: true }); + } + + public test(_path: string, basename?: string, siblingsFn?: () => string[] | TPromise): string | TPromise { + return (this.relativeParsedExpr && this.relativeParsedExpr(_path, basename, siblingsFn)) || + (this.absoluteParsedExpr && this.absoluteParsedExpr(path.join(this.root, _path), basename, siblingsFn)); + } + + public getBasenameTerms(): string[] { + const basenameTerms = []; + if (this.absoluteParsedExpr) { + basenameTerms.push(...glob.getBasenameTerms(this.absoluteParsedExpr)); + } + + if (this.relativeParsedExpr) { + basenameTerms.push(...glob.getBasenameTerms(this.relativeParsedExpr)); + } + + return basenameTerms; + } + + public getPathTerms(): string[] { + const pathTerms = []; + if (this.absoluteParsedExpr) { + pathTerms.push(...glob.getPathTerms(this.absoluteParsedExpr)); + } + + if (this.relativeParsedExpr) { + pathTerms.push(...glob.getPathTerms(this.relativeParsedExpr)); + } + + return pathTerms; + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts new file mode 100644 index 0000000000..c73bc8e372 --- /dev/null +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -0,0 +1,496 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import fs = require('fs'); +import { isAbsolute, sep } from 'path'; + +import gracefulFs = require('graceful-fs'); +gracefulFs.gracefulify(fs); + +import arrays = require('vs/base/common/arrays'); +import { compareByScore } from 'vs/base/common/comparers'; +import objects = require('vs/base/common/objects'); +import scorer = require('vs/base/common/scorer'); +import strings = require('vs/base/common/strings'); +import { PPromise, TPromise } from 'vs/base/common/winjs.base'; +import { FileWalker, Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; +import { MAX_FILE_SIZE } from 'vs/platform/files/common/files'; +import { RipgrepEngine } from 'vs/workbench/services/search/node/ripgrepTextSearch'; +import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch'; +import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider'; +import { IRawSearchService, IRawSearch, IRawFileMatch, ISerializedFileMatch, ISerializedSearchProgressItem, ISerializedSearchComplete, ISearchEngine, IFileSearchProgressItem } from './search'; +import { ICachedSearchStats, IProgress } from 'vs/platform/search/common/search'; + +export class SearchService implements IRawSearchService { + + private static BATCH_SIZE = 512; + + private caches: { [cacheKey: string]: Cache; } = Object.create(null); + + private textSearchWorkerProvider: TextSearchWorkerProvider; + + public fileSearch(config: IRawSearch): PPromise { + return this.doFileSearch(FileSearchEngine, config, SearchService.BATCH_SIZE); + } + + public textSearch(config: IRawSearch): PPromise { + return config.useRipgrep ? + this.ripgrepTextSearch(config) : + this.legacyTextSearch(config); + } + + public ripgrepTextSearch(config: IRawSearch): PPromise { + config.maxFilesize = MAX_FILE_SIZE; + let engine = new RipgrepEngine(config); + + return new PPromise((c, e, p) => { + // Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned + const collector = new BatchedCollector(SearchService.BATCH_SIZE, p); + engine.search((match) => { + collector.addItem(match, match.numMatches); + }, (message) => { + p(message); + }, (error, stats) => { + collector.flush(); + + if (error) { + e(error); + } else { + c(stats); + } + }); + }, () => { + engine.cancel(); + }); + } + + public legacyTextSearch(config: IRawSearch): PPromise { + if (!this.textSearchWorkerProvider) { + this.textSearchWorkerProvider = new TextSearchWorkerProvider(); + } + + let engine = new TextSearchEngine( + config, + new FileWalker({ + folderQueries: config.folderQueries, + extraFiles: config.extraFiles, + includePattern: config.includePattern, + excludePattern: config.excludePattern, + filePattern: config.filePattern, + maxFilesize: MAX_FILE_SIZE + }), + this.textSearchWorkerProvider); + + return this.doTextSearch(engine, SearchService.BATCH_SIZE); + } + + public doFileSearch(EngineClass: { new(config: IRawSearch): ISearchEngine; }, config: IRawSearch, batchSize?: number): PPromise { + + if (config.sortByScore) { + let sortedSearch = this.trySortedSearchFromCache(config); + if (!sortedSearch) { + const walkerConfig = config.maxResults ? objects.assign({}, config, { maxResults: null }) : config; + const engine = new EngineClass(walkerConfig); + sortedSearch = this.doSortedSearch(engine, config); + } + + return new PPromise((c, e, p) => { + process.nextTick(() => { // allow caller to register progress callback first + sortedSearch.then(([result, rawMatches]) => { + const serializedMatches = rawMatches.map(rawMatch => this.rawMatchToSearchItem(rawMatch)); + this.sendProgress(serializedMatches, p, batchSize); + c(result); + }, e, p); + }); + }, () => { + sortedSearch.cancel(); + }); + } + + let searchPromise: PPromise; + return new PPromise((c, e, p) => { + const engine = new EngineClass(config); + searchPromise = this.doSearch(engine, batchSize) + .then(c, e, progress => { + if (Array.isArray(progress)) { + p(progress.map(m => this.rawMatchToSearchItem(m))); + } else if ((progress).relativePath) { + p(this.rawMatchToSearchItem(progress)); + } else { + p(progress); + } + }); + }, () => { + searchPromise.cancel(); + }); + } + + private rawMatchToSearchItem(match: IRawFileMatch): ISerializedFileMatch { + return { path: match.base ? [match.base, match.relativePath].join(sep) : match.relativePath }; + } + + private doSortedSearch(engine: ISearchEngine, config: IRawSearch): PPromise<[ISerializedSearchComplete, IRawFileMatch[]], IProgress> { + let searchPromise: PPromise; + let allResultsPromise = new PPromise<[ISerializedSearchComplete, IRawFileMatch[]], IFileSearchProgressItem>((c, e, p) => { + let results: IRawFileMatch[] = []; + searchPromise = this.doSearch(engine, -1) + .then(result => { + c([result, results]); + }, e, progress => { + if (Array.isArray(progress)) { + results = progress; + } else { + p(progress); + } + }); + }, () => { + searchPromise.cancel(); + }); + + let cache: Cache; + if (config.cacheKey) { + cache = this.getOrCreateCache(config.cacheKey); + cache.resultsToSearchCache[config.filePattern] = allResultsPromise; + allResultsPromise.then(null, err => { + delete cache.resultsToSearchCache[config.filePattern]; + }); + allResultsPromise = this.preventCancellation(allResultsPromise); + } + + return new PPromise<[ISerializedSearchComplete, IRawFileMatch[]], IProgress>((c, e, p) => { + allResultsPromise.then(([result, results]) => { + const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); + const unsortedResultTime = Date.now(); + const sortedResults = this.sortResults(config, results, scorerCache); + const sortedResultTime = Date.now(); + + c([{ + stats: objects.assign({}, result.stats, { + unsortedResultTime, + sortedResultTime + }), + limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults + }, sortedResults]); + }, e, p); + }, () => { + allResultsPromise.cancel(); + }); + } + + private getOrCreateCache(cacheKey: string): Cache { + const existing = this.caches[cacheKey]; + if (existing) { + return existing; + } + return this.caches[cacheKey] = new Cache(); + } + + private trySortedSearchFromCache(config: IRawSearch): PPromise<[ISerializedSearchComplete, IRawFileMatch[]], IProgress> { + const cache = config.cacheKey && this.caches[config.cacheKey]; + if (!cache) { + return undefined; + } + + const cacheLookupStartTime = Date.now(); + const cached = this.getResultsFromCache(cache, config.filePattern); + if (cached) { + return new PPromise<[ISerializedSearchComplete, IRawFileMatch[]], IProgress>((c, e, p) => { + cached.then(([result, results, cacheStats]) => { + const cacheLookupResultTime = Date.now(); + const sortedResults = this.sortResults(config, results, cache.scorerCache); + const sortedResultTime = Date.now(); + + const stats: ICachedSearchStats = { + fromCache: true, + cacheLookupStartTime: cacheLookupStartTime, + cacheFilterStartTime: cacheStats.cacheFilterStartTime, + cacheLookupResultTime: cacheLookupResultTime, + cacheEntryCount: cacheStats.cacheFilterResultCount, + resultCount: results.length + }; + if (config.sortByScore) { + stats.unsortedResultTime = cacheLookupResultTime; + stats.sortedResultTime = sortedResultTime; + } + if (!cacheStats.cacheWasResolved) { + stats.joined = result.stats; + } + c([ + { + limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults, + stats: stats + }, + sortedResults + ]); + }, e, p); + }, () => { + cached.cancel(); + }); + } + return undefined; + } + + private sortResults(config: IRawSearch, results: IRawFileMatch[], scorerCache: ScorerCache): IRawFileMatch[] { + const filePattern = config.filePattern; + const normalizedSearchValue = strings.stripWildcards(filePattern).toLowerCase(); + const compare = (elementA: IRawFileMatch, elementB: IRawFileMatch) => compareByScore(elementA, elementB, FileMatchAccessor, filePattern, normalizedSearchValue, scorerCache); + return arrays.top(results, compare, config.maxResults); + } + + private sendProgress(results: ISerializedFileMatch[], progressCb: (batch: ISerializedFileMatch[]) => void, batchSize: number) { + if (batchSize && batchSize > 0) { + for (let i = 0; i < results.length; i += batchSize) { + progressCb(results.slice(i, i + batchSize)); + } + } else { + progressCb(results); + } + } + + private getResultsFromCache(cache: Cache, searchValue: string): PPromise<[ISerializedSearchComplete, IRawFileMatch[], CacheStats], IProgress> { + if (isAbsolute(searchValue)) { + return null; // bypass cache if user looks up an absolute path where matching goes directly on disk + } + + // Find cache entries by prefix of search value + const hasPathSep = searchValue.indexOf(sep) >= 0; + let cached: PPromise<[ISerializedSearchComplete, IRawFileMatch[]], IFileSearchProgressItem>; + let wasResolved: boolean; + for (let previousSearch in cache.resultsToSearchCache) { + + // If we narrow down, we might be able to reuse the cached results + if (strings.startsWith(searchValue, previousSearch)) { + if (hasPathSep && previousSearch.indexOf(sep) < 0) { + continue; // since a path character widens the search for potential more matches, require it in previous search too + } + + const c = cache.resultsToSearchCache[previousSearch]; + c.then(() => { wasResolved = false; }); + wasResolved = true; + cached = this.preventCancellation(c); + break; + } + } + + if (!cached) { + return null; + } + + return new PPromise<[ISerializedSearchComplete, IRawFileMatch[], CacheStats], IProgress>((c, e, p) => { + cached.then(([complete, cachedEntries]) => { + const cacheFilterStartTime = Date.now(); + + // Pattern match on results + let results: IRawFileMatch[] = []; + const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase(); + for (let i = 0; i < cachedEntries.length; i++) { + let entry = cachedEntries[i]; + + // Check if this entry is a match for the search value + if (!scorer.matches(entry.relativePath, normalizedSearchValueLowercase)) { + continue; + } + + results.push(entry); + } + + c([complete, results, { + cacheWasResolved: wasResolved, + cacheFilterStartTime: cacheFilterStartTime, + cacheFilterResultCount: cachedEntries.length + }]); + }, e, p); + }, () => { + cached.cancel(); + }); + } + + private doTextSearch(engine: TextSearchEngine, batchSize: number): PPromise { + return new PPromise((c, e, p) => { + // Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned + const collector = new BatchedCollector(batchSize, p); + engine.search((matches) => { + const totalMatches = matches.reduce((acc, m) => acc + m.numMatches, 0); + collector.addItems(matches, totalMatches); + }, (progress) => { + p(progress); + }, (error, stats) => { + collector.flush(); + + if (error) { + e(error); + } else { + c(stats); + } + }); + }, () => { + engine.cancel(); + }); + } + + private doSearch(engine: ISearchEngine, batchSize?: number): PPromise { + return new PPromise((c, e, p) => { + let batch: IRawFileMatch[] = []; + engine.search((match) => { + if (match) { + if (batchSize) { + batch.push(match); + if (batchSize > 0 && batch.length >= batchSize) { + p(batch); + batch = []; + } + } else { + p(match); + } + } + }, (progress) => { + p(progress); + }, (error, stats) => { + if (batch.length) { + p(batch); + } + if (error) { + e(error); + } else { + c(stats); + } + }); + }, () => { + engine.cancel(); + }); + } + + public clearCache(cacheKey: string): TPromise { + delete this.caches[cacheKey]; + return TPromise.as(undefined); + } + + private preventCancellation(promise: PPromise): PPromise { + return new PPromise((c, e, p) => { + // Allow for piled up cancellations to come through first. + process.nextTick(() => { + promise.then(c, e, p); + }); + }, () => { + // Do not propagate. + }); + } +} + +class Cache { + + public resultsToSearchCache: { [searchValue: string]: PPromise<[ISerializedSearchComplete, IRawFileMatch[]], IFileSearchProgressItem>; } = Object.create(null); + + public scorerCache: ScorerCache = Object.create(null); +} + +interface ScorerCache { + [key: string]: number; +} + +class FileMatchAccessor { + + public static getLabel(match: IRawFileMatch): string { + return match.basename; + } + + public static getResourcePath(match: IRawFileMatch): string { + return match.relativePath; + } +} + +interface CacheStats { + cacheWasResolved: boolean; + cacheFilterStartTime: number; + cacheFilterResultCount: number; +} + +/** + * Collects items that have a size - before the cumulative size of collected items reaches START_BATCH_AFTER_COUNT, the callback is called for every + * set of items collected. + * But after that point, the callback is called with batches of maxBatchSize. + * If the batch isn't filled within some time, the callback is also called. + */ +class BatchedCollector { + private static TIMEOUT = 4000; + + // After RUN_TIMEOUT_UNTIL_COUNT items have been collected, stop flushing on timeout + private static START_BATCH_AFTER_COUNT = 50; + + private totalNumberCompleted = 0; + private batch: T[] = []; + private batchSize = 0; + private timeoutHandle: number; + + constructor(private maxBatchSize: number, private cb: (items: T | T[]) => void) { + } + + addItem(item: T, size: number): void { + if (!item) { + return; + } + + if (this.maxBatchSize > 0) { + this.addItemToBatch(item, size); + } else { + this.cb(item); + } + } + + addItems(items: T[], size: number): void { + if (!items) { + return; + } + + if (this.maxBatchSize > 0) { + this.addItemsToBatch(items, size); + } else { + this.cb(items); + } + } + + private addItemToBatch(item: T, size: number): void { + this.batch.push(item); + this.batchSize += size; + this.onUpdate(); + } + + private addItemsToBatch(item: T[], size: number): void { + this.batch = this.batch.concat(item); + this.batchSize += size; + this.onUpdate(); + } + + private onUpdate(): void { + if (this.totalNumberCompleted < BatchedCollector.START_BATCH_AFTER_COUNT) { + // Flush because we aren't batching yet + this.flush(); + } else if (this.batchSize >= this.maxBatchSize) { + // Flush because the batch is full + this.flush(); + } else if (!this.timeoutHandle) { + // No timeout running, start a timeout to flush + this.timeoutHandle = setTimeout(() => { + this.flush(); + }, BatchedCollector.TIMEOUT); + } + } + + flush(): void { + if (this.batchSize) { + this.totalNumberCompleted += this.batchSize; + this.cb(this.batch); + this.batch = []; + this.batchSize = 0; + + if (this.timeoutHandle) { + clearTimeout(this.timeoutHandle); + this.timeoutHandle = 0; + } + } + } +} diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts new file mode 100644 index 0000000000..cb403723ba --- /dev/null +++ b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts @@ -0,0 +1,553 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { EventEmitter } from 'events'; +import * as path from 'path'; +import { StringDecoder, NodeStringDecoder } from 'string_decoder'; + +import * as cp from 'child_process'; +import { rgPath } from 'vscode-ripgrep'; + +import objects = require('vs/base/common/objects'); +import platform = require('vs/base/common/platform'); +import * as strings from 'vs/base/common/strings'; +import * as paths from 'vs/base/common/paths'; +import * as extfs from 'vs/base/node/extfs'; +import * as encoding from 'vs/base/node/encoding'; +import * as glob from 'vs/base/common/glob'; +import { ILineMatch, ISearchLog } from 'vs/platform/search/common/search'; +import { TPromise } from 'vs/base/common/winjs.base'; + +import { ISerializedFileMatch, ISerializedSearchComplete, IRawSearch, IFolderSearch } from './search'; + +export class RipgrepEngine { + private isDone = false; + private rgProc: cp.ChildProcess; + private postProcessExclusions: glob.ParsedExpression; + + private ripgrepParser: RipgrepParser; + + private resultsHandledP: TPromise = TPromise.wrap(null); + + constructor(private config: IRawSearch) { + } + + cancel(): void { + this.isDone = true; + this.ripgrepParser.cancel(); + this.rgProc.kill(); + } + + // TODO@Rob - make promise-based once the old search is gone, and I don't need them to have matching interfaces anymore + search(onResult: (match: ISerializedFileMatch) => void, onMessage: (message: ISearchLog) => void, done: (error: Error, complete: ISerializedSearchComplete) => void): void { + if (!this.config.folderQueries.length && !this.config.extraFiles.length) { + done(null, { + limitHit: false, + stats: null + }); + return; + } + + const rgArgs = getRgArgs(this.config); + if (rgArgs.siblingClauses) { + this.postProcessExclusions = glob.parseToAsync(rgArgs.siblingClauses, { trimForExclusions: true }); + } + + const cwd = platform.isWindows ? 'c:/' : '/'; + process.nextTick(() => { // Allow caller to register progress callback + const escapedArgs = rgArgs.globArgs + .map(arg => arg.match(/^-/) ? arg : `'${arg}'`) + .join(' '); + + const rgCmd = `rg ${escapedArgs}\n - cwd: ${cwd}\n`; + onMessage({ message: rgCmd }); + if (rgArgs.siblingClauses) { + onMessage({ message: ` - Sibling clauses: ${JSON.stringify(rgArgs.siblingClauses)}\n` }); + } + }); + this.rgProc = cp.spawn(rgPath, rgArgs.globArgs, { cwd }); + + this.ripgrepParser = new RipgrepParser(this.config.maxResults, cwd); + this.ripgrepParser.on('result', (match: ISerializedFileMatch) => { + if (this.postProcessExclusions) { + const handleResultP = (>this.postProcessExclusions(match.path, undefined, () => getSiblings(match.path))) + .then(globMatch => { + if (!globMatch) { + onResult(match); + } + }); + + this.resultsHandledP = TPromise.join([this.resultsHandledP, handleResultP]); + } else { + onResult(match); + } + }); + this.ripgrepParser.on('hitLimit', () => { + this.cancel(); + done(null, { + limitHit: true, + stats: null + }); + }); + + this.rgProc.stdout.on('data', data => { + this.ripgrepParser.handleData(data); + }); + + let gotData = false; + this.rgProc.stdout.once('data', () => gotData = true); + + let stderr = ''; + this.rgProc.stderr.on('data', data => { + const message = data.toString(); + onMessage({ message }); + stderr += message; + }); + + this.rgProc.on('close', code => { + // Trigger last result, then wait on async result handling + this.ripgrepParser.flush(); + this.resultsHandledP.then(() => { + this.rgProc = null; + if (!this.isDone) { + this.isDone = true; + let displayMsg: string; + if (stderr && !gotData && (displayMsg = this.rgErrorMsgForDisplay(stderr))) { + done(new Error(displayMsg), { + limitHit: false, + stats: null + }); + } else { + done(null, { + limitHit: false, + stats: null + }); + } + } + }); + }); + } + + /** + * Read the first line of stderr and return an error for display or undefined, based on a whitelist. + * Ripgrep produces stderr output which is not from a fatal error, and we only want the search to be + * "failed" when a fatal error was produced. + */ + private rgErrorMsgForDisplay(msg: string): string | undefined { + const firstLine = msg.split('\n')[0]; + + if (strings.startsWith(firstLine, 'Error parsing regex')) { + return firstLine; + } + + if (strings.startsWith(firstLine, 'error parsing glob')) { + return firstLine; + } + + return undefined; + } +} + +export class RipgrepParser extends EventEmitter { + private static RESULT_REGEX = /^\u001b\[m(\d+)\u001b\[m:(.*)(\r?)/; + private static FILE_REGEX = /^\u001b\[m(.+)\u001b\[m$/; + + public static MATCH_START_MARKER = '\u001b[m\u001b[31m'; + public static MATCH_END_MARKER = '\u001b[m'; + + private fileMatch: FileMatch; + private remainder: string; + private isDone: boolean; + private stringDecoder: NodeStringDecoder; + + private numResults = 0; + + constructor(private maxResults: number, private rootFolder: string) { + super(); + this.stringDecoder = new StringDecoder(); + } + + public cancel(): void { + this.isDone = true; + } + + public flush(): void { + this.handleDecodedData(this.stringDecoder.end()); + + if (this.fileMatch) { + this.onResult(); + } + } + + public handleData(data: Buffer | string): void { + const dataStr = typeof data === 'string' ? data : this.stringDecoder.write(data); + this.handleDecodedData(dataStr); + } + + private handleDecodedData(decodedData: string): void { + // If the previous data chunk didn't end in a newline, prepend it to this chunk + const dataStr = this.remainder ? + this.remainder + decodedData : + decodedData; + + const dataLines: string[] = dataStr.split(/\r\n|\n/); + this.remainder = dataLines[dataLines.length - 1] ? dataLines.pop() : null; + + for (let l = 0; l < dataLines.length; l++) { + const outputLine = dataLines[l].trim(); + if (this.isDone) { + break; + } + + let r: RegExpMatchArray; + if (r = outputLine.match(RipgrepParser.RESULT_REGEX)) { + const lineNum = parseInt(r[1]) - 1; + let matchText = r[2]; + + // workaround https://github.com/BurntSushi/ripgrep/issues/416 + // If the match line ended with \r, append a match end marker so the match isn't lost + if (r[3]) { + matchText += RipgrepParser.MATCH_END_MARKER; + } + + // Line is a result - add to collected results for the current file path + this.handleMatchLine(outputLine, lineNum, matchText); + } else if (r = outputLine.match(RipgrepParser.FILE_REGEX)) { + // Line is a file path - send all collected results for the previous file path + if (this.fileMatch) { + this.onResult(); + } + + this.fileMatch = new FileMatch(path.isAbsolute(r[1]) ? r[1] : path.join(this.rootFolder, r[1])); + } else { + // Line is empty (or malformed) + } + } + } + + private handleMatchLine(outputLine: string, lineNum: number, text: string): void { + const lineMatch = new LineMatch(text, lineNum); + this.fileMatch.addMatch(lineMatch); + + let lastMatchEndPos = 0; + let matchTextStartPos = -1; + + // Track positions with color codes subtracted - offsets in the final text preview result + let matchTextStartRealIdx = -1; + let textRealIdx = 0; + let hitLimit = false; + + const realTextParts: string[] = []; + + for (let i = 0; i < text.length - (RipgrepParser.MATCH_END_MARKER.length - 1);) { + if (text.substr(i, RipgrepParser.MATCH_START_MARKER.length) === RipgrepParser.MATCH_START_MARKER) { + // Match start + const chunk = text.slice(lastMatchEndPos, i); + realTextParts.push(chunk); + i += RipgrepParser.MATCH_START_MARKER.length; + matchTextStartPos = i; + matchTextStartRealIdx = textRealIdx; + } else if (text.substr(i, RipgrepParser.MATCH_END_MARKER.length) === RipgrepParser.MATCH_END_MARKER) { + // Match end + const chunk = text.slice(matchTextStartPos, i); + realTextParts.push(chunk); + if (!hitLimit) { + lineMatch.addMatch(matchTextStartRealIdx, textRealIdx - matchTextStartRealIdx); + } + + matchTextStartPos = -1; + matchTextStartRealIdx = -1; + i += RipgrepParser.MATCH_END_MARKER.length; + lastMatchEndPos = i; + this.numResults++; + + // Check hit maxResults limit + if (this.numResults >= this.maxResults) { + // Finish the line, then report the result below + hitLimit = true; + } + } else { + i++; + textRealIdx++; + } + } + + const chunk = text.slice(lastMatchEndPos); + realTextParts.push(chunk); + + // Replace preview with version without color codes + const preview = realTextParts.join(''); + lineMatch.preview = preview; + + if (hitLimit) { + this.cancel(); + this.onResult(); + this.emit('hitLimit'); + } + } + + private onResult(): void { + this.emit('result', this.fileMatch.serialize()); + this.fileMatch = null; + } +} + +export class FileMatch implements ISerializedFileMatch { + path: string; + lineMatches: LineMatch[]; + + constructor(path: string) { + this.path = path; + this.lineMatches = []; + } + + addMatch(lineMatch: LineMatch): void { + this.lineMatches.push(lineMatch); + } + + isEmpty(): boolean { + return this.lineMatches.length === 0; + } + + serialize(): ISerializedFileMatch { + let lineMatches: ILineMatch[] = []; + let numMatches = 0; + + for (let i = 0; i < this.lineMatches.length; i++) { + numMatches += this.lineMatches[i].offsetAndLengths.length; + lineMatches.push(this.lineMatches[i].serialize()); + } + + return { + path: this.path, + lineMatches, + numMatches + }; + } +} + +export class LineMatch implements ILineMatch { + preview: string; + lineNumber: number; + offsetAndLengths: number[][]; + + constructor(preview: string, lineNumber: number) { + this.preview = preview.replace(/(\r|\n)*$/, ''); + this.lineNumber = lineNumber; + this.offsetAndLengths = []; + } + + getText(): string { + return this.preview; + } + + getLineNumber(): number { + return this.lineNumber; + } + + addMatch(offset: number, length: number): void { + this.offsetAndLengths.push([offset, length]); + } + + serialize(): ILineMatch { + const result = { + preview: this.preview, + lineNumber: this.lineNumber, + offsetAndLengths: this.offsetAndLengths + }; + + return result; + } +} + +interface IRgGlobResult { + globArgs: string[]; + siblingClauses: glob.IExpression; +} + +function foldersToRgExcludeGlobs(folderQueries: IFolderSearch[], globalExclude: glob.IExpression, excludesToSkip: Set): IRgGlobResult { + const globArgs: string[] = []; + let siblingClauses: glob.IExpression = {}; + folderQueries.forEach(folderQuery => { + const totalExcludePattern = objects.assign({}, folderQuery.excludePattern || {}, globalExclude || {}); + const result = globExprsToRgGlobs(totalExcludePattern, folderQuery.folder, excludesToSkip); + globArgs.push(...result.globArgs); + if (result.siblingClauses) { + siblingClauses = objects.assign(siblingClauses, result.siblingClauses); + } + }); + + return { globArgs, siblingClauses }; +} + +function foldersToIncludeGlobs(folderQueries: IFolderSearch[], globalInclude: glob.IExpression): string[] { + const globArgs = []; + folderQueries.forEach(folderQuery => { + const totalIncludePattern = objects.assign({}, globalInclude || {}, folderQuery.includePattern || {}); + const result = globExprsToRgGlobs(totalIncludePattern, folderQuery.folder); + globArgs.push(...result.globArgs); + }); + + return globArgs; +} + +function globExprsToRgGlobs(patterns: glob.IExpression, folder: string, excludesToSkip?: Set): IRgGlobResult { + const globArgs: string[] = []; + let siblingClauses: glob.IExpression = null; + Object.keys(patterns) + .forEach(key => { + if (excludesToSkip && excludesToSkip.has(key)) { + return; + } + + const value = patterns[key]; + key = getAbsoluteGlob(folder, key); + + if (typeof value === 'boolean' && value) { + globArgs.push(fixDriveC(key)); + } else if (value && value.when) { + if (!siblingClauses) { + siblingClauses = {}; + } + + siblingClauses[key] = value; + } + }); + + return { globArgs, siblingClauses }; +} + +/** + * Resolves a glob like "node_modules/**" in "/foo/bar" to "/foo/bar/node_modules/**". + * Special cases C:/foo paths to write the glob like /foo instead - see https://github.com/BurntSushi/ripgrep/issues/530. + * + * Exported for testing + */ +export function getAbsoluteGlob(folder: string, key: string): string { + const absolute = paths.isAbsolute(key) ? + key : + path.join(folder, key); + + return trimTrailingSlash(absolute); +} + +function trimTrailingSlash(str: string): string { + str = strings.rtrim(str, '\\'); + return strings.rtrim(str, '/'); +} + +export function fixDriveC(path: string): string { + const root = paths.getRoot(path); + return root.toLowerCase() === 'c:/' ? + path.replace(/^c:[/\\]/i, '/') : + path; +} + +function getRgArgs(config: IRawSearch): IRgGlobResult { + const args = ['--hidden', '--heading', '--line-number', '--color', 'ansi', '--colors', 'path:none', '--colors', 'line:none', '--colors', 'match:fg:red', '--colors', 'match:style:nobold']; + args.push(config.contentPattern.isCaseSensitive ? '--case-sensitive' : '--ignore-case'); + + // includePattern can't have siblingClauses + foldersToIncludeGlobs(config.folderQueries, config.includePattern).forEach(globArg => { + args.push('-g', globArg); + }); + + let siblingClauses: glob.IExpression; + + // Find excludes that are exactly the same in all folderQueries - e.g. from user settings, and that start with `**`. + // To make the command shorter, don't resolve these against every folderQuery path - see #33189. + const universalExcludes = findUniversalExcludes(config.folderQueries); + const rgGlobs = foldersToRgExcludeGlobs(config.folderQueries, config.excludePattern, universalExcludes); + rgGlobs.globArgs + .forEach(rgGlob => args.push('-g', `!${rgGlob}`)); + if (universalExcludes) { + universalExcludes + .forEach(exclude => args.push('-g', `!${trimTrailingSlash(exclude)}`)); + } + siblingClauses = rgGlobs.siblingClauses; + + if (config.maxFilesize) { + args.push('--max-filesize', config.maxFilesize + ''); + } + + if (config.disregardIgnoreFiles) { + // Don't use .gitignore or .ignore + args.push('--no-ignore'); + } + + // Follow symlinks + args.push('--follow'); + + // Set default encoding if only one folder is opened + if (config.folderQueries.length === 1 && config.folderQueries[0].fileEncoding && config.folderQueries[0].fileEncoding !== 'utf8') { + args.push('--encoding', encoding.toCanonicalName(config.folderQueries[0].fileEncoding)); + } + + // Ripgrep handles -- as a -- arg separator. Only --. + // - is ok, --- is ok, --some-flag is handled as query text. Need to special case. + if (config.contentPattern.pattern === '--') { + config.contentPattern.isRegExp = true; + config.contentPattern.pattern = '\\-\\-'; + } + + let searchPatternAfterDoubleDashes: string; + if (config.contentPattern.isWordMatch) { + const regexp = strings.createRegExp(config.contentPattern.pattern, config.contentPattern.isRegExp, { wholeWord: config.contentPattern.isWordMatch }); + const regexpStr = regexp.source.replace(/\\\//g, '/'); // RegExp.source arbitrarily returns escaped slashes. Search and destroy. + args.push('--regexp', regexpStr); + } else if (config.contentPattern.isRegExp) { + args.push('--regexp', config.contentPattern.pattern); + } else { + searchPatternAfterDoubleDashes = config.contentPattern.pattern; + args.push('--fixed-strings'); + } + + // Folder to search + args.push('--'); + + if (searchPatternAfterDoubleDashes) { + // Put the query after --, in case the query starts with a dash + args.push(searchPatternAfterDoubleDashes); + } + + args.push(...config.folderQueries.map(q => q.folder)); + args.push(...config.extraFiles); + + return { globArgs: args, siblingClauses }; +} + +function getSiblings(file: string): TPromise { + return new TPromise((resolve, reject) => { + extfs.readdir(path.dirname(file), (error: Error, files: string[]) => { + if (error) { + reject(error); + } + + resolve(files); + }); + }); +} + +function findUniversalExcludes(folderQueries: IFolderSearch[]): Set { + if (folderQueries.length < 2) { + // Nothing to simplify + return null; + } + + const firstFolder = folderQueries[0]; + if (!firstFolder.excludePattern) { + return null; + } + + const universalExcludes = new Set(); + Object.keys(firstFolder.excludePattern).forEach(key => { + if (strings.startsWith(key, '**') && folderQueries.every(q => q.excludePattern && q.excludePattern[key] === true)) { + universalExcludes.add(key); + } + }); + + return universalExcludes; +} diff --git a/src/vs/workbench/services/search/node/search.ts b/src/vs/workbench/services/search/node/search.ts new file mode 100644 index 0000000000..3aff65d1bf --- /dev/null +++ b/src/vs/workbench/services/search/node/search.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { PPromise, TPromise } from 'vs/base/common/winjs.base'; +import { IExpression } from 'vs/base/common/glob'; +import { IProgress, ILineMatch, IPatternInfo, ISearchStats, ISearchLog } from 'vs/platform/search/common/search'; + +export interface IFolderSearch { + folder: string; + excludePattern?: IExpression; + includePattern?: IExpression; + fileEncoding?: string; +} + +export interface IRawSearch { + folderQueries: IFolderSearch[]; + extraFiles?: string[]; + filePattern?: string; + excludePattern?: IExpression; + includePattern?: IExpression; + contentPattern?: IPatternInfo; + maxResults?: number; + sortByScore?: boolean; + cacheKey?: string; + maxFilesize?: number; + useRipgrep?: boolean; + disregardIgnoreFiles?: boolean; +} + +export interface IRawSearchService { + fileSearch(search: IRawSearch): PPromise; + textSearch(search: IRawSearch): PPromise; + clearCache(cacheKey: string): TPromise; +} + +export interface IRawFileMatch { + base?: string; + relativePath: string; + basename: string; + size?: number; +} + +export interface ISearchEngine { + search: (onResult: (matches: T) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchComplete) => void) => void; + cancel: () => void; +} + +export interface ISerializedSearchComplete { + limitHit: boolean; + stats: ISearchStats; +} + +export interface ISerializedFileMatch { + path: string; + lineMatches?: ILineMatch[]; + numMatches?: number; +} + +// Type of the possible values for progress calls from the engine +export type ISerializedSearchProgressItem = ISerializedFileMatch | ISerializedFileMatch[] | IProgress | ISearchLog; +export type IFileSearchProgressItem = IRawFileMatch | IRawFileMatch[] | IProgress; \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/searchApp.ts b/src/vs/workbench/services/search/node/searchApp.ts new file mode 100644 index 0000000000..772fd69037 --- /dev/null +++ b/src/vs/workbench/services/search/node/searchApp.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; +import { SearchChannel } from './searchIpc'; +import { SearchService } from './rawSearchService'; + +const server = new Server(); +const service = new SearchService(); +const channel = new SearchChannel(service); +server.registerChannel('search', channel); \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/searchIpc.ts b/src/vs/workbench/services/search/node/searchIpc.ts new file mode 100644 index 0000000000..e02dc9b615 --- /dev/null +++ b/src/vs/workbench/services/search/node/searchIpc.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { PPromise, TPromise } from 'vs/base/common/winjs.base'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IRawSearchService, IRawSearch, ISerializedSearchComplete, ISerializedSearchProgressItem } from './search'; + +export interface ISearchChannel extends IChannel { + call(command: 'fileSearch', search: IRawSearch): PPromise; + call(command: 'textSearch', search: IRawSearch): PPromise; + call(command: 'clearCache', cacheKey: string): TPromise; + call(command: string, arg: any): TPromise; +} + +export class SearchChannel implements ISearchChannel { + + constructor(private service: IRawSearchService) { } + + call(command: string, arg: any): TPromise { + switch (command) { + case 'fileSearch': return this.service.fileSearch(arg); + case 'textSearch': return this.service.textSearch(arg); + case 'clearCache': return this.service.clearCache(arg); + } + return undefined; + } +} + +export class SearchChannelClient implements IRawSearchService { + + constructor(private channel: ISearchChannel) { } + + fileSearch(search: IRawSearch): PPromise { + return this.channel.call('fileSearch', search); + } + + textSearch(search: IRawSearch): PPromise { + return this.channel.call('textSearch', search); + } + + public clearCache(cacheKey: string): TPromise { + return this.channel.call('clearCache', cacheKey); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts new file mode 100644 index 0000000000..32b848105e --- /dev/null +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -0,0 +1,321 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { PPromise, TPromise } from 'vs/base/common/winjs.base'; +import uri from 'vs/base/common/uri'; +import objects = require('vs/base/common/objects'); +import scorer = require('vs/base/common/scorer'); +import strings = require('vs/base/common/strings'); +import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; +import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; +import { IProgress, LineMatch, FileMatch, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchQuery, ISearchConfiguration, ISearchService, pathIncludedInQuery, ISearchResultProvider } from 'vs/platform/search/common/search'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IRawSearch, IFolderSearch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedFileMatch, IRawSearchService } from './search'; +import { ISearchChannel, SearchChannelClient } from './searchIpc'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ResourceMap } from 'vs/base/common/map'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export class SearchService implements ISearchService { + public _serviceBrand: any; + + private diskSearch: DiskSearch; + private readonly searchProvider: ISearchResultProvider[] = []; + + constructor( + @IModelService private modelService: IModelService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IEnvironmentService environmentService: IEnvironmentService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IConfigurationService private configurationService: IConfigurationService + ) { + this.diskSearch = new DiskSearch(!environmentService.isBuilt || environmentService.verbose); + this.registerSearchResultProvider(this.diskSearch); + } + + public registerSearchResultProvider(provider: ISearchResultProvider): IDisposable { + this.searchProvider.push(provider); + return { + dispose: () => { + const idx = this.searchProvider.indexOf(provider); + if (idx >= 0) { + this.searchProvider.splice(idx, 1); + } + } + }; + } + + public extendQuery(query: ISearchQuery): void { + const configuration = this.configurationService.getConfiguration(); + + // Configuration: Encoding + if (!query.fileEncoding) { + const fileEncoding = configuration && configuration.files && configuration.files.encoding; + query.fileEncoding = fileEncoding; + } + + // Configuration: File Excludes + if (!query.disregardExcludeSettings) { + const fileExcludes = configuration && configuration.files && configuration.files.exclude; + if (fileExcludes) { + if (!query.excludePattern) { + query.excludePattern = fileExcludes; + } else { + objects.mixin(query.excludePattern, fileExcludes, false /* no overwrite */); + } + } + } + } + + public search(query: ISearchQuery): PPromise { + + let combinedPromise: TPromise; + + return new PPromise((onComplete, onError, onProgress) => { + + // Get local results from dirty/untitled + const localResults = this.getLocalResults(query); + + // Allow caller to register progress callback + process.nextTick(() => localResults.values().filter((res) => !!res).forEach(onProgress)); + + const providerPromises = this.searchProvider.map(provider => TPromise.wrap(provider.search(query)).then(e => e, + err => { + // TODO@joh + // single provider fail. fail all? + onError(err); + }, + progress => { + if (progress.resource) { + // Match + if (!localResults.has(progress.resource)) { // don't override local results + onProgress(progress); + } + } else { + // Progress + onProgress(progress); + } + } + )); + + combinedPromise = TPromise.join(providerPromises).then(values => { + + const result: ISearchComplete = { + limitHit: false, + results: [], + stats: undefined + }; + + // TODO@joh + // sorting, disjunct results + for (const value of values) { + if (!value) { + continue; + } + // TODO@joh individual stats/limit + result.stats = value.stats || result.stats; + result.limitHit = value.limitHit || result.limitHit; + + for (const match of value.results) { + if (!localResults.has(match.resource)) { + result.results.push(match); + } + } + } + + return result; + + }).then(onComplete, onError); + + }, () => combinedPromise && combinedPromise.cancel()); + } + + private getLocalResults(query: ISearchQuery): ResourceMap { + const localResults = new ResourceMap(); + + if (query.type === QueryType.Text) { + let models = this.modelService.getModels(); + models.forEach((model) => { + let resource = model.uri; + if (!resource) { + return; + } + + // Support untitled files + if (resource.scheme === 'untitled') { + if (!this.untitledEditorService.exists(resource)) { + return; + } + } + + // Don't support other resource schemes than files for now + else if (resource.scheme !== 'file') { + return; + } + + if (!this.matches(resource, query)) { + return; // respect user filters + } + + // Use editor API to find matches + let matches = model.findMatches(query.contentPattern.pattern, false, query.contentPattern.isRegExp, query.contentPattern.isCaseSensitive, query.contentPattern.isWordMatch ? query.contentPattern.wordSeparators : null, false, query.maxResults); + if (matches.length) { + let fileMatch = new FileMatch(resource); + localResults.set(resource, fileMatch); + + matches.forEach((match) => { + fileMatch.lineMatches.push(new LineMatch(model.getLineContent(match.range.startLineNumber), match.range.startLineNumber - 1, [[match.range.startColumn - 1, match.range.endColumn - match.range.startColumn]])); + }); + } else { + localResults.set(resource, null); + } + }); + } + + return localResults; + } + + private matches(resource: uri, query: ISearchQuery): boolean { + // file pattern + if (query.filePattern) { + if (resource.scheme !== 'file') { + return false; // if we match on file pattern, we have to ignore non file resources + } + + if (!scorer.matches(resource.fsPath, strings.stripWildcards(query.filePattern).toLowerCase())) { + return false; + } + } + + // includes + if (query.includePattern) { + if (resource.scheme !== 'file') { + return false; // if we match on file patterns, we have to ignore non file resources + } + } + + return pathIncludedInQuery(query, resource.fsPath); + } + + public clearCache(cacheKey: string): TPromise { + return this.diskSearch.clearCache(cacheKey); + } +} + +export class DiskSearch implements ISearchResultProvider { + + private raw: IRawSearchService; + + constructor(verboseLogging: boolean, timeout: number = 60 * 60 * 1000) { + const client = new Client( + uri.parse(require.toUrl('bootstrap')).fsPath, + { + serverName: 'Search', + timeout: timeout, + args: ['--type=searchService'], + // See https://github.com/Microsoft/vscode/issues/27665 + // Pass in fresh execArgv to the forked process such that it doesn't inherit them from `process.execArgv`. + // e.g. Launching the extension host process with `--inspect-brk=xxx` and then forking a process from the extension host + // results in the forked process inheriting `--inspect-brk=xxx`. + freshExecArgv: true, + env: { + AMD_ENTRYPOINT: 'vs/workbench/services/search/node/searchApp', + PIPE_LOGGING: 'true', + VERBOSE_LOGGING: verboseLogging + } + } + ); + + const channel = getNextTickChannel(client.getChannel('search')); + this.raw = new SearchChannelClient(channel); + } + + public search(query: ISearchQuery): PPromise { + let request: PPromise; + + let rawSearch: IRawSearch = { + folderQueries: query.folderQueries ? query.folderQueries.map(q => { + return { + excludePattern: q.excludePattern, + includePattern: q.includePattern, + fileEncoding: q.fileEncoding, + folder: q.folder.fsPath + }; + }) : [], + extraFiles: query.extraFileResources ? query.extraFileResources.map(r => r.fsPath) : [], + filePattern: query.filePattern, + excludePattern: query.excludePattern, + includePattern: query.includePattern, + maxResults: query.maxResults, + sortByScore: query.sortByScore, + cacheKey: query.cacheKey, + useRipgrep: query.useRipgrep, + disregardIgnoreFiles: query.disregardIgnoreFiles + }; + + if (query.type === QueryType.Text) { + rawSearch.contentPattern = query.contentPattern; + } + + if (query.type === QueryType.File) { + request = this.raw.fileSearch(rawSearch); + } else { + request = this.raw.textSearch(rawSearch); + } + + return DiskSearch.collectResults(request); + } + + public static collectResults(request: PPromise): PPromise { + let result: IFileMatch[] = []; + return new PPromise((c, e, p) => { + request.done((complete) => { + c({ + limitHit: complete.limitHit, + results: result, + stats: complete.stats + }); + }, e, (data) => { + + // Matches + if (Array.isArray(data)) { + const fileMatches = data.map(d => this.createFileMatch(d)); + result = result.concat(fileMatches); + fileMatches.forEach(p); + } + + // Match + else if ((data).path) { + const fileMatch = this.createFileMatch(data); + result.push(fileMatch); + p(fileMatch); + } + + // Progress + else { + p(data); + } + }); + }, () => request.cancel()); + } + + private static createFileMatch(data: ISerializedFileMatch): FileMatch { + let fileMatch = new FileMatch(uri.file(data.path)); + if (data.lineMatches) { + for (let j = 0; j < data.lineMatches.length; j++) { + fileMatch.lineMatches.push(new LineMatch(data.lineMatches[j].preview, data.lineMatches[j].lineNumber, data.lineMatches[j].offsetAndLengths)); + } + } + return fileMatch; + } + + public clearCache(cacheKey: string): TPromise { + return this.raw.clearCache(cacheKey); + } +} diff --git a/src/vs/workbench/services/search/node/textSearch.ts b/src/vs/workbench/services/search/node/textSearch.ts new file mode 100644 index 0000000000..2b415b5ead --- /dev/null +++ b/src/vs/workbench/services/search/node/textSearch.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as path from 'path'; + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IProgress } from 'vs/platform/search/common/search'; +import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; + +import { ISerializedFileMatch, ISerializedSearchComplete, IRawSearch, ISearchEngine } from './search'; +import { ISearchWorker } from './worker/searchWorkerIpc'; +import { ITextSearchWorkerProvider } from './textSearchWorkerProvider'; + +export class Engine implements ISearchEngine { + + private static PROGRESS_FLUSH_CHUNK_SIZE = 50; // optimization: number of files to process before emitting progress event + + private config: IRawSearch; + private walker: FileWalker; + private walkerError: Error; + + private isCanceled = false; + private isDone = false; + private totalBytes = 0; + private processedBytes = 0; + private progressed = 0; + private walkerIsDone = false; + private limitReached = false; + private numResults = 0; + + private workerProvider: ITextSearchWorkerProvider; + private workers: ISearchWorker[]; + + private nextWorker = 0; + + constructor(config: IRawSearch, walker: FileWalker, workerProvider: ITextSearchWorkerProvider) { + this.config = config; + this.walker = walker; + this.workerProvider = workerProvider; + } + + cancel(): void { + this.isCanceled = true; + this.walker.cancel(); + + this.workers.forEach(w => { + w.cancel() + .then(null, onUnexpectedError); + }); + } + + initializeWorkers(): void { + this.workers.forEach(w => { + w.initialize() + .then(null, onUnexpectedError); + }); + } + + search(onResult: (match: ISerializedFileMatch[]) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchComplete) => void): void { + this.workers = this.workerProvider.getWorkers(); + this.initializeWorkers(); + + const fileEncoding = this.config.folderQueries.length === 1 ? + this.config.folderQueries[0].fileEncoding || 'utf8' : + 'utf8'; + + const progress = () => { + if (++this.progressed % Engine.PROGRESS_FLUSH_CHUNK_SIZE === 0) { + onProgress({ total: this.totalBytes, worked: this.processedBytes }); // buffer progress in chunks to reduce pressure + } + }; + + const unwind = (processed: number) => { + this.processedBytes += processed; + + // Emit progress() unless we got canceled or hit the limit + if (processed && !this.isDone && !this.isCanceled && !this.limitReached) { + progress(); + } + + // Emit done() + if (!this.isDone && this.processedBytes === this.totalBytes && this.walkerIsDone) { + this.isDone = true; + done(this.walkerError, { + limitHit: this.limitReached, + stats: this.walker.getStats() + }); + } + }; + + const run = (batch: string[], batchBytes: number): void => { + const worker = this.workers[this.nextWorker]; + this.nextWorker = (this.nextWorker + 1) % this.workers.length; + + const maxResults = this.config.maxResults && (this.config.maxResults - this.numResults); + const searchArgs = { absolutePaths: batch, maxResults, pattern: this.config.contentPattern, fileEncoding }; + worker.search(searchArgs).then(result => { + if (!result || this.limitReached || this.isCanceled) { + return unwind(batchBytes); + } + + const matches = result.matches; + onResult(matches); + this.numResults += result.numMatches; + + if (this.config.maxResults && this.numResults >= this.config.maxResults) { + // It's possible to go over maxResults like this, but it's much simpler than trying to extract the exact number + // of file matches, line matches, and matches within a line to == maxResults. + this.limitReached = true; + } + + unwind(batchBytes); + }, + error => { + // An error on the worker's end, not in reading the file, but in processing the batch. Log and continue. + onUnexpectedError(error); + unwind(batchBytes); + }); + }; + + // Walk over the file system + let nextBatch: string[] = []; + let nextBatchBytes = 0; + const batchFlushBytes = 2 ** 20; // 1MB + this.walker.walk(this.config.folderQueries, this.config.extraFiles, result => { + let bytes = result.size || 1; + this.totalBytes += bytes; + + // If we have reached the limit or we are canceled, ignore it + if (this.limitReached || this.isCanceled) { + return unwind(bytes); + } + + // Indicate progress to the outside + progress(); + + const absolutePath = result.base ? [result.base, result.relativePath].join(path.sep) : result.relativePath; + nextBatch.push(absolutePath); + nextBatchBytes += bytes; + + if (nextBatchBytes >= batchFlushBytes) { + run(nextBatch, nextBatchBytes); + nextBatch = []; + nextBatchBytes = 0; + } + }, (error, isLimitHit) => { + this.walkerIsDone = true; + this.walkerError = error; + + // Send any remaining paths to a worker, or unwind if we're stopping + if (nextBatch.length) { + if (this.limitReached || this.isCanceled) { + unwind(nextBatchBytes); + } else { + run(nextBatch, nextBatchBytes); + } + } else { + unwind(0); + } + }); + } +} diff --git a/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts b/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts new file mode 100644 index 0000000000..497b7916b0 --- /dev/null +++ b/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as os from 'os'; + +import uri from 'vs/base/common/uri'; +import * as ipc from 'vs/base/parts/ipc/common/ipc'; +import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; + +import { ISearchWorker, ISearchWorkerChannel, SearchWorkerChannelClient } from './worker/searchWorkerIpc'; + +export interface ITextSearchWorkerProvider { + getWorkers(): ISearchWorker[]; +} + +export class TextSearchWorkerProvider implements ITextSearchWorkerProvider { + private workers: ISearchWorker[] = []; + + getWorkers(): ISearchWorker[] { + const numWorkers = os.cpus().length; + while (this.workers.length < numWorkers) { + this.createWorker(); + } + + return this.workers; + } + + private createWorker(): void { + let client = new Client( + uri.parse(require.toUrl('bootstrap')).fsPath, + { + serverName: 'Search Worker ' + this.workers.length, + args: ['--type=searchWorker'], + timeout: 30 * 1000, + env: { + AMD_ENTRYPOINT: 'vs/workbench/services/search/node/worker/searchWorkerApp', + PIPE_LOGGING: 'true', + VERBOSE_LOGGING: process.env.VERBOSE_LOGGING + }, + useQueue: true + }); + + const channel = ipc.getNextTickChannel(client.getChannel('searchWorker')); + const channelClient = new SearchWorkerChannelClient(channel); + + this.workers.push(channelClient); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/worker/searchWorker.ts b/src/vs/workbench/services/search/node/worker/searchWorker.ts new file mode 100644 index 0000000000..001e672da9 --- /dev/null +++ b/src/vs/workbench/services/search/node/worker/searchWorker.ts @@ -0,0 +1,369 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as fs from 'fs'; +import gracefulFs = require('graceful-fs'); +gracefulFs.gracefulify(fs); + +import { onUnexpectedError } from 'vs/base/common/errors'; +import * as strings from 'vs/base/common/strings'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ISerializedFileMatch } from '../search'; +import * as baseMime from 'vs/base/common/mime'; +import { ILineMatch } from 'vs/platform/search/common/search'; +import { UTF16le, UTF16be, UTF8, UTF8_with_bom, encodingExists, decode, bomLength } from 'vs/base/node/encoding'; +import { detectMimeAndEncodingFromBuffer } from 'vs/base/node/mime'; + +import { ISearchWorker, ISearchWorkerSearchArgs, ISearchWorkerSearchResult } from './searchWorkerIpc'; + +interface ReadLinesOptions { + bufferLength: number; + encoding: string; +} + +const MAX_FILE_ERRORS = 5; // Don't report more than this number of errors, 1 per file, to avoid flooding the log when there's a general issue +let numErrorsLogged = 0; +function onError(error: any): void { + if (numErrorsLogged++ < MAX_FILE_ERRORS) { + onUnexpectedError(error); + } +} + +export class SearchWorker implements ISearchWorker { + private currentSearchEngine: SearchWorkerEngine; + + initialize(): TPromise { + this.currentSearchEngine = new SearchWorkerEngine(); + return TPromise.wrap(undefined); + } + + cancel(): TPromise { + // Cancel the current search. It will stop searching and close its open files. + if (this.currentSearchEngine) { + this.currentSearchEngine.cancel(); + } + + return TPromise.wrap(null); + } + + search(args: ISearchWorkerSearchArgs): TPromise { + if (!this.currentSearchEngine) { + // Worker timed out during search + this.initialize(); + } + + return this.currentSearchEngine.searchBatch(args); + } +} + +interface IFileSearchResult { + match: FileMatch; + numMatches: number; + limitReached?: boolean; +} + +const LF = 0x0a; +const CR = 0x0d; + +export class SearchWorkerEngine { + private nextSearch = TPromise.wrap(null); + private isCanceled = false; + + /** + * Searches some number of the given paths concurrently, and starts searches in other paths when those complete. + */ + searchBatch(args: ISearchWorkerSearchArgs): TPromise { + const contentPattern = strings.createRegExp(args.pattern.pattern, args.pattern.isRegExp, { matchCase: args.pattern.isCaseSensitive, wholeWord: args.pattern.isWordMatch, multiline: false, global: true }); + const fileEncoding = encodingExists(args.fileEncoding) ? args.fileEncoding : UTF8; + return this.nextSearch = + this.nextSearch.then(() => this._searchBatch(args, contentPattern, fileEncoding)); + } + + + private _searchBatch(args: ISearchWorkerSearchArgs, contentPattern: RegExp, fileEncoding: string): TPromise { + if (this.isCanceled) { + return TPromise.wrap(null); + } + + return new TPromise(batchDone => { + const result: ISearchWorkerSearchResult = { + matches: [], + numMatches: 0, + limitReached: false + }; + + // Search in the given path, and when it's finished, search in the next path in absolutePaths + const startSearchInFile = (absolutePath: string): TPromise => { + return this.searchInFile(absolutePath, contentPattern, fileEncoding, args.maxResults && (args.maxResults - result.numMatches)).then(fileResult => { + // Finish early if search is canceled + if (this.isCanceled) { + return; + } + + if (fileResult) { + result.numMatches += fileResult.numMatches; + result.matches.push(fileResult.match.serialize()); + if (fileResult.limitReached) { + // If the limit was reached, terminate early with the results so far and cancel in-progress searches. + this.cancel(); + result.limitReached = true; + return batchDone(result); + } + } + }, onError); + }; + + TPromise.join(args.absolutePaths.map(startSearchInFile)).then(() => { + batchDone(result); + }); + }); + } + + cancel(): void { + this.isCanceled = true; + } + + private searchInFile(absolutePath: string, contentPattern: RegExp, fileEncoding: string, maxResults?: number): TPromise { + let fileMatch: FileMatch = null; + let limitReached = false; + let numMatches = 0; + + const perLineCallback = (line: string, lineNumber: number) => { + let lineMatch: LineMatch = null; + let match = contentPattern.exec(line); + + // Record all matches into file result + while (match !== null && match[0].length > 0 && !this.isCanceled && !limitReached) { + if (fileMatch === null) { + fileMatch = new FileMatch(absolutePath); + } + + if (lineMatch === null) { + lineMatch = new LineMatch(line, lineNumber); + fileMatch.addMatch(lineMatch); + } + + lineMatch.addMatch(match.index, match[0].length); + + numMatches++; + if (maxResults && numMatches >= maxResults) { + limitReached = true; + } + + match = contentPattern.exec(line); + } + }; + + // Read lines buffered to support large files + return this.readlinesAsync(absolutePath, perLineCallback, { bufferLength: 8096, encoding: fileEncoding }).then( + () => fileMatch ? { match: fileMatch, limitReached, numMatches } : null); + } + + private readlinesAsync(filename: string, perLineCallback: (line: string, lineNumber: number) => void, options: ReadLinesOptions): TPromise { + return new TPromise((resolve, reject) => { + fs.open(filename, 'r', null, (error: Error, fd: number) => { + if (error) { + return resolve(null); + } + + const buffer = new Buffer(options.bufferLength); + let line = ''; + let lineNumber = 0; + let lastBufferHadTrailingCR = false; + + const readFile = (isFirstRead: boolean, clb: (error: Error) => void): void => { + if (this.isCanceled) { + return clb(null); // return early if canceled or limit reached + } + + fs.read(fd, buffer, 0, buffer.length, null, (error: Error, bytesRead: number, buffer: NodeBuffer) => { + const decodeBuffer = (buffer: NodeBuffer, start: number, end: number): string => { + if (options.encoding === UTF8 || options.encoding === UTF8_with_bom) { + return buffer.toString(undefined, start, end); // much faster to use built in toString() when encoding is default + } + + return decode(buffer.slice(start, end), options.encoding); + }; + + const lineFinished = (offset: number): void => { + line += decodeBuffer(buffer, pos, i + offset); + perLineCallback(line, lineNumber); + line = ''; + lineNumber++; + pos = i + offset; + }; + + if (error || bytesRead === 0 || this.isCanceled) { + return clb(error); // return early if canceled or limit reached or no more bytes to read + } + + let crlfCharSize = 1; + let crBytes = [CR]; + let lfBytes = [LF]; + let pos = 0; + let i = 0; + + // Detect encoding and mime when this is the beginning of the file + if (isFirstRead) { + const mimeAndEncoding = detectMimeAndEncodingFromBuffer({ buffer, bytesRead }, false); + if (mimeAndEncoding.mimes[mimeAndEncoding.mimes.length - 1] !== baseMime.MIME_TEXT) { + return clb(null); // skip files that seem binary + } + + // Check for BOM offset + switch (mimeAndEncoding.encoding) { + case UTF8: + pos = i = bomLength(UTF8); + options.encoding = UTF8; + break; + case UTF16be: + pos = i = bomLength(UTF16be); + options.encoding = UTF16be; + break; + case UTF16le: + pos = i = bomLength(UTF16le); + options.encoding = UTF16le; + break; + } + + // when we are running with UTF16le/be, LF and CR are encoded as + // two bytes, like 0A 00 (LF) / 0D 00 (CR) for LE or flipped around + // for BE. We need to account for this when splitting the buffer into + // newlines, and when detecting a CRLF combo. + if (options.encoding === UTF16le) { + crlfCharSize = 2; + crBytes = [CR, 0x00]; + lfBytes = [LF, 0x00]; + } else if (options.encoding === UTF16be) { + crlfCharSize = 2; + crBytes = [0x00, CR]; + lfBytes = [0x00, LF]; + } + } + + if (lastBufferHadTrailingCR) { + if (buffer[i] === lfBytes[0] && (lfBytes.length === 1 || buffer[i + 1] === lfBytes[1])) { + lineFinished(1 * crlfCharSize); + i++; + } else { + lineFinished(0); + } + + lastBufferHadTrailingCR = false; + } + + /** + * This loop executes for every byte of every file in the workspace - it is highly performance-sensitive! + * Hence the duplication in reading the buffer to avoid a function call. Previously a function call was not + * being inlined by V8. + */ + for (; i < bytesRead; ++i) { + if (buffer[i] === lfBytes[0] && (lfBytes.length === 1 || buffer[i + 1] === lfBytes[1])) { + lineFinished(1 * crlfCharSize); + } else if (buffer[i] === crBytes[0] && (crBytes.length === 1 || buffer[i + 1] === crBytes[1])) { // CR (Carriage Return) + if (i + crlfCharSize === bytesRead) { + lastBufferHadTrailingCR = true; + } else if (buffer[i + crlfCharSize] === lfBytes[0] && (lfBytes.length === 1 || buffer[i + crlfCharSize + 1] === lfBytes[1])) { + lineFinished(2 * crlfCharSize); + i += 2 * crlfCharSize - 1; + } else { + lineFinished(1 * crlfCharSize); + } + } + } + + line += decodeBuffer(buffer, pos, bytesRead); + + readFile(/*isFirstRead=*/false, clb); // Continue reading + }); + }; + + readFile(/*isFirstRead=*/true, (error: Error) => { + if (error) { + return resolve(null); + } + + if (line.length) { + perLineCallback(line, lineNumber); // handle last line + } + + fs.close(fd, (error: Error) => { + resolve(null); + }); + }); + }); + }); + } +} + +export class FileMatch implements ISerializedFileMatch { + path: string; + lineMatches: LineMatch[]; + + constructor(path: string) { + this.path = path; + this.lineMatches = []; + } + + addMatch(lineMatch: LineMatch): void { + this.lineMatches.push(lineMatch); + } + + isEmpty(): boolean { + return this.lineMatches.length === 0; + } + + serialize(): ISerializedFileMatch { + let lineMatches: ILineMatch[] = []; + let numMatches = 0; + + for (let i = 0; i < this.lineMatches.length; i++) { + numMatches += this.lineMatches[i].offsetAndLengths.length; + lineMatches.push(this.lineMatches[i].serialize()); + } + + return { + path: this.path, + lineMatches, + numMatches + }; + } +} + +export class LineMatch implements ILineMatch { + preview: string; + lineNumber: number; + offsetAndLengths: number[][]; + + constructor(preview: string, lineNumber: number) { + this.preview = preview.replace(/(\r|\n)*$/, ''); + this.lineNumber = lineNumber; + this.offsetAndLengths = []; + } + + getText(): string { + return this.preview; + } + + getLineNumber(): number { + return this.lineNumber; + } + + addMatch(offset: number, length: number): void { + this.offsetAndLengths.push([offset, length]); + } + + serialize(): ILineMatch { + const result = { + preview: this.preview, + lineNumber: this.lineNumber, + offsetAndLengths: this.offsetAndLengths + }; + + return result; + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/worker/searchWorkerApp.ts b/src/vs/workbench/services/search/node/worker/searchWorkerApp.ts new file mode 100644 index 0000000000..eba396c51d --- /dev/null +++ b/src/vs/workbench/services/search/node/worker/searchWorkerApp.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; +import { SearchWorkerChannel } from './searchWorkerIpc'; +import { SearchWorker } from './searchWorker'; + +const server = new Server(); +const worker = new SearchWorker(); +const channel = new SearchWorkerChannel(worker); +server.registerChannel('searchWorker', channel); diff --git a/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts b/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts new file mode 100644 index 0000000000..258fe19d3b --- /dev/null +++ b/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { ISerializedFileMatch } from '../search'; +import { IPatternInfo } from 'vs/platform/search/common/search'; +import { SearchWorker } from './searchWorker'; + +export interface ISearchWorkerSearchArgs { + pattern: IPatternInfo; + fileEncoding: string; + absolutePaths: string[]; + maxResults?: number; +} + +export interface ISearchWorkerSearchResult { + matches: ISerializedFileMatch[]; + numMatches: number; + limitReached: boolean; +} + +export interface ISearchWorker { + initialize(): TPromise; + search(args: ISearchWorkerSearchArgs): TPromise; + cancel(): TPromise; +} + +export interface ISearchWorkerChannel extends IChannel { + call(command: 'initialize'): TPromise; + call(command: 'search', args: ISearchWorkerSearchArgs): TPromise; + call(command: 'cancel'): TPromise; + call(command: string, arg?: any): TPromise; +} + +export class SearchWorkerChannel implements ISearchWorkerChannel { + constructor(private worker: SearchWorker) { + } + + call(command: string, arg?: any): TPromise { + switch (command) { + case 'initialize': return this.worker.initialize(); + case 'search': return this.worker.search(arg); + case 'cancel': return this.worker.cancel(); + } + return undefined; + } +} + +export class SearchWorkerChannelClient implements ISearchWorker { + constructor(private channel: ISearchWorkerChannel) { } + + initialize(): TPromise { + return this.channel.call('initialize'); + } + + search(args: ISearchWorkerSearchArgs): TPromise { + return this.channel.call('search', args); + } + + cancel(): TPromise { + return this.channel.call('cancel'); + } +} diff --git a/src/vs/workbench/services/search/test/node/fixtures/binary.wuff b/src/vs/workbench/services/search/test/node/fixtures/binary.wuff new file mode 100644 index 0000000000000000000000000000000000000000..799b38be88941ec9b67f4b6d0bd4832fcfe1d93a GIT binary patch literal 5486 zcmZWtbyO7GwjVkqq*GEtff*ROQ97ktWayz|NGT}+K}KMx8M+aWQc6O)rMpAmBb1II zo__bfd%yMGS?ip$&)&b^uJg}XCq`RKnV68C5C8xWtEwpI{@&SsPfmQ?-)ru)>LdU_ z*a?-F*H)F6XVLa_vx7R@0sszg4l?Yu^-bub+Pox{oBBs1ik^9Dv8gHX=V158vT5o% zWbnQ(;eMJIwnHoT4L=OynUXfPMp_RMH?1}unIfGc5%<%odRu!`#azYwZOd(@g!l6G zq3Xq=*_n(iN$BdrO;?v+92Se3istzVYzyyg#PO4aH@M&&e4lm{NJABNbaWJUPaA#^ z`q%{-V^ev*_;UqU68uyVKQzumS{AIx-QkTwQPNReCUPd4XTvOKKB6@WPZr|nTsM|cPMr}5{!G_(Ce!!y|ZTBr8<+LRD!cT^R zfHkn(e!YE|_TYIm4%y>Qw!R3eD%@##4Cly4RSf6yIQq|tQK3p=c&V%Oi&4$L09oyV zegtl-(t%Ns2v$NX77|^#G8Kwl7A%|Ca`+=pxyn8Yf7Vx&(RTUnl#$eI0&0qM4o^&D zJV$Zs2wdbP$C&h^NOGUT7^m_lO6Xob6V>MVOD?nI!C8(`33HH~yaP77rG8vwzm-}V zeNp7Dl~S6x-P=ZpV3j2=CIy7fSR^HZ3Q}lHH68l0!?e10md6<$N;Zpa5C;4fyQ=Vu zE#hGw!IKEy=S&d1+%Ro*2hO^OSr08JTFIdb9C^U8j2)#@jwp)mF550CD}p2Z>JNVL zm+)ET8#ODq-(&Xi$B8sg;*#Y|mF89E)yr6glMdloNis=L8>YF4eT8#&<;02p#!V|v zsw>7TU}NeHqN50(TO_$OnO}|ER=69!}5*C~ioGsIyBUne5MEizF zz?#pRIsc7E>e%-JfzRphhb-;Df(a(yob6<+g6;Zihm0qT$I7Y7$ql9VfKH*qRtz=L zvt;{Q`&K^E&6&3rQS8=if|{I$cJV{O>0aO$Hc{eIBiLH}T$!~9FVsYjfA5wjUqkDn zf-6RS#zMy<_||~wz(}GdV(FKI8|Eqq^!FvRUhsDR7x}}+Ne+R`|FCLf*)m^x;*;L zIn$p;^e(ht7_Jls8{8&EeThm?D(ZO_@1q%36#2t^m^kAs19V9kC>l&2LVC{`VmzqA zqt9csS1|qHO99QFF-?@tL1TvZVO3JQ)_r|VsL5jTa3>3ewOZ>#ns&0xZGw)6r^xXkDY@Z zluxDPt<=WgM2APWO()7gnx2MKKqjZvrH^aA)DYD8sQD@lnue3Hg?tHS5U3ZXL8c;o zO-hU}P0H&VECrm!m%;tVRkjI;B7^9u?}nlk(CNeJ!HO3qA$8SNWOMqnQngbx#I<}i zF3cd&W&6P8%a1<#&HX-eKHK|BTw)r1D0jyWcuVFj8(ISWEk=Z5f^%ByMy%gX_qw1- zHJfptky=tnEYJ%E8}gL;sQ^2M4R|*l@^0*Je;vMReb=PkpkAu}>0`B1qEp2l9GP$S zWr4~!r8yhzbD^AslVr`soO$!DbD4c`qlRm6V#TX?d*TVM9(ge;4Jl$X3oZFy&+qbClvzCgr1t3>otB`deciBrHl5(f?2y|s(=zg_WYB3~Vo*k4Y3p5U za}fHb2W^X9%IHQ?Nq(GWzWRQ;vb(}y$pC*eN?t-n%k1|wnx2wUpZb(_R@hI^-6-0m z?--8ErO(Y9Et)HzgVZLBFr{xu+h3sSAwUSSvuS&N=%wJrvQ(#k`iX1QNvB>1Xv8Rs zS@a%;Ey@O7FMvNmaounvl8Z~$NW{`oksHv(E8-~{^;il#6F^i)%))2Oa0+m|s3&;~ z_=sK^)AYW77wZ(4nvjs-Mt!7Enzu(FL7+}C-)*=4d3}ZafTI3!tJbl`Jn~dnoWD^b z@&h@-Qv{Kkku{%TmzaW_PC87qNR+ALw$#y#(oiz*L3B60tEKV1-MrL0dhsd+8&o=| z->1><{L-FG1drw=L;$&z29eQ#n-VT%glxFj_nfmZ;V0Hjc z*`85a&(_4iu)@5hHDJ?tygDlbp25)8aHIQrarLs&bZ#Cs@8+%Q{r$y|_?YMTiH%;1 z!RY#yoa(ruOLqo}!?qp-{8 zKf9yfWu{_+mRhB5kGpwBjI?eYPn^yS@;`3PV9)rxG-_!*h}w_8L7pPT>!a&yT!~jK zJkeh2ri19|g6TUE9kZ3pNZ){;J&QrhOjCE|>@^#6(rYb;=nnLECIy)tf7;t$iuTUi zgUTKW7P8c`On!;So7P53GczkG(uyyPy!Dzzr8cA%#`72Tj?I7Fv_IIO=?!ps&G4$J z_3QBfdTOavJZPy6bQ83Cwl2D(=_*^_wo|csu!gbsrpPzzr)wK&(8|fPo3+7SgK%)T zr0i~R+138#)nD49+83!^Aof=?A$nJaGR}gFQtP6}3l8f$JG6SYNqVEqr{LLZ`4F<3 z(j%X^=Eu3t53~=79>Q@}y{Ro}EPB^6V(Misy+Z8U%ggV|u~EWRvPUX4)KN5;7SUpF z;BPSXQS!86tkoX$YhjGstCM9!QrdW$qnM9Iaj^I?^{N0)w4~>&0N^S~7XKrAN?H5} zZImt>3;ntr(G&QQsMeE6d2O7}qX2skwMLQ(cTy&K($jB}C~YM&x@tJ%IxEHq=MS-T zu{CorMR8&yH$6uVtd6nxLww1@14TR?Vn;a|cg)@P-3^X_gjR@6TzILKL|SRGNI@NI zA@LIz6{x%#gYNvL(LDURZSubG4s;e$dar)_>aTm<`_%udx+lHMCU+6`S-`A*fYFzk zpO1wJaHMJfwKz5g!phI@WCC>Agf?YWH!~)(cmd8!R3^_zMph0@Z?L9$<;{0%F#o)2 z3EWjoyZ``djz1GaRrm2P0009C)i;J2YidYXySV@%Hf~n7KtC7v-)I0p%1`3A=wb_l zu=u$+yLw6ZNwfWpkoYbCQG?i6{)WJuq}hx$wOQodJZ)KofkHrjHW@+|78WT_8#@VI z1*Lz}f1jk;9AGea2@uHF*B9t32z2wb2MLIai-Y*VATXHkH-gX0-xUV&<8$?5|Cf{h z^`l_xW$g)dhe6$3S^oHiSh;z_q}ka11p3eTSDvNzY7HYv48}C{Gk8% z{!Nwoqm|Hx`q?@gD?nXrUA=zCkP#G;`WycL4F3)I52DFGh+_XF{=@KZq7>*)`u`~B zU%mQU`@2~(gi@gYG+u@U_ZI_r+(^-rf!xxE^voU@l|V>ZzVvGgu89 z^O7-5-<(`4Q2yv43mU5d;FXyY8Z{i~cldICtnzI@JOZ(8#m=gTYy4p$^j(z#O^f-} zcGdNEDA`vQcZW%qHTXIO!MEEx?#_lv99oK4CluM2(a!H1Q>LmUUCU4A-UYx= zZ2{(q$F@o~p2RuYeR1-Y8{}|9tcxGIk#Vm*x#$tGh8jsmz{9oWh2z)NBVGE-AG6E! z{TgO5&r}d9ie1*R_O*`GHZd9*FaOL{>~GuaByiV&MBDu zkry2axjJnCGUJ09Y@b$-ZX=qY$V3s%G(9dDmZX-aO!*xQZ3r}14G_)_-QCel2W=Ab z6b@Pjrel72tHPt_j?ImivugjhS?epnyYu|l|9~8^4 z`y}aSiFH1W_jAgcR<^20H{bH<QhG)rv0U0JJ-L4Hl*mcVs>EdFJ zB$_zJZsNg~-=6Gb2{EYfUU!5MN`TL|mW=4Q4OKG>IR-X%=MXiS78`dK_0nhPG9Hmn z4Lqwh9dbRy{;2ah?C3kth05H?+-OBTiYuOOJTOg!tMw^yBniKwf;**C0B2@OauS=U z&!qC#Q2_WUBIF*x6hg^WA6O-8lZ(J0n)=tHave7C|)@HsZ5{&%DeCEQ)_aG^? zU9oCsg8TbbN*GEfFKwH9%ZSBzD8B|^%oyv*&-NC^#c|ViTu}mkD?8!geU6qEUXht1 z7S-_3my=Z^BlP6yEh@@|8Jpw}kj&Lh<(8;m!mlhlc+~wyw=D869JbQ^Kk8HR<|)kL z%?7<(C4pwZSq^&od4oDK8yn)o_C*Q1v4S0xkeRPvCu^5BS1S{wPf;+E1?E?$Bg;M| zEPLTunst{G;NJ4O$h6S|Y4c^rTiLsZ2h!zB)F1MoII;N+7P+L<6-l zX58!kcFQ-7iL$)d7$b?+;#WwnnvutG_)ONQu7i7dQwXPrCQyaoU1O1FvgjUMu z9*?uf3^qa$mOiPEDPq$tEQQ>u+KEa#6BF-zsv&_xKAK)d)DYjSop0KBIfT3GhZ%>R zmk#>sosCG{cI;tIMcyoFl1U;_?;IxwN#uk!1L6c$(Q2Z5h1uAum0h>^R6HKpOu5hI z6sqfgXrGFKScmg5n2RA#2nJdtIwMs4&i7m&|HvaB(9p8pG5&I9!*gPi>-n=HB$>`= z_60sB=SQ%8ursN~tZumnBdtGy`~-q0Zn{A3%UguHKMC(S#&;Ip>F`X#ha$`j=|q>b z7BNEoJCgBYU^0{xvS2gldax4(8CqKG{*kr9^Ht<= zT1P)$jT)UME#&6h2RhBS!m7ehrifrzsUCcQBGNfx5e?*h&4^`eQz3h`)hqvj0QF$G zSMQ|P0$?XybCLF)i5TCq&ev;z@nlRuedZOJ3NJn3m&Q zJD&;q_lR4{o0g%qPHKfmp9f>%U0UIoh`R_)3iGWmzPlaPm3>C7lgn(>S+<6oC8$00 z3u;}%@3P>XJw=+ha?2bkLp?e|1`Hc^aIQB%>PGbWfKdPNEOL0_%8#h4*?DSBoQ(XD zTw?uobMOs?M6)s)@7@SPCzl)abPLzScoW0vQ@Ht-0ft+FFj0ZWhqNQ2gS!mU?cY~^ z*i$`0`ck6z`Q{j`|0+QD{m|_RO?b|u&7S_|e$|5$8FHmxK6JPi5##LKy_D}VlXUCW z8DHrB`1~y6Y^K20h_iZvX%V*smE7EZ+Z2>zw#a$y%w$oOF_E}%_Dy~C0LMLL9VklzvBL= zZn&!H2nV)+>sO(j`dzuOPChy8P0UVCGw%CawM2Hz!n^BK=&Z&rU{Y$5DL^bYrUB-T zgWsoHOTw0XqfO1d^GmwRND~cA4IK^=bzC9+ zl?9Y*SYt4LCLYkISa>S)FFJivW_9jjpj__5@zr~SFV_u;lH`dJ8J#En(V#pGE!N7r d;(jN5#w^E>HPd|20sZrYR8`bcsC;f2_CIZ(M6dt= literal 0 HcmV?d00001 diff --git a/src/vs/workbench/services/search/test/node/fixtures/examples/NullPoinderException.js b/src/vs/workbench/services/search/test/node/fixtures/examples/NullPoinderException.js new file mode 100644 index 0000000000..b4905089c2 --- /dev/null +++ b/src/vs/workbench/services/search/test/node/fixtures/examples/NullPoinderException.js @@ -0,0 +1,118 @@ +// CamelCase +'use strict'; +var Conway; +(function (Conway) { + var Cell = (function () { + function Cell() { + } + return Cell; + })(); + (function (property, number, property, number, property, boolean) { + if (property === void 0) { property = row; } + if (property === void 0) { property = col; } + if (property === void 0) { property = live; } + }); + var GameOfLife = (function () { + function GameOfLife() { + } + return GameOfLife; + })(); + (function () { + property; + gridSize = 50; + property; + canvasSize = 600; + property; + lineColor = '#cdcdcd'; + property; + liveColor = '#666'; + property; + deadColor = '#eee'; + property; + initialLifeProbability = 0.5; + property; + animationRate = 60; + property; + cellSize = 0; + property; + context: ICanvasRenderingContext2D; + property; + world = createWorld(); + circleOfLife(); + function createWorld() { + return travelWorld(function (cell) { + cell.live = Math.random() < initialLifeProbability; + return cell; + }); + } + function circleOfLife() { + world = travelWorld(function (cell) { + cell = world[cell.row][cell.col]; + draw(cell); + return resolveNextGeneration(cell); + }); + setTimeout(function () { circleOfLife(); }, animationRate); + } + function resolveNextGeneration(cell) { + var count = countNeighbors(cell); + var newCell = new Cell(cell.row, cell.col, cell.live); + if (count < 2 || count > 3) + newCell.live = false; + else if (count == 3) + newCell.live = true; + return newCell; + } + function countNeighbors(cell) { + var neighbors = 0; + for (var row = -1; row <= 1; row++) { + for (var col = -1; col <= 1; col++) { + if (row == 0 && col == 0) + continue; + if (isAlive(cell.row + row, cell.col + col)) { + neighbors++; + } + } + } + return neighbors; + } + function isAlive(row, col) { + // todo - need to guard with worl[row] exists? + if (row < 0 || col < 0 || row >= gridSize || col >= gridSize) + return false; + return world[row][col].live; + } + function travelWorld(callback) { + var result = []; + for (var row = 0; row < gridSize; row++) { + var rowData = []; + for (var col = 0; col < gridSize; col++) { + rowData.push(callback(new Cell(row, col, false))); + } + result.push(rowData); + } + return result; + } + function draw(cell) { + if (context == null) + context = createDrawingContext(); + if (cellSize == 0) + cellSize = canvasSize / gridSize; + context.strokeStyle = lineColor; + context.strokeRect(cell.row * cellSize, cell.col * cellSize, cellSize, cellSize); + context.fillStyle = cell.live ? liveColor : deadColor; + context.fillRect(cell.row * cellSize, cell.col * cellSize, cellSize, cellSize); + } + function createDrawingContext() { + var canvas = document.getElementById('conway-canvas'); + if (canvas == null) { + canvas = document.createElement('canvas'); + canvas.id = "conway-canvas"; + canvas.width = canvasSize; + canvas.height = canvasSize; + document.body.appendChild(canvas); + } + return canvas.getContext('2d'); + } + }); +})(Conway || (Conway = {})); +var game = new Conway.GameOfLife(); diff --git a/src/vs/workbench/services/search/test/node/fixtures/examples/company.js b/src/vs/workbench/services/search/test/node/fixtures/examples/company.js new file mode 100644 index 0000000000..ca4a62bf24 --- /dev/null +++ b/src/vs/workbench/services/search/test/node/fixtures/examples/company.js @@ -0,0 +1,23 @@ +'use strict'; +/// +var Workforce; +(function (Workforce_1) { + var Company = (function () { + function Company() { + } + return Company; + })(); + (function (property, Workforce, IEmployee) { + if (property === void 0) { property = employees; } + if (IEmployee === void 0) { IEmployee = []; } + property; + calculateMonthlyExpenses(); + { + var result = 0; + for (var i = 0; i < employees.length; i++) { + result += employees[i].calculatePay(); + } + return result; + } + }); +})(Workforce || (Workforce = {})); diff --git a/src/vs/workbench/services/search/test/node/fixtures/examples/employee.js b/src/vs/workbench/services/search/test/node/fixtures/examples/employee.js new file mode 100644 index 0000000000..9dea3e1adf --- /dev/null +++ b/src/vs/workbench/services/search/test/node/fixtures/examples/employee.js @@ -0,0 +1,11190 @@ +'use strict'; +var Workforce; +(function (Workforce) { + var Employee = (function () { + function Employee() { + } + return Employee; + })(); + (property); + name: string, property; + basepay: number; + implements; + IEmployee; + { + name; + basepay; + } + var SalesEmployee = (function () { + function SalesEmployee() { + } + return SalesEmployee; + })(); + (); + Employee(name, basepay); + { + function calculatePay() { + var multiplier = (document.getElementById("mult")), as = any, value; + return _super.calculatePay.call(this) * multiplier + bonus; + } + } + var employee = new Employee('Bob', 1000); + var salesEmployee = new SalesEmployee('Jim', 800, 400); + salesEmployee.calclatePay(); // error: No member 'calclatePay' on SalesEmployee +})(Workforce || (Workforce = {})); +extern; +var $; +var s = Workforce.salesEmployee.calculatePay(); +$('#results').text(s); +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; +a; diff --git a/src/vs/workbench/services/search/test/node/fixtures/examples/small.js b/src/vs/workbench/services/search/test/node/fixtures/examples/small.js new file mode 100644 index 0000000000..5e57b4c943 --- /dev/null +++ b/src/vs/workbench/services/search/test/node/fixtures/examples/small.js @@ -0,0 +1,24 @@ +'use strict'; +var M; +(function (M) { + var C = (function () { + function C() { + } + return C; + })(); + (function (x, property, number) { + if (property === void 0) { property = w; } + var local = 1; + // unresolved symbol because x is local + //self.x++; + self.w--; // ok because w is a property + property; + f = function (y) { + return y + x + local + w + self.w; + }; + function sum(z) { + return z + f(z) + w + self.w; + } + }); +})(M || (M = {})); +var c = new M.C(12, 5); diff --git a/src/vs/workbench/services/search/test/node/fixtures/examples/subfolder/anotherfolder/anotherfile.txt b/src/vs/workbench/services/search/test/node/fixtures/examples/subfolder/anotherfolder/anotherfile.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/vs/workbench/services/search/test/node/fixtures/examples/subfolder/subfile.txt b/src/vs/workbench/services/search/test/node/fixtures/examples/subfolder/subfile.txt new file mode 100644 index 0000000000..b094313e6d --- /dev/null +++ b/src/vs/workbench/services/search/test/node/fixtures/examples/subfolder/subfile.txt @@ -0,0 +1 @@ +// sub \ No newline at end of file diff --git a/src/vs/workbench/services/search/test/node/fixtures/index.html b/src/vs/workbench/services/search/test/node/fixtures/index.html new file mode 100644 index 0000000000..bccd24d927 --- /dev/null +++ b/src/vs/workbench/services/search/test/node/fixtures/index.html @@ -0,0 +1,121 @@ + + + + + Strada + + + + + + + + +

TypeScript

+
+ + +
+ + +
+ +
Press 'run' to execute code...
+
...write your results into #results...
+
+ + + diff --git a/src/vs/workbench/services/search/test/node/fixtures/more/file.txt b/src/vs/workbench/services/search/test/node/fixtures/more/file.txt new file mode 100644 index 0000000000..c4d0bf153e --- /dev/null +++ b/src/vs/workbench/services/search/test/node/fixtures/more/file.txt @@ -0,0 +1 @@ +Conway \ No newline at end of file diff --git a/src/vs/workbench/services/search/test/node/fixtures/site.css b/src/vs/workbench/services/search/test/node/fixtures/site.css new file mode 100644 index 0000000000..cbd1fe228a --- /dev/null +++ b/src/vs/workbench/services/search/test/node/fixtures/site.css @@ -0,0 +1,35 @@ +/*---------------------------------------------------------- +The base color for this template is #5c87b2. If you'd like +to use a different color start by replacing all instances of +#5c87b2 with your new color. +----------------------------------------------------------*/ +body +{ + background-color: #5c87b2; + font-size: .75em; + font-family: Segoe UI, Verdana, Helvetica, Sans-Serif; + margin: 8px; + padding: 0; + color: #696969; +} + +h1, h2, h3, h4, h5, h6 +{ + color: #000; + font-size: 40px; + margin: 0px; +} + +textarea +{ + font-family: Consolas +} + +#results +{ + margin-top: 2em; + margin-left: 2em; + color: black; + font-size: medium; +} + diff --git a/src/vs/workbench/services/search/test/node/fixtures/site.less b/src/vs/workbench/services/search/test/node/fixtures/site.less new file mode 100644 index 0000000000..140601bfb0 --- /dev/null +++ b/src/vs/workbench/services/search/test/node/fixtures/site.less @@ -0,0 +1,2 @@ +// lss is mor + diff --git a/src/vs/workbench/services/search/test/node/fixtures/some_utf16be.css b/src/vs/workbench/services/search/test/node/fixtures/some_utf16be.css new file mode 100644 index 0000000000000000000000000000000000000000..30c3b9d38ba3e73ef884db9b587be76fe28d751f GIT binary patch literal 1408 zcmc(fOHaa35QWd$#Q$&;7A8he5qvJ)=o)ojN(;ue7>e)s*VS)sE2Rb2;66Z?Sgzz{897G~%3qeZt=2&e1z=ikJcC5w|fbB66e*gu`RSB}Wr*XZj(2oyQeS zLTodQ(FH_FkoI0!WO#`@_bJ{jcAcx!8)(b$pRHw{Ve^Kp7<9btHu|*Bgc$E3)fkbv zoxl9h!2jayN?(-RyHtwFw;)H^m}> zm01ixkMU&_b;59dB61}p|u?!6#p5MMUR^^F~J>v;8ZMjTzQp#Q_`=vZx)0JkV zBe*o#tgn0z+-h#k{FgBmXZu3msjSs1Vs|Uds+i6GUHdu6+3owzy=CRO~*^GRHQkDS23(yKrgFQ5~Crh_Tp&u2*yD8QRQmn$6`&PVsFs@v`8s$m$P6X}+TX literal 0 HcmV?d00001 diff --git a/src/vs/workbench/services/search/test/node/fixtures/some_utf16le.css b/src/vs/workbench/services/search/test/node/fixtures/some_utf16le.css new file mode 100644 index 0000000000000000000000000000000000000000..aea04aa2cd17f11ccadedd0f133fcdbc3e797790 GIT binary patch literal 1408 zcmc(fNl(K-429nriT^NC4@ihEEG^rS3!GW*X_~YZNs5}5vWUM9e4Yf-r1XqN8BaXV z_Vbc_eRj34_CKs$?dVPuMRFP{Q0`BmW=eSGXyZ~YKux)KT7XIQ1S0TfSL$2pE7X}z z@pPmP$Q!JkUh%Nho<^J#u#ebV+y#2aO%XHTJmNNHMMRF2fpB=Nxa4R8?o2<#uk*Nq zNr-KxF}i>#3DVvRiwrN3=RU)`#jbO8T7k9iXD2sxcyS z%i-Pc4u-a@dm*(QQxmIq2Cluv{Q^uq>&u$G_1P-krcP76k-J%R=w_dDs%_|)xG5G1 ztjyvT^cY_@QTI&10rG*jIeY>l1YUEECebtd0^6|E-g-n12lF!LCT_${LCo=Qy|rj( z%;8G|^8{wZD(1mZy#}J{sNJ?S(E0CE { + const rootFolder = '/workspace'; + const fileSectionEnd = '\n'; + + function getFileLine(relativePath: string): string { + return `\u001b\[m${relativePath}\u001b\[m`; + } + + function getMatchLine(lineNum: number, matchParts: string[]): string { + let matchLine = `\u001b\[m${lineNum}\u001b\[m:` + + `${matchParts.shift()}${RipgrepParser.MATCH_START_MARKER}${matchParts.shift()}${RipgrepParser.MATCH_END_MARKER}${matchParts.shift()}`; + + while (matchParts.length) { + matchLine += `${RipgrepParser.MATCH_START_MARKER}${matchParts.shift()}${RipgrepParser.MATCH_END_MARKER}${matchParts.shift() || ''}`; + } + + return matchLine; + } + + function parseInputStrings(inputChunks: string[]): ISerializedFileMatch[] { + return parseInput(inputChunks.map(chunk => new Buffer(chunk))); + } + + function parseInput(inputChunks: Buffer[]): ISerializedFileMatch[] { + const matches: ISerializedFileMatch[] = []; + const rgp = new RipgrepParser(1e6, rootFolder); + rgp.on('result', (match: ISerializedFileMatch) => { + matches.push(match); + }); + + inputChunks.forEach(chunk => rgp.handleData(chunk)); + rgp.flush(); + + return matches; + } + + function halve(str: string) { + const halfIdx = Math.floor(str.length / 2); + return [str.substr(0, halfIdx), str.substr(halfIdx)]; + } + + function arrayOfChars(str: string) { + const chars = []; + for (let char of str) { + chars.push(char); + } + + return chars; + } + + test('Parses one chunk', () => { + const input = [ + [getFileLine('a.txt'), getMatchLine(1, ['before', 'match', 'after']), getMatchLine(2, ['before', 'match', 'after']), fileSectionEnd].join('\n') + ]; + + const results = parseInputStrings(input); + assert.equal(results.length, 1); + assert.deepEqual(results[0], + { + numMatches: 2, + path: path.join(rootFolder, 'a.txt'), + lineMatches: [ + { + lineNumber: 0, + preview: 'beforematchafter', + offsetAndLengths: [[6, 5]] + }, + { + lineNumber: 1, + preview: 'beforematchafter', + offsetAndLengths: [[6, 5]] + } + ] + }); + }); + + test('Parses multiple chunks broken at file sections', () => { + const input = [ + [getFileLine('a.txt'), getMatchLine(1, ['before', 'match', 'after']), getMatchLine(2, ['before', 'match', 'after']), fileSectionEnd].join('\n'), + [getFileLine('b.txt'), getMatchLine(1, ['before', 'match', 'after']), getMatchLine(2, ['before', 'match', 'after']), fileSectionEnd].join('\n'), + [getFileLine('c.txt'), getMatchLine(1, ['before', 'match', 'after']), getMatchLine(2, ['before', 'match', 'after']), fileSectionEnd].join('\n') + ]; + + const results = parseInputStrings(input); + assert.equal(results.length, 3); + results.forEach(fileResult => assert.equal(fileResult.numMatches, 2)); + }); + + const singleLineChunks = [ + getFileLine('a.txt'), + getMatchLine(1, ['before', 'match', 'after']), + getMatchLine(2, ['before', 'match', 'after']), + fileSectionEnd, + getFileLine('b.txt'), + getMatchLine(1, ['before', 'match', 'after']), + getMatchLine(2, ['before', 'match', 'after']), + fileSectionEnd, + getFileLine('c.txt'), + getMatchLine(1, ['before', 'match', 'after']), + getMatchLine(2, ['before', 'match', 'after']), + fileSectionEnd + ]; + + test('Parses multiple chunks broken at each line', () => { + const input = singleLineChunks.map(chunk => chunk + '\n'); + + const results = parseInputStrings(input); + assert.equal(results.length, 3); + results.forEach(fileResult => assert.equal(fileResult.numMatches, 2)); + }); + + test('Parses multiple chunks broken in the middle of each line', () => { + const input = arrays.flatten(singleLineChunks + .map(chunk => chunk + '\n') + .map(halve)); + + const results = parseInputStrings(input); + assert.equal(results.length, 3); + results.forEach(fileResult => assert.equal(fileResult.numMatches, 2)); + }); + + test('Parses multiple chunks broken at each character', () => { + const input = arrays.flatten(singleLineChunks + .map(chunk => chunk + '\n') + .map(arrayOfChars)); + + const results = parseInputStrings(input); + assert.equal(results.length, 3); + results.forEach(fileResult => assert.equal(fileResult.numMatches, 2)); + }); + + test('Parses chunks broken before newline', () => { + const input = singleLineChunks + .map(chunk => '\n' + chunk); + + const results = parseInputStrings(input); + assert.equal(results.length, 3); + results.forEach(fileResult => assert.equal(fileResult.numMatches, 2)); + }); + + test('Parses chunks broken in the middle of a multibyte character', () => { + const multibyteStr = '漢'; + const multibyteBuf = new Buffer(multibyteStr); + const text = getFileLine('foo/bar') + '\n' + getMatchLine(0, ['before', 'match', 'after']) + '\n'; + + // Split the multibyte char into two pieces and divide between the two buffers + const beforeIndex = 24; + const inputBufs = [ + Buffer.concat([new Buffer(text.substr(0, beforeIndex)), multibyteBuf.slice(0, 2)]), + Buffer.concat([multibyteBuf.slice(2), new Buffer(text.substr(beforeIndex))]) + ]; + + const results = parseInput(inputBufs); + assert.equal(results.length, 1); + assert.equal(results[0].lineMatches.length, 1); + assert.deepEqual(results[0].lineMatches[0].offsetAndLengths, [[7, 5]]); + }); +}); + +suite('RipgrepParser - etc', () => { + function testGetAbsGlob(params: string[]): void { + const [folder, glob, expectedResult] = params; + assert.equal(fixDriveC(getAbsoluteGlob(folder, glob)), expectedResult, JSON.stringify(params)); + } + + test('getAbsoluteGlob_win', () => { + if (!platform.isWindows) { + return; + } + + [ + ['C:/foo/bar', 'glob/**', '/foo\\bar\\glob\\**'], + ['c:/', 'glob/**', '/glob\\**'], + ['C:\\foo\\bar', 'glob\\**', '/foo\\bar\\glob\\**'], + ['c:\\foo\\bar', 'glob\\**', '/foo\\bar\\glob\\**'], + ['c:\\', 'glob\\**', '/glob\\**'], + ['\\\\localhost\\c$\\foo\\bar', 'glob/**', '\\\\localhost\\c$\\foo\\bar\\glob\\**'], + + // absolute paths are not resolved further + ['c:/foo/bar', '/path/something', '/path/something'], + ['c:/foo/bar', 'c:\\project\\folder', '/project\\folder'] + ].forEach(testGetAbsGlob); + }); + + test('getAbsoluteGlob_posix', () => { + if (platform.isWindows) { + return; + } + + [ + ['/foo/bar', 'glob/**', '/foo/bar/glob/**'], + ['/', 'glob/**', '/glob/**'], + + // absolute paths are not resolved further + ['/', '/project/folder', '/project/folder'], + ].forEach(testGetAbsGlob); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/search/test/node/search.test.ts b/src/vs/workbench/services/search/test/node/search.test.ts new file mode 100644 index 0000000000..8c38c21c3b --- /dev/null +++ b/src/vs/workbench/services/search/test/node/search.test.ts @@ -0,0 +1,723 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import path = require('path'); +import assert = require('assert'); + +import { join, normalize } from 'vs/base/common/paths'; +import * as platform from 'vs/base/common/platform'; + +import { FileWalker, Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; +import { IRawFileMatch, IFolderSearch } from 'vs/workbench/services/search/node/search'; + +const TEST_FIXTURES = path.normalize(require.toUrl('./fixtures')); +const EXAMPLES_FIXTURES = path.join(TEST_FIXTURES, 'examples'); +const MORE_FIXTURES = path.join(TEST_FIXTURES, 'more'); +const TEST_ROOT_FOLDER: IFolderSearch = { folder: TEST_FIXTURES }; +const ROOT_FOLDER_QUERY: IFolderSearch[] = [ + TEST_ROOT_FOLDER +]; + +const MULTIROOT_QUERIES: IFolderSearch[] = [ + { folder: EXAMPLES_FIXTURES }, + { folder: MORE_FIXTURES } +]; + +suite('FileSearchEngine', () => { + + test('Files: *.js', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: '*.js' + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 4); + done(); + }); + }); + + test('Files: examples/com*', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: normalize(join('examples', 'com*'), true) + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + done(); + }); + }); + + test('Files: examples (fuzzy)', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: 'xl' + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 7); + done(); + }); + }); + + test('Files: multiroot', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: MULTIROOT_QUERIES, + filePattern: 'file' + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 3); + done(); + }); + }); + + test('Files: NPE (CamelCase)', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: 'NullPE' + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + done(); + }); + }); + + test('Files: *.*', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: '*.*' + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 14); + done(); + }); + }); + + test('Files: *.as', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: '*.as' + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 0); + done(); + }); + }); + + test('Files: *.* without derived', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: 'site.*', + excludePattern: { '**/*.css': { 'when': '$(basename).less' } } + }); + + let count = 0; + let res: IRawFileMatch; + engine.search((result) => { + if (result) { + count++; + } + res = result; + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + assert.strictEqual(path.basename(res.relativePath), 'site.less'); + done(); + }); + }); + + test('Files: *.* exclude folder without wildcard', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: '*.*', + excludePattern: { 'examples': true } + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 8); + done(); + }); + }); + + test('Files: *.* exclude folder with leading wildcard', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: '*.*', + excludePattern: { '**/examples': true } + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 8); + done(); + }); + }); + + test('Files: *.* exclude folder with trailing wildcard', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: '*.*', + excludePattern: { 'examples/**': true } + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 8); + done(); + }); + }); + + test('Files: *.* exclude with unicode', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: '*.*', + excludePattern: { '**/üm laut汉语': true } + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 13); + done(); + }); + }); + + test('Files: multiroot with exclude', function (done: () => void) { + const folderQueries: IFolderSearch[] = [ + { + folder: EXAMPLES_FIXTURES, + excludePattern: { + '**/anotherfile.txt': true + } + }, + { + folder: MORE_FIXTURES, + excludePattern: { + '**/file.txt': true + } + } + ]; + + const engine = new FileSearchEngine({ + folderQueries, + filePattern: '*' + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 5); + done(); + }); + }); + + test('Files: Unicode and Spaces', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: '汉语' + }); + + let count = 0; + let res: IRawFileMatch; + engine.search((result) => { + if (result) { + count++; + } + res = result; + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + assert.equal(path.basename(res.relativePath), '汉语.txt'); + done(); + }); + }); + + test('Files: no results', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: 'nofilematch' + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 0); + done(); + }); + }); + + test('Files: absolute path to file ignores excludes', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), + excludePattern: { '**/*.css': true } + }); + + let count = 0; + let res: IRawFileMatch; + engine.search((result) => { + if (result) { + count++; + } + res = result; + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + assert.equal(path.basename(res.relativePath), 'site.css'); + done(); + }); + }); + + test('Files: relative path matched once', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: path.normalize(path.join('examples', 'company.js')) + }); + + let count = 0; + let res: IRawFileMatch; + engine.search((result) => { + if (result) { + count++; + } + res = result; + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + assert.equal(path.basename(res.relativePath), 'company.js'); + done(); + }); + }); + + test('Files: relative path to file ignores excludes', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + filePattern: path.normalize(path.join('examples', 'company.js')), + excludePattern: { '**/*.js': true } + }); + + let count = 0; + let res: IRawFileMatch; + engine.search((result) => { + if (result) { + count++; + } + res = result; + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + assert.equal(path.basename(res.relativePath), 'company.js'); + done(); + }); + }); + + test('Files: Include pattern, single files', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + includePattern: { + 'site.css': true, + 'examples/company.js': true, + 'examples/subfolder/subfile.txt': true + } + }); + + let res: IRawFileMatch[] = []; + engine.search((result) => { + res.push(result); + }, () => { }, (error) => { + assert.ok(!error); + const basenames = res.map(r => path.basename(r.relativePath)); + assert.ok(basenames.indexOf('site.css') !== -1, `site.css missing in ${JSON.stringify(basenames)}`); + assert.ok(basenames.indexOf('company.js') !== -1, `company.js missing in ${JSON.stringify(basenames)}`); + assert.ok(basenames.indexOf('subfile.txt') !== -1, `subfile.txt missing in ${JSON.stringify(basenames)}`); + done(); + }); + }); + + test('Files: extraFiles only', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: [], + extraFiles: [ + path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), + path.normalize(path.join(require.toUrl('./fixtures'), 'examples', 'company.js')), + path.normalize(path.join(require.toUrl('./fixtures'), 'index.html')) + ], + filePattern: '*.js' + }); + + let count = 0; + let res: IRawFileMatch; + engine.search((result) => { + if (result) { + count++; + } + res = result; + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + assert.equal(path.basename(res.relativePath), 'company.js'); + done(); + }); + }); + + test('Files: extraFiles only (with include)', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: [], + extraFiles: [ + path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), + path.normalize(path.join(require.toUrl('./fixtures'), 'examples', 'company.js')), + path.normalize(path.join(require.toUrl('./fixtures'), 'index.html')) + ], + filePattern: '*.*', + includePattern: { '**/*.css': true } + }); + + let count = 0; + let res: IRawFileMatch; + engine.search((result) => { + if (result) { + count++; + } + res = result; + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + assert.equal(path.basename(res.relativePath), 'site.css'); + done(); + }); + }); + + test('Files: extraFiles only (with exclude)', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: [], + extraFiles: [ + path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), + path.normalize(path.join(require.toUrl('./fixtures'), 'examples', 'company.js')), + path.normalize(path.join(require.toUrl('./fixtures'), 'index.html')) + ], + filePattern: '*.*', + excludePattern: { '**/*.css': true } + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 2); + done(); + }); + }); + + test('Files: no dupes in nested folders', function (done: () => void) { + let engine = new FileSearchEngine({ + folderQueries: [ + { folder: EXAMPLES_FIXTURES }, + { folder: path.join(EXAMPLES_FIXTURES, 'subfolder') } + ], + filePattern: 'subfile.txt' + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + done(); + }); + }); +}); + +suite('FileWalker', () => { + + test('Find: exclude subfolder', function (done: () => void) { + if (platform.isWindows) { + done(); + return; + } + + const file0 = './more/file.txt'; + const file1 = './examples/subfolder/subfile.txt'; + + const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/something': true } }); + const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { + assert.equal(err1, null); + assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); + assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); + + const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder': true } }); + const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd2, 'utf8', (err2, stdout2) => { + assert.equal(err2, null); + assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); + assert.strictEqual(stdout2.split('\n').indexOf(file1), -1, stdout2); + done(); + }); + }); + }); + + test('Find: folder excludes', function (done: () => void) { + if (platform.isWindows) { + done(); + return; + } + + const folderQueries: IFolderSearch[] = [ + { + folder: TEST_FIXTURES, + excludePattern: { '**/subfolder': true } + } + ]; + + const file0 = './more/file.txt'; + const file1 = './examples/subfolder/subfile.txt'; + + const walker = new FileWalker({ folderQueries }); + const cmd1 = walker.spawnFindCmd(folderQueries[0]); + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { + assert.equal(err1, null); + assert(outputContains(stdout1, file0), stdout1); + assert(!outputContains(stdout1, file1), stdout1); + done(); + }); + }); + + test('Find: exclude multiple folders', function (done: () => void) { + if (platform.isWindows) { + done(); + return; + } + + const file0 = './index.html'; + const file1 = './examples/small.js'; + const file2 = './more/file.txt'; + + const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/something': true } }); + const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { + assert.equal(err1, null); + assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); + assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); + assert.notStrictEqual(stdout1.split('\n').indexOf(file2), -1, stdout1); + + const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '{**/examples,**/more}': true } }); + const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd2, 'utf8', (err2, stdout2) => { + assert.equal(err2, null); + assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); + assert.strictEqual(stdout2.split('\n').indexOf(file1), -1, stdout2); + assert.strictEqual(stdout2.split('\n').indexOf(file2), -1, stdout2); + done(); + }); + }); + }); + + test('Find: exclude folder path suffix', function (done: () => void) { + if (platform.isWindows) { + done(); + return; + } + + const file0 = './examples/company.js'; + const file1 = './examples/subfolder/subfile.txt'; + + const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/examples/something': true } }); + const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { + assert.equal(err1, null); + assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); + assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); + + const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/examples/subfolder': true } }); + const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd2, 'utf8', (err2, stdout2) => { + assert.equal(err2, null); + assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); + assert.strictEqual(stdout2.split('\n').indexOf(file1), -1, stdout2); + done(); + }); + }); + }); + + test('Find: exclude subfolder path suffix', function (done: () => void) { + if (platform.isWindows) { + done(); + return; + } + + const file0 = './examples/subfolder/subfile.txt'; + const file1 = './examples/subfolder/anotherfolder/anotherfile.txt'; + + const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/something': true } }); + const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { + assert.equal(err1, null); + assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); + assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); + + const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/anotherfolder': true } }); + const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd2, 'utf8', (err2, stdout2) => { + assert.equal(err2, null); + assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); + assert.strictEqual(stdout2.split('\n').indexOf(file1), -1, stdout2); + done(); + }); + }); + }); + + test('Find: exclude folder path', function (done: () => void) { + if (platform.isWindows) { + done(); + return; + } + + const file0 = './examples/company.js'; + const file1 = './examples/subfolder/subfile.txt'; + + const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { 'examples/something': true } }); + const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { + assert.equal(err1, null); + assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); + assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); + + const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { 'examples/subfolder': true } }); + const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd2, 'utf8', (err2, stdout2) => { + assert.equal(err2, null); + assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); + assert.strictEqual(stdout2.split('\n').indexOf(file1), -1, stdout2); + done(); + }); + }); + }); + + test('Find: exclude combination of paths', function (done: () => void) { + if (platform.isWindows) { + done(); + return; + } + + const filesIn = [ + './examples/subfolder/subfile.txt', + './examples/company.js', + './index.html' + ]; + const filesOut = [ + './examples/subfolder/anotherfolder/anotherfile.txt', + './more/file.txt' + ]; + + const walker = new FileWalker({ + folderQueries: ROOT_FOLDER_QUERY, + excludePattern: { + '**/subfolder/anotherfolder': true, + '**/something/else': true, + '**/more': true, + '**/andmore': true + } + }); + const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { + assert.equal(err1, null); + for (const fileIn of filesIn) { + assert.notStrictEqual(stdout1.split('\n').indexOf(fileIn), -1, stdout1); + } + for (const fileOut of filesOut) { + assert.strictEqual(stdout1.split('\n').indexOf(fileOut), -1, stdout1); + } + done(); + }); + }); + + function outputContains(stdout: string, ...files: string[]): boolean { + const lines = stdout.split('\n'); + return files.every(file => lines.indexOf(file) >= 0); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/search/test/node/searchService.test.ts b/src/vs/workbench/services/search/test/node/searchService.test.ts new file mode 100644 index 0000000000..08974bfbdd --- /dev/null +++ b/src/vs/workbench/services/search/test/node/searchService.test.ts @@ -0,0 +1,271 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { normalize } from 'path'; + +import { IProgress, IUncachedSearchStats } from 'vs/platform/search/common/search'; +import { ISearchEngine, IRawSearch, IRawFileMatch, ISerializedFileMatch, ISerializedSearchComplete } from 'vs/workbench/services/search/node/search'; +import { SearchService as RawSearchService } from 'vs/workbench/services/search/node/rawSearchService'; +import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; + +const TEST_FOLDER_QUERIES = [ + { folder: normalize('/some/where') } +]; + +const stats: IUncachedSearchStats = { + fromCache: false, + resultCount: 4, + traversal: 'node', + errors: [], + fileWalkStartTime: 0, + fileWalkResultTime: 1, + directoriesWalked: 2, + filesWalked: 3 +}; + +class TestSearchEngine implements ISearchEngine { + + public static last: TestSearchEngine; + + private isCanceled = false; + + constructor(private result: () => IRawFileMatch, public config?: IRawSearch) { + TestSearchEngine.last = this; + } + + public search(onResult: (match: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchComplete) => void): void { + const self = this; + (function next() { + process.nextTick(() => { + if (self.isCanceled) { + done(null, { + limitHit: false, + stats: stats + }); + return; + } + const result = self.result(); + if (!result) { + done(null, { + limitHit: false, + stats: stats + }); + } else { + onResult(result); + next(); + } + }); + })(); + } + + public cancel(): void { + this.isCanceled = true; + } +} + +suite('SearchService', () => { + + const rawSearch: IRawSearch = { + folderQueries: TEST_FOLDER_QUERIES, + filePattern: 'a' + }; + + const rawMatch: IRawFileMatch = { + base: normalize('/some'), + relativePath: 'where', + basename: 'where', + size: 123 + }; + + const match: ISerializedFileMatch = { + path: normalize('/some/where') + }; + + test('Individual results', function () { + let i = 5; + const Engine = TestSearchEngine.bind(null, () => i-- && rawMatch); + const service = new RawSearchService(); + + let results = 0; + return service.doFileSearch(Engine, rawSearch) + .then(() => { + assert.strictEqual(results, 5); + }, null, value => { + if (!Array.isArray(value)) { + assert.deepStrictEqual(value, match); + results++; + } else { + assert.fail(value); + } + }); + }); + + test('Batch results', function () { + let i = 25; + const Engine = TestSearchEngine.bind(null, () => i-- && rawMatch); + const service = new RawSearchService(); + + const results = []; + return service.doFileSearch(Engine, rawSearch, 10) + .then(() => { + assert.deepStrictEqual(results, [10, 10, 5]); + }, null, value => { + if (Array.isArray(value)) { + value.forEach(m => { + assert.deepStrictEqual(m, match); + }); + results.push(value.length); + } else { + assert.fail(value); + } + }); + }); + + test('Collect batched results', function () { + const uriPath = '/some/where'; + let i = 25; + const Engine = TestSearchEngine.bind(null, () => i-- && rawMatch); + const service = new RawSearchService(); + + const progressResults = []; + return DiskSearch.collectResults(service.doFileSearch(Engine, rawSearch, 10)) + .then(result => { + assert.strictEqual(result.results.length, 25, 'Result'); + assert.strictEqual(progressResults.length, 25, 'Progress'); + }, null, match => { + assert.strictEqual(match.resource.path, uriPath); + progressResults.push(match); + }); + }); + + test('Sorted results', function () { + const paths = ['bab', 'bbc', 'abb']; + const matches: IRawFileMatch[] = paths.map(relativePath => ({ + base: normalize('/some/where'), + relativePath, + basename: relativePath, + size: 3 + })); + const Engine = TestSearchEngine.bind(null, () => matches.shift()); + const service = new RawSearchService(); + + const results = []; + return service.doFileSearch(Engine, { + folderQueries: TEST_FOLDER_QUERIES, + filePattern: 'bb', + sortByScore: true, + maxResults: 2 + }, 1).then(() => { + assert.notStrictEqual(typeof TestSearchEngine.last.config.maxResults, 'number'); + assert.deepStrictEqual(results, [normalize('/some/where/bbc'), normalize('/some/where/bab')]); + }, null, value => { + if (Array.isArray(value)) { + results.push(...value.map(v => v.path)); + } else { + assert.fail(value); + } + }); + }); + + test('Sorted result batches', function () { + let i = 25; + const Engine = TestSearchEngine.bind(null, () => i-- && rawMatch); + const service = new RawSearchService(); + + const results = []; + return service.doFileSearch(Engine, { + folderQueries: TEST_FOLDER_QUERIES, + filePattern: 'a', + sortByScore: true, + maxResults: 23 + }, 10) + .then(() => { + assert.deepStrictEqual(results, [10, 10, 3]); + }, null, value => { + if (Array.isArray(value)) { + value.forEach(m => { + assert.deepStrictEqual(m, match); + }); + results.push(value.length); + } else { + assert.fail(value); + } + }); + }); + + test('Cached results', function () { + const paths = ['bcb', 'bbc', 'aab']; + const matches: IRawFileMatch[] = paths.map(relativePath => ({ + base: normalize('/some/where'), + relativePath, + basename: relativePath, + size: 3 + })); + const Engine = TestSearchEngine.bind(null, () => matches.shift()); + const service = new RawSearchService(); + + const results = []; + return service.doFileSearch(Engine, { + folderQueries: TEST_FOLDER_QUERIES, + filePattern: 'b', + sortByScore: true, + cacheKey: 'x' + }, -1).then(complete => { + assert.strictEqual(complete.stats.fromCache, false); + assert.deepStrictEqual(results, [normalize('/some/where/bcb'), normalize('/some/where/bbc'), normalize('/some/where/aab')]); + }, null, value => { + if (Array.isArray(value)) { + results.push(...value.map(v => v.path)); + } else { + assert.fail(value); + } + }).then(() => { + const results = []; + return service.doFileSearch(Engine, { + folderQueries: TEST_FOLDER_QUERIES, + filePattern: 'bc', + sortByScore: true, + cacheKey: 'x' + }, -1).then(complete => { + assert.ok(complete.stats.fromCache); + assert.deepStrictEqual(results, [normalize('/some/where/bcb'), normalize('/some/where/bbc')]); + }, null, value => { + if (Array.isArray(value)) { + results.push(...value.map(v => v.path)); + } else { + assert.fail(value); + } + }); + }).then(() => { + return service.clearCache('x'); + }).then(() => { + matches.push({ + base: normalize('/some/where'), + relativePath: 'bc', + basename: 'bc', + size: 3 + }); + const results = []; + return service.doFileSearch(Engine, { + folderQueries: TEST_FOLDER_QUERIES, + filePattern: 'bc', + sortByScore: true, + cacheKey: 'x' + }, -1).then(complete => { + assert.strictEqual(complete.stats.fromCache, false); + assert.deepStrictEqual(results, [normalize('/some/where/bc')]); + }, null, value => { + if (Array.isArray(value)) { + results.push(...value.map(v => v.path)); + } else { + assert.fail(value); + } + }); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts new file mode 100644 index 0000000000..f9f0ef7e36 --- /dev/null +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -0,0 +1,310 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import path = require('path'); +import assert = require('assert'); + +import * as glob from 'vs/base/common/glob'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; +import { ISerializedFileMatch, IRawSearch, IFolderSearch } from 'vs/workbench/services/search/node/search'; +import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch'; +import { RipgrepEngine } from 'vs/workbench/services/search/node/ripgrepTextSearch'; +import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider'; + +function countAll(matches: ISerializedFileMatch[]): number { + return matches.reduce((acc, m) => acc + m.numMatches, 0); +} + +const TEST_FIXTURES = path.normalize(require.toUrl('./fixtures')); +const EXAMPLES_FIXTURES = path.join(TEST_FIXTURES, 'examples'); +const MORE_FIXTURES = path.join(TEST_FIXTURES, 'more'); +const TEST_ROOT_FOLDER: IFolderSearch = { folder: TEST_FIXTURES }; +const ROOT_FOLDER_QUERY: IFolderSearch[] = [ + TEST_ROOT_FOLDER +]; + +const MULTIROOT_QUERIES: IFolderSearch[] = [ + { folder: EXAMPLES_FIXTURES }, + { folder: MORE_FIXTURES } +]; + +const textSearchWorkerProvider = new TextSearchWorkerProvider(); + +function doLegacySearchTest(config: IRawSearch, expectedResultCount: number | Function): TPromise { + return new TPromise((resolve, reject) => { + let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider); + + let c = 0; + engine.search((result) => { + if (result) { + c += countAll(result); + } + }, () => { }, (error) => { + try { + assert.ok(!error); + if (typeof expectedResultCount === 'function') { + assert(expectedResultCount(c)); + } else { + assert.equal(c, expectedResultCount, 'legacy'); + } + } catch (e) { + reject(e); + } + + resolve(undefined); + }); + }); +} + +function doRipgrepSearchTest(config: IRawSearch, expectedResultCount: number): TPromise { + return new TPromise((resolve, reject) => { + let engine = new RipgrepEngine(config); + + let c = 0; + engine.search((result) => { + if (result) { + c += result.numMatches; + } + }, () => { }, (error) => { + try { + assert.ok(!error); + if (typeof expectedResultCount === 'function') { + assert(expectedResultCount(c)); + } else { + assert.equal(c, expectedResultCount, 'rg'); + } + } catch (e) { + reject(e); + } + + resolve(undefined); + }); + }); +} + +function doSearchTest(config: IRawSearch, expectedResultCount: number, done) { + return doLegacySearchTest(config, expectedResultCount) + .then(() => doRipgrepSearchTest(config, expectedResultCount)) + .then(done, done); +} + +suite('Search-integration', function () { + this.timeout(1000 * 60); // increase timeout for this suite + + test('Text: GameOfLife', function (done: () => void) { + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'GameOfLife' }, + }; + + doSearchTest(config, 4, done); + }); + + test('Text: GameOfLife (RegExp)', function (done: () => void) { + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'Game.?fL\\w?fe', isRegExp: true } + }; + + doSearchTest(config, 4, done); + }); + + test('Text: GameOfLife (RegExp to EOL)', function (done: () => void) { + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'GameOfLife.*', isRegExp: true } + }; + + doSearchTest(config, 4, done); + }); + + test('Text: GameOfLife (Word Match, Case Sensitive)', function (done: () => void) { + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'GameOfLife', isWordMatch: true, isCaseSensitive: true } + }; + + doSearchTest(config, 4, done); + }); + + test('Text: GameOfLife (Word Match, Spaces)', function (done: () => void) { + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: ' GameOfLife ', isWordMatch: true } + }; + + doSearchTest(config, 1, done); + }); + + test('Text: GameOfLife (Word Match, Punctuation and Spaces)', function (done: () => void) { + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: ', as =', isWordMatch: true } + }; + + doSearchTest(config, 1, done); + }); + + test('Text: Helvetica (UTF 16)', function (done: () => void) { + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'Helvetica' } + }; + + doSearchTest(config, 3, done); + }); + + test('Text: e', function (done: () => void) { + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'e' } + }; + + doSearchTest(config, 776, done); + }); + + test('Text: e (with excludes)', function (done: () => void) { + const config: any = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'e' }, + excludePattern: { '**/examples': true } + }; + + doSearchTest(config, 394, done); + }); + + test('Text: e (with includes)', function (done: () => void) { + const config: any = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'e' }, + includePattern: { '**/examples/**': true } + }; + + doSearchTest(config, 382, done); + }); + + test('Text: e (with absolute path excludes)', function (done: () => void) { + const config: any = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'e' }, + excludePattern: makeExpression(path.join(TEST_FIXTURES, '**/examples')) + }; + + doSearchTest(config, 394, done); + }); + + test('Text: e (with mixed absolute/relative path excludes)', function (done: () => void) { + const config: any = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'e' }, + excludePattern: makeExpression(path.join(TEST_FIXTURES, '**/examples'), '*.css') + }; + + doSearchTest(config, 310, done); + }); + + test('Text: sibling exclude', function (done: () => void) { + const config: any = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'm' }, + includePattern: makeExpression('**/site*'), + excludePattern: { '*.css': { when: '$(basename).less' } } + }; + + doSearchTest(config, 1, done); + }); + + test('Text: e (with includes and exclude)', function (done: () => void) { + const config: any = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'e' }, + includePattern: { '**/examples/**': true }, + excludePattern: { '**/examples/small.js': true } + }; + + doSearchTest(config, 361, done); + }); + + test('Text: a (capped)', function (done: () => void) { + const maxResults = 520; + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'a' }, + maxResults + }; + + // (Legacy) search can go over the maxResults because it doesn't trim the results from its worker processes to the exact max size. + // But the worst-case scenario should be 2*max-1 + return doLegacySearchTest(config, count => count < maxResults * 2) + .then(() => doRipgrepSearchTest(config, maxResults)) + .then(done, done); + }); + + test('Text: a (no results)', function (done: () => void) { + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'ahsogehtdas' } + }; + + doSearchTest(config, 0, done); + }); + + test('Text: -size', function (done: () => void) { + const config = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: '-size' } + }; + + doSearchTest(config, 9, done); + }); + + test('Multiroot: Conway', function (done: () => void) { + const config: IRawSearch = { + folderQueries: MULTIROOT_QUERIES, + contentPattern: { pattern: 'conway' } + }; + + doSearchTest(config, 8, done); + }); + + test('Multiroot: e with partial global exclude', function (done: () => void) { + const config: IRawSearch = { + folderQueries: MULTIROOT_QUERIES, + contentPattern: { pattern: 'e' }, + excludePattern: makeExpression('**/*.txt') + }; + + doSearchTest(config, 382, done); + }); + + test('Multiroot: e with global excludes', function (done: () => void) { + const config: IRawSearch = { + folderQueries: MULTIROOT_QUERIES, + contentPattern: { pattern: 'e' }, + excludePattern: makeExpression('**/*.txt', '**/*.js') + }; + + doSearchTest(config, 0, done); + }); + + test('Multiroot: e with folder exclude', function (done: () => void) { + const config: IRawSearch = { + folderQueries: [ + { folder: EXAMPLES_FIXTURES, excludePattern: makeExpression('**/e*.js') }, + { folder: MORE_FIXTURES } + ], + contentPattern: { pattern: 'e' } + }; + + doSearchTest(config, 286, done); + }); +}); + +function makeExpression(...patterns: string[]): glob.IExpression { + return patterns.reduce((glob, cur) => { glob[cur] = true; return glob; }, Object.create(null)); +} \ No newline at end of file diff --git a/src/vs/workbench/services/telemetry/common/workspaceStats.ts b/src/vs/workbench/services/telemetry/common/workspaceStats.ts new file mode 100644 index 0000000000..54e982f595 --- /dev/null +++ b/src/vs/workbench/services/telemetry/common/workspaceStats.ts @@ -0,0 +1,334 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as crypto from 'crypto'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import URI from 'vs/base/common/uri'; +import { IFileService, IFileStat } from 'vs/platform/files/common/files'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; + +const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/; +const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/; +const AuthorityMatcher = /^([^@]+@)?([^:]+)(:\d+)?$/; +const SecondLevelDomainMatcher = /([^@:.]+\.[^@:.]+)(:\d+)?$/; +const RemoteMatcher = /^\s*url\s*=\s*(.+\S)\s*$/mg; +const AnyButDot = /[^.]/g; +const SecondLevelDomainWhitelist = [ + 'github.com', + 'bitbucket.org', + 'visualstudio.com', + 'gitlab.com', + 'heroku.com', + 'azurewebsites.net', + 'ibm.com', + 'amazon.com', + 'amazonaws.com', + 'cloudapp.net', + 'rhcloud.com', + 'google.com' +]; + +type Tags = { [index: string]: boolean | number }; + +function stripLowLevelDomains(domain: string): string { + let match = domain.match(SecondLevelDomainMatcher); + return match ? match[1] : null; +} + +function extractDomain(url: string): string { + if (url.indexOf('://') === -1) { + let match = url.match(SshProtocolMatcher); + if (match) { + return stripLowLevelDomains(match[2]); + } + } + try { + let uri = URI.parse(url); + if (uri.authority) { + return stripLowLevelDomains(uri.authority); + } + } catch (e) { + // ignore invalid URIs + } + return null; +} + +export function getDomainsOfRemotes(text: string, whitelist: string[]): string[] { + let domains = new Set(); + let match: RegExpExecArray; + while (match = RemoteMatcher.exec(text)) { + let domain = extractDomain(match[1]); + if (domain) { + domains.add(domain); + } + } + + const whitemap = whitelist.reduce((map, key) => { + map[key] = true; + return map; + }, Object.create(null)); + + const elements: string[] = []; + domains.forEach(e => elements.push(e)); + + return elements + .map(key => whitemap[key] ? key : key.replace(AnyButDot, 'a')); +} + +function stripPort(authority: string): string { + const match = authority.match(AuthorityMatcher); + return match ? match[2] : null; +} + +function normalizeRemote(host: string, path: string): string { + if (host && path) { + return (path.indexOf('/') === 0) ? `${host}${path}` : `${host}/${path}`; + } + return null; +} + +function extractRemote(url: string): string { + if (url.indexOf('://') === -1) { + const match = url.match(SshUrlMatcher); + if (match) { + return normalizeRemote(match[2], match[3]); + } + } + try { + const uri = URI.parse(url); + if (uri.authority) { + return normalizeRemote(stripPort(uri.authority), uri.path); + } + } catch (e) { + // ignore invalid URIs + } + return null; +} + +export function getRemotes(text: string): string[] { + const remotes: string[] = []; + let match: RegExpExecArray; + while (match = RemoteMatcher.exec(text)) { + const remote = extractRemote(match[1]); + if (remote) { + remotes.push(remote); + } + } + return remotes; +} + +export function getHashedRemotes(text: string): string[] { + return getRemotes(text).map(r => { + return crypto.createHash('sha1').update(r).digest('hex'); + }); +} + +export class WorkspaceStats { + constructor( + @IFileService private fileService: IFileService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ITelemetryService private telemetryService: ITelemetryService, + @IEnvironmentService private environmentService: IEnvironmentService + ) { + } + + private searchArray(arr: string[], regEx: RegExp): boolean { + return arr.some(v => v.search(regEx) > -1) || undefined; + } + + private getWorkspaceTags(configuration: IWindowConfiguration): TPromise { + const tags: Tags = Object.create(null); + + const { filesToOpen, filesToCreate, filesToDiff } = configuration; + tags['workbench.filesToOpen'] = filesToOpen && filesToOpen.length || undefined; + tags['workbench.filesToCreate'] = filesToCreate && filesToCreate.length || undefined; + tags['workbench.filesToDiff'] = filesToDiff && filesToDiff.length || undefined; + + const workspace = this.contextService.getWorkspace(); + tags['workspace.roots'] = workspace ? workspace.roots.length : 0; + tags['workspace.empty'] = !workspace; + + const folders = workspace ? workspace.roots : this.environmentService.appQuality !== 'stable' && this.findFolders(configuration); + if (folders && folders.length && this.fileService) { + return this.fileService.resolveFiles(folders.map(resource => ({ resource }))).then(results => { + const names = ([]).concat(...results.map(result => result.success ? (result.stat.children || []) : [])).map(c => c.name); + + tags['workspace.grunt'] = this.searchArray(names, /^gruntfile\.js$/i); + tags['workspace.gulp'] = this.searchArray(names, /^gulpfile\.js$/i); + tags['workspace.jake'] = this.searchArray(names, /^jakefile\.js$/i); + + tags['workspace.tsconfig'] = this.searchArray(names, /^tsconfig\.json$/i); + tags['workspace.jsconfig'] = this.searchArray(names, /^jsconfig\.json$/i); + tags['workspace.config.xml'] = this.searchArray(names, /^config\.xml/i); + tags['workspace.vsc.extension'] = this.searchArray(names, /^vsc-extension-quickstart\.md/i); + + tags['workspace.ASP5'] = this.searchArray(names, /^project\.json$/i) && this.searchArray(names, /^.+\.cs$/i); + tags['workspace.sln'] = this.searchArray(names, /^.+\.sln$|^.+\.csproj$/i); + tags['workspace.unity'] = this.searchArray(names, /^Assets$/i) && this.searchArray(names, /^Library$/i) && this.searchArray(names, /^ProjectSettings/i); + tags['workspace.npm'] = this.searchArray(names, /^package\.json$|^node_modules$/i); + tags['workspace.bower'] = this.searchArray(names, /^bower\.json$|^bower_components$/i); + + tags['workspace.yeoman.code.ext'] = this.searchArray(names, /^vsc-extension-quickstart\.md$/i); + + let mainActivity = this.searchArray(names, /^MainActivity\.cs$/i) || this.searchArray(names, /^MainActivity\.fs$/i); + let appDelegate = this.searchArray(names, /^AppDelegate\.cs$/i) || this.searchArray(names, /^AppDelegate\.fs$/i); + let androidManifest = this.searchArray(names, /^AndroidManifest\.xml$/i); + + let platforms = this.searchArray(names, /^platforms$/i); + let plugins = this.searchArray(names, /^plugins$/i); + let www = this.searchArray(names, /^www$/i); + let properties = this.searchArray(names, /^Properties/i); + let resources = this.searchArray(names, /^Resources/i); + let jni = this.searchArray(names, /^JNI/i); + + if (tags['workspace.config.xml'] && + !tags['workspace.language.cs'] && !tags['workspace.language.vb'] && !tags['workspace.language.aspx']) { + if (platforms && plugins && www) { + tags['workspace.cordova.high'] = true; + } else { + tags['workspace.cordova.low'] = true; + } + } + + if (mainActivity && properties && resources) { + tags['workspace.xamarin.android'] = true; + } + + if (appDelegate && resources) { + tags['workspace.xamarin.ios'] = true; + } + + if (androidManifest && jni) { + tags['workspace.android.cpp'] = true; + } + + tags['workspace.reactNative'] = this.searchArray(names, /^android$/i) && this.searchArray(names, /^ios$/i) && + this.searchArray(names, /^index\.android\.js$/i) && this.searchArray(names, /^index\.ios\.js$/i); + + return tags; + }, error => { onUnexpectedError(error); return null; }); + } else { + return TPromise.as(tags); + } + } + + private findFolders(configuration: IWindowConfiguration): URI[] { + const folder = this.findFolder(configuration); + return folder && [folder]; + } + + private findFolder({ filesToOpen, filesToCreate, filesToDiff }: IWindowConfiguration): URI { + if (filesToOpen && filesToOpen.length) { + return this.parentURI(URI.file(filesToOpen[0].filePath)); + } else if (filesToCreate && filesToCreate.length) { + return this.parentURI(URI.file(filesToCreate[0].filePath)); + } else if (filesToDiff && filesToDiff.length) { + return this.parentURI(URI.file(filesToDiff[0].filePath)); + } + return undefined; + } + + private parentURI(uri: URI): URI { + const path = uri.path; + const i = path.lastIndexOf('/'); + return i !== -1 ? uri.with({ path: path.substr(0, i) }) : undefined; + } + + public reportWorkspaceTags(configuration: IWindowConfiguration): void { + this.getWorkspaceTags(configuration).then((tags) => { + this.telemetryService.publicLog('workspce.tags', tags); + }, error => onUnexpectedError(error)); + } + + private reportRemoteDomains(workspaceUris: URI[]): void { + TPromise.join(workspaceUris.map(workspaceUri => { + const path = workspaceUri.path; + const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); + return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then( + content => getDomainsOfRemotes(content.value, SecondLevelDomainWhitelist), + err => [] // ignore missing or binary file + ); + })).then(domains => { + const set = domains.reduce((set, list) => list.reduce((set, item) => set.add(item), set), new Set()); + const list: string[] = []; + set.forEach(item => list.push(item)); + this.telemetryService.publicLog('workspace.remotes', { domains: list.sort() }); + }, onUnexpectedError); + } + + private reportRemotes(workspaceUris: URI[]): void { + TPromise.join(workspaceUris.map(workspaceUri => { + let path = workspaceUri.path; + let uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); + return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then( + content => getHashedRemotes(content.value), + err => [] // ignore missing or binary file + ); + })).then(hashedRemotes => this.telemetryService.publicLog('workspace.hashedRemotes', { remotes: hashedRemotes }), onUnexpectedError); + } + + private reportAzureNode(workspaceUris: URI[], tags: Tags): TPromise { + // TODO: should also work for `node_modules` folders several levels down + const uris = workspaceUris.map(workspaceUri => { + const path = workspaceUri.path; + return workspaceUri.with({ path: `${path !== '/' ? path : ''}/node_modules` }); + }); + return this.fileService.resolveFiles(uris.map(resource => ({ resource }))).then( + results => { + const names = ([]).concat(...results.map(result => result.success ? (result.stat.children || []) : [])).map(c => c.name); + const referencesAzure = this.searchArray(names, /azure/i); + if (referencesAzure) { + tags['node'] = true; + } + return tags; + }, + err => { + return tags; + }); + } + + private reportAzureJava(workspaceUris: URI[], tags: Tags): TPromise { + return TPromise.join(workspaceUris.map(workspaceUri => { + const path = workspaceUri.path; + const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/pom.xml` }); + return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then( + content => !!content.value.match(/azure/i), + err => false + ); + })).then(javas => { + if (javas.indexOf(true) !== -1) { + tags['java'] = true; + } + return tags; + }); + } + + private reportAzure(uris: URI[]) { + const tags: Tags = Object.create(null); + this.reportAzureNode(uris, tags).then((tags) => { + return this.reportAzureJava(uris, tags); + }).then((tags) => { + if (Object.keys(tags).length) { + this.telemetryService.publicLog('workspace.azure', tags); + } + }).then(null, onUnexpectedError); + } + + public reportCloudStats(): void { + const workspace = this.contextService.getWorkspace(); + const uris = workspace && workspace.roots; + if (uris && uris.length && this.fileService) { + this.reportRemoteDomains(uris); + this.reportRemotes(uris); + this.reportAzure(uris); + } + }; +} diff --git a/src/vs/workbench/services/telemetry/test/workspaceStats.test.ts b/src/vs/workbench/services/telemetry/test/workspaceStats.test.ts new file mode 100644 index 0000000000..1f4f1d976b --- /dev/null +++ b/src/vs/workbench/services/telemetry/test/workspaceStats.test.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as crypto from 'crypto'; +import { getDomainsOfRemotes, getRemotes, getHashedRemotes } from 'vs/workbench/services/telemetry/common/workspaceStats'; + +function hash(value: string): string { + return crypto.createHash('sha1').update(value.toString()).digest('hex'); +} + +suite('Telemetry - WorkspaceStats', () => { + + const whitelist = [ + 'github.com', + 'github2.com', + 'github3.com', + 'example.com', + 'example2.com', + 'example3.com', + 'server.org', + 'server2.org', + ]; + + test('HTTPS remotes', function () { + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://github.com/Microsoft/vscode.git'), whitelist), ['github.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://git.example.com/gitproject.git'), whitelist), ['example.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username@github2.com/username/repository.git'), whitelist), ['github2.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username:password@github3.com/username/repository.git'), whitelist), ['github3.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username:password@example2.com:1234/username/repository.git'), whitelist), ['example2.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://example3.com:1234/username/repository.git'), whitelist), ['example3.com']); + }); + + test('SSH remotes', function () { + assert.deepStrictEqual(getDomainsOfRemotes(remote('ssh://user@git.server.org/project.git'), whitelist), ['server.org']); + }); + + test('SCP-like remotes', function () { + assert.deepStrictEqual(getDomainsOfRemotes(remote('git@github.com:Microsoft/vscode.git'), whitelist), ['github.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('user@git.server.org:project.git'), whitelist), ['server.org']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('git.server2.org:project.git'), whitelist), ['server2.org']); + }); + + test('Local remotes', function () { + assert.deepStrictEqual(getDomainsOfRemotes(remote('/opt/git/project.git'), whitelist), []); + assert.deepStrictEqual(getDomainsOfRemotes(remote('file:///opt/git/project.git'), whitelist), []); + }); + + test('Multiple remotes', function () { + const config = ['https://github.com/Microsoft/vscode.git', 'https://git.example.com/gitproject.git'].map(remote).join(''); + assert.deepStrictEqual(getDomainsOfRemotes(config, whitelist).sort(), ['example.com', 'github.com']); + }); + + test('Whitelisting', function () { + const config = ['https://github.com/Microsoft/vscode.git', 'https://git.foobar.com/gitproject.git'].map(remote).join(''); + assert.deepStrictEqual(getDomainsOfRemotes(config, whitelist).sort(), ['aaaaaa.aaa', 'github.com']); + }); + + test('HTTPS remotes to be hashed', function () { + assert.deepStrictEqual(getRemotes(remote('https://github.com/Microsoft/vscode.git')), ['github.com/Microsoft/vscode.git']); + assert.deepStrictEqual(getRemotes(remote('https://git.example.com/gitproject.git')), ['git.example.com/gitproject.git']); + assert.deepStrictEqual(getRemotes(remote('https://username@github2.com/username/repository.git')), ['github2.com/username/repository.git']); + assert.deepStrictEqual(getRemotes(remote('https://username:password@github3.com/username/repository.git')), ['github3.com/username/repository.git']); + assert.deepStrictEqual(getRemotes(remote('https://username:password@example2.com:1234/username/repository.git')), ['example2.com/username/repository.git']); + assert.deepStrictEqual(getRemotes(remote('https://example3.com:1234/username/repository.git')), ['example3.com/username/repository.git']); + }); + + test('SSH remotes to be hashed', function () { + assert.deepStrictEqual(getRemotes(remote('ssh://user@git.server.org/project.git')), ['git.server.org/project.git']); + }); + + test('SCP-like remotes to be hashed', function () { + assert.deepStrictEqual(getRemotes(remote('git@github.com:Microsoft/vscode.git')), ['github.com/Microsoft/vscode.git']); + assert.deepStrictEqual(getRemotes(remote('user@git.server.org:project.git')), ['git.server.org/project.git']); + assert.deepStrictEqual(getRemotes(remote('git.server2.org:project.git')), ['git.server2.org/project.git']); + }); + + test('Local remotes to be hashed', function () { + assert.deepStrictEqual(getRemotes(remote('/opt/git/project.git')), []); + assert.deepStrictEqual(getRemotes(remote('file:///opt/git/project.git')), []); + }); + + test('Multiple remotes to be hashed', function () { + const config = ['https://github.com/Microsoft/vscode.git', 'https://git.example.com/gitproject.git'].map(remote).join(' '); + assert.deepStrictEqual(getRemotes(config), ['github.com/Microsoft/vscode.git', 'git.example.com/gitproject.git']); + }); + + test('Single remote hashed', function () { + assert.deepStrictEqual(getHashedRemotes(remote('https://username:password@github3.com/username/repository.git')), [hash('github3.com/username/repository.git')]); + assert.deepStrictEqual(getHashedRemotes(remote('ssh://user@git.server.org/project.git')), [hash('git.server.org/project.git')]); + assert.deepStrictEqual(getHashedRemotes(remote('user@git.server.org:project.git')), [hash('git.server.org/project.git')]); + assert.deepStrictEqual(getHashedRemotes(remote('/opt/git/project.git')), []); + }); + + test('Multiple remotes hashed', function () { + const config = ['https://github.com/Microsoft/vscode.git', 'https://git.example.com/gitproject.git'].map(remote).join(' '); + assert.deepStrictEqual(getHashedRemotes(config), [hash('github.com/Microsoft/vscode.git'), hash('git.example.com/gitproject.git')]); + }); + + function remote(url: string): string { + return `[remote "origin"] + url = ${url} + fetch = +refs/heads/*:refs/remotes/origin/* +`; + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/textMate/electron-browser/OSSREADME.json b/src/vs/workbench/services/textMate/electron-browser/OSSREADME.json new file mode 100644 index 0000000000..07f8a8a5ed --- /dev/null +++ b/src/vs/workbench/services/textMate/electron-browser/OSSREADME.json @@ -0,0 +1,37 @@ +// Listing in here dependencies that come in at build time from vscode-textmate + +[{ + "name": "lib-oniguruma", + "version": "5.9.3", + "license": "BSD", // according to wikipedia: https://en.wikipedia.org/wiki/Oniguruma + "repositoryURL": "https://github.com/kkos/oniguruma", + "licenseDetail": [ + "Copyright (c) 2002-2007 K.Kosako. All rights reserved.", + "", + "The BSD License", + "", + "Redistribution and use in source and binary forms, with or without", + "modification, are permitted provided that the following conditions", + "are met:", + "", + "1. Redistributions of source code must retain the above copyright", + " notice, this list of conditions and the following disclaimer.", + "", + "2. Redistributions in binary form must reproduce the above copyright", + " notice, this list of conditions and the following disclaimer in the", + " documentation and/or other materials provided with the distribution.", + "", + "THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND", + "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE", + "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR", + "PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS", + "BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR", + "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF", + "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR", + "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,", + "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE", + "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN", + "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ], + "isProd": true +}] diff --git a/src/vs/workbench/services/textMate/electron-browser/TMGrammars.ts b/src/vs/workbench/services/textMate/electron-browser/TMGrammars.ts new file mode 100644 index 0000000000..32e974b2ed --- /dev/null +++ b/src/vs/workbench/services/textMate/electron-browser/TMGrammars.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { IExtensionPoint, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; +import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchModeService'; + +export interface IEmbeddedLanguagesMap { + [scopeName: string]: string; +} + +export interface ITMSyntaxExtensionPoint { + language: string; + scopeName: string; + path: string; + embeddedLanguages: IEmbeddedLanguagesMap; + injectTo: string[]; +} + +export const grammarsExtPoint: IExtensionPoint = ExtensionsRegistry.registerExtensionPoint('grammars', [languagesExtPoint], { + description: nls.localize('vscode.extension.contributes.grammars', 'Contributes textmate tokenizers.'), + type: 'array', + defaultSnippets: [{ body: [{ language: '${1:id}', scopeName: 'source.${2:id}', path: './syntaxes/${3:id}.tmLanguage.' }] }], + items: { + type: 'object', + defaultSnippets: [{ body: { language: '${1:id}', scopeName: 'source.${2:id}', path: './syntaxes/${3:id}.tmLanguage.' } }], + properties: { + language: { + description: nls.localize('vscode.extension.contributes.grammars.language', 'Language identifier for which this syntax is contributed to.'), + type: 'string' + }, + scopeName: { + description: nls.localize('vscode.extension.contributes.grammars.scopeName', 'Textmate scope name used by the tmLanguage file.'), + type: 'string' + }, + path: { + description: nls.localize('vscode.extension.contributes.grammars.path', 'Path of the tmLanguage file. The path is relative to the extension folder and typically starts with \'./syntaxes/\'.'), + type: 'string' + }, + embeddedLanguages: { + description: nls.localize('vscode.extension.contributes.grammars.embeddedLanguages', 'A map of scope name to language id if this grammar contains embedded languages.'), + type: 'object' + }, + injectTo: { + description: nls.localize('vscode.extension.contributes.grammars.injectTo', 'List of language scope names to which this grammar is injected to.'), + type: 'array', + items: { + type: 'string' + } + } + }, + required: ['scopeName', 'path'] + } +}); diff --git a/src/vs/workbench/services/textMate/electron-browser/TMHelper.ts b/src/vs/workbench/services/textMate/electron-browser/TMHelper.ts new file mode 100644 index 0000000000..85f88614f2 --- /dev/null +++ b/src/vs/workbench/services/textMate/electron-browser/TMHelper.ts @@ -0,0 +1,137 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IColorTheme, ITokenColorizationSetting } from 'vs/workbench/services/themes/common/workbenchThemeService'; + +export function findMatchingThemeRule(theme: IColorTheme, scopes: string[]): ThemeRule { + for (let i = scopes.length - 1; i >= 0; i--) { + let parentScopes = scopes.slice(0, i); + let scope = scopes[i]; + let r = findMatchingThemeRule2(theme, scope, parentScopes); + if (r) { + return r; + } + } + return null; +} + +function findMatchingThemeRule2(theme: IColorTheme, scope: string, parentScopes: string[]): ThemeRule { + let result: ThemeRule = null; + + // Loop backwards, to ensure the last most specific rule wins + for (let i = theme.tokenColors.length - 1; i >= 0; i--) { + let rule = theme.tokenColors[i]; + if (!rule.settings.foreground) { + continue; + } + + let selectors: string[]; + if (typeof rule.scope === 'string') { + selectors = rule.scope.split(/,/).map(scope => scope.trim()); + } else if (Array.isArray(rule.scope)) { + selectors = rule.scope; + } else { + continue; + } + + for (let j = 0, lenJ = selectors.length; j < lenJ; j++) { + let rawSelector = selectors[j]; + + let themeRule = new ThemeRule(rawSelector, rule.settings); + if (themeRule.matches(scope, parentScopes)) { + if (themeRule.isMoreSpecific(result)) { + result = themeRule; + } + } + } + } + + return result; +} + +export class ThemeRule { + readonly rawSelector: string; + readonly settings: ITokenColorizationSetting; + readonly scope: string; + readonly parentScopes: string[]; + + constructor(rawSelector: string, settings: ITokenColorizationSetting) { + this.rawSelector = rawSelector; + this.settings = settings; + let rawSelectorPieces = this.rawSelector.split(/ /); + this.scope = rawSelectorPieces[rawSelectorPieces.length - 1]; + this.parentScopes = rawSelectorPieces.slice(0, rawSelectorPieces.length - 1); + } + + public matches(scope: string, parentScopes: string[]): boolean { + return ThemeRule._matches(this.scope, this.parentScopes, scope, parentScopes); + } + + private static _cmp(a: ThemeRule, b: ThemeRule): number { + if (a === null && b === null) { + return 0; + } + if (a === null) { + // b > a + return -1; + } + if (b === null) { + // a > b + return 1; + } + if (a.scope.length !== b.scope.length) { + // longer scope length > shorter scope length + return a.scope.length - b.scope.length; + } + const aParentScopesLen = a.parentScopes.length; + const bParentScopesLen = b.parentScopes.length; + if (aParentScopesLen !== bParentScopesLen) { + // more parents > less parents + return aParentScopesLen - bParentScopesLen; + } + for (let i = 0; i < aParentScopesLen; i++) { + var aLen = a.parentScopes[i].length; + var bLen = b.parentScopes[i].length; + if (aLen !== bLen) { + return aLen - bLen; + } + } + return 0; + } + + public isMoreSpecific(other: ThemeRule): boolean { + return (ThemeRule._cmp(this, other) > 0); + } + + private static _matchesOne(selectorScope: string, scope: string): boolean { + let selectorPrefix = selectorScope + '.'; + if (selectorScope === scope || scope.substring(0, selectorPrefix.length) === selectorPrefix) { + return true; + } + return false; + } + + private static _matches(selectorScope: string, selectorParentScopes: string[], scope: string, parentScopes: string[]): boolean { + if (!this._matchesOne(selectorScope, scope)) { + return false; + } + + let selectorParentIndex = selectorParentScopes.length - 1; + let parentIndex = parentScopes.length - 1; + while (selectorParentIndex >= 0 && parentIndex >= 0) { + if (this._matchesOne(selectorParentScopes[selectorParentIndex], parentScopes[parentIndex])) { + selectorParentIndex--; + } + parentIndex--; + } + + if (selectorParentIndex === -1) { + return true; + } + return false; + } +} diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts new file mode 100644 index 0000000000..13990c5dae --- /dev/null +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -0,0 +1,361 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import * as dom from 'vs/base/browser/dom'; +import * as types from 'vs/base/common/types'; +import Event, { Emitter } from 'vs/base/common/event'; +import { join, normalize } from 'path'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry'; +import { ITokenizationSupport, TokenizationRegistry, IState, LanguageId } from 'vs/editor/common/modes'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { INITIAL, StackElement, IGrammar, Registry, IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2 } from 'vscode-textmate'; +import { IWorkbenchThemeService, ITokenColorizationRule } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; +import { grammarsExtPoint, IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint } from 'vs/workbench/services/textMate/electron-browser/TMGrammars'; +import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; +import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding'; +import { nullTokenize2 } from 'vs/editor/common/modes/nullMode'; +import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; +import { Color } from 'vs/base/common/color'; + +export class TMScopeRegistry { + + private _scopeNameToLanguageRegistration: { [scopeName: string]: TMLanguageRegistration; }; + private _encounteredLanguages: boolean[]; + + private _onDidEncounterLanguage: Emitter = new Emitter(); + public onDidEncounterLanguage: Event = this._onDidEncounterLanguage.event; + + constructor() { + this._scopeNameToLanguageRegistration = Object.create(null); + this._encounteredLanguages = []; + } + + public register(scopeName: string, filePath: string, embeddedLanguages?: IEmbeddedLanguagesMap): void { + this._scopeNameToLanguageRegistration[scopeName] = new TMLanguageRegistration(scopeName, filePath, embeddedLanguages); + } + + public getLanguageRegistration(scopeName: string): TMLanguageRegistration { + return this._scopeNameToLanguageRegistration[scopeName] || null; + } + + public getFilePath(scopeName: string): string { + let data = this.getLanguageRegistration(scopeName); + return data ? data.grammarFilePath : null; + } + + /** + * To be called when tokenization found/hit an embedded language. + */ + public onEncounteredLanguage(languageId: LanguageId): void { + if (!this._encounteredLanguages[languageId]) { + this._encounteredLanguages[languageId] = true; + this._onDidEncounterLanguage.fire(languageId); + } + } +} + +export class TMLanguageRegistration { + _topLevelScopeNameDataBrand: void; + + readonly scopeName: string; + readonly grammarFilePath: string; + readonly embeddedLanguages: IEmbeddedLanguagesMap; + + constructor(scopeName: string, grammarFilePath: string, embeddedLanguages: IEmbeddedLanguagesMap) { + this.scopeName = scopeName; + this.grammarFilePath = grammarFilePath; + + // embeddedLanguages handling + this.embeddedLanguages = Object.create(null); + + if (embeddedLanguages) { + // If embeddedLanguages are configured, fill in `this._embeddedLanguages` + let scopes = Object.keys(embeddedLanguages); + for (let i = 0, len = scopes.length; i < len; i++) { + let scope = scopes[i]; + let language = embeddedLanguages[scope]; + if (typeof language !== 'string') { + // never hurts to be too careful + continue; + } + this.embeddedLanguages[scope] = language; + } + } + } +} + +interface ICreateGrammarResult { + languageId: LanguageId; + grammar: IGrammar; + containsEmbeddedLanguages: boolean; +} + +export class TextMateService implements ITextMateService { + public _serviceBrand: any; + + private _grammarRegistry: Registry; + private _modeService: IModeService; + private _themeService: IWorkbenchThemeService; + private _scopeRegistry: TMScopeRegistry; + private _injections: { [scopeName: string]: string[]; }; + private _languageToScope: Map; + private _styleElement: HTMLStyleElement; + + private _currentTokenColors: ITokenColorizationRule[]; + + public onDidEncounterLanguage: Event; + + constructor( + @IModeService modeService: IModeService, + @IWorkbenchThemeService themeService: IWorkbenchThemeService + ) { + this._styleElement = dom.createStyleSheet(); + this._styleElement.className = 'vscode-tokens-styles'; + this._modeService = modeService; + this._themeService = themeService; + this._scopeRegistry = new TMScopeRegistry(); + this.onDidEncounterLanguage = this._scopeRegistry.onDidEncounterLanguage; + this._injections = {}; + this._languageToScope = new Map(); + + this._grammarRegistry = new Registry({ + getFilePath: (scopeName: string) => { + return this._scopeRegistry.getFilePath(scopeName); + }, + getInjections: (scopeName: string) => { + return this._injections[scopeName]; + } + }); + this._updateTheme(); + this._themeService.onDidColorThemeChange((e) => this._updateTheme()); + + grammarsExtPoint.setHandler((extensions) => { + for (let i = 0; i < extensions.length; i++) { + let grammars = extensions[i].value; + for (let j = 0; j < grammars.length; j++) { + this._handleGrammarExtensionPointUser(extensions[i].description.extensionFolderPath, grammars[j], extensions[i].collector); + } + } + }); + + this._modeService.onDidCreateMode((mode) => { + let modeId = mode.getId(); + if (this._languageToScope.has(modeId)) { + this.registerDefinition(modeId); + } + }); + } + + private static _toColorMap(colorMap: string[]): Color[] { + let result: Color[] = [null]; + for (let i = 1, len = colorMap.length; i < len; i++) { + result[i] = Color.fromHex(colorMap[i]); + } + return result; + } + + private _updateTheme(): void { + let colorTheme = this._themeService.getColorTheme(); + if (!this.compareTokenRules(colorTheme.tokenColors)) { + return; + } + this._grammarRegistry.setTheme({ name: colorTheme.label, settings: colorTheme.tokenColors }); + let colorMap = TextMateService._toColorMap(this._grammarRegistry.getColorMap()); + let cssRules = generateTokensCSSForColorMap(colorMap); + this._styleElement.innerHTML = cssRules; + TokenizationRegistry.setColorMap(colorMap); + } + + private compareTokenRules(newRules: ITokenColorizationRule[]): boolean { + let currRules = this._currentTokenColors; + this._currentTokenColors = newRules; + if (!newRules || !currRules || newRules.length !== currRules.length) { + return true; + } + for (let i = newRules.length - 1; i >= 0; i--) { + let r1 = newRules[i]; + let r2 = currRules[i]; + if (r1.scope !== r2.scope) { + return true; + } + let s1 = r1.settings; + let s2 = r2.settings; + if (s1 && s2) { + if (s1.fontStyle !== s2.fontStyle || s1.foreground !== s2.foreground) { + return true; + } + } else if (!s1 || !s2) { + return true; + } + } + return false; + } + + + private _handleGrammarExtensionPointUser(extensionFolderPath: string, syntax: ITMSyntaxExtensionPoint, collector: ExtensionMessageCollector): void { + if (syntax.language && ((typeof syntax.language !== 'string') || !this._modeService.isRegisteredMode(syntax.language))) { + collector.error(nls.localize('invalid.language', "Unknown language in `contributes.{0}.language`. Provided value: {1}", grammarsExtPoint.name, String(syntax.language))); + return; + } + if (!syntax.scopeName || (typeof syntax.scopeName !== 'string')) { + collector.error(nls.localize('invalid.scopeName', "Expected string in `contributes.{0}.scopeName`. Provided value: {1}", grammarsExtPoint.name, String(syntax.scopeName))); + return; + } + if (!syntax.path || (typeof syntax.path !== 'string')) { + collector.error(nls.localize('invalid.path.0', "Expected string in `contributes.{0}.path`. Provided value: {1}", grammarsExtPoint.name, String(syntax.path))); + return; + } + if (syntax.injectTo && (!Array.isArray(syntax.injectTo) || syntax.injectTo.some(scope => typeof scope !== 'string'))) { + collector.error(nls.localize('invalid.injectTo', "Invalid value in `contributes.{0}.injectTo`. Must be an array of language scope names. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.injectTo))); + return; + } + if (syntax.embeddedLanguages && !types.isObject(syntax.embeddedLanguages)) { + collector.error(nls.localize('invalid.embeddedLanguages', "Invalid value in `contributes.{0}.embeddedLanguages`. Must be an object map from scope name to language. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.embeddedLanguages))); + return; + } + + let normalizedAbsolutePath = normalize(join(extensionFolderPath, syntax.path)); + + if (normalizedAbsolutePath.indexOf(extensionFolderPath) !== 0) { + collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", grammarsExtPoint.name, normalizedAbsolutePath, extensionFolderPath)); + } + + this._scopeRegistry.register(syntax.scopeName, normalizedAbsolutePath, syntax.embeddedLanguages); + + if (syntax.injectTo) { + for (let injectScope of syntax.injectTo) { + let injections = this._injections[injectScope]; + if (!injections) { + this._injections[injectScope] = injections = []; + } + injections.push(syntax.scopeName); + } + } + + let modeId = syntax.language; + if (modeId) { + this._languageToScope.set(modeId, syntax.scopeName); + } + } + + private _resolveEmbeddedLanguages(embeddedLanguages: IEmbeddedLanguagesMap): IEmbeddedLanguagesMap2 { + let scopes = Object.keys(embeddedLanguages); + let result: IEmbeddedLanguagesMap2 = Object.create(null); + for (let i = 0, len = scopes.length; i < len; i++) { + let scope = scopes[i]; + let language = embeddedLanguages[scope]; + let languageIdentifier = this._modeService.getLanguageIdentifier(language); + if (languageIdentifier) { + result[scope] = languageIdentifier.id; + } + } + return result; + } + + public createGrammar(modeId: string): TPromise { + return this._createGrammar(modeId).then(r => r.grammar); + } + + private _createGrammar(modeId: string): TPromise { + let scopeName = this._languageToScope.get(modeId); + let languageRegistration = this._scopeRegistry.getLanguageRegistration(scopeName); + if (!languageRegistration) { + // No TM grammar defined + return TPromise.wrapError(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); + } + let embeddedLanguages = this._resolveEmbeddedLanguages(languageRegistration.embeddedLanguages); + let languageId = this._modeService.getLanguageIdentifier(modeId).id; + let containsEmbeddedLanguages = (Object.keys(embeddedLanguages).length > 0); + return new TPromise((c, e, p) => { + this._grammarRegistry.loadGrammarWithEmbeddedLanguages(scopeName, languageId, embeddedLanguages, (err, grammar) => { + if (err) { + return e(err); + } + c({ + languageId: languageId, + grammar: grammar, + containsEmbeddedLanguages: containsEmbeddedLanguages + }); + }); + }); + } + + private registerDefinition(modeId: string): void { + this._createGrammar(modeId).then((r) => { + TokenizationRegistry.register(modeId, new TMTokenization(this._scopeRegistry, r.languageId, r.grammar, r.containsEmbeddedLanguages)); + }, onUnexpectedError); + } +} + +class TMTokenization implements ITokenizationSupport { + + private readonly _scopeRegistry: TMScopeRegistry; + private readonly _languageId: LanguageId; + private readonly _grammar: IGrammar; + private readonly _containsEmbeddedLanguages: boolean; + private readonly _seenLanguages: boolean[]; + + constructor(scopeRegistry: TMScopeRegistry, languageId: LanguageId, grammar: IGrammar, containsEmbeddedLanguages: boolean) { + this._scopeRegistry = scopeRegistry; + this._languageId = languageId; + this._grammar = grammar; + this._containsEmbeddedLanguages = containsEmbeddedLanguages; + this._seenLanguages = []; + } + + public getInitialState(): IState { + return INITIAL; + } + + public tokenize(line: string, state: IState, offsetDelta: number): TokenizationResult { + throw new Error('Not supported!'); + } + + public tokenize2(line: string, state: StackElement, offsetDelta: number): TokenizationResult2 { + if (offsetDelta !== 0) { + throw new Error('Unexpected: offsetDelta should be 0.'); + } + + // Do not attempt to tokenize if a line has over 20k + if (line.length >= 20000) { + console.log(`Line (${line.substr(0, 15)}...): longer than 20k characters, tokenization skipped.`); + return nullTokenize2(this._languageId, line, state, offsetDelta); + } + + let textMateResult = this._grammar.tokenizeLine2(line, state); + + if (this._containsEmbeddedLanguages) { + let seenLanguages = this._seenLanguages; + let tokens = textMateResult.tokens; + + // Must check if any of the embedded languages was hit + for (let i = 0, len = (tokens.length >>> 1); i < len; i++) { + let metadata = tokens[(i << 1) + 1]; + let languageId = TokenMetadata.getLanguageId(metadata); + + if (!seenLanguages[languageId]) { + seenLanguages[languageId] = true; + this._scopeRegistry.onEncounteredLanguage(languageId); + } + } + } + + let endState: StackElement; + // try to save an object if possible + if (state.equals(textMateResult.ruleStack)) { + endState = state; + } else { + endState = textMateResult.ruleStack; + + } + + return new TokenizationResult2(textMateResult.tokens, endState); + } +} diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts new file mode 100644 index 0000000000..9c526133aa --- /dev/null +++ b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import Event from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IGrammar } from 'vscode-textmate'; +import { LanguageId } from 'vs/editor/common/modes'; + +export var ITextMateService = createDecorator('textMateService'); + +export interface ITextMateService { + _serviceBrand: any; + + onDidEncounterLanguage: Event; + + createGrammar(modeId: string): TPromise; +} diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts new file mode 100644 index 0000000000..b99af4d8cd --- /dev/null +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -0,0 +1,1076 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as path from 'vs/base/common/paths'; +import nls = require('vs/nls'); +import Event, { Emitter } from 'vs/base/common/event'; +import { TPromise, TValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { guessMimeTypes } from 'vs/base/common/mime'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import URI from 'vs/base/common/uri'; +import * as assert from 'vs/base/common/assert'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import paths = require('vs/base/common/paths'); +import diagnostics = require('vs/base/common/diagnostics'); +import types = require('vs/base/common/types'); +import { IMode } from 'vs/editor/common/modes'; +import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, IRawTextContent } from 'vs/workbench/services/textfile/common/textfiles'; +import { EncodingMode } from 'vs/workbench/common/editor'; +import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; +import { IBackupFileService, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup'; +import { IFileService, IFileStat, FileOperationError, FileOperationResult, IContent, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { anonymize } from 'vs/platform/telemetry/common/telemetryUtils'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { IRawTextSource } from 'vs/editor/common/model/textSource'; + +/** + * The text file editor model listens to changes to its underlying code editor model and saves these changes through the file service back to the disk. + */ +export class TextFileEditorModel extends BaseTextEditorModel implements ITextFileEditorModel { + + public static ID = 'workbench.editors.files.textFileEditorModel'; + + public static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY; + public static DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY = 100; + + private static saveErrorHandler: ISaveErrorHandler; + private static saveParticipant: ISaveParticipant; + + private resource: URI; + private contentEncoding: string; // encoding as reported from disk + private preferredEncoding: string; // encoding as chosen by the user + private dirty: boolean; + private versionId: number; + private bufferSavedVersionId: number; + private lastResolvedDiskStat: IFileStat; + private toDispose: IDisposable[]; + private blockModelContentChange: boolean; + private autoSaveAfterMillies: number; + private autoSaveAfterMilliesEnabled: boolean; + private autoSavePromise: TPromise; + private contentChangeEventScheduler: RunOnceScheduler; + private orphanedChangeEventScheduler: RunOnceScheduler; + private saveSequentializer: SaveSequentializer; + private disposed: boolean; + private lastSaveAttemptTime: number; + private createTextEditorModelPromise: TPromise; + private _onDidContentChange: Emitter; + private _onDidStateChange: Emitter; + + private inConflictMode: boolean; + private inOrphanMode: boolean; + private inErrorMode: boolean; + + constructor( + resource: URI, + preferredEncoding: string, + @IMessageService private messageService: IMessageService, + @IModeService modeService: IModeService, + @IModelService modelService: IModelService, + @IFileService private fileService: IFileService, + @ILifecycleService private lifecycleService: ILifecycleService, + @IInstantiationService private instantiationService: IInstantiationService, + @ITelemetryService private telemetryService: ITelemetryService, + @ITextFileService private textFileService: ITextFileService, + @IBackupFileService private backupFileService: IBackupFileService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + ) { + super(modelService, modeService); + + assert.ok(resource.scheme === 'file', 'TextFileEditorModel can only handle file:// resources.'); + + this.resource = resource; + this.toDispose = []; + this._onDidContentChange = new Emitter(); + this._onDidStateChange = new Emitter(); + this.toDispose.push(this._onDidContentChange); + this.toDispose.push(this._onDidStateChange); + this.preferredEncoding = preferredEncoding; + this.dirty = false; + this.versionId = 0; + this.lastSaveAttemptTime = 0; + this.saveSequentializer = new SaveSequentializer(); + + this.contentChangeEventScheduler = new RunOnceScheduler(() => this._onDidContentChange.fire(StateChange.CONTENT_CHANGE), TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY); + this.toDispose.push(this.contentChangeEventScheduler); + + this.orphanedChangeEventScheduler = new RunOnceScheduler(() => this._onDidStateChange.fire(StateChange.ORPHANED_CHANGE), TextFileEditorModel.DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY); + this.toDispose.push(this.orphanedChangeEventScheduler); + + this.updateAutoSaveConfiguration(textFileService.getAutoSaveConfiguration()); + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); + this.toDispose.push(this.textFileService.onAutoSaveConfigurationChange(config => this.updateAutoSaveConfiguration(config))); + this.toDispose.push(this.textFileService.onFilesAssociationChange(e => this.onFilesAssociationChange())); + this.toDispose.push(this.onDidStateChange(e => { + if (e === StateChange.REVERTED) { + + // Cancel any content change event promises as they are no longer valid. + this.contentChangeEventScheduler.cancel(); + + // Refire state change reverted events as content change events + this._onDidContentChange.fire(StateChange.REVERTED); + } + })); + } + + private onFileChanges(e: FileChangesEvent): void { + + // Track ADD and DELETES for updates of this model to orphan-mode + const modelFileDeleted = e.contains(this.resource, FileChangeType.DELETED); + const modelFileAdded = e.contains(this.resource, FileChangeType.ADDED); + + if (modelFileDeleted || modelFileAdded) { + const newInOrphanModeGuess = modelFileDeleted && !modelFileAdded; + if (this.inOrphanMode !== newInOrphanModeGuess) { + let checkOrphanedPromise: TPromise; + if (newInOrphanModeGuess) { + // We have received reports of users seeing delete events even though the file still + // exists (network shares issue: https://github.com/Microsoft/vscode/issues/13665). + // Since we do not want to mark the model as orphaned, we have to check if the + // file is really gone and not just a faulty file event (TODO@Ben revisit when we + // have a more stable file watcher in place for this scenario). + checkOrphanedPromise = TPromise.timeout(100).then(() => { + if (this.disposed) { + return true; + } + + return this.fileService.existsFile(this.resource).then(exists => !exists); + }); + } else { + checkOrphanedPromise = TPromise.as(false); + } + + checkOrphanedPromise.done(newInOrphanModeValidated => { + if (this.inOrphanMode !== newInOrphanModeValidated && !this.disposed) { + this.setOrphaned(newInOrphanModeValidated); + } + }); + } + } + } + + private setOrphaned(orphaned: boolean): void { + if (this.inOrphanMode !== orphaned) { + this.inOrphanMode = orphaned; + this.orphanedChangeEventScheduler.schedule(); + } + } + + private updateAutoSaveConfiguration(config: IAutoSaveConfiguration): void { + if (typeof config.autoSaveDelay === 'number' && config.autoSaveDelay > 0) { + this.autoSaveAfterMillies = config.autoSaveDelay; + this.autoSaveAfterMilliesEnabled = true; + } else { + this.autoSaveAfterMillies = void 0; + this.autoSaveAfterMilliesEnabled = false; + } + } + + private onFilesAssociationChange(): void { + this.updateTextEditorModelMode(); + } + + private updateTextEditorModelMode(modeId?: string): void { + if (!this.textEditorModel) { + return; + } + + const firstLineText = this.getFirstLineText(this.textEditorModel.getValue()); + const mode = this.getOrCreateMode(this.modeService, modeId, firstLineText); + + this.modelService.setMode(this.textEditorModel, mode); + } + + public get onDidContentChange(): Event { + return this._onDidContentChange.event; + } + + public get onDidStateChange(): Event { + return this._onDidStateChange.event; + } + + /** + * The current version id of the model. + */ + public getVersionId(): number { + return this.versionId; + } + + /** + * Set a save error handler to install code that executes when save errors occur. + */ + public static setSaveErrorHandler(handler: ISaveErrorHandler): void { + TextFileEditorModel.saveErrorHandler = handler; + } + + /** + * Set a save participant handler to react on models getting saved. + */ + public static setSaveParticipant(handler: ISaveParticipant): void { + TextFileEditorModel.saveParticipant = handler; + } + + /** + * Discards any local changes and replaces the model with the contents of the version on disk. + * + * @param if the parameter soft is true, will not attempt to load the contents from disk. + */ + public revert(soft?: boolean): TPromise { + if (!this.isResolved()) { + return TPromise.as(null); + } + + // Cancel any running auto-save + this.cancelAutoSavePromise(); + + // Unset flags + const undo = this.setDirty(false); + + let loadPromise: TPromise; + if (soft) { + loadPromise = TPromise.as(this); + } else { + loadPromise = this.load(true /* force */); + } + + return loadPromise.then(() => { + + // Emit file change event + this._onDidStateChange.fire(StateChange.REVERTED); + }, error => { + + // Set flags back to previous values, we are still dirty if revert failed + undo(); + + return TPromise.wrapError(error); + }); + } + + public load(force?: boolean /* bypass any caches and really go to disk */): TPromise { + diag('load() - enter', this.resource, new Date()); + + // It is very important to not reload the model when the model is dirty. We only want to reload the model from the disk + // if no save is pending to avoid data loss. This might cause a save conflict in case the file has been modified on the disk + // meanwhile, but this is a very low risk. + if (this.dirty) { + diag('load() - exit - without loading because model is dirty', this.resource, new Date()); + + return TPromise.as(this); + } + + // Only for new models we support to load from backup + if (!this.textEditorModel && !this.createTextEditorModelPromise) { + return this.loadWithBackup(force); + } + + // Otherwise load from file resource + return this.loadFromFile(force); + } + + private loadWithBackup(force: boolean): TPromise { + return this.backupFileService.loadBackupResource(this.resource).then(backup => { + + // Make sure meanwhile someone else did not suceed or start loading + if (this.createTextEditorModelPromise || this.textEditorModel) { + return this.createTextEditorModelPromise || TPromise.as(this); + } + + // If we have a backup, continue loading with it + if (!!backup) { + const content: IContent = { + resource: this.resource, + name: paths.basename(this.resource.fsPath), + mtime: Date.now(), + etag: void 0, + value: '', /* will be filled later from backup */ + encoding: this.fileService.getEncoding(this.resource, this.preferredEncoding) + }; + + return this.loadWithContent(content, backup); + } + + // Otherwise load from file + return this.loadFromFile(force); + }); + } + + private loadFromFile(force: boolean): TPromise { + + // Decide on etag + let etag: string; + if (force) { + etag = void 0; // bypass cache if force loading is true + } else if (this.lastResolvedDiskStat) { + etag = this.lastResolvedDiskStat.etag; // otherwise respect etag to support caching + } + + // Resolve Content + return this.textFileService + .resolveTextContent(this.resource, { acceptTextOnly: true, etag, encoding: this.preferredEncoding }) + .then(content => this.handleLoadSuccess(content), error => this.handleLoadError(error)); + } + + private handleLoadSuccess(content: IRawTextContent): TPromise { + + // Clear orphaned state when load was successful + this.setOrphaned(false); + + return this.loadWithContent(content); + } + + private handleLoadError(error: FileOperationError): TPromise { + const result = error.fileOperationResult; + + // Apply orphaned state based on error code + this.setOrphaned(result === FileOperationResult.FILE_NOT_FOUND); + + // NotModified status is expected and can be handled gracefully + if (result === FileOperationResult.FILE_NOT_MODIFIED_SINCE) { + this.setDirty(false); // Ensure we are not tracking a stale state + + return TPromise.as(this); + } + + // Ignore when a model has been resolved once and the file was deleted meanwhile. Since + // we already have the model loaded, we can return to this state and update the orphaned + // flag to indicate that this model has no version on disk anymore. + if (this.isResolved() && result === FileOperationResult.FILE_NOT_FOUND) { + return TPromise.as(this); + } + + // Otherwise bubble up the error + return TPromise.wrapError(error); + } + + private loadWithContent(content: IRawTextContent | IContent, backup?: URI): TPromise { + diag('load() - resolved content', this.resource, new Date()); + + // Telemetry + this.telemetryService.publicLog('fileGet', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: paths.extname(this.resource.fsPath), path: anonymize(this.resource.fsPath) }); + + // Update our resolved disk stat model + const resolvedStat: IFileStat = { + resource: this.resource, + name: content.name, + mtime: content.mtime, + etag: content.etag, + isDirectory: false, + hasChildren: false, + children: void 0, + }; + this.updateLastResolvedDiskStat(resolvedStat); + + // Keep the original encoding to not loose it when saving + const oldEncoding = this.contentEncoding; + this.contentEncoding = content.encoding; + + // Handle events if encoding changed + if (this.preferredEncoding) { + this.updatePreferredEncoding(this.contentEncoding); // make sure to reflect the real encoding of the file (never out of sync) + } else if (oldEncoding !== this.contentEncoding) { + this._onDidStateChange.fire(StateChange.ENCODING); + } + + // Update Existing Model + if (this.textEditorModel) { + return this.doUpdateTextModel(content.value); + } + + // Join an existing request to create the editor model to avoid race conditions + else if (this.createTextEditorModelPromise) { + diag('load() - join existing text editor model promise', this.resource, new Date()); + + return this.createTextEditorModelPromise; + } + + // Create New Model + return this.doCreateTextModel(content.resource, content.value, backup); + } + + private doUpdateTextModel(value: string | IRawTextSource): TPromise { + diag('load() - updated text editor model', this.resource, new Date()); + + // Ensure we are not tracking a stale state + this.setDirty(false); + + // Update model value in a block that ignores model content change events + this.blockModelContentChange = true; + try { + this.updateTextEditorModel(value); + } finally { + this.blockModelContentChange = false; + } + + // Ensure we track the latest saved version ID given that the contents changed + this.updateSavedVersionId(); + + return TPromise.as(this); + } + + private doCreateTextModel(resource: URI, value: string | IRawTextSource, backup: URI): TPromise { + diag('load() - created text editor model', this.resource, new Date()); + + this.createTextEditorModelPromise = this.doLoadBackup(backup).then(backupContent => { + const hasBackupContent = (typeof backupContent === 'string'); + + return this.createTextEditorModel(hasBackupContent ? backupContent : value, resource).then(() => { + this.createTextEditorModelPromise = null; + + // We restored a backup so we have to set the model as being dirty + // We also want to trigger auto save if it is enabled to simulate the exact same behaviour + // you would get if manually making the model dirty (fixes https://github.com/Microsoft/vscode/issues/16977) + if (hasBackupContent) { + this.makeDirty(); + if (this.autoSaveAfterMilliesEnabled) { + this.doAutoSave(this.versionId); + } + } + + // Ensure we are not tracking a stale state + else { + this.setDirty(false); + } + + // See https://github.com/Microsoft/vscode/issues/30189 + // This code has been extracted to a different method because it caused a memory leak + // where `value` was captured in the content change listener closure scope. + this._installChangeContentListener(); + + return this; + }, error => { + this.createTextEditorModelPromise = null; + + return TPromise.wrapError(error); + }); + }); + + return this.createTextEditorModelPromise; + } + + private _installChangeContentListener(): void { + // See https://github.com/Microsoft/vscode/issues/30189 + // This code has been extracted to a different method because it caused a memory leak + // where `value` was captured in the content change listener closure scope. + this.toDispose.push(this.textEditorModel.onDidChangeContent(() => { + this.onModelContentChanged(); + })); + } + + private doLoadBackup(backup: URI): TPromise { + if (!backup) { + return TPromise.as(null); + } + + return this.textFileService.resolveTextContent(backup, BACKUP_FILE_RESOLVE_OPTIONS).then(backup => { + return this.backupFileService.parseBackupContent(backup.value); + }, error => null /* ignore errors */); + } + + protected getOrCreateMode(modeService: IModeService, preferredModeIds: string, firstLineText?: string): TPromise { + return modeService.getOrCreateModeByFilenameOrFirstLine(this.resource.fsPath, firstLineText); + } + + private onModelContentChanged(): void { + diag(`onModelContentChanged() - enter`, this.resource, new Date()); + + // In any case increment the version id because it tracks the textual content state of the model at all times + this.versionId++; + diag(`onModelContentChanged() - new versionId ${this.versionId}`, this.resource, new Date()); + + // Ignore if blocking model changes + if (this.blockModelContentChange) { + return; + } + + // The contents changed as a matter of Undo and the version reached matches the saved one + // In this case we clear the dirty flag and emit a SAVED event to indicate this state. + // Note: we currently only do this check when auto-save is turned off because there you see + // a dirty indicator that you want to get rid of when undoing to the saved version. + if (!this.autoSaveAfterMilliesEnabled && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) { + diag('onModelContentChanged() - model content changed back to last saved version', this.resource, new Date()); + + // Clear flags + const wasDirty = this.dirty; + this.setDirty(false); + + // Emit event + if (wasDirty) { + this._onDidStateChange.fire(StateChange.REVERTED); + } + + return; + } + + diag('onModelContentChanged() - model content changed and marked as dirty', this.resource, new Date()); + + // Mark as dirty + this.makeDirty(); + + // Start auto save process unless we are in conflict resolution mode and unless it is disabled + if (this.autoSaveAfterMilliesEnabled) { + if (!this.inConflictMode) { + this.doAutoSave(this.versionId); + } else { + diag('makeDirty() - prevented save because we are in conflict resolution mode', this.resource, new Date()); + } + } + + // Handle content change events + this.contentChangeEventScheduler.schedule(); + } + + private makeDirty(): void { + + // Track dirty state and version id + const wasDirty = this.dirty; + this.setDirty(true); + + // Emit as Event if we turned dirty + if (!wasDirty) { + this._onDidStateChange.fire(StateChange.DIRTY); + } + } + + private doAutoSave(versionId: number): TPromise { + diag(`doAutoSave() - enter for versionId ${versionId}`, this.resource, new Date()); + + // Cancel any currently running auto saves to make this the one that succeeds + this.cancelAutoSavePromise(); + + // Create new save promise and keep it + this.autoSavePromise = TPromise.timeout(this.autoSaveAfterMillies).then(() => { + + // Only trigger save if the version id has not changed meanwhile + if (versionId === this.versionId) { + this.doSave(versionId, { reason: SaveReason.AUTO }).done(null, onUnexpectedError); // Very important here to not return the promise because if the timeout promise is canceled it will bubble up the error otherwise - do not change + } + }); + + return this.autoSavePromise; + } + + private cancelAutoSavePromise(): void { + if (this.autoSavePromise) { + this.autoSavePromise.cancel(); + this.autoSavePromise = void 0; + } + } + + /** + * Saves the current versionId of this editor model if it is dirty. + */ + public save(options: ISaveOptions = Object.create(null)): TPromise { + if (!this.isResolved()) { + return TPromise.as(null); + } + + diag('save() - enter', this.resource, new Date()); + + // Cancel any currently running auto saves to make this the one that succeeds + this.cancelAutoSavePromise(); + + return this.doSave(this.versionId, options); + } + + private doSave(versionId: number, options: ISaveOptions): TPromise { + if (types.isUndefinedOrNull(options.reason)) { + options.reason = SaveReason.EXPLICIT; + } + + diag(`doSave(${versionId}) - enter with versionId ' + versionId`, this.resource, new Date()); + + // Lookup any running pending save for this versionId and return it if found + // + // Scenario: user invoked the save action multiple times quickly for the same contents + // while the save was not yet finished to disk + // + if (this.saveSequentializer.hasPendingSave(versionId)) { + diag(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource, new Date()); + + return this.saveSequentializer.pendingSave; + } + + // Return early if not dirty (unless forced) or version changed meanwhile + // + // Scenario A: user invoked save action even though the model is not dirty + // Scenario B: auto save was triggered for a certain change by the user but meanwhile the user changed + // the contents and the version for which auto save was started is no longer the latest. + // Thus we avoid spawning multiple auto saves and only take the latest. + // + if ((!options.force && !this.dirty) || versionId !== this.versionId) { + diag(`doSave(${versionId}) - exit - because not dirty and/or versionId is different (this.isDirty: ${this.dirty}, this.versionId: ${this.versionId})`, this.resource, new Date()); + + return TPromise.as(null); + } + + // Return if currently saving by storing this save request as the next save that should happen. + // Never ever must 2 saves execute at the same time because this can lead to dirty writes and race conditions. + // + // Scenario A: auto save was triggered and is currently busy saving to disk. this takes long enough that another auto save + // kicks in. + // Scenario B: save is very slow (e.g. network share) and the user manages to change the buffer and trigger another save + // while the first save has not returned yet. + // + if (this.saveSequentializer.hasPendingSave()) { + diag(`doSave(${versionId}) - exit - because busy saving`, this.resource, new Date()); + + // Register this as the next upcoming save and return + return this.saveSequentializer.setNext(() => this.doSave(this.versionId /* make sure to use latest version id here */, options)); + } + + // Push all edit operations to the undo stack so that the user has a chance to + // Ctrl+Z back to the saved version. We only do this when auto-save is turned off + if (!this.autoSaveAfterMilliesEnabled) { + this.textEditorModel.pushStackElement(); + } + + // A save participant can still change the model now and since we are so close to saving + // we do not want to trigger another auto save or similar, so we block this + // In addition we update our version right after in case it changed because of a model change + // We DO NOT run any save participant if we are in the shutdown phase and files are being + // saved as a result of that. + // Save participants can also be skipped through API. + let saveParticipantPromise = TPromise.as(versionId); + if (TextFileEditorModel.saveParticipant && this.lifecycleService.phase !== LifecyclePhase.ShuttingDown && !options.skipSaveParticipants) { + const onCompleteOrError = () => { + this.blockModelContentChange = false; + + return this.versionId; + }; + + saveParticipantPromise = TPromise.as(undefined).then(() => { + this.blockModelContentChange = true; + + return TextFileEditorModel.saveParticipant.participate(this, { reason: options.reason }); + }).then(onCompleteOrError, onCompleteOrError); + } + + // mark the save participant as current pending save operation + return this.saveSequentializer.setPending(versionId, saveParticipantPromise.then(newVersionId => { + + // the model was not dirty and no save participant changed the contents, so we do not have + // to write the contents to disk, as they are already on disk. we still want to trigger + // a change on the file though so that external file watchers can be notified + if (options.force && !this.dirty && options.reason === SaveReason.EXPLICIT && versionId === newVersionId) { + return this.doTouch(); + } + + // update versionId with its new value (if pre-save changes happened) + versionId = newVersionId; + + // Clear error flag since we are trying to save again + this.inErrorMode = false; + + // Remember when this model was saved last + this.lastSaveAttemptTime = Date.now(); + + // Save to Disk + // mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering) + diag(`doSave(${versionId}) - before updateContent()`, this.resource, new Date()); + return this.saveSequentializer.setPending(newVersionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, this.getValue(), { + overwriteReadonly: options.overwriteReadonly, + overwriteEncoding: options.overwriteEncoding, + mtime: this.lastResolvedDiskStat.mtime, + encoding: this.getEncoding(), + etag: this.lastResolvedDiskStat.etag + }).then(stat => { + diag(`doSave(${versionId}) - after updateContent()`, this.resource, new Date()); + + // Telemetry + if (this.isSettingsFile()) { + this.telemetryService.publicLog('settingsWritten'); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data + } else { + this.telemetryService.publicLog('filePUT', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: paths.extname(this.lastResolvedDiskStat.resource.fsPath) }); + } + + // Update dirty state unless model has changed meanwhile + if (versionId === this.versionId) { + diag(`doSave(${versionId}) - setting dirty to false because versionId did not change`, this.resource, new Date()); + this.setDirty(false); + } else { + diag(`doSave(${versionId}) - not setting dirty to false because versionId did change meanwhile`, this.resource, new Date()); + } + + // Updated resolved stat with updated stat + this.updateLastResolvedDiskStat(stat); + + // Cancel any content change event promises as they are no longer valid + this.contentChangeEventScheduler.cancel(); + + // Emit File Saved Event + this._onDidStateChange.fire(StateChange.SAVED); + }, error => { + diag(`doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource, new Date()); + + // Flag as error state in the model + this.inErrorMode = true; + + // Look out for a save conflict + if ((error).fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE) { + this.inConflictMode = true; + } + + // Show to user + this.onSaveError(error); + + // Emit as event + this._onDidStateChange.fire(StateChange.SAVE_ERROR); + })); + })); + } + + private isSettingsFile(): boolean { + + // Check for global settings file + if (this.resource.fsPath === this.environmentService.appSettingsPath) { + return true; + } + + // Check for workspace settings file + if (this.contextService.hasWorkspace()) { + return this.contextService.getWorkspace().roots.some(root => { + // {{SQL CARBON EDIT}} + return paths.isEqualOrParent(this.resource.fsPath, path.join(root.fsPath, '.sqlops')); + }); + } + + return false; + } + + private doTouch(): TPromise { + return this.fileService.touchFile(this.resource).then(stat => { + + // Updated resolved stat with updated stat since touching it might have changed mtime + this.updateLastResolvedDiskStat(stat); + }, () => void 0 /* gracefully ignore errors if just touching */); + } + + private setDirty(dirty: boolean): () => void { + const wasDirty = this.dirty; + const wasInConflictMode = this.inConflictMode; + const wasInErrorMode = this.inErrorMode; + const oldBufferSavedVersionId = this.bufferSavedVersionId; + + if (!dirty) { + this.dirty = false; + this.inConflictMode = false; + this.inErrorMode = false; + this.updateSavedVersionId(); + } else { + this.dirty = true; + } + + // Return function to revert this call + return () => { + this.dirty = wasDirty; + this.inConflictMode = wasInConflictMode; + this.inErrorMode = wasInErrorMode; + this.bufferSavedVersionId = oldBufferSavedVersionId; + }; + } + + private updateSavedVersionId(): void { + // we remember the models alternate version id to remember when the version + // of the model matches with the saved version on disk. we need to keep this + // in order to find out if the model changed back to a saved version (e.g. + // when undoing long enough to reach to a version that is saved and then to + // clear the dirty flag) + if (this.textEditorModel) { + this.bufferSavedVersionId = this.textEditorModel.getAlternativeVersionId(); + } + } + + private updateLastResolvedDiskStat(newVersionOnDiskStat: IFileStat): void { + + // First resolve - just take + if (!this.lastResolvedDiskStat) { + this.lastResolvedDiskStat = newVersionOnDiskStat; + } + + // Subsequent resolve - make sure that we only assign it if the mtime is equal or has advanced. + // This is essential a If-Modified-Since check on the client ot prevent race conditions from loading + // and saving. If a save comes in late after a revert was called, the mtime could be out of sync. + else if (this.lastResolvedDiskStat.mtime <= newVersionOnDiskStat.mtime) { + this.lastResolvedDiskStat = newVersionOnDiskStat; + } + } + + private onSaveError(error: any): void { + + // Prepare handler + if (!TextFileEditorModel.saveErrorHandler) { + TextFileEditorModel.setSaveErrorHandler(this.instantiationService.createInstance(DefaultSaveErrorHandler)); + } + + // Handle + TextFileEditorModel.saveErrorHandler.onSaveError(error, this); + } + + /** + * Returns true if the content of this model has changes that are not yet saved back to the disk. + */ + public isDirty(): boolean { + return this.dirty; + } + + /** + * Returns the time in millies when this working copy was attempted to be saved. + */ + public getLastSaveAttemptTime(): number { + return this.lastSaveAttemptTime; + } + + /** + * Returns the time in millies when this working copy was last modified by the user or some other program. + */ + public getETag(): string { + return this.lastResolvedDiskStat ? this.lastResolvedDiskStat.etag : null; + } + + /** + * Answers if this model is in a specific state. + */ + public hasState(state: ModelState): boolean { + switch (state) { + case ModelState.CONFLICT: + return this.inConflictMode; + case ModelState.DIRTY: + return this.dirty; + case ModelState.ERROR: + return this.inErrorMode; + case ModelState.ORPHAN: + return this.inOrphanMode; + case ModelState.PENDING_SAVE: + return this.saveSequentializer.hasPendingSave(); + case ModelState.SAVED: + return !this.dirty; + } + } + + public getEncoding(): string { + return this.preferredEncoding || this.contentEncoding; + } + + public setEncoding(encoding: string, mode: EncodingMode): void { + if (!this.isNewEncoding(encoding)) { + return; // return early if the encoding is already the same + } + + // Encode: Save with encoding + if (mode === EncodingMode.Encode) { + this.updatePreferredEncoding(encoding); + + // Save + if (!this.isDirty()) { + this.versionId++; // needs to increment because we change the model potentially + this.makeDirty(); + } + + if (!this.inConflictMode) { + this.save({ overwriteEncoding: true }).done(null, onUnexpectedError); + } + } + + // Decode: Load with encoding + else { + if (this.isDirty()) { + this.messageService.show(Severity.Info, nls.localize('saveFileFirst', "The file is dirty. Please save it first before reopening it with another encoding.")); + + return; + } + + this.updatePreferredEncoding(encoding); + + // Load + this.load(true /* force because encoding has changed */).done(null, onUnexpectedError); + } + } + + public updatePreferredEncoding(encoding: string): void { + if (!this.isNewEncoding(encoding)) { + return; + } + + this.preferredEncoding = encoding; + + // Emit + this._onDidStateChange.fire(StateChange.ENCODING); + } + + private isNewEncoding(encoding: string): boolean { + if (this.preferredEncoding === encoding) { + return false; // return early if the encoding is already the same + } + + if (!this.preferredEncoding && this.contentEncoding === encoding) { + return false; // also return if we don't have a preferred encoding but the content encoding is already the same + } + + return true; + } + + public isResolved(): boolean { + return !types.isUndefinedOrNull(this.lastResolvedDiskStat); + } + + /** + * Returns true if the dispose() method of this model has been called. + */ + public isDisposed(): boolean { + return this.disposed; + } + + /** + * Returns the full resource URI of the file this text file editor model is about. + */ + public getResource(): URI { + return this.resource; + } + + /** + * Stat accessor only used by tests. + */ + public getStat(): IFileStat { + return this.lastResolvedDiskStat; + } + + public dispose(): void { + this.disposed = true; + this.inConflictMode = false; + this.inOrphanMode = false; + this.inErrorMode = false; + + this.toDispose = dispose(this.toDispose); + this.createTextEditorModelPromise = null; + + this.cancelAutoSavePromise(); + + super.dispose(); + } +} + +interface IPendingSave { + versionId: number; + promise: TPromise; +} + +interface ISaveOperation { + promise: TPromise; + promiseValue: TValueCallback; + promiseError: ErrorCallback; + run: () => TPromise; +} + +export class SaveSequentializer { + private _pendingSave: IPendingSave; + private _nextSave: ISaveOperation; + + public hasPendingSave(versionId?: number): boolean { + if (!this._pendingSave) { + return false; + } + + if (typeof versionId === 'number') { + return this._pendingSave.versionId === versionId; + } + + return !!this._pendingSave; + } + + public get pendingSave(): TPromise { + return this._pendingSave ? this._pendingSave.promise : void 0; + } + + public setPending(versionId: number, promise: TPromise): TPromise { + this._pendingSave = { versionId, promise }; + + promise.done(() => this.donePending(versionId), () => this.donePending(versionId)); + + return promise; + } + + private donePending(versionId: number): void { + if (this._pendingSave && versionId === this._pendingSave.versionId) { + + // only set pending to done if the promise finished that is associated with that versionId + this._pendingSave = void 0; + + // schedule the next save now that we are free if we have any + this.triggerNextSave(); + } + } + + private triggerNextSave(): void { + if (this._nextSave) { + const saveOperation = this._nextSave; + this._nextSave = void 0; + + // Run next save and complete on the associated promise + saveOperation.run().done(saveOperation.promiseValue, saveOperation.promiseError); + } + } + + public setNext(run: () => TPromise): TPromise { + + // this is our first next save, so we create associated promise with it + // so that we can return a promise that completes when the save operation + // has completed. + if (!this._nextSave) { + let promiseValue: TValueCallback; + let promiseError: ErrorCallback; + const promise = new TPromise((c, e) => { + promiseValue = c; + promiseError = e; + }); + + this._nextSave = { + run, + promise, + promiseValue, + promiseError + }; + } + + // we have a previous next save, just overwrite it + else { + this._nextSave.run = run; + } + + return this._nextSave.promise; + } +} + +class DefaultSaveErrorHandler implements ISaveErrorHandler { + + constructor( @IMessageService private messageService: IMessageService) { } + + public onSaveError(error: any, model: TextFileEditorModel): void { + this.messageService.show(Severity.Error, nls.localize('genericSaveError', "Failed to save '{0}': {1}", paths.basename(model.getResource().fsPath), toErrorMessage(error, false))); + } +} + +// Diagnostics support +let diag: (...args: any[]) => void; +if (!diag) { + diag = diagnostics.register('TextFileEditorModelDiagnostics', function (...args: any[]) { + console.log(args[1] + ' - ' + args[0] + ' (time: ' + args[2].getTime() + ' [' + args[2].toUTCString() + '])'); + }); +} diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts new file mode 100644 index 0000000000..d99cab1b39 --- /dev/null +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -0,0 +1,348 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event, { Emitter, debounceEvent } from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { ITextFileEditorModel, ITextFileEditorModelManager, TextFileModelChangeEvent, StateChange, IModelLoadOrCreateOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ResourceMap } from 'vs/base/common/map'; + +export class TextFileEditorModelManager implements ITextFileEditorModelManager { + private toUnbind: IDisposable[]; + + private _onModelDisposed: Emitter; + private _onModelContentChanged: Emitter; + private _onModelDirty: Emitter; + private _onModelSaveError: Emitter; + private _onModelSaved: Emitter; + private _onModelReverted: Emitter; + private _onModelEncodingChanged: Emitter; + private _onModelOrphanedChanged: Emitter; + + private _onModelsDirtyEvent: Event; + private _onModelsSaveError: Event; + private _onModelsSaved: Event; + private _onModelsReverted: Event; + + private mapResourceToDisposeListener: ResourceMap; + private mapResourceToStateChangeListener: ResourceMap; + private mapResourceToModelContentChangeListener: ResourceMap; + private mapResourceToModel: ResourceMap; + private mapResourceToPendingModelLoaders: ResourceMap>; + + constructor( + @ILifecycleService private lifecycleService: ILifecycleService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + this.toUnbind = []; + + this._onModelDisposed = new Emitter(); + this._onModelContentChanged = new Emitter(); + this._onModelDirty = new Emitter(); + this._onModelSaveError = new Emitter(); + this._onModelSaved = new Emitter(); + this._onModelReverted = new Emitter(); + this._onModelEncodingChanged = new Emitter(); + this._onModelOrphanedChanged = new Emitter(); + + this.toUnbind.push(this._onModelDisposed); + this.toUnbind.push(this._onModelContentChanged); + this.toUnbind.push(this._onModelDirty); + this.toUnbind.push(this._onModelSaveError); + this.toUnbind.push(this._onModelSaved); + this.toUnbind.push(this._onModelReverted); + this.toUnbind.push(this._onModelEncodingChanged); + this.toUnbind.push(this._onModelOrphanedChanged); + + this.mapResourceToModel = new ResourceMap(); + this.mapResourceToDisposeListener = new ResourceMap(); + this.mapResourceToStateChangeListener = new ResourceMap(); + this.mapResourceToModelContentChangeListener = new ResourceMap(); + this.mapResourceToPendingModelLoaders = new ResourceMap>(); + + this.registerListeners(); + } + + private registerListeners(): void { + + // Lifecycle + this.lifecycleService.onShutdown(this.dispose, this); + } + + public get onModelDisposed(): Event { + return this._onModelDisposed.event; + } + + public get onModelContentChanged(): Event { + return this._onModelContentChanged.event; + } + + public get onModelDirty(): Event { + return this._onModelDirty.event; + } + + public get onModelSaveError(): Event { + return this._onModelSaveError.event; + } + + public get onModelSaved(): Event { + return this._onModelSaved.event; + } + + public get onModelReverted(): Event { + return this._onModelReverted.event; + } + + public get onModelEncodingChanged(): Event { + return this._onModelEncodingChanged.event; + } + + public get onModelOrphanedChanged(): Event { + return this._onModelOrphanedChanged.event; + } + + public get onModelsDirty(): Event { + if (!this._onModelsDirtyEvent) { + this._onModelsDirtyEvent = this.debounce(this.onModelDirty); + } + + return this._onModelsDirtyEvent; + } + + public get onModelsSaveError(): Event { + if (!this._onModelsSaveError) { + this._onModelsSaveError = this.debounce(this.onModelSaveError); + } + + return this._onModelsSaveError; + } + + public get onModelsSaved(): Event { + if (!this._onModelsSaved) { + this._onModelsSaved = this.debounce(this.onModelSaved); + } + + return this._onModelsSaved; + } + + public get onModelsReverted(): Event { + if (!this._onModelsReverted) { + this._onModelsReverted = this.debounce(this.onModelReverted); + } + + return this._onModelsReverted; + } + + private debounce(event: Event): Event { + return debounceEvent(event, (prev: TextFileModelChangeEvent[], cur: TextFileModelChangeEvent) => { + if (!prev) { + prev = [cur]; + } else { + prev.push(cur); + } + return prev; + }, this.debounceDelay()); + } + + protected debounceDelay(): number { + return 250; + } + + public get(resource: URI): ITextFileEditorModel { + return this.mapResourceToModel.get(resource); + } + + public loadOrCreate(resource: URI, options?: IModelLoadOrCreateOptions): TPromise { + + // Return early if model is currently being loaded + const pendingLoad = this.mapResourceToPendingModelLoaders.get(resource); + if (pendingLoad) { + return pendingLoad; + } + + let modelPromise: TPromise; + + // Model exists + let model = this.get(resource); + if (model) { + if (!options || !options.reload) { + modelPromise = TPromise.as(model); + } else { + modelPromise = model.load(); + } + } + + // Model does not exist + else { + model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : void 0); + modelPromise = model.load(); + + // Install state change listener + this.mapResourceToStateChangeListener.set(resource, model.onDidStateChange(state => { + const event = new TextFileModelChangeEvent(model, state); + switch (state) { + case StateChange.DIRTY: + this._onModelDirty.fire(event); + break; + case StateChange.SAVE_ERROR: + this._onModelSaveError.fire(event); + break; + case StateChange.SAVED: + this._onModelSaved.fire(event); + break; + case StateChange.REVERTED: + this._onModelReverted.fire(event); + break; + case StateChange.ENCODING: + this._onModelEncodingChanged.fire(event); + break; + case StateChange.ORPHANED_CHANGE: + this._onModelOrphanedChanged.fire(event); + break; + } + })); + + // Install model content change listener + this.mapResourceToModelContentChangeListener.set(resource, model.onDidContentChange(e => { + this._onModelContentChanged.fire(new TextFileModelChangeEvent(model, e)); + })); + } + + // Store pending loads to avoid race conditions + this.mapResourceToPendingModelLoaders.set(resource, modelPromise); + + return modelPromise.then(model => { + + // Make known to manager (if not already known) + this.add(resource, model); + + // Model can be dirty if a backup was restored, so we make sure to have this event delivered + if (model.isDirty()) { + this._onModelDirty.fire(new TextFileModelChangeEvent(model, StateChange.DIRTY)); + } + + // Remove from pending loads + this.mapResourceToPendingModelLoaders.delete(resource); + + return model; + }, error => { + + // Free resources of this invalid model + model.dispose(); + + // Remove from pending loads + this.mapResourceToPendingModelLoaders.delete(resource); + + return TPromise.wrapError(error); + }); + } + + public getAll(resource?: URI, filter?: (model: ITextFileEditorModel) => boolean): ITextFileEditorModel[] { + if (resource) { + const res = this.mapResourceToModel.get(resource); + + return res ? [res] : []; + } + + const res: ITextFileEditorModel[] = []; + this.mapResourceToModel.forEach(model => { + if (!filter || filter(model)) { + res.push(model); + } + }); + + return res; + } + + public add(resource: URI, model: ITextFileEditorModel): void { + const knownModel = this.mapResourceToModel.get(resource); + if (knownModel === model) { + return; // already cached + } + + // dispose any previously stored dispose listener for this resource + const disposeListener = this.mapResourceToDisposeListener.get(resource); + if (disposeListener) { + disposeListener.dispose(); + } + + // store in cache but remove when model gets disposed + this.mapResourceToModel.set(resource, model); + this.mapResourceToDisposeListener.set(resource, model.onDispose(() => { + this.remove(resource); + this._onModelDisposed.fire(resource); + })); + } + + public remove(resource: URI): void { + this.mapResourceToModel.delete(resource); + + const disposeListener = this.mapResourceToDisposeListener.get(resource); + if (disposeListener) { + dispose(disposeListener); + this.mapResourceToDisposeListener.delete(resource); + } + + const stateChangeListener = this.mapResourceToStateChangeListener.get(resource); + if (stateChangeListener) { + dispose(stateChangeListener); + this.mapResourceToStateChangeListener.delete(resource); + } + + const modelContentChangeListener = this.mapResourceToModelContentChangeListener.get(resource); + if (modelContentChangeListener) { + dispose(modelContentChangeListener); + this.mapResourceToModelContentChangeListener.delete(resource); + } + } + + public clear(): void { + + // model caches + this.mapResourceToModel.clear(); + this.mapResourceToPendingModelLoaders.clear(); + + // dispose dispose listeners + this.mapResourceToDisposeListener.forEach(l => l.dispose()); + this.mapResourceToDisposeListener.clear(); + + // dispose state change listeners + this.mapResourceToStateChangeListener.forEach(l => l.dispose()); + this.mapResourceToStateChangeListener.clear(); + + // dispose model content change listeners + this.mapResourceToModelContentChangeListener.forEach(l => l.dispose()); + this.mapResourceToModelContentChangeListener.clear(); + } + + public disposeModel(model: TextFileEditorModel): void { + if (!model) { + return; // we need data! + } + + if (model.isDisposed()) { + return; // already disposed + } + + if (this.mapResourceToPendingModelLoaders.has(model.getResource())) { + return; // not yet loaded + } + + if (model.isDirty()) { + return; // not saved + } + + model.dispose(); + } + + public dispose(): void { + this.toUnbind = dispose(this.toUnbind); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts new file mode 100644 index 0000000000..3a49ad4c19 --- /dev/null +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -0,0 +1,717 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import paths = require('vs/base/common/paths'); +import errors = require('vs/base/common/errors'); +import objects = require('vs/base/common/objects'); +import Event, { Emitter } from 'vs/base/common/event'; +import platform = require('vs/base/common/platform'); +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IRevertOptions, IResult, ITextFileOperationResult, ITextFileService, IRawTextContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { ConfirmResult } from 'vs/workbench/common/editor'; +import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IFileService, IResolveContentOptions, IFilesConfiguration, FileOperationError, FileOperationResult, AutoSaveConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { ResourceMap } from 'vs/base/common/map'; +import { Schemas } from 'vs/base/common/network'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; + +export interface IBackupResult { + didBackup: boolean; +} + +/** + * The workbench file service implementation implements the raw file service spec and adds additional methods on top. + * + * It also adds diagnostics and logging around file system operations. + */ +export abstract class TextFileService implements ITextFileService { + + public _serviceBrand: any; + + private toUnbind: IDisposable[]; + private _models: TextFileEditorModelManager; + + private _onFilesAssociationChange: Emitter; + private currentFilesAssociationConfig: { [key: string]: string; }; + + private _onAutoSaveConfigurationChange: Emitter; + private configuredAutoSaveDelay: number; + private configuredAutoSaveOnFocusChange: boolean; + private configuredAutoSaveOnWindowChange: boolean; + + private configuredHotExit: string; + + constructor( + private lifecycleService: ILifecycleService, + private contextService: IWorkspaceContextService, + private configurationService: IConfigurationService, + private telemetryService: ITelemetryService, + protected fileService: IFileService, + private untitledEditorService: IUntitledEditorService, + private instantiationService: IInstantiationService, + private messageService: IMessageService, + protected environmentService: IEnvironmentService, + private backupFileService: IBackupFileService, + private windowsService: IWindowsService, + private historyService: IHistoryService + ) { + this.toUnbind = []; + + this._onAutoSaveConfigurationChange = new Emitter(); + this.toUnbind.push(this._onAutoSaveConfigurationChange); + + this._onFilesAssociationChange = new Emitter(); + this.toUnbind.push(this._onFilesAssociationChange); + + this._models = this.instantiationService.createInstance(TextFileEditorModelManager); + + const configuration = this.configurationService.getConfiguration(); + this.currentFilesAssociationConfig = configuration && configuration.files && configuration.files.associations; + + this.onConfigurationChange(configuration); + + this.telemetryService.publicLog('autoSave', this.getAutoSaveConfiguration()); + + this.registerListeners(); + } + + public get models(): ITextFileEditorModelManager { + return this._models; + } + + abstract resolveTextContent(resource: URI, options?: IResolveContentOptions): TPromise; + + abstract promptForPath(defaultPath?: string): string; + + abstract confirmSave(resources?: URI[]): ConfirmResult; + + public get onAutoSaveConfigurationChange(): Event { + return this._onAutoSaveConfigurationChange.event; + } + + public get onFilesAssociationChange(): Event { + return this._onFilesAssociationChange.event; + } + + private registerListeners(): void { + + // Lifecycle + this.lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown(event.reason))); + this.lifecycleService.onShutdown(this.dispose, this); + + // Configuration changes + this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration()))); + } + + private beforeShutdown(reason: ShutdownReason): boolean | TPromise { + + // Dirty files need treatment on shutdown + const dirty = this.getDirty(); + if (dirty.length) { + + // If auto save is enabled, save all files and then check again for dirty files + let handleAutoSave: TPromise; + if (this.getAutoSaveMode() !== AutoSaveMode.OFF) { + handleAutoSave = this.saveAll(false /* files only */).then(() => this.getDirty()); + } else { + handleAutoSave = TPromise.as(dirty); + } + + return handleAutoSave.then(dirty => { + + // If we still have dirty files, we either have untitled ones or files that cannot be saved + // or auto save was not enabled and as such we did not save any dirty files to disk automatically + if (dirty.length) { + + // If hot exit is enabled, backup dirty files and allow to exit without confirmation + if (this.isHotExitEnabled) { + return this.backupBeforeShutdown(dirty, this.models, reason).then(result => { + if (result.didBackup) { + return this.noVeto({ cleanUpBackups: false }); // no veto and no backup cleanup (since backup was successful) + } + + // since a backup did not happen, we have to confirm for the dirty files now + return this.confirmBeforeShutdown(); + }, errors => { + const firstError = errors[0]; + this.messageService.show(Severity.Error, nls.localize('files.backup.failSave', "Files could not be backed up (Error: {0}), try saving your files to exit.", firstError.message)); + + return true; // veto, the backups failed + }); + } + + // Otherwise just confirm from the user what to do with the dirty files + return this.confirmBeforeShutdown(); + } + + return void 0; + }); + } + + // No dirty files: no veto + return this.noVeto({ cleanUpBackups: true }); + } + + private backupBeforeShutdown(dirtyToBackup: URI[], textFileEditorModelManager: ITextFileEditorModelManager, reason: ShutdownReason): TPromise { + return this.windowsService.getWindowCount().then(windowCount => { + + // When quit is requested skip the confirm callback and attempt to backup all workspaces. + // When quit is not requested the confirm callback should be shown when the window being + // closed is the only VS Code window open, except for on Mac where hot exit is only + // ever activated when quit is requested. + + let doBackup: boolean; + switch (reason) { + case ShutdownReason.CLOSE: + if (this.contextService.hasWorkspace() && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) { + doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured + } else if (windowCount > 1 || platform.isMacintosh) { + doBackup = false; // do not backup if a window is closed that does not cause quitting of the application + } else { + doBackup = true; // backup if last window is closed on win/linux where the application quits right after + } + break; + + case ShutdownReason.QUIT: + doBackup = true; // backup because next start we restore all backups + break; + + case ShutdownReason.RELOAD: + doBackup = true; // backup because after window reload, backups restore + break; + + case ShutdownReason.LOAD: + if (this.contextService.hasWorkspace() && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) { + doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured + } else { + doBackup = false; // do not backup because we are switching contexts + } + break; + } + + if (!doBackup) { + return TPromise.as({ didBackup: false }); + } + + // Telemetry + this.telemetryService.publicLog('hotExit:triggered', { reason, windowCount, fileCount: dirtyToBackup.length }); + + // Backup + return this.backupAll(dirtyToBackup, textFileEditorModelManager).then(() => { return { didBackup: true }; }); + }); + } + + private backupAll(dirtyToBackup: URI[], textFileEditorModelManager: ITextFileEditorModelManager): TPromise { + + // split up between files and untitled + const filesToBackup: ITextFileEditorModel[] = []; + const untitledToBackup: URI[] = []; + dirtyToBackup.forEach(s => { + if (s.scheme === Schemas.file) { + filesToBackup.push(textFileEditorModelManager.get(s)); + } else if (s.scheme === UNTITLED_SCHEMA) { + untitledToBackup.push(s); + } + }); + + return this.doBackupAll(filesToBackup, untitledToBackup); + } + + private doBackupAll(dirtyFileModels: ITextFileEditorModel[], untitledResources: URI[]): TPromise { + + // Handle file resources first + return TPromise.join(dirtyFileModels.map(model => this.backupFileService.backupResource(model.getResource(), model.getValue(), model.getVersionId()))).then(results => { + + // Handle untitled resources + const untitledModelPromises = untitledResources + .filter(untitled => this.untitledEditorService.exists(untitled)) + .map(untitled => this.untitledEditorService.loadOrCreate({ resource: untitled })); + + return TPromise.join(untitledModelPromises).then(untitledModels => { + const untitledBackupPromises = untitledModels.map(model => { + return this.backupFileService.backupResource(model.getResource(), model.getValue(), model.getVersionId()); + }); + + return TPromise.join(untitledBackupPromises).then(() => void 0); + }); + }); + } + + private confirmBeforeShutdown(): boolean | TPromise { + const confirm = this.confirmSave(); + + // Save + if (confirm === ConfirmResult.SAVE) { + return this.saveAll(true /* includeUntitled */).then(result => { + if (result.results.some(r => !r.success)) { + return true; // veto if some saves failed + } + + return this.noVeto({ cleanUpBackups: true }); + }); + } + + // Don't Save + else if (confirm === ConfirmResult.DONT_SAVE) { + + // Make sure to revert untitled so that they do not restore + // see https://github.com/Microsoft/vscode/issues/29572 + this.untitledEditorService.revertAll(); + + return this.noVeto({ cleanUpBackups: true }); + } + + // Cancel + else if (confirm === ConfirmResult.CANCEL) { + return true; // veto + } + + return void 0; + } + + private noVeto(options: { cleanUpBackups: boolean }): boolean | TPromise { + if (!options.cleanUpBackups) { + return false; + } + + return this.cleanupBackupsBeforeShutdown().then(() => false, () => false); + } + + protected cleanupBackupsBeforeShutdown(): TPromise { + if (this.environmentService.isExtensionDevelopment) { + return TPromise.as(void 0); + } + + return this.backupFileService.discardAllWorkspaceBackups(); + } + + protected onConfigurationChange(configuration: IFilesConfiguration): void { + const wasAutoSaveEnabled = (this.getAutoSaveMode() !== AutoSaveMode.OFF); + + const autoSaveMode = (configuration && configuration.files && configuration.files.autoSave) || AutoSaveConfiguration.OFF; + switch (autoSaveMode) { + case AutoSaveConfiguration.AFTER_DELAY: + this.configuredAutoSaveDelay = configuration && configuration.files && configuration.files.autoSaveDelay; + this.configuredAutoSaveOnFocusChange = false; + this.configuredAutoSaveOnWindowChange = false; + break; + + case AutoSaveConfiguration.ON_FOCUS_CHANGE: + this.configuredAutoSaveDelay = void 0; + this.configuredAutoSaveOnFocusChange = true; + this.configuredAutoSaveOnWindowChange = false; + break; + + case AutoSaveConfiguration.ON_WINDOW_CHANGE: + this.configuredAutoSaveDelay = void 0; + this.configuredAutoSaveOnFocusChange = false; + this.configuredAutoSaveOnWindowChange = true; + break; + + default: + this.configuredAutoSaveDelay = void 0; + this.configuredAutoSaveOnFocusChange = false; + this.configuredAutoSaveOnWindowChange = false; + break; + } + + // Emit as event + this._onAutoSaveConfigurationChange.fire(this.getAutoSaveConfiguration()); + + // save all dirty when enabling auto save + if (!wasAutoSaveEnabled && this.getAutoSaveMode() !== AutoSaveMode.OFF) { + this.saveAll().done(null, errors.onUnexpectedError); + } + + // Check for change in files associations + const filesAssociation = configuration && configuration.files && configuration.files.associations; + if (!objects.equals(this.currentFilesAssociationConfig, filesAssociation)) { + this.currentFilesAssociationConfig = filesAssociation; + this._onFilesAssociationChange.fire(); + } + + // {{SQL CARBON EDIT}} + // Hot exit + //const hotExitMode = configuration && configuration.files ? configuration.files.hotExit : HotExitConfiguration.ON_EXIT; + const hotExitMode = HotExitConfiguration.OFF; + if (hotExitMode === HotExitConfiguration.OFF || hotExitMode === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) { + this.configuredHotExit = hotExitMode; + } else { + this.configuredHotExit = HotExitConfiguration.ON_EXIT; + } + } + + public getDirty(resources?: URI[]): URI[] { + + // Collect files + const dirty = this.getDirtyFileModels(resources).map(m => m.getResource()); + + // Add untitled ones + dirty.push(...this.untitledEditorService.getDirty(resources)); + + return dirty; + } + + public isDirty(resource?: URI): boolean { + + // Check for dirty file + if (this._models.getAll(resource).some(model => model.isDirty())) { + return true; + } + + // Check for dirty untitled + return this.untitledEditorService.getDirty().some(dirty => !resource || dirty.toString() === resource.toString()); + } + + public save(resource: URI, options?: ISaveOptions): TPromise { + + // Run a forced save if we detect the file is not dirty so that save participants can still run + if (options && options.force && resource.scheme === Schemas.file && !this.isDirty(resource)) { + const model = this._models.get(resource); + if (model) { + model.save({ force: true, reason: SaveReason.EXPLICIT }).then(() => !model.isDirty()); + } + } + + return this.saveAll([resource], options).then(result => result.results.length === 1 && result.results[0].success); + } + + public saveAll(includeUntitled?: boolean, options?: ISaveOptions): TPromise; + public saveAll(resources: URI[], options?: ISaveOptions): TPromise; + public saveAll(arg1?: any, options?: ISaveOptions): TPromise { + + // get all dirty + let toSave: URI[] = []; + if (Array.isArray(arg1)) { + toSave = this.getDirty(arg1); + } else { + toSave = this.getDirty(); + } + + // split up between files and untitled + const filesToSave: URI[] = []; + const untitledToSave: URI[] = []; + toSave.forEach(s => { + if (s.scheme === Schemas.file) { + filesToSave.push(s); + } else if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && s.scheme === UNTITLED_SCHEMA) { + untitledToSave.push(s); + } + }); + + return this.doSaveAll(filesToSave, untitledToSave, options); + } + + private doSaveAll(fileResources: URI[], untitledResources: URI[], options?: ISaveOptions): TPromise { + + // Handle files first that can just be saved + return this.doSaveAllFiles(fileResources, options).then(result => { + + // Preflight for untitled to handle cancellation from the dialog + const targetsForUntitled: URI[] = []; + for (let i = 0; i < untitledResources.length; i++) { + const untitled = untitledResources[i]; + if (this.untitledEditorService.exists(untitled)) { + let targetPath: string; + + // Untitled with associated file path don't need to prompt + if (this.untitledEditorService.hasAssociatedFilePath(untitled)) { + targetPath = untitled.fsPath; + } + + // Otherwise ask user + else { + targetPath = this.promptForPath(this.suggestFileName(untitled)); + if (!targetPath) { + return TPromise.as({ + results: [...fileResources, ...untitledResources].map(r => { + return { + source: r + }; + }) + }); + } + } + + targetsForUntitled.push(URI.file(targetPath)); + } + } + + // Handle untitled + const untitledSaveAsPromises: TPromise[] = []; + targetsForUntitled.forEach((target, index) => { + const untitledSaveAsPromise = this.saveAs(untitledResources[index], target).then(uri => { + result.results.push({ + source: untitledResources[index], + target: uri, + success: !!uri + }); + }); + + untitledSaveAsPromises.push(untitledSaveAsPromise); + }); + + return TPromise.join(untitledSaveAsPromises).then(() => { + return result; + }); + }); + } + + private doSaveAllFiles(resources?: URI[], options: ISaveOptions = Object.create(null)): TPromise { + const dirtyFileModels = this.getDirtyFileModels(Array.isArray(resources) ? resources : void 0 /* Save All */) + .filter(model => { + if (model.hasState(ModelState.CONFLICT) && (options.reason === SaveReason.AUTO || options.reason === SaveReason.FOCUS_CHANGE || options.reason === SaveReason.WINDOW_CHANGE)) { + return false; // if model is in save conflict, do not save unless save reason is explicit or not provided at all + } + + return true; + }); + + const mapResourceToResult = new ResourceMap(); + dirtyFileModels.forEach(m => { + mapResourceToResult.set(m.getResource(), { + source: m.getResource() + }); + }); + + return TPromise.join(dirtyFileModels.map(model => { + return model.save(options).then(() => { + if (!model.isDirty()) { + mapResourceToResult.get(model.getResource()).success = true; + } + }); + })).then(r => { + return { + results: mapResourceToResult.values() + }; + }); + } + + private getFileModels(resources?: URI[]): ITextFileEditorModel[]; + private getFileModels(resource?: URI): ITextFileEditorModel[]; + private getFileModels(arg1?: any): ITextFileEditorModel[] { + if (Array.isArray(arg1)) { + const models: ITextFileEditorModel[] = []; + (arg1).forEach(resource => { + models.push(...this.getFileModels(resource)); + }); + + return models; + } + + return this._models.getAll(arg1); + } + + private getDirtyFileModels(resources?: URI[]): ITextFileEditorModel[]; + private getDirtyFileModels(resource?: URI): ITextFileEditorModel[]; + private getDirtyFileModels(arg1?: any): ITextFileEditorModel[] { + return this.getFileModels(arg1).filter(model => model.isDirty()); + } + + public saveAs(resource: URI, target?: URI): TPromise { + + // Get to target resource + if (!target) { + let dialogPath = resource.fsPath; + if (resource.scheme === UNTITLED_SCHEMA) { + dialogPath = this.suggestFileName(resource); + } + + const pathRaw = this.promptForPath(dialogPath); + if (pathRaw) { + target = URI.file(pathRaw); + } + } + + if (!target) { + return TPromise.as(null); // user canceled + } + + // Just save if target is same as models own resource + if (resource.toString() === target.toString()) { + return this.save(resource).then(() => resource); + } + + // Do it + return this.doSaveAs(resource, target); + } + + private doSaveAs(resource: URI, target?: URI): TPromise { + + // Retrieve text model from provided resource if any + let modelPromise: TPromise = TPromise.as(null); + if (resource.scheme === Schemas.file) { + modelPromise = TPromise.as(this._models.get(resource)); + } else if (resource.scheme === UNTITLED_SCHEMA && this.untitledEditorService.exists(resource)) { + modelPromise = this.untitledEditorService.loadOrCreate({ resource }); + } + + return modelPromise.then(model => { + + // We have a model: Use it (can be null e.g. if this file is binary and not a text file or was never opened before) + if (model) { + return this.doSaveTextFileAs(model, resource, target); + } + + // Otherwise we can only copy + return this.fileService.copyFile(resource, target); + }).then(() => { + + // Revert the source + return this.revert(resource).then(() => { + + // Done: return target + return target; + }); + }); + } + + private doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledEditorModel, resource: URI, target: URI): TPromise { + let targetModelResolver: TPromise; + + // Prefer an existing model if it is already loaded for the given target resource + const targetModel = this.models.get(target); + if (targetModel && targetModel.isResolved()) { + targetModelResolver = TPromise.as(targetModel); + } + + // Otherwise create the target file empty if it does not exist already and resolve it from there + else { + targetModelResolver = this.fileService.resolveFile(target).then(stat => stat, () => null).then(stat => stat || this.fileService.updateContent(target, '')).then(stat => { + return this.models.loadOrCreate(target); + }); + } + + return targetModelResolver.then(targetModel => { + + // take over encoding and model value from source model + targetModel.updatePreferredEncoding(sourceModel.getEncoding()); + targetModel.textEditorModel.setValue(sourceModel.getValue()); + + // save model + return targetModel.save(); + }, error => { + + // binary model: delete the file and run the operation again + if ((error).fileOperationResult === FileOperationResult.FILE_IS_BINARY || (error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE) { + return this.fileService.del(target).then(() => this.doSaveTextFileAs(sourceModel, resource, target)); + } + + return TPromise.wrapError(error); + }); + } + + private suggestFileName(untitledResource: URI): string { + const root = this.historyService.getLastActiveWorkspaceRoot(); + if (root) { + return URI.file(paths.join(root.fsPath, this.untitledEditorService.suggestFileName(untitledResource))).fsPath; + } + + return this.untitledEditorService.suggestFileName(untitledResource); + } + + public revert(resource: URI, options?: IRevertOptions): TPromise { + return this.revertAll([resource], options).then(result => result.results.length === 1 && result.results[0].success); + } + + public revertAll(resources?: URI[], options?: IRevertOptions): TPromise { + + // Revert files first + return this.doRevertAllFiles(resources, options).then(operation => { + + // Revert untitled + const reverted = this.untitledEditorService.revertAll(resources); + reverted.forEach(res => operation.results.push({ source: res, success: true })); + + return operation; + }); + } + + private doRevertAllFiles(resources?: URI[], options?: IRevertOptions): TPromise { + const fileModels = options && options.force ? this.getFileModels(resources) : this.getDirtyFileModels(resources); + + const mapResourceToResult = new ResourceMap(); + fileModels.forEach(m => { + mapResourceToResult.set(m.getResource(), { + source: m.getResource() + }); + }); + + return TPromise.join(fileModels.map(model => { + return model.revert(options && options.soft).then(() => { + if (!model.isDirty()) { + mapResourceToResult.get(model.getResource()).success = true; + } + }, error => { + + // FileNotFound means the file got deleted meanwhile, so still record as successful revert + if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) { + mapResourceToResult.get(model.getResource()).success = true; + } + + // Otherwise bubble up the error + else { + return TPromise.wrapError(error); + } + + return void 0; + }); + })).then(r => { + return { + results: mapResourceToResult.values() + }; + }); + } + + public getAutoSaveMode(): AutoSaveMode { + if (this.configuredAutoSaveOnFocusChange) { + return AutoSaveMode.ON_FOCUS_CHANGE; + } + + if (this.configuredAutoSaveOnWindowChange) { + return AutoSaveMode.ON_WINDOW_CHANGE; + } + + if (this.configuredAutoSaveDelay && this.configuredAutoSaveDelay > 0) { + return this.configuredAutoSaveDelay <= 1000 ? AutoSaveMode.AFTER_SHORT_DELAY : AutoSaveMode.AFTER_LONG_DELAY; + } + + return AutoSaveMode.OFF; + } + + public getAutoSaveConfiguration(): IAutoSaveConfiguration { + return { + autoSaveDelay: this.configuredAutoSaveDelay && this.configuredAutoSaveDelay > 0 ? this.configuredAutoSaveDelay : void 0, + autoSaveFocusChange: this.configuredAutoSaveOnFocusChange, + autoSaveApplicationChange: this.configuredAutoSaveOnWindowChange + }; + } + + public get isHotExitEnabled(): boolean { + return !this.environmentService.isExtensionDevelopment && this.configuredHotExit !== HotExitConfiguration.OFF; + } + + public dispose(): void { + this.toUnbind = dispose(this.toUnbind); + + // Clear all caches + this._models.clear(); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts new file mode 100644 index 0000000000..398602cfc2 --- /dev/null +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -0,0 +1,317 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import Event from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IEncodingSupport, ConfirmResult } from 'vs/workbench/common/editor'; +import { IBaseStat, IResolveContentOptions } from 'vs/platform/files/common/files'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { IRawTextSource } from 'vs/editor/common/model/textSource'; + +/** + * The save error handler can be installed on the text text file editor model to install code that executes when save errors occur. + */ +export interface ISaveErrorHandler { + + /** + * Called whenever a save fails. + */ + onSaveError(error: Error, model: ITextFileEditorModel): void; +} + +export interface ISaveParticipant { + + /** + * Participate in a save of a model. Allows to change the model before it is being saved to disk. + */ + participate(model: ITextFileEditorModel, env: { reason: SaveReason }): void; +} + +/** + * States the text text file editor model can be in. + */ +export enum ModelState { + SAVED, + DIRTY, + PENDING_SAVE, + + /** + * A model is in conflict mode when changes cannot be saved because the + * underlying file has changed. Models in conflict mode are always dirty. + */ + CONFLICT, + + /** + * A model is in orphan state when the underlying file has been deleted. + */ + ORPHAN, + + /** + * Any error that happens during a save that is not causing the CONFLICT state. + * Models in error mode are always diry. + */ + ERROR +} + +export enum StateChange { + DIRTY, + SAVING, + SAVE_ERROR, + SAVED, + REVERTED, + ENCODING, + CONTENT_CHANGE, + ORPHANED_CHANGE +} + +export class TextFileModelChangeEvent { + private _resource: URI; + private _kind: StateChange; + + constructor(model: ITextFileEditorModel, kind: StateChange) { + this._resource = model.getResource(); + this._kind = kind; + } + + public get resource(): URI { + return this._resource; + } + + public get kind(): StateChange { + return this._kind; + } +} + +export const TEXT_FILE_SERVICE_ID = 'textFileService'; + +export interface ITextFileOperationResult { + results: IResult[]; +} + +export interface IResult { + source: URI; + target?: URI; + success?: boolean; +} + +export interface IAutoSaveConfiguration { + autoSaveDelay: number; + autoSaveFocusChange: boolean; + autoSaveApplicationChange: boolean; +} + +export enum AutoSaveMode { + OFF, + AFTER_SHORT_DELAY, + AFTER_LONG_DELAY, + ON_FOCUS_CHANGE, + ON_WINDOW_CHANGE +} + +export enum SaveReason { + EXPLICIT = 1, + AUTO = 2, + FOCUS_CHANGE = 3, + WINDOW_CHANGE = 4 +} + +export const ITextFileService = createDecorator(TEXT_FILE_SERVICE_ID); + +export interface IRawTextContent extends IBaseStat { + + /** + * The line grouped content of a text file. + */ + value: IRawTextSource; + + /** + * The line grouped logical hash of a text file. + */ + valueLogicalHash: string; + + /** + * The encoding of the content if known. + */ + encoding: string; +} + +export interface IModelLoadOrCreateOptions { + encoding?: string; + reload?: boolean; +} + +export interface ITextFileEditorModelManager { + + onModelDisposed: Event; + onModelContentChanged: Event; + onModelEncodingChanged: Event; + + onModelDirty: Event; + onModelSaveError: Event; + onModelSaved: Event; + onModelReverted: Event; + onModelOrphanedChanged: Event; + + onModelsDirty: Event; + onModelsSaveError: Event; + onModelsSaved: Event; + onModelsReverted: Event; + + get(resource: URI): ITextFileEditorModel; + + getAll(resource?: URI): ITextFileEditorModel[]; + + loadOrCreate(resource: URI, options?: IModelLoadOrCreateOptions): TPromise; + + disposeModel(model: ITextFileEditorModel): void; +} + +export interface ISaveOptions { + force?: boolean; + reason?: SaveReason; + overwriteReadonly?: boolean; + overwriteEncoding?: boolean; + skipSaveParticipants?: boolean; +} + +export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport { + + onDidContentChange: Event; + onDidStateChange: Event; + + getVersionId(): number; + + getResource(): URI; + + hasState(state: ModelState): boolean; + + getETag(): string; + + updatePreferredEncoding(encoding: string): void; + + save(options?: ISaveOptions): TPromise; + + load(): TPromise; + + revert(soft?: boolean): TPromise; + + getValue(): string; + + isDirty(): boolean; + + isResolved(): boolean; + + isDisposed(): boolean; +} + +export interface IRevertOptions { + + /** + * Forces to load the contents from disk again even if the file is not dirty. + */ + force?: boolean; + + /** + * A soft revert will clear dirty state of a file but not attempt to load the contents from disk. + */ + soft?: boolean; +} + +export interface ITextFileService extends IDisposable { + _serviceBrand: any; + onAutoSaveConfigurationChange: Event; + onFilesAssociationChange: Event; + + /** + * Access to the manager of text file editor models providing further methods to work with them. + */ + models: ITextFileEditorModelManager; + + /** + * Resolve the contents of a file identified by the resource. + */ + resolveTextContent(resource: URI, options?: IResolveContentOptions): TPromise; + + /** + * A resource is dirty if it has unsaved changes or is an untitled file not yet saved. + * + * @param resource the resource to check for being dirty. If it is not specified, will check for + * all dirty resources. + */ + isDirty(resource?: URI): boolean; + + /** + * Returns all resources that are currently dirty matching the provided resources or all dirty resources. + * + * @param resources the resources to check for being dirty. If it is not specified, will check for + * all dirty resources. + */ + getDirty(resources?: URI[]): URI[]; + + /** + * Saves the resource. + * + * @param resource the resource to save + * @return true if the resource was saved. + */ + save(resource: URI, options?: ISaveOptions): TPromise; + + /** + * Saves the provided resource asking the user for a file name. + * + * @param resource the resource to save as. + * @return true if the file was saved. + */ + saveAs(resource: URI, targetResource?: URI): TPromise; + + /** + * Saves the set of resources and returns a promise with the operation result. + * + * @param resources can be null to save all. + * @param includeUntitled to save all resources and optionally exclude untitled ones. + */ + saveAll(includeUntitled?: boolean, options?: ISaveOptions): TPromise; + saveAll(resources: URI[], options?: ISaveOptions): TPromise; + + /** + * Reverts the provided resource. + * + * @param resource the resource of the file to revert. + * @param force to force revert even when the file is not dirty + */ + revert(resource: URI, options?: IRevertOptions): TPromise; + + /** + * Reverts all the provided resources and returns a promise with the operation result. + */ + revertAll(resources?: URI[], options?: IRevertOptions): TPromise; + + /** + * Brings up the confirm dialog to either save, don't save or cancel. + * + * @param resources the resources of the files to ask for confirmation or null if + * confirming for all dirty resources. + */ + confirmSave(resources?: URI[]): ConfirmResult; + + /** + * Convinient fast access to the current auto save mode. + */ + getAutoSaveMode(): AutoSaveMode; + + /** + * Convinient fast access to the raw configured auto save settings. + */ + getAutoSaveConfiguration(): IAutoSaveConfiguration; + + /** + * Convinient fast access to the hot exit file setting. + */ + isHotExitEnabled: boolean; +} \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/electron-browser/modelBuilder.ts b/src/vs/workbench/services/textfile/electron-browser/modelBuilder.ts new file mode 100644 index 0000000000..3d07bba1ad --- /dev/null +++ b/src/vs/workbench/services/textfile/electron-browser/modelBuilder.ts @@ -0,0 +1,221 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IStringStream } from 'vs/platform/files/common/files'; +import * as crypto from 'crypto'; +import * as strings from 'vs/base/common/strings'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { CharCode } from 'vs/base/common/charCode'; +import { IRawTextSource } from 'vs/editor/common/model/textSource'; + +const AVOID_SLICED_STRINGS = true; + +export interface ModelBuilderResult { + readonly hash: string; + readonly value: IRawTextSource; +} + +const PREALLOC_BUFFER_CHARS = 1000; + +const emptyString = ''; +const asciiStrings: string[] = []; +for (let i = 0; i < 128; i++) { + asciiStrings[i] = String.fromCharCode(i); +} + +function optimizeStringMemory(buff: Buffer, s: string): string { + const len = s.length; + + if (len === 0) { + return emptyString; + } + + if (len === 1) { + const charCode = s.charCodeAt(0); + if (charCode < 128) { + return asciiStrings[charCode]; + } + } + + if (AVOID_SLICED_STRINGS) { + // See https://bugs.chromium.org/p/v8/issues/detail?id=2869 + // See https://github.com/nodejs/help/issues/711 + + if (len < PREALLOC_BUFFER_CHARS) { + // Use the same buffer instance that we have allocated and that can fit `PREALLOC_BUFFER_CHARS` characters + const byteLen = buff.write(s, 0); + return buff.toString(undefined, 0, byteLen); + } + + return Buffer.from(s).toString(); + } + + return s; +} + +class ModelLineBasedBuilder { + + private computeHash: boolean; + private hash: crypto.Hash; + private buff: Buffer; + private BOM: string; + private lines: string[]; + private currLineIndex: number; + + constructor(computeHash: boolean) { + this.computeHash = computeHash; + if (this.computeHash) { + this.hash = crypto.createHash('sha1'); + } + this.BOM = ''; + this.lines = []; + this.currLineIndex = 0; + this.buff = Buffer.alloc(3/*any UTF16 code unit could expand to up to 3 UTF8 code units*/ * PREALLOC_BUFFER_CHARS); + } + + public acceptLines(lines: string[]): void { + if (this.currLineIndex === 0) { + // Remove the BOM (if present) + if (strings.startsWithUTF8BOM(lines[0])) { + this.BOM = strings.UTF8_BOM_CHARACTER; + lines[0] = lines[0].substr(1); + } + } + + for (let i = 0, len = lines.length; i < len; i++) { + this.lines[this.currLineIndex++] = optimizeStringMemory(this.buff, lines[i]); + } + if (this.computeHash) { + this.hash.update(lines.join('\n') + '\n'); + } + } + + public finish(length: number, carriageReturnCnt: number, containsRTL: boolean, isBasicASCII: boolean): ModelBuilderResult { + return { + hash: this.computeHash ? this.hash.digest('hex') : null, + value: { + BOM: this.BOM, + lines: this.lines, + length, + containsRTL: containsRTL, + totalCRCount: carriageReturnCnt, + isBasicASCII, + } + }; + } +} + +export function computeHash(rawText: IRawTextSource): string { + let hash = crypto.createHash('sha1'); + for (let i = 0, len = rawText.lines.length; i < len; i++) { + hash.update(rawText.lines[i] + '\n'); + } + return hash.digest('hex'); +} + +export class ModelBuilder { + + private leftoverPrevChunk: string; + private leftoverEndsInCR: boolean; + private totalCRCount: number; + private lineBasedBuilder: ModelLineBasedBuilder; + private totalLength: number; + private containsRTL: boolean; + private isBasicASCII: boolean; + + public static fromStringStream(stream: IStringStream): TPromise { + return new TPromise((c, e, p) => { + let done = false; + let builder = new ModelBuilder(false); + + stream.on('data', (chunk) => { + builder.acceptChunk(chunk); + }); + + stream.on('error', (error) => { + if (!done) { + done = true; + e(error); + } + }); + + stream.on('end', () => { + if (!done) { + done = true; + c(builder.finish()); + } + }); + }); + } + + constructor(computeHash: boolean) { + this.leftoverPrevChunk = ''; + this.leftoverEndsInCR = false; + this.totalCRCount = 0; + this.lineBasedBuilder = new ModelLineBasedBuilder(computeHash); + this.totalLength = 0; + this.containsRTL = false; + this.isBasicASCII = true; + } + + private _updateCRCount(chunk: string): void { + // Count how many \r are present in chunk to determine the majority EOL sequence + let chunkCarriageReturnCnt = 0; + let lastCarriageReturnIndex = -1; + while ((lastCarriageReturnIndex = chunk.indexOf('\r', lastCarriageReturnIndex + 1)) !== -1) { + chunkCarriageReturnCnt++; + } + this.totalCRCount += chunkCarriageReturnCnt; + } + + public acceptChunk(chunk: string): void { + if (chunk.length === 0) { + return; + } + this.totalLength += chunk.length; + + this._updateCRCount(chunk); + + if (!this.containsRTL) { + this.containsRTL = strings.containsRTL(chunk); + } + if (this.isBasicASCII) { + this.isBasicASCII = strings.isBasicASCII(chunk); + } + + // Avoid dealing with a chunk that ends in \r (push the \r to the next chunk) + if (this.leftoverEndsInCR) { + chunk = '\r' + chunk; + } + if (chunk.charCodeAt(chunk.length - 1) === CharCode.CarriageReturn) { + this.leftoverEndsInCR = true; + chunk = chunk.substr(0, chunk.length - 1); + } else { + this.leftoverEndsInCR = false; + } + + let lines = chunk.split(/\r\n|\r|\n/); + + if (lines.length === 1) { + // no \r or \n encountered + this.leftoverPrevChunk += lines[0]; + return; + } + + lines[0] = this.leftoverPrevChunk + lines[0]; + this.lineBasedBuilder.acceptLines(lines.slice(0, lines.length - 1)); + this.leftoverPrevChunk = lines[lines.length - 1]; + } + + public finish(): ModelBuilderResult { + let finalLines = [this.leftoverPrevChunk]; + if (this.leftoverEndsInCR) { + finalLines.push(''); + } + this.lineBasedBuilder.acceptLines(finalLines); + return this.lineBasedBuilder.finish(this.totalLength, this.totalCRCount, this.containsRTL, this.isBasicASCII); + } +} diff --git a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts new file mode 100644 index 0000000000..f06992d6d1 --- /dev/null +++ b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts @@ -0,0 +1,194 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import { TPromise } from 'vs/base/common/winjs.base'; +import paths = require('vs/base/common/paths'); +import strings = require('vs/base/common/strings'); +import { isWindows, isLinux } from 'vs/base/common/platform'; +import URI from 'vs/base/common/uri'; +import { ConfirmResult } from 'vs/workbench/common/editor'; +import { TextFileService as AbstractTextFileService } from 'vs/workbench/services/textfile/common/textFileService'; +import { IRawTextContent } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IFileService, IResolveContentOptions } from 'vs/platform/files/common/files'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ModelBuilder } from 'vs/workbench/services/textfile/electron-browser/modelBuilder'; +import product from 'vs/platform/node/product'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { mnemonicButtonLabel } from 'vs/base/common/labels'; + +export class TextFileService extends AbstractTextFileService { + + private static MAX_CONFIRM_FILES = 10; + + constructor( + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IFileService fileService: IFileService, + @IUntitledEditorService untitledEditorService: IUntitledEditorService, + @ILifecycleService lifecycleService: ILifecycleService, + @IInstantiationService instantiationService: IInstantiationService, + @ITelemetryService telemetryService: ITelemetryService, + @IConfigurationService configurationService: IConfigurationService, + @IModeService private modeService: IModeService, + @IWindowService private windowService: IWindowService, + @IEnvironmentService environmentService: IEnvironmentService, + @IMessageService messageService: IMessageService, + @IBackupFileService backupFileService: IBackupFileService, + @IWindowsService windowsService: IWindowsService, + @IHistoryService historyService: IHistoryService + ) { + super(lifecycleService, contextService, configurationService, telemetryService, fileService, untitledEditorService, instantiationService, messageService, environmentService, backupFileService, windowsService, historyService); + } + + public resolveTextContent(resource: URI, options?: IResolveContentOptions): TPromise { + return this.fileService.resolveStreamContent(resource, options).then(streamContent => { + return ModelBuilder.fromStringStream(streamContent.value).then(res => { + const r: IRawTextContent = { + resource: streamContent.resource, + name: streamContent.name, + mtime: streamContent.mtime, + etag: streamContent.etag, + encoding: streamContent.encoding, + value: res.value, + valueLogicalHash: res.hash + }; + return r; + }); + }); + } + + public confirmSave(resources?: URI[]): ConfirmResult { + if (this.environmentService.isExtensionDevelopment) { + return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assum we run interactive (e.g. tests) + } + + const resourcesToConfirm = this.getDirty(resources); + if (resourcesToConfirm.length === 0) { + return ConfirmResult.DONT_SAVE; + } + + const message = [ + resourcesToConfirm.length === 1 ? nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", paths.basename(resourcesToConfirm[0].fsPath)) : nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", resourcesToConfirm.length) + ]; + + if (resourcesToConfirm.length > 1) { + message.push(''); + message.push(...resourcesToConfirm.slice(0, TextFileService.MAX_CONFIRM_FILES).map(r => paths.basename(r.fsPath))); + + if (resourcesToConfirm.length > TextFileService.MAX_CONFIRM_FILES) { + if (resourcesToConfirm.length - TextFileService.MAX_CONFIRM_FILES === 1) { + message.push(nls.localize('moreFile', "...1 additional file not shown")); + } else { + message.push(nls.localize('moreFiles', "...{0} additional files not shown", resourcesToConfirm.length - TextFileService.MAX_CONFIRM_FILES)); + } + } + + message.push(''); + } + + // Button order + // Windows: Save | Don't Save | Cancel + // Mac: Save | Cancel | Don't Save + // Linux: Don't Save | Cancel | Save + + const save = { label: resourcesToConfirm.length > 1 ? mnemonicButtonLabel(nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All")) : mnemonicButtonLabel(nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), result: ConfirmResult.SAVE }; + const dontSave = { label: mnemonicButtonLabel(nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save")), result: ConfirmResult.DONT_SAVE }; + const cancel = { label: nls.localize('cancel', "Cancel"), result: ConfirmResult.CANCEL }; + + const buttons: { label: string; result: ConfirmResult; }[] = []; + if (isWindows) { + buttons.push(save, dontSave, cancel); + } else if (isLinux) { + buttons.push(dontSave, cancel, save); + } else { + buttons.push(save, cancel, dontSave); + } + + const opts: Electron.ShowMessageBoxOptions = { + title: product.nameLong, + message: message.join('\n'), + type: 'warning', + detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them."), + buttons: buttons.map(b => b.label), + noLink: true, + cancelId: buttons.indexOf(cancel) + }; + + if (isLinux) { + opts.defaultId = 2; + } + + const choice = this.windowService.showMessageBox(opts); + + return buttons[choice].result; + } + + public promptForPath(defaultPath?: string): string { + return this.windowService.showSaveDialog(this.getSaveDialogOptions(defaultPath ? paths.normalize(defaultPath, true) : void 0)); + } + + private getSaveDialogOptions(defaultPath?: string): Electron.SaveDialogOptions { + const options: Electron.SaveDialogOptions = { + defaultPath: defaultPath + }; + + // Filters are only enabled on Windows where they work properly + if (!isWindows) { + return options; + } + + interface IFilter { name: string; extensions: string[]; } + + // Build the file filter by using our known languages + const ext: string = paths.extname(defaultPath); + let matchingFilter: IFilter; + const filters: IFilter[] = this.modeService.getRegisteredLanguageNames().map(languageName => { + const extensions = this.modeService.getExtensions(languageName); + if (!extensions || !extensions.length) { + return null; + } + + const filter: IFilter = { name: languageName, extensions: extensions.slice(0, 10).map(e => strings.trim(e, '.')) }; + + if (ext && extensions.indexOf(ext) >= 0) { + matchingFilter = filter; + + return null; // matching filter will be added last to the top + } + + return filter; + }).filter(f => !!f); + + // Filters are a bit weird on Windows, based on having a match or not: + // Match: we put the matching filter first so that it shows up selected and the all files last + // No match: we put the all files filter first + const allFilesFilter = { name: nls.localize('allFiles', "All Files"), extensions: ['*'] }; + if (matchingFilter) { + filters.unshift(matchingFilter); + filters.unshift(allFilesFilter); + } else { + filters.unshift(allFilesFilter); + } + + // Allow to save file without extension + filters.push({ name: nls.localize('noExt', "No Extension"), extensions: [''] }); + + options.filters = filters; + + return options; + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/test/modelBuilder.test.ts b/src/vs/workbench/services/textfile/test/modelBuilder.test.ts new file mode 100644 index 0000000000..b50a49a9ae --- /dev/null +++ b/src/vs/workbench/services/textfile/test/modelBuilder.test.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { ModelBuilder, computeHash } from 'vs/workbench/services/textfile/electron-browser/modelBuilder'; +import { ITextModelCreationOptions } from 'vs/editor/common/editorCommon'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import * as strings from 'vs/base/common/strings'; +import { RawTextSource, IRawTextSource } from 'vs/editor/common/model/textSource'; + +export function testModelBuilder(chunks: string[], opts: ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS): string { + let expectedTextSource = RawTextSource.fromString(chunks.join('')); + let expectedHash = computeHash(expectedTextSource); + + let builder = new ModelBuilder(true); + for (let i = 0, len = chunks.length; i < len; i++) { + builder.acceptChunk(chunks[i]); + } + let actual = builder.finish(); + + let actualTextSource = actual.value; + let actualHash = actual.hash; + + assert.equal(actualHash, expectedHash); + assert.deepEqual(actualTextSource, expectedTextSource); + + return expectedHash; +} + +function toTextSource(lines: string[]): IRawTextSource { + return { + BOM: '', + lines: lines, + totalCRCount: 0, + length: 0, + containsRTL: false, + isBasicASCII: true + }; +} + +export function testDifferentHash(lines1: string[], lines2: string[]): void { + let hash1 = computeHash(toTextSource(lines1)); + let hash2 = computeHash(toTextSource(lines2)); + assert.notEqual(hash1, hash2); +} + +suite('ModelBuilder', () => { + + test('uses sha1', () => { + // These are the sha1s of the string + \n + assert.equal(computeHash(toTextSource([''])), 'adc83b19e793491b1c6ea0fd8b46cd9f32e592fc'); + assert.equal(computeHash(toTextSource(['hello world'])), '22596363b3de40b06f981fb85d82312e8c0ed511'); + }); + + test('no chunks', () => { + testModelBuilder([]); + }); + + test('single empty chunk', () => { + testModelBuilder(['']); + }); + + test('single line in one chunk', () => { + testModelBuilder(['Hello world']); + }); + + test('single line in multiple chunks', () => { + testModelBuilder(['Hello', ' ', 'world']); + }); + + test('two lines in single chunk', () => { + testModelBuilder(['Hello world\nHow are you?']); + }); + + test('two lines in multiple chunks 1', () => { + testModelBuilder(['Hello worl', 'd\nHow are you?']); + }); + + test('two lines in multiple chunks 2', () => { + testModelBuilder(['Hello worl', 'd', '\n', 'H', 'ow are you?']); + }); + + test('two lines in multiple chunks 3', () => { + testModelBuilder(['Hello worl', 'd', '\nHow are you?']); + }); + + test('multiple lines in single chunks', () => { + testModelBuilder(['Hello world\nHow are you?\nIs everything good today?\nDo you enjoy the weather?']); + }); + + test('multiple lines in multiple chunks 1', () => { + testModelBuilder(['Hello world\nHow are you', '?\nIs everything good today?\nDo you enjoy the weather?']); + }); + + test('multiple lines in multiple chunks 1', () => { + testModelBuilder(['Hello world', '\nHow are you', '?\nIs everything good today?', '\nDo you enjoy the weather?']); + }); + + test('multiple lines in multiple chunks 1', () => { + testModelBuilder(['Hello world\n', 'How are you', '?\nIs everything good today?', '\nDo you enjoy the weather?']); + }); + + test('carriage return detection (1 \\r\\n 2 \\n)', () => { + testModelBuilder(['Hello world\r\n', 'How are you', '?\nIs everything good today?', '\nDo you enjoy the weather?']); + }); + + test('carriage return detection (2 \\r\\n 1 \\n)', () => { + testModelBuilder(['Hello world\r\n', 'How are you', '?\r\nIs everything good today?', '\nDo you enjoy the weather?']); + }); + + test('carriage return detection (3 \\r\\n 0 \\n)', () => { + testModelBuilder(['Hello world\r\n', 'How are you', '?\r\nIs everything good today?', '\r\nDo you enjoy the weather?']); + }); + + test('carriage return detection (isolated \\r)', () => { + testModelBuilder(['Hello world', '\r', '\n', 'How are you', '?', '\r', '\n', 'Is everything good today?', '\r', '\n', 'Do you enjoy the weather?']); + }); + + test('BOM handling', () => { + testModelBuilder([strings.UTF8_BOM_CHARACTER + 'Hello world!']); + }); + + test('BOM handling', () => { + testModelBuilder([strings.UTF8_BOM_CHARACTER, 'Hello world!']); + }); + + test('RTL handling 1', () => { + testModelBuilder(['Hello world!', 'זוהי עובדה מבוססת שדעתו']); + }); + + test('RTL handling 2', () => { + testModelBuilder(['Hello world!זוהי עובדה מבוססת שדעתו']); + }); + + test('RTL handling 3', () => { + testModelBuilder(['Hello world!זוהי \nעובדה מבוססת שדעתו']); + }); + + test('ASCII handling 1', () => { + testModelBuilder(['Hello world!!\nHow do you do?']); + }); + test('ASCII handling 1', () => { + testModelBuilder(['Hello world!!\nHow do you do?Züricha📚📚b']); + }); + + test('issue #32819: some special string cannot be displayed completely', () => { + testModelBuilder(['AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA123']); + }); +}); diff --git a/src/vs/workbench/services/textfile/test/modelBuilderAuto.test.ts b/src/vs/workbench/services/textfile/test/modelBuilderAuto.test.ts new file mode 100644 index 0000000000..a4536fbb24 --- /dev/null +++ b/src/vs/workbench/services/textfile/test/modelBuilderAuto.test.ts @@ -0,0 +1,138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { testModelBuilder, testDifferentHash } from './modelBuilder.test'; +import { CharCode } from 'vs/base/common/charCode'; + +const GENERATE_TESTS = false; + +suite('ModelBuilder Auto Tests', () => { + + test('auto1', () => { + testModelBuilder(['sarjniow', '\r', '\nbpb', 'ofb', '\njzldgxx', '\r\nkzwfjysng']); + }); + + test('auto2', () => { + testModelBuilder(['i', 'yyernubi\r\niimgn\n', 'ut\r']); + }); + + test('auto3', () => { + testDifferentHash([''], ['', '', '']); + }); +}); + +function getRandomInt(min: number, max: number): number { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function getRandomEOLSequence(): string { + let rnd = getRandomInt(1, 3); + if (rnd === 1) { + return '\n'; + } + if (rnd === 2) { + return '\r'; + } + return '\r\n'; +} + +function getRandomString(minLength: number, maxLength: number): string { + let length = getRandomInt(minLength, maxLength); + let r = ''; + for (let i = 0; i < length; i++) { + r += String.fromCharCode(getRandomInt(CharCode.a, CharCode.z)); + } + return r; +} + +function generateRandomFile(): string { + let lineCount = getRandomInt(1, 10); + let mixedEOLSequence = getRandomInt(1, 2) === 1 ? true : false; + let fixedEOL = getRandomEOLSequence(); + let lines: string[] = []; + for (let i = 0; i < lineCount; i++) { + if (i !== 0) { + if (mixedEOLSequence) { + lines.push(getRandomEOLSequence()); + } else { + lines.push(fixedEOL); + } + } + lines.push(getRandomString(0, 10)); + + } + return lines.join(''); +} + +function generateRandomChunks(file: string): string[] { + let result: string[] = []; + let cnt = getRandomInt(1, 20); + + let maxOffset = file.length; + + while (cnt > 0 && maxOffset > 0) { + + let offset = getRandomInt(0, maxOffset); + result.unshift(file.substring(offset, maxOffset)); + // let length = getRandomInt(0, maxOffset - offset); + // let text = generateFile(true); + + // result.push({ + // offset: offset, + // length: length, + // text: text + // }); + + maxOffset = offset; + cnt--; + } + if (maxOffset !== 0) { + result.unshift(file.substring(0, maxOffset)); + } + return result; +} + +let HASH_TO_CONTENT: { [hash: string]: string; } = {}; + +function testRandomFile(file: string): boolean { + let tests = getRandomInt(5, 10); + for (let i = 0; i < tests; i++) { + let chunks = generateRandomChunks(file); + try { + let hash = testModelBuilder(chunks); + let logicalContent = JSON.stringify(file.split(/\r\n|\r|\n/)); + if (HASH_TO_CONTENT.hasOwnProperty(hash)) { + let prevLogicalContent = HASH_TO_CONTENT[hash]; + if (prevLogicalContent !== logicalContent) { + console.log('HASH COLLISION: '); + console.log(prevLogicalContent); + console.log(logicalContent); + return false; + } + } else { + HASH_TO_CONTENT[hash] = logicalContent; + } + } catch (err) { + console.log(err); + console.log(JSON.stringify(chunks)); + return false; + } + } + return true; +} + +if (GENERATE_TESTS) { + let number = 1; + while (true) { + console.log('------BEGIN NEW TEST: ' + number); + + if (!testRandomFile(generateRandomFile())) { + break; + } + + console.log('------END NEW TEST: ' + (number++)); + } +} diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts new file mode 100644 index 0000000000..a20dec7dc4 --- /dev/null +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -0,0 +1,450 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { EncodingMode } from 'vs/workbench/common/editor'; +import { TextFileEditorModel, SaveSequentializer } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { ITextFileService, ModelState, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; +import { workbenchInstantiationService, TestTextFileService, createFileInput, TestFileService } from 'vs/workbench/test/workbenchTestServices'; +import { onError, toResource } from 'vs/base/test/common/utils'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { FileOperationResult, FileOperationError, IFileService } from 'vs/platform/files/common/files'; +import { IModelService } from 'vs/editor/common/services/modelService'; + +class ServiceAccessor { + constructor( @ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService, @IFileService public fileService: TestFileService) { + } +} + +function getLastModifiedTime(model: TextFileEditorModel): number { + const stat = model.getStat(); + + return stat ? stat.mtime : -1; +} + +suite('Files - TextFileEditorModel', () => { + + let instantiationService: IInstantiationService; + let accessor: ServiceAccessor; + let content: string; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + content = accessor.fileService.getContent(); + }); + + teardown(() => { + (accessor.textFileService.models).clear(); + TextFileEditorModel.setSaveParticipant(null); // reset any set participant + accessor.fileService.setContent(content); + }); + + test('Save', function (done) { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + model.load().done(() => { + model.textEditorModel.setValue('bar'); + assert.ok(getLastModifiedTime(model) <= Date.now()); + + return model.save().then(() => { + assert.ok(model.getLastSaveAttemptTime() <= Date.now()); + assert.ok(!model.isDirty()); + + model.dispose(); + assert.ok(!accessor.modelService.getModel(model.getResource())); + + done(); + }); + }, error => onError(error, done)); + }); + + test('setEncoding - encode', function () { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + model.setEncoding('utf8', EncodingMode.Encode); // no-op + assert.equal(getLastModifiedTime(model), -1); + + model.setEncoding('utf16', EncodingMode.Encode); + + assert.ok(getLastModifiedTime(model) <= Date.now()); // indicates model was saved due to encoding change + + model.dispose(); + }); + + test('setEncoding - decode', function () { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + model.setEncoding('utf16', EncodingMode.Decode); + + assert.ok(model.isResolved()); // model got loaded due to decoding + + model.dispose(); + }); + + test('disposes when underlying model is destroyed', function (done) { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + model.load().done(() => { + model.textEditorModel.destroy(); + + assert.ok(model.isDisposed()); + + done(); + }, error => onError(error, done)); + }); + + test('Load does not trigger save', function (done) { + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8'); + assert.ok(model.hasState(ModelState.SAVED)); + + model.onDidStateChange(e => { + assert.ok(e !== StateChange.DIRTY && e !== StateChange.SAVED); + }); + + model.load().done(() => { + assert.ok(model.isResolved()); + + model.dispose(); + + assert.ok(!accessor.modelService.getModel(model.getResource())); + + done(); + }, error => onError(error, done)); + }); + + test('Load returns dirty model as long as model is dirty', function (done) { + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.ok(model.isDirty()); + assert.ok(model.hasState(ModelState.DIRTY)); + return model.load().then(() => { + assert.ok(model.isDirty()); + + model.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('Revert', function (done) { + let eventCounter = 0; + + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + model.onDidStateChange(e => { + if (e === StateChange.REVERTED) { + eventCounter++; + } + }); + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.ok(model.isDirty()); + + return model.revert().then(() => { + assert.ok(!model.isDirty()); + assert.equal(model.textEditorModel.getValue(), 'Hello Html'); + assert.equal(eventCounter, 1); + + model.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('Revert (soft)', function (done) { + let eventCounter = 0; + + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + model.onDidStateChange(e => { + if (e === StateChange.REVERTED) { + eventCounter++; + } + }); + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.ok(model.isDirty()); + + return model.revert(true /* soft revert */).then(() => { + assert.ok(!model.isDirty()); + assert.equal(model.textEditorModel.getValue(), 'foo'); + assert.equal(eventCounter, 1); + + model.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('Load and undo turns model dirty', function (done) { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + model.load().done(() => { + accessor.fileService.setContent('Hello Change'); + model.load().done(() => { + model.textEditorModel.undo(); + + assert.ok(model.isDirty()); + + done(); + }); + }, error => onError(error, done)); + }); + + test('File not modified error is handled gracefully', function (done) { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + model.load().done(() => { + const mtime = getLastModifiedTime(model); + accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_NOT_MODIFIED_SINCE)); + + return model.load().then((model: TextFileEditorModel) => { + assert.ok(model); + assert.equal(getLastModifiedTime(model), mtime); + model.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('Load error is handled gracefully if model already exists', function (done) { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + model.load().done(() => { + accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_NOT_FOUND)); + + return model.load().then((model: TextFileEditorModel) => { + assert.ok(model); + model.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('save() and isDirty() - proper with check for mtimes', function (done) { + const input1 = createFileInput(instantiationService, toResource.call(this, '/path/index_async2.txt')); + const input2 = createFileInput(instantiationService, toResource.call(this, '/path/index_async.txt')); + + input1.resolve().done((model1: TextFileEditorModel) => { + return input2.resolve().then((model2: TextFileEditorModel) => { + model1.textEditorModel.setValue('foo'); + + const m1Mtime = model1.getStat().mtime; + const m2Mtime = model2.getStat().mtime; + assert.ok(m1Mtime > 0); + assert.ok(m2Mtime > 0); + + assert.ok(accessor.textFileService.isDirty()); + assert.ok(accessor.textFileService.isDirty(toResource.call(this, '/path/index_async2.txt'))); + assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async.txt'))); + + model2.textEditorModel.setValue('foo'); + assert.ok(accessor.textFileService.isDirty(toResource.call(this, '/path/index_async.txt'))); + + return TPromise.timeout(10).then(() => { + accessor.textFileService.saveAll().then(() => { + assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async.txt'))); + assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async2.txt'))); + assert.ok(model1.getStat().mtime > m1Mtime); + assert.ok(model2.getStat().mtime > m2Mtime); + assert.ok(model1.getLastSaveAttemptTime() > m1Mtime); + assert.ok(model2.getLastSaveAttemptTime() > m2Mtime); + + model1.dispose(); + model2.dispose(); + + done(); + }); + }); + }); + }, error => onError(error, done)); + }); + + test('Save Participant', function (done) { + let eventCounter = 0; + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + model.onDidStateChange(e => { + if (e === StateChange.SAVED) { + assert.equal(model.getValue(), 'bar'); + assert.ok(!model.isDirty()); + eventCounter++; + } + }); + + TextFileEditorModel.setSaveParticipant({ + participate: (model) => { + assert.ok(model.isDirty()); + model.textEditorModel.setValue('bar'); + assert.ok(model.isDirty()); + eventCounter++; + return undefined; + } + }); + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + return model.save().then(() => { + model.dispose(); + + assert.equal(eventCounter, 2); + + done(); + }); + }, error => onError(error, done)); + }); + + test('Save Participant, async participant', function (done) { + + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + TextFileEditorModel.setSaveParticipant({ + participate: (model) => { + return TPromise.timeout(10); + } + }); + + return model.load().done(() => { + model.textEditorModel.setValue('foo'); + const now = Date.now(); + return model.save().then(() => { + assert.ok(Date.now() - now >= 10); + model.dispose(); + + done(); + }); + }, error => onError(error, done)); + }); + + test('Save Participant, bad participant', function (done) { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + TextFileEditorModel.setSaveParticipant({ + participate: (model) => { + return TPromise.wrapError(new Error('boom')); + } + }); + + return model.load().then(() => { + model.textEditorModel.setValue('foo'); + return model.save().then(() => { + assert.ok(true); + model.dispose(); + + done(); + }, err => { + assert.ok(false); + }); + }, error => onError(error, done)); + }); + + test('SaveSequentializer - pending basics', function (done) { + const sequentializer = new SaveSequentializer(); + + assert.ok(!sequentializer.hasPendingSave()); + assert.ok(!sequentializer.hasPendingSave(2323)); + assert.ok(!sequentializer.pendingSave); + + // pending removes itself after done + sequentializer.setPending(1, TPromise.as(null)); + assert.ok(!sequentializer.hasPendingSave()); + assert.ok(!sequentializer.hasPendingSave(1)); + assert.ok(!sequentializer.pendingSave); + + // pending removes itself after done (use timeout) + sequentializer.setPending(2, TPromise.timeout(1)); + assert.ok(sequentializer.hasPendingSave()); + assert.ok(sequentializer.hasPendingSave(2)); + assert.ok(!sequentializer.hasPendingSave(1)); + assert.ok(sequentializer.pendingSave); + + return TPromise.timeout(2).then(() => { + assert.ok(!sequentializer.hasPendingSave()); + assert.ok(!sequentializer.hasPendingSave(2)); + assert.ok(!sequentializer.pendingSave); + + done(); + }); + }); + + test('SaveSequentializer - pending and next (finishes instantly)', function (done) { + const sequentializer = new SaveSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, TPromise.timeout(1).then(() => { pendingDone = true; return null; })); + + // next finishes instantly + let nextDone = false; + const res = sequentializer.setNext(() => TPromise.as(null).then(() => { nextDone = true; return null; })); + + return res.done(() => { + assert.ok(pendingDone); + assert.ok(nextDone); + + done(); + }); + }); + + test('SaveSequentializer - pending and next (finishes after timeout)', function (done) { + const sequentializer = new SaveSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, TPromise.timeout(1).then(() => { pendingDone = true; return null; })); + + // next finishes after timeout + let nextDone = false; + const res = sequentializer.setNext(() => TPromise.timeout(1).then(() => { nextDone = true; return null; })); + + return res.done(() => { + assert.ok(pendingDone); + assert.ok(nextDone); + + done(); + }); + }); + + test('SaveSequentializer - pending and multiple next (last one wins)', function (done) { + const sequentializer = new SaveSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, TPromise.timeout(1).then(() => { pendingDone = true; return null; })); + + // next finishes after timeout + let firstDone = false; + let firstRes = sequentializer.setNext(() => TPromise.timeout(2).then(() => { firstDone = true; return null; })); + + let secondDone = false; + let secondRes = sequentializer.setNext(() => TPromise.timeout(3).then(() => { secondDone = true; return null; })); + + let thirdDone = false; + let thirdRes = sequentializer.setNext(() => TPromise.timeout(4).then(() => { thirdDone = true; return null; })); + + return TPromise.join([firstRes, secondRes, thirdRes]).then(() => { + assert.ok(pendingDone); + assert.ok(!firstDone); + assert.ok(!secondDone); + assert.ok(thirdDone); + + done(); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts new file mode 100644 index 0000000000..521c39e38c --- /dev/null +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -0,0 +1,348 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { join } from 'vs/base/common/paths'; +import { workbenchInstantiationService, TestEditorGroupService, TestFileService } from 'vs/workbench/test/workbenchTestServices'; +import { onError } from 'vs/base/test/common/utils'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; +import { IModelService } from 'vs/editor/common/services/modelService'; + +export class TestTextFileEditorModelManager extends TextFileEditorModelManager { + + protected debounceDelay(): number { + return 10; + } +} + +class ServiceAccessor { + constructor( + @IEditorGroupService public editorGroupService: TestEditorGroupService, + @IFileService public fileService: TestFileService, + @IModelService public modelService: IModelService + ) { + } +} + +function toResource(path: string): URI { + return URI.file(join('C:\\', path)); +} + +suite('Files - TextFileEditorModelManager', () => { + + let instantiationService: IInstantiationService; + let accessor: ServiceAccessor; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + }); + + test('add, remove, clear, get, getAll', function () { + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + + const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource('/path/random1.txt'), 'utf8'); + const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource('/path/random2.txt'), 'utf8'); + const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource('/path/random3.txt'), 'utf8'); + + manager.add(URI.file('/test.html'), model1); + manager.add(URI.file('/some/other.html'), model2); + manager.add(URI.file('/some/this.txt'), model3); + + const fileUpper = URI.file('/TEST.html'); + + assert(!manager.get(URI.file('foo'))); + assert.strictEqual(manager.get(URI.file('/test.html')), model1); + + assert.ok(!manager.get(fileUpper)); + + let result = manager.getAll(); + assert.strictEqual(3, result.length); + + result = manager.getAll(URI.file('/yes')); + assert.strictEqual(0, result.length); + + result = manager.getAll(URI.file('/some/other.txt')); + assert.strictEqual(0, result.length); + + result = manager.getAll(URI.file('/some/other.html')); + assert.strictEqual(1, result.length); + + result = manager.getAll(fileUpper); + assert.strictEqual(0, result.length); + + manager.remove(URI.file('')); + + result = manager.getAll(); + assert.strictEqual(3, result.length); + + manager.remove(URI.file('/some/other.html')); + result = manager.getAll(); + assert.strictEqual(2, result.length); + + manager.remove(fileUpper); + result = manager.getAll(); + assert.strictEqual(2, result.length); + + manager.clear(); + result = manager.getAll(); + assert.strictEqual(0, result.length); + + model1.dispose(); + model2.dispose(); + model3.dispose(); + }); + + test('loadOrCreate', function (done) { + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + const resource = URI.file('/test.html'); + const encoding = 'utf8'; + + manager.loadOrCreate(resource, { encoding, reload: true }).done(model => { + assert.ok(model); + assert.equal(model.getEncoding(), encoding); + assert.equal(manager.get(resource), model); + + return manager.loadOrCreate(resource, { encoding }).then(model2 => { + assert.equal(model2, model); + + model.dispose(); + + return manager.loadOrCreate(resource, { encoding }).then(model3 => { + assert.notEqual(model3, model2); + assert.equal(manager.get(resource), model3); + + model3.dispose(); + + done(); + }); + }); + }, error => onError(error, done)); + }); + + test('removed from cache when model disposed', function () { + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + + const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource('/path/random1.txt'), 'utf8'); + const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource('/path/random2.txt'), 'utf8'); + const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource('/path/random3.txt'), 'utf8'); + + manager.add(URI.file('/test.html'), model1); + manager.add(URI.file('/some/other.html'), model2); + manager.add(URI.file('/some/this.txt'), model3); + + assert.strictEqual(manager.get(URI.file('/test.html')), model1); + + model1.dispose(); + assert(!manager.get(URI.file('/test.html'))); + + model2.dispose(); + model3.dispose(); + }); + + test('events', function (done) { + TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = 0; + TextFileEditorModel.DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY = 0; + + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + + const resource1 = toResource('/path/index.txt'); + const resource2 = toResource('/path/other.txt'); + + let dirtyCounter = 0; + let revertedCounter = 0; + let savedCounter = 0; + let encodingCounter = 0; + let orphanedCounter = 0; + let disposeCounter = 0; + let contentCounter = 0; + + manager.onModelDirty(e => { + if (e.resource.toString() === resource1.toString()) { + dirtyCounter++; + } + }); + + manager.onModelReverted(e => { + if (e.resource.toString() === resource1.toString()) { + revertedCounter++; + } + }); + + manager.onModelSaved(e => { + if (e.resource.toString() === resource1.toString()) { + savedCounter++; + } + }); + + manager.onModelEncodingChanged(e => { + if (e.resource.toString() === resource1.toString()) { + encodingCounter++; + } + }); + + manager.onModelOrphanedChanged(e => { + if (e.resource.toString() === resource1.toString()) { + orphanedCounter++; + } + }); + + manager.onModelContentChanged(e => { + if (e.resource.toString() === resource1.toString()) { + contentCounter++; + } + }); + + manager.onModelDisposed(e => { + disposeCounter++; + }); + + manager.loadOrCreate(resource1, { encoding: 'utf8' }).done(model1 => { + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.DELETED }])); + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.ADDED }])); + + return manager.loadOrCreate(resource2, { encoding: 'utf8' }).then(model2 => { + model1.textEditorModel.setValue('changed'); + model1.updatePreferredEncoding('utf16'); + + return model1.revert().then(() => { + model1.textEditorModel.setValue('changed again'); + + return model1.save().then(() => { + model1.dispose(); + model2.dispose(); + assert.equal(disposeCounter, 2); + + return model1.revert().then(() => { // should not trigger another event if disposed + assert.equal(dirtyCounter, 2); + assert.equal(revertedCounter, 1); + assert.equal(savedCounter, 1); + assert.equal(encodingCounter, 2); + + // content change event if done async + TPromise.timeout(10).then(() => { + assert.equal(contentCounter, 2); + assert.equal(orphanedCounter, 1); + + model1.dispose(); + model2.dispose(); + + assert.ok(!accessor.modelService.getModel(resource1)); + assert.ok(!accessor.modelService.getModel(resource2)); + + done(); + }); + }); + }); + }); + }); + }, error => onError(error, done)); + }); + + test('events debounced', function (done) { + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + + const resource1 = toResource('/path/index.txt'); + const resource2 = toResource('/path/other.txt'); + + let dirtyCounter = 0; + let revertedCounter = 0; + let savedCounter = 0; + + TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = 0; + + manager.onModelsDirty(e => { + dirtyCounter += e.length; + assert.equal(e[0].resource.toString(), resource1.toString()); + }); + + manager.onModelsReverted(e => { + revertedCounter += e.length; + assert.equal(e[0].resource.toString(), resource1.toString()); + }); + + manager.onModelsSaved(e => { + savedCounter += e.length; + assert.equal(e[0].resource.toString(), resource1.toString()); + }); + + manager.loadOrCreate(resource1, { encoding: 'utf8' }).done(model1 => { + return manager.loadOrCreate(resource2, { encoding: 'utf8' }).then(model2 => { + model1.textEditorModel.setValue('changed'); + model1.updatePreferredEncoding('utf16'); + + return model1.revert().then(() => { + model1.textEditorModel.setValue('changed again'); + + return model1.save().then(() => { + model1.dispose(); + model2.dispose(); + + return model1.revert().then(() => { // should not trigger another event if disposed + return TPromise.timeout(20).then(() => { + assert.equal(dirtyCounter, 2); + assert.equal(revertedCounter, 1); + assert.equal(savedCounter, 1); + + model1.dispose(); + model2.dispose(); + + assert.ok(!accessor.modelService.getModel(resource1)); + assert.ok(!accessor.modelService.getModel(resource2)); + + done(); + }); + }); + }); + }); + }); + }, error => onError(error, done)); + }); + + test('disposing model takes it out of the manager', function (done) { + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + + const resource = toResource('/path/index_something.txt'); + + manager.loadOrCreate(resource, { encoding: 'utf8' }).done(model => { + model.dispose(); + + assert.ok(!manager.get(resource)); + assert.ok(!accessor.modelService.getModel(model.getResource())); + + manager.dispose(); + done(); + }, error => onError(error, done)); + }); + + test('dispose prevents dirty model from getting disposed', function (done) { + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + + const resource = toResource('/path/index_something.txt'); + + manager.loadOrCreate(resource, { encoding: 'utf8' }).done((model: TextFileEditorModel) => { + model.textEditorModel.setValue('make dirty'); + + manager.disposeModel(model); + assert.ok(!model.isDisposed()); + + model.revert(true); + + manager.disposeModel(model); + assert.ok(model.isDisposed()); + + manager.dispose(); + done(); + }, error => onError(error, done)); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts new file mode 100644 index 0000000000..d04a210650 --- /dev/null +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -0,0 +1,415 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import * as platform from 'vs/base/common/platform'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ILifecycleService, ShutdownEvent, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestWindowsService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { onError, toResource } from 'vs/base/test/common/utils'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ConfirmResult } from 'vs/workbench/common/editor'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; +import { HotExitConfiguration } from 'vs/platform/files/common/files'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +class ServiceAccessor { + constructor( + @ILifecycleService public lifecycleService: TestLifecycleService, + @ITextFileService public textFileService: TestTextFileService, + @IUntitledEditorService public untitledEditorService: IUntitledEditorService, + @IWindowsService public windowsService: TestWindowsService, + @IWorkspaceContextService public contextService: TestContextService + ) { + } +} + +class ShutdownEventImpl implements ShutdownEvent { + + public value: boolean | TPromise; + public reason = ShutdownReason.CLOSE; + + veto(value: boolean | TPromise): void { + this.value = value; + } +} + +suite('Files - TextFileService', () => { + + let instantiationService: IInstantiationService; + let model: TextFileEditorModel; + let accessor: ServiceAccessor; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + }); + + teardown(() => { + model.dispose(); + (accessor.textFileService.models).clear(); + (accessor.textFileService.models).dispose(); + accessor.untitledEditorService.revertAll(); + }); + + test('confirm onWillShutdown - no veto', function () { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + const event = new ShutdownEventImpl(); + accessor.lifecycleService.fireWillShutdown(event); + + const veto = event.value; + if (typeof veto === 'boolean') { + assert.ok(!veto); + } else { + veto.then(veto => { + assert.ok(!veto); + }); + } + }); + + test('confirm onWillShutdown - veto if user cancels', function (done) { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + const service = accessor.textFileService; + service.setConfirmResult(ConfirmResult.CANCEL); + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.equal(service.getDirty().length, 1); + + const event = new ShutdownEventImpl(); + accessor.lifecycleService.fireWillShutdown(event); + + assert.ok(event.value); + + done(); + }, error => onError(error, done)); + }); + + test('confirm onWillShutdown - no veto and backups cleaned up if user does not want to save (hot.exit: off)', function (done) { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + const service = accessor.textFileService; + service.setConfirmResult(ConfirmResult.DONT_SAVE); + service.onConfigurationChange({ files: { hotExit: 'off' } }); + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.equal(service.getDirty().length, 1); + + const event = new ShutdownEventImpl(); + accessor.lifecycleService.fireWillShutdown(event); + + const veto = event.value; + if (typeof veto === 'boolean') { + assert.ok(service.cleanupBackupsBeforeShutdownCalled); + assert.ok(!veto); + + done(); + } else { + veto.then(veto => { + assert.ok(service.cleanupBackupsBeforeShutdownCalled); + assert.ok(!veto); + + done(); + }); + } + }, error => onError(error, done)); + }); + + test('confirm onWillShutdown - save (hot.exit: off)', function (done) { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + const service = accessor.textFileService; + service.setConfirmResult(ConfirmResult.SAVE); + service.onConfigurationChange({ files: { hotExit: 'off' } }); + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.equal(service.getDirty().length, 1); + + const event = new ShutdownEventImpl(); + accessor.lifecycleService.fireWillShutdown(event); + + return (>event.value).then(veto => { + assert.ok(!veto); + assert.ok(!model.isDirty()); + + done(); + }); + }, error => onError(error, done)); + }); + + test('isDirty/getDirty - files and untitled', function (done) { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + const service = accessor.textFileService; + model.load().done(() => { + assert.ok(!service.isDirty(model.getResource())); + model.textEditorModel.setValue('foo'); + + assert.ok(service.isDirty(model.getResource())); + assert.equal(service.getDirty().length, 1); + assert.equal(service.getDirty([model.getResource()])[0].toString(), model.getResource().toString()); + + const untitled = accessor.untitledEditorService.createOrGet(); + return untitled.resolve().then((model: UntitledEditorModel) => { + assert.ok(!service.isDirty(untitled.getResource())); + assert.equal(service.getDirty().length, 1); + model.textEditorModel.setValue('changed'); + + assert.ok(service.isDirty(untitled.getResource())); + assert.equal(service.getDirty().length, 2); + assert.equal(service.getDirty([untitled.getResource()])[0].toString(), untitled.getResource().toString()); + + done(); + }); + }, error => onError(error, done)); + }); + + test('save - file', function (done) { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + const service = accessor.textFileService; + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.ok(service.isDirty(model.getResource())); + + return service.save(model.getResource()).then(res => { + assert.ok(res); + assert.ok(!service.isDirty(model.getResource())); + + done(); + }); + }, error => onError(error, done)); + }); + + test('saveAll - file', function (done) { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + const service = accessor.textFileService; + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.ok(service.isDirty(model.getResource())); + + return service.saveAll([model.getResource()]).then(res => { + assert.ok(res); + assert.ok(!service.isDirty(model.getResource())); + assert.equal(res.results.length, 1); + assert.equal(res.results[0].source.toString(), model.getResource().toString()); + + done(); + }); + }, error => onError(error, done)); + }); + + test('saveAs - file', function (done) { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + const service = accessor.textFileService; + service.setPromptPath(model.getResource().fsPath); + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.ok(service.isDirty(model.getResource())); + + return service.saveAs(model.getResource()).then(res => { + assert.equal(res.toString(), model.getResource().toString()); + assert.ok(!service.isDirty(model.getResource())); + + done(); + }); + }, error => onError(error, done)); + }); + + test('revert - file', function (done) { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + const service = accessor.textFileService; + service.setPromptPath(model.getResource().fsPath); + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.ok(service.isDirty(model.getResource())); + + return service.revert(model.getResource()).then(res => { + assert.ok(res); + assert.ok(!service.isDirty(model.getResource())); + + done(); + }); + }, error => onError(error, done)); + }); + + // {{SQL CARBON EDIT}} + /* + suite('Hot Exit', () => { + suite('"onExit" setting', () => { + test('should hot exit on non-Mac (reason: CLOSE, windows: single, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, true, !!platform.isMacintosh, done); + }); + test('should hot exit on non-Mac (reason: CLOSE, windows: single, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, false, !!platform.isMacintosh, done); + }); + test('should NOT hot exit (reason: CLOSE, windows: multiple, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, true, true, done); + }); + test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, false, true, done); + }); + test('should hot exit (reason: QUIT, windows: single, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, true, false, done); + }); + test('should hot exit (reason: QUIT, windows: single, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, false, false, done); + }); + test('should hot exit (reason: QUIT, windows: multiple, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, true, false, done); + }); + test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, false, false, done); + }); + test('should hot exit (reason: RELOAD, windows: single, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, true, false, done); + }); + test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, false, false, done); + }); + test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, true, false, done); + }); + test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, false, false, done); + }); + test('should NOT hot exit (reason: LOAD, windows: single, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, true, true, done); + }); + test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, false, true, done); + }); + test('should NOT hot exit (reason: LOAD, windows: multiple, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, true, true, done); + }); + test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, false, true, done); + }); + }); + + suite('"onExitAndWindowClose" setting', () => { + test('should hot exit (reason: CLOSE, windows: single, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, true, false, done); + }); + test('should hot exit (reason: CLOSE, windows: single, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, false, !!platform.isMacintosh, done); + }); + test('should hot exit (reason: CLOSE, windows: multiple, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, true, false, done); + }); + test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, false, true, done); + }); + test('should hot exit (reason: QUIT, windows: single, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, true, false, done); + }); + test('should hot exit (reason: QUIT, windows: single, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, false, false, done); + }); + test('should hot exit (reason: QUIT, windows: multiple, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, true, false, done); + }); + test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, false, false, done); + }); + test('should hot exit (reason: RELOAD, windows: single, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, true, false, done); + }); + test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, false, false, done); + }); + test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, true, false, done); + }); + test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, false, false, done); + }); + test('should hot exit (reason: LOAD, windows: single, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, true, false, done); + }); + test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, false, true, done); + }); + test('should hot exit (reason: LOAD, windows: multiple, workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, true, false, done); + }); + test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function (done) { + hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, false, true, done); + }); + }); + + function hotExitTest(setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: true, shouldVeto: boolean, done: () => void): void { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + const service = accessor.textFileService; + // Set hot exit config + service.onConfigurationChange({ files: { hotExit: setting } }); + // Set empty workspace if required + if (!workspace) { + accessor.contextService.setWorkspace(null); + } + // Set multiple windows if required + if (multipleWindows) { + accessor.windowsService.windowCount = 2; + } + // Set cancel to force a veto if hot exit does not trigger + service.setConfirmResult(ConfirmResult.CANCEL); + + model.load().done(() => { + model.textEditorModel.setValue('foo'); + + assert.equal(service.getDirty().length, 1); + + const event = new ShutdownEventImpl(); + event.reason = shutdownReason; + accessor.lifecycleService.fireWillShutdown(event); + + return (>event.value).then(veto => { + // When hot exit is set, backups should never be cleaned since the confirm result is cancel + assert.ok(!service.cleanupBackupsBeforeShutdownCalled); + assert.equal(veto, shouldVeto); + + done(); + }); + }, error => onError(error, done)); + } + }); + // {{SQL CARBON EDIT}} + */ +}); \ No newline at end of file diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts new file mode 100644 index 0000000000..f7bac25134 --- /dev/null +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { first } from 'vs/base/common/async'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IDisposable, toDisposable, IReference, ReferenceCollection, ImmortalReference } from 'vs/base/common/lifecycle'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import network = require('vs/base/common/network'); +import { ITextModelService, ITextModelContentProvider, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; + +class ResourceModelCollection extends ReferenceCollection> { + + private providers: { [scheme: string]: ITextModelContentProvider[] } = Object.create(null); + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @ITextFileService private textFileService: ITextFileService + ) { + super(); + } + + public createReferencedObject(key: string): TPromise { + const resource = URI.parse(key); + + if (resource.scheme === network.Schemas.file) { + return this.textFileService.models.loadOrCreate(resource); + } + + return this.resolveTextModelContent(key).then(() => this.instantiationService.createInstance(ResourceEditorModel, resource)); + } + + public destroyReferencedObject(modelPromise: TPromise): void { + modelPromise.done(model => { + if (model instanceof TextFileEditorModel) { + this.textFileService.models.disposeModel(model); + } else { + model.dispose(); + } + }); + } + + public registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable { + const registry = this.providers; + const providers = registry[scheme] || (registry[scheme] = []); + + providers.unshift(provider); + + return toDisposable(() => { + const array = registry[scheme]; + + if (!array) { + return; + } + + const index = array.indexOf(provider); + + if (index === -1) { + return; + } + + array.splice(index, 1); + + if (array.length === 0) { + delete registry[scheme]; + } + }); + } + + private resolveTextModelContent(key: string): TPromise { + const resource = URI.parse(key); + const providers = this.providers[resource.scheme] || []; + const factories = providers.map(p => () => p.provideTextContent(resource)); + + return first(factories).then(model => { + if (!model) { + console.error(`Unable to open '${resource}' resource is not available.`); // TODO PII + return TPromise.wrapError(new Error('resource is not available')); + } + + return model; + }); + } +} + +export class TextModelResolverService implements ITextModelService { + + _serviceBrand: any; + + private resourceModelCollection: ResourceModelCollection; + + constructor( + @ITextFileService private textFileService: ITextFileService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IInstantiationService private instantiationService: IInstantiationService, + @IModelService private modelService: IModelService + ) { + this.resourceModelCollection = instantiationService.createInstance(ResourceModelCollection); + } + + public createModelReference(resource: URI): TPromise> { + return this._createModelReference(resource); + } + + private _createModelReference(resource: URI): TPromise> { + + // Untitled Schema: go through cached input + // TODO ImmortalReference is a hack + if (resource.scheme === UNTITLED_SCHEMA) { + return this.untitledEditorService.loadOrCreate({ resource }).then(model => new ImmortalReference(model)); + } + + // InMemory Schema: go through model service cache + // TODO ImmortalReference is a hack + if (resource.scheme === 'inmemory') { + const cachedModel = this.modelService.getModel(resource); + + if (!cachedModel) { + return TPromise.wrapError>(new Error('Cant resolve inmemory resource')); + } + + return TPromise.as(new ImmortalReference(this.instantiationService.createInstance(ResourceEditorModel, resource))); + } + + const ref = this.resourceModelCollection.acquire(resource.toString()); + + return ref.object.then( + model => ({ object: model, dispose: () => ref.dispose() }), + err => { + ref.dispose(); + + return TPromise.wrapError>(err); + } + ); + } + + public registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable { + return this.resourceModelCollection.registerTextModelContentProvider(scheme, provider); + } +} diff --git a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts new file mode 100644 index 0000000000..28f3e626f6 --- /dev/null +++ b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices'; +import { toResource } from 'vs/base/test/common/utils'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { once } from 'vs/base/common/event'; + +class ServiceAccessor { + constructor( + @ITextModelService public textModelResolverService: ITextModelService, + @IModelService public modelService: IModelService, + @IModeService public modeService: IModeService, + @ITextFileService public textFileService: TestTextFileService, + @IUntitledEditorService public untitledEditorService: IUntitledEditorService + ) { + } +} + +suite('Workbench - TextModelResolverService', () => { + + let instantiationService: IInstantiationService; + let accessor: ServiceAccessor; + let model: TextFileEditorModel; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + }); + + teardown(() => { + if (model) { + model.dispose(); + model = void 0; + } + (accessor.textFileService.models).clear(); + (accessor.textFileService.models).dispose(); + accessor.untitledEditorService.revertAll(); + }); + + test('resolve resource', function (done) { + const dispose = accessor.textModelResolverService.registerTextModelContentProvider('test', { + provideTextContent: function (resource: URI): TPromise { + if (resource.scheme === 'test') { + let modelContent = 'Hello Test'; + let mode = accessor.modeService.getOrCreateMode('json'); + return TPromise.as(accessor.modelService.createModel(modelContent, mode, resource)); + } + + return TPromise.as(null); + } + }); + + let resource = URI.from({ scheme: 'test', authority: null, path: 'thePath' }); + let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource); + + input.resolve().then((model: ResourceEditorModel) => { + assert.ok(model); + assert.equal(model.getValue(), 'Hello Test'); + + let disposed = false; + once(model.onDispose)(() => { + disposed = true; + }); + + input.dispose(); + assert.equal(disposed, true); + + dispose.dispose(); + done(); + }); + }); + + test('resolve file', function () { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8'); + (accessor.textFileService.models).add(model.getResource(), model); + + return model.load().then(() => { + return accessor.textModelResolverService.createModelReference(model.getResource()).then(ref => { + const model = ref.object; + const editorModel = model.textEditorModel; + + assert.ok(editorModel); + assert.equal(editorModel.getValue(), 'Hello Html'); + + let disposed = false; + once(model.onDispose)(() => { + disposed = true; + }); + + ref.dispose(); + assert.equal(disposed, true); + }); + }); + }); + + test('resolve untitled', function () { + const service = accessor.untitledEditorService; + const input = service.createOrGet(); + + return input.resolve().then(() => { + return accessor.textModelResolverService.createModelReference(input.getResource()).then(ref => { + const model = ref.object; + const editorModel = model.textEditorModel; + + assert.ok(editorModel); + ref.dispose(); + + input.dispose(); + }); + }); + }); + + test('even loading documents should be refcounted', async () => { + let resolveModel: Function; + let waitForIt = new TPromise(c => resolveModel = c); + + const disposable = accessor.textModelResolverService.registerTextModelContentProvider('test', { + provideTextContent: async (resource: URI): TPromise => { + await waitForIt; + + let modelContent = 'Hello Test'; + let mode = accessor.modeService.getOrCreateMode('json'); + return accessor.modelService.createModel(modelContent, mode, resource); + } + }); + + const uri = URI.from({ scheme: 'test', authority: null, path: 'thePath' }); + + const modelRefPromise1 = accessor.textModelResolverService.createModelReference(uri); + const modelRefPromise2 = accessor.textModelResolverService.createModelReference(uri); + + resolveModel(); + + const modelRef1 = await modelRefPromise1; + const model1 = modelRef1.object; + const modelRef2 = await modelRefPromise2; + const model2 = modelRef2.object; + const textModel = model1.textEditorModel; + + assert.equal(model1, model2, 'they are the same model'); + assert(!textModel.isDisposed(), 'the text model should not be disposed'); + + modelRef1.dispose(); + assert(!textModel.isDisposed(), 'the text model should still not be disposed'); + + modelRef2.dispose(); + assert(textModel.isDisposed(), 'the text model should finally be disposed'); + + disposable.dispose(); + }); +}); diff --git a/src/vs/workbench/services/themes/common/colorThemeSchema.ts b/src/vs/workbench/services/themes/common/colorThemeSchema.ts new file mode 100644 index 0000000000..69d0cb7592 --- /dev/null +++ b/src/vs/workbench/services/themes/common/colorThemeSchema.ts @@ -0,0 +1,200 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import nls = require('vs/nls'); + +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; + +import { Extensions as ThemeingExtensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry'; + +let themingRegistry = Registry.as(ThemeingExtensions.ColorContribution); +let textMateScopes = [ + 'comment', + 'comment.block', + 'comment.block.documentation', + 'comment.line', + 'constant', + 'constant.character', + 'constant.character.escape', + 'constant.numeric', + 'constant.numeric.integer', + 'constant.numeric.float', + 'constant.numeric.hex', + 'constant.numeric.octal', + 'constant.other', + 'constant.regexp', + 'constant.rgb-value', + 'emphasis', + 'entity', + 'entity.name', + 'entity.name.class', + 'entity.name.function', + 'entity.name.method', + 'entity.name.section', + 'entity.name.selector', + 'entity.name.tag', + 'entity.name.type', + 'entity.other', + 'entity.other.attribute-name', + 'entity.other.inherited-class', + 'invalid', + 'invalid.deprecated', + 'invalid.illegal', + 'keyword', + 'keyword.control', + 'keyword.operator', + 'keyword.operator.new', + 'keyword.operator.assignment', + 'keyword.operator.arithmetic', + 'keyword.operator.logical', + 'keyword.other', + 'markup', + 'markup.bold', + 'markup.changed', + 'markup.deleted', + 'markup.heading', + 'markup.inline.raw', + 'markup.inserted', + 'markup.italic', + 'markup.list', + 'markup.list.numbered', + 'markup.list.unnumbered', + 'markup.other', + 'markup.quote', + 'markup.raw', + 'markup.underline', + 'markup.underline.link', + 'meta', + 'meta.block', + 'meta.cast', + 'meta.class', + 'meta.function', + 'meta.function-call', + 'meta.preprocessor', + 'meta.return-type', + 'meta.selector', + 'meta.tag', + 'meta.type.annotation', + 'meta.type', + 'punctuation.definition.string.begin', + 'punctuation.definition.string.end', + 'punctuation.separator', + 'punctuation.separator.continuation', + 'punctuation.terminator', + 'storage', + 'storage.modifier', + 'storage.type', + 'string', + 'string.interpolated', + 'string.other', + 'string.quoted', + 'string.quoted.double', + 'string.quoted.other', + 'string.quoted.single', + 'string.quoted.triple', + 'string.regexp', + 'string.unquoted', + 'strong', + 'support', + 'support.class', + 'support.constant', + 'support.function', + 'support.other', + 'support.type', + 'support.type.property-name', + 'support.variable', + 'variable', + 'variable.language', + 'variable.name', + 'variable.other', + 'variable.other.readwrite', + 'variable.parameter' +]; + +export const tokenColorizationSettingSchema: IJSONSchema = { + type: 'object', + description: nls.localize('schema.token.settings', 'Colors and styles for the token.'), + properties: { + foreground: { + type: 'string', + description: nls.localize('schema.token.foreground', 'Foreground color for the token.'), + format: 'color', + defaultSnippets: [{ body: '${1:#FF0000}' }] + }, + fontStyle: { + type: 'string', + description: nls.localize('schema.token.fontStyle', 'Font style of the rule: One or a combination of \'italic\', \'bold\' and \'underline\''), + pattern: '^(\\s*\\b(italic|bold|underline))*\\s*$', + patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be a combination of \'italic\', \'bold\' and \'underline\''), + defaultSnippets: [{ body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }] + } + }, + defaultSnippets: [{ body: { foreground: '${1:#FF0000}', fontStyle: '${2:bold}' } }] +}; + +export const colorsSchema = themingRegistry.getColorSchema(); +export function tokenColorsSchema(description: string): IJSONSchema { + return { + type: 'array', + description, + items: { + type: 'object', + defaultSnippets: [{ body: { scope: '${1:keyword.operator}', settings: { foreground: '${2:#FF0000}' } } }], + properties: { + name: { + type: 'string', + description: nls.localize('schema.properties.name', 'Description of the rule.') + }, + scope: { + description: nls.localize('schema.properties.scope', 'Scope selector against which this rule matches.'), + anyOf: [ + { + enum: textMateScopes + }, + { + type: 'string' + }, + { + type: 'array', + items: { + enum: textMateScopes + } + }, + { + type: 'array', + items: { + type: 'string' + } + } + ] + }, + settings: tokenColorizationSettingSchema + } + } + }; +} + +const schemaId = 'vscode://schemas/color-theme'; +const schema: IJSONSchema = { + type: 'object', + properties: { + colors: colorsSchema, + tokenColors: { + anyOf: [{ + type: 'string', + description: nls.localize('schema.tokenColors.path', 'Path to a tmTheme file (relative to the current file).') + }, + tokenColorsSchema(nls.localize('schema.colors', 'Colors for syntax highlighting')) + ] + } + } +}; + +export function register() { + let schemaRegistry = Registry.as(JSONExtensions.JSONContribution); + schemaRegistry.registerSchema(schemaId, schema); +} + diff --git a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts new file mode 100644 index 0000000000..3f2e043ceb --- /dev/null +++ b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts @@ -0,0 +1,218 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import nls = require('vs/nls'); + +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; + +const schemaId = 'vscode://schemas/icon-theme'; +const schema: IJSONSchema = { + type: 'object', + definitions: { + folderExpanded: { + type: 'string', + description: nls.localize('schema.folderExpanded', 'The folder icon for expanded folders. The expanded folder icon is optional. If not set, the icon defined for folder will be shown.') + }, + folder: { + type: 'string', + description: nls.localize('schema.folder', 'The folder icon for collapsed folders, and if folderExpanded is not set, also for expanded folders.') + + }, + file: { + type: 'string', + description: nls.localize('schema.file', 'The default file icon, shown for all files that don\'t match any extension, filename or language id.') + + }, + folderNames: { + type: 'object', + description: nls.localize('schema.folderNames', 'Associates folder names to icons. The object key is is the folder name, not including any path segments. No patterns or wildcards are allowed. Folder name matching is case insensitive.'), + additionalProperties: { + type: 'string', + description: nls.localize('schema.folderName', 'The ID of the icon definition for the association.') + } + }, + folderNamesExpanded: { + type: 'object', + description: nls.localize('schema.folderNamesExpanded', 'Associates folder names to icons for expanded folders. The object key is is the folder name, not including any path segments. No patterns or wildcards are allowed. Folder name matching is case insensitive.'), + additionalProperties: { + type: 'string', + description: nls.localize('schema.folderNameExpanded', 'The ID of the icon definition for the association.') + } + }, + fileExtensions: { + type: 'object', + description: nls.localize('schema.fileExtensions', 'Associates file extensions to icons. The object key is is the file extension name. The extension name is the last segment of a file name after the last dot (not including the dot). Extensions are compared case insensitive.'), + + additionalProperties: { + type: 'string', + description: nls.localize('schema.fileExtension', 'The ID of the icon definition for the association.') + } + }, + fileNames: { + type: 'object', + description: nls.localize('schema.fileNames', 'Associates file names to icons. The object key is is the full file name, but not including any path segments. File name can include dots and a possible file extension. No patterns or wildcards are allowed. File name matching is case insensitive.'), + + additionalProperties: { + type: 'string', + description: nls.localize('schema.fileName', 'The ID of the icon definition for the association.') + } + }, + languageIds: { + type: 'object', + description: nls.localize('schema.languageIds', 'Associates languages to icons. The object key is the language id as defined in the language contribution point.'), + + additionalProperties: { + type: 'string', + description: nls.localize('schema.languageId', 'The ID of the icon definition for the association.') + } + }, + associations: { + type: 'object', + properties: { + folderExpanded: { + $ref: '#/definitions/folderExpanded' + }, + folder: { + $ref: '#/definitions/folder' + }, + file: { + $ref: '#/definitions/file' + }, + folderNames: { + $ref: '#/definitions/folderNames' + }, + folderNamesExpanded: { + $ref: '#/definitions/folderNamesExpanded' + }, + fileExtensions: { + $ref: '#/definitions/fileExtensions' + }, + fileNames: { + $ref: '#/definitions/fileNames' + }, + languageIds: { + $ref: '#/definitions/languageIds' + } + } + } + }, + properties: { + fonts: { + type: 'array', + description: nls.localize('schema.fonts', 'Fonts that are used in the icon definitions.'), + items: { + type: 'object', + properties: { + id: { + type: 'string', + description: nls.localize('schema.id', 'The ID of the font.') + }, + src: { + type: 'array', + description: nls.localize('schema.src', 'The locations of the font.'), + items: { + type: 'object', + properties: { + path: { + type: 'string', + description: nls.localize('schema.font-path', 'The font path, relative to the current icon theme file.'), + }, + format: { + type: 'string', + description: nls.localize('schema.font-format', 'The format of the font.') + } + }, + required: [ + 'path', + 'format' + ] + } + }, + weight: { + type: 'string', + description: nls.localize('schema.font-weight', 'The weight of the font.') + }, + style: { + type: 'string', + description: nls.localize('schema.font-sstyle', 'The style of the font.') + }, + size: { + type: 'string', + description: nls.localize('schema.font-size', 'The default size of the font.') + } + }, + required: [ + 'id', + 'src' + ] + } + }, + iconDefinitions: { + type: 'object', + description: nls.localize('schema.iconDefinitions', 'Description of all icons that can be used when associating files to icons.'), + additionalProperties: { + type: 'object', + description: nls.localize('schema.iconDefinition', 'An icon definition. The object key is the ID of the definition.'), + properties: { + iconPath: { + type: 'string', + description: nls.localize('schema.iconPath', 'When using a SVG or PNG: The path to the image. The path is relative to the icon set file.') + }, + fontCharacter: { + type: 'string', + description: nls.localize('schema.fontCharacter', 'When using a glyph font: The character in the font to use.') + }, + fontColor: { + type: 'string', + description: nls.localize('schema.fontColor', 'When using a glyph font: The color to use.') + }, + fontSize: { + type: 'string', + description: nls.localize('schema.fontSize', 'When using a font: The font size in percentage to the text font. If not set, defaults to the size in the font definition.') + }, + fontId: { + type: 'string', + description: nls.localize('schema.fontId', 'When using a font: The id of the font. If not set, defaults to the first font definition.') + } + } + } + }, + folderExpanded: { + $ref: '#/definitions/folderExpanded' + }, + folder: { + $ref: '#/definitions/folder' + }, + file: { + $ref: '#/definitions/file' + }, + folderNames: { + $ref: '#/definitions/folderNames' + }, + fileExtensions: { + $ref: '#/definitions/fileExtensions' + }, + fileNames: { + $ref: '#/definitions/fileNames' + }, + languageIds: { + $ref: '#/definitions/languageIds' + }, + light: { + $ref: '#/definitions/associations', + description: nls.localize('schema.light', 'Optional associations for file icons in light color themes.') + }, + highContrast: { + $ref: '#/definitions/associations', + description: nls.localize('schema.highContrast', 'Optional associations for file icons in high contrast color themes.') + } + } +}; + +export function register() { + let schemaRegistry = Registry.as(JSONExtensions.JSONContribution); + schemaRegistry.registerSchema(schemaId, schema); +} \ No newline at end of file diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts new file mode 100644 index 0000000000..6f607a301b --- /dev/null +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { TPromise } from 'vs/base/common/winjs.base'; +import Event from 'vs/base/common/event'; +import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { Color } from 'vs/base/common/color'; +import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; + +export const IWorkbenchThemeService = createDecorator('themeService'); + +export const VS_LIGHT_THEME = 'vs'; +export const VS_DARK_THEME = 'vs-dark'; +export const VS_HC_THEME = 'hc-black'; + +export const COLOR_THEME_SETTING = 'workbench.colorTheme'; +export const ICON_THEME_SETTING = 'workbench.iconTheme'; +export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations'; +export const DEPRECATED_CUSTOM_COLORS_SETTING = 'workbench.experimental.colorCustomizations'; +export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations'; +export const CUSTOM_EDITOR_SCOPE_COLORS_SETTING = 'textMateRules'; + +export interface IColorTheme extends ITheme { + readonly id: string; + readonly label: string; + readonly settingsId: string; + readonly extensionData: ExtensionData; + readonly description?: string; + readonly isLoaded: boolean; + readonly tokenColors: ITokenColorizationRule[]; +} + +export interface IColorMap { + [id: string]: Color; +} + +export interface IFileIconTheme { + readonly id: string; + readonly label: string; + readonly settingsId: string; + readonly description?: string; + readonly extensionData: ExtensionData; + + readonly isLoaded: boolean; + readonly hasFileIcons?: boolean; + readonly hasFolderIcons?: boolean; +} + +export interface IWorkbenchThemeService extends IThemeService { + _serviceBrand: any; + setColorTheme(themeId: string, settingsTarget: ConfigurationTarget): TPromise; + getColorTheme(): IColorTheme; + getColorThemes(): TPromise; + onDidColorThemeChange: Event; + + setFileIconTheme(iconThemeId: string, settingsTarget: ConfigurationTarget): TPromise; + getFileIconTheme(): IFileIconTheme; + getFileIconThemes(): TPromise; + onDidFileIconThemeChange: Event; +} + +export interface ITokenColorCustomizations { + comments?: string | ITokenColorizationSetting; + strings?: string | ITokenColorizationSetting; + numbers?: string | ITokenColorizationSetting; + keywords?: string | ITokenColorizationSetting; + types?: string | ITokenColorizationSetting; + functions?: string | ITokenColorizationSetting; + variables?: string | ITokenColorizationSetting; + textMateRules?: ITokenColorizationRule[]; +} + +export interface ITokenColorizationRule { + name?: string; + scope?: string | string[]; + settings: ITokenColorizationSetting; +} + +export interface ITokenColorizationSetting { + foreground?: string; + background?: string; + fontStyle?: string; // italic, underline, bold +} + +export interface ExtensionData { + extensionId: string; + extensionPublisher: string; + extensionName: string; + extensionIsBuiltin: boolean; +} + +export interface IThemeExtensionPoint { + id: string; + label?: string; + description?: string; + path: string; +} \ No newline at end of file diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts new file mode 100644 index 0000000000..024cd759be --- /dev/null +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts @@ -0,0 +1,347 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Paths = require('vs/base/common/paths'); +import Json = require('vs/base/common/json'); +import { Color } from 'vs/base/common/color'; +import { ExtensionData, ITokenColorCustomizations, ITokenColorizationRule, IColorTheme, IColorMap, IThemeExtensionPoint, VS_LIGHT_THEME, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { convertSettings } from 'vs/workbench/services/themes/electron-browser/themeCompatibility'; +import { TPromise } from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import * as types from 'vs/base/common/types'; +import * as objects from 'vs/base/common/objects'; + +import * as plist from 'fast-plist'; +import pfs = require('vs/base/node/pfs'); + +import { Extensions, IColorRegistry, ColorIdentifier, editorBackground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { ThemeType } from 'vs/platform/theme/common/themeService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { WorkbenchThemeService, IColorCustomizations } from 'vs/workbench/services/themes/electron-browser/workbenchThemeService'; +import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; + +let colorRegistry = Registry.as(Extensions.ColorContribution); + +const tokenGroupToScopesMap = { + comments: 'comment', + strings: 'string', + keywords: 'keyword', + numbers: 'constant.numeric', + types: 'entity.name.type', + functions: 'entity.name.function', + variables: 'variable' +}; + +export class ColorThemeData implements IColorTheme { + + constructor() { + } + + id: string; + label: string; + settingsId: string; + description?: string; + get tokenColors(): ITokenColorizationRule[] { + // Add the custom colors after the theme colors + // so that they will override them + return this.themeTokenColors.concat(this.customTokenColors); + } + themeTokenColors: ITokenColorizationRule[] = []; + customTokenColors: ITokenColorizationRule[] = []; + isLoaded: boolean; + path?: string; + extensionData: ExtensionData; + colorMap: IColorMap = {}; + customColorMap: IColorMap = {}; + + public getColor(colorId: ColorIdentifier, useDefault?: boolean): Color { + let color = this.customColorMap[colorId]; + if (color) { + return color; + } + color = this.colorMap[colorId]; + if (useDefault !== false && types.isUndefined(color)) { + color = this.getDefault(colorId); + } + return color; + } + + public getDefault(colorId: ColorIdentifier): Color { + return colorRegistry.resolveDefaultColor(colorId, this); + } + + public defines(colorId: ColorIdentifier): boolean { + return this.customColorMap.hasOwnProperty(colorId) || this.colorMap.hasOwnProperty(colorId); + } + + public setCustomColors(colors: IColorCustomizations) { + this.customColorMap = {}; + for (let id in colors) { + let colorVal = colors[id]; + if (typeof colorVal === 'string') { + this.customColorMap[id] = Color.fromHex(colorVal); + } + } + if (this.themeTokenColors && this.themeTokenColors.length) { + updateDefaultRuleSettings(this.themeTokenColors[0], this); + } + } + + public setCustomTokenColors(customTokenColors: ITokenColorCustomizations) { + let generalRules: ITokenColorizationRule[] = []; + + let value, settings, scope; + Object.keys(tokenGroupToScopesMap).forEach(key => { + value = customTokenColors[key]; + settings = typeof value === 'string' + ? { foreground: value } + : value; + scope = tokenGroupToScopesMap[key]; + + if (!settings) { + return; + } + + generalRules.push({ + scope, + settings + }); + }); + + const textMateRules: ITokenColorizationRule[] = customTokenColors.textMateRules || []; + + // Put the general customizations such as comments, strings, etc. first so that + // they can be overriden by specific customizations like "string.interpolated" + this.customTokenColors = generalRules.concat(textMateRules); + } + + public ensureLoaded(themeService: WorkbenchThemeService): TPromise { + if (!this.isLoaded) { + this.themeTokenColors = []; + this.colorMap = {}; + if (this.path) { + return _loadColorThemeFromFile(this.path, this.themeTokenColors, this.colorMap).then(_ => { + this.isLoaded = true; + _sanitizeTokenColors(this); + }); + } + } + return TPromise.as(null); + } + + toThemeFile() { + if (!this.isLoaded) { + return ''; + } + let content = { name: this.label, colors: {}, tokenColors: this.tokenColors }; + for (let key in this.colorMap) { + content.colors[key] = Color.Format.CSS.formatHexA(this.colorMap[key], true); + } + return JSON.stringify(content, null, '\t'); + } + + toStorageData() { + let colorMapData = {}; + for (let key in this.colorMap) { + colorMapData[key] = Color.Format.CSS.formatHexA(this.colorMap[key], true); + } + // no need to persist custom colors, they will be taken from the settings + return JSON.stringify({ + id: this.id, + label: this.label, + settingsId: this.settingsId, + selector: this.id.split(' ').join('.'), // to not break old clients + themeTokenColors: this.themeTokenColors, + extensionData: this.extensionData, + colorMap: colorMapData + }); + } + + hasEqualData(other: ColorThemeData) { + return objects.equals(this.colorMap, other.colorMap) && objects.equals(this.tokenColors, other.tokenColors); + } + + get type(): ThemeType { + let baseTheme = this.id.split(' ')[0]; + switch (baseTheme) { + case VS_LIGHT_THEME: return 'light'; + case VS_HC_THEME: return 'hc'; + default: return 'dark'; + } + } +} + +export function createUnloadedTheme(id: string): ColorThemeData { + let themeData = new ColorThemeData(); + themeData.id = id; + themeData.label = ''; + themeData.settingsId = null; + themeData.isLoaded = false; + themeData.themeTokenColors = [{ settings: {} }]; + return themeData; +} + +export function fromStorageData(input: string): ColorThemeData { + try { + let data = JSON.parse(input); + let theme = new ColorThemeData(); + for (let key in data) { + switch (key) { + case 'colorMap': + let colorMapData = data[key]; + for (let id in colorMapData) { + theme.colorMap[id] = Color.fromHex(colorMapData[id]); + } + break; + case 'themeTokenColors': + case 'id': case 'label': case 'settingsId': case 'extensionData': + theme[key] = data[key]; + break; + } + } + return theme; + } catch (e) { + return null; + } +} + +export function fromExtensionTheme(theme: IThemeExtensionPoint, normalizedAbsolutePath: string, extensionData: ExtensionData): ColorThemeData { + let baseTheme = theme['uiTheme'] || 'vs-dark'; + + let themeSelector = toCSSSelector(extensionData.extensionId + '-' + Paths.normalize(theme.path)); + let themeData = new ColorThemeData(); + themeData.id = `${baseTheme} ${themeSelector}`; + themeData.label = theme.label || Paths.basename(theme.path); + themeData.settingsId = theme.id || themeData.label; + themeData.description = theme.description; + themeData.path = normalizedAbsolutePath; + themeData.extensionData = extensionData; + themeData.isLoaded = false; + return themeData; +} + +function toCSSSelector(str: string) { + str = str.replace(/[^_\-a-zA-Z0-9]/g, '-'); + if (str.charAt(0).match(/[0-9\-]/)) { + str = '_' + str; + } + return str; +} + +function _loadColorThemeFromFile(themePath: string, resultRules: ITokenColorizationRule[], resultColors: IColorMap): TPromise { + if (Paths.extname(themePath) === '.json') { + return pfs.readFile(themePath).then(content => { + let errors: Json.ParseError[] = []; + let contentValue = Json.parse(content.toString(), errors); + if (errors.length > 0) { + return TPromise.wrapError(new Error(nls.localize('error.cannotparsejson', "Problems parsing JSON theme file: {0}", errors.map(e => getParseErrorMessage(e.error)).join(', ')))); + } + let includeCompletes = TPromise.as(null); + if (contentValue.include) { + includeCompletes = _loadColorThemeFromFile(Paths.join(Paths.dirname(themePath), contentValue.include), resultRules, resultColors); + } + return includeCompletes.then(_ => { + if (Array.isArray(contentValue.settings)) { + convertSettings(contentValue.settings, resultRules, resultColors); + return null; + } + let colors = contentValue.colors; + if (colors) { + if (typeof colors !== 'object') { + return TPromise.wrapError(new Error(nls.localize({ key: 'error.invalidformat.colors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'colors' is not of type 'object'.", themePath))); + } + // new JSON color themes format + for (let colorId in colors) { + let colorHex = colors[colorId]; + if (typeof colorHex === 'string') { // ignore colors tht are null + resultColors[colorId] = Color.fromHex(colors[colorId]); + } + } + } + let tokenColors = contentValue.tokenColors; + if (tokenColors) { + if (Array.isArray(tokenColors)) { + resultRules.push(...tokenColors); + return null; + } else if (typeof tokenColors === 'string') { + return _loadSyntaxTokensFromFile(Paths.join(Paths.dirname(themePath), tokenColors), resultRules, {}); + } else { + return TPromise.wrapError(new Error(nls.localize({ key: 'error.invalidformat.tokenColors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'tokenColors' should be either an array specifying colors or a path to a TextMate theme file", themePath))); + } + } + return null; + }); + }); + } else { + return _loadSyntaxTokensFromFile(themePath, resultRules, resultColors); + } +} + +function _loadSyntaxTokensFromFile(themePath: string, resultRules: ITokenColorizationRule[], resultColors: IColorMap): TPromise { + return pfs.readFile(themePath).then(content => { + try { + let contentValue = plist.parse(content.toString()); + let settings: ITokenColorizationRule[] = contentValue.settings; + if (!Array.isArray(settings)) { + return TPromise.wrapError(new Error(nls.localize('error.plist.invalidformat', "Problem parsing tmTheme file: {0}. 'settings' is not array."))); + } + convertSettings(settings, resultRules, resultColors); + return TPromise.as(null); + } catch (e) { + return TPromise.wrapError(new Error(nls.localize('error.cannotparse', "Problems parsing tmTheme file: {0}", e.message))); + } + }, error => { + return TPromise.wrapError(new Error(nls.localize('error.cannotload', "Problems loading tmTheme file {0}: {1}", themePath, error.message))); + }); +} +/** + * Place the default settings first and add add the token-info rules + */ +function _sanitizeTokenColors(theme: ColorThemeData) { + let hasDefaultTokens = false; + let updatedTokenColors: ITokenColorizationRule[] = [updateDefaultRuleSettings({ settings: {} }, theme)]; + theme.tokenColors.forEach(rule => { + if (rule.scope) { + if (rule.scope === 'token.info-token') { + hasDefaultTokens = true; + } + updatedTokenColors.push(rule); + } + }); + if (!hasDefaultTokens) { + updatedTokenColors.push(...defaultThemeColors[theme.type]); + } + theme.themeTokenColors = updatedTokenColors; +} + +function updateDefaultRuleSettings(defaultRule: ITokenColorizationRule, theme: ColorThemeData): ITokenColorizationRule { + let foreground = theme.getColor(editorForeground) || theme.getDefault(editorForeground); + let background = theme.getColor(editorBackground) || theme.getDefault(editorBackground); + defaultRule.settings.foreground = Color.Format.CSS.formatHexA(foreground); + defaultRule.settings.background = Color.Format.CSS.formatHexA(background); + return defaultRule; +} + + +let defaultThemeColors: { [baseTheme: string]: ITokenColorizationRule[] } = { + 'light': [ + { scope: 'token.info-token', settings: { foreground: '#316bcd' } }, + { scope: 'token.warn-token', settings: { foreground: '#cd9731' } }, + { scope: 'token.error-token', settings: { foreground: '#cd3131' } }, + { scope: 'token.debug-token', settings: { foreground: '#800080' } } + ], + 'dark': [ + { scope: 'token.info-token', settings: { foreground: '#6796e6' } }, + { scope: 'token.warn-token', settings: { foreground: '#cd9731' } }, + { scope: 'token.error-token', settings: { foreground: '#f44747' } }, + { scope: 'token.debug-token', settings: { foreground: '#b267e6' } } + ], + 'hc': [ + { scope: 'token.info-token', settings: { foreground: '#6796e6' } }, + { scope: 'token.warn-token', settings: { foreground: '#008000' } }, + { scope: 'token.error-token', settings: { foreground: '#FF0000' } }, + { scope: 'token.debug-token', settings: { foreground: '#b267e6' } } + ], +}; \ No newline at end of file diff --git a/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts b/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts new file mode 100644 index 0000000000..29df37b234 --- /dev/null +++ b/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITokenColorizationRule, IColorMap } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { Color } from 'vs/base/common/color'; +import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; + +import * as editorColorRegistry from 'vs/editor/common/view/editorColorRegistry'; +import * as wordHighlighter from 'vs/editor/contrib/wordHighlighter/common/wordHighlighter'; +import { peekViewEditorMatchHighlight, peekViewResultsMatchHighlight } from 'vs/editor/contrib/referenceSearch/browser/referencesWidget'; + +const settingToColorIdMapping: { [settingId: string]: string[] } = {}; +function addSettingMapping(settingId: string, colorId: string) { + let colorIds = settingToColorIdMapping[settingId]; + if (!colorIds) { + settingToColorIdMapping[settingId] = colorIds = []; + } + colorIds.push(colorId); +} + +export function convertSettings(oldSettings: ITokenColorizationRule[], resultRules: ITokenColorizationRule[], resultColors: IColorMap): void { + for (let rule of oldSettings) { + resultRules.push(rule); + if (!rule.scope) { + let settings = rule.settings; + if (!settings) { + rule.settings = {}; + } else { + for (let key in settings) { + let mappings = settingToColorIdMapping[key]; + if (mappings) { + let colorHex = settings[key]; + if (typeof colorHex === 'string') { + let color = Color.fromHex(colorHex); + for (let colorId of mappings) { + resultColors[colorId] = color; + } + } + } + if (key !== 'foreground' && key !== 'background' && key !== 'fontStyle') { + delete settings[key]; + } + } + } + } + } +} + +addSettingMapping('background', colorRegistry.editorBackground); +addSettingMapping('foreground', colorRegistry.editorForeground); +addSettingMapping('selection', colorRegistry.editorSelectionBackground); +addSettingMapping('inactiveSelection', colorRegistry.editorInactiveSelection); +addSettingMapping('selectionHighlightColor', colorRegistry.editorSelectionHighlight); +addSettingMapping('findMatchHighlight', colorRegistry.editorFindMatchHighlight); +addSettingMapping('currentFindMatchHighlight', colorRegistry.editorFindMatch); +addSettingMapping('hoverHighlight', colorRegistry.editorHoverHighlight); +addSettingMapping('wordHighlight', wordHighlighter.editorWordHighlight); +addSettingMapping('wordHighlightStrong', wordHighlighter.editorWordHighlightStrong); +addSettingMapping('findRangeHighlight', colorRegistry.editorFindRangeHighlight); +addSettingMapping('findMatchHighlight', peekViewResultsMatchHighlight); +addSettingMapping('referenceHighlight', peekViewEditorMatchHighlight); +addSettingMapping('lineHighlight', editorColorRegistry.editorLineHighlight); +addSettingMapping('rangeHighlight', editorColorRegistry.editorRangeHighlight); +addSettingMapping('caret', editorColorRegistry.editorCursorForeground); +addSettingMapping('invisibles', editorColorRegistry.editorWhitespaces); +addSettingMapping('guide', editorColorRegistry.editorIndentGuides); + +const ansiColorMap = ['ansiBlack', 'ansiRed', 'ansiGreen', 'ansiYellow', 'ansiBlue', 'ansiMagenta', 'ansiCyan', 'ansiWhite', + 'ansiBrightBlack', 'ansiBrightRed', 'ansiBrightGreen', 'ansiBrightYellow', 'ansiBrightBlue', 'ansiBrightMagenta', 'ansiBrightCyan', 'ansiBrightWhite' +]; + +for (let i = 0; i < ansiColorMap.length; i++) { + addSettingMapping(ansiColorMap[i], 'terminal.' + ansiColorMap[i]); +} + + diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts new file mode 100644 index 0000000000..36dddcf7dd --- /dev/null +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -0,0 +1,1062 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise, Promise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import nls = require('vs/nls'); +import * as Paths from 'path'; +import Json = require('vs/base/common/json'); +import * as types from 'vs/base/common/types'; +import * as objects from 'vs/base/common/objects'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry'; +import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, IThemeExtensionPoint, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, DEPRECATED_CUSTOM_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, CUSTOM_EDITOR_SCOPE_COLORS_SETTING } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import errors = require('vs/base/common/errors'); +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import Severity from 'vs/base/common/severity'; +import { ColorThemeData, fromStorageData, fromExtensionTheme, createUnloadedTheme } from './colorThemeData'; +import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; +import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { Color } from 'vs/base/common/color'; + +import { $ } from 'vs/base/browser/builder'; +import Event, { Emitter } from 'vs/base/common/event'; + +import pfs = require('vs/base/node/pfs'); + +import colorThemeSchema = require('vs/workbench/services/themes/common/colorThemeSchema'); +import fileIconThemeSchema = require('vs/workbench/services/themes/common/fileIconThemeSchema'); +import { IDisposable } from 'vs/base/common/lifecycle'; +import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; +import { IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService'; + +// implementation + // {{SQL CARBON EDIT}} +const DEFAULT_THEME_ID = 'vs sql-theme-carbon-themes-light_carbon-json'; +const DEFAULT_THEME_SETTING_VALUE = 'Default Light SQL Operations Studio'; + +const PERSISTED_THEME_STORAGE_KEY = 'colorThemeData'; + +// {{SQL CARBON EDIT}} +const defaultThemeExtensionId = 'sql-theme-carbon'; +const oldDefaultThemeExtensionId = 'vscode-theme-colorful-defaults'; + +const DEFAULT_ICON_THEME_SETTING_VALUE = 'vs-seti'; +const fileIconsEnabledClass = 'file-icons-enabled'; + +const themingRegistry = Registry.as(ThemingExtensions.ThemingContribution); + +function validateThemeId(theme: string): string { + // migrations + switch (theme) { + case VS_LIGHT_THEME: return `vs ${defaultThemeExtensionId}-themes-light_vs-json`; + case VS_DARK_THEME: return `vs-dark ${defaultThemeExtensionId}-themes-dark_vs-json`; + case VS_HC_THEME: return `hc-black ${defaultThemeExtensionId}-themes-hc_black-json`; + case `vs ${oldDefaultThemeExtensionId}-themes-light_plus-tmTheme`: return `vs ${defaultThemeExtensionId}-themes-light_plus-json`; + case `vs-dark ${oldDefaultThemeExtensionId}-themes-dark_plus-tmTheme`: return `vs-dark ${defaultThemeExtensionId}-themes-dark_plus-json`; + } + return theme; +} + +let themesExtPoint = ExtensionsRegistry.registerExtensionPoint('themes', [], { + description: nls.localize('vscode.extension.contributes.themes', 'Contributes textmate color themes.'), + type: 'array', + items: { + type: 'object', + defaultSnippets: [{ body: { label: '${1:label}', id: '${2:id}', uiTheme: VS_DARK_THEME, path: './themes/${3:id}.tmTheme.' } }], + properties: { + id: { + description: nls.localize('vscode.extension.contributes.themes.id', 'Id of the icon theme as used in the user settings.'), + type: 'string' + }, + label: { + description: nls.localize('vscode.extension.contributes.themes.label', 'Label of the color theme as shown in the UI.'), + type: 'string' + }, + uiTheme: { + description: nls.localize('vscode.extension.contributes.themes.uiTheme', 'Base theme defining the colors around the editor: \'vs\' is the light color theme, \'vs-dark\' is the dark color theme. \'hc-black\' is the dark high contrast theme.'), + enum: [VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME] + }, + path: { + description: nls.localize('vscode.extension.contributes.themes.path', 'Path of the tmTheme file. The path is relative to the extension folder and is typically \'./themes/themeFile.tmTheme\'.'), + type: 'string' + } + }, + required: ['path', 'uiTheme'] + } +}); + +let iconThemeExtPoint = ExtensionsRegistry.registerExtensionPoint('iconThemes', [], { + description: nls.localize('vscode.extension.contributes.iconThemes', 'Contributes file icon themes.'), + type: 'array', + items: { + type: 'object', + defaultSnippets: [{ body: { id: '${1:id}', label: '${2:label}', path: './fileicons/${3:id}-icon-theme.json' } }], + properties: { + id: { + description: nls.localize('vscode.extension.contributes.iconThemes.id', 'Id of the icon theme as used in the user settings.'), + type: 'string' + }, + label: { + description: nls.localize('vscode.extension.contributes.iconThemes.label', 'Label of the icon theme as shown in the UI.'), + type: 'string' + }, + path: { + description: nls.localize('vscode.extension.contributes.iconThemes.path', 'Path of the icon theme definition file. The path is relative to the extension folder and is typically \'./icons/awesome-icon-theme.json\'.'), + type: 'string' + } + }, + required: ['path', 'id'] + } +}); + +interface IInternalIconThemeData extends IFileIconTheme { + id: string; + label: string; + settingsId: string; + description?: string; + hasFileIcons?: boolean; + hasFolderIcons?: boolean; + isLoaded: boolean; + path?: string; + styleSheetContent?: string; + extensionData: ExtensionData; +} + +interface IconDefinition { + iconPath: string; + fontColor: string; + fontCharacter: string; + fontSize: string; + fontId: string; +} + +interface FontDefinition { + id: string; + weight: string; + style: string; + size: string; + src: { path: string; format: string; }[]; +} + +interface IconsAssociation { + folder?: string; + file?: string; + folderExpanded?: string; + rootFolder?: string; + rootFolderExpanded?: string; + folderNames?: { [folderName: string]: string; }; + folderNamesExpanded?: { [folderName: string]: string; }; + fileExtensions?: { [extension: string]: string; }; + fileNames?: { [fileName: string]: string; }; + languageIds?: { [languageId: string]: string; }; +} + +interface IconThemeDocument extends IconsAssociation { + iconDefinitions: { [key: string]: IconDefinition }; + fonts: FontDefinition[]; + light?: IconsAssociation; + highContrast?: IconsAssociation; +} + +export interface IColorCustomizations { + [colorId: string]: string; +} + +const noFileIconTheme: IFileIconTheme = { + id: '', + label: '', + settingsId: null, + hasFileIcons: false, + hasFolderIcons: false, + isLoaded: true, + extensionData: null +}; + +export class WorkbenchThemeService implements IWorkbenchThemeService { + _serviceBrand: any; + + private extensionsColorThemes: ColorThemeData[]; + private colorCustomizations: IColorCustomizations; + private tokenColorCustomizations: ITokenColorCustomizations; + private numberOfColorCustomizations: number; + private currentColorTheme: ColorThemeData; + private container: HTMLElement; + private onColorThemeChange: Emitter; + + private knownIconThemes: IInternalIconThemeData[]; + private currentIconTheme: IFileIconTheme; + private onFileIconThemeChange: Emitter; + + private themingParticipantChangeListener: IDisposable; + private _configurationWriter: ConfigurationWriter; + + constructor( + container: HTMLElement, + @IExtensionService private extensionService: IExtensionService, + @IStorageService private storageService: IStorageService, + @IBroadcastService private broadcastService: IBroadcastService, + @IConfigurationService private configurationService: IConfigurationService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IMessageService private messageService: IMessageService, + @ITelemetryService private telemetryService: ITelemetryService, + @IInstantiationService private instantiationService: IInstantiationService) { + + this.container = container; + this.extensionsColorThemes = []; + this.colorCustomizations = {}; + this.tokenColorCustomizations = {}; + this.onFileIconThemeChange = new Emitter(); + this.knownIconThemes = []; + this.onColorThemeChange = new Emitter(); + + this.currentIconTheme = { + id: '', + label: '', + settingsId: null, + isLoaded: false, + hasFileIcons: false, + hasFolderIcons: false, + extensionData: null + }; + + this.updateColorCustomizations(false); + + // In order to avoid paint flashing for tokens, because + // themes are loaded asynchronously, we need to initialize + // a color theme document with good defaults until the theme is loaded + let themeData = null; + let persistedThemeData = this.storageService.get(PERSISTED_THEME_STORAGE_KEY); + if (persistedThemeData) { + themeData = fromStorageData(persistedThemeData); + } + if (!themeData) { + let isLightTheme = (Array.prototype.indexOf.call(document.body.classList, 'vs') >= 0); + themeData = createUnloadedTheme(isLightTheme ? VS_LIGHT_THEME : VS_DARK_THEME); + } + themeData.setCustomColors(this.colorCustomizations); + themeData.setCustomTokenColors(this.tokenColorCustomizations); + this.updateDynamicCSSRules(themeData); + this.applyTheme(themeData, null, true); + + themesExtPoint.setHandler((extensions) => { + for (let ext of extensions) { + let extensionData = { + extensionId: ext.description.id, + extensionPublisher: ext.description.publisher, + extensionName: ext.description.name, + extensionIsBuiltin: ext.description.isBuiltin + }; + this.onThemes(ext.description.extensionFolderPath, extensionData, ext.value, ext.collector); + } + }); + + iconThemeExtPoint.setHandler((extensions) => { + for (let ext of extensions) { + let extensionData = { + extensionId: ext.description.id, + extensionPublisher: ext.description.publisher, + extensionName: ext.description.name, + extensionIsBuiltin: ext.description.isBuiltin + }; + this.onIconThemes(ext.description.extensionFolderPath, extensionData, ext.value, ext.collector); + } + }); + + this.migrate().then(_ => { + this.initialize().then(null, errors.onUnexpectedError).then(_ => { + this.installConfigurationListener(); + }); + }); + } + + public get onDidColorThemeChange(): Event { + return this.onColorThemeChange.event; + } + + public get onDidFileIconThemeChange(): Event { + return this.onFileIconThemeChange.event; + } + + public get onThemeChange(): Event { + return this.onColorThemeChange.event; + } + + private backupSettings(): TPromise { + let resource = this.environmentService.appSettingsPath; + let backupFileLocation = resource + '-' + new Date().getTime() + '.backup'; + return pfs.readFile(resource).then(content => { + return pfs.writeFile(backupFileLocation, content).then(_ => backupFileLocation); + }, err => { + if (err && err.code === 'ENOENT') { + return TPromise.as(null); // ignore, user config file doesn't exist yet + }; + return TPromise.wrapError(err); + }); + } + + private migrate(): TPromise { + let legacyColorThemeId = this.storageService.get('workbench.theme', StorageScope.GLOBAL, void 0); + let legacyIconThemeId = this.storageService.get('workbench.iconTheme', StorageScope.GLOBAL, void 0); + if (types.isUndefined(legacyColorThemeId) && types.isUndefined(legacyIconThemeId)) { + return TPromise.as(null); + } + return this.backupSettings().then(backupLocation => { + let promise = TPromise.as(null); + if (!types.isUndefined(legacyColorThemeId)) { + this.storageService.remove('workbench.theme', StorageScope.GLOBAL); + promise = this.findThemeData(legacyColorThemeId, DEFAULT_THEME_ID).then(theme => { + let value = theme ? theme.settingsId : DEFAULT_THEME_SETTING_VALUE; + return this.configurationWriter.writeConfiguration(COLOR_THEME_SETTING, value, ConfigurationTarget.USER).then(null, error => null); + }); + } + if (!types.isUndefined(legacyIconThemeId)) { + this.storageService.remove('workbench.iconTheme', StorageScope.GLOBAL); + promise = promise.then(_ => { + return this._findIconThemeData(legacyIconThemeId).then(theme => { + let value = theme ? theme.settingsId : null; + return this.configurationWriter.writeConfiguration(ICON_THEME_SETTING, value, ConfigurationTarget.USER).then(null, error => null); + }); + }); + } + return promise.then(_ => { + if (backupLocation) { + let message = nls.localize('migration.completed', 'New theme settings have been added to the user settings. Backup available at {0}.', backupLocation); + this.messageService.show(Severity.Info, message); + console.log(message); + } + }); + }); + } + + private initialize(): TPromise<[IColorTheme, IFileIconTheme]> { + + this.updateColorCustomizations(false); + + let colorThemeSetting = this.configurationService.lookup(COLOR_THEME_SETTING).value; + let iconThemeSetting = this.configurationService.lookup(ICON_THEME_SETTING).value || ''; + + return Promise.join([ + this.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + return this.setColorTheme(theme && theme.id, null); + }), + this.findIconThemeBySettingsId(iconThemeSetting).then(theme => { + return this.setFileIconTheme(theme && theme.id, null); + }), + ]); + } + + private installConfigurationListener() { + this.configurationService.onDidUpdateConfiguration(e => { + let colorThemeSetting = this.configurationService.lookup(COLOR_THEME_SETTING).value; + if (colorThemeSetting !== this.currentColorTheme.settingsId) { + this.findThemeDataBySettingsId(colorThemeSetting, null).then(theme => { + if (theme) { + this.setColorTheme(theme.id, null); + } + }); + } + + let iconThemeSetting = this.configurationService.lookup(ICON_THEME_SETTING).value || ''; + if (iconThemeSetting !== this.currentIconTheme.settingsId) { + this.findIconThemeBySettingsId(iconThemeSetting).then(theme => { + this.setFileIconTheme(theme && theme.id, null); + }); + } + + this.updateColorCustomizations(); + }); + } + + public getTheme(): ITheme { + return this.getColorTheme(); + } + + public setColorTheme(themeId: string, settingsTarget: ConfigurationTarget): TPromise { + if (!themeId) { + return TPromise.as(null); + } + if (themeId === this.currentColorTheme.id && this.currentColorTheme.isLoaded) { + return this.writeColorThemeConfiguration(settingsTarget); + } + + themeId = validateThemeId(themeId); // migrate theme ids + + + return this.findThemeData(themeId, DEFAULT_THEME_ID).then(themeData => { + if (themeData) { + return themeData.ensureLoaded(this).then(_ => { + if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { + // the loaded theme is identical to the perisisted theme. Don't need to send an event. + this.currentColorTheme = themeData; + themeData.setCustomColors(this.colorCustomizations); + themeData.setCustomTokenColors(this.tokenColorCustomizations); + return TPromise.as(themeData); + } + themeData.setCustomColors(this.colorCustomizations); + themeData.setCustomTokenColors(this.tokenColorCustomizations); + this.updateDynamicCSSRules(themeData); + return this.applyTheme(themeData, settingsTarget); + }, error => { + return TPromise.wrapError(new Error(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.path, error.message))); + }); + } + return null; + }); + } + + private updateDynamicCSSRules(themeData: ITheme) { + let cssRules = []; + let hasRule = {}; + let ruleCollector = { + addRule: (rule: string) => { + if (!hasRule[rule]) { + cssRules.push(rule); + hasRule[rule] = true; + } + } + }; + themingRegistry.getThemingParticipants().forEach(p => p(themeData, ruleCollector)); + _applyRules(cssRules.join('\n'), colorThemeRulesClassName); + } + + private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget, silent = false): TPromise { + if (this.container) { + if (this.currentColorTheme) { + $(this.container).removeClass(this.currentColorTheme.id); + } else { + $(this.container).removeClass(VS_DARK_THEME, VS_LIGHT_THEME, VS_HC_THEME); + } + $(this.container).addClass(newTheme.id); + } + this.currentColorTheme = newTheme; + if (!this.themingParticipantChangeListener) { + this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(p => this.updateDynamicCSSRules(this.currentColorTheme)); + } + + this.sendTelemetry(newTheme.id, newTheme.extensionData, 'color'); + + if (silent) { + return TPromise.as(null); + } + + this.onColorThemeChange.fire(this.currentColorTheme); + + if (settingsTarget !== ConfigurationTarget.WORKSPACE) { + let background = Color.Format.CSS.formatHex(newTheme.getColor(editorBackground)); // only take RGB, its what is used in the initial CSS + let data = { id: newTheme.id, background: background }; + this.broadcastService.broadcast({ channel: 'vscode:changeColorTheme', payload: JSON.stringify(data) }); + } + // remember theme data for a quick restore + this.storageService.store(PERSISTED_THEME_STORAGE_KEY, newTheme.toStorageData()); + + return this.writeColorThemeConfiguration(settingsTarget); + }; + + private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget): TPromise { + if (!types.isUndefinedOrNull(settingsTarget)) { + return this.configurationWriter.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme); + } + return TPromise.as(this.currentColorTheme); + } + + public getColorTheme(): IColorTheme { + return this.currentColorTheme; + } + + private findThemeData(themeId: string, defaultId?: string): TPromise { + return this.getColorThemes().then(allThemes => { + let defaultTheme: ColorThemeData = void 0; + for (let t of allThemes) { + if (t.id === themeId) { + return t; + } + if (t.id === defaultId) { + defaultTheme = t; + } + } + return defaultTheme; + }); + } + + public findThemeDataBySettingsId(settingsId: string, defaultId: string): TPromise { + return this.getColorThemes().then(allThemes => { + let defaultTheme: ColorThemeData = void 0; + for (let t of allThemes) { + if (t.settingsId === settingsId) { + return t; + } + if (t.id === defaultId) { + defaultTheme = t; + } + } + return defaultTheme; + }); + } + + public getColorThemes(): TPromise { + return this.extensionService.onReady().then(isReady => { + return this.extensionsColorThemes; + }); + } + + private hasCustomizationChanged(newColorCustomizations: IColorCustomizations, newColorIds: string[], newTokenColorCustomizations: ITokenColorCustomizations): boolean { + if (newColorIds.length !== this.numberOfColorCustomizations) { + return true; + } + for (let key of newColorIds) { + let color = this.colorCustomizations[key]; + if (!color || color !== newColorCustomizations[key]) { + return true; + } + } + + if (!objects.equals(newTokenColorCustomizations, this.tokenColorCustomizations)) { + return true; + } + + return false; + } + + private updateColorCustomizations(notify = true): void { + let newColorCustomizations = this.configurationService.lookup(CUSTOM_WORKBENCH_COLORS_SETTING).value || {}; + let newColorIds = Object.keys(newColorCustomizations); + if (newColorIds.length === 0) { + newColorCustomizations = this.configurationService.lookup(DEPRECATED_CUSTOM_COLORS_SETTING).value || {}; + newColorIds = Object.keys(newColorCustomizations); + } + + let newTokenColorCustomizations = this.configurationService.lookup(CUSTOM_EDITOR_COLORS_SETTING).value || {}; + + if (this.hasCustomizationChanged(newColorCustomizations, newColorIds, newTokenColorCustomizations)) { + this.colorCustomizations = newColorCustomizations; + this.numberOfColorCustomizations = newColorIds.length; + this.tokenColorCustomizations = newTokenColorCustomizations; + + if (this.currentColorTheme) { + this.currentColorTheme.setCustomColors(newColorCustomizations); + this.currentColorTheme.setCustomTokenColors(newTokenColorCustomizations); + if (notify) { + this.updateDynamicCSSRules(this.currentColorTheme); + this.onColorThemeChange.fire(this.currentColorTheme); + } + } + } + } + + private onThemes(extensionFolderPath: string, extensionData: ExtensionData, themes: IThemeExtensionPoint[], collector: ExtensionMessageCollector): void { + if (!Array.isArray(themes)) { + collector.error(nls.localize( + 'reqarray', + "Extension point `{0}` must be an array.", + themesExtPoint.name + )); + return; + } + themes.forEach(theme => { + if (!theme.path || !types.isString(theme.path)) { + collector.error(nls.localize( + 'reqpath', + "Expected string in `contributes.{0}.path`. Provided value: {1}", + themesExtPoint.name, + String(theme.path) + )); + return; + } + let normalizedAbsolutePath = Paths.normalize(Paths.join(extensionFolderPath, theme.path)); + + if (normalizedAbsolutePath.indexOf(Paths.normalize(extensionFolderPath)) !== 0) { + collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", themesExtPoint.name, normalizedAbsolutePath, extensionFolderPath)); + } + let themeData = fromExtensionTheme(theme, normalizedAbsolutePath, extensionData); + this.extensionsColorThemes.push(themeData); + + colorThemeSettingSchema.enum.push(themeData.settingsId); + colorThemeSettingSchema.enumDescriptions.push(themeData.description || ''); + }); + } + + private onIconThemes(extensionFolderPath: string, extensionData: ExtensionData, iconThemes: IThemeExtensionPoint[], collector: ExtensionMessageCollector): void { + if (!Array.isArray(iconThemes)) { + collector.error(nls.localize( + 'reqarray', + "Extension point `{0}` must be an array.", + themesExtPoint.name + )); + return; + } + iconThemes.forEach(iconTheme => { + if (!iconTheme.path || !types.isString(iconTheme.path)) { + collector.error(nls.localize( + 'reqpath', + "Expected string in `contributes.{0}.path`. Provided value: {1}", + themesExtPoint.name, + String(iconTheme.path) + )); + return; + } + if (!iconTheme.id || !types.isString(iconTheme.id)) { + collector.error(nls.localize( + 'reqid', + "Expected string in `contributes.{0}.id`. Provided value: {1}", + themesExtPoint.name, + String(iconTheme.path) + )); + return; + } + let normalizedAbsolutePath = Paths.normalize(Paths.join(extensionFolderPath, iconTheme.path)); + + if (normalizedAbsolutePath.indexOf(Paths.normalize(extensionFolderPath)) !== 0) { + collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", themesExtPoint.name, normalizedAbsolutePath, extensionFolderPath)); + } + + let themeData = { + id: extensionData.extensionId + '-' + iconTheme.id, + label: iconTheme.label || Paths.basename(iconTheme.path), + settingsId: iconTheme.id, + description: iconTheme.description, + path: normalizedAbsolutePath, + extensionData: extensionData, + isLoaded: false + }; + this.knownIconThemes.push(themeData); + + iconThemeSettingSchema.enum.push(themeData.settingsId); + iconThemeSettingSchema.enumDescriptions.push(themeData.description || ''); + }); + } + + private themeExtensionsActivated = new Map(); + private sendTelemetry(themeId: string, themeData: ExtensionData, themeType: string) { + if (themeData) { + let key = themeType + themeData.extensionId; + if (!this.themeExtensionsActivated.get(key)) { + this.telemetryService.publicLog('activatePlugin', { + id: themeData.extensionId, + name: themeData.extensionName, + isBuiltin: themeData.extensionIsBuiltin, + publisherDisplayName: themeData.extensionPublisher, + themeId: themeId + }); + this.themeExtensionsActivated.set(key, true); + } + } + } + + public getFileIconThemes(): TPromise { + return this.extensionService.onReady().then(isReady => { + return this.knownIconThemes; + }); + } + + public getFileIconTheme() { + return this.currentIconTheme; + } + + public setFileIconTheme(iconTheme: string, settingsTarget: ConfigurationTarget): TPromise { + iconTheme = iconTheme || ''; + if (iconTheme === this.currentIconTheme.id && this.currentIconTheme.isLoaded) { + return this.writeFileIconConfiguration(settingsTarget); + } + let onApply = (newIconTheme: IInternalIconThemeData) => { + if (newIconTheme) { + this.currentIconTheme = newIconTheme; + } else { + this.currentIconTheme = noFileIconTheme; + } + + if (this.container) { + if (newIconTheme) { + $(this.container).addClass(fileIconsEnabledClass); + } else { + $(this.container).removeClass(fileIconsEnabledClass); + } + } + if (newIconTheme) { + this.sendTelemetry(newIconTheme.id, newIconTheme.extensionData, 'fileIcon'); + } + this.onFileIconThemeChange.fire(this.currentIconTheme); + return this.writeFileIconConfiguration(settingsTarget); + }; + + return this._findIconThemeData(iconTheme).then(iconThemeData => { + return _applyIconTheme(iconThemeData, onApply); + }); + } + + private writeFileIconConfiguration(settingsTarget: ConfigurationTarget): TPromise { + if (!types.isUndefinedOrNull(settingsTarget)) { + return this.configurationWriter.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then(_ => this.currentIconTheme); + } + return TPromise.as(this.currentIconTheme); + } + + private get configurationWriter(): ConfigurationWriter { + // separate out the ConfigurationWriter to avoid a dependency of the IConfigurationEditingService + if (!this._configurationWriter) { + this._configurationWriter = this.instantiationService.createInstance(ConfigurationWriter); + } + return this._configurationWriter; + } + + private _findIconThemeData(iconTheme: string): TPromise { + return this.getFileIconThemes().then(allIconSets => { + for (let iconSet of allIconSets) { + if (iconSet.id === iconTheme) { + return iconSet; + } + } + return null; + }); + } + + private findIconThemeBySettingsId(settingsId: string): TPromise { + return this.getFileIconThemes().then(allIconSets => { + for (let iconSet of allIconSets) { + if (iconSet.settingsId === settingsId) { + return iconSet; + } + } + return null; + }); + } +} + +function _applyIconTheme(data: IInternalIconThemeData, onApply: (theme: IInternalIconThemeData) => TPromise): TPromise { + if (!data) { + _applyRules('', iconThemeRulesClassName); + return TPromise.as(onApply(data)); + } + + if (data.styleSheetContent) { + _applyRules(data.styleSheetContent, iconThemeRulesClassName); + return TPromise.as(onApply(data)); + } + return _loadIconThemeDocument(data.path).then(iconThemeDocument => { + let result = _processIconThemeDocument(data.id, data.path, iconThemeDocument); + data.styleSheetContent = result.content; + data.hasFileIcons = result.hasFileIcons; + data.hasFolderIcons = result.hasFolderIcons; + data.isLoaded = true; + _applyRules(data.styleSheetContent, iconThemeRulesClassName); + return onApply(data); + }, error => { + return TPromise.wrapError(new Error(nls.localize('error.cannotloadicontheme', "Unable to load {0}", data.path))); + }); +} + +function _loadIconThemeDocument(fileSetPath: string): TPromise { + return pfs.readFile(fileSetPath).then(content => { + let errors: Json.ParseError[] = []; + let contentValue = Json.parse(content.toString(), errors); + if (errors.length > 0) { + return TPromise.wrapError(new Error(nls.localize('error.cannotparseicontheme', "Problems parsing file icons file: {0}", errors.map(e => getParseErrorMessage(e.error)).join(', ')))); + } + return TPromise.as(contentValue); + }); +} + +function _processIconThemeDocument(id: string, iconThemeDocumentPath: string, iconThemeDocument: IconThemeDocument): { content: string; hasFileIcons: boolean; hasFolderIcons: boolean; } { + + let result = { content: '', hasFileIcons: false, hasFolderIcons: false }; + + if (!iconThemeDocument.iconDefinitions) { + return result; + } + let selectorByDefinitionId: { [def: string]: string[] } = {}; + + function resolvePath(path: string) { + const uri = URI.file(Paths.join(Paths.dirname(iconThemeDocumentPath), path)); + return uri.toString(); + } + + function collectSelectors(associations: IconsAssociation, baseThemeClassName?: string) { + function addSelector(selector: string, defId: string) { + if (defId) { + let list = selectorByDefinitionId[defId]; + if (!list) { + list = selectorByDefinitionId[defId] = []; + } + list.push(selector); + } + } + if (associations) { + let qualifier = '.show-file-icons'; + if (baseThemeClassName) { + qualifier = baseThemeClassName + ' ' + qualifier; + } + + let expanded = '.monaco-tree-row.expanded'; // workaround for #11453 + + if (associations.folder) { + addSelector(`${qualifier} .folder-icon::before`, associations.folder); + result.hasFolderIcons = true; + } + + if (associations.folderExpanded) { + addSelector(`${qualifier} ${expanded} .folder-icon::before`, associations.folderExpanded); + result.hasFolderIcons = true; + } + + let rootFolder = associations.rootFolder || associations.folder; + let rootFolderExpanded = associations.rootFolderExpanded || associations.folderExpanded; + + if (rootFolder) { + addSelector(`${qualifier} .rootfolder-icon::before`, rootFolder); + result.hasFolderIcons = true; + } + + if (rootFolderExpanded) { + addSelector(`${qualifier} ${expanded} .rootfolder-icon::before`, rootFolderExpanded); + result.hasFolderIcons = true; + } + + if (associations.file) { + addSelector(`${qualifier} .file-icon::before`, associations.file); + result.hasFileIcons = true; + } + + let folderNames = associations.folderNames; + if (folderNames) { + for (let folderName in folderNames) { + addSelector(`${qualifier} .${escapeCSS(folderName.toLowerCase())}-name-folder-icon.folder-icon::before`, folderNames[folderName]); + result.hasFolderIcons = true; + } + } + let folderNamesExpanded = associations.folderNamesExpanded; + if (folderNamesExpanded) { + for (let folderName in folderNamesExpanded) { + addSelector(`${qualifier} ${expanded} .${escapeCSS(folderName.toLowerCase())}-name-folder-icon.folder-icon::before`, folderNamesExpanded[folderName]); + result.hasFolderIcons = true; + } + } + + let languageIds = associations.languageIds; + if (languageIds) { + for (let languageId in languageIds) { + addSelector(`${qualifier} .${escapeCSS(languageId)}-lang-file-icon.file-icon::before`, languageIds[languageId]); + result.hasFileIcons = true; + } + } + let fileExtensions = associations.fileExtensions; + if (fileExtensions) { + for (let fileExtension in fileExtensions) { + let selectors: string[] = []; + let segments = fileExtension.toLowerCase().split('.'); + for (let i = 0; i < segments.length; i++) { + selectors.push(`.${escapeCSS(segments.slice(i).join('.'))}-ext-file-icon`); + } + addSelector(`${qualifier} ${selectors.join('')}.file-icon::before`, fileExtensions[fileExtension]); + result.hasFileIcons = true; + } + } + let fileNames = associations.fileNames; + if (fileNames) { + for (let fileName in fileNames) { + let selectors: string[] = []; + fileName = fileName.toLowerCase(); + selectors.push(`.${escapeCSS(fileName)}-name-file-icon`); + let segments = fileName.split('.'); + for (let i = 1; i < segments.length; i++) { + selectors.push(`.${escapeCSS(segments.slice(i).join('.'))}-ext-file-icon`); + } + addSelector(`${qualifier} ${selectors.join('')}.file-icon::before`, fileNames[fileName]); + result.hasFileIcons = true; + } + } + } + } + collectSelectors(iconThemeDocument); + collectSelectors(iconThemeDocument.light, '.vs'); + collectSelectors(iconThemeDocument.highContrast, '.hc-black'); + + if (!result.hasFileIcons && !result.hasFolderIcons) { + return result; + } + + let cssRules: string[] = []; + + let fonts = iconThemeDocument.fonts; + if (Array.isArray(fonts)) { + fonts.forEach(font => { + let src = font.src.map(l => `url('${resolvePath(l.path)}') format('${l.format}')`).join(', '); + cssRules.push(`@font-face { src: ${src}; font-family: '${font.id}'; font-weigth: ${font.weight}; font-style: ${font.style}; }`); + }); + cssRules.push(`.show-file-icons .file-icon::before, .show-file-icons .folder-icon::before, .show-file-icons .rootfolder-icon::before { font-family: '${fonts[0].id}'; font-size: ${fonts[0].size || '150%'}}`); + } + + for (let defId in selectorByDefinitionId) { + let selectors = selectorByDefinitionId[defId]; + let definition = iconThemeDocument.iconDefinitions[defId]; + if (definition) { + if (definition.iconPath) { + cssRules.push(`${selectors.join(', ')} { content: ' '; background-image: url("${resolvePath(definition.iconPath)}"); }`); + } + if (definition.fontCharacter || definition.fontColor) { + let body = ''; + if (definition.fontColor) { + body += ` color: ${definition.fontColor};`; + } + if (definition.fontCharacter) { + body += ` content: '${definition.fontCharacter}';`; + } + if (definition.fontSize) { + body += ` font-size: ${definition.fontSize};`; + } + if (definition.fontId) { + body += ` font-family: ${definition.fontId};`; + } + cssRules.push(`${selectors.join(', ')} { ${body} }`); + } + } + } + result.content = cssRules.join('\n'); + return result; +} + +function escapeCSS(str: string) { + return window['CSS'].escape(str); +} + +let colorThemeRulesClassName = 'contributedColorTheme'; +let iconThemeRulesClassName = 'contributedIconTheme'; + +function _applyRules(styleSheetContent: string, rulesClassName: string) { + let themeStyles = document.head.getElementsByClassName(rulesClassName); + if (themeStyles.length === 0) { + let elStyle = document.createElement('style'); + elStyle.type = 'text/css'; + elStyle.className = rulesClassName; + elStyle.innerHTML = styleSheetContent; + document.head.appendChild(elStyle); + } else { + (themeStyles[0]).innerHTML = styleSheetContent; + } +} + +colorThemeSchema.register(); +fileIconThemeSchema.register(); + +class ConfigurationWriter { + constructor( @IConfigurationService private configurationService: IConfigurationService, @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService) { + } + + public writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget): TPromise { + let settings = this.configurationService.lookup(key); + if (settingsTarget === ConfigurationTarget.USER) { + if (value === settings.user) { + return TPromise.as(null); // nothing to do + } else if (value === settings.default) { + if (types.isUndefined(settings.user)) { + return TPromise.as(null); // nothing to do + } + value = void 0; // remove configuration from user settings + } + } else if (settingsTarget === ConfigurationTarget.WORKSPACE) { + if (value === settings.value) { + return TPromise.as(null); // nothing to do + } + } + return this.configurationEditingService.writeConfiguration(settingsTarget, { key, value }); + } +} + +// Configuration: Themes +const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + +const colorThemeSettingSchema: IJSONSchema = { + type: 'string', + description: nls.localize('colorTheme', "Specifies the color theme used in the workbench."), + default: DEFAULT_THEME_SETTING_VALUE, + enum: [], + enumDescriptions: [], + errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), +}; +const iconThemeSettingSchema: IJSONSchema = { + type: ['string', 'null'], + default: DEFAULT_ICON_THEME_SETTING_VALUE, + description: nls.localize('iconTheme', "Specifies the icon theme used in the workbench or 'null' to not show any file icons."), + enum: [null], + enumDescriptions: [nls.localize('noIconThemeDesc', 'No file icons')], + errorMessage: nls.localize('iconThemeError', "File icon theme is unknown or not installed.") +}; +const colorCustomizationsSchema: IJSONSchema = { + type: ['object'], + description: nls.localize('workbenchColors', "Overrides colors from the currently selected color theme."), + properties: colorThemeSchema.colorsSchema.properties, + default: {}, + defaultSnippets: [{ + body: { + 'statusBar.background': '#666666', + 'panel.background': '#555555', + 'sideBar.background': '#444444' + } + }] +}; + +const deprecatedColorCustomizationsSchema: IJSONSchema = objects.mixin({ + deprecationMessage: nls.localize('workbenchColors.deprecated', "The setting is no longer experimental and has been renamed to 'workbench.colorCustomizations'"), + description: nls.localize('workbenchColors.deprecatedDescription', "Use 'workbench.colorCustomizations' instead") +}, colorCustomizationsSchema, false); + +configurationRegistry.registerConfiguration({ + id: 'workbench', + order: 7.1, + type: 'object', + properties: { + [COLOR_THEME_SETTING]: colorThemeSettingSchema, + [ICON_THEME_SETTING]: iconThemeSettingSchema, + [CUSTOM_WORKBENCH_COLORS_SETTING]: colorCustomizationsSchema, + [DEPRECATED_CUSTOM_COLORS_SETTING]: deprecatedColorCustomizationsSchema + } +}); + +function tokenGroupSettings(description: string) { + return { + description, + default: '#FF0000', + anyOf: [ + { + type: 'string', + format: 'color', + defaultSnippets: [{ body: '#FF0000' }] + }, + colorThemeSchema.tokenColorizationSettingSchema + ] + }; +}; + +configurationRegistry.registerConfiguration({ + id: 'editor', + order: 7.2, + type: 'object', + properties: { + [CUSTOM_EDITOR_COLORS_SETTING]: { + description: nls.localize('editorColors', "Overrides editor colors and font style from the currently selected color theme."), + default: {}, + properties: { + comments: tokenGroupSettings(nls.localize('editorColors.comments', "Sets the colors and styles for comments")), + strings: tokenGroupSettings(nls.localize('editorColors.strings', "Sets the colors and styles for strings literals.")), + keywords: tokenGroupSettings(nls.localize('editorColors.keywords', "Sets the colors and styles for keywords.")), + numbers: tokenGroupSettings(nls.localize('editorColors.numbers', "Sets the colors and styles for number literals.")), + types: tokenGroupSettings(nls.localize('editorColors.types', "Sets the colors and styles for type declarations and references.")), + functions: tokenGroupSettings(nls.localize('editorColors.functions', "Sets the colors and styles for functions declarations and references.")), + variables: tokenGroupSettings(nls.localize('editorColors.variables', "Sets the colors and styles for variables declarations and references.")), + [CUSTOM_EDITOR_SCOPE_COLORS_SETTING]: colorThemeSchema.tokenColorsSchema(nls.localize('editorColors.textMateRules', 'Sets colors and styles using textmate theming rules (advanced).')) + } + } + } +}); + diff --git a/src/vs/workbench/services/thread/common/threadService.ts b/src/vs/workbench/services/thread/common/threadService.ts new file mode 100644 index 0000000000..2b733f8e28 --- /dev/null +++ b/src/vs/workbench/services/thread/common/threadService.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +export interface IThreadService { + /** + * Always returns a proxy. + */ + get(identifier: ProxyIdentifier): T; + + /** + * Register instance. + */ + set(identifier: ProxyIdentifier, value: R): R; + + /** + * Assert these identifiers are already registered via `.set`. + */ + assertRegistered(identifiers: ProxyIdentifier[]): void; +} + +export class ProxyIdentifier { + _proxyIdentifierBrand: void; + + isMain: boolean; + id: string; + + constructor(isMain: boolean, id: string) { + this.isMain = isMain; + this.id = id; + } +} + +export function createMainContextProxyIdentifier(identifier: string): ProxyIdentifier { + return new ProxyIdentifier(true, 'm' + identifier); +} + +export function createExtHostContextProxyIdentifier(identifier: string): ProxyIdentifier { + return new ProxyIdentifier(false, 'e' + identifier); +} diff --git a/src/vs/workbench/services/thread/electron-browser/threadService.ts b/src/vs/workbench/services/thread/electron-browser/threadService.ts new file mode 100644 index 0000000000..983d898490 --- /dev/null +++ b/src/vs/workbench/services/thread/electron-browser/threadService.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as strings from 'vs/base/common/strings'; +import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; +import { AbstractThreadService } from 'vs/workbench/services/thread/node/abstractThreadService'; +import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; + +// Enable to see detailed message communication between window and extension host +const logExtensionHostCommunication = false; + + +function asLoggingProtocol(protocol: IMessagePassingProtocol): IMessagePassingProtocol { + + protocol.onMessage(msg => { + console.log('%c[Extension \u2192 Window]%c[len: ' + strings.pad(msg.length, 5, ' ') + ']', 'color: darkgreen', 'color: grey', msg); + }); + + return { + onMessage: protocol.onMessage, + + send(msg: any) { + protocol.send(msg); + console.log('%c[Window \u2192 Extension]%c[len: ' + strings.pad(msg.length, 5, ' ') + ']', 'color: darkgreen', 'color: grey', msg); + } + }; +} + + +export class MainThreadService extends AbstractThreadService implements IThreadService { + constructor(protocol: IMessagePassingProtocol, @IEnvironmentService environmentService: IEnvironmentService) { + if (logExtensionHostCommunication || environmentService.logExtensionHostCommunication) { + protocol = asLoggingProtocol(protocol); + } + + super(new RPCProtocol(protocol), true); + } +} diff --git a/src/vs/workbench/services/thread/node/abstractThreadService.ts b/src/vs/workbench/services/thread/node/abstractThreadService.ts new file mode 100644 index 0000000000..787dbf1a89 --- /dev/null +++ b/src/vs/workbench/services/thread/node/abstractThreadService.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDispatcher, RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; +import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService'; + +declare var Proxy: any; // TODO@TypeScript + +export abstract class AbstractThreadService implements IDispatcher { + + private readonly _rpcProtocol: RPCProtocol; + private readonly _isMain: boolean; + protected readonly _locals: { [id: string]: any; }; + private readonly _proxies: { [id: string]: any; } = Object.create(null); + + constructor(rpcProtocol: RPCProtocol, isMain: boolean) { + this._rpcProtocol = rpcProtocol; + this._isMain = isMain; + this._locals = Object.create(null); + this._proxies = Object.create(null); + this._rpcProtocol.setDispatcher(this); + } + + public dispose(): void { + this._rpcProtocol.dispose(); + } + + public invoke(proxyId: string, methodName: string, args: any[]): any { + if (!this._locals[proxyId]) { + throw new Error('Unknown actor ' + proxyId); + } + let actor = this._locals[proxyId]; + let method = actor[methodName]; + if (typeof method !== 'function') { + throw new Error('Unknown method ' + methodName + ' on actor ' + proxyId); + } + return method.apply(actor, args); + } + + get(identifier: ProxyIdentifier): T { + if (!this._proxies[identifier.id]) { + this._proxies[identifier.id] = this._createProxy(identifier.id); + } + return this._proxies[identifier.id]; + } + + private _createProxy(proxyId: string): T { + let handler = { + get: (target, name) => { + if (!target[name]) { + target[name] = (...myArgs: any[]) => { + return this._callOnRemote(proxyId, name, myArgs); + }; + } + return target[name]; + } + }; + return new Proxy(Object.create(null), handler); + } + + set(identifier: ProxyIdentifier, value: R): R { + if (identifier.isMain !== this._isMain) { + throw new Error('Mismatch in object registration!'); + } + this._locals[identifier.id] = value; + return value; + } + + assertRegistered(identifiers: ProxyIdentifier[]): void { + for (let i = 0, len = identifiers.length; i < len; i++) { + const identifier = identifiers[i]; + if (!this._locals[identifier.id]) { + throw new Error(`Missing actor ${identifier.id} (isMain: ${identifier.isMain})`); + } + } + } + + private _callOnRemote(proxyId: string, methodName: string, args: any[]): TPromise { + return this._rpcProtocol.callOnRemote(proxyId, methodName, args); + } +} diff --git a/src/vs/workbench/services/thread/node/extHostThreadService.ts b/src/vs/workbench/services/thread/node/extHostThreadService.ts new file mode 100644 index 0000000000..dabfe53639 --- /dev/null +++ b/src/vs/workbench/services/thread/node/extHostThreadService.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; +import { AbstractThreadService } from 'vs/workbench/services/thread/node/abstractThreadService'; +import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; + +export class ExtHostThreadService extends AbstractThreadService implements IThreadService { + constructor(rpcProtocol: RPCProtocol) { + super(rpcProtocol, false); + } +} diff --git a/src/vs/workbench/services/timer/common/timerService.ts b/src/vs/workbench/services/timer/common/timerService.ts new file mode 100644 index 0000000000..bfbd500e5d --- /dev/null +++ b/src/vs/workbench/services/timer/common/timerService.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ITimerService = createDecorator('timerService'); + +export interface IMemoryInfo { + workingSetSize: number; + peakWorkingSetSize: number; + privateBytes: number; + sharedBytes: number; +} + +export interface IStartupMetrics { + version: number; + ellapsed: number; + timers: { + ellapsedAppReady?: number; + ellapsedWindowLoad?: number; + ellapsedWindowLoadToRequire: number; + ellapsedExtensions: number; + ellapsedExtensionsReady: number; + ellapsedRequire: number; + ellapsedViewletRestore: number; + ellapsedEditorRestore: number; + ellapsedWorkbench: number; + ellapsedTimersToTimersComputed: number; + }; + timers2: { [name: string]: number }; + platform: string; + release: string; + arch: string; + totalmem: number; + freemem: number; + meminfo: IMemoryInfo; + cpus: { count: number; speed: number; model: string; }; + initialStartup: boolean; + hasAccessibilitySupport: boolean; + isVMLikelyhood: number; + emptyWorkbench: boolean; + loadavg: number[]; +} + +export interface IInitData { + start: number; + + appReady: number; + + windowLoad: number; + + beforeLoadWorkbenchMain: number; + afterLoadWorkbenchMain: number; + + isInitialStartup: boolean; + hasAccessibilitySupport: boolean; +} + +export interface ITimerService extends IInitData { + _serviceBrand: any; + + beforeDOMContentLoaded: number; + afterDOMContentLoaded: number; + + beforeWorkbenchOpen: number; + workbenchStarted: number; + + beforeExtensionLoad: number; + afterExtensionLoad: number; + + restoreViewletDuration: number; + restoreEditorsDuration: number; + + readonly startupMetrics: IStartupMetrics; +} diff --git a/src/vs/workbench/services/timer/node/timerService.ts b/src/vs/workbench/services/timer/node/timerService.ts new file mode 100644 index 0000000000..8b0365211a --- /dev/null +++ b/src/vs/workbench/services/timer/node/timerService.ts @@ -0,0 +1,134 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ITimerService, IStartupMetrics, IInitData, IMemoryInfo } from 'vs/workbench/services/timer/common/timerService'; +import { virtualMachineHint } from 'vs/base/node/id'; +import { ticks } from 'vs/base/node/startupTimers'; + +import * as os from 'os'; + +export class TimerService implements ITimerService { + + public _serviceBrand: any; + + public readonly start: number; + public readonly appReady: number; + public readonly windowLoad: number; + + public readonly beforeLoadWorkbenchMain: number; + public readonly afterLoadWorkbenchMain: number; + + public readonly isInitialStartup: boolean; + public readonly hasAccessibilitySupport: boolean; + + public beforeDOMContentLoaded: number; + public afterDOMContentLoaded: number; + + public beforeWorkbenchOpen: number; + public workbenchStarted: number; + + public beforeExtensionLoad: number; + public afterExtensionLoad: number; + + public restoreViewletDuration: number; + public restoreEditorsDuration: number; + + + private _startupMetrics: IStartupMetrics; + + constructor(initData: IInitData, private isEmptyWorkbench: boolean) { + this.start = initData.start; + this.appReady = initData.appReady; + this.windowLoad = initData.windowLoad; + + this.beforeLoadWorkbenchMain = initData.beforeLoadWorkbenchMain; + this.afterLoadWorkbenchMain = initData.afterLoadWorkbenchMain; + + this.isInitialStartup = initData.isInitialStartup; + this.hasAccessibilitySupport = initData.hasAccessibilitySupport; + } + + get startupMetrics(): IStartupMetrics { + if (!this._startupMetrics) { + this._computeStartupMetrics(); + } + return this._startupMetrics; + } + + public _computeStartupMetrics(): void { + const now = Date.now(); + const initialStartup = !!this.isInitialStartup; + const start = initialStartup ? this.start : this.windowLoad; + + let totalmem: number; + let freemem: number; + let cpus: { count: number; speed: number; model: string; }; + let platform: string; + let release: string; + let arch: string; + let loadavg: number[]; + let meminfo: IMemoryInfo; + let isVMLikelyhood: number; + + try { + totalmem = os.totalmem(); + freemem = os.freemem(); + platform = os.platform(); + release = os.release(); + arch = os.arch(); + loadavg = os.loadavg(); + meminfo = process.getProcessMemoryInfo(); + + isVMLikelyhood = Math.round((virtualMachineHint.value() * 100)); + + const rawCpus = os.cpus(); + if (rawCpus && rawCpus.length > 0) { + cpus = { count: rawCpus.length, speed: rawCpus[0].speed, model: rawCpus[0].model }; + } + } catch (error) { + console.error(error); // be on the safe side with these hardware method calls + } + + // fill in startup timers we have until now + const timers2: { [name: string]: number } = Object.create(null); + for (const tick of ticks()) { + timers2[tick.name] = tick.duration; + } + + this._startupMetrics = { + version: 1, + ellapsed: this.workbenchStarted - start, + timers: { + ellapsedExtensions: this.afterExtensionLoad - this.beforeExtensionLoad, + ellapsedExtensionsReady: this.afterExtensionLoad - start, + ellapsedRequire: this.afterLoadWorkbenchMain - this.beforeLoadWorkbenchMain, + ellapsedViewletRestore: this.restoreViewletDuration, + ellapsedEditorRestore: this.restoreEditorsDuration, + ellapsedWorkbench: this.workbenchStarted - this.beforeWorkbenchOpen, + ellapsedWindowLoadToRequire: this.beforeLoadWorkbenchMain - this.windowLoad, + ellapsedTimersToTimersComputed: Date.now() - now + }, + timers2, + platform, + release, + arch, + totalmem, + freemem, + meminfo, + cpus, + loadavg, + initialStartup, + isVMLikelyhood, + hasAccessibilitySupport: !!this.hasAccessibilitySupport, + emptyWorkbench: this.isEmptyWorkbench + }; + + if (initialStartup) { + this._startupMetrics.timers.ellapsedAppReady = this.appReady - this.start; + this._startupMetrics.timers.ellapsedWindowLoad = this.windowLoad - this.appReady; + } + } +} diff --git a/src/vs/workbench/services/title/common/titleService.ts b/src/vs/workbench/services/title/common/titleService.ts new file mode 100644 index 0000000000..5d7c7cc9da --- /dev/null +++ b/src/vs/workbench/services/title/common/titleService.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ITitleService = createDecorator('titleService'); + +export interface ITitleService { + _serviceBrand: any; + + /** + * Set the window title with the given value. + */ + setTitle(title: string): void; + + /** + * Set the represented file name to the title if any. + */ + setRepresentedFilename(path: string): void; +} \ No newline at end of file diff --git a/src/vs/workbench/services/untitled/common/untitledEditorService.ts b/src/vs/workbench/services/untitled/common/untitledEditorService.ts new file mode 100644 index 0000000000..452ff2749f --- /dev/null +++ b/src/vs/workbench/services/untitled/common/untitledEditorService.ts @@ -0,0 +1,306 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import arrays = require('vs/base/common/arrays'); +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; +import { IFilesConfiguration } from 'vs/platform/files/common/files'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import Event, { Emitter, once } from 'vs/base/common/event'; +import { ResourceMap } from 'vs/base/common/map'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; +import { Schemas } from 'vs/base/common/network'; + +export const IUntitledEditorService = createDecorator('untitledEditorService'); + +export const UNTITLED_SCHEMA = 'untitled'; + +export interface IModelLoadOrCreateOptions { + resource?: URI; + modeId?: string; + initialValue?: string; + encoding?: string; +} + +export interface IUntitledEditorService { + + _serviceBrand: any; + + /** + * Events for when untitled editors content changes (e.g. any keystroke). + */ + onDidChangeContent: Event; + + /** + * Events for when untitled editors change (e.g. getting dirty, saved or reverted). + */ + onDidChangeDirty: Event; + + /** + * Events for when untitled editor encodings change. + */ + onDidChangeEncoding: Event; + + /** + * Events for when untitled editors are disposed. + */ + onDidDisposeModel: Event; + + /** + * Returns if an untitled resource with the given URI exists. + */ + exists(resource: URI): boolean; + + // {{SQL CARBON EDIT}} + /** + * Returns all untitled editor inputs. + */ + getAll(resources?: URI[]): UntitledEditorInput[]; + + /** + * Returns dirty untitled editors as resource URIs. + */ + getDirty(resources?: URI[]): URI[]; + + /** + * Returns true if the provided resource is dirty. + */ + isDirty(resource: URI): boolean; + + /** + * Reverts the untitled resources if found. + */ + revertAll(resources?: URI[]): URI[]; + + /** + * Creates a new untitled input with the optional resource URI or returns an existing one + * if the provided resource exists already as untitled input. + * + * It is valid to pass in a file resource. In that case the path will be used as identifier. + * The use case is to be able to create a new file with a specific path with VSCode. + */ + createOrGet(resource?: URI, modeId?: string, initialValue?: string, encoding?: string): UntitledEditorInput; + + /** + * Creates a new untitled model with the optional resource URI or returns an existing one + * if the provided resource exists already as untitled model. + * + * It is valid to pass in a file resource. In that case the path will be used as identifier. + * The use case is to be able to create a new file with a specific path with VSCode. + */ + loadOrCreate(options: IModelLoadOrCreateOptions): TPromise; + + /** + * A check to find out if a untitled resource has a file path associated or not. + */ + hasAssociatedFilePath(resource: URI): boolean; + + /** + * Suggests a filename for the given untitled resource if it is known. + */ + suggestFileName(resource: URI): string; + + /** + * Get the configured encoding for the given untitled resource if any. + */ + getEncoding(resource: URI): string; +} + +export class UntitledEditorService implements IUntitledEditorService { + + public _serviceBrand: any; + + private mapResourceToInput = new ResourceMap(); + private mapResourceToAssociatedFilePath = new ResourceMap(); + + private _onDidChangeContent: Emitter; + private _onDidChangeDirty: Emitter; + private _onDidChangeEncoding: Emitter; + private _onDidDisposeModel: Emitter; + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IConfigurationService private configurationService: IConfigurationService + ) { + this._onDidChangeContent = new Emitter(); + this._onDidChangeDirty = new Emitter(); + this._onDidChangeEncoding = new Emitter(); + this._onDidDisposeModel = new Emitter(); + } + + public get onDidDisposeModel(): Event { + return this._onDidDisposeModel.event; + } + + public get onDidChangeContent(): Event { + return this._onDidChangeContent.event; + } + + public get onDidChangeDirty(): Event { + return this._onDidChangeDirty.event; + } + + public get onDidChangeEncoding(): Event { + return this._onDidChangeEncoding.event; + } + + protected get(resource: URI): UntitledEditorInput { + return this.mapResourceToInput.get(resource); + } + + // {{SQL CARBON EDIT}} + public getAll(resources?: URI[]): UntitledEditorInput[] { + if (resources) { + return arrays.coalesce(resources.map(r => this.get(r))); + } + + return this.mapResourceToInput.values(); + } + + public exists(resource: URI): boolean { + return this.mapResourceToInput.has(resource); + } + + public revertAll(resources?: URI[], force?: boolean): URI[] { + const reverted: URI[] = []; + + const untitledInputs = this.getAll(resources); + untitledInputs.forEach(input => { + if (input) { + input.revert(); + input.dispose(); + + reverted.push(input.getResource()); + } + }); + + return reverted; + } + + public isDirty(resource: URI): boolean { + const input = this.get(resource); + + return input && input.isDirty(); + } + + public getDirty(resources?: URI[]): URI[] { + let inputs: UntitledEditorInput[]; + if (resources) { + inputs = resources.map(r => this.get(r)).filter(i => !!i); + } else { + inputs = this.mapResourceToInput.values(); + } + + return inputs + .filter(i => i.isDirty()) + .map(i => i.getResource()); + } + + public loadOrCreate(options: IModelLoadOrCreateOptions = Object.create(null)): TPromise { + return this.createOrGet(options.resource, options.modeId, options.initialValue, options.encoding).resolve(); + } + + public createOrGet(resource?: URI, modeId?: string, initialValue?: string, encoding?: string): UntitledEditorInput { + + // Massage resource if it comes with a file:// scheme + let hasAssociatedFilePath = false; + if (resource) { + hasAssociatedFilePath = (resource.scheme === Schemas.file); + resource = resource.with({ scheme: UNTITLED_SCHEMA }); // ensure we have the right scheme + + if (hasAssociatedFilePath) { + this.mapResourceToAssociatedFilePath.set(resource, true); // remember for future lookups + } + } + + // Return existing instance if asked for it + if (resource && this.mapResourceToInput.has(resource)) { + return this.mapResourceToInput.get(resource); + } + + // Create new otherwise + return this.doCreate(resource, hasAssociatedFilePath, modeId, initialValue, encoding); + } + + private doCreate(resource?: URI, hasAssociatedFilePath?: boolean, modeId?: string, initialValue?: string, encoding?: string): UntitledEditorInput { + if (!resource) { + + // Create new taking a resource URI that is not already taken + let counter = this.mapResourceToInput.size + 1; + do { + resource = URI.from({ scheme: UNTITLED_SCHEMA, path: `Untitled-${counter}` }); + counter++; + } while (this.mapResourceToInput.has(resource)); + } + + // Look up default language from settings if any + if (!modeId && !hasAssociatedFilePath) { + const configuration = this.configurationService.getConfiguration(); + if (configuration.files && configuration.files.defaultLanguage) { + modeId = configuration.files.defaultLanguage; + } + } + + const input = this.instantiationService.createInstance(UntitledEditorInput, resource, hasAssociatedFilePath, modeId, initialValue, encoding); + + const contentListener = input.onDidModelChangeContent(() => { + this._onDidChangeContent.fire(resource); + }); + + const dirtyListener = input.onDidChangeDirty(() => { + this._onDidChangeDirty.fire(resource); + }); + + const encodingListener = input.onDidModelChangeEncoding(() => { + this._onDidChangeEncoding.fire(resource); + }); + + const disposeListener = input.onDispose(() => { + this._onDidDisposeModel.fire(resource); + }); + + // Remove from cache on dispose + const onceDispose = once(input.onDispose); + onceDispose(() => { + this.mapResourceToInput.delete(input.getResource()); + this.mapResourceToAssociatedFilePath.delete(input.getResource()); + contentListener.dispose(); + dirtyListener.dispose(); + encodingListener.dispose(); + disposeListener.dispose(); + }); + + // Add to cache + this.mapResourceToInput.set(resource, input); + + return input; + } + + public hasAssociatedFilePath(resource: URI): boolean { + return this.mapResourceToAssociatedFilePath.has(resource); + } + + public suggestFileName(resource: URI): string { + const input = this.get(resource); + + return input ? input.suggestFileName() : void 0; + } + + public getEncoding(resource: URI): string { + const input = this.get(resource); + + return input ? input.getEncoding() : void 0; + } + + public dispose(): void { + this._onDidChangeContent.dispose(); + this._onDidChangeDirty.dispose(); + this._onDidChangeEncoding.dispose(); + this._onDidDisposeModel.dispose(); + } +} diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts new file mode 100644 index 0000000000..c5ddf18be3 --- /dev/null +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IViewlet } from 'vs/workbench/common/viewlet'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import Event from 'vs/base/common/event'; +import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; +import { IProgressService } from 'vs/platform/progress/common/progress'; + +export const IViewletService = createDecorator('viewletService'); + +export interface IViewletService { + _serviceBrand: ServiceIdentifier; + + onDidViewletOpen: Event; + onDidViewletClose: Event; + + /** + * Opens a viewlet with the given identifier and pass keyboard focus to it if specified. + */ + openViewlet(id: string, focus?: boolean): TPromise; + + /** + * Returns the current active viewlet or null if none. + */ + getActiveViewlet(): IViewlet; + + /** + * Returns the id of the default viewlet. + */ + getDefaultViewletId(): string; + + /** + * Returns the viewlet by id. + */ + getViewlet(id: string): ViewletDescriptor; + + /** + * Returns all registered viewlets + */ + getViewlets(): ViewletDescriptor[]; + + /** + * + */ + getProgressIndicator(id: string): IProgressService; +} diff --git a/src/vs/workbench/services/viewlet/browser/viewletService.ts b/src/vs/workbench/services/viewlet/browser/viewletService.ts new file mode 100644 index 0000000000..0db4b6cbd3 --- /dev/null +++ b/src/vs/workbench/services/viewlet/browser/viewletService.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise, ValueCallback } from 'vs/base/common/winjs.base'; +import { IViewlet } from 'vs/workbench/common/viewlet'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import Event from 'vs/base/common/event'; +import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IProgressService } from 'vs/platform/progress/common/progress'; + +export class ViewletService implements IViewletService { + + public _serviceBrand: any; + + private sidebarPart: SidebarPart; + private viewletRegistry: ViewletRegistry; + + private extensionViewlets: ViewletDescriptor[]; + private extensionViewletsLoaded: TPromise; + private extensionViewletsLoadedPromiseComplete: ValueCallback; + + public get onDidViewletOpen(): Event { return this.sidebarPart.onDidViewletOpen; }; + public get onDidViewletClose(): Event { return this.sidebarPart.onDidViewletClose; }; + + constructor( + sidebarPart: SidebarPart, + @IExtensionService private extensionService: IExtensionService + ) { + this.sidebarPart = sidebarPart; + this.viewletRegistry = Registry.as(ViewletExtensions.Viewlets); + + this.loadExtensionViewlets(); + } + + private loadExtensionViewlets(): void { + this.extensionViewlets = []; + + this.extensionViewletsLoaded = new TPromise(c => { + this.extensionViewletsLoadedPromiseComplete = c; + }); + + this.extensionService.onReady().then(() => { + const viewlets = this.viewletRegistry.getViewlets(); + viewlets.forEach(v => { + if (!!v.extensionId) { + this.extensionViewlets.push(v); + } + }); + + this.extensionViewletsLoadedPromiseComplete(void 0); + }); + } + + public openViewlet(id: string, focus?: boolean): TPromise { + + // Built in viewlets do not need to wait for extensions to be loaded + const builtInViewletIds = this.getBuiltInViewlets().map(v => v.id); + const isBuiltInViewlet = builtInViewletIds.indexOf(id) !== -1; + if (isBuiltInViewlet) { + return this.sidebarPart.openViewlet(id, focus); + } + + // Extension viewlets need to be loaded first which can take time + return this.extensionViewletsLoaded.then(() => { + if (this.viewletRegistry.getViewlet(id)) { + return this.sidebarPart.openViewlet(id, focus); + } + + // Fallback to default viewlet if extension viewlet is still not found (e.g. uninstalled) + return this.sidebarPart.openViewlet(this.getDefaultViewletId(), focus); + }); + } + + public getActiveViewlet(): IViewlet { + return this.sidebarPart.getActiveViewlet(); + } + + public getViewlets(): ViewletDescriptor[] { + const builtInViewlets = this.getBuiltInViewlets(); + + return builtInViewlets.concat(this.extensionViewlets); + } + + private getBuiltInViewlets(): ViewletDescriptor[] { + return this.viewletRegistry.getViewlets() + .filter(viewlet => !viewlet.extensionId) + .sort((v1, v2) => v1.order - v2.order); + } + + public getDefaultViewletId(): string { + return this.viewletRegistry.getDefaultViewletId(); + } + + public getViewlet(id: string): ViewletDescriptor { + return this.getViewlets().filter(viewlet => viewlet.id === id)[0]; + } + + public getProgressIndicator(id: string): IProgressService { + return this.sidebarPart.getProgressIndicator(id); + } +} diff --git a/src/vs/workbench/services/workspace/common/workspaceEditing.ts b/src/vs/workbench/services/workspace/common/workspaceEditing.ts new file mode 100644 index 0000000000..fb38248e0a --- /dev/null +++ b/src/vs/workbench/services/workspace/common/workspaceEditing.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +export const IWorkspaceEditingService = createDecorator('workspaceEditingService'); + +export interface IWorkspaceEditingService { + + _serviceBrand: ServiceIdentifier; + + /** + * add roots to the existing workspace + */ + addRoots(roots: URI[]): TPromise; + + /** + * remove roots from the existing workspace + */ + removeRoots(roots: URI[]): TPromise; +} + +export const IWorkspaceMigrationService = createDecorator('workspaceMigrationService'); + +export interface IWorkspaceMigrationService { + + /** + * Migrate current workspace to given workspace + */ + migrate(toWokspaceId: IWorkspaceIdentifier): TPromise; + +} \ No newline at end of file diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts new file mode 100644 index 0000000000..689b649e64 --- /dev/null +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import URI from 'vs/base/common/uri'; +import { equals, distinct } from 'vs/base/common/arrays'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { IWorkspacesService, IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { isLinux } from 'vs/base/common/platform'; +import { dirname, relative } from 'path'; +import { isEqualOrParent } from 'vs/base/common/paths'; + +export class WorkspaceEditingService implements IWorkspaceEditingService { + + public _serviceBrand: any; + + constructor( + @IJSONEditingService private jsonEditingService: IJSONEditingService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IWindowsService private windowsService: IWindowsService, + @IWorkspacesService private workspacesService: IWorkspacesService + ) { + } + + public addRoots(rootsToAdd: URI[]): TPromise { + if (!this.isSupported()) { + return TPromise.as(void 0); // we need a workspace to begin with + } + + const roots = this.contextService.getWorkspace().roots; + + return this.doSetRoots([...roots, ...rootsToAdd]); + } + + public removeRoots(rootsToRemove: URI[]): TPromise { + if (!this.isSupported()) { + return TPromise.as(void 0); // we need a workspace to begin with + } + + const roots = this.contextService.getWorkspace().roots; + const rootsToRemoveRaw = rootsToRemove.map(root => root.toString()); + + return this.doSetRoots(roots.filter(root => rootsToRemoveRaw.indexOf(root.toString()) === -1)); + } + + private isSupported(): boolean { + // TODO@Ben multi root + return ( + this.environmentService.appQuality !== 'stable' // not yet enabled in stable + && this.contextService.hasMultiFolderWorkspace() // we need a multi folder workspace to begin with + ); + } + + private doSetRoots(newRoots: URI[]): TPromise { + const workspace = this.contextService.getWorkspace(); + const currentWorkspaceRoots = this.contextService.getWorkspace().roots.map(root => root.fsPath); + const newWorkspaceRoots = this.validateRoots(newRoots); + + // See if there are any changes + if (equals(currentWorkspaceRoots, newWorkspaceRoots)) { + return TPromise.as(void 0); + } + + // Apply to config + if (newWorkspaceRoots.length) { + const workspaceConfigFolder = dirname(workspace.configuration.fsPath); + const value: IStoredWorkspaceFolder[] = newWorkspaceRoots.map(newWorkspaceRoot => { + if (isEqualOrParent(newWorkspaceRoot, workspaceConfigFolder, !isLinux)) { + newWorkspaceRoot = relative(workspaceConfigFolder, newWorkspaceRoot) || '.'; // absolute paths get converted to relative ones to workspace location if possible + } + + return { path: newWorkspaceRoot }; + }); + + return this.jsonEditingService.write(workspace.configuration, { key: 'folders', value }, true); + } else { + // TODO: Sandeep - Removing all roots? + } + + return TPromise.as(null); + } + + private validateRoots(roots: URI[]): string[] { + if (!roots) { + return []; + } + + // Prevent duplicates + return distinct(roots.map(root => root.fsPath), root => isLinux ? root : root.toLowerCase()); + } +} diff --git a/src/vs/workbench/services/workspace/node/workspaceMigrationService.ts b/src/vs/workbench/services/workspace/node/workspaceMigrationService.ts new file mode 100644 index 0000000000..61a97fe709 --- /dev/null +++ b/src/vs/workbench/services/workspace/node/workspaceMigrationService.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { once } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { StorageService } from 'vs/platform/storage/common/storageService'; +import { migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/migration'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { IWorkspaceMigrationService } from 'vs/workbench/services/workspace/common/workspaceEditing'; + +export class WorkspaceMigrationService implements IWorkspaceMigrationService { + + public _serviceBrand: any; + + private shutdownListener: IDisposable; + + constructor( + @IStorageService private storageService: IStorageService, + @IJSONEditingService private jsonEditingService: IJSONEditingService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IConfigurationService private configurationService: IConfigurationService, + @ILifecycleService private lifecycleService: ILifecycleService + ) { + } + + migrate(toWorkspaceId: IWorkspaceIdentifier): TPromise { + this.migrateStorage(toWorkspaceId); + + return this.migrateConfiguration(toWorkspaceId); + } + + migrateStorage(toWorkspaceId: IWorkspaceIdentifier): void { + + // The shutdown sequence could have been stopped due to a veto. Make sure to + // always dispose the shutdown listener if we are called again in the same session. + if (this.shutdownListener) { + this.shutdownListener.dispose(); + this.shutdownListener = void 0; + } + + // Since many components write to storage only on shutdown, we register a shutdown listener + // very late to be called as the last one. + this.shutdownListener = once(this.lifecycleService.onShutdown)(() => { + + // TODO@Ben revisit this when we move away from local storage to a file based approach + const storageImpl = this.storageService as StorageService; + migrateStorageToMultiRootWorkspace(storageImpl.storageId, toWorkspaceId, storageImpl.workspaceStorage); + }); + } + + private migrateConfiguration(toWorkspaceId: IWorkspaceIdentifier): TPromise { + if (!this.contextService.hasFolderWorkspace()) { + return TPromise.as(void 0); // return early if not a folder workspace is opened + } + + const configurationProperties = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); + const targetWorkspaceConfiguration = {}; + for (const key of this.configurationService.keys().workspace) { + if (configurationProperties[key] && configurationProperties[key].scope === ConfigurationScope.WINDOW) { + targetWorkspaceConfiguration[key] = this.configurationService.lookup(key).workspace; + } + } + + return this.jsonEditingService.write(URI.file(toWorkspaceId.configPath), { key: 'settings', value: targetWorkspaceConfiguration }, true); + } +} diff --git a/src/vs/workbench/test/browser/actionRegistry.test.ts b/src/vs/workbench/test/browser/actionRegistry.test.ts new file mode 100644 index 0000000000..bb663d49b6 --- /dev/null +++ b/src/vs/workbench/test/browser/actionRegistry.test.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as Platform from 'vs/platform/registry/common/platform'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Extensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actionRegistry'; +import { prepareActions } from 'vs/workbench/browser/actions'; +import { Action } from 'vs/base/common/actions'; + + +class MyClass extends Action { + constructor(id: string, label: string) { + super(id, label); + } +} + +suite('Workbench Action Registry', () => { + + test('Workbench Action Registration', function () { + let Registry = Platform.Registry.as(Extensions.WorkbenchActions); + + let d = new SyncActionDescriptor(MyClass, 'id', 'name'); + + let oldActions = Registry.getWorkbenchActions().slice(0); + let oldCount = Registry.getWorkbenchActions().length; + + Registry.registerWorkbenchAction(d, 'My Alias', 'category'); + Registry.registerWorkbenchAction(d, null); + + assert.equal(Registry.getWorkbenchActions().length, 1 + oldCount); + assert.strictEqual(d, Registry.getWorkbenchAction('id')); + + assert.deepEqual(Registry.getAlias(d.id), 'My Alias'); + assert.equal(Registry.getCategory(d.id), 'category'); + + (Registry).setWorkbenchActions(oldActions); + }); + + test('Workbench Action Bar prepareActions()', function () { + let a1 = new Separator(); + let a2 = new Separator(); + let a3 = new Action('a3'); + let a4 = new Separator(); + let a5 = new Separator(); + let a6 = new Action('a6'); + let a7 = new Separator(); + + let actions = prepareActions([a1, a2, a3, a4, a5, a6, a7]); + assert.strictEqual(actions.length, 3); // duplicate separators get removed + assert(actions[0] === a3); + assert(actions[1] === a5); + assert(actions[2] === a6); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/browser/editorStacksModel.test.ts b/src/vs/workbench/test/browser/editorStacksModel.test.ts new file mode 100644 index 0000000000..368cac38ba --- /dev/null +++ b/src/vs/workbench/test/browser/editorStacksModel.test.ts @@ -0,0 +1,1989 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { EditorStacksModel, EditorGroup, EditorCloseEvent } from 'vs/workbench/common/editor/editorStacksModel'; +import { EditorInput, IFileEditorInput, IEditorIdentifier, IEditorGroup, IStacksModelChangeEvent, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory, IEditorCloseEvent } from 'vs/workbench/common/editor'; +import URI from 'vs/base/common/uri'; +import { TestStorageService, TestLifecycleService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Position, Direction } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import 'vs/workbench/browser/parts/editor/baseEditor'; + +function create(): EditorStacksModel { + let inst = new TestInstantiationService(); + inst.stub(IStorageService, new TestStorageService()); + inst.stub(ILifecycleService, new TestLifecycleService()); + inst.stub(IWorkspaceContextService, new TestContextService()); + inst.stub(ITelemetryService, NullTelemetryService); + + const config = new TestConfigurationService(); + config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); + inst.stub(IConfigurationService, config); + + + return inst.createInstance(EditorStacksModel, true); +} + +interface ModelEvents { + opened: IEditorGroup[]; + activated: IEditorGroup[]; + closed: IEditorGroup[]; + moved: IEditorGroup[]; + renamed: IEditorGroup[]; + disposed: IEditorIdentifier[]; + changed: IStacksModelChangeEvent[]; + editorClosed: IEditorCloseEvent[]; + editorWillClose: IEditorCloseEvent[]; +} + +interface GroupEvents { + opened: EditorInput[]; + activated: EditorInput[]; + closed: EditorCloseEvent[]; + pinned: EditorInput[]; + unpinned: EditorInput[]; + moved: EditorInput[]; +} + +function modelListener(model: EditorStacksModel): ModelEvents { + const modelEvents = { + opened: [], + activated: [], + closed: [], + moved: [], + renamed: [], + disposed: [], + changed: [], + editorClosed: [], + editorWillClose: [] + }; + + model.onGroupOpened(g => modelEvents.opened.push(g)); + model.onGroupActivated(g => modelEvents.activated.push(g)); + model.onGroupClosed(g => modelEvents.closed.push(g)); + model.onGroupMoved(g => modelEvents.moved.push(g)); + model.onGroupRenamed(g => modelEvents.renamed.push(g)); + model.onEditorDisposed(e => modelEvents.disposed.push(e)); + model.onModelChanged(e => modelEvents.changed.push(e)); + model.onWillCloseEditor(e => modelEvents.editorWillClose.push(e)); + model.onEditorClosed(e => modelEvents.editorClosed.push(e)); + + return modelEvents; +} + +function groupListener(group: EditorGroup): GroupEvents { + const groupEvents = { + opened: [], + closed: [], + activated: [], + pinned: [], + unpinned: [], + moved: [] + }; + + group.onEditorOpened(e => groupEvents.opened.push(e)); + group.onEditorClosed(e => groupEvents.closed.push(e)); + group.onEditorActivated(e => groupEvents.activated.push(e)); + group.onEditorPinned(e => groupEvents.pinned.push(e)); + group.onEditorUnpinned(e => groupEvents.unpinned.push(e)); + group.onEditorMoved(e => groupEvents.moved.push(e)); + + return groupEvents; +} + +let index = 0; +class TestEditorInput extends EditorInput { + constructor(public id: string) { + super(); + } + public getTypeId() { return 'testEditorInput'; } + public resolve() { return null; } + + public matches(other: TestEditorInput): boolean { + return other && this.id === other.id && other instanceof TestEditorInput; + } + + public setDirty(): void { + this._onDidChangeDirty.fire(); + } + + public setLabel(): void { + this._onDidChangeLabel.fire(); + } +} + +class NonSerializableTestEditorInput extends EditorInput { + constructor(public id: string) { + super(); + } + public getTypeId() { return 'testEditorInput-nonSerializable'; } + public resolve() { return null; } + + public matches(other: TestEditorInput): boolean { + return other && this.id === other.id && other instanceof NonSerializableTestEditorInput; + } +} + +class TestFileEditorInput extends EditorInput implements IFileEditorInput { + + constructor(public id: string, private resource: URI) { + super(); + } + public getTypeId() { return 'testFileEditorInput'; } + public resolve() { return null; } + + public matches(other: TestEditorInput): boolean { + return other && this.id === other.id && other instanceof TestFileEditorInput; + } + + + public setEncoding(encoding: string) { + } + + public getEncoding(): string { + return null; + } + + public setPreferredEncoding(encoding: string) { + } + + public getResource(): URI { + return this.resource; + } + + public setForceOpenAsBinary(): void { + } +} + +function input(id = String(index++), nonSerializable?: boolean, resource?: URI): EditorInput { + if (resource) { + return new TestFileEditorInput(id, resource); + } + + return nonSerializable ? new NonSerializableTestEditorInput(id) : new TestEditorInput(id); +} + +interface ISerializedTestInput { + id: string; +} + +class TestEditorInputFactory implements IEditorInputFactory { + + constructor() { } + + public serialize(editorInput: EditorInput): string { + let testEditorInput = editorInput; + let testInput: ISerializedTestInput = { + id: testEditorInput.id + }; + + return JSON.stringify(testInput); + } + + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { + let testInput: ISerializedTestInput = JSON.parse(serializedEditorInput); + + return new TestEditorInput(testInput.id); + } +} + +(Registry.as(EditorExtensions.Editors)).registerEditorInputFactory('testEditorInput', TestEditorInputFactory); + +suite('Editor Stacks Model', () => { + + teardown(() => { + index = 1; + }); + + test('Groups - Basic', function () { + const model = create(); + const events = modelListener(model); + + assert.equal(model.groups.length, 0); + assert.ok(!model.activeGroup); + + const first = model.openGroup('first'); + assert.equal(events.opened[0], first); + assert.equal(events.activated[0], first); + assert.equal(model.activeGroup, first); + assert.equal(model.groups.length, 1); + assert.equal(model.groups[0], first); + + const second = model.openGroup('second'); + assert.equal(events.opened[1], second); + assert.equal(events.activated[1], second); + assert.equal(model.activeGroup, second); + assert.equal(model.groups.length, 2); + assert.equal(model.groups[1], second); + + const third = model.openGroup('third'); + assert.equal(events.opened[2], third); + assert.equal(events.activated[2], third); + assert.equal(model.activeGroup, third); + assert.equal(model.groups.length, 3); + assert.equal(model.groups[2], third); + + model.closeGroup(first); + assert.equal(events.closed[0], first); + assert.equal(model.groups.length, 2); + assert.equal(model.activeGroup, third); + assert.equal(model.groups[0], second); + assert.equal(model.groups[1], third); + + model.closeGroup(third); + assert.equal(events.closed[1], third); + assert.equal(events.activated[3], second); + assert.equal(model.activeGroup, second); + assert.equal(model.groups.length, 1); + assert.equal(model.groups[0], second); + + const fourth = model.openGroup('fourth'); + assert.equal(fourth, model.activeGroup); + model.closeGroup(fourth); + assert.equal(second, model.activeGroup); + + model.closeGroup(second); + assert.equal(model.groups.length, 0); + + model.openGroup('first'); + model.openGroup('second'); + model.openGroup('third'); + model.openGroup('fourth'); + + assert.equal(model.groups.length, 4); + + model.closeGroups(); + + assert.equal(model.groups.length, 0); + }); + + test('Groups - Close Group sends move event for others to the right', function () { + const model = create(); + const events = modelListener(model); + + const first = model.openGroup('first'); + model.openGroup('second'); + const third = model.openGroup('third'); + + model.closeGroup(first); + assert.equal(events.moved.length, 2); + + model.closeGroup(third); + assert.equal(events.moved.length, 2); + }); + + test('Groups - Move Groups', function () { + const model = create(); + const events = modelListener(model); + + model.openGroup('first'); + const group2 = model.openGroup('second'); + + model.renameGroup(group2, 'renamed'); + + assert.equal(group2.label, 'renamed'); + assert.equal(group2, events.renamed[0]); + }); + + test('Groups - Position of Group', function () { + const model = create(); + + const group1 = model.openGroup('first'); + const group2 = model.openGroup('second'); + const group3 = model.openGroup('third'); + + assert.equal(Position.ONE, model.positionOfGroup(group1)); + assert.equal(Position.TWO, model.positionOfGroup(group2)); + assert.equal(Position.THREE, model.positionOfGroup(group3)); + }); + + test('Groups - Rename Group', function () { + const model = create(); + + const group1 = model.openGroup('first'); + const group2 = model.openGroup('second'); + + model.moveGroup(group1, 1); + assert.equal(model.groups[0], group2); + assert.equal(model.groups[1], group1); + + model.moveGroup(group1, 0); + assert.equal(model.groups[0], group1); + assert.equal(model.groups[1], group2); + + const group3 = model.openGroup('third'); + + model.moveGroup(group1, 2); + + assert.equal(model.groups[0], group2); + assert.equal(model.groups[1], group3); + assert.equal(model.groups[2], group1); + }); + + test('Groups - Move Group sends move events for all moved groups', function () { + const model = create(); + let events = modelListener(model); + + let group1 = model.openGroup('first'); + let group2 = model.openGroup('second'); + let group3 = model.openGroup('third'); + + model.moveGroup(group1, 1); + assert.equal(events.moved.length, 2); + + model.closeGroups(); + + events = modelListener(model); + group1 = model.openGroup('first'); + group2 = model.openGroup('second'); + group3 = model.openGroup('third'); + + model.moveGroup(group1, 2); + assert.equal(events.moved.length, 3); + + model.closeGroups(); + + events = modelListener(model); + group1 = model.openGroup('first'); + group2 = model.openGroup('second'); + group3 = model.openGroup('third'); + + model.moveGroup(group3, 1); + assert.equal(events.moved.length, 2); + }); + + test('Groups - Event Aggregation', function () { + const model = create(); + + let groupEvents: IStacksModelChangeEvent[] = []; + let count = groupEvents.length; + model.onModelChanged(group => { + groupEvents.push(group); + }); + + const first = model.openGroup('first'); + assert.ok(groupEvents.length > count); + count = groupEvents.length; + + const second = model.openGroup('second'); + assert.ok(groupEvents.length > count); + count = groupEvents.length; + + model.renameGroup(second, 'renamed'); + assert.ok(groupEvents.length > count); + count = groupEvents.length; + + const input1 = input(); + first.openEditor(input1); + assert.ok(groupEvents.length > count); + count = groupEvents.length; + + first.closeEditor(input1); + assert.ok(groupEvents.length > count); + count = groupEvents.length; + + model.closeGroup(second); + assert.ok(groupEvents.length > count); + }); + + test('Groups - Open group but do not set active', function () { + const model = create(); + const events = modelListener(model); + + const group1 = model.openGroup('first'); + model.openGroup('second', false); + + assert.equal(model.activeGroup, group1); + assert.equal(events.activated.length, 1); + assert.equal(events.activated[0], group1); + }); + + test('Groups - Group Identifiers', function () { + const model = create(); + + const group1 = model.openGroup('first'); + const group2 = model.openGroup('second'); + const group3 = model.openGroup('third'); + + assert.equal(model.getGroup(group1.id), group1); + assert.equal(model.getGroup(group2.id), group2); + assert.equal(model.getGroup(group3.id), group3); + + model.closeGroup(group2); + assert.equal(model.getGroup(group2.id), null); + + model.moveGroup(group1, 1); + + assert.equal(model.getGroup(group1.id), group1); + assert.equal(model.getGroup(group3.id), group3); + + model.closeGroups(); + + assert.equal(model.getGroup(group1.id), null); + assert.equal(model.getGroup(group3.id), null); + }); + + test('Groups - Open Group at Index', function () { + const model = create(); + + const group1 = model.openGroup('first', false, 2); + const group2 = model.openGroup('second', false, 1); + const group3 = model.openGroup('third', false, 0); + + assert.equal(model.groups[0], group3); + assert.equal(model.groups[1], group2); + assert.equal(model.groups[2], group1); + }); + + test('Groups - Close All Groups except active', function () { + const model = create(); + + model.openGroup('first'); + model.openGroup('second'); + const group3 = model.openGroup('third'); + + model.closeGroups(model.activeGroup); + + assert.equal(model.groups.length, 1); + assert.equal(model.activeGroup, group3); + }); + + test('Groups - Close All Groups except inactive', function () { + const model = create(); + + model.openGroup('first'); + const group2 = model.openGroup('second'); + model.openGroup('third'); + + model.closeGroups(group2); + + assert.equal(model.groups.length, 1); + assert.equal(model.activeGroup, group2); + }); + + test('Stack - One Editor', function () { + const model = create(); + const group = model.openGroup('group'); + const events = groupListener(group); + + assert.equal(group.count, 0); + assert.equal(group.getEditors(true).length, 0); + + // Active && Pinned + const input1 = input(); + group.openEditor(input1, { active: true, pinned: true }); + + assert.equal(group.count, 1); + assert.equal(group.getEditors(true).length, 1); + assert.equal(group.activeEditor, input1); + assert.equal(group.isActive(input1), true); + assert.equal(group.isPreview(input1), false); + assert.equal(group.isPinned(input1), true); + assert.equal(group.isPinned(0), true); + + assert.equal(events.opened[0], input1); + assert.equal(events.activated[0], input1); + + group.closeEditor(input1); + assert.equal(group.count, 0); + assert.equal(group.getEditors(true).length, 0); + assert.equal(group.activeEditor, void 0); + assert.equal(events.closed[0].editor, input1); + assert.equal(events.closed[0].index, 0); + assert.equal(events.closed[0].pinned, true); + + // Active && Preview + const input2 = input(); + group.openEditor(input2, { active: true, pinned: false }); + + assert.equal(group.count, 1); + assert.equal(group.getEditors(true).length, 1); + assert.equal(group.activeEditor, input2); + assert.equal(group.isActive(input2), true); + assert.equal(group.isPreview(input2), true); + assert.equal(group.isPinned(input2), false); + assert.equal(group.isPinned(0), false); + + assert.equal(events.opened[1], input2); + assert.equal(events.activated[1], input2); + + group.closeEditor(input2); + assert.equal(group.count, 0); + assert.equal(group.getEditors(true).length, 0); + assert.equal(group.activeEditor, void 0); + assert.equal(events.closed[1].editor, input2); + assert.equal(events.closed[1].index, 0); + assert.equal(events.closed[1].pinned, false); + + group.closeEditor(input2); + assert.equal(group.count, 0); + assert.equal(group.getEditors(true).length, 0); + assert.equal(group.activeEditor, void 0); + assert.equal(events.closed[1].editor, input2); + + // Nonactive && Pinned => gets active because its first editor + const input3 = input(); + group.openEditor(input3, { active: false, pinned: true }); + + assert.equal(group.count, 1); + assert.equal(group.getEditors(true).length, 1); + assert.equal(group.activeEditor, input3); + assert.equal(group.isActive(input3), true); + assert.equal(group.isPreview(input3), false); + assert.equal(group.isPinned(input3), true); + assert.equal(group.isPinned(0), true); + + assert.equal(events.opened[2], input3); + assert.equal(events.activated[2], input3); + + group.closeEditor(input3); + assert.equal(group.count, 0); + assert.equal(group.getEditors(true).length, 0); + assert.equal(group.activeEditor, void 0); + assert.equal(events.closed[2].editor, input3); + + assert.equal(events.opened[2], input3); + assert.equal(events.activated[2], input3); + + group.closeEditor(input3); + assert.equal(group.count, 0); + assert.equal(group.getEditors(true).length, 0); + assert.equal(group.activeEditor, void 0); + assert.equal(events.closed[2].editor, input3); + + // Nonactive && Preview => gets active because its first editor + const input4 = input(); + group.openEditor(input4); + + assert.equal(group.count, 1); + assert.equal(group.getEditors(true).length, 1); + assert.equal(group.activeEditor, input4); + assert.equal(group.isActive(input4), true); + assert.equal(group.isPreview(input4), true); + assert.equal(group.isPinned(input4), false); + assert.equal(group.isPinned(0), false); + + assert.equal(events.opened[3], input4); + assert.equal(events.activated[3], input4); + + group.closeEditor(input4); + assert.equal(group.count, 0); + assert.equal(group.getEditors(true).length, 0); + assert.equal(group.activeEditor, void 0); + assert.equal(events.closed[3].editor, input4); + }); + + test('Stack - Multiple Editors - Pinned and Active', function () { + const model = create(); + const group = model.openGroup('group'); + const events = groupListener(group); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + // Pinned and Active + group.openEditor(input1, { pinned: true, active: true }); + group.openEditor(input2, { pinned: true, active: true }); + group.openEditor(input3, { pinned: true, active: true }); + + assert.equal(group.count, 3); + assert.equal(group.getEditors(true).length, 3); + assert.equal(group.activeEditor, input3); + assert.equal(group.isActive(input1), false); + assert.equal(group.isPinned(input1), true); + assert.equal(group.isPreview(input1), false); + assert.equal(group.isActive(input2), false); + assert.equal(group.isPinned(input2), true); + assert.equal(group.isPreview(input2), false); + assert.equal(group.isActive(input3), true); + assert.equal(group.isPinned(input3), true); + assert.equal(group.isPreview(input3), false); + + assert.equal(events.opened[0], input1); + assert.equal(events.opened[1], input2); + assert.equal(events.opened[2], input3); + + const mru = group.getEditors(true); + assert.equal(mru[0], input3); + assert.equal(mru[1], input2); + assert.equal(mru[2], input1); + + group.closeAllEditors(); + + assert.equal(events.closed.length, 3); + assert.equal(group.count, 0); + }); + + test('Stack - Multiple Editors - Preview editor moves to the side of the active one', function () { + const model = create(); + const group = model.openGroup('group'); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + group.openEditor(input1, { pinned: false, active: true }); + group.openEditor(input2, { pinned: true, active: true }); + group.openEditor(input3, { pinned: true, active: true }); + + assert.equal(input3, group.getEditors()[2]); + + const input4 = input(); + group.openEditor(input4, { pinned: false, active: true }); // this should cause the preview editor to move after input3 + + assert.equal(input4, group.getEditors()[2]); + }); + + test('Stack - Multiple Editors - Pinned and Active (DEFAULT_OPEN_EDITOR_DIRECTION = Direction.LEFT)', function () { + let inst = new TestInstantiationService(); + inst.stub(IStorageService, new TestStorageService()); + inst.stub(ILifecycleService, new TestLifecycleService()); + inst.stub(IWorkspaceContextService, new TestContextService()); + inst.stub(ITelemetryService, NullTelemetryService); + + const config = new TestConfigurationService(); + inst.stub(IConfigurationService, config); + config.setUserConfiguration('workbench', { editor: { openPositioning: 'left' } }); + + + const model = inst.createInstance(EditorStacksModel, true); + + const group = model.openGroup('group'); + const events = groupListener(group); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + // Pinned and Active + group.openEditor(input1, { pinned: true, active: true }); + group.openEditor(input2, { pinned: true, active: true }); + group.openEditor(input3, { pinned: true, active: true }); + + assert.equal(group.getEditors()[0], input3); + assert.equal(group.getEditors()[1], input2); + assert.equal(group.getEditors()[2], input1); + + model.closeGroups(); + + assert.equal(events.closed.length, 3); + assert.equal(group.count, 0); + }); + + test('Stack - Multiple Editors - Pinned and Not Active', function () { + const model = create(); + const group = model.openGroup('group'); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + // Pinned and Active + group.openEditor(input1, { pinned: true }); + group.openEditor(input2, { pinned: true }); + group.openEditor(input3, { pinned: true }); + + assert.equal(group.count, 3); + assert.equal(group.getEditors(true).length, 3); + assert.equal(group.activeEditor, input1); + assert.equal(group.isActive(input1), true); + assert.equal(group.isPinned(input1), true); + assert.equal(group.isPinned(0), true); + assert.equal(group.isPreview(input1), false); + assert.equal(group.isActive(input2), false); + assert.equal(group.isPinned(input2), true); + assert.equal(group.isPinned(1), true); + assert.equal(group.isPreview(input2), false); + assert.equal(group.isActive(input3), false); + assert.equal(group.isPinned(input3), true); + assert.equal(group.isPinned(2), true); + assert.equal(group.isPreview(input3), false); + + const mru = group.getEditors(true); + assert.equal(mru[0], input1); + assert.equal(mru[1], input2); + assert.equal(mru[2], input3); + }); + + test('Stack - Multiple Editors - Preview gets overwritten', function () { + const model = create(); + const group = model.openGroup('group'); + const events = groupListener(group); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + // Non active, preview + group.openEditor(input1); // becomes active, preview + group.openEditor(input2); // overwrites preview + group.openEditor(input3); // overwrites preview + + assert.equal(group.count, 1); + assert.equal(group.getEditors(true).length, 1); + assert.equal(group.activeEditor, input3); + assert.equal(group.isActive(input3), true); + assert.equal(group.isPinned(input3), false); + assert.equal(group.isPreview(input3), true); + + assert.equal(events.opened[0], input1); + assert.equal(events.opened[1], input2); + assert.equal(events.opened[2], input3); + assert.equal(events.closed[0].editor, input1); + assert.equal(events.closed[1].editor, input2); + + const mru = group.getEditors(true); + assert.equal(mru[0], input3); + assert.equal(mru.length, 1); + }); + + test('Stack - Multiple Editors - set active', function () { + const model = create(); + const group = model.openGroup('group'); + const events = groupListener(group); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + group.openEditor(input1, { pinned: true, active: true }); + group.openEditor(input2, { pinned: true, active: true }); + group.openEditor(input3, { pinned: false, active: true }); + + assert.equal(group.activeEditor, input3); + + let mru = group.getEditors(true); + assert.equal(mru[0], input3); + assert.equal(mru[1], input2); + assert.equal(mru[2], input1); + + group.setActive(input3); + assert.equal(events.activated.length, 3); + + group.setActive(input1); + assert.equal(events.activated[3], input1); + assert.equal(group.activeEditor, input1); + assert.equal(group.isActive(input1), true); + assert.equal(group.isActive(input2), false); + assert.equal(group.isActive(input3), false); + + mru = group.getEditors(true); + assert.equal(mru[0], input1); + assert.equal(mru[1], input3); + assert.equal(mru[2], input2); + }); + + test('Stack - Multiple Editors - pin and unpin', function () { + const model = create(); + const group = model.openGroup('group'); + const events = groupListener(group); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + group.openEditor(input1, { pinned: true, active: true }); + group.openEditor(input2, { pinned: true, active: true }); + group.openEditor(input3, { pinned: false, active: true }); + + assert.equal(group.activeEditor, input3); + assert.equal(group.count, 3); + + group.pin(input3); + + assert.equal(group.activeEditor, input3); + assert.equal(group.isPinned(input3), true); + assert.equal(group.isPreview(input3), false); + assert.equal(group.isActive(input3), true); + assert.equal(events.pinned[0], input3); + assert.equal(group.count, 3); + + group.unpin(input1); + + assert.equal(group.activeEditor, input3); + assert.equal(group.isPinned(input1), false); + assert.equal(group.isPreview(input1), true); + assert.equal(group.isActive(input1), false); + assert.equal(events.unpinned[0], input1); + assert.equal(group.count, 3); + + group.unpin(input2); + + assert.equal(group.activeEditor, input3); + assert.equal(group.count, 2); // 2 previews got merged into one + assert.equal(group.getEditors()[0], input2); + assert.equal(group.getEditors()[1], input3); + assert.equal(events.closed[0].editor, input1); + assert.equal(group.count, 2); + + group.unpin(input3); + + assert.equal(group.activeEditor, input3); + assert.equal(group.count, 1); // pinning replaced the preview + assert.equal(group.getEditors()[0], input3); + assert.equal(events.closed[1].editor, input2); + assert.equal(group.count, 1); + }); + + test('Stack - Multiple Editors - closing picks next from MRU list', function () { + const model = create(); + const group = model.openGroup('group'); + const events = groupListener(group); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + const input4 = input(); + const input5 = input(); + + group.openEditor(input1, { pinned: true, active: true }); + group.openEditor(input2, { pinned: true, active: true }); + group.openEditor(input3, { pinned: true, active: true }); + group.openEditor(input4, { pinned: true, active: true }); + group.openEditor(input5, { pinned: true, active: true }); + + assert.equal(group.activeEditor, input5); + assert.equal(group.getEditors(true)[0], input5); + assert.equal(group.count, 5); + + group.closeEditor(input5); + assert.equal(group.activeEditor, input4); + assert.equal(events.activated[5], input4); + assert.equal(group.count, 4); + + group.setActive(input1); + group.setActive(input4); + group.closeEditor(input4); + + assert.equal(group.activeEditor, input1); + assert.equal(group.count, 3); + + group.closeEditor(input1); + + assert.equal(group.activeEditor, input3); + assert.equal(group.count, 2); + + group.setActive(input2); + group.closeEditor(input2); + + assert.equal(group.activeEditor, input3); + assert.equal(group.count, 1); + + group.closeEditor(input3); + + assert.ok(!group.activeEditor); + assert.equal(group.count, 0); + }); + + test('Stack - Multiple Editors - move editor', function () { + const model = create(); + const group = model.openGroup('group'); + const events = groupListener(group); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + const input4 = input(); + const input5 = input(); + + group.openEditor(input1, { pinned: true, active: true }); + group.openEditor(input2, { pinned: true, active: true }); + + group.moveEditor(input1, 1); + + assert.equal(events.moved[0], input1); + assert.equal(group.getEditors()[0], input2); + assert.equal(group.getEditors()[1], input1); + + group.setActive(input1); + group.openEditor(input3, { pinned: true, active: true }); + group.openEditor(input4, { pinned: true, active: true }); + group.openEditor(input5, { pinned: true, active: true }); + + group.moveEditor(input4, 0); + + assert.equal(events.moved[1], input4); + assert.equal(group.getEditors()[0], input4); + assert.equal(group.getEditors()[1], input2); + assert.equal(group.getEditors()[2], input1); + assert.equal(group.getEditors()[3], input3); + assert.equal(group.getEditors()[4], input5); + + group.moveEditor(input4, 3); + group.moveEditor(input2, 1); + + assert.equal(group.getEditors()[0], input1); + assert.equal(group.getEditors()[1], input2); + assert.equal(group.getEditors()[2], input3); + assert.equal(group.getEditors()[3], input4); + assert.equal(group.getEditors()[4], input5); + }); + + test('Stack - Multiple Editors - move editor across groups', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group1'); + + const g1_input1 = input(); + const g1_input2 = input(); + const g2_input1 = input(); + + group1.openEditor(g1_input1, { active: true, pinned: true }); + group1.openEditor(g1_input2, { active: true, pinned: true }); + group2.openEditor(g2_input1, { active: true, pinned: true }); + + // A move across groups is a close in the one group and an open in the other group at a specific index + group2.closeEditor(g2_input1); + group1.openEditor(g2_input1, { active: true, pinned: true, index: 1 }); + + assert.equal(group1.count, 3); + assert.equal(group1.getEditors()[0], g1_input1); + assert.equal(group1.getEditors()[1], g2_input1); + assert.equal(group1.getEditors()[2], g1_input2); + }); + + test('Stack - Multiple Editors - move editor across groups (input already exists in group 1)', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group1'); + + const g1_input1 = input(); + const g1_input2 = input(); + const g1_input3 = input(); + const g2_input1 = g1_input2; + + group1.openEditor(g1_input1, { active: true, pinned: true }); + group1.openEditor(g1_input2, { active: true, pinned: true }); + group1.openEditor(g1_input3, { active: true, pinned: true }); + group2.openEditor(g2_input1, { active: true, pinned: true }); + + // A move across groups is a close in the one group and an open in the other group at a specific index + group2.closeEditor(g2_input1); + group1.openEditor(g2_input1, { active: true, pinned: true, index: 0 }); + + assert.equal(group1.count, 3); + assert.equal(group1.getEditors()[0], g1_input2); + assert.equal(group1.getEditors()[1], g1_input1); + assert.equal(group1.getEditors()[2], g1_input3); + }); + + test('Stack - Multiple Editors - Pinned & Non Active', function () { + const model = create(); + const group = model.openGroup('group'); + + const input1 = input(); + group.openEditor(input1); + assert.equal(group.activeEditor, input1); + assert.equal(group.previewEditor, input1); + assert.equal(group.getEditors()[0], input1); + assert.equal(group.count, 1); + + const input2 = input(); + group.openEditor(input2, { pinned: true, active: false }); + assert.equal(group.activeEditor, input1); + assert.equal(group.previewEditor, input1); + assert.equal(group.getEditors()[0], input1); + assert.equal(group.getEditors()[1], input2); + assert.equal(group.count, 2); + + const input3 = input(); + group.openEditor(input3, { pinned: true, active: false }); + assert.equal(group.activeEditor, input1); + assert.equal(group.previewEditor, input1); + assert.equal(group.getEditors()[0], input1); + assert.equal(group.getEditors()[1], input3); + assert.equal(group.getEditors()[2], input2); + assert.equal(group.isPinned(input1), false); + assert.equal(group.isPinned(input2), true); + assert.equal(group.isPinned(input3), true); + assert.equal(group.count, 3); + }); + + test('Stack - Multiple Editors - Close Others, Close Left, Close Right', function () { + const model = create(); + const group = model.openGroup('group'); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + const input4 = input(); + const input5 = input(); + + group.openEditor(input1, { active: true, pinned: true }); + group.openEditor(input2, { active: true, pinned: true }); + group.openEditor(input3, { active: true, pinned: true }); + group.openEditor(input4, { active: true, pinned: true }); + group.openEditor(input5, { active: true, pinned: true }); + + // Close Others + group.closeEditors(group.activeEditor); + assert.equal(group.activeEditor, input5); + assert.equal(group.count, 1); + + group.closeAllEditors(); + group.openEditor(input1, { active: true, pinned: true }); + group.openEditor(input2, { active: true, pinned: true }); + group.openEditor(input3, { active: true, pinned: true }); + group.openEditor(input4, { active: true, pinned: true }); + group.openEditor(input5, { active: true, pinned: true }); + group.setActive(input3); + + // Close Left + assert.equal(group.activeEditor, input3); + group.closeEditors(group.activeEditor, Direction.LEFT); + assert.equal(group.activeEditor, input3); + assert.equal(group.count, 3); + assert.equal(group.getEditors()[0], input3); + assert.equal(group.getEditors()[1], input4); + assert.equal(group.getEditors()[2], input5); + + group.closeAllEditors(); + group.openEditor(input1, { active: true, pinned: true }); + group.openEditor(input2, { active: true, pinned: true }); + group.openEditor(input3, { active: true, pinned: true }); + group.openEditor(input4, { active: true, pinned: true }); + group.openEditor(input5, { active: true, pinned: true }); + group.setActive(input3); + + // Close Right + assert.equal(group.activeEditor, input3); + group.closeEditors(group.activeEditor, Direction.RIGHT); + assert.equal(group.activeEditor, input3); + assert.equal(group.count, 3); + assert.equal(group.getEditors()[0], input1); + assert.equal(group.getEditors()[1], input2); + assert.equal(group.getEditors()[2], input3); + }); + + test('Stack - Multiple Editors - real user example', function () { + const model = create(); + const group = model.openGroup('group'); + + // [] -> /index.html/ + let indexHtml = input('index.html'); + group.openEditor(indexHtml); + assert.equal(group.activeEditor, indexHtml); + assert.equal(group.previewEditor, indexHtml); + assert.equal(group.getEditors()[0], indexHtml); + assert.equal(group.count, 1); + + // /index.html/ -> /style.css/ + let styleCss = input('style.css'); + group.openEditor(styleCss); + assert.equal(group.activeEditor, styleCss); + assert.equal(group.previewEditor, styleCss); + assert.equal(group.getEditors()[0], styleCss); + assert.equal(group.count, 1); + + // /style.css/ -> [/style.css/, test.js] + let testJs = input('test.js'); + group.openEditor(testJs, { active: true, pinned: true }); + assert.equal(group.previewEditor, styleCss); + assert.equal(group.activeEditor, testJs); + assert.equal(group.isPreview(styleCss), true); + assert.equal(group.isPinned(testJs), true); + assert.equal(group.getEditors()[0], styleCss); + assert.equal(group.getEditors()[1], testJs); + assert.equal(group.count, 2); + + // [/style.css/, test.js] -> [test.js, /index.html/] + indexHtml = input('index.html'); + group.openEditor(indexHtml, { active: true }); + assert.equal(group.activeEditor, indexHtml); + assert.equal(group.previewEditor, indexHtml); + assert.equal(group.isPreview(indexHtml), true); + assert.equal(group.isPinned(testJs), true); + assert.equal(group.getEditors()[0], testJs); + assert.equal(group.getEditors()[1], indexHtml); + assert.equal(group.count, 2); + + // make test.js active + testJs = input('test.js'); + group.setActive(testJs); + assert.equal(group.activeEditor, testJs); + assert.equal(group.isActive(testJs), true); + assert.equal(group.count, 2); + + // [test.js, /indexHtml/] -> [test.js, index.html] + indexHtml = input('index.html'); + group.pin(indexHtml); + assert.equal(group.isPinned(indexHtml), true); + assert.equal(group.isPreview(indexHtml), false); + assert.equal(group.activeEditor, testJs); + + // [test.js, index.html] -> [test.js, file.ts, index.html] + const fileTs = input('file.ts'); + group.openEditor(fileTs, { active: true, pinned: true }); + assert.equal(group.isPinned(fileTs), true); + assert.equal(group.isPreview(fileTs), false); + assert.equal(group.count, 3); + assert.equal(group.activeEditor, fileTs); + + // [test.js, index.html, file.ts] -> [test.js, /file.ts/, index.html] + group.unpin(fileTs); + assert.equal(group.count, 3); + assert.equal(group.isPinned(fileTs), false); + assert.equal(group.isPreview(fileTs), true); + assert.equal(group.activeEditor, fileTs); + + // [test.js, /file.ts/, index.html] -> [test.js, /other.ts/, index.html] + const otherTs = input('other.ts'); + group.openEditor(otherTs, { active: true }); + assert.equal(group.count, 3); + assert.equal(group.activeEditor, otherTs); + assert.ok(group.getEditors()[0].matches(testJs)); + assert.equal(group.getEditors()[1], otherTs); + assert.ok(group.getEditors()[2].matches(indexHtml)); + + // make index.html active + indexHtml = input('index.html'); + group.setActive(indexHtml); + assert.equal(group.activeEditor, indexHtml); + + // [test.js, /other.ts/, index.html] -> [test.js, /other.ts/] + group.closeEditor(indexHtml); + assert.equal(group.count, 2); + assert.equal(group.activeEditor, otherTs); + assert.ok(group.getEditors()[0].matches(testJs)); + assert.equal(group.getEditors()[1], otherTs); + + // [test.js, /other.ts/] -> [test.js] + group.closeEditor(otherTs); + assert.equal(group.count, 1); + assert.equal(group.activeEditor, testJs); + assert.ok(group.getEditors()[0].matches(testJs)); + + // [test.js] -> /test.js/ + group.unpin(testJs); + assert.equal(group.count, 1); + assert.equal(group.activeEditor, testJs); + assert.ok(group.getEditors()[0].matches(testJs)); + assert.equal(group.isPinned(testJs), false); + assert.equal(group.isPreview(testJs), true); + + // /test.js/ -> [] + group.closeEditor(testJs); + assert.equal(group.count, 0); + assert.equal(group.activeEditor, null); + assert.equal(group.previewEditor, null); + }); + + test('Stack - Single Group, Single Editor - persist', function () { + let inst = new TestInstantiationService(); + + inst.stub(IStorageService, new TestStorageService()); + inst.stub(IWorkspaceContextService, new TestContextService()); + const lifecycle = new TestLifecycleService(); + inst.stub(ILifecycleService, lifecycle); + inst.stub(ITelemetryService, NullTelemetryService); + + const config = new TestConfigurationService(); + config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); + inst.stub(IConfigurationService, config); + + (Registry.as(EditorExtensions.Editors)).setInstantiationService(inst); + + let model: EditorStacksModel = inst.createInstance(EditorStacksModel, true); + let group = model.openGroup('group'); + + const input1 = input(); + group.openEditor(input1); + + assert.equal(model.groups.length, 1); + assert.equal(group.count, 1); + assert.equal(group.activeEditor.matches(input1), true); + assert.equal(group.previewEditor.matches(input1), true); + assert.equal(group.label, 'group'); + assert.equal(group.isActive(input1), true); + + lifecycle.fireShutdown(); + + // Create model again - should load from storage + model = inst.createInstance(EditorStacksModel, true); + + assert.equal(model.groups.length, 1); + + group = model.activeGroup; + + assert.equal(group.count, 1); + assert.equal(group.activeEditor.matches(input1), true); + assert.equal(group.previewEditor.matches(input1), true); + assert.equal(group.label, 'group'); + assert.equal(group.isActive(input1), true); + }); + + test('Stack - Multiple Groups, Multiple editors - persist', function () { + let inst = new TestInstantiationService(); + + inst.stub(IStorageService, new TestStorageService()); + inst.stub(IWorkspaceContextService, new TestContextService()); + const lifecycle = new TestLifecycleService(); + inst.stub(ILifecycleService, lifecycle); + inst.stub(ITelemetryService, NullTelemetryService); + + const config = new TestConfigurationService(); + config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); + inst.stub(IConfigurationService, config); + + + (Registry.as(EditorExtensions.Editors)).setInstantiationService(inst); + + let model: EditorStacksModel = inst.createInstance(EditorStacksModel, true); + + let group1 = model.openGroup('group1'); + + const g1_input1 = input(); + const g1_input2 = input(); + const g1_input3 = input(); + + group1.openEditor(g1_input1, { active: true, pinned: true }); + group1.openEditor(g1_input2, { active: true, pinned: false }); + group1.openEditor(g1_input3, { active: false, pinned: true }); + + let group2 = model.openGroup('group2'); + + const g2_input1 = input(); + const g2_input2 = input(); + const g2_input3 = input(); + + group2.openEditor(g2_input1, { active: true, pinned: true }); + group2.openEditor(g2_input2, { active: false, pinned: false }); + group2.openEditor(g2_input3, { active: false, pinned: true }); + + assert.equal(model.groups.length, 2); + assert.equal(group1.count, 3); + assert.equal(group2.count, 3); + assert.equal(group1.activeEditor.matches(g1_input2), true); + assert.equal(group2.activeEditor.matches(g2_input1), true); + assert.equal(group1.previewEditor.matches(g1_input2), true); + assert.equal(group2.previewEditor.matches(g2_input2), true); + assert.equal(group1.label, 'group1'); + assert.equal(group2.label, 'group2'); + + assert.equal(group1.getEditors(true)[0].matches(g1_input2), true); + assert.equal(group1.getEditors(true)[1].matches(g1_input1), true); + assert.equal(group1.getEditors(true)[2].matches(g1_input3), true); + + assert.equal(group2.getEditors(true)[0].matches(g2_input1), true); + assert.equal(group2.getEditors(true)[1].matches(g2_input2), true); + assert.equal(group2.getEditors(true)[2].matches(g2_input3), true); + + lifecycle.fireShutdown(); + + // Create model again - should load from storage + model = inst.createInstance(EditorStacksModel, true); + + group1 = model.groups[0]; + group2 = model.groups[1]; + + assert.equal(model.groups.length, 2); + assert.equal(group1.count, 3); + assert.equal(group2.count, 3); + assert.equal(group1.activeEditor.matches(g1_input2), true); + assert.equal(group2.activeEditor.matches(g2_input1), true); + assert.equal(group1.previewEditor.matches(g1_input2), true); + assert.equal(group2.previewEditor.matches(g2_input2), true); + assert.equal(group1.label, 'group1'); + assert.equal(group2.label, 'group2'); + + assert.equal(group1.getEditors(true)[0].matches(g1_input2), true); + assert.equal(group1.getEditors(true)[1].matches(g1_input1), true); + assert.equal(group1.getEditors(true)[2].matches(g1_input3), true); + + assert.equal(group2.getEditors(true)[0].matches(g2_input1), true); + assert.equal(group2.getEditors(true)[1].matches(g2_input2), true); + assert.equal(group2.getEditors(true)[2].matches(g2_input3), true); + }); + + test('Stack - Single group, multiple editors - persist (some not persistable)', function () { + let inst = new TestInstantiationService(); + + inst.stub(IStorageService, new TestStorageService()); + inst.stub(IWorkspaceContextService, new TestContextService()); + const lifecycle = new TestLifecycleService(); + inst.stub(ILifecycleService, lifecycle); + inst.stub(ITelemetryService, NullTelemetryService); + + const config = new TestConfigurationService(); + config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); + inst.stub(IConfigurationService, config); + + + (Registry.as(EditorExtensions.Editors)).setInstantiationService(inst); + + let model: EditorStacksModel = inst.createInstance(EditorStacksModel, true); + + let group = model.openGroup('group1'); + + const serializableInput1 = input(); + const nonSerializableInput2 = input('3', true); + const serializableInput2 = input(); + + group.openEditor(serializableInput1, { active: true, pinned: true }); + group.openEditor(nonSerializableInput2, { active: true, pinned: false }); + group.openEditor(serializableInput2, { active: false, pinned: true }); + + assert.equal(group.count, 3); + assert.equal(group.activeEditor.matches(nonSerializableInput2), true); + assert.equal(group.previewEditor.matches(nonSerializableInput2), true); + + assert.equal(group.getEditors(true)[0].matches(nonSerializableInput2), true); + assert.equal(group.getEditors(true)[1].matches(serializableInput1), true); + assert.equal(group.getEditors(true)[2].matches(serializableInput2), true); + + lifecycle.fireShutdown(); + + // Create model again - should load from storage + model = inst.createInstance(EditorStacksModel, true); + + group = model.groups[0]; + + assert.equal(group.count, 2); + assert.equal(group.activeEditor.matches(serializableInput1), true); + assert.equal(group.previewEditor, null); + + assert.equal(group.getEditors(true)[0].matches(serializableInput1), true); + assert.equal(group.getEditors(true)[1].matches(serializableInput2), true); + }); + + test('Stack - Multiple groups, multiple editors - persist (some not persistable, causes empty group)', function () { + let inst = new TestInstantiationService(); + + inst.stub(IStorageService, new TestStorageService()); + inst.stub(IWorkspaceContextService, new TestContextService()); + const lifecycle = new TestLifecycleService(); + inst.stub(ILifecycleService, lifecycle); + inst.stub(ITelemetryService, NullTelemetryService); + + const config = new TestConfigurationService(); + config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); + inst.stub(IConfigurationService, config); + + + (Registry.as(EditorExtensions.Editors)).setInstantiationService(inst); + + let model: EditorStacksModel = inst.createInstance(EditorStacksModel, true); + + let group1 = model.openGroup('group1'); + let group2 = model.openGroup('group1'); + + const serializableInput1 = input(); + const serializableInput2 = input(); + const nonSerializableInput = input('2', true); + + group1.openEditor(serializableInput1, { pinned: true }); + group1.openEditor(serializableInput2); + + group2.openEditor(nonSerializableInput); + + lifecycle.fireShutdown(); + + // Create model again - should load from storage + model = inst.createInstance(EditorStacksModel, true); + + group1 = model.groups[0]; + + assert.equal(model.groups.length, 1); + assert.equal(group1.count, 2); + assert.equal(group1.getEditors()[0].matches(serializableInput1), true); + assert.equal(group1.getEditors()[1].matches(serializableInput2), true); + }); + + test('Stack - Multiple groups, multiple editors - persist (ignore persisted when editors to open on startup)', function () { + let inst = new TestInstantiationService(); + + inst.stub(IStorageService, new TestStorageService()); + const lifecycle = new TestLifecycleService(); + inst.stub(ILifecycleService, lifecycle); + inst.stub(ITelemetryService, NullTelemetryService); + + const config = new TestConfigurationService(); + config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); + inst.stub(IConfigurationService, config); + + (Registry.as(EditorExtensions.Editors)).setInstantiationService(inst); + + let model: EditorStacksModel = inst.createInstance(EditorStacksModel, false); + + let group1 = model.openGroup('group1'); + let group2 = model.openGroup('group1'); + + const serializableInput1 = input(); + const serializableInput2 = input(); + const nonSerializableInput = input('2', true); + + group1.openEditor(serializableInput1, { pinned: true }); + group1.openEditor(serializableInput2); + + group2.openEditor(nonSerializableInput); + + lifecycle.fireShutdown(); + + // Create model again - should NOT load from storage + model = inst.createInstance(EditorStacksModel, false); + + assert.equal(model.groups.length, 0); + }); + + test('Stack - Multiple Editors - Navigation (across groups)', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + group1.openEditor(input1, { pinned: true, active: true }); + group1.openEditor(input2, { pinned: true, active: true }); + group1.openEditor(input3, { pinned: true, active: true }); + + const input4 = input(); + const input5 = input(); + const input6 = input(); + + group2.openEditor(input4, { pinned: true, active: true }); + group2.openEditor(input5, { pinned: true, active: true }); + group2.openEditor(input6, { pinned: true, active: true }); + + model.setActive(group1); + group1.setActive(input1); + + let previous = model.previous(true, false /* jump groups, do NOT cycle at start*/); + assert.equal(previous, null); + + previous = model.previous(true /* jump groups */); + assert.equal(previous.group, group2); + assert.equal(previous.editor, input6); + + model.setActive(previous.group); + (previous.group).setActive(previous.editor); + + let next = model.next(true, false /* jump groups, do NOT cycle at end */); + assert.equal(next, null); + + next = model.next(true /* jump groups */); + assert.equal(next.group, group1); + assert.equal(next.editor, input1); + + model.setActive(group1); + group1.setActive(input3); + + next = model.next(true /* jump groups */); + assert.equal(next.group, group2); + assert.equal(next.editor, input4); + + model.setActive(next.group); + (next.group).setActive(next.editor); + + previous = model.previous(true /* jump groups */); + assert.equal(previous.group, group1); + assert.equal(previous.editor, input3); + }); + + test('Stack - Multiple Editors - Navigation (in group)', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + group1.openEditor(input1, { pinned: true, active: true }); + group1.openEditor(input2, { pinned: true, active: true }); + group1.openEditor(input3, { pinned: true, active: true }); + + const input4 = input(); + const input5 = input(); + const input6 = input(); + + group2.openEditor(input4, { pinned: true, active: true }); + group2.openEditor(input5, { pinned: true, active: true }); + group2.openEditor(input6, { pinned: true, active: true }); + + model.setActive(group1); + group1.setActive(input1); + + let previous = model.previous(false, false /* do NOT jump groups, do NOT cycle at start*/); + assert.equal(previous, null); + + previous = model.previous(false /* do NOT jump groups */); + assert.equal(previous.group, group1); + assert.equal(previous.editor, input3); + + model.setActive(previous.group); + (previous.group).setActive(previous.editor); + + let next = model.next(false, false /* do NOT jump groups, do NOT cycle at end */); + assert.equal(next, null); + + next = model.next(false /* do NOT jump groups */); + assert.equal(next.group, group1); + assert.equal(next.editor, input1); + + model.setActive(group1); + group1.setActive(input3); + + next = model.next(false /* do NOT jump groups */); + assert.equal(next.group, group1); + assert.equal(next.editor, input1); + + model.setActive(next.group); + (next.group).setActive(next.editor); + + previous = model.previous(false /* do NOT jump groups */); + assert.equal(previous.group, group1); + assert.equal(previous.editor, input3); + }); + + test('Stack - Multiple Editors - Resources', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + assert.ok(!model.isOpen(URI.file('/hello/world.txt'))); + + const input1Resource = URI.file('/hello/world.txt'); + const input1ResourceUpper = URI.file('/hello/WORLD.txt'); + const input1 = input(void 0, false, input1Resource); + group1.openEditor(input1); + + assert.ok(model.isOpen(input1Resource)); + assert.ok(group1.contains(input1Resource)); + assert.equal(model.count(input1), 1); + assert.equal(group1.getEditor(input1Resource), input1); + + assert.ok(!group1.getEditor(input1ResourceUpper)); + assert.ok(!model.isOpen(input1ResourceUpper)); + assert.ok(!group1.contains(input1ResourceUpper)); + + group2.openEditor(input1); + group1.closeEditor(input1); + + assert.ok(model.isOpen(input1Resource)); + assert.ok(!group1.contains(input1Resource)); + assert.ok(!group1.getEditor(input1Resource)); + assert.ok(!group1.getEditor(input1ResourceUpper)); + assert.ok(group2.contains(input1Resource)); + assert.equal(group2.getEditor(input1Resource), input1); + assert.equal(model.count(input1), 1); + + const input1ResourceClone = URI.file('/hello/world.txt'); + const input1Clone = input(void 0, false, input1ResourceClone); + group1.openEditor(input1Clone); + + assert.ok(group1.contains(input1Resource)); + + group2.closeEditor(input1); + + assert.ok(model.isOpen(input1Resource)); + assert.ok(group1.contains(input1Resource)); + assert.equal(group1.getEditor(input1Resource), input1Clone); + assert.ok(!group2.contains(input1Resource)); + + group1.closeEditor(input1Clone); + + assert.ok(!model.isOpen(input1Resource)); + assert.ok(!group1.contains(input1Resource)); + + group1.openEditor(input1); + + const input2Resource = URI.file('/hello/world_other.txt'); + const input2 = input(void 0, false, input2Resource); + + const input3Resource = URI.file('/hello/world_different.txt'); + const input3 = input(void 0, false, input3Resource); + + group1.openEditor(input2); + + assert.ok(model.isOpen(input2Resource)); + assert.ok(!model.isOpen(input1Resource)); + + group1.openEditor(input3, { pinned: true }); + + assert.ok(model.isOpen(input2Resource)); + assert.ok(model.isOpen(input3Resource)); + + model.closeGroups(); + + assert.ok(!model.isOpen(input2Resource)); + assert.ok(!model.isOpen(input3Resource)); + }); + + test('Stack - Multiple Editors - Editor Dispose', function () { + const model = create(); + const events = modelListener(model); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + group1.openEditor(input1, { pinned: true, active: true }); + group1.openEditor(input2, { pinned: true, active: true }); + group1.openEditor(input3, { pinned: true, active: true }); + + group2.openEditor(input1, { pinned: true, active: true }); + group2.openEditor(input2, { pinned: true, active: true }); + + input1.dispose(); + + assert.equal(events.disposed.length, 2); + assert.ok(events.disposed[0].editor.matches(input1)); + assert.ok(events.disposed[1].editor.matches(input1)); + + input3.dispose(); + assert.equal(events.disposed.length, 3); + assert.ok(events.disposed[2].editor.matches(input3)); + + const input4 = input(); + const input5 = input(); + + group1.openEditor(input4, { pinned: false, active: true }); + group1.openEditor(input5, { pinned: false, active: true }); + + input4.dispose(); + assert.equal(events.disposed.length, 3); + + model.closeGroup(group2); + + input2.dispose(); + assert.equal(events.disposed.length, 4); + }); + + test('Stack - Multiple Editors - Editor Disposed on Close', function () { + const model = create(); + const events = modelListener(model); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + const input4 = input(); + + group1.openEditor(input1, { pinned: true, active: true }); + group1.openEditor(input2, { pinned: true, active: true }); + group1.openEditor(input3, { pinned: true, active: true }); + group1.openEditor(input4, { pinned: true, active: true }); + + group1.closeEditor(input3); + + assert.equal(events.editorClosed.length, 1); + assert.equal(events.editorClosed[0].editor, input3); + + assert.equal(events.editorWillClose.length, 1); + assert.equal(events.editorWillClose[0].editor, input3); + assert.equal(events.editorWillClose[0].group, group1); + + assert.equal(input3.isDisposed(), true); + + group2.openEditor(input2, { pinned: true, active: true }); + group2.openEditor(input3, { pinned: true, active: true }); + group2.openEditor(input4, { pinned: true, active: true }); + + group1.closeEditor(input2); + + assert.equal(events.editorClosed.length, 2); + assert.equal(events.editorClosed[1].editor, input2); + + assert.equal(events.editorWillClose.length, 2); + assert.equal(events.editorWillClose[1].editor, input2); + assert.equal(events.editorWillClose[1].group, group1); + + assert.equal(input2.isDisposed(), false); + + group2.closeEditor(input2); + + assert.equal(events.editorClosed.length, 3); + assert.equal(events.editorClosed[2].editor, input2); + + assert.equal(events.editorWillClose.length, 3); + assert.equal(events.editorWillClose[2].editor, input2); + assert.equal(events.editorWillClose[2].group, group2); + + assert.equal(input2.isDisposed(), true); + + group1.closeAllEditors(); + + assert.equal(events.editorClosed.length, 5); + assert.equal(events.editorWillClose.length, 5); + + assert.equal(input4.isDisposed(), false); + + model.closeGroups(); + + assert.equal(events.editorClosed.length, 7); + assert.equal(events.editorWillClose.length, 7); + + assert.equal(input4.isDisposed(), true); + }); + + test('Stack - Multiple Editors - Editor Disposed on Close (Diff Editor)', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + + const input1 = input(); + const input2 = input(); + + const diffInput = new DiffEditorInput('name', 'description', input1, input2); + + group1.openEditor(diffInput, { pinned: true, active: true }); + group1.openEditor(input1, { pinned: true, active: true }); + + group1.closeEditor(diffInput); + + assert.equal(diffInput.isDisposed(), true); + assert.equal(input2.isDisposed(), true); + assert.equal(input1.isDisposed(), false); + }); + + test('Stack - Multiple Editors - Editor Not Disposed after Closing when opening Modified side (Diff Editor)', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + + const input1 = input(); + const input2 = input(); + + const diffInput = new DiffEditorInput('name', 'description', input1, input2); + + group1.openEditor(diffInput, { pinned: false, active: true }); + group1.openEditor(input1, { pinned: false, active: true }); + + assert.equal(diffInput.isDisposed(), true); + assert.equal(input2.isDisposed(), true); + assert.equal(input1.isDisposed(), false); + }); + + test('Stack - Multiple Editors - Editor Disposed on Close (same input, files)', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + const input1 = input(void 0, void 0, URI.file('/hello/world.txt')); + + group1.openEditor(input1, { pinned: true, active: true }); + group2.openEditor(input1, { pinned: true, active: true }); + + group2.closeEditor(input1); + assert.equal(input1.isDisposed(), false); + + group1.closeEditor(input1); + assert.equal(input1.isDisposed(), true); + }); + + test('Stack - Multiple Editors - Editor Disposed on Close (same input, files, diff)', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + const input1 = input(void 0, void 0, URI.file('/hello/world.txt')); + const input2 = input(void 0, void 0, URI.file('/hello/world_other.txt')); + + const diffInput = new DiffEditorInput('name', 'description', input2, input1); + + group1.openEditor(input1, { pinned: true, active: true }); + group2.openEditor(diffInput, { pinned: true, active: true }); + + group1.closeEditor(input1); + assert.equal(input1.isDisposed(), false); + assert.equal(input2.isDisposed(), false); + assert.equal(diffInput.isDisposed(), false); + + group2.closeEditor(diffInput); + assert.equal(input1.isDisposed(), true); + assert.equal(input2.isDisposed(), true); + assert.equal(diffInput.isDisposed(), true); + }); + + test('Stack - Multiple Editors - Editor Disposed on Close (same input, files, diff, close diff)', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + const input1 = input(void 0, void 0, URI.file('/hello/world.txt')); + const input2 = input(void 0, void 0, URI.file('/hello/world_other.txt')); + + const diffInput = new DiffEditorInput('name', 'description', input2, input1); + + group1.openEditor(input1, { pinned: true, active: true }); + group2.openEditor(diffInput, { pinned: true, active: true }); + + group2.closeEditor(diffInput); + assert.equal(input1.isDisposed(), false); + assert.equal(input2.isDisposed(), true); + assert.equal(diffInput.isDisposed(), true); + + group1.closeEditor(input1); + assert.equal(input1.isDisposed(), true); + }); + + test('Stack - Multiple Editors - Editor Emits Dirty and Label Changed', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + const input1 = input(); + const input2 = input(); + + group1.openEditor(input1, { pinned: true, active: true }); + group2.openEditor(input2, { pinned: true, active: true }); + + let dirtyCounter = 0; + model.onEditorDirty(() => { + dirtyCounter++; + }); + + let labelChangeCounter = 0; + model.onEditorLabelChange(() => { + labelChangeCounter++; + }); + + (input1).setDirty(); + (input1).setLabel(); + + assert.equal(dirtyCounter, 1); + assert.equal(labelChangeCounter, 1); + + (input2).setDirty(); + (input2).setLabel(); + + assert.equal(dirtyCounter, 2); + assert.equal(labelChangeCounter, 2); + + group2.closeAllEditors(); + + (input2).setDirty(); + (input2).setLabel(); + + assert.equal(dirtyCounter, 2); + assert.equal(labelChangeCounter, 2); + + model.closeGroups(); + + (input1).setDirty(); + (input1).setLabel(); + + assert.equal(dirtyCounter, 2); + assert.equal(labelChangeCounter, 2); + }); + + test('Groups - Model change events (structural vs state)', function () { + const model = create(); + const events = modelListener(model); + + const group1 = model.openGroup('first'); + + assert.equal(events.changed[0].group, group1); + assert.equal(events.changed[0].structural, true); // open group + + assert.equal(events.changed[1].group, group1); + assert.ok(!events.changed[1].structural); // set active + + const input1 = input(); + + group1.openEditor(input1, { pinned: true, active: true }); + + assert.equal(events.changed[2].group, group1); + assert.equal(events.changed[2].structural, true); // open editor + assert.equal(events.changed[2].editor, input1); + + assert.equal(events.changed[3].group, group1); + assert.ok(!events.changed[3].structural); // set active + assert.equal(events.changed[3].editor, input1); + + group1.unpin(input1); + + assert.equal(events.changed[4].group, group1); + assert.ok(!events.changed[4].structural); // unpin + assert.equal(events.changed[4].editor, input1); + + group1.closeAllEditors(); + + assert.equal(events.changed[5].group, group1); + assert.ok(events.changed[5].structural); // close + assert.equal(events.changed[5].editor, input1); + }); + + test('Preview tab does not have a stable position (https://github.com/Microsoft/vscode/issues/8245)', function () { + const model = create(); + + const group1 = model.openGroup('first'); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + group1.openEditor(input1, { pinned: true, active: true }); + group1.openEditor(input2, { active: true }); + group1.setActive(input1); + + group1.openEditor(input3, { active: true }); + assert.equal(group1.indexOf(input3), 1); + }); + + test('Stack - Multiple Editors - find group based on input', function () { + const model = create(); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + const g1_input1 = input(); + const g1_input2 = input(); + const g2_input1 = input(); + const g2_input2 = input(); + const unmatched_input = input(); + + group1.openEditor(g1_input1, { active: true, pinned: true }); + group1.openEditor(g1_input2, { active: true, pinned: true }); + group2.openEditor(g2_input1, { active: true, pinned: true }); + group2.openEditor(g2_input2, { active: true, pinned: true }); + + const found_group1 = model.findGroup(g1_input2, true); + const notfound1 = model.findGroup(g1_input1, true); + + const found1_group2 = model.findGroup(g2_input1, false); + const found2_group2 = model.findGroup(g2_input2, false); + + const notfound2 = model.findGroup(unmatched_input, false); + const notfound3 = model.findGroup(unmatched_input, true); + + assert.equal(found_group1, group1); + assert.equal(notfound1, null); + assert.equal(found1_group2, group2); + assert.equal(found2_group2, group2); + assert.equal(notfound2, null); + assert.equal(notfound3, null); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/browser/part.test.ts b/src/vs/workbench/test/browser/part.test.ts new file mode 100644 index 0000000000..344a7a430d --- /dev/null +++ b/src/vs/workbench/test/browser/part.test.ts @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { Build, Builder } from 'vs/base/browser/builder'; +import { Part } from 'vs/workbench/browser/part'; +import * as Types from 'vs/base/common/types'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; + +class MyPart extends Part { + + constructor(private expectedParent: Builder) { + super('myPart', { hasTitle: true }, new TestThemeService()); + } + + public createTitleArea(parent: Builder): Builder { + assert.strictEqual(parent, this.expectedParent); + return super.createTitleArea(parent); + } + + public createContentArea(parent: Builder): Builder { + assert.strictEqual(parent, this.expectedParent); + return super.createContentArea(parent); + } + + public getMemento(storageService: IStorageService): any { + return super.getMemento(storageService); + } +} + +class MyPart2 extends Part { + + constructor() { + super('myPart2', { hasTitle: true }, new TestThemeService()); + } + + public createTitleArea(parent: Builder): Builder { + return parent.div(function (div) { + div.span({ + id: 'myPart.title', + innerHtml: 'Title' + }); + }); + } + + public createContentArea(parent: Builder): Builder { + return parent.div(function (div) { + div.span({ + id: 'myPart.content', + innerHtml: 'Content' + }); + }); + } +} + +class MyPart3 extends Part { + + constructor() { + super('myPart2', { hasTitle: false }, new TestThemeService()); + } + + public createTitleArea(parent: Builder): Builder { + return null; + } + + public createContentArea(parent: Builder): Builder { + return parent.div(function (div) { + div.span({ + id: 'myPart.content', + innerHtml: 'Content' + }); + }); + } +} + +suite('Workbench Part', () => { + let fixture: HTMLElement; + let fixtureId = 'workbench-part-fixture'; + let storage: IStorageService; + + setup(() => { + fixture = document.createElement('div'); + fixture.id = fixtureId; + document.body.appendChild(fixture); + storage = new StorageService(new InMemoryLocalStorage(), null, TestWorkspace.id); + }); + + teardown(() => { + document.body.removeChild(fixture); + }); + + test('Creation', function () { + let b = Build.withElementById(fixtureId); + b.div().hide(); + + let part = new MyPart(b); + part.create(b); + + assert.strictEqual(part.getId(), 'myPart'); + assert.strictEqual(part.getContainer(), b); + + // Memento + let memento = part.getMemento(storage); + assert(memento); + memento.foo = 'bar'; + memento.bar = [1, 2, 3]; + + part.shutdown(); + + // Re-Create to assert memento contents + part = new MyPart(b); + + memento = part.getMemento(storage); + assert(memento); + assert.strictEqual(memento.foo, 'bar'); + assert.strictEqual(memento.bar.length, 3); + + // Empty Memento stores empty object + delete memento.foo; + delete memento.bar; + + part.shutdown(); + part = new MyPart(b); + memento = part.getMemento(storage); + assert(memento); + assert.strictEqual(Types.isEmptyObject(memento), true); + }); + + test('Part Layout with Title and Content', function () { + let b = Build.withElementById(fixtureId); + b.div().hide(); + + let part = new MyPart2(); + part.create(b); + + assert(Build.withElementById('myPart.title')); + assert(Build.withElementById('myPart.content')); + }); + + test('Part Layout with Content only', function () { + let b = Build.withElementById(fixtureId); + b.div().hide(); + + let part = new MyPart3(); + part.create(b); + + assert(!Build.withElementById('myPart.title')); + assert(Build.withElementById('myPart.content')); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts new file mode 100644 index 0000000000..7abb639efd --- /dev/null +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -0,0 +1,208 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorInput, EditorOptions, Extensions, IEditorRegistry, IEditorInputFactory } from 'vs/workbench/common/editor'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import * as Platform from 'vs/platform/registry/common/platform'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; +import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; + +const NullThemeService = new TestThemeService(); + +let EditorRegistry: IEditorRegistry = Platform.Registry.as(Extensions.Editors); + +export class MyEditor extends BaseEditor { + + constructor(id: string, @ITelemetryService telemetryService: ITelemetryService) { + super(id, NullTelemetryService, NullThemeService); + } + + getId(): string { + return 'myEditor'; + } + + public layout(): void { + + } + + public createEditor(): any { + + } +} + +export class MyOtherEditor extends BaseEditor { + + constructor(id: string, @ITelemetryService telemetryService: ITelemetryService) { + super(id, NullTelemetryService, NullThemeService); + } + + getId(): string { + return 'myOtherEditor'; + } + + public layout(): void { + + } + + public createEditor(): any { + + } +} + +class MyInputFactory implements IEditorInputFactory { + + serialize(input: EditorInput): string { + return input.toString(); + } + + deserialize(instantiationService: IInstantiationService, raw: string): EditorInput { + return {}; + } +} + +class MyInput extends EditorInput { + getPreferredEditorId(ids) { + return ids[1]; + } + + public getTypeId(): string { + return ''; + } + + public resolve(refresh?: boolean): any { + return null; + } +} + +class MyOtherInput extends EditorInput { + public getTypeId(): string { + return ''; + } + + public resolve(refresh?: boolean): any { + return null; + } +} +class MyResourceInput extends ResourceEditorInput { } + +suite('Workbench BaseEditor', () => { + + test('BaseEditor API', function (done) { + let e = new MyEditor('id', NullTelemetryService); + let input = new MyOtherInput(); + let options = new EditorOptions(); + + assert(!e.isVisible()); + assert(!e.input); + assert(!e.options); + e.setInput(input, options).then(() => { + assert.strictEqual(input, e.input); + assert.strictEqual(options, e.options); + + e.setVisible(true); + assert(e.isVisible()); + input.onDispose(() => { + assert(false); + }); + e.dispose(); + e.clearInput(); + e.setVisible(false); + assert(!e.isVisible()); + assert(!e.input); + assert(!e.options); + assert(!e.getControl()); + }).done(() => done()); + }); + + test('EditorDescriptor', function () { + let d = new EditorDescriptor('id', 'name', 'vs/workbench/test/browser/parts/editor/baseEditor.test', 'MyClass'); + assert.strictEqual(d.getId(), 'id'); + assert.strictEqual(d.getName(), 'name'); + }); + + test('Editor Registration', function () { + let d1 = new EditorDescriptor('id1', 'name', 'vs/workbench/test/browser/parts/editor/baseEditor.test', 'MyClass'); + let d2 = new EditorDescriptor('id2', 'name', 'vs/workbench/test/browser/parts/editor/baseEditor.test', 'MyOtherClass'); + + let oldEditorsCnt = EditorRegistry.getEditors().length; + let oldInputCnt = (EditorRegistry).getEditorInputs().length; + + EditorRegistry.registerEditor(d1, new SyncDescriptor(MyInput)); + EditorRegistry.registerEditor(d2, [new SyncDescriptor(MyInput), new SyncDescriptor(MyOtherInput)]); + + assert.equal(EditorRegistry.getEditors().length, oldEditorsCnt + 2); + assert.equal((EditorRegistry).getEditorInputs().length, oldInputCnt + 3); + + assert.strictEqual(EditorRegistry.getEditor(new MyInput()), d2); + assert.strictEqual(EditorRegistry.getEditor(new MyOtherInput()), d2); + + assert.strictEqual(EditorRegistry.getEditorById('id1'), d1); + assert.strictEqual(EditorRegistry.getEditorById('id2'), d2); + assert(!EditorRegistry.getEditorById('id3')); + }); + + test('Editor Lookup favors specific class over superclass (match on specific class)', function (done) { + let d1 = new EditorDescriptor('id1', 'name', 'vs/workbench/test/browser/parts/editor/baseEditor.test', 'MyEditor'); + let d2 = new EditorDescriptor('id2', 'name', 'vs/workbench/test/browser/parts/editor/baseEditor.test', 'MyOtherEditor'); + + let oldEditors = EditorRegistry.getEditors(); + (EditorRegistry).setEditors([]); + + EditorRegistry.registerEditor(d2, new SyncDescriptor(ResourceEditorInput)); + EditorRegistry.registerEditor(d1, new SyncDescriptor(MyResourceInput)); + + let inst = new TestInstantiationService(); + + inst.createInstance(EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', '', PLAINTEXT_MODE_ID, false)), 'id').then(editor => { + assert.strictEqual(editor.getId(), 'myEditor'); + + return inst.createInstance(EditorRegistry.getEditor(inst.createInstance(ResourceEditorInput, 'fake', '', '', PLAINTEXT_MODE_ID, false)), 'id').then(editor => { + assert.strictEqual(editor.getId(), 'myOtherEditor'); + + (EditorRegistry).setEditors(oldEditors); + }); + }).done(() => done()); + }); + + test('Editor Lookup favors specific class over superclass (match on super class)', function (done) { + let d1 = new EditorDescriptor('id1', 'name', 'vs/workbench/test/browser/parts/editor/baseEditor.test', 'MyOtherEditor'); + + let oldEditors = EditorRegistry.getEditors(); + (EditorRegistry).setEditors([]); + + EditorRegistry.registerEditor(d1, new SyncDescriptor(ResourceEditorInput)); + + let inst = new TestInstantiationService(); + + inst.createInstance(EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', '', PLAINTEXT_MODE_ID, false)), 'id').then(editor => { + assert.strictEqual('myOtherEditor', editor.getId()); + + (EditorRegistry).setEditors(oldEditors); + }).done(() => done()); + }); + + test('Editor Input Factory', function () { + EditorRegistry.setInstantiationService(workbenchInstantiationService()); + EditorRegistry.registerEditorInputFactory('myInputId', MyInputFactory); + + let factory = EditorRegistry.getEditorInputFactory('myInputId'); + assert(factory); + }); + + return { + MyEditor: MyEditor, + MyOtherEditor: MyOtherEditor + }; +}); \ No newline at end of file diff --git a/src/vs/workbench/test/browser/quickopen.test.ts b/src/vs/workbench/test/browser/quickopen.test.ts new file mode 100644 index 0000000000..d4f4176ac3 --- /dev/null +++ b/src/vs/workbench/test/browser/quickopen.test.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import 'vs/workbench/browser/parts/editor/editor.contribution'; // make sure to load all contributed editor things into tests +import { Promise, TPromise } from 'vs/base/common/winjs.base'; +import Event from 'vs/base/common/event'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenAction } from 'vs/workbench/browser/quickopen'; + +export class TestQuickOpenService implements IQuickOpenService { + public _serviceBrand: any; + + private callback: (prefix: string) => void; + + constructor(callback?: (prefix: string) => void) { + this.callback = callback; + } + + pick(arg: any, options?: any, token?: any): Promise { + return TPromise.as(null); + } + + input(options?: any, token?: any): Promise { + return TPromise.as(null); + } + + accept(): void { + } + + focus(): void { + } + + close(): void { + } + + show(prefix?: string, options?: any): Promise { + if (this.callback) { + this.callback(prefix); + } + + return TPromise.as(true); + } + + get onShow(): Event { + return null; + } + + get onHide(): Event { + return null; + } + + public dispose() { } + public navigate(): void { } +} + +suite('Workbench QuickOpen', () => { + + test('QuickOpen Handler and Registry', () => { + let registry = (Registry.as(QuickOpenExtensions.Quickopen)); + let handler = new QuickOpenHandlerDescriptor( + 'test', + 'TestHandler', + ',', + 'Handler', + null + ); + + registry.registerQuickOpenHandler(handler); + + assert(registry.getQuickOpenHandler(',') === handler); + + let handlers = registry.getQuickOpenHandlers(); + assert(handlers.some((handler: QuickOpenHandlerDescriptor) => handler.prefix === ',')); + }); + + test('QuickOpen Action', () => { + let defaultAction = new QuickOpenAction('id', 'label', void 0, new TestQuickOpenService((prefix: string) => assert(!prefix))); + let prefixAction = new QuickOpenAction('id', 'label', ',', new TestQuickOpenService((prefix: string) => assert(!!prefix))); + + defaultAction.run(); + prefixAction.run(); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/browser/viewlet.test.ts b/src/vs/workbench/test/browser/viewlet.test.ts new file mode 100644 index 0000000000..235a38f026 --- /dev/null +++ b/src/vs/workbench/test/browser/viewlet.test.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as Platform from 'vs/platform/registry/common/platform'; +import { ViewletDescriptor, Extensions } from 'vs/workbench/browser/viewlet'; +import * as Types from 'vs/base/common/types'; + +suite('Workbench Viewlet', () => { + + test('ViewletDescriptor API', function () { + let d = new ViewletDescriptor('moduleId', 'ctorName', 'id', 'name', 'class', 5); + assert.strictEqual(d.id, 'id'); + assert.strictEqual(d.name, 'name'); + assert.strictEqual(d.cssClass, 'class'); + assert.strictEqual(d.order, 5); + }); + + test('Editor Aware ViewletDescriptor API', function () { + let d = new ViewletDescriptor('moduleId', 'ctorName', 'id', 'name', 'class', 5); + assert.strictEqual(d.id, 'id'); + assert.strictEqual(d.name, 'name'); + + d = new ViewletDescriptor('moduleId', 'ctorName', 'id', 'name', 'class', 5); + assert.strictEqual(d.id, 'id'); + assert.strictEqual(d.name, 'name'); + }); + + test('Viewlet extension point and registration', function () { + assert(Types.isFunction(Platform.Registry.as(Extensions.Viewlets).registerViewlet)); + assert(Types.isFunction(Platform.Registry.as(Extensions.Viewlets).getViewlet)); + assert(Types.isFunction(Platform.Registry.as(Extensions.Viewlets).getViewlets)); + + let oldCount = Platform.Registry.as(Extensions.Viewlets).getViewlets().length; + let d = new ViewletDescriptor('moduleId', 'ctorName', 'reg-test-id', 'name'); + Platform.Registry.as(Extensions.Viewlets).registerViewlet(d); + + assert(d === Platform.Registry.as(Extensions.Viewlets).getViewlet('reg-test-id')); + assert.equal(oldCount + 1, Platform.Registry.as(Extensions.Viewlets).getViewlets().length); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/editor.test.ts b/src/vs/workbench/test/common/editor/editor.test.ts new file mode 100644 index 0000000000..659890f1c7 --- /dev/null +++ b/src/vs/workbench/test/common/editor/editor.test.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput, toResource } from 'vs/workbench/common/editor'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { IEditorModel } from 'vs/platform/editor/common/editor'; +import URI from 'vs/base/common/uri'; +import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; + +class ServiceAccessor { + constructor( @IUntitledEditorService public untitledEditorService: UntitledEditorService) { + } +} + +class FileEditorInput extends EditorInput { + + constructor(private resource: URI) { + super(); + } + + getTypeId(): string { + return 'editorResourceFileTest'; + } + + getResource(): URI { + return this.resource; + } + + resolve(refresh?: boolean): TPromise { + return TPromise.as(null); + } +} + +suite('Workbench - Editor', () => { + + let instantiationService: IInstantiationService; + let accessor: ServiceAccessor; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + }); + + teardown(() => { + accessor.untitledEditorService.revertAll(); + accessor.untitledEditorService.dispose(); + }); + + test('toResource', function () { + const service = accessor.untitledEditorService; + + assert.ok(!toResource(null)); + + const untitled = service.createOrGet(); + + assert.equal(toResource(untitled).toString(), untitled.getResource().toString()); + assert.equal(toResource(untitled, { supportSideBySide: true }).toString(), untitled.getResource().toString()); + assert.equal(toResource(untitled, { filter: 'untitled' }).toString(), untitled.getResource().toString()); + assert.equal(toResource(untitled, { filter: ['file', 'untitled'] }).toString(), untitled.getResource().toString()); + assert.ok(!toResource(untitled, { filter: 'file' })); + + const file = new FileEditorInput(URI.file('/some/path.txt')); + + assert.equal(toResource(file).toString(), file.getResource().toString()); + assert.equal(toResource(file, { supportSideBySide: true }).toString(), file.getResource().toString()); + assert.equal(toResource(file, { filter: 'file' }).toString(), file.getResource().toString()); + assert.equal(toResource(file, { filter: ['file', 'untitled'] }).toString(), file.getResource().toString()); + assert.ok(!toResource(file, { filter: 'untitled' })); + + const diffEditorInput = new DiffEditorInput('name', 'description', untitled, file); + + assert.ok(!toResource(diffEditorInput)); + assert.ok(!toResource(diffEditorInput, { filter: 'file' })); + assert.ok(!toResource(diffEditorInput, { supportSideBySide: false })); + + assert.equal(toResource(file, { supportSideBySide: true }).toString(), file.getResource().toString()); + assert.equal(toResource(file, { supportSideBySide: true, filter: 'file' }).toString(), file.getResource().toString()); + assert.equal(toResource(file, { supportSideBySide: true, filter: ['file', 'untitled'] }).toString(), file.getResource().toString()); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/editorDiffModel.test.ts b/src/vs/workbench/test/common/editor/editorDiffModel.test.ts new file mode 100644 index 0000000000..6adce0e2a0 --- /dev/null +++ b/src/vs/workbench/test/common/editor/editorDiffModel.test.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { EditorModel } from 'vs/workbench/common/editor'; +import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; +import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import URI from 'vs/base/common/uri'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { TestTextFileService, workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +class MyEditorModel extends EditorModel { } +class MyTextEditorModel extends BaseTextEditorModel { } + +class ServiceAccessor { + constructor( + @ITextModelService public textModelResolverService: ITextModelService, + @IModelService public modelService: IModelService, + @IModeService public modeService: IModeService, + @ITextFileService public textFileService: TestTextFileService + ) { + } +} + +suite('Workbench - EditorModel', () => { + let instantiationService: IInstantiationService; + let accessor: ServiceAccessor; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + }); + + test('TextDiffEditorModel', function (done) { + const dispose = accessor.textModelResolverService.registerTextModelContentProvider('test', { + provideTextContent: function (resource: URI): TPromise { + if (resource.scheme === 'test') { + let modelContent = 'Hello Test'; + let mode = accessor.modeService.getOrCreateMode('json'); + return TPromise.as(accessor.modelService.createModel(modelContent, mode, resource)); + } + + return TPromise.as(null); + } + }); + + let input = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'test', authority: null, path: 'thePath' })); + let otherInput = instantiationService.createInstance(ResourceEditorInput, 'name2', 'description', URI.from({ scheme: 'test', authority: null, path: 'thePath' })); + let diffInput = new DiffEditorInput('name', 'description', input, otherInput); + + diffInput.resolve(true).then((model: any) => { + assert(model); + assert(model instanceof TextDiffEditorModel); + + let diffEditorModel = model.textDiffEditorModel; + assert(diffEditorModel.original); + assert(diffEditorModel.modified); + + return diffInput.resolve(true).then((model: any) => { + assert(model.isResolved()); + + assert(diffEditorModel !== model.textDiffEditorModel); + diffInput.dispose(); + assert(!model.textDiffEditorModel); + + dispose.dispose(); + }); + }).done(() => { + done(); + }); + }); +}); diff --git a/src/vs/workbench/test/common/editor/editorInput.test.ts b/src/vs/workbench/test/common/editor/editorInput.test.ts new file mode 100644 index 0000000000..dc2b1283c2 --- /dev/null +++ b/src/vs/workbench/test/common/editor/editorInput.test.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; + +class MyEditorInput extends EditorInput { + public getTypeId(): string { + return ''; + } + + public resolve(refresh?: boolean): any { + return null; + } +} + +suite('Workbench - EditorInput', () => { + + test('EditorInput', function () { + let counter = 0; + let input = new MyEditorInput(); + let otherInput = new MyEditorInput(); + + assert(input.matches(input)); + assert(!input.matches(otherInput)); + assert(!input.matches(null)); + assert(!input.getName()); + + input.onDispose(() => { + assert(true); + counter++; + }); + + input.dispose(); + assert.equal(counter, 1); + }); + + test('DiffEditorInput', function () { + let counter = 0; + let input = new MyEditorInput(); + input.onDispose(() => { + assert(true); + counter++; + }); + + let otherInput = new MyEditorInput(); + otherInput.onDispose(() => { + assert(true); + counter++; + }); + + let diffInput = new DiffEditorInput('name', 'description', input, otherInput); + + assert.equal(diffInput.originalInput, input); + assert.equal(diffInput.modifiedInput, otherInput); + assert(diffInput.matches(diffInput)); + assert(!diffInput.matches(otherInput)); + assert(!diffInput.matches(null)); + + diffInput.dispose(); + assert.equal(counter, 0); + }); + + test('DiffEditorInput disposes when input inside disposes', function () { + let counter = 0; + let input = new MyEditorInput(); + let otherInput = new MyEditorInput(); + + let diffInput = new DiffEditorInput('name', 'description', input, otherInput); + diffInput.onDispose(() => { + counter++; + assert(true); + }); + + input.dispose(); + + input = new MyEditorInput(); + otherInput = new MyEditorInput(); + + let diffInput2 = new DiffEditorInput('name', 'description', input, otherInput); + diffInput2.onDispose(() => { + counter++; + assert(true); + }); + + otherInput.dispose(); + assert.equal(counter, 2); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/editorModel.test.ts b/src/vs/workbench/test/common/editor/editorModel.test.ts new file mode 100644 index 0000000000..7a2d83fa1a --- /dev/null +++ b/src/vs/workbench/test/common/editor/editorModel.test.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { EditorModel } from 'vs/workbench/common/editor'; +import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; + +class MyEditorModel extends EditorModel { } +class MyTextEditorModel extends BaseTextEditorModel { } + +suite('Workbench - EditorModel', () => { + + let instantiationService: TestInstantiationService; + let modeService: IModeService; + + setup(() => { + instantiationService = new TestInstantiationService(); + modeService = instantiationService.stub(IModeService, ModeServiceImpl); + }); + + test('EditorModel', function (done) { + let counter = 0; + + let m = new MyEditorModel(); + + m.onDispose(() => { + assert(true); + counter++; + }); + + m.load().then(model => { + assert(model === m); + assert.strictEqual(m.isResolved(), true); + m.dispose(); + assert.equal(counter, 1); + }).done(() => done()); + }); + + test('BaseTextEditorModel', function (done) { + let modelService = stubModelService(instantiationService); + + let m = new MyTextEditorModel(modelService, modeService); + m.load().then((model: any) => { + assert(model === m); + return model.createTextEditorModel('foo', null, 'text/plain').then(() => { + assert.strictEqual(m.isResolved(), true); + }); + }).done(() => { + m.dispose(); + done(); + }); + }); + + function stubModelService(instantiationService: TestInstantiationService): IModelService { + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + return instantiationService.createInstance(ModelServiceImpl); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/editorOptions.test.ts b/src/vs/workbench/test/common/editor/editorOptions.test.ts new file mode 100644 index 0000000000..8ad2a25dff --- /dev/null +++ b/src/vs/workbench/test/common/editor/editorOptions.test.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor'; + +suite('Workbench - EditorOptions', () => { + + test('EditorOptions', function () { + let options = new EditorOptions(); + + assert(!options.preserveFocus); + options.preserveFocus = true; + assert(options.preserveFocus); + assert(!options.forceOpen); + options.forceOpen = true; + assert(options.forceOpen); + + options = new EditorOptions(); + options.forceOpen = true; + }); + + test('TextEditorOptions', function () { + let options = new TextEditorOptions(); + let otherOptions = new TextEditorOptions(); + + assert(!options.hasOptionsDefined()); + options.selection(1, 1, 2, 2); + assert(options.hasOptionsDefined()); + + otherOptions.selection(1, 1, 2, 2); + + options = new TextEditorOptions(); + options.forceOpen = true; + options.selection(1, 1, 2, 2); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/rangeDecorations.test.ts b/src/vs/workbench/test/common/editor/rangeDecorations.test.ts new file mode 100644 index 0000000000..0e89fb66ca --- /dev/null +++ b/src/vs/workbench/test/common/editor/rangeDecorations.test.ts @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import URI from 'vs/base/common/uri'; +import { TestEditorService, workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; +import WorkbenchEditorService = require('vs/workbench/services/editor/common/editorService'); +import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; +import { Model } from 'vs/editor/common/model/model'; +import { mockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorInput } from 'vs/platform/editor/common/editor'; +import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { CoreNavigationCommands } from 'vs/editor/common/controller/coreCommands'; + +suite('Editor - Range decorations', () => { + + let instantiationService: TestInstantiationService; + let editorService: WorkbenchEditorService.IWorkbenchEditorService; + let modelService: IModelService; + let modeService: IModeService; + let codeEditor: editorCommon.ICommonCodeEditor; + let model: Model; + let text: string; + let testObject: RangeHighlightDecorations; + let modelsToDispose: Model[] = []; + + setup(() => { + instantiationService = workbenchInstantiationService(); + editorService = instantiationService.stub(WorkbenchEditorService.IWorkbenchEditorService, new TestEditorService()); + modeService = instantiationService.stub(IModeService, ModeServiceImpl); + modelService = instantiationService.stub(IModelService, stubModelService(instantiationService)); + text = 'LINE1' + '\n' + 'LINE2' + '\n' + 'LINE3' + '\n' + 'LINE4' + '\r\n' + 'LINE5'; + model = aModel(URI.file('some_file')); + codeEditor = mockCodeEditor([], { model }); + mockEditorService(codeEditor.getModel().uri); + + instantiationService.stub(WorkbenchEditorService.IWorkbenchEditorService, 'getActiveEditor', { getControl: () => { return codeEditor; } }); + + testObject = instantiationService.createInstance(RangeHighlightDecorations); + }); + + teardown(() => { + codeEditor.dispose(); + modelsToDispose.forEach(model => model.dispose()); + }); + + test('highlight range for the resource if it is an active editor', function () { + let range: IRange = { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }; + testObject.highlightRange({ resource: model.uri, range }); + + let actuals = rangeHighlightDecorations(model); + + assert.deepEqual([range], actuals); + }); + + test('remove highlight range', function () { + testObject.highlightRange({ resource: model.uri, range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 } }); + testObject.removeHighlightRange(); + + let actuals = rangeHighlightDecorations(model); + + assert.deepEqual([], actuals); + }); + + test('highlight range for the resource removes previous highlight', function () { + testObject.highlightRange({ resource: model.uri, range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 } }); + let range: IRange = { startLineNumber: 2, startColumn: 2, endLineNumber: 4, endColumn: 3 }; + testObject.highlightRange({ resource: model.uri, range }); + + let actuals = rangeHighlightDecorations(model); + + assert.deepEqual([range], actuals); + }); + + test('highlight range for a new resource removes highlight of previous resource', function () { + testObject.highlightRange({ resource: model.uri, range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 } }); + + let anotherModel = prepareActiveEditor('anotherModel'); + let range: IRange = { startLineNumber: 2, startColumn: 2, endLineNumber: 4, endColumn: 3 }; + testObject.highlightRange({ resource: anotherModel.uri, range }); + + let actuals = rangeHighlightDecorations(model); + assert.deepEqual([], actuals); + actuals = rangeHighlightDecorations(anotherModel); + assert.deepEqual([range], actuals); + }); + + test('highlight is removed on model change', function () { + testObject.highlightRange({ resource: model.uri, range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 } }); + prepareActiveEditor('anotherModel'); + + let actuals = rangeHighlightDecorations(model); + assert.deepEqual([], actuals); + }); + + test('highlight is removed on cursor position change', function () { + testObject.highlightRange({ resource: model.uri, range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 } }); + codeEditor.trigger('mouse', CoreNavigationCommands.MoveTo.id, { + position: new Position(2, 1) + }); + + let actuals = rangeHighlightDecorations(model); + assert.deepEqual([], actuals); + }); + + test('range is not highlight if not active editor', function () { + let model = aModel(URI.file('some model')); + testObject.highlightRange({ resource: model.uri, range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 } }); + + let actuals = rangeHighlightDecorations(model); + assert.deepEqual([], actuals); + }); + + test('previous highlight is not removed if not active editor', function () { + let range: IRange = { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }; + testObject.highlightRange({ resource: model.uri, range }); + + let model1 = aModel(URI.file('some model')); + testObject.highlightRange({ resource: model1.uri, range: { startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 } }); + + let actuals = rangeHighlightDecorations(model); + assert.deepEqual([range], actuals); + }); + + function prepareActiveEditor(resource: string): Model { + let model = aModel(URI.file(resource)); + codeEditor.setModel(model); + mockEditorService(model.uri); + return model; + } + + function aModel(resource: URI, content: string = text): Model { + let model = Model.createFromString(content, TextModel.DEFAULT_CREATION_OPTIONS, null, resource); + modelsToDispose.push(model); + return model; + } + + function mockEditorService(editorInput: IEditorInput); + function mockEditorService(resource: URI); + function mockEditorService(arg: any) { + let editorInput: IEditorInput = arg instanceof URI ? instantiationService.createInstance(FileEditorInput, arg, void 0) : arg; + instantiationService.stub(WorkbenchEditorService.IWorkbenchEditorService, 'getActiveEditorInput', editorInput); + } + + function rangeHighlightDecorations(m: Model): IRange[] { + let rangeHighlights: IRange[] = []; + + for (let dec of m.getAllDecorations()) { + if (dec.options.className === 'rangeHighlight') { + rangeHighlights.push(dec.range); + } + } + + rangeHighlights.sort(Range.compareRangesUsingStarts); + return rangeHighlights; + } + + function stubModelService(instantiationService: TestInstantiationService): IModelService { + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + return instantiationService.createInstance(ModelServiceImpl); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts new file mode 100644 index 0000000000..788925e24e --- /dev/null +++ b/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; + +class ServiceAccessor { + constructor( + @IModelService public modelService: IModelService, + @IModeService public modeService: IModeService + ) { + } +} + +suite('Workbench - ResourceEditorInput', () => { + + let instantiationService: IInstantiationService; + let accessor: ServiceAccessor; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + }); + + test('simple', function () { + let resource = URI.from({ scheme: 'inmemory', authority: null, path: 'thePath' }); + accessor.modelService.createModel('function test() {}', accessor.modeService.getOrCreateMode('text'), resource); + let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource); + + return input.resolve().then((model: ResourceEditorModel) => { + assert.ok(model); + assert.equal(model.getValue(), 'function test() {}'); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/untitledEditor.test.ts b/src/vs/workbench/test/common/editor/untitledEditor.test.ts new file mode 100644 index 0000000000..50c9ee00e8 --- /dev/null +++ b/src/vs/workbench/test/common/editor/untitledEditor.test.ts @@ -0,0 +1,316 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import * as assert from 'assert'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { join } from 'vs/base/common/paths'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; +import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; +import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; + +export class TestUntitledEditorService extends UntitledEditorService { + + public get(resource: URI): UntitledEditorInput { + return super.get(resource); + } + + public getAll(resources?: URI[]): UntitledEditorInput[] { + return super.getAll(resources); + } +} + +class ServiceAccessor { + constructor( + @IUntitledEditorService public untitledEditorService: TestUntitledEditorService, + @IModeService public modeService: ModeServiceImpl, + @IConfigurationService public testConfigurationService: TestConfigurationService) { + } +} + +suite('Workbench - Untitled Editor', () => { + + let instantiationService: IInstantiationService; + let accessor: ServiceAccessor; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + }); + + teardown(() => { + accessor.untitledEditorService.revertAll(); + accessor.untitledEditorService.dispose(); + }); + + test('Untitled Editor Service', function (done) { + const service = accessor.untitledEditorService; + assert.equal(service.getAll().length, 0); + + const input1 = service.createOrGet(); + assert.equal(input1, service.createOrGet(input1.getResource())); + + assert.ok(service.exists(input1.getResource())); + assert.ok(!service.exists(URI.file('testing'))); + + const input2 = service.createOrGet(); + + // get() / getAll() + assert.equal(service.get(input1.getResource()), input1); + assert.equal(service.getAll().length, 2); + assert.equal(service.getAll([input1.getResource(), input2.getResource()]).length, 2); + + // revertAll() + service.revertAll([input1.getResource()]); + assert.ok(input1.isDisposed()); + assert.equal(service.getAll().length, 1); + + // dirty + input2.resolve().then((model: UntitledEditorModel) => { + assert.ok(!service.isDirty(input2.getResource())); + + const listener = service.onDidChangeDirty(resource => { + listener.dispose(); + + assert.equal(resource.toString(), input2.getResource().toString()); + + assert.ok(service.isDirty(input2.getResource())); + assert.equal(service.getDirty()[0].toString(), input2.getResource().toString()); + assert.equal(service.getDirty([input2.getResource()])[0].toString(), input2.getResource().toString()); + assert.equal(service.getDirty([input1.getResource()]).length, 0); + + service.revertAll(); + assert.equal(service.getAll().length, 0); + assert.ok(!input2.isDirty()); + assert.ok(!model.isDirty()); + + input2.dispose(); + + assert.ok(!service.exists(input2.getResource())); + + done(); + }); + + model.textEditorModel.setValue('foo bar'); + }); + }); + + test('Untitled with associated resource', function () { + const service = accessor.untitledEditorService; + const file = URI.file(join('C:\\', '/foo/file.txt')); + const untitled = service.createOrGet(file); + + assert.ok(service.hasAssociatedFilePath(untitled.getResource())); + + untitled.dispose(); + }); + + test('Untitled no longer dirty when content gets empty', function (done) { + const service = accessor.untitledEditorService; + const input = service.createOrGet(); + + // dirty + input.resolve().then((model: UntitledEditorModel) => { + model.textEditorModel.setValue('foo bar'); + assert.ok(model.isDirty()); + + model.textEditorModel.setValue(''); + assert.ok(!model.isDirty()); + + input.dispose(); + + done(); + }); + }); + + test('Untitled via loadOrCreate', function (done) { + const service = accessor.untitledEditorService; + service.loadOrCreate().then(model1 => { + model1.textEditorModel.setValue('foo bar'); + assert.ok(model1.isDirty()); + + model1.textEditorModel.setValue(''); + assert.ok(!model1.isDirty()); + + return service.loadOrCreate({ initialValue: 'Hello World' }).then(model2 => { + assert.equal(model2.getValue(), 'Hello World'); + + const input = service.createOrGet(); + + return service.loadOrCreate({ resource: input.getResource() }).then(model3 => { + assert.equal(model3.getResource().toString(), input.getResource().toString()); + + const file = URI.file(join('C:\\', '/foo/file44.txt')); + return service.loadOrCreate({ resource: file }).then(model4 => { + assert.ok(service.hasAssociatedFilePath(model4.getResource())); + assert.ok(model4.isDirty()); + + model1.dispose(); + model2.dispose(); + model3.dispose(); + model4.dispose(); + input.dispose(); + + done(); + }); + }); + }); + }); + }); + + test('Untitled suggest name', function () { + const service = accessor.untitledEditorService; + const input = service.createOrGet(); + + assert.ok(service.suggestFileName(input.getResource())); + }); + + test('Untitled with associated path remains dirty when content gets empty', function (done) { + const service = accessor.untitledEditorService; + const file = URI.file(join('C:\\', '/foo/file.txt')); + const input = service.createOrGet(file); + + // dirty + input.resolve().then((model: UntitledEditorModel) => { + model.textEditorModel.setValue('foo bar'); + assert.ok(model.isDirty()); + + model.textEditorModel.setValue(''); + assert.ok(model.isDirty()); + + input.dispose(); + + done(); + }); + }); + + test('Untitled created with files.defaultLanguage setting', function () { + const defaultLanguage = 'javascript'; + const config = accessor.testConfigurationService; + config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage }); + + const service = accessor.untitledEditorService; + const input = service.createOrGet(); + + assert.equal(input.getModeId(), defaultLanguage); + + config.setUserConfiguration('files', { 'defaultLanguage': undefined }); + + input.dispose(); + }); + + test('Untitled created with modeId overrides files.defaultLanguage setting', function () { + const modeId = 'typescript'; + const defaultLanguage = 'javascript'; + const config = accessor.testConfigurationService; + config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage }); + + const service = accessor.untitledEditorService; + const input = service.createOrGet(null, modeId); + + assert.equal(input.getModeId(), modeId); + + config.setUserConfiguration('files', { 'defaultLanguage': undefined }); + + input.dispose(); + }); + + test('encoding change event', function (done) { + const service = accessor.untitledEditorService; + const input = service.createOrGet(); + + let counter = 0; + + service.onDidChangeEncoding(r => { + counter++; + assert.equal(r.toString(), input.getResource().toString()); + }); + + // dirty + input.resolve().then((model: UntitledEditorModel) => { + model.setEncoding('utf16'); + + assert.equal(counter, 1); + + input.dispose(); + + done(); + }); + }); + + test('onDidChangeContent event', done => { + const service = accessor.untitledEditorService; + const input = service.createOrGet(); + + UntitledEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = 0; + + let counter = 0; + + service.onDidChangeContent(r => { + counter++; + assert.equal(r.toString(), input.getResource().toString()); + }); + + input.resolve().then((model: UntitledEditorModel) => { + model.textEditorModel.setValue('foo'); + assert.equal(counter, 0, 'Dirty model should not trigger event immediately'); + + TPromise.timeout(3).then(() => { + assert.equal(counter, 1, 'Dirty model should trigger event'); + + model.textEditorModel.setValue('bar'); + TPromise.timeout(3).then(() => { + assert.equal(counter, 2, 'Content change when dirty should trigger event'); + + model.textEditorModel.setValue(''); + TPromise.timeout(3).then(() => { + assert.equal(counter, 3, 'Manual revert should trigger event'); + + model.textEditorModel.setValue('foo'); + TPromise.timeout(3).then(() => { + assert.equal(counter, 4, 'Dirty model should trigger event'); + + model.revert(); + TPromise.timeout(3).then(() => { + assert.equal(counter, 5, 'Revert should trigger event'); + + input.dispose(); + + done(); + }); + }); + }); + }); + }); + }); + }); + + test('onDidDisposeModel event', done => { + const service = accessor.untitledEditorService; + const input = service.createOrGet(); + + let counter = 0; + + service.onDidDisposeModel(r => { + counter++; + assert.equal(r.toString(), input.getResource().toString()); + }); + + input.resolve().then((model: UntitledEditorModel) => { + assert.equal(counter, 0); + input.dispose(); + assert.equal(counter, 1); + + done(); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/common/memento.test.ts b/src/vs/workbench/test/common/memento.test.ts new file mode 100644 index 0000000000..61da604acb --- /dev/null +++ b/src/vs/workbench/test/common/memento.test.ts @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; + +import { StorageScope } from 'vs/platform/storage/common/storage'; +import { Memento, Scope } from 'vs/workbench/common/memento'; +import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService'; +import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; + +suite('Workbench Memento', () => { + let context; + let storage; + + setup(() => { + storage = new StorageService(new InMemoryLocalStorage(), null, TestWorkspace.id); + }); + + test('Loading and Saving Memento with Scopes', () => { + let myMemento = new Memento('memento.test'); + + // Global + let memento: any = myMemento.getMemento(storage); + memento.foo = [1, 2, 3]; + let globalMemento = myMemento.getMemento(storage, Scope.GLOBAL); + assert.deepEqual(globalMemento, memento); + + // Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + assert(memento); + memento.foo = 'Hello World'; + + myMemento.saveMemento(); + + // Global + memento = myMemento.getMemento(storage); + assert.deepEqual(memento, { foo: [1, 2, 3] }); + globalMemento = myMemento.getMemento(storage, Scope.GLOBAL); + assert.deepEqual(globalMemento, memento); + + // Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + assert.deepEqual(memento, { foo: 'Hello World' }); + + // Assert the Mementos are stored properly in storage + assert.deepEqual(JSON.parse(storage.get('memento/memento.test')), { foo: [1, 2, 3] }); + + assert.deepEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.WORKSPACE)), { foo: 'Hello World' }); + + // Delete Global + memento = myMemento.getMemento(storage, context); + delete memento.foo; + + // Delete Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + delete memento.foo; + + myMemento.saveMemento(); + + // Global + memento = myMemento.getMemento(storage, context); + assert.deepEqual(memento, {}); + + // Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + assert.deepEqual(memento, {}); + + // Assert the Mementos are also removed from storage + assert.strictEqual(storage.get('memento/memento.test', Scope.GLOBAL, null), null); + + assert.strictEqual(storage.get('memento/memento.test', Scope.WORKSPACE, null), null); + }); + + test('Save and Load', () => { + let myMemento = new Memento('memento.test'); + + // Global + let memento: any = myMemento.getMemento(storage, context); + memento.foo = [1, 2, 3]; + + // Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + assert(memento); + memento.foo = 'Hello World'; + + myMemento.saveMemento(); + + // Global + memento = myMemento.getMemento(storage, context); + assert.deepEqual(memento, { foo: [1, 2, 3] }); + let globalMemento = myMemento.getMemento(storage, Scope.GLOBAL); + assert.deepEqual(globalMemento, memento); + + // Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + assert.deepEqual(memento, { foo: 'Hello World' }); + + // Global + memento = myMemento.getMemento(storage, context); + memento.foo = [4, 5, 6]; + + // Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + assert(memento); + memento.foo = 'World Hello'; + + myMemento.saveMemento(); + + // Global + memento = myMemento.getMemento(storage, context); + assert.deepEqual(memento, { foo: [4, 5, 6] }); + globalMemento = myMemento.getMemento(storage, Scope.GLOBAL); + assert.deepEqual(globalMemento, memento); + + // Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + assert.deepEqual(memento, { foo: 'World Hello' }); + + // Delete Global + memento = myMemento.getMemento(storage, context); + delete memento.foo; + + // Delete Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + delete memento.foo; + + myMemento.saveMemento(); + + // Global + memento = myMemento.getMemento(storage, context); + assert.deepEqual(memento, {}); + + // Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + assert.deepEqual(memento, {}); + }); + + test('Save and Load - 2 Components with same id', () => { + let myMemento = new Memento('memento.test'); + let myMemento2 = new Memento('memento.test'); + + // Global + let memento: any = myMemento.getMemento(storage, context); + memento.foo = [1, 2, 3]; + + memento = myMemento2.getMemento(storage, context); + memento.bar = [1, 2, 3]; + + // Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + assert(memento); + memento.foo = 'Hello World'; + + memento = myMemento2.getMemento(storage, Scope.WORKSPACE); + assert(memento); + memento.bar = 'Hello World'; + + myMemento.saveMemento(); + myMemento2.saveMemento(); + + // Global + memento = myMemento.getMemento(storage, context); + assert.deepEqual(memento, { foo: [1, 2, 3], bar: [1, 2, 3] }); + let globalMemento = myMemento.getMemento(storage, Scope.GLOBAL); + assert.deepEqual(globalMemento, memento); + + memento = myMemento2.getMemento(storage, context); + assert.deepEqual(memento, { foo: [1, 2, 3], bar: [1, 2, 3] }); + globalMemento = myMemento2.getMemento(storage, Scope.GLOBAL); + assert.deepEqual(globalMemento, memento); + + // Workspace + memento = myMemento.getMemento(storage, Scope.WORKSPACE); + assert.deepEqual(memento, { foo: 'Hello World', bar: 'Hello World' }); + + memento = myMemento2.getMemento(storage, Scope.WORKSPACE); + assert.deepEqual(memento, { foo: 'Hello World', bar: 'Hello World' }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts new file mode 100644 index 0000000000..f76beb90c4 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -0,0 +1,439 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { setUnexpectedErrorHandler, errorHandler } from 'vs/base/common/errors'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as types from 'vs/workbench/api/node/extHostTypes'; +import * as EditorCommon from 'vs/editor/common/editorCommon'; +import { Model as EditorModel } from 'vs/editor/common/model/model'; +import { TestThreadService } from './testThreadService'; +import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; +import { MainThreadLanguageFeatures } from 'vs/workbench/api/electron-browser/mainThreadLanguageFeatures'; +import { IHeapService } from 'vs/workbench/api/electron-browser/mainThreadHeapService'; +import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands'; +import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; +import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; +import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands'; +import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; +import { MainContext, ExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; +import * as vscode from 'vscode'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +const defaultSelector = { scheme: 'far' }; +const model: EditorCommon.IModel = EditorModel.createFromString( + [ + 'This is the first line', + 'This is the second line', + 'This is the third line', + ].join('\n'), + undefined, + undefined, + URI.parse('far://testing/file.b')); + +let threadService: TestThreadService; +let extHost: ExtHostLanguageFeatures; +let mainThread: MainThreadLanguageFeatures; +let commands: ExtHostCommands; +let disposables: vscode.Disposable[] = []; +let originalErrorHandler: (e: any) => any; + +suite('ExtHostLanguageFeatureCommands', function () { + + suiteSetup((done) => { + + originalErrorHandler = errorHandler.getUnexpectedErrorHandler(); + setUnexpectedErrorHandler(() => { }); + + // Use IInstantiationService to get typechecking when instantiating + let inst: IInstantiationService; + { + let instantiationService = new TestInstantiationService(); + threadService = new TestThreadService(); + instantiationService.stub(IHeapService, { + _serviceBrand: undefined, + trackRecursive(args) { + // nothing + return args; + } + }); + instantiationService.stub(ICommandService, { + _serviceBrand: undefined, + executeCommand(id, args): any { + if (!CommandsRegistry.getCommands()[id]) { + return TPromise.wrapError(new Error(id + ' NOT known')); + } + let { handler } = CommandsRegistry.getCommands()[id]; + return TPromise.as(instantiationService.invokeFunction(handler, args)); + } + }); + instantiationService.stub(IMarkerService, new MarkerService()); + instantiationService.stub(IModelService, { + _serviceBrand: IModelService, + getModel(): any { return model; }, + createModel(): any { throw new Error(); }, + updateModel(): any { throw new Error(); }, + setMode(): any { throw new Error(); }, + destroyModel(): any { throw new Error(); }, + getModels(): any { throw new Error(); }, + onModelAdded: undefined, + onModelModeChanged: undefined, + onModelRemoved: undefined, + getCreationOptions(): any { throw new Error(); } + }); + inst = instantiationService; + } + + const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(threadService); + extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ + addedDocuments: [{ + isDirty: false, + versionId: model.getVersionId(), + modeId: model.getLanguageIdentifier().language, + url: model.uri, + lines: model.getValue().split(model.getEOL()), + EOL: model.getEOL(), + }] + }); + const extHostDocuments = new ExtHostDocuments(threadService, extHostDocumentsAndEditors); + threadService.set(ExtHostContext.ExtHostDocuments, extHostDocuments); + + const heapService = new ExtHostHeapService(); + + commands = new ExtHostCommands(threadService, heapService); + threadService.set(ExtHostContext.ExtHostCommands, commands); + threadService.setTestInstance(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, threadService)); + ExtHostApiCommands.register(commands); + + const diagnostics = new ExtHostDiagnostics(threadService); + threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics); + + extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, heapService, diagnostics); + threadService.set(ExtHostContext.ExtHostLanguageFeatures, extHost); + + mainThread = threadService.setTestInstance(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, threadService)); + + threadService.sync().then(done, done); + }); + + suiteTeardown(() => { + setUnexpectedErrorHandler(originalErrorHandler); + model.dispose(); + }); + + teardown(function (done) { + while (disposables.length) { + disposables.pop().dispose(); + } + threadService.sync() + .then(() => done(), err => done(err)); + }); + + // --- workspace symbols + + test('WorkspaceSymbols, invalid arguments', function (done) { + let promises = [ + commands.executeCommand('vscode.executeWorkspaceSymbolProvider'), + commands.executeCommand('vscode.executeWorkspaceSymbolProvider', null), + commands.executeCommand('vscode.executeWorkspaceSymbolProvider', undefined), + commands.executeCommand('vscode.executeWorkspaceSymbolProvider', true) + ]; + + // threadService.sync().then(() => { + TPromise.join(promises).then(undefined, (err: any[]) => { + assert.equal(err.length, 4); + done(); + return []; + }); + // }); + }); + + test('WorkspaceSymbols, back and forth', function (done) { + + disposables.push(extHost.registerWorkspaceSymbolProvider({ + provideWorkspaceSymbols(query): any { + return [ + new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/first')), + new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/second')) + ]; + } + })); + + disposables.push(extHost.registerWorkspaceSymbolProvider({ + provideWorkspaceSymbols(query): any { + return [ + new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/first')) + ]; + } + })); + + threadService.sync().then(() => { + commands.executeCommand('vscode.executeWorkspaceSymbolProvider', 'testing').then(value => { + + for (let info of value) { + assert.ok(info instanceof types.SymbolInformation); + assert.equal(info.name, 'testing'); + assert.equal(info.kind, types.SymbolKind.Array); + } + assert.equal(value.length, 3); + done(); + }, done); + }, done); + }); + + + // --- definition + + test('Definition, invalid arguments', function (done) { + let promises = [ + commands.executeCommand('vscode.executeDefinitionProvider'), + commands.executeCommand('vscode.executeDefinitionProvider', null), + commands.executeCommand('vscode.executeDefinitionProvider', undefined), + commands.executeCommand('vscode.executeDefinitionProvider', true, false) + ]; + + // threadService.sync().then(() => { + TPromise.join(promises).then(undefined, (err: any[]) => { + assert.equal(err.length, 4); + done(); + return []; + }); + // }); + }); + + test('Definition, back and forth', function () { + + disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + provideDefinition(doc: any): any { + return new types.Location(doc.uri, new types.Range(0, 0, 0, 0)); + } + })); + disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + provideDefinition(doc: any): any { + return [ + new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), + new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), + new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), + ]; + } + })); + + return threadService.sync().then(() => { + return commands.executeCommand('vscode.executeDefinitionProvider', model.uri, new types.Position(0, 0)).then(values => { + assert.equal(values.length, 4); + for (let v of values) { + assert.ok(v.uri instanceof URI); + assert.ok(v.range instanceof types.Range); + } + }); + }); + }); + + // --- references + + test('reference search, back and forth', function () { + + disposables.push(extHost.registerReferenceProvider(defaultSelector, { + provideReferences(doc: any) { + return [ + new types.Location(URI.parse('some:uri/path'), new types.Range(0, 1, 0, 5)) + ]; + } + })); + + return commands.executeCommand('vscode.executeReferenceProvider', model.uri, new types.Position(0, 0)).then(values => { + assert.equal(values.length, 1); + let [first] = values; + assert.equal(first.uri.toString(), 'some:uri/path'); + assert.equal(first.range.start.line, 0); + assert.equal(first.range.start.character, 1); + assert.equal(first.range.end.line, 0); + assert.equal(first.range.end.character, 5); + }); + }); + + // --- outline + + test('Outline, back and forth', function (done) { + disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + provideDocumentSymbols(): any { + return [ + new types.SymbolInformation('testing1', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0)), + new types.SymbolInformation('testing2', types.SymbolKind.Enum, new types.Range(0, 1, 0, 3)), + ]; + } + })); + + threadService.sync().then(() => { + commands.executeCommand('vscode.executeDocumentSymbolProvider', model.uri).then(values => { + assert.equal(values.length, 2); + let [first, second] = values; + assert.equal(first.name, 'testing2'); + assert.equal(second.name, 'testing1'); + done(); + }, done); + }, done); + }); + + // --- suggest + + test('Suggest, back and forth', function () { + disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + provideCompletionItems(doc, pos): any { + let a = new types.CompletionItem('item1'); + let b = new types.CompletionItem('item2'); + b.textEdit = types.TextEdit.replace(new types.Range(0, 4, 0, 8), 'foo'); // overwite after + let c = new types.CompletionItem('item3'); + c.textEdit = types.TextEdit.replace(new types.Range(0, 1, 0, 6), 'foobar'); // overwite before & after + + // snippet string! + let d = new types.CompletionItem('item4'); + d.range = new types.Range(0, 1, 0, 4);// overwite before + d.insertText = new types.SnippetString('foo$0bar'); + return [a, b, c, d]; + } + }, [])); + + return threadService.sync().then(() => { + return commands.executeCommand('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)).then(list => { + + assert.ok(list instanceof types.CompletionList); + let values = list.items; + assert.ok(Array.isArray(values)); + assert.equal(values.length, 4); + let [first, second, third, fourth] = values; + assert.equal(first.label, 'item1'); + assert.equal(first.textEdit.newText, 'item1'); + assert.equal(first.textEdit.range.start.line, 0); + assert.equal(first.textEdit.range.start.character, 0); + assert.equal(first.textEdit.range.end.line, 0); + assert.equal(first.textEdit.range.end.character, 4); + + assert.equal(second.label, 'item2'); + assert.equal(second.textEdit.newText, 'foo'); + assert.equal(second.textEdit.range.start.line, 0); + assert.equal(second.textEdit.range.start.character, 4); + assert.equal(second.textEdit.range.end.line, 0); + assert.equal(second.textEdit.range.end.character, 8); + + assert.equal(third.label, 'item3'); + assert.equal(third.textEdit.newText, 'foobar'); + assert.equal(third.textEdit.range.start.line, 0); + assert.equal(third.textEdit.range.start.character, 1); + assert.equal(third.textEdit.range.end.line, 0); + assert.equal(third.textEdit.range.end.character, 6); + + assert.equal(fourth.label, 'item4'); + assert.equal(fourth.textEdit, undefined); + assert.equal(fourth.range.start.line, 0); + assert.equal(fourth.range.start.character, 1); + assert.equal(fourth.range.end.line, 0); + assert.equal(fourth.range.end.character, 4); + assert.ok(fourth.insertText instanceof types.SnippetString); + assert.equal((fourth.insertText).value, 'foo$0bar'); + }); + }); + }); + + test('Suggest, return CompletionList !array', function (done) { + disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + provideCompletionItems(): any { + let a = new types.CompletionItem('item1'); + let b = new types.CompletionItem('item2'); + return new types.CompletionList([a, b], true); + } + }, [])); + + threadService.sync().then(() => { + return commands.executeCommand('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)).then(list => { + assert.ok(list instanceof types.CompletionList); + assert.equal(list.isIncomplete, true); + done(); + }); + }); + }); + + // --- quickfix + + test('QuickFix, back and forth', function () { + disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + provideCodeActions(): any { + return [{ command: 'testing', title: 'Title', arguments: [1, 2, true] }]; + } + })); + + return threadService.sync().then(() => { + return commands.executeCommand('vscode.executeCodeActionProvider', model.uri, new types.Range(0, 0, 1, 1)).then(value => { + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.title, 'Title'); + assert.equal(first.command, 'testing'); + assert.deepEqual(first.arguments, [1, 2, true]); + }); + }); + }); + + // --- code lens + + test('CodeLens, back and forth', function () { + + const complexArg = { + foo() { }, + bar() { }, + big: extHost + }; + + disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + provideCodeLenses(): any { + return [new types.CodeLens(new types.Range(0, 0, 1, 1), { title: 'Title', command: 'cmd', arguments: [1, true, complexArg] })]; + } + })); + + return threadService.sync().then(() => { + return commands.executeCommand('vscode.executeCodeLensProvider', model.uri).then(value => { + assert.equal(value.length, 1); + let [first] = value; + + assert.equal(first.command.title, 'Title'); + assert.equal(first.command.command, 'cmd'); + assert.equal(first.command.arguments[0], 1); + assert.equal(first.command.arguments[1], true); + assert.equal(first.command.arguments[2], complexArg); + }); + }); + }); + + test('Links, back and forth', function () { + + disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + provideDocumentLinks(): any { + return [new types.DocumentLink(new types.Range(0, 0, 0, 20), URI.parse('foo:bar'))]; + } + })); + + return threadService.sync().then(() => { + return commands.executeCommand('vscode.executeLinkProvider', model.uri).then(value => { + assert.equal(value.length, 1); + let [first] = value; + + assert.equal(first.target.toString(), 'foo:bar'); + assert.equal(first.range.start.line, 0); + assert.equal(first.range.start.character, 0); + assert.equal(first.range.end.line, 0); + assert.equal(first.range.end.character, 20); + }); + }); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts new file mode 100644 index 0000000000..ce2dde0e41 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; +import { MainThreadCommandsShape } from 'vs/workbench/api/node/extHost.protocol'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { OneGetThreadService } from './testThreadService'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; + +suite('ExtHostCommands', function () { + + test('dispose calls unregister', function () { + + let lastUnregister: string; + + const shape = new class extends mock() { + $registerCommand(id: string): TPromise { + return undefined; + } + $unregisterCommand(id: string): TPromise { + lastUnregister = id; + return undefined; + } + }; + + const commands = new ExtHostCommands(OneGetThreadService(shape), undefined); + commands.registerCommand('foo', (): any => { }).dispose(); + assert.equal(lastUnregister, 'foo'); + assert.equal(CommandsRegistry.getCommand('foo'), undefined); + + }); + + test('dispose bubbles only once', function () { + + let unregisterCounter = 0; + + const shape = new class extends mock() { + $registerCommand(id: string): TPromise { + return undefined; + } + $unregisterCommand(id: string): TPromise { + unregisterCounter += 1; + return undefined; + } + }; + + const commands = new ExtHostCommands(OneGetThreadService(shape), undefined); + const reg = commands.registerCommand('foo', (): any => { }); + reg.dispose(); + reg.dispose(); + reg.dispose(); + assert.equal(unregisterCounter, 1); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts new file mode 100644 index 0000000000..368400e0d0 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -0,0 +1,405 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; +import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { MainThreadConfigurationShape } from 'vs/workbench/api/node/extHost.protocol'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ConfigurationTarget, ConfigurationEditingErrorCode, ConfigurationEditingError } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { ConfigurationModel } from 'vs/platform/configuration/common/configuration'; +import { TestThreadService } from './testThreadService'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; + +suite('ExtHostConfiguration', function () { + + class RecordingShape extends mock() { + lastArgs: [ConfigurationTarget, string, any]; + $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise { + this.lastArgs = [target, key, value]; + return TPromise.as(void 0); + } + }; + + function createExtHostConfiguration(contents: any = Object.create(null), shape?: MainThreadConfigurationShape) { + if (!shape) { + shape = new class extends mock() { }; + } + return new ExtHostConfiguration(shape, new ExtHostWorkspace(new TestThreadService(), null), { + defaults: new ConfigurationModel(contents), + user: new ConfigurationModel(contents), + workspace: new ConfigurationModel(), + folders: Object.create(null) + }); + } + + test('getConfiguration fails regression test 1.7.1 -> 1.8 #15552', function () { + const extHostConfig = createExtHostConfiguration({ + 'search': { + 'exclude': { + '**/node_modules': true + } + } + }); + + assert.equal(extHostConfig.getConfiguration('search.exclude')['**/node_modules'], true); + assert.equal(extHostConfig.getConfiguration('search.exclude').get('**/node_modules'), true); + assert.equal(extHostConfig.getConfiguration('search').get('exclude')['**/node_modules'], true); + + assert.equal(extHostConfig.getConfiguration('search.exclude').has('**/node_modules'), true); + assert.equal(extHostConfig.getConfiguration('search').has('exclude.**/node_modules'), true); + }); + + test('has/get', function () { + + const all = createExtHostConfiguration({ + 'farboo': { + 'config0': true, + 'nested': { + 'config1': 42, + 'config2': 'Das Pferd frisst kein Reis.' + }, + 'config4': '' + } + }); + + const config = all.getConfiguration('farboo'); + + assert.ok(config.has('config0')); + assert.equal(config.get('config0'), true); + assert.equal(config.get('config4'), ''); + assert.equal(config['config0'], true); + assert.equal(config['config4'], ''); + + assert.ok(config.has('nested.config1')); + assert.equal(config.get('nested.config1'), 42); + assert.ok(config.has('nested.config2')); + assert.equal(config.get('nested.config2'), 'Das Pferd frisst kein Reis.'); + + assert.ok(config.has('nested')); + assert.deepEqual(config.get('nested'), { config1: 42, config2: 'Das Pferd frisst kein Reis.' }); + }); + + test('inspect in no workspace context', function () { + const testObject = new ExtHostConfiguration( + new class extends mock() { }, + new ExtHostWorkspace(new TestThreadService(), null), + { + defaults: new ConfigurationModel({ + 'editor': { + 'wordWrap': 'off' + } + }, ['editor.wordWrap']), + user: new ConfigurationModel({ + 'editor': { + 'wordWrap': 'on' + } + }, ['editor.wordWrap']), + workspace: new ConfigurationModel({}, []), + folders: Object.create(null) + } + ); + + let actual = testObject.getConfiguration().inspect('editor.wordWrap'); + assert.equal(actual.defaultValue, 'off'); + assert.equal(actual.globalValue, 'on'); + assert.equal(actual.workspaceValue, undefined); + assert.equal(actual.workspaceFolderValue, undefined); + + actual = testObject.getConfiguration('editor').inspect('wordWrap'); + assert.equal(actual.defaultValue, 'off'); + assert.equal(actual.globalValue, 'on'); + assert.equal(actual.workspaceValue, undefined); + assert.equal(actual.workspaceFolderValue, undefined); + }); + + test('inspect in single root context', function () { + const workspaceUri = URI.file('foo'); + const folders = Object.create(null); + const workspace = new ConfigurationModel({ + 'editor': { + 'wordWrap': 'bounded' + } + }, ['editor.wordWrap']); + folders[workspaceUri.toString()] = workspace; + const testObject = new ExtHostConfiguration( + new class extends mock() { }, + new ExtHostWorkspace(new TestThreadService(), { + 'id': 'foo', + 'roots': [URI.file('foo')], + 'name': 'foo' + }), + { + defaults: new ConfigurationModel({ + 'editor': { + 'wordWrap': 'off' + } + }, ['editor.wordWrap']), + user: new ConfigurationModel({ + 'editor': { + 'wordWrap': 'on' + } + }, ['editor.wordWrap']), + workspace, + folders + } + ); + + let actual1 = testObject.getConfiguration().inspect('editor.wordWrap'); + assert.equal(actual1.defaultValue, 'off'); + assert.equal(actual1.globalValue, 'on'); + assert.equal(actual1.workspaceValue, 'bounded'); + assert.equal(actual1.workspaceFolderValue, undefined); + + actual1 = testObject.getConfiguration('editor').inspect('wordWrap'); + assert.equal(actual1.defaultValue, 'off'); + assert.equal(actual1.globalValue, 'on'); + assert.equal(actual1.workspaceValue, 'bounded'); + assert.equal(actual1.workspaceFolderValue, undefined); + + let actual2 = testObject.getConfiguration(null, workspaceUri).inspect('editor.wordWrap'); + assert.equal(actual2.defaultValue, 'off'); + assert.equal(actual2.globalValue, 'on'); + assert.equal(actual2.workspaceValue, 'bounded'); + assert.equal(actual2.workspaceFolderValue, 'bounded'); + + actual2 = testObject.getConfiguration('editor', workspaceUri).inspect('wordWrap'); + assert.equal(actual2.defaultValue, 'off'); + assert.equal(actual2.globalValue, 'on'); + assert.equal(actual2.workspaceValue, 'bounded'); + assert.equal(actual2.workspaceFolderValue, 'bounded'); + }); + + test('inspect in multi root context', function () { + const workspace = new ConfigurationModel({ + 'editor': { + 'wordWrap': 'bounded' + } + }, ['editor.wordWrap']); + + const firstRoot = URI.file('foo1'); + const secondRoot = URI.file('foo2'); + const thirdRoot = URI.file('foo3'); + const folders = Object.create(null); + folders[firstRoot.toString()] = new ConfigurationModel({ + 'editor': { + 'wordWrap': 'off', + 'lineNumbers': 'relative' + } + }, ['editor.wordWrap']); + folders[secondRoot.toString()] = new ConfigurationModel({ + 'editor': { + 'wordWrap': 'on' + } + }, ['editor.wordWrap']); + folders[thirdRoot.toString()] = new ConfigurationModel({}, []); + + const testObject = new ExtHostConfiguration( + new class extends mock() { }, + new ExtHostWorkspace(new TestThreadService(), { + 'id': 'foo', + 'roots': [firstRoot, secondRoot], + 'name': 'foo' + }), + { + defaults: new ConfigurationModel({ + 'editor': { + 'wordWrap': 'off', + 'lineNumbers': 'on' + } + }, ['editor.wordWrap']), + user: new ConfigurationModel({ + 'editor': { + 'wordWrap': 'on' + } + }, ['editor.wordWrap']), + workspace, + folders + } + ); + + let actual1 = testObject.getConfiguration().inspect('editor.wordWrap'); + assert.equal(actual1.defaultValue, 'off'); + assert.equal(actual1.globalValue, 'on'); + assert.equal(actual1.workspaceValue, 'bounded'); + assert.equal(actual1.workspaceFolderValue, undefined); + + actual1 = testObject.getConfiguration('editor').inspect('wordWrap'); + assert.equal(actual1.defaultValue, 'off'); + assert.equal(actual1.globalValue, 'on'); + assert.equal(actual1.workspaceValue, 'bounded'); + assert.equal(actual1.workspaceFolderValue, undefined); + + actual1 = testObject.getConfiguration('editor').inspect('lineNumbers'); + assert.equal(actual1.defaultValue, 'on'); + assert.equal(actual1.globalValue, undefined); + assert.equal(actual1.workspaceValue, undefined); + assert.equal(actual1.workspaceFolderValue, undefined); + + let actual2 = testObject.getConfiguration(null, firstRoot).inspect('editor.wordWrap'); + assert.equal(actual2.defaultValue, 'off'); + assert.equal(actual2.globalValue, 'on'); + assert.equal(actual2.workspaceValue, 'bounded'); + assert.equal(actual2.workspaceFolderValue, 'off'); + + actual2 = testObject.getConfiguration('editor', firstRoot).inspect('wordWrap'); + assert.equal(actual2.defaultValue, 'off'); + assert.equal(actual2.globalValue, 'on'); + assert.equal(actual2.workspaceValue, 'bounded'); + assert.equal(actual2.workspaceFolderValue, 'off'); + + actual2 = testObject.getConfiguration('editor', firstRoot).inspect('lineNumbers'); + assert.equal(actual2.defaultValue, 'on'); + assert.equal(actual2.globalValue, undefined); + assert.equal(actual2.workspaceValue, undefined); + assert.equal(actual2.workspaceFolderValue, 'relative'); + + actual2 = testObject.getConfiguration(null, secondRoot).inspect('editor.wordWrap'); + assert.equal(actual2.defaultValue, 'off'); + assert.equal(actual2.globalValue, 'on'); + assert.equal(actual2.workspaceValue, 'bounded'); + assert.equal(actual2.workspaceFolderValue, 'on'); + + actual2 = testObject.getConfiguration('editor', secondRoot).inspect('wordWrap'); + assert.equal(actual2.defaultValue, 'off'); + assert.equal(actual2.globalValue, 'on'); + assert.equal(actual2.workspaceValue, 'bounded'); + assert.equal(actual2.workspaceFolderValue, 'on'); + + actual2 = testObject.getConfiguration(null, thirdRoot).inspect('editor.wordWrap'); + assert.equal(actual2.defaultValue, 'off'); + assert.equal(actual2.globalValue, 'on'); + assert.equal(actual2.workspaceValue, 'bounded'); + assert.ok(Object.keys(actual2).indexOf('workspaceFolderValue') !== -1); + assert.equal(actual2.workspaceFolderValue, undefined); + + actual2 = testObject.getConfiguration('editor', thirdRoot).inspect('wordWrap'); + assert.equal(actual2.defaultValue, 'off'); + assert.equal(actual2.globalValue, 'on'); + assert.equal(actual2.workspaceValue, 'bounded'); + assert.ok(Object.keys(actual2).indexOf('workspaceFolderValue') !== -1); + assert.equal(actual2.workspaceFolderValue, undefined); + }); + + test('getConfiguration vs get', function () { + + const all = createExtHostConfiguration({ + 'farboo': { + 'config0': true, + 'config4': 38 + } + }); + + let config = all.getConfiguration('farboo.config0'); + assert.equal(config.get(''), undefined); + assert.equal(config.has(''), false); + + config = all.getConfiguration('farboo'); + assert.equal(config.get('config0'), true); + assert.equal(config.has('config0'), true); + }); + + test('getConfiguration vs get', function () { + + const all = createExtHostConfiguration({ + 'farboo': { + 'config0': true, + 'config4': 38 + } + }); + + let config = all.getConfiguration('farboo.config0'); + assert.equal(config.get(''), undefined); + assert.equal(config.has(''), false); + + config = all.getConfiguration('farboo'); + assert.equal(config.get('config0'), true); + assert.equal(config.has('config0'), true); + }); + + test('name vs property', function () { + const all = createExtHostConfiguration({ + 'farboo': { + 'get': 'get-prop' + } + }); + const config = all.getConfiguration('farboo'); + + assert.ok(config.has('get')); + assert.equal(config.get('get'), 'get-prop'); + assert.deepEqual(config['get'], config.get); + assert.throws(() => config['get'] = 'get-prop'); + }); + + test('update: no target passes null', function () { + const shape = new RecordingShape(); + const allConfig = createExtHostConfiguration({ + 'foo': { + 'bar': 1, + 'far': 1 + } + }, shape); + + let config = allConfig.getConfiguration('foo'); + config.update('bar', 42); + + assert.equal(shape.lastArgs[0], null); + }); + + test('update/section to key', function () { + + const shape = new RecordingShape(); + const allConfig = createExtHostConfiguration({ + 'foo': { + 'bar': 1, + 'far': 1 + } + }, shape); + + let config = allConfig.getConfiguration('foo'); + config.update('bar', 42, true); + + assert.equal(shape.lastArgs[0], ConfigurationTarget.USER); + assert.equal(shape.lastArgs[1], 'foo.bar'); + assert.equal(shape.lastArgs[2], 42); + + config = allConfig.getConfiguration(''); + config.update('bar', 42, true); + assert.equal(shape.lastArgs[1], 'bar'); + + config.update('foo.bar', 42, true); + assert.equal(shape.lastArgs[1], 'foo.bar'); + }); + + test('update, what is #15834', function () { + const shape = new RecordingShape(); + const allConfig = createExtHostConfiguration({ + 'editor': { + 'formatOnSave': true + } + }, shape); + + allConfig.getConfiguration('editor').update('formatOnSave', { extensions: ['ts'] }); + assert.equal(shape.lastArgs[1], 'editor.formatOnSave'); + assert.deepEqual(shape.lastArgs[2], { extensions: ['ts'] }); + }); + + test('update/error-state not OK', function () { + + const shape = new class extends mock() { + $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise { + return TPromise.wrapError(new ConfigurationEditingError('Unknown Key', ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY)); // something !== OK + } + }; + + return createExtHostConfiguration({}, shape) + .getConfiguration('') + .update('', true, false) + .then(() => assert.ok(false), err => { /* expecting rejection */ }); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts new file mode 100644 index 0000000000..8897bb3b5f --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts @@ -0,0 +1,263 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import Severity from 'vs/base/common/severity'; +import { DiagnosticCollection } from 'vs/workbench/api/node/extHostDiagnostics'; +import { Diagnostic, DiagnosticSeverity, Range } from 'vs/workbench/api/node/extHostTypes'; +import { MainThreadDiagnosticsShape } from 'vs/workbench/api/node/extHost.protocol'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IMarkerData } from 'vs/platform/markers/common/markers'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; + +suite('ExtHostDiagnostics', () => { + + class DiagnosticsShape extends mock() { + $changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise { + return TPromise.as(null); + } + $clear(owner: string): TPromise { + return TPromise.as(null); + } + }; + + test('disposeCheck', function () { + + const collection = new DiagnosticCollection('test', new DiagnosticsShape()); + + collection.dispose(); + collection.dispose(); // that's OK + assert.throws(() => collection.name); + assert.throws(() => collection.clear()); + assert.throws(() => collection.delete(URI.parse('aa:bb'))); + assert.throws(() => collection.forEach(() => { ; })); + assert.throws(() => collection.get(URI.parse('aa:bb'))); + assert.throws(() => collection.has(URI.parse('aa:bb'))); + assert.throws(() => collection.set(URI.parse('aa:bb'), [])); + assert.throws(() => collection.set(URI.parse('aa:bb'), undefined)); + }); + + + test('diagnostic collection, forEach, clear, has', function () { + let collection = new DiagnosticCollection('test', new DiagnosticsShape()); + assert.equal(collection.name, 'test'); + collection.dispose(); + assert.throws(() => collection.name); + + let c = 0; + collection = new DiagnosticCollection('test', new DiagnosticsShape()); + collection.forEach(() => c++); + assert.equal(c, 0); + + collection.set(URI.parse('foo:bar'), [ + new Diagnostic(new Range(0, 0, 1, 1), 'message-1'), + new Diagnostic(new Range(0, 0, 1, 1), 'message-2') + ]); + collection.forEach(() => c++); + assert.equal(c, 1); + + c = 0; + collection.clear(); + collection.forEach(() => c++); + assert.equal(c, 0); + + collection.set(URI.parse('foo:bar1'), [ + new Diagnostic(new Range(0, 0, 1, 1), 'message-1'), + new Diagnostic(new Range(0, 0, 1, 1), 'message-2') + ]); + collection.set(URI.parse('foo:bar2'), [ + new Diagnostic(new Range(0, 0, 1, 1), 'message-1'), + new Diagnostic(new Range(0, 0, 1, 1), 'message-2') + ]); + collection.forEach(() => c++); + assert.equal(c, 2); + + assert.ok(collection.has(URI.parse('foo:bar1'))); + assert.ok(collection.has(URI.parse('foo:bar2'))); + assert.ok(!collection.has(URI.parse('foo:bar3'))); + collection.delete(URI.parse('foo:bar1')); + assert.ok(!collection.has(URI.parse('foo:bar1'))); + + collection.dispose(); + }); + + test('diagnostic collection, immutable read', function () { + let collection = new DiagnosticCollection('test', new DiagnosticsShape()); + collection.set(URI.parse('foo:bar'), [ + new Diagnostic(new Range(0, 0, 1, 1), 'message-1'), + new Diagnostic(new Range(0, 0, 1, 1), 'message-2') + ]); + + let array = collection.get(URI.parse('foo:bar')); + assert.throws(() => array.length = 0); + assert.throws(() => array.pop()); + assert.throws(() => array[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil')); + + collection.forEach((uri, array) => { + assert.throws(() => array.length = 0); + assert.throws(() => array.pop()); + assert.throws(() => array[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil')); + }); + + array = collection.get(URI.parse('foo:bar')); + assert.equal(array.length, 2); + + collection.dispose(); + }); + + + test('diagnostics collection, set with dupliclated tuples', function () { + let collection = new DiagnosticCollection('test', new DiagnosticsShape()); + let uri = URI.parse('sc:hightower'); + collection.set([ + [uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-1')]], + [URI.parse('some:thing'), [new Diagnostic(new Range(0, 0, 1, 1), 'something')]], + [uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-2')]], + ]); + + let array = collection.get(uri); + assert.equal(array.length, 2); + let [first, second] = array; + assert.equal(first.message, 'message-1'); + assert.equal(second.message, 'message-2'); + + // clear + collection.delete(uri); + assert.ok(!collection.has(uri)); + + // bad tuple clears 1/2 + collection.set([ + [uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-1')]], + [URI.parse('some:thing'), [new Diagnostic(new Range(0, 0, 1, 1), 'something')]], + [uri, undefined] + ]); + assert.ok(!collection.has(uri)); + + // clear + collection.delete(uri); + assert.ok(!collection.has(uri)); + + // bad tuple clears 2/2 + collection.set([ + [uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-1')]], + [URI.parse('some:thing'), [new Diagnostic(new Range(0, 0, 1, 1), 'something')]], + [uri, undefined], + [uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-2')]], + [uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-3')]], + ]); + + array = collection.get(uri); + assert.equal(array.length, 2); + [first, second] = array; + assert.equal(first.message, 'message-2'); + assert.equal(second.message, 'message-3'); + + collection.dispose(); + }); + + test('diagnostics collection, set tuple overrides, #11547', function () { + + let lastEntries: [URI, IMarkerData[]][]; + let collection = new DiagnosticCollection('test', new class extends DiagnosticsShape { + $changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise { + lastEntries = entries; + return super.$changeMany(owner, entries); + } + }); + let uri = URI.parse('sc:hightower'); + + collection.set([[uri, [new Diagnostic(new Range(0, 0, 1, 1), 'error')]]]); + assert.equal(collection.get(uri).length, 1); + assert.equal(collection.get(uri)[0].message, 'error'); + assert.equal(lastEntries.length, 1); + let [[, data1]] = lastEntries; + assert.equal(data1.length, 1); + assert.equal(data1[0].message, 'error'); + lastEntries = undefined; + + collection.set([[uri, [new Diagnostic(new Range(0, 0, 1, 1), 'warning')]]]); + assert.equal(collection.get(uri).length, 1); + assert.equal(collection.get(uri)[0].message, 'warning'); + assert.equal(lastEntries.length, 1); + let [[, data2]] = lastEntries; + assert.equal(data2.length, 1); + assert.equal(data2[0].message, 'warning'); + lastEntries = undefined; + }); + + test('diagnostics collection, tuples and undefined (small array), #15585', function () { + + const collection = new DiagnosticCollection('test', new DiagnosticsShape()); + let uri = URI.parse('sc:hightower'); + let uri2 = URI.parse('sc:nomad'); + let diag = new Diagnostic(new Range(0, 0, 0, 1), 'ffff'); + + collection.set([ + [uri, [diag, diag, diag]], + [uri, undefined], + [uri, [diag]], + + [uri2, [diag, diag]], + [uri2, undefined], + [uri2, [diag]], + ]); + + assert.equal(collection.get(uri).length, 1); + assert.equal(collection.get(uri2).length, 1); + }); + + test('diagnostics collection, tuples and undefined (large array), #15585', function () { + + const collection = new DiagnosticCollection('test', new DiagnosticsShape()); + const tuples: [URI, Diagnostic[]][] = []; + + for (let i = 0; i < 500; i++) { + let uri = URI.parse('sc:hightower#' + i); + let diag = new Diagnostic(new Range(0, 0, 0, 1), i.toString()); + + tuples.push([uri, [diag, diag, diag]]); + tuples.push([uri, undefined]); + tuples.push([uri, [diag]]); + } + + collection.set(tuples); + + for (let i = 0; i < 500; i++) { + let uri = URI.parse('sc:hightower#' + i); + assert.equal(collection.has(uri), true); + assert.equal(collection.get(uri).length, 1); + } + }); + + test('diagnostic capping', function () { + + let lastEntries: [URI, IMarkerData[]][]; + let collection = new DiagnosticCollection('test', new class extends DiagnosticsShape { + $changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise { + lastEntries = entries; + return super.$changeMany(owner, entries); + } + }); + let uri = URI.parse('aa:bb'); + + let diagnostics: Diagnostic[] = []; + for (let i = 0; i < 500; i++) { + diagnostics.push(new Diagnostic(new Range(i, 0, i + 1, 0), `error#${i}`, i < 300 + ? DiagnosticSeverity.Warning + : DiagnosticSeverity.Error)); + } + + collection.set(uri, diagnostics); + assert.equal(collection.get(uri).length, 500); + assert.equal(lastEntries.length, 1); + assert.equal(lastEntries[0][1].length, 251); + assert.equal(lastEntries[0][1][0].severity, Severity.Error); + assert.equal(lastEntries[0][1][200].severity, Severity.Warning); + assert.equal(lastEntries[0][1][250].severity, Severity.Error); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentData.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentData.test.ts new file mode 100644 index 0000000000..35af880309 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentData.test.ts @@ -0,0 +1,469 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData'; +import { Position } from 'vs/workbench/api/node/extHostTypes'; +import { Range } from 'vs/editor/common/core/range'; +import { MainThreadDocumentsShape } from 'vs/workbench/api/node/extHost.protocol'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModelChangedEvent } from 'vs/editor/common/model/mirrorModel'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; + + +suite('ExtHostDocumentData', () => { + + let data: ExtHostDocumentData; + + function assertPositionAt(offset: number, line: number, character: number) { + let position = data.document.positionAt(offset); + assert.equal(position.line, line); + assert.equal(position.character, character); + } + + function assertOffsetAt(line: number, character: number, offset: number) { + let pos = new Position(line, character); + let actual = data.document.offsetAt(pos); + assert.equal(actual, offset); + } + + setup(function () { + data = new ExtHostDocumentData(undefined, URI.file(''), [ + 'This is line one', //16 + 'and this is line number two', //27 + 'it is followed by #3', //20 + 'and finished with the fourth.', //29 + ], '\n', 'text', 1, false); + }); + + test('readonly-ness', function () { + assert.throws(() => (data).document.uri = null); + assert.throws(() => (data).document.fileName = 'foofile'); + assert.throws(() => (data).document.isDirty = false); + assert.throws(() => (data).document.isUntitled = false); + assert.throws(() => (data).document.languageId = 'dddd'); + assert.throws(() => (data).document.lineCount = 9); + }); + + test('save, when disposed', function () { + let saved: URI; + let data = new ExtHostDocumentData(new class extends mock() { + $trySaveDocument(uri) { + assert.ok(!saved); + saved = uri; + return TPromise.as(true); + } + }, URI.parse('foo:bar'), [], '\n', 'text', 1, true); + + return data.document.save().then(() => { + assert.equal(saved.toString(), 'foo:bar'); + + data.dispose(); + + return data.document.save().then(() => { + assert.ok(false, 'expected failure'); + }, err => { + assert.ok(err); + }); + }); + }); + + test('read, when disposed', function () { + data.dispose(); + + const { document } = data; + assert.equal(document.lineCount, 4); + assert.equal(document.lineAt(0).text, 'This is line one'); + }); + + test('lines', function () { + + assert.equal(data.document.lineCount, 4); + + assert.throws(() => data.document.lineAt(-1)); + assert.throws(() => data.document.lineAt(data.document.lineCount)); + assert.throws(() => data.document.lineAt(Number.MAX_VALUE)); + assert.throws(() => data.document.lineAt(Number.MIN_VALUE)); + assert.throws(() => data.document.lineAt(0.8)); + + let line = data.document.lineAt(0); + assert.equal(line.lineNumber, 0); + assert.equal(line.text.length, 16); + assert.equal(line.text, 'This is line one'); + assert.equal(line.isEmptyOrWhitespace, false); + assert.equal(line.firstNonWhitespaceCharacterIndex, 0); + + data.onEvents({ + changes: [{ + range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }, + rangeLength: undefined, + text: '\t ' + }], + eol: undefined, + versionId: undefined, + }); + + // line didn't change + assert.equal(line.text, 'This is line one'); + assert.equal(line.firstNonWhitespaceCharacterIndex, 0); + + // fetch line again + line = data.document.lineAt(0); + assert.equal(line.text, '\t This is line one'); + assert.equal(line.firstNonWhitespaceCharacterIndex, 2); + }); + + test('line, issue #5704', function () { + + let line = data.document.lineAt(0); + let { range, rangeIncludingLineBreak } = line; + assert.equal(range.end.line, 0); + assert.equal(range.end.character, 16); + assert.equal(rangeIncludingLineBreak.end.line, 1); + assert.equal(rangeIncludingLineBreak.end.character, 0); + + line = data.document.lineAt(data.document.lineCount - 1); + range = line.range; + rangeIncludingLineBreak = line.rangeIncludingLineBreak; + assert.equal(range.end.line, 3); + assert.equal(range.end.character, 29); + assert.equal(rangeIncludingLineBreak.end.line, 3); + assert.equal(rangeIncludingLineBreak.end.character, 29); + + }); + + test('offsetAt', function () { + assertOffsetAt(0, 0, 0); + assertOffsetAt(0, 1, 1); + assertOffsetAt(0, 16, 16); + assertOffsetAt(1, 0, 17); + assertOffsetAt(1, 3, 20); + assertOffsetAt(2, 0, 45); + assertOffsetAt(4, 29, 95); + assertOffsetAt(4, 30, 95); + assertOffsetAt(4, Number.MAX_VALUE, 95); + assertOffsetAt(5, 29, 95); + assertOffsetAt(Number.MAX_VALUE, 29, 95); + assertOffsetAt(Number.MAX_VALUE, Number.MAX_VALUE, 95); + }); + + test('offsetAt, after remove', function () { + + data.onEvents({ + changes: [{ + range: { startLineNumber: 1, startColumn: 3, endLineNumber: 1, endColumn: 6 }, + rangeLength: undefined, + text: '' + }], + eol: undefined, + versionId: undefined, + }); + + assertOffsetAt(0, 1, 1); + assertOffsetAt(0, 13, 13); + assertOffsetAt(1, 0, 14); + }); + + test('offsetAt, after replace', function () { + + data.onEvents({ + changes: [{ + range: { startLineNumber: 1, startColumn: 3, endLineNumber: 1, endColumn: 6 }, + rangeLength: undefined, + text: 'is could be' + }], + eol: undefined, + versionId: undefined, + }); + + assertOffsetAt(0, 1, 1); + assertOffsetAt(0, 24, 24); + assertOffsetAt(1, 0, 25); + }); + + test('offsetAt, after insert line', function () { + + data.onEvents({ + changes: [{ + range: { startLineNumber: 1, startColumn: 3, endLineNumber: 1, endColumn: 6 }, + rangeLength: undefined, + text: 'is could be\na line with number' + }], + eol: undefined, + versionId: undefined, + }); + + assertOffsetAt(0, 1, 1); + assertOffsetAt(0, 13, 13); + assertOffsetAt(1, 0, 14); + assertOffsetAt(1, 18, 13 + 1 + 18); + assertOffsetAt(1, 29, 13 + 1 + 29); + assertOffsetAt(2, 0, 13 + 1 + 29 + 1); + }); + + test('offsetAt, after remove line', function () { + + data.onEvents({ + changes: [{ + range: { startLineNumber: 1, startColumn: 3, endLineNumber: 2, endColumn: 6 }, + rangeLength: undefined, + text: '' + }], + eol: undefined, + versionId: undefined, + }); + + assertOffsetAt(0, 1, 1); + assertOffsetAt(0, 2, 2); + assertOffsetAt(1, 0, 25); + }); + + test('positionAt', function () { + assertPositionAt(0, 0, 0); + assertPositionAt(Number.MIN_VALUE, 0, 0); + assertPositionAt(1, 0, 1); + assertPositionAt(16, 0, 16); + assertPositionAt(17, 1, 0); + assertPositionAt(20, 1, 3); + assertPositionAt(45, 2, 0); + assertPositionAt(95, 3, 29); + assertPositionAt(96, 3, 29); + assertPositionAt(99, 3, 29); + assertPositionAt(Number.MAX_VALUE, 3, 29); + }); + + test('getWordRangeAtPosition', function () { + data = new ExtHostDocumentData(undefined, URI.file(''), [ + 'aaaa bbbb+cccc abc' + ], '\n', 'text', 1, false); + + let range = data.document.getWordRangeAtPosition(new Position(0, 2)); + assert.equal(range.start.line, 0); + assert.equal(range.start.character, 0); + assert.equal(range.end.line, 0); + assert.equal(range.end.character, 4); + + // ignore bad regular expresson /.*/ + range = data.document.getWordRangeAtPosition(new Position(0, 2), /.*/); + assert.equal(range.start.line, 0); + assert.equal(range.start.character, 0); + assert.equal(range.end.line, 0); + assert.equal(range.end.character, 4); + + range = data.document.getWordRangeAtPosition(new Position(0, 5), /[a-z+]+/); + assert.equal(range.start.line, 0); + assert.equal(range.start.character, 5); + assert.equal(range.end.line, 0); + assert.equal(range.end.character, 14); + + range = data.document.getWordRangeAtPosition(new Position(0, 17), /[a-z+]+/); + assert.equal(range.start.line, 0); + assert.equal(range.start.character, 15); + assert.equal(range.end.line, 0); + assert.equal(range.end.character, 18); + + range = data.document.getWordRangeAtPosition(new Position(0, 11), /yy/); + assert.equal(range, undefined); + }); + + test('getWordRangeAtPosition doesn\'t quite use the regex as expected, #29102', function () { + data = new ExtHostDocumentData(undefined, URI.file(''), [ + 'some text here', + '/** foo bar */', + 'function() {', + ' "far boo"', + '}' + ], '\n', 'text', 1, false); + + let range = data.document.getWordRangeAtPosition(new Position(0, 0), /\/\*.+\*\//); + assert.equal(range, undefined); + + range = data.document.getWordRangeAtPosition(new Position(1, 0), /\/\*.+\*\//); + assert.equal(range.start.line, 1); + assert.equal(range.start.character, 0); + assert.equal(range.end.line, 1); + assert.equal(range.end.character, 14); + + range = data.document.getWordRangeAtPosition(new Position(3, 0), /("|').*\1/); + assert.equal(range, undefined); + + range = data.document.getWordRangeAtPosition(new Position(3, 1), /("|').*\1/); + assert.equal(range.start.line, 3); + assert.equal(range.start.character, 1); + assert.equal(range.end.line, 3); + assert.equal(range.end.character, 10); + }); +}); + +enum AssertDocumentLineMappingDirection { + OffsetToPosition, + PositionToOffset +} + +suite('ExtHostDocumentData updates line mapping', () => { + + function positionToStr(position: { line: number; character: number; }): string { + return '(' + position.line + ',' + position.character + ')'; + } + + function assertDocumentLineMapping(doc: ExtHostDocumentData, direction: AssertDocumentLineMappingDirection): void { + let allText = doc.getText(); + + let line = 0, character = 0, previousIsCarriageReturn = false; + for (let offset = 0; offset <= allText.length; offset++) { + // The position coordinate system cannot express the position between \r and \n + let position = new Position(line, character + (previousIsCarriageReturn ? -1 : 0)); + + if (direction === AssertDocumentLineMappingDirection.OffsetToPosition) { + let actualPosition = doc.document.positionAt(offset); + assert.equal(positionToStr(actualPosition), positionToStr(position), 'positionAt mismatch for offset ' + offset); + } else { + // The position coordinate system cannot express the position between \r and \n + let expectedOffset = offset + (previousIsCarriageReturn ? -1 : 0); + let actualOffset = doc.document.offsetAt(position); + assert.equal(actualOffset, expectedOffset, 'offsetAt mismatch for position ' + positionToStr(position)); + } + + if (allText.charAt(offset) === '\n') { + line++; + character = 0; + } else { + character++; + } + + previousIsCarriageReturn = (allText.charAt(offset) === '\r'); + } + } + + function createChangeEvent(range: Range, text: string, eol?: string): IModelChangedEvent { + return { + changes: [{ + range: range, + rangeLength: undefined, + text: text + }], + eol: eol, + versionId: undefined, + }; + } + + function testLineMappingDirectionAfterEvents(lines: string[], eol: string, direction: AssertDocumentLineMappingDirection, e: IModelChangedEvent): void { + let myDocument = new ExtHostDocumentData(undefined, URI.file(''), lines.slice(0), eol, 'text', 1, false); + assertDocumentLineMapping(myDocument, direction); + + myDocument.onEvents(e); + assertDocumentLineMapping(myDocument, direction); + } + + function testLineMappingAfterEvents(lines: string[], e: IModelChangedEvent): void { + testLineMappingDirectionAfterEvents(lines, '\n', AssertDocumentLineMappingDirection.PositionToOffset, e); + testLineMappingDirectionAfterEvents(lines, '\n', AssertDocumentLineMappingDirection.OffsetToPosition, e); + + testLineMappingDirectionAfterEvents(lines, '\r\n', AssertDocumentLineMappingDirection.PositionToOffset, e); + testLineMappingDirectionAfterEvents(lines, '\r\n', AssertDocumentLineMappingDirection.OffsetToPosition, e); + } + + test('line mapping', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], { changes: [], eol: undefined, versionId: 7 }); + }); + + test('after remove', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], createChangeEvent(new Range(1, 3, 1, 6), '')); + }); + + test('after replace', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], createChangeEvent(new Range(1, 3, 1, 6), 'is could be')); + }); + + test('after insert line', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], createChangeEvent(new Range(1, 3, 1, 6), 'is could be\na line with number')); + }); + + test('after insert two lines', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], createChangeEvent(new Range(1, 3, 1, 6), 'is could be\na line with number\nyet another line')); + }); + + test('after remove line', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], createChangeEvent(new Range(1, 3, 2, 6), '')); + }); + + test('after remove two lines', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], createChangeEvent(new Range(1, 3, 3, 6), '')); + }); + + test('after deleting entire content', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], createChangeEvent(new Range(1, 3, 4, 30), '')); + }); + + test('after replacing entire content', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], createChangeEvent(new Range(1, 3, 4, 30), 'some new text\nthat\nspans multiple lines')); + }); + + test('after changing EOL to CRLF', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], createChangeEvent(new Range(1, 1, 1, 1), '', '\r\n')); + }); + + test('after changing EOL to LF', () => { + testLineMappingAfterEvents([ + 'This is line one', + 'and this is line number two', + 'it is followed by #3', + 'and finished with the fourth.', + ], createChangeEvent(new Range(1, 1, 1, 1), '', '\n')); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts new file mode 100644 index 0000000000..9f34a78ca5 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts @@ -0,0 +1,359 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; +import { TextDocumentSaveReason, TextEdit, Position, EndOfLine } from 'vs/workbench/api/node/extHostTypes'; +import { MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/node/extHostDocumentSaveParticipant'; +import { OneGetThreadService } from './testThreadService'; +import { IResourceEdit } from 'vs/editor/common/services/bulkEdit'; +import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import * as vscode from 'vscode'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; + +suite('ExtHostDocumentSaveParticipant', () => { + + let resource = URI.parse('foo:bar'); + let workspace = new class extends mock() { }; + let documents: ExtHostDocuments; + + setup(() => { + const documentsAndEditors = new ExtHostDocumentsAndEditors(OneGetThreadService(null)); + documentsAndEditors.$acceptDocumentsAndEditorsDelta({ + addedDocuments: [{ + isDirty: false, + modeId: 'foo', + url: resource, + versionId: 1, + lines: ['foo'], + EOL: '\n', + }] + }); + documents = new ExtHostDocuments(OneGetThreadService(null), documentsAndEditors); + }); + + test('no listeners, no problem', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => assert.ok(true)); + }); + + test('event delivery', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); + + let event: vscode.TextDocumentWillSaveEvent; + let sub = participant.onWillSaveTextDocumentEvent(function (e) { + event = e; + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => { + sub.dispose(); + + assert.ok(event); + assert.equal(event.reason, TextDocumentSaveReason.Manual); + assert.equal(typeof event.waitUntil, 'function'); + }); + }); + + test('event delivery, immutable', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); + + let event: vscode.TextDocumentWillSaveEvent; + let sub = participant.onWillSaveTextDocumentEvent(function (e) { + event = e; + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => { + sub.dispose(); + + assert.ok(event); + assert.throws(() => event.document = null); + }); + }); + + test('event delivery, bad listener', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); + + let sub = participant.onWillSaveTextDocumentEvent(function (e) { + throw new Error('💀'); + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(values => { + sub.dispose(); + + const [first] = values; + assert.equal(first, false); + }); + }); + + test('event delivery, bad listener doesn\'t prevent more events', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); + + let sub1 = participant.onWillSaveTextDocumentEvent(function (e) { + throw new Error('💀'); + }); + let event: vscode.TextDocumentWillSaveEvent; + let sub2 = participant.onWillSaveTextDocumentEvent(function (e) { + event = e; + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => { + sub1.dispose(); + sub2.dispose(); + + assert.ok(event); + }); + }); + + test('event delivery, in subscriber order', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); + + let counter = 0; + let sub1 = participant.onWillSaveTextDocumentEvent(function (event) { + assert.equal(counter++, 0); + }); + + let sub2 = participant.onWillSaveTextDocumentEvent(function (event) { + assert.equal(counter++, 1); + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => { + sub1.dispose(); + sub2.dispose(); + }); + }); + + test('event delivery, ignore bad listeners', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace, { timeout: 5, errors: 1 }); + + let callCount = 0; + let sub = participant.onWillSaveTextDocumentEvent(function (event) { + callCount += 1; + throw new Error('boom'); + }); + + return TPromise.join([ + participant.$participateInSave(resource, SaveReason.EXPLICIT), + participant.$participateInSave(resource, SaveReason.EXPLICIT), + participant.$participateInSave(resource, SaveReason.EXPLICIT), + participant.$participateInSave(resource, SaveReason.EXPLICIT) + + ]).then(values => { + sub.dispose(); + assert.equal(callCount, 2); + }); + }); + + test('event delivery, overall timeout', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace, { timeout: 20, errors: 5 }); + + let callCount = 0; + let sub1 = participant.onWillSaveTextDocumentEvent(function (event) { + callCount += 1; + event.waitUntil(TPromise.timeout(17)); + }); + + let sub2 = participant.onWillSaveTextDocumentEvent(function (event) { + callCount += 1; + event.waitUntil(TPromise.timeout(17)); + }); + + let sub3 = participant.onWillSaveTextDocumentEvent(function (event) { + callCount += 1; + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(values => { + sub1.dispose(); + sub2.dispose(); + sub3.dispose(); + + assert.equal(callCount, 2); + assert.equal(values.length, 2); + }); + }); + + test('event delivery, waitUntil', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); + + let sub = participant.onWillSaveTextDocumentEvent(function (event) { + + event.waitUntil(TPromise.timeout(10)); + event.waitUntil(TPromise.timeout(10)); + event.waitUntil(TPromise.timeout(10)); + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => { + sub.dispose(); + }); + + }); + + test('event delivery, waitUntil must be called sync', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); + + let sub = participant.onWillSaveTextDocumentEvent(function (event) { + + event.waitUntil(new TPromise((resolve, reject) => { + setTimeout(() => { + try { + assert.throws(() => event.waitUntil(TPromise.timeout(10))); + resolve(void 0); + } catch (e) { + reject(e); + } + + }, 10); + })); + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => { + sub.dispose(); + }); + }); + + test('event delivery, waitUntil will timeout', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace, { timeout: 5, errors: 3 }); + + let sub = participant.onWillSaveTextDocumentEvent(function (event) { + event.waitUntil(TPromise.timeout(15)); + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(values => { + sub.dispose(); + + const [first] = values; + assert.equal(first, false); + }); + }); + + test('event delivery, waitUntil failure handling', () => { + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); + + let sub1 = participant.onWillSaveTextDocumentEvent(function (e) { + e.waitUntil(TPromise.wrapError(new Error('dddd'))); + }); + + let event: vscode.TextDocumentWillSaveEvent; + let sub2 = participant.onWillSaveTextDocumentEvent(function (e) { + event = e; + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => { + assert.ok(event); + sub1.dispose(); + sub2.dispose(); + }); + }); + + test('event delivery, pushEdits sync', () => { + + let edits: IResourceEdit[]; + const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock() { + $applyWorkspaceEdit(_edits) { + edits = _edits; + return TPromise.as(true); + } + }); + + let sub = participant.onWillSaveTextDocumentEvent(function (e) { + e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')])); + e.waitUntil(TPromise.as([TextEdit.setEndOfLine(EndOfLine.CRLF)])); + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => { + sub.dispose(); + + assert.equal(edits.length, 2); + }); + }); + + test('event delivery, concurrent change', () => { + + let edits: IResourceEdit[]; + const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock() { + $applyWorkspaceEdit(_edits) { + edits = _edits; + return TPromise.as(true); + } + }); + + let sub = participant.onWillSaveTextDocumentEvent(function (e) { + + // concurrent change from somewhere + documents.$acceptModelChanged(resource.toString(), { + changes: [{ + range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }, + rangeLength: undefined, + text: 'bar' + }], + eol: undefined, + versionId: 2 + }, true); + + e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')])); + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(values => { + sub.dispose(); + + assert.equal(edits, undefined); + assert.equal(values[0], false); + }); + + }); + + test('event delivery, two listeners -> two document states', () => { + + const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock() { + $applyWorkspaceEdit(_edits: IResourceEdit[]) { + + for (const { resource, newText, range } of _edits) { + documents.$acceptModelChanged(resource.toString(), { + changes: [{ + range, + rangeLength: undefined, + text: newText + }], + eol: undefined, + versionId: documents.getDocumentData(resource).version + 1 + }, true); + } + return TPromise.as(true); + } + }); + + const document = documents.getDocumentData(resource).document; + + let sub1 = participant.onWillSaveTextDocumentEvent(function (e) { + // the document state we started with + assert.equal(document.version, 1); + assert.equal(document.getText(), 'foo'); + + e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')])); + }); + + let sub2 = participant.onWillSaveTextDocumentEvent(function (e) { + // the document state AFTER the first listener kicked in + assert.equal(document.version, 2); + assert.equal(document.getText(), 'barfoo'); + + e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')])); + }); + + return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(values => { + sub1.dispose(); + sub2.dispose(); + + // the document state AFTER eventing is done + assert.equal(document.version, 3); + assert.equal(document.getText(), 'barbarfoo'); + }); + + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentsAndEditors.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentsAndEditors.test.ts new file mode 100644 index 0000000000..1328e979ed --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentsAndEditors.test.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; +import { TPromise } from 'vs/base/common/winjs.base'; + + +suite('ExtHostDocumentsAndEditors', () => { + + let editors: ExtHostDocumentsAndEditors; + + setup(function () { + editors = new ExtHostDocumentsAndEditors({ + get() { return undefined; } + }); + }); + + test('The value of TextDocument.isClosed is incorrect when a text document is closed, #27949', () => { + + editors.$acceptDocumentsAndEditorsDelta({ + addedDocuments: [{ + EOL: '\n', + isDirty: true, + modeId: 'fooLang', + url: URI.parse('foo:bar'), + versionId: 1, + lines: [ + 'first', + 'second' + ] + }] + }); + + return new TPromise((resolve, reject) => { + + editors.onDidRemoveDocuments(e => { + try { + + for (const data of e) { + assert.equal(data.document.isClosed, true); + } + resolve(undefined); + } catch (e) { + reject(e); + } + }); + + editors.$acceptDocumentsAndEditorsDelta({ + removedDocuments: ['foo:bar'] + }); + + }); + }); + +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostFileSystemEventService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostFileSystemEventService.test.ts new file mode 100644 index 0000000000..38dfe9ed99 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostFileSystemEventService.test.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { ExtHostFileSystemEventService } from 'vs/workbench/api/node/extHostFileSystemEventService'; + +suite('ExtHostFileSystemEventService', () => { + + + test('FileSystemWatcher ignore events properties are reversed #26851', function () { + + const watcher1 = new ExtHostFileSystemEventService().createFileSystemWatcher('**/somethingInteresting', false, false, false); + assert.equal(watcher1.ignoreChangeEvents, false); + assert.equal(watcher1.ignoreCreateEvents, false); + assert.equal(watcher1.ignoreDeleteEvents, false); + + const watcher2 = new ExtHostFileSystemEventService().createFileSystemWatcher('**/somethingBoring', true, true, true); + assert.equal(watcher2.ignoreChangeEvents, true); + assert.equal(watcher2.ignoreCreateEvents, true); + assert.equal(watcher2.ignoreDeleteEvents, true); + }); + +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts new file mode 100644 index 0000000000..82366df173 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -0,0 +1,1118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { setUnexpectedErrorHandler, errorHandler } from 'vs/base/common/errors'; +import URI from 'vs/base/common/uri'; +import * as types from 'vs/workbench/api/node/extHostTypes'; +import * as EditorCommon from 'vs/editor/common/editorCommon'; +import { Model as EditorModel } from 'vs/editor/common/model/model'; +import { Position as EditorPosition } from 'vs/editor/common/core/position'; +import { Range as EditorRange } from 'vs/editor/common/core/range'; +import { TestThreadService } from './testThreadService'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; +import { MainThreadLanguageFeatures } from 'vs/workbench/api/electron-browser/mainThreadLanguageFeatures'; +import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; +import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands'; +import { IHeapService } from 'vs/workbench/api/electron-browser/mainThreadHeapService'; +import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; +import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/common/quickOpen'; +import { DocumentSymbolProviderRegistry, DocumentHighlightKind, Hover } from 'vs/editor/common/modes'; +import { getCodeLensData } from 'vs/editor/contrib/codelens/browser/codelens'; +import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from 'vs/editor/contrib/goToDeclaration/browser/goToDeclaration'; +import { getHover } from 'vs/editor/contrib/hover/common/hover'; +import { getOccurrencesAtPosition } from 'vs/editor/contrib/wordHighlighter/common/wordHighlighter'; +import { provideReferences } from 'vs/editor/contrib/referenceSearch/browser/referenceSearch'; +import { getCodeActions } from 'vs/editor/contrib/quickFix/browser/quickFix'; +import { getWorkspaceSymbols } from 'vs/workbench/parts/search/common/search'; +import { rename } from 'vs/editor/contrib/rename/browser/rename'; +import { provideSignatureHelp } from 'vs/editor/contrib/parameterHints/common/parameterHints'; +import { provideSuggestionItems } from 'vs/editor/contrib/suggest/browser/suggest'; +import { getDocumentFormattingEdits, getDocumentRangeFormattingEdits, getOnTypeFormattingEdits } from 'vs/editor/contrib/format/common/format'; +import { getLinks } from 'vs/editor/contrib/links/common/links'; +import { asWinJsPromise } from 'vs/base/common/async'; +import { MainContext, ExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; +import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; +import * as vscode from 'vscode'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +const defaultSelector = { scheme: 'far' }; +const model: EditorCommon.IModel = EditorModel.createFromString( + [ + 'This is the first line', + 'This is the second line', + 'This is the third line', + ].join('\n'), + undefined, + undefined, + URI.parse('far://testing/file.a')); + +let extHost: ExtHostLanguageFeatures; +let mainThread: MainThreadLanguageFeatures; +let disposables: vscode.Disposable[] = []; +let threadService: TestThreadService; +let originalErrorHandler: (e: any) => any; + +suite('ExtHostLanguageFeatures', function () { + + suiteSetup(() => { + + threadService = new TestThreadService(); + + // Use IInstantiationService to get typechecking when instantiating + let inst: IInstantiationService; + { + let instantiationService = new TestInstantiationService(); + instantiationService.stub(IMarkerService, MarkerService); + instantiationService.stub(IHeapService, { + _serviceBrand: undefined, + trackRecursive(args) { + // nothing + return args; + } + }); + inst = instantiationService; + } + + originalErrorHandler = errorHandler.getUnexpectedErrorHandler(); + setUnexpectedErrorHandler(() => { }); + + const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(threadService); + extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ + addedDocuments: [{ + isDirty: false, + versionId: model.getVersionId(), + modeId: model.getLanguageIdentifier().language, + url: model.uri, + lines: model.getValue().split(model.getEOL()), + EOL: model.getEOL(), + }] + }); + const extHostDocuments = new ExtHostDocuments(threadService, extHostDocumentsAndEditors); + threadService.set(ExtHostContext.ExtHostDocuments, extHostDocuments); + + const heapService = new ExtHostHeapService(); + + const commands = new ExtHostCommands(threadService, heapService); + threadService.set(ExtHostContext.ExtHostCommands, commands); + threadService.setTestInstance(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, threadService)); + + const diagnostics = new ExtHostDiagnostics(threadService); + threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics); + + extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, heapService, diagnostics); + threadService.set(ExtHostContext.ExtHostLanguageFeatures, extHost); + + mainThread = threadService.setTestInstance(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, threadService)); + }); + + suiteTeardown(() => { + setUnexpectedErrorHandler(originalErrorHandler); + model.dispose(); + }); + + teardown(function () { + while (disposables.length) { + disposables.pop().dispose(); + } + return threadService.sync(); + }); + + // --- outline + + test('DocumentSymbols, register/deregister', function () { + assert.equal(DocumentSymbolProviderRegistry.all(model).length, 0); + let d1 = extHost.registerDocumentSymbolProvider(defaultSelector, { + provideDocumentSymbols() { + return []; + } + }); + + return threadService.sync().then(() => { + assert.equal(DocumentSymbolProviderRegistry.all(model).length, 1); + d1.dispose(); + return threadService.sync(); + }); + + }); + + test('DocumentSymbols, evil provider', function () { + disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + provideDocumentSymbols(): any { + throw new Error('evil document symbol provider'); + } + })); + disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + provideDocumentSymbols(): any { + return [new types.SymbolInformation('test', types.SymbolKind.Field, new types.Range(0, 0, 0, 0))]; + } + })); + + return threadService.sync().then(() => { + + return getDocumentSymbols(model).then(value => { + assert.equal(value.entries.length, 1); + }); + }); + }); + + test('DocumentSymbols, data conversion', function () { + disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + provideDocumentSymbols(): any { + return [new types.SymbolInformation('test', types.SymbolKind.Field, new types.Range(0, 0, 0, 0))]; + } + })); + + return threadService.sync().then(() => { + + return getDocumentSymbols(model).then(value => { + assert.equal(value.entries.length, 1); + + let entry = value.entries[0]; + assert.equal(entry.name, 'test'); + assert.deepEqual(entry.location.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); + }); + }); + }); + + // --- code lens + + test('CodeLens, evil provider', function () { + + disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + provideCodeLenses(): any { + throw new Error('evil'); + } + })); + disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + provideCodeLenses() { + return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; + } + })); + + return threadService.sync().then(() => { + return getCodeLensData(model).then(value => { + assert.equal(value.length, 1); + }); + }); + }); + + test('CodeLens, do not resolve a resolved lens', function () { + + disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + provideCodeLenses(): any { + return [new types.CodeLens( + new types.Range(0, 0, 0, 0), + { command: 'id', title: 'Title' })]; + }, + resolveCodeLens(): any { + assert.ok(false, 'do not resolve'); + } + })); + + return threadService.sync().then(() => { + + return getCodeLensData(model).then(value => { + assert.equal(value.length, 1); + let data = value[0]; + + return asWinJsPromise((token) => { + return data.provider.resolveCodeLens(model, data.symbol, token); + }).then(symbol => { + assert.equal(symbol.command.id, 'id'); + assert.equal(symbol.command.title, 'Title'); + }); + }); + }); + }); + + test('CodeLens, missing command', function () { + + disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + provideCodeLenses() { + return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; + } + })); + + return threadService.sync().then(() => { + + return getCodeLensData(model).then(value => { + assert.equal(value.length, 1); + + let data = value[0]; + return asWinJsPromise((token) => { + return data.provider.resolveCodeLens(model, data.symbol, token); + }).then(symbol => { + + assert.equal(symbol.command.id, 'missing'); + assert.equal(symbol.command.title, '<>'); + }); + }); + }); + }); + + // --- definition + + test('Definition, data conversion', function () { + + disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + provideDefinition(): any { + return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; + } + })); + + return threadService.sync().then(() => { + + return getDefinitionsAtPosition(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); + assert.equal(entry.uri.toString(), model.uri.toString()); + }); + }); + }); + + test('Definition, one or many', function () { + + disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + provideDefinition(): any { + return [new types.Location(model.uri, new types.Range(1, 1, 1, 1))]; + } + })); + disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + provideDefinition(): any { + return new types.Location(model.uri, new types.Range(1, 1, 1, 1)); + } + })); + + return threadService.sync().then(() => { + + return getDefinitionsAtPosition(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value.length, 2); + }); + }); + }); + + test('Definition, registration order', function () { + + disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + provideDefinition(): any { + return [new types.Location(URI.parse('far://first'), new types.Range(2, 3, 4, 5))]; + } + })); + + disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + provideDefinition(): any { + return new types.Location(URI.parse('far://second'), new types.Range(1, 2, 3, 4)); + } + })); + + return threadService.sync().then(() => { + + return getDefinitionsAtPosition(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value.length, 2); + // let [first, second] = value; + + assert.equal(value[0].uri.authority, 'second'); + assert.equal(value[1].uri.authority, 'first'); + }); + }); + }); + + test('Definition, evil provider', function () { + + disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + provideDefinition(): any { + throw new Error('evil provider'); + } + })); + disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + provideDefinition(): any { + return new types.Location(model.uri, new types.Range(1, 1, 1, 1)); + } + })); + + return threadService.sync().then(() => { + + return getDefinitionsAtPosition(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value.length, 1); + }); + }); + }); + + // --- implementation + + test('Implementation, data conversion', function () { + + disposables.push(extHost.registerImplementationProvider(defaultSelector, { + provideImplementation(): any { + return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; + } + })); + + return threadService.sync().then(() => { + return getImplementationsAtPosition(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); + assert.equal(entry.uri.toString(), model.uri.toString()); + }); + }); + }); + + // --- type definition + + test('Type Definition, data conversion', function () { + + disposables.push(extHost.registerTypeDefinitionProvider(defaultSelector, { + provideTypeDefinition(): any { + return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; + } + })); + + return threadService.sync().then(() => { + return getTypeDefinitionsAtPosition(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); + assert.equal(entry.uri.toString(), model.uri.toString()); + }); + }); + }); + + // --- extra info + + test('HoverProvider, word range at pos', function () { + + disposables.push(extHost.registerHoverProvider(defaultSelector, { + provideHover(): any { + return new types.Hover('Hello'); + } + })); + + return threadService.sync().then(() => { + getHover(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 }); + }); + }); + }); + + + test('HoverProvider, given range', function () { + + disposables.push(extHost.registerHoverProvider(defaultSelector, { + provideHover(): any { + return new types.Hover('Hello', new types.Range(3, 0, 8, 7)); + } + })); + + return threadService.sync().then(() => { + + getHover(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 4, startColumn: 1, endLineNumber: 9, endColumn: 8 }); + }); + }); + }); + + + test('HoverProvider, registration order', function () { + disposables.push(extHost.registerHoverProvider(defaultSelector, { + provideHover(): any { + return new types.Hover('registered first'); + } + })); + + + disposables.push(extHost.registerHoverProvider(defaultSelector, { + provideHover(): any { + return new types.Hover('registered second'); + } + })); + + return threadService.sync().then(() => { + return getHover(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value.length, 2); + let [first, second] = value as Hover[]; + assert.equal(first.contents[0].value, 'registered second'); + assert.equal(second.contents[0].value, 'registered first'); + }); + }); + }); + + + test('HoverProvider, evil provider', function () { + + disposables.push(extHost.registerHoverProvider(defaultSelector, { + provideHover(): any { + throw new Error('evil'); + } + })); + disposables.push(extHost.registerHoverProvider(defaultSelector, { + provideHover(): any { + return new types.Hover('Hello'); + } + })); + + return threadService.sync().then(() => { + + getHover(model, new EditorPosition(1, 1)).then(value => { + + assert.equal(value.length, 1); + }); + }); + }); + + // --- occurrences + + test('Occurrences, data conversion', function () { + + disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + provideDocumentHighlights(): any { + return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; + } + })); + + return threadService.sync().then(() => { + + return getOccurrencesAtPosition(model, new EditorPosition(1, 2)).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 }); + assert.equal(entry.kind, DocumentHighlightKind.Text); + }); + }); + }); + + test('Occurrences, order 1/2', function () { + + disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + provideDocumentHighlights(): any { + return []; + } + })); + disposables.push(extHost.registerDocumentHighlightProvider('*', { + provideDocumentHighlights(): any { + return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; + } + })); + + return threadService.sync().then(() => { + + return getOccurrencesAtPosition(model, new EditorPosition(1, 2)).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 }); + assert.equal(entry.kind, DocumentHighlightKind.Text); + }); + }); + }); + + test('Occurrences, order 2/2', function () { + + disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + provideDocumentHighlights(): any { + return [new types.DocumentHighlight(new types.Range(0, 0, 0, 2))]; + } + })); + disposables.push(extHost.registerDocumentHighlightProvider('*', { + provideDocumentHighlights(): any { + return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; + } + })); + + return threadService.sync().then(() => { + + return getOccurrencesAtPosition(model, new EditorPosition(1, 2)).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 3 }); + assert.equal(entry.kind, DocumentHighlightKind.Text); + }); + }); + }); + + test('Occurrences, evil provider', function () { + + disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + provideDocumentHighlights(): any { + throw new Error('evil'); + } + })); + + disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + provideDocumentHighlights(): any { + return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; + } + })); + + return threadService.sync().then(() => { + + return getOccurrencesAtPosition(model, new EditorPosition(1, 2)).then(value => { + assert.equal(value.length, 1); + }); + }); + }); + + // --- references + + test('References, registration order', function () { + + disposables.push(extHost.registerReferenceProvider(defaultSelector, { + provideReferences(): any { + return [new types.Location(URI.parse('far://register/first'), new types.Range(0, 0, 0, 0))]; + } + })); + + disposables.push(extHost.registerReferenceProvider(defaultSelector, { + provideReferences(): any { + return [new types.Location(URI.parse('far://register/second'), new types.Range(0, 0, 0, 0))]; + } + })); + + return threadService.sync().then(() => { + + return provideReferences(model, new EditorPosition(1, 2)).then(value => { + assert.equal(value.length, 2); + + let [first, second] = value; + assert.equal(first.uri.path, '/second'); + assert.equal(second.uri.path, '/first'); + }); + }); + }); + + test('References, data conversion', function () { + + disposables.push(extHost.registerReferenceProvider(defaultSelector, { + provideReferences(): any { + return [new types.Location(model.uri, new types.Position(0, 0))]; + } + })); + + return threadService.sync().then(() => { + + return provideReferences(model, new EditorPosition(1, 2)).then(value => { + assert.equal(value.length, 1); + + let [item] = value; + assert.deepEqual(item.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); + assert.equal(item.uri.toString(), model.uri.toString()); + }); + + }); + }); + + test('References, evil provider', function () { + + disposables.push(extHost.registerReferenceProvider(defaultSelector, { + provideReferences(): any { + throw new Error('evil'); + } + })); + disposables.push(extHost.registerReferenceProvider(defaultSelector, { + provideReferences(): any { + return [new types.Location(model.uri, new types.Range(0, 0, 0, 0))]; + } + })); + + return threadService.sync().then(() => { + + return provideReferences(model, new EditorPosition(1, 2)).then(value => { + assert.equal(value.length, 1); + }); + + }); + }); + + // --- quick fix + + test('Quick Fix, data conversion', function () { + + disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + provideCodeActions(): any { + return [ + { command: 'test1', title: 'Testing1' }, + { command: 'test2', title: 'Testing2' } + ]; + } + })); + + return threadService.sync().then(() => { + return getCodeActions(model, model.getFullModelRange()).then(value => { + assert.equal(value.length, 2); + + let [first, second] = value; + assert.equal(first.title, 'Testing1'); + assert.equal(first.id, 'test1'); + assert.equal(second.title, 'Testing2'); + assert.equal(second.id, 'test2'); + }); + }); + }); + + test('Cannot read property \'id\' of undefined, #29469', function () { + + disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + provideCodeActions(): any { + return [ + undefined, + null, + { command: 'test', title: 'Testing' } + ]; + } + })); + + return threadService.sync().then(() => { + return getCodeActions(model, model.getFullModelRange()).then(value => { + assert.equal(value.length, 1); + }); + }); + }); + + test('Quick Fix, evil provider', function () { + + disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + provideCodeActions(): any { + throw new Error('evil'); + } + })); + disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + provideCodeActions(): any { + return [{ command: 'test', title: 'Testing' }]; + } + })); + + return threadService.sync().then(() => { + return getCodeActions(model, model.getFullModelRange()).then(value => { + assert.equal(value.length, 1); + }); + }); + }); + + // --- navigate types + + test('Navigate types, evil provider', function () { + + disposables.push(extHost.registerWorkspaceSymbolProvider({ + provideWorkspaceSymbols(): any { + throw new Error('evil'); + } + })); + + disposables.push(extHost.registerWorkspaceSymbolProvider({ + provideWorkspaceSymbols(): any { + return [new types.SymbolInformation('testing', types.SymbolKind.Array, new types.Range(0, 0, 1, 1))]; + } + })); + + return threadService.sync().then(() => { + + return getWorkspaceSymbols('').then(value => { + assert.equal(value.length, 1); + const [first] = value; + const [, symbols] = first; + assert.equal(symbols.length, 1); + assert.equal(symbols[0].name, 'testing'); + }); + }); + }); + + // --- rename + + test('Rename, evil provider 1/2', function () { + + disposables.push(extHost.registerRenameProvider(defaultSelector, { + provideRenameEdits(): any { + throw Error('evil'); + } + })); + + return threadService.sync().then(() => { + + return rename(model, new EditorPosition(1, 1), 'newName').then(value => { + throw new Error(''); + }, err => { + // expected + }); + }); + }); + + test('Rename, evil provider 2/2', function () { + + disposables.push(extHost.registerRenameProvider('*', { + provideRenameEdits(): any { + throw Error('evil'); + } + })); + + disposables.push(extHost.registerRenameProvider(defaultSelector, { + provideRenameEdits(): any { + let edit = new types.WorkspaceEdit(); + edit.replace(model.uri, new types.Range(0, 0, 0, 0), 'testing'); + return edit; + } + })); + + return threadService.sync().then(() => { + + return rename(model, new EditorPosition(1, 1), 'newName').then(value => { + assert.equal(value.edits.length, 1); + }); + }); + }); + + test('Rename, ordering', function () { + + disposables.push(extHost.registerRenameProvider('*', { + provideRenameEdits(): any { + let edit = new types.WorkspaceEdit(); + edit.replace(model.uri, new types.Range(0, 0, 0, 0), 'testing'); + edit.replace(model.uri, new types.Range(1, 0, 1, 0), 'testing'); + return edit; + } + })); + + disposables.push(extHost.registerRenameProvider(defaultSelector, { + provideRenameEdits(): any { + return; + } + })); + + return threadService.sync().then(() => { + + return rename(model, new EditorPosition(1, 1), 'newName').then(value => { + assert.equal(value.edits.length, 2); // least relevant renamer + }); + }); + }); + + // --- parameter hints + + test('Parameter Hints, order', function () { + + disposables.push(extHost.registerSignatureHelpProvider(defaultSelector, { + provideSignatureHelp(): any { + return undefined; + } + }, [])); + + disposables.push(extHost.registerSignatureHelpProvider(defaultSelector, { + provideSignatureHelp(): vscode.SignatureHelp { + return new types.SignatureHelp(); + } + }, [])); + + return threadService.sync().then(() => { + + return provideSignatureHelp(model, new EditorPosition(1, 1)).then(value => { + assert.ok(value); + }); + }); + }); + test('Parameter Hints, evil provider', function () { + + disposables.push(extHost.registerSignatureHelpProvider(defaultSelector, { + provideSignatureHelp(): any { + throw new Error('evil'); + } + }, [])); + + return threadService.sync().then(() => { + + return provideSignatureHelp(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value, undefined); + }); + }); + }); + + // --- suggestions + + test('Suggest, order 1/3', function () { + + disposables.push(extHost.registerCompletionItemProvider('*', { + provideCompletionItems(): any { + return [new types.CompletionItem('testing1')]; + } + }, [])); + + disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + provideCompletionItems(): any { + return [new types.CompletionItem('testing2')]; + } + }, [])); + + return threadService.sync().then(() => { + return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { + assert.equal(value.length, 1); + assert.equal(value[0].suggestion.insertText, 'testing2'); + }); + }); + }); + + test('Suggest, order 2/3', function () { + + disposables.push(extHost.registerCompletionItemProvider('*', { + provideCompletionItems(): any { + return [new types.CompletionItem('weak-selector')]; // weaker selector but result + } + }, [])); + + disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + provideCompletionItems(): any { + return []; // stronger selector but not a good result; + } + }, [])); + + return threadService.sync().then(() => { + return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { + assert.equal(value.length, 1); + assert.equal(value[0].suggestion.insertText, 'weak-selector'); + }); + }); + }); + + test('Suggest, order 2/3', function () { + + disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + provideCompletionItems(): any { + return [new types.CompletionItem('strong-1')]; + } + }, [])); + + disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + provideCompletionItems(): any { + return [new types.CompletionItem('strong-2')]; + } + }, [])); + + return threadService.sync().then(() => { + return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { + assert.equal(value.length, 2); + assert.equal(value[0].suggestion.insertText, 'strong-1'); // sort by label + assert.equal(value[1].suggestion.insertText, 'strong-2'); + }); + }); + }); + + test('Suggest, evil provider', function () { + + disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + provideCompletionItems(): any { + throw new Error('evil'); + } + }, [])); + + disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + provideCompletionItems(): any { + return [new types.CompletionItem('testing')]; + } + }, [])); + + + return threadService.sync().then(() => { + + return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { + assert.equal(value[0].container.incomplete, undefined); + }); + }); + }); + + test('Suggest, CompletionList', function () { + + disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + provideCompletionItems(): any { + return new types.CompletionList([new types.CompletionItem('hello')], true); + } + }, [])); + + return threadService.sync().then(() => { + + provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { + assert.equal(value[0].container.incomplete, true); + }); + }); + }); + + // --- format + + test('Format Doc, data conversion', function () { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + provideDocumentFormattingEdits(): any { + return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing'), types.TextEdit.setEndOfLine(types.EndOfLine.LF)]; + } + })); + + return threadService.sync().then(() => { + return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }).then(value => { + assert.equal(value.length, 2); + let [first, second] = value; + assert.equal(first.text, 'testing'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); + + assert.equal(second.eol, EditorCommon.EndOfLineSequence.LF); + assert.equal(second.text, ''); + assert.equal(second.range, undefined); + }); + }); + }); + + test('Format Doc, evil provider', function () { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + provideDocumentFormattingEdits(): any { + throw new Error('evil'); + } + })); + + return threadService.sync().then(() => { + return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }); + }); + }); + + test('Format Doc, order', function () { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + provideDocumentFormattingEdits(): any { + return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing')]; + } + })); + + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + provideDocumentFormattingEdits(): any { + return undefined; + } + })); + + return threadService.sync().then(() => { + return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }).then(value => { + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.text, 'testing'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); + }); + }); + }); + + test('Format Range, data conversion', function () { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + provideDocumentRangeFormattingEdits(): any { + return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing')]; + } + })); + + return threadService.sync().then(() => { + return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }).then(value => { + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.text, 'testing'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); + }); + }); + }); + + test('Format Range, + format_doc', function () { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + provideDocumentRangeFormattingEdits(): any { + return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'range')]; + } + })); + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + provideDocumentFormattingEdits(): any { + return [new types.TextEdit(new types.Range(0, 0, 1, 1), 'doc')]; + } + })); + return threadService.sync().then(() => { + return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }).then(value => { + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.text, 'range'); + }); + }); + }); + + test('Format Range, evil provider', function () { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + provideDocumentRangeFormattingEdits(): any { + throw new Error('evil'); + } + })); + + return threadService.sync().then(() => { + return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }); + }); + }); + + test('Format on Type, data conversion', function () { + + disposables.push(extHost.registerOnTypeFormattingEditProvider(defaultSelector, { + provideOnTypeFormattingEdits(): any { + return [new types.TextEdit(new types.Range(0, 0, 0, 0), arguments[2])]; + } + }, [';'])); + + return threadService.sync().then(() => { + return getOnTypeFormattingEdits(model, new EditorPosition(1, 1), ';', { insertSpaces: true, tabSize: 2 }).then(value => { + assert.equal(value.length, 1); + let [first] = value; + + assert.equal(first.text, ';'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); + }); + }); + }); + + test('Links, data conversion', function () { + + disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + provideDocumentLinks() { + return [new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3'))]; + } + })); + + return threadService.sync().then(() => { + return getLinks(model).then(value => { + assert.equal(value.length, 1); + let [first] = value; + + assert.equal(first.url, 'foo:bar#3'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 }); + }); + }); + }); + + test('Links, evil provider', function () { + + disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + provideDocumentLinks() { + return [new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3'))]; + } + })); + + disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + provideDocumentLinks(): any { + throw new Error(); + } + })); + + return threadService.sync().then(() => { + return getLinks(model).then(value => { + assert.equal(value.length, 1); + let [first] = value; + + assert.equal(first.url, 'foo:bar#3'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 }); + }); + }); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts new file mode 100644 index 0000000000..5cf21a35f6 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.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. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { Action } from 'vs/base/common/actions'; +import { MainThreadMessageService } from 'vs/workbench/api/electron-browser/mainThreadMessageService'; +import { TPromise as Promise } from 'vs/base/common/winjs.base'; + +suite('ExtHostMessageService', function () { + + test('propagte handle on select', function () { + + let service = new MainThreadMessageService(null, null, { + show(sev: number, m: { message; actions: Action[] }) { + assert.equal(m.actions.length, 1); + setImmediate(() => m.actions[0].run()); + return () => { }; + } + }, { + choose() { + throw new Error('not implemented'); + } + }); + + return service.$showMessage(1, 'h', {}, [{ handle: 42, title: 'a thing', isCloseAffordance: true }]).then(handle => { + assert.equal(handle, 42); + }); + }); + + test('isCloseAffordance', function () { + + let actions: Action[]; + let service = new MainThreadMessageService(null, null, { + show(sev: number, m: { message; actions: Action[] }) { + actions = m.actions; + } + }, { + choose() { + throw new Error('not implemented'); + } + }); + + // default close action + service.$showMessage(1, '', {}, [{ title: 'a thing', isCloseAffordance: false, handle: 0 }]); + assert.equal(actions.length, 2); + let [first, second] = actions; + assert.equal(first.label, 'a thing'); + assert.equal(second.label, 'Close'); + + // override close action + service.$showMessage(1, '', {}, [{ title: 'a thing', isCloseAffordance: true, handle: 0 }]); + assert.equal(actions.length, 1); + first = actions[0]; + assert.equal(first.label, 'a thing'); + }); + + test('hide on select', function () { + + let actions: Action[]; + let c: number; + let service = new MainThreadMessageService(null, null, { + show(sev: number, m: { message; actions: Action[] }) { + c = 0; + actions = m.actions; + return () => { + c += 1; + }; + } + }, { + choose() { + throw new Error('not implemented'); + } + }); + + service.$showMessage(1, '', {}, [{ title: 'a thing', isCloseAffordance: true, handle: 0 }]); + assert.equal(actions.length, 1); + + actions[0].run(); + assert.equal(c, 1); + }); + + suite('modal', () => { + test('calls choice service', () => { + const service = new MainThreadMessageService(null, null, { + show(sev: number, m: { message; actions: Action[] }) { + throw new Error('not implemented'); + } + }, { + choose(severity, message, options, modal) { + assert.equal(severity, 1); + assert.equal(message, 'h'); + assert.equal(options.length, 2); + assert.equal(options[1], 'Cancel'); + return Promise.as(0); + } + }); + + return service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: false }]).then(handle => { + assert.equal(handle, 42); + }); + }); + + test('returns undefined when cancelled', () => { + const service = new MainThreadMessageService(null, null, { + show(sev: number, m: { message; actions: Action[] }) { + throw new Error('not implemented'); + } + }, { + choose(severity, message, options, modal) { + return Promise.as(1); + } + }); + + return service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: false }]).then(handle => { + assert.equal(handle, undefined); + }); + }); + + test('hides Cancel button when not needed', () => { + const service = new MainThreadMessageService(null, null, { + show(sev: number, m: { message; actions: Action[] }) { + throw new Error('not implemented'); + } + }, { + choose(severity, message, options, modal) { + assert.equal(options.length, 1); + return Promise.as(0); + } + }); + + return service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: true }]).then(handle => { + assert.equal(handle, 42); + }); + }); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts new file mode 100644 index 0000000000..0ca529cab3 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts @@ -0,0 +1,349 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes'; +import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; +import { MainThreadEditorsShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostTextEditorOptions, ExtHostTextEditor } from 'vs/workbench/api/node/extHostTextEditor'; +import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData'; +import URI from 'vs/base/common/uri'; + +suite('ExtHostTextEditor', () => { + + let editor: ExtHostTextEditor; + + setup(() => { + let doc = new ExtHostDocumentData(undefined, URI.file(''), [ + 'aaaa bbbb+cccc abc' + ], '\n', 'text', 1, false); + editor = new ExtHostTextEditor(null, 'fake', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, 1); + }); + + test('disposed editor', () => { + + assert.ok(editor.document); + editor._acceptViewColumn(3); + assert.equal(3, editor.viewColumn); + + editor.dispose(); + + assert.throws(() => editor._acceptViewColumn(2)); + assert.equal(3, editor.viewColumn); + + assert.ok(editor.document); + assert.throws(() => editor._acceptOptions(null)); + assert.throws(() => editor._acceptSelections([])); + }); +}); + +suite('ExtHostTextEditorOptions', () => { + + let opts: ExtHostTextEditorOptions; + let calls: ITextEditorConfigurationUpdate[] = []; + + setup(() => { + calls = []; + let mockProxy: MainThreadEditorsShape = { + dispose: undefined, + $trySetOptions: (id: string, options: ITextEditorConfigurationUpdate) => { + assert.equal(id, '1'); + calls.push(options); + return TPromise.as(void 0); + }, + $tryShowTextDocument: undefined, + $registerTextEditorDecorationType: undefined, + $removeTextEditorDecorationType: undefined, + $tryShowEditor: undefined, + $tryHideEditor: undefined, + $trySetDecorations: undefined, + $tryRevealRange: undefined, + $trySetSelections: undefined, + $tryApplyEdits: undefined, + $tryInsertSnippet: undefined, + $getDiffInformation: undefined + }; + opts = new ExtHostTextEditorOptions(mockProxy, '1', { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + }); + + teardown(() => { + opts = null; + calls = null; + }); + + function assertState(opts: ExtHostTextEditorOptions, expected: IResolvedTextEditorConfiguration): void { + let actual = { + tabSize: opts.tabSize, + insertSpaces: opts.insertSpaces, + cursorStyle: opts.cursorStyle, + lineNumbers: opts.lineNumbers + }; + assert.deepEqual(actual, expected); + } + + test('can set tabSize to the same value', () => { + opts.tabSize = 4; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('can change tabSize to positive integer', () => { + opts.tabSize = 1; + assertState(opts, { + tabSize: 1, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ tabSize: 1 }]); + }); + + test('can change tabSize to positive float', () => { + opts.tabSize = 2.3; + assertState(opts, { + tabSize: 2, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ tabSize: 2 }]); + }); + + test('can change tabSize to a string number', () => { + opts.tabSize = '2'; + assertState(opts, { + tabSize: 2, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ tabSize: 2 }]); + }); + + test('tabSize can request indentation detection', () => { + opts.tabSize = 'auto'; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ tabSize: 'auto' }]); + }); + + test('ignores invalid tabSize 1', () => { + opts.tabSize = null; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('ignores invalid tabSize 2', () => { + opts.tabSize = -5; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('ignores invalid tabSize 3', () => { + opts.tabSize = 'hello'; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('ignores invalid tabSize 4', () => { + opts.tabSize = '-17'; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('can set insertSpaces to the same value', () => { + opts.insertSpaces = false; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('can set insertSpaces to boolean', () => { + opts.insertSpaces = true; + assertState(opts, { + tabSize: 4, + insertSpaces: true, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ insertSpaces: true }]); + }); + + test('can set insertSpaces to false string', () => { + opts.insertSpaces = 'false'; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('can set insertSpaces to truey', () => { + opts.insertSpaces = 'hello'; + assertState(opts, { + tabSize: 4, + insertSpaces: true, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ insertSpaces: true }]); + }); + + test('insertSpaces can request indentation detection', () => { + opts.insertSpaces = 'auto'; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ insertSpaces: 'auto' }]); + }); + + test('can set cursorStyle to same value', () => { + opts.cursorStyle = TextEditorCursorStyle.Line; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('can change cursorStyle', () => { + opts.cursorStyle = TextEditorCursorStyle.Block; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Block, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ cursorStyle: TextEditorCursorStyle.Block }]); + }); + + test('can set lineNumbers to same value', () => { + opts.lineNumbers = TextEditorLineNumbersStyle.On; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('can change lineNumbers', () => { + opts.lineNumbers = TextEditorLineNumbersStyle.Off; + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.Off + }); + assert.deepEqual(calls, [{ lineNumbers: TextEditorLineNumbersStyle.Off }]); + }); + + test('can do bulk updates 0', () => { + opts.assign({ + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('can do bulk updates 1', () => { + opts.assign({ + tabSize: 'auto', + insertSpaces: true + }); + assertState(opts, { + tabSize: 4, + insertSpaces: true, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ tabSize: 'auto', insertSpaces: true }]); + }); + + test('can do bulk updates 2', () => { + opts.assign({ + tabSize: 3, + insertSpaces: 'auto' + }); + assertState(opts, { + tabSize: 3, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ tabSize: 3, insertSpaces: 'auto' }]); + }); + + test('can do bulk updates 3', () => { + opts.assign({ + cursorStyle: TextEditorCursorStyle.Block, + lineNumbers: TextEditorLineNumbersStyle.Relative + }); + assertState(opts, { + tabSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Block, + lineNumbers: TextEditorLineNumbersStyle.Relative + }); + assert.deepEqual(calls, [{ cursorStyle: TextEditorCursorStyle.Block, lineNumbers: TextEditorLineNumbersStyle.Relative }]); + }); + +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts new file mode 100644 index 0000000000..b52af029b5 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { Emitter } from 'vs/base/common/event'; +import { ExtHostTreeViews } from 'vs/workbench/api/node/extHostTreeViews'; +import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; +import { MainThreadTreeViewsShape, MainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { TreeDataProvider, TreeItem } from 'vscode'; +import { TestThreadService } from './testThreadService'; +import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; + +suite('ExtHostConfiguration', function () { + + + class RecordingShape extends mock() { + + onRefresh = new Emitter(); + + $registerView(treeViewId: string): void { + } + + $refresh(viewId: string, itemHandles: number[]): void { + this.onRefresh.fire(itemHandles); + } + }; + + let testObject: ExtHostTreeViews; + let target: RecordingShape; + let onDidChangeTreeData: Emitter; + + setup(() => { + let threadService = new TestThreadService(); + // Use IInstantiationService to get typechecking when instantiating + let inst: IInstantiationService; + { + let instantiationService = new TestInstantiationService(); + inst = instantiationService; + } + + threadService.setTestInstance(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, threadService)); + target = new RecordingShape(); + testObject = new ExtHostTreeViews(target, new ExtHostCommands(threadService, new ExtHostHeapService())); + onDidChangeTreeData = new Emitter(); + testObject.registerTreeDataProvider('testDataProvider', aTreeDataProvider()); + + testObject.$getElements('testDataProvider').then(elements => { + for (const element of elements) { + testObject.$getChildren('testDataProvider', element.handle); + } + }); + }); + + test('refresh calls are throttled on roots', function (done) { + target.onRefresh.event(actuals => { + assert.equal(0, actuals.length); + done(); + }); + onDidChangeTreeData.fire(); + onDidChangeTreeData.fire(); + onDidChangeTreeData.fire(); + onDidChangeTreeData.fire(); + }); + + test('refresh calls are throttled on elements', function (done) { + target.onRefresh.event(actuals => { + assert.deepEqual([1, 2], actuals); + done(); + }); + + onDidChangeTreeData.fire('a'); + onDidChangeTreeData.fire('b'); + onDidChangeTreeData.fire('b'); + onDidChangeTreeData.fire('a'); + }); + + test('refresh calls are throttled on unknown elements', function (done) { + target.onRefresh.event(actuals => { + assert.deepEqual([1, 2], actuals); + done(); + }); + + onDidChangeTreeData.fire('a'); + onDidChangeTreeData.fire('b'); + onDidChangeTreeData.fire('g'); + onDidChangeTreeData.fire('a'); + }); + + test('refresh calls are throttled on unknown elements and root', function (done) { + target.onRefresh.event(actuals => { + assert.equal(0, actuals.length); + done(); + }); + + onDidChangeTreeData.fire('a'); + onDidChangeTreeData.fire('b'); + onDidChangeTreeData.fire('g'); + onDidChangeTreeData.fire(''); + }); + + test('refresh calls are throttled on elements and root', function (done) { + target.onRefresh.event(actuals => { + assert.equal(0, actuals.length); + done(); + }); + + onDidChangeTreeData.fire('a'); + onDidChangeTreeData.fire('b'); + onDidChangeTreeData.fire(); + onDidChangeTreeData.fire('a'); + }); + + function aTreeDataProvider(): TreeDataProvider { + return >{ + getChildren: (element: string): string[] => { + if (!element) { + return ['a', 'b']; + } + if (element === 'a') { + return ['aa', 'ab']; + } + if (element === 'b') { + return ['ba', 'bb']; + } + return []; + }, + getTreeItem: (element: string): TreeItem => { + return { + label: element + }; + }, + onDidChangeTreeData: onDidChangeTreeData.event + }; + } + +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts new file mode 100644 index 0000000000..d0d5efd352 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -0,0 +1,469 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import * as types from 'vs/workbench/api/node/extHostTypes'; +import { isWindows } from 'vs/base/common/platform'; + +function assertToJSON(a: any, expected: any) { + const raw = JSON.stringify(a); + const actual = JSON.parse(raw); + assert.deepEqual(actual, expected); +} + +suite('ExtHostTypes', function () { + + test('URI, toJSON', function () { + + let uri = URI.parse('file:///path/test.file'); + let data = uri.toJSON(); + assert.deepEqual(data, { + $mid: 1, + scheme: 'file', + path: '/path/test.file', + fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'), + external: 'file:///path/test.file' + }); + }); + + test('Disposable', function () { + + let count = 0; + let d = new types.Disposable(() => { + count += 1; + return 12; + }); + d.dispose(); + assert.equal(count, 1); + + d.dispose(); + assert.equal(count, 1); + + types.Disposable.from(undefined, { dispose() { count += 1; } }).dispose(); + assert.equal(count, 2); + + + assert.throws(() => { + new types.Disposable(() => { + throw new Error(); + }).dispose(); + }); + + new types.Disposable(undefined).dispose(); + + }); + + test('Position', function () { + assert.throws(() => new types.Position(-1, 0)); + assert.throws(() => new types.Position(0, -1)); + + let pos = new types.Position(0, 0); + assert.throws(() => (pos as any).line = -1); + assert.throws(() => (pos as any).character = -1); + assert.throws(() => (pos as any).line = 12); + + let { line, character } = pos.toJSON(); + assert.equal(line, 0); + assert.equal(character, 0); + }); + + test('Position, toJSON', function () { + let pos = new types.Position(4, 2); + assertToJSON(pos, { line: 4, character: 2 }); + }); + + test('Position, isBefore(OrEqual)?', function () { + let p1 = new types.Position(1, 3); + let p2 = new types.Position(1, 2); + let p3 = new types.Position(0, 4); + + assert.ok(p1.isBeforeOrEqual(p1)); + assert.ok(!p1.isBefore(p1)); + assert.ok(p2.isBefore(p1)); + assert.ok(p3.isBefore(p2)); + }); + + test('Position, isAfter(OrEqual)?', function () { + let p1 = new types.Position(1, 3); + let p2 = new types.Position(1, 2); + let p3 = new types.Position(0, 4); + + assert.ok(p1.isAfterOrEqual(p1)); + assert.ok(!p1.isAfter(p1)); + assert.ok(p1.isAfter(p2)); + assert.ok(p2.isAfter(p3)); + assert.ok(p1.isAfter(p3)); + }); + + test('Position, compareTo', function () { + let p1 = new types.Position(1, 3); + let p2 = new types.Position(1, 2); + let p3 = new types.Position(0, 4); + + assert.equal(p1.compareTo(p1), 0); + assert.equal(p2.compareTo(p1), -1); + assert.equal(p1.compareTo(p2), 1); + assert.equal(p2.compareTo(p3), 1); + assert.equal(p1.compareTo(p3), 1); + }); + + test('Position, translate', function () { + let p1 = new types.Position(1, 3); + + assert.ok(p1.translate() === p1); + assert.ok(p1.translate({}) === p1); + assert.ok(p1.translate(0, 0) === p1); + assert.ok(p1.translate(0) === p1); + assert.ok(p1.translate(undefined, 0) === p1); + assert.ok(p1.translate(undefined) === p1); + + let res = p1.translate(-1); + assert.equal(res.line, 0); + assert.equal(res.character, 3); + + res = p1.translate({ lineDelta: -1 }); + assert.equal(res.line, 0); + assert.equal(res.character, 3); + + res = p1.translate(undefined, -1); + assert.equal(res.line, 1); + assert.equal(res.character, 2); + + res = p1.translate({ characterDelta: -1 }); + assert.equal(res.line, 1); + assert.equal(res.character, 2); + + res = p1.translate(11); + assert.equal(res.line, 12); + assert.equal(res.character, 3); + + assert.throws(() => p1.translate(null)); + assert.throws(() => p1.translate(null, null)); + assert.throws(() => p1.translate(-2)); + assert.throws(() => p1.translate({ lineDelta: -2 })); + assert.throws(() => p1.translate(-2, null)); + assert.throws(() => p1.translate(0, -4)); + }); + + test('Position, with', function () { + let p1 = new types.Position(1, 3); + + assert.ok(p1.with() === p1); + assert.ok(p1.with(1) === p1); + assert.ok(p1.with(undefined, 3) === p1); + assert.ok(p1.with(1, 3) === p1); + assert.ok(p1.with(undefined) === p1); + assert.ok(p1.with({ line: 1 }) === p1); + assert.ok(p1.with({ character: 3 }) === p1); + assert.ok(p1.with({ line: 1, character: 3 }) === p1); + + let p2 = p1.with({ line: 0, character: 11 }); + assert.equal(p2.line, 0); + assert.equal(p2.character, 11); + + assert.throws(() => p1.with(null)); + assert.throws(() => p1.with(-9)); + assert.throws(() => p1.with(0, -9)); + assert.throws(() => p1.with({ line: -1 })); + assert.throws(() => p1.with({ character: -1 })); + }); + + test('Range', function () { + assert.throws(() => new types.Range(-1, 0, 0, 0)); + assert.throws(() => new types.Range(0, -1, 0, 0)); + assert.throws(() => new types.Range(new types.Position(0, 0), undefined)); + assert.throws(() => new types.Range(new types.Position(0, 0), null)); + assert.throws(() => new types.Range(undefined, new types.Position(0, 0))); + assert.throws(() => new types.Range(null, new types.Position(0, 0))); + + let range = new types.Range(1, 0, 0, 0); + assert.throws(() => (range as any).start = null); + assert.throws(() => (range as any).start = new types.Position(0, 3)); + }); + + test('Range, toJSON', function () { + + let range = new types.Range(1, 2, 3, 4); + assertToJSON(range, [{ line: 1, character: 2 }, { line: 3, character: 4 }]); + }); + + test('Range, sorting', function () { + // sorts start/end + let range = new types.Range(1, 0, 0, 0); + assert.equal(range.start.line, 0); + assert.equal(range.end.line, 1); + + range = new types.Range(0, 0, 1, 0); + assert.equal(range.start.line, 0); + assert.equal(range.end.line, 1); + }); + + test('Range, isEmpty|isSingleLine', function () { + let range = new types.Range(1, 0, 0, 0); + assert.ok(!range.isEmpty); + assert.ok(!range.isSingleLine); + + range = new types.Range(1, 1, 1, 1); + assert.ok(range.isEmpty); + assert.ok(range.isSingleLine); + + range = new types.Range(0, 1, 0, 11); + assert.ok(!range.isEmpty); + assert.ok(range.isSingleLine); + + range = new types.Range(0, 0, 1, 1); + assert.ok(!range.isEmpty); + assert.ok(!range.isSingleLine); + }); + + test('Range, contains', function () { + let range = new types.Range(1, 1, 2, 11); + + assert.ok(range.contains(range.start)); + assert.ok(range.contains(range.end)); + assert.ok(range.contains(range)); + + assert.ok(!range.contains(new types.Range(1, 0, 2, 11))); + assert.ok(!range.contains(new types.Range(0, 1, 2, 11))); + assert.ok(!range.contains(new types.Range(1, 1, 2, 12))); + assert.ok(!range.contains(new types.Range(1, 1, 3, 11))); + }); + + test('Range, intersection', function () { + let range = new types.Range(1, 1, 2, 11); + let res: types.Range; + + res = range.intersection(range); + assert.equal(res.start.line, 1); + assert.equal(res.start.character, 1); + assert.equal(res.end.line, 2); + assert.equal(res.end.character, 11); + + res = range.intersection(new types.Range(2, 12, 4, 0)); + assert.equal(res, undefined); + + res = range.intersection(new types.Range(0, 0, 1, 0)); + assert.equal(res, undefined); + + res = range.intersection(new types.Range(0, 0, 1, 1)); + assert.ok(res.isEmpty); + assert.equal(res.start.line, 1); + assert.equal(res.start.character, 1); + + res = range.intersection(new types.Range(2, 11, 61, 1)); + assert.ok(res.isEmpty); + assert.equal(res.start.line, 2); + assert.equal(res.start.character, 11); + + assert.throws(() => range.intersection(null)); + assert.throws(() => range.intersection(undefined)); + }); + + test('Range, union', function () { + let ran1 = new types.Range(0, 0, 5, 5); + assert.ok(ran1.union(new types.Range(0, 0, 1, 1)) === ran1); + + let res: types.Range; + res = ran1.union(new types.Range(2, 2, 9, 9)); + assert.ok(res.start === ran1.start); + assert.equal(res.end.line, 9); + assert.equal(res.end.character, 9); + + ran1 = new types.Range(2, 1, 5, 3); + res = ran1.union(new types.Range(1, 0, 4, 2)); + assert.ok(res.end === ran1.end); + assert.equal(res.start.line, 1); + assert.equal(res.start.character, 0); + }); + + test('Range, with', function () { + let range = new types.Range(1, 1, 2, 11); + + assert.ok(range.with(range.start) === range); + assert.ok(range.with(undefined, range.end) === range); + assert.ok(range.with(range.start, range.end) === range); + assert.ok(range.with(new types.Position(1, 1)) === range); + assert.ok(range.with(undefined, new types.Position(2, 11)) === range); + assert.ok(range.with() === range); + assert.ok(range.with({ start: range.start }) === range); + assert.ok(range.with({ start: new types.Position(1, 1) }) === range); + assert.ok(range.with({ end: range.end }) === range); + assert.ok(range.with({ end: new types.Position(2, 11) }) === range); + + let res = range.with(undefined, new types.Position(9, 8)); + assert.equal(res.end.line, 9); + assert.equal(res.end.character, 8); + assert.equal(res.start.line, 1); + assert.equal(res.start.character, 1); + + res = range.with({ end: new types.Position(9, 8) }); + assert.equal(res.end.line, 9); + assert.equal(res.end.character, 8); + assert.equal(res.start.line, 1); + assert.equal(res.start.character, 1); + + res = range.with({ end: new types.Position(9, 8), start: new types.Position(2, 3) }); + assert.equal(res.end.line, 9); + assert.equal(res.end.character, 8); + assert.equal(res.start.line, 2); + assert.equal(res.start.character, 3); + + assert.throws(() => range.with(null)); + assert.throws(() => range.with(undefined, null)); + }); + + test('TextEdit', function () { + + let range = new types.Range(1, 1, 2, 11); + let edit = new types.TextEdit(range, undefined); + assert.equal(edit.newText, ''); + assertToJSON(edit, { range: [{ line: 1, character: 1 }, { line: 2, character: 11 }], newText: '' }); + + edit = new types.TextEdit(range, null); + assert.equal(edit.newText, ''); + + edit = new types.TextEdit(range, ''); + assert.equal(edit.newText, ''); + }); + + test('WorkspaceEdit', function () { + + let a = URI.file('a.ts'); + let b = URI.file('b.ts'); + + let edit = new types.WorkspaceEdit(); + assert.ok(!edit.has(a)); + + edit.set(a, [types.TextEdit.insert(new types.Position(0, 0), 'fff')]); + assert.ok(edit.has(a)); + assert.equal(edit.size, 1); + assertToJSON(edit, [[URI.parse('file:///a.ts').toJSON(), [{ range: [{ line: 0, character: 0 }, { line: 0, character: 0 }], newText: 'fff' }]]]); + + edit.insert(b, new types.Position(1, 1), 'fff'); + edit.delete(b, new types.Range(0, 0, 0, 0)); + assert.ok(edit.has(b)); + assert.equal(edit.size, 2); + assertToJSON(edit, [ + [URI.parse('file:///a.ts').toJSON(), [{ range: [{ line: 0, character: 0 }, { line: 0, character: 0 }], newText: 'fff' }]], + [URI.parse('file:///b.ts').toJSON(), [{ range: [{ line: 1, character: 1 }, { line: 1, character: 1 }], newText: 'fff' }, { range: [{ line: 0, character: 0 }, { line: 0, character: 0 }], newText: '' }]] + ]); + + edit.set(b, undefined); + assert.ok(edit.has(b)); + assert.equal(edit.size, 2); + + edit.set(b, [types.TextEdit.insert(new types.Position(0, 0), 'ffff')]); + assert.equal(edit.get(b).length, 1); + + }); + + test('DocumentLink', function () { + assert.throws(() => new types.DocumentLink(null, null)); + assert.throws(() => new types.DocumentLink(new types.Range(1, 1, 1, 1), null)); + }); + + test('toJSON & stringify', function () { + + assertToJSON(new types.Selection(3, 4, 2, 1), { start: { line: 2, character: 1 }, end: { line: 3, character: 4 }, anchor: { line: 3, character: 4 }, active: { line: 2, character: 1 } }); + + assertToJSON(new types.Location(URI.file('u.ts'), new types.Position(3, 4)), { uri: URI.parse('file:///u.ts').toJSON(), range: [{ line: 3, character: 4 }, { line: 3, character: 4 }] }); + assertToJSON(new types.Location(URI.file('u.ts'), new types.Range(1, 2, 3, 4)), { uri: URI.parse('file:///u.ts').toJSON(), range: [{ line: 1, character: 2 }, { line: 3, character: 4 }] }); + + let diag = new types.Diagnostic(new types.Range(0, 1, 2, 3), 'hello'); + assertToJSON(diag, { severity: 'Error', message: 'hello', range: [{ line: 0, character: 1 }, { line: 2, character: 3 }] }); + diag.source = 'me'; + assertToJSON(diag, { severity: 'Error', message: 'hello', range: [{ line: 0, character: 1 }, { line: 2, character: 3 }], source: 'me' }); + + assertToJSON(new types.DocumentHighlight(new types.Range(2, 3, 4, 5)), { range: [{ line: 2, character: 3 }, { line: 4, character: 5 }], kind: 'Text' }); + assertToJSON(new types.DocumentHighlight(new types.Range(2, 3, 4, 5), types.DocumentHighlightKind.Read), { range: [{ line: 2, character: 3 }, { line: 4, character: 5 }], kind: 'Read' }); + + assertToJSON(new types.SymbolInformation('test', types.SymbolKind.Boolean, new types.Range(0, 1, 2, 3)), { + name: 'test', + kind: 'Boolean', + location: { + range: [{ line: 0, character: 1 }, { line: 2, character: 3 }] + } + }); + + assertToJSON(new types.CodeLens(new types.Range(7, 8, 9, 10)), { range: [{ line: 7, character: 8 }, { line: 9, character: 10 }] }); + assertToJSON(new types.CodeLens(new types.Range(7, 8, 9, 10), { command: 'id', title: 'title' }), { + range: [{ line: 7, character: 8 }, { line: 9, character: 10 }], + command: { command: 'id', title: 'title' } + }); + + assertToJSON(new types.CompletionItem('complete'), { label: 'complete' }); + + let item = new types.CompletionItem('complete'); + item.kind = types.CompletionItemKind.Interface; + assertToJSON(item, { label: 'complete', kind: 'Interface' }); + + }); + + test('SymbolInformation, old ctor', function () { + + let info = new types.SymbolInformation('foo', types.SymbolKind.Array, new types.Range(1, 1, 2, 3)); + assert.ok(info.location instanceof types.Location); + assert.equal(info.location.uri, undefined); + }); + + test('SnippetString, builder-methods', function () { + + let string: types.SnippetString; + + string = new types.SnippetString(); + assert.equal(string.appendText('I need $ and $').value, 'I need \\$ and \\$'); + + string = new types.SnippetString(); + assert.equal(string.appendText('I need \\$').value, 'I need \\\\\\$'); + + string = new types.SnippetString(); + string.appendPlaceholder('fo$o}'); + assert.equal(string.value, '${1:fo\\$o\\}}'); + + string = new types.SnippetString(); + string.appendText('foo').appendTabstop(0).appendText('bar'); + assert.equal(string.value, 'foo$0bar'); + + string = new types.SnippetString(); + string.appendText('foo').appendTabstop().appendText('bar'); + assert.equal(string.value, 'foo$1bar'); + + string = new types.SnippetString(); + string.appendText('foo').appendTabstop(42).appendText('bar'); + assert.equal(string.value, 'foo$42bar'); + + string = new types.SnippetString(); + string.appendText('foo').appendPlaceholder('farboo').appendText('bar'); + assert.equal(string.value, 'foo${1:farboo}bar'); + + string = new types.SnippetString(); + string.appendText('foo').appendPlaceholder('far$boo').appendText('bar'); + assert.equal(string.value, 'foo${1:far\\$boo}bar'); + + string = new types.SnippetString(); + string.appendText('foo').appendPlaceholder(b => b.appendText('abc').appendPlaceholder('nested')).appendText('bar'); + assert.equal(string.value, 'foo${1:abc${2:nested}}bar'); + + string = new types.SnippetString(); + string.appendVariable('foo'); + assert.equal(string.value, '${foo}'); + + string = new types.SnippetString(); + string.appendText('foo').appendVariable('TM_SELECTED_TEXT').appendText('bar'); + assert.equal(string.value, 'foo${TM_SELECTED_TEXT}bar'); + + string = new types.SnippetString(); + string.appendVariable('BAR', b => b.appendPlaceholder('ops')); + assert.equal(string.value, '${BAR:${1:ops}}'); + + string = new types.SnippetString(); + string.appendVariable('BAR', b => { }); + assert.equal(string.value, '${BAR}'); + + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts new file mode 100644 index 0000000000..1b84c204e4 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; +import { TestThreadService } from './testThreadService'; + +suite('ExtHostWorkspace', function () { + + test('asRelativePath', function () { + + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/Applications/NewsWoWBot')], name: 'Test' }); + + assert.equal(ws.getRelativePath('/Coding/Applications/NewsWoWBot/bernd/das/brot'), 'bernd/das/brot'); + assert.equal(ws.getRelativePath('/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart'), + '/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart'); + + assert.equal(ws.getRelativePath(''), ''); + assert.equal(ws.getRelativePath('/foo/bar'), '/foo/bar'); + assert.equal(ws.getRelativePath('in/out'), 'in/out'); + }); + + test('asRelativePath, same paths, #11402', function () { + const root = '/home/aeschli/workspaces/samples/docker'; + const input = '/home/aeschli/workspaces/samples/docker'; + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file(root)], name: 'Test' }); + + assert.equal(ws.getRelativePath(input), input); + + const input2 = '/home/aeschli/workspaces/samples/docker/a.file'; + assert.equal(ws.getRelativePath(input2), 'a.file'); + }); + + test('asRelativePath, no workspace', function () { + const ws = new ExtHostWorkspace(new TestThreadService(), null); + assert.equal(ws.getRelativePath(''), ''); + assert.equal(ws.getRelativePath('/foo/bar'), '/foo/bar'); + }); + + test('asRelativePath, multiple folders', function () { + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); + assert.equal(ws.getRelativePath('/Coding/One/file.txt'), 'One/file.txt'); + assert.equal(ws.getRelativePath('/Coding/Two/files/out.txt'), 'Two/files/out.txt'); + assert.equal(ws.getRelativePath('/Coding/Two2/files/out.txt'), '/Coding/Two2/files/out.txt'); + }); + + test('slightly inconsistent behaviour of asRelativePath and getWorkspaceFolder, #31553', function () { + const mrws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); + + assert.equal(mrws.getRelativePath('/Coding/One/file.txt'), 'One/file.txt'); + assert.equal(mrws.getRelativePath('/Coding/One/file.txt', true), 'One/file.txt'); + assert.equal(mrws.getRelativePath('/Coding/One/file.txt', false), 'file.txt'); + assert.equal(mrws.getRelativePath('/Coding/Two/files/out.txt'), 'Two/files/out.txt'); + assert.equal(mrws.getRelativePath('/Coding/Two/files/out.txt', true), 'Two/files/out.txt'); + assert.equal(mrws.getRelativePath('/Coding/Two/files/out.txt', false), 'files/out.txt'); + assert.equal(mrws.getRelativePath('/Coding/Two2/files/out.txt'), '/Coding/Two2/files/out.txt'); + assert.equal(mrws.getRelativePath('/Coding/Two2/files/out.txt', true), '/Coding/Two2/files/out.txt'); + assert.equal(mrws.getRelativePath('/Coding/Two2/files/out.txt', false), '/Coding/Two2/files/out.txt'); + + const srws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One')], name: 'Test' }); + assert.equal(srws.getRelativePath('/Coding/One/file.txt'), 'file.txt'); + assert.equal(srws.getRelativePath('/Coding/One/file.txt', false), 'file.txt'); + assert.equal(srws.getRelativePath('/Coding/One/file.txt', true), 'One/file.txt'); + assert.equal(srws.getRelativePath('/Coding/Two2/files/out.txt'), '/Coding/Two2/files/out.txt'); + assert.equal(srws.getRelativePath('/Coding/Two2/files/out.txt', true), '/Coding/Two2/files/out.txt'); + assert.equal(srws.getRelativePath('/Coding/Two2/files/out.txt', false), '/Coding/Two2/files/out.txt'); + }); + + test('getPath, legacy', function () { + let ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [] }); + assert.equal(ws.getPath(), undefined); + + ws = new ExtHostWorkspace(new TestThreadService(), null); + assert.equal(ws.getPath(), undefined); + + ws = new ExtHostWorkspace(new TestThreadService(), undefined); + assert.equal(ws.getPath(), undefined); + + ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [URI.file('Folder'), URI.file('Another/Folder')] }); + assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder'); + + ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [URI.file('/Folder')] }); + assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder'); + }); + + test('WorkspaceFolder has name and index', function () { + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); + + const [one, two] = ws.getWorkspaceFolders(); + + assert.equal(one.name, 'One'); + assert.equal(one.index, 0); + assert.equal(two.name, 'Two'); + assert.equal(two.index, 1); + }); + + test('getContainingWorkspaceFolder', function () { + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two'), URI.file('/Coding/Two/Nested')] }); + + let folder = ws.getWorkspaceFolder(URI.file('/foo/bar')); + assert.equal(folder, undefined); + + folder = ws.getWorkspaceFolder(URI.file('/Coding/One/file/path.txt')); + assert.equal(folder.name, 'One'); + + folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/file/path.txt')); + assert.equal(folder.name, 'Two'); + + folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nest')); + assert.equal(folder.name, 'Two'); + + folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/file')); + assert.equal(folder.name, 'Nested'); + + folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/f')); + assert.equal(folder.name, 'Nested'); + + folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested')); + assert.equal(folder.name, 'Two'); + + folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/')); + assert.equal(folder.name, 'Two'); + + folder = ws.getWorkspaceFolder(URI.file('/Coding/Two')); + assert.equal(folder, undefined); + }); + + test('Multiroot change event should have a delta, #29641', function () { + let ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [] }); + + let sub = ws.onDidChangeWorkspace(e => { + assert.deepEqual(e.added, []); + assert.deepEqual(e.removed, []); + }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [] }); + sub.dispose(); + + sub = ws.onDidChangeWorkspace(e => { + assert.deepEqual(e.removed, []); + assert.equal(e.added.length, 1); + assert.equal(e.added[0].uri.toString(), 'foo:bar'); + }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [URI.parse('foo:bar')] }); + sub.dispose(); + + sub = ws.onDidChangeWorkspace(e => { + assert.deepEqual(e.removed, []); + assert.equal(e.added.length, 1); + assert.equal(e.added[0].uri.toString(), 'foo:bar2'); + }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [URI.parse('foo:bar'), URI.parse('foo:bar2')] }); + sub.dispose(); + + sub = ws.onDidChangeWorkspace(e => { + assert.equal(e.removed.length, 2); + assert.equal(e.removed[0].uri.toString(), 'foo:bar'); + assert.equal(e.removed[1].uri.toString(), 'foo:bar2'); + + assert.equal(e.added.length, 1); + assert.equal(e.added[0].uri.toString(), 'foo:bar3'); + }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [URI.parse('foo:bar3')] }); + sub.dispose(); + + }); + + test('Multiroot change event is immutable', function () { + assert.equal(1, 1); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadCommands.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadCommands.test.ts new file mode 100644 index 0000000000..c789edf22b --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/mainThreadCommands.test.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { OneGetThreadService } from './testThreadService'; + +suite('MainThreadCommands', function () { + + test('dispose on unregister', function () { + + const commands = new MainThreadCommands(OneGetThreadService(null), undefined); + assert.equal(CommandsRegistry.getCommand('foo'), undefined); + + // register + commands.$registerCommand('foo'); + assert.ok(CommandsRegistry.getCommand('foo')); + + // unregister + commands.$unregisterCommand('foo'); + assert.equal(CommandsRegistry.getCommand('foo'), undefined); + }); + + test('unregister all on dispose', function () { + + const commands = new MainThreadCommands(OneGetThreadService(null), undefined); + assert.equal(CommandsRegistry.getCommand('foo'), undefined); + + commands.$registerCommand('foo'); + commands.$registerCommand('bar'); + + assert.ok(CommandsRegistry.getCommand('foo')); + assert.ok(CommandsRegistry.getCommand('bar')); + + commands.dispose(); + + assert.equal(CommandsRegistry.getCommand('foo'), undefined); + assert.equal(CommandsRegistry.getCommand('bar'), undefined); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts new file mode 100644 index 0000000000..199115127a --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts @@ -0,0 +1,228 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import URI from 'vs/base/common/uri'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IConfigurationRegistry, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { MainThreadConfiguration } from 'vs/workbench/api/electron-browser/mainThreadConfiguration'; +import { ConfigurationTarget, IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; +import { OneGetThreadService } from './testThreadService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; + +suite('ExtHostConfiguration', function () { + + let instantiationService: TestInstantiationService; + let target: sinon.SinonSpy; + + suiteSetup(() => { + Registry.as(Extensions.Configuration).registerConfiguration({ + 'id': 'extHostConfiguration', + 'title': 'a', + 'type': 'object', + 'properties': { + 'extHostConfiguration.resource': { + 'description': 'extHostConfiguration.resource', + 'type': 'boolean', + 'default': true, + 'scope': ConfigurationScope.RESOURCE + }, + 'extHostConfiguration.window': { + 'description': 'extHostConfiguration.resource', + 'type': 'boolean', + 'default': true, + 'scope': ConfigurationScope.WINDOW + } + } + }); + }); + + setup(() => { + instantiationService = new TestInstantiationService(); + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + + target = sinon.spy(); + instantiationService.stub(IConfigurationEditingService, ConfigurationEditingService); + instantiationService.stub(IConfigurationEditingService, 'writeConfiguration', target); + }); + + test('update resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('update resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc')); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('update resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('update window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('update window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc')); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('update window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc')); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('update window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('update resource configuration without configuration target defaults to folder', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc')); + + assert.equal(ConfigurationTarget.FOLDER, target.args[0][0]); + }); + + test('update configuration with user configuration target', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(ConfigurationTarget.USER, 'extHostConfiguration.window', 'value', URI.file('abc')); + + assert.equal(ConfigurationTarget.USER, target.args[0][0]); + }); + + test('update configuration with workspace configuration target', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE, 'extHostConfiguration.window', 'value', URI.file('abc')); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('update configuration with folder configuration target', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$updateConfigurationOption(ConfigurationTarget.FOLDER, 'extHostConfiguration.window', 'value', URI.file('abc')); + + assert.equal(ConfigurationTarget.FOLDER, target.args[0][0]); + }); + + test('remove resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('remove resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc')); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('remove resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('remove window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('remove window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc')); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('remove window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc')); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('remove window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null); + + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + }); + + test('remove configuration without configuration target defaults to folder', function () { + instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); + + testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc')); + + assert.equal(ConfigurationTarget.FOLDER, target.args[0][0]); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts new file mode 100644 index 0000000000..19e2b99efe --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { MainThreadDiagnostics } from 'vs/workbench/api/electron-browser/mainThreadDiagnostics'; +import URI from 'vs/base/common/uri'; + + +suite('MainThreadDiagnostics', function () { + + let markerService: MarkerService; + + setup(function () { + markerService = new MarkerService(); + }); + + test('clear markers on dispose', function () { + + let diag = new MainThreadDiagnostics(null, markerService); + + diag.$changeMany('foo', [[URI.file('a'), [{ + code: '666', + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 1, + message: 'fffff', + severity: 1, + source: 'me' + }]]]); + + assert.equal(markerService.read().length, 1); + diag.dispose(); + assert.equal(markerService.read().length, 0); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadDocuments.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadDocuments.test.ts new file mode 100644 index 0000000000..aad783fd06 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/mainThreadDocuments.test.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { BoundModelReferenceCollection } from 'vs/workbench/api/electron-browser/mainThreadDocuments'; +import { Model } from 'vs/editor/common/model/model'; +import { TPromise } from 'vs/base/common/winjs.base'; + +suite('BoundModelReferenceCollection', () => { + + let col = new BoundModelReferenceCollection(15, 75); + + teardown(() => { + col.dispose(); + }); + + test('max age', () => { + + let didDispose = false; + + col.add({ + object: { textEditorModel: Model.createFromString('farboo') }, + dispose() { + didDispose = true; + } + }); + + return TPromise.timeout(30).then(() => { + assert.equal(didDispose, true); + }); + }); + + test('max size', () => { + + let disposed: number[] = []; + + col.add({ + object: { textEditorModel: Model.createFromString('farboo') }, + dispose() { + disposed.push(0); + } + }); + col.add({ + object: { textEditorModel: Model.createFromString('boofar') }, + dispose() { + disposed.push(1); + } + }); + + col.add({ + object: { textEditorModel: Model.createFromString(new Array(71).join('x')) }, + dispose() { + disposed.push(2); + } + }); + + assert.deepEqual(disposed, [0, 1]); + }); + +}); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts new file mode 100644 index 0000000000..2b34f40488 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors'; +import { OneGetThreadService } from './testThreadService'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { MockCodeEditorService } from 'vs/editor/test/common/mocks/mockCodeEditorService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta } from 'vs/workbench/api/node/extHost.protocol'; +import { mockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import Event from 'vs/base/common/event'; + +suite('MainThreadDocumentsAndEditors', () => { + + let modelService: ModelServiceImpl; + let codeEditorService: MockCodeEditorService; + let textFileService: ITextFileService; + let workbenchEditorService: IWorkbenchEditorService; + let documentAndEditor: MainThreadDocumentsAndEditors; + let deltas: IDocumentsAndEditorsDelta[] = []; + const hugeModelString = new Array(2 + (50 * 1024 * 1024)).join('-'); + + setup(() => { + deltas.length = 0; + const configService = new TestConfigurationService(); + configService.setUserConfiguration('editor', { 'detectIndentation': false }); + modelService = new ModelServiceImpl(null, configService); + codeEditorService = new MockCodeEditorService(); + textFileService = new class extends mock() { + isDirty() { return false; }; + models = { + onModelSaved: Event.None, + onModelReverted: Event.None, + onModelDirty: Event.None, + }; + }; + workbenchEditorService = { + getVisibleEditors() { return []; }, + getActiveEditor() { return undefined; } + }; + const editorGroupService = new class extends mock() { + onEditorsChanged = Event.None; + onEditorsMoved = Event.None; + }; + + documentAndEditor = new MainThreadDocumentsAndEditors( + OneGetThreadService(new class extends mock() { + $acceptDocumentsAndEditorsDelta(delta) { deltas.push(delta); } + }), + modelService, + textFileService, + workbenchEditorService, + codeEditorService, + null, + null, + null, + null, + editorGroupService, + null + ); + }); + + + test('Model#add', () => { + deltas.length = 0; + + modelService.createModel('farboo', null, null); + + assert.equal(deltas.length, 1); + const [delta] = deltas; + + assert.equal(delta.addedDocuments.length, 1); + assert.equal(delta.removedDocuments, undefined); + assert.equal(delta.addedEditors, undefined); + assert.equal(delta.removedEditors, undefined); + assert.equal(delta.newActiveEditor, null); + }); + + test('ignore huge model', function () { + this.timeout(1000 * 60); // increase timeout for this one test + + const model = modelService.createModel(hugeModelString, null, null); + assert.ok(model.isTooLargeForHavingARichMode()); + + assert.equal(deltas.length, 1); + const [delta] = deltas; + assert.equal(delta.newActiveEditor, null); + assert.equal(delta.addedDocuments, undefined); + assert.equal(delta.removedDocuments, undefined); + assert.equal(delta.addedEditors, undefined); + assert.equal(delta.removedEditors, undefined); + }); + + test('ignore huge model from editor', function () { + this.timeout(1000 * 60); // increase timeout for this one test + + const model = modelService.createModel(hugeModelString, null, null); + const editor = mockCodeEditor(null, { model, wordWrap: 'off', wordWrapMinified: false }); + + assert.equal(deltas.length, 1); + deltas.length = 0; + codeEditorService.addCodeEditor(editor); + assert.equal(deltas.length, 0); + }); + + test('ignore editor w/o model', () => { + const editor = mockCodeEditor([], {}); + editor.setModel(null); + codeEditorService.addCodeEditor(editor); + assert.equal(deltas.length, 1); + const [delta] = deltas; + assert.equal(delta.newActiveEditor, null); + assert.equal(delta.addedDocuments, undefined); + assert.equal(delta.removedDocuments, undefined); + assert.equal(delta.addedEditors, undefined); + assert.equal(delta.removedEditors, undefined); + }); + + test('editor with model', () => { + deltas.length = 0; + + const model = modelService.createModel('farboo', null, null); + codeEditorService.addCodeEditor(mockCodeEditor(null, { model })); + + assert.equal(deltas.length, 2); + const [first, second] = deltas; + assert.equal(first.addedDocuments.length, 1); + assert.equal(first.newActiveEditor, null); + assert.equal(first.removedDocuments, undefined); + assert.equal(first.addedEditors, undefined); + assert.equal(first.removedEditors, undefined); + + assert.equal(second.addedEditors.length, 1); + assert.equal(second.addedDocuments, undefined); + assert.equal(second.removedDocuments, undefined); + assert.equal(second.removedEditors, undefined); + assert.equal(typeof second.newActiveEditor, 'string'); + }); + + test('editor with dispos-ed/-ing model', () => { + modelService.createModel('foobar', null, null); + const model = modelService.createModel('farboo', null, null); + const editor = mockCodeEditor(null, { model }); + codeEditorService.addCodeEditor(editor); + + // ignore things until now + deltas.length = 0; + + modelService.destroyModel(model.uri); + assert.equal(deltas.length, 1); + const [first] = deltas; + assert.equal(first.newActiveEditor, null); + assert.equal(first.removedEditors.length, 1); + assert.equal(first.removedDocuments.length, 1); + assert.equal(first.addedDocuments, undefined); + assert.equal(first.addedEditors, undefined); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts new file mode 100644 index 0000000000..e58a6457c5 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { FinalNewLineParticipant } from 'vs/workbench/api/electron-browser/mainThreadSaveParticipant'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices'; +import { toResource } from 'vs/base/test/common/utils'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { ITextFileService, SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; + +class ServiceAccessor { + constructor( @ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService) { + } +} + +suite('MainThreadSaveParticipant', function () { + + let instantiationService: IInstantiationService; + let accessor: ServiceAccessor; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + }); + + teardown(() => { + (accessor.textFileService.models).clear(); + TextFileEditorModel.setSaveParticipant(null); // reset any set participant + }); + + test('insert final new line', function (done) { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8'); + + model.load().then(() => { + const configService = new TestConfigurationService(); + configService.setUserConfiguration('files', { 'insertFinalNewline': true }); + + const participant = new FinalNewLineParticipant(configService, undefined); + + // No new line for empty lines + let lineContent = ''; + model.textEditorModel.setValue(lineContent); + participant.participate(model, { reason: SaveReason.EXPLICIT }); + assert.equal(model.getValue(), lineContent); + + // No new line if last line already empty + lineContent = `Hello New Line${model.textEditorModel.getEOL()}`; + model.textEditorModel.setValue(lineContent); + participant.participate(model, { reason: SaveReason.EXPLICIT }); + assert.equal(model.getValue(), lineContent); + + // New empty line added (single line) + lineContent = 'Hello New Line'; + model.textEditorModel.setValue(lineContent); + participant.participate(model, { reason: SaveReason.EXPLICIT }); + assert.equal(model.getValue(), `${lineContent}${model.textEditorModel.getEOL()}`); + + // New empty line added (multi line) + lineContent = `Hello New Line${model.textEditorModel.getEOL()}Hello New Line${model.textEditorModel.getEOL()}Hello New Line`; + model.textEditorModel.setValue(lineContent); + participant.participate(model, { reason: SaveReason.EXPLICIT }); + assert.equal(model.getValue(), `${lineContent}${model.textEditorModel.getEOL()}`); + + done(); + }); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/mock.ts b/src/vs/workbench/test/electron-browser/api/mock.ts new file mode 100644 index 0000000000..91da489345 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/mock.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. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export interface Ctor { + new(): T; +} + +export function mock(): Ctor { + return function () { }; +} diff --git a/src/vs/workbench/test/electron-browser/api/testThreadService.ts b/src/vs/workbench/test/electron-browser/api/testThreadService.ts new file mode 100644 index 0000000000..83b9485995 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/testThreadService.ts @@ -0,0 +1,161 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IThreadService, ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService'; + +export function OneGetThreadService(thing: any): IThreadService { + return { + get(): T { + return thing; + }, + set(identifier: ProxyIdentifier, value: R): R { + return value; + }, + assertRegistered: undefined + }; +} + +declare var Proxy; // TODO@TypeScript + +export abstract class AbstractTestThreadService { + + private _isMain: boolean; + protected _locals: { [id: string]: any; }; + private _proxies: { [id: string]: any; } = Object.create(null); + + constructor(isMain: boolean) { + this._isMain = isMain; + this._locals = Object.create(null); + this._proxies = Object.create(null); + } + + public handle(rpcId: string, methodName: string, args: any[]): any { + if (!this._locals[rpcId]) { + throw new Error('Unknown actor ' + rpcId); + } + let actor = this._locals[rpcId]; + let method = actor[methodName]; + if (typeof method !== 'function') { + throw new Error('Unknown method ' + methodName + ' on actor ' + rpcId); + } + return method.apply(actor, args); + } + + get(identifier: ProxyIdentifier): T { + if (!this._proxies[identifier.id]) { + this._proxies[identifier.id] = this._createProxy(identifier.id); + } + return this._proxies[identifier.id]; + } + + private _createProxy(id: string): T { + let handler = { + get: (target, name) => { + return (...myArgs: any[]) => { + return this._callOnRemote(id, name, myArgs); + }; + } + }; + return new Proxy({}, handler); + } + + set(identifier: ProxyIdentifier, value: R): R { + if (identifier.isMain !== this._isMain) { + throw new Error('Mismatch in object registration!'); + } + this._locals[identifier.id] = value; + return value; + } + + protected abstract _callOnRemote(proxyId: string, path: string, args: any[]): TPromise; +} + +export class TestThreadService extends AbstractTestThreadService implements IThreadService { + constructor() { + super(false); + } + + private _callCountValue: number = 0; + private _idle: TPromise; + private _completeIdle: Function; + + private get _callCount(): number { + return this._callCountValue; + } + + private set _callCount(value: number) { + this._callCountValue = value; + if (this._callCountValue === 0) { + if (this._completeIdle) { + this._completeIdle(); + } + this._idle = undefined; + } + } + + sync(): TPromise { + return new TPromise((c) => { + setTimeout(c, 0); + }).then(() => { + if (this._callCount === 0) { + return undefined; + } + if (!this._idle) { + this._idle = new TPromise((c, e) => { + this._completeIdle = c; + }, function () { + // no cancel + }); + } + return this._idle; + }); + } + + private _testInstances: { [id: string]: any; } = Object.create(null); + setTestInstance(identifier: ProxyIdentifier, value: T): T { + this._testInstances[identifier.id] = value; + return value; + } + + get(identifier: ProxyIdentifier): T { + let id = identifier.id; + if (this._locals[id]) { + return this._locals[id]; + } + return super.get(identifier); + } + + protected _callOnRemote(proxyId: string, path: string, args: any[]): TPromise { + this._callCount++; + + return new TPromise((c) => { + setTimeout(c, 0); + }).then(() => { + const instance = this._testInstances[proxyId]; + let p: Thenable; + try { + let result = (instance[path]).apply(instance, args); + p = TPromise.is(result) ? result : TPromise.as(result); + } catch (err) { + p = TPromise.wrapError(err); + } + + return p.then(result => { + this._callCount--; + return result; + }, err => { + this._callCount--; + return TPromise.wrapError(err); + }); + }); + } + + public assertRegistered(identifiers: ProxyIdentifier[]): void { + throw new Error('Not implemented!'); + } +} diff --git a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts new file mode 100644 index 0000000000..ffa4ede773 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts @@ -0,0 +1,179 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/workbench/parts/search/browser/search.contribution'; // load contributions +import * as assert from 'assert'; +import { IWorkspaceContextService, LegacyWorkspace } from 'vs/platform/workspace/common/workspace'; +import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { ISearchService } from 'vs/platform/search/common/search'; +import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; +import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import * as minimist from 'minimist'; +import * as path from 'path'; +import { QuickOpenHandler, IQuickOpenRegistry, Extensions } from 'vs/workbench/browser/quickopen'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { SearchService } from 'vs/workbench/services/search/node/searchService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { TestEnvironmentService, TestEditorService, TestEditorGroupService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { SimpleConfigurationService } from 'vs/editor/standalone/browser/simpleServices'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { IModelService } from 'vs/editor/common/services/modelService'; + +namespace Timer { + export interface ITimerEvent { + id: number; + topic: string; + name: string; + description: string; + data: any; + + startTime: Date; + stopTime: Date; + + stop(stopTime?: Date): void; + timeTaken(): number; + } +} + +declare var __dirname: string; + +// Checkout sources to run against: +// git clone --separate-git-dir=testGit --no-checkout --single-branch https://chromium.googlesource.com/chromium/src testWorkspace +// cd testWorkspace; git checkout 39a7f93d67f7 +// Run from repository root folder with (test.bat on Windows): ./scripts/test.sh --grep QuickOpen.performance --timeout 180000 --testWorkspace +suite('QuickOpen performance (integration)', () => { + + test('Measure', () => { + if (process.env['VSCODE_PID']) { + return; // TODO@Christoph find out why test fails when run from within VS Code + } + + const n = 3; + const argv = minimist(process.argv); + const testWorkspaceArg = argv['testWorkspace']; + const verboseResults = argv['verboseResults']; + const testWorkspacePath = testWorkspaceArg ? path.resolve(testWorkspaceArg) : __dirname; + + const telemetryService = new TestTelemetryService(); + const configurationService = new SimpleConfigurationService(); + const instantiationService = new InstantiationService(new ServiceCollection( + [ITelemetryService, telemetryService], + [IConfigurationService, configurationService], + [IModelService, new ModelServiceImpl(null, configurationService)], + [IWorkspaceContextService, new TestContextService(new LegacyWorkspace(URI.file(testWorkspacePath)))], + [IWorkbenchEditorService, new TestEditorService()], + [IEditorGroupService, new TestEditorGroupService()], + [IEnvironmentService, TestEnvironmentService], + [IUntitledEditorService, createSyncDescriptor(UntitledEditorService)], + [ISearchService, createSyncDescriptor(SearchService)] + )); + + const registry = Registry.as(Extensions.Quickopen); + const descriptor = registry.getDefaultQuickOpenHandler(); + assert.ok(descriptor); + + function measure() { + return instantiationService.createInstance(descriptor) + .then((handler: QuickOpenHandler) => { + handler.onOpen(); + return handler.getResults('a').then(result => { + const uncachedEvent = popEvent(); + assert.strictEqual(uncachedEvent.data.symbols.fromCache, false, 'symbols.fromCache'); + assert.strictEqual(uncachedEvent.data.files.fromCache, true, 'files.fromCache'); + if (testWorkspaceArg) { + assert.ok(!!uncachedEvent.data.files.joined, 'files.joined'); + } + return uncachedEvent; + }).then(uncachedEvent => { + return handler.getResults('ab').then(result => { + const cachedEvent = popEvent(); + assert.strictEqual(uncachedEvent.data.symbols.fromCache, false, 'symbols.fromCache'); + assert.ok(cachedEvent.data.files.fromCache, 'filesFromCache'); + handler.onClose(false); + return [uncachedEvent, cachedEvent]; + }); + }); + }); + } + + function popEvent() { + const events = telemetryService.events; + assert.strictEqual(events.length, 1); + const event = events[0]; + events.length = 0; + assert.strictEqual(event.name, 'openAnything'); + return event; + } + + function printResult(data: any) { + if (verboseResults) { + console.log(JSON.stringify(data, null, ' ') + ','); + } else { + console.log(JSON.stringify({ + filesfromCacheNotJoined: data.files.fromCache && !data.files.joined, + searchLength: data.searchLength, + sortedResultDuration: data.sortedResultDuration, + filesResultCount: data.files.resultCount, + errorCount: data.files.errors && data.files.errors.length || undefined + }) + ','); + } + } + + return measure() // Warm-up first + .then(() => { + if (testWorkspaceArg || verboseResults) { // Don't measure by default + const cachedEvents: Timer.ITimerEvent[] = []; + let i = n; + return (function iterate(): TPromise { + if (!i--) { + return undefined; + } + return measure() + .then(([uncachedEvent, cachedEvent]) => { + printResult(uncachedEvent.data); + cachedEvents.push(cachedEvent); + return iterate(); + }); + })().then(() => { + console.log(); + cachedEvents.forEach(cachedEvent => { + printResult(cachedEvent.data); + }); + }); + } + return undefined; + }); + }); +}); + +class TestTelemetryService implements ITelemetryService { + + public _serviceBrand: any; + public isOptedIn = true; + + public events: any[] = []; + + public publicLog(eventName: string, data?: any): TPromise { + this.events.push({ name: eventName, data: data }); + return TPromise.as(null); + } + + public getTelemetryInfo(): TPromise { + return TPromise.as({ + instanceId: 'someValue.instanceId', + sessionId: 'someValue.sessionId', + machineId: 'someValue.machineId' + }); + } +}; diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts new file mode 100644 index 0000000000..c920e8255b --- /dev/null +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/workbench/parts/search/browser/search.contribution'; // load contributions +import * as assert from 'assert'; +import * as fs from 'fs'; +import { IWorkspaceContextService, LegacyWorkspace } from 'vs/platform/workspace/common/workspace'; +import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { ISearchService, IQueryOptions } from 'vs/platform/search/common/search'; +import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; +import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import * as minimist from 'minimist'; +import * as path from 'path'; +import { SearchService } from 'vs/workbench/services/search/node/searchService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { TestEnvironmentService, TestEditorService, TestEditorGroupService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { TPromise } from 'vs/base/common/winjs.base'; +import URI from 'vs/base/common/uri'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { SimpleConfigurationService } from 'vs/editor/standalone/browser/simpleServices'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { IModelService } from 'vs/editor/common/services/modelService'; + +import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; +import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; + +import Event, * as event from 'vs/base/common/event'; + +declare var __dirname: string; + +// Checkout sources to run against: +// git clone --separate-git-dir=testGit --no-checkout --single-branch https://chromium.googlesource.com/chromium/src testWorkspace +// cd testWorkspace; git checkout 39a7f93d67f7 +// Run from repository root folder with (test.bat on Windows): ./scripts/test-int-mocha.sh --grep TextSearch.performance --timeout 500000 --testWorkspace +suite('TextSearch performance (integration)', () => { + + test('Measure', () => { + if (process.env['VSCODE_PID']) { + return undefined; // TODO@Rob find out why test fails when run from within VS Code + } + + const n = 3; + const argv = minimist(process.argv); + const testWorkspaceArg = argv['testWorkspace']; + const testWorkspacePath = testWorkspaceArg ? path.resolve(testWorkspaceArg) : __dirname; + if (!fs.existsSync(testWorkspacePath)) { + throw new Error(`--testWorkspace doesn't exist`); + } + + const telemetryService = new TestTelemetryService(); + const configurationService = new SimpleConfigurationService(); + const instantiationService = new InstantiationService(new ServiceCollection( + [ITelemetryService, telemetryService], + [IConfigurationService, configurationService], + [IModelService, new ModelServiceImpl(null, configurationService)], + [IWorkspaceContextService, new TestContextService(new LegacyWorkspace(URI.file(testWorkspacePath)))], + [IWorkbenchEditorService, new TestEditorService()], + [IEditorGroupService, new TestEditorGroupService()], + [IEnvironmentService, TestEnvironmentService], + [IUntitledEditorService, createSyncDescriptor(UntitledEditorService)], + [ISearchService, createSyncDescriptor(SearchService)] + )); + + const queryOptions: IQueryOptions = { + maxResults: 2048 + }; + + const searchModel: SearchModel = instantiationService.createInstance(SearchModel); + function runSearch(): TPromise { + const queryBuilder: QueryBuilder = instantiationService.createInstance(QueryBuilder); + const query = queryBuilder.text({ pattern: 'static_library(' }, [URI.file(testWorkspacePath)], queryOptions); + + // Wait for the 'searchResultsFinished' event, which is fired after the search() promise is resolved + const onSearchResultsFinished = event.filterEvent(telemetryService.eventLogged, e => e.name === 'searchResultsFinished'); + event.once(onSearchResultsFinished)(onComplete); + + function onComplete(): void { + try { + const allEvents = telemetryService.events.map(e => JSON.stringify(e)).join('\n'); + assert.equal(telemetryService.events.length, 3, 'Expected 3 telemetry events, got:\n' + allEvents); + + const [firstRenderEvent, resultsShownEvent, resultsFinishedEvent] = telemetryService.events; + assert.equal(firstRenderEvent.name, 'searchResultsFirstRender'); + assert.equal(resultsShownEvent.name, 'searchResultsShown'); + assert.equal(resultsFinishedEvent.name, 'searchResultsFinished'); + + telemetryService.events = []; + + resolve(resultsFinishedEvent); + } catch (e) { + // Fail the runSearch() promise + error(e); + } + } + + let resolve; + let error; + return new TPromise((_resolve, _error) => { + resolve = _resolve; + error = _error; + + // Don't wait on this promise, we're waiting on the event fired above + searchModel.search(query).then( + null, + _error); + }); + } + + const finishedEvents = []; + return runSearch() // Warm-up first + .then(() => { + if (testWorkspaceArg) { // Don't measure by default + let i = n; + return (function iterate() { + if (!i--) { + return; + } + + return runSearch() + .then((resultsFinishedEvent: any) => { + console.log(`Iteration ${n - i}: ${resultsFinishedEvent.data.duration / 1000}s`); + finishedEvents.push(resultsFinishedEvent); + return iterate(); + }); + })().then(() => { + const totalTime = finishedEvents.reduce((sum, e) => sum + e.data.duration, 0); + console.log(`Avg duration: ${totalTime / n / 1000}s`); + }); + } + }); + }); +}); + +class TestTelemetryService implements ITelemetryService { + public _serviceBrand: any; + public isOptedIn = true; + + public events: any[] = []; + + private emitter = new event.Emitter(); + + public get eventLogged(): Event { + return this.emitter.event; + } + + public publicLog(eventName: string, data?: any): TPromise { + const event = { name: eventName, data: data }; + this.events.push(event); + this.emitter.fire(event); + return TPromise.as(null); + } + + public getTelemetryInfo(): TPromise { + return TPromise.as({ + instanceId: 'someValue.instanceId', + sessionId: 'someValue.sessionId', + machineId: 'someValue.machineId' + }); + } +}; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts new file mode 100644 index 0000000000..ef7483d708 --- /dev/null +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -0,0 +1,1141 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/workbench/parts/files/browser/files.contribution'; // load our contribution into the test +import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; +import { Promise, TPromise } from 'vs/base/common/winjs.base'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { EventEmitter } from 'vs/base/common/eventEmitter'; +import * as paths from 'vs/base/common/paths'; +import URI from 'vs/base/common/uri'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService'; +import { IEditorGroup, ConfirmResult } from 'vs/workbench/common/editor'; +import Event, { Emitter } from 'vs/base/common/event'; +import Severity from 'vs/base/common/severity'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; +import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IEditorInput, IEditorOptions, Position, Direction, IEditor, IResourceInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IMessageService, IConfirmation } from 'vs/platform/message/common/message'; +import { ILegacyWorkspace, IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace } from 'vs/platform/workspace/common/workspace'; +import { ILifecycleService, ShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { EditorStacksModel } from 'vs/workbench/common/editor/editorStacksModel'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { IEditorGroupService, GroupArrangement, GroupOrientation, ITabOptions, IMoveOptions } from 'vs/workbench/services/group/common/groupService'; +import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; +import { FileOperationEvent, IFileService, IResolveContentOptions, FileOperationError, IFileStat, IResolveFileResult, IImportResult, FileChangesEvent, IResolveFileOptions, IContent, IUpdateContentOptions, IStreamContent } from 'vs/platform/files/common/files'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { IRawTextContent, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { parseArgs } from 'vs/platform/environment/node/argv'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { IWindowsService, IWindowService, INativeOpenDialogOptions } from 'vs/platform/windows/common/windows'; +import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; +import { RawTextSource, IRawTextSource } from 'vs/editor/common/model/textSource'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { isLinux } from 'vs/base/common/platform'; +import { generateUuid } from 'vs/base/common/uuid'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IRecentlyOpened } from 'vs/platform/history/common/history'; + +export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { + return instantiationService.createInstance(FileEditorInput, resource, void 0); +} + +export const TestEnvironmentService = new EnvironmentService(parseArgs(process.argv), process.execPath); + +export class TestContextService implements IWorkspaceContextService { + public _serviceBrand: any; + + private workspace: IWorkbenchWorkspace; + private id: string; + private options: any; + + private _onDidChangeWorkspaceName: Emitter; + private _onDidChangeWorkspaceRoots: Emitter; + + constructor(workspace: any = TestWorkspace, options: any = null) { + this.workspace = workspace; + this.id = generateUuid(); + this.options = options || Object.create(null); + this._onDidChangeWorkspaceRoots = new Emitter(); + } + + public get onDidChangeWorkspaceName(): Event { + return this._onDidChangeWorkspaceName.event; + } + + public get onDidChangeWorkspaceRoots(): Event { + return this._onDidChangeWorkspaceRoots.event; + } + + public getFolders(): URI[] { + return this.workspace ? this.workspace.roots : []; + } + + public hasWorkspace(): boolean { + return !!this.workspace; + } + + public hasFolderWorkspace(): boolean { + return this.workspace && !this.workspace.configuration; + } + + public hasMultiFolderWorkspace(): boolean { + return this.workspace && !!this.workspace.configuration; + } + + public getLegacyWorkspace(): ILegacyWorkspace { + return this.workspace ? { resource: this.workspace.roots[0] } : void 0; + } + + public getWorkspace(): IWorkbenchWorkspace { + return this.workspace; + } + + public getRoot(resource: URI): URI { + return this.isInsideWorkspace(resource) ? this.workspace.roots[0] : null; + } + + public setWorkspace(workspace: any): void { + this.workspace = workspace; + } + + public getOptions() { + return this.options; + } + + public updateOptions() { + + } + + public isInsideWorkspace(resource: URI): boolean { + if (resource && this.workspace) { + return paths.isEqualOrParent(resource.fsPath, this.workspace.roots[0].fsPath, !isLinux /* ignorecase */); + } + + return false; + } + + public toResource(workspaceRelativePath: string): URI { + return URI.file(paths.join('C:\\', workspaceRelativePath)); + } +} + +export class TestTextFileService extends TextFileService { + public cleanupBackupsBeforeShutdownCalled: boolean; + + private promptPath: string; + private confirmResult: ConfirmResult; + private resolveTextContentError: FileOperationError; + + constructor( + @ILifecycleService lifecycleService: ILifecycleService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IConfigurationService configurationService: IConfigurationService, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IFileService fileService: IFileService, + @IUntitledEditorService untitledEditorService: IUntitledEditorService, + @IInstantiationService instantiationService: IInstantiationService, + @IMessageService messageService: IMessageService, + @IBackupFileService backupFileService: IBackupFileService, + @IWindowsService windowsService: IWindowsService, + @IHistoryService historyService: IHistoryService + ) { + super(lifecycleService, contextService, configurationService, telemetryService, fileService, untitledEditorService, instantiationService, messageService, TestEnvironmentService, backupFileService, windowsService, historyService); + } + + public setPromptPath(path: string): void { + this.promptPath = path; + } + + public setConfirmResult(result: ConfirmResult): void { + this.confirmResult = result; + } + + public setResolveTextContentErrorOnce(error: FileOperationError): void { + this.resolveTextContentError = error; + } + + public resolveTextContent(resource: URI, options?: IResolveContentOptions): TPromise { + if (this.resolveTextContentError) { + const error = this.resolveTextContentError; + this.resolveTextContentError = null; + + return TPromise.wrapError(error); + } + + return this.fileService.resolveContent(resource, options).then((content) => { + const textSource = RawTextSource.fromString(content.value); + return { + resource: content.resource, + name: content.name, + mtime: content.mtime, + etag: content.etag, + encoding: content.encoding, + value: textSource, + valueLogicalHash: null + }; + }); + } + + public promptForPath(defaultPath?: string): string { + return this.promptPath; + } + + public confirmSave(resources?: URI[]): ConfirmResult { + return this.confirmResult; + } + + public onConfigurationChange(configuration: any): void { + super.onConfigurationChange(configuration); + } + + protected cleanupBackupsBeforeShutdown(): TPromise { + this.cleanupBackupsBeforeShutdownCalled = true; + return TPromise.as(void 0); + } +} + +export function workbenchInstantiationService(): IInstantiationService { + let instantiationService = new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()])); + instantiationService.stub(IWorkspaceContextService, new TestContextService(TestWorkspace)); + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); + instantiationService.stub(IStorageService, new TestStorageService()); + instantiationService.stub(IWorkbenchEditorService, new TestEditorService()); + instantiationService.stub(IPartService, new TestPartService()); + instantiationService.stub(IEditorGroupService, new TestEditorGroupService()); + instantiationService.stub(IModeService, ModeServiceImpl); + instantiationService.stub(IHistoryService, new TestHistoryService()); + instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); + instantiationService.stub(IFileService, new TestFileService()); + instantiationService.stub(IBackupFileService, new TestBackupFileService()); + instantiationService.stub(ITelemetryService, NullTelemetryService); + instantiationService.stub(IMessageService, new TestMessageService()); + instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); + instantiationService.stub(IWindowsService, new TestWindowsService()); + instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + instantiationService.stub(IEnvironmentService, TestEnvironmentService); + instantiationService.stub(IThemeService, new TestThemeService()); + + return instantiationService; +} + +export class TestHistoryService implements IHistoryService { + + public _serviceBrand: any; + + constructor(private root?: URI) { + } + + public reopenLastClosedEditor(): void { + } + + public add(input: IEditorInput, options?: ITextEditorOptions): void { + } + + public forward(acrossEditors?: boolean): void { + } + + public back(acrossEditors?: boolean): void { + } + + public remove(input: IEditorInput | IResourceInput): void { + } + + public clear(): void { + } + + public getHistory(): (IEditorInput | IResourceInput)[] { + return []; + } + + public getLastActiveWorkspaceRoot(): URI { + return this.root; + } +} + +export class TestMessageService implements IMessageService { + public _serviceBrand: any; + + private counter: number; + + constructor() { + this.counter = 0; + } + + public show(sev: Severity, message: any): () => void { + this.counter++; + + return null; + } + + public getCounter() { + return this.counter; + } + + public hideAll(): void { + // No-op + } + + public confirm(confirmation: IConfirmation): boolean { + return false; + } +} + +export class TestPartService implements IPartService { + + public _serviceBrand: any; + + private _onTitleBarVisibilityChange = new Emitter(); + private _onEditorLayout = new Emitter(); + + public get onTitleBarVisibilityChange(): Event { + return this._onTitleBarVisibilityChange.event; + } + + public get onEditorLayout(): Event { + return this._onEditorLayout.event; + } + + public layout(): void { } + + public isCreated(): boolean { + return true; + } + + public joinCreation(): Promise { + return TPromise.as(null); + } + + public hasFocus(part): boolean { + return false; + } + + public isVisible(part): boolean { + return true; + } + + public getContainer(part): HTMLElement { + return null; + } + + public isTitleBarHidden(): boolean { + return false; + } + + public getTitleBarOffset(): number { + return 0; + } + + public isStatusBarHidden(): boolean { + return false; + } + + public isActivityBarHidden(): boolean { + return false; + } + + public setActivityBarHidden(hidden: boolean): void { } + + public isSideBarHidden(): boolean { + return false; + } + + public setSideBarHidden(hidden: boolean): TPromise { return TPromise.as(null); } + + public isPanelHidden(): boolean { + return false; + } + + public setPanelHidden(hidden: boolean): TPromise { return TPromise.as(null); } + + public toggleMaximizedPanel(): void { } + + public isPanelMaximized(): boolean { + return false; + } + + public getSideBarPosition() { + return 0; + } + + public addClass(clazz: string): void { } + public removeClass(clazz: string): void { } + public getWorkbenchElementId(): string { return ''; } + + public toggleZenMode(): void { } + + public resizePart(part: Parts, sizeChange: number): void { } +} + +export class TestStorageService extends EventEmitter implements IStorageService { + public _serviceBrand: any; + + private storage: StorageService; + + constructor() { + super(); + + let context = new TestContextService(); + this.storage = new StorageService(new InMemoryLocalStorage(), null, context.getWorkspace().id); + } + + store(key: string, value: any, scope: StorageScope = StorageScope.GLOBAL): void { + this.storage.store(key, value, scope); + } + + remove(key: string, scope: StorageScope = StorageScope.GLOBAL): void { + this.storage.remove(key, scope); + } + + get(key: string, scope: StorageScope = StorageScope.GLOBAL, defaultValue?: string): string { + return this.storage.get(key, scope, defaultValue); + } + + getInteger(key: string, scope: StorageScope = StorageScope.GLOBAL, defaultValue?: number): number { + return this.storage.getInteger(key, scope, defaultValue); + } + + getBoolean(key: string, scope: StorageScope = StorageScope.GLOBAL, defaultValue?: boolean): boolean { + return this.storage.getBoolean(key, scope, defaultValue); + } +} + +export class TestEditorGroupService implements IEditorGroupService { + public _serviceBrand: any; + + private stacksModel: EditorStacksModel; + + private _onEditorsChanged: Emitter; + private _onEditorOpenFail: Emitter; + private _onEditorsMoved: Emitter; + private _onGroupOrientationChanged: Emitter; + private _onTabOptionsChanged: Emitter; + + constructor(callback?: (method: string) => void) { + this._onEditorsMoved = new Emitter(); + this._onEditorsChanged = new Emitter(); + this._onGroupOrientationChanged = new Emitter(); + this._onEditorOpenFail = new Emitter(); + this._onTabOptionsChanged = new Emitter(); + + let services = new ServiceCollection(); + + services.set(IStorageService, new TestStorageService()); + services.set(IConfigurationService, new TestConfigurationService()); + services.set(IWorkspaceContextService, new TestContextService()); + const lifecycle = new TestLifecycleService(); + services.set(ILifecycleService, lifecycle); + services.set(ITelemetryService, NullTelemetryService); + + let inst = new InstantiationService(services); + + this.stacksModel = inst.createInstance(EditorStacksModel, true); + } + + public fireChange(): void { + this._onEditorsChanged.fire(); + } + + public get onEditorsChanged(): Event { + return this._onEditorsChanged.event; + } + + public get onEditorOpenFail(): Event { + return this._onEditorOpenFail.event; + } + + public get onEditorsMoved(): Event { + return this._onEditorsMoved.event; + } + + public get onGroupOrientationChanged(): Event { + return this._onGroupOrientationChanged.event; + } + + public get onTabOptionsChanged(): Event { + return this._onTabOptionsChanged.event; + } + + public focusGroup(group: IEditorGroup): void; + public focusGroup(position: Position): void; + public focusGroup(arg1: any): void { + + } + + public activateGroup(group: IEditorGroup): void; + public activateGroup(position: Position): void; + public activateGroup(arg1: any): void { + + } + + public moveGroup(from: IEditorGroup, to: IEditorGroup): void; + public moveGroup(from: Position, to: Position): void; + public moveGroup(arg1: any, arg2: any): void { + + } + + public arrangeGroups(arrangement: GroupArrangement): void { + + } + + public setGroupOrientation(orientation: GroupOrientation): void { + + } + + public getGroupOrientation(): GroupOrientation { + return 'vertical'; + } + + public resizeGroup(position: Position, groupSizeChange: number): void { + + } + + public pinEditor(group: IEditorGroup, input: IEditorInput): void; + public pinEditor(position: Position, input: IEditorInput): void; + public pinEditor(arg1: any, input: IEditorInput): void { + } + + public unpinEditor(group: IEditorGroup, input: IEditorInput): void; + public unpinEditor(position: Position, input: IEditorInput): void; + public unpinEditor(arg1: any, input: IEditorInput): void { + } + + public moveEditor(input: IEditorInput, from: IEditorGroup, to: IEditorGroup, moveOptions?: IMoveOptions): void; + public moveEditor(input: IEditorInput, from: Position, to: Position, moveOptions?: IMoveOptions): void; + public moveEditor(input: IEditorInput, from: any, to: any, moveOptions?: IMoveOptions): void { + } + + public getStacksModel(): EditorStacksModel { + return this.stacksModel; + } + + public getTabOptions(): ITabOptions { + return {}; + } +} + +export class TestEditorService implements IWorkbenchEditorService { + public _serviceBrand: any; + + public activeEditorInput: IEditorInput; + public activeEditorOptions: IEditorOptions; + public activeEditorPosition: Position; + public mockLineNumber: number; + + private callback: (method: string) => void; + + constructor(callback?: (method: string) => void) { + this.callback = callback || ((s: string) => { }); + this.mockLineNumber = 15; + } + + public openEditors(inputs): Promise { + return TPromise.as([]); + } + + public replaceEditors(editors): TPromise { + return TPromise.as([]); + } + + public closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise { + return TPromise.as(null); + } + + public closeAllEditors(except?: Position): TPromise { + return TPromise.as(null); + } + + public isVisible(input: IEditorInput, includeDiff: boolean): boolean { + return false; + } + + public getActiveEditor(): IEditor { + this.callback('getActiveEditor'); + + return { + input: null, + options: null, + position: null, + getId: () => { return null; }, + getControl: () => { + return { + getSelection: () => { return { positionLineNumber: this.mockLineNumber }; } + }; + }, + focus: () => { }, + isVisible: () => { return true; } + }; + } + + public getActiveEditorInput(): IEditorInput { + this.callback('getActiveEditorInput'); + + return this.activeEditorInput; + } + + public getVisibleEditors(): IEditor[] { + this.callback('getVisibleEditors'); + + return []; + } + + public openEditor(input: any, options?: any, position?: any): Promise { + this.callback('openEditor'); + + this.activeEditorInput = input; + this.activeEditorOptions = options; + this.activeEditorPosition = position; + + return TPromise.as(null); + } + + public closeEditor(position: Position, input: IEditorInput): TPromise { + this.callback('closeEditor'); + + return TPromise.as(null); + } + + public createInput(input: IResourceInput): IEditorInput { + return null; + } +} + +export class TestFileService implements IFileService { + + public _serviceBrand: any; + + private _onFileChanges: Emitter; + private _onAfterOperation: Emitter; + + private content = 'Hello Html'; + + constructor() { + this._onFileChanges = new Emitter(); + this._onAfterOperation = new Emitter(); + } + + public setContent(content: string): void { + this.content = content; + } + + public getContent(): string { + return this.content; + } + + public get onFileChanges(): Event { + return this._onFileChanges.event; + } + + public fireFileChanges(event: FileChangesEvent): void { + this._onFileChanges.fire(event); + } + + public get onAfterOperation(): Event { + return this._onAfterOperation.event; + } + + public fireAfterOperation(event: FileOperationEvent): void { + this._onAfterOperation.fire(event); + } + + resolveFile(resource: URI, options?: IResolveFileOptions): TPromise { + return TPromise.as({ + resource, + etag: Date.now().toString(), + encoding: 'utf8', + mtime: Date.now(), + isDirectory: false, + hasChildren: false, + name: paths.basename(resource.fsPath) + }); + } + + resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): TPromise { + return TPromise.join(toResolve.map(resourceAndOption => this.resolveFile(resourceAndOption.resource, resourceAndOption.options))).then(stats => stats.map(stat => ({ stat, success: true }))); + } + + existsFile(resource: URI): TPromise { + return TPromise.as(null); + } + + resolveContent(resource: URI, options?: IResolveContentOptions): TPromise { + return TPromise.as({ + resource: resource, + value: this.content, + etag: 'index.txt', + encoding: 'utf8', + mtime: Date.now(), + name: paths.basename(resource.fsPath) + }); + } + + resolveStreamContent(resource: URI, options?: IResolveContentOptions): TPromise { + return TPromise.as({ + resource: resource, + value: { + on: (event: string, callback: Function): void => { + if (event === 'data') { + callback(this.content); + } + if (event === 'end') { + callback(); + } + } + }, + etag: 'index.txt', + encoding: 'utf8', + mtime: Date.now(), + name: paths.basename(resource.fsPath) + }); + } + + updateContent(resource: URI, value: string, options?: IUpdateContentOptions): TPromise { + return TPromise.timeout(1).then(() => { + return { + resource, + etag: 'index.txt', + encoding: 'utf8', + mtime: Date.now(), + isDirectory: false, + hasChildren: false, + name: paths.basename(resource.fsPath) + }; + }); + } + + moveFile(source: URI, target: URI, overwrite?: boolean): TPromise { + return TPromise.as(null); + } + + copyFile(source: URI, target: URI, overwrite?: boolean): TPromise { + return TPromise.as(null); + } + + createFile(resource: URI, content?: string): TPromise { + return TPromise.as(null); + } + + createFolder(resource: URI): TPromise { + return TPromise.as(null); + } + + rename(resource: URI, newName: string): TPromise { + return TPromise.as(null); + } + + touchFile(resource: URI): TPromise { + return TPromise.as(null); + } + + del(resource: URI, useTrash?: boolean): TPromise { + return TPromise.as(null); + } + + importFile(source: URI, targetFolder: URI): TPromise { + return TPromise.as(null); + } + + watchFileChanges(resource: URI): void { + } + + unwatchFileChanges(resource: URI): void { + } + + updateOptions(options: any): void { + } + + getEncoding(resource: URI): string { + return 'utf8'; + } + + dispose(): void { + } +} + +export class TestBackupFileService implements IBackupFileService { + public _serviceBrand: any; + + public backupEnabled: boolean; + + public hasBackups(): TPromise { + return TPromise.as(false); + } + + public hasBackup(resource: URI): TPromise { + return TPromise.as(false); + } + + public loadBackupResource(resource: URI): TPromise { + return this.hasBackup(resource).then(hasBackup => { + if (hasBackup) { + return this.getBackupResource(resource); + } + + return void 0; + }); + } + + public registerResourceForBackup(resource: URI): TPromise { + return TPromise.as(void 0); + } + + public deregisterResourceForBackup(resource: URI): TPromise { + return TPromise.as(void 0); + } + + public getBackupResource(resource: URI): URI { + return null; + } + + public backupResource(resource: URI, content: string): TPromise { + return TPromise.as(void 0); + } + + public getWorkspaceFileBackups(): TPromise { + return TPromise.as([]); + } + + public parseBackupContent(rawText: IRawTextSource): string { + return rawText.lines.join('\n'); + } + + public discardResourceBackup(resource: URI): TPromise { + return TPromise.as(void 0); + } + + public discardAllWorkspaceBackups(): TPromise { + return TPromise.as(void 0); + } +}; + +export class TestWindowService implements IWindowService { + + public _serviceBrand: any; + + onDidChangeFocus: Event; + + isFocused(): TPromise { + return TPromise.as(false); + } + + getCurrentWindowId(): number { + return 0; + } + + pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise { + return TPromise.as(void 0); + } + + pickFileAndOpen(options: INativeOpenDialogOptions): TPromise { + return TPromise.as(void 0); + } + + pickFolderAndOpen(options: INativeOpenDialogOptions): TPromise { + return TPromise.as(void 0); + } + + reloadWindow(): TPromise { + return TPromise.as(void 0); + } + + openDevTools(): TPromise { + return TPromise.as(void 0); + } + + toggleDevTools(): TPromise { + return TPromise.as(void 0); + } + + closeWorkspace(): TPromise { + return TPromise.as(void 0); + } + + openWorkspace(): TPromise { + return TPromise.as(void 0); + } + + createAndOpenWorkspace(folders?: string[], path?: string): TPromise { + return TPromise.as(void 0); + } + + saveAndOpenWorkspace(path: string): TPromise { + return TPromise.as(void 0); + } + + toggleFullScreen(): TPromise { + return TPromise.as(void 0); + } + + setRepresentedFilename(fileName: string): TPromise { + return TPromise.as(void 0); + } + + getRecentlyOpened(): TPromise { + return TPromise.as(void 0); + } + + focusWindow(): TPromise { + return TPromise.as(void 0); + } + + closeWindow(): TPromise { + return TPromise.as(void 0); + } + + setDocumentEdited(flag: boolean): TPromise { + return TPromise.as(void 0); + } + + isMaximized(): TPromise { + return TPromise.as(void 0); + } + + maximizeWindow(): TPromise { + return TPromise.as(void 0); + } + + unmaximizeWindow(): TPromise { + return TPromise.as(void 0); + } + + onWindowTitleDoubleClick(): TPromise { + return TPromise.as(void 0); + } + + show(): TPromise { + return TPromise.as(void 0); + } + + showMessageBox(options: Electron.ShowMessageBoxOptions): number { + return 0; + } + + showSaveDialog(options: Electron.SaveDialogOptions, callback?: (fileName: string) => void): string { + return void 0; + } + + showOpenDialog(options: Electron.OpenDialogOptions, callback?: (fileNames: string[]) => void): string[] { + return void 0; + } +} + +export class TestLifecycleService implements ILifecycleService { + + public _serviceBrand: any; + + public phase: LifecyclePhase; + public startupKind: StartupKind; + + private _onDidChangePhase = new Emitter(); + private _onWillShutdown = new Emitter(); + private _onShutdown = new Emitter(); + + + public fireShutdown(reason = ShutdownReason.QUIT): void { + this._onShutdown.fire(reason); + } + + public fireWillShutdown(event: ShutdownEvent): void { + this._onWillShutdown.fire(event); + } + + public get onDidChangePhase(): Event { + return this._onDidChangePhase.event; + } + + public get onWillShutdown(): Event { + return this._onWillShutdown.event; + } + + public get onShutdown(): Event { + return this._onShutdown.event; + } +} + +export class TestWindowsService implements IWindowsService { + + _serviceBrand: any; + + public windowCount = 1; + + onWindowOpen: Event; + onWindowFocus: Event; + onWindowBlur: Event; + + isFocused(windowId: number): TPromise { + return TPromise.as(false); + } + + pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise { + return TPromise.as(void 0); + } + + pickFileAndOpen(options: INativeOpenDialogOptions): TPromise { + return TPromise.as(void 0); + } + + pickFolderAndOpen(options: INativeOpenDialogOptions): TPromise { + return TPromise.as(void 0); + } + + reloadWindow(windowId: number): TPromise { + return TPromise.as(void 0); + } + + openDevTools(windowId: number): TPromise { + return TPromise.as(void 0); + } + + toggleDevTools(windowId: number): TPromise { + return TPromise.as(void 0); + } + + closeWorkspace(windowId: number): TPromise { + return TPromise.as(void 0); + } + + openWorkspace(windowId: number): TPromise { + return TPromise.as(void 0); + } + + createAndOpenWorkspace(windowId: number, folders?: string[], path?: string): TPromise { + return TPromise.as(void 0); + } + + saveAndOpenWorkspace(windowId: number, path: string): TPromise { + return TPromise.as(void 0); + } + + toggleFullScreen(windowId: number): TPromise { + return TPromise.as(void 0); + } + + setRepresentedFilename(windowId: number, fileName: string): TPromise { + return TPromise.as(void 0); + } + + addRecentlyOpened(files: string[]): TPromise { + return TPromise.as(void 0); + } + + removeFromRecentlyOpened(paths: string[]): TPromise { + return TPromise.as(void 0); + } + + clearRecentlyOpened(): TPromise { + return TPromise.as(void 0); + } + + getRecentlyOpened(windowId: number): TPromise { + return TPromise.as(void 0); + } + + focusWindow(windowId: number): TPromise { + return TPromise.as(void 0); + } + + closeWindow(windowId: number): TPromise { + return TPromise.as(void 0); + } + + isMaximized(windowId: number): TPromise { + return TPromise.as(void 0); + } + + maximizeWindow(windowId: number): TPromise { + return TPromise.as(void 0); + } + + unmaximizeWindow(windowId: number): TPromise { + return TPromise.as(void 0); + } + + onWindowTitleDoubleClick(windowId: number): TPromise { + return TPromise.as(void 0); + } + + setDocumentEdited(windowId: number, flag: boolean): TPromise { + return TPromise.as(void 0); + } + + quit(): TPromise { + return TPromise.as(void 0); + } + + relaunch(options: { addArgs?: string[], removeArgs?: string[] }): TPromise { + return TPromise.as(void 0); + } + + whenSharedProcessReady(): TPromise { + return TPromise.as(void 0); + } + + toggleSharedProcess(): TPromise { + return TPromise.as(void 0); + } + + // Global methods + openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise { + return TPromise.as(void 0); + } + + openNewWindow(): TPromise { + return TPromise.as(void 0); + } + + showWindow(windowId: number): TPromise { + return TPromise.as(void 0); + } + + getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderPath?: string; title: string; filename?: string; }[]> { + return TPromise.as(void 0); + } + + getWindowCount(): TPromise { + return TPromise.as(this.windowCount); + } + + log(severity: string, ...messages: string[]): TPromise { + return TPromise.as(void 0); + } + + showItemInFolder(path: string): TPromise { + return TPromise.as(void 0); + } + + // This needs to be handled from browser process to prevent + // foreground ordering issues on Windows + openExternal(url: string): TPromise { + return TPromise.as(true); + } + + // TODO: this is a bit backwards + startCrashReporter(config: Electron.CrashReporterStartOptions): TPromise { + return TPromise.as(void 0); + } +} + diff --git a/src/vs/workbench/workbench.main.css b/src/vs/workbench/workbench.main.css new file mode 100644 index 0000000000..c8343db9c5 --- /dev/null +++ b/src/vs/workbench/workbench.main.css @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* NOTE: THIS FILE WILL BE OVERWRITTEN DURING BUILD TIME, DO NOT EDIT */ + +div.monaco.main.css { +} \ No newline at end of file diff --git a/src/vs/workbench/workbench.main.nls.js b/src/vs/workbench/workbench.main.nls.js new file mode 100644 index 0000000000..4d6dbb92d5 --- /dev/null +++ b/src/vs/workbench/workbench.main.nls.js @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// NOTE: THIS FILE WILL BE OVERWRITTEN DURING BUILD TIME, DO NOT EDIT + +define([], {}); \ No newline at end of file diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts new file mode 100644 index 0000000000..081eddcb12 --- /dev/null +++ b/src/vs/workbench/workbench.main.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +// Base +import 'vs/base/common/strings'; +import 'vs/base/common/errors'; + +// Editor +import 'vs/editor/editor.all'; + +// Menus/Actions +import 'vs/platform/actions/electron-browser/menusExtensionPoint'; + +// Views +import 'vs/workbench/parts/views/browser/viewsExtensionPoint'; + +// Workbench +import 'vs/workbench/browser/actions/toggleActivityBarVisibility'; +import 'vs/workbench/browser/actions/toggleStatusbarVisibility'; +import 'vs/workbench/browser/actions/toggleSidebarVisibility'; +import 'vs/workbench/browser/actions/toggleSidebarPosition'; +import 'vs/workbench/browser/actions/toggleEditorLayout'; +import 'vs/workbench/browser/actions/toggleZenMode'; +import 'vs/workbench/parts/preferences/browser/preferences.contribution'; +import 'vs/workbench/parts/preferences/browser/keybindingsEditorContribution'; +import 'vs/workbench/browser/actions/configureLocale'; + +import 'vs/workbench/browser/parts/quickopen/quickopen.contribution'; +import 'vs/workbench/parts/quickopen/browser/quickopen.contribution'; +import 'vs/workbench/browser/parts/editor/editorPicker'; + +import 'vs/workbench/parts/files/browser/explorerViewlet'; +import 'vs/workbench/parts/files/browser/fileActions.contribution'; +import 'vs/workbench/parts/files/browser/files.contribution'; + +import 'vs/workbench/parts/backup/common/backup.contribution'; + +import 'vs/workbench/parts/search/browser/search.contribution'; +import 'vs/workbench/parts/search/browser/searchViewlet'; // can be packaged separately +import 'vs/workbench/parts/search/browser/openAnythingHandler'; // can be packaged separately + +import 'vs/workbench/parts/scm/electron-browser/scm.contribution'; +import 'vs/workbench/parts/scm/electron-browser/scmViewlet'; // can be packaged separately + +// {{SQL CARBON EDIT}} +// import 'vs/workbench/parts/debug/electron-browser/debug.contribution'; +// import 'vs/workbench/parts/debug/browser/debugQuickOpen'; +// import 'vs/workbench/parts/debug/electron-browser/repl'; +// import 'vs/workbench/parts/debug/browser/debugEditorActions'; +// import 'vs/workbench/parts/debug/browser/debugViewlet'; // can be packaged separately + +import 'vs/workbench/parts/markers/markers.contribution'; +import 'vs/workbench/parts/markers/browser/markersPanel'; // can be packaged separately + +import 'vs/workbench/parts/html/browser/html.contribution'; + +// {{SQL CARBON EDIT}} +// import 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution'; + +// import 'vs/workbench/parts/extensions/electron-browser/extensions.contribution'; +// import 'vs/workbench/parts/extensions/browser/extensionsQuickOpen'; +// import 'vs/workbench/parts/extensions/electron-browser/extensionsViewlet'; // can be packaged separately + +// import 'vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution'; + +import 'vs/workbench/parts/output/browser/output.contribution'; +import 'vs/workbench/parts/output/browser/outputPanel'; // can be packaged separately + +import 'vs/workbench/parts/terminal/electron-browser/terminal.contribution'; +import 'vs/workbench/parts/terminal/browser/terminalQuickOpen'; +import 'vs/workbench/parts/terminal/electron-browser/terminalPanel'; // can be packaged separately + +import 'vs/workbench/electron-browser/workbench'; + +import 'vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution'; + +import 'vs/workbench/parts/relauncher/electron-browser/relauncher.contribution'; + +import 'vs/workbench/parts/tasks/electron-browser/task.contribution'; + +// {{SQL CARBON EDIT}} +// import 'vs/workbench/parts/emmet/browser/emmet.browser.contribution'; +// import 'vs/workbench/parts/emmet/node/emmet.contribution'; + +// Code Editor enhacements +import 'vs/workbench/parts/codeEditor/codeEditor.contribution'; + +import 'vs/workbench/parts/execution/electron-browser/execution.contribution'; + +import 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; + +import 'vs/workbench/parts/themes/electron-browser/themes.contribution'; + +import 'vs/workbench/parts/feedback/electron-browser/feedback.contribution'; + +// {{SQL CARBON EDIT}} +// import 'vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; + +import 'vs/workbench/parts/update/electron-browser/update.contribution'; + +import 'vs/workbench/parts/surveys/electron-browser/nps.contribution'; +import 'vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution'; + +import 'vs/workbench/parts/performance/electron-browser/performance.contribution'; + +import 'vs/workbench/parts/cli/electron-browser/cli.contribution'; + +import 'vs/workbench/api/electron-browser/extensionHost.contribution'; + +import 'vs/workbench/electron-browser/main.contribution'; +import 'vs/workbench/electron-browser/main'; + +import 'vs/workbench/parts/themes/test/electron-browser/themes.test.contribution'; + +import 'vs/workbench/parts/watermark/electron-browser/watermark'; + +import 'vs/workbench/parts/welcome/overlay/browser/welcomeOverlay'; + +// {{SQL CARBON EDIT}} +// SQL +import 'sql/parts/taskHistory/common/taskHistory.contribution'; +import 'sql/parts/taskHistory/viewlet/taskHistoryViewlet'; +import 'sql/parts/tasks/common/tasks.contribution'; +import 'sql/parts/registeredServer/common/registeredServer.contribution'; +import 'sql/parts/registeredServer/viewlet/connectionViewlet'; +import 'sql/workbench/api/node/sqlExtHost.contribution'; +import 'sql/parts/connection/common/connection.contribution'; +import 'sql/parts/query/common/query.contribution'; +import 'sql/parts/profiler/contrib/profiler.contribution'; +import 'sql/parts/profiler/contrib/profilerActions.contribution'; +import 'sql/parts/registeredServer/serverGroupDialog/serverGroup.contribution'; +/* Insights */ +import 'sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution'; +import 'sql/parts/dashboard/widgets/insights/views/charts/types/doughnutChart.contribution'; +import 'sql/parts/dashboard/widgets/insights/views/charts/types/horizontalBarChart.contribution'; +import 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.contribution'; +import 'sql/parts/dashboard/widgets/insights/views/charts/types/pieChart.contribution'; +import 'sql/parts/dashboard/widgets/insights/views/charts/types/scatterChart.contribution'; +import 'sql/parts/dashboard/widgets/insights/views/charts/types/timeSeriesChart.contribution'; +import 'sql/parts/dashboard/widgets/insights/views/countInsight.contribution'; +import 'sql/parts/dashboard/widgets/insights/views/imageInsight.contribution'; +/* Widgets */ +import 'sql/parts/dashboard/widgets/insights/insightsWidget.contribution'; +import 'sql/parts/dashboard/widgets/explorer/explorerWidget.contribution'; +import 'sql/parts/dashboard/widgets/tasks/tasksWidget.contribution'; +import 'sql/parts/dashboard/dashboardConfig.contribution'; +/* Tasks */ +import 'sql/workbench/common/actions.contribution'; diff --git a/test/OSSREADME.json b/test/OSSREADME.json new file mode 100644 index 0000000000..051e768a62 --- /dev/null +++ b/test/OSSREADME.json @@ -0,0 +1,35 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: + +[{ + "name": "Jxck/assert", + "license": "MIT", + "licenseDetail": [ + "The MIT License (MIT)", + "", + "Copyright (c) 2011 Jxck", + "", + "Originally from node.js (http://nodejs.org)", + "Copyright Joyent, Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "version": "1.0.0", + "repositoryURL": "https://github.com/Jxck/assert", + "description": "The file assert.js in this folder is based on https://github.com/Jxck/assert/blob/master/assert.js.", + "isDev": true +}] diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000000..60860e3f52 --- /dev/null +++ b/test/README.md @@ -0,0 +1,32 @@ +# Tests + +## Run + +The best way to run the Code tests is from the terminal. To make development changes to unit tests you need to be running `gulp`. See [Development Workflow](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#incremental-build) for more details. From the `vscode` folder run: + +**OS X and Linux** + + ./scripts/test.sh + +**Windows** + + scripts\test + + +## Debug + +To debug tests use `--debug` when running the test script. Also, the set of tests can be reduced with the `--run` and `--runGlob` flags. Both require a file path/pattern. Like so: + + ./scripts/test.sh --debug --runGrep **/extHost*.test.js + +## Coverage + +The following command will create a `coverage` folder at the root of the workspace: + +**OS X and Linux** + + ./scripts/test.sh --coverage + +**Windows** + + scripts\test --coverage diff --git a/test/all.js b/test/all.js new file mode 100644 index 0000000000..cb2141be68 --- /dev/null +++ b/test/all.js @@ -0,0 +1,294 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/*eslint-env mocha*/ +/*global define,run*/ + +var assert = require('assert'); +var path = require('path'); +var glob = require('glob'); +var istanbul = require('istanbul'); +var i_remap = require('remap-istanbul/lib/remap'); +var jsdom = require('jsdom-no-contextify'); +var minimatch = require('minimatch'); +var fs = require('fs'); +var vm = require('vm'); +// {{SQL CARBON EDIT}} +var TEST_GLOB = '**/*test*/**/*.test.js'; + +// {{SQL CARBON EDIT}} +var SQL_TEST_GLOB = '**/sqltest/**/*.test.js'; + +var optimist = require('optimist') + .usage('Run the Code tests. All mocha options apply.') + .describe('build', 'Run from out-build').boolean('build') + .describe('run', 'Run a single file').string('run') + .describe('coverage', 'Generate a coverage report').boolean('coverage') + .describe('only-monaco-editor', 'Run only monaco editor tests').boolean('only-monaco-editor') + .describe('forceLoad', 'Force loading').boolean('forceLoad') + .describe('browser', 'Run tests in a browser').boolean('browser') + .alias('h', 'help').boolean('h') + .describe('h', 'Show help'); + +var argv = optimist.argv; + +if (argv.help) { + optimist.showHelp(); + process.exit(1); +} + +var out = argv.build ? 'out-build' : 'out'; +var loader = require('../' + out + '/vs/loader'); +var src = path.join(path.dirname(__dirname), out); + +function main() { + process.on('uncaughtException', function (e) { + console.error(e.stack || e); + }); + + // {{SQL CARBON EDIT}} + var loaderConfig = { + nodeRequire: require, + nodeMain: __filename, + baseUrl: path.join(path.dirname(__dirname), 'src'), + paths: { + 'vs': `../${ out }/vs`, + 'sqltest': `../${ out }/sqltest`, + 'sql': `../${ out }/sql`, + 'lib': `../${ out }/lib`, + 'bootstrap': `../${ out }/bootstrap` + }, + catchError: true, + nodeModules: [ + '@angular/common', + '@angular/core', + '@angular/forms', + '@angular/platform-browser', + '@angular/platform-browser-dynamic', + '@angular/router', + 'angular2-grid', + 'rxjs/add/observable/of', + 'rxjs/Observable', + 'rxjs/Subject', + 'rxjs/Observer' + ] + }; + + + if (argv.coverage) { + var instrumenter = new istanbul.Instrumenter(); + + var seenSources = {}; + + loaderConfig.nodeInstrumenter = function (contents, source) { + seenSources[source] = true; + + if (minimatch(source, SQL_TEST_GLOB)) { + return contents; + } + + return instrumenter.instrumentSync(contents, source); + }; + + process.on('exit', function (code) { + if (code !== 0) { + return; + } + + if (argv.forceLoad) { + // {{SQL CARBON EDIT}} + var allFiles = glob.sync(out + '/sqltest/**/*.js'); + allFiles = allFiles.map(function(source) { + return path.join(__dirname, '..', source); + }); + allFiles = allFiles.filter(function(source) { + if (seenSources[source]) { + return false; + } + + // {{SQL CARBON EDIT}} + if (minimatch(source, SQL_TEST_GLOB)) { + return false; + } + if (/fixtures/.test(source)) { + return false; + } + return true; + }); + allFiles.forEach(function(source, index) { + var contents = fs.readFileSync(source).toString(); + contents = instrumenter.instrumentSync(contents, source); + var stopAt = contents.indexOf('}\n__cov'); + stopAt = contents.indexOf('}\n__cov', stopAt + 1); + + var str = '(function() {' + contents.substr(0, stopAt + 1) + '});'; + var r = vm.runInThisContext(str, source); + r.call(global); + }); + } + + let remapIgnores = /\b((winjs\.base)|(marked)|(raw\.marked)|(nls)|(css))\.js$/; + + var remappedCoverage = i_remap(global.__coverage__, { exclude: remapIgnores }).getFinalCoverage(); + + // The remapped coverage comes out with broken paths + var toUpperDriveLetter = function(str) { + if (/^[a-z]:/.test(str)) { + return str.charAt(0).toUpperCase() + str.substr(1); + } + return str; + }; + var toLowerDriveLetter = function(str) { + if (/^[A-Z]:/.test(str)) { + return str.charAt(0).toLowerCase() + str.substr(1); + } + return str; + }; + + var REPO_PATH = toUpperDriveLetter(path.join(__dirname, '..')); + var fixPath = function(brokenPath) { + var startIndex = brokenPath.indexOf(REPO_PATH); + if (startIndex === -1) { + return toLowerDriveLetter(brokenPath); + } + return toLowerDriveLetter(brokenPath.substr(startIndex)); + }; + + var finalCoverage = {}; + for (var entryKey in remappedCoverage) { + var entry = remappedCoverage[entryKey]; + entry.path = fixPath(entry.path); + // {{SQL CARBON EDIT}} + if (!entry.path.includes('\\vs\\') && !entry.path.includes('/vs/')) { + finalCoverage[fixPath(entryKey)] = entry; + } + } + + var collector = new istanbul.Collector(); + collector.add(finalCoverage); + + var coveragePath = path.join(path.dirname(__dirname), '.build', 'coverage'); + var reportTypes = []; + if (argv.run || argv.runGlob) { + // single file running + coveragePath += '-single'; + reportTypes = ['lcovonly']; + } else { + // {{SQL CARBON EDIT}} + reportTypes = ['json', 'lcov', 'html', 'cobertura']; + } + var reporter = new istanbul.Reporter(null, coveragePath); + reporter.addAll(reportTypes); + reporter.write(collector, true, function () {}); + }); + } + + loader.config(loaderConfig); + + global.define = loader; + global.document = jsdom.jsdom(''); + global.self = global.window = global.document.parentWindow; + + global.Element = global.window.Element; + global.HTMLElement = global.window.HTMLElement; + global.Node = global.window.Node; + global.navigator = global.window.navigator; + global.XMLHttpRequest = global.window.XMLHttpRequest; + // {{SQL CARBON EDIT}} + global.Event = global.window.Event; + + require('reflect-metadata'); + global.window.Reflect = global.Reflect; + global.window.Zone = global.Zone; + + var didErr = false; + var write = process.stderr.write; + process.stderr.write = function (data) { + didErr = didErr || !!data; + write.apply(process.stderr, arguments); + }; + + var loadFunc = null; + + if (argv.runGlob) { + loadFunc = cb => { + const doRun = tests => { + const modulesToLoad = tests.map(test => { + if (path.isAbsolute(test)) { + test = path.relative(src, path.resolve(test)); + } + + return test.replace(/(\.js)|(\.d\.ts)|(\.js\.map)$/, ''); + }); + define(modulesToLoad, () => cb(null), cb); + }; + + glob(argv.runGlob, { cwd: src }, function (err, files) { doRun(files); }); + }; + } else if (argv.run) { + var tests = (typeof argv.run === 'string') ? [argv.run] : argv.run; + var modulesToLoad = tests.map(function(test) { + return path.relative(src, path.resolve(test)).replace(/(\.js)|(\.d\.ts)|(\.js\.map)$/, ''); + }); + loadFunc = cb => { + define(modulesToLoad, () => cb(null), cb); + }; + } else if (argv['only-monaco-editor']) { + loadFunc = function(cb) { + glob(TEST_GLOB, { cwd: src }, function (err, files) { + var modulesToLoad = files.map(function (file) { + return file.replace(/\.js$/, ''); + }); + modulesToLoad = modulesToLoad.filter(function(module) { + if (/^vs\/workbench\//.test(module)) { + return false; + } + // platform tests drag in the workbench. + // see https://github.com/Microsoft/vscode/commit/12eaba2f64c69247de105c3d9c47308ac6e44bc9 + // and cry a little + if (/^vs\/platform\//.test(module)) { + return false; + } + return !/(\/|\\)node(\/|\\)/.test(module); + }); + console.log(JSON.stringify(modulesToLoad, null, '\t')); + define(modulesToLoad, function () { cb(null); }, cb); + }); + }; + } else { + loadFunc = function(cb) { + glob(TEST_GLOB, { cwd: src }, function (err, files) { + var modulesToLoad = files.map(function (file) { + return file.replace(/\.js$/, ''); + }); + define(modulesToLoad, function () { cb(null); }, cb); + }); + }; + } + + loadFunc(function(err) { + if (err) { + console.error(err); + return process.exit(1); + } + + process.stderr.write = write; + + // replace the default unexpected error handler to be useful during tests + loader(['vs/base/common/errors'], function(errors) { + errors.setUnexpectedErrorHandler(function (err) { + }); + + // fire up mocha + run(); + }); + }); +} + +if (process.argv.some(function (a) { return /^--browser/.test(a); })) { + require('./browser'); +} else { + main(); +} \ No newline at end of file diff --git a/test/assert.js b/test/assert.js new file mode 100644 index 0000000000..d588745a28 --- /dev/null +++ b/test/assert.js @@ -0,0 +1,474 @@ +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Copyright (c) 2011 Jxck +// +// Originally from node.js (http://nodejs.org) +// Copyright Joyent, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the 'Software'), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +(function(root, factory) { + if (typeof define === 'function' && define.amd) { + define([], factory); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(); // CommonJS + } else { + root.assert = factory(); // Global + } +})(this, function() { + +// UTILITY + +// Object.create compatible in IE +var create = Object.create || function(p) { + if (!p) throw Error('no type'); + function f() {}; + f.prototype = p; + return new f(); +}; + +// UTILITY +var util = { + inherits: function(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }, + isArray: function(ar) { + return Array.isArray(ar); + }, + isBoolean: function(arg) { + return typeof arg === 'boolean'; + }, + isNull: function(arg) { + return arg === null; + }, + isNullOrUndefined: function(arg) { + return arg == null; + }, + isNumber: function(arg) { + return typeof arg === 'number'; + }, + isString: function(arg) { + return typeof arg === 'string'; + }, + isSymbol: function(arg) { + return typeof arg === 'symbol'; + }, + isUndefined: function(arg) { + return arg === void 0; + }, + isRegExp: function(re) { + return util.isObject(re) && util.objectToString(re) === '[object RegExp]'; + }, + isObject: function(arg) { + return typeof arg === 'object' && arg !== null; + }, + isDate: function(d) { + return util.isObject(d) && util.objectToString(d) === '[object Date]'; + }, + isError: function(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); + }, + isFunction: function(arg) { + return typeof arg === 'function'; + }, + isPrimitive: function(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; + }, + objectToString: function(o) { + return Object.prototype.toString.call(o); + } +}; + +var pSlice = Array.prototype.slice; + +// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys +var Object_keys = typeof Object.keys === 'function' ? Object.keys : (function() { + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; + + return function(obj) { + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + var result = [], prop, i; + + for (prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; +})(); + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = ok; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } else { + // try to throw an error now, and from the stack property + // work out the line that called in to assert.js. + try { + this.stack = (new Error).stack.toString(); + } catch (e) {} + } +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function replacer(key, value) { + if (util.isUndefined(value)) { + return '' + value; + } + if (util.isNumber(value) && (isNaN(value) || !isFinite(value))) { + return value.toString(); + } + if (util.isFunction(value) || util.isRegExp(value)) { + return value.toString(); + } + return value; +} + +function truncate(s, n) { + if (util.isString(s)) { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} + +function getMessage(self) { + return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' + + self.operator + ' ' + + truncate(JSON.stringify(self.expected, replacer), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + // } else if (util.isBuffer(actual) && util.isBuffer(expected)) { + // if (actual.length != expected.length) return false; + // + // for (var i = 0; i < actual.length; i++) { + // if (actual[i] !== expected[i]) return false; + // } + // + // return true; + // + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!util.isObject(actual) && !util.isObject(expected)) { + return actual == expected; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +var isArguments = function(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +}; + +(function() { + if (!isArguments(arguments)) { + isArguments = function(object) { + return object != null && + typeof object === 'object' && + typeof object.callee === 'function' && + typeof object.length === 'number' || false; + }; + } +})(); + +function objEquiv(a, b) { + if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + var aIsArgs = isArguments(a), + bIsArgs = isArguments(b); + if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) + return false; + if (aIsArgs) { + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + try { + var ka = Object_keys(a), + kb = Object_keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key])) return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } else if (actual instanceof expected) { + return true; + } else if (expected.call({}, actual) === true) { + return true; + } + + return false; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (util.isString(expected)) { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + if (!shouldThrow && expectedException(actual, expected)) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function(err) { if (err) {throw err;}}; + +return assert; +}); diff --git a/test/browser.js b/test/browser.js new file mode 100644 index 0000000000..1f2e1d4e04 --- /dev/null +++ b/test/browser.js @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +var express = require('express'); +var glob = require('glob'); +var path = require('path'); +var fs = require('fs'); + +var port = 8887; +var root = path.dirname(__dirname); + +function template(str, env) { + return str.replace(/{{\s*([\w_\-]+)\s*}}/g, function (all, part) { + return env[part]; + }); +} + +var app = express(); + +app.use('/out', express.static(path.join(root, 'out'))); +app.use('/test', express.static(path.join(root, 'test'))); +app.use('/node_modules', express.static(path.join(root, 'node_modules'))); + +app.get('/', function (req, res) { + glob('**/vs/{base,platform,editor}/**/test/{common,browser}/**/*.test.js', { + cwd: path.join(root, 'out'), + // ignore: ['**/test/{node,electron*}/**/*.js'] + }, function (err, files) { + if (err) { return res.sendStatus(500); } + + var modules = files + .map(function (file) { return file.replace(/\.js$/, ''); }); + + fs.readFile(path.join(__dirname, 'index.html'), 'utf8', function (err, templateString) { + if (err) { return res.sendStatus(500); } + + res.send(template(templateString, { + modules: JSON.stringify(modules) + })); + }); + }); +}); + +app.listen(port, function () { + console.log('http://localhost:8887/'); +}); \ No newline at end of file diff --git a/test/electron/index.js b/test/electron/index.js new file mode 100644 index 0000000000..aa02f48f9f --- /dev/null +++ b/test/electron/index.js @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const { app, BrowserWindow, ipcMain } = require('electron'); +const { tmpdir } = require('os'); +const { join } = require('path'); +const path = require('path'); +const mocha = require('mocha'); +const events = require('events'); + +const optimist = require('optimist') + .describe('grep', 'only run tests matching ').alias('grep', 'g').alias('grep', 'f').string('grep') + .describe('run', 'only run tests from ').string('run') + .describe('runGlob', 'only run tests matching ').alias('runGlob', 'runGrep').string('runGlob') + .describe('build', 'run with build output (out-build)').boolean('build') + .describe('coverage', 'generate coverage report').boolean('coverage') + .describe('debug', 'open dev tools, keep window open, reuse app data').string('debug') + .describe('reporter', 'the mocha reporter').string('reporter').default('reporter', process.platform === 'win32' ? 'dot' : 'spec') + .describe('help', 'show the help').alias('help', 'h'); + +const argv = optimist.argv; + +if (argv.help) { + optimist.showHelp(); + process.exit(0); +} + +if (!argv.debug) { + app.setPath('userData', join(tmpdir(), `vscode-tests-${Date.now()}`)); +} + +function deserializeSuite(suite) { + return { + title: suite.title, + fullTitle: () => suite.fullTitle, + timeout: () => suite.timeout, + retries: () => suite.retries, + enableTimeouts: () => suite.enableTimeouts, + slow: () => suite.slow, + bail: () => suite.bail, + }; +} + +function deserializeRunnable(runnable) { + return { + title: runnable.title, + fullTitle: () => runnable.fullTitle, + async: runnable.async, + slow: () => runnable.slow, + speed: runnable.speed, + duration: runnable.duration + }; +} + +function deserializeError(err) { + const inspect = err.inspect; + err.inspect = () => inspect; + return err; +} + +class IPCRunner extends events.EventEmitter { + + constructor() { + super(); + + this.didFail = false; + + ipcMain.on('start', () => this.emit('start')); + ipcMain.on('end', () => this.emit('end')); + ipcMain.on('suite', (e, suite) => this.emit('suite', deserializeSuite(suite))); + ipcMain.on('suite end', (e, suite) => this.emit('suite end', deserializeSuite(suite))); + ipcMain.on('test', (e, test) => this.emit('test', deserializeRunnable(test))); + ipcMain.on('test end', (e, test) => this.emit('test end', deserializeRunnable(test))); + ipcMain.on('hook', (e, hook) => this.emit('hook', deserializeRunnable(hook))); + ipcMain.on('hook end', (e, hook) => this.emit('hook end', deserializeRunnable(hook))); + ipcMain.on('pass', (e, test) => this.emit('pass', deserializeRunnable(test))); + ipcMain.on('fail', (e, test, err) => { + this.didFail = true; + this.emit('fail', deserializeRunnable(test), deserializeError(err)); + }); + ipcMain.on('pending', (e, test) => this.emit('pending', deserializeRunnable(test))); + } +} + +app.on('ready', () => { + + const win = new BrowserWindow({ + height: 600, + width: 800, + show: false, + webPreferences: { + backgroundThrottling: false, + webSecurity: false + } + }); + + win.webContents.on('did-finish-load', () => { + if (argv.debug) { + win.show(); + win.webContents.openDevTools('right'); + } + win.webContents.send('run', argv); + }); + + win.loadURL(`file://${__dirname}/renderer.html`); + + const reporterPath = path.join(path.dirname(require.resolve('mocha')), 'lib', 'reporters', argv.reporter); + let Reporter; + + try { + Reporter = require(reporterPath); + } catch (err) { + console.warn(`could not load reporter: ${argv.reporter}`); + Reporter = process.platform === 'win32' ? mocha.reporters.Dot : mocha.reporters.Spec; + } + + const runner = new IPCRunner(); + new Reporter(runner); + + if (!argv.debug) { + ipcMain.on('all done', () => app.exit(runner.didFail ? 1 : 0)); + } +}); diff --git a/test/electron/renderer.html b/test/electron/renderer.html new file mode 100644 index 0000000000..0c94643deb --- /dev/null +++ b/test/electron/renderer.html @@ -0,0 +1,19 @@ + + + + + VSCode Tests + + + + +
+ + + + + + \ No newline at end of file diff --git a/test/electron/renderer.js b/test/electron/renderer.js new file mode 100644 index 0000000000..579ce26a9b --- /dev/null +++ b/test/electron/renderer.js @@ -0,0 +1,265 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/*eslint-env mocha*/ + +const { ipcRenderer } = require('electron'); +const assert = require('assert'); +const path = require('path'); +const glob = require('glob'); +const minimatch = require('minimatch'); +const istanbul = require('istanbul'); +const i_remap = require('remap-istanbul/lib/remap'); + +let _tests_glob = '**/test/**/*.test.js'; +let loader; +let _out; + +function initLoader(opts) { + let outdir = opts.build ? 'out-build' : 'out'; + _out = path.join(__dirname, `../../${outdir}`); + + // setup loader + loader = require(`${_out}/vs/loader`); + const loaderConfig = { + nodeRequire: require, + nodeMain: __filename, + catchError: true, + baseUrl: path.join(__dirname, '../../src'), + paths: { + 'vs': `../${outdir}/vs`, + 'lib': `../${outdir}/lib`, + 'bootstrap': `../${outdir}/bootstrap` + } + }; + + // nodeInstrumenter when coverage is requested + if (opts.coverage) { + const instrumenter = new istanbul.Instrumenter(); + + loaderConfig.nodeInstrumenter = function (contents, source) { + return minimatch(source, _tests_glob) + ? contents // don't instrument tests itself + : instrumenter.instrumentSync(contents, source); + }; + } + + loader.require.config(loaderConfig); +} + +function createCoverageReport(opts) { + return new Promise(resolve => { + + if (!opts.coverage) { + return resolve(undefined); + } + + const exclude = /\b((winjs\.base)|(marked)|(raw\.marked)|(nls)|(css))\.js$/; + const remappedCoverage = i_remap(global.__coverage__, { exclude: exclude }).getFinalCoverage(); + + // The remapped coverage comes out with broken paths + function toUpperDriveLetter(str) { + if (/^[a-z]:/.test(str)) { + return str.charAt(0).toUpperCase() + str.substr(1); + } + return str; + } + function toLowerDriveLetter(str) { + if (/^[A-Z]:/.test(str)) { + return str.charAt(0).toLowerCase() + str.substr(1); + } + return str; + } + + const REPO_PATH = toUpperDriveLetter(path.join(__dirname, '../..')); + const fixPath = function (brokenPath) { + const startIndex = brokenPath.indexOf(REPO_PATH); + if (startIndex === -1) { + return toLowerDriveLetter(brokenPath); + } + return toLowerDriveLetter(brokenPath.substr(startIndex)); + }; + + const finalCoverage = Object.create(null); + for (const entryKey in remappedCoverage) { + const entry = remappedCoverage[entryKey]; + entry.path = fixPath(entry.path); + finalCoverage[fixPath(entryKey)] = entry; + } + + const collector = new istanbul.Collector(); + collector.add(finalCoverage); + + let coveragePath = path.join(path.dirname(__dirname), '../.build/coverage'); + let reportTypes = []; + if (opts.run || opts.runGlob) { + // single file running + coveragePath += '-single'; + reportTypes = ['lcovonly']; + } else { + reportTypes = ['json', 'lcov', 'html']; + } + + const reporter = new istanbul.Reporter(null, coveragePath); + reporter.addAll(reportTypes); + reporter.write(collector, true, resolve); + }); +} + +function loadTestModules(opts) { + + if (opts.run) { + const files = Array.isArray(opts.run) ? opts.run : [opts.run]; + const modules = files.map(file => { + return path.relative(_out, file).replace(/\.js$/, ''); + }); + return new Promise((resolve, reject) => { + loader.require(modules, resolve, reject); + }); + } + + const pattern = opts.runGlob || _tests_glob; + + return new Promise((resolve, reject) => { + glob(pattern, { cwd: _out }, (err, files) => { + if (err) { + reject(err); + return; + } + const modules = files.map(file => file.replace(/\.js$/, '')); + resolve(modules); + }); + }).then(modules => { + return new Promise((resolve, reject) => { + loader.require(modules, resolve, reject); + }); + }); +} + +function loadTests(opts) { + + const _unexpectedErrors = []; + const _loaderErrors = []; + + // collect loader errors + loader.require.config({ + onError(err) { + _loaderErrors.push(err); + console.error(err); + } + }); + + // collect unexpected errors + loader.require(['vs/base/common/errors'], function (errors) { + errors.setUnexpectedErrorHandler(function (err) { + let stack = (err ? err.stack : null); + if (!stack) { + stack = new Error().stack; + } + + _unexpectedErrors.push((err && err.message ? err.message : err) + '\n' + stack); + }); + }); + + return loadTestModules(opts).then(() => { + suite('Unexpected Errors & Loader Errors', function () { + test('should not have unexpected errors', function () { + const errors = _unexpectedErrors.concat(_loaderErrors); + if (errors.length) { + errors.forEach(function (stack) { + console.error(''); + console.error(stack); + }); + assert.ok(false, errors); + } + }); + }); + }); +} + +function serializeSuite(suite) { + return { + title: suite.title, + fullTitle: suite.fullTitle(), + timeout: suite.timeout(), + retries: suite.retries(), + enableTimeouts: suite.enableTimeouts(), + slow: suite.slow(), + bail: suite.bail() + }; +} + +function serializeRunnable(runnable) { + return { + title: runnable.title, + fullTitle: runnable.fullTitle(), + async: runnable.async, + slow: runnable.slow(), + speed: runnable.speed, + duration: runnable.duration + }; +} + +function serializeError(err) { + return { + message: err.message, + stack: err.stack, + actual: err.actual, + expected: err.expected, + uncaught: err.uncaught, + showDiff: err.showDiff, + inspect: typeof err.inspect === 'function' ? err.inspect() : '' + }; +} + +class IPCReporter { + + constructor(runner) { + runner.on('start', () => ipcRenderer.send('start')); + runner.on('end', () => ipcRenderer.send('end')); + runner.on('suite', suite => ipcRenderer.send('suite', serializeSuite(suite))); + runner.on('suite end', suite => ipcRenderer.send('suite end', serializeSuite(suite))); + runner.on('test', test => ipcRenderer.send('test', serializeRunnable(test))); + runner.on('test end', test => ipcRenderer.send('test end', serializeRunnable(test))); + runner.on('hook', hook => ipcRenderer.send('hook', serializeRunnable(hook))); + runner.on('hook end', hook => ipcRenderer.send('hook end', serializeRunnable(hook))); + runner.on('pass', test => ipcRenderer.send('pass', serializeRunnable(test))); + runner.on('fail', (test, err) => ipcRenderer.send('fail', serializeRunnable(test), serializeError(err))); + runner.on('pending', test => ipcRenderer.send('pending', serializeRunnable(test))); + } +} + +function runTests(opts) { + + return loadTests(opts).then(() => { + + if (opts.grep) { + mocha.grep(opts.grep); + } + + if (!opts.debug) { + mocha.reporter(IPCReporter); + } + + const runner = mocha.run(() => { + createCoverageReport(opts).then(() => { + ipcRenderer.send('all done'); + }); + }); + + if (opts.debug) { + runner.on('fail', (test, err) => { + + console.error(test.fullTitle()); + console.error(err.stack); + }); + } + }); +} + +ipcRenderer.on('run', (e, opts) => { + initLoader(opts); + runTests(opts).catch(err => console.error(err)); +}); diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000000..785f4e3abc --- /dev/null +++ b/test/index.html @@ -0,0 +1,31 @@ + + + + VSCode Tests + + + +
+ + + + + + + + \ No newline at end of file diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000000..adec072363 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,5 @@ +--delay +--ui tdd +--timeout 10000 +test/all.js + diff --git a/test/smoke/.gitignore b/test/smoke/.gitignore new file mode 100644 index 0000000000..532798d3ea --- /dev/null +++ b/test/smoke/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +npm-debug.log +Thumbs.db +node_modules/ +out/ +keybindings.*.json +test_data/ \ No newline at end of file diff --git a/test/smoke/Audit.md b/test/smoke/Audit.md new file mode 100644 index 0000000000..2503fcdc35 --- /dev/null +++ b/test/smoke/Audit.md @@ -0,0 +1,13 @@ +# VS Code Smoke Tests Failures History +This file contains a history of smoke test failures which could be avoided if particular techniques were used in the test (e.g. binding test elements with HTML5 `data-*` attribute). + +To better understand what can be employed in smoke test to ensure its stability, it is important to understand patterns that led to smoke test breakage. This markdown is a result of work on [this issue](https://github.com/Microsoft/vscode/issues/27906). + +# Log +1. This following change led to the smoke test failure because DOM element's attribute `a[title]` was changed: + [eac49a3](https://github.com/Microsoft/vscode/commit/eac49a321b84cb9828430e9dcd3f34243a3480f7) + + This attribute was used in the smoke test to grab the contents of SCM part in status bar: + [0aec2d6](https://github.com/Microsoft/vscode/commit/0aec2d6838b5e65cc74c33b853ffbd9fa191d636) + +2. To be continued... \ No newline at end of file diff --git a/test/smoke/README.md b/test/smoke/README.md new file mode 100644 index 0000000000..02717c281f --- /dev/null +++ b/test/smoke/README.md @@ -0,0 +1,64 @@ +# VS Code Smoke Testing + +- Run `npm install` +- Start the tests: `npm test -- --latest "path/to/binary"`. + +If you want to include 'Data Migration' area tests use `npm test -- --latest path/to/binary --stable path/to/currentStable` respectively. + +Detailed prerequisites and running steps are described [in our smoke test wiki](https://github.com/Microsoft/vscode/wiki/Smoke-Test#automated-smoke-test). + +# Architecture +* `main.js` is used to prepare all smoke test dependencies (fetching key bindings and 'Express' repository, running `npm install` there). +* `mocha-runner.js` launches Mocha programmatically. It is spawned in Node environment from main.js to ensure that it is possible to listen on `stderr`s (primary `process.stderr` is not readable otherwise). This is accomplished because WebDriverIO command deprecation warnings need to be redirected to a separate log. Those warnings are coming from WebDriverIO because ChromeDriver has not migrated from JsonWire to W3C WebDriver protocol. +* `test.ts` contains the main smoke test suite calling the tests that are bundled in areas and defined in `./tests/`. It includes all tests separated into mocha `describe()` groups that represent each of the areas of [Smoke Test document](https://github.com/Microsoft/vscode/wiki/Smoke-Test). + +* `./areas/` folder contains a `.ts` file per each area of the document. E.g. `'Search'` area goes under `'search.ts'`. Every area file contains a list of methods with the name that represents the action that can be performed in the corresponding test. This reduces the amount of test suite code and means that if the UI changes, the fix need only be applied in one place. The name of the method reflects the action the tester would do if he would perform the test manually. See [Selenium Page Objects Wiki](https://github.com/SeleniumHQ/selenium/wiki/PageObjects) and [Selenium Bot Style Tests Wiki](https://github.com/SeleniumHQ/selenium/wiki/Bot-Style-Tests) for a good explanation of the implementation. Every smoke test area contains methods that are used in a bot-style approach in `main.ts`. +* `./spectron/` wraps the Spectron, with WebDriverIO API wrapped in `client.ts` and instance of Spectron Application is wrapped in `application.ts`. + +* `./test_data/` folder contains temporary data used by smoke test (cloned express repository, temporary user-data-dir/extensions-dir). +* `./test_data/screenshots` has action screenshots captured by a smoke test when performing actions during runtime. Screenshots are split in folders per each test. + +# Adding new area +To contribute a new smoke test area, add `${area}.ts` file under `./areas/`. All related tests to the area should go to the alike named file under `./tests/${area}.ts`. This has to follow the bot-style approach described in the links mentioned above. Methods should be calling WebDriverIO API through `SpectronClient` class. If there is no existing WebDriverIO method, add it to the class. + +# Adding new test +To add new test, `./test/${area}.ts` should be updated. The same instruction-style principle needs to be followed with the called area method names that reflect manual tester's actions. + +# Debugging +1. Add the following configuration to launch.json, specifying binaries in `args`: +```json +{ + "type": "node", + "request": "launch", + "name": "Launch Smoke Test", + "program": "${workspaceRoot}/test/smoke/out/main.js", + "cwd": "${workspaceRoot}/test/smoke", + "timeout": 240000, + "port": 9999, + "args": [ + "-l", + "path/to/Code.exe" + ], + "outFiles": [ + "${cwd}/out/**/*.js" + ] +} +``` +2. In main.js add `--debug-brk=9999` as a first argument to the place where `out/mocha-runner.js` is spawned. + +# Screenshots +Almost on every automated test action it captures a screenshot. These help to determine an issue, if smoke test fails. The normal workflow is that you understand what code is doing and then try to match it up with screenshots obtained from the test. + +# Running "Out of Sources" +If you did a fix in VS Code that you need in order for the smoke test to succeed, here is how you can run the smoke test against the sources of VS Code: +* Set related environment variables in the console: + * `export NODE_ENV=development` + * `export VSCODE_DEV=1` + * `export VSCODE_CLI=1` +* open `application.ts` + * pass in the vscode folder as argument to the application + * e.g. instead of `args: args` type `args: ['/Users/bpasero/Development/vscode', ...args]` +* `cd test/smoke` +* `npm install` +* `npm test -- --latest ` + * e.g. on macOS: `npm test -- --latest /.build/electron/Code\ -\ OSS.app/Contents/MacOS/Electron` \ No newline at end of file diff --git a/test/smoke/package.json b/test/smoke/package.json new file mode 100644 index 0000000000..b5d425c29a --- /dev/null +++ b/test/smoke/package.json @@ -0,0 +1,26 @@ +{ + "name": "code-oss-dev-smoke-test", + "version": "0.1.0", + "main": "./src/main.js", + "scripts": { + "compile": "tsc", + "pretest": "tsc", + "test": "node out/main.js" + }, + "devDependencies": { + "@types/mocha": "^2.2.41", + "@types/node": "^6.0.70", + "@types/webdriverio": "^4.6.1", + "@types/electron": "~1.4.37", + "@types/rimraf": "^0.0.28", + "@types/htmlparser2": "^3.7.29", + "mocha": "^3.2.0", + "spectron": "~3.6.4", + "typescript": "^2.2.2", + "rimraf": "^2.6.1", + "commander": "^2.9.0", + "simple-git": "^1.73.0", + "strip-json-comments": "^2.0.1", + "htmlparser2": "^3.9.2" + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/common.ts b/test/smoke/src/areas/common.ts new file mode 100644 index 0000000000..d5b625cff2 --- /dev/null +++ b/test/smoke/src/areas/common.ts @@ -0,0 +1,188 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; +import { Util } from '../helpers/utilities'; + +/** + * Contains methods that are commonly used across test areas. + */ +export class CommonActions { + private util: Util; + + constructor(private spectron: SpectronApplication) { + this.util = new Util(); + } + + public async getWindowTitle(): Promise { + return this.spectron.client.getTitle(); + } + + public enter(): Promise { + return this.spectron.client.keys(['Enter', 'NULL']); + } + + public async addSetting(setting: string, value: string): Promise { + await this.spectron.command('workbench.action.openGlobalSettings'); + await this.spectron.wait(); + await this.spectron.client.keys(['ArrowDown', 'NULL', 'ArrowRight', 'NULL'], false); + await this.spectron.client.keys(`"${setting}": "${value}"`); + await this.spectron.wait(); + return this.saveOpenedFile(); + } + + public async newUntitledFile(): Promise { + await this.spectron.command('workbench.action.files.newUntitledFile'); + return this.spectron.wait(); + } + + public closeTab(): Promise { + return this.spectron.client.keys(['Control', 'w', 'NULL']); + } + + public async getTab(tabName: string, active?: boolean): Promise { + await this.closeCurrentNotification(); // close any notification messages that could overlap tabs + + let tabSelector = active ? '.tab.active' : 'div'; + let el = await this.spectron.client.element(`.tabs-container ${tabSelector}[aria-label="${tabName}, tab"]`); + if (el.status === 0) { + return el; + } + + return undefined; + } + + public async selectTab(tabName: string): Promise { + await this.closeCurrentNotification(); // close any notification messages that could overlap tabs + return this.spectron.client.click(`.tabs-container div[aria-label="${tabName}, tab"]`); + } + + public async openFirstMatchFile(fileName: string): Promise { + await this.openQuickOpen(); + await this.type(fileName); + await this.spectron.wait(); + await this.enter(); + return this.spectron.wait(); + } + + public saveOpenedFile(): Promise { + return this.spectron.command('workbench.action.files.save'); + } + + public type(text: string): Promise { + let spectron = this.spectron; + + return new Promise(function (res) { + let textSplit = text.split(' '); + + async function type(i: number) { + if (!textSplit[i] || textSplit[i].length <= 0) { + return res(); + } + + const toType = textSplit[i + 1] ? `${textSplit[i]} ` : textSplit[i]; + await spectron.client.keys(toType, false); + await spectron.client.keys(['NULL']); + await type(i + 1); + } + + return type(0); + }); + } + + public showCommands(): Promise { + return this.spectron.command('workbench.action.showCommands'); + } + + public openQuickOpen(): Promise { + return this.spectron.command('workbench.action.quickOpen'); + } + + public closeQuickOpen(): Promise { + return this.spectron.command('workbench.action.closeQuickOpen'); + } + + public selectNextQuickOpenElement(): Promise { + return this.spectron.client.keys(['ArrowDown', 'NULL']); + } + + public async getQuickOpenElements(): Promise { + const elements = await this.spectron.waitFor(this.spectron.client.elements, 'div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties .monaco-tree-row'); + return elements.value.length; + } + + public async openFile(fileName: string, explorer?: boolean): Promise { + let selector = `div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.getExtensionSelector(fileName)}`; + if (explorer) { + selector += ' explorer-item'; + } + selector += '"]'; + + try { + await this.spectron.waitFor(this.spectron.client.doubleClick, selector); + } catch (e) { + return Promise.reject(`Cannot fine ${fileName} in a viewlet.`); + } + + return this.spectron.wait(); + } + + public getExtensionSelector(fileName: string): string { + const extension = fileName.split('.')[1]; + if (extension === 'js') { + return 'js-ext-file-icon javascript-lang-file-icon'; + } else if (extension === 'json') { + return 'json-ext-file-icon json-lang-file-icon'; + } else if (extension === 'md') { + return 'md-ext-file-icon markdown-lang-file-icon'; + } + + throw new Error('No class defined for this file extension'); + } + + public async getEditorFirstLinePlainText(): Promise { + const trials = 3; + let retry = 0; + let error; + + while (retry < trials) { + try { + const span = await this.spectron.client.getText('.view-lines span span'); + if (Array.isArray(span)) { + return span[0]; + } + + return span; + } catch (e) { + error = e; + retry++; + + if (retry < trials) { + await this.spectron.wait(); + } else { + error = e; + } + } + } + + return Promise.reject('Could not obtain text on the first line of an editor: ' + error); + } + + public removeFile(filePath: string): void { + this.util.removeFile(filePath); + } + + public removeDirectory(directory: string): Promise { + try { + return this.util.rimraf(directory); + } catch (e) { + throw new Error(`Failed to remove ${directory} with an error: ${e}`); + } + } + + private closeCurrentNotification(): Promise { + return this.spectron.command('workbench.action.closeMessages'); + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/configuration-views.ts b/test/smoke/src/areas/configuration-views.ts new file mode 100644 index 0000000000..5994819eed --- /dev/null +++ b/test/smoke/src/areas/configuration-views.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; + +export enum ActivityBarPosition { + LEFT = 0, + RIGHT = 1 +}; + +export class ConfigurationView { + // Stores key binding defined for the toggle of activity bar position + private keybinding: string[]; + + constructor(private spectron: SpectronApplication) { + // noop + } + + public async getEditorLineNumbers(): Promise { + const lineNumbers = await this.spectron.client.elements('.line-numbers'); + + return lineNumbers.value.length; + } + + public enterKeybindingsView(): any { + return this.spectron.command('workbench.action.openGlobalKeybindings'); + } + + public selectFirstKeybindingsMatch(): any { + return this.spectron.waitFor(this.spectron.client.click, 'div[aria-label="Keybindings"] .monaco-list-row.keybinding-item'); + } + + public changeKeybinding(): any { + return this.spectron.command('editor.action.defineKeybinding'); + } + + public enterBinding(keys: string[]): any { + this.keybinding = keys; + return this.spectron.client.keys(keys); + } + + public toggleActivityBarPosition(): any { + return this.spectron.client.keys(this.keybinding); + } + + public async getActivityBar(position: ActivityBarPosition) { + let positionClass: string; + + if (position === ActivityBarPosition.LEFT) { + positionClass = 'left'; + } else if (position === ActivityBarPosition.RIGHT) { + positionClass = 'right'; + } else { + throw new Error('No such position for activity bar defined.'); + } + try { + return await this.spectron.waitFor(this.spectron.client.getHTML, `.part.activitybar.${positionClass}`); + } catch (e) { + return undefined; + }; + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/css.ts b/test/smoke/src/areas/css.ts new file mode 100644 index 0000000000..331c4cc43c --- /dev/null +++ b/test/smoke/src/areas/css.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; + +export enum CSSProblem { + WARNING = 0, + ERROR = 1 +}; + +export class CSS { + + constructor(private spectron: SpectronApplication) { + // noop + } + + public openQuickOutline(): any { + return this.spectron.command('workbench.action.gotoSymbol'); + } + + public toggleProblemsView(): any { + return this.spectron.command('workbench.actions.view.problems'); + } + + public async getEditorProblem(problemType: CSSProblem): Promise { + let selector; + if (problemType === CSSProblem.WARNING) { + selector = 'greensquiggly'; + } else if (problemType === CSSProblem.ERROR) { + selector = 'redsquiggly'; + } else { + throw new Error('No such problem type defined.'); + } + + let el = await this.spectron.client.element(`.view-overlays .cdr.${selector}`); + if (el.status === 0) { + return el; + } + + return undefined; + } + + public async getProblemsViewsProblem(problemType: CSSProblem): Promise { + let selector; + if (problemType === CSSProblem.WARNING) { + selector = 'warning'; + } else if (problemType === CSSProblem.ERROR) { + selector = 'error'; + } else { + throw new Error('No such problem type defined.'); + } + + let el = await this.spectron.client.element(`div[aria-label="Problems grouped by files"] .icon.${selector}`); + if (el.status === 0) { + return el; + } + + return undefined; + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/data-loss.ts b/test/smoke/src/areas/data-loss.ts new file mode 100644 index 0000000000..dd23e211c0 --- /dev/null +++ b/test/smoke/src/areas/data-loss.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; + +export class DataLoss { + + constructor(private spectron: SpectronApplication) { + } + + public openExplorerViewlet(): Promise { + return this.spectron.command('workbench.view.explorer'); + } + + public async verifyTabIsDirty(tabName: string, active?: boolean): Promise { + let activeSelector = active ? '.active' : ''; + let el = await this.spectron.client.element(`.tabs-container .tab.dirty${activeSelector}[aria-label="${tabName}, tab"]`); + if (el.status === 0) { + return el; + } + + return undefined; + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/extensions.ts b/test/smoke/src/areas/extensions.ts new file mode 100644 index 0000000000..5c5a6be504 --- /dev/null +++ b/test/smoke/src/areas/extensions.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; +import { CommonActions } from './common'; + +var htmlparser = require('htmlparser2'); + +export class Extensions { + + private readonly extensionsViewletSelector = 'div[id="workbench.view.extensions"]'; + private viewletExtensionIndex: number; + + constructor(private spectron: SpectronApplication, private common: CommonActions) { + } + + public async openExtensionsViewlet(): Promise { + await this.spectron.command('workbench.view.extensions'); + return this.spectron.wait(); + } + + public async searchForExtension(name: string): Promise { + const searchBoxSelector = `${this.extensionsViewletSelector} .search-box`; + + await this.spectron.client.clearElement(searchBoxSelector); + try { + await this.spectron.client.click(searchBoxSelector, false); + } catch (e) { + return Promise.reject('Failed to click on search box in extensions viewlet.'); + } + await this.spectron.client.keys(name); + + return this.spectron.client.keys(['NULL', 'Enter', 'NULL']); + } + + public async installExtension(name: string): Promise { + const extensionListSelector = `${this.extensionsViewletSelector} .monaco-list-rows`; + this.viewletExtensionIndex = await this.getExtensionIndex(name, extensionListSelector); + + try { + return this.spectron.client.click(`${extensionListSelector}>:nth-child(${this.viewletExtensionIndex}) .extension .extension-action.install`); + } catch (e) { + return Promise.reject('Failed to click on install button for selected extension.'); + } + } + + public getExtensionReloadText(): Promise { + try { + return this.spectron.waitFor(this.spectron.client.getText, `${this.extensionsViewletSelector} .monaco-list-rows>:nth-child(${this.viewletExtensionIndex}) .extension .extension-action.reload`); + } catch (e) { + return Promise.reject('Reload was not prompted for an installed extension.'); + } + } + + public async activateExtension(): Promise { + await this.common.showCommands(); + await this.common.type('Smoke Test Check'); + await this.spectron.wait(); + return this.common.enter(); + } + + public verifyStatusbarItem(): Promise { + try { + return this.spectron.waitFor(this.spectron.client.getText, '.statusbar-item.statusbar-entry span[title="smoke test"]'); + } catch (e) { + return Promise.reject('Failed to validate extension contribution.'); + } + } + + private getExtensionIndex(name: string, extensionListSelector: string): Promise { + return this.spectron.waitFor(this.spectron.client.getHTML, extensionListSelector).then(html => { + return new Promise((res, rej) => { + let extensionIndex: number = 0; + let extension: boolean; + let tags: string[] = []; + let parser = new htmlparser.Parser({ + onopentag: function (name, attribs) { + if (name === 'div' && attribs.class === 'extension') { + extensionIndex++; + extension = true; + } + if (extension) { + tags.push(name); + } + }, + ontext: function (text) { + if (extension && text === name) { + parser.end(); + } + }, + onclosetag: function (name) { + if (extension) { + tags.pop(); + } + if (extension && tags.length === 0) { + extension = false; + } + }, + onend: function () { + if (extensionIndex === 0) { + return rej(`${name} extension was not found.`); + } + return res(extensionIndex); + } + }); + parser.write(html); + }); + }); + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/first-experience.ts b/test/smoke/src/areas/first-experience.ts new file mode 100644 index 0000000000..9407b6a4c3 --- /dev/null +++ b/test/smoke/src/areas/first-experience.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; + +export class FirstExperience { + constructor(private spectron: SpectronApplication) { + // noop + } + + public async getWelcomeTab(): Promise { + let el = await this.spectron.client.element('.vs_code_welcome_page-name-file-icon'); + if (el.status === 0) { + return el; + } + + return undefined; + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/git.ts b/test/smoke/src/areas/git.ts new file mode 100644 index 0000000000..d1ab20feea --- /dev/null +++ b/test/smoke/src/areas/git.ts @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; +import { CommonActions } from './common'; + +var htmlparser = require('htmlparser2'); + +export class Git { + private editorChangeIndex: number; + + constructor(private spectron: SpectronApplication, private commonActions: CommonActions) { + // noop + } + + public openGitViewlet(): Promise { + return this.spectron.command('workbench.view.scm'); + } + + public getScmIconChanges(): Promise { + return this.spectron.waitFor(this.spectron.client.getText, 'div[id="workbench.parts.activitybar"] .badge.scm-viewlet-label .badge-content'); + } + + public async verifyScmChange(fileName: string): Promise { + let el; + try { + el = await this.spectron.client.element(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.commonActions.getExtensionSelector(fileName)}"]`); + } catch (e) { + return Promise.reject(`${fileName} change is not present in SCM viewlet.`); + } + + if (el.status === 0) { + return el; + } + + return undefined; + } + + public async getOriginalAppJsBodyVarName(): Promise { + this.editorChangeIndex = await this.getFirstChangeIndex('cdr line-delete', '.editor.original .view-overlays'); + return this.spectron.waitFor(this.spectron.client.getText, `.editor.original .view-lines>:nth-child(${this.editorChangeIndex}) .mtk11`); + } + + public getModifiedAppJsBodyVarName(): Promise { + return this.spectron.waitFor(this.spectron.client.getText, `.editor.modified .view-lines>:nth-child(${this.editorChangeIndex}) .mtk11`); + } + + public async stageFile(fileName: string): Promise { + try { + await this.spectron.client.moveToObject(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.commonActions.getExtensionSelector(fileName)}"`); + } catch (e) { + return Promise.reject(`${fileName} was not found in SCM viewlet`); + } + + await this.spectron.wait(); + + try { + await this.spectron.client.click('.action-label.icon.contrib-cmd-icon-4'); + } catch (e) { + return Promise.reject('Stage button was not found'); + } + return this.spectron.wait(); + } + + public async unstageFile(fileName: string): Promise { + try { + await this.spectron.client.moveToObject(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.commonActions.getExtensionSelector(fileName)}"`); + } catch (e) { + return Promise.reject(`${fileName} was not found in SCM viewlet`); + } + + try { + await this.spectron.client.click('.action-label.icon.contrib-cmd-icon-6'); + } catch (e) { + return Promise.reject('Unstage button was not found.'); + } + return this.spectron.wait(); + } + + public async getStagedCount(): Promise { + let scmHeaders: Array; + try { + scmHeaders = await this.spectron.waitFor(this.spectron.client.getText, '.scm-status.show-file-icons .monaco-list-rows .name'); // get all headers + } + catch (e) { + return Promise.reject('No row names in SCM viewlet were found.'); + } + + const stagedTitle = scmHeaders.find((val) => { + return val.match(/staged/i) ? true : false; + }); + + if (!stagedTitle) { + return Promise.reject(`No 'Staged' header title found in SCM viewlet`); + } + + const monacoRowIndex = scmHeaders.indexOf(stagedTitle); + try { + return this.spectron.waitFor(this.spectron.client.getText, `.scm-status.show-file-icons .monaco-list-rows>:nth-child(${monacoRowIndex + 1}) .monaco-count-badge`); + } catch (e) { + return Promise.reject('Stage count badge cannot be found'); + } + } + + public focusOnCommitBox(): Promise { + try { + return this.spectron.client.click('div[id="workbench.view.scm"] textarea'); + } catch (e) { + return Promise.reject('Failed to focus on commit box: ' + e); + } + } + + public async pressCommit(): Promise { + try { + await this.spectron.client.click('.action-label.icon.contrib-cmd-icon-10'); + } catch (e) { + return Promise.reject('Failed to press commit: ' + e); + } + + return this.spectron.wait(); + } + + public getOutgoingChanges(): Promise { + try { + return this.spectron.client.getText('a[title="Synchronize Changes"]'); + } catch (e) { + return Promise.reject(`Failed to obtain 'synchronize changes' title value from the status bar.`); + } + } + + private getFirstChangeIndex(changeClass: string, selector: string): Promise { + return this.spectron.waitFor(this.spectron.client.getHTML, selector).then(html => { + return new Promise((res, rej) => { + let lineIndex: number = 0; + let changeFound: boolean; + let tags: string[] = []; + let parser = new htmlparser.Parser({ + onopentag: function (name: string, attribs: any) { + tags.push(name); + if (name === 'div' && !attribs.class) { + lineIndex++; + } else if (name === 'div' && attribs.class === changeClass) { + changeFound = true; + parser.end(); + } + }, + onclosetag: function (name) { + // Terminate once last tag is closed + tags.pop(); + if (!changeFound && tags.length === 0) { + parser.end(); + } + }, + onend: function () { + if (!changeFound) { + return rej(`No changes in the diff found.`); + } + return res(lineIndex); + } + }); + parser.write(html); + }); + }); + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/integrated-terminal.ts b/test/smoke/src/areas/integrated-terminal.ts new file mode 100644 index 0000000000..da39791eb9 --- /dev/null +++ b/test/smoke/src/areas/integrated-terminal.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; +import { CommonActions } from './common'; + +export class IntegratedTerminal { + + public static terminalSelector = 'div[id="workbench.panel.terminal"]'; + public static terminalRowsSelector = 'div[id="workbench.panel.terminal"] .xterm-rows'; + + constructor(private spectron: SpectronApplication) { + // noop + } + + public async openTerminal(commonActions: CommonActions): Promise { + // Backquote dispatching does not work in OS X + if (process.platform === 'darwin') { + await commonActions.showCommands(); + await commonActions.type('Toggle Integrated Terminal'); + return commonActions.enter(); + } + + await this.spectron.command('workbench.action.terminal.toggleTerminal'); + + // If no terminal panel was opened, try triggering terminal from quick open + try { + await this.spectron.client.getHTML(IntegratedTerminal.terminalSelector); + } catch (e) { + await commonActions.openQuickOpen(); + await this.spectron.client.keys('>Toggle Integrated Terminal'); + await this.spectron.client.keys(['Enter', 'NULL']); + } + } + + public async commandOutputHas(result: string): Promise { + const rows = await this.spectron.client.elements(`${IntegratedTerminal.terminalRowsSelector} div`); + for (let i = 0; i < rows.value.length; i++) { + let rowText; + try { + rowText = await this.spectron.client.getText(`${IntegratedTerminal.terminalRowsSelector}>:nth-child(${i + 1})`); + } catch (e) { + return Promise.reject(`Failed to obtain text from line ${i + 1} from the terminal.`); + } + if (rowText.trim() === result) { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/javascript-debug.ts b/test/smoke/src/areas/javascript-debug.ts new file mode 100644 index 0000000000..21febee50e --- /dev/null +++ b/test/smoke/src/areas/javascript-debug.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; +var stripJsonComments = require('strip-json-comments'); + +export class JavaScriptDebug { + private readonly sidebarSelector = '.margin-view-overlays'; + + constructor(private spectron: SpectronApplication) { + // noop + } + + public openDebugViewlet(): Promise { + return this.spectron.command('workbench.view.debug'); + } + + public async pressConfigureLaunchJson(): Promise { + try { + await this.spectron.waitFor(this.spectron.client.click, 'ul[aria-label="Debug actions"] .action-label.icon.debug-action.configure'); + } catch (e) { + return Promise.reject('Clicking on debug configuration gear failed.'); + } + await this.spectron.wait(); + await this.spectron.client.keys(['ArrowDown', 'NULL', 'Enter']); + return this.spectron.wait(); + } + + public async getProgramConfigValue(): Promise { + const lines = stripJsonComments(await this.spectron.client.getText('.view-lines')); + const json = JSON.parse(lines); + return json.configurations[0].program; + } + + public setBreakpointOnLine(lineNumber: number): Promise { + try { + return this.spectron.client.leftClick(`${this.sidebarSelector}>:nth-child(${lineNumber})`, 5, 5); + } catch (e) { + return Promise.reject('Setting breakpoint failed: ' + e); + } + } + + public async verifyBreakpointOnLine(lineNumber: number): Promise { + let el = await this.spectron.client.element(`${this.sidebarSelector}>:nth-child(${lineNumber}) .cgmr.debug-breakpoint-glyph`); + if (el.status === 0) { + return el; + } + + return undefined; + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/javascript.ts b/test/smoke/src/areas/javascript.ts new file mode 100644 index 0000000000..ec61bbb67c --- /dev/null +++ b/test/smoke/src/areas/javascript.ts @@ -0,0 +1,186 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; + +var htmlparser = require('htmlparser2'); + +export class JavaScript { + private appVarSelector: string; + private expressVarSelector: string; + + private foldSelector: string; + private foldLine: number; + + constructor(private spectron: SpectronApplication) { + // noop + } + + public openQuickOutline(): Promise { + return this.spectron.command('workbench.action.gotoSymbol'); + } + + public async findAppReferences(): Promise { + await this.setAppVarSelector(); + try { + await this.spectron.client.click(this.appVarSelector, false); + } catch (e) { + return Promise.reject(`Failed to select 'app' variable.`); + } + + return this.spectron.command('editor.action.referenceSearch.trigger'); + } + + public async getTitleReferencesCount(): Promise { + const meta = await this.spectron.client.getText('.reference-zone-widget.results-loaded .peekview-title .meta'); + + return meta.match(/\d+/)[0]; + } + + public async getTreeReferencesCount(): Promise { + const treeElems = await this.spectron.client.elements('.reference-zone-widget.results-loaded .ref-tree.inline .show-twisties .monaco-tree-row'); + + return treeElems.value.length; + } + + public async renameApp(newValue: string): Promise { + await this.setAppVarSelector(); + + try { + await this.spectron.client.click(this.appVarSelector); + } catch (e) { + return Promise.reject(`Failed to select 'app' variable.`); + } + await this.spectron.command('editor.action.rename'); + await this.spectron.wait(); + return this.spectron.client.keys(newValue, false); + } + + public async getNewAppName(): Promise { + return this.spectron.client.getText(this.appVarSelector); + } + + public async toggleFirstCommentFold(): Promise { + this.foldLine = await this.getLineIndexOfFirstFoldableElement(`.margin-view-overlays`); + this.foldSelector = `.margin-view-overlays>:nth-child(${this.foldLine})`; + + try { + return this.spectron.client.click(`${this.foldSelector} .cldr.folding`); + } catch (e) { + return Promise.reject('Clicking on fold element failed ' + e); + } + } + + public async getFirstCommentFoldedIcon(): Promise { + if (!this.foldSelector) { + return Promise.reject('No code folding happened to be able to check for a folded icon.'); + } + + return this.spectron.client.getHTML(`${this.foldSelector} .cldr.folding.collapsed`); + } + + public async getNextLineNumberAfterFold(): Promise { + if (!this.foldLine) { + return Promise.reject('Folded line was not set, most likely because fold was not toggled initially.'); + } + + return this.spectron.client.getText(`.margin-view-overlays>:nth-child(${this.foldLine + 1}) .line-numbers`); + } + + public async goToExpressDefinition(): Promise { + await this.setExpressVarSelector(); + try { + await this.spectron.client.click(this.expressVarSelector); + } catch (e) { + return Promise.reject(`Clicking on express variable failed: ` + e); + } + + return this.spectron.command('editor.action.goToDeclaration'); + } + + public async peekExpressDefinition(): Promise { + await this.setExpressVarSelector(); + try { + await this.spectron.client.click(this.expressVarSelector); + } catch (e) { + return Promise.reject('Clicking on express variable failed: ' + e); + } + + return this.spectron.command('editor.action.previewDeclaration'); + } + + public async getPeekExpressResultName(): Promise { + return this.spectron.client.getText('.reference-zone-widget.results-loaded .filename'); + } + + private async setAppVarSelector(): Promise { + if (!this.appVarSelector) { + const lineIndex = await this.getLineIndexOfFirst('app', '.view-lines'); + this.appVarSelector = `.view-lines>:nth-child(${lineIndex}) .mtk11`; + } + } + + private async setExpressVarSelector(): Promise { + if (!this.expressVarSelector) { + const lineIndex = await this.getLineIndexOfFirst('express', '.view-lines'); + this.expressVarSelector = `.view-lines>:nth-child(${lineIndex}) .mtk10`; + } + } + + private getLineIndexOfFirst(string: string, selector: string): Promise { + return this.spectron.waitFor(this.spectron.client.getHTML, selector).then(html => { + return new Promise((res, rej) => { + let lineIndex: number = 0; + let stringFound: boolean; + let parser = new htmlparser.Parser({ + onopentag: function (name: string, attribs: any) { + if (name === 'div' && attribs.class === 'view-line') { + lineIndex++; + } + }, + ontext: function (text) { + if (!stringFound && text === string) { + stringFound = true; + parser.end(); + } + }, + onend: function () { + if (!stringFound) { + return rej(`No ${string} in editor found.`); + } + return res(lineIndex); + } + }); + parser.write(html); + }); + }); + } + + private getLineIndexOfFirstFoldableElement(selector: string): Promise { + return this.spectron.waitFor(this.spectron.client.getHTML, selector).then(html => { + return new Promise((res, rej) => { + let lineIndex: number = 0; + let foldFound: boolean; + let parser = new htmlparser.Parser({ + onopentag: function (name: string, attribs: any) { + if (name === 'div' && !attribs.class) { + lineIndex++; + } else if (name === 'div' && attribs.class.indexOf('cldr folding') !== -1) { + foldFound = true; + parser.end(); + } + }, + onend: function () { + if (!foldFound) { + return rej(`No foldable elements found.`); + } + return res(lineIndex); + } + }); + parser.write(html); + }); + }); + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/localization.ts b/test/smoke/src/areas/localization.ts new file mode 100644 index 0000000000..eefcc8c634 --- /dev/null +++ b/test/smoke/src/areas/localization.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; + +export enum ViewletType { + SEARCH = 0, + SCM = 1, + DEBUG = 2, + EXTENSIONS = 3 +} + +export class Localization { + + constructor(private spectron: SpectronApplication) { + // noop + } + + public async getOpenEditorsText(): Promise { + let explorerTitles; + try { + explorerTitles = await this.spectron.client.getText('div[id="workbench.view.explorer"] .title span'); + } catch (e) { + return Promise.reject('Failed to get span of title in explorer viewlet.'); + } + + return explorerTitles[0]; + } + + public async openViewlet(type: ViewletType): Promise { + let command; + + switch (type) { + case ViewletType.SEARCH: + command = 'workbench.view.search'; + break; + case ViewletType.SCM: + command = 'workbench.view.scm'; + break; + case ViewletType.DEBUG: + command = 'workbench.view.debug'; + break; + case ViewletType.EXTENSIONS: + command = 'workbench.view.extensions'; + break; + } + + await this.spectron.command(command, false); + return this.spectron.wait(); + } + + public getOpenedViewletTitle(): Promise { + try { + return this.spectron.client.getText('div[id="workbench.parts.sidebar"] .title-label span'); + } catch (e) { + return Promise.reject('Failed to get span of title label in explorer viewlet.'); + } + } + + public getExtensionsSearchPlaceholder(): Promise { + try { + return this.spectron.client.getAttribute('div[id="workbench.view.extensions"] .search-box', 'placeholder'); + } catch (e) { + return Promise.reject('Failed to get extension viewlet search box placeholder.'); + } + } + +} diff --git a/test/smoke/src/areas/search.ts b/test/smoke/src/areas/search.ts new file mode 100644 index 0000000000..da4cb47473 --- /dev/null +++ b/test/smoke/src/areas/search.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; + +export class Search { + + constructor(private spectron: SpectronApplication) { + // noop + } + + public openSearchViewlet(): Promise { + return this.spectron.command('workbench.view.search'); + } + + public async searchFor(text: string): Promise { + await this.spectron.client.keys(text); + return this.spectron.client.keys(['NULL', 'Enter', 'NULL'], false); + } + + public setReplaceText(text: string): any { + try { + return this.spectron.client.setValue('.viewlet .input[title="Replace"]', text); + } catch (e) { + return Promise.reject('Cannot set replace input in the viewlet: ' + e); + } + } + + public replaceFirstMatch(): any { + try { + return this.spectron.client.click('.monaco-tree-rows.show-twisties .action-label.icon.action-replace-all'); + } catch (e) { + return Promise.reject('Cannot replace the search first match: ' + e); + } + } + + public getResultText(): any { + return this.spectron.waitFor(this.spectron.client.getText, '.search-viewlet .message>p'); + } + + public toggleSearchDetails(): any { + try { + return this.spectron.client.click('.query-details .more'); + } catch (e) { + return Promise.reject('Toggling search details failed: ' + e); + } + } + + public toggleReplace(): any { + try { + return this.spectron.client.click('.monaco-button.toggle-replace-button.collapse'); + } catch (e) { + return Promise.reject('Toggling replace failed: ' + e); + } + } + + public hoverOverResultCount(): any { + try { + return this.spectron.waitFor(this.spectron.client.moveToObject, '.monaco-count-badge'); + } catch (e) { + return Promise.reject('Hovering over result count failed: ' + e); + } + } + + public dismissResult(): any { + try { + return this.spectron.client.click('.action-label.icon.action-remove'); + } catch (e) { + return Promise.reject('Clicking on dismissing result failed: ' + e); + } + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/statusbar.ts b/test/smoke/src/areas/statusbar.ts new file mode 100644 index 0000000000..aab9ee7335 --- /dev/null +++ b/test/smoke/src/areas/statusbar.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; + +export enum StatusBarElement { + BRANCH_STATUS = 0, + SYNC_STATUS = 1, + PROBLEMS_STATUS = 2, + SELECTION_STATUS = 3, + INDENTATION_STATUS = 4, + ENCODING_STATUS = 5, + EOL_STATUS = 6, + LANGUAGE_STATUS = 7, + FEEDBACK_ICON = 8 +} + +export class StatusBar { + + private selectorsMap: Map; + private readonly mainSelector = 'div[id="workbench.parts.statusbar"]'; + + constructor(private spectron: SpectronApplication) { + this.populateSelectorsMap(); + } + + public async isVisible(element: StatusBarElement): Promise { + const selector = this.selectorsMap.get(element); + if (!selector) { + throw new Error('No such element in the status bar defined.'); + } + + return this.spectron.client.isVisible(selector); + } + + public async clickOn(element: StatusBarElement): Promise { + const selector = this.selectorsMap.get(element); + if (!selector) { + throw new Error('No such element in the status bar defined.'); + } + + try { + return this.spectron.client.click(selector); + } catch (e) { + return Promise.reject(`Clicking on status bar element ${selector} failed.`); + } + } + + public async getProblemsView(): Promise { + let el = await this.spectron.client.element('div[id="workbench.panel.markers"]'); + if (el.status === 0) { + return el; + } + + return undefined; + } + + public async getFeedbackView(): Promise { + let el = await this.spectron.client.element('.feedback-form'); + if (el.status === 0) { + return el; + } + + return undefined; + } + + public isQuickOpenWidgetVisible(): Promise { + return this.spectron.client.isVisible('.quick-open-widget'); + } + + public async getEditorHighlightedLine(lineNumber: number): Promise { + let el = await this.spectron.client.element(`.monaco-editor .view-overlays>:nth-child(${lineNumber}) .current-line`); + if (el.status === 0) { + return el; + } + + return undefined; + } + + public async getEOLMode(): Promise { + const selector = this.selectorsMap.get(StatusBarElement.EOL_STATUS); + if (!selector) { + throw new Error('No such element in the status bar defined.'); + } + + return this.spectron.client.getText(selector); + } + + private populateSelectorsMap(): void { + this.selectorsMap = new Map(); + this.selectorsMap.set(StatusBarElement.BRANCH_STATUS, `${this.mainSelector} .octicon.octicon-git-branch`); + this.selectorsMap.set(StatusBarElement.SYNC_STATUS, `${this.mainSelector} .octicon.octicon-sync`); + this.selectorsMap.set(StatusBarElement.PROBLEMS_STATUS, `${this.mainSelector} .task-statusbar-item[title="Problems"]`); + this.selectorsMap.set(StatusBarElement.SELECTION_STATUS, `${this.mainSelector} .editor-status-selection`); + this.selectorsMap.set(StatusBarElement.INDENTATION_STATUS, `${this.mainSelector} .editor-status-indentation`); + this.selectorsMap.set(StatusBarElement.ENCODING_STATUS, `${this.mainSelector} .editor-status-encoding`); + this.selectorsMap.set(StatusBarElement.EOL_STATUS, `${this.mainSelector} .editor-status-eol`); + this.selectorsMap.set(StatusBarElement.LANGUAGE_STATUS, `${this.mainSelector} .editor-status-mode`); + this.selectorsMap.set(StatusBarElement.FEEDBACK_ICON, `${this.mainSelector} .dropdown.send-feedback`); + } +} \ No newline at end of file diff --git a/test/smoke/src/areas/tasks.ts b/test/smoke/src/areas/tasks.ts new file mode 100644 index 0000000000..083a0f6183 --- /dev/null +++ b/test/smoke/src/areas/tasks.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SpectronApplication } from '../spectron/application'; +import { IntegratedTerminal } from './integrated-terminal'; + +export class Tasks { + + private readonly outputViewSelector = IntegratedTerminal.terminalRowsSelector; + private readonly workbenchPanelSelector = 'div[id="workbench.parts.panel"]'; + private readonly problemsViewSelector = 'div[id="workbench.panel.markers"] .monaco-tree-row.expanded'; + + constructor(private spectron: SpectronApplication) { + // noop + } + + public async build(): Promise { + await this.spectron.command('workbench.action.tasks.build'); + await this.spectron.wait(); // wait for build to finish + + // Validate that it has finished + let trial = 0; + while (trial < 3) { + // Determine build status based on the statusbar indicator, don't continue until task has been terminated + try { + return await this.spectron.client.getValue('.task-statusbar-item-progress.builder-hidden'); + } catch (e) { + await this.spectron.wait(); + trial++; + } + } + + return Promise.reject('Could not determine if the task was terminated based on status bar progress spinner.'); + } + + public openProblemsView(): Promise { + return this.spectron.command('workbench.actions.view.problems'); + } + + public async outputContains(string: string): Promise { + const output: string = await this.spectron.waitFor(this.spectron.client.getText, this.outputViewSelector); + + if (output.indexOf(string) !== -1) { + return true; + } + + return false; + } + + public async selectOutputViewType(type: string): Promise { + await this.openOutputView(); + + try { + return this.spectron.client.selectByValue(`${this.workbenchPanelSelector} .select-box`, type); + } catch (e) { + return Promise.reject(`Failed to select ${type} as workbench panel output.`); + } + } + + public getOutputViewType(): Promise { + return this.spectron.client.getValue(`${this.workbenchPanelSelector} .select-box`); + } + + public getProblemsViewFirstElementName(): Promise { + try { + return this.spectron.waitFor(this.spectron.client.getText, `${this.problemsViewSelector} .label-name`); + } catch (e) { + return Promise.reject('Failed to get problem label from Problems view: ' + e); + } + } + + public getProblemsViewFirstElementCount(): Promise { + try { + return this.spectron.waitFor(this.spectron.client.getText, `${this.problemsViewSelector} .monaco-count-badge`); + } catch (e) { + return Promise.reject('Failed to get problem count from Problems view: ' + e); + } + } + + private openOutputView(): Promise { + try { + return this.spectron.command('workbench.action.output.toggleOutput'); + } catch (e) { + return Promise.reject('Failed to toggle output view'); + } + } +} \ No newline at end of file diff --git a/test/smoke/src/helpers/screenshot.ts b/test/smoke/src/helpers/screenshot.ts new file mode 100644 index 0000000000..e87cb344df --- /dev/null +++ b/test/smoke/src/helpers/screenshot.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 { SpectronApplication } from '../spectron/application'; +var fs = require('fs'); + +const __testTime = new Date().toISOString(); + +export class Screenshot { + private index: number = 0; + private testPath: string; + + constructor(private spectron: SpectronApplication, testName: string, testRetry: number) { + const testTime = this.sanitizeFolderName(__testTime); + testName = this.sanitizeFolderName(testName); + + this.testPath = `test_data/screenshots/${testTime}/${testName}/${testRetry}`; + this.createFolder(this.testPath); + } + + public async capture(): Promise { + const image = await this.spectron.app.browserWindow.capturePage(); + await new Promise((c, e) => fs.writeFile(`${this.testPath}/${this.index++}.png`, image, err => err ? e(err) : c())); + } + + private createFolder(name: string): void { + name.split('/').forEach((folderName, i, fullPath) => { + const folder = fullPath.slice(0, i + 1).join('/'); + if (!fs.existsSync(folder)) { + fs.mkdirSync(folder); + } + }); + } + + private sanitizeFolderName(name: string): string { + return name.replace(/[&*:\/]/g, ''); + } +} \ No newline at end of file diff --git a/test/smoke/src/helpers/utilities.ts b/test/smoke/src/helpers/utilities.ts new file mode 100644 index 0000000000..58bed50676 --- /dev/null +++ b/test/smoke/src/helpers/utilities.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +var fs = require('fs'); +var rimraf = require('rimraf'); + +/** + * Contains methods that are commonly used across test areas. + */ +export class Util { + constructor() { + // noop + } + + public removeFile(filePath: string): void { + try { + fs.unlinkSync(`${filePath}`); + } catch (e) { + if (e.code !== 'ENOENT') { + throw e; + } + } + } + + public rimraf(directory: string): Promise { + return new Promise((res, rej) => { + rimraf(directory, (err) => { + if (err) { + rej(err); + } + res(); + }); + }); + } +} \ No newline at end of file diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts new file mode 100644 index 0000000000..10c36c2f6d --- /dev/null +++ b/test/smoke/src/main.ts @@ -0,0 +1,222 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const fs = require('fs'); +const https = require('https'); +const program = require('commander'); +const git = require('simple-git')(); +const child_process = require('child_process'); +const path = require('path'); +const mkdirp = require('mkdirp'); + +const testDataPath = path.join(process.cwd(), 'test_data'); +const codeWorkspacePath = path.join(testDataPath, 'smoketest.code-workspace'); +const testRepoUrl = 'https://github.com/Microsoft/vscode-smoketest-express'; +const testRepoLocalDir = path.join(testDataPath, 'vscode-smoketest-express'); +const keybindingsUrl = 'https://raw.githubusercontent.com/Microsoft/vscode-docs/master/scripts/keybindings'; + +mkdirp.sync(testDataPath); + +program + .option('-l, --latest ', 'path to the latest VS Code to test') + .option('-s, --stable [file path]', 'path to the stable VS Code to be used in data migration tests'); + +program.on('--help', () => { + console.log(' Examples:'); + console.log(''); + console.log(' $ npm test -- --latest path/to/binary'); + console.log(' $ npm test -- -l path/to/binary'); + console.log(''); + console.log(' $ npm test -- --latest path/to/latest/binary --stable path/to/stable/binary'); + console.log(' $ npm test -- -l path/to/latest/binary -s path/to/stable/binary'); + console.log(''); +}); +program.parse(process.argv); + +if (!program.latest) { + fail('You must specify the binary to run the smoke test against'); +} +if (!binaryExists(program.latest) || (program.stable && !binaryExists(program.stable))) { + fail('The file path to electron binary does not exist or permissions do not allow to execute it. Please check the path provided.'); +} +if (parseInt(process.version.substr(1)) < 6) { + fail('Please update your Node version to greater than 6 to run the smoke test.'); +} + +// Setting up environment variables +process.env.VSCODE_LATEST_PATH = program.latest; +if (program.stable) { + process.env.VSCODE_STABLE_PATH = program.stable; +} +process.env.SMOKETEST_REPO = testRepoLocalDir; +if (program.latest && (program.latest.indexOf('Code - Insiders') /* macOS/Windows */ || program.latest.indexOf('code-insiders') /* Linux */) >= 0) { + process.env.VSCODE_EDITION = 'insiders'; +} +process.env.VSCODE_WORKSPACE_PATH = codeWorkspacePath; + +// Setting up 'vscode-smoketest-express' project +let os = process.platform.toString(); +if (os === 'darwin') { + os = 'osx'; +} +else if (os === 'win32') { + os = 'win'; +} + +main().catch(err => console.error(err)); + +async function main(): Promise { + await getKeybindings(`${keybindingsUrl}/doc.keybindings.${os}.json`, path.join(testDataPath, 'keybindings.json')); + + const workspace = { + id: (Date.now() + Math.round(Math.random() * 1000)).toString(), + folders: [ + toUri(path.join(testRepoLocalDir, 'public')), + toUri(path.join(testRepoLocalDir, 'routes')), + toUri(path.join(testRepoLocalDir, 'views')) + ] + }; + + await createWorkspaceFile(codeWorkspacePath, workspace); + await cleanOrClone(testRepoUrl, testRepoLocalDir); + await execute('npm install', testRepoLocalDir); + await runTests(); +} + +function fail(errorMessage): void { + console.error(errorMessage); + process.exit(1); +} + +function toUri(path: string): string { + if (os === 'win') { + return `file:///${path.replace(/\\/g, '/')}`; + } + + return `file://${path}`; +} + +function runTests(): void { + console.log('Running tests...'); + var proc = child_process.spawn(process.execPath, [ + 'out/mocha-runner.js' + ]); + proc.stdout.on('data', data => { + console.log(data.toString()); + }); + proc.stderr.on('data', data => { + var date = new Date().toLocaleString(); + fs.appendFile(path.join(testDataPath, 'errors.log'), `${date}: ${data.toString()}`, (err) => { + if (err) { + throw new Error(`Could not write stderr to errors.log with the following error: ${err}`); + }; + }); + }); + proc.on('exit', (code) => { + process.exit(code); + }); +} + +async function cleanOrClone(repo: string, dir: string): Promise { + console.log('Cleaning or cloning test project repository...'); + + if (!folderExists(dir)) { + await gitClone(repo, dir); + } else { + git.cwd(dir); + await new Promise((c, e) => git.fetch(err => err ? e(err) : c())); + await gitResetAndClean(); + } +} + +function gitClone(repo: string, dir: string): Promise { + return new Promise((res, rej) => { + git.clone(repo, dir, () => { + console.log('Test repository successfully cloned.'); + res(); + }); + }); +} + +async function gitResetAndClean(): Promise { + await new Promise((c, e) => git.reset(['FETCH_HEAD', '--hard'], err => err ? e(err) : c())); + await new Promise((c, e) => git.clean('f', ['-d'], err => err ? e(err) : c())); + console.log('Test project was successfully reset to initial state.'); +} + +function execute(cmd: string, dir: string): Promise { + return new Promise((res, rej) => { + console.log(`Running ${cmd}...`); + child_process.exec(cmd, { cwd: dir, stdio: [0, 1, 2] }, (error, stdout, stderr) => { + if (error) { + rej(error); + } + if (stderr) { + console.error(stderr); + } + console.log(stdout); + res(); + }); + }); +} + +function getKeybindings(url: string, location: string): Promise { + console.log(`Fetching keybindings from ${url}...`); + return new Promise((resolve, reject) => { + https.get(url, (res) => { + if (res.statusCode !== 200) { + reject(`Failed to obtain key bindings with response code: ${res.statusCode}`); + } + + var buffer: Buffer[] = []; + res.on('data', (chunk) => buffer.push(chunk)); + res.on('end', () => { + fs.writeFile(location, Buffer.concat(buffer), 'utf8', () => { + console.log('Keybindings were successfully fetched.'); + resolve(); + }); + }); + }).on('error', (e) => { + reject(`Failed to obtain key bindings with an error: ${e}`); + }); + }); +} + +function createWorkspaceFile(path: string, workspace: any): Promise { + console.log(`Creating workspace file at ${path}...`); + return new Promise((resolve, reject) => { + fs.exists(path, exists => { + if (exists) { + return resolve(); + } + + fs.writeFile(path, JSON.stringify(workspace, null, '\t'), error => { + if (error) { + reject(error); + } else { + resolve(); + } + }); + }); + }); +} + +function folderExists(folder: string): boolean { + try { + fs.accessSync(folder, 'rw'); + return true; + } catch (e) { + return false; + } +} + +function binaryExists(filePath: string): boolean { + try { + fs.accessSync(filePath, 'x'); + return true; + } catch (e) { + return false; + } +} \ No newline at end of file diff --git a/test/smoke/src/mocha-runner.ts b/test/smoke/src/mocha-runner.ts new file mode 100644 index 0000000000..86522406f6 --- /dev/null +++ b/test/smoke/src/mocha-runner.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const MochaTest = require('mocha'); + +const mochaTest = new MochaTest({ + timeout: 60000, + slow: 10000, + useColors: true +}); +mochaTest.addFile(require('path').join(process.cwd(), 'out/test.js')); +mochaTest.run((failures) => { + process.exit(failures); +}); \ No newline at end of file diff --git a/test/smoke/src/spectron/application.ts b/test/smoke/src/spectron/application.ts new file mode 100644 index 0000000000..f4e2988488 --- /dev/null +++ b/test/smoke/src/spectron/application.ts @@ -0,0 +1,171 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Application } from 'spectron'; +import { SpectronClient } from './client'; +import { Screenshot } from '../helpers/screenshot'; +var fs = require('fs'); +var path = require('path'); + +export const LATEST_PATH = process.env.VSCODE_LATEST_PATH; +export const STABLE_PATH = process.env.VSCODE_STABLE_PATH; +export const WORKSPACE_PATH = process.env.SMOKETEST_REPO; +export const CODE_WORKSPACE_PATH = process.env.VSCODE_WORKSPACE_PATH; +export const USER_DIR = 'test_data/temp_user_dir'; +export const EXTENSIONS_DIR = 'test_data/temp_extensions_dir'; + +/** + * Wraps Spectron's Application instance with its used methods. + */ +export class SpectronApplication { + public client: SpectronClient; + + private spectron: Application; + private keybindings: any[]; + private screenshot: Screenshot; + + private readonly sampleExtensionsDir: string = 'test_data/sample_extensions_dir'; + private readonly pollTrials = 50; + private readonly pollTimeout = 1; // in secs + + constructor(electronPath: string, testName: string, private testRetry: number, args?: string[], chromeDriverArgs?: string[]) { + if (!args) { + args = []; + } + + // Prevent 'Getting Started' web page from opening on clean user-data-dir + args.push('--skip-getting-started'); + + // Ensure that running over custom extensions directory, rather than picking up the one that was used by a tester previously + let extensionDirIsSet = false; + for (let arg of args) { + if (arg.startsWith('--extensions-dir')) { + extensionDirIsSet = true; + break; + } + } + if (!extensionDirIsSet) { + args.push(`--extensions-dir=${this.sampleExtensionsDir}`); + } + + this.spectron = new Application({ + path: electronPath, + args: args, + chromeDriverArgs: chromeDriverArgs, + startTimeout: 10000, + requireName: 'nodeRequire' + }); + this.testRetry += 1; // avoid multiplication by 0 for wait times + this.screenshot = new Screenshot(this, testName, testRetry); + this.client = new SpectronClient(this.spectron, this.screenshot); + this.retrieveKeybindings(); + } + + public get app(): Application { + return this.spectron; + } + + public async start(): Promise { + await this.spectron.start(); + await this.focusOnWindow(1); // focuses on main renderer window + await this.checkWindowReady(); + } + + public async stop(): Promise { + if (this.spectron && this.spectron.isRunning()) { + return await this.spectron.stop(); + } + } + + public waitFor(func: (...args: any[]) => any, args: any): Promise { + return this.callClientAPI(func, args); + } + + public wait(): Promise { + return new Promise(resolve => setTimeout(resolve, this.testRetry * this.pollTimeout * 1000)); + } + + public focusOnWindow(index: number): Promise { + return this.client.windowByIndex(index); + } + + private async checkWindowReady(): Promise { + await this.waitFor(this.spectron.client.getHTML, '[id="workbench.main.container"]'); + } + + private retrieveKeybindings() { + fs.readFile(path.join(process.cwd(), `test_data/keybindings.json`), 'utf8', (err, data) => { + if (err) { + throw err; + } + try { + this.keybindings = JSON.parse(data); + } catch (e) { + throw new Error(`Error parsing keybindings JSON: ${e}`); + } + }); + } + + private async callClientAPI(func: (...args: any[]) => Promise, args: any): Promise { + let trial = 1; + + while (true) { + if (trial > this.pollTrials) { + throw new Error(`Could not retrieve the element in ${this.testRetry * this.pollTrials * this.pollTimeout} seconds.`); + } + + let result; + try { + result = await func.call(this.client, args, false); + } catch (e) { } + + if (result && result !== '') { + await this.screenshot.capture(); + return result; + } + + await this.wait(); + trial++; + } + } + + /** + * Retrieves the command from keybindings file and executes it with WebdriverIO client API + * @param command command (e.g. 'workbench.action.files.newUntitledFile') + */ + public command(command: string, capture?: boolean): Promise { + const binding = this.keybindings.find(x => x['command'] === command); + if (!binding) { + return Promise.reject(`Key binding for ${command} was not found.`); + } + + const keys: string = binding.key; + let keysToPress: string[] = []; + + const chords = keys.split(' '); + chords.forEach((chord) => { + const keys = chord.split('+'); + keys.forEach((key) => keysToPress.push(this.transliterate(key))); + keysToPress.push('NULL'); + }); + + return this.client.keys(keysToPress, capture); + } + + /** + * Transliterates key names from keybindings file to WebdriverIO keyboard actions defined in: + * https://w3c.github.io/webdriver/webdriver-spec.html#keyboard-actions + */ + private transliterate(key: string): string { + switch (key) { + case 'ctrl': + return 'Control'; + case 'cmd': + return 'Meta'; + default: + return key.length === 1 ? key : key.charAt(0).toUpperCase() + key.slice(1); + }; + } +} diff --git a/test/smoke/src/spectron/client.ts b/test/smoke/src/spectron/client.ts new file mode 100644 index 0000000000..c351c9ee4d --- /dev/null +++ b/test/smoke/src/spectron/client.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Application } from 'spectron'; +import { Screenshot } from '../helpers/screenshot'; + +/** + * Abstracts the Spectron's WebdriverIO managed client property on the created Application instances. + */ +export class SpectronClient { + + constructor(private spectron: Application, private shot: Screenshot) { + // noop + } + + public windowByIndex(index: number): Promise { + return this.spectron.client.windowByIndex(index); + } + + public async keys(keys: string[] | string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.keys(keys); + } + + public async getText(selector: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.getText(selector); + } + + public async getHTML(selector: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.getHTML(selector); + } + + public async click(selector: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.click(selector); + } + + public async doubleClick(selector: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.doubleClick(selector); + } + + public async leftClick(selector: string, xoffset: number, yoffset: number, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.leftClick(selector, xoffset, yoffset); + } + + public async rightClick(selector: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.rightClick(selector); + } + + public async moveToObject(selector: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.moveToObject(selector); + } + + public async setValue(selector: string, text: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.setValue(selector, text); + } + + public async elements(selector: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.elements(selector); + } + + public async element(selector: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.element(selector); + } + + public async dragAndDrop(sourceElem: string, destinationElem: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.dragAndDrop(sourceElem, destinationElem); + } + + public async selectByValue(selector: string, value: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.selectByValue(selector, value); + } + + public async getValue(selector: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.getValue(selector); + } + + public async getAttribute(selector: string, attribute: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return Promise.resolve(this.spectron.client.getAttribute(selector, attribute)); + } + + public clearElement(selector: string): any { + return this.spectron.client.clearElement(selector); + } + + public buttonDown(): any { + return this.spectron.client.buttonDown(); + } + + public buttonUp(): any { + return this.spectron.client.buttonUp(); + } + + public async isVisible(selector: string, capture: boolean = true): Promise { + await this.screenshot(capture); + return this.spectron.client.isVisible(selector); + } + + public getTitle(): string { + return this.spectron.client.getTitle(); + } + + private async screenshot(capture: boolean): Promise { + if (capture) { + try { + await this.shot.capture(); + } catch (e) { + throw new Error(`Screenshot could not be captured: ${e}`); + } + } + } +} \ No newline at end of file diff --git a/test/smoke/src/test.ts b/test/smoke/src/test.ts new file mode 100644 index 0000000000..ed00283fde --- /dev/null +++ b/test/smoke/src/test.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 { testDataMigration } from './tests/data-migration'; +import { testDataLoss } from './tests/data-loss'; +import { testExplorer } from './tests/explorer'; +import { testConfigViews } from './tests/configuration-views'; +import { testSearch } from './tests/search'; +import { testCSS } from './tests/css'; +import { testJavaScript } from './tests/javascript'; +import { testJavaScriptDebug } from './tests/javascript-debug'; +import { testGit } from './tests/git'; +import { testIntegratedTerminal } from './tests/integrated-terminal'; +import { testStatusbar } from './tests/statusbar'; +import { testTasks } from './tests/tasks'; +import { testExtensions } from './tests/extensions'; +import { testLocalization } from './tests/localization'; +import { testMultiRoot } from './tests/multiroot'; + +describe('Smoke:', () => { + testDataMigration(); + testDataLoss(); + testExplorer(); + testConfigViews(); + testSearch(); + testCSS(); + testJavaScript(); + testJavaScriptDebug(); + testGit(); + testIntegratedTerminal(); + testStatusbar(); + testTasks(); + testExtensions(); + testLocalization(); + if (process.env.VSCODE_EDITION === 'insiders') { + testMultiRoot(); // only enabled in insiders + } +}); \ No newline at end of file diff --git a/test/smoke/src/tests/configuration-views.ts b/test/smoke/src/tests/configuration-views.ts new file mode 100644 index 0000000000..06c9c07ca5 --- /dev/null +++ b/test/smoke/src/tests/configuration-views.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { ConfigurationView, ActivityBarPosition } from '../areas/configuration-views'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testConfigViews() { + describe('Configuration and views', () => { + let configView: ConfigurationView; + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); + common = new CommonActions(app); + configView = new ConfigurationView(app); + + return await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it('turns off editor line numbers and verifies the live change', async function () { + await common.newUntitledFile(); + await app.wait(); + let elementsCount = await configView.getEditorLineNumbers(); + assert.equal(elementsCount, 1, 'Line numbers are not present in the editor before disabling them.'); + await common.addSetting('editor.lineNumbers', 'off'); + await app.wait(); + elementsCount = await configView.getEditorLineNumbers(); + assert.equal(elementsCount, 0, 'Line numbers are still present in the editor after disabling them.'); + }); + + it(`changes 'workbench.action.toggleSidebarPosition' command key binding and verifies it`, async function () { + await configView.enterKeybindingsView(); + await common.type('workbench.action.toggleSidebarPosition'); + await app.wait(); + await configView.selectFirstKeybindingsMatch(); + await configView.changeKeybinding(); + await configView.enterBinding(['Control', 'u', 'NULL']); + await common.enter(); + let html = await configView.getActivityBar(ActivityBarPosition.RIGHT); + assert.equal(html, undefined, 'Activity bar is positioned on the right, whereas should not be.'); + await app.wait(); + await configView.toggleActivityBarPosition(); + html = await configView.getActivityBar(ActivityBarPosition.RIGHT); + assert.ok(html, 'Activity bar was not moved to right after toggling its position.'); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/css.ts b/test/smoke/src/tests/css.ts new file mode 100644 index 0000000000..320b2ba3b8 --- /dev/null +++ b/test/smoke/src/tests/css.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { CSS, CSSProblem } from '../areas/css'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testCSS() { + describe('CSS', () => { + let css: CSS; + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); + common = new CommonActions(app); + css = new CSS(app); + + return await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it('verifies quick outline', async function () { + await common.openFirstMatchFile('style.css'); + await css.openQuickOutline(); + await app.wait(); + const count = await common.getQuickOpenElements(); + assert.equal(count, 2, 'Quick outline symbol count is wrong.'); + }); + + it('verifies warnings for the empty rule', async function () { + await common.openFirstMatchFile('style.css'); + await common.type('.foo{}'); + await app.wait(); + let warning = await css.getEditorProblem(CSSProblem.WARNING); + assert.ok(warning, `Warning squiggle is not shown in 'style.css'.`); + await css.toggleProblemsView(); + warning = await css.getProblemsViewsProblem(CSSProblem.WARNING); + assert.ok(warning, 'Warning does not appear in Problems view.'); + }); + + it('verifies that warning becomes an error once setting changed', async function () { + await common.addSetting('css.lint.emptyRules', 'error'); + await common.openFirstMatchFile('style.css'); + await common.type('.foo{}'); + await app.wait(); + let error = await css.getEditorProblem(CSSProblem.ERROR); + assert.ok(error, `Error squiggle is not shown in 'style.css'.`); + await css.toggleProblemsView(); + error = await css.getProblemsViewsProblem(CSSProblem.ERROR); + assert.ok(error, `Error does not appear in Problems view`); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/data-loss.ts b/test/smoke/src/tests/data-loss.ts new file mode 100644 index 0000000000..6597b24f0e --- /dev/null +++ b/test/smoke/src/tests/data-loss.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, USER_DIR, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { DataLoss } from '../areas/data-loss'; + +let app: SpectronApplication; +let common: CommonActions; +let dl: DataLoss; + +export function testDataLoss() { + describe('Data Loss', () => { + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH], [`--user-data-dir=${USER_DIR}`]); + common = new CommonActions(app); + dl = new DataLoss(app); + await common.removeDirectory(USER_DIR); + + await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it(`verifies that 'hot exit' works for dirty files`, async function () { + const textToType = 'Hello, Code', fileName = 'readme.md', untitled = 'Untitled-1'; + await common.newUntitledFile(); + await common.type(textToType); + await dl.openExplorerViewlet(); + await common.openFile(fileName, true); + await common.type(textToType); + + await app.stop(); + await app.start(); + + // check tab presence + assert.ok(await common.getTab(untitled), `${untitled} tab is not present after reopening.`); + assert.ok(await common.getTab(fileName, true), `${fileName} tab is not present or is not active after reopening.`); + // check if they marked as dirty (icon) and active tab is the last opened + assert.ok(await dl.verifyTabIsDirty(untitled), `${untitled} tab is not dirty after reopening.`); + assert.ok(await dl.verifyTabIsDirty(fileName, true), `${fileName} tab is not dirty after reopening.`); + }); + + it(`verifies that contents of the dirty files are restored after 'hot exit'`, async function () { + // make one dirty file, + // create one untitled file + const textToType = 'Hello, Code'; + + // create one untitled file + await common.newUntitledFile(); + await common.type(textToType); + + // make one dirty file, + await common.openFile('readme.md', true); + await common.type(textToType); + + await app.stop(); + await app.start(); + + // check their contents + let fileDirt = await common.getEditorFirstLinePlainText(); + assert.equal(fileDirt, textToType, 'Active file contents are different after restore.'); + await common.selectTab('Untitled-1'); + fileDirt = await common.getEditorFirstLinePlainText(); + assert.equal(fileDirt, textToType, 'Untitled file edit are different after restore.'); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/data-migration.ts b/test/smoke/src/tests/data-migration.ts new file mode 100644 index 0000000000..58ead5897c --- /dev/null +++ b/test/smoke/src/tests/data-migration.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, USER_DIR, STABLE_PATH, LATEST_PATH, WORKSPACE_PATH, EXTENSIONS_DIR } from '../spectron/application'; +import { CommonActions } from '../areas/common'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testDataMigration() { + if (!STABLE_PATH) { + return; + } + + describe('Data Migration', () => { + + afterEach(async function () { + await app.stop(); + await common.removeDirectory(USER_DIR); + return await common.removeDirectory(EXTENSIONS_DIR); + }); + + function setupSpectron(context: Mocha.ITestCallbackContext, appPath: string, args?: string[]): void { + if (!args) { + args = []; + } + args.push(`--extensions-dir=${EXTENSIONS_DIR}`); + + app = new SpectronApplication(appPath, context.test.fullTitle(), context.test.currentRetry(), args, [`--user-data-dir=${USER_DIR}`]); + common = new CommonActions(app); + } + + it('checks if the Untitled file is restored migrating from stable to latest', async function () { + const textToType = 'Very dirty file'; + + // Setting up stable version + setupSpectron(this, STABLE_PATH); + await app.start(); + + await common.newUntitledFile(); + await common.type(textToType); + await app.stop(); + + await app.wait(); // wait until all resources are released (e.g. locked local storage) + + // Checking latest version for the restored state + setupSpectron(this, LATEST_PATH); + await app.start(); + + assert.ok(await common.getTab('Untitled-1'), 'Untitled-1 tab was not restored after migration.'); + await common.selectTab('Untitled-1'); + const editorText = await common.getEditorFirstLinePlainText(); + assert.equal(editorText, textToType, 'Typed editor text does not match to the one after migration.'); + }); + + it('checks if the newly created dirty file is restored migrating from stable to latest', async function () { + const fileName = 'test_data/plainFile', + firstTextPart = 'This is going to be an unsaved file', secondTextPart = '_that is dirty.'; + + // Setting up stable version + setupSpectron(this, STABLE_PATH, [fileName]); + await common.removeFile(`${fileName}`); + await app.start(); + + await common.type(firstTextPart); + await common.saveOpenedFile(); + await app.wait(); + await common.type(secondTextPart); + + await app.stop(); + await app.wait(); // wait until all resources are released (e.g. locked local storage) + + // Checking latest version for the restored state + setupSpectron(this, LATEST_PATH); + await app.start(); + assert.ok(await common.getTab(fileName.split('/')[1]), `${fileName} was not restored after migration.`); + await common.selectTab(fileName.split('/')[1]); + const editorText = await common.getEditorFirstLinePlainText(); + assert.equal(editorText, firstTextPart.concat(secondTextPart), 'Entered text was not correctly restored after migration.'); + + // Cleanup + await common.removeFile(`${fileName}`); + }); + + it('cheks if opened tabs are restored migrating from stable to latest', async function () { + const fileName1 = 'app.js', fileName2 = 'jsconfig.json', fileName3 = 'readme.md'; + setupSpectron(this, STABLE_PATH, [WORKSPACE_PATH]); + await app.start(); + await common.openFile(fileName1, true); + await common.openFile(fileName2, true); + await common.openFile(fileName3, true); + await app.stop(); + + setupSpectron(this, LATEST_PATH, [WORKSPACE_PATH]); + await app.start(); + assert.ok(await common.getTab(fileName1), `${fileName1} tab was not restored after migration.`); + assert.ok(await common.getTab(fileName2), `${fileName2} tab was not restored after migration.`); + assert.ok(await common.getTab(fileName3), `${fileName3} tab was not restored after migration.`); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/explorer.ts b/test/smoke/src/tests/explorer.ts new file mode 100644 index 0000000000..b129f5c962 --- /dev/null +++ b/test/smoke/src/tests/explorer.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testExplorer() { + describe('Explorer', () => { + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); + common = new CommonActions(app); + + return await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it('quick open search produces correct result', async function () { + await common.openQuickOpen(); + await common.type('.js'); + await app.wait(); + const elCount = await common.getQuickOpenElements(); + assert.equal(elCount, 7); + }); + + it('quick open respects fuzzy matching', async function () { + await common.openQuickOpen(); + await common.type('a.s'); + await app.wait(); + const elCount = await common.getQuickOpenElements(); + assert.equal(elCount, 3); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/extensions.ts b/test/smoke/src/tests/extensions.ts new file mode 100644 index 0000000000..8c5641b5c4 --- /dev/null +++ b/test/smoke/src/tests/extensions.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH, EXTENSIONS_DIR } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { Extensions } from '../areas/extensions'; + +var dns = require('dns'); + +let app: SpectronApplication; +let common: CommonActions; + +export function testExtensions() { + + describe('Extensions', () => { + let extensions: Extensions; + const extensionName = 'vscode-smoketest-check'; + + beforeEach(async function () { + const network = await networkAttached(); + if (!network) { + return Promise.reject('There is no network connection for testing extensions.'); + } + + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH, `--extensions-dir=${EXTENSIONS_DIR}`]); + common = new CommonActions(app); + extensions = new Extensions(app, common); + await common.removeDirectory(EXTENSIONS_DIR); + + return await app.start(); + }); + afterEach(async function () { + await app.stop(); + return await common.removeDirectory(EXTENSIONS_DIR); + }); + + it(`installs 'vscode-smoketest-check' extension and verifies reload is prompted`, async function () { + await extensions.openExtensionsViewlet(); + await extensions.searchForExtension(extensionName); + await app.wait(); + await extensions.installExtension(extensionName); + await app.wait(); + assert.ok(await extensions.getExtensionReloadText(), 'Reload was not prompted after extension installation.'); + }); + + it(`installs an extension and checks if it works on restart`, async function () { + await extensions.openExtensionsViewlet(); + await extensions.searchForExtension(extensionName); + await app.wait(); + await extensions.installExtension(extensionName); + await app.wait(); + await extensions.getExtensionReloadText(); + + await app.stop(); + await app.wait(); // wait until all resources are released (e.g. locked local storage) + await app.start(); + await extensions.activateExtension(); + const statusbarText = await extensions.verifyStatusbarItem(); + assert.equal(statusbarText, 'VS Code Smoke Test Check', 'Extension contribution text does not match expected.'); + }); + }); +} + +function networkAttached(): Promise { + return new Promise((res, rej) => { + dns.resolve('marketplace.visualstudio.com', (err) => { + err ? res(false) : res(true); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/git.ts b/test/smoke/src/tests/git.ts new file mode 100644 index 0000000000..3d1378f60d --- /dev/null +++ b/test/smoke/src/tests/git.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { Git } from '../areas/git'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testGit() { + describe('Git', () => { + let git: Git; + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); + common = new CommonActions(app); + git = new Git(app, common); + + return await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it('verifies current changes are picked up by Git viewlet', async function () { + const changesCount = await git.getScmIconChanges(); + assert.equal(changesCount, 2); + await git.openGitViewlet(); + assert.ok(await git.verifyScmChange('app.js'), 'app.js change does not appear in SCM viewlet.'); + assert.ok(await git.verifyScmChange('launch.json'), 'launch.json change does not appear in SCM viewlet.'); + }); + + it(`verifies 'app.js' diff viewer changes`, async function () { + await git.openGitViewlet(); + await common.openFile('app.js'); + const original = await git.getOriginalAppJsBodyVarName(); + assert.equal(original, 'bodyParser', 'Original value from diff view is wrong.'); + const modified = await git.getModifiedAppJsBodyVarName(); + assert.equal(modified, 'ydobParser', 'Modified value from diff view is wrong.'); + }); + + it(`stages 'app.js' changes and checks stage count`, async function () { + await git.openGitViewlet(); + await app.wait(); + await git.stageFile('app.js'); + const stagedCount = await git.getStagedCount(); + assert.equal(stagedCount, 1); + + // Return back to unstaged state + await git.unstageFile('app.js'); + }); + + it(`stages, commits change to 'app.js' locally and verifies outgoing change`, async function () { + await git.openGitViewlet(); + await app.wait(); + await git.stageFile('app.js'); + await git.focusOnCommitBox(); + await common.type('Test commit'); + await git.pressCommit(); + const changes = await git.getOutgoingChanges(); + assert.equal(changes, ' 0↓ 1↑', 'Changes indicator is wrong in a status bar.'); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/integrated-terminal.ts b/test/smoke/src/tests/integrated-terminal.ts new file mode 100644 index 0000000000..f2e6561285 --- /dev/null +++ b/test/smoke/src/tests/integrated-terminal.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 * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { IntegratedTerminal } from '../areas/integrated-terminal'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testIntegratedTerminal() { + describe('Integrated Terminal', () => { + let terminal: IntegratedTerminal; + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); + common = new CommonActions(app); + terminal = new IntegratedTerminal(app); + + return await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it(`opens terminal, runs 'echo' and verifies the output`, async function () { + const command = 'echo test'; + await terminal.openTerminal(common); + await app.wait(); + await common.type(command); + await common.enter(); + await app.wait(); + assert.ok(await terminal.commandOutputHas('test'), 'Terminal output does not contain echo.'); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/javascript-debug.ts b/test/smoke/src/tests/javascript-debug.ts new file mode 100644 index 0000000000..e7179f0152 --- /dev/null +++ b/test/smoke/src/tests/javascript-debug.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { JavaScriptDebug } from '../areas/javascript-debug'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testJavaScriptDebug() { + describe('Debugging JavaScript', () => { + let jsDebug: JavaScriptDebug; + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); + common = new CommonActions(app); + jsDebug = new JavaScriptDebug(app); + + return await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it('autodetects program attribute for launch.json', async function () { + await jsDebug.openDebugViewlet(); + await jsDebug.pressConfigureLaunchJson(); + const value = await jsDebug.getProgramConfigValue(); + process.platform === 'win32' ? assert.equal(value, '${workspaceRoot}\\bin\\www') : assert.equal(value, '${workspaceRoot}/bin/www'); + }); + + it(`can set a breakpoint and verify if it's set`, async function () { + await common.openFirstMatchFile('index.js'); + await jsDebug.setBreakpointOnLine(6); + const breakpoint = await jsDebug.verifyBreakpointOnLine(6); + assert.ok(breakpoint, 'Breakpoint was not found on line 6.'); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/javascript.ts b/test/smoke/src/tests/javascript.ts new file mode 100644 index 0000000000..1053083f11 --- /dev/null +++ b/test/smoke/src/tests/javascript.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { JavaScript } from '../areas/javascript'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testJavaScript() { + describe('JavaScript', () => { + let js: JavaScript; + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); + common = new CommonActions(app); + js = new JavaScript(app); + + return await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it('shows correct quick outline', async function () { + await common.openFirstMatchFile('bin/www'); + await js.openQuickOutline(); + await app.wait(); + const symbols = await common.getQuickOpenElements(); + assert.equal(symbols, 12, 'Quick outline elements count does not match to expected.'); + }); + + it(`finds 'All References' to 'app'`, async function () { + await common.openFirstMatchFile('bin/www'); + await js.findAppReferences(); + await app.wait(); + const titleCount = await js.getTitleReferencesCount(); + assert.equal(titleCount, 3, 'References count in widget title is not as expected.'); + const treeCount = await js.getTreeReferencesCount(); + assert.equal(treeCount, 3, 'References count in tree is not as expected.'); + }); + + it(`renames local 'app' variable`, async function () { + await common.openFirstMatchFile('bin/www'); + + const newVarName = 'newApp'; + await js.renameApp(newVarName); + await common.enter(); + const newName = await js.getNewAppName(); + assert.equal(newName, newVarName); + }); + + it('folds/unfolds the code correctly', async function () { + await common.openFirstMatchFile('bin/www'); + // Fold + await js.toggleFirstCommentFold(); + const foldedIcon = await js.getFirstCommentFoldedIcon(); + assert.ok(foldedIcon, 'Folded icon was not found in the margin.'); + let nextLineNumber = await js.getNextLineNumberAfterFold(); + assert.equal(nextLineNumber, 7, 'Line number after folded code is wrong.'); + // Unfold + await js.toggleFirstCommentFold(); + nextLineNumber = await js.getNextLineNumberAfterFold(); + assert.equal(nextLineNumber, 4, 'Line number with unfolded code is wrong.'); + }); + + it(`verifies that 'Go To Definition' works`, async function () { + await common.openFirstMatchFile('app.js'); + await js.goToExpressDefinition(); + await app.wait(); + assert.ok(await common.getTab('index.d.ts'), 'Tab opened when navigating to definition is not as expected.'); + }); + + it(`verifies that 'Peek Definition' works`, async function () { + await common.openFirstMatchFile('app.js'); + await js.peekExpressDefinition(); + await app.wait(); + const definitionFilename = await js.getPeekExpressResultName(); + assert.equal(definitionFilename, 'index.d.ts', 'Peek result is not as expected.'); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/localization.ts b/test/smoke/src/tests/localization.ts new file mode 100644 index 0000000000..ccb4df4ce3 --- /dev/null +++ b/test/smoke/src/tests/localization.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH, USER_DIR } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { Localization, ViewletType } from '../areas/localization'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testLocalization() { + describe('Localization', () => { + afterEach(async function () { + return await app.stop(); + }); + + it(`starts with 'DE' locale and verifies title and viewlets text is in German`, async function () { + app = new SpectronApplication(LATEST_PATH, this.test.fullTitle(), this.test.currentRetry(), [WORKSPACE_PATH, '--locale=DE'], [`--user-data-dir=${USER_DIR}`]); + common = new CommonActions(app); + const locale = new Localization(app); + common.removeDirectory(USER_DIR); + + await app.start(); + + let text = await locale.getOpenEditorsText(); + assert.equal(text.toLowerCase(), 'geöffnete editoren'); + + await locale.openViewlet(ViewletType.SEARCH); + text = await locale.getOpenedViewletTitle(); + assert.equal(text.toLowerCase(), 'suchen'); + + await locale.openViewlet(ViewletType.SCM); + await app.wait(); // wait until git extension is loaded + text = await locale.getOpenedViewletTitle(); + assert.equal(text.toLowerCase(), 'quellcodeverwaltung: git'); + + await locale.openViewlet(ViewletType.DEBUG); + text = await locale.getOpenedViewletTitle(); + assert.equal(text.toLowerCase(), 'debuggen'); + + await locale.openViewlet(ViewletType.EXTENSIONS); + text = await locale.getExtensionsSearchPlaceholder(); + assert.equal(text.toLowerCase(), 'nach erweiterungen im marketplace suchen'); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/multiroot.ts b/test/smoke/src/tests/multiroot.ts new file mode 100644 index 0000000000..78dbf04b73 --- /dev/null +++ b/test/smoke/src/tests/multiroot.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, CODE_WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testMultiRoot() { + describe('Multi Root', () => { + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [CODE_WORKSPACE_PATH]); + common = new CommonActions(app); + + return await app.start(); + }); + + afterEach(async function () { + return await app.stop(); + }); + + it('shows results from all folders', async function () { + await common.openQuickOpen(); + await app.wait(); + await common.type('*.*'); + await app.wait(); + const elCount = await common.getQuickOpenElements(); + assert.equal(elCount, 6); + }); + + it('shows workspace name in title', async function () { + await app.wait(); + const title = await common.getWindowTitle(); + assert.ok(title.indexOf('smoketest (Workspace)') >= 0); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/search.ts b/test/smoke/src/tests/search.ts new file mode 100644 index 0000000000..ba047a1094 --- /dev/null +++ b/test/smoke/src/tests/search.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { Search } from '../areas/search'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testSearch() { + describe('Search', () => { + let search: Search; + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); + common = new CommonActions(app); + search = new Search(app); + + return await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it('searches for body & checks for correct result number', async function () { + const s = search; + await s.openSearchViewlet(); + await s.searchFor('body'); + const result = await s.getResultText(); + assert.equal(result, '7 results in 4 files'); + }); + + it('searches only for *.js files & checks for correct result number', async function () { + const s = search; + await s.openSearchViewlet(); + await s.searchFor('body'); + await s.toggleSearchDetails(); + await s.searchFor('*.js'); + const results = await s.getResultText(); + assert.equal(results, '4 results in 1 file'); + }); + + it('dismisses result & checks for correct result number', async function () { + const s = search; + await s.openSearchViewlet(); + await s.searchFor('body'); + await s.hoverOverResultCount(); + await s.dismissResult(); + await app.wait(); + const result = await s.getResultText(); + assert.equal(result, '3 results in 3 files', 'Result number after dismissal does not match to expected.'); + }); + + it('replaces first search result with a replace term', async function () { + const s = search; + await s.openSearchViewlet(); + await s.searchFor('body'); + await s.toggleReplace(); + await s.setReplaceText('ydob'); + await s.hoverOverResultCount(); + await s.replaceFirstMatch(); + await app.wait(); + await common.saveOpenedFile(); + const result = await s.getResultText(); + assert.equal(result, '3 results in 3 files', 'Result number after replacemenet does not match to expected.'); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/statusbar.ts b/test/smoke/src/tests/statusbar.ts new file mode 100644 index 0000000000..9cbb14aa00 --- /dev/null +++ b/test/smoke/src/tests/statusbar.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { CommonActions } from '../areas/common'; +import { StatusBarElement, StatusBar } from '../areas/statusbar'; + +let app: SpectronApplication; +let common: CommonActions; + +export function testStatusbar() { + describe('Status Bar', () => { + let statusBar: StatusBar; + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); + common = new CommonActions(app); + statusBar = new StatusBar(app); + + return await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it('verifies presence of all default status bar elements', async function () { + await app.wait(); + assert.ok(await statusBar.isVisible(StatusBarElement.BRANCH_STATUS), 'Branch indicator is not visible.'); + assert.ok(await statusBar.isVisible(StatusBarElement.FEEDBACK_ICON), 'Feedback icon is not visible.'); + assert.ok(await statusBar.isVisible(StatusBarElement.SYNC_STATUS), 'Sync indicator is not visible.'); + assert.ok(await statusBar.isVisible(StatusBarElement.PROBLEMS_STATUS), 'Problems indicator is not visible.'); + + await common.openFirstMatchFile('app.js'); + assert.ok(await statusBar.isVisible(StatusBarElement.ENCODING_STATUS), 'Encoding indicator is not visible.'); + assert.ok(await statusBar.isVisible(StatusBarElement.EOL_STATUS), 'EOL indicator is not visible.'); + assert.ok(await statusBar.isVisible(StatusBarElement.INDENTATION_STATUS), 'Indentation indicator is not visible.'); + assert.ok(await statusBar.isVisible(StatusBarElement.LANGUAGE_STATUS), 'Language indicator is not visible.'); + assert.ok(await statusBar.isVisible(StatusBarElement.SELECTION_STATUS), 'Selection indicator is not visible.'); + }); + + it(`verifies that 'quick open' opens when clicking on status bar elements`, async function () { + await app.wait(); + await statusBar.clickOn(StatusBarElement.BRANCH_STATUS); + assert.ok(await statusBar.isQuickOpenWidgetVisible(), 'Quick open is not opened for branch indicator.'); + await common.closeQuickOpen(); + + await common.openFirstMatchFile('app.js'); + await statusBar.clickOn(StatusBarElement.INDENTATION_STATUS); + assert.ok(await statusBar.isQuickOpenWidgetVisible(), 'Quick open is not opened for indentation indicator.'); + await common.closeQuickOpen(); + await statusBar.clickOn(StatusBarElement.ENCODING_STATUS); + assert.ok(await statusBar.isQuickOpenWidgetVisible(), 'Quick open is not opened for encoding indicator.'); + await common.closeQuickOpen(); + await statusBar.clickOn(StatusBarElement.EOL_STATUS); + assert.ok(await statusBar.isQuickOpenWidgetVisible(), 'Quick open is not opened for EOL indicator.'); + await common.closeQuickOpen(); + await statusBar.clickOn(StatusBarElement.LANGUAGE_STATUS); + assert.ok(await statusBar.isQuickOpenWidgetVisible(), 'Quick open is not opened for language indicator.'); + await common.closeQuickOpen(); + }); + + it(`verifies that 'Problems View' appears when clicking on 'Problems' status element`, async function () { + await statusBar.clickOn(StatusBarElement.PROBLEMS_STATUS); + assert.ok(await statusBar.getProblemsView()); + }); + + it(`verifies that 'Tweet us feedback' pop-up appears when clicking on 'Feedback' icon`, async function () { + await statusBar.clickOn(StatusBarElement.FEEDBACK_ICON); + assert.ok(await statusBar.getFeedbackView()); + }); + + it(`checks if 'Go to Line' works if called from the status bar`, async function () { + await common.openFirstMatchFile('app.js'); + await statusBar.clickOn(StatusBarElement.SELECTION_STATUS); + const lineNumber = 15; + await common.type(lineNumber.toString()); + await common.enter(); + assert.ok(await statusBar.getEditorHighlightedLine(lineNumber), 'Editor does not highlight the line.'); + }); + + it(`verifies if changing EOL is reflected in the status bar`, async function () { + await common.openFirstMatchFile('app.js'); + await statusBar.clickOn(StatusBarElement.EOL_STATUS); + await common.selectNextQuickOpenElement(); + await common.enter(); + const currentEOL = await statusBar.getEOLMode(); + assert.equal(currentEOL, 'CRLF'); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/src/tests/tasks.ts b/test/smoke/src/tests/tasks.ts new file mode 100644 index 0000000000..1bd7be5c85 --- /dev/null +++ b/test/smoke/src/tests/tasks.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application'; +import { Tasks } from '../areas/tasks'; + +let app: SpectronApplication; + +export function testTasks() { + describe('Tasks', () => { + let tasks: Tasks; + + beforeEach(async function () { + app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); + tasks = new Tasks(app); + + return await app.start(); + }); + afterEach(async function () { + return await app.stop(); + }); + + it('verifies that eslint task results in 1 problem', async function () { + const expectedOutput = '1 problem (0 errors, 1 warning)'; + await tasks.build(); + const actualOutput = await tasks.outputContains(expectedOutput); + assert.ok(actualOutput, `Output does not contain the following string: '${expectedOutput}'`); + }); + + it(`is able to select 'Git' output`, async function () { + await tasks.build(); + await app.wait(); + await tasks.selectOutputViewType('Git'); + const viewType = await tasks.getOutputViewType(); + assert.equal(viewType, 'Git'); + }); + + it('ensures that build task produces no-unused-vars message', async function () { + await tasks.build(); + assert.ok(await tasks.outputContains(`'next' is defined but never used`), `Output does not contain no-unused-vars message`); + }); + + it(`verifies build error is reflected in 'Problems View'`, async function () { + await tasks.build(); + await tasks.openProblemsView(); + const problemName = await tasks.getProblemsViewFirstElementName(); + assert.equal(problemName, 'index.js', `'index.js' is not a build error.`); + const problemsCount = await tasks.getProblemsViewFirstElementCount(); + assert.equal(problemsCount, '1', `Problem count is different to expected.`); + }); + }); +} \ No newline at end of file diff --git a/test/smoke/tsconfig.json b/test/smoke/tsconfig.json new file mode 100644 index 0000000000..733c1107e8 --- /dev/null +++ b/test/smoke/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noImplicitAny": false, + "removeComments": false, + "preserveConstEnums": true, + "target": "es2016", + "strictNullChecks": true, + "noUnusedParameters": false, + "noUnusedLocals": true, + "outDir": "out", + "sourceMap": true, + "lib": [ + "es2016", + "dom" + ] + }, + "exclude": [ + "node_modules" + ] +} diff --git a/tsfmt.json b/tsfmt.json new file mode 100644 index 0000000000..fffcf07c19 --- /dev/null +++ b/tsfmt.json @@ -0,0 +1,17 @@ +{ + "tabSize": 4, + "indentSize": 4, + "newLineCharacter": "\n", + "convertTabsToSpaces": false, + "insertSpaceAfterCommaDelimiter": true, + "insertSpaceAfterSemicolonInForStatements": true, + "insertSpaceBeforeAndAfterBinaryOperators": true, + "insertSpaceAfterKeywordsInControlFlowStatements": true, + "insertSpaceAfterFunctionKeywordForAnonymousFunctions": true, + "insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false, + "insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, + "insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false, + "insertSpaceBeforeFunctionParenthesis": false, + "placeOpenBraceOnNewLineForFunctions": false, + "placeOpenBraceOnNewLineForControlBlocks": false +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000000..040841ddd8 --- /dev/null +++ b/tslint.json @@ -0,0 +1,77 @@ +{ + "rulesDirectory": [ + "build/lib/tslint" + ], + "rules": { + "no-string-throw": true, + "no-unused-expression": true, + "no-duplicate-variable": true, + "no-unused-variable": true, + "curly": true, + "class-name": true, + "semicolon": [ + "always" + ], + "triple-equals": true, + "no-unexternalized-strings": [ + true, + { + "signatures": [ + "localize", + "nls.localize" + ], + "keyIndex": 0, + "messageIndex": 1 + } + ], + "layering": [ + true, + { + "common": [], + "node": [ + "common" + ], + "browser": [ + "common" + ], + "electron-main": [ + "common", + "node" + ], + "electron-browser": [ + "common", + "browser", + "node" + ] + } + ], + "import-patterns": [ + true, + // {{SQL CARBON EDIT}} + { + "target": "**/{node,electron-browser,electron-main,extensions}/**", + "restrictions": "**/*" + }, + { + "target": "{**/**.test.ts,**/test/**}", + "restrictions": "{**/vs/**,**/sql/**,**/sqltest/**,assert,sinon,typemoq,data,@angular/**,rxjs/**}" + }, + { + "target": "**/{common,browser,workbench}/**", + "restrictions": "**/*" + } + ], + "duplicate-imports": true, + "allow-async": [ + true, + [ + "node", + "electron-main", + "electron-browser", + "extensions", + "smoke" + ] + ], + "translation-remind": true + } +} \ No newline at end of file diff --git a/typings.json b/typings.json new file mode 100644 index 0000000000..663d6d2f62 --- /dev/null +++ b/typings.json @@ -0,0 +1,25 @@ +{ + "dependencies": { + "@angular/animations": "npm:@angular/animations/index.d.ts", + "@angular/common": "npm:@angular/common/common.d.ts", + "@angular/compiler": "npm:@angular/compiler/compiler.d.ts", + "@angular/core": "npm:@angular/core/core.d.ts", + "@angular/forms": "npm:@angular/forms/forms.d.ts", + "@angular/http": "npm:@angular/http/index.d.ts", + "@angular/platform-browser": "npm:@angular/platform-browser/platform-browser.d.ts", + "@angular/platform-browser-dynamic": "npm:@angular/platform-browser-dynamic/platform-browser-dynamic.d.ts", + "@angular/router": "npm:@angular/router/router.d.ts", + "@angular/upgrade": "npm:@angular/upgrade/index.d.ts", + "angular2-slickgrid": "npm:angular2-slickgrid/index.d.ts", + "angular2-grid": "npm:angular2-grid/main.d.ts", + "ng2-chart2": "npm:ng2-charts/ng2-charts.d.ts", + "reflect-metadata": "npm:reflect-metadata/index.d.ts", + "rxjs": "npm:rxjs/Rx.d.ts" + }, + "globalDependencies": { + "core-js": "registry:dt/core-js#0.9.7+20161130133742", + "systemjs": "registry:dt/systemjs#0.19.29+20161215140219", + "underscore": "registry:dt/underscore#1.8.3+20161123125004", + "zone.js": "npm:zone.js\\dist\\zone.js.d.ts" + } +}
+
+
+

${escape(localize('welcomePage.vscode', "Visual Studio Code"))}

+

${escape(localize('welcomePage.editingEvolved', "Editing evolved"))}

+
+
+ +
+
+

${escape(localize('welcomePage.customize', "Customize"))}

+
    +
  • +
  • +
  • +
+
+
+

${escape(localize('welcomePage.learn', "Learn"))}

+
    +
  • +
  • +
  • +
  • +
+
+
+
+
+